From 71109c5fa3f7f4446d859d2b53d1b90d79aa13a1 Mon Sep 17 00:00:00 2001 From: Parth Mittal <76661350+mittal-parth@users.noreply.github.com> Date: Wed, 24 Jul 2024 17:51:35 +0530 Subject: [PATCH 001/382] Remove `pallet-getter` usage from pallet-transaction-payment (#4970) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As per #3326, removes usage of the `pallet::getter` macro from the `transaction-payment` pallet. The syntax `StorageItem::::get()` should be used instead. Also, adds public functions for compatibility. NOTE: The `Releases` enum has been made public to transition `StorageVersion` from `pub(super) type` to `pub type`. cc @muraca polkadot address: 5GsLutpKjbzsbTphebs9Uy4YK6gTN47MAaz6njPktidjR5cp --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Bastian Köcher Co-authored-by: Oliver Tale-Yazdi --- prdoc/pr_4970.prdoc | 14 ++++++++++++ .../frame/transaction-payment/src/lib.rs | 20 ++++++++++------- .../frame/transaction-payment/src/tests.rs | 22 +++++++++---------- 3 files changed, 37 insertions(+), 19 deletions(-) create mode 100644 prdoc/pr_4970.prdoc diff --git a/prdoc/pr_4970.prdoc b/prdoc/pr_4970.prdoc new file mode 100644 index 000000000000..d86f1af1e860 --- /dev/null +++ b/prdoc/pr_4970.prdoc @@ -0,0 +1,14 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Remove `pallet::getter` usage from the transaction-payment pallet" + +doc: + - audience: Runtime Dev + description: | + This PR removes the `pallet::getter`s from `pallet-transaction-payment`. + The syntax `StorageItem::::get()` should be used instead. + +crates: + - name: pallet-transaction-payment + bump: minor diff --git a/substrate/frame/transaction-payment/src/lib.rs b/substrate/frame/transaction-payment/src/lib.rs index 69fad6e0e324..7df658a4732e 100644 --- a/substrate/frame/transaction-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/src/lib.rs @@ -207,7 +207,7 @@ where // the computed ratio is only among the normal class. let normal_max_weight = weights.get(DispatchClass::Normal).max_total.unwrap_or(weights.max_block); - let current_block_weight = >::block_weight(); + let current_block_weight = frame_system::Pallet::::block_weight(); let normal_block_weight = current_block_weight.get(DispatchClass::Normal).min(normal_max_weight); @@ -291,7 +291,7 @@ where /// Storage releases of the pallet. #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -enum Releases { +pub enum Releases { /// Original version of the pallet. V1Ancient, /// One that bumps the usage to FixedU128 from FixedI128. @@ -394,12 +394,11 @@ pub mod pallet { } #[pallet::storage] - #[pallet::getter(fn next_fee_multiplier)] pub type NextFeeMultiplier = StorageValue<_, Multiplier, ValueQuery, NextFeeMultiplierOnEmpty>; #[pallet::storage] - pub(super) type StorageVersion = StorageValue<_, Releases, ValueQuery>; + pub type StorageVersion = StorageValue<_, Releases, ValueQuery>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -433,7 +432,7 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_finalize(_: frame_system::pallet_prelude::BlockNumberFor) { - >::mutate(|fm| { + NextFeeMultiplier::::mutate(|fm| { *fm = T::FeeMultiplierUpdate::convert(*fm); }); } @@ -471,7 +470,7 @@ pub mod pallet { let min_value = T::FeeMultiplierUpdate::min(); let target = target + addition; - >::set_block_consumed_resources(target, 0); + frame_system::Pallet::::set_block_consumed_resources(target, 0); let next = T::FeeMultiplierUpdate::convert(min_value); assert!( next > min_value, @@ -484,6 +483,11 @@ pub mod pallet { } impl Pallet { + /// Public function to access the next fee multiplier. + pub fn next_fee_multiplier() -> Multiplier { + NextFeeMultiplier::::get() + } + /// Query the data that we know about the fee of a given `call`. /// /// This pallet is not and cannot be aware of the internals of a signed extension, for example @@ -633,7 +637,7 @@ impl Pallet { if pays_fee == Pays::Yes { // the adjustable part of the fee. let unadjusted_weight_fee = Self::weight_to_fee(weight); - let multiplier = Self::next_fee_multiplier(); + let multiplier = NextFeeMultiplier::::get(); // final adjusted weight fee. let adjusted_weight_fee = multiplier.saturating_mul_int(unadjusted_weight_fee); @@ -675,7 +679,7 @@ where /// share that the weight contributes to the overall fee of a transaction. It is mainly /// for informational purposes and not used in the actual fee calculation. fn convert(weight: Weight) -> BalanceOf { - >::get().saturating_mul_int(Self::weight_to_fee(weight)) + NextFeeMultiplier::::get().saturating_mul_int(Self::weight_to_fee(weight)) } } diff --git a/substrate/frame/transaction-payment/src/tests.rs b/substrate/frame/transaction-payment/src/tests.rs index bc0efd2d64a3..35d5322a6f33 100644 --- a/substrate/frame/transaction-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/src/tests.rs @@ -180,7 +180,7 @@ fn signed_extension_transaction_payment_multiplied_refund_works() { .build() .execute_with(|| { let len = 10; - >::put(Multiplier::saturating_from_rational(3, 2)); + NextFeeMultiplier::::put(Multiplier::saturating_from_rational(3, 2)); let pre = ChargeTransactionPayment::::from(5 /* tipped */) .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) @@ -270,7 +270,7 @@ fn signed_ext_length_fee_is_also_updated_per_congestion() { .build() .execute_with(|| { // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); + NextFeeMultiplier::::put(Multiplier::saturating_from_rational(3, 2)); let len = 10; assert_ok!(ChargeTransactionPayment::::from(10) // tipped @@ -305,7 +305,7 @@ fn query_info_and_fee_details_works() { .build() .execute_with(|| { // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); + NextFeeMultiplier::::put(Multiplier::saturating_from_rational(3, 2)); assert_eq!( TransactionPayment::query_info(xt.clone(), len), @@ -362,7 +362,7 @@ fn query_call_info_and_fee_details_works() { .build() .execute_with(|| { // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); + NextFeeMultiplier::::put(Multiplier::saturating_from_rational(3, 2)); assert_eq!( TransactionPayment::query_call_info(call.clone(), len), @@ -401,7 +401,7 @@ fn compute_fee_works_without_multiplier() { .build() .execute_with(|| { // Next fee multiplier is zero - assert_eq!(>::get(), Multiplier::one()); + assert_eq!(NextFeeMultiplier::::get(), Multiplier::one()); // Tip only, no fees works let dispatch_info = DispatchInfo { @@ -440,7 +440,7 @@ fn compute_fee_works_with_multiplier() { .build() .execute_with(|| { // Add a next fee multiplier. Fees will be x3/2. - >::put(Multiplier::saturating_from_rational(3, 2)); + NextFeeMultiplier::::put(Multiplier::saturating_from_rational(3, 2)); // Base fee is unaffected by multiplier let dispatch_info = DispatchInfo { weight: Weight::from_parts(0, 0), @@ -472,7 +472,7 @@ fn compute_fee_works_with_negative_multiplier() { .build() .execute_with(|| { // Add a next fee multiplier. All fees will be x1/2. - >::put(Multiplier::saturating_from_rational(1, 2)); + NextFeeMultiplier::::put(Multiplier::saturating_from_rational(1, 2)); // Base fee is unaffected by multiplier. let dispatch_info = DispatchInfo { @@ -637,7 +637,7 @@ fn refund_consistent_with_actual_weight() { let len = 10; let tip = 5; - >::put(Multiplier::saturating_from_rational(5, 4)); + NextFeeMultiplier::::put(Multiplier::saturating_from_rational(5, 4)); let pre = ChargeTransactionPayment::::from(tip) .pre_dispatch(&2, CALL, &info, len) @@ -797,7 +797,7 @@ fn post_info_can_change_pays_fee() { let len = 10; let tip = 5; - >::put(Multiplier::saturating_from_rational(5, 4)); + NextFeeMultiplier::::put(Multiplier::saturating_from_rational(5, 4)); let pre = ChargeTransactionPayment::::from(tip) .pre_dispatch(&2, CALL, &info, len) @@ -829,7 +829,7 @@ fn genesis_config_works() { .build() .execute_with(|| { assert_eq!( - >::get(), + NextFeeMultiplier::::get(), Multiplier::saturating_from_integer(100) ); }); @@ -838,6 +838,6 @@ fn genesis_config_works() { #[test] fn genesis_default_works() { ExtBuilder::default().build().execute_with(|| { - assert_eq!(>::get(), Multiplier::saturating_from_integer(1)); + assert_eq!(NextFeeMultiplier::::get(), Multiplier::saturating_from_integer(1)); }); } From 5878ea27e027e8551d6ed709b0900c52fb01ea0f Mon Sep 17 00:00:00 2001 From: Muharem Date: Wed, 24 Jul 2024 15:38:54 +0200 Subject: [PATCH 002/382] Tx Payment: drop ED requirements for tx payments with exchangeable asset (#4488) Drop the Existential Deposit (ED) requirement for the asset amount exchangeable for the fee asset (eg. DOT/KSM) during transaction payments. This achieved by using `SwapCredit` implementation of swap, which works with imbalances and does not require a temporary balance account within the transaction payment. ### Problem Currently, every swap during transaction payment, processed with asset `A` for native asset, must be for an amount greater than the ED of a native asset if the user lacks a native asset account. Since fees are typically smaller, the current implementation necessitates additional swaps to meet the ED during `pre_dispatch`, with refunds for excess ED swap occurring during `post_dispatch`. Further details can be found [here](https://github.com/paritytech/polkadot-sdk/blob/115c2477eb287df55107cd95594100ba395ed239/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs#L115). This setup presents an issue where a user is unable to transfer their entire balance and close the account. Instead, the user must transfer slightly less of asset `A` to ensure there is enough not only for the fee payment but also some extra to meet the ED requirement for their native account during `pre_dispatch`. In some cases during `post_dispatch`, the user will have the excess ED swapped back to asset `A`, while in other cases, it may not be sufficient to meet the ED requirement for asset `A`, leading it being left in the user's 'temporary' native asset account. --------- Co-authored-by: Oliver Tale-Yazdi --- .../assets/asset-hub-rococo/src/lib.rs | 16 +- .../assets/asset-hub-westend/src/lib.rs | 16 +- prdoc/pr_4488.prdoc | 25 ++ substrate/bin/node/runtime/src/lib.rs | 29 +- substrate/frame/asset-conversion/src/swap.rs | 52 ++++ .../asset-conversion-tx-payment/src/lib.rs | 179 +++++------ .../asset-conversion-tx-payment/src/mock.rs | 27 +- .../src/payment.rs | 294 +++++++++++------- .../asset-conversion-tx-payment/src/tests.rs | 171 ++++++++-- .../frame/transaction-payment/src/lib.rs | 5 + 10 files changed, 541 insertions(+), 273 deletions(-) create mode 100644 prdoc/pr_4488.prdoc diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index f09647854cd0..83bc12cf9b41 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -71,7 +71,7 @@ use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, EnsureSigned, EnsureSignedBy, }; -use pallet_asset_conversion_tx_payment::AssetConversionAdapter; +use pallet_asset_conversion_tx_payment::SwapAssetAdapter; use pallet_nfts::PalletFeatures; use parachains_common::{ impls::DealWithFees, @@ -798,11 +798,19 @@ impl pallet_collator_selection::Config for Runtime { type WeightInfo = weights::pallet_collator_selection::WeightInfo; } +parameter_types! { + pub StakingPot: AccountId = CollatorSelection::account_id(); +} + impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type Fungibles = LocalAndForeignAssets; - type OnChargeAssetTransaction = - AssetConversionAdapter; + type AssetId = xcm::v3::Location; + type OnChargeAssetTransaction = SwapAssetAdapter< + TokenLocationV3, + NativeAndAssets, + AssetConversion, + ResolveAssetTo, + >; } parameter_types! { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 178b886fc3e8..2d9de07f251f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -55,7 +55,7 @@ use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, EnsureSigned, EnsureSignedBy, }; -use pallet_asset_conversion_tx_payment::AssetConversionAdapter; +use pallet_asset_conversion_tx_payment::SwapAssetAdapter; use pallet_nfts::{DestroyWitness, PalletFeatures}; use pallet_xcm::EnsureXcm; use parachains_common::{ @@ -787,11 +787,19 @@ impl pallet_collator_selection::Config for Runtime { type WeightInfo = weights::pallet_collator_selection::WeightInfo; } +parameter_types! { + pub StakingPot: AccountId = CollatorSelection::account_id(); +} + impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type Fungibles = LocalAndForeignAssets; - type OnChargeAssetTransaction = - AssetConversionAdapter; + type AssetId = xcm::v3::Location; + type OnChargeAssetTransaction = SwapAssetAdapter< + WestendLocationV3, + NativeAndAssets, + AssetConversion, + ResolveAssetTo, + >; } parameter_types! { diff --git a/prdoc/pr_4488.prdoc b/prdoc/pr_4488.prdoc new file mode 100644 index 000000000000..d0b6a877be6b --- /dev/null +++ b/prdoc/pr_4488.prdoc @@ -0,0 +1,25 @@ +title: "Tx Payment: drop ED requirements for tx payments with exchangeable asset" + +doc: + - audience: Runtime Dev + description: | + Drop the Existential Deposit requirement for the asset amount exchangeable for the fee asset + (eg. DOT/KSM) during transaction payments. + + This achieved by using `SwapCredit` implementation of asset conversion, which works with + imbalances and does not require a temporary balance account within the transaction payment. + + This is a breaking change for the `pallet-asset-conversion-tx-payment` pallet, use examples + from PR for the migration. + +crates: + - name: pallet-asset-conversion-tx-payment + bump: major + - name: pallet-transaction-payment + bump: patch + - name: pallet-asset-conversion + bump: patch + - name: asset-hub-rococo-runtime + bump: patch + - name: asset-hub-westend-runtime + bump: patch diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index cad2cc119f0d..5d046539b036 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -69,6 +69,7 @@ use frame_system::{ pub use node_primitives::{AccountId, Signature}; use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Moment, Nonce}; use pallet_asset_conversion::{AccountIdConverter, Ascending, Chain, WithFirstAsset}; +use pallet_asset_conversion_tx_payment::SwapAssetAdapter; use pallet_broker::{CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600}; use pallet_election_provider_multi_phase::{GeometricDepositBase, SolutionAccuracyOf}; use pallet_identity::legacy::IdentityInfo; @@ -123,7 +124,7 @@ pub use sp_runtime::BuildStorage; pub mod impls; #[cfg(not(feature = "runtime-benchmarks"))] use impls::AllianceIdentityVerifier; -use impls::{AllianceProposalProvider, Author, CreditToBlockAuthor}; +use impls::{AllianceProposalProvider, Author}; /// Constant values used within the runtime. pub mod constants; @@ -574,22 +575,14 @@ impl pallet_transaction_payment::Config for Runtime { >; } -impl pallet_asset_tx_payment::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Fungibles = Assets; - type OnChargeAssetTransaction = pallet_asset_tx_payment::FungiblesAdapter< - pallet_assets::BalanceToAssetBalance, - CreditToBlockAuthor, - >; -} - impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type Fungibles = Assets; - type OnChargeAssetTransaction = pallet_asset_conversion_tx_payment::AssetConversionAdapter< - Balances, - AssetConversion, + type AssetId = NativeOrWithId; + type OnChargeAssetTransaction = SwapAssetAdapter< Native, + NativeAndAssets, + AssetConversion, + ResolveAssetTo, >; } @@ -1705,12 +1698,15 @@ parameter_types! { pub const Native: NativeOrWithId = NativeOrWithId::Native; } +pub type NativeAndAssets = + UnionOf, AccountId>; + impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = u128; type HigherPrecisionBalance = sp_core::U256; type AssetKind = NativeOrWithId; - type Assets = UnionOf, AccountId>; + type Assets = NativeAndAssets; type PoolId = (Self::AssetKind, Self::AssetKind); type PoolLocator = Chain< WithFirstAsset< @@ -2259,9 +2255,6 @@ mod runtime { #[runtime::pallet_index(7)] pub type TransactionPayment = pallet_transaction_payment::Pallet; - #[runtime::pallet_index(8)] - pub type AssetTxPayment = pallet_asset_tx_payment::Pallet; - #[runtime::pallet_index(9)] pub type AssetConversionTxPayment = pallet_asset_conversion_tx_payment::Pallet; diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index a6154e294147..1485e7166f30 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -112,6 +112,37 @@ pub trait SwapCredit { ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)>; } +/// Trait providing methods to quote swap prices between asset classes. +/// +/// The quoted price is only guaranteed if no other swaps are made after the price is quoted and +/// before the target swap (e.g., the swap is made immediately within the same transaction). +pub trait QuotePrice { + /// Measurement units of the asset classes for pricing. + type Balance: Balance; + /// Type representing the kind of assets for which the price is being quoted. + type AssetKind; + /// Quotes the amount of `asset1` required to obtain the exact `amount` of `asset2`. + /// + /// If `include_fee` is set to `true`, the price will include the pool's fee. + /// If the pool does not exist or the swap cannot be made, `None` is returned. + fn quote_price_tokens_for_exact_tokens( + asset1: Self::AssetKind, + asset2: Self::AssetKind, + amount: Self::Balance, + include_fee: bool, + ) -> Option; + /// Quotes the amount of `asset2` resulting from swapping the exact `amount` of `asset1`. + /// + /// If `include_fee` is set to `true`, the price will include the pool's fee. + /// If the pool does not exist or the swap cannot be made, `None` is returned. + fn quote_price_exact_tokens_for_tokens( + asset1: Self::AssetKind, + asset2: Self::AssetKind, + amount: Self::Balance, + include_fee: bool, + ) -> Option; +} + impl Swap for Pallet { type Balance = T::Balance; type AssetKind = T::AssetKind; @@ -210,3 +241,24 @@ impl SwapCredit for Pallet { .map_err(|_| (Self::Credit::zero(credit_asset), DispatchError::Corruption))? } } + +impl QuotePrice for Pallet { + type Balance = T::Balance; + type AssetKind = T::AssetKind; + fn quote_price_exact_tokens_for_tokens( + asset1: Self::AssetKind, + asset2: Self::AssetKind, + amount: Self::Balance, + include_fee: bool, + ) -> Option { + Self::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) + } + fn quote_price_tokens_for_exact_tokens( + asset1: Self::AssetKind, + asset2: Self::AssetKind, + amount: Self::Balance, + include_fee: bool, + ) -> Option { + Self::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) + } +} diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs index 538d88bfacfa..825a35e62138 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs @@ -23,7 +23,7 @@ //! This pallet provides a `SignedExtension` with an optional `AssetId` that specifies the asset //! to be used for payment (defaulting to the native token on `None`). It expects an //! [`OnChargeAssetTransaction`] implementation analogous to [`pallet-transaction-payment`]. The -//! included [`AssetConversionAdapter`] (implementing [`OnChargeAssetTransaction`]) determines the +//! included [`SwapAssetAdapter`] (implementing [`OnChargeAssetTransaction`]) determines the //! fee amount by converting the fee calculated by [`pallet-transaction-payment`] in the native //! asset into the amount required of the specified asset. //! @@ -47,19 +47,14 @@ extern crate alloc; use codec::{Decode, Encode}; use frame_support::{ dispatch::{DispatchInfo, DispatchResult, PostDispatchInfo}, - traits::{ - fungibles::{Balanced, Inspect}, - IsType, - }, + traits::IsType, DefaultNoBound, }; use pallet_transaction_payment::OnChargeTransaction; use scale_info::TypeInfo; use sp_runtime::{ traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension, Zero}, - transaction_validity::{ - InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, - }, + transaction_validity::{TransactionValidity, TransactionValidityError, ValidTransaction}, }; #[cfg(test)] @@ -71,30 +66,19 @@ mod payment; use frame_support::traits::tokens::AssetId; pub use payment::*; +/// Balance type alias for balances of the chain's native asset. +pub(crate) type BalanceOf = as OnChargeTransaction>::Balance; + /// Type aliases used for interaction with `OnChargeTransaction`. pub(crate) type OnChargeTransactionOf = ::OnChargeTransaction; -/// Balance type alias for balances of the chain's native asset. -pub(crate) type BalanceOf = as OnChargeTransaction>::Balance; -/// Liquidity info type alias. -pub(crate) type LiquidityInfoOf = - as OnChargeTransaction>::LiquidityInfo; -/// Balance type alias for balances of assets that implement the `fungibles` trait. -pub(crate) type AssetBalanceOf = - <::Fungibles as Inspect<::AccountId>>::Balance; -/// Type alias for Asset IDs. -pub(crate) type AssetIdOf = - <::Fungibles as Inspect<::AccountId>>::AssetId; +/// Liquidity info type alias for the chain's native asset. +pub(crate) type NativeLiquidityInfoOf = + as OnChargeTransaction>::LiquidityInfo; -/// Type alias for the interaction of balances with `OnChargeAssetTransaction`. -pub(crate) type ChargeAssetBalanceOf = - <::OnChargeAssetTransaction as OnChargeAssetTransaction>::Balance; -/// Type alias for Asset IDs in their interaction with `OnChargeAssetTransaction`. -pub(crate) type ChargeAssetIdOf = - <::OnChargeAssetTransaction as OnChargeAssetTransaction>::AssetId; -/// Liquidity info type alias for interaction with `OnChargeAssetTransaction`. -pub(crate) type ChargeAssetLiquidityOf = +/// Liquidity info type alias for the chain's assets. +pub(crate) type AssetLiquidityInfoOf = <::OnChargeAssetTransaction as OnChargeAssetTransaction>::LiquidityInfo; /// Used to pass the initial payment info from pre- to post-dispatch. @@ -104,9 +88,9 @@ pub enum InitialPayment { #[default] Nothing, /// The initial fee was paid in the native currency. - Native(LiquidityInfoOf), + Native(NativeLiquidityInfoOf), /// The initial fee was paid in an asset. - Asset((LiquidityInfoOf, BalanceOf, AssetBalanceOf)), + Asset((T::AssetId, AssetLiquidityInfoOf)), } pub use pallet::*; @@ -116,15 +100,18 @@ pub mod pallet { use super::*; #[pallet::config] - pub trait Config: - frame_system::Config + pallet_transaction_payment::Config + pallet_asset_conversion::Config - { + pub trait Config: frame_system::Config + pallet_transaction_payment::Config { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// The fungibles instance used to pay for transactions in assets. - type Fungibles: Balanced; + /// The asset ID type that can be used for transaction payments in addition to a + /// native asset. + type AssetId: AssetId; /// The actual transaction charging logic that charges the fees. - type OnChargeAssetTransaction: OnChargeAssetTransaction; + type OnChargeAssetTransaction: OnChargeAssetTransaction< + Self, + Balance = BalanceOf, + AssetId = Self::AssetId, + >; } #[pallet::pallet] @@ -137,9 +124,9 @@ pub mod pallet { /// has been paid by `who` in an asset `asset_id`. AssetTxFeePaid { who: T::AccountId, - actual_fee: AssetBalanceOf, + actual_fee: BalanceOf, tip: BalanceOf, - asset_id: ChargeAssetIdOf, + asset_id: T::AssetId, }, /// A swap of the refund in native currency back to asset failed. AssetRefundFailed { native_amount_kept: BalanceOf }, @@ -147,33 +134,35 @@ pub mod pallet { } /// Require payment for transaction inclusion and optionally include a tip to gain additional -/// priority in the queue. Allows paying via both `Currency` as well as `fungibles::Balanced`. +/// priority in the queue. /// /// Wraps the transaction logic in [`pallet_transaction_payment`] and extends it with assets. /// An asset ID of `None` falls back to the underlying transaction payment logic via the native /// currency. +/// +/// Transaction payments are processed using different handlers based on the asset type: +/// - Payments with a native asset are charged by +/// [pallet_transaction_payment::Config::OnChargeTransaction]. +/// - Payments with other assets are charged by [Config::OnChargeAssetTransaction]. #[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct ChargeAssetTxPayment { #[codec(compact)] tip: BalanceOf, - asset_id: Option>, + asset_id: Option, } impl ChargeAssetTxPayment where T::RuntimeCall: Dispatchable, - AssetBalanceOf: Send + Sync, - BalanceOf: Send + Sync + Into> + From>, - ChargeAssetIdOf: Send + Sync, { /// Utility constructor. Used only in client/factory code. - pub fn from(tip: BalanceOf, asset_id: Option>) -> Self { + pub fn from(tip: BalanceOf, asset_id: Option) -> Self { Self { tip, asset_id } } - /// Fee withdrawal logic that dispatches to either `OnChargeAssetTransaction` or - /// `OnChargeTransaction`. + /// Fee withdrawal logic that dispatches to either [`Config::OnChargeAssetTransaction`] or + /// [`pallet_transaction_payment::Config::OnChargeTransaction`]. fn withdraw_fee( &self, who: &T::AccountId, @@ -191,25 +180,13 @@ where call, info, asset_id.clone(), - fee.into(), - self.tip.into(), + fee, + self.tip, ) - .map(|(used_for_fee, received_exchanged, asset_consumed)| { - ( - fee, - InitialPayment::Asset(( - used_for_fee.into(), - received_exchanged.into(), - asset_consumed.into(), - )), - ) - }) + .map(|payment| (fee, InitialPayment::Asset((asset_id.clone(), payment)))) } else { - as OnChargeTransaction>::withdraw_fee( - who, call, info, fee, self.tip, - ) - .map(|i| (fee, InitialPayment::Native(i))) - .map_err(|_| -> TransactionValidityError { InvalidTransaction::Payment.into() }) + T::OnChargeTransaction::withdraw_fee(who, call, info, fee, self.tip) + .map(|payment| (fee, InitialPayment::Native(payment))) } } } @@ -228,14 +205,8 @@ impl core::fmt::Debug for ChargeAssetTxPayment { impl SignedExtension for ChargeAssetTxPayment where T::RuntimeCall: Dispatchable, - AssetBalanceOf: Send + Sync, - BalanceOf: Send - + Sync - + From - + Into> - + Into> - + From>, - ChargeAssetIdOf: Send + Sync, + BalanceOf: Send + Sync, + T::AssetId: Send + Sync, { const IDENTIFIER: &'static str = "ChargeAssetTxPayment"; type AccountId = T::AccountId; @@ -248,8 +219,6 @@ where Self::AccountId, // imbalance resulting from withdrawing the fee InitialPayment, - // asset_id for the transaction payment - Option>, ); fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> { @@ -277,7 +246,7 @@ where len: usize, ) -> Result { let (_fee, initial_payment) = self.withdraw_fee(who, call, info, len)?; - Ok((self.tip, who.clone(), initial_payment, self.asset_id)) + Ok((self.tip, who.clone(), initial_payment)) } fn post_dispatch( @@ -285,53 +254,45 @@ where info: &DispatchInfoOf, post_info: &PostDispatchInfoOf, len: usize, - result: &DispatchResult, + _result: &DispatchResult, ) -> Result<(), TransactionValidityError> { - if let Some((tip, who, initial_payment, asset_id)) = pre { + if let Some((tip, who, initial_payment)) = pre { match initial_payment { InitialPayment::Native(already_withdrawn) => { - debug_assert!( - asset_id.is_none(), - "For that payment type the `asset_id` should be None" + let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( + len as u32, info, post_info, tip, ); - pallet_transaction_payment::ChargeTransactionPayment::::post_dispatch( - Some((tip, who, already_withdrawn)), + T::OnChargeTransaction::correct_and_deposit_fee( + &who, info, post_info, - len, - result, + actual_fee, + tip, + already_withdrawn, )?; - }, - InitialPayment::Asset(already_withdrawn) => { - debug_assert!( - asset_id.is_some(), - "For that payment type the `asset_id` should be set" + pallet_transaction_payment::Pallet::::deposit_fee_paid_event( + who, actual_fee, tip, ); + }, + InitialPayment::Asset((asset_id, already_withdrawn)) => { let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( len as u32, info, post_info, tip, ); - - if let Some(asset_id) = asset_id { - let (used_for_fee, received_exchanged, asset_consumed) = already_withdrawn; - let converted_fee = T::OnChargeAssetTransaction::correct_and_deposit_fee( - &who, - info, - post_info, - actual_fee.into(), - tip.into(), - used_for_fee.into(), - received_exchanged.into(), - asset_id.clone(), - asset_consumed.into(), - )?; - - Pallet::::deposit_event(Event::::AssetTxFeePaid { - who, - actual_fee: converted_fee, - tip, - asset_id, - }); - } + let converted_fee = T::OnChargeAssetTransaction::correct_and_deposit_fee( + &who, + info, + post_info, + actual_fee, + tip, + asset_id.clone(), + already_withdrawn, + )?; + Pallet::::deposit_event(Event::::AssetTxFeePaid { + who, + actual_fee: converted_fee, + tip, + asset_id, + }); }, InitialPayment::Nothing => { // `actual_fee` should be zero here for any signed extrinsic. It would be diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs index 245900760de9..acfd43d0a7cb 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs @@ -24,7 +24,7 @@ use frame_support::{ pallet_prelude::*, parameter_types, traits::{ - fungible, + fungible, fungibles, tokens::{ fungible::{NativeFromLeft, NativeOrWithId, UnionOf}, imbalance::ResolveAssetTo, @@ -145,6 +145,22 @@ impl OnUnbalanced::AccountId, } } +pub struct DealWithFungiblesFees; +impl OnUnbalanced> for DealWithFungiblesFees { + fn on_unbalanceds( + mut fees_then_tips: impl Iterator< + Item = fungibles::Credit<::AccountId, NativeAndAssets>, + >, + ) { + if let Some(fees) = fees_then_tips.next() { + FeeUnbalancedAmount::mutate(|a| *a += fees.peek()); + if let Some(tips) = fees_then_tips.next() { + TipUnbalancedAmount::mutate(|a| *a += tips.peek()); + } + } + } +} + #[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; @@ -221,12 +237,14 @@ pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< (NativeOrWithId, NativeOrWithId), >; +type NativeAndAssets = UnionOf, AccountId>; + impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = u128; type AssetKind = NativeOrWithId; - type Assets = UnionOf, AccountId>; + type Assets = NativeAndAssets; type PoolId = (Self::AssetKind, Self::AssetKind); type PoolLocator = Chain< WithFirstAsset, PoolIdToAccountId>, @@ -250,6 +268,7 @@ impl pallet_asset_conversion::Config for Runtime { impl Config for Runtime { type RuntimeEvent = RuntimeEvent; - type Fungibles = Assets; - type OnChargeAssetTransaction = AssetConversionAdapter; + type AssetId = NativeOrWithId; + type OnChargeAssetTransaction = + SwapAssetAdapter; } diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs index 0ef3fb111143..dc7faecd5608 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs @@ -20,11 +20,15 @@ use crate::Config; use alloc::vec; use core::marker::PhantomData; use frame_support::{ - ensure, - traits::{fungible::Inspect, tokens::Balance}, + defensive, ensure, + traits::{ + fungibles, + tokens::{Balance, Fortitude, Precision, Preservation}, + Defensive, OnUnbalanced, SameOrOther, + }, unsigned::TransactionValidityError, }; -use pallet_asset_conversion::Swap; +use pallet_asset_conversion::{QuotePrice, SwapCredit}; use sp_runtime::{ traits::{DispatchInfoOf, Get, PostDispatchInfoOf, Zero}, transaction_validity::InvalidTransaction, @@ -50,150 +54,216 @@ pub trait OnChargeAssetTransaction { asset_id: Self::AssetId, fee: Self::Balance, tip: Self::Balance, - ) -> Result< - (LiquidityInfoOf, Self::LiquidityInfo, AssetBalanceOf), - TransactionValidityError, - >; + ) -> Result; /// Refund any overpaid fees and deposit the corrected amount. /// The actual fee gets calculated once the transaction is executed. /// /// Note: The `fee` already includes the `tip`. /// - /// Returns the fee and tip in the asset used for payment as (fee, tip). + /// Returns the amount of `asset_id` that was used for the payment. fn correct_and_deposit_fee( who: &T::AccountId, dispatch_info: &DispatchInfoOf, post_info: &PostDispatchInfoOf, corrected_fee: Self::Balance, tip: Self::Balance, - fee_paid: LiquidityInfoOf, - received_exchanged: Self::LiquidityInfo, asset_id: Self::AssetId, - initial_asset_consumed: AssetBalanceOf, - ) -> Result, TransactionValidityError>; + already_withdraw: Self::LiquidityInfo, + ) -> Result, TransactionValidityError>; } -/// Implements the asset transaction for a balance to asset converter (implementing [`Swap`]). +/// Means to withdraw, correct and deposit fees in the asset accepted by the system. /// -/// The converter is given the complete fee in terms of the asset used for the transaction. -pub struct AssetConversionAdapter(PhantomData<(C, CON, N)>); +/// The type uses the [`SwapCredit`] implementation to swap the asset used by a user for the fee +/// payment for the asset accepted as a fee payment be the system. +/// +/// Parameters: +/// - `A`: The asset identifier that system accepts as a fee payment (eg. native asset). +/// - `F`: The fungibles registry that can handle assets provided by user and the `A` asset. +/// - `S`: The swap implementation that can swap assets provided by user for the `A` asset. +/// - OU: The handler for withdrawn `fee` and `tip`, passed in the respective order to +/// [OnUnbalanced::on_unbalanceds]. +/// - `T`: The pallet's configuration. +pub struct SwapAssetAdapter(PhantomData<(A, F, S, OU)>); -/// Default implementation for a runtime instantiating this pallet, an asset to native swapper. -impl OnChargeAssetTransaction for AssetConversionAdapter +impl OnChargeAssetTransaction for SwapAssetAdapter where - N: Get, + A: Get, + F: fungibles::Balanced, AssetId = T::AssetId>, + S: SwapCredit< + T::AccountId, + Balance = BalanceOf, + AssetKind = T::AssetId, + Credit = fungibles::Credit, + > + QuotePrice, AssetKind = T::AssetId>, + OU: OnUnbalanced>, T: Config, - C: Inspect<::AccountId>, - CON: Swap, AssetKind = T::AssetKind>, - BalanceOf: Into>, - T::AssetKind: From>, - BalanceOf: IsType<::AccountId>>::Balance>, { + type AssetId = T::AssetId; type Balance = BalanceOf; - type AssetId = AssetIdOf; - type LiquidityInfo = BalanceOf; + type LiquidityInfo = (fungibles::Credit, BalanceOf); - /// Swap & withdraw the predicted fee from the transaction origin. - /// - /// Note: The `fee` already includes the `tip`. - /// - /// Returns the total amount in native currency received by exchanging the `asset_id` and the - /// amount in native currency used to pay the fee. fn withdraw_fee( who: &T::AccountId, - call: &T::RuntimeCall, - info: &DispatchInfoOf, + _call: &T::RuntimeCall, + _dispatch_info: &DispatchInfoOf<::RuntimeCall>, asset_id: Self::AssetId, - fee: BalanceOf, - tip: BalanceOf, - ) -> Result< - (LiquidityInfoOf, Self::LiquidityInfo, AssetBalanceOf), - TransactionValidityError, - > { - // convert the asset into native currency - let ed = C::minimum_balance(); - let native_asset_required = - if C::balance(&who) >= ed.saturating_add(fee.into()) { fee } else { fee + ed.into() }; - - let asset_consumed = CON::swap_tokens_for_exact_tokens( - who.clone(), - vec![asset_id.into(), N::get()], - native_asset_required, - None, - who.clone(), - true, + fee: Self::Balance, + _tip: Self::Balance, + ) -> Result { + if asset_id == A::get() { + // The `asset_id` is the target asset, we do not need to swap. + let fee_credit = F::withdraw( + asset_id.clone(), + who, + fee, + Precision::Exact, + Preservation::Preserve, + Fortitude::Polite, + ) + .map_err(|_| InvalidTransaction::Payment)?; + + return Ok((fee_credit, fee)); + } + + // Quote the amount of the `asset_id` needed to pay the fee in the asset `A`. + let asset_fee = + S::quote_price_tokens_for_exact_tokens(asset_id.clone(), A::get(), fee, true) + .ok_or(InvalidTransaction::Payment)?; + + // Withdraw the `asset_id` credit for the swap. + let asset_fee_credit = F::withdraw( + asset_id.clone(), + who, + asset_fee, + Precision::Exact, + Preservation::Preserve, + Fortitude::Polite, ) - .map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?; + .map_err(|_| InvalidTransaction::Payment)?; - ensure!(asset_consumed > Zero::zero(), InvalidTransaction::Payment); + let (fee_credit, change) = match S::swap_tokens_for_exact_tokens( + vec![asset_id, A::get()], + asset_fee_credit, + fee, + ) { + Ok((fee_credit, change)) => (fee_credit, change), + Err((credit_in, _)) => { + defensive!("Fee swap should pass for the quoted amount"); + let _ = F::resolve(who, credit_in).defensive_proof("Should resolve the credit"); + return Err(InvalidTransaction::Payment.into()) + }, + }; - // charge the fee in native currency - ::withdraw_fee(who, call, info, fee, tip) - .map(|r| (r, native_asset_required, asset_consumed.into())) + // Since the exact price for `fee` has been quoted, the change should be zero. + ensure!(change.peek().is_zero(), InvalidTransaction::Payment); + + Ok((fee_credit, asset_fee)) } - /// Correct the fee and swap the refund back to asset. - /// - /// Note: The `corrected_fee` already includes the `tip`. - /// Note: Is the ED wasn't needed, the `received_exchanged` will be equal to `fee_paid`, or - /// `fee_paid + ed` otherwise. fn correct_and_deposit_fee( who: &T::AccountId, - dispatch_info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, - corrected_fee: BalanceOf, - tip: BalanceOf, - fee_paid: LiquidityInfoOf, - received_exchanged: Self::LiquidityInfo, + _dispatch_info: &DispatchInfoOf<::RuntimeCall>, + _post_info: &PostDispatchInfoOf<::RuntimeCall>, + corrected_fee: Self::Balance, + tip: Self::Balance, asset_id: Self::AssetId, - initial_asset_consumed: AssetBalanceOf, - ) -> Result, TransactionValidityError> { - // Refund the native asset to the account that paid the fees (`who`). - // The `who` account will receive the "fee_paid - corrected_fee" refund. - ::correct_and_deposit_fee( - who, - dispatch_info, - post_info, - corrected_fee, - tip, - fee_paid, - )?; - - // calculate the refund in native asset, to swap back to the desired `asset_id` - let swap_back = received_exchanged.saturating_sub(corrected_fee); - let mut asset_refund = Zero::zero(); - if !swap_back.is_zero() { - // If this fails, the account might have dropped below the existential balance or there - // is not enough liquidity left in the pool. In that case we don't throw an error and - // the account will keep the native currency. - match CON::swap_exact_tokens_for_tokens( - who.clone(), // we already deposited the native to `who` - vec![ - N::get(), // we provide the native - asset_id.into(), // we want asset_id back - ], - swap_back, // amount of the native asset to convert to `asset_id` - None, // no minimum amount back - who.clone(), // we will refund to `who` - false, // no need to keep alive + already_withdrawn: Self::LiquidityInfo, + ) -> Result, TransactionValidityError> { + let (fee_paid, initial_asset_consumed) = already_withdrawn; + let refund_amount = fee_paid.peek().saturating_sub(corrected_fee); + let (fee_in_asset, adjusted_paid) = if refund_amount.is_zero() || + F::total_balance(asset_id.clone(), who).is_zero() + { + // Nothing to refund or the account was removed be the dispatched function. + (initial_asset_consumed, fee_paid) + } else if asset_id == A::get() { + // The `asset_id` is the target asset, we do not need to swap. + let (refund, fee_paid) = fee_paid.split(refund_amount); + if let Err(refund) = F::resolve(who, refund) { + let fee_paid = fee_paid.merge(refund).map_err(|_| { + defensive!("`fee_paid` and `refund` are credits of the same asset."); + InvalidTransaction::Payment + })?; + (initial_asset_consumed, fee_paid) + } else { + (fee_paid.peek().saturating_sub(refund_amount), fee_paid) + } + } else { + // Check if the refund amount can be swapped back into the asset used by `who` for fee + // payment. + let refund_asset_amount = S::quote_price_exact_tokens_for_tokens( + A::get(), + asset_id.clone(), + refund_amount, + true, ) - .ok() - { - Some(acquired) => { - asset_refund = acquired - .try_into() - .map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?; - }, - None => { - Pallet::::deposit_event(Event::::AssetRefundFailed { - native_amount_kept: swap_back, - }); - }, + // No refund given if it cannot be swapped back. + .unwrap_or(Zero::zero()); + + let debt = if refund_asset_amount.is_zero() { + fungibles::Debt::::zero(asset_id.clone()) + } else { + // Deposit the refund before the swap to ensure it can be processed. + match F::deposit(asset_id.clone(), &who, refund_asset_amount, Precision::BestEffort) + { + Ok(debt) => debt, + // No refund given since it cannot be deposited. + Err(_) => fungibles::Debt::::zero(asset_id.clone()), + } + }; + + if debt.peek().is_zero() { + // No refund given. + (initial_asset_consumed, fee_paid) + } else { + let (refund, adjusted_paid) = fee_paid.split(refund_amount); + match S::swap_exact_tokens_for_tokens( + vec![A::get(), asset_id], + refund, + Some(refund_asset_amount), + ) { + Ok(refund_asset) => { + match refund_asset.offset(debt) { + Ok(SameOrOther::None) => {}, + // This arm should never be reached, as the amount of `debt` is + // expected to be exactly equal to the amount of `refund_asset` credit. + _ => { + defensive!("Debt should be equal to the refund credit"); + return Err(InvalidTransaction::Payment.into()) + }, + }; + ( + initial_asset_consumed.saturating_sub(refund_asset_amount.into()), + adjusted_paid, + ) + }, + // The error should not occur since swap was quoted before. + Err((refund, _)) => { + defensive!("Refund swap should pass for the quoted amount"); + match F::settle(who, debt, Preservation::Expendable) { + Ok(dust) => ensure!(dust.peek().is_zero(), InvalidTransaction::Payment), + // The error should not occur as the `debt` was just withdrawn above. + Err(_) => { + defensive!("Should settle the debt"); + return Err(InvalidTransaction::Payment.into()) + }, + }; + let adjusted_paid = adjusted_paid.merge(refund).map_err(|_| { + // The error should never occur since `adjusted_paid` and `refund` are + // credits of the same asset. + InvalidTransaction::Payment + })?; + (initial_asset_consumed, adjusted_paid) + }, + } } - } + }; - let actual_paid = initial_asset_consumed.saturating_sub(asset_refund); - Ok(actual_paid) + // Handle the imbalance (fee and tip separately). + let (tip, fee) = adjusted_paid.split(tip); + OU::on_unbalanceds(Some(fee).into_iter().chain(Some(tip))); + Ok(fee_in_asset) } } diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs index aa2f26f3a6a8..aab657199533 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs @@ -22,6 +22,7 @@ use frame_support::{ traits::{ fungible::{Inspect, NativeOrWithId}, fungibles::{Inspect as FungiblesInspect, Mutate}, + tokens::{Fortitude, Precision, Preservation}, }, weights::Weight, }; @@ -239,7 +240,7 @@ fn transaction_payment_in_asset_possible() { let fee_in_asset = input_quote.unwrap(); assert_eq!(Assets::balance(asset_id, caller), balance); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) + let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_5), len) .unwrap(); // assert that native balance is not used @@ -297,7 +298,7 @@ fn transaction_payment_in_asset_fails_if_no_pool_for_that_asset() { assert_eq!(Assets::balance(asset_id, caller), balance); let len = 10; - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)).pre_dispatch( + let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())).pre_dispatch( &caller, CALL, &info_from_weight(WEIGHT_5), @@ -352,7 +353,7 @@ fn transaction_payment_without_fee() { assert_eq!(input_quote, Some(201)); let fee_in_asset = input_quote.unwrap(); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) + let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_5), len) .unwrap(); @@ -429,7 +430,7 @@ fn asset_transaction_payment_with_tip_and_refund() { assert_eq!(input_quote, Some(1206)); let fee_in_asset = input_quote.unwrap(); - let pre = ChargeAssetTxPayment::::from(tip, Some(asset_id)) + let pre = ChargeAssetTxPayment::::from(tip, Some(asset_id.into())) .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_100), len) .unwrap(); assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); @@ -512,31 +513,21 @@ fn payment_from_account_with_only_assets() { let len = 10; let fee_in_native = base_weight + weight + len as u64; - let ed = Balances::minimum_balance(); let fee_in_asset = AssetConversion::quote_price_tokens_for_exact_tokens( NativeOrWithId::WithId(asset_id), NativeOrWithId::Native, - fee_in_native + ed, + fee_in_native, true, ) .unwrap(); - assert_eq!(fee_in_asset, 301); + assert_eq!(fee_in_asset, 201); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) + let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_5), len) .unwrap(); - assert_eq!(Balances::free_balance(caller), ed); // check that fee was charged in the given asset assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); - let refund = AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrWithId::Native, - NativeOrWithId::WithId(asset_id), - ed, - true, - ) - .unwrap(); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( Some(pre), &info_from_weight(WEIGHT_5), @@ -544,7 +535,7 @@ fn payment_from_account_with_only_assets() { len, &Ok(()) )); - assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset + refund); + assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); assert_eq!(Balances::free_balance(caller), 0); assert_eq!(TipUnbalancedAmount::get(), 0); @@ -587,7 +578,7 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() { // there will be no conversion when the fee is zero { - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) + let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) .pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len) .unwrap(); // `Pays::No` implies there are no fees @@ -613,7 +604,7 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() { ) .unwrap(); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) + let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) .unwrap(); assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); @@ -663,14 +654,14 @@ fn post_dispatch_fee_is_zero_if_pre_dispatch_fee_is_zero() { // calculated fee is greater than 0 assert!(fee > 0); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) + let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) .pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len) .unwrap(); // `Pays::No` implies no pre-dispatch fees assert_eq!(Assets::balance(asset_id, caller), balance); - let (_tip, _who, initial_payment, _asset_id) = ⪯ + let (_tip, _who, initial_payment) = ⪯ let not_paying = match initial_payment { &InitialPayment::Nothing => true, _ => false, @@ -740,3 +731,139 @@ fn post_dispatch_fee_is_zero_if_unsigned_pre_dispatch_fee_is_zero() { assert_eq!(Assets::balance(asset_id, caller), balance); }); } + +#[test] +fn fee_with_native_asset_passed_with_id() { + let base_weight = 5; + let balance_factor = 100; + ExtBuilder::default() + .balance_factor(balance_factor) + .base_weight(Weight::from_parts(base_weight, 0)) + .build() + .execute_with(|| { + let caller = 1; + let caller_balance = 1000; + // native asset + let asset_id = NativeOrWithId::Native; + // assert that native balance is not necessary + assert_eq!(Balances::free_balance(caller), caller_balance); + + let tip = 10; + let weight = 100; + let len = 5; + let initial_fee = base_weight + weight + len as u64 + tip; + + let pre = ChargeAssetTxPayment::::from(tip, Some(asset_id.into())) + .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_100), len) + .unwrap(); + assert_eq!(Balances::free_balance(caller), caller_balance - initial_fee); + + let final_weight = 50; + let expected_fee = initial_fee - final_weight; + + assert_ok!(ChargeAssetTxPayment::::post_dispatch( + Some(pre), + &info_from_weight(WEIGHT_100), + &post_info_from_weight(WEIGHT_50), + len, + &Ok(()) + )); + + assert_eq!(Balances::free_balance(caller), caller_balance - expected_fee); + + assert_eq!(TipUnbalancedAmount::get(), tip); + assert_eq!(FeeUnbalancedAmount::get(), expected_fee - tip); + }); +} + +#[test] +fn transfer_add_and_remove_account() { + let base_weight = 5; + let balance_factor = 100; + ExtBuilder::default() + .balance_factor(balance_factor) + .base_weight(Weight::from_parts(base_weight, 0)) + .build() + .execute_with(|| { + System::set_block_number(1); + + // create the asset + let asset_id = 1; + let min_balance = 2; + assert_ok!(Assets::force_create( + RuntimeOrigin::root(), + asset_id.into(), + 42, /* owner */ + true, /* is_sufficient */ + min_balance, + )); + + setup_lp(asset_id, balance_factor); + + // mint into the caller account + let caller = 222; + let beneficiary = ::Lookup::unlookup(caller); + let balance = 10000; + + assert_eq!(Balances::free_balance(caller), 0); + assert_ok!(Assets::mint_into(asset_id.into(), &beneficiary, balance)); + assert_eq!(Assets::balance(asset_id, caller), balance); + + let weight = 100; + let tip = 5; + let len = 10; + let fee_in_native = base_weight + weight + len as u64 + tip; + let input_quote = AssetConversion::quote_price_tokens_for_exact_tokens( + NativeOrWithId::WithId(asset_id), + NativeOrWithId::Native, + fee_in_native, + true, + ); + assert!(!input_quote.unwrap().is_zero()); + + let fee_in_asset = input_quote.unwrap(); + let pre = ChargeAssetTxPayment::::from(tip, Some(asset_id.into())) + .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_100), len) + .unwrap(); + + assert_eq!(Assets::balance(asset_id, &caller), balance - fee_in_asset); + + // remove caller account. + assert_ok!(Assets::burn_from( + asset_id, + &caller, + Assets::balance(asset_id, &caller), + Preservation::Expendable, + Precision::Exact, + Fortitude::Force + )); + + let final_weight = 50; + let final_fee_in_native = fee_in_native - final_weight - tip; + let token_refund = AssetConversion::quote_price_exact_tokens_for_tokens( + NativeOrWithId::Native, + NativeOrWithId::WithId(asset_id), + fee_in_native - final_fee_in_native - tip, + true, + ) + .unwrap(); + + // make sure the refund amount is enough to create the account. + assert!(token_refund >= min_balance); + + assert_ok!(ChargeAssetTxPayment::::post_dispatch( + Some(pre), + &info_from_weight(WEIGHT_100), + &post_info_from_weight(WEIGHT_50), + len, + &Ok(()) + )); + + // fee paid with no refund. + assert_eq!(TipUnbalancedAmount::get(), tip); + assert_eq!(FeeUnbalancedAmount::get(), fee_in_native - tip); + + // caller account removed. + assert_eq!(Assets::balance(asset_id, caller), 0); + }); +} diff --git a/substrate/frame/transaction-payment/src/lib.rs b/substrate/frame/transaction-payment/src/lib.rs index 7df658a4732e..c17ab393b5d3 100644 --- a/substrate/frame/transaction-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/src/lib.rs @@ -667,6 +667,11 @@ impl Pallet { let capped_weight = weight.min(T::BlockWeights::get().max_block); T::WeightToFee::weight_to_fee(&capped_weight) } + + /// Deposit the [`Event::TransactionFeePaid`] event. + pub fn deposit_fee_paid_event(who: T::AccountId, actual_fee: BalanceOf, tip: BalanceOf) { + Self::deposit_event(Event::TransactionFeePaid { who, actual_fee, tip }); + } } impl Convert> for Pallet From b5fdfe8f50def754da0a1c6a61d615eec4ea9245 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 13:51:54 +0000 Subject: [PATCH 003/382] Bump peter-evans/create-pull-request from 5.0.0 to 6.1.0 (#5093) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 5.0.0 to 6.1.0.
Release notes

Sourced from peter-evans/create-pull-request's releases.

Create Pull Request v6.1.0

✨ Adds pull-request-branch as an action output.

What's Changed

Full Changelog: https://github.com/peter-evans/create-pull-request/compare/v6.0.5...v6.1.0

Create Pull Request v6.0.5

⚙️ Fixes an issue with proxy support for users that run self-hosted behind a proxy.

What's Changed

Full Changelog: https://github.com/peter-evans/create-pull-request/compare/v6.0.4...v6.0.5

Create Pull Request v6.0.4

⚡ Improves performance in some cases for very large git repositories.

What's Changed

Full Changelog: https://github.com/peter-evans/create-pull-request/compare/v6.0.3...v6.0.4

Create Pull Request v6.0.3

⚡ Improves performance of the push-to-fork feature.

... (truncated)

Commits
  • c5a7806 feat: add branch name output (#2995)
  • 4383ba9 build: update distribution (#2990)
  • 36f7648 build(deps): bump undici from 6.18.2 to 6.19.2 (#2977)
  • 5f7c158 build(deps-dev): bump @​types/node from 18.19.34 to 18.19.36 (#2976)
  • db1713d build(deps-dev): bump ts-jest from 29.1.4 to 29.1.5 (#2975)
  • ca98a71 build(deps-dev): bump ws from 8.11.0 to 8.17.1 (#2970)
  • ce00808 build(deps-dev): bump braces from 3.0.2 to 3.0.3 (#2962)
  • 7318c0b build(deps-dev): bump prettier from 3.3.0 to 3.3.2 (#2959)
  • e30bbbb build: update distribution (#2947)
  • bad19b8 build(deps-dev): bump @​types/node from 18.19.33 to 18.19.34 (#2935)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=peter-evans/create-pull-request&package-manager=github_actions&previous-version=5.0.0&new-version=6.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/misc-sync-templates.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/misc-sync-templates.yml b/.github/workflows/misc-sync-templates.yml index 835a3116d446..6fe907c76aca 100644 --- a/.github/workflows/misc-sync-templates.yml +++ b/.github/workflows/misc-sync-templates.yml @@ -158,7 +158,7 @@ jobs: timeout-minutes: 90 - name: Create PR on failure if: failure() && steps.check-compilation.outcome == 'failure' - uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 # v5 + uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v5 with: path: "${{ env.template-path }}" token: ${{ steps.app_token.outputs.token }} From 2c175a9f062e0b26f01121d5a89da3b865a9b3fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 13:51:55 +0000 Subject: [PATCH 004/382] Bump docker/build-push-action from 5.1.0 to 6.5.0 (#5108) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.1.0 to 6.5.0.
Release notes

Sourced from docker/build-push-action's releases.

v6.5.0

Full Changelog: https://github.com/docker/build-push-action/compare/v6.4.1...v6.5.0

v6.4.1

Full Changelog: https://github.com/docker/build-push-action/compare/v6.4.0...v6.4.1

v6.4.0

Full Changelog: https://github.com/docker/build-push-action/compare/v6.3.0...v6.4.0

v6.3.0

Full Changelog: https://github.com/docker/build-push-action/compare/v6.2.0...v6.3.0

v6.2.0

Full Changelog: https://github.com/docker/build-push-action/compare/v6.1.0...v6.2.0

v6.1.0

Full Changelog: https://github.com/docker/build-push-action/compare/v6.0.2...v6.1.0

v6.0.2

Full Changelog: https://github.com/docker/build-push-action/compare/v6.0.1...v6.0.2

v6.0.1

Full Changelog: https://github.com/docker/build-push-action/compare/v6.0.0...v6.0.1

v6.0.0

[!NOTE]

... (truncated)

Commits
  • 5176d81 Merge pull request #1191 from docker/dependabot/npm_and_yarn/docker/actions-t...
  • ec10ae8 chore: update generated content
  • 597e8fc chore(deps): Bump @​docker/actions-toolkit from 0.34.0 to 0.35.0
  • e050dfa Merge pull request #1186 from docker/dependabot/npm_and_yarn/docker/actions-t...
  • d1fcdb6 chore: update generated content
  • a6067b9 chore(deps): Bump @​docker/actions-toolkit from 0.33.0 to 0.34.0
  • 1ca370b Merge pull request #1183 from crazy-max/revert-gha-cache-to
  • 2c95ebe chore: update generated content
  • d189d0e Revert "set repository and ghtoken attributes for gha cache type"
  • a254f8c Merge pull request #1179 from docker/dependabot/npm_and_yarn/docker/actions-t...
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=docker/build-push-action&package-manager=github_actions&previous-version=5.1.0&new-version=6.5.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release-50_publish-docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index 723883eaf64c..2255f7bda5dd 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -288,7 +288,7 @@ jobs: - name: Build and push id: docker_build - uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0 + uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 # v6.5.0 with: push: true file: docker/dockerfiles/polkadot/polkadot_injected_debian.Dockerfile From 4c0918c978defa46b4559f215a493273c34e4ba0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 13:52:05 +0000 Subject: [PATCH 005/382] Bump docker/login-action from 3.0.0 to 3.3.0 (#5109) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [docker/login-action](https://github.com/docker/login-action) from 3.0.0 to 3.3.0.
Release notes

Sourced from docker/login-action's releases.

v3.3.0

Full Changelog: https://github.com/docker/login-action/compare/v3.2.0...v3.3.0

v3.2.0

Full Changelog: https://github.com/docker/login-action/compare/v3.1.0...v3.2.0

v3.1.0

Full Changelog: https://github.com/docker/login-action/compare/v3.0.0...v3.1.0

Commits
  • 9780b0c Merge pull request #741 from docker/dependabot/npm_and_yarn/proxy-agent-depen...
  • 2fa130c chore: update generated content
  • 5e87b2a build(deps): bump https-proxy-agent
  • e039495 Merge pull request #754 from docker/dependabot/npm_and_yarn/docker/actions-to...
  • 9af18aa chore: update generated content
  • 668190a switch to Docker exec
  • be5150d build(deps): bump @​docker/actions-toolkit from 0.24.0 to 0.35.0
  • e80ebca Merge pull request #730 from docker/dependabot/npm_and_yarn/braces-3.0.3
  • 75ee3ea Merge pull request #733 from docker/dependabot/github_actions/docker/bake-act...
  • 793c19c build(deps): bump docker/bake-action from 4 to 5
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=docker/login-action&package-manager=github_actions&previous-version=3.0.0&new-version=3.3.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release-50_publish-docker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index 2255f7bda5dd..63124a489e9d 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -210,7 +210,7 @@ jobs: ./docker/scripts/build-injected.sh - name: Login to Dockerhub - uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: username: ${{ secrets.CUMULUS_DOCKERHUB_USERNAME }} password: ${{ secrets.CUMULUS_DOCKERHUB_TOKEN }} @@ -275,7 +275,7 @@ jobs: ${{ runner.os }}-buildx- - name: Login to Docker Hub - uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: username: ${{ secrets.POLKADOT_DOCKERHUB_USERNAME }} password: ${{ secrets.POLKADOT_DOCKERHUB_TOKEN }} From a164639f7f1223634fb01cf38dab49622ab940ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 16:13:38 +0200 Subject: [PATCH 006/382] Bump paritytech/review-bot from 2.4.0 to 2.5.0 (#5057) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [paritytech/review-bot](https://github.com/paritytech/review-bot) from 2.4.0 to 2.5.0.
Release notes

Sourced from paritytech/review-bot's releases.

v2.5.0

What's Changed

Full Changelog: https://github.com/paritytech/review-bot/compare/v2.4.1...v2.5.0

v2.4.1

What's Changed

New Contributors

Full Changelog: https://github.com/paritytech/review-bot/compare/v2.4.0...v2.4.1

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=paritytech/review-bot&package-manager=github_actions&previous-version=2.4.0&new-version=2.5.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/review-bot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/review-bot.yml b/.github/workflows/review-bot.yml index f1401406ae47..0d83b75b3bde 100644 --- a/.github/workflows/review-bot.yml +++ b/.github/workflows/review-bot.yml @@ -30,7 +30,7 @@ jobs: with: artifact-name: pr_number - name: "Evaluates PR reviews and assigns reviewers" - uses: paritytech/review-bot@v2.4.0 + uses: paritytech/review-bot@v2.5.0 with: repo-token: ${{ steps.app_token.outputs.token }} team-token: ${{ steps.app_token.outputs.token }} From f4fccd60f16a9e7364eac59f3f64459fcbf70fac Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 24 Jul 2024 18:18:33 +0200 Subject: [PATCH 007/382] Fix after ring-proof api change (#5126) Ring-proof backend API changed to take some params by ref. Bandersnatch-VRF was not updated (Fixed by: https://github.com/w3f/ring-vrf/pull/92 and https://github.com/w3f/ring-vrf/pull/93) --- Cargo.lock | 15 +++++++-------- substrate/primitives/core/Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d085f2d90ff7..bb4636f89650 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -580,7 +580,7 @@ dependencies = [ [[package]] name = "ark-secret-scalar" version = "0.0.2" -source = "git+https://github.com/w3f/ring-vrf?rev=e9782f9#e9782f938629c90f3adb3fff2358bc8d1386af3e" +source = "git+https://github.com/w3f/ring-vrf?rev=0fef826#0fef8266d851932ad25d6b41bc4b34d834d1e11d" dependencies = [ "ark-ec", "ark-ff 0.4.2", @@ -649,7 +649,7 @@ dependencies = [ [[package]] name = "ark-transcript" version = "0.0.2" -source = "git+https://github.com/w3f/ring-vrf?rev=e9782f9#e9782f938629c90f3adb3fff2358bc8d1386af3e" +source = "git+https://github.com/w3f/ring-vrf?rev=0fef826#0fef8266d851932ad25d6b41bc4b34d834d1e11d" dependencies = [ "ark-ff 0.4.2", "ark-serialize 0.4.2", @@ -1399,7 +1399,7 @@ dependencies = [ [[package]] name = "bandersnatch_vrfs" version = "0.0.4" -source = "git+https://github.com/w3f/ring-vrf?rev=e9782f9#e9782f938629c90f3adb3fff2358bc8d1386af3e" +source = "git+https://github.com/w3f/ring-vrf?rev=0fef826#0fef8266d851932ad25d6b41bc4b34d834d1e11d" dependencies = [ "ark-bls12-381", "ark-ec", @@ -1408,8 +1408,6 @@ dependencies = [ "ark-serialize 0.4.2", "ark-std 0.4.0", "dleq_vrf", - "fflonk", - "merlin", "rand_chacha", "rand_core", "ring 0.1.0", @@ -3047,7 +3045,7 @@ dependencies = [ [[package]] name = "common" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof#b273d33f9981e2bb3375ab45faeb537f7ee35224" +source = "git+https://github.com/w3f/ring-proof?rev=665f5f5#665f5f51af5734c7b6d90b985dd6861d4c5b4752" dependencies = [ "ark-ec", "ark-ff 0.4.2", @@ -4973,7 +4971,7 @@ checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" [[package]] name = "dleq_vrf" version = "0.0.2" -source = "git+https://github.com/w3f/ring-vrf?rev=e9782f9#e9782f938629c90f3adb3fff2358bc8d1386af3e" +source = "git+https://github.com/w3f/ring-vrf?rev=0fef826#0fef8266d851932ad25d6b41bc4b34d834d1e11d" dependencies = [ "ark-ec", "ark-ff 0.4.2", @@ -16291,13 +16289,14 @@ dependencies = [ [[package]] name = "ring" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof#b273d33f9981e2bb3375ab45faeb537f7ee35224" +source = "git+https://github.com/w3f/ring-proof?rev=665f5f5#665f5f51af5734c7b6d90b985dd6861d4c5b4752" dependencies = [ "ark-ec", "ark-ff 0.4.2", "ark-poly", "ark-serialize 0.4.2", "ark-std 0.4.0", + "arrayvec 0.7.4", "blake2 0.10.6", "common", "fflonk", diff --git a/substrate/primitives/core/Cargo.toml b/substrate/primitives/core/Cargo.toml index 2ba4f959a5f9..258caa06fcfe 100644 --- a/substrate/primitives/core/Cargo.toml +++ b/substrate/primitives/core/Cargo.toml @@ -63,7 +63,7 @@ secp256k1 = { features = ["alloc", "recovery"], optional = true, workspace = tru # bls crypto w3f-bls = { optional = true, workspace = true } # bandersnatch crypto -bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", rev = "e9782f9", default-features = false, features = ["substrate-curves"], optional = true } +bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", rev = "0fef826", default-features = false, features = ["substrate-curves"], optional = true } [dev-dependencies] criterion = { workspace = true, default-features = true } From f5e7eaf610b50c6a6e3f65649908100ce8bea5b0 Mon Sep 17 00:00:00 2001 From: Shree Vatsa N Date: Wed, 24 Jul 2024 22:13:41 +0530 Subject: [PATCH 008/382] membership: Restructure pallet into separate files (#4536) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - What does this PR do? This PR separates `membership` pallet into separate files for `lib`, `mock` & `tests`. - Why are these changes needed? Currently `membership` pallet consists of `lib`, `mock` & `tests` written into a single file. Refactor it into separate files which makes it inline to other pallets and improves readability. - How were these changes implemented and what do they affect? The PR will not have any affect. Signed-off-by: Shreevatsa N --------- Signed-off-by: Shreevatsa N Co-authored-by: Bastian Köcher Co-authored-by: Bastian Köcher --- .../frame/membership/src/benchmarking.rs | 179 +++++++ substrate/frame/membership/src/lib.rs | 499 +----------------- substrate/frame/membership/src/mock.rs | 117 ++++ substrate/frame/membership/src/tests.rs | 217 ++++++++ 4 files changed, 522 insertions(+), 490 deletions(-) create mode 100644 substrate/frame/membership/src/benchmarking.rs create mode 100644 substrate/frame/membership/src/mock.rs create mode 100644 substrate/frame/membership/src/tests.rs diff --git a/substrate/frame/membership/src/benchmarking.rs b/substrate/frame/membership/src/benchmarking.rs new file mode 100644 index 000000000000..515be7eb5386 --- /dev/null +++ b/substrate/frame/membership/src/benchmarking.rs @@ -0,0 +1,179 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Membership pallet benchmarking. + +use super::{Pallet as Membership, *}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist, BenchmarkError}; +use frame_support::{assert_ok, traits::EnsureOrigin}; +use frame_system::RawOrigin; + +const SEED: u32 = 0; + +fn set_members, I: 'static>(members: Vec, prime: Option) { + let reset_origin = T::ResetOrigin::try_successful_origin() + .expect("ResetOrigin has no successful origin required for the benchmark"); + let prime_origin = T::PrimeOrigin::try_successful_origin() + .expect("PrimeOrigin has no successful origin required for the benchmark"); + + assert_ok!(Membership::::reset_members(reset_origin, members.clone())); + if let Some(prime) = prime.map(|i| members[i].clone()) { + let prime_lookup = T::Lookup::unlookup(prime); + assert_ok!(Membership::::set_prime(prime_origin, prime_lookup)); + } else { + assert_ok!(Membership::::clear_prime(prime_origin)); + } +} + +benchmarks_instance_pallet! { + add_member { + let m in 1 .. (T::MaxMembers::get() - 1); + + let members = (0..m).map(|i| account("member", i, SEED)).collect::>(); + set_members::(members, None); + let new_member = account::("add", m, SEED); + let new_member_lookup = T::Lookup::unlookup(new_member.clone()); + }: { + assert_ok!(Membership::::add_member( + T::AddOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + new_member_lookup, + )); + } verify { + assert!(Members::::get().contains(&new_member)); + #[cfg(test)] crate::mock::clean(); + } + + // the case of no prime or the prime being removed is surely cheaper than the case of + // reporting a new prime via `MembershipChanged`. + remove_member { + let m in 2 .. T::MaxMembers::get(); + + let members = (0..m).map(|i| account("member", i, SEED)).collect::>(); + set_members::(members.clone(), Some(members.len() - 1)); + + let to_remove = members.first().cloned().unwrap(); + let to_remove_lookup = T::Lookup::unlookup(to_remove.clone()); + }: { + assert_ok!(Membership::::remove_member( + T::RemoveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + to_remove_lookup, + )); + } verify { + assert!(!Members::::get().contains(&to_remove)); + // prime is rejigged + assert!(Prime::::get().is_some() && T::MembershipChanged::get_prime().is_some()); + #[cfg(test)] crate::mock::clean(); + } + + // we remove a non-prime to make sure it needs to be set again. + swap_member { + let m in 2 .. T::MaxMembers::get(); + + let members = (0..m).map(|i| account("member", i, SEED)).collect::>(); + set_members::(members.clone(), Some(members.len() - 1)); + let add = account::("member", m, SEED); + let add_lookup = T::Lookup::unlookup(add.clone()); + let remove = members.first().cloned().unwrap(); + let remove_lookup = T::Lookup::unlookup(remove.clone()); + }: { + assert_ok!(Membership::::swap_member( + T::SwapOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + remove_lookup, + add_lookup, + )); + } verify { + assert!(!Members::::get().contains(&remove)); + assert!(Members::::get().contains(&add)); + // prime is rejigged + assert!(Prime::::get().is_some() && T::MembershipChanged::get_prime().is_some()); + #[cfg(test)] crate::mock::clean(); + } + + // er keep the prime common between incoming and outgoing to make sure it is rejigged. + reset_members { + let m in 1 .. T::MaxMembers::get(); + + let members = (1..m+1).map(|i| account("member", i, SEED)).collect::>(); + set_members::(members.clone(), Some(members.len() - 1)); + let mut new_members = (m..2*m).map(|i| account("member", i, SEED)).collect::>(); + }: { + assert_ok!(Membership::::reset_members( + T::ResetOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + new_members.clone(), + )); + } verify { + new_members.sort(); + assert_eq!(Members::::get(), new_members); + // prime is rejigged + assert!(Prime::::get().is_some() && T::MembershipChanged::get_prime().is_some()); + #[cfg(test)] crate::mock::clean(); + } + + change_key { + let m in 1 .. T::MaxMembers::get(); + + // worse case would be to change the prime + let members = (0..m).map(|i| account("member", i, SEED)).collect::>(); + let prime = members.last().cloned().unwrap(); + set_members::(members.clone(), Some(members.len() - 1)); + + let add = account::("member", m, SEED); + let add_lookup = T::Lookup::unlookup(add.clone()); + whitelist!(prime); + }: { + assert_ok!(Membership::::change_key(RawOrigin::Signed(prime.clone()).into(), add_lookup)); + } verify { + assert!(!Members::::get().contains(&prime)); + assert!(Members::::get().contains(&add)); + // prime is rejigged + assert_eq!(Prime::::get().unwrap(), add); + #[cfg(test)] crate::mock::clean(); + } + + set_prime { + let m in 1 .. T::MaxMembers::get(); + let members = (0..m).map(|i| account("member", i, SEED)).collect::>(); + let prime = members.last().cloned().unwrap(); + let prime_lookup = T::Lookup::unlookup(prime.clone()); + set_members::(members, None); + }: { + assert_ok!(Membership::::set_prime( + T::PrimeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + prime_lookup, + )); + } verify { + assert!(Prime::::get().is_some()); + assert!(::get_prime().is_some()); + #[cfg(test)] crate::mock::clean(); + } + + clear_prime { + let members = (0..T::MaxMembers::get()).map(|i| account("member", i, SEED)).collect::>(); + let prime = members.last().cloned().unwrap(); + set_members::(members, None); + }: { + assert_ok!(Membership::::clear_prime( + T::PrimeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + )); + } verify { + assert!(Prime::::get().is_none()); + assert!(::get_prime().is_none()); + #[cfg(test)] crate::mock::clean(); + } + + impl_benchmark_test_suite!(Membership, crate::mock::new_bench_ext(), crate::mock::Test); +} diff --git a/substrate/frame/membership/src/lib.rs b/substrate/frame/membership/src/lib.rs index e38a6ba5d931..bbd17c66be4d 100644 --- a/substrate/frame/membership/src/lib.rs +++ b/substrate/frame/membership/src/lib.rs @@ -35,6 +35,15 @@ use sp_runtime::traits::{StaticLookup, UniqueSaturatedInto}; pub mod migrations; pub mod weights; +#[cfg(test)] +mod mock; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + +#[cfg(test)] +mod tests; + pub use pallet::*; pub use weights::WeightInfo; @@ -403,493 +412,3 @@ impl, I: 'static> SortedMembers for Pallet { } } } - -#[cfg(feature = "runtime-benchmarks")] -mod benchmark { - use super::{Pallet as Membership, *}; - use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist, BenchmarkError}; - use frame_support::{assert_ok, traits::EnsureOrigin}; - use frame_system::RawOrigin; - - const SEED: u32 = 0; - - fn set_members, I: 'static>(members: Vec, prime: Option) { - let reset_origin = T::ResetOrigin::try_successful_origin() - .expect("ResetOrigin has no successful origin required for the benchmark"); - let prime_origin = T::PrimeOrigin::try_successful_origin() - .expect("PrimeOrigin has no successful origin required for the benchmark"); - - assert_ok!(Membership::::reset_members(reset_origin, members.clone())); - if let Some(prime) = prime.map(|i| members[i].clone()) { - let prime_lookup = T::Lookup::unlookup(prime); - assert_ok!(Membership::::set_prime(prime_origin, prime_lookup)); - } else { - assert_ok!(Membership::::clear_prime(prime_origin)); - } - } - - benchmarks_instance_pallet! { - add_member { - let m in 1 .. (T::MaxMembers::get() - 1); - - let members = (0..m).map(|i| account("member", i, SEED)).collect::>(); - set_members::(members, None); - let new_member = account::("add", m, SEED); - let new_member_lookup = T::Lookup::unlookup(new_member.clone()); - }: { - assert_ok!(Membership::::add_member( - T::AddOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, - new_member_lookup, - )); - } verify { - assert!(Members::::get().contains(&new_member)); - #[cfg(test)] crate::tests::clean(); - } - - // the case of no prime or the prime being removed is surely cheaper than the case of - // reporting a new prime via `MembershipChanged`. - remove_member { - let m in 2 .. T::MaxMembers::get(); - - let members = (0..m).map(|i| account("member", i, SEED)).collect::>(); - set_members::(members.clone(), Some(members.len() - 1)); - - let to_remove = members.first().cloned().unwrap(); - let to_remove_lookup = T::Lookup::unlookup(to_remove.clone()); - }: { - assert_ok!(Membership::::remove_member( - T::RemoveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, - to_remove_lookup, - )); - } verify { - assert!(!Members::::get().contains(&to_remove)); - // prime is rejigged - assert!(Prime::::get().is_some() && T::MembershipChanged::get_prime().is_some()); - #[cfg(test)] crate::tests::clean(); - } - - // we remove a non-prime to make sure it needs to be set again. - swap_member { - let m in 2 .. T::MaxMembers::get(); - - let members = (0..m).map(|i| account("member", i, SEED)).collect::>(); - set_members::(members.clone(), Some(members.len() - 1)); - let add = account::("member", m, SEED); - let add_lookup = T::Lookup::unlookup(add.clone()); - let remove = members.first().cloned().unwrap(); - let remove_lookup = T::Lookup::unlookup(remove.clone()); - }: { - assert_ok!(Membership::::swap_member( - T::SwapOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, - remove_lookup, - add_lookup, - )); - } verify { - assert!(!Members::::get().contains(&remove)); - assert!(Members::::get().contains(&add)); - // prime is rejigged - assert!(Prime::::get().is_some() && T::MembershipChanged::get_prime().is_some()); - #[cfg(test)] crate::tests::clean(); - } - - // er keep the prime common between incoming and outgoing to make sure it is rejigged. - reset_members { - let m in 1 .. T::MaxMembers::get(); - - let members = (1..m+1).map(|i| account("member", i, SEED)).collect::>(); - set_members::(members.clone(), Some(members.len() - 1)); - let mut new_members = (m..2*m).map(|i| account("member", i, SEED)).collect::>(); - }: { - assert_ok!(Membership::::reset_members( - T::ResetOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, - new_members.clone(), - )); - } verify { - new_members.sort(); - assert_eq!(Members::::get(), new_members); - // prime is rejigged - assert!(Prime::::get().is_some() && T::MembershipChanged::get_prime().is_some()); - #[cfg(test)] crate::tests::clean(); - } - - change_key { - let m in 1 .. T::MaxMembers::get(); - - // worse case would be to change the prime - let members = (0..m).map(|i| account("member", i, SEED)).collect::>(); - let prime = members.last().cloned().unwrap(); - set_members::(members.clone(), Some(members.len() - 1)); - - let add = account::("member", m, SEED); - let add_lookup = T::Lookup::unlookup(add.clone()); - whitelist!(prime); - }: { - assert_ok!(Membership::::change_key(RawOrigin::Signed(prime.clone()).into(), add_lookup)); - } verify { - assert!(!Members::::get().contains(&prime)); - assert!(Members::::get().contains(&add)); - // prime is rejigged - assert_eq!(Prime::::get().unwrap(), add); - #[cfg(test)] crate::tests::clean(); - } - - set_prime { - let m in 1 .. T::MaxMembers::get(); - let members = (0..m).map(|i| account("member", i, SEED)).collect::>(); - let prime = members.last().cloned().unwrap(); - let prime_lookup = T::Lookup::unlookup(prime.clone()); - set_members::(members, None); - }: { - assert_ok!(Membership::::set_prime( - T::PrimeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, - prime_lookup, - )); - } verify { - assert!(Prime::::get().is_some()); - assert!(::get_prime().is_some()); - #[cfg(test)] crate::tests::clean(); - } - - clear_prime { - let members = (0..T::MaxMembers::get()).map(|i| account("member", i, SEED)).collect::>(); - let prime = members.last().cloned().unwrap(); - set_members::(members, None); - }: { - assert_ok!(Membership::::clear_prime( - T::PrimeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, - )); - } verify { - assert!(Prime::::get().is_none()); - assert!(::get_prime().is_none()); - #[cfg(test)] crate::tests::clean(); - } - - impl_benchmark_test_suite!(Membership, crate::tests::new_bench_ext(), crate::tests::Test); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate as pallet_membership; - - use sp_runtime::{bounded_vec, traits::BadOrigin, BuildStorage}; - - use frame_support::{ - assert_noop, assert_ok, assert_storage_noop, derive_impl, ord_parameter_types, - parameter_types, - traits::{ConstU32, StorageVersion}, - }; - use frame_system::EnsureSignedBy; - - type Block = frame_system::mocking::MockBlock; - - frame_support::construct_runtime!( - pub enum Test - { - System: frame_system, - Membership: pallet_membership, - } - ); - - parameter_types! { - pub static Members: Vec = vec![]; - pub static Prime: Option = None; - } - - #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] - impl frame_system::Config for Test { - type Block = Block; - } - ord_parameter_types! { - pub const One: u64 = 1; - pub const Two: u64 = 2; - pub const Three: u64 = 3; - pub const Four: u64 = 4; - pub const Five: u64 = 5; - } - - pub struct TestChangeMembers; - impl ChangeMembers for TestChangeMembers { - fn change_members_sorted(incoming: &[u64], outgoing: &[u64], new: &[u64]) { - let mut old_plus_incoming = Members::get(); - old_plus_incoming.extend_from_slice(incoming); - old_plus_incoming.sort(); - let mut new_plus_outgoing = new.to_vec(); - new_plus_outgoing.extend_from_slice(outgoing); - new_plus_outgoing.sort(); - assert_eq!(old_plus_incoming, new_plus_outgoing); - - Members::set(new.to_vec()); - Prime::set(None); - } - fn set_prime(who: Option) { - Prime::set(who); - } - fn get_prime() -> Option { - Prime::get() - } - } - - impl InitializeMembers for TestChangeMembers { - fn initialize_members(members: &[u64]) { - MEMBERS.with(|m| *m.borrow_mut() = members.to_vec()); - } - } - - impl Config for Test { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureSignedBy; - type RemoveOrigin = EnsureSignedBy; - type SwapOrigin = EnsureSignedBy; - type ResetOrigin = EnsureSignedBy; - type PrimeOrigin = EnsureSignedBy; - type MembershipInitialized = TestChangeMembers; - type MembershipChanged = TestChangeMembers; - type MaxMembers = ConstU32<10>; - type WeightInfo = (); - } - - pub(crate) fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - // We use default for brevity, but you can configure as desired if needed. - pallet_membership::GenesisConfig:: { - members: bounded_vec![10, 20, 30], - ..Default::default() - } - .assimilate_storage(&mut t) - .unwrap(); - t.into() - } - - #[cfg(feature = "runtime-benchmarks")] - pub(crate) fn new_bench_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::::default().build_storage().unwrap().into() - } - - #[cfg(feature = "runtime-benchmarks")] - pub(crate) fn clean() { - Members::set(vec![]); - Prime::set(None); - } - - #[test] - fn query_membership_works() { - new_test_ext().execute_with(|| { - assert_eq!(crate::Members::::get(), vec![10, 20, 30]); - assert_eq!(MEMBERS.with(|m| m.borrow().clone()), vec![10, 20, 30]); - }); - } - - #[test] - fn prime_member_works() { - new_test_ext().execute_with(|| { - assert_noop!(Membership::set_prime(RuntimeOrigin::signed(4), 20), BadOrigin); - assert_noop!( - Membership::set_prime(RuntimeOrigin::signed(5), 15), - Error::::NotMember - ); - assert_ok!(Membership::set_prime(RuntimeOrigin::signed(5), 20)); - assert_eq!(crate::Prime::::get(), Some(20)); - assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); - - assert_ok!(Membership::clear_prime(RuntimeOrigin::signed(5))); - assert_eq!(crate::Prime::::get(), None); - assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); - }); - } - - #[test] - fn add_member_works() { - new_test_ext().execute_with(|| { - assert_noop!(Membership::add_member(RuntimeOrigin::signed(5), 15), BadOrigin); - assert_noop!( - Membership::add_member(RuntimeOrigin::signed(1), 10), - Error::::AlreadyMember - ); - assert_ok!(Membership::add_member(RuntimeOrigin::signed(1), 15)); - assert_eq!(crate::Members::::get(), vec![10, 15, 20, 30]); - assert_eq!( - MEMBERS.with(|m| m.borrow().clone()), - crate::Members::::get().to_vec() - ); - }); - } - - #[test] - fn remove_member_works() { - new_test_ext().execute_with(|| { - assert_noop!(Membership::remove_member(RuntimeOrigin::signed(5), 20), BadOrigin); - assert_noop!( - Membership::remove_member(RuntimeOrigin::signed(2), 15), - Error::::NotMember - ); - assert_ok!(Membership::set_prime(RuntimeOrigin::signed(5), 20)); - assert_ok!(Membership::remove_member(RuntimeOrigin::signed(2), 20)); - assert_eq!(crate::Members::::get(), vec![10, 30]); - assert_eq!( - MEMBERS.with(|m| m.borrow().clone()), - crate::Members::::get().to_vec() - ); - assert_eq!(crate::Prime::::get(), None); - assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); - }); - } - - #[test] - fn swap_member_works() { - new_test_ext().execute_with(|| { - assert_noop!(Membership::swap_member(RuntimeOrigin::signed(5), 10, 25), BadOrigin); - assert_noop!( - Membership::swap_member(RuntimeOrigin::signed(3), 15, 25), - Error::::NotMember - ); - assert_noop!( - Membership::swap_member(RuntimeOrigin::signed(3), 10, 30), - Error::::AlreadyMember - ); - - assert_ok!(Membership::set_prime(RuntimeOrigin::signed(5), 20)); - assert_ok!(Membership::swap_member(RuntimeOrigin::signed(3), 20, 20)); - assert_eq!(crate::Members::::get(), vec![10, 20, 30]); - assert_eq!(crate::Prime::::get(), Some(20)); - assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); - - assert_ok!(Membership::set_prime(RuntimeOrigin::signed(5), 10)); - assert_ok!(Membership::swap_member(RuntimeOrigin::signed(3), 10, 25)); - assert_eq!(crate::Members::::get(), vec![20, 25, 30]); - assert_eq!( - MEMBERS.with(|m| m.borrow().clone()), - crate::Members::::get().to_vec() - ); - assert_eq!(crate::Prime::::get(), None); - assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); - }); - } - - #[test] - fn swap_member_works_that_does_not_change_order() { - new_test_ext().execute_with(|| { - assert_ok!(Membership::swap_member(RuntimeOrigin::signed(3), 10, 5)); - assert_eq!(crate::Members::::get(), vec![5, 20, 30]); - assert_eq!( - MEMBERS.with(|m| m.borrow().clone()), - crate::Members::::get().to_vec() - ); - }); - } - - #[test] - fn swap_member_with_identical_arguments_changes_nothing() { - new_test_ext().execute_with(|| { - assert_storage_noop!(assert_ok!(Membership::swap_member( - RuntimeOrigin::signed(3), - 10, - 10 - ))); - }); - } - - #[test] - fn change_key_works() { - new_test_ext().execute_with(|| { - assert_ok!(Membership::set_prime(RuntimeOrigin::signed(5), 10)); - assert_noop!( - Membership::change_key(RuntimeOrigin::signed(3), 25), - Error::::NotMember - ); - assert_noop!( - Membership::change_key(RuntimeOrigin::signed(10), 20), - Error::::AlreadyMember - ); - assert_ok!(Membership::change_key(RuntimeOrigin::signed(10), 40)); - assert_eq!(crate::Members::::get(), vec![20, 30, 40]); - assert_eq!( - MEMBERS.with(|m| m.borrow().clone()), - crate::Members::::get().to_vec() - ); - assert_eq!(crate::Prime::::get(), Some(40)); - assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); - }); - } - - #[test] - fn change_key_works_that_does_not_change_order() { - new_test_ext().execute_with(|| { - assert_ok!(Membership::change_key(RuntimeOrigin::signed(10), 5)); - assert_eq!(crate::Members::::get(), vec![5, 20, 30]); - assert_eq!( - MEMBERS.with(|m| m.borrow().clone()), - crate::Members::::get().to_vec() - ); - }); - } - - #[test] - fn change_key_with_same_caller_as_argument_changes_nothing() { - new_test_ext().execute_with(|| { - assert_storage_noop!(assert_ok!(Membership::change_key(RuntimeOrigin::signed(10), 10))); - }); - } - - #[test] - fn reset_members_works() { - new_test_ext().execute_with(|| { - assert_ok!(Membership::set_prime(RuntimeOrigin::signed(5), 20)); - assert_noop!( - Membership::reset_members(RuntimeOrigin::signed(1), bounded_vec![20, 40, 30]), - BadOrigin - ); - - assert_ok!(Membership::reset_members(RuntimeOrigin::signed(4), vec![20, 40, 30])); - assert_eq!(crate::Members::::get(), vec![20, 30, 40]); - assert_eq!( - MEMBERS.with(|m| m.borrow().clone()), - crate::Members::::get().to_vec() - ); - assert_eq!(crate::Prime::::get(), Some(20)); - assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); - - assert_ok!(Membership::reset_members(RuntimeOrigin::signed(4), vec![10, 40, 30])); - assert_eq!(crate::Members::::get(), vec![10, 30, 40]); - assert_eq!( - MEMBERS.with(|m| m.borrow().clone()), - crate::Members::::get().to_vec() - ); - assert_eq!(crate::Prime::::get(), None); - assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); - }); - } - - #[test] - #[should_panic(expected = "Members cannot contain duplicate accounts.")] - fn genesis_build_panics_with_duplicate_members() { - pallet_membership::GenesisConfig:: { - members: bounded_vec![1, 2, 3, 1], - phantom: Default::default(), - } - .build_storage() - .unwrap(); - } - - #[test] - fn migration_v4() { - new_test_ext().execute_with(|| { - use frame_support::traits::PalletInfo; - let old_pallet_name = "OldMembership"; - let new_pallet_name = - ::PalletInfo::name::().unwrap(); - - frame_support::storage::migration::move_pallet( - new_pallet_name.as_bytes(), - old_pallet_name.as_bytes(), - ); - - StorageVersion::new(0).put::(); - - crate::migrations::v4::pre_migrate::(old_pallet_name, new_pallet_name); - crate::migrations::v4::migrate::(old_pallet_name, new_pallet_name); - crate::migrations::v4::post_migrate::(old_pallet_name, new_pallet_name); - }); - } -} diff --git a/substrate/frame/membership/src/mock.rs b/substrate/frame/membership/src/mock.rs new file mode 100644 index 000000000000..2746219e095f --- /dev/null +++ b/substrate/frame/membership/src/mock.rs @@ -0,0 +1,117 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Test utilities + +use super::*; +use crate as pallet_membership; + +use sp_runtime::{bounded_vec, BuildStorage}; + +use frame_support::{derive_impl, ord_parameter_types, parameter_types, traits::ConstU32}; +use frame_system::EnsureSignedBy; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Membership: pallet_membership, + } +); + +parameter_types! { + pub static Members: Vec = vec![]; + pub static Prime: Option = None; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; +} +ord_parameter_types! { + pub const One: u64 = 1; + pub const Two: u64 = 2; + pub const Three: u64 = 3; + pub const Four: u64 = 4; + pub const Five: u64 = 5; +} + +pub struct TestChangeMembers; +impl ChangeMembers for TestChangeMembers { + fn change_members_sorted(incoming: &[u64], outgoing: &[u64], new: &[u64]) { + let mut old_plus_incoming = Members::get(); + old_plus_incoming.extend_from_slice(incoming); + old_plus_incoming.sort(); + let mut new_plus_outgoing = new.to_vec(); + new_plus_outgoing.extend_from_slice(outgoing); + new_plus_outgoing.sort(); + assert_eq!(old_plus_incoming, new_plus_outgoing); + + Members::set(new.to_vec()); + Prime::set(None); + } + fn set_prime(who: Option) { + Prime::set(who); + } + fn get_prime() -> Option { + Prime::get() + } +} + +impl InitializeMembers for TestChangeMembers { + fn initialize_members(members: &[u64]) { + MEMBERS.with(|m| *m.borrow_mut() = members.to_vec()); + } +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureSignedBy; + type RemoveOrigin = EnsureSignedBy; + type SwapOrigin = EnsureSignedBy; + type ResetOrigin = EnsureSignedBy; + type PrimeOrigin = EnsureSignedBy; + type MembershipInitialized = TestChangeMembers; + type MembershipChanged = TestChangeMembers; + type MaxMembers = ConstU32<10>; + type WeightInfo = (); +} + +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + // We use default for brevity, but you can configure as desired if needed. + pallet_membership::GenesisConfig:: { + members: bounded_vec![10, 20, 30], + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + t.into() +} + +#[cfg(feature = "runtime-benchmarks")] +pub(crate) fn new_bench_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::::default().build_storage().unwrap().into() +} + +#[cfg(feature = "runtime-benchmarks")] +pub(crate) fn clean() { + Members::set(vec![]); + Prime::set(None); +} diff --git a/substrate/frame/membership/src/tests.rs b/substrate/frame/membership/src/tests.rs new file mode 100644 index 000000000000..7ab3e75c653f --- /dev/null +++ b/substrate/frame/membership/src/tests.rs @@ -0,0 +1,217 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests for the module. + +use crate as pallet_membership; +use crate::{mock::*, *}; + +use sp_runtime::{bounded_vec, traits::BadOrigin, BuildStorage}; + +use frame_support::{assert_noop, assert_ok, assert_storage_noop, traits::StorageVersion}; + +#[test] +fn query_membership_works() { + new_test_ext().execute_with(|| { + assert_eq!(crate::Members::::get(), vec![10, 20, 30]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), vec![10, 20, 30]); + }); +} + +#[test] +fn prime_member_works() { + new_test_ext().execute_with(|| { + assert_noop!(Membership::set_prime(RuntimeOrigin::signed(4), 20), BadOrigin); + assert_noop!( + Membership::set_prime(RuntimeOrigin::signed(5), 15), + Error::::NotMember + ); + assert_ok!(Membership::set_prime(RuntimeOrigin::signed(5), 20)); + assert_eq!(crate::Prime::::get(), Some(20)); + assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); + + assert_ok!(Membership::clear_prime(RuntimeOrigin::signed(5))); + assert_eq!(crate::Prime::::get(), None); + assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); + }); +} + +#[test] +fn add_member_works() { + new_test_ext().execute_with(|| { + assert_noop!(Membership::add_member(RuntimeOrigin::signed(5), 15), BadOrigin); + assert_noop!( + Membership::add_member(RuntimeOrigin::signed(1), 10), + Error::::AlreadyMember + ); + assert_ok!(Membership::add_member(RuntimeOrigin::signed(1), 15)); + assert_eq!(crate::Members::::get(), vec![10, 15, 20, 30]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), crate::Members::::get().to_vec()); + }); +} + +#[test] +fn remove_member_works() { + new_test_ext().execute_with(|| { + assert_noop!(Membership::remove_member(RuntimeOrigin::signed(5), 20), BadOrigin); + assert_noop!( + Membership::remove_member(RuntimeOrigin::signed(2), 15), + Error::::NotMember + ); + assert_ok!(Membership::set_prime(RuntimeOrigin::signed(5), 20)); + assert_ok!(Membership::remove_member(RuntimeOrigin::signed(2), 20)); + assert_eq!(crate::Members::::get(), vec![10, 30]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), crate::Members::::get().to_vec()); + assert_eq!(crate::Prime::::get(), None); + assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); + }); +} + +#[test] +fn swap_member_works() { + new_test_ext().execute_with(|| { + assert_noop!(Membership::swap_member(RuntimeOrigin::signed(5), 10, 25), BadOrigin); + assert_noop!( + Membership::swap_member(RuntimeOrigin::signed(3), 15, 25), + Error::::NotMember + ); + assert_noop!( + Membership::swap_member(RuntimeOrigin::signed(3), 10, 30), + Error::::AlreadyMember + ); + + assert_ok!(Membership::set_prime(RuntimeOrigin::signed(5), 20)); + assert_ok!(Membership::swap_member(RuntimeOrigin::signed(3), 20, 20)); + assert_eq!(crate::Members::::get(), vec![10, 20, 30]); + assert_eq!(crate::Prime::::get(), Some(20)); + assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); + + assert_ok!(Membership::set_prime(RuntimeOrigin::signed(5), 10)); + assert_ok!(Membership::swap_member(RuntimeOrigin::signed(3), 10, 25)); + assert_eq!(crate::Members::::get(), vec![20, 25, 30]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), crate::Members::::get().to_vec()); + assert_eq!(crate::Prime::::get(), None); + assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); + }); +} + +#[test] +fn swap_member_works_that_does_not_change_order() { + new_test_ext().execute_with(|| { + assert_ok!(Membership::swap_member(RuntimeOrigin::signed(3), 10, 5)); + assert_eq!(crate::Members::::get(), vec![5, 20, 30]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), crate::Members::::get().to_vec()); + }); +} + +#[test] +fn swap_member_with_identical_arguments_changes_nothing() { + new_test_ext().execute_with(|| { + assert_storage_noop!(assert_ok!(Membership::swap_member(RuntimeOrigin::signed(3), 10, 10))); + }); +} + +#[test] +fn change_key_works() { + new_test_ext().execute_with(|| { + assert_ok!(Membership::set_prime(RuntimeOrigin::signed(5), 10)); + assert_noop!( + Membership::change_key(RuntimeOrigin::signed(3), 25), + Error::::NotMember + ); + assert_noop!( + Membership::change_key(RuntimeOrigin::signed(10), 20), + Error::::AlreadyMember + ); + assert_ok!(Membership::change_key(RuntimeOrigin::signed(10), 40)); + assert_eq!(crate::Members::::get(), vec![20, 30, 40]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), crate::Members::::get().to_vec()); + assert_eq!(crate::Prime::::get(), Some(40)); + assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); + }); +} + +#[test] +fn change_key_works_that_does_not_change_order() { + new_test_ext().execute_with(|| { + assert_ok!(Membership::change_key(RuntimeOrigin::signed(10), 5)); + assert_eq!(crate::Members::::get(), vec![5, 20, 30]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), crate::Members::::get().to_vec()); + }); +} + +#[test] +fn change_key_with_same_caller_as_argument_changes_nothing() { + new_test_ext().execute_with(|| { + assert_storage_noop!(assert_ok!(Membership::change_key(RuntimeOrigin::signed(10), 10))); + }); +} + +#[test] +fn reset_members_works() { + new_test_ext().execute_with(|| { + assert_ok!(Membership::set_prime(RuntimeOrigin::signed(5), 20)); + assert_noop!( + Membership::reset_members(RuntimeOrigin::signed(1), bounded_vec![20, 40, 30]), + BadOrigin + ); + + assert_ok!(Membership::reset_members(RuntimeOrigin::signed(4), vec![20, 40, 30])); + assert_eq!(crate::Members::::get(), vec![20, 30, 40]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), crate::Members::::get().to_vec()); + assert_eq!(crate::Prime::::get(), Some(20)); + assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); + + assert_ok!(Membership::reset_members(RuntimeOrigin::signed(4), vec![10, 40, 30])); + assert_eq!(crate::Members::::get(), vec![10, 30, 40]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), crate::Members::::get().to_vec()); + assert_eq!(crate::Prime::::get(), None); + assert_eq!(PRIME.with(|m| *m.borrow()), crate::Prime::::get()); + }); +} + +#[test] +#[should_panic(expected = "Members cannot contain duplicate accounts.")] +fn genesis_build_panics_with_duplicate_members() { + pallet_membership::GenesisConfig:: { + members: bounded_vec![1, 2, 3, 1], + phantom: Default::default(), + } + .build_storage() + .unwrap(); +} + +#[test] +fn migration_v4() { + new_test_ext().execute_with(|| { + use frame_support::traits::PalletInfo; + let old_pallet_name = "OldMembership"; + let new_pallet_name = + ::PalletInfo::name::().unwrap(); + + frame_support::storage::migration::move_pallet( + new_pallet_name.as_bytes(), + old_pallet_name.as_bytes(), + ); + + StorageVersion::new(0).put::(); + + crate::migrations::v4::pre_migrate::(old_pallet_name, new_pallet_name); + crate::migrations::v4::migrate::(old_pallet_name, new_pallet_name); + crate::migrations::v4::post_migrate::(old_pallet_name, new_pallet_name); + }); +} From 60d21e9c4314d5144c9ebdd4fd851ee5f06b0f0d Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Wed, 24 Jul 2024 20:18:58 +0200 Subject: [PATCH 009/382] Introduce a workflow updating the wishlist leaderboards (#5085) - Closes https://github.com/paritytech/eng-automation/issues/11 The workflow periodically updates the leaderboards of the wishlist issues: https://github.com/paritytech/polkadot-sdk/issues/3900 and https://github.com/paritytech/polkadot-sdk/issues/3901 The code is adopted from [here](https://github.com/kianenigma/wishlist-tracker), with slight modifications. Previously, the score could be increased by the same person adding different reactions. Also, some wishes have a score of 0 - even thought there is a wish for them, because the author was not counted. Now, the score is a unique count of upvoters of the desired issue, upvoters of the wish comment, and the author of the wish comment. I changed the format to include the `Last updated:` at the bottom - it will be automatically updated. --- .../scripts/update-wishlist-leaderboard.py | 79 +++++++++++++++++++ .../misc-update-wishlist-leaderboard.yml | 36 +++++++++ 2 files changed, 115 insertions(+) create mode 100644 .github/scripts/update-wishlist-leaderboard.py create mode 100644 .github/workflows/misc-update-wishlist-leaderboard.yml diff --git a/.github/scripts/update-wishlist-leaderboard.py b/.github/scripts/update-wishlist-leaderboard.py new file mode 100644 index 000000000000..caa30bec846d --- /dev/null +++ b/.github/scripts/update-wishlist-leaderboard.py @@ -0,0 +1,79 @@ +from github import Github +import re +import os +from datetime import date + +g = Github(os.getenv("GH_TOKEN")) + +# Regex pattern to match wish format: +wish_pattern = re.compile( + r"I wish for:? (https://github\.com/([a-zA-Z0-9_.-]+)/([a-zA-Z0-9_.-]+)/issues/(\d+))" +) + +wishlist_issue = g.get_repo(os.getenv("WISHLIST_REPOSITORY")).get_issue( + int(os.getenv("WISHLIST_ISSUE_NUMBER")) +) +new_leaderboard = ( + "| Feature Request | Summary | Votes | Status |\n| --- | --- | --- | --- |\n" +) +wishes = {} +issue_details = {} + +for comment in wishlist_issue.get_comments(): + # in the comment body, if there is a string `#(\d)`, replace it with + # https://github.com/paritytech/polkadot-sdk/issues/(number) + updated_body = re.sub( + r"#(\d+)", r"https://github.com/paritytech/polkadot-sdk/issues/\1", comment.body + ) + + matches = wish_pattern.findall(updated_body) + for match in matches: + url, org, repo_name, issue_id = match + issue_key = (url, org, repo_name, issue_id) + if issue_key not in wishes: + wishes[issue_key] = [] + + # Get the author and upvoters of the wish comment. + wishes[issue_key].append(comment.user.id) + wishes[issue_key].extend( + [ + reaction.user.id + for reaction in comment.get_reactions() + if reaction.content in ["+1", "heart", "rocket"] + ] + ) + + # Get upvoters of the desired issue. + desired_issue = g.get_repo(f"{org}/{repo_name}").get_issue(int(issue_id)) + wishes[issue_key].extend( + [ + reaction.user.id + for reaction in desired_issue.get_reactions() + if reaction.content in ["+1", "heart", "rocket"] + ] + ) + issue_details[url] = [ + desired_issue.title, + "👾 Open" if desired_issue.state == "open" else "✅Closed", + ] + +# Count unique wishes - the author of the wish, upvoters of the wish, and upvoters of the desired issue. +for key in wishes: + wishes[key] = len(list(set(wishes[key]))) + +# Sort wishes by count and add to the markdown table +sorted_wishes = sorted(wishes.items(), key=lambda x: x[1], reverse=True) +for (url, _, _, _), count in sorted_wishes: + [summary, status] = issue_details.get(url, "No summary available") + new_leaderboard += f"| {url} | {summary} | {count} | {status} |\n" +new_leaderboard += f"\n> Last updated: {date.today().strftime('%Y-%m-%d')}\n" +print(new_leaderboard) + +new_content = re.sub( + r"(\| Feature Request \|)(.*?)(> Last updated:)(.*?\n)", + new_leaderboard, + wishlist_issue.body, + flags=re.DOTALL, +) + +wishlist_issue.edit(body=new_content) diff --git a/.github/workflows/misc-update-wishlist-leaderboard.yml b/.github/workflows/misc-update-wishlist-leaderboard.yml new file mode 100644 index 000000000000..a825c4641124 --- /dev/null +++ b/.github/workflows/misc-update-wishlist-leaderboard.yml @@ -0,0 +1,36 @@ +name: Update wishlist leaderboard + +on: + schedule: + # Run every 3 hours + - cron: '0 */3 * * *' + +permissions: + contents: read + issues: write + +jobs: + update-wishlist-leaderboard: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install PyGithub + - name: Update developer wishlist + env: + GH_TOKEN: ${{ github.token }} + WISHLIST_REPOSITORY: "paritytech/polkadot-sdk" + WISHLIST_ISSUE_NUMBER: "3900" + run: python .github/scripts/update-wishlist-leaderboard.py + - name: Update user wishlist + env: + GH_TOKEN: ${{ github.token }} + WISHLIST_REPOSITORY: "paritytech/polkadot-sdk" + WISHLIST_ISSUE_NUMBER: "3901" + run: python .github/scripts/update-wishlist-leaderboard.py From 240b374e052e0d000d24d8e88f9bd9092147646a Mon Sep 17 00:00:00 2001 From: btwiuse <54848194+btwiuse@users.noreply.github.com> Date: Thu, 25 Jul 2024 05:03:57 +0800 Subject: [PATCH 010/382] Fix misleading comment about RewardHandler in epm config (#3095) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In pallet_election_provider_multi_phase::Config, the effect of type RewardHandler = () is to mint rewards from the void, not "nothing to do upon rewards" Co-authored-by: navigaid Co-authored-by: Bastian Köcher --- polkadot/runtime/westend/src/lib.rs | 2 +- substrate/bin/node/runtime/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 093476106518..e582598d9d85 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -630,7 +630,7 @@ impl pallet_election_provider_multi_phase::Config for Runtime { ::MaxWeight; type MinerConfig = Self; type SlashHandler = (); // burn slashes - type RewardHandler = (); // nothing to do upon rewards + type RewardHandler = (); // rewards are minted from the void type BetterSignedThreshold = (); type OffchainRepeat = OffchainRepeat; type MinerTxPriority = NposSolutionPriority; diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 5d046539b036..fd8597563a02 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -852,7 +852,7 @@ impl pallet_election_provider_multi_phase::Config for Runtime { type SignedDepositWeight = (); type SignedMaxWeight = MinerMaxWeight; type SlashHandler = (); // burn slashes - type RewardHandler = (); // nothing to do upon rewards + type RewardHandler = (); // rewards are minted from the void type DataProvider = Staking; type Fallback = onchain::OnChainExecution; type GovernanceFallback = onchain::OnChainExecution; From de6733baedf5406c37393c20912536600d3ef6fa Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 25 Jul 2024 08:56:18 +0200 Subject: [PATCH 011/382] Bridges improved tests and nits (#5128) This PR adds `exporter_is_compatible_with_pallet_xcm_bridge_hub_router`, which ensures that our `pallet_xcm_bridge_hub` and `pallet_xcm_bridge_hub_router` are compatible when handling `ExportMessage`. Other changes are just small nits and cosmetics which makes others stuff easier. --------- Co-authored-by: Svyatoslav Nikolsky --- Cargo.lock | 3 +- bridges/bin/runtime-common/src/integrity.rs | 21 ++- bridges/modules/messages/src/proofs.rs | 2 +- bridges/modules/messages/src/tests/mock.rs | 2 +- bridges/modules/xcm-bridge-hub/Cargo.toml | 5 + .../modules/xcm-bridge-hub/src/exporter.rs | 44 ++++- bridges/modules/xcm-bridge-hub/src/mock.rs | 150 ++++++++++++++++-- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 1 - .../src/bridge_to_bulletin_config.rs | 3 - .../src/bridge_to_westend_config.rs | 2 - .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 3 +- .../bridge-hub-rococo/src/xcm_config.rs | 17 +- .../bridge-hub-rococo/tests/tests.rs | 43 +++-- .../bridge-hubs/bridge-hub-westend/Cargo.toml | 1 - .../src/bridge_to_rococo_config.rs | 2 - .../bridge-hubs/bridge-hub-westend/src/lib.rs | 3 +- .../bridge-hub-westend/tests/tests.rs | 19 ++- .../src/test_cases/from_grandpa_chain.rs | 32 ++-- .../src/test_cases/from_parachain.rs | 34 ++-- .../test-utils/src/test_cases/helpers.rs | 4 + .../test-utils/src/test_cases/mod.rs | 10 +- .../src/test_data/from_grandpa_chain.rs | 8 +- .../src/test_data/from_parachain.rs | 4 +- .../test-utils/src/test_data/mod.rs | 18 +-- 24 files changed, 284 insertions(+), 147 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb4636f89650..630503f6041f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2171,7 +2171,6 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", - "static_assertions", "substrate-wasm-builder", "testnet-parachains-constants", "xcm-runtime-apis", @@ -2329,7 +2328,6 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", - "static_assertions", "substrate-wasm-builder", "testnet-parachains-constants", "westend-runtime-constants", @@ -11989,6 +11987,7 @@ dependencies = [ "log", "pallet-balances", "pallet-bridge-messages", + "pallet-xcm-bridge-hub-router", "parity-scale-codec", "scale-info", "sp-core", diff --git a/bridges/bin/runtime-common/src/integrity.rs b/bridges/bin/runtime-common/src/integrity.rs index f661db8a2205..a0a9367dd140 100644 --- a/bridges/bin/runtime-common/src/integrity.rs +++ b/bridges/bin/runtime-common/src/integrity.rs @@ -27,17 +27,25 @@ use frame_support::{storage::generator::StorageValue, traits::Get, weights::Weig use frame_system::limits; use pallet_bridge_messages::WeightInfoExt as _; +// Re-export to avoid include all dependencies everywhere. +#[doc(hidden)] +pub mod __private { + pub use bp_xcm_bridge_hub; + pub use static_assertions; +} + /// Macro that ensures that the runtime configuration and chain primitives crate are sharing /// the same types (nonce, block number, hash, hasher, account id and header). #[macro_export] macro_rules! assert_chain_types( ( runtime: $r:path, this_chain: $this:path ) => { { + use frame_system::{Config as SystemConfig, pallet_prelude::{BlockNumberFor, HeaderFor}}; + use $crate::integrity::__private::static_assertions::assert_type_eq_all; + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard // configuration is used), or something has broke existing configuration (meaning that all bridged chains // and relays will stop functioning) - use frame_system::{Config as SystemConfig, pallet_prelude::{BlockNumberFor, HeaderFor}}; - use static_assertions::assert_type_eq_all; assert_type_eq_all!(<$r as SystemConfig>::Nonce, bp_runtime::NonceOf<$this>); assert_type_eq_all!(BlockNumberFor<$r>, bp_runtime::BlockNumberOf<$this>); @@ -60,14 +68,15 @@ macro_rules! assert_bridge_messages_pallet_types( bridged_chain: $bridged:path, ) => { { - // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard - // configuration is used), or something has broke existing configuration (meaning that all bridged chains - // and relays will stop functioning) use $crate::messages_xcm_extension::XcmAsPlainPayload; + use $crate::integrity::__private::static_assertions::assert_type_eq_all; use bp_messages::ChainWithMessages; use bp_runtime::Chain; use pallet_bridge_messages::Config as MessagesConfig; - use static_assertions::assert_type_eq_all; + + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) assert_type_eq_all!(<$r as MessagesConfig<$i>>::ThisChain, $this); assert_type_eq_all!(<$r as MessagesConfig<$i>>::BridgedChain, $bridged); diff --git a/bridges/modules/messages/src/proofs.rs b/bridges/modules/messages/src/proofs.rs index 18367029d72c..a3318833fa61 100644 --- a/bridges/modules/messages/src/proofs.rs +++ b/bridges/modules/messages/src/proofs.rs @@ -63,7 +63,7 @@ pub fn verify_messages_proof, I: 'static>( let nonces_range = nonces_start..=nonces_end; // receiving proofs where end < begin is ok (if proof includes outbound lane state) - let messages_in_the_proof = nonces_range.checked_len().unwrap_or(0); + let messages_in_the_proof = nonces_range.saturating_len(); if messages_in_the_proof != MessageNonce::from(messages_count) { return Err(VerificationError::MessagesCountMismatch) } diff --git a/bridges/modules/messages/src/tests/mock.rs b/bridges/modules/messages/src/tests/mock.rs index ffdd536830b5..99da019dc084 100644 --- a/bridges/modules/messages/src/tests/mock.rs +++ b/bridges/modules/messages/src/tests/mock.rs @@ -225,7 +225,7 @@ impl crate::benchmarking::Config<()> for TestRuntime { use bp_runtime::RangeInclusiveExt; let dispatch_weight = - REGULAR_PAYLOAD.declared_weight * params.message_nonces.checked_len().unwrap_or(0); + REGULAR_PAYLOAD.declared_weight * params.message_nonces.saturating_len(); ( *prepare_messages_proof( params.message_nonces.into_iter().map(|n| message(n, REGULAR_PAYLOAD)).collect(), diff --git a/bridges/modules/xcm-bridge-hub/Cargo.toml b/bridges/modules/xcm-bridge-hub/Cargo.toml index 092df477265f..8fbf61a0a535 100644 --- a/bridges/modules/xcm-bridge-hub/Cargo.toml +++ b/bridges/modules/xcm-bridge-hub/Cargo.toml @@ -38,6 +38,8 @@ xcm-executor = { workspace = true } bp-header-chain = { workspace = true, default-features = true } pallet-balances = { workspace = true, default-features = true } sp-io = { workspace = true, default-features = true } +bp-runtime = { features = ["test-helpers"], workspace = true } +pallet-xcm-bridge-hub-router = { workspace = true } [features] default = ["std"] @@ -51,6 +53,7 @@ std = [ "frame-system/std", "log/std", "pallet-bridge-messages/std", + "pallet-xcm-bridge-hub-router/std", "scale-info/std", "sp-core/std", "sp-runtime/std", @@ -65,6 +68,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", + "pallet-xcm-bridge-hub-router/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", @@ -74,5 +78,6 @@ try-runtime = [ "frame-system/try-runtime", "pallet-balances/try-runtime", "pallet-bridge-messages/try-runtime", + "pallet-xcm-bridge-hub-router/try-runtime", "sp-runtime/try-runtime", ] diff --git a/bridges/modules/xcm-bridge-hub/src/exporter.rs b/bridges/modules/xcm-bridge-hub/src/exporter.rs index 94ec8b5f106f..5bca3ad8e277 100644 --- a/bridges/modules/xcm-bridge-hub/src/exporter.rs +++ b/bridges/modules/xcm-bridge-hub/src/exporter.rs @@ -131,6 +131,7 @@ impl HaulBlob for DummyHaulBlob { mod tests { use super::*; use crate::mock::*; + use bp_runtime::RangeInclusiveExt; use frame_support::assert_ok; use xcm_executor::traits::export_xcm; @@ -138,8 +139,8 @@ mod tests { [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)].into() } - fn universal_destination() -> InteriorLocation { - BridgedDestination::get() + fn bridged_relative_destination() -> InteriorLocation { + BridgedRelativeDestination::get() } #[test] @@ -149,7 +150,7 @@ mod tests { BridgedRelayNetwork::get(), 0, universal_source(), - universal_destination(), + bridged_relative_destination(), vec![Instruction::ClearOrigin].into(), )); }) @@ -163,7 +164,7 @@ mod tests { BridgedRelayNetwork::get(), 0, &mut None, - &mut Some(universal_destination()), + &mut Some(bridged_relative_destination()), &mut Some(Vec::new().into()), ), Err(SendError::MissingArgument), @@ -192,7 +193,7 @@ mod tests { BridgedRelayNetwork::get(), 0, &mut Some(universal_source()), - &mut Some(universal_destination()), + &mut Some(bridged_relative_destination()), &mut Some(Vec::new().into()), ) .unwrap() @@ -203,4 +204,37 @@ mod tests { ); }) } + + #[test] + fn exporter_is_compatible_with_pallet_xcm_bridge_hub_router() { + run_test(|| { + // valid routable destination + let dest = Location::new(2, BridgedUniversalDestination::get()); + let expected_lane_id = TEST_LANE_ID; + + // check before - no messages + assert_eq!( + pallet_bridge_messages::Pallet::::outbound_lane_data( + expected_lane_id + ) + .queued_messages() + .saturating_len(), + 0 + ); + + // send `ExportMessage(message)` by `pallet_xcm_bridge_hub_router`. + TestExportXcmWithXcmOverBridge::set_origin_for_execute(SiblingLocation::get()); + assert_ok!(send_xcm::(dest, Xcm::<()>::default())); + + // check after - a message ready to be relayed + assert_eq!( + pallet_bridge_messages::Pallet::::outbound_lane_data( + expected_lane_id + ) + .queued_messages() + .saturating_len(), + 1 + ); + }) + } } diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs index df72e7a3c4fc..0cca32ba9e5f 100644 --- a/bridges/modules/xcm-bridge-hub/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub/src/mock.rs @@ -25,14 +25,24 @@ use bp_messages::{ use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, HashOf}; use bridge_runtime_common::messages_xcm_extension::{SenderAndLane, XcmBlobHauler}; use codec::Encode; -use frame_support::{derive_impl, parameter_types, weights::RuntimeDbWeight}; +use frame_support::{ + assert_ok, derive_impl, parameter_types, + traits::{Everything, NeverEnsureOrigin}, + weights::RuntimeDbWeight, +}; use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, - traits::{BlakeTwo256, IdentityLookup}, + traits::{BlakeTwo256, ConstU128, ConstU32, IdentityLookup}, AccountId32, BuildStorage, StateVersion, }; +use sp_std::cell::RefCell; use xcm::prelude::*; +use xcm_builder::{ + AllowUnpaidExecutionFrom, FixedWeightBounds, InspectMessageQueues, NetworkExportTable, + NetworkExportTableItem, +}; +use xcm_executor::XcmExecutor; pub type AccountId = AccountId32; pub type Balance = u64; @@ -50,6 +60,7 @@ frame_support::construct_runtime! { Balances: pallet_balances::{Pallet, Event}, Messages: pallet_bridge_messages::{Pallet, Call, Event}, XcmOverBridge: pallet_xcm_bridge_hub::{Pallet}, + XcmOverBridgeRouter: pallet_xcm_bridge_hub_router, } } @@ -137,15 +148,34 @@ impl pallet_bridge_messages::WeightInfoExt for TestMessagesWeights { parameter_types! { pub const RelayNetwork: NetworkId = NetworkId::Kusama; - pub const BridgedRelayNetwork: NetworkId = NetworkId::Polkadot; - pub BridgedRelayNetworkLocation: Location = (Parent, GlobalConsensus(BridgedRelayNetwork::get())).into(); - pub const NonBridgedRelayNetwork: NetworkId = NetworkId::Rococo; - pub const BridgeReserve: Balance = 100_000; pub UniversalLocation: InteriorLocation = [ GlobalConsensus(RelayNetwork::get()), Parachain(THIS_BRIDGE_HUB_ID), ].into(); + pub SiblingLocation: Location = Location::new(1, [Parachain(SIBLING_ASSET_HUB_ID)]); + + pub const BridgedRelayNetwork: NetworkId = NetworkId::Polkadot; + pub BridgedRelayNetworkLocation: Location = (Parent, GlobalConsensus(BridgedRelayNetwork::get())).into(); + pub BridgedRelativeDestination: InteriorLocation = [Parachain(BRIDGED_ASSET_HUB_ID)].into(); + pub BridgedUniversalDestination: InteriorLocation = [GlobalConsensus(BridgedRelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)].into(); + pub const NonBridgedRelayNetwork: NetworkId = NetworkId::Rococo; + + pub const BridgeDeposit: Balance = 100_000; pub const Penalty: Balance = 1_000; + + // configuration for pallet_xcm_bridge_hub_router + pub BridgeHubLocation: Location = Here.into(); + pub BridgeFeeAsset: AssetId = Location::here().into(); + pub BridgeTable: Vec + = vec![ + NetworkExportTableItem::new( + BridgedRelayNetwork::get(), + None, + BridgeHubLocation::get(), + None + ) + ]; + pub UnitWeightCost: Weight = Weight::from_parts(10, 10); } impl pallet_xcm_bridge_hub::Config for TestRuntime { @@ -160,16 +190,114 @@ impl pallet_xcm_bridge_hub::Config for TestRuntime { type LanesSupport = TestXcmBlobHauler; } +impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime { + type WeightInfo = (); + + type UniversalLocation = UniversalLocation; + type BridgedNetworkId = BridgedRelayNetwork; + type Bridges = NetworkExportTable; + type DestinationVersion = AlwaysLatest; + + type BridgeHubOrigin = NeverEnsureOrigin; + type ToBridgeHubSender = TestExportXcmWithXcmOverBridge; + + type ByteFee = ConstU128<0>; + type FeeAsset = BridgeFeeAsset; + + type WithBridgeHubChannel = (); +} + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = (); + type AssetTransactor = (); + type OriginConverter = (); + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = AllowUnpaidExecutionFrom; + type Weigher = FixedWeightBounds>; + type Trader = (); + type ResponseHandler = (); + type AssetTrap = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = (); + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + // We just set `MessageExporter` as our `pallet_xcm_bridge_hub` instance. + type MessageExporter = (XcmOverBridge,); + type UniversalAliases = (); + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = (); + type TransactionalProcessor = (); + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +} + +thread_local! { + pub static EXECUTE_XCM_ORIGIN: RefCell> = RefCell::new(None); +} + +/// The `SendXcm` implementation directly executes XCM using `XcmExecutor`. +/// +/// We ensure that the `ExportMessage` produced by `pallet_xcm_bridge_hub_router` is compatible with +/// the `ExportXcm` implementation of `pallet_xcm_bridge_hub`. +/// +/// Note: The crucial part is that `ExportMessage` is processed by `XcmExecutor`, which calls the +/// `ExportXcm` implementation of `pallet_xcm_bridge_hub` as `MessageExporter`. +pub struct TestExportXcmWithXcmOverBridge; +impl SendXcm for TestExportXcmWithXcmOverBridge { + type Ticket = Xcm<()>; + + fn validate( + _: &mut Option, + message: &mut Option>, + ) -> SendResult { + Ok((message.take().unwrap(), Assets::new())) + } + + fn deliver(ticket: Self::Ticket) -> Result { + let xcm: Xcm = ticket.into(); + + let origin = EXECUTE_XCM_ORIGIN.with(|o| o.borrow().clone().unwrap()); + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + let outcome = XcmExecutor::::prepare_and_execute( + origin, + xcm, + &mut hash, + Weight::MAX, + Weight::zero(), + ); + assert_ok!(outcome.ensure_complete()); + + Ok(hash) + } +} +impl InspectMessageQueues for TestExportXcmWithXcmOverBridge { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + todo!() + } +} +impl TestExportXcmWithXcmOverBridge { + pub fn set_origin_for_execute(origin: Location) { + EXECUTE_XCM_ORIGIN.with(|o| *o.borrow_mut() = Some(origin)); + } +} + parameter_types! { pub TestSenderAndLane: SenderAndLane = SenderAndLane { - location: Location::new(1, [Parachain(SIBLING_ASSET_HUB_ID)]), + location: SiblingLocation::get(), lane: TEST_LANE_ID, }; - pub BridgedDestination: InteriorLocation = [ - Parachain(BRIDGED_ASSET_HUB_ID) - ].into(); pub TestLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = sp_std::vec![ - (TestSenderAndLane::get(), (BridgedRelayNetwork::get(), BridgedDestination::get())) + (TestSenderAndLane::get(), (BridgedRelayNetwork::get(), BridgedRelativeDestination::get())) ]; } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 987372984682..7a60a3f64840 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -121,7 +121,6 @@ snowbridge-runtime-common = { workspace = true } bridge-hub-common = { workspace = true } [dev-dependencies] -static_assertions = { workspace = true, default-features = true } bridge-hub-test-utils = { workspace = true, default-features = true } bridge-runtime-common = { features = [ "integrity-test", diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs index d97e6a1d88e1..83b92918dc4f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -28,7 +28,6 @@ use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, target_chain::FromBridgedChainMessagesProof, LaneId, }; -use bp_runtime::Chain; use bridge_runtime_common::{ extensions::refund_relayer_extension::{ ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter, @@ -48,8 +47,6 @@ use xcm::{ use xcm_builder::BridgeBlobDispatcher; parameter_types! { - /// Bridge specific chain (network) identifier of the Rococo Bulletin Chain. - pub const RococoBulletinChainId: bp_runtime::ChainId = bp_polkadot_bulletin::PolkadotBulletin::ID; /// Interior location (relative to this runtime) of the with-RococoBulletin messages pallet. pub BridgeRococoToRococoBulletinMessagesPalletInstance: InteriorLocation = [ PalletInstance(::index() as u8) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index fe854e20c244..9880e8a17c2b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -26,7 +26,6 @@ use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, target_chain::FromBridgedChainMessagesProof, LaneId, }; -use bp_runtime::Chain; use bridge_runtime_common::{ extensions::refund_relayer_extension::{ ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter, @@ -47,7 +46,6 @@ use xcm::{ use xcm_builder::BridgeBlobDispatcher; parameter_types! { - pub const BridgeHubWestendChainId: bp_runtime::ChainId = bp_bridge_hub_westend::BridgeHubWestend::ID; pub BridgeRococoToWestendMessagesPalletInstance: InteriorLocation = [PalletInstance(::index() as u8)].into(); pub WestendGlobalConsensusNetwork: NetworkId = NetworkId::Westend; pub WestendGlobalConsensusNetworkLocation: Location = Location::new( diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 512c1199f439..062996ad4115 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -1416,7 +1416,8 @@ impl_runtime_apis! { impl BridgeMessagesConfig for Runtime { fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { let bench_lane_id = >::bench_lane_id(); - let bridged_chain_id = bridge_to_westend_config::BridgeHubWestendChainId::get(); + use bp_runtime::Chain; + let bridged_chain_id =>::BridgedChain::ID; pallet_bridge_relayers::Pallet::::relayer_reward( relayer, bp_relayers::RewardsAccountParams::new( diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 2f11b4694e3b..8c077470ba1a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -21,7 +21,6 @@ use super::{ }; use bp_messages::LaneId; use bp_relayers::{PayRewardFromAccount, RewardsAccountOwner, RewardsAccountParams}; -use bp_runtime::ChainId; use core::marker::PhantomData; use frame_support::{ parameter_types, @@ -212,7 +211,7 @@ impl xcm_executor::Config for XcmConfig { Self::AssetTransactor, crate::bridge_to_westend_config::WestendGlobalConsensusNetwork, crate::bridge_to_westend_config::AssetHubWestendParaId, - crate::bridge_to_westend_config::BridgeHubWestendChainId, + bp_bridge_hub_westend::BridgeHubWestend, crate::bridge_to_westend_config::AssetHubRococoToAssetHubWestendMessagesLane, >, XcmExportFeeToSibling< @@ -302,22 +301,22 @@ pub struct XcmExportFeeToRelayerRewardAccounts< AssetTransactor, DestNetwork, DestParaId, - DestBridgedChainId, + DestBridgedChain, BridgeLaneId, ->(PhantomData<(AssetTransactor, DestNetwork, DestParaId, DestBridgedChainId, BridgeLaneId)>); +>(PhantomData<(AssetTransactor, DestNetwork, DestParaId, DestBridgedChain, BridgeLaneId)>); impl< AssetTransactor: TransactAsset, DestNetwork: Get, DestParaId: Get, - DestBridgedChainId: Get, + DestBridgedChain: bp_runtime::Chain, BridgeLaneId: Get, > HandleFee for XcmExportFeeToRelayerRewardAccounts< AssetTransactor, DestNetwork, DestParaId, - DestBridgedChainId, + DestBridgedChain, BridgeLaneId, > { @@ -326,6 +325,8 @@ impl< if bridged_network == DestNetwork::get() && destination == [Parachain(DestParaId::get().into())]) { + let bridged_chain_id = DestBridgedChain::ID; + // We have 2 relayer rewards accounts: // - the SA of the source parachain on this BH: this pays the relayers for delivering // Source para -> Target Para message delivery confirmations @@ -337,7 +338,7 @@ impl< AccountId, >::rewards_account(RewardsAccountParams::new( BridgeLaneId::get(), - DestBridgedChainId::get(), + bridged_chain_id, RewardsAccountOwner::ThisChain, )); @@ -346,7 +347,7 @@ impl< AccountId, >::rewards_account(RewardsAccountParams::new( BridgeLaneId::get(), - DestBridgedChainId::get(), + bridged_chain_id, RewardsAccountOwner::BridgedChain, )); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 1d3d9e55f7ee..ca0d89f038cb 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -147,7 +147,7 @@ mod bridge_hub_westend_tests { }; use bridge_hub_test_utils::test_cases::from_parachain; use bridge_to_westend_config::{ - BridgeHubWestendChainId, BridgeHubWestendLocation, WestendGlobalConsensusNetwork, + BridgeHubWestendLocation, WestendGlobalConsensusNetwork, WithBridgeHubWestendMessagesInstance, XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, }; @@ -313,11 +313,14 @@ mod bridge_hub_westend_tests { } }), || ExportMessage { network: Westend, destination: [Parachain(bridge_to_westend_config::AssetHubWestendParaId::get().into())].into(), xcm: Xcm(vec![]) }, - XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, Some((TokenLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` Some((TokenLocation::get(), bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs::get()).into()), - || PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(BridgeHubWestendLocation::get()), XCM_VERSION).expect("version saved!"), + || { + PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(BridgeHubWestendLocation::get()), XCM_VERSION).expect("version saved!"); + + XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND + }, ) } @@ -350,8 +353,7 @@ mod bridge_hub_westend_tests { _ => None, } }), - XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, - || (), + || XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, ) } @@ -363,11 +365,9 @@ mod bridge_hub_westend_tests { slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, - BridgeHubWestendChainId::get(), SIBLING_PARACHAIN_ID, Rococo, - XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, - || (), + || XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, construct_and_apply_extrinsic, ) } @@ -380,11 +380,9 @@ mod bridge_hub_westend_tests { slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, - BridgeHubWestendChainId::get(), SIBLING_PARACHAIN_ID, Rococo, - XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, - || (), + || XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, construct_and_apply_extrinsic, ) } @@ -454,9 +452,8 @@ mod bridge_hub_bulletin_tests { use bridge_common_config::BridgeGrandpaRococoBulletinInstance; use bridge_hub_test_utils::test_cases::from_grandpa_chain; use bridge_to_bulletin_config::{ - RococoBulletinChainId, RococoBulletinGlobalConsensusNetwork, - RococoBulletinGlobalConsensusNetworkLocation, WithRococoBulletinMessagesInstance, - XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + RococoBulletinGlobalConsensusNetwork, RococoBulletinGlobalConsensusNetworkLocation, + WithRococoBulletinMessagesInstance, XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, }; // Para id of sibling chain used in tests. @@ -519,10 +516,13 @@ mod bridge_hub_bulletin_tests { destination: Here, xcm: Xcm(vec![]), }, - XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, Some((TokenLocation::get(), ExistentialDeposit::get()).into()), None, - || PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(RococoBulletinGlobalConsensusNetworkLocation::get()), XCM_VERSION).expect("version saved!"), + || { + PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(RococoBulletinGlobalConsensusNetworkLocation::get()), XCM_VERSION).expect("version saved!"); + + XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN + }, ) } @@ -555,8 +555,7 @@ mod bridge_hub_bulletin_tests { _ => None, } }), - XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, - || (), + || XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, ) } @@ -567,11 +566,9 @@ mod bridge_hub_bulletin_tests { collator_session_keys(), slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - RococoBulletinChainId::get(), SIBLING_PARACHAIN_ID, Rococo, - XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, - || (), + || XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, construct_and_apply_extrinsic, ) } @@ -583,11 +580,9 @@ mod bridge_hub_bulletin_tests { collator_session_keys(), slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - RococoBulletinChainId::get(), SIBLING_PARACHAIN_ID, Rococo, - XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, - || (), + || XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, construct_and_apply_extrinsic, ) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index e2671d3d606d..4326cbf926d6 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -101,7 +101,6 @@ bridge-runtime-common = { workspace = true } bridge-hub-common = { workspace = true } [dev-dependencies] -static_assertions = { workspace = true, default-features = true } bridge-hub-test-utils = { workspace = true, default-features = true } bridge-runtime-common = { features = ["integrity-test"], workspace = true, default-features = true } sp-keyring = { workspace = true, default-features = true } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index 42d5ef3eebdb..be4d40c2275f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -25,7 +25,6 @@ use bp_messages::{ target_chain::FromBridgedChainMessagesProof, LaneId, }; use bp_parachains::SingleParaStoredHeaderDataBuilder; -use bp_runtime::Chain; use bridge_runtime_common::{ extensions::refund_relayer_extension::{ ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter, @@ -54,7 +53,6 @@ parameter_types! { pub const RococoBridgeParachainPalletName: &'static str = "Paras"; pub const MaxRococoParaHeadDataSize: u32 = bp_rococo::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE; - pub const BridgeHubRococoChainId: bp_runtime::ChainId = bp_bridge_hub_rococo::BridgeHubRococo::ID; pub BridgeWestendToRococoMessagesPalletInstance: InteriorLocation = [PalletInstance(::index() as u8)].into(); pub RococoGlobalConsensusNetwork: NetworkId = NetworkId::Rococo; pub RococoGlobalConsensusNetworkLocation: Location = Location::new( diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 5d4c35d6610a..42c768861bde 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -1100,7 +1100,8 @@ impl_runtime_apis! { impl BridgeMessagesConfig for Runtime { fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { let bench_lane_id = >::bench_lane_id(); - let bridged_chain_id = bridge_to_rococo_config::BridgeHubRococoChainId::get(); + use bp_runtime::Chain; + let bridged_chain_id =>::BridgedChain::ID; pallet_bridge_relayers::Pallet::::relayer_reward( relayer, bp_relayers::RewardsAccountParams::new( diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs index 763271fd7af0..98a789eb4e00 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs @@ -27,9 +27,8 @@ use bridge_hub_westend_runtime::{ SignedExtra, TransactionPayment, UncheckedExtrinsic, }; use bridge_to_rococo_config::{ - BridgeGrandpaRococoInstance, BridgeHubRococoChainId, BridgeHubRococoLocation, - BridgeParachainRococoInstance, WithBridgeHubRococoMessagesInstance, - XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, + BridgeGrandpaRococoInstance, BridgeHubRococoLocation, BridgeParachainRococoInstance, + WithBridgeHubRococoMessagesInstance, XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, }; use codec::{Decode, Encode}; use frame_support::{dispatch::GetDispatchInfo, parameter_types, traits::ConstU8}; @@ -212,11 +211,14 @@ fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { } }), || ExportMessage { network: Rococo, destination: [Parachain(bridge_to_rococo_config::AssetHubRococoParaId::get().into())].into(), xcm: Xcm(vec![]) }, - XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, Some((WestendLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` Some((WestendLocation::get(), bp_bridge_hub_westend::BridgeHubWestendBaseXcmFeeInWnds::get()).into()), - || PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(BridgeHubRococoLocation::get()), XCM_VERSION).expect("version saved!"), + || { + PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(BridgeHubRococoLocation::get()), XCM_VERSION).expect("version saved!"); + + XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO + }, ) } @@ -248,8 +250,7 @@ fn message_dispatch_routing_works() { _ => None, } }), - XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, - || (), + || XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, ) } @@ -260,11 +261,9 @@ fn relayed_incoming_message_works() { slot_durations(), bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - BridgeHubRococoChainId::get(), SIBLING_PARACHAIN_ID, Westend, - XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, - || (), + || XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, construct_and_apply_extrinsic, ) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs index d6dfa93731a7..f2f0ccecba13 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs @@ -94,11 +94,9 @@ pub fn relayed_incoming_message_works( collator_session_key: CollatorSessionKeys, slot_durations: SlotDurations, runtime_para_id: u32, - bridged_chain_id: bp_runtime::ChainId, sibling_parachain_id: u32, local_relay_chain_id: NetworkId, - lane_id: LaneId, - prepare_configuration: impl Fn(), + prepare_configuration: impl Fn() -> LaneId, construct_and_apply_extrinsic: fn( sp_keyring::AccountKeyring, RuntimeCallOf, @@ -125,10 +123,11 @@ pub fn relayed_incoming_message_works( relayer_id_at_bridged_chain, message_destination, message_nonce, - xcm| { + xcm, + bridged_chain_id| { let relay_header_number = 5u32.into(); - prepare_configuration(); + let lane_id = prepare_configuration(); // start with bridged relay chain block#0 helpers::initialize_bridge_grandpa_pallet::( @@ -141,7 +140,6 @@ pub fn relayed_incoming_message_works( test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::< BridgedChainOf, ThisChainOf, - (), >( lane_id, xcm.into(), @@ -197,11 +195,9 @@ pub fn free_relay_extrinsic_works( collator_session_key: CollatorSessionKeys, slot_durations: SlotDurations, runtime_para_id: u32, - bridged_chain_id: bp_runtime::ChainId, sibling_parachain_id: u32, local_relay_chain_id: NetworkId, - lane_id: LaneId, - prepare_configuration: impl Fn(), + prepare_configuration: impl Fn() -> LaneId, construct_and_apply_extrinsic: fn( sp_keyring::AccountKeyring, RuntimeCallOf, @@ -235,8 +231,9 @@ pub fn free_relay_extrinsic_works( relayer_id_at_bridged_chain, message_destination, message_nonce, - xcm| { - prepare_configuration(); + xcm, + bridged_chain_id| { + let lane_id = prepare_configuration(); // start with bridged relay chain block#0 let initial_block_number = 0; @@ -266,7 +263,6 @@ pub fn free_relay_extrinsic_works( test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::< BridgedChainOf, ThisChainOf, - (), >( lane_id, xcm.into(), @@ -328,10 +324,8 @@ pub fn complex_relay_extrinsic_works( slot_durations: SlotDurations, runtime_para_id: u32, sibling_parachain_id: u32, - bridged_chain_id: bp_runtime::ChainId, local_relay_chain_id: NetworkId, - lane_id: LaneId, - prepare_configuration: impl Fn(), + prepare_configuration: impl Fn() -> LaneId, construct_and_apply_extrinsic: fn( sp_keyring::AccountKeyring, RuntimeCallOf, @@ -361,10 +355,11 @@ pub fn complex_relay_extrinsic_works( relayer_id_at_bridged_chain, message_destination, message_nonce, - xcm| { + xcm, + bridged_chain_id| { let relay_header_number = 1u32.into(); - prepare_configuration(); + let lane_id = prepare_configuration(); // start with bridged relay chain block#0 helpers::initialize_bridge_grandpa_pallet::( @@ -377,7 +372,6 @@ pub fn complex_relay_extrinsic_works( test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::< BridgedChainOf, ThisChainOf, - (), >( lane_id, xcm.into(), @@ -452,7 +446,6 @@ where test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::< BridgedChainOf, ThisChainOf, - (), >( LaneId::default(), vec![Instruction::<()>::ClearOrigin; 1_024].into(), @@ -557,7 +550,6 @@ where test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::< BridgedChainOf, ThisChainOf, - (), >( LaneId::default(), vec![Instruction::<()>::ClearOrigin; 1_024].into(), diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs index 728b4e76b105..0988528c3f2a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs @@ -104,11 +104,9 @@ pub fn relayed_incoming_message_works( slot_durations: SlotDurations, runtime_para_id: u32, bridged_para_id: u32, - bridged_chain_id: bp_runtime::ChainId, sibling_parachain_id: u32, local_relay_chain_id: NetworkId, - lane_id: LaneId, - prepare_configuration: impl Fn(), + prepare_configuration: impl Fn() -> LaneId, construct_and_apply_extrinsic: fn( sp_keyring::AccountKeyring, ::RuntimeCall, @@ -138,11 +136,12 @@ pub fn relayed_incoming_message_works( relayer_id_at_bridged_chain, message_destination, message_nonce, - xcm| { + xcm, + bridged_chain_id| { let para_header_number = 5; let relay_header_number = 1; - prepare_configuration(); + let lane_id = prepare_configuration(); // start with bridged relay chain block#0 helpers::initialize_bridge_grandpa_pallet::( @@ -162,7 +161,6 @@ pub fn relayed_incoming_message_works( >::BridgedChain, BridgedChainOf, ThisChainOf, - (), >( lane_id, xcm.into(), @@ -234,11 +232,9 @@ pub fn free_relay_extrinsic_works( slot_durations: SlotDurations, runtime_para_id: u32, bridged_para_id: u32, - bridged_chain_id: bp_runtime::ChainId, sibling_parachain_id: u32, local_relay_chain_id: NetworkId, - lane_id: LaneId, - prepare_configuration: impl Fn(), + prepare_configuration: impl Fn() -> LaneId, construct_and_apply_extrinsic: fn( sp_keyring::AccountKeyring, ::RuntimeCall, @@ -275,8 +271,9 @@ pub fn free_relay_extrinsic_works( relayer_id_at_bridged_chain, message_destination, message_nonce, - xcm| { - prepare_configuration(); + xcm, + bridged_chain_id| { + let lane_id = prepare_configuration(); // start with bridged relay chain block#0 let initial_block_number = 0; @@ -315,7 +312,6 @@ pub fn free_relay_extrinsic_works( >::BridgedChain, BridgedChainOf, ThisChainOf, - (), >( lane_id, xcm.into(), @@ -399,10 +395,8 @@ pub fn complex_relay_extrinsic_works( runtime_para_id: u32, bridged_para_id: u32, sibling_parachain_id: u32, - bridged_chain_id: bp_runtime::ChainId, local_relay_chain_id: NetworkId, - lane_id: LaneId, - prepare_configuration: impl Fn(), + prepare_configuration: impl Fn() -> LaneId, construct_and_apply_extrinsic: fn( sp_keyring::AccountKeyring, ::RuntimeCall, @@ -435,11 +429,12 @@ pub fn complex_relay_extrinsic_works( relayer_id_at_bridged_chain, message_destination, message_nonce, - xcm| { + xcm, + bridged_chain_id| { let para_header_number = 5; let relay_header_number = 1; - prepare_configuration(); + let lane_id = prepare_configuration(); // start with bridged relay chain block#0 helpers::initialize_bridge_grandpa_pallet::( @@ -459,7 +454,6 @@ pub fn complex_relay_extrinsic_works( >::BridgedChain, BridgedChainOf, ThisChainOf, - (), >( lane_id, xcm.into(), @@ -557,7 +551,6 @@ where >::BridgedChain, BridgedChainOf, ThisChainOf, - (), >( LaneId::default(), vec![Instruction::<()>::ClearOrigin; 1_024].into(), @@ -628,7 +621,6 @@ where >::BridgedChain, BridgedChainOf, ThisChainOf, - (), >( LaneId::default(), 1, @@ -691,7 +683,6 @@ where >::BridgedChain, BridgedChainOf, ThisChainOf, - (), >( LaneId::default(), vec![Instruction::<()>::ClearOrigin; 1_024].into(), @@ -747,7 +738,6 @@ where >::BridgedChain, BridgedChainOf, ThisChainOf, - (), >( LaneId::default(), 1, diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs index 78b8a170f0d4..c4b5e5583baa 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs @@ -22,6 +22,7 @@ use asset_test_utils::BasicParachainRuntime; use bp_messages::{LaneId, MessageNonce}; use bp_polkadot_core::parachains::{ParaHash, ParaId}; use bp_relayers::RewardsAccountParams; +use bp_runtime::Chain; use codec::Decode; use core::marker::PhantomData; use frame_support::{ @@ -267,6 +268,7 @@ pub fn relayed_incoming_message_works( InteriorLocation, MessageNonce, Xcm<()>, + bp_runtime::ChainId, ) -> CallsAndVerifiers, ) where Runtime: BasicParachainRuntime + cumulus_pallet_xcmp_queue::Config + BridgeMessagesConfig, @@ -278,6 +280,7 @@ pub fn relayed_incoming_message_works( let relayer_at_target = Bob; let relayer_id_on_target: AccountId32 = relayer_at_target.public().into(); let relayer_id_on_source = relayer_id_at_bridged_chain::(); + let bridged_chain_id = Runtime::BridgedChain::ID; assert_ne!(runtime_para_id, sibling_parachain_id); @@ -339,6 +342,7 @@ pub fn relayed_incoming_message_works( message_destination, message_nonce, xcm.clone().into(), + bridged_chain_id, ), ); diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs index bc1c7ec5e032..a36a74dbbbc3 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs @@ -320,10 +320,9 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< dyn Fn(Vec) -> Option>, >, export_message_instruction: fn() -> Instruction, - expected_lane_id: LaneId, existential_deposit: Option, maybe_paid_export_message: Option, - prepare_configuration: impl Fn(), + prepare_configuration: impl Fn() -> LaneId, ) where Runtime: BasicParachainRuntime + BridgeMessagesConfig, XcmConfig: xcm_executor::Config, @@ -333,7 +332,7 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< let sibling_parachain_location = Location::new(1, [Parachain(sibling_parachain_id)]); run_test::(collator_session_key, runtime_para_id, vec![], || { - prepare_configuration(); + let expected_lane_id = prepare_configuration(); // check queue before assert_eq!( @@ -430,8 +429,7 @@ pub fn message_dispatch_routing_works< unwrap_cumulus_pallet_xcmp_queue_event: Box< dyn Fn(Vec) -> Option>, >, - expected_lane_id: LaneId, - prepare_configuration: impl Fn(), + prepare_configuration: impl Fn() -> LaneId, ) where Runtime: BasicParachainRuntime + cumulus_pallet_xcmp_queue::Config @@ -459,7 +457,7 @@ pub fn message_dispatch_routing_works< assert_ne!(runtime_para_id, sibling_parachain_id); run_test::(collator_session_key, runtime_para_id, vec![], || { - prepare_configuration(); + let expected_lane_id = prepare_configuration(); let mut alice = [0u8; 32]; alice[0] = 1; diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs index c61a31e5454b..5c1dc492a9d2 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs @@ -152,13 +152,9 @@ where } /// Prepare storage proofs of messages, stored at the (bridged) source GRANDPA chain. -pub fn make_complex_relayer_delivery_proofs< - BridgedChain, - ThisChainWithMessages, - InnerXcmRuntimeCall, ->( +pub fn make_complex_relayer_delivery_proofs( lane_id: LaneId, - xcm_message: Xcm, + xcm_message: Xcm<()>, message_nonce: MessageNonce, message_destination: Junctions, header_number: BlockNumberOf, diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs index 897fe0d0b0f1..b99c275bd0df 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs @@ -195,10 +195,9 @@ pub fn make_complex_relayer_delivery_proofs< BridgedRelayChain, BridgedParachain, ThisChainWithMessages, - InnerXcmRuntimeCall, >( lane_id: LaneId, - xcm_message: Xcm, + xcm_message: Xcm<()>, message_nonce: MessageNonce, message_destination: Junctions, para_header_number: u32, @@ -267,7 +266,6 @@ pub fn make_complex_relayer_confirmation_proofs< BridgedRelayChain, BridgedParachain, ThisChainWithMessages, - InnerXcmRuntimeCall, >( lane_id: LaneId, para_header_number: u32, diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs index ee3fc1ed2c41..106eacd799ca 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs @@ -32,20 +32,16 @@ use bp_messages::MessageNonce; use bp_runtime::BasicOperatingMode; use bp_test_utils::authority_list; use xcm::GetVersion; -use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; +use xcm_builder::{BridgeMessage, HaulBlob, HaulBlobError, HaulBlobExporter}; use xcm_executor::traits::{validate_export, ExportXcm}; -pub fn prepare_inbound_xcm( - xcm_message: Xcm, - destination: InteriorLocation, -) -> Vec { +pub fn prepare_inbound_xcm(xcm_message: Xcm<()>, destination: InteriorLocation) -> Vec { let location = xcm::VersionedInteriorLocation::from(destination); - let xcm = xcm::VersionedXcm::::from(xcm_message); - // this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor - // or public fields, so just tuple - // (double encoding, because `.encode()` is called on original Xcm BLOB when it is pushed - // to the storage) - (location, xcm).encode().encode() + let xcm = xcm::VersionedXcm::<()>::from(xcm_message); + + // (double encoding, because `.encode()` is called on original Xcm BLOB when it is pushed to the + // storage) + BridgeMessage { universal_dest: location, message: xcm }.encode().encode() } /// Helper that creates InitializationData mock data, that can be used to initialize bridge From 48afbe352507888934280bfac61918e2c36dcbbd Mon Sep 17 00:00:00 2001 From: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:44:06 +0300 Subject: [PATCH 012/382] CandidateDescriptor: disable collator signature and collator id usage (#4665) Collator id and collator signature do not serve any useful purpose. Removing the signature check from runtime but keeping the checks in the node until the runtime is upgraded. TODO: - [x] PRDoc - [x] Add node feature for core index commitment enablement --------- Signed-off-by: Andrei Sandu --- .../src/fragment_chain/mod.rs | 2 - .../src/inclusion_emulator/mod.rs | 26 ++------- polkadot/primitives/src/v7/mod.rs | 6 +- polkadot/runtime/parachains/src/builder.rs | 38 ++++++------- .../runtime/parachains/src/inclusion/mod.rs | 8 --- .../runtime/parachains/src/inclusion/tests.rs | 56 +++++++++++++++---- prdoc/pr_4665.prdoc | 21 +++++++ 7 files changed, 96 insertions(+), 61 deletions(-) create mode 100644 prdoc/pr_4665.prdoc diff --git a/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs b/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs index f87d4820ff9a..b5fe70e76923 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs @@ -160,8 +160,6 @@ impl CandidateStorage { state, candidate: Arc::new(ProspectiveCandidate { commitments: candidate.commitments, - collator: candidate.descriptor.collator, - collator_signature: candidate.descriptor.signature, persisted_validation_data, pov_hash: candidate.descriptor.pov_hash, validation_code_hash: candidate.descriptor.validation_code_hash, diff --git a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs index b5aef325c8b4..2272f048089c 100644 --- a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs +++ b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs @@ -117,9 +117,8 @@ /// That means a few blocks of execution time lost, which is not a big deal for code upgrades /// in practice at most once every few weeks. use polkadot_primitives::{ - async_backing::Constraints as PrimitiveConstraints, BlockNumber, CandidateCommitments, - CollatorId, CollatorSignature, Hash, HeadData, Id as ParaId, PersistedValidationData, - UpgradeRestriction, ValidationCodeHash, + async_backing::Constraints as PrimitiveConstraints, BlockNumber, CandidateCommitments, Hash, + HeadData, Id as ParaId, PersistedValidationData, UpgradeRestriction, ValidationCodeHash, }; use std::{collections::HashMap, sync::Arc}; @@ -521,18 +520,13 @@ impl ConstraintModifications { /// The prospective candidate. /// /// This comprises the key information that represent a candidate -/// without pinning it to a particular session. For example, everything -/// to do with the collator's signature and commitments are represented -/// here. But the erasure-root is not. This means that prospective candidates +/// without pinning it to a particular session. For example commitments are +/// represented here. But the erasure-root is not. This means that prospective candidates /// are not correlated to any session in particular. #[derive(Debug, Clone, PartialEq)] pub struct ProspectiveCandidate { /// The commitments to the output of the execution. pub commitments: CandidateCommitments, - /// The collator that created the candidate. - pub collator: CollatorId, - /// The signature of the collator on the payload. - pub collator_signature: CollatorSignature, /// The persisted validation data used to create the candidate. pub persisted_validation_data: PersistedValidationData, /// The hash of the PoV. @@ -800,10 +794,7 @@ fn validate_against_constraints( #[cfg(test)] mod tests { use super::*; - use polkadot_primitives::{ - CollatorPair, HorizontalMessages, OutboundHrmpMessage, ValidationCode, - }; - use sp_application_crypto::Pair; + use polkadot_primitives::{HorizontalMessages, OutboundHrmpMessage, ValidationCode}; #[test] fn stack_modifications() { @@ -1162,11 +1153,6 @@ mod tests { constraints: &Constraints, relay_parent: &RelayChainBlockInfo, ) -> ProspectiveCandidate { - let collator_pair = CollatorPair::generate().0; - let collator = collator_pair.public(); - - let sig = collator_pair.sign(b"blabla".as_slice()); - ProspectiveCandidate { commitments: CandidateCommitments { upward_messages: Default::default(), @@ -1176,8 +1162,6 @@ mod tests { processed_downward_messages: 0, hrmp_watermark: relay_parent.number, }, - collator, - collator_signature: sig, persisted_validation_data: PersistedValidationData { parent_head: constraints.required_parent.clone(), relay_parent_number: relay_parent.number, diff --git a/polkadot/primitives/src/v7/mod.rs b/polkadot/primitives/src/v7/mod.rs index 06b704652083..a729d8cee4c7 100644 --- a/polkadot/primitives/src/v7/mod.rs +++ b/polkadot/primitives/src/v7/mod.rs @@ -2040,10 +2040,14 @@ pub mod node_features { /// Must not be enabled unless all validators and collators have stopped using `req_chunk` /// protocol version 1. If it is enabled, validators can start systematic chunk recovery. AvailabilityChunkMapping = 2, + /// Enables node side support of `CoreIndex` committed candidate receipts. + /// See [RFC-103](https://github.com/polkadot-fellows/RFCs/pull/103) for details. + /// Only enable if at least 2/3 of nodes support the feature. + CandidateReceiptV2 = 3, /// First unassigned feature bit. /// Every time a new feature flag is assigned it should take this value. /// and this should be incremented. - FirstUnassigned = 3, + FirstUnassigned = 4, } } diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index ec07cca2107e..b2a67ee8dd24 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -30,21 +30,30 @@ use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use polkadot_primitives::{ - collator_signature_payload, node_features::FeatureIndex, AvailabilityBitfield, BackedCandidate, - CandidateCommitments, CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, - CommittedCandidateReceipt, CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, - GroupIndex, HeadData, Id as ParaId, IndexedVec, InherentData as ParachainsInherentData, - InvalidDisputeStatementKind, PersistedValidationData, SessionIndex, SigningContext, - UncheckedSigned, ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, - ValidityAttestation, + node_features::FeatureIndex, AvailabilityBitfield, BackedCandidate, CandidateCommitments, + CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CommittedCandidateReceipt, + CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, GroupIndex, HeadData, + Id as ParaId, IndexedVec, InherentData as ParachainsInherentData, InvalidDisputeStatementKind, + PersistedValidationData, SessionIndex, SigningContext, UncheckedSigned, + ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, ValidityAttestation, }; -use sp_core::{sr25519, H256}; +use sp_core::{sr25519, ByteArray, H256}; use sp_runtime::{ generic::Digest, traits::{Header as HeaderT, One, TrailingZeroInput, Zero}, RuntimeAppPublic, }; +/// Create a null collator id. +pub fn dummy_collator() -> CollatorId { + CollatorId::from_slice(&vec![0u8; 32]).expect("32 bytes; qed") +} + +/// Create a null collator signature. +pub fn dummy_collator_signature() -> CollatorSignature { + CollatorSignature::from_slice(&vec![0u8; 64]).expect("64 bytes; qed") +} + fn mock_validation_code() -> ValidationCode { ValidationCode(vec![1, 2, 3]) } @@ -584,7 +593,6 @@ impl BenchBuilder { // This generates a pair and adds it to the keystore, returning just the // public. - let collator_public = CollatorId::generate_pair(None); let header = Self::header(self.block_number); let relay_parent = header.hash(); @@ -614,14 +622,6 @@ impl BenchBuilder { let pov_hash = Default::default(); let validation_code_hash = mock_validation_code().hash(); - let payload = collator_signature_payload( - &relay_parent, - ¶_id, - &persisted_validation_data_hash, - &pov_hash, - &validation_code_hash, - ); - let signature = collator_public.sign(&payload).unwrap(); let mut past_code_meta = paras::ParaPastCodeMeta::>::default(); @@ -634,11 +634,11 @@ impl BenchBuilder { descriptor: CandidateDescriptor:: { para_id, relay_parent, - collator: collator_public, + collator: dummy_collator(), persisted_validation_data_hash, pov_hash, erasure_root: Default::default(), - signature, + signature: dummy_collator_signature(), para_head: head_data.hash(), validation_code_hash, }, diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index 281dc5d0c5f4..a5bb6e6cc60f 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -338,8 +338,6 @@ pub mod pallet { InsufficientBacking, /// Invalid (bad signature, unknown validator, etc.) backing. InvalidBacking, - /// Collator did not sign PoV. - NotCollatorSigned, /// The validation data hash does not match expected. ValidationDataHashMismatch, /// The downward message queue is not processed correctly. @@ -1222,7 +1220,6 @@ impl CandidateCheckContext { /// /// Assures: /// * relay-parent in-bounds - /// * collator signature check passes /// * code hash of commitments matches current code hash /// * para head in the descriptor and commitments match /// @@ -1259,11 +1256,6 @@ impl CandidateCheckContext { ); } - ensure!( - backed_candidate_receipt.descriptor().check_collator_signature().is_ok(), - Error::::NotCollatorSigned, - ); - let validation_code_hash = paras::CurrentCodeHash::::get(para_id) // A candidate for a parachain without current validation code is not scheduled. .ok_or_else(|| Error::::UnscheduledCandidate)?; diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 18def664f4b2..3ead456cde5a 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -1545,16 +1545,52 @@ fn candidate_checks() { None, ); - assert_noop!( - ParaInclusion::process_candidates( - &allowed_relay_parents, - &vec![(thread_a_assignment.0, vec![(backed, thread_a_assignment.1)])] - .into_iter() - .collect(), - &group_validators, - false - ), - Error::::NotCollatorSigned + let ProcessedCandidates { + core_indices: occupied_cores, + candidate_receipt_with_backing_validator_indices, + } = ParaInclusion::process_candidates( + &allowed_relay_parents, + &vec![(thread_a_assignment.0, vec![(backed.clone(), thread_a_assignment.1)])] + .into_iter() + .collect(), + &group_validators, + false, + ) + .expect("candidate is accepted with bad collator signature"); + + assert_eq!(occupied_cores, vec![(CoreIndex::from(2), thread_a)]); + + let mut expected = std::collections::HashMap::< + CandidateHash, + (CandidateReceipt, Vec<(ValidatorIndex, ValidityAttestation)>), + >::new(); + let backed_candidate = backed; + let candidate_receipt_with_backers = expected + .entry(backed_candidate.hash()) + .or_insert_with(|| (backed_candidate.receipt(), Vec::new())); + let (validator_indices, _maybe_core_index) = + backed_candidate.validator_indices_and_core_index(true); + assert_eq!(backed_candidate.validity_votes().len(), validator_indices.count_ones()); + candidate_receipt_with_backers.1.extend( + validator_indices + .iter() + .enumerate() + .filter(|(_, signed)| **signed) + .zip(backed_candidate.validity_votes().iter().cloned()) + .filter_map(|((validator_index_within_group, _), attestation)| { + let grp_idx = GroupIndex(2); + group_validators(grp_idx).map(|validator_indices| { + (validator_indices[validator_index_within_group], attestation) + }) + }), + ); + + assert_eq!( + expected, + candidate_receipt_with_backing_validator_indices + .into_iter() + .map(|c| (c.0.hash(), c)) + .collect() ); } diff --git a/prdoc/pr_4665.prdoc b/prdoc/pr_4665.prdoc new file mode 100644 index 000000000000..7a8ec7398e64 --- /dev/null +++ b/prdoc/pr_4665.prdoc @@ -0,0 +1,21 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Remove runtime collator signature checks" + +doc: + - audience: [Runtime Dev, Node Dev] + description: | + Removes runtime collator signature checks, but these are still being done on the node. Remove collator + and signature from the `ProspectiveCandidate` definition in the inclusion emulator. Add + `CandidateReceiptV2` node feature bit. + +crates: +- name: polkadot-primitives + bump: minor +- name: polkadot-node-subsystem-util + bump: minor +- name: polkadot-node-core-prospective-parachains + bump: patch +- name: polkadot-runtime-parachains + bump: patch From d6f7f495ad25d166b5c0381f67030669f178027a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=B3nal=20Murray?= Date: Thu, 25 Jul 2024 12:23:20 +0100 Subject: [PATCH 013/382] Add people polkadot genesis chainspec (#5124) Published as part of the fellowship [v1.2.6](https://github.com/polkadot-fellows/runtimes/releases/tag/v1.2.6) release and originally intentionally left out of the repo as the hardcoded system chains will soon be removed from the `polkadot-parachain`. After a conversation in https://github.com/paritytech/polkadot-sdk/issues/5112 it was pointed out by @josepot that there should be a single authoritative source for these chainspecs. Since this is already the place for these it will serve until something more fitting can be worked out. --- .../chain-specs/people-polkadot.json | 2961 +++++++++++++++++ .../chain-specs/people-polkadot.json | 1 + .../src/chain_spec/people.rs | 5 +- prdoc/pr_5124.prdoc | 20 + 4 files changed, 2985 insertions(+), 2 deletions(-) create mode 100644 cumulus/parachains/chain-specs/people-polkadot.json create mode 120000 cumulus/polkadot-parachain/chain-specs/people-polkadot.json create mode 100644 prdoc/pr_5124.prdoc diff --git a/cumulus/parachains/chain-specs/people-polkadot.json b/cumulus/parachains/chain-specs/people-polkadot.json new file mode 100644 index 000000000000..6e30829eab49 --- /dev/null +++ b/cumulus/parachains/chain-specs/people-polkadot.json @@ -0,0 +1,2961 @@ +{ + "name": "Polkadot People", + "id": "people-polkadot", + "chainType": "Live", + "bootNodes": [ + "/dns/polkadot-people-connect-0.polkadot.io/tcp/30334/p2p/12D3KooWP7BoJ7nAF9QnsreN8Eft1yHNUhvhxFiQyKFEUePi9mu3", + "/dns/polkadot-people-connect-1.polkadot.io/tcp/30334/p2p/12D3KooWSSfWY3fTGJvGkuNUNBSNVCdLLNJnwkZSNQt7GCRYXu4o", + "/dns/polkadot-people-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWP7BoJ7nAF9QnsreN8Eft1yHNUhvhxFiQyKFEUePi9mu3", + "/dns/polkadot-people-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWSSfWY3fTGJvGkuNUNBSNVCdLLNJnwkZSNQt7GCRYXu4o" + ], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "ss58Format": 0, + "tokenDecimals": 10, + "tokenSymbol": "DOT" + }, + "relay_chain": "polkadot", + "para_id": 1004, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xec030000", + "0x0d715f2646c8f85767b5d2764bb278264e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x15464cac3378d46f113cd5b7a4d71c84476f594316a7dfe49c1f352d95abdaf1": "0x00000000", + "0x15464cac3378d46f113cd5b7a4d71c844e7b9012096b41c4eb3aaf947f6ea429": "0x0200", + "0x15464cac3378d46f113cd5b7a4d71c845579297f4dfb9609e7e4c2ebab9ce40a": "0x1c00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c12708c4107d205432aa56d2018d46daa0b820669d068b11abb5588584f05d4935071a306dd46ee404511af11b9123964dbf1ec087aef60b766f04de5d94891019778eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b86896496c8cf62f725741306fa59a240c8a721f1daa9a6374897e4530a33e5e0b0fe5baaff773e3f134d4e0c90ef5e5f37adb603bb591b7aadf67a2a757101a1b70302e89a539b16e7cf02cde91ab273572701a7fb7bf712212a75d2d48cde142e2b40", + "0x15464cac3378d46f113cd5b7a4d71c84579f5a43435b04a98d64da0cefe18505": "0x00a0acb9030000000000000000000000", + "0x1809d78346727a0ef58c0fa03bafa3234e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x00000000829f057679c4", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da924f98efe9bb1acaf26d612783a88052296c8cf62f725741306fa59a240c8a721f1daa9a6374897e4530a33e5e0b0fe5b": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9381ca18820b278a00faeab03d52440be00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9529b5fafa1d11181be42fa36618c5bd4e89a539b16e7cf02cde91ab273572701a7fb7bf712212a75d2d48cde142e2b40": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da964a799358e9d6f2692d4b47a57ae3ef71a306dd46ee404511af11b9123964dbf1ec087aef60b766f04de5d9489101977": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9831ad22a3ac7b99bbe1efce6cdb4db35aaff773e3f134d4e0c90ef5e5f37adb603bb591b7aadf67a2a757101a1b70302": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9dfc40f350cc4563ad9ddff5ad92944278eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9fa4cae44dc9cd8d9310d27efe3f67fb108c4107d205432aa56d2018d46daa0b820669d068b11abb5588584f05d493507": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x5a283d003c70656f706c652d706f6c6b61646f74", + "0x2aeddc77fe58c98d50bd37f1b90840f94e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x3a63": "0x", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd00585cc104ae0b06b3124f107867940ef054d851429d2d9e8a3d35e90ea28b60bcdafdd3951e23ebde3a5b1034f580f84a9d1e1ef7530ccc0805df70662f231ff255939f72a121b998ab3ff8b60c4d36217bcbbde59652ca945226149911c41141309e91bc8627a179e6ebfb729290a9419b8f4e9e440e8ef9fa7c3949c01a04b31af49ec31a04ddca3c28ab41af2ec11ef4bcc6479e448d1a6432394c0daae1241987f120981a64e2dce441304e9de431bebebd9c24ae1a7479b763a61a84c32d771b6fcb4d1ed46eaa41523aac4197430f3239e649443fd5201a347cab41d8ac41d365300f9271ac06659907616ec34b103aad4152fa5583b21a44e397c3cca10751873548963c48fa0e5e3d087aad413468788d1a54c32dcb6b6a108d1aaf61d5f0ea4196d71a5433b31a0f9a5c5d0255aa4125af51c36d946ad4c0e17e5d5e43735dd835656a98604619b47a5e30d9c36a48928ca4fd4c23523f190b66f0b26a6092c298609e0c6924af39330c5a354614c24c6a1a59fdfa22c9c81a99ac61ca9e8d0e8835490673d4a85e3636251396cd1d76f0a0f61d76983030385c07f85c07aa4387075dae43878c65911c87ef402fdf41eaa08307c9b80e363a3c520de8385c8769e43a1ce541308eda61070fc27c87ba83a33c684a1b1b4cc671b80eb286ebd03636363a3cc8721d35c8c6669439aa561a0fa24e331d87ef50e3340e83f21d3cc8e43be8a0430eaf354807f7ea3b78d0e5ee411007e7a5529b1c87dbd0d0b8d7d4b80da771a7f1a091d33cb7f11b0f22f90df798719407911cc59c3bca6f3c08c6dd839eefa0c3cc0c0e9fd9c1673c48c667d883309ff16ac3867b4d0da2a94134ee6ea3c66d7810751bec41d2abebe041d075e01c361e64b94d0dca61e3386a500eafa9711d3c28731c1e34fda606e9d0e15c0db251836c78a9e43335e874721b3568c64b25b7e14135dc460d3ad9f09307d1aab337ec9ca39c6e4e53834e35e8e4344ea9e3f0a0cb71d4a0cd1d8707619588bd61df1ce5415735626fd8513548475d8277c0bcd6a0eb721a35888663d5310f9a1503580dba309fa94197d3e9d483642a04f6867dc6836ad406ec0dbb0e35c8a686e7a841386a100eaf5c0df21b8ea306ad189cb4e18b2f3548830b9eecc84441055040c10d6698411251d84942c675decdc2d4cda1eebb59bf51fdddac9beadc1bf61a1a199fa9413660fc54835447b8620c59b4eca0074f38c28e8c8a06485ca1054ab0421bb44cb123e34132358854b7983adfcd3a4cdd77b3a376a85f1fd593076555047bc36eaa4135ea12bc9344e6346a10c94b75891e3b498c7cab4159e6a41aa4d281143848d181196590e10c3ba3c582972b72d0021a98a18436ec8c3c685483acda43aba777b39ed57d373dd4ce875f87d587ae45f6869dd620ac2e81da4902fa55830e40042dca008432a6e0c5073bb32ee13b49e0e0620d43d8e20a4ab8610c3b49b45b1e64d520597d6fd88362dddecd3aacfb6e78a85d8f5f7f15b537ecebaa27a4a30190041b9c2411868ead9fbfe7efbd24a8f8f7de7b4be8ead8d6ca1aca78ef547bc3ad53ed36d38919df9e75ea7db37fcfb5e922e8ef152ee0b0be5af77b8f1f9735aa7dc7c6dded358fd9e773a511c20e9d5fc7df8fd90bba4fb9997ba575b6eb6376aae37f4e800dc8d6493b630e7eb63e96b097ffb1be090d127d1973efb526322bfa26d9687d2ce1a1ff51c0b3b7af36040eb116641b7af66ce8d95b0bdd9f9deada9f6f3b7bf79c3eae87adfd0d69f77975366a1940e3ef1528b8bca986ae8e0d7a8f4e75d3874e759b772a3a013a053ffaa953bb6281968f5ed3a9fef85df65d8f8fbe9dd33b5df6dde9a36f9d6a8f6eead4f3e8d1a3af4dac3ebb6227947d6dba1eff9c32b74f39631ed82886eeda10e856bd28f5ae1dd07b76cc4d9dca6636a5ac9d74cce59cfe7ceb94f4e77472fc5472dba86d94e6ac01e9878dd26aa739e693dbd738cabdfc4693dbcfb87dc9a14ecfbe8bc3b5b33cfa73fa9829b78f715301ec1707149fd9f4ed746967ab0191ce95b3b88edff2a7ad2cb177d93fa7505b58ca1a65d3a8e7d3bd5113feba4eeb3c8bdb5f6e1bd50eb98eff87bf7cf9f2e53bd5af4bae037abfce7536aa9d359eadb16e8d6ad7e9cce49d5a7f5fd329ffe73bd6c7fbe7db29f6cee69fafcda31c8d40d7a6dbdabbd937edeb7471b61edbae9c38f95da5c0c977a46f1fc0ef0a892cdf1a0f8f8dbd7d3bd529cbdb67a7f6db696b3c56ddedf9e3f62d8e9fb2f69c3e0e887f7860a3a007d9fe396b0dd86d54973dc7daa93ece67711dfc0e3ad07be9abf144d7694a5703226b075ffa748bebfc2d7f5cd775dd1301119d116ff936ca729d37b99fa07f6e715dcd43a79c496eff71fbab0d81ff22f7f3fe39e4b651ecabf1bc0a1bc5befde65059fc94a08bdad6d7a2db295efe754ada4eed53e6766ba7b433de9ecf94aa6cd09fb3b7c6c375b7f6c76da39ed3ce668070810c8bb3bf1aacdb289d5e8d0736eae9346b40a8be7d351e15334381ae4de7ff9cee0e1b5d9c6d1bd56d3cecacfd30626868e88867a79dc14eb1af6fa7dad7579b8d7aaed3cc658d7abe63eb4cffbc1d7a37ff3935750a366afdf9a953fccf6b3ac5fe9c7276ead4fef3d520d704c73351cd6f9fdf9546483bf4765881f8b96ea3d6570bc2deb56b67d7699d5e9b577dfc3787aa99b1d00562b3e9143b7b0dbbb3a356cfbe837fd85f9fbfdecd67df97b37dd36dbfde6def4321b0b1f36f17607d35ba3c6cddf6eca84e75d9a9531dead9d977748a9f7debd4ae90a0f2eca64ef5b36fe7f04e57f3ecec6bc3d5a78704e802b175d9b7679dda2b5bacbe7debd4ae5660c6b79b3ab5de7eea147b7b0dffc0dfbe365d7dfc37878a390174756cbb3aa289efe6af6f9dda2b5bac7e7d766abb00a3cbaf679de25fdfced99dcef4eb6bb3d5c77f73a898155082c245408313b01214a5176c40d87eb059b155b1f960ebc1c6836dca2665dbc1a6838d8a2d071b0eb61b6c536c36d86ab049b1d16083b2c56083c116c58604890d0f08ef07cf8a57c5f3c1cc620e616231ad98554c1f6453c89e904521bb926119e560b485511c482d1895314a03a909d20a484120512101816405a90b9215520e4837204d41b201490a120d4833205d2179710d6529c890b85230b1901d912d91a92053224b8272a15e184931a2c1680623198ca08c62308a6204c5e80523178c9e18b160c4c4e8c9c8c90889d111a321ed8b1607cd0c6d0d5a1a34346867d0bc6866d0c8a08d4103833686c6056d0b5a16b42d1a16b42b686268616853d0a430ad408a4306854b091214a41790644082428a01290a120c484a207141ba822404ad06231c90aa6459e8113627d9d0b6041215242290a290b220d58034041216b40833cad442894081901d8126a1a74091c02f6017700b58064c038e41cf104290980882010bd801f2834727013f3820480352241714600002d88e285151ca410907a51b94a294a628d9a054839214251a94645082529a412906a5284a30283d51624169688bc30687ed0d1b1b36356c64d8ccb08d6103c3f685cd0bdb185b1736256c5c6460bb6283627bc146b439b12561021220d992302ad2a8a041419b429b812603a6c240e01fb0155c05fb807bc03ce0292c8577c03a602a38078c03be0147e129d8065c0396a27b3ac8bcc8ba207dc9b8d886b6146452b22bb21b6c4d644e6214281a0240b7d856408d40b3a043a058d02ba810681028150a030a0525dac2b075d9c8d8b8b0956113037582b2803641574095d8d2b0ad6173c376c6d606ea84a68022418fa043d8170c0e581b303750156066606cc0d64093a04f6c67d8bc9498c0bc6c5f4a4d9496283d29a9a0a44429899293520a4a486066c0ca808d01130316060c0cd817302f60451897adca4604ac0bbc843705c685d20ab037d02718194a4e60646063606ac0cac0d280a1013b036d0175017dc19b426340a3a032a050e80ca8149406a5233633b033e812b406581c2813d406748a2ea251280e36166c2dd89ec0b6806901cb02b605c3c2e682e602bb0266054c0b56058c0a9818af085818d81430296051e82c1813302cd815cc0bac0bcc0ab6044c0958160c0ad8133030b02f3027604de82e605cf4137a0ab6457f8125014302a6057604cc085811b02a1811b02cb021605760586042c0828051c18080fd00b3a265805581f900eb01c6036c0a2605db01a6038c0a2c07180eb01b60519a0b3605e90c980db01a60527016180db61526030cca36042c065814180c3028b017602ec05ad036c088b02730273016604d602bc098c096c09e602ac094c092c09c6029c090c08ec086ae2f571c2e385c675c6fb8dc70b5e132e362c3b5864b0d5719171aae335c5e2e335c65b8c87091718de112c31586abcb0586c782eb0b9717ae31ae2e5c5cb8b85c45d7162e2d5c59b8b65c58b8aef098b8ac7069b9aa7051e112e30ae39ac225852b0a57960b0ad7132e30ae2f2e275c4db8987061b9ae5c5e5c5d5c56ae255c4ab8b8b856d71657122e24740d2e2dae235c46b88af06a7055b9887065710da1a5b8b0b8aeb8847005e1a27201e1fac123c265c555c5e583ab07170f5e1397946b07970e2e2aae1c5c38b8a25c53b4102e1b7012ae1a5c525c33b8647041b962d061b4960b8abe4263e17ac1e582ab059713d70a2e263808970a2e27db96d983c98339654a993b80494c1d4c2a660e260ee60d36265e95c783fec206b02e2fcab3c19586cd08db125b165b11de13cf89a7c44be23979297848bc23de507fe938341cfa8c7e43bba1dbd066341b7a0dad862ea3d3d068e833b497364397a1c9d064f4185a0c1d86eed260682e74167a4b53a1add0556831b05533a19bd06074109a4a03a17fd0566c5b7415ed83ee41f3e02dd139682a5a078d83be4147e9299a063d8386426fc04a602e9e0a78c548e02d580b3e021b8185c0456022f010808ce01990d0c050982ce9800730b005e10216107213bd40f997a90fae7e3d4c8c7c800811467a44408408e53744921e2246808c0c418408b6dc83e403468092140102224042f42c39324492234488d8c0c1d190186947d068431b82232048a2a407c848cf1221981c61d293a407884df64508224a4090240812244a160024440f12244a16d04316871e191c902031220412244616708608804290a407c91bb6a7070444940011011241088ef490e0266bc3324172c40850cf10447a7a40a0235bc32a01419223444ab0a4a7889f12188180676ad81e2192284942c4089123447c8f1049943c002849114a8410e26f64650ca144889e21706467582625e8f121c28449097a842451728409939e0970999725c111227e498fcf12251718c26565d81e2023431c4162a409919e227a8a88000586e80941088c50a0878c0ccbc447881e11081186650294a467498f08921cf1ac4b922320a0c9beb0421c6192e4c81220237a8894000911441821b2a4670824478ad8f1114209929d9acc0bcb64262b5a23446a322dec0888180132328411a09e204ad916131c4162e408d091244760322deb238412241f20d2035484103d46f4d0c8aab04b942c20cba8b049941051049121901c29e2034492181182c811221e2849cf0842d03381217a8628028910498e24a980912545bc8e4c8c65d27304e8c8122522002ac2488d2c8c4da2e408909125450c410448091226448e10f1469614f130dcb482e5e50a17caf0aba2bb1a1a7a563d1b327e217e8189600b964aa5923becbdf1bafb4dda54cab792f93537afb4de5ad7db2c8b662fbe8da6ab5b46d35afb9ae5b2e45ed9bb3824c307df83afe96b6bb9e583bbf0edc6045c5b77f77b9605dfdb95d6830c97f77a2fb3aced7e56f6f6dac78f97e3be8c6ef35a566fbf9dcb4f6e5bbd9dbdccba322bb3f89299b5d7e3ebf5be27e56e373f4bca392733cf6e6e9a0d0196949b5d96f51e85afadd7fdfa59d765bdf7de6b4bcb41db5d2925af5c6bdf9b533e5eee77c24e8b618d61bd8b618b61bbcbd8628bed2e86611826a194524a29e5dc297bad9818290f1013b33de5eeee940b1bae94134e2a77ae6c0877e54ac852b264e691d5ab71af36ea662e45ba3fc8d7cc71574a5eb924e65d5e665e2957ca656e6e53b3e4de6ede7e8f7b247dc8b70690bdef754bc9cdfdf649c972e7dc9e2b5bee2ea52df7d5581cbd8fda780be9ebbdba47567733efbeddf561e576f7c5bdd6ccbe9c6e6883b4fbd8d4a902b45c0268bb3994965bca0108807b795b5a5b2f4bee26691c80de5ddadbb377adddded73dba5f2ff35be696cb346ced9a7a7732338fddb5b2ddeea6cb3bda1d8db41e2def6a3c7b1f00deecdde6c9bb2dbb7771e69cbbb37bbb7bf676ef8ea494529370e16466dee6dd9d6b6d645e9ecc56ecddd6b8997b7bbbb765336f33f3f2f2eef6f62e77f74e5ec9cc9bb1b45e46ad776d568cb1dbda95fd2ed9ddf0f1b3762dcee4db7ecc6ff76d77cb96d26a29a5b5c36a2a7b9010364b09679a59ca9617ec191e6eba97e57bddefc5ebddd85e29574269c1e5d72c7963094a69068fc6760996281941cf08a48d0d40089420211282244742100225486c90bc86113d4324394224c9112322100204205052078004c9119f1e281d3a503ba06076c0e8d081079b1ea421901c118110448090146104043d43dc2c3102c4440789898dd38c8f10463e80048992253ce430d2b36446a6c7870890113d4972e809810d1a4c80981c31a2a726020961844912234b8088001d19c10894a06056004740d0b384885a6389911e2442d0908648d24304114688f484c0c81225404cea0600c8c8929e243d21d0815b01002951b284c8103d4b7a8658a2a4e7c812201e8ce811414d0d100015410428c9912544c86c004670042849114b8808c1e4c808aa009630a9d9009400044796f4342162800014a0000218a22707b60218a28709939e25448c4089104496f434512244cf122246980c21440f9100f8a40a20002284e8614244091011255822444e0e3e0820004e80141123949000e8068d5401044044003d3e46907c804808921c110113232270eee95902829a26497adedc001049a2e40893253d4d888c80c812254046845082048911224046f4e4d02449cfd3916dd1ab52a9689155a9b0222ba49a4584788ba8f812125249d5b38454ab52a9845e916d215844c542422d2424f48aa85848d545542c24242424a452c9222ba452bd222a5609a95e91555d4554fc8a08b18a8b08b19090100b0975911552759115e2222a16e2222b242424a48ab3c80a097511156f91151252a9a0ca2aa26221952ca26215ab6217d92da262954af554b0c8aa54aa5764555c6455aa2ea2802706ef2e8f8089910d8fb9f78ff3f9097acbf797eb18cab7432dc87be80c3bf897af775c81aad4782c1ecb57db9277d249de519fbe9db3b9e5256efbc6d23c03eab71cf3d14d07f4def238aadd6ed13259bbe8ed932b55d96805ea97de61d1a7b70644569f27d9fbaa40fdcf27fbacdd6e79e86d551f0ec22e6bd6a87dd875d4f18a200c3df77b85ca96afa28ca7a49177a6e95a166bc3f6d52ca7b433d6b82e6c1e627a3947ee88adf37f307bda3e73fb429b8fe5f13b7fb75ca7bd5317468acc916aa77aea975fbe1a659eab62a051ecf0ad174792ab319d6a7a611cda8731123dbb6a93de996a3a75f973a18ddf6d3a6561d49ff718f9ebf83b668279506ee23ba07eec0a952fbef30338152bffdc66c76a58e527cc035a5cc73fc240a3e09038e260a3f83df50ee8b9c66da3a2671c6c54ac1df499921136a07e5a3b4cd68e2db76a07d4b1d6b5791a5f38b8a245184f399b7db33551c618dfe3f70a10b068e2e9e26cacc503818af7df2b3f70f274e966963664f49d741a93ebe6cfe8ddd133df4e6d7e39ec14c9af7a952ed7a0c70b6a4030c04c408da74f8d7ad5d4a84783ab21c05bde99fe7969e3b651d3496fe492d332aee32678320afd39063a05bd9d9ff00ffbededd0598ba7dcd4788236147b373366cee23a9853c33ba0f7edd03b09fde26a70db37ec4f1b42bfa1ebbc29b96eb73caccc04f360d7690c746a1f3a33c13fec43971a0facbb7113bf756cc3952abe7800fc5ea9428aa724cefe45970e3b67baf476c9c146b13604e8a5c7e7164b281dfae460df40a0871e6be71dd077eb48b26fef54eca502aa9d76143c4fe255152860a7bdab50330eca0bbb76aa0e09f6cfa1b73624faab1df458a548871a907d294f7c4756216cc78ae74f13723dac1d047e17e7fae6ac6ba7fade37ea2af0a4e5f5b1ac7add505018040f3dd4866c2feb83efc1fa1ebf80ae16e5a167440f2700fd7a8197bf7cbea0e8a16b500c3df4e99444805fef0ef04b72cdd74deb356ed3a9ccd7519dba7cbd870f9dba3818e38b168671fbd69c9eed93a6eb348d10ea123aa5235f2d084ab11175cb69cc7c3f66f587e5d123e616c71fa5c7daf1d32cbbb421d0b3ea23bd83ce44f5974f6d0813d563ee7339745abbcbb11a25b797ea25a779c6a9688444d73cbae658ed548f91b8e9988fe6303a8973e2c48913922bb11b00e61d257603d0bdbf6ab7a3f37e829ec4edce7c3c25246c9a3f4d08f4cb9f1664fa553bcd67a5b99cbaf4d6826895e6aa4232b73c73cba1d38cfa64417496f1c7da5d3e31a789be3ffde26862fd217dfaacfb985f8fc5cbc7eaae39fd79ac3ffc25fa1b027dd6cef2cb31e68f1cbf10abb0a650b400cb6397a58d02dbc2d79ad02069fcd9fad8dd516b43e0d0d0aff3fa3acf92b95cb7944dc7c3dec5910e9d39cbdb9ff6837d1d5b1f56ed9cb51f3c391a215665c79e23dd76e660a35ab6cb754c1b4217a77d2bb638cd432fb95d9cee237e753caca6e6823e7f9c063676c863ebe6f7c6c48ae3376960e3d74b1c4671c64b7f33c3401128c2788610c040190f6e79a84ed1488f0e814e753618e854872a22824e75a7c50244a7785730c0e223f658aeef76cb181fbf5b3272e24e07ff6dfd91a53fe9bbec40f297dfa6533ffc65c93a907ce9ac0921f2edb27db9ebee0e7e1a0f3b65ce476a40386b3fde73b64ef0d283508002a129f4d2b73b1510ed3eb376cfd73bf943fa13d2fe2a0a987b4eb95bc8739655089f3a6579f49a55b2416738fc5ed962b59976050595975cfcad532b3d3a6d0d0838c0210e71f8d5beec8e75c9f1975f6d6877f4d011cfdecfcbb5a23ffc9d79f4e698f05fd56741b0ed0aca93effaa333b78417273ad0fc9ffdf2d457fb91398651aefcfc19d7498f7e71eb7427003d73ba1cd8760505ca3fbf9cb98e63d6ab7577f9f3d68408c9fc720a33d68058d42f2e73a809993fa673ddc561d7e98cebbaf2e2b05f4eb9ae9fd7af26f37f5e1c86805825b9b940f7a71d8b8f55f83844c4d640f85d4551c3f714bf2b1b50e151bf2b1b28f1ec446c4f09bfab195879b6f2bb92c1d0eff85d4199f2eba37d60bfcc033a697d2c1cfef96e8ee49be7ebcb0ea58e0e86b038fb1a7c1d1d5cb13cf661edba7642dfa43f044bf8d92f13b6d12c5b1f5e64d9f23fa46f329ffdb9e41ff6f7bbfe4efe73fab2a805a167107d07e40ca220f2bb225ae0051845ff53e4d98b0dfe4768f41d1017b4709ae23ba02953502e7c07b4022ec4ef985861083d1821cb774caa647976d682cca1a1204f0bb25f9e3d6a41fc7f2067c1da316b42a477ed76f0c56b3fa047d779dda9def2c8d108895578e892f3e998a81ebae53ffbe5cb17ae53b5f5c5472aa878cee26215be391ccf1a106f02132b500bf16f6fe991f3e94a1304ca4a2304bae5d2a3b3f603ba5581e8ae7480e581fa63edda9bf047971cf791e3be81e8b7ef137a68adf0fcc0a1b745b04adc22c758fe81bb6cf9f5e69cdd59c7f80738f4ebd4ca9ef663b75873ce297bcec77346d4889d2291e5d92d67e83472f59b296f91b9c5b1679c8fe5ec4f036209b19cad55b2edea89a2c7dcf221dbae7450e5b1cf9cd297b10664fae518d75dee63711de696538ba3090287865e73f8ef57738bebd75ca7ad2ceee290bcdde2464ee23a0b6a3f2c1f556fed87e59bebbc8da31162f9c82d1f558e4608f696636fd599c581d22d6fa7dbcbd91de816676a1474928fb8fdc5e932b7b421d8673eb33ea0476d08cde240cfeacc68c4f1d0af068487a4945ca7b79753aab06fa04352ede6779b8fe59877962f10aa2d300cc35ca729cc58fbc16e3d2d88e598cf6871a09316070e3d5c1c9864834347bce6f4a25a25f92ece45f2d1c575efa98fe8e8a2ae714b46a4cc759a94f9886a995f34abbb3b68bddefec5ad5b1dfbcaba35ea79fd76adad26280536f8bb7a820914d8e2efea092bbb389be3092cbee677f50494efa16ae8107a14fa17c778587fde972fd007494db24f20ea5e6597ab0dd92710bdac52a8d077918b49f609441fbd33c1cb289320c0c9ea63958202a159d7887fce4e1b72cf57d380141408ed183b97af94a11b2cd001145196e810050858868088d8315eca108f2a2861a809029cac8c18ea4cf011891d63e7aa433bc60efb732943315a08420ac23034b4b3150542b30e4187b523e29f97b868610771703234b4b35a10353eb6ec0125faf6751ddfed72fb3deb63498f97025db55ff0e70b6bac4e94c0d989fea4c64e139d327dfb4c89cad621f9f51edf99be5b2f40c049e4b88946b5fb6c1d37f1eddd7ae1db83a06fef460d5210610a2250291a1ada89febcdbbe771e37047de0440e90c00112769c2881b3f3fc498d1de84489de91fe24013bcf23f724013b415650e0791004de4dfbcbf2edd183aca080f4588374de4dbbac41565020faab41fe6eda63edd1ed6af5bcdb2cbb061530410a19aed0032e52285db1ed8e0a065f20c10636e0428b10d2b0b33bd0bb1edfbe1a057c6868e779139dd2f1ed907bdcbe1ba2df1c4250c50dad781072d199ad18ad1823ac6954f4d6f877e5c40b783a3ae4f9e12f0f5dd69a4645081fec86c2fa5c1fd1d7697cfca63fe94ffab3fcf994cf9ff4f71e954430722b4bedd2593a0d74fe7e1af884f4c8b1acb0513b23c70fb95d7db1fdf307ffbdf77c661bf01e7e572bc0c1ef88f2d8d86589fd07f6ccbf1a4fbf7ddd3c844aa794351ecca943b7b8cc777d681e9d6a9cea3b6a693e96af538efeae9c187ab838b1976bcf38cb2f8ebf7db9e91937fd02c2beaed3b376ebd3e9721df575fab2c9b5e6bc3ed37da8b7631c7d97d32032a7d479f78789ea35ef48f006387c79269b650a144cbc563b4a69cc26d797432ab3c9b5af064448563b23fe72ea7435205b3b8fda0fcc31d779cbedb3c643ab0ff603f3ad98af1664fe557da6b7775348fbf48bdb87dcaa28c328d05d1fd2d723951c011a159d60e27b342afa6a5dcd477fab099165359d8379f403ac0f5f9ce9d177746a6bd4bc3c3ac66ddf4cb7e99c49f331c7d437d3a36f97ef08f0d169d6077fa41757d3a8e9ef77e5840af88b2c419ffefc77c58230fec4da741acbdff7d358f587bfbce5bb72c2c9af174148c39b38be6a4da3a6c5c5c5595f0d4a8eff49a1dba9a50fc8abbdbb63b521fdb88e5fe638c1dc8915cf2f5fa7f4e276d54d3b7bd9c56dd150d48080da90f7cf316e779ac6ed7fee00d5133f87f4582f0fd0f9e94f13d2a4b79c721c7542449cb5d6009dec690dd0f9e9ad0149f23f426ff96a41b20e077d596b0e504df1d3e9cba0f64315e57f8afc74dadc3b1a7542515eee0e6a6552b36686cd8bdb1d7a6518b73b17b73bad056997b5e3e67da34ee87771ba5db5a0e8a563dc76713e34343434b41304faab4ffb31c20912470cfd1259bbd7da10e8ef7d9e475fad01fb562de2f440a7977517a77bceeff9b0438e464857826fdf5f8da75da7855a253b9a315700c5bbace3f1ec9dcdb35bcf2b26cae0d307319ac28a092c662c57847dc0b6ab279e3c0ffd734a5a5f9b8e87be9de670b65c04361f3ee2a3f76f175480c2f3b3b72684875efa724d6090edd79feffae8a0d304c11ed61fd2af6f15bec7c7dabd0abdf7cbd3ce96b3629db51fffd9fed51a1efacda18adc3ca01ad828e5b1ad9b6a500a6c70a32b4b59a360a34cde000860016518d0a1cfd96ddff94ba7ab638b7ed2e954371bd0a96e2bd229e907908e81a197ce814ebd976fb5bcf4e1e57ef1d2a1eb74135fbc94524ae96b23abcf7a69326810cba153c87116744ba7e1bc5ca721c73f5dfae59dea92935a5c137ec835e1b79c32e42ca7ddbe38d0c70ac2720afda706d2a52f839cc5b9d5b376f0a74bb7b8d7b076d3d92dd779d64fd0b7d556cf596777b051d35bfbc19f39e4e0acabf1646e719de54db64702878ef86efa8f75c6115fbec9fce990eb347f1e391fcca3530aeb84d07fa613248ef8254de45fdec47a26aabf2a7c3e81fa473eaa3f7cc6631a07d48f79c6fdbcc72a4d105e7d74a8f1447f1c749d8779e49af43fa7ddbc3bae1ffcd1310e5ab5e3b7fce23abeb84e66d775f955a7eb3c29df774c54cf0e3dab3fa0b363dc7eac9d557d60fd09faee3d7b90eddb759a72a4353d1ae8ecd09ba381de6e39851c3b65ad013a96b3434efbd16e390cd26e59ce3ffc650950b65a10228214f9e973dd07bae574721d7ce8efaa4b7ed6cbd0d003d1f8e9ef01653f871081714b7e14f0bd38d081e463def543bfb8259803c99fbe9a10227fb94e4fee07c75f8e713f0a785e9c8bfbe12f7ff9e43a7e78d5dd1db0b51fd1a1b3c6236ba77ae9909b9363b7b8fdf5c8751a0f5cf232ceb0608592a2d3c8edb3e6f1c75ad9a1a420d4a9d30cf3cd1ccbfc720ce34c8d5abfb88d5b67a7946bc2cf5c137e4aa95338e2aad394f3917e6d9da26ef93a6b247eba1acfcad2d628cc7b7ce6acf170d5793acd5cc7ef23fdf2f5c16f71be38eb5ba77afc3ae5b646ed7c4b88f4ab4212e6d1a167ce9a04a663f5d4a8f5ac9ab6512b9d9dd2d5aedaa9fef28c632ee33acc2fac76f031a7cedceb8e3ff3ce01aa2d7e7747563bcc1fc6ed63dc3e5fcf75ba7fe05baed33a4d25d745679f1c0dd71f7eabd658191afa1e70dee26a1ac51f5516c7305008b9086c3ed0757cf77b658bd5ae8faec747550c827da4317a076b27f4d0bb2b88edc2c72a65fa72d32367011e55e0e2cbce3ae4baa0873e48768cb75c7a27f4d1777fd8e5d1ad173da0f240fc564db263fccffbf2e565958202a17d02d1ceac43eb5dd0c7da41efae7faef320a7e32967935b5f4d03fb04a29dd5f142ac8add5268428b5e42d42f679d308589a12950d9e20b32764c5b74810c3900230b3d58ed6c54b6ac51ecd6af175e80f2fc9b103a18afacb630dd4cf566b6510d21e4f857db007dc27d41590bd61c40830510f982a1eb34acd3e685802a606b14893202b6d51ca07a08ab8a9901dde9dffa7c0e77baec817ca1c57bbbed9df1b6de1a10aaefda7db6fe08fdfa6e3b50c117bf2b1554f11de9a1f7f07b6555c69360a79e53ded879831a10aa573d7b73a52af4693cb15fb69d925e3242f475ded823c74120fcd2757a768ae607bf7459e7b3478e7f6d60f5d9e24015c0beeed3cecc958cc0fb83ee6fbb5a428a8fbfab275afc96e822207b4ae24d7ad7e730caf64ed1fc687ffea4b3c34e75d17d9e43972e7d76aa65dd2d7b9dd6691533674117011b256da776a5822dbf0e3bd5fe7c3deb140442051d3a251d607bdefe23f4f0879f3f402d93937cdf0d0fce83fbcb4922f3249660bfe1db9e848cefbbe9c17b1879124bb0efbbd9e13bfcf4729220d51880703e4ce3b1a6695e1acfacbb31cf18a37fd4782ccbb2e68c31660f61df40d9c1cbafdac16bd66d549496ece0c358a16439443704dbae8030f436bfab1f6c89a24b14660c3d5d22db2a010ad1d32db2ad12a044794a92be1bc538fe9952958d260874cca163d50773cb3bacd204b1a2d32a25f3676515f68df478a930ae8318362ddf57b22cd55b0eb5d928cba9cc305fd6ae46591d50ff05a4288c4e3d56811485d1a8fd8ee9f30752b4a5c3d8075294e5296f408ab4346a9f52eab47690d246618ed509a4a86872408ab6340a48d1179d52fd7e6bcb252045581ab55f94653521d6972f5fe2f0560552f445a37603834233a8143c1bcad42912f390b018fd6ee17ff8cbef96e729308b4452e3797280ced99d7e32f42d39c6ed185f8e113b697d70ddc55959c980bb5be5117037004aa010ae1350bca9515ba3cfc569a7cbbf3e7364d3be83edd7fa585ec10d7ec34263168effe96a40fcb976fc1ce6f87c9bac351e5bd7c3872ea850f4ed048807f87653cde270f780391e5bb77d9fda7bb91e3e74aa737ca7bd0140ac8ff7eda84e75bba9e6dbe6dbb3dd2ccc032a54b0c0c2cb18636c05b65d01018b6fdf5515617c97adaab0f2dd34000562c4848d7afc0ea0ac043be9d4fa7baf63f4f7e22b8af2d1e98d4e875fef707e757cf48e004ddeafeffab02eccd7b3cd04b339f3dd445fe7fd61be9c1eef267ae6dd9cd2e7f30e077b8f97838177137d66b59bdbacdd267b3c0e1cdff5d8a453b82698416ee332ec82cedcf2805d4d8b035da7e7cb81d91be8d17753bba88e003a7074f8f9f1469cce1c065e0e0fbd9be8736e5b778a4e7968cead76302f873efad382f8773ecf4ee1728fb26a5a1cb92d4a3ae5b24549c7b8b928e9170717b53f4c5bf6633e7c587f829e9d6b9146c5e6fa25b784038b137d0b1fbd03cb23fa560c342afa6ae128bf3954932d2d9868eb4cb16b3a05d4ffe003f507e9a121a086cf0e21b318f0c1ca8b8a27f8f6f8fda0f4253ce27bd80ebf1f940e349f1747e8db3988f7123a8d10ca5819760ad6d7d129ec1c2756f0f2f3f5d1bf52a8d39741871b5c2285faa39be53a4dad2ca31cef342733ca9dfa263ab7ea678dcc456727b6e79d098869510dbf6bd6a8e831e8318fd1359ee9a6cb63bcead384f0972f5fbe7c3bfcd5bad20499fe7c56d8a8e8af36e9df1ab5cc82adcb3e5a7509d0fc2540dbd7342a9e16277a73369647f4c7ede2485f593989ed75a6971d83407705db3aed7e6f216c7fdfdba8e5f7de882608fcf6feae4c9a19caf42d62db2e52a0e5a7fb166d97e4ae53cd6ef60c975d6a3c3c67956dfa355d6a40a87e3a859964eb72cb721f74764c2c6f599677ab25da70a84659deda0f23bc5196b3129be5d13b7feb72ebf26ebe7579b7bd7579777acba506c47f4ea7bba5ac51d2bb1d6f59dea353cf2d09a7557db628da008b19ff9c5e61c293b7bcbbfc738a45cb5bded269f42efa9220db33697f907a5352171d88df72768ba39cc5d104796ef9ab3eb4dd72a905a1de6ec51fd233f11df22a74a80db1bc236cc2df0e9d92a6d3a57e39cd0fbfe54de0b3ff909ed93187da10e9b436790efd6943a2736dc26fd5eebd25edaf362b4d10cb9f5bd54756e8d30ec41f7ffcbcfac38f318c015d9b8e570fe7747ecf4aa72c2cf0a753a809793edf9cf3694138ce2c56d82fa5b374b6a090d9762a0b87f10b975913f25cc6fda805618c99b1ba358afd5913f2dcb2a4f4283d3a713281e716166ff9b49ec72ea0f8e8b244a354729b9a1ada0f5ebd55bb8c7a073fc6da51ce89155df4f6892e9d6e9a1096d2b520d1a5af765dd5b95fe765279b1e076800058a2031a253cd442c054f612c4cd95298d6322b6564d78cc9b6533ed237fb9dde3ed32d4f6942b80b2afeb9653973fb530b32ddf2d5b66b72b242f8783b8e7865b5268637b92070d65d54372468e33f2e081c5a62f9121eec4027e832480f3d3bd72596034de832087c2b080a1e0af977e5892dff5cf5ad0151b1474d88ccba5ff3c2d1cf7b4bd6657f5a10b1427e95c5ed85fdf13f8e2f67a7f3aa0fe069b9c3f73420543f53aab23d9fcecf75462e5ed7e540f62f577d91e92d7d35866e71aa670e28beac308a08df6a7960d0b5e9aea14e9d5ccaf876ca79059b74ca4cb7f93d38a209123ffafb583bebcb972f677cf4c8b506e4e4943793afc653c3759a4608459ebcf39730dea15e4279379007428153de09a676a8cdbf8350a605a1c4dacd5515547051852f9ef4cb0515a0f058fe3141cb3f8f333b712897be3d79e9b053d172e9196f26678da7867f00ba109b7a8b8271e845f8875d1e300e7d719c6436d2c378371ff3dd1c6b6f601cebf1ac595ca94a74d664dd2dba0684eaa35b4e83eb68f80da7377c73e834cb64380b8b6525071c7494d11c9c6e19d47ef8d4e4e0376ab81c326bc544eca453371c3a3fe91c77e84cc45230155c0543c150380a4f79e89bf6a3ffc6952afe799412fa0d8e3fca2dc6b8b509fff663f3492d184fc597039f74f3ca6038d8281887710e746aabdde637aa9bb41ffd30343ffc30de04bee53fa4b76ab736e4b9559bf0c3d46e8beeb3f9741a1a101fe940fc321e2b2fb1c52f3a9583438f593a078767321c84445c87d128e8ceb596be814e61329927340ef0c8c446727648c5430b265241055681ab4e917cca4358c543ef20160f7d3ba8c543afb1c4d66d180f6b68407c3697d507881f36d128e834863b6e4c1cddf01cb8c6d228e838b8fea26fe094d7a0478ea56086e2c968049fc4bf51d9c9932d3a0caca2739c5871920e651c3a94a253964387512015704aa7681cc632b08c9f742ac621fc132705874b4eca13dfc1b1fa2e96f170fb53edf84f2e396a69d1291920fe39f80defe0cb96a95d0e35bb319b9cbc46729db57a68adbed0328617eb8c275044a9a28a952c5bbacc323a85837362853c9d1c76e1822e2f7dc683887eaa5dfdce60679234dbc975ba351e19a7e1fa9fc6535377797433423f2385c4195f593bff199799711a8e9fbe8c86dbd684585564ede0a95a5a68ab82adb3b07828332324fa4ced9af05b4e3b8be1ae3c41861932b3ba7081968f7e694136cb617cb52171669cb566a3fef04ba7321c907d193f71fb947676e2f86356cae1a30604c7cbfa83e3a5cb70fc3da2317aa80121bdac3fa4977ee2f865f84b1a90ed7fd8cb0fff1f2e7ae927a794b7d48fc6939ce527aee32f7f724b03c25f5e4a8771aafd901ebd862624bae43f7109784b03a28097f547012f65f671c3914c65d2df44bef44c06c675dad2a2536c61d12918876e55b1b4e8d4c270a60aa5881c8c929951e3e5a177a697bebb5d1e7a97bda429b9114343434ede88a1a1a1245e3adc9c22eff50052a34228acc4d66d5107a168439e478f9a9077850955fe05f1c34fa3093f0dd77934b6fda2831ad7d57cc74eb2ef20d79d306e89f5dd167535b3c8cbf1616fd8a377537ab7595bf4d031d0a9e7d029c0019bcef1a16fd8a13300884eedc395154e36cb73cd2f9667ef4edfd5c4daedeab9e621f4d54ab0386cc4e2b05f43bf39845a45d338b850cabc113d66873046e6da21c73fd6373b7d95bb28468d87dd88b72074cbb284acdaa9f621fb6ab2218c82794008a1601e906114cc833d4a583b235a56e62014eec7825c47e892638f5c43c8bdba5c363912ada46bd3c1280f9d1a11d439aae9eda453ecb3ae1970683e81441d84f2d05f51cfbe817e3263f3f2afcb9cdb763ab9a3503dbeebe16483eb633507ec77409052ce34cd6dd8f0d6786c38e5d168e440f647aefac775fe6f725d919f3ee25e89db7cb5a9798953fde480e26f9ad69dda27f7567b738e46998f741a8b156568f9ec01e023c7f2578280e5e9e77008e5b330e33905db734a4b19bfdac1ef90643ff21a1acfdb369fd3e43abd6d9bebb489eb84bee4abf16c595672fab83972de9e8fde7ccf473ea7bf973d8e9be03a95f559b63fbdc43d1f71996b3cd4378de7b9770e579910baa9539a433f75ca8643afe95474cb3b1f2eef7a3c7c379877fedd7c48a376aaa7e193337989abe11bb73fda64623ceb54c7b053323e7d762ac6a7d31197f1361db0df7366f319df4ecd6cd347eedd5dbbad766c83ebf86d380d6d08fd518551684076b88de9dabe36f7a743289dd27cfaa6f14c92c6a339a527c7e1dc37eee43ab8cd5bcbcbe91ebc7bca9cf290e6064ac64bd36160606056074ced540fc3f04b238f8929b9ce2bc5f8084647958151f00f0028e10c91c1a95d7f8c43283f7218198f992399ea133372ca591030238f29c5f8a8d2c8f8e6a7f7e44d79393a3972c0c1f9c867ed828c7cfac8b709e3305ca9cad64128bff90eaee3dfe19b0604003e4900388d4a0e806d54f293cf6c3ed2808cdca7e4ae8dfc41d91c9646ee331b007270be384a35870c4b8c8c0cb71fc37525f8e974b963b8e97469cc560332aa1dfc91c7f8e4baed5f0719512a510153aa9dea4b3ee2e6c9378750f00f23df3a28c577a7124cf581da9098daf5c3e8f0138c9f9c6e5eaa1d84f22707ea9f9e7dc961c9372f954e5bf5990ee3253f790fde35195fd2f26e60c9b75338a5d30edecd6efb92df7877fa92ef967cb520303e4bdcc927fc186e3a6b43e24f978139d51184028773d7c1e9f01d3b2051a3362d2fe73da9f11eb43cec2aef06ee70c379e882f29bda9d76e8dd75fa3d79390f4a5779393a39bce6898da3ba4af51b5d45872a0f517ee3060f373778a83e2347f9bcb92164e4a81b372acd0dd7e19b3f282fa7bbbc1b98f3a6bc1be83b780e289cdbd4ee84c36bedb61c5c873a7df39952951c3ab88d8e5aaab201c5cfa1836f3ee3530392c37d74a86e6943b2cfe1dde5e5c0bed121a583efd0e5a13bb78dd2a14eb77953dc618ee975c69d7b53dc71d46ef3da01c59fbe790e6fca439f991945f937ba12fcc869cc2207a398f16dcea29fb37640103e8e11fc1c7cd6ae8b7ef3f838aacfc867e2e7e0dbe6ecbba1f1eef4d3b7cd6928ef06a66a7c3adcd4c9a7cf4dc1f8f46c53259fbe5a10339e43071429f7eb5177821f3d2d48847f2308ddcee94e32def51831de7597d7ba26e335df5af32eea548c6b0ea3e01ff83527ead4bee636b8fded548d6b0e3b7572cd67a7605cf3ac5325d7ea6e104a0dc766340afa89e3331a051d86eba146412f71eda451d01b0ac9a1efacdd7c92d3349487be41f9cd4f5ca7fa93b3c67372fa4a4e4b95a601fb2507b25faa25246cfe2557fd88ebb8f39f5ee2bae99bd3ce46dc8cc38c38d5c37040f1672a8c8279687ee26c38e56cd3cc7839ace5dd40877392de4d77a233324e3993e1f8b568d112f364e474c4c578d464ac98ae84ef581143ad6c6a3fa6effa98751767ba8cb3c62333e2f8791b396b198d5ccbc8b58ca66b79e897f6a3e49bf336f28ddbc5197989dbc59131aed355b6189f3eda6262fc693c31a3da21c9bee4d34d26eb614c2618ae13faad06b76fe2b65135dcc44d5f6dabc1753cf2924f6ee4258e9f308fe91bd7f16fde1acfa8ae977cdb37e2473e399d57da6aa7facd27c74d346afa88eb543fe2d9ea8ff5a5d2468429cfdac8296b3cd36970b4c465be71cf35ae63331efa8803b2ff5ce3b4e71ce466a31e57998ed56e6ebfed4d77fa597b7362c52a6106446fd5eef41d5779c81fb985517e73a87a60016ce19a19d76d0f99d68e2965cab5cbbc81faa1cc6aa7fa8c42a7751ff7cb48b1e785794629a5d788d22cbb5cd332d779d99551ea3a3dca1cc34a23d7bcdb7c74350d7af9c8698c32bf4815fa8834ea12cd1cf36ea33ed2327a59de8e5dd3aaddcb216051b40b5f47b94ea5c4220b1653b080f202f8bd72c5185cb7bda675744dbad6aef3a4e6fb1d2d973fefb5a8fd50bde5943ed7b86e7b6d5feda4bfb7b5d3bc5de7691c500781cfae692ee593fe88b4faf39e3bcdd7578b9a767a91d60ea8b5cca3773d724dfac8da31ed4772e99b9c2e4715e872525681ded31a1dbb669da64c6b2b4a165a3c0fbf57b2c862e8d989df2b5878794a9a9dea9ed3b4ef5bbe4ea78fe0b31cd50e4a43f112cada911c5e4e22c19750e71135a9febcef9839ab834f637ac74d7c33132e5d7312d76dff3ac888d1a8935f1ad54ef52312d7c12749dfb6ba8d22b9ce23711d137d271da89fe4abf18ceacffb91eb7489eb84de87e49aaf3644f36e7e57f2cdbb1a5e027a560e4c1c492f394c89e49246ed76cb4f2fd128992a50ff88e49a77352a503fc9475e721adc36aa54bbad02f593461273e89acc38c80dc91eabdd35aba906792e9909e6616a14c72ac4aa525b2ab8228c2baa784a82ed40fdcc5c10aa3ceaf74a1088ae90e275fc5e1142192f84317e00bf578440e5e952f81dd70e2ed76daf0a12da76ab43c1b35bf5d775022b2222222222a25744a5a350a142850a959ed254ba4aaf62bc78f1e2c50bcc6ab55aad4c4444444444356850a142850a152adf5ee2a1002f587116d652c444abd56ab52222222222227a455e5e91193dd44f9aa8a194b6d56ab55a91888888888846afe815bd228d47ca054c34a1709429548aac56abd56a45b42222222222622c9c85b51475d1bcd46cb55aad56948888888888e8dbb101e4b4a0c876593378087af1b262a2d56ab562281c85a73015ae825dabd56ab55a7dfb4cf940d463fab0ad4e58a097222894fbe9b46db3cbe6e5b466f8103f99dbc927f462412f127a89d00b845e1ef4e243c66ad80cf0c4b7c728af4787f2cbbb8dfaf0edfb76d59ba57d63949713b3bc9bf6ac76b39b58edb659bb93ac9dc7da39ccd2a1be8b51bedd8a599c308013736edbe9e4bedd4e6c3e58f0ebf487acb3746a9d5ea3e80b9f1ca2ec00291e84102ee380eecbb142ba25350b3cb1e2c787f7017a1f1df5aa1dcb25b7d5e29c58ae84153f3e3cd0e33a5e0e40a77ad54bd7796f779a935c844e5f26390ac07882023c670ab07591c0e2786f9eb71684e32856f8de831fa487a003cd97fe8470a2154dfaa1cb08eb560853202da26fa7368d6a29df637e8ff9b94ea3fed56e6659c682ad3b7de7a0faa6bd9dbe1d9d9af55541bd7b543c2934ef1e1423ef5e13ff48de3d27ffbccfe89c57d466d40ec9a34227b703d5c436bdb3b16954bf22e9a4dacd51edb6ee94d5ce69ed50d2b1daf5f8ee157d9bf15d8f6f6fedc755df9415e8bc57852524fabe8c5256245013d240c0c205ff264d10e8cf617d35c97bed84ed5944bf3954239665f8b638eb9c8568598bf32cabc2f720bf0d42181f6429e37bef61ed48f0b12184d9c7ba62a0b0cab3cf2ecf6e3d28cf2e1bcbb34776c8539efdf576f7eeae536ef6e6ee750b08a7933b8ef8e07bdef04deb3df85e57f8de7bf0bdf7de7bad7a6c358162d7eeee5a32c2dddddddded67cef3eef96eb8bbbbbb17a3236e8b0b652cbcf7de7b4143f1de7b6f77bb394ebedddd8526530d1fb89f0fbffe5e8fe7d9a90784f6e4f71e36fae12f4f13047b0ef438cbe241f816ce4ed1d3e9bdf89e44dee80d217488c180f21520955f2ffa754bae7e3dc2f57705be42f790cae208a17befbdf7de7bfd64a08377681d6c608e582567f9c471e580dda035198d36f3ed749491b22d2b65af4666ca60b2984c263b6536b2919661d7b46484afe7d431276a147d53964f89fa58e7c5067a3ab9e388bdf57b1b094eeb3df8fa3d52bb4e338450e27477bfc70da13fa710c2f82a6c94966f4e05f9e2c1ae0f7e4f16a761b0b1ef73f91e8ce2a37c0d21e4d9a8187cac9dc5a61f156847b1dddddd597ebda3fcbae5e5d7e57a5c87febaa3e8eeeebd463441d2f09da551eb960fe8760bb6222db69d1eda994ed9aa4fe6cef49632bd5be86bb9110ff73d1fa07e589bbc975202cb6b7c0ac2d7efcb971f9e77bbbb4bf8f7cb971dcb75dac703ea9762f96a1650c08ef5d2002184924f2787f0d5ed21e97da03ff8660eefcdb45e8a17b51e21ec579b88e7f4e81472b351d6e84108897677d7fa57b761b0cd2050ca1bcf19c8a0412f5b8fd60445455dba7829a38c2e5dccd0a54b195eca20838c32ba98a18c2e4545455eba8c4146195ec6282ada3246511458acc4618ca78bb36d5a3a15b520941a102e5f74ea0b2d40b884617140b8687162b37caf70c9f2ab0101c2c5aa40b868e99c6d6868e8881de8338bdb5b40b8581a2cc3c6be5ade175cac0cf9c2c50a172b5cc2e0f205172c5cb270d1c2250c2e58b858e16285cbea71c1c2c58a12e8da74325efe4926c3065d3ae530fae6b1ea61cc24c75ab41fed356ac75fc3794bc78f445279e8720b6b2163832f8c2a9b04c2d3f06eb640fb51f33ffce58b16e739e586915ad9cc0c0dc755a43095774305cbbb799e69bc6ad4ea5f171b3c4593c251decd738a61dae99f5b913bbd75d11892140c054a06f3cf4d616cb19ab27cb131148d7abe694cf49c6a198f410ca48cc3b065dca594f156b2d2a8d2aa515de0b275a5d59afa8dc6f026a246d120fdf3d16ab492297a37cf498d22955632452fc794a56f5eac3245a62c44df99b290a0bc4c1430d0c8f80b5ed8b80b9b4cf6efe5090d6704115efeb94c14992cebc34447744447744447d444c4331c3f7937cf4b1acf3c693cc443313259e4e8abe069643e54226a141746abd3757100035a8628528464461341977f634c0ad06810786458569c07f0c10706e8ac8068d4f31aad01cfa98d8ccb307991ff9cb9ac8f79c6fa5819b7a2dbf0189f91a927afa105896ea36ea3624e5c89ca167ddfe4311a8fa9ee161d870684ca146f684162b5ea0dcd9d30c3f57837cf6f68a8463dbfa109793f4dd37443e3b1e84cc6636ca62c9426e3ede4341a8fc9a94c9e1844be55777162b4bab0596dc647d49f749a8bd607fce7313a65f2e7dc85c958412febc3863fea5b377ff3a5de6dbff952ef4ebff952effc37df4e51ef50bf75962ff9c2e0639589c2856d1b15e3711727c61486494ba39ec798aa298b29cb3f2e1be6351a8f8c73d1faa8f9e7f446c65eb888bbbc1c1a7fa62cdabe1c539677f35cf3196ebe9be7ab49d977f35ca79f9bb2bc1c2b6cf87393964ec5f873d3964e9dfcb9a9e8e5ccf8734a6338fe1ce209785196dbd082c49f369ebe2c863bbd4c4dcc62b8988721753f339cb53796e7a005996f726a65319ce9e9cc62b81a4faf2c86a3f114cb62b8d2539ac570dbd32c8be1484f47a3a7a32c86d39e92b2182e7bba65311c7d5aca6238ec698d6c86db9d18ee7a5a238be1a6f514268be1e4d3982c868b4f65b2180e3e3d6531dc7b6a238be1faa9006cd0cc8c62dc2a410c274d2e4db533810dae1765f98c16043e8c539a853d32b94e9fb8186ea68578712c9fd178609cc218aee667b89295be794e73aaa555a39e976a9a37c291c9697e616aa73271bb3856a5e14eefe6798cc633eb6a5cb4ed96038e2c728d833d8b5ccd536ed7eaac3765d99be7347c3767c62fdf4d452fad5ece4c1dadfae6f91badb6b0b1539b2a1303534d356a8d6e9aa6ba1a8f55e369704bf83b2eead8cb3fdfb80e5af9e7a4d1ea9f8f4aab6733fe79c675a62afc0be33b5396ceb4e59f77a62c3ced5013329dc3602dbc05766a56c8928a108e9265cac7f9abc54a132489a4d228192f329d638b945072bc28282384503e08a39411ca20306f3cb887c883bcb15033dba203bbb2e93a58d8dd6cfa09d563c351a241e38669b32c961b3a5b50dec387130d1c39941c07d402a972363b363fb116de7b39fcba5013956559966559267b504d592d4de0a1c626f6b0356adf7bef519a858dfd65d7fb89002fc22d3e1cc0871ea80da77934c0872d3e80c1340a14267a37fb787cf7de7b5a00d038a7d3b6ad73ed9e0bc1f13e1def705e9c1e220ff2c612830835c5d0815d1acffa6ac1c2dbe1bbf7dec332bec2c6ae03b5f96e7737db4e51a6e7d032cba7db4ceb64aa279fb2a13a087de0bb3852067dc3519a4f6854c9a1068d1ba61a35261a986c264626632dd104518c789ab1413343b3bb1996b8fabd9165b1cabb11a3c68d6ccaafd3c09143c97140d93c7bce8b630552e5e0bbbc7befbd0bcaa83e612b10bd9623b3ac6ed6cc26632d3aec705d3a50d77553936559966559267b286285ad98c083c6b35aae2bf690b19668822846847047c65a360a67590e30cb47210a55d8d8bb884544bde5d2c66bdef4eebafc72769f6bf2150b09ea45f8a8906510421e08ab16b1caaf675984724413445e2052f9f538e501008707007a98208a1179bcf77af00000ce8e1e78b841e9d841079b1c95731c39dce8818022d0a5f96e7797dfce7cb7bbbbd998f004b395f96e7757c6184bc258a6dd28f05bfd3abd3209ecceabf26ed675bac625250d8a95329acd2851362ddb48236dcbe49437e5341a65120a94df30340825db1efdeebdf74a19f6bb34ae1ad99b8f9ab207a3f53b3bd539f4f5e7ec3e300a5b672fe3c559972f53028945c69ce08bdebdf75e8ce25126e3296cec6fa177bbbb44bffe2084109ee89bb14173aa91b91193c37c3996370c0e93d7e068d4528ecd86a4c368074d4786a237180f570f73878523011079c01e2f002d8001cc8ca0efbb817fa20132d3dfbda3268bce52d8f8bbf9eb63bf9ff7bfdaf53caceb51d8d8e18b90ebe6cfd8689a195e5a93adb6b50bcaf24be59f73968dfd2179547e9d46e359e72b1bd7e6e7f03d7fb48666c6c6492606c654834669238db48c62d7b46484af796b46dbcd19e86e0a427fdeb56b8715366a5f0ac2d7efcb971ffa7d96f0ef972f4fdaf5b1deb5c31e36feae9c02beebe33e83be0d727cd9e3f6f4e3bd14797a39b2eed8f17c62286cdd8e7e3992ebf70486606cdd7ef14074aa5b2708a9e91bf6f8eae98b2d3bc074c2b6aae67671f6009d62d781dc0eae0701a86aca6a69c26af9e70bbb8815b66202b6fae7fc5e09b2482abfce58b66ec7b32f1249e51f5fd9ba1ddd3b3a159dbd0701625d121f3aac48de15308a1ea8378b4c026fcf66666e67d6fabd4a23e45f05e2ef15129b9a2a6718c306627c6db4b6b0f1db3cf44b5a81a655165cc8f76acda9a66f9ecfced99de7db15fefd735f1ffb36b05ad99509e34e33bf5c64918497bf2b270c7d67f2ab611537a8f064528128536d81062650a1085480318532ecc0245660b06208672883186188037679a10a23e80408b7cd24f3dbc5149ef81abf5d8851e5bb6daaa046082b800213be1872411a6ca07281136288610a2e5658d5c08a1224beb00216b2c00228e04003023514610b18511c71c508dd0c1966152cc67084143ef8c28a00a870c3aa094eb8400a54907ebba042141efe7641852e3cf068492c9c10b9b9cf061632d85658f0a01aa942851ab4c892250b260c40448597221da4210d5de4e90a34e8e882063bc811841b7c40842834e109465802055e72308537f4e00662642173c517322b63d8a2848d81cb12a0c0e00c2a6029c1942b7a80450d4bb0024584db668af95d5d91e5bb2d7a610952d8c268750517a402fcaeae68e273fc7641831da09e1dfefaefd278f6659fec275e6d0974049b694faa23d6f005289cb1031f4841c24e8c4a0b37f0000c25a461095a9cecac2f924dbe18fd14638cb1665d6c1d94dcbcf9ed028c2abe7fbb00a30bdfc16dca0fcfbafc218241806bc7bb380f7ac792dbe2675453fcdb2dfcabdd63a2fae7af36a051aa6740a3da49656ceddd5af9f61f58795ea7143e5f58bba0677f95e6c7faebf42e6a3d3a40f5bb3b62c713043bac3eebaffe04fd5bc2eabdf801160fe3c50fa8684ec1d6ce9eb90fcd804ef5af778ba54d2ffde428e93db030450ab69004129a10258b1de9f3e5ac169c68411bc0b80219bc10871d192bd7f8ce8732aad00533aa7cc10a2c78b1d36dffa27025084780810a3464b1dae9500f0413b080a10c48f480064bd8d92eff68fc730fc3d32c30f18625b4985206173bbbe22adbfcedd4fcdde6776abde042518f1bc857dab21242187288318527a88008608081053276e009b0d001d1123238e30d48d881d25f3549eeb449efb66d1f18a4e8b0511c5dcaf40dae1e6a0f2184109a22dcb6cda4fd5ad9e20ddf6dd00a743bb5bbd3edeae74eb7587e7df9072c895f87800f2f6757bb00243eccf7416850c1841720f1210c2fac28e3d78b2abc08e0d78b2a92f0fc849bf0a79db197b79c2f8e9dd62adb368a1fb7dff28a5c50a32c76d2295917d66e680d1b0d17d1f0187f4650a32c7ec24dc0465924356cd7c575f3bb3c3f79cb833a05df721e62279127d6dd9c23e2dfd6beb051f07140f09f5ba6465959a32c356cd3b74e45b7dc64f9c5ed73a053d02d2fc23ff05b2e02244434d9fad78b2ab478d89dde622f64a0ec598eb561fcfaf48101eb57a654f1a5df2b528af0ab015909610814baac19b7358a6735f9d6a9edbb10430d7f7d145c72decef31d9d03779eefcb4900166200052e9e700213aab0f37cbe9c1a5afca00bab2c4f9c74d979b543e2c33f3f358abbf98cc5b6df997278887d4da653dfb0f315dbfe007ead24a1cb977e5742a0e23b93cb7e5120fd7a810531164b8c524a19a325e59c96b4628c514a19db0021847008328f0161f3e48e31368c31b2c19a968561d6b42c5f9a302c6bce392deb9a13c3ae79599665cd39ad275c8b915bba3a6d1b9c1964025dee1e726b549cb3a65151d653a3e28eec8bffecf4b19bfd74ca36535c0184d0f2cec6041d4218638c114219a365c928218430c60877b03d5eeeee2b71a051d33b9b97979b4e359da21a8ff56c503b767772ebfb96c6635996b51f37d6bc9c96755dd39aa6973cb3ad531633f7906058d8164baf3e5c6982b415ddeab9356a3237e735274629766173ce795dd7ac3e2e95d0717bb7982dd4cbe6c91db96377dcfa265a735ad282dbc76e3833e9b4db3946338230e57938a30c2a645059bda5c1f7a6170e03e45dad83d0755ab54cc63333c3f7de839099773bc67ebdbcd567835178efa12c6bad406859504a191f7c29f8e229e47893afbb4b226814e61d84d9755d173fc67537f4e5eeeb8210be37bb279c4f729183710d978c464899352ac69a2dcbef437fddfc28e5c5735e979472fb685dd6b4648c3888da8fa2e77e4fca17633c423e292def4cb053d2db6ba494b223ecb8857cf2f96a5525b9850778efc9186394f2bdd70d3bf8af83968411f66ba8c5822c33e8abd50e764fae1d8ab7b8253cb98ef294b3276f39e4b7fcd278ac3a25907e105ab563292d0b42f85e9c33caf8e094e73c491d9cb553c552a3e1a8c4a917ca43490c82ce21433334a3494002a3154030381c140dc743822889b60314000f97ac504e1c0d63499005218aa29051061960080184901100a3da340168ec4a889ad9cc9a8d451a8501de3b508fbdc9eb69d2c8ebac3841d0394fe9fdde49f60ea3f47479c34ba15440f05e33f40ef432de55a3ea6a4202464a020111da83836fbb0c90712d433734ee0ec992c1f8cf235783416f96c896c8ffb59d64e45d3610e8c8b45b0d76778d828e78a6b62e8ca5bc9cb361e92c941b986a70f4b67b127d3e65acf8bfebb8aa15b32db77a8d956a5b068aea17269113e05363760b12a8d3ab0d96134f9337872e0f06d7016918c0c9115d80e6bd0cce70b6e3cb70825e86ba68585dd42cf5d492818428fd07f240888d2f00e33ae00f386e4a56949f697362ee65e370c69466d4b31da7393f29067d842d3699bd83edf7c61d452bf28a47584823de5ff2b657f2cc213c3d140b4d08c6635ec3a1fe9c521e2091fb69de78e9dd44f34478858ffa9e37063cbe3e7790a7cdbb1178660c453ac7023d40917bc659265d68bcbbd01014e48c7ea8db7d552c430ac12455fe23bf3db179129c56d283cfaf0422837877096e136057633f6ddfb2f646e76a46ece0c5ebbbd553216abb775f09f329515fdf05d5f26a305d5922947f814018a2a47a19aeeb6a37b3c99e667ca8e39079d71aec8e1a031a4df6efb12deb6fa1228da785b48ef675af016738ed13bca5dba884841f82c361489fd323feb57131c8dd2b37a3453d38f5a3a60919b904006d70eaaf79f3fa621077f4d54a463e498da4331ba02772a021075e45a7a4b2540a297fc87f6e5960577bccc247fa6aaacb7301e79c566f819edcc9a842f1bf6de4e8f2d80b31f4f34235468149d246d12d03b05711ec1c617a4274b4e13f371a3f190cd1aa84385dea414c6fe5b60e69a55ad3242adbabc2d2820360a9ac653fcc44c97e6bf97f77178eec9cc62f1c4be5e5684b64803ae4ab8650190fba05cef5dcf4a7bfe550d15fe8b2403921145cc3ee72ef02e46e8609edf522c32a25ca1d605d222ff9e90be14a551393accd34fd5afd1b0b1668207bc610d5e82291b3c9e2e0e3d3455ce3f1ffed18b957a48cecc2bd9e3830a16946f731766650e9cd7ad33772db2f9b3288fe10936f7497d1bdc6dc91a03fd31cd4f91f595935097953de7aa88200a0a227dd92c894b19bcdf0e19defb36188036686f31b67b32d0d6de21ab3d76402caf6b1e7bf5e49d7251bae8230cd55b496c183b8be174f331bffa0fd26d2dbea456a740679901e124b4004ce105c9efb0ba51de33952eb75924a603c31aad55237fbbb63fd0941361051fb7123f8e82d3d86509fb3ff7c74fdb71dd10bd3e3d0426b1a1808716fd8bcf8c1981676bf24d4350a124abdd7245c1ea61994c136d01248b69cb2de38aa31cad22adf0ec9e527ca16cac96ff7c80ab4c63575072b1458406a8d7e676127eea626505fa7ab6f93874ab3ee236464d9635bd2fd0ba2e033782e79e96b425dd920c165bbd9ab16149a6e46849a6a1ab3904e7238d143e8d41572379b53ee4faeaff522a34242f153d28e7225e43ce600f7cfa3037693f09397fc9846d2c6b09c8bac62b05dab439400a2c606409f26eb3bf18cf13e6065133a681942cf4802343290fcbd3b3d88f5ed06766678a41eeef32ccf8c04ce7357416f0785913d2b0c13eaef7e50fe199416c8b35abb78613b40efab806c05237446692c13e442abced68b7a568c33c50594177028cfab264b6bf64b7365055b857f58a8ec607d5e3bbb133a18c4f9e1668785743f86fcab61a3da96faa640e35ffda32d228f0b2f2b425115167cefcfefb8a374b38ca250754a081dde3b72cd8f6494dee119642189cada2b83ded507a511a75e0694b034a32f242cbdd0ddf74d0c38763fb36aff107abea540d777f34e71f9acacb0b82929d5633878227e0828c242b704f46de0aa4c64910b61724af0dd4c574bd7c0e4fc1c07af905fdd64060f8c8a9df2bbcd2c423765522f1531c889e637dfa099583ec619981e761eead7f038a83c883ef4b4be92b8aa2381154476e622e9eb4d7ba65fd1afae9508a6adb45d1a24cdc22204d8069a0ec353a4fba912f53acad6a15b520691b094b37ab8561ed3be0053a1692f2ae152778b4b290363ad39be2659bd9cce6e2ba6b639b272e1a52425dae6f960cc11383612f20bf7ddb95d4ba14b550d12b1a5ce24eae2973053497e5239349c3b87e40ba6833af860117f4e0fb110eaf4e54e13e7b9aad91a2e5e38c96bd5d65ccde4499a9fd47743d1f8f1e1402be2e14f4318163acd4e43f814d17bac916a72184ee1243e0577152d92f4b46cd880b385f36e4776f6eff13b3766a2210482f782839b016ca36a21470c3de539ea9e12c9c4f5521da65150a478c5e8b1db3d185bdec6fb2b1c5b7788504cff78f08283487a915241245fba8eb60f1d99df41ffc9845d7c1598a2a2b45be60cd1e5b1b95f8592a4b7403bb6c77ff4addf6227348d7a93301b4becf573eebc8f576dfe7311ab4e03257cdaac1978476dc1ffb2d1427ac028db742dc1b8e6c22ed595c5a5e5ea417fdefa4629b47fd331c7ab48eca9bfe3eb61399342a4fae99913cea6aacd87b61cd557b24c6a09a02a38833db4cd68f805e634c528b9eaa2f5020b2874b8c4885bd5347c08a20cd7cf428cd1c565862d21621c313defef8358bfd330b2b7d477206bede6a47df161166288bb277d527fcd80443d10119d998dc22cd3c355084a98e51eb0ae991210bc603fd0b1128d187e8620620545e02893370cea3fe33e2c210e9822716e9a242bc1dcf9b3c07f684762f6a340d3d7c7608a3460d9f3c51dbbc769f4b00fc46149c08204f35b927f2585de6108cd55eb0cac76e48c019ccc7555f5d8588c001e91a36d865b614036aed8dde3bf7d833bd00d232e7de0a43e2f5b99dbe834fcd1425f030663fc7e8efcff49c3d1bbf006a00c643986a63c6b78813216e6fca7983f5c648957e65f6b89bfa047fe3776f53f26a3d9cb41656f6a33156fbbf6314ed6571f4221d95100bca307abbb8b3e1e78c5ea62e2f238977264c1ffef5fb19d8aedcc033b9c860b6740a39c128c98a81ee713d8c0ce358e38770a96903e552df64e2a79565ff4f8c97e0b6d18f26e6122bfd6b6e4d94693eb4d5ce9efe4454e9859bbd1afa1263744f3e40187a15cd451ca69465179678695dbec5f797326f2fb76fffaddcb49fe56d4bd9cc7adf55a9ac045e75a705cc334b836ff0ececb51080f654bc2776b80c2b7f9af705e4e5e78a00dece5af2eaf8a3f8397422f70bc6c33a0b4561049d4748a5f499b0a0684d99f6779f357283f9fe36b1cd7bb3bd0a59e6190c47024dd24c4fdbc11043aef1acacdddd36ffe3887fa64352478b623c22fd54099d05d84df8d043040b3fa58c2240fc5a2684275b1c7b3df4147ab9c7ceb1464863354a36cd4cf00abeb3d74cf579202ce6aced00000091a57e6750e5bec9a3b73e1fddb128fa7bbb6949ec2328afd9ad6d417ca7127226a36c965e3c55f1b54f55658a034ee3d2b13612107bacbec1f8bba4b8d2de3aa49805d4a0a4e15922df500163af00d8ec1b5b17ce9a7d45a6ccd5a671ebd961fd4c40e41d41eb3f3ebc1df7455525877caf1cb12efb9d6aa0631af8e554a128e94c86749d3120285a537d6eb767c2c5f2daa04569faab88f8025566e512bdd9759aa0ceafd10ec83aa238e7a8eb85af578c4665cdc3e52594c547e90d01b25473005b88445ffec897fe5f43909192a5568d2be5e1642afbad0e861b782ee07da8abc2402818341b120c93e130783fc9811ba82f08fded539ed608cb7c11fa189fc7660f8896efaa308a792082a3e3c42dc9fc84a2b63cfa76117b4753b17db3461ee18c541069795d0a2401f1eaa2a174032bc7808218dbebbf93d88eed46babf95864c552061062be48f7ba3304b0a8b41254bddbf53131017628806abbbd9196b484b52d5344ea375a4d4abc6033bcf4966d205d2e0e37465531769c3a114578a9a088e30a852e82762bd3e26ce57c78be18db7948355d536a4838372f01a3be4249cee7ee3edd0dac4de45be724c6b0f8d59c9ad2d1f612ea3d5054d097c1e851d2df06bac5255c7ad3af937204a90d2e19c7c359fd92d366aa60018ec668e4ba1a21320f93bc6e4b6eff1a1c668493e09a0e084af9c0ad2c54b2e9677684c584b1da51846d066616c55b8e7fba5aa29591c73aadfd154ab643222a87adaaac50c0ebec6746c80d6ef93356e185e3130b21a62be3249b2de468443035c5f55962e3f31482768ea50cd0abeac4f21e817a33204064825ad7c041f9c8b7ff109d68a8fe008d0bf8255b6a2a3e69a8a9a8f9039027c55a0c8fcd8e1ba96d97fccee569d8865498b84e63e717bbd05691ca9f0aa7a27a82156d0ad86bbfd6fc53c92b0d2e7113048496b7dd3bc78e443974a370c3090ef8af35933903eac64858f1aa6ce91b6ab1f28a14f4d0197ca2525c8ce58a5f1a204bbc5426ebc8c0d1756c84d8d40fbb38b88ebd33a4317be8f599d3a9ba2cd90db7b9d5697b0eba95087dc3e3c9ad667c8f583dbf35dc389080c821e5d87d1dcd25089b81d6a2dc2821a86595edf438dabcdf4c43ad1ab329f35f8c11a73fadf5ac8d3240811a6941d0a0ac2911f70c5a4a15688788199d7d7cea26dac457bca3c71c8eece8ea6ab7d854af61fe29fcd36942e82403c305f93267e463dddda4876a6116cab405cf5d2ec4acd96ea4a0a64cf2e71714b19df70343177931e2015255bb18d1a12888380245934130218381484c30b7a572f4fa9593ca048f6bcd2d39422fd36497bc79433c26ccd73461a72533a1a23ef7f2c1795af31d882f244d17efd5a5e5340d6d1abc8b0f3a227d127d1c2cc0593b23d49df63cb0401af2067b5d796bcb6db5fbeb1348576031e5583fb777ebc25d3872cd71049d3d56d75cc3ffdf085e054fd66507e456587c9182f1e6ec09fdc4c26bf3f29213ae95f59057268d3a8b54d46edd36507b3fc57e7f4a9c8a97ab3c4290bb6118745f03782d0ea090dd303bdd8a6a9c89923f96fbafc19efa32f05f5d2670e134b2d78ab6bcb048666e598c6159517ff20f49ac121479513d24b07501c7680a193cbce26f45e342ed5c31a1b5c9087794e44f6bb666bf7d0896d88d3e7c5b2197228c250f6f00a2f03e5f9bba936a4524812c271e07f3f52064e55d5ff83cae5f9ff766a02745c728c5527349649c0029e185b1696d9fc3b47fc3a74ba4bc6206ccd503ffc8b6cef42ad187732387ff6226fe223c0be94584978ff03b7bdff853901f82358a3ee17837f789ead69063d02893c6e5683e12d4fa0b96cfec6ddf9223cdc184859be83efd8a1a81fe8bb4a38a8d43f09a86a3d2abff6f28f025b5c535ba36e29ae56f1a1d1c6148f09a88f7e5b476d8ee7305db34d23550f11ff9b97fb49ca69cd5c37781aa28c81bab192f300bd2e7a542760241596197d918b2dd345437487d45c38c748a57c11dc7a760bf5977172804a28d965e145c2b6613d1211aec947f8a05c908777aa38a3bcb95a83bfec6c91e275e98bd70ad4c99ecdd34f34caf2629fd5fc9f5ff8a915f87c8b3b8cf2371f75638144f7e495c27cf9617d54c5ea8b3adc0bf1ce43850b8bf1eea62e6bbfa461bd4e86c062e48043eb9f0d18a680e89caddba44a46d05fb628af669069db6ded934feae49cc426a6196e6d037795946911c75731d0b6d9725279cc61da09f37871ac21504035232767dcab4553db538f4148bb3b75c7efc01b0fd3d17db2a720600b6ab9d516c1b3dd3fa4c7ac9eeeb2d89a8ccbfec0fcf2f07e8c957c955ccf0e879f496ae74df73b2a698c2bb060db839adc44bba88975f214e5a4476474ae7468951f1eb6ed272e40454dcddad308752e05c531c906d4c1f2cad758ec02f31457ef07357f7d88f15727e003f8b8411b1291af8c986228b337862f2fb909680df2e7f839ffda770053095abef2b58da071959e92f13b0492940a827812d55b9af69afeb15dfdccd9c7c2f648e0b3303af6566d0fe1e261f94dc40e9540fdac8e7246d87082610539d764b47b0be824f0ee46b277f918c0e6c495d2015accffd1101f6edd22423c58b6e9f9e4ad6eb2ead3d88b2037f14c76d7c09adeb7bdfc403aa95d77edf2bae43efbaf1d5950e23fede90afe3fea18a44efb1a853487a595007891b3ce6757a7cdd0e2eaec00120544528fcba3b91ec36beee4716904e31f3ca2b4177187ad0f7513fbdb1711db0faf8b9bb09e7badea05e28bdd27e5f69dc126d7b6a3938bde8b485afb80c5f1255e1ad7a6f4ec9365d1771d262c3bbfd043d0890b2109c71fdee27b9bf1995393e921ef6f758a431c98efcb3471bdeb4d6e311b2e3255c666c7dfcadee5eb6b54c2853c7d43d06bfea1e315644e089cf8bc2f8119fd8e6f311defa38647d5a2d76addeca293dfe5ebb57dccaadb3953e2e4c9c997cb67975acccb4bafd6171cec007ee4df5677b050ef666d55d7c24ac6e7fd23f8b8122aedee9ebd712467aedc4bad979f511651d8569d63796d6815324a8f5f95c971f96d211f44eed3a496c3aab3662ab45908daf87ea7e589b8d9b30ea0083ecf34a080659d83e32123ef96ebaf4b7e1e3edac62e612fa4ffb628804757451962db9f6cd8ab2530a28303781ae8a49f4656e3849b13bd76580f49896aece13a80c57c393780af5289c938e3b4a3aa1351aa09df5b1d1f18e57ca1aa31db5114e3c3f1bdf0ee8062f5ae3ae22fcb1a33085a7072c65dc18c1c8cf97d18023a5a85fa629fcd8fe255c552c078f4416aad6308361435de489efdd7289dffcf0223ce20afa2c8542b48b8e9b9f3ce8060ba3dbe9a0197c4de463c786fd6f505c07dc03a9b85e92abcbf8bccdcc6b5bcf98b4818090827bd1011ad73e3e209d4f30cbf33ad4f72367b932ab6b7c113e7e03022373ac5a347f022d0df665091f51c1288c87070ef3eb9830851071972d43c25d890dc6869b324f128cb4e1638310d0b4bb788a0538b1df4385a59eb1bd467441f40c4a26c47e20fd1dc02cd9f580f806fbac87de09295e04fa37f21b1412835652a009fbfaf93b037ef0f7297539b801b844b1f14d21aced03626eadc019fc87137f218dc39bcd596e03486270cd3cf867425b9306a048339d518c1875b47a3b2d3f371922a6006de116973788fa9599d4daa95a24c8f078957527c1d1eb98ea8de20d10d8fe015ffdb8ce3225ed1731c2192e698f512396035c86557636e8de57995b30b54d799431d93b2c0ddaba6115fc1c82682e39219ae484d589f8075bb7573f5ab360214da4956d72591ad5ca4def783f00dcad90c441c79fc6f7710a5c1271e549d337eff8b360ccf38264c5eeb06e95d613b461f177827e84ece650b256b5a913b172a888937d8fa3faf82c0134e90a04f0156846c005bdd8d0460ab4bba3832abe246da6405b1371af364b1d940138114c9d4c57a1067baa39198e76d8f9ab16be1b7b81b42386343ec974e32b8aeb8d52fbb850aed06f0dfe4cbbcfb9e813a821ccabfda921507bdfdc952026001dc7f414d9f6e42d3c02ed87701e680a3ee02ec86b4990a9e85ba39925a5d5427763f359ae014dbcf32c5a0e30d97f08513ac01ca400a6642019b0626dfc0ae7a85a016dfb979e1c94f39b2f7277683f4bef32bfd62be4c52ecadb256956498da11d894509342af96d5522c9ffd38ea13a6c3266ccb027798d7a57671fd4adf371d197411fb87c6008ce58d4f4907e7e56612395a209cdf43653c36a735c575aeef870068a56052afc01f5daef1d48fc998a15ea61c95726fbceb31b1fd7c1fbeafa8fa5e549b302665c3b307d34d9fee7a468e7e3a43e29507b2bb76551ce0e8600d39847a555c9cbfdd153cd8c0677e082572da9d1d2479a291e56dad7d8311a6f2eb92838bc6cbc281f458c4a559f1364b058af993de2250cf6e5eee3902d1cf1f7ab315b1ec41f4713582a56d8f0b047668269d999911c03e0a231696dbf83a921c153224b9e17e0c493b31980a3d16d0054716a68691cf50eca21a56e3d5c6cc52428325ea86fbb65ff9b919ef20b080313a032e17b6495d09f70c645433d0eb9944b6f08c003880b5dc7eb1ca3c066d5adca65073ecda7c88ae3bfd4f37f9fbae44700ec7b1c2a342507f6122df7eba962a5c364480d0f021a757e37b88730b2411772a5401bc37746970c6e19dc32ba677065e8ced89db12b235746970c2e195c32ba667465e4cad09539e08c4615d8f390b32160f5a9c7e36a0f7871f9aa5161a2a59f2f33f382f67f4a73be1997c81b98917543ddbdea09eaf3f44a068a5f0bf71423309a4de2ef7b791af2c1337ae66516b3677eaaa6f826d0120d66ad9c8adbcf927a60dfb8ef9e45e4212472b11ed846952b351299db98bdebe2e88fb21c148a62f84a4e47ac53dbeb788a43aca9de27b58d27250914cc7697054c0c484d88018ef0d77748fab125f61f8c337c6c09b419afcc675ec5b108c18e00115c5ba813c50a617808dabad92493f4fc432bdb94322a9619d61e7a7e5fa944f386e74677e29bb7e274be0dbbf184c6594da53fff2423f766591c8086ae9de84cd58a60f2bd0862caff35f8bcdda09a665ec3e28d90ce12a60586d696a4f11deff232f7b2b1f5b0f018947dfa7d0869f3539333972b49f1c5e84ef0dfa21b876ec861032de4babebaab338b0b8e48d84b13cffe832cfe7e42893190595f77ccbc1c31e88f48c3eb32b8b12f65c7f454207ad5474db5ba31da9e6726e57873e1e0d55000eed439f2acb2eab18c342fd203e474c38272c9088b51c23b5ca9e5bdb9dd2975caaa2a1c7399a5a5be7f68502483e5153bea22687f2f1ec7889d59f51773a713aa64f1671a0fa26842ba866e181c5a532a673fd89e48fa809fe7bb296ae80a9605bb017920abb45ebd4f36065a22f78a33937ec4064be4de7d81442a7a030295b6d2574655e71c7f84421df78756f6619108fc1464e990c20f695e47d823a64fa2259128887a1048296f0ac51bb999a286bd63f463eb2a8006d013a9c8db5650029b2406789087b5f337b4c64d79e33ed736a9c4923631917fa4a1c9f4d2b1d43a0e8b021f51cffea20890ae2b684449311ab98f30f3cf5754178bfa180f49a66726d70a43e4b703f99a35e1435f41438835f8f02f3dd5ca647f3dc2379686d969d4c49a57658b20125299f1fa683f6407f28618a7ff55cc06068db6cc1e30f46fad76333b1c36a583d8b8deadcc92cbc9e40c14c14bcb83bcc126a9458caaa713d2c9630c892a51ddacd588fca51f310cb396f53572961dbaf464ad50af679411c610e26fb19db9e47005e4ade83444b0cc6b151c35680039037c859cdcccacee3ebfd624884fc8946f7ffaab646f977681c35e269616109b732f88f1054164254a5b95749989b77cb941f89210bc12aa71224e9649adfd26024748c2828a4063ef7e4d91be5a8409384e157e6b7c12bf647d5698b8e5eb59b77e206fdc309ba44378f21fae8c895e270b84e9f9d63bd19b836c5daa8a7e40b9271e2755f2498447db8f42c484e07f336078ba6441b139f46811b6aae5880ef6dcf390824353a35b13c87594add8566365b639243a7724cb59113f4f0e56666d52dc799b3033ec4b8a6655d04bc911c6288292156b869700279b41ff4c68d60590243d044c3a1cfc81b00a68a62ff10ff9cdf4c3d176e5e312b1c48b60195693242ccd8328f296d1b05ba63d8144926818ecde0cc966712c9c2f6c340f89e10666f688cc39180681cc987a9109c9fbfbe712c55752df5167ce808d8a0c71c03c7cfc037e624b0297f7cfefa58416c78080dcda7886ded911e3916e76c166cc6706f55bc049bb41dc9342ebc88509473d8b200c864e4b0f239481802db1cd2bd6dbed1acdd009c15f2e339432f289582da7b4544dcc1b79d9742f24dd521dcd9fe12f81061c04978dec56c894d8991414db31d5b896c3e97ef82ef8fca99914b0c526a55048a806ee453b9ddc8171d7a1d64e36fd0f4866c32911e6e809d085aa16f5e14e3d9f01c17facaff039e377accc657e174bc139639e53fc2e21e15fca2aadcfdc0d866da4bbd12686064e10b012d94d3c3b13ae921284001349991087710dfd38b97106a85065782dc963586ab6cc65bda4f8b92463d00a2e75e03e2a036fd943188b05e903cedb8d6dbe8d2abd88233bb115ed0f6bd5611b19e7640e41242167894e61fcbc744fe8680b18e531f8ac0b0ddc42b5cd755ab792d84664aa8a6b744d79637ca9df22c3292c22197044c516efffa110715b1b9f7661fa825152e1750c4a742a1d6a218b0280ce4f3b93c033a5861c9ec8f4cfcf356d3a2003811caf39d22985ff4e3960057322d68055f14ebf6cc5d77866ad7f5f4d8cc68901d7403a4aee3c068e3f31192286825ea855676c715541f04f72832a4d4164af732c2533d38b5a9b36e69298222b7a08a6a67fcf26852ba4c84065e7dbc9c5aec8e7d07914efaeebba5bedaf45ac917766b23180ca479480cac828a52a10604e1a1690382d2966643ab9ca983622c3047cba378874838b953b060122b195876bd11b5a449f59d4e116d3c2b5f44a2d7349af1d4f10b8232a9e24f411a98415b62afc301d131b185ba32c6349e0e011a05bf219025963aaefbf955b5ff7e36edd20d546ca787deef19cad088f009f15ce78e4c4359db3d83b9834bc35439306e67d98a557dcc194fbff695399b34894ec0616017f37f3d761e5772a62cd21702472db697f62a5556a9d4bfeda8811253b5ce794841fa08b68c1b248fcb6534cee438602b3af8513a2584db0fac082c2251e35135c4312c16097d74e5ab94a7d5bda62ac4aaf6826cab2b158d2fc7d189e9f886366bd8c3d7e91cc9978dbc45c9e25f4dc2bff8f23a01c9db31d60d08e9651e6dcd2f8b15c940eea6f954f2bd6a5632b3c907679ce94d990da8eca5e4af594044dc4515f7978591ba2895b4c4f9e975355b704a4a8acbab465016a56ab2f8e873717ee4aabd23a93f45aad04193aa6e096801f28db70c5caa6d6ebffb1353e69cedb8e1f3068df2147db8f2fd4dcd3a7c52ab7e3c9898900a2344743b7db6ec24658362702615b14e5d385590705717878c1a84ddd4fbb65208c96189d3d003937637c8dc1391e36f92a777e0efb10035548ab58bced9b3ea9f63f012decd70641c44a3cb4cbb9bd85e91807b360c0162ab29f9acc42b2b7403da42b6b9f86642c74088073eb7ad59383fabdf41e0fc978e399e9441f3843c40c18a4eb13012b0b2942469a4e1c9d3a5003b78f833e8ca9474f609a3a1b74b09b214653328ba592ae98e3d6fb66e40d93b6382567c8b344d5a60cbd115adc339304259ae80153b10c5fa9c68c0e29ddcecf10b8919482089b6ac092578b12b840e108267ba92361df61b380adf77ea3f6b9fe0b81f8a4074c35ef8117ca9cd79900ccdf19f48116a4473aaa0b6772a8c9eac01dabeef58ce9f6855fe07cae71b303f4842768bb2ae64f70b204001a43db9c44f42cd98b252fac1ef5b5efbe31d79df4cfc35eae4ea6c61d24927f994a68162bbd792885b3a421032218d93a84e10e13ba0b04aa1fd03138d492b26156824066cc976552ad6e71c16ddbeba71b5bb02acd21240dc9cecbd0d32207d9391771b587450bcc8bd938cf9f61f91def803688be93ace800776add900cad66745b9a25dd7b2cb5d0c04cb87d793159a546bb5033492c117af9216eb86d446aac475673f1868b8868c28f2fd921a8c1c0efae9b780e10ada7270b449790cd02e40d1656accacd72322247a984046b40e7e3a33d51cf83b4cb2a2ce89bffc610671f7c02625ac63d9206de29214b02c933b82914572b72cc5f048df6db204f3adb0b5915b80d86f3ee09d9cabbbb7ce90291fe473abb3b6330465e75719abb0c968e792a9f8f26c001cced1c1b37a617200c1825d74c75c6aa229fe62e7a7754c0587fc63e349e4789f4ea95b70e224f01200f246192756a44b91b82f138b066f563610f5c41084455f3f84f62249b807e8f8e506eb7ab04ff961b91eda8fa870e0bb6fe2b3483cfce84134398afb137772ce34029885fb5160dda41781485219655f365eca199821fa85451e2fbe8f1acef77ed229541cc1a779851f1a8b33251a15f40970d880494aaf99e4c1a914dc80524e80c45bfb1416d2f2ebdb5d6f680fe3dca0f7be210cd53f11396508f23d0c4e64ef49560da32f0bb5bc2870055c3e66b77e3e4d37080f5c485bf2e9281ec2f94e0ab4500c9f61736b8ed91dfa4e8a29d40d2f0ba7fa5f410b4eb4ce7f5662764883659dd2f5fbe30eff909d290f2f6576625256f109d1e28950c610f0fbda0b2c144263565001f62331b46f9bd4a4401c2aef6cb0a349d08b432666c34afd982a4c956b92aaec53e9f59e2527852be6d9db231bc1a578bd9ad0314902279fcc028a0a357e29f6c1355a7f5d12cf43af7417cf5aa14ad9133df82de72cbcaeeb30ad8d3a71fbe914c9459f991879650dbb0c6c2fb569c1c5406719345f7281e9ef6a2d8475210320f568710c57b8b1b1422005ecd676eb138ccb03a50dcdf5044e74fc3adea891be11eff93fc8ab44dc2a03c8112239350af6a551cd6cb9fbaf55faeb28eb498fb7d7bac5e4c2dfabfd5687b3973c51bd73fe690bc40909e7658ef0fd68f2f8d1f6c4b15e03bf0a28349814f910e3f65f8a3d9e359fb2b5b033387f58220fc779523e98b58b7f267a6e003f681a7dfbb20f04507df1b606548bb67319fff0da8d751af0294ff929cf6482d5d72d1b75ca163e52ec979b1b61b52ffb4539b2451c55344bdc50ad48e6e77151ca0b26583108754c252d4e79f961d53672d952edcfc6aa9c24e3ce9de81aa2da9c5549b63c6f55f38870663783429a9f8b2268f15c4c3089755b31e84fd1fd8b241b1c0166b35b9d7f896630414a1cb2f777b0b986dba2f1a8343c98d05940072bf790871ca55baf6ada8b52079efd815a9f3e2eb03070a62d7ec489dd1bf25ff9f0d8b7f68821a4a339da61b827ebe34f4dcb2acc27056f408d87ae0672c094519c005f3527da736659321c21427670fd0afaca1b441de18382f1a85e620bf275592757cf79ad088ea94340137f7c48341506608ea4379397598e102da2b583cbae30165eabe3777787597a4795c8346d33f50c6c5632acbd48b037f39014a0bc196c5add522eb3bb593548734958810d500623e2f5ca21700e44d5a150cc77e5d28f581d7a03ffa0e128d33b2609ae29a223249d0b6e1eda2dad021682aab9318d174bfe278e1eaeba51c10d4048cf19d3305fb2c29f16afeb7e062ce154c848001cd37864d850669df9592c0f6a194126997d53734584ae4ad1ed89cdaf6395cb868187c4dd011fc055c0d35b83afb8b7d56387e4c42c04d4408b86eb45ba1af3292430d4f0e784e01d4a900b399109bf74bfade9cc293ea79cc0e97bb70fc8155cc3e054f2b0ed0c42e0e84580fbbb8f35300b18ad445db3a5decd1df9b8b1308a24ed1d05e4a185677d4e2c9e3c8b77d6df689ceeeb363d24a40f81627c230c2f998f79b5f097d1f175f0740f7b9ba068c0fa9513de6def80785190cfcc9f7c2a3e1773cd85ecee57eca61a47a0b449651154674026e8cd1fac170ff7103061211e566ce6bcd4e9f82627d9538b955f3470fc62bf7e523453b919737b59856a8e1b78800aacd13dabc3c09ffad2e89c9fc0e9be3a9599ddc7ff41000bd44926b90ae9f3d6e8372d2373a5e3eebc323d7f87b9f19f36866259435f24a38adeabd9174cd747cc28dbd426a43ed6dee3177e04f0f3ee65ce35829b60b742c9d370cfec6ebfc6b3a86353b9b9e578d7b232be6b66a03845144bcf9e4406f0b3f89b7fe1402068770af9e7870a3cd63334b10aa108f3d2f05b8484cb986973d440225b96072336f63e5de39b9e65bdbdad02f0a7404f3d7023b806e605368b72aef6899c2c9e093425a9fc3f5d40bbfa6cf258344dff7fd755931b4e04e79a5fe3fcfd0fd39c98b202238764de15587062bf4f62c7f07d2c0c6ade623bc5581cd4098146f4a960da906f7657635f12b6178f12d030439edfb038736493008aa0d18707ada1e963d5ca34290a848157152df19ac66c03248af0a113f444a7d79ed324430b28a1586fa344e78c30d39779ebdccbc95d93db48d5176ab811e726db68b0425dcf293cea92cc8825663a530b00e5c509c5b6465322758e57a920ed0e1d07bdd179e21584b29c1164de236d32e761a977f60ec6fb017f5db33a56040b2b203a9974536dc1a60444cf700be96a47b401405c0c144091bf474acc93881f8277de8472279dab316906151d2009f3696cd4bab2e40c03e32132f7923ab28f3329ee769364fdab942547fa3d7461f2a80d294d4180e4a8c589bd4e72f59a5bdf00a3c8cb740c699bfd8c1690acfa997bcccb2f9c397e84c1d6b98642c903357c4221e16b5c9dc01b46cbb73770dd58d928b3ac9f81d4cc2cbc5f1d24f90b7e18aca737d590e42396c593b3129ac0b444db19effd5d887d490b59766a65d8557b352bb627af69fb0395e3582813f690037d74ead0c88f25ef94f0b528235adc273bb2f066ad0fb2f3cfe1243318457f83e3816862bddb5e3bb85c09e9b5a5305be109adb393617b594d9142aef9ea4ae6a620195223726cd67521a22975ae3899eb0b7106c712dc25de92740e55c8375798773fc9e953b2f7d446483c0ff40205530d8b1877526c0b0cb00f36d14cceba659c409833c3b6f21cd8b280a835fe59f95395cef52781a5f768290e0afaf7d1ba43fcdb6398709481ecef815c182ef7e819440704f2911eae3440f0c0c6090f9dd063b5234630642c5bf53f3d6ee79a53f194773b8583a013704c2ca04d4f6c449c8f7239592c542a312c34fc3d0ca976880904ac20afa36b12127242503acbdda1108ad312ecc566cc1b48ae84203c47c7e882f6dd309ed3cdb00e149ea9ca42c3234498eaea00ca0269803079061a63217570e73549e6637bce87bb30ab60f652645039e78d06235c5e746422c8cf7baf675816a5721534d5d687e4fd7aa19c117b79912bcb3bb4a094c333d33ead14f9dfd5e93ce38a83273ae2bf3441e5daf5e8a8a9c98f97b44b9ffb8fffa7c012cd09acca041d30b79286f5fd93385533cf1f1911d2309a3549cd88051653c2ff79f45b1e8c256264c9c664a02618292698aaae07d7828b9014e00b619873e469da0b3dd41302a75a7a6e20eb634e209be514b084dacd0a99830114341a8ef7ad5e19277d7ab5990a42dbcd2f47b74d46adcb6eb8f4bb08a51ed665d60cdf29426f366b395e0caaac580badef0102acc145c75478e8c2cba0d93a44172b7c5ae8ef71082af884148f633cd5259c2e0b1d3d71169e30bd0ebb72d24c7a33e7fd81556f48942f60be6975acee4e9a1fdfc68013f412c054a83e94197d166dbfa34bf56722d13152a8f4ad778ae58257128b8b7c5e8c2c10c176315635103f30de11ea979d7b2fa4b6bbd19cfaa2adf62f96fd91f4437473001c2e2257177a5688304234ab2590e2e89dffb0ff21af7cc65044c3b860dff043a969b45d8cafcc938713d8c5eae10d42bdb6f142e70ad03f646dc02579a7a9122cbde678bc9aba69ad86591049fa2c0f9aba4ad0ed4b20e871f632aa3e7f62e07e0c84a269198ec4daf132207d862447076b6b422455cd76b8b9d5a73c68f90bcab40b47401a08cc3800e83476106e91a326eb98b17f7934255fe072f282f630325fcf20a103106fd6376143b1e34c90806386f0da26a44559c4a6f3df3d95ddc56d00f193a94909f2de8b4542b5bee7bff0a317e52c8ed3345d2cb4ce516b155f6b4f513f3dc279fce0d8f5c98a1a89949a2e3dacd7b08d4f5ee60e30314d7b1728c1ba9f40285bc05718adb75c4670263e288537739cf3a59461060221978389adad76507c1b29e1860a0dec891a73e52a6ccc3cd98a57472ff320f6015d84c9a5ec00c702f0ac9f1f880e4a21d99a4908b6b9ef1b2c0335868cde3d0a21345dee76ac48e1e9bc646184251bb4be179a9803b3de06262ef522d98322931e7095ddb755c3ee581d80d660dc4b4a5443ec0c088ca3e5c46ef3b8231f4161028ec0e9e03ba8a833e6614b4189396c55b122dbf730ff6e79c6e9230808234a278f3a75225a099b0a14ad10a123b47da677fb6a08cce06e71efdaf92314e4e31c180cde1668bfd6bea1e93e312672b4c22bf80a5dc19e70ecbf40602c82a9c1c904a6708ffc661f652501a2d9dab15de0e214cbe593d55a8d008ba7910140bdb4e2907ada27a335ad2b2b8271dffa632abba853d5a84ae9f30221cde7b16f3a9e1211856b31206d5c4c0d054cbf9905430feb3b7341ef402ea1829c52f9c1cd0c03df0fd67f60b5919804152c87edc8e4f9290e43483fddc3c8846d98e4238e191be692b3f753ec721a8ce672a72eb75d6be1e091d848f2d241e1125d184cfa89e8d4823ee64b89930e73d023dbd7724bd9bd0a3470a02cb14e551fe736ed58668186ce0e68e70b49f738d118ab8f91a714f41d0fb2c90192fc40c3f0db2f3c25b948db1fa780dcbe10b977560a5a9017d876f4ee65ade4e191e5ea37412080a1d985b6f65f61c64af437921c40b42c13ba4c4707e83680765fa12112d2d2b31aff238548499b69873e6653e893658d7de0159cb82ccf81e2aed6f1ff24b0987f5feef50a8847bb18143aad87acc0a877a8f9e0ed56e1402ddb162c1c248a61e9c42aac4ecb8321a22ed1ef455145cc15799a38d852f357ba5273529a770266d554280d32afa6ec72a29da3e9d33c8dfe887f778d3bc86e48e710a0a9fc9f8d7e746a9666c2ae83e37114c79e406c3788c5fa52f748b20ede33922f83a2016db15a795d314821d5bb6780d931eee854aa4a5e8f1c251cbdf46d4e1de12a4446a4561cb68c8563a03f9ad9cd0348989a439aa27e4e0352d5523524208e53cc8ae94e29a8b5e17d5528ca9e23d120efd394840f93c6a133debd9120ba08970f2cba221a6c16ef7c693b207da1712085cde46b5d039197dc8a42822648b233b0aab34337e1605f1aa24cc156afc402bcf97a3ffbf41d6e70b0d4f39b15eba5ab031e6107c325f7257c76aae148ce9e87fca2ffb96f8362222368e145e86740588894b01916155081dc4458e5dc4f5766448354ef222a4d431a47a5a60469e11d38c6d02eeed0f2d4ded83cdf1d7f36d3a541d2f752281641f4662a4cc132ec3cab1616d8ac903a776611c0317d0b0f4d6cc6889d3f7958724e4cc168988dac5f52c25f5594e9a5e0bbcab0524a0a3c8cc6dbf75f2457653d5e8a2ed471f1f41a8e60f0363dc51af7433b8e76c848074269af094343d760b5112d8851da25324358536417959244a608ffd650bf87442ad1b10524c4772ee4e00710831082184b202d92eb24c87b8040a9b87a49721508a60b2ada1e9144b843af0b37ee8a19daedbb9f0899b55c92fe06886152050c00d3537527d5736fd891c67225bc8921d965d21405391518418449149c4d24e8db27d7bd826bade0492cd4ab35b32811ff88f8923dabf2a52df3d287a05eaa10535383177848f7a47bedc9787a60834b749cbdd0a2217c1c4dee659d957582f1062cf181d8763e15aaaa3f8c778f72d90ad29f0f3578e7e6e31858bedfc4957406bd83323bfbc5567164143e38f35f080591c468670912a0626ea14086118f564ea15ebe3ea9be8d03c5ae1ffcc5867aa8c1cc3623144f13261d7329ca7ebc25c2117e47d75bb9e8292ee740260c22cd17c489b9e5100da281d07e082c4bfcd7584b02556d21cc3a03046e14e609561727108a031cc095f78cf6abe00cfd326b86964133a722978ea998ad259061348da01a40c98e038782b2d470c1c0e39c9559e0c11c5dba8382df3331e7270c53073d65dc2144a019cc53be5be7898dd35f837b1204e01c589eaa72059cc5169169b7e23b2810032f671575b4ade86a951995f5ec5ee93d6650d4030ec53d0ef27b11036fa25b09a8f283beac692880123b981b1838113915c78f414f64593ca3a1200d2639c03b4058a0678195a7834c0614d4803796d899f9c60041d26b65c410597b53552d7f1edc4593042e9922039c5e528dcfe091a2d48e60370ac460f593cb868a01717a516536ef7ec2f29dc317c3e7a5ca504e596ed2644102e28cb45465de62c0e74dd29159463a190133ca35847c0470488f87d156ac3405d9b3c90000c1786b911dfc819fc581d4a149cc45bc500990e2eee9d21f6ec1beab369d748cbe7c2962eabd43953a485fc264324a9b2be6815e8d65077508b77a2f0188c93735d3a6b8f517841e7a6b2306be992a9998de32e41c054580162f5c8ce5570704e3507eb6e90626ffe84daaac6e641d1f4548658aab106f4b96e8ab7605155a7c9d027dc1729a84e1c5d95727df9816e558427f39333fe0bc0eab5839290ca0a5dbcd645b5d47d70436a14d7eae56d31af23a18ffe2428ac66528e3422d1020ae17ff4fdbb7074eb4da736b1314d538125bc1e0d4767c45c61289aece721b449425fe9f462b82f94a6720e706adcbb9210e71552191dd57779a137798ba8bf66bcbab38f55814a65f8be0b3c29f6b139367d202148b08d878157686897ab9004b730f2578074b95061a2d78f8fb4261ebd21a7cafbfff3800d0e87c2390448a6ec05f90c56322474e52c95bf8552a023acf373f155c11b2451c48015c1c642114a82955009d78a7f4a7ce6c8e34870cc70d42db1b73e6db41edf456cef63a00bc6b19d9cc0c588041f7a1b76d067b6e42cbe081addb0d71fdc922a30db58788c8f43b3c451a3fbb32d26a152c1099ec7666096fdd54ae3cc80e3b60e24d1f3f40acf9706cb648b268a6862c2c3809eb8b19d7706b8247c7e363c6c6d170ab71320a210e15b4d2b8b7c05761b017ba1ec186f38a191b184825274b39c0c0a2b1fb95dcd67770816e78e5aaa96b42670f256da577f91fc5d07d77368ec645472e12458678e8d7f8566fb0cd5c030be1e2571bf1eed2e680db3be4408ab5ab8d3fba12dc60b7df6ccb5ba0451f0557e98fb56740366abface141580f2619d16e944170143ee610c20d2526dc01bd94c802470f7be63446be175275c9eed67ca3b79fefa5e676b15af15976efa09ea573fe8df0489fa1d569f96a4696b5d865429b60486c1669fa9e3364cbf9380df73d6713802f388cc46b23cf8005a1c7548fdc97f5bae91fa2185c489aad87dc2194611b01545f03de60ba02e69382b8c09c39f06e0bb9a63bd9ac81bc5de820b3625c850460331cbf979eb32df86226cd21c0eb40403e9c49660a780a8b175901615850bc1f746c7b5c4fe5e75c7bae96427f882f1287f9bf9cc01f804ff5a1f5046453026edcafe595aa97a8111996074cf237ae940b26ce997195a2dbf0bc6663d2ecb84008fbb82b01f158f2c731bb0a452307b610da86c971727f3786e5bded7b6a44b42a6a7736fbc5afc47bc428f306512ee328224628586025e6b05760c350f745b4a724f50fc36056524c56bdc9e2fb957aa4a54a803ae439def6bfead16814c0b6f3ed2b87cf05749b41b39267f944a7e15dd1bc82b9b89edb92b8ad275c5ce8a46f684b632b3239403c9f3b323aaa447d3284d0150f87b3f25e6bd90485031a4cec62377f3661433306e3075959aa8cdf3e6d20ddfda83a04a32068d01252005324831597f839e85cf34ae7678f6fa76138d2b3664414585dda61af22edf62b075872334d1770f398848188c196532ec7193ec17a7e8c8cf2578e15cf4ee8ec998d8da4534274d17f7bc2b7cb0eeb291ec0bd5b805c9ac115ef623a8b8825ad30d9ddb0685322830f1123fe0532690efc0dbd4c5f459774a5245d5504169f96ba4401de551019f6824aa4a3922eac79f6f3d6f06322995794e77231d1d15f87cc38dff262830248c014c989c1ebed3ec6f0b486021a4304f6711328f8ad87f7d2c6f73ec36380429ebc04b8b3f64c6996c8e7ee3cc89bff8dafb09724c85c1dff6f8f03020cfb15bc874171668b4eb78d46939326cba69c581ad5c4cbeb37543ead59e98608cce5a2e3bddbe2bcb137f2591865ccab9d9ad0d1beee222904c35ca38315a27725edeb0f5fc3887b0c99daa075c1e38016010e99b7ececcdb7b386f2554863a6d40ed372b073213a06b607995f11458f514823997e3c0c522ce1d049c6e273ff4a5d5443942c51ff9316ccb6e73ed05c7cf61be14cceb554935c47a5189dc0956410d1850a04320d2840b773e8247d9af2b4b75a287d05314aa51cd00a898810322a21c50442a6489960dc749c8903a01fff2d718e5371ce64d4ef5fb425daba158fa1479d3cfc094a3ee2facdb0240094c4b771a26fb52ab8714d0f7d61d578fedcc6eb45995c768c9ee32212903cb959eda555e475631e80ed694b933547f3faf2d9c903001f28724dde2c2dae20a880e63c264e09913dd5e0098488967bf16a04569d4b9760bd19f28cac923bf385e01e361c2b81c28b1a55975a5e9aac6e0dfd6b80e025d3ec0d40d78d75d26ff27fbb5985deebcb89984c2c7d413611bdd78a2d88f0ac4e5baacb0134517431b50cd186c1c8f6d662a37ab70c83c9c5c7547affd8cece421f14f5e4a90a222cd37a393948235ab1e4f813ba18dde72c20620d9c3260a33e3cfe3c158bd53cc58d0393da375688666346d8e02031abec1798108869914edb4dc6f70c24e4e6c8a7d3d9d419e49f106392923e1f21e6021b4091fc02183b76fbaf4b3d93e31d015f6b4fd4bd662ee0297f53808b370e72b314bf6ca21e818cbafd721be42d3e6a69ff1a6e2ad221183b5688067d5690576b7c2cc4806d16fc983807cf157788437e2d8402d220dfea2791c01d286d97c7a869a85fee0205ac5f3283bc3db777f47828a3e25a2dd6bda68a50f338883d8b722bde17364789b5fc59319830e41cf5007f5ebddec199c53f17dc5c94352786e58927996a9722df91d1f4170fca70bb527ed82638c047e40797e484af94257be17cfa3998bd3940edc64bae5e685acb478542bff987327f23d642c225e65c236a956ee6c7457e940964f624868b1fe127525878e80531a1a459d2ae0d0ab27aaae2e5788990dc0f64a2a13a176e75c818cf7c570ab37bc86ab4093246dfe19981fe0e47053252f5da8ea70bbdaaf9b5b842651bfc040ca5dcf6519a695a466945a40c8daae3dc139890f652d357a2ae6d54ae6dc09b24109b90825568f6843ac0ff698d4462313681c9c000448331c7737c63ccbd6dce9f03a908c989f9da612f46c24ba20f566e77ab8153a605a39ca79423a14d0b7fe87a55eb9f1fa7cee5666e18fdd2d38a79521cc6ced682d7f9964f786dce2c005c16bd2c5b78519cf2e6ee2a229a0c0da372a39a69c4872eb3427bf52a0d2e703f8f389a94ae4a5f3904b6d5de3564198ae37df31ccd2d306695cde1b9f629a276d58368c111f145b37dd470aa5fbbbd6e636a8c4391fb47e43907b5f7b99442d305c45677f41cef248b175d1e9a737e9c05b29274ddc6e1f6fd4659b579be6035792306f1d6807da50e7491d07637d8969f534913314f7f276cc7c6e149e1807a16cd3363e96c0836ea77c8237d7dab148dc64a29349a9806a0ecd502108dcaec896509c1111d00204ee77efaf79db6195d8f4ba8cce2d4fa8e3b20da8b0b1b5fb43954aac8d8e5513603af3f4da6932bac75f0510eabae1589a9807d3955a17c83626f67733d886768e4f85575a5470c8d0b461ef9085f2eae088a093fba52fe619d745b4abd5e7836f70d337944c10c11d4966cdd5e930a69b01965cd96ca6515244c98d701114c0012e4348423e061422f14a30bde0c6045ea238ac78fd156a7f62afc253bcabe8171bfbb0e830a28b0cd21161b28428d1bf3cab76880f887c372e9aa2b7e9df21e70500b97188e349aedb696ae29ee7f5a592f75e50df5ffa53e076b3314ad2529ce720595067a8cef3b948e6cf75229bf11829ccb863ff70f593733547922e8c041b299e2fa3c6121ff15d592c20f0cb9457c94977cad067b6a8cd18b969ac15a342a4c2ee076c698670a357649a096012c94aeecc01378a9e636d28efd67b2aa5a315e4a2f69c9d313a9fd034b813f5ab337f88d378dbe539e45d0b193b0a3a7ce280115f918d354d87d230ad69e01c266176fa1078b91c6ac40d710f6f29c884e640746fd870aa6c5fdb4e12789478d4b3811d9563a00e135a7f739d6e2cb250d7e40ab417142107002f1534e262448854f184f0ee87845aa564405c0c12157a82c0fe81ea38e5b26e7c9671c4c3fc21515427a0048ab47e11a9b1164c890b04d4bdae6725f7c20960dfc929acea0c3795d3b8c18a722b46cc876396a03040cd845c493eb1b044b3be3b4e42a23d2f167b6c61d49837e653084a49bb37b0f847b3276806db49720ba0f91d1e2fe650fa645664d002c4acc09a6d814930eec22838027f0192cd2105076128046850e8b6fc22806e896b798137bf3da120c577abafce319eb6b1728a60332307efae730a189dbb069188e9660dbccb4b6620aee7c9282145dbe2ae4f84c5217d25b29f16c3334c11d5d351f86d268518ff9346fcf010e063cfdac9380724de906a172812ba8ebbd50c587f7e2f5998fd54ee803cb0cbc921cd8948c0eb692fa8bf87137af4e4e42527fa3b306e67d5fb984f4c54f07d5418a722d8caf90d8d9e71cfe3b7c4612c165d577ac0f737df710d2f1e085ebe6d685202fa4dad56304f317b771a2e81dcfa9760bc3e8496b23d4810580d25b42cbb70a356eddefd01503d985275adc783d9887f463da370f763e9cae1d3b946f86387530f5fbc9928e962c8cb8504c3c1294659cc69217e46e77e422068e52c3eb41b0f6a72fb913dfdc71cb58b12d1f090d302871b7b356ae6502bd5f0cb8ff2d2dac2e4ab037907d329a9eec79bfcfa89671f79880d6eae83330fadfb81b75df9ed3c75efe12d9c4f005bb9729684bb41eca8ca6e2b38228a1c7790f8d8b5ebd82c9bac06c16e4c5fdab8dc43ea20c2d8ddc8867d2b87d0a9d04f1ad7aaeb26099eb61536a94883ea465aab5e74af3ed223f7b2d649cf4ad8fa084099c1814f222c226affd2f674aec5005d962f882d866448c41cde9777133affbeae4720c342cd57655f04ae37149b49e6370efdab7dec7e793f63de3843ed46d92825fbd0586b02db1fb50631162cb67374ae5113b1fe56b24173d832decc8da7c435d8230d24b8c2f109408595b002a05ef866195ce22e1714256743f169d0c78a8541a8a49db5e46bdcd4635c73a8da26039d070879b6938b8658e57fa88bf32976c97e9207140b516c433edcaf49d12908d540e7e049ab18280c3f9dbf099d358dc13be135425a36f9905bd603f5e4e6b53dcfbd04c05e84b4cc145e2a874c0ae2ecd9e42e0a0535cfbce2096fbe43b3cfbaa14004f7729c4bdccdc45be62dd11101d1dec64f792c2cff00337c581ff4a2dca19be15ca6c087249f255615a22bceedb444add16cec4bde908d069019b63a4916765a83d9ae00ce2fa72c04f4f8903fab9c2da800a2960590e658ac0273073f481c599feb840d0e5feedd35c9b20aa4c63e98a0a459ba9da9388c4dabf10322eccc052c331d0ab7162a331034e6dbc9b9fc1c7ac1095a792128b3479100002810a6f69c9498173e253020f845c78ecea1a64ab8491c351afc9599f4e68cd4bd11dccbd511bcea160089feecf2dc69dd3025d0ab8fbdf15174557e990c5bc8d8d001352d1690c38f1da4299e642c928462fbb2e690da68476a6b498e2a5ac4d814ecd25a7a95ecf81665df56e62da354ec162e25d1f1cc1f65a1f0b72ad5010fa795f51539cfbb668e71d76b41cab485c5e6e551b663305c9074ca0e5dc18f97ca2e2288ce36f175cb505e885ae2883a0cde7991c321014a84ef372413f4ad9eb5fdb048e0cfe7f7256122718655a19a6c7ac49b9b9dd94c6a19cce7737aea937b3614d1d174c247e1a6719b41a55dc93a6bd2cce3e0dd63521c79f408ff451a6d60edfafc94cd499e7fc20b9c3e02fa8bc54bce34d0d949d8b104ed41f6d8aefc0a80c564bb16fdea53f5c00db470c3748b62c3a506d3c7ef6d26ba8c936dd02f1685b1220df8f8768ad890dfbe92b3062a887ee94090b412be611fec30c31db9093b9d2bae876014aefd30357388b30ba4bd629d2ca7d957985e4a7ccfcfb0981a73d30cfd653b64a7ee8dacf3a3c0a38333f0817516863620edb9c810630b1fda5c5003733070c906c68e0b805d952b204087cce77dda73f107b652b5c716b839b5618815dde1a923823d0a47db01035f90604078256760ab19632a80de1163ce815fec85c719692ba9cf17ac34eb3d9ba2937640264fdd0a3f281defaea31d4c9e4916bec2e10bcb288b4a8ea25ba369cb38c9cfaaac309345b5b26a6df38aaaac9cb1800a48281a50d9fb1489beb99ad9e272b7dbf732443ca53aeea9760cd9ad94776c40fc245276fc623b169f34755cf0c5faabed190423bc379050a85efa0e1b4f294712830c90ba2a9299ae48789938da5776612ad40824bb6fc27ed467837157e3e41a9695c32c3e5514d84246ae0dc92a34e9e1cbc1f1b653870f793f49c22767403ef660a6fc53c48fc9024e5a7f278232f070e2246b254f0429a2cc327dd2ad103cb87bc814333f6bfea4064ab058fb060bb7af30f7beb1c884c885496fdfbd1b463e745c4990c5da1fda74210a78af7b06ccc55a97191f80b8f1f2469a6e1a8f6e958d0b070dc26d58452eb503233e470c13cfd13a09787c64b1e244225ada8df986d32416964dee33c1308c723b0ce1df4c70095e258ceff830dfeebb957c62987e963b59e6a06f9f30f8206dd82007f7d60d11da6b2c1d07e89e04b8546d9957082028e5a15cb67ca6b036530a291e7e57804f2ca4189cb2ab96d7895e19d962cb3f1a8db7e9d6d2e4bc4d2515e51a230a88c2adac3954fb53287f45e0c80219738933466f95cbf9d75ae420dce6e0a3b6ec84ce26e5ff0e391f7649d044cbec9f48ba7111291e5c7ca1dcadec7e35aff03ea7562ff7fae86f75ac9c431eaabfe69172ca3973ec2bc7fa85c013af8b107f2004a3f92ecb8650a029a7617cf1037d890130dbf1d5a5f06975e98e84833191988d9b3325b766011e61c72493d5c4b03eeced593da9105299805115dc00dc55a950426037241c5181f73b11e43ad74f916a29a4dccd9dc8e2c3e60e92d33a63caa2d20cb85a32d223d7d83dc33f83380284d904005ce1290d510f2bb0b594787ebbebf58b246ec488cce4f5606c760ff5339f2c7bf2827973a995379ff47cf3871912fec4d20779b306e2d695fa8b90178842e807642c01e954c397ee7eaaeda73b6b87c605cffe8e08c7ee8dc04be38c759c16967cacbf880e4aa6d0fa13a9270d64169e2d54e12494aa2e0a4cb1119e0fd498775019742dd55ac9c0e559d0e9a0c5acd46536d5ad44142c5d19c191af92f37d22137bfebb4b34573a377d95de417f010e0f26e5d6a9a4ce049fefab7bb512f65da190f226ec02d74c444111a55bc1dd739a395955dbc51702ff7dd7a329ece1d5c832f5d3f988c9a129da3f3040e783eec51114b3e6c49d961acd38a103b55791ff96f278489538aefc281001bf0c907de724c48330ed9717d022f76a192fc59816a262453523ebf591fbc6e7fcfba56ddd9f58bae5337ffc62abfe572cd5fa8c7fbfb0a5fd1b4b6b7ce69f5f6dd1fe8ca55b3ef1ef57b6357f62a9adcff8f3ab6dfa1fb154d367fef9c5b6ee6f2ca5f129ff7e65cb4ecaca8f9a9fe8ff57b5367fb39436b6264939a7f5e9b958d3245f5ff3cf966503af263f099f0ba83bdfccf2763ffe77c918a166b5940d0e09fc42763f12b8d978a468f2e5583190f96fbedd7c582434d58567271b83fcf28b34670e1c62274b5e72215289ccc450801e541cfa60967a825a95449004a6977b1ed4fbf27de466a92ac3ac88e8c09f040532f0a64fb72cc5daf2fd13c1e6e0de45cf0952f4a5673b56313ccd617744c30663db3a2bcb95ed81fa2ddb47443b00213794202d5ceede7453204661b7faedbc0101a8df3250eb308854d5c77337aa0245cbbea2644c78f8528df4a0872cbc6f4f81dbf55d8645cdef457d320fea7e951f7d5e84498368ffc422078f3c429e863aed87ad1bf7d9d8adbbb70b88e19495bc177b1cbccb5b527ab39d19e30c854574c57187627407f43dcdff3927b077e680aca1ce574c7ec16f5bf806e5cc3aa6f525c62d0659c7bc526d7d38e93965d40da10aae5c4eef219807675400908d295854a01d97a602bb81175e192cb8f6cfaba22a030491f6494f8a9f2abeaf06fc24b813f725626e7df5c1c9d8abf131263be834106299c6e5d9f7ed0ae44d3a7580954ce3c2e8a5e5d297ca0169e9a934029aa4501c2c148a05b32d1c1054c4f2762aac528153800436b630f5d01bd6a87d5edc6529eb9be36c5af6d8f507a4435bed064cbe5fb2adb6d31b9c670f056311ca3ca7ac847630c1eccca559230535eb08c97d3fa5fad80409c37c201e6c1fa1052e0a0b872246bbe017a6140f4daa8969c2d140d93a26f0a01785d2304182a63946947f7b4c85382235bf16b3f1d5d05f4c2bce61a41a3c4e42114bbfd9efe3051640c725624c4cc4d64f8681dec21f23307527884ab3245a62b311766d2ec0cf3bc114dd9a58e72ef03ca4c7dfc8eb55e50166dd5052d1ce3d9e2e5d12546aeb4c895d36eff1078a94752796e1be419ea7fe96dbc21bf3cf92634a3213f58a83baae7746173bd2bfded5f9076c4a570a477e3648b7bde21df28ebdf9b92667304459f129662118533f31b93e693c7b4f7ef232217298c504f0aea8a96fdfec143ed145d6321accea14a57642607acf74262df3c884fd4a4a3a5756037c67aa35a144b15da3d5db0c1a66acd7de2d836954493af14fce434bed4d92bcc2af2e642d958844ea603190d834acda38c59b22281574a3e461775ef8906b699aceeb5fb64af06af17363ad173e32b3f2cb80c2de2b6baa57f5bd90f2849cc2e3ab460a80b113945f004fe0d752694bdaa0fc7b836c4e4fadae50f1cc5b265a98219c6e6033bf77770747f481be82fbdf2b1f9fdd179ee6ec4981834febd37194815ba2566698c85e6f68e21b5e0f4a7e90a4aa35dc536c4f68daca47b2735787b20512d2acaba7743073d03f2bc70975b4a28fb8dae79314da72c826d39371ed8c31301802288845b7e17097822a5a736ead2dafff6cdeca6699bb0d792df046aa4bbf51c668ea633414cd3168e8cae09392698cec8ff3949df748064adecf8055600871558e1816a8ad2e476aaad8e2eef07c2357efab95783cea7a539b3147b1dfecfb16e900699f09adad72b672da73b14fdfcb862dea1f849afab529b6ec57e34f745fd0e72711087069e3843a92bcf5f28bfeac3670fdfecbaa198c2927053a3225edf1fce162fc8da24d7b02c8174fbc664461bb597fd086a13285d2d2279be597832d4d4845eff626f6051ffeda29420bd3542df462aacb13524d78333933dfffd176f5fde43657b064b9106ce7963d2024f4747740267a1ab924a501c3919e450ef6ffb67100f870c33f1077569a8d0ccbf69ff63dd1051c0a991483b3f832fd49db40426efa818f6496e1c0d9bedce9946a531e0a81052a1d9269f930993438f20082b47816f6e12e9775b1724d080240ccdb548f5699c082937298349a6d3e94f78c3ecd51614eef340e26be8042fa530b4d9a13227103f0da4ee0e12cd31a215af4dd1391bb4d008c5b54b07e71aa22301c9b6c0bb3ffb5e49b65aaac892affa0b738ff45940bd1af3052b2d0e529f4b87b2c8dabe4c0623580d02e58eccaa8d668da5f0ad9e4df6fa061f73688cef6290a4e7a036c2404de0284d70c5520da554d07da4bd8211d8c06e3dae2c78f98a7b359b9aaa0140bcf8f7ce442e054f166411ca51d40a53e40dfe1accc060b18d626dc426c0675abe6dea1bc7417f557a938ac0d76c834b26d6d6fb9f7de52ca94a40c120d9d0ce60cb107ceaf3ea43de1244338cba8f1e1dc02fe708291fde5c329052ee24b25f65ac1416cf587d328fb4f2ce26b25c85bed5af94104fbc3b924fbcf2aa26b05498347b1c74dec41f3feb328f6d0f1be42d423f600c0fb77f45180f79fd115f3fe5a74bd4be6fdbfe8daf1fea8e8caf1feaae8c2f1feace86abdff4717f8fe33d175df71388483e5fafed3a01bf9f96afb603732c48df75942d351229b32658a4e12aa3d11d74709394527090ebf84a613c4efa879225a86f97b9138424ec9364f44cbac2e29f7ab20b54beb51f922ba34255462fed27e5670105f9a9395207f69b01524edd28e567e105d5a172b1cde80367008fe0c0e5f5503872aac12c32ab170852877942ffc24f002f1369701dfe342e07ddc08e0dc073ccd75c0ebb80df8ec7abe7187863c8e21af0d695980019ff44adeea1f26809a60d4c50d08feab54df9008dc1504eeea014cfc38a001404d3002020202020202e2a6600cb8a926f20607d93799056eec1fe2aabcd5dfff63bcd247f9257f49e0fb6f8fbf84f87e5baf4d96c0bd6274ebe401c1c9d95b3b3bdac15c1b9d595abbadf33a99ab80ffebc3d7dc047c8e8b806f5d1e1ec7ede1c17b80b7b90678fac3d3dc05f8e8f10930c04dfe61013e28200108e88187031860e593e1ac589a96d5216ec7861ac95b4dc46d25af931a6cd68d1b2fa64ec7aaee64a71a0014e076bca91ff35793c9715111ac8fe3aa2258bf755911ac0fde8f36f58b60bd126079416e7e79fbe5c83131deceeaed0987c9a47dad7c3a6d2a165002f3f7713aee4be062e085b8453c055613b842be0237fcecd228ddd0a8f855515abd5840488881222e603d20594ec150107373fee56fae4b02f702fe3677f5e0ddf1382e0ddfba377c8e5bc3d7dc9bdd1b77c5e3654e394526d3aa27e9c471df773a9d4ea793caad8186554c7ec12f4a3e306f19b9059062270f88edf96c186948a8771492a3901c85e428244721e5544ec9295f6cb086928d9b1c7fc3dba9792b3ff0a3b7d37abbeae8ad11c425e26d767196b9ad8488a897741168d3ad260deb1c385a35466dd45e787d74840387b713732750043bf2d6ea27da9ce0c311d35134d88fc36b245b20f9d4130f8897414606a7a0d4c85c1dfe6f90afb93c9fe302f9d6cde171dc9d07ef00dee6eae0dcfc377787a7b90478ed461e9d1deeea8700f76529c8d581e7a69eecdc540e37e5c54d21e9dc14141d3a6abe32dad4d7714375dc989cdd1816f6a736de8ea6eab20ba323bbacacddbc641c776888c7dde32d1c6f482e794b2e35d0cf4f0be1781d1444236b44bc8cdce10dc9246f75120d5ca7fd683faa5c331ab29baa21a6cb6e4b7e191e31aba5d5522c167b581b2f1f5700dfe3fe789bfbf2e0bd8fe3def0adbbe3735c1abee6aefe2f8f97b935fc046e115f818b81cf2ecbeac50a71c3bfc0953a14b8f268488657daf024520c29ca36e504718978ede2a7b7c6b3eeccc35c1b7f811b757230705749ab98b77e08200033de4e7df1a86175e405128c072443c1171bd4716d789aebfa9beb8f735fefe3e67c8f1b80b7b9f5c1bb7a1c77c7b7eefd1cf786afb92fffb78697b9347c7657f28d3b34c4e370872c30c46b2fd5023c92b20a66748414c316b82b246fe50c715746de82b9eecb52b451e278acacacacacd440c3cb0d3beeeaa6901a8482fd468d1aa08acb061816cd0410809c97cb061816cd6a641727cfccb4ba98fbe375dc9c003cce15c0cb65030c8bfe104000725e2e1b6058d9fd329df180d02c3f1c7f83b793e36ff07866ae791b7574415c223ebb279b1b10fc57a9be0f46fb0a00bcca3256c6a2f1c9ca08670b23a51bbb31ed869a6ff537bc9d97bfe1f1ccfa3647177305f03aeeeb69aeffcdcd799c1b8097c0bdc00b712df014b843d4ef715ffec6bd36577bf0def0386e0ddfba347c8ecbe36bee8e9fc0c5c057e016f1a6bc430b02e90ba027e393aed1ddc265030c8bde18d2cb08e0e7a75de16a0cab3104e0ae8abeb84839af27c3852f900df802c1e00bc4c21788e20b94e10bc4e3a69ad470534634dc705347ae5d2d0e05f148d92565e42dd8cb4d35c1401117b0c010373c2034fbe8e4e7e37d783bdafbf07866bee1651cee10114484ab2524887c997efdc8c26165f328a73e5c2da598449bfe7a5358c49b7eeda69af8b829a31e444ca0023715f3569253e0a696bcd5efc30342f30d9c822df175f9c61d22e271b84343bc8fbbea71575e34d88f7357b068d344bc46045e1979ab3f88fba3e90c815748de6a69f927475e20e17083573f27f86283d4dedcd54fb4e9a7b92f4b2b2c2cdccacacacaca0a0a8ad14dc19a3c39f20209003ab2bb65992b9510112d292a6a12004fc2b2abc55c1985003c89044976b32c73e591eeff2b9d088ab9120a4f0ad56c3314c4b740991c170f0df119c79006737f60c8086ff5f707d19f3dd993bd1d3c030000d9eee8e4577f87b7e39f5d9c6c6be862aebf8ecbe369ae0d7f73efe35cd7e37057efe3bebec7cd799bbbe3c14bc3d7dc1bfe6f7d99fbf241dca8935d9a6fdca1209edea11f4d2788d782b861c76b3dedba46ad4474dd2d22d87f63deea2b83d8ea0be4adfea406fbbb10c1aee1c39b1064693f3f396ec01729f75fa47685f7e7fee4dcd59206fb5f7755d4607f107745e4ad7ed75d21d9a2ff1a6ddf6fc37d89459b7e1ef70529daf4dfd89581fdfe0b64bfffbbd07dff2775dcd780c31bee61700872cfc2e1734f71a8d26e2cc337e67db85acafd34dc146cc74d35b9a92cfbd58b96551c3e804e7eff03f07662deaebaecce8c735745d1a61fe67640cb35cfba1dd0b2cd6b4a987a4cdf7f73573dd1a65fc77d498a36fddacfe9fb352730eefbb52e3c7fdfaf1db52b46f0bbb901c17f95eafb62a8e550fbc90d94553ed49072bfcd4d79910347eba69e803775149369f08b52b4e96f1fff1bd9df549301d4abe349dbd09f8307c4738653b032ecdc5883f7e5dfa476c17c7d6d08fc35be3eaa5d433faf33819799afffedb2f1f567dab5fa8ac32160ceee4d6ab0c9f0651fded88d35d8ab3bd3204fe73704a7fba50fc4dbc99ede2b06cc476f87f5567e353e7a3bf8ad7b3b6fe3d6800df6cce58153e30e4de0e51613c038e4c0dca12186b8a9262c9c822d7df5fd77c0c915e7f0e3164087aef176b4cfe1c6ae1837e9d6e02160ce56da673803e0cc3bde0e119635ec430f5bd45b93591f4e19419807e205a199f5f6864ebb295876534f1aec97c04d09715348deeae7f180b472c529a3317cb1c17a716409dc54936ef5eb9005f1514dad0cf3fe91f5d9bfc0047185f0567f0e0de6e00191ddde01f237d5a4c17e999b326ab09f2706a7604a7cf2c3940e36353e72f8c031303e5c522d3e502c3e7c640f7363660571a38e76635ee1b0487e79ffac76dece8bd3b831ab6e984acafd2e374c2de5fed40d5d7a727fcb0d5758e47ed40d53b1dccf72c31732e4fe951bae5290fb556e9842cafddf0d5fb690fb4f375c9520f79b6eb8aa22f77bb7bbe18b14c21730c29732c2550fb2759f1f37429d83ed10c160b244c6c1788858c847c8294dc068825741e4949ce130469bec334cc17802c69d2c03c38beeb2cfd2ab4f329fee29a3dc2958b79aa660b9a560af971aecef251b4a39d620c1a908045a0a3d616d9294dd493bee01c982d4dc3836022340e470043f93d62c0c5a2b1431621f18a348d362496244f9a1b41991631262e4d8607c4588720c9388714a3696e3dbae935ff7f57bde4e52c663e4f8328ccf9444528e7f9292e39f3c1e565efa54569280b2c2a2f2b1b0b0b0b09862b4099340caf1e3d5c65089a4d0510f2a111438a4dc2fa51f459bf6a241297969297735ca5ee43027420e69e418df8f6c76a58e8c3f5a87288759cf52d6c311f517db916d07836a1f8940ed7ede6acd5badc95e7224db11180803411808031d08f62ad5d19442658bf293fd49a1af2ae94ad454a90ac51ef5fb69947e6d3e95c8852a910b552217aa442e54895ca812b9d0d623444499f491ffc41ef3fddb8b7e69deeaffa0a8c4f83140c7691c61b9a5e62f2ac6911c2338a0948f42c7c80d74a4c106f258838d57f2635e01c932cb2007d2925af0b9c7d926e957461bfaeded646f69508d5f8372d7a01ad460d7a0a0966daa33da505a85eec8954ca2067b7a912bf1dae34e8fe490155fdac71f2459bb9b17ddda8e1aec0fb70d6883c51ed9f76f47fad541b11d55d8765461db51856d4715b61d6d47b91d7742db11ea6bb02b8c6803c215d61d551510e57edbdf37dfbd1d29a594524eac6ab0510df6ec5883fd1e4d6a57d0979b5229517ca814233e39d6234672ac4ee4feb01ec9d1a3c434c3516ed12e14a992d48250ee2782d938136e421f8d3146f718dde3ff6830471ac6c7329259394a7f711f3d88bb158a2af4e48b5bf4d3a41a147b6cdf4fc7e89716a55bfd50a08821c6186374942839f4a096d25272c59a52831d56a1dc55c3b1ba130df6cbeb4240b00735d86f85fe4d486bca026b6518bed84252e9de1bf6510b29c124508cc9307cf1334d33ad82741836ce84b507a6d443e4312058183edbb36adcca3ba9b0b45cd50b2be6c6ad61030788c3e6066747075343d7428da489baa8616da46474841473a29e3eeae9e9e9e969a45892d2126bf542434544444444e4d2d3d3d3d3d393fb53333c8e282d754fff489866f41da990021f7243c8ad6a253e49eb898836213232328a3d62bc7efc69147b04c51ef4e38e24e4287bb4985df0c30d8cdc1feec8fd2f2125da623e44b9896677916c9fb466dac6d9ce339d3e951516544bca4545e365c5828909f54c22d6ea8586ca254606c68184e8512cd5826259519179969003a97c27d3df9504aa4a0df33a5b2f7e9140194c89db325c8386fcf82ff23150ee6f0da8c68c2a6bb11c6e44b1dc9f6d441b914b5629fa628c36536ca42651ec1123b99e961fdba91a444921a45c6c1b0e8750a2ec9e217d2e45d63ea45c64b9e2d73e9b948812d91e272349f6f1b78cf361d26ddb36997138c656b8a96840c5bd9d1502ec73ff83c5d15bdcff103fbb31ab08e13b222e691f9372119d223a41e81f21a7f86cfff970ef572508df117129ebd80f3fe93f7f78bda12a67bf6193207b0d327a260dffe88cce39fd83744fd699d32459d32511c972614550448282955c6faecdf8598cd5e9cd590bfed762c0e681d6963dd75aeb95c0b5d95ace9ae85cc2c6192b539e4ab5c80f71c0d65f158d30277798b309807b89c3a805fb61b442f7618c42f63e8c4dc8fd6174921b6b1efe3aacb21883361b116d7a68fd54a22b7b01e4441ff15ba28ffaf543971c1f1bf1616442b3110daae0bb467440cbfe1b882ec73c1aec2db2cc0b32b3f656e3a8b753630f9690468ef443951c69fc1b44d8d9d67dfcabca2a2edaf89a9683d3ba3436d41054d7caf3439cccfdc7df382dab74bacded99baf838e0a5a1b2bda4900b91fdb390bf34ed536552480ad1e4302ad1e4500a5d23fc75436cf56391841e2ea21472c7980c723f8773649b6c8eedf2cd74eae6a98bb147ca7226fa9216d11af5858cd10fbf170e38c468a483b7a20b8806a3eb0111ecd7c15f4200d12e231e105d1b901f6a38e4ceb1071fae66fadcf64b683adc0be1af0fb811ed923f1b882ec984c4a1d643b1d5bf5d2945dcd1ff017fc99f7649a10699882ea98308f62f5d1f9f255e27ca1a42a924f72f81a34345ee97446d838c426cb514f184246b6985efc341647f99495890cf15897379faab636b7eafe49981ec5fae42fee2beb90e70b7a77752089c4fbd2033d7b7b3e35e4a115f43f1f5deea2115ad26fa4d02196de65bc9dd1f4d67c39a0fb5093e29639b249dd2250e39f0c091b1dad919d6dbb22ff6b0178706fbbbcb52a972b6cdf886ff5bb339bb28efe391b7cbe5aeb39532a75ddcf7f3c081bb1dcdb0e623e868439f7a3bdadb5a7fabee652bbbd92fefe3fbed32e82f0ff3903c220f31b49761db340aaa766d4d726bdbbf72d8b457a970408975c8a15dfe1507c73924f022b4bb0636ced4bbe588148fb275198f22d28c474884b29d2d84a40721b92495b2a52e95e492d01352a98d1a966d9d0d6b23a9d4b020a06c330a14d430204ff258b65af598270135e1b1593489b2ddb249348b3c36897e724fb65c57ef6afe96b56c5f548a3edae3fe93896610728d31301cc89b685863a124cb10924ff4c0456d42ce301232f7471ca34dff14daa345d93e69cdb6cd547fc6a3d490d3ee1a9c2f1f4d737777d768f5966eaa4e0690c6932c2801bd986f4fb35f3efe4bbc4e92fe98647e7ff6b21f5f15a2af3fba57802252277a9304b2a510bacf5f3eda2b719a3265ca149ded637c95bf387a69f28d6cbba3d7c60c773595f735286f8d8cb3ad1dbd37dbaca3f7b3d53a7a65b2dd3a7a63b2e53a7a61b2b55d47ef2a5bafa3f7255b53472f8d6c4f1dbdaa6cbf8e5e976c553a7a53b9256b570923361312dbadb135bfc50ba2e5fa96a5a317952daaa39725db968ede95acf265abeae83d654ba3a3d794ed4b47af97bb6c5916a6a397cb36a6a377cb5a5633eee8a5d9d6e8e89dd9ce74f47ab636ec8d0e470b34d1d7a921c897469b2982ecd3c16e79f7d3a6524eb629bdaaff277ca1d6f54b8562853c3210deea377df89d3e547d1fbeca832af7bba71bbdc5bde9467f15e1ad7e96aac1ee0622fbfe22fcf5eee150cb31e93f158763d21693b6ac611dbe3e41b58b95cd9cec39199567d7140b5776cff6e93e73e30d4c6f47a28ee091b93f08971b6f807a3b8dbf3a6bf5965533ad826cb95f4a1ccbb85d83120c6be5d7de0ef8e564f9d28c4ffe0fefcf3faf6dea1a8c5dabac543e0372b7c046d93ee9a9c2e0cb3c1e89a7a699e2512765b350eeeec9da265a6bb633356ddbb828727c0e89ed3a6faa509fc9343b8d0ae5489dc8f1834e9dfc82768eccfc7dde4ebf8ab723cbf8ecca0a0b458242b5b47c2e2d2e2e2e289615171795cfc5c5e5e462f25c5c5c2aa5316565b0dd6c9f34a7d698699dfc62839ab7c36a3082d937cf7a3b51766d9ad6769d67eabed3e9fb54703454444d16166f47fb3a333e161614aaa505f5a158aa2f95f276663783fa502cd5e7a232e3a36fe90db5eceda695e78a430fe4fa699f573cadea697cfe2fde8e6ab5f276be06bdb2585f76181809862f0463626464fe3dccc9f5a5195f0d4137e3bb1e902dc7c718cbc4c0b05618bfd050b9e0540b0a63cc825754f08731c6d98d29461bef1b33952b3e36ce84740675ab3f53fab4cff00c9a4ecc235449b45de7f1cc23352eea6ddc965f5dfa2f57fbe9c4cfa45c5630312f322a2bef0fc6174e7c81ef9f135f34efffe373882efcfe3ac4578ef7ef21be6a66d074a204f3c83c329d5855d4bfd0687915d613d132fd15d513d1b2f6d676120c9f2b2d792be6860d0e1c3638fede21f0f1b5adeb93a46bbdd4b4168eabf2560f7de0d31f7f81dad329a5c75f34349716b55c1bdfe22a33bed35bad43dde4189baf322aedc650ebadcdf549e262d83cca03c2e5d4a362311b544b5661aeccadeb4a439ee449b307e5f9922fb5c0c0b0b85c87824242d250de4ec6753f733bdeac7ceace08aebccbd522b8f230f78be0cac75c5404575ee6b222b8f2acfbd166a58be08a2a822b2b2aefdfe205b9a81b73ea5754563ee5edb0bc75f170787174e4a232b59e1819a9689cc0263058eed7aa46e38caf43dd1a9f5d9a2852e80f05a24154884231977a7c1a5ce24545b95fabdacb0d2429fcf7fbf17525ab8b9d88752791aa0866b49a42d9b561e4ad7b2712be13caacb933c95bac29b45203e68bf9186f07e6adfc581fbd1d199cdd88fff88f1f691088a58a324e78500c8d1b03e35008b9e74890c8c8b4c0dc2904c58c621621215222e59349b98f8ce73ddea3bd07c4cb905d19a2a20b832712964ebbd1e50ee9f8aae3ad8eeb93a4a53448f3357a6989e6cab823fb1a335b86bd9d8a82a9c19a79b119b2c17d34d4461d436ab0652369e5728768686870c3ba8886e6a5cbaeaa953411110d9a14d832957074d28c6f63c95869490af5a1b954c85b51680ffda146ee95b9e6b6f4861aa891784ba841a17b55aa7f10c4c1c1617446765732cda540f4c75bfd382eedf128504441b42445e501f1cea0695348f55b9e45526a4c6fa58ea8c3c626bb2a467e7eb41a1befadbc89373a7464f76b9feee91e1b1ddd5b8913716e6ee27d81b953294a834f33aaa5c4a0e3c6be953be20e1c1caac494715c5ad42d29312d34684f8a88a85d2195824a418bdae5af8a3db497a2c2726949bbc1e9a4195ff636bbdc5b4943a4c1fd3d90fde5eff0766694063bcba494eb782a35d8abf772ea4e973ba348a13d4b324aa481a592526e9692bfa650b73ae27840bc3c85ce40bbec72a7d08db9338aa964c2bc45d4602ff1806c9985679194f2b95c39060d292c9392b2bb797174040509490c198bb5683975e51369644463838b0d291b5e3ebb35ab865a0f73876ebc4ceb5177e807d4697dcc0d3c91b868e02924a7fcb97165d090046a5d8924e6f276f0fcf5abcbdbc95e2f69c6174ea5ecd21cf32db7035aa68127920e6899f59a0acfa50883a71013be902ac9fd5449bb424a4489628f28ed627dff546a97ccf7cf25d6953e3258f6c81efafa5c3ec7db49bd199fff0f77c9818ab9496c19067fc00c9ffc302ed1c8261448523651dc30857a50252abaa4c585468506b8f2b262d1250d36554295f0c0c1072d7269a1a182d360af6e0e3f68dc21105471b943340f450b9e4253c88786001048ad1ba8125a4497a85a3d282044ecc6a5c5e57db4bcc5e9b43b85261493e64ef0ce226ff5e3e0016909eda0b9130a000480866a5a71c97ec5e5c635e2066fa7e586588eee51237dd8b8dc094583fd283c91a4f014128350eeb700688134d02043834baa8506140b0d34b464d40a8bca0ae7eda8d09fdc6ff23acb29d1208f9b4766d074427644ca960b2b64905d8b2690320072d402a88acc034652cc7eddf488d4793b443162b12425e9ed4449cab22d16539a48724f2131c688a224468cf378c088419942624c23fe337fe85664a61d60031b67421b657cda4bec3fda7523eed3a0d0d675b1e7e53fdd6af7699006b671617ee6b2bec6a58faff644dc20de8d7497749455bea046b23f4d4174a92051a3f8a2c0fb53587c09f1feb428be24f0fe9428be2af0fe14497c4de0fda9507c11f1fed489e8eaf1fe3428ba7cbc3f058aaf21de9ffe041eeba7c74397b9ffecd7e4cce0b04054b66f710883439c5cff0b2cb9233b9a5737e69a5720c8dd5befe617bcaf1fc308856c5ff5617c02ce101e79fb1027db0fa318d9e270088fdcd90d0be9de7bfbe1dca17bef51ddcc2bef63bb58acb5af62e518f2c8c9d64658663f1b9f646bed4f9c7699defe02620fd5db8ff30b3f481f2679d36fbfa792520e6b4f8ec3ab1dfcb9e7f1e7be48de6e38e5f53e7af67f340a8733577be717e44da5f1f2beb068a824f57f425c947dee3b286f9f94eda9b3d67e7c96db537c599647fefa34d551288f8e7208e6aaba2ad1a61fd5b57476bb1bc7ddaffe76b577b9a1cc1df01c7e2013f1ccf2dd0d87a832cbd7ce2c2e2cb8d2eb99e5b2581957dd76f18078dfedd4b6ffa36b72e5b8ee65cc290f88fced5db4b7efdd0ccc6421f2371cbabc3693ed5b1a1dcdc04ce67e2673ef225fe6759f9a843eae6c5ba7f2db0ab6261c7eb7ac87c3afcbf6a5d7c3bdf63ffc475b0d0f9909bf0ccce4213319d520954824142c570a9de10be5913cf2a25df6fb2552bba0b42b1c9293b7b7aceff3acc5e167bbcdeec32e6fbfbd954719c95f32df2f63f2c85f5acb5c6b5f7a3b9cf7db3dfd7cef3d6fe784e36731916957ba89a1a4ee910e52e5c3f3ceacdd9394eb6fadc7bdf7b15ddedbfbdd6bfec2df610e87f23b793cdfd3ffeccf9867cc76be7d93b763adc5e1e4baaedb4e96dbbe56ee7f38c772e2417bfb424ecfa97cf7917b151c763ebd4ccadbb3e0ed6bd87db709393df7dd8744a6ecbaeea367006e7b4b64e6d36f78a57df7d9f6168741b6b7bf7d679fc3d586a8cfba93c7c3bd35711d0ab3745cd7b1785cf75d673be8a459fbd786d33b890f701cce517298fd9c39fbee654cfad8bec33fdcde508ec16121767a3c6838dc3aedc3f91c0e6552cefef49c8689f409731f4eccd9afcf7d98a968feaaf1dc477fcd3cf7b6bbdb9df974ed4b8f079aed6b156f32266fb25f31fdccbd5fe3e2ffe1df8d79c392a8c1cc6adb879bdd36adfbad627b05e92353623fb41d16b2e15066dfb66f6f07ee3bfbdb0d89e055fdd367f6b37fd95efb502ac9a799b7eeb3f71e9f4ef6b3375dfb1a0ea5929c7de7f16c2fa4c3da5b8f872e6f9b7def2511e7ed781ccdf556ee8620c8dcdbd9b9b703f7dadb1b12e1b8b777e6aac9bcf5b72f89da15b37d7905e943e67ffbd4dbb1387e324926719f75f7b6c3ab1502641eff0ffff71310757c4e20f3189fe0ff7e916c2f77b71b1a71b5ecb95b246f97c8cc1a96579037d9fff08ce21bbde5adae45603a87249c9c7ef869dd94d74a58967249965249969228cb4f29033129874582b210d37b1f3d20614ccaf2bb8e49d9f45e4eb62d1f4ed487da1192059525ab0310f3e1af3e9cc992c6876096aa0f6bb2fc3ee491a5c4218dd5ed29bed5bffffb4be69dc6b3defff33f5dd695bfbab1c1bfb15b3257bedc9ee20b55ef2f9f9fd789f9255e07e6fd55fef249f2f24bbc4e0c8dd3df981bbb057363db40e3c6066f6cd0e5ca4fddd860cb95cf72a5ca7763599eb0e9a7a9bbe190989435eee22139597be9edb00245125c30f3763b9c97ae52ac8d49393e527c32a5900bcc8a176c5698c00737a8951c61449812cdf082154408424f0c8660050d52180107388041136460a2027b051f5801064cc6c8c2193b6e3c498109c68042d00982c222207851104e4d64763ea104f62bfa014e912b98b0c40a5a3c3185338250a4054a625cc8c00927c820869b1a3ce103280c01053a98428b28250c317c50852d6071462c07072803c90b8410fbc2c7880e56136674810a5eb8c2126c10da3869e20cce890b7e540220a50c5ee00292a2293c515385a121644189123e55a47274d24452ee7274d2041421d4ce66939129751733d09882a4af65aa2f3a944ca9acf56adbd70d7f4d977024534a29a594563abf1cc32f87385370c9fe857ffc50dd922e39fc32256aaa90422b8b232594810b9850165f7060490ca258c1040b67b8c197a30a1afc60754a41ac091956e06305d82f61093e5230e1841363c01145a58a13b468a0e789164cf801195b7072842e9cf8e00b315041ac082d5811164e4c9e3071e9421541545801186c308589cecce20c56a8c20966d8c21267d8c8115604cb2d39360143690bd99e3a9b524a29b3cf9b5359a526250eb797c13559bec623cbaf7426b6e4cfedb3b7dbbd5a83bdf5f6b3c1fe6fc3db5737dc61edfb1c432dfb37679e52b07186caaf3f46dc9f4f121af6cfd93fbf9bae7e6d4ef798678ed1854477e739ecd7d779f48184cc443a776719630f229dfbe96cd77c7fcda740b748d2c41313acc00b59009700841b3491c5139a88c217f2431c64cd6c54010693294cf244e9cc39333ab43ddd302a8c9e744e947bd454393299f283cc92239329413389e909366420864ccb518b2540f1ed9a2ad68cdc3829b539974cfa1cfd0d47aa6a9026cf573de8af08c389f3b599e167a9e69c4428ca734eacea57844dbc032944300218497070062874c2cfc9111f9aec51a8410ca268d1041c204189af393a79c119f5056764c949da450194944005d8154cec200537a08002217ca106495394f164882674214990112a9a28b211b53881135e00431342445ad0891f85cc8861d36209506824e8e20c5280e2064a5228237e014a0bc460a4892864f4a0092c06a62aa888c064f5d520c20a58882054838c212cc290c489255160d2440c7e40f4c491a415941171154860a1220c2a0e343922842db4200c5d888218b0e0240667082189122e155e9ca80863c505880a4cb6281ab241147466132db0d0450b9e00460b7040859314e0d04a851559534a67a6bb3baea918ac5139931bd0a0b25e6555feb278c64911601354cdcccc80302a7440c50a723883b335dd8111be29884c53e040e594236c0a20248deac0a85082fb3c856c162b408297d163848232ce1045153b4bb8800b24252f60c0834f460c9e131434318720a50c54942049132bce20f20413445378a2053e41d1639840615374e1452ff8a00b477e008511a8008519402d48c29432c8a083152c94108439e7fc3e542a96c3af9e91d19a851329b69926582409a369e4c8644a14b297236c8a2e66e031c618636ce92598d2bb9dc8b949ad33af9d6b9d74c237e52a08a765eeee3ea34dfc495128966ffa7477197cf265dcdee413c31863d464d4a8bbf7449b48372dab747ae3186d681676ba778c6df21b286539454f0c87d851969d12a56c9471b07126c471818af533ab1eb68f999b77c8629f257274b6b7b837ee66bfcd3b64c4ca3f8908d30141f620d0be86ec9ad3fbdf7f15710fda3b4e40d4d1300eedb985cf9bc4e7f4e95385e89b3fdfe791f7c41e5ef4abc590f2939ffce427f1c41dbb82d32c0633898c9c3492a3f6316b57faf4c81e6944fe78d62e11239492f8c0edac5d0a08111b52040480a0de324e2aab6c9f2ba0b6767e6dd6f9b55ae7d76e27c896ebfc5adb755d76639e573ae1c9a020cfb3993c2281643c71a6fab44e26542091b4f4a1876d4b418ecfdd1f5b4e4e8eb528c8f1a7d6a186a078e0e074d775da1239fe877a95bb773520d8f237e0793bfea61956e7b42a91adec4c9087b60d4b24dd8a317ad4144a28963e948422f7775b782de39c26fa55f3598124cafac3c2c699d0e5e9a3f76df1855a46f2767864fe3c0214c94b786afe9e87e6efe3995a3735cde3f1fcbddd3aeae910b3ca15dfcca10721fb6f1e4fe7ef2d1716c93cdc7f1fbd20dc7fde0a01a867791f501896fd5970741fa2b7422fca3c59e65ee5edc761eea507e42d777962e69ec32befbdeeb9efb8edade4817bdbddddddeeeeee73ce3929a5b4d69a651cfe8c3e23ce7b8fa7eb3cbcd27297793c2befad789eb701efadec3e02c9b20724cb2b6fba595eb9d153c13c59fe62b439e1d089bc20a6ff625e7d2fe4f4a6ffb0ca15df2ac8e94d7fc2317f28b98687474ec9dbdb25a3a4a4a218ad5ccaee2fa59452b65176ffeeee9e1e4f751a94dddfdd9d7a3c742665f79f73568f67d2a2ecfe94661e8f57afde11724a3661cde3e96cbadf12beecb5df3c1eaecbbc1db27c7a6b0a8be40f7fafe5affbd36bf9c47d7b3ca73f612424d961e061201d8e0d5a5c9164d091b2e3b0d63089a33cc489b2fb159fec71a02ea2d22d3a70a29642c90b9c28c32127e767a95d39f52877fda256a43a863a25eee8af46feaa316f21b5ab2a35f84574553044d0a88ec19de476587426f1951d79a19c1dd922f77b7684c787d991dcef38994fb644eecf7a6acc5f5ea7d425af31f75997aad25795aa5256eb943a066dcb4c348355a01664444936ce8451f6e4818383434e4ebbcffcd780a04b54831f4ba5a294d2e932c21c0955a962d4e2cb1e3983905b6295a26f4ea2a62ee3b24d94f6b411078b60304101077b6261dc1167c421715f7046fee262de426a17a7d4e017d1c5818133ca9d655ccc5fdc92b794da657f38304497e5386bb9257f59fbd32e6b99882e6b396ba9c8fd96a86de8b740fee2c15a1ba3607372acb5d6daa1f80271fe559f9643613329da740a2c8c0827a29faf023267d3eef676e4ed4c352bafe490cc39efccb676d2a358de997f38244064f276d429f8fc432ad432d669ca689e9c141c92f883f8d25ec32a0f5fbb7cb8408c0e864509587a3082197ea220c6145328d17991e20493d8135a808509605de8626e52ab950a55c3435cadffc1b8c8b556ed519ac6bdc6e1984d8a8231c9b5d6fa53ab9ffcecabb556d8d6e5888514b690698e584c810aada991256c50b03d5046607c11041cc0800c483df209944044c613622485610b53dc009bc106cbc1ad6144032bf460090a7c5640a3045d9c000a1433308104a0adc296e0baf0c4146aa821fb6b5c6411454cfbceb0e51ce10ade11c8305dc18513a4c8e206403698c2006650c60b963c21258a2a747294d470042f8ea04526ab5cc2363328d8feb2a00118957ef2cb310b1af08006e1d5c407420e3b0251cb03c620a38a2f6c7044060b74a813c6f0040941485fb001195039c28ee044b63962d185284ce1afd689aff9eb868d9ff9f9f1df5ffef1417f71351ebfeec7d7c15ffff17bf097ccc707c25f31301fbf087fb13ebe11fe017ff5f8cbfa6bf5f2f16590bf687c547d7c89c45f2e1f5f12f92bf5f16591bf685a1f5f1ac9a3eee3cb98bf583eaea87c7cb9e4afefe3774ffff8cbf4f11bc83bc85f1b124700b17df740f467bf33a49dde032289ef4ddfffa35b5a5ee5d4fa687e47478b46071e4ad9fccae9d3f1998eefbe888ecded81c5d2eaa27251d17061a9607ec78605638387625e90a841b9a86c9edabc5d7dcd45423e124720f143bf3f4fcc43f39100220999f77e7b2092f8372121bfa5e56ffe7e111d2b2f1235bf019991380288d50fe0a31e854720f3e0479d990762f503ea713c0e3c021d1bbf83f3567a3dccbcf47ab0f1d1dbc1c14337646eb9ab9bc75f44c7d6dc04441d1ff0516fa5f783cc835f03c43feaa30784f7e0470f08d3a3f0aae6fd8be8fce81f0dde55cd735f44e7e6f650e36deba29ee6b6bc955dea4a1d9abbfa01fc1ec4ab1fb2071fc423003f6bdd5397e33bdb5d4afb1f9d7adbba2cd6b3f0085cac8551511ab333549f755b0f73693ee6469d98ffd1b67567f0ea87d6cffc0c1e81ccb7f0ea072ec77334d7065efd40f3ad6fe111fcd3e0d50ffe344f834700be7fd4893a37aed4b1b2bb71a34ef486e6f770e37f34d8df9fe32691e407f9852994d0a383e35b9f0409c0a7791c2d4c0210c7b621ec28b2fc11908006f738942e1ce9d1d97e4372e3dab833b7c6c5f1967c7c73bc25ffde1fde92ff57871ebc253fe602e12df93057086fc967dd225657be5c0944e34ad59542de92ef7225126fc94f5d49e42df92d5716794b3eea4a98b7e4b3c85fb932c95bf255ae54f296fcefca256fc93fdd36ddfef1967cef36d05de9e8b044b2c1b26755f3ab1fb6e77e7b0e83de92bfe116ba2b1db8070d4b232c8f6c700f151bf181550dee616295b7e4af7ee8c7ef2df98d3bc85bf28b5012e068d31336ce843396e52331821339c29648c93639c2ae20233751ecf1451b9980d6d1bce5842f6ca23c91fc45cbb024d4d3d3d353025891bfa415384802d3913f899020c5b27c1722f257111df95ee4307f653ae8c10e7e8cdc8f96261091bfa2d191bf5ab6600632827a746491234c89f94beac8ef257ff9e8c111918e7cef310a9a3d51c77f8e84e64f96ad34631e05d5aa18e8748c527d4dbecc978ce179ce4f9b9963154af539494111ec0cc5db122c64dbb6e48acd6572842d414266c9910917964a60a5945a025c27de200b246c415b52c5ac91236c490c96a460c6da84732dd1c2e0476a30842b6e608630a080021d2f8110ae200524d8200833a8828e3b79c150ae393a39f2c513aa471b9694402b421856f0e00b1a2cc10929bc20ca11ca30c50c945c318331e8e428e111c5912651cc000856cce0670a4fc880890760c1075ac0785004304c5043164351ea912fa64a0d9ec084703045922e74661958e08213604e68e2c48fceec72c4428a0da0d8b07328a5e6ad788574c0f31008fcb71c3d1f664f8f0fb2a747c77d88301dc7d15b5176cb25aa176169a68917669a68a1ce4431831c7ed38709bd856741032a60450823bee8abbeb2d7b4ccaf165bfdf452124c29011b675c85070c230dc63cbf76a9f0c492ac8cdd268f11942a5a835f838d87e6c7ff1cc76e45159ed8528331b3e4082bc2518e1f67a29c827ffca6d67deedaa440b7bc22def89810a5b568c252fea476ebd3ca9f9a4b2cc45f737a433a2dbd9d7be79ab70d87f2e7dbffe1f28824727d7b43fa99fc0d08992fa716dfb67941a8cf972fc47766d7a0ac19adb30611e2f4a590ae5f7a38350c1b67bac917fe87fae6ab7c62ff7c92c88e808dfe6a5ff9d0146bde92ef77e583531cbdb59a2f7b88dec208681d0834c9604558a2c1e488051198c833c726453f5feb56eb566bddb48ca5925feba5b82342352572728cd106d6ca4ff6cf6db3b6da6d031b9c59965d4ae7dbd973ca484d33d166fe371b9c0ec5379355edad9c0d36b6d3556bddda47d7efda2d65adf5395c6bddb4acd65aabc43eb3c96ac456ec98da395dd3dbc7f673ce891d55c15cdfe3a99966eb5d4fdacdda512053f3ca6ab04656ae5115e79c9347b4a938c61a4377777a3bdb858d38ccb12d37f72e894c3fdbb66d23e2ed6cdb74d3f468576dc669dba67199ad1df5a6c94ffdc98e27538c36b4ca6ee9e40333d9ddc9edeb7f0d6659d6992abb32a3da35599a263111b9f1b0bd10f919ceb26dcbb26dcbb6ad1271bb6d59779d655b966ddb46b7fbd186fbd92047b32ccb62262595cf7df576e4e37057caee867dfd514a29a3669a8936f18dc8d9b79d0d6e52d20fa5942fa594f2e54729239552e2503ead1f524a29a594524a29a594524a29a594524a29a594524ab78dc8ccf2b3fe7e2b6383dbb66ddb365d516edb6f588b526edbb6454d4a4aa9c41abd4ebef84372b2fc88e30cf4c77d4e7fedc647bbff669aa850e1cc5af36d72d476de8ea993dece8f7e6febed78395f371b9c382442ad54751587f29b534567b23ec217e2e489eb6b5b1be1939fb9bb4b275dadb3fe1432432215d78a9d72df400d524e03ca210fcd0279437fbe5f23f2c6bfe2fe6916c81bff19b35fedfd3610d5b449bf5eff79351f1a94bfe13e7a62e475a8eeb74cd3b44a6936b96f907b2239ebf2a494524a35faae793bf52bebf3af5fcde6fedaf6fedbd7e7bc20fe1b0e79a8d6a3536d4897e95beab5ddabf50ed5ae76779799feacb5d65a3955831ca735d8cd06bbef549e7f07bb2c1ce9649b5fadb7e7e99ade3e654afbd03ea4b433454266a71a91ceeeb3662b40b4d9debae3b839ddb64eb3eeee9bd3edb71c1adc866ca8ed036548a963e944abb341a733feee5e93e5c726d951145fe15d7422298a524a9ffe44754bbeb0cb8d9a9f7d3827fd2964d2ac0e894d76bc089fa93defa6a55cddb20ed5aef0e5bb287f4a31cac917762ec73f51fde5b09bb99b19458568f043fde8b80a12ca4c6f48c433c5a1fd90fb8d66cff12a08fd99290e43faf58b64d39b86c4a36c7ab7a7d3db13cedeedaec96e26d36f26ecdd30c2ec0d5196b3f6398b759059cb726850d3dcce59699e3eafcc35537add677a2241dff12a887ffdea3f2436c9f58be422b948b6f3ae9ee6d91ace4462e61597b71cbd20b2a727d7e80589b05cb1ac79de503524a77dd2fa81bcd2c1ff94c3059461bed53acbb2c745644a294855f33bef7d6bd10ddba7bfe25ec8f6da73db6bbfe122321da20a7b5071ed42d2dd5d95e53b8e27f8000a4354b9fba743c1cefadddddb8cb9290d6514509cd2afdfbbbf67bbe4b7b7fcbef49bd2eff69f410d1671ffe84d7c44b6a3e16f5056a135dadd46e4cec9a10e5988e9edf70d631297934d3f93b753f8e546b584ff427de3707b0d3b8e0d865afe6e4ff1752f3f87769dde3fc75f28d7f7fe3ab44be5fd0bd0ae9516dc7d5fa8caaef2c913ee2cfc1caab8bb5d3c648655544618b97ea675c7a32946f7e4a642997ba56cbbee54862ffc80f4f65f277371648d1d0e7354b08f388c466087c378c4e1180b71da65a37dd9bdecec6f1b92980f27cbe2507a1e0f3673f63b8fe7fb9767b52b7ea4f1730c1aaf526a178db76fbd1dcb72595fafa6c2d29272f9ce3aa50d875fbd93d83829dc4af6730cfcc3b7d5732b5848f65d9d5f903e569e436dd9779b8a87ee5538ec9cfd54ca9c0b0e4128291cbe172d38543d61c1429b0adeb60d874378acdc50e6ae7eb8fd86439ccc3d16926ddcea2b5ed5cf5ff228e5e25193ecba96d73e9c4a597656c2e01556b9e014b6afa55af0c6e23c1eaee5b76d7bd3f6747b6ffbd3969a2d9af651a12ccb32ef24b89f634c2939d4ba9f63cc28da4a86c3ce2bbf4da5f9056d2ad5ccb0b88f2dd465ee27f7d96fdac4443a675888fd15d91941817951f4849338642ee491b9c7c99c7d6de5b78ab5acae68730c96cb7a951bf3a82bfffb4c8f0a41d61be12f79e420f8281cbeca8443958795366ff3386e0b67def06af5f2ebab549fb67d1dc223731f0ee191bd87f910273feac309a29e75a9c7c3bd90a467a8446b182bab9a648acd8800020000e315003028140e8804e3e13c4e23c90f14000f7ba45a58489b48c3718ea32008528a18638801841802c60cd0949068ba1bb76da839b02e56b6c293e7c056f05fb18c243b5a4d9ef0bfcd39f806e4bfd3b3502183e4c767771d57d53eeb869115ff2de7ce0fd62469172883643ea3ed9913a8415cf0332f505e7aa62d2e01b9396d7b7fc33be62ed835b649bbd7c3be616967de901f62d57ece2b5a9bce7eb16ccca013675ff48a10cd0834ac9d067f23f3f32ad0cd262470f5b076d5566cbddc5bc0402d3a8d587a6a71cf0a8410b022d3ea0365b9e9c8caf707a5b548887c04582e31a2f51595ddbbca46cb081673e2430b89b98adcd1a35eaced9bec4b4e6ed719990fc1e56947d2e9457b13a7f4e87122988a33c1b5b01a236b6147f92712f4183f720785891436125c5dc017e456c025c94a5d328dd4210972a3d2f9c57136cc1a3c32ec063d5b448a26283bcc55d1325d2dbdde1d53a04e29d0812cae21fbf6a0c4bab3b9a7187060d860c4cdec58bafd6d1d487ce691ddcf6fdbf606a951a08dd8bc7a6d9cea32c6ac5834efb48c1117eb2bb80d72fc981d0d11755a2c2e7d56c14915cc859e2a601ffbb9b2b29602ad0e5f38125f50d79f5c2e2f7cda76ee775b9526e7966de8d96ef6790fafb641c6e46a55ef47c9a57cc3dcf2d31feb7181c3ed5af2c4b3b38c1668ebb0e396b281ee3af89648a71c199f0a40dac018c7e78c65d94fd84fea42731b4b11796e5c755387f8427af14884f552f9c8076f04fb3d0411380a58da12d7f7cbf5f69fee6d1611430ea18e5f080e80336ae5bdefadf885523794e51828adb05133611bb9474a6698670820047609b6523fe14a218f2931189ce726fc013f6d75301a4086ec283e5f19e7bcae099821000ec279c6cd58de8789eb5f08abfc013098af4f31dbc388e047b7abbd623bb5fc9e55a140ff70062c0a3da258f25e355d21aed15c9194df566e6b351b428fa900a0b1e7cf588a4db0ea456d840002ac8d03623dc1e60e2f077d48fafd4adc96566bad96ae1cfcf7f3ca8ccbdf7f55a1d2d66d59102afc0af5041c789f550370baec1b1087293cc64ecfdbfe8537d91b0251a1e62460c2a3ada1abd4270ecf2bb876bfc72ec1998c1c9077659121f055d8298dfbe6489b60b714a48b65df920531ab6ecfa38a046578aa9d947190f492834f8bc6610f0706ef7dba4c15e5c70db1f6ad95c460d4ab61817ef2e9451310ecbd9f0a16305a0885bb4a37d2127a51d12ff583cf04982be48db946b7d8d979987fd936d647355409dc70b76c9be6bf3b5c2e37883ddd33d0b33dda09fc439cb4744b6b0db15c44a7827261ef17445e299dc02cbd8e5911ff5169160f27556b8a29437b3b283420672d9579f6efee499f6443db7b0267ab71372bc4eb6eff658ef648cfafc9e3a70ffcf0842d5b5752fa131167172d72124398d785e1df1cb3454d00239bafd9466612570d3c55d9c4facb1ead83b5101dcb525929f45f9c2623cebfb5ee84e852db5088f2af7bf94342a9a3642c5eae3b7a9105bfaa6a05a551157c354f5be681e304a8eae8079962d560ac860f8e2d0a15cc430b9c55ab0719d123d8869cf597705175e24712e879380999f537dbe2cbcd23b45d4b42f33be3d9c8bb0889e6229605bfe024908826589560af583dfd5eaf6002088a5b2c5f548c684e4c782b662b8cb64b7bc6b4fca7a81775552f52256694af211e293a80341f6ea89cc6b3e0c4694da1b93921cac7a790b3435df567d5df0b4f0f3c4e809738d784a8555fd0728653460d9f9087f14541767c3413a4d82db0d2402835260f85a459f687adf8a5b00ea8745a4d10260fad5499ba17d9f79c3b8ec9497a6e2b977bf868ab48437d961813e59ad635770e12504ed2d8161a076404cb2384069d757aa47aff330331cc424aed2468ea6a3cd6a10a03ecd37185d4fefe538367026c5a80f7ac03bf7ef1996368e19fd579614db62830856105bf6a03d566e4365a9a17aa7d28a3b4e8cf37371fe17f62eee3eb0fc4c158ba7136f761a45458ffb13791d3b61fe40c7325b236f70718cc459115a9e6cae6b00a4c13f8b545eb32ca99ee906e4df418303e273c6c56576baad460dac9dd2cb3eaa3ab182ebf0cab354e7f797552756c9f94323d8f81a243f436757864b871d554a46d5ecd83e59e193723119c09d1b1d0cce6309fc95016d9ee614a1e9ef5db9f63627a4e2606544a76ce6ac6723e0c9c69a45f999567dbf0b9c49a5c245a50327638b180b25218665f6a85bc0780cabd87bfcf9cb6915b9efab3fa61f0fa399ecb5eccb060a75b4c2794cd8eb8024fb8264b97b63e19fe8f1778b7624a33783214c3391b17a3df7e7bfbfa7d4dc2a3a2e87c5325ff023faabf2134a63fe33574e4a3ee4ce8c2aa16e815f22663c5c1f33ab659eefebd306aabdc93167a48626b45e6ffe2af492be2868e9cad040aa4dd8389ba23ea4d7ed0a9458f217e622a625fe1dc8a18d403bc2c38523c423af0feb7b122f9cc2215b648e084932b1d7023cc074abb62a3a6fcfa29ab115c04a8acdb5105908c4426c1b628bfb2feb92f792e9f0c2735dbeb5ee518b24c1f90e5e046bf5cf97e033425c8e4cfc8e3a3dc252814f95fac13c91a7ea5519a62630369236659120cd5d8d4d4731dd56700e7e50412d090386964ce8e87c13c999ca246eaac0f33674bb1fb332bd507db41fe6c50e434299e812cb76866079daa98826137040318c363284f3c8b7211aa4ffcb5fc6dc3f135473897350b7ba34265ce32d19b549adfbcfe034b44c2423fea7c7b9fb1fa8589ae3efb4cc1bf442da0f6d22b5f284ad38ccfe9d39a8fc25ceabd48634046837dfdea54287ac0fdfb9b41a413ef476c853a2379d6a1314f838c3c45abd7a5199a44115dedb299257375f49b560b4cf95dfd67a1b04c4e935358409aea418c72576b8d1c5cd1ccabeb402fe819913ae7417678a864706f6137469a0339f2f9a4c01eeddb2cd385bf9602faaaa48e41442c53b287cd201db70a5088ea264a055c24508f5f3f215cd83dc1e3a5a0ae6522153f002125029628f04f92e4331791661a73e2115c1e372cfcba908bab803c0701d664bbda573c83b6819e9ff62dc33c7240b284defee0aa6676cc1c62ead3dcb45a8577a1350a8b92b115aff4c92f0d1662fd04fed1815e185a02f168590909225b9af52c02898bab1500e1045564d4153c6ab017789d96fe13d79029493f4c6263ee79e673c58c497bdb3871e51544c8b182d332922a50e004a7278cf04b6f70124631b8bd4842964e59bdf167dccad351768d92c80418800b647812f59b876a1e1350ab978509cc5f9b9c01ad40edf63637a491c09fa064f9a16872000c28851564f2c729c6e21caa14f9dc12c48c8f9900a7cb64533869303faeb02f8d158c8682fb414a2b86c92b030d990e303780eba93e991e84c5b47fab1f9e34c3f37ccb209a58d586450ccd8d035125b69a8bfb44760d69fadce1ed8ba418b125953a9034809d0638ef8318e61481563e5f330eedfb117e0e6ffb0d2e8edbf8711c0ebff53b1e1cdbe537dc1cb4efd7f81cb231fce41cb4f4621fd7cea65fce17b2f4d9b0da65f3e98d08a88f0538e9fd50770f71a94f4071aa28e98ea144cf92c9764818fee4c68f57e41b0b997e444637a303385c266dce5b1807dc6139aaf74b51f73c35946d99479074c090b7655e088a8660f769697c3c4d6dc5f0bea569fc4751e4682148005a6930385aa6ed168736cf9b10dde8cb8e9e86691df83ec63aeb1f6cc71903bae0dbd689e78213bb91d07123cf560c27919c1b5f6c3546b3b5a7430d8062700c07505c787c024011444e6554d81a21d0730b213f8096f01d0296b5b3c4eef096767ac09bd5c086f95e17e6cb63824a4375d2f999aae2cfdace36e6933da4d432d5035ec9a78d8246de9731f179324d81e90ed16d6cc879664db7385ab40e4474873c3f8c4cf4c3a47586bdde80821a5577538c3753fbeddccb8c4ced309eafafc6aaf4c799f195a0002082b3f35f31fbfe78c324eca185f61cb37e5f397ce4afc369cd42fab53dba793dfe60d25d5c778500d4d97aee5c102091f8b28deaa456bb490ae1ae5aabc310d41e16dcb0f8367ac11906d53c274066734fe68cd70979ee27be2fcec5a0fbc4980adee92c0158445e3fa3322696e0c29780d05b2bd4ea62b6d864f23ad3e176381ef607f7af64ff19eae38e89dfac33aa8b97b037cca40ab205b3459d5eb02d10a0890f2f1a1b0eaef245472b3aaf90cca0cbd5e77f13495cdb9da4d9ccf2dadc96c29538f4b8f4f61fa0f1696c2178e56a825dc009e2be8b5d6d7f8fab514b6a3288e209f3aaa8f1c9fcd262432806a174cb77cb3a5fa695939f9dd22b850a37f62ba81e66490c0682f42995773970ba1660ec27a3f7585eedff2e247c86c13150f790d95e77132ed4ba226c40542d3a05e106019f8d2a124b739dc6471ed3a2a8e3845f97d00940adc71e309c7e545f4006f81ab21e81af62333f1181aeb3cf3cbec3b5a61be3b0f6380c6e096702285145dd92191274c7ee1556482060e438564491ee628923c0473e2b48849385ccca3d273edb596146229835415a6d24d268c9bd6b3f10ec3f6190ce8ec807c8bbcf91324bab178cdda1b4a48b17daa74a3be129a176cb9661b0db3083d8c6ca1cf7fd52636758825cb04e98b097d8533677498c5d697e2c0eac667fca46d34a09e64efff1f249abef5e6ba768aa7b68a1883b5e3bd536f9cfb3b08633df6b18cff62ecc906c2faced6eb592841de3984d74b0038e02d1aacdc682fd88d51eb4d9b07cd96110417c697994702490172783548a8e4e11eeff0fd8e9e418e893d635c3e66922e7aa4a4f67ec5d2d047afd6305e8bf7e6cfa27cf18e6ca58557e23be8f57b204656b03a576247cc62bf0d053dffb42942ff8bc5eb8a18606b294660b35f6b0df61d855ea88c2d6a5f6d703e1da11f087e8e74df5937906830b3b13e1e7e3aee0b0e2d0c7449aa1dc48ad80cd4b2efb9f7c1dd0d3277ff2afcc744dcef58bbc958ed505b1fb031713c3e85b4abd9f32d4142bf7aef20a9b2013816c370d68ac51daba45945209c9c001bd1f82458c08de785d967246ac1ae02a16bea2c6d2653081475d0869bb0109c5a158e3f81fb8572c581e74c784300c94ada112d2592974b392e9e4468f5e54c567982507822650b7bdbfa334a52eeb67bcb0651d70676a44894ba5711f2427c585d37259df5f881623b71657824b4ef89cdb6be1f464741c7a72d8dabcc2b9cc401930055d0a87b93584ef78ec03d8614d8a7024a2eea6a1b1958b9c2d34aef16e1efe748aef3d0067c4bdd71c9cd2833fb83c550a91341ae27bf8bbad6d59ee156678074916992a54ad80a3d89f4a1fa68dec50399cd38e72307ab551d74da3bf639cf581e474143bd0d78d64ca99373567fde4da5b22364a14baec7d4eea6db1a20cdcef8f260393dfeb6408dedcb7db4309adb63bcba3d5b24c987ddbc5ed36157c8a9098a6ee6d6014f65978446871abaad7e40a4906d99e17e506b081763404f27b05383f7d19b2e45e9e1a7df4106d45dac56ce7c5440aff4762fba6dc8acb346b057bf52d6f6b5a78fd23723ae9d79c54f92d9dbf9af10cbbd0f833467413547d41ad82b254d7a5ff7051082607c48ec9fac868a84fc1557cfda5411542fa7a3a18a8c6470ff2f150c125148c5e3f3087a6f10f7d575dc0cdc452ec2faddc04c80932be01a621a92f5dbd1e5815828beaa15b90435cdd3348e2b07678a4a7c280bd8aab84f609d5ecffc1086c05c368c6a78f82f5d8307ab10c274e1ada43d8cb1ee740c92dd9dc485d2e507dec1f89dcb4bfad7d7035ebd576e1be842c61e981bcdf3d5bfb1e9df4fa23867c15f680ca19e0c6894830f10e39da436d379da764bf76dff528708cbd868033143feebada75a8a08f1bdcb2bb0b2c800c4d09b6b00570816884c697549e9e34e515004aa697313d544796bfe22410c55b66375ee56cea7c9efae1ba56912e43dd4daa32161e5c481bbabdeee6e84bbae0d00c106921cf50bf0fe2fe88da7b08dbb589ebc611b1c83728312168feee212ad8395e509281e215fbc1eb083323a512f7229262b50389de9492dc9d88d4b19da7cc681fa6cd91e094f30be1f6fd32002c536ea20cf5a9ba1a43b33369ac1a67978176524e4d64ff3cca3e25db2c0c0320fc704921d89caac6e2a90c8accde9791e0843f05193a0770452098e9df04a817da75d368cc188868ce58c5ff07c15f76318ea68070c24590cc42d1196cf8439e305925494b7b57cf7af17acf051b430bdc0f1215d0669230ea01a3803545d533fe321d98309b5b2abdee82cb1964150a6cf6c8d75c2cadacf296bbe9aabaf506d2a332dd29f66e7f26bab3a8cc5fee7de5725ee45062b75710392db4395513366bd4cc88084d53133e36ad799296aea5b7c0e0482b3f402b71faebcff685507121c203d39a1d1e050b10c057580a5535fabebf0ba31692b921b8a7e2524edd7b00eb316700895687da8d0916f0d8ef5bd6e2344f45b7235d164a88f2bbf5affb32a66428db9ba32d2a87e78a234bfaae2759e59b1a991ded006ea99ffcd75b6b7aea3fcddf3907ae0987934eb2103f691127d56be99a7cfc64258ff6178cd1a1f19dcbaf24d36903d0d3bf95192c3a2dbd8b5333817be7c465ec46bdace21c2254f823f794e6189adcbaa0976db1c502e807fa5ceedd756984a5e83c5a09388a08b3e717ac9251b6ed6936c30b689872699897e722757d2a6b270ce82d9b9f85fbf5bb59e9741bd97f491cd8ee25846a060a7aea1441885a6b1bd85e572828552eb4180055431903dbd3bd0ed6b3a6c783748e525249a8bfc987ccd6fa22e1c780995166996a210f380233d90142e4279eeb22f0ed4e3ef0be04fe5d4061740dbe5612a13dfda7a4f00a3384d952441d596a3867c92fcbbed8d22ebfec9ff329a5dd61aabb6c8dcd1925d57576c5cf87b9b20c5ee3a57570233f4864857e80cc869a006b3d9bb8d2f32c00b302eba365dbe7950991449863a09496cfd51d5d405ad5e1e40963fcdd24a32d819005e12c71b55bbaee5dd679afc612288e157997bb8f270c71a56065ed818aea1cc638e44780786544a869c9178a9bb53a0b0fd52a933c3179b6544d2e445c534a32e8a8b2c3f5ee80279441876f1400a1b94e84101769d6ce953634a6b04d9c91107dbd1590ba551ada27ba782378be3a49206b780e528528d2bd1502d2f58c3fc72ed702c54da5ca743ca4bfac212e0d368f9912da93731788cc6a72a07201117347aea3824cb92e0257c6599ad4b944cfc689c796afcb69b2b5730ea5c413364b67f39e620d3730acb3f6331424bf46cdf06f3b105fb8be5643bab92ccf550ec7e73870941fb12b3e90675c39f97ee371923ab8746e818056c4bbf5b926a32ce0ebf043467907f9355b07ac07848faf9907ef9dbdf7a1f577c034f86652b121586ca517f542ac5bb25590a38130538e59098ae9561458cb4d6ea3865e60933025fb232490149b5c7d946d27782fa6320aea49debc538f09740e3421b03b10c2e0f4296e287602e866631dfc7e23ae0c18572c6b62c5be9ee2e95fcb39c1518c764c8565fd0603e8888c8d56f81bbfb5c38fad0eae88ad990283eef4f7b8daacd5298f3502bab0745986b141c905a016e7290a22cacaf9f7e4af9ffd1fa93de3973f12709338d35cd7162733f93a1719dbee42e852f43443229da31e4c6174dd9c2e210626416b9f920fc0867c75c4fff2edc88aefb91efe79e6b20b5904482f12304fc5dcfd6af007d40eb5173f7b2cec98ad90507c39a2a37b20d2c583ffd08604e5ebac542b2c57192e355c78d4594fa03bf7abecd07d50f90a0935007b0c952dc1e372135de69358a8b06b5aa26191752bda698f7861a3acaf8d85fc32519e541c98ff3991a314a7d009d59d3a8ba6b3ac7bd2931d4400e483e08242699cbdde88dbfa9cf24c67600e168da87ebccec1229eae963c21f79b2238779f7aa6c580f9a9d69e81eba2dd557d7ccd49b03e13c84500b472e1728d580a7a4e90167a17e267cbca1acdb8aa1fdc23aa1b7523f9bab2cf3b446f94c4accefee86ac508472d8ed0352c7d43f04c60c9d40e470e442d2c9b4170dab9af59baad6fd559003b09007ada0c72536178dbbd8045801d5fccaed87c12f3ebc3b25732bd4514217e55fd209cae918474550595dc7fab9e21626f3d3db0b5a25aa0059ba4a934557e6d8707177bc742101ce8755482aaa30650bd4eaa90521b75f4f8e6494b3df4ef0275a4444bf36aa02d75885ada1f81f3c5b293f0d3186ffb4d5a984a4bbc07a8913b065ebd9dc008e6b197d1d890ba8155154fbed80aeb0a76179c75f8d6fc7d661d76b805bc015982d4a90aea724a0d46833406ee902ce73e90b8540cd9ba95fbc31416cfcdcbe4c486ae36038e0eaa947975269edb04701342bc0409194cda9b37c3cc55810a385422cd2ee3128da202228657756d3124e09fbf90eca063fff1903f0e237c43fed9115075fad6b6a3f01b97df9db55420058fe40a14d8b66b4cb8ccd090e00dfe0bce9709560a2b0b54e6d7148f95f203790f6746c128e5f333715cc6f656194ee1bf2feafe347808c2913e822ebb9e23a00e88197cf551765c31f37969160737dbfac5869c1797887e6c0023ecd36a1466581ee9d289a85737aef8eddce39ad75f5c78cf6bb262dcc78c366ae4fda44a75ed00753fbe0b9c1ccb43db7b2cf46294398f6530d5a2a3d2efbee20a589382ea3422dc56b73bbb9b3eaf0a073ced86acab02955b74949d7a6104211c3c6a4552901cd647a17bc5bb64fec2f8e34f7299580dd18244b8d98a96cb32e11a1f97cbed72925de3f09e35e55aa8d2b9964e23c05ba4b84c031b95a532c5d48d154a9ea2a8bd88125832f1a2be74ac5ebe4e2d8b283b7fdad8cb7cc4e9259c8249cf836b5c8649855554ca4684ceb6a80ace23c4a00eb805791db427e549be3b407ace938b28def3a5a272128defac82ab4f2c6c6e21732b036a0d896a1ded1fa7b46b4086c20290f0162157767b5ed33b7aca860e214a8bca418c8b2ce12cd42520e680dc877a20f9d3e641fde2043e5e8f355e6819a0136b18f90745366ef6ec94721fe20d13632dc04eb79874efe109842d037e61cd72af54b56f748d827c59f9faa4c6a806b94d5028e83d2a71e1e87e03827800099937712082a421bfd8661f45c33744d05590f5848881374171139c17c1e9f0eb18466a002665e8e43951b85e5fee717c5a3d4f74986a24b1d7e593c42696368d9b392831ae306f08b12463ef09edfeb71777c90d76c7724796cb00c4158ebd8b8318e9a30574757cacf40e47bccc9956f54ab4ae59e5098d3473319d5f3c11cbe967f71f6bab9e2b5b6d8b6b5531ab9dc10eac29b0188d32370e19ed510b553731a999485e5feb5c27eb7b4791b70d1619b222d37598a4df2358b5451664270a74d9c681dee21c82e4b5d21476244994a1034e9991c9f59e727a65034235d705282649f2e9c0df41ebb062761e73736db24ca816b4de3ab5cc10dca82831e47bd492bc43f36dd2feda7241f558a3ec29f6c309db0365269adf862464e024709bb3b3e858222d0ad95feee07a7360fbc6a105e6ed7ba3dd3a74a265adcac2e98c5c57b572ee4120e8a7dd23072bba74ccea16463e524572d49dee502332221e2202c7db48f134430249aa92c804c626d6fa67fe0ab2255f103b41b5390de3943ae377832f81a15aba94c73ce146ed0068603e03b4bfb2a51dcea44a5e1ae821836317ab65f487312135b63c68a5014b63393dc65b9975c891ff2a1f4b2ae0805bde238bf201e652cbc80509e811b39dde7ec77a6c65f39cebfbc015760529968b7b7bfa85f4f1a68f697dd9d64959561fe0b2d9d5113252002daeb2e6e76c480df9bd59179386c78137deb8b5e2593cd86337119ff796ad1891ecbe39ce77c345da847be7ce9abe6487ae155388edc218e3f7b7c6ce2071184b35056f56d288163a7732b0540ea3a6669b2c179120202bb70c81f7b9947e610d0cf73d8651d12410a3d23835378a3154508c4843ae657d9b28cfd4b27d813821c68095db5bf4150b0151b05e8c69e447b4c461603fa940b474b688714c1c568181f862506db2d37f85b94b5831f9d9b9d81875f0a744a975686b6a038d996b9b7724704ccf421be86d05f14e6c0744cdf26ffdaca2d39e45894e6c39e2789eeae3d594f8d2ca97f5fb888a3268e72ccf5ae28a0f41bdcbd9965042d8ed9aac2d31ec2fd75f25922821f0ce26d4c4b7fa5ed7c18e2d45112ea30dd869c7d314268e0037ede3cd707694f6affefc29d340792f0dc4258aa9e66c709a0837bfd6ed9a79eca59f12a87daa4fc7b1b6c8d0c9e921727862b4a5fcf31913d25f1299d4251c680b57cd1406803d0f08462a8eebdfa9cefc88862ae83058f3d63ec8a8eba8906dda103f99b357c866764e054737000cf98a8878487a1686890af7288f0c394b8d2473b1f35f15b05c2b6048615757ded6ba33bbf514d719aba099cd228d72d191a28c92d77e317784f430c8c8e3a9f34ba2153682c540d341da2a9fd1e2d7ed1f3fe55c9392fbbe8292b1aaac7e751994ca305a72a6c42a9a4c0b274f4446528e94c53055a5c711fe0c5bbe1c1891f422f2288308e49b00b454eb1d5b2492883fb1d9609466cb3ed3aba5cb158683a31c1103483053d02de6d6e90e6ac46c88c5551854c42129eed735c7fe04bbef8eb8c1c6e80cf022780cd5c6a9b79d572e3e4f83a386fae4bb97c163afeb230fd714779149540dc620f1d698c4d355d5d222186a83b389660e5ab61e6dcb89c90149a3a627aa7933df1ecf28d3a8dc45a88fcd3f278fdd7bfb96ba5d3330b2355b37d26832efe8b63b728bc28e10f49c5ccecbcb0717125e793881513acd50fcf92424ed774cd44d90e0768fbd44a7435bc5a89738ff5cc02841062aec9bb3f5dc19cdf29e2b636be12f0ecbea59a0e45fb519e143e8d705b80db258c142083ab183771fbf9cf018f892e2bd0f9ffd7083b84bd0cfb1eb2cd97919ae057bcb7c380761b6835582be03add630de21129cc7fa3c08d722843919df5840b78a713fb80de1232c54890e149ae6d886a86701270cc293f8389e3a8743aca4967e069f7ff446b12ff57f28160be70f67b1327907f8d29806717f742b4635e118f25411eaf8080edec40e5893e02980aef131535b9791e1516f9389a53b6260f6e82edcc5d6a3a05003b75bf942da69256d18bec8498146c27906448f71d63c4fd56efaaa3b43233a70f3d78cf6f8980e780fe407b9a61b43ab99f12156ee4a83262d99ccd8a83920c19cb5b5a6b848781c8860c0ec865ba6809142229e49f4d1e37091eb0cf2af2cecdd6da1a13a2e70090502f538a84c01cec75614f41f0f044f45db02ce30bfc307e43fac0a79aff2d7169b2ac3bf3f0e26685a945245db24b8ebc8aea78392a36ecccc677bba0eb512159cfec9a11d71421bd691fba08c7e41e23e70adbc8be0b0657858702ed5eb0bfae1c543695dd4bae4d09deace84b6d44a688358be6225da463c466d2a504fda5d526f7fac00bd5bfd123d2153e15e3797ab90acb78e31428f8019117ab3fb2fd010089fe9bf0614381897e0cea58e3e6ca229a96327ac9a1b711305e4bcbcbc90515b1803e7abb8498ad0f15c565ee05e6120dfa65008a376e5ee4a1d3643485f0b6690f8a198d613d181c08b1ecf641fdf4bd16802b5055e2423ec2304d1be1b618d15941dbb335e5f647807c58029b5b240e17346647d0999d8ca0a3b898d2800ae07d82a64faeae884f665c48c11fd0bf0840086582338a0fd28699fb4b412b23218fdf9c9eec8844453d4a8f240cec00568760fc89555fba1c7c800cc827ea9df0a30082d3d37fd6aa30328d823021623f887ae4a4792f14f9bed468aa3e9bf10713456ef6925f3d3298e60ffaef19d3c06086fa8e21136b00c223613cc1eddca2ed384fde362bad10696aba053763c79ee88affe5c8e62423a5ff9780a1b9e40220245ac703f02055014a4c26fc89d4b2deab5a1f0d09b7fb636b83c0e55e7a15c51ffe88140a501fb4a27e4f24ab0b2881f29aa8562832e9e26b6e2cc0a70340f286b0cf107834b3e8f6e4b9d6805cc58823541dda542f892018a452ba049a140812e07ae44b89c61973f399c3c3675e82fd33fc6c01c30ae3e2850306b39d3edd1491df90bcb9c6f275c834b2f0564422b18cc0d50e9707d7736197a4f693d6cc0174f8423fbd9878070f4367fbbc7bdf7dc234d03a854b0fa2ebf6592df74dd9b2cb507550e88a8257f7c71532ec9420717fb128235e3754ad0062a57d023a90d403d0f356af21aaa0d4c3a56bfadf3555d9e10014e4013d856880d61ef6e7df83dc8c7aeeff68e04d063afd5bee8e497f7cab5263711e06affd1a0b2de6be4229d4c6c9fac68343108d9666ded0a61b6fae5cfb663dec49b89994f0836aa695082761a544200701a7f2153e4cacd7f38b7e0761dd2cddf04d20982e8a85262aa2948b56f56481306a5dc3dee7cb9f75896d4519f871b137349efd6ea80ee6a54086caa4f5f28f847e76f26f41a6cd46bf9595d4c3b4861869ddc417fc637b7b26cead80cf83f6f84e65b5b5b7390d28f4bb6e28fee3f44e62c4bc9d1eec351fa7ddbb756d55cf786ec84d5dba5010f1c8cfb1003345e7701eff32e50779a321f1d13122bff0c40c42f6129a82fd24dec5428398bce0225bf26e00ca8b6034d496bbf457d297ca0018b5fe2ad3b0478b20188a0581bc10ca3ed9eeca680ffb537c9c3ac131e858feb35d31aafdb67a74cf21bc7881173a3009e1eb07ffcf7a8603f6e18ed6f1a9711767cd598f21f031c4c1b171c2c222abb455413d1e37b9ec00ca380c70a96ee06830b50a918f563c5b001999cdc406d9b797d2a07486ca8763a2c5e9a2b87153c6c44a2055ebebde3ff4ad06509476ecd3621039126b95cd9357b5524d2eb2db64e769540bcd8c1c713ac42702b580c52db8805bb9461d5164c829384187e1e0d6dbc6d06b24c802c1fd9c3dbe5960d0c960522ad3150c05c20130b51b9cf292b7d98a4059cd28c4ab6bc459a6d2b3ea65d008c017ae62dcaba4a08fb0f002b4fd33638c06b86ad129f2ff9197e6638d966835c51fad115b0dcb51ed2949ea8bc2565f53849272cb850179d195443026ce55ef04827bdf626b63f9dc31abdd62abcf4f9613ed1e8ed4a2e8119de3a55713c673ddd1927407e9971ca27c6e1645a4a5cbfb0874dc0e453c5a0f0900669ac2ba22894e303a8be69b3999ab666034acba62517733d0c765ae6deea2ac82394d5fb51812603c20cd3d4f85360345ddba8e9a8e72162e1d936d523f894cd0e5a477a6b692270fcbfdf354ca276a6d2a15057b244c95d724e7133fde3401e2c8d7a6b66edad98b577eb7a72d008ccbc96d199b04f258a0a9ae4ebf36920cfbc35c8fe6aa00f330dc29431263016aaf3cbd250d7f66427257ec99be40533854972228f13bb02b31a93b9f1c54d94d008666fc283cc0a36af3f737427fc834122cc069f3d198b454a176678ce1d5841a7e36fae14b6b72644601022c309e0a24336db0978eadcd51152aaca9d0e7ac45371fd9d1c5b1fab7550130957c7a10a7bc06498426e7684a2ee1d6b7991b7632a0984a78019d9f83a5b92e7584e8d81e47f8e85a5a68928e69f5839481358e326df1bb33634a3e558e59833a6cc0446c2e5347405c77c011666be6edf18c57959b1d1c65c13430acd01fbeb543e3200c5db74b35c6b6d1233cfca28b4055a92804f108215449249789817b67289699777b21276f23b3f0a4bc94a15e9d0529397a822012a4863c853c8a77d4e93fa9a0dd23ae1905111657c7c70247dcc3ace63fcf101043c3033900f80e53c36423e809af3586ece033fe7f1b401a32793a2cbebd19b784a5ccbfd7c26e3c3abea2ad03dfa3843e26aa937a7eec8cd52fc46eb225ddc036a1e1e098db9c88fda5ab3e6f685acb8332f1024566b77e435fc44d2375747fc4db4ef7a9e46095cf216c4907df14d7ac1dae5ece981b333d83f25a30017e0c195fe813660301e20a3dcebaae66019873464899a4064bdc8b74a2eaa74ccda118efdf0dd7309ad505d29773d3a6588a019ac1d8263eb0046501739c6592c5dd517159b0ac949741df2d48dcda96c03d060d4736f93abf86becb6cf2bd5e0c96d432b188b3bb6eea5d393614946c0beac555644f7dba642f0819cc00dcc8dc490ec26faca1f9388ff145f177804bc0a102d0e1f82f113f310569744c54218d8de977281c8fb7a575dda89fffe190dfaceab7e5a36ad7d13b15955cd12d056daf3fc23a0eab3473faaa4d1f4129200fb85b506e1a575dba148a1ed2746f8ba9eb94d029198ef3605b2523f9ed2719470e0210578b4fcad5491486c37a8ac5e85d36c702cd139133a7a8a1683a269531e240e184fd2989f89fa59ba2c8df4e74da30b9845efd717da12eb95b546fdab1ef889448f3e6da76d652f2946b146dc183c28b0685179a36428cce24c1c8f2d095f37bccbfe1e0f25920f56ee1708249b2162d32e2d81102ebb9ec539fa752851e2752707871b4295d3fa1a313de3ce51158720caa149a47145846a17e54899dafb3da523a07dfefd25418945d63de1dfdf9730a84c78b1900c5029d4c2e2e67008fd259b9a624e443ccd7c0d286de921d28c8190adeba961ebd8f8987c9300ee26a1c8f5c4e4cbcfb8d2d435d44e16027a5c4b76dfa0d565c63197ca98e3d27ecb5340408da8b542fe2b6afd5010647ca4150f384973709ccbdd1cd44e4fa799a16128d0748d75478b15d2ca4c8030245f0e0b6d3e531ee7e8504981f419195dfcfdc78f7fca8330e04650b62b3ffcfa490dbc21e8d7995cf9f5932ae043d05233b9f643131022ed7d562eb19ac04e02a5d4cdb26a94175e56b146c669aa00309e9735b0fde28063e04aef720670471d2e20af5cec8ac88c28a6647c2907706252ae878fa4c533c7ed2042f9f086024f32f66dae4887558a5bdf6b0a9c415cd40b972acc9e5075f80c90d81e254d95eca02e030451f9f073e7f8184b66d16170429abd41101d4502808ea3018f04d280a882729f2b3f7c81497f21124ac65c3c9ea0f3d55cb9e7a21903e538dcf386922bfb264706352a72a376416917db4ddff2d04af3042629ada8899b3e8f8ad8defa432c2dc630aa338345d0ccc037c25448dd3247aa4e2a7b9afae354cf0d6a9a63a6ae2263d6dd6225f66b9ceef637740376fe0deca6cd858267437513449fe19b4e532a27b6e53e1830a846103c63e244753ce62aeb6e65d5639d3e5fbec21884bfef887c06dfc2ed1ab12449425479ef01baf8d6ba2f81b2666d6920f901ee8d0447b714ee8cac5cd41c019fabd6bc696aecba24266adba506026df2d3f66ee33202e261a8ffbf27e2fc0de9d33b34f7491c3cdbd51193736fcea50904eed055c8e449d014157d2937e71a0e5d789b23151905278e6dcc0457864f143c0f1ef5be0e483fe9f29df7f56de4ba85d95d31858b7c425f1ef521812b4aa17f60570e5e76575c642d5e79d37e3742d413db2e5623f6cc7fb77748004bf4fbfc2f449a18da9a6601c3e73ba88fb4f3fa5df3558c41e2c26c81a24d217fc6dcf385a304f74b83cbd366296c827a010db606f3f9cb8c3f9b23cc448cbb75570fe8e8171ee3c45035a5476d03b64b8813ccbec4a2a8866073ca3fddf2a2d8397506b1bba7ab4e810dd9eedd6c389fd0380711b73d38670953021b38017fa57488d30ad0d1a39b0010b02de2a19741da60dcc79ea9e83bd537dfe7101532a924e8e4ea3008968c106de5e4023e705d6cb8a111876fc7bdf984998832a4044fa73e5c003a62f74dc2da9a80c440c43c3f64f2f810707c0fb136f9ad7d75156f48ed27d27531e80fa1d95e3f9ef6f41ee1c5df1d12a05e64ce695ad44987f00f026bd4e0643b24bce521706880ab4f8961966701e99687507a48a1f41037d1a6ab43dc7e588b860b9e6525ce180dce889a5e5bdf786a1ec9983e0ffc5566157022e78a2a985ceacaae1277a5950d46659a877c36c4fb9b41ce507974a39746a1f757337c4b103e1ef8aa6d920e287fe394669126a37ca71e05c335c54d12e9c7a06347cbcd2e12ae4caabf46dd8c5e8356ccdd610996caad7fc61ba4b0d8a7a7b678ad7daba0f560b46f6751954ca541ae849e4205bc428b9e832f278d53f6da1b2c58128c060a2f8fe752c6c31b9b40291b3912de4391d1d18c1b47e23ee85a0385de6fece1c5cd3265e3d3d0330ba4019184d0143d9d5f9f3463faaf5be6d381268a5a47e0ed17dd928e921a207afbeedcd0e966e83540ac7c378fe95237552e329bf76294bfc01051a4263c6ab83c878f6612516a193125b878455fde74e0ce1b3f1c53c1352ff877a704579c30faad1d0d258e62550dc6fbe7fa9c8fd9bada0bbeab63e1516fbe92027fa002bb03c51628cb4a9aebb96aa3768493f9a7af238a6d5aab4fe9033bf01f58ce818b4d991d31a36d0afa3d6a1debcdb99d965b9d47d9784a0ab4071accc05b4bebafcec8dff026f6daa11f878d254b93107670f22909e7bb5721be03306b15a6c48cec61394e5d7a77326be2d1d9fbc1d3136d564a3a5ecb196a36fe325a07ae35e34a4695eede75fdf1aa96f8d3e91ab7e003d63791837d599419531edef5c8b1360c4196c9297b0ed2d8b1a843dceb6f1507e87fb6a65f0d318c3c0a4d4c22bef9660fb4abc18e0fb2d89acbf699fae7fa25b11048b18123d7b4d2276502d2633e8e2e61067485dec889d5ae356ef5e2dd75dd3c116a53b04a8eaad23492e844be1a18f38c815c401e848791a1a409f4c40c8c6af24fc69a44f087ec464c8dec34f7324396df2d64981667808abdb611364c0976aefc40644a6ce4361ffdd468b9fa620747cb46f45273070f7a64d80cf085044edf540fee6c1af7da1b03a04fb825baf4362b4338782b76a7bfb95d01e4c3579c4bb7c8c21a70a90c525d7ae903af558185afdee8c08e23e9cf1d4cbf2bbb93c4fa48d4d00b0f5d848474d0d0a73040943d71e4d3b32577a6118075f3aacfb0c1b088732ac255fd0ed79ca57d29ba0b5c1d0db8f760baa446b11de1a1982d76e4ec20f26ff036502b618aff2033e19deb1cc16ace039266f21737830114181a03cc2bfaddb3fbca2c11e3054bd8d4caaaa631672380407dd65fd16063ef6c993d2a05a4554ec7a32ac7256a81db9973a9211aade576d4ab9066c6422d3d8f4b2a5cbdfcdde57623c252a838355b978fd71131b961caedc356baf57eea5e7c832367bff0c59f0fa23c39c599253143538924644eb244f5db87d17b937421a88dc39484ef6dbd2ed51d8476c5834b81bb317eca2358fb426ab8100e94397f945543ebb2ef61cb4aa3a6559f44d52a3e3ab8ec077881ebc6a571a2a168a036e0ef9e973786426e61974239f2dcb2d8b526b4146f67181d497057f89cfe0be9c78c6e02a5d313c9019c6fd536a5712963c18045462eae57655257168b4d0f42f9e5d013099394a597702ac9421e97247d1e9d80cc936378d53438bc9c255a32300800464f71b6984ea28be40d44528a38b265969a0678f3028c56e807da5de25a8bb0703965e6518789d87399341a4909e4ceea1e1a9a6fcca44e8af31de81bab8d66a59d6f0a34d8559a3867fc5fd3f18b70818019eb99c8d391c35f13aec0b4ce5c64cf7d7e64334dbed2ab7d8916c25daf676f4f965f86f3db3683a6c191261aceb43eb8f727531737a03022e80bf297a9664f0b8690add13138a6e90555e4b78acdc65b6746e8baa8882a0a10e7ebc130b70072cc76a298df4d488e2e60667aaab0bc8296f55455f10a6a2458f6e62b030778059e67b4105525ee669048bf22036d699b109e3f081f35943d53f1f578fba2f7e5e5d295dafe73e74cac410f35156cd7bf81024a080e67fd09664af3c97892eb538b196e427353db2078bfd8c96f3f4fc4e574e09e82497856731d903e2764e6579f48cd65abe3a656e3062451bf2368c626e9c69475e9e3ba744df881cc8ee5cc3740525a54cab0d9a5aaf458539bd2ac594fdfdcfcd7bf0a16b25b7e7dfd7ec8d7df3e51274a8e70079b8abf5d58339c2cf381cdda1e40da554a5a958eeeac09dfc059b59be4e2c9bc65b35c0a6d9013ab9d3de0bc9bd2ce9ec409530466d0ef9cce75a9d7bc8731115156c39720e0bbfabbd2ee4d738fe3987b2c8c267d63fd279383f3c3b6df58e58072c1b91b3001aff767f8cefc75004b6208844660961f4b0e6609bc7cba5617867dd7cd55bada1cd69e63e3809fad4280e4d4bb58be6a6f57748cc52a34398725ceb293e7eced880648e7439af2107c371144801b3c2d7edcf82173140e3a772d42a5a7d4b57212f62341220bde45aa2b923c480774bd3415faccda7fb8820d70ee06d0c23888669d384b4c34499c5f5cff5b06cc42626be3281753c5ee7495341c49e487f7684327e43a6c6950f939fab60180b2cf9e85e5b42d2864705ff4e42c184349e85d9959f0a220329980bfc11d353a4bc13b1055a1b9e838a8bd3989d52ce006043de07b1c8c21666beabf98766105bc5990e73806bc19dfef82d2a4e76e6de281395e87c637e27ea30f6b443b37272a5a5873aef7e2d601cc6a886d562bb431e0da62cd7da61c5e65ddf35ccdb1d3cf49951204908aaff6b5fa59e8d71581af0643364eb98603a43c229b5f519a172ca61522096df91c0636ab6064d141badc290fcefd6929b51825d19fd51417bea3dd0d254ac5ef72e388bab74a51a96fc63f7e2499e47e938070152f22145069d708168e387299beb510e5ce2a19cfd2cff146f97858e312ce9254c3f950a94fa967cb37396e597cd87070763d0f5ac3b3877f214e8f0f8ea3ff18de031b881b287da9e11161379ba4e9a66d734910b259d585886534dc406bd67e612966071fa9ad77135454ef86641bc7e0bf40190b4c8fe27e33d3335cb5cd2e09c32cbdb88459460cc77ff6ffa833890907f777d728bb01925028e3d2c184174100e866137b6d1158d5ef0ae4f34c015696f8617f625004e869d028d6632c5f13aee47ecc633f31f8a1143e68df385b269ad5c32006543c1b855c79d247e0559972758129dbe0678a37dd1f563431e711736edf0dea14a4f9d7b27d46bffc1702b568bb9350f32465f9fd58179215b038f7a5b85899d144ee3c9ddcc5031550a70ce12b417610a6ced1c62bce574620a2630b20d02450c584e0b38c11ad45cd156aa017bedbf77933a96e1fb2afd28a27156bce1b5e8c1f90ebb1dd8e7d9bc299146115e81f5c9dc54f918ab081d81f39d0444d4388c89a86fe5adb8718462edfac0a7ba7f91a5b107a9c1449af53e35e8314e95acc6e3246cd1744803943c6b3178b9e1637cecf998724ba014588f17902b919130eb18284b9cba53a5312bcf5bab2806f91fb40ead7b33eec62163e65335b5224411e8b71b087f7143baa4e872eedbc465a80488cf7a80e74fc591819b8efbb2275d669923a382656051a4e0936bafbea3b43866b687b6a1147af39121d31a3bd221200e08e716f0f47102ff81207d03071506ee003b1ba7995839d1b118db8787278c0f42311d568976435b1328ceda526b6e3d79a0f330b809ee683af7dad098e425169227af6ea46f45241a32a5727209a4605ef934460deea6d61e021e3030684ba32fec1be29b190bc118bd420d49883824a140a92ac41d58053591b691aa5592987c030df2b33dcd5f6a6c0f1c26a48c5286c1336249bad11fdd79aa0e047b5cf486c4ad59627d2326721777a55bbd4e3270c3eab52b12ca2aab7d751259fd7ddd7284fa40535cd249cd3886ae7f943320fd7bb820db584eb6df7a18ae468043de7436d4b81e37727402086e1d110eba0a89a85873b43e2da91e8a9f0adeb61e823de62a2c6e6492ac606e4b46cf9a0831c3aa9078316bc017a5b39b91e3fe9daac8428bba097f89c3d5d7d8a0da31176cbebc9f98895d88be37ebd01d5d699b4f9d0fe3663e4df1b8b29300ae974f01aff8a72621e80dcd68029baaaca3b02dc4fcadd20a06f93a714cd109bcfd093f8066815f50ddbb60ae03925bf8dd38285e4525f63fe9b4386a8a8d2c769cdb7df3e695f86499e991b43a98ed4a94eb02eb402bc38d44955e211e80867751330dd06e16850daf2fa5c987ee565904c4d7a0a498124432750a29f4fb2e57118f486f0c65a63b75e50bb1ceea823eeb44f056ce14fcee7818c4c30e837c9f13eb6d23d11a914135f0a7db0dc0188d8ac7228bb056761616fec95731684305e182511726f6b0e783d6c8bbd7905eb61fa8323543d51305241b19e3a20399382121b6d67d1b7d18808cefb49bdc5aabfaa6cb7c5f4a2266215f750b65d280b244a99c14fd81d36af1e1392e91fd4f80600ba80ced98c8494ec335bb071bed2292cef1c87db86cf21870c10d7ef630e1856205a7c2e10a8c37508a617a461162b06eb57443476107b87002e5d0af9fe911a630cddb0ff51906fe1890eb614a914eb0e4d723d3017d14fa7bb8c67ad94343efe01091f33d4d33db7f631903ba836bdbe0f55d7f539e33de5f2de81905e14e555d5dada3ae3fefca9d269a4b618639e3736d86365db5a61dbcb044e08142c9962d6f9950f8d803d53111ecad59c36ef112e56b150bc47489da852b67d8c94cd0c9f8b7022751955e7b81c0e102152c6667c14384695f3f630e362a126208f102877d0cde9879d078414b84868b63e3e9fd79dec09a31116dc117dd9a03e9e9d93c0409246ea40438d79d6eaedf3dc375499924535cb8a0b6969f89feac312a2d1be0d50f026d8c6ee4a472f34ac0f33e1f8e578f3c7a9d67de147f079d388c2e3eb4abeee2b30662470bcc5eb9d38a8f2af7707548716813254d3c82f395e7f033923617e23db40517dcd97e9fe86390dad44782faa8bb2edfed90426f6c45443a33e9db9342c185ae6f5c9343d2d747bee07413d159c33072cfe53b96dd52f94b3af457c543f0ed2d8c14af2f6fb1e6de620b732af7e631c2fa86751369151427ae0191159f7c7a1f35313907c6fa5e082061fa2f6e967d09afac08cb96c683337067a3e17ebe5382351d923957135d54f86582a6a42b9e0fb86688cfe411b007ccaca682d3c740d5d8d76dc788f1ecd8e2a583cb1427a6d125706c0996f5ed60effca0d618935dd128a8cc5323d95fc7d13284c08422f206f8a2ac7b72d20ebd1e7c6d059c2161b4257e9396f70ae418dc085b0232bdcffe596799a3f00bc1456d2c5509f77482d4f5f794e9dcf60da627f47ed91794a9c2c8d3a9df46cba1973674062678735f722e17c42dce130a0fde13507060f3630a6b29544d8206d86370afb217975238818489c476d96feb29e8643be73d9624746de06b67ac6f39bd0e6383bad0f7fca649671904d61493afbce1e29cfd350b1c6d715db7b6060e9e2c10b1791d362c7d40dad9db00d0dc7f2a230c2af4325cd2feea11e0f61e77d59ac1e04cf13b26f63931b50daae90b141acf860c25873cd6f929fa0f6d8e8129daf05de0e6ef2c3e072d0062fe8c08c1528ac222b204d90c3e321a8ed6874e0214ec131189919f2c5afc007e192ea09380119e278dfc644516cd8d93001abf0da611a2bf9001699d4208af3dff804f90cd88c61241bddb2626f5d571e53f220217bc2d430d13579899a91f4981075c7645d6f2e983532a64b326969136482af8be553de4bbc711619309e3bcb033dbd634a337e77df628cd838704ec301e091e307ab4bf91aab3c0c74a020bae59872f99bef14b43b6c602724c58d87043b6172691809e2052801e58817601612619e85b29ed9ea1f35d96c771115d010a82275e5b2824ea8bbb0b47441d520edf9a21431fdefe547b320fc9db922f9a29b4d2ff7d9e7b9381101d8a3dc06e5ea865946b74e5f1caff2dfd1238e27f1314e23d89c1e7223b1968427910c02a13431002eee61cae8d23c9015e593ea1d73838ea59b7116c90f59a5a007da9882c85ac711311b32e9519302c0fd20749fe12cb39b97b4820c2c00934f0d1980cf4955b386f7e1aa913594ba4dc15cb65380820858489863701173b442da822e03fc2084933a71f7e2d001dde561b33aa001a21310e47a93333f92ae14ba65ac8be05617e903aff8c10a08f2b4f6ede7c8de504b68b8e3523fba114039ab503b5f7059b0f9389a62ba7bc61d5b81c3e33f428b94be3d9ef93a726e0142612b2d1e839d17890dea6ab7e913d3fdd8a900d61d6dc8d0b48e3d4fd98fd16bce554bbae9b65fbf8efc89bb6645e0b8209ff27f151b15715f223f8720158c675e44e9554b69aba54e1caec9b167aa2b86b7ef3377c81ed883227e4ea043c0588aa9ba27b59d49058edf21200074cc928bb0da279ddb948439534cac70037a2079c374ff34aca23e493847a200d64a714c8ab23147366e13b6185cba7647a096ec1ebb7a0b6eb96983c5b94115c5dee164b8636781d335211a699d1f4b396b6a960bc9146198cc94e29b3aba16d7d74038fc02d32864388fd158f42af3a24606e1e3af318d09ffc37f96c372069b4692efa9e00c6826b43f6546e5f5528db4a7daa22198412df00663f1f30891539589f08ea70f97e57042d7ec3a49d61c914d78df3127a6ebc0030f1ad918c3794b570a627760c045cf88585e9ac7f5e51cfbee18bc9a50c8c3dc8a392ccd425bc27c83a8fbfa9175aed1f10686301a8af789ff66498396c27963f79056820e32e2f8aa150fdbc9c007f5259988a438e4ed82c59aed8b637401c17b04cb1614b1e329698126932a01d0d0785e3621bd655d8708b9402d60f7cdefc1add6c68807406977c99c99c11c1ff01cf5ff375dc18000083c7da03a4b833285834cdf427c05a34d4809595de38942ab6990b198413248aff4a6dab2cf758fadc3fa4bb0383d1c52c6f30752bf448eeaecff93f6a3580a22826ac7ef50ff9f96614a5614de7a7d6ccfc58eaac8e2c65256a8950c2a6a567d4dfa96daff3fc089b286d7676dce01aabbc01f0fe1a87fa0c7781c5583b4e6888d656340c3bf169c128c74f30eff64f9fa55f00dcac8672b02e68281c500ea1a7b8e8bf963f65bedc61634d1033f10a348b5a7ef8aa82008cfbe131cfbe6b3d0c87f8974d85352f16c3e055d08d0215becd700758cdb523adf2c2701d72e40ffbb1b054afb45cd19de6866584403c121cd59cd2ede6393e486b8327624d340099295aa02977318cc1247408036b9cc04ea06d76353f5aa624d4aa6f43e86d09e0ec514299ecb3b154d63970774502d32085891310987c5a8b54091ad4aed345ab4f5e1b211e7705f351dc9c93fdd8ebf4b266d834bdd0eb6b97af2ad61047e834941a8d8e93969885ac1e8bbcb057d3e20060d389e94b03964c1975a8eca07fb8330307e7939854ccb46b46f878382fa066e348c5ba77e1d3c9059473668d09945585a94126f5378b94c2607feff8aad81fb0b732c8f566dcb097d3bed70f74e7b7e93e62e00caf5a2f63918b607a9124055056ad3719227613442aae6c1ac09917c5845e411d0ff296c98e7b4ff8a817820de9dc36b299e812df0456bce7f09dc08884ac94baab0bb88da3033f8d6a1d57566ae5ed9e8d6241189414c1f0a0fb947683abddd7530e0331b71d6808b86d4836fbabd23ab7a6a26c89eef4e9feafb5b76df8f11f27b4ea6fd1e95d6df9921ff7d359560249ba65aeb377d40e1505dd816320b927cf7818a5544b0f173eea9052ad097afdcb467a3a4940fce35073291ad1dc07aedace641530091870211026f1769fbda296cc162d2844007dcf8f66a56a490f058cb654005ecd3f08d4a542ce85556ddfc15622ed7111b263e3791dff7e8106e8998f47e09a75e7503fe01a83c71a8ae7bf07c4def77982f441e5d6987e4581bc50cd6f9cdb0906da3edfbac348c9c31e3c063ec0077fa823ca1fa3d328238040b2a7f49ed28a0fdf07f29e40f8b2c8289f87f252ba43c5fb2b82b703699948d862b4cbad2d94ccf792a194bf831039a4368831b6283fca9c8f328605f53f3149326504d88b7397a0eb719cb464d2030309a0f371c7a1f972196444908c06d6414d44e3567f4c52136fb43b833db917c6894b3f7709b8d29e4fc4f84c41957320b79d880c657225decb2616df20b5d17446f0004e78cdab5e04ce886c1d16339f17d268f3479d2436560274dae73aed87dad1db40dd5a89913b1d9e10de492dff7c627aa7e50ac0b828244dfdbd71e6cc5634dd49c7f6bd137e7e6c5425d4678f0dd165e38e0800bae3595cc40a4e79c0d2ba1907231ec2d0d4e7208e408cfc2e227988b4b81cd023cbecc8676ea7afb7a582c40b9d8115a7a0dd66dbed74503381170a52e85373cd7e06e134397f8245b0307759b62302a8dc03629ee437e18e00ce462806890f803dccef9b4e15ea1315bdcb32f1a2f76c63987078cb845e8632cc79d890ca5a004514fa70fa0b60c1ac56944e0d4c0f47aba1c5d983a1490111b80644dfe4e54694fa8e698173f2b8fb8e90f15137c46323bf4fec17a3bcec20a77560b9c9dcccf3773c72959c32a55ba89804f9256a4130be08d0947af520b3cdf209058831766695257e61842d25ef906843a5e005052e0630e9b4cd46257fa12bc01c3cdef0e1438f3d4cf7949adb2921b153af828e6dea08ca7d0721dd13a6e1e8380e2479994c22f4efb911f2426275089455a0115fe4e7bcb6c300b6d225d83bc9a09181e421dd4ab267fa196b13348d9b239088a73389a0283c8fcdb7cbacb62bde9ee5addb53eeaeebb87a4e1b783cbc8c91b648a300f4334782c8464052a82a7a924cc2416095e8060a9e684118e05670b2b027a7c2b813806c9aaaeec7073b54a8686a7dfe5128a1211dccf65fab620c07a082ccf9068253d1ca7e61261ba57de135134367b0dc2563f8798906846e9fb676d57538fedac06932058cfd4c70735e5b7c1265935bdd34ae1d111211c07900393a10c577652c84f4449c3726a8ae0139692e28ec7a03c3601bf182ce47df9d24afb98ac1f50ff6f4e077c6407aaba826e9cc034c8c0deeba7eb6650b4eb39715caab1f02aa11a0c116b2b26b1bd120d6b85393f697bf57cea3bf7a6747bffa038c377496eef0afe847f158be8677c8614f9e860f834153fd051843e9aeea04ec84e4a9c63ac190a0ef0e000c4be65214639f73d3fd42b6ae967551ff73b0754e2b86b7cd6079e354cb4922b1a1763efd2a103845512a567f47931e1c201d7e0cd654b987bafcf8a5d403c6c7998ff8682986db7b8efd1d609d4960aa5527a98d870e00d1f90fb3a478961d6b5f6221338ab676794c20bcdc3b9ec847c7547e4250aa248757578457cbc36762b22172f6dad56bedade47298df92ba5c0793dab70cee9c33468f218ae1cc1090bb75a4cda17689b922b5922e29323a091385e072a9cd9f7462483d4cad701e74a015b59cebbf5c1ac30797ebab63dda15bc74cb6a76048a3727f7d4482cb7f2aa13d8dea084d5f0758ab881ad7051607878e9d4e6340a00407f52f0ba147a00689abaf2f54d8117ee5cc5032730f69b70c947270366e7fcb8b0f8372aa611594e44b4e1c1e8dc8b83d1da200d264af0304857ce2cf8e524a3a01c4448700b59647caab894b16d8a41faf3d944507d55aae001e1677222e27a8d219d7940206ffe0c93dc69220efe7929f4ce30857375710f826a4a9a5e109a0316c39dc4acf35dc897d9dbbc3315ad4dfc62d686b6f8863eec3928479b2945d8f427eae172fb5074d30692b6c8d081ed80dd77c3e81695c67b6f8ebbe2ffae14fd3a474597eb4eded7de3869a3f62d5b0c849ebe9f16da41e806e989262d4bfc1195563aab319c4023100d2b0f0572be55304d9adf39f512cb33e4b7d9266f6fe2edcdf98b34314948a182dc1ca7d4bb30e63ccb40072e7efc40b5af910fa114c2a4ac717b3f9252b537cfbb4b1c7e43a330446e8988bc01a26423fcc7ed78caad0af2ec6f79517734a286c31136468ca82305d6918785706cfc0675182f798566561ff9663a97d0a503acc0816f559cecc28271354a7a0cb6f8968b9deed4987b178c52ecd3b69cc14951faf9c733a6f0e4237934e65faa558e15c843a76561ecc7ad3c9310bbd3d78549eaa9b5fcfc1bce204887195fb4420065310e7d2cad364f4a42615c3df3153726c2288504b86ac698e26088f940a0d6386bfcb8ed799f1a9badf90434ab70c678a9ecc895dc1a74d32433b73484a47e5361b1cc5959231d1a1f9d8860a923b66c6300bc93dd9ce22a070c3c216d5130fbdf771614b233ec22888abe5f7502375e88dc3e61758bdce8988d91d382ef6028b34a3ff7636e65d18abdb220f87af86b6c1dd34ec828c42ed164c8bf6c18e15f707996b01c5cb437c37650788f35a759dc69834893675a40f5406946c33949690b9b51576ce5fd2be442be78ec3113dce693fde7d0a121a1f75b4a74b7ef4c84277ccc0cde7efdbda6da556ca43363b6be8bb055c493e325d6319ef7c5b34417ffababa376001957e0706d2907d56671ac817cb863dcea0681d092642b28895cc48ae18ff44d34ac9844fd55a7bce1d519284b5937c59a5a85a4815c3b67d4a36f7c3ee74fd25e36bbc7257bc141bd3020fa43fe9df975bf5c6dbd4640e71648615bb10558dd4c9f2f074ea6a743eeb560d54c0fbd7b805ec7287866851c89d6bc46cae61e717674b5a309a6e54563a41027215cbcdb2d745ef33f6602960bac95d43614e9d25a78e1d0821fe7aa8a90e1696f3a40e4cc3d6c19b471ef3aabe59be403b4f842aad2cc31350159eddd30aa0ad9587da706a59b902797585127890a89a787abf54c2ae8f584a93bc70a1cd692a24d959c1d192cdcbd2cbaa4be406a2f956f140522164852b0640c494b28a8ae1409e4f9a594bb46128888c52e9024e2106beed0d2ba001f9ac43c8e41df30978528abe1f8917591eed0f68eb3719e7f6fc59c020458ffd9189320bb30196486061102a27602017893cded05cc0be201bcd91202730873d152f8fbfd805faee450f20392c7e54a43a3c86114d3ee0310c44322790f004f5104f9ee2217fbe0fa7462ea9bbefeda479879cd831ed4c7413aab914c52ff743c333f48dbd6f4d8e631b9051a535cc7f62973e17b25533219c1009374cedb8d0b303c779beaf8d1a06345ba14d48c5d1318c04006cdc4bfd86e29d4a2fce9d62d66921e4bad233fab4bd8a1962015f612c3872c048dc7e47cc8803f1e4504a6ee197d6ee16509020ef9978f276f4dffca10731219b04a0654fd8fbfeeceadbdd282a404715b76cb7a0a01e2e8f395b9bb07f3da27bfe42fb086f9abd4d7249b2f3f619b55efe2b768b42fadb2ca260ba3c41ab1f3b1204540ff222260e75ea295a1bb097ac604332a7516f5a5d0c883f201cfa7f1c55387a30a08ced64ae1048b1abc79e57ab27685c33bb1b249536501f8257e4ca1783d9bf0136615b4463bc6dcefc95ca9572bd4faf2d02969d0e54bd098060607a9964e403894ffdeed03b8b5bbc2776aa9636c89a899030c2b8e661241ce65c30e8c12c780c69d6740725b08d2ce86d4bbf7d70df94124180a7ab63de061562bae93b3f08c86bf8004f760c0d712b0bd348c421c193f65a02eea423db46bd845281e1126eb40ee93bb6c0c21ba2137d9dc9bd8ca06c46c191d9ac1ccfb36e601dc564a08167fbac8f01168c176017c41d9d5504eb3261f9701ec8b11b9763240a5afcf3ed24ac7830c264791747a662dd40a149d25ead66aa1868d08f76c1fd2f9fd230235cbc0c6280e4a6f085b24a18140744a73e6c784a91de293390ffc857b0fe416ac0d97bfa67e0ce224ccb2ffe41025f23bd4d7091465a4bb2ce3848be3ec80a4f029276ae21b2ce384e385be85e0bd781908643a1d0495f05591da41f6382210a9e59cdc29fd0ecb9324d27fb1591b95a74326df2c979fd019b841ab00a6373b790a70a2d3cfb6c9f9c39fe92ac0560cf76c31d698bbbecd930a8de942201c803fc4b947e18f0791618b8688cd5fa7c486c815a8fc63ef684fb0e65e40fec10cc0a93c25ecf6b25aac286dc7e9d225c3a610df1085303c0bdf488dfae1beacefb3b8572a2af86decfdd14de69017edac3eb58d3e56b2e0968242a29f34c5c1054d9c86e0b3a05a4a745ab871dc65eec30548c29f37d3a9eded723eab5c14b83c1994b748ffe82b4cd24994f7e4d088ac134c40eb5db92dfd7d9b30c806397e22a0b00c648ce220f426cc53838dc0140955f9ad949cfe7e8a564060e4f5101b2f33565e323c649526e53e0e7f33e111e98cc811524a7f31001d89977b2f9702e454170d60221bb11aafa4b478e3fc1b1f20d63012521f1b9a46084ed4b6869174c4254673f0f3ce19fabf5cac71866f88c1f0cd838ed77df9a7688f1aa2825d08aecbb88a305fc1356fba7ea413c16a88d69bfe2d8da2d8b49e227fa7f58ea8593e2d8d7abca2cd4ac57a75a863e931e80b6e27f2e45f6e36e031b43fc96591fc03e274a2ca60cf1e18d8bc84b35e8f4c05c715fe1b7e1e4ea4c9bf4ab28bd19957e3774b44cdbdbcfaaafa706a280c39247fa074b2231fd0b1c7bec82e0d7a4f2dfd137c023b78b9af05bfdc289267433488a767579d920f307c8af71388ea289e394dd84038c55c4d35df6cbcbb6320b90c4c01a99fd8ca2eae3812cd12eeb349e38141fa10fa5bfcb966353590ac58e617d830c2ff33df2a335714678c68c0a934cdc90fb114faa332d2b1b2069b94add29c204a622402aad2872ffe2f082a0b277d7255b9f46ed2c4876c7a3cc8a55419ddcb79bbddd4982d6fc27e524ee7849dcede6c9fee74c1cb949659659c3a2492958baff6f7afd404bd4c5b9684481041ce9b3c5b64d4bc7cf3bd7c9baaa0893db63bd5389f89d320478426c1c7c867830708a6024713a6f0effea910135bab0cfee24f849cafd53a3609cfa54505eae5c367395be5efc789abac8c43f97501b7f047c66a01444693613f7cfaab7ddbf4c74eec7a5573304b915f632f4e3edfdbdae12045f6757cd1be30d420bf00af52f7297754bc4903cee36748d8e29036f7f6c6535048bb51a37deba223adf786fb836df54c1fd7a1a6a445b1bf13aea4afc4e3f29ab48a228aa2b0a85f47f915991ace66e675926a71dc3e917b673ab781c04f647a4984e3ed0f7694960be5f618aa76eb2b65f6dc2a5dd441af9b7886062489023af57d695a30ccc9d31caa9eb24aea076078e4190033e3a32358824546ad8e147fde6218cba89a4b07436655cd4843f48ac79a0f3d793f5d27bd44c3f3952be6b7799b32413529d2504fff7cc666c805226e31cb5f719a888808913249990243073c06a8065135aa2cea46459545455127516551516551519f5f8e3b6b5967b97b39dbb9579d057c5e2a7a8fd423fb5eb6b7c83d72a92e955417dd7ae416a965bd549f3dd3cc912707b25faa7a54e41ea947cd75fd06e66a04fce72a0bc6f0fd6cad20e79dc95dd77ddd64baf73aefc57ac56aebbaae7b5c4bdafded4befdea8bbf73aee22b595d864be72909989f2c1a48a0ff3c4d94fa326cbfe6f267b192f175e5e6cb4b24c4122861393e9dbee8811f7791aa1ac2731a1787bd202864f5d1fe879f714a5f661afbb634f3b7c12c0050457169413d0e0a4469bb32a58503db0825c6f1e40cf7a6d860e0f0951da94f16a2284469a6979e801458c0f2a3661fcde33b7e3cae4176ec02e37f0c00afbb245c6138e206a8c95d52913a373bb608ec455c2cbfd0de37f62cbff259ce37717cb2f7c84388695294e8c7ccd30ad0c5a9e9cc140474c49d982904fa8fcb7d66ebd2092adb597bbf7dea772e64db642f85f08ccbd356348aee5f38d7cde784657e4c2eeeb408fc80db3ba2bccc1b176c2fb769872c48bdb124266c703358264dd90c3142a191840523646cc99202da26e57da1fa0bd617ceb72e2439d6fedc4ed46b224856a59522a5035215b96f950e78bd36e5f6d378c6f5c528e1de99d6af14085caff52cc7b47ea6aadb55a3af4968f2d2a322f8071f143ad8716925455b14a339583697d4c81f3a6872d75d440b1dd30aefdad918f87b815bad6bef7423a3eac3b46b660d851b5b27c5946b4b297db4aab4b087ba089dc61dc57bdaf592e8020498a35f0e9653a55871a4949ab481dea7c7ad97b8ab5aa3267a5858378ad15ce36d27ca8b37b6855c6c90698c5c1915da77a0483e77aded3ee69f5aae727b0a8e5b5e3d2b994bd3fa19617ac96974baed9ff6c372858d4f2be7fe57983ef8b592cb338a11e757f7fc9769d521dc55a5e7be27c3f02f5a8f30de1be05ee8b70bf03f755dc138b591c4fe2020b82802a1871a3404408db8f4f0fe6d9a98921f8791d77ad57ea2684fe51cb7de3077588be3f6111b9afb640c936f7950f2fbcdbe1083130a19494e45ee77eafdbac1a9f10d1b5d6fa759b1583038009ae544a4abad65abf61c109c920f26fb79b23956f46a29b51595650d1de44a9949434e6d6c4871f80b0ea614e999e2daeaf165aea54a1c18c841839555a3af870f143ea4d171c4f6d68b871440b9a0bc24c39f3a2c5579498171944382383903367c0b0e9a18c0544494665a94314106b884c0141a3028d10d04ea08317b0155890414c10183332a8c440752959334308ae242fa0c112c3058f2f406122be7aa8b171444c8e18657094a1b961b382142b2d159828959292be14593227cc981fd0d09860c6922d506470e3820b06af115e34f0c6852b5b9294a1816556283262c0aef0d8d284ebd6a5c7d820e6450bb31c60d0a109c7949cb42b5571bca26051c0a527089d333ab42922d602069b3935b499e3c5cb1b2922b6987842e34a122a41f860e3c214b31a6e643d9192e14908a224a3b2d442e483143a436ef4c022864d1d34176780c09042112c26d766a2544a4acaa2448c863a51969c917246d6a4ce8b3126694a66f8019be3421b395f6c2853a40502e41042a4073a34b0cc30e2f315021b1a35c470c38c5610176ec0bed010030d2e565ee8214a322a4b2c5d597ac821090e5a375c71597ca8392b0344abc714f6ea3857309e5ec4a113f6038e8da733aa205f6af4e0214a322acb2b36a8dec091c2440dd89916a81770e8414b132c2c1d77c744a99494644549888e2f1739a88499a10e14b33144be14f902e546adca0792aca0a32a05ae272c0a88dd90c4853545c46450e18a5d4d3a5820f3416656e6ea0b080d0e283fd850c78a1aac1937e4e280ea48122263338ce9baa02b86098e80f992a40aeb0568a2544a4adaea322be400a2c8d6101f2f5cc0480c302dd6b4b00155e7a352638b152b6572567842246b8516e29049d361ab481c4f6b034fb48a9c29e13293d66056cce878fdc801860c101d519251596655b025063651d2bcc28c7480ca0d5064f00202c48b0f9c8952292909ab005a68b2a26a4c4d416256a92a5d42a2f4d8c1c50d9b4b94645496574372e832fb7ad2468e0f1064052163ca151c196166d644a994943445ab0c12382d6099515364850e2338f4e011862386328d131995a5150a6578b0194283485958120c5d6cc8a0a225cc97d29192c6aa09a5a4a405749a1b407cd8603386c895a0363dbca8b9d2029419404872244fac3b5091acd56b402de92b20d75a8d904c51d2c5863233aa164c25a92b2f64710ac2070fb382f001080da8b5d60ffafa1f3420024abeef03adb5d6033dfa719476d477ec2b55fa75118aced6eba2be3184b9a70afb0b099555fc760b7ada4173b5a867f0296611db68074da5c5194ce9d7f768079dd522d7b7b54b3676a0149ffd3f3b2936d75d446b55f3e5eacbfbc97d6555b6c38004e4bbc38545c10716168c6059018d19f3c6890336a48b94b0196ac0dc5747bc6c49a1f12b2bf68acbaf8e30c95dee2b24308a707f7756574e32f6aee47e7101360603386c39134c3e1c237a0b1718313652c8fc70a4b7605892a686b3ae2e361f577e7c9937ec072e4d60b051432b8d0b2ae430f794264bf26971b521a81eb1b02cec9430a467d77ab3cd5eb33739d21c5aea3ae8d7327d2fa37042a48bdaeb23144e8674d9b906f1e447922635729071316b2d77ad928d6b94049e580e5c3b17b34efc3928b1b2b25cfdfd562b9742cbfe6e295ae5f2773a859696e6156cca6d55f6da09da619614bb9a07dee7d1f7c44428361bf703e83ebc1485cd52afd5a9fda8573be7ecbdb47a3d99622b37c85c65ffebcf8d5e03b61fba73f9cb61a55d7d87fb2e9f25c81c89bb88dedc5d14763626f8b489b5a4b72ea2b6ff20b19192aabc22cfd3f56800f936805cffc6fd09804c4fa0f327d30fb9a7289ea480e2c98b1e1a40be9745ee2ee2be769faba1b0d96c3714446c680d3aa320b2a4cbcedcdf8e044ad156997b2f779923bf2d224b726dcab622617146d89636118ff6787f8e081bb4baf627fb601eabd102652a2a92f7f54950e1435ce77d6028d6c4aee600145c0e39d1d66c2379a047640fb4f79d3d99ac989a963c47e4b3cb28eebeab9db84f82772b4850e14e9240aae8b2237a86668f315d763e3b24b387971e7222de87be22cc9dcf2e7b0098ed93d08935109ab28a1515899af5e4c99424f2f4a688599cb862810cd4a1fa544b586651cbda616b727daa058bf9ec3011ea51d74e78ed43649b4ff6b12753ef57d423db573daae44aaea843b5bebd2aae8400424552e1bee4ec321edce3f3630b01922a94e0b3c344c8403deaf1117f445b881a084dd9690d84a6dafd80ec77be1fde7befbdf7fa8849f9fef97119f5d0fdb176c2c19d9329771f7a0d057dc0974b3859d45050ff30840abf2afc92277572b2491a5a7cc8f46df3d44e505b432166af8b68d36fc7fe97475015f5c8ff866b2dfb00456af3ecb20e3a9f54069d5cbfe90f4e3200480b5db625190004486bd09972b98be8532b0a5a84d4a8966dc24922b6442d9f2c77ed3ff79f9f9b56f0b195159642132c30cc058ce1e2c1061216a68499fd458e9227334ae698b1616610e548970d4daaf090c6ec6f2b339adb96806227d34f8133e521f7081ea15a9bf654a15a624f4bc854a696d2205aec7b41fededfae39afd239932c0e87cec9e501080f9e223804676642cc6680f202356e99499b4185365f6518c76d1370b8860391715b8dd68f3add950f2fecd79b1862c8582187206239e498a992d63596868d99a4f5432c8bb9afb8c0904f2c96c1dd4e9fa822b5d9a78912b9bee5c66e9039cbfe5df34ba95968bffc2423a32633dbb4f5657466ad00f597d199fe4d26ad9d08ab0522f0819295053b8104152288652440299a2cd36fb24c49b1968dd6b4fa08f2bd611f8126d0a6e743f69bcc6fd3df7ecf12dc264776e5c074aad5bf84b9adbca66c0678fcc36b733ff5ebefb51429ea8f70496cc991e6ae3502da76519d81d4f4ed15a95cff24caf56b50ae6d65e548ae65f914adfdd37a3677cddaf7b6f9fe08f753dcf7ef5aeb2ecffbb6f60014f27d3b8e70dfffdeafb51436772f829aa2520dea739576307697742a6cf319942dad98c4dc8cb029e0cf59794165ee2dae47dc736f53d41fc19298aa083d10737751d8b5eb8e45416b0e69757bbb1e6fe8be5ca781c2c3dbe251e4d31295a6050b51467278317d4ddad49a265a358228a1b0d0ed70aa5b2fbcb00525052e0e5aaf8be791622db1f584fc12ddb67e18f120cebb09a1d9829242e6b8a6292d244c989c80c80e551d9cc9418a0a071826782222ba09195d2718046170bf2cf7257b05d95da8fcb7cd3996adda3f4b77ff33b94a0f3c28f4e8cae2e6cc881b30a6ff94d51597ac312241986e8f5060b3d6ddadaae4c84e417677eb5fe4e410b9adaa387137bac4babac2b3c1b2f3f0be285cbfe2f0c4ce5aff2771c76c39a9ebe523ae5aae56ce66fb49a2b7f38eeec0f56f77d1bddd30e69efb36ca9572ffdde5aab516bce449bfe98aa64b4534eec37bd0ae90b4b5eca6dc65e8795dc771f75aeb5e6b0b983ecdb4967dddbd2eba5942e7e152bec379f80ee7e152bee3a439a46e80fa9b72932118ad5315cdad2c85ba64fdda3402a5a0dfdfe489cbe0d3a829879d48475c06f787deb743494a9e1ce0b0845dd442f6efd7fb22d68147b8efdd6a89c3ed5bd2849a573458ee4ba2e8fc06dfbf5e9f8a38a94f61b4481a7d831bf4467b2fc7759de77d1f087a0e1c544371c9a05a56d1dbb9b45ef2e440b65c97aa7a386c54545994e5eaa2a2caa2b837afc0e2945954d246fa0d8c5b0cc1dabde41293599659d42c94bbdb20879f48c79eb1e7e6b07677ae75dfd9a9b4bfcfe7462b8beb59a49713ebb0ecee26bca3e308136893bb4f02ee7b7cb4b973e6c8f6a1ce1d228f37d371cca1f54a6ddf1df740f13e98c19bc1fb74f40c5ed2f3a130146b39dc396de60913336dca2bc42c8ab49eb6495f218e26d8bce276392f87dee77d5fbfff4ef64a8e6366aa4db66e40a3caa0a4861e6ab021869b64099df1054b147389c968c993b49a830c25971482903297983af0526265d667a26764fc306b092c93c9f6c30094c60f132819e382c0d09202bb2264ce902b2ff040c75c620a9f94796322a11cf430fb3b83ea899e2574309c42b57f76da2d57cfa9ade48a5bb675ecfc1fe8b43c027da756aad4130d2326d3253342b74c8579612e315d20314cfb4c3e47050e3038001da2c34b0793e9de10c7ac4c4766a5b40cdb9196da4bc60c31fb4bfe4c7587134031ed33d53565ac4cffa50d56309798ba18a62591d467ea62984e22a10f81d06b6b4733ac1ed40020c9a2f265522b59bc4c95c032bdbdb67465692207acafda7c716282231019255f38aa4c701ce9181812a80b66cac90447201c0cc0e30854176523052f6f4793291d1f581c2822a0f88d54871cb22d263896c032c3104aea0b6a4b1c2a1db5a7336066b833e4ecca0c79c46f6cab1a649e9821b6d2224d494fd8921d467cc46fa44e3ef484053759b6e5933a65800312303d981f4942092cf3234ff0fac6de9ac18319131cc72f330c21c2df279f53b492fc30c1d17665869672c1c28a4274e5dd365f4e38196fa4ace9a88e31404e4074e56d05a105cec77506c70670cb6a0859d8464b05918b4c151b21335f5bc2d0607edf2464c604bfc99cea22fa97831e265d6a2b737e0831992c1d3a3873d0c3887e0f66614559633259375ce667058b0c9ac97453e0627e6dd5d9f321aa140111c0f60f802a6001153e74c5ad607ffb5e053be95df81be914b9d457345e38994c0ec644e25d987e93e9589892362b3269b9483d3a69945ceb515b69a1aa47941eb1f7643a9bb22539518918195c37b63c3792a50a7f9607aab02e3db212d0460e98f97d93098eb4089dc2c222a65327fcc31bce2ab23fe5baff9ac8a0612e7dcf44a988b93191dc6f324370fc46f25c71cbf6b99a2d4882df18c2d7df6436994a0e8e6dd291fb12984ab063671426d891b408a5e2c2e78a4ea31ad057aa1d5008e0871f924bd40898265c26d803ca8db220030a98f98db60714d30b3d14a44c706cb01b605c2638028d99619541438c76a9810233c111e8abea007b7232c1d182a70ad64217ab0d9caa801940ca0b272638b619c2f7e083e49275414d95f97d93197e5ee771d7bbdf4e083533fc9d7cd24c242497984ccf87280c37824099e0083483197e5401506862e6378a3ccc90de5a8e6d02d1a74e6a823899ddc6861e626653728969411033fcc0f6b67093e50aa64b470f8d05881beab8088e4649547093e59f7c1ae53ad21a33627831bf718315ccf08ae0f8a5166eb27c3e38621ff22cdc64b9c9f2893f2a93e9274e091f6c7899948d0c2be6477afd1540c7982a51ecccb3a38632131cc3316653893125cd5ceac8984c36194cf0bf71c9a3da010582cfd4c5b0c58809b6960d3560666f4973a5cc6c2c6fe8849921277e2385e2ca226682a3111333ec3c2e52333899e01844c30c6ba2930f28b38bd98f6907b5a711d08e249a31fbb1231dc00534b12e33dba4d9cbb5cfa60cf4c149a55cf161e6002ea089993c22382e5531b22993a906c4ec11bf71a9bd4ca6a6b254617c552d31856fd0acccef991a2ccc0ce6472201a2df942979521f1b5e4ea51434e647fe88df12ad81c3ca5c62d2e0cb9593f93d13d2971acc8ff42b5342c29c98df285e31439b088e4b74460e6226531533bb7270daa8880e77cea6ac54cf72d020667e63e865861dd2f64af10e500a9cedef64eb5618e008ca1da2d64395524a29a594e2fe23f436c2fb84aeb5d6babb9bfa5ead42bad24a510843b7c26ee55284506c939a6b5c3b1154cb10ac9de80ec51aae9507dbdaf6d3de3f36211bb605dd70081bb605ddb0900ddb826ef8d6c6d2da098f760540b513d86fb805616b41147c961790a0ffb5d2cad3fd531576a5d52dfdb2145c3f5ac9930399be68c33c8e033e6dad76c2b61171aed53cf8b2ad9651701b8cfd71edc40db8be92d73188b6c1fd618ff59e9e9e9e4a5bc4ced9946f3cb992e7063225ad932eeaefc0d0349e5016f6d263b31a8be75918cbf3b48238093def0712ff724f992335664dbe54c4d4e0f9800e2e7c80c2a5c7992ce68d63058790335341b402909b4a18b2cce5a65266cc2cdf2950bcf2d57261c8063b00990396e3484fbf1b014136a0101e1e61c3b7eeae604405dd1dd4371f1217f43c89fe2e0abaf19063298a6e260a757777061f75f7526c77771ff2a4dc86bd2eb2e1b74a322abfa86a40006aa441ed447f5320022885913fd532492a2d0d830c02500e49e1a8020288eebe21c0dcccc378180f635f786ee6666ee63f789cee16bdbf954698448fcf053f3e16d8f06dcc7fb02eee75e1666e665db899d3d233bbf7de0e1232332bb22dacf8998ff9999ff91b8fe37038ee72ddedb8eb631c77ef757791babb5b27efc47ee6633ee6673ee66fbc0ec7dd3a67f7fa99d7f1b1b1338ebb7e76efbd7e76af9ff9199d0324f1311ff33147f3317737ee3e070dcde7389acf713447f331511445511445477334eb632ee73a7e2ca1856161581816465555d61b3187e3701c0e9caeb5d64aebfb06a44287e3701c0e93189d0a824ea591800382c3e18724fbe656f992bb710a25f4a16df536d473f71edcdd71083d7b761dcdd66aeba5b656a7b992fe1586dd24fd5aab19b5d5e22e6a4ba2da4aad7dda45f59dacb5d3e129775afd5ed1691208ebad95a42f4ec0dddbd76fad50a108d97e8f94d27bbffd669a4571c54fc9efaef676cbf5bbc277bfd62bcd344be83be0ca35554a9fc229d9a7e4fed37d4a5be511dcbab5d652cff330beddfecbf2343a9346b05faf75afeef54a85613702b86bbdfa73ef35527dceedbbfb2579dcdd89f45b186cb86eae4ec9096362bba4eda76ad99ad545146a4d73d9532857ea6f2f295aafffcb11629bedc752bff7de7befbdd7bbadb5e479ef6d8e2a73dd9fdc7ba2e77df7f761d82355c13b9972672fd7cfbd1dfb397ba2ad7dee769d0a2574f95eaebf1bed73635b7724cf71d408db8e3c39eab56ddc43fdb6b39772df715c0e2190cd5c0e794cb05dd45f3399151052e7396b2673045a2aacf5051716d894a9cfd4637e90999544429f08bbfd6caded635d3d0cfdaed7d9b8a4eb79180f833d8ce89dd65afb1ed7751cbdd6432c7e7dcf917ca88a22e46b3f7793c823b8c7e2ee7cd65a6bbd7ae4edeeeed65a7b3debf56ccade05de0d7fe12bd5126bc2f733a72eb25fdf49225f2364538345302c4350ac7db59df6a11244b5ec2fbd9d8eb3f5dc1e9b19a65d647978ffa941b554e369bc0cd3b7a5c1b42686a028924b4c664892f8acb5d65fbc4fadfdb3bb6d5fd2c7b2dcd0d160ea3b60eeaff40ca68fb9329fd65a1b44864513be63e4b9992c13b58ef99867c1f4cdd432987e8f25ad1daa46e2c7e3e1cec530fd92dba21a9c9cf51055c40e22194cff26001ca2c746b5e0ee9f1f1f1f63448dc41e0763b18812b504c3b3531b3142aca036692b193ecbd0c10405bd2866c803839becab9e83b11d09ee450f73de846a1e706fc953285fcf92e14b9edffd4f62857d00d44eb4e80989ae03763019d802e4d35a6badb5d65e1436b4d1ae342d8721444a42beef23d7f608ecb616fcd35a6bc16ef0aded26fd3d1636b752bdf5c8ba513e57881dd775cd74a2a9937c8e9fd3265483664235b07f6fdb2654037b0275f25127e3d9544297c7ef8ef69d8bedaf2464cedeeecf7baf5d723f3d16365b0fc3f4fd8badc7e7c75a6b6d182244adf645d44e78b5fc7c7ef5a3e0569f6af19b8dab40ecdaedee6e6b4f4fbce478dedadded1d57dcda027950ad4fb5d0a75bdde0e7597bb9cefbc4aeedf0e09ef6918090500294e69eacd4015acb62a660a6d81dbba5a5538a6f7eb90ec803cffda45117f53756eeab4cf37bb5bc896028d676bcd29da7348862b1b6c383bd525cdbe1c13d5e69cf0e0feef1f14a7d78708fcf8f57fa837b3a2bd311a8667591cd2172f8630b814550e09552d077c77691cdd671d7fef8f460e7b155da3ef9a4d5edf54a6f757b3917ed2dc1755e69675dac65752f087739e46ea6ef0179c0e5afd2066d358100b9496a029741a6a4092c2e903fcd9f974de0b2092c6e06ca9fcdba9314cc8b77cd4d3a58775abf52b5277da7f40654bf96bf1cfa97bb96a2d60c662ed36bbfc935256a59ab77c144b5ac4db95a5bd545961413eab8901ed71705e482abdc8d88c41b18285f13b82df8c4942ab3a8e463a99e7efa4daa9059d4dce20dec63429828a8f48db71435ca0cdd0c00002000531640002010108a058351928579ac7c14800d649a3c5a52341106e4c0480cc3200c62280681100481180431481167a4922a390028ea8532c05d4fa850b34c30611db9c5916f4d189850ecdd3d1866430c61d957a541306180a5d82ca4314facf5be2f8f78efe3cd5cd64b7869a3b370a13c154f2a5f4a605fac4581c6b5305544568c58edd163d13ef53bf5ccb01a7dbc97388befd6c0a193911726cb26b2f64cf2b2385ac06d5ff9aecbf9678c63a4b432dbd5171f6dd2792fe3fca5679016269e6fc9ba54aaa0aeb946598b108f7bb3a17a178687373a0153398b9a191eba498b4372617cc5498f1059409116bc034e358c2a5e8df23db25f13e9c07b9c7dcc064f461ae07b42df2a46c725abc8a22154b61b3bc5e0c9bf2f393db01f3e44823479c4cff741068ec4c2396f48573bebfeccbef408c1302e052c89b00f91023e2bd01f6e4811a3d8f30ac323660dd49e853132f859c74bf5025484ae5060654c136889165621b6aa3d687a69121695baf228ead74b2e7577eedd34546da7903799cdad7e847123ad7e163fee7d5701aac00d33c040f7a12f82cb3f06fe27e40da7f259a23f14cd3e5c2f0cd2496dfb28f4fcf4904e255131b5e7c620551d28fcf8f3c4193e99de82a8648e2688bd36d2b3f8ff63fe57d317e31ab7d1d73d4a46bfd126eaefaaef9f63c3cd730ae8933424c01b9a8e7fa4e65b0228d02f8f5023982210c2b323d193a5f4e2c9f7011ed7c7f37a69e3cb24279ee9c4613ab8cf2346c48845081163b76038d797df5b508c1046c90f7dbf1cd811aec77679fbd5d416cdff1725c37ad5d6c1657a2cf8157c4fedc34d361f57d2d420b019b04144bc2cf26a5b194799ea0dac2178e36285ac0f1ed32b8178d929cc0fcbf30a0078f57a03a9cc7c6320212fdf872c8c4fd8185405be47a6a073653b67aaf729bd1374504ee9c3716f62e2394e8d60fd8f8b786db9ee7894fff3391601a87f143031bf14a099886bbe6759641e834f464c6b6398db8e64e6ca46c957d3571144312e080e9a94f561d24803e9f685f41db3e2cd58b3cac4130e50faaab380a6cd2a43bb74dd5202eeb2443854ab3bcd87a1a2a69cd7a904f47e7415310557c94d6e74201066797bae4c4e07b942e7a5c8eb1d9ab03eb400f0094d2ba30fcc411e1c8cc284567b2823d1a6dcfa24f0da093db0b77e458623f0f35fadf8b3cd422d053d4c186d5f8b12b8e40a409f135565f3ab8c126a1efc663092dfc80cda7140b2b50d5d6c949642de28db179169e45e01461fb0d28d1c1a070682b347ac148b5d14d4cbf55a34d520f05da088c2990435038575f9d6bb2f1ee08a3550a1aaa999ff06c72ca15edf1103682d841ee8d17129def38c0c20487f57178e2fee051195375768c4e3664300ff34343346bc7da6465ce2ae8c5840647339717967d1b6b9fb0e630d04671a70d52c00d7a16b62150c01f39823c63e00795b23763f6195fa3b9a4f984c42b4aa4b6785902ac20d51700720e55a2082e233071e39109de23d7b2792c55ac411c4a76d4f3992c65874c69be32f58c48cd480027188f055c902bd53c8090fb4ef83429de006a8591a6eba3aa5306f03a76639e1e6f710b8909cdb34a00af2e571540b9ec3754985c89a8343c8265454d92752a2007a3bc34e0e952054476fdd8875be9b36f262137ca4a82b7166e90c3045b194564e38a3efa333b4680d6ef588469433e0f68a524cc7ff125a9fb1dde21f720235f2f583d8adf5dcb13ea10734fca7fac782a7ed2d40c573bb21f5d40cbc804bf4ead8a203adbc878b31c2c684205eac8a36f9062b05fe3ab829855770417f6c4fb78c126585061c4868a2c6847cbd746944e3e5f706592c2d6d123e9dec223b9b5c90d12f3bcbad4948fbdab0ed3099015d97b9137fa133881d53311c1ea257da6886f47b747a7ef4cbf00e19def5cbe50a49b97eb9b64288e9979f9d900120f6c21e96127ab3e0429b858410e2ff5af3c8a1697d0b0c9795b4fa21d4623e9336b809172b002d76b03462f5c20467accb1027ec1003854ef6491743f4a79e4f16bba1358d62cd28572677d31537ba6929a9790eef36ac85ddc6dbe6e203e737fd7228b9b283e6f7d653012824662702a6db58200309407e17f8a14fc323d88da9c4fd628466166b40c09ea7cd06daa21357c0d91f85073ed9646d224e2dd72284f5fda6d3a0e057faadeb65657d89e62091907200e9267bc3a227923de742615e77836dc8f2d98211dcb275d035d006d7755b980c1969d5a8e303f9d343e732a35fccbbd8fad8993645c0bacd18f9a03b404c674833307afb2e9a204ddfdd48e9162c1464b266f7b4218e7b88d53cd80474966fb8dfe45b2e484e288512e2f674be9f517277c927d086d0b827f395c412f88ca82f4671710ef4c0140261fc067870a9bd243de664ce5ccefd88a7bcbe37d8aff886c6c14007849eae95a0f80ef8c216dc35190b6f8ca28342b3b831d2502f89600e1d9200cbb148f33085a3eda5432c0ec2e98df07ce976c6c73129c059a4533731d94c73e870f9d0050368ffc0ecc05eb4d461c595c8fbb507dd96317aed4caf90921e5b3bcb10805b41bf9fd85f74d13205314e26077b0bdc069bd8ad855ed2d25cf8732f2f888694b4fa31ccca798103321539b44c9c1a16222fa4fd3db5239f660168cca4a2f54bd3f31ed5f4a98420916c2b53e64c31d17f45c840fc09605c37e7cd9f4a82c1ba9d7b436bf7afd96e4fe1e3587fa1bc8861f35a0918e4bcd005b502f207c5fc9312a6b207bcd4c4442a6b90bb3bccb66bd9fe0f34e1ae3a582364ba09c7e33d3dea89508b6287a6994dec1a8abc6b2210b2ff914909024281833b1b6a00966fb94c79f970a5a1df7338486165c2d14959cf453087da71ff5104cd78c28ff7c180be24bcc920782f09f6d736d1471494a0f383809fb5fda0e752df0c538627daf0ea926da37c6e147d01ae28b0dd248f431053a6175982d8cc8348966b2f0ea653beda46fad4c5b32e8a306d346c704bb1b47bc3c4042b69ef54efdc83911ebd8ca45215b76eabbe535e04fcc5ee2d48504e6bc07a76fe62d68139fc9a6873fb387644dd88836d0c728dfa3fefa75487c7b8cf28fe1e3ea599149905cff39b28791e50afeaa51b941f1ceabb08ff8f39a1ed00e979c28c46fccd4d0f5acce34854b08e01a96bb6a85225202bf6e7dae8ac08b6a81390f9b8ad63bbbf76d00f210e49d4034f9bb7a462dc46908133a332e595e5439dffe16ec9b260d0328e29420e2876562341715f3acfde37321e259b8ff79d30b89daae347066e1841888b23d71de0e3d419b1e1223407ae9bf0830ffee1bf7e9ff9af0eac81e02292e32b29caf6d950edc2f0cd1545696abecda6b6658da06e2f44968c529b2086320c144e55a111ac08643464814a573acac98eeff5603346fc97cf854bd707df66593c3895d5d70ed1416f6045075fcdd6e7ede0e474264955a070ee56dc5aaba06355abaaab528d365e3470bd85538f3e287a7d5a129a81b8e59bcb5cdad454848567a2f2e6869143846010398d58f08e9455cc54b8401a896a2817201c63c1f83e5d1835eb5752090ad19b7a8634ec17dc574a0906d185de992c524be20dfd151f27782ea84960cc32182445bf3a38d525b9a70537156028d7c32c5bb4f6d8acf5653ac1236f91892938125cca09628d9a438702a90c0b0c5237c8fde00cc148dcd475a87d213fecde64b5b9501e841f70b01b77f6246116ea323e54203c4ca88fea60256464d4792ec5ae9035bb1a7f0baf817b48c5158122bfee6e2a4dfaf55a4fbf4c386148469944ad08aacda1caeecaa36b9f27644a346f26f11d99f0c79b8534060333496f1ffb25b8e48112c4592c650a4731eb81145c5a4b49ef6586f8609344145ca63b13a3edb8b97ea6ddc416b9d005a35ebcf42ec60978097ca14afa8fb1e8f36f611e146041c1f2ca70f0353a201f6e133ff265c2ac09a69fc0d86b636b583ef626093fbff09038c9cf2f13552f21fa21a926e58de5b3172a7d06d0fef7084cf53730f2f7d7cc2a8129b567220c9883b9074a9062f8685b2f07faf5deb1b7845e0816f7d770ca221d20ef623f7fa0d15377b0b857c3e0119d72b9858e5ffdc0bc4e9c239ac3ae4945cb63298450e289e4cb1fa7162f42bca9a0e8d29f40d4511b96cf59114489e194ddc836b044bc07541a6b381e731743cbcd3512408871d0d48b3466201ec0ea615fe9e884c52e9fd20f81a92e41891b00955940d5e8e705fac415a9a662dd59b3c01bc85e3c96ec92b9ff0f601f26fc860619ebc0b36e822087c38f8d3690a0317ff986f5ce4338f30ad9f6fb469fc35ce6b075f293c54162ab379c486561ec7d13c5272dbb1faab67fd2b4a09d46f37b99f4583de366b8c09098d4a4ad6ea211d6dfdab4759a75398f158b4ae286d2824d8b377e0447a1bdd4e683a4a956ddc807d2341cae7a6b782c95d72a20c77e857f666755f88e72314f679100e39c529370419f63808ef59d583f423080a1228c2a376e07c41528c38f8cbe4a5cc5ab8a34d37fa122bafb890556623b6628d26bff9ae6c74cadced557cb5b3d291e7b1a0fd5cc2e61dc4eb6b2183d66b713d76780daa1d9b13ea7d6562321114642c23b69cb35de37cf97869f1443d844ff42f01f250dc09f0a39a7437446169eb3b6ba6d8401388d546cce6aa75a48db15b24bb015eea639078bf6938eb4b9954be7e8fb88d988cb2163f93c58b26c279a04033f20434c007529ee8eafd4919cfa2b0500a249b47c4f1d552036534ff67af803c6fed9c3cec7ebb070c552b4f69cbf9605e5c3e02c61b97cb95959e0a9964d777a502d2e3543cb3d10d51b805ddaca9dd0a338eb9ac3f014d05499fcde3dfb47126ba0ad9a055c5a1c6f9e1ee164ba6e2bed2844895869d71acc40f6b1c54c3e136d91693e86f641430e9cfd28ced6352587009c6649072db0baf5d8a013a3235a6ea0db185b952a614fb02050790d61d93cb215419a88822bdd6e89f55b42fd50aa5fd3e86fe40522de2ee08e231c80d635ce7effe8fdaeb5984facd651a46a820bcedead7013fa8221cd95326dbe5971e2e472c0ed0173feddfe3d9646460b54bc7b12c3d50013391c27c6bf2e0da493a2a151d4ce8d82bce07b88ad97ebe5427a2b77a4b787ba9712894818dfe680af0c433018c9a8b468a2a1417560c6fd382338585224a93c630cd8652447206655d4880719ce7e9b6dad31c1559fe15383869161e32f408d1ef0cfea1bed74adb29b4d1e84f34d5b00e87259b0e2e514e03c137848c4cf48ac9c067a71c4d73b35b9fadd4b30d6d46af7fa884a33e1c9a5481a08e581a7927842b6ccf9dcc17b5b9f6a499d08b1acb596b45f8a31416e8be3e6c031fce5558381e1b8d34c00dcbeb7f0f01d7b2224cd9c42d0f426530e560656a2a5398f2f5d4e2c7b2493fa3ec2a1e04ce0a5debd8c7500be3548117b0892a2d974e496b705d01c630f0b0c708af1a7ab4e2db95df092dc0a345046cd630dd2dbbb9654e29db5979ea22f2f36ccd44c1f5d43005b357e6473c3e417c8bdd60052b9647c3614c1b25bc20cf85b0217ae295a3766a85a777f3f24f2b046d88fe1e72b882d84a1a801d131866517927cfd5c4e0b6b2dd17672abb3b5811eb68df6d81e86ec453dd18344b55ce04fe9f231ecd80c10b9ae4812dc17186dc04ab4cc0990b5f3d47afb93c286ee94ef0287f93ca1453a8395c0e3069840078748e3e9522b05e7a8e821ddcdfb997004efd97f36248de03791a2aba8f366545841e2ff3100c0a6b2addc71fa2c8b6758032e5b1604612a0831b026bfdc829eb32f66f178cf7483bb4277edcd6b3395c740efb6355ca909be276829f4f32610725929ef4c60d5483d9d0887b32e48c1775a11cdde50f62ed1b0a48de38cba989c72d58af820cb42af6260dfb992730859346cff0e28686fb7a3a81b039689a6edb9fa7877e5e746865065970eb4f795503c1c8425e376c938a9ac8b213e0e236edd22d2379243f66a0bda71a82b0217eaa214337291e51765e57a8c17da9517efc99e7decffb13cca98630e459ae537eab2e13cabd6d551a3b11251d280051de88bee4ec469b05a050b36c3f3d393df6e86053bb675328ecde7b70d787ec625acfed66e356dd53e5e381f04060c5638a7ab652d3c9655a19d4809e6d74d7e35b21ec7192566bbf7367328e99ad0b0ef6c5580760bd614c659b8c53015b46a8fbe955671f56de4522f0c58d001fa0bd44a407acd8217d65a4861f1fda6e2489f00ffb594048980f3080b07260c04d46cb5c2212e8de180f972a3cc3401c1bb05eb776a0808f80e7d9df4c666c541bddee717dd7b77756bda09775fc1e1b780868544ecd4803101634e28dc7bc6dc6b602c749be51ade7ec2d4a5584d9429b7bc2bf053bc1b0272cbf3263d6877f2cf3a6705db8dee98aa6546e7dc1840cf1fe673efcb4637063399c105a097dcad36a67b6d00faf009e582dd4ca4465555ca5975960315d6a932e86df5d1b936c1c8671c460f7329855d4df4a242b3a29ce85124c754ac5a59a9047a337910f71cdaa7000ae08e55d9871c38040121eb5c8fc27ce0f5c16d547d2433feaea77018f57565e003870f144527706fe4e49daafd2d77643585a679c345cf8ed6e6b100198543345cf001ed7d445269abac9199b5662a7a465ce23f018c5933d9543300a747c30b447496294f3e48cd7f293a6641c2cd9ce36f18153f7df8d20af2419dde439bdd0d5709542b977abb45eabb15f1ef0de0bee28a72c179c8e0e828833a0189cb34f537d2029a49eb0635df2f34c9162b664bc7f2924599bfda3b06e2d7fc8f48cdfa649a029142bbebb772967da18cd28c2abf8a13349fe3c5515c7ccce48ef0b5e014011437f91525d7fc7cf6396c2854b1615efdc654382d7fcb8f0eeaad97ee5c8dfa7406d88408643d6d09d4002ad339ae16de7cb28a9f14c1a078291b43b325f8af5e28eb0908cc81b0e08fda1e207f1245cf82ba5caf010f3d213e648a511ab829448e68fb7c734b615a63990c3fdd017ae77ce6fe151c837b056b70d2c96bc9be1341a75d638513f254e8200dbbfeeb81bc105eedf54e2105af51c1182a13b2a731431696d8913ad77cb8797e0715adfa56b337f703e0aae23132590adf69d7406b8438d1077c428d5e7e86d9409356d004733706d12be3a36b9c3f9a2dfeccd2c8c383367be78956071835c2fb3242b36ea750921a8a44def6392bdbf82661324bca014030a475865a7e549e398a29773cb43f12187d5191dce48e266f471899a9085c21fbfa13e0397d2e8a3956cdb870c4cf0c5693b454148186051b9cc588d2777150d92923821e18700e97c9620b32fcef0a59cd73b9b5bc890488d7134f912908b32a5cd3903eca18ac4dd034016fb0364e81c149b96db152541c5f3b32e541b5c4c44628cc82f9fbc9780251ba2c5e9a828d235a845fa1c3fb214544fa8a0783c7a3463bd30254fa53b695ea88a1f78ce4897480755e09b60469ac93796a97c18cc9612d2a4e2fb7301588c3731f2ef3355ca22d0ce10cb99733d5903a16254008a0dbcaaed869bcc592dc6d9f25dab008862be944558e0279d440b494b3ec72301d1e8b8b445aa13e3a524f0d1da3b1d5783f12d7f0609951278de50132ead4c86fcddd3be63e0f0139388d8c6b467036e333b0385a985e138804a1f468b6f4b38cd92eaed811387add98f6c86940c9879fde10840382c01a864158a8e54333ab50b44e968e26e7264a5af6036b34d7b4446d630a1337fd7d4b221169d7d16a8d32cebd752b800afcda5118abe3a0eb2786d028e9dcb8979d6a7c00a5b5e6be430442849e60afa50ed16e7dd3663422e274f6b34dfb15ef5f017be4cab6baa048605a97a218c1e9d4566c7e8bd8fdd6daa0dbd563b05cb8a3b53161566dab16907cf3b5a79e55e281a7661899bcc03c1c9dc4ae56d8a6ff5c8e0342c51216f182b7f41fc7482670db158a43fbf52c26a28c92245e6210ead75152b28f0f5f5ae5a424b1cc1035ebe91d7c880378b40c992b353b9d0a8f20dbfaf0476cca946a091a59a6689b0d6e7109e25fdb477f45e0f5b62d2051836d6f8f728d91229be3452462cc43e8cabce52c28d0bbd0bf8dc0ec2b408308d3f3742be9415f9bd9338ab152457d8273e1549f7a70ee46d9143d7afd8ad1925c303660bf371b47fd6cf48d6d94689e79dab13317c6c915457faf9a270440665128e3c274ae25b75f0b137aa66c223834014d053bf47882a88c9938307763d1db357201a10d2b36321a77335af03373da3ec8027112c5f8d927f4972f0bd4557a837f4dcee4a4a56db1a93397b4dc8c60dbda0fc5760ddb53c90f19ac3036e7daf855b6f0e05e83ac24471fef5358e98b4a025ac967bbc4a5ffd9cf229abed2b51b99bf82a97e1d1895453440f974583031e0c0d80790e2567c07b00772e4b5c2496881901783f048c8f45a9176e0c3306dc9a2c462f306db1cc0b9b7584f93cd167b193a04a44b86d35066b1ffadc5defcd8ea2e08e5473da74b00bc2e32f8d102bc553ee983be828180ed8b98aea1cf8c6b02befd43fe9074f005cf1574da98c3bea01783ca894d632e1a750e8e766008d7b8a38808458c08bbc577e1966b209f7534ffcb7c4c73a9adeb2d572252333642f7f31c7e8a31a100a92587be760380690e542f67bba3da9fe2b18696cc407be0de84fc01d98290a4542f037345e751da60f1bac10c467508f7f3e15b066218fd08b42ed381e70ce6de490693fe6f1bdad371e6fa9e38c570e2c77cf9225eb96d368040a725235b2b1524077b1e9acbf5195e788cd4b7049c22b59320433b59bf906698a8057b8eeba2df002f065d195440ace16133eff84d3a6d2036ea7dbfd549ecf08e3cac1e866f3b3f053005c34dbd522b5599cfbd71c8a1b47ae25dd17333b9679ba3e466c026ec2122021768eae0cf34a69b775de5ef1347524e8077f62797a41abb3c2e1868a9fcc766dbb2fa850b6cc8af007e380a7e34ec4665169156e1743d43191bcef72928ead90c5a1f44a8127f27d604f1e0208063587e89c04474406b685ba3da22f8fe65b323785de17fef87a2998b30c735b410447bc953a340d1b9676c2dbd93188e45598c4208f36a3b7652ec445b220397921d0883523db77322075542bee2625580e407b00426e1634e00dfd066e24d72a09df2734271ec6819f02a5e96599906bba3c0d1e4c83dec6c7d7008091a1cd767490474f914a520ae1dde9ca587f95551583400b103eb59c25a808bf2c49ca74ee24432969009bdb246b98f8c8e2572898116d5c37856087581de8e20ea474098dc6fdc7eb0c1d5089c0dbe7dfa3890311ac14f4bb4fa4e1db61d09fe35df1374163c375ae5a0714ca5e04221f3999a69400fa26cbaefb011208ef0f8e167e156ad525c6f5b7df4f45ba6a42e4b1c620ae771040a7b1de6c966c170665517c8ad0698fcf4d8bc066486658bff39659611e781da95c8e7c55d3fe29dccfba7063650686042fb6f475689060d15286c3bcd081e08130da92d57122f2e86fdaedbe8623469244555ce57523feaa237e630f46a292cf44ce0d793ef737e896e74f5adbfb58e1caf7a97073a0e74c697c151fe9ff7f75908e83c0b629d135f8df848cae888800bf0bb6a9b08c46b4d29c088377985d274630fcba80caa94034242a908d9aa50aae33290c19914e0f8cfc327548216db21b2b74055f4eaf5f8429bdb97b7a011a94fda8ae68f3446ace604567530e8d5badd7967318b492135cf2857aaca6b60c2ebcc4d8553f467d0ef7289a574d1a378efae34d672ca43c3e095ee03e446f061e85f26c2033b1419e95734fb0fa010dd0e060c97cd76c1bf6de449e9686f26e2dc22e47c5d3c33f86c2e9e749f1dff87d4af5eb52041077877d7fd0345caee75d46c185433df353ee94d3e8bb2e2b16248e177bf2cf58514c1c97c984e3b9670e420158344a0366f926a1328335a84404e45022a54676cfcf80c35f64d20c621da6f65eb891c5320e1ea9da415d937562971bdcace516e40d38de9dccb0dd7dcdc70266a481e5b660305a84c95413e3a0364cbe209723bae6584b209134a23d7a32286747e8d47351e03393bfef9cb4d09888d9810b17f2c0b24c87fe87a74073254e49c4a9f097dbbe3d997d7f422c5b18eba1cd51d298c366f921ac8fc2a4dd209ba55361eb5141cd064161d032e707738466de8300a68b9b90a096cf028e089966c92b2f07eb818b7da9d39c27140a5bd68b77e68194b758b739a477a6e28846b9e0073443be7abbc371e705758e671f552250e8c39bf9ac39becc199c4118dd483c5aa630f1b2393081e01dd8d204a81eaa3fa217254bfea26ff53a7adae912e8ac7a6036a730624c1a08b92cfb14a7d717bd01415f38898f50be4b76ea7a3513322487f5795d955c714ba90027cc166e0dfe3bee239e734835ae9209ac005ecad23fd276428e0232b86e00788aabb899d08847850783c3e56fb972b3124cf5c819f920b9c42ab1aed7075d010f42b2d9893216957fff5ded9389db88ab4d66a2354a55dfad086475b670517e55b244b857f8b8b11b98b7ef4f0a4948ab140d01073fa34be33a74a9ef0e9778596bc086348a0705606125c84864b479e880418efafa0ea37e9f5166c8f1ce64d6b4900535e868e955c9354963c71581ea8799582f82f036321d4b9144ec99840c70c7661681f7aae31caa7ca91d787595e217f22c5f9cd2a5cf2e574fbe2998832d02dacb40fb603c6b09ef76a02c62c9aa3df43c12802fbf45efac5c895791c909231683163100d3eaa7a9ec0b0718735e53a26d65ca4739de5bd49fc324a8b71fcf9db746f5c59e4a865c2aae26bd2c2af45805785e3ea1e68944dc8a06de6aec1075036def07c2e82d68d312adf4802c8033a7412a23dc49f71b1a8022b218c3956fa45df44ccb7694c3441b11d70f954680629141b58aae04180335ce4bd1038c77e03de40be03a58d9bc0139470014d2b87c4dc86409de4ca2085bf8be55d63cbad3dbdee4f6976dd0f9388a7c1e101f5f80837dd67b9fc36d4006aebf724eabdc0ff1af799a70669022996725c9c678bd1286129ee4c898ba4e0a8df250a2fc0531bf07512ed915e36279db9a64f87a240f6f8304c040d0f666f6291c66c3ef5b365583d2c47140a3cebea5c38a91e2e56351c65d5b43c0a2b69a18c710dda2a6208986e247f067c80aab794743375a5063cf7003919e4f570b1fbc674a98ce0a5def71d9622689d6781fb81d6200c2f89e23f0eedb88e587c936fafa881240fb219289b33e956ea46acc26f5e6b33b60c2c7d4787a09f0a314815fc36978db40e37b3b310066e94d2d1a3d72fe9ad613d4e793808d16201fc2901fedffe8379f31f15d5efd3c50ffc727fed558ca2f463bd145f08aba2f4eae5943401724cabc179cbef0e9f3b76e8789d9aea41bdd9f079200a43136dad26a39a0b66c900ba4fe0a4336dd3aab01214fd314477a526dbaefff498069c399bfb1ec2db73edf558713c73e482b9f64be7c0da479810cd5f8bb005a54edef36a5d642927545e24e287d049822e8ab4e4bc7272f4250af2afa93778b9c99350e4cddbbd20f7f5f7ebf0f4fd372344662b3d012efcfdc7ad1a0c3cfe46f316ac458c22d7df19e261893b0c74b87c9186245325e8ca64e2a03907045445673f7f2f0869014e7f60c4d547c16f5dcffff29f81649e5fb75c71f4cc97864db1399b4e40cf64db6edf0e80dcc5522742492ea64f84e21388854a1836b0851cc21fa814e0a808b44fc40c4ad82402c7d6ceb431ae525df19704bb9f58bca434baae40c2c06004e9147c4ccc59307c9c9a215163dbcbbdd1c332e3c9e1c921f36cc015a0710ed7b4dd32cec80226196c7d910b9a0a592ba492c837f3389b4403555e182808769cce2cf6e0dc3d65f284e190b6844e926cebf3ed7fc256c68c37aa8efd4b1031f80ef08fe76ca91f122ebd47a9f04e05fdbedf249304f4a73f0848ba8a37edfdee892d0933282bf419f300a79640a97812323ad2515c2273c8e93ecb87be24cd769afab497e07415308bd8ebaa135c91e6f4f9169a695a63305e7dd309a5c683542cde0dd11ea0f5a693e09cb2cf326b291c718e04b01edaaa998e44e08593e134141066e19000b88dfb3e4e269b70d41c87b4606f3c019c0d6f87e46655482372c31b89ba55ccfa70a3abca7e51a567970e47f6ee7532d1fd8aa2b2e5a584edaa70d55e0dc13cdd9c8c5bd1618ed6351d6256c3d2f3017970b3fad5f27be2ac6303dbaf6b2f08c149984ea3a96931e0647d1dd81e59441f1eb5baf1061169197cbd990d217e90190f5829e00edac7ea89ea56750d2f8336ce5231550b788110eef513007c187a0813d57d329504dad60aa093c4583052f6cab65fdcc46f9bbefcfecdafdf7f097eca176cbea7aa04e56b5fe218e9b28c33a4a5bb3166f78d72619f5d73cf26d6ea6e86b53264c191ee8bbc43543e9338c056642ce28ee1e95c97ea17cbed6e2964f3660c5fd44836d091da8fe9aed57a1661f087a75791967aaef46d4598fd60824f07a88c759120f868dcee3b5808d3580655465f77edb9aa434d26c3526dde2d28d8f22e4f147ba8bf54fb66e2b85053c82c4c551c80aefd986ff8957eaeecc9eec28a234b9b700c3c3cc6735f8a7e468c0880209c4c69895d881042c8f3ae98090bca8b8a40005ea79568afbb330b8dde557ef97df842aa6c008c5e4bb43a4861c603f1943c0048f781fa8cfb47958a13e768b9b26a828db07abe6d43b4867d7a238f0bb367eedea2b356c89615a42011598428228810f145518905b87a7991e848ce93081bd09b3d7e1fdc4e4bf324307feaf4d05eb80ffcb63c6d8ec188704598d06469cf2951797ebb12f4b3e2364bca2442eae3d48db8d3179be211df8e30407dc9d85e6ca8fc4c5ad76236b35d910f0d2ab48699c48fdff72141d1c8b62e477c8029d845e17eb14b031af9d875430cae5326fdef1824dfbe843afcae4f9c6d18cce985d6ee5fa909ea97fd3c008aaf041f3a08c95032655fd0d1c5dfcdfeb2ad17a564f883c6885938c23d822e0c63e3bc4ce4788470ec5c53571116b58636e8db8ca3bf9b4dcaedb960cc22b94472a2717d8b82a5c5ae7546839be87556b705883168517b7c3f2ec60ba7eee7b06ee69bd2a4e0ca913837b21247fc21e4a29e709cb991acc38f288af5c2dd581957e8b7b1d5b23609b1262aba0563701bb6f114f78a122e93873171ce75a04b65f9b32366eb62bbe45473df5b2982328fe87b6b5597408355bf2bdf5b995559a5a997e898391ba1da6edd006ddd9a9c88f7e17ae8bea35ecaace9edfac62f8c3d2fabb516817fa0a1fe69cd7a3f104c446ce5c547cdfdc3cd5aac1e10a00c3dc08260b3d2e21c7936cb5adc26095eacef43df29861860d86dc12d75e9658d18bc5bb7b4a15768c4e974159fda47787648eda39d30fff232a0351a878565bc309d019181aa2ec4c31e2e1f7d601312a75399129ba41728b80266bd33982d326424c40e441d84306f4158b9af4d26ea3d80c8c3fcbfd7f6e06edbc453307aac720acd1be9d227bb5f57cb03aa7fd9ee1391872b77facfe0d427181786300bb5702a9b60d0178faa3d4e3356c85edee2a847c02f40766fdf38544523e9d20e063f46894751007eb39614b8cd4e9f0c8fa9612ec7f3d114a7336778a5aa8b8238bd8f20b8a49bed91f527f0639a972ddcc91917aaaf13b96fc46e8093d3ce3ce875ccb0fe6e5718c142d19b24261e48b191092d391631efe49b053b23d9ae0f628c1ed72b5c30996acfa322dcc61ab40301d4ba3b13480ca12a0464051ce9e4310e7922a6b4becec0fa7865a7bf8ace70e1771b9319f610efaf1cb000650f45a31fb43697cfb3afea59f1084e017c67ebeb981e0b46c95abe6ec3aef4b126dcd35eb4a70beb04d76b112f479f59fb5939e8a32d1618ded2dd6fa370be2420021d27aad74e15465f1b3b161c3bb80482dad8b858625de1b56a32201cbb7cb0709260661a7f923389a921d1f92d804221dbc39f1e685800c21cc83a2991ad4d62c4e50560410c94bc8e9543e1e768e9408cea55ba5efd8d9845a0e93bfd406d5d9eabbc3ccf4a364b0866747370df753221788495deb4c8e3ddc60b6721922f37b887821e87d7f7b0782a468b4ccd4f49a63d8da8ebbeac6c7085ee7f5c8f0136e9c99b9b9066cea2d790cd31c134c3fdc6e005331178223372f81ebd3953b8f174d4094511ab1f7c568fbd5c8281632b70407c0d6eca3f2e4ea60524c78eeab7e78e7923641f9542829d01ec28bddff2bf4cfc6950ef728eec599a0189c94cbcb302ff422f02a28492e805c43319967f42819981a42a1eaa593d8a874244b08b7d7a9d15e5a9507a637ac7083adea04eb02e1abaf0882cceef7f883b02322885c16f4aeb81dcfb41bac2292e8a6bae920cce8b7bffe4e6b217ab09d180c9f18b6005f5b504dcd1ec2a80cfe2ee88fe018fbb96c162f093035e9e6c3402d9d880ece72df2fdcae47875646daaf0ec48226c42dc3f669d02683d886f7a0d4f85ba6be9943b518422cba6ec821b7efa50f3315e3dbbb4d4f0048c44ac53daa88f097bb84b547bafebcc938f59ca28055dafeeef0cf13b488f01031c1bff207a446257ee56206e58197619daa316b4cd0396c0c2b18149927c35304749e03d298427d7d9911f032c0451a7354d63f4976c05928b13be2baea031cc88ec96678c2fa6f5638f599d53086f74b780a7bb957a28098700fb0fed48569d93fd357ba56dc6796054feffae06baf35efebd0a3b390b253f3401bec85783d58445bc935b4396207407f9c145621af8725235fa219a28c856a3df798d91ed831dfd6fc0b5d60aaecf280b729bee7e708f4262e554faf1880221906ddb4ac4bbdd0251f403ee75635f9961ff224163723f3811b077f73aef2f121470d77a1b0275a78d207837061ba847cb65871c7656c9708d4c5ce00b0b5db5a47d5815f561e95a50fb695e460810d909702730900b9302b3ea5a1c69b301230627ebd232928573014f15ca60825c80788fda2d6529299cf4b202ad39aa86328e895f0188158a7c5947df3b2b00cfeb9d1ff98309ed00f8e23ecef00fc4897a5e13f549ddd5e278b26ee0d6d94d160c145a9510c16007f6a01482782e4e0659d2cf667da577dd650d5364aa41ff1a77128d28557b02ec684e779342d61742f446bdce3d387cd0a96cd29e9735c397896bf359292278546f99bc40074cff54073f81ab695a1ba3d13821270c70216d2a3c2ecf62020d594b3b11170ac366e4ede16ff1860c6ab123b158d9e96eeb35bc5277e408d85d6640bd9e07b265e1064304069fa92221e30ed538d793e93b4837d4878645b997867c8d2c3dbf2c9334edac108bc3bc11b805bdae0d85a8b2b071ca1d1527d60c58d491f7718fd32f1f66fd9495fae07afb112b9b3343630f19069cdacb1bc35df753e164cced7f3bdb40e5fb8b871cb75d5f5a78b03a1ef4e2a3a358be01bef0f10981042b81155caac084fec68fee4f8ebbfdf4c9a0f3c49c5b7287e388341d55d401e9be156c9ecba989bbe0947c874dd6a9bbf6ad1da8f6edd752df8aad07a36b15aedacf1a7623c302ad57c86015bd05407f3f433d38393f1582abbb61ff13a419f279872573bf6fa918950b108bb699388e4afcc762448820023cf13125d2839c4df1a96525431d20c23c9582131ed29eb68f116a603e2fc610377d31be015fb06797fc7f3fd38ef64c30cf3996156d4c6773cf3fabc4e3eeb854989b252b44c46185157e5c13a2d7ff9e95a236cf81220baaf2a9efed2e376d73d5398a1d440aaea9e583c8d0e9b1232e0d3b10866b6eb610d8b52397f7c497278ba1393544fa38e2a3435eb3852433323d41405d465b1271dfd8f7118132c81ae71d7dbf65ea637ec86829bcb9dce17fffc3c905cd8308aed6b29dc160fb8550e2f88b7c100e23e7af8c7c95bb284a6f072e86a928d8c68471b80d9cd56ce0d9544485909a08f50015712a11852a01368bea14fda010a582a3a424caa494aa18c740248f82a6e0583908e61242aacb621f015a128449c45c9c8af83a77bfec83a3c82487042c076c7cc4567f80e25da8fa53d6d8921a6920705198cf9faecda829a0143f23d6801a88795dac461cb010b8435a0870c1e23a7b52b8d2195656fef943eaae64e05a76c055d376b5e2a7e9fc885f21cc4fa25495a6394a3a018019bf5176ae3fbc3f70b2a002fe1bb104954f9c4ba04a3204ff3cf9c568a6c73e8e3424e88e4922e4591577a8485afb9ce02dfb46a5cdb900c421bca6b67ea2f9214a22a4d11808a68832c1501641792c7f2d0e37e7fb3081d9fd4d02d1a6a86a8c430407e7f5d039ae65e97ee63d2f4b59c36d92750383a0082c39f73bbac8b336f5bfb2c7ca6dae42718e15eb57a48597943c3fb409bb82e1e5adead5114f99103e098ff78d2e3dc400dc76ad7655f6ef87403ee34394b2ea2acba9d932926440f2bb997e74fb68780b0111a67e4577e41b58619dd36ca9fa9cb36f42a244508f07aa4bb02972ac4cb9d96228b3e89b6949ac95485dccca3631c9052fe136c9d9067ef226a9d84ca222669bbc5f4f90ce8210794c8b31909910772bcdb5621ba5edbcaaccda3d500403e70ffe6f10188c682c6d02db84f05653dae2611e268dcbffdb392ba623873b93cada68e497eadda00ab354a0c84b6d32d64d1de3683bc1dac909f2128ad5b95fff963d0495236db17332e4cd65192fd56dbce4e194789fa3c39b8be24057674e3bfd53a7b9180eecbcab53f5e70ccfe66f19001c3e1860726880453c8d3a93b3422bce32d44c80c1c9f476c86af0012995ae33915e8db0fc853e61aee6af3e66058fd6e56a5e64a49d242f8e283d108139291d32eea6084566fe916157b0b421bac04ccdc54028e4c6025fc32d3e586d7fb66086074da2064e8f4804aa70587a3f7de439ef76af6fa5d82b61b9e018898d1dd28341af7ace2c3651630870ce5344d4ad7b806320f1478a846e74f886eebf54bf4a8066790acffc6311b37d2410a4302ecc64c40fb4f13675b2670252c54fd7858846872bee82aab921b9a7c6f02c019cfb40aab06fdd017554d2c10a89d0d8facfecffdbed5ce3ddd12ae058ce9804b8ee2ab88c71ad5b9c26f5f2d7f5ccd907062b05fb6e44d55f3299afed0cb6606fd2d1b5e059d762b46a07a72bf26059538cbbc66d70bac198ae22cdb8a31723e9ff2bbb6575efa486cc7a5de4899acc0571dc168f8f8d8cf55e3b86e35d43be602538fd41ae902b540c90f0c48cfa3e5058ed6f6fac70b457d5ca2659d339abd00afcff48054f797a665328fb23633ee7024f30333d3cfae7a89c9261aac119bd326a7a309cc683eb3c0ea95b0bd3d2060254d57e9ebdc0501c755d4d1a6f2d91425a3a33c9306248e53d1c4d470eafa1f47c50d590a3b0dd73c54e42de0eca97f2b95405211710cae9416ed1fd8d911128d351c9706cede09c7b126d6425e8d46491f802135fd0e8283336c9caf1f83b17e7267ef320c653ed860d94c9eac9b90fd927642445b85b7481e10768de517f69a89a22719fb154b32912356f0b69ce5f728d1856dc67186ca38612cf11881fa62e4c0ebfe5033a9a2526970c2fad9181264c8c8da7d958ae11e669f90111ddbfe3875a779ce28008de586492a860ee4d931c76a0d0d13948a804ee27614834597eee0f31fbf6c0b464b2a11e9b39c37e51a1309c64cb54b62674a16776f6aa88f6e5bf6e92dbbd43bad33330af169170981bc381a8a13179d28353105cf78ccff32db03076e02748cc88e60fde485127369b3dff5152a24db9e9c39337bc509602425b0e499a161b120090cf0cbb6486e6aafc18357c3f260ebb7a1c16801722bad0aa2a0a45273f98eee370cc764946c5679cbf6615130c8754f0ca5e4987fee8129653c9452557ce82e2558dff03ae6a91c437a79cfb21ebfb23596bdcad7e2df8969143f2352b6fa5eb97e7d06ed09de128cc9c0305ff4db6b591ec31e641b2d6236e974a10dde6ef0eff569380f7ea12260e19325d209f13d6732b7166a148a1bff601766c3a16293e44583629957613f126fddafe4172fe2c56496e2ee52f88088c345532ad8022acbf680ea713fd5124854522ea15cec810a07a9555c11fd214b92d2cdb9a80ecd1e2f07c3fbeefd7f37d7f3c9fe7ebfd793e9feffbf37c3d9ff7c7f37d3edfdfe7e3f9bebfcfe7f3797f3e9fe7e3fd7dbe9ecffbf37c3cf4d52ec4a0d6f082b533a08c0cbf4b9a9bf6d9b304027d756b769a6696efec048292a21bb0164bff769c7c1fff84625712382c3688b30f64d13940673077a897dd1067deba3b4c0db5c906aa73bddb3e6654e4e00f57d6f998b9db1d846d4ab1994b480046ce897064577ec8fb43fc2a24638e39593e3085c56de3c23910e91618b7e8a185b58755d6f638402c408177c0d19026c605840eb6123f100e7627040f3fd3c9caae713dc95e62f49726518060ad3ebc199d3e85372862e9e63387ef269f5bd928e2561ffc4174a8137d80f3a07b7444a3b4a5586519dcdc051a943bc0b9100fcdce4fa2a17a56f412964679ac20ece00a7c9529fddcbd1a319d7e0a3428ec00eb295fb09b05ab241496b8c936f9ba77acd1c683ab94451355266155ed958e88879dd0079d0f3a7347324a2286552c94a54bf199982eae4568662cfd50ea9c5246d1aad0489ed8c1e022bd825520ba542588d2079ddc419c46fb61471a4b23849bd5e076271b2ac2dc76f097e6c0688cab082c06c35f30df5b76b9887ba31c6a806275f010b1fa6b26008ecaea22d5d45ca427272a5a7133ac2e474e3a04dd1ede5b78514e4cf0964b37eda003dc079c0bfa033a926a5486401f461683ef4e6843bf438d38a297c9e52e3528ccad1ce561377a61e7f0c6e818462588280f9b3e1cc55085e08993f800b344444aaf6fcca10f4731ae246ce308e11bc14c69105aad943c00653045f41a5bfe65c48474adea960ea133107a20ba2555ac61c787357513610e3b06de95508cd4a22d04e271611483d70e42f0de6330d78683757697e38c69c3cc7d6a6a1114e1c7ae5d4801b53d0d1ae7b5b56912f5f7bd17666116f9598c6e5ac81b5acd8d4b90aa5152092849289d037d8db793dc675fb9beef5b03d01e32f3884b2fbd79a6cf084eae2d1052eb388dc3bf274ae334352806c17eab4f0dc410581159f0396814416a66fec8fb371b348dbef590fdb3f2adc635385a657198de9f5504e06f7d50add5d2db6a21c636cfaa6d69173c1a3284ef52f4e57d3324572dcf0be8a044828aaba40ce8c9ffba09a859952841479093ebb641a4faba840b337a5bb1abda94dd7e4a9247fd1bb10b1510437f1cfdc13a7a850ff2cbcd7aaf7dac868b20b26162b24265aa4921b4807decc6912db779c4e565f9570ea856a908b3b8279a9dec00c15c6c65e82fe23513fecd01d22b657c60587193f1a29270b8e5a3f8fa02232aae7a6ec2987e74f20868e6664efa4a9be1b0e672d16f8e31c5430017ffb0a6ef21744a828daab32dd9366d6cfa17901c3b6582f0abc6642a25a9ef649facb846544257ef7701791f2650216cb3269851b6a349483302759456ca58688259cc065ebb4084cd896535cacee0bb079c69edd25c0f43d4409f9cd15dc7ca2f359296547503b6efb50706ccea038a168b9d5a301616a9037e87e918d3f541a347e65e2497b0eec0740a861cce4967a5a4de044271f3afcb560f97144689a6023b57bd722773621086802734060ea7adabfc226dc5c9fb605c2bc6db4bbc78114198a50449b7dd52ee2d534a32c509de09da095ca7c867a522617cbcca74f930aba755b2a53494fb44642731881c30ba3cf015baf7ae5b4901425b77a7cb9cdcf69884717777f7184276fb18443cfc6310f5e8cb5e0142fc4951f324b1f2faa0644cc61c43a636098afea40ab79bdcfe1af7f1e5f611ff4ccb5cc55324aaff09322ee37613b9fdb2b984f5988471e2b20eddbb9f696f59d7322a9d12db8705f9d33eaeeabebf7f1ac8ffe964ed8f0c3a33c07a64ad3486d0c62149123d492859b14caf98dc9e91618f4aacaa8ceac761eb7cc2ca2554d4722ca182765baeb8dfbc8df6d0e18aae3c20250c1a10e105152680f800117aaa208425ce00c214265c71440824ce20c3678b1c302042840c14d4400941acfa73b88a03591209b415fff6344162f05971cf24c7e68a724c49263935b7bf01b24514902b2e0452a2f841233d84c05538e0409b0018b3242cd1b999e206437c70860c5e4280c28a25353d68557cb88a044f28e14491571249f860d58f0157c925c0707c74808429a8b082d513c428220a3368208315f8acfa1fe0aa060421d3c14b0a539620c6aabf45d3092bd62d048d027d42207d64b6efdab64ffd8e0a3b646fafe6dcb4bef403c8d0176f5c06cc38e87f6ccf3d0364ca939061a7fcd5de9cde9ca04fbacf48294758ff8078f033587223190949097300992c6e9bc1a37fb6493f0d74f880cf471232d54c64d8a91e99c89494dd291b66fac73f398eb2c75136fb676329b151ceccc806c81a954e89fef297d4e13f8ece9b44f18b2e3f7f0b979fca27117072476666eee9b0fb676c55e8abb5a7d3f71802694f6a50baa57420fbcbff58c681900959d567306a7aa1ed4518521534d90efb7deae89f36c91401fae42a1128e3a8fe368363b66d4eb075a0846787abb8dbc966dce69fdbccaef3fc6281ed5146833981d2a923b77b629d7a3293176c9d9ed1ddbd5b1d027b8c2261685fcf7e5cd55e304aecb1b99032aeea98b78f0311c6edfee971e6c08a721cc7b19d78f78caef297bfc6dbaf9918061ac5ff23cad107a7d692c63bad8a40a37a68950c8d9a61865aad3d9dfee5c8727400d0c07bf9f1b36d39b39c53ca29196420977b4987b48be37ea629f7f2e7b365c9fc93b9b9fbfef6f1cfafe5c6028f7bd0723a457b754a3af7a0e54cf935c8f7972f1909dcfb9c3ee7e6f59472c52db743305cf921ab460c727515cb5cc6fdc986376ae89327991c3278a0ff4e312775807ef4f33d19f6a364aa76d87fa36bc894e8e58fed7ff8d3ef57a770e8300999e2800cfbbd5abd5a99ef94a1af57fc8dae689158c6619c928957f2452bfa268fa36616b6adb0dbc74310160f7e064b6e24cfb7cf48e8bb4f4a18ae756aad9db85c84713a6cff3aee6b25cdd3f36b0ec8148b21c3feae752846efdcfe6e2764d836f44a04f6112b324ebf36eee7e4266f1514fae4eabfafbb9d88070e38481a0eeaf217a026c0e29c687c74b875a845f3f9eb6c3af4994dd87e7133a92301b77f3261693aecfe3e42aad80889eaef3a12a9c615190767092b720d070707c725e35acd070e19645fe867ba0133dc9c5c7765a7a6ec90c463df13f170fee405f195a2c9436e92927ad0a874282f0e77820f6800f3d84e3ab5cd8c2412a984230c914434f8adcf835fa7a00c3cb883394a3232322c9c68fbeed20849518fed1914652ef39df16dfbd3f5daa1dfcdc11b2c221e1b18766805cde786be5e793442dc27ad885233e7275da89c3c16b64f16895d3e723b87a9887870ad8f9030dd4830aabffb094689cdc4d4195d06a711cb62bf1a0756e42a35afd56ab55aad5ffdaae1b82a64ede9f41fca88323b3473d7e0c0610369ff9da40ec95fdfb6c2d674286fc7bc85eab6754f87fdad73fb0b4957b5b4c2ca8dd483e6a384ad5db90409415c29190756ec8dd4230ad7f88ccb5c85b7b9310edf8e754a9438f4ab567b7a1742dd6d4124013ee36e2ff611091a09754e875ceb10d8af8d6b1db6c8b5dbbfe108250c3fa81f80e3e9e4f4ab6b9a65060dca4c09cf8eab8acf0e12463a79c08deef1d84af9325bb6011e5b76421326152b2553018d44441c8be6b1952f5c5983411a3cb62b7029ec8c27a57407a5d481943204ce0ebb2573196cc32c68cb36507ae6f89633bb2492282181c4cb1147dc78bd6046bf81d8e314f5df80fd99c199eb85c32b043005d8c06e332cc00676d3a183529a00956bfb9cdcccbe31333333d32ddb383f2e08400002109834343434b2cbb3810d6c60033a6febc9e690430e39c064b191e747e8c70f110f29ea41bbb93f443d68380caa466717cd9d65ce3176b948a0aa412a470d6490e10005680f48018ac818a0480c31c01080a5c8273274f53f397166661c322520000d0680830661288000f8d0644e629599e5304100667003861980e0f742e370abd67a6957ac3227185f30c28537c2860d5b5f6a28197958498f3176b98b5d17e605a981736201a7a58586a95dccc2a16bcfae388584a60cacd830da0ded865684fb6b93a807ad48a7440ee5d5e1ef41cbf96611b607ed0596f86b251db2883a0656be4e87b2c88dbde941bbe941cb895d2fd2aa1e71b617c771cf71dcd683a643cba1e1d06e56443d683734578fd7693822b1955cef41bbe9505e6f259d121bd6b0e6a9fdfe32aefa3f9dc41cdc320ec83c79c393ccdd2c3634e73cb1588e52296570f4e01b4ad9753dface789e277baebb3310d18d9536c4a3f65891d64e6badf5661accdd2dc3c343118f69b76de3388e562929b7cdaef3e68ead8d0e2537473e89b01b1782811543b565b769635986b01b37048e0ea78db536d6822fb0e287e61076c4325d568232c8b8c0e268c11cc2deb0b1b24390ed2ae5ec3647930bf5e05b29a5b6eb3a1008e4e5e0d1a3af27254b8fbe52c2d0d014c2ce197074c87f8305963b64c33dd4ecee21cfeb3e4d2b128d462b5692489423310804da40d31b0462b922732272977451bb72e4a2c89541b8725cc291cbd2a939b1f8b96ec51070ae833df41dc1e9f6705ab95224dd7ed174e5680749c6f48ba22bdf07ebc3e96e3fddbfd2af258c3ff7f598d741911d663950047ef9cadb97ef4ccfaf75e03baf0e7cb95e6eda7e057f6f06619b5fac2dcb9bec062543e1345760b92bfe952c1d5ee1fe6009d3cf2c29c866d615e7b77c16d5c0ac56c99956357de1ea57d33dfd5a75455f7ee20fdd07448e15f561deed5f68e0d7dd40075bf4c2813ee8cb4b635795fb171a4097038d5467d6ad1ffd2cff6cc2bbf02fdcc7c5703166b8b914a6f004004300babb1b00e0f72fb8d0828d17166a9c5ce44b29bbbb594c25925d1989421ea8ab94dba6374b29c391a9b6c536679d93859b13dc219c3f7b4ef739a77333dc4cb045619bdb9cf302958a990237829473ce1a7df2b9558ed60ee48544a3154b2a995868b4b89c6af83b8a932ce5b6712c70ee552cfff49e73ce39e704450f5c07414bbc0a5d54e18b2a7c5105f63814c333868691128724c036000e45c31c95c572a74d66c3215f08955c30955a603179b0ae27d1f7cf2668acca2aecd6136d4679a88b47adeb7f43715c58588a436f36d9e6e32821d8cd55d3629dffef3994c681e3c60d1b365858aec8b1da67a12b722c191ce18db77112a94ba42e2ec64d2776f4f5456e7cce87fbe1685c8d858e971b1dd57ffa5a25879beb5d657a7e4f871e2370aeec31a245802b7b70bab81ccd559b8bb25c657a178da3d196cafad7307790b993e309c2cecefc5e42efff520377e5e869a20cb7d3613b2de77aed88cf0e5512d29eb9c48e6e207de9069637819c9221a01f3d3fe963f9b8911b417107990bfaca8d1200fde83d534814c190d07b2e3d529072433fdfc351ddebb1674beccafde1fd0ae841ef7d2fddf3f5dee3be97109801fbf33330f986483f987e7fccfa21fbf90e327744e536a784e3d9b9fdde8d8782230c9b76851537d8e692304e48d5763a914872738d5e6c283de9694a20cd4ccf26967eb3d6617397ce6feb408ab3b9369cedb5e938e9eed36927e64a64dee34e3e3949df1c1d35a34998f6d31d43579c3c5e6c6079d2d3b0fcc673fb69485f02c7d0157b2495baa7c395ac51fd8d2a7d1b6c83f9ac057315c955a55a4b24178e256138160bfe498e88547122c7e2a2e36a1714b9314697419f49187a459fe00b04640874faf4b76fdbe9708349806dc85cc5d1402f8eba619d4486fd654898fee9ea243225a5538151b04ef913e46c8daec8d1260a36d7f55c08cdd51177798bf4f9cc513e3f12c667ee2375942155db9c446e7b1bcc551cd7e4360f27bb3dc5ed07b971c23adc2e289280e9869eba28ad81756008c8dbbdfffcb82b451de8c51eb6eb03dff95ee839f49d8847089c3cdfdc895971c26e3fe81367ed34ebaee83fd761cccc8ddcc88d3c0e6e3bdc488515b98dc8942b75cc8d8553c60bee7c02c7e2589daaf1669fce7394b12bf6d8e3a96b05f0f95175d3c5446ca597d3d5a92642a6b624d2d5a90dd66109e4588e02593d1cd337220af48af43ea330aa23619e60543fc5713a6271690fe88bfffbebf9d7975d703fea8484e1c60e93c814c744862c57f18a63428fdcfe145d42861f84577084142d58fd309b40c50ac94b0df3fddd550570c14aa43a52871352d2286eff948dd70acb778e9cd3e16bdb649d129bb53d39c23acdea9adb3f65739cb18dba28ac86796715762e91a8fe26ae2c6e4f2e5aa7dba2394a98fe39e208cdaf932bfffef2d77736ce2596af3869b73f00221aa4940b02f9a643578b5d2d9a0d88a371b42b7234fa0289ea3722468f90d12566d4c5eedd4f9fdd9ce31c137000038842ef1d202473e9d7ef5e069103e6871ef4331a14205891ba6e1496f4a517a9ab566b4fa712d8511708dc41e6ceef74981f7a908886f9a1f7e6e7bd8feb2ff36203e94b9f01d297c00cb0bc7414ef20f3b7e35a1d72353d964506110fef6510bdd8407a96cf40e94def7d1de8d0c36146133916941d10e0e5c0b1381657e3aad2f7bbe733976262a1f133cde250e4ca67aee2583438d68c63891ceb0e59020545d476661086145b6c54fcfcc0078c294d86287d1ccd51fda46f3683c2d16e3fe9fbb99aab4a25920e77770578ab03634cf142ca0c9690042b786802135020c29427531499ee5e8ed975206f865037e74c40682eb1db157bcc2172c0c73ae5a058bbd0d3097231ae6732b1ee7a10f821503c5d91baa84bea88491d0ab8edbd58412fda8ece90ecabe1853b8d949ede94409fcd661dce42e036a9b0a2b75aee8162adb6035b4e5db4c8edf726ff74c8205719b9580d2cd7df5340682eb17dc51e2730ef10a9171a36061fd0917a44616d57f41975cda0dc7efa729594820b86c0c384cd0e9c40b2eaa73aae92478002095f88310512a4b86225c708c30640f4c00b276c62b0eaa738ae1a52d3821d540982144f7e5eb002b9d5b18ade5c06a96bb4e21c5d97baa88bba681112bda12e57798be42deaf296b76a9d9aa3ab6ab5f674f2a95de8b5d29c032478f2865899a32151c9336dcf9c3b0f3fb02e3f73fcfd18dd1eea65fe7ef4504f2f211fbd86f083422f02830811fde8431f440892ee87e4c821bf533fb8ffb1bd2fa07b11c73dc0b3e8e5aa417f43e845a1078142462ffad007092204f4de23a119bd28f43dc0513c2ffc34a21f3d8b5e18b401347a4fb68c51fd35f36b80b02b1b3f1b5916abd97820ecaa051a3fd3ad0efdc68bcc29ac586f6e77cff2a2cf56dec792e945977595c77d9ac75572d52307bacba6c3d66dd0733ae44427eef4f9cbb9d007faee137d6de193ab95e7fe5da5516c8691ae0609f71d580384f59a96ccaa7b20ec8ad529fe8ea263dd0346f577154dc5edf742a2d9cd2385257d2f6f3f16def4d5f8d277fa95cfe559be969f22fef91919bd8fbe85af6557ded3f854c02b1a6fe3ab014266e57dcbae462f453c6ccfe00bf7bd70a01f2161609f7b22eea5c8c7e8abbf1ce5546e7ffd81155b36baa2bf10203a139703fda5c48a2d13cdbf115ac9481df30f70e51740eed854ae04a3fa81f08e0df41b8e7125580502d06fbaab021025fcc5a87e7e10b0cf0defe032644c73188cea377911b77f24cce83ac8350933c507b5d9032bb62c0acbc333399f4d587fd199d4b1e3f6bb0e8f1d7d8b5ec65e5628e38c24b6706d3184a5040ea63cb1f303204899a2171b44effd0da30f3d7fde957d52a2a48346eccfb4f7a465ae6a1ecbe32f22cc78e1a035050d665064d5ef3caeb2001038b8c21763e4f8008955bfbf5c3544052e60421456c06242a062d531393892832a88a08322e0acfa3def65f7a3bf01f41e2844f4a01f814290d0881ef44246dfbd5cbdf08f7ec84af4bd30f86243f7a2a7e95ef4fc223008120669403f0283881ef441462dfada67f44d1e1baae197b7a40ef71ffff11fffc9d1ddadeb443ca688c67bf9e4aac69f9e854fae3cff78fb5e7c809e027205fa1b1c8a18445c61257aaf5b3eb9aaf1c9d5e9932b978f453c7cd4a860086be5599e4d5ffa4daef8e5233d0b9ffd1a9fe94f5fe95dbe956ff9589ec6c72bfac96b835cd590defe4cd7805ef433ed23042b9966b7e5caf4c955e9932bd22757f6932bd0ca3784c52c4c28acd8b21ca66ffdc5f4e48face5100298aed30006dcdd3b77e7af064742441657b846402c24f57f10d5509fc57f9950587ffe17ee875c4e031d561cb68e431b03913f4080700d105771ad56f399bffce518987dddb33c8d107fcc4dea237fe73e97f1f08c356cb00ee7f0ab48872c20356ad4b031457f79139e841be12349f8087fbd7c7d41dfcbecbed1777f83e841a010d08bbe038520a101812f3eba7fa1805c75ff72c383c097f9decb95e88574e0cb045f6c007de86fe8de0385784290d078dfbd90d0835eae462f57cdf3f988c763d765a34fae449f5c853eb9f23eb9e2e1c35f8ee281edb195743877604f8e9a3b565ee947f812ee844751f397fcf117bf388775fce52f7ff911cff1d78b7bb8866ad7e9d438fd6a9d86bd107a00fc3f00668330840430802a3e5404405f00221edb0b40d4a32501dc37fac3356fc200e8e65d70dfdf5ac8de42a6aa4c6c19d7b2ad03bf7fe5dcda6d03c51eb6ab03f7fc1d334b8e821d0f1edcab22dfeb7588ab2db99138a4835dae7a3bb77fc43352e28dcef5963eb98272e78f8e7438d658ede97053c28a944669aef29ad4b2f0f1b8f8b9fd9fc8f3c67ba3d63ad6b15616776a3eabe5f25021af546aad4aa9b151c2841e4ac4837b147db7cd83fefc9c080a38a6e6f3964d0c213932610924ab1f2617545625162ae4d8b3f22375c82a6e8db71a014f69b50d0722a57932570d31420b981188788215ceb8c1aadf9bb98a87911f7c814385093b3fab21434831c20d98200519476062d5efc55c5582170a64708320924005d9aabbaf9d384bc284aee8ac1c57e6b2ffcbe5fa22c82457646a7bfe2a774819539f570c8a3d70eda4c3ae6959c792b0f3c58e4db063750ab82161c58eb5887b0f46c2b08fe88f616b4dc250f1466ff446cf49c9ebf1465739abc4f246673979b90a48a3f87146174f0b365f2199cf994fdaacdd7e161a2d2ea71a2cbcd868c18517fe035912b175886d27e284276f885e1006c22b055fac64175114e187be020f56485806262bf17499c511a941dce68cf84e915cfc53c0a540a62ad0291f1590291e43fae81453e990a320c39692d371108fd421ad28b9caf415e62e2fda9617ffb2a9a5e55bbf32bd148db6d968a565b3276f7f83c28a1ceb722d57b57c3fe7ead40d8753a4b9959577118ff923aea651fd2d9fcd0fef7ff4502f4744c6f4f770ba5c0a248cc7f2a69617c2f2269617d2c2f2feded3b0bce9a5e8c5036d207dcbdf40fa164b95180106bf683fee86c3e18a74d8cf4262f9e46af29520e71a811b0d4a87dbecf2b7786c27c7e25a5c4d876ea4f42e5feb572e4ffa6a80f895cbb77e55faf87ad58cdb2f7d6edbdfcc00b79f5ff9b62a3d56dcaa5c333a256feaa753f6fb2d28edca8a3802c5172961c4fa84c65d017fb0dc1eb65be5f60fb92f3fddb166da5271f9e475f96a779b759d3bb9fdbec56eac1d6ffb08dc9474784bdfe6f241913a56be7f438254714424aa9f0b6209b757408e2584f42e2fa4e54b2ca7297dcb0b7179d20b297d0b8f9795a769f9d24bd1cb0a7803092cfd0d2d6097c020b4075204f15a91401952122b57dc10af955c21d974ac05598911cb603b8975e8dd735d1a07bdc3b7d1cb9597ef6d5c6c47e01828f6db56beadd66117eb708322e5f6f36ccbb1dc15b7d98dedde635b7da874d8ad5f95e84ccf28ec0bbfcb0f5991dee56948ef020641c21f84e54960102434a46ff92034dee519a46979d39bc020a66f0183b43ce987acb617a5bf5c79f558c443f4cfdf7cb90a1204098dcbbb802ff6e52a88cbbf580cac802f13b48157d25122101e7eb8a1177d3cf075af3e1db65c911cac555a3e1a5f0b87eb3d7bf2849d71feeafbd7c376bd2a5f4be95acb1ce55dd3275720b069db0eacf3c6c46e2c6c3ab60345ce75fbab8fab4a543ac5bf55297dd5c7eb4f876674d862dc1f7c458ec5b1a6032a152376829b193f1d6e54d80d143ba78a8f614539338363712c8ec5d990b81a8e4562c530c02cef59cf7a565b2da23c171db2d29f10e415a594db01a98377b88115341f328820230c026c11859233aa1003270aaf65520cb3219362150c8ce26cc07ac549189692a9a1ca2c6ed9e01dd286ac912209f03bc82c6e31ab03524a077c9300ffec40879d03b7d51178e2981eb10a06c92c6e714d877c042b720e0e28497a40841648386108ab072831038c23a2ecc8c06635c4892b4c61848c28a048a9c2aac500e289154f3c810553ae58e5b4aa038d6a2a7ce4c43b70e08781553eb0a49d8ab80f3590d20b1b1bfe1c3a6c0777e0c28a528a0c0636906f3f9f777020b3386632cb871038b37847e5987e7eb90fac62259c6972c87011256e6e730db80537b0196266fb8a6cc3636e61d9866d54bcc33023585106068329e11d98abd886da308c6d5c25bb655aa6655a267cc29337443fb9e04f9ef816fec405469d4275743da7947de37aec2a38908563ba0a56b9b0b1b40bfe9a355142c6858ce12bbaa03d3ac7b4b0f38a1cf3cddd5d4a87b11fbc74728a60c566754da738219a8411e5f6e5072275ccef2f815435cbbb45e4ba826819d46a5daed81bb344f1090a12734ea3341ae8507c460b5152e54e840e0b1d1656b004f20736f20b804898fe1e691886230beff0221ce36030ca8bb0dce6b68de3b62d02fc33058c221c0657c106b77f72ada751fd4fda09c76e2fe9f18995300973917342530bbb912ebff0fbffe62e1f3df2c03acf4e871c9333d4ef6f2252c54de88b278e358b63602f915cc47a0c430a84fe00913e3a85a351fc4e6e8e4ad3aa50a3580c439f538806068c7780c0cdc53160b08a0362832581314773e72a1d36fb74dee2182058458bf05637ba1d2d8255927947672163a86054bf162c689984e9718984a912c686beb6f44e79453ae55f7fce38e8f3e710e8b9ccfad79f44b0fc6d97061af7e3c6d2933e4889540291ccff81aa6045029178df634bf268342fe99dbf7bd1671ceba3ef81b81df60de6713b73738eb5c1b88de378380f14653641aa4d264e9f29cd8a3265e6b329e1f66784f4fe33890cb739dd672170141047b5287db61e4b5f6196f2cc89d3e1cd74f5a04918da1cb92894af27758cae9c3dcee9fd7c51e6ce99ae417e71bde9ea7af41efd7a522423ceb807768b59b3c9a45dd1a3840942aa7a8944b5b7ba39dba3e1628ee68dc54d57d13279ba8c90fcf3aeab0c7314f338caabe8194707d9492cc64f64dc38723d0c85671b6cdb61e90a7d1326758c3a55abebc01f7a7ff1af879e6be0cdcbc2f20d3dcc67b378936d4998db93cd082c6c446c2eae0a6ef45e20bb1a21e911922e71db9b324bdfeb8220c5942dcac001148ab00241c1480ca44882104d98b0c13c1bec1d881bb92a3617ef0081d3c5315c15acda8cd04dd7c6b9240c47c408adcb3bb817c8987e147044dc7e71c2c0051d9f9bcb05973f9ebcda1894468801152a58fd309168b1229db8abb355d84c667bd20898ceddb106fd7cb1c74db63de956a740d4a9535af21ce0efbdf75de3bdd82cd6cb6b1b9c3a1c14dc0efb9c3a9de2680d05b7d3a97ef5e6e29c00e2a41ce1c010d9c0b16851b609e54e2f2617dc38639b0f2b0cd694dd96326b51e48ec6e1989e3eb77b717e71fb4987dbac5138bc63cb42c6f46fb4c9735b0b09d333b8fdde269330cdea1074c54d260129e5829ebabcebde9d85a5f4d251a28fcb9f8b787832a983b3902a4a65d6012321582148684c5f7a212c4f7a21a62f7d14560892ee85909ee58594def4a2cf483480fe85067f1e82f51f72e7126bf96f203dcbdf507ad3cfb4243dcbd3909ee53b90a6f4a656a73c10c8f4bda3f859badb7f827e2eb1f376ef9fc8d7673f7fc6652ea8bba603e02de7d92f18bac9648e0ca2ae299b3599c854d3a0bb05351ce3364030aa4536804aaf53023be60d9bf578ba39ac5c7206cf1d3de83d0f916852471429a75c232496aba6ab59f3c5d1159b35753aa4814ccd266438618eea284d3494ab335d13d63157f16a4e97cff2abab7826790897635ac854a74086dd2da6c231ec8441b1529d4e81ab289126681932ac28a03557f1ca3562418505915a1421c419ab1fb61560b142c2ff0d61b5dd6a39d068dce99099bd1d6fecf06e3cb0287e5180416fa753dcf2f3adab4c3fb79f73dbb6a7ef6da3dd5ef48e5cef88a80653e8d61abaf70abed0d0bd7fdd7e7e1b77917eb5ce3899b0e311ac8c41ef86fb463c232530578d1374c5caf2ae58597794eefe3c812e828415bdd1065ef5cb51b3b2ea743263ecd3994aa7ba48954e39699d2ed22967bd44f187b59662b3af03362479874fb9922bb9125fb812a43a0ece507697e7f4da7a3f3f6f89176b54bf470587fd9de3c96ebf37ba3095db2fc2c035e6956986d28ba71a7201adba2a29a015ae4a2fb630432bc7aaf4e20bd6dba630bdb807d5a4b0bcfdc0529a922e64fa5eead3f821abd2f752bd1eeec586d29b5abe04064142537a960fe2f2a6679086c6b3804158bef4dd37e21929198d266afa4c26f0a57bb90a62fa97ae5210c8f4c9550964a4f41e87a4085462f9eae88da40eff1f7cc53a6e3e90d1f09edcee811529cdeb7c329bd4f738c9b18f3c369d4c59873eebf457374bc7799cc24ace301ecc581cc30356b11358cde21dcdea7e31add26177bfbc9efa1b14b607f5e9f0a3749c9ba5b525e41abd18123da5997ec8aa9ffe748ae6f4359ebf067802e58a411750320aa401b6801d63540f5b15fad3519f2d0a9d10ebc7f36a20086ea0378edd4de7ea6cba67518f97e88afde22bd2514aa1344ab3f27ab26d079603db65535f399d027baa3ac6a87eeb453a9c9bd3c8a8eeda31efda5aa486d0b1da5dd429b1fa75379dabb3a1e243a555e166b3d54c336695f99aaff99aaf122582bafa554dfd6a25a8abc631b48a5ad32a6a2387c1515871bef83b36a1cc27b7413184737b32b9fd528262688825b55aebb1570ddd845c211bcff33c291d7a316fecbedb633d3dfea234cee7cce9908ac1cdee1b14bd2a1ea5d56e1e2151fd4b4c276614f3e5d5240c159f904dc815ba99aff99a4766ce7cbd2a109b9aab667764ccc951e4f6cb16bb1b19cf6163bc514753b3b0f36c063ba29d4022091fe125615e52478f384ec483c29ce52dafd928e722af3ca1378a3ee79cf5996b37e7775f6be72eaace7215a5352b94b9a23b8b86404a773a5659aea2a3a34e39405126accc4fac5cb15fb19a940ea58e26316f8cd1240c8f15ebab09eeaf4954233235eb2bc96d50ac535eafd9d56ef4c6988cd9a75798d322a80e13276f74c56abd5887dc4b67e6bcf71b1432ef49a7669dd2a17b289d0ab5fcacfb7195b324aa3d9a57f37c4e2edb0ab1aee581952b13f8e2a3e55f684aef1ea55eadb55650871e3611e907ddc7974edebe4eda136fe641f1467714c7e2ccb9ada24aa42703812ddbb1a2b3288b8807a184dbdce5e92c96b3660f6eec9cf0c75d508ce562b90bba5b123b5dfcdb7d7ba0e872c5f9f2a760071a29d19e0efb557af58bf26c4f50d89cafd73856599dd110a356c5d84d5a7cd66eaca2d6e2185a05abaa128cea5993dab216ef682eaee82e58d0b1eec58e759bf15305746b5ecfd7974ee7786323f180e004a7534c3d2b4a57ec58e9d63a76d8dd57f698d4c1b7b956f999634828579c2ff6e9fcd329913a6bc25a459c654f1f3af1272decb3ab74e41d1dd82f8ee9c622467da26051bbbc833e41c6502fc840e24691309446dd3de4b1d85b4ddce5383fbaf9ed5e8f070419d33f3f2b2e4fb0ead49c97102c4f81af40a6aae3d42a4e449d52e7ac6377bb70173ca34b4e5c3b9fd4458b9860b31cfca8ac7e74098d5dde41b39031940aaa45a3807a5b8ef517e9b86d221e1d85ddb835c4b6f680443c38a0ef47eb11f5555f35575158a3fa7fb8a03e0cf3a93587eae8dcb8fd35a7bb71f9632aecd36133ad530ebed0008a1e04edfa0fb93446f5d73a3d2fc4d6dfebbae5581abe8028b8c8811557b492a064c554d8a75e66f6e7671f7ae9771232945abca4483ff6a1c2dc65e651698d7d181457ae8b3627ac48ebabc39c0e753aecaff5f5ea544ea7746afdbadae1dc0e47c2b4182cb146e10b9962824cd51f82d6b5c8b853f02254b71c7b9a3eaa5524cc8f3fccf277d52b7f07d29d0e772077f8502f4fa7984271fbe98ed4f1b9383fb779bbbae7ef5e07fe6e75d8dccead6e79eda2a20d7d49df6bab6d3ac52e8f433d9d8a758a73d2a94ec6640785714cd351ea58223d3a8e2e5e0545f7de3f0afa6acf0b0db47b0ad62157d646b7fb69eb843267f38984e99c0ee76beecc2374943adad2faaa3a399b0eac4869544aa76a9da2d283e58adee89f27ebd049875ecceb81f10e6f0913fac4ede74c57f4462fc685381e0c7a4ebc1e2fd6a19703db81ecc35490344dac4e2acfc945aee82c16908a83e3c6159d253a8be58acea2e2ee0480675c6b9a44861dba184a9211f6c2621400cf5a886ef9a4230b91e43685c9d004d21975bd6a8cc1507df5103a08b236b15948b1b5b0af9e58a7a677ef2207a5edfe5dc4a37bdf72accbadd5a34169264aeb2b584503f45aa078617a7204cf0911bcb109dbfd7cd17bd91a279137d6aed65a6bad94a3b5761d0ca179c5f99a4eb80d06a3fac15ba231f1a470cccbe72729134aa363c79c40aba5af57330923576ed3e4c68b74b5765ba9f64c9df9aa916359b862c5c2f29773b7266c2b79799ec73e9d639d82b9cf16e2768bc37ef1bba173ed545c6327748b777420d5e118ea6227b08a16d1ba4d757807cf38a695a854e7c2244c7db5123abca33a2163904832bbf52575b447697d45912a3a4a54ff0ab5a276fb2913a99a2f55acc98c06e1f66f4c58d11b474746b011cf48497d7134def746df6c1d892230e4d611cc51239e91920e29f883bd51c28c8e748cd2b83ac65ac483c2ea58c75a8ff0e5ba097cade3f81251ca4377460a8b759f0c67a5308a05487328d76a57a3549a84a9506e7fedaaacd2a48ebeded5dad5eab07b63b3e5587eb1d26a5c2f067e552675ecb0eb4b3a361399a24dc870ec14358114467b38a673e8e8aafaa2231d3bc533da844c711714e62ad981b03aab51aa178ceaa272517deaabd6521da5d43a45a553d55b2255942651fd360b5b9bdce6a27b8f2df5c6ce8b815e4f879ed41145aa5a46a7d027b85c29e28eb9ca1b1d35c6bc31e68db18e7aa3b78527dbfac8c6d4455d1cd8ae256cfdf93c8fcc23ad92f33573ea4f27f7347ba612f9b3d371473bcad1ce6dda6b6afb91f69cf622ed81a17963eb7b6c7978473ad092fa729547854704987745d5b9fd01f046212d6f7a21346830d07821a66f7921a5a7f122a5c14043e34bdff2a6bfa10534994e34c03a9ac093a3da08cbd73fd229d84e95ddd25350acf7f38e74584d5fecc6322856d9eddfe1be7ebf7f943e75ea9482a20e3d6c977be799a341a8748db357452e999a010000200083140000200c0c8643c251b158d4d3e40714000c7e984e74561fcac224875114659031c818428001408008c048c90c1500009dd3e353358d691599108a9510e8a4e0ffd360b20443fbb502a2fecc2b93e0f2981b5274c0a70531908af52711048e26e2e8a63ec9bdda596de5a6f66887a98f32f5a38f4084c4454b459d8e6dbdf134491cd695a64859f9092b448b48b870989c893e5a5f44661c369d1325a2515518085386130dfabba53a013b5294ade0b788bfc8cbfd1934ddb87fcf8b067216b81210131bd407d256cc502a9250c37eb377c2832e63350335b58193745d71650ab826cacf8dce36992837ffc3768c2846b7793f7845cd5239fc0db645efbee7fa65f15803bda15b738daea4ff4cce5a9f62d170963bc4ab5f9d416e2b21f729032fd1cc24465a1b65178ab94ee2f1f7ad88bfebf5f2ca81e89cdea9a2a362c44e671c979057a72d0e6e9d172445b1e9ff6b9b275b36744231be5daf15c63d99fce3e317f9d5932bd8be4ce6afb259150830515c2d299f562bb1484fc063fec56ab60f0491fa4b20462e607b512b84543e48f5495217c58c4d035bd0ebc1cfc93e12f33052f77ce4e9aaaf152e795da5bf5ed7fa5a9adfc20b9c2e5bdfdcbdeb736d3bc99f4b3269bcd2f461e5384db6850a7dcdd2dad318e963dcccdf4b796ffea8daf320cb6ef1848c229f8ddcb015f9e5cb1edbaede79864411ae6fa57009e5686deebe5e1a71aedf6e2c34ad48c232ae692dcc9e4d6fb428808a990b27caeea3f0127c4c116e8e531503e93c981c71e02ab2e5450af6995ff4b972f73c1bc248dc6dae312e3975c357d344ee19fb5e6b3128543e415007c61aa43f90149a4bc81a0a804d7b60be350b6441420a94c2135e0b82e2fc92540bc3aa16f27e86806cc0b447dcecd5ca78521f6ba7673ac112bc8400150c9367183f6c584acb9263343ab95616e824cd20b5ecd7a8f851f82fe605183438d36c9e21a70abc24cb64b81bca6e985dfb6a99017aca292698d810c1935a8c8dcf75821e87bad62ab67d4b9ba8365c91af3ff892838b5acc8ae556e2a55c43af9be149b4ca4218c93a2994a00c2d439bc03015f6f398fa279526492dcf4c62fe374994c57b8d88a5c6aaf123f3552bf0c9653f970ca4cc5e9bef0dd0a43c10da6a518e1f0a3b847eb07e431f650309239a9287b224f4fcc764ece6d4bddc9cb2d6259509ba6803bd0c5cdeba3c063f47c1f49ac9a5a21c7303448921c0780f90d688a7d78a87b9aaa0de01e52d5b3020a2fdbdb0c0b2a06c6e5acf0f9fddcfa201babbb177307f78ed6e7b9928d569dc6f5cce790426ad05f31e61bab69977a0763dde8bd58bbc041aaea3e672fa0b5e530cf30e20040c81d99e3b00216b767a1b3a69050b3dc8b89ea963b9c31bab107a9e9ee6d2b8786675f1e47f95980c77c90bdef2c59f93c5ab932f4ab6f26184db1dea23f7fe494ece9b5bd895c7c687a00813da36c62adc00f6793337699aec5cafea1de634d91f3a2a43ce485cc74a17509a2cb67d4d543412fc186994da3012c0342cef84771753013c051350a38cd350ea24b3dd2ab16eb64299f9f637619089254032d5f4165a1ef5fdbd2f0cca5623a6f274789ecdfe798701ceebda157c5f057088d3e87eb0b373aab7336b5981a93c2c30c0dc6aaeeb28e7f2a7f2ecfc56d4510d65372d24269eca9c70236e0b665482c82933562a196fef9d16a8fb5f8a656895a459de9d760b2f9be560e1c8b089339b845ffbcb5a168cf11820aa1ed42831e144c81bdb02ddd6784d48b07b543c4533cba69ab4a7b627d2c1a02b4f4e699c54eae4f083d1660815bd8ae31435863bd867954ffa065bc1f70b3fd562f9efa4c98aebe694f714ac7b6335ab74ecbfeb5d35bad65e171f030193b619afcaf8c5cd3feaccf83895d396b7f62c9edcc91f4eaf846f0aae371ce84386e8b575a279aa6af0d5df0c0351036a3c96f84a50333aff097c13967ec7ff92fec86829f562fc7b4997b673a4defcb1611dab289d3fe84a9e7a2a2e28d5d37364f6a8978a0652260adafcd93c52c0169f1981b4bf8f3ae604b8d2297f934a21ca4e67d235e116f6c310e64a3cd0bd35a336413e3f31b1638af11580c124e494fe8dc8375e0fd5a3acb6697cc4f8dfedc9418a02cd2b54677f25ba82bffb33635cdf157f9e4c83fefb28bf214ad43b60c7ce19e1ed18a465934e28994560908d40621f587a3a85502a233aed8c5e4e502876653da161f488d25bf7a648a766f9a7dc09f12eceedcf3a487e92d5e212c2774da6f10a37f75ee89a12495e6b37801cec9ff1ff6b27c5ae3350caa2a45697d6e091768f59eedc70648b128f25c79c5034426575cecf118fddd6d510cc870631e0b157f4f5eef5ae1621b7645e88bd8fc7ef62ff801e02f792004dde401393104f604be42db2b4fb4538f7ea20a7435f6bb2aae0d2ecf1196c527fa5ed76401eecec3370a36e3bc0ca2cadd212fcccd9eaccd4935f7073b965ac5f52772045716befb740aedad20ff080e23282b92efb464a39d821a5bc0c8ff8b1dbaa4e31a16740159d0837dae9708c64cc9929d0d608ccc4ba50873b93202a281bc6815c9ed65c5a2a0453d03289de39599e1d26576668cef824bf453c35cf60ed632b3d7642d072730eb9b3bc1b0108d9b525f8f42d3af337e0575ee2b07b2193f20f700214f183acf57e2d42797abe65212a7500f133b0f4c1cf8054fd0bca8abd6e06313234cee28577f0797e710c5b03fcc5bc87c8a8c080d8a0adf58c03821cf017c75af44b571e124c1337c46c1426332a88ed0053c90118909022b397c292415206620a2a367fc68d71ff6729dd95f81cec3fafd94b745ec287d1cfd02d68eedd33f6e527ef545329b9264c82e11b4d1480e2ed924a17d0fe51a54e4fb459dd9e9697e60c3fb0a312d5836bd7a846567b32ba49d37cbcc590208dbe687e701c358c8184d2104d4a1a46506a47e5c40de83d5bd8053beaa3f6fdf91c9f1480abc16a3d2128f427b98cc305896bc834343f9c15c011f4e2baf838556708e2c43ef9bb3c15e6dccb0833b4c825bf402aff4ba2d656ea2fde14467d58b68adc348e23d7e15d0a01106016305286759e42ca744357e0f09db3009a27ce992d1515bac10de9947b6f7bff475a82227549ebea0c0673738ec78590667c3bacf0ddc1fc5456fcd8dbed00e36f0dc94b5d93834d48f9f4a07ad2e139919375bc7573cd9a277c4416b8cf72ec1a609ce7384fc37dc631a8f77046c5260e4f27dc191f9b7451a78694be7b6eb7714be08d1d35f9f16d3bb90f31b0b26c2b8ee734defb30783d2881b49936959a1045b762c5841a907362e4c647e09a5dd6dcd4be3e202e9acdd43306aa160a47ed78f89718c408ab80b232a8a536c70fc3d2387ce6cc151394a87f6f5be15b14812b6e94e3dd784b0cfe2ccd3251bede50dc2286a11a1285d0591fa4bb4ffcc9948cf0fb279da44f090c6ba2e1f30c167b366882c8860e53f6c15a045746c4f712f0a1e102ce084a5200de8a1401b44e47d288b971756a84abaa8643db805e4f88abf8eed8cb9405e002c1df2807f290ddafa65d6c6aecc588d814c49e6d8e2e67ec831a85ecceb17df3fb6c093604c5e13711cdcfed5d7a1c5d72c680910e8dcb2fc326e5c647251b853d2086d453dabbdd294a0a2f4ae5dff94edb4f4837b4f5e696bef0c1436d17377a338be4088657ceb888b2d291e0d1659f1cec0ab733d0c4619e7b1bb10cefaa3ef73336de333908c614050e2be39fc0aa74e88c8ea88facdef280170d869a8927e7c3c03991bb44f63a54383203448f2738492a63ba8e4d15a5c4ec06c6208748052dab4fb1194c5f272fe2a3dbb89d0dfa94d93e31508da90c3087579d09b7b56d043422dd20738403f654660f80c12342d9034154f9117bc92cc0dae0c077a1a9c8573491ea81e4d847afeaf35ff6acbab85d9ec871e92f672fb95426495bce7cd8b8a5436117dd7c55148aa797f5744a59c0aa482f9abe1e8dfb9f2886ad2f9d305394c1b121d5855916c23de231755298f3d2fd4a47b80bdd1eace98fb82ac9e20861f2fcb2607538fece09f52d48ec07158d9092e8ef02ad622f4a72f4ccf9f9513edbc9dda9b939a8730fe4ecd566ec4cde07fd6985086b15e5a24c308889d803f5721398509cf42536c475578b7a90e54c13cec5fa050ce8737af115797c1a19be8130bb4e9bbffa3b6565b4dcb384f7a48e584456787e4790e0b23bd866d8c4d32c279d2b958d3d620c075758b269793cccedb4d49ed6e204f67c4ec2529f296adff2b41c427c47d35614281e45e6ba4c99f4eebd1e8b27aea3d413d122633b7ca31628a43ead4b4a7a988898ad7c134d14b3da4fb0d67d1d58dff17813d56164a430a4d13ecc403cfd1a9fae7710c41a5f8a1270540125371956fe43b040c62964eb84af7ab0d51e2f58c2e5e5d691d6cc319d22c029f5076e82f36620469cdec1f7e865cd709d0d623f49eb5ac82869d44b6c9d22d7606aa880c9d579ff2dbf0581735e425616773e413e566a7ded4f71f7605938dc54908e36f90cc1385b3c593d31ca8278fcab5bbdffd8bb0a4e3b894ce34d06fa6904f6f404f8941968ca26329250837a65e8f8a61763ef6ac58c09f8bfd9cdb741a7bd0457b0ab02d609b6593587a19b9207580bd408531ee081a8f27c6083f1744ebf478cc9a2f3825e540f817d237087c8033e7c094d8921e45ba3688069924ef37c1af8f21ff6c80ba61878b97978d02d347996e56b0d16bae353468b5fa0135366a6ae85f06c4c68417a0a3941ebccaa221e0fafd3032ae8b0b199ea938c7492e449d436c2796f9b0795147e18491c29a1c5b654f34d14e4862007f741a8ea32818943131146b3de20c8d3fd6e78c3cdf007c90b11b5e0f4789e61f1c941cd7e3c2705677eb0a0aee78b87f533077f564d1efb31509fc9e102905a4129100446f896de4170b05803ea02464b47ffcbbc0d5e73c1b8d72b7cf5bf533f38712e44e61afe6989290623919c409fd13d5487e21024f4554f5abe00e476f58042f28a87450939d6fb1d87ef2263d9c8f065c4c025541b7156ef5afa4101e693a5a8f2e5dfcd2c64358c49047bafc9970acf751241fcadf0781de1d3d0b47078e83b5a0a93b50b1fb288affcf3b6c84d6bc8786e07d8e0410affdf6e93daa83176175a63593392f8bbe690fcff1e87709a43eca93d88f19a11fd595c797d35f231c600090a65007448a4eac5a53b342947b03cdcaedf5bdc96be63aa2f42dce978ba35a9b6b5afd780517d43b44cb21427607c648ae2c5eec8216941557ee76631a5057c4d22e382cd045173e7265cf2d09cfb0b6ac5dc85b5d39fc909faf8b08aa1e0e4a041151e32a7933a4175f0df9547c45414b9cadedac240907c384671424a327a69a0204a8486e420957ccd570ce28bf9e3708c66b12e48293059c24f077eb0f04abd93d4defad66675f49bcc4c43a090b4aded2b23684cd767874c559a7e2c6fbcef3481485cb8bb780d6d4938b89014cb84aaff272f36675c7d0275048171112bd733888d38a69c6848dc7e4268c5c1c226a969c068a8714847ed4e03fbaeb12d94dcae13e1f563c83b6727f5b245c2613b9cc1aa67b2ad4aad58edd67a092c40fb84666796e60e56145a7cd66c6f02cf2b63c91b266d5d394d6aca847285f18f0cb79bbc791847010bfc3c09d854eb779dfc3d374d2b4fe257ada467bdf17fc0e0078796ba94f8f5eb1c9bc8e1834eae93f73dfa12ecc3092d255c68c9e780c3ad3d9c7536835ae00a88a5f1d5c6f10331195c46cb12cdf19572755c400a4a70c72dba377cdfb8560f47115592f12b3d893baee53baec02647f2602815f66af45070d3378e487441cd9782c210aeac304f57b59b76713fe930b0962895d9cff4d0eef74336096940bfd606cd050d07cbbaaf48841cb8f47ccaf20638cf900923585074163f505328a7a9ce6b4eba82e0f03fa7b797472bd29f692c6267c0470b5a323c8a36e557a902fa0ca5bd1b8b0a5d1d0ce3228e06dd314e4354bc0386c42d833eccfb1a80c867c13009df06337ae634973dc907b6ae33bde6af1a5af8e2b5e075e856e1ce8910ee05d379cfb8605245258930e705e9474e80dbb941fe32879447400a8108c9d4907d1c4c051ea6adf981926379dbfc1305ab68d8e6733b8c4b87f9b92212d1966ba73088765248e06a940d9e11a0454a540e831d8a06e25f6b20853da0c8d0758974482a92ba043a9ed3387d5e1b2f29ca34c590ae7a2be7f240b2ed01f143718264fd037d74e3073d5039bf75864098c7644ffcfe1455b7459b320edff6630531f021322e554284b3528abdb73248806dce3bd25a6b27f06a27231514e8c0326277b0ac09ed94c98b702c4ccb029c5863317c064e76eb58508dc7c8756697f3d401a65b8810f0f152ab2826c741351d4caf53919065ee6366f6d8dadda13f9c33f9ab2090628cb4bb7fc4492c741d3bca44fe4ab43f080c6aff4bc530886fa5b03b34977aa33a2640e8c7ce9fd046f47e1b105f11e5c01a7c8f126b1fe6727320c0b6f6f319dd9470bf091211d2943b187f53b50ada08146fdc02c9c3ddc6d7e3cc928bb176d4d8f19f5e702d991a3b8233249ae51b0deda181fe1c0d07d14069474310d1c0562593fe0408ca0edba2ef50521d70035909a41d7fc62d51b7f05c32895453f217cb4a7bf44cb16d322e4f0bd6ec707b2ced0a3980be8102e1f554653eb12121fa6f3cf3bc3d2d235a9a50b43742472372dab2fc51039095d977165b889d6cd4e9a9cdd26e33c3838eb2aa1c70b7016cd56b0525db9f0607975420ce21c6fec0e1be18e1169d5121f87ca33e015548688387404b4b89dfe17f5dc3fe019dc734fea34053243467a99ffcda89bf3ab6649a867c659226ca62578ac20b1703d162b2a9619c1252ce9f94aef55beada521834917cc621b728931d0ad0061c78fbeb7806560250743bb742766e961fcd7d80a1be9b54a63c0f5cf7c7da27c934c00a158501239d9bbbad8dc3fb78344b12a21b8b0546b6490a4b584db42ed4d9cbc4af8b7948428693c973d7bb2950597a0da274e5151baf0c9b6418622df4c9596ff2466311159118f8fb13bb44271f7c1d603ef2e72ccf0e36e9fbd27d45457c0d014c3e59c4c163a68612512eaf528db2ca80e1a52d908f9a3af2ef6aa4943a72589057f162ccd5abf125813b4bced6bb101e8692b6437409eac12cee236f688b3156c192beaf0edb56d7739390236925a71c62863b9817ecce680cc497495b2ef3c733642eebcfd19d03b9c04787c5e7c321e4589a2a8291e7443347c2cd46f067481d4b1febbea798f426f7a40541f0a49698c714acaa7acf0e8b5bb7bf2bfea7f58f4e17c4765e11e24479fecc851292e8d2447fac30b7b4651f1db10609b143a0cb29d642c75a35c4dbc2e7dcb7dbc0797d391621a7229fc063d699cb43351f83057a3f03c9302dafda7500b0b9433e68dedfaaf47e8c4dce7c2f18e46a9ad8cbea9a600dff16296c1a5691d825a96edcca602a2cdeebe3980f3f7029411de2e95960372d49bac47d5372c08b05367267ea15785b742a0f7ee07dab1489dac5193765724b1254c5b0b8a3ba80a039a22eb81bcff58b73123d3a8cc27f5b7390b0cce3a0600c16494f1b436547f0f1260032d331a0892af6008a561aac5158698a5699bac960119bf149cb656da44dadbbed9d254d125807198da15e820411f0d23241d03fd4b0d374c657c05f164d57d325e440db9d29f2b12547112682edc792a50d2ad08ae8dee0326b2dcccc720e31e6e68f2ce1b153519d6c0b7e012632256b5b2677742daf6a700731005d6380c37c6a6340cfcca6c4f38b243da6edf84c3b2ca8aa3b2d9e05ad89a7c9715ba44079a814b6338fdd4a7443e018849c8edb94c502be83c4112c774c551818d6ca1e86daa38cf01d581d13af00fdd8a219dc98b6858c0101f60b88be994914ac3e4abf9036e018e80509f040065af7e48e61606ce460cebd74e1147f313363f1a18769c758ad582f66f3f0569dafe719499de756a6f202a9a85eaeb89338974224f6e2569b50132c167e1a4aafe3274b8ba91f79279dd52112114a4a897a4deaa600aa138685c9548a4182361a053b7b6a574d84c003c6d7345f4defbd0328151306eca3f6b4ce3900b35b563ce8fa03463240423b208891b806503993a69179c93dc0793ca0d0b05b5c335124de359b3c60c23ad5139b7e5cc671595808521bbd6178529e4eef913640f50b5ca3036a905171abd667777be6c8ce499f101363f8f26c17f5b8c32929d9c3c035dd6646b45bd7867892798178139d3614ba15b98b0b3fbc128cce66849d47c02b00a75c7ece3f1f2b242c08e120f8e18d45a004005dcc50434906855cd32c9648fe3e782ba858af0ee3a25cef4eb3e81896c6555ee78fba889aeb8578100a6c81467d446f397018686f057e4bc448448bf2b488519705c30c5c5d3321b27cc227583d18a7289e38ee772da78740bae1dc56de3b58610c42888bcd9dc43c2cd20cd99682b07da0b6a1898102211864701969826fd903f877fe7dc183b542c7c2b862ae59adf36c427f80bfe6772f5825b6dc27c02f9a3fd78e697d29d5677e956f887a8dcb672d382c1890c38d059f802c103482d630f2f11a310ff48cc52eb945c0ae9b359cb53c126b7f2e8dc8de7cb3a859d7333df64516b11f3f9effe878aa512c0089b22cbc8da747ef4cc357099160d96b7fb8d43082529de13f41d77ab102fa86a8325f351aaef01b4fac77eebd40722da45cf14f06b132feb9f1c85b66f62ce6241d0767edc8a10d6bf42f9791c32b6727651c8b3612ff7b92c8e1f36ba4d31adc5b3115822372eb4e53e36b510368fed663048f55966382e2d66c17f4ea5a464eecb91a9a5d408f64d2c1037cdcd268a8f304ac230081803daacbb3a479fa5a5f63de1367938f610bb2ab4e3b25ed6ca8ba795b78eb9071f244b15c968d161b99b8913cc5e13a120de31d302ccf3442f3ede2917ce90f244c1c3afbafbd34383774c77b80754e00a54dcdcdb03649fb54e0a2a4cddaf282cbdf658547b6bbcd1ab1c18d76d8e9b29cdc2bc24d28136b6df60069eb10e9849ecda35962f617566adef995203805cb21ad4fe8c834159dfc0283256213864e37d8dea4aaf4c6892ae752e4079c8abe01695aa9698a543720ffc5656acdd7142c6c405e9cddbe1862d001046b98ec026bc645cb3a4cad044dc39e7311f65fbe555d8b43c0af2234cd5e1ba805d7d52554ebc4868e4fc6f8a6c4b38d997744be7a4c1308b5ffcbb8f4f44e828dca9b63c39ad4ab6c71e03a7057e47884d6f34eda496a305291c3cbbc24de0081257bd784c14890da10f2e8d34fd8be4657ab3f80668db8ad1593573a4c1886e6c5c8d0d763e0872ef3d073bd7576294410258ba5376651bc1d065fd6f859e6c019d66051b2b3bcbc25125e504e4a69565e0aae95cbf463486dbf8d7d08742b9a39f7fdf97a25446604e8a4dcbefc6723d40764409b6f6526dbd9eae9d351aef94fa8aa5a201b1043d80ea8861200186895ee4b84a1014bb4ccf568b8303ea84f1cedb921e8a5911b0a9c86259cc22e319c8b202dbeb7989046a1c7026859e93e32b7c29d50796fd06ca4f0ccca08fa0d66e84dfbe5f306d44ec878a79b34f71b01d91be5e97133953e9881c131a5ba63ae718c793d3e4efde2b86caf6dd221c9850140ed3d796b02587fb65e48e62213a04494d161fc96514e2cd1c5574aa43e024e1f800c915ecd82d02042d1f537a43397cd89f606b4d38a642d1a23fc28097745450229023919922bcba3f2de04346354798f0716a74d53f1dab9e2aa30153d3c188f1baa578b8be5695e105aba9506446394458631eac8965243ff8bc98406ddb2fe2b1342c84a0da91936db033061c704e2a7dd2b8a0de7c2e157488b9d1a2bdee8bef34fc4924af5fd858c25a3485b4f065f9028e3ab050d55e00245f5e4d47b334a376e01e22c910ab09502a52eabe7421a2e5ceb5e267c680ddeec3d171810d32af8658a5b29d1415a4ca8ebafbd6426a850c81dff642b0a29ca3a594884e9fcf3d6edf6e42589d98aa2f1163293290918a1b3c1b387878ac866c604047ad907f48619819fc9d8ff32a47f0cfcd0d3bea1ec3897d04e93bc9468f2af50892562ae6d7d14d6914e80b3c400853ec7f2827c1d3d2fc98cff07f08ffe0a1bce411e48dd7e4981fc28263f8e6aae150c87282981a589ef54d12e0d5e6b14c028e12de13692d7675aa9a9c8a61e71d85e9771c36b7d2b4891417df06e0b337d451694b7aecc1a63bbaaf1f5bb1680673d2ce951af7787a76d7555117bd0ce653247cd499cfd2df6721b9c61fe45cbcc116345072028abbb770ad114915eac429ff4942c6526617004fedb9e374022f466185a951e0559602d24ba3e73d19d3b3813399d12385e0bbbb7197da772e74e9698f2af84e2a0ce128028ef817892ef90be05828033f746ffaa20984ab2446de68b743f0c0db797c802082387851e02d5aab3cfbe77dd4ddb1d9f01481aae1d1c2e0b6294541ea8f80f1331fc1e374b1d8d929d28bd486312f4a5312cdf3b7bdfaffe37faa3c3b89444e94dd4fbadbca9f5e341984880b487eb6cb62f422d74982ffc6918956c5785038d90f76705adf6557cf3780c6c9ae56ec8387a11df4289021f052cd912945874586efd1ea4799922bc123a917a9b280d5a36b7f9462e8c097a87e7bb07ffa6109f14f17cab5a5e4eb5b6b27e1a66bf2e6c4ba1e943161111e9daffc107d206edbe668e78f7ea25dce9d598bc896e900f79d2a16d8892c7b5be1fc1da5fb725aa59c4779dcfdf4f00ab6d00818a7daaded59a9414976629d8781c7831e451ecf01e9580a404d97b7d9f9404140c97d79192c0009bbd22460e4ae4a15b0e37e205199792019436e9efea62b3a11be3c77214391a77ef59054c7914c5ea9848814247f5b6877bf44bb96ea6929016b0ea7a8d1861495782b8bbd959a8573b015c6ce40c4eb115647ffdd9a4a5666fc719a5c38044518c87bdd641d4a66a2a996c6765067726cfb0b24d07a651f8683252afdc9a72c169653b6ac6cf5225aae9afb4a1738de725b651d22a96d8dce92dba3ec9ed23ac16a80f8a549e5f30e4fe734427f5e89d85019d6c390db3f6e9bea922009d8d58f71e5c4fccebbf48108d211457927ad2a76a9c9eb42bfa90f43ca95c38c33b11cee8f6ea298f461b1de480f57a967806dad2f0e157424d2e4f736ee5d520a5f05513510027756ad10fdde169881e2404c94387a25f0f9ad1f7527e1b0f60fc14bf907fa0057c0ef7562100a5884051289913a275588ba6e35ab9634643ee503bf19051f53c28f6540393f1900505bb6700f87c48a78f962e08713d68ee9f86dcf4a63c451d333d3cbdc0c3a3426065ffe975601f760611360b7f25469a140f9880a65060e43bb746a8055956a2d348ccf4327156772ebe343f0b79a34766a921fb0e1fc821ae411ef75fa51ded2f53c5fab7e32181cbe53ec6b95cbe33b011314a7145c5de5aa9e9c53bcedf11dab6f277ea289601a9fa9678e51c41865732859a0f867cfc3d4b1f548895ab312b50a10c0f23217c0ab399885f6de7fe645363c71eaf169641ddb3930e7858e5c16a7c83ac5b59ebd464ece0007c38e04200caec6f428c5e957831db9876f40fe7311a0e90a77e10cf3700cf60f92d39e17d434b21227b2cab859bc529ffd8e1efe99572459d75b57e84925f06e2650940455c7d5f64d35dea5d65fc9b0d2265c26ba327d4976d82483fda5f52af8c752af05357a06a6c55293861afa8092fb94321ac220c15e5532624d004223189fe133419156dbf325419002b57a6e8ccd6b4b928dce6e006bfaa8ac8262c6c23f6820a37fac651455dff8dd291916de9877e19e1a06bcdd070284bfcb959722c35f5a288a5c284c75bbcd01cfd9a1195a38cd8d5d229d1daf362267cef3ad91ac2408890119492eec553e636a388e71e90d204721118a5935a57f8682568bdc518288c0227e673973aafd2c58907ff8b64fecae030e3c2776de107f67dc1dc281ce19362c4f482a3f6af0c3f4813dd964d51c46406164e44b461c65c44e8b44cd883452873018396b6004a9d26abd8c3b98dda7ef16b8bb34b14259ffd9199f81a845eb2002062eda61c994dd2d113fa424419ae77e55e5b082d33a14d50e67b57c5efe2f017d161a0cc0064352a20ec97303a6ab7f25cbba449a908a5c297d3cc016e3df24d25a7ccf21f2b446a57111e481cf26f7709b9a6dd31951ca2c1624958adcc3371ba3c53a6028b9f329c7430a3a49d74360006a2eb04e4d5744c551a18f3d29cf2d3244648dc7d33041df07368e702fb87bcdeb1fe4ebcbed1e62c7a40608d8626afe23b2d722c893b5722e0b996802b4426e44a484d9d0839639370ec2630e97c20b8049c8e47c577a2d62786ede61c3b3ecec4b13d38c3c09e6cc3520ca8a5e730d78baed804d501998b11d59223beca5be49834e309e01306712b1cdce95169aae78d24b6eca150325456de95d6059a5884e0ee821f5d15ad93a034bd208d6bac0102fddb69e1f6266657d084fff60b1188620652ca3ad85e16af1a382bb23fa1cf503d5ca9036fe01c46e826a3f92fc18bf328c9fb580467a6d5f18c0d9a239b9ca72f6afd55a06116a8032ab03eec853a4f679a366566620da4252bd7c96a55258b6673a28fa9fd913531b1f168ce487128b4dccfff5302e4a13890fec3ea4fc276eb90f06347a389cd8c9293e69db2e78228c924038b0c2e8422b0a818f01ef78b3c3560f1e269fc7ce2145d6fd6b4c1ac817e5e6f7ccddc4bac20d5db3ee80d0183179b63c4d9dd3568d66be94893d7b0a304cd13102fdee83455abf033421b01b2f19c92a208a41ae6346ade54a160494d991e9055c13765f0c018b183dc6987d8e9f48f2cc5a74636ced57e6e381d72027c82b8f4ec7df290efaf908d70485f99a7cd02a01f9f3c58d1aaf221e2916d36eaf60029c97aaccc01a65d4fb21aed5fe50634f2c3ee205b6da9f0b67026db8cdf3d6439d2a4ba628fac3b37b4ea3b2575f80aeb16284a9439fc95864d9caa32c0aa3f64f9c224ca79b65f5a6c1976d07b0b78570ced1f3210dfdfd8c0ea0dbb3d67abd4a00d128385595518d22a0d8836cfc93d2790078067cec836a153dd4cf9a8f8d6a8874ce95cbc3032a108148123d0e626c845dcded997406eb3c897f388c4f39adae1524feb58d0976babe9a2280876ff539e7ef7a5e0700b139556851b64b9442acfe053b9f8a398087ccdf2de5c859355abd036f99a658dede89231d62ca2910256613a331495fba1a5cd063d328432c23248ee8593dd77d4ecc49f519bb735b75c030b7940bda7096c3cebd3d721284e55d10a99502c24a9f7da2446850eb3c339dd10906dfa82a0bd3d2cfc21fc3fae5f9a8d280723d95efb72964c5706b8ae8129f487728370106b6fc26a791aae0da24699053a95428fedc2238925622833e0d1b90ab45fe0a7abbb1a85de82c9d1e208da374af9197200d59c3950f792eb8b39a086f3895487f68ca5f640c674eb0344667141142ee778579c1e45ff64204b85141a8e75400e792c9f7737a8fcb07aea3ace3dfee31c5ab3cf026dbe82fd770fc7c55925547fd575883a732f7e832b09398b5470817e12a9af33267e969fece22131bc517c7401228c282e49b017da4e65c1c5ed7d44da2f5816ab2ca899affe5dd04458c510c73ba030362db4dc21f68977882d44c9af52431ba77f3597979ae32fc62021dcb9c13f10f15a832eeb3756fe47ff038fb67615f6d073dcbe15be0f588c0b31e4442485e8d07343840b2087cc183dc8e7213cd1c726441e69449e3ec97f038ab0eb11b01efd30674d49d7960010ca6310cbd375916f117d15ed133a2cb32c09576c6cc68fb3f580af72ec39ccec088ff33775a1f52b392968cef801867cc9c07be08acb65b32b94e2174fc27f7eb0f648df3e55c7453fdf8c343049a2b82532ee4113dcfb1ff888bb07242870ad80f4dad80c28377233132eb8b5fdf792981620164312d1a7187b564bfc7a85255882b448f46f290e1998029cc23a771dd93f60a6a0117da3ad4ddf2f27d0c5c604ed6b442c5d491fabf0ad4e2d0b4e7174c443f466d95687242e1ff8119ce302fe62c5b7181e9f50f54b901de67f25295cfb4432ff745241a5b97caa471f872c9350f6f0760a9053d66efc92a48056970d18f8c98a9ffec8534e4525666e33197af06f88ac1d24963e87db70f44c951394219749eba34760c7d8489b10a9a42abf7cd13b9dad2e324be47e685da78caaa36f3544b3e58f0647dfcc9dfde17f621df4e80b64c1e3bb5901b6c36778dd6e742ad53380618ba852fdadf0f0774878ee635232ad0d80ba26f3ccb2f47a95c6187ca2f74137648c5c1d3a1901a7f976476d68a347467814fca723cbf42d90e1294a8b6323ffe3cfe61ec251916efe3eeb9d8d10effe670e982413b1de2031f3efec2294b3c377a47a6018dad9db079b2a58a34beb24b862d11af979716b44748f02b4352682538dcb3abd2fa74e8d803220ee05e2bb309b274cdfab03dae8993fd51722b6a7b40fc53360351dc5b7c99a5d36b855e5a3ff744ad5739eefc5ea4dcda29a4e3eb785bf696b46681380f83fda8a2d16a9c088aced08de1519dc3f601c51d8a31fe5253e8d426a8b04bee440427150785268e1a5500b4393a2ca1fa5062b7192958cd37c4761c619a9820d19a8c7aa0baa39e330152c48948fbcc73e72231ca5ead9aab3421781a46a27a5693e75e47bbe5c585553d2456bb176381065616de668b42caafec49699f4082243e4b0e6d51d4279f09b9dd3104ec9bf321f6bf05819a15b07b4b0d8e0e33df0f57cd544c497fef1c9acbea8a284ba5e12494c0f1f8447d1ce9e97210edaec571ff1ae354811489abcdc0abd23d26c03efcf31d4b72df7acadc8368787616ac208f8a7a64a4fbd00c826c449a1001e0b565de5b1267a86c4e5d88d5c1aa99cc989ad1a33e7d1e5eecad8e933873bb16accbc3fb1c136005997a71edc9d8e5b59f5a4c7e72817952b5e6be82ab093baaa0613d5aca15a7c6aea6f1cff3c4a509cefe3d3b853e064bdca8175787fbd199b993a3ee827899b0ec8e9c4e7c5643a9df99e1d9c923593df9cfd3a9764967171bc277e039e9e01eeb8a74eaaa0b24b005af363a818cc95ea8bb307d5b014b5386defc7f525cbc4d72e280ee48b3c54c902812e23f6a1a1c39c83babfafebb66110ecd6f9c8590381b60268eed2fa8f1108562d9befd23ace83129244f5c6634c6980a0effd7b4896d4392c348bd48489ae0fee7f61dd8e4adc745542fc5578b2d7be0dd301916d950de1a59415025cd18f683aec316b37e332975b3bc1710ddba80c1b06e4e8a95a1ee0f4d2337526127092bce254ec1b0340faebf56c1d0b3cbd00ab8ac6ffba328e1fec4a14aba1523c505dc4037db4e1208e7291c7398aa3a040d303298f778e25cc03c1a1256ef256ae235b37adb588e23ac92c444d994189279e0ed537137e74d46d4ac6496473c03baceabe5964dccb8c24ddd6d2c70c2cc8cfe5c4fa5200980df3d295f1f6f3813ca2f155b533377409bdb4e4f6a0a12f66151fa642ea92e9f764f23da0d0db1f12359df29da42f79e31aa754df874c345c87a116cfa05cc8acdec311dd50ae2e60f7aa1a814b2e10736c781eb5ab1f05218b31c84f06e82ce0a9d4d268ce60de6c3b696c26a908c1eaef1c6c0e072a1c5d6b9e74e8524028d11c0f9fc54f3bbb3c2a79b6e66eeaae2ca2eafa9dbc1d8a067e88ed6d8c4340c8f74449aa30978a9a2ae842c75a8cdef1159ccbf24c644ba0d8a7c839af38398d5a41a348a2e17fa8ff8ded8ed84e75db40b5b1ccba82959351206ad3c86dc954897bbe481003256a07ae99c4e5276b02892ebdfe79bb0d5e2317fd8a44533f73c1d3c3fdb322c0897c2c10606fe943377e56a5d01d37a23c2563da056ff59da803531b7b8ca99abd5df82c2549cb9d7de9efae464cc0c649f2724631e480ef97ed2535f50dc4805476fc02149eab9e1877255461d8627a36fcea409cbc1d65a88d9226e557b3a284f69ba33459519502751b0c5fa3d9e57d87a2fffc95695cec83be05d45ef97499c1f993b4c748d48e2389e18daf742e8cc1c47309f5fe28e035519e6b9a9e2ac983fac2d5b41e96657a87fa1d82f2a4a05c77b8728dc62106bbbe32aba014b8b34618495736fbebe18ba000bea0b883aaf69006b2a80542db98fc885c4713ae5efb4c7ead943bd5a795fcee5f3914d72cd69ed5e958f9e1d909848c32e8d717ce5d337c646e75c5fc5d957d50634afd3e8be4e844640dd10b1ba6f3530145597f8442a888423911ea28a940e0f560b6830bcf80de9668e092b0b5f84e460c9c37a2fe46f4961592c70eace5bad85f34500f0ea79e907dc1bb54d1d0e813082dd87c36cf2d5a810087654a204125791d0713b917e0b39e5ebe396b8d1e8d222759def046d654d618bfbe8b4428f2864f8780fc7ad18cc94d0eaad395a19fd1726120a0768b86f67ed85ef139fadea295548991dffecaa2938a7cb565fce5d8e29c7e8904893014a4890b2e75a3cafc5dde023f1c5d70928a535320884fe06f99c03127e15c857eee0ce4588e72488d65d79ac26dd0885a7285444b534423698f257b27857f1fdb18b3b5e97f15fe4ba0b4ef9efb92670865ed355c6ba543c22ff5fe3052b3e8a2af5eb1a57f957f2800708e9d0c6c296432f123be9fed6d530a95d4ea4cf69b2a7d28105d2ff7a708f98539321ad2b08503549bfc7659b8fe5307540421e8640acf052f1da0c47ee6880cd391e5d5e8d3e59ce47244f48728725feca7d822117ae155d3fd3b05ea4b943d1e84f5ab3ee25940c0c250ddd05eea9d32b3a466b95ac8a7fc45c1930e047f7826f07dfb1023b6114999aeaf19c92129eb4e2ee1861101a8eed3174894e33a44392b19bb2bfa587756e6b28938f03dd163cd63a119e6874c879c642f236f40caa67fe21ed1906731c96eda749dfed5f5120a345c4b80cdf76c5529f247e2308d0b9e19e133b74fec4d2771d8524cca8a0d279edc505feb462ad644622e53002bef130e3e5c9619268596aa0b86e30454511872bec973ad3bd1e79e3d2300195a4928d0a35b93278603a2f2da46f417dac57357aec91fdccf76ec446bf888c134dd23f12f83207fffa11180156784906f00207791cd22d38ed66bbc5444fb0fb12bc41f69b95591f63a70b12b6e446682b8910a998bbb6a515e7b290a31a4f3852cbf7c883cb792435b180227abc25071f4a0ea9a8d445859c37d75cc26dffef07f0264545c431a22d7266ec5b7ce2ad53801b460ef302c40b84c9ae16a28bacfa4b8cb4ff29358b163170dc540d5d40a8bc756c0f00b169958537dd6102226d10bb068b96dfa9ed6538b50d3d829e59622a491a5e8f57d69d24a695018b2fbd4e51ff69b5c4db9a5cf508ec852dedcf5c5d96ad573b6f0c36c18367dd5b5e74ada580b32c14887d98a29a1dcc6231346c786e2e1fe2baef9d1d6aaff75c115103e70b9e6708b0b93128667684d62c7fb17f4184ea1e65851fed622290f5673727573597289952067d0a07c24863850b2a140ce965d2e3604fe86d3fa93fe0e9b2286c06e7001b967b99bad11538154a0caa33d0154d4a2600df81625edd6ddcd1d7e37db86f06cca5d8fcddf4d6f1c82c30720b44630c77eee5e120c4bcd0fa21fc4589f0f631382b43148da7e64d936209ccb33419c68694b919abd8c0389eb004b2cba68a6546b452265d0fd8005ba068a594f20d81b887d8d0615a606428112e110e3a4b9e6ac918a80c1aa338986c965d11b74df84750f03aa5f29bb0e01ad78f2da37e43419be0da3b3c7383d146af8f35a8b48d220cac4685dcc01404b5686633327d2547baef8229c9784f85bb4a1878ccb888af770f787fb05bb832ca0f5c99d3a3a3c8aa74d3500413b4bf6a1d27f2407376b5961d312b9520c705b89e86fe1a0636d57088405deb2b5d1d0d60110465485c85b1be9635a13848096d7a9cd14dcb2ca5d1a0aae2ab78d77d7d0c429a2440923f53f91b7c9baed1a30f7101b0ffc3681214771ed8000ea74a5632f1835a35d2f1a1d3cb4fa88bec7a44a4905c15c44cb857c8b1a6acb5d6282f59f76817c15053fac1a90f2a96d3935cec77f02f45092dc9eec85a72d0a5b74704680608c9e3dcf1c7b6310d858916523b4e67b5c2eb4dc622ec400a179ab01b546cc99a4cd499e6a20d28a365e419b58fe83c6719d1cdeaddb7402166992a5ebd79f8e5fef3a348e7b45c5556153769d04d2578ec88ccada2937a6cbc7603a6e9a70c44f2061516f7f926fd02846f6715c71434f69744402c67a004e7559d470dee6b8126e47e2bf85583bea2ebe3236a08896392d363a1299d42da292582457e5ab95d6a3d074e5e1cdc09577ee3c48e47fc50da61ed55f0ad89b1ccd69a5f8e3204cd51b20330860c4740a8837e00f42512573a90c5d7918bd9311c628cbb3751eb6db683d72743bd336b1ca7b95caed18fc812990af5492852fabe5738aa9b3805fe8742b70a287e5b52549804fe9671c50d4cc6c02648d5a956330d56365a131c18163efad626e6c1c2a9080dec08673a775c3c8871301647f0b5a5634b91203b769e45011f8f828e4e9a96f100d4a52bb3f7ddbb4d5891e2be47dd48b2c64163780944cb15952e8a1d4eef2bdc18bc42aaf742e8e430d0edac1a288e5f6065695df0b3431006eeae6d5c567b2c33f7cc18a6953a08b7eb686b0fb94683b1cfeb7e7e00f11468a5f30b392a4cbf88871e8f3609bdeded331f57a782eeb89fff92795a62fd10d5e4c49b0e60266e52514335988d354c35182c015ae2524479d42a3edc121f18fb31f2a96a9a43da4ba38a57e3da64615587be1650688d1b589afb091a90b2fd5ea3866b154de19aa1a837f9f05f17afd88d448dabdc58cd36cfc7e9a18d51ce8651e29cd1203132cc6150e870dee8e5fbc789290c5ddcfd8f5da83bbe99e901ac663c8b322872f89da0f617dc1c97d3ba9b350d9f21ba845298c5ec8db2e715730a3b52a8f46f26ad9565a4b4addb24f2cfb0b032e64fac1fd907fa4880b55f2bb594b9f50b311bd0d42e662d436fd00cd9ea4acb15de8f1db53d6d59f7bd4a4a0672097503d16517dc6c61c062c2ee87a1bd3557383364f6b20e735794da3d6ca71b464a8b2cf031e45727079e6a178eac908ec1aa80e91307b8a477069898695f0389ad998801a0768d729fb8381f547d3b3594e9a56645f1c6a75ea2c419feef613a2a27754d8753e94f94dbf09959b6651dd9bcd4ac5fcc550e144021358119f50c3f0fb41732e1934632e088b4de5293c54ab23b8165caac58620dd05018b497a441612ba6390b6afbfe35045e363134bdc691dcb0564edc1acef2ced45e0803f5571b6e52439a288ec7990700be92befe20458dd580113ae2079304f08c4c58fe58788132d0b3ff182a12c234f17b8ad420ebb9e3550d6797b2c4dac356fa01419ed7d8d66f6abd206d509306226669dddab54d3b7dfd4dd9ab6833be42530cc88eb2a90520f19fac2589790be30863f25f1af2c568dc73a90984d69532503d087976c457b214d884c40b8c6e44bed09d8aa452a400ad836ee7127e57af22bd414561ecf7d301930e60d47b00fe5c00939adf00fe609ac61c295654e87ef70715f8536c3c586bba40983a48b4ba1ffcf8826cd6ecac5b845fb9114f187d6a2499572b818c4c35ee25d86c98328dbdbf20a8082ea3b34353c7180e0aa3cb1959c65347354125fd04fa54ed3ce302e2af4b361e10174ac49c23a23406a7248567beee081b6db314d5d0e83a4af0994d5a2180911f71e75b229b67b7464212b2f8eaafe3823e73a6ead7ce55d45eceae5913b2766d1669eb70190441435dff9f346d52aed095a9871d5471269e6486f34a77fe41204239d7b8a6f615f858fc35f4da78fe21526ec97a297003cfef55d8412248de13c75f327f330948a0696dc04243cfb5b459e8b9e57b52c8a5079aa3e21fca035628cd85ae5092c078528681f83fdbfc3003250c220b3079edbd5db734cc892dea286689128dc7d3b388ee9c97349a8a4cdd74e221f544c29c8885c15c8b3b68bcab488d354ce07a407458062c2914fa66b0a620d20f7277f4c97954e1fa2f54d4d69b25bfe8a5db07f5c09f208241df301d36a0d7459df7cf40d6927229c4ffcee04a73e1c76d89914d93622d657340e2c22d311fe0cd0084487fd98f790078a2f25c683a0faf1fe6fcaad493724ceb753e24b8552228556880f52649de99539c9dcbbf11030be6883e004bc0a4e6a2dde19ce6f723bd3f46419154777e0308c51f13068f428beacf86486b998086e947fde4c9f2c7724c599ccfb00fc0fa3c929cb88a0d1ba4e8c212cfe7ea18f1ca41c6aee057f64f45e7516a84bf3310b1e817b2363844a163aae3faabefcf2c8ab7236eea940d7a419780dc3b4c60161baa7d5cfb244914da2e4709849d0a6af45f0ba447370b485637f9c37b10590d42cd30c5c0336b7dc8770e917c5f0cd3c03f5930d56dbe3f4d76031002ba06fd5cd39dc91c6529ecafbcf2762887d8d10698421e18cc167dc3ee39bd19f999bc29640bf0b68033a6af457a7bed08dbc6c01a0eaedbc3b9a975f91e84b0e1276227d8c5565018f4be9b0256674adc1cb5902f0b43881bcd7b5e325f798de86a63508fe516421e0e420f4ab96393dcf5262f60b1ecf62acb3c425bb5f0359cd27145a0b10bf43fd5a84f4b45152f5400547d663bf6ffac22dbecfab3e300267a734fced26896e5b81e81b2ba73bd0e6478332e9f58ed9c6d63a2f60970752e67d4a6a33772cbf2701416217c8eec9d5222ceb81007d8ed10d27008020edde50fc0baf32bfe11f0648111d3aede594f8b57aa3ad4e4bcc5f06494b45f877d72a76a5f3b8804c060b20b8c6f13d9103d1ea8d0d2765c754af9c5413b840d9765e08d48e63934dd7d210c081a6701651f710826e556421932e7e0230aaa042458097a96564b1d8c8099aa3f944181a48697659c449e410903faa1f8434cb774480f25cc465d714ba3a3f426f6193cc0c9c1078be49457c953292084dc989404710664e509d02da46a791783a70f99e4e025c4c1130d2705ef59d42ea0d40a11cc54b22521e640341f15643eff935c1a71f6cb42e13b0fbbaad367c4794a9a4d8789f7368c0a9bc4d1438b72ffc8d1560f5fa813c696b29ca48a5198f41af86ca94004676e5aaa4fa46abb16c56253e23e436b0fc0719a22191869180409b23f96aeac996b7a40d88b23c2c896ca6c3df567f354fdd94f2cccd0fef3fcd79b6dc1a43ea87944baf237bc8b2c926a61b8c7c8fd323c454b1d12fd044e2f2a5dffa0d5f757caa9555be325a0caa4ecf7b92cb4fe8e5dc68cf6cc18249a8758a4bbd703bc48dba31f2dd1e3e071f4d7b350874442dba31b6d4b247e1a59a5c1267a1128ed3951aabf6195d17dcf6997655f5164506fce5dba111d830ab2979b64db689438eff09ccb8c7912afc840b760b765d119035c5f57e48ffce997b62370704047c70889571eb0f1c7a8283bab429d7f38cf27487e2be8e225c629000eab2d8e131ddd4d79545c42abd6aff304fb1bb051e602b8a4021c8e24111df635017a0f0ec100d53372f81814d7034c26453a8f76999dc9d9f7bf0ce1ed03e6bdc0c29bf4963fc635a3e38f603f4f7cf207e11455ceae8bf5ee82444ffd50060670fbe5afbb52bf65505dfc1cebd3ea69af2711ce09b0f0d181b82231e139c79da8cdbe0eacd3810d21422ccf6d029078dd22423af500e0db11876e1b9fc5e1114b177a8f2408e0e98b3756a0009bec0323ef01d45207ae16055555fafc7a733bdc1f8d5f4d21f2853645cd081b24d67cfcf623564983560a442f4ba38823a583532da2c7b1cfbe58fd791ed928c7d5d9a16fd1ab1e36a0c0626f2be24c10841298b7fa5da906209736bdec0c4ae9ee7f55a057b63caf9e5ada21653532f11fe2f9782a2dcccdf5a5660d1a5cc60fcfe728f250e159cee70ca12e6ff2db264a4e8b8d4776473ea4ae572a05ca2040929bd2919f0bf765e57818153de5076affc78145a79a3b7e4b1fd7a2e5f8691e125121cf65f7f05631e563a516a7f61d7d2db352e146102bbd6907018aef926be6e62e9471f3e91d497284a63e95d5a0c6cb0b7d7fe29a6b0d2909808c016280c76b8d7c102f0f67661c8b6dd650a7870f66b471227805a7c6865fa8bb177867dc7d5834208b66d2453db6106dad8e5c4aedd3a1aae8a85265606e2a9e45f51a5db1e70f8f5cb299ba74300ac85b6a53b3a0e5fbfc941df88dca0c33e2e834a64234229e5253395d73ae4e08ebc8d03a042d544b5366c6269c64100dd13de337861a3f17649ecdcd847121f2186fd5e7890d3694180fabd4201f84821624b5e7ac3f1207d17d3816aa2955d24b30a4f46c8133723a7cf9961a8377b6b755542223b459f91dfd4ff3ca04365786e3dace2d29c7da548054f2f1399261e8ccfbab79b0b87561860b0fe6a99da8b9edf444e9fe81a3308ecd2a100e802147af802922824c493103d2e141d66feb81ed9881a521f7dddcd89c7fee4806d8c2209d523096ba66ea4369e1cf217cdc58c529dcc8467c51da29e2c5ffc1b9d3a6963b169598a81c95deca736ffed8ac3252a46d7089dca502a501a05e759c5906e6e68b459f45985c478f77c18148484d68c276c068040e181adbbbe6cd248ab67dd2ba6bb3783d943df33715caf64a4f9404ff39c026bf50c30c979bb1557f02b8e3b4dd312ab808c5e603dd9843deff95df173d847fbd77d8919d49b5897374203ca1b41908a4585fc51d07961a000f3409d37580a235f843f0e638918fa60a8a41313b958d2ec720fd2cb6039e7eb65d3090c7580ba6f1029fae355f50f89d3c25c098f010c7843137f8754ae134c4f8ad4b3fd0b02da16b09dc5f32c32d0b0ad087bd7fbd0ed2ab5d957c5659d7f793776fb1319fc1cdd0c36d465ebc35c02b5b3b3abc4a4f84c705b5595a1dc0a53b798dd474f042744ada447f0d5c31e3c39dc663575c3ba0628a8fc548bf6290b2bd64079649da546f0546908208cdd136ad52f256430b3749a50ee4819d45ec952dbb7e3b23a908b23e469875176e7da321ee9a99578c371b7ef19387faeaea475ddd19b2955b80554f9f21930aec57a33d4393b37caed2dc808a36546890fd7efd485daf57605f5f8728fa809b4a93a83ebd5d05356d716a4045754b8d53d2eae0578a52d8798c1714649617032c214152439d761bfb3773089f5a4db271943390f96457dccda6f9e781a6cb797e514971f99dffddcd446ea09b22df087141bcfbe2f2bd8447b2aaaab7025d34546a5f4035986ba3b8f7c07e5de0d65d9ff55ea59308eae3f5ffee32243e8a4685009dc2881d4224ab8772bea804f3a676cd0c5fd0a7304ea0e097aa9b9bd0d49930655946b33a471899e8980afd3ce676b7e19c158c8e5284ea7e99a5226f1306cc770ce603c98d575f5a71bc4e93d4ed7dc4b4a5fb7c998503a4dc13f38901b4a621e1a995542789202bd13b28a508d0505d9ab7638658e1260a1480ce89331ffca6ca4d984984e5bf40c751b9ddc705c099003e9228f648c3d167982ea117662242af31e916cd89badbee635df09a61230090d671e898daf6d3ed0a25e3e9e12fd2875a752eb3c9e9b28bfe77e58c8b16d8917e93be63038f3f9a7621ba38ce55f12d5db7b5b85fddbc0c07770293244987387f97edaafc26aef9162470fbec20737e9eaa6aae880e48ca3844771b8ed82c659809dccdfc06894f513342d7d177386358285369c220bf1f493bb6447cb938b37d9fdb97f02d86bfff272de998b5bf7e1e05a80edda3cc79b93723f75bd4c17de01d5b540168ed667684cb35f335224868d3d34ccd1b914193e7ef3a06720148ce6c11cd695b9e1da0374f95674c82ee1f494f8a5ff85db3a36e8e725ff558ed061e8106fcbcb258dd873684b7b6a0b1b41876950650eb0e55c2847773738728095db1a9ad17898c93992a3ab6a264899d7b201fdc1722eabcf570aaa3567689e4d73868aeaee954ed59d029c6f60028b686c1ae65e7c98e5be8ec3c1630691bb3624a274a11bfc149974d47ca56802238bb02d748be355bb416e53ee30965964659221b8a841ee746dceddcb999489a8737f24b2a5c36e0927f0d23da09d27fb4b55c16fad8e725ef083784298703caf10499b80c9a857eb9023203d9ce020391b30b41e17801cad282563cb2b22b2377584b8306e55886c0d45f40ea0b06a2a61405f9d7ae7564456446d589be97fba4f6f246fe08f67782bba006f40d4743ba10ac84b845466e9971d1a0d0ec62449fac4432ee865fc3e7a1641308e9fdd989e3e93c4ff6890843c4708db80478d47e8bcc77e2c15799c743e3beae881c2cc37d116de7ea15ab4fbed7ce3f313223518dc55c6780483c3452e96efda3d7f70c6b80489b60edb86be084baf54465e11db2999231f6b9f5aa89759fa1b27c305d06feb33673720c290617446149cbf9f71ea00ed182e191fbe99f2175bc414a7505f00d4c0eb014172a034daa2224ad9712913c141e96351b9e408779d2d09df51c1a886793bbf6a8803faf5be5587fad527489f6c898b683bc2748a164665fa6d62ae431db89a4a1fdf9d9489f4633ceec91777c6af099440229f34e290ddb4fe9121f1bc5eb661e35644118a214eb85e0ab46b475138467bde372fee5192e78124f23ceb71b1ad76b941f2172d9fbcf9a0d2426939dcaf8393c90520d3928a3f014dd2d46f422fd590b9aa34b99825a98f976b5cbe9f624b261ca1fb37fbf9435decba71f39b9957987575027fa5a03120b715d2c2bac122439ca2d5e7c6dcbb892a15adf3955022d96959af81eb181c9323c88e1c9bf11f575b9c9842cb6bda4e71628b00bd2f6928ee35f27c7f6147ce747a756ff08757df350c1c59e6e13430cc46cdc3401cd0522026dbfd69d52963d75411a539eea95d22015f1ac2f3b9d9c7eba2db45c44755301747b9a512fc131fde7ab63e2654d5157c4e30012f2ae9f892c5f1cb07df7b5d81dbcbc95099c2c5ef30dc6f815b1d36284f417caecfa1c00c6a835e282dfa569636a2e9c238f5e2372437772ca18b66ddff06712efb0c5cc0d5a13c32df91050f44af6aef3dff65b38e6e7d428565eabdd93fea2fc61b9acfcf543fd7053a057e71c06aa1781f1b32d989ea2d341a90e9b6995e71118e15ed110a49ea4350fb6a3f9328904ab41b35597190e989ede18e1d15353fb21acb80f451045b03788136a4e3d0dc43fb762c4715299d3d6f0eb4756e4f13dcbd1ceaf18d7f72d7006ba7610a5c0e4a3166e49f041dc9c67adb4c150bfec54dc48ebce0d9b2f789e806fd7a8fd623ae6257169b4f4502cca703a58ef7d2b3d4c5862a02d40bcbf7b2141dd74a0a568a43ae21239b520a4ef9fd7b318024ed65a58d94d6470865e4d2019c9c9dd0915f45699a0cc4c59091549a71f5f2c344c20ca20d050a604f5bf586c3a03976ac561e6cbadccb45f98c4caafcf917c4de7943d26cbfc76fde591629c3a58c3b0b94b1c36ee0ce075510f793e4b1360d4dd9546f6985906d1e92be5d152d05c6aceb048cfccca512d699bd4eeff6c46f98ee898ac11b05c5b9d1d2cc9d470008be7858ec0f16ed33d8fe5cc885823f864ee937817a9ff66127deb82729a4530bc87e0c961b6efd109f35caa9c757a215aee3e73581c8a2d4083d004f9ac0a326a1aea5a633e30b975b79afaeada68436f8af7ffd6bc7eba65cbb0732e7624e1860dc2004fd821032401831075b9941306775e62e0d08d70b6be93d8f3b7163f072ab33024d6609be236787c549f1b52fb10136eea95258e4b6ad6d07ec600ac966e110af12e54221e94dc4403d82c01ee3bc1f7872f00d0ae9b2c0ab069893f22f1d330f21aebae20eab767d1ecc1a438b288241ae35372a6e1cde9bcefcd327b8fa826ef541c00eb8f05fda74b98079a8f2a86f4adcde3bbb022e0d2a688c7cf6e63b895fc60cddd57b1644b180265d40cdbbce588ed42e2104497349177ed9acdb08c7e1566876771b6e4d7ce29b73929dc343489e7fcac5d20061d33df65013d025044afa7a844bf935abc25da8d1fca8102be67e570b6f0674ae5952b463126a0aaab1a23057078dd8f8c3e698b0ef70fc19d8e4cf1d62c1a4db26319555e81c6b5a135bd4b4fef49d85e89c9365f66bd392f3f795de6ab5a06804bc4b51b23a3b05ea93e43b14461bf7af88a1c116eb5cb0566dc03dde46f9e78cff6dd9b9ca9e02217509fc4907a78f1c5a7c4be12e6597e1b5bc5edd6f8cda21e481d1554946feb21397643cb715bc400c163b2e60b23998e99a5b1abe535a8f6850c0188d1761e0d7d0bb83189047ed1e24e7906187fb4b9d002d52ac1191025aa892a314331a191498a5cd04440b082796b93c568bf4d434d53e0a4fb409eec6c78e7cd51f7511e08325231b4248ba9374d052c081db01e2e7ad6cdfa39bc464996c11c42e81dd51a8a06f8f10676b31dadce8e6b65a970fe5d9fec2795f05bebcbe8b8544adf5d505dc401e843c27f952f31f3c5ae31fb8375105125f81be44850ff8f47570b0ad9c8bd5527a75b7082e4e22d320cdd18563d22329d35eb5bcfdc5f225d8202ce7a03725e02f497e1348eaf9c7035697ec5df2e62bab7166d3a3126892adc7d125a097028560d44bbb08892e0a842f63f4b3a040b78fbb3aa896326bc40d1f212090a3a5db8e0c3ee88dccb8449cdef609c947c8cbdb88e166e554842bc3487ff37b421fa1b8219a69a6a0d833387b0b222efdcaa6b7991c05f443f496ee99a147e8ded2de997b84ee5db23753cf103d4aa037e35e270ac427a9e33618eb8b772d77769a33d88c8db1ee8b8da88d30b941dcccc2a9a118fd6f14c9d286316d232062a1a7c5550641bd96364280941e9cb4e141eace2013172599e317cf541e7498fbc8ead6df7ee407d18c7426d241c8a5c9df7fe4eb10752e70049095ba45f2efcd5b71cd6256e819e4ff6928e6f75f5b9289ed4b1fdcfe272fa8aadcbef21561b99299d6545305fc972adbbdda8a22b0d69cb4d01d5e0e5379fbbeb3c38f09fe16841cad71e1dbd09718b134744cd1afecb0088c15229963215f3651b9b520a74b7dbfc422315648a681eec5bdf78e51fb8e3e19f59d19906266b86bebc60afbc67ae358294418fa489851fadd17ceacddea352ed3aaed815205b7c24d92f451e086dad0ab7963a2902bd1f4059e3f753170b52b8b5533d02ad26b1a8611d7fcf8782ad4c2f526992139d60487fb06e404dc77a6be534243f9042f387bf992d2e2e67def5f334ea47a25df10529786ed974b8acaf6d8e5e8e55a7029d71b81afee759d1b8103b89d88cd46f76eb7a000d752b1380fde0b4d43af7d83695ef934cb3c7e358679884d76794fd69ad2df37b63dbffca5b6bb616bafae05b792de08ff074046e8a92e3b9ca7de77ca69af7809a80057b1eda51ec4d695627ae5fa7195e8dd81ab9feb4bcb6d2c3d1bc65050726674d294e3d86d3f481fa9ed6eb4f7feb586db20092564ee2dd11ff2c1e92934bb920c58e27bfa9738018b6e29496e840c95121ea307e612ebf99167d43b50ba3eaada85dfa7ab1a7e56b8d0446c59d96eafaf1d9752bd15c8ea5c57b023876d0977e56ea04b6850ce16f4acb6688caedfe3ec23abaf852cd508aef993b4a8d16b155f55acd7bab2d131db57ed842ff6510c3f40c2b4f7b848f7ead02ba24ae00ae626e3d4177a3fa0d3af100ea19a2cc55e9aa32b20903634e5e4b7957a071869456f5cbb573a8badd46cd13291a2a874d3ce30e3e68f53b3123b632c6715afd2ec2c735c99c013f26de959a8ba8889a2829b5ec1219e07ad89385c767c4c0bb833c01a88939573a074c84162ecaab1642dc464ca7fd318f69ee511f765802c1440aabca19eba365ecba3957c3696edec81e4b0395857338fbd92cdd8a4552f07e0b1cde66805960bdf8c73b91f75fe9e42eec83e5b027491c9d08fcd3e776ebefb6e7f0c76334c0c297ce4589afc16be0ba6fcc106f4f97f1baf5d32469f7dd78f080b12c320f67a5143040cf6bc4be2a57adc9dcaa34fe0a020556ca3370a0d11441931e7b732531aba03a303416e705586cf380dd4e9d4cdadf6ec947a1c5bd700f69b1262a7f419203e176418b8e48f7ae32e6603c6996438f4ad4050333458c33db223b498612982df25d5419e97a82d143e0f55ed5b33f3fd73efe8650ddaf60f3930175a651681a9d3aa177c23474fe76c779444e20d399bec53c77d9a6a870eafd811796c3f81db5ef66e084a65e49ea02abe279ae6160f4ca8ea2a83b379c7c3093f27f72d18aac463de19af05144af786a4a657683c9a00d869411075f4149be112b31d2dfb15e41259a765a22af29c954785691dc7692ccd784a58ae613daa6daddc7af840ff687aeda1d1016e50405bf35a629404199645580d6ef3ee1766c7bc9ba2d1e3eba26858a3b2d2b6cf40c6a6e248423aed8cc0ebc85066fd89c3d16bcc961faff9bee82cd2b78065ddd63c2a3f0216786bebf1d5e1d4583daf3527c427a00e8245f8132d7f12fc885fab02a6ff9e54d95151636af88611e12289aa72d5042a88f9349e957b5b4a5994fae2d499ad5df3e769dce12a64ce49e08c04f4785dad0320de0414386b54409cc89fa94f118524f33e469c7201d62e81d4e8ed71f9938698e941983ed37daa53bd7ed8679630b032a9ca4207c6c4e7db535cf029f1b6b1afab988d6fc64dfb72a8432fa32317d1155a20d8f8b1b905255fbc70317088df4f156342cecc5f9ec5fc6f9971e715895ccfc2c8858d1a01cd6c9f6a3f59199a9fe2144fc127b62e22e141e0a4adee51a19c336b4575ff2c3457d67a133a761c400b20126f385ac86c89470a0e92758d1df61d5dfea4478c906d49bb9720b4b0a14f6175192cd10af57cdeb71c53085d9c421770e7c69a67b4f07e406f1640f2418508ee84786648eb3f24f4acdbf7e562973e2fefabba6ae67af90c42d1e9460c0ee8c130bc2d236c64019696f6c48ea69554ff26d52947a237740280360398488b1be986a3ec164be5b204af5899742a16c1d2cbf8dc60c8d7e2943065537c691c83bc8f9e7b502a5efc1ab199b9610532122da9d0e45d5437cb4ca4ba1be32b714319609bb3d0c95de77340427895ca2d5eef8be9618e658192b3b3e0241429b0bfb6cbaa9985fb4d9e1e8e51d4cf66ff7780a2d0caab8fd59d3eaf93870090aeab00fb0f985a6780aa05ac574a2e8885d4c05985974266240c5c38e4c20206f4aacdcbb870ad58d36dd4e62355f4be190cf30f59ea86f2cb44a41a1dcacaa96b9b5135319b59827b1bc5f896fa6b516d09737789fd1fc871a67ef3af2a9bbd984b416705baa781af47be0dbd920fcd4dce6b644e78ad49f622904c285ce95290f6f8ab94dac91b7e29836bd1e44cc3cc2e567723fe414b3bc3a91b5e374080ecd7fcd8bd66e806570168747ed1a2e48cd9a7d816e1e4db77b5b60cd1f65d3fc1c32c8f32f0a2e24e0c4765901bca9f47ee105d1170e2111756a86bb66d3c4857d1a84ec077d6ae8bf3839574dbf3b90c401174329b7715f598c1f599abbeaac71a523b0308d90f2b17d163844048d9deb4c4ecdea10074bf67f3578ac143b574440f9c5841919fc7cc9b0ff61ff2565caf4c4533fea55741974c8d3fcd63a2bf0e05a630cc54a4b957b385660ead67511a872c735bffb187fc35aa67acbc35b7e6ad7b6b6bc559335666cdc06a047598cd30015a2b2483aca770830a915eb59690c462ae7eb4ea0260955bd9688ce7be05d46f7d6365568d756fd558796b6edd5b7bebd68ab36eaccdaab1e2ad0d5a5dec431633d99f29f45865593d92dc6f72d311ba416b5179dab16d059eef4fd446683225a28762a0d788a05b283c16b17cf6db01f2a5459f58da4bf9325db4c2db71bab61d925b821327d2f6b89dcac68c373e94bcafd9d4a59136086bfba21f7a49f5d800a0cec0800d9291d603a4952b50f0d96700ca2ab789ba16fabf2792e74cc27401b2023847305882a00b8e43a6771317bfc291a4638e696bf7418985ae8c6a855194dda64716d06a3ed89f6cab90835699a22d434f818eebce7e174de2579b7091c8cad43d63940a379ea687e9ef42cb351bc52fc139a3187e3a90ab3092a40b0ee767dc7d4cdee2134bcba18c8b6db185d1fe3049c24e40561b53edc2be8c763b52c2dcfafc559305352cd94b82f1bc7650925020042447901662f5f53a5e3e223b763944716019c477c0f21177f89211ebb825200e3fd2a23ea8c2b75dfc8eb52e0335458defaf26e0201569eb1763b53cba03494764f4fd261e9dbe4dead5815a7aa7d574ed13cf1a2eccee6f3ee9e69864fae519e364bca98d94797b490916732ebcb6765894496480e145cd273a6d3d36734a3c11f4d692a5cb1a8d618f2de221d362ca6764e093eb893b6565aee14532ed7c1617a3e08f3168d73aab575b0c89309d7f083f59f927390f70d11e8c1b039c9fd03265e4fe564547f4248bf4a53e110a8e9b2ee039fef023f369942994c48126944caf6ddd1d2a182d4be88157f69cf658dedf6d86dba5ed331c01c55dbc6a993bcdd186db7231d1e7ab41e9f11e5dd7d36b835775481a66254e6ee1d079058ad2d28c26e02aaa4ec7b7cdd7b67ffbb434458ce803b10000af3806b7188efefd873b74288e695c60bc6ab1d4bcfa6ec7f2ab1ee89a67a91c9ffff2242942298d898e8615d61c2990be115b0937f5e19baf90a250af953aa073c9f740f8127f485959b0186ee27bc0ab4c00f5696e83e87f2dad1a4fd7fc56e418215116589548327e5b1ee483bf69f07a601fff00e7c0e7cf47fb5f0c079c0d05de39c48f0d592018564d073d984996615fe9170ffd883d126c42dd3b057d3046d281623313a55b2f575917b6459bd9c809a1d341194710cc08820f7d4fee7086a685fee3ed358cdfb9e1d6ff90aa30d731d7c6dd95d89becbdb26d29659232760d830d8c0d2606d84083b71860030df6157d30005a207c81de7c0b6220821670d0df5b3395fd749c09ba8a860cd41a8f9684bf7ee48c96844f21417f5f1241efb2f34c1b7afb4e6cb233a4d539eb9c7e84efb486b8761a71211157368c298f359488afba3f03c622c55da299d03c13c8575a4e4cf94721e2906986757285b49c27f60b2b856b85b805ed4f7a1d9e693cdb7eb54236dcf5add090b559ad59080eb9aaeaca3279628a7bd29015aa4f2a7537a41c6fedaa0ded50b5d975b41e07b52450c81a6f6ab54356c8410f73aaa623932980a1ad8e4c82c042efc9b3bdc80e71278ed6b1e3411d1c3d0f171ae2f474b9aac69ac9136556f34918d127fa4c9e3d79527afb10d67bf260bd7d68f20c0d0dcda118683972b284717ae8a0d717dc9984c70577dbd6a4526f6f453923b33b71d0a7143c9e64c7da7b5958fe417047be669d44aeda60d19034835e23167050dad4187d6cbc34c749a9ce59e72cd91ffbba9a0d67aa081dfa8477ae2ca0f1b82a498fab2a0550e6509db3cea845685f99fda1800da7b561186af959751f7290d64a6bd5d16610997ea28c149488abb5662a95a3dd9497333e9188297f26ec92ebb1eacde471a298834f7c6867c777b6c6a37d6b720ebd7ce8e5432f1f7af9d0cb879a5c274fb4d69730a8f14c1e9a3995cee715d312483649f19f58e3a79f493ce53f9578ca6d26123309a7e2b573270caeacad4b5770b70de792bbb51c2d27d6c83bbfbee04aada4474f9eea821bdad05542aea25eeb1097eaed3fb60b1bc6cb56e155c6cb86b289a7fc81ac932bf4e3e3e0cbc1c9337926cf54d2cd240efa5037342473668ac85553e6c81c992373640e11ff249a451e218db82c2273f2ec71d5d6264f1288b4ff4c3295d49a41edd5801c7072ca22e416da9ffb915249cf8fe5beba48cf6941736e5ba80532c4b0535c2c244b133ce5af05592d28d630410b92f23b8156543f25029195f280949c6ff83292ad7f4829947069f9a7bcedcc540743b4d180e28cfff7dbae4ebf6fca4a4505d317cd417ef7f537a86bf73f405d7770bbefa27e1696c9c99bd5d9c99949c993f124418955e8f92363a98844f9d25757e9b9d9c9ed4587b88a3406f5028c18465459aa85dff40ada3f6e2a34450c8bad88eb5efecab3fcde7822edb8ed156b96bc5e33a44cdc1bd4fc0e35334b0e82e486945ff98c921f592b39c8c8085314018795925f4aa1842befd7a0def73519dc99c3fcba434ac983f4a497f91dd4503db697cf637ba949f93ca296197c1ea4d63afcfefdedbe0e0722a7175ef49fa137b2a6dfb3680eb978736d3b146f3a0ee220cee840e7a2e4924bf6606947e13634b23dd9cc4c32ea8dc9f6824228b1037938447575d87cba855b914d47ceff4dc74197abe675584ce1adc8eba87be0a0e57c57b6e9f8eacabc0d6d420e3a93bbb9b622dba6a3e3aad7fe5b1157c99f3f77a42393297c74d5f1891e14d17e454cf933c1bb90b1301cf67298c3dc8903394ca26caff923675e1b147ee6cf4e0d3628b8b61fd8c0882940cd79938af9b3bdb61ae854bd379dad0651ab299036d40b499d902bc9a2854bc69c62d8292e16b209550b72502bc2d1cca8aebbe9cb89164a5d5f25296d22065470b1f6b4f67e4cac995f3f46504b2c5ff2c7c74119d2d70c65d4e18cc99a5529a517a9667deed21e5097b6851674faeff7a6337fb0148a512d65cd13d6e4c7d382b42d5cd286669a84ba36d8b624ca6c3fda7fbb22ce6c4e64b144addb2bd37b7b551ddc2d430d28cac81f07258f9ce9e981ce0d74a0b319e1ee330c636118d61e5d89b86a6b405a0728d6ccfafeb2c755198e28673600b4fce945309f2640a69e23f06840f2e7087225abe0f00c9222ca44399910e714d30952c415a53b57681742d011748f9c521871377dd9a258139fd0fe150591a5061506b606b1bd58cab85598af30ac14f78a489950a24946af1f9f4d87d64d67db99abd3c6a3e3a0c6046d4808da9d08c2472b404727824881e62ca9ce59e71ce1ad88de5a9016e42a20925227bea25b93493831717a7d9ff214383d055254fef4651cbe6d87b5b9686872e47095ff883e5b110737570eeefc4dc7417f1a7ae6235f96a912dae3e0ab8a4b7136169ddbcab176f413656a95c19dda027a7eccfc016a94fcec75e8839299be74d5b973680a6de1b67d115551889b97b1699ab6bde68a2e91e2b5c15eaf79af17e578cb5d9ecaa6b3e9e4e0e06c3a9f9cd90f4d22ab902b5a84138e1281269155c81f1ea0ef193ddd72b432a2cc86a3b720a216b6160c412b8a32a56ddb7070b7547959e42a20735bb23189329a19beda7c7c88afb6269eda5e2adb8b522652527ecfb0a5f2298f82c0f7a7a700921b4eaff2141819610b467058a72c5d49dcc89a4488f16245f92a3987ecebfe214489ce5efe883e911b514a6ba594cae0d6971996435248c666ca6558e48ea80c6f70f70ce7fc22aaa21055d9240ac38ee62c4d9db99261481d39f424e6a00c6528c32a9f542924c3b00ae12a87457798c360405388fbb0b33c0b87d1579535a32f0a054cda64992691339442215734c9fc99295a845ccd2a648fb4d9c22a7edc88ab08276ff79c941453d86b49153f3da1562453fea156b4e9b8df3b6da00569453eaeda9bfcd114e6aaad05e926ffe32a5ac5f5af35b8f3f7f68a35b206b16627d6288935d90fa2cd16ca191bc8ad89b81de12b0ae4a3bce96c3a3b329d652a54e9166e61f65b0bfaa1680b5165cf88aaae68a768e2a0a8b3cfd968b52f3427685c4495a60569415a90463b3c8db88a44195a29a52f9f5893c5b22380b4247af244bb90132740a4aee351d25a9016a4bbae061aba9b6118669fed20495c3515a22b8fedebcbaec7f6357f57ebf3d0bee6982833aa193543d435873f4021ba7bd9bd86bb9b2e1ff97e2ba243371dbae9501d8ba9a638ae0aa5ce5cc5d7e6da76e62ab258742e29cb9754e260a5c1dd5ad116ced5f69229ff2c6f6b6b7ec95c6570addef367d38a362c79a8786d4ca22a8aa8cab6d796057dd5a2b9a2542bda72a856a4fdb58ebe60d20c1c0729d5a4ac599572d67aa2e227aee64f9dc1ddf455a4a90aa28de49137348eab86c41a229f55883634899cf1afef3fbd90abed3553b38b58235f9258618342a6fc3359278dabf933ab9036344b275ad0ac42aeb42da6931fcd0c2fca6acd6a75220f72981369a301c999a0b9d28a666a0bb9d2ba2053fe4e76942e9769401a108569405a177eb4229a8516dabfca204c226d64cf06c5f69a2bc9236d281871860279ca9f7e127d436ffaa2af51a64251c65fb01e6943af883350f82a914b7f6fa1ced683b8e9d4f7df6e10371d4495b6f10cd95bcddbcbd874b47f67e394b334dac2b99a5dc4947f18b60ab70ced5b38431c37a328cab8bbfb8bbe3a1ff4459b7c3e1f7d61d5a4a10cff5958ee0d3d678693e6478630198eac26e3c6995c06e108774acdaebcdf59431184d6162c1fe8118660cd2ce39c333261d35292b801fc2d47a9b434ef91cfb4f687c8a5cf594ba51ce2a87dcd231fdda13ba81b3cd0e341ddf0fde94f39488e99f24d7b98687f457026e1cedfa00e1a2c6ef79b463b10cc416b7d2a115a9ea3f232c853294152528a43107d740ef7494e8831a4a31362eccce8e884183dfaf5b297659264a7692f43ff4e3bfc09c7ac621962e94406c530d6b8704c4596eded6b4e9be597cd1f6536bc69689a37d785467ace84274e900c517e623a473cee0a8211d9a4081fbd6b490f6582e2cc9c210e4a4d4bb9419d4743b6a5899ebce8b62efa1acd3f81ca7bdbba46145079efa5ca7b3fb392e8d3d1e7426a437b2d40c273c88fe8a3ed9b9288318803e282b817f7a3fd63e92bbd2218dae699e37ad3979c720a3171fce7de9f51f893c35bfe44932fd70e5cb3887620d8966bbaec90d1d028dc4a34cad9f22114cf1d4c1ecc1a44d07ff24c20b828912bf1b61765e68060a6b8ef7ec721ff097fbcff3e2eb97bc4c393c455b7443a6d1ca78262ea50ded4751d0a3ee2fd649bba48a5238ac01c1017c4bd54a43e71b024935a3a47e1c4d9708cd8b14100238e11d2ba5ad6c810ab7d1fac34c5e5e874ed55ba2446cd731cf416780b3cc7e69f6d777c094da2c003960ff50536602121cd114b58ba28e028e4388ee39ecb4823ede09c7fe7ea344924d2ec48df753e3e4d7a8eaba4a798f4134bfa229d303853d9577cc4fb0f9770a9a7a4a4f4eaba1dddabea4d5fdf7b7fc397b3f7f28e767765a3df7a1c1cf1741c4c1ff171d673a6b49e334a62c4a3b95c4a418b2672c685f02abc7f4599efa91332f565ba33539f6b31c5c723add014d32551661483386a3938923150429988a04fe1841353b07c98322083854452191486a4aef4fd29c79b46268e87a96ba67aa8ab8712b9dbc27447daa4d5cefd0884686a676af4277cf12867dc53ccfdc432a8b461d7193ee23da993b574449076712423088dc1f2619eb104169229674b4ae94de6e8e52bc7c49929f7bc21574d1607e57fd8ced468288673356a713709465cc0f28102218899693fddba5c3fb026d4559ff2d01eeaa23ba39775b9ac6bf43b88323c9033feb25217ddf151a6d469de1c11a7921fb930b4cfd18f1c1af98c5eb4bb19693f7138ed498c2484abb61c6a327fce39e7908dd9220d47cb011580841d1861f930b5b0828544a24c4b3f7e4c8ebbe68f5ed9686447d65a6badb5d6dad76baec22551c6093933fa894b3a877de53c6a19b14b62ca5f0a7b858b09da5a6bed67bf1775c55cb5472dcd711cc785f83a28ef4f90c402cd20b516052333f468c8d2518cc7e2e86d7138fb4e7c71846699f2476621b10e73ea7c247b1d9ea5bd1e1d6b51467ec4d37dc6a8c595fcade96d8abf5bf4b635d1e608b918b359387bbaf29eacb4c9720c2880981869239bc4c4263220202641fb2740472b3819628d872be18a130751f2a546b911ede92d83e634929c592d65f808ca5794a79807cb43f7dd2c0b499b95980f3d983c9905492400507ea0003201c895d4a2e210e978cb6be02cf01be4041165fc29ad3fdcdab262859598e7b8c89a4dea39ee829598b4a17925286825b612246d5684568228a52fefbccef22f3cb4af52b5168a9048ceb88542aea41632e533682157514a9b1e71c66b221558d03407b8599671b475b56ddb342d57825684caa049a93fdcfa9e9361a95d7e0b59c802881e70d1a28285050fbc90820826f40c018225c4084f10e1ba42cb0b1d7cc1b20013b45802a70c22279cf42003a30d200b580262ea90eb60447921ac52a954fa4d43b5e26648928c16529665d9d7d7e1adabbd2483070dfb234a4774341a71a311371a7d42e8d8d00f973e4dac710e4b9a9a824bb370f74a0c8cebe94df382f6f2da9764b82b04735ee14a6cead57522da5fdea0e06e169cedb7bc12f34ad14d51396d1567eb467987a6b733ec6f4457786e1b4952b771ee7215b98a5c9e41778d46dce86b7d443e9dafc8e7fa8cb82a7b95140f46a39253728e99a2f9fb02517e72648f23e68525ab35ab59566b56ab07a381843b9fa341a265dc0dca1bf3d0bbf02af8907b610528680f69175684a48db54ed82c5684a8ded6711cd5635a40cbdc43e9b77d512e5cd73fd3bea819576af92c3f4450fa20aa5870f496476839847c99e32a397a2933e8750bd76a7f69aa85bb3d0c638d4923d9ae25cb0f51e560447004e290c9f2b6f9bbc29caf5a6b66b35acd701575cf818144cb00e36e0128ba9be6835870a25eadc4624f765cc26b0914895883136b465ce84d91d05408d683d12cd8e711a34799148b3258b87b4548862c431cf4e1dada58e3422c2a882a072364c109af40748558dea1692dd75f1172951c8a352e1486cd262a7de542ee0ddd6d5f34342e1a96076ddee0a47165360bc39086d658148b421a1a1a8e86868626005eacb10920451b455ca1bc83d35aa143af8b88923d985b05904a94e86efbd266ca71e6aa2473b5da88db685c45c13c6909bf025870e48c0b726c96d92cb3a10dc2913657ced0cc940be42a0195e54322ceb89c07eeba453d984ad3da7b3f1616d3ff4c7998e34c8561286598fdf627322bb950580a8570158e0f7115685fdbbe4879d3e8edc1babc73e8edc13698c3e288f91c244a29f53c2f0769000418807d51302ec9be624d495be1a24857550368d05f6ce8444f88b43f7121ed9f850e4e1c32ae949fd28a35f42ba7001d9a45490b60c0cd88b4a3e6b83952e24a18898f91ae4d624ce51e56d79287f2a97417f2b0be0b0dd5ea524af943ae92d9ad9531f5d97316fb90832eb3bfc9507c287b4b02b5ee66c16172e9bd3a3299c212cde9f8440f8ea0b77d813864a2cac170a12813d24a3d9857e485de9017d3b4d224405e3993803ae79436e035815e90f745ac71302ea7b70703e3a2768cb47c4d777a7b300f26b3adc27d491f22aa3c25d817ad74c6402ad139e99ca6eee625279dd20eb9aa075f954adc024a257967a5b452ea61093b4b2500d8a43db8ea2776b707931e6a5aa910945443e99084c1ddb38624b17dcdecc7c1166dea76805a7e099b1e057f2ff11194e73e7cdf84bd47c148b2e76c48d6c76d13d7c1071cfb9229b324ef7c0f7bb199725aed7d7f1bce950967157b405e90177a31046caf2928200119cf183a14d0dd5007e91307c01dd228d8bbd81b02b517ca19674966548f69019d65295c942f6d77c180620d0e254495cc04c500c500a5109ef2f79c302e90b9b23e37474f4dfb637da618775be9f37392af927f58d1b3b23e1a1dba2f1d34334529a52a94f7d731431c145cba014150da4898ecc1870a7a6194f160de1551c6eb22ce78587814ee8e29b34a236db22c6172a6c2688a984d5c4a2badb5567929a536cb6c96c91c692383883326905868ffdb4309a070b7072b8aac9c300c73841e7a98651edadffe24b3250fe6c13c982572d5f61c9dd998aba685d9a09b72439af4ef9c33ab35ab559368ebd2f049a8c1c22760dc4db3485ec743d3d9bb115fb90f92bc931a7185624d51acc9dadf06cdd54a50906de22b49e4294904a42513d60c7b8568c7d8160cedcf80ce5d510687ab55bb21d1d6bde9764c0fc88db82a13badb83791865beb8db7356629ce6aafad987b0e972d088833b42be72224ff9bfbcc4c4e0c011430cdbc350cbec311c8de3a011ae17e40139983d98835edcedc170c2b95a11f294b3e00c61c98935dcfb732b4fbaf75f89ad10ad14fd8841bafbd16865a88b2b75f77b45684528aff020cde0a41e93d4001f95444346aac101f56bf62b315769d409d71fc75543c2bb578284b43fb783879366e8d1001f19898611a9868e86fc66c895a01527b1c68558794254f910be92c3c50542750e17777b300fe61e90afdc88a7dcb5c5054273e6ca853cba9ca523a50fe7aa945dc61a0f28ca9411559e1222e82fb3077bc2ccb207a34eee760f261760193072d24db6a3f2a09534c3d43db80690465d0384e89a5f62a9e50bf96ac80a401692fa4390d85fc1b37e9067b0908c18d0edd0b48c5a0af90a898d5976d3659947d461a979f0e031c443a1a127b16614ebe20b1f23dad489d3751ca9eb485d97b7108fc80bd2dd8e20ed3af260f5e8bda34ef09c687f1ba3ccf6c228e30175405c6f8f76d8bb22caf893b2ef20cae4d27bb0d053fed6decbc2e21e4cfb4f20dceda1b419c2bd7515f7b7bbe17ab0d9c438b36520d288abe48e1f116b46afb9f64541126dea67b987281304c7c1215106450571c65a6b4b1e9642ef41fea43430d22c9572101f582c2d924b24107131664b39374dd3b49ff3ca2c73e4cf2c2b692467cb55a37c1df4e79ce6ab658e844194d1712224e8ae1968ff0ac49dbf65cbce929e735447958b3a5c878fe868345222b3d0fe94662c80412b88bb6590141a7a39a1c224491da4ae39d73399a4e4430fb3759dbc68385277437d5e5c121e9e8f9aeb78844968d66416b1462a71157d0d9c29ff9932bd96948ee8a88e280eaeda2c1ea0f1d57b8e185c953de8343f0e3ec8e2a07cb198ae4c39eac10711c819972f1fe44a42318a41116a6923c38833b20941918c212e28ca300188abcd956cb582642b68078a962d9e1e57d1979247e813f6843d0e46cd23ece9f199723e8ff035318fd0a7aee04e2533f3087db41eae932ab8f5631cf47f077b78463cae923caf1e5fc996a73c89124ae7f479250979429e231c8c3a09e5893532c7c1966bfa83bb73b45aad22441c0cd2825a419f0157adb43973bc4833c75ec8011763f62865f47ddfe8fbbe144a294dc1cee2827c4f2b57554e1afdbe6fe54f1afdbe4f659465a32cc341c6421953c1ce5ac192a51d49f90fe758568a8a4a2e9148f387f83d0e6164c98739a8f2f3b3b75305eb949494df34be77158b1d8dec68c400f0a3ccc447be4f4951c9f385b0504a37cb766c7feb97c9510146d9ca9969e516d266063923b7902b1b94f46167753c4a3acb5a51c66de4cc206d240ca24c410ca6c84834cb689645208e281d519a655946a794d92440ca5449f9cd82535252b22ccb94eb48a8d19f5e08ebc328fa529bb4a3a0e8aedb8929ffee632c69a5d2c72ebb2185e21bbecf92501e07ae43f1d6762380487df78f58544aa99364b98c8e68c6555436a27444e9ccb9d97393345fdecc81620d55425cf98e3f413b1184240a62ca838080b37098292fe90f3b8b06715fdad1c8524ab39ffefe3363a1948eec889eb815122dbda655469ed7dd947ea4614f19a960c95ac12bffe1212a7feab2151ce4b33fc35cb9042fcaa5414403f8abc4580093f71ce742925df71267d6592a2ba76ce5b4b2f22c2b2b7fc2a81b58fef42ca715fa1aa7695ca6c5277890a3e973a4ee46d33794eedfa92ddfe8edf617f810accd89701deddf8d48238d562929a5a511cc479594cfbe677d3958dfb338e8309619261159246333cc6c278eaccf7596d12cfb708e657d1fee41e244194f8104835620b154c1a893ca0b61712af83138532a2a7fca3f530e824fc1a81b4e9ff2a70cce94abe498cc1070d6f75fc66132b9337640902b87cd1d69e338dae590136111e4107876a28c9423eb20a54ff0c0a537496759f65d779349d9dd481c66ca9f8eeccb2b7198ab18a3166398fd0ec95c48e477318ce028e76d7f8cc0622aea1cd1267bf937124067389333feb0b9da404cf98761ab002b0307580c659c7367eeccbc5976e888ba8eb4c95e7e0e169733f2391812f742a6e9141c5913cf3063108ba674f21092a3ad7d89bb1fe1d2c79aab7177535f057f4f736e086291b6bfa3a346dcedb0205fe93d27d644263aaeec86ef555e250739a560d40d29aff2425825dc61ee8465d0e957b20ccaa8ec65d1ab60d40d2baff22b59861995fd293bce4ca550598680b3545e25cbd89ca920622bb8db7776b6c9e4ee1866449aa35194e56f0484e6a47078cbfc83be5f11653cc853584830d442fb5721ee7698128cd830f5c1ad2db8da23d182abe5cda26596d91ffd7627d9c88eb003398843c6e229f9f36b69144680092ec66cede6b83940a02591c37364930136d0304441f0c20058b34a73b4b209c6d2bb8f76d84c95b2f34cda13b90e3bcba5e043923525e5596248da64990639e34bc8d589c6263e4483b4f11c3993639ee44a255dad28038328e3afd2d9419c21a20794e4ad583389147118449b1983fb01007330c0411b22083e07035ce538393b5a8e96133fae4a89e1b70cf4517e93c2b82cffc2bf03e133719a5042fb9e486cf0eff983fc7b1a51a3f27b1e31a7ddb1ae17807c0f80e72ade2691d2abb852d5419e01c47bd37f3885fa505fd0f5f00e56eb20cfe0fca5a6fa07496f2f6408900e66e0832a8a90c40f718c254da09841ab0a2342af12f7df97ad11eb5ac2ddf6fbeffbcf4e97c734a0d24af618f6220dc683e5e16abb32634431200db6a262995ad0d58234200da6c17a7a7a7a7ae4fdf04a8daf4183869c355ed6a8f13a1cc78f0174f4d9a28acc2606b126ea95f7ba1bd557cc694e5e03bc0ebf9ed7dd18e0b7ee069577108d7a2f8af94d73fa3d80d4c67a7e0bd8f2ab9a9767a9f17e7e8d35377e7e266d4c3ff3fe2183a64fe33fbca9fe8a9c28e7d2dfde9aabadf54276c9fb9e4e1f93378d694dd882d81a59617b84a7fcb7d6ac7fca9b86de5b6bbae4f9429e30d9f54bb649a28ceb05377422ac150d5d17dada8751bee26d95d8c1f5a44123c75bb3fc43063d3feb7c98727c78a0e773292493a6631ad088e4b1a8576ab88a52cad3f3bae24787a34c03644d88bbb51d22073d7acc631e6bc554d4b12ed7153b26ce4bc1281f3672986fe3a38e01a37cc090037d183e6a2de6e0e7d5e93df5beeff3a256a4116931770d46916ca73b3c3daf38fae766ed4c2938c660ca3af0a05a8720daf43ea63655ea14879e3f31156136e8863115f50d0c8c38b48dcd9506b33158ccb5f23ae3a835bdb528441d6a415a83ade8adc14cefe556de2bf4319671667ec633cacc57993f5bf35f8e3c96a648bdc889b49846a4da1a4cabb6a623371c0db6e514d1602b1d9d2b6057045d115e31c4a582b28e3e5b1c510b803f82f50980c108d64f05e00110801c13c1fa03c02f11ac9f03b344b0fe0c4e89607d01e01bc1fa30605204ebdbc03682f565308d607d1cb4db016a530070d400d81193c2517bb7e35f58522ec9a44d5987209abe4f9d4c16d17346a1f2102146851610718521213e00224917703005d71928a076c71ab150b87bebd97adec375fba03ff961ea3dc5937a38eacfc3527f786aead5546bc5a285850e162e2c76a80bd55469fa1aed6fc2242346b365fafa55c7254e284273261c35f57cb628e2b3059123ab37e11d268f7ade6fde7b6f3b1a8468cf7b1deee51888e4d85aae32656b8488ab6a3eb27aff55763da1c7a930877c9e4003ef6bed76d82eee9eae7944eb8bbb271562cd354dece269d40ea9e7f7ae5db80fc72833699e3ab3aba69eadc5627e45110c0e0a0b8f561b2c318282d5d6e358e84db79eeff77df9f8c2d717f2a664c351c3e46d5157cfadc7554a5c45e744b96fedbd630c318345062c5fb0022870589cede26a41403030eed66041933eed6810b2c3b55512651c455fea4a497ae61f241d1df48182891ae06009557cf14310142431859e19fc50c4453eb2da7a7ab69eabf2b1bb59791daef232e09805133178b1c608d271898e3e6310d1f86ffcdb2e6e8de7a215bad167c98fded1fe1a6cee708f01693bb186e60d28caf8fbccf38928f32bac711a4c0372d50bae82d1424dc8f35ec0ae323d7d18d8bf600d48f3b00d1e36e52db5a95a60664dc894b530af82886005cf922c9005981f244f6d9aa613dcefb7c7605a45e4aa08db602b3c9a744e6dfa89e934ad8eac22ebc328d3479ddfabb57ade7b796b62faede7150421740e1d5f489c688e7392ec5c7ee21c288d0e9e7e8719394942991ef52fd8bb9b17ae66940fefebf3f0bebe10bd83e84931cc14a227dec1354c962c9033a8a77847271af53ae60b061f855bdec3d1c1d37c17bc8568978fdd4dcbebf00feb98b2eb31d2de7fdf534a5f877b2fe0a8bf177e3ee7a40fbf50eb4f4ce58c3659a0be0e37cdf9c2cff994bef03a9cbef03abc7e6fc2f32b7ee1053c8b6c3f5b934f876f3eaeca6112f171d055391c59fdf672d063cd2adbd84cd9f84d43c7b05c1df3315a1d53e3cb314f036f1a4de36d1797068dfea7dd8e1b8f6fe04da36fbc75ddf8d5df782fdf781b371e861b6fcad153377ee2942873e3c6d76c63379ee6d80dacf236f0a6a1b775c5e03d7da0fd6be08cf7d518eff92aff78cfa0599e86cad6e3e017f73f93e9b35e5cd37fd6b5824f3e5c14c5d047094c13719f2918d1353afa8c31a47d9e24f1797284898912424e98285184891244b42ce2abf8811ba35022a45f4320f21a821076c755a6a7d1a66ff9ef39aedbb1f2a7e76247a9edc28680467f4f710868740eded78ec7ca9f5adecb9b467beff22c384619d4ef186a607ea7ac687ec3e89adf3068ea2b9bdfd67b95b7c12fe01a18f47c1a3c7f85e7fc979779e63d59f6dcd953085abbe016bce77f6fc239acfce96d77a3f28346afe42db50d9e112988d0f3f42b359ba48aa6afed942f6a13fe41a34f38ea9553b63b0eba3542b50db5586bc65a3505a70c41a3e9574d3303746caf4d898e4f084a4111444378020d50c0721b461812820dae90040b94d0627335bfdfd6fb98e9378b5efd6ccd2f6febe5ec77f5a6bc59f4a45b8f9e3f3dd3aff01024d564fad9e2b926d3a3743408d1b58729d3e0a0ff0c7288fbe365d37bff4d1dffb0874d186faf8d8bbbadab47ef7984ab92b84aeec9c3e3aa9626d2febeb5669e77487c1d2023d95a4cdfc072622ab6ac4e4c45ddba81b9622a0ad930f57cef22b07a9ab7aeb95a65ff07cc94bf3f81890e807852851fb004106345d6ea77ec992b9a3c7566ca5dba07ccd61697c912437464a20491a6a12393248ed0b3156b60106be6afde3b1f235d57385a5db164510c64128932fe3b584d03ed3f6510679cc77c1d5f4150325b397ab6527077ccfbead66ccd199f70b92f7d6b7b9e1bbb1ed6c945c92070507e8763d861d3323388b48ca182c041c761b65aae9ad35574e6582308f45cc5bf71a34fe8a3e3aae8132a21225d7367b6a60e45e26ea1015c8aa1447599fe0e20d6c83f8076ebbe537a34678c314a1daec3c1cb525d7195e598cc69cffe39a2ffd6b2af5efdad1219f838e10c28b010420d560ce1079815662c910596fffb831f65fc62e152874d772c703166732e2d3f823134c6411c207813339a3eda6116e1d62e669ec5adb483e891a679f3d0a43ac5e55a7b8bc91b7f1b7295f5917bb60d65345e1de936e4a07cc75bd8ddc41cefeb98730b5b5a3ed77255cc1bcc41f95b132dbf5a71f7166e43b446ca187f138a781b927a6fe136e42aa975120765b8096d43913edda16e30540ffaf163fe18f11558c725314272824335c234378ad13934b771955229a3a914a1d01cf5a1e2fafbacb492e250a5e2ce1886e646a48863179ab3d4b5e7ad839e59b7a3a46b763da4a3c0824757ec325a6b3d2ba17a5420bae62b8a74c4d2355de5cbe894e65cd63036adb8bb0e69f9281fd28996a3dae4663d5cef095809c44d5a8178468afb47848541b6be3458ade2660f24d334ba49a64959fce81ddc4788d55ce98592854befe05afedef49e59dccd92c5cd1ed523be7fcc350937fb88816c11bf8e19d523525d3310aaf78f08fb1161baf63898428ab8365151a12ad3656429d5df34a76da488f1159a239122c603d05c8914b129e21a230652ab8f96cf79a3ac523a5dcab849bd44732414135abed458674135375dc61967d6915e3b15f22b155789964ec5dd954e77a7328e4ab527cac82fd1e99d8c28259491f6bc79682da6e563ac33bc6bcf15447870e62a8291c4121e8011dbc20f7058f2b99cb9aa80901327728c5841104a58f2b7d856b4137355fc296ede52ebbd99a1b37c44db886273b585f2e58731c88b32b2a0220b58e06141841531a2080b3f20110422bcac3b5ab774e464c7636afad64199a2eb73b576144fcdcd9ae39c3a76738b6d3107a5945f5f7375c222280b155c31861167b0e4d79fb9724962091b18a1080b8c100396fcca537baab89c0b46059ca4818b313b878c0e4a4f7389597bd4a28c3fad5256aac5959c52ba74e9f2373e92b93b2dc26749d07f0ce820756286a61fa378424bcf918d52d378b83cb0e8fa53c396fe88e62d6ff65c8637cd7bf47494d167d1fb669f72647b4fc5546a444de5c2ad1d30e208289ac0d1820e8c4c71c4132fc0b0412c4706d9b304e0de9a59f275300aafb2e6982b2144604117b680446c084cb0205ce2084a90820a9630d1c4f441f50f77ffb4810914392f6012450f5a0428a28822805084176061055d751965a89e5f733cb2bd0e8f1e802114c131a2c4185920430c3196e0a007346042c40953e7982b0f4c0109601ca1c290278058310a53480291a223f8f8e0072c8e05579461051344424fb0be2b9c48c21244c8099960f9733be0620c1077f2a0405cf9d3c69a520f0410a181e6ded4c445d1f1091d0069fa40ea6f1ff186e3f65e47bf618e521a07637070ded24b8e76a4a75d0faab7c781c3c149ca3b8826fd244dbd65ebe0de4173b18b80901bf0a0c00a3380028912b860454f917ec3a5a7dd8eed515ec7dc8eac3c0afe415ac9349e9ac1cae0aa2d6fa9b79fbf47dafeceb4e969acf9bcb7ae2abd7dfb32d6a8bc7d8f35296ff3fe41d2db77f33b4cb385bbfd8e21061b416b4b3fff8455b08c32f653b04719fb1fa651c69ab0fd8bb57d9491b65e7aef7a585dca395c65bb9f2fc3b66df90749dba79d8fa80420280108da52d2db2765520e0149e77010c7b677d0f6e773984670b33a04d136878393c65333869f9be2e0fc18f4c5617f7e8ee96f330e07e78f7c945f1c9c9fe51c2c15879ebf5966ccccc2d5f6eb14df82e0628c749fb40a3918f56bf9d68cbb73e81cae8ac1a5d3c4e0d9462146cf176744c18a1ac81411450a685044032d10b170d091c1138c54010a4045b048108519100103838996192cf92fd78203c223f2a8154738ced4a6d136b2323b53f407081ca454891b63eeb4081763767da9b4b4d9539ccd30b3076796d9e8ec59c21b9465318bd9c7b9ca5e70a5fe7d6fd602fa5767b166f4b5b44ffaca54f63bd3236d81d0dc94ddcd66f30ddc07197de9236b76a8076ca48f9dfd20f4b9b7f986ed4939086af42c33958df20322cbfa7b37c21708cd4dd2086f4068cfd15ce5b25169841d08cd593c7ad2737f01d27319a55da07b94965110e07efb1bb8df721024da0741d94abfbd101f526b1f81c8ba0fb86f71664759a8b3b70fe03e0291c565fadc964f307fd3f76fa07f739c37549d6979844719cb4c65cfe19942c11d46c19155eaee4c65b5c7c1ec47992ac18382007dee6f98bf7de571307bab4467cf6da391cd47489135ca7ec3915579e6aaba3c957d46e2aacbc1ecea2cfbcf1eccfe66cf927dec1e3084fefcfaa2cee1808b13b8f33dfa5ce1a5b7d53186aa38203c1c45c74cc5fb38481b5781f6af3a1c902b1d624832a416b4eb78d9d7fc2d6f163d726bb32cc3a3e734cadfb7a8ecbd17c24265197581fba6bf01257b94cff20df74d6fca414a52cc00091790308283c3ba2f8455faee33d767bfb323748ca9ec4bb8bb89d286a2a0c3251c59b2b3e1ea2d1f317d863323aeb9ca4c59e6ca6ad6d236aa20620b20a4800a4850c1129263031f2759084308232f60f9d7582dbaf58c1b59f20b218851840d70705871a6ee4c796421e161eb6248b42882115a38382cdad540fa2dc39b94fd0f92e678909a6678e7d87b6f190081ce3e66bf6578e3d0a3e72c89c4490debd0ddd82c038d0a6e36e4ee1ae3baee2623495de4233a38331ece371ff4f42c9164899e1e2649625a4647264950d143001d7db8f8415b97ab8000d9e16a13cb5fae62aba900a423908e3e54c0e1d11c4a8c41b92fd4fcea5739c8ea6b5e086b5f4df3366f9383a09a60d1bc10d666d1f2daa8c14110f032d0c85b88a661c35ae1aabce4a2074394844ccc700203a060228427bc50c10c624031c7cdf9f2f3853ec78d48b203f316a2c1d7ba1bfa26d34f93c9647ac1514ffa2f144f8a27a5efddcda4f4651299f5e575f8c42f2f2f37e067302e3f3d53deafbff796dff67b98e73ebcbd3625afbfefb79ebc6dded79bef7ded6ebcede5aaeffdbfdfdec3e44d89e7e5cd85b81ef457616afdef83f13eaf7adc24c264c2b3256766164e224c1e9149a456af46ede1f9268c727981c19b92ede520b881ef5fabac311a04b156e43385984ee9e833051fcd818fca5b068d6a4d14ea5b3ea2fe848a9d0e52a3ded472726939b97c9f8ffaa6e751dfe47926cf8389f90fdfb8613255134cad28fc82bd2e313764d02cff02cbdf98b225aaf9ef5192a4f708845039439fca191dbe83f6ea9b7e90f4b711f16a172eeabddf5b0f0f3a888212b2e8420da4c0850d442421064d9002892158d85a3aaef28fd44a1ba9e97bcfa170ddc245a1ec0e7ad327d1f7f0f683a45179d3c20ad750cfcb368613f036ef1d050ef035efdd0d346f93839094b068debb1b565f938320c0095fc458ab1b53b6440efad7609aef3d477d1eca4ae17aaeb94a5a619ba7c135ded5f03d6a07d52e8f42bd108d3af5b8a05ea7bc29a9ef4cb81448c0d37cec2870805fe520528a1670f162ad3e7637d43c4d0e826a8255f3b1bbc1e683c82796702164d9bc0e8fb475bd3c752691a168caf152ada34188a6f9474c6f3dfff2f234cfd66807776fad047c8d050ef03634b8e6637781d5dbe420538a2e42d6aa06d3fcec6e4ccf49efb7d42653b6f1307853bd6964d1d0f803e0790510163e38ac04e008c3c16171b2bb39b2fa5502f001f0cfd4d6b3f968969962e26e1ba3795362895c35b796dd94c0c412404682c0852ab480041034203a6245114ff0a2e5e7a330eafdc88df7222eea515fe329be0ea21e06a35c1ee6a556c128975779f0b9893f4b74f76cb562f2dea1de68c5b06e3c27bbef3bfd7f180618369f1f22df4b77c345d3f3f07274d07b13f64e1fbb7afa5c487f1aea3949da94b80af565cd0503e901a25ee56f605ec744611b0f83a38ec1dbde78ac01610de6e04cf23abc05ef1f295aac68ae341f58d4b42456b85bf3d1ae35d17cb49f39a45b748c6289269a9ba4ba438d4c9717d152d73c8938485bb2377ddd54e7afbf27911d527bb3f52edfcbd31b0f8302f1d6335b13e7cfdb82795f5476f9fa1ff8df9f7074f0dba7afbff5ccef61f83ed49b3ee625ef50f50b0c36f5dc1b7907d72fbf33d0f430f4a97ec9397cfff2fdcc19e6770b4cde2e54352ae7f0fd4b8f2fe740bfea17d329efe0da45d32fef4944d7fa3abc6252caeb70223e44b616add4a31fa5744b4db38ef97d4fc22f1fbb6fb6a48df77412f1565a8d98fad385bc5cb3e761a5b43ead986e21947e78e2979727fd6cbdbc90fee5e99d3e9af45cec6ce0f487b7d4dfa4791221675a73b59aab555efd97370bcd7b79dfaf79db9a5c236f4b93e34cd5f81898fc6fc2371ec7680df6f4eb7b29daf4e5d731b71e18de3b1ea877798ca5d6605eae99665484216fdbf23572cc6c994c0f9abec5f4a777c1a36ec78d373d6749b2c3ef82511acc822d274c7a9c9f03710bbe8133e92711d2266ad2c74944ff5b32aed4fff95bfac17fd293be65c2d0f22e35bec6e9f1df88f91ba6f7de64fa98cc84bfaf786740575335d5faaf6b46bdbc9737ca06d49bbc9f79eb0ce898156fd473303f5f6cf0de6403eabf37515a6b46bde41ea81c1d34fd977f3e35e1fa30f974b226fcbd349d62ece9141313a34f356274cce78fa9d1f220be71036c79978f5d0d2deff22d2e2e2e23971697077fe402ba3c0c2e1986071d86af1886160e04c1a79f6564985e6ad3cf5ce36546f5f89fff2d2d4f32c9ae86962791f03f89f42da8fff08ec980f6fe65bebc68eae2925fc097bc5934fd9da25dfe74ca30ef82a38330795f0a0313f3debfbcdcf82fbf87b98fc1fb478c36fd8d47611e7ab88fef617a52de2d6ff26c70eddd0e53de72e2ef29de313de67fae29de373e06ef4d3d6abcf6720e2dff35f0be917fc4e8964cfafc3a6686afae3a06bce38fcecfe19d019ddfeb6e401bf3ff196f44b6d6bf09ef186dfa4dc737cbd5a8df36889e799b3e3f97ab70a9571f26bfbcf672d5e765ed31d5356b2f07dd84413deae1fa7f6ba560aa37229bce8bcef9a393747eeaab2ddfe41caf6ce99eb9a231457fc768ba33a0e7fbbfe80d96b2fd9832cd9a12077d5c1577103d67d65e3886b85b53a229719596b5231c74576ddace5c693a3ada8e4e4c4747257c9182190c41460b8834c1c2a18226b250a4c9112b72c0f2b7315b6463576a1a6fe3635783d434dea5ffe56da1f1f9b9a86f8cc20833340e1da33002a68f1042e77731e2e5434abdc33c3d4c8ccbc3f0e05357dd78f05d4c3d3787fadefbf8b8e9839a4c26d457ece511488dfaa8bd9f39a0b20e3db4f73b5e1ef551dbf0f243cf4765ef51348c68e9fa378cc0890208b1287e20449fe364d7e3e5e76783f77504527f26d3d3e74c78beee1171dd81ea81cab5fe0bdea8b7f1b5a3c1e5bfafdd0c52bb7ccdf4ffb951ed1c2075fd3e760e90da659bfecba69e6bca3f52fe636783cbd79c83cb7f39871b5f7fa498f28f141df334ef1f29b3f341e346a60ec6e46ff99a370c393a083e75109c35ccbc65ec6a383dcc9ff0f641c33ca76130cccbae866d7a8a83f6e6d4b2ab61cf374d7dcafb041ae65bde7635b886f95ac37c53f6f26ef91da686f94d738dbc8568985ce35b66d7c3758dafb8469eeff2346fae05d397b7a56507a92be96beca27c487dfa7ce36546f5a0f1f369fccc7bd32e39b87cdef4e96367038dcfd1e5f34b80c66720843abfcb97b710fdbd8777cafea14d94d2b9fa9797bcc1373d0a9561de7b1dd3c336de84ed8378ff48d1a787f90ff320f5e9bdebe1faf4f963d7e3f4396f973f79d26443747067407bdcfed3e1a78cb7cb9f9e3be1edfa858b1d8f970c3ec5a6ffb0f727bca53efdc4dbbb1edf7ba6d883be29bfe0a84fd8f5c41be6b507710e2e8f1ff5a7ae4774f0c67f4f531cbcf1b2eb31f30d8dca39b83c8d1b78c3e41f29da25e7b77133a86dfc86378db6f116ef1fdac697ba9b961cafc4d19fe3fd91a279909ac6ebf0ae878aa6f125bcb5a67031de52e39f78a7e8b9a7c6afb71e4ff9db78ce06de8850e16ed4fcd15b36019a4966cf6b6fadadf53a5c2ab163dc3d5b34f0768d5334c64234b5f152dbb0d1f294bf0d1b3345d31a340103657d7e6c100f54e7609d58211a386a1b986a3b053bc6ddd63567e611352933421a45adc455231bf4f293339162e7637e7d13ce6166940faf66540fefa9ec76783f64d026d3bff0b462963761969f1cf7f544148efa433d6a892413d58342a1b84b22e1f914cb247286f413a5934bc819d2cff9a44822fdc4a311e905678f22b16424154c72b0b3566bf5c00522bc8ec04512c2d042035414d14451112d422a3cb9b155a72459295cd3476a04021ed504ab357bf4e79aa5928bda41b756cbc1d973379d8d88835d8f798619daa47dc553049abe1f992d6b8b08524d78cf24f627116d220007915f4ca183d8ea7d885a8cb14513ac5546625d2ebbb3b5b6223426de8838e804213c71041cd88048091894c088304220b06048870bab6722da6023c49aacb2016d497842e3d03326aa807caf833c4307e0092063030601fcfc94a89a51e5f8f92f1f550550e1f8f973bae68af4444e0c054b8478e209263f0ce5fcf43c19a2c52c72d323c6dd5bcff6caa6ce0f19b4106d79e6ea03599491843196b8a067aec02030f1c30d8660320414ec4eab58a8344df3864dcf32a568040000800013140000200c0a0744c231a15828aa6afc14000c859e54765a1b49b324c7610821838c31640400004040201a860444c283634949ae244cca9840156c592ff1a834c256f8bbd3fec5d995367cb48ecce8fb4bf3aaaf88f52e2014b98b4bbefa080af3094bf1a29dfc31802a05e295a8a3f3d21ff0a802fa1599a94dff421b8d2d4900968b62a1587bc490e17c9fdcab0fc03b6f8e574b278935be6747588e4db4e65bec8a8004ab3f33697feaeede70ef22cc85132c686446a767d44f14520893e612b321330dfbd0be4c5996b9a9a9f39b0475583e76032aa1c0b8dfc32e6a15015bb2a24384d27d79d89b6d385aef6555801f076098893bb12557245e03a6dc1a3ea3fd39550b4bc26fed213775aea6df07d175ccf120d8b91cc1670862a9389deadd52b4bed770ac5e11f99c9818935d2d1b63ae017131680c4ce2661c0bc7426a50e75039aa1fb04c9baca7dcaab8d16e25e8a8bed7497c2ecf5b3115c88ce090a965c375f452d9d60d3da5e48273bd88a434f6e0ed68713c65eadc12d596f1727bcd4ad109739e1cb687948394dc7690bd4677909be1d22fde41f2e1bee76116003f3901aa7435acd30f39677c671e7ff865421c90461a03c7c833f943b3f85e61edd886cf3648e455b6bd2f49337ad90604f20ef130f30e9902ed87e09e6e3f6447ea7834e9b80f3dc33a1b2c83bfd029b19e7c27818f24bd72bbf1b32bd43db6a097cf1e6b1de67065e21fab65845e924928344bb82ac706c97de2cfebd77bdba209ce664a3b1202f7dc7bbeca3cc0d1e23539a0b928887180453e19bf2863bbc84b7e6cb805bc2b999727cef7109738882e90e0c35e80e9321d5ae3d5303f25ce1226cb58a7f6c340f61c3504034b5d3164ee07cd80987972a63d2d85a2de03f35a4e64fb34d48a84d1379f307b58559d491b4181d5b236d8ae8c67ad2e8d0cc0e2d7e701641e68ee668a0e5a63e597985c47d2f3d9dd168586cec9261e830e652ef502566e7f8abfcc6844c8a9f0cc215b9245a1d3bffe2dc21bfe3dfe3b95b0d3682956ca06bd771e9feb3a9c7936aad0f447b455381d6d6b8cc61074410314329af10414e9468728c3a2378cc1e967d3f0b88bce9f2acfd7000c292cec06d985a5ab4ae87c9dcc5f5850fe4dbe8d4802ea1f23667508e8d0661474d29eefe1261ddc91decd556309aa55962c19fa6a42882f3511ecdb34fb71658620e26687fb92898e9ddd9fd2f93fce9f37e89c996bbdf4b5f767991b2e6d57701df9736020a062217f42939407f03747006acb1b2a315cda8a63fb353aa216304de21712e219142ff074c60756ec1d3e5de5c25cef19081de71f869444490b10946ba0ce911ee2ccf9b44ad26c046f8cedcfcb0e58366864182780cb9ecb650151c6cbc65d60221de9acd8f730d5167adb020b8f88bfd8b2da51f1f92d150e137d2bc9ee66cf4312a333f2d2cfd25c526a242a2da3e814916b15e3156a354eac378e01a4d978aa7e67054ebd1076226f9f1518a0a7029f858e34e18a851843a7b06f944cb8b4ba0863d071e7162fcd3b9acd4703c2e1b833d554931378ed2ed4faa68667b44438c7212b09d7a27e9d48ed4750c894e0147644130df915cd3cb3d631f690b2d6c41285ee705d77034e6a4364bd5ca436f70dbfa7823af03afa2cc6e75a1b6bf5d5cf6b79620fad2b907d61e7ab40beb2e5107cf8a56dc518fd5495c05578423cfa99d4d5f2508d6a7a656d72f6c00ff95a1879a4b7b784ad3a6c158ec37c2d7021dd79984a03762a2ced9d2b4efd62ae16f26a4655181bdf18f91df2320de4b7c30dc879afaa01022e7a3e5055488e8d8a337918fef9e648979cfedfe3379257d37d85afff2b9479fc781a02652657f09615e5e453d679d605cfdeeeb22abc98031c70fe475566539902933e59e3dcacce2b12220283e63e0ffe6d3b1a4cfdfb5b6aa7fe0b16a237998ef4d5633c6c284b84aa6a99fda97a42605ba2601b644eab31248ce5b9a1ea92c10f3dd35650fff88fd8e919d000dc34c93e0141e61c4818f12116f4d6575c207e185c2c232127102a10fb0b8fe5c16b868906606b98608a7176fa25f03c3777f201fd4a3d53ac042b50606044deac99379757e692358a84d5a5242d361c1ee32765c00e4aadd8ce701095d121b33c23d931e373febf215439f0bdbb9c7713bf4c216eeeddf02ddb02bac1bebcb472847dc038cc1e0ef3d8ccfbcc02394c42805998709d2200ae7472896be088fdc4431a9c2ab53c39c19dba9005578351d8d4a9b5009206131c944df53e8c4d41ff8e88427e215bf036aa40fb77eed925b1e68b9d5bb0146df214ce4ed58110bc4d5f389b217ebb3224d7d94f7d9f998041996879cb24bf1e066b67e8e95138675261a2348120c5947e1364e4836c6b93f176f98cf048d7d6106726801dce68c2d19b20a507ffe0e7da6ec818ec5dfdf11adf95895184f886dd8205cbb7744b44bfa82205a775ccc46e3cbe4f9d7c5787be5de80d7f65378821718d87a128d659fbbbb48048ac2b1c278a5d1cfedf2fc4692a1ec252f8d711e9ad160cc1874c614c6e1277cd2dcfea1839b15aac326ab79fc8ce26bfaa217da258a8d5f0575ef8d50c858f6f49a255d99947d120424fbcb330d0930bb6c5cb4ff3c363b1bcec4fa396e570ebf2bab51f33d14e470c31b10ec974e78a7619002e6b2d701ff51405501f91c76cc60f84c0e7a1931cea16b1ddecd56c2af4338f6450474fddb28febb967c091088ffb489761350d3dd42b1397fe9beea6dd67a77a4f2510ca8439bbda3d41a1e49d7021ef855eded06dd993a39ba9749c5e4d4717d055342dd2d402a26194482451eb03992f48f8c12bbe8c57e2004d73d8f0b65b2d094f96e8f3f0a920bfa4e07754b13aef270d58d4b70a8c7b9a1730225e21a6cdec75463d71db88044a457ea341b123e90c090e94e48f918cc5c34c890318523fc6aadd6bc5d91ae641887c373086922f2b94d4f16aee9510aaf1c0c102f33d1b3c0f6051abe032b87295673a86579fbcfae838e5d43d69cb5959c04c343eb9fa5a1faf20d4746c7d8ec01078ddd279783b28cb051c1cecb8fd559910b632e516d8ca7221178e3285e12c0aa1d4c6867c3581c9ad0c17bd789770d18398d7905706a82d90b5a012abd6e4ed5ec1bcc5edbb011dc7eced2fc24411a9c6bae3d32ab4a5647e12288d95533a1e1aa6146b20606fb1a7cc60b46c0c9237ecf70ba219d47c877d5af2fbf6eb52b4359df5453a8ed9b7aa967f7f4b923a9c50949eddfe25d0167b079a015b5e52c58c5d9f90293a677dea97f6bb5082021ffd24a1fe0365068f38d10c7a8326347843a9df55e937521f8acd5ea71eda2a7368b09356649829185bf883da2fa99fba1ca57b78a0a7cd7c88bdd58205d33acae00e66bfcc49a79b1fb3e013b136ae3a8a1404a6dd2e0ad1f9bd46cd408a4c2802abea8be8f422fc89861fc130cfec8a0fccd197544c542c5f694013feb9ca484aa83bf9fa564b775b93f4c0e9953fc4d3d04d95432c7acde03e67c69b8cbb4773a66b1c061bd619bb5ac04dd42367a602a4c10588c314a70f8795f90991ad5e80780442656155b4148098a9093cc088384989aba4c8d6d0adb890f00307506fa9ac29c05d3ec4b450546e6fe9188d0f32f1009917035c64e18a32f6ff0b2974c2fda896cc5b0a0231d851abe1a10a99acfb2363029f9568041052f377b27aaf23494f34f374be768a23a07a6959125470bc60639383884560fc7566ba8b15783f44775cb15396be85cc18e5ab72d576ed4e0e274465776df79fd975ecfed99e7c22b939f653a803b10c8e8d49ba87e63c0b6afd7cf9b814c819b0482c9bdc08cbc2a9e6adf8f1b035a01740d8999f1e40df0a9f59fd6215cdfe10744e05516c8ed45e9843e8a558b12df62019caeb3899fb043ddbcf6d2171a5fc2804fb2a4bc995930d820d2739902e9e1564e985b2e7d2d09e179e8c8006a1d6aa14585323ba655b8c8f3377661733d16bf0c206cd79fc8a581d3993b7e1ee004c7302dbbf2a836f506c0ba1791b9b5d0eb05aa892c5272a1d528bd40b1f171c7605db8c8f7293ec7bd733d05e5307f86bd2fcdd303b1ea83ad3e87fe63950a571433744cbda9d1813624c1867713f00732164da53d8d5f9f680f1e423a64667cfcf19530d20920c6d270c254d638d26c94a345075959621ae169567ea8ea26381eae4008f40de2438a32dab4e768861217b409f7f8cbfdd40132e5964f4782220aa35fb4089a36d0a117b420e8436a074f02086c930a24047e267e1bc85aa2109313ac3a81fd5ac7048e9baa761f2b9a5788d73d944be780ba02085a34c6df0cd0355a32a6225054841500d3eaa66cd0b92780725c608e403fe5a0dfbe29d9ab9aa92b60eaa258cd0e82cd0a7c02788532900be4835155114104848841c8a0a433011d1ebf55217c059a03b6d58ea49034b38b02edafab6971316279b18a2d9212f3f533d304209a280de1283dd7aa8a6ef239d16c978e1236a480d7434ac02f20a41f9b8af7c866f82730ea048f3e2b05c6c116b4ba0ea9dc214ebbbeccf29b9fcb19990fd6874d9553f99871d8c4eca7f8b550479d3c8020235047f34a7c908eff0e957ff8576287098e111d83309e4b2fabd2ab9df49ac36959bdc42d4f3fc34084e2b5df32863d3d480d83212c9934ffb7288207c9025cff0bb198e756ed266c12e5a0aa234e707045b2ac7329f734b613d9e26a0511a5189a6191fa7611cf13cf932949818955371e4a1517d70c5364920671f2a5933a72a1217cdd9f1c98ffedfdb710206f2c2a294d2a4f701b4090ae080dc49f68a574785b93c272e5c167af80e811224cdf3b424b2c14f484470f09010d579df8e033636f8786497400a6e01cce5e4d61d2cfebe065104f5194c416e4c2840fb85b45069173f3d99bb08e45cd32a81eb06c96dd1690ac5fd7b4f874eaeb3ce5e5db59a8f0cde043b85d0aee63a5f6aa9c79fc375b760c091141b20eaab915666e6c2a1af289cc42b522ba6128a3fb2bf1f334ee43360d07e724ca1d961ec6467f8f2e38e694f1c11966dd4e360f840eba38865be6c965b5e1ea6aa3fb63091a1048ee473353126cd4f1dfcb04c3083617307ed75912e9e487c85cf17e936960b07e0d444668deef5dc072f3e00a8dc51e4b5321490e5087120da8c99660cdaf000b2b7faad9cc67b29faadfdc735fd4c03a3e863fa02b4e31135926d620e1163d03b5e3f1f714d2af5cc8c607d1faeda1b520725f119ab1b917cc2042186a5c26540d91506025302bafe9fdd7c0652c3ffa39c6283b652dda8cae0d8bb88f37c050f3058eb92ee68c13d766aa432ba05959ab1d81493e6070a338740dadd96e0ca6f509541294ecfabcd90ad005c52588f2b6d1df5d4b8a0e45f7358c42913bf98cceec8267707db630d7ff4936e1406f9a645176ed375f0208ca4af25be96d8967dc1bd42e4a5146096a9c3a4695ae78512c20e51b927f7af8e58c854dbbb927a4ac35826a8c4f92b47a66cc21b3d1e18b8bfa7b9cf57e1cfa0cc4893ac6d764b268497ce5515b5a455df56d9c8286d36d433b8827a18380ab40efdac6adb2d97af354318d48ae2dcef865ce68f6871f12a175c89727fdc236e92a0a1a4a580343532476c9075d6a88bd00185e46b3e728692584b277f63f88121a0ca85dadec3d229fbb1779cac457b67de349ad58ca99d63a8a24ef859d3a827f9ed1589f473d06238d3bb796541428a1c3e01c96ae2bc1b08ec15b3528b5b5a5a87059f6a73609a7fca7b28671278699f75a99a0bb491447bdaaa99d2f7b17ad2c19d8e4602c07b63e967fe7f9265c182376a4ba409c0757b7d6896b043c00ca7865e737eab82ae2c47b1409ddf6c79ab7832e4f05a3018e430a8351626aa0718b25921cf01069ae943b45d376cf2beb38e826957240ab4346ba0bc666943dabac213342c1645fa15897263b57cc5e3f2ea506b3209ccf09c82c37547909d5ff74221172b4958ef82f6aa799760c656c750a4009a28834f9f602dee2e3d501c5308549f30376e118179909d132f35f29d36314bce7d30437b6c8fe0f76d233591067b78bcb7b779a875c74c35c5f22656dbfbd4e1d2b96f4e48c18470bd7109c8d6625c5e7558e33134b2598e8aceba49ff0498254c7a99d6ceef61bb4951dbd781e232fdf9e14b812ef338225927c7a71ed9f539ca9aa89c11caeb38279176241dcfe06f342f72d86f26d97f4fe23e25f57f86a57fef3dd064a874fb4e7957c87036be8721d62947fe154034f6905007bdf59a2c346ee798bdf7f10a150ce3da36c8e89545432d04888d07f0f5c672bc23f61ce464e3750abdb3741e0fd127ed8022774d15559421da3685c88bd5917dc5dd9471613c6bc04c5f25ca5aba1dbfe53152aec22fbb0b619c064881eec0bb093388763432c3b87608926300bce106776c6a0f2560d84a7c99fd7043d300e6022786789801bf663db0ea9e934783fb76046c876b18af3a46e13e5894fa8f2e00b13bb87d0b307b487218de1a7a88424e432092a9217d2b6104412cc34d8c18394041faa25c7bf5813b426100ae90627b71356d417697ac89a31c1dc04c9a9a4bbf3d4223cec09ee2ad7e9d91fec3bd9d8d064bf9ec73f329c80a222eff66899c35d8eb1fcd04096c664d86f135e5cd456d6c711761eb78e67c1c3c8840488896b2ad5a0ab397e58de5fc301e2f2e528374fb46fa4db91fe7533892ff4974a54048febfaa86c32e27f5e90123e21c1244632a4b06ce320a42453b11223959bd9e2358d6f6e7b75c29c0610b8286fc6a18d4b223aafd6502bd44b4e94bbe2603a00bfac294e3ff1316890a497408423c9634ca0a4785ae13e86658522da4154d575deb3cd250243594c85a0136566ffe38f5b48aa9c4871ebe0db43b0020a6b5a36eac109a039c74fa7f514d94e0840a8d809bdb011258560cd0df49d876c823a795e08733544c548cc893aa36e37a78e2853f300630cb3ddfb78a3c48bbe39b0a7bfb06cd31a8a099afab0e9507ab806dcf212116427c66f3576c249454f47c0a4072b73b3a75900b91aea2c0db64c40670812d9765db60e466e73c6202eb0839af412206c1b3e0ef925d803f093445d41c2717101fa4f74f90f31a56f04158ffbc03ef5874860125dd64efc92862d2ab21ad5b476fd0887a8f7b75564bd5e26e61459592565a14a944b59dd79dab4bf5fda73c2acdfc74132d97140c1850f113b1e062d5b7b4cc17892a4b0f0b33e58cf01c3820ed665ad4139e51cb969a118d2a43c6f6d49c6ab5879f6c4f32857409c4244444323d8e034a4ee0ee4749cff487e6d13b9095a75a15597e20a3a6236f0e5ab692c1f5588863816418dcd91e078e248a45716de01351cdd4f6a99bdb07a59795b931f421b49a3601796b7020783327ad4c9ab9ec8902b4eb64d1ecdf082664381776bf955bce56a81295b8ae9fd16991b21258660a28997d4a200a0ac4b42e4ae06a4d7164f30403295f2548cea833a8e8a4910e02a60fb6941395467b62422004318f827ccda4be5fe006238293dc600baeb94922c72bf40915fac3a92755ec8f586d09d5e4a9f99cd9742b5b31d5d92fa2c6019dc4aac2127fc88af3b869816520481896cc3c13f8dbff72f4486bad49db404bf7f47f6884d1f8e79cbf17f83d62081fa5a2ac26ab207099c101312d59a40271146d27d8ef638b3d5221768443ef17add28ba294cf7cc714a556908109bb9392c6bc081525b98722da0bc5f2f745ab3b322c08618a0803c080b1d5aee48cf1da869d40c782d14648782a761f197cd2df1ed06837512b3d9366dfd59aae6133908f92db474317eccc3bc0da47c09668e420edda2b3e3983eefbd84b10c207d1b43bd6de45953731454c65c96dc5c1846bc80f055fa4329583b3103d20cf0c5396d9cb80f5b973ddfd5a6a7ec8f3c61b0f29c62521bae0d6ca1e7f9b17ec47694f826d51a911628322e42602fa44a3cb3aace207f927342a4798fd2f521a89d32b50bb869afd7c4cb6c4d1696ea52b64d136b4a70cfa11c9961719e757eda6ac4e814631e8aa6e2f39ba1cae99532d20b523a6a8d055880f97c57b5bec347d73298b67019005ceb107d386ca14908e39f0eb84dcab5b31ea4891f87ff1748449fb2ca48cc21f0b6b4b0a2f031475ed59d9fadb8918ca1afe19794073db8710811c5c0b5be19bf934dee01784a1f30b5087d4a4af3cd8db245654c38ec5b66768cd4bdc4a77ffc675003fe94ac26dbffa04002a4bd710abcc846a5a4df689b3f46dded2cfbb4bebd4920560fc8450a87f7ab7ac36c6618e7c60a9c39fc94736fe2341272ae102595033fbc556d5ed0053f3d468e230ea1d4eb14f1a5818909c344009464204a170b36d937d170353af5c7bcd1676e68503b798252553afeff0704c9cb2d3a8bf7645cacabad47255968023c361576495f22186dc9927267fbf39ea9a1939aaacc797700151035aa9742ecd0c2eeb1bd1bb3264b1811e821074a938673c17b5b8869dc63b83cd31eec596eda881398485ec2622ad3680f4eaa24bbc31f3f5723c923874b93a1d74961b86b60e389dab254401af72d37ea1d0acb5e7e8745d8e68b6067361bfe97a18768edbd2a370e4bd55f61ead53c659c9a96537a713947871baf8ae6de72660ddea2a069c50976b0894f7e66ebe26e8bdf277d72d20fed783439177532aa0a95ed5224b93cc65f92f360b74e4ecb212024335c640ce07a384113eb0ee936beca749d9c5003cb8d7edd6f8eb783b189d6d86d8bc685a1b2675634fdd3d557c9cfb0ebcc34735d516a762e5292df77bec247f463869fb78f8e1bb878da038376d0a5dca1e5113ea8223d736c4ea742e425e9d6d28a80aa4a5611c019355c63aaa0bc9acc539548734b62450c98ca8d1a1157fee29f827aa93b04b6146bc86134b9d80946c553b2414b0565a20d08fdd8eca4b0439b6080d66ae43ee3b78eb62ccbb37806967943400802896c850a7f4cc4d2dec7de5a128d35ecd23dfc1a6fbe2104ab94a92da22cf866ead05cb4aaf731fdcbedab8248f19c724b7c2bd96f6704c25518cb1674e8f9a1225283a93cf51a45a09619786da3c2f67552391bd42610b0aa471f6f9950a807f4165bce7da4f1ca5c23d988be618dbe54bace2430074acf810a37f6a6a6d31608c9461518e4f67ce29ef3a6a19d2b5e47bd91162a632652bda214e105df671cf5d735088e04191990cfdafaef44b656992a514aea48bd168df3a3e54283d801a234cb25d049c530478b9ee2ee13d3a268f1a04a5572c78b5136122b1f7f8c4cc34afd6bcc9310726ec568488f85f556e335eab4994801d15907e8678ff0395bd33f72b8354b6611b05f4202fff1d2e26ab435ed9589bb16653e4c6da8223a79ae5d55b32e2e0c9a445725b8d6bc33ee03ce965c8452ce65b292c5e49539c86d1b1ea03315f53b0b6dade0ccd4e1cf4cbf8019fab92c6812898a464ce1f1ad3539b3092823dd833844551b2f21ee07f8fa2368d86fdf98f982c6a4057d61199a74ff9868dbbfc3e6de56e77ebf554afb7005d416f85686ec523fbd0d5332eb238260084ec7a41bd22103c6f56a4fc2e93c5b017721560fb48fda3eaf76c3371f5ddd274d726aaec64b2aaf46723e52cfb6c60b8aa74f0654fba1c213ba847bf2a87dc38ec8edc6ad9a6e687473e7a26f5259580fb20901d373ac541f05467c586bcf1b17e0e8522330422f5e215bc4983c88ec94fa0b508df86b3ede327b25d6cc7c0585ff34ec9a68071ab4ea6e4d60b878d8d856dd6039ccbb4d92005c06979c20d5014ccdc4757d62b00c3f630417be3745b751bbfaabefa3fe59535f2001d40b06f74f839478f125f718709abe005cd75cf8fa3afcf98b0f67a5650a81fe7545d1ea1f68da180f067a3b1d241f07b08d44fb20c8ceae95708bdd15d1a4a1996e5c99071cb4d3d1b03f242838772a0eedf863541171991e7f762796e387a11c9c0adf90cdc9fe71b130ba4b7106e9739b15b18130f8fdbf142167c3dc2de96b749dc1f5b599c6af1aece20ce87ca0d7dc8e044012393a58f9eb0e84567c23bd04ed9ccb9d15459c01cc52f4b308485ea7d490fe84b99bda60adeacf6e7cc14399039853445a5b22e6d4aef1c380acd5b46d9addb319972e88c29e48b76eacd3021c3135dfa304669bc802f737239bbbf08a2388d98b277127a372c19bb0c4acd03134b0e620617a54d408efde8f196083975035497820e21fea283f64120faf32d8136ce9d9d0c297491bc40fe2181678f94a0288c3a18d70390137f16ae237b10383f56deb507d6ec9e4d53b3c89453bfd6659a99ee9eca4f5c4f07e50f94eb812cab0a2ea39715cf1c31218952213740b3731ae78a1416f46035a6d3c0413cd23891448d26cec1bb865df57813a267cc0c97b14d59252b540117e5d72b89ae24c7b81a2d62d328e63b45f249ccd4842cb8a8c0c3e96cb0b24fdf8bd64c25f574aff91e0e139c17f8aede304da37679947aa4c1a58fc008cb1811c796b7d76e1265d45f0609fd89bd796ce7c1050cd9eeb3600e15ad5cbdde5a3f9a4418669bf0aa8f6a618b2a678c82df18b627ad1d0f175ad4ac4ed18915c13c16e4f2946b1eb72be68f118282952f42b58cfa33fa09147d06974c58aeaebb4b560f4ef194ffbab5d4faa1d4ded6494482014e018db86e26dc47904cd51a003b3a1df93de1ea1a1e1a131a9bb31102e2cfdc8e5e1ea2f8798345203aed829b9cf72fc6799ea3fd752030042879cf0b0212b698bef83f5739bb1fa5e1d3d13a6c7ba8685d471fc8b5a42efa8f0323c861071d257b6af84070ba07771bf88148a599f8afa9cb25a770c916ae6eb897bdc1de075b09f14b8a2b47309e4cdeeefe8e4c43e17c9b1c0c0fc7eb4746d4139f0b22b57e6c9080e2be5b264b14ba9165889fb7dabf8bd0cd9a55063f5eb6e03adc83078d66882d120b6320fd567973d652706be7cff3e0e80023d780003390fa890ed9d3834d12ec17142d7d87f77003d8db7a8cdbcb2b88d08e823966e53354451b6e854c374571553fd3143dc73d6fb857b01c543c335a98f13c2b3ef18b38a7970b5ad2893a4e96cfed1bd2f89431e23fcdd39db173d61893383e48dc7e4ccfb753538ee661e7fad0a224f8cd8258313b3f48d4fbc24b4facdf996e56703aff8b9fd7292eb8298dae736be883ff075a6d0ae2dc0b6ed25cedd4a6a6055b9cfa9d385cb66b2686b4095a3e4a9e3332284c3561b965dde5e7a499ac33c5ca4426465b91644eb22a5d849023a60f3b612d0b5905d66db276f7a20bee706f7084656155d58897f15e8b6a77a5d67ebb609495b29a5c16596e346d99d168a39a3cfce87020ad54568bddafae06babf4dc496844db4951292a000f4b0c0683df64591948b0a93d0b978f482b2188f049d75494892e04a58ef594783789ccc02627c79275674c40a880e6cca63a9c34a273d11ecde6c7a54c85c9119df0d429629ea1141e4eee8a6735052e7b94b1bc85d52a76e64472d1cd497724e555407dd1d49991790d552df44960caca20aa80b998371bb947a620f5144d28d15eee1a0540e0dba916f19babbd861e2f538f9d1b4846238951903f6c82373fa3c36666a327f54b51538b4da7670944938a86b2e863e983acfc84326dedd8c3c4184859289cc456685ee744dd13ec3a62cffe419b1cee833a8d387751f168863e1d8e0290ef92265890afb623ce4c8d315fe696b7d511eff06f2141f5d3ae1dbb20697729e516358f0169fa63529d459cba203c24d2e1bb5c3ad18004bb363276615336926c3db6492a5f5689686d32403656882b8a222f2556c62d4ada135ab609a6519312ebacd8a4238926eac4c504450ea002c0d746039310505b0a309945beabcf4046617e1f66b2542d5cee1743577c8b1652dc902ef29f4126138ba44cff6b516fd6d51a3f0f178aa7ca136588a439fecb05d3b00b57d50cf766827b2c2d86772f003c58216a4e3424216870cf7a621687a26bd4a8258fa54f431c2d13082ccd4ebaacbfda8675945084624cc83cde50c9bb41f2e7378f61624333d0218481a8e1216fe90cfd62243e8a218b603098ae55bb8e7e9414b4b566ad040e63ed9c4ccaebb721b3882fb8ec3f828152a78171b1365648f046f6b342ab8ce2e21141db3c167fba2e44929467dbe1ef6c3871d4bf60b7b00b55ec07a70ade6c11ec7b5bac2d3ef094d1003c1f9107471bdd788040edf5da6b1afbf649ab3cf3801b25b58980d282e713740f41b94254880c74fa4d0051c7da8cba6947ca60dec7e3c774131975c0d7ad530c2ecd7eb37f1cfac13538c0f607052336b16fcdf95337b08f51a5af8d36eb0b12eb1204e23596b033f759fbf96059275bed560f5c2a95f399a0e1c9bff35a2b286addadba54924071cc63c3f0e54afbf564797c19f0eb5e90e1880e23df55cdcf9c10a5145b83f40f7a1d4eb0abb776c7aa11313bcac5ce535336c4cedfaac17c3d811111a38a3b4a94aa4ac828de5ff5b36a1c9a0ffa2097e6f06c92992f556c72d768df6f5b73535f4d13fd019be31b46fd91d919775cc0425966c1b2b9c930f956cf805bd623232b1f6c55aefca0df5b819f4a4e4a3b62c52d40500e5530398a06c8334f8602e62eed2ddd276d1929499e341146eb9c21e88e34f8a70efff1cfc2de2522c55acd697ed839082348cba85fa7772ffdec9155e0f418916f1d45ad262c430b32c8a809d41fc27f50e31e93e8845696e5cb4b441eb88fd1a82a4601109958a88759c63602c58b1c96d369027f6469e9447e151399b8799f710bf69065023ba07cb5b8aa62e4ca6ffe8341c6a9b6199c5e6773d824ae40979a71b7576c927d6ccf7fe2fca17c0a937e0ae54e0420d4e6e20d2e533c49cb86faed80be18cd1de5e9a90bf15f2ec7bc6934b8ab4d74c9ad3c2b9862b09ca160bf4fffab1b1f0012d9ca10ac3b38003aa047f5855eca28370e76c8846478dfb5ce6e576c0b6b7eb41a2862637320c82db51b0066d75b4d6bb173b748dfd5527f3014bc2b5c3a75a1fa1fbfb3cc7a290e6481f8d5ab48f0b7779e947a2fc42c383c16de22dd2bf813480f41fd9a14b61a0c00d2cb33bb9a2dd7da7d5c6c3e4529b98ce5d03832b76aa9796948e417caa4813450629407c8e98bc9f5a9b28fe70cc9961f219a7cd652d2b4a98962ce7c6efbc0198dcf013c3c87f63547226f1c450d12ad841383865e7134a0c5b68fa2636b1a6fbdb1ce9503e7a1011164ff06a2ac9c07a6a76aa67b23fecf8ae5f03b8089492a3734eb6b8a809fe177560b72aa14691e8c36ea56d15d42c24713c11b0856439e7956b7c04fa6c13329d3d1fc58d91a27b5d3da78c2b976eb57df8a5d1868564404d65908020c8343559c8e5d968547fd52be9712a2ab6af35f3ad9638da0162033dfac190e13e5948b6d9315cb7ae65942c64653b10da2d4aa379b839d0f947a787859c033aa8963f020b0b9989ce38eb2c29223d13e1225bf789b3625083705640dda62e768ae3c4d5692d2c92a7eb0af6e1c40b1ea169e3451a3b462e385d48a63cbeb7f4869ba69c0197ddbfdda11561d7b75b9c072883941c1efdaf19ad334a2d1392146629fa5c8040d1ee818a48120faf0309f6906543af462aa44f8a245aa628a1a115531f379e7ee7ab0119f54a00f39351798b9fa0dde8c39942358ef13f6e8d59e89642a664e4ea17e158e418d8e3f00e5a7e2c395783d5dc4398cdb3e13a20491c3e7515938d0e61dbbf3a985633fb2e66b6ce5f3768fa8a10c0509250046eb1d385bcd9698adc30a8dbec908daa692a6341040acc43c8778f3a0cb7208fa968d5b3a7a28a260c0356499423aa6754574bb1103f12d117f5fe0c3aeb5d026e378639186c20671bacca5d394d9a6a7aff9d4267b03d549ca337dbc0349259eeb968d36c171f5b12229e5cad7b80801d7b528a9ea8e6c52a9e80c5bea2b57225861779f68f8ad200821eab11ebed7f408a6c73f7caeacdf0cbdbf79c2766e203baf1c3b60ad8c09048c0a415eaf8f79716a08577e8fcee702a03e043576c251135bc336417b9bd08a41ba7f21b6fe91f71ba6aec9e3332548094dea3d9bd373437414013e50d34948d505ae0648ee3039196cd1fd3d201cc406e454edd60a1e2fad707cadf9f13e22efabc2d4ecf3a8796f38b0036a699283b90623cb3a9cf1594c8f57044516b1cd384976e3ba6b89a80fd26abc5b76308854c4fb0489c0de6091a19c673ad4be050b2ba307c39ff43dc67a23a6533807544b74bd285916ab89d49501157e66efd786ceac99a20a75669707c1d801d448a99bafd847a4ef0e982aa50d7e64ec1c3651427538788e14444df65cce3a85045eee63d1ae97d82629e0cd638901995a2d248a17d445afb2945139be6b70b7d7e292c897c40b63748a469d85416d9d4f1891506953d95d63278a0c1adadf550dc83aaafc803f40eaf821199c8904e4e942940ffebc5ff8d9ed8834a68cf9359ffeb79dbe592bb546069ba7fb67da5d5847025aa054f631bbe9b665a7c0a1f383557ff796c08a06350701f617c460a1e825f661fdf49f1e73bfd663f1ec762cd33729f8fcafa1d7062c830804af99ac7c21aea602cb73ccf435c3bfb168e6a1dba8b36cea39e5c7b6da0c5a3e69d46d796e284925f0e340867232c6e73ce1efae9a0a1d87b0a05895a119075a6b28bc03228ead98da2c85c5da39f998aec1af22b5866e44705f4b63cb46a2215fbbce23ffa6e76a457c61e1f100bf44a04960aa2e8e53b55bc58d2546de5c9a4ff941ae633ebb0da558263b860f2bb2d771393ae39a43554a1459f162d70336a809bb40568d4a12eb0a8d032cd1a3bd534df823fa686c81fbd88e3ef2d5e63fa6615898a3aa7aafe149298cde2403f08069fd413f06b0729843ddfa63e4ec432c25c4c8f444bb7177c511b89ddd3b602b73e873f72ba219281c4d49f6899326f92d09bed58dff0bd58a6580663d3fcf0bb796e90110e94222d19330fb947484771d2284598bf717ca9287b30c13cc707b774e26e545926139cd953d295e6c4fcb8902d702531b0db52057f8cf0a9a2fa825db801be5724d67767b827c6fb2ebd3f68117c0f657be5970cfe16916ea8800363a6dfeaa3865f4e27df18d2865f3e8f1e83699c32c3aa29d8d68a09b1b04cb2efa6cb4149254d9ebc369aabdd6a9afc89d22ca4551562a3d9fb3280e4218ab4e3975bada54fc90fe190d0f858cca636353afe0efe201716a949a23e2aac976ff0f2217191b44b4d4b6fbc606cfa84f6ad44d966cb34d1ed08668217b0d13d48a8a52760b36e082effa00b87187eadd3470c551c301dd894412661389ebd2720c3da51f9f8385a349cb8a66ccfb9e1c856ca780fa057bbc7d91aa153017b4549b96ded4902861cd8f5534ff1c282f8636471fea35b1d5569701b4fa2f8bbac1197629ea5064cb71c3edca865633106d25a4f6a39edc56239b1552633b107572ef3942a9b6d1e6d4a26d20ac165cf1ccd2dbb951adb3d8c01eff292ebb05484329ab46526b61310ee56caca5b9bf322af462fc03bc3aedb4a1591f4071ba35b1bc2fc8584db16b46fa178fe17d6e03452c4a3297b6bdae0cc50489fda011b3ca9695815132a5d396b5a30cc9fcd847a798ce79c0f72c0a6b47d1b58baab2f3a5279ba5e660b64ea81fde826f891141a2259a4b4ea7c4489dfd2b028ba85fe4d1417840db3142dc55cbdd8c1fb219d6a9af14983811c28081a40b22c26515ebd61dc9f0dcab3a48a502330ac2b6ca419300ad1e34527ed72a3ce1f5354c9cc9e03fc4da8aa1178a2f5e18311f80e85b06ce92b320d3725be8c20e7af15ba382573e9322fd9a6b66525f96f7303b440c90a00f5be437a224917c4fc573e1a28ab0008a5237d1010308ce517e7ad0325dbe732025a028ffdc9a5ab32cde6579ab3b09d0eb384c0209cb5689a3d0bd9631dd709b96ebeee8e621b978ac8520d60f582643147631d2df67af17be0ed33181a81c49e5144bf834c60ec5dac858c799d1a1ad2aa3ae3dff8e75a8a0afdc5e832210edce2eb16b406c36d4f9b7a2aa89c2b0690f59aba89c6f40e94b1e5170138ad41d60f18ff4204c765cae86fb779145a699e2a600d303e1141d28abc2e949887c4ad5547c72c255e1cc8bb2553e7556bff53a880594b411c06b0735ef02f9b56e46937ccc3315d04d83bd0e18775eaa0bdca1bad3858a4f69e56af54b5f801b6bacfba83e143b82f0b6b0840cd855021046703239cb552b6708e466c032a104c065007b23277e69180901f0c462792aa3965d9147875030186a8c31508349b7244dd8d284bcf801c0afc16a1ef43ebe5c9baaffc010a5f4e0e9571edd8100aaabfa1241d3df6d21a8caf1fcedd4d6972a116af09e2c27dd30f088028665971e8d65bc7a59046cf7885bdb0d93b964ff1419f02d5a9b31d9ef66556fbe7acfe5430aff38613d82fbf037c03daff3eb95dadd58cfb589387a2ba005e2f35f3de5903edb64343297a41899a43c9a817258c25093645beb963b25000f712dadd8df9b261d2e2cf0854a2d16e5c04d4460093ef8e400fa86b3239cdf3fef888d1df278c4077882e902f798932a224ec48577ac21e196eb6726172c442c032c98d55e2df9708fe2a15bbb1de30ce968cfbc851abfcb0a5ca44e45254d59e8047d19f4f2d4357be0cdd4dc96b7b5eb6add2ccdc2f1503a79da104ed52afaed01376abd3d9a87140bfe654db95b700f5ad95015b7714a0e81ed921986b1d6fcb1dcdb847440c1de1a4964238144cc5caa3e4304fa3cede5fe69d72e0fd8d7269932b800f6d1151eb6db0f9e1d52e62e8d308622f40fd1823a04d546c11a606e06c18c5e53f0cb2c2548f8fa1c51bc65e5ce55ce5b123ef9f1d5863592092ce431367548418a47ccc968f99a7917f4a0916e5e69ea8654852994d8dfa1eb083d313b0a9f9ebf754f007946e74d9373924520d80378505aec9e61138303e9d54cc22559359f848b005f5ed7efbc05ab311a3f4222bbfb82582d1c8ace8345bf5627c3e5925fc72aec2b8786cb4faac27d08ca59aab07e4ec1831d448de15eae3401c14d0567a686c1d536b1b9b9b47498c5ee8ecfc16a42ee333b13f916d042a5ba99a488ca58c19d047bfd5539722b728ceb4953a019a846e19e58c70275c01d9e872739d09d158f7957e24158f43e3bfcddf1afe5077eade4fd8d64cc983124cd6f3e5919bfbe8cafe7342550084c6146bc6bba01c76915481b75e09441b51044a261a2654057952f3072770ae56f6a3c6f8c375fb537d011ea2ff87df239671fc00b5b58050c601d5ecf67a2f59c9ab7a8104680cec9f01439da259f91ece214a257a5cebaadc055212f2bc56d59235056cceca1a85d60e046d9419aa81105ea4ad7a3681470c279e062fbeed23f53f058e47f074a97a95fd43e09f53b3c6110edf02eb1b16c7d50ea69d5e52f70b1e7229262fcc8a7b89d31a2d6a1fca078ba0c18facd5bd9ee9ad7278c4ce341150dc36e50fb8801053ca017cf8cf4af4532c58adb8c88a2c38ce1dafb8b1ce815062062ea69d61e2329cdd86b4c1b08b5c68d724611928196e4abeb185b08bfb004dc18a3d943221cefa2da7bdf0b3a1a94aa53f0b9692782c1240faae634c28a955bad474da8b3f9bec748e421e13bac5842945ff608e7694a36f35c217d799abfe21bc89acf01f069f0d6edd4bd30afc6b96074758987b269120a6897f5c342029eec351036c3dfbff2d0e2bfef877fd0b2e8ef5d5ae2bd6436340b26517c7ef52af6936417360d7f247bcf7dd4be7369fa61ff82cd0a0a7d1d55f7035274d39aac6175774865a282ca8c8ad8de8e2be231503529d878aae3cb48de340a78004ed6221996f2b4628641d03b72611c01e08c558928f97a1d7543db9026e6b9d8a24ceea30fd5b1a068c467929fb3c621a0b816c0b843f1341b09c97dc5dc3e9e40bbc6603dd6baf4434b8a321422178194139f3ea44ae7d589315a49a28d6423cc3fa2d647707e7a3d982c5f5255dce7cc533d8807bc5d3af668906c9e955fc33d7ef75aba818715d02175cb85990a4ea9782896c288b172bf760364e80fd4ddf32dcec5686851d604f3478f656f21c11938d21b188326125b013fe3586afd924333bfe4a32c94456f4d5649e51bc8857e99a2874fa54e7594712993deba013b97b1050063b582fc1c146f7253888976570225590a91f890e89f65959f4ce0303d243110ff133919cfbbd7c6846c8d5b48d94a3226a5919aa9c29c0f5cdf0aca61a41040806b63aae49d0ec08f51c99a2286a3c73687d7f2b544a7d000c706f4095ce1bbd197c4096b67e075bd86befb2c5a4c5bd320fb29782715ce9f2985faaa64223352e2283a0b332fd965ce017f39b00b2be294b86ccd65387a5bdab43ef3e87558d4eea3fd4614ebab9c55947c959266c3a96a3be2b7a2780fe3662ddda744e93b39123b4553727091e9d622357a1e51c9c8cf6d7e270332146af488b4c57223ce77d67f52a92f5ae885d6837f1ea91f17c310a8e0f0046ca3183de006324a7fc434695abeaab3f04c756ffbbf9f6221540340ec22d9b238459c4f81ead514744e00bb9011ef8040f1a1157a27370d1c94fb3bb1164ebd000e73a11337855f3ef6c28acc88023edb9e5f3309e183dc24cb901029096d844cb2b8f032ac31c20a578c0ff07e25f60f99f309b8138305067326c348c621b46a0d4d0a28dbb45b98af9d46a5e042e6a32204e4770f88c082dd3e17a6216d5a2e1242e7ccb1810761bc3bc06cdb9065a60e1000c966d343ea13f5a1c19a423de9cef2c651561108c77b1fa028add8e8c5a6f653f88a2d144eb1c8c4dfc9a770c5aaa99d2199ce248f4e6051210152408fb6132bec5de09c601735723624667a48b63003c91a8d12eddfe6a3f3f10e885d806ded44efa503e5d32bf20c8181140202d3a4d09da89c00dc2169f7e6769d4af42ec57e48c85ca0dd77bfebc29838ed6d2d0ada9bd5e0247d0569018824a842eb5c197784a10cf3fa80b0de204075f41492bcc1485c061fd221ab2dc461ca6b5afee25eeb60b79f13c911ba5c2a7f47c444fe2526659031c9fa6dd9d1c90570f1fa2005efb5297031587f39bccecc836a929e73fc8059932e25caaabcdd259863b1ab40c851677c1b4f9169ffa63f8eae4b7d9f96ca77baadbc4f5f759aa12829e6304bec30f0038ad3354ef8d6e8fa9d95d5119943e5ae6c48f00bcb21a47be4b6a2a3c1898e6854d33ee99dfb69026f47c1813e10434d3933e898f7488f192822d2a45420218dd2745d5630ae79142f122f45883ccaf304b881090478a2c97c81f0657bc4a3d7552a9b8b788aadb5a40a1ab569ecd692b8996141ec2d86144eed9385c0b071cab1afd1479df207d1439b1d0b5f9f041d766b37350358297831f19502344669f2a88a45e2683d2b632f54eabcd512783ad37d945499d9eb638efeecf28a2f41043af42c5d26cbda0aea89777e83f2b4e0554e5d540270208cf18b0b32bfcd9b254423c09884c1f8c7f2ee3340a801b1b3806619c49bbf6115beac751450ac88aa41922be8d150bb11187c14683631fa17cd68cce100b0e11fc1c65670c4255a8a01055822d5751e6f9159f44b35ea30deb8c048a6e7c19153500148241cb4b84c8c0e4085502a582cfbf16ee6e0bad112517a8c99af4e90e44dd2d0e047b26295e47c3b634e48b06cea3e0c10bec0b34b60b104dced9052dbd1477bdf5f04bdb6f61e7a9dbf67d7ba81e8b2c19e6bb329d01c63f209e309b0961997e1d5b48d9631054d5a5595f3c93958384f1bb7755188d5e337ab224e49decbbde9a9b6152df0fea0572f8d855d386c89903886521e1291c91096fe11a1d8663ffaef481b968d69d506c5debbf61ec1c6c53f1a7b1d78e2a4ade125569f6f4a2c0fda652a18ee5adae2874a7a8590eb73c096975b92b8eb6674e266831c1082e05f103d1f225f340924881c116004ddcc5717023189c24e577c18e064e3181e43472812feb300902d02485aea1b1f8548a15ac0a12745d4003133ae828e039791f78e6b2a4dc59636ed44e7162e9e5f00e3218e0f87ecfd8050d1434c2cc653591501082a23fed2839d47954040058741acca1e6dbac225fb4075341f39a430f73ed1383f435cc01a5b25d0f083966824bf53a8f556af638daf7041d7a3933f6b1a4f96c5d289d62c4d6a84090c5a21dab9c1e4fa1e0f86eac485135a9b4a5c6e1812ee514120a246193eb2410198d41b3ce27f09e856244edb5eaae21c32b952d06cb6e8b206b84732d0ca276922838eb01c9b9f0ffa6808aef0e4248d8ab11dc900d06d347e339555db583d54f79c12d05b98e05197220d3ba06ed6436f1726dd67f5a496aec0e133f6671672a941086005e4b89d6a0404811ab15d57f9497066bb547ed71223c736bb70084b15bd6de74df85a16053f72cd0a30cf4754bca5bc5f2345c4acf8ca61a048c442a2f8219a1ae089a148a255893621ab42990fc7408540969b549fb1ac1fbef5b63a7cc2251d54bc1986adb20bb9f22ca0d9b471a56cd6f79eeea0d13f7b0a01b61a5a9dce96a86aa15bb351868651f0934256586d99c2f1afbd8275b048e586caa3e85326e041caad010d2d4a8288ccfdf11402abf00aaf0a0898616d2eeec8e4facdb531e15106a12d92de0d5d89e44a0e7f15866ecbad11cb57719e569af4ea9e1b8e8b86e4f187c88456e24fee568f9b33f0c499246da3a9c456ab0c92a0b4c3369c8400e96004eb3d3324a4a6aff27aa04c773a75af49180a04247283ae5c75295b9dfe28a22f12b1a9acc9e4636f6c0f2d3ed9c65557a5fa596bb9d0606960d6afc15aadaac05a99a52eb87d2bdf95749f8cd4e082ef72b36f78fa6c8595addb2c8b82bc7d5dfa20c3aa8b54523fa93b9c815890c248e16f2e5393a9816320ccea43f4001a696995a54036dce432bec56d891e10a9f05c6515c31f5b7093ae967eb8d014489e469847aef9cdf4af58cb5ba9f9fef8f9909c249f8f1a3482bd305b13b6343abc875c501ea59c02b57b151a532e6cbba340f042bba8cc2bee57a195bc9170a07d1ff741d3f04ccb292f5f3580dab5db5013d50a6b4d1d459d605174416e8c75c2fa342992c35947cb34c29191dff5a26889167ca9ea32b178e113b7abcc7b4ae4a02c8aed6836430e1295260462891fd72d8eedfd34020acc8b01b3eb866a15417805a4b35a3b8e1a7a9a31e06b8dc9970877db49d9c3a67ac7476dd54ebcb33dea3848611614cd236404985cfe9acaa792196f504ecf45dfa94a983fdace5720c37292d1bbd33df9f6e03c3944b0c9e13c569799d31e30afa3e9cd5c286a6c6a7255c261599a83b8f8a01d26b921616ee18dbccfb66d7c353e56650d1666feb7c018e022a484c1123a6f248650f29dba42de726b0acfe426c495741e220750e94932cf3a409d15d97b1057403411a72241f6dc46cd2a4dd2c5501e0d23229f71d020bc28a083c987c79b8522a4300fc3aa2be4b4fe25691914cf2141e53b48ced98734643c847f31d9bea4ecdd51d24ddc662028a23ca4e63f35d668380c08160bcc2ee047f0c35a321066e4c034fe0dfda8e19f8797ed2648943c2bb1d1ec0d77b3165365b67e91f0cd717cd2c7a10095d11001baf07a2ef08e36cb3e6230468f0ba851b094947c90813f3a881134ed3184e8309bfb8607608bbd045cf48e53d26126a120719f171fcb116448a39a941870943d46954360a876b6a8592b2043e8404fc0ffaa4562acfb22a221f90b8de3626182cdd46c04af84da11fc243d1c4fc78c5d85c6339f39640397738372f80a684250f2bd6e4a321e93698234c3726402448211029802a14c0b9fdef6f1ef13f328aec8fee89e6cb229a8116245e01a5a8ec7c9582be5036537d3f8df8425e3294db6fdb9ff88ed63830149bda66ef30896357a742b33b3c73e2e053812cc24fbda827bac8b372c5ef1113c4da6cc97c366bc074cf6b8506ef412672e966a731abd17df1786a1ee7614fc2d48ee12c5ac6e2b4adff5b6e0f64a7a39d004d68369747a30d62b8a22f4ade85c4ea90441c3a51244c2dad1e7fd2454f3040187b9df1db7fd4612853c4a863bc4a1c211e04796f8e375a03050b7d61a83ac21894cd72b79e8c23dd554a2a2dfb213efb73a350b6413d52b078d1aa4f0ffbd241553e8c7613183c76c2dee2e1c9ccddf0a8da008422133c0f7818f0430f4e200e7de02c8f1bcc5938d5261311877fbf341b485924a4808073c30f631c1176182aa2f6cf374afdfcc80da9304a0c8877b0496f34b3ffa889ad6f6745f631e2911640c61f54f5e5edbd83c0d1646fb825035b390c4755f2536a04c8894b26e5d5ab01d31674862e86f4dc88f26010a6e7d3a9ca9180859e63717b74eec189eb2baa794c50e109ec2c019b65d12db93ecef01dc62bdb31e9147a1789019e521c10ae1eb1a60812ca1775ffbf1aa5358e460b806df29e8238aea750e0a755f1bfd4e047479f84ef20218fb321d2190472ba9b6d90ff250e0abef7ed7f0977b0fc815e1d7d23555c6af2036be25b84bb4719bf8671dc40c5f92df08f143ce56defc623363b6fe7f2f0c654431ab9037575f16bb0272d1f3f41d3254fa06f256607ba212c8989e4778679100299a207b59238727032c47cb544d735849e3a8318cef5d4ef6c6a7da24d55cf7ca9e0c3752b6beaf9b35a4407bcded1566a1ea30317959c68882a3ae53c9cb908f2f106506c06504d02304d1857cec9bdb64c62cc22f7f399619a49bd03adaab7b60aadaaa85445bcf9c2d796ce5f4fb3c4940be30b0709a007077b31df0d559f1b1f1afefdf2f1fbe49465ef6c8e1211688ac35a053bb0405e88aec637e0072987e3a6e52b857fc1518c1228b773232bfc60cf0680ea6e5dc61fd9bfa0b322fae8a643907f4d48fa735508ead99647693fbdbc8174aa3f38686f15fb1e30825ccb8ee81bd6233240854ad69a768db70efcec9a831b35ffb65d3fd5b3e18fecaffb6f88f887ed66bbf1c1b693940cfb231352f8c351452cec11ca09ca72201a9c9b2bda9d2cc9c80d0dc9664fcc61cd2d60cdd68f7cf60e1bb143d1f74493be810d5e56a11c6761221894f9612abe08b85da0179cfbb4fe2e34e3bcd05467a33c901093e824b1671234d8dff93cdddc40b661f894ab8ac5c2b652bc76fc3646766ad9d04b04e9b8fa0cc8eb0dbc910ecd7518bff0b18b20b9879a7baf9e0169bfd22a6b61d49de7df810af9010c0aceebc144e602419d94f603919298182f6c615a0290e8c32d11820c7a004407b8a4b864c93d013f62841b9bb31ab1b05c157d7115b50a50360168330e786a8c1a5f86b5b602d40eeb8cc038f58feade76090034c24858c038c6f8091790440be92452125b39b586a3ef29fa3e44f22e09fbb2005b3d76eb1953a6903dcd1733748c3c01c291334ff6820b4dcf5b3f25fc80e1eeb7679800ef0455332a107cbbc97c8135799593915a92243e1470ffa6fd26680198549c82b63c621c50ea01243682f720b4116f883dc9f177763ddb99efa3dce8483b9e9a9ea15b033abf10a1ed4d48ee27735949c08a42ab6885231c3ff4bc5206b816fa88311f0e64fdc94d3727bfd03a5f918883f7282f7d67112107909cf14948606179c3ea9a6eead53f1a6f72126d9fba7a0d28bf66075bcf75709afdf1e0b5e9b8b51490c810b9dbbcf80939c4e360c57f29d0a55303a10b9c29ae06bafd1c3cbecf41fe57a8c8b6ee0904047099e2f6de7aafe094bf0cdc0d932f252b9e51c58af17f179e50bb1bccd7fc827d1c783c028d2caa9bd1b0f9f6c66cea7733892834fa6ea06e7e129800ab099bed6642a356275ef087c0a71db65c99186ff081bf1bd8be91625cc461b2900036f98baf312286c07c6332f160fcc6a95d093e1366873952435b1b7c761caf1f0e0db68e1875d4d5e9ae3c03f5aa43acfdb8c3da63f3f095cef4f915b98a3bd071e015dcf1bb2c5d6b20f58de1c0edc6d3c7fc65ec618189c1f4c176302af6af6e7bca7e6e2fa52403c149a6fe057982949b666ff26637d1aa102e824bb8538e413f25a6c1abc306073162bc56f8363f1bbe5d4a40d821e28f25e2af00e0abaf073a01683464024efe7391b27b2cc89645e0655fea2d68a2338ee8e39bf4dff5e69b5a771a5c921f4a08bb35dfd3bb85682994f010a51cd322241daa5dc667e9514fbfe12ba656ddad15a5d99458e76bdf28dd1f25ca11005a8c42344445ac8325f3b2adbfc5e469db68f4edde2a7480fb0419c13c06ebadba7eb2b66ef32bee8276b1841b1af338bda7b89591fab44f2a9a28fc69b7cef03cb1870d1570864e0e5a2d20342252a6c34296075ca0e421cb41b8a4bddbc74a020e80954d57d090abffac9ec79188d440bdacbcd082886da96a2e64c6e5dc933fa7a5ea6ee497c93b1812c912ec24948f9c9b3b74b1a23250354397ac1018d710f3571a64214464ea019135ad1b4887f5fa5813c2fc010fabd90d64651a1f74b22b91940b7100f0c3313e1feb073af529127399a6c5d68924f4c993acdfbc9918d0d62b54c9586e2d0055f3592600a66dae0f4ce9dc1f0daeb2d608642bb21f5b682ca2c0c7b7a193ef0c4fc5d70bb550caee3954e1549cbc6c45f3821c58887315801fc29a19e7f3b574644f4c0a7aab1ff8ff0d96e3bd9b5515e603ff5ac615818398bdee20307b2fd3f2b8f5f55ae8d5459c9185ddd6629e989ae2629ccef3ab03fd9559174043b45c8fe300a7c3749edfa036476c5c77e617120892bc662601ab41835b1fbc6a6300982dc18e5b31baa4805908f3c31df8160e55f29de82baaab9b4b41e0906604a7a723d61c900388f2fb1e83812372a35ff97aab5a1d3442437988a6b87df818b57ed55538c0eafc1f7580a3da43c09acc35eafc0cdbb0d1ba9e7db8780654b9ceb2c46fed8831cd60e8e9a43e149801c9eadbf3fbf419c02fa9094ebe9951f7b11733c6c0265ec181be5d329ae2961c59b6289c8da2a34d54d57df690417f1d15ded0b30f82d5d31426db51fc2402305772f89ae77528c16623e82a19fdfdbda8fed8e07e428962fc36c54d32d40f0c36b4e6cca8b243b10967915dbdfcd68d913463ac7ff1090985c34057089c1e7cbcf4238c938d805383cae3fcf3b31a1f92a5ae8d6e9d1e0af017deddd1e002d375fa7cb1ed50fb62849d6ef82c9197ec22314536e9c9a40a170e3226590a910e6e55660d16f6a745c2e92a294bac76ae7d3fa80c8426db416e804bb8d995647e83a805b672aa7d46d528f6f333241e386226bdb0f3b46a2be4e531a82c451478871b6a75c07e4d5f1b14a4668f33dba2a7a080696f37b07d9eff974da104164290f57c3c296d191cd7e7364dc061b3a0e3ef8f315b0a65dfed3a0f73f7f15df8850bef809f8679b7c83413ddb3b7c78ff13b52a5e60152146df8d00201ac43b3e43d09281d5e41c2b5041b39c63c11a887961a62cdba6aacb1a4b4aa35cf648e3e9ef717f24336567e134c99184792f282068169151357e782f6c73d8d1689054d3158f1a49cefdf9f3a072bde45885032d05e523a9aa783eae47b801eb890c48d66cbe56a181daba06bfe541525025effc8bb6ab478152cb5828940e269e07e056b58f22e3e277d7f4387a008db60c7e2edaf7617456ec018cf6d477df2dd708e8a354aa7cedc538ceeec62929a8a5fe5bda4f88697fe95385caeeccae84f700b37f0d0bf3cc2a848557f0f0ef17f88b11298bc3cb9020f6a7d16cdb7fb1328749c277c655e39db4ec2a72ed03ab455431dfe2440aab345ee910a79f6204bbc60523d53ca3619ed9ac6c4153fff4dfe966d0b531edf551288004c748841d524fcbcd634adb5c76ff5bd767ada6a1b3e39d94ead7ef5b32258aa30d5b01ce1da9adc66595bfad89255ee3899b47d35a37eb0679552a6835ebe663a4793b8210472fd95f48dba9e478ab8be44e3060e60f0909a722ef71347b1c965a4be776ff0ac236b5b00341f951ff14bf992170bc260c0e237a5a28ccec27ebf8963aab0b82d4e016f4a7270e29a78955bfd1d6049b36c366fec0248b29df5b6f5b39f70241a4890f872b2908756ab25b2bdfa11c0bc3f07ec592670c604c099fb1fbd8d3490a8db1e767cc9f58beac1c16c96ab39e3106cb9a0f5808c00fbaa69bad8e45559bb097445113c3f9dd9c16556a6bb35dede524cd8096228e654a29d7af18e536ceeb2f08cae05248c1d088bf62d401adb5864434e9d86189991a93f5ad173c212443659eae2c72b1e016810fa6f34165e53be1f976cbb29fd64243e0fb184a2db57d28763bbfe911b212724b299200bb70e64c4a0f5ab1d7848c21b86828107f679de1fb95d161ac70930d9a8f14b949d284d2fc1771947da06e710dfc8e738e73344ece44e5508648d5ca9e3d05d176776eb376844ccc2d50877faa9bb9e5913a71e60f5059550be5395dfee67442c86ff6fcd04a6e557a06ba44bbef7cfbe07d6c4fe7eea3218b5a27f2ccfeed42a7e1b0ce4a5bf405e96801f2fba0dbc0cb7b7be323639e04f86f46000e067a05482535c245ef0b8766e1a8162e0534810ce8e6bfb01b2ce85ee009b102b845cadffedcb821f7cd1519f20f34430d84880e01ff63360d0a66b9d7e666282d40941737165ab6d8223fe87de3d8a3ab577197300be8dd1a355a4f0f1d4f1b13b1c197b624e9db04f5f67a9bf425c26d1f259fc2332b967c5947e1808ea68456b3e05f5ed47bf3b71ba1adf73ad1ed0d1357efa188e73b42629b32807004d982ec136101b069621a043d6afa6386ab3c6d89c5497f8230e07426f5fa35cc42eb4b47cc0ca9b068a20cbd196024db3341ba254c461a56663fd949e80bf10c630e774b1bd853f21b64d8286f033e28e0b44c71080543dfef736f04243ad1b21b78f17142490466f795e3d38e961cf0b5d8ffa2774fb62e74f49bfb95f78c712e6f777d553dd305923a10504b0609b969c9da310183624ca8dfd18e85aa410490e1143f7b393dc5dae87eb0be2f7a194df3f2fa8663f2d371f7472b5184e9c0fa1b0deb404e05eb8155aea54171292a7cac67a9139a189c506a6db08b5ca69c65b09a0795e34cc93585b92bf9c37f3714e520fd769d891cb046a80aeab6a5a070eb6b55b45449ee523dd2a20b459982993973497e4a4ece3afa6476277d8dacfaa5311485f2086f7b101c6205162f8e0d4560e4188681272f1215cc25336af90dce3c33dc3158e60c76be83115ac101c468c9ddaed68bfc895abccaf623a3aba4d470a5d64ca3f8375eb5435cb500e9fbd4eab50a1d877c2d79386aa3d6017ce366cbe0cb9bc12e1f6d2a1842c97e9fda54995f70333c78b321511e4ea45675de7281807e83858cc4b9afbe35d128baf0e70b85c38b96292c4574824750a5fcafd83872b158de72df853e07f325e3f1f092a3b0082e1f30834401f69ced942226becdc73d36335188b9c55e64946d9cbc832889dfbf7d937df0f1152522981d8aa5890831400c14010118fa9ace7ea9da66fa0ab909452590b04a48914f27c2165ca1db1544bd401b3bd20d970a3ee1a70f08b48ab0836931d203f2bc8e3c7e1fdb034a4628844d611f67ea41af237c3259d65041d90df4211fd2679ea71868fa2b313d76c7d371ab245c44f2fb22bdec9d581d636107325b356a22bb645324e2c099f32f922b3227ee424e4e2e647413c4891c85a69c6464d3829a7fc0e95a7e851356ba89cf40b9824e009ced91851cb21b66f1cd5e327fc2d19cc5b2762c0ef28339e4e2e809a0ae062243fe8cb96dd0b15a584c611eeec26f30d5465eea06a614b54784950d870903a93edd550afaa1086fec5677af6b10adfa344fb1f6fcec6c3fa2625702a844ef9d4ab4c9dd2e5f06ffbbbfde4df5fc3880c7e28570ceda2477a232fe5182e08d491ac7a885061e76332e89b60b2d62bbb14aea467cebe357ab477fa4ea3a6a2d1fb814f8ffa672fb5daf8628e1d2e177cb826573adcab2710d9a9c0141eb98f9689edce019b3936dcb4934676c738705cbb211534e16b36e6df12455a1a889cd8f521db401d3367d35aba959591b80e226c72619da17c4ed66d7c33968e404fb9913310f29b72d4103ee83c666a310794ab3aa2a6692473f90d2e9426a7425c7dd72ad6ca32f8fa375eb370d18c2b33dbd5ea15fcf6df409a6fb118f49a0f224723407185c30f2a5272db3f68b30fde820963603b4fb245a2ecbcf5312002a50d04e431bb6d4615d428e3bd83904c4718d5aa31ab17ab03180ec01e8e81240ccda9454f504fd08b82daa9920388af11a47cca4eb0c9862309042a76cc6f4cb998acb1622293acdc749098e8f65953e26166683c0d24f751ea836f6a065539160a3c3e622f8e33e05d6677017df8b4d16f8513660081436e90c701b7a0f56e4b5c49d66fb93d19f55aef5ab5678d4a0508531f69c5ed59632eb116beaddc93b40876ca8eb3ffa5c6bd3e4639097e149adbdea668e8f872832662ffbb2683688767594ff5afdd41e594a8508ea4a3780bb292c039761bdd646204891102bf47a9a7aac66d58a9a7fa19bbd681ecfcb5b3c07a2f9a4f221ba1b1aa063faf4b1ed252158ca0be3aaca992f04b5faef26d2c9c0d086ff3a44e49337f9fc8e2b9325f385fcfd79590086e826d9c52bf9585e79237e7f08ade213f3869c4590b8944fd6f6cbc906f1d6ecbb12d4c67c581621560c8c77b71ef8c80fff9a74a136515b4035a2344131be6936c06998eef2270ab08d99135b877eece7d7bef25528a6794a5b78deb7904b7c0b9a8b49788d4f306e08cb0c1e7873d49a88c0839b835f0b2e0ef5bef4f469961a11fafbcf1c28b9469f18ccde0d0e92a7b475424026282b134259c0481ad1d9cd2416e336f5a8de6bbfc4f5df7325458f2024f1df61e6c77151ee4a7d512ab8ae2c2fcb3c1d96da5656b583d0363eaed3a40c87fdbb1321f0fb23c8542cdf91a3b9f596e89081c57b2f4182321f62b7585f7042b5929a6d34f8194727fe6b5dce4499a801ad49c7c10f214bc2b5505990a74faf1b7c2826a60b5c58898c28ebbfceae42b56a0cecb1cce2b318ca79be87c645175d5766184e1ad96dd5fc83300774817cd494b8fb0f6e2f05fe16ca0e21f36435d12f1f6f3e8dc4e704066b9c1661d266a293cf1e83e13c184cd84e46c618e5e6e07fdef8bd21946b100272d2f3f73e571627537e926a2c169dd949170c9095b0f64c8d2027313247bcac4d3d35b2947877e51192c1dd9466fc379d283db683bf2edb39476ddf74d9a01488defa4940ebc0ed3790acb387ce30ec5aaddd5002e91d089087b87708c262056f31f2e19d629321cecddb4e06054b006bd5370f85430f71b1220e27db6cbbd37b12c10e14010fa823e71782a01c131f4a188b5eb64f5cf08074f42f7f132c08b83e885dcabb8fa220f70ee173b22e93cc8e4135d0f17e95d41827110c60e38d861402fdcf8634637d5444ce79deb2639ce253fefd6b60d3014d8db2419982c6ff7128e4076df59f85729aa0a327f913163b09f3ac53021091245dffdda6bdfa88d3f896f73583bddf514c43058b6517489477d9ed8ed89c9c66afcc4e4b68aa15135ed2cbca0b626cd94418d39a4cc0bca1241e4ba8cad23c663881726b380b31419015f0d572183a519f0e0c859d69cdaa23c2d593dc4bc6c88029d55a5d781e47ea0f91b4bd523637cbd119693cca59b5e9e39b5e41e7963433ba220a6598d7a58ddf7417deb34daf9ca54501ecd32a46d70186fd68fe33143aa5e2f96edcc7b52a32bf34782746409ad6a3c8aaef66531314ed1bd10e10195b16d555d50310acc94bd43ae0dd1a88ee452dd26a7ad98b90d83bf495a52cc020b3dc63b4f7b2f99795c1c52d682410633df1de31d2bfe574f6901a4635584644998ee16433ffd8d9268032c023005be242cb59be9f76602f59c304e1d6fc6aa452df34703290aaa9a61d782c9182bb0015891896767581d6dd98ee5bb9b41864faf2e87125bc8ba10120d16193cbe8eb1eb60a7f473f6dc8bb1a72635c2f62533a3cf90a87210a8560fc3ca4bff250535deb1da6771292888da8b8a768a9d5a8340124a5073d4103ca79205ab035728a26d30b0361e92eb7d78a2fe42f16aa31dc1e554af5ca70e65ab42ea0bc9dc271f84e83203ea636090784e8f7644cf2c21dda2c8867447a2730608d8f96c7354c106c5a4750b1f77a7564830a203874e8546dababcec4d2829d48c79c41583763abf82bfd253b6bc06ccafa3a86f3f78bbad7e92dc9eed67684c8a361e1885c0579de7334fe4e5813fc0c6fa654b351bff8593a877cfa83fff609b5fdcd499a3b78d493e657930c262250c3433402679bc5662cdfdfd4b95236412481404b7a9e70d1213ab377b7361ce337407e503f0738c9370f064a7c0b36eb3fb08cd33aedf75af463060f1234aed0e5b930559f73168ce340fbaec209c481baa0cc8317854c64b2600f459e5a1044a4c09452fbd730113a8ab2b80fd9dd7f3b6c4430a5832e5ac8cf6c77073d58e8b54121a539a59d10135a0cdd2644d17ec89a9ac8f55e84b53e19bee1e09d943b5b8b5e8f705f3afcce59cb738f2bb6d7cf907700ce08637f6ab9287efc59ec24410ae522c3fec78b1ae9ef54d64b9e52f4f6f72b84cd82422eff23262fbb41647a2e6a8f17ddb81ffc8c0727eefc7e1f746a3b3027e58f490cb61d440808a504c315b13521880af15a1faf19dcf28d425266e5c25c29d65de2a04a01a7c0ac5c79b48dce3818c51d121f0d88838ea0550378e94d9351223d0ca518f7258025feb3e3ec11c1711a91c9b86c93bd9d5fea688a5c54df0bf974b65ece4f275a9d914e8fb04b76b5ce1a69ed41f08bfaf6f9217a64a4d28218ff1d196c9028c39734da60fcde709a3776608cfaf3f9db58c12f5d3b451958bd07058802160c0000dfefbb0ab1840ee0da13de95605ed60340e6cadb5e23b972567948d4a74f060ba1a7a4e004b2002367870fff7ab8e64d85a1a690c5e581db678ef4f59d489b3a72f8852fc31249ed680f371684c97fd83fac1826384553de835fa8dc0c313c167188cf0b7901dce72f3846bba8a113d1f845140db7bd9750223d3b2f4674d7f690ed972714106add36f68ecd42e4c879f523e6ccd296e0025c15b01882a365b0ad9e5534c351016f7f991dc26b3de869896026c75dd2df4dc10333a69c8fcf406118a318044462385c1c5182b36cb312292d1895bfe825b7da43438f0af8748581c201a7c94a2c981dace832f9d024eca9c515b376aa4d2d295f80ed89583446139f8445d7e868f8e02dea2946ed25ab305c29b7725542d2a26db73bd13291ece71754d4c4b8b7d695583bc3b8baaca14ba294baf17ad13c2f40d6b190c46522db0bd1d50176dfc7b5ad0654d86efa9e9ca5bda1c33ef385badab904274cafb546fb1d3ca739ffeab6e12e92713e8098f5adacaab70944cd6868c1f63f2844058c71dae8eded62b91cda00830144860320fb5789fb561f06c3195ea1b2e3bf6d63a00498ee31ed971ede1569261086d3b23ab9d6851cc88cda4a6b6f5e8df1dcf5f6aaa34d95168c0a986e9f35d1d8008f1a33a035a5c85d6a1225152dbc36160949e91067f768c74f732351446e60da19782fb6d4ad271f6e8470c317d50f4a5f277758a48365a1487a735a60cfbe53297da0bfe945b0222623cb14e3baff55212a6ffdb11d316144b6ec5baa28e01f41ef1431550d48d1a9fd32b613ba26673889df51b2c3c11a03a87d90ce5bb922b4239565718a19a610bc02458aa11388abfb83a76a6aa6c3ea2a82381559927f6b9a939222deace02ab1644dc11ad21568f3aacc6be99e85db3bb5f9aa564312001dcdf38130d69893ccdc04588ccfec6239720f2d20034303a5599d2625ca91a04bef37e8255ea015df1f18ae048a2e9aa48c5cdbbff098a437455e144a3c32116bc8323730caa8a18668cc309a1f6ee32b6734e487c4aa1ba8525fde5ae2e54c4c3654b6135dc6c24690bc16f64209c2c3cb4935246db0c6d3a4c63989bc7fe2cdb943e17a6fb0da95de20aed064ab1e15ae48001fd1f487ece7c2190c8cbaea43d9112e4f46a6984851085d52f17c7562c6bb1586b25171445e9b995af049a9ec25c387ebc37b0314e71d123adbbf5186253451a7069abd6cfdb540931fef9760836ab0b42a22f89b2e66a9b6fa3d4e09448e820a9cb88c2afa2323b70807dc51b919aa0bfca30769a9bda21a0d170e9d19d6f9438637aca569f1f3d1951afa00e1ebbf63ce42ddcba031acab51c6a90700d6d1165cbf2929b22699fbc170bc338254cefcdc42b143c218d7a0e9be2dc51fa7642055ea44bafd1bd9d689fdb8cda4e534dc2a1ceb456c9a532f221b6baed048328892096ee2b6024cc962e0069eca2ee4b02ba4b38f50d714b3ab57413834d82802a88ac2172e99ae4128c238dc215461ad38baf1af4ac7f41bdea9268b9c00440d1d958ebd39372a3a88e508fe84ca9c0796c7287edd50bc4cd693e5517ff8555755b1b28168c02b507623f2118af433a299c1fd6e0ee75c24f0254198cbf40a102aec5c9b5c108b7af8d2f6e7c08bcfdf7c259672c350adad3ad71203557fc0eda6336b7f99e61fccd62895a413ca281e21b3acab8f1f2632ba9bd76b29744a1085ced8d68993cfac4c4bed1518f1f88826401fc8bc824876d490738cf85d9248d82e93b8ef2f1d9a3d9b0387c1cf4c7e2420d3bd16aa9add601b96008df39f334bfb7c9ebb417377a7d74578c62d2a6f1a8b11a38248cc2018b4958223374f859bf59d1dc4224784b447b72e580acd920243b51bee435a7794b65b4bc833ab3831ed113b69948707a738261efbe999d3c7e0620637f59c923419c622fe8d459f90aa4423abe444def9ef02d2b4745d1b04a0c6d5e1441b54351796907dac5a8002aceb48c19ddb1c199c354d79030bd720ab4ba6ae1b85f40ab2d3ea10985f44a18cdbf3268260c8937d0a28d81287ae9283341c18610786c2dacc41e67d11e9a83950622b049c0d4e9b25a7353db74d9ee544fd9125c8d948cdf8f10960c25692ed6929c56f8d71e8f45f833895a8f27a26cb4d03804e8efbb7cc082f90cfe1662e16cc9cb67817e119d21ad296abd491f3c1acae0acf64196032f09ee4085c068914baababe0c8f224237e69f728d255ef4a7082f90dd372b6bf4aa64891327eec77456c2c3af6288116f2f5d625293e86b4f2030e5d3d6dc50e8711497526358e202e61611ff2114caed218be70de87a81509979db3a73f46abb6e28aa0c062086dee372308c3bde554b5150726b04f447d5ff4db13f9c2b6d665a1b5b2f75efd2d7d6ef406c3dbd18a71bbc2d326feaf3d02858fca8cd95d9440a88aa004dd12a1eae3fc673d46eb48ca93bcbba318453070e8704681a8964456c028299e1b278f13fd73cfb2b7fdd4c1ee56a1a39294ff71d826fbbbc760159d8018257c7e97972a5b075b678d4fd981f3eb8c2e092ba600a2ab7cfd5733214b330cb37ccf7f893958f449bdc81e1a7e57188b4794280a0b74a7379edbae1f8250e06f428b952a198a38b68c887ab2c72650675881215796155a0afd2552e2d834b73ed0c4f0a83472263d0129601e867fe054c835be8d404704aa1b8bf64df62b1734caa89f21ecd93c140301c8f40b44cc82328b3c68736bc71df834ccd1aae8aa61d1345b800f0e4617ea3ad46f45c8ac9b93cf0a3d26a20d226b171168a41de79c53d300e1c0919ecffc39b4e18eb086d7227295b40f86ffc9bc7e8f7149846d6eee02d7e66c2c230ffffc4fc243f6a868f88f03a762d980a518a910c72e190f6427ff3b86f260ead626839372d0899876a821ff0027093c645b29ee78221237a4ebc0e30262f0c8fcc038e3810c41e9cab8407d274a8612d2d9ce9cc0b74bbada1bba8677e14317c2dbf370f6a8b85d3c483ab4f1391489b13c3e89e826cff021d455e5778aa4e42f76c5ff8f8e08311ab581008783d46a68de7b4a252e88200c5c4d1c17dfd167143b86f3f41cfa8dc56997180cc91ba70ddaf7fe7db95b159bcd2bdf9d8792245b10d6e88f020b668c18a749f2facab035069598af47c1d0f4b94252982a068f435cad6df931c0c123aa2920bd0f06a884b597f9f7fe440c5787c25686a5f0c1ac6d10d88aa1d89a2de7d739a073d7fba035d358c62decda39197af9c68004b07c5416c97d4c592106313b6932919fe7552db2f580e856defe3dc2439e6a4dd9740e3739dc2c8b3f7cca2f3158541b7040586fb2624f24f4d88fd01096ee3ae0d1ccef98b349f4e7c2c9c5056122b69bff2cb318a1d01ec36b9aab3d4adde507c6f4084085ec7fe5c541527fc4d66a177c6dbc5cde9a6df919e74aa92832830acdc5f5d98a64aa101022a8a24b5350e10c27e1c6e54e55c852ee0db34bd9aa284aed843658f94f1105b52753fb65e96aa75c6d20aa9d6cbb28546bfd519af01bf0beaa1a2b54e75f4aaae5f01332f28a90ca6b47e8d5260210a48f6e0af687d68f1547eece2960934a1e322feec728a55673f6eb9af03422d2e39bed5224c3957af7b67b570920761ece442140a2ea1862cc695c06f9f1612f173bd257f140b236eafdfa60aa1dd3208c1e21112f235a25dc6fc5f30dcba4e9bf05677c2ec715d2f009a2643a3368457ba813c6cf8ec90f058e823f456a64a4a72eb865f1e7efdb7376f3a040c89aa88c5f57487f3cb4c249b484d2c13f6b66f621ceebc8a85ae15b24a7e28fa718200bc8d06af5b86f7cbb3aa11bf69d85643558ac9ac2116c484e99abee3d159ec10b3fe8e76a6ec28aba37351d4ae6857f3e52e1fa49700006a66bf247104a5125fb90661a003c6f410c1f013d7c44a89afaa524a64d82e5aa5c8eab4872e0509380900c3dd1c1f07ae32bbd87175da6795d46187409dfbde242349a44ab04cbbab7b9d9404f018434fa62217302c5ad53ec5e6c58a34cf16d08e8efcb95b503c1bffb905e65be4d678aea9ca241b427dce8b4f90ae7ac166d58fb51ef3ae9010f9d6963e5dbed4d2ab1e1c78c41cf271904bed0f9e69c15366dd3b09b9999f963712939a502531862184300ee61b8a0625c5c89d36337f96b1b1dc08e5c3470d8a3ca1d6f580068ddbab36be1ee37e71596fee84574cf7b5f3a9ee6ae72da208fb6667ec4b5da430fa1f9a7bba4d791844161a82ef83180c360f960067bd852dfa04822814a9a0b7f6ba6e9b3f1717017354023b1496410d98741800d0df75308b2da8f571705983c8a50ee8f900f21205d587881b8607d0219238264944074ec7c452d869456b637b200742f213c80b88e5c84ffc1a4f563cdb49453f1ed73fc6aa2d3c11a49017a1214d63764b35a9100691e522a0417b7499511258540ff5457f504e77a356207a9ef08b69d59cb33d8152146e9a573aadb72c8f69e0081695c0d7c4c642211a2c685bab113638d7a498e48b92f1940448346b6ee68993c01a1e8251391cc0bd5983638fead4c7380b062ccd19a1077dd6a7d62bc78c514827ac427c4fb2f1e9106fe20bb300d33d138c4c612b5a6ce0aaca1cd5cc26758f9e96e40b5eccc20477141657dcbe38d04f676131b6390723eebadeff19d7cbecb9b8da0419fa47476327d9feaea473276cf28b38333ab870ea19013bf3fca9cfae836919252a0cd9a81ac546c1dcd342449cc1c9c283293dab5cca2ea4acfcd0c499986d80f78c2975991107c452509a29ac9a904258dc91b59ca2af8d12e76297d8fdcedc21e62b2a804606911114a0d67bb2f178161bb4ecce3357170ea28f9a70542ac45e53e470400182a734119aaf97b6cb5bfc90c8cced4cc1680d42f152a5a7ecf33b0128d719b1521e9c4ef653d099e4f6dd868f29cc302a9251ecd3a12ab212bae6f3a45ef9b2319a642e9fc5b3b177a9b04bfd434de4c467da5c98e476156d54e41d0100fc082c9be29cc1d887137658d8dea84cee051d3ba79f6bef3897872c8431cb20a65cccf63018385121ed703445b581a3890afdd04621e7c299db10866d9842e47b7e0858050305ccd461826873cbb0241c97798d0a0345f461ff463465f831eb55414ce907967dd80d88597ca20d31a9648994e3955362aa4ddcce0d7b5da528bd4c710623cfc41d2fb98a4c34d551873e5a8300b4870e79834304d393a830ca818c8b39c0cd6aa199ba0f814033cffefa393d25f14f4a439167c50b15bbf56cf47f03fb34a1d9de38a32be625fb802fcdd50ef0f444a7320b1831b411ed2a598fa9f161d26305827d22ba2e54a425e319037904c0498a644fd5e127de79743d53084780e66c3ebaae48a3cec61dd5be52f90e037b83fbaecc3a9cd27610ec862611a0fba73ca3dbea08fede088c7d1fb8a0ed34d1470e87a95ddb0ecd965fe2c0d478bd7c65599107d163d4a5afdd8d940f47152898681f34a5e86b2a1163536bf0f45648f5146e83c946f1f020316fd2e6f5dcd4a99e9939b2710296ace56022558764015612d9f6c632267a9127329e1976766a12f3f4b369065a6685ce3c4530d01a0d0ae862626e6858bdfb6c0e4653b27798eebf71793f61429046a7837dfdb1f2b306ae33cc0a046d7c1e1100f118af77f8798e0ee3ae74454ba91842fe57014469851e4da8ede7fee56d022d1daae71d10990d7806b8af02b5d32ad3c91e5fc8aeb39556e088045f19c3590734399e0efa49fc19b9c1c3b1bdb0cd9897ca089995d0aaf272fbab417c0f8eed9d588c4683bfc7c698e371d9fa0b1e67209c09decf5a03e139596a9b99da84cd36a5ebac1b2bab01e83d08530d738378158db01fb106f19f2b7a890135e2ae78154211bca3bf5a954e353205220e3c159d117a8fd4583c5c2c39bccd0ad10e667bee33cc6c9dd56dc4faa9024d1227091afe8c11dd199091e9e6d71a4078b69a0f48e225bf2fd2bcd205f371d0bbf8784e20f92e02ecb6452822a4812d4f4a4254971b56cbe906a70dcc8b1b4379cc7af2ab605c06ee42eb49c220c18cfb62305ba6c0783211e675bd84a303ba7185c12324c128f9417488df58c26edec0c462582ebcaf65fcf8d949bffe473f8b3372dc7047863635fbe5d8a35dd4306a1c320187ae1f41651a78292cc321263fd7dd6b6eed032542c33fd41b608b9d5c4fcf799a8e376f7e4d864519360b8d3b6061c3d648677e0396fcf1532b58e6fa173e90a4f48a246acbd0047317b7bf1a931d5ac124dfa5f57433f1ee427f2f16975de08d0d9ede9137cef2772a24812b83c1d1affe64c524bc26e4c6448268a1e84ff9d1905f5d87f45439d2858122d9d0c9842ef396209d9535924c3592180795fe2d650536eb42e6d406d661e1004b872b235bc412a6738175d6f8b501aafc26ae58e9dfdc0524e473fb466a83e158c342b056fe7ec434101fc9fcdd9c7bf5def43d286b3a784b56ec5122e7d080cbce6b1e1fe2889e14344ab6c1b633e2c757450af22ab8598246e25b1ab827cfc6f58c7716d75ac310d00ce1b827807a88683a5a3bcf4c3504d528335b20fdca39ff096d2256870214526583a46253784ab522e0ed5bb27b6c6498bc72a13730cc089a2b911bb9b8bb3a4b3cd2f64e1d2e28a031c909a8e2284b463fa72b8828a8f2868388b17155207f3b3217d052e7aef76093c692b73720132846e1d7c39c97ad2f1e65b56850ee3cc2fe6dd2feb2db4911234507881e2d7ed62e87c6425ac4c82c24c03078ec1dd46be3970ce626e94b7256994577386684d7a1bc03469789c857fe78f61256ccb3ab15019027be3607dbe8f2572d89aaba35fcd2f95cb6b3a0a572ce04982962eff052cd382856f754d732da5913e0c65faa8304346ffc0a96175693eed12cab184568eb62e5c682681f627b2bca09c082697740a7d0fd74a85661f1629c073a84f9a9eb9a05346c1a7ca583307dad613d6f870e80c61f875a6e8136b322ddaaea35ed502d28598e40903a33780823e9a28bcb70a21bf93d7a283c8368801386bd5dae250e1fd0264a1dcd8ec65bb0dca6e1215bb108a7b5559d2f08330b53e8f0cdfdea11e71c8c9ed7423daf6a8c19ee83b16d495b73b2d3dabbe3b4be58425c451a845f863d425253eb8f11e359c0f0270ce78f95c4e4ebf313b7be8589992137dc6b4d0c8f8c113ea280841f60ac3ef94fd6a451b10c9b26ed24838902c5c4f689c9973c26709b04aa30cfbcc620bcec4cafe2a69702a605c14bb2274a591efaf8001e2169293de9f06369f0fc0c63bf85fa509111a485157d9976e874936bddb618eea05094bd5de09866dbf3d9c73d17bbbc2d0efab41f0666e8dff10a76c3133c04141dfc864c4ee9620302e4c801fe3290a305ea8602f440c1fbba9133d74d6063dcc05e392f6347955792eaeef0595698f3eebf799fc1fa1315fc1008dde3af0602374eee3fdf7a2a8bbe3bda641e0bd96db3fbedfc2b0d14997e2c470a0354c5678c81cda694a1ed5dcd012fd96b215c7a76c1b3d8326f9e38c58e3ec62362c5b949390d5e43f3feb420646c346faae39dc1dd357e036d9c44f0d69ab42adb0cf323dc3aeed4185a7edde5d2ddc62e5f3772a8aa0e4bd2d40065885f75b0f9322ede65fe11f6e95afeeb6405087c4d395ed71d3e9746cd817dd1404a57a621653e0d54f9dc560f4a334e1f135c02d442de82032e7bbad5b4357e0c7bea749e5a049337e92e8062f24bd31084b0abca2e29b55dacb65e35033b6edf8d099fbfed00104ab8f1a365e4e849e9445fe6a5dfb01af958b6b6a60842e1e6f4f877f3413eae315532ec0305bec804d90f581040e86cd301601ad9d7a9bd1383e182584f4be7311422665828b783005d058383ea73c0dfdb36b3a11ef93fe76c7159a7072909f50733d741f12dba0c12718bf7f9f9210f9c060bb2bc42d9111983debac9ac9b08d952eecc0ea1105f10b70f0502b7ef8008dcfe5113b74fa5a99b58a124a84f3f072ba404f5e9ef60851e20c554c50a1921c50a1d81648590a03e7d2656680bf5a1621d609d947e8658cb8fac93120d8a51a9e53ab4de838d42ded6a3d828b0db2a9d584be9b94a273ee24ae01023b1c50a31d55873124d1e1f71256cd217be52d6d06637e131aed1d94a39031519fea45441ac8898ea8c8b789fa8a9ce78e97da2a73a6322de2782a2b51ede279aa26551de278a6a99d1fb4454b40cca73f8494ba081db279282da72783f0815ade9f07e10295adbe13d9b9d3f97669a97f65f1a8d463b8dc9cfd73d81c94ff09e70263fa1c96b176aaa3346e27da1a53ae323de1762aa3336e27da1a73a632def8728885aac0f2fd6f210482c21280cea09ea826a70b579768282827482726604715050501095215fae11e8c7d58027611989f316cb469c91ac131ff123ebc44894558ea062443985974aa6cbefc1f279b094fa48b1b44d2d3b584a7d74e06e452c11d174f9512cdf08ca1497ffc4465dbe134bdbe4212cad311662eaf25b4d2c137b721e3839175cbe03ecd9ddce25f79ec3057a9cdd13971b502ab9b7c4d2abc4d27bdaf30c48a2e5e9f671b0e717704396a6dbb7c19e3005d8d34cc018b7f3480ef003d2ed1bc09eb002d8d3ac41084cb74f007b9e86011861ea84c9604f33862eaa6e1f067bfe8800ae966eff057bc25cb0a75944ab9e6ebf057b9e8515744075fb46ec0953c19eb0148a68f174fb446c1654b73fc49e3021f63c51901844dd7e00ec091bba37a6db17b2e701604f4e0a20bc05eaf683ec0943c19edc13369a02abdbafd9f3272705cd9e1c0a4e50061401a9dbff5193ba7d13ec6996a023eaf67dd813f6d3e367eaf679f8d8a6dbdf614f988e1c37a06e3f66eadcd059e27a806eb07bded81ba593764f1bd24f4e8a9d19241b1238c39e1c171ce61275fb307b9e938243c13d6909a4c01a1a91ea843d71cf2feae44670cf2feae4b8dcb391441bd2d26deb9d653d3af2b9a42575b9044b2e074a75b9b26427661c7110cd101013d29e33879454cf98e3ee59213ffbd615c7dd4322b90b55ae6c7911464f78e5f6bb9f47bf179d363bbcf273b9cfc7e5ca2351806c8d69b33582fbd996559d812018bb7d5a6b49ddd36604609b0d7a4835070fea2ae19a9608ae13bee79146e0e37279240a90e7771ee9f661fd7e5de2415d3eade590626289e009bbcdeffaae09d6c425eb467db06819d732ee1d1589447df8fca64b6bd0f0c560907c6be538d1f3447034f295b48ab05fdcb56be53a8ee33aafebbacefb98983ccff33c21b50b03b4bab53255a69eeaa93163f8899fc22085d4f3431665d77974fbb4438278393aab620242d0ec771e12121252d310788774fc8e990f63ee8204468b28303c7cdba4de366761843a75176a93523024bf7637f5dce8e9f90de69e1ee61e6ed87bca9a1b966cf3a7a7bee74bcf8d9ffc9e52a74d18ac7c096bf3c66dd8b0414715eaedf96b8f91a3dbd3f3be4e4f133460dd20ac0f4116792b8e5b4feb299f47e63c0f64cf4269b3cdda5c09a5cd6a9abf952b4f0e0664bd4cea41f088592b799a357c77f0a1d7755dc7ea78a9ce9070975b91d430439b958b0149a4de081290ce98d48fd6b82bdd6cb3feb895499d812049c2ee573ab576b459cd3ae3a5a5366b12aea9a5c50a26aeb4b86d9144860c48e67105befcefad90c56e6c216c1dbcb9cd8ccf080fc339cc75327459f02f3bf241f038a45843de65690eda13c905c7bf2ed42e97cb55e6a4707442e9c8274757a634a2715d2592fb3a79cabc7bd9b1e6aff26cf21b65c695f9eb3cb640de55ba3edaf320489227ec8e650e4d99c2d195295d7fdd65c1d7d853e6142cab0b749d2e57f90ae240e94e5709a59bf111e7b4084ec9b539a33cec5c9bb0d70aaff2e47bb3c2cd6d6eec59e5dabc3e3e1c2d7722686d5c1f3b9b83afbb3eded89c7b6773f07517599edca1d46bf3936b73fcaba46db6cad6b9566b1cc17104cf7d3c0b1c415006aef375fd558aefd7f9aff26cdd9692d6bf16f8dff9df777bb63ef2bf2fec44d29e482e59f3712c4f1a77fceb35aff2a4c1c3b3b3a3a393933363060e0e0c76732343868d4d4d0d0dcdcc8c8c0c49be5e2ed7388a6218b65a2c960c19e509da9ce07d7d779cd1ddd7cd8cf2f4ee78d8712ca53eb0f2f4eeeb36afd39bf2e4eeeb36976129f5b1294fee8eaf91d57c2ccf99937fbdc6526abeca33a4276df719d7c3cbd8287c5de7c6227d5de75105beeb5f79b6eeba508f291c51a176b9ca93afebe35f967c7d9d2ccfdf57df973dc37be35d44e8157ffe8a63a9a475862594eeb6caef9465c97bfe22b9b4c937c61863545d285c119bcb385c83d48741f7b7d9250c469226ff6db609a5eb31dbec779b95f97d589f823f2d063d8c524acf96b6e0f625081728024b0aae1c4131821778e0891794800a0643f4941e94621d700fb9ae3fc9bbce1d4992a2196cdeb3e355dab85c8fc1e6afc7e03a59be4a9bd3a12445331cdce1b2016d7adae45e6720a51c92244533b46cce379d24459dd20c7566738574e338226011379dccc44abcb4f3935685303ac43303e7dcb926f31c580aa1402b9ae1489b372f66cf5d4d66cf8e759d87fc0cfb0a2d5f9d9cdb9cceb84e12969f21d795c8a15947815d2e9d92b09043f56d49d7b9c35ad659fb53f20801ab3971c7e69cc7dabc8ee075c032a7755da03edc3b8be4058e710f419deb805d2ef7306c7aa3595cae88c5fa107abb7f80642739adb228c7e6df59b62829e9e6354f92f1190fabe862a033c7e54e0f0212ccb9ce67b0f9cec1229edb582a4d25e736418a6a8cfb8e8e1122dc73ec17d66d2c957a17786d5ef3235f6c0e96476a3ea39cd2afb950bb6e63c7d7589bcfb0352ed4bb7ea4de755679a43fda7a433af4428dcb458770c4d3cc78437c71ac7898a5f98d9db90c2b731b4bbec6bec8bf5c1fe90c35c6ddf5b224f719ee345c11cd11d8329c6a3d7790cec0ee66811cc7fd2bb9366bd7e792ae95bb4b7e6ebf4ff6117e6c2c10830b2cf90913f6d4a6f5d293ec4a272f489b3f1ddb61feb005f814ec481a06a94301c9bd3472242debd2a9e39b572e70b91074eb8b68edbb15c6835b6f702b580f0669b3b659ff9544daace7ae20c1848ca43e497d51fd0cf5331c2901099ef30009fe3506d6d3ea83c6ea6bfd3969dc0a963fd4a7fedc5a74bd83f6fcb9e0bf8f0f72ff96fcdcee669bb5a7abea9cc29c22b4e7a4374b2f47bbdba50882f4345a4312416f44a74d7a0a6b935e88fe879e467f7af35627e0835e12b3346324226df28bdae41b0993a4632d7292d1087f49cb4e708479debdf2ecfeb24c6a8c59a077ce869f5dd249b020bf9f548c0da7318e499dd1137625e4b709935bcf35b4d9b721492f6919bd6177dccfad25922ec536e91635f4cc08913aa3d1ce37f9241fb6c4eaee05dd503a180c4a57e448484825146e1788b0ebc060bf0e0ca673d86bc8f7d0ca4849b8728206538031626a0b28b4b0dac186e92b7365e62e2de37ebebca49ee9a0b1b6d5d003ad6d21c46dbb6dc2ed07e91848c1503035b08da90a74d683c6faaf4a6b302448831176a209e859d416847b34f3a53708db84b847fdd9dc85d8164448f98ddf901a2f2edfee0c1eca9224486c00bf8ff57dadef0bbf4ffcbef1fb5cdff7fabe33521dbdb395fe0e5390688dbff2e4a421f61c6f8ff4e72bcf9715a94f570292dfd462ba55b356224274f65363fc1ae31959633ca3958e55a8b9059599d65c376ccb97499b7587124a9bf5222093b4ecb0b06b42679432a135eed6774fb4764211acdc5a2d777b4969439b956f8531b7010fa96100e5a54d86829fe88914d5198c9c824dc12e6fb97c32d3d185d559518df14926796a0c578f90866026911ae3aecb9a926c5364284e8c54a5618868ac294fc15470c95148a8a8a89ad80b3f7d3f190a5efaa050e175bd924bda26122a2a2aaa3b0315e3c08a2f5261a608a3c4a45545c36c5176545d98cb4320d97c46b21d7701f252215aabb476020a5c300327d4b320ad3192a4e407336fd3846aac87380e905c2956d523aebb1470ddaf7d2a3aaf9dcece39eee7eef4ba93eb9c785d231d92b1395d9943c37899c7e0faeb31c89c2ca98c97f9ab9411afabfb4aa7d785da2563e910adb30eaa63dcb91794cc0f771af77337b932477cce111e8bec853ae4ce91e76e34ced5b4c99de30e96dd14efce0eaace78a83c3783cb396997e3e160383fe9cdd2a11a960e95960ef1583af49c56e5b8d718f7953a30ae7b9b9cd9b381303e99eeeaab61655edad7796ccd6f69acfdf2021d4aa271f274cc613d86faf131f45db73d76b40a92cf352c8d97963c8fe16d93b0cc11cb221ae4bf0bf52d959dbbce63a97c39b273d7a9e87c7cc88580e4d6e958c4d3e21e9e0a9289baeead5639845ef01f20f95c448306793a7e4d8a72589fa17efc0c7dd7499bd39d87e74743346c4e7752e6e48f9c2ca77ce9ca23aff3945376ca9cee3d45a7cce9ca1c56e942fdf8237dd7ebc7cbfc6828a443de0baef1052fa748e62fd02199d7c39ef11525fd30f39b0bb54787c22a82966f8e9df90c7b731c2be3306bf3d1d6dc6569eed59c46e6aff025435363c35d06f79b194e86cccc6b8ce3a4d7c72791779d8e4532b7668d717f8ddc390e9020773ad449f5ddc3dd1cb1bac64a487bb89f3efc4181f68f1efaa9b37b67ced22166920fe353062384e168a83bedf161456bb69aeb3245c1e5132e77cf49c79cf4e6fda4555c615c4b11e4928e7eda64da8f367f922ebfd6be91346cd99ca2306cb5b80f903c8e17ea3adafa9d75500074a8ebd082f6e3cef2bb9f1a635859d3269fdb00497f92ac23ae3f23d43aa322a5344a6375264463445a66a4e848cbbe933466ad484dbe6741ca51939a54eae7e4e5ca264cc4cb95b47402b996f5483deff959eb6fb6ccc6fb85f39346fdc973eb98f37347fc29eafcd4197f86b7e6b87eb6787eceb8ac4be327cead27eb7fdedcead99f326eed6eb5b9ddebcf7a6bfca4b9f54d6bde6f7ecadc7ace805f0df65a9e367ee3a10d7bc3f6fc3de68d9292d586a5b7de147b2cd860c893acfd0ad218971c99c42155b2d65aa3b4a7c1b3a3933303077623c3a6e68a042908c1ad9fa997a927eb5fb6ded5baf56378ebc596ebd6b36ced380b529f5a766412ada225f052c5b5c1601521ed39e9d5e5eabdd3a19c5ae6b810fe7b0cad7b2515efad7f25952f47bcb74ee5cb29fabee7c4e03de4b1890a7cc173733317813654cb9d8b92c2839fcda9cfe9ca1c17becf3b585201ef81f74e8565439b5c683dee5f7c738a704e11aebbdced5b7f3ab64a0e2c9dc8879ff7d1af5d6391ef9e552e4b64b552c744d2267d012ff3f0b317b44ee465ac0f4cc296385af08a96de16e9103712f1135ff1552bf2f3ae74eb508f5f49c9ee2e9738297d48e6327882a265f50cd53399926f3c65ebedaec8b3b728f11453cf92be7897e9ee672d960f7de9dee30b24f91efb3d442bf589501aa4651dce18193a4bd2415010b2427249ea8ca76afdd454831f4f55f0845d188c244f90ad6aacbe4bbee2b3f6fb4abe953cd566fd82e0fde32db7beb7b4ac3b6beef7fa021aa3b22b4616d4a4b32b460acaf3bba2e6d24a73fbb29bd7dd54cbaee8197779d2ee533dcda523d00209669527089627f99527aceeb894fa7ca747e4916f4b5ea867e4eb2bd74dddd1d8375dd4cacc5d6bad948a2aa421a521b956783eec6573fe656aceef99819d6fde1c0a78679cbfa3653ce7ffb4cc49131ee3ee1c0a78699c73fcb45d3e9f694dc6f9cd36226726cfaf69998b26e77c1aade99c8f42cb74fcc6f9415a86a3e87cfe09d496e35c9e4bee7996de1e97b44e02b8689d5eb80b2f842eb8f0501c5d2f526686c605172c1d12807da1c8656c0aa7b12afc658ddc452334924291872c58b0c6bcaf604485148af49897c7258a2f51244551461467449146146b44d14614c37a45ebc4c273ac48529fbe0e7bc3c27472f4a0b1c3020b0f5f35363272e4e8de8d176a16c4b1e458b07448c67640204f93b4248f1bbeba8eec3a99ae9be93a9aaeabe93a9bae93d1759dd8591fbb8388a5b4c7d994b4d43c6ba8cca98e9b63877ec30e19f21d1b80e3d820bfb144775921392e5190003ca411421424004397a6c646c60d0c67064bccb1b6dfb04242dfb1281cc706fdc602b9cb022056031284c2c31a00000942c156abb1917103c39991d31273ec8fdfb0279cf01d0b741c2bfb8d9ddd6569a6093319d0431b1937309c19393ae237a6409bc9807e98607303c39991a3b3138a39d6e7376c09a593ef4daa20dd12be63791cc7f6f88dfdb9cbfa780e1c7706c6c2699c5838b9c3859f1e3c1edee4c801cb910327478e193972e4e4c8a19323c74e8e1c3c3972882e58166ce8c352fad38387f5d9412343521faf459c2acee0c49c4ed4f1c41d9eaed3711d3ae5178a62676fbc66dce4e89ee3218c459c2ae670a24e27ee7822cf27d2c071a36c934667e7271c450beab0de6f58da260ecb43c346016f0edb09d77bcf0d5f357274767868dc96a43863693a5b5ef235325124cdbbcbd03c9c4143d373f2dd5dae929224ce68e4e8922eee421971b4adcfb0173653f6b4c21d16795aa451c573a2edc4d2136bf4e01ce7a6f48ef3eee243d84824ce6895dd8c485af3a235dfb2b177f637f630649163c719896225254d9dd1c8d13565a248f3ddf9c6ccce8e25d7e64e6775726e2843ecec8c2b5a90c4b930b1e7fb6c7cdf8defc3f17de6f7c5be2fc7f77da28ed1c8d1fd3ed875d5dcc8e8ac4dcd0d77441cf637352e637bdeb2364e83e6deb0422e9c868593d6c98592d658510b4e2c94b03fb4f19a9e1ae579421b96d29e1aa5798f75bddb9520f589e5d0b1c387478f1f1e91c64cd8e2b151c08b8333637b5e1626731675b4b8a38a3e9cc8a3137b78e28f8f9c9d1f1def393faef790868c8e1d3e3c7afcf8280166e90e51f411451ea2d843147f44d1872896208a2688a2153bfb82892e6a7bcc1d360a786fce55853c58ecd1e24f157d7062099d688227fe00baf9cd4c0fce8eebfdc6b4b9a415c9b0078b3f2dfaa862099c684227fef04420190da53e34dd0fc893bb9e58235ed18234560766a38077e6e18f8f164ba8a2099cf8a313813c51369b91b9b194fad8f0c162092d9a50c51f9c08d489324f9c7d226d34227a0f6f889d65e1103b0b86e668b936c59828635fafb1366514f0be3ee3dd77a779c8e355c2eb5532f5f12eda9675d99a36bd9336cc415a97e5a280f7558232e7aac89394b5483b81136b9d68f344143e3168342243a33323731d0f67b4164fa8628d136d9d888227068d4680749deb2eb2f4eef2deb29dedce320fcb45016fab04c590b44d1e3962a6cf8e1ee0e7755cd541a98f77dbba3c0a01ef9dedd79deb3c1034d205507282eb59d297252d59c7e492d3b108a5ba5cabb58405defb92f748b4a4a4dc9758100b2e9750aadb9dfb12eb796f2d69ddb342b65a39b4a4ee12abc43babfc127b52aabbe44a949cd25a03cec34fbe2c6ae3207eb6949f1cd24fb0ca4f71cb4f32899f302d3fafc44fd3cacf9fcbbaf2f307edc94f2ae5e42705b3048b899ff486648f6c484c4d6d7a79aae13784a574442c15712f5ebcd8a42f1ce6bba24e65699ad2d254845511b71b6eb8e1861b6eb8e186052c6001389cb3346579ca0295256a74bd4899199a1a1b19263cbc81e1c09a880004ca240874395bb1fcc3f98a652c070ad3b1a42f0c8412d00de8f6e432272557b1b6a436dba6c4a484b535d598eda94d28a0a2a858c2daa498b0b6a90adaacd87c1885c8b86069f39ec8936641450355a5a18a062b1aae9afc342f914ba4c94fd825d2e427798934f9095e22a74d4ee4610ecd8d15dbc359892f50983c81010618608082618a964589010c478edc7befbdf70e0d0dfd890dc08b988a9a8a9e3809272c6b394f208ba5729680e523a8b01367242c4fe0542c67391f6159029780041ee234353d94625be7269bf46549cecd104543142de9c06e4845484549e50c9e16ae5a08a3852b1b90d01458e16cd182550b4f2d2cd96c369bcd66b3a180020a414fc12a85ab14c6a4b04515cf8c5b0ab714c2d0c8a1290093029814a66c187d67248af29d7108777b7878e3c7c3f2882bc1c5d6aabcb80ac3ca8654b332e1361f6c392025290db1b485a9a9ce5888f76b5b5ca94dd56e35aa96b1952345564e403911e584d404ae264005040404040404042493c966e70848d5196bb9c14383876984c8cfd4140f1e3aa280a680947e5cf5e8d1a3478f1e3d7afcfcfcf8f8cfd4cfed87aa84eb88d221a5634ac7153a6e3ac2e8a0d2b1858e2a1d563a5470fb3a50406d39e0a0311249a952e5e310d6051fc6fe44c979c912254b943cb9504bb14cce854b590303544547214c8ab00a22f424167b629b7440e5b8ca419523478e1c3972e4c8a143878e1dbf017523ea8694cfad7daa330b65a7b051960a2b65c1d8297bb31ab87d2b85f9d892874b6ccf43ae416bf0ebe5f2d4e1824bec929723115098361b0497884b2c1015a8c4798b05bad518558d015535502b51d6c0744c4837031b3d36de039636249d25bb64997e7bd48d1b376edcb871e3060e1c38cceb2ce930e934d5196b8959b91d89a6dc2e3da4522ed42098ea58d76e0dd4bcc5267d79722c1faa8169a027654d8aea4b6c65690b5397262f4f5048dcbecd086acbe135301de36c208591588932e90b5fb90f34ae8a40e14649472927c75a6badb5b62ccb1abfb9b971801313f130479597e311120909a91c5fe0421d569c84ad59d558ad8aad4d6db1b52b3ad6574a599372aa495d1d2ea24acba6bc6f5bb231d9bad89a6c5e9ed8a06c5136266edfc6a5e645cf92be48b9162b29f6c7cf0cd2cf0d12f5d1973125036a6767676767676787878787c667666696ea8c9170c0971e9a226bb13257550999307a96f4053c6b2965ae40bbc3e412f554675c044720aace98082b08dc707070707070707066cc989173e61295436424dbf314948a5cf5e87165c8902143860c19373737b0134955a229a21b1148e53646a2274747544e47a2a30b7588a377c85425297d8893002f23a6679c44cb9e3c0c3f29816e36206d482d7b92d4322a2d3b3a6b2993be38a939b13a22158c3eb14e6c847885cc94cc4d86aace6494c82b1a1a1a1a1a1a1a9a9a9a1a9b8b54e215e2ad677c048f1b2391942a55c21c226fb1a254155b672da528d5e01765e383428c0ac7845524499224499232323233ffbeef83e283eae15d214fa0d00718e45993ba5528d0ad06a667495f18e9587ce031866a522d9372f147642d9691b81147b012d686546336e6626d4faccd89b5e51885f4b86ca5b429791520cf4fe913e20be253fa86685914a34379e2a465cf96d490d695f2061b898be0250fca631ac7711cc77174b95caf03c1c096439db1961ec2b42c0a55cb8c80aa80c40059511d74e008238c313b442d5a4a5bd228444a69439a810de94a096fe088f14dcbd3962724ae90a04a922449922449922461000318709eb53c6981d212a5450a070fe6202ce51e3f54dc88e21f90a2380b1268830d63cc608c2cc6d0628c2dc6a0c1183518c30663541dc5e0480647588c4175e482a3171cc1e028cc11d511d51855606c4829e0274b71d218ead3ef497a0a622a08a8031ce0000738c0010e700024489020e0fc03d20f4a3f2cfdc094801f551ddd8ea88eac8ebc38ba3a0ae368cc91182d738254673c84d2183db8fd231ef880a2e7c908534f4753a0010c200409086102427042085e844001215440084e427832828051048c9a1042931117a3258c9830ea62d464d42484a7296a4015e02743f120554ab93a5aaa3266000318c000063080010c80061a6820c07998e2e1c603150f5535dce8c988c9a8c908ca680aa328232a8ca48cc0184d19dd84a081db3792a2842e25d3d51293d11238c30c5df0a08b2ebae841175e74e1832e7ed00510bab83a7570eee0e4a20babf306270ece1c9c624eabd3aa8b2b2fb649869fdcc444e90e88824205454a00021080000420000108e0c89123309cc9121326264d4c9e62f87975569d56e798530c25484a929428291942c99212a62e8e70fb6711a82d873392e52158574ac94a3aa0aa624fdb930d8a9eb11275c649fc27788d6cb91126ea15b3309db328d5196f296b5635c6d4327662aab3304165ec840aa879da966e7329adb22869b14036a031202c9c50019501c1809a4e4cb50ce8d6a60d806a004403a02d80b400ca02680640544058d4aceaac26558164001403a0304030a035bef23ed00b688dadbc0fe402a01bad39a9d5acae18144ea01ddc3e50550309a58035a917ee0b3fc11daaa4a8ca409506aaa6a8da401507aa3a70fb475115c917245120c100d47562913c417d9058c08a457201ead3e72b160914d4e7c852eaf3e4e480246e14a574da41ca7590f21ea4944e9c448904caf35ca516221eb2580509548df5594b5554115b9e9c062fb9f027f6069e5108cfbde16c3a955658618515565861851558608185167e03d50d553758dd705567acc5012f3a92a83a43f284040a8914123048a6905c81e4d6b227ef2309d33227ef23a1aa331ee27d245575c6425491e0f6918c80da72386891fe33124979952a9e15463a1d89aa5ca879c621acfba4642425ebe40c847d72c662a19c7fb0466fc23211e708585e3a43c01671ee62a33c8993780ee71fac932655902efff090d65a9b69e8e2227e00ab03dc8a142952a44891224552482105154e802702401120ea4b0f0f65222b616d491d6b24ce5aac0da9ce92584bf9c5071a573e7618721aa46898a2e14603d200ac820409122448902041888888841c862818a4609882e1c65b8c78c853c24ebbce19ae131bf15067c784769d3adc5699e4e42d505c98690c335501010204081020408000000000103a6f81aa333e22aaced80829de32b5330ae1916259370c02120689552c279db558663a2b61f90167262c0b715ec2f210672e16284cc79cf870a05b9db1125bac0d8824ac4d4b927552820d2561f9d0971d0e05765b3934f9972a671fac13275048467a95120aec323914f04a79131ee356b94d0bad5179df5604ad1dbd6f2382d61cf0be6d085a83f2be4d085a337adf9685d6a2bc6f0bc2a6640382d65aefdbb0d87ea0b51cdeb725d15a93f76d3ed01a93f76d57688d91deb759a13529efdb9068ad4a90d2b7dce6247eda94b87ddb5203e124982c6b393fc0260941511f7d1faa200df1031eb20e5c6ebf87256e3f0a13b76fd425396a8c44460f75183d34b2381ef20d7a83ad18d993d6b4c3c64844f3d266d39ed88aa8e572169a17da53d3a08c8ccaa025a32b2d632ba6791c3c524a24a851a3460da4d22a168bc562b1582c478e1c3ace23c523c523a5c387f7f4707948b3285da88bb8cc84fd62748680cd5283b9f8e0a4f4a12a31565e5c851134062907b71f74036ab3414c5bda0c5ae2a41f38512410014f140f153b6376aa7a7a7a7a7a7a7a7a6cd8b071e3385120881345b523e9364622f036c621dc0d7da22e68a33cc91a9d88cb4c36684bc7ba34b5392e05dd96683ce8d6b22a20276dd1b2aa20ab202f82ae82c2e0010890a01edc7e100f82a8e859d29728465151ec0eb3c6c481828d8155fdffff5b6bcbd740d540d540d54041f98758828b92f54108db23022e4f503db1c513554f583dc1c44f20e9fcfc5c1d1d1d1d1d1d9d9d9d1d9eb3172b212e68756ec0a6968c501991ead1e3e2e0e0e0e0e0e0cc983123e74496b2dc1c8938882c3e7218810d826c45a8d94a784364264b7fb82c8475e2259628695eb294b4a6a01bade916a6659ca565464ca5951a5713c985ba968c5450858669e63613555353535353535363636323e3ae2557936b097c6c24e22b4646a59392508766d2e5240b5a11bc61ae80635c4aae21c6a9114a4646464646464666666686e62c25700c498263421382962ea535b56c4bcfb84b99f4850bc2b27cb87cc5066dd16650558d814a04dd6a4c89fb249541542015a80538036a7b026ed1322a9fd2030f52f8014d388837ad72b0ce980b337d562e97cbe572b95cafd78b6c01a539516f75b643cba83cb56c0a0d8a36052daacef801475722ec71258794d40f98a463140205057579fc70d6a67b823e53bcc45413544d3435a1b464c992254b962c59d2800634004716cbba399c957852c2c7971be6a0a2a76aac6f6d866928a2da54b272811bca2c30850935041294114860922433c8428b2d6850031bdcfe5155652c31a03e8d45068c050bc642e502eac3585e407d1a0b0c184b18c642c558a8184b151538a270f8c951428cb921c4d3d2d412940214a000052840010a50800d36d870c37908a4059cb154d51933dd5a56254ccb402b5adbc18b9651b96ad994305ad6c318c62246cba42031104a8ca5073c983de1e5069629300109104302624c400c27c4f0220605c4a880184e623c4d81c094084c69428ca6295ca62c318589295da6344d6912e3090a1b4f08602b52d407d21596255a3b1a53800214a000052840010a60000318e000672b53483ee5690ad3942e539aa6404d99624ad4142aa6484d0133656aca4d0c0ddcfe1429684f5b7a9688b0b4346509aca1862f78f045175ff4e00b2fbef0c1173ff802085f5c35d141931d34e1e20bab263768828326396822a6895513ab2faebad46022c04f66d241a98c12d503550f5232c820830c32c820830c33cc30c300aec3120d6f72d5a4aa899826564dc63411c309929324274a4e8670b2e484e98b23dc7e93229ce004daa0306d0659b519e4459b636a0c4897a4259808bab5d94c944157351644132aeaa31f34a6654662f48c97a85267cca5e8b4c80836623c1ba11414654414638c88e2085537ca08a5db340cd018cd298a235019cd096a4631a665b4a63631408b82f6850605ed02340bd09ea03dd19c82a2ea2c68a9d22a40a300cd0bcd09da046812a0350505455dda14340edc3e0daa8184210872c145d0520c31fc04b990828b0c70a181db77c2c5145c6c800b0e74e0f68fa22a0e5fa80f0e51501f1c30407db880ba7cc5e2f0040e16c0e1023840e1a4f483c4e53a38299d941808f0516c14f24681712971602b4c4c5c2c0e504c5c44b19532e90b0f614721ac6b54b2120c4b6ed4c792a6264a2eb8e0820b2eb8e0820b2fbcf08200be840aca8f1c87a83ac3e10907287080c2410a0730384ce17045cb7a781f875bcb78781f87303850d5193fe07d1caaea8c95e08204b78fc308a8ed49d095cb4622ce626474e5742432ba507f1cb2030e9e8cac163be5bc647b380f52c216d9890958e672960095274daa20dd2a5ab44ce11fca05582de066c4881123468c1831b2c20a2b7c060b4fc013089ee0e58625385183540d51354cd198880890420018044c21e08600242456448810214284081122458a14790aaf212a4bb7809a0d032c68ac453081db2400224a80c9ca005e9181a9c69a0b9732284c63f9d0170ec289994a199e6aac1f74abb3a42f2c4459662993b0951a986ae85243530d4f3540d53045cba86ca181dbaf410a025011408a8888888888888848881021672b76c8f98a6d1050b345409b0414a82f03530b27c9168e0593089a4840637d5bda90b4d9dc4495972a2f4fb47634343434343434341480000480ad58d60df217ac98b8e190185301357b895b760b68ac7f82007011c1d0036e18802d3714228256ca5908cb4544e0cc645989b315cbc33908cb431c89fb80079cb358272d22ce5d2c16ab5442c0f2706ec226f112672e4158d0201b1421c4a1c0ae91d27ff817b6722622cbd94a0905763908be722b0fb201ad3d793fa806413408da82d69cbc1fa405ad3196f783b2089a01ad3110ef0751d11a27bd1f8445900c682d2806b4c64aef0785a135fee1fd2018d01a07f17ed00b688db3bc1fe482a01bd3cfa01ddc7e50550361a60f8da9b13e0f619d046008ab241edc3e0361451756f4e0f6198b17b7efc407b75fc58a1f580184db7f72d53fd8211d28d9a11d086187b8a03e5658ddb662876e407d867090c50ee5c00e89a13e4faa30711d98b813c65232714e323a036164543a3113e590558d259dd40a21502b8870fb435f50a0d62f33d9212b6ac5d5ed563954c54bd82f47a6fc96457714c273870cb11ae2c590abfe903175c64cef134122424489c8102debe1c94f3ae5c3ed13b1426d4fde1ff2346489d618c9c8c8c8c8c8c8c8284a94283a3ce843550e380892a4037ec2422162104a4444fec1960d026af6431544260b2221215560889398ce40b0950f415dfee121a7449e41b71b74fbc102996a13c8153441371d7ef396ed28ead322a024a0669f996c9f80c6fa344fb0378c060706e4e7e51ac590474b470e16f8c53c1347c7a9509be98fcbc959c9d2cb562cbd0c84a597b1587a1902960ef10f592e27f9609d98e97ce51d5567dc25ea2779e527cccaa3ea8cbb94cc44b984cc52e52b4b561ae9a8ca52b7850b131395293df020e5014a7012768752872846509adcfb13274cdec4d2cbc4d2eb007bb6086e3f07259e6ebf014b96a0ba7d25f6fcc949614413cc5c80c572bb6d8302864082996230dd0005b0320533bfa8fa0c321061e9f663b027cce4e2e92fb8e005d5ed17ddfa04b7cf823d612b18c1c1d3edab7003aadb4fc19e9782438110222c986e3fc80bac38299aa87252efd951b027e80aa48e82cd9e1c0ac013684f5cddfe2c68e9f665f634817cc6985082094ab7efe3a7c60dc6c3c7acbafd1d3a78623c521c0e5ad3eddfb027cc064ed4edf7e04405ddce49f11aa8dba711740377d88b17ab4b41a796ba05e43d699e401b5793ab6986a5c452025f750cba9db07bd22da2aca8e2260c20d51c3112cd555cc1fd80f301f802f005b4d6d4a74f41f082965ea136bbeefba0b459003aa3363b3d454768cd14800b34d60396e7cd254f9def0341273bd4a7df9d03410e04bf8fbc3b76e88cead05ad73a24ecbb5e7932ad4d09e11efd7a3b139c30fe4859e01e7dfebcf3bbeb84b02d05eed137c138a4729f487db86329b06da42cb0adb907bf53c0f476bfd3fa1da4b5bedfc191e83ba20c046bb97db96f99fa700f1e897855ddce13bf100cb213d351d599d959755e5cf0fb5a1ff830f43e100441b2def3bcee0c06d96dd16677f06cbb7fdd0d688feea1d7b5aaaa5a26a667473ece82f5963d427d66e80e03edd1dd0ba9ea2ceca864fcee4445a8cd081116d013dcee41680d7c47d6901d2882cda091ee27505b143088da841885eec7ed6cb73bad7bcf3b0bd3a1225b4add5b54b4f6bd6b5551ddeeb5447dba77589067abaadea84f57648fb4d9fdb333b06d00b447f74e8bdbfda456dc0e89fa74ddcd1d3debb6e858f7ce2b3baa36bb77af2ad713dff916ea80f4c4300cf511bb2a92de8baab38e08540a0ac4e6df3d283c285a0bc3509fcf8bbadf29957541a0e6e74171bf77577566538631a0b1ef5f4883fb5ddd2f1473bf83e5d98d71bf7378b301e9fda456de14dda2cb5292bddfbd2e5e53cf8a4eefe97ef7967af6b188b4f9bde525f58cacde10b4e6a4f4ae06a493cdc18e06e4eb27b5227abdc715ba263cc675dde6cab8f4647912b9e4457bd2aa0083e41b8661b7057952ab7a5dafd7d53125a135d73f9acbd408e9b961d91a437d3e6f4a0bf13c1239611a3b639db00c6997d02ed7d0b2f1df91b4ecf5ef367422f9a25d7a06e334efeb9c0e4870850e0f05bce2e9f8306c89a148b9d5a2b148ff812c8e5290721ced7e6730486abd2bdafcc2b317e67e173d17d01edf43af0b6fb79685e9d9928e7dfffe71169f385ad743bbc432a140b8df1b407b7cf7463b036fcaca49c6549dc928bfe6fe1d86f0df073003ad89b7f027c5e27e3f426be1bf9e773a30b1450ec53048116a8b02ae406ddd151ba13556589e1d18f7a460eea782fb9dc8cf8f9fce8afa7c1d15799af77b6b0cadf13fd63fa85b49242cc833bcddef2c5bada8cf97a4cdef6c97b4f99d6599509fefdd15db9ad01edf3f1a84fbfdecc0b85fc7447dbef777147a66f3efb49681ff6e63bd2b3af679536d7e9fbadf69cf572a216faea6b1424a075d6200a66a071aded56d8e52da2d54637c92a1cd590c07297d071405a59f211b203600b8cc0595754a63dc26c2ed87bce484889b2e0e5863e4737e349443963934e0e49453be909771b23c82f39bdfe0fc6868c6616f9d84594a63e467587286a543f4e6422da6cd1bb0e42906030609fe6429538609335b9fb182eb27f925e3ebaff23cba5ed9a48e713dab44e61e37714c1586abf8a94d2f7cc563b88aad90407262903296a758acbf4a06c392e2db42ebe7d19d299d702ed42c556738255775ac4f9ef9b2ed64595672ea710f8fa1e0cb1d698a5bc2f5421104c2ed7f1c84dbe7188cdb0fbdcee678a752628c70b925326492e6df48f47a8d251f822fcfd6c992ad580c57b55e73be6cd532f21dfeac398d929abfc693e5594369c49a1a9c19279cb3989e21819439777af972d5cc677ef20bc7f2ebaf92a5a64814dae4a899922c9d70ce379cdb4da6e42918aa4df23c3a198a1538eee19d2b3deee13df446a2be2708def35e673d7c59661d817cbdc9f3ce02cff24e7af12cbde4abe4276e8a62033b969fa19bcb381d5560754520c1d2c94614c356202875a1f475c2c12969f70c3601c981304ba90937def52ec3520ae4bdb31f35bd7f36a4a64763dedbbbb91eab6431560d54c5d4eb9c74d7f3ca25535c287d6d38923ab329c58ef175aebc7ac7eea1fde286cd9536e7e2db592a25c6ea8e445c05453295f71dd1b48c0d84347c5dddda97ddb3e7d67b5de7894c592c4e07e48c10f3762d06c37654dd1822f77b4704f2ec6c07554110aabb0d81e45a25e884bb9f5d42bb5e297642a85ebaa7964151df89215925c87162574eec3ad1fb462329945c77e545d1e04f1df387c6120ad2b2f1e229d3e98c88c75e30d5fa62bad53b8f445fd77d4b64ab432faec26819eb3ce6b6fe2a4f1683fcd92e6aeb052f8d3ce7b22c5afec4a42fde4326c7331864d896bb23119b80e45649724eebdcbb5bebbb4bff6888ecd269e64743321fef38fe87d6687746c692b60b024987664ede2d6dd62932a7997ad78735640b244fd5193f8dfcd466fd3245a67c8dd57769a636eb3b3048d687c266eebe60db05dc11d7550461a595fe90179673644a39c503c14daa7f3414b258415aa532fba0fbe90eacb4d6f2845d5bfde9bdcbce82dd759d5772dd2cea204fe32aa97c0272c7e5be03e438af2b9dc8b850ba9f1a8490523e45ca9d7f7e4672f7bc73ccb980e4cb9d5222483e3d91dcef6ccff3c30a965e0742e92ee79dab48596e5950a114b3c20b15d7f3613af7e7eb52f6a8794f521cefa55678b1e2eed9286414d8e5de95faf4791cc2dd2e4fae0fab3dbb3cbbdae7cb1cb313580303f90a35cf207cddc691d11adb5807bf90c7d6290cc6e260b52d0882b7ebbeeb79defdbeee8220ab82b5415114592eef7cc14ef42ce879f6fb3ed1b31e088a9eed589ee55a2dd1b335f46c8b9e57abe591c5f2c691e5f2ee7de43ee6aa8b95fc801e0869734ddf1ca52047d47574481518aeb96ee64abb6de56a656edac3dc54487b98d6c4d31f3e48fe5516c9fcf55739e5759972861aeb1f0d514156d00ab249cb4c4401c66c596d95e6ed832320cd97e598f8c0102845a200a3f285ff2a61649de5c4f03a79b29c7234f42ae9501f1401d96a62bf1b36e5eefab3ca0d4bb00af268e868e8a4b77bef2229aa333ad44d3586172d37128936672c9db440644877f1dced2e1d55e82e96a7614929db482b67bd78b64af8e35b36a748ab089f5deb47971b8b703996670f2af01d8570456cbda1e5cefa61693f68ed08f5a1331828bdfd507a3b7fee78a907c8eedfbd832120bfdb66d3ca1f6d7e2527be2c5863b524c7067b500fc57125c7bd1bbb8ee36aadb46b37655a2a21a54810a6694b1657a514155254bc908c195205159e47b7bffe4e6f4b0da98ae23e0e0429a59541da62c7b8d3bea778002bf0e0455a735deedc05c8225aab97fb91195ac6e75e43cb94688d5a71b98354905d6b8636b9b7ca236d722020cfdf8e656bdacc8941fcf8b1a4f28575b1fca1d5594e0ce3c58b2515f16349ab31ee5ec9bde8032bd4e7a8d0ddbb572ac95101bcdec1eb95399c3b5cef54ea16615dafcc51c17bebad52498e0a4abab7ca205e49e4d6d026f72316490371e74621451e202b10dc4ba136eb0c04491206bb99f572ff1122c21545913524d801b2cfac0fa982fa3ea40a8aec2e785639a42aca1b89582c287d591f45f0f4b24ad905cbaf765d51c7b8afe948cbd7d2ab988288a8290a4499200743418cd8c0078880e256012286fa143ca320a481a0865cbfd3624715aa60731e3306e84ce46a409edc1539ce06243311d29e90a3af9491a80fd6e24510ae190449927225d726d840f5f46dbb6b11c8a6dce48390f6d423dcbb720aec743cd2dd2ba790355663b479a8eb017972278803aec0c3573fe4b8e9d8462a547315041d48437bd26e7806830c4ff3762137df163da55bd478f6bb21c781413dae5590dfc3fe684b85dfae8a3c19eab24e1d409fa04074ce0acf3a3fd11a8ba13e1d94126ab2bc5cd693d4994ec93aa031d6593eb8ac9aae3c29112e57753620c177e7b08801b5b9ce7ab542652c6ab2c4a84856accb38cba6b7e8d9ce59e265b180e8fcac31ff9e9685e338860fbdb0a1686d6c28bc30fcc2100c435618b6c2300c43310cc730846aa9a72ba58321c270b420f569538675e291ac8733d68927bcc8b4477857477b847d0bcf0a9d78ae63953ea4f31d9d32bcd287784ab0c6c2309499a1a9b1917103a31165b02e83b163937a08cb87be84d7b13de4b251c8d7190cf2e60c06c95497c54c3b61784a677da33156486f40f20046bf382d4278f62d881b866f2ce88cabc2cfd8be1cd832f2e1c519688de686a00d482e60403811aac6a05c5581b82cb23a9166cb5c27bfa3653227ff436b33270fabe499240fd6198d930f6f70444e07646530489b33e9b1171e8b75e601b5f593057a7059e359afb3ce56b4e63acbbbd932f2de7788f776eee9786ce5f505bcaa1a63dd3b53593fb5c9baf71ddb5456ed026a5e80ca3a0c35afac2eeb616d1ab7658d1e1b3768dc96357a6cdca0715bd6e8b171e3ca947da33e323225f5110f5aaf6f7db3e9dbcf65426be0594fb2a465b7b11141ea139e455b56b10627f674a20d4fbcf18938bcd75fa3eb65b936bd917caab3bed5a5ae48505a4119342c5b552458af4b64e984c7d45823fdeab20ebb5093a48bf519bb7459bf61a8ab64736d6c6cbad8bc1343f20d6f731923910d0892240cf69be60da9d56d46a2b0a4246dc68ee859d7653dbc8d9c1664787a6d2ed354b4c697f5beb58cbe8ad6947a889e35538ddd80cefa2987c562f1175c8419acc358aeb2c7b4c97a33c1ce7a3fb9ce82511ad7f519e54903a73c6134e553e984599f294f90f58e0b72e634778141baba1d9067f58273d6eb53b3cec229ebd9632eeb9d0ec8936ed1f7ac4b97f5b60a4365d589ba80caaa97ee1bad9167bdc7b44ce6ac37559dfdac2b9bc3cab3c5b82c16eb3736ac4bd54b756291bfb9b9b9b9b9b9b9b9b9b9b9b9b9b9b93955cbfab25c67b95caeeb7a0d2dbbb9eb352391ebc6562892af68195f168bc35cd61905d43600da8375d60c54705967a99689679dc1b4cc3beb3c456be359af9b2d93f9eb3b5a66f3d77f684dc65f07ebec7fbd4eeb2b07d4641d56677d3b531957b5c9faebb74c6a8cf51089019c032a5b424dd6595497068b2c93501f96355d2e9bdbf0529b2cd76d58a719895e495807419284c17edeac294fd3559e3facdb406bafb35e43cb68ce3a9296cd9cf5d0d6e8b17103c7279a2e57835c9755ecb17103c768c47cbdbc7b6249470b7233e3399af7d75f3febf5f3c7559ee60f835516cf8ed5b1b486e30adec512bc11391d9037630bf686618b15b25c32a5d966f8b261798ae30adec13639cf08b2ab571c9253a4ded7eb7d95e579801b96639923b6beb34a25158aa62f078484b4e7b4b95d728a9c62af2b2bd11a5f144b8a85123d3ba5203a25ead35572aa660cad3116ddbb777f2dc1f2c0edce8aa2b57a517c7db1c4b5e5ad71c3166b8a70bb9faf2f6ac6501f300cd989af57b4b7e73691ebbddaca3573d7ae82ab0cf38c20cfd69252f81ec7d371484e283e672c8bd41bbedeb0c5f3154591d6726cae8cbaa474bbd77148cf0d4b5614f5e9de23517d6b89d6c2777749d19af88ef5af75f03c0ae9b962593386fa740f2d39a505c94d8d4549b4b6ac136691534ef8b34b4b6897d2b3a268ade776e7a068adc7e61656206c308bae624d3f20e428e5285712a194d292524a61d4876dac9bf3d91aea5336b703d26c93664428089136d9347fd46f479b355fcdcd4b4195127f6cd8a8367e6cfcd06e1cc7c31b96f6e3c7068ef769386e587afb8766e327eca452e77f68b5e4c82446825d7ebf06f5067f61dc6eedba9bfe099db14af0a447b8cc75d3a6dc5b36639d82ac927bcbba3ea5608b499d896547c3b25e8921c1a8451c07da5022e125ae4b46d84bcf604cea8c43e1c78f1d3b7a7a5ae7d4e1705074bb79ed7111521ba4dbb58a96799deb8e22cf0d39ca717395fb5cdceb862c72226d9979e93f1fc8b3d5a2621bdf3ed5edce7660a63aa9db5dd132aa9671ef6e8a96eaaa6ee7c58b1f973586d67ee8745d3ad63fbb0e6aa9bb2d6d3249b50c4ccba65ac6b7d67aaa9a31355761b4ac7a1fd8f2c4f01345f09e5d2775738a586795537808fcd150d82d755dee573ab1ba2d43dd529dd55cb16aaec69a9aab8b23851024c0c5165448e0ca501f51408b5b16b72c2440420586fa9d5437f57552de54d9818101798251b4f6148eae5741ce06c9eb30097cc3064ca8c66dfce407b1446ad4e8e961be10111cef9f2fd84f1a975d97792e8f97772eeb5c0e2fb72ecfb8ccba8c7399f9e6b27759c6657beeacb95cb6cce599fbd56ecee589e3e6431c9648102a45c4c461e96d212235ca973536b8c190e7cfcf0f5a8d73cf857ade27d2537264d2172ecd1a645329d9f943a465674f10233db77ff8f64b7b1a3c3b3a3933706037326c6a68b87f00c58c0c9fe4bff8ae912ff2437e8be575b6f99519aa8a0be260d60bc221f5d2cad5ca71d76666be79e993208a6057a2cefbc01f34eb074fd4873e1447d235127156ba25ea436e5982c9b4a4accc7857c8b37bba35363270041172733ea815a43ef47524fa41ff7329c9bd5e5fd7dc545345d6543de958c16aad82ac350464a5026120a744d68b55e95292e4e62ac82ac81e9617a1e98d4434142e3d49128914b6c36c89211d85b02e6d531c5fe44844392f76983b7efc983424ac46ac216135a20dbd0ec7892095c16c52aac33d4526c92e454e89eccfa06de60875152bd7549246a382e4a1f19188726dc26039456e974f94da540a694fc860cb6e84a1a59c17a408cb717b2ef501f9dd7b65d778f0b09685070ffe2d6bc5d07d3c1d63e0eeb24eaeb7acd37896aba46178d28e7f59d745f13d1e213f96536035069e7c8f475e779553c81a03ff7a8fe0bb1e90e78e0b5efc095e90f5d02ee1a6db6a5d49eb54cc05df2a41ae07ecba960003484646dc64646464c44d7093919111f70323a3264d9a9c09932e97c5e5b24e4f56e2c26226278c45e90c819fbfacd3a42b3fc11f4632c7fc0a664556513961205897459e30217eea5c9679593f41fca475f989c26535c1dac27ac837a002419284c17e932a49c9d2d0133b11b4dc0a43b662ab1c034f67a5a7331036e42cb6757eba2214a3b8a065309f124d4958257163000318c000063080010c38cf53c9d98826994c267b3823476709cb5e267006c232d32bc054267de11f9aeec4b94b9313ace54b1290c0e928248892d694e58a0c2ca7b4d933280044a98b0532441316c8968e01e9d26610558d359313a3331065900b826020c3a80cbadd0001540ed041051d54d041051d7e9257051d7e8257051024491daec2439e207e13ea889cc5f211489c9d8c68e28215a8c04346c2b61647d8c6c2c9880ac42e279d26957d02ba13810b5a27a600051eb215db3a53c0267db1a1cd7e48030233a8a8a8de25c7ca0ba6a65e30d5b3a3c354830ed40b522f807901c9052b20408000010204081000000000425f416a052892842a95d8696a6aaa91040fd70579d2152cad4057b034e51f89a8fce3102e34670f6f1cd1a5a9c682a43a1634d566772983986aac8d8ee54341543450074155a332282a284af62018d89eec40654a0f2de3414a9df10382a0e83317a696756919d814e4a5657ce5c908117ee20aca4fda152b46661455518889c22a8aab289aa250a2d168341a8d463be184136ae727ae4c5bfab811b425da16da52102113a47cf8f0194353a20d319b9a4195504209259450420925986082093f6e8292094a262801dd670c0f241e4a3c86e0b1c4630b0f261e5d7834f178e221c4edf30882da9e9435c02bb4a62667106c4283a209ede95eb14b9785b034a8a641c154fa58551f2f7ae663d532213fa060565dd9b1e356e3e653e523c6a7c947c9c7c7c7c7c7c7c787070f1e3d6e56995566d5cf6bdca8eaac468d1a356ad4a8d1532307b75fe30601a9dfa8544a562f643dec98a21100020000c315003030100c878462c1581ae592247b14000e81a464624c98c7922057414639c30c0302000000000008c20400661b3365606c379686bc31ffe8c81496af2b3e7a03e00c50e48b74e0a71700a470c1d5a6202dca7a470650a03b3c260e39c3437092cafc80c9371fc4f6685b66c3615c7f63881ec2b8fdcbe08788917f9a121c030c318ab2aafab4583f61a8b453e2a9e18672d1088bde80aa97f4e411871d0e4ab8e1775102615edcb65ac47ecb237e64db9af3fdf9c9e9102be84094be7647944322bc02f80a7110d70e8209afb4daabcf4d90854d1afd0fd164863c89262a89726b645ed9f0f220f7aa269edcfaf1239d754fcb8715a58253c8934d5a13fdb7f55ad3ee94359fc93b46e392e4f70382ee625ddd770f94b6f74054fc7091e003c5ac766a13ddf4fc74013b386e738f07bc9400ce567584ce8187a33286cb394179f15ca5c0cc08025915cc9b2b01bab3fe2e26f395fa358d216f4bd3dd96a2ee62005588b29739f91b7321f49afad619b9a0832ec673f6a6b89515b79f6f92584b1ca18857e2a93814c5f7b7b26717dcf76354f57fa393e049a73c6ba1b08bcaae9218de5047fba95b74bbe1d444746e8238bab4c170d6b578cf6c042be105787d42bc4666adfc59a78d192268b48a472b4d994a64cdcea44ae14911dde89afe757fb7e99818897dc24a6b8fbbebd22e541597411f3a8dba11682212992b30fc68fb1f34a12f0002b77ac6596637a9cfdf70460253b97e8a32738cdf64e64deae538450a05c7e54ddac56bbaba3960715bee28ebdb5056cabdfb7f81966e70b3340b0de7a38003707d22fe0d6cbd03c1133d68073badaddda3ec69275c0e5572c46462bb9ea985b305e2027721a5ae8a35a66e15af5936724baee578e429acdc7c86cbbf2f8d70dbb24420b8905847225f1d97ffaf57ea47985a1db32fd89a984216a07d05843648d48ad4230d1c4546e9289bc0a84b8c718271ff64a2fe18950bc9d751cb4055fba080b60840c8af7394821afab74dfe0b7739d44405ad06587d9b92b4148c7fbdfabdb8b5312ceed071353c82ccd0fc5c5bf2d9d6f81181a79bbf527a3d88eaab7298f24a0eb4d877eeae17156bbdee5c3910d80e559259199c2e8d3291eba4a0c81014e78fa5c771c9ba2ecbc92f48814fb8e63bd4ef94d79a2e4a326dec80036bd807a7ee260c417437bf40f215b355c9bbf582271000f71b0b44c17801f0c61527bd794a3ad82e4ee6b452e65e3891d39fd23f9ecced37a34a7ebf4c2b5a5915e007783cf758c176adc521e6f3798f803a6bbb18008e9c38b7f6e8aaa6e96e400e2cad91045242b0da63d5fbb14eaa8e3f43667d3f49cf64d82adc4b15806a485868cca22fe3603651faa6dd14b12c4fdb6c34774e99baa1df90e4fc1f43b89d92d9c4698a4758b80a5c0f518afb1ecb3508e9e566f6be652ee919151bd79191b144e4b59f87210c7c4bd87a712f3d6c1cf9a6ca565450cee8f99de743c3ae0da13f07a07f2fbb2effb00b041eb83c55d1295a8146e638a3e7a3ef5ff91c330a318d57e3a11d2a1a87a89ce2ff0b7bb872e9a45a34c631030351f0a4cb1bd020978d98311460fcd32512ba11fbac42076b73068475d2ea65e45bec7a48daa4af6274f259de12e28f354381013b842626c1079baa69719a0cd2fc94e225761e1055eaaaf05b42986fb7f3bbb940f05246a4c4b31f7668c57ebe2b6e6e05cbc589aaafabafcd34e7087a95b4862d15de0ba1706a03add6996aa079783897f5bb3bb0b22fd43680ac54f4ec06a90abd282463ce9baa76f2356fbc8b8bb26e5e473fa4769643c9a88c1be06ca3afcba8dfe5b3455de907ff728d3a39b52729ce91d70a96c89da1b77a74321a276b1b70740048c4cc9257bc1988a507e96df0e3eb65265ac4d5a5b0182ae61497ff08ab8f44e33f798d75d07005beb85a94d174ed0928481efc16169c8c5296f2f96c6c146d8781ec0883496e7e99ff084669e7503949863a0e410517c50b7706e341b3f94653d781baa7a83e153b73bdc4c2a9f30e9dda2b6cda1a865922f9c940e63be6f5fbe9708cd933758b5c66d613766bb49e4429488ca43e89223c24e690fff21aa9255df6916eedd79ab57709f549eed62df3ffdce5753bc20ac01da1f4ac5aefc85f2c607c4a0a1fb47ea18425850045f75f0d6a00566efdb37b84b09acdc41851deb6ef78e4cdf7afe08485743ce9e0bc27a3bf0b5c3c08895846aa3acae14dfa8715afc0192c0ac146c77b510f9b8db8b0b119a6b10ae02e8fadf4cf775634f724dd457e2b034453ec945879c2509f5c8cc60ccdc18f87b28fc60bfd190989d9b9358215898a7bd493f4355865bd4f9384bb9419e4be45e69c123b1cb5a0789e54e47a3a0d845293e90cf054390ada95cdd20a01b1e19f623ec83cdf4e157124a792590cdcc0982b5132396f891d883f5e866d0cb1e61f4c202a07256765da6657e73c7023b8319ebc1c6a2eb42b5dfcf7bcf7998b08c0960a84d9020bcf9d3871111c14333af2d8be5657da0dfea378001996ed6b85d3dce8e83cbb5079e993280fd29c367d9a9bbbe276d4057a74291927224302fdbc52c8f3684a92f7dc702dafaeb37b7beadd1ae4de43fbbac99de9f3a9375496656011f58699d217bd66775bd85ef15250f236423d9bff1a2c34751ad77e2046f592e0d9679c2ba9ab565297479839eb465bda4d36a9fe7af6585484c263199ebf76b6bb3eacc45208c4aafcbf1357af54f5846ca130d79c80a12476104d6c195a5a90265233b96cf77e42a6c27cf2c68bfaae5b0791368a1fc6f94e07812d250362045c82598e7766c6e7a70e3552c7869fd230152a8af9da5b5e2fdfafbcf513f51695a6293ff5526019b7c0a065944cddd5c24f15b08e66cd1413485616b15be16abeb739942b8fd8399c97735bb683db87cba58faaa92148c16929a396985859f133c37435c40d4e1555e37e6e70727d16c671f62cc8330d5bceeb3e7fb2d81664cd695cb8eff9f27412856455dae6714d367b343200706d6dc5cea6ad5f430b1027c9470e86ca9329d50cda858ba1a358e51b8fb773c09f8c5191c67e0da350540e62123a8b81738c10204c3adc3aa9fa019bff942526e91aca45a369bcdd21a944bdcd8157a06f3a122fb2559bf394a1653d09e99ba16bbd4596a966fc5d62cdf8f3a0dadb97d2a4581cf169a1f7ed5af03394ab81f4eaf56661a28237a58b38a0261086bd58cf3799167bedc6e69bb688758fe09181e639a44adc072bcf862dd1d979faad5bd0c35f2c4985d620d6f28848a0c11e5a899c16c0ec051a87a168fd313f038462c1f3cff23d52f7bf192cbc28ad70ea487f6a9bab0d08455e2085290243616bb745b812b8451603c931212352413c200f5664e3762f495881e88dd271e4ca5aa2bbfc7a236a24c3373443174943825b3878b30d21509ce55826d929d0e718b090d9f9e1def8aaab469e24d240de143717c63f493c62093f033fe83658ebb941dfb5d26de97b9c97d30608c88f5588893c37758b16dd6e2cceb86356f65e48ad5c62a0741ca547f72938659abef73cd6a2d2a5aaa555703d6e35c5882d88dc8a8cf433e864053aca3397ec5789439a394b999b4fc9be41c92d4d0d6aac1ad0303969e494b979eea3e1d2be94a56781fa6aca997bbe708e223169948096123a93cf512c064bafccc25848daa48226ae531f36f728378adb4d0e1c348d4d0c04cadaa16829dc31d5a1b96c85ecc50a1009b6b41f6da952fd39795654ae3d6028d8a45bce56c3ebc2ebe24bf32dd652fba2de1797cfa6003380b00556f0fc1363238fd27786eb0302f3c3a1dc23128a5db60ce1cf297391507dd47d6f555c9bf4b75c2b122a8d0d12c82b18fa8a805a4f3ef005013edd99f1f71b08c564680d3cf856354bb2d8348232aa5920f89ecaa19019cdd37aceff728d37237448450a6d8b18376ad719f1b69c7e7f15cb659961a977788d69ead03459774b304d8784af38844f8cf6980f33d32117bfc07bac7f4eb14d8faea77c66b754f748171ee761eef8f4af5a41c2b107077a54e31d4dbe31820c4f4ba5c34768d045673cfa8d925f8a29cdb7984b59e8be15257f13b7c40f620fbd4bff2aae125fc55ae904dd5d83a870806b3d425640adb6e1de8edae3cca55b673456aa62f602c1f6b4a75c3a18e860eae1af2cd04afff7da3b64ec522d22d033e14fb3c48012b41f23c7683ea8843c701d939351d89e59313dce7c1004425b76345c75b2e79901b3156bed276ef498177549e00211bd0fa8213580a5a6cfa4dc592e5b6801e835b9a6710c072652329cedd296048ef60226cd29406096403aa234ec2779fc4aba4da930dd4cfc1d995c18a2b7208c16fa0354edc5a7e6f236e8230833b31f5646ca78ba23c1f639e6f196151d559d353db64f6391d57f846831a0f4fb737743b16b2312bb753016c2dad4a4147a5fc4b9a1b348d47f6a46014cce7343f7e323c0d5fd43372d4a0cc5d0f61a2e6f9e4e874c6d9a602eec215e96fd97193afe0291191118cae497546af62569c9edcae65d690d02c425bd60b6ad6db09e64142ad730ce93ca7816048bba88b18a01589eede0ae73e6e49eb9ee3c58b4a1f843963e7bed377399e35c41862597c396386419ade5ab53612823c4fa74f2d5b5984d6817ae697794baffea7eadf1b2529b098a4ae8e103042f5d747f3d856fb5575683a7033e3536c2f9168fe252633c0212b95b45087a8d52a090889890e5f89889affcb44c0f5014408e98ff8b03274d2a15b9ab9ca4f40c245511e9526a481bc6aad3fb387b95cbbc307a3867f4e832ea10cbe8752eb0004efa3aeec6d1d6027e340c25b0a6a7c65cb0be99ff12f904f0548b3862733ed3990974907bad0fc3133b89e1766b5f3c7f884f8ed958056dd91be90f59afe4feaf373e07521272f3c54e447938c7c0cc3d3df48efc78942688b0dfa6ffe0ca9fc01decf79dcbbf73a2163a7dab22e92f07fccf1876e40f6b89fcb13bc4bf1fe7b7810f4d535832a27423c6b5bb5b88c039f3daa561886faa31da340339cdfa5275213b4e11c2b2dffe3f413aea290bd28f0c2748af3db3207df48805e9e9f26c53c24ccf2ff6f2ae26abf13fbc78c8d10677fe119e6f3d0c58b5c849fa2ae95b25a3e9a8859f182f54d6f1972f3f1d10fee6db7202159c8c15d7e935926824899ca6ef8db236ce45fa3434f89f8d438960d28929ad3b5b1e112d25e16737926f662741c7bd449fe17a958dee36f157a5e190a70c532ae6eb2e6109f9319f2342dd6c1e1620ed1d50df7f85e02e732c8546982230fcce979b9de2c2a91dfea2d454d9bb45852fc72fb455e9614323199c9061aef19ff4be7c1c46d423326547bf941d3c006eedafe797de6415cf86dbcbbe5120c650c19c655d1d0cb677e4abedfb32c97defa0fb5f302a2f10e8a6397292a6a49c912bec8cfbd2b541381f130efa821e92a1ecff796083ea70fd639e7ef7e26b81b7a3a891873d89db5b84445912d9ef5341c1db2b38c4e36402bb372eafe5f995a7fd34fdf8b345992ae2e34e02007f4f63d1e35c8cd6d7aa565ba80a0328d8bc16653dc43d62bc835b25d116d9db70cdfe0e1e46103d64dd2b28fc761dda303873589e5b115465c0147a0ec967a9f56f41cae61e86bfee86a84432bd383d9f72e88b0d0b31ba801a7ad7a6fea66b7049c892e79ced533fb93de1bfc1d2371077428888f7189000a46bbe8bc112d7389a20f2bf325084d7d048a7bf64e88d20462cbd9d1c3a54f6bb4070bb380bfbb5c48ca839babe526b926a7465b86b0b7b211c80f1e9372d12062935f2e392372f9a7fcb2801a71074c8b44a40b757906fc881e492fb77035b0efd90b1f0967ba372fb003ed00fb68a7d1bdf6ca9d91ce974e9605d93716a71ee712e4d2420daeee25400c687657ce89d9d631a730e2a2fbc72efd48b675ad739128a2f824b8e4d87bdc05c10bd69159010856514a634e82038605507987a1358a1d29c5914c310edaae989670791e11037761ea597807504cbfe13ec0749620a9868681b3a641d1d35c759ba659ef0d1f68fccfe6397dddfe75bce4fde797f29c480a55612ca518a099440ff25272aa2a6030dafc38a1aeeb9d254a8919260de4babef1ebf06387c180c43e3479751dfe5bf038422cc8f566129264014f774132de02640ea2852c96adfc7c02a3536479d1f4844150b5ca3d19fe21a8535f583064d6c0bd87891b289d186757eb32e2a29a987f7a141b1fdc477c5c12f2fd6bb6fdbe1d5df804d25c50a0fde7358c14a0db1a253afa9082540c54aadc82b7b3d71b6c115d5421e2bd54ebb657b74e01690210e324f8ca0d5225a1019ecfc7f2a0fb3cb6b217c67e020cf737f9de38183a0870e83f8fd83676ab8d41cba38c79300cf0224aa66f46a8dbb442754316482fc565d33a4ac82a83ad93415d0b16041c22b5cefde442960b2348ab26d7e8c4416110828cdda515e4d3a6c78b60e8dfa5bbe3b86ef3f2877793677b172f52cec875498f17a466f4ccfa05f6909867fb91a3f4f19c2c78ec6382ad85820680c0925c641fa3c27b7f452fb2ea19257508f6bce349f5a6329a85c1d3d239add6ee52e07a21831040d06167febb38c0d8981ac1951658938782d2fa1a918fd4b731918e96a2d07b69c412e19e48e56b5fff65262b49456e9dfafe451a30ac5aa3fdc1b95474fa7ca714dadad3ffc818ef85e5b523330aacabd644e9b7d5438c3ce821ecdd5e078e1f26d2905d5563ba814c14b0215a852920ab889ae46a7690aa75090dc0ba65173cb46d52414de2515898590ce618f1c179f9a5daaac571bcc5b7907d3e80049040a07d29777a4201510d9ed9a2eaae24eae7a13887e4155f4998f46935f7370b10570e854413640a816d218ce28a0bee1be3aac754ba8266c3fb5565260b01eed95b826fd81b4114325367b701c501498c0eabefb1c9ddd4df4afc61303e796eaeb131799590d319749220713fe25a1eb01d262b0f1a039dd04a0f5b325255afa8c7bf70bebf4adab26a1c7b3c41de730e42516f662e5c38ea192632f6c8510328cc23f29844a66d6aaf295dabb8eed7abbb2c3b9a89e8a11230d7316f8cbf795c9b7b13a031d0357103642002f634f770eca02b53f9acf138bbfa84529031a0aba96627b289f56d303524161f413069a119910cc888bdc0dfd9f0e0aa95cb12e1a8e46bf2160c834b46157d8398454b2d88c6029bf435c7d45515c1d90d0de81fb24b15099f5141c2908e7ea8734d9733049cea4aaf07ddf3cdceaa46f519e843a597b2a8ccb74f585315e069fb7e93d0f6f0ed285f1c2f69cfb8894f7140c54e78d7e2df8def4e0ccc47b00474c012af54d510ae13ff9e0c717b1e9e7eb725bb5f63c2696c5cfade7394b6c22554af3f771c53624390600143858e481cd7cd3cd5435d85a146949959b2ec13136009c0fa9a02318424490701369422b37ab08843c2c75388b6386fc27658e89327c9027563f3b83cac939ffdecc6e6646f7c43e05fe744d5cd864fd75614fea26a8ebd9505c983c51deacbeed964627f76da6bc877f53d39533ef494fa7101f00ac123a8f69e1578bc4e638e4f1860bd49ffe9f3c1aa9cfdd884931d88c2e6e22389b115e9a0aa02920cb7b7addd83ce54f930be4d1fb4a69a0b8c9d93f816894733858f1fd4acfa7bb0b74e02d7aef5c4db2c021baf4047e0aa5169aeb554f961d2ceb1fdf7c4b11354815501b4c3920848c33bf3347dbd4b39568da8baa4a01fa572c5fddefd701a5e4f40aef554f7686ef46bf039f4125522fad372a9aa56519923427a4d0860fe18930432b70801a4f3776b0f5bce030c0496d79767dba19c13f864c6eeb06c7225aacfd78f8bcdfee43deb5928d5ebe909e96e65bbcd8d97b55a757b07a1b684e973b7a7015d7716d9115ca1ae9e566be1c799208460d57e32a306d34e194f2a98ec9e5b07428b36b26fab50e77d88cea1db4dd3b11a0a3b067a9cb935652b365f1b656ddd8b1e2ab599c7663c53a2e2b8b4ecef620c85887ddb192febcc9268d1748b85f75968eb7f85ae417988852aa733d2b19a2d6655a5d30664ce9d5a119934fbdeeb77af1e0b8168b85a47aee3e707e122908559ba6ec0686d66869504b7868970ebad59ddc6162e4fa2529ae882fa137fdb7a26ba6b5fa45211098d769fcd1d9a9934e0322f480c412d226447168941e68d7751d9762f79908b01015d800a019f41913c4a9091b55388f6ed8696442175a3899979057985417c5513cde58065d980d0fa69fe76cfc4b9ace1e1c2e016fa93d2a53ec6c9e5b4ac3281e75af345fe007a3dbc2344a9582f0b91e88c42c3492540b1930bd9f8851c2ca6951d00afe3d2cc266e25e48cab5703107e06acc1a290862279abc7059c208cbe6f8340646a2c316b627b3806ad0f03e3da0adc13feca1126cce0ab618cbe6b57bfc5e7c126afecf4aaf3303676e4bbcb5a534314a1c0946984303909bc47ed013ffcecbf43dc1ef57fe608b3d9732cb8783c12ae2b2af1b34ce11d393f35a990a3c89afa883ed7a6bb3cdfa28104434e66b76fab9de3a71bb53c1376ab82152ecdd63036afb0407e28eb3cc87d359c41e38138361dfce88d3d1d1d6a84e7a50dcce8ed98cd3b91742a42a8637223b371e72f0cd9400ce888a1c5da9e82aa1fcdcca53a3bddb43aeabc936fcee8a509191b64e8a9adf5e16bdf057a23247b7b967110cc74a7fa0e203e860fa646c27ac6a69cbc881dfb0b5ccd4459dd74eeb7210cda657676f50bb4c09eba5df6040fd025e79396c2877dee9ada0b7332c4d2a129f12b80a7bee732db6394d74fe9623e99f7545de5b15db982a85e457180e61a2de5a0b74881c2644f63a9eb28f514d393dfc65ffaf3d7f9c9dd38481e83efc02ed2b1e0d3a48c0b1d3a8cf35df831c7d3fee6904888176a3830bc463aa30733eaabba29be4cbe15f33fc11b4cf99ee7b3420c5d60dd1fdef3341d7ecf29ce8928f0876d2cacc50be85157e495423c382a4bebdcf1e14168b9717aec1d8bc7a8a0d43761bce1d2acc466a955d516e52691e450ccc215a449ee6e410f02c4af2008730b80cf11796090185a9a32ee32749589bd8be14921f5da308f885f7a256e7675eb1268f1cfa7c2f6861b7420e67c6f39e91997a01cbcd8321e56f571a3097566079877478a4e57300f0cb9a929624ac3bf94dd1b020b8622660da99c9db42ba50ac335b68439a5b3f95c5896644e94af7ab3fe093e73e7b0dc4c43869a09bb4db107e6e4b64f2ec657105e1e748e26d685cfa2510751c99b6785040bd14984d704956936948e1e34714c4e99ad38ba5ea4ded49ae64bf38acca4668207fd7948850b93ac2e0227003ee11ef095ce4524529b60e9059057d0265cb2e253bf21704b5b83a2b93e1fb1e7d6773a32d8bf3325cddf4800a1a0552c34e3dbeb8e6d3f9aa103dce8c3747748d9bd2841bd01730bce7189604920b44986c2297da77dd873f3ec5c5fa2a91db214b81bcf4a6ee9ea26b7b7ecf7d08440b191783d21d894a7fec6fb6d8e8b0e001fd2125e097d2cfc18c0f870125ed9118c9c90c281a2085f6c6dfc5e76a5898460b486cc6d0a15370fea1b392f6f549b32ea73bf48defeb80c8a8380fafdfdb6ee14d33250284df289784666de8a3fd8dd66cf0a6740494bede834e764f52aa13f17695b90a1e359ae41c1eebab66461af2b68df7dc1b210bad147176b99c9044213e59294f3bb783b3750c4047a9a1338ca9998dd3bba62b2e434caecabab1050c72a7fb324009236c05bf55ecb2f7beb11b883faa75a48bb3bc8474377d4be68383091218761c4236ac1484dcddfe638af19deb79d8fb9c6563f08108bfc4e914e05faddb39f19bb55f34b310f9763d4308801ff29288f808db5e0aa43ad32c61e4eec92855422375f7d7fb6ad8bbe7ee162c5df4b975dfd3425b7217ccf4c278971d947f9f7e4e7f7a324795690a51e4335558b3d51ebd6901fff99bd20077fd7fc3110373b725ea5d079cd6bd122ca0894a3d7b10881163180ba5787b318432a5f92588c3bbf2416f0f0c15ef8aadb4321b4f2a694c3859224ed46292d822b6f04af930fde3a561c111827497170f017764c881ea562d0d864429becec6a47dbec3a36ca2b4fbbf5828d7c71db52d1b18c6461fa290bb33480d46f50c99d43cf58886fabb8060b12263612eacb278c1ca6f2f9472a1487c794179dc5905c3eef7df41b390e7bf38752e8c9b72061ef997e4bb67af06a9ac870c818398188e788cea629c4f41c69c340f0013ce2049c5ec2bcd8b90c878a56ddd4d451914353bb7b4aba67132cb27aa8bac32b0f9aeb7e97c1b2b45eb664aae8250ce52f6862abae4da210c31501b0a47371aa43d21befad1e39567adfd84bf54d1d3d8b59e7bbe22c3f6436077f580666fbb77217425f0cc522142b3690b1baca1af2f7106d47e11f05a09256429f073d6e2fb5cb16dd8802f7286986ad714c91ee9272afa39f6a532efb9a77a59e39ef9486ba6cc1f200f4b2ee5566a358748976ea7971ba2e767404238d586da014708f3aa8126cc58a35e760536bb56f3cc0fc23d13a0a684e6097529b7a5870880bb281bb0828445d6e6b293badb7cc691e0b58d51350fe78c1e59fe5a7369379a3bad949ba2fb658fecddbdf314520310de64e975951454c5d95767e410e637b91b95aeeb7e5c0bedd0105d4619628e9360018d9e24be387847a425816a3e37e5f34e9db7099c4b3dc8eb14dea6b66f9db8a355052222a81b757ab1bb33debe364db6bff2f151a19881b144dff92c3d346f3bdc8adc6d9b6e75da8d6ce5ed7300591ce7abb701fe3e1c3978be77ada9fa5d75a5916045945b8e6eb51ccce5c5cc7f107069b218a026195c545b1315597e5a5299bb72fe9e808e5a0ed4b600e0fb87d06d7faa6a48c40b63cd0188dea1d4f90030ae6a8117535b20dda9122e151e2f79207f770a76d7d61cd833c2c4816776dfc5f1bb6c3449c1864cc0bb0fdb229b954d2648f43953de270796dd1005bde13728d8a8182914a7003ecc886ef177e1409fae5750c8f364a9090935e777383728222242bf53a04ce13e60dc28e45ce095b224532de56f43bbdaced8b309aa468b2d33942ac9216252f5315b3ba222100af72cf21b72a2a6ea0baf617f191b1937108b2789058c2406985a32334a620f185df46e16aa0eee3dd80b7321e004dcc33d3684b55035b6b3e4c29a7b661fc53da525a5cf867e504e4e3bd19fe8c40dc433055707e26e2acd4f554d6259969990d333b93435193b811d84afe5991101bfcaf533e7b43804556cce0691700d48bafdbc0f388b65b1436eba729245d011f1ac886f87d75430f7d4ed1dbd7fe5aad226aab3b214b2f52c9d8c180372298ebcdf38cb0b8ffe95175207fade447120dba635e708b190c274394a8da95884c403d874712f0f95d2bc1de5ba41a0f6acaae2e111bd47ec018a7e37675ade92cd7d1b939045238099c474ad0c1358b442883c93baf97c1b8666e0e0c10bd8a43a95794bd4cf686d1d60f31b0241068957ad1ef72bc38a8aeb06633b47ea1618c449a382eaa4e84896662849c9d5f3b9e482411ec515039a2d8489947f65fd59042b1f8ca74a04a3e45ee8c8284ee1d01d0ea4471c9e55033266704e4a2cf2e4307ca78d0af4cf253756f2aa0cff8a723b41daeb4be702dbb491868c247bb0cb1836c14aee84b3ae45b440515f73c9bcd8505e72ba469b74a87a687baf8e7f6a17922cb6d18a5f1fcd5351935ecb269364da8c7021d3f8e5824be6850b00304dafee86a381d5f29a105a7071c223d55d336aac1eee275ead23c032a4be9f30c587cdba9358810754156fba753be18610c10845130ed45c99fb87922cab959608110a28c111dc8eac9b772ac19d293bf833e390cc3939a39f251b0c259a3522a9abfd5060bb48f4ace3f11535cb6b499dc7df4dc618c89fb0bf23172dafdf9da4aa76a63c7e234e1ee530223ef8b1c8d9434171c567a54eb22c4b4d48a8995fa9e5814519a2be2f79e713f2f018426424a674b9496a81a8e9a407bad3eb520deef42833ad27086eabd7e006e83ce9578f8fd8dfe21e7914f4dc9d4285ee41d28c4b093d3f811040110a7987255a0dafbcfc94a487eae4e40c3b1263fba60f9ac1524c243c67819acd8d5eee10e34b64d239dfb0305dcdd866d878df9805fa16509cfabd2a74930ad97caa54c159a75f6e2ec222d6472d124854bcf70531e0991beb6db79b710eef22bc31045869b36ed4e61e24d924e9aef32b100628a111ed0ba3a1d18267af8a79564cb8a5b046e10fdc755710574523075bc8d564dcd0e40c111ff0a87121a46333191e151ac9d5595e4aba9a694e501c00d687c25b4d4882eb0d95ac5c18f8fdf71ba43149a365315c3c0b956bad760bbaf3b1856d4e6f0c1085888f2d3cfc64c25bf3165659552e95658411695990f98fa5fe69afc3f1ad16ef0a12432b9a335cb7c35b7da1fed0a349bb4c539e26cc8a777823a35da97356eceb85e2023607d976c72e9251b8c99296437be1af38b13c80f1751e0713b8dce754230de470b5f0419a835a0a84bb88f3ab060fc357e1935d75c2297c9898c39123aacba44bc04f4c678a0afe8e97a02dbc415c14e3679268020034acec84ba10a2d8da5d86a44177d3acf1a408f18c7d0ee3a6e8d7c9d30424d791dbbc14153cdf4657753dc8dc5771e040f2791d8cee9e86271baabb332c801f23c2447815514b0a6f7f463d24d666c6776ca3dad39b72603fd1c2bd8dadff42adc36cd950fa48dc72e43a4139179beb79192f6dd303673c7ba53be40e3c00a9a2fe77cc7c7c9ae1826c4b161920bb0df0670560de9f59d8ffb77564b94934dcef9e350af6f469ae950540feb392f18e868099c76bb0f63414fc65363b01c469600c4559d450ff5cbb637795662b40714f3260fa99de7d6100997ebb7c473da8b6d34b393491dceac6701a095c7f38b41cc6b355a874231605b3dd155b766166c3c83649fdd031e669f11aaf75ded71b24e207ae3c8315fbc981b7ee4a8dd47a2e952695e6cbe1503c8218bd65841e1aa0ee3b10ffc8d052589782f72e5b461f0a8eb3db7c2c063e720f18924d7c042179e04f54a90ea64d2b03c259bc08dfb2e28536f4202c68b6af60177f812be885c57dcaeb74c3bbe9d8fdd60a1797046134f22c1002b0254f8a9d6459969a9080d2403e9a2bcca43bfecbd82b679b954963f7cf4b8f0fc2f6f377fa0de48268654c2d808b599cbd4339007874e467712a4d3f04dd7a5cfedefdec4861a3db729200ac8031877ad0763a76614ef9bb2ad524c5de50ebf830a1f006c216016586773cf8b831c3442bd8d7f584cf67faa7abf478531adf1fff184dc47f075c9ef3d7290966b2037f33b07e81fa84c5d3bf55f9edead8e81f350e7fdb7d29e1dbdfb4330324dbdc4e267c6f7b5df276d788de74dabba7aba3b300fe726ff2bebcf9f1dc051887656df1790a42f8ea16d993c8c0f2b152b773c74983d8cfc79a2ec59f91679a8135b601e69707ef46c144ee9dbeff729bb0045b44a1331329622458f936938bd23d9ecfe03b4b6ffa9c5f512b62b94a03c96b2d6c18c14c6b1fac5a02db6ded568210f593c268d998dd808618dd04ea340989fd3d0bdb761318c67fef79a4da150a15ec5edd22d1670058378095fc136b3ee45c0be86e11f2709994b35e231aa8476f961419b8ab6e557e74c65c6bda3be71b7b82ac3d33d8cfe82751f82981342e84e1f17b3ba1f9572324653203f190bf908b1dcaab9f4a9bc2ad601e62672af925d75ba4fedc0063fb2a446ff710f5709e37cb894bc9c707a1c8eb7a6fa1e27c4e6a2b4fe7bd63a84e34c14bb650bc60ffde13930fff892fae84d0f9defbf0eafd49f6a155f176ffc0baf7a1b04604ebc96a33be03e27ac9005f0aae91376c7a30b1540e3dfc226e6fd3c57e93b6841067bd04863d9994bb6338657f18eed98114d64d3348a88024b6bfac61d1941fc5be067549500e02737eaaf7026bee58d28934ad1eb2f6b1aeaaeab8d562f54129c58956db9d1fce005a96d4374c3f0f4399d0398b961a460c73a9d11e5c8a213ab653c6e808884f805b39025b6a6f3dad4b2d15a891c78176a93109e96481e14231b4d4b07fa24b2d11f8415207e252632454080fc3334ec51eec527b01a47fff68c82eb5da609ca670895c096fa6e64bed9da07c0a849aaebefdd0991a26003353a39b0c984c0d2988451034ff13a0865fa6562f24f95fcd9e1c53e399a9b5db33352c6079364df94ccdf870f7641aabc380917174a6961e982009c0c0513ebdf0ef88d8780c4ecd20afec8493208bdc6c0cdd9bda354e9a994f21c484cb4dd93e4c4b63e060a79c3cd5a666c4c714692e1de8f37480b8fc6a91e6b523ba62b8235a6e1fafb4da9fcb7235b51ac11d42a90aeef50a5ae24cad1dcdd43c8032877db3c333357eb8a97d736a3adafe4b44b59fdc3ab5597b763e37aaf53726cd60756afb6f47a39ac875ef8eefd6119f51ad8b6196542759d083c56018ef6d7ba32defbdc2dba1074fe4a157a3535b78482477940066b997e4f16af1ef36b35bad9cd906903ce6408a2ee0850806d0a697dd273c42192377af88166ce787feab1224293a2578343d29d7c9f4afa923a0e5681273fdb5090e48afedf58647c81f64a65ad52120458ea19ec9c853fa1127fbd910650a059b59fd14a0c22d97a5e66b5a4802f80c5283170c7bec5f301e5a8b651b4d0babd16c3505cce5dd3e8eecec413ae3eae3a1d945e47250c882b0411301e6ccab7123efce02c0900fb368bae50be2ea4396d6845f6998a598d1d6ef78400d89bcfab7df0b39adef89af690745d268d4dfc9508e653bee0c7badec19089e593d031c999c5b4d3411f5cb795cd7004cf99301f2eb552096c990b1728e7da4e5005061452052b99dd25fd96fba2214566215b05d40ee4003c7d838e9a3b270d366d5b7d7d089e38f0a58eb01a98ed72b2a5da8dc524ccddeee5e8d7c7f65342fa2eb32df0a0b2c11bb391825684c77420862c23cae9eb28de0dba9494a84a28c38ff300fb2494cb8b374166919a48af7afdf9b431d7dfc01bbb373301684af834ba81c84fdcbe70da436cac7c504b28d7292ec307bb1a9e39d7928e8d13246167ed8275727e6324f301dd375c6245ee67120310da47a0d243f8f58d666ad969a9b51fa4fcba30de5dbb35d7404eab8f2185182bca1b4bf88f7be3971b37a831bb0a9248d5558f9db37903ec422063a0b8ed3870a7a2247faa1df1053a25236b6414860162fd6002b58627c875e0f50eda9a6c84b094f8738a437cd81a52382995ecda7ac10583be26f43b16947c9d1d7abf42620a5b6c2e65370ed8ded884916bae80d07c5e1cd456f0c96490942414977c689892ee0c4968e2be708e7e960ac31e96bfb4106dfd8a7d2941cf1475d52de2040164f8bbf4655437260fdc6bea33eec4b526c366b8f1bd2afb8daed52ea63e99ee05b82d0c0ac6532a4b10916e9dcd07074a3a817df65bd4a3968c66ecebfb10981d8da46ce24da9d72f02353c17403dfbe59472db73a5776826e6cdf2f78bcc6b9bbf04078a955d16ececd241a11f6df4d63b2971ac16b5a6ef51bdb92e3a8866d907612bf47c3705a650d195c421ed911f69bcc34f97e1bb5e87163b64dcbe58fd7ed4edd626fc05eb79c0ea9d2bf095449282f79a4fbb19a6257437f192c905defabde5f94b9009d15ededf4a9f0f66e43bffe5023b69889f1c2fb95179d9e2ebd3fff454e8f77cf45249c009f43592497ce8565efc652b07c84923905e567c8594995cb153900788155632b9bd2803fc3a152431283332206b1be01e552147b7888cb24a74d11af914428aea690ab27f554595c0cb1ceadc002904a388dfe3d87815c83465a096838f9018f41c6f484848bad28e185d15baf047cdbfb5eb7958c48e63a4d7c106e9bd0b05e39000da0ce3f35f9e1f15c5998f88811ce9b6f17de37402ba48568ce2687dedb9d0eeafa285e0cbe37a3223994203a6243d16a0168714f82278e3f92b7b5d340a39008e72866f2d4af8830911eaf2077d1a347d8c9354895eb70356a105205eae0bc525ba672971446a902a202307425c6643578481bb2403bdd54e2b199b9abae7df86bdaf3b6db4f482212e821089a913873e0a6cee3761f61d6b4ea470d85c65fd9376a0d83a732d1bf349eec963af02be98d429f672011ed818051425f008982c75ca21c7f56b9b963bdc05d926d915b5037b8566cdf1f172d2aa572430cf452fb291206196db1cacf0bd6b3b8159c88d7eb5b028fe0de606903160f5e70bc551671de28ca4e8c0d6694fe81391f58d153f6476e892cc1d7c9f33108b88e52660c8823219db9a1ed03a488ddff3e1753a6a1d2c0f5acf9ec4cdbd1d510e9245cc4385312850f9bf944579ad8fc91ff4bf9001afff5853283c42e9c86daf821e3f306ce9b0999476f95da1cefbbc9f9e4dcb2f117cd7b0eb49c3d4df79fdc0d7ed522bf7fe9646975de9c769d288c4b4027de7547ce930dd4c3fe356284af18887cb4809afbc1667ab6142f42e9a19a691337f658c9bfe4c58f5b5e6cec2944c961643051a374b57cf1be656afab5f5117e745ef4802e3be8e0f0f177d4d8a8a0510dc0ab9601420e865151050ef9d3cec13f1b0cc0c7137a39d639e6bf54e49ac8e1f60f881c7cbf596032663eca3c7901c21c48193fc57382d24bd6f16f3dcf090b7e05b32dcb292924a8204efd401dd54e8c7ed078e89538562905ddb5579923e7e1b458005ec851072a02ab885923abb75dabee899f62d3ead959fdf63c540f01b55217fe9740cf158e59c01120ef87b7b91fb075e17361015e4a012919593f4f53f924a24723da4abcbdee3974c3104e142f06e6148354584d9e0b2e19690503dda690f79064bebf5c4251e137fc8f3d112f91483e210e154a974615b10c4c4eee12b79c0367a2d008261e78b8aa0027e0cd1c6b041e311e6ddfbc6bcc74a8d378d09115d850be266b6456143b01e64fd131b894ee2d2f16d1eed6a7fe5b594034f02f3722ebd6256a64e13e83fb15340cbd33485542380b8a0c62b52eb735494166caa0d7c6787f06b44a71a6a82e0dfccdb98a45d33d809e9070672ec015a49698077f4b31b72cfbe21052f4b62a75d7111ce3991c4e4a72785035b0c6e348b392a12a85eb0dc943332102a66ca5618290a443a10c54c9ea0f3f805febcc5022e8ac281cd9501bd5007ffe55cda9dea82b77b63e025e155377edd2ecb39ef95425e93381283f1f9ae053bbb966b937dcfb2442d24ca95d186603cc96d4c7f9b3e142c33a6c73a7c11d713aefb55f764f67e9b540be1682e8fd0b31bf021244a612340d154ea9b89eef5f29e070f8b92591b41cd4293a6b88b83b9aca108328c2310fc34915bb58fbbe86b8c31f17e4912213c346a869664019616069b30b85a743ac21690049884a231f4f2adcb96a0c806547c3055619a7470dc1cc0ed044f3f54344b873a552f9235c04fb29fd09ea24adecada248818ccad0805dae10bd7acb90fa89e2e76c2fd1b5cc2553446012ec1de13940b9243235273d8d9e9c9fa910796803a2d20614bfb4be0049530ceea3661e3fe3d6598b4087c1cca12e841f9bd90a43b2c55c7d93e66426190947539f2414cb1a5c01baaa87435cea65e00326581b6e44a06b1ab124020505f945a67d3a89159e0cacf678be4662764c2fe23223f61d9f8b688c8c50b8cd2bd3cd7e2ec7b312c462a3042379a50f11fd6825405d9cc1ac6110e388936f3a7bf3440716f00101019a30bb8453f680913fa7f91fc61854206a603df690e397cd3b23f2373f6e0e9781127734352ebed8c0eac9482b98991d9b4729dbf29bf25c5187c76f15731989a7eb36c3c02d56c03d75f2affca410b0df4abdd316ea3be260925de5ac33d9307af74a2a9d4731b80c14ccb4802e19d8b57b84f6838197960a5136f478c236f52f5468b635166837806ab1c98358663036a05947e621a509ad1709c0de6c1987bc4969b0c775ab67c26eb497557bbb6e90562c389c1e4a60c8868fb106bd5917c5e75be44ea0ce3564f8c3b96207b67daa96084f149b0ec809984572ca2ba6180c19a056041031d3bf4e5da00c801cb28c2eb7c03e47196d445f228487101af798f3cea52c0dddb82f9eb14bc12ad675160930bd27f32936981602cd1e1d97b193c305e36746b84595cf20e31a700f3cdaf5b5d4414ddc63aec5c24c27a672b339baa4b9996a3efa0e271d22a7e477a807f0c445aba8ea53546033df68b834bc97615ab638b8ba6236ea6f9e7d11d7487247e1e26b2af3e9d4e0884dc8e684df396b3fd64a03732bfbc12bf2f1b4884ab23b7104804b9dfdfce680e16efadefd0785b0071701c50221055b4d70ad7712d996b9eee31ca609afcdd2828507b49c5f7a69b73cce38a48065377e1009ee0173e3e8603ea040c973e69b610e4659cc06b3734f213a6020a79357a80e6191fc3e763d84e92241e9d449572e6c52cdef96c1edd52c17f63728073842d10054ff69472fe710f8dd60673d654444cffe90ab9d69139a69f2292f851f16648343674ae1f1be6cd89c2265ecf59b8b39a96afd1d7a9e5e6a0650d104d885dad8837adb31fe5250c15407f871dd46615e2d4a9fcaed60acc98f524f92e454c300f6724b7ebfa65197f794581017340d0ddf438efbc6d47227d46b04bdbcda7b8889ff3fd5bf4f4748fe6fd367ac4c489ab1ff70cfa71484be749fa817141ac7f6ff6f26c99b1916f1f96efc127111b01bc6130f20953425297af1014fb1e138b229011b32d0043079bfa1c3790ab4a538f61ae282f442df3e25cf03eb1edfab4eaf60ef1a7481b89003a5d5ca7aa86b7cd7fc6277dc43079f7a081334ff77d08c5f48d9e94b1b57187d7395dec2d886e1db820fb97021a234f48c060fef27add4331c9016c9829298699564c3e689b89776f1d5e6909a9b6b959b8c3349d3959e4ba5d4976b330381d2e8e3e63a368fa1e0b4acb66c7a19d46b4528c4a98bda482c3ba8da623c311fcfcfb2467aebbf9d8b2a39abfbfa7b5325f981266c26149ce17b38e37f27487d0624ba64aa8155dd9b625c87360311b143c0a66dfe29ed6def55ab209f85bae94eff404fdbc371fd8491d6f22cbf4e59e429bc1ca64f4e2aca6d1a00c22c42c31c9c7df2999f440e0e766cfda4184fb43dabca25f23b1d21e28abcd50ba918f9156da0c50b14c90bc8bd0482316d0393316888bd76db511b0618229d5e874cdcb652342418151dfc822b2b5adea072bb083c0766f77a630d41d5461905498aac9c53d62f8f4df66b19f13834aefd137af0dc8b977948f78e284a0304779f01f85d1d01f8a302675848b076a18d97b28e43901ee98cd138986cb3ca2c540355c4876df8c5803933110abc39e62e3d1993994ab44ad291408c5b7b9124c6dff42aacb3d64194c0099f738b12b02cd188618f1aa4bafe5c664d1928b8943e46ab1275471641e395c147aa783a54c9a348e5d227e588a1c60e0a837ac936216affa49971303957733b6772163e696411954619823fcab857c367580850a045198591c205c2536efaf186b65c913a2128cd4258d0cc9de47c22873f84694f36c7df882dfda7d84a7d68859eff7ec881cb99ba670a0aa58ed5fc41a706b6e6f9b6435c190caa2ad911e9ea3086c3a86e5aac0b09d020c9b34e93cd556783412eb1e5b9fb24b8f9cc34667674a67625aad7a25395a37ac08aba7344aeab35b013fed1997e06ffb2cf930f06cd5a026438c5b08f82bae7232bfa3c247d7d3212e11e5f6a8f0fb484aaaea7ad9319e5d9cd7b1992dd7c75628ba291e4fc08afac03c1636b1470237f0c9c0f2b68f4720d8a8ae98975a913f6a4dbcd1e18e7706a473162a4e9d36f85cb0a833f753fa4b88ba8a35585e99d94e6571cadae5cb466f23c620d9ab4998bd29c0032ea5813cb55e9d9bae391a769c9d22c5627628f28d1d03e17c7d63e6c0f103c948fcc1da2d8b05fafe30cd26aa7efa9f23ded9ca0350270ec8705ca6dbf9525fd1247c98fc556624b2d1b126387fd7c1966f2561b3daf9aef71e60012871822d0e80f8794cbfe954afe2a4e091fc556e2963a3724c48ef97d294e09dfc52ffdb3295d80875cd8a0dcd85f79c9afc494fc69624533d8b90e7709a9b4c7c6bd79915436e2072126245472f8a8d64918ffd26428e225a70b9755266f727fa4720ad1435b05f33b29dd388af706e93cbceaef9ffc4f8ee37a78176c308bfab8db5cbf0f1251fcdf4ec5eee7d32da049acf555b53159133a4d0635f9d0f01fd724323388ff3a12f7ae1fcb990142dfc3a0962386415a064cf88c4f0a4f113981cdc4bad08876687025947d6c18eb419d9d1cf7660557631ef8a474b0ddaf90b1f7e7de57287ad8efe517254b08ab3d8dd15aa2e95ff1af0127ccb378f796df1e2c905274adc2034605827e140c7fdb4dab4e7404eb065d464d2d563724bcb8276f7f7a57e883f9ce4be2ad19c84a47a50b80a5b33f2030b8451ce82beb85b325c50f3b9e21d93fa0cb1fde12afe467e24bf9316229a003e7940893cd4577064a1a0572f7961aff47be3980bbf1f2d40c389556b8538a58f1c9e4559e7c4308240f9e4702e9607604db6ceb1f38a4efa80125c454ca9075044bc435b08540f1046f60b7e045613b9403ca99c21a1b0e2b2ea72e25d14148ca19930ff2e678c77ab3a43b9912781d971e16ff627e4014b9ed92eabfe90145b83d2143ee6f8aa03bb8ea68dcf67f86e08b02f2ebab699d754c931b70637eb6340a318f15d0551b470b9dec244f9996991e0a39cb2581230d14316b827ae0321b38fbda6dfa80f46feacea052397e13757255b72025a69668a24968f33361551cd3d1d13ace7a0674d7e02256def764032006282899da1fa504c15f3eb296b32530dd22ccd7593c923aa26c692e69bcb065a1286ea54582f0b7bef8d0fc32b6a3eb8ad7aa57612f87b356cf8f6d451811a69bf20d375cdee93c34eff5533b1fdac491ffdf117e4f2fe18b58253f116fa50a2c25cdc5cc9f71f3d087c9424f44c5a88e5ec568a89ae58705605e69f3413fa57d946194448358144a4da86960e1231820b56a4a84d8731fd02b3965ca19882d8888d2eb5331257f13b7447ee0c0de4eecb5b92a5d3a86da7265c464dc79e614633ef493f602ea67b153897817e88c98801ea7bf4f93601a426e9463c43dffe37ae1337b90813dbc957630588f454be387c5a5606e0c144d2d356ebc4ea5c194a3181f855ba91a8aa5a07aa26c830b8b702d1fad3808d810c71b87ba7c2c053da1c36ba565c15e98fdb25b20f44c6ee3b2ae81ceca5548900f0ec835eb6c256f9fad9aa70295226a9eaafb99dcafd0f7b71cc5b877d922360d6cfa1dbab06ebd738d9229b106b23014db099c673ce2d98ed30d6277cec97ec582078673f6987f82c79c6ca071e56eecd682ae93351a9b593e051c6252d2b874ae896ede97caf7944ac140d11f32b4c4039a134954b7a5d1ed2242f21c2a24f7ab8a57c79cd19cf0bb5cda85d3394e66112afe3efe3f2862375ae6818f2723b7484616090de8d8207664f9d44a2148474558c24d188441cde73dac145f1670fb59144ae28462cd07df39c34b0d071d7dc0ea38791f11d505f439ae6ba9f47d28eb9ff486b10ca8d54c309b632cc6e52c4b6100a363f7a920ed51b87d5444e83bb720ae5530e0004d7ffae79d72cc26b48a5038a041c1d9d00334f00509336ea8fc883cfa89f41b2782206e97a69cebd29c97b0fd6d15fee7b938be96d265be5c8aa4a3424a112ff4724bc2a3761c458c0482d69190fd95752035053885ee42d8449cf1e0d399e658270410a338049807dcecdd1850ff26b6495985e45d4c8fabd77cc45c9cff8dcec49c7234eb376204ec5c98f074cfc5e6a670800387cdc93d1323f3be0ec18cee472f144b5023c6557e1a25182bcc2b0fe5d1be9f79037b780277d6583a82c284cc689939c50f993a5c3c394cc311f75ec82d5ec6714490e2b93abdaa5247c9ae1cd13f5ca132900a4704b212797177860370c112d61cdd8dceb2ba18a00efe7e1a7d2deebe2f91269b4386927ecaf5ba5cff378a30860d3dc2bdf87e980df9323daafa86befbc7e63e026758260b9b9ba693d5b3940d4bf706f9893fba456b5a2eefa4ee961664587b972b7824148d6993d567d571c1807645494274cffe77ec2e9b9e3b50246960285a0b08cf0aca0a66fcf93a6c9f9db59670798874b3a3009d85d9f77f53106624a98ebe956cadb5c0584fdbf47de69fcb8c19ad25900629504db0fdd27dbde37a98e4dbf947c7e2e83c743a040297428364858d6f30df75733745d1e18dc23680620dd3656b5af98dcb7a802e61a268020e21fed93245ad910886de0fcadda4422e9d2cb50af97fe4ad8808b4f0456d42d3a7fac8b58ded636db2933807d1f421e7c9dba7fcd1fe9ea00925bf437c66e764df825d5d7efbc32f328c42240f8d844d0ee9e5497089756b985c420cd1ea0223ca67f5de100685222ade4ce78bdf252284cef4e92db4595c6433316ee62324727558cc266731a3d5862588b6e78d93b3befdfa6bd1cc9083d135716ed6bd50d3c583620274d91dba5c7c786ebb1aa8d324e399bc8e6fc8314fcfa086e2a137f49d756b52b940d3d2aa4fbf1d5a74e409431d82f6beeea11b006facb78717ecce19618816abfca172dc6c7e1e2ff5f20ef24e8d68138715a237c1f87dcdf746e9dbfbc7d41363b974f569765efbae622168a243af513ff310fe771b80dbc7cd18ab89f3ff93d72c25193c158855be97868d94157dd66c380c9e0785a1e7735ae6f4888a92f1cc48c06b9ee554923c88f634a17fc1ba70a0863b2177e9be381a66a43abc33a0d686a30c837d07f2394a4d05cfc22ecdcf626599d25e9fc9e1c68b8e5d67667a3265348ad525835e682477b5ee7038a11e3941f5e9f5ba21cd3152447e727d089c553448d0798414c1f27222121d3528f095992613f50af4928f45cc73d198300f39a20e5eb8ea2dbb8d1e8d68ffd7873cfdaa4f0d96451406fa104de9144c32ac3c4b963373f08fe77e3cacd5e8d2e153a3a26c68681ee18a4ff5dfc0d4dca6df77e6413dbf079d803cfd9396dfe4b6a44ea5a79724039775093f4aab91e7499ef47f126549ae61d0705a03b7c9d16a5a7a4bfa156de1e81bb4c5e67277be943530b9d665bd5b70c3c81e72f45343c77eb9023107e6807f0d3a34bc44274ee4fc4c9643b1255d9923a4a5574fbb41149407e2e29e0db12dcb5a3e11ce4408fee5ca271383f1c851d36a847ebaa2c944324cfc25390be42715840a18f97696732d5f4fc61d44f58864aa2106f856ab850bdaf9348ad22209d74ed00e8c94329ba826c952de57361183a1d0ecce09c36533b2d914893267f96532c38db7198404cd4093ac204eade12c5803aabd49bc2361301ae13c9be29cee1eae6cb108b0efd90e0b4f02a0302b95c01c1e4aea972cd7b76558b409abac5c82df7bec47c4c7289f066679f35ab6cc08eb277e0fbfdc6b3b9474a871121cff6c0e0d3090deab0b01233e51052e4a780f5819b69ab859f024b080c397440e0aae9278410d0bb6761c9f8816d9f02968effc164ff617684a5483bdf49a9e411133ebd041ae1515b39bbe81bdaf4d137038025db234c0c01babce4a36493448ecdfe73fc475a858450d4a1d6bb8eb262b733239bee7d16fcb0a237106da89f1c9d536be88b1e6a9b876b68525c4be45122c70dfb3a70618153e8b96f423d24a353cea753bba5fd232fa490f980523ff298d0f12321e58ff6e3e8cecbcdd4d513e06aa9001102c03bda52caf5f0e9c678c86fe65e3516f470cf1b02eb6508123483cdba10bcd38af4d31e200631a3d0e61a681191736dbf805067ba4a9b624af47a0dbf517af1593338c7ebf9071d9aa322bb509e53d1dcd4e35d49b899441a397ec750ccba65aa0465d8a5f86ab968f0fdd3a2945977dd9056974c2542ec1cf666731d23edf290fdfec1a9608f9a464bea814c78f3a59251c2605078b53ba9ca633416c4df27d5ecf8c3458b6a9c3365e1f55b526d0017d8426b390c3921cfe7743416136bc83f9bcc2ceab55a40ca4866f315df931773ff6cfeb1f839d739f6129069f44f3de0a9a508a3701e8e98a47d57d2ef27a8b600a85eee5cf64ec48ad3921a58ed8e0c76553ebfbba72bdd2fc50ee1899143466c8a1655812dff3e5113e00381d7ae934df815fe00e40e3161bc34ee862603b2fcef673f1b3feb372255e726a5ba8c8b8d28e199058b4ab8129b358474b57d46db2bae7a8eb408c08871ef7f1a750e7e6e9dc2c6547f6f3e094a4c55b0579feaa7d14775a250a9ba239902507f86709b900951ae7835e199defe007bc3d58644028e71577f3b1ffdfbf58d8243fb92a0214beff36ac4459d4cf0660548df71feadcd988f050e95efe70726e3fcdcd52eec8761efc925afc5591c94b8d8ff2ceab446153928359e602f1242307a8a2e278d05ba3fb8efca007468bcc28f53c31ee3ed6dfdb171b9fe695af568c82361e036b69abcac11c0c54c8cee557d1279132d88f18f961a61d4eca9edfc472262ade6799572f42339557a96ab959c23bbbba1ae5cad8b5f23f34f8b27a41a66134997f2eb07eecedf89ab9ba29cec4be85df3028b298f9aba64fd7d78c6890a2d39d5b21489b8dd8970b81a82c8c00f1f7f7fca78fc1edfddf8153ee36bf00477b59b9bc8d0d3f5b61ae987e10091a4b0b2e8e99b3e4d14603b2a0c50e8f1b16e649d968ffccc243f24656ca0980f23e44abf999b3038b98955cc72023fd2e3854eefd8715da0570f8bc8160045f4bb77b0563a081dc3a432d323e6c121e9840ca7483925a8b40a0706b20f2572feab1a8095be04b2e1a8ba1fb083160e6b8fad978d9f3695ef8b52d966c577b5c07c49de83fed0f691bb89186632474f8f62073e60a8d27c952372d9034db2a8e206d8a9827fcea0197952326fbd6fb9782259db546339050f9f0f90a0ea9d32e3d60f50f7d37d6e7d427b8f68d31e5c1c38c47b72feb33332b436e5538a8b8bcc4b90e6ef8d5080a0a4601ef8df86a23c537af51cd835f5e4939369dd54a49c152ceb15c0e5a7a9c1cf7be9b48009ec9bd24246bbdc64c5a66f6cc6c91bc267d5d2b466e6921b0bcec2bb91db93a7ed671c230b98c4c3649a6ccbcf655c58ba578b43637af9a58248c45e734cc93eb12ef77b70bb6d33e57cd255788d4a4540c7f6f57a9ba6dd2123d3f4dd94d0f68e25c562932859d2bdd17b64b030a8ba2fcad66bbaffdb458a488d7be332b1a6294df3bc69b58a72a5fc7cf90ccda823f453298d82d7e3f03361a4ff752737f31a4a00b853f4b95b413031bb711445316fc8ed35ae649d903af96113791c32c4b03fe49fe3f41a9646b247cf9601cfaed8973e76e56626227fca9d99bee2aea0303104eb8e6f4dda380ecde236042f090e2d0612570ec54e8f8dbac28f547805d469496f53bdf02f4d30a9b78592532f6431a9af90be5a4ab58dbef5904c21a869af4413ad46e854d52092ea9b5ab119bb93dd3a2c99f4fb5d83c96eb8464f34e69a9b0a03fc287e6a58de4491e38e91fe01ce0784c4c40941703a7ef65b912725f825a61c187c958018fdc6993bd46ae8f48eba993d587206ce26ba178cb01baa07bd1028c90241a0c9d4ce9c3ff7ca0ade354ea1f3be8650e3e8724e802cccf0cd782cf5cfe5404d74347fed04b0e86c9a9a9e3003a548f29f4ab7130832cc2005cedc8251e43e8f5d68914e89f868b5f590eedcc1084c98361df5923deb80796530c9734c3a575e383e03e076eec1e591c0a203fc45f2a0257074563db7a292ec3ad095b77170360982670198e32b285574744e63cd167ccd02956793aeb4d0326144e9261a764c66c199ca7a9b9f0cce30f8f2db44d5cb1e1a0794d8436c2d90024d2f73878a6914a43f6bb097612eafbe29e5268e77c65baf58b2f2ef84997877e8506aba89e8468a067783a248327c1258e9c99a77072cc571d9f87c937f2082ebb42f4e2e6c88c76ab8273c0c8ccb7af6f99ba872153bfd0a626540d96ea986e6c47cfc14b2db48d7af843177261b5c92d45d27d79587d4776b7ec9898b9159b7e77cf5b16dac36ed7a449968df30bedda41caaf42654c385dee087af73c1e41f769400d14c903469b6e27b934e323fdd37b49e954a226ab29d9154b5ffcf0b4f6153bd4d2a76ec3d70633dc9b43c8b49203ed9621a40d7f2a1645be1254206a2219ed0db2d440137e28b2235cde0cbd1a5291ff6cb2fc9f299d7270047120d1e1b7a4a78a71a6e11cbaf7cf723a67009d56d01a316bd62d3b08029f3169b9dfd86dc2989d23ed022e6142b44a691005ad58700e9b48a0d610b9d9963819b4c6e3741ccd011d4e38ebffc64acd8c371845a5ed02d1a6665db34943dfe9202701e906366827723c36a68c803597f021f6801a0275374ecfe611137ddad039c5f3705b7710a21e78896dc8a93f20f24f39cae9ba05c2154801872444e0dc52c29f3607891078cffef0e50aa08a0f71f167a8db3cf78aea49f8809750936fc5a0230f693a5ee689a186ffe953ee2abb1029046a7066afa05f367961751ee8aef11c29870225977806af14a1eefe851231ee2be381c1d69d112645f8be5ce1749a0a736fd0070a44bfd07402a1b302556eb31f161d2e12430906e4431529e0ab07a7c48344a7154811920901b57bdd239bc2192ef1cd974dfccf221def7897208df0a334c9e1b2223bfdc09530979a357fda328b598631252ebbc8864ae6bceb3505d7207fb6dd7c3fca44f45e4bc96b8662d38285fadd4399d7da6c75104229d9eb9c0e1b83025b911dfdff4a8c18a11a826707473bf11c97b87ec8da7c2908dbff6326cad6e7bd8aa49dfdd0a8b81a1b275480cc6380501db1915a4a336f80ba4c804a2972c50961e3c1e4901392eb5431335a67601483d762dd34874f125c5dd79075416d3131794fe96ce0cc2183bc3867de88eb1f196f257ae95a8712978657d0cf5b59db8e969340877a12a85e27c06902a70323ccfe94809e4acbee1b4a95f6530e06b60a20f610d8dc703d68a04803d46820e09aaf4e3e54f4c4dc3eaab9f2d34b84ba9f9e2fd54f0730ce440d70b0953acf45ac87f4a1a5cd90dca5c9bb34f0bacc8b44000aa63696c53dda6a337b1f04716cab5b9fdc4f869de648607cf727b0ce192b043ec87a85097702ad99b5f11d463b7f1fd5834db64ceed80f8164b4b98b8bba725945731388d78c43e704498221b71eb74bae23da2196ce727759f3f4971bd0cf3aaf5715221cc0d75a10c6511c8478ca6acdb1f7f4aa24cb406c7b1c35c9f603cd89c99a72b84910cdd7701f667df8fc6447f794b7bb18105f265fdc8b288e75fa2811ef9ede0436abb56963836d8d09fcf3cac0a07a1fe6d0f2810355c7ef451c022da69b36aff0bbd3ccf833c56ae8e2f3fe06aad1eaca2b3468e25787fbf68f7963a593919e791e0d50c4eac957a5e329bb34e7e387ea71fbd268c988c2778151c7032a34793b71910d1469d81e0e0740ce0cf8d6018611d80c5ac8a18b4965cfbcf57a0e34f7aef905e5742074df2d778056e8a3a303653a744cc00f091002c43034f4d69cea3a7a9a384fec3d5fafc1a7a371997d876bf9a225271b6b933daa99b9f20fa8e62c2a1e82f29e49bf3f3134b3615de2a9de7586b70471df60bc25508795197cdac516d862fba8cc7521ddac42d63164afe41472ca5d93c7ade65a2734c96267de9832503ddb260119c6bb5243915a93ec69c8af1ce280442bb76fb36eba45bdc471aaedd4bd0185f23e31689487011cd8d6e5d18e20551f03b799de5edeb5e765e0d971b7658991f19de1c1e42074a1747c070b68cbb4a380a42e47656e359bba0ecd53808d5fc9cc268b099f6827c9a801ce3f600a682ffbde5f3a1b03711340c7ce7b9aee3f913798b28bdead2a8591cf4cc84204da3283769a414da31234c32beec98aea967590e8a410b08421a3504ffca29f24324d235452a20cb74657960824a7d96bc8d2971ed03a293a78529a30af9648481325d5049ebdeac26b4f44c6303a959a58c76671bc4db57f2ef9cb4623e14b647d03101bb65435ff1c47a4813a79822b8862793d5812df98c06464afdb38b18cc060636219ee8c9d164a6a0c77aa31e14ac8daac9e0c8f6963a2720b2b712ec2e8a64ed6071a4511c32ac6c15994a86e8a0d898755c92f9bbbf45a5a4157319e05d86c9835118cfb9a94b2ff92093fd242914a91cd5dc899a1ea9a085d7eb99cb67d41c1525b29953c03c4f9bf5dcc449630c2a50475d65d94558fa9da17e35a4c005961037da30c5b5d9e7aedf90ae5a29251a3a07c4cb84f0f29727bf1fe65dd34bbe263425e5395f1ef2e53074e28b7146f6336b3158d8dc8ac4565270aabe85845c922027691b38b889d48d644b229821dd1ac8b664d045bd1588aca4a1496a26715198ba8acb62e19e1a018979d6b7b635a98ad0a54296521fcab4b63076371748851331c8a3d6fa938c11b43e2d14998a44d08fb588350e9ec79aa1f52114c3e938370556e73fcf0e43bc579f958c3d8fc2e39e1690e739db1862753c0a3bd1a1ecc4376c3c62ea43b7d27180520d7f73b972beef95c91f7064ca115f3973439f500b33376f13da1aef668802ded2e5c14ad3b4f3530081552b41dcd1f82ff05ca7380f50857a663394d85b0d6daf6dd25b4813ca35b3f2549134471d31f7ebbb2b1609aabcf0566509b719ba41d5ab247dd4b40ec96bab0f98038282df7e377b24194c0a642152087a9e1843cd93952f0b138ddcac8e73434bd16921e8c81f8bd5199dfac791639eaacf15e3928f33f2b570c0fcbd5fd0e94451daf96252da859528a3e358442abf6fbc6e499559297e1e49a3c1337319b0dac3ccb9b1efe2b63a72368ba2bcaa3000e2ad05d9087a2f215de1274011bbcb00f464425382c778a9c050edae4c4e3b304888e73c7dde1e6542adc5b8900d30e0e10753533957dd14cadabd382ecc7c11cfeeef693400e10e5aa41875410adb2ae6668d4febbdf07e72ed9d6740aedb10403f0de6a33763099194d00d1a45b8c706863cbd2ab65836d86342bb1f972d92675fcc60e35c6f2750bb8de4b1d9a447104a5008174fefa4c3fbc1ce71c1ad0af1896f0c7565697d38435becd43f730da02686387ca7565595259836550338ffdf8349736da6872ef485ed0b4781ec84dbebfe2f8a62d0022290b1ef4f80b9b529af25104e429110c2d5e7e08e6a6e3ba6d840aed7dd822acea4d6d1548dc151bedbe003e17f86aa88fbf57cfb3a134a53207724337fa045d9e34c73f7bb1db5b8338a92072ed6d0861d88d13f7df22529185069fc4d69bdc3c869a13062bbf28562eb300511fbf9655c42c0e409ca41b03b596956b712a79938e6550898103ea306c6d6e919ef3d1fdd2fb5380e3a3202688242b43c7da93c5355b2f61694e20d5fc67b55f4e90d432198f0c4b9aaf6f6eef6a8fc040a8482a9ca806a78e4cf0cd49d5f2d25653566996d64745e7dcc2d29a6ffd470554b5d004583d0267cd52e1a442f5a8ed617f000a9d1234d3059cacc1b6a984047ba721ae110c24182bc77a056103e2589c1399df8ae2668f6296e89d24ed53e8f0401544ceabde0d1da191d1be06db5bc9b2c4f4ebdb62dd89ce915502d17af5bcd67f3012da5bcb6ea64f82290e7739ea3c252348225f9a709ef2a182575a39e2a53ebfb4261883176fbeb0ef290314aed9178d2f513a4229a9ce9e32515925de8eda0dbdcab242ea2db140b9127b3050376fa2a98358f935e1bb72b086a98b97b7c2fa76a6844965ee6c656f00a7345785dbfa25f729fc8145ce189872d761ce1d443091864c7c78a565c55fc08244242c6d337b04692232842078e0a1dd2ab705a93fa231eb514e27b7e0ff0aeaaeb634442ca70fcdab1d604182db3e04084125bfa77a769f7ba55352caffaed08013c5c3a242ab09e36c6cd89a1e8c7705a3203e3105d3cc1d306b7b04c8b6ae3d4ab4af08841db5dac0ff2ce0e3b3afcb74fe9377403fdb45207b7d7f2a407e244c565d11f35d22aa0b0b78c9dffee60abfa672adb3194c16f7f76a9bffc59da85ddb0905a7f39356a19a2acb19f837774f40810b402b74af188214307c25c5f968902ba66b4e810b465e3f2939a255a397720bab049688faae5cb3aff095d0f78e7c9f67559a0a9ff75056fa4976a072b4c38b181634bd39f0ac3b6d4fdbfa49f978fdb19e11499af4e99aaf707e129b15693635160e337098897b5a63483c97b130d6a0f76d23af8cd682ecca6a32ee25b1765cbe816d42f501db64198786d3b92d48fc772f310b27fc42ee4cc959fa7dfa4433957fdd9fb02b91cf18d6110193f729d640d07c4cf8baaff1cb40613cb02928c0fa931700423bf95b26d6da198a6c0d50b9a64d91ce835404f88a6791c4e7d6a7f88ca425d8dab70f46979ce360e8e269641c845567d5c8ddf77058674c58dcb2d7b95d5511c207a8898c89a9837d898cbdc470e00c2cae0a831472440b7bf521617053cf0b44d5908e1c8b5a047b10a74408320c8eebbe79a421190a00f06b39cea4d6f44ff16a17cfd91f22b80d4e5c552e6a57a8e91ef5b94adb9e0922c67beb833db985e88ce7a38b1754daa3615bbcea9dd95c14f6684dd8d233af50c46a5fe45a60bc39183f091f48e94242144561a20b335324a6e8ab5a4bb57b0b65b7da16bfe0c01f0809dc2c9c051565285205b889611229e025ccc6fae02602c9ef361aaed90b247f92d60a39494166b7f86005ba5228138fe6cabe1e1bd3b369d3243ca29c3c17331a243f67bf7606895be07ea75202f8d1f176bd784fc8387372f4da482f2ddd8982e4e4b9a9af12b008066681ac20a162718da989d8cdd9d54fdee2e91ac46116264777c94399aecd2606563a8a2d03a3bf65bb6a0dd30615c322c475464c612880f40defb013bb5f2a2ae941fc0ebdab5f108ea20797b2eb589bd0a1bd670f424602cba45d7e165ca5b3952992c55529acc53fd811089828f1a452e2c15ebf4518689383c7902fd798259e2748120f362689bb9c2399859878eb4ced5092a0279b15a94f380878c3aaeec4a518f4c69435e890ba88de14f92643de58e9d994566af99a05d63769c54de3f7bfc4c66e5f91023fd227d7a102b2157725ba0a8f8687254348d84606105df3e017052053343cd1330044f7c3d5e54f56401e5171baa33f38296937a8723029fd6e125bd2239e4d7de1fece531bca8a0366c8c1453310515c3d34c1ab7c8a25b824748bbdd8121b41a6fb49b751bc4e2cd26a597553a8dd0c280001c34d8499997b137437f03205aec3957bc4ea2c2e447f73048d7971c5225ffe6e287eecc55e87b2f493e08e68e597861641a01598d4d7be4c38bfd70f436875ee344d2bc707b05a0ff4fa8f2825ce0d171650814c8a733e7fd4ac7874b68c7b978cd3371f7ed1b98c53024e9a2995d2c27a8bd7634ec7f728952ff6d0c8cb56593268c0d648b4f0abc9457374cf95975be7f35b3e2080cbb57c15006bf91e3970291ad6d5c5ee6cc9b04f46aa14f2411eec749930ae9dd9b6be0541b7260a9b78a37af0a4e9c6d567026da8b6f2d07f7492e4f1ef86476417f6d007b2bd453e4fb3274bf7c5ca898692eba562de73b345c8fc223fec48a461bed38eef92d86fa35c9a8f39d3ccdb0c54262b28b0ba40316b01e788c3b82de39d090a45188b34b9480c8163e7cabe45070ec42bb46c0128281e30c53961f66d84098a5fa0b5de90af261c674d5bdf6ef8f76fb040c70a2970bb56aa2e8052c02b74f04a9e9001c5065c8aa0f5775313bdb0fe74fc2ea7d913d64853c67a265c174dcdc5640aad4092292cd83dd3e1e27fd4d1fdd96a658eb11f546f80c393df13239f6b818f73239f0c7102d03e56a2aad9bf22f8883e399262b07c74bd1e3bdd59a468070f557de806b74d31ac95df8c91cf1932603d78dbc56558549be2118070e54fb58564f2fdd6cb9c81e79a2addacf3ceed1c13a07e8e88079060a434aec30c47f83b3b26d4f5c1cb198a722a13a10513cbd9b1a2adcae1481da7187ba4217034515b3ad006c364089fa989161eeca1262f6cfd2de1402409d1a1c720c03f7e949586a0b46a3a4bfe83a64b51f7afdec04fea5acfeb48f28a9e6f432e945633f240d6c8e96ad2d82185634ea15d4b9e8b055cf4a74331cc5d652be8986ae89acd266b8ab273f272c60554815cfdc5819729407b9a13327eca4842553af9125b6b2fd5a04dd6295b9a254530e07d05c0c789890dcd2ba0923cc8dca3ece6fa241747dd4fe09e712b57133db7ca9765a8a87980396b0f0c1b42ea36ef98b3f9a2bf09d880e16ba7fae3e8055f59b5eced301980a8f2af0fc9a83f1441b8e90f5024883818571ca5aa91f525b12cb858ddbfa853fb59e91885563296874577f776c1886a608c65a81da3231ef72329f7031dccd00cba0e32c8a019b2e056e6540a3f32381fbd819390be610accba9467b1a3971fe0e804b3d539118d4cd836898c3b70e494136898bf11f3c94365175be9589023c7d8d858ef9b55dd66ada462ae1b5a4b6e779d84105fd8abc287fcfb752ca25ddb3a8720ff58dc71a185a8f074f7d7932c465946c3d68648b06a2f8d44690eaa6f49e26b91f4217265c4c1cb4e925ab358262e5698197ae6fafb9b91e6fca1217dbdf41a8b3ad8a3c418caa0580a199a32b3e4da31b53ae5b1c5da6c3ad3fad374d2b22aab678a7c01f67dbb242f69253eccf46b58d5be72b949ea754903a57ee582fdcb034d943ecbcc976b8414ba5924ac3c58345325661693ebe4e390f31e6196e4a36fc7967859db1a5275d9b216291c8850978491373f9c885a0974599f17d1e22e97f6cf532ba74266a73f4c71914839dbd72cb118011df5f21ea8ec2ca31cf1e5de23c8afa316c9b45565a72d0e8b494671e78fa0faf9b71e6821f42c8931d0dba62c38bd0fead6ee568c78f04e4b467bc15dee00bf38d72cb3871d8c0a83fcb9befaa1780c05534be4e872599fb9c3cb66148689f65a0df06d527b4cf490aa67a41959e9ecd7fcbdd15e46c8d38a563a2493a7af282a0da566872a317feccf9f9ed350369e798fd5e488ebf61cd47b4610150298005e5cbeec5791ff4f466498a1c62c5861baae0ee339da259f83a5240c2fedbaad2491317c7f938135bda2618aadfaf67c44216b082e52bc7d24ea9733f795189ff43f193951e84d9d036a68ff044902736b51d18e7e1ff0e4c5a68364ce89cfd1fb32a5b08673fb1cdeba99f18425fa521e667074a241d893414dc2a161b78d4eb00b7787c618c5591efbbcffd36e2643a1efe2836a68c4d2b0dd04370dca771c78e542a91c05bff0ea526ed712ed6f0849a1c7cb3e7701e7b8094403cf5a7a18c49d3994fb782b7d0cfc11935fe3867db918b7fd8112f0ffba53f3cb1ea124fc33d01a6e20e6e267c4ddf839e78804baf2820cced5786ca05116924964c88d18de93901aa384cf9f04d37a4b0003b21f86a43ad32d9e09a289e44ed7cb7d286aae9e24ca3e59594a92b2e062891a01f9c43253b1b69a5d62412845890549697b20b7e1953a2b2de9f3baf8246ee029323fca6b11916ab6e38db3b4a5a032c981023d4af516df88a7d7cff9b9d0bf5892a21e0841f5cc9342e7df890eec52541481a489f67c5b903d54ee4646364371350155676b252b42c07776124444ded9766862660a39bb05f2fa2193c6b1dcbb25e4a243508c5f7072f436c081859f0c464811805b9810bfa675e80f2310896538a450332f6600864c79e48e80e996397934880b46051bea4a5112826d7471e8f34b349fd0b35f35732f8ce31be764e84e0c3d31c5e66bce71af34918acc82a25f1bacae5cc6836ff437c7e0e2b6662b407b4584e2a9d9384b4c5b704936aa924413eb48aecdbd1c519a2fbf09b262c954c0b7c9fb4c33eab2b61732cefd11e28f72b6e559804eb87e1c88f396b351d19cd0ac34896c74ef92c2387b4c109fc3c4c063d726576b4bbf9e17e7507c45318ffaf0c4fe9f83b7845c035fc756153fa4e71359dd49149e23c5406467ad3ae01a0291c425e39a2be327a52455b2c5224263f4b6d22e97455e0376d6d22f7b22be591c6eefb3b04dd119bce20f0c2521df792dd0a288a76dc12a6f3fd2b4f9518c368f1883c70fa1168c78a6d1568948262b26bbe9383a79ce6f9d55f10d8c7e44e2e41373ca75748c06cefb972a2e1e2ea64e5a137edf061a78bfcb62318a25bc352015854a5f4b53bd40ee1a5a11eb56079345535368556fd361e410008c6c991497900c0eae7312fd0840520b5f6c63a4af8a5c7c40ea19c8000ed4442db25893e1f445d93bcb3cc621c29e86c39a57a017e25f75195593dbc2536a021b4c9ff9040bcd179c62ea5c55adfd0760e84f03db0e5dacd8437d1daff8983ca13564346035e8def45d9a2e1781ef3842f7c5037ec25fceb59733b99f38d7de4cff564501c2bfe53e51b37eb5756ab3d75ad030bd516d4c0b5c15c855aec4a3f671bcd18480dbefbe77d8546a3ef6b0d667ba0b074aa403544ccbef9d32bf1749213c16f78533cd0c1f1ff08ed6b8202ce347aa1050eaa42dd743d911108572be22669d7cf5beadb1f02c75da65c250aa970f4761ceff72d4e40f1f7028f61bab57fb04e254f663d021ed3f035239a068c96e3cb41459cbaf246f1f2cf3c174b55bbcdb25145754521a3ef280fefdc633ae29935b84ebcf7250e28156f940ae7b9a25ebe8c27895d588b32c10155dcc043ba1f82b93dbb5146c31d46f67951a7a7ddc7cf350626e1e4aa0eb30a08666f517f2fb80b94e6c0a20c8e880de0ef674d6df7e5b2d764fab4eda0dcb7722821c683e6b17a24742123bcccba99494e769a1018543e6251b0482051b51e24a6e0a5946295bfeb1a03adc008ed628c01101f4ce042e2244b80c53206015251d5913e65d3c4ff362f5481ce8284847397009616392cd218a8259ee760c2bb6fd7df6c07710d429d0735d054b09de6b3d10e5a3743510c292a3859b19c7177df1bfa1d6766b846c4236d97b6fb977bb0c790c9b0c7148834e7e5578329401905f159e30c95507ccd6dd11e17d0f41ac15f7cd913bf23c9e8adb219f18b2fcebed652eaa7630918d4975de4aed0131d35bdbeb2c2f351d30f7fb8ea8ea977a7fc9af941f0684b3fcd478e0dcb714be86efc93c14fcfb698113597e48d3bc0bb9bc124df37092fa774f4a960f41d3bc2a3cb9a20a4faec84abc2ca2f0934bd010a769ba625a65954fb6843bd6129c1d99238d654d2927edbe817d31ddcc4966dcac8f1a1131739b85399fdbcda3df583e2e4789234e72d51958847653a17dae60a4e9144f555faa3ffae9e530ef5472531abc6793e53ea5a20a7a059367f3aad0824fb8a34fb860dcd11fe4c630b8a351640bc6b28e3acc2dcbb26036eac436b1b85dcdb737eb32d25c5f3acd75bc70eacde84ea54b9e0d9467639f4ddfdae2d988deb7869ecd088a67737f9e0da94a691a5955e84e54c578cbf86524f7bb2b0d4f157776a20fbec0e2a9624f8f157ab8d023869eca83a78a404042005202a404a02b72dfbac353c5a1aa82a1ea82a14a833854bff054728aa7924c62aacf53069e34f0bc81e70b2696154c2c2cac0094fb560c9e4a0ab1e1a9a61427215c1e085937a872b8775041ee2c9ddca75478aaf90325a6fa4f4e65b85ff8c942edb6223e767534ba3250f3c8950103466c1b02460f49b172744c7f71a71b6dcd45cfca5756be22a34fdaba0a201616201620162016a076220eb5bc250eb5c4a19638d4f24e8610cfe9c473e239f19ca0164ca28b64e22299b848262e9209cc36987384d00bea325e5ea490509671bad836c493e94f2832649c4ef7c2fcd47aefa99391bb092577737d7a32fd189d8b01721fb575af0bc844eebb6c1d8c026621b71cfa066d459721cb88a4974757cd31b6b77ab12c996f9aeed023d489faded11dcb03d38f817d318f7f5cff76e378bf7b5a3c273124539cc4ba0254333313fb5c09d479b05232ec21275a28c3286f7ee166187e0ee1aa00de19cd4595a95333de49d937c1348d8ca32c656419234b2d4b982c6d96a82cb12c5fb2bcb274c9f2f5d9cc4b2bcb962c69962c594e1b1fa4299bdec598772a59be9f8d75496a7957caf2f2f0d9d4cbdf67e372893b1a87b73436985373d194631f46d290a6781889ef8c2d03c38df1016f971103060ceac5e5d4c2b2adc82bb2bc8a7c499e247fe5474fc66ed883fd64e4e926658c81d613b67befb077d7bbfa0e9504cc2bb9a3efe63bf92ea6bbaf79302957934a452f79328fce28f231899d7c7223145c73f13136a27494a6919ff1862a883263a3472e2332934edc3ed25c9451873ad16f9c55d17b32dd15692a66fcbca269601e3fa53c9b95c753279a463ece6815e5e926926e4af16ceca3e9f1f28ba65161699a96c7cb2d5a45ab88a978fc4e7291b777f2498e97489e8de8f1325e4ad134a3ab45d390809aa6f47869a45514483a91e35f77128a1c1fb97836f5f131283e9138cf067bbccc691a9db8a455518b1ce316393e5611af88a9c7479fa8a2c1bb41dff0e81ba80d72fcfc994a7837ae9c977793093c515599c4f0f83069f06ed4274f6c90e3e55054d59e988aaf3839f6e8bce1dda84b96c421c74f3d3c55dd6122773406395a42b80aa11cbf5591e3a515de8d6a040661040b395a4031152f8477c38a1225498ea7405165f9c4547c954155f06e585ac020b460418e8ff1f1b10cef8625050c428a33e4488562442bde0dcbc8a5d3a968243a2106ee6ae411caf1eac9f1dd1c4a3263c2b668d52e6c94894aa4ab62b22d2c27d45d5eb51b9a615015ca9583e4055f392e2d2c4c9e3061f224c7135631d59ebaa4a5f6b0d49eba24c73f5b77a2aa42a9502a94a8ba74eabb7458fe728b4a56b137d9abd89b5a54b23d0b06626fb218c5c394556eca2aa6ad46d17244d82d5a89da8ae92c2a1888e9d6749615ccc3846f5eb6b716974856ad38f98262a8ca20c7cb3bca80a20059a028397e8ab4cbf2d122b37cac8f16399e6216b576a4b076a4c8f156d5b9762e235428aa2e27da08bc71b9e005116fe1ee42722f287214ca110696911c5f351d746651a37f29eeae9d8864c656a3742a3ed37440692ebe0a71ef4e39f5aef1c6b10a5072853263cba1f119b7b6c64657166635b6b9b29dd5d8e4cac6acc61657566635b65e599ad5d8e02ab55d3a3115cfbd607d3a9be47351448c80f46f7e18c5e3e6f99b27f656ad59792adfdda523696c974e1300c8bccc2f7733f2ccd3c640e6e5c4281e32cbcb2c714482234f73114784d74e8eaf358a96a32a31b33563ab2bb944c320e2ed3cc4738337de12cd43e2e0bc907cae0862eebff93ea4184863140f21305f3acfa61f7fe934cd930fb614251173e34be7c9c48e3cf0f2716220f07de9e4886f7092193acfe6c544d59d713bcabd176eac06c88385267ea005197164fadad5ba36ebf5a9ae19cbb2fa3376b25f8c512a41732b41a51652f802132029b6b044004ad08b83b32941b7f08957bce245a23978c520b026aab25fdae8f15c54d9c7d106772c4af394330877d69d55b6d96d7ba9786c8305c8f126d3d678142053d17b32f16f7581882fd01539a239a8e5e86f008920132b4173944bd07c44e34c83e7e02ffca9367503dc829cf572ce39abc5653967fc9cf10cb763a294dd5c8e975376b7c49d2907e1ae136ecc27259cf27002713bcb4f885f7367b8f337f2acc9f3fdbcd4d343152ff5b2bc108fc6bafccca3a1bf917134b55e3f2b4e72c9e6e635ffb060424fd6c29d4ade12396ac1149cbc25b0c130a9f6f9fa54d927eefad1ccce89e929c624eee2569b4e3a8fe10b4b2d07ada7d3bdf415f7b3b64e5e56539611d7c67ac2e0f628c9bdbd95f4eeaa702b38ec5736d3f6624abbcaf6624af4d246daeef596cc1ea820ce858d44b83bc7cdfce0c0794b8a5c91ad9a13771defa78fc8d67ba6b9d83d6777abe9d45bf296fc686d4d814058a8839d3f9e8c7dfc0bd25c542204973539aa3c02fd681a21a20b51a2660fbc20e2953865103c99a80ac1065a5502110e9205951fcf469423bdd1ac1019613ae946a8bc741c6d8d201951ba8a099f622a8a4422ed7074d106043e7b4db2a6dd643c50446840de851d8aeccb30c3389a8b436cafeb8e175313cf1d5c51d717348c31c6589bcb9a002db9703b696104ba3130cdc53e80ce57d6061123b825b1b776b3318b33cdd5c8f821ecbbab3d46dbb2ed6d2fa670c09a3b5cf84e882cc436237767813c1f4f3576182062660bffc934b6a1e9904d1fc441b8dcb4318a876cfc012e4f3c236dd868154c9fe24ff505d59e6458f280a0b8b208476ce05121c773e0a976bc2187e18b1c6fe736f364e2774455007ec29063018ae8d59d7d0d40b80278430483c8c139e1e17991e31f8cefbdf79e5096f7c53c99f7d6724829299594ce399380c51e9888818918a0fcf082b2c46282c8524ad9d598f16e643008f977b8d53c19798b3d66dee0c6fc2c7863c60b0216e0a5e4257c27caf23509820b9c15b2c40247c3686d4138dbd90f4ecb516f919e705feb7d5d4dc73c99eb160a9efee56b43c1d7cb3b040ae29a79e49e7270e1bb1b44a9b0116b866895fddbf1ea6b7cad31a853b15ae1d6572c9b8331b83f22aa4e2f45bb9a0b344db7235b8fb47b422f458f6d0d3b24ba1264cb920c682e3e3ed634178548c2b58123ad4faeb76f5edbcbf652c7befbee5064b7b41c56bbb2b7c14ca5e854fd4bd5203c99face871bf4072f887aa97323acef0aa3dcc5c3a2a351bc299e157790031b4e62b85c78d50a5175ab88414d0373a5afa765783796f88b4091c945cf90eb2b12eec3320f17e6676b65af4f7d72dd9e8afee0a52c3c44890febf4140fb19e108725e0ac2c0c0fa7d083b37a2b16c429534e5666bad472c423cdd515940f7aeb161e828ab7ce728a6b5636948f9521a878163c7d72fd0ac61153b526d75b4a2d6b9b89a97aba7131551fb17045c7de71d9cab1749a4645f2442157ca73b2e24e916b14b99e3eb9994aa15fd025558bbb45ae272e72bd0cc27b4f1621094ec8f5758a5b799e4cf5b9f0a477f1c85b412d094a9edec25316e13e9b58555e4fb7689ad2ebe90f9e6a0275d719a8e87a2aa5551027522efae4c43592ab95d3d12f3a1aa5a33c57d025b9fe35d522d72a9e4c9d564c2667c8138b5c4ff13c125355946513b79b4047729d5372ad9f539e0dccf514e7a98e78a9fa5aa734eeee4ed0112b6e4339d5fa7a049dabe5ee3facd39e98a2a73c3155634f177b628fbd660c6ee7ee4d791e70dfc911a54ffb059aabef2d899b7a1e17cb57235cc893eb4d2ab83e69ae843bc8666b6577c3f27b0dd45c3d8e9e36b81dc43942aae2be2f84c8dc410871ea6b853838b9c69ef95a3fdfc51f21326b1bed89f5b5de77a74a1a69973cdaab4ad534521561d9d35cfdc53c92a7b9fac8857ac9d334daeb2512d9d3342fd7dba9599889341c81eefc8d366770fb5986798272bddd2892e6ea8529cf93a997c185f5b6e77174cdf28cc14d22560cc4fae4e7c38b1c6c8c42b8bcebcf4ab8ba36940febd72deba96a74a39618634b86b82547eb98cb4bc16f75e2aee5daea76636af4e0b370dc5210259f1a85dcef6eda85dc6f29379830dcaee463127a36f02cb02615320d72c8fdd83fef280b723f967ca24c43ee478983dc8f32cbcd0486fb6ce03b8bd843d4f403212abba3c87d19b32fdcaee403f46ce0185490fb2a38cf660a813e2a5c2005411e52507fce88210c727f4aebc71091d218247f954841b92d884b3e259febba4411a378e4d8b5f8b0a0c105b684d7e3cfc55afedcae8473c362ed3cad8a32cbcaa8066fa97659d79f264dd98b8f8b2e829e8d2d42a4417e8f3bcf068378682cf27bff3c9beb1d0a3d9bfaf778e407f91df23c1b2bae21bff7904bc8a7295a9cb4d88065c90a1d5686600a8316253c4d913bbb9ea49665a16e58b26d7192fb52d3815b6c901b7bc7b224f72bb642875e1942358521f72d93c45d03fd30016a1204a58453c229e194704a38389abc2d69f03f5a0ef8733b15a17b3d1718a429725f27d706a325223a888660df91a68891ddb0644cc85e27b979327cbcae0d72bfe56849ee29a2836808cdd3e3f3d34ca0101cd2d9814d601014951d951d951d951d951d959ddc2f0595824a41a5a052502928f74b3e259f924fc9a7e453f2811bc4607218349dcc0adb06bb0358040c835a18ae4bd3c97dadc21d995991fb9965bb996c77901bdb3a28648a29f823d5c4037b4c3c3e269e2a4c3cb9af22040fa50add13ce85725fe5a7fe5c262715959fd349bb61c9f3f1e75a8f3fb7a2302d725f7ab88c50cf70615a5478e921f7ad75bdbb8c90fbef3ac3ece0ce8fcbcbf652772c2ca817efda0a1562cbc222f7315a31f522f7af6ee605c19d97f873a574c93408b9b788472c4983606df18232fccf853f4d26dec53550892713358b5b8b0b0e4b9ed805e7c968e1c2772e9525bb00fdc0047aa5ec26bca796c7ddfce9f6e7bb3bf189de7c4d2729633f3123ae4fe60eb5457e13a563c2315889d3383fc16e3474ffc8afb9d6b11accfabdf7de7bef4978cda6f0c5f8628c7194dbe6b244a26753ab966d2ccce6b69916c39e8dccaeebd944ad4596f56ce0a8a3f441ebbd57e7d5bdf75e17638c31dacede7b4f13654190a486b0bbfb3e99f7da4d0e9f006394321ea2e48b648955345b3a49f4d7342c5fb92d986533917007795e4c5ddcc11d11c65d76ba98cbdd6b027562842c2d959c5e2594324628a58472e83d1bdc6d3882a2190d66d442ac5ed7a96227cbd26c6aa21a1b964802c02da3029a8b52663d573008f8641ea6969c3fc428b91842881a53cf251b8002b1d8ac2472c429094532bee905293999559a713133369a7bb7323833444d43345c34d4d86ca6b9c7754c735024318d10765c6c0bb604f2bbd55235168b654f7b2f48cd5bc14c56253b95b4184e74130c497447272e8bb2e6b412cc8e9a3ad4984ecd65910d51c2b458ec923050d6ab3eeb44231a52dcbca454c1230cf7ba27ce01478c319eebbac54934ca127718c8f1385a469531ae4cbec892c1cb424ba3737463974c2a5a8ef74c2b36833335167dc452fed1add6d3e9d9eca7987a5fd14c229551e99248b734822293b692b1d8961376ba5cea0bcc0edc1466c29031a28c77d8871bbd27bc62522991ee8805d31a4e91e84492a3d8a447a11565db93798f125e3a1af5b3bc9fb70bf739b6caf482ec0284108e4411cba64013f61d63ecd8b14759b0b271d6dc3b9765a61ded4d946dd39408be263746f1a013c810cfc176f0c2da9c176e576d7773a2fe4f1d84f049f928718da9d7f0fdc8f1893d4393ac048bb5f01eb9cf068442ccc44e8926d4187ff22bd210da4146fab238e796d5295c79e7b470cb1cdd34132a31f39c3265ca1233cb99a8a4a99229dfb41f1bde205219bbf8ebf2f3383a5eb84289bb22d99af12f57996b941b10afc6f3e1717d9ad5f817445f46db3f20f2534696cf0fa3783ccf5f561aa23f9b475f0fff287dc53c28bef976e5e1a584c177341a62488321f76cfaf452ca0f623065d373768c30ce092794564218e31862c4f717fec558fb8bfc240b625e378d471a9ff12ec2348dc7a67120b123ccc944e330319a606834186e0c8e96cffc7cefd5e8647a3b88037fe092096fbc4c57b09af173426ee6d9c0f7f847b51c516ee18acfc6e678098ff05435ef87094ca290fb01656159cfd6e3d4899645298d3047d19f7d7623b08bb067d88964b77f98c546d9062fda7e644760b7f1160f5112b10ffb0c0f71b9fd9097632fb2b21866b38d88cb9fcbdf89bc1c3e6aa8225e0effc3e5efd61e6bd19364bf4073fd1f5125c2d9868ad8868a1855017bec4664983d668ffd784fb2ec023fa28aee6474c7629495419c97a194688e1ec1d4091c08d3a4a54fbdc2b28af1563f20d090127cd13a30a7b92d7a60128b4032c6861d66b38791c1e84ef2366632fe80428c0d75982d3bd4ea6158307e409cae4fb810e78adb9da2266594bf79394a275c9985c82c0465e11ea8ac474e0ecb4fcf5979cb2d6a83f16c83f9cb7659042bf7671527a873ec30ee033b0c3c4489fc1078ec12fbb087c143deed9f6df627540ff81eb3d3367acb2652293de6903ea21baa0786d91e39394c682f3d2787f49cd1b1e7dce7886eaff26383d886c343f2c9fd922d828313537dc4059a3bbd482624b98f0a3c68924f2ec8fd44ee132afedd1ede08ec0f1379c70e3111253edeb113b15996719ecc14b7cb4c5986fd2d1f2db6bd28711298edad4e51f5a353f875ea47730d71e011880375e0df8aae14a91d9385d13531aa8818bbfb44546615e7eec9f424a34f2ae615263c12b9fcad34d1b51b31bae94668773161224a7cfcade8a7b68d26c5494cc7d1a3edada666d22ec2a81e2278455a0f199fd73eb9cbe444df83e4185ca97cfac4947cf79aa060cecd64f9624d9be82a1b6a7b2b183b7d9acc2ab2941667f5ca7b32612a4f2c54b9b6ba15116f55c1822a11d2fbd9d627721f6f7308a9b74c8fb1a9c8d894d898ddedad48b38afebd11f1a4f8fb7822f7fdb73a94b7b1676f6f05b749855c4529e5cfddd374b1b41cd6a346b5fa9bfdcab6297f89f2bde2da9a89469f5a8f9c97bbd41b1163435d8ff1b782b165877123b4c7c0303011253e4818755d7b5f3b918b511746555c44f6781fdafbd9a39d9a4894c168305be9a8ade52f1bcb5db695679be9da09e788ac8a69e5870990e8be4826f4c8c9113dfbed714f9aa7989217699994d254a4768b19ec8b79d9c3158b04d817d3459e98d343883c2280f7c6bcfb0ce27ae1e1717466fd3d5e721d637d3eebcfdd29b70deec9e9b9e17b737b96db3521e1be192d807c74efe754801b80d4cc361327cd9f5132a333a384fa84dc54c8ad45ee7735721f4229369a67121aeac4ee39a769ba2330d4b9e41110487e713add3be35663eed7a77a71ce18f1a9a3605f4c4783641f40fecd1e51253112f147099468b883449056c94f558ccf514237d9724227f2e4020cda6017602afb5bbd22e4b51b119fc56bf144b4cb3f2f20ec02ecd30b6eaf000f63804ef2fc836dd028bd3f8d679ca8425d2ffdad26eac2a80a8cee493742f492e8a48be661d03c84320f816670c4d4fc9defa7c708a37a9c8ef2d170c667e3d0788f9723118220cd3d2838382b2aa34b32ad5c05470747c7a7a9c8c211518b5a22ca6389feadf46b83f985c6675cc6cbf656b6da40153c99a9822b80a5dc419c1286e5ee417902c481c2ed6f34fe6dc6a156c45bc9f88bc5b92c12cfe6fa7c079e0a72e1a5e6e775f52d21d7698fbfdf3468ccf8d35015f710894a3d6460ba41e0ad60cb31dc43468ecb0fa1003d1b9d677323cfc3262e5b8ee9f7df72481fcd25cd4d2d985c102ec9f31028aae06a1e3689aa7fce777dc48777ad13f3432835fe0e0e65fcae5db6d3bff9b0bd954b8dedadb8edad62369ff9c993e77d26ac22cf43297a90e4f9f92d9a0342e273d58ba7d6709111e3166ad8cbf74981095ce45786fca4b08429d9c64c8610ecbfbed10d8a3c7fc131e4f907819eccbcbc221b695af61126327a36c2d9894c69c5d59e1d7e5ece4b275c2be3c8d8deca924fb8f0566651cb7169a81ea796dba9e5e89183bf5d184615a1fd3722bb8c73dcdfaac65fe346c83887896497f11a9888121fd9659c88f6ffad6c4645a7711fa2d318a25d748a7d943e030fc9deca82800c0bd5e3746a7979abdbcb3195e5302a8cb5bd2c23c76c2cf7618371bcc17cdb50fff672199bcb5fee1203e79cde721cedd372727941c1c060d981b1bd15cc5b554185cf4a85f4572a894697645a699d3cafb293a7a9084eff453201452fca2eef438451f45a8647cf6ec4e89996a1e889a028461591fd6222f7d9499888121ff7d98990467f2bf9d2a577834d626a8ab6f9d17c36af6d1028a626a418e63437e7a5122e8551c4ea1970b07da36a5a36849036e8bc21c8f011e22413a6b989ef4f7723c1c263e4f7fb5435a6bade808141f46fbb5a9f406a3d7dbdf4b9f52f5f7fd5aa152701ad9e51ca98e726afadd61a5517e679d2ec3d9b4969882cfc9ecc94b782103b2bfbb7c17e5b94935ab55e98cd5a138d46975452316d2b2c2da7b76d9b8ccf88e68bacb0adc8cadaa7517abb59c7b67ab97556f381bde22148c4543c76abf9b0b7f09023622adede6a1078ab1f6eb8fd209087621a0fbab20d49a2ee047962799a25bcec4a030861849027434893f0441672bcdc82401e1411f1f231366c924579f70ca71695bb7ce512ab9bd5b4b38c8e5db4edc8dad388b0b5de7b3afd1cd7cd641146dd63b843ddc0295984bde24ed4edc8f5dd8e8cfde51db9bebb9f9607e9f56217ddc029797451adf79e4e3fc77533d9a22ed601a7e41116d55beda4adbb2a1fa91cd3ec481ba91c4763a3ab5cc340543ec24058ae69af5b27c2416ab28a65d1de1a0f162c9b6bc197d770e7229b5bb197875a036cc9f492a9644da552092b5953a964b1923de1d3e964ef19acb5dad6d564ed76eb765897961515124ccdf6289753b66f59e1b285c124fc82f10977b70577a7ac9d0577a7156cc2dd392e7715df9a5b3b8c0d47d44650bbb1761b3dda8f705793add501a7dc32dcec5d4d909a9a1d416af21210d5232fac7eef2dab49b8f5cdc353dcc17a89acb7c98b004890207d19049bc4ccdc97050e76f2cb02073801c82f0b27d490ef90e385c02c8bf8e429ffee00e1dbc2900997df4126907b36f2f13d2b932255e4aebb221326cf3f1b148f297f96cad73821d093692639be6687537257f17d32f5150399afb9b3308a880984629c047b8d2a0c43ebd92ffb007a6b339d9a93b73932dcee41c91289675355b217d08a2cdf72eb9d27233f9d58f2751302b385d9939173ce39e79c73ce39e79c1d249b60dfec532e67366cb2679046e57159f6ec84af4afc29cbd9e381c8fe9128c31d3dd4f07b32d5ba30ab3120cb2c6d0197ad58fd846d218414e6d8ed68e9438c315e88d3a379731d23ee82d4706d4118af0fd46420f1d7ef7de86a328e98ea083caedfa3b35b4cc6616cbfdd5e37951f48f45b381bad8a610fb3d30d18c7a8e8ae06475471b147d3d434c7848cc39c891f86363a89f4847bf3e82408c0285eaa9fc44bf50d8ce271fd241ed7158110856d84b1a1e065bcc80a662b02e6db7dc07c3bfc06e332f0901fc687c838cc8baca017577b6b39ae19aecb557e7a7d6b37bdf282b79fb62431ceb21d6ed1aaed7d1843e56dbae8efd22114ad8ad13415f63c9b9557ee21284154c1887934fa7b34371e47fa7b3435f7870b6edc82bbfa381620107772c546a7e24d9b1097b1a9e00e929e108c02fefc7576830ad11c7d8ccd46c380d9ae8d3e72fda0dc381f8e0e52534262851abc97d674105fad78e448d366c2844ed8f352257cf5c9575828bd68ee65cf066e3c464059e6972dec2827ad3952a9042b94a78c726b8e2cc3b6fcae5777bc5c5df2fb7b94924cc3455d8719a583e60b33a0b9f9ce257907a03c4fbfb03acf06e779ca45f73c059aa7502815caf3d6c6e3c9cc2e8c8c21cf77161379be2ee821cf05403aec7832b349dd5e9668b89d0da24728cfb3b1140a9b69a2d1259534061c7a71c9706bd663c76c32a10445155dd2d123334aabe891989a2fbd1b4288fbcec6683651753062782a1e17e8d47c47990c51c21dfd11825661e3e28efac48c70477bf214e1233b74c85cf18fe6a6adf5d109b78e66d0e8cd264179de06358de8f3f649d34079364157de5ea2c6addd70b9de2231eb4c4cd56c4956fec30a2ec2fd1155477079be47547113e7d96c3b4c90d4c0053952f8c1ca3ed82f5e87f9c2dd8f5cf1556ebaca4d3de619506badb5d65a6badb5d69a593d58d7593695af6ca6d7ad833c3e58ae8287b0bcb51e3135bff29e9f6e98af3862dde82fab5a4d03c4fa7b92a785ab1552f9428693ebe5b3b99e218161196655292d69d998bda6c1de9954309cf230fc1ef768b8e6ea81785ce5e89f3cf69e1be4a20a0e35cc0364e271f535e2aec2294f058d74aafeb26223d389a6e9a434f200ec40e4a191e6ea49b379eab826b6bd2619dbb07a1c5637449e5c9e5be7922729cf0bd12a0865e67a2a0b67be3e9bfb8945183a017754703b77d0c80525aae00fc55d2d619cec240b070e3d997a69652b3f26f07632cac50e6f4ff3f8447887d74872bd08d7239d0c178bbbd375e1b9860ba168ae1e1e69ae4227a0131bdc69ae5e76713bdb77d088fdc50e759aa66bec5d904ba7b9fa6b83396db8f2171d47431dd1ef33fbebf5ba6d0b0742c9f5d7a15839164e73f59d3b0ba7474a29e52f70d5cfe8731b49ab6c748abed6374fd38c447d5fdf3a6d4488f8d166cab69bd58647a26a884ed5218ee47a6864073ad1343d2557f85470e8a5f2f0544afc7836589e5fb020571c1da883617008f2b40a894ed5cff9ec3293f39a6cb8b425bf532cbf5b30d08b097f3a55678543d8352d0bb32c6b599965699625b2ac91655dcbbab009b3bd1b4bd419efc60e18447d7dccb3b9ba1ad83b1f729da689f364e69babc7b6acc9a53dcf66be9e0e4d21a7c8d8ce30dcf96bee8037ae7a855c993c9b1fb8a469b07399665996655996655996655996590be3ecb4ea85d94c1359f5c26ca689a017723e3a017b6cb8b473e66b4e27d7774eabacd743294d3331dcb5915c6fe93c1bfbecf5564ed5b44bd3304db39a96699aa669224d1b695a66eb85d94c1359d948638025a585d7f5e416a42663f6137735b37120cdd726ca38e2dc82d464ecd9a59603fb8472e5ed77e4eb739b6bb8389aab1fa2b9fa6bb3b265e1348dbc0a4ae9547d95180e35579f86db0dc1d51fd15c3d97edaf2d484d1ed25c2d4173f5f3c9c570089aabcf7090e66afd44c3ede050ae55e5f58d7344d374aedfc09c73fe024d837d6ed1e74aae397a1fad52c1cfbed6f77836d76bbd029e4df6fa219a46a3c1ae563779619dd94c1b690cb8d81fb6d863c6ae0c670057b0cf8ee01e22cac68c554fa87e85f07170cad8856c9f1d6d11e777dbb38b1b0f4f3992e1c6996d27182757be278c30fae42ec8037af012b529b371569a5d1a0316d0986b0e7ec646174f48e241c64e6eeedc93a9b9577eb28d19b7e52881975adc6e36e1a269acf74bd7e576bd6e312def302eb21c032606ac49965db82093249f26dd0452ad04c51493393c2a685684e614a977b30aee3e17dd8a6e17ee7489e276e132e493fb2b415115a327a6849a264615cdcde1d1c47082b612b4d923bc543fb5d9184e783436088feb5b9ee68e608df06c6abc6f9558296c112c116c14cf66b34962f444558c9f981a821582456283f06c5edeb740b03f7836a8670313a3877b67ca9de5e1deb1e4cefaf40138ccbd899955ea32407c868dd7b0f1186c6389bbc4294b9cc4c6adcce04d62e352ebc1823765ca942943383838ab9c1fae5d89537e411de63fa3d1bb6a89a2cc46719a00fdd4f65e005cce6d0f0ee0741b5b4e13759503046ee2ab1a78ee81069e5366e0a987183ca53c99be0f78e201e399870dcfe339f464fa3a35ab1f70135f29112244899b65e059872723f1b4c393e91878d2e1c9f461e009b34da1298727d37fd9e61cb46d7a91e25686e69487c5f889c2c7c6c85328aa2ccf4bf56bbdf764797c6e67795890b0f034c78293fb526a3c6896786548cbb162599068395878b8689aeb5b9698a5e79e472846cef008280c97f4cef2b8f03c998e176a9a08574c2a1d97215db95eac1d4beea690297753e8966194312c04e66e65a8aa6cdd67be98505686465cc8fdd1ca50eef964721155f5de2a4522d1dfca4fc6d153c8d221401946eda6662994595962cbf364a4b89dcb9090944ec9d79c2c2f3292e5b5bf96cfa0c8f2f6af23922c8f4991e5afa1dc3e591e6a3a26eea1ae763757ecc21dcc1742609e529a7090e893e77c327668b44a13dbe2e272db198f1a17971668dd65fb408dcbe95b5af0bb2e2d5bcd4f2342e58283d4b46cb465a3b7d6d6524f6fd9ba9adc720bc5d97867a393f932ef6a06e2dd9d4d62aa2794f6e1d14c374cd436a7bc545f669b7670c3a39973781c94a699423ebc3ff7f06cb6f7a71e9e0d7e7f4a9979783632de9f78783677783630de9f43cf26c6fbd30ecf06f5a63bb10ecf867b7fd2e1d9a4de9f5f3c9b1fde9f73783631ef4f2f9ecd8cf7a71c9e0d8df7a7d0b3a901c445405c3bc5e90300f1bea53b313aee3d1996c36cefc9ac5cc6f69e8ce9df30e9b67fd8649e4c3fb501600be2c9f451a9ed8727d39fb101f164fa319b8d27d358e69bcc2f43e6315087b12d7133ea30db12a78cc24964f0e8be008f002db3cdc4541f88edc554e6620388f70bf6ae4ea01780187972cfc6c51e6a3aacec82df9339e1ce7acbe971c44e0003108200342a0418a0a3391d6de25a4260eeb88ecb1dd705a1b0264b1c64fac422a9b66368f17957ce7213620c2c4134a8570ad7680e748444f40551135817b9ffa40fa641c9e74a21f7df688424f79fe80bb9ff444de4fed39890fb6fb3b2dc6c88e17625969d6703a15cd913f60a581e3029586890fb30282bf9644fe43e84581e721f625719721fd22cb79826b72bf9acc880d6285842d60f2a2928eed44b06b9df0159dcf82ecb314699c5ec1ebb792aa5bc94f2ca4be33ea639ae1b9ec63b29a38c324a6e8b692e6a8fe1b81ae724c6271a9b3435f71c6b3a518cc4f1e65c248237fa311ea6692e5f654dfe81031c64bc9204c9de1788f0852204c9dab6dd90e8147c370ed23412dec066bdb24ce5ccc3a5f505d1bf402f816df405d19dadc3245270cd27d3334f947c328d2fd01c9c93e6886d7bf6bb65f73f90e523e60ffc3349dc051982de5a971b90581f6f611411f0b5f110d1da7267cd999a6bdac4ed668227461c4ddf937fd6ad8c1b7dc1ed20cef58eb15f7cf192dbec6ce47e6f169e5bd58941bcd808c8dde3c9a8e214b77b5f64254a3024e268c87307fb6282d4c4e0bd8c8adc4dcdd01d1b622033bdc43e3b052e6821bbe4abe595af1a4cb90bd97646856c63263795152021a3e083c834b93714c47f59a8200bf965a10229d9ce304011861e2dbec8102731248f80252b71332c49c9b66672cb84b2bd32b995cc902d96f5869a7f59e05c01356fca74c37eb2cde4466f9ff644222df7f6b2c049429eb349a7e2bb2c3f6ae476334d20ee4c333f6aa2d544a34b2aa958da02666c8ef13ba2eabe9a638867aec7cfa0a8c21eb58b3013d1df0abbaedd88eb9a66afbf1586613531159f6d30b82fc38c2a02fb751ff6596ef224c7dbecead824aa26132d5e93c90c8ef81df133f135f17643f5517debd88bb08edd87750cbf98f2517f612b4b2aeecd31002401e417df71394a21479b1cdfb9e49968671a63eed9c01c2f6764df1df80e3522ae6ce18e04f9e930e5a031be5fc7289ab7e2673f9b6749325ceb30c7cf5b1a8f1150ce3aca49b71c5b0eeb97a603660bbfc477bae578b83e788926de5e72bc1903dbb7b239383313e19b4afabe1915511353f035324e538d96e36ee1ea9055077c32f0b0470e134cd498829676b33d19f8edc9d44b47266dd56edee33a1277a3dd7496f78e8ab8b1b2bc1de5203d0102d828c7256fb3ceb22ccbe0ab22a9dd58ec41ac31d13cc50f834a14999096c1ea91133f44890f3b1a61d887e8191ef244b7d7fad9afc487e9f110beef03bedbc7ca333c24fb0afe35c96df4bb89ae6d4394f880a822e447f721b10fb1bf1759bf8787d8e221383efa080f51e263f4687d84df651ff14c4c591fa2fdc554255dd255fe56a5bf15b4decade214a7a94f010b81afd9eb41109414c59a7d64718e2c494659da40422439440e0ade63191f9b7ea204ed6e0b51e39f44cc0d5fcd994f8806f3c04fac494c44394f8903c31655de2f8214a7cc00f51e2231ee2217027a6ac47cc93adef344f077db2f5b78aab1763d922b2a917359e7e33d3898b2dc54e10ac60adc13684a788af77a377d8bbec9d664f6f45524a3bbaf7a38b25cc224c84a596691866ccda4f8bd12ede8219478c75eb68ecf8b41c126f2db72ee688a3a525b31d3c6b655a8e7938e71cc28272becf2ceba58d48f4c885f645389458624b9fe07b5a8f1b53af4f1b2a6b181831ae0c2cbbc532ee9df260cb281438f321a34fc4c46ebe8b5bd319197d22461a35b2ee9d2d97c9e630dd0177f9c30f4000118138c597e8326774892ed125c69797179807800723c607e3c17830de9b50de20b20004200a60cae1c24b010c204b0d40eb8123a6705085c8e413271a19016ad0e05219cd0f29206c64325a8e276303007026ab09c08c00b2d734340731b70019cd01d5017de2de18228373ce03bcf7a29c59c2b975f2344fb9c52c9beb6c93c9ad009d5f73f646263704e4478f5cd806c8b296c3001a1171ca9429531e02b207b3ca680e2ebc8d33469bb317553a75458a2a151a20a88975ac746a547aa6547ad4e156a4db5546da744e2b5269c34bc49cc404a4e3689b80ec4d23e29429f14d99b2221981801509df98923972e8d0714371606fb41caf43f18034cffc7833336a0278c1be914f8eb731ab2dcf20a519463c129332446f303442d2a9f839e2e189aae904144edc0e1bc2863029adc290683a786e7085489e4c9db84264126b71ed1d94ccf5393e6b95fdf63ad5757b8f8335bf5b8993d863437668ee40e142982bee34e093e1e136ebcd32b5c1ddb919f1d81e8c27470891e40e1bca71e4831c7fbd1b231ebc20e221569fcd25b7514f7335b8f2dd88278f78749a46a7393877728d50b8dddc999a8e2d437cfd3c9978ecc84b6b0d7868c8f27327c3434a830be1dcc9726e2324239e114f6d1aecf1598c1ff1c451cfceede6cee4892a179f2c0ca1092a9062c91556f1b327aa3270240865f8c215a690811156f173277e1ec120c498850dc56ae124f6383ada18b5da5cb4389894e6a4d085efb0a1914fedd21bec8ba1714f358e0090dfe9136e466572955dcdb214d18bb61898ae1826b56397d23a3507a3f56de2205c9e9f1a112e5aa8210979de46d6aaf7c393b1e70472c455eb8905fbeca8c65fdbb5706659d2b2a66551cbb22cab5ad665599865419bebc4f2b5dd99d18a25dda468e2f8ed6ad57b9f4fee7c625f4c57e2a9d7f55a343471a46419457e91e98332eda11eb27d5dcf6f46a04c9fe3fadc5046b87c7e622272431971bafce96f65a71d321e295a7583703a457f5af2f2ce46eee68e8d2b94a9464fd4cab41fd2ede74ed35cee528893bccc9d97a78df0684973940a3707c9fe0645fa672dee9ba3e4e226be9a479a00f4dd89b4cd1d36443768f65d4b7992e945f3087e4f0665b18e8b5e747b51b4160bb17217e40165948e2b6bbf190ee52035d948a62fe1d025b7df95727aa839214fc86b4ec35d9492e9a350a6ef2214c9434fa770e727fd253111f997534c44099179d49fefcb298c2781f90d8a2a1878b445731475a1e65fee63fee5d75ff0102517f6210f8387a02e3fe485dea0ebdd8d92e7d53809cc71f4bc4f9aa33728aa465be0d1169367f6cc9d7904b593e95fe4914c4fa570df4f13d48bac503e6060301c8a29fa971759d1d5436dcd135374e565eb2331b5e4ba2e8ba32d2dc77578ed61932640b16e0d445756b41cd7ad8643f32f180ea17cbc1c75141e22ff821b27a6e88bace85b8ba679adc512fade7a4973f4766b26cdd196d3787a5ba5b21355a39e98a26fdcdd5cb3937e6d2468e7f6562dd7e5f656a39e522dbde7c2259c8dcb375d48b1dd9dccd775bd7ce364c7d26e9cdc38c9b97192b373fdc6c98e0c6eff65e144273bd1697ce364a7e4834c7f5daf54cb7155eda2585cead3506e9cec3447439de464db5bd7b90f5fbe7162a46b4eabe64ea7a8139c2b2b22ba8c72451f803ca467b75626a35c1ff3484cd1cf1759c90de543fee52f78c8e47991b913536e2892e96526b51c374e74ae4bd3e265f18d931d2752cbd185942e869a7b375dbc2cb51b2738375d48699ac6dd8594e65ee6c9dd0d2af9802375576bd5e827a6e8ef5f828c983efa892a68a453d7a936fac9a3c650a7b9eba4cb8a1b97b3adbbae8bb60e4764a13d99de34773a99bbd11699fedea00e4a91e94b9b1049df41cfe6f5e0689ad75b649f4cdf8af692a6a9a247d368e7a9abae99ece8344ea6ef60cf294bc9b47ffa8a4ce9cbb4a7f05e944be117519e341451a79dc8afbbb0e81e1715dbea53c95fdc9d7224e1978b304ac76312295840638db4e910dd9036987d9c0eb547ad07d46e8e303af6c34cba68eecc9da68199f629cc3476729fac95bdc41f48ed66f41b537e4f06098b0d396bce947f903eb59bcea2cf8e48163d6a37107694935a1989b4d92bb3af51e5f2d37db8bcc61e2fa6aed9208b2cd5915242893132d17123ea9eac3dd7e6447f1a8fadcbb276db0f42e822d28ea3513c66be9ff96220a38f6edd8e36ecf6e2ce0bfa167a3640b4776e7bfab9339d805d12ae7d8b4ecadaa196a34fb21d451b0e0534cdfdd15bf4b95ec09de6e8e9cfb5ef8bba9f03ee9bc24f1519fbe8526b00f6d1ad0e9bed5bd331235b6c4fb3bdb7eeadb9859e8d3d7d43691aedf41da5697a884cd391049c922ded7046b78cc2d5a070b55b489f06724b4d8726ba24c3ed28cdd18b2e23ed7944e8c9d03794e6e8357a5986db610d25d337934c6f257c2deaeb88b9f1dc7932f4140af773a746e7066a3fe64f2ebd7590e769304747185f26ea5090b5d3ace9d80d7b6ff6d866ffe24d86bb2090275bebbacb76da62280652e229f5106bb056eedcf76a6c3df667ddfedae0c3421c862004e38295c5b509006b4c45141c59bdaeda2d05300d51c059d526002a8a95dd4676c801d0ca4e18032e7eb0b3a5a07f9eb80193953db6a560868a95dd621ea0e0acecccecf6dcb0049c95bda28082d0ca5a99dd50169f1e9327880074b299ddb8191817ace0141a2cd1a9c1a1043020e167b563eb31848bdac20a26a1073f40ab2ecb222aec90869d95cd32bbbd363c11b4b25a663708051c765656f4640c402b3bda02676555b2ed6111850e7c56a823acec069d1c6181cfca9632bb05608b2782565625b35b0a601341f859d52680dd52f0e2f082a0556d02c0db15219e9565c9ecf6beb082d0cabe28e1c8198456f694d9ad8f74f1b3b230d906932005157e56f625b3db5b43173c2b8bcaecf6d000059f9585c9ecf6ba604268656164767b4e8010a997a8346d94954dcf3a866844000080007314002030140e898462c150301e09dbdc0314800d8fa64c6a5299c8b32086710a3163082202000000200018699204853cecc0236e307511c6017d919d3918db7b8b4a88d8f83038892b888146f2181c5d8eb9b60548d34bb06c41b399dde4294aab8f46041b1ef7e299389cd4f5d75e9e90d75347b4ac740007597ef5d3b94803978ffcbea411fee6d7e74b045f1659fac0e28ee279dcf803213403774b59b36c11f8f3af66ca87ffdb2e2d52a520d65cac241d15c49a4bf27aae3476c5e8158de03cc5f44752b948956ac587f084316354f47f5d08fe58812094aa95a48e83f4c76af17878186563b0a665caefaef2305373cc8ce18abe24487b8d3e2474b16a8285fdd00577c3ffd6fdf5cc7b9abd1de320d430696ca49e68fe2d83f31a0b9b83be9f2b861e52352849effe50c24db6b013da56496fde0d64ea24a9e4608913eaab32fca9f9a1bb1dc0d8f3480909dc6709ce2b0338eb1f0f10af06868ee577efa4d86a40ba5326c809938d76ce615e610fe4048c4621272a588e57ca740eb20d0a76aea5b29bd1b832d80156f5f2f8edb072a8d5ed0be794aca8a3d9808485192842ed5040b6d56ae03d2ce24f794503871bdd4cc2d827aa194d44039d059afd453898a0f62c6d81cac0e84e64604bb1ad4f14f094148a02d5a3d08d97c9a3bb936bcd3b85fa6353069e7edd71313b2dc5c64076090074b51b88b2d66633cd3433d9627be2c183f757e23f5961f2b604a77bb2ce4b9d85ca3a3a9ac68d524486ce6d8c75300eec892b7d28fccba39487ddf928803e4b837d935f3193640228e916e1b112f0e70f72c4b17d77a5df6bfa51143b3b51ee0291323948be336e078db30d053c34789d5dbe4d656e48c4c540b52910b8c685feeab984b4387ca4c581c48437abe3005e0ee6f2ff806ed4611d239031a297bcf86cc352ed98d6cae58f935f63fe9d18fe76c031743e569fb92e6aecfce7d28b4a7c662cb180836bb01134ecb3d6c4da319409d10737b8a1e65ef0c5f751ffd2abfcd25f4f64b626fef5943118c944a75b8ad6fedc608d8d4005a02922ca2f75369e3906fa5a5746ab66e576a5c077a1c8be2685040ed809ec9e2dae5372371c409e360dd01587e0095d237ad9ee37ba24ae8154f63fcdd2d23d07f1cadc38b2c315f08650e705dbee68ea236c43e0d8b0ea778499305d7ccf334eefaba39a8cab0cdbddcc33b0d243e6f9befedef8a80584436465aef6754db01028b0b8f39678d981cb8fc3c455f28b2a0fcedac23b887ab57bf97ecef11635898e05e07c7a8285a07297595c581639b6a276e6747ea3242c79e92742b0bc3c3aa1b0499cbac456eaa821eb791e9e91389f891519a7f1517686b94cd0bfa9e2ea2901d448c6edba4f21b671f92c5aee3d608a7b67dd104c25a74ba881c1c99442616fe74938691aaae3b9b3b7e22ea407dd8340dcea13597122b3124031c1a2d750ea6ae9a79b0394c021ec451120c0a218c8d432de66b523556c0b20025736ec9cc560116af9d156897786aad106c56992ff69c771fcc82cc2c710e174f92062c3e895638468ace346b3e6c8a0f7bd119299edf48890d466471792e38978b1797a61d69cceb13d266537fcdc3451ec9f413f111226672320fa9abecb8ba214ab40f1985362d47d558141110c31404dd0f34863ea6b755dcfc7114544a7acac14e68805ca5fa232b5fb6cadfcab59636f5623b5c0648736ac198da8e998ce01bd3f3a90744a079cc6ea80092d126cd86892f9ead7cbd068108f7a465a0f965a4077df409547e37d4af5fa8911c85c974d9bbf3c0c82204fe619051c9ca4aaee3c6eec998d106d9aeb87709734a4a34bc0c5b7f00e3d10bf10068c838b449f6a9fb0d8daf2d288225d22f7b1c6f4202f8d61fb6ee03698c9b31abface223eb00ea770a7ee6edbab3741da8e8d69fe9279e4648572a4301d3b1bee12c2211018cc9831712f747cc49514f050c12c63d1a8417948b9f46fb9bc02457e552a8ea2c5c2b38fe1cfdcc738efd495ce8aad58c2b537df0f1499aeaa9b6f1b1c676554287b3be3e15fe62c6a01d07f8955e41a72c07b773898a63faee85466c9b40b387c3c74ebce332c8eb04712250e7ff174856bc1c6f935350bb5fde00eb66ccb3ae38f09d195fbd0aa55f0ca93e1a942657bd0235ec6ea67a6a7691996b9623be48a7304d1bd209a2cc18952878d1dd400da5bdba163f224f7974e53e4ee01cbcbd9cfad91eea488904bd8913055273ae922bb4cc25712fe8984c7dc5b53c554d01178d022179564dc9c2852a07694aa767c65c7fbbcc598ab1457988e5f3819a24f06a8692d65fdf1b80cdb5eec38deb4dc5d09a43a77f5e61840655f864927cc6bbb3ccde1b94028c28a7c3d52b5c1c4e156be051bf9ccab3f2c002ffe8d09f84658ee6ee513182b9e8f44a5a8db4c2ce6938f5761eec1332950f3c70da78b49a8658cf3210262a717f13150fcf0b4700a1d4a0db7c5a66b1a724d9aa5148a709ec866b0dde9c49305f1013d112b806c0209b49d5a53c518e28bef039daa5cd13a5b8efcc3c430e43edec7a6321de232249fa26eff8a3ed388a00f74747a9e59c67167f1da7919e224ee134d97fa92c61fa39bce9c4350917f2ce8981e60a806833ec2cde418f40ee9104455c0b059be35ea282e0d9f73883368e3a8d58257275e716d79a7fde11221f8e4291a9ccb959b72c507645e9568a29b061b94a23c81d9868a60af914f88e384b965360a997da329fcac3bc7352d12059aaac7bc91f8df74406ca83e4323c4868450eccf25ca213c6d398c063a8e5080a82d74e8632aaf7805d0a5a74966056382c1aec40601acb2acb96a6a39dafaac2f6755b6d9bb49fd18d0f6386dcdc533c1268c7e5de14ee30cce4c33819e5fcf0c7c3ff6ff542b3baf6185fac2351b20ad5d20ca5826ad04e1760e520f44f1247905281600733793b8c434eaf33cc584686ee4d7d82b1a840abe9f7048a267384c867bb29a0a2f32866e1cd0798a32c2df3499e73b49ccd2e2c707e8c67dfca972958fc152d70d0f7d940987f47065ed67b3a319bfbbb336e713cec69800a6074e7a3282b05c1216595db79f53efd6dc547efe41e173ea61007b5d8a8d8b13d5415b7f8f3ce0be67f306b71a93a71acc05160d73cac7c03e8408c42b701bdbf454c949ece186d5137b636e85035b5ce124b4670995183d721d231bd5fd1c3e43c5389b80d5dc74649b7e821227dabe8f720e32d26c0a9bbbdb9cf00d90300c7fd066819a85cb284e31bfa314f24b005b60e4de20378eea6f08c9405aa900b253eb0c7f7319465eb07d1e14b0da347ce867f12af8ce86869a64b514552c25ade59fa9112d67f3b46c968ccc6f1c93d04ec50c518f8fa26635789a07a892cef314095b8e1ba63856714bf6680e710ec15debf00b5a55b87e6bb7aca8146afbcd3ca5c7ca7d55cbeee7446cbc74106478c898f2b2d5c3ef4c9685921c449f716879c3775c85a23da9fd86ed92748fdc1bcdfd0ccf330c07522613fc036a3d8b721725a7f59e7da5cefdd0dbb74e770afb1bd79f646e398b5b46126236296b8e82d804160a76c933d052e4e63ae1c119143388c3cfbeee0cbd19b1b1ac9d040530ab49940cb274a1ae0bb951afe401b87c7429c2d7a052afbabad35e55e42986b54aa94f3d1826b9e58199faf84ec166619bf5e6e1caedc3c43ce4aea8d1d9b8f7e9fd1f5214f37d1fcf4c15a61b4df9237be1238c8fc6ad4c3f586052588e83a2354f5c11f038d7d984edc05ad5ec757c62721a497c519c03bb7200a3c0013ee51af6b083aa593af2b8e0de4c6c9512fa813a9fdd754a2fcec42f69ee197229aa301541452011a9da7e0e4fb349e04dcdc93948afe1d257fc2bf2aff3bcc8515f0a09fca08835078d2c09a667320958e313cd24d20c989be83d5cf2c90ce053a4d28af11dda3a652cf2e53100b49c2d6064608924a62efdaffaae45e87b971deb720e11c315413c8c1379d536440fadceb5a52b5f4f3463bc8c6fc07df0dd62c0487c51755255105dd261cc11112d6db4f67f9593b2a45965c92c1210e215db1587b821fa8b8043def8bae6dec78ffc2273c74ae4a84eef0488b70f630b3dc27a8ea188779d6aad817643819e34a7445bd00bb14947bd9fe2be3c660bd772fbefb43986180a6bd2523ee4066cd53d41c3606cc313c32383b5ac3060f9f2aac055cfee63bdff422de5c6356d0521dc0bb05d2f763a5ed184c55fdf091532c7d2b5d4fd124393cb4a63d89ae13ca9aa71e06c3ec3bbb84f8abda14deeebe24fa477884546d7fbef786cb9e04b460a6d8e09599528129db864309c011158d7a1b0465c5df80f745c6dab62c56d5ebdedb416a0db3bfdf7a60c725e38a7341c419a6986be16c8ffb24ee0db2acc76f18944b8bc86027a16569a60c2315f1e3de18e3af88ae3670744c9ae0fd784fdbfa3d8f57b6049845212d0aaffff143df9308445619325a9d5c81cf230e9df46cedaa5dc55fd47ca039f47ba1bdb8566cd72cd2292e415d20ab8a278fd68580853e3815d8683006edbbf73de3a6a5f5dc2ff5769e9580141f2b20f6982a2b06ba67fa3b81ed3f04628fefd0fcd1c8a187939f035aa613e47e0975594cab2f9bed113b453b19973d109f8b7d8b98c1b9fb675268e45b142f1aa08cd096f1d6fa30ab19b41c3befac4b9a74c0ad090ddf6051f089bf55e4486290cf83c079de31eefae27453585a4ad656b130acb69910a670b9bac83e22d19cbac9b511fdfcdd9d5d4e7495f10b16cacace4f21e30d06b22b932a5ff9da90ffa8510180892b9a95c5a0c935f3ca24a02de848f6e5956f82f9efc0b3bb466a8394f5ca98220f14dde290b6d816effa048b3749c537a6a03de669f25a31288c45f33063789dc7091c4d760361f7ef7ece6521b098a81b31650958a6623e6312da876dd1307b100599d1031bb4b58e1baf1e08172d5c4d4c8e2ff450a64d01b0cad1663f4dc67673c03edf4881e05fddf0c6b56343c105d767c9e561d40202dfd682b79f6d87fd2920af21865f7119f1937d951877d2727e6d831dd5e81a5001bdaa162e5d2dba632d922f8fea0901a5244d8aa16dd07efe0420a3533aa7ceb2d544334a2ad3e5fcce5028ba9bffd62f9eb11c3cd09dfc58d6744fb10c98bb7dab9b4d4ec0f967f64ead75464b815dd605f0ee42256d246b56775b985b4f60f847a0f874dbddd3ae870e6d2c83b471e20b615c7de11b5b7ec95d3823a7b21fd2d852d1b4e955164c1439608ef114885843a2cf7140290894747524a306853a10259dc7cf2729643c2318ca79e1676cbf55af0e9451dc8e48a211251cdd40c0b492ffed66aeaa24d0503f0c0ed061a14835d5fee01b79195c42c718b5d0451233a569898db11d1c08f4fe81b79117ad6cc1b38911d828a118d9ea4cf5748b8e48664254c99544c926a4460aa4542e685579d5fa6ffefa70ed397aad49013568644a60d35b5c14db5e88bd9dd215f455c2d10a75affeaf276d9db34f07b4fc3cd9d1317dace63129ce86439fd6a454ebaaebcc799e4d3d60761bc55bb5f6f57784c430e690b41e90b542dba3f1cbdc9596ed333c49ef1cacb906892a1ae23524b2e4ef76283c90c55ef634d350c99ff15f976a92317bee877cb086e623489691094096ab0534aaf7b2e3c52e6193e04fef5d5fbda831c7045237a9ee7321e73731d5e1e5fd8876b18af5f8b9d14a89e3bdd702379b0993bd9d452a91a651c760c7e6243fbae93e1d68ff6e04ff7ec4773f9a773ffa9b9d9205fde7b653ae27873674759d18e4d0d8716470ae20cfa9a05a26ee88b36de71930df7c43e2dbce10d29b0ac2b8fc05e8c25ea56a75bafe2d3a6b1568fab8ac3102663f30114a342cc10eb400e439416482b0f79f91be5ca5dc5dbb6f5b41d98f1ce07dd0ef405072781d8aecd5967e709b5fec8770dce6e75699b62c7faeb66a3e2f958a5e2e71c53f659b45128a9399db6ac73b6257962fc60c130b52097cfd5a28e9942462496d400223d7ea9abc1c83240a39cf7851f440eb5c28a6439d56b2764251c702cec596403d288c3fa3962a9953ec5669cf47230d68af0ab2d811a2c621fafc8f0c126704c75bd7d55c20e495aa8c01005d646eec3113fe54de9cfb668af1f926118387a35e8d8a4c75610cdfb48278e92cdc95a0f9414109bedd36fd5a33a0bff2861136f6f54dd9879dec838a8d59586010dc6316c1444737dc5dda57a304abf8ca526c26ce214413d68de6889f9b438b53607a5143a1902c3a8b14ae447aa5c702d9230db6647ebc50f58462fd02e738e2eba24168766227b6734ec1ea5705c622eda5de97948230c16b2522680d0f072b75d9ba425056f9ae0e8883e39ae56edf2e123d3e7707f3faa6a79d2c0b3374500506772ca191b783960808b13be2405f9c2a2b5ca30f6d54ca1558b6f775c29e8518f43c1cc5f98a214993f4bf7ec4e87810335c842a6a3ce75f7c8b912d9ab874c7e469b50406250d020fc8817c67023c00cf75985f74ce5bff53400c0c8d0451f8b45d91fb1a9d6ecea11452805a34a4c1acef9059ae3346d0f7921140fc2a5e51f0347c08cbe196752a0d5419cf3d6021a8f07bd391582179f1f6d768370312bbd260c8c4c4f6c7b31df4cc6134e27f703247577e92b66623acbf965aeaafd35afa6bcfa3bfae786e8973ad4afa5b7542c311125044e930801e2848071161b89ea881258f28af4b4477c9347c93c041c88d46cee5f66adb2170428a77d40875682abfad54795b73851fe1a9de24cec7d4392a9e474dbb9585666634f84a7a5d4a4936e3b6f27a96071f77abf51dc5493f52a5886e61558bff885e0675d85a0bbcaa3512d090d2b13e34166aa807a1b34e494971757466f190da58092c2c10c0f60e6c6c0283d95b0d2dbc6da5a81988d8db8088fd0b60d17e9afbe3d89ce6d11b66fd2ddb82c2b59a8ed0442a702f4a527d757f088ab263c689c62f52d6040333a12a0c5d609db20f60d4bca8fb10ac9460840c58d5959d797e6c0389cd52da95833af01b255269b357a930ac9a3cf43ba1086c2865a30a2b205f480e90a3efe9d23b5b03dfe7f163a3055b60e34a512ce9b17f5a5e8be1987b33a3377311c9edd581a9ae0ec42fb88865815710fe01e2aa54f08880be5c9aa17cec4137e1fb0a7442e004ad3f1aed4981d30ba80474ce80b92106d6ebfba89207692a7c657e3f3a7924c6b056a4440c58b48aae3ca31dc1767c08c1dc88ea5e7b63fe070818bfa8ec3b15b7588780d14d990c105b25cd00984316117fc709f375db308d2c6d223a05b5e2539e19b1d6acd4afe3c38af66e333688d23a8842acf43b3d3363305c8121501f37863705bd3056c7f20a7aa672377e5578ecd8fbc093fccaa64aba72a2f1500e4ad704340ea9d8460e5ceb27c256f9cfe593d06d14118e861b7edf31e86ee81188abd30b7b621acdc31849482b6a80637cc740743196e632b86cfe725d45a28c7230a7e1be3b816e7e37b0cc9b80311c4d6768b3e23808eb728396d1448f84a5cb92a491e2157dc4c7382bff6e7fd255c99b268165b7417bcfa0538b70646f84520431ba8ec8ed9d4380fc67ebe794b3f12367a51d8288258df17852988c2c2e9388cdacc0c395d29c77b8a700d0235000b62dbb1a33bff006f9d7ce086d64c51f2e3a77a20cfd3259c1cd5183344f1f77f4c68378be115ab2b50968460f8754de5b10e9b7835e296f298ea87888f7665564ca5880958ab56cb20fdf697fec7567508d9d3fba6c11ed52d151653c57a705c4a291fe5bbd4b198fda3c97d9d1628a8352840326386d46bf46708cf99ed6b3453231d991bd20a9ccf71cde40fae100f498842099e52286bdbce43d222142b72abeb9c77d85c19f753642604a8ee1a546c25e7d2cbad0137f08cb0ac8b3dc9fbbe7d641994244d30babad0995e2a5a340c40b0f698285aa30501a264ee063b44dd4a5e64a31bd307270c21c1405e62829614d9cc9da4e59e9fa939c2be8f7127514ca4c1170801279f4c6f5b4a9c1fb9e45ecbf50120c188d3824927e7932ece28f94cd64d62e619849d1be5f799fdbc566ed63106c29ef42e4c966aafb60e12ed2d0efa6aea0325dfeec4bca7f13ee932858c770296ff1658d9554a01bd6d11febdd2341683074cd8d1ac6e4af59950a525263b2a0bd7f8c8c12e4ff958df651a51c6f22cc893c47506abc5398a15f269086b654330a0259e28594215840bfd1633e26e8473d190839130210cd53a29ff71ea0bcab25776b2029c71a48d012316abfe08df9f79d115bbed8ae6825a38330b21f1d12b0d119cd25b09f1d29d5ae1b27a860784990fcdadc35c84307a578121414e671ca722914e3b1b145535242af283d834a252d94117f9e7305edc2ba543145d5f3c2e4a29b23f1458ec3da4363b616c405fbf7d2a84cd72349364f02f45a75cc50ec88a84495b5d12c60b83728f24e0e4f1dfc69ed257a04a1c19e172452c6de0245bde24d143c688706cf06cd1a261c5c81799773d126a5c9ca7d0157c2f2f0a9d0b9e89160f6c24a5d1b732241b8bc44c88bd2c2a8c2310d916c26f791fe18c21e1e24bebd375a6e86c632697f5d43c2f1c6742ca38f023a1a288d207eb3c934f61543b655136eed6d70bc3f1a60b60ccadc5f1c4dce9184215c473e4ae95d4b6b77f09ffad731ca0c7d45c5fbaf81c5893109599d6471593cc94bdf1ea799d00104b676622641651d226bbe1c1d699b76733d5103d247b0e82b4e79af67d0bc1b3d4ba3678af6bef9d9087d0c0702f86c49c73800be29caae30198fd33c888c99bd491b780db254505e87904041f6bf8df3402a0691bc2d18348f82d47d6861a7c7af841674234f6ca865e1e1a81291fc4b7105a50dbdde08ff5aa2134e9ee8c47899f62001eb96516d8a3b106dd8ebea36db7ce86dfb15b615052c78138e53ea2722f2cc3acff654b93fa50a87d8085e11bdafed0a9aff0fbe7fbdc9dff253e1a2fdf9e2700d12b1acdfcd78a9a487a3da5ebc89e255cd5100a4092b1d14579fe51b28e21975f483a650b17bb52ab3c66fbab756b6d7265aa9a43b04c125b28fa83416a3ca7e6c30f4a542a0e35aead72379c01f734195fcffd294fafe46a0b5950769bdba870ad46cd53bf47b5474dd4ab94684e05809e63c37a6b9e3d7ff51f24f602bc7c8948eac5e1acb66cf7809e648082d95f9be515ffc591712887ad4f1aea66d91f349a863dcec6a5e18743d75890df7ad9d1e88d8d55c77a0b01ab3aa31a73ab3abf9b50b6073e77fd7efa39b2ce69346fb488f7a9be0b45cd4fde609207fd36d43861475583e9a993da981ffc40551169492a922d9c434cf3f19e12deaf75bcaa9c1ff1a2024414e9a580d6e9e6d803cb12d84fea1d30785869a138df39d4de651d1a1c2eedcd00146f2f281baa1269ffe324f8d99fe206e8402e83189354ffa0943d694c7072df4732e7956df1a6a0e774175173c0f5d4b037cc354cae4639d869a4e9f83f64972b10d36205850055f1ada83db6eb75ecea135fe8402058546e1375ea427803f2a69cf6102c51944295a34887230167fe91ede072bf34a4c58754c9049eb1887091fb3697e24bd49dd8d48582358dbfc08dd681990f19f8e0aaaaff972630a2c13883f5f2bc1a7a460e3cd2893fb18aa14628302a5e24ded77b1ed0c3bbc19ab95f4c3e67fe87344c29be6877873b77f8d00a1aa804470190e77627a7fe9615e81b06299c297b7ae30aefb1f6f80984062924a0a88dbfcf33516c108102ce7d446634a7ae3ae681daf10bd10d4e3c54f1fbb4cdb218905206f6033f5457e113598fa50f78110be1a5c8a5d3244f46b2bc614e5ddb8e7808a430312e8e6a69f6ebd288ee9afc0b9cd8fdfbad7adcecea1b118b80db11700189868c02a89ed625f643c514911ab378221639655f0c38e43a014b66703251c8742159f2cc5b7d98a851b0f68e1a5e19007c7b0c0784657e27e104f471670876dd9f8ff0277812aa1586f41bbc61b482ffdecf18696d1d620bf7694443cd36eadd3655596781874f2ed82848d5a1e8748efffb267ecd7a496b8bb1e517c5d65f4e47ab38c1c7bf638a1f0c2713ee3ad8c21d4cab94d337580727d8278fb8070c96fe5049ae07590aaed1f99cb0e4e6dfc23eee8e01958eac7394a46ea95be20154dc52c293426c2676d5c5fb1c697213be20cfe1837eb51f760bf3abe61caa02b2516b163fd013cb5672ad5496e2273753131514c5b6f3ea5842d91727b5ffda81fa8c0163c2b3c65920b0930e43df54f62c83b68d40e72e362488536acbeb05ba0de5e04ff6b1e673e29aa418b9d3bd92728d2948bc12c8d91b0e27c2122cfd5891c1143c7b88899bbe13822c3ba51a71416702fa55ab5f9a92091a81ead5e0c1da5c502dde5f4a860fe1bbbbd0108e13be8af8f12b5ec618e53cea4fc9dd35516964e4708d46b8d356b812d80a17b679479653f557a52cc7bb7180e83c97ce884b08ac0daef08784c1bcdf5a025d41964c89ecd912e3e38dfdcb13d25974e13ee86e25cae0a7bf1f788fc2661795eadffa9a023aada5466024bf0e1e8850f6ceb55820f4c4593e043eaa32fc107314276c99cc9c25f13f07ab2c978675b8a9d5c0305adedec3ceb93b2ac8471a04de1cbc9e27ab72d0f039f7290ef12bc9eec5be95659d9f7de5f6c4526937cc599388a8ab1d881378655ce3de14a1e911f4708e014ed87e06118138968999a3df8f50a69ac7fd01921fbaa0aed698ca6cb17520a01e2c040d9f78a65e327b1a2913217b3c9aa0d24084c10b706a99b61cfb8d11040e3a52ce92d0c4307be903c839e28f06099e0db142430ec1a6b1d4480e3c60ee1130b12da2f310910832483258d9b1cbab21e3e546105a41b8d639274a0311307da5a060e62140329fb8f82fca2d5fb16cafcf71ab3a38eef2efead5818318069da224bac1e58a451b981a4391c92cb9136b44c5b61170cf70ac7702d6c0edcf3598434ba5df8e843938c78020db17bb398c864b8a24f5b0f06ab9aa247b87537244073a889b992d50844afafd25d957404a427dbcb1c8f95c36b5d598101f0667daaeed571a05dd4f1be0a3fc6448b3f3139bdde38aed5a0d3c55f31ba0d4cd68da4d2775b84d21dd50252884640eff34ec440f2d3a0b2c6a0d2adcf9aa3f5a27d05879e660ff8ea7836882f2a6826fa39781f0199090f844ab6e28bac6d085d8c46ad78e2168d426fbc645d1a7bd5b106cee22231813d2cd9391924711697e87e66552545b4ff7b48a58d4011e00158b877808d5c8c5c48a7d57f295e1c7daaa61324287364b16b72a89ffee12094576c4c251da6836a4d2d49879df78ca0a1d67c4945091b94723b7ad7501b28e2e456a7ce57a0b30b070458dfb4023f8b48c4d00c3622c224ba7e3f0cc787c2db545dcca1cea7ba1664514df77ca2b89bcc79d1ab5c90852b4059e453c800ebfd822c8ced8581d54359ac364bacc0c2f42ba8bafd822c70238a11beb21d1607b4646190232b1d75b32ec8a2eabdf2d808f9d3aaff1764d16fe4ec9a1f8d2cf41459377e2c3aa8e4af8a0559fa88e4475934bd2fe6b12d1d66400d63cf28e53f65cbb09f21835d969faa161beb9448113a0b3c0ab2386f73603ece29c1bb5e90208b96da06a1b82c86090047eb2777bb90a069bfc831f1f7d33bc31c464d3168c98cc766581ba478565dc40407c6c69aca75878ed9e4457acbec2a29fb76a9ca67037095338d05a993513732677a7ac94b49f3d1e7b66f643482f371a5792a89b1e8c4e9b1519fa22c007e04c3843e4d1019ccadd2cdd2aae2a712b4b4ce4c16a4cb186c8af14e5cc4c61296a27e57135e18d1e3d1f52ccc553586e9b7eea47ebf907ee109829591f907757b017af04a3d76017c2f143ab4a5d7fa014b16632d591ed25b396075b4885ceb250c2093e629f524a15aae8ec07036e442a8da618ca296d2ed756df028ecb91362091076b541d202a0e0901b13017694ab46849901f1a1a55b7b3c1c581847301f8fba0dce5741a44cdc5a1360fd4459f1d84679bff6facbf5d1476ba79f7bcba9f20b358d1f2fd9e56f68bade9912702baa2c25d76b6421cbee54098ae3165cba833c7893b18682bc86276dd41a8a6af0f44da3404aebde47eedd4f7217daba5006c65eab7294a6f0a9bd3359cb37e13b9c2144a2c1dc69ae500d9218a9780625239213793429d9d9e212cf78ea6e4ea1d7e074c6c54a105bc944b89a5c426bf416b09d9e5604a73ca713ce2e597869a544251c9cc5f893ac0a44af0653b624925d8e7a570d63fd9d615c7dab0d09ffc6ee94bfaa582bee6f28052b65a0ca04dd39f57656e9253db6b2adb9e88f8303a486e58072d3b3680c9f1c9ca49840dcb2bcec125fee79f4347a34be87e084937758e1905521d53c65c422336ad8ae3be52832d9106a550df6a1eeab2a6569af1e85910a49e46b14ce91f2c16cea0c6b12e85189a15bcc48f8bb4223cfd4013f726c197f39d41edb8818387aade46c116236256f01a2b688f64951f0dd2e6c0f47d21b52928e07435a9e32907321bfbc587324288e33888fe0c673a2f246f051a458e547cfff81be7a573180f5ddd21a11da58ce4cc26cb1048468204fa918d07307be7301e3e6f1ccb5facfe2e1aa86af8323f01c06d53e415ab49af2c4e2ea466767a4ef98c901648520d2f43ad68e55361195278aaccaefd831b56be89b280cd27665e35f7b818b021c0d5ca2f873a5262fd5d956635cebf0b2852371cfe2b2e5f52b182041f5b54d628292850c05090f2bce01cb3fbc2cd330fc2ebadf8e7ded9b086bf09e7e83c0d4e5b98f886d84a3320de4646ffd85df09c00fc33404781289ae49e95ce66adc0343240ac15512b3219c6a6a93394a505fde03a0b400b18cabca06fc238658083996d6baee496c9ca063b6c477cb839dcb0ddb91920a0918db2eab574f237a6037e73ebd196356ad7634f137473bf60eb76c54030bd085445c5345ef4ec6522bb5ee944e7fc63a9c4916275435bdad01b479435e2da0ab42f744338136fdfa7659cbe386c98c3f9e0ee00b199b5262442343282ef767c2c0aed1167e21f2c456e74de1f9adfba9b751486764a39872263afc56378a1e98c1da168022c2308a49a27cf738d71b703eabae5944509250f441692cf41577216c4f8fae626499721d2e69a225cdc41778ce0ed85118b07f6bc4ec52bb84747a542f3e25634e7e855e09d6404aef0ab4ed58c0b2335754c075126e299a18995ac4321a343fabb2201a8857d54c731630612ae2a1f5752b57cdf000d4726e2b6a009fe3fff71ea86f5b40855d14febb52e0cd6ea070d181a2fe43c65beb98cbfb5a08619540e54c04f204edbe1fa5eb8b04cd07ab37fd6c53bcbad7d8c3c83be0d4486d657a506d3157eebf02febce9c84337d924895920a528e625ed705e6d171eb208b0c8048842614aefbad39e9f54dc01692f370d509740e8aa94384335109ca17ebb6f12a7c9906fdb581f169184111afd7245c761d2c8cad1a0f311b5561b2bd109aec9fd32b6780fc2556ad5671e43eec641a04a802d3d08ac695fe0bd1c1d45ac1078faef420880195c334644d6082615d32aa31a17bdc67565b5ed2035426e68dfd708f664a04509a475aa5b2fc42d7d10ad45a718713a38fdd70f546cb9d45b23aa3aba1edddec8d8422fb2beab124eeb934d03a1184dfc5e2ff24ca7f22f1f9c05f0ed05f0e1c17bf2c0fbf382fdf9a0bd79e1f41e911764ce9960786ab36948eb0f13f93093eefe1d5b7a9c516772960b8877bc7194f1a23d8339d4a1ef09d0eef9473a004adad92af35cda979c5f8cf88662b22d8666e77071010c60832a1b2a30b12e09573af4d88657f0076660f6d9e1766821fc5033cbf046aaf80ca0fd1ab364b9b728403422db12336bcac7d245322c2fa1c2fc7212fbe61348b8b7b9a666856010c91879e619c893d133d81988075d401f6b4c64fd89b262e2ad74d185666b4dc0fa136415132f891b2a333542183edb03b493095e106dfc324b68b5305f9138a58fcb83435faffb33efd5b3fb003114c0dc3f50ba80b1fa76bb8c286cfc7623ba646853649027999e5824c6e0132421f07940d0ffd96735f3dca09dc828397e94007e9c143b4a041b4d8a8d2781e140b2ba31f45961c2d69bb815f980a27f8de08419ce244502d43cbab684c09e970ef9d73f994c9293c0d5281db9412c45225bfe0a7e3659a59d0b04852a7239f4fe9164c0b91208cfa164a973722b118b8a343c11737a163e9766220c42d73aef20428f64316c3833463da733be803cc71d06e6b74b11e6a6337adf15c3d33cc138ec1c498e4a2fc864d2508a7d794d253b577316c472df73904dd478567b19c7a7468272c60878006b3705d594837821219f69ee28914b1414cc9456548085777751e41b127c8ceac98ef94e1b32be97fa8e919512eeeff8c2cd86eed6bfb386ebbf8a45570ec828d17236d129923815791a8822890fd2ae883135328fb269b05ebd3847799b5dc8580706d147f0ef51955ed0c2cc7d48e9280675d795711e9a33a58f7445124d5ab9441f83ca1b253c1b2f507172eb5c64fdcfdd2ba481e67bcbd07f67f12af6a036e9bc26c73095e03a1273bffd8afa559001b13f696cd914b40cfcc0800409c08d5ae4c31c2a61c549bebfd736f74edf419a52d619b5a36729941c09b2d8641c4aa5d6b5c04d7928052bfa37e1b94b38b8a2d999389311d0cbcc69d09ba5b3254bfa2842b37acce98e5510c705103234d4755ac81e150c96cbddd0bea8aa979f10ad4b94966c48f9781dfc63c913c2d85836b3b4ebb01a9720b5ff1c62abf1b6e61cf96dfdca8443cc8e6e78f9f3b013102e721d585f01f812d24efdba5713fafa806345727fbe1863ad8044161f61e5b1ce5f101d8abf95b92fd6b3b1ce3a1800665e5e90cf27f820c7e5b25d1892d24f7695bfc4ca2571574c09a05f4068660e2897c4a3289533ded5231120130490c497836a1e684ff5ae842e7eb0a93f24d2171840db657febb9780e2037dc659f537129700dc3eb23d5802202bd6b7a73140c0a07fa9fc0ed31f0dda79a2766a3a9d48db4f60eca92f89f1b74ba3a3d58f42976709234f88df7d4640a7ecf2f4ada76b6096d4ed12ddf806df6c97b7ccadfe86778048ef1e4f888698921b139e63f43b66131b925508832d755ba89f7c139bb543199263983c990d400a44a0b909e86fefaf4f7e7ad55996d8b8da0dbc54424f04a079d0a478e4d7a19ceda63eaeac45c1c6a8425794bca5a012501888aae746b7ce0a1697ad2cb4d89303d21c65c5ba15f3f694382aff606e41f97e46b6f0010c802c93901e9dc3ce24a5faebe376d755d1c5b23cfd827dd30b1cf6ff595adb828469e04499004eb7f98a887533abd0cfe2bebab7e9357428aa7ea9dc5efa713403f6860a9e2e03f0cd96d545c409d275facf74e70eeb3081513dadf442d3cb142c25fb9d8e328c31ac4bd9029e853059af85c08d9738e59979302b46767598f58f13a55dbab8aa2330ec8627a2bcaa9ab169b634aaf656f017ae08d059ffe9e4bd79d717db5743b64ede0da329d2a9d99fcbd5ea3354aa4377ae0abb5bf9b15287caa37275131e69bfce3ff429756911867010ae918e3a5948fa49fc8aa8b6b60f60df456aaa4c168df75e390e680b885aa6903b731dd0b73dc293dc35de2ee403c91160a904d60708c6be0f98cf1fbe0a7cbc52011f6f169b18c4ec2a37b85ef33c01572f0a7f1f5211952e027546e53d5d16ab5112e2401c9a9cbb3e214e7ca6b4008d2f2b7d7179b3d2a32c360322328dd8e81f99048afca1443e76ba4eba5c3c59880d3d9b7db0a65b634701e5ad42cd3d4cfbf80cbf20593dc8b13494afab63f401d32a1821967310892d6f71949ef0a1fcb5cba190453acd93ebc35ba6eb774c551b56d144fdf72c28287b09f460a8530c0632495136129e4b13a749fc2668cb08d6a2dd89579e6ede8828a26666e66633f4ee3d46a6ffc7ac6660620f0072e19a6e66babf594e8f4cfc0bb5b0fd5a6cf8e7fcdc5b9de6697ee5fc510edc804acf530ec0dc3540ab6530fd1e717532be4d17a922109578b39a31ba0126b8e364b9ec621d8b327a395ebb7f7d3e78ced2fa3da460ef928cf5331a08644a156c5e62a356b2630920c69bad52ab23a96972cd2cab9b20311b77426acfc4a3fc8349e295acdd1ed50fb917148ca26f2de6772fdfe8973bde0ba94266513a208b284c34ecf7fd080fc9643079992c59f624820328875452fa0190e821df7a7f992f9103fa1ed6d0aea49db541e433f60b3ce882faf3a9446bfa8b085db4b08db0a014913debdaf35256fb79cc122874b0d1aebb62e2dfbd48a02c95cd58409cd7e7ffdd4e1493b0adc2d2205886eecfa3fe760c880760e649d081df44c4ba95a9553eb25ef067d882017f6226a5b51a5415e64b6d5779360b941a0bb1b1d24a5da365d3a004f0f1b3f7f06abd48151f830ea8e1d6026c9c26a1d641ade089d90bda2b3b59e4590fe0e8a23594d5f806339f8469c0b6b05b4c90968730fcbcab06152882be2118701217204e3259000718c0505ac0c2bade30dec65cf227217934c6e07ddc07f808858018e1e781e2555cd8c9939bdaf18087452feff89c512c45f6cf6ad89b352cc3bca71071bdf2a1e4e1d5254baf72336f53d7b62a92e4b80db052ae3613930dd4d278dfbde813233990f67f2a70a99f02827a85bd9b9f0d71b1b252effccdcd455708fc56d6f1fd04c0e698a969f5894f5c02d1eb617bb76773b91a29cb3a75c81dce83041f7300f11c3b3879957335e47e535a062ec187e5bd30f8f60f592d43a51dcbf487cc81c16ff7388de1813dcf2686342c32c4f1f7d79ae2f66d511b0c9655a97dcd2d73cbf49203d6c387ba89967839e2cd19764a586f5271590c0865c28b4d569d23449f75371e5bef727d613edde17b6264139009560d0e27e272d231a89fe91b5a1340dc99e671c42530b1182fb18ade5af61e965a32d36f4be17d989d14e71006d176c30c11a3d84a80bbea3fda6105a9070b598aeebe14df35e45317b641f0a7c94e7d4b454615a5b6985a6603635548ec7ec3e556113f91744835a5e2e32954e787f046c125a10c60a28ef734398a3a342923035b55f3a123b82a3d4e6dac42eb715013e0f59e5415b7f62f6c7d98bce628a99b59a3a2a04b915f60cdb5c00caf94d314d9fb14585137cc80e7409d59c109b1d7376e0ad6a302b5ec8004ae10c0bc809a544e4d15f08fb843bfb0c365a0ffb643c8e0863e537fbf738a56c24e8aaf431ee415b94994b1da377cc16bd2ed5f27f4744fcf883c31fdf9a5c515499417940cb6b48174336f28d326d17f7defe13f448d3ea080e3ae4315bf37dc7180e9b1be3a9fd87d7deb37ea95545462a84e5882e02c64acbfdf031c95c7be497b1345e55b41d095c16bd310659d8ea85cf8bb554041aaadec6c1da0b9ad2430957f2f178e61596beb3016128302a1cdbe562f50f70b144e87927dcae7d1aa654e943552eb560d0103d4a317446051ebd95b9e0f25b63ac4b7f08e7e575076b3ab2cc9a6a82d90da484843e04b63c605bc389d318d7cd6c9ea500dab4b4501284bcd3bfd4cd4dae4935fba56556cc8058c71d19900178d8192102772e941941bfcd9e34d65d38e0d885f890e3c1954266652bd62dc5e962acd5c77d6ff18906d93977f0a344ba0c10cad00a750e4f06d70ad8953d05b44418b4641def41a6102eea02e04b2d1007d7ac5d7fcfbfccdbe0c5988c05d096660aa2e1110f77e0217827848af8b1f92ae0c0d29956642fdf55444901ccf7c0e8d7412b4022927f5590e9bf6c3e96a3fb077f38801d44c6cbb778b978b90c15ae7c4799f58ad2b413227faeee704ff06bd0afe6028f755f66f43972ffaf2c184cc4288240ac8116258691f2e133acf7dfa1200392df593b8a433ec1636a24ce0830d951306f8ece32ea1270850a3a6fbdd6668d1734a71b071d7f155cfb3d59d53b26ab2013b3b6f7cf927ea7ac22ed66a5df0da32424383d728598e8b950d06cde0536f4dc4711763baa72d41e65dd5e5b7dbbe04af03af225382b0c88408b1303fa42fc0c768d7c89c045bc353b35bd70a72d8bdaba1c1eaab105e7547fa896403e4e64d0abe32b141285d2aeaf196bfeb872e4c82c1edf709b3ad2870519a49c6fdbca69fd020d373d8bc5f259b46bf61132bfd2f5ef42bf6062cdf1100d13137508cad9249d8c20ba32e17d68808bf0c241abd09872c9e9e2740986af9ecc6087fda780078e0a45fda0f9229118e40848e3df6063632ebc2df68c7093ec5e14d3eab8d8c963ad159c99582bd05de379adece06329e0c5e22761b36bb961063a9cc42d4905cd61829c25d5903a0439d9678607b1f069e87345b74a16fc36494b499fecc9fa15f5d52c89ca6526f34de7ae01a9c0f7747c812d913ef349fae51fb586c841d1d09f0b9f6df57ebe575c51e10b421aa33993dc21866d786d08efb3e9a9331629039b95045316d82eb2686754446f378bcee305001521717e3596268d3a49264d586eb9e5be056d14dad900ebef7d5c39fb421f12e19e5ce7ed7fbe33a67631c7f3de0620be4eada8b1fbf536f43458d95d55b8daa5d47f4815cf59c26430c6aab73ee93c88ec01587915c3ac3c2d31a69ea5a544ecde3eee3ad7435f878588cef36b945dae7d8544da726d2cab2c54e3db403796630be0ac6ae9806638b6a355235776af17d5108e072307ca65d3e945d3e99ab5802661e776d7fa1bbdbd2afbfa1b19817c1372f818e8efbe82d6fa60408254e30690a21ff1880329d9b7060d7122a5960e7629de356eaa355d8d205f52c7bfb4bee830642302e4b396913b90a5587c3fc88b37947acbed991ecf74144e985a7d18ec9ea8fd6780425507e349bcb774c29f476052e6b941cb8981c68cc4946333b7ac7b841b15dab301cac3860c29c01e8ab4558f0f0c97203161216af9d3ef1e5d413e7dc0cb0ba948e176521cde6b9f9e854ca1063e24746be5b8810abda0f248f3532c98d80810cbbec31adfdf83f4145c99ef5a2d6283354f0081464a20c14b09b37f2ff96a796ed4d329c9d6cf9867ff1ce949670f20292d5ee81c7371c46674ee44bc13e84d2180858b319fc54e8af3d6e5a31031bd4f1f6ece52fccd0c1ee303815d727131b499fdacca6ea4a12009d00fd4b4e786718a45fae6d40b6a820b05da137d2784eec42da732582ac79506eb714e5f918289c1c9bcaa5d3ff02808fcbefbe772bc66ec00e8bb6e77f0de8545370fe24fcf47c1ac5072c2f931594114727b2cf270667d1ac13850c7a663188218513809580cf34711c4467791d641c22dd43db92b6c9abb78a5f1fe4df61ddde26c9576bf6d93f89214b7a919af4ecbc70bae83171aacb5d18624587146e609473acf24e0908a492031e3894245e7c089d7376e9d4f26e4d8ce322b5d6cc128b8af145b2e4318dab5e32e076230bac49d7d43fefda5f4bc1ffd5d3c7865f292a5ae758c1d9e979a3eea220c4b7bacb59a9e8d773cd3f9a4ee580cafbfa66ce9abdd08f005b23fdb826ec82335434d9fec9abf71f3f810698223e9ee1667ccb00a88d5ca2f4ef5a7a9b5000df0ee9dc36bbe1a9d297df5b9a7cd9798e4773e78515baafee0d9a32953f3a7c63a962518fa77601760766b2dfae2338b857b5f3670dc0d50beb1b2a1a4e212d02c7efc65eb21afc70b306d1e2169314b86795201be2b371b69cbd05b8e05000eeb742c421dd510ae4180644a600eecebcfc5a518b0b9ebbf61dafa50253431a8ec1bd0861376088d6e629761b7ee6f06c455804f6aa83e491991e5a052cf3e194fdce816b3b879c9f28b12d78924ffa67eb9f8fb5825e50343c80e6822a20dfefac3df568e4f5cc6b4451cba49c2b655e89e49acddb33ae672a50b4a2a305d4cfe8d22fde707436c18e690580ea852a2e07dc5f010ff4552ab90da9aba086ce551172bc889a536b9446c4296b5464144abbc1017eac3c37eba8225a3105e2e7ad8f28ecb85b403b1431ce88b715214dd2307f96004428e910f631e55305d312072fe54dfea44b87cd0f3314910fc46b0c55d6779d1dc8fee9c8dd26dc506427919122f24fa99be1ee9e07537a4b0b2589e356d263a7f124280bcc7fdb450a32cb1b2682131bd7b1de49609b155ba836fbf54c06350533af19dc1886b198c00c153cf138ceeb79dec9be8afaf8056e1f60a0f605de261b823e6361f9a4bbc294f7207c3c402f2699f39904b30ddeb3c676cf2b9b0a590a6da9fe9babc9db585141ee52f20cdf5536eb6b5681637e2a6d1a71001596113066bf9d9d02013a7eb86c027cd0ab5cf33de992fd32b397dff9f7a6f89fe15d3a27e65cb8e6f8b7615f73ac8eb051aed96f0b2b98012263385d7617c003749996a25d6cb297e16c36330c820c80d1fa8a2ea7061c30a10251fb0c14349a31b22ca1d0c2a9d4d8ffa7b1054c91b621ccc3db98a3d376ace70abec2472fb6170efbf98d18293a7d83c3514d0d691b135cf50b4799d6cf05aebc9dc741e794251b7d955f0137406f80f4421ab81e3892af4054d1b08e821444f4884ce89fc08906110f29484bf0a87ee3ac2ca5d9a2de241ba11a8abed7f8de1aa204ed07320705bc58594195ec8c60cfc50ecaefec9357c1107f290b8bceae4a579efb4dbf7f8c6d68d75c645c8a75be3912f4ea667b2a9cce0a981d6ee945ee4be415e3abb774e026633203fc578d819a46b419dc5f28d60baa0c5c8839bd399c12dea78fe04958d6bdc6f42b0156a078f6ceaf532702e9b08546beee09e67be1c5cb0c6d65b3cfeeef1c9368866dc567c7e07cfb78d567d86e4420eb1125bb1c20f60eda506030331a7982563a4524fe7127803ee01d7a05888531ec29c6bc25bed12efac639d80820401def6401969df998ab140c5c60be89b5e109123bfacd6ba6337708e4cd1b892b7e1d1e0af347d6bf2926b1668e4b247ed86d0aee5a22ab61f276e53d8feb60e2a83debc39216a5274cbf30f41fb5dac6eb6432d7f70da428a8d100bd66e7005bb514c1ddaadf6ac52bb890e1858e4f62d731796087c3ce78e89c831de76ffa05340a2fdd4b47648ea1022c11f87a438c872ab79b976f3c3514526487e26039d0dd9ee1ac252a2a5051ab2b8f3cbf5168314b1e8672a3d2667cf6b1632d3488e2ea4b0bcafe99bb881ddb9aa28587a3fa130bb21e790e6a51ef4683cc51f80006b573bc2bfbdf2a29dca8c76c6555bf70f2c0015d0bd7ed7d10c5d3fa91ced6aaf76eacf7627bc789040b288032422fde09981316d864186c04cd7a4ef815d8e52568467e67a938b76e4920d5a6ab3d6249245642171e30f9e875979a69d95d5d79d1cdeac825123d403d5cb76590f9ff0c4932dfdb74b4b15ea652edecedafac45deb5766a6711976cf17235e537b8bcb92f00b4684d6785e841fe6ee6b455171d3ec88c9b07c22e84a147e04d526e4c69931f4063c3c84b35bbae99ebddca62d342633eee7e5c5163661c0e2c8aaaced6f64a6dd0ae2b9d9d7623a2579e07f4a433db7b4730bdc156e1cbdf98e0ab32924a3065e71e20582129652cf0b504ef65370d6df2b983397a915aada5f5f7c1db84402efee07143ecbe176d2a7a9150eb152510b9494ceb4cce0de0a010c7ad23979f749a79ce4e9b6a649e2ad93f39bacbd2c68bdac33cabe96dca403b672cf19aae8bcd554c39dd4caa786e31a93f06043666f1bb96a3a669aa337f7bbfc6d35a9f52e51186391c3fc76c0f9eb06868213a2e1a2c76a3d421f3e1f6f9e280a271b3637bd30a13e49cdda2b3853c510983eab1e99421088f03cdd7d15b485bd33708e8d711e36ed799e9645f8f8c0a45ceea83ccef3aa1e2fac8b34b38a54e0534d41462754beb291b521c5bebc8ea85974ad8fb760ff53597d4b47bbae32cf9267c81d2ac8dd434fd95c79ab8bc9b2e426b25d333c89c4943323cd1a454af7ca6ab971d54f1894541457dee601a1e595fab33389d759791b6a96ee32db0144034c8c8cc043fcd5c84d0a4150d471662945ce7114b200aa324b04949c737ea6224108eb70baad2dedee250bf62b5cfb8dbf9943300afb50f5a0dfc97a483f555e8c6c268805922350fc9c831ecef796d50b60c3efddd2e411a081634630010581bf1b16906e13e040ccb162389fce8d02ac97477b1fcec306fd9aba054bfc720772d1b518a43773177dd605ac08732a591ebf20fbe01b4d489419490b77bde7bdf1a0458b372ec55b4639abd562ab15a83114d414952d9efe35b32b17e2a6f6ef4c5cada7ae424f1a528eaa948e90bf7e4fddf4b9149c29915586ce94be93f020a9c8c5d342b0231823af6e3b5be7cdd7c0dcc23a6075a2d8720b94230e0567b264a4a17ad63896c6f473cf009c8219622572bfb83ef2162941a53db87f8d51a986bfce95bc7e70c02d7904479dff38c0ff072a70eca4e49ba5a65d487aeda903ca17ea41c77ea0ea0a3d2209baf6581dd76b3c838ae43f78f758987b24a638589bec588f691d512d35a18b645b7a5750f141c2574d95ce44226a0a37b456e23753d43b808cb4199169a60e3b6cd1b6bb05bf93375dc8d6ba8d8cc28f9b2e68422784a6b120734c01cbc18dc9fc62b8565310a971d96ab1f0e98b78dd8f89552c320491a9b481e5ef2ee9769e5915bfe221690b9f71dc83985b8a370d10f8e7bc18c096a78fdf44204c7288313d5a9e2ac4eb3745c097e198a13249c30ba0934d3e5b7fa03976198b755f82073d63217a4d080335372ceff68ced64246bc614d024de28e23e2fba109fcbb34dfb208af1736c7d7b712dca551cd5aa88afdcec0ead1175e35083fb100405b43ffb3e124521698b2238f4cb2f29ccf3bbeb4a14d2e5585bdeac72410ec5ddd0f2419c45ad1f9bf17626de34a55bebe62df6610d85b9e55abe42c46a6c928c376235f3c59ef3dd5a8c97f107432d6971e5b3bfb6e496d1e46182a7b6eec0f8425078eee2c670a1df0430e7bceeedcd2927e22c8a70bb482f76e425a75f9539bdf89da8adc3299fd961071e1c90753ab35bda8cd6b27ac0c09a52aba7efe12025cce79391909b369b487bd0b2c3e068ba618031acfec21763be580ed0cbbf7a849a2fa61b4cd7bcb01ca8e9e9778e16c05512265c94a97dc7dc702bb1f8f2218d370e50a6b2b25a2056314282309b36f122e082e4cbee3b71951112217230e2b523657ac90cd1aa2bd1e60415cac18b23f446910d64807d0c31a6abd6407bf821dcd9d06bdd8b8272dd47a049bfd330794b186f80901b4e278d41710a30e524c1818d3cc10a1d1f307a6a2261b3a4162dd72183f249b40871e6fc6a7a07a8c6b4e2e2703d5a7b72bdb1922a89628f89c3c9e8c20d77b894dcbdef87d1bfa0259643c24c9079c27cd581ac9f38af3b6791b6155ab01e1d6a3aa124e835453e07cf7fc060e49f5f89dc9a950b36cb450eab97f0422b1f3dbeb6e5121b6cc0b9eb524c2211827093bac4ddf1287db43d1868266b4cef965812f1ba995ccaf021741bfe372f0b4d08ce5193d4c41ecd050b6efbbd10567c04e2e7ede29fef63a581600a089698ecdaad1faf6e6f97a3a1659a19531988efe93aef944e53adadb6667866b1598d824063c5f1a8e2b45323ab45218d521bb489184990226759dd92d222039c997201259291da83a4656db4cd852e38b0135d27d77eb1d67acefad76ef72855e7b2232069c8d95b831a73486a5c90de4b500747ad9a0bfa188c64e2a0bab34f325f7c911841a9f0d01bdd7637f1428673f16a6f885aa3e3130e197ccbe1bd1ac324cf9004904768a610990da19fb72083785f05491b0068734bf9469a0e95d0630b689138cf5b9079ac961058f21edbbd1b667b2f909597cb92d2c83e5f16737f7d5124ac468678ea387f054e4c05281b8a7f42ac59e73d5615639c0641eca1ced0fa7e23f3629a55b3720ed259010a0229d77adfddcc4eed355620234f75eaefcbf721c6069cd3273dd2abb705de4b4cc94f6a36669cf8971335c999990f2fa2badba91fec6d635568f98562d6caffc47b190d2d118a40556aff35306e0055a262291d7a33c14b9434856e5c812f4e065e895261dedaee0aba99d97fcb7523effe3298ae12bc5647f50e0930102e1661af3709b55c8cb7c3eeeca2bdc650e1363313f7246a47af3e54c737d9e343e0b18812fcde8d4018509744c10ab9c25426b8fb4ede8e0553903b1abecf1dd4af5ee9282e264673046ef306c8507bfb80a2ab8023ef479dcc2bdc61a4fc171cec3bb2031cf41337903d18951ad73af0c466f4b7b97060047aa626ba0df5ae037cf23fa32086a437fd0d20ffd64c44941cf5bc46695b82d70aaa468e74d0aacafbe5d53494c81a4bcaf5f2932694f398bd2618a445f1c24b38416960545684a83d3d41a731b0f94266aa88d5b214bfb7b25cec90e5a085ca8350f540439948355eac80917ce58fa4cf5b0b2342a31a87102e15e8f463af8645372d4611dddf2adbee1b10ba983ac8f206dc4f26a68feea0557ff281a024e244e1bdad89a02628f29f2ee536ddde30dcfb70781c706a4f71130ea7d11fcbdf332465b55a628b200480bee045f7dcc8a6b7e49f8f95d517e854c883d9c903f2ada8c9fa5e2bb8aacc39193cc1981e5b6a47f4583de2175d3b07ad2399853feda0c0e0a4d033c713bd35738a39a4db329368d0d9e7e6d473aa102241ed684795fa634d791058395937acc82371a23afb4f6f16b329bdd1182ea0308451e6451331dc19e907ae6b071fccba49a544c237cb5aae964a5aef317f01af86b3f8ff855f82d9027ea783117696da227da665b2d822467b79303dda38456860b0ed411516ea85bd8149048476d05226b458e0fff419210193fb2e71c31a3e72381819b8a2a414560ce75d8d14d4068ad316705c1c111cc8838a80c997a1d2c80b4c0ca0b370a9ee5b4b2caf9a6771cab747d2590dc9328118fbe9fd7dd499436c8675c1ca0d8da120d1c4859b78412e52270071c8d9f12e1014e56cad412307361bba42b70fc624b3a6928415bed432d0af50a95f6ec1e45821996fcaa158a004139c6f9f7de0e40503641da14352d6205e0c168a9ed249473314191dba4c914b64666088f0de14d37c22251c20a4274719b3ede3298d4e6aa379fc3129297c83ba52b38158fae052d2db0c0d951a677843c72bb6e5e85345a2822b316ee76f3dc1ff3310f3a1711febf5cae0f8fe827d6abbe69b69e7ef9587953395a9e95d0ee96e7eba04a4088fc5b57670006f8f58d051a4d954e5664ce646c9c5fb8bcf80299bf58f769d4d154f973a96b2b6a087cd93359d4de54aed72d9cc904753cbc234988430004f845f21dc1a03596183d8836227d2aec3c2e2256f1f16a8191e939937e5b8b18ec8a53898070c26913666aeda838b3fc263b0cac11c89905dde1eb585a6b5cda285d329df9b81fc3d55ee79a9675f90a0d768057c8ba8e8275d9fed2eb242851ad95ec31e27cdcf0e8129c6b449eaca55a3bc95fce32436657b8c8d93869baf85b572cba847a32f85d8aae036e838e97b59016a7252800e4e0a6bfda5cd4e2e55598403299650c349859daf0c28df140f09422ff24931f6c0648d7d648b6f121bb2a5681c9598f56f91fc3d130f4090b153a1f1d2e63e9d3ee37b6bce2ae5752a6af5c2271b76dc4833efad11a4fc19e24ccaca6a540d5da4987d21570f8c8209efa6806155ac91de086f2ac55a0a6e5c358966be0a9a00774e6872a9d282fd6008b18856474700210f20c3947cd771eeadf2ca4ef13a140b43fe3a783fe432dbdde68092fef56d45b6d0f84a071c71c7bc65b48a617bf8a1d5f3138acc16263c26ed3460d88febba689c177a49a3fde685c74a47efb2934be6bdaaf0b18b89fdd5c34b65144e140a0fee77bce28fce6e870317436a5488a69ffebd4e48f3d4a0f27ba1c6d82badb3edcfed4951a8363b6e816d532f922b26c3013baf124fe089451f2536466d63e00c8be408e9925413e3331dfece5fd3ea66c7af6ab0b4cb91a2571eb55ae64400bca7f066fe526b599539d6d1a9ce3a22c3e3c535786a4ab4da616ba766a76b01693536a7f6bef4ebb465e0252f01e0c2af6c26b3c275972479823b2325860ffea4fdcaa9725eec36c93ce64b572f9d5cd3ff9fd22d9284a3d2c9a07ad164ce4df438fa403ecf11ca0f75f791343dc52d3b0dcf791fc23ec18b80e23e0fc1862ad695bbba5162366e1e4433432d84154529144dc42c80385a277098552800615a697407d622c91633d18c62cae045b0408c377e44667f8d8f8ebd63e07f18dc628eeee2e8205713297f06db472bf19c6629344cf18d80c7de73095822060b9947688960e10718a782bced301e91d031ed944b698cea3ee8be83b555489100b26dfb34e7282a99ea04a88d2da898d389ca622a6129492aba318d8ecd3a9e335de1d2b4949d637c2b3d6b8533b9567047407b7150acab47aa79bb412f8a173c8a3891f63759308b41f0bd2ac47897f3e422ceee39156bf0665074d9103d106e41fd534869e830a6800b85a41c745cca3da607071f0fe20796d10f310f49d410aeca7c2ecd36028ab2524aa0d665bf29c3a9df6414fd2652806b0d0b5455070adf01e286871e041aa9620cbddc6494dcb6e3d0148d32347442717e06cb366062f3511f43458c2f88e3c0984bc02b1b75e7e905891fe00e060ccb67498ff274f04d62ee74a1617f59de2142b6259b05dc11479e1e6f4e0221c9e3c9e8ac073226a85d8b452a5c29fbb21929f2af8df54231c38dbb8b51e2c43745f7da1885db544d1a89e23841fd0f23a8ef0908f8fb48cc8f8532791087c6eca915b823c1a771d466052b5b945e93e2dd510d90d48974402eac82fa8563898f1f129731fbb5441556e7b9925134638ce6758329a8d047028c026fc4dc6def4e08326b20ea33fd951e6a76dd3f718cf48dd7a1acb615d1c94936e1dcc0529d8e85fec67c589552cb1dc1b0403a4928d3322a942df3a06cd07e6cf9c7ea4e462fc9bd26b01c970d23a1077ada751f579c4313be20bccf864ce2edcce0bf2b9a79853f14732f9157b1675d35e0156eb0c4fd0c1bc337e85cb4f43b32565e726be9923b1bbc3700080876d10fbf3a6cc4394185bca9e28a9b21b13e3d1583cb2a0a12c4fd195cd68790208168f7cd9b396ea665f88e7f4374fed332b7d674c4d8905079f2affd554a67b4318fdd2921d935c2b90a32242435684ebe5deacd3f18459ed0afd821f85ce8295e468618f5dd443e06c5d2b43b15fb2392fc5a298fbe5aad471d230a97a8da977d348d8f820f81f4307e66f96d3e8c3e3fbd05f601e37607eb0d4d121e383d87776993502fade7d064a30968eda4fbe556304ee96cd7656a94b8a89e75308e5937b4ddb8107995c4ffb4d0feefb73540355afc9ccac00175972cff7c3d00d73e0b888c94b2dad547bce72f92c4279520551b7e193a55f1984c2c83de3053587aa80a7903746df390e4b0e24a89facbd9d3fd3c344ebce5fce7de2313b26c51539c0d8d24faeb1937677722d53df0cc3747ca9d4149442e3365216a70a2dbe5524c99518292fc02ca5cb9df82cb3312d8608793db933b4725ebe19b20a9f345dc0ef7a8e2a7d0c50f9d7a7adf168fee0f21dd1d09f341e230af373b2642fe6b81cac8e9a727e3ea8fe33e481893196547f033431e84f629a16c831d82b82087df77b1297aab111831c5f65bf6353242dcf17c9f1ed30aaff8780e31c99a3ae0230e905ae9c861f7878978f98bc41e0209464c9f4a4a6b2e268620fe00385661036e8a2b043c269cd81ce7fa4604ae7c38e47a95e14066af971bba5a1584cffdc8baa2a9bf46926b0d59e7185168fdf8567d1b326a79721d64dc6e79635e68207b01eb5a74561371c05e117dd24ec7d92df3b8be25e0523544d6a605019b6a163206f369cdd2a7adf279ad5d1e5761cbbbc0ad925956f41c14b53fafff833b8ac4baeb7bbd8867d9cca0ff21697e831c565598cb1d4cf95e49c082f0ce6d12b5739feeee3f6709475665635de90f05f2949e38edf8e3f366941236be9b1e4d19ec2ccb6158baf3139bfe5823a017b5c6bf8ceadcf254d2257c0e13c4e91c33d0b35e1bde10b28b9afa819e8d99f0869035e89f655f96520c41b8af53423d162623f2aab1cbc02d87e1484ccf3c61abacb4a153e637c0dc151c1a5d8662b795941483397c0392af5555b0ec1f71a9c52d4868c761a2e2becba3d61323f2b7494b1679ebefb9764763d369103bf98b2cfbc85fb211fae33064f188fe574950dbb03799e3074bc35637677473694429a73aa46f1766aabd5c1d9d47aad24d9a5572844246b3a130dfaee0503675b1537d532a1288b2d51b27d3073071409dff77300117528a7c3d54e803002a5fc8ed799aaf66518214d97c4ea54c0c6c3f0251bc1c82752ae26b4b3756eb0068b84206d06b645fd4aab760ea0ad3cd651005c1adc1bbc691d93c162d36d4480060ba8b4c37bfad5955097a9514c0dbb373b8e2673f55b17e1b538a7174d4fa42776a66e5a9afaf73cfbf3cc4311e9da04e23cb43196420c0cd6d82b4052c8500c3915ff8ddc02a6ded0d94c068eb24c622efd1619511816719302c6f6698ade219f4b9dddf9ef0a5e3bb37395b034a1e0b627b3d6ae09cef9d859eb60174dc5910e03b01d16828725de80115f80a0caf0cab4d361a1f852f991c65b05a450facd92fc68514ae5fe68edd30e32f72517686b33039c1ca62e04932cbb15136e440dfdd229c69103a45ee5f2685cb65e119a5e9c3e53a987fcef1f9b52da912a563ff9638329dac5dc67c2d2da65d40f9bda7d9b58d230528d2fa777e906021ac3a42b31e7db4d2705f1688c3c9d1f228a08ef0384a9faa05ab455cfba81ace3537ed8ca5b369886d43ede41a3e6fa8b7a9878c5e7810ba0a6e58c0dc5e9c17547f59cc29df5d23e3a372bb4bf923f146c94d3998c46ee3d4669a8d940197f9dbe52c4d30c601a82ed7e03dfad7d9efa1884f6f6da143497d3c17e5c214d375f22a5c3376a0c0f0b99231e9144c8f1c7c333fc0747298ccfe5a37928760e1f006915e21404a80afa402d3fc2f7f9666beba2a37874419ad868a5d2d4ae56b0885efd2ec071afbd694dfe2a18ca1d9e954ef37a712dd25b3e5f564acbaf4aa6059753420079660a44086c42d242a28c71d7960fd914902df86f62ce155dda365585110b99e4f301732ce0d6ee98152d17da802b0831ec18e205a0f500feb49b88e84386884439a5f713fb174e476971f79c324d917f700e3d87663bf0010abf34f279dbdb351602991b68c3f49f135d0cdfc65d6f543b6cbf664f8fc797eb237f0c983f9fbb645c721e97dbd8b16f4199941748a57ac00a46ffe2f07618dc6dfd0bbf02682e1edbde097b9345a8c8c4624607843093fe8035ddde3aeb55a53cec5f104d206d62b8e26fdc6c99b59a352c4a19b065d05a8aa2a530ca876b6ca068b1620dfa00c7903ea306f4021ec0d500978034a2137d3e087a59cc01c5399261074fc683762cbb221b48baa6871aec1411de1298ae4a5f6bcfcb715d64e5a32763e1e976b705c28066121681156e0e14c91855cbadb7a1b802e37b7143d0b396163890181c6f582e412815ccd639e62be13e1fff51395440b928d89375f0362c7dae694f8291e9c516880d3a08bbe140c209d52959b122f4d657a705e27d2a82489215712d2540132c5b94329204ed69cbb1cf5e1a5ea673ad3f9a02d3ad69f6c39c206f626a1a4c91d6c0c236b6aba9020e67e806cba0cc50246777427939a3ec35cf8eaccacccd0391ba82424f12ecc5a00cfe768b6d5f0ef3897a36764d00ba8c40ce12a01a2d3f1f4fa6c9d702503ae58683e06d1ea80f7472ba446f9d1a40e709500b545f76a25d3b464d65ef17db0c65a7557834a05ad39c849815f465554e854b19619c4ddcc908ff1b13b9d190175e8260f9a114ee12ea463354013920caea49e6299873f8ec6b54909bc678c27faf7070ecd882eb4541f0e8d1a2b64587f93076908426d8c3d9572a739e5adaf51b783050819864caf1ebee2b600e91ca5347024a1fcbe08e6a36c80cfb07043e4363e2f09c83dd778ab5cfcc3c4175327c23c551628d982102f246205227310b2d59a55a497b42cabada6cd0356e6b493bb371b648492028a1f84cacf905189490f8e0fba8f472900539a9f60dbdbad41355355b95d16dcdf2ad50d77c1038be008693070fc35a0efeadb13cb0db87ab0a29eca24a83da689dfa5a9932ca33856aea9c0ef73a7d772903de11da1d368a4210fce1393049e583a2db87e6dd56302b43995f4102c085e4b21e3f142945ed3cc96ee6cb738b6aed6b859dd9522a72c8687dde9d8a993a196448d62b9c7f378434dad5dd8f13908e5d71b1ea0635686027c330de0aaebc88ecf3c18a3a0290756dd8c3ce434535bfddb3dd61b88ef15dc11a30d3e46d3d2b4fa9f2e3b3ec1f3f2e8979c891ecd23332e85dbb226667cf710e20d913402069b26f3a48ac143209740e84e8340668151cc711a73986a8e29eea00a14dfa7d68b9110493fc72eda865344357e39a545dd9de1bea14982da4df1288ce9182a618f31687f869368d081119e3417210accf9b965c1cdcabd7ffa27e853c4d46108531ec198efa2bad54a4825d30eb96ee4f229e455235c8e60263625ef73963a0f1d3fc259d7ba52068302250feda182df1ccad1096d3327c8cd723e825e0784996dc23ad3f0c9be149a32c2dd39bde8cf84c1c01d2a4bc8f76d88ec621b4c95491a1742461d4a44c11d4cbd9b7fbc52449ea3d09974f44c65b7e83b12361adced9840c57e74589e155f933d4e27e42c291d43ca84e573e78ebc3332c61165f427c6e010e860c0ee3305fca2cb138eb3431e81b7c20384d166e92e9ae92fe98e5c879f1e9199e9667a1e1f5527ae2bbb41ade78eb5da24207cdddd10309ed8878f7d1bdedfddc33a8de2c9f018fcc68112e6793ac04e75d44697cd739a84ace0e9d8d4f3360b73c04b580d2454a1904922fbb7541dfbe49c602bf4b6a539bf09d57084260c1f3ec6156e2122e36eac8279e21ac4cf07a94acc12ca659705875550ae882d0fe2371edce36b1409ba7181bef2f4d93c2ed947f4866bd0db1d2685c842cb2fdc49c4920bd4c4d8e8a78a11b82b639b81c1edbd242495d9287758c0df200e72b705e4d63912f7a15cd8f237e4f20cb5dc51d2ec31f043896e2343ac804c6a84b331ad91da017fe0562083a9e6082dde695832fb9f947aa13d7af188de10aee26d42002d5024cf12115259559847a9a4c8438c963e36dc3d6a45396dd12959051ff6ce64179842fba95c9588707430e2dad1e43a8989fe55eebd18619022ac0662d6d9c9af1adfe40eb8db28278145322f2430f270f775b2851b071b0c1d56284170396b15896dd5f4b1b647b234d0bcb413278fce274d5c0ebd55f8494481db57a967998ed82b96a33e9adf4d6a981a76ffffec0800ce6220c5419e603c805a578beb31023d1c9fa29184fcbe0d836c39f46ea614f4d6fa9b75c50505400451f1f5918ae14cca2c2e1cb384b6234debea8f04a3961b3d491af8f3103e31665e1db2bb09cb33c327f37409332b396e86ef3003f4534ab3b51ecced23495c29c3fe7de83bd3ff06cca2e74f8eefd912b4ff2d0388bf03fa5d9aaee483bcc954d26ca4e5f0da0b72905fe742baa26e326ea361816a8cc120ae51d72882a33670e0c5144baad24e4fe7db776267dfc0c3918b19a4d1558b7846cac03476a9859f4c5529015c194eabc47216d42f23303e625365457eb239ce10535a88ceab55d11b0f4013282b3882d90c2398122a1e10a27f9b41f24b595d08ba9869b05176e54c3cee3070363f93d5118fa6832d8780a37ac83832a48eaf3724d2cfdf904bd1e7c62ff694bf8758ab0e9c284f2e187724c6606b2d1655988d632412f3f5ad535016e5c0880a9eb4404065ce1b44c88f76b0abadd1dc97ae7f7db97eb5a6dd379c07875af893643f4ad296cee4f0c24c9ac9490099eff5918e284aa9afd69b1648001cce282a327c42a1ffdba02ff8aeef59e053d9057df8dcd4bfce451a4a84a1484d003fa29b29a28425d7f30c5e6202e0429d4e253ecab6d7c69d225a971aa3050b6411e789af513f9b61c29fb414bb97cbd7f1f5e2e3740ab44ae12a282576a4a1ddd0ce8ff8d4ea87eebaf03bb46841360e7950b35ec8d613b3ca1d3fee305c75dc6a7ca6967e7d5577a42de2884775ee31ec6c58e74561ba058b1387737d264e1a82628d0cae352d566a7528278d46e92e41d35edaa1ec128ed605bf5464cd5c895a25fd27d974126e22b15e2729b0e8ef9bdf4941da7457c3a2ff5e51af3d19d2fc9dfcf9a67e9663a4094fa70ddfe7463506d8bc8834fc130f6ef718a249024ef9c9fe496b64abd533c2c38af384cd36a869c7dc433980244bb4aa4eefff7c7f2433765a89c13fdd2334dbace5ce2a4fd748efd41a10e97f19885a804f828050752411303072550b424b831c07b1c98653ab8f02108849fcbd5ffc51fa9015fdbb91ff4c4a2704c7ffc0b172b8b7bbecc3fc430746e7a562551b3be57d4a029cad32b13319c764e735be2e8c217a431d3bebb69c1517a08ea4a9b0ff80c8583f1558475cf6c0c3d43e336ab72d040275e421e238181676db0400b8350ccc8174321d75ffc7d36dc6391b901d21de6a4e0ba239c99a7649ffc4697820d6c06771316b9c711d805688e44fc503b911007449e067c0d2569730e5efe3c08a4810df490060fcd35617431356223516d95b72155656aed2e235fd84e2d5a003fe837d98fe85f718e04fba146ca61ef883b823f86d3da257cef045af50bc46af041636058b181892342165ea1f18d6c1bed9816ba5f9e5a5b761076c8da04f2e131cd8338fe9166be1ce20fa28e43daa46df3969e69b5b250f07e74c1b2bde799426276958054026a26c54209fad3cb5a57733b7187ad60579f16447b7888830b8366862da04af24d8e17dfba822bd499eeccee2fda8b9120c513c8b9ffa1b71136bddc87c4645871b65a8d3c25e580d45062fb98cd8579768cb3c4b4d6e3416be34c652768836962f3355f89219752da0485bb61e975f5fcb59a8f9bd7fc348d88b84b6be87caf79644ca0bda46c064644242943ab2cd6904c1da743ea810d1d939bce937ae4a206a1f9fec4bcd3e94c94fc10daf02fb3d600d18c0de22de578795bc5c24d7c8a946a42b1112fb844d51aa0fafb116f3ba9c27a0133d516ca50339504e547b3f50f4d43e3274824088c84930a532ac72a95c654219b330bff2e91069491b9c0af38fc55400740710b6854a41841512bd0d48cd8ef39e168ee8d60bb02875b80aa1ef4626b8fa138e862ff67d183568140716f2822c0a6f55f9a4e8de604bd2eab5304f71a77fddd084fe6b221479a84f3a9e04351c2c04d4eb27553f37a0c06431087442470e87df648bfe83eafa3a0ebe198bcc292e1c66ad57e7d1c043e5220c550a94c978042ad107a91f518df61b93d820bc0cf49aeaa50b365bf2785181d260b8fa72ed6c88c51f9f163574d817c2c188d00e0884fbd90e7d7e2e08074d11fb3067f7d043fc02786c2c26609929da92c942e61b6bfa0eb17fe03badd1b3d1d98bd1d795d2d31e4f8802360308e51967fa6422d57d522b7731e93e64c10b5b50f923bfaa6119ee56fe9197fc9561369a7768ef63043e016af65c6e8a78f8fd2ef0a91360bc3e9d148c4229e1b2152d2002998efdcde925a1b3eb024200e7c6186af9f6429e73135e6a879ed8cba424fe32c7f61c0f3e8b0bdec9288648c109b18f6b7af3201263c3d841344d987c5905c75d53273f18b0e8cd4d03b552a84f85887969835eb1f4b10fadb3673ff5499aef22f19bc4429de3f9ebf683bfc8dfbf1fd54588f959ca21742e236807ec7ea357e3c3b2cbbbc1519543a188e204254035d18b529625bea065047fbd18e5f9793ebbccceef1821dfed391001b5b09732565540fd33ecaea0857c28ae5c8f9f70c870b339d9d0e7e2d9086a1bb4ccdbeeb1007eb4ebf2d881e6373b91e12e36b5df1795e57af1e048c203c0e8fcbadcc4147744185c9fc09c74c835483c34748a3f005f05351b0c9ea96f78bed73e27f807737e7c0dd7be702fe0710ce26bb0aa1aabec0acb3e81fdcb82c68b7d488019be5e38355a2c9be351ace91da38cce2de0d316beeab85a79b16681ad96bda120172311d46c103196864c098fc9619ab2d2e599bd03b3008e2c4e4640ba16f8407093f46b78cd20179e013a6cab084f5b7a120bbec525dfd5e3abad8795d4190339c4ec73fd104a7d2096813f01f14b2715fce9928580e36be4a726229d284254248313f50cd24b8e7c71eee608d44ac4b39f60ded26c07bad72548dc9475319f9eba31bde963cc870109b017a35a3998734a174dfe24e1a45601d08f52620c1d882773b662062b2ad74cf1a4c6247ad8717c216943104a91f40844c5192f8d60bb85223c7cec66c8c77c13ecd49ac0fe61ed1d7a0913e758f90ac55eda14855bf714ee771d12f31f5dde564ed36909e7ed0edb243e16dd5841a122347ef5e44a958168c87bb98442aa328ead4dc3a29140c7d9940fe2fb4dc12245266a8ce07173f7c960db44effec6f56ecda9ff3cbc913da638c0bc1b703d422aedeeb42162721ce85bc48a382c22589a465e350fc1e4102d4b9eb1d5957229fe23de87463e6382125d7e931933fc394ce20e7c66f4b1e0cee47020ca1794f7d561bc5ea0abe62acf5ff580d5826206dac767674b531bf3225d188a64a959dc117a9902b1e497caa3d75649c6889cc8c04051d8d819ae531d9730c986a3182a07c68873b47b1edef5aef9364ebbd02eca02c3b9f8c99fcaa70d32e2730fed8f09e62db9de7d4a63212104ee54a32d5b71dfcd06477b104a8c939aed3e25952b058fbb1ae4ceee95fad9d7a410ec0b021e7a3f5b6972b081c348909362cd7577221c30398eb6a5c31a4e3479c1f09c5eef25391defcfe6e2fa4ee1c582cf030ffffa2eaf8721a799cc1369818c70adbf092d716c951f79dc8c6118167d3d18cfda4fefa197afa0128e62ba28ccbe1ebbe8f73061f4a2e81d0cf83a6d98c40596a19dd62616f7c72d8a4e57b80de7e376dfadae762c88e5b4ff151e54fa5b8729a6020a0cab05f8808fb42020c59f4d1079d460a2cb77644139df892f2dd8820ad5b2553781c87c8cbda4856cdc8dde5f724cec0d269056da79fc0af75c66ceffd013d0fd60c1d06be1a1d241e2b1cd985c06aaa82927dcae4f48a1f2727d37df6ab9127da3c97afc14ee434363039f81c5f02d7abd20823b19b04655ea1a85e398e9d94ea897a3cefe8bd12c911d1e21492a73c2a1ce5de44ec0005e9bcbc46bc111287a2189a330ae2d4c22859cc248c72027eb9c72ca00efe0e6fcddf123ac78e563f8c26a53c90b9f6d6ba3423a45c2a5de00869aa6f7add9ea30f27e9b2961c5163ae113cd5b455a6e6d343d77041f62b335d323cf1309e5382f8538c830c80ff14805948f9fd143dff1f810ba1dbad3cba28d173cd2d694699a9e809a119eb490705021de2f4bf90277bdd9f24e8fd89c0bcef673e9f016bc1048434628c642ef9dfe39bb7ee5e2b00c9660484591147fddd0b9587b455c9d6e77a17be9392870cc9ab5bb18c43244984ee8c81294a7a87a847be362ddfe7de088623a45ce20174c4754467a47396f0d9f43ccf1f7b9b5e486702f93c0b7038aff802a6b51023b89feae02154c892bd1280683944e2f628f17fafb5eb8189d44145cf4f06503d3c566739ab37f89455830e85515763b265d8af3d6afa51174bfb3dd28ec691bf1598e055a8aa1ea2c2d7c02b5913de73909cbbd02ade440223891b87b7fb0f0289b23106ab62f53b49b3e6ec1961a4e961b7ec691f0526e83050c5f642d295d2b9d9793134bcbaaf4226672ab6f3a5404cfa7c15f75bfdb0580570aada2d35c351f685443aa8ba9664eb20755cfd0b60204bd071ce0bfa4d4272d747c25448799c4a39f9d28870869f49130cc206f437bcc978d9b24cdf3b0a949f3c59eab474427d49103b48d896726a11c7f5949568c710191c51dc7db44bde66e36aeb53f56f17dd312944b3fe9e126c8b7726d6d15cb5220106bf7571aed73a19fa176fc575481b849613c2a2224a309bad4380a4d7d33ab1c01218201e2d164473b1d6d9d84b52c05d948e15424e993053211823a0f89d557d409f378aa9638183044c3b8879666800ff8133c788bb79382082a34bc15470d17aeb035c7bb3fc663fb5ade079d73c56899457f26071a55057a225ae0043425bdd790a000330650ea2d55478a764ebafc9e002d2124704ba741898614b581b0b12ffa03d388b9e9679584c2c0799a9525b012ad0e5073e7c456e30f4394cac69e3e160f5d9c77b771c470894ce59a7f1074559db40115c8c40f77db80f9725ee72516177f3cf76e121a0aab1d661157bd3fa04ce82e4d54d9090eae7292a705a72a483d2c16511ae4422d6a3568413bce7d6a190043dc0288a7f554b138ae661635cbc232fef914324da85a05a16c6a750d7ab42626dfb4f60f91e0ef910409cde92c59808dc4b7cbff5e2207979f095888ec1827d86c5b05d92d72156361eecb5c149abab8555106730f6a081f8959ea5a3ab9f41eb2839a8475710fb75f530d400a9a8febe179bf13a28bb566b1e2507971319182c7e3b58c1764cb722283e6a9531760a6cd3607aaf200d6258cc6050b4e3175cf756bc42fe5e36cbbbf683532bf831329c4c5ea92122a14a2c51f853d45f6abe98e5738790e66a4f4f47832f27e06dcce2380dfa5a7818858084cb3d9438a6da1cb7b365a43d3a4202cd9cee7da1adfa5da2753f6b4e6fe62c74095f60c8da786cd4ef61232e72a24df62c44fea39cc5ae65608af6ba70f902e2d4c202cd19af2634b88d6f888b41e332b89f6bb45aee9c68e5a05d3455c8c1a8c939beb425a20464cae667d125a41bf778ce81b41e9d647b5ee3f1d065448b60f03d2d101c9284171ddc1ab52cebb5cfc065c8f79d7e238c65021f0f9e5cd1747049b77fba30709bc2cddbe98af222d56adf593cdaba4bc3b2b5465c3dac5597346267cbfe0f005a352531d300624f3032a6897b45c2012b2e21c55c59b7db4ff561fd26da7d587feaafe39d93ca7b316da494cf4a55db789013896f25ddeb5fe0b221033446d540220202b6feee5545ceb20529e0be4b721dadf30782897d863498500001c8a02c1b7dddddddd5bca2465f40a140ba70bbb17742f50792177e4c4f604c9516721cbfe55e46d9c30cd993f676c349fdae090c87686bc55e4fd776245720b563ce530ddc24ec929947316d0fb0b517156d0fb0be99c057bff176ece42e1fd6188554fe1d3f0c7a727904f51bbe56b904f7fb708f914dd2d56ec52d069f843a727109da24a1f44a75f7a213a456fa7d0bb090b5f08053da42623498adc8c2421109ca90cddec547212392a2369fbcbd0c9d09d417f3261a10f1fefe3e3e373e3868fcf8ddb82f386cf8ddce3fae4a0af26ccc7f7f0171308a65fb87d6ebb8c1c9221243247fd0790635048863c55a38c51dadd528110ab40986dff05c48010d391eb6eb72b77e4da6202854a4e1972b54ace4716c0ffc843de4846f240b968c8854f12be0af0478efc4f01dec6e8b60e8074802b0e805b81e6c36887e201906ecbc8eea6aea8ac1988a807c0396b062247fdb688f8d0691856417b41f7c2ae4846c2434582819af22fc00ef1079007faf1f14830505d2abc3f920b54178ef747024475b9de1fc90fd5a5e3fd91f8505d39de1f8905aaebf5fe487aa8aefdfe4864d5d5e3fd91f080244975ed787f243b54d78ff74732545d40de1f890ed5f5f3fe4872a82ea0f747824375f9787f24b1ea0a9514f9b4ea8003db1fc9cc7714797f5200398621390617b287240fc57084c4858c7b18b1fd7708b6ffeb88ed9f0389edaf4304dbdf3582ed8fa368fbab60b45dc817488ec10355e5ff23c7f081aaf2df9163004155f98b590b9a0bd95f618e6188183a5055445495ff4f8ea188aa7221db970ab956958bc8e320f23a7210f91ee2ef0c81738bffca1040e9225a2cd1c204db3f8624787c0f77a12c9263a0812f16320f1d0391915d1abe907b01f78294178c5e58e285da0b4d5e389a818898f79f812806857a4f41bf7033e2947939721bfe03c82fb69bf2dab6f169d582c89e3e5c4c200cfb1613e8471fb1c510abaaa1cd537b59c2d10320ad561fa4b940da2ee4930835a1f026f307fff85811f5105515122fb49722522953593203e0415f327ff0867d987dd8f87467b6fd51587e995595bf8ffc52c40b51507e51b293f12ee353df69c5c16e13c904d0434408308001bc8d1395b5c211f5c8592bd06c6cb7454477531edad8521bdcf60f401e821b72bb297fa13c244a0a7908120a79084d481e82c4102347fd83e4212b1ce1bf02cdf4fe4474a5f7928b3a0dcf4fab153ba19cd08f2c54c5517f2359687753feaa203da4463b89e41862b5c78401e49308e7949d3f387a0e01c9270e55c5423e696ec31f281715f99f229aa451f2c8f6bcd5f8f473b7db3edf06fbf15f0ee7abf491c5278d1879f27a75a7241ac96fbb29ff1ff99df4289dfc296126d08c2dfe0a7968096c852761f96b379bfc297de4997d4d20748b4f247fcd6df8c835db484632e4a91fe37fe1fb0e75bac2167511911f403e6537e55f249f3e32cdfe5acfa350e74944f7d47a644ba3fd947636032a5fd9b29840486220383f76c2fb9f00e487e534fd94cc9f92b4a9a119c530e607fc400f065fb7b5a674e35fb9ee9debee91ebe691ebde91edd691edce91ebc6916bfe8c55d70dbb7d3ef59ed8feae6cb7e8f336a7ad5fed1bba56958f4e59ff7d2bd7cdca75dbc869789e9067c03b3d63b117a397245e9a6c7f00e4b47c210ad3f7292f43bcd050ff12844b99c1a24ee5a44287a64ea5e68c9d158b28e84eab1644150b27158b2c2a1649dba5d429b2d8fe2e56ecb67f4b4e5d4a9d02c6f63f738a02a354a5c8552cbcd8fe65d6414e7dca4eab0e62dbd3af6dffde7eb58db50ca36d0834f386213e8314ccd2a127dbf62f17d67a332bf75e7bbdd9be56065c0c79b3fbe43983d3fe8dc53c2b3897b3d65a6bdd4c01dc17e636f73bbbb37beffddcb6bddd0e63eced3ccfd26020ccbe09e43a4db7f718c88bc5ce1d9cd704aaaa8adfd3b8a669e36d3fea1863ece3a88fa71ceb1a8e7a0e4f5659e163fcb8a66dcee193811a35be4feb13dbf1d370168d1a3646f2aa976e0c1bd462d207ce94c6f647da357c72586fc6006fb6578efa756fd57838de3c296c0c63f0560fdf07df887fa86d086d586203f8f7770d90c06d89ef915f343e0d8e7a0db82d528fe5936f44149fd4362cb181f49e1473896d036e0b0687c151cff3fe4455ef438fd4f5a6740f77b35ee054616807f0efef103e8d0a57d37c7fff8697a6f9f4f7ddf0c22edb15f340a3c205ff7e11f0af2e12beeb1afc43fc34df87ff6915c407dfcbe14d790f669c82530234ec62610ff86b6b2889cfcb7c59f8b231cd4f594c11b56b918d65a1074ef29ceedaf0d7c8e183bf0790dbba79fc15e38fd55bcca71597ab3a92e6e6bc2d8cf18a860ded01e4878de5956cfc20c8e2cd4eb61899e56ff24966bb4bf7bdd41af2583ef5760c25095fa897a32fffd4659f5c25cbb7d8147ebf99f16c5e78c6c8f76fb2bf9be397462fbdbcab0abfcd2abc6c64a93c4bb907f903aad427dbe9b61994ba876d06efc9271d40336a15eef57fc00d1bec7153482a0c0dcd3cea87643ee6c91ed555da98e53d9f2f66151cbddb7c9b67c17c1b4de3bdcdd7170b259b37c2f236e7db709fe58d9c6ff336da06ff33e6ac313dcccb9f626ab2e96932cccfe49797c927976f613933909bc2cfd2e2a2dbe1580ffc66c68f9b9c76d76d1fbfcb0da29f45c041c055b00e0741877778b783afe11711ebd7a08b8c230de077e544ef23e10cdfabb635bee331c37ad8fce30827a8f1ebea65d45131bc31f8eeb54efe7d23e5d5362c29e25fbef85ff89ff945e39b7afcc6afaf30fc51db307e387ef83698dff77d36e6e8f73d198aa5b7b1db6ac0173660c8d4f5a6468dbaa9ef02e7d5362cc16fc37df25ddba069bc0f8bf8979ac6d32a8c25f9f7c9b738c05ddf9a309a21f7c92fe25f66dce4acffbd98f3f7d9993bb8055971524f66897f3db73d6d6b3dbb6ceb99c66da6718c1bc2cd7cca76d073b9ade7966d693e9aef4fdbf33c6e6bf3f79e3d6c6efbe57b713ef7c347dff636a495cfa3e51791efd96a64cd5623ff865f42ea1aca17bf06d2347d1e2d337a53d77c0c9c7583152c227e0de18bdfc36d51c491fc9767bb2daf76c5bbf27954ef6b02cb0cf1abeafe878fbe772b4b5073ced3bddf459d70487e927f8d1feaeb8571de25e18f79c9f7e05b51b3d4ceefeb0ec12ffc3ee8d35857fb7db72c310e3958644f8e90218794cffac95920187ee289baa9d22cfdfdfba8ea6279309f3ed9e9a8a3c253a7e1d9e3b65cc4e793b85a2fb16854953980299f2a8a4240299f2719cc5c9e0d287358e2705f096c51d4690dfb7b9bb6b6df8a4d989743088bec4908708e8ddd7eeeb4df17ab4f4facaff537551170ad452244c2c851fb60b64b58247838d39af449c059295a83b35216dcc3f03cafc5ba4844c06db120dab7dfc33e10fb2b1f4f09597148153c657d2c0a3d9c69c56d6b9f851c477cec5b2f878475d43e7a5b35acfbe4486bec1c8edaafe1a8adb1eda3357cece7b05787385442b0835d1216e02c70436d8ad1174f8228e20a2ebc095971c2f23c3dca59e3d7af7116f9a569d31cdb7ec5a9e87d53466b4d4655f47e994f15bd4fe6b39615bdb8a2f77e38fbfb1ad4e92c7fb4036ad8b886a32cefa8fdd33eca7eb51f9e7e6d60b308711071a8611724693aad7fb5d81985ac3834dfdff0ba5f3f54616807fbe3ef60b50d4b6886f8db2fe2ff0aff86972e42fed535901e0b0acefa121f7caf647e49fc22f645f17ea9543a61b829efcd31f42f82dfefe3ff7491f2495dc3fd22f6ef4e37e0861f866fbf48f8f6d32b6d6737e5b9aec1ea1a7adcf17c6f081508418811cc62b15ccbabd65dbdb5bceacbd62194c0ca0a90f040099ee0e2555ff67bdcf185c3f7f0ef7ba2aec1da6ecafb2545fcadaec175bd297cbd0fb5e7dddcc1197856a725fe1c07fed46ff5c7f5abfe9ef66b035054d7cbfbefbca0ba60de7fe789ea8a79ff1d5b75a1a4e44f7770b0fd776ebe23bf3f5942f65e36b93a5a83b36d0f8d0e4f2a635b6c62d927cb4bad2a97986df37da162605e66644ad95fa8189817fb3ae522d9eaa5df45b39cfa4da5c71902e72efdcd1040ed922ed2da66db183652996dcd1270727d9590bf97ceab242a8fff976938b712db5f688b1512db7f65444577b7c5e345e501238a241cce1ea2d8dd206633108247c793e3b9f1205d512508e72c9e2e2a2ae40415a12367f16451d124e05248ba2d9e296a8a089e284144cee289a2a22edbe98e4db7fd7140cdb35b72eed862dc2cf6d831303a4e304d287bc85154d108c16fc79b91c2f7e5b6a26256a5715311ab718b648312d1ea0ec9aaebab01de9687e179a250efe82aad91745b66144ff94b894d91519955d9a615f38a1139b4c498345e31919cbcf562307b563ce5edf0b6a598970479e5adb8941bb35fbfd0bef7e55bad636f57d3fbc05d4d30dcd50cc55d4d71dcd51cc95d4db2dcd52ccd5d4db3b4ab5932ed6a9ace5dcd93655793a56557b3c56557d3e5b4ab797ad9d57c81d9d58489d9d58c41ed6aa26476356566763567687635696a76356b6c76356d6e76356f62ec6ac6b065bd2def89a7fca348417d95273bf52b3b47ad58edd9eca7beabe6cfcdceb6b37ef18d81b65d6813c71c59de5a599ab65dc9663a595a4a9799ed347b8189b1a164ca199ab2a6bcb6d3a578ab4e712a55acd4f7dbca9768e264f5641565d7779ab5e22d18aee47cc87f70255ec4ce59e2136fd9d92cca4cca6cca8ccaaccaae6f6d5689b7accd16619158c2d6c4c9ae6f006fd9b0ca6905756568db190ccefa7eca4b414539eb934ae3716e35bc35fa6d15ab69f817d74853c92c6d48b1c6f6c466b3d968664a66b3d96ce693a935a9d56a35d48ddda11bbbb11bbbb1986b3b2f0cb95c6db55aad562bf2e644b3bc648c185a3177e5eeeec81d4c08becc94cc66b3d96974b9b5dbe4d66eedd66eade5daced96c369bd96637078359fead39b2e66a4ebcb3d02c2fd9128297144316f03b674a66b3d9cc747397cacddddccddddccde5b59dd766b3d96cb3d96c369bcd42b7819714c312f899b627369bcd56ce94cc66b3d9b59dbb1db659dbfd3414ad2db4366b03edeebba41892df687b62b3d96ce2b59ddf6c3b8d46c33bbbdbedecceee3e4b1bdd73cf5d1cf10e5bc13bbcc33bbcc3bb6b3bafa3ff2854e8b4f0d313f5e84e9d367a48b8a8d3b23c9d409dd6d4e0e0d0d83236e0b7dbf2a478ca7f0a8e4aaecabe92e469efe6e893d33afe70e31b6ba104b3edf54399dac6d65a10b41604adcc9819706be4ccf732efa3cc35c1c6a396e1d664f227233353db326f9a00f8f42eb1ed4562db226eee0fdbdea1d0af78d20dd5f7884b034ff8343421c4fd8d8dae0180382be96e8ae6ac5292a35e0aa3a2ae35694226855053fe2764928b9af22f855159e41615f52467913947854006a1ba50205457aaca0faa2bc707d5554217d585a3e3a2baf4fb9774b7658add540faa8be7fd491e54d7eafd492ad5d5f3fee416d5a54575d17897ea0150d299f02613bef4e4ce7798f0fea429668a59198f936b55c57c09393a2974c6e93df92ea92aff55be25a82a7f9e7c4d5055fe3a0440be495495bf2a5f1254953f8d7c95b84b54d58c5cab2a75c2a327bc4ece092f433f8ed6bae8047d6b269c2c7f9b00c10cee13f23521838f1bb3c68dff3ac4798c1037fe0d6e7c1bdcf83538771a5daa919f9239b00467746acafcf732e965724f7099b84d2e0a6e0aae0aae0b2e0b482b64117679738a36b935d36ce5a624901527f5ac57df4b4198eba9b10d05f7f69ef65c135b559e874b5cabae8c4be90dbb56d5acc9e717e34f019fe761ef03adfd6cfdeaf7fef5034371b479244b0eb049d8f5af6326358fe31fd8723ff003c33083201866f0bfef7b5c8c310e7f02df9f9bbc25f63ebf9ee781615856eb177bfea517a71f064b8c3f32bc380ceff7f80b757853e2e9de70bcb78e75ac63ad9824499f9baa1e382db9dd03a7bd5f6f596b2df1407e255b5325ef022459cb7b3da7d9efbdeed7de1dc88ae3efeeeedff779def77ddff77ddf87dd5aec570464c5b9f75e6bade8a007faea65c1b7b97ae8cbca502ffb55f62feb596bbf87575bcec9be5fde3f916e84c74bdc0ac04f008f91b7fefd70e8ab570dbf66f0ab0c7dc950af8a65ffaa8f2b76ebf5bd0d905f11a7db77a46dff2be2f437d5b054bfe225fc6b4bbd42b0e76d6d05788fdf821fe6a1243e2f51832f0b5fa30e5fcc329f57f863f8329f97f80f7eef005400c0fd009303c07db5023c0d81bb8b68eef71300ffdca4971693df87410c5ef0be67b5917bff03416d84c7ebd3e14d55bbfa8ab0c3e338628c492f9fa5a3e3388e7f1a71e3a85319c627c97c7e4894e58803aa38a09e1d7e0f07bd7006d10ca2ea0ac7177ffc6bfeac76a86b558dd66e7006d1db131e9f1c53192f871b6305609d56dceed9a3f8e9cb10db577bd431705575c4998eb8ed3fe2accdf7c518b8ed1f8eb85bed8cdd17841b7508341fd1e7ab89a28fbdf1dffbf8e87adef0f94c7043cbf0d57c7cf2f7e20d1f9f1745517cf07dc4077fdb1c4f5ed7f124be3b9ef4303661ae273fcf033f13760e9d44a7927356550e633d198226ec1cfae185e6e80b1168c2604f8aad0f4dd88d1bad93e8543264c26e3c398a3c9e247b7e34613e4f96648f27cd723f59929933c05e4f9a6ab097254a46389e3c5f88602f26730620bc5b4fb29c45c04ea2d37496cc93c54b72142dcb799ee759654aa7cf2d6f589db65aad67b12e92eb6ecd75d768009f5edb914fef11914f2fd10b9f5e5a18558927392adad3ca8ddd94f8a95749e1d31b63097d7a875a2d7c7a65ae007c7a67287cea38219ffa14d8a79e0bf2a95309b2e28db7402c2b6cb9508405960fd68f4f71b6914f511ccef48c6d9174412e099b06a0b7bb9a42501b489fb1aa12753a631b5a02f4f3aebb2d1b230fa3b26c9ca8a8dbd81885311bdc256221af40922be421226049faf8647b23db4db672dd242cdb4db2b2dda42bdb4de280e52ae4ab0943e1abc9e3497b027d35613f0ff452923d72dd248f5c3709539224e92e37b275296fe4ba5bb9b272dd61b6ae6c5db9922c2529b6b0034aba29b7b2438a22a5851d09a4d3932c3f269b2921ba29f70103c26c86d948e2c856271634048050167400a41f2023bb1f40820841c1c84e48ffd069988206a2d333884e514274fa28e814ad821686e28c1a054e08f704290a5c7d82348e3e7c049164fdf99ddcbe414284843604498c060d225588e85ad8b560a5852467f9f8f9c1e381041182420a1f52c16347005e5ee8e553e061fb61ab2a203a0dc3203a3d4f213a45a150d0a176d58278d8b6ea5357ad2c4d58f83972dd3aec2944c54a9163bd9b30a19ca3fe376eb45aad568bf5d584ddd0423a47b31015471df643743795c349b9192d31a4c990a3d369c68c193b9fdae0847237a5db5949fa0941e04336a4e62c58189e270af5dfd292faec920dda79ca6a7c6a83b64fa0efe14080bc8fcf7fb1f3e7ab0903f245f21220ba9d43242976647724e988ee48eec89423b823b526474e5c5c5e5e5e745ad8b590d4c29558100a4280e416925ab0e22810109298b37ede817c0009b223004229fcd81100a1147e005911d920aa2a7f1e99d4652bdb0dcb76b35cd96e1cb705a70b2ee0768c4f676c458a10c94474446c8caeff91a423bb23558ee88e3c39827424ca919ba3cec29273d69fdae05ad8dd54d24d1120460019017e20c0ec126d14f4115b084e9a4732e4ac2099b3809ce5a384af1587919de5f12fc1c89111dd0a341c1c1c1c1c9c15682d2d2d2d2d2d406f4d5810ecab09b4335bcd545792a4e0a6e46c6c6c6e90c86e0ac90c091192221ca5ddd40b372e72312e722d2d260c08a6d31b36ec7d3e1c4ed8f77052e8b6e04cab0e8072d100926088edc46eea85323ae541e91415757adae8972418a4992916c7cbc9ea072f5fa367a6f0a01f23be9cbc984a350646302f2acf7d30c3bea82a2bb0a4960cbb1223f38fccd11f1c9d11cd70d1b01d4c2ecaffe522fd1f09ce94c68cc68c87a63322a21179eafe0f2d38619ec584b93c8b098477e9c9534963162ab1385e4e2ccfb7daf598a0c7480849080a2a460c47b0e7a8c704a8d56e27eec2ddb803773d477e4ac968c8aaead618d466e41f23da4d11fd28f11f9d96fc3384f34f0c760596e49261561cbdff726271c0bcf842bfc0c9be250ddb7d24c8483590c44f5347fb7ecae8b662fcfd54edb66efeca3c757f362322daf7d26851f67df22b53a1921ea31e25c250c9ee394a6120255bed565f8c60d9427e41a804d1e520553c48aeaa70424896e77e902c2acf142b7c065becfbe14c5655f77b8e5218b81f444dadbef8c159417aa8e86df3ecb4274577da7354f61cb5f41c31d9f7c99ea324f6fdafe72884ed6056aaeabe1092c50123428519a1aaeedf67315b7238bfe4aa6a68879fdbf75b72a7d2cc414c39885155dd0fd244aded2049d420465424b6ae065920d1a6388aa2660483ddeea6927e62b21f7e80c0c0be9f9a39ecfb2de6cfcd3ee920b42aceb442a1a5154a937d5f08ed0a3112527396f72c265014ac53b228b75c54c293a79ea3971713f67d48e42c2124222a4b084a4a765358d4d6ca08ab5d8f91873fa8ac1e252a7afff2a0e9a342dafd971c445755f7cd98c5b1d2591e21285584909c15a48b8a623307a90521aaaafb29d96d0561525346a8467b775b418ca829256650448f91b3820c51d1dcbeefa2531e2bd029f6fd169d7e14a827f67d169d9e75064decfb9e926dd44e69ccf6fd5486ed6049f927f6335455f7573a8be36787ca73ffc680877d3f5de9f635e920b4aaba69cf518da3aaba2f2357db951d7edaf364dfefb9df63336d4f625e8a2882e6adfc3859f62f191a66c553f761603b2f07676ace66a6126fe5bf4fca94a9fc4fd2d49432727e9cac9fbca5996f369a712cf6d2c9b01dec8aa3f773f24feca6eedfe41f1af38767931a9694c4999a4630d8edfbe612f74d2367797f9da466937d9f8cb9b90e73f372ba71b9b137161582f5f3626a4af6eb0ba7e8bf842ce6f4226dfcfa16b1430d669b737853f83f9fa87b7453f86ff2b5dd14b6ba5655fa55296e349e801e8c36bbed5b9cb4521176b1eb8bbaea82f99aaba8afef15e1ac99afefd19cf5f2f53d2472bec67c7dafc97f8df1f53d246f888aa3947a446c1c86a764d7f782a82c9c24a3793810552fe6c97017bb7a316f8673d5054eb1a2b2bc9aa34e4899a2b2bc234f06f5f113b5e5d9a8c04e767ddc446579374771d5457e7d7cab2e5b0dbf8697e6539d6d3af5e7fffa38d5552383c6798266411a08c393c8334d0f26060949288ab700406443fc81a13892e5104e9a5fad72cef672518cbff99918ab9e9e9e9e9e9e9e1ed48c67ea31d2fd20465edd4cab1b93dbccca88d56d885b8f0e3db11e1f7a643d42f4cc7a8ee8a1f5186d1bd24d0aae0a93a9bc59c6f672d150fe24e14b0f0d25f95792d54beb1d59128d58d2ce6ebbc3bcb2d19986e71703e399daf46062be1cee0a1856bcd06531c58a93a6c4c262c2be9bf3b6c945ff35ff34ffa55cf3261111259a189b5f31a935397252c384114f52552d9104183ad58189e9a1c4a3c3ddac7851a58b9c1552a6c0e98ecd46a2996c218282144445d02d088b209c2e0ccf134594ed071141a92c2125909c2564444565093151d19bb3846a8e62515b422ea8a9db120ad2394b2817f483ca12dae24c514ff4d99b9d42d26dad86b03581d229eaa441d2f6b799675755424a0819093121541372c116b92fa754a590b25d8ab34aef2f84f39b6d6060ca9b6b86705a14ca847d9f040ab55119660332f96503678a67be139cf6734b4e1ecaf924e14be74bc8c1e55c9193d3d180ce4c87081d229d221d9a4e4d878913eb1cad7080d1edc0a09ee0af937764507bfc4bc83b4f541bfe31b91bf33043bc574e0f4c4cce1539b89c5c4e2e2767cac9059193cbc9e9684067a643840e914e910e4d87099d9aced1f6a729ad70345963637333cbcebbfc454ed24a6823cd84ae6917e8238da4a1dc53dff2177997937472d2d5e99993b412da4833a16bda05fa4843d148fa66736302dd6c71e6a6b491d9f86033b381c2c66603c506c9a60a9b9b4dce260c6dc2303c4f14ead310077b5f3f1f9ec89cb57a62765bab208e6c66a0cdcc66f6a7cdecdc331b286ca0d820d9546173b3d9c246b7fdb1b8432a7a82d60411a8d315932f75ba4ae2d3773584c943953018a7b56751fe18260ce663984078c77c3669981f5a1233030de74e0a0a0a4d7c3e8bfd748725afa2408a7242b1cd9e3c11840e934fc293c576495e54e9e2a1a82a1a98bcf3c4d1528cdeb17d4ccef4dc27f8983873094573d4112823d415281c6a0b540e2504940e95840ac35326360b82c72adc4982e289caf304c9d128b79bc2393ac5d19cee260625a413aae22d1cbdf3c46fbe2350349411aa062823940d6584aaa1ae40e1505ba072ce5ae1801242124a775b3c5ea0c2a8824a72164f172f4373e27c273077c9c5e6f285cbce250c97a4130f35758addd469e6e8e9b29c8860f28a08e989ed1ffe7f1a6e1ab1282a4fd2eea67437b5436527b783db91b273db89b283545550b6ef3c51779e54d49fc6dba5deb131395d6c3aa74e575aa72b26269dae92d8fe9eb6c1e8746584cb172e3b97305c924e3c9c62a7d9490327a25268513be772d544e529bd6dc20407232382e8615c72a6bf73b4ce24d110b15809e9869b928b117e2538bd1cce120e8e09fbdecbe12c793abbe42cfb572e2195a0946e261d4c31930f26994908d3cc44331d1103639ab0c9c8a5894bcd592cffc4ef9f346fdfb26ce0bc252825a4d24d67a7a59ba97443edb4748329dd7ab8997430c54c3e986426214c33d311269ac968fbd3d851eb921d820c1926ecfb32af6c39a5c7613cd9ab276a104745491831842ca6fba187211c78927617bfd31d5b29efdca0f2d8906e5270557c4bce9c5cd3735b19324a511475231123d178c4481b4d5053a3d11d8f7c742236da683869b29713b71075231123d178c4481b4d301a8d47a3132c3a0d696074fa578bbad1b66aa2f21c5595139ba34fb213bb5655b1aa6ac2648583d1f687d1a94e8c4e7f081a9d94c35d01c38a17ba2ca65851d153063b30a83cb62763bed937dfede5a29bcf31f34e14476fb86afef0ec18bd83543b4b4eced2dfec11dfe41375534fe6e49437bb8e4e79b3adb5ba5664cd67ca36a36ecade5c779eecd84c70a658c70412378dce375b26e3bce49f29334aa6f2d82f05692068e62c968adabf59b5495579338d6dcab6aa7272cecf66679b78ca9b67ec27433cd905f91024438720da6dff2059900f299fd4e661048fa41f1af831d3e161048fa4edff63f64303688f2b091c311e44f020b2156d7f9ceed8725c49f020e241848e978b701ee33c4d9e81061b4c58b2dc8aa8aa5c475417cd57369dbd6a82095112312364abd94a96c48492bd0aa2feb0573dd4d8e601a3f2506165efe8a0f2f8e3e8bc83746f79079777a6e46e6a078bed2f23a4b1c1848825dbfe2e9ab3524872d71195954218f52517699d421895b50345459db563c326d3abb1214199999711258665f6764e0c06c9996ddfa6a6c6bcb165be3b1451e3cb8c4c29fb0e45d478324dd8ab01491b940c9dbcedf7a5fc333219958bb22ed2fa3f1e2f76443b45edd86eca3f46de41ca3bb79b82d13cbbaac2dbfa6cebe570ae767d1926d0cd1635cfee2bc1597a984f776c2f5926e764337fb6998cc93fa8ac5a4b98dcc351d4b638dba6354a356c94376b2fb3ca9bffbd5c9475bd299d6f8ea1f3cdc63181c45dd2f8669337485779f3ccf67251ceeb1c1d661c5e2eca8f75785337c76c524779f369933bca9b5db6978b864af824e10be731cee3246709e570189525b445453d687753415754d43f08e7446509392174e4a80c6a2b8514926eeae6ac2022473d2a2a2b280ccfd38b79312fb6ab14ba959199574ff2cab692c9ab97bcaaad96c8ab529ee1fbf2499d77645053fe39efd4a0a6fc71bc92cc0ccc4bccefd460478aea7279ff1d1a54d7e9fd77a2ec40d999c14e143b483b32d889417579efbf0383ea9279ff9d273b01304f036931d3e2072d62595cd1224613a178355b53f8891a34d933e08d67ce0a1f3f1982f933cebd33c06c075420b64547c19ddeb05147f106ce1bae36f63015fc17f456f8ef975ea2edc574b18deff53c7bc37f310dffbdbe4bc23f73c3f7ecbb86ba5e1bfe705f8b675ec9847951661b3f3996fc511aceba8f53bc431d7e3553f03e0d37f86994a3353801f81e8573561536857cba68fcc7dbd336656b2e20204da1e9ba166dfbcbe14c4f169c17959ebb7ee8a8dfecb67bb35d1f9b5f1fbff7690df2cbe1045970a632576a6492b63b39c122fb3e9e6d447254e6516314199dcceeb6c6db79fe45b2a71c8f0ceaeb94bbf794bba75c8c7dd2c5e04c47a4525a81efb12733f30a9ab0f1e68db7cfd482536ebb98f72937de50b27cd2ccb6f28c5fe6d486bbc71d8237ec7094995911c4f65a2edf0c61b24257cb5bd4798f482392a31eba1ea3c8e8f2be211a917478441aa3380bd4e90d5b17a1505f9cf66f4a5b4a1196a4b5d7da8d6fc9b1403fb5c2bef5fb3ccff3be5a55189f480002082050eca71a14f6ace7d98b04597196784f3ab6f17d9eb635f57df67beffb3cf0fbb0e77d5f6fd8d7c39f0782a0077edf873df0fbea1e97940087187c74dff00a5fa736fc244b1ce0b369b8dbfb5a7becf68a6f483dad408a37f815c136bced45acb635e579a08680efefe2580cd7f04b20e0411ef396dcb0fd2dbe607c4ec00fe3cf890f7b453f0aecfb16d4b6aabc1063117bff696c71b84a65199f930ce209634f1cf3198667b59784f1d92beb53ad976c555d5cc9f116fae909d0c3e0e7c56456e6d1d0805d0d1248f20c00430e01474c163c5d1fe30f0402fc80c01e1210df0aacb32a499224107f9261e3b76179aab15a6110a481a218c4296bebe2b717c420fe3c24206c55791808206a6711bed8ba2c108c7fbd394d85eab2b46b936846d89d2361b759b77933223bfb6eeeea6641dc1c888f2caee14c735fdc2119db1833d91866db5c163708b7f9133b2f2ccd1aa1c214f7b1a767b01b5f5ddd8bed1edec3c36a5d144b586317dc77f41dc55e86aeeddaae2d97cbe5721e2a168bc56237766dd7766dd7766d9f0abc56159cf6fde6ac27719456b44424be6e752045f0f3c22ac41cbeb77aa526bc71e6e54659e9e8c4f6568c4b344111187ff0948fa36c37c1f7d3d7fb405014a7bc58d4d17bf1c59a460d47fd71d6f807b55e6da3b71586288e57f37c7a54953f15671afa84176b41569c1466b76f756ca778c9e1c4840b15970db4c480c50818e3a397295e723831e142c56503fbbeb7c460df771623ecfbee85e68fb531daf7b4e2b480c989529592072a15e4d5b1189d569c16303951aa52f200f66aa4289350d6c82cc80cd8108c97c894a24c425923b3203300969ff7d140b1af684514418803eb85e8482314a2155104210e3e708607f0e8ebc216daa49007f0e8eb020c653ab02f948b01d01a7d1df0a0fc8422eaca0ebe8abb429a40e5ddc0e9ff63718ed9d5c2d096ec0b43abaa4bdbd73e0c8c685144909534dd58fc9ea7b1c635fc9e5fec7d60a86bcc4f31234fe62b0ea36cdf5b63377551d984819aba2f934d3c549649070c54d7cbdf375da0ba60febe0988ea3afd7dd30fd5e5f2f74d3ebcc902a61eaaebfcfb26597599febe8987ea32fffe18bb2d15ae29896987ea2affbe69a8bac8bf6fd2a1bac2bf6fcac1848329b6ef09f529cc7dd3cc77a0b4ccdbfc7dd2cc31ff42f3a75c0453f32e2c35317f66089c3be65b3204503b66e697cc40036a87e28fa50aa8efc79cdbb82f03b3d3f1664f484ff76372518da681d1e30eaaeabee8118baaba273deaa0aaeebbe8714a55dd163de6a0aa2e8b1eafa8aa7bead18aaaba6fd223aeaaee4c498f38a82a538f5554d52df578033d4ac9e3adaaee8b799ca2aaee8779a4a2aaee8379b44155dd6fcbe4b18a9aba8fca231635753f5d472cea89bbf7c79bfd7bf578abf7668fb7db32c5c69b2936de4cb1f1fbefbb3fe0febcfefdb40b9331668ab9dc4f4d317b8ac1f88802d4ce1c2f4bce7bd2eb61fba72c1e0e87f1560263c4c18434de2c06b3cbce84745b2e49279909c96577dbf677ea82b311606c77493221b9ec5034fba9cb2e5f5955d97ac1ec62c56577b5df1c45ed300fc88a93d66cf78abad8c4ae8f8f2acede2142dd36830887f06fe9c10ca2c5046f769863622db8db8a91c5b4e06262b9c7ff31316fc88b79de901773d4d39ecc511a263d18ed34a6252676539fa6a9c5aaca3f9455950381b7306648c7c47270da4f636242dc594dcc3b223f8bc5211d4ce09361d6a101ce9ab1abe7e0fbbeef738c2fa629dd0fd10d56b711def3baa38044dab534ffccd5058624f9d5708ba1e761ec79187f8fbd5cba9f241c92f9bcc84f12bec6f7b9a12c7c954fe6a1243eaff23cef3b6be596c7967945b256ad338f9bbc24f636f9912019deab537dd67d7e5f1d6d254bf87bb8a3c83cb4e47bf2616cebb329ffbfe097b98eb97c92ccf5457e3b4c12be868692ac5ee49b7fed4a146da4a67d025931c68e31c6d856b2548228fd654c8e62ce55f5a448e6f0a6ce0d665b55a20944ee4fcca1cdb449b10c4d2032afb60522377e30a7d5b66dc942c3c60aa8b7d0cf16985e6b087c9c4bcf86cd43dfbf2eb971c6796809fedecbe3c6b7071f5d5bddcb62b9bf8c3ab94f41569cf42cc790bc59b5c73cb44404ffcb0c409fd8200076c8b3d1ef25f081206a19806e05803a043f6c06f59c4a6b7ea5a35fcf065ff44fa7e08bdfe38269a86540b77d51d49e982b7e319727aae61b7368869e4e57d858a7f6f9419102fc14ea9da7af26f36be9abcb571816dca2439639375f677cf56e1ad8f56beafdb0eb7b3d54568fd1cb572ec0c07971eb02298b1e9b15ad9ea39bbaa9d5ced18bcaa88ff33a98645ca35089e509891c0d89a86848bb21163515ea1c0d7fc0c383a2ff289419b33ce46a93a3e791a9dc7233a7581e33e7a8b905cad4958a4ab452adc4448c1882654de9a8458ab7fe5ba2d8969bb33c1a1a4a3c4d633692de148bc3c4599e9cb3563a47cd2d2a6bf50399476973cabe6fea4a45255aa9566222c6d499ba509bbaf04153579aba1a6dea4a472d26aef434ba250a0e152386603953ca45321e7fda7364e5eb41cf5c770a97de903964cabcef13ab144e849cd8c2af2654136a2282522021848490d158ce28b390228410099909f94188cc5121434262579272f21201d4578c979187fe9384af9cc7c932d42b47cfc8090246e5b112c48b3066e8e484e610f0f54b9c697d81ad881748d9f75fb617d2ebd6e3646441594386e6fc32f23f4e2ee1c9d490ce27095f32d0d71145c74d07153a703b84d831db41db7104bac3e8751483c288504f3042edf1a2dab84f665892a3337388f7d2d1b05dedb93d473aa2e8b8e9a0a2b284a0881fea88b2efebd821446dad8cb063765babdd0e9ab37a8c761c51593d4a843feac0815a070ed4816bd181fb747aeac0cd7618edd791c5b1d2bd9cc41c8961144f34c12489cfba6c3a6c55d573e42cef5d3717ce65bb4f9665699a2c366860e366830a1b34b0816309c19ab168ac2358462e27366e36a8b0816309c19ab168ac236ce06ce06ce06ce06ce06ce058462e272e2732927286f67d1949393167796f9aa5120cf8b9b8802e304fb6e41db22cfb9077c8b35c838c9471f98a59cec9b002337e6096651ff22cd72023655cbe22cff22ccf724e861519564618d0458933b5c9d5684633229acd957ddf666793c48222f44d268c7c9309d492ca43434b30f8e58c0c2a460cc796d5ee4a92a356765515c48b3a832ff6fd199a0c2a460c475449b66025ce7426b7daadac782b7f3a5365df9fc9cde84ca5c9749a40de768119c78fc5e5c9d30b10eacbeee58b1720ecfb92746154505330b50b6373182878761ad268a20d0cd2cc14cb5383bb463d4ef20c2ee1d380dae07bcfd10e4ed80e660596b4d2ad7ef09204a302989a0d068aa363a8442c94884d4f3235230000004000e315002028140c87c482e16098c5a9e63e14800c759e50604c2589c44990e628850c320811430c10011010001991480022120f92c65ea8408ccca20b1782545c24585948f427c8e5540bd8f2232ee5896db717b4d7005067efa30ed30c8dd893e884fc4ca58d4c9c8386f3e03e87b42145c827c81754e779e6005c759e1d83a88b885bf19f70f1e52343853a0caaae039e1f02be8f06c17b22cd0ce41bdfae8bfbb614afde5736ada5c9eb5a0b394a4dd0a73185179f8fe4f02f42d1f684b3165aaae8b28a2bf92302356ccbb719a0c8150291e769298dba26c28c847407647a5c53993976bebd2f30406e5c58bbad1ee22160476e8ff24ca4a498d28cb972ef0f0fe5180dc53b1614afd95012fc636d9f00939abca5e497561b7b5a2ffdde2e8a433c63daf3d161f356181a74ad6a2d1cdc86a7460afb30de5c585ee5867a065e79592c920f53da03816f2afd1ff8fa770e466659c2d810bd5640d152b79875791c87da751b0e28f289d40b168b4f5225474227368341d109c4ee473e57ed71f53a56b705f6498b4ae2751de9819a700e0fc82268040bd1da2f8ce3f4b50e8d35ecad9d7fb943e4221d6d245d27294da20d750895a457cec51e4763e4285e6df6913bed0a22302cf50f27dc8ffdfc549bf72df70845a5133547c1746ddd174e6757ff7fcace2c1243902f6cfb0f6bce1358baf03888b6a61f4cfa39874ba63adb0bf5e69994fd1381cf36b137e6177b935301bea470f3234a4fa2630f8e2c7d706c190a93113ff9a241e171d4e23cd2d8e216330549a73615ffc6560f0cee8d8a706f42e10881fc215531a9c9dc1001cf9a9161cb24d328f4a711ffbb1f41fc4e83ed63d5ac24f7c9d5d6dc16a0e9f32986716fac312b9b7a964d413ea799c9bf1997bbbf0f992fbff27286092f741fb8a4fa8a3786b72ef840370b32e8c538c6879337e22ec35f8934aea346220d965de938d23c6ebae386d2f73dcbd5883b02ddee7c894a1b412d38b094167cb0648e44740771f4899ea7d4aac7089857e36c5ea4350326172fbe5c702ca3280c7607076a3c450bb8d1ead7150d4a308eb10dca6ee0264644322edc810e3764bd39c18f7c0128f09f0a0801facae51dd0d47ee41f1a8b54955aa1c87abc10301f81375f329c1501d0fb2b5ce4a264fd2d7fb09ba7e775a0eac327f9046e7b22a81afa17389aa5dc0ddff45bb761224c45680f3a970da64d6262463ed1f2a37f582e980d058fa32743dd518b401d7be9961706257b96f49156898c8d40e1c142f384c0c1fac2ed63e0303f5fb351b58b08a87d741d6ea8c52abf6e9a7be3b045b98595a1484cf53fc9d0a40e903afaa62dd825d09e0ac7c905bcda4088948eb9a53b5030c3d68d76d63d9a0c510542941111374badb2cd4b90ad1f736a91266d7e987097506b598f2b793c56b996e8fdacf8079d6d5b4257e8abbd45e09222646ceaa0cf05bf5eca8d904c9479c7c834f41c2074bea7212e0b96824d27b21016d1e4e748712a64f28a75240ef8338c50f432c36c6f6b45378064b70e982bac4a8f23c9ec73820aa423f72e4f1257fe502ac51f2f8b2d8beb4bcb1924cccd1aba2bc8b78d1fd4113737eda4f8431d6e4a7de245779bcb6e389b054e74e90fed3f937fc4658b76fbbcabce2928eaa5ae64b87a8b59f274fb872bf55fdc7f46720f36d019c6c6d3cd42ef6dc1a9ab100c182f0920eada7a540e9182cab9cde0e82aa11578435151c433e75081b602d5d2b86ef35c7d7270e751711f2de0799d1c67b1f3cec6de513d9a0a5733951381488ad08259875584eeadc72592f072d5c6f403a167558bc5e73fdb890de45684d2989522789c229428313547102c2406397f6693c4fd9d60b158843e7cd0e514e5e47942c01690879e32d670a8b58e01481bad0028076eb3c0349d0247e8324e651b5857814617da69c3adb708cda5b4c95952807dbc1ff1482a3d53f3a1391334d3dc78f4d9dd2cc90f1a447f6ac6606959cc40aa29b28eb60c266354b86857e9066b9e2eea26fbcd01cd3b6eae4f840589538f34fdfea0e6f9726f9ec999518097363505588bcb446c23d93a09473ccf020eb611bf9bea56e7b90758ae2038e259669402bceb8a7b6bf58225d7c54c9bcd0c45e688ef6bde77f03c571cd911129be2230f9a26c3451ed8ca2a527e4ac89f4e6d03f6d446ce847a2ffa12182ee7f34f4422aa7f1346ec9c09a50110c1614167b99d7b2654cbd16c144a8da3ec25b5992873cafaec624de81ac584eab221ee7df818c70ae53c102d580fe06aab481ca209d55ddf0aea22b74fa44252354d68b2fa4821dbac1e445be670fe733d67aa2cd2da72769b632828ee7cd5843a530317bc1e409a832d2cdf3337996a7ce752c23cae3c69cebd4b65ae2930bdf8c94a62f328778b4ab221ffa6def8d5a19527fb14c43961646cfc9613b21e172a997f024e30137afc4ea15569c7bf9265b9265e91aba7b9a18877af28dc84de5de341dd79876b70e3a30055fc7ee29ce9416f19105c13eab1230146acf0921afcd16a421bee0a0c39fe209b505e6a780b76593c0134d021818705c8faf5be02641e61604cce668b8c8878283694f9bf84ebe8949f90eb9426563a89dcc1167ffbcffa56026508814de4f2199577183b2b5f0afb16d16338c574a0bb7e851af60ea8abfd4148f58e85520ed1aede4f6305b5dc17365a99c4cae2500885688ea3ad48cdad5a38cdad31613c0580b6ac35993b9449a30a83ec734593739eb4d18b1542ad244499741e506741213bbb4ce17fdc85117e1fc5ebf636b8768a65fae110003078adc3442b833239f3c0d1613d08b7d00aafad83251e06fcf8eceaa72893ae5068a6a667b524f53d01023d5ef71665c69bc6a7eb2466a69a1c848b72927f3d8f3525694fa37c1f6f35c727150cc6883fd261a4e54383004945bf9279efd9a2cc1814aa0a29f277e13e640082df3e70939117639499ab2e252bf2e9130dc8aaaabea14fbba535d335c913ff6c30a134f9c511c7409eee04d7fb3cb8e7554e0d19d6f80b6a11e6afde0031ae6058139aafdce3095bfb8a7c43e3d8e3b6024b8f046e054524d5090dc6a25e10cbdebad237599c1acdb89865e74d7d5b5ca62897dd09c33c485637aa7359b78913855254901e342870337e5d847a615d2743293459892bfbd302c91bb5d7ea0c394fa19412257d6cd79c88f92d0b9dcc705d7172b8e0a6ca375185ea39f40ee3b9b4dcaf84517dafa7aefa97e20dc7cfc345d3acb9d7ec2a322f17c65dbe6ff955b902a1b0b63f04d21badb623798f3b2ccfa0b4b3d0a449d02a36e6c099311aa1a5356488c2ac09dbf14966a65e167e9ef0f82cc2c198787c662aae973d9aa7e5d2711932dc9469f760b1e73ceced53b990ea825417afbab0f2c54f652f0c6a71a043ed96533e1ab5d6a6e586d6b7b4ec2d563bd83c7b77b45b30f9d2d4a296965bb48c2da365dd79bdd9cd9d44be5ab5d4a0f56f186deb96ea1d356ed0f86aa85569596dc166e99d6a56557e912af642500702451b1c3a8a641a40d4b3cdc546e4684d6c98354f90b9a6d24c66f5d121a0d23aeae3aa4073feef1bd598b99dc1a12fa3df3a788b12cc1f701b7e79672a2ceefcd1ddf826d7f5d9f131e21c48c5351246c0b2cd8d3e68e275ef0b3826b0cbf99d454c3ede7d2c904fda781554de942960cc88b00d10f45c384c56250574ad766339c1924aa60b6925e28b13a9f31ec4d519db70c9990f8e56091c70baf19bb80ba19415ef15f2ee4b804e6705e80a79fb12a0d3b50234426ebc4da316930cbf6175f98cceff1132ee55402bfdee9f001977155091be4d28052dc910fc32d88d50ca8af70a79fb1242a7b3024484dc164aad16a9965f5ebb3fbdf77f0c1967151091befd20352df4487e0d7623815ef52ed26e38a5a0ed1c0d69dbb4c4cd0096ad535a746825a71fe7cf0c913d8deab8aea323685fb6dcff5d155a4164c78b61ece0e457301eaf07788458b6ef4cb86e31aec7d2cd16e400988bd3f704b1040e542f607bb9356f5368ce26d9078bcf361f2312ba4d5430a17f41c6fd60e369d42137544c418a3ae565d3643f7c580e0c6e6f438575b9de3cf95a77354af409f12c225a7f7da6f35e17c5cb27c4f3766e80d834886abbfe4e1fb8e4b3b7798f586c1f4417cb5b8450c96a1dc9a5dfee078f0bdb150096addaa4f06273089e520b51d3d05e84438769f449495e4c405f13fbfb722ad8bf3cefc1624a47863c45dc6401315459a4c8dc2ff50529f7688b077593a15a303559f6f3d37b7590b8bbdd2a2a76007e9a446827981428a910ab051ec076eb1e08eab6864e1645cbeb23ca07290453c39be3c024dd04ccf520712306969d9b97f04232049e1cf68c826239667e176afd8a57fb965386769dc26054ab200140befbd15a5ae5724f8c4377ca23f2fb988b8b2818a1588a8b8272c4146b1701445cdc4f5021e7623b0f9a4acda4ba49a59020c2524813932b6e774bbe6d663db2672ea49cd385a46e23919a1a44f455c98bf4f91747a4aaaf8d62e5aacab9f45296e080d7c6d66810d420191297a5e1ac21b0ff078a6f23a474e66d9229e550b34377375420069b5e6a411e6ba1ac9d0430c85b472d1edbc42cd2cd6e48bf633c07988b58011a9f8fddc36329391615a045533e11472766f11243ec8cd0f6089df92ca72054024d944128743f6138bc111813cc29d4992bf05e1a9cc7ca5b46b3d1ffb383439bbffae5d9841652807782cf601a6e733f9fdbd9efe7261017b51bfcf5c11f39dd3248fb02d4e801d51ec70c1c2ad8ad2e29e144cc89216227a2486464962cf6ffdc62fffaf6e317b9bb6e5023bd0097eb647a701632caff8bfa6bb09ddbbcff7163c319de701fffff86d0c513ee6cd132c2647e538ebc1132c0c31db0a52bf6ebb831c8a46fc39f9ef00d48f5da2ad44b88d9dd8f2b87bb81e8983fbc210dcd543fb70387578ef79efce2a393b045d882aa290c302139f691e9384b39c72cf0411b171c131f5f418db2d627c41431c564cc7eb47d503c3ff5f6d696e3a7082aa3535df0aaec066fe3e6530129e492360b4bd977e81cf8c42b22f5aa05c5d51ad9a9d17df910425aeed669ba1bf9e664dedd9dccbdeeaea5c7fd2a245477f9df4019587f1f56efc402a09352b9b9d993565db0fe70a2bd48695e62e114122eb7471f844bce29f609aad47d84fbfd6c4a249ae47df797f0d7bc0feba557ddb6ed132e403ded5e756800090eac71c8e120cef3cfedfdb766812df12937308417cba99f69c1baacd0ceb4772a797b0d537e0cbf4a1690cb2f62e2119332445884f32bae703e2690cc8184721c32e706e08ff97ffa111cb167ccd636fac652b4151d75467aa91561185a7d748b8bda780a7ef762d05394a54789114b80c18358f6317ff5c170965705fe51efaf937e89dbadd4b4f82f89ddf985bb3f70d8a49e3eb8c925573a9b943a1d7a5f49d3abb8c07e8b606e4f1f045d64955910600823431837d83eb469136dbc05d524cfed658ace053fe25bd98e5bae38fd2cb2182c4ff10ff725432d02c3297f3e344677301bd2a12e6b738856d3bd38808531be4354687d61263e6bf410281b71daff7c43d01dbcd01303fc1969448571df41f2d0419c06f17cad9063025c8bb2a66ff3af718f07b209be4b76e33a6d72d54769ea8e46ce144648d8f8a0667c65cb98943a2c9cf6b0bb17382230b7a8f00a2558930673c57bab4e9196c152b70177dd1f7f936f9cad061802051536127947436ca26ab979f47e95ea6cf703f6ccaf84a89beff47057f452160c75c3f1a76c2082647c14d4d9e1db6c8a2c112967bb2c051f7f1929f5e390bb693bada33eab7f970bf5c0ceae0a5385cbe0084f562a31f0680949d962ad34aabd3c77c9c78ddbdc6039de6b7b8c54693bf8575c51f8048e1bdfcab5b44afe80e0dbd73617db245b270612a7242d7b7d34192f3261787ae47d23a689997ad86f78733fc5230c88dd130a357c66fd9f1f8cb9ebcbeee625e81b870d93b1863b54eb7d036bdeff365b5decfb556ee263c7572b94492751d39be701e4ec9f6378604143a32792e9f7c1abbc0464450d002a04cbe7903dc3f625bef02a4d1394c7e770cb8d0b08e858e7c5aaa84594dedbdf0c2fffd478ec9c1ab737d16252d2d8529c7f4a6f94a00ef53796dd1ca8773cbf31448c6110c0be64ac38adda09fbf20f7b32e4da68b1b5e261b5002f66a6ef8f2493f511967c6a096f71c15a0677c2f106e464cfee02f41e4b44c3edc0f631e83d6d34b1b00d7e0ac8e80e37d778099dd13110e878463c141a57a197cf6d9880496dd70df7ae62a1ff2676069a9d6d7e1576e1eb2ad80092a15d22dbf1809832f913acf1a7a24642b2b8b268550b57371e49f8a4361476ccc4cd6b299dd908f13c3c6edd04d2db32965906123ae3e51ad349f89742a241d9495b117c8f28e53ef60b3f5624163858ff74522bae9a5de174c97ec5336a4dd4aa3f6af9d9be039741292e5e8197d02f57929eeafa61a90f02d939ee42b0627dc36d21b45ae9c4a58aa9db3bc24a60715a690d5679ccd05928c0d5743e26a6bf82a1104732ee6bdd05f320fcb5443b0a58d7f2f49fe687451dfce15694f44d4b8b26438bddf201d65f214657a426a971b9ba642fd32a9f109d45a184618c6eb59d7cb4fd54b348c9c67e9fa1f84708306f9f328229a6134c65e1da5b337781a31ab171c2ad5c66a542e5ec6f4e8153654c5178f0d40f326d00fa4932508dc9e56ff949cf25a8099abbb3f626b831d238cfc6c98be250475991fb717ac0b45cf5548b1e12b3602abec1de05015eee6cdd8d833fafb0556589462b06c8095d0f766b7cd5dd070bdbee114aced59e6826845837340826ec6098b035c723ea05dee2ebadf175babc597a4daab26366c2bd9e7d8ecf7912c1eebd72d3eee52a85b3738f2ff57d8eef7f73688d4b92ac5be989196d528ed929a657ae936c65c49594e06da8bcd858f78f6691d5bb92cff6feef1daedd87119ee3c4d8bc2e47a55f41945454fd0667225fad0bde18def8c137f61e9e098b023945b607cfc7db8bc4816f9cd324c2cad47210da4ec22c5b7d8e446c2c7d0d3ecd5ddac4360aa26e00fac75db883a013be23466b64fce1133ff1120ca6db92b69ec1934f095648180194e10da07797a3deeda6d1036840bfc9752d79ec4f86a4b6b0f76bc684ce15566a66e2f4552d96db2c585251e382052261b1625dc15efc5e12d50102b833fe6acf5f838463d84695fc17d3c26db371590fda2df8fb9a04d6e16e6df4b7d1ebc15dcd7422b8cf4f6e63d0088c45c2d855058e2a8e89fc07030ac2dd8008a78358abca75fc012ca82388b91e9275c8b1e0c50fac274c3102321c6fb7355e94974a21c3b16433ef051dd0889691771b4dd7be17ec9b7848103e85c413dd7e02ff4ab74687c2a02edbd6b0f56ee2c772778e501cce08d239ffa398facda411ec168d1beb7479c3f76a7b15899c4759211d66e8b478bb8a73ad3dd8bef060bcbfcacb5971643105f2dab64d51b1e98908f3b842232fcc3937e64a61b80a7320419d96c7855defc5ee5a7d62f1f412b79c7b2a9838cccbb82dab85d11827e8e101b432010db22a910b8033a0fee90cded04ae4e239df7ff80a20794dafad0bd1abd70754f5a930f59c2e274968049de341f343875c80cdfdac6186d41acc56a2960ebd136982d68b2092b5af9543c176f2475955bdec8ee5d01d9737e5716d9252a080674dd5a5554223e3cedc3adf51922c0aece51f40fd0e530633b967c004e8ee66946a33f0672626bc97a9504a528d93a53c49b11aa2cf2a3c58b889545cc1dfb639285bd465a97a0e7e97da54fb2bd176f94a53756f41060763cda4324f123a8da5564dabb23f010674665bd115e0affa108652a808dec5b659f68ce5230615a6f1772d54b09011ab6e7da6cdef5d544bb8908984af906e8c757b911a1a7f426ab89ccd47c974c63765fdc732d6a4f634c67dee4bf0d7099a61c57d9dd6afcc98309386b0b1bd9d63c58820bf5f27b7734ee8fd7462cdafdc976837f9818e1e5a2df8b6e11693af91ee4448f2fdaae377ec7c0d7998e7726eca0b2a3992fcc964f15ffe04bc0fb5af566a6033d51ed83cd519364b55792e62cbf685501187ebbeb959f73ef009d0e7b203b50eb9e18f300eef1dbcb8a7593ff4e63c9d2e9cbd527ef3aa10ed38e8dc10f352ca22c774c7e6147e5948d7bfd49c8b3ec82fa21044b2b2d29c7a6291a6c6e1bb382892ccc8ee68324cd5cc4c001f6e4e2a8571087436c3db3fea489883a1a40f6cfd2157bb0b526e7a348248deb0f55cff88dd712be4844cc812000ace670a6a8309a5f50305e8f668c14d4f3dc2006102b6a25d369069fc9511ee342fb8392d41f92d56a661ca70cc93b5cf071e651d56560b57669307cb1f5298e083eb7aeea4fd4ebaf7cee4f8479101470974dd48e8015b067d37ae4eeb689eb184514ae119475e5447c44f10a6b4d217218f1c0fffedf2740e89dae1e7060fe0c91cb2e32b8168c5e90660766aa0d5845dd22a07b2a1a1abe41bef74c33cadf4f2c333c52ee0c1834d82dbbcc3c03910f56db16315d2c9b04faef617915f7f9b34d8ce328e9884248ceb9aadbdacfd7f9ccc1cb35707605bd9f4bffe26e30860ab21406b4ac3177e069d0b1f977ead92edb0217470c793f181d2c16e3e30f5573b0617481b371bdb75ad2fb3e6a6790d5b8713143297ba8f86f3f44afad7d32d0fdb3f0bce6f43c3df607b8ada081b0b7878d00c766e78bd181acc8be73705b0482da641a053c69c55f8b66425f59d8ce0a4c407c9effcee2e2b56b708029800f6313118d3dc4736ca1f495919b49cffde430cb341a39207da8e871ca3f61445a078568f496679f953fc1a966ea0fc07de990cbd2fea6f3e17b1f5a63bd55d6f7ae13c98890f0ece3f5a307c8a206dcc831e16a88be91b8adcd645264242b384ac96f8c96f9c0a1206da0bbbe9873016a98fa29bf74cfadcdb250774283ba188e06cc899d536b4aa64a3d85489a39ec80c66235a495e9cab133dc7876c2edca03e643e9320665318341c4a7b0475218614bf22a1408a62f32e71cc64211d2c8597f55250d98f04a32648408cba94a3644b194bbe525cb625ae8e825105eb7738194d8b02cf5573a3a435bd690dc2f84a9870316c00c2e5a721b6edaf2a61a3cb22e1099c5b23847ecbb7cd6eb14ca5c1a1396d5d59b64c99478d6fae5a84e4ad5133fe8f96713f0cea1dc8b5630917d05c91b5c0ab6a80fced9c131c11c092c7cac4e60c31ca1d46f21c5b5460096083f40084f06dd038f278a35d5fd1f701238cbe7af3a9eb857039c42c60ad310bb8c1afff5bc1f742ffd470f095ce17b75ba6c31414a62ee57bceae9c4e203713bb333b93cd6d087bfc962e447bcef928f1047ea7da12646d03d7084d651d27d29a43ae7e3552082e87fa75ca1c5a3e8d7124b53257b5891001a6058403fc07c5a86361855801c1654948cdab5af96a7934a9003921c28ea1e8e2cbd4b64deb9505a47075e996bd52150da969bc617bcb2986c5c6ecfdcc94896a6ba67b12bdf73515aafeac8c2583c5888e16387588d4e8e159a57f4e633e08de6b0792a0d01286dbd49730be89e5ffbdf74c039b6ee068270315bb0b138a2d8101d59d0213b2669594909ecad893c9966f775fb401e695f5bb0d0b5ad97fddddabb22e77a74e6546ee758c3fcf5fcd68b90f8dafc3a8de1e621d835e58cf8683633c7e6170b7ac3760165588e5915bee7eadbb4dde0609f8597565ed131b7ece14259e52454f7e87b28062c5014020421392c53204e7d12247c19182e80e164c4a9a047b73ad0296cc46c5c0e59d289dfdf003899f38b4a542cac06e607dca2aae7985c5e57588dcd9c7e75b220fe1c4dd9cd573091104e705c51e41408568bb7929f818a7f8873039997fb95377c4861d0945f1235e35aa204810a6a3e0f42b770c107405d55225807235093c3b9c6b536e3f18a31cf5df1c7b5fc85d8382a7f509ccae5e733f14e08f86fb152f9be1bd1e9a320563683d67d16de0ce1da838928b515c4f77a88f9afdb31270e11d8a3e4e8ec1c061d751d515cd15d939d12c0eddcc9f0c2120555b676e70c043e7c4c53d3ad77f3d9f763e6a378b09b8b5cccac7c78b7639a2aeef1c883481342750a7a700c0c7b27d6f77d172ac42f8cf80aaa37197f3a8dfccf888dce5a87acd70a720de0f1243db30a08c5b3211d95cd62123dc5b67649b81ba512fd50b85ec0817656be1c60c14fc67477db6f32d8cf379d26198c915ae84ced0936a8c80ed8a99465b845455a0340a1d0e50c1ec93178dc360a090669ad5b775145a2f50e4588558f8371328a6c30e2b7cc3bc377351c3d9de30d4dfc7bf65c7184bacc43327634d9bd0366db2e88e099c70614844d21c734667bd730c3d7474d7bfba8c74098fcc14cd2ea8a8c0072a9108075ee090466996613da07ddddfbab4efb799fbe31bbcd0041f8a130b0af4f54ed443fa053abefe7cf406eccf71317f679f15cecefb70d0c44d96e0cbe3922c0f3215b7a88a068432106e1feeba91fe1794fcade5f3e73b97346b5dd74c263fdc5db27f2caae98e9dd47f6f7653799daa0b89ff613ba8b8cfb63d1a546d1a5d91e5b505665c940113698c0091c2785d7c9702a096f612f6f0e7302ebde1a0de870c68a3d06e4fbea359f4a817412a08bd04ed1a39e9e7976f91fe353896abfdc4848abd6a447a694a56daf43a3f241e838686f0ea8a60a0e18f9058fd65d4754663bfbcbdbbbb38d041207cddc03f8fc89c067cb1eb55dec6a4d3b17e49650acda4ad3b05f081fd13fd4eb2eac2badb8578c258c05385e5453386a54e021fe933ff6b22bb00fdc431fe9ef01ebff93f717665bb2786f2aaab66fb60114424d0d2db1dfed660cbd03a0e0c7534d7f8ad682d6649accd7af5ce89ea3d4e3f1f8a1917f3aed69821522feff6b899fe2c878e0021949ad8af61196783a2915dbf026c5e7ccbd0d89f2679c63e5e9fdaf56930ee685fb5e851027c0202bc9d23465b621053ef08346686109aff28a56359fc2d63408b5867bdf15764e59d1d2ba538a9aae5d91124566d08dccf6e63c506fd2bccc07d11c0d4f48a053b8a430de8cdb5c2574699668cc63738c3874fe48e8ba7b829d9f4a1c411d0c05bfce420f9da92cf08112e4d5a1142df0504ab64281677ffb48755bbb5375a16559dc06fac23cfbae03f6a54aac7e13a45f3c1eba962d7102a3cb00e2625968f1a9a9d1e3bd8e8d27a00ffee188c67a9a49da64de251d7d78d33105e6ac9953a5185a9813b3ad992887712d64362301a9b8530f86dec3eb1e8ed506e34b9c94d62131f78a77b624777b35f34bb3501b44360070681122e2735fcf493d7b7e70e5e6ba7397e83f45c459c6fb432a46db5531552120ca2355b4c8607ac4fa3c7a7a81200c6777d0c91740860ee34650a9b82d2eefa4cb31d37360e355df18cfef9e495b62d1fc411cc46004d51122f7054040d28da903099f3c738b35e1bf09d69b53af20c5f52717f18b0d97421760c01558cc63ba50382309f46d8604136b322d3cbc6fffe0d3e79f4eb5c0569fd1ef217bd8e483bb9cbe623042ea075fff7c58334bf01f16159a04723a9a5dd9bf83f99ee779ed56f4fbc5db65c0f44054683c311bb529b6742bfd49b329eccef336211ba34eb2ed54e4e901f7fa2b431dc66f26b013494a4f3689baf941d013dc45036d5a90e174077785900815fd8949de5742f1d6931ccf690e842a0071c44858576c72eed601c6045373c2603c0935b998ff1ea9ddff815c1578a02f6dead2fce0f88191003e00a32b9be9046d217a4daa57b4d92db2bcb5fdc0eac527cc3ff51083fcfdbfb6606cfbd38b94661e52bfd19e4b2efd3cb2a64e8796411ca7b2bf5f2c720715489830e4da3a77262862c3e7e956d783956e85cab4183cbed67738c016d195dbd1ad38be0219a383f8c7273ec1f1c1d595adac29988948e934c897c30cb043e48a27cc04cb418de653123c8b99dd3fb47d06491f9bdcfc69597f21c79fb2a495fa7fc99d8b427c597bdd7d4d6084ec05d834e4430ef41104f63c85b6fb2bc7dbb6e7a1c44b011b2d8fa20e5deddc877e8465d34890921a9611d063c7fbcc290004ef9006f7721d2d12973f47b15f2136aac8b7cb1fb601f9061604a174982ea7711411ad2261379def5c1086223372a1db5838692996620207a1c8c975d81136e4a8e76b0fd837792f4763ef78848091db32b79ced3c7d4881ac84bdf90b7a643c26e4a2d47cb874b5aeba314fd99aa759c63882bc05cc88650fc3ce1f74c9a898ce3421e17dd83b26d23811fb065d102797efe53f60ecbb2eeca4b6061a198ab45b2f9751f795f22d3317ad7c04fde9d084ec89046616d0997339c0e737510624cd954d6411c1758bb4a6badeec866582f9666f4cd40ad2c12249cb63d9a6184c9d73a2432a45a2bc1337f93797e1ac303980b0ccbe506372db02753236a293ff224707e513ffe0aebb68c9a1f381ebcc683d21ac55dff2871650b6afdee78f37d93b96c8914ceb9463bfa39fb21b078ba601bf68819863b83f0c1be3c7f16763bb046ce71b2471a506f17196e38b0fcd0b3066bc83e64371eaec11be05ba0603b552c93cace8beb191ef3b30a6030afd207fab8b1e0c2f64edc8daf9e1d6637e1a4ca387aabf87429204c2adcf1781c71537f60c316c43e58b9a9d2944b3a87a8e055952beae1283384e78849851b510b41825542939e8b366c0307728862a73567d8f4735739bd81ee0d1ced0a60fa2f7c2666a4b08b0db0e30f3632de2a2c62315e22d87966a28621d7f5614f876352899a69ffb3508fc2003b0d982b2493b686c01655e0f2326e88372fe6f42aa7f7efa4559eae3b59dec1336d60b13f856b7f29dd556d4eb3f77d5afb28948e82aa3a5a08e0c132d22acc240edc9a5d3cd5eebdf63109bbc806d2cf32f962a12e5c4526eff8a6ca1c3dc7536938c75461f86a2587d93714775556463535bf90614d7363abf5fa7615ece272dbc41066ffe7006a262747dcae2f076fbef575d815af37db2e0b1eb65f58a65cb8d7175581d4874b2a13b541551e5cf283257827a9771b54eb2068b51c36259f9ce6196642b8e5b473ec43be24d479aa6f2f55c88e9e1d1f35cfd8a72394116e4165819542fd69d10d2dc682c4b0cf2823eb7636f0f107368cdd4c65e36aef6320112921d17e1f6783b49526c3af6456742b3ffd83fe58509fe5db350cc424155151d24f9061decebd5d908e3f1cf58197d618aa7970487675f5499fc52dda8211f34e2542e6ec7bdc4343214aceb08520ec86470f3197ca9941c8d4a2c6ffc12ff331de184707e9de48e903692a1d967aeb07930e9572b5f2f8ffe0558e44ca0c953971f513190ff0a943d538398f632a6ad94fa4b83fb03350d1350ac3eb92c50702ac4fdafbc594365d7964a490495b4101b30c3a8e637c6e58045ab4125ce225dba9fb9506038aa99456e75df8cc11bc1f9fe88621a0534697be8244d699600f53dd22142bfd0adb7513e6293cb7d94d45a9f71f662d7868fc48b57b06999d9f64bff2e32a1ef51c069089a1c38f8ba096eadc546a21c9aaac8832f6f36516531ee2fedab5238079971210891b87aa310c5ca4f6ad0009893b0760dfbc35e8f95944812dbf58a1f00c070384c22d17da8c93a29a17d217ea4db020def5e86a38b10d041a1529b36c730fa80c914d61d9b1c4e2220c6ca3dd607dc736f80a55e2a71215d267082887d5fdc28266d7d8405300a9c48b2cdbd5ed17f0a76f1fc84b316c9c602100971d8db7feb76d4f7192f781d9af6883c74df0dce1fd363690177ecf5734ec4c148d7ec6117cbb4d8c54de914c83898b72648007c43594198a6dc5361d1ada23304c80f4c10b0b871ba5a380d26c4fb0fb34aa540ecdacc1c3128f177e8058fe80a0794c38056c334acc18ad3dc2659c46563b4959f7805887b733f432ab5d60120a3ba4abaa80843ee0126d407d17280c1650ed477c11038afc36b79d3ab81105a40092bbf4b0b8047db2da7b63e932b2c7dd905bb4d46921e1bd45066cbcb30e6c7ff16f8d3c46f69f08a60723f21a4fb5759e1307b569225a43e0b41de8156dd45961bf708db71ce17c086358f1961829986f23ddde62c255413384a1cea65a1ff509222266a023603c5107bf83917576fe90431b1fd1189f720b66205213472edf528da8b764127cd00edbc6565cbf489f124345746d2f848e549cc161973decc64c5aac5ece1a7c20df636066132b91ed0298dc86558976f63fab334e8f696b5709f8a62312cba2780f10ecc60129e6b00197a58e7829644c947b532a115e5a482a14e49413382f5775d40a22975f558038284bd82411d2e114e02e281907778f2adb89c61d5a1c71db14c836bd566f4d235ae9628e668727fc5ae374e7c0fdf7ffc92b8adeee1d7d4c2136469f99e9a1a5905eb25fc75caff447d6e407c3672c1ff0921480190b07ffbe1d17f6891468a5c3e9b62506a8e2ba0171594b0527f1169e0ad5a0af4807204941034d207838afdaf180d282217a8affdd4851959410c4832821d8daea6f2356fcb56c79e6d0367c6f41aa1fda35fdd77e3a4b0b854d10e9dbcbc8a37350ebef59ed1f8017c58300db49365c618d924211270dc1ebea342c0b464c2491ab68f490e3ad44eed9dfd61f9cd31fc1f0c3a2d0e0a0ac1bfe88b00b00954e748e54e413dd62c4c335603798ed2064c33d40fdea6f0a10aab626fdb6d2309bde558b619f24f2a5022420c948932385f4e678e2924d45b681f3e9a30bc8ae6a29230cbe79c78849ee466ae9d498135fd9e15a7866550608c99d40e84a3a634d9b8f77b828a0457e2090a63a4efa64ddf3effa56fa9659c69f43d7adeb66c6d11f205eb47edde920803c2cb077eda1940ea29f8b04aadf07a977ca700f4a675241879aec9f91892ed052bffd21f94391f343ebb229045274852868c5b5321f4daf4f80241191b33ebcb51025ba7eb2b1dd63679261cec72297f1a10b5e7c1bafcd988f9aab8a29332d690b90d9b1aa5857cc9ecbd1c8bb82f45b25f8382b272fffe8a4e48125772bd2b94608faed0f5c50a930617cb1d3d3337158a45be36bbcd465f323d785479d574fe02ff610be268ae7b45236de5b7d7db5c726578a335dc164aa144f122c1ee8d7426c13449bfd12218d3ed3c6461c7af26ee4a1578cfc72717dbea50c25f89ddbb70c33db1e2d7f6fb8415144ed27d0605bd69d2111577d5710543fcfe4ce2e15d0afcd8bbb1d28c30c56bf2ae9609533d2eae7ae6e74177b4ddc13644bc6c4053718b0221fee491abd8ce993f9229e4b285ceb63a1102905115b61fcbb79ebdb4db8673789a4d9a9588dc9d957378d7f3eddbcf4e73119160ac16c687b6014429e7735c2c85633bf40a9329846ac7a055818fe706aaf2a0670842ebc5480f8bc0d3a1c86f3d5362470b153df96a18a04bfda7eecaf5c79a5f9b06841ab6e39efd04242ddbd71fbe1e8e608d5ddb8c3a310c3cd7e2ed99c5e01708e18229b69da354365824e01d6c44100a4857d3608cb388811e4ba937ab6053888decc5aad9a3547db46a58a926a3330bc19067d6255780340ed29ffb78dce8499a2b695f7c4f61286c630ca34bcd72bd89b56815f0924dfeb54957a980bb5241a3e04caa17b06748d9ff87854d904e53d480f956c35e6de43fa60cbbfa4aa4bb6983c0585c19c094a2f262fca4f45c70989c38425471b52f63eb3e188816fc4143011da5b737374d16213bdd3d347fbd8c7ca0d8632eac3499d3d1c9a7e451cd01cd1a8fd98fc2ae9e4429e8a1a5effa78e16a920d7897eb49e18e83e4ad39dcbc4fc924bc42d1689e1f8dd3484f840e986e91ff4279935e42ca7352492703f7ea4ab998ea0538e72515663f83bace80d6b7049534a69b5a0748fb2cdd76d2c69066b9db37a00a324b50bc57fd5f3c833e6ee19c2c1e061b7d698141b41cf5381eb09183c18a330f2b76ab8f526a099b8187d9f1c9f78992d0bd38a8a9da8acf47b678ee124fe4cadc19abe7252a742831de7608a6977418b95aed8f421587e9430e7d6864ae19d06b6d8217a36f8106f332c5876a0e8b8a5c8b3e1de19f06e086aa5f9a0a1c1caacb963952f4d88572e8462b0be14517a6e8d36e0179e5df91bb36b868f95a3efab65eb2f980c1fbadac0745ff5c74e0f240669dd48efe53eecf482d480a6ed695e8d74bfcce462617ca09720c5beecf811802c1b308d3ca9791315fa1351fa460f48c23e9b8ece6abeb4b3f2c5b4dcab13680f7c84e15e34b9b2c38c7db980e98cd558dde2339231170efed29544e8ac79fef9c3711f65d9d093c7b525bd85eb4e83dd6057e72b5678804f3ca20b7f62d7bff636ca23bb958dd99602ecb6200c3a789427b782b07b7f1a7b3be54f47d30c5bf0b227c9294d0310fc887a219660106935a69394d8b1f1415016ae03871629319fbd02c4eab54b529fce2816825bfaec22f409dc6b8842d8f1acbdf8ce0d1eb54b7b2af250deeebbda7785a4501fcb03715fdf9445775b159249e1ba5cfd4c5e545c09a84767c1f78b014caba869e90506e9c2e3ae3e39a851d6ea580a8335ca69649ff62f02ce3c3826b508ba723e14a09564a6a3ecd5f7801d64eb8dff8da6622bf28d05e84969b11647857360d9c4387c76845711a49376038ede0e73291e6634584d62ad0a18c0530473cabffc975e01a6c038ab5a4919d73ea94fe1f80baab61f4d74780c2006a6796c926e43d5f1583ecdff6d614b5c0a4e915a1449ee3a94cd2572c89ce660bbd5742248b5721dd8f53fefca4a1c42fa470ddd526438d6c1cc342708e666398378650027364320b34b41048ad0ed60e353400e0d6e2325c5e6bf56ddfb028ff763a0908b8b16b2bff54fa1cbff10590738a520c0eb2703454ae164ec8f816c8275ef89f351fd51e440a0a613544b01440077838ad2e489a2a7310686df138b583f530e06ca3809ea6c2f0512411c0eca51430bcbdf85f50615c903f75bea2ca6edf21489e8a78263af795e3d050877a07dd254a2b56d9c5aa26d129a081b3b9cec1ca4e15065250e017d51436e28b21fc93e33454e7657885eae4f5fc2a2bfec226072d1602e3698fe48d7b7306692bcf852292880fa8943229a4eb063d401c709b1be70794ed32d293ccc05a0fd2e830a5f1ebc01235128ccd80d067a8275d887a3e36e534ccfd92d8858e66220799718015f0a3201931154980c00c951a19525efc2ba01fa5500f0e09a9b2d4e6771e99267e37173ae86077ee64bce38234094102f992e0ae0ad7c852571a4e851633dc3315da8a0be88b463125e394e7d4750cf990adf11f49febee4063f5db833b57a93d990cdc598dfa12c60fd0349d5232e9a1de681f0217948424edd6940c816396504b18849e38168863406756bc67598782ce3a5b4a1c99682168c36229c9b7609e3516a55eb2996cb5ffa5cb7f29a50ae899e5622ba7947c2c7881e2a6e491a65b8bc03b40e1a8bc79df4f8db65a12638ed09c9e2837f7a25cb41f810541e22477d2146681634a50491aa48c381c0ba489d7b37fdcd180b22c0aced3f42a85bd9a05ce0fbc82e4110ac6317d60b9d1c0a015fe605cd7e2ed89c7ad0bd4083e2f02e61648dfe4bc38ba98021804a729d8cf41e3bd56b011b301b582c0b949e48ad6681d7b0d77006a8c697e5eaf577a5183111a305e4957b976c1b3f67834e54291fbee31cf02e9c475de1ff76f4f0e1a279ed9af049237e550bd0fe61f7682cea1af0ab1f25f887119dd2e803957356e4c9ba07333693a1ee10c0708f9ad32b2cf08de2781daf17bee45987d566cc78cb36bf8ecd03e2b2c43b9943e66977e1c3f477c29c110c14fedb7c7cd37445b201140f19c18826d0a7ab3e8fdb3acdf4d8bee2d40d99f68b972a14fa4e0ef98cd90335e62f43fa4a122d71d187f638a446b6fb61a5e5edf83fb49761691c29dee301211762b62192814b1684dfd5c1bc39a137c655f475ae3b396f464150b79ac64c68bb1963990cddb667178f23777a3261d81a0c7d477a7d4b965f7d38e21a5f432ce888f5806e82a133483a071bb1a9f05e14a88488a8900b5cffa7d81abecb4e472398976dc40b3f52cd8a2c1c84181c7b9fdf2d72fa2b15e284dc264f9c6e47dece0d07c120c5d321962f040644463a1f1b28371847c82f7ada3773a0eff9a36dbe7628034631f1784eae6214911194516a6dcb04ad57c83a20d1318cf784ef8e66b03c0e1894f1b16889f06e3ca86b561049c01b4b40ddbb9e88c9439afc46d7210daa36a1a496042a507a44642ba720998f2962438330ce0bd429c44d234564a4eb10741e0bd21176192a17f19ded812262285b93f0ac3fa45b8596ab863601482ed68e656dfb0a94d6e5243629b8445a15d352b218ae057e3e799f4847ec33c0fde240a0435c0aba70b8bfec225e03fb2f2eed092fc3347003c0aa5c1b2bd33d6ee0674e7e7729a8eb0cf2e94b373e58f40e56f278c5c39f83f0a7b1784d99ac35f7e14be34b33d73a873e96a53363986134b898a0a12c80c3f9f21af609fca10b408201a2541a4fede34052b3bbc499398bd81f76f00274fbe6c7d8725edf9f27d6cfbc16824f359ab44cab2250996b9f81409768adceaa6d9304611cea0c2470bc6370bf1d80aa5f667cb9654b25800c182cf19f7ff0073a61e0211c923cfa9158150830060c9110894abf80184856cf20b9e3e094223ea75730445cf45c6f80ee6fe2feb9f290f85cdb74058428fb0d871c1c2fdc242a0fe55880e8b172296652316b67c8ff2e55f08efce65c52ea09d14ee6cc6e14288c0cd417b11a267ffcad965a1458de5bafeb5d83596481b1be9daa41fe1eb5a250b3717d2c613a05e27ad121fb97a890d3018d063c7dbf75cf45b5674efec4b3d414b6c5b9ba8c61311ee8671dffea91c4b927534945166965a835b422b34c4f83597ebb1c19dbf812e10e1619946e60cc5846d823e0fa713022b0fff03cd2b78372903e92d604d321d5dfd2e3783e73afb6f523ddf6fb5d03886bfba75a9711b6c6841946633a154b20ca7109d3da81b715163fc0d73d0556b76f851bca91d64f85e9c36df6040553a9d4d6cb268b4a1b6d1a555ff56d14fd5c2e3c1f4c48072e04d6f7602ebb6766e4057df873c1658e305dc41e0f8fe6f84bdfa6344deda8553414b984ae391595bb7e1c0c05a397dae364f5ce2ab11f2df86e695c784221fa3f433b54510d8da04346be4bbbd8466ac8ab1e0ac0dddca8b9ffdfbe4c48a98e0704d07fc96738449dfb8a5c4169a01204326f8fc57059c163a25db5d9f3222abc706d558d756bab126fb2f76cce13fdfb11a7ed6874f8fd6e2215637e85dc64efa793b650f7c01a8d1c28e9bf9894e02fb9e8d80c979983e54c9aa2148d99b1e80b4bbc673030def5083d0a225fc42bb2690d02d9e7445b34ae1d1ca84b9f94819f60c391ff8179ed1e655f3a369453b8308a5496c9a4eadc4d933ddeabbbfc9f347936fc601c287273d7aab984432321577a9280d28a542bb810d299c7deddab5b45323682915bcf607015ba5a6c32c3fe88719c7518ef61edc653b315f5f17f6903bc0bf8d8391c51aa17d802bc753ae8c12f55f7b341cb291c38ac23a049f8c4fca28e3bc0541b97dfe4361f451fe4dbeb476640de32cdfa5addd7043e5a96c7a8deb7383c8d60b6c25db07503407ae7bac62856e9cc93c98fd8f141c83243683ddcb079d9acaa54e1dab4115fc82486127537589e9fa0ec18af6322de1911b9caf9994aa0caad7789b2c84d494040807580bddcba48d317f709dbf7503c6ccc529b6bc4c3e667acd674d5754cd90d4edd149811dccadc8222271b2a64afe9a80d89b97def70588113b3fcb14bc3e2607b45dc4832f2048237c0fc7dc47dea1518445a589d2d5259b26c206cd28372a18124b2352046976f81df8e360f9e1ba0555315354147a01949e08f9cb860464c6591caed029c6f8077610261d31da195b632d3d9d4f57fdebd73647130b33919ecf72f169f4ac46f2f5bdfac75da3fa73079ccb383521ed7b5c8079a589b9d699240ba9f995bb4f7af5797ce187b464efb0a3364b17ebc6994211ef14a31ff9994fb4c4bf695033d48e74eb6d1a53570b123cf40797e041bc4d8b6744a721b3c03505bd3b4303993ddce816a099a90a93300a0f03899311d923a8202c657aaab720718d42e655a54074db31a324b3b332d40b13e544cb63b8cfc12326a60ad1f53162a52cdac80fe60603cda0bb1b49f4fc51db1206626a74d8185bd0f86b1bdbb8ec2a2176ba3b3f4096b1df9612c9a227e411362989a70f6af3ca60e9243753f3f31943f78d576efbd90743e38ae956e59aad62c209509352ec96e13ace444920c64b37b6bf37ef7719287e37f062ba039eb4298dcc7ab2085dcb0f803e2ccf2b719f1ab28b7531bb60e81fe618bb7db136b108faf9a8adeb6179a11f6237235af8f1fcf91f5b354559865bb9b751b54ec9194bdcf3da27652ea854dc52c02cc5579190a47036f3e6ad9a166f073bc7f2c46790303fd82eb5648baff9d81c1597d408af9a0052bc8fecff44ee15b6550c22ba15235d5af8bf6d055079fab10548131a9da7787871d394bd0fc5fa29a2267a8857890dd27e54c84afa1ce9d02d88658b6c32f50b04e0600e4ca7458801828d7bca0ce3f2843060d949c55637c9b76908c21907e276a092755a1a27ee25c548d29ee1d1862cdb98c17bdbabbcb937a606b0f6adb71b925037311d7ad7f7bd48bbeeb635aa6b8c9264a5e38e5bc09b1c41b50e1f952ff8a82253b79f0868aa99e33a6c89e20d5f40c48aad4aa56c9655d85317f2fec44546c05f44f1ed9a4849d58a59c7b6883739e751e80d7f6fe608f44e21084dc3b64188998100771a39bc6008aef62a49809bb77e6b2ab50b2edb8f24ee837c125ee3d20fce9aa14a87c06138b754a6e60844456bb2e48f4ea54b31480fd8247dc6082e3aca3cd5c48f87dcdd9ba489ce88ec50d7b6e3884c0363c2f8fc061699c600f6c12498cd2459c368d753d0851f912f740c499641c0e9436589f85771a7b2381b390ebc2a853a982f756f944f4f0fecca05dfd1f454f1929a84cab5b16a5cda1b564a09d299509f777e26c0ec7a26aede7ff45774cc6b73b39b57779a0773d0aefc6c2d8932d3c78e68cc3e4a4e00218681b7d3186d5f264b3c73d2f32ca063252e72af5524ce0635b42af380cca11405ce0e31bdd4ca29aaee85be37e66b3b49e164ba57523ffe46f52070f6006bc6cb4e6d0147c474448d15a120eb31a920e4867619af9d52bc428288dbe038b2712e353cfcfc1574dab553ffc68d4e8830f900a6e5fe093f8aa9207f9577846c51187847c9abc15c400d9513ec82b6ef459d470d62505dd331541c37d52fefcb632aa5d9d5091cce0bfadc9e61c898592b383579079f54616dffaf7612b813a79ed1049ccbdd52abf817dfe47d53b98c3322ee313361474bc37ed2d4776a4b860ce25141fc40f473269141c630008fc7b0037c675dc6b9d9fea3ebbde38e1c3dc8e56e73ef0f74e2921c362918386a34af133458a782758e3a1625b9be338d5542546724df5cfff2a85497ab4865a58fc1b2b1f65cbb11ce26250ab754b4357a94769988f51acadb60885b8bc1b749da59dbb8f753d8a45b5a47b88b869c9e177b0ff6c9c5537e4f58284a0cfe1c5d880fed73ea8da27707d8344351ceb38f045e650819412db7d708484804534327029c6a10613d6da88980042851ebede35958ffb9ea436f65834b3cd479dcb26e66ef0701973e7c5bc2ec3c59b3b52c258e0618eccdf1daf2731ea933a0ab5d2b21ef4ebdf5e916f0a7e753a7019a5e6b9db35748a57d068e917010960407f2873086bda417d015e648c5340d01cb5bbd7c6a99e52d46b5d0489e01df48e25edaa9e78640be632eb146031e898ec814491faa36ce021d52c2066e69211a502f78130c9a982b38c27036fb41a5c017f6d432d0849350d7976e9a9e02194620788cfaac1a5a875337b9412a5856bf305309a44de644114b733761948e4c35836a176d1ef5e3c146e929335ee6d5478c11cd91527c4a8ad032df4e0a3e4370208b7af1a25bab27d617f82a0feae987262849e2ee8f0d46b2ba576737d8c2854c44283567550fdf7c688def1f9e30913940738d97786e0cd0333ba941923690ec9478f0867a9114577fe8e236f13db9ec92df24ce7997501f2b3d05e046a572a128ea8690645e149f9540fb1983af032b857701aa3a9de4269799b772bfb4424b606f3faa083c5ac171be64668188a70e323c5e47049eeaf635647cfdbf576576558da395004bc94a4de43a9af660f2411ec8da4ce8163f8769db3f9c5bd0bb30ce801a5caae42031d259bab1348409092bb17f59ad4c67fec39609597d4db53c5584eb8bd395d0f1be1b8282d8aa4eb33cb2b0200c23330d0b22ee1cf2e0a7a56ba5a86b2759a789f01946cbf9cf07c516e46418e34324ca033e990f98c93ba5a53dbc8e425de59486ea6578483844b0ca73bc5ac5c0cae43941e7ab55a0005bc1b0ed3772e4433a7146a9d983fbb032dd0a97f21a2941cb1dcadf1eea8955651ae9ea087fa11d0ca031b5f4bdf49aef4719c7b1e42d4d74854dfd6dcad1174235b85cb60f62451d6c93ff67248f63259d3cd2afe37f4ea5604d31d33408fab20ca6cbe15bc3bc43d45ab22370b909f0e627113e9b7afb155531b50228877160ae0641c944b8f87ebbba0dbef0a9da055f46559710f9738e5ec9d76d875fe762c6f665d09ab288f433a1ddf08aa1f6efc39f98007e249d0f387d775e45e5192ad1d980ff408ba6fa1a74543db084a931e968aeb2e824ed0e5d16c9f6eb11b9fb908fd2a23dcb18ccd0ca0d9bd86f310c65e2035250171be927167e1583bc6e366f6a4619f9baa770c1e8f3b9e84abe12d33356d5ce4662a402c203008d76182f4778ab50adcf362bb118379a790cb2f59c5ff57aaffd91218248868e583051dafa56c369cd495e3d7caae542411704b5c018cd9c57c38956dc8572b018b4261b672638a42b458d91101096a318fcc0f5b5cda0aa2182234097dd3e25761f9ce3f1acdf5cd784556617224b4244fc5859835e775300a22b2efaa8a7af2ab04ce6f39dc3dd94f4dd974734fea6e81fb52bf1e3e750b97eab59acc5599e11b21f1a27425baa2e0ae35555c4809a1d483412a0863c026e2e04bf74140ca53d279114ec4b82488c9b1c63304ccd0aa891f3fc5231d7d08c9192823f38ae0072523baeaa2b0a43c1411fb6f789ea2be149890b26378464efd6211b95fe1d38f2a6727a788a0efa6f35032f454c886e0fae05502fb718c2143c607d000dea46d89b6a8a1c3c87d16e8c1966dc0999e2f4b463ee7128dbe4944a464a2b504861f18495bcffe6a72702834477aa26a142437e11ed34525e440922201b9e7c84743232906ed718801bbdfe38290f5d9f2012b9061e01bdd15587e1e6b1c6bfa7d0cc09196d389393b4bf87fcd98153a598c5c7e2b1e5fc097a63ec05b354ec033b93c00e42fbcf6f2a4595237b026acc466d8869314000b19d148d36503e81480a414213ce00794072a201bec9d7fec59959e45a17464fa4078f43109538b5ba41b26c3d29fdda79abe3f20fd42c6e73a63fa5317cfd903b6a4780d442de65adf753e08c3febe8cd5e6fe98608f3974c7e7e249ba929099e5493ef8c7244e4c524dfa0414d5b7148cc6bef0d6c5d77bad15ee5cedb65e8e4e0ecefe718cc1bee001afdbf105fe54c1e5c05df9cef3612cf1fce25bf1b18c3befc08fa2e599f048358fd926a090f3bd5ec1864f5f07b9c64b31c7291796df30fce93f93307b9bc7331060cd8b95d5535a874b27a3a4f6c4e7a376dd9bd1697d3fbc924f647088e631de6f04777a7981e39b79fb9d25e0ffed6ff2e6e3ee377e117c2ee46f227b309f0c34cb8811210f0f5a313d4774d9f437f0cf09976d2dfd8639a1848c87c549d50589a8b837eb35066b64d517a448017137300cd384cc472deb72a7b84ba6cc7a1e6bb19271232774ae984e9445911a30675c76de31416271f6081676ac9c2a8b4931e6af9811f90a207f835d5cf6d816328a2f78ba686fd3c281346b99bf8d684b1589cd98913c618511853505418b75cc75718b539361f5802fd29f0052a3736bf17290d0b63702e8c69a5c95188fff5c2c81ec1976c4e1f8631f10486716800e75217b4415bb2f92d109a2e8ce7fa98ebd327bb0be3c6f3f69008737c60ec7f8472cae18db0aa2967579a72975f08e0d218c631c71098027caece46e23a21c8877c7cc0e5c909d85bc3715909e0a18f95ddc13292486a0adcc14925a3df876369ae6ae39b6c1e3ed44dc8fa43c367822bfb616fe83fefd3b345314de1d44ce5971cf6270ece5fdebb7bac3ecd3e3dba53c1289d096953305f1dde31d0694a48024644981de328617a02a04ec6f98db2de57cc8f64d320477232d25f1efee163b32268e7f0cdc5934d66d43b82a2090aee9f85cc193b80949a13c9b7fe034910804d068d08e8bc2371bc291019fb93b6fe2757af741076b709315d606f386eda640c8b174d19e2e9abd158d89652baf06999bcc6d7f3f96ddc1a93158e0f3f4d6c9940b12962dd6098915a074fa8e1dacf8fa057f709f0c9b3ebda19861c6cd92c37243d674a25edcaeb8e34660fd19bd10ffe0f2cb2b0ac5f0e549711a81d57d574e8fb8e2e0c3759391bc8af8cb2e1563074bbbf7010f3b0898e880444f06116c6e30c8223bf25a5494aef2ec9c745079dbc8227441e6d702e8ed60ba3d0ac8fa41ca0922a41cdceeb637350dbe72c9da170d46630fccac171f3361dee1e628256aad49019b8640f730797bea02761510d641b63d58db841c45d48743a9e552200fb3cf2fad2e2c1dc661abde067c305f981ab86c5ee3e4b24eea1c6ef0985cdcf3cf41b3f444e730ed806e4f50524507f09e154bdd434a1a7f1d7edc71977b32424ed6cf5cbe53805be7b2b46b5efd2aa168b73551a3b2ae21c2af07f120f487581b8f293db080841850abfd5dd4cfc3883c5f070d2a103b2a19840fea2c15c4e6185e0c50468650d7e1a5213b166341ae396effca26efe5fba0b1591f34ac7acd44ca56850d4317da70a6b27a25db2a7a3d76a4dfa46978e52cecc47fb31f66ba817a817f09c765a2010e74d37f73748777899f8f389d8764c4226a89da1b8836b11a1d4c78ad694349886fa84ea996d2cebb3a94428a233fb8d008b3e4f4d805ee45afb5967c5addeb62e62c505ea8824ac8c8edc4fa9f9d3c89dd85a8c80670a1eea247ed6a2cb51c5ca163db73d948f534a2fce8c32f66660f921885e76519d3a52fcd0b6ce2151af916a4472f5e9f1589f50c7b286496461e81838a2340839f7ef8d5f9a0604d1af26491f37d698df1edc014dbef3a737042ed1402603f543d94bc2ad8a2eca448374585f3df88b60f4d73fa8c4f31828ae0b6848e29f84c8356acd48213e200fbb96ad367540155d2e1f4b9c8146ea790221144b8a8ba9a26d055afaead58f6652f7a049e79f18bbca56407d5542b30c50799bf93267d5a9100ddc8886cda83ef8dae308ba5442091d52e8878f21fb0ad6fb94e612d72b87f5f78b68e6cde38a30549accb836b9031060c5b3e6eaaeffeca1b83e0c9b40e09b9845b91872355afc4e186190b527efdd97217d7c60254be40fcaf9552c96b61edfa0ad4f38f240b3b42293158520066ebbb9886639e3bf33366b026b216adda81d9588f10a00edaf0670bb3a17e8d1493a096fc5f515b63636f3a7f2a4b8de1e39fd49d1db0fc2ac11d3749e709a7ea44d755f3d4f906fb2b370e05c6732f4bbfa7294364e717d86dca5523bda706f8145f641b6ec6f473373431fe1c7ce225888cabb78c705493543eed0d2254b634a7e658084733d699c7d2219f238079172ae4f856601c7116b60ca7a182cfae1a7f908e8201d82e84dd06de91f169ceb6f261dc6dfd77ba0a8ed7fccb9073a7b0ebdefebfa3d5c5f8560fb1a873ae8cfe05825d73f0580bcdae713abcb7a60838e69bcc4b1a7cfb89b3d801827a16a93609cd18a3c0de2580d0648a91e346d1699a4d8ec818109fb52f55b86fda0a1592bd0ab062fe8ea85ce74ef41ee7ad340c066600f4a0f218ac2f121113aec24ee290692339097d213e7e4936255385d4843572aeabf61e11aca570533f7e36f721e5071c958db5a2c551fe84fd463df254cb79c5830a5dff681e951d8fe873f0487e82f06a67be6f68ba729b23f70ef4b14986e3262a7ff7fb7196a0746a98636be579146d11db6b241b0f7971384e3f698939d022919f29a9370fc41a86d698db8c3484f61717b864bca0ed6b73fd1370e949365d4c819561fce665c30c8ba2bba76b7f653c344aeebcd5dfcadeceaa27b237a631538f6333baff4ea6e8dc74a0212dc616516da207b3893709f2be75371b4903219aa62bc0547fc772f70b6e6a63f0e321a722556acd33b1b195f98abb93e53cfc6dc0df573da09bcd5370d5f78916d16c709789c266ba85bb9cbe288840dc6bfe5bc85c7682871b63f587d844a53ec048251ef03c37dd22a80456ee35a85753917070770cebbb33bd211831264ddb79383a5c1ae030c1d6f9fc96436e04a8a588f5edd9f4673bf00a8c5e4425a2bd387c9a360a4cd890a4ac48374717851ccd7a449e8eff4b2601598659bb504c8824f6f620996b7b7b996144a7eda7b5e94a834cd402cde2c3ccf0e1ecef036d524ec0b3445ac7b38a2e6ed2eceb2c1a76340ca13604d3c11e147edd8be376504f60f45ea67aba02167c3ab5ee6eb4dd24482e1216769178fe8f0aabdc4ee8f2031ed8d57c49d32b6d758ce79b95ec54bda58a4f5a04a40ce5734d3c007637bcf308f8b564e91c632803679cbb1d04d562029921015673c298541823ceffd43a1ecff815a87161791aed2477bee8c1a8db7287f311a8aaaba8c04e3417a4656d38b950c19a44a2ed76c8dcbb05bc3e0cd8e4a940481b5251ab0f11c7e1923abde8de0a77b4cb4bf0bec12a7c755355047b453004a09546cd8657009a7b036b3e9f0036516083bdb566a6e797f646734f456b26e55c852c97f3dbf3c501effd66f468792c7282f81b257cc5846e06074cd616d1513af692d2b0ae554fdee505f270e72ec0fe846ff10bdc388a0ac1078021a55371ff54f2ce5e3d72b756984005fbffd905d884641508052acd19a80086495ed7a8a1dde0abe651ad4f2325da77ba0e98e8901dec96602c29917e16a496782f9526bfab7084514bd974682a1e57fd75404f11d18f49fd85d028b83808276237ad8b3c79fd6e774c5fb37abb0dc2d8e137c52f5f14631c8e45276783d20a36ccfd1b111b76cfd30d597972a87db12d6286b0d81b938056e49bbe116f2fd8670365d94000ff3df150cd07c875e541e7dde64a20c43b982d3823f10ccf6b4dcd770669003401a8633dbdf0b5ecd5f5231502ce7282992571968b6f094754cc4c618841b553c1dd7490907ff4a2f89029970b4896171a405b97a836c7261b8a9aa647c2f22c37e31460d6cf7a16134686b9963a49f2fd7f418b514934338fcfae10673afda936d47b91b18faa4e88dc67e530aec02c2e0ef32391be8e6868f116856ac1286ebe7742d288209a8626e3c88e86635624438f62f9c8960b788c2a6d41e36b78ee526d9921b18b91850029470472bc9833b103e0fea45ef6d5350229549245c915666c7e27ea2b0580f966fba4d067c12944e12c0c1a2cef7e8783b63780cb831884a581e96966e390d825ae7f4985cfc6103998c3cfd6c31a64a73dea0a51fc87f1f89f3ed5b8a2e85432a1db0a15602afd40e9bc6bd1104b464eb8e7ad6e30ac8022bf0bc8654f4aa8bf407268010ff0ba28d02246770310c9ad6f238d08d29eda1af9e8df31946a0dfae04912206034a7c6b82b6eb8630431215d19e463cec57956843ee8023f6d33734060ce778fd304d84131f3996c7cc2d1ec8a2179e8366aafb0e4f3fc9c075bd24d5580f83b22fe0d5570204157ab6dbb53134f786ee20054425401258e0341c7d6e4ad96963116afa204c4d65deb8bae8ae19c907a989d5f780b64b3325bc24b1b48bda58053442accc479e1f19ca938c010d958c112ce629030f797b6fe79bd1a14d7962cfd164fc2dd83cf9289c0eaa2c802b1e3065c70e4b2b3e853f9dae2d6d4c8ce3da180e26d9255ecedaef2a94a1b9e699fe3150c0fedb4fac0310334e13b39d54aa21fdb198b598dceb660e1634584fc32c4e04672d4ef832358c12e6a0cec7a02c25896d450320b5a292e8cf4f93d8ac6874dbfdce01067e9f0f99f3c1600d5b8f078e47ec96eeeac24e1d5c9ec9e996284213d28e4cb5f23ba6c19068ca2bac95f358a93cbcc6b4570d1482aa1a3f6c040168c7e991165fad21f726ae5865dd2611a03d453c3e71462053162b46589011f1ca531cad81fe5b9bfb96c7b163fce4a153712f4d53659d5dbdf77d396c8fd35458296887dd85d29cdd450999f28ac54e3348adc0c1c1c7040355a1dfdaedad055df36f6efc6cdc594eba382c38911c24428fe659bca54609b3e4a21c2cada6a8f57fcb2c27448076a341e564f5e42b647739fdad6f1b114d0dfbcc1deeb7ee10ab17a1070070e1949792550823bb1c39aca71ce5f469d0e9c97f9818fb935ecf610eeb4d81b8a16e4edbb5370ab98573caf95007848599b7e62c8c2b463fe6bf4f6f64ee438bef454c42b0dc59218cdb0e470c6106dc34aa09c9dece10b072c0a1444cc7ff548ab25f07a58d1cfbe2de246c8be152f8dee6912811e633bd3ad901e96de13e9ea5e9429fbfbc65e259f56d0b6284775b7e000d184928e6a0c225bcf17062b4fbfb827dff6f2b49d27f6ec2e6356c9691d4b7b8b9e059b251c555a18f8bb1317ff37e46a11d2d3bd1d21dee133f08d5919ee9dbb1337df0b9821a41c57888449e55512fc27532971fc3d93f677120920fd7850d72a30f4e8e894141f70b85ef37a70c6b2bf2006829c3945a50cace5c7c4cbfaeebac3fbf8637403c9d79f26b50049ea14ededfd014105e1512338763b37fd7b1189af0983f7bbba86ba02f84043c617479ea0b4404b4804c6948763995f34ec44b14680303339ccdc0e2f09e3cdddb8cb9027d1861fc7ccc1164f60f2286022a7c097f87a57c0786b7722699d45012f87973d6c59bf1851ec474b8114c12d3a5e380144c69560948bc4087668f96ab37e207cbe2bc5b05ae3544b60c56309a114d73ad81e387b95aa46d91ae85dc2a72b5e883e50de0cfe45f49f30e735ba4cbbbda8520dd22add6126cc2c10008476f3618993152e269fc3cfcef4c2ff90b1d7e3712c40f9a219ae1d3b7dd4837111121213bb80cd70c750cd5aad5d67ad58ad5eaa9f553ebadf5e9cc1d197c91b4400527e4b719203f1f8a7077605cfae26834f1c6205d242082d04c009021849d71c539dc3e8d2bce414676b701b82016b7e3102f26954a8d2087bb7d97916b458669b8b306e52897311a7d84879420303ae9240c45a97115a58f4efae82e470a768db767935f297b374d04bc0c2419364d13014fca646459c98897cab151a4128f549e3a10496bf0a40ce2a0e1f69b86a6697e6d0477a3ee0694ddede97a605cd4e51c0ae5ee41c1a15030e95a6184b990e16dcb25e57043d9bda66abd9744fa69342987bb5d1a689272b8f30c717b47f2438d5e038f3412335c97b7fa2e2afc2cc3dd5e14930c37e25101e20e882241486a79b314c0c635c780f21b1125bba36823c7ccb9dbcd4564e7fafd25e954119a35c0f5c0b85ff81ff67200da27d9dd1ae012c38dd7f1a33e1a1b4d035153075e1d9abd2c1c0d27381af03de199e0e1db4212686baab5d9f8d9b46682bbc1192e9aa66b208a3465423065a009de4adda264b1afa341ea36c510c8f4e18d1b984ba3e05637a8a8a93e3544f9288bb7ae1fec08659c92ceaca65c7063578d3d984bdfbe976d1ec8b4489fde6b140fabaffeef9166eff6e3eb7e74b9a6a2c05c1e7cefec3d6bfaa2a8465a834562e32a83edc311774d4d39982fecc4cb5bd3542a32bc956d7d93b55c8b445163fad4f8c565298ed429fa1431d5a2fb547a98de509cd6a8e853bce6831767450a5e9b0c1bd32d5a9b60b85bfd466f52a9578057532fd2489f127ddae0cc0e0afc788db72b37e4a2513545d326788abbc8908b989a4af5c97de1c6d374cd0c02302f5cfaaa0cbf03d29b3ec53ee11c00013872bc72244001f472c18e88f4a64f95c571a5525a9e3637be2bac69fad4a71c09985db81bbdc925980fd247b86b20044647c14d034df037ce46eb34aa71b668546341a3e04e2d042a12c5789b06c9a5df9ecdb3b99161d8d1a7eaf047340ae510a651cf0627e5dd7891b7c6c2a6ad6813bca7c24dc385bbd5148d8e177d8a879f3bce463dbccd515509a82a0554954b55e9a8aa97aada51553caaaa5f43f8b6f652475c2ed7a720b223ddcd1a8822e1890333a5954d15b566558c6e755c71ea8c30bd993af02e980e2d47f81e8ebe81798f56317acdbae6e68171373813b3ae81a7377001d703e3368da3b1856bbda6fa646558f509d2bf6cce64938a31dcf8cedb94c9b0ca7eb406bb704872c43a54f4a90f3f67fa44825a53ce1482779d20c363345ab8fd7ed0077d6a1ce14c9fb648391b2619be1a7136709c8d2b439abbd52daa18636fcf66eb5397639096928e2ac76ff7b666e120aded80aff5de9bcc75fff5b3701528c7d10fa5fb71f19040140993a0a947f8ea2e7ed74fc7f5ee8c510691edbb284e8637a64caf657a0351a3c311de017f93a149cb1457317a95754d6bf03b62b2158d1ae16ae44886afb5699a06529a9acac2ede7849595eab75bf1cc35d5a7edd15759956b14a317690df608bb36d1b4065f5eb0772cdcd149ef971ed651e59a6a2da33cd61444b5d818c9f028d9775c414689c735b5b838b779c0e27b6815bb806b5ae16e4de37634167da2c9f0b30a77bb3e60466c3d5a833bacfb018820341300849841630816010c80003588f07c4ea80b6a29808d1b4518a0e40008c09173f820ea9e2204d6ad4839dc777b354a05755d2291cc354ba668c6199ad5bc81a6fe1c8271c0394be827fcdce20ebdeb19f9b3923f1f51fe5cf9f3d8a817f3c15b9096f885c49098f39f472fe6fc7cce396956f22b93f1db2719787b59c6dda38e17ef86cb25ff22961c622e25d85d29039f4e6bf4c28f07bcdd10de2e8974ad046f5ac5022b7e1177de176f084ab3abb5896f6bce915030cca2693f8974af7d3110d55a4d4cea62d1b47ad79fcfe7332fd0ccb0ab6bee5934ed2791eeed5b53ebbd24d2af692c2c9fdb4ff6ab5c04f8d91b2b25f1ab1ce61806636883a111687a0fbeb117d9f142ec25425923f602f3277b71f9f389ed70f9834148b8319f7cf4b99d9f978bc785614defdc7befc548bdd33b188ba6fd24d29c1347292c68410b5adb376d63c1eebdf7de7b8b7d3e9f579f873e8f9f8b3e2ff95c28b8a479bcb168da2fc21b895482b77b5da320e876662e07a2ba06cb0c6de529a4edc629a21455c6507051be4529a0a7ba1b2f404ec41408b4e3881e303e28a0c4120fc284902786bc549f8a3c232f0a1d1877328ebe59765d4e0442a9fcdda964ae4d3b159a2cbea5947e3e747bf60dc9f433c3d7bcae3bec839b80d771f43c85a71fbc3d2b72490986787b1343c2e60fa6f49fcf6d2acc857e32fa8d08a5d4e68a7d0a61f757f2ae7937dc097410e86d237b679e4671817c2ef356af87cd16f5e9833722f9f339c45bdb6412914b622e1f7c612ed389200d86a0acc4e6bee2ba260e78f1b099629b655da463e8c5a94eb5f6f9c722dd5b55b09a5d95cce56807748e9a999c66f72ad9cbf1c9aa6ce59565d0076d82355deb0ec9c8dde0cc3d8ebebfbd63c199d6e807439d2a3b8c629833d370067b152a9fe9d37d1ffaa06623ddcf075e17ec9d0ea3512aefc35476eb966657c9b263a853e59b557986e17aae52f2904ad639ac6c47946d6e8b2b57bde344ec1c4d5d9ed7f18b53409fe6f57e8f3eddf7617c50e087127dfae01797e9833021a44f257a973f6ff758f2bee2dd70b9a4e4820e7faa777db2d067063ac2918ab72720c6a3e67b5b3320f5f79e83b2b6a94dd02cba775813345fcce51e34b7ce1e503641bf5906319709ba28abf75422f9613c42af984b952dbc4aaceb945a4a7f5dd7e6814c274669fc1c477f329729fdcc2668fe7ce07760931f6627e6627d1ecb3ea77805f7fefa7f2239308a4b49a8fc8751f9f1901fd00fc1288651f97f3c44053b682a39911c25635cd7da75d7da75775f1d8d0b661407bcf3f5f3eb169479e6bcc733b3ed1e8beef188b227741028f401fdf39981fe7101e15741d9fce7cd5967d69ffa5beb07622e1f5c9a5756926d4262befeb995599a599fcf2deb936df71920923f9f97dcb28e037e3297417758096e826610c8650b3bbd7518127dc51b82fefa27a3bfb2ae79437c7e33cc857e0ecaee71c0fbf995f5156f88cf5ffe64d8ede7739a6d44b07f32d804cd18bcc3aecbf2e08a65b1ba4c6b6dcd6d1546191e7ac715208e06b1e06ef5b5926c5dc18d7fef514404f3d545400f5150fc401e8af90a0afafaf9ea30f315c630f4b5b5abb569871b67e6a20dece802bb0989b9ba736d9337219fceb66b13626b7d94f69c50ca18a3847076679851135ccbda8d48f3a8d5e251abec38605ff18670efccd1881f8803eb561c4441a0b1c3fdb083a6837e70413fb8efefc814b00a3139e01f54831468327c8d77e365f897ba06df4ef793020868dc1a5df76828058dec7e5d1ff1f5b28fd6e3f5f8aff4c9f388b7205af691c1cce1c2f7b85a7bb7078c3183690dbbb21412c9fa655dd65db6b7ac655978e5f596551fc6a39e25573ca3064bee01e363620c683962461421b1165a639c516bbd3d7498b28f165443581f007c700f9c1e1fb896ce1877aa2c5c45dec91e78c80c553a3a944cca231ef5249b649a5167fa3431311ed54e50b6d515b3fbc9f6f260d962d673c5cc66fbb1a08b56d982ae98559a6de8125d3193d99ad8936b6449b6441a9dc890e189783a1af2740319fe1121399061944153b2c38d53b54fa5ec5424ec173d9777b079f79dc5d938f79c0d5a81f8567ff8765780f86105838026095708b20c7706a2609a02334a906f060bbcf968c1a554d0c899383343ffa878d84b9117183a95f066b060915a7416f11569f75e3784314a39e77b10bea745163f5ad2899f162e278b1fadf5b75724f72f983ec1bffcfca0a0cd2041d48f2cde7bf2bd13b13d5ac35e36a3b5928ecef03d5aebb3b436b395d540133c7439bdc48f37031f4d9f5c6cd3fbcc2ea91f147eb00469712898e9d7394afc68f09b09c3ddae99875fdc8c3e955afb2bc1bb1c37a7c5d6e2b2b85a7b9705d6ad6cd2bddfe7ab90539672bc6c4e29a190b31e474f599f8340bd3b04e81f86e29dde61287e00f3301472c8901fc0dcdde69d48bd3b4c3dfdc343a07097a71ff23e4b49bccfc3bccf430ce3ac57511d4b2d68bd5566d1b4595aae61f3227097ddf2ddd7e1edf6dbde66280212203861065708034e121207b68582752bdbd3f2d4e23e01517f92c31aefa6fafcac7020aa043331fb9c88b5043f1df57ce221f39e0fa1dba3b9ecaf2783fd732239de6b36a44df272b85b7ca2b529bc2313431376fb7ea24dfd232e49147773362eba24126f222676408c3140c8cb3d456b0f7450e6a6146ec445f2fb4b2db123473d2d490ff6dc3a043cb730143f80a1b70e45858075cf3d180a7a6b7b331075e1174fb33ba3703fb804e339760f7e3225085028725cb864e17704972050a1a8b69b7d6010a8f0bbc1af069a9e96a94d5f4462d79b543e22d3d87cc988797997e79c15acde73ced5372715eec3adcd672f2eb5c62086945a96990c223b8c01162432c9f042862279e21732141103c2bf8c410c2cdb75dd46f54aa40f8b9799b3f05ae9536fafddef464c65f71f105561cd4ac11b025ecb7931be18bb31978eb0eda519bc1422c33f0153f06e98dc100e0a193e4204688e063c14c27d11635c744c3472b79b5fe644cc103f232ea8d9485731f27bc3774c763e10e968440567a69452da34c6dcedd2984529b2238cfda28bd473b5260f330bab0a84523993ac928ee0c6bf1888aad1267929bfa34ff0f247f4e813762951a1cbbb46795e2bcbdef5014a7e1dcb7ab426b18fad25cb97d240166f35b42cddce47520cd39a027664473c992703614d6bf259d19a948f264b29ff68a07c32506612a99ae6d879b7b25de7d2bb5b7a89865bdd3afdcb568ace70a5bdb45824cd70fb916635bb158981fc65d1ed629dfda02cde81b2b00d490ed77d4ba9a4d59995a1e4a83297e3077ddb304a1747637a91648adc7f59e4c6423abb381af1b65696b5b087ed855d2f8b5ae4788ff37814e068c45b78c9ec89d6e26136a4b5f897bdd42bd2a648c4132fd52897036f2f8a1ce397d811ab6fb67e2e8a2d60e28844b89dab5bcf4ebf91b66f8742b8f9af5d17727458c8f11be8e1cd4921c7ea9af0611c26b8f00a105d39be311cadc517259e586beded70365e8e9738645b26ccdb2b0516177493e741371005fa3c0807a2ee67fcfc49cad90022cf8374403b20377c03e56448f27c8624cbc9c600448cb3a1d9e9e87349a612ae753b2d4c42bd2016b707979ebe7236ae53f72de526889c7203516f1bd5e47997fd904735a31434cd1324a93c9970820455a11049aa0cc3c420207f3f3108c483b01049b219a9265011dc272f4e6b37d322f7d25c993be36c609fbf3b7036ae5f996b6392a24d738a3c31cfe7824a42a20b6271e101903d9f0b2a09894c64a43547fad403c8a88bd3a67924794e615d5049486691c9891d59dbd9cbdda09290c8e46424005055955455a8aa4455655255275535aa2a5255391b9de7851063460aa23ac7bc42c0194766c8c42adcd8831b841121649c8d15f80d089d26ac57a7770a67a33f7f6920459faa237db22e166c90e72129a5b5d9367545b7a5f5ed864c2acf94358be4f99e466609e6092c9a3c8f93e7dbf3ead5d1e8df2eb81ba3d4c5e953f5f9bb459f3c9fbf489c8dcf08045fc8f3da0ccf3faf3e37646ec8c00a2300c7f34fe5f98c522310781c06c6b4f03653797eb3afbe869e534c9e3fc5d8b871367cc8f3a723a7294e32168416c20b420c420f841f082f84200841338af4c97a006ec8dc901122491ba1d6e76926841833e2cedd6ec8d010dc4d882442247937fa03f0fdbcb1d0e4792ba2d484d2134a4794922845a15406a5254a6fece0c6fc0d2a6ecc381bf5460f201677dece7081bb7171de10136e33609067c454df664891e7ad89659d8c4828324a29b10757882410f53e3f238a3e55799e85c6d930e5f919a91945f2dc6618c9f3b607082184104208218410c2db0b8b5fb8b48937464b11fdad4526c993a5466b538824f32c33ce06fc3ccb8c42c89b1049f284f853e368cc4b2edcfe668d24c9f34fb436c5c893c6c5d9de07f2fc7c21c0429ef32e499eb7b21eadcd5be7863cdd2dcca57a2f337215f74c381a73c2f99984fb6ee1239c8d7af7e93075363242bc1b74665ade919f7d73ced0a4b3f1b2a351f17535c38c03be7b6310f9a648e9201cac5be908a5a4db7ca4b4be2529f7cc2266094360e32b44fd94f6e9fb7cf5a694b6830e5e3b72cf286ecb8f07218410bed3574fb3cd7591e1bb16e98b39b06e25ce084927554aecde1032d64354c52df1db3d6ca19904b216889ac033c157fc0434c13b356478d9e2c8d0926177b1234b87d303e230d13b4e046991d8ed30d1a78d32e0dd21c743278690d67a389c98794407429d1b676bacb534dadbf7660d8d13a7c20d85eb24c8610c859c9ddc99d6dd4db3eac61de1b68033d0044fe96d3593a18ddb810d6d621019da4094cc81d094a3dc49c5c0d6e9c2cab68763590f8985e52bf7902dbce855b32aebec784129924823a55b58d4aa95352deae9c7ec93ef5582e9d06a756362a41ceec4a9d9f4a2b518889a61b41847328c78ea98e0eab4cdf03b6a6ae64e9fb63877a61771eac497d31aec223f9d984a754ce1b4091ee7bdd82f763f3cb7c099485a83b15fecc9451719e2c01a3a033f135377da049f8426c3faea358ad66caacba288229a6646d3344d8d993ab5476bf0881d64b8a908a7a0538c4828324a293cf470e368bc2c649519c9b9dbcb790e47b709bedfd402fba03538ad9854b436655a8373e68b6be1ed699161f6bc680de2c0189bde504a290ee4420c880371bc8856ccccc8991863a4f469d19df1508be4a6dfb6eaf547f4c89e14d4486b533ceab678a9723f6ad131e2381ba236b5bda41caa6f27d979b2fb7ca8989235b0c2355aeec6ee0d115310356562aa5aa235f8de0e7c2fa731ccec9a3975a009fedbadc1ee0d512f31c5c9a816e8161e3d4386af988621c363f4864e3193e90c9ca154f469a3d52ba582cad029326c9cda5b64f88a4e416528153426f6690cbde1a2b55aef2591b47efdfabdb98384b578d770c5f49a10478271e9cd9cb79dd51ca32742292192e8e4bb068b0c5b8b0cb3c850a77bc7d9d0a962741cddefe160199e8a6a35a9f5a4d651ada45a516a95516ba9d62933671c0d0f0809f57c0b32916c8193e173cf9a36c1766ed674eaba9acfafabc41ec3444cb4060f825b4f3f706b9f60f604c32c7e5998e01286ab40b9cb225c05e95e9f7bd55c1f5cc5e85bc5a50b3f2d4a70163490e2add2373f33197839140aee01c540826818c2dda64ef6023c01204729a4e002670b2436368d13a74f554e5d976dbb606fcc5a6bdb767757d7e301793c251e4fc8e311793c261ecf89c733f2784e7175341ab78be7383aee78ac472bbe6671bb196914ade8d149f188b6a235d834eee6883ec5a658ad9e5a3fb5de5a41b596d41aaa55542b66d1968da714ae3ce63293381a207ab3a34d508adcbda3073dc8c57a75b55f8bd4285abbc3257df4ada650285646568db8d4d4d634f48676e5a1aa7aa82a95aa7a55655585abca87aa5aa9aa87456b5957118baf2aac8a6dbb26c61324ae6f14878bd6e0c5b85bad2a920c946c94aad67d58f901087905a1d1cb646d415a46988bf5048eaeb6d6b6eeb07a7a2beb5b1b4fdf593ccda2176f887eedd3181f16fb04de1c9dc390a8b5091a33faceea4c8e58d5322b55ec0403ae6f14e3de5a72fc9c515c23328e5267aa0f2a4de35212a48f0e2303a3603cc2f5b687cf47e5f3f9e7937d3ef8f3f1e1f359f97c7ef87cac6b46716bada363f8d73077e332eb70371713c5113d6aa5244a83d04c001062060d7a05c0daca7361db169e52b817de340d6ff171d382b47cb6a9535310456fda047f3b36ece8b966f67274b966184ec4981b633aaedcb88ad16ffae432cae5a05c60ad4d36369309d7f3b93a0a24457290c43b00d13110e5fa08ca5f7cbcc3dd3a26bfbb9dfc3ec24472c4a7034def949e94c9f82843690c8674197808e98dc18c8e828738c0e5a8a935dcad833c994c9ba64f40e85d17dd97d38973b8f1a12b5ec69d74b8f414dfab42948326d925818aeca0808419026407052cc2c8222987fb8eb8327ccc4498cb8b27f98171dfd3f1322dcf39dc88abc9c9d4b97d10126e7cbfc017371abd46820b2427860c2969f4acee48c15cde16ee56539e0c61f7e9d9b4c49a280467824721bdffc6b10eb7668724472b1c4ac6e1a30c44c9c0b5487f8b34c98da771ba6a6aca81661bc98a6cbb15095584f4113cc70fe49d353aa5a4ec65d144c0cbecdd3c2d5a831f650fe7a6b5a669ad6b2ac58931c657b75546b588c3a5bf81c3a55ace5585ab1dadc11eadc50c877b82af4f5b1052ae8f5a86af291c2423120973b12e0634f1c6c81ab8f9ded3041c638c7cffb9fdf578ecf3d8fbf166a287b98cf212f72f0b42aaa95a248c9b916a766529d92f2ba374c1f4e8530032bccdc9f0d6c2d85a8cb1facb355614bb110945462985871efae41e18b47ac37d61dc3e02d24787451c4d3348736de9c20141f790af8c650bb18c12b67c927ada8a4f8ef04f82e0de2087fde1bad9d1ade51d60dcad69faa64f10a74d59f4e9354dd3b4156eb87d11855bcc9d5bd2516df486a6b4e16e518b0c3fbfb81b29a31eb7703628c4e9148be5e1728d8295ad2941bc229dd54c77a61777aba98a7665d90bf37caef593b4ec85793e145b80176164f8476fe80e0d03558bd4b86a6a65255b6cb8ef9d1f9e3171b4fcb270273a05a72dc19d9c8d790951a47b91e14ed7dc4013fca985e0b429b8535be1349c0cb3a6b1d5ca0f4004a19900d0485a839d93e11f17b4ad0fef5af901882034003c5a757ba1e385831d064dd3d0d0d45a7dab51d4caaaaa5bb577a4f0d612458610deddb05815a534f6a0820409173930ca496b65a97441a39cb45696bd5ccd289884311d5aae07c30bc8d2a8166882ce4635bd684d83df5ae68ea371332ad2a6038c8edd429146df2e12ae45b69aca980e2db586bb4d9d9b46e18026f8e8e375585872fd8547d5076d82c73535f176893712caab22f256534d53430676d084e1ada2dc7b5367d420c170210edc022271365e550355c7879ee0be9cabfb89bcd725f8411897fee389f7c83b2725de9ec761d88bfb9439ceb7cd85b9488ab9447c97590e3f3ae7b4709ebb6c7bd955d57549815c8f57bc1bd791a8f883b7ced7db86bee22be4e52b0ce472f4e5abc2a57a87371ef2eea7aeaadc85bddc3b2b9d8cf45e47ad88b7971d1257cde4bc9cbbc45bccb69b610f7922662df226abd30dc6ead2f3ad6dbab190ceee97eed5e3b720578c1f8c4775179dcb62be32f772254fb117770bc37f407f59c93b0be180d9c518ab5f558cb8544f0fe3e16906aaa141ae7c3d94957840596c1386e32f7a2015be0e3124ae783728de32606da4ccb56e1bc8f28a77c339bcc1c37ae5ea9501f115de1ebc8c348bf0d7f6b0c8f2cf0af92aff22f4b4c806d6597775663fb66f9fc54dbc312e21b956b6b295ad6c652b5bd9cac2aa8295adb07334a0555541ae4c1f16f092487d300948f295e5e587209389c80e0a42d0b9c936dfdb4bfe92ef9665599685dff36048d86c610c7b31797c7cee3d58dd872c74cbe4389b5f02a2582e8374f2ce547828a18c6ea3f54e29a5308b5ed4a9928a30907a8a84fd17ef86e8d2f2e116de3ad3fb80b7b64e2c9225c3b244d9b22c0b8bea69bdbc111497440fe3e88704a29e0ce807208238a6fd637a0903de1f8684f5f92e6906aab9423a3ff97761485cd9ba9c2fb9096f2c1adeb42080c01be98795f7aac25ee48158bcdd929665fd631dbbd63d16de82b48024de3250555246ce2cda3690df7bb1f016b37533cc3d44be471d99c5c85bf56865ef555b252b06f2a875d727fc2deee46abebaa5b23ac540de4d2ead2335666429618a28366a09686a1f6e7293bff72eb25eb117931f07f4210b1d67a29b6c2cd9334f15003079d61219deeaf15655f0767b703a2958070563961c3a0b443d19e819b16867b920cfb16f2d19c35b90168c01f61e102e6117dd3a8e7e329045d36e82b793ee076f1789e7d64519a8e60669c9b72494b9d6ee8194e0cffd43026a23333ab5920484b7202d176f1920923dff5c979519f36433631bc8f6165e3548335ca7647bddbaa8adb5d65a6c81d85fb7be0569c9d088eb106fcf1e623c64b6f76431633cacf52a8c3c07006e5f8d192ba8fbf2bb4847cedb27abaf953d2e5aeb0320737dfbaaad892029e581f1403117b6d63f62b7b8933f9d7d6efbf0bec44c6f3b5689b958a04bd006caf7f0137be94c4d5e7211881efac44ad8c192c388b083a153cca5baadf5f4826e41354710cd11347304c91c413982608ea0ce11f42e0f8f0479f0c55bdf7d66d7b1e3e84fe6390e983b484bbe70fce20de18e03da4c87ccf456569de29b3d10897c5a7251c978ae57559605e19c734e6ab11fa87c610f0abc2217235e87f9dc88c0f8682ddebed7a3b5e8701525c7d13283692dbe0744b5b416e39d70b79be39c524a29dd673627848f72de77258669cd4713117ba4211eaee16e0ee73ee6abd7f64ec81c048b0c5f044b8d1667c3fa3797458e2d59cec88f059372fe5ef29efec75379aa27b34dd24899a40e1f99035126b80734d5247daa625aab833b552d70b64715d382be197cb5a48519f192b08424f494398f54775a8ba7fa06a7c5f77015035126b826a93b7daa8cd437b8537502a7c53b99034399448333c59764b20dce145f93405415034d46fa2473aa13b8932c83d3e2b3996519dc090d4eaec19ddeafcf5565ae6220eaf159d481318359e6649d654e9f9cd35aa2c1b5e1e69d3e8073446ca0fc0ce01c013693fc9c13c036b2937ade75339a435949f66c8f781ff7a05beb791ecb85f1f4c8ddb48bb7f9968732eb29c3c8f158b6c938e4f889b938ecee652f9bd5fc9267b15545c961664b321f584956c35345c959723c39b124aba46853fca78a692dca9c9c3e75d1a81a1f9620dc77698f3e75ce307da27df802caaeeb6b186536b3ad66f8b0ece2c2f72b198c021a57725950a2c77ae39fb0f0c692f156abe8de108954f25f4d8b7f18a26676d72c4b5ef5f5bacec6f558ab731065dd7ee42532c18e38f178ec5579ac554ae2fe73988b612a9e52b8146ff07d411adb15cec7ad5421672671ca15350ade5743d36634aa25bf4ffdde7b0f4268c4c2183bdacff5dc9bd15cacd7e7b1da7879b2ea9fcc7a67b79745ddfa84b5d5f53c59f587bd58fbee757b5d5736bb5a4aa795372b02fa089ee3ca78f97a177b633c0490edcbb4dad35fafac0521e1d6c71aafe8c40960fe6623278089f146ca13c3e7e9480512e9d51863bd5464a1924aa5522b301245ee87a8d0c2bc411572bf442e410c35c8a452a928faa05a022fc4a452291534a10458c8fd4bbb90a9542a95021e6e90fb9fab0957104326954a493193fb1e8b0a30622a954ab1c08adcc7ac8d19527027954a99404ba58c2421f7afaa0963702127954ac5a06f6991dca9542a05032ce4be1597c084243ba954ea045dc8fd8a32610663c849a552290043ee57d00c1a27954ac5c0045de43eb5ac689b542ab5021e700087dc9f253ca049a5523248a568808331725f56273c99542aa5829b4ad16005b91fb12d5852a9540a9e4ac520f761e5841c90e1a9542a264507b9df13425236bfc5fc3eb9b3a889e2a652291b5499542a08b9fffad7dfc45b105296f8c2558aebf216f35b4217d2902176021549c89dfb57a3ae1c3ce4be23e55219acb31f68e3b926025fd358b702bbe5cb14e0e268e878a67e6fa10a41480c318e1140d33382a406ec13121f3e7c40d4ff17238930e73c8e76dd6e4ef7f972205135d62bfeda82389cec9c801d82985759c7e670328f943e5a839f5be8821bb1c89fc79a98d5fc9a7aa193136bdeaf379c9fc3233aeff630cacffbc67a94518c243bd5e584cf07f9f337f3a99fd005f7ddd2eb659fe8f9dc479f3cffe47c3ef7602e9f073d7f55bc8f3ec5c3e7f9edca30e7ca361863b3b99c27e37072496219a3123fde639638eb8c3d4f19ec7befbd53e06590e612e97784752bd639e79c732f427839d807c61559a9e5779db6ab5748a43e4594b692e15d646924f4e5aeb5147151ca0cd45fe674cc70369325c34ba2e16e9121cc19bee3080c13a5196e86f112d25c9703076ba391ebee9c0b8075ced5d862bec38a6421bdd21db0a24ca2279243fe425429d2c3ccd3fa49aa3ef1a59fd8c1d84f8b2b34412b632249be41585a20aac2159a7ae69a334f6f9bdee9c4576c680354ec3e6d6c5fccfbb559ea51a47419484aa2fb181de53e483f398e761005c50f6064fce4509470e91ee5a3a35ccac0a58b4b229c04ca478741191de6a49f60f9ba684d4322e5c991bc981713633cb5dee49b8659328805f738fae4a2c7330151a4bb0d42d4688a66b4d34a3297861c6fe2b40cbf5dd569b6059183353ae9938ffea620f5898a8e4112f31e176d8a1f654fe7e4ede4085ac1b59c7871998eee46d9d5a6677aa22abb594b20eae4f1ef0812dd8f89e827596c1304ddcff66ab667b3dd4be49e127dba5a8bf1040cb9459f7c73487294a8d143a1c4a3640fa74df1c60e777b37a8518c8cc947a29f888ec41763926d6f2647a993a44fa2f7e322a78be745187d925894c1279d6000fdd60fba6ac5b8a2c71c67033eca27a58dcfeb7f6e1fe6040ffaedb6169ff2ebd84d2ebac35e4287af725f55c77e491f740fb31a452fa7b5f890a0e06ea1ac8937c66b62b62a6f0fc9951f057e28b144102684f449c2b766ddbacdaaa361899ecbddb0eb68f49c984b95b2d26ad47a91b443423b35872b5f21c9f1148335bed8317aa4ddaedcf2f36e291dc3c31d62841042e8dc6d5102179ff9851b5b4380753959fe5dd67bc857c6b227d36cffb24fb6f5a232b8749333195e5ed24069d3a798655f92b224409627a57c85a54ff69bd3e2a6882c3dd6bd45b449aec87ff02671b2c418bec912b748f92ba2f417a2fa94fe10154f333d4b9f6664fa59d1894929319039575729581a83fb1660fbb6b3242a34b50b0809173eb686bd4418638c2ec5e332917e6f8bbea26b1ecda3696981280721842cd069efbd2f62bc287d485a9e0f1144104104112511646119b1177becd6662e2fd11a542248556bad25a078df3f5911198691df3dd91677b23cc51ebba5c4b21ab9b857dc89f10b969b48d945c9f6ce45fbcaa1a233c15b6b2dde82b4c42fe2176f08e9483db79aa31019da2fde102f7e017a974def64d067b6bdbccd8baebbc51cabf7475ad6abb351724bfe56f852da47ec45de73fb68fa0a294559ca4f3293cc5e2177517a1c10644fb3c8cf26db53cc05f6ad7ddbf4a95dac27bb5a83af5fb8186e9bcae39edc9adcdacbcbca9f64261912365b79ebf95c5049486472516c0156bfbe2971f4a3e92b3e1b96b46563a98cf1cdbbb9852b4f15307f449f9e0d84b1883cab47229c099a628c2d0e553fafcd379ef3de6e7885b5efba7eaff173fd36609bd23ef9680d7e3a144c9b7ab4d6030706b7bf91b47783246577bfe7f22009a266d47a2fc9696d829aa3f182db0f176fcff01f0f76616be1202db9fa3cac82c42fdc6d4c9aeec0967864fbe99c5b2bcd79ab74cebcd52965cc79ab2f11c289bdc0be39d8cbcbcfea29945ccfc53241b8abaaaaaa6a9e22117a2989d14d3e01934fc0411386fde4a2df975821f6021be032911b64ecb24550743b1dec27a54726638798e8a20a6f4462f066efb2f850467234e049766882d7a7a218e80ea266c9c5dbc51b89b43d4f2d4f161613155c8a9261495319cb033ebb4f2909f9781889a3eb86f8d3fdba042d255444a1052dcf6d334abead64b7a5884ceeeede8ca8d6d0bd25249236b38412da4feaeaba10e3be198852a24d7d7a3365e251d127256a7e6fe6cdf4913745a39e0f2cfc6698801ebc550ccfdbf79768d4bdeacd1e7e9685dffbe6662e4f572310618b172b9cdbc31b91211ec84fd40d8afc5e1dcc3103d2c4900ce573423ac7bb6f0f57975aeb2b4495bce20d3ba4d7abdf4a5f5233a517d57ff0f6b0c89f5fd3fd7870adb72fc940998313c3d6e7e1ab0c54732f1ce4cad4ba9d2f1403b13015f2e8df29c6576b4e422cfaf4ccc2466c63c11a4bc22431976d623aaa8b565685e978b17085bd38a75d1573a1df2aebd65ed70bc4746896f562afcb7af216bb2cd6a3880844e0b92ee9625516c8b2cf5a6bede773afca5e16e662257159b72517568239c0f33c1e8fc7e3ba7aac632ff5d62fa6c3e68a2de8baaa25ade7c2309799c4e73674dd8730075cec05f440201008e4ba7a40d88bf57910a6c366eb53e579311d165429a8fec5129269c596900d42f8479f4585ad2ef5bdd7ddf0ba60c478d46a638ca51d2edbbf4c2457df40108369ec85c2482239e073cf04ee8db2fa5645ccdeca96524af95cf5d8e3687b2812d98d48b6d9ca77dd5ef4a5e473cf4500fafddcf30b03aa24f6029a14e3f1dec45e2eada24c29f6f2a957cd1cf0b9e71506f3a9157bf1642fd7ed2d8cc755612f57c55e4c2886c4859da361e92df622cad81b235fd8bdc2c5b097976d3fcc41cb83fd587d63648721d1c41b235fb8c4c31252957858ae14ff02ff4e24c399b95c8af81e8844acb63cb80365facad9b06fcb829625b3bbf5783c1e8fc7632f05bc94f0287909bd644706bde4078e0cc2dbbdbb59fc67abb99aa04e2e2fbc9d64ce99a665e1ade6792b679a176594e2ed479ea30c5720a8a5186cdf76a5744e296384f0b997451d3c221e23b7c05e535768d8e27cade23a4c4681d6ee70edad1a2d8dbadeb3a230181f6d6a3bdcb8d2a68e35ce703717fb3d5bdeb5e66a64cbbeab5af156446b5de37dab9113b7e0a494fd5c4aa32ecc84c50e9ab6194bc05aef2591ae43d24972e1b70ae324e54822496c89ea4ec4d8986b6d52ecc565713787d3a35124688aa89320f652651c66b82e07b9ae8755dc329884c32dcd25fa54025aa24f37330183f4a9bede4baaaee50a6ff54855cdaaa25555abaaaa2aabaa6c555d55b5449f3c79893e6139f6e9ca4bf4c92ed1276b893e5555f5947858997eca583be88e6f48002543b7d63566f1035b8c3537346df113d77889a3a311b1fc551deae5e8deac2ca9702116c15442ccb678921f887780508cdbb4b1944f69ad1f8914e9b3d89a7336664da197a28afb51892f652642c3a3f4a4d652e84568b89be829f46578519f2a95b4adcc4a3cb697333d916c6926a970b74b1fc529ad3d4a29a592ca19011aab75f03df7dcf46c40b23ca4c92bad515ce211337d678ab70a64894bf44432c5b1f432cc14977810c94432916ce50a6e26e10aae8d13bf375ce7198275f6a3f569569665d12a4a29a594524a29a5949ec2bbf6407ef4c1791c4d63061f1ffc7b71a7b5075f77c3075f979e7b58476738bbfec03adbf1ee55767dea39e79c75ce39e7ac2cabf2743f366e7a827fb55610ce6ad609bb6977cda481771812f12e4338e77c7ccc6fede9e8dcb406a1549665e89813f37010ea9827831f17b405b7344f647b53ccec888c89f3e43e4e909c8cdec341e3f48d1ce223ce39e79c73ce39e79c73dedcf8e8d0c90837cecd9c82d3a636a351407c9ee44e7df3c343a1f998272ecd13b1332bf19833ce18b39981087de587cc41d39c1de38018cdff30bfd25398f371cae96294213767541149eb1364b92d45bcb86ca3bdcba05f201488ee45cade144f8accdaecfa28135d5ea3cc479f88ae151eaa09334cbab524d21d3622911e754e3ee7c9e15663b49247779d9247a351fc68343a3c818718122778341acd51761cddf0ba7df4e2baec2f4bcab697499034825f795cc12b3f5c056fbff04622e1edfe04890ff53de0ad62ecdad40310d88a3fdc15ecdac443c9e85836baed86304629e7a4b45629b097974fee4e5cf58c8e89808919688a150bacf845dc895f7c3e336bdf7d312d6ee227c5c921f622f3c99f9d38c002ce648a97d80bcc272f16705a7cdfc069f1f3e4969e2ce168c49fe0967134e2adacb5662cabd93d8c9ee0524af3c3749cd03967ee2aa5cc10d3711263cc515e1684306319ccb2bb3396759e2ef404f7e42f6ff565ecf3b6b614adc59f64a23ee2a34fa2202e25fe761f82c87cc87036e7cc46f7d1a7aaca22bcb5ccc754bc55939450e874e18de5335379df448f8329a59621df372a8f2af7a8dc44e5292a2fa95c86ca51464f8a96aca28374953c7a964156f66429592993d1374ec42cdb40cf3690cae3df913efd24bc0569898f3af18b98fbf27595eb39f0ddd2750c3bc87a50cec3534c9e02613677c05fd35858421fe16d47de460fd2924340427719f2108542a1504802197e93401e85422b18e647281412e1d17d64180a85b61d197edb91472526b7ee01d725cca6e8f0e430032f9f3cd432e5c9433fc97834f1c6c8f2f725c20ae3759269402587be81729f64a5247ef8ca617e087d05c33c79454c87cc4dd8e9aea311ca74bc31b20c1d8a545050e629ca290a8a499096100ed29251b06bcdf36972954b131c854089371e702c4d1203649c74192412892483f412a944ba2c09db97ec656641b6542a1dfede98cf5ffe7c6220a0377f81b0cf750c031dc3b08342a1961dd624e5a4f6604d6490f0490f3c588f490a8a086f1aeccface4a1cfe70f33e2e2f1398637d03f9f6ceb0d043117380abd31aa823b1fc25b8b057a35fa87aae08e461808e8d74118bbca2dc9b61d59e51173290569c91a783c4cf2a57287b9fc0fbbfeec878faa6aa93ee79c53e55685a6e05e1745c1d6b96ac9554624770c44f54d9b6277df748c0a4945a472a2a2a2a2a2a282dd7d3928b85bdfec8471a305ce1648b8c8e942c70b7ca9fcbd23ae2754a24f73e2d8da9b984694bd22d466a5873294770842f8e92094188f98452291e8988be8d01e5688c2f7e132f4397a58dc90f1ed21214b1932f469674866f08fa66b2086a24c056f2fab7c66a5d2ad2d95ec4b6fcca56445a23f119421e3a55b4f1692f764a1632e505e622eb095e8d3e37ff429869660a24f2a9f99fdc45c546edf7da2b5f83c35203390798887c41863ad357bb23a27a5347b324a299d73664f366bad52caecc964555531c6ecc9a2655910c2ecc9a0b5b6bbb3276b17dcd1af0b73191d257bd9333a0c9d02adc987feb0971f8e4624bd7df44902f193bb46addce4ae513f3ce540643173ae4d2b996bd3903e51ec0325ab322465d1d190f721c3997443c8cba059da10c4a2532b876eaf92b97c59d9244bc936523ec14ec46ce5f82786a45e913e89f02644466b6d09eecbd5cb552a6316047aa807363c5c9a5ce52a29efe10212fae8a1cfcf0c1a71f2d1b3ed073402ca900c7d23e5102e893ec25be925e4f28f1c22854821528814228548215288243acae82425f2245df410498412225d449281452878fb91476f8c2cfaa7ba7a90e2c1ce0f28eca08b1c4f81d8857d180fd2a70889d1432f6f8c7c22a52c89b08e37463ec14dbc316a7d94f69c50ca18a38470764bd1514e9e5144596c2d7487bd887e72293ad121fa090f11ae8e46e827b83a1ad91b23e380a14c9e946da2d3949fa47c946da1d0e1b1507612ca4eeeb006bc51b6857e82413537848548ec120349f90906725d0a91212c44e614ec5a53911033a2ba8cada9e07b2ef1d6436ccd620c306180c94b26d9e6815c9a2f954287a3d27ce8d0a4744f693ec4441ee1cd73213097b688372130f38079b887071ce1dff51c9648284b2e69096ec988974dfeb2c92706127ab8c4a3fabc9543a1832c24aa8342075507811e1ae18d481e1d66db7fe4394529708a308a0f1910fe04dbcf6c64627210086fa19b6ca19bdc9a645b43ebb017f893cf6cf49e70fefe2403127a15ddc48a2ec24042afb68985c81cc2a092ec66b97dfbe1c1aaea765a5616fb06662fa74fb1d63bfb860b9b5bbdbc1b12dccd21c91566cf3523927d4054c7547921693ec64c23f6014d2f957ff878a94870bba64f9bdba263fa24a53832850c153dd33ea0b102a6d639e729d02709692e4cf5d017d882a527b8b53a4adf9c2d258c3142283b77de02b155a8848dd594498e324523000000003314402028140c878462c17848a868b2f00d14800d94aa5278509968410e534619460c2802020000000200108020001a78140bee520af5581dee5db21bfcd3d98827048f65abb5465ee9bde899fbb727c931b5cea3f4b94ed903c675ee4bd95c1694096d3380f2e164e8c2d0089d0aba109ca7f810f7211fa5b1a37b53263bff1ac84e5b8f0f9be06d8fe174a1bfc1982a6f23dec9f74089d90082f4a669db3b6657311675e67b48920d600c238aa5613ae18761af7211ed37d717f4a98b28b884ddc1e5bca230ef25908dbee88905dd46f59d70bbe080970e5da68051b8988fd02fcce6cd81ec6e899f095ea51986de4babed7deb63f25b7c04131dad5ba0d9f83a5e33d92bbccf15247c7fec231f98ba3868b686e7c9b4dbf8e0bdaf5cdde63bc4ba1d7e0e5270d861f9f7681b3a8a084cc2f7166e9f3fc294f780543d526eb5b164c0c2d76d7a722e50571a94a045cf50d2491141998b653cdfb8a2ba719994cb852e3770fc3f2fdbdd6941a1db0a0f74c676d4d44ba34266a7ed213f2ebc256266bcad7992d9069463aa84041c6684fa9bbeee9db5f069693f79a331e6654325ef5b53d8ee00aeace8e2b1ec7100945a7b21c2f99929f9c6aa361b133cd7c6b7b821e89bc2e252a24307908a037245d2ab20daeec6496b18cd55f28387504d2cdc437cb4b231c92f34037cfd10d93bf921ef4cb76e5098102706e3632ddd79b7b57a189fa37630c67c5fecbbec6c0f4c9277ed75f6e67ef2b4823c44e4924c77c876944793ff2fcff50b9bfada00fbd5675cc603a64593af5016ec22e09fe7573070d19a2cd3d79403b870800d9327ac109e930a28aefd74bd7e9e78ce5f04ec1ccc0b2794b431dbbf7ae7efd9e4238b5cb563dadc3aa6b2ad8337514a909813ecdbc13b3f8ddd357afd02c706408dfe49b6412fd432ca3a30b211626928302328cc9fe7145a6c5040c014e2cf7ec2dcad00fc3c937555f21c340771af7eef7b82b099cb8293fb8b7b3123414805d95119cea848b4f719689bced6a958c791334f0baa4b00aa1ed980bb8c90a110720ecc4543ba0be450b66ac1fe92bf165fd8526a211f23cd137416101d2b58ce30e819a7dd9f65b58bd407f85c5502355d734e9a38fd53cd4f16a6c51de69356fc9651768791fef69163ed188216949df1a9869f50bd47a159a2c13bb344201bc627d210be03770417eed2a868dbd007a1520c0f8bb6acbdee4d1488b7a1c708a05c7d2280d1e3aa0469d1aa459d55eed6d72d5ef9675257cebf26dcbfab1c9e4be2f41754d9b4564573bec7135ae55b14f6cef8bb33af6b079aa4ff51f6b2089f646933a339d499293570fca3c1c6ceda5853b4511f7c7b1953c7affc9de652daa272715c9311b049be8da008a9217123b116831e31cee525cc408040599d14edba5bef7ab800a4b6ba816e0b8f3ccd2761630270037feeb461d23c4d057048df6f54548c910266e2cd4b931948d24ac8c893f7b1e2595624a19425733bad2c55008d04fef73c8b14617d83c2acc7e2262f05a5e4d554eed701f3ffdbca0341bda217bbb54a7f0bd2003f533f75e4dc4f98f3ed5573e8d06e3d86f8f236fc931f390f5a71f35589b15b3380ed6f2a765f3b9a2c10ddb6155f2ec0cde74f91e05759579f09266b2f8898b913b25053352b720ebccac91e385da16d016d4a35a574766b0b942853e0a534e7ab9e9f0272cf583a13f262093f401bed8f2a25a371ad18b9af47323ace535219e6949f22cd2f61bad79c271a6a6d340ba6220e34022d1a08cbae57bf0463b8624e67ad5babb7fd1d167873b641495715cdb8bccf3f68be32a6dcd44ee72c16a73aed13b700cdc2e78be2fd7df449f9291cc57ff34856d60b368d463b3939270a811165ffba86083eca7da0388e1fe73125634c0be386d32482896c82bfbbb014c00ccda94679de51b7623cda6304c4ecb569a8ee6df00f022bc1e29fd8f5b4a2198978a06e53f700b92d5ee372268c39c039e393e8a72585d11c876357e186b15407865d5121b8714db2338d93a7785cf3c97956a407d413a3b7a8c7c4529c46fe9b166ff2c6db3eef2c2e6484d1a800e3d65500726bb614e203687e49f15269e8bad259e7c68c6efa4549065a37b2ef6d64ee96f95c983ae17fe5c5a374e075056e82d1475300d632212b7659636abb42bea04ef23d5b867ebaefb770430f49016529399bad85beedeceb8533e46c81846ca70b8ad9685268174d7ec4253790f30bc0177f0bea4ca5fd331d9bf9a5397eeccb66f42af8bfd65712ebc86fb89b5b430c81636d4d2093c06f13302aca74f470c81c855f1acb887e81e0c201466b36c56aca436d4540a9cf8f13d904b5d7f9b2942b72ad289f11beb2b82e43ce51d40c7217bd0c00e8f8e67e9fd26e676f1ee4ef13c25c10fdbed69e65538667440473d9d3fab64be30c962916209a0812e10ce2fb619b86cc5f5ef7ae3a3f0a176d0e495b048c2122b0a495e230a639649b29be51adfd21e21637b3ce31c0e11ac827411bd5dad597eb013fa406fe69b2cd05937b41705fdbf9025762900548b8c2453c1a79018144ef9393ea571159f3155a80e205c9ddba1fde71b1a9a9a55ddc6b3877882326b1c42742e295b0b709c80b4e5e5d6322c65cc2a000deddd165255568f85fcfeba22a830806ce4d7cf67c92aece5e66f3189b8533cb156c23796a0cbaffa7840a6cc16ec4052eedc929762697f2e2efa6400a6ddb3e4845819c1f51718fe6e53a8c41b9dcee0582fad407c9332212329472e3429f59dabf60d94c1234a1d85834ae3425296fd704c5413f78c85f5d4ea3fa0c8ad51082c9f165389a3eba1b31ecaaa620044e196157219196e5a57a91a442687226e5c5460eae0c84115152184d1abb4d3f21bea9a94ef63cc2df20f804929c6329329af40ce0a8dffe9e282831091bb27125a7e130b2933d4a9a79226534b89619d115c1fa14bbc76333333c0aeb4ec10dc7b7b00865121c051d9041aadb4a3b0c32097b1aa583264f8bafcdff300050fb7b5ef81180a95fdc574a68b0a8c80dc2a0a1fe12530083706c8aadf02f601fbcba1649b4049fcda0d6801311920d9e32b9c4077931b4c20dc20bd170e68bd28008de60432524ce4fb66552a7b89878fd65c58b54d1f70fe0e55058ba35bd17f48048e4ce943761ba424c0727d6f4c59e887573872c7a8f0f20e9f2e1437ff32ea25b4516a4a735f1499c48d82fdf797c39d387168669a4aeabaa46fb1bfbd1df7aeb8f5d1410fb115f7c189068e87fb07bf5b4eccd72199c50557eee1260ddeec506d35b6a63e24d6789d46a82ca6a628c2dbf536dee0d0fcee9437bff9164f369343c299bcd4aa6e414c1222bf06ef2e8ee2f59c1af5936bba17346adec4906b0a66df6fb73a29ac2bca5cd90b5db636b149301f55361e3ae3e41575dce64060a9c8a8852dc8a25f422062005d09f9de3a4339362421304158f59ee85ddc26b2bf8e450b67c8b222d06e67782c772a5ec176a7def16eebd186ac7c4055d103ce8916441fee04779f7c78f6f6f7754538ee896285d967bb4287e43111b8fc258374c99b3c899cb19acf6be095466e0f83e231078cf22482051e2a094aaa482187be68b4ac3cd4a8e91ce554f70e7b81817fb8dd1d2fa4dda798feec910a15d55a47252b4187a22c949ad1d161ce42f894c59e75529bc606782e29b2e335ac9ccbe1580b8f5300361ee8249fb10d8d4a972199ae53d26380f6055d90b16ba00205bbe61f19c3e0e144bd738f102974524c1aa245aebea616d085309c839ea5343b44b8b037670574d98b41adb9b9b9b2094337a1a8fc49d9f357c19f9b47d5e24ee2ae911768f52759054d291f26125f4071f85458463484fca3e8fe36bba1b6baac2721d708b7bc248453e03ba81c2c5b51d98b4d52d036814f11eedbcaa83a8b4fe3e04f1dd86ab10f20116cd6b9f5eb9872e6b2724bf8248f45914e781a9c3742dd0e28ba4efc8cadcd5129be5cad1f28816995f396917aa67a01bf78b7b8dc919af54ebc40a484b8e9f286dec8e70fc17677cc8e32e1b6aeef2a6d8783e96409e783799b0056f9088fa3bbdd35e4827c4a7cc88fcae78b6f80eec5533f66ad9ecba7ed993ee2a691960b6073ce04788357cb5e179c1786b9d1ff5cd8a2dbcb857093feec2084c76791df9857cad6ff47dbc69b35e2b3e44ff5493ff3842d845ae90f5a5cf348dc3f08f8601dc218ec8d696713a84c96f9183c151b32a1b7fbd1b786db4d82eab2c0e6856da99ed7119462cb87765e09ffdcd472a57b9c2d7ff9322272cf670e292bb1c138fc46d210cc3241c4f3b671f3b9f88714f77c809a778c24e8b567727ecb0c846ad0e5dd328352316bbe868166629b4bff41495766eb7ba2c95fef091533e0c996fa28cb6dad621b782f844cb01fe25dcfbd167a6df08adb7d60f6125ab31f3c62599631bf624b1f3e083ac77c521a61551ded36f14f583f7238a8c278da5ad750f6036d381b2c0440491ba0a6a0916de9797728f5a45dbf1e73e8525943acabfd8c2ddff5f0b55969c618dec88fdbb9f1c0d778a83f06f4283046cfe434cc654db6649b78026da2a82148cb42c3d91af02de7b4add7a4b5b3d608629073284bc5f894a3a266ba9c5783bb56721ba487e07acb23175404838684f7060c809d98c66437560084d00634e36cfd92bad54bd091ef716bc648a06b5d047814b6442fb8ad54c638a6e5c27e164acf788262579a0b502a88dd48e236eae9009d317006256b4b098aa3dd05b09caec0fcba25b727468eb7c46d0edb8e674d08115d6dcba5ceba920ee55d80304f7d638c950081c043fd88d2dbb096757b7a8aa8f117461eb812f28cf45ee6da1b335f8648ac84cffb84c188962c0a7058e41070b882a1d2a41a899ac44664deed69a5a4761fa788dafade46697416e019b1d8b7c9150b3159c2fa527e262c30ccc2cfa69268ecc7e049756e86c76f64802d25c5aa50a43323c8d56108e4bebe857a1249155d29264e362a01d7011861cd95db7162e840fe9d3edca77933e79757ac1413b07f484e4caf39e75b89f8aeb8f095a912517f1fb0a4f4af60dd36b86764d7848be4752809acd85e2a380ec5848178d1003f9d7fe60bb2e3c1298cbc2a702ef84924fae3ac138f238fc0a387155a9a3004735a2481620bd53920855858695b11e6ff4b4c35b0930f14dafccd51f132cc4afb86bafa19911746cecd7aeee880575e24ac3bd745100a8c5caf34827b124fb476ed77dd5686b09d57b3cba4734324443ecbdc85116643ebe61618362d0202aeba576c96966c5bbbd1424f46bd0f718e28a0bc280232ef4b87c17007cffb1d56c1a3a4984ddee1ecb4e7066b63b173f90d22bd4a118aa79a32901b99a62466760aa675cfc9ad96dd9e2a8996d0c8b01b137f4aeedd5a38bf2cf0a51f5bbcef18869a0874202c47cb2e0760ae7234ed1d4f89327b8f55cbe952b5ec3f652a7f5e375844537853752798b549f91ee2552fa467a9f48f68a64f9d7ccfa8907b0b3006af785f90de05e24de50bacf72297e0480e6c46b9cf59df15ea15a3d69a72a6b66d59e45c051154c8e137ed28044a566d412f6a1362831edeb80a893fadf9179bb79779e8770f8fb9c49f5bb576a0b0f99a2b0cacc9a7592436dfdaaeb7dc3e8de4c66b63caf094e1bfce29ae98226ffe8d5c18c099929032c6b602ab0cd0799590fc5fc7128f1fe5489f2d7ea526466fbe92faae2a370df340a08b3fdc19866394b0af93be6ac4457151cde15aa9613051e53bf9ec063ae25d3eb90e665669dc2218cf75460c98e6b66dde60ac1ccb4bdd03cf549cacc5a27d5e05059957fb9333cdb8ebb6e842908e94c8e7b986eeab9032d3390597748bbec2610e69e0cf8c4070bdd50a53a6dcde693692968716e08e3c5ef1d797f402173c41afaf44afd5988b53bd9589b5b9f232d93576583e41d13a32845520886a130b63a27b8e280ffb82efb60c73ac626068d7398e50b0ab638eca58d5ccb71e2227e8fb85314a529aed69b725f62755b1c0c6b8dd1cf76ac2f7ff23a8e0a3c36efd9c5c5574f58f758fa127ea0930ea5b181f021387ef8b643215c076a8bf514dcced92d13637921183d3c38efa24ccb060edb58e8823edaf8a4ad2e01adc81ed819e871f314cb16a4f8c55dabb634c9aba3feec246774a4f274a45f79e08a7913b8442ad25709d19662a3a0e933b97e20b8f221d98031ae93c7a2fa04b09dcb3cdad222b30c0bc9bd0efff159271b605f14e6270193e9606c3199c50c6951500a4a98ec927c3a04321e0b84b08ad23b1d361fb9cf02520ed94de973cec7977557504f4d30919367b340913a60e1bc701eb24a6a9ec1f70bde67703f82f733d80f175c3cd2aa80c906041454589717023130f76d050eb4b326e6005b0dd0973d680fcd013633aca8245edfc8f44b5850ef6353c59c27d9698d1ae63cbdf4a88f6f39a7250fe1bcf4c4bc520affbb6a09cade971fce067bb110203e64a0501a14d42d628375b935f6888db81fef00d731dde02ab8ea09e89c35c534bebceab179ca8d094e815b66a033e3a64a116907907517b0f3872c2e41966e689f8d7e24ad91caf99227322844cf29e14d0b64974823159e092b29e06059802cc1b74475613df27397784692587c28dc9d6d16edfed901affa35e7ef3b69d95d6d1f827b980ea3d04aefc37bc7502d647891ede169557abfb35bd8298fff8aae927a396528129a2e536c7499ab0603338f394eaf734fe4227f68b1524cb42fb8db7653e85caaf6ebbb9ad67d88e71aaa016a0a7f492a4ea0a9bf0748d6a14068d77f3d742927862bbe1071144d8e7664b40b4c69bacc058868ba7170e75acbae4532fc16148adb84856c83c4d56e3a4a3b7ef52ccf6125fd8211c258abee710438327f3bc148589ff979ef07645ae2b8457509d40f4468fed0c080eb26f87c59d6e01526d682b92a6684422f4cef413e6527299dcbdabd12e2401b964a7a5a21d525086e33b7fdd42b3e0b4f247bdd1fce97a0943cc8c94df8d7e47ce995340a59ecd317f20d3f5a650edc0c9a98aff5fba32593cd21b3e1aadf5f47155da3bde6d4227a8adcb985af2a4939d410cf87f405e3652011e4e04cba679b337a0efdc858c30bb9b53ce8e5ccd28246b40811beec0095072750c44b4e8c5d0687c37c755caca3c252315e85d765f1ba4c225abcfaf78afaa57527440a2dce5d5dfb85dfe473a9af0236b2b0b2af2cc8077ab6b08e89899eb4e0eb0e65e04c74c57da52a964a2c416cd44bd0909019f129d01c6d0a7e8fd44d669c722b0a5a505ac14efc7748958d6735ee7656d3b985587bcd72379da24047de0e18b0fb4c342fb05f7b0a44fa270b35031b3852ae87346cf5a2375cca714b582de88119bc4f3a0dfd44377af92a39c7ef827fc298aebec9a62b98631b2e16d38a559481c9f2e661e89fb63a4b98c88c8cf94743be59a65c7b0943fe0312b84521c935e298aa4a89f6a46209dca38f8f9fb9ec2c48aba172c15fa2d67ea9b3413654159b127c10e43d6bb906242598ac7b88ec63158d7c8ac9406365582223fe600ffd6d0bbfd809003d2eb1b2ebaffc631373207ff6fc3d8a06535c2120c3ecda373851b22717da9b424c2a0237d4bc29e48b913ecad2797a1423784f607c53bbbe247c606508952c1e5db878da544a61312a5c789262c85de0c7ff94865389236f781998f6d61b9410fa81b1d7efcd71dd0662971daba86899a9cbc774c51a304b8c48b7c7a554695d0ecbba1266894e9af116afd6283bac029c44a38354f0f22d399d64eb1ddaf040d961f282d06cf6c3d4d6223e2ae1976f260f1a853ae0f63a795c94eaf0a983d99336dd5470ac49e761aa3c9ad7c8943f11c49f524e066d5c35909e87ca54257f1070af54ea1191b65cc9584ac306348269c56a6c7aeaa0c568aa00efff1e74c53bcf9b9e764828093ab995be505611cb90b8dd5a76e2a55e08853f710a09bbabdae97b9de456d02166418095db8e61723b0b99f7c4edb94f0b8c2cfce2aba5630f4606b03f4455f765fdeacf5fd8277bd195dc582acb63c153e2a5118b08b8b3686198740cfc539425549009d60d3508715cad67726575a9a02fa312d4a5a7f6dbc381981d329324ab8ea16690bf270fde3cc49d16a257fb1f04f46d62f485c616638a9ad7eba9e1ea74735f7782c9d807e926ef881e2aad137d19eaa44e77510f1b4e9d6b769acf708608e918104dcb634d46aa1695dab06dd901279bd2f92c487189a293fd4835102f0574b8fa7f5d8c3e61664a4ec745df66cf1e7feb84675cf141d64ae5b28d8804bcd994b6c13266af191dca1a0151044b1b7a640ebf00318539013d4e340abfca8ece5a829580c5330d2715c9e33564ceb40e75d984101660dd5dacd5a633a1ce1dfe009b2e36ac046a8f7d61e2b5dd00ddbaa2ca3a7b7d8fa317670083d741ed10a6982c27929368a4e75f0db320d6f909e07bc4d7e5f0f24526616a485bad10232ab77ad64ea259ba7b4687b22a783d5924032c7e3cab527116fc3de1a6e1661db0a589077a68cf5159ccab3d9f01ce51df6344bf8f372badfd4726885773cb24c643cde722d3aaec5d1bde41bd3e5b509e03388c0371a76a47bd40d289d1415035abbb0db1bb1c89deacc6f584b5bdbcf3b28d33956a2e1ee70f022ef2694e905829c9c1addb95ce1348fc830ee6903c17037fa5b30c48e46f1be8cfe7fc7c70f03351e3e07223d5d1cd96f18d12c62cc187f4cb8d2db0f5a03869b0dde6a6527f84362462b15255c67552fb5e5a86358f00fd735477527b3c33d8d8a4d4d85304c8194584a902214769b864309195553be39b86def7d80f800d8b65b17ad6afc6a278d29e1822b96b1688aa76abc493049d24f1af08a0f9ee62cca499668285e5187887f2194ed0603513d6bf5ccb6ecd3b4d9d49e90b582315b0a3a10abff21a9131e5511b6f5431a967e816e8080ed6cb10661f7629cc70cc7a8956a137389926d5ca90c992cc70b5bcd90ddfc389ad9b8898f0ac8609f0b4f4863ffec68112e1d6bdb532d15bfc77380da5a01436c69cc504750bb83c2fad664bfea999e0b4a32f76e6c4a9d04bf2de62fd97bdb6441c869b62b4108441c5db17c293691de59fa2fd625f31bd8e07df1fb12fb18daedfa1ae25e58bb65e7e66d76d8bfe484362f93d7821fb784695a4e3f0323c0eae51d8fe3444ffb2f3263725f7413d5cbf75235142e5aad2a87db298b0388c12c6313f625fe8a29aa1815848d1be750067d7109e878ecd22360b4808fb0fe6c4b902cbb121fea448a222eabd79d9b18822ce809a794674beb4639f417d4f3da01fe60632f17eaceaf235b3c71cba1e2c680a34ee7d6cdf4c8344632ae4d9929d42c3760247e43d40a10e09df7e1e471b4470ca074e37914606cac7bf34669156bc3fcea15f43d88ebfba25d1f681ae153c87f1f5aa5c8a1c94cd6f34a0212491b08fe9c3f540c61480e72755767a77252ba48cf168540ac457a8c1e8dd83508a88ecd80a472e4051b922412c6500a7ef7c3f6089f1454838e7be00ab1dc5b23619f80e8ffe2957f9c2a04ef1bfa799586dcdfd71571dddd6e76ffe4d4c8dab53b2e05703720ffc3b87c8617b4a5c32a7919bc53bcb8828c087cc1b4b8f8fe07cbaf66fb1ba988d3bb54547d986de0755d68d6895141e40968424887522655992ed212ff22c1612b5a691ba36ec2a28d2dbcfe2746facc08ca4cce9f2108a66e694a43e39bb1a2d0e195003ece6aa20241dbec1fe46248426828390d0a5ab90e8f88340d8976ce0d30d7f70ac4d50e84c2398670b31b56a31a53f0968c2d107be333b60efdba00af1a637fd51d49d6ad4aa90387c449c766d662848386197119fee5945ec466c5b2d7e19a139e4a583a959140e6f71bafa7fc17e3be2a8913349d027e69c8d7657664d272f451dd7ec4e13b6cb2595c4d1d020aad444aecdfc10443c08bf7c817921a347fdc789293677baabfffaa8f909503e133eb055ebb786933937dae8e1654b3609fc90f5e1a17c861fd7273590fd5a00cbf46f7bbc3380db5bdc7eba3f56e2c29d8d049113aa340c04282b8685bcad077abd0478b56120b2f818b5809c800dacbbe855dfe0993fe96d6965e3f72fa5181ad8e291fd6a69bfea2b09ddc0eed8e3787e53acc5f8c645dc64c0e9c32313d804598ee9bf6d35f227f4bfa4d48db36e110da7be147a56a228cfe810d35350175142471a3fc84988e10d1d7e3d2663ef1f2ffe34d946c9ed4bc1344dacd812bbb263339c560158360f53fa3b7ec1ca80254dc747124e44a8dd78ad31d48815e360e8a01062e9d07b813b7d15adc84242598c3718dcc1fb614ab9a746f212f4940c938857645751d5b422fa0c25859384923c354c481d0d776175b7f3be266e5325e8820ffa23493a0ae8d3ab8560e897b2495241a94fc748e1bed25eeddbc3e132137a2a4757a621216c8201c5795ae7cf3c4bbfac76084258b6d522558965d0e3de0e2bc590d7ccef658bbd1605b8e56517e494c43761a943cff59075f0e7555225b7baa85abfe1baebcb10627a9a15bc71e390c75a73fda15722f46a0319753cf95cfeed366a0c0bdcea7828fe69bced13def5ea3eaba10aedcc1ffa201194cc4059d0b23c4ac3e70a21037cbdcaeb67798f463a0d833e1f517728fb4a3fdea38aebc18e17c74710b2b2266b25f08392dc959e59efe0c30da77e4d735f91867f6faf33883fa90e93ce8d1b49a0756a3374812823d229fdedf67fe76abe2b8c6a4794e89af1c004517b518b0e98e9ce998b12b00c36ce54e3a87bf6859f3fbe7733c8bb21100142290dcbe1403832d2bb4df3784baccb428f1bc96a5c361736bb43721d2a3c8c02756e43d745c9023257b6fb4270365ea955e56d688841f9920b55e477fa229ec85f30feaa53a459bb3d774aafc087b2c9c504dd59906f3e6a044db91042e06686d7e48cc781c558d92027d1c05932920d3a1195a5a1338e0648980c327285b9fe6f9fa2bc5c6ce30dbc33613857b125f7bcac5ace3c764691f4f680a045bd5caacc95767bed98122828dee19d276548599e5d52d63d48921326140fd48e32561ebea499cd222cdf1015706e0fdc0549d609544390766186416832714c0872abb809be14d444f5255056f523c7566365126ba52e89e4a56b74f0a97bf328234aac6ce234cd25ce5586cbaaeb75855dfd727f8e60926b0e13dfde4c21e73e661bb5b0584f314fab9ffa405d425107eb764435e8e12eedc49afae4dce16ce8da4a0ca54c203427ddca0228c9be40be71e4fcf9807cfea9978bc9bc2266174d60d2643315dc65f1f557ae040037ed0654478a55adbc287edb46add9bc390020307ce4dc56932cedb5140a1a312bd81e7f7e804825143d6793f71cb6d6e758e2fefa6f0a840822b284a0c878954135f4cce72c02205f6a6ea3f8579d7dc88517c13603dba4f4b5961533ba9acb898cca59600bc8d58456f5069e4e49d41500f34450874d0b3490c6f44a492bf1712f54d358b809c8ce155f665e4b21f8854c5b509be2e1a81011025fe1875930f6c2a4e9ea74f503bb0cb968427e1a8dea41b9238f796780eaf977a896a2639524c4bad29001e7d99d9e3b1226fec8ab58c4cc9ceb25d59d0b040afde81d5645e32f975ce81083a114176f531e48703ce195fd2414ba32671a6e08563a668091a2d7e72a1ae90c9c4bbc42213514860cc93f9c516e47830e2c645753903c8019372d116595f7fb1a874af96901249de0858b0c00f72462535cde750df268bbf6bc2f8457db7bf944e09ecd7e98f41ec97a08174bad324f9dd91bc52d7a1a7b34b8ce7dd26ef26c0f2de5221e9352be25aea51384fd0ce8afdfe405d0833ec364f4673e033671eeeb809bdafcd17afabb0e19a3d67617ed776ad48ddcbb228d34bcef1c1d3b78acaba3285073f0bd3086af8b1c34d5950721b73b5cd3f684e586ee9af9f6500a95c1d47045bfafd2bf05330980482ccc21ccdf6472d0482a16cead666367657b477b0039da3f4e6ab0b220bae371f8b440b56b04f862eb25d30151e06988d9423b45f34f887d6483819eb355e254a742102bdc592956203b6a80464cf3c80d4df1af103af02dabcf873990e89e21ef3a65c8c9fb0992a25599b0197ba690c03dcf28fa7e672825d4d80e4416fe47a3f809d48a9cbb14eba567394fc6bfa43f7fd4f6504e1d60804605345094abbc35c73409cf69686dd7b0f13b5c9e45526daeb166137e8e8235e88cdc3235075e52e091fa23e989a3ae0f71189579565791c3a420077e04b59aa9fb0c53facfe40b75f49c2dd5e704030635eb974f37164da1a0900cc6d86469ff303b329c5dbd2138963a843534700fa952fb2b087386300078c83d0c67fa052284f7da739efef0419828feed0af210a21ee58b366bedb194a0560c6d503f17b12417b9ff2996d4d0858e30a0069d043f62cb02a1135b8702693698a5ae590b4c140c5391ceca3e587bac6648166fcab1494c227e225b6fb8a0f876fe7913d460209f79254fdcfc66b321bc0880b336111f2dcab1f31ca2fd692afcc1535b77ec43ca19fb8e6abe19adf05f18c791f4e38baa23933b81fac357486e70ec730d6e2b07c0b6796571993c11829df218f900657579723312382d433a830f0c8d3c658f5a117ccb7ce7ce8b42ad4d74855528a42bb4a9811f4c3e2f2fa9c4892b091d83126568b6c89e0cd40618731d6743570c0733848bfef2881689c48b744fda79ea0d3a0060585c4eaa26c1acd0591cdce9119953290430b5e2e2189930d37438e07afe0e501e90732528996ed9f0f81e0d0e4f8039eb63f1daf4146b149255ff98f4fe17edd290fa2ed8a11e443784625f1afdd95cb45dd27c789f6c44d52def87bb254cc3e57816b6b61ed657173dcb4c7d529aeec7980e69248d4b3b262b0645c2925a51259db13526576d02de3638d67596dff85cda7d0924a0e572fa5c82adde7efc055db61d62fb122129c133911ad851e24120f35caa7811dd10c5c7cc4aa0ef1b1bce548b77231c9ff6a5ac8f5f028ca1b92ecf9e53b66d4e1c58578ead4126a3d283747645598d36b163956d2d9d4689e13d75b8478a998d909571e44969c388b641a7ee6b819ff2893f4f1b0b67fd21575187e62593eb88323f90554036aa2bfecf9bb5e527e8136aeaea01bcb0097a24604decea75cb66524f9d91062fbfd594104788ae6c4c567668cf5c6670d7ea823fe26935771b9db16433663ed8ad19a7295c5f000afff0394a285846a1aa3cabf2ed62c8f30a85cefc8ebfaea5463adfe647e6c508b45fb2719dc0b4a52137bfb6288aa000c48a19ea63069b40b0e2bd75b52a9a5eda53a762c60592f2a5b52597edb37eb1d590506475a927721629370147ccc55dc913687cd11e35cf2c41e6134e6d20903f9e9e2a325cfbf517dac427a756ba9c6a61e7c2301caf622c73620126416132a8e53121fc5b14c2d043fe0a696134628ffc2037cba9e46cd8a36d9fc0b78c64c2e25445ef5b2338736e75c11207bef42e07fe33dcb5d4755a84a5aac80dd50ea40494aa3daf1bdf35901c4a5e2d1a1b303b375c0ffe850c26ec25677c3c3618144e6241d7ca0088b972c3f8ed26195016b4fe4103a07ac28bded07ea62975dc617f4451e11a490d34d80ae7126a70f64b8fd6e6e929d3dc4666d4d9ac99276e634edeb22703ede1e60ea3c411c813b646cc3c29458c228f05d1dda83652683cadcca4f0d4f8c45b4dd6da371cfd9506e3aae401c635f55bdbbe8a4288e9bc578b54020edd09d479fb99e5c4ae71c4e895af914e6d534a66b1d42b69f7d09ef9e04bcd586161e0540ced5a6e1f69023868025573d03fecbc5ff1d7b45a5f767530aff0deca38b8ded5081457c1ce4bbca5609f7c1be645a5a1656928fd61d6cdeda15dde2df3cd6890232a65710f614d266eaf9dba55ba99e22d3332e945b3dd3de6f5178c6d88b89fad1374d8421b7e3934ee404e048a8a65a53c7b9ea8c92c387ccb90ceba08bcbe0f10cd4dfb1789ba9a9af5f3c032daf021a56d5d48d5fbe5a39bba3b6436c8292657fbeb5565b949148d235c745e52e0ef07b7d6c5158f3a5cde4b50c15d1e504ac88212e9cb82189ccb9f6f045d47a7d6933b1b6ab4dbefe9c4ff1a96ba9198dc7587cc974f60beba9d94b531eccc4c6a98c08bd9b5a2025207fbf268f51dbd25a35d858f4441a266cee0a1c8b6a3013e2c754c619c9bd34c9c3fdfd05e4c6c12fb5c397f8110cbe142cc8ad2086ffa118aa5be04353d1af317b42a2094b78ca2bd3d8368ab496d664d314dd3eaaeceff61e65504c38ad9ab6cda7b1304af3aa6fd5647b9dcdf8879507335791c7be7d92f44b039ddc1abf82b5522575293d930b09b85593112dafca653ba69604f899d3291e06591c7fe269d6113792ae7cc302d78dd349633d539b8631dc1942589470dee35ba73267116ab5114711e0f5bb82c6c533df8353cc6fa8c92dacabfb6f20df3bcf26f1679ef6e9c30ffc642c3d1b71e6624f7eaa5983b2016f5ca87ceaf070b003a4fe064a1213bd78a2dad8d04bc00b8311976c275c7a6e885bf74d602ba31645aedfe26fee4c8b4e226a6c3330bca958ddc53a23cce11bc65e537e343e61fea5c44e402f8f460785b6c41e5d22cdb2112ff508037ac538e4e3649e20c5529fe63626026f534d0292a09475b965b5889bc67a43c732918108393dbfb957c728066d76772b74aeb5cb61af72b86d196616da3661adbc8cc6ef4716ee3b382a95e14a3e0902ee3e6ab708983fd5e0c9cdedcd3418087dfc4b29dc94717537cd0cdbe47d03a159d86c825f7ea7a370e56e6e04676880aa0a81727d1dacd9918f87366137ec9250062e89a1f08aa0bfa25f183e0ced10ae8198c356b11e40887a9d9db5fe16ec17104ef6ad086838a7affc85575b45477889df8dad1afbdc8301768a230e2813f48e9c2c585940d75ac0261c0a802cbea7aaf89668de4047b0512121e424e9fa032653e53fa089371e980bc3c246d65c6cb8a2766e8cd0caeb0e7d15e983f0d34683427cfe1d628abaa96dabaa086206a3c63776e02e8f81f20527df3a6c5267ed4f8a8ba5d08d952e8cb6024778ed830b7ddd7466767cef5a3508321bc64eb059d9e3bae3638f49dfcd4fc40861bd675459359d0984434cb5c8e7a36498694ba34c5cd6b2a3252cb24b7427ffb4af5b9e46dea52f1febe4a533303706e67a6b29fe65b2fd9ccbe21841fb9ce7dac7a7a3f7bb400f4e3367d94e0a1b2445d1b274914475d85a99a69aabfb966b75bbd53a04a04cc77e2b46a0234e937b6b111d3841852e93efcf48074ee8180148e591678e125f38b6b32b13fd3f81e68046bebe29fd93decb438af21c6f1d1a0ec4f9f9ce8ef0c65481a335d8e6f164bad060266627c9e49c464008fb8246964a78c85702ad8880261ecb2806866e3d0cfa1a530680220bb15fd2e1f756bd7ff0a2198b386fa292acc507e35a7d4a5eb8cfc84a00f077563a5712a063e45fdf7bb12bf23f6de3bcf3804cb9ba6bb319efb45ad2825cc00d383696cce225ad2869e686a809dc963151dd963b0a8c16f409280640c26786d4eed796b0605e82c29b11010c240ead06f7de4303397e19dc2f98ee61365f14f625ad7774f5720697241d7f689f19462d22abcaac56f17055493cd1d7747e8f51ea8e316efde158e9256bcd1eb7421dc9d96f733cadb24b80443e7e1de41a89f305bd29ca978eb2297e94951f16eebab6c5fb08ee1dabf4a295819b6e2aa0e058a59932f28394ccb9dedd40dc0bff4abd5b5c0b5333d99542b6bc1d73ec9f3a18ecbe25d65df6072f385d4f88db028cf80ccda5ca9f0ab7794fe9415e0b5a7b0e990dd53512e53462db2b00093aa1c5892cc5e0f9a410a67d81e5f91d0222c49a48d7950b14b0a4fe0b44a81c4892cd3b0e710f1f5abc92a2ccdb2fbcc90d6b169a08ba1de83334d188bdda38c800e154f5ee92b8262ca2cdadb33a836e6798a8b0689174bae0c04e0030718f731a2f7bb055f9d9a9917faa41f2d0479fcd88361e4976551bd000afb650028e8293c43687dc68e09b2b358e9c12d33bc2e7a73cb4ac1062828de453765f990d78b1a4ad6227bf9c0bb9727f74d0ca8c1269be06971c53be5c351c9c3ec72f9334f2fff085b71d5bc4291642b5347b32d1c1c6a7141e22d80975b9c55add74a65374995f9eb5261957c9061c3f9d9f04118b28d137b0fe543d1ca4b66c68dcb6a8f049b139f0db41041bb556eac3811d1a74a6b7ffa5213c73788c1fc80931c9455a3645f2d0b2d683d34f755cbaaf25ccf7d1bd3e880c2ccfdc67b978b38cb9e63c0f3cd6c7e995ae660c84c9b5992c78b3168e60900e6c676811214b877720176c8c7f9e84660a0856d689596381833919267046a314b847baa944d1bfdfc86f258f9c0221b57fd14adc6ec07c61f883cc208b719ad8a2a564656c139ba7aeec03dee260cc6dfbfa203dcebe5a5768069138674a89e63d247cdb7e13e17d80c3c1360c06d50a8a2228ab981feeb2ae8439677cdb3101e6dc563938a8fe07e7dfa9be1048d8c1113552c70a337759d92b22ed8a3bc5563cd0dcc9e469151f188081e72a8223b1e82df93a2a379479c9ca803ff643152f43893b6226000eaeb819982d657a2a91d56a20f4fe08e45cd3a9bee2c4af16a8fc7e5b5dc6965f3e41a9f0b3b763818813203b3ecb4912c476a50edc819e54cbe462136ad198152895976da10a9a467aabae02e2751d4a886d09d52f1eb04447a396977f3f01aa468a31d62658c589d12f4a2ac4dcbe08866c97a53b5e18fa61b854f0bc4924c5d6e2e2301844cdc184f7a4487e521e4ea46d775750540b602e298fd1f8d58d4e00882cc2fa8eca3e50189b8fdfdc0b643074caf77eea4e96fddca5ff1b6cf5e11a4678e3a016bb08c76358b6c0125dc2ad00841be696d92945966f08f07fdd86d2d3a855002ba6c34376508098cd89e79d0d2a819623c546690c1d11525e488a432c4c91cf9603318bb0acbb4a99fa3087672bdee85377651fd3785070b20e8d634885e8d190fb613a897a2ea61e0f082ebfcc6b68ad4b649def1c3c72f1c2abb8183ea4223b516c43f9481adc6e3e97ac5349897e3400056fd0d1452a2ab9dcba5d097452ddb95a5536c7a41196b4fa1898f51a488657782bcc11e712082a1788c0ebbca469b1f9c96461834a24c62266dfc973ce63b34e3cec6ec4f999bc0a5791eacf9b6797c5cad5d59422be610bbc9cf48bc97d29091c2870f0387391f0c5a39a39c2310b757706f9244e4e0d841413cb9b8c01e77e38b1ba4892f201f605fdacfe1c856931c9ea7b789434eca7eecbd214916d783d3b29a4b2049615ecebf33352d6c1e444dd7f5e5ad0139d9a5e4a1ab944b591518eb03970736d59b3b785c2b664c1c2b2b6749aa8fcdf967ca208c36f7242a97e0b39bec0048843122d28e13a3e880229833a26a25de0d4896b6257f7f0263c8f8bccc53dda7a7df935846b5d153962bd90e259ad0106e556a8c44400030a6993daed0c4e4f5eaf9fdb45bb4c01186ad9baf92c5c1987c2b56d510f8fb6a51e3c821c45c3f727af168666aa09c26b4e0b2ba9f5f12129edcce807e2fbbc6e214609b517a4b104484a1d37bcb3d76791acb57ac883e149d1dfaa0f48f29daf504da4a5243ddc9eb8dd9a02a43f760c947fc4ed34355b57ab9da242627ae8f8ae46708dc0efc158bc2bb31290b84bcf9328edd3b60992456a28e029c0005284ea1dd8fe24745b7c91705895adeff106efff5e4e1b32f6b52e045cacd9ad86e9f19bd58d2f099783cd978fcc0656e38d73d1006aaaf6dd8a9cce81813688bfa0457527cda7d2f5281c13cb453c9241403a9a408f660be474067cee364cbbd2e871296bc5f83821c2567f0a0b142e47ad02050a3da5a988ab6af3397bd539a6f49b921d6cf556883fe57381b857063861ea039c9cee1b84aa08088ab705518b52682e3b26327d4db960e98bd3fca49e49c5657d2276c337ea07b5e1ba6399043a549ccd96db531594f95d08fb31cc61c8de07ccfad90ccb7ce7e04bd88f3ee0d0969d172f38ff1daf888c41809651103dfbbddf27afc94cb515f389a01989e069df953d78b326c9a75d126984365c78c5d0e5ba378b96a45f836e7951448ea97dbe4d07d4c9f87ff828358a8d0ac1377a8e48ffbd743a7696fb4b706f5fe78dd68abccada020299cc48ea0c189122459195329441879137f6cc1ff37808d532e113410bf6ced4af57a79e88631fc124933f1756b1ce819b204d4ce5dcc9b45d00790bef3cf0e69dae219c1667983166dfc84e40808787a2160e010151c06a457d6946d35f09249e9e3946bc7705f75f06b71106121308bddcc953036a033aeb414a6f9e02d6ed374bc7b2903da2818dcd05733335981a5352bd25b797f67edd4d04e353f30c289418b638795f43707b75f000b86ea634a5f92921140bf964e18cf2eecc8122ac6968fc75236de6d23e82d6d64e665915f168bd5eef35e76006b71e2b75d3455ebb265a36133617bfd8711f37eb98889a03c60a75aa8acc79ce3f5ee70b15502fc1384d0cea884b8e9f143ff8b7439d4c76a40087a890040197d40300c6c254065e9629331fd491bab237b2f11b0507f599c05e83c44893a54ae536b8be432f11093ed5e72932c8b7f954524110dc33a0de309a5e4bf18880d7378ef95a9b92d32aea070d27b05006cd02d64fe6842b0d31145497d253bcea8d9b95ca62de4a6a6644894340be284f081b2507870aaaa85a1a068fef319623659a69a1c75c4bdeee3d63902969bd1b0d24f137816c1ae0da22cb1831fa4da86270ba6e228cb6e411ebc62d53562e09f7817b8e50d675a5086a6e71c73f3c369e2a929a6dee7eec2518387a6d89af9a24cc8c6b89f7451cfdffaf448974c5f62eff86f799642c90e98844d8fa90950833f05718e797d8d24bfe3c136aa683e85553d13069d9f85a99b04bb687c263eace7feb3e065278be18e6ec3224141e4c64e67075d6ade0e5232c81a3ce50a5bdb9fe150aafab6af9b1e5c94c5828369422300fa4d2da07ac418b4eeadd6dc0a7a7c63805e5641b4f1899e0e2c891d6f3087e2260cb7139cf02f2a3ec397403195c5cef67622b4c4f683e2df49b04d027bb93a6d60065d6e68914b500f99d1b60df17de3a4822b2f3e9750fdadba8b1408aa763f109d200952d0f3ad9886f932e431d33806d0e81e81051032810eb73575c230bad0b24e45867c2df7c919b044f8c4daf059f25445b4614be61732ef2b74375e10125b7e3badc9d73205fad31e38019a061f333569f6f47810a86a495123f648d0e0c2f29d6e8a257847f51e03691dd8de23c9bd1244baee08a8cca89a0c8d09050e1258c7a113422640d6258873ccce36f0b2653ace6603deb73103717e70c3e7921b8be8cd6634346b7474a76f59bfd83420497f576b829133a7bfd7f39012c89f0bfdc4db0a5022214da6b741d691a8df55cf26f760cdf766be1624adfc6100a3ff0d5a79a7c6961f21d9e9c6622b8899f63632a3ce66cc4c9620abd9cc401b95c54ff7c8f3a4089c6053d155f55e0e54b144657044c10a5cafe20be86dbb87b2d4ef3ca4c3d024928bda255b58a9aef436a0906598f1f245b08c626b8f48668a9356dd231f6295276a1aea4cbd3d012782b8132ce7f95b9ee08da287445579c9b0b55d32b76d01bf2577e2536ab95b5733176c3d8374b1e9b36fd5f437389cc9d6dea56163bbf8d167c0737c69b70eda40a080caf5bc323b0ec296830b815a99661064194f1bf5cda5202fad8fc1b8a802993513cf253e7d5994a4f51db19d925a10825b52e866e7e64c989e3900064fab21372ce704909fedda4263bb76da36d0bdbfd6d223cde630b32979c9beb55ee1d7dc63b600a9642762aef8c8ab36fa425c3f67fb121cf8dbecc6b4c77108292e321d89f4acf79c402d4cbf41699fd6af7d136756dbd5b7216519f03cb675259a91d29ad3074c47210b14f5130993ad873f52269a94859ed9a8fa2b0cc1d62389f3a65b58a2f18d1cbb31745c2aade65d3c3a66b4682429afb2b8808565a4c246871eefda29873e9b5fecf46cbe59212651893522553a88e460ce05296421da54abddd46179077aeb1d0a2f22530ed1a67a5cc71a2e6d875f31081afc6858f482dd46cd2cd9ac990e76ab6460d686b300d402918ffa22ccb0196ed644fcbcc17176887b6f150eea1a5dc5e5870244891150cc67513f156035d8b039d3e5560f553cb87091475cb4bd881254b5a87c10dd561bfb7a8759da49d457e3c3e8d7bce7647e41938fc9efadacbac1647012c7e87409e111313a82b6a27f513ece41a30cd6744f4511a08582ff03d8953b89fc84601e3adeac64b193281c2c194afba78656a831b57349420b8a2dd0d5a62240d8e6f696c52509bd3a402cba9408f1b9e6d8361e748fe82582e96342d76d780dea412d319a188fc3601c590b953c0bba055d7b7ca852061909f157e479a011396991e49024b41f62cb2ff79c704decead5222a4a10936b34d9ad33687b5186d1fefd5fe296c19c9ab73d53d4925f27daff7aa95485c8acd4490456a127ad10635e96d06340a9b600a5da21ff65fc0438268ded32b917a941f70c07900e19213ab7aef515d75f25b0790ab79b29eb2bedfed53061b8cd3c01bd16e33ec86030295242d1f064bb6542f0b96ecc61b1984dc05a4dbbf33afcc80ef8d25c3a9f5daa6dea60adb2fdc3d58807ab39fb15d036fc4faf425432580def9efbdd715b379036377b3f0fdaf1ed49234ee9e5948955916c48ec225dd9532dcdcd3579b8932bddf2e00b2955013a54dace679ca783130890f8d366d57d762f515eba34517bfe7a11bca100ed71d5bfcaeca06c8a1609def75797fc9786916e778805a30f75f416fce93e812f7b737c1cdcdd6bf7afc446f9be8625ca0fdfc47216cbf038a41c12ca5fc6d19a498f9f817e9e4721e11b4e16e29adf3d26b72e48045ddb182a6430771ff33892ec9111ef55c904e8f3dcb2b60db0862ece05e2bdd02eb220af22a13a1ec6839c998446ae425dbc86590040c08f12ad65b65815013baae803dd33046152012cec26fe09e84a1147883ca7b841affd4fc7df2219da3359afbd75c198a5bdbcb9d59b0bb3fcbc95f941d8b2a95a86ad0124ad2eb5f32e81f9131d8fff792b069a9a44b9494551fdac1415d0de85b79449ffa818889604de8a54ee49664c0ecd610be89694428d34d700bd09a4d1b38642a5c52d2d963ed57d63456f437c426929a6c4cb75561759b55f4443768cccbc70c72b73a805f05813cacd8557adaf165998c87cc62d9a273c46c030bfaa343996a48a04a8b4ca110e0116083a0f0c941ad960f63f6f08bf8d5fe25942d318cf1da762e1a1bef75403f0578c191e283e8c1bc1240a1b12028acb2e81fc81cb9f7890a596075c12e7b0ee25e44fdf3b38b6418e5d2db1b3a40764fa415c6cc3ed6a4402437e29e6ea277fe55807dc9fc93e9e1a1fdc6ea926cd00de9183a013ac83217eef76baeb0a4562cc9a5459cd6ed8375df9af2ac3be4c9437d55bbe6b629aacda7e80363a5424ee8afcc965f501aa07ecdf042bd0d841c410f7b23f3540d5bb9fcc73594aff8c27b309440a250e121bb0c21c930599c1030a945982a244d1e36f19e440e48d4404a81c99d7a93ab0ba8e03d53a4433eb9750eedf04cebc768509548e3c6ba0f581933bc22573fc858a964e1c793d0f7ff54e2a6bdc64283d6168dfd0e315aec463c181dfd691cf69b0813b624de7f3a304370f4ec45c3cbac7fff4e801dc4cd6baa65a9ed120cb7e967fa0a5facddb2bd15f1a3ea99d7e899ef62605cb26380a4ed1a679827b58f1409d04e66007d814c02a7a4fefa4a9fc06d8a7189821757a279bee3008fc83d4da039a27e879dfea4664934c5fc46ad922997921a89c4b4fa15f5969f569bd0c7465f9fd10724809f3e651ab7e3b3ddcc0d4322f4c7b78f0eec16ab1f896398593a5b08c931536cdc65eb38d487b4d83df87b1b18ecc5c76e2f17f1671f8099d417249e0db912612067076265cc5f9592550245971e806a1ff7acb7baa42b30a6bba5dad58675e40033f8a60d6f01873a6215945320f82f7c76239f6845956242026fee51f6fdb7f26ec7392c87c666eb8100c22520bacb2afd9c4907417200fc246a46668b1c059b3eb6cba7498cd7e716aa39a1e1c3773b986c624a30e69e093cbf0721c4b2f31a95ac11c91fe36d8fe5a32e5ab57af042e35b3e57f4063fdcb14e7c0e9319f09b073ef4a6c0711d620dea6cb520a72d2ce0200285297bd4fc141707c2736a74dd2418cc8ae5f74510ce8c47057bcbcb3f05806cedb79871634b5113ce007eaf7adbac6f92b0a9ea9c867b8a9a9d37ade8a5d0f7a687e1aec25bcd691281827607d928a281ea8d35b3d25b3860451644db979f053d2a1771b0b2e8ad61b56b80808e9ad9676bf90ef56daf4dd809e85075a471dbb5a414d49b85b6fcfb540bf5b429c6e9be477b3792f7bdf4d17ca1ef2e469248e916e321f00d482fb52839f322b18bfbb7d26273e816e3562c3da7459959066b463a2203041e0417e0002f3049a08b7e1087faaa16f430789462c7622ae00b1f2e640ebc35b1e363daef1c1e687d85e69bd523c9725b3ffc527a3e2cb7a0c231bf4bf2bc5aed02a34adb1bb9b97b0ffea90765565ec41b2b4d0b45a05b7bd82e36a52c0cc9b268c0e7c19583bb4c0faa7aeda537d0acedc1d4a91b7d85b3ee95ec8250e42d5c4eb18326a440ad1e16ba2745244ea4c55f49603620870bea6ac79d24b0d0438531a32fe7ed13354acb3b0a8da9d967ffb0414ad354f961318f69457c6f2827ada6283e8db2cac7b75652d944616567d63292f6dd3b883b3a49f3e1faa0d8e4e430e97d94e8895376db39f4b6d49c875cb5ce38f845319afd9cc9ec2fc6f59aa06ad2cf705dc7696e1060305e32bfbe31733f8c2c749d6391cca461d56027fe1ea2e489632da3a3e4e760f2a718e69dbe63c8edf399a16f15ca5f7c74bf5c800ab0f2bbac97cfa1c3e5f1c9cd7046d3b13165599af89355f51ac36e20e724656c5dae7996a8de4292d611a22742877c2551bf319bec3c0d1f24c821f1ae28ee33caa9c093b869aeb99bab27928b8a6b17274db7ecb69eaa6eb50fbcddc4a70d3ef00e208e17fc6f18faaf5eacf898526b4d0885eb4d161f4a7945dd9ddf32428a7e392af2245ff8a15bd68a21b0df4a253e8350572e5bb34440aabbb50c610068854070bb771bdb05ff613684da2abdec52a51e0ae519c5d0028c9ae9ddf50de5fb0cec03bcfc6ab277095e54331f102ed9845bd402fbcb60d8899c08b38104be3af4edf4518443ffa0b95771107e2affd653bd080392695ab371237dfff006e600113713d4e432d95f37b9e5d6be16e72d3dd6e71d79d723389d36d565599607c90b8256f6e4b4e50c8fb085480700a344d5654705a63ca739c319b82b1fc9bc34d41863ba341569a2340c9ce9c7b020edbd9c0cda13c4424deac268b4906e9129dc95e065e69047dcb46bbb55fd23b80d62a1ff5b36fdd81ee9f3d003dc7fab521d4b80963a73b0e04c740283942a704d7771a74b1fc272c6dfa5b8981264b4a916aa13eeb7d3c48897f2118ea913ead7679c2f2051028b8c250b96aaa802a68a566c248b3ad80973c09447e1b4c7df433dd2261b7fc8ca91bcadde19c400ecdba0ff4cb98f5fc7fc6e9a8873fa09272daf89fb1f7f40ff3a4fc7d497aed0400c084c90a8699bf3900667c8ecfca5aceb61a28182cbc1a63d6deaacfa21ce3c1c81538059eb0a231f502226e7a180426a16525c665662a8779a62c56abddd61c57d4ac4eb19bb3c9360798ba443b62faf8f8264db20e621d4700f6e43525f52611395ab18580c639b19bf31f5a7e5d7e636fdaa44c9b252acda5c58faa8da83100a7a1a2f06669e10f3e629331b272e302f3ba39ce49369bcd66638e5765dbfab42b63db01e0e46a01991d9e88e013a69d34cc0462e9944f3448d28b85646f6898473bc8f7a3bbff236d4634c659af7d9a10428a0951294d6e01175e046a11407e508b5cb30db588967ca845ab9c422d12a700b5282161a8457e872ccdff0a0bd4030ea5c3b45fdae07357d3fa9046fe823f711d771fe33a746b5e74da1c576b2357d7ed513df792d0ae1964ffc02a7e9baf880c6e90fdba6eb5695938fea410589823684b5ed780dba9f1f17ea60c93f4c87fa5a92c7b42c848402598e4933c5712a271b2626b3cb66c0d80b767cdd5c3ef4daae1032d86d985f94f79ee8503842a65320779b43e6b654c7adde6786427657ae7efe981bace36e5b05e826df8b6ad67b189ddfc85c20733fe82b655ccf8ec2c6ddf1b75e2193c2a242c02354dd783e747616bcfed5266a9cd9c416b33cfd35cd27473d5f019b2cfe1370865a1742e6fc8fa3dae222ad7d9406dd8ee1b97936f5f70b4c0b8d1fb9a0ceabd8ba598af753dc494ef8111425b8678615af037f85273104e49c240f65e4478059c04bf980f42cfe9c7f7d84fbcf34a5a60f670148b03d37e09bd9666fe8bdc870940c6ac35042a1ca767574f2b3b70f73dcf1452fe40d009bd8af139bc34b16e0d284d7a46f8672bf922504c4abac1f7c02b64afe29091747844dde987de1e2e365101f2ddca7ac33258e6453ee64564cfbfc54155c6df42e3224905ee4d0e960f2382f818d60d2cd5e49b2a39b58e10974ba336ce7f7d2460666eb22ab11944c535c2c30f78b105469d1504ed76da0ec3e2b608be36035d74d7a16db4ae00161577575aa87ee10fa66e6079f00566105f4605746247d531f2735a2a5e473437888ae8ca055bfa6fc8d0246718d3e61d20cda99d089ac561827b00500c1541b54e37f149b0452aa98fdeea3172e6b5c850d0880c2f93577027b031bd2a45a1d299f57ea9bfeb7bad02db40c73d30c85d8131c204d6ff2020e7045830ba5434f4b1718c9ec1a104edb28a746b80a0ddb1d833aadefd220a77e2d37013f55ce6e3506736a166276529e0a80d13312b8eed894db2d1b23e735d044cea34af26372d34aa3f1635346b01ad29249032fbfac82ffcd7a65ee20e6d85effdecd505ff51dda3ca861bdf811e3cc4cd225d4f5cd2c1e4e603ec07217b941541059df416a37f122274b38078170b496ea559b38431ececd1a5fb40ec9f9683a403739b65c123c7e06d1005c95cb3ef1cdf760b68d60a1ba57d1b38423a9766e4764637ca850ffb883197096aa0c67fc0739fa2caaead287474deb959f5efd824e8839a33d86b84a5e81f60e1cbd5031a29cae41d870ae535c9062a6976a76d65110b847ca001dd0a565378d27c6dffcadd5293fc7957f66697d1f755cd2c394d6e4b39388c143a111195101976ac36ff3af0c380cf3aa8128089443d0b4700626ed7799601d7b87171f1ede2df5f04d079570ff880162446dcda563ef1cddc41c0222b925f536a747b3e05b9e113ef9b681c79ead0ef0167c585de075770422ed7f87c842a64404a15fd3c0fed4caa1f0cace0a7d81fed8b3e2a1af2c407d36b09931d21bce240be5e8b846bec05719d8305f598f1a6857e076b5791f177b46ac8d95edc731cf8406e2ccb1a87541476735c50ba017f0b062300873e03c119d44647d6f74545e5ebfdf6b8801945362c0cdab2c52179ea3e60ab04bbb0b694748be80be36bb9fb1adae186b87269142d1c321ccc6f849e7c0ce63826ca71689d9e9b4132f4a170c07b8dd486002d9734cf8b94f89284d21cffb77e8559ca3112ea24c0a6184accf69494e2faa47fe297d35647bf5ca4a7e6d3e0f75be10b5b3fded97cb82e7456e2914ffd6eec6f013a06a314091407e43117050a866d939885ac5a0319674e80d2540a96b5024b77d5417e45e0d595e85ab297440e3e2854cb59f7249c771624d62b48d6bd89d4683da0240982ffdba8ac7106734bdf1b082027e2307079b90097ea93afdacd1e02d923c704259bc922e68c638a1179958bc62da8c7c5a0f62088890b52799822895fd4c22eda4dc07112cbba411125190e9112003f95f0987862936e3209a53345b0bb3ec4f213081a456e0d84df8c07a0d0399a0716e63fa5899bf741dcc91a06ac1055e81aaa7885ec42b5430211ac5c039a6972f5707cf776ada723d6be5db2d027f520246f8973b0ea48a448862bfdf894da0991b3ea36806a4a6007a5b49348acbcd8ec2e973b8e2f33a6a837881ec1066da2eb0a83b6246e140894185ff2407eb1c14df26152b2485227f5d67ce42642128c6ef749772acf22a1a60c4fc4f402fc66dffbcc89a569df220ea1de5f73fea746552553ef0f94768ed5832743e28751afc5088bb6e56219ce1adeb910ba4ee281fbc53be2cd6d2a2c8159dfa29096cf2542134f62e87f999a3c925d7b1abb4459b1cacaab5a6d852bb64a7a4843d34bd5d179de5977dafc83b501a986f4b121690573adfc1c40633b2ac2190aa6a1ce51834cfe28b2cc4b8d61c306b90e90855369c2259c5956688b3722f01293caeea430207e82512434da5a49a21f10186b6ef1c22bad053040cbad13eb6b5adf336746c069602020a068d70e3e126bec50aa8469c3cff4a9a9710544b0f4e27b43422df141e06ad3e850bf4fefed9a4f4ed73d76981e0b7341cfb75a1e15bf3a26961411a0adcd05741ba0372b1fa589d8896e3b0e7126ad41d3a567c89146ecef4ed86318ad83279b976a8466b3a98894ff9898d1d996fc7136b90b12d7bbdf515d978e0c6c9550c6c8f21b8c0e99e408dbd14f5167954359974eb7eeb2eb556938dc7c71ab457b7217a76e72c5b0f70a0d0b9160a28952db5dddfb9845308788b35b47c4fa403d939f049797e0505ede4915273392f41d5ad2972db41a009a5ab1c12b5dceb6c64cc8d7cd3ca15b7caa4fd5d4f100269dd741059731a5a1875aac3c7b5a2e42d43a5c0971e455716b7762b05c1ffeacc89c294a77bb55bce844402190c21eb748ba01bd30bfc0df5ef43e05a15c29b399f5b5d225ededa7fe1c3034a03f6f1ed99f4dc71f5b5314b1a9951618f7cdf59cdb4cfc6773c74f418d8d1367adb664fa951ea49122e188c43889e4e2b656ee4372967da267de3de9578e6d5198447ac5a3f8471187ea44f80076a8b826926dee7817030cf54107e1bdfd6eaa845f9610800cd5d19b1fbda9f7b33c258fd74c212fad52647eb24288de47dabcd724513d61dc29bb33f55b64c6cde3c0aeafc5ceced4a2eb0229d4695e8ec6787eddc2890584c2c149d7da1133e8f69083c69b4bd08d875980b831389dca655c275c1bd1f1107d9286b23e02c5f3c83de97caa20d833aeead26eba887f1a8a78435da0ae60d39a8dfa3214cf6692f9f97e8c8e8be08f762e0ce8d25527b3791724790bf95b39c35694852f6349d09188833345c16cace82661d865984858f4f91a55090ca808d7e3c4efb16d5f8e15727751dcba5cf26342ef389f7fe149b34904efd4295d2b5e5c8dfe70580f1c1cea7748ae78800833fd76b2f5374b1c2cc348976ba0988eedc46582d527132ea5e06e22a2ba302f374553dd251f7a700e2ec45b3aa6d1d71d2f1b8a0a8f987a609550e6d8451f15fa30ee40accf4099febbaa5214951494c3a14b578ceaa218483480e4eb52d374c627898088b4c5cb9d17f2b97d1a104160f81e25a7abdc3833fd6fd2d529153de0dd8149e045c5387d21876d178de75441bd2155899899de9d871dc17a08a929160950cd05d898dbb95a4082e4fb75e574c9beb85af5273edb5c096472241b9a2c393a0966c4240d2623e8d44d398a7169524574fa29fa41c6606f0c6ae910b42d33f5dffd08427e376cfbe02d498ba1d1789450050c874814213d00c842f3730bad7f92f6a5ed61602edbeff211a5d8acb2d0145b2aa19f4eeca659f5f727310382f50b721347251d661316cd6196af168697b866b16a34e8897b0d184b5afac080a775c44ff8f4653e0cc9d20b64d32e65443110619d0106cab77a06737f124ffde4633daf6a8dbbd13bfcc3c983d23f4fd9b27c7c9243e81eedebd1234d0c37fdda02724f668beaa827b211a5da13109a1213d8a4e88c64e9068494c690480ca463f30112f9b331f2dca01b903f0d5ef360f632455a1d05d6e0a5c7a5c81fde2d5354b5253371f5b07894bfe2bd155adee1d93958cfe2590696f98a3c51a22333137928d0211a597c3051b853745b16f3734a7dd44828f84c81358422b33412ff93959567bec8f39d82eb306ddb306f1661b4eaec09629915c38815fc263ac62646a96c9620eb4244480447ec939fe9467bd723623e32dad47f24107f9fe8c008cec9b4bd0722a8f9523a8622e9268cd5ce759044da8e7dc71184cee0871f494f72d7083316967bc8b6cd872fa588d6f30dad1550d03a05253d4c7c651c7d86283ca44a999d92cac6c6546840d3995410c037510e85d1647c35254d0b88d139b582a292d01a0bbe180093e93e278e7fcc154e45d501b7831a7b68f0c222be702c36a279c86d56ef938dcefc3d1665982f6d6fe02e2a193bd68eb5a51dded6ced2e948cbe14e8aa632316e81477ea7a53075214bc966cd74b04d998a76fb646429305161db490196eb5e84377d5f167d90046500a38336a7ba76bdd3f8c2c77b16e28d7440e2eda56cbd65872c09884231e2805e3f8a8c25c5a6c5826939da017972770b4663698eb518cbb290dd5096d15e84b75b012418cdace170aa47d5b8216be134c87d56dc251eb4124208b29230e2bf2523c0c4b705d7e462a4dc5c8f8b11b3547ad850f4e162cbacc56323f65850e4b1e2d80f25f2701a2cec0b65035c5e9d10a5dcc07514a084866332041acf43b7e22edb6dba4522d1f2840867294e67b3820e71c64aeb78d089446a2b51effff08c85c3f3f9dfe2bec4f8a9515c8fbcfddbb69ee9d7561f24969415b3e34d0afab225d2cd55619d41de710efbaaa929d3b986d29698c616e1f164eb89546dd024094414eee06ff2474b2bc3cf2b48a999005552ba1bcb0632424453d9a2b86b7bae0ead0615543eb0e6dd41a66642a440d1cbc63d830fb9c82fb97cc1e4a076461d2990ac44a00e91ea70c8b4dcf12389cb70dde222cabc5f28f921746d2fb1477483b7b4b45792209d29cd3c08c3b34a61a4eb58c64366b1d881c847a17b01db3d8b0b59fa8f7f0f81d5edc4da95e87739b6be1f869e8a03065331f1a24c28a28a616f4c3a430c1f850af9df9fc39f29cc8746570ddc2cc46bb0358353ab225e5b2da175ee4d74a32619b2586d3b9b333c2bc90de44436a2547b02425349fc897eb7705c1d679109b9201fc4af417bfb7d7d12e93d2e153d6b1f4d7efbb3bdbc5bf8a1fe7d719b91fe3141e8d6699eba21b387e090f37fb149ddbb7a5037fc9c82934491020073ff581a05007338f19a5ad5e6030199b160d8d98e003b8705f9e5251d5402a909620c5bda5770a46dba8be6aad01622027f3987fb841a3ca413bd7f9b6919bb213f864339fb175bfe926052ea3ffb6814488ebc4bfded1a8ff37e15572cdb4d7ae882c349f70bb0ad4d94c8a6931eca15e5df051ee2ea598a858686e4c45467480739f9301002a212eaf6f4a9ec39daee4584afb6f38d0633d494c329b4fec8b6d95aba32c710b3933c8a6fd2800e8dceab25590db4a1ced0bfc7630d20a24577a4716b7e3b2932251eae34019de8868b90fcd2dbc67706f81ed1be033b95c324297b1442d707466811c32aa3c6a19ee2f80a4b79c03bd3d018df3fc05eb0531fe290d6468797fe83c0c05179027466ef54f77ba64ab6a33a3e3d40ab88f4284ecf1526f6ce393c54384d8f4e5f1d99936ffaa092a16237030edde34c1a8efc43fb46220929dacf345fc9789ad721dd821dcb5d436a4d8819dd23fc08af841ad9b99338347881b05d5753076a9b2139ced56106cb657b0bdea98ad24a9ac4089294096627184525f557baf017ac49e1520041e92a68f09141a4333c3a0cf8778bccbf0f43ed031624003b6e8cfe82c1f285b8933e746881fa7fecff82247218b68a0aebac8a3fbc38683b26011d3d4f0ef566e71c844d403dc7e8912ea4e23c0c0dbc995a7ffbadfbe9d0fbd2984d23acfe7882e0ea8f7fe4abfe98cbb4ab3f46077ef547d69b577ffc4489fd32d2261f99524c9c13f0739134e758dabaaa5d0b11a4d245e9b470aa1fcbd456cee1c7f9e1d05f3488175a669f6535b6f92c8cc0c959f7085f198300e041e1c33bc4b989f2b9030c2481f0a2555907ae5c0b2912504eeacd7094797dab30cc700109fe372a2f58bfe557dbc660ec5e61bbe6c57757ac22dcf0361e4f46369b471261882798c8fd896e7d93ec3436b272ce5ec92a4d405651c93057d13d532fa65e92c30b59f217df334c7dd5c24b1d2b0b5659e9a8c422231610cf88eb82c1afa4e06654046df0c018c004b86b3f3f30f74dd9ac9eb7c2cec1415855b8a637aa5807659e5adc910559214d6e346be6cfc154e2eb40ddf2b135ae76d0c13be0f7f6a77b2464acccb4852954b83dd51443beda04f2ca0d04ad679235f3717feaad1632e5dbdaaf44e25eb37646cdfc633ca3aff2bd8912b6473c1dc1332a327059afd13c47b1e3d469a744efda053e029d12c1ab07581a3acf51022d1a6b772ee374a1c32b9b3541d2f55d7d08e49868a7657a2a090dd47a9c10a1d3e649d8c60eb436f61f366eed4077acdf742448b842f3733cea843b96c86d5e656719b71410bf5df3364688fe296549c12f419b3d66fce431f1e85ad1c01d002147df34848664c99c6248499267707b7afee90180d898062a9915218db1838c1a949a339df810dabd1f077209436c1c9fa358368835f0b81866589f6d0811928bb391ea4cc3d94689bd60422e8313e89df919c7ad51f53330c7fa6ed148fd05520b947eb6128fcd590e04aa61ef78c95d196bd704b5406cbcf56aba636f0731340f00e56c34ac0b461a5d64fbbbd5e49453445742c3e3fbf742e71b819bc2843808375162d9491e5f14638e3dd4e6257124d8afe238d7ac7451d8d45a449e89656a6c99d913009da1808597bb86ce73090ce32c0a6366610504633bcf4c295724e2aa9b6126877f533ae4b28732c1922682ffe1ce8232a80b0dce2bf2325479cdf9e3c8471e6414815fea11ab3cfae408b9d6f181c2b724c0f2d0d7b82aea1df3351efa658c57dac9949451799261ad4af2d270f72d3277d3e26bdd80d6fe6729958826366d886f3b01cb6a29ae0129a1b1f35cb77a33258a00984d39bf237659e75cf7eaf277cc6800e91513bc241b0856487917d8069a2a38e948d06f63c385f0c0b0d308bd920407d066bfc0ee8c3169a18d8c17b293dd096c7f3c9027159438e500237a04813e9a300dbd83771e126d35356cc8aea4c55aa8debf27b815f4e214a28272758649375a9fb89adb22fe6460c5f43b03aa4239c9a6dcd48c08c9bd6ba1ab01de59798994c01692118599eb34a44345b0a5c7050b9d7d1a5128ce7ef9720ddae49ac92eb81ce7dba8c0a2205d5eb7f69eefcc05506b5c8542a626d52dc4bcea719d1673390caeba82f545122ef42498d14ef32362d9303674de9f332800fcf0de2ff041d824a20a3d42e500d0f03ec8e4a82a407767015b985bdc0c8b22a84f219763caad38caceb2642d11716e48247124ea21818d5afbbeb05c57f7156e26c2f5a55ff86cebd0168601fdc856b0ff83f0280619917eec9dd25ed2860225cbdf69f18863a809586022cbed94dff82cbab0e73ab158814f4270b8294732716a57a163df22ef921b991e4bd091ca467a3844bb70d570f7ec9468f0ea81ae83a08e3ef23f3ae84417ede84017f4a17bcd03f3f59e1bac5953e2c91bfd60c7387e4e7e9c61b219465f48fed898cad860757014c05d8648824351085d981c78171913ea2aefc65d6ee4c8dca71a18a2ee3c5bee4d5874ca182796e7206be9f7bf2c74e8e1abf9ab2bf32c8d72142e4954fe176b0368f6154a5757fb34160dc71d382344d9c3adb18542ee7acc87487e4b74af5480413f500f133436fe8d4449f0c213bd8bdaa5297e95c14245632fecc3b41cb8105e83934ba7bae90a87d3d9e691a3279edc6c926ce5592320843d0d3728faf401e17970cdab8d04fb412ff40845fe755f741137530c8951ca0556b9f84ed19af725c337666c9593d07fe150df3b8a8ff650ef8a9b6f29467640cb3991c14b6e6a835cf2ca275bd7d7905b37540bebbef34a14ce48b5c483a9d42ad10e9e6cc91ff87d18a860494d20e61857dd99fc41cedb8cf46915fb9da981100f247fc080deac37c8fbd8254af58f884fff201b7ca5adabc850a8c3fbdb49cae5d95bb7a06b25650ae9817aea978dd304ae03c56c9dd5555b9d0eead87793373c8e796089d0f368a652043185a598f2f84669116dc464346eb3a14b28ad742596b2e4957a4638973d1967f9ebc209da616ed1c7f61b2de8d1841efa2e1e1a37b5801be6dff923eefea4b6afb169078ab08a871556c60167704159935900d98cb9b90c842d68ce122a64fbd27478122548470ad2946e5c4b1049c8f7b5c19c209461a2570808cd3d548fceb5a1fa42ff21f00795ad93a14b30122165c2d3f58c1bb2e1aced3657e2d0eb409a304db8633140cd38327e642b674d42a02a8048e88005f30e35c41483c2449a9310deca07760c6de4046925f526ebb41e90b9a492965c8a124b6ba9bbd666f566fa31619c055a3038e534fe7528e9ca5f8efd937ca184ae394d9ff9ff0b25dc55db85124af37000bd99980e9396997f236019bfb678c5a7782e796451007bc5fe1486f0c0482e16020df034033c66ff7b68f08037a7604fbb3df297da781be973e0cc8f674f08aaa7642cc7fb044b11bc77e516298b4aee50d45843792442f60e34a6051cc85aafbcb876c00e92119307cb3e80948988426f8bed2796b238b91097beddeb0523b582727744cfc7bea86fabb8f821f7076f70c9ce62a5ab5c7d052b57b5f2acdd9e8ead571924d9cb664070a914b8fde3b8390b73bb3beebbe526b7de8d8209dea73baca4967c2f6a02297f447fec4004ef7dd2f3aae6f145d14a4bc9621d8a23c11bc50913a70ef4acdf6b122716558af9ca380f0e9593882af72994ecba14ad2fa79fb3057ae6e6b09bea8b3049627ab3d14356582e8a6887b6cf2bf599d679db08f006d9477a906733c9177e68eefce83d8037f3a763a5b36b386b934719f07fd529964c6290a07581c28bd5e4bb3145205548bd23630d8b42a2aa4e17e51b3ed1bcbd4a1d6bd253a7c4fccfa7034edfd7be0f77d034c078e2ef42c1c640c2a92890d843c88e0522318678a4510ededc1c6bac5f626e1c0277d6229ef2cd34dda3826d11e0bcd8e63363c48a6d05cfc3b14f8dc6e8b807ec6e33cab7ad8126b92e2931e827a58b612d177f3dcfb137638a1d6cdac12db7a53c2a2a340cfdc9f98cd3dc72a49a44d6888c44a52eefb55ca35571ce65e6635a6c6d66f0d6a43790d38c08298d4984b45cbdea0609d67fa80a2019f123f23290c1e8bb06e0be5ad7e9b5c401d3aa7cc095b18b7f7701d5462077a7ba5acc390504e067de190af26b6f1996e592fab4b1d2c6f921c794690ac225092366500b1a6389ee2ecfbb27169e51819cc89e942357ba9a5dab65cd8bbe8d457ba121bdc068d3a2256b493f951a11b4935be5ef8485c8b128469d67435113d2f3831287543e3489f444d53d3d5b2801cef231c020feb239b129c06a4da5bb3f94465b2b562b189f571dec32bc9d3856c925ce46b6ecb0404e27f71339fbc7ec284b250fc66901072318b654e6b7f2a9b2452b10f5e898e401507bb0288f1c202d8976e2ce35e437fc710d29f64f97f002a5bf0c471ea49b79640a52bd1c1ba4897d3ee810755b865c2e79e638dce055498305156882a12b3ce5dac2492b723588acfb8f3de4d51f301db0ec4b74fe9d6891c98c7bfb4ca3820eb32ab087640c5b74a0fa637a15cdafd24f64910957e5bcd5fdcde25aa66412659edcbae44a4837f157928dd4adc2bbff1ef7ba46ba31486f9eaa8cc0d6ab0f5e5672916362486e1986dffb5fdfd47ca8beddbe510d274d5458b8c9551e004500cdb6714e5051ae49d4ecd41684eaabed679713585f65dd96b02b1a266bf558ac6a47efdf1a2acc4812b2309b5c686a42c96fb019c8091947d6c032b337326184862a111b6ee79ef26c20c8e4e5cd2adec473ef836082858ab5bbb9214ed5a6159d11814875e57b8948de29662556a627ee456cdca6fc1284efe3a010fa2f6c3a6d5a25845f1e757cd22d365fb523448045c0d9b0a7eacef95c424a69a8d539a55ad47ee4f82a82113cd66a73b2888187a6550c07a664376ec860de57caa9218972fd7eda5181cd5811a6daaeffb084e4f4eeb8910fd41e19db3065197ec64090ed2f1fb9bb93288c12e44f8e547794ae4e72c2d90bf44abf77ee52dcc02ecc61377e985b39f1eda254ceac9719d374c1317590b97222bce145e014c4bee08ae597381b9253f301c7011467bbee44bb699dc0c4127923319b5252511ecd32a0616e0a569771b4004536a37334096816d234dcbc1f17e3f746c2c40328e9b0979c09ba1ebf602d73b9f158e0b4aea98a99e4615d12fa721d7824b243b934a8b298aa04d38fcf8ee556ad741c4023ac685f3cd48484a844856273f68875768ab12789dd31a890bcb241078d3090827152b505a224fcc329d3303bf1ef542ed8a6a14398ed9c115a9ce4d4b4efac766aa6465d9450e4743083944da73e3e798ab011d6c4a4cf3aa69f3aa5571da26f9da4779da057ac1bf7a66e78ff7ce018ed22af1a3ac4eadc493c764aae093da78600b47e94c2d56da8a312efacbf81d4b81624755cf7ceeda3dde709dd29eee4fe3cbf988a0092b2875cc4983115c03ad7a72343843b69d0d04a4b35a55a75b4a4910f22eb97ef8e8e8db4cd59f9a0c5beb4d1684896bee504b42af5ec6eba782647f88818706bddec5b15703a92f04e59dc3155eca25053ab73b55c31ef47e4623a2500cc992ac5218d199857e0d50ada3bcdd38787947a57b6af234627155841fbba5f33ac4ea2c403948544cd8958518d2f8356b4c1411209113a3051413c38202f73ab5c40d28449fe2b0d6c2e48c5367f091699c38aae9c086cdc8d0870b653c0e5420b399a3948aa4f64c9880663faff1b4b90188c77735182334298f9d6365af59ec1994d0c2c6cc729c9933aa7e74c9772fe9fd3e7f88f801656d9cdf07c8e8aa38ddaa426b4975f50c81246cc8cf43299c78b21413310f792c56423c16c2fcd84050d315cc8888e2a11e178bda27ac49a58d7117be8343b55f21e02a70ff9f7e7d2272b674253929d1dfd6628b7db7e06306f7aa046a967201941324a5971cd99a956883ab832cd662e77a2928181e52a2d02335f999a3e326a428b413000c8dde35671ddb475222118f70cb224f112012cc39accd09ecd414bcc19fbc76b29381de00047db5c9aebbf546a74efa55a5e207a1aea495e000b226297b44a133545d3c683f4024a3866bcd4c28105861a9efba8ceab42e64e2d7ca236315517f111a72aeac4fd65d5a546c6018f4b354f141772bf096c2e6f1a7edf92055442a3346aba619216c7a4516b3cc6971a42ef6c0fa8f9132400f64cc357536e428727c2c80e78c1ab00040bb7b89b84389b011680f3681733964e33c52e2e29e6bbf97ca4227c9201186d9ec60e3f4edb684a53229aa749fb684b5f293a4090c45c0254266d860c88901618bbed3160eaa80124773b316c45f52736e2d27a136be58f511253bd05839475f1de8cfc552c58a2b39c494afe3b9fc8fd2ecfe33821f3ba0a134d9174717501998d3141e6e21996d5aeb795d9a796d64c82b4639582592836ece4c66212de59246463bef521c90bc3f6b8602cd153744aae528ef7921edb1b1d052649f91458d89284d19cc24e328470a667404a6a51c54d76c193143f3702c335576cc5a23093b3504ea06a7e1d04c5d29b892de3088f7e0819a1b4d74f3bdc3146128378b80ccee7837ff8ea47b0a7b0d17a459c0dbc05b90ea32a2d6ef72b3102884959afa1fc2e33f43e2458852399434b2e616aea4feac9296607794496e04830ffb2e8c305d1681c6a69507aff41933681e04c48773e8069550f892d9ee6383e52a2333a680a166c040c7d41a3ca3253c9b9fa7d8fd26dcc616ce340cc8de8ee6d9b2573496ce9a478da6e44405e715132d4fed14359fa733e90d5689f10ebee7ec5b78f36e211e1b55f416bffff44f3d545fb21b4b53b8898a9f2fd68707c4bd4e40ac37a4b6a3af03d241e546ffa0cd46feb4994bc3462b197353d55d5dcfa1ef4d51250d8ab50c454dafffda6f539e658036a599e06a1e4ec31a095414479c7d6640a674768f133f119f9e1e7107d2f6b3d77ea0d93979a199ba1bcae0d0ad61ebad48ad8d9d35b1d191100d89604cc9944fbe00274bc21c557412703c3e54315fb4407c7292879c7be9aba5322588eaa63d133d9f8eab42414bb31c49d7725b6689226f1a5cb4626d0415b7b5ff7af13bb55da3c2693a641cf4bc03071e34a27dfcf505ba18d7969b2d26076ef3ae7030f326379bdc17ad403d4ebff64af0ecc702e7a332d114e444339228e7ff37c1e5bed92aceaf7d9eda6e06db9334485c69776d47488dba75655f61d7632c9f289a59d31df8035a906742e9e29434441173115747728a217f2782e2acf1530e3812fe45cd3b7a0d1c153ab27634b124ca87630d54eb163e428340e9fc352d7e390a3483fe5ac35d31b30af88890a473aa6ae04aa70a497dcb348db37b69b952164016c832412fc85b06e274cde7470949ddfeef78ed6962ea9751b0f48bce7a3c940377610f7691b1775d8d9f01635f02052ddc151eb962ab68058bc8299e12b8696b8c189ec1214c0e5c62f70cd8d07d8aad6a08624c6caca68df5554ed8b2f84b9418847821fe4e8e31a0971d3e9d9f1a0a363da6dd51a4aca4c4255e75c07dc615736ecbb52375d395f1ce3bd3d1287f9e6f9dec193c91966de30854dd52d3922b23e60069f1cad998bcc61d447330d94838cc32fc567ef7ce226d94679ac3002866712a69cd8a4ac7efc72498205e8c4b35062dc773c0d850f39452263f224a1aaadb4886b9c69fe2fade8e729491ff9a8ea75413b3546afaec0605a48411d4586c38ab9ed10cb967aeb7f36c127b915c517d6208a1ca3b738b589018c8022e064988ea3cb0b8e1936ee850afbacf44324eb15094b1897842d72aa2a04ed94c820a18ffef21c9b2550481ffaa633a28f3ef14437be7842a56c44f13ff4515e2522848561cc29d0813e1678cf7e578633b4c74d9f657487ffac966150b5783dfffa45f064c2a403da715bddd8f29c9e5576712c339553e72b0b0cd882c6b94bf06a27e7b5441e9606366c4bf0bbc3da10ffb022a8b385a27188af9a5dd60faa1f0e2808082e5dcb89c2d2ea88ab88e79d70bd76872a171f1aa18e56ece027584d67bda7f42d4aa73568a9e71706760946bfcebc71112a160593daf373b52def9febb353cc247a54b1ccc100710e5d661cd5517fb47b80f4119b453ee52f6463dc1a1b3a9c5131f9ff82ee41551c408e3a1b5682fac638960d92b2d0abc6ca02dec3b5703392ffe3ad41a76dd3cbd093d7439b653d5a6bede0acc893dd43e7e086b9568f6c0d324ea3c04a48b2cbcf5df9a191ed8dcacc92892ba77d5366ee43be436068dbcf68a03f7aa485b70c14ccbe59877e4c5a010b30fb650a6684eec9b80e04b50e5e44a1ad25781de17d04d808d5e4f5ca78795c21a10a435a2fa5d023a5605fec32c6defee51a4260ad6f495322af1533b91e78b536d4c50a6da36df6dc3f88030fee5841e74da915dd1df53b597f530e1dae3b0029ee0333d114f1696ff4a8a29a93967319198218024658f4c228faead6b02622e22ea92a468a75e0f5d6eaff6c6ee9fd7d87a75fda4bbd2d7f5f6ae368ff52cd6e9b1362fa39b7bcfe97fd49f90438400d10015ae073b901f2c63ed6b9264bb8655f52af36a6764c698dd06d45d245d4e1713bed67079e5cb26ddb9b4a7638939faad27545efbce419bf53a651c2aa26762f4a74551512276478a6dbba976432e6d385ad5ebf56ff27eaaf62bb97161be1587753e15ea87b771c91ae4a6480381a71a14ba80b0398f61f64869ece92404cebd3a2c7bafe1679fd580362d3ac1aa0f18bfb6da5a5d0e98cece7d4eb5892a8c6547e25a1bca428cd497aec6c4d514f099c67493f6c5aa8f1e0b8a84b3205b3f80fb901ffb3e3913e9ef1eb7493bf11a8642ee7823042c516471fd49cea68c7d641dae148e3e3dbf611149e4bb8e59186b1da954c051cb6c31a2f5902130cf1893f61b62ba657f0ecd699c846d090b664b935854cfaadf6617329e62c07639cfb1ebd9aad480c2b5eec4802a26c4ff83b6a927e0d4113d440ea810efa8c6609cf2c27d888ce3a4dc497eeef59edb5c2bd4f3045f06c13ec0501b90b0daf32d9e5353bfa1ec2d5daa1118da9fa2894f06cba97a741b11f77235a3526b8fdf59855be413e949361f8a6eff637ed8b9ea4a4710613cf4429dc27314ed4f1afbc8c3439c1ce15f8ee243cc8eecb19f1cae6c9689879478ab58c353c3fb1bcf6bfc3a7dc916d442a4911367049f4f5d5becc7c4af4115db7d6f94ab4967bc2ed2b0de71840ff75329d305af096819dd742c226e1a5ae5b01118512d302284324bdd0650e7988bd0b0982bebd9a3ae19f8119ac3788d0405a0145b0cdb98316a551492244f5a7119ffb1ad802e52238872cc57f58119c4272f806952aa069057acbe6037d1facbdbcc74c7f1969a219bd397691d00e1cc2ae4b4511c988a0e64149da2a6262ba3b502757b37ad0f5dc9ab55094f844d6ee6ffbb93261d640bfeaaf335f11877ce474564e0f7b085322339c3795c7d4ee2d4b8bcb8f1663536f5d3b4d0ea53e1a47686636b61e1fd4818c804b4e9f1f7aedaa8c978086935e26c4e51284e7b65b9d08b789d2f02780c653416b3ee461db3327fcdba9f6aa3dc59cb09af179578a33f5ef6dc5695b4addb2310df0f6e1a28524f51ecd6e617ae35d2983147b2e60be50f0208c6248b068adc23682c93d7c29a620d165e9fbf22a7fa440fff14b1ab0664204bd50cfd4489d915207998d216171defa09c07231d3a8f503664cbad8f1e91de6252dcbd8fa5330c3ea35a4718ab3fff4a5077056bcb8aacf2198aa4a545991786d97bacf5a9ce0e9cfc2d775e7eda764746f4aad95cf66654c00a35bd364740deb530a9058030402ba622b1cf36372c1e49f8a27747fc4f187d772997b9123a4d09baa87cfb10a0350338fb81e43e9cce02c6de32fad0da4e266363fb57361a0b23da57df8ba3204298419feb01b643fa42ec3e530bc63e96248c21d3da7fa7e5f0e6c3110ef99d3714ee96ac2c09d76335f3dfe4dfa6e1ee51f23a432caa04daaaf3879e20061c709e87e59ffa64982249037114776f2e32b8a984ddd0156c0b8db0ba43f0c838db8cd910c58758bd3f8a5a5fbb4d5c21528a1a8be387d1b9167e1cdad5293f9c513d86d27a04af1f2a21efeca840c9c1a27991948755a0dc9dbdd5f5408c85cd71e15681d25959d13eb41c752623e71001232f399b5bff6b69a2bbd5d67e86c6c53e505f101e3553f8e9058979634412c02b40e749d561f7425e1f3aefec4f5b58535cdadc2e66e1d9da397a7df5ecc968dfa27d76f19428095a6f8afee9a5093bea1a11761b0e862f22dc36244f601990520c6f937bef5979448d4f5cb35ac1b04c195fc7095e56329fec483d75ed595aa8d6e5096e47d172d240d5e4658712dda816f74e70b7722dbfc572a205d6bfeafce3c983fb4a5906e76b8f7e75db74db385c426c23c3a43b204f40baffafe3e3e3bc9a3ffd8233aabffeec6a98a5251da45767803bcca9af03930abd02dfe44802c0e36fa45f5e663d1e1664f690995583225cfdb392d9db4295ad43bb7d06e56eb5e8df8583f3097079a62deb1cde12660bcd75575bd91cdaf87c95fc353faf3b412dfedcd40f36d88f13cffecbbe43649d9c2c798164841dcea55e37fd270cd469c46dd680e3c18093130e25e3c9b923f2ec9d441086f12c8022668d257f0c0c98a4a7c96ac2d9f0af66eec6f382573e17adcd78000ed6078fdf6568f4ca616efb278520cff83734aea9c90024a876a277dc8b55332b8326b40a38337566a7d2cef8d126cc68d5408e73d0f87ac6b27c69e334235e78753a7004f80acd1478588e94bc987b2f99604c401cfe1bd1293de1f5ba7aa3428e4d9db94d8eb7ec2fc4d5430f74ca668e6a0110ca3759e263db8bfa3c69e1c051b35271ba12c912e993872d2110f0f2d655c941f7891efb3843dc933dbc2585a5ddb9b2274907a306c3dfe63204262bb79fb9ca26d494507597e7d2303d9d7963f1401d84dba8a317606db8a986dd6a31f9e0726e15740d828a26694d0d56db418a4ff1c014462686953630fc28694d01a2de8a8fb0baf83d6efe2034d750df1df1b1c1021317b02a1dc150c4af48994c7a31f445593260e582bbc178e15ab30cfc9e0308bb2c6657eddb1118ca8e0292030cd1b8ac552ee37d44475a830b6526fae40f65d6ca88751e820465d4c799a29a9a6f6727651a5666615888f01ae97983f2af84f90f78a82f8b5cd287b2f54b5d8a75e0e20f67204fb5446b226306b6650bb44b610ba4632c7a5f755154963ebdd97ab6fc2bf992e057414d70f2a0171e48f8fbd30636f61516d1b04f27031cfca4c02867679a317da69c621831d648e1fc75792638ad51bf03dac1936091a7e893bbe49858aa84538065747a6e5eb2eec6aabc9e72b4e427b5d3e333701be6904a531004306d9e2904612d3c88a22d314d108d10c222cad897b8e59479e912a529562f50a62b10ec0e30af350eb5ff279193b1aea53a92143211f8ee3da2b628fe689b5e6476741a30f3dae90274c0a4bb6cfffde67ef1e3be92f1a45792d7811492e0f5809aef3f096a2ac9d673cb1c11526b79bf8b87035bf54ac8217717e1ade39f0209d4a55a8922a52e6a4902087e02e88093839208f7db7e6677c69d3e1476771a529cda435e3b9b96f5ceeed006a76713a2f80519646f58a91483150afce7b11acbbbeb729b6173ff7778af03c78255730b9d0bb7e76839af74e0c032b26c58a9f8d2bb4cc76232c3c22b352cedd1862ab6d05462757f0b3bbe9b0a5fc1efd3b23179145ee30f61efacd09c223503b8d8972e4b8d83273840bf856e96361ab7825f075876764564a96025a78389ea4af4076f4f7e7e861ab501a581bc09a600a63366700d8eb888c40ff36331643ccff835b3e48d36758d006455cd22799de9116f2ac3e317d23d6e2513ca5f4354eb4d0a191fc2ce31996004f8fdb5aa8d48829aa9a0ce7d4e60e811f782ce7be255f88eecf086d08bfa1e48c13b2de4763a9afb9cb1fdeb102682894f6f85028e10885857b824843e156fce2889c016fecf42681ac82a70aa5a5cac3d8e6a83ab955952d405f678b798c87cff30b662a9eef1795f7827a30d8f595e482dffa4474e0ab710b4caa1afa83c0704d9d617a7ce8f07c1ddead90b89fcf297000acc1624478e1cfca850b7736288cd403b384022edff9640c55e846228f96cfa71ee10444b997612958c26334e34dd9830b5f7321269977276b38750d1150ff3cac2109d0619576386071b966780cc552e371914146ce4cae82847232c200544449580f36c889d7e904a06dbc109e93f506268e8420d654cc8bbe4c0ba0264133159a91a2493a9aa1a25132a326e14fd8f9b466cec3034122563a65afea0144ee46c989c0df740a3926396a1cdd8dd8ddef7d70ad0f0e3458a9d398495ed4d71db6ac449fa106faeb9e632982059bb3872e805bee9d10e84da925cbde49e281bee615dd388ad8a39f31a4f0b136ae110fae0c04b30106a23e145c57d2f4e8f38ec70521cd753525ddfd5c30a8a51d972feea88b0e4ffb8598c59928675838134bc958fd5c0c76fb77d1cd7c8de897a4df81dcb6bd6799d1c9f76d3c719a7f928e9e46963426aa274eac3186aea59b31367f88aa24d216de4e7676e3859a3f074ae54dddc0606476689452ac012f057da8c985689c31d9a38faa0149aa171f28a5e8ead9986f1d76c82f3fd34b4870f5c8396563b7be4d87b0dc4bd8a80bb1f9d33e5d384575e80aaff6ba54792d434c449bb142f908f336c8f1e4783e7e26029f65d1bbfe21dfa850a421c9c45eed37f7132d40fd3082e3451bfbdff05b7109a8520ebae62566ff516b51e30ccda6019babcb7916d33d1ab7dae3f44fd59dbfe21a6b0233b1893cb2eb33ac113859adbc53385b93d85002098ac129527aafdd9018ff07fed8aa32f156adcb07ef7987baf4b56efcf1a36e9840094e6fe08e468d3787e0788de87ad8a51e1624fbe4fb8c59a936f2f60634d892ffa7bcc83fa6d7f19557a99e21660dc0c0a4f5aa25a9b69078147c7d9ae1d4da685a1802e2f5cc527605d9af646df9dfae63c9bfd411c4efd795e3885d3d626ecd059e1d5d2695d35e985198d0dbf5e675d6c33d775d7f0c5e57ed948abaab06ae26013e2077a4d4c6bb68b7be01186f99b313f1e7aa838a2591773f134f7d2b98b6e04f369a1682c2b1084051a606f57cd374bad7acb6596e05d3e0a11ccc7534dafd283e05d20cbc3f09746a44f295071fc9302c0d0083fbc0e0fac5ee7f92b532f18e5153cde6c6b5fc1ee7173e582c28384a542c4283d88633cb9ffc7c0ab6b875b561fa9b5e40549cd9e19dc77365360d9be91cbb0a330b2262099b053d45b437e58c06c937450365d8b8d11307336e604b8ba5b678281e3ac0459f41ea89890dfefadb434a3c72218fb93dae282c5da28941d78291ea8808eb367081ae8620041ddae831d9c7a66840561b1346015dbbf62cca31e5a449c311b4b91ec9ed1234c38608e3e43096036de787ced6cc73defa499ca9f8eda1d3d794f3f30643e2302498b3fcca264569130b6ef3d80ce2f767a2ae9e7e471d1082860fff5eb262bd3698fbbf06e850169d35c6dfb2f2a6e93c32af6b09765958ddd51368e784f32c728da23d2af9404d4c24a8db4c6f329d44f8871e4fe85969bbe2b9e140cf6ae2d0ac3454630c7f5a28a054a159589c1165c204e4b075dfe35cb34667303f98e5fb469ffb4de737506c08e69085a0808b7bfb4b4b8169c951cd375ba14797e10b321d1720fd333877a2d88fec84ba4d8ec1d9eee93f0db6bf5d9eb4da11266eca8e2ec1beb7b85dd5726a5cbf4763f91a98ea769534fd1935fe225b0ed395dc944a96359a5041df48084f4205803d7ce4e105c756a614a7604766fe71dcc2157db5668328bfa18237072cdec3a01c6edc0d7d50ff38f3baf48aa2a086ca463950c811446f8c494b9c81f02e341a12d617e98346207736d9d8258c5b68d7eff3fc3da7f378cb6dd92468d9398c5ca6c42db6536b22e6edd612a6e6b376dc3296e7c2a591e33dd2f86ccf037a361ba6542020554d07593303c0c5152007d02e36d14ba1c0d6fb1b38bda3c2cbc7ea641c956cb05a007c2b4860d60c9cc3d109de7bb6ce4f762c301b8bb06c16e5ca4dd790cc30686c30502f69bbadc9ddd6f696524a2965510ae909750a2b36f61472e6b464c2a8e394cd82c2a92f3a83bac18451ed604e957d44331acc31c9e8d1b447306753077fcc19540e248c7a0e5240ab324c9b414b293267c225314536bd3ab29f1fa1391316c1086dea60adcac2a017b026035c3267422531c0259bfa475465e10f0cfd11851f514873c181e10499336191229bfa8b20b42a0b715a682e6845e64c18a445919f2a43d558fcbc7e84e60c6a8928b4a99f6a2b4eb5ad428fdf923983129a30ea294e4b3675144e95a16e30675045268c3a7d514769140e4aa3706ec0bd1e39a72dad4717da9e47f325ecedcec49b1f56f1689e6e7eeff5cfe5957eeef9e591bbec1acf3866d1c6deb9689022851229f205022485122a5424a122df9c1c153358b1e2aec8f7ae9041ed8460d18ad916d9a2f96dfecab3eb2fdcb1f05a941c27b1dd18caa96318b840ce843b3482c2a009a345414864907c51a75dcd4712913d3c5288148bc8d79ce98c2061140a234808f296f07d757bdf4bd9e26939714909118d9f22dba5f560537fbd80b0c838ee6ed122db5017302859a777c3b409a3249fc14513469d3a78024bdff1646d7727a4b349aa1d1ada3f3e54a44e73e74c070509c3454ed05ad8162fd89d0b17b4641c77bf78f1e245122f6079361d966de8c62dd8380727017ef753a4e6bb1efa1a22840748b7842e0812769bd00de11bf9d6f9e0e7d5b08ce36eac612c70a1f1d84607f368fcb5d1b97834dedae8607834bedae86278349edae86478347edaa8f153beaef3f052bec00dbce41bf0652b6d4ee3a50d39b19eb03c03332eda70308ebb49dac2c8b6562be124680d0df7b231f20d1723dba0a04dbd8ba1828ce36e8b4446b64029f0c93bb6a7070a2a527fc9f6887d41c18581f9b131902aa3a971ea5887c6aa53af796b693b16888d65cb23f3e63772599e8dd2c80736f61b841306d117ce9c41d1240cff9833a81f0c84c687828668383f36cd41452183aa995043251fe5cf0c18dbba94af6dfda50c1759190d1f55d9add3f89a97f54542e9546964e3470da1a2c05ea322c545d5d3248dd2dc49eb16de595bec61121e1fb0ad774e6b3416058d8d9c3a6ac8ea3c1bed9d759854e0841936c636096944966d8ecdd9f4c3dbb5dd8894390f2c9d42d3679242a1a856decd6fc5659ea938cb65b20dde2b2a385cd2d15274b88485a22a720f8b5626a9302854821206a94225281dfe70e18f4c6d131e1b853a49e9716e93949f9884a670485824ecd9214f131f580a71c2206088530a71c220a7ccd596807aec8a7c255d5be275a31fa19f1f21d2765143504550348d2a627dbe0a4328c65a9aef8ad017f58fc8a64372f20c3aa54969738953e499dd367c00d46d4a602209449070848e2665ecde8cd053846f8eb8cd460f3d4062b1101622c21298366408df787584680cdbeb3ddcf03620295f68e9122aa37149a3af8e8cc6c75056e3a357a3695ae79b759b10a8acf7ca20dc5a29cf17e45d9d39736f306561d07c51a7711a9261530fc33065343ac4a9e18c551d9e6153b721ce0e0e2e5db73766de57653f9618b9bb4634b36672e307f6c8bbea73eb46978e36bb75a3ad9b1bdd3804db5229e279e9d4d7ceba172ed56bd0fa846f64adb5deea3a4965e77927f348bf43c3391a35ded1fcb441e3eb919ffafaa94115a802812f6d80255226913a9ae796ca3c120fbe161fa99b9bed91329c6f8eb7d6267d1454a4b5566bdd942d5ad8d96317844d695414d2f1c896d6e6b40988ae601cc1cece955ab86e459e6d1b10be51ed60defc70c52f6c582c2ccf6ae7120326f3d0683e3d3d305c6aa579cf060cbb61e4d98cf122eb762a4e7de1549d0a8445868d01e3cdefe28d441a797dc2d77917e3cd979be64936ee0e8de3dfa9e96a9e0eedd05aada852222a12552f95b2572f57ae7623279136d7d8efed5ebcf9d98dad0c188fe647db4bcca3798e731899043d973c831ecb373219434bd5697eb321692cbbf50cea589e95b4a522cde38af1c8ca68a8108e203a7cd3471ab4a9a57929615c4dca33abdf93c5783606df8869feda2071b4ea1d1a977987f6d4283d52698d26318d63fd83c922e4a3b1310cda412e1f7cd49529508abbddf72fc6f3728d47f3f6bdd68c9c4ba38de495078fe6ab8df94490006763cbd9d96992de7af08d486c3e1a491310cdf38a4ca92b99642bdbd4bdf160e593f80b1dd6a58ce3b7d52620da973253ebf5e9c226470e7677b7bd7bd679ad9d396e3c1cff1e0b715cd44d4c874c3976b3747dbb373ee0604be768bcc647558c4bed44dc830ed33a5a95a954df5761ec186d9a6f7cdfab2977036ebbfdb68ff7a5a9e9689dd3cec77b77dd35bf817c038e9e5e11e49bbb473f77cd4b1b348f978c70112eb25b6ed6074ffa05aa2cbcc11e49194cf363e4b9f4181a21aaacc669f8286b4541a249868bea8b8641793eb7f1efd4f8a534b41d0a849a00e65e4a02983cdbbc8b7930197b6ca33845455b8de18daac9320ebb834f96b1c7dcdd3d27c539f5107bfacdb7e7a6636232f61976f3cc90f16664ec327588a8569391c9d867b20ddd16175d9d10e7abb4f974d7fc04f2bc23473a1f8cbe1342c58ea71bd2435fd47f36f5eab3a96f5727c4d11943136cea333479869d26dbd0f97377bf43c3b7dfa971120df76a7c54e36110bd3a13463d0cdab5e6278d9726e886d0174fd711a15767ce743c90b0aec8e63a1fd0749b0ebe8ec68669d090a4d27519796675d4fa03101e5765342e5dd2fce499b48b2ff621880f79eae0f0eed72fa2625ade6347cb3199354723fc567272ca983cdb6855e6c917465922e4d9d4df0131f704bbcbe1e1ef84743f74341c50ff61ce78a5977b8f9d9bfcfbd1db9f87353a7e60e72376ce7afcb61c7cddc851ce63e427f750d955f9c19e6f7432f21a239a7c33f2937734f28df52b8229c5ec34f2cddda0d3c83a6611a39ced1135f9c66ed03bca43bea1387ba447ee83db28a724d8e0942dd979c8b3d11b41f788e479d6347906ea1af5fb70977e6ed3f3a0e1953c1fd9cd71b486eb6a689cf330cf382d3f974df39d908e072fa4853c3426b34d77c4c8473abc9f3ff2c4c14555d6d154db7191380ab15d52ecbc03800744be24ef7247fb5c0bd823c724acb1a5e20e8dee80b8baa3cd24341079e600e0faf521cf00e0752ebbda50f648cc73864d7dec306d246750b509a3dd8e05ecc842049182186dcb494f4a113bfe01761aca1776eaa81aaab6e93b2000f9e6b7e5044000010c60007936b76d7777c41eb9143b2df19201cc1f0690737858e800618dca687ca4c71e5e4d282888e66f109a182a528f0912aa6ddd7643dfc83a641784d0052160b78ef50d8f00b90615e9c5b42ac345f445394cf3e10f03e050918639318fca425a7d511fd118a9b31fe6509106f9ac8f21cea63821adcaae8e4e4813c253d5a007c833ebf7c31f54a4f4eab83e14fab01606b9a0220dda2201429cf0479863f16c9675b0e88210bab02deb6673eb9b7ed180aa00d9c51a20c50059f5443d407880fc7df37768fcd278605ba71122df8cb69d7b3e5eb2e1e0221c7cd41190b92e01a4119700afb439be40e499d5b6dbe199c052f6c00e248d3870c3e0056da5534609df6b533832749a41062a165d10821754b5ce5a6bad93ba1768ad9bcb5aab1e91382249f559379edb943e5aa25a6b155d9ab80c4f2eab8ce5295ee2dc73d673283d62b8941eb9aac157ff6389ec798d5a7d7b49c5c982392367121b45330d7c8302ca73134fa93cc539e89c9bbce39e73d37fd339ce5524079d7b92773ffad89d8e8ee503129b033980c42ee90eb436a759d979949cf35cd21c57d2a30fd041effc9afecb6e32a235f30ed0395dd2ac4efb80c4266916c97994bc7352c93b2f71268ef38f7bcef38f9d4bcf4b9e73fe4d9237824ed2a0cf076aaf9ce6e17949f35039d7c31b41ddc3dbdea7c71ede56a9383d9a7cde7da07f9dc941ffc0cec4a15028d4f77ddff76d503081a077a0e9471f289e4b8ef2f2fb3c37cd1bd0373d92c0cf3e20914b5d2d390f93775e3279e726efb49f3eaf51c1a7fb0359a0f3f8dce4ac9bcf4d6e2a69cf7d7827b4e4828e426147a1091985193303c6eb37c618bb94507802added539eb46e2ce0a8511b7d1cdb7cd47daabfed1a5b7da6aadb5d68eac73d6376badbd761bc24ff52ca9e42e5e62281547d896de22676eb82c89b339ac2dd9d24fd9d2f9a45e83e6964e4b2a8ec41ba2c8e91c4150910037320aab4729a4c39503031971dc26ba188d46238fb12e2946a3d1e436acada66d00c224fc852eaa9c85ec13278cbac7c136fc42452ac68873a645c2a8ffa860a20bd9427d34ab9139735dd209abfade698f102bd571f7757b2d9d4ed45509b6744b471c4dd993d35285f7c85d3e6754248cfae7c2b55429b2857afd21bddd7dae50856bc2a8db3af1ee70471a3dde2317756984a6856ca1558bb285eae0234d9b5343cefbeeb6f6ad1447f608d24b298eb4f42c6755d049d7275d44e92e2f3134356ee4983358969e93f955351797fb805a67ad9392b8bbbde872d55a81ac30375bf26c3797e862ce946a9ee2c47936e7cf3dba97fac8a7084625d2748103a7ce393b6ab96069e49674c3d7eee09b3ac7e67c556e7cad376d954147b3b7d2cf4e7cb5786b95913223b74ee74cc9eb7c44d956b6a98392fbb97108f266f2ce4a4a29e9e9b63466e26a9e8dbc9b399cb5ac23782a5b36aeda3c1bc9effba8846dcebd95e2e6f939e949716bd9fcab3250be36df36bff9c6a79cb534724ad277eb309df8622cbfc421c16d17dfe9d2a5b90975b2e59345bb336de974cee015d7d958cdc9a25a290960f7866e3d671ba84871add5efe394209b6f7aec218bb62d703ece209b3ac5da8bb05bcdb35ba48333d28318ecaa015964b22e5d2309c8cb79ddce25cc59f5c887f6c9e99dc5f5d66aab26015962d3c2a6850d68034282a4db7ec43d1bbb65c377fde6990d3a9db67bc1ea5d376987bd11fed55e75281d8732792a6d344ad9d551f8551bc5914c4a414ab5d64224f41502f9e09ba6ba6561c3c2760528beaf36e104140882a512f87352a327b510a8a4b34f684f80de668593b75501e5d5264c30cacaf315cece71df9749240a1402017f50210ae5cbb30d9fa43c6ce261b0dba2703242d950308a09c62b1414c628d6c6c22c95479360c79b579f4455e528ec276ea2d229ec27284f7de8a93c0b57e60d46a5a0a4a4641d756fe116a6e4196a1ef9a67f295fe559e81dc618a3505aba4035f1a1f028a636159cdaf009c94d2413c94432914c2493cc38b03ea145e1c327261885531f22d93e44e910680a1dcbab4ef846ceca36291b24aab295d75d1d7fddd5f3c01faf8615e833c9385a3b0ca202902acf9aca8a879fae4cf730083a07967e70e9e7878a53f8a6772c7f6292fa10f58f7d73bb39de50db09fc4ca50cfecca2f00b21100845dd630834a36e27121a0b9a42185b7bed0991095d9983272995d139e130293dc19e3e854f524a69b534565930299582b7379d92b2690a158d7ca38bdda2859491d593798a139f673d8b2fbe2e4d98156b8e6e75fefcd8705676adb5d65ab7287cd5efdcae7542275ddd8693426397fba4b54e84a17bbad3db566ba84ad936bad17b1d6b4f1b719ff8aafeb13a565e9f3c6cbdb6de2dfbc159dff0e6491470dedd71176397f60e5e1754d7690cfb28dd6a4ebe27b58a8a150dce6d6badb5dcdd36bf9b96f525e9157eb40004a404a5cd4006f3270613080ca47f52f84c73daa2a95a9db67429499b4a6badb5d65a597fb823402309f6a53ef6d5b6085b2b51372d476bc5b37e95932d555b22e61da6a45f087166d7b14ac99744fa02a8ecea50f9124353e386fc5c41c830ade2a0a1286dce39499dd7795d6ec137a26a1bf583aa89df7ac237770b88b95bd79b1f8bcf6fe572792ac541143f5951b93b8bf575e0835efa53cb41d055783ada1cbb219f836f72d48f436c14a8e304b242fff4c8da9172f00bf54daaf4b9dc43d4a8a81f519ee24f265e0a777c0eee30396afc342bd4372614e8253f816ef223ca5fe5269583fae42cde6d3303606ed0a4fadca4419fefd3cee232ef5039a879a8dca479a838e83eec06b50fbb57fca4e2df72506505d4a3b358255f954e4e4e4a7e02fec9e727d4898325fffc8472e2ab13074bfea1f4b83a69815e02534e748aaf521cfc147d3ae9b15bfdc9e7b7f2d3ca4fae023c812a1c7c42e873875ed2263e4bdef252aaf43c52ee6ab9cb5d2e97a74a3f0eb14b268d42e991dba793d3bca4af9389977ef461b7cb53f81786ee72971e531efe98f2d0bbf047ba3fef6acd3b6af8a6ef43f9e969be29394ad7a8dfc95778f83c52ae4d1ede94f44745139349f34879cb6bd093f661774abbfcbd4655f1df065f6ed7cbbde247dfef297e1c62bf7b79b662d1f22362bbc31dee40b47dd4dd729b6f52bbf5bd6f79263aaa861a6a790d2abed65efaf1db250f73b483a01e4bfede7d135fab15a4bfad1f891d06ed9f27fd2ee9ec7f46790682a81a8dc92dc409df101a1b51b44d0b8580a4ac83730bfe041e4077686a5e1814d6688ce3dca33194737af461471fd68a7ca391d7a0f4d4c3c78e7681a17c72ce51f4541a791038b223edc3eae0e10e77b8c3e65837251f7949fbd046d86c3797870869de87426c58f330880541b49e1f1ac31e5a223e5d111ac33a1412d2a848b7267c6347a3d1182784c6381e1a1b42634cf83ae7389adfa45379404447867e6a3e413d403c3556c4bc3a3ad8349773892e5056acd2e7342e5c5e68ece4d2636adcc831011ae3fc0605117d4212c7892d47195be6fc40657768cb198c0343cee01f1346714e7d51d0679ed115d4701a3cc8d387529c5ef32af99aa0844d9c897fb0697c54d9353ea6685c1a5ff3343f67def17bdec829ed2cc1c7f3f17c3ef4459d7e3e9fcfb70509a3fe6de1eb828451ffbaf07d41419673892e7248198ff31e52e6e4d887a0b19263f74163a0639f40a4cce7d8a712a1639f44a40ccab14f1f1a4b393671ec3348caac1cab1ca338f6b944cab01c3bc5a13115c74e7fd0d88a63a746a4cc8963a75290323067c6477a05191f29928d9dd2a80c1761da83690e9581b41998ead441ce8045c6798728a80cfc91f93893c47c9c4ac020674021181fa7187ea80cacbdf8388fc8f071022541ce804b62f83895b0f1c9c5c72964c3b0cf18c8995390173ece196cec1fa0b2538b0bec199033a7222d7c946ed827163e4aa117f61b3973129a30d147f9c4287ff60a77a1b253adbe52f8189d8e7e0a38cb57c05ffc04dce525e0b08780c3f808f8cc3fc05b18f0d85fc07b3c08fce52de032de01336c76c69d1e13185a036c63e002160041057e45815f4d40021180c0039e85e38067fdc830b25c82d9708fa73c3b94565f34ea9333ede929c5d8fe05de3c17381af054c2b03f8b7e4f8ad85d3e94227618af922276d8a74811fbccbb6cc19f1471cacdef14efb2e72f77b77d8bfda57c79a6e25d8985e761d36be7bc7d2f88c45d2a71a00743e3437c077c03efc339d0c3cc655ec579bc0756a8a410992a8d89884849d16a094e7db17280e8d0eaa682ff1b97f13446f4e0aebf31c417e1369fe229befa8c577199c7331fbebd8aea3ef3d6ca65d1a59b9ac9c403e4239140f0fbbeeffb4e43ac24a8590d1e4a3dac86546a4a29e3e8f66a08e753743fa278524fba9f7ad2fdd4936e3594f2b66789c1ecc005066c46c689cbc55579f6c2bbf9cdb8cc3398772aaff540ac7c00baedf5d63f97144dfa838a383332282c099ac37aa15743402810caca54e7099d949457d2729cfaaa0d11b58aa8e802033623a3d5235b50f0a54c22225fa9641c75ce21a5f5da2ff48a280e9fa4a28aa71f01cef209f0172f84bbfc011cf60570186f009f7902b800dcf50cf0d82fc07bfc07fce515e0327e007e5f0ac1806ff52ce05b34057c20010810e200062800019e85338067fdf881492000cfb315ef5e4e5aba0ef32b7bfb95bda24724762bcfb8136f7b154fd9f7e5ff6ac3f89d221ca6e7915a4411aef3cefca9af9f1904348766ad5643d97767d4d36767f6b8489e6ae3cd95e26d6f92e2aaa84815a58a686c7420077279325ec55fbec6f6d8db70d7dfb0f91d9ee27df019ff83cbfc8dcf7c0f0ee3691cf63377791afe8207d75fe33cbe034ec41be1dba3526e7c185ac306de03eee3a70d0efcdc99b3e6819f3c9346452a272de588f1950f807f10eee3a50d2e0767c43b10dfe1c30f373a7a768d212545ab2538ac1f19c7b655f0492acaf81efce5b70d8ffd0d777d75d9e7709bf7e1c6653ec5677ee6309e86c39ec65dbec65f3c7696afe1fa79f0ed4f5765680dde5ab91c6b35b4225a15e1e4641c77fb0fbff9d6925611518e6f0dddf816cdc6b77448de5ab9a1de450c25a122510d0d9a590f5f5d5aaf00b5cb666111bdff1d3ee870d9ff7093e3860dbf3b7ed0e1c34d8e1b36fcdb97a5c2f01679e6e22db28ddd2fbc932dbcfb31bebd4966680d45f8f87068d6b6ad6c2b57d2137c33bea5a445a4b38621162216a2971efe222545b661e1fdffb0bdc77bc061fec6633f73d7fbe0b2d7e143bc0fdfc017e11cf8d16d5ec5537c8acf78ec325f3dc6f7e0dc03c05b0f048ce7c1615fe32e5fc35f3ce73cde0827e23be0db9736e737c5758ae890e3860dbf0ec44214b4d2f2951f8484c4200629235b351f6e5c2787769d1bda756c68d7717db5eb6cda75b607586af05053b464d6c3ddb82d68af5a2b4956442b2546fcaaa803cfc2f1f1ac9c229e05647c964e8adae2cd4fc65bf28cf3966c63778cbc7d4c15619815d90ad046f13155b45ac34a0d12469d93f995925f15ad783c0b8795c302c2e2c0b368f51543af8652f07933beb5c4e6fdc7b786640bd52da2faa232ba556404b75368c7a1a24ae53ab285bee841c1b77db76f2bf62d5a984a91c2300cc3d08448c909cbaf869228295ac2c261fd80c1b0f0b61f6dfd477ca068fec0993833899cb33df7ef630817fa172004cb4fe00154a43da8483d06de30bec7af92d017ede1325ef46ac8a5c7d7d889a9052123e7d051370e1c1bf6ed5d7607c3fbaf2ee369fce56bbcc717e1305fc363bfddf5d7656fc353bc8acff81b2ef33df8ccd3f0173f7396c7dbdf1de377c6d139efa1880ebe03d7195a030fdb4caf868c7ca3abb6a9bb6a3436ba84b6d08d1ebe55b34187a25dc377e03a6b18e241b310b110adb0107dbaa688d5d02ac95549e136fcaa303079f6deb164dfaac916eadb13db11d7b760be0544b69040f0db80be0de8db80be0de8db804e3e7242583d9e9584be502f5c7e9504f62b2554a43ef3ab2219bf5a22633136965f258199f9152ef32d3ee35df8fd18977d0b8f3d0b87f997f7f80cf8cb8b2ee355f8f675c7f81d0d38cbef8c0ee3777a388f7722de8778dfc07b4b03ce69c0f5e82e3c1cd6c3fd8de0f60bbdc3c357b62334e6aab9d020c50da8ca58886020652e37c8574e95b95c45dc0e64cc450729529828baab54dfb7018da922217b74a01f0d7c2ba8b66a2ebe15c5b7805c6870d55a39af6fe1b010b9dce01a72d1c173eaae22cfa94b098389a2bb4af57d928568d3ec63aa6853179f154445ea197896107dd121a03db44ad2c2a55b3a295ab870fdca9696db7a3c37c32c78b0709d2a5b71dcf7e9e080ed15d1f68558f6ede9be32d88d61f95512fd2ba219af57432b991617312d5ee20a15295c2c315a157362928a095131312ea5cdfde56ebd5c79ac92e2d5379cc2fa910564535ffdc81a593c634b0dac1cd692160f5a46d8d4513fb270469692560c762b082d33ac8858518cad128cad158c2d1b8c2d2d582030141408c96c23fd4833389182cdabd600e9676fb4ae885643ab21233f293e242e40270fd8b089a13a617ba2065404b009897b2a0df7541aeea9b4114e13db18a4ac12ff0055edd1b09ddf275150a54b224bbb44db139f6b1c5d6ba59452dada44b5736e4ecc89691c1115690d5269b2ea50710557bc768439fc287b9fd837d929d348c4b69b7cdd8931c618638c31f7d206c618638c31c620c0b265c2dc6db5e95639aeb6cc1cc7a036b189c10b5e084318308e134e98a156869311ca8682514c305ea16cac8d85592a8c371432b480b0096d423408e2f9d8961b71d756df36af731e24e7bc2339e724adad5f7d012448935ac167c8676877b3542a31e173f26122753af930914ab9cca7547d13973915fa8427b673d9272ffb2464220c8930e13314a698483da7bb373121c284894f1f139321132652a99474cd0b853d528dd09fb94f45f6c927113d7dc6131ff9c9c44f3e5e9f3dbd7e273d7e9e7df44c27ffaacb3ec5ec93f6939774caf38f293fd16cf2ec2810cc7a74d9e389f6414925b71bd43c3e7044e9d1654f13ed835eed83eecf497ab4774f9f4ccc1993e6f13978f3691e25bf1b1ca5b40fbac3af343fed9389093b717efaca99bcfb465d6784dd3c4aeef77b93cf0fe5df9b4e2794d7a09f9bbcb31ec94b25f7debb4eda51f949e572e4e0ab9ea3af118eeb2352e9472e49ae1abd75bbb565c267d7a026d4e482aff3f6b649bf1cb74352f1e2d1cf7aafa5afeb32ef18625f1f628fd27dd88f0681e3dc3bb9a43527ada2af4ae2388ee348b204b64ba1316b33ca53e0364e9edda3b19267fb292abb44f6d32fc179c739b8815a7e5a3ed8f3712e77f69c6739df70ba6649b1cb5295d5ce3aaf590717a670da92f28ca4e5c7b9f6a13d8bedb61ca725be790747c559a37a1fbd452934e6c3eeedc3ce24769139e71c67478fd035ea0edb89273fb9cc254d44dd270f5d66d04ba56e9c36efe876694e2f957c26b1e5d92ce91189399ba727e9f1439733270c7d864e6791f0f4e3dc279f79d69da69e1efe1cff7493efa44cae6b50699dcb36d6b1efa4dc9472d473a94ac2976683541daac4dc3367cec83df54c629c49987c8e9cc67ee7e7270b82ee4df7f6d4e3ece1edcf3d3d5a9a6d78746ef7a779a03e5f814cf84e3e9ef468efc6a036398fceeffe4c48e8c9820923f9fcda618cb106bccd03bb759a6d24e63c159cd7a035a8ff8a0358b068c0bb4ad553652c1c164e0f0ba78785d3b3a9c3641cb0cdcad9bee0029345b125f68293b9b49e9513c3c261e1cc6871219369d989494a16a264b2cd62c04b1bdc0a0545f5238bc7e7c74bc0a2b596d0aa024b070a2d39e4d8800746504310922021560e0b878503450a9a02e8d4c142d7992a010850c281b3e7a5afc495728e6b049bd12d8d48d88cbce434df8cbc54848d173aca1310ea04501bdc95701ef9cc38889877b0917be4b473cee79c73ceb9c3647fde514a29a5b5d65a2da763adbdf75e8cf1b68df407943fa0ec1d293b989dcb3627074fa006330770aceccf418da3cb2eb3cdcafef27776c33678977a36f57c1bd9649bce3939f29973ce59b3cdb6bda1ea3344a9f5389da15aebcd36d76e47acbdf36ed9a6e211f310c1794846d986e46d59c7b6b7cd59dccee368936fb2a65eef48cae1f488dd066dab2d154300b47bdc1e908642b83db309283ce1f6dc1a546d90e847b9eb43ffbfeecf79156ab1a44c7e85e5ab0f1457a8504961d1a20655a18a044911ae0afd3b0151dc47f82eefa627bb92d7842cf4b0cdda71e1d8458ad72bc5cb3b17ff63892ac4855317b46e78e5e77e7dedd9a93c3cf587e6eae3535756a635a1290e73b4c5027a0a7413d057406781be02fd047414a89222087e5e3fb95b6cdb023fd06d9e9dbcbb24164a00017271f74a2227076f9e587f541c9c2d7ba8d7befd8a234041b5287c7e8ca438cc712a5229294f955071b45534252252829333aab54f8193034427c85b9ae5a92f52ad9958ba8fe55e9eadcc569a42e1d2e40445ae84582a6f250a5aa349723ead7cadd5286a925a3444a4a4281bf9f9e1ea1c50def6ad22d38aae4335eea5498b772dff63095a648583d487faac004fa90da354402ee5b472e14253203b3487d228aa169aa0c2bc338b5229146f7bd554328988b82d6562923ad9a4f0a9503bc5f6f814a1a2112a52a0a05a7d11f9390285cacfdde26710507dcda15a4d45c5f6d89e5ab33d36c70e28a6d356f116e8487dfd5422358a24443df7a4084a2d5aa5c814160b84e2c891154b45aaa4a47c467e7e58292b7245454562ee7de56d0f112ab6ee76b91495966cf5acace4992552c4a7c726296df12d117d515fbd2dc20a55f627b564098d8d960edb12a13875aba4888f8fca8a4bb73dad569e5922d6c7f6b0bce79d7adb6389d822f6878ad66712e9e93959a9351693afb53ac4fa9aa42ab138f54544455a97d4a225ae6e9bfc1c3279784653092040820879e2723ba555b098b0fca022cee4de7d7bbc515eaebe4681fa9d1cb10ea974adfde07a42889d202e505fd45df90154cc3aea6e61579e6d2952d89ebb5975e8c439146d7de48aae359b6453b749686cb44bec12294384c64e9cbaeda1b19553b73e27ab9b627e26ae22cf52189b60d41bc1d1c4c4702aae46a1c2e4775ebef23b295c7cdbf3d6e7ed4f7dd923160a2ab27e6785b242b4b142a57754f8dd59e1dd4bf41519c768a7b4145788a96f8655bcd79aeda9b51575c8456441c516f3c5997c8da20ea5e0f01261b016ecc424050b513018aabaddc0cff4a33d627b6c50370a4212243429f5f036aa43b546aa352848f5c81d21c142059085ae33cdafd2f9868f1377f52e66631cb8669b4d8f315de7ddf4e8571bb96da3d966a3185f6bedad98d26d44da36ce858ce2caa1cd49d962c71cbb47cca6ee4d324c9a0ce26edc9cc3573921b2ddbcb4798e3dc55d7774af47c57bc5d1a8eb669ea9549b839f77e0e69f3695dcdb4adbb661935793965ed29b1dd97cd359eca3f8424517acef6839df82208010a4caf975ceafd680c8719aa692eeedbc1987af7ab99e4e359736979bd5a429c822ce480f64208bf0ac41db9664769eac0a651c35a8e557fee2afc31ebbf89cd75a4d83146d20633496c4d491322fafd5faec152abcba0b1af397159757af21655ab656e7aa2ce6d53f1a52158dbd57df726685d713afddc9d71a92efc4c75a83747df91f4ba488fd8f143761b90f6eaf9c54a3a0310b052952239bba3d926403b9ffc2b91737ef2af7a277d65bbddc5d11b6ca0b8c53ef60fec71275088c9f2a0f0fcc09f51b7ef9eab3537beaab1ec9f5878a2816b879353544d8aef25548a5d1303ee5d946bd253637448a27cef2a0144f5c7f4a8a27fe8f22c593917c9db8144f54a478e2f424cc37bf4dbc230d61a758dc89c989a7f20cc53b134f084174744c4e7210940002e44455e249222767536fa9ec60f27682e85091ea58fd51715a2830bfffc257dfe2dbff9ff8cabb70158fe2fe2c9ce555aedfc455be85b3befa96e2775edcc5235bb83b844dfd0641c2e875a1572d5a3c5d428b8efc35727fa848ab97c7e4ef90db43944a5d9a6cf97ee02baa6ff64818d2fef2d49719240ce8d6aed00dba505c34ec11ebd23a4fc83ef940e5aaf88a4cad581748888a415ae2286a2a292bac2443434a32256a79f3736fe5990aef523c9097a7a5425f1a14971e39f2d158fa9aecc08787768b5c1e1aff8386c77440e329bf13d3dfd1fc8f25a81116faf3c3e21a6d333339986191910922e38243e86dcff23b31c72a1ae6c555501e1a15632a526cb0639ae6acc83b1407475461a5882d6fc68c1acc60e15e4c8c6f1fd3e2071fcb53a26bf1e61bbe93772bdef62a2f597ee7c55b8f75b0121808fe81718834ced12b300e36756c0309a36e8f5414e82f3f6b33a8be8226517d0dd586582c95ca5d145d5c5a8ed861bee1fb64b053fc4e4ce545df222293e9f2fc15d24345aa42e5a37d7d96c04ade4d1812868bcbc700091010ae5b85b964ef660c1903060c0d048a23475c60c8903262e4d9554245ba6d73fbae13f78b30d8b12a6536c718df8ddbfeaf90cb4345ad2f0d0e306274decd17f92243061ef27f975c221598c8a5d522459ad0841e3c041391329c919f9f7bef8d71af0c1809735f5ef2ec5ed1db1eff25ba4a6ed15d42c522d307634af3beb43adc1e22317d7968a3118ed17e7d7d966c9308ccece959b18219244685d068436279f2f0b8c0395309aa8223a8e8312a31ac1816d9abe09d179f419b411fb02344ec02b5aca3ee177ee11979b6c9c85c22ee45df8a8054d91542e5a5e1219b3a1e4263232eb28b481979955c221a6339f55ba4f23fe4387265f0cc4c9e61d79ac5659ee94de56fb0748d942b6406d35ca2ed57bcdc2a7ee7c553fc0e8cbb7e27e62a7f95601cfce3718ebf3807e3add88e0c7d8bea8bea4b4445990c9c442cb6125b8111ebab7669b5de7a55b07d79b820e363b20b155d62c430410c1a3060f8f6306a261c2ecdc545e52f76f1154b5f1e1e58fad2e0f0cd1a302a5fbddc92c6f48884ca24f5e32572a32802ebe0203827a594bd6ebc4429bb0ad5201aab1be5b649c348b00114a549bb1b47bab7bb1549972b3621f1822a600d9d748d297036a592b6c9396d1bbeba512436140a2ad220de1d886f39009ef300fc8ac7fc758a8407073a1cf891b20df848d9103d5246048f94f1102263363195193d8cb220020004006252686019c020d00d601297d7985718afa012197bf1ea530929ebe1350564731c5e9dca99063c8b8ecdbc7aa84a71199bf1ea2e1993e1153b05a2224592f3610a0545327e0cffd1f214b88d25389fbd701fdc76efe165543e85fbe0f6a5413c9b3aa84363db112ae280fa0654cb20651c0c2497535f49688c0b02ba41c6b81d489106a1479818003d8a0e841e5d05003daa62f4f881344e08a8a4fb01b2a9cbdebcebaae71f84cfdb6ddef502a26fa8746bd84bab182a452300000000008315000020100c860483e1689e269a9adb0314801279a85e624a98c8c328c6519052481963000000000022000223a31900e02c1b44e43c52cad13353ce618f0e8fee067f89a8863363832aaf9b57dd7be8ed6e104645838e982b9d372179127bafd169b176811bde0de2f39a39d7d9475adb8fde2f97381ff2eae5046c2c5f6f30e9bb9ce477fc30327f97b428be5e89fc739169cff216d59b5aa43af80d359779947ea2428599c3c4870d137566e52dbc19f0c823455cd5083c93a69f05e492b80d7d5df62670a4cf63e13609ea55253e21bd8724bc1cf369f197ab21e1089bc7594986d1e6dfffdcd9324b2d18a3d2e6682d59ad271637c4da0b12e28b8e5d02c2dc1dc9f5c30080c8913c9d950296a94e52bcd09e1b9011d41c65d58335b5f3b859ab4e7c5fd0910488a303de47869c2179c9d54d36afe0c611571cb147c9408334c8c8a21753fb1324c1f34deedb7be74aa1e2b3e000fa4c07e2138e224bdd7a8b031cd053c59e63395b9695165a56d24c1c7a1274eb7ebc590fabd42d0e3e713d4c0e235a43f4af119a9a7370e140de5f5dd6c32161092198d77d0e5500c0e0464a81db7dacb757ea09d6610e5e3e8083ea5334b314f3fed100e3e1c70e7629982135972a7c20c9d31acc81bacb5c9fa457050d462febf88e3b6044171d52489a4878ff7faa4510ab06b0201a44cbbb2698392da8849d6aa41f53129df8b1bac0a2e484a1054c97a241eca3a233e0b1880ec818c8a95fcfba53a709561c4001a7f581bcf00e5199285a222c9c44785ab63571ce64c9b32fa38e44f2e6f31beda919901c966257d98f26108e5865e2ae446d6c04eee1cf58a528fe719500d6a6f31ea8b98baa77e06b723f70109a7fd98294bc20773c57b703949038bf2b6411d6c35b32120c0c4ae3178dfb0dd15c0744c401f7915198d4ac84db0cbbe61f908973cb6e186062db0d39a4512a34fd682c8c0f2617955c03b32cf63ed39657197e764553e9f84f9704425b43ff2bf21cab48d651f73b3061af86957f7476b20331de92ad17d4f1a5b50499a0f0b99426238a1c07f48134227565c27857b0587606db12bf1d2a8fa135616fee545537ae671b8e658d515c0b994e470937c1bfd133a4c4847e68bdffb46363c43640e6b6b44da351504fcd99d8c191e880b8ebf7ce20e2d686bda464395e67981d7b8086b9a2765a135cb48697341d65a08815e71cc55b911d35046ab236c0bc7d6d6209b25f0f0768db3ba5ccdc48688b1812c5a3286167305285ea0b7e33e2e563622e5f9b627d69e304fccab2aceff3118a47714183bafd8cb54965b206749a958cea15adae442ed9af373337a9003c90ee81c8cdb7aca75b50d750a4a6b796e7d61ded02785adeb6a0f96303aa3030b8ba183638df357865768553509a107e6b433685c8b36b5f4fd158ceb0bec6835134e93ddd609c09b3ff107d1f7a101ca5f8feec41ef6c5fc519d1549e9a06839f5cef6b601fcb636b5c0f7fd75aa8262c225ff92600cf5e86a1e731251daf4ac54d4d755ae25e8f632b26639e6ca995a08d76829b903597a3c5a591e851ec4781be86ae56a1b199b6d89b04130c896aee556989c2b8adc50c736a29be43c969efe16c18818e7e7d4bff4a177994b47c7bbcb61967add2764fbb8cfa7ecc9cb2696068251991b4cccc156ce1899d203ce9ad00dd56ec03a7dc070fd54a6caa781417623a2fb6e6015b0f5f0bf56d089ed1e03a41a016126ab1ceeeab8cec0591ed09cdd43eca05a78e3ba67e026e9d1c3c7675f460f7ac7e0b7efafbce06a185fd2d4bee64e8b174e2f22fd126e5d9cde82cc863a36b0604cafc1d82c443804dc396d4fdc5dce144ca1fe18a87c0db851122508be296ceeefc421185cdee89fb8413012d08d61cac82307e87403cf24aad87c52b748dfb538e2d771ce505562e8a47f1ca85bccd28df9cbe4b8ba0bebc6d2e541f2ee4bdd2fa8f2df836a1500912b97a4cc4f452f75f281e6ece7474ea134859069a01bf56ce3519e8d4a6c0a0e43654d964b8447e56f3bcbfba494541721112a27a6e226bd1ebd00b20f40713947e3dd020aa23727f5933766f23be007b281b418e9cb17b3bcf445de0fdb71244228eed98fef96ec3dba1e6bdcf1e0fab0ee56e2ec3213b2d8181660ddf6b6adefa61d87b24702f66a4861548a2b260655ac22f7bbca037600f9bb0393e901d4abeb95854761a0f3d940e7c88623f52bced2a536b729b3edaf7402afbae9687601078020ea4f7325ada6e967480caba0b231e9473353e610a407f0a2d863adbeb533538bbc7008d36ceb0add1b6efdcc63bec3cd9e25edaa6c18a7068e3b1b29b699fdd967046cdbe9342f3c3aa6d1a5e94aa4128ac2c5e87f9383aa7623e4517c82e51211506e42c7314b1e82d6acca3ae067814b4d71c2982fcfb0165d04de34850aa3ac7777e67ddfd5d297b9d4d706ad4132c252bbe46e1234e96d8b5c0563335275992fd358c02c8bffab879fef51c4b11e2d512d405d00878144a7154a414f95e4ceee6235c09a7b78f4e6faa968495840478b0a560028c2df10c8693070f8d3dc40468e887c4ecab469e0349959e47a1a3e0b0c6eca0967f4039654d0d252e548c382ed88fd6936f5bf93f0170c6b2dfbe1244378e1d18694383f9afcf622736c58f11c77457d75053984f8125350274c769dd421a2d25d032f818898a29081470e180077b1997cca697a060b289d46055dc72e4cad6d22f6a80e5091785cee41cee22b01780bfca82477b0fede27d40f51c914164aa971def71f69c1d4800db592fd27cb954243eec63e64b75f5e0fbcd96d8ccb6d16a6e9944b45504da0e7c20a1cb280c9cb914f87464252198fa80ec1c8183149ae07cb10a4d23d8e587dce8575444c5a79ae3944437428f904d2739e8eb2c08975d365c0fd13638ef875d1136142b1d2e8f49e82808e10562bd8ec639448b3546fd5c1fddbd216355a87c89f4cf89d49279c823dd10c809d1a29d52230510be9110f0d27cda570b4bb750cad784379cc20bd8abee3c27935aa17bdf1f370dad1afb01392cb8616e70475b98a9046d72cd914c99af35c4f2146c4ce7e787da47ab17c3416eb60d41a1b9b838f54257aeca36dd3909d6f9343fc6217eb3181c5e1b78ab51d8870d5d0f215ba1107748b5c90a1f6922d04440f4c960775f1e4d04a5f7fdc0fdfa942a4bfd1855500fa42b0b744a8acd647516ae1b44f0d8b12c947ee586bb011c05d2f63285e92734529e8671902e6b52281044763427d8788e66cfc759495a7736c2a918c79ddac3acca664693c651a05d915c6a38b8e175b0e4d7b34b29e6b32d5bbc875b553d7ba59491c0c227070fd06227211eafcf32a15804564f57404049670aad79f513a6f14c2130d15e301a2e6cac6030d7d17140363e6b04be601fcfd7ab4b94407cef735959bd3a4f39067058b02f61bd195994990f5bdb7d7c47870560907620b3d849b50a4cd6941a8c069dbc0c132a657b87128f8b4728608f3847b11cad45cd1d82e68034aa11b3cd190b353928210144e04b674b6f35261acd6fcd017175a29f2535dc4280a3e662d2a6091f8a982a762cfaf9b2c7ee6831a72734de6b61f53408c8d6fa1aaf998061ac52e6c478cd54f4c0a50b18bbe74dca6ccc0a5df5422da49db54b176aa760576f17798f8087ee1de503ba91185e96ce2c8ec59144bb7aa7a8a565337b326091c61e15e04bae358dd2656cec00e2c5316d8fbe026c9c76cdf7c46c1014ea01dce4e7c237b9ce68ce2d334242723cfafca454695dc663a0f431ca6bc6e565908026be6aedbb35271ebbeffab63b826364790a2876e85f3d82d9ce2b4375f32a6533eb39b9c4f27c3ab9b79f03dea3660762ec664cb7510e59733553a5a24cbe6bf12a57b3a2bee94b7ffd95cfd5f08b8a1281ba563fa5b7125f8002234c583cfa424b9a3bff0e222c92d59cf7e591e01f1388294ac7110621c4ee7a75514fcd830640cd5632dda8c339923b7c42a818082cc02663a02623268942f1b31ee5f0fb517a831819156b63dc76e74a36b1a99befe56d8c724e76eb4033a0ad4c1e008edd97b43d2be2288be7f9b1fee9601a63a25e5569a4414bb1f8cb9c2822310ae3db337b792803843e3954b3e863a38159ca122e4b7179ebaa1ee075e766c22aaa41fe50faf2d67629ff0da21bf74b8b2bff9f2886294381822df51cbc8a6fa57be34e1b05050539aaee642cb14744c28e5b87257db8e16fceff09c801052447e779400e5a1f13d7fe8d02fa82c3f846786efbce5e645f0970bc5345831d62546c20476d44ed442b9edd6ffa25568527474e705d01e9f7bf81b5c4f72e224efa7faddb00e618a8a66ebb177f9e650c09e41de2474c163ce106aef497e7a20ba74bafcc55c420bb34d9639f521d4863fc6d0b6eead4a0f22e48d885808ec8bfc15875a09c7411731d6c36d956a0b2ba933b8cb603771eaecd40d10d513fd47fb35bebd306613cc945b33e45123362af6105fd4f570d13b813f5be40c8f12a92ec65b1409f14408bbf9e4bb83cc808659dfa926a897ed1c4a90cc64bec99ad9e701edf5433dd1186adf4fd2920de3322880f6acae0f3f3ca75d7fb7f680eb24d96f3168a9a6303ea257a239206462250aed8007c1538fe57f713f377114a7cd396542027e2914bf414e1ca1830ec29730991eb333dc63bfd5463df11eaf3c10cae05033b72bb408e48b59fb8381064cc4ccdcbd53347a359f2aa9f5741ed49c5ef70f6880e3c1300935d9f4c78f64755331814ec622af403917e1367b2d80fc41686bf888a405ef9e1a182cd113643d7b6e8f51470c12d1df0ba391ad2554ac4853987a2b6cfc6b684216834c1a75f573c898a27d57875fe9d72fb219d05f959d0c6121193ebef7671b0f96c444287087d9a3400e463f1de8fec138e7d390717a5cab27d6d5025df2cf92a2dadc7232aa3af7109fdd4ad61d7f7c4136aeaeaf86940b014563fec449cc02a682efa1b6c9965474413629080268110357925dc6e4c86069135c309022d93887e38b50e117f766fca9ca7736e4d416ab05025b7e9f2223a81d5a88bc9606a3a4f3ac328566359ad1b4f916ba48dd05f10650fa24232e898e2f7d84d5299ac61463158eb4ae7f72a5a0a825a941a268cea2c5438ae37c27527e621f33e940343e1fd07ef448ab8365c5ddb839847c0a48e021ff1fa27fc3c36a5aecf4d7ff7c6ae1d1800bd84d629d020cce5f179160a287b8d1c111681775684c3fd526d54b5e5bcdb2ec0ba67dddf470d351187ef036015164c2752d3a411c5dc9d5474edffc0dbb211815b271c29f62427e1037c3cf4d01c061aca55fab389265ad10b92a283e80335984472f295306a5f19d957340f2ce8efc74ee7214867d31c36111be0c9cc7ea263cfb24deb40414a7fe12808cc5eb893a5647eb02226d4ddfe297ced716257bc3f5e58c7164164b00492edecd68a9c9c4ca343db61d2b48abd51eba063d64dcab352ad93b29bb8e038e2e9117b22869f79809d33429fe80b10c25b7e51a042f4f3129b0175742fc2a36ee29940f74a4e0f93780cc84b596bcd9867e937800927527c50ba206af230c5c7805c946b6dba5baa2460dacbc8c68831d020545fcbd64fc3682c037b43712484a4cf35619200bb7f88ce10784b836f440e4eb6b6859efba9b1a2bb0122d45d6838d8380945ac4056e3d17c1a051cd5e832aa9cd840c516d1f203d77984b1d4ce8cdb554140fa588a40b0ae87e8268676b9b38e3becbb251f45d9debd4c22b1f8d545a67aaa55f75523e2c0d3d691eeac510e3fbb39b4f6add697c19c393dc40283dfbb77553be17e665014f17a463322a2eb07bc6d681abfddfc6047e2b047e2935a47ff875538bb1fde98cf5a59893a957130d0692e3e197132a50ee8251c2268f52471f39192320c89a3a9690f95cc39017de13efd6bca1460386fa686c5d13fdcf2c853271fca91d8c3595c98743bebf387128deceae8d3c6d6187324dbbe773d746b6cd4b47f2f2b8900d84b160773a203dc382ef2109e85bf8a15f74610d954b8ecd39979cec396f4c9d0fb70f27d3a566c2194fc930dfaa25c86008f6006a735893e321c74885891d5d04401af0e8894d7b30532ce1609f800e69c8fe51f2517d131fb58c004f8c989118668e3b72a8cb4fb0d315d63324d2b458b228d26c1b8173dc80152057366cd1b6800cbe70a45f7a7fc16f9c7f5bf2220e651eb12449b50c30779f7bf8df5de089fe010e54976cb149e488626b7152d1a736783af8b68dde670b5180f70452d2252e997c670cd0a9ecac31a2e52d46d2db53ce62844d983e36ce7c86aa6e9b17b5690cb7cd6707d5ba5bcf8807d73c5d621b7627125930bf4293168055e30c1d51d4e61234ff592c5351de0932087979022e81c6efb2629010f2ae2650e95a0565c89e8991e8a020bc147712e426d598450498a2aa5de05070909591dcc8d99d796e47be53a3ad12463e99f8210caa777cde9078b25507a9c7024d278579c5e3f5b049913da42a1e56a61371ee26c16fa95336e8bf088a901199867c66707df3c23446952796b5eb723222eb2137ac9998704664ba219a421daee864834bf524a4d8d7d972e812e8848158a89c31b216092a6cba144ad8a0b1115a8011be61e8df156c13b014ff7fb40986eba34989b2e327922c95601d48dd707f7fbdcdfc5db29911f8947893816f710777f22f413cb4336b8467d52867ae2bbc1e38e5115d3c124c9d84998f66d5cc28434259c06bc61aadb3a23602e9c47a61ec106eb06d9816cf877cdf2de2bd7d043331910c4e6f38565c34ca105794c667fa31e03d04274e81837a08b12b164dded0c45bb9ddbb49d96e8b138e379462fed745e8f1b9488f6b6fa130e31e31470a000098ca8796e50ab050353c0776ea8eba39d0cee490f42a7eedbd66512ff74221764ec39e60075a2e2759f8ee33751487ab4072616d2d58a841b12df28bb136e5b8e687d96db98fb2cc0fd8f94b12cc1a5d91f21c34a6cc42c0bcc33f6110315667b720c80cfda899c11eee583250c312d8fcc33f88f429330fadc7446b613fabf946b0e789c7ca369f036b111de29ed72e2c6e8f757445c8c84b5d65726cd4db9ee9783027483baed09f23dd9df73eddbda6fa68d15522831af2cc2a0b05be33c5d8b714889cb06d0b0c9bec590e3df4a91635def754b948ea384380889e60546f42a9d40b993489319493b1883542c16496c0cbd66de080fba0bca2938873b0f1739b6926c83a93d5681ef3ed8092de763745520949eae0dd0e198a781445072bec9ff98905e9440ad767e3477d66d12922f7a6641306bfaaa38a9b378034c10587ef7c7196b69ae0c7493a75a99477248146f007a3036e976dc386e9d6ebad150cda57fa14167e78de7ee72db1a910db77238c01b8fdae2dab22295e5c9f0dadb665f8cba83fe7177d8adb1d90661dc329f056565e9f633c5820c5239a79747cb3301f6999955dd7d5aa0859b7155de7f298193eea3d97c30a6037298a43f7e7ff88d9c61aa4b6ecad9a51fa2610e76e0d0b84de06d95a284dbe2cc0eb712f61cab97844aa28c279785bd24f02d84a6345f943c46d72c785bfcad742e6b13dc76fb8cb7c692e52f45ef1065f59dacbe93e5f4c2b46e7e5cecd9695f4198facf602c4a4c63a12b7308b2537e6b893859c40def7d9deca26263417e7055613a32278c4bc001a76911eb7c36cb2d28974cc5b4fb63c8f2e5b02ac0da649c364c320216f204770207642923a7e92803268ac44eca483f57c93563367d0722a5ecc5d1be19704d9363a529703400ce7eb22d3c103136dc9fd28386353de899872047a75c9259b3903719bd5656be9a7ba42c1955b5505d407c20771f6371f04242ff087e82930923e5f43fe0143f1ada00b219a6fbf0fff2533726d0786fc727e888da4fb5f5505f88885411371a6ab679d128cac6fa40618d4dcbc948323922495019faacab8cba980af177c0af643ff60196a8f545bf8c41e1e6fc83b49ac8385acd6b9f0c1a333b3cac407dc2e3648d73bc5fc9e752494b7216cda6953371e42b0b51d33b46bbe49e071d00f2770f7beefbcfa27909f331e74f3b27c54ee372fd44a17828ff21148206310409e07defea18b10118b5cb78b49117e673ef44309ec4975588d0a67ffd85984b41c03d032d8a09dee7405281dacdb174647766adea4b7362989c76cce81dbe0c80906f3d0555343661311d8349fc82067fb743c6ac67101a360172b11c4ff5a7554ba249840f281c155b901a6ef441fe74b983bce4741ec85320854c5e3e63fb58f8a06ed2261ff5a3b8834466dc8b4af270440109d62afa092f61197b01e98387b9ec0386013d8d8b4667e6713c821f85a88f7641ca5e01cd711d8ebca18acb7779766c7d455db8eaac9f4176ed9ae4ed6a395f99d6ba516cd0679fbf14e87617fa4d88ae49a3df146993fcc04ab7a47ed3ba0191ac67a62b52ff96be3b85585c6a4eda2433eb43c85cdc5fc6b4430126953d36e4a0991f9de39a515a94b88a53e70797cf22100d7b84137d2ea0a5b8a0756df572939f1815532eefad0c54580502655eaef4c026617a50275a64bece4cf64739d874db05e9300b206dccd88f6603e3a3758cc5728d0a69cb17dce22e9a0729be60aa76e4b464641239afd7bb9b73a387d458a2c59073d23345e0c909372a55798e9d30608ed62e8acac7abbad8bc54d95d8ead554e3a7b74535a39293c2ff1ddacbe3fa633cc808b40651fc2e6a7cfa36ae3051740507c646349d2b0ec2f839930d3e62ca050827d965edf0cf50d17ea197bc2d183271ba60210a4a9ff5ce41c9609189aac4f4d1a160ae6d494da7885a962c67f46702e3628feeb3e6561f8a752bc065d1c84c1226c5a605c297062c81862ea5c87114f48367e189f1b610414028e8cb970914c560add627a8ded837cb7e8976bc8af57971f865053059af19a2a9ae88975f3b99764a1602d06047e5330210af285ad2a9edc0cc0f0896584f581417ca8f6ae9adbc1964f00883dc3665c2d1d24414a6a307f7b34ccc0ce846204d933e02ae15a2ff746c05d064bf3b93cc4606eb9a632a2c75a1e6798596a27058ec62c4b89e5cafedbfd31b4c692ae4051af6d55ceb2ee28bcb8ad08ef1e10feb811c0f283b534ccbcee2f4499d15578947b41b442660b4239028667fef8e7a1045770177cbee4509954821f9fc93d3e846609bbc601074776f9caf76f2252775264c428df9c09df4b155babec08a727688efe536b2a9d84944b52f4504feaa3d0bb42f8acf8e0d52b7409d10969e2c11116d1d445498abedd40e4024806048c2c600aa04a1b3b776e07a83a61e3b1a3cc4b9345063ced69a6e0e24addae76540e56904f0684384b05386b61969a4aea3fea68a28a5a547101d0a75406ac1554f474aeea61f14a798dc0d75351c651ea7cfa3824121f90eee58141824aef404e4b78d5ad902635f36f0a587f19263268b20a837a25e57b27f0b6acfb25afef7abb1917d070a5b71633ff83ac6c3394da98e298dc9e3599c930c52b92ffec4518d50348dabd37e21852ffc5545b2a441e9bbfe4676eb4d8c03c981b516f2c1c342c4031c32d310ed17e24f82489703ec8ab02da69fc506ec2bcc0d5bb2f25cba4408b56b1827cd35b2a198ecc7c1baccb647f1ccc23520a85b8663e19dd350dd8587f7655b020c45f3d65e9fadcbce0faccb9f64b31174ac5b1128fe1db2b586bde13fce54e8d7b6358943bc060940a2b53b0260eedf651e64f04f64fda7338946236b20a0fa72ff161f60ec9ee8d3d93d93ea20789e854923bfbb1923beb0b07cbece392167532b8c362c9441686f1927d8c327107cb1e53dc2a34fa686119020478e0becd1b4b8236effe17d7ac529f75e6433e7192e1e59d4a690beda2823ffd07e75bf843c2171398ca3bd2a6eb47d54b3e54ec455975fce46ee17c05d29bc1459413891bc14808cfd085eb69a68eac231ed6c16e524351107ebda9ec2b3638e7b38c575af23f5843db337bd25b5f5301c46cb6b895fd54e5d6f98965350374b75c577896a2e6a7fa772d1d7963a24b0b4a3d24c4319a707a4c419e621348d6059d282ce65e7f6a0ce83365f6198360cf10ed3192c1d760ebff9741a15f75f4cc4b7ec025d98d695fa4c5e3f07d58de16e9630b81d31f281736a34c12d289001eb7a9349cfb2af054487a150800abebd6d1ec0b6e87b34a6c8c5abeeb35052f5d9f6583dce301bb060e47b5b0c584ca217ca236a0d124675363686a09017106f117720cfa60a8dff8b51a84d2f134c7809e2f659a56931b630cd1cb13352e69629a0e9aa0ab4b19fadf19c0e0d7ba05e5f957984edff3c69dd8ae6f1ac658d2ef9678827a4634efecf56dbe14fcfffa7038a50180a647440aeec8c841fe12d752ee7d5373af0a66d7776dd4be43b6bebbad1be9ea17bf895091f301de9d4a4590b2997d679e1d399e161e0c7f0bd845b09aa5a8ef81cb05b5a4d54c0c590e781b26ce91806a19b28199ecd6557203f4363e546f44e4e6c77a5b2af43adfe188e896e04f2392914cbc81cf78e80c87a89519aabbcc0d64f1b3e2bb99c6fde40919d397ee23827111cdd9d9134a03b1b4ae02c72d08dd8073ac34f99ac0cecf39ccbc1387af34e34c207dda31cdd04bcdec90622c868e8b905e135255ceae17894133c730391671120625d94dc32269badb32a8f345b7013dfab7a7513d85436c17ef20b91250b31a305d2e201c28c11edf7a48ba1b48e6de5bbd8712360164e818b3e909783c8c1598c8455cbdf8421cd47b234bba62ab1b3c778c6eb213cd593e00340d8ec6c3437266fad7d06193a69ee3eb4e85a0ad349c8fec0631721fe69e80007bc392769f5cc430a239ce08aafdb7c8d3f7d4b63ecf72cdf608c909f70283dd2c901c649eb605098110f426e8447a3ef41022f4b7b983356822deb078ae53b6538964655bc2389abdd2f087165a28bf11ee9ff00876bde17473e7e4225afdd1715bdaacadd19a4787bc334c869fc595b0b45ee234ed9701504bd2bbc45d968bb8380ba2f53939f8f888ba7e220430750accc41b8a832307aff81440c321cbcea26d6c33dca393663052f063db9e1f0deac7c3a7ea32a49aed2f21d85c9f7e5956533b72b0e3179483faf1e893cfc9eb0c28071da267f41ef9f3e2c4ee377c8b4c1788e20a6d0c2ba6297c948b12b22886871d0755878ccfc32a937418ba5bda0356de7dc90236ac50b91dc340ef04f472dba29e800ed59da35e20cae1480fc0e7fb55f9e7d04bd49e588aa23d04a47144cede7cba2eb5656fb6e3c81fa35af84757cec247101546a1cabd7733837a0b65ac466cecebb0ede81cd96fccdedec6ca4553e2e54a76e37b30e34e1eeec7a3f39d46e5aad57645d5efd70261d715b995d9e70b1735ab575b91ad3fffb8b8727913e18a9895d5f68aaaefd732c2ae15b92bb3e70b376a5657db235bffed7186e27286c1f961e8fe5310b0b2b2bd22f4fd5b44876b3527b27b26dc851c2e2703b810b362b55551f5f8b544d865454e647a17aacb5b9c9f179c8f47e33b8dca95abad88d2fb817111674e837121bc7a453b32f5721ee396436aed2d371c44af0da574071ed93e7a1fd3c95fe442e9d3b9961772394888af18f7a9b578c0cdfe15938e3988db22846991e48598912b1e8b182a698606b92830e478a4bca007c62066e9ecb3c794432fb1210201b860594b76a694afd7cdfc1f5f7fcf90b6010299d663cd57cd68b98bdb3b3531e96b4b167f789fb2b6bc5705f6380440b22b842d8aa02ede577f0d87d8eb7c3b21e570f16c01250d53029591f3dca04c43ae282310cf54151f1dc8f14bddd9c08f9c8abc298d773a58c4b2eb90ef4a371d65ee7aa85ed4d34ac501c33c8bc8a247db1da6582d2bac75ed35c1682dea093a20b7d96fa6c48c27bf66e223d53ebb2494ae1ace0a62df3c10fac4dbe7793185d8bd9d7ebaa0637d23ac8a4728a8279a81b2dde4579b38049989fae2f38cd78f0f884dbcfb5f42d8a53911057aa33b8966b3220e40aff32bfb8dfb4d2147770935164d4e32cf04cfdac1baca185d03035c099803ecb0c4786023d485740bad48b9a51e2d04890ff410b659831670bd75248bfd8a38901d7aaf79fad599681331187ffb18654661ebfbd8a2651385289457a69ed6630948a9233b9e4cb379afbf8e69a54faa5501574dbe4429b35c2b12e961c1c511aea51b9372e515079e8cabba9bb364e7e76001e6210fa3f9706664dad82d54a62c2c24ba4fb80237e513d2ba923ebed383916d322fcf4bf5080c19cd9dbcee0c2c8d23ad40477f87180bab3a7c78102752a2ebfa1584f4bd9a7997e130a5bab305ad633a70ecebc3c460b9716a1c676777080337b8199f893dda186c0f4300df24de696ebfa9b9cdb86b6f02029638505b90765ba6cd4882e937e7086ce57c3fbf8556fadcbac81495a3be9184f74b0dcdfc0767f8c5c5219bf6860e5b841c496470622074c061ba380e2d3eec8fb58922bd917c52bdc665ffdfcda78f292a758eeb8277098e914c0dc04515ed59dfb6e02a88224bc56f8b0692be3d421d69895be51c75667200b772cbf9b9ac6026cd1a6945239225471b250ac098494b9208aebd88f469369cadd39b1cab06da799f5f2d09b56f7e29d7406f90120b49b0a523f92ba288f0ce785865ab54f74ed625f450fad64647aaa48f111750c1d3303f14452b931add57d1905647bebdbf455d83938806ad284c3e921753494e5adc91d5b19ca7ac6f00e5e801db05d22e32853d9f708b033c90cd97b81417fc85a760bb4cd9bba7b8d42040652acb1fc12f8362900cd333800843da8f86383019fae1a9ca6863aa405578b2c344e105263fb1f76cf5e3f785b2b435df790adac43b94e20440341218d1c58a31c897c19b786c3d79208b7d1584a6a4f31b28bef913f0313578ab975e05c4fbe464f049b1a544a98b1f0a0e876913ee95243bdbc33524434e5d71bb8f089ca6d6d354040dd76b9adc2876131eeda1c25fe50e6086286b79729ba44ecb7d19c15e230a3769d0181549d842e501e064b61cdc45254a25d6f8291376a13f2971c4c8b908e72346e269249512561e442b8ab988b2f62d070b041a32da9183811ca55eda95bde40414d7aa05db3f0d47f934870cd37b22205ee8e691cbbbaa4008a045f1ddce92d00da20d9bde0757c171807e96371b76d22b053fe9b80b00a553e3722fd32c40c4e5dcaf1bb2bb25caf008767612a90262d397a264d6761a6ed1a214febe4106a7bb47b287bb5f5b4cbef36c42858fb970d4fcc3cb5b5d8b2e8e7d2c00a782c18c168d13086877e7ddaeacf2a4ac71f714dad46d088c1a45ab2736225709118b5e575b21f4ce34c7d206d5afc62b92790c0d278a5e918926b8833f60083260a497ccc2e2cc350c2d9002502a29754e8f9e7fc183eb3bdb37a4a5cfb3a13d73749ae4c553ce01877e5cc0b13d4af68bbd47bbb3e35b0946e25f4662422bcc0c24096df9056aeb999ce2a4d636508e5230ef42472094678a580ada02d87ec634245596ac89a5675b7ed3473bf61bc00639f8c6780da38091b4bd5ae08c19cbb91171b04aa2b5ce5b0c7dcfed61e27fdd31302a5b6e09958e410b80aa12acb07f6000d44b98ebab7b1bd38122eebca2a556250eb2d7a80e65393e7193edb45413009104c007957bfb40a5101387b2605d3ee498f214f93d74c6e83a28e267832664aabcaad85079ee8e19769d0962a9f9c1fdf22a53574c426ae5040fe6b13db69f458631c0f70c5dfd7214a4b01bccdf1392cb8418b3c18125bff5e91edd84fe006269e2b345e4b1f21e663bdc375ad0118449e868ddf8e379b7c68e32f429d4537255f311455fcfaa83c20638d580eb0ab3ea00f26fef415de0079f809a1b3e7b762a223db5b073c2fd51b6935587fffde6c1ceec13507bf9670aa5699ea6dae3b22ff98c643c944d196060c039d644545da945c062763139d59d8e9f9fbc133efe23ae0f627cd8b306c86f69f013dde0214c524bfc5bed1fe52d0eb048d8acc1aad95ab34206492489e8b522c64c35dc4b3c30d0d152f1876f5468710e857267f760026321224bfefd0ebefca5036501f61dd10251f88579e5577ffe20b2283e85e2d0e68406e373ba8ec4ff4653a2d79c795e243f7816d84ae43a0819d86a621a78c92793695abc3564451de95ba0cbe7089d969961a02ec300d4195a21ce591993556f6b5633734d8a92328bc876fd95e1dca56b1aac531556cdd453250052593f42558639e096ed4196b1cbb24f0d7bf315b47114553e991cf655a3f178bbfc0ab52aa779913c8a48a89387446e2d66e57311af25bf2131b6cd5beb6c327eb781886daf7be7e1e8b4a289615b559c21573f41dcb7e9ff297333dbc57c514452b5225271cbc62800a07fc49119eaff13a32a35f07007ac395590e827a5a5059dea5c1f48e365ebe3ac1ca0663626830670959ffef9a7425a536af1ad28feefab711d9d2e13d6b9c035bb668677d4c7a0fbc0706e4a0489fbe222cfee1423cb1cb139579583e31412a6702b682f23cf1a21aa2ecc49237622aa5d8839948823144e9b59ba66aa1baf115e20104cbb3babf78d75a57a991e04d8aba795514672af0ff6c627cf8cf4cd705d325c5b98ec0a087805c1194bf67a380fe8ab2d409889787f3bc6ca394c48c58731fddcc0c339d001acf462120c85c52cb75bbf303cf406d3fab3252a7f62546ff2d3dac78b766c145b160746a4d3cf26e6df997b1e8c87c0b8ed05223b27997a531b529dc3f790a4f01c235b0c5a2026e06eafe36a33540b7777d47b61c0840cb8b343f9d76e463ad389cd0c6ed7cf9b995f35c606b48ba02b76b40043ee3f7b9429f6fcd5a86e04f464e03cdb430326c4e0d4338637c62ea302727e5e0ff58902a777bbb2f322b5de61c1981264583c9d0ea331f097b412e3b5ba2eed4b272fd4ab97388c614668d133649d713ffb1113b37a814a5badffc237fc42d8371dcc8061583aaa70f72e651c71a8ba2bc69bc43eb6f7843c9322028b3072c9d3e535a3fed3754974a9deb0b1d00f4395127ffd7fe26dcbadf99d1acfabceb5f9696061938f91c353f24a9548c152c1ec9eee3c12b89a9ef1a23180c2c8efefdab1431795ecf14d2c9a7c9b102799ddd70504480e0e28d85e3b7df6a2ab7f7073bcc6fc44632e9b60743a98ada1a081ad20f86627b99d2a799520de0d1a7bd7cf4c454c3ad36221c82d103617001546205a4d3480201600b79f3fcb139faa72c972e025eb841f82e9f90f08015fff68b9b2da8e745811146aac0792c307f9e16b3e05aa218f2d6e8d3dafb7b8f414303cf060dd733a44ea77d39eacfb46f1a78f9fb63e1e25f51469f80ddce8a2570ef74733bc2a8c468b4972c0863899f0365dbc15a4f3cfad2282bf178bfee1afa27452584f5f0c76a0db78e9a78af03bef9c08d5223077a35754498af97438cf73bd7bed9c7ff40545a6e562ef0c71b55bdf06741d353814d0b4e0bf9b6caf190553b26067ee7d239c28b6e940a2481524c42ed5e59e55629ae2e900c980694c7e8d7fdce3e0f8a22e2b3ca012d958a84ea49ff477ae508fa8d75a0cc8a3e5c4b08d66f6dcfd10c33943dce09a01e8ab39cfd7b184fc23147608f39403eba0dc4f251f8653a7e94e95949fefac7689f39e55c2ec8c8bcd0e7c6f521a6c97627c6184973c2491cc2d6ba0cfa6b1813df9b962c9cb35370b0e971887f0d29756deac2105c681605dd1c66f925e4ac5915f4b60013477173a2b9710dc7d85a958e0d987f85485009f741fbaebc3430a7d81dc3ab95304ae1484bf1a4627fe937b134366f685d0ce3ad140da91dfe0220933075f6348098524733565a73187e07bc6506f2a16ccd889acaee7a3616286ec0838297f221083ca751025c52e6e2971543f416df4399315a344d861ec5d34918109026aa1da290383b259e1df04c8c8d25ddde7f408073d89e4bf09ee8dce157cfdabcbf56af4356a4cbcd242abf8c8f8cbf6bf84ac3b561357eff15f2f2239cd232f60407cba551863c30a8e31f6e200857725812cd511f1dea96f1955fd899be54f3603845c73ae3678447f68d98d0f7e8198c12fab14b3417029cfc0750e5aebb994ba53d4401aef918a7be2bb3267400805b695de1932dd135c5362f6ca97be267f23fc5ec400fce49090da886386f1e42f14f0ee140acc0249181eae8f1f72285d836899099463a44fd3be702645fa3c64192693af79adfcbbeae4ce1b68b0bab3c18c8b9c514c302f74a7c429b81f5e6477b6a85394ac663a4659cbd7655424232744f47b0a5531a17476f707268aba12754108e0da509ec74f6aa3541920303e5d46024791ef0f7ab04caa8b7d2a91cf63c312022c344fc5f6087eafb5526a8f8d23ae4a8aeb0d8d6f2485cb02cab100d2cddcbd6c23a070de5a3392fda1e3dff0ff29f609804bbabfa736ad86409768c5dcb1ac8b612bdaa134065bb2e828a981f76db8c4389240e9c3c17af807560c16ac1097a77eb32f8d415e736606a144958076b9702cf29f276a3390b94288236b4776aba54e9840900e7c072c89c8f2211707c1a0931d06a4db349c7a262c195d112e6bb0a753ee920b3065cfdde2b331c07c3779a60620f9b4ca50aa1cf3759ae721ce1540933f40e14cdb3ef07645022e6bb78b148282720b222dad77da9952884149d9be4fb349a3612c2987dc709c9f642882aa3d0b9333ecf069236ffb9341513940827310fba6cee9c425b11f58aa6ff35a4400f7733eac41a72f26b181aa75e2e84216fa973887fab2a9e3401fef7639b2157f1d11985c80d25b0554edc1bf4ac9ca256b53503f78714b124d5b37ed037c09d8cd846e27c19f73736b6d3bdc9a189872ffd4a10d867a2230402ba77e82d9fd9a30cf690dcb67d7aec038052bbc1caf97fabbecbe827fe40d6ebf593445c84c26cf02cb77ddbd6491ff04429793805dcb777ea62c52ae7fc115f097160db7bcb9785b2d4dea2f948a29ed5b8256e89c16a34d7af9b09c36303d5170f770d3c6277790d0ed84417ac6404eeb04342c3f5a99040ceb6d70d41352aa7fffb039e36a4a20563cb84f83becdd394fb0543ac7b50b75c5561b3313f9630d63ddcea52e37e697f2cd86c52c1bf13f6375e30b31f09cd373532be8974c28991c75c168226e07a6bd3bc918ac8a31a7dec1ebae94d3f3c7b1b86ac366addba3b49a0fb75aab5c79a77eb692dadc77e14df8b51339f40abbb19659224e94029480b5879730c0e6bb7d213054270dde4b1fe30f78afd108b6127b2f450afeaf06531332d2c5f63fe93b5f38fea9f33a8c251e0d114ea87bc1e5a0f662858f518a39fbcf0d8bb876686d6e16c9dcdc1b29a93db9bf7d5f358018c64fb8b84b5f84c82740eaca0749c451c11b2805d336e862e40a0bc1dee079fa93bbc87f7d02b9d4b1b611d08f1bcc59962391a389455a81680cf938e1583609124972b4ec839dcf4e54d6c78daa1083ccfa2d25552aa1c64ceadcb9a43b7c546adf93ee5d4616bd7cefb550fad7884c16f362da51f7c87f770544b2131cac37ba064b995767e1f4d78e03168a0aa6d29329c9c28750cde03b1838347e682ba7ec7ccdb53fb8f41b0663964cbc65d1ae278f01e72bcb45438483cbc10e8b0041034a6a1517593d69565124013f1e89c20e72cf0a262323bb6c64e3c849e9690c11a53245311c3a883c96bc8dee31a0be22f5392a91ec9e33d1c85dae474b572c22b7d71ac877b6374335032b0830eac36ae7967f244ef1b0e94ba5767a73984e4322b58f6a0555a9b61c9dc67ae5c57b1b1ac2cf0372993f7d06d0cd8365a8d7a1a81062d163a235c0c5d8c25adc507d45827cf8330c1efac12c59ba1b3c22880b108d847148016813a95711fe4960afa0c0161fd6813bc32e7c3addb43deab3deae93e1dd5068acc88df91427e2594e0f24c34aa5bff2ba81450ca55c5b1a5382a636c098b6dd3391faed2a950e27bcf0551c717ce391f74ead1b2832aa5072555ac4013c3fb416114b9412c77ee5063358371d546f4aa1aa22d4b7f16d397bf65bd365860748b743eb4a19cce54b4d31f5fc63db41eb36c9be5a1418dfaa7f0ba5c1417f127b1f2ec16294de91be947e3887de407e41e0579a8e3ac23e8c3e383e0d05318006e139b14161b142849558bc8770a348a688e1c41ed6a65adde10612cccb029b219da3238805a1440472157e5c126706c6d13167e51ac988e545357d8454543ffcf439c5f8386ea3712d34be5818f3f190169227eeeba7ff72cf49bcb14cce5434a4a1ff0ba31d20c1bdf4ec5ff98febd28ecd7a28a2976bd28bf7f2cb00d3f4e568cf12e1f0ef9764fdbd5d6ee44c9588addd8e537f3b9cc14b93778323059113be909884618009ef3c7c4f617206337568c3d85f96015ceade4e67b1786f07ded397ff4c74c4e05297967cea9b80afd52733ce7ab2b4df8a3caf56fa504ed9fee4d32c9943171fcedbee42a36749af550713e192278cebfdbdc943c3c46ab71c1edaf9cff592e7cbc687ca0147cc16b7728b8600497bd9ad4eb39f1c799ac39d01c1cce4687bfa4e78d2e1a7ba76607ae02e142e44f45b8e25a90528394c23f37e435b30c22c9d8332a1b9f6c17622bbd99808b5523a1089547729205a935d991245c429ace1735e2bef41d4cad390d909bd893c6ea69d1fb5d82bd2bce0ec7b63706b652e8d017024b744ac417b37947abb50f262c82273b421e9aee4db099b7e387a73c3ca654f1bf93d023062b82d43331cb7f9a466f4015c76e384ab2a5f4506aaa9a2f61630713645e1d9828c82afa59b4f955879655e86a01a08e9d314d805f9e9ebbda4f976386abf601d26cd40b2985e1653fee8e17a4cc6293e4fa8f7bdda03ed96d262ddbe0d73514260bf767143dee55db78adf299734b1e981a680d6d13744651d729d6587aebef28bbe78b41a27c92949f44f040d30000f7ef7d9e2a5da04083bf213d89980b9a80fbfd7d28f8aa7fe21e32fe7fb54da42ecc8a150517627d175aa34aea252a2dd92d01047e37406cbb57dc2428102173fae335d5a8882f3f4129cd1dcf733b32a4f0ce86180723988a4cdc1d94d9c168ff2740d3027d051b6cc7d3f32db42cc1f08547b86999e3ad1a4dae55f96ccee428506c7bbada6eb5a5bf85091f3c8ea352f9a14618de35d1c37c2e01728915389287114cf18540700608d7ab641eda6923b6405a664037bd9a0c2c7e91a4802eae4c6c1388890f3598372db8cc187a8f42acd0d127a83848db786478d0eda824498a5eee94c95788c49f5b6b09d11116ef9224b73e7303733ffda37243a261cfafb1fc6ac690a47bec03f8e96c899530449888dd3b262b616c606c813e7e3d72427c95fe6f42fe18f0173bb4faa4e73822a43a7aa0213825accf36004eb7551ccb8016316ec575f85386882b499f0db02ef2e467bd03a78b5eb567bf884f96a79de36a110e1c8a8e3a817d416b2a488c42a08de1339d4647c9932e265f6985a133ca59ab53fcaa0127240a4bd786f16b7035a817fb866994be6dd0d172d85dd532db216d9600404ba483816b2b10960456526f9ee1c1dc9d9672c04a9d761b4e9c86ba7e530b6ec9287c8e77a73f1a3038a826752c5723d7c49f035d7d9e68803719424461716a9b4e17560e7540f6eafb4597971d806e755088e7b24880e8acf6cabdf3d9ca32108634102ef913a885eabec0a47f1fa10b82be6eb1b6b8a0afa8b3c9a9c87f2a8f83fcb580aff66d76714365a333b226c4e8b355ab2e47e3d5c5df0647f5fd805eb79d17cb5faf5f7d0910d3301ec8d937666902d1d3c7f43adb08c97057326ae5b179071a927ef095c78cbce5aaf4b23884568805f997818227854caebc8231e1699a10e845290a8450ee5d492542a28e71c47672e183ed2c8d064c06220e5159b28750a36800695c779c1cfc6d0c94b388f252bc528d8cb6e4b6254d7c54335045adafa8da2b7cd6c4f6ae78d4e861d75e824d0a6b7c49d96a481ef4cfa2ed0cd4a966785d039ce414a66141ff50757fa058ef07fd0c4a743debcb150402d9ca4ee3a96580c38d11cb543cadc68f5083d4990d42cf17ce1006050aa866d3f693f6a617855090cadc0ab01dc3a4716a513e532abf4aedcb14635c7bee73c31aa11a6914c892e15c558e194068dc37e27048c892321f9e1ef403142546d093285be1f37247846ae118d7eab427c723911f563d4fd90a5716f443f55b9c770bec62e29b5ca2a2965098c25d534054fbb0b21a4b7ec927c9ca556bb3760998b9c665b2f689515fa2e9d71ec1fbf14e36fe27a639f91170a38499ec71bd7e2850e48296c474fac6fb08016d19805a6a2c0cdacefe00efb899713211a1573a0e535814f4f730f7412d8b5fcb2827aa0d829b71c980e97159c753bb655f5f9798653b2452e04d0580afd4d2bc6bae104925a400925ce7adc82d8e642e2374930c37240663467d0d1249582043629180c6fe5674f02a398bb26e4e190f108e43f2b577b13d4c60d416c1a3cdac7781ea5858a62a2ff4d10d078d966782d5e0718002f2355427ee941a80d4bb669d1e048408cc6b2df8bd8be8cd2ac10514defd2220fdbb231afb69efe21a20b110bdeb8786e26acc99d3aa29adbb97bf35ed77505392bfcbd9d3b0e1c1453de1d0af948b2e2798748a44c02af3749d1fe639151d09c30844434a39aee6c5551212fcdf7a71b09eacf4546344572b792642a7eacb55184928b6cddfacd15922094cdf31dfc00ddee5931cdaa78005b516a71633afb291dac404223eedbd7a8406542eeeb8e8c626b1c9dfc2bccfff0bf46459e9fe00024e5b7b3cfd271a4383fff8af4c1004d52573734188160fcf7629856adb3bcda9fb875cfcd1482d80b1cbd42655465265a3ca899b5b2d81472c0b3b84190b28049a976760b3ec7e5272c13f6a6e40e3c730f07eea5eb474ca5157752044fff717f3171ecdf5f3150819edfdc8197a773d0bda2be841f66200370b583293d7ec8a0dad96028260ed80816b90dd581fabeea001906f29e1b6d80be2e3a4152d9ca304f0109334e620172748dac07c75bc544c036acca5d44abde2f17b4b842bd3ad23ec44dc5726d9a4b420a347acf7fe143d2f02d27c02e5c21d9d35b65cf6d90cd9221098bcedc59f778cb1986c371dd6288c4774fcf8b2349de387239529ba0590485f2df5b6e32920d6af3c72492122846a96ba779099f63db8760d58a287d3c1edcabdb32ef3d88d1a659d86a8da5bbcac931035ae8630e6768b92caeb18bd209c77409dcfb374e2d82ff995199c0b5cb4de4e0b33b4e70a9564930767ef92e22a70fe6619651eaf1458dd0e324c4df7261e77316e3581f8d1f9fa6b55e75bc14483507556891fa140dd2266f02f8aaada0b1ee1f7469895fdbb888260778fa0fa48d643d8f85ef9b754e9e674721ce523b72eddf99b9d46d8549628ec8b63fe02b57cc238ecd80ad43f369ade4a36c95d9d2cafb3a28797a369c24fba8b1a1c701d4ce9ef88a7e75590ffa3b3026e6eae400a2d550c0426edb705d1e3f01efe082ec2061095c9dab772c63f79e39d6cfbf365c58872fa8b23942e4a6fa3020048034b0dfdd0389ac0307a5aaa51ee2164156e801a099134eda5459443c5e539a835f10db43db56f5377799d1ea1de04ac0f17b8a56250b87828b6f654296c546569fcd77ce3a702fa98922b9201d4ca2694091901eb5f5c9906384085763d06cbc89ef884235fc58d22f704645df1c9a53d7f7f0cd9e036872dbe5a2698e83e6873f821fca411931a7d615d45b2203a99595c266988fbcfe6c7c98403409ec01c21044fa29effce3899d5fd068bf2aedc4ab502f027d1b2cf2b8225bec08a88bf00764c453825f86de6916ee014464ac8ff391623c74f75fe4275b05e6a204ba82ae9aeabd0b19176866e53d8ea1bd5181f23296e7a6344098f57c0dec9452b92fae62654da0508e2666929c7b86bdf1cdad8c89bb4ebe204506cc0dfc2c79fc12ceeb9e9f2be0a7b7754db9be489df65a82c79e1d04e6a4e0ba2490a1adabb9a3d1958fa93e32c1b71f40922263f2da94b2ed301e03fae411014ecee9aa584388a56fc0dca84fc1db2e708aaf5d2867e9c844f4ab34587ee8c144f8bf3a285ee21795c629ecfddc2dd3f635c0f30c85df7e7552a3909dd805d36f41a22f24d12e3a0e2e73574b4b48451870d345f67c03726c1c7bb00e7f5f0fb7ce125b6d6369a1bcf6f49fdb055bc4daccdb37b4ee806f96b9b0cc8a6a6fc41dd3195bcb9b27010953316bdab0e1b0457dbb5829105c9df65d5af5705ee0ea2d5b8bf5f2201f9b7904d65f918305a7af4f50ac7decca28fc55e1b9decc9af16d85e73dfcf4313945259ccc649bef8405f7f5a00aac85637864077b2026db6555044edb011f67d5c70d8acc010ca639a35c0ebb1f675467d282f914da301d9bf42f38b43beaf247eda570379cea384bfa220d764081145649aafdcaf92b5e062db12f4c1be4b1abe5fadcef94b742b8873e809f10398d77d0de0bae03f4dcb83a9a2279f5d3b50bdde4e93fa3fa308a896b9d663f65806286c74ead55328763344f993860795e7bfd70231820d304559c10a20361d236c1be989c468295837c52d9c665ee10029195d4aac396360094098d96200b48a39ecfece451b359c94f8ffd3cab08b30bfaa2b175ab1bd0bf97b05b71f5b587fd04f90f0cf82e18ad56c90c8a3767fe73f7a9d5b34ae9f201bf8ced374a5b173bee62a1017ad16a0db1a9c5c85974d6a7a1686dc4b762348877e129738c6bb46ce30c38511017255a62f99e9d9ba0045b6aa18c1394d8b17db76848e0a434e0878d1140b402ce12c7d79a2928f8bd3dea6702cffb82cc702b74e12a4d033f57b3373593da4c46ba0051482b539800a25de8b2bdb576138eecbf0fa188a315568b15d2b6b85a1c7021e1e9528312b68b0b5e5010c37152d3466aa03056d7f9b06ba10d9ce5bf55f681101bfc063ef764f87522da37bcf38d4d67bd91f96a8a6dc7f886400cfbdc8f3057435e17f38a0d35c004bb1301b49b3cc1c07d8b9ed997840736ee60462a8d7d510190e0bcebd18039e0f0d21cbffe8a470815f96d612ce4b38b3d81d4980af9f02096031d455367b87de92d92f4541ba846bbc6cebe2672f11cc4aaf2e09091c12539a1cdf0392153778a0a6d243a6035a6f41446ad75f436999c86f3503a631e862932c55230392cfc8a554c9002e43fe3e1d938e95871008a2b53a582fb90ad35d66e4cb5447ee5702272e8162e700188aaecd1c49e05342fea1a329897f76eb35d6f187e86a6e8fa34954a61646f0b8f3730cf2191fafe19b7762db63d391cdb06fa60e99bc140c8b76634cb9e6c6c1e52529fed9ffbd9696c9516c95124f9c633769a782516742c57014715e4bbfbea2d89cc66819463a749629f76d62bfb6e25a2666d66d8e7a45fe86a80c4b0686a1209e0ab5db76b6a8c441f9de1fc289551b0b513c0dad12756a6fcf96df6f688cd0fcaa476e48edfb330b49dfa647fdc6ffe1c5bf83f9c873890ad28727db3ffddbefb1e398bda6b1d644e7ae156e4b10ebdb42db3f6d86eba06c93b3ebf50bbcff754a8a4b3e487c7c4da0c9379c5480588e035f81e179e6e444879344e6833240885f83fb2759c31abc65262014cd5acf38135f9266161849bccc9a60dc60da61563b168747eb3a82038d4bd174a05b5edaaf6e9ebb3744dd6344e06458789480f482f5620646e62ebd5f61d6385e29069138dc6babfbb587b2f3c7527631cb77b79e86ef850473cbca7205071b0090ff2da16725b3447ab5cf296f2e9b2fd3f5c5bd9edcb1fcf5616750ca2eeff1fd5fdf674c50e1b8cf5e67f8d5a3dce236f7083d966dae129b13d9accaa457ecf869995e33a49532a991ff5b74c729056756e3e681183d632a37415a6a0d0a565941c2cc170c64ad0430c66273475427d45a1deb0d6fe1c87a820ef3eccf7da8b6b65aaec294be9dbc429accb261ce7027676aecd810eb535e9342026a690f4a0384e221cd8533ca84bf3c9755b677e2a31c8b77acebb9e12de3f32b28c7fe224647d8375ed3bf271aae6c08fd24c904f1b2da472146cfe5c268856960c896a70c44c3164f62dd2c86ff1a3a819574a9692e2dd0f5c59a04207e98bdae65c26d4d28641c41081e79a0a03a165053ff23f824673648934d25336c214e8ee98197a0611f3a40b195a61d14ff16d741386c65fddeb28575a803e52893511b8f13b87ad6e30f5b19fc7adf8086ad6220ae54cc2a7a0d6d38567f8449850ce601e65ae16c8cf7b1b0810855575198855bba1a5d227cb0f0f7815a2ff44406555101519cfa01722e68cb7d48ea10775315d92582b6964cfa525fc742790644d21c279f3d7130b23e6cc906011ac49762a7efaea93d34cd74cd2c9075182c65f1e261b9e37b65a5ac018709880c9f2139b98002940998ce8aa775efffb075cffa36efc00b0b9d4c0354e68302294e9757d28f9c38cd73d63c7257186b751ca54e224cddc43acf9d452e151c46df04c4943d448d8dc2b92463a180e5d3270001969c9228ae9b5c2c81427a2c71f134fcce3c633807933a1e79d8e4f88474f05328d37fd86baae0a203b4119d74b4d300b0b46550ed462b3d0c5219dbe1e9d989b537e428f0fac1a5ea229846ea0f67ac427d177999707b470bb4ae1d81d574c82cce400ae6b888299e267524078ab7dc2f333eed980a95a2e9eb16e27b74b135602a6f346c1b307adb19413e669f7866fed40394ea4a9a9f3299265c5728d70b7dccbbc89a81d457bf8a18ea76a5341110e922f4f3525d8add4150bf492862d30fbc997843a94e204c3d9823715f3538958fa9f5948520a4dd3a523849eaf2b41860dcad0646800e99c387c8ea0290b7aa60f23c1a770b2ad51776a78ab5249a5c28f4ec4c96227a9e96eeb8b01da5f8b041a22bbc1801bdee371b7b29950a62d534ffb38dc6a251082b6de6049169e4f4131e425ae96033d17cc2289541e92600abf0450f4e9b345fafbbc1213beeca0f17c892566f0553d70237256dc062a81ac5b25d4187cd1fa787827468c6701fdefb74b9a56ff420ecf13c738cc45238e64c37d831f10116267941645d6d05b80337b270fe9ccef4f6979964aadaf5a0f143a1031ae3d4de2b12af1081e9b64ccdd18cd0080638cbda19f94a3800c8b937190c25436699413bb1f055b50ea9180ec238d082bb8f89f12bcd284db0859442b4c34c60aed1240367523c16bee1dc00263f5628c46705d9e901a6e41b068c568acb1e3ec21f0b04be30910c094a42d8ff9e08f620d30a5923b8544cbdfc48b51ad0c9c7ce7e6056c29bac408e98b0d6166c0ce557ebd93d7a7ba2c90d8db4e8328b662f02744e93211e123853c1f4dead404098059e46a65d2c29b4381319b898c28c0fd3ebe65e6a9f84bbfca90c4c294b926119b62819b2a9f1342d8b217206b2f750eeea2ba09950f6378496fa09597fe842b60e647baff3ebb220bd84a8ee0499906d49ecfccd9d413e56f17dd64850ddf992fd163d9d5fd9683443af48963176d828502050449da4b7b78973ffa35843d6d0c5ccf9c5b19293e5380006047cbb5a003e2dfad029a284d7c19870f1237f865b4a4c82c876df161976307a26fab5d27d2f52a50dbf79c9c122ce9672a11303dc4f2bc7224c70cdf68af4713510fd759bd3aecee3abf0fb2547717f49c217886ebbbc0a0a9594ff788ba29c5edaebd4f057601fd9a250be2895cfa6e835cb65b3e0d6a5c68e8ae79697b6c673b4477cdf48482167699130bbd5672ef8dab7d0931390fcab8083b4551f418868a1af44e6419236790a7041d9b3bd48b165ff4a3c11503791150df23bc16f371b51534a1597c9ac4fb747579e80c56129a8c19cb71cd56843489a968a5b7a5c1b9e22471f0f0808da4d897d4da80dc0d9bb9260f856dd3b2f892338af4959095eb1a3aeb637653453f264c382184c40973b6bb6679e4d51979f1b7153b327df954e1e700caabc43849cbddc80a987ce6aa546acbba63622d8832b97cfa001e60baf4aaaadef1a4ad863bb17ab6e5c2902c0ec62c48e6fa867f44ae21cc093f3f24851289cb1832ada415ae4e8ef8f25a51c7617f90e20759854961bddd48b2a37ab35f57a6bb451d53cf287f11b4134d27c26a66a6769b7ba9c174b7e23ed1bb1beb94a89ac1ba2cb4aa39340690c4b02f73838de62a2f2e01dedc16044db0a11aacc3f3801f960067fa9e9ca655a3f9ce088d343ec543ce3ced79c21a6229fd96dc91d3f32b3223604c8fc790e6f5d0b8cc00f08488a6e19c24798b2f70027d32c8143ce646094db3d33ccf3af61c90d5bdc51e4010ac59d4177698b614bbc0ae8f76706ff1bbb017f4ca903512e1810995e1d31d0542a071f897b196d143a7be8b2db5b2d1ee60329572f5f861f1bb77303f03a228d15635ecac960573cd57c7dba77c0d96f8071b4a094f958eabb9e5af72d6d36514ab79787130ccc6dc6da8a601fe80c01245f35283404412103a957f7acc08c564c462677b5aee232fd85ed61db3b5e1715cf2f591327ef974ae5d9cadc83599e6e5e783483786015a043803a5c2da6ecb78554b7262bee55fab171d45d4dafa0f6bfbb48c34f6d8c23ad99005d8033512d665c23b3aa44c4007518d43da0b29c9416029116259e5aa13653d2741fa3f93655ebb667b5beedf196477ba4ea70cd89a065d19433b5d0dc1133b10bd7fd06d13b2e23ff0271f719f03cb6641f4e88eeac1a5f8b60ea9b315ca98fa181067e7b466a95ba5269c71f2735ed195833a69633d5b8b8b8fed8461ab5528d56d5dce6ded5bdb2275e72e8c30ddc13118bcc7121b9e515e9de62c456ffca0b915ef72604d1460c869d49e0898bf01a5039a3af626bef3e3ee594a5330339935a59c5a06ef058bb6189c3cacc0e4a0d57f36a90f9c469a88e3a480e746a31de53c0ef6d88a9309448ce9a874b8f20f0742b3ab8ccd80638c48a2b39c49b4c9f1035556bc3c32c720c9f9d68a3eddbef7874bf0a220d6319af498b214c23e16d9d76a116d8413759f63808f2949b3609dfdb5790c443b903a24c30f703ac9ca9474155c4710c02b42b9e44a649507be87ac2c58c4021439f4f03c1eaa62298b163b48c25bc38df2064e1003514f41809a88870135f915044358768b4331561a085837a65ebc2f8554db420354903a4e08e65a6edad87aea02a48ea4036f38fb4002728364202f149b663b46c3c8a5d887694c7a3d9c9a01c1fec233c19afce1c8e034a8bc1235047d4b8f6ceb61143df7b2899afdeff0fd545a27eb306ceff97f461847ff8799d6643a812a66b881e143488ce90e308d05626f4f04cac27732a16f50601d945a93d77f307724d782cc45db03afb810fe8dbe7157968a2e43b15232034c6e44a85bb31c77d2fcbf518697b35af483d9f64cd6c6a125eb7c4ae0b6e193866b80b89873eae009e7ddf27b6258751b78506f9624cb974cde48ae2a6d90a17a336542178fa69d8cc4f94feddceaa66d386d02f6c4137daba8832a7fcead65c4dccf5a212f65eee2b42669583133e3c3b4bc02ad0a9a3cf147d2e2b65c3720661a040dee0d521e82c116a4783362fa90d51e1bfbdfa977bdf5d91038137b17c32d3e1074341d72066cc41da62b9f35b6ff3f6cf39f8c02e66b309c56c0cb053f3954bae6a03bbca8c5e34bc44e466285c305af8cee4fa4027c84c63bab9e638ddbd58e489ec08478bc15eb58645ca5f26d4aa9e9f036c7310a806994ef7a0fbcc31590eb6882d784f6a178f112477682785eb4652bb51e29723ea6415232ee2a08ae5673f03704c0e1145227b844aa1a4bd4e26d42a56bbf809a38b5006b8423476c5a54cc90a1e4e17fd892c5473365205c53dca0773394e3bb4c58922cf5aa33ffdc6dd4c4e799c83b3830f813d964bd7383c9e1746c372f13f680607f68da1a25f902425ad05e49d17f7d8210140287c00fc507d9ba31250bf125939c7dd697e1b8ff108f194bd3c9133554194ab954d89c7c40404ae30732397956264c13eb359b7edba67a0be80ada0adef515375918a903efcb844fd29798a707d6e20d8378b4351f3fcecead8c0123c36f1c5c7bf43188f5582173d4d8bdcbef80ad435cdd998a9615089965b132a93748501b85a4b60153e9ce5534d91abe86695a5f9c96882c0fbc3282975419b4255b021222365429f49bd53d022a77a37e1b672b01671dc91d0524e3a0f3cf7473f3af9c5673082abaf52a6c58b6d35289014d60e6044f475418d10271094c28b54a91c124d9b613ff83a3e526de885421c1a297c9053edda2868c53cccf0ee1c12839cb1cf6a3fbd66f2c5f20a7217e61698f191beeacf257fd5bdb789ae6164f74124ba8c633829131fe86d5ad9e99aed36a24b7411d2788e9b53446abfc03cd012c97f7e120a8a3a7d54a6ea925e54e9477fed4aa6e08ff285873052ec4acd29445742c7e20588618a2877168c94f91c12023295c82f3f8fc8e5d32d63836539a6c8603f13dd2c212284f7b5e61c132f84a05af6a39e618afa1b8ebda82abf66cec07a687443e7d34d5a68bfeddfb4d14ed90454e137b846a826129c422aa619f0def30104d5535933bddd3473804b1101c65656cef4616347567bd0e04df3735815a2ef52a12d2823014e745c4d85d0bd10b01f1bbe01a88d59849489d38edd2e6cb8e3db9ab0d4123ac55bcfca1f029853eb9e72819c22ed46c4d022739f5af70b7c77ad032b44c46626c27d7df1870616b556dd1b448a41789f5b0c5f95f485ea7f514a8ba3af6f051bcd0bf9d8de576062fb8cc5beac7533293c08a94dd372694acdb0be6b5df3f987382c0c9dd45ee4a89755d4a2109608339eb44f16d5a0eae9324bb365a63c440e13c5435d23bd87b78548b9001dcf58cc369e8b84ca70b25e7e5dc61ba7abb064f54e386b6927a8e805ad4dd13136132a3f3e014a833d7c0503aa480e7957301c4d750e9980e4a26efd268682d49171245b6aaeddc92bce7459892a91203b8439786f4482380b560070388991a9ae3280fef556ed3c879b5dd7967449bf2e49d725fd24fddf49db65d1b66d973d44242864e5962277a5077d0747072727a8e828648eceef478e28631b83167b4a12d27ba3460a2a7a4db7bb5cc35036f66c46bf63ad868aae22b6e3f591d44115dba9828a6ecbd7a6d7b3a1a2dff2fe893ca49c4bc38edb97bf7717c48c34446feeaecc82f9e5154a1d65b0dcd1b36bb14f090c67220f2947e4d19188d062bfada039d157d01bb079e3e2e4dfbc78f9e2a0522f8b2f2f23f4f1a1dc816541ccea8042e4b9b8f20ae54f99059147eb4859c8918585869252c3e19e07edb876aa9c21b65d2ef0009d30f284bf21b09c842150250b6fe3efa681f91ee00281c012087bd1e6359c892530dfc23087ba125802f5ca8532d85e2194e116a5d64973c02cf61aa58e7066862094e1520be60d2e300194418b5d37f2b420958216d7f8b1216586120c25300be9755d09d423a952026f1e7530061a6863da58d4286f5787a0100d59d1c71785ace85a0ba1068a17178634d231e92b8c95cde2b2b2d232fe9843aee288a325d75a461c0952ad81c01f9094e3859443d229456096896276920e2947761d292727df441e5cae668a14688e949322e5c0224fa9a664d38b6116f75acf219174b04d9553d61414079128d4200afb4592542bbabcf8527af90c73c2a0fb06e71af7295099ddde11c798a3e2a775da12b45bed353437ba69d37e4b392cf8eaa006a2225c90af08b704661886398761ce61bee2cd39e79caf68820b5a2f0e96b1ec286aad43f18ee24892492c018f4ba83104b20a363c4e3cc1e3c48a67398b573df7da5bfcf618ffc2f1133f1be04c00021190400fb7182827304c562b58e45aa87c6208aec1c769259e47bb140568bd5cb61112919a0acf134e38f1048c99cfc4f8176e93b15c254505fe5aec221c6c2377306b83d0464221647df946f264d96d231106f36df3b2cc07dbb6cd1a42db562264318d659dc5aaf3546ed5a5d216ab2e55ad22dfc619ed4c7ce693f52b70e9733d03fcf50d70d92fc0475e012ef20870924f80d7fe000f70da3be004c51760009a2268ed031ca0eed4199546a5a1f2541bbd1a93bf2839c278ecbbc1385916e3714e96fd7ee86c6696e7a9a62840ebe51a2111a9b164a34ae65b959b6728c55685d0dcca4d54e43c470f598ba26468879ed1c8c3638fa0440502ef834f60d5c3ffe012789d931fd2e33e959e95aa6783e3fbb5fec57111780aacbcf63627f99b8b3c8c8ffc8acb5e857c109515cbcb85be7878e4e084ca324d43a150281412a9acdc6ca8d6976f46dde51d7a3638a8bafba0c16159f7542a4db349ddc14e371d71149a93b979d39a87684e24f7e263b868cca8342236782bd9aaee7a780914dba8eaea24ffbdb7e365998e7a7d54576964d64b4d59d51a592b5b551ad7005cf601f091cfc1451e004ef23878cd85c9dfe0fe0470daebe05b88dd06c0054d1104f8978e0efffaa93bccaa332a8d4ae3559e6a43edb978b5e6869797a8a30bcdedd3497eebc57216c9dfc0f0f5ee64d9a33757e7025f3ceb43bd63b8ebe66419fefd60e5c9134282a7446a938678423c219e10cf48c2c83fc5581fad130fdb741ef420cce5e45b47f9184f7995d75ec5491ec645dee6237f73198bbb7ec57b781f5c025f01279fb452d11481038109fcb5b93d18f023bcd8e3e5c5c7f0fd2d5c7fced567e1ac5fe114f830e6a502ef383ec4b43b9d70c6b2b2e2d9e8d5a8bb926f71dae3dc3fbbc9bbf8c9bf38caabf0941fbdf62b27799b8bbc8a8f3c8ccb5ee5afdbec6f60f8ffcd0e307cabc61d58523b1a5ecfc67778efbddb701e666970be9265341aede028b87253c1f0545eb5a1f6305be33aff2022f2d735e46c7d8c97fc8b0a37f9d605d7c252754cabe225899ecbaaa3b1ac7bcaa7fab46e5312174173232e82de20b63217395f7c0c273f944253042b85aaea9086d3b8af24d8035f1134c12d5b571e43732a8fa13788b2b3f5ad9764f77f89c053c021f03bf8041ec6e828aff2d9aba4fc26f91517f99b8f3c8bcbdee63d7c055c02ef837bb42cf585af9704ae85a56a0ff2b4abbafb69b1c77eb04d175b438bbd257cf9bcb8b80fb7baaf65f5b9fb545fc817f279b0ddadd0507959e5e6054aa5c9b25d057ca0004e96b5d02d0909c9cccd91ff9b1831524e3c1b255e3e553df15422d40fa8332a8d4a43ede1d59e6aa3e2a8be09bcfa2399adea6e64f63731fcff06869fbcf350de6df82e9cc5f01d63751a9a1d0363f55e96994c5e32d3d1a6ff4be7f5e3192141e2916c94188d46a3d1a85443a3ee6646486cf44a4a642edaa561e3f2782623ae132096d0ea99a5dcbc98a1c12c8681a2ee5c50565cdca8788182ea8059941429505a9fbde4599cf62deeafe2268ff3ff15cabf78cadbbcf637973d8cbf7e74d7abfc856e677ff3e2856f185b2f09ef81fb688a58e161f1a83c3c24ccaaee3e763faf9fd70b5c12bd1616ef81fb8aa05959d555afeacef6ea8cca4ab5c951965f5e4373ad970f4be66a8a93502077fc5f392f1c9e10123c25d210c913227942244f88e4098544aedb38ad0dd9ab3b7546a55169609617d266af57676ab58779caaf8ef2217ef2acbff8186ef2204efb0f2ff9d8ec6f78f0d7dfbc7092bfd9c17b7897c07b04de21f0aec3836f1edcf5c24760b8c80ece7a21762b5b6f60b8fa42ec76757207db3e76180aa1ed8388902579f2cd831989f0f66144c8cbb70f1b6cfbe861167f2441633d95923c2d3ef178f8574f7ef984fcebe715fc20e263f7d1f3e087111f341f497cf43ce8410f7ad0831e3cf1d49d3ae3ebcbf7013abdce10db736db9c95564d7a72df575325a9397686ea542b94992682ee5a8d3eb6e7bba4d9c7cdfab8372ddcd7173a076707b4adbd37ba0bd3bf976af2f87c9b1df26c21d261578cb58e2063fa9ee2ad1faee0d25c296f0dc371f4e9291676827415c041ef3e124b9ce90b1419e09d23f9e09adb5a3da345fe44bb5872de9bbd4a4d66b4903816d23c72e63c6388e7c1ca93220df0e5af72a84d8b6d7f7f7c6bd9707ef843b76801802dbeefefbc3ac0c6c0bdbbbf7baaf4f873b80903103443ac8cdfd40c8d0671ecfebc3b6df693cdbd267601b9981f61ca23d4b9741ba2edc01840aec81faf5557c333565b6a36e825019d1644f0781501c72b9d720f679a2a589f7f619bbf570dc81d6f47bbe10b8dddb379a33dbf24badfedb8e0e04b689622b03db402e6e1372eb908e91b0e2cbc8b719b88665ddc31d6b482f1086d724cc30a62b6551a3211c09da1e2f676af67a383939747470748ecea175d293042e0e23cb6c76d09fa147f697ec3a47d6c08ca3fd6e10eec5c5e55f1ef7de8e18448e1f080543c1100ef9847cd2138434ece909d214a429487b692fedb5388b9adca5797aa0696844129554a8d48ad532a53a2d2c3f7fb96e0a5c51d39fdbcea65253b34941c17122caa195889e9889802423a21f919d8cad09f900b189e180ada39c11153ed5c9b7d4d792eab0a43a2df634c7c58daa10d2b057e1082df614b8b7ba624f7f2eae84e396feecf24de4d9ed6a2eaef4abd5947a2928372e0d9fa43e13dfced76217d1442522d14c2422118d8844223f24928cc4924821241208e983448a8d4654b8296cd5a5a3f5870af8f573fd7ee57953124d49aaece6470b6d617e0ad6d509716767b501833521eae8c050108be510736227b86510df8f313b0edee655c421fec41f08089a131d24879c2116751d836b2bf52e8b9bd0ba094c0871a31b9752cd0c5ad2eedaf00077c072072c421b02422bba19986f2ca12ce4e110803a6443fef2a05016f2f245e12ce4e79b4236e4e9ab4236e4e6ab846ca815ca42a39016fa6843b336e4bab2272496e52d8d3cbbe881518834a2377c2ee60e0a2c1b61a1c50e02c2d32d8be64467d11b702b33c244c396ec04b6895a032fed09ed841037fe2e0dbbcea1a1d0624f3d52f4cbe059ea288137c0117176e1ec07b33e257b71e60742daf8338900b21995d518191919a131424282e6b293a037e0767493e444b58e6e227d73f7e3537a7cb963a74c43b986f20b41cc48b3f0790709490966303fc6982427cceb187376c2bc1863969c309f634cda09f338c6343961fec6a41061e004e584f4201de6c39870bc24684e5cc573fc8d3a634e49f9e212a28d6883ea26703038b14666d350b127d246606243e4992622f01177b9a7878648731202149413d53bf986d319d8e69d1d680e066b68eede9b9fb40466d1502fa7d4d339173702ea1c1747dae99d7c2301713d9c1ece0898d3e21c2d769d256c38d8268a663e339af32f67d01872537c124ee8494b88ae23d9d888bf94c7fa437fe7cf2cf7aa4ba53ae7206e6bb30409c704cd85d630cdb44c135599dc448e1a9a75c67083c8d34c510fd228742a895228558b65436f18db16eb4b1d3bca2b94c012a8aeab14eae75efaa94ea552af742355eaa14a37446b69b7db95c0d3e9e425bf2e8afc8a4ca792c8af9b4aa23238fa5247b983d4d392826d1b4265b0d451ee28af90a21e3a52d40647b941a41da9865443ee5557fe0047a39fac75305339715ffe6019f650868267878e6e6f36ed2816adba0edc959595c01248aae197061f349cc10c25108a329021d58dbf12f8938994c05f09d4c05f4dcd187a52318421e5a911192981e3ceeff7c3816f38dc2af26419769ceb4a67bbe25691e7ba795fe401434fe4b95a873b74ce98fc42aa2bf5da9ec8d38a54b8b7520fe782167b8905f7865b49397ed7189072bc40ca1921d96776d0dfbce03efb7070727e3b0320d1c5153482484d8ea3abc01c1849d0984712993932cb414847902ecbb2741558e4a5a99b2a3c9a9de823f6884111888a3cb87d7a90e6082a530fdd3cffc6c5c3e5c54ddcdf1817bccbbdd10719c57d1883ee355d492da42508543a483b480341a0f6d59e9f97d07bcd363657a3b9f3080d2cbbd06220667504d116af56af215e17c8d2d78de6343bae6430bd3ea23933240b21d57e53dc7be8bcefce69b366d3681008f4e9e9f90089e513f7678cc62a2ba4a99393c3b3b313fb80dd4e96f3a6620ada7b9fe8872deecb987d8373f3e5e24ebf2e9a7e4f3f87484fb3dc201e481c1d9647dc833c4edc6f5f0edddfa70ec956f49b4356f4165c08425f3beec998163487fb31464c99c0f9e31a512f0c4f44ebe9728a47a4baf1877b1d73fe0dce3d56bc437fbdc1615e96d160d607e9f8c33692d48157b4456e33175212a9243ec94b25d34d37498ee7e966e8c47d8e41c5ec9141e4165292dce31ae2399adba5d3740a718f635091d4422a8a6e0af15e2aaeac84d6b63867dc9234131237e6fc4cdc6b1da48fbf9c5e20b0c6c5372447931312f7b2d2536a20f9a0b604661401e011de690b22c511f720512b7a09dcae2b83edd640eda668ea976f451f5d804465d27103a3e83b8cc1fd081a7f3b40e80d0635b957d00909688e59c4ddfde232eafc7a31f8f548bf5bf16f5e5c5f9e30cb4ec61fa42763e6000e1e7b195f791c9ce55d784bce4b7e85d3de05c6ff5f5cc8908c3e484566e1c51e2cfce6ad27c08bc5cd4106071700f857d0d78b7d2105cc8b4b6e853ad343f9159e52fb185779160efbecb6d8e37ce5659ce559dee23253e17fe27ab5702f572b04f772b5425430c2b2252e58c4b42f328736e0000019172c62da17c992c864b4d37f0000c0c16d9fc3ab7db5a17db5344e965150d01ccd51429453ffcdc90f913aafacb3dee10a8b0da6524b19c9d9c8c8c8c9e99f838adacbb80a00dc861b9de5b39ffc8a0078cbbb52f8ecd5bed66fb55cb7a17da58059f9fce456b4346e24af58bc563b7133a5c5b20353199dac699b1e49969558ed64239172b26273b55862af592f9c6115df5681f9f2d9944ca69a17944aa552a974ea8102d513dc0185cf8fba53676c3593ff2132e07367767777695cd70c1964fb2c2b8bb398fcfa373a7f07e2538248f19de2638a3b8bcaa4f8981292e22f2939a5059549596b2a6e56b2ac057581d996dce22f9a63b969b3ad9cfed951dec553608f63f9cab370965fe12dffe2b3cffdeb13d7abf572b568266d68ad3041bcb884b8cdd220abbba03977f3667bcd60ffc2a9a5cc683dbf5af9a0fcea2754214ff2faab1d2e0fb2f223aeff567b36e5431ce53f4e5613d7d34a5eb31609c988888c0db1c02ab803b6be7ad7632c9adb6eca5cf98684a42fab1ef605f51a8c4a8adbfaab1dab9e7515fc40da8ac852b0295e295027538a1229458a57669179cc897eaf98030923996659cc4537666376403e2c48774867c94d029dc3e6649ee8220c44e38835dec934f7de7bef156dac8c344cc619fd03052fdbe08540f4869b6f3009518bbd8c9ba0432d923e901319b493c7eb811209341ab4d6e27863bc9b04e20e402036a478ef9dd806238c30810944306c227611bb043423ad492711a43dc8e3983f150551cc591445511445719f208b2bada8c5951a88259f42aadb9bb5f7288aa45e77ef665197cc9d6f246339e79cb328668c4912549a27eab149581a29e9ba582c0341ab4fab3311f79a71360b74f906319644af17c8be5624793dd829d829d82bd82bc8a2887378851c98cd9a082151bcd74cd391f6d138865cb78f685bb0b468b56a7fa7d309058d44fa53ad6897a79a9e49b261d7d834f60bb60b4623910e66b328076643a1539f1e7a9360b7608fe07442a13e0332b4c8f46d6e739bdbdce62ef146a2530abd3bd32904dab522de78030502ad720f3c007dd0fb003a88ae8ea0e85d5646ac9c838b91d6a493a9e4a63574122aa3bdb48a466e4273231fb550c76cf65b23258deec85b688ee4a6eadc35ab159a2395469a541a6993674737ba511445d9dc48452651e94b4e98d6d2cede91462391489483908a442a7a03abd5a9dcde60be9d3cfbf5d19e1dcf601ac7344a5f7aa2042ce9c06cf65289677434f541aaf5e3ab75284492a7bdfb1169b486981579a0a7e95392429b7dbf0e3bf1ba9c45ad474dc2af2c8ca328ea710c912171f4327511784cc8cb726f264a0f3d13a3876b1751d7a37bace1e8e859ba89e6cab21451997115b59725688fa011243a4805de2fb6a2488e5a44b1ee81296ed7b1ba541b0bd931fb15b5488e0f68f3e8e11d425c7f103dfb1dc58be6e0819de22850ad3548d42ed0b2347d6ccb1224def36a2b80f61652ac2d6c50e6008dcc39291a4b926f440f3d6ba0c53aa4e53063f1c66083b01736881cfd92ba1d37186e39de3b96f78eed25491d29e8662af8589665399220bd17b0b787e2102c2b553a4b7b679a3bee300d431e79244127aa5d450e5d0f15a6e9a659fe6d41e835cdf3baacdc1a498d059e587c82984f9d8a22899e621e4950696af477f869db6263c1676fd0066d0f8c984d02eba20dee6e1c6730a2b706f63198f7de7c7316af98af0e1558dfdc82d5175fdc896d66d902f2ed39672de290d51e6ee6b0dd1e18cda16f8aa1b9869724c78ce6cec7214dfb689620adb5d6dbcd917452c457d43afcabf3db902c08f75d3716b628e622945ea4d1d8dc7b1c6b1c0b6409db344bcc396bcf7ad43e3930cdd4a8ef4bef6e13f0157d5a0cc399f69c039ba30bc49207c578c7bb4330def1ee997b3711f7661d38c61b04e1fe40b877580a381c1ddf28701b521dabbdae862d619b532cbb9e4f36a4a1ebce1a69e86218865809586c4d3f642de6ac473263f29918731eb3e72b93d71a5aeb2c8a3b6618bba62815a521d1cd019748a234a402e33027ccb94b2c61824db1241691eaee12b9abc41229130ae526944e557ed315ca5b2d3f5fee25cc611ebefac7fc49df1a159b01840cec333ae95b27a305814c27579d3a80d337e977d5a9e62ab3c9eb201dc8d57b49dfa76aae32e3ead735c84d1e3e7a38f9da55e3e8e1a48fabe89792eb0c51e5e17bfdc1c3c7f587986f10e901731d7313ca770ce6bef2bdead6ff7bdd4bf7d25fd781b6bc25e91a54fabe4ebaf67555fa7edc92a055b7f219b92d5d27ee57b9be5ebeffa57d6bbd7de5da5d5b6f976fd3ef8afa5d5519945f2fdbf387d35b66cb5b286fb5fcd4c968efb882f4559d344dbf280e286f7988ca98de5a75a79badd79d6eba69be0e0f208ff7d3f63a483bcbcdffe1f4d345c771fde174542b95da5691239b71b8114d657d85f29ad9604568541abc90c4f5ea0ceefde441dce443660f23718db444642abbfa10908f184c5567445eebcba7ee5c64c4b5834c0643e63222c145e5c50643aa33342958abba5341320223c286c0c0a8522698120906266c572a9a15c020075d7082e78916ec70cad3072d1679461a36a7fc953a98c5b65227fef2addcd1c1b612b884d0566621eb30737ae9f83cbddcc9b7b204625b596621b495c13b825086ddfc6b447882bd0ce69b4996953ed87677985d4368bb44846c3699b92a68b15f1e56c16e926f261234a01dc808d00b5aec1a64ee40e66ed3e89dd67b976fb81462efb00d875a5f23c2db3982f3824eb3247d40d3badabbcba9549ae61d3e9403137387433e73d7e274b731c8087914712e61adb50d4e8b5b90a8cdfcb72d7144ad0267b20be9cdeb0d675abcf792e4bda90fec6024b598c9518b2b089831fe81fe470e7b92fb6b7fb9d69209d65aa95e6908c8074b9589c07ec66e47eea8d729253dccb313e4687533e27924c6b231d6cd91d248266f8b8dfd72cb844bee5a4b2f9a7eb73e5d7d49753a43764cdd3bfab025b95635e72cea52c4068fa7475346838666244f36cfe4dd6ef4efe487e8ecf00075fc727260f6c7c7279b4699843aa54a51a82a6bc53dad154a754a85807ce0484dad952a04e483a5ca444699843aa5423073d53aa948a89369e7940a554fe4e0092c994aad952a0484843aa5461935ad54a34c429d52ad35074dd924017111fe8d208fc393d145a33c6691699457232334d8a6c7923edca6bf01f13c02315d286e4f0fca34caa5284ca35c8a42fdb035b556aa10908f5126a14e29d6df564476318bf50112a232b55624d40987da848e0e2b474ece25ef252f199af2cb7743c4e1cac14bde4b5e3244b56a994699843a95a2488dbfdf78c97bc94b92dcf7bf4aa6d64afcdd1bd7e73b995a2b55c8289350a9d39a7d48a6120f46d9547a52699d21c3547a39f9639bfa50f55907ebfa14cbb118ebbaee8aa139919b1f1fa6bc7212ea946a4571529d541b04cd954c79f4426c97959511359ab2e96f586ffd4dccf7fb783127cb749a607da54a818038e9c34feb0d889beccf30218694d630248405817c0a02f20112338d7206d246a234dd4ef03c718a2bd6e1046916310e77d7665d23e79407a0750c65a27b04d166bf32d643af3a9c89ae225e16e79bb1a8b18602e7e280746c85163b768c936f3edae41b76bd5e51ff40d42e2c6a2626c02d9062d8b2df8595ca5924c58fb98fa5b205add45cc296ec5acc23b95ef4667a78ff6cd97c33d75a5803010e4cbab3c09b4c99b02e58e410b66417ccae64c74790f792175496aa90130bf2f16ac15c28d6fa0e723a9d4e3115b552858484a842d657338eca880412e590c88be6522ad66815ce52371065f7acae72ce7eefca84e6526e9ece1ad4f47a27144a031b3c1e2ae58143838626f6fa60adefaa152fcfe49d4be57ff2d693f8c7cbfcf522be7ecaff51cefa115fc5602e10350a950c758a81a8aad5ca056bbd3e58eb7b8bb55a43542b95e7c3510790a5ba56417f7d84b0d657552bdfbcf485f3fa58b50d4b5dadda86b5beabaa958aa5e29c130e4fb2c362e1c51ea4af1ed4e6d38ae6522cd54a5d85acac57552b1b084bb55affd767fdfdf1f151592cd54a5ded10c45617acf5faf0075157ab98ffca5255ab57eb631f6162f24738b1b37372b2f29ad0d1b9e4bde425fdeff510d6aaaa56e426b7ff06c125ef252fe9bf7ea851b056638e9c9cf192f7929784c2e7e7e2b8bf7d046b7d55b5f2cd2be2de50b93e15dfdeb5c1e30d81595f61adac5656966aa5b2b0ceb2a0b9d4fabf7e88ea51a858beda39b8d0d9ebfffa2c5225d7ff88c158ebfbcba5aa56adec1783543e15a63eeab0542b75b541c052add4d506014af561ea3a2a5f5464400e5e1e8077b5fc865bb1b174204000047083a9d4527c7d9a49490bca896bf66269392ec6d281000110c04d3c5fafc368eed31c51dc44b3bb7992296e864cff317cfd09f8ec7b70da4bc04b1e026ef211f096e7c15d15709594a780a3acb458622f54cd864ad53210281d2abcd8e3e404cdb14a50351bea74ba5a39bbbf18b2d9d7a7652f7956c8666f790f67390dd9fc3ba4dc44b556985ca5444ea7906a065341a1d2d50ca6924a89d4b3f639b7fd8a594fa9542a954aa71e2850aa1de8155a35e2326b24b3c261cfc26d2b8f4be1250fe3b457e1b31357ebf572b5d647bd5c2d1529605e502b7028b485892335ad4f3329694179cdfc75b26273b55862af994a8d6652d282f2b09bd27c4573266edeb405c54dd8397b132f7994fd0258dbd7fffdb93eeb6e878282e2230a8a876d8ba7d05ccb1aa6281e82d282e2282ba681694c4c4c4ca88c8989872dcd4b688eb686a9897f98d04c5654c664c536783c5c837b30d87afebb9facd85c2d96d86b0653a9a5cc6826252d285b05eea3bb8f6b584265dcc9ec1eb6ab93d0dcba86a93b88af0e43657cbd50c0d0dbe36aadff9a651b29ac256b67ebaa0705e93f037cfd06f8ec17e0b45780973c02dce413e02d7f002f80c3fe85aba4bc031ce5e5800c3480010b50400210700003640e14e07138cbb5776d9056884bb33093b5e469af996ce7025f1fb6aed6fa37d7e71b01396daf19ac467e5e477c2581ad3ff33d3372a23ccb533e050072089f52a8940b9d296aa8d00c000000000473170000180c06044302491426699e4f791480165b8e72564e99c9c4c1248861184621648c218610420018223042404326000db8d5d6f9f67656607b416ca7a32052c84d35f7a34daf93ef6d18a4cb36c5327ae7ebcf67400c79715e366a7ddb093ac34b01fcee31e8ea8022cd4839121a519b812aae1beb67c303c4cecbcd12f5fc8c29d4f3e763fe4b8353db88e6fdedfbc8a22422f37cbdbe6af63243bd76b3b0a6e9698870e4d4750c41acab75cd931d4c23c7525c4c9821f4f92a7d41745df1f219f0eef3391cde352658ae678dd3cd55e4f8fb0953cedb48559080bbc070d8a58ede4223d6ffa188e1c01741dae8bd14ee4dcf9fc3783775ed3fb1e7175999ce91f9ac6bb3bab0729014fdd88386c11e8b7efe339485aad891db17599b7654758203e144142642ded4da6739c1271781ef8555bfb5a51e153e7be5413c6f2303fbf4858a35c31acfe3110abdc0c074db9bb83cebcd3d38683e723d4334f7b5421578cf759fd3e59365a6a4a40c4411fe77ff151c13fd26fb1e46ffccb7c401801738a2c58d7a93855f608ef65c858ab3186a8e106796d54bc59313da50caad66097654891e249769d1cdaa1b2cbd9e5039a86ea95db98f458dde43147cbfa0e1403ffc4844894ff0777fb31a62716810e201d40e01ba73adda153832424177a7436e27a1507f7819b34aeebd140d62f7c1577563a8ac0c474a02cad4f2f6161d7a893618796459a22d4077d3b131e477cf4e663d07e6f37512d33e078277f90e1b4732a39cc9084312a81a95c902ea0593c72608244443433f389874b01fb5b05423a21199472432d2af5fb4dabc71264610becf0b1c0d6b85a0eabbab40b03d91877e302484de4e29fdfdea1e3d1f396817ffcae7585c74cfff5d23a662cf76ebd6f04be9f3c518aa1ae8dd9ecdc0d00e32a1ebb3732eb04a372739dd6c91711bdc2ec12fe3edf37dc17e69c6c90fa5008a30996c5b57d7afa209fb65e2c1299536267a410479253aa51f6811bb0af30efaf989a5650503d081458bca46a98486d7d6d2e12e4a41ad0f3303909effb1c67ea94cc001e4af20e84095e2e624cc3fe800f29180608823265d0b2fcc0f3e99fdee0e607e3ea478ffe06b790e60b2f0480f4873dc5e6faad69077ff74cf750760ec2fd001548c2e51a302c01a710e202e346000ca50cc95fda59c03dc24502cac83b7c03c5c749ce0750e303d064469fceb4b9559c7b95c0d58af2c886d1d8633ffeae80007139c3a8035815477c590b0583dad734bf8f0f34e72836148f200b41673e9b6925649c333ffa5f301d48110ac02900000848b2fac7a7bbe60ac4ce7fa5765d09be8f90024526e916a7e9d50b8e22f85140763c87a95ea0d45629520bbd1763c0e51640fa0c06f8cf6052df4de3073020244cb0e3aef0fb93fb7eb3c9a72bd8ab4ec6d0c245ba6bb689cee87b081e804891319f3c461bfaee6ae4b2722ff013a5fc0098d1355e17cc524c10e04106816af160f30b27302c1dbbd1b98c7a820c066abd533ca9b9b65d0a8080142788a0d02ec6643726b254f07c3dc8ea65373181f420071de06c231270420088e2664257c485a7c4a0a04a2a19b2f35a32d497bc88cd03774bffc43001dbe364c12a23ac8f832597c818b08a03edd8665b5f088ebad1f5b9f0063f49608980b2f065d14010abfdbbb11140193d4a3056fe1abe0b5ef04cd637e4580f5fbd6a52e0206430cd437371701d36705fed3b7d383f728643b5784205a62ec689241b7b4d96e20a5edf293f36d20fc198eb184d4c8997b0488269710672b5419c73f7590b87a04bc8f9589e0e7b3c038edf19000a28bc46a24205adcd6e336d47ac118b3662400a70a5c433b4b13930410b62de849b4c0e8b2cb60075ac656f492f8b3d09bfeab51acf2c842227fb75cead22b3242837012400a45d143ec88807ab2da819bd24455369077f422ff2754cb12304307ddc42cdaca65435230292eaa1fa21a8b240a69cad6e82c01fca4f59e7c2b6cfdc0367133d42ab5e95ba3bd04f093d6f6f22d24eb17dae464f59e3623c4ff3d3d972e0240b5beba3c2660cbe049a3ccb4bf4f866bbfaf40879835a742c15f4cc9d0918f3f9882612bb81ddbb49821dfdfe5748150e96c2c0fcc315a59421ba009f0eb3ce0f167c4750166c9b2c382876ac40e71ff2e8ecb225eb42bd526c0fc1cd629c46b556c0065eb88eb1804b288398a9f25154ada5227e9750a8c64b89c0063e7d86b7cde72028881bc60bf75024ea79d77f0f10d7b02a0e18ab1f1fd0464824afe8da5fa4001c4e16a274201041ad04d3914405d2fc65ce5786a788beab63fef7f92862fc50b172a31afc431dcad46012173a01552c0fc3f294d0a58f1fa783638ce3cc8f77b44c7f73dbfd2a8a8000b0fd8db0b7c88d327c6a2021058c2fb83fe313339706736734f4e05d8e013c29e7e049a006c3c15e08cd8f11c668e06bc98b5ec04386d6ec11af0ca2115b00af0dea181d448d7bc94c7c7781ed43c60b29ae1005d1eaeeef77024016e8ad772f7af987b388dbda042eabc9bc004e18aa99988aa8e974cb7e8fbe9b074ed56c0e689393ea8544bd8866aaafd890826d1153075bc356589c545a72b60943cc4442781ea733c27c8dc757f239b23af2d546fb2e0bb9ef810352346a05e015200d26b5dca46aea7cf34bc0012fb82bd00ddaf23a34cea1720e7d9ba2964f52c2cdc351258e80a05276feba53e6d9b80c65f0a3c04d5b167cc199c5ff7a57dd935e7e0273ced8646ed8f7e1a432a98d2c94cfe59150160372b3aa3123105820ec6b76d30af6ec606caba25dd81ed2fdd06a3188e8c926280b464835aaf7db0b36fa02b90a9796a76d26ecdd9382545a9c20bcb8d4614419b77d06a4b41346e3c8236d6bcaf7fbc145ad686d37cc2de696b72f37e919241ed72dbe604d1f60077ad29b460214459a1a670777bab3dc5000a734df5738a5a3b5a5371cb836786d2aa5a9c62136351cab26da0b748718c1d765823f6a1ad25a917b54bc781d075c61aa900f7b0ae26a95d4e00f1b76835ba9af3851f95697692f01a81c543c352bb3437a7d7ccc37edba38b2156c75ab9ce832eb007e949261eeb3b923cbabc6099c32a846e630c7be50961e016e9d09699f63ebbae8110aed54eca663f0ce4e7205ff2df56c9f8be22fd9a34d20ef93e645ab14b9bfaf99fa23969bc5352bc4965d28c7a2495c8d98f617cf321c9c0b6c87b02a14cdf2962ce92b021ed5dd6f509fe78b39c95305ebbc4d93f616f551322e676374567ed72094ab1089d3c9a2705312940743c25ffeb984e4a4ae49a112807f0ea2354d8502d1577403a1e267ba873e50248d8b07319a546affc17da22050c5497e953a5daec86a0485c16ba76e91ea3a5ee717bba0d934c1f3a7dac583caae27b9218cb5a5f775b1e8ae6fde2ee1be2610b7fe1a81c6988011de2216643675355cdc34077b4bf63454ecba5e96b3acc96b9a593621026242ca1c2680efb0f5e3693572e4ea9bda024eeff8141fc6ab5fe718bfd3f4fbe39624a4de8b0c5e18c2e785a8e91cb65428a2a03465829e8b6398153a95072bbf9ff89205ce9838f679a4c2c9b6678227dded18fd99c45c7f8ffdf8dfc76fdfff7a64e893bff2ae5ec57fcb15562dbcbcdcf43fc00c05201844545dd8dbdb94181725100a0060f00c9e70fb5e74019b1e5c1d19d5ebc9621e39e3f3c00e8d3b9850064f259931ee3259e6b2d1521527343baa9cd58442a068a34962ede22dc13baec0f5aa9cd98f8c69054ca9e2d00fa57130c3d27d40d7eb6476ae14d20d0f2173860bb9a06c0a32ef227bc03a07c44cf1cf569a5c46b7987350e7ec42d818f90e95a57146500a08f635cb59b9b3f40d57510f5d0f0b20a955a03a0b6ce1d5f7000f4a1d7d3826c1eb24c07dfb6d0150e007aaa96e1c2a2c60e005da0ef6af597e61f4ae4efa773bc8943765c3e9e1db7744abe34830dfe8c5b97752901a0ffd8395063e8e036c9428ade3701181d2ff7adfbf968fd223d51eaf5612efa082800040263079d824f0168d7c0491710a254c6ae2d422b00b686b4583519269f05a0c98fbd930d85b4fe355e699b057ea9fb9ff102de4ca5215e2c59da2ff0a834b223cd7d998a034236039ffbc6b1d49868002cb4199e38cf9435008bc0cd6a080488759e380046a30a5df85b28b346078064342c0feba00a6c118184464080904d77e395af713789ac9489a3b2164d3d43ec29d9815cff3c002d7735f30110021aa4af5c19b0490028a205177053db3fa40d6c610ba58c76c41fa5a2e04b8a96288c15840092f01c27471a345aadeec2de2fb0cb96e64400d4f81ea32f051a554f77a0d62fb412c09a36ed8c50efe7f452540f482be18c561ac46dacb659444700e8091beb3a2c31c49879c55148e62967cb065b8d6785bd9fdb71915f64542561a520d84900fbf60100913a40725b6c7b77be9f8c15f07e6f3717c204c01fb2c3276cb5e353b7fff9ba634148def5a42309051ed9ffca4af69e913824b845d10980c41f5c380f85fd7aed33275f5df69d00f27adae54c8c058c2f9ec31a2880fccb198d02201c444f370540e9ac659b42cb6ca2ffbdb8eb97d08262e7d619cd0c54df2bd0620d8d512359006f38b5008493a7c0e20280a77ba110f82e80094349d917c002a78f89d0a644cf4c1ad481cc258900a39bac9b015065c5c33b8085d8deb36801711c50769b1b894b390de49706807ed36426c07300e3168ff035fd2001df49895cc89d29f3e3c90c68563282b23ae91506d7001e348e710a5791f03a7bd1531ea4edb072ae449038fc1b0079f6625b9cd168e200c64d9fcaa967419813b92bdd9d2c824cfbc558be82af690a8d9ffa7200b60a3af737cf202c07501c02750073b90d413004089abe953b02ecc00b230158367d4666287be640f7cbb4ebea430930266d80b87dd2549949000afcac4b53006c6357ba08e70b8a59d64069ed42138684197318c6208f152f2f45836b5f736dd79c096084e1a17d0f020815df4c80157850a1635d868587700c678f1923846dd4e42a184e828809d28010c7c81e71e5280640c0ce23a772007ca1015865a88eb6ebd3e5001ffe4a510ee06a62906321498f2f00dfe18464278cdd60327a8e65f07fcdc6359d4266199d49079a5fc40a90f62ffe165d83c01d73cb36627d83ea6a1ca2673a4e49a1669a11424abecb5d2102d4b64b486a2924baab81e2a3a1b3dd46a6fa9b811c03688f7de64ea32102d44dc65ac404cf0da4a8b4db5e829e94c8df87317e23401f7197d61408f549d67f89dd5b9a9096a4dfc3841d91cb405fabe83793005776dd78c2a48a306f3e8f63e3941c1380e651219e858d25252543e97488f7d03754639dc68ef40cf2eda1245960060e9771f07b0be8788ca530b54d42578b629013c02ae249abb3822bd11cbe106aa70e9e00e905aea08a22a1eff13866d03c9aa7a57d8ba00856e4a4d83263d8c31ecc1360856692ec6c146b0f0aeadb290d7e027c94b0c81f6f09bd86565426aa50806d07a8c51df5b8405c72466bb715cba200ffe95ba89fe2f0768686fe82536e1aa68fd095f14e0cff916e6f97a770718a12f61c687e42cab570e172b2e1a1003dac09c952365de7b5b65c72cde4da40bc2e444dfb39e0433e85b679b663f028586e51ae079d155e3051ad4e4d99865e584ea52895c10592a596e51de5b2d3c6526a0617980f5acad6923822bbe98f08bcec651b40a5307304297e2bd47a121d8c40b541060bff55a88a8a8838a2f5d214ddaf9e6ddcb0b302686f746104be15ea1184c0f50a800f170e4fb56dff004a4d3513261da4c1375e0e13e58a852473220bc0e9aedf10e90958d87df4327145bc5ea703fd21e3bde69a28641a995c24d466133dc9160d91831ce3dc41b0a4c8ec43b183a4c159d08a65505d3f52e4ed5a928a582ab5585839c400ecf66300f061de8a3200e38f8c843079f6e118e1647fe6cd00fae52bbae444bb07257f68ca429e02d08cad40037037b08651ff474781d83f8102e01d0e52987ca6deb941b2cdd80076b0ab9bbd045213b001c8fa963155baf79ce9b1759fcd920c366c00ad6e27e1addb00d04f7648eb0e93c94d9513f208d7aece008cd0ca37122a6f0023bb8811b5aab5dae541991028e73a00ec8cb01da451d920363e214bc9185965fc8d0515599333234bf1e0c81a6e20998a6f4906c127251e651502e5f22b7762403d536430aaa08053772cc00a0cddc6df1c808882feeadd83f7ce346c95aad00d157d8aa25e5875057627bd427faee4cccc01705bdf5358dbf9bcd2131c6cd57ff84cd20180509647e362252ebf66f782f88cb8adc92c43fbecdbc15a26c0090584dc87cfd2cc27b1358231cd533ebb8179276628b265c56f88eb8e07f031d64629bbcda18838f900e8073c8d5da6a873e54847a6a5c06a50dd49730ac50f1b710a281e1522913c4713f1cd74743250efe70388eb59de8e16448aa5a5ca9091c4713a63cd3a82b4492b099ad3e75624377d2c7f00f5a4e677de09cf4377df73e3c01fc0fe8cf18f3f80a0541808d71f809a61c2e43af3383b71f2d250265f8973a9532e488ca20c1b5bb7f9cca7ddfe00a82226d4bb6c923baa5566325ceb8cbe0e890587773a88030c023c3a8026b3350e1cfbdb988800bda427144038c7aa2a007ed56aa6b33b8dff2c2d9ffed9629d04934dc5e20dfc1292e6c04200b3b2a795de21fa4e9e0ac95c87002d8775fe09206e0703fc6b19e8a5c91ea33ba346ce2e180c493e6cd9201748d60e8f46e1e0a13467f9e004e9994aac9b2bb11a8e3e77b612330288481e089909d17262aaa94fb3b47f828e2f9cb78f4e27572bec8136a583afad22fad686a3a4d48e075bfe94fa11921ceeb50bf0d624aa5ddbe1c51157b7ec68e49f41a91379223e701ed85278dacc71d10fa2c637d6a678c327e87d72f521a3e8466602e5a3ca2177588c00aa39823af08cb1e4c1b1022cfa551f990310c605634907b0dc85d8cad6d8543e0fd049f10140d7486f5cf4bbdd78ca39c3e591e5cd3bd1acc77b09c368c8f001f07e673cfd99e7dae9fa83b658bebab890acd6f5677909f155fe85f991779b14d5d1646653b1b35f52d4dec93c05a3d964e37ecd49c6100102807b22eb06896b6fa60beaaf30e5387bfc5e8fe3e7119531b7a414c649415ad00628c81f664c891818554e114fe9a56b640830a0a13ba3c7359314e81e0011fa63f85ab2082180bb43ea78298818f7fe67ba792ea3e71bd4f494003f9cfd68d46fde5895107b118e94c27dbbe771fe725bd3a61316dea24004cf2e8f5c086074c1f35620d163224050695e6d1624f02d0224237c5f2f1bc4d42aedcc4cdccef725c0e7798c9f63e2a5a50e9111e055f6940d5dd14b2d9064763df4485632186af2f84889d5b09f66c2da5f8d2bedf34d0eab7f9a02048544ba56d6f89d01c4e9e237c52140649a1c35144d00bad079b24c854501e87f84a500f64f97bc9f2a942b94a15f552d7e720a504335d6b7f048599319c398c3dac7b2515cc17f09b3774b6d9d78e03cb7dc38cc670d63aec8e7f28fc8b8c2e4a9f9cd4c0e8b415680e923dc30a65fafaae26be0ef622632e4a40db17434737f2d41a1aecdd0e261bf62bfc5618c4a930d944a3258ad6c80756f8f06cd0a8e888292a2f9f9b467050cac57a2ebfa76e25add0e8b0b22f6f11ed37d16200ded8e7b36c1eb5515975e4aec5502e502a44c20bc95d206575d00fe656bc20bb0aea509bf7c60e7a438edc1e64e99ca0af8a37a8d8265972fd523619bdd324e3114d72bb1bfae079c18a37b0a0797c6479f12b8fc0be29a199ea234152187885188d19cc937e87b800a1940f81f07bd12d98aaf855c32bd51de7c765508818d6a04035ba9ef5280a00d441b4c8fed89311ac34cfcdedf14c5e5a452f82f5458f3e84714327618706452a9e46000a52bb85e52263b12e832eaf923370ec5e4e67dbd4d98a045df52d78b58a4ce05426b656135dfe7e4da0cd2558cd1102a50f47bca98aaf09da3898d251b2a0fc34a38b5d00e753d069866085f32407b90596e02f4d618b97de6b05d0618ed38dc98d7853aad26efcc0368005aa16846c14533801b6403d9b112c87b092079a8fa11ac95536bba8e099e1c635d97c5241f33fbd20044479bbdf2dbccdecb2335f0d75da801486846da98f59c8e82e113d8810236c02a1a99cca3ba71ea15847d4e7aeea717afeabd4b82fdb4ae6d3e4fe8bf83f84c0b70359de7d1bdf1c757fdf3bd669127a5a1062db57bfb6c52078455a6efa0bbba3eaf3502392bf1556fabc29166113b44841cec326b65346763a3b612a1398881887567c23f6ae50f5e6449cd7f9b5ac637b2a47e766a0517a9793f6dab297659c45fc0676b3ceb2d20f0284baa1749ab48073377049dfa13fba904e2db6b42e018c780e6617e4c874f10d195ccff98861c7c8c2d0b4c0e24c86d3578cce10012a7138a5830253e82e95d9351a9696f15b9186d041e23e3d7f12698669e9ddb59e08856089a5a9ed827056dc5b8ba4a0df04de6a43faa3f4fe378f8d8070215dddaf3077f91d6c06ef0b15a55fb207d67d2567ec4cc6dfc54b80a647006bc4f45484399647c4219467f0547124b7028d34b32de923490f97cec10ca826f4cf9b3254a06854b5451ad2e11d9fd298e26a409e600f2617b18614853c3cdbc80f790b78834e22f172be76709e575d63bfcc4a3c4a1559bf1f7b2e694174dd14d94e90f7376c69adda952d7bb05c1d8318fb746b80b95a4e4149d1b37ea833d6e26f49fc36beeaf4ec0599e8ed64a77b19cb264424d46e9962b2f4f11613c9c54e0a03be238b780ea3ee282d48c3a13f8a0e802879700740eb62833a73dc33383a5757d978f978efdaea85aaf1fdacc787e1bd0558bbad57aca56a927a1cd647ba4fee85f582cdcebe058aec1c1a49b92baa71e67bca6fb321b6e65d6667668cd3e750f2f813de46c3304c6695d52c0aa4cd862550930b9178545afb470cec4e9357da8f92391d7683cc115f93b19989bb9fe5202afc2673324bf8665b366d22c39e15367ff577910aa2ccef356f3c56e5a9811708eb15382f5a423d0b998f1dea5669f6cd7b4059d30fc5e81ed85c6969375dbbf7837309d3166e63330813ac9a77bce13e4987e9fc68c49bd40033b8c403b9100778d02c1b9fb2cb41fcfcdb246b37592b2d8a1cbd33ea51ff71b526110dd72b1056900e6c0466786b794c7ba71f0e185ad7be28033db9075b9d65b4437d1660ad793cea0299f647d046b8eb29819d4a019b3cc41516c198e54651df3bc07db6fea1bb2280ad1f9843b9830babb4631ed77191d75b46b05e85d73952743ae774d57298f0f24719672a1b36a00e65b7519b222d8f58a44b0730ed8cd8cc3918ff93ae942b48edccc54959f30ad2dadbbe027d2ddc8e5765f1ea0c331c508194f9c9924a0215620b014fa49c2d8f3a49a34c660a8db4fc019b10dec77ed9a05a9d727aeb9b74137a0dacf97d1701ec391774103019cb3242b9284c2f6bb86f4d52f6e872fc32a1efe45231123d59761a947d438b949b6c2ceba42bd6217d58ff8909f701acee2a79acc416146f37ba06c7dcdd52c1516d36b701466c898114d45e8d10e509801aec3eeafb9d871faac75d275ab71c0641e418c8c593f77a5bcc5448d03723bccb4f03a285c84e7afeff5c498d17ea931ea672e023e2ad75f18fdd74c0f6c6a8fd39a31ee5142076ccccc5445a39199f13ce69bb9f83e363cebb4e18a2364fe944ef2e2fa7c6918e69c93302b0531a36bd26a63da90811900ad76625e0d7571b221d5e7b2c1a64d825e4c4fdfe401274e970df62e16a267ecd9342f385da597223a35b605145b67899b97aa395f8ee7f6054fe90eda4b524990ea9b6289d58ed63a0cddc772b8ff793bce4a7b9cf8100b5fc8649c69036c19ca8cd7ff8302c33b72b80e1da04328b5c13ce5da30d94a6e0efdddf313048d3a60f1f3dfb345104930c25acc199b4788e8f5fda6c9cc232b0b8568c3d6a9e7eea0e759b4b518c7f6ca074c19f025af71dffb125a47fc47b8d08e431895bf9671feff0da0160d5374f1ae470a0ab03444c63d400b6f37aab89abcc898ead754b5b73b6c142c7e99b2589a8a572306bc84f51234f6dba88bb6f750b7e1099b33b76026f321661b58e4fb164a94a0f2b9c5cdd816b3982faee4ff005368768b711351a41e1a1d600b5db298bc943b305466e568fad69bf85a779de7d160ed3f0c5cbaa001d92ee54e632c93b84309ee5bf4d77032fdd26f413b155f18a049621a01bcbebd952683bd74acd593d9de0617bd39122e1c2618594fb65fad6aafc205daad8634315cb090de27ed35b8ddb235c250ca1717ef1e2eaec38b4060c261730a2556888c0b79b6e60a6624329072e382233af7a1622bcc42c3b9aed1a69affb312d4d57ea00933ce372b2a404360ea037ad1ec4eb940f8a672e9ada597cd51cd857c9c93c46c02692c046f2e48e92b86df12f5092909adde75c1cf85d0da03ec3b21f2de347386188b2051185a5c2a646e47175a357bab87e011b1fcbad0ca42d11ed57c67633e3172a54e843c6cd851946a5521953dffc1adc10b5617038d7fe57398215d684140418efae0bfba581789067e695d8447057b190c4c3f3192df495fd775713f32e3adc1d50a5bea0dd10f50a671d9a0c2caa6af3ec2f84154f7b39eb623fb09c4948807a1591b8bbe6322f930cdb3d460d545e3ebafd0d8897b897b089e6935c727f0cf12f4b4ddb33a0f7a3e6027aae7ddc2768408a20d8b3d6b9eeb06dc7bb99add8580f15252f8d8e8ea1f56811850cc766b57b195f09c20ad58e52f1a1bfb11db496574c42ea47ac765404bbeafe6b7e40861e078244e1b79ea09ff1ac8e0cc1b92c7620147f6a3c2c4aafa491cbc72ee889da68b1650d97ef65242db6b08874bd81419d1fd087a6c97b54d8ddd7ff37be79da27076a3909012543190a94fc9cb5a6d97b77c3606c4edc2b651f5b313c5f90454137d1e240ca857c2501d3374a2f8192b8af563da9016397cbf00a459d8a7f9518d45b6f68fac2fa74f68ac4585dcf544d5f1824aee65061014ad8be3ecbaa8de6c403f5c5dd54c026434bf0b01ef984fef42c858d49a9cbc4588dc9b08957377eb67de318d67b86bb3d0a3b7a22b596c5a8e61c276d11ecedcddb68bbcaf363b9692319aafb0b14cdfd6e7ffab1963a7b4467a0c265e18d27a71587e8b634d3e4fbb1f00e9102c6186187c09ba29a1428942f4e78c767b9419eca23fd1d2da0671124c8fdc8eb54c5e6cf42a5d9a6eef1109558d69b7dfccadeb2f12a3295ae11fb5458260ce8bcd933bbc1fed8b56f2ab0858a727c5b0c3a9cfb09f379bc00ef8fc047502e8a6ea0be534e4bac90e16ae4a83e46d469a301195f86e3fdd85b6ae6aaf77110dc0f373c8eb04dffc17a44b71304463c5c26dc973752290791fe3605cff352fec129e2716d4b568fd4b96a4279dd2d467bfe6c557e03fcfa4b290c63fa963835c6ed0c482dbafc9178b50cc5a5af57baa0c747f25ac42f2794a75e1bc1808483292fe3f74b3a00b83cd4a21a921dcbaf5e219d4e168f60cb70954d3675805817f94118961c2409c88228be00ff867cdf74c91423e06b9e95d50f742dd0c0db7ec013dee324449cca240e8ce096b0be52f7d899f31f07f8dd144efef7c213d2881b9ad76beb87b8e7bfc5f74bb3271dc77d2208c456686be98c4371314f6e23c1240420d8ebc9389fbe263997e58f92014fb8f82256dc29d4e53885f8094e6d4e261e70b6ae4ebaa413931119318a9e05a269e2c9c84618014b5fff541a8653b3f368f506eae4cc002dc796d0648b556fb3bc2e9cd233fd0b18a0ec29a43e80d6d0f97a0b7e3fe920613ea6f410d6ee604dc18731e5e8f4e4dbf7e2e0260f592a4492a1c4a60a10ba2187844d34308baf1bfd28f2ec665a0a527a30b76fb2786ef2e0e7427a27c89556a9dc8d14451d0e23d85c9cacfb7afd06a70795464e56cc21f4a45a856e24ef975da429ee5792a9287ec278a36aee84a1abd354215f9646456b97ba9dc95514e08417a9de3b4ce97a6b681e47ad6c0a2b71137d5c4b52c90d00a64ee9fee00a6e00830006e4744a9150cb77cb89c7fc28290ff20c3fc5eb4bd8e1d6fbcc6bd8fc9e0a27df858f9814b74dc4ca0f6f776280cdc3ed4a601566e3e4ade6d5f745705bd634193e0f6cd75e5d9cd510d27526535702447a1d61b9b4832808796cd10ef55d9bdd6d5e0c81157552ba59870910e12a3c391c78a08c4b601cd44cc2e2d3c1d5a82794278a0796288ead8559438d5c6fdafe2a5a576ccef93c0486fefa657738cdb52e7abb729bbc991594b7448a0076cec4939a2c63ed0cd27269768508c2c21196f2a89ca1cb9a94bb6dba8a8bf62ccffe6d8871afa20a194dce6d3438899217e63b4b94295d979a8b3a993d1f2baaa78e5f7162f32dd85d6acfda5c09e399e571f835b0a58aa6ab115b8594b6c1f97ad6415fe43c2da72b248b3510d4bfca5ae7339dab4781154824a8a2ab447deb048c630ba5961f671ea5ed071c7faab6aa4725f644090e873f6f524ac875146251e2a9c9f29312b8ff1c97265bd94f8002cb342badff7006e5959b87924a6327da24b8dd29930e5d4b1164d3bf40dd89ad7ef72d8af0bead097a825de7e435f91c05b6356429059619fa42a911019fa9b29c5c95cded5080f425effe5d2b0de14c8d057ed9facc59cc3874cb2fd4564c51e065e26a611c127e9c77ea94b622ab3124d398b67ed7f6621bb244fbb06a44ceec1ec93e1dc3492c34ebfb669a5e3d697056f9d9d7e4f65565ed2c4ee308f79a92188b0a14b02831210c0bcccc1320828780ea529b3820829395599b823232ea375032805f79148b51f18853c7f8eb11f18ad7717666b1752e17e3924fd2cb2517c13d4a5cc0a49611c38eeec2b21769ba11ab37fccaec7322b03516863959485cb206ab4214914d8a6c9955959b2f4243b64c96fa3c3bf2c926765bccc0a75858600650467628ec26599930c1029ef1207bb67c1b5c522e71527cb658b94b9fb2ae8c11cad0842a599439459913991ee2af07d3d200d2bb1ee0342e13029048d63f0d454bdf33dd66f99deae8c64e2cd6109a2e439a4e3bfd6d6aaf86d1930a851f2626211a44e975949ea1a8fdd0c67b80642d93b2688d0e54a1c1a6a71b4cc0ab7cac3a93d664566ed4cc839e2c8981531123094e031bc54584c0614cff937abb54a4d56743ff12895b016c731991539877f18a9ff21d832bd364a6dd60dec8296ed020a55367f1deadf708159044b065c189cb08f27c70cc8d53b18a044a8477d332b5a8679060e50813cd3abd53d6ce3d28b23080a17744c174f55e6f69095c0034235b8e414be665f83b26e4ea475ff67783b1c6e2edfc7f5c5d8949209898f7bf41f455c446364535f7c50a508c726f662899e492cbbd88674865b890b2f61ae4abc7f8d6ed1ce9601f3b32cbc794d2ea28e0cc213f98206fa59cdc3cd20d3ed000b13ec4f10013771fe495718cd3a5cb392cc4087fac7a9a2eb5bd4c9faff45e8624a30bdee4456ca7aaa78ce196b45fca0814e8aafe1f3af8192b8cb5dd035f834e2f87b71b33610277b620130b8d984111c185481d3a1c1f6e1b1f176cfcaf5c45d65017b56c29989c8e9b61f3a79dcff47768d5971bdb97bf9166d44effed160f6d1c3f3c151498a77b3f83030e79fa015ad736440b4c873e84002bcf68b10aae6f83212d6d0ca28220a0309b829328997f0b0646322139951b2b62f89692b6513916d9da2a73cfe40ed53059591516c11242a86097b54460a617dc364b5886a2191e429a87aaa0d554b68c8f9a823cfba9dc2eff73d44719451cef931710196be15dac09e29f75790758c6ed71e25a51b828a3583128c15f3ca0f8dbf36624ac98bee82aa87af1ddac2f604d946949ab4a00a37fdddf0f0b84f6ec61aadb4f8db508d9ea62086e7e990727efb8b0829114d1b724336fd6b3d75c65ec99a2225245331dd630b22b1da0985b69522ef31384d40ba1607bd126ee3038b7dfdafdfb8bf0d7d669c54bf7142324932dc91b0a390cf7464743cc5f585e997c728b53b92a802ffcce239035991079de6be437996a6fe2cd257da11ab684f62b26b83250d1bca315076f75661b1d1f50356910bf2b59fbeac4486c0601cc48bf4fe7c56aabce6485d29b1a25d906cba4d68c882c455375e9b121ffca82c0eb1faf141ebbdbd591489638b1bb1974e8917a3536a6cb44ba7c426299043f179b3cd80551202eed8607db2424aa77cfd4d51e5b0eb974e01fbd40c7a503aaa5d3383abfe33893df0d3f1bdddfaea6f7ad988b902a14ed9fe04ab153c3f3601fc2c8798766788ac70a4d8c151a748b439e543a08017b571aae33f397d335a5883d94940f5f6fa425c42a8504432be5b506574a989621532e66cdcf5032326d894285761206526191232c0488d98a90f92b1db112cdefed77e097d9c0f2c88b56b7a3de8e187c2a7dc244100ddac7c4e28f2188cfdfc49a00848cb3cd34db3077685851ace8935e1f8a18a3ff0df43003f49940bde9c26858472307c708152084b7962aee8be53046a5c59a68c71d9790a8a8db2bcdf62efba2afd033fc1eac0936b5c056629be0ccaca7492e1139e0a3a55c1ce8e8e25ae04d500d7de76ad505f613e4a00e44b792c0f94f0322cf2529e4e8cca371fca7ca7b40d4dea6b0af13ab3aeec2e4f8a545ee85162ddd7cfd95753e40627b565b33207bfbc946d828a980394a42039aae069dc64e0dada210396b20b7ca4c69e927d46b20b8aa8de63a6bd30506a5b3a644f9f702e08a23ef84e19cbd56f1560b11c73f1044e9d51288a9ff296d25f5b485308a723a78c2b6106d6789d652e3dee12f6ba6b4a8c87ad02df67c2af7b092170d02ef07d4ce875c704048abf51bf5230509a9e47497e38af441454d3168821adfb21ca174db839888109d1f8aaf5e9632946859afa8124beb068d3965b511148eaa6e746ed29270aafd41c29b288dc0345f830c9318f1b8349da9b91712661693e7fd9db379883fe258de6ca1a166f64715cac020f302029656b7a0bb63019c32c37e115f7f9cd2922fd908e71bb7f4369f202e6c8b5ad5883bb604a664793b56d88934ec139e25c84a2eae2363b93312e352fdf33a071a149ed23667f5395cf48fe5c77b4b451d3eff0d0cc66b63b422f3415b017bcbb2ce65bc8377c1d5a2f71b89ff1bfe148576918421ff716a9241a9dc4d94c689df0545a8fdd8f238f473977a0cb0058ca55c7acfd1ebbda20a30360f7c09d80338517206e8e7d58053d41d237c2704264809e46f019266799391b1ae14f9e152138054c198b66a6f9e013f95f8069759c4e3acd763f40588895b7eb74ad47c3582e94ecc464e7ed8728ac8872d697f17f86e06b7f8f0d305aa4fb3bc9fc8fe6a1b2e251086d60156ac14dd3f0bd4f4aa0a31c8e6e41824432320fc57337f9153448897693fbea0909f5bcfb7a3d4c0d33b6c63e802a3e246791aca3146ff5a09fc6fa24a4b035ab8017a4a794ac1ba1e799e9d3e0f505a53813fd08dfb9d43aa808baddb4c0a371b09476d13da5fe91fdb625bcb6a051c0aad322135101466251f4b2d3dc7c92be8f642c33e0fec34372c8ed21d3acfc8c3d9b580c4f8bfb47d151bd7d4f75e4d4794aa7d9dc519e75165bdd3063fd808de1ba13bb23e64f496ef7634e024ac2a52b988d808c937e2503ce3eab6722ac70c97f002a6888364c7ad616e1caf79fdcf7f730442a4d3f5439ab55ab18813327ef5a407d5c58689a3396701af9a05c2197d2b196713e36f828f964a3d133955e540f1cdd4233320d8ea0512ed52a745aaba9834a2143ad6f9702dedc385236c84a85214713202575c594651f697db672f22f7f09dd1cdf3f5e249dd118e08d31c697b9485611b5708056a1375d85ac307b13debd12178cf1b8c0d9b45f8d98ffde139938414da36347205167831497de8025acdcea269e1ddb7755332ab1b4a1244705195a3312adc1304bbc4b2115bb189c0b0bf38cc88214b3f83bfb30b482ee114960dbeb13c3f09e73d351e9558c4b53bd89431bf7400be6294b428a31246426084c8b4b5612d2a4a37021ecf8276f4668260486bd20853ae7d9985bdcc2ad3cf072c0a6c94631f52d3df725a4b8ababc2c3189e9e76832b4c7c1f39db298fb167b596c30b24dfaa4e9840428a89bee8ac4e2cc32fb2b3c603f1a75dc0c2b04d51866fc70351453e02e2bddb5676edfd823e7cb32f3b81bf2ec44ca6bc03facc2968a4a866d81633990d65dfa5ef72e83183a474b13ec3728e27a3c59130b7aa3f5d2eee594720ef188b335e854100c1fa0b43ca36dc1a54ea46b247780117fd015465a41076a0330936fa39aba864a4321987a4b0b5097d3b2445c806f84d1ce3f21d971d52054cdeea29ab12052579dce2d5fe1f4b73c67d17357da094ad9f72f824d21f5e6a258b2d48929eda87091c69c87b45425c87a47ce9037fa0dfbee0600522a046528a1eb39f22bc874f7fb847a31651db7d05eb66c0c8c166ca16e08ac14b6153f3f1c1330873c781120838c0e2a65a350c4c4e0c4d212f5d1f80f88da788f4543c3e12015a550d4d59f2edcb643627e2bd2ff2f2f89e9eafd824d8d8f9ccee3934e7818b05da86117c7a1cc9889053dc58a33f89789450018a32f268490a8b82d6ea8fe1b44dead70005b8a239d17e7c494ac4eac51564848289ff460bf538b1bf296a2c41232bbe60e48cf6b3f52a426609b86ac067529c8deaf71ca03205544fde17d2d1d046683d795276ad27785041f6e74939ef09fa750555833619152591ee555e289c2765f2212e3e1b75d02ba2858c3c29488a550c0299aaddbf2b01c8f94e7e237d3bec5ce5fa1188525a5948f2ea31bb89432360babb4788524c7e0260304daf479442be2249ce7523549642dbc4527629a57fef7f7cab122deb1b318df51d30048f3eb0da54bc401c60c9f633f0fd7c29655b43cd6d4cb89422319c9a184a4307dc2547a0bb5811ed617962d38fc015842d591629a47f6a4bd0d2a594121960c5323038f84dcc3a9752120734c8f4bb54062ea5cc349ab9571020d1a0bfe127de544a0f96499f47c61be85bae60fe1e7bde55b2551881a86fd456c98dbac8a769d87200b1e18c0e8016135a4cc9f023ff7d3c14796b1aab4c2b109f48fd6292550a876c746630cd4c14b48a3913cbf18d4ebe6999a2b56e99b04a6113fbd7fc9748dd9c554a939d2527b4d00e9aff4d5629837a9cb8bb86b24a015f1901c9d937f3b7a9c13d412fb8df29dd718a8b2863afbd22942f97dc079e859e3ca30587f94f9652d0041aa97452ea8382ebaffcae4150e302ab148ce4a5295a84c8053629fd053a0912bff428e5158d66592e412c13e2821c6a66ac226089c424559ad33bc9d651418138460320733816dddf0b8f20b2adcd938f14abfccf2892539a4969081ab72cd34c41a8506851503755d9f659ce53860d27e517d10cfd521d2fd4a19d3a68374eff898da57b193c270511e486172718d8a8b3b5c18340b6761ff204abc74780879c4b6287b0e1388b1309894cddd6e42c4e47700e773413bf00236fbcca48ffe6de8a7de09d8a2a6c4ef2c85d69ca433725312df472a12fb12f3fe8f46dfc2630cfecf24685f9024afb2990384974a42ca2c52e13fa25b6e50f1dacef6b524aba2072514795b09337cf564e6be2242b346d8bf72171e7d355724df581a36b21fd010eef9b5263ef8b78fe78a0a1267b03b4c3c5d80deac3468944e1fb4f74c5dba650a6391c7be1c49e352b489b2fe559986855fbbe96236717a76cb3b498ef31b1f408007cf449635dc2b08d4cfdd73838c72f2a4bc95ff4dcfbac78db26a422adc8836be6989c0f4c68930c84c5da3a9ffdbec061848035d2d6d0f16bdcdca0655caa66d3122f29a2042c5e5289831d57f4f640e09ee2b08b287187e88b7a3b17a2a8a2e11f3dcbc043a9a17d66c0bb5e85adb427eceb87af5fff1b7c3b603f04064bfec84c6df6b1533a224663c4e9dea2be3934b10fb6d5d8abf0eec888e34c89d365262956adcd02a97acbfce1c83488d8b536a3112a9a509262236ef180061ebab1fc3b3ead0201acfcba641b92866c64073fc62cab90dc1891818d44813ac1e251ac0b0143553e4fabaa33d3e2b73c208756889b4996183224748411ac2d8447872066c242b09737bbf9a0bd86285f45d11ff8d95ed7e8d94ae854f36b9ccc8b14f8080d4023a9de35afb4600594a84aa9d9678db44e542f461a7744c1caa7c9982367c564025bb936441be59f1f0d482e980622cf658db5b6f628e8e4389ee217fe9365268032455cf42a619b637aba17ba4c91bc50a6847d7161a5ba66713b878a45b1bf135bbaf3453657e0001119f0dbdfeece2a0bc5c9c5bc88d5f2e8184984d22069762de20a887f2a820a55c01db741d0e85fae82639507ace0a5091f214facb4ec2e21339ee474eb4a7a5f3275b224a60b6b24163d73bdd6bd116d3d8e1b92c44a427b85c19cda984bae5bf32e44aa2eb16417d9a596ab0fc76ffbfa57d515d3f4b291b668469f52587702bbee9d43df4e0c1e959782f4082a145c89c7c6b5f6df057a3dc3a33daa439435101520d010af439030eb6552512f60eb625db4e359eb4f5d604d897a55bb0f5cbe9721d08e4563f6118956cfa08bd08e93b0c665783fc6e17c46f8e226fa6e62f8322dac9b2cbf2558b0129fa22671710a52edc5c7501473c283e10274a1a2790f6ef28808c38a1903baa10b392923548154246bdd2e7d014ca2a3554567af056986fc04e6c77e771170a77f769e27338d973262e216c7de5bedb24e5cd9538143d72ddac51122602e8142c65229ac02ecff9634579236f7430112cf5c1c9fb2615e078a9a63258692ec063eab10ac68a0478b6025d12b8e22b582cdf92058310f45dd7c2dc13acc704e27c8ea2fcb22d25c17498a687b1f1e17cdd5de2e40bb11410c070cde31a0fd757ceae11d9e61270ddd37ac2086391ddd47418686df331cac8df695a734d2390b87dfc6005618b03389c24ad5d8bc152798011a43b4f092b9730fb16ae179b6c524fb2993770cc3d9b5382021d797aa0dae244ee57effd32cd4e9d1af8240c737f022984617d936763636425d58a9913e0ecbb05441ae64bfb0d2c9943bf528cc5937bafbf98595ea44be91e2593e3ac27e1d6b85eefc0382e10e01a8144d06e0df266c18407dc41a181f3d166b8457dd9b1f0b86d9d94ed89dd2db25b213b6a58a9110defb5b45ea39b314ed810c1f9bf7dc037486bc16745b513645ac20104e77e9acc7c4c7e6ca834ffb9537053cf08e2297ab4f2f5b6285fc2686e921372e114408ae20baf58b64a6890c86721811b4202b0e24ebcaea18619718415e45b31f6c8303ad20c72f02d8b73e8d442077a98760e50b704496686ae71a0304afe3310628b0bdf099b696379cdcb0f10cb56b99319a86e06c30da2285ab72c999aaaba4903edbb2e16e09c7da6deb418e9b129ecbd2cc058678aa426811a57ab78d3660d79fafe88117640dc8e2aa82dafa2bf76454c0e4e812124da85b2f321d112cd094e4d41ca3e97a36d04363e9206b29d21c8ad17d8f0015658a971a9a13f1668acdf97377110f0da49e6e007054a0a521294c055c22f4e88d35f1cb5617f7de928e12c601bad2f387391a02bf97b1b112759d3aa93244a2763669418f12f428e9436b108165f9f590c894b0516beb612e7339e828e4399804c7527081cb93d2ae5d809db104e9947aac975a2821a739c762aa479207e828cae9648fa20059792c6ee914981fc89da1ed58181b35905620afc7eebcfb16ef65075cdcadd8a3f3bedceb93e66a6e7055d40cc7dd67808624debaa5eb40a2243522254651c7e6f6cb4732810498efde42ce38b693c63ca19a951eff94e89d9a05f82aa0010c37f81dd380d54d904590fa917878c42d1e388423e8adec4a662f0ba2df98e1e32fb8b95c94b570b2249a54bf1ef7a81facf34916183f2c9405534da01d61340a0b0ba4bdf715e50e6931f2c2dd248916ad65865718069aa2ce2a5a4b6cb206e9d60eda190ec968c7053002541549f04c92eae2506945532130982abf9aef577bed8f28d9e29f9305b01d2cc46237d839d64d667aec51fb646c35ceb105493558d8b49ceb1dd33851fa44779ad85edc9ffc1f88a3a9c44072276cbe28a5b0b9146b95859651446a1844234119827cf7ebf2dc2d5a888212ffb51c80944bcd37ee306d3123322c0295efdb449502fd1c5860620bd3a1a1e85d1e3e930c1d0089641cd1569595c37593f4cdd7321f2cb31d2c3a3689986191eaf22881d2ff5acaf13d1cdd09ca66a3112f52d90322b634bb712b0231cd33ab6521196d111901ee752432af475d14a38654a299ad36b379cc0aec1ec66d5bc6e926f262750e37baeb84886c04838bdabba9bdac042ba4e1e93c706174128ca4324c90891c4a92007af759d6144235c74bd9f972f04c3f1f72c321b98d935ba226c104cd7c3d900826257bdd2b3d65ffaf733b796bb61d1a2857a06c2fe065ffaceb82a2eb95559e4c63b3809d8acde324112033c759d3383ca5190768524cb27447b5e0a6dc0d2a36564d37657965079741a7123c32e94da02c58ba03c523d8b1b1a4df851af744c9964e7374c77c8f556667b57731b2a6ccaaae1bba23ff8702842052cc3415c60fc808fbca334534a568a293e08b150002fa152c30160b39d7b62c64d373e8faacf3872ec2f14a23fae88b67a8007d7a57367a4fb738f5004d90747e834f1dd68bf07d1b388002a06364bb1c97c81ccb1d8e1133838e2ca724586ffdec0e59097b7e80e27abf4c92de55804f98988d3566b1d0e57be630ff2b8a28996fa1b062227f81750fe2039a957baee792bf091ea74545f380b4009296b05ec40404dcf2ccf9a4eb15b27f4ee4a6be718d30e04dfb94d1463d7f0bba2cab925bf3576297000a9fe1e8285fe2d500c5f77be32ca8fe8082ec8e6b979c024d5b19a08f5cb93ddcdeefe69bc06284e4a349de8f6e8af5340c4c8898a2ee8ba97ce34c8e6dd37197c1e2f89738bc7ea938afc9f538683b6c605637bf5670dba6c314985afcba45cb28f7088e7beb40f2b39015a0951411a2b4f2cebdbefa816b42d073dedc44b374d028028181683ed273debe094629ad7142984f2334b5b45867a438972da94bac1c89a394f538e80f9a8125a49bf2733092f6695ca67d6ffbe8b4906e7ba0a15695ae586cde45f2fe40178418078df08d8e967346b9aaa64c484ef99b626bdecd8748848abc4c1a67bca873b253cf9e6fe58b892525033d05ee6fcea362e3c6069eb4fb7f88d0de5ceb07bc6af7d843d749039f2ab07d92d1e73bd2a4310ab5be612d1be922290594e39f626cd71e3bedd84f8d0625cfa1ce745abad835f430e5ae12653f0a3b586c1b45e2ff84bd4767e748acc89a9954fbdda97d4a590c1ed3b35d03286e033dca2c259942c2300fd6aee4792bad42f03633923e3f2d76e8477a36856779293b8382b292ece3b4f6750f529c5fdb7d86112594b35cf162347e3d50e3bf33e1df8c675f449c485a89a55f31a1091d59452d1ba7b194032660fe3da7a6231d4fb44777f90d515a215aa4980a9de2d7e61107fee1b845a9c20e71cf6743ef03c6c88e7ac6b7e53a27362bf43affeeeb1c8c86ff378b45b621efbe401cb2d348fc450d0435865dd5230e1b16073b6e50ede3241ff7fbc98e6136d21321c620125ec224abb25e759c00466521fac6db45b8a04a99809bc735de345019eed0e7adf8b316387b2d52cbb708204e1ac737a979ebeae9bb93c5a2cf48a1bd206c7daebc8a870eaccefe0834c719a3bb5894e205502a2ee2d85c4446e91e6d7a28beee55f52de6672c838afaf143f78788f9bdc6ba064600c3047cf42281073cf6de51b92636ca51e1ae627eae2d37ad3fdebd996b6e53013f780eacc8bd350878efaf7285b0ca5d00623f9299cb6353f3bb0b9aeeb110a68dd667b4638f16c4837197abd758a0070c8590e7d12c9ec0347512b89528af75f9210b8a4140a705b2bc20007e08d282f8a3a580465095e245cc0602d55dd14e2eb66da9de052931195e5db3b49048a19f177389521c3e7b098c2d18bedc3a79c08141e0b6e9b8d9864127e443a84fbc5cb16e7c5a3aa6c9828d7329361a597822024b6b4b016ec60951dcc16f07af427600bd2a8901b177a6f1d4d401970fc6d4e0a2f7355d7041c4599152db74dcdaff985a40d63290c1292853b9a7817997f493d8cd9911c9dfd07dc3abbf9ef54017d11f7d3718dd9dad04507bf515d907e3beb482fd01fc95100151e48c6ee0e32d42ea3e7019e5f61061b9a1e761e0679c04807cd70ed071bfaaec27d9a81c3ac011c73876e3ac18cd474d8a69d4049e757588a1fbbe2601fce792fea892f7f81f1e76dccf20f85dd5b1d73f57f143c5f4410656fa9ba43a1977667effec9f10a06b3d58d2d3c0a1eb4ced1254576bb15f1b2b3d7011c9fadadf957f8a7c77e8e860f933d5b9a8bea503efcefc00f9cd9aaceb71de7b8b038bdc813c0d2eacec69d4bcfd96c8395cd663a672b5a099955816a3376833d86dc90fc8c2cd3b64b8989254a920532511ce93f4b443abf6ac8b27f69381038a19a7bb24e2e5c4aef6a5d8d60f686333dfcb8dc2eae16eac3c1cca2a3bb3f12b046149ebe110950024706a35024ec24b65bbec707db166b3a014e9d5aabf979ddc1d7395b9e17622f8abc9d5a39a202a79cb6815986439957802cddaf4c27eafece34e9fa4984fe56945267771e2f18a4bee48fd9294cdc53697f405f64ffdf072f40eb0df6617b6c7b23c9df02028428bd37188036182ab969c08962dfd69132639ca62bc81713df1a4dd0582490ec8214ee5df21515a5eeb28fd04c50bd47f65e77bc4e5f340ca2082eeb373dd27eb98b0ed51060372e936c9c06d268966e03d330a0d3c5d1ab46ae08ebba556408e19a6893677111136e7405053640920ec19e2403af4dd5a5467612c8518aa40fc42cf53b2559a96f171b74d876b29d803828ec8ef8c51d625bc02f17a2764a8641e74b406142cbb34160a9110c4bdade2752249e3b835eb383f40dc8c6131501df621f727257178fdcb2422d39f0cbf161006a532c4a7a4465b95b670e1a2208cd92066e8e47f5ac2b8ec598d82e456496b65232822c49a268f213655c3b69984f212ba137b8897d68185bc64bf08b9a9428484fd82f5a7cb67050b5742cb51d315b8fa2d0667a1be06860adf09eafab53abc1cf820c1afe7bfe02a486fab43ddd3f5fab03f8303db91ee9d205024841d159854eba0514d0b9b0fbd135f66fb2def9323ee0c56921048004a6008b96802ebf2bd5e267d3dadcd66bab5db04f5edc73d7aea156ee3503a50c76305ae38e856f1efa3034104cc8ddd821c2bf169649fd45c77898c424c4ccba6200db67e9e33b6f3da6c50ef766f160f29f090144236f989bb9a44fabc88e9d8859b82179ca30eabc330b765570e89d2143431ce69df1404261caa6fbe81daa6202aeb66723c6ba36200355281b20597824e09de2ed8686674cd8f0033975fbb38de74e8c02ea951ae7728280b6a9c168d2a02ff7756c992eddfaeaa84d1e5538848182d12bc85b7c68e5df05a0fd7d578086d9625a427180558f9884908e5562bcd497fa1eb6aa179c4b0059665cf5533f431062fb0fd76aecdf3ffaf1d1f3fa0f14cc58b78018998bf403f10d6831b57c313ef6b325f76aebfe3429767fcb329c96240986177b0d393df5b1d1e02da7d2e931964c3d6488bbebcfa2b2e38b1c1a28069f533584012100ef1ede2d1956ded9e034531d3c809208d095351c8489ffd171be1916c416c85e81bd4d5d92e8c47048a8effbd5610380a6d3af595cf05fe046f7ad2488b04bbc1f00e969e904ffab62dc1a87d0d461a5f0dd64525103226ba49d53da0f09eac594dbc8db7ff424cff7e0a8226a59c2047479aa0d1c712027f5ad2f6de7befbda59452ca500b9b0bef0a64b9852ca96ba5cfdc65ae039bd2f11d3ff18e832510d29174b6cdbf7208f9e4cc67ee9546b34dcd215a598ae0852a0f99d3dce6d4696ea3d19cd6fa39db48bdb3737ca6c31d93f90f3b9bb94d5b4b7766b31cb7e95829735a06f9b21e67d3c0224cfaf531091e61c676e81eb2b71cafa5d10e8d560e11aa4094dcb90a44852a10dd49f6769ddc9708c908423507924d9bb907949e24f7599f9247eee33ce92678249bef90469832a74fbb363df330dc71997e1c7ddedf71b008338895d15ce6337d2afd4cd32f7d2c774d48f5d281542f5de9497d22a1f646da9c96d4959eea24fbb62d54816432ff71677a67c7744ece36cd4bb481c55d93fe3d7b80314b23d13db0bad0af0f513294f4d122d93fee484ba559b98fdc9fc9b4e9b4cc551e323f87fe246d4295876c93a5979e1bfa4a235a6a7a2ad199d621b529f39c69fa90d5c9dca42625239bcd487246b75005229ddcc0ea647ebd0056a7e397fc6222959e6b3924bf5f07a1f4bb879e52bf1e8211ab2349bf5ba802915ac7a996b9689ae6b8cd699aee133f8ea69fa36d4e2b81e4d81c47d7a7e91da7fef56d74c771f48ed36c3ac7a9a6f995d1f4e83a3217aa3ce8cb7c479f4943ffc3520d3ae8c50441d3043d1673198f93a1efb83b8ecf709d9aeb944e7aa9654e358dac81f0c1752217ebefcc4997e913c92784e6b46673729fb521395e3afd7dd66c3b48493a7d244f5d96e330aa6936a739a49743662ef332670799b9cc675bb669e599e3e3cc73b9417d1c27f559ba6cc3a6ccd269fef2f1ab6507f565331c277db6a9bf9c9643705cb691707cb6916a399aeab3dc49f67136b537775acd9d745b06a96d6c6fbecfa44bee73c6a6f6a6e3d8de74f699e4b534003540e83b1e522f7d2c4393a4792923a9d3bc0c77dcdc71923a4db64f7307249d923c3b9bc74d1e2735cf3eeb9732a72425dd74ea32484aca7012f4e2652cb66b41402f4ff16548328f518fb9cc632ed3339acf944aa7954647ec53073d16731990d9ae79a969fa24e1693b3bfbc45f7a8edb34b5b79c7d26d93f3d70dbb46933c6cf3c741da75697e3250d87a6615ed347dcce9092ec871b04f5a9e3782e835821a0c73cb64f9953d7b32174e6a5ce719a3ef1d33c67ffb834bf71aaedcf9ce2e0b8fdd9469239e849f6653b466afa31f36f344c9f487ef49cd2c87cb1f9a4fa20e82efd81262252079dd42054224498d44f1a06c807f676cbb22cfd965b037ba3f8455d1ff40fac0eff752220585dfd91d21bf51ffbc78964030d3e14f2653db8bd0da5fdea4d2deefa2552eac1042e3a3079492383103b015c6192a1092586a0418bddf511ae93300214e649b5fe3e751e2bb4f5a23d9882b48bbbd7f1bddf235dd2bba4add5de7baf63272b7decf90f8bbdabf3eec58f7bec0dffcdf79eb69a905a13f2f9c1223206808dea39bb8fbde15aad03c0cf9cb39fb7c78e94fd96f3bff9ebf38ccfaecf3c9fcbcf3b9f6d9fc7cfb537fd14dd4f1d989fa10671fca47dfe3ecf3e7b9f69f889f3397f967dce67ecf3fd1a7ecaf8ec96ee003dc74fd8cde79cdda43b743ceff3e6d7c79bce65bf595da68f6f9abeb5446699eb766bd2ce1e5cd4d39369f6864ff2cf221f4a952bcadb666fd8412ea6f51a3d34b6f6193c3bb69a4e0e6d8693a9551699cede67d8e79f977695d9c9ec63ce0e7e5ec661154c9ad46936d0bf71038bc31a7c313d3f7b3cc6b8ba609e3d1e3bbd7ddee3d39d461e24ab189ae4dd1b6afc30763c62da15f33ab5ba4bbce2d0a9cef5b23a0fa438d041ab2b8fc03f42c2838f7d2c2bbd898ef54982fca257ac417d56a3d1c750db27b58b9e1bb8f93a86e12ee94ff5b3035f71787ae02cbfd25bf5fc3231bd4de428cd6d349abb8de634b71bdbdba5dd6c3729d8ee1817b20aaa1da2726b465bab0623a5f76ed0de7407805c204ec0586ea828972b2a1acae58a8a8672b9a1a28c73453634193b733d67eee6537489406cda2a33376475f87a35e9c57e419046a3b5c98a252e2b2c21568dd5abed6bf5b6591d2361d629b4c80c3d748feed39f52ece1a6d44d7bbba0a6d9dbf5d3f6d77bbca5b5a856a744348ab35eab836401949eee5a900ee0bacf1e9f77527e2e3248894cec568875ac0405435a0e25f378db5ab718c12966756a345a8d94776d03fa9f673f6b8d036a9d96df86096126e5b7fbcc0e82fac3665d4d1807d0e6a9e79e9e9643ca23f54170a4b6915afa5a0e793dddf628fc3bbe80d811cc20c638c4f75ed7f9812bdad17522f95a90a7a20d2b1d3d17254b5aefabdad2e84809fe547a5c1e555aadeb883a7e8be90ff5ebc162588591627ca55c4c7f6650b235840ac548fd56d3058bc5843b7e4129ad350b7ac5a5428a2b61900089a094d67a25cb53c0a45b57a41e0ce91515d87bc5eba25802a9e0bd595c816281186ba5f5a6896aab8555d96bef104f019366fdcd72a97cece28b4b320d0bfe049d467dccde70de817e4fc67befd40d1fd3532e4ca590c131bc918bcf4c48c5f77d374d1683c330048fd436b77e51a5897bb5121fcb5fd01f1a84058371a2039848b504dac58924be7e754bb37b9a09ef3ccffa0373a7f4f47a2d88f8771f793d9487f749f7bd1757355ea73d4c3e666fe00c9e1d5b4d278736c391c564dcc0cc97ab244731043f2fe36b6906ccf24fd231d62d40f1c6545ab37cccdeaa4c0119ad135da9c6ead5128144d3c7ec8dd28bbd78adc119e72ebe780a984fb1d056acf898f7791ff562858aa78049b9784f0a51098cf1076e4176013dcf03c3b2cac76a2886a2166ffd5221a3c4511c9f7ab1b2c309ba66281aab124da3542fb84086f4c75e4d019366f1d6ed532f55ba3c054c8ac553ff9e7ab1f2c29ff86d4877d0f1a9172b277cccdec6d288ea85251fb3b723f86df4879625100d9664d5402825b508264d834b0c23ed8dda5bf8d4cb5418d65c029b9a5a814d3e61a43de7bd18678c2d686f93db94e4b600bbe069796dae87952fd5db9cb3a7953d4c7a5a23c0a2e85e1b4a24ba75ef0b110c3f17de357f2f862078431462ae8e74cd775ac75f67ff1194031aca39dafd8f1919fcd878766ee0c0e5f0f15b4d486728e768f73f8e1c6311878fa4cd47b1bc6980f36807f84d13f11b9a018ed30a701c7a019e437f00826790f3a09a8e4c4604b49582b276cadea668b52e9feb23e326f603149341ea20c0c6b37303072e47ed268bcd06e0cd643762b1178ed08c0c643273e6e3423332c0c181fde8eb407a28c783b42701add91d9ae5783736edc3737a781ea4af0be921cf51e4371fafe90ce9f0ece08ca1338483538349688706cf0d9f511ae1c8e1e44de3709c16da8f6367c92a6162ca91a3270793a6261c3870e0c0e1f46f78ac34bab1a989c3378e1b38669441706ceb64868d626f4ff6c97d06b9a36deee3b9202021a1db179bcd66b3397d1e97511af16c6ada9c868dc7e66510dbae6c5ca7a6c4de6e4d68d79cd81bee6fbbc6b433c46fda007e4317c0719a03c7a185780e1dc4858034109fa10fe019d4747a80f4d4f8a1511ae5ecf47cda564347ef1c070fce06046cd4202de03b95aa4e4ddd00400209cdf8a99547d792dca0c1b383c336460fcf8e8f6d0c1e0ea47de434f0707856ccead323ebafe9a31e5503022cf1d81180cfe947eff00c340f9fa1af0f21ed790e3de438b4ce715ac74de7388f2ef21d1fafe9e8dc08a03374fb82c3fdc070376e6440dab491ff681f1ad4747478768cc6d080003a39e77a4141a5d10d0f2a81e0bff94883ac6a0308ca01cd90f2bedbcef18ed41c69d31af88feee119681d3e8300ae733ca7af8f1e12d2a0e720f21b3a007eabe9e8f0ec683006017474d4c02130e77a3e3972e4f0f151c885ac6600795f2e28bfcfae2d09f9782e084828839fa12f39667c99ba9121e369879b9b9b9b9b9b98137b63e251d2c4c449c74fd08eae2a6162d2b92dc74e114f91ad0887cee13aefa0fee110d0161082824004bcefc677718e2b8d703d70012883e4d83d02f0c303ab010c20c7a6afffe80078067a87cfd03a86749e4313f94d0bc079b4e73b3e351d9d1b38c6b001565048010c1d765449082f926c4589e1c21547ec28ad64ead522428c0d6ac61e7643e121baa34e31a5eef6469d9ad4e91223115234f131db486d0e7ccff4ac97b18354744af3b8f769e6eba42e3950f2f28fa47c4f709ad669526ebec23c4d33468bc1ac622ec618638c31c618635cef4b0736652ed11dd6e9e764ad17d7d630dd868d1e7bb3366cd4b8dd6ef7760380df4e1b1b8c814d98b79e13ff69bb76090b2d00a8a012583c3b5ee3461633613d346a362812514c91929145d695d5172a1a9ab6d68c3c7a5f814ef606de404543d3d61a2a2a2a2a2a50ab0906dbd137577a93c93a33d36241d414169c1c1795187b736575a38f138bbb5e7d9cc4914ce5516295595697d32acf505d8e8bd92ec9fcf552eb12e5b65c2ca04441555c51972848ca3565751d5cb9b0505d075200e1e4ec52cb455562d14a2c271fa2a038a9010be8e6146afb1015fdb9242e812851d28473068fe8e008ab2f53515559221e5cc63c291254a84c59229e90b4c1439490a78b73e2646f4c9a94086112b244c3e65b27a1a135136d6763f3f4b80d212b50202392bc90125a6822457f2e7886a7c7c647631f2d5991b1ba443c579768070bac02432a916a6cb2b7eb640dcde35befb8d6ee638f4ed2a3b7d710835545ce827a929af2c1b240014c697d8402cda1020d2a81ea6052d2e4f424f6eca3252a1f627c60f9a822fb10f3d77d6409004a004f029012c094cf47166cfbc8227d648159def691e5f9c812c0d44b71253af91005c5490d5824aceeb0f261457f82a8ac2efb0eac1d593bac768ce34892a68e313ab07454a1638c8e2c1d94ee4927a59ba29bd281a5a30a1d593a28dd934e4a374547968e2c1d593ab27464e9c8d24dd958b2a1e4afdb58b2c16475d949b22c6332992ce6234cbba09bb40cda05eda49fd0561a4b57e1a4b36262f63e9996413be927b495c6d2556827eda49db49376d24e3a4bccb17db4c4e3320d35c3bd1598e70ed654945493134f92bfbeb3c5b3546eb96ab8cb05db81cd704250cc5f6e4b88102149842cd11f2127fcf599144e088af984f168698b8d25e251f3d775b2745cae972c663a95ed2eb6d67481b334c4d4172366e013a88363e5145415d31d90e520aa29cca325ab8b25b13a4fdcb2ba9c965db37489725b384350a2a02a38539728480ae78bd57570856304d57520450ecd7170965c67e32ce158e5b800729c106c9afaeba78fd3510914e8faa78f4ea03f4af49193bd39b1b7a7a88b9392eda325fa03c533c3c9294aeae912d9a22e51ada9c9344b23999b3248bd9d725a15c8cb6d05550992eae0aa03294250cc5f10d55650950ea4c86d817b4bcc6de5b6b6725b415414d7e4d3447faca86ce8a329a98b8b8af2d4a38f9c60fa8849d311d3514eabf2382ae1e804ada3259e2f9768c7ca8aea6d5397a8d6d4e41415e641269547075715e8fa0d920a9a124485a301d794dbcaadf9609e8f931acdc116076c64747095affefa4b73f074134485a301ca1a2756c7410d58407f0651914154b020aa31882a88ca4a1095299595959525e3cb94de60a8acf840654b8b2fe013145251b4cd04bbd91c4855fd75215257c894102aabcb6e9640c29461acceb3443c500eb53d561a795752d0db755c53ae4d6e2b2888848da6c55b52b0f92babe3000c7abb4e75a33988c2355d220eb4e4da4ce5b62e11073e044de12148caea384881165099bf0ec3358db8265c93c535150106d7f4d7dcd1474b1777bd863e62bab8d7e6402a88caa587a834fdb2537e39022716254a9425e2d17b1f25395a7272b23acf8978fcfa48aba1531b6de4d63caef50caf8182dd8ec64b4bbba53f7d9cb498cc1fa64b74b4758f962ed18e5f3f625afaf3a7e92f65f3a09f9408257f7dc4357af4d1d291121afaa8a9a68f9c8e98a48858a22255958507df190f8e8c27c663796c0d41fa79b4ade94eefb8efcce0e1d9b1d5b068760255e38847ec75c423f6917c650b8a4e8ee33886ee1ac7d273e191fb30ff615de410d1c7a10ff7e972d911769a2e7f695a6ed0de6a285481c24dbf7d7e356b76820a16a5784829b44e73954768efdfd0e95b7d52aacfbd23f7c37dd623a5ff7c1caf8739dba388ee33749be479d7f37b8fdc330cc330f490f436767127d917df8b911e0f2f2ff5697e99ab3c5edbc33d7011de67e9382c4b8cbf12f473080b0955a0d24bcf7bbdbc57f597f77abd5e94aac25e2fcff45ce551dddc3fae577aaf98e72ed76b27d90ffdc4b0d74eb21fdba707944e193ba9bedfecd3dc2fa1ca4314aa40a5907b42152896b79012ac7a50d46f3deeb75b8f7bcfcd6fa0ad32cf9c59cd0b6292ac58469c000bacb3d829087edf0756fc79b57ad6398c31d643d5f3b267a38aad83d96b0c4030a6ad3ef6561d748149ddac9eab2d8cb4877c1afa385ab29675537cad2af9814a7c319142cf7ffa87df58e65aee53e9735a5617823fdc3f6ede319aed3acd66751ec5d98a3ece4215084cc2f4bcc6c02f3e5874f2cffaa7500522f738fa0855a0eab41c77ae0261a7a5d190fc476e9b36a250e571e62de46df0824d23fd31bd7f11cbe67cae7b1e5827f2c3fa06367bb3f9b4a0e70daafe61a990beba7f9cf4f643830dacce33edd7fed3b5701f7928fa58646f5674bcb30be659546475e1d6c0deecb7a146b7fe630477cff61b888449475a76ec60e5619768f6669a619267fde9333b8edd4d0bc3c4f2c2504aa9ae5e6f2e8daa8f43f6463d3a641deb5c05da546868c87a15e78cf1d09fd693ec537a7a4a4830cd51c7f5bee739fdaa4f4a453d1a0ebdf53ccf7370dcd4356ccaf4f6cd404ea4bbb15b17aa3c3ecfb3b7852a50c6fbac7eb7d058f1174c6b39e4dbd4a3e5902fc992608db0b4eaf859bd5eaf7e1d097f18db30c6fe6910634bc39f67ff61bdef73ece397fd87fd4cd2de6ace1bfc2b3c92eec0a0531e6f73d0dc252a80c5dda794470815c87aeeed10eb05b021541e21052222a238ebaf3fe911f4c7fa4963f0957ccaa3267910cca0e77d2b8f0274407facd724efe12dfc94fed0abc75e9394598d7982ff7992ca54e6ab5712ac2e63bae3ab15ac35ac55ac75ac95acb5acd555eb08fa969e1fa13a4a45303202dd51fddb2755f356e663ddf6bce84fee121a31d21f133b6975559733ceb52015bc365a75108b69fd8c91661573846b2f107c2d4837cddeb077312fb60e2619690726e512c6bac7254cd38fafd144ca5eb7591352c9135fc77e4b21d63db7a590117cfeba231fc963d6d8b1dd60aef604a3e8b4e7cc5a1f52a61a7a75ba447417be6e74b5bb974ef2c23e5ea29ae837aeb4f34897e14a3b8fe209d416f02a81cb5f4d97a8b6801b37dddc2528fdc6e92ef4eae48402b59aa95bd4303e74f012ddecda07a5dff8cd4672e3a52bedbcda072e7ff96b2379b9cb95769edfe8f1a5457d52ad0f49a84b98d6ebaa05b944721a93eee8eea5e9eec7dde012c13c740f3e2032e6acae7ee843f72a2ca23be8879e33f14b83f7f10acc227487fdd06b12abb31e7a65b2baeb21f5d06b93d5550fe9960f7d0294885e4acb7c183aa5148c0f7d0c4b229f9b2f1717003a0c8f188d2912a12dc0bc388fdc1f942e632381dd6c24af9de465fa8b24fdc67c7ad16df9c18dc3c20f5e6e6e2422e433a688fd86d446ea132d7548d9299921d48acfbbc8dec2217b0b9d8e3b676fa18fd88c19b388d5891e7a08a1ffb0b7d07119938acc5da21e8b0b3df4b3e843a8975fa2d72e72bb4422dc22f616fa15a19f144b873d740dac2e7be82ecfab293abd6118d29e702365ad51cc786f07f05bb7b5dab2220981dca79b489579f3266271d641d03469209ce49ff4cb0697c8c7e272e4b64ef113e6d5f4e95f31684e3a08c63c4d0ac22602ba808619c799e72a8fd267e7d0d3729ff5cb21e393fb5502d1f1b0e6e4e4387517499d46a3954e69fb54aa4f6d361e2f69b58d647372c88e53af5ed2da909d7dda27bdb64f3bf4e5c9b3abcd6d4ef32d54814ab7d5767ce63203904148a733ea25a5d565aea35f25109a53178da4d1ca4a739236964633db8e6d762d92b2a4a72cc7654ef35ce5413aade84f5999b391a897434a277de632a7f9e8e78e87e13e875ee6b69c9d641f897ae9343f7776927db2745a69fab4ed9c7de297d976ca4d7aae02c9dcf5cc6b3a5a47a8f2a064e9365b57615630e99fd6eaabe321cc336fd9ac7ba540bbc5f4b7e99596cefbf4d548cb1819fe3ab656f62ab6f4e0d3b69807356394a73e46eae94e8ae9c6ebd55e9cbd0f246f48de4bde91ece2a4845a6b254b114cba66f4e2de7b4b170e968bcc3967d76b06e6fbbeef65d24081498661189a30d1098c1cc77184dd74b9a1c90d5996657923e3c6c9ebf57ac98875710288180983c16031998c942143860c194e185d6e40412693c970666174b181cd8cbcba8b0d5ae458ba704c20499224abde3fee4be9e57abdfc87259db42e577579b5d5a5492224499224f9d244acee86c8cd8dd1cdeb5537117bab2f72cbdcd4504cbe93f9387351973ebe3e8f74b95c2ed7a6f646c4554319fe7297cbcfd05d995a22999fa2c3fcbc1e7397cb92ae978e3de9fe087397b893ee633f932ee9371a3665de9d641f5331d5f1e38d24de93ccfbc6cf70df90e4b89144c7f9316c27d937c54a6e25ef7239a83f77694bce74cc4b2d8a3e3aa9c17d924f922eea4cceb4cc4b3d7a88453dc6fcc71d2b09e290341cd2631ac76547e21e7148d04750c734b638d045f0ba42eba1b60feaebdf266202ac7463cb14c080524d90eed33ab6f6833dcd3fec8bd21fea5d8e94b2e88e5ab755dfa6cc1ebbc8deae5bb77f0efd75f04ccf35e2650fe7bc29fd81faee7ff7abd3aad3eaa07d61a43b34c1b47ed23e66926075f003e9068df4f8eab42619418f82365f7c5eaf20193699f7acb5fa899f3ceb26ed2dd718bc425187a2a8cfa44b29c638c4d4b3ae4c15c873ac4fcff3ec79f7fb7bf1bd15dcdea7b1e739fe3cb0561c566f4b7d6b2da80334c18ca1e8260a18198ae8493e38a3dade563244c1bc854e02b37e187e18f33bf34bb3b98dc76b3bd7b1df7cafefac602977f3197a5b962eea33e9c52ad6648ef7591be2f2ebf493ce9a6c07715decf493be7a9561bf434a17fd7ace59b683949b6e5a1d479f228f973c7e376cea55de92077bb979fc6e241e2f37d28e5f711b219f27eff878776c396efadd674d47c7cc715a8ed372724cec59f4efe698fe5d2ceed30ceb6ce6e66c9f35e79bf375d3f3cdf792007be8157b2cd4629849ed9aa1a9c595fbc49fab671caffa34428a1a0438c4186b7108c65bf47a9241aa979bdaa3ba4931ccdb087969d908f920a8a4be691f30cc9d243c92177db29fb71bbeb8ded26bc619b4ef93b3ba71cc497fb3081a8594deb297af63bb636faccc78eb193c2582cc970f4307ab53d749aad3ac0ef4cf411d7ad5524e109742ae87e116354ccafc9c7ee8542ed0f46b10b096e1f73912e8d7c7ef3f6d6910ec954370073e4b81b027d19ed21f9afdca20f74f13fcf739f87d9e047e783fd4f6c87efec3d23e4cdbb5aff6ed23f5b177a03ef60d55dd9eb1b70e92260d2aa3602b5c221ae1e2ae6315ccef31147fb10e4071d7cffcd4ab07f4e73a8652c1041f04efb626cd8451a2dcc5ddebb9eb33abd3b47ae2e13ff780669a679ff7ec4f6a85bfedf4071ff98758d728f676bd3e85b7365d0324f1c3095649b009e28bddf511427045932556565869622385c3e8f4b8a74aaa3e732580dff4d4995c85a1de812f9fe4290d974cea4ab7da14e8ada2cee4c7577670f471acb696f5550ea9ee2a81c8befab9c5ba4b6ef5b234aa9f93a551dee328732a03f6f231bc1609c64f15f431bb28ea6090867749e521ce70b8cf7dda07fd3279b90168d501f3ae853f2e91934ef39cbacf20988ae2a9e3178b346ff32696e9a9b3937009243b12ddd8c54cc99dabe3b02a0cf316af4562b16ed3d39fd771ced9f175ba34142f13a7becfcbe4af6952ba6bc641aa2edba2535a69166f28bb4d15283b580291b9e857579fe9d17174e9595fd97519dbbe5c39e7ec7281eecae1f5bb24d31dba88e485d96d29c473eaf2ecda35ea3ae7246bc9fce8a526c3120875a470930e96432e8db6cfa44cef128b6575637ed07b500406c82eeeb39e49d6f3f4493efdbcef920a7429a5365b325697dd62dd2595c765f258631a1416eb32a9b45e264f788fedcb9f83f09f7be4e99f68a391207c766ff780f02775233dd74b4fecb1193a35f2a1cd4044518417dd089117f7f9391def17926308640c12eed3c86737127bebd4484cb47786588e1a8eb822767bd0aaa29bad67da63adb5d9de2a668159a91dc2f696dde23288b117850e629abd10dcb54751ceea42af3e6475a08b9e07026fdf723d211493ef46c71b6adcd4e2409c4dcc82d1b43d2608f0237d5f6edf8c30762623558049abb0a0f2b6aa0bab7b0360d2621f10a156a6ccf5fa4890ca3c95fe2b40ee1215dd8bbd87d5d5bf8e5d034b4477a097ff24c197393ffe5c035de9495dfb5ce94310a1dcb520a6f65c9a74992e63b83482a25d0f887c606fb756a75e498e3a9ee321f8897e3d7015116187701dec629ee65f975b5d2d48be9e6bd9953edb1ba6ed2217876b41cc9763d365321dc275d34d2f626f5804ab33f799df740c734a75b21d82a945c86ffa49b11ec9dc45e87f11f3d2b47971a6d332482dc855fa9a10bcb4f4d9f3467a6d289ae3a5a5a75fc4d2f6099afb04b1b562ba362d31f672d728af46a94eecedc9de9c7064c41cdfb87971454210c14886cd7064c46e4cea2fed325d3dcf9f8b120c624c7872450d554ea0c50e3b09974849ca15112c4091a68b0a7ed8612f72899452f002154d566a00e3ccd48e96a9a18a98279440f1e2053beca395f1e62e3265561e768d92b9192566d2623b06e3c1dedc856538be710c73ec55c90897c8838bc38e2f11edeabee9218820e30616d355c90817876fe89bfabe07e66bbb5c1bb437700cf3041f3be8b50f725ccaf51c57dae9e89a0734a76d24754773a59d0e418b60c4deb0d3f40817873d479370eb5265b237ec6248bf0482dfdb45c029136307ab98668dd91b2b4031ccead63a08eab09f9f8360f5362db2d7c39ee7e0c6fe9ee7797ecbd1ec1814047bf480f919f42edcbbf4bbfccc79912ed275c6de457b97cbefcd86f9bbcb41f0345dfba49535e76bb65e5df67a71c11798a7cffbdc1f1a6c602ba63aeb18a426f83f2c78898cacb4288e5a6d511cfd258aa3464a30511cb5b2b5c600d71feb58abc578d78eace521d39a770d6f6abfc230827447a577db74200ed38277534cd7985529d73b84174bb4e7ac4f36ba838e2378b5e856fe687954ff6623f0437d92e03daf16d4a7920846ecad2aa94cbe7af5ca94f5d545b03a7c3794e8d5895ca20dc4fde5cada5ba55fbe8a082b353c7902892a4ed8552faaeeb7fa84a7d05b8f6f0086518d1459a7596b4d20526b55529554162a1311129e56254d2c7db5568b6071d5c76bc475fcd49051a211468fd11da4a9a9cdd682609876844b54a96a54b94f9a6b9f661d3d974621640d526166ff71c7518b606fd547900af3ac525f3dacf4c79e267865fb20bc2ad6c55507cd4aa54f09bc7ed12b56cd95665149dc20d85b2d626ff504a32a16b865562c7b56325f6dd117b1b76a034b160b6ce8620a0b40ecaa9bd56976e965c32b2379c92b922278af83f78aa3488e64288a1e8a65f9e3929edf6bed593fd77a04c97bfe0db97f8e8e441e8d4e7631ab6ba4d2ab27b155c9ba4fa4eb60f5d10b82247a58450fabe8b4243dafa5e89e46227d14bd0c9d96d74b7dbd6a105b4db3b71af3ea7b1717f4bccdb534fa7206b339c4f3bccfeca317c4cbee6924af96d98d90aee7c6a5318fe3ced394e2ac45f2d71bb2b79db75313049915c03039c500b5c3b540f582254a5270c418b830bc88a2cb941a1864e149b2644916537cd89284aab6195d7e95bcad59a2f8486d986239e4e5d267fdd3e5b0b0f2f8dc2341d3c1729ff5cb21e383bbb200fae8461e2d666f36b334f2acf732ed0d3ff9b549ccd966f97104c798625552561628ae2c541e395b07adf5ea7bb9b250812a0b6155425fd59b62088d0d6104f6565ddca7cd3a6d3d3e678f1296de73ec5595c0c5841c4c00e384165425a1414a4ae90a1054beb8228c17787842072c4d402a26332f8cc1c20c182cd051876ae3898e8b165030c61863fc3dd9feecc9c2841a5940699dc0452b8a1c9e281a28c248e9c1cb97342adc0ba58a97718c1c840803cd0c5d54711971006bfc199be24c3ca18d66a68c71021b159a687aba320307a43091d58588263098e2444b9553ed86da448232336c51a6043669aad89db48f8a35072a460e60912b9e4cf0824986aa2a369446e2d67f35609a4519a9157c3063a684247667eec78ac31820142d3aa0a0459625b448d989226cef70aa343c2901802638c67431d546ca164b4cf164aacdcd085280c6c90e4f4a78d8018f33944638891e1cc2f01c6cd89243992d4f534c3e708c1a249eb0b4c08b1aac7522440b4a41fc8035830952543871461465c0454fb84802872d4b4c6a45c369e831cb0ae5f7d5ba23868a6383c493229a4c5cbf2ac414d2c3186318a6e079614c32650b282f5c68d3e604f5862a51e89818d86011b5841a2ba4b8e06488194a96c68b2f9631ef79636dc1b4a2614c064e2db0304eb900b361eccd5e7ddfb842ac090c1e3fa661e9b157a6efcbfd68a681c1aa0aa3d7146a132dff7d5e51f860b70829565a2a38628b3418dbf03808288fb1077eb012c30620acc088da612c7e303c26298d706c4c0f1324a29821aa6a862c4d6a78cfeb093f9c3041ab492a082a4dd41a5aa0424942184d20d1c395345786f8c6af9a50c58485c755258c80c5c8728247bf308aeef8f2b8c825ca57d57d72ff7abd9c3afdeb5f4eb3badbbf1c7c39b568c6303efeecf1a12f93c65334417f5235944d92b332f16a141e50e0040f6ca4a820458a1e9caa7a3883a58828aa5820c6922a292754a8f2418aaa1f7ea89ac2c27bae14e5b9096a094aa317fea2260ad60d3ffc80d50275c2c264660534436449593383942822a157be806191041e5ca23c5573ce59392b6fe5ad2a359585d0f239e74cc36ce531fb08537cb619679bdd2737e445327cee61eb21859bcf99ea33fdf2395b34564d19d147f0058bf88be822c0f9cbc5552fc9d72c37fcc7e53f2701fc823ffce5f310e08f084b0e3eb08879ec2030b1d063ff40be8a394183f6a882e63dc7de96a92c4bb6583143b5e54a15ef795269e4e12be60c93250f74006b8a5a51068617a09c101175c24783112ef8cef88042123a4019f3050a44f1c808a5d137e673760e60fc83a649172a8059620618be265039e8c9d6132d4b69b4a0601437f0b9064aa36f4bedb31bc9f51533a3040b175a9a5cf86277e67e8622ac00291722a8c0228a0f2540e902638c91ce2d41d4f0397b06f0eb83272f4cd1a604a9184cb1d88a9589114f79c7872815c06005142080d8bdaeaec62d0206c000a143065b80b07aec17c00ab882aa4c145b4124816577e668b4c717635c65788c316692468a0e4d6ca0628aa61dde946adca7b7448b76393729482d501a61d143a862e933d4e54f1a46cc7b1e55e3f32765b3e3f9599938a9513ca767a5f29ed59f35cb7b6ec57b9e8326cd6f3933ef79d18f0d3cf0e18bf79c4811a8f75c84117c78cf49906ac1d20f5998b4dea35b7ce0e23daf4e4f3f401135458a14d37b5ba82cf19e5780ca730b4c8cb1b84f6f89022cd8e1b3e3ec13f05c7d04c1a4cc9b16d9d283188fb14ba07a1fd09ba921c6312cac59214b941a2d98cc7b1e019cafae5c6e73b98feb5d4eb1dee52e97cba2d95ae2ca16850697272f36708182868b0e55ef390472441968ed3c07e1014597c85a89184fe110daab30b956073461fba21b834b0e4c3cf606881907344488b1020c4a9864a875499525ae8f181b96e0b0822aca9ca08adb5e6d306f22a591c7058727bf44f9ea042fbe56aeaac4acf0580c138f7d01b42b5472a498e284024c6b734c15a103bbe108aaa71c8090c204859241143d3cc60e4a81c263ffc0b4160b0e3a013da0a80184628bc78e0093ae91a23565cbca1160d41a4313b3f7aea89043175fa48049937c659a5a529cb882a5093c80891580c7ee41ad4110211b72c5a403506769e29a358de162c414d413033c5106470102e085155e9a8cf1839422f695a7d807aa98fc5006862ece3c21840ac7c1145419e409a7db06f9bbf8062145ce0784c79f3127849c18f3d88f681f0969b26c81f102124b56b0564d42bc40e904532fb8b046871d9e60b3f33c2ecc336f656cd404148f3570314d1a2d3b4c81440c3b2c610c03962494a88cf9a18814547041cbd6cef344d313fda48ddf0b8d6b9fe0b84f53dc278d00a59167b32c9169b56c8f3dcc0a7c84c97aecb426b43cf69d2bc5b0459a295400450e67b078c18ad6d4181b7e9802062598a17a26a878af88f71c34697efb914b000cb09400c6842c5ad0820b56b6e098c04b9313605c60e2ca0a8c63b61e1c4b78010513490840690826bca8008b262c6061b98145cdebb18318bb8f188e3f7dd00429f1a44a89ad258c78cf7be4945230039329a2f870058c999c9574710208d78d21b4a89c7952c396285790315b8f1510443cb276b8018c165548696209c618d71684c882a6c4d6145ed6503901d40b2a1883ab9a147ffad0fe1c724193312ee0020923aa90884155125baaac9ebce73bf277f135abf490f39e8318eb0280f711521e7b3942cd632fca5fad59be8855a26a4417458f5d571ad12d62bcc0ca126bcaa430c6c51777a58c8eb28c114f8cc87aec434b5f73b6e2038cdec003450f98a627c05c51040a55454cf19ee7a811841a99509884982d445439e1c5861dbc6cf10589f73c287f329c80668a8833554a72b8f8b23045a6e6ba614b0e534c818317349ec5d85bc5ca7ee6ab7c9577c65aca1951b5e671148fbb34f19888a7c70e44cb5869bcb8e10b29ca248942caee34df7b10acce7b8cf10a8fabacf018b7a009307058c3a60a0537ecb017c958594cb65619e02b19635e7ffabc57b56589f7968852c5850d0c555568def39fdc55c632ab4efbe54f7b84cdb2546c162597c95fb74e5647b564d1f4d74febe4ad164b5f54d0420c1eac3cb13bed530a4b94b8d20a33030a76a78dda6293244cbd12f3977a7982c4f76725738bba028a8c95272a4eb03b6b96120a585549a05192022ca2d89d55cb5a55591d519f6a94bf5ea354f8eb67e541853f2bd5089448c1851a96b8212a88dd599710628a0b37bc200b1bd8ecce3af5d769f5b32a392b139a757d842a7fffa46a685daad7e2ee94c5d5a227fefed9e36f0a4f9b98a284172872b8220b18764e8830810b1b5890028c1d94bf8e51c84981165a4ea8208621a0ec6ed3164e4b8e00a2042dbbd3fc1c7571350a893f6347fc69b3fdd99346ea469a28b088c242cd7bee738bb2f6e97b8ec3b2b14436cb6ea581e147fda3df288dc62626ed0265e96d18f2d1747951745c69249a23d0135b967663175a543bcf6fa6984ce6eb1918fcd7c47f0e80d2e8136de0236c0e7346ca67c75677e3b3d70049f23f4c896e4b4b4b26502d2ded3ca75457b59696def31e324bf52933691e0f3d761aa511b6ba4479cbb3564ed5c319c20afaad026b65365687b7b76b41c40fc7679e63bb1604df383649f7762d88e7a38ffb2cdd73a517f799e33457fabeef4a1445abffbefc093b5fa28f9ea3cfec9fd3f4888383838343358523359b39cdc7d96c16f595467ba2e9fa33ec34ab9f85b5b4a5f5350bebea6bfd18cc65f80883c1ac64c890f14586ae0fa3bab8d7abe96f5c2eccf4e6f87abda26030d8134cd77f395d5cadefba4daf17d3dfa58bab2f1f5d2e97dd7abd5e56ab665d5c298ae2d48fe395bab82ae69cc37d66a6effbf292e7e12d1a3468d0d858ebe26a7d4fd7b74a98dacae91259ab0a258831aa58c145155438ed4ef3bd24598680bd40852b24b09c4143c5cedbe1d5cc103372a89ce4f0444a0a2c00e3842b2e705999620933546858614d18ca63e6c90e686d103dd06ab5dd7c192a4ffc35d3c25faf9949505a908a020a0c499c665871227bcfc15a77e8d42c2a726e80c2c3153880a9a24d35214b14371058210a356d6e589202175aeb0e5acd52727d769554e054a50557a2a8a14ad3eecc89f7cefe9600e68bbfae8179ab7680ca228589154d9c76f75e590d1a4689005027feec71d22a2acfab78e23d304f64bad4608599f73c57b4430cc0784fcb1547bce7b1d2c8e352c506d15b9251450a6e34151e9488e20c0c667861f70191ef6f5dfacb258b95bfb40928a618625881932625ecae17c917f6d4cb9fb1a13f6def393d7d06f0e7d07b6cfef69e8366142d549c79cff35b0eef69818199f7dc2c02428b326cb29c49019918b4f172c2094a3d44cd00858b26587458533d0f9bb25e543dfeba6b6646c48af27fbdcc5a7e60c27bcfc1cf9b84c1378124ce48715212440c53358a50a2cc5b97c866d15dbd5c7da55687c6ea3c17f18efc02194011444d51c1972eb85431c9e6cc8829a100a4a021092d301801e609f10c155c9c6660e3c4971d0cd9bfd2284f91e66dfc750fcb10e6144ffee6ad122c3d6d39a382102bb042c7b012c37bef9d49f30220986e50034375c16679366b69cdfeebd866c9c8d2431554564c49b276a769b39c9a42cce07acfc1227447186dafb09fd6caca5e2931f3960bee550a543c89e242eb0414c46077d2decba14a0b4c9490841628761fb8e72014d9e012e5ad8baba018ef7991d551363acaa50821de7b4f0c1354aa40d14209a99de7b74ba4b4021d6cf8a20a19a83452ecbc7d1aa1599fb70da5a4a8d4cbd7aa939832643312000400b315002020100a86c442a1601ac65918af0f1400107da25a5e4c1949c320477210851033c410630801636004608666d80070f80385393daa396097752fd2caa80910369a97d54ec2892dcba45afddf6ca604ac9f1d17166df8771fd2b6d10c80a6c8d3c40b93516c6f1962bb116812a08dffd5f89b2fb17d76e8365fa65189c300a9562fde4f8b50222dc5f02d0c933a5539a3006f5497bb183f66a5883b0af4741d48e09350e76b0d288a9585d7edf38c18a2c7b0102b8dff251f0e84aa8b7ed394479d74e8dec44af3d4721f4508a0a09b32e6162043106d35dae983d8e43c455f32769530fb78f06da5c2f80b8f0d93226b927015a9c54707bf790c2965ace22010422b7960caefaa4eb86551e21aa67ee077e5549d95c57180b62efac3c5e41a6a3224a5d4642a3fc697877bb24b8dc18b0afadf1577ff94aa3503f3a897e684338d4bd85df81122f03b8938770187ff3b5a08fa6eed6550fb9c9b93dcb9157d0249c8e9439a9ea862696e2e534e15d98d7333d5f8fbecf5809a0c63701185fd1d6355df06ad49549cbd644f3bd9f463a63d39d4199f2379bf7eaaa7a653c0d3feb131038e3a06a99ca847114d292af51f7529ad048cb8d6c93864e172ad2dedb5dc42fda2d243e4c0688f9885d68a00981006d4313d21f3262f4c2b606285c83060c2b9c71ddff6fcbab54d9073331edeff3b3370bfada79a2569950707995c8b776dd0f47d72c9607c1e9f712f61b3627d303555273761f8bb347b5caeffff560f0c7566d0c6a8ffdde41ec1bfa5fcb199e174e6d1c3dd6bb1a9687c43ea3944b6aa0d5c5c0c46f9d850e712d168c1b388fe00b590ef2e9502094d5cd6861c9f0e1c1ac50b89e4d2a27bd24f6539c4f6b125d0eee50f1c5815c6ae13d88652fe7c4fac7700e721a8996506d53848822b0072cf1bde59961d3e9fc409c2af358c43c50ff566b71beb1d416c10c17b6bad2921139a9eae64eb1e944d6c446c8912df3b22b6296e092472dd682b7f0649507c9721376abdf4e3625460cff73da3a615e658564aa9d7b7aa140b9674a7f474ea8e5d4cee5d753297a26129462ac0d61ceb7f3cc13f0c343f4728ba1c7aa68150130c0d165bcac95c68bc5227590886f0ce2be5b1dbcda4ae8d538746b7a883634cb94284322ccdd96e9d4660af234573f80cf19b7b8c003753708cad5239a219fad01674710c6bbe3508282a36948aeb78814db1440b75a51fe169e8d56e7b6617a802cd09450ed82b7cf754dce7c22339d8643612634812368f8a9c1bc4ae53614576132643146c44027dfe3cc5d9e3557071163fe6ce38ab81d15bd97a36021014a4748ac77d79b1d52baaea3be9e8c7eb2b15dd2c56c5768f240cae22c6a3283e8e0c4bcde54fe28f4cf5cc3cc373ccd4f3df4b107fffb60895380d8c95d7aa8c99bbef2067c67e60298960dd0213aea3cde800112e9a00dd3e7ab77f7b07ef4bbcd1997babee952e6c242ca1d433bfc5d8f2a31b210f9ea09834e66bf8e9b29b2789506ee4fdcc94ebb5af1c9b29dc7bbc60361e371ecf9d4705b88d34444989ee7009265678c7095c9fd6b79264dde8cab94687f040adb890e6a72a4e8e77c4807ce1b1bb21060fc4e7177466e52e33b7f6932c623d7a5dcd8f60f7ee8f413edc8d4e6c47218990c0a6a0d9211364b93574674c43924a97a59f7627d7ee0d6d0dfab086298803002efcbccd26d22530b67ddaa1970980fe903d72f1050f2980f5067bd0529442299041709871ab622b27bd0d2e75915efb614c8b2e7f56c0abd71a3cf8880704e93fa8c853c3163e0f1ce085bc38ef56207ffd79f9cad1d920233ebf3ff3c367021871c25c137acb805243c0450f207d31e648a0b418416a0fd990007b730274a1f7aa23dc88b33ded06eabca25d0fe5b5db9d2250589e022813e055e05d30426826ee26a7a64d7eac98f067521ad1553653f90380aeba0d4ca5b819cdfc580f244c4e94720443d91250153181c6885afb3e9f3fc45960534b259b07e0e670fd01dcd1afc6d232ccbc37a0b3174b20b125d22ec66a126ef7355de909f3103020c7143f13c7df55dbd7a4c8f94dcc88d4f66b341ebd22fe9352b48b323f819d68ad41c2212fc806563126139beddd8fe411f54dd61535bd8cbcf2503b78e20e542905bd99e79368f6d39c25fa3c9ee7c9328706961e24953653a60b2c8f88ceb50050e6550e6e34fa6c188580a7cff91288d62910b95363d07c4592dae01d43d58f8fd48152d265c2447d2d4913e41d8ed26478568c247a0311fcff948ae0e01282d0d1a7490a83e6f463ce1de73836e85f38702a110764ba79067bd2b4250f4cb7366719605b7abb5a2e44d628c1a555d033e640abd03412002680b74e8c0251d59999a1a204a7158b04b473b809f51c7a7fb0a509aa0539cb49978be3303c5bee5e1fbfa3c10ed14b8ab4e302c21b40856360f6a6b38b4458cfac23245f2614abff46d350e8883217ff5215cfe5b66060378d70b0528182c06fa33f36a63395127d191075ba51b2569de746d13cd9fad600b9431253729e3e6fa5319e13ded75583802cbdc1b328fb4a738d9d6092121acec9199d4a9f31e28454d37c1f675d59b57e146d9a1f7a9351f807a92a441ea4c6a75cb8a02053c641f595d6094a7da44024708da59cc8145a70b0c4bb8e90c02bf15e4a282620ea8614d704c91df1933ab863ba783b8fb47347a8237708f89f7c772af00729580c31830388759639ac34c3968fe5628c121896233cd57c821717789d94e1f6758cf11619bc7f1081a5cc9f290498822597590cb2a2c89cbccafaa8ec7784e5365531751dfc60fa9576b00f443c8c82c1ca5f60b3680705aba64ef0c8012bcde779fc03c3a3bd4c5e0ca13157ad2ddc610253a881a83e489ea328be499ac8ca34ba720bfa7df9051199dee4bfb0b299898470d42eb1073f9cc6dab604e35a0c13176f83323ecb5a7b774d89c6430b7c9fd60479674d30894d7e1a65e42da33782e0f072a8a0a4e5dbb59437a281eb37edc28e659a77ce7ff5eec290e2fb246f3020d2480a7e218284e2d524651dde27f9a29c81ab92a70294a3987d860b3d6a27a56bc83999d7b006db17acd4dc911c74bbd08bc027cc46f1067b5a363222696e02164f47ffe0b4eb5c77277ceb72517b1829822c3cb5abb52e7f5adb110ab097b32608ac6ea37e457941bea26b3be5ce9e10fcc24ea5f7052bd50906220eb18270047641b33445516c494708d6cd2b1c4882a657996c413468999789030103e6ff1b08e103c516873ed055663633e30ffdb4c535bc70be6a370aac05a68426d780ab347ae54e6067a7d1eab6a5d87dd49ae1b990adad39af3f8c5dbfdfbbea93215392bd7bcd0e90ac5507954dd2775cd92b15582c6613b2ef649cbaac3f47fe257c24af1345862aa1967a64bf25a1da9078553ade9a596ffe8d4fa034f5788e4e1a250edb77cdf2beb17dc4299dc8e99de2e754e2ef32a6840877de100f6b77e4c3be489e3bb1518dbdd536775626e7d76d40e92f6e92a52e31173cb530c1558fdc77f2e2721eea1cdac9164ba26bea356a2cdf2021e14e90be2b8b7803472a2951566c6f01a1143ac92631c478d92f70132ab248a82737d6d2ebaf25b5f62777b9e922ba0270a9155e84856b3dada5436a1d09ef958d57fd5ebee03e3a15da59629ce146c2bccdac87e266570fe32b50d186f2f152d01170274719be90e03e15502279e4513fd7b753a57bec4a4927c10ab6a15cd89afcb19d6b03c2b252400fc52f588293c48d81dfd24fcca82497ab8fad91e0cb8b96f46de072e00d6b997078b9ef982f4bb8a7e6606139b8858841b95983d71cd87ac27cd6e6f4e162e396bb5312f089df5fae0e1153681443022c823d8317912e15d5232ea6e7046cf6b16588c56a7ae686b07428301b2308ef58a5040796edc63903af35df8a893dc1a3257a7f447a5f9e0055363a554a1d309b77aa4a3e5dccd21739b833a76176529f4afbc70f150be3fa9d2cd6adc447d8c30680014b52d44380a89153abe83147fd226dc358b5133c7c6e3350704d7be277fd2b4103181e2f7bb982ca4dde1d79773fe51e61c7d07f6ef43336064fa5d934faa8fcf4c04f0b0b1ab63e25af7e3df8c5ecf0841600ffdacdffede3fb1774dc2d4150d5c0179773606133c49ee3875a3aea4a3051e09a7d66f599f4ba3a13f1d75631b2e5517adc69b07cf82e90935e61d33e664f8a3b005769b2a3aaf563f84929c66d59e3e605395db1523775fe4855a131517bf2b14701db11be80ab58bd6862e71455fccb8ed6f6fb911e19a4a05f8c8705af711e359473d2b85d48938804da2d463389cf4c2e722b3c35028e7a9d687b76f07ec66aceb137cf1765afc10c426d00aa713d46f57d2ba51e7667fd2a486d40c99751aa1f99102454edd1002ab6b9848c6d83b1eb9679318a206f512cccf3969fc7abe18f2b7c35b89fe11a8f6454180f40d591904c602fc480b30fdbc7e1bce92b8991b99c33eed39955aa73e4eed349939f7bf18b190881b583918acf6392faedc0562a2e44f0b33f92d8c62cf9c16c5dbc8ed11ba2da500fab1b54fdc78b504f97c1fe4797b0d99cb681a89e8c6b701b5c9185034ec61971330c1e0eb8e0691135554c563bfd80efe3553245dd325968e58684fdbe140bf3174af4c73233d49fc57fba23cc2084bf3d14d3cda4fd6d01419d345673d28703da7b78f830ea2cdc72ceb5703266839195f4081ed071533ef03756229f6d2a75fbf596a0b37fa31956b0c2b1255e19d833b9477dd4213abb53063ccc9baf2fd3dfb4611fe8e8b24d3fca9db1029b46e7d98206b2498182d2e60412cc6cc22e765c0a57f86adfe18d4710567746efd0d7a3d1680af8597268c5a69c0c8778ad7cc829415ab9c3fe6b2c143c36407a2460f2ece8ec9bcced8fb1c1b57f0281296a41277408279db0c7c5616fa573a20c6c11b584c3b5f3c464f6bd0d09d284135db126f7ee1e052d9fa9ad6197bebb10da80f29558bc8ac54eccbed99c4395d1a4f63f40bce8bd225b070a6f68f763799da10112e3f70657a520bb6b1f416bfd16155c5f4ab908aa146ea8bf4fc32309c175c7a286a783d4cb20c888b6714066b215213dedf07dd23b5a4c79778fa7554167df6538578a0977e1a11c9c29c49b117159ba1e23fc6a018a29c32952b45a35c32d76e682609b437f8d934c250340be301d8264719125080b1d38b58930513559018c4ada82018c871c2849c297ed114751560ba9ad617b860fb4405cbe680e23fb375c40d7eb4435cad8113054b86a4cea9042941561140ca687b61e9d15063a13cedf8e32a076c8d4dab2be2cd5cbef15a14b28bc33a63061bfe796eaa384044b37e9550efb65ec3f0ff6d726f6753b0d1033ab2439b21d6058c590800086a1336b961767a9180bb650be608ad509fd4861086689bed70db65ef86c8fa8738eaa899bb0f34649bef7dd14b26d50d7ad9304c19122af1ac424604c8f905a22c569cdc5bced71235ef40c9901ebb86e03e367575f28b2b252b4d0ee861d371d4a7b0416878b11890810a0d266cd7d6e06d3d51dc96bfdb9836973e405dd018223c4a404f5435aac13cf722ce9a044010f3564fd126e2c8e46de5d9a1ab29da9949155534b8480627ebf179df791a1dd8f3db92d444dee63e24254a6c280e0a9a572d38f7144c39b5f826dd564ebb05b4d847a2eca9ec3cd043fab26a6d88e11570f955936274ac18acf8c9e864a9a36d974c2da8f7eaa508c08816dbf173fb6285ec56c81b1885532f072d3e5efe4e3170753532a781779f704a01cda5be7a4bcf5c02c550774b4611388ea150a0f708efb06e5484af4492a9687abba9e6d40a3c4acbba5500d42108c0e511d516776e808e40b275229aa46be4527063422d8ddfbddba18d4841bc3b2e765666b351c32217919a66fbf6ae3fa01644c85a8e07fc39beea9f5f130a4b39cb1b1c900e2759a1f58003257a6ea4ef1657e8a49a7a908365f0cc12cda38614620054833944864471cefcdcbf8f0a557958bd5c2849862a786838be480df61c9e33cf203b670bfdcc0f26e4298f35f80850b95ba0414aae469479dd89b21ba612a6dd0a98743db1246d76f64d27047814e781a3117cd522b063500ababe9e77457dee60ae1235b9d327ff4988c85480398cbb295a109c04c63736eebea8b0617d46c257dcc5086f4fe4052bfde23b7280eebcecb463dd60617a2aecbbf51b8a8899feeeb95e87ed0462bd3f70c700a2a53fa4d469520223d385a22201429638a74285dc416aca7051f7e5c668d240b1f0868c1bb18eab82c8c2ede018ab7b4ec26f8721842c165bc2e230fefc5fb519eca6b407e803187488b1af48ab24b7e7fa62d487d99454c7e838b90690dd702e795b881069814c957e8f3a789041a1f074ce7b5dcf1c2859097881bd0028bf293179043608ba238e9e29c7814722a97baa8b815af73e674f5de833466bd979ee79a04d25099c62f735afb32240de17a967d94fadbf92b98fbd07286b4d6c6f77f2a37ed1f7e3689b553944ebae4c041d379c933c0377dd8ac70e0d498dea435c18cfaa6656b653946797812309131095533a6ebf689c8bc4cee61faf34c64418a8f9af9a6a0bb3d3b1647bbfd3b5f73bf099a70aff368caeadafcd443ce6f32407f53a1019171fa713c25e8e23607182c438f8179f702216c964ae6f8d6cf7f9f2cb872a93edba365968d8d65505461291f5238f70c069d21a6326edef7bedcd7beddf7db87c17db0ce31fb7236b1443b588b4cf1e695f2c8ad450644adbfa042161343468ab68c17346e2a4c6432c5127fa131a83ba56ea43b53b387a40f75baf8bd1b47ae48fcc3929d86425c48a869cfe91faba83959871017026dfa73fb035db2a2c848895e5072d158840f11357da0bb2110840c0d86389112d3bed33f96f642ae23c48d40991ede9d8a0a21650da2fc489469bd61fe525c4c876914c9438c4c5723e8bf47049306adf83987905e36c648958903ce4230cc81654a708aa719b3916c82538c152007433d09dc76dffae53d88d0517ae61f34ebd534c55503ef99b087d32132fed8163e6ab738790fdf088c4047d9cc907393337783a6312a029886be0b37500dfa64b8e4e5ed21a551e128bf3011313415fca35116f9534a3a2cb03e2801def1149408d1f3872e4bb46b45d293457ffaed1657215233fc1c45c1b1589c89591826adfef6a8318e2440defc6f5ed97ae5a48fa137c2c643443b7b209f44f1166449c3f8b7cd895750786a3b86b25fde661938872523671b66a5b30e1f4e90d17696c9306c6f4f3f87943d9b0a091cca2a29a4e490e2585a2b2ea42aaa94609bc9b782cbb4ae8b2328336faba86be5e2ab2f1ef6c60ac6a3b124b4351718ecabafc497883fdca09fa20a6c9ad2066506fb90125455afe28e61075f120fafa2dc38711160ac26dab44dcb6088f15aa822904c4801f75882885a32988f8db392bd94dc1011753f0228f34cd23e736c4aaea7ee17f2b302f6626c2d84585bd5c628c14cc32fa6cb28581d29a9b7765eb231a4a63dde9b7eb33d212f6220e7758ae19f5b30fd8a33e99053f6116c03ef1902b77317fca6e837d367db3d2fb2c285408ec237799f51cbbc2afacb4e0cca73a7d268cd2ad295bb21a914d2d613c2a2e7734552687bb773e503a742f0106e8ed78b37595cfb7f245a46260142b2780615e1d9a03701c21d15a09e0cb18989e97546e54d60404eed1bd40e4185b02d7c01b840124c7e87f6192e0343f74aa080e7d458f28719b12a3ac2d56705cf70d3dcb6791e945f108f92c1c307c6cbd2f899e41753330613c527dddd0fc97bd30eaf9160f20001ee72bdf45dce64482d9f1b3839ae08d7f4cd94e4c9ae47b4bda613a90994ccc54e9e142d847e0522b2aed31622d70c6fdae4309703c16a5cd15dc02e65cc37236012c1151854187b44dcdbc35a596e204e42bfd51c16ea7fe39a76ba4cc8d26997671289b60232d4b69c34a909a72097d5a7fe7e30893cdc7776e69e22b7d0ac279773872343ff42d34dcdef8d57da6a37bad8df8af49e9719146b6cb4bdd7f061bac750cd962c3bd10fce18edf8416f3d690085eee8a273092f7357725b97b046ad809197829896fe7b240da38944212591d3624b4b0c26ef3b3a16c506ac33af20aa59920851fdc838dc4c2e5f7c21955b2bf5c48795b707d16ae06be63cfdbfb92c49567424b8da21b319ea51e13d183720483ec1b8d67ed501afc2185287548176c4a6e93d347d95edefa5a6974686a1a58cb59468fb4797c42bfc9d551ba2fb83bc2b63be6cba896845cc81cc2366d9756bb4e89c28493f016082c862c0b50f1a62c32d85b96844a5e405cc1a29d325e88beb4b96afdbe438fd6ccb602f1b419c0f84811c3253839d84230cc444e0ace0b4faae7d80e4401c736c2c2b6ea9aca9f372b052f5a7c1e612ca4fc2060320cb16d685cce6ba7b35701467bf4dd8213cbbf01a2c73a03cf064d32f9a96008f524b7c3774b425916c4ac95bc8559139baaa1c98c98c552a4c592399da176013925b33ada245c2549b3920140565b6850348e399ab69f29c3761a4dd14c8ae62a448e6d0def5ca65b1deeb7e4ea7f7511209164c942d766948310f7e94f6f23e314133b567fdb2c9a4b9b7753bbb8baae68a3619359dc3f7584918c8f2b283100d0e0c8dc9d1ebe3dd822b5599f8a196a324c7f2eb1619f86326724680488a1e831c2a31dfb8ab25a4b0e0abe2d26e86d07de89e6eb01557384d6f70a4ffd4cc9e4cb37a7c02e7c0e7a1c9a51c0a48c4f89212af7c86d5863148d3c0dc497ba1c76cda43d742b19306d9f5c093d2f400b7073ab888d8edaccaf8a0063e691966d3898c4275089753f0494d17065acdcb2763c63a5ebe7f973002240307fa248a211a11dcb9ef0c62c7a74f9a7b45e7894fea7be35f2a1e094d6f9fc070b3ddec981225c04b0b8bb7b34a68d31c454ba9af365e601c49f5bb19c374d4693669b81bf979936c4ea2d8d8dac7de6ef25f7392f1fd9ea33989742380d5252ec0d099db9c8cac26d37a3eef126a6b6aedd6b989af5cb872c8143317c715420ca22c06bc0482e8dba55d948741dc372c7d487612486b6b012f1debeafad90169ad984f4376ace5e1c2cad7a51480aa7f7a4e099a9370273a38279fb0d222af73523c55cb22df9d938005d77d8d060ce9a0317d3719ac13ca91a14b9f8c1691c0ac5850da9fa670c75778474ef0176f36bd2f0f4787d2d890002dce26836b042512e5e80458c2a2a45e396e1cbfe82097866de20507c65580226294aed47fe01a44cd2c1a9a2885c6b7f0922ef8651cdb0e82db0d1c8646295392098633425a898cd2a06a385c2227a74b2d00033ecb33e5a49850e7a277d6393a16257e15a02783a91744e93e4c39b51bf03acf078a408cb4631eaba739074184b84154309e8d773c3846c11c7c0dda78318caac272f2f7f8649df2d6f75bd2ea4119bac9073b455281cfb757a4b9c66190834c5636164fa71c6dec805412ec92319e615d9921b0d3e09460e356203d0504a571c340e39365b3f62d136892dd53afccc7f3e3ce7983356d4a9f4a25fcf04f229122cf0c8cb8dee42cedc9e8724dc25e5c1620e57cb2611b963d17f29cb26a5cd81dc28444c3ecacf864d6c15b1cdab08301097345ef3f997534a65287d1624377a9b379974998758a40e204a4e03db17f21a48aad6100a9ea8f79e40fc5234377ba8d68fd744646cf156d5c5d0450ccbc63b819cec2f0d0baf2de4220d3040fe93375609f693291c75fa86ad3fd7366c34162d4d36e4a424a94a223cb5c3c2b3960510fc80e0339cf9229a9d669b20c3461549385fa26986409db783f7258199ae0651a5a41d3de2009f0c7bc3557653d9599739a1ad45879bca68df57ea3b0bb6d8c0f90f1acb78c259f0a5b6fc110c4e9edb0e6787ed020ed61adb72a43359ed55b0d8ef8f0ce54a6ff5c13c38819903c2435b073168cea6572ad8caacdc8792f43b5d2e4e544e815c90e89a2ef826615203e57df700b740b851987fca4dc62cfeaac25e6f1a9318727197b4748028220542932fd39bc7bd6aa48d95c234c9349a44e5a64d6fe6681806b83cafb8c8b478a5262fce823427179553bee36078986cdb5057392fcd6ab076d8b12ba09f68703d33590e2187073e80648d5889f9990e8431546ccd8be583abcf365f0480ad5d88dc66a3b1ec54c8caebb89b3d34f0632d458c9a49c3aa0f855fb335ae59cc46c6b8b13ffeaa2822ac4084df5af67c1c6a9c29c4d799d23f9f79a993cb0ab2082db33beff597d5396dce71f8485f3c21fe2dd0fdda0cf6e0c3e05297897fb0e68f9c3e94b957d84491fcc0ec3c2ea2d0795e39f68d9c1f41444bc82af8f46432d8cadfdd0d24ceef12cfb1ac0cee37edd77456c8c98fee179ca990a153ab971643d6a578177d0a0469529ace186986f971e0e69e337f6694a738135292774983fa56fbc997e29379213bf12a1f61ba88f0fe72d2005924672b4cc02a0a2445dcd95ddd99500fb29928ee515ee7ea86fcd8e35bf31a2f369ecd5d230d50300d2341ada573fc827cf1a92da7564da642a50d18dc71beef3261efd185b6091a120e67ee4b9a6c1f714b92ecb04dc2aaaa390d03c01e2b00049e8ae1f7f47a76ed61bcb6189ed853868e3f8b6d6ce61e88349a472db802f30761796ab73c46fca4619d7267be5b08006a66fbc3766e3a5c642a5bc62227d81edcb5cd2fc717f12680b90424bcd70b1072c0e1a726e59464687d9dbe0ea07629d2cd7ef0237b9f86d3826b5202f9f4faab4aee016f6ef776eb5abb1e2651a3e2dbbfacf458c8f464d5d48cf9cfe1977a223f8f87b03258805f0f716c0dbb9e5c22785e58846742673ff76342cc7eec50f8fd95723cfd4db7867ced730eb57152c505220798ab9ae1ad045568d9db800da9a6071e42c270abd1c357175a2a962fa5bf47c28b4a22d76360eb1b392ecb538835c89a5e556b00d350d429cd81d1d0fb4d4888e633fdf2fd4c3b8431825230a8135d5f7771f35243d42055e273dc0dd4d768dc1ba0e02521e403e4bcb355f1f6dfa686f52c6cfc5bcfef2b53e459b85119ec0e3930c7d344567dcda3d525611df2396eecfd343125562715c3c0cbe014bb940d50d589af523c43f39c153956c796ecb95d1b148abc9a885dbeac426371069d4c1a56a00f50e433fb194cb2dcc3d39524ecc133716afe614c5d9b4f664283246b8f3108dfa1d181f5cab825160fd28a59460f2014d15836a9bf50303f61f3fc0c0eca2c9e278b403e4b214c7955de6eb48c00c7b4bac63a6a398c83156139944cde19d0604dc6c446513a751f07f6eb08a34b5535436a52d0aa78aba6e5479c4de0bb8cad1bcc01c50a5f0392b0cd482092c7f34999d5b653d1f15d4e14c189ca324f38d3e64821168e25c9e068ab66a2ea425afe5bd3b319a9c34bc5d4d10ad5a6dbcb3dfa6f7d27a3cc7b1d121fe288f67797dd6e6f5e783326bca3cafe44e1f3ec13d820d1d7f0e026fee4612e13ac187c05b82931f8cb75844b97564f1d0530f0893880613fc20c0c429499c2e0f8e78f930aa1db8255e02cbe4f2de447f3df2dcd292c8f6c9dd797f3fb3a245c02c5a94be847634f4e89dddadd494f9b45eb5987e7890255e782cf6a9e72abc30196f4cf382e5423f5d680624a77aa271c0800d4c77aa83c6af2e07a5d77f2b033399d8dfe60bb0db38cfbeaa2d04563d54d57f9cb491174037a20651c6b93199633829142a50368027df0c35fee899cb7710bde538fe294dd7181c25c96614a55526bb4c4319bc843c42f3468f2ef95422c13ee26a107a8e179e3e6e0094dd57ea4cc8eed49941029e25b340b9c08723d89b8779e066dde43d7cc4975bd98b00127da42323d1eeec6b50b15d72245aabb49101da5cb7ee105655e73fbe0b3196520d2c1e4e908e20b165290474d1b89a0f229903793bd4c40b79413ffb99d9bc23d7dbc50c1967f5ffa8d3475223a34dadd25bab543e0d9eab03ec520e0825392585bbdf98c7b0ea8c18fa2db1432b27d1ce44ae52527fef4e81cb4dc0e94e83cab9c3b44a368eb47f18179801833a114ae267774e8f1e8c3cecf03310de6087be8ec01fb02218ae72d38adf4481d4c667a99a53bb42052cd154e288556429dbb00905aaabcf9eec4f2d54a8109ba47984d8041caef5a66e23306f56597094ce35abf3f690340107a1a1f012856410ac4be93639a88de41aebb6ec345003afd223a85002b805375515934a877a549cc74ffa7c7a7928f36df2b2d232903adc64afba42859914e5d1c112d4e79a5c2311e694af94b6727584b92c3593009e18ae5582239cac671cbcb1c2204039cc0e8053ca201c2e0b40c54823dfb56853391fd2295ae036bed003a9d7b73061cd35b8eb0c9fd50c2587314659548295cad178e1a39afcfa07eb9b1ae40d0ca17a41919935ff30f83b8647107542103b0dd175f2f3cb78485027731741e34eb99dcdbc1e3aab994f915da379409f85056db862bd4dbe0e03ec3a0bf28e33d2d3f007a7a7bcdd5396bb34247145cc93eccc51764108f93a87c3eab198685ec5ba43013ed93c7d7f1dec058425ea337829eb185f9d1cfd17b3b94ea61d9200dcc5067880340174b2d3e2d425f2f10618694297f9939f5b4cbe627d78b0f6c15c68dcccbddf88c27addba34f4df2c9063d2cd0be7fbcda9cfcd5426cb1ce0e842142132263eb42c2bf60ad2adb97576437fee5ebc8b1c672690a1231cb57425624fcc6d00d366c0599e1922322d4e595b1dd97bd3a2b48b5b46abf3872dc0620a787b9d9c89a8a59c3c7506d8b2b28f3cfd1d2ba4cf97ec3b8efd9f65599d8f62625d3d156c5cf21ac38e94971ee69b3830fa13a0c135fe25ecf1d8c8e1245e33c07dc487712268352d8d3df02c429c6d80b04825076b4bc4a973769033498a6cfe8d514d1358c3fbde2b66054413c2833fe58a119749fb5344a281daaaf08c6042a1314d2623b2db85ac24f0355c7f2b8fd700aaf2dc6ad2117997c4cd936486b1d2af7c2a616802ad6797666d4790861156383e7ab5dca2c0c4376811346f0bf92efa3a548a1bce1c460e7b537cbcb7dfe151c1d28624aeb508b47577a395cee21245c9c8db9cfc9b302cf9aa613ca306651ffa48f8e06db95d82937a0a5946a95e314475155afc7af953370a6c749b3242bca69f765e081f5ee06aecb9953b93fc8214013392e846ff3ff7ca4beaf1ab6d83747f80649e26ba2e64ffc7b3b4da7c3eae96f46485b343149d69543144009c510828f140219c7f4167619af904d796d643ae80de78b0c7100381a6c99ebc9b3c1a4e6088c9053ee663a2246cc021403afe30fe75d329ea609013b12c8f42a624cbae0d62cb03ae1ff4e782c3061e6c4d46e5b0e354ce6e0ae54a1212d56edd1d087b75e950ffaea18e9ac418a03be139021791f29fbebef1067d96a1f9518158e2c73562242cf1a5a6d6f85e7d71f4e30f851e0fab3c67e545dd080fb4646c40967a745219a944d56af50a16d7df46dbce0c43e08349f64909ca10fc841987f1d30fefc26aefa4d83e3ef356c678be90108ddfb0e5d4b52d197e45825faae6992c793cdab5afa1a4211cd3df79b0e29b4ffd2226f6eedeb614cd7fdab91c64d9667f758e28c42fb40ac317f28e03fefb34aba05e46e0999a9704fba5e4c458afba4588c1de566fdfed93e705de961c08fd2175c90264e00131b1cef12b89b1ffeaac33128d8303b00d4f7b349a9e2cf1703bcf391ef86deb16a721ee457bc37228529ce48bc9255d6fbf257577ee9c05d56d4066f1e074b415daa8c45b80a98c38d26d21da424cb29ec0b88d8285654e8514d460dd585a713b799a94586be21edcc4d5fc6acdde15ad1345043e9e49ab3f3993cb3760e0a34d5043d9c47beeeac51a5fd0c5dc04023661712d4aa29d2b83758c868322683531ebf750d229f490462695edd08a387f37046c0bf828934fc0e41779c84d647fecdc84caed3f1954847ae59efcd9c59e4ddb8c080ed0a7b21d5f5bb6d27378786f0bf0cecb3d7202a0a77aac42e4e1f8ab9f6fb6e2767ee4e23b3f369268c6da125fa10a6257b7309a0c26d3cf25fa447b45ddef3b46ec49c376ab8edc7fe00a6ff1e9ff884f8116cee01271d9a3b37be86921719e5a7973b2afc6978a22379427420168917143338f9ea353087ae0239c7bbb943a71c1490bf6a5847db8d67670e3a82c19caaeb53b2eac2c536cf7d5820b2e04d1d425c86fb736ea07622db8a1d50a57c828de9dd064d5b55bac131d7ddfaf98d4796a1828a1fa606db4d4f57d520caba82c24c4e76d6eb827d5d1e13bdeaee0d9c2cac640d01ac08b28d3534208ec2cc56f18f6e9a6a869e5bc26195335c6c932e1de51273a6df367c6d6e444cddc0b6dd6a15cc764f7ebb29c0dd2999713fb305bc82d97c8f6dce2259c173756b2af02071991993bf813fad1260f8ea9bed61c4d34444cea500e649e7e4344d26266f8c2de9bdc5e61a3ef0e4bd460b16ef169bad8b758821976c6a754b1b2dc86933d16a6d4c0ab2b1d2b004d44c1ec44b4c7c378be4afcc58759efb38a750d261e82dabec665061080504484442ab143d7ac65d84f3c4821c127323f26651dabec063165af86002d42314cfd995286f8552d17a19487deb4fc6790b2c9603ecaffb01e25e9f78ef23447293d56e9db664ed883196cd6144a688868d99dc9e86551da9e285b5a24511e3e5c4fd87b8fec945299780df90701d5024ffe6277c05d7ca2e873cc734b101794c05f6b028abb66f681d66d37537c9b426a00cde40afade188ff70175a918d4b73083436bd0549d8680ff632e860d96f54c3b72a83f20bafc4712f65b93f29320a8daa70abac7e038a8c530a0bb664afc03a2b2673f23b476d654525954779cfd627fe39e08756302ff3385d3ba0981b5d9d77935242e132f3d1bfa1a378e9b925c66f077fa0ec637a709091fadb7a7054366b92cfca89ba8436ccf555f790ce12c08cc41a5b48df22e25fc803783a150fabedb540cd8881aceaba0929f4fea2d35e66120f2d34d3705c45c05f199859cf770619de395cbad0a8f2d049bf6a967a2d42ed9c176686aa30d8352d312f37cae8014eec06fde292c4b1c89a12d1e73cd9ee964a27ce6d3389b1745a975b6dc0fd8ace645ff7273cd267732d9d96dfc0d4183c27e6615e9314c629b7994b993def6354c12771a36eb39926192614b59c450fd97596494018534a9657b82a25bcda45cb863cfe8394e45a0245a0fb9898e8b1970ec19ac23d4725c606e9766233fee7c288fd2a8ed2d71c41f3cc6207a587b1341a0327943c0a0d25dd3a787a2914d428eda7e4092317599272a2c29dea07d18b7aed82835cfa441627b455a4b8c546cb69874fec0863fabce6feb8072c8cae5dc83b90a36441f85fb09442c575078d165166b70250ec7bcebab391b3ef157d15cc6831cb7b6514ecbb1f82014284761853c90ec3ac957fce69f21a293f0d841ddf0defcbb245593b522f1c3a920f3e1823188a3f9b340850dd1d6d15b8bb702f7203d6a27ad3d8100e9a810a2a1ab8056aa1305e1928f7fb5c2a42c88c26d6f2e4761430d5b0eac278211e53e7ec9826bbae349d46cade10e885302d6d9d6f471ec23558d0459a2a8f0f52e8fbd1cab9e3427a28be221b8e21eb00a3ee5c2f9188cb1e9f3519cec765e4e89182a55f83210d9fc38e4ef65b79c48a6bb821e4712d0ef34f51b8bd9111c403932312fdc046916f27c2794d739b9819a2165ab835a39b90d41c78981a41194c2ad2327a0c97dd9833fc4d26518c9e6489c886f98a710cac19b93848a4f32c882c5c5d95503f8a37385fcd0401a89c17b06280d0056f3b62b03c21cb003718a5047ccdc225bd15ecd18711b2470f01e31334204689ae31479474cbea7aed14032b5763af136d21a2c2a682756e156834550ecadcbcd57aa88f5cf2a5dad7f0cc21ea564cda31d1ffa795454aa787171001827657d0d10676a9f1551029da858aef4fb27dc0920afb09250ac42f7005e4f52c375d8485ac372c2dc70c86dcd7e295ae1dd0f7d196fc5813dfb13c0270abf0790f4221143ff33e04b45f8c180012d4275192226f3405d486bf11ac30977153a1e96713ac0c73cc9b0a08e4e283c0f100e8d8722e04f95698f66e0c084e8076dcf3e1da66d0971530a98f655450a847e06fbba8ece3033620192d41c936e157a2292782010d16c6e00be0cf062573674663bec0b960d390860cb54d9e06d4214d0ac9e464026687c08386854899c5c06945e34e06e28c14530d9f27fd53e1fe92946c144a114b136631438e91ac91a54015901f22081deb315b3e18a36eac926e0c0b58f6cf10ff968aa94a9b985b0021629f1245ad6946a6f5976887a2aa72224593a3228c33beb43186e97d46f6196c64cea6b889615655cadf036ece2224ea7aabfedb45a92a9505e944b11eead967d99c5b482c86101f48e67df3b521ea9c1ec5c4402c1d6c921a93f7a658ef63c1a9e6595bf4c2539344c4c90d01b454282182faffb1932e23cd20a20a60a61a0bbe3044e95246cc3cd641d1cf9adf7de343a5441cd816ec347555daf0009a4ac719418413fbbc657f115231d19650b98a149c2c02edb47eb291baa2b9bc029fff209621a30672c1c8b285a0c61b215dc2d8cc0811772a6e78ae0930332629e8e84e324aed0a5a4414314bb15ab86140e4d37c627b86ca259e6c4a72fd36529011a2ec1eb7801b5dc19a10f9cd38bc5c13892e2766b7e39fe622f1190bf435beaf18af2ae1f0888acdfd26ae7430a3c238a20767fe740495b73dba6504d3fa892562ca7513643dceb130ed4eeb6901b3fe45a7bb2d6271fa5f30ac3c85f2aa4642294d0bb1b741cac41a1f755f0c853bd6e4f72419fd89aceb6266aba016453999b3345f2f81e6e080eee1215ce7b88270b4856b854c4ee6a7abaf9424a33c6b44a4b992bc248ddfc0bfde627ac9b7b7ead095aaf5a36b15135d38842b54ddd5cbd2b909efc815c304f6ea24af2e6ab0d677ae75553acad95257f0636baa0a89010eedf055dd225f2e6a4395b2151376c8571408fb91fc491ab5c50c2e8d5db3727623af13019b82bde20a509ed830c82cdacb0b7b0f98316bf395856019b7e5ba9edae60e3355f33f838af9c0a73495b63a21fdb6a9e2cf4fd57832ea30488aafb08285ad06f072442083c1d38276831e6864b4e8c5171e0b50616c3ada0cd75f564c9e1b20c0322114099464c83e7da9e2f07586f7ab5db06e0ea72829deb1f98f7328cd1e191f93e929be4b4478b6c18e7500331d4941bcefd5e28590c0f29839570c365ce37d2101e33419353e455208a9f0f1e61899c7be9670868e6c79a08ca76bfccb9841ac17e9b73a59739b77902ee4788eb38bd61ca13973947159d24c02944b989b6c788f2041b75dcc563cac1f40ed290a03ccc0da072fe406ecd7c2817807b81ceac713fc381fc3584009d5b6c0fa5daf449e6a74f29cdc4394000e89c631d65c20c4bc348ea88c20705b535a180a0104777296675013a5710c5f3746b04153f8477740eae8c5919fa4095d30f51c602db00d978928019a31d9dff6e3a57c692612971217a53ec35ee89f2f4fb2d1b053f368283a3b708e5e22cbe9d8071125bcd1af36884c44ef6bc7474ee311e592c2eec062428b09a6fd24421333d3465a3a90ace541eef449322e636b4721711e1a00a129365a910aa0d116a3887856d58a9b03ec8ecc080648d2b671121d3b78477d329683fc05e44340ecafcb9dd0e292238a15cf33cd8447283dee358954aa711bf560d488b6983ea08cb6338361872e4f743eaf10ec68056410c770628b6020b68213d6fd844e1f49f83f118e421aea2e6770d1bc5208cff0ef1025f0491725e2477250ea8688e8d0194728b770ee2cd876a3b110cb61484c485e7df7e312188f6253d23a06f1bac47873479cfa66a29181e839073e0a629cfff6aed6ec3a640a167515888caead934497d8aa199a78c5b7012ec74ca7343911d33d0cf476908f0d40b7d73ebfcdaec3fc5f737dedcaa3b90c7ef680e8587033b26005b0af56dc9aa4527e50a81e0b8f89c7b2cd26ad6014f481672829c19cbbb59ce020c03ecbb32fe64ac016dfc86c7ca5ac3432e6a4802ad866c09c054472e67215143a2d84c262e03a09a91781815f1b7f963c3ae8f62cc88c0ce52cfd1154030523bc0f684a44616662ce458c1c1bfd4f3ed9a6762b65c6129c9e121c202e47180006948664b3d5773cce71885096e8432f49631adfea9589f89a8fe0d55a51340bfb9b0e7f8d02b7c08a5c4466097423cb78dd3d973f64dbe57604f9770979d28927401de57a181c100562a019e91a247aad36a461fa18ceb0062ec5bec817fc64d0eb03a59608302f22d4503f0e7b078a6bb903eb6b24416e7d2b702bc7e5c365a06aaa844efb3a9fe2be6da44d15ddb2a488aa7233917732f883a0f0c169f6ba163864baeaa3e4e76e5f2b930d50551b75645dcb53b7c4331d2105cd3337d1596bb53d3c13b6cf139a7529582826e08215444b02c3e2732ebb88edf87f6c6a09428e93be93a1dfa338cc5e7b6ee8288ec7b33e05d017773dbdda15ad6150801c0a8effe83a06fb050edc36a3cc40a384a7b6ae7652585ee5a769c82e53738cfd900f133baaa8f3ff42b50acb650699f0f2404d90df202d1430fb0b6621563236f5d450ee5e6f0baab4ef0a8a460727a3e97b3cf2fc09e3fec35b1e0a222e10f968b981f2e8ebadb7445b4cf59645eb67cfe9f53ce88f6f913eef3e3b964689f7707f5d4fbbcc11d8d0f0ca58ebc4e7a8a1621cc01008ec51a84d91d000c85398d214fad830e6820c65c9420dd3a2ecf2f0b962b53645bec875038bf99c9bb055bd0bba62ca2a23ebadbfce34b8fedcae3c9339e238099a05f7b7ac75b283bafc70ba509b88f779621e15ef598304264394a6991e585794495efe768156997a73f0e8dc6e2008d54bc00d8fa7eae87f077574a0a7b3c737ea68d987a0572336ed52c77ac6539ea7ab6ef7596d4427a84c4ade0bd490274b57f0efe8d71e98f21426e513679fbfc9dc2b67f9e4a3b393f041bac763b3b0dc5dbbddb6c17314a87c8f89729c1b7d671a3c5c2ab8c567807e8e6b61b8e0abac9f68047a259e0de5234e71ad9f09d30e99f7770d9d9aa39618b30e2ecfec9490ecbfee131d7d6e742e02e7fe6c95489850be113b2f2f808e72895a327c6dc007f716cdbde39d6ce41253bffe6892f5700585e4668a03d30a17d225a48ef6c45d118f308ac3e98ab113d72f844e20d2703d2fecf9f1060d64d48ca00ccfacf132bc048d5fd4b1f0176f44058d46e335b6da2486fb83af074fef124824ed5386aeddd4b51d275a89f47465a4b69b5b23ef5bfa083aa70e16698ddcfeb61edb877f4a10b7d7ad7875085764d6dbb4862f773c4a546cbdb7d0432a1d91f70d43e8dca31cc154178e576b7efeb8e150c65705fdce0823f349561070f6352d409b404b20a6ab2cd54649de1a70afca12676abe4dcb02dbc760827900dcf89d184120d26e41a926fab630462c21625e057ff4735668926293f592d774015db0c640fff079e25892dac024ff8124bcfc1beb7874c5f98596ab2ca87cbfe4ea806964ab75c4ac596345706e6074f7a9e69e86184ad2a62a4c6ac6590bc9182a19d348517be2e2999ef35928c4f0420deb63d2c293246d646980ed692c0f6a6fcdb826fe4ba5c4b5b3f86aa5aeff9c20399be210412aa9c42889af478b2e08203fcfc0e795d2ce0d2c6a5da1f28916fc46559943c4e4af5b8dc496b100022d18bc1ce1edbb9e17b3d90e9c1a3c00af06dc03c452d38efc6232782eddb56017ebbba3edd4b46ce456022f242bec4c55eecbb164a29a5447f31e79f51e0ad7c24330a40ba0f4f8ca502639c4d15b944e40420877ecd159d2601204924a7642aab04fa9468b03d46e69f12ba381a2c52bd827a72f6567331b5d266082cc74f5fb48fb6816c3546c0c47caf9f77a740f62b2b6a43b120a30a9c82aa568a180464e50dba2b0b00bd63002a87d10747e5eba04dbb43a2390d78d4006996df2351742c765a0bb2e3dc1e1ab2ff4712223e1d32808aa18c6d284bed799ffbed981e4d267b42d195d1c46461384912e8b4ae0c13148e3638c22c55af43598f507264f3cb31e804f4326fc7010bb72ec834fc9361feace9d2e50619b565f6c336d150ee8830c6da910624a2f8b5411c9a364d62395531ab767c5678d93d0dc46a7a83ac3bf40db2eeaf28e16d04f2a5d7dc88710f420107198e647e724b914234e84368b318853b810174f74476c09ff1f75f7d5b1b130d29b6b9d15983ea3751901dbe72b5dcbda407af8d2487c74eac0dcd3c0e1364c794e23ed5cde8830c7063e7ce3d8236287b0814e310ef7adfc049c8078bdb05959e2b45f311ef6c5b922afa344854eaf2b38baff55ab37177f961d008aa67debd55962b7ea88a506277500b83b4d942dcda764f79830bc98176510c2a3c83e48647552b0371293e606e042f5d1779b561e7e627a7c1b22b75bac8d94160128868617f6dfd22f7f4329bd6610000408ede41138e310db872d26b04942f3b4bf2ea39d51e036b52b6fd5fdfa52e8f8852e9ca0184cbfc611f591a8840f6caf5a3d3bb69c096dfe8cad2c4d07efea54882ce229f7e8a5bc1611b0771b8e7cb7451f04b0658221f0a1f7d41b39702ee6553cbacdf9444ab0152112029453a3dae721ed282742ed3a034dab6364953887f0e12b1f31914d439d77bec58d764d1a4055e2a172845a24d7bba37e7d093bec0e5af4cad49a52885e5bf1a73400a161a875535882d316c93291d6815806da56f5d1b07fa92d235f1d5a6f0a1241e20d0b3e90b1cf72f9650c58036d1adff22d6e6017e6be598e453fb70b6f2541dce026117132828438aeb72ef31d2137a85ad1558724cc85576e52295b1053a189a38e089c52c84ade17becfa791bd70460012e499502045e59d85f343a53c1b6c32851d77894293236295b8cf818051c5230859bf15fdf48943e8c3668b912e061e93d43a41e6e4cc50eb9842b663f92f008fb556c99f0871856f88faccb5d34c67aee5319d6493077c379755e9cbb370b3acd7e75238239076b61ccb60b3100634f0fb55537ec211aba6b9b7c9ada3caf14f27bc219202a7d1d80b13f4951b992d5797220eac4c5b8d9166b620a308431ca369a16d8c127d24447ae23742a28402892613441ff4744b0e22a7b99d63e438671b38b0657276b801ae50055610bd2c2cdd152525680fa7bb7f4b7157408ddbae917f70c9982992e32ea63b8a8849b0250e27158214738d4529e3d8c02dad3b3faa80f51434e7088c4c4a517d68fd6063d5340612ead585037b1c57698d3268c16d873ce53a042cf174559168da8b425081b7b3e64e1e810ddf30d3dd7299f5b57d953095a82ca1a4111d8117d87e532e6a95380b34b1a7490e105e0842c9a118d2ce388d93cc82ae2652ebf6b1aecf4b6900d4930f12eb3b2007e10e693e6efd66208a1c9028b6a1f3d8b37040638d93aefcd6226b1e26798f2ca411ec74afea925b802034c3947777079e171cebf4a5860809e6e23f088ccb4ccd7ec3a30806e3dbf90153147f5702ddddc28ce05035051e6a5f3a443ca17456da928ea3195a141067bf0f2fb8553ef994f3d06075d4fd10d1a52ed783ec890b567e9b921d6ebbfa6a7ceab25264d4164cabcf580728da77026d587113261b209538cf2dced86624c47e3e41fb78ab523f711a884cc446b1296998b71daf3ba60e4fcdeb890e2bfc052abfb4f7c8428960e554209afa177ea3e87323db1c23a793fa7b40285c6d8b1f13299513c2312e41a3a920c3f91249901338db1e2343508150fa1758da1b349a8945ad58ace5adbc4d18a9e55fea3231229fedf92808bbcb66316ef6e7dad7bbd69158d7a3c940b098e0f0b88d1c9748323fbbd3f2c58738d1640f5b4213b5e75564fbddfb512fd4666d24e5b8e8c1ccba81c65b9319ff3d0054ac8ecdc63d161075171431aa15c5361cf8e08453a49066f004de0dfc6e2349ea4f3f1481462dc3671328c0c09be125ee4e0087b822032d6ba93c4ac8d0ad00d1841e17746d31688356f595be6d520b07bc8b7dda89dab9680986fdde1e02275004b4807df0288aa95b5b575053e6c0d592f78cf2e7f8377cd38d251801ec9aac967e2b704afaba3c57e22780f932c2ec21202be4ec034dabf3c9ad18de0c942d523c8d79cfcfbffcedea7cf28fb75aedac2224d91359a4b2ec24d21b8cab1d91196393166c59c4dd73b9fc875215f71ebfdb31f3f69ac82f3098d39443a4b0bb09f0ad1dfaa229848a4a9647504e14162c8a71f12739f2208feadd576496b86b0aa207d6c1bd913b651326f7e81fce73fad91541ae781785f44684b2e074b848e56b35bfdc8e536830f74f7edd13c7ff42667bbedc16fe05d5fd66c5ffaa034d32b474aca325d0a0f1893bfac96ce5e55eaf1eec142833e7ada9f5dc3fa7e8b0473babac6dfb3315949982295402ec3959404390953c010b31d3f8c6a40268ee42df7bc43e59d721fa777de6387b1c70cd9ddbf4e2ada0be9a40ea1576d6198884a8ac88c89d7741e6708afb701cce96bfe40eb0bce807e78d1fc43745e601d75dca1faa6a260414f8b76637d5ed78a883ef81b0fd7a136450db2ca52ed82282865ab35b1c844582cd9f05c4be30e90626a235a17678454394299dfa632946cf5aad160b97b2074645511437113f84e787dcac2aa29e1576a88a2b8f5fe1785b617a22099cae4c419d6365a6440f0916fce4f6c612db4236999ec0921a775dba4be7003e2b11cacab6df39e4840373bcd1cef5a96b5a4e43a73406d3ac9ab1cdad5d0a4857ac40d7f497934b0034635d98b96da84b72e9a66e5075d0f4da9a069239c5ba708e9ad08d612042a28d3e6a0b94aee942b4f5959a567f868dded96e78cfe6855ff05dc7d94e75aa96088137f4e361fc6372736ce638a68b5a4cf5fbf25df96c2009fff8b1a7a01460f90b317fe05e58f71b6010a7bf402a0b926547ebd4ffb11846b8c3dd5ee8d386b1cabd8961ddbb7c50366cb4307e121e574c39c70c7f9de59a932228355c6d96ebe5d1a79a3b3f120fa8d757900f72e725d12c89fbf0d22541812e6e775e49864489be191b8a7bffcbae1d8a1469a726ebcb8169d9fa69eb224d545b6774d2f6daac65e9be87d52f12b793ad729e3809a5008b56222389483c04a6180889708c95c5c907443131d6140a82a1df01dae2683384706c6b86d6595f7706753bf9bb94a658513491cf40e809df747db23bd94dd5ace8124c99554e15c6f888f2d72cb912beac5ed6da85d589c3eaca442807a3d4ff8370257dd4d7f61abd705bcbe078c5d06124196c6f0ded3886e1dc354c8a0b5c663ef13351220e0c79e91b6049fdaa1135bc18a29264451edd363754d4e4318c88356bf3747a03d9d9440adf79b43cb6ad58f6d26497760d7174fe20fd453c648569118518bfa1a201324fc6725f0887c059e7374f57ef34d4e012bbd59262dc69f76c94d145897e16ee0e41e77b59db0abbf275a59d207d8df7d1ffb1ad62a4aa5ccbddf1be86877984716908d224654ba42435591dcb1bb87cc824e12f92ba66db4e6f5229a9c4564b19327bd1b83092e4b59bd47635eed8407dc8b121490545376221fb857ddf0fe832d487727c0585f79d0f6ce32acfbe1bdd420a3bfb2b61b38c80f56db98fa2ddb53cdfe615c00f79e7784e2767d7e88c96c1f968ef045edef09a55458c977041fc5b496feb0fc9ad5d2671ae89a85cfa3e7d3b275c38a01e0a052fd6117362eae49636f73dd19032aff40911a88ac03fc4eba462b89346a397026ddb45672e9b3cc5fb98bee3e243220c4b6c4b91fbb44c2cde805e953c703b08aecbf7a8a7290b8bb10a998550ef9dfe18979f31fe492aa1c854dd63a1d202720453d24fcaf04baa19e732e3a766fa7da26e5a4581428bde1f41d04c0982806e7350c08bf0656d213a3ea0f8d47a26a8d3f845c96fdb88e262c8052d48517679d927d93407aad21f2bd887837ce9d90af180950161f71a5530811154ea1b98879938d708db7d76af1363635506ec9d4a8dc3f68adcacdffc40d1cc47c745029c458e444f231c9aaae80a43b56b046a1edacb4c2339078e157e844be52b49ff8583d1fc1938cd77372ac2429d348886495e4841f866f85b4f48d4fa24b33033f8358956c29ea4004960d550528b3dfccce64059a144ae58e90f9d9db8d73203fa72259fb480dd063f7355c22d6ca5e574a0b28b1418e23dfe28be6008a0f64b2ee04281d88625e83c9c12e35bb32541db6a44970c658ca0d1b8e2ef089b68b636c3b48c44743c9f90077f03d484c980865f229d2bdb2d42871d356ea0e1f728b6deacb204fd3c5281a6ddc48b57c30ad48d47b244cf788593a8815c16eaecd8240850ce0ecce35222f7a565690fbdb0cee35ce92d31cd6441d0a6027411a22c9e3f867784485d893e67d3cac94c82dd6c82ac2b9186e634a54aa6f1f841c2fa18f9637cfea5f14308876d9c10aecdf2451ff1a362ef5cf49542b4193d1332365f688a72adb8bb2ed764881b1a56c1b99e31091275dd7ecd62c2445954573ba783eb01c1151942a4507ac70d5e5284e138b76c131a42afd42dc1a4bc9bce41099a9cbc155f3d6b74f3c528d32e0060c5c5561b5f06dd22057e0613d6714497473a4b9318bd964d772ce1f366d5f8cf086e3739172ed208d7d183c035b4e56e97cbf38bef56bf77abf200ccebf66cfd708afd65ce514f2cd44c9baf0a11dba987037318c7fb84b4e329e0a495872d8a158aa772f46e5d81394d9e0d799414733cace0f87b224ed8dcd9e90c1747f1b6e76f63911b6b0d5db56e9a69341ba4fdaee4657d7db2375784774910fedeeae81dfad29cc93c8745adf0bef34ff5014ca666e300ad6feb2682caef7bea92ae86075f9bbd523da339be81f3b4ec2d930501083f2d7e12f4e926609a5621c03a75aad8a0274b97fd8d411817438215bddff2af23771eb08fa45d4b7875dbd295ed85f70b28a91de39ecdbb6cdf22dfdf7a69b6bdd7b33939aa476179c747207d2c2ec9fb1073cdd6c969947dd08e49972b04869a3883ac5e152f9e08c9d210d94012a6d144fdbf694dd987608e2b8e22a743909339811b7bab1ab9dfd76e1eb0ae23d08c81add99b9aef8d19ee43d004ed0c34704d44a8d44bac1bc247488344cdc97bf57d2c02b79ba83fdadf2eeb338b8555191497574a07270af276e2bb70c5516579ae7f2558de1644dee426a5c7a33c800fc19b8c7593d52ba959b28581e5272c603e50f8b4c2839e3d1811a2abd2e61dba3159700ba3c571ce1e8d32d5f3ed3452da8b797060cc6271a1850e8f983cfa99f38eaec076ae88532c2d6ccd634de4da866fbc9d851ec5fb775021182fc8beb4a616c66356e5a8aa40960b350f6ab59d202f9b0ccd187eac108c00dffd7c68f6c969c4ca6688495791edd170202d747c03585ac990b6bcb61631ae787c8c6ce59d963b32f3460b988b73d9ee0bd2b8e941030dced6373aab431908600b45f3adaf4d8bc8aea18e845f0ee1cbbf9eff7b4e096d735c3628e2ed2835f845e6873aa42ea79414e41afcefff0999226ba23c5d4074793039c188c0c5b9c704c747adceb8bd09c6df3eb8ea302f9b01dcec2f35a4614067d1024ec779841b5bcf7fd7fd9867a3509a1c3e38615d4cf756a0f92ddc0b926a9d1840ee97b299c2cf81462466c3c4651db66d2680b4fc229361246b81dec78a40570d6c610a11a5ca4b4d3cafeb98b41027d35c5493df78f6ddbfc6756202031c6e81058fe0d0291dd6e44b98078228b70023cb508d4ae94b7441b3241240a866b7e28b56f138f86c90cdc982e0f84ec5b14f8f47aedeb7ca9e43334eb05d82826ae03ce256af4e838b5dc7c80de03b5c4fffbdd8ac9cd0352d47acdbb4c1abe04b2451871d68128100bdd26e3fab3a9958bdafda53f35a25bcd601d760a86cf20dcaa3c829fd0730d39196ae95d2df98e0bbbf0a97c3ea77c6cf4df3827fd2ada609e4633249fb058cb316066167de55ec7e758fc35cd7b6b01dbc2bd7d210cbb470fd272cae33e12e84197251a72afdae760eda0408cebcd7c83a1644fff5ed3d35f7346b7941183e62e8e4629917ba4c7e44f41cc5e30139cc0148a234e90934c5d83667aa8fc1db6bcf0a0e5607d4c827f9139a40fd2965decb9aba62a226bb3f402e70177ea09f0211948aa082e7d32e3b16ce6daebd24c6b4bd3bd25b81c2f04a2ef8f26724db266e920d6785f249a684c958e7a42ddf7739fb6a9ce8a814c2a286d03c289fb34d859581ac44e37f82f7b9f00c5f91145334b43765976e4b73a31b37493edb24243c8e4302fc75bfacfc7e312664458e39316b443304e4137103d8fea3a010d81315c809b2da308e5af9fa0135c5a20620c3337e19c54a8e3c34c0baf9c6ab1002e89050bda70c53dc9c482bd0c2ed169cfb4bf3660820b55e2874ce193345fdcb4ee176378a44e31ee0c40f68ae8eebb5584b6b4244408b09444012e26f68db15c43c0f2904f017e20d8b34cc3a6a94a31427757b0900f5d1a78044e2560a901ced32c096a7c6c2673ddb5621c9b3656fc65c613101f11f1424aa9250e87cd4340637fef93fd3c2bfc3a2a5c3f0f26e7fe3420d361ce4bcd607a36a4b0e1b083b20d3b48357366c2b87de55782a868b096e5c84d11ae9f6f5afcf6996162958001658af0660bb3ec8958ccae17fc7f447224f95806837fa87cea306b17ec63a514c917997a7b0d7bd9c52b09d282161b65aba21a245f54dc7d456ff44586072408cd268c0a9ebcc8923451d07743ae70a6b6152921520f5dd3ed8b6ef4d9d57989f7a288f5ba8fe232c081851132bc63302ff12ddbb14ebacf5b71e4e48ec3f01a05abd442646f0692bde88bd63566df29dbb6d9e4638b651130ff66bda161274c4e2ac21dec330323c805addc20d32f177698982142ecfcd08a6ec27b8a0738ed22f4532208e2b12df68903915a84090f0eaf2aae6457b80ec2708fa1909b847c284264aa9d6af60a128afc69af4404f0d45ddf0089dd07c6641b8801753b173cdb58626cd2552d27e51de8d771625d0f554a7702d49650fc383005806d7b51dda7108c3c79acde3e95b5a43d744d7b317b4b9d572c1c773f1ff01a17af953b8a85da13a0bbd21a19a3601d19f56d33f7a55c436f16c8fb6522efbb7cd6b0654c8f8b620e8279b9097d20c573c8e8fab9dd3f72bb2a1af1779c5ebb7788f056b6ac0e24ea4a8e88d18e8d20184afb3d0e8720784c43e57447a59703534e545d34504d37ac4348908044bdd5ad5372cb2705f2afe4764a4e6d15fcfe80fce816bb378b7ed508080b3c4998a11dc42e2e9494cc8a7c8cbed7dbcd4434db235998e028f811ac5f3c865c354471f615651d590aa9cae011cc97a22519cd48b5ba272cce6885ef486f3aa8177bc06cf73ed51f19a5199caa8895467a8eccfb15087c4d369dc3650b65c1c911d2a86134ebd19619622771f6ea7e1e384a5fcaaab0c106d3be0242c322e49c4e29287ef54eeb000434938990863e8a6db8a59ec8d784a8719254b6cfedf971017ed1621ae985d16e8c9513568fbe79b67ff07397183ddf42da571045af739c4d420ec4ea110741865dcac4a29464de9553237775f0816aa826df2f4aff91252c285f89055d3f519234315f1808d9f1b565c9824baac69f8c4356d57c700f8219e1324d5bc928f4a76379ab9250d8f250ea7717618549e413b3d0b591034140e290c010207d12ad6db3f2a9c782d1a2cb8701f61c457fcd4ed17b54d5b7e1302484815952d4ba16cc513f564d0b9bad3a6721422f4b318a9f82995f84213a4b520be9510502fdea5b40be4072c5428042ad668cd65792e569d5976a99c5a6380a8d0fb4f73ee9e81e859dd1161a34430c6d625f48474bea33a46650713238e1c8b5d2d594a24a5d3d173d46df143e141822bccc4a0b5305dc147d63d22fff9cc1231059017b2f5570d5b7387c69ed55884c0eefbf803ca63fc502a965c10512c90dcb83582517156e7c1608b3c0afc8c029ad502b23dd4b34ac96c744720fd3646be97e14fc273e856afce14ec6093488b9fd76c3b34fb1632e6f501f1fb16189f7255557fa875730c014c0a29196f5b261313a62f2f95fbd368504a159d4ed85d49a7000bdf94ab9c740bd5d407bc34419bffa4d167a2e6a5f560ecc470d11b9eac71d34cbe39b72cb18622d40fe8c707081b51c3f654e9a7caca7e7831067b8a325a37e9c171cd885b22a72ae8dca1bc187b8613e92651ba19473f69f944ffadadd2abd42ca2e6c09c96e413544a307c0db6c7737614a3badf1de590b7e78c7b86743429beb0939108c2288d92ec6e5bcbd4c3eae80a704eb6277ae8303e6b695f65a03a7bbdc2d9a3a84ede089685119984575bb112d55b13d905158e7bf81ce5131ff4a00758179b15881cddcffd6c7dded3e6e55129ac3501f0709348e89eb616e6e7e4eeea0f90fe17bac79412d855e428dd30f17bcecad29a2db3277dcc05e681ec13ef8151da015786802d6b1d62f7f6c21e457be56402ff3784143e0eb06770dc5ce2bc2292ecd98aa8c60843bbfcc2971b60e54ec560310c0df0162252d778997db3d40be8125f95518dc27b675184db88f9fcc63b0c1540faf3ea3b04d172badd09f842445bc33fea7c28476b902004a392981c80263e92529a3b5f901221fd91516038eb22d54029aec57014c5a5036e4d9ae02963580a1c1064e2dd6c3a6c65dca83b44b8d24f46b4f9efab9521ba31bb9a111d02491e637b7f7373eccb367858894c14dc152b31e190ab590b781afd79a968537a11d3028b85fc92a08b614b5ea50a96efe6b87aca0855faf7b825780043853a2495db5956ca4206276a0a784b922c23f5d819a8abc03e314a8db3e6c437125057cce544e55653cd223977921aef8ec270d5a05811cf97c9ea104e5dfcc7d9f0642690dec4b41e682ffcb8352eb2ffa5bdac8ee349872e1794512d2773961a1f3841042ef31d52c506a5feee1e9771ac95a3584be1ded52f1be067fcf694bb6a9d1501a2738b56347a2c5b2b1909fc5af119e1e8371f8c70cb40d12b3c505085068e4179865b5afc462b80a64914a69444e3054fc0696efa7ce77f4bf25184309c2c8860096f622aad9dc0d882ea1c299d84b1aa69af4d02795da81e9df4b3d8875521791866fb4c6093b4703d8a06ae48642d61c557d919c962d38fae0daf716fcfb5ac850ae28c74ca8c126eb6839b2d46ffb913ae5b85bd3242c5d4f2879415e2c9f64c314f97f2acb39cabc544340e592c7cd33e701f7e9983d5251044064d8741b1b591232b7d205d9b3858e650e205a619bf86c148834f2bbe41ecb9f0b579945b12c717d329def68c5e28028bf97b74830d458a83020402972180da58ea2a2cc216a141c8b4239a7a828731405450ea2a2e05034ca39448d127328792852aec007f67d1ce96f2e4a410789142b643276ec42c01f6c10d39e9b8bba37664cc72d634fcbc07e2303755fb9ba17a9820afd7cd56372dc0760a57ccaa0c80e59d9efa2a05c6097feac0cb8e207aaa41595c3eb1b7691030d15e376de2f7a25cb27351b2a6f38f957bdd443a8bed9523f580640ce0c94fc6c248ac2f97b4cb6f07fecfba7938d4570c604bb75bf30ddbb76add7c9393f9831e3e5fa67459bbbc8ffdd0f7d850d0d7b2763b81f9cf8c8208e82a13c8f5fb11f4d522533ec47c9c689081bc29b2803ba5891241469d64ee78f670ca05668f85ffcd0fa3d9e5810392286052f15630d361738253a9db5be0e348bf6a054d761194f21773ed51498fc5a5852f2bc404c2158e30039ff8ad367abb08188be89138447a004c3808132167d18443b49d69a4834575a2645bb082c825cae3f66aba06f42212bd1d06ddc3582b38cd4353376586464d9263f6465c052e6c7e80e19220e7e830b0438409ae0e598861a0d912505031798a0f9e3efceddc9685d161225a650a1a07dd3fd1952663be20741eb6fd94bfd3ce88e3836466ec06f9c184682260dbde36c09a2c51defe80e813b968dd1001909926e5aaa1520c0bf39d275dc2b38dcbbbb10210b8ad0cf83f370325d938a1720fde51a36a0d066785db23e2f29e30786293d74a0c47512d9fe43a8af8b660aecebb88f2bd7f638adacde6a7688c7efc62d8b443ff675fddd1c38a0b004d262de7bc889cd9f8313a4b2d685a2b88da67505fe1537a20c0cee885561e955fd605dc0f123786ea369f15f61bef243f2630bf8bd4d032ee2f9e7bd457c990d4a0e8104687496e61f4ca04396e23db1c37c5401bf55716dfd16fc79d4dbfda95c22c2d6f16906064fc7416219eff03fb7eee7cb59d490605b0174ec1515fc4ff63ffa6f26940421c021825f192cc9c4945e6031200c37a8796c3d86a91b98c824263289893ad427486e03fd678abccdee6e1d0de2bafb5372b60e91829265cbda6633e478e7dfbf6a0cc16efa084af850f66de3f97be7d3f7b1f94120384911ccd96de4a810199fb60a7480e7f923bcb96b68d5bd8d7347b065413ea7b815206253d67d7eaf6d4958a324510468037c446e8febfe2355eee704fd712985813447e11663e9b00df5b4c19efa56f3d3a747114ad908618bfaa3a20ff918deb52837524ea1af7764c67c40d3db3a55df7b83b397891dd213076fa882616cb976110ac6c753fbd08f1e8e1e2e3620ed0df9e725296748dec14315fd779129529cfc2ddab04ca99b027a548133a138dc709ec05e698be67d5efa12e3a1bee14c72fe7ee781764d55b0fcd94785439202311a146813a0a0ab144803a367d88ddd2a344dda4baf961ac91a7d16d1af7eb03138fa1dff93b3ab00a8633326ca7c88117700f3ec8ebeac72b21bbd0580d8afe8b70bcba105ee6940483a65f98620ab363fb6058058f1f8cd591e67c5af3fab97891a189aec885558c0a2906f0219a0446069134a327afc569efdc26103d089b12bb9884ab3580af8d3b70a0db32b948f27ac488b44f5a7ce174d9db0a53896c239353707faeddb9e5b9c26801a8050a0f32d403862467bff9e64904bf3991ba9e881bca051b17a156388d6b07689404fa3e6030de5f03758f108c3cb1b6c7ceddb5091572b78f4a301d9fecd3fb8fbd57ca66f79e67ba0bd03045570bc57f4252eb001f63cb21b82d0a3ad9304a0f85cf587cf1badf962f65d67b1317a68bcd140c3e6db35d4424efc1ffbb3fab6f909ba6798f5cddf71058f0536652c7aa9878f0eccd5caf664ddce63d5a661ebd2e85d61cdaf177103df1e6aa0a72d36f904219eec42485f6df3f5293ebd0ecbf20d3c61e3503de9b66b9ede89239712ca5c40691199c6250d53d9e6bf4c9d985f6e7eba7ee60223e0ec27e0cdef3922d60f0f81c0b0f8fcb7c59b8f030abb99ff9520d7a0979f4551b87425544107c1f9b94d823fe085fca55708dc6debeac48bfe619d3398dccbe5d8cb37e6fa99ccd5730c22fc757a233a739262aa9b47f6e6f98e381f8cebe82ff35c8b5bca0d7b84ef4fa0cf17a4a5ffb8cabc61e8490d2e18563c3ce805bd3f2a4e3a16a5056a413d9ff3511b8f8456abc6e4c69dd7f96000b009e968fcdd21871ff755e19febfcd787b852aea3cbef9341806d0684e2c56c36718b19c482e06cd63f5b71ff5dfa8202a54b89e3cad9c3c34f39ee7c7a9be3e789e3480a3e773ed074f06dd7a6cd77df9527dff6a10d5dbf086bd7b9e36e2e532dee7cc361e7835999b2342a8e8d2cab4903bd80240932a5940ea11e02773eaa29057cb97393dfe02c98b5b2dc22e9caecf9944d0049bf24cf6746149e8b77e19a36f4937c3b685e3af10a0a3c762029fbd596e1ab2d8597078e9e3a2a954755b1e7336ba13898364e5b8f2d51e60a1c7bdcf9785af27a963d5fae203251efe295a870ca1da6eff93aa866306301a83046d2d6e35786dd7733b638033e5ce0c20f1e0bdea4a3518c2fcde3a8d11d36f365a03e495299546ce884ec46a38bb83e61a70f6df7f43eecfdaf17d0ece7e7db6f4e31f2630f0fd1b9c9e820dde5502c79b844544d26eb5c6d2c331d24d18a5b423396c4ce210113888333f8f743153a11104c8053e36951703ef8209d38bf5a4ec9ad9d42277a98dbe9192874d0f0adcac36db619c0efa097db8467caa5d84705044a038a05afab5630929c7203fc7ffeb27c5775ee61a390676241b1479e454351ef60613aeea4d17bc7599af450fdc5771533c4cc3c3bf328228220a0aa0882a809fc10abf9c8525a909576688b6bb2b10d90d1355ad9a52d69a74c2903a10ab60a9a0a6bf183d80e91038b1e5a23e479b541616225997e746cc9f36a76028b06dc951c8d20795e91ffc5050789454a668923cf6b920c5013d4e65a79eebdf7df9f7b0d3ada9bc6634fce7df975b72a016a42d89c085955d354b7afd513a8cea651c69fd5a39a9c53adbeee1a226208ec25ac883cab36ffc6a885adc590da534e9ed51d0d7022ff6a0eac28cfdcc99fdced730e70265ffe9953fd994ffd9987f8335f7ecd39195b5432040b27b7241c146144b0627c35cde49917773be6aa521041ebc1437e4aea6084a34d6c4b76c2225a41e2456587ecf6e3b20434c43823cf3c09021b0273b03cefbd366fb93f81cdfc3ab9a5febc8dc79d9cf7f2ebbdcd8608e138dab841d44491d6c394c2a6a934429e370250131030d7a2679d26cfdaed6bb96153f9676d3ceaa4d669af89fb04cdd289596942e5f090266b25a1114f9fae16371f79d6c50790809a90fa3925f24998d372fa4453b9af7dda8cfa736a84fa4f7f4e933f2b7e6af535aa348e80229b11a3e8452682636482598afab192e7b4f8358b8b1b2c70a854e090e7d4e6ebb41d583f378018d3e439154323c2d5f2c4b1d294e714b980b93515376a7c4cd49081803e00464d48bd86e7be8e9b3fde179e24706aef8218e8932e6b8f037dc265ed7ff0fa586748935f3b65489bbef617e8ff02596a1fd266281935c904307ac5ac04bdd014e50d4d1ce6d48ca6709436b2f6d231e4a18b8d2b86479efd0f56f58c309a7a7bb991e52e99116ac69ba41df28cf2b557e1f6894eee0ba9778d7e9f77d2d2ef7d17c9fd3e6fe256f9ddefa2ce8042f120b141ec9d6ddc36bfebb4eff6b4ff7adabd66aed0359e4de7efccceecba77dd9f6fbfeb9ddeeccdde7b7ffefc0ef7b87bb4f7de937a52efbdc8e7f7ddfbb11f7bef10e83db3acdfd31ef7de61df3b99d4ef6eef36dd86ed77cf7befbd3f4bce9a3e0989d73b700ac993a4e449e2f5093885e449027526f32950f0da049c42e1498124b87e0a5eebc0291434d893c5fffdbcce81537e3f0abbc5f97f5e9780537ebf00fcf9ddc1e2bf9ebfcf95ff7a8df3fe7e37566a0d33e71f19f1da064e19817b7f7035fced72c9985d6543dff5b159071240f75dba311b516454e4c79ec75ed3c029718799c9517f7e0aa563e9c3b0d7225838f7753deb7508e34eea64f2d7982ae1d4355546c3acbb1d4e998c310d6610ad75098d90239365e0940e7b1d03a7c06037463029c1a9f43546e2bb636cdff5c01c32beebbd300719cc5075d5992b22b28767c50d933c03873f5e5aeae588227ac1f4f401849cdcbd104497978958892fb137281c59cea117b7073028d452bc9d6832be68ac04c2076347c6560f8a18210eaeebba2e6a4b98d87f5faf8bacfbc64723585e9c465541b8cb632c31d11b389040663dc092629aef1bc3fa8301d02ab09a2d301e6134dff59ae57d5557f5811071cdc995c38d298631a36f71f5a08263412c0287a4816cecf54aea5630f9eaf9ee17f3bb1ee8b58af0dde062338426485b4b9392d6689ce4639d81101b1e602bbc9c2fe4d7c3de7de177027a243877a2f6107bdd014e89bbdebeebb9eb592facef7acd211e894fa07161338b12eb01e32e475fa4192c2d077ede547238d190898dc4c1b9133571ce1d368dbeebb9ebd998ef764d15516d6c909cc39c73ce397f39e75edb9c7d1e82dee19cf311fef379c99f53b2cf39e74ccee44ddee49c73b7cf79cc39e79cf31adc7caef2ce55fe3dca8ccffd099f734ef4b9e79c73ce8f3f18aef0618f2a7dd8739d81011f863fac7db838f5619f12815360f8173c6e113c92320cfb540e4e818f30dcfbfb6f85ff16f9effbb254fdf7434e9c7fe1e0c9fbfa74089cf242adf8fc91119f0a815346e0e0947756c954c27e1cfb3408fbc2febf3e7d8353def7aad921c6ef501dcea03cbc8052890192ca38da718b55b837c9fd7db7c2777dd8773d773debfad81d6bf3af52550cb6f8079cc2adc278d68a73714452377d5fbcc1083072ead8b4200a5921c3ba86ceebd33528628befba3ef501a7b82f061ea41aa59e39ad15ad53f7a84ab96608824b337bb67825e9667027d963c9e778a8c53bfb445d2edbb675dbb66ddbb6ddb551c69fdfeeb66ddbb6f52860bf3de1b728b342ebe2b7ee6f5b8fb66ddb7c65cefe03beebd334f70dba3e458353dce6be90fa5f4cd3e79ef32899191e5c75a65147627ac6b4d02b98fc5e0208264cd0ef501c78a260a8a262669e2b938f17c41db1199cd2325d19d00cd9aa124b5282dac110a197a2d9124c0e791ea6696be4a24c37044445a4ecc6914b0916bcb134fd983982a9915fb78c4c834031001dda98fa12863e8fc1e7830232f17d3e9fcf07c77fdcd21c9ca33688c69c708a2f88c628c47da22625adeabaae7bf5abaf018bbfae28eb4a819b1f334b3c724530d6e6da5c5775b9aeebea51b8febaaeebeaa3b46cd875f53a0d4e840a06f8b38997988acf7dfcb5fc1cecc3e73ec521e48967cd6c1ab71e8037f13f10801f8049300869109b1051064210824401780104d9394ec5e00e68596b11f749daba110962b58dfb245ddbc89db44fd0e66e4aabca489453bf9f7b0d6e9f6c1a35b494c47d823289c4a2d1b94f1e70d522f9548dfb4413593212f9da3e1961d69011e5d473b89db4e1c8544efdded927a85c54894de632c993bcdbfe7eebd1b4df7ade467545fdd6ab260cc85df6739f82c129fc0657d1ef5c7151376844358f5f9d7322d1ab0b4e598fe8069b9c123c92385933ca3a104376e3d8220e058b3c0fbf77750b163d96d40dc512267c8c2961b4cee21b4c304249581fa2dab6d9de88e272097221938c38776dc029fdcb841a2e051a1fcd1c45d86274947849438bc2d11c02c1d3784d912b66e878d11e2d495e4288b4854db567673b30821cd570c8dd55dac5d5a75b69948de302b49cfb245d4b9969bbf443a1a5b1d603014cdf86152f327a88406e84ce5a15bfcfa8f17bf7b155990d18b3b61a546cbeac140b4e69d75ea05cf10c309d151567ec940da1b1c1d265a3e2cd8668d8ee1445c925850d215c6e56b860718bbb51430a32224488b6166e720d9f55eb35ea003d63fb18f2d7d5a75570caca612e1049415b622b29d23545c1e413a3ec14559262a8810171399701a7741f5b0558f2bd7d1cdc824a5cb9080dadc873fc8990d21e40d3e282ac89238608b9fb5c2ab09f5a5c578f1a9ffbd807720ab832e12c2d5df0b8acd8a1b91ea2f143ad6a64e0acc87c35d3e4759552a60d014baa7145ad229ea09e9269309e922b3013922a5a8528d239f6288a077004d543c3ccd96921772fe49cf315278d46e48426debaa87eee0b4c4e2f0fae66124d0553eb9654758885eddab6be25eefa54094e719b15744440fa3cc943e6739fc6687d1b40e8e0b04415abb0ca5ad44d4508558985c0c31267c2069259dcbafa827327c24893ce8abffdad4f9158eeb6491cdcb57dc1269f66c1a334d9ebf9127a1e83cea0fe9e47b57eeff77abd5ebfd133fcd9c4a547e9e2a67c99a1049163c457172de0949edb86a82aeab140b6fcf8eb0aced3358df1b5e39c7315704a57b6f03053709e4574d455e4c6099565baa6414b6915a856661873be9e72ef9c732ff6a71eecaf9eaf3e2d8253d6deea8ecc00daf57a8b822835faeb50ea03809b1b538f1e3f564cc3ef5d0dd4a3c7af6f9575295d119b309c73eeebbd239cd2d107b41dbeeb53216571c584a05e8737abb826900fe557ef3f4c574f2a3f728a183905b9402f9752e57843654eb9b2c532c432f721ea779ffe7079bd180143a3f583c9b23e9f73f89cc7786d2955d9c0f1c490390f7befbd99c1bdb7db0f06bd7eb225252525255eef80534a8268ecd98cbe050b5e9bb124245e97fdf8de7b4d06a7f8f77ddff77ddff7f55a079cf2fe307e20d0eb1c700a3088c6ac8eb3391d50387ac83ccd4072bde2802c16363b34336e82e49ed763704acf09a7ecb5185f5555beaa7a132ddc275a56fd709f7059f528ca9ef6f06794d944c1b43ca3ce1ad8f8aaaad2d8a4c7a74f4d5555a9460ee9c7ec47883cc71e307e5d95a8797a1b93b1d28a189956cd1638f28c2e2350447665e332838c489ed1a4aa929090789684c46b1cfe851272fbeffb8af57abd5eafd7ebf57a5e87c129bd1d6fe1df6b46fb6f0fdc6b487f7b16ec8bc50f4000bcf6825302a07cf1f32950a0408102050a14281cb90dbb62b9a8bf3d6fde407971f3010000afb9d82398cff39cc7eb79de1a6ffdbca7172c9fe7e3e90e3cdecbe3a93c1ecfff151627192b324d413c2b37a3534aa5c813cfff860890f555af6d00dfa7a39effbeaf675faf6b785e09280617357f6fcf82fded63137f7ba04ff1b7173e19756da13cb998f93dcf39fc9e67b568c029bcab4f02095e33e11412a49afeefe7813fef4d84bfdfcfb3bff8ff9ea4407c55015ff53a0b4e51fb1bd6154ad9afe07905afade0940a4134669934fec81f21fe913fe24d7406d58f78a1077964c463d019747f24ee03d1090f6f32465bf2888e1c4c43408a7a9ce439fe237e6f0580c685edaf78fede574a345865594959379833e094bd446794afaa6539b640c8c8d5d898855ff54394f955a8a7e257d5b9af5e9df8aa9f80cea02ebfdcb66c48c5412155a14e1928425b5f2fe194118268cc02c9f89cf77991cfbd3711ee1334a9b27dbee34438479f690d6e7ceee3a91ce73dce47789490f2731e8fe373cafcc5282204848819da0e539ec159150e2927a6115bf21c47090519c2b3a4e3c382d2c2a5d4614489289a1c3344b860fa703381e48288d70c29b1a43ca749413efeefe73515bb3d3aa3fced875430c6a6e0941d54fc23235e4bc1292324e67ebe5e9a371d8604117faf37e4f75854dc78e468bcae96e49ed74a0fd414214a55afd49fe757815e7cd56b283845c5729bb2da32bbf1842a3f4ff779423ecfb3409e2b7a94950dc93caf9f3c9cc28b94f83acf755e3bb159bff51ceeb76dcbf36d4bc4c46f7d6d1aa46ddb272fe2d2d3498dbf25b69b6d8a29268774818324b7453ed434dd4491b5c86c425618db705cfdc8999155b3438d68c481499ed1653b24c2e20211af26e053d88dbff79623fe2df7f4b7d74c70ca56550ff4aceaf59257c6157d1ecf6b2556c7707e76f959af61c0296cb248cd5fd7bf16ffba0e31a0dcfcb5f957140c75a6cc789d531bdeead9354ea2cba0cfe3799d04a7f084b008021343a5864a9ee3162d3ef7fc6d033c76d05eaa7e142165ee35927208f1b721fcd6eb17704afb021b5f45c68e8230a1e4390ee2f1b7df40fed6c9e192531a89c2e4edf5915208d85755af8db48bdd6ef7b58377bbdd8d1d6af5a6f2779eef3cfb46e3ef7c0cc4f9bb2041fe0e4eebef767038fe4e08d2df0d89f93baf5bc0293b206f3f8ee3d8c75eb83bc871ec31e80ced8fe338dedba4843fa34ad48acde5efed59b6e6df3e6e03fadb03db8efef6c296bffd10a5e6ef28371f7f4701a1fadb6b1670cae61f9d513e9f859fa33f847cee391bcbe79ee55e2357a074f82184e0b50a38250427aceff3b97d9ff7fb24250af7c9d0e7dbdfa784fb3e5f9bcfa33e9f4f07a535b5a20443051116f0f0392eb5a8d9609220080a2b4c050c1e3abe981e2e6262b0cc7a4ac833bafc3e8f7e7dcebd4eb1b747c7fefe7b3badfced751150a340d788765cd7c2f616e0764ebf05d8a7716f832258d04f118226c08153843291e34592bc063310638e2b4e07a9e9e8abc796fe304145f301aea905cb664371c70a29124e6e66c4210574390d2d39bc80c8d201ae8d78398b4244a59fe27143d15b1122b29a1b44ce44b83e487c9c39952b9060b009edc274c87c78190b83c1e2a8ed412642cd840b1be4170e97dcbd0faac018330b2785cacc9185d34e8d056e4076acbc91015101b2c1d38c661808aa1e16646fdaef7ef86b7989e3eaf086a2ec87cb87558ab92797b5212cae766843633baa388c00ea61e3b3bb3a0f2d70af9f62685f2cac7e8e96f21a9c63f509b9f6bb91cb438a2e17507028e4eebdee59f3a970efcd0beab5b498184dcffe2e380383f330c749648312d35125b741af76e62253f4911a93bbca9f58107fef5ef3a0ecd74c9f66fa7c3e9f1bdc9f7d4f5f8dd9f7fcb36fc79f7dbee4ec33cab3cfeaef3217a812351e5cd46419257e76c4602306b992675f871a275882e88851b121cf3e1bab194c217cc878f89167df4e7364640e6cc439f2fc7a6444017f1e9947985f8f98fd79a4eccf23647f1e49ce2323563208d165c4a2e3012d4bd6592f439c64bc015991e7111c349a58ccdc44cd29c9f388cddfaa81232acd427890e7919daf7d7484a078cb81cc92e71124aaa60cae184a4c48791e59f67a732bf2dc73f6ba3cf77a6e5ff7903d644fc79f7b39fedc1bfb732f39f7965ff7ac361cc170c2b0d96951c9218fa003870d49c552a54b9e7bc59d4e548c558ca6281979eed9f8a043f30492ccc96d459e7b3b236dce1ca5139c90a63cf7906a951b5053cbca1b943cf792e278ae28cfb1f3eb3876bbf0e758efc43b71d3f9e718c79f63b13fc7c9398e63a31b1a8f63d88f3299c3da538e4e90391835e4392e3e20886bad2988876309798ea1e44698a832ee8093e7782768080767cf8e1a943cc74f333029629819bbbae4393ef278735ff3c8be9e793c9ef0cf3c396de4d530f2c0c2fecc03fb33efebcf3c9e3ff33ec8338f27e2493c5a52799092ab5ac6e48040d55c0dc933aff835550e268811643e66c833cfc60232ae6a52e8f4281de599e732e4acc7d12c0c479e79af8b4f0d992cac271ef2ccb3d91199030321cf3be7d7bbe796e7ddce2df8e79ddcd7c41df36be2ceebcfbbae3fef9a7fde25e7ddf2eb7967f5b5df11f7c906262d66d2c69b914f5326612bc771eec3d5cd0779def9206fccda8a25a4c6e479a7b2e3e589ac0a88180e79deed28633ab4d00a4336e579870c809a1029a50f123c2ef2bc4b529fa809ea73ee6b8fce30190cbb7d0dcb39e11a4e18ecc69f61ae3fc3473839c3cb0d336cf5b58789fb648391061a8be2cc1a08a4fc811f623630668430c1c9335c346ead04bd10d125cfb0cdd7c525e40e8c0c97913cc33b3af9eaa42bcfeffbae7d0d17803fbfcc97f9daf8f35be3cfefd69fdfe4fc2ebf7eadbef62f719fa060706a644cc8a1a916b23e13b10269c5d3ec21cfafdcb2c74acc4cc5998e3cbf61c61a0f47684dc5a41c44fc20f3f5614c479e5fb526081e26a52653479e5f26d404a639d7f9b5ebba6e00f8b39b7493aed69f5d1a7f76997f7693b3eb12f7090ad8b84c38218b9c7064320f366e441031de58c9b3db231957a406c8cd8a489e5d9b2b15242124689a3562c8b3bbb3a247d9c1a9a825873cbb35d45c7a7046040d2992673729056a428ab9b66d5be09f5b643bb74de3b14dce6dab839aa033c7e559a7d3e94afeacdbd1cdbaa6f1a8ebfab36ef9b5ce2a85414d4861e656796659969d59236b64b3fecc62fd99bdfa339b9cd9e5d7acd506a52525af9f183e9262b2520c321c4570394bf2cc16bf5e7b51f34c86102322cfaccdd7372890962f268866f2ccee8c08daa9da21b3834d9e5994aac55b0e1413050d1ff2cc02013581c85c4f9ed7755d29fc7995fb29714d89abd59fd7197f5eabfebc26e775f9f56af5b55f89fb6404324643a88a44485872dcd24edc8aa38c2d10f21c3c2e409f93c89c9b905011234ee9cb8945e6127881ec92e1b35c43ca9cabdc044547e4dd7028d34f20c6dffe042d672931aac5ebb31f8d6ad15a83a0a967530f8cd23a5dd232e25a4905b810830ac8cd88a9275256b15894e48e462c5d3192f71545a1eeb81cc78e6ba9e9384bc3fdf62c37ceb1e8caa99eb8b88caf6eb5854d7cb460024b8fb2222296686a6475eda92903c4a28816c6c654492b54107b8b2b6a4b46a76acdfdbdf7efa35134e4beeea3515a6f662c8a26924270f829fa010345fb5afb80e3728206d4970e918e8e8d9b8a188ddcb082ac8aabc58fa31647182811bda24891958e371c2548c833cade1b90c69ac2e0961ec2dadab456a16bc3a0c674701b520e590be1c57634a1ac52fbb98a857a9b0bb5a7936d29b8da551ebf567fefedbdd8df227c34c9d682478aaee3c2b2ba69d94213f212e7849c29668d18015810ac9b0e42c598a210200d9de851c193e6f26201dfa3ec9da6a907a67e88c2299b3ea669ea816eca5bdd92aabb6f833f2d04c63150f74387d628d87c58aaa09bde08d9071e9b9c3918721750b99a1292d5c319a3a6e0a54ab487aaaa2a7feaaabea0fe08421d807e93a012c886e2abbe82cb064d849e88eafdb1153385846c09adc801e4a481c462465493272c060694e1d0efa1203e36971072c6902a58ca90733236bc386160cac68288409908855ebf4fdee0f0f8d5e48fb2af3a9de8c1ad20b50011c5e6b622ab4762ce1c2e3513264e60e82af727297188268718dc8f267f94f8f3f0ef587817a61205594d9a1017a19d192f238b5cd6940c8292dcab321b395a41313a326f1a730113d44c813642808f465989f8bc8f4659797811a7a967530f54aaaaaab25360263e511b9aff29ea390af42ba0648c0b73d3024bcaa9120f8f0f201b696572ea81a947e3611a15cf1928d1b5c245430604f4e316b6a2d9a69344844afb9040ddc4b5b52856f190b9102aaae60f10219233f04aa64843e10204d943620c016534220989719496e09e4db2f0430841d57dd5a728022c4cf1a04caf340f3a885c3046c04b5037349c681c5cd214362f3730216e14ed9ce0c2c789caa919377442e7072a4754079d241899b725a12d654cf9cc68810a975c8cd54d510913bf143c429c58e1c3ea89a4d375a066d66284960a1a8ecc9136a89a6f2d572b660a8041da3819bd3515c9b1410c35ad171a1d4658d1c8d0ac74bd720b6f89cf69907d838ccc8a9f24105c70271600a2c30f0d99159b32886a610514015967371cbe64104de1d0ac000089b018419aae72c68003f82815cd8b99b53ffffc7ed1d335d5373454aed6e9848e94d892a8c354c25c7558390b0b810ae9d038ad3b5908143dbfba4215556dcf0ee952ef0a4a81cdd53da77bf7de7b90558351351d382bc247a7bce2beeea3535f47b400313aa5c2e8a7147eeaa754fce89fa61e62edd3af72c2ef36bf0bfb01784c9568aa48b2b0923b05d456aa964bb19537b907813f2d04a632353f3f292de4c8061615479a184d17247c6cc06c79c372450299650304d0351b41ca0607571944774ca46ab494b8511023b11a89b5d496135cf251a92d991fc247a5b6825e2a12dcb333d0624b2bd2a3346293e738d524348eb5ae19645a0c081a2374684674395d7676e8e8117f9320c29868ea810a72937dff28d54c911e17c9d087aa0db77f15edddc2bc7defbdebadaaaa0ab36af0ef20db531d960ae9d49d822801742100d5b32a4d9244f2c79300c68e1373446d429c64b52b89840c5a5923fb91d5be769defba19b59dd2290e667a02dd68c5d934e59456bc35bdc47c9241a2acbc4a16f05165d3f3ef3eaa4c169f361c69dca076189f645922a2ba703c3d7589404353fb16cee0c035443df3c1a743c8f119e2aa22c8ca5a0e0f69345c647e952ce620f3a052b55c7b51b29773ae7675ebf3dee75424c63edf32e73c467795da5595f52acb39a712c1f4392c73ce3957f98ee39f08a2bffb2895889c3fc76d0abf763435558342ace79e0dfe521154333b3810747b7607e718a008d39f3e4989bfdf6a013883f5791fa59a6976bd6db02f8b014540df432d4cd5e0fc7235deec0eb26a4c15cf2ccf10bf9cdd257035c80ab590ab6c67bb57bb67fdacaa3d58410b592d9cdfaf0635cb598fb638e8081898f9a7f037fc3ee7265178c37d01f5420fdc17d098b9b702d003a6fa10829788e976acff07047ef4c96beacf3feeb2a1f843ce8517cccf8d16c612392c19221f282b2819f5680c501c636b384ecb0f6547f21c7f998f7a0ea004c93183c65433a8c88d8c7a54062e6af2faf90454dd280a5b20d619744c0bf700c323aa79c972aec1b4c92a0d0a0f6e621d503c0dc96a701ee1a3536551bf7f94ca03923b6baf7a566740b956833a38a36810d54234037f80be41fad1b9b99f6acf1e20d66b2b9ab27e4d8333ebd374035178a76e697aa5e372170085d3a9685452d5b35b3d7251bec65b87da83f3bcbdeafbde3c3877bf2fec59f55b5581bdf7b8efbdd97d81655936886e0be97fa155ad66e0aababdd67a07d1096812536333befb512a181ebe1e72d116c428d5b1e8f73e4a250483f7ea0cacb6fa288a069d41c7aa07aabe822babbe047dc26555f5acba7abe0ae96274c5ed5b874db2f1d07919d9272f1b5555633dd30524ee1af2f7f6e84f5f48bdfbd1a72eb13fc73f5d357bf3d4a509b5409f5c6bacf728acf7dec97a76e964bd13e859cf7a260acba26b3b2828282828282828282c6739cb630e8c597627d3619aa62cf7c2348d53ef631f7361ecd9348e87c754b34227101f4d2ec9393f9a6c2afa9a893aa0133d50784c359bb2e94e590e4c759c6a36e63e4d5956c8516296e5a966f9300572210be46ccaa6292b8cd398e56c0ce4409eb25cc8d9638a12b33ce64e9eb23c4dc66cccf2214aea591618c731cb0239907befd3742844e1299b0285c314c88742cdc62c67dfee996ccaa6719a3239cb5138cb599eb269fa7f4476e7a7fa99967d74073001cc31a5971f382d39f28cf2511e1a41c696a488a0e479f8511ebcc940e32a428485051d7b3edb725e706892677409f367b409e2cf68b2014734b81c0f67704932da25b3030a498e048f938ca23e75d319e0af5f3e8afa13a01b8ca8c23ecc08c2b1d4428f8c8a7cd100737b81c2d134f9c878eaccd8c8738a0cc25831baf0eaf28993e7f43802153d5547705e3945f29c2e534056f404f4e1a88751268301ec5d34059b01ca8f01ca17c1cf69d3e5cf29b305f5e89cee145121e847d33eead3b5eebb677b08be279171df11fe218621497f46b191e7137e67d6883bfe196d0efd8caefdee56e4ebcf281c2af7bb8a8a9c1a69a28e2bfe9cdaa43fc7df53646a4423e9cf691209794e9752e592043fa7cc1a52667f4e9b21e494ccbbbd70f1e714ee459e53add203d05fd0f8b3b69191679df3bbfe3922d7e2feac9346c85ac96c33fab36ece1a4c93e9e7ef7a4dbbc911fd59c37995de69cefebc6d76ceef5ec68b05fc9042fabd5ecffb9e1796f89ec78042f47b6fef432fed017b6bbdb7b7f67abea734164ff800622523f74038ad8a557333363db43fffce94ef8e3f63f82ff36b0ac57f3d90c47fa3a8e6fefb462da3d650805152561f080402bdf6e1d3fefc73feb9c22ff1e8f197949444a1fd12e58d5fe2750f38a5440d4ee9b97defbd4e83537c9cd82721f9249ae59390bc9e84a4e749487aa4c8c24eb0b1f9409349dc8e45546aac0dfde8fd7eafc2ef0d61fe5eaf3724c8efc519fd9ed768effbbe3cce5072fcbdf7f62642209b89bf49f6dea8d3fb20b037c9de16f60e6ae711026f2839b4ecb1ebbaae5acb70fd76283bf3fc336b6a3c4505d4a51ad921122d3a584d47581b684e40b9bdeda0e114adab8c53094ca249c2c95bd9dee3d0589d2a4344e145e154513c55146b3ba0d40bd781d167da72a4a50d09f3618032078b155c21a484dd44ebaaa92e95d1719bfb020a2b934448f420395e92872622166563e9d64f5c092ee71bd20ff993cb18e1f68b20aafd8dd7af13f2753a1d90e8475f408b58a37809a8f456fac24a546388d22df8b39845d91245258c4a2cb5514570bd229fb8452706d7170ee751a58d264d27ab4525abc8de3746d6180854043202d9c43aa0d2d78396fa021ac464d9d8a975f1aeabba75db2856798a3a1e238f4dbc0251c0f9867db853d8ac2bca4f9d5a17af4ed5a9ba2296c72ccadaec7c9ba3be80ea9cdaa117bad0ab677befac4eab3ae604bcd7b5c7465f407b76583586faab8e0d16044534c8ada122116722606a294a0b526e35ed0d33a2e323dc10f10f156313382c95899e1ed3c2d3187803b775b43a5dd775dd7e6a594b70fa48134a0b5245a502261d0f2c2fcab0102a958f8f6bf6d7755dd7de63883aff43245300ad4f298a7ec513352a265866903c1c0047530e1988b13615285e58839d388e47c0020281719c2402afc426041e0090fc0e3bc00f2250f8702c592bf9ad90a0a93e22051cefc8f247b573a3ed157d56be3806aa1847922fef7b48eabd6877bf488c7748e0aff2e4c9f4debfb24beee0b07389b0d99bdbf6de92d4927cd35a1b1c9f43520bfe44a372e774523f4b510808b31c3132813918f6834026cd3ab82e562e35ca82b82f4bea7f51c602083184dfb5d5b932314ad43766593636367babea8028f00aac07027d522d81d5b14a1918e0068630b2acce82490926a8909f361e81fbc444930153a1890c9c490926429397c6c863fe44f8eb5ba4b6ae9e7d52d9761aa69823d6105354126b68806936395d86dd5d59ae46999808bdd27799a7b9f9ca26b994da3648d43d32aafa03c6716ce2abbca7b8bede649d595fe87811d97d9c3e7051fe4922beb1ec738c81724f52728cc8ea58b775d5a44fa7d3b147a4db3e611df5763d7257601c038f4421b7c7d74d62a1f7c2a124bbbb2a42fd79601c03859a6b12cbcb4d62a1f7c2675f7f50f01b03ca708872747ab623e118d12642ef851a848e2a910fd7751924e9907113edec27a4a41e91cd2ed181fbfbb6aed6254450fd4f92869fa4dbaa6cacea7a5c7aec0fa8381caadbc4d5e9b4d876ef43e27547877656a79a75cea22c50936dee7ec038061ed7755d75baaeebba755dd775d5adebba8cb9492cf45ee853ec10485b8d7e7b5d570d3dceae1b7aad2b8f4f9bc442ef854bbeae28c65efaf31a82885b19c46cb54ec9438c510d40c828b5e3baaeebda5561936559df90385677e0466678c43c13cdde0cd76f67229e8a611d246f2645086f09e26e6607767376e0190f24b439cdf739826e67c7d522614f14db1c106b513c38a44fc30d33e78734be384ca508621c0335946886382b39918c95219a3d0d384755055af8e893931ab0281eaaac6eed2e90ea8b22bb06a3a48e426aeb567685fd0a07e3ad344ab66d3eefcf4808f67d9bf037f8d1a764cc4a64d9766e045def49579764aa4e491cbae4d367c3abf81667e4f5f222ab3a3a1829d91022f4a2478f5d639f88ac58dc933b25f6538caa66f1d4e4b2efa89737359f4ea86ad7217d5b559d8abadeb3e32cca36113befc91c31bf2624db948c53db9535b11b20614b4c553e8509ace4980aa7b3a29c0d0e13f30b005b1c77f805aeadab43a3f0d5de7b5f22dd1298dd6a95f51ff348d272d47d31b0aa49a1ab6123f1d5ef46ef55c1f9b6c7d2c526b1d07b21b338122f3befe781710c9491ecad3acfead64e9564772642ef855a4f55555575d525b1b05f3e792b74ef8ce56eab2ad5507765acc1543ebdc5b279b7aa52e5acbaaa24978a86bd6aa960d695c652d7e3467a0ccc2aca326299b4808c6c7de84140841d39e166944bb25ae4ecad69e962ae455ffbdabb8eafeb0fd5cab8f6ae3be3328d62df298e76cb56499c866debd29ede60750c642e3b0694e1701dfaadb5a687395cedaaaa6a9da56a2575b2d632ee6a0a35b58ce5ab2404b68c2f54d246abf6b5af5d55555555d5aeaaeaf00329d80e3b379d2be584c77cad12eedddae9a2902ed5cb2a91edd389b73e752a96dc1e039f982c50892a4b25ad36309fea235b8e52db7c3a063ec171310a325412540ae64e9e4a7ab0fff4a239c292aa495403e0a34f30a478ac6b58f75ddbd5029bc421b7dd204b811fe42f43d256d26de0d02247d7940e8b1bb10a15d8f8f75a00023f9a2463fe5ccda17a6fd92719c05dc250a8140ec790aeaee5ab8e653bab53f51bb36cace343a1508841b7aec0b402eb8140af61ecb9e57856658e15f8e9c984cf09a797feca95636b145474b87a8cdacc829cf2bae3044eb338f513a7029718aa2bdbb22e91df7b7f954d9dceff8869be479ecaa22babeac842e09cc78eda4b9058a75e8f8d765ef5bba08f327bba345f9ded3a6fd57b47dbd917d014d5f7def7aadb2c46d04a2f7c2679bcbbcee6775f41059e395b0b68ccdf7d05255ad9ef14e4debbd0c40b7bacdc41049a683823ee0ba817c2c07913cf7bc62cfbb49922330e91a02187134a46040d6548af936443ba8ea5be806ad0c164e31ef7d735e52cca6a3083db71aa7c7afa349039f705949d409165013efac4247bbb3adcb26d40b7820702fdda5aab63b77c5b027028140edb8a6e16544fb796d4b2e8ad6bba95edac4ecd0a72fbcbc63e9c6cdbcd2e6128140a31ecf4a08d9285f3aa15580f04fa1fc9d5c2505b8abfaeebead92c19970f85c2e10f1b0c4a6d476a5b8cbfae2b8925b9241f1f4c14c92bb3468d2e2a2eb625b8925d41e88140cfb6a3aeb125008742e170cdc84f00e6d8a515841e08f46ccbdd46f3575f615d7b0942a0d047d200925860bde6edafebba9e80927a0c2708871370236240e11fa55271fa33060020e9e057d75b571522a4bfc1a5bbaeebbaae6bab5b75ebbaea7efc755dd7756587ace9d69222b7bffa0aeb0ca2beead6752583f90f18c7c0213bddb36af76ab7492cf45e98b42fa02a91a87568dd27c39de56a0c3eaec9f749dc5521499fea74b12e088c33ee0be8577dbbe11455e7d9a089ce82d0ebfcec775eb70b0eb5d8d15f57aa0e6f7f5ddb95affe4d8520639315952bee0be8d7b13a869cf1afabf73a7f7de575f5e83e3969c11221c329175724af39e0d1b2d5c342eb2b87bc06513068a1170a390e313cc13029f860591618b72d9155c72ad9e0db9a2eadd7831859e7ab0a21ae714f69f054aeaa31102ed9e39e36fd75f51ec8715f40d93ddc603c24e89f162a91fa029ac4be2581601f44483defddad861b581cc7ebd317dc5fd75508a62fa03f0899be807e7df2db789bc451e3ab1fa5a219fb730cb3c403e8acf0484dfb9ac660ad59fbc63fbdb4c5d53bf72ce72ae75c66884a98cc0bb920505bcca520871e2770828aec0091b7626c0915c1a03a1e9568249d70b69039f339438e470e9c5109d67c565cd48e82a425892032c1c0e49868f194de40e02c0bc8d00a8b8c138f730a490365614c52606c37522f6c50c523c8071823d676dcc0e7891584e5d5239701216240d8b6641471883afaa98a9a124f8aae9e21391d52666475d4c1a58614048c72201362ec4584d88da50a88a0cad21241d4aa110165530f54a64495f795a7372d405a8a082005914ceb860ddc081239f8710b225ecd689658374a4449a763753aa81a2f3ee72bcbb9aaba70099f33a8344a9d2973522123150d0110001318002018080808456118c779562ae30114000f4dcc444c4e369486c4a128c85114843108c25010218410801021484987a30140c809b4e738470fbb661b80b1789d2db15f41fa0bf291ca3ccc835c55f2ccfe028f891bc19740cd4773cc6cd835e9d9f4929265c459170511476af9cb7d52f89cf74e0adc911d407afeab2aa1cc06d7197b10f0df5c921def7a9fff1747e685f9916941472f4342c17a1804da1cf2af2a39b4d490d1fb8c0d36db0cd89bcf8b657bd4650cd98b7fdea380594606daaa04427df4b6fec40aa2a1f43a176af1cec648a17674c2a42c43cc5dfd6dca9d4bdddc38bd5e40977a490654209acebfb354413fc03e67ef3f9e8b5490467dd977f877b229e079681fdf3f2c870b0f7645e72c125efb0c90770722b128cd6491fa1c93c899edc68cf0c2c16219223d9e284c1401ed0b18071a75e9077637151143c49dcf2fcd3d6a97fc2e9b8bdc02fff3c159aefc30fe2037e133f5a281a6c6062fb3b50fa66bd29b5066e732ceee52816a92c8a1cc0f541eb9a65e01b1fb84ccc9bfa65cfafc45ce07f4b6bec516690740d118ca83d5381471ee50abf06ac5ee1f0de2043b10b9fdf0975356b68416625ae915b00444e685b31fdcbf8f4e203f6c8b8e2e2f47cf7ca953b06308c80691c352aa88102c08ec934089b76ea06a0e7ae7df1ea38e257e049dac4219ebafb4f8049e131258358b4117061153d951c09e4b2d99969c0f51e65d2e7fe6d394b9c96acb0be14a26834b016065f7323ac7e7322141e4c1805513f436dafa4d576659e403fd67647f839a8a5790eda7124b87ba83dfe82a49170453afd70189d9fd4e58e0d5666a5180c5029f614ddaad00af38ebb66820985d4d14b9262d333b996f1ef175b85558e7df22d4088130227fc09340c411df7be384a67deb115137ed2e45c11f534b8e26a85a1fe958ab47f5e329290cdaf8ab5e7c05deb9962eac6b8214e0e4291fd3ecde3e7b5bde4bcff420e5ef805ede2b44485bfee92a04d673574433588bcb483ce4e6986264ccf34b2c909441e76c2e68c2041650a81a482215169ef4029ae2a29fdc6f75d644f31396e975692b57b695373ccc8e9d12f475dbeef512281527154db23acf4c8d788fbcd53c985145bb49b845c4114a452628593f62dab6973bc33e9723399b9f6893bf39ab14142615d9e605010f5dd0ad0c75a0ee1890bf5e06a5f019514e84186719b4790f02723100231acdb488121908e5de67002e4de54682e1815011eeced0822eb95be8c67296dceb8a6a8dec7221480a1477c560d77d7160ac825116b76a004416e1b8e2ec4fa31e2e18a027ae0477c9b9aea8d66036db07b0a070cf9f7c289afd6c704d94d4e1a68deed8f19aa25e23fb794161b9a2d8a15f469cec9aa2b256d6dcbc06061cd072979a5d66d479c810f54c6b07e23d76d86e61921cc105493d85247a9709cbd3cbcb4488a5e88aba7cbe1b8a761f3d2ccaeac55264445e3fe74d45b39f144541594d284557a4f215d1452f4d0b4b96bdcae21e352c13864fdcbc9e0ec3475717d4d81316b231879dbf0d52f6fd3e7bf024ca1ae8588607da2616171e317993e23326961ba1806021d1f39a4845deabf839fb1f5942675fcc36f1c4c52850f511033bc3318e9802089c7d2afeecd78838f1c2e0fc768a4f94c70aa5066d96e4e7ed8552a57141af2d75a4855eb43aa98c914a80e4e4a8187193af3cc979f45662dd75d40f5e95ca7dbbe55597a37bd9ba7f1b54a8fa20f83d7617fd64fb8fe3a0c976186aad921691cf488f8c381d9e23e47a0d8a67296da258d92efc827fee09f87308daa2bd48054212580090a8f3976a89bdf160e8bda02ac84c64dd0b1fad1fb66667eeea85bfee0fff33a8613dd05f2fe1b0a2974594363b20ea4b53acbaa59803565973d06a8eab80406d887ceb1a05d0a1106c601609e8c256ec9ca7dadaef5f437e140d4bf872ffb686e470a0109d4948ed21ac8596d22aaae877d12534debb7e9d9ae4f7cef6b6bbdacf157013939cee1259c315a07326738257994b1ccde5a73834366aad71b12894d40404d4915efc55ed77f297cc5799ab74c10ce7429d5898a011f1292ddefeb3e8157e9e4952b55f17f8ecbf355410186a926a982e148af90155a018fcc51e809e14519c7543d1ee238613f5166c2a123ddc902853cec11e45cb32479b39a3891e92e7e1e156662d727f79377365d0d282dc4a1e554ff3f2adce001df9501975ccde93036c26a2cd89281031272b1cc8b8f900f093c4d0f00d0482bb379dc47082a3372f97b271727dc68cfc0210e2324b9d0b05eced481cb11ac4d8f7dd1afe7544148040ecbb1f62bfb907d3ba73b8e0a0b39301528e8b96a0470e0c19530f49399114d9ebf34913c611cbbe9210c57ac9546005d73f03dffe8d307eb8eaa90ce69e7beb6d45ba550148859e55183256742a41767906913065ebbceca9a8f4595047e31b7d354b2cce81a84a5b12913d6eaedf597109ef6154ce11ffa249237b5eb9dbffb66f021fc2b3fb397e81c17dd70f78fc2101e587495d6977ce002eb92ebb15a40474f38f335850f95357938d2bf6e9ee3c3f7d263d4b812dfe22c1e06d83964db823c8010466541bb2da6feb808d99b12f227048562d03168564c8f0ef21569105a8016b666b3c447164b174cb6f0344e8ddb8ae5ff1dbc6747b595ef9392211e5c42372ff2ffde1a6d1184cd219a9ba41516f93152331d6dba9aeafd81b930789ef2ed470accfd83454919e67697766822abc9520d7ea4bd92b0066ba6a7e1e13bacf4c29b5ee7ef415b9958bbc9ff3341c83441b1c19da39efa40d775036e7fe85c1cc41a827322db1960c6344da48a19a962e6eb3659a611232e19ac65616fd70ffc90356153bb77265355efda7456e657e233686a82074d26f4e11831cab00550081b94f054fdc6c9d5da0ed12a90086b724e4904948e761bccb8eddec3456f2f091cabb550205172294df3889e1950189eded039a7ff4ec9e095af94196655959c1e8902f001270d2852748e39b6f499875fc55864d58251f397ddd9b7d9bc3ceb49275a439be301097704712ceb4417348b805c01dd9f8b215071c9076a209b72a2bdb3b27aa48f61bf4abc730602c15c455082dba59f51c0e8cd502b822b4e962d5170b8c8502b8a48f09e990189812d09b8c80adc889e26e4541d40d45b3f765f26229fa227d6941de702f45b113bd7ad187afd8bc0d83a652c0cdb78a08a8f98c08441b5b4253ff8656a0f71c7f8b5b7b3690fc282b7a6869ef550173c3987e4bdffba52da381843178293f7129928e1d2e31b265003f1dcacf85a82b49629b34b9fb40a72d94a4237fa8e8a2fee611b6062ebda93ac414dc6c4580d6c01a81969c1844a109093eecf837708ea3878dd5779ceefaa3ce8e280b6057dad264e85b38661f9ad0ce2c96e87882ea3f5823415f2b4f6845857808d174ae1cc74d1eb2c23c1d9c11547a639ac9d1d72ad0cb88fc6cee9f2538b153c2cdb19c9bcd71a464fdc0a679e74d001667decdb6852335af50b38406c8a6803377ae3e3c56f15e6361bda372e338255b2eebb6c430d1892fe06e5af21174fa02427b0f60c8494c00967215cc21f6ac136589daba698e97c868c71f5899099dda9ad9748c7afea13d267be49847a490767392fb0330c278b3ac4aa38b7ae594d03105be462047cbbe3c27c2f078bfddb5188b95f99ed68628dd582a036ab8de1045cc5968e3bc68079e3bfc6ef690bde7bc3ef32e6b2bc57be410cb8797cd904c77cff3541541e4dd0f542fadf07d082a7bf10466022e2db476e5f365b247e3a3ca1e8ac6421f3dd2916eaff0e07d0c4dde34c4b8d6c582c6596b4088b6cdd9a16499ae040ef53c5e4643d9c42a028316bb94f43a5b0738f93a5997582084c788db9918123a095255c675761fb2d01b08875d8a2a0882fe064b7e53116406be1ff61a0754cd6c0dff8462206eee110533b395f36985f78c7c22bea84f2268756a3029b4480858a6fea0cd317805ad0a7560bac68898ef0071a99d5f280f582cdd24629bc6d3ca6cc20040b7d4ac9f6c0de10ed6d4580a4d1b98b8a6af3f9f9f99784f50539250c8f17da46124d60849b228322b659365320ac71a1227d6491bc67cf09909df5ba92bcdf1c45fe32ae677adf8a9d047f127cedfa923eb0274538da9d8dc0171530df4ca3f615e4e1c3917a0eb6a3c43d7b2696a4cc5f42e71de79d37809d13f1376079c37e43c5ca543b169b5651e9028e07824763f797a47bb2da2a9f0c8ba449381941e20ab4c982a53aa9b036287dab8c1415648f18c301a8f770ac7163a5fe9c5fea53ee0fcd2ff824f5f2f591c8dd2d00b27bd031db44a641a34da5e85063414415ae4057d4103d03d6cc06a298bcb11759ebda6bf9530c247e9ae9311de9d778b61d3bf1e3d777f264cba7026617878f7f5ac5e1f3eeb4700ff6094357e007eac6aa0e7b0c3eecb334c25a961a2906a06fc1f45524d546d82d598dd91d7447163fc1a6cc4cf596dc7203261b267854713115d7c78897371c5a54983a7f2e4cc7aa8724ffec14089617835f3ab07219fb15781243e52448de6d09981a7875a55fc53785ef9ce76550d970495cf916946afabbe596de0ae1a9b45ac4e4aab886a54778958bbffbacf67c30d16a24dbd055d3d023972c0ba7529271d752b8411065ed6ad22488053ad3d4fa8d2da41b71b9c0c310aafb4d082092512710a12b28f05dc64a72b6da6e889d9a28c5c672fd65a43e021f08375023822b4e87ed11303860541b8b2baf276938748a486704c39f0076a68cdf1719f8a649f1d5cae127a50d05cae2876c291a845d119c54547339aefebf97a135524fb6db6436f1030a8ebc7f82a5a0f2c25b8f0d101776968201b99fc6f96e4e6aea807f96144654a583577e53b8497d7100960054218de7058aab65554e6e53fdd547aea8912e4fb47c84c4ea07211665984ea9f348b2dfc840041e7482f6730d4ff6864e291e46424acfb03fb62852d1d4dfbd83157abd4eeb41332e7bb42a763a7c1612add2c5da134fadc0570cd6e5609e99d9863e5a372343a32fd2b5ed5bdf1a975be9c4a0154ff7a6c19434c1d620f43fcc1d0d981cb431b9dce3a736a7801257a2fbd2942acb84165361911c14b12b0ae040d3a415618c189e8527c6211c3b944bfd0f9ed2139b5dba5f28cc31f5a53fca14b588e81e7890ab9d8a0bb318f0181f8a3053114a9d4eb9af45ea90bc8b8483702102078ccc571b175070cd7c3b9c6acaa0fb78bd97d5702db422887c804c9bdb8c3ee5fe8079d22764755a03a34038f618c9f60e4213876fcad6035bf682ad3da1dec09676eed73a6bd3772cb2264f2a3a13449a3617127ff45540b0f0decff5c6b39f315d1e7ed5855046945ba06e6faab3f7e5c02387fb86ec347bf5d814ef0768e95c943cdba439d88100ed8fd62797641e9d24dee478a57bf2dfcc5e08b9c6eeccf9cee917c5f2e54cde1ca6f2e2c87b3111752737adba138d516d0138d2e0045551f88a46188ba86e56b988026e41837673e0772ee3d4811006e46442c200e0592d241ac707ddd54274e0800fb09353d5328721f415971d46bf39d3ca1ebd5d64209551cbc6ca110ed77713ac3c3cce20fdcaf5db51b98a57df50516f2d828fe5af79144e5a1662ed0452c680d8512cb679ca800a93ca7a8e4c86fc3f2ed56366b9f606610dba3708aff4ebb04ed3aa55a321f37ee088d0550eaf4b069917e9b73b101802effa8a85e12c36944652c3ef043f016f2f2e9218886c2c3465f51afd5fef64f8e10ef5ac6096f28d21d5f27aab36229ba225ffe8b99a0865e55eb9a898b2d0c1178672fdc65be88b9d517cbeab15bc7af7909564dcc44445641533f3870c723cf74a1733605395dddfeede7348e3705d6dc6ce65d18fb5ae03d797e4e457a66c65e33431c718f69ba7a44f22cd0719c295e6783a53e491e5a56e76dbd476d0f05e958e7880288443ef93186afb182882af8c457fa06692e4c4363514d4c5dc187ff1c5c7ff6f063ffdd4bdd38a4447283bada0e763af7b77d833fac9b7dc29050c5bf0d13734008671924665b99defa52f10004b08aed00b925d8347b3a414ea4eddbb8409897a9f9ef393c9b71d7d279adde1a86273a2b211dc214412a353e2b1079367a00a3808bbec2fbba24c360a181a029f73587fd965cbde4159559eec13331e7088f4ca7badab877d27ab534d31322d3708e0816138300ba4dcdadcf46648347d0944cbb2277ac2acb82c0ca6ed74de76184c59a8dfac340bc5d423a068317a62852649b45c0678995dab18824697f101bf2f8cf89d1a2ed121dc992a832b6cd51498136f14caabec86fdab3237e41154f815d30c6341189710e54df9389ec1f67d6f5c62b338413f843975f91b05d2ea06da5a4579bf00742d0ba4071466c904cdee5eb927659ce1993e339ae38e659d32ad60a6b1e8d5caa1618afaca8a26e78e889978a2b5efa3de17c6a4210ae69e233ebd98e6538701d8aeeb1e1d9f85c135a9bd0b031a829df97137100d1605a33343822a7682cb6013ac388f39470655b711fc64dc4e58ff191a6b9b8a0e146d2d5b3551099e94abbd8be6344c2d49cb99f45c30ec64edc6312f2d382d3c708374cefc9a4787d4874c19198efb34ddd1d28ce27622d92998022922d2892719b2170d732bd0f8e0b6444e588932f3f8466ccc78c4ff4541a7a7c4775c0119417c56368923ba8bdd309b9716d7e04562d349777d88c2caccbcca095d56fdaa0972b8b4f6ef12c44f783031628c5a1b1125ce7697ec8fa2b317eac80edd2736c8cd3de837ccdf10b25f14a801abc3aeaf765a50bdcbc1bedd265fd0fb3c6560b3154854f24a9cb520602c0a69298053a76d665ee84884cc82b9e9bcf8b30647649df3127d174c8829930c9d1690df038a06021f36b6071f1975319f2eb355b34463c1cdce6a0014ec1a58df22bda71f7071ecadebd616c080093be5cf2ed6bb70005753c62545136d74fcba5bd86b56a6c04a3c0caffd454ef0674d7bf8a31793648c7f9b54634fe0d41c74a7740547f22f0cb846d6d83e58466ebbeccde960e223d393695e222a3340f475e51a738a16381714213ba14772294e2c0017f271ab1316462be6169d094e6d01f8fd08851bee84722ee8942b915597d4921114e1add1771eb11a6bd079f5684b15f329079530ca940e4b619de50de4b6df31daeecbf5d0842cc6e7cde591b29312f636624c7b68d9ebefa3fc3c5ae183e44213108332d80f366b4b3f33e297e26e52ca04f61ba096d59e7eecc0d89e46eb385f9098b4290a34275ead01f7cde540f48d8f19c1ec81f61098cf4cad670141d4a627a4b0369bc09936ef21a6558463cebc3bd96c80fa27b8616b7a52c8f59041c8a6234a4334d942da148b2d7489111638a82afdfede5b2fab83f3a1919a4bd316ec7827b6cbeb6f10a57a3a5eee5593994fa38d4ab34d645452eb0bed12f7e3436de71c25ce8e8fdf154144255387ecb4fbf8793a0075b9a06e246a642481c4c6c8bff9ca3bb84e4cf364baa064828154c4865e6683087a1093e2c66e850c604467b3b464dbb3cb13ed7b008aacd1e3fae317f2b4d1adb6ea30039f899403ef7862b10e28fd37425a844fae96514b9cb6e9e0e35e563896a98878278643fd1a06219c887c5e8ac96d07863b0672ea489b950732cdf448bf48292848067cfad5cb66dea9c307bdad307b9a4fa35b140fa2c28eb0828a35946723b0658b187b56f35e408d2d4321206975e58a66830a03d8efd0aa42f5767554cfba6e3422a98b95e32711c11ea6b8ef324674472b4ac95ca266c9a0c231e8ab21169158221900b79d15c45903c67073df1bc68ac585e09e93be7089b609678257cd91e575c924b466091390066fca22890b27e7493dc54c340155ba25e639f52e7fc349a0c09b7fcd86a97a9879bca4eec92b84a8b04754ea4c8f3a7e5b3472270f67bf42fcd130f7c489d9beec5b613b76386a7fc009e5a06fe152765e5ecea42455770edc9a2ca94cc700d488f13bc85c21c1242ad59e09cbba190fa83ccf0cc1566b52d68ef6ee6e60d062152478ddabe370778b5d6ca0ef454097e43145fc1536c2f2e35086a9f182a62cc63a6313ba1412fe428f68e883c12d5f5b84ca8029dd2319db1a9ee01701daaf78a1eea412f27720d8fc7f2cfcc5ad25dd1afb08848d0b1ef41474799ddce8035f8059098e8aa2022940c0279b040e694c796e46109406eb992b294fff9b95abdc3a3b6d69d9fb50c474111bfdb73e6026f7270bbdfc64fd06b609ffb672e18e4c7cbc06f4d51b9da383e23c0831c722cb8a6960b446dd1f4b1855bc8bedaf82cc202c4d25a1accf7ce561f8d40a94b7b1d397500ad811e2066e5867f359c8fc98d932c081bb898d4c470cbfb8d19bd387a11216c3604392f00dd5ecba0ce0107da0a7e669e20f8420e9459f01ce2004c2fd4b2b2da1c1e449af8cc451ffe811687ff02333680b776c35a01a16e5ae5ca897d2dbe02666dfc2f9272b2b3d557afcc971190a4e71a19ec15cf861e26f2852683bb5660a29dfd980b40e1e0a9668b6a108749fd79dd8e85b717e2ef24fa91f8b9ca66c3b7572bfc4a879750edb5807361f6a9773d567ee8968fbd5379a49887c1a87d7da2c4f15854bf7d4ae799514ed20b6dd2b64a3a63b6657b471a664f0537e52665f56a1d92bedcbe3ed7d913af05afa5abb2b327dd2abcb60e4a9da6f51409058d5d971511550be585887b0f3a25b5536612dcb863b9e6d87cf62f9722740c00b3548aca96c2a71e1d9ef215712ed4759d12826f190af909c526cc60dc629824ed2ec1545860cf8f4cdd60840daab881ef19758173a672c01d0a05370b5c4cd3872152669f7c4924d3031b765acea33b4aca0b79a6f1512edeedf28564e1f38424de4b6a1e975dc1c418c0422d324e91268291ad14800cabbf23bf92c101159210f7efc764398db7c036e5da3c9ac2ee26abbaac8cc6f2e4ee8425c8431692cb0c400f951b58dbc98c18161098d47bc9049f72d5c1bc6f22d02203bb54c0c6f5a30af28501563ac3e9cf0ffa88b6364a2c74d555883e8837e3c9eda0e613e4495668df97a7924fd8c07fbb5c0267b3ae03c05a118aeb4cd6f936a669fd0e30815d3e915efe3cfccf35011f8da5c9e36cab3c16f7f4279ae70572382a809a9a3db7657d014eb3e07f681a8805a6641ba6464a3435ae47dc69a54e2aada0fcd535555a4799ebd526687591d53d0e9cbe1afb09af09b968c42d8ebe68e8680ea68181e8ba67b7fcbd955f3702cb9c666ecaf6dd6c16de9ae148b2bd4bb63919d80945d792cbda969bc661ba5efcfa8ce3dc0685e43cf31e8bd68234ded8bf7c999b05b5aff00bafcc0dc0875b96758ce3204c7d85d871cc39cb32194dc3fc31793e50e7e4e6b031d8f9732ba681636a9bd25a18086004048ea9faf661e90fea0c1417583b1d4f02b7486e73d183e16fb3b9b025599ed29c989962505949fa94f2f22eacb7a02bb0428cd00ff224ac750144cfc5dd629f0964471b67223a29b5d6fd7ab67ad3585286d174c1401fa3fb10ccea6c9ca1b433eab9b788ac49bd971c98af89461da85e3bf11733660f8051ee029a543a6482b1959e41ba915b7cc8f3f3240c7711a2c3a8fb2e573593d1e115c8e0532a1572006cbb53f73b88c87185e465b775cccf03001275e54c5bf226affcfcc0ca2d3bd45b38d2d516da94d4ec9a7d6637d22ea7180d83051071178b96f1b191b15e0fd2982804d78fda225636a186f3126cf3b428cd23364bdd662e68cc29e66c25e8aa6bf35892d1d578e4802666a46a275d2e213eed0a061498c329ee5953b0c1c690ae812d6bfb66f8550829e4faa9e86c3bd64e5238ee14e1332b1d6777ef048298f0f4ae017fa9fbaae34223a520f862bf8aa7cb253b8ddfdc23fe3f48553fba8df1002cf43c2253d787598f87a7685dfa12a9979b484d1c44a0bced2c123e1c4fd669b3ac646747c84abf63cc386840920aab003478b80193c03ffb31f3b49a46b17264ab1d7522563102bd0a0388b55bfd4ba74f68d9c7f5e74ed702df459abb7f28155f3c2ecdc9662d0a06ac6510516429a0744c57eface8fba0b08fa18085ea81831877605d4620c680178aa5802d17f44baf29b64ce0cd2bb2fcbd59ae35db33c8728510883ec4ff063e364c681f719c9ae097afef4ad6bf92dd856a3091d37a21bc9f596cb1455cce0d24f0d69c033ccb00d2be49eb95868f496423837aa724f768ed479db1a917ad16b1c09c64550177be131858a7c70cc1d187fee56f0831fdaccd211414020a300e8093998452b1c6a5d11162091a22f03df6f6e0a8cd2a49c54e98132568b7f2274a3b08a1394bca8d046c8d7dbfda154ca0e9a0f9f683f099b75de6dbac4bbe55b31dca3cc37fd67481bdd70c1247890c1be512922ba4e4475cecb6a3ada4b74d2f05b501af8018e9077a8370e7e2da4cac1e59070f17a12785640a3fe0d4fa284551265ea68fa4f9a0cbd375a088b5dd05c1b40358bb38f1a2565041cee5e837391efd2e76a219b875e07ae0aab8ce94249d171b6fe18a4b64224be09104cd52c2174c06f8ffddbe73f4770a13a1141178565bdcfbdbe8c766ceb8e82a78e3c39a3894d8fa3cdd6fea9a7151f17e3a176fc03a478e4911f7d0a5a1090eb09d140c0bbecc8ea8e6c5f19c7eec4b1c29a5a8c0704516131706888c8245e704e6a5c1f312d0ac2f3132d50a04784a0027117e7f1c17fd8a3740560723d0e413976b1ebd12cc9f07e8dac5278ef752ff77c99e432af351a022965677bb9fd484fa0472ac19e2b636653071b6887744758c6ee55c8fed328178357d6e9e867f03c9262e2c037df20f0d302485bb612089f47382f4a897bfd6098d6904399952a71630ee68d7a729af62157e509fa7112eb76ae086494b3a3383b7627f624e1f2c6579d2248f0e56af2c660718b02b82903b910d897a26c87e5c4e5f8c440c2b6f668600358082c3101316dcced2fb6ae466e56f4dcc806b3791b68019ccbffe4299461e0aefd069e4c838d8cc4d34583474e09214236b85be4962f5279704e2323679669dd6c1f8e45df510a1438a50e40c57a4f8d6c9a17304c1bec996533a366c344f0dfc2efa61625f1869a595ed6f0d220156b5f68982abfdc9c61a97b143a4b6ecbdd33d2766d07d6d1186a63e33376602dda0139988c8368e27ec299143595e28d32b06de2f6ed15a8b6208b52b7c89207d0aabb55cfc459091c9a5cedfd2c4921f3d0da32a925c44b63602a93763ba1ea56316ad8c8dcf78aef532e43ba9163feaa8b156bc2b096fade5f9912c4c5a2c1e7acffe1eaf6fbb9655db0e4b1b144904b3842d7a925a014abe713773fca3f85486fc2e2bda52cba4934d58a1df80953fa543bee4812f2b027f3c169f27023ca0ff1fb8e954bebf9b3974d7b0206de4dc5e61dd1ce60af53ef1eebde200eb42cf3461c7c17fb2650035f9edbd4b638ec133718ce05d34944f45991e36b8af5bb5c48c3a2c1d73703928908554c96a6617155d48ec0a221839da6a40e1b6058b17dfd9df1f783f3dde7829a1777e208fc0d0e3c129aee46ffe6f741a1a9688ac01685f916baac36ddaeb719347c78474d8facbb5431d94e6653b50341227d6fbf26d64867f1a0377adc6e6b54581d5954fde6057b6ffa57f4ccf2dd84c7e7411befc083fec9684032823587ad1e48491ed22cd21b9acb8fb206508cdd001497d18ed1609d8b812e370b7bc96c51a3b71f3a4824e6de431b441922f563aa9dea4dfee6c69d21da5b75f3c681803e72e8166f2d1ca999336e24d64ab757260b5d851475a153ffb18031ac0507e3fb6a3d43f8d48c78dd424ca2589484747dbfa26a0402f85c0caf262d6f192fa4e882e48aae7c5d45a80d3efb37857af39126766aed02731743fd247048d0985f0d55cce87c8d5e3de657c47d689019dc0243c743c59e517f3e8d37db0319a7000ecd5d06e8a30091a671c6c083793317ffa1a85c2410df981001b92157475c9629d21e8f5e08fb2760e01005c7fea31135715caaa8c09d4d2ca79d890c7ad16b9edc25d96afde5b8614209ab54059641bae23f46758e067294d35ad81ad1f9dd1841c5fdf5adaeb6d5fcb4207ed6e3d80234122e6672e5bc91be0173766f573fe57e75e64a44b8f7a8a7d96fa803290af6b57808863c1c557e30a46f6dd9c4f7dfb0fa36db69460a7da5dae76e9ae8bd706f2c9d0903676569a845a21a5c335114b15d00a488b020c073ef8d5dd3902a612c0c4deab22057d5486851b264d08028069a95bf6fe65fff35a106c212e08b65fb0c33c6ec88d8ee69da0ced5b79e1faf5d0c65bb863a8da67ee56ca477e27de7c29c0cbb53c403432070bbebac8915cff424167227965fbf8bc26a59f11c34f663edf8914b91f30165a20053cb96e804aa5bf5d1bd0a81197ad073dc09ad929febfabc48e3981c3d8858a682773146d420e2f961d4616c95df2afe14f555093fb958fb8e45db2486893b3916e3e274c0d120e32f511d453cc0c5ae234f4e34d8d6eddfe92f544195c32a727085bf6f1bededd16eb0d2ad5be02387e19f8cb77415acd225478c163493db1d01d5e6228cd1b03b3d3adb4dae0704443a1033771129280863777a12855d517c631471106cfb8a77388d72dc98e4da9ec13a6897b076c756f37fb79f6727e82bd2009e0ef126a97794fbd43998f3320ee7de52875aac6cce7e92d89c6d80c5834f5a7a07d01e05acae3be692f8809c1cba9174b3b76392e413c6c48f4481658135a598020ac8c6465e5480c3565e0e1471ed4dca3968e0d302ad5e1ee7981e11746110673e107684b0f74069c18a4de738131fa4c00828e604020a522a5fb45bd1004fcd8a64879e7975c981ebbfcb1fef3a93f1453189a5599fb3b1d7b5e942978347e3eb3744465d5a74278d2b69088b02c850734700a6f7839ee6c701b1c247b33e8565d41084628665a4c05e0d20c96aa7953731b181afc43d190c3ef9be5052bef2e71d53649bae9643e5dfad55f7da8c9d5a200f5108818b40ac95a02781e325423c2107592f7e62a39d2c95a7bc42ae8e149fff6f5f5ac8bb6c10cce3cab8021f2f1d53c68927a509b0cc66092ebfcdedb9539f2b5fc576d78bb8344bc44aa21607a8384ad490aad9ff41b9b7b9daad71248342bf917eb16f919c1871ae5eab1c02d5ecf0ed0d779c27b1811e0469b76cad0db0afb665f2f283cf23badc78c73122a4619003713e01ecc5bfc83e04e49678d3aa6786faa4ebf07cf89ca844f1777f2389710761bcc64f90101a6c1f58dfd58573106c4e656cdfc430632e99ad7e6528cd2ffb794b3e320fe1d40b7593308081bcbcc3d260fb96576cfe08becde68e6cdefb40f196e42e179b5b74b9d0c0d522e9dd511ee94a9e940e86b8a08fd5b90400ac4f07fda9acbc288db5f5d2aca0d4cd4c4d73e3a221782585a65b36f184d1b322de65d1a79baecdd1667c587f7a2882acc5eedaeff53205799125cf7f379dd04384a417e92aaf1514566593dcf0c32d6becfc740dc486d1fea688e140e09881c66823e0b1d8b2d5110945c442a114aa797baa4a429e38d1dd7eedbaf10187da8fd284f7351b16902510c2eb725cb9fc651626764edd3ec381845b9abc9c588415e23d9ec37446539c804a48989a851174cf1dd24b0131d6a374e08c4626a024dca285322a86c41e026c3b75469382b40b4cfce0510d6e385d0cabbeea561c212af9910b538b0b6e71b532c3070186d5dc30293a131c28b01252f01b7043fb455bf9c656bbc58c72d1a7c9a68a19fc35bed006f36b012c84ec91f900cbec1ce809800df86bc090f3abed42f6cae2841b812a31bc8b5562a127e5659ab8bb946c99d5b8969d156fb9ce3cc3a86e00fc0d45284a84a4d67efd55a0548e8ec9447b460e8e1a444bdbd1e55ffab84b4924f3d05f41a73b3d8d7aa87f324ac1d441a2dc48639e20c3a71527a3ce6c4290b091d1ebdca66f7b9f9c7365fd7f33472144a5dd0ea5b312372bad0b52da50bbc12a0d9c0f9ccd61edf19e9bdb878214048eeb86d6dd421a8762613206b00c5eb1446b582313b9148ed04e6d9738609efd6d9c4bb80795aeb8b5be7fc78a9bf43d4f20eec78f07eae2a07be4ad15bb709599c56434048e1268301f557f8114df0767aebec0989e86a4c70161cc2b841763d799bfb6d1d855423314fb6502981e66e57d9b75f34520dd99cc4ef38fe674e03789dcb36d67f4c72b7f914732d2a095aaa714c94795a9c507b38a7b42f59c831e3371c59f33fec31c0d023921a69a225c0819501637d72d84d085de53ffa383e5b80d7f5ed1d80361ae8a37909cf0421f0dc256ed551f3ded807ecfe0b2603c2b800c777a1b7595ccf44327cd60fa85b360b4bf0888e6779900191c9b91acb0e6471a2f7c4c74636468fc2886da60d677cb01dddad218d996e64c3b96fbe544c0e7266f9f02a19cd4bd8d4adcd85a651927c556f3aa71ce869a8d7cc7c8e4d7e466afaf4d7489b8f938a1315608f4c926115968079c0c62b0355af39a84a2d7364f687768d7ae755ce39ae0e49533c488ce1ff096a214e7da7c86257fc7f9576226addf183dd4a826e10279a84e8b5d5c9e0b092de943644848f19ebbfc2c687a27158f5e1586685f5366836552b28f1ed127cc1c0f40b23204da9ac9408d0643a5360624e417f0e6d999e6ceaf591d705a1b93f3119d0e063f6765af16cfd4c2c6510cfd0fb2faf972a2837fe4070914da6e7b0daa2ba49f5dcebdaad8c8119fa47743f871ef8205dde8aab7c8596b717d0ea98b6e52fe18c37c7c3f6a7db2188cea6ebc3c00d9d224d609746398af04a045bae2a004c1916a566a055c6a7ada20f35b95dea6ae3e2b18a3057462f8cb76e000e344c129e687761b261e7e5228a1b3d10d1a0a463d378317a44bd15b7b0b500002a8a26184f84aed5796323dec35cf5c6ec50176be0852115a17bf8ba31725bb5bc32edc98722c87c7abd808af88b73306e6566c221e0a46c57bc1de89f41635e623f8513810deefac2a43c2182d9230c014e5fb0892a000335308acb63817dee5524b60ef0a77ae610343126b8392538d6320bb271638556bba9476a068a9c5ddae7ca2b6f6383e8d0e12929a2e446acaed5f944d08165a26cf9e0f331532da9720e56511033585c27eab771d49c4d534cb68720d9f6dfeb7f101fc32f08de7d4297f89f87b14b1c08baaabc648de7a6784a224280cb11165a1872fde86f6fd7f580a2643ebae60585b57bb3c290133dc978fee402a9b7921d20426baf207a6a154aa59046ea2b58fe4d908d1f99ff33f1d7676c2a63a2b7325d73192ce4b394b0a9c32d0bc57540d29a943781335f06b633238c666da0018abe30831c8c5acb46149b3dd3f6e348546433a157fab4f1df1afcafb9ead5159d6b8c6dcf03d0c36aaad2346eb1e764c8d6016735c3a692cb8c3fe2b8cf8414b95cff4b710d0b6be420c3f640ead1a8f567a482e86d4c059f7660a55494999db1bf9fe91d835698f90fce374b755db4ae6a26cefe81f1a62c38375e56e5224a815e468252a89b77683bcbf997e0216228d331f6157cf6e3ed651ebab79da0a5bdd59b2da031963fe47488b4c34865c62a4f0cb79e01504d38accc26621ecfe636a1b0760f39615cf2d0a8b4c4449ec3036ae8c29cecdfe191541e0312ce36e6601184e94cdc62792fad8d9390d4de53df47194e763a3ddec1d32b9cb1e578ef867c51c147d55e10c94ff218033e2e01793753dc00c4d644817642616033133d5cff1e0944579c02cd216e17855868311d85b8c10c113ae16432d660966e2b13acfb22cefad7a454788e763ab5e96b9cb5a7abcf8915f536cb6c56a22c2247485d2781aa31bad3168f38cf328fa5fb9ee2b21d10c8c194bc08b7383b2c7e852e9f361d629036c33389da2221a449974c0385db23cb249dbc8c16ba58d6b6fc59fc6df2dbb44335fe75c0d511aadca5f9ac425bc94669b35432ec74250c7002a172d2794fa9d9c5b4c663ce8a1ef209b7f5f3a1cd63ae345d580b8106732f9784ab84aea566cb9bb203b75f977811dd299db51e9999c8b7d9eafae1b5d9203ff52acc9de44f69d7ff034fee3601ce68f88983d1a53a92716195f9315f08f02c54cb9cfe211ca4006eb8949c85258d0c2ae54ba46cd8d448500914b220668a738bdd1f4e5e1bf35e0f7d4068d7345361a50c8a9ae992aca112da22a68c78307f32823ecbab55490633a394c04ce90b1c19c9cc114c7a272de2d56e057bb8d83992526b9e12394a6a6405259471f1ef51fa15065bae39d18c3685a2c49a544e1a443a31c0f8a647c8431e75f9bd86a09ce509e79358a254fd76c078ff8b1dbfb362a9e9eff8297e4436c816bae1aeacd09b47848d33fb291e8e1f3ca710e455c5b493df008a793fcad927de663b621f482e09d710caf50427a538bf4d500c2d8d60a1b3c5bbd36b49d1a02cbc716f2b20a5f6d15352104c505c3d12096608d45b89b5e2db7fd11ee3846f44a8ede1a95e5e4af232184e5cf4b45204f134c79b3794c71a08bb810a097d23c47222b8735a9f64e5085199cd7ba4a1285ee91d2e7efecfca4332eb034a5544543dcf4846a2a34e00bbce02a891a72fc1dcc22133adc02188f0b427ee3a019620aaa59b261d036aee9cd729db584a904ad7d51eb9fe270d07d781d905782df4bb65cf99088db17c68ebd46883f8a01e9a0388363240de83290993e0fd1d75f02a029eb5f2d3633de540c254dc401e46e38bad92bcee27720cf4931b8f080864898c71139852cc77a36c31413460eb8195659d0f229b7ca049d417c7a80b35ab87b8601a242a1c37fd2b4ce9e12db5b88d876bd0a8c9e56b50ae8de7a2d574d9ee3e6896cdb1709afa2204afde85e4fdd452fb07210cdff6f06ff8fa6a26e9dfce01d0a255db4dca10a7982c34edb468b38c39ee0cd6b7650f301bc4657fcb094d32ae7a0c9527c081722a35875bb2ed6bcd25bf504ead9f826e1920324fa742e3841947069f5501c84c9830e3da96597f4474fa7e76fc4452ba36a931f5c584677ed7cb5fed0886aaf8dd0132883de240ebd40592c53cf3de05396db3a637432ae5d175ff55fb2643783a273499818cac19774f97fb22de169e93c877a5ef54d3c209bdaffca5915ebcac68aefb8c544045f51fd3e6502d1003390b2d130b0d1e530fff2dfaa3e08cb1441131fa9f981e5cb1230df2138785395094563aec145e30acd2ade5749634fff8f1515811b879de98b9d6ed283e3579810606f33cad2e8a6be0290b971a4d75ed30cc72ddd2c92f43a5feee8efc82f49c810dde1e6188c0ab3b6e57fe5ba06d73e935b57666fc98b29f09445999835c3052abd3d38d930033fa7b017c0f80e5371f32a11876370c1e8c6653125eeab8ed96eeee94402d45810b74bf4b395e7b108b6bc454dc092a5e0b817e5209918ec9a26feeee3c833192866166f8506a560a407a55d4bb2443dc6626b40e6731b58aab7b443ddd1b715bce76b939ec788194bb2c3664212554401603e97c1a2859c2d03cc9c170092e0553d95ee331ef09e64c6922511bf402bf1e2b0036f95a665c58ab54fd560dca45b2d45a957df990f6d139e900af15b57669dd015b89a9126072733f525c6f934176aa305112c422950daa4200210897e91e27fc8adf59f2a25ffd86be6b6de436d1b0e7d2166a04a9423f20c46d343d24c13428305e502c2fe101ead0ebc386a1d94ff236f3dad173d881e43cc7face83567afa4491e2c53118042c3d1882c775736e6ef7896f39419b836efcc15032fdf280fcfc8998aebf5f2b23bb21f8a7446dfb6151a7d6d8148e7b1deed8bcf90543bf6934c5572ae1b6aec147775ecbbbc25e66c7970840ec7c8de512d8f254b681f7ae0d751aa5367c63ec2cfe7521974bca0e67025c9e31682f060bbba8870ecdafae989d5ad619464b228f166a36f8adaae21dc835eff2fbca69ce41c509a89818611d97ec977074b59e2a901ae6fc51e8f6c8848bf4061e1d937dd54f92d0bf00d095a9946000fa40c6ccb9e111302af981caa5489464b0437f2187c0757ce2138e1de0ed0325e7c7d16c187999a2d39089725e58037b696f036f41d8fee6772884867474d11e8632603269147580e38f0da7d714cf3030ff50dfd03fb3c027d5931d9d33ce3e5d6e5324af03c65b3aa285bb42ccfcc75501f110be2a14c23dc068e805877afe6833e21db6bd608253e6038dd335b152b0c70bd453ca5a22ca355faa5f325aa9b9ef68f0a62fe5fc24c77eb0fb8ea29bd2adf2de77c1b19317c8325ab72417aacb7dfa2ab064ef297d0035b02e6ce635cd11b08fa25d649c230cfbff1459641c997fda5e5da6750ae34c0dc4d5db9e538bd728f05e6560aab66953e96f07a332c306ee9109c5220edce03a22d6792ed9b0b288c263ca00c6a8d0be80f3682b75c109c708e2c6ac8839d5d3d958947e5f888ec1b8113b6470ccf3df07f15ed2f094fd8ebd379e856394da2fb1f50e0c303c544372401a88dd5d2301396e67e9f7e798cd47ea49135008505c5649846b8a9420dd394562a489b060809ea64d3704680107f88f1c92608e0a6390021c44b99efb2c8d606bae29599191d0d463c20f2d6e01b1b295d5a1c98b5a104478d92e50b0a5bb1b97b9c332751a3d37f0a19c6342a4cf915076a6503f7ac7737a8dd5423c4c77a10784ca3ef4b4d60176db485d2caaa848396a3fce5041d56b6da457f7af51f82d750a023ef293711abfb57da0f05fbcfcf240ddafb230f3456f2e8203f82e440a53816cfb92a2d93366876d3480b854ad221d3c8ff2861667fd4ec31897552573ea6d1672fb7051c4256e69974545084462c85506ff4b217c633234b94bac9be34f923241f78e36d1c1de348ee01920ba7fe1f40a0201c91bd4484a00ff070833a1668a10fbb0fca588773cd842471181c69d01ac2f0ddd181eec180673732074728f16e2e6a24112be0b7edabb07c2b5f74fc9f361a43c1f0802d68c6c80f369fb2b607edffda3433935ad2159818ae630238cb630c9d1ab111e4f38bd31cec4f4316c40dda9df80481cae19e610259986e9c582a43468f306e2cab755ba23dc1ef97ce09bf6afaf509c49f54cd7c68f7c6c57e0caedf66cef0f58f6bb709a15f62b89321448a78673d8f8b16f1c2ce51560c1d771d9fa65c1b3e1c8c748fbee98477fd9bef69caae4c6113d02c73f18fe539736f4576e67b0bd3f330af5395eea4f9ebb136e1cf369283b53e2cb428c0781bf14c6fa62a60e4feb2f19318b8f5d95c5f93e5972a9803f33b21a858f617a61d9f954d30aae1af233c32c5c10a23b41c7a92a45aafe96bf563c70a96afcedd56712c978761c237638eec24f1fdcefc05bd0b26ed45c112ed5ffd4e54801f90d535622237b7678ad3b6fc609f90343a2545549288ab2b90e94a12b9188d4b4af28afdf260b4e03d25c5102ba80db1cd6fdebe931bfbc7dfabd389edea067688cf99fcac7676af8ce5aa615b4292ef784d14f2c9ccff4d79ace22042b0675f136565dc0f9178164b78e9eb6066678080c32662ffc12678d1d63cd884887bc08ac6086b944e7bd9511c443e8d821d909933344462ad198a278952ceec08be29b24d282ccf5477d0b30cb086a8617f3ba928635734a321902d8e68715eb5d1fc57acc9fc12a028a51b62a9e672e4b384d15c68c8e157b749fea4796714e75f0491085f81569d7e3cd2eeb2748c4a4d8c96572b872bce08ca312f84b7ab6405cce0d79c380c084888ea10096f60ff88e7741ce8e4ebbf62fe0df55108c854f2d8ad5ac45dc0332a521491f7cf43a7d3fa260c188b31e73549865beb2c0eb03e79bfb5bac88f84f2b6a11ca91227470c630e0c6ce4f4aa27f65984443003c1c2d2b716235a906907e8dc1ff7d77f751f0637621d0b143b2e2ad36b8fa26914b3e14764141b13d36609f29eb184cb92248acba7028b025ad909c9720a6bce18b92486f3f495d4c2af7698227223bfed83e592a2852e14d24f1d3e8eba6ccfc6d00a288b7d0952050988f61b3f1e995f8f8a0f1bbf603bfd111d34973b610b0ed618e2314880ec1c5356b6cc71190704021706250b55cc1baf33ce9fbec0894db83ceb8855cf4cebf58c60cffb8c100c21e6b02633c192103cc86704f60fcb8e9b3b5ba95f9cb85287763170d081992a33853c6bac168144158e08587e46e2542aeb19f914327d020167a43742b0a004f7a316cc65b3e853c7b5855633acae6d85c95a0b30b209a37d9d8c888de63f41a07b7f5b2947947790110fb346643971c86a1ea7662e66288a7ed39c4c22257b772454e9390a94081595fcfc2f372d26a526ccacf667a6e3115d300ee50dc23cd915020a8a8f14173c29bc8a4ad2c9e20a2be2aef818d9806e8c86d9e8cb8ccd55d5581ec24ad6e05e8167353718949089208152cdc32b5e8deaf804245a7ce71c42861f6e580c15f3928b1761679f33ca259a83cb6065b5ec8cdfee5a985c974020517d4d83214a6038623dfff8148dca270f243ebf8f84fe34fe26fc2baddacda6083407882de737fc575cf8241322c1cf107e59040f1e7b1a38cf1b3cc99b15167930cd517fa85bbade84daeb3e9f5345a493181d09ce8c580cedc165c8a50059d0773ecff30642c6e69bd9b54d37cf13429d24ac24541682ce146c4d8d3114d604abfa88b547631f20a19a0efa2e31a80a5b63b16a518c3c459ed61ca802d195fb06e0acee2eb8b3cdd2701d5bea775c74741fe36480e3abcfe41cc236077c0e7545b7d24b37babb1e4927b4140e2783fcfc6b8632bef7a78e9f068c23a8651acf2d5836131d6914f2c32f846fdaab46c746401fa41a441b9d51a0998e6f9a46a36d7cc63430ebcdb0576c06c8cf6b35f56d60f40f1916e352ed5f578f434204a59f3d30aec651a5f0bb503fadc3296ec264e1a12c1978778e87b026dcfe5b4f6cf772dc737d69e88cacc3eb391f3e613a61616c2f2d4b2e09e3a743f8dfebeef34901029b3482c9c670e90c724f0e5ba685a2e4b41be76e31eb17e881498bddabf5f0d3babc0a9588d28281f02339ede2825c5af131446798350200b4757a0cadedc65b5b12782ac339de77e82ae67cb0660b640013f375ca5bcd9d9032ba5703883e1a1cd85b74bb6c2a7a72e5c308c70bb55ffda0268127be052b0f88744e658470035e1aecfb2294c631d1f940d4013a1590036685652b3d2715a1398f00b973160b0e402a61765e5998cfc314f201cc8e7c2dc8a05e5629dfc6ba40c30d22771f4933dd59d5e6ed413036837c4d4380d68ff813a32816936d8988c7c1861f06a40e12f5918a8d9f9984da9f10a31803145dab15810c34517b6877f2a95fb0de14c00275f217bd76abe5b41f37c5543cd45a3a960ec909d67b357c3a78c1c643b61be43b4866adc0e3e93cfebda8e2254955b3b462948146d232998d2f4f13c4a7a180e5a127a5a819df940b97cce016fa988d44a6a1fef072ac78ffbe865333f5a8d7732696e06c3b803fada70c41793b2bf1fa5a5afe516dd53d43786d2a63f714ef46b216be76da5a93e15064f91afc568273ca2c6d41bd9bd7d58b476364fa804b65469845d9ad7e380316ecd7f10cab7fb945054b337c6488024703f6a51dc4dfb0327b0906fc7ea11799b470d427e4c7dbcd3e7f615f42d012a92494af14c44a6723f44f99004053121a84a5b1407f37770b48fc293563ee4869881d0d3702a5afb7e83505c0f65ecebc537860ca08e0ff8d005c1ff61646cabda387e082270a6b48499f27d9b8323e9b837d9d48df6d178ac3b49bc90a991bc63707e10c6c67ebc77924972ea9320f960d899fbfc3da3d0acf9e5e94bb6a9d2523d11f097d5076805f9c8975564d581bd46e9ae89ac40aab1e634239269892e45c1fdd5faac9906e24e755a1d98219db92d003614223772dae5bb713a98da17e0f3dd7ecf8fb2ac2e65d2c25b020ebc7e7d51013a194d5c9db104754d2e5f256541ef16dad86738c4844aacade44b8348af5e1f7f1db0ec63030d4249d018ea91dedc32857fca80bffa71144cc7d8e53274e4b08e4912700424f2e5e158fe7fb3ceb9b589dee206b3a8a12a594057854084d3ebf9ebf9b3a41294f5478ab95c073e551e9feb41becf7a38cfd1a20b8a97dd4408f9514e55674e827a5012cc1d92802f331399696a6bba778be51d9e88a4d56b67b7ac4105fc4951ba297ba4ca957da342ad2bd89605b5be961b80971c047c62952203e1f33ddd99e44c5d96bf09aed2cc1733257976acdcb7d095430fa576c84d52ec78b4ced41af144a51dbe01cf0223c9027a0170cd5ada1c052991361aca7fe097d8ed76e0d3af59e6bdd4a0048de7481a0e14ab14c1c78fb10891ce8bcac052353d8a3418b216ad2665ee0ccfe74483aaca0e959c2bbd2d13bfff96d5975c0c845840d14f0019a664d4f58508e58cf0183437882049ce836db175f911bd4e84362ac5cd32b13fa16d02697b72bfa092513dcd5d853f8cc21579f5d8780e15fbb1b464e874faa12393e8b620f418fcf6faa66341585bd6257944eb69432a594529252cabe04be04d904734ba837269895af13522d6a2af0afd85bc73077bf200d71efbdf7de5b672006c65503bb61ac7a9d09ab97bcaccdc1179b0dd77151a7a9c44c960663be701046b8dc410d4aa614ef451c9cc72cfb2a02026689fbeebbefeda5c3d118e3da77df5da4450c1cc7ca0feba7a825b983c4640ae614732180ca9a3d31bdf7de7922315f39e7bcb9ee5ec39e1f89c939e79c338f999173ce1552b8def556afec9ec95a6b8df3c6e67565e59cf7dd8463e2bd9bc3d438e79c73ce9d73def79a3f79ce91de5aeb9c73ce324132466ff60573ce3fde39e77deba61b268ba354434ee49c30a79973de7be7bcf1ad75938e30e79c29d071ce3dcf6e9733872587cb0b476bc36b91634768d078cb30768443518b1391745efce64819a1d842f461f3d6d2c1a3c6a605a66346c852d751a36469726c587a94e5cbfb919b2fef1ec3847a451c8ccd4b966defbd572c168720ea14ac1a429959c0f5a2c9c70495133c46abab9f97a898a10c61303020bdf1ac7b534f7dfcbbee1feaac7937daa13ec6bec288b92eeccfb7abdd5ffadfad027f1f08659f1bf479b3b20a7175a1054520dc60a471686a0aeeaaba2825115a53382c3abbbf27e577e79a73cfbfcb3907698b46dc729eaa459c6b9cc5943cb4d67e648b55752ecabbf725ac513113dd4dc2e49eb96c3c72cfc7cee022fc73de23f04db4aa27175bb21e481ff2ca4aa6ec4845ceac3cefebeee0910e3747d9d18eeb4865f7cc33cf4d9ecbd5f4eaef5bd167c65d5b21a531d46c5f79f7236345beee3ce0179ac84eb45eff2f1ba621278c0db1acf8cb8c409114fd39e06084acc87ffefbe7af57adc586affe3ebdca18aa78752e7b7bf5ffcd603339ab1b362e2988c114ec2cd96230452db105f516e1c0eced4c78363c83c15feb262e1f19cd013222e3eb84d3abbf4fff9ffe0510d10a10e1818cc838727d696dea43723f676b2270ef8a095711f58a659fbc4bceebc4cba59f47ace66ab0651a727e31f3085833ff2f8f250c3de27594f2235f43641f092b26e911aee64f9b5c18d918286224bd2f8da7c27bdfb35ef3944f7bef9e999b184a276baf3c2379a0eea71961d249d32624d0ce66a489987afe00a0880238401a2929601de318bae7a85b4900766dd71db8edcfee95f339a387dc9a479592f7e2830aab87d2218131311610c08e2252e26c2c5f004535d9dadcda36eec813b269ce641cac0de5ae0dcbaf3f0ffc1f08576ce934ec7e9cd0f1e727bf61c3eeddd6e102efe91459ed4e1d4b2286d4c594a4a9d10a66139a1b6a6f24d68282b9f7dd775771d6b90dc3df3f0ffc1f6883d8c32ce8de77bd77df989cb34951c856661d721ebbb321fdbcfb66cc6b3af5661eb79673ce435b8563e604f088c83967a90ebb680097aa0d87aa2905f74126cbcb841148650531e79c0969cfbb713658c85ee1bdd95cde8c7670e36d8c1723e7a837ebf4320722421678641b62df019c52d961cf31bd39e7138cece70544d31cd48d60464be1b4818886968c0c0d25d7f2e69cb3ebc115e79c73e0bd775dd7bbdebbbac2ca61e6dc094925859d0779ed1ca629e19f9c838ddb33937f8428aa21e7041e75d430566eefe4325f5d8629c16b8da2204888e0dd0f4635e4fce02c8c4fcfe3c6ea66d9fb6ccf3036bc0eefd61c738d32d75845d879905f7a2d3c8cc987c7fce43c673c6c3c723d273d2e3d343d3a1e01384551595e553d9b3945e910ea64e929860da69ca457fd7ce881efc994e3f27abc27130edc0b0d72deeff90476f5f2def3090c0a7d535bb0ebd09fffdc9fa7a7de72382b081efe2eef375455a445fd3fe655856a089dd9688d669fe354aba9d6d1094e1e0d719486b012c7175441234a9f125c6b3929aa749a01e7e7db419a3dc621e8ef784fa490165a52017faea4b5d6c91fa7d7dd114386902524e3f7a9b1d6c82586576bad35c61c89b5851432f192e0140c45613cbd9550956c9446b0098d6053b9400a8c969f61cad34a887129651bc0cadbbe27d2284680f7442eb35ede7b2285a80a60f2faf7441a9b183d518c80f7446695783dde13292444c3302acc00c74c35252dda7acefb88dc17bb76deb6f30dff4ef119a7290f0f6d73ddb00e86557d1706fecfe33dd7fbafa6fe9ae660783d3f32c7fc2af0970281dedde03d996a34f4135cbb2baf713f7a67b26be75c187abd9f07fe0f6c5a49289b10a1f75defdd1ac20f2d0e8f34abde94efa8618c90a562e94b91697e198d35717316c482328c194de588a95b4314fccf03ff0712bb8e048ebeeeddb5dea2e1bc3318bff3a0b3acbbac76bd255cdb7f7218dede3736eb5d1acbdd0cbc3f57cf76411a475a1dfd9f31d938beb7c8b2445f3b9a10d481472704918c1359c17d81262014241a472c3bc19d7bce39ebaef58a95f7bcfba6869c2b57d11e4ea9dcb4313fb9951a727655f39e516c9dd6e42e4ed4bc37891c4413eeceeb79a7037b37d824aecdedbdd770debdf7de7deffdc253bd19b3f7a3960a0f9e1d61a975a9815138807069459986b0a40a0835b8a490a34b93eb9ae7b8160daa29fb8b21cc7682815ad9d2cac45391e626b899a8ccb25c92d6848e30d0f6de7b07cd6c3c587aee7984346d12b64cf0272e2734ac875feb6e7870a874a822641ebc12a2a7ca289ac5db79b7d89b3ab2ce3af75cc5b6fce426306ac8c9e32854f8da0bbb3fdf9b675da7c4badebb295f3b230fa5b8c6d673dad33a1e8f9e52ec232946d66b9d7b9d8b07e727c7a05c35cf72def5de2d61aa21a711b3f5688dc61b3939ea9df374f63df3724fb9358c1e3eb9ca52aba97bd3293d46846f451c4c6a567bd381c0c39e9f7cc8c46aeade7b67ba112b4535e4e46aeade7444760445a8a6e24420c8f7b352c8598e88c1eac2bd5558ceec26c831292c4f3824c0c1b8586b1d1f369a13b0c27c0f4e29010bab8d7003e603aba9e7a1ec6119ed3ae660226011257826139609008671bcd8a66dc878472f3a9651bb60db30843618a70cf2c11bcb1e5e7623c270ccbdbd48b141947b33aaad36846574363c287764ba5a7abc59a92127c7f90ddb2b8ededc2be69c3345e02cca959788b04fdea2ec3867bd43701d5c69ee9d73ce39e78e73e6c939e7cc832c360e70b9d1d5ef003938dd51414ea7e9ebea0559b25fec19167bb7a5a973ce396fbef9e622195d81c25315c0415284039bbef21bb9c586e39d077ced9d73deb755b3bb7ced9d73cef9e79c73ce79ae03cf4197a3cd37dfe955060f7ea817820d332f6e88d0500415cccda3adaa2b1fb59460d65b6b8d0f60c6f99e6971ef344756af3dafacaa77996ac89939d1bb995c987bcfe8caaef5de5b46d853bc379f5162618fdaeda39559eeb9bbedbdf7de7def9da6316decde7bbb4119b9e0ccc955af77dd75575dcc89584b5a0e35485d483917015d6f30a08a4c406a273690110279a496269695cac02005904c1c081715a5e0f94f11287753e6bd243df506cdf97b97c42fe9f19e4e2f746cbcfb4f1c61200a8272155f73f4dc28c618638c3700965ef096c4fa925ad40b2410542dd082f8481664a206dad50c37678c31c618639c317fcfa4d25712abe2b7d51a44ff9c5b8d39faf527c122d0adaab5c6ef792e0005e9ab82f46d2da0d6d5d5d5b5ec46d31755014641c8d0bff9a9f1d8516f806544475542ac4817cc20609866662b5eb4a52e4968913711ca46397feed109222e307ce6f26e3de75ce51b8b45a9e01c4369d6a54ca1a52461a10551974a969f127ba145b34254b76005c5b525f8f8775d2a1116f4e7621d1f5250c210be7ed7559f625cd09f238710a0f4b4c1a73f7f62a950a4f28dc9e432d09ff30d5ad7733ec675417f9e09e0e3df75c1903e1ed2ba9e732f0f173a6ca08f7fd7dd4a91e1a3d88264cdf815addfc7bfebbe8002d19f2bb1175ad773ee22a882fe3caa5bf0f1efba2ba64e42f4e75c5b42eb7aceabd23ef4e7621d1f3efe5d77ea29447ffea1ce836d5dcf7994940e01d6efba2ad4dc050ea62a04e8cf35bae41435e4d39fb7efe914e5f3b7d8f3a77e92079cea7e4f27aa1d11cb97e568d30594c07d45f0bb95f278642c12511874b8829fc10976183841d1491eb19e104c5c750533080a047e825ccc4484257f8218e4036a089a01c9f9752acfb783b4ec2501929c47927349723649d021094a6cbe24e8919a0a827ef493a0362ffad18f7e8fa29fa3df45bf08fa2980fe0fa07f05e84761d08f16d18f1e517499be404db75bde49beefbaeec662627e57d2e5c3c18d2bc688142ee4d5118e4f0a1f9c8b76188b57ff929c1a2b747a5ad85ed0c32cf9a932ea68ada9046db0571f5fadb50593359a9a1a489393a076b9527b5113c2a450f9a052b39122984dd1bca65d732d5f0a7471e7278bb75dd72c19e3f1612966cda2abadeb39afc0b751859afaf6de58e77d622ba37ba731daaf196e3d3288ae80c4508e84311dc64a9abc825b67272a9ffb9beb05705db13e676c7ad5cf7f40a4d1c386560eff0e72ae0f68d107fc88a82bee3c6f97b7aef28d994dcd36fd39ce7c03194d2634f4e73a9900614c3765faf315163cc4c7bfeb7e218168e8cf3b6c60eb7aceb98ec733fd79b10581b1a4a246d1fab194bed2f4e74aec85ab109acda86ea1753de72c422841e3da127cfcbbae0a141bcef4e71fc43a3e5ad7739e2289e816c357250844346e766aeb7ace9d90906a3e3df5f1efba6af2a8059afe5c67bcb1d6da6bfe6b3ae1c5a63fd7472723b6579b699df10fd8e3f234e225f98908778ad324d04ace3de79c73eec0d88fd3d1272946d75e27401589f09ec9116e7028aa629c7ee5ec8a00c8627119863520dc961b2c8d64c86cbac2b67abde40fdcbbbe67f2a719947aad64644a3442734e21522fa9a8222b1b534a5d4a1a68d0b53fb7d2da111a61f4c11956da75a62d7b8e645c0c3f4003426b5e8c7fc9149817e35f32f5290370102a65e5a7ee931e94b793942b3dfddebb45532e1d62d589b1a921123c61535b271a66925e4160affa79d0d68b7ebc8b1b9f755c26f9f33d04c741b4b23ae0c984986e91f350185a9b710b9f851c3a7af57b22875ad848a0339c947e022b6bf1e7585d628b2e2461dc7bcfa7b01098ebccead69bf1535889064c103294bccd00c13c3540890ac48e082faa1f15a4757bdb32238c35d0011d843c909f07feef896e6b06bdef7affdbfff3c0ff3d3146bfd0fbaef79f88b55af879e0ff9eb84203a0f75def3fb189e7fb79e0ff9e78c462d0fbaef79f08a257e1e781ff7b624c040abdef7aff8928adfbf3c0ffbd0dde84de77bdff3d645ec2cf03fff71be39cba8f1ec62afe1cab1ab8d6f6c6225c790f285e4752eb85568e9c62e7e5c848d8cd7db4661eec16c70af5d141d8719c111cbbeb1e161fb9a7c5375d146e9edcde6f9076bd43dd08f10e3d5f04e8a5aabcf943724d1ad60cd74adc8cce82b98d4b4be783965213ea03891d24a014fbbeebba1b8b78b5d63a05475f904251e5c6a4152227a86f54a590404ba2a144810ad1c2d14c7a39b2c230458461368249c7624d797cf2919371ca10c159efae5755d5cef789070fbcb3050d0e114a589207080239406c2848593340597b68c1e2d6c4c399002c589d502d9d09538c9d4c2f486ca891f2a1c250b5551fffaeeb6bbcaaaaaa4b1c69eb7aced5921dae5755550ff808ad8829bb3ed808aa6d6a68314e7e328c05559d3592467dafba9dd402fc764c6060f2c7d1b573f592fc44ba5054c5300526e17413110208471345165e1b38a2a1583114c349c3501f74bdea7b42edfc88ac274d8cbc55753357f46bfca4b5d65a6315ab2021d88f59901f0662f5a7f3a5af5eadb5d61aea03a577bf27944e168f6357a29fb5ba976476c93367af8b4b3d1d206ecfc92f093d404878926595d85a96553ae3504a456ffb9e504a5fe9cce618f776024ecd555f8fa5277b3db743d74e1d095625aac2ae08d8808011828834aa0537d2060e617365c393ba9e0050a48cf81122b51c0f202424e265cc84a689aec7f501035158d89a0e87e03e51ec09ba414199aa42d1943f75f5b24ed5d673dee7a426206227833127e7eb8393b956024cf8d6010525957b32ea81c87011b40244169d8de09e29129d29db5010d5e98c7c1ac13ef5ec37760d8280084670019ea13893b6ab9d15712b92bc5a14b5d0565a8ce02885730111042637a69cdc0cdcabba9195a42dc9f8c8208102c5d756b1fa36847dda7dc34508ab0ba808f85670e0e055a2aecd0b03328c922aa09a4d6bbbf6ed25399724a7d586808b1bf5f3aff63d995c64e0b16102a62590cbb1a8864e1063d0c909ab91d2d0869e522dca942f78466060727bc5b694741f8c4cb8b44db11875c57002724179010b0d1d9ae18cad213716d848daf0b695e5c4e6f22525f3f5352364a4b23863dc637b2e4bf267ed840b19365a6227789ed6e256bcb829e93028c519d5340844d0b98c206aa234d9c63c287d8cd052a0343d4930952aa7a41a1a402976816998014af29e62aef6ad365dd82156d2d28710aa4cd008eab1b2e41551d209ada49a53350d032b322323286b1954040b6aa2cdd50ca01a1918fe3da13ea80184a2e8779998b44633ba70cc619acdc035e9a8a5cdb04a52f1820523a5f1d3b2600d8d454f8922a7280e0461e114e1080d3df8dc2c9de1cf3d3c1d45a4b170419e0500c450dccb4f195ab145b1989662df775db7a935ce1fd0485533af179838156708bdc1a85afa5241828806550916c5144dcb7caa3a6896c08a8480ba5480c538aa0c79100e959820f2e87b426d306e202ef534a1e88c21081e923c54cd259d000b4aebd97a4b1301a5410d4a839aca7572f8da39060c94c7c3c6bdcfedde33e963838d788ab9f1a4cc34dd0ad330f654924562449a3245ba842e8c7caa901bf80d9b4e4e758a31c618e31e4e3bf0e7bd20a0575579cf0deaf092f4b01b0e8a92cded93cd810e2c5f5831196ef40473105c518b0f533e5efae9cd69500bafa5c5d41453ee052944f205272e965ca8746c9ef86806e834004319000000001807822809a3d4cadb14800922e24c74b86048209947a37140140a86621806e22806821006c13088c138a0b30cd700dbf204f6661d3eee7d57eea167f2a38973e0ae55d8db3aecfab72eb9c2f7a4899c5f122ee28cd3fba76d3e211ad2c79bc36b538541bfde4daed09d0499f34b42459c917a3e699fa54403f87cf3786febe1fbf72eb9c267b2444a6f195ed22d63e0d394ef34e6fabd7e8c36c5df0ecfec94adaa7cb7dd0647c3d80530d89f3a2a948bdb8a0e09c25b8410a1bc734d6083f84f891d63b485153c611de27e4deef36e40fcb575f8fe0694159197025e58906ecb57c0c9e24148c7429179543a2f25bcb060bf2ddf81261787265266053dc3f6400c97641e27e2f1dcb59b1777a7c16aa133702eb86fa495525b986d5d16e0886d4d0726fea947422584b64f5482f2e5e6407b5ed700155143e5bf64204cf3cb1fa4d95e49edf03dcafcc37a4d66937a8d3d6587d1ebe9379d65e78f3898d6b36879d951843682bce1e1da4a8d00c88e2820289535bbc6efd2797661f32b0b44befe3c1c93782fd837a8c7e0a0a52b1e5bd40a354a438b2fb60d33608c03272639b5586153e191f5393df8059fb7a141451817abfe02b94e0be7650079386415f11bcd89e35e92135c814b5fc1f39bbb33d848356bb2defbf6620f0ce2227b27a4e440403720bb15e4964d2fdedb108d97e6261614a61ce8160f48f3cea93ace4f92d1f26f86783d915cff330188550f6e687f42597aedc89f134948a9e85a4e5c1e64a6080114cf3229245483dad1dcc4b088e24b1e07178e56dd107daa70a579543b0c9cbec595291af0ad32b07c83779ee1f85a1ccddc501fc414e9447e80598c08cd3d3a90feb45c0cf3a86e543ee5e34bb91ba8ada4eb7ba47815ff1b149641d692138a5d0d3568e6077eef261157c78453ecb29d745bdbe5247a34aeec724811e1fe729d4d4217136a1f5920f9ef6bcfe6359ceb54a64d6732998b71a80e04cd412e5f6486d12da5e824159abd51970ed08a61082e6c518e7c65c141dc3dcc84ebe3c498bd065e581d11128d14b3274a089902a440893e6df2cb66a4e5d316dd208c7c61afddac9c876fbb4f625af9b5c927d1ac60c84ffbb1df789d9dc9baccc5dfbed87152a25aa69fb9d58aad21a04c430fafc76c0bd70596d7f4477ddde3dc6ba10635bbd04740d5f04ddf6d4c31d16231ff6fb032a10d0a7538308194e2a1499a2ec3934bb8a6b649a2fcbc3bddfdedf25e66924ca207649ce557b40fc7b456ac86148d89d2c362e2c653fb1978c8a54bf179bc8f58bf71a7585aeb962b00261d490596664e785e25abe14897d1f93138ed18ca49ff74a9c974189d3ecad36f2018ea4a5118c5721a3316dbbd9e52cf229eec4090ac4a00a42f8c20340cf9b044d10a05f4f452d6317e470fd9b90f9d3801010e126e57bd9d6965058cdd46cceb8ff5723e0247db44016526ad8563ecadccd2cee47480c9f72a672f7d5a7ee8f73b30bd5db7bce80a9564864e02be93521b47dbdc043aaba86e663cd364c58f5ee6113fe7804f233d1aa4c01bfa12ce307be96271ecba4e008f6c408d78725b9c7526c0768e521109ae498fd3f32b596d293c2408ab77f791449de3f42049376726dc38b0c8a67b8bc11b7800d7ad96cc405d3482f82ee0cca9c761a4a17d747158ad8d7735b1c3ddc2665c7e0f3607a59e6c63f5da94813ee02210e6dba0f0c545130aed6bf1588e43fcf0c512e60c6c210bea3218ad5c4ac1614eadd71eb21cf740632d76b946662bf7328784b70bf59f77aa6a601aefdb13437635a0c1c32170691db959962ae099f2881652ae1c574f1307be432ce05bbd83da715449c1df2ae9c62fdec98f85746591a9a7ad7db87bb2153c08d55021a72d808a3e92a610552e7cb59bf68c29e27bbb57f4ae097f9d35efec40b1335030b689ce2416bbfd982b05396f39f6036e29ff5a5c005cd9a1a1030bb6b3b07391223bb188b490058e491e79f9559789b6881fba932a64712b466b4d1fcca0acb4e02e17e17331c030fcdb035b62cda786f47ab59ece1b1509c29770fa1e5b5349a6479e1bdfe9e7af49f8089875bf48a9414e10b9bde6caf462cdb334c5e7525e8c9b3e0fc11fd48c18155c2b6791a594c6027280b41e3031be9938befabc645a6dbb6e48f2684a314afaa8d18886c43a0ac63960327402b06a57067550e20a5c3ca1c4172d33cf9e1f70bc2d365d5c851110ed737dece0ad03a8d0bb778a9d583af3f9f5709302d11f8c6c6119dbc705372a639b8aebbf7ab918d39c5c67e6b112741bef1eb1c59a7425819860251739c8ca0e7ab8c42716f9c79dc5ccd08ce68bcb6ea1024d3c2983b1ef15047f221778d587b1b7d74017613cad07b661ad3ee70bffde75f1fb10f6569cdac1ce441e3702d7cf5b13e1235870d9f8703b80c4bb141ca4d20e94e1b927fc1ac727f72ad4a43004744a97ff8d4d11f343f08b912c73035766486a19a5e9ccb83ae55d9571cb1ac27876529ccda699f4753f381dae5722d125e2ced56ba2ad633e823093bce4c1ac769ca1d0ad2378ef2f4662fbe3695b88960b68ecde0faa87af0357c4d3c458346dab869d3e90079512295658f5ef0a2bd3f0dca2382f43d0aeddede4b68a0a99afe27436841715b2c7fc9d5eb7d093d7890524586b43d888f91603985ad2b38dbee95fd0854edf33e274845bb3637ab9c025760cdd2237a67bd597b70ab6a05d2fabd484333e0e54d472e088b56282dd4d86215ac3b78c203926b6afb9e4281959dd85da92fdcbf3a6fb22230ded912e3254a9e0ee3d67c06c1893ecc647e8332378ce8afa9804731d863f5880526b4c1ed3c3f5f7c763b4aae0f472bef6dfd4155ce95aec11f37113d0efd0e51a203994aacc699aaf1a66aa3463a4d1152d3396d139b0804e9f815c32672f72e178b079b4b9b9864eb75139706bdc9b44ee9c350936c79be579b8bb5e46469ae0ca2eeda2e7f5dda0e135f29251d0af519db565b550a5ebf652766e02ad9e4f98a61505ac1e4d4fb17dae74e714cb042578e18aa11879b6983e77dde3a900b3d9b8d6534704f1f74c87b19f34777f10fa5ec24a9359d319e696b869ed27d5f7b99152c9d0baf0ae9a85b152228b0b9db25f95d5a19b56d138d469e2f9dcfa256aba0b83fb36d4ebe0c08547eff0816f764e160bb5102e0d3af69c4a04d45de3a5739c4bd3f01ca5029360c42225b9c2bfdcc2590939a6b6efc80d85754334111234adf894d27203b98ba51111b34a9943d7bf0c41bf4d610fe045aedb260ffea5b8a5e39ba98fc911da64d149a0b996e38701a5e9672811275282501b9b0e580ca628886bee926dbbebe7fd62d298a1c831066bed8204680f0f795250dad7d8f8a4f7ef4b666f3e36b9906baa48cc64c3ddf1249c514b433c9e1a0d95b44937176561ff80c8184f5056d8b970d2df05cfdba633f90234f255589c55fccb1c6e71b6596cec34bea76bb31092092d9710ed40fa270aaabb8c8a781688dc8c084671a89bd9d389ae961e502e28db4ce59379dbbc92100e1779fd08ae6c42824b6371bd92508f457dc3385fbf5daac98b32e085742ebe2f5a88c01db7a15b3d17e23e66a037a4a571b9f220b87cca1fc1e5d23661979eb504cffbc459441a9a9cf3e82c6c0c1bdfbf8ab3f01d5d9512a8bb5e90f031069b8bd163482e8c1e04d7ace63707df43cf1fd543707f438e214aef7f10c92eeac1657edb69012754e4053a717fa31e09ff4eb4902bf951c7485ff4247565fe16fa58ea49f7c522c25edb8265ae8d29563feb502978f2e264f421987fd94ab10b30f1336b9260c31fff8878e6a29a6f095fd2e14d360ed104d57a62e4d593b1a584877c81c7a0e5b1612eeab57670cbc23425d4b76196e8438177cd99a8634a4cac69afce86a657d90d92ba7296128404d61147090490b1ba5823d4e911ada7b2647af1e288d357d2f24d6c5e604938d878d06be81eb66493006d49e7cbee592192974ca107bd247179bd50e17430d9182461f238a1233f1fa00af5cbd10373ad2964b29e4a7a3032d9ec9c038bf3fe147e2753e0113c9978486e2559271ef093b2277bcb5fe5d89e0caae92f4b1a654ace764958f0405880be946805a6cc3d496e0589daca7e4d59396636e5c9425f00ec087ec7c364ae772c950ec201cf9521145db9103e30884fe112bc10645b7e1ab06dd95e5c5fc08107ff31479243d92d23d62054c2e0abb278c1d0305037c0d3e6afb68708bd192e060b71b104d3c561d0921749432eeebd77784e412cea1217376d71b1bce330ec352fe62801eeb577784f7b58647431465ba58b365e0d9e53bd80abcdd9112e890f5e088bbc8be87bf65d5ce43a5c537ffd3bdd27fb770b1f70457a516f18f562b3f30a449eff4fb0bec866b8f6c55d1987644ca000d177a26fb873d8bef22faed37fb114f7611100216a728e1b1c5e22167d038c9b52c0586cebb00890103519c70d57b98958cb73306eaa0763b16ec3220042d4e41c3738bc3c2cfa061a5751d0382d83588338ba365480289c7f527b70577234e2dda3f1735f37651191ada3062ef0fd3b8c2368f9b0f1cb90898d6545f3a11d1b02134d3ab1d75bd811c8b3b1f1cdc4c7c6b24213a4069ad3c046c73e7716ffa5635b331d8d6a599019a9a8c185ad279bd6adc0535a1d4f9109269915443ff4fe0c75ea0157f43bfce11f045f30520a4afc81e876a88dbbb907aa0c42f1b770a363ea5f4eaf4bf8f673c24c44bb39715bd0d10ba545774245bdf6f229812bbbb14b99c8b3f65d577b5f9c64db1d18ed499443f49a4e120c0922ee5312b0c2010bf72270399ea67f030651d7c70429b2eb84359a8385d4c61590b70a2e0fd7f548d9e92e6087dd610218008c44fe5529a7be1f910922f3c3b55df7359ce79bc730a00481cb829488b706584abd179994feaba0a458b79cbf0a49181f1dedf42b55f9277975037e1489d6475633d448e9cc47051a51a3e42690e206144896150a24de8f0129a62f80645e987f44fc803fda3a353f0a0dd17de42bc1e923eb930f7b2ade1a899f6b18be1c7d21619906e012794074b21fe9c517d7480e8a2808938b5799a71d2e945572ebaa017f7c6e641d869fedfc95c4d07bab2194c07e5d4408b8a8410ea4e9b148ec6d88469cd6c37649b4f4ba45f068abe9d96fa433eff9755112823033c174551d49679cb1094edb9820854da63976ef26d621ebce3c6461839228fa6f72310466857b3ec488115ad371bd4326524b3688d2bda31a54e16b798840ac56dfd1040532e00b8d7915f0057fae825fa5d683ff6798d9778b2601aa95ed2e13ec984e76fcb6502e36e3717d02dd1c4f04c560a4fdf3f4326cf8967e48220511dd6766873b270826408e2419e3c7bbbcd084eb594eb7a2e7f5244d1092d7e5d4f136a59377a5a494184d5f0120fa7483982c7967567d3b5860342ce30f4f2e19e90ca5596429e9676fd192a0a046fc0522650457da554d45297de756658796de6847e653b2542f05ee1843cd3185da59dac0d5b2d6dbb71e901c05b5d82396b182d1172110844b291d5b87924ddf80208e1343c4cf0789885ce2e5368cf740f3debee9a2e26df1b62cf46c32af65240b1687dad0c7261441bc0f4c629907c4ff693595b301308fbdabe6c5c8269a80ba281e301cc6c62d6c01883baf75e28e662dc62813fc7816792bfd791ae20e54907468aa51e4a0be3f6d45fecfbb3be4b782947c53545e32f4f66b59b64364670598f13e74571749ec8f2791aeedc4e8b57c3c4473acbbeb9e4c27d9c39e04bab15ba4973df109c8033d1f2dc9f02ebda24361517aa8d8d8afc09b1ef93934948abf00d36655e8f408b00fcf7010ac42da014396f81e5126c0b9510fe5d6c32a64af9fe876d2bf770a47985635ba3dcc0c84e9288fc7b9f6937d7b2718d1e0b161c63d10e71d581ed0fb6989840cf2ede642e462020589be13fd7f414dad5466f2f5910931f995f7b37612341dc855ccb086c04a519b34d6a7a0e56611f962ab86cc00c59a2e7d5fbc1ae2b3c617d47674a541fd2426922c03867fd2a64e2abd3584f50ec2565bc2dc67697fd4ea4e8029d7bb6d5e7d7c28012ff64fcfded64b3ecda84e71b51671831e4d6c78e22f340d6462b981a1e4fb5b6991eeb3a0082e78983337215d81bde4fa16d03c01a2f6e4a2b027651f85d2b7c304483081db2ca008285efd59941149ed7d219602f42466472027b3aa0c9f3ede251081934cf0c2ce1848116a5aea71afb2adf58f70b604d8add5fd8af2110fdf4d6944f560abec1b1f504574ba143b7707cb50f9e121b59d75f56ca87f299f3743dc599c92938fce4270b0fa29829583769acf8c594996d0cf2b4a43e24bb0810d0ed45983a364bcce51e75502a9474114a885dd7f9b766e2687cfcbccc1942613eb96b48ad1a1fad8d12badfbdc100a1661b6042c1638a765282e884c80841ec56d3e968c307eb1f75b90777c59dbc31d362a61b69ed348db3ba2d970620117d2e9cb5344f1a5791f4cbdc3ff74aae9828d66c1e3bd43791cd0a60af84c959f524b0d2436f4b61ff1dcadb0d3da788c9541ef1ecefc339bd74a8c71937ace5d0cadaa1c99c0b46621c0141371fe38386d27cf5bd47718551b452ac42aaeb66b08344dcebf9d09c5b26a332ae07b69f754d11b52c43b8808e8030fcb8c355144e540bc67f3b8dacca9e616dc211ebaa1d2159a82bbc71049a1ee25fe6d8f091d085545201e416d418584f292abadfac222de0b38c9b0829456a63f0adbe1e57cb600f3884877fe85508758a22ba77319d69c89ac82c448268a5a328aa16af48f6f4fd76513ada278dd7d1d5454a218f5ee808606435a04a64106ec5c07f18e3d598dfd5631d9a4829b90d0823990578f6c58d42e04d1608fd8ed5e4214634e9e2bc06bc00916ce56ef0e274277b44683c8ebbfc2b62444369d789aaa30880693995fd38c5d19658741f66e93044da063958358063dabfef42c42a322c1cc9bee1b39e4159759906b4507f31397898785e841288529d7cbfb2d61fe38281075f905bdf7a22cfee8b7c805cb7a67c3395c662506fad3138aae5e9e8b5149499b8242cd7d6170201ae4a629d56e4a1823b74b1af40763ca78775e2c34c6c0f0840390a81e20b598ad3c5c3edd1617b4cf7a8a6b137d178ab876b5705d2672697fb844997bf26884fa030559a008368156926d54480e2e312d7c3ab8700982962c11b1616f21b1d4b0ab270b4a968a7f562a3b2496f4722acb1574fe1b21ed66090a1eff636b96c835d4254009b428a34d021b0011b2f7183d5cdada0368f2a268df2696059b9d3bd473c5fc33760451ba46225ca17b3bccec9f5548d6ad16dc3b75e84f5e229aded0de5bd11607065da6c957f162454510af772004d3a4c2ce1a27558d0c51a194b8843a369f7af39542bb4634d0331dd712f45a7c3537bd8b4f0f152c12a92aea5cd733538f1225c3b554d1f91d255baea35098a7a930bcd4d53eb4aee63c46ca522a3a96ee12e4b2aa87583e32851e1ade0903ea71be35827cdcef12c812bbeef1c1062547fc45baf6fae952bd65b5e2895825afbd2a6101401b76da9b1b20e8cd02495539fc9c571744da8ed6409193164665cc0d37f5498956370b529ca2756379f0b8a5abec809a31c8325df707ec88e30dea6d44843b9458585f259632ed3c16a507530b4bdba2c47f9348dd18d872d14db1d83d11be34255944898540d34ee63f2c31bf89a62e3c6e66bbe1c2872a445c20004c3a692ace72033e344a246c0f09c2d892b0c3a48af0828d61e6e5e120705bf805ec3136c072ba3fbf91747eeee5c8c20acb112bf4b38c274425d669f65014d19a39effec20828296ba43a64190fc4f5418790f411380744387053ac50687a331f440e9a4c21e2cd143ac203b1bf35a6fa5025568d38750efdb518f3f7040ecd73b0c8712e77c08cf4256f2286e3d4d37b5199dcbeb95422799cc9723f336938107e88de2f06187990ab8330cbcf17179ce66bd407010dcc5ebc2f479946650b11c692245b2c2a8edc370a26d34e995eba5b99174fabd778791f3c3324a4afc2882ec3d4d29f4cd21d5e7e013644aadc34f57d864d9fe28c4e5926421fb5fb05fb1ededbe348dd9cb73f9249b72105b1b33bfff27f32fb5c6df9b93446611a053faf23c348100cf1aeaf439552cf7bc6be9f46d61ca232c653458159d1f94f7419eec0347f2bd488b0e976fa9aad1c7a9b00c9d28ae5d5594147cbbee80a3195dda2163cb69539a4f17710d2cd0c63229d2b7a92ed19c39e27ee9e11a5b93681ad53bc6c369f820c514e89abaa059559dca309aa08bfbdd0e50084d8ffc82919412370289723eea534c9049350de53f201c93a60028b66a6409bf2cc66906253f573a676338b4979abdcce7b560eccda21762cabbef5aa9c8f4e252e3fdf2dd431f81fc66c4ce8b0b17749f334652ff3eb3f389fcf2ce71d48afd07a8236d7c61bc8504e4e25e4aa345c44a21e986d3c69d384a405543d29e87aed5400c757f1049f389c41652d54bbe5b54d1a613826c443647f9c4d0c13cffee35a84a6879bc675e47d4c7813cd7c6ef06cdbbd77e6ccacd59698ea6ae2355be16a81b39f5450587a29eced5bb7f92e7193204161a54365c0a9849a5e11fcd81dbfd3ac0f1eb4a46f0642640b95a3ef9ac4f1c3b12871c18c10c7c3d66531f6891e01d1141a664f647a293b8c3fc75c5d83201a4bc92c4bedba327e2d4b208a4fc665a9ff719069d6a49382a7a556ef899b9e172f8ae6c3392d6c27111909c14545831c2e4b4592e5eaf94ea699a1ef5365584a9d541c9e2391f676841a22d4b200a5c6005166c40ff5559e908eee5d2ee03d691e75917203b6a7d598291aad99e73a5eb297ead447950ea91dcd4222edd6f68dafd115d05ee6f99ff4d03e9f77fdce239e62060bd46ce3ff6b622b13a56ea0785f0b8fdb6e41718bb4710aec592af9d7e44ce3d3e9ae0e0ea05bed1a102189f1cb4b32ab07741b1e376e9d432ba9ad3337daa608d42d676f4b189c75fe24a7ba33629732d06816dbc861a33200d21057baa846f83ac4f315645578d720c5b7f29ad5740d48d7c94f0cd4634b1bdb31ba999650e039f3cdda0e1ecf97469dd2ebbb59d842d2d51b50eab7def9181dcf540a2664596225e67ff10d493d39b056e76a699aa6ac3f580428ef534bf9256eae52926f37b2ecffc0b97ed9dcbcdb02620ffd453ac90847b2f4cfdd8c9b8b69468a95a86b5127c8aff88eb73919f7f7f2f1171398d302dc0e3120f4041ae5a8604e29a5146ce2e12f6f8c88f6743eafa90763372c59548c4ab26274742244ec51cafff9413650235dd61de88cc1891237ac6b13cb0392b8bbb71a876a13d420a04829bbf952dd0044576d0a1ae1d77adcedcbf89008722f6bc0af96e1b4fe6fa642d8656e36e5a73ba2894c3adb8c0bb48333544857eadca4f61b9a1be459598ea09d562a8a56c15d4e5fa2080301efced43f65b05a74a04c8bf9e56e949a6ef1030f06bd96377911b2afdfcbed16e652d83026124e8210a7a57e3199315b9663317b961df379dd315608eb936a6b16c1e004bd009c29d0cd63eb3fac5c496af43953f60104431cb2137d9857f8a1445326bcf2dbad491c38b0b952e1286f7f3ec00c66365eaaf186386ad69ecf402728533c46233e8306a4b31fe4ec7f26ac04ea1605591fb2e260dd9407af7f8dc68ad96256c050298a10b8fd645921c840f17c905eaa88bfeeb27ce1f32ed81c7b8d082fd57eb4f5851519f320e6d914d29002bc8f58881fddc16067632c7e757091893ac2aba2fb6650004c7d30b7650e896e65953f68d5ea802300f082ac0671336ffbe3eec0e3e44ff73a7327e05c86709e0249690ab35aa52043a8b7c4dfa3e615a7c88face40a8f7859a53bfe110f98eb171cf0b22a779c831e32f645447c51cd0ba7c08f8bf13e3749dea2b41c2862ccb1cf59e85474ee192e8f2ec4a8a2634c476986f55e28923192bc3f0ae052d11c61d43cd25c6a45e73eb5265e2a5ae3d4f718a9e863439f92a1b00405bc30087542f32f15d5ba0ebb0d0f9e223c90f622fea6ef5034b7999a4ba50a6c65d21acb86d511978ac4087b8bdb05b1bca3a692ebeda382f5b6ca207efc08b6ae8436c48c5a407dbc9f9c7d3b77d423477505d4ce6dc96bb88b0071ff619dc009f4c32e189742e8f05e4b529899339312c38a2ea518700e1e73162b7b7015c114f5187e44350c81cb164aa1d5899d5b150d69080089f22089fe47e20c05bd736340d6075386ee49581ce85991d87583929ebc40a949bb9faec2c5aa05581eef79d41ebb8f4db5a64687b2e06846aeda9d368b00be3158d93539f6126b22c0d7df2886f1502b9c584014e8bba6427ea436019710f9cde30ba60eea30a5d35ab378c155d2d8856a23bf9b7206c58e1299a61ac311bbceb2bb0ede12c681d935962cb865416d31ec9a743dbcc6fb3c6c9d23a5a16429027dd886070162af8ee0b17db80b4385d7b064458135cfe7c13e967b77c7285add211e8896b84c0e389b819229f87d9a3b233e9f1ed508090796479de8e0a748f8e8976d99ec444a08e5aa80e92e41defd88f4f78794ae939603e35b4cf78ba78ca38b2813eff783ade2f700287fb0cd28a9eb874d2cca1859dcb7712f2944854e3fda120b576caf8aad92a4b796a90de3112bb6dcf5c52b5c7b7699dd836886ee697a84ea750d51b385c24e416f64b13d6641543f3c7b3438965db26035a80875ee6f49fe24985b9209c1db8fa4b66d4f7050f6de70a7c6db96aa187c207814dc8c74329c19ab8286a6f57bfc7474f6c632145a8e5ce73cd9c31e4168f9d9917dbc4e663d64126b7accc1943ad2f2c8017a5013e75e822dce9178da2ce619b03de4676a58a610717a3391fc2f66f85cd14c6fbde564d19f6a20dd2c39bebff013d60a2c415938ae79414c13b237482b6336f98d26b79439c8730ff881b4008f8db76570ee79e3bd0c0136b0ff63e28acfba3f70ab0c258212bdd4921e98fee84a37ccde1d7c684152a6f979e01a68ac20c60b9c5f9fe9872bf38b3b2f93ffadcc42d61bcb47a324f42c56cc949422048602f8e18850461dd15ee68bf6129d8542514d80ff99b1667764f694c9755400ff9a2e79f9999e672effaba98338f9e9c745ee0e96fd2f26a62c60ecc7a659e40ad47f2871da10f329d95e0d93ca4a39752285873d00a8229745102832996f8a5e6865e0b664e6ca7f7763147d86934413fc6d6048144923df8d447c527a28e2cd9289d3cc1e68c95ac9c4b8dcff3e7b0241057ee33deb162ce38452810531fae0951f848332942f149bc053307833de07f2108318f6189456cc5197291e9e6cad5f36146f4f3039d872385d1372a10d4814c5fa06218a8512f2a6ce59de1b41ede61961754bc0fb181eb7530e4ea3848f87170c165fc21c4ac6636c72196bb0a1ee42cf633d4d2a74bd013affcb3502de76cc7b4015432cc0f8d156b894328c7d747416cb16111222e26ca43205fcab49a1cc37ca2955e9655f7c523c88c382ae4d3b3432639aa01381e721a42a00f5be042ba63f54d29a442c9304ea260dd4eba844979e225720f3f36926f5a68737daace604e6d5842e81b8b3944581dad333b602818312cff8b1c4c110a5042c9a7dcc13c28692d44d910595fd19f6d609b5d4c3f49c7362fde434b73c0d922cf9b17dd5941cd5a7250ad5b3c567ee3b8ef232f115fb41e4419ff6bddcff1f25b2da284075f91fe0386ea25752cc1a9b292aa4e58c838a70e6041244ffb9c08dbab85884c2af7cc09ce0704a1429056bff59df0a9c5da6c872b65be59984c0726ab75cfc7228635a0eb7c60cbec76da371f4bb8ae48fa5a6672a7a64e96e3a731529f9691a6412aac14d2fea6d165f24c66289ae29282aa448f701483f66394fd12d6c752e7af5da91e148a5a226fa7d79eb88b33d618d9242fa921d633a27c35b635c58275274da2633eb53fd1b1daf173e824a5c22852172ac41fe9570a0a9907a140b6ec188466edfeb72fe7a8b4a6960a6688c18742e68e2d48aee26a5b68f04852eb41417d83cb78f9a58d4338c2a7a06ba541133951b0a9533cf8283579e1d1727725b17dfee165c9cd2edb8de4f9bd07677f429eea02ffce091f44bc5059a61372495889adce949c75341cdcab1fe2309e2b1a01122d968232ce100ddb3fb38441fbca09874e9cc755f7e752264a50c0c4e80ff54c92536eb4f133c24a8244c3e1dc0400155f3d7727dd896234c52f7a77c5920b59c9568f15124dd847a4369e8826a0579b8c6d66a9973d105708f6d8bd351fc4912240ff15fbbadd0f743f7dbe1c192845436c19b72f825af5aca617942c102c6d232de238799342f73c455932e4810ed48fec85c3b839d258ae5a15761543ec49e4873ef49b9040af3a4dc09cb43ec235416756796536f0b1413193c71b8794efd6dac86d387f8843a65fae72200cbe1516965e4ac7e7c70ca8de6a42c8801e91349844756cad7e26629762eb57550c9a8076e59ea733087a1a57a207cf90c8bfa96f2621530071e47e9cde27c63fd045aa1da7e79d75ba16b3aaecb6c868375f62712c901dc134266c6c077cf8d0525eb686855c83211c6fe5231b667323ddcaa3de33214e8f71da2d479a6237eee8f14fe41b902d7bd95f59ccc920f90367bdce4de9e41f9fa1a55b1b0e8642ffd52f2787c6043a68fb464a42eb3cf45d4168cd5c24fb3ece6679a6e07b0d584b46a3cd6b4590ac6fb07e5f04881ad4ed9aff699def8e2e31ae4eb340f02594c627e1f0ac29a4ef9632f550da14ee7dd442068e3cb8dc325e0ecef61417cdaf9182473dffd43b8f1a68f9ba6bff601f55e8cb440652b3efea3526c5d40bf064aa5dec47068d6e2f4a5d3488ca44204370f9b2242624d4550c02a15944ddbe2a4109e790911e35963d30e8813a0cd3408ce306553d9cabbdbbed120329537a6c258d050966fb2bd973c7109573ca10925128f06aef632fb4afab5486333c417d6208fc67ce4a9ba0a26007c836bed907e12f5711b3f767b6d1b836324d064d67023808482888b6721e14add1f68334935f26d09066e4af019182ae9c860f7a4ae578690ddb74990754a2518230833b47dec94e5ad6b25fa0877ba9a9ae202dec3e17a3197e59ce89ab9acf2502c5ca81b90b9c4dd25b37b4a0fd7a50e585b0d3605f80ea789ad830e9c31b4752668eb7af11c5e58e8620a239c2e473ca5b168249f736a34b8b1abb7e831fa40daa7c5094cd39017a7f589494b17694e4087053ad90f37a16130e8f4d410970d066f5ab0a2fdd5405eb09325545a159bf5d42e7367a5bb4be328573a174e5834d72fde4e280feba533f5a13ba3b31c1852d61893e08bd8a53aec89d12b4ac5f625ff12441ed3cf175137cc96988acb3f0702ff10e5129c3308306019f857f653c97c0fc1e048445eadce5202290492b6016b31c21de215dbe981c2e060369029b4bf391128e1edb5f6e62d1bc6d756aaeb5aa9c6f7b174095fa56ad94479d119501e675cc8eedc2f12cede866f0a2919805e2b8308b6a7252501cd8c7c0226e610d88d10f8c2585113957a6618e89edc0ff2cee0b2dc135cab34178a1dac4b06f3d042e4b06f1eefcc13879ee7b9128221e481d13379efbe492e4c9f3c809cdd21b110b80ae684057231d9ca8ad0f36e21281cab5114f8095cc67356ff6449d076302870a9680d58dfc588a2c7849a863844179715b79bc51db4a667889df357ce07d1302367cc09e843b88eca0bce78edee4a97d6a776437764b538a37ebf8908c4da69b407a75b1ee50ab5d504c82a4ce0b9c163e84b60d14b0d2a6c3db87b3159eccb9d1d07a1209e934fd19eb420f02981984f63bbd82c6e074ff683e0e9c4c0b202e3a5a520a68b2a5665cbdeb37c07b9e046322d7805831a7ef9407ce96553fec7965de16388232445bd985be91b5ec782525a8baac42249ab8431749a317ecddd84465b485c89cfcd575bb8f266e7c5badaa0dec4ecb2608c88787fdc2f20667b48f214bb2545dd747ba2e2a8561e04fc25c6d0d92a75df3aeeb312afa46f4b27e9c5321df315551d321f4543ed1ef35a6827013e5adbed957d62687436b6d08e9161fad0e8c9a81fc47c65bfaf8dfafe7ed6c432526b1404763ffaa4d1b680cbe0bc851bbf43c23fa4470bfeade26f2eaffa3227703bff23c484c540a2f9af89ba46b28ab50489d0718562fc6f44c9958581caee1f98819830cb4cb6dec3709af6eaaa5220b2700531ca5ed0a9364006942fd940fd1d4ce79ef8b48aa2f2f1d71ab425aad1bdd20b591d4f37260622d297c2dde64305dc33066bd860f1aa77a4b0f0a7328c0a0e137af1f0461db9ce188f796cd2250339e4a5737206fda757daa96a0d7dd184050617c8849ad07e0d33d594d8f2e91ce166721ff81246e0f0829ec7b1ef45afb7f2c67a2c1bc698720867943b4469d455610211833ef0d22ebdc5307802af804c55165470fd2ed11883cd1b4d763afbe4328935bef35cd878023be14ee3810d0d812cb6ff9ba23b6643066b9e46d3228c5167ca3db78bba4ef272f8ba28fae9c02e33d45e1497b5f58c53117103e1c90bbad22766a37e4324e95861b94e533d12463919a649b677dccfe797fa921370752e4e24e89c7dd9fce35291f02a47c0e52c68cf95a7a5c7bd113ee5c368a614f1fc2bd1110a87c4e5297d78a0081887cd1aa4c646c9628d3376c0f9e9c03dc9eef1a3ce5f5975e0f09305f2642d0bc531b82243f60c848cd19270d5322dabe4b1eb73cba2656cd446e0b91a8c59ec27bd29c18c03c3aaac8007400818bc7fdf41f94e53b922d671d1fdf83b44ecc35996f3545a3c60db35fa636ca4d5be724253602f5800b224492cfa5717749c566a85e1e5b09b5159279ff502accf2e3ee581d09c2ad2337bd39d3733d0cbc14906391f80acade6cce6120f722e40341853161cc6d07b21851ce86298d7a3c9f330ae313ed14d7f1a23dd603b2c1663f2fd3930561443cb8045f9bcc310a969d7b4647f328ec058705fa84494612aa1df4b4695ec0e9d606ce6476a4fa173d78b7120313f6552218a307d3ebbe9209175a91eae000ea0bb640b1ac6008faa802f5ba584f832f4ca6fcbc84b55366214f7bb5f1a1c753b8910a814f134b994d37f7f38691034de008ab0a02852704c6d751584e469248f47497405e08340ae7993f1c10c4966a710a40494580b4e115807252096cc1d942c402dc11ad093bc50b23670d23ee0b0ec1f473ee12a27f0efedda12796505a33f3f9132c9acfd1d950276025b02249840287496624ebca598781351b24b72438251a20589aea3e908a751b3088948684884d04c907605484bf273e7f3d6a3c653b6f3b5bc1231a54ca1a3944384b3735363e3d652e30682066f264e061b6385017bc9728986706a39c162b4d2a37293e2926c3b4169fb3899336103f1a2448ca43a5225021582a20772d4fbf8c9cbc18389914dde6543b675766d75687463735c72567152704c6f25b4416eca36992c9b0a1bdbc8da16abbda6a991a579c1a0a23dcf9a5e24fd5c105979cc6eca5ec8d6922dd644c6b62cdec4ae6156b0b015d52fa9975457932b698ba8caa37593f582854d5e6145a8dbe99bd5556aad0aa3aa46a550a9a9e633c94924c5137503757d79ba2639b88a6cb0d5e0cdc335032b066117543ba4164855d0a420898368021e096e227839aa252150137960ebc05b035706ac0b842950354a139042a0d921a94874000f871b035ee2b464016922046c37bc69b80e6015202c4395280d400a431380a421d106cf851b0b2f308ca4060c910adb8c37e13568dd40b11fd7a66ae83f65a637403c1a8d48af869848f4407ccf44da77341ab3bbaf47f7843e1f476c7c7df1d1e7f3d0e181630a0d671b7d1c9e533d79e39d81bd9eeb5ed3eb8c04cc42eed915f17bb3bbfb2b03cc39fff4e662d1ee9fe1ad7f0813e17f2889dbf061e00fe9467195203fc0f14fca87e20ddaf26d25bdf116f35708efbddfe49309ebaa86aa444a0389e1f6e284273345baae9df00c8c61642f26125f1411020cb1acc5596dcf904e4cc8daa840b1cb7e688305bc27cfe7ca7f2017c2898235a35df2f8b3ac4f15ef844685d05329ee5d93a7d36d83538e2ccfcb3a2571f5782ab793902c23a71be78bdf34086c212121ef7f972b1612786318e7680381a1112748777071642e14dd7d5f771c918d038644188eddee57268fc66d5488a609335e18afa0c6dd8a5324460dc653ca91d11d8d362a8966fb47c78242220cbf2f3c2e1650b08bf3320be142f1e5a1ab2623cd0f359aeaf6cfca0816af7ab4c6d289c09e7826eb5cebe85410c8e11dcee3efac74e177ccbffd3ff41dc89e74058767edd1e5c636c6717ea8aa123fef883d6a9241bb5b2a7c72bd80b5f61cfdd8ef82d45057211b4b27384f3435234698d9044a4a4466c7021b4b6130bc21c882385a208b73c528d9050606e5fb3ab14d9a1a9993f9f78c1d66ad65885ecdd171543a72b9ffb770dfee0e6efdd43abf6b65bcd41aedeec2d8855a6bad5d28d45a178db10bb53761f0158d71ec4000f48644183e7e86da6414b7d65a77c4b14359901f8ebd942e6ccbd3cd078589bab7656746ce8d499b359b3213739f0ddcff13619b36e47f94cd091bb41ce7f3e980ecf57a3da2b08d2dcd96b310e93a89a22f8b58d1154a4d24aa65ee6571c9c08c769e2975673845a9d3db51591490cbbb62754a67b2cca612e2d76e0661f273ef50bafee086b07547b1dc3b948c905e6262a3a324e7f3744f85c408ae645a97a46cea6a12b2702d3187d69e94de4ca2a6dd14e8884ab4450d0908b427ba3b8d6849091508ab9c1b22d2859268fd7e067470bc263011e94ed121215fe4070ea4ceff9387f4ed57bf7ba6cbc18b6162d025efe842b675766d75687463735c72567152704c6f25b4416eca3699ec9c0adbdcc8da16abbda6a991a579c1a0a23dcf9a5e24b918b22ecd68cae458c8e44a5ac41883637115330b5b0186b502faa5c2cb44571117cf964d352e84569c49561e96dcd51ad5c534cc4a4b4a55f544b5141d01f599c279be38e150a4e040a2e8a0b24f674e2c546ca590364535994c01ffffff9f9dc8e395e2ddeceeafbbbbbbbbbbf3b81cf337b57c1bdeceed5b67a5cf0b8ba7164667abac2e2585a122b4961f27558aa3fc585ac707c48394b6aebcbca617880b9d3028cb57540945fd9ceceb5e939669742a30d9111e2fde5dcad0d94a0915abac61c744220e8b134ccc05ed1835313f4ac019b1cab2682ab1adf29b1ee985a1179e174e51785752554ba5dbca591e4af4d5b06322d186f958443f4f4a5572293c5e2d2e37bc9ab515d8bae694efd5ff353d4017d2f2504a4ca0dc254560d9b0f2b04a2894ce9a4e5fefd5ff343d311746d80a5d2159cbe6ac36a04ceb96c669694411ca03ad09da48f67affbda9116406d2857aceade5dee70cbd6fcef2e7fb9c10704ef271ee70b4711e7c60debf38edfefe2e15b5a76ac1077e4a0674009e7eed06864418f6f7e934c84031d62ac2ec70f6e953c602439808f4481514f0edee3c050a1a61a289f7a9ce2f01986ab7a9d4acf5344406021a6d66c5c5cf593d2a529e61bda8801b63ca3aa3930ba1e3ed3f0dd1e85034c6b1ccd3f62024c744c231739a1620f5e37357c56993b3ca6e14e35f7eabb0e101c5a33e7af67c25e4e09b43bcdd7d15a3f1eed46c307b1c1f9d417fcafff9dd193460f8392f9d5a6b2b12c358e7e3bfd65a6bed5380d6354202a4b6bfd54a7083b622f969a8525e1820ce552cb000556a1bda33e8d77f7b150990a8a3bffd9d6a0b0c00fefda66a0b62381261f8b7dd9faa4c0380a78efca8b632867947851b47776fadb58e597aca2129b43a3d2860da210f0d877435020b8cfa4592dfe9322ec0c4176a55eb22d882bb9d2a260098aaff5dff4c6592a1e1dbdd4dadb5fe2e14b409834f1be3d801e80d8930fcd334475a2fc58e4418fe6dff6d88ad646186e046e0517968301224505e313140ac80e954a4a549b3dc9c8c9e346f67cb15d8a3119f3249ed4aece32cfb167d40943c71c2fcb9c92b9209b159b19393d72402812a86d6a4eb069b34f41d5466a070263542351eefa563c246a264b774ab90474b524a11c8d89c91c7ff907831d3ba182a230bda5042326dad05a4d963e7f12c2798457059715a4065724adaea920ec4d502137121cc9471576fcb9f29ed06114a54c684d04058ecb4a21848424f46318253ee87d8e73bfaaff7669562d1183be36648f0679c0b6e78a36d3dfc3766ca6e37f6cafe5f0e61794b3b4c7d872ffc324d6d1f10f866cd34f4bdd8dd9aa909e8af3b9043ec632221fc2fc63ecdb4fcb9c3ec713183998c8bed839741e656e3e78c5dcffc7b66fb6d1fe69ccf1405a0560323ab14f212f1f8b699aee41bc95d33d8f3f329622651ad95673484115fd3acad33eb93c7bfffcb7cf17030c821eb9d05fce376f7769df3bfff7ff6f729cc4418f6ecef52052f1ae3581f8dbefff7cd1e247cdfffffff3fa682cfb7bb2b88fbff77a648f0dffdffff8bc6380ef220ff75ac2c0cb772d9963aac8d8bc7c330b296ccb8a148e14061f72bfe77c4f13103d285f2982137d285ca3c0d506207a6826ffbd6397fee69d83191b843e2800eeed486dab49652b9a555738665a6ad89b5a3a28b456305e487e2b1e9dd6153d6088d65b5525defce084a25212717b427886b37ff12f92e41912f8d4d508529941594f35162cdbd2bd16a5dd4dca63ef6bb54857cdff77d33f2c3f0f5aa2971113cd45f95656d0e88050c5bf57865514d0b1565550a0dde0e8a5da8a46938690000485100f31a080400846118c8910c896b0014000a1bce405ca848202468cc2810864281402008088481a1301888410088a30008e2310ec3d05700e3c2fbc384eb23dec96af8ee7030338a208c0e4be48917a8c4dce852c94123dca17545182b03895ba11d4b883b7b04debc61f16900eff76538a4810bb9bf3d34cda23f8601c1c62535c3fd159b9f15e016e477b73fc513864e22bda630d6bdd6d9fefa81909d5e1e2a2885fb1d392fdaa9c722ab9512e874b9db2f7bd784568c1546d41648a57a41ee62c054423f4038c67e928357887d62d6716f7b0492ddeb1986f84dfbef99b21135e9660556694bd1228fdeaccf2d01fa2aaf02da15f4dfabb3fd2326e35315c0d40ebc41928e72c474639d35af944c966be1eed7a144c668e8b0fc0bdca24b735441eb10c47c3826cf33ee81101cca993d54c2b2d8ec1882eeb0800fd362efa8e803b562ec5a7a7598af51e0dba280a2739c00bf77d49151b0273bea9f4351b98e7efa18569576bbb8a221d11caa783286299e11e6d0f91f667d8e4fe43fdce82905f7571efc08145df7dd452e074643851f797ee9b78ac99ce7c551f4c9de2bfea0be79379848518f15d58c5a7631ef866ddacacb9b249003e5e6bb0a7e33db8d456c523e14a7b335e5c2d6f4736e8e487568391dd9b0ca36dbf26b1af7ab6b6b493c9bd34ff1afd69ffb2ee173655281d282cd6a66920de46e93df443ad885c716a34aa881a37cc4717180c681627b6f0347043401afc59456cac1fae5127b035cb0d76ee4a63a9ae9dd7122b7fd5e12baa56452593e27c05e24235482c002ce9d0902a26e3c3c1b335ef4e2c60ba7b9fc99bfd7902164f3cae890c40429034a921c0984dccc136521fa977ef4b40144b5ccf25e43a2664aa0e3c964336de0ebca9d6279f9f5b8be47b4131730ef17b61cb5b3c1bed3fe9b3eab2ed08b182a666088126008dd4a6ce70a314e9f431632874c43f4a57c51e00f333c6b3d694787c7b21066fc74f2d439ef71c0eec874c726262c5e5817da36fba8d9c557e2a1912855be1637519d42eb6d4531c0f4bbc6bed95cf891d72124e94852ecf55825e0076d49bc526305586da7fefa94c6fee8ac5d6ec0d456e686678a7a938e9f8c217b60c8c51c393221af128622abbd2cbd2f1b39a191f7d403bdab0a5c1e0b2418465a5694e5768247c817a6a103222f047a90752a35b843c86574df554a8f28168ea002bac2f454e566a25c46e405cd534181ae20c4158494a3ae6aef1644eb84ed4c5a39d9c3a54f99fe22ca89e02b37c77fd8a0603b9dac84a68ee4864aa7576f187c690d0837c83628afcd043edc820ec82180803f09ea68366ba3d00e3507467ae6026854a8b520d7349bfd700cd3b7c0775cbd448bebba70472f7cb94f6c8084a6cf66cb548545ee7d1b23304d442743ec442c4e2ef489fe01bf23880ef7f957176e42d6c777e33ce59e667701a76fe24575706c20b28d80f403efcc16b6712e86a3078dc17e025661685ae20d4e6228e765900345633d177a378ed53361bb91d5d31fca91fb6ad336e72a1b2b795f4a64f86c55c8a20dbc7a3617b523f7aaefb23bc8eb6f46304e9349056cd75f469876e8a280c4f560537bdf1a9d6eb9b08d0db183c73c3a52dd58437d1886357d306c3bf9b22b504ef3849a36cb16910e33676e8e35e96e0b4e59b7deabc3264c8ae8eb259299c2031f88778e695524540c7465bde172608c00d69883be2784f501e6888275b04744f039f5475571e40f5f2b44cef120fe7858e2b06757283d09dd423961864ed69906283c27b70cd44bcff95212e25f0fc06a6e30bf8501ca4f9ad9da346db1e7ac2ef3cd9987fdef247847785c6ebe8f69642ef4cd392a915884a652a6360cd0d9390ceca3c5ace5a6f477731abc78400596b0ef97450034bf40fa42649445fdd113678558cc1d5b2de71f4067891884542512a894b60750245bf2fc080ae5e7372eaf36e3a828e930f75dd16b27b8d6791bbd0c3f468b8409e3cee21717ec259f648099752aeb703bf82ef924109ad5cc84d5024718451e1b4c1bf2c4bf02b3b101cd79d6b73d900c09f9358ff21238413bf540b9e2e9bb38cf17cdf81515f1f05ae7a74911824a5c000a855161f89c7b565cb3b4c8c0e49568c3c2e486a8bd58820b2408df2dc0b034220111350bd092b17c858da92c7ea0b1b189bef8674f5ee550732fd1b34426643b41680ac833d17a11a25856cde96c4cc337c88485d8d620248684c7b9e687c3da0f5d75eb5a29c1f4ac2ff27761d67f9809b4997b162c30f2038c87ad791b71f7c0def8238cfc9c96f5f515a4e43d5bd8dc2cd952d693e93d1325a9e5973ec3047cd1edc19e806cdca6e03c8f7b246539b570e1d23b07bcdcafbf67199120886fd1a814ae6e844a083ecf3df7edce5cd37d1ab8668938be4a55dbea0c48e9151656b01c93e506a950a477461f3884b367a83cfb39b6894a7e2703348377c785881a2f6c80a9a44f127f0209b3bb9017f80602cc8ec5194c73ec47cf050436990864056f986cb4de28552bacacfa94a3a93b0273099a0d0a9924e0187e9ec53e2667e48e52c705095bd84f20452fb39b71be550000f5480b14e2686620d4d45c8d11a5a93a3ca741004bc9c4c40764dd6a4ef88f7a3cbe1173bd28348daf03754f9caa8887292ee8dba9a1acbd5d49ea5402a90f4c9de20b1178065e7172cdfba73bae298ddf908b7aaec0a2e79d17bbb1101d7afbdd1f9463f3591536c558dcbf4c873829bb93d4e30299b510c510aee98f2565e9282394868bb8be3d22e28fc7720629d3cf59f420fd799162ff4e7ac2e6a735d52f6313c44a66ba0c575e6e72e6d9a67d452d6ec798eae2a7a2260705c0fa72e495c1b5ea3279cec65000cc793013a198410d1e081ee23beb2d17aea0e095e145f66acbd3feaaf0def3e21826580e8580af47344b7e0dcb7c5d1748e988304b88d5966df694eb0b348b556adf19b5549d2411a3f4487e96f7be761dafabaa570f94fef7cfbc81ae8a43f717b0849cee422b28811552672354d4107a350de6ac1a22249073fd654aa8af301472321e57a86d47d3694f65312588daa3dbe61422120126cd9714820700b6944bf06c7de12d5f2caaf461887b54f9c807da68fe5fb479dc0161a4fcb2d4da7677a07359bea49aa613e4a31013f9eb704b598fa61da89fdea71e1d930d3eb232b2457bfa2e0489a17a768cc9182260102fc42cc4dfd393018174ef41315c1e9112296bf153e61b6be2e1c117e9fb0b1d14445b39a39595d10a2c72eb950cb1fe3dff17508dc2ffe637a24d3ef51897af1fd374647974ab85cf7953a0c97f89519cca620d24820c4c9acc4faa1f04ce4de0038af96ec96b9a7dc6a8d7f984c28b8c6c47b75202a6f5131655714fced6d72a38a1879058d2a1163174a223b3d848fe35be21d2ff4235972d1a45ae514f2df37e4f43271f29e2bd475beb3f5052b18bc18eb6c520708b8024c9238ba8282dcc94c913ae1923014ec29097a293830ba58508a2cf0e08670b849652dcd8351c3dc0a534c6de6277312640f9130be28a8c27cc32e224b4216fc6a898906ca4aacca2801349222af53cff1aa4f99aa079a119542a0f856b5921a565288180caa262f741427ee3e5fc08a20da1cc8196fc0d81474661ed1cac90404bf870d0c96375928e9ac8a9a728c451914d5b3165d343738c6158035bc86b5b575504c66bff286a42d8de41ea0c900061de57e0969b9c6549c4ef276a496ac865c95afd25473535be28a842adc559854ea519093c15141b942c92b77821f62748945f40e53a589d8c56a58c266f0b895014292bf710efef5b7f884b6ff9948a294803c7153921bf8bddff1409e13444f02a89aad5bb18d2faa99725253b44210eb867ba22d4ea7a7dbe89b06005c21be654c30e49f10bdb183be10be206692cf1c05f02c651ccb8bec6928ec706163f58081d3b7d34c1aa2148ea085ad52fbe9acd0dcb5632e7188640c3d39505da278334ca4157a4ba41da23376914e23cb45d55edcf2d969756206da1147d7d1bb46cbb019a1434f97124f0aff0be6c4fd299a0a346b0daceaad06092ce1f1301e9d225399610d1a223984a7992aff848b15972106c41d25e4b5a6bf8081ac37689aa3312554f90b0b3091f3105f5a78d0cff810faa3e871068035ace21aa244daabb237f8eadb05e2d65cd808f3c98838d34a4825e2b3254e163acc49af3932f50a1bb294242b7b1f2802e98d95deee123316574b4027ddc37b71e6d9227a0628bed46fe0702879d9281ac63cc5382ee67670f8cc880eda521ebc61ec69f2d72e04a2042c690a363de3b3d7ece7ec3a32d043a7b4a5277fb680dc4a321d077d5517c2532392c87f41428308a79448f3c18c01b342b3c38124e8da16f71ec258dd68b1f3b7f3051689c412ada4489192683d2f7282dbfa7396682c149b834ccfbe31c78d608306780dbf0e9e5eccb174e542cf4ac2cce39052107e8254532e93d0b62246cadcb4d70b5e9ab4623d68b16eb5b0b9c78b761c6e1138b76061d3147263b54696f610309bb5c1ee63ed127d8e0900f28b5c00181c52a78e8c6ad8dfd4c507dbdba97a1a3c4f5521042516faaf8422b2a4037ff3c8c006f6461f9221082e39b50dc14216a0c15f88bfbf7236c7fc9cf9d4fc2779b6f2e5f993f1adf22ff657e763e0ffe55febadb77ee7e34ff06fe275f763e0afedbfc79f94dfcc8f813f897f9b3f939b27fea7b737c45fca1f933f02ff999f349f06ff3efe12ff347e75be43bea7e6ed9f3f0efe2afe317e687e6dfc0ffcc9fc647c177c53f0fbf991f1b7fc4ec8fbfcf9d9fc27f9a7f8faf992f38ff06fe257e363e0bfe69febcfc4dee07fffe46be235f367e1efed9fc79fc42fcc8f91bf98ff8d3f939f9aebf37c7fb95f8d1f933f21ff989f169f84ff1cff137f305e35be4ffc4cfee3dcded8fe2cfe577e60fe79fc07fe4cbc647e1bf8bbf8fdf991f387f22ffd3f693619fc97f9a6f97af883f347f47fe253f333e17e0ff4cfe81fcc5fce2dadbbcbd67fe78fcae7c58fc75f985fcd0f827f88ff993f938f86ef3eff1bef3f6b1f327f01ff3b3f173f29ff2cfe12b460a84dc5dbefb90bfcb778c8f82ffaff7a97d3f937f881f9c5f851f947f2f7f115f38df06fe677eee7c4efe51fc71fc4edb8fdefd0dfe27be6c3e4efeb3f87bf995f9c1f82bf09ff893f3b34b6488d73423f125321fc3c04dc23224d3570b7ba8dfe64915bdbd6fb3e71dbf29bf54fc9efc38f3ede7fbc2af37bf4f3e90fcedf13ff1d5c6df821fb0f7d7fbde573e6dfc7df241e2ef9fff88af347e1b7e80f9d7f37fe1d39daf831f48fcfdb27f757ba5f15bf2c1ccbf9fff09bf6abe1e7c00f9f7c7ffc5d73bbf493ec0fcfdf1bf665f37efb7e003ccb77fe49f937c3efcf932e6e3e3b70f9f3a7e6912f28320f4c417c9c057a944985e1d156c848819e100875e4f70142b728ba9861b0d977aa9d2f9f14d4658d45ee91122d0c48fc1350245932d579b08e413b4cca38d57759e11c94c199ebfad3764c067dd8710460b6339550494829e304d2e1b3fa05ba613385703b919335013e6e2b4a8bd5da64e6b841836b9c4091d800328774ebe68d0bc33c8c0b280fca2b78aaee9a0e40874e9487ad6d508d69abeb3e08c76446834b4430ca92800d1ef1d3c513f0900bbaf90aa31048810d225af42dce310ce46ee2bf75ce305a96d53c803778c89a218ac182b59a3a10118bb1c51535873d87329beab93227a1bba8f371ee2c4f8fdc2cb779de5e0a90ea1220f08754d2303bc71344e583278a0fd43d45aa6e266533dce5ee8f54ef2f82ae06481f4b4d2daba2dfdf8f2713b4d5c13fcf6dc2d45bc41061d7a9e4f78ca14dd4d27e116619bb060052cf9ca9924a147067708b92573343b0bcccaa657ebe5245b3f5e49a2bca04704960101941cfbdb87ca0ba0a0717111b0e35ed273d198cbf3f402cf3dd555a2139734aceeecc5a03c092577f168aba759f21b9b64210a6b720434bd752fd8279b3688648af598c7f02bfdd682bad81721c0c11286a302c2eae3c47320e2f6528ac02f653794fd4c3b3c65eca6017faac9db7cb5be002709eb19d22708b74f7bc934ab2b6329170803741fddb98385605f2beae7b4243e7b4c868624d226b6eebb48ca7a1da33703918bc006c7d740819073df1bad85d04e2be150a9c0e4f4ca7074c050690ff3d02e9d39129726c55a669e5cc3b4de9a801fa7b494a51d353e44786a4e6ea18f0b6a000025974e78e0f3cc5bce0ba6e88c2d4e5775a81bbe1d473531471ad25e4344110876274e2b1b5d77f7f8ea36371bbc08043cf7013a8319505a30cc69940e7d6ce6788ed4b56e964b4629558eb453a9a8dbda1865492a851ced78cf7733ccf101601a901e2873076038f84b15301c628bd87a475da1d8aff05a4d3a32b874a17772711095b4a6412209d95bee2df7965226290384058c04dd04321dd3a36c733a48775a4aee025c03d2e315a15f7acc2e5d5b7a393bf002fc0ede9774cefbb542b9c7b9029e4c3fe591d39916c8ed57bbaac9dbffbd6396d1ee7de6759935265945b09aaea68eaaaa2e5763b0b2a8666f2476998d46bbd65a69e84919743b920b810e50da550b6dbfd1d9def2cf39ce567c8c19c663ddbe22ac97060d271dac78224d83e857681a2b3c3d8e80a554e4ea910324ba2294340fe5260a258d0b65d0d8dbd770832a83eeedf1fc56849246b4695be7d7b0022e4adf324c9212ec99eb5724176300822505cc1421962420081b3576284162839ab9a712173c2c4049e9518369867c831b383c926cd933029c248fb3b6001e3e7692919a0fc99d6484c945b97f73e08914ab294462b871026d3a40063e66b27cec61c2429b3f6bf327d3cc743e95e5302348c3840d097a9c1086b68424cb3dce9a5ba5994f244db1584c85d79661a0ac448873f620e7dff9323bb939678d0eb556184ce66d0966cd0d7b84d5273d6377ed666beed8f869726de9c58f0961f8586bb1ed6aa87c6a8de13c65070e1729aa49c5b8a88342106ac200e9c14307505c083247881da9a70d8ee0f0410f131cd2e8a152434d7ce09869c25333a18a52bbf9e1a8b59bcddcdc41d7f801b7fcb206ab0f8909fcb1409e1c646c0801e5e948980a8abcb1c187d3096a74e066be51f7d60ae328a5144ad48f1b56532be0c832d2630d0b2510f14324072a2c0a4685104d920461925aa2e695660f5698217c4829f9d2826afad307851a7ce8f801032b2d576e040125080ddbb66ddbe6e52ab12ac306480f22341ba50472d6bcc0038f198c60a1f4c7091c7d99ac5eceba3cdfdb65530ad3226487198424f981c8d291940f31b8f9b1431b1c3728a5f486e1c28295235f6e706a53270a923244f2588571b2a185309edd4e1419cda266389330ba186f77c37826cbb9bbd51524fedb327e32c6740b2e62428f113c590364882e593edc108287162a585430d6e2691141096ca093c78e121c76a0dc11c2c20e2364c0d84a26cb27a256208a31879fdb6c055df5ca9b83e3385b815ab5e236b5eaf081356cc1168ee3389c25cb20a5c610247aac9094a17141ecec90a6899a1ed66c1bfe803006f1092c411c20616a690a1839556e0a3065821127364f589e30c618638c313603450263218ccda0e9c3595b6db56d2a58ed7018dfa8966619c7711cde81e33016ff2bc751a0d60571b29c3b8ee3388e33f226072960ece4189205c395297ed2a8b9ba3183911e509bdd362b2f1a94a8a0856987274be4d0361e4c48bef4c014058616864c81a18af46108384d54682861eaca9ea5252f80618a7286863c94362569332fa530ec6d6ba2dadbb669a7158a58d991e3a90e6d93e2c3150f41dc191265dbb66ddb644d3c60a9a011385bbdd63aa5979be28c66503334db194bbf4cadb322118a5aebd46836c327446aa9add65ab7d6d24a2bd5f2376bdd5a6b6de5386badb5d6729bb5d65afbb359d19037444daefcb83f9874f4ca0e4a29154384c906b714d39483cd1dbfd326df4b5fc043002432664848210a0f2938342ac7c891343de21cb9b14d13f765324a29a5d4e5864ad315d7d29415973929e06082109a6dc265cd9c2b35b8e9138766042566c80f4d509c7077ae050900050acd162b3e2c39bda1a2a44eb15a7283132a1f57a6dc2d7290f49133c3d40b475a98c1e3040818e02cc1e1e9c707801359fc7f92029e00f6b29c3b139e6cccb649a949bc157e44e0434e1c1de40039d3662a8810c03903850f280296654ff955afbc3596b9ad56a0d607d6308db235f19075449581e2c4e986a91672a010678b0d1b75b610797a01f1d011070d941786a043e3a230819ae302103f8078dba0946913c46708255957b49cc142e3d6c41963064d0d2a2a30eb876b53a1eee4e0b8cd5a12e8130e4d91a08d42095171daa871a10645a70855d60f1b4778b81cc7711c57afdd5804b299d8dd7b676d8c21abf6ebad962ed6a7143f6d03d5c66996a597366cb6f4d226aac5965ed864d9b3e6b171628364c408af081761bc62768b7011fe5b7f6e20f6f7a607f7bea7ab8bdcfb0cbb47d4115e7316237ebe7416976a10ea9e61f70825176af8b8820262670b0d37d3f9554d1e21e76caa29a8016c8fe8baeb973e89773dd2e7de1bf10e26719d37e2bb113f4117235e88da31358a6e770550aa8becebbbf7d1beebb1e8721ff3dedfe5aef78f85af2a99d3e3eb3d3dd6ddc1f0be6e4ec378690e48c77eb47a866ffb313d56bb290cfbdc771a86f776737ac554d1b9f40cdfa3d2ee3c4e8f45dbf17b585b5f2434b1dea0761df6af86412fc1d9b4e07ea52ba605f7e5aca80393aac57a9c41415f6b5050fda0b76052505058e97761b762aa78ee3db49edcee3d12f53c1cebb6d645eced8f95032f88bd7d7cbf097acf859e7b38b70d5bbe22568495e4b961df7a19f0c860551fc4dbf69d88a7428f3f07fdf72fa07ff9d3efbe861706d4a4e679df85238dddddaf19286d77ea01a5cde90f0460bbee46ee4bd4f0c6beea5146dd214cb0edd3775d82d319286d7bdf0b438ae41e761f0b47ee5b6f410704bdf7167c3177d0d370f4ef34e8a1d73dbef43dfdaa92a9ae1b047da76104bda76100fd0cdf545fa06f3d0535003100d2d5c5acffd3b53e4048e8f1e3c718dff7faf75d08bf0b3fad7f5d3f836e3c562a026b11ef12f13414e17ac4aeb0feccaeafaeaf3f419d002b710da3fbadfb4d97c000f6feb0773d7abb83d1fdb67d37769eff75ef71ddb6efe84b847d6396f33ee8855edee742ea21be25fe1514f45e28b750900e61ba85fdb6e9b19b60064a3ed67d5f5a4c481731ee1eb6d109c2bc4d84255a2f024c728118c0b64c16be7b38de87c1ba9785e3ddb2afe1e8b55d6bfdd1847f8bc4bb0c4aff6f2883dbae658049ad19be473c8950ba5847103d8c14d004f1014e1f375c2a90e70e933a499ce4c859118a74483ed18c4e6903962c1466504149d39392912f7aa8a07658d2430d1a893b7976d8b121840997bc54a50c1553143637d066a548eef7aafeb7700de9fd5cff866e3555d4af554e67aaa8217d6fc95dc3965fb9586570346cddb7e138db7656b9c8d59c69a9fbca25c77ef4daac425b480a7eba2cd31ff3a673d63c3dafacba39975c1c676db3e180287b44e1ecc2e975f5855ca9e42bd9be5d216e3bc73bce3131662eb607dff6a959548f63cd7d1dcfa6f76abf7291c6eefd31a66750af465fef7a0acb422fe57d0ac2d81051188b3df7b30613a133901eb390d0df7fcd9add34e8b9f70e4690a6e30cbbf1dbed310d033f7dee278801d630bab79b72425ce79a5eedfb4e67aab8dffa1bd63c6b2e562f536ca497a9337bfe9c38c0121c107d04248d484b3ae44423079b3e51f87408cb1ac6dec3fb7aaeb6fdc0a943a6988fc389c3c52517897e050514842b424987849e2894742848e2edf39cceacefc9a0db03933cda7d7de92c64965ea569e7ee56571b98543dac48eeb1f7c2961ee76ec562b18fbd8349b1a658589d8ee779a174ed74aabe9f2f2da483349e4bf8bff1725ccc037a3afebeb1f7b89f57d2bcef8b7839caf6fdfc48f4beec5d2fe9eb257da077f0a525ffe63e34b4b1e4f57a89a063a4e6ecd9301c06e3995b589e983c84cca61c79bc607650913a13678619a068ee030d0e2be449210f902c9aff07fe22f81bd116f366236924e54b4d43be2ed141a531bfea51c455721157e3c1e6eca43f5cb2fa3e1d7c31b502f0e77ddfa739ae82c9374b243a8b6a090ceceb10ecdef4cc45df7214d91645d5267e06be83da25953a37afbf616ba4c713a24c567fab7ff5bb38e9f628f67c147f62be38e51720aeb8a1e3034c90aaee480955ce0421e5861eb4f9f2de597f60f0b55a2d4d14d3f6a8da76fc24d7a760ce6ce9054c96ed9f802dbd80b1f1bfdd453b37d87218a1a0e8e205ef6293ec9ffd65fed2ffceea13ec24d320528146630458139a13fa2300d140733d0dc9a7461d2c1a8d112c88414a179aeb47d8a8a8c2d05cda49090d28f71f07db7377efb7f79fd43f0e36f85e1863d60a4fa4c9de7f499e7ff7ae5fb8f851e0d7faf9a2f43c7ff0bd56f87dfa858bf31b5065c878665bc564e95f1593679707d416d3065f2a4b103f92237defe117be178e1e60edb5740366d8fd624c1e6597121a6cfedbcb9d01ecb3e6e2df4f2ff9e08318354c1e917ebca3ecefc55acbcd4d1c729b88ca236cfe3b8b13cea284ddae3fcb8104905eb4dc3323ebd55f69f83ce32fedeff9ef305c8b40b2fcb1688b2866fe48ee943ed5dfdc99b6508a12439a3438c42cf901044d84167ecc01a242132b4574901406d331a54e1aaa34535eb2b8d927b6f492a5ca6e514d75587060593a2171cc9a1134524e099946ba309145a0e6d3960af472bd944704382ac0073d36c0c75ecaa303a078eea53c4af1de4b79848023157fdf672a109002c5010c3024168000033821001d6444fcfc19027904ab316dc8a3963c026fc82312e491ac047934421e99208fe4cf9f27c8a3097efe44411e91f8f913873c32f1f3670e7904809f3f53904701f8f9530579f4e1cf15f203e971ee0062614f9716d2e3ecb167901ea78fa0c77a9c2decf952001fd3e3fc117babc7096406e18478df69a9c739e4beeb7112d9f3761e676302c04142ae1750d6e19432c5bb36fd00b4367d0098203181a45f22bc3205fd113230acf43f5838a54c81a5a64b0d5262bd71d76135f7482f72e4cfacad30f543ea50e2151411fdcc94d289e57bee155f54c1575d4111915ef22f4056a020bc72a8d62a2ce5cb45d5d5fc8a9d2a7db0c9b746c9141e234b38e04c40420e3588c0ca4e9f1af331934cc526674c823ccedaf7b0ff1e08c9eb77d583591de8e70cbb5fafe75eaf0278f9e1029921425861a2505957f2d4b9c2c2036d92d1a18e53ed8a080a68948a50290194216eecb1fa439b424cb2bfd0d770568d48fbce6a25f5fc83684c8fdd0b3d160aa7950bf947a51984e76f0f7dc3fe86a3526db77ed6280c067b0e56c34cf27431f7f7f3aaee69e52c84be0a5121174a127a39047bd83bec69ade1edecf8a2389bed6a341a6979b1d7b2101217e707e95186f578bdf6ddbbbe9b582f21f22517bd46e76be9434f2ece77d2cec4c559bfe4f9b38aea2a0ff634ccefe18863cfa7ad19dbfe7e033570fdf9f3e77b9cbff0f5ee404ff5583f3fa6df17be44847323d11bbe9e767fefeb456461b2a5972c4df4556ce905cbd55ca23f3ad3a6f315ba5359b5efd79be4dfd19a53f9948ff12e73ce597b267106d4bd3c9441f7ebe58b8249aff7f212e8250b9e2d41177849d5b85ec9b33695a9d3af6cf228ab3ca03259ceb2ea678d4a56af510cf0e274678f122b3fb49170c49cf9c1cf9f1e5561640bfc336b2b4cfd903a9484253ac00f3ea869808f5f89f685253af8be57a2c91eeef8b0d227480b2560b5c016e860cb95b3ab05e249f11e2cef0b2d88414a97d1a966d302492b9804de9f99fb1b723f3a96d1accfe8588eb57d5f286998e3420fcc9df4d2925dfbbe0c4384cd96600426968d0f909c5b000f1fdce87db6f7d8af5c9c94521998f427fc4abb1ec733dfb1680ba7c29a2ae95f6499111d9aa28b5369cf9f6f445b7c2dc0d66c0d3d91f9617f98ca29214db2f8d888f20406a41733a32d4411c6d85b16c7e9b166b91e34d91fcbc51943a6b26e8f01db6eb7637996d39ae77b089b3353fb664f84ee310593a4ac52a6806dfa9e376b345c9ca3cc22411309743c7d9c976acdcb4bb3aacca67a9ccdf67879ec913a35c141ab3419b39b749167f248a3f67c1a65268330236f0ac5895431bf0c133c1b8a56f11692361f4b6cbfdd3ddeb68de3b87b2f7d17f4a54ce1ef3d4dbfd77b1ae5e29213aae91817e919af60feed93471a259d3e390b286741b78f42f9cc5f27ab5fdba28fe45ea621ddf6befde8a6be6bad2e7cbfb464fa1fd5528ae3143fd005e8537ab545df85a7c7eeb7773d768fff0b97bc8b5416d5ee3e1a60128d7216ae678d32711655674c268fc9de633119cf5c99b27431a903feb4618060cab5a993541e65d67c9fb76a8210a0b44f4f244aedd57e2f2cfac045f9c589077572c2b127757216f49364cc7dfd2904c10373771a04ef2f78ec851968fcad8dbb50ee0b01a7493a34ceaadde9711b371dc39faac661f228b394360a17655496740a36044b9aa43dcd6006ff9bd2244d7e9673cf2aab0661eeefe343b594834dcc995d9454c2f67c226bad638c6fd79f60d2e4acb68ddbac72a89a39cc705a2971b3fa4065ce399fe69c93560d64aa66cb283ef5a37352f953861ac8344d141f1d750796ee4f140a8f20fef4266b4ba93757d8651fab28da5e64fbf6a9edd7f7f861071da0c4655f8982be140e55f6dd72dbbfdbcadfbe257d6fe517ca2d938243b5a5149c31dbff005b8ae9a267576fb1e4c45fff2d793dc621f7dcbf4227f40299762f73d13faa8453c8f06d1fb71e8f1e1801fc5ecaa3fcae50ca14598f987238042f981486a74398dbbe6fdbc4641f6d7c01d5a35415d5c6fffd043548c0fe90689c315576f7785a6a2928650afa18947a3306cb0db2947a43b5bbcf8fbfa7181c6dfa16eb0e1cd90b5e60bf2bd1bdfd991febd1bf1f710e47fbdcdbc7388411e36853dd81a34d9f6a295370cf3d475f821a746fbfb642301cbdaf4ff1f778a48f9f32d5a3d2f78523d62f474f8730b76cfb5928864eff6a91339adb6a05f87199ec5eab671a858b3e370a3ab7cb3f288cb1e4f9f2a332d42b4f8cf3a966d98bb445f7f2e9d0cdb21fc52de56c7271fe4402262d917428bb98c5a7436f4487e6ecc5922e12a0432ece7132997216544f21d3c9594c26a6aa0fe68b30df030d3c6d4ad4e910b8566cc32b53f87b4062035902b1c76ffb0cbbe9879143d4e012f2bfcded12524b314c2e6c298689ca26ca32864d9f0952349bdbb24b1f231b0399c6b0c7bb65921a84485dc31cc8f2a7d36a99ea30b96152c344a6d65abd33e0966298b278dfb66d1b35e36d29c6869d7b6f193136e6501b6dc08fda28d37a590141d095c94c09612e0690eea8e4ad55743d0b99828830004011006315000020140a874402816030c95220d1fd14000a76ae3858522411c762811808210c05011003410c83300c82300c01064167112d0e0012f407ebfb9150aca236b1651dba42e5717ee56ac68ba54740f938c0947a85d8f8c5601232219fa86cf5ca8f26373c22e4281d8375fe6d4b30d08dfbeb0ec9368bb07b342983bc6624618a7e0ff37a74be2b25d399bd3fe5b10c472baa5a252aa29b6a840a2267530d1130e00e6870327ef086a67d67299180d7b26b9daa11e7b44119a23e984e69f26576909581b03de225a22c515dd1f0ccfbef6d3a06894dbd5df869d007a5f90b24f587b7e14a617f5289352d040aaada3ee00eb32ba491aeae6180c2eca410cbb145d02b3f54023048bd40286fc90d50713645f09842792029a2ccfdf39dc0f828243a75d565c4b7099dee30de7b9ba0c830299ac501b52c5ce91050fedc87b24759544d58f37ced11d61b3778357c33379102491cd5d9866c13334d72e4a3d19b6261ad34a654345804da26548bbad31705fa29ace902750dd28722993369bce40ba841d05dda08d403da463f6589c2325f021259390467d529637c0c476ec0ca3e988c3f32a5dd05e45cb60f4d9c7eb140d1c150670ef5b7de89bc7303fbc819156c43699a89924545733d906e14a30b05b4ee2981877b9f46349753239acf2ff3b389a751d03ed8fa358fb8d005f8adb16cb869d03f36f921f1f7893bac0175a3f0683ee63bb305b1b490cd2aa44c8d5fef71331e5445fdf4a2ce1cd4aa171a3bfb6b73ebb8db990d15969c7dab01d0cab8ba3998140d56575e842f1dccfbdd1b90982f0278335e8a3d87b5221b62fee253f29f84a193be475577eb844c1cb865ae95304189c9c43cff0fcfa6c240a659084d135f3332b85efce9433c0385d66ade8fd452ddef711623654d8591a00176afbdff452374c201501abb5d0e97ec32fc356b621adc47205e1b806f62d064b9a249c03e6cbbc98cf931c047e78c08c81bedf6d0c6e02e037ac9686449318932771560adbd5d978b9c0e97047974134b1b315f012837448249443a4ab224de9ca7f62be600fb18c552c9f2be4ac12704cb87580c915f124ae8244b9f7fd1fe3d4ce75a7a7e0091bf12b8f6f57bcf1835bfe5466fe6515a51117f89c4a0ffbef4065d4472134c141af679981a86b4a0046e7e304e284181eba6f725a10c0f63f6d9092d1c7c7f02c85116f45ed5f74d10f2423bf973c9351830e3322f5b340c2936ff692e48bc2e91051a360499f7ba6005c7fa32d20452c2759136ce8ed4b0a8fd13ff6214c37bfe5fe9cb18ac78b46d3c3d308bb1d5f09110197f88dc06d7e93b7e3c5e8b0d8cfa9bace780c5d0a216c08220fa232d8d3b775d199baa35c5f066c53ba3a9a8b4897b5c3e310ad56f168379c715e8c5127993496a622aa04a2bb7274ce41e501038878d0889151fd7d30029f215c9426fbc5172a8cef4c6865fc9583d50dd1ec2bccd733b6a2c59627ab95f9bc6f45ad6cfa3e5d5af22480819ff4a7b2057d1bda01b50f8ef7c6b898b6f3c97690686fc03b0df2667ca980503ea6c039e6d9277a80f25d764a6700a6ab71c5536b7366364f720b3be450c8a112965e46d2d93043d98c3f1234a374c0430612cfb34fad8d8e0628597f3ee09217bf3856f03b1980951f87ad174356808438542f8b200cc728769bc904449329ad3bd3b284538e5b4cb41255aa44c4d1096af2e5842c975d61b9b751126be72b96ed7136024c92849266b98d4e4aa9743f740dba3e8420b6fc20891d8e9fb31157ea09ab8f907c15f0cfe9aba5bdd09fb300cc074c511c9812630ea8b2685534dde5169926b3456e71d41217c68697ab76dff2672247977b6233c3f0a4f334729671ff9915f78913230d8d191e5b4ca56f9135fab8ab869f15723e202d0008102656118fe5847c4fbcd00582186d8f4c2ca51080bc7bc8f857314b22a33abbd919498316e3b0341bd2fb7a2cd6cd743b0334e6c7755e85a666dd812ebee0d63bc1fa3a0f68cb54a632e99bc44370445490e992ea7b32982da31473092838a709b8d717ba7f5b871d98a57b0879021ec111c93cdae3fb25dc3184060141759871215207ffa3877eb42a11b9cdace8e0d255a445a0f8d3cf72671005b9b87d3f89cf9ff6184cc7c7a6e17d35bbe8b723eed6cbe98b8a8210c4747c998465d66e2844020347f8878ea16422cda9a095f006018eb81542afa643f765e2aaf6f68419f9f375d20f8c2747888f37c6c6b19d0cc35c09aace1174124aa5f0e3daf964c4a79ab6cf466347325740ba96d42c039c6683335ef07a100587997a48457a8718c4640a2f72fd30372618a99ba00f95e066fb11e2ed2b9f333dc8ba2e9bee6698f58bc2368fe05ed1ef9ada973f379149171c521dcd5c9f9508c2c65e8ee26dff4589c7994681143179a2546e1aa976fa06f28297d1173485d7cf6efe944bf342999388303b7f7e23629c4c48c0a50157650fc09a8528ab5b3c5701c959d3a0dafcca4886bc6db507a9545077fccae9b0e06a584f3c8d4db50bdb96787c127dd60b656713d91a455e7a658317318e28721848f82784536f154dec2207350a83a5405eb1314a72c545fe31f7c0367d3af713ed6f7a75a2bb9dd268ab170d828aad89c257a94247b5b303650c685ab2e29ccc4db6eee992980d8924c4506019f9ba20eef3316ec10b0a9687175d3067e80e9314c9e69088589973fbf055e5696dc36aabeb8ab1d60f1ad47fcc880152a62a4626529ba95d9a9e482b2e4f98428eaa89310061139b60045ebaff8defb713f5f5831c0b4e4b79d05848cf6226be6d50883e3c730a722e4be108cbf1c3bcb11bd25c7710849151473adf70f332f89a711c1cbd4a093421335d5dc42b0c7681417dc16aaca7aab695f2c429c35982316d534f2eae3b0c1e843a85893bcb1983b9a46644bf8a893b36f54f04492c287712672ace6518833cacf7a12ac3698e979a12544c1dcf4ddb1d567e4501d708999cac508c76ae2f15d84229d84f5c83a2c36b81710e75358469ea9411584ab0c2e3a22201497ee9468885ad9af9683405d67f6c05e74aa1af9eb34bdf54f2fbc4f25fb32936d55ca876f71ce1f75bc13e34820408c579cd81cf9be05c18478f13c8859c0276775c3215c3f711d1491b62f1aadcce1c1137b360752838c56ef6afc93e21d43ce638081438481c7f0cea47763ff24eb298d2e0065f51694e2104e16ad5ebc52f5e575b154b156c8cb007a4a0d5a9b4ff1d2956c42181749e769b52c60ba44109b7e1cc1aa1bdec144726118ecac7d59163ddb28086421e92f0c827f38a184423d1992bf5d301e5957cb43e990cd52c120ca0b32cd5a45f0c01b02e8305138f76fedcbb423ce02c58542c9374fcbd20402c190951bea029834b610ee62806ef9f966a42faed223783980819eee6fdf75e05a644bc2c05105c54024254f955917d3d0e88a2e002a9f1007832061135ac2e49f706249d8f291349932482697060f68a4111c9020a4b104d786fe41360895e5cbb785185b1045df3288e978745304c70b1945347b9c1312355705caa8c3d21ae4abc835fb4e10491bc030898190176b99dc8502ea4e1561557955a8d412ee9f6c585791c88e17308f2909165b6e0c811fa3c5acbd2b10b76c9262822bf4d82c38aa0369a15d5ebd60c0923f39d254206a1bdaef9100336a3aa233156a646de6a0b974b6c5be85dc990bc4c1e02e9976e628a53fa885ec32b3e7f249d99e166274aa54a192ec13afb9d016be3cf1228433041e83bf94010c2a0c6448bcb18d3a07f58d50e94ddcae637543d4ab1510561e285269c839cda08e460a7465a58faa0eecdef10ebac9c89d11d14bb53e04afab3f4f88ea6a1e717f9948c1e64a4abb07135ab02f60c306108e3f0f1a7631a8878158ecdb9d85347ab0c011364123122828a4c5e4fb18f4755ad623c0c171c4e28edf6a4970def5ef92769511e5ff799dbca62e8726f1e1a28a9bff87af5d13bd3e06cc648630205ec0c5b26721d5e9390805afb44913101a6b10d0b4e89185ea34ae850c6ac7def0ef1a01af0cf7828e67bab2a084f07faf66e106beee1842fcce51a75f1356dd5667b940fed0085e516769a06b4a2748ab0106daa02b50f51845489470f2f0494c67ae75543272daaec319ba63ea7c9452aca70d870a7400210aa03e906309d62890289088658e57c349532adf60c09ba47f0b41a8ce7d97db54cc192a94d8bfda304900193c52cef0bdb834260ecde7c7e01b7512e05221173ec5fc835b00140e73086742b96bc58ec9e8c338fcd62ef868fa06234d902fcba42fed5dae6ab3082a5ea28f1b869d5e2a5298593022ed5f9bdea0fc0928d022961edb7a6aa3380e8e7904e10a8df98b59a517f9f87e5617e5d6a839fa713003723cf05b84a3cd7be86501be1b1862862f7775423f9648e957d944d6025718f08ca9b92a414b52c4208eec0bf1abe2d138157382a892815056de12a01808e857e99402f84f8c59384071b87a6ebb5a24f42379a9e17ced17ab16ea84fae3255f174d0258bb9506f28a889ee6a1966ecd92eff5bca0fba27545f9d9ba7b0982f96301dfe983d924a6c5c8b631956c42e5cea611873659636d5a0bcfdb2821380c56b7d51f42211c0984b7a5b7523f648827a615cc405180b3effbb2cd96d25120ec915666f315b11861b934eb5fc7376d181a880f0beec0f15b5d9886169a4ffb8b04a8db1666b068c85864cfdd8c2412b4d4f3ca0fcb76a926372492fdd058e92e9aa98a4bc5e82a57b4f423d6292b73ed23666ec138c56f8a0578079c3b37716ad1d2cb7906200debb293f9f569ef4300fdcc5f879becc4121e22d10a3a75f1d4d03c22d182026e14844696d9abe06d021a7258a4d80a4f33b34a37b4889de78b3c043c4789c178760b9bac2f8eaa512bf2c352198abcf352ba06350156781089cb9c1b4e8e70420d1775883ba4891c509e9f50e79c8bbdc30aeb785327aec390880b60a9e7ae5db5b86dc8c245c542d22a87f0aabf109d2478f727909fc704b4242614bcbd03d006da790190fcc47f0016b1e3fd19da88eaf55da127a88725d8bf86210a5fc47fa2b71d6c4e97026bf0d0390008063a41e29fd59b346a0190b4c6d1a9bc3a378290cb073155390110dbc73a21afd9cfa87c43ceaf759abc2c7014c331b1222b61261f7a813bf2a3699dd19bd580f98d1f610a9fd2454235dfe8b128d02e8f1d3f74202d5e408f6787250f74c0d22a9024f3fda9eb4e3f0db32c1334d6d1f7e9400065af58cf66a80974206fec5baf484f04c8818718ff4182c810df0236fca43883c3ca1e734e16e2dd05cd5b5f3502d83ec801e8f4087f90386e088299d6074694a12bf664e8ade42a34b3a5b48df3e176ca1112237d31a63c0a34b4501d0bd68d243b1f81f823278d464b10ee955e9709e0c1462d510f37c5f6b3c1a4f8c207d0012a619a2ab2aafd046410a7061a61e6633b505b97c90d36cbc46145499c2b9cab037587dbef0a88058664972b511559a88bfd48ed9b817135a339bafca06ded010a0724a522350ab5ffda7252f4861b352bf0e384b5465c804e0a45d98148b85739606b3cdc437113b41a07fd35bd2d543e50edc0d58874bbb83a82769777399e40ae0a7a79695a2389232493f2b6053edaaf68b4b83e7a7934e28ec7dab7522b11d701091559b8a24d37f8ff0100cb8d69143d0b1bdd34fde2deaddc21300e7ea4c0bbefc6859bd4290f2a1daec8a85c14d6dfe7586c6e3415e2a9636501294617edd45b4648dd1e90a4931186c365047f196786f7544317d8656fc11f6f38362d4878eae7e5d2b27b7e24c75fa28953ca8588700840e8aebe5932e6c468328c184dd4a0cde228e85d3fe9e39b92d3e4456a9653f03a4bb3b86b04c4a49ca51f92b394611d6a6e96868ffa5c25cb09015a591b9d707b622a94c868780c3dd5f9668dfabaa1687d35da4ae91517ebb7b1b97f087a35bff5d6bc035baa36e7ca46d205e02e0661f2d6a3a184175373e6526da059b7cb9179826ae09b5b5927dea2edbb1a45a2466992e08a981425a1e6ec63409af6b8b89797371bc467e2e8f3c5631a77dce864926963b33b08cb6e14cd560e6f8295fd3b537b7ce5ca0cb59bbab1dbea4603ed56c35f7a40b964c960fdb68bc69bcbd6ce930845d3f96e05a6f4a800e6bca122c768e6ab6b66224dc6c6243c04f3c3810c6161d3f77b002c51062a78e5b28243ccb5bfdd084cfc20c64f25660c2726e8da76ab812207f8034501c2fca7788a88833eadcb265740af662698adb91bbc6f128cf50092cd931d0bb0c73503c1532ddc2c31b62f16b5f3070ca723e551dcd3c2625d6bc06cfd3733eaa2f202e375001ce0d4bcdb01f42b4138a85e922ab0dbed863facc7130d0d6cc3a302aee6a8aef921d4e7bd3be7bfba7252c14492e27536eb77a2104884df105d465273b1bb8c12519fa0eb7ffcb5a5e65c6104e3ecd6367eafd066b3e6ec01e82cdb0f59864c8b46cd6aec6a78300b8cf20e0da2f17f6bcf07db8e483b6e74f7a87eec92a7b9d70149872412e8a6e614a4ee0f0ee06b03a24d81797ab420c55f2ef9203ba38cef08d6c5d55cbfb444ec0bc105781914de5f2ed030de2d1b4848198c66ed575b0482999423ebfc2f504c8b9249a0e9fd24a5a18a0ad33b95c01a805245894ca6a06510b0130fd764d569140dccd969ef753abc6b086b0d60f773f3fdf961c930ea5c836ce78062f0da778d01a62068d847ec589da616381ee22a50013834d01287e6ec1cb5fa4074901f6b7dfaa45b1cf212efcb9a731eca190f907bd0581066c348836b2cf75d84309b3344e5120b070bcc5d9101dfadf53d7e12853887c3d1d0411109d3f64520492b2822800a49530a7b0ebd3feb835e3cad41ad730cff89f79fc9fae55843fb75522ceb8f9a9e8716fcbf4ce6c05afc31e5ac96de5e61f77ce7bd4d3951958c211bf8d2a6941d4e0ea8a1561189547b48e903f1f582e23d1b313a956d8d7b2cb68af04ca48799181f3940c42adfb71a8629c82603ac86dcdbcbd56a1879f22b797b005079c61edd769d9c7aa74c52608147106b64b7b317424787baeea9e385a653e525d1de76f6c152cc7f60182a34c312b7ba469257a5940fa5efb2534d4e479952dbc916a5d63d2971580f76a72745834877419f22598ca764e74bb0780583cff5538888215d9ff692ea3608feaebca50bd9b393476b63ba64ae002dfe0577c365a047ea8a490917030389d4b0048f4e00c361ec8dcf4b193178d3cc570a67af8a0b762bead68b36b261a7a1433c7fd2a55d54d11884f973042b2a053c1a58c6f5d7ab6417609bfbe782c8893e4a1f085839b8052d27c53c0840ca573017ee941886ad282fb8bbd86b0b9f2b1c258d80bb8bc2f6210f8e2d937cb2999d5e47d67357a6d424d7675dbb1736a85ca2b802f8b5d96f377c6163e1090ab1ad15deb4a9d98b268dbf47e43359ad0ac6226029b35fa8da268d5316774671349e952478180eae04b0ae02667307b0a4ebeb8305cd06900b8be47958ea47f72ce4790f23bb9b283f2d0cecdecccadbc236e3964ca7f5b418a46a85a0625742dacfc30bb310925e07479d7d4a6ac7939238ac580364c465a66d43629a005a641c5b569198296e4b41037ba374d47b2a12a15720e5f33dde66498673e1706740933c75fdbf1b0da3530ff65203c2db78202641406d821d912aeb8a617060aad5287cd4e5ec9f575c815c3fcec66e135ef5aa376486626785356faa0b87f94f78618f232b80f96a5179705686e808f6fb36c19d16c0bc8894d5c9e98a02e033f9c086177953ddc32267d79eed527ac8ae5876aa6873107040a7b37203fe6320a885282785df1ba592cd8232553d34521904a7a1967d427f595d5fe0f064c9c59739d65840041a47ca872494cdb0fa9190fde9359fa0845a26e84be4f01152ebf67154125163042e98c5f369031d07ef0174ef4546082000f1f2b2d476ac4c3c0de0df71bcd4284a6240a9d474456107c38c1895c09ed9828b3dff2ffb464d2d9d1ba46f54a6dfe89ecdc39b7637024791c3591a7d7671f796cfd1d4f4d927fa2c032a8932e567d36074c81620cd87f09e7395f90c4af615c36688327f45bf8383768b79df8d4bf0be72a131d0adea02e6d11d9134dc061d48fd81980a8e781214cf8783e61204aac60b2585a582290fbb6c60f1a9a6fc856ce215968ab307ac0076dc94139b70ce4400c741075cb4c407b8992fc23f9ca3fe3a9d8ee5e4dbca48ec2d0d43362e35f277869cae67b4481de6ce41fb530d7a0525b16bf3220125d440245fb3ce05cdee8e1db006563abdd498f2be4b0bc26ff3e59018f8554a566f48e845fd85fb77c20d2cb94cd991f88aa1aa65db208ec4b4ebd0b754d9d1e11fb8b7deeeef15c1ea9586e1a077af08da372bb349714550e5a3941d10e2d627b32230e7d41ac8337ccbf293b223ef1248c9f9675246e84c226a1db09601d36385032dbe02f682122c02d130c778dacc826697075a62550911717c761ca3a54f6078ec7c588ee135fc9ff264114486b9bd17b2e3184c1c50c4ba5bd7aa51e96f94686548d433c8d3d04f5ff0c5db8d11355a4332b3e8bc837ac3d322fab1a0fdd057d8e9b0bf90e35b574df1ae42ce527a6333d2954845ff59658111ca53d3d5d0ec54a2058c74759fd3b81f9db740240331bf26d921c6b50417b7ceae2782020b1db5ab76c922e01dc82200dfb3976c68ef0e822c820bc1e9c011a83e57486c6a22c5227e5b04df7eb9075669d77304049cafa313941d9465921d9cd2cfbf78f5a3081a8cd38a1f9abf8e0fe06e0ef54274cdc6243b0296822d16cf51abeea58833078d7170908894e093ece80209b8462ebb10ba6a2248126482283411d00302bfcf8f7989403ad0ebda004c606747cb1b170394d2abb0d1f1ee49f3176a8b5422788ecbe0b03a498ca6261120eeeaa3644e834b2b44d2420176f609dfd093003f76f4e8d685088e954fd800ca53b28ea10cb0205115632a0db9fe311417aa9cfb1da6bd156819bb1d4fde0ba3a3da0c0e51de87603f27c8fbec21ec39f97bde45f0ed5ebdd9ef0636cfe36f4974b0f650f0ad6746b645c321a82f6280e34e0ef54cdedb7e002ae6e5cbf9d79b27b9a95abc0291faf6b00d380e038a820276f9b16412b8c9e93250e6c5893770d5079e863b7383779c7db4d8d677e90a31f1617c3220835162df386dee08281522c9a99bb1303c12b7637e6d1de50dbd6dfeb664b59089f12d9685fd0758d40a7527dedabf2f55c3f334b853207a7dd70b02b16a3055458905442b0ba224012cef0a40f3c1351f0490b975c7c9b9097bda4c845f2d0065811ec92a030cb0d0f4640bcbea81510ca04ca9e17a4403be6b46b52abefdf7efa92990c8c0ad198e407d46b557266e5dcbfe566ec2816bda82f60c8a6b1ba08a0efa90e142e3d75d4d6b02771da889cdd63ebf8ed21d3f136d77d6710954d55d517465967c204048fd69595b0354ef9f68753574cd0e6ce0dbb78c0d9a252bea6ff50f7e2e60fb7c82d59368f9013d2b4e9f671d7953d533b2b3154af2982b000ef87c546d5c58e8f4dfd02d03fa4a7127208efe8da8ddc8a602f0a37ef185a576f6ecc6e99c7fc6831e530590018a670053d9f63a1d9435fc7903a002ea9c06d650620b294b256110fd5fff0410fa9f2980773305f6f13576fa5c64816926ac82c6000017193b08413292ff194f5c5ab9d9397cfde36d0b2785e9b3833e5b216ffffffa04f8243785209993afff5fffd027bc5b1b60f2fe6f8e2836d74ebec236285901bd4a26961c714b96b41565dddce881d724bb3bf68c9794e9603a60b68c41a18ae4b1351d3a54c328c8c5a55396b0b908011ffa270807e698fc1fa024c9a30d6f83fcdaff9176b32f64f343c956b53570d71840d9ff0bd7efea9e20e91c6c66dbd96d50a379f79d29c2c18dfb8c03857c4da051dfd88a8879aa2c5c689a32d255207a28ab795f08111a89d9a656142e45230a8d3669865f8c22dbb40a635e5dfceb3a42bd363cdb61ca05c10e9e95b53531d1a8ff95e15712be4cd3ffcc686b429835658b8f879b623bb2a215eef338eb82c00201633c24c6b6c4881d23596937e14e415d87c3f48ec9a572446a50453f21b3db2f1ed0ed700331458781032527da8fdf1a229c452d22bce3b1f73a66042df4a564fbd98f69c0a9fa6d6cb31050ebf30921a99cd59e02b46b17171eec0f5a265a68f9bb02e4fce7d1b5965954e0e070c06b8fb717f5c28fc7b5852d3822683383a9b4812aa56b3af5752a67b533e2fc37272c2760add50b1a702a1e431a179fe756cf150a8873c0033d742860e81041d0cc58cc00d6e0fc43fcd33941914659110400320c970b6c3ad96d6de0d51be8fd29c0ed6703825403be1003e2e48311ebdd28800f6b2829dc807d08c22c1f879aa20d25507c751a140cfb334e83668a6e90000c61fdd92128d926c1677d5da1c99fd825dd1a0a7f23bd45d3bb33842f7d955b0f38cf590396a3d9dd00830f76bebe8f8a5e97336da8e0878432ff9a6b14dfe4042a10acaeda3bda38a3d95254cd06911ea42bc170698f959901b000dc8d51769811f125167c9f804591a0b433031b2a652f72f9d851cac2abc7aaa3fe1c8a511900e5f744eee504361d16fc65658072a3264f910476752f78ba3ed5491f4c1b23ab213dcd054895d79c2741a7e374b0767c2e57af978d3330e0e4851a11416f0d6317b7368e32ca2d078669d57a06b05edf70fdd3b5e9624df451b9c0bcbf02d8070e480d0b649b3f838585237ecfafa993618a0dd0da17f331cef3d951b269e1813504f59f31958ab6b849d5d8c61b0cf7f5f7081509b92985e81681a5332f9746f74801446c51b54dc040eb3a03de5f64b540c493857bb34ab72064a1117ef2d7abfc4a5e35504a0739df3e51598ba800a8caf9a61595d063e8b2d3a1ac82e264a299e068d5330f09775a404b128a7ebe8bf65534eb016afe3c045beebc6f6f1e211c93cdc5de91e32d42de04aa1b35d143aea00717c2894fde38b0119805e0632d293f59059490ce2db205839dd13663ef600128517effdbdcadcf3a38905505725839bc9020c4eb5ded8268bdab37ec4351bdab6ef55833a977d59dbe6c24d030bffdbe3655f2e011eead7863423b72cdd6e254b66d83a2058e1fb6973eccd34dba39cd7484018d033fe445ada0a4a03ac6f047d41118c54cd94980bc00d00f289a3b952e43b1417051c32fa6c0193105a00f7efca770d5094a20e9470c8e80b5d6c32993cc063c2d06b22d50a210cabe2ee429817ec3295183a707a12c4c6f76e64ea4784b2cc2f3c54a6579f181773291b2f29d6ea884de9780824398bb68308b2d15c8e56e03910075aef44bdfe2e0920bc1546dfa479f7a0abd547f9e7835b6a8e06dfe4984b2224ba1b782cf87a6917abd9cecfec3d55ff2eeef1240eaed8c2cc1244b906344cc9edf4630e248cc88235e7f4953b00fa79f6ce6af0ba7f9deb81ae0a1e5d2d07d9028908550818323edf4b2c5054c9d0bbe479bb42865ad5b45da8aaf300cac92642a582445837ef7f7e20a250e5c4b073b716e26e075f409cc2f471242bda767e3e6b65d9fbc3947940ba2ae645800adf46c48e3cacc87372fd55618ddcdb3a7fa2edbcdbcde80325573775b232fb97aa655659747d378176ae8a90292d0b4250d9581c5b076962068ebe0080361f61ccf8d09b89731fd24d9591663eaddc74c6e5a3e4a6c8575b2fc4fa854ad19b0e1a264999b0951d2f8594c457e5ddc980f338c8650147755555c4ccc4d69584193b2a41ba9f0d6181fea9672bc483701b78757246a3832b418c55b6c3106ec7316dc4e504c2664a4b0d798f5c83d22408c95024b12d4b2b63830726f65a4d1e0f31133e5159bb72827191fedb034c4f0022c30f8e0d1a9e13aa65183a9c188be4a2d0f7a3d6b16a0e0b3c0709f8db3e2c58aec97bee6f20d32a691a81f45b245c6769107d62ce13667585649ec48c34d133038074cf2b9b8158325739f1f116437633813ac11ecfe86da7052f9d440f8ee83d519e3c859834386505ffa0224b64220f11b38c43c6b08065e239e7cfd6bb593c2a8dc7b80b18db741bc60729030fa614976433d536eb1a5aba146b7c9f935dc5c60e316a3635565fd531e2d023a530681732ac247cecdc7f537ccc1e5661253fe062b1053f403c2dd25ab923d3f17c9a4d357aa65e64dd43c322932580a7f841347907fbad408744480d4528672adf2ecc309a439aaf492ba4afb54c07755021419a8a95874f4e6ad1bb191830f20551716fad97e50a08ab38c4c3e92d13c3c71618272bc985ed78f554611dee1e37505c1d5eb7a27b0c4cc8c3932671ec5c1fd3f61d7c5fa2c626c4a37d9381cc1bdd8c29e5cdaddbd98bbbbd8fbcf1773dc81eb1742ba513f38c04917270ef8416bd7dda8617f901d9fbc32c71d347e21a6b9f5c1118e1472e2a01f446b8b351a69b18e41e63802d72b8474467d7080972e6e1cf083b65dbb8f98031658a310a635e983234e8a72e3d08f46db16eb6d2285c876030747e35268c7419c106a219aedb483c35a6a5171785f750d8cf7a764dd88892d44b9350496df90180fa2cfcecf02a56ec408c4b9551d3288a367e5217623470cfdc17ca4cb785eff27bb0837f22bbf0b1b414ec9479190e55e46513eb8fa5933726e58c60db55e84a85ef6866698aedd4b104781a5100c36643336932c6667409b38eb8890bbbf352e156ccb8c9d845eb03c8e82ad416a61599bfde06be3f4bacc76386640c8a6e21264b2ecb2d0ccf375fac72391903a93014b102573fe18cace4f51b1af2e39e2d6488c842df2a410a3506d0bc685589d42a0c5be0efa7a116921ac7f44928703e24c8bc82e6bc0a8031f71a74bce3e8b904efd7d9b0c1f933ce3cbcb10acc72ee7fe6a49924916e90cb92b45b9f859e177743a63a03d83f190aabda2d406e2505dd30e363bd156055d83b3ed7087f49c4e84c3950d9bf4d7aa41c11b51f76ba169874b825b048169f677b676a5acaf7a4c8dc01d4fa0e2be147f13f85e5c9369ef448ca72836acda2e901cf91e399639c527f5791bb1231fb5bd289b42b8bcde0d5efe4dd26aa69a5bedefd07808e69bf12c945d5db78618d714d8e4742935413869ef110a03bc3c857b11c018cd22b91e92fb033eb458be906ea3b54527d725105a4fe9843bc8d4b44738e90058a441fe27b37c065410b57c10bfedbe102a9e86409f1ead6c2f7445e2d10d14013c1baee2e93806e5a2afb98a0c9c17afa4cb79642c65d95b7c0b80721716557a2ce45f738ffc4e06d39c01c2e2b26c9600d6b74d94c80bc09ca9813559b260a344725755b354d63ca958e163ff72062c7a8a41c578962bca0426026883b3b1b74120ece719b7c5030034ca6419b7eb428b883a56fe7620808c14f9c566f2bb664544ce404f334a8d99014c67817a5bf7bfa310a0e10c2452089b45fb10d4234c77f1f67590e46ceb2b8e00977527a34d0eb41529db3e52254328006077423bf2c669910412e8333340aee4792b44a525335a791fb1381c175c3d25efe74a2de714dfd538f57aca638a56ae8fce008808ac3733b2e354a107440f0f895365cb5175e73762193037197960886031fb64032e896d6a78d289f4cf984304eec3c46f48ab6fb8867b52ad2a9da227f6a56bf91c6219179e391974a34ca6546b36146ee9b4fbf777bfdde6774737c6d8fc73633c6b2fdfd5f719b538c9ea5d71f7a3a033570f9807e6e3c391bb8741cb8d8cd2c7bbf2f6c0c088a5773fc37565d1f15db9bfedae06f0d0483c2eb3ecba2776e571b4cdc0c50dc662d71f00961d079c42f8305aec2a5d9054816438090f9b53e2c96450b65e48a071da9b1bb9097631769b9aa5301872365ba6211abb11c379c314634a075677423cd18cd15371639762d6498e9be0ee39c9893cdb0df78a6f49c0d87d7b16da40230072893cd582a841fe2ac6435eec92e61b717f60ec8b0a9ec25a767dbefaf4074430ffd91f398916938179c417d38a4581ab85e12025396d67bf536e3931c02332df46b0ec5e09aa3de13e1b61357cb670e87dc9917d11fd28dc77bfb3523931d3f34479635c06f81ac97765cac062fdbe47c6d98a2fb4ba4605f48c28d02c941f0aeb77415aa95a4da15ddc928789d60ed413cdf1b729f31577c15a09434fc346c61001682234bb36abe755f40373c04afaa74083b2c3b0f087eb0159b4dba99849d8ada4a13190aaa298a27c2e8013d84e41036e3808fc178963994b46886515e529ed4eddb8a2df0e2b6da020bdeb7b5926f7e8901c903684ffb688c8cedfb5efbe6d07a34a125e056341e1c9c63bbd93eb5236bc0cc620af061bae099acd499a43aabffe3c820e3c70f05468f208f1a45f37dfcf4a67db40f5a4ac769b932ff29bb186d20baa24db0ec4f60f3c2c39b436a771b1fa56736ad69cd718dab6abf2b24870bd9914e7f9db725559498842e8568c8a8f81e520d063bdf78ee6b9f9f137f4e470d2cf410f6172839ddcf836cab25687b31cc7ab9cdd104d3766fcdb88ce133759de2bb32a1d35f39a02aa1b53ad450ab73b83e03840a8f13aa034f030931bf29e04111876631af481d213e218aa4ae6260af9fe0c45295d260f1b224b2666173a34c8c816623f55a1d0c70fe5af208631a4f7816ad943c3286d5bc26438a041ae2436ee0e1d46c44cd3b34cf050cb28f3b2c1432ca34cca3de8b076cb281397328c664f7b40ff53470c23d5a0ecc55000328ce27adc472c3799db7523d037e268599ab8a4ee53d8fa150ce3826e6d1c3a49a3d02b8127c7653e707dca11b47fd785b12e0a8ebad09ea36cb84fd88ce0e2657c78e7820c465ddc92a530eecd6d0c0aaf0f4884348c603eb5fe402c8fa20a70d2c46599378c1b7d82d0c0d0d8d60c977a43dba502d63002e39c0b777185bc4f763895e8f080c72b46c4fac533934f1b9b6bc398a1e72b331b7b31f801388c34713e09e92e4d7ea3adae40ccd34c112ce0b5ed0d32b0618ce9e784c867bd380188534519382e16990e00c338dc20f0981662a596bb309afb531ce1e3e674a36bb32aa4c316f42f54180b224f243849c916c608d8b58816800aa341547c4d521e376e8b4400092346e5a3929db400d5d0c8c3504f12d8e0c2fff76913a3f4174e361770ccbdda0d61043b7c4390de1765762f4118c98036cdd59ee3c9295b92498d9a776e223ba6ee3d45a5d9612e83f6e77c301e65d3febb4d4b0445c6b6f12775304205e44b6ba308454e29a0746c1061da0e4d58701098e760dc269f05d1804bdabf62f3167d5de7bb935b4218b9aea6cabefa5c59dfea609c76820aa657003a3efcc0ea14668230c2cb1c42857cf22452c040e562c213ea58fe9004b3ce9057cfedb1be5e2e98be3466db63101c2e050ea384970e59205c554d3ac9f092c71b5e24434315551cd24994391062601e7efc9a5893d8045f41296df8ab163a8b05031b044080c0427054074ccd7179e7943e1dbf756cbc79a7db60e9bf97dfb60c584c6ddaa41b21441a2164ef2df70e350fd40dad0e3888672963a6d01258640be6a85457664d259551442979e4885030910913d58f9c1826e9476aa61a465790804842fa5531117107f69104bb3cb0fbf080eb302cf6007b8bb7c03e5e876123f332f67a83375c39376c384e33210c2f2786cb7156e43e1774456e52088c40fa11621194fb9e11c490fbdb1aa68ccec17c31df076fb340760aa409e4ac4bd02490312390314064ccbcebbeaea4304e466dda05dc0fb56992e767c0b511596e9f9f96cd1f1236a78c0cc81737903a9035f426c84082903b0dd527943fb5a13159843c4e215a18e2f2d8802585754cbef44baf66b383320a55575d33aafa9563a27ad69e61d74cfcd128b430852924cf53298463fd4183680f643c7b7b4341fa96f2258f2fb95dd8ffe5e502a7a59cb4246d0efbaabee49a89af0bcadd7a0bf3bc1c7f85ff15e6a91e50e206b3180feb1c30b2bd27d67acf5dbec2e2ceea2d6fc13cf7ab9e987cabe21b39c828c828acb26b68ddc023486a1809dd1a46488aa88144d65a2993b0e5add7f36cc0095210c34fac75196bb962c541ecd80e10c55abe53714003122c89b5cea3813504c55a2e63d5e848bf64aeb5d62332d7304a22d73062a25f327b925ace28896914d42f997f9037b3d56ab5debaeb2d3077610e4c1a8c8148cb5b3e769396db15e97e9037dcaca568229b47b4080da23692a8c868c9519f3691f918599f7e5dd2af47f488a433fad34d7277d71af4725d6ee88570e4be5c650d7dad4730110855e104406a00c51a9450e2a701adb75c820a68bde506e74385560b0744c65ab06cd9e280e0be8724892b925092c7364a2249eeb79266a2a9e60854db87af3c821348c862d2e807a9a4f506b4ce5111de2b358fe3388ed35a13777f13ccd1966853f8ad4dc2133054b9576910ce6fca1aee95f4912e77d40cb973e186822714a103b9df4bda86f399c9b195e4fee9a4819656d1860fc2e694db3ce7d46eca4f3405f51972efc09cf074e7086070bada08807248b7f7223d3713731529e67c8064ef065e781ba848d78428ad87d4a0811a68a2f01886f7b98d6200754dadb9877491c9713f509bbfa4cc7386b5fb542e3dc91b535751f9e44e7e8350256f0830e50dca6d90325366ca95244d8b11434686ae5a2d3fc5a3f61943e6f5ffb6dcd65d5d1ada49a97337e596749cae35a5eb64d9935e7fd287ae4949b154a31c9df444438aa234054717c2948f728a9d14944f1bbd261ecf610ddf62f1a128de63f933cbbdfa355883f45b3a4a8dd2595078cc2c32ca5ba6e153ac21692015de9a70b757e1362cb626d4a689a0186a22a310873834e5ceac7247eec74883f45a73a7de33739c4cbd647eb86c994c2693593cbef2288d8c62a0dc1a1764b927f704880b43ec437b1addfed446d61a5923a38989aca611b5d99a7411877b8aad49eed396c94e779c4cb47735eeaddc36baada45fa87777871b28ac03917094435985c799bbf74322ab533087d2205f5ded5e03e57a2bc78136a60e877b74d6de436cd02cb08bfaaa0d6431076448d7502186000524dcc00a2992a0e23a38a2081c10f1e30111781ce7a369336b2d8739203784f76931f5b039e48070b30ec3f7391f205d43bb267c78e73d4fbbe955fb89a69d6b5911d190908f2c222a3a79bd266e9a2658ba630f2d4f4cc4e7b1d54f3091d5098872d3777209e6703b7c78ce25a47aaa95f3d1b2fd89bd73c99d5c08ed8d381f1a23864c0a1e5f283c5ed3b40d8bae21bdfe9984c71f9e9f57772a01e75432979c70a08d93135147082d7378c342666bb235e91a13ee21fdc2a26b503091d5e7cca53b337867fe76f487e8ff00847779d098b986cc44604045145a300335c420480458a0832b8cc2e0041320e1c957e582f880828a29823a60052663a0c00f56b00406414d9c018bbe0477947c7e65cc3312ca24119b4d92f082a09837c3dab24a6f1bc8f399b1d57f145180d8ea3e33d6f2b181244f4f8cc7c84eeb2d5f5de69d8eb5601e1e24823091c55a97a0786385c5fad5650b8b15df6879eb32d672dba8757b890fc2f6e91ffda244c236fa81da3410104966da44134ab200b26ca208b2aa554dabb58e5c03f900552026f043bffa8709ccac691a562140c3443c60a47ef556b224cca15fd2e887ae191ba88186acae94b0fa2a022474c10ab98254c6d473a777d49175a0b27dfbdcfe31851088dafc408d1aa881fe43f501e27c2825b2ea21fd6a5977733e5db36191de0a23627042d1d10a8eae1084144760218313dcc0052530ea17fd91fbda07c221489228d2041a3001095188b5112421307204238e86882085041120d4804914644c883516a6e003062998f84c810520a2300409214c4831640a05345952041467100390d9106af881d010aab0042e4021d60e50020d84d04408148cc2a0c5c8f59c152ca5f7f19ac4c08c29b91ea5431eedbd24a1bc1c10a370e47c721d9af908c57a7e60c41ad2b083231e00c18879e409a1980473389fd79859ca18fa15907efdb0c2b225e7238fba491bf57b49104b0321bb3ccaf5463d0e1cdbd65eedead0af3ac3ee9f342b98d337ea7154adb5ffbefa557564520028bd87abadaf7ed518587813984d3f202c265d1da84c5df4ab5269445f3d30c7c332b4f6463d8edfa8c7518f53af43fd3d8e524b7aee8e135acfc363877598d1afaae2343b43071184557c40662ad220a50ac79322acef7ce4a540b3a7f8dbe9bd8dd6db8fd6d6dbfa1c769ce3bc213cc7713de947a9a85333f7ce1c1eb72cb2e036aa632f6251eb9ceb1a74a0a69834578ef66c6a2bed496b6fd774a4355be989943772a4b6b76dca98dca34d3b3444253f8592e4f9fa7d9447a0c292dc4275621e33cf2047a80214f902aadce33d731ea00835283a3aaaa2284b212900c9e324429e7791651355dc204fa197be11639565bd415d7cd2a6de20ea41244d35ace09387c03d86842304b2c8f43af740eee6de97e4711cfe42997f18f176b41d82c5c33dfa7543d8af7847bfe6a67139dc49b390fb8e1e5db37d879eee382ddefb142dd666911d3d86a0423883260eb68fb3c8d6d18f24f32c3293c873d3f087837ed9cc9f22f3670aa197241c6b6542b842848a226cb08414545144860109122cf0011640ac48c1af4c4951e40422a2f8c20db260e9810f5a10042146040105b1f91d43cc29f4158144892379ec231d1494e73b896622cfcf7b38e8206a538d9a1a8d1d6474440a49810b40d2600522a8e00103aa9044094f70a20a3640e208c7cded3d8d77b4779847c5a3ea11bbcf03ee78dfa8ca626f576c2c2a603bf71eb477d84777ed1cf66164a7c3dc7d480ab35984dbb58b2d760fa473d80778d27bf0be7b6f40776efa482af610850e886052c466117e075f24c37e483f5e15a18c894655e0e08a3200c164cc5484922b80c148161be7913c74b308bdd9139b4656903e63a4fb30b2f3ddbb8cf9f8380f8b3d74e7b0a4b01eb4262af0820b5c302493c55e1436cfe3d331f01ce0b0d89f3e08652cc68b091128c104262891c9623206621f1ce8b08c84c5c63a433a50d8bc28dec83be133111206e2dcc718dc678cc331c4bd2ea1309312a18c893dcc58f719eb3e84ec41286334891782c86232e66171a73be9dd49b81a51d83c12761feb90d8b8872a117dcb2dccb4b723b97135aa4cb237896c5549bfeccc28cf59bfe63b4888da60800734d8811020249008426cbe87e63be80815a1e719f1bc9e473d31ee8a3b1ccf6cd21393a049a421911c2521f6d071ef2e63620f9a8f4dce1e60f7cd41b1a883cafd1c168b3a7d2c4423394a22f7eb45657a25923028d976a0c41eb46fdfb00fd2b5cb5877b96f973109e22822eef070b87d361c242071673b770ef3f48c6ab80e7514b36596ebb17d8a81275fa408e56796ffbcbc1640800522ccd00121c4508a641102a32f90806889987c28af925f49f96a9d9a694b7929a5fc8bc439643ea0dc53ca08651b0c726f3fb9b719ed211983842eda60d051fc94b22745382ff336eb9af9493191ef383d1b2680dcdf4c206dda48c2faf6140447be03550d72c72037b7b1604ce579145923a549de6c9f0fe50dfdbf4f3caa3416a48d176a8aacc86db7bcdd1a320721658ca8611ee01577c09ffc04f3e84061b17ac79641e68472df715bd2537614e1b8cdb81b1621ec1880343a5320effd0e9a32e6bbee81b4b15548585f46e6861b5c5c44f1944ff49b524610ef31411a1d9342487ff2a270d48a50aa15c1a2023ae6614b4461fd5a44386a45460b856683a9159137359037f6aa22844be4e641ee4dd3364dab35d48ea84d1de2344a647fac0fb501dfb740b6481662be5fbd206d341f0a1ba236da1185f53517e47e9886b15ad1e1910b55aaeff309c72fcf6a6f6d9d75d659abdde19f51120f906aa6adb6da6ab7d906e6540be6cc6da624f78d727bb468ca2ed59772ed982773289fd80290bc92e51359002a7d337cceaa5f9de5a794415f974c1914b751ed82bc99b8af70afd21082cb3cb62cd620cb41f40873fdb441b60712d6ef300c3a326195e60227723f084dc8584facbf016d5b10dd8ec89b6979706dd07cd1f74eee8cd51b84e336246f64e4fe5664fba136a5733379c3e5de84f0f6036e3311849d650e5ffa6883724b30e7c68f108bf33ca5df8479b6a1f7c4c25bba477a6242e20cbf537af8f930bc0917c9fd106f3f14d6e709cafd1206caed71dfbca28dedda3bd3ecd1af74652cbc32d660ce8d6d4661ed4521ecefe0fb67462bf6a010d222ab31d196542284e3369b574bd22fa37e1175cd38b729ba86db92748dbd566413a23627e7b83054a9665a1076664d4992bc19e54d49ee9f0c15a136dbcf360b5285576db30b694534ad08b591b1a0234243b935a2dcdbacfa20d498606d49bfba2a116a4a4005c858e912bce11da7592a13426f76b9d696166b25b2aca8545229a8138a293c29819fd791b84db395f69431f392c889ac3627f845615dbad487c2fa1b2d92fbe00f50eedb206af3c2417854d9a05e429de89e560de1f6f384b0ded3eecd6d1382d35a6b6dd4af1ebb2ea1b0bea77a09e5f135be725d426dba6d5d42dba82ea16d5497641af3309551587f8a70dc6641b3172784f3e36aeb21b50d3af0a84ac5c950aae4fbd5a30b1b2db962226df13744a657e0c99791caaca66d74db28a574d368952294d933124a0a1b5b48fcbaa32881378c2224fa3c6f062fa2500459ecc33db11c649a108e3279c27670c40838c082153a8062872c07369801121e506186d8bc4c0c19102018c902165644a103197c21072061904d51640d3e55c80090075600831050010d4444424a421032b4e405494ef0e3128517b6b0846c082c20c3945300c30c8050832b9860c9156a58c28527a220821019901850400542ae10862788c0091a4d78200a244266e0c44ca888044a8226090d661086342039438f08ca30a485226290841f3098150947520849414ea1674ac870030d7ec5aa61df38070a8b05629628b24cac87ac13265e6060aecc2fddbf0d439b67089b7feee7eaacb346d657f6e21c5d411e60f0d8f9befe1f5f6eedeaacc3dc56189ca37f6f3fbe60987b53e7be270bbfdceec3e0ba7a78ff7951ce5df1de5d390e25558ca35f3333331f67b01153c6caa3630f6a718ed4efa43652af3376068bacd7affa2b7a987fe58a28ab9f8035c659aa78b4404feeff07c19cdbcc626520a7eefbc2a44e411d9b53f738fdb1aecb751fbaa67497eb2957e669c49431b3eb2a39ae15eb8e3d99f5aea5dbd728ddbe27f3a460564fdd9edc771a315fac5c67b95ea22d2d2d2db6554032a5ad564b6b5555ab5438dd6a695dd52598d3727c63aeba2e4eef0d6f89b904736ef88b2bf3d205df420078192ceaf468c35be56b95fe028b3af404a73dd74d51b93ca1ec57bdebcaac2297481d00fc05dee91700aeb8f2b5aecda64b908651187377707175e897fc0d7786e9cece3a648cfc0d22a21b3ae44d671993f9766e71679ed13532cbee2e57e61657661211a174b9ca5d2eef6d20cc30320065972c915ce193abe785619843959354b06833ec38add34419768f53b9adef788419961a513a5ae595877459e54e6573f906dad0c0136cf004a00cbb0569746ff977471e3b32ecdd5dae1035c3708eee2d7f5d1e7275afe5d2778752889ac71d39c7a5318638dd4ad5d47168cb1d5bb847cdf45d27e9861ab75d43d844ac4f30c775cf73fdde111f5f0503d3b27a59617c5f2e33cc0beb2c9cc375d655bf776cf9bda5f87dc73bb62a477dc5ab2f7fdd6ad1965f181caaba630bd6d123bb72f0e0c93097195f1e34c360d1c6a45807b54bd0b3e02beae0f7f11b5f050303a37a7dcbb56f3d75b97e517a4b5fef3d0e5ddd965bfb05f3d6e5fa05a3fa9d19dfdf735d93fa65a55a5e6fea7d47d75978e4522995ebf8ba9ebab25ff75ed51d75f4c8acdfafae8e2fb3988e507a3fce19025c67e5605de6fb0679f0b248b18eebacbbaeca23ebaedfdb92838500fb3e0b8b36dae21cf4dd1b13b56e2fafa3476e697524e58a478eb6dcdece17a38f4c2fe78b710341481933f6643a92728bccd4e2268262234238b3cb555cc4cf10bb7ce52b5fd156ddf5ff3ee530302e2f3f7336834c213b76ecd8b183d4a3aadc2bde1e55bcf785defa721597f70b4e8181c12ed55d2a97cbe552b98e5dd8e582e2c228c728ef8be2618c5fff75edefbdb282451d97d3bbacb87cc5e52b2b2b2e5f59097770f4b547cd2b27b97c6525f4422f74e951f30aaf5ce538f4bba1ca492a5f8139587471394eab34921f5c91533fa901f3163c7eb9e59e9c3268a57606e1c162d64317eb147c69799d32546fb93665dcb7e0b7b4dc9b219dc18071b1874b1858d21cc6e5bdd4ed53eec1b83416064d2a48bf681aa8e2217a8c216c955b27b238ca1e0fabd465ce0947799487a46e732af5fa1d417e54b7e29cc964b22cc4c4aa9f21ec245de382c7995da8abba1ce59ecbed24fd9247b95dc444e8f2b1a7c8728a7ec9bbbc6004a4ee15edef655679376466fde019a45f600eabb65c77baee55daa361f2ac4bbca35f52fefd92fd9365ff08a19fabf6e76cf5947baceb3acdfd6184e5ba77a35fd2759959a7600e0bcbd0f51417cbe5da01735e2c49b4c20d440304e60cd1836342d8385ad86211d2f74104d0f3a0596491ef3d7cd5bdee73b9bfcb8bebf6ba5eafeb12b451fbe5fa38b3eb2c1bf5f6a99ba3b65e6fb168a3dfba7deaac3b73cb95c263bd1033a7be7aea9575677e59dd0994aaf87232a6e5aa4b92312da7b7ce172dbf579b2f5a8e435dc60fa67b0b6e28c09cee2897ef245d23b344e951f3cf81360a90451659649477a7a40e3751bf241aa46ca09696fed7f742f106cdc505c818cdc51e2e690ee3d25c09e334a7341706a58101e3ca18cdb5d17abdb5f5b2aee3cba9560a8f35a7de3abda34c1ec7b1fa0044a697b9f2e0719159f7e697ba3c9c3c751e9406eb7275a3d693d39c93d55b5a38ae6fcdf4eaf84e9eca71f2540315e92155084fbeb2e1a67ed2a39ede96a84347d9935b3e419d8a59c7a127638face3cba9950e5dbd8585459dee9dc2384eaecc36ecf4cbd66abb45e6da727770f44bde862bb3d4880867d86abcd32ff97b83eb42384a221c372e1b8b3add5f6edc937b72bdf367b66c2ae89503207ce088113831022d8035e6d01073e82887ecc2634f76516a69adb4765b6b1be3a8b55616bdbd2cd76bbf64bf542d3a148fb25faa533ca2728bb5917a9fd24861a923876cf148bf7abdf7f6deabaeeb8e3dd9751c9a6ab57ae440b632b72eeb945e8296d5d51b82f509a49bc562b56e2f7dcd69bd9573314eb3ae6b36677346251e5599c382a9bba711ed49b09ba6523ce8e9de4b30759cee3756bd529904571cb599187b68eab2e071defbd3e9ded3efbdf7944ae148d113a1b98ae65eebb25a2eccbbd65a2b9d3e7802e917758dbf7d7d79fde87ac5e30442c306c6c7d629cc59f530ae6c58ad2dc75f9da6e5f6aa95eaf5b4b582198f318bbee615ced1f255fffd827bf4e86a9dd52d3847eb2b9d16ce416ff3ca62dca3f36d513c8d982f1ab770eab278a48f8525e55e7b302e0ef756efdbc238dcdab07ecbc5e1d21cc6a579cb5db1682e0e389ce6c250e1b07ac9ad7bf4c3e1c2b80773576f5dd65beeea3417c6e76d39cd0dc00723ccaa00f45d77e6d66d1a2d78759c0e407bfdd15c186f1da75d2df75a77fc72eb306e6d58eb2d778234586fb94e77ddcead9b72962b336ad4f19d4e1b086746790acaa9ca67a88372f1e257f4effbd3cb5f5c773986394679f761ba887c0f918d832c5bb7efdb7aea8e3d722a85c5d52d1e451e5adef6b405cb7eb5a54f1d87a6ee98facaeab479683df56e115758a785659f75c7d4535c6fb9deb85ea5d52dd771ba854d47428b757c2a97200fae37cee17a0be780b90e578541d181c1b55f2ff8aa371e5db0ec177eed17c6630f7a7fba3fdd133d9dec6beaf4aef774d5e94d6f537854bd07cda74b9006cac7da3fe15ff5f73d1e67ad78f454aad7beeaf52c5d9be56d8f599f58d441393dca6a75d1a25c5c5d14efb87a0ae5a2f8552af5f5a079cb654beb345736acd5baac53f12b4f5d2174401b28172f411d7b118fab94b5789cf4b6bed6588f7a5bd659299c6375d60a8b4f91590c33ea8eaa9cf2953bf6c8292a950f05a5d61c3295366c0ed92f94f74d39bd293abe8c721005cf21a6f0e4ced29d43fd9207ef84a25f72ceb42242ef16c9f21de9ce99f681709cb35c1ba86bb84f217706e9973c921f0065299425121f18656fc79cb37ec96f69081bf77817d15c7e07b5b9d141f27d44be81ba083dcd54bee2e0da7bb3e9a579823466aef236119adc7207cd37caa31b7820443644892f98610c5de842132e4062054e04a96202c9cf22f273263f7fe4db88a6e70d88e005d1b26c0f542d10b5f15e6f8b501bd26b770b446d3edccd4449baa294f7308f118977bc773c9d05ead7478b835cef79574a49043c4e5f3b44c62c507d25d959bfaa0ff0a577d787911df0a5fbf86e7aad0f5592ab9739e4fa539b93d7cb509b8e0b434f732214e777ef3b1f16e749f7ee7d8774ef1c777df830e2c3f4ef0d8a0af86e7a03c09788f86e3a11e03dd3576f10d6aba84daeaf47e48d8c5c7a95529abc296716a738c1fb08ffbd07d0c7c9c1fa03e5dc7f9c6ef280244761f528d774b9922d82ed905725806e7027610c0aab2f79a5ebdd9fbc27c6e3792f7dc77be9f3a552097355c64a574a2f0a612863e66bc81c844ac64caf6e5ee9ca12e6aef8610e9fc2e449778c91e5b58b65284c5a6f0c5272f6f414fba0b798ebc187bd26632fd50caf0f1ff50aa8d76ec51d7b5cafb843af9d5ec316d74b31476112683635f2e4cb6883b2bdbc272faf7a9692ded2fba5be3a5098767ab52699e3282df19859e48d1bf2aea8bd27f61235fc7e71ef8971d35a11cad8bdf7391dae69b4499808e5ae03b5b1400de3cee10cd42fee313a5aa0cc9dcab8531fee7dc4bd9b70f76e0caa21211477f04cdce9deefb0f5a130eedc8f32c7e11cb6dbb2c00ba2c7cb586557cb9d73988171269134ca2107a3fe5cd29f44b3a88790c27ae028cc9e7479b53c6bd8b18b107d0debcb8f9cea0868c3a38b3c72b9b95b1c4e3caef088fa542bef2f39e4be278562c84c10477fe8482e22c11ea890b8f3cd209ceea107d0bb9cecbc7f3db1eef21e6e70c77b8779ba7b5852d81c6710784386c22688ef486574e82877ef9a8c9348dcc13c40b9bbe20ea949ef89d173136812cd29fa15c59462be98445b08e7290e658cfc3889aad69d12a2244431e594d1559f108bec490a8452880826a417c785ae4b216ad345266c9ee38a8454a8877a88c2aa6c561b849bccee20a46f59a678f3916d3faa14a6854210299cdf7cac6afad815aaaab37cb795bbc9369f297c7ef4cb076f3e32222d849b4cd6353f7cec2692322c8ff3237d8a0dae67c0869515d7a7b8826fd88065de914231176e230a4bd1b24db9f4e5384d2f250a2b9128b89642de10f10963aef7eb4cde84ef5713d413a8bc5f77206f52ef571ec81bd4fb9588da0c20880ae48deafd2a0579b3f27ea542de88efd72ae40dcbfb3d6465acda0369637f28ac51206dec91245d635495740d45e9eac4fd588320eb1148e45ac44281aa60cad098305f10216d34fc918e20bb7ca429c8fdbe42de90a1cb206fcc206f48ef771379730659a306493b206b406d0985f561f00b7e3161cad8d6305f10694ba80df793c3d851c8fd768224000cfba862aebbe031c4f8475b92fb9bcc428123e28228ec07058f292929610b1ec31666612bb3b299493fd4863b3a22fd70a49f0cdedeefb3bb78495fb9dcb9236ac31da9be72c53b658cea2cb7658ceaf352f952a94eaf275faa73472ad547924cf5916b92fb158fb36f82b057dda32b27ad503cdec82b3888cf3cc79e1a20609e21f7b0008f37320dd446c6ea71c01a35cfd98d1d865c7a70f99ce0cecb5d308f4c8214622f7396810c426836a982a82737889a47d518e671de515e51ed574e84db4907aa9f45e66cfed48f3e303723ddf923e44878bc9ab30ea38e2fc11a346b385c037ee941d2797c09eeb8fc05f3884288b9606ee71e631ea9842a8218c52e9614d693ad7d0da3235dc3dd0675cd36ad9db38655cd862492b6693d674649582156db50044fd6ccf2fc8ccb967b9e8882b07ea57be512d4915ac82af7bca65f8d2bcdd0822cc86206a8f1caa24c0ce108534424e73045b34ad146211ca510159231455d0393a46b4c5fbf262785382bd4b7b465bfaaa08c2bd7616759e59e2786159519822a17c59b5229dd552fa15e524189a2288aa2288a220dcd4d2d411b7d514ef128f230f3a62858a787d0c051ae371d879aee68facadeab333c984ef31667b08ea94f83eb51ee68fa4a779aee8ded59689aa6a3d161672c8d7374a7c13950efebb0b3dc9dc5a8af9c8236547450b8f62b051fc4b55f250cd6300c6908521a1a82a73a8227959a3ae1d441130876b71828ce61ba7814d3457a1593a9aaa8984c342aa69e4c436f514e452166aeafa28847d36978a0a139bd33b36c6770a6791dad4f4639ed7e320a0dce61bacaed2c9bb0f895d61d4f8ae495afeea881a3bc02b26019eab0b32cc4cc2a2ab84593dcc2e51a57c6127820d482675bad4d087bd3ea27ca5a8ec89b180f7ad04b036c0fa1a106415c80014a4a4ecf882c9f3292dac8cc286cdacccc286c1e758fe5aa2ede95a3dc17b9658c1a36cfcdcccc5c30b758b92daee89f646f7e3357c648bc324a64506e4cceb93506539452f9fccc8c5e6566e66706cf04295a426d7260608ebaa6e61b66fd6a71d442098c114c51d7a07c1e2649d774e92434813bf464964b16dc28584666898c51a2a4c592164c72de3a6f1befeeeed71a211c01b53882c2095a1c519b9c6f44401034c710b22036e33db1164d361684a38c1191035cc6484689cc9219782965c8cce68b79d053ad66503e23a2601aa4dc38b49ff90cdf35c64148c6cccfc014c1248191994d1952be307d9a3e768639426d74704e18dae0382c29cc911cae34e9d75cb9a2c638f3e92b4b56968842e4cdccf31e18c2d3e9ca3ce3e6bcded1fae451948d2a77ac790c5137ccf27c8ae98ea47066567740dabe1937e77d3526daabcc1537671e39a10b44f5062f5e00fe451179a302a4f41b7ca8cd0cfaf91b663704e99a94cfdff0d335a836d9b01051d8fc69e534577514cd53b75ee5bec87486eaa69c06f999716b4ce5c66498226a23cf4214b210e529f3a35f5306888ccf5138b63892f931d39f33b354ca95f9911162446d6c7c9e6509b539c0e7599ab01c519b199f97324132b33a86706441d2a289bc31bdd27cde034fe817d6cf643f14ed3b813ba028a5394d3d0b1296a1ae81a26b26cd74c6e901aa8b4c2f6582c8b4cc519ed7c01c1926a18c927e4d19a3da3d86709499e57b16ac41ca44742e33a3363a18a688c2a601aeb8630029c44085a39c2beee47cc667601e8945126239ef89c9ccba86945f5e3fffe248ded07ce64115bf18c9980953f42a923132334f2688cc4faeb6281c618a8a64cc6ce12363e6a70867dcdbb83fc0fd8cc280fa653fae2c6909923161084719231919a03c7fba630b2c5ce4b1c5d1f8a206791ee58e750776e68e75071e18c2da339cb13ad72152821f9deb9ce0444fa3f379941195405e19bba34a0f2a7da4f4d49de6a03f9d8efa4829a5a7013f595a8837ac5cb0625a39b40ce0044839dd0af05125532d539c8fa94c6dce1f53327d9537a95354a634c73e9e327dcb1b955353a628995eaa4e61a778041fde03c3100cbb0effc809cffca0ee4c90d27d120f928145108d659c0210e00e20070f2c9dc8970df42e7a18fae2822fa95b892c77ca985be9491177948fbca127451d8d94b0197634d724ca78e0f2e92d4197c99b007c9ee6e250f3964cde9c7c9e45d435305ebf6c234ec6c82604e50dcd337803d0af99432ce3837094f94133e717805b6a4216c075906f3ececc628e64cc3c8cfbfac390a3f8059012dd1f8c8bfac92d9dde569631face7161a852fddfc7d711728c87b7c511d844c6cc1464148e114a221c657e987ef884a709654c1d7b7248731a3ceec8f4a64c710e7b3abdccbb6b3a427392e1a93bd19c30111daa9373671c85d2cc58d38c3551fb996b3a125a1414bb596ba2a1a6920bea250526e552b141f55a81cdb4585a8837ac5c9c5a5831dd3d1862cb995e00200725c6c50d2d602f1b5c302f2ef8bea5d5046b25b2aca8545229a8121876a799cc9d14e5f99b3bd20e0820c61d49367724ddb1a7a8b923ed000e2f002023e38e1c29479be1766fd36497d430e3ca98473f19236a23ca1a36df1f4395ca2857318838cb53040286b0364823a7694ebe46f126b843e333de801a370016770c3067900223463103bc2786d333aeb833e339cfc13c920b2488cd784facc55147d4119d3ef3098f12c8273219334fb72b4b429599d369b0e9b6383a9d9ec3de844db79fa6ab3e5a6bbae5c1f41cf634c7e9164d523e5b00687971990b0307c00587979a15188c8d2b860d026069f1ba115ddcc05ac5dce294dba2c96d71458b231a1ad4dc68409a16472d9ad0e0f1793ce1991fbc72b4c2e4ce047942387633b33cdfe2a86bc699598b265d33cacc5a5cd135a4cfb7c06266a6c3e76566370288615383430060c8bc00408c8b1b5ac06cbc6c70c1bcb8e0fb96166b25b2ac1ce01d91f78570b4432727359137341fedd0c9d4e2e8d5608ebde9ce9ce6d67b7408618ba339330d1e2590695e00c91b9acf9baee94828a3904b0783320d6e0992312f0867665d23ea849986be7e2648d7883668c61d9906e7a0f3a28ce6dd4f97e626134824a7c5510e960daba7313558a3e6f0de0c6950664c4240692cfad4192cca6850685e1fde717663cf1c89b2998fb58a3e349f29978a0daad70a8ca58578c3ca85e9b246152dd73195abcdb5ca1b94e38f289aeb78caf52d6f4e37e53aa2e49a828279c523cded3d9a2bfa883fae28ebd794d49e4694c98c5b34e9d7cc6d710592509475cd0853e4d335f3e20f11084c91cee71fd435e1e7655e0020c6c50d2d602f1b5c302f2ef8dac03fda0930e020bfa5c55a892c2b2a95540aea84623a00c6a1f9c40e8d75073bc8638b2398077d993433cb5347872b33cbb36b5c591763d1b8b28e0594e73b8a70bb4472aee55c8234ea5b1cb5c0e24726227b14cce131937bd8cf4c411b36dbfa9cfb241a361f3414e680320a8a818a216b9726ca08a5845eafe994968801dee28a236a035364000cd3e228cfb832660098225196e74798a23c2fcec41f5126fad4cc621dc418342dac3f346c561fbeb904081d312c7e85aa232778f2a5de99a794f1c9b2ac5d536d50614856893d9eb51f73c874db6848afa2975cd04729a5bc742163e46915a17c4919f4dd41f4a899884cec61c6ea67ac629f0f262ff66716754e72ff2413d140c51c854999022f6c2a2b8a6a3d393ca23247d27aceb6f6fd9add3debc5e050d2d5b8d79cb6fbf63286fbabbeea474f6b6b5debbc6a5e722de34d2d0c315e5cc4a821fd28d341486cc003520e68edac33653299ac077d02372fbfd2b92c42f0a57bfd5dd312e1a8237fdf4bd7b39fa724ac07a24104d02cd290415cba4d8510887ed57a2c84a57ffdfa3e4aa32a63ec4bdff77144e8e57617fdaadb86351deaf7f194b79ba48c04cc17dbf650de94b8cfb83a3021e45e83e331b3f7044c190a902fea6b14b9fecbf55ee5386b371d66cc1047bfeac79d5c1c0dab9fa00fdbabbc01efd9eb43bfeac18be30aa1dd117568e64e6d74e6709531db4b35731cce01a773be1af0f53e80241268c35651e7bb10b4deab31b671031d8ed1f616cf6858cd76cb556e7bddec86451d54055039877ed5b05f3546dbb8107224d657829b114b52d0040910964822568f5085a20f2c41459124b11fa80d276176ce90b053851c89d17737a09990406849ac2f6332300df22a2cf4c8da67ef518d525a69b760a336dced35affee036d39170fbab5ff4a6a070bbcc759b31096e1baefda2971abd8a1ec1e6c40342c7157972d65acb598eb3d65a6ebbcfc99d3e3b349e3f80d48cd266026ddec79656ca9037dfedfc317f4ca039b35abf34f85b7003356dd35653a6526941dae687116dbe04963a7bee79d3b1209eb230943136a6a336dbea51d20f239667d336cc7312c7e970f8d3f0948155a33d354d93a4929431f63c5dc33365b4ce8a75a0726ba1361675b08b0aa0f294492c6c7400416d70e0c0d1412646109882ee6e8a89d4777d129e7c79a28a1ffb09249fa06228db4f2176088f911d52a954ba8677c0779887034ffae7bdd37e2afde482ef6b44c39eaab18ac2ecbf4b2a954e0eda9ffc741e233bfd13e631b2533a09f3bc28ccbe84cf6364e7f4c63ca77fe165ac6dcbd8e9cad8bc327672658cbbf62897c7c80d13e699b1920f1da8a90727b884715098b53729e183c7c802648cbe5ec626f6412f63a32ccaf69b6f22668c1ad999efbef79dbeb724dbb74fbfc33c40e0e94361f6133759d237f560fec5e21cb3b635ca12c90f946489e40753e02c91fc2028b700f00200b7f45a6b8acba6b8aeccae7b7682ae539cb6b6da9a927297cb75e93dd79da1ebd6e57255972bc5e5aa2929f4f5ba6e6f0a006ee912741da73dcf7e15d47e7afbe26079153202450e409642467e3213f540b22b3cddf352df3c8139a7d7da5d6bd7a6a79fce72bdd3e9f47a3a9d9ad29f4e2ca7130b0b7ddfd3eb65398139a8a7803927179750822d9e6a208fbe8b8cf5295cbb8b748dfcd82fd2dd2ed2af160db4a381867411d88194f44b0e519b9ea26192430335759e60cc7b724feea009d43531979f46984f4c19fd335fc8cbcf19489b1d12262f4f2e6db8bce1d2c5e5e7107923b31442a88491217adc1d5908477934c48e1e218dcecd848c91ef3c33c5a28e4804c667b6998e1f119a4f2018f80685c9cf0c80cb7b3ae48de91247d7bcb8bc0f5d237379204e392b84148f213ebd5fdffa1797be850098d7c3bc27bfb8f5adc6a2ce7df9358139a954eb8e3db9f5d3acb5be46e9f53d99d2d7b6b427539924a78e435b97e5a97bfabd2f2f143cd6ccfa2fbe634fc637a9dc5bdd997baebf45945ee9948bea9fc01aa4a7b85caef751c09c66c2b37d168b45f30baed9de8bc157d60b0f30ef7bf685c5c277ec412b2bc7cbed591faff7f2f25bb108e3798d471d56f7bd0174a3547e528edae91539ab554a2589923e12896e9b9494da96947e94aa72d36ddb2e5d10caab68109af2bca4dddef78fc4d9a6ddeb3bea69946a42fa354910cadbd758c1690ada58e9de1dd5355d4ea7e1b127d70f06a1bcbce172b49955f286c32a19232f679fdaa586da3629376be526515d63bb3f35787df9d2b56947e958b94dab9444ea48240df58d20ac79ca77379577247de43e6e1fb54b92c8899ba8b5ccb6d25a679eb4ca5aa5a42975a56d6920f31509c7f09591346122b7cc04a620ca27f2c5b39eb66d9dbdeeceec49798393f40bcad5cf761a48ca63f70ebbf7135108ca9ef7bdebba9a5129dbe955bc9951f2a69e6ef7baee7e29367a9828411679ec614226cb16a041b25c91c864b9e2b1ca6479c3410cc9b2f71e1d116d22cba31492650fa7e68bf9a20832e4adb79b226354fdea1004e17ceaeb6ee813aa644a730a2ae571a8141e4259d5e9616226cb16e8214290ccfbbc7b9ff76d624ef579af5ee76db9bbf875176d7791d45d9c9d27b3d75dafde4687caf4882c7b41b2ec611eb4892c57dccd17949b2fe88b08a751f65e2fb5088424ede4fba6380269052d08821ab88120c8811d4802bd9dd2f800ee892984b23de98927c832eaab5dd35fcd494e0446ae4956d3b47a8febd7e91b67f6480df260ef1d4541ae6b3a3c92c6ee1faefde272d47bcfd161ae5f5d6596ef3ea232edeeb8bd3bc76d9ce7d9ce721749e2e42e6a9cf7a1be24f9bb57bd2704e50f9f74ef8eddbbb156dcc28147513944d1f0eae58b57e54485e1cb6f67486ed4ce6d751bbb2992c4592f76e536cd6ada36334ed77499dec8dc8dd12f8ac48cb6f16e63348c3e00fd224128bf7d0c0037031a35e3f48bfe1b12ca2c49b29b325ae66d9f57ef0714ca7f4884fd89236fba4cef955cbc5e5920d98f64c5c7e4b38277b252a9b2502243690c60bddf08c2af5ff49f0c2c48431b2b932c650caa6b343cd210a4c1611219f24f401eb4734775b59b9decbaa3b734f6cc20739ffd95c60764ae4a8ba524f29090b7f3c8d6a35fbd0300e1388be650d7348d426e59841f24ad849e6adbb66994c7da6ddb364b5314281c2513af7e1494fd9af790084b9d8ae52762cd8ae9acb71efefd1dbaa6e5fd1b5d03bebf8383dae0f75fd4e6e51deed0f103b5993e541afd3072bf7be3a418c8db1094dacc9f1f9feeee228741a77ae9d2ed9b45286dc1f4a84a2b95542452a95422354855a592aa6a2f6da592aa542a757705fe944a5ba9d472bc021a8654a3210d29162f11dc4b88eccbfd318d1a87becbfde1f3d80b9e4a1a261b877132d1726a56d94fd5752acba2512bcda5db96eb3a0f052720d15973f0a027241278b0fb3cd249a20ee9da49a42964ef38fdbd20944e3421244b23f880499ef92bddce5dd55eb56dab75d32aa87d1e49b35d0776dd96fb75eb93cb83e64d9323c7793d39b4cf6c7534b07655ab57d4aaa6d52b769f47aa55d3aad56eafa6552aa5b59b3644873f1384a36422a790b58714941d88d32cfb1da13f2e18679e3f644cdfef1e8d54671e275b10e1eaf7b64b3f8348799eb76db57a1e49ab5eb5b5da59e47d42c22e4b23283145b63c54cc83a76cd66f5766cf9492bf0473c2ef08cf7e576bd93ed6a56fcd23d4660e35ac7f84867684a7359155b6236a0d94cf439921ebdee94b1d9143049bec7784046b70474830758404797cf73b4286df77bbd7e85cea5a60ca31792c50497d29efbbab6b45ab3b93fc9c6a964d2cb10410e50b6db882d317a65427a54fbc4460ceb20283e7d1d0513e72783299452a50a13eefe4c377c65eee34921476fa5290f2a56c263c8b82a43e930a97651184a0842f047366d18702f0f354282748f15652600ec59f0950df2c1c259313c86366d43d166f8804d2e85c7aca1bf2b2279a6aa062aa81297bab548d9989e0ab505680923d568d99535f01b33482124680d96bd994b380262184276938c95ecb5aab699ad5aadcb494dcf7569fa665698424c8a065efdf77e93dfc7df72b41a8f216d8c4964b5b96465062894cefb584f45c064105482b7850f4052d88600745b842ccbb1ce96a77bbddbdb2eb9d27a591275b3c8d6611fd48108e92c938a7acc7374db3b576ef3e8f546bb5b59af21bcbbac685746dbb266e2495b5da46b225cff57df71b41a87d7bcf58bf89b5c493eb58c5cf23558d27771acd9de5b2ea96368df49935d2b67dd3b6edda4f90c7cc1b0b48c3837df6da4aa9942fa521fa059a3aa78cf3cad84978eda76ce66ebbd56e458d07cd9a66edb76dbb325b1e3c59146d80af27da36f32c2addce9e7629d5c083e0a76c6699c53782d016e5ae38547a3cb9d6eee3f7d1fb48ba767bc50ed3e8b2f8891ea6d1659184697459ca187a9a330eb557668d074f773f20a134420b84f238b36022d39049a7200d5945267dca78a83c68ae1775b69a7b56990c05b9c7f6991bb4a1421a6593a9a37b0cd135b4eb7bae200cdeace017768d102dd9023d5c9ee77e04ef45382f9158412431773b0b652134f03322a497357f4799c7968540014fbe79690685d03ea32cc4579237414c59e651803cdf32599e3aa12ccfacd3459ecc9f4fe87932d34b4ae99c3950b0419640289764790f06241560d9af303c814ef10415849ea0420f1e76c143794f50c1678652da8072716ac37de6f053ade6eb9c73861ff785494897daa5446245933cbea8a4993b0ec919a0c8a36a6513be3fb3778984f748a48777b53dbc46b44b50a5615147fbf651c591483d24eddd1d3990bb694f482ffd3bb91dbb6fc7a1241e5be9dd1d7bb246ca51d27ea271d73a12d6ba531b02c81d267df328f7ee9ed7df24753434296348f55da9c45de37ee29d74bb7b57e7a6fba87dfb89764eb4c15d3b77edddd8bd1b35afbb208974120e8274371953a2a55bc1fb5d8fbbdebd91f4ee9bcc1ee91d0ef147bacc23370a31b3a51fb7064fbe8cd52853d29cdbac42735e829388da50230aa3a73164ea275187e9a911b5e919c5630c1999d7eb2a5518d2df843db1a645f246669a637eca3e6579b60d8f9189a58c69220aa3447348de8cdbb557d91da76cf24c590c87c268df1e5bb6627fcac61e5bb638474e631e42f480e0269443994e598c9ea109120db975e81a1a5638817cc91a1a5648817cc93c4a25643acea14c6708b2ccb20b3fce0045162930c3098662b4075e202aa2810d981084580e3f60020c3ab8c209473788f5cc80881a88cca0c9155f10434c460182204d4043185a4044143d4778800a1314410a672882418c5e86dab4345106258840451168f0428c5671d403244c704493229ca11a6d4ea0ef3c73cb2f0da1c421f5d6108e32f9892a14a9e42bfe4a5ed5af092260668a5da8217cc95fa956a71d6892bd6b4de8a0887eacb55efae4066b601264af67d9a327c85e0d92472dd70e34d1852499be5a79c39d622dd7cb4c12a266d2b99b24f338c9a47bda072437a183255bd3ae765d67bb4eebba6e722ca5b167486e22c888bc5dcfbb771e3c9c43bbf71c25ef13acd13a7dcf2b813ae078274780760f8ff45a0e879f2062924958ca18ee27dcb9aea93587942e460468d76868db25a7829034b525e6ff6aafdd1f9e768de0b056b3d6877e4daea8faa0758cf9d77ca85a92830faf19ceabe625678484473bda5f27e3280d390e088a754cfb8ff6c64448ed3d1fba66b4277dfe0712f681fb06e6701a37eb0f735ef3c1877e4d1efcc30480d0d1b50715a2e61f7afe5544d581caf3f44dfff5abe92a07c5f75e66bf5ee65b072adb715692c6699ba6e1b1ded068bb2161f3e748de0d21a7c5e084c24d866e5ee7791c8a237d12364717797eb9527a1da86c6bad987ba2bee46a63b74bd8cb4b9660c5327ce99e2ea09452fa2b64add65a4a29b5f5f338b472b95fbd03a16ce910c251265b99e9436db6f7ed8cda70d7deb74032f348bfecb91b5218134d84a330ee14666fed91235d33729689ae19873092377d7b996c47996c1b9396c8187b9c7e1dc9d6464b51a79b4d3be079edd33540ba66bbc55ca676823c8c764662e1f0a881a37c2d907e59d92fed02467994d761673db7d92bb0c1061b6c70b9603097ebf53aecde0b0683bd6c7085abd7241501bedeefdaf584728313a471733db299b61cdce947ee1f587abd067e822088899cd8a086f5bbc19cef5f88d23ceedf49ffb86e2563a815029b7ac10992ba8e92f33030302f2bd5572ad5bf52a956776119d256f5dfb385c0800457850ee8661b6b6ffd61e403338b98918f568a0a8b42199593d62019d301f9c0932f402a58923b6e3b89646b1821d9388e9b737292da7054debb577a13acf1739d516ad8a3dceb4ffb6fa7ba17d7a0f6dde58e7269e69808714ec7691d50a41d6ef44bbb0e37769043dd3f9c9e600deddbe746d2415a59a5b0a45440f0deeadaab2e77d255fddc2944359938c2121484047a2c5407156a437dfb01c40e1d279d0b4238673348d04c621e994c748d6aa8e4a5dcedab8b4add994410cd3a544f66169abdd50a2671b7ddb96bb7f565755027dd56d463740d09ff74459dd3b99fde934977e5de0c77e857c5a28d946fdf64ae5b9d4196f09a139ad9eb6f47e431b33df74a272d6d97783bcddb0f5d23ea6c67390b1e57df36dc937bb20fdb5b9c70945b2e56af7e3872acbe611ca28dede245bc936beb22ebdba5a08d4da7f5163c8a6fe1f9d3af39a508ebead5e6da7afdfc9936e7f068dd7e75a57cb16e410bd49879f516ce59e12a63587814bf43bf6a5edd823a35af700e118b3a3fdcd8ee6d7766191f4656bf5846ece11e1f639e9ed8c52f0aab97b17a6f037378ccccf238399b6339cd2c230732eb32a7eecd508cd1352ba8a7bc7e46d7b06c5785e1c965db004161f527f533682651cf722713fdaa0782e3429951df778927103c7dfa357f704308e9eaa8a3ee6833cb5757447d05cf2029df54297747bf96e854992caf362052663f7806f976ce08dbd571772ca18d1bc8db5758ca174b95312c7864b9887760c1a2ceb7b3e01bfd9a00fea15ff7f316ec4185393b6d2045153f99926eef9c792a39a48253785455d2e7ec627c5d8e314f3d6289980bfec53c05f8800766318cd3affa1309c7e8573deac40921ec74e8978ad6570ebc49a5ba2a57558caabca854ec94f5b19c658cd18c240100003314002030180c098562c1704026ea19a57e14800e9aae4c68569707421053c818630c0100000000000000401030000141d5d4626a0ee4a36204709c9c402a8a08ea684b10c8a6c11e38a6abc88894e66297c704226937847ad0e6cf22fa1182ae6d7ec85a4bccae04d6fa7a78108000f0afc9352a650ca33bc4370eb3f2e6225baeaaf2c85fa3a8de3e66ae170be7881e1d33ce036f5c637323bbf55f429666ce029908918c6c2cef55ac79702dac8ff91f97ead702d0d187a9a0ac54b00c84c22af07b53e13d48d670d45d5c0db329ea3f342eb4db1faf25e5462c9791a6b5b1537e55217b848f81246344405893bec69f3ca73ef47d405a554662c56456ca97e351e3f7a1496ddfe1fec1ab448bd0c8a0ab16bc63b80f08a4cb7aa9d377b1fb80828a026ba968b7d3fd18daaf08f742e76ae3fdf458ba8689c4d48d46107ab6a5b990810b316aacd57297531c7f13fb9941856831334072d03a9eee4f2924092105299b20eaf94bb99d0eabac6afb11ff3d2e1d9a9fa2eea7a56674fd7d4d983c7ac85acc01fb517be0e688605df1292149ff401262aa8cad1ad568fb7bcc18da1fd764e0b516da01bd75b51840ba4bd9577c20097da6117066ab3b58da6a0e6ffde0fa9f614be13f9e3be0aa4dc44adcb70487f3bd189ab9990ee02e019df4d6fd70cb6eeacd7688e835de740de1bd6de8189b9c9987bceb6dae902a176df2275959760b503903a42b5018862247aa057f2232dc0e623599687eb32551f1cd276a5e0d7e7f95163b737f6179b9908514ff4d5756d70df0671590913a42e7b6d89b0c40d4b8baecc734435b99365dce7b80af74c1d8607957bf9bc4b9f42b1cb6fb3087f9604c969f4e83322751b0c572f6f22762b8b9ee75be327c4dae3b03a2645aeec6f2841cee5f4af2efafd9d2de70efe18c82e847ea60fe150cfb18c6f308837d0fe33c0b8cee9c771510d463cdcffe4e3a0e87c79bfd76f7d091dc8eeeee8cd3c7c016d51a348dc0d2398bb4d92cae9cb068b86c0d0541a668e157631fefcebb1185b282f75e5e8cf6482dd7ba36a272502cd5d254e690d6099b89089250d9b18a96427d43ca18734a4713022dd97f7fda2dcc8915c301b926b1603e9cad4ba25ab4dba4150821a0cfb85bc298f2bc1f1abc719c94c7000fee99f671b195c2cd3826bc9d22bc72c698c230c8cbf70275797c596f7341c16202e6379cdce0171083b34cb7daa6f71d084b2490093bd08840dd44c3500827892b4d9b1476cf9a2c71caa023d08ae85f781e9c7b8141b648f1b193210606cba366cf56f0e4f00a377a87499df1e8d6cbe29af182280d032c9759f4393c896d0f75f5ee4d79477e2c77f5c14e24697e3a3a28c223d2d9151f9f5f3ff92d9da2742caa0c7568b033d76a66ef2ae570e94e4b1abb60dc87ac7e95557420ad49fde6ffa75988ae0b02682bd17633b23d6f2406079cf9cc04c8a0b81350fa744c9a3dd2f829be9089a50ddbf6fa37ecfad02315fbc142b1d6328995e0e693a8d504c07daa0c284433d571ea3c205115fbdb17a3e808a65f19d521eeabc036fa6f97df2516303b0beac446af5f74989cb9abaf1fde42c17c8c3a32a8c3a7c4152e29c1b78bc3fc4dcec2b7400af920b7624fa4081e667254ecef512afd965943b14356f1443bf5e8b6ebfda17b86121f9dcde721b8666493d93a7ef900e972a2af3cedbe9af2c1a3481a3d396fd62a2e30b1e48230d5056739649978cf72229cac610e16d669feda31fac942fb63c6a286cb79690ff3b6bbf410a8b61ec581c8cc61dbcc449aa5622defb25614ab0ff62487f983db91c88d6e1cfd415e6895079cd3fe2b5b8d818a3fa7f402d1adb406603c554cd927879905ca379d531d4eb93201a014822716d85fc7c327f750d99c4f30227dbc5e828a6009645e3551d76adb93a280865dd2a96c8f1b758c6bfd9f44aa30ca7c7dce76dd724c2b14217a3b09685967ac146fa159c8ad38e75243b6a44c8210eafb15b64665f4061a68a80cab5f2380135a181a3db3083600f3109a44751cc4c123a69abc824c1101036b5460c42e6a010a84fd57e53668a56a9f83f78499be77201fec5201aa7a05be05e6007d6dede3c310f8d0decf92484f74cc8bfc9846a5f3879a79971c2f673d45adf43e3416e38302125be73c042ea50f6b4c935a95634217b94ea6666cc9aa24e4dbcf439e7392bb512c48124feac7e9d28eb6ae7545422f172b5cc769834d4c4190506dc28fcfdddcab06db398d1393c0817cdf867fd9ff63a8118399c7a5238ac2692b47b55c3c20f12aabd2dbf929bb7e7a6aede669dbd2a89c7548bf1aa07d2d78e94bf472e9ed3f3ce12ebb4b9515d60f6a0cf2f363465bd97c613f80b1493bc1cdd86496d012031191f53ab1c8cf05a61fba2e5f1053608fda508067c28dfa2719c2c101748fc2efb006d02a839c5bd51b56ec2a0fb55d39e3fa5107b9902a5019398f07fd28e5783b690cdd79662610c57d87479b941cc62de9481d1eab52bc1eaa45459fe65be64f2710ec6c3e244903b0cf96089226409f32c0201715f2848df1c538b84574c7bbf1678091a9fc0d828e15ff583df7a7f113fe8fe42f37e2f1beea18fbdfd13fa5a03c1a6fc661cb87765ccfff01525306b7b11de947c29faef0dac5380ad52509d6731e4faa8729cdedb125c7ab1ef93f10abad15a50130b399a70d2e491d3e5704d27d540586fe8288f6e518524c84c85f5a14e3f2620bcb16ede676490b433f2b8dc0c4f2d45d3229803697744003f59a7b00caf07e6cd965946995b3829e1708a89b42aeed5287d92ad9603eb0f568877b9b6eeb8c7c217f8f4044e443a82eca62b46e237eef0032bfc9865481e98eb86af8166b9ca0ae5b3710f692bd317ca7088682043ca54b2cd3f5f3102626ce24964accf021687775aca6ca9e822d9a5998ec0d1c778449ffa212bb186c3ac1e60d84bde46fcc611d63176189c737e2adbe15bbfc9143b028bb90f4ee6e68648fab595f5db79b9e6579bb7039681bf0f1b1404e9a2f3e474761fde7bb0962afde1d663265ed29d21bc4f53b46ef24ef6e2dc45826edcb529abee9f716cd44c3d1b9664c1d8c42e15b90ff2658ff3600b999d58a952c972d3e600583f8ab340705abf607e8e834228ff5001a9dcc420c8dadcd00e155d4c33cc7f669d6637997d34bacf5347e07825484e3ee833d60391ac355a10685e0b22fa3f07a0ffec3f5308e316efcbff90186b12ba5a4b581cd2028ba2e1a68435cf218eb177fdbfb4f41c32d448c177316710cf22a1cc0451071d6ce1802aac98794c2cc729798f6e0b8b9616124b7c41083aa01fa44c2cc26022ee7f7eb44dcc1d9b0c037568089ef0196acb57595a5985320be6696455aee79bc22265449ac6609dea6eecf404ec02d862c135dc9b5165ab0b70d52a440869c24d1a12c43ad65def1e29c0c19ee44007fe4fb5102220b7365647a02c15009187b4a2e61b22a99d922b867b5e12508b74e71e28f80d3123ab2805a6769d2c0e88310901f87c000f680ce8f71b067848bed7925af9f5462e7a1895f06b56796821a89516d0a005e35762f731bf162a4269aac8b3d0bc5df10106f8e6b947d25cd4ea2391bc274940f98b441b6fd4feb4eb361c0ecf2e0e2d60fc9243e3a93a6b3a25c8e90d2dbf1b5adb1a962655bb492c48a9d8bc9069c5179d4f11148a28a393a3abedc86a2a8922a5db4ad069b0d4db3ccb4290355151f71fff4f3217ebd87acc7434702c12de57a4931386c00fd647875a93a60ed3bd2e9cd56a7f6af9050f2a303c1232be6664192ff3004e9c98b11b2c65a8b1df69ab81009eb947aaec7e0e9b0626163117c5e9ebe0478652b5f7e5fc1ea1a79f81e20bcf4a6b4357cb07076ac79f6f98571333c83cae38dcff0a6527c7c1970652874eb399c3b9c6476c440122e1d6c178eaa52ac34ff787700435dee30334afd2c8827a3dc2a16739bc9e7179b952d8be2108744a6ee623899905cdddfbb59478611c0df316a80d3d9ec2cc3da4d809ec08c14e683c5f866a826248fa20038b3198a5214a7154218b977cac3b91a694b197d7addad1515cb409d36995ab2921de4cdb1f05789d832d11aef1d6b064980e9115cf90c15aeabc2d24f05a1c08f23068970da06a8f8b556b9e260bd707413d56ebc2c0639d25519d9dccd739604a282bd86526e4f0c54dbfe3be195c4a8caf82f8a3ef98e3fa17ed98a6f08e6a6908cc95c3f66270ae0523d0ee45983bddcae8f8dff0a5e8d1e52bd50b30db9e9326c3e6106ffb045251da2d0ad0a97ef7e130cde166504485a5dc0a866f1af858e5b9e9fdac025ac91ca169dae995aae69869eb9e25a1071db1ebbed36e97ea96da66946c6285a1cce90ad851a25ef8afec83e11007278935764a51a93fd99c05dda6c93b4e83ce8cf42e2634aa7a2c70546d0b17ab2bcf6fd1efff4843f687bee494ad384cfbc16e1ae5b1deb9dcf2f2116ef9aba7bc4b5a8a128d236fca3a45e248b8bd637d6f00848198dd827ffdf2b7ef9b18ac28f2c7b4d9e919b4eb1f17c4628872e6e80572881d3de5afc6b15f03837fb979d222124207dc942a0a152f6d2d39044ffe42733d38725ea50db04489f1baa44204b90bcfd13725a20600fd522daafd16b1d3e9080015c3e41e34ef10d98521df35be98107b8292a9339b0bb9578086467199f55e9e4db7fc0837c9f7e32b7e814319b07044f7514dd170a31d7bca27a6b07b896ee64313460592e16f4e40fd1ecc3394a8ddc03f04d521e32af5c0e39f54e0a6c31ad22e0aa52b190546d9ce1055cbb6fd2c0758fabf07a513205a21401f0d80950ccb52dad80bed59469a1fc0e22096948246ca048c0cd53e2cf9a28b4c021eba74b243344898d0859d33832b0023138db3065c4f177bd7ebb2f34ff9324789dfee363df47c8ec0ff078783cd68d69f47b5025a075d6f91a3bcc4ba63f2d8f5a65cc05e91aa4e6a2bbf0b52c8322916ba25212c635967eb984246c45209ac2cba4cd487a56023813798dedb35fd1f34fcd24bc6c6e25a250d4b06aea5e1878e82c9b6f2c090dd73477d431f3e9f595426b44e2f14d9612a19f12638afde2130cdfb4da2a975ea25ca3b72b9accb16bc95d1770e3cf623f423e5b182c2c60a54bf5acf47890c867b19ae78aa66e65e514a429de19273e520a2eb8eb9b03e4b54df04a5a67cd8a42f287ebb9474bd69359ac381f04894069ba6b9dbff23ae4f45690cc01ee53e3c7c37c9b34cac88786907de68053f91f8dfc1fdcdf6576fcb11177d5c826624b183b4fe26ccb90368776e099a174cf005141f188d380ca7607aad618adb340c520ad82bc451f8231a575afc1576ac3aecb355a0d667efbc9bdeebddc26d1d20226f538af4e80cabdc8a5a6b61b5a684fff1c356ad0285d38be1eb47fbc36cc0e170418a8ec91ce70fcc34d2bb610ff8940797a49619c6db7d497ac342b452d8e964ad3a4720cdf59fa3e197bd7045c09b425fde1565af38f55cfa9474c5cd4b04e81b47ffd0a75a282a4d6902587ca7e2f8236ffad0b8818759cbca180f45028662bcefef73c493af70cc22e2605b8f629f4b4265e001ce907c0dbec65effeb8c603d4cece2f18854c40a296725d5bee24b15029b74f87444fc7172b927aff37b30658c9378694955bfec4b24c1a4c15a1a4639701825c607875d457ea1f153084ab9e22d803fa7ee28589b07786fefff48a7586b1b850bddcf86cf4b9ebf76baad719ed548be452b36526cece7e8568fdd75b09c805a86ef2a5c05b45f85d3a370fa3cddaaa91651bc9c92c70f63a74946ba23e4fb61ad44e0c66c4f9c204c336070051184e2aa572f8463bab5ab64b7cfe3d52827515275b40fcb7c782d76a47fe31f1002e85a86169725e399a1a341f53713d2ea96dd52deea0fa82617457017afe6aaea5fbe6ef1a59d9770d981fe389653d45efca23888d0d27a081378154eebbb2182a81c9be750691201034dd774596d8e3539f7c7cd77d910e013feae430fb15ae4f48f006c8b0c077f5a31bd546172bab2dc085d79cd6a48ec0abbf758d89139488c2b292b915c90b7cd76d804c5506762cb5ba5fdfdeeb0cae9ea212800fe2d4b1d1929733f9c05b6bf04eaff0fac7de7db69ee9e1a2b607eb8dc00d2f01164ac5c8e110806df767db610cdc19904708af750472db4f32587b4ed3cf4457397ed41cdce1175edd1fa89c722fb8a8bc4a3276602da25603c69c462b3ef245fd781c08afbeffab8892bf654a70e0ef6e6eeb0e5b4d4601ca623ea457c9b1d2edd4b3effc1f709172c0efdfb3611ca766c86f43907d83988309b103016ac1c190e08f0054054bec5b62363329cae35e9f23da1423e76ee4c70a9539818d4923a5af04eb01bb42c1794ab994f7839aaae6666a2a39c5950015f2e1fa4a2244106d67a8c3764d1318f07d050aa365e080b032d74452974babe4c4c75d0aca1c25fa7ef946ea11e211220806a90484b3c8f3a4474ed20fe35d502f274eb3e517b1ed27a3f3d83f1744a51362c16c51705549e476aabf19b61602107b3d7f3d00afeb15471373ef047f4821c2bf9a255e2d9d46569a52cf4ef72d1398c6a204896a866e1827ceddd4d94777f9c8d6a71c5b1e212c14558b0f0241a4a40038c0ce039e7a886d2dc3c8d1ef01341beea679f24e84dfc76022a22e76b1f11dc170e65947822ac7496afccb3f03854d80c7ed0516aab7ce919fd5648dfc5367ae3d333ab45b737f5039f8bad2d5f01f8e82cfe4bb43b97996f9f01779f36fd472ec23936fda94c8d86535d72434f0c769f352f03984744bef12e06a1378c2484583963640745b50497b254df410b218ef95b8b56a192754dda62c91a5c90a980e9640ed69065ed3b8bc2fd26a062060511955eb056e9d3366974be9dbc77fa1b5c18cef0e8237b0c7db224b98ff25c8c05e4a77dde5650d0d9facd4efd76c7d370e18f232a09695083a35145c4dfb1b21b19f788dca559640af369c76ffac09bf3572ea08a67e4724900612e607be1be4b3eaeca4122b7b415b78b77872fa053c1964b0da2b4aad4001386d00047d56d1da289cd22e5269e68881527bf8534631b07c22d39799e21128779706c811b8d0e22cb0dc37ab5a3549037dbd9480b83114c8192b9535ad3a08caef694a213c187d9f7178bda7e24317e28932eff1504b8c894a6dee1e553b8922e281be817f50e5f6c7131bbb1f78870f1ec3b28906ae3e35789241d2a90c0b0f13b7f3ea2f1b9a1a22032ca8fc9c749b812b4636567b8f6c2a1f6feb2f734c07d83e9e6ebc32b04e0d3926752313be54f22441b584d2529650d3668781145fd57f46935246283fe8a87a5549db3f485c5374ab23af6d4952ceddec6525cd099110f53267e127ed0bb4ca22d523fe8d3a967f43111adf208ad2ff0596e801d58875115413347a8bbff7ba7ddac408c207e020bbf19f8b2a9c1acc1dca308735d54f36aa2eec9f9798dc7b896f0d6da1ff800aa0eebacdd8d20a8d3ebc2dcdbe7f3686c51caa3aa900ab778d617cc68b283cde967c99b720cbaf04fdd519c5bf04c655f77bb81a51d30ed6afe9c5fc9a23bf24c28f1e9c3edc96521a9ff46b429c5bf3d7e206af5fbf83af342fb31bcb25afb929a2239236de120c20402bf383a3b1442d5145e22cc0e4df2fac2ff86f56f79c1e01df83589c79f230cdd48778535a82650ea740fa4c1151e82bf9824054a4efc9821cb6c6d4f9a451ec80c1f034035859ba24d96a46287df286efa1f1c78dd79df1ac91535c5551f8bc616574818cfaf17e7d60e1dfbf468ab3dc03a1640cdc6039063df28105965335d91a3ae274ed3f0832664cde2157549c1c89f3a3364294a6d6ff365a8861618e4ad5c480028590f42265af3e25bc07524b6fc673e4fc52a0bbd989681c028c5ac4e41e6fe9c0499adc2dba31e2bb71ceda3c2948f487610a2b247e450c961c47b00514eae52b74698df1d4de205ce23c901442df206c6cf6dfa023076bb431f4d04406f0136b5424dfce125c6f90e12bbeb57c0cae326569dc35e016870311f393023854cf690c28cf883610bba3bce845c524c4b0afeace44ef92365765011080c61882038d48c36ed03863fe7380017269f5609146ab58671fa0728825a0bd64663e08fa5cc45a827ee818666394aa4e84286f8209bb30bb576a8ba80995bedc9a2ea1fce18b7ea5a575c602710acd73603cf0e3cd7e936b02f09c82e04f017444e7a18c464a01f3118ae6941a2aca80edfbb090aba4ce757b1d4651e9aad50880f069ec0c2a8b0c433753f58b7e35e2caa00fab61e5ca248db8d4ce65ade70c1618fe697b304b62bac48ad8a90bf02141576fc58c3e335c7492ede71c2aef830dac92e2f07ee623b88cc5a73f0cdc9ecbad815d35f55dee926bdca2c73c32695cd1635daff5342c5acad01360e89e532b70d0b1529ce82138cc726b776a7c98a43697bdd32d5ee9293754462fb225fd54fa650ca7f2dc095338bf7031e2bf32de99d07b6dcbbc819e06b4a58348a48d4cef9104161124e66c98915111a1d461806ea1e73216ab2853fe43a731c0752926a6009482bd5eb3ef0f7c5ece57aba3e36e0417f0df1ee0af33905f37135c8e2ba27cb46e452d376404db39bcf4b17ba0a044df2eaad95462bd53a8a85f8d49dad68c827ead08473a2b2b0d071dce45e120d3c3aeccef888a4f79f1ce37de7394e0db5798c7b8a418dca3bd77b2d0c02aef27446ea0a9b616705b2b68d86d03967ca3f057c06d3cc1166637773cb8f52f77de8cbe0e16618570f0b84cb5ff8b53a69d315bec5494b076a3565599ec8ee654c9c127986ce41900d134cb4f6c98a2409d8e37f8d40c93d7073bf027fe7d1cb35946800050e64befe3bd69ebbb356294320035e61504d098eedb67e8059e910589a41facc640a47bf8b4af847794282bb50ad1b44593d2f2393e7c542d79566c63f6cdec91c48d0f358748c64f9229e492bad719b2d89262998f2611218c66eac6f07b6178d1e553d80fd83f54a5516bf4cc99e2f1df9a7c940506af1ca21f11239daabe07aa85d15f99243b6be9dd39a7e013a22e08355a740a80bf5eea94bac2811c5281f9aa1d352fd1911ef0a2ad4b88a81775c338cc466d49e3b4807536092f4bed3da15df571ef5199d5297eb29c920d3083d348f7a7a8f5d860a3ef65e3e0cb4d75f852652502db48002e7885d4f64258d1512d5b149c759624d270aa61a590ff489a25df62f8522dc2a66ace42a3dbbb2cbefc9571099e76268928ffe1372f7bd30d666fdde4184a0e705ba2abc37520c4f2dba9bf5425e8df76c9c934d24bbd2b88c09d6172b6ba8cfb3d1dce18d30a6bc80fbeba0313f9554d5f095699e05d6c51caf17498a0567d8be1a5e212e6527115bdd43ad35d14a0b95e2f3ae9c7827db2f67bd25b07d81db7fcb682b173ec7c2af80a1ef433bb6a62d28999df85cb78c888403b44cab1fbdcd86c302831e4b948aeec5cceb006176209a14061ca2a335ad9465f1f849aced52f9702dc102321fa12605736b71cf03ef8412476c5d9a1b0b392606502f90a90e6147520b2b6b62b64ca110a310443481a8e7b27498718c0af9a086021ae5c8911e60db36a167d889960d051332296b53da87a1b695cc0c8aa47244a69cba8196fe6261d4179a16ac7646736759c8dc74a9601c5dd561183a3f5623f03606a0aaadf77a9900eeb1065a262a636902fe7d8b2be6a6adf367e8f02a2f927927179f3ee7c4119d1e54c708f10adbd750f110203e82903bbbccb3664df44a07c10915bcf71212ed8298bc1aeb2ee73a07051caa08c93f3342371f28310b0e8d1e0e9033258b2d33d9e85998db0dc1e85d4d09043b1b74c01549f70aac88e4f362d386f62de6de372883e1220e9e68705ca7983582eccad9fe61efa273d3cb70318fd457f985eb3eba7fbb7855214b1217ac1a944306dc47c03c77e1587c79dc2f9bcb2b5da688b030f98171f9601b586466304c0c0713fd6321e2534cdc86f173455cbd7d833638beeae6b101263a8c70a9c58acbccb9e023b0b51a19db491b123da1d753af12a8241a49e41e2a370b12afc288c4a6c70d19ab666b77452fa794107a8019f28b4f027f2d2e3f4bcb427d95fc4624b56f1862920793495bd74e00f829f09cdc2fbb84166b0cd47d2fae5d6d1c7344975d3077a21b5d1a9986567d560666f1963b406190effff6fc1f60bec7dcc9abc7fc2341304e4a16cdd9bb31fb9abc32d0c4dd7cccb8b8525162adb3dc14472b85ba71ed42226e9b591f3215624bdab00b2909b6c2dde398f64ae44c124029f02a6cda73dfbb87e72be9719bf6fce597e59fa3817c16106a0edce6c4e35603b7d824a61ced593925bf76d84d8bd83d3e898ab04e50b49a9f0f4eeb607c0eda0974134606a7c41ee965681fe878d0c237ae2cbf67e9b3e9cfbb1754404d035ed7aa0eb978a74f0f5160bac517a910818e9573a89533aa76fdad11636440735f89b11487b6e3e52c526c8c262372500f0ea788e274de37f5522adda793e919365d23c30da96eb3fd4a34ee7c196926323cf66f760697a4764dfb93116acd9d512b7ab6855ec990fa1d0cddbc75437950387ea49da97bd0d92d4490291519b9f76b8adc3e8ab206e6bc4e88b1eda76224d4aa84ca511eedd92c7119517310743f3308f1b6d5c3aa408116ff9da796410bf67844dd460fa9db758dddf3c87ec001881268a76d42727a1559754417eeeb3b660dbfd093d91a7cd68b057c364b351539267674c9c0cfc999dedb0d30126871a9dbf884a929d446c44af56487a3a3a7e1191b5fb22dafa3c9490c18fb65ba604a038ab8f7a0854ed4889ccd7d9b21b7015ef4564167648dde4d4478469668ad3c0dd1ec2bcfcc9aed37be4d7ba133230c7a842fcbfacb779cd91333c2471150c55ca0c3a80f06b192b6741495ec6a197017c8b1157826af05ab567f137468975c7286d5c63414a37b6039bd2c879ea8cd9d6e4ae28c5740ba94e7d06589d80f332d1d7c6f6c34a8b397750e862cbffdc5fb0736f38596ea860d0c42fffd88a8021232282b9041968b93f7c00d7e991865d83d67572d0bb785f51eb56d915502442dfd1383339fa9cf7b47f4b83c69f122957e3cae75db232698ac3994e630ac07d63e3fb41a7ceb62291917f12f7e323ddc65a459a8b601823e46693b9663c750283fd88e0e1378b598b61b43f94a863e29d73e0f15c9b2d02cfe10d2d0bbc99690699d46f88b50d99a16e431e69c7003b0c3e759195de7d6b65981cce7e09cd34abb72aeea934f9dfeb1c52b519a899caf81d728a279f0c84bbd8133aff39da2c5d55673c554c3d7bdef3ebe4617a858c196265755bd99013ad9fea806816fbc3d1d12a2786e8c24d2e3fad7c1155ca1b69cd57a0f9c414391d3c388a6a93db7272da0400e328edf7ac3b7ccb487bd324f57f6e616bea50941a6bdb7290c048c711f7b6dcd89682b15691dd9ef86d25ad970282cf77732d245940b07b69580b0044e318d76df93bc9f6909366c619e2064dc501a4d49da6cad9b0f88dbdb4f2eb2faa214df3cdfed3e71d01b7bbc42887126afaa98aea38cbd0458124f5544715572b1205c2f3e559ed4ba57cf4636ec5ddcc0ff0187010bd68a1b62caaf6d64292a6504824d32d0048eadbfbc5c00f9004e7261129a7a12569b707c8bc063be958c8d882bec5aee1ef0e7768b12861c2d69a83ba5a09f683bd7cb9a46156c6b304be0640eb86039d717cb9c680f74b7324759685811b8d1189c041693a92864d28e61943520d7c206d7d42c70a49c848d09761b14ac041ec740e62100502211addf2f18310fb07d7a8892d8669ae6105c8b57fa0685de0a6333d52e99b8437886affd33834a1caa408a6f7f4b3936e6f92832c421a50f7a3d7b781919ebcc76532d5a39af4cb342572e0b9cf137660dbcd63933a1e32cd8e3335e4f9cf76b36d83f0d60d0e0262d8141d8e4b55c178a694ea408d77ffd73af1a04253416425d6b0d7ed3eb3c99dad6a03942b28461ec621d87eea5eaba0e309c71c3b9b070e7c8793790fab5d1804098ed5229d29cc3d7eff1ee0bc03ef08c1ea6b57546fc08e58c2913f5db3f1d2043449c816dae02061c05f9e8caabac56ede9e69aa5ce5b49511f512c49d1b5c261af822c7e38dc9e011522c4b80726ad96d27d68708af11d4c2d3209145385726bc65dcf5b8ae98ab2a58acf24f1ea1a41ea0f242c223218db0bc704fb01da5c7ccb85090c6457c959cecb3be0880d103f3d17ff52f0d16ca9bad4c459e47a90c96a144d9dc46941c04ad4129acea201d2925327e64bd36f882f3e81ea1ec3c4a6be124942853b23b3cd3d0588a6b3807fe1c8bca19dce86af420f8bf41d989f296b718617da5f7b4f083d223a7c0ec8bfcb4c148ddac23345030e4eb62c93a5cc6d96e8fb0718d9164afe3df9621b62e5927945a65a1ff2f058741a211f79d85b8a3146ac7646c7fc03a790babf7a151cf21368a78e51340f9ab3973118c154b71854f4a71838643055f85628f0cea9924cc88b39ae233c944f1b67732722ec2d3365d1e78a76e119e0b60dd70442c7290da1f3eee9b133cfa903540090448ef5d1d37ab4c3fce4502b7fe4a818ce8f16fa00e45a37c7c18a4fbdc7493395239fb1e6269320cfe24b3b8e51e1cc209f7be58d8a28628d85a8dd720aa019172079adf9f970bcb4c3d05a452d4e4f2d3345ba86ce021944b5d8f1d74bd89dc77175e41e549e7df05a2de149c9305c8f462c90ccf769070961e0edaf3e5d2715ba700a145e11b0d5dc4a1ca010a90a0c12fbe059c503759e873bf48ebd922ab3d948552397c43f96a035ab6ac0d74d6a0395b1c201dcfc9c0789d48d3e89a488d478dde8d4e1165e7b8a39548356f57df26b47496f4266cf287993016e0c873f0da6baeb9ec97f38d820b155a67d5377dea78a07e759a6463156f413d2eb8b891b85d7a5c20864ac1cdb6d9edd3bcf5676b4b0891a7c505bd37dc845be6062bb7d9ad4a23a61f5cb405fa0f81182f6e41784c0c1a863b96d43d3c57e870be19a0cc9505d5814613b2bce06c342ada0ddc5ea2bd1fc117ad16df202ea28842a650b5927d3453295b82e50795f49a8dd23445dbea5d41224c14d5515e5c65020e08c0d2045634895860824c900211de2db664b43beb465872cf5300be3618e130ef61407a21beda562a2e0d2132889e1ce9f2c53d8ba0cf66a26e283c44cfc17684fa98feff181a6cb3a10ecec416d5f76414e53a8f69a425b502eb9d053378572ec5a3c77684c7eb4b064865a80115cb3548ae8896da57178a2bc3a3e55317c7357f86c97c47b30fdbf023b8968899202da11282a7aa003f78f30e61b46b7ad712b19ce74723b4dec88b1c1018b27e824ef3b1260ad73b07950e58f9920e59352cb704d37fbe542b092a0d67c670f1f93defe2e68e469818cb81e4fbb3dc3fcadd9900d60fd0c62009de7f597fc310e8624b47b9012a7c4ed02f593f555e8e2623f3066bd9e289b82c97059c312d8ee3b0ac110f81a333b6db08859ed13890a262ec828493337e30be6ce68dbc7ad955c4c28985190ca334e75c9ea84081b39803a38118a8002ad9ab3d07ac8d1e597a1c804e32b08592518371650d92478130fd4835355434e6006e956a5cd8cb2ddc90a44395df6247329f38520e363204ce487bc4486acda50af44faa43492acc435f076a11899f7a470c22553fd7284463b7630ebc59006e2cc34c808f95e254c09652fe7a564596f3f58e37eee669cc281b8163e4f71e35c54ec17e3401e58bd18f85db0c8c8b0d41909bad3999093ea1dc69be8ef02663067493c6b54baaf5b7bde1c9d27dbe997d4b136522b939dea4e3e9600f748230f9b3919237f67f0c2731e6a218041dfa8bb346e264d6c8d3cf461b80b59ea8961eb3acc16a351359d467d9ca2f6b66b5a328d36043776dd7908e0e7f41e20dbd39983bb81f31f4036d611079194205c1089bc0d94ad45c06fdbaaefa24935d9024d07f16c82c8eae87831a5f15e28c662f8e71254d295ba4bb0250c473428e8913e75e344062aca90ddfd9ec8d323074d24e827da9796eeeab82ee3f5a29e50a3165d93f444168c29f33b29f8aadad02a508fe634f42a366b9365f7750f1fce629bed02b49790b7cb3e46379a575a2d21fd536bd31a9ae723cc57fcb81f7bdfbce8229add5bd5457b09478818b5a89612e33aae89ce8afb44a241d47e9c68395ee5b05fd2d14508695b53bde9c2c2ba05a15d497c9c77cf2658c977b134f14672254f3d1956046c7a33f6a065d637d31b872ae1a9e7b1b1250280af5a7e4bda5b46bbbae1985eaec6e0a708d0713ffa8e6986eec8d6119670a1a8f6e2c50f889edfa629df290f38833860fcc3c3a99730c55f4d29fcb1a65c53608e83a64f4e940d5606231c9125be576141ab8017e3b3c3fef3bf6d88ef6ab15b210a013912064c74811090e06b7026f6a79c262d1136b0efc6c7c396e5a535373a877b4594efbfc8f6ddd8e0336382209bcda93f7aff04363476d3320fa9b13209193da8a8b1cd5f2c084cb8a094f9c7af9ccd6a5eb804a8a21de70fa77b45613cb55832215aa8517251380b440564490b552d5c3e9f3b04690ffcbf686afd6f4ead325849e041fb1f8cc3794767e2ea15c978f4fa8c8ecf9b9b7022a9c3a21614c6a705a2d948ca0f4a2276f85bde738cb51b8e92d3431b9d790f8589d5d1282538b4299a947042f80de7ab462207aee58f2e20b78c402637c49b3a83b0ecdb2406fd0597549900faf1595caf2ec9028d5702e36419304ebc09bcbec1c8c0b9482c3a142bf92ff0cb09efd270f382673653ddaf4ce52af638c5c287eacb1d1be5e00b1e9dd54aa0f0119ebd9f77e60c888383912d38582ce12bd750da6fd34612a9bab731fdce9d2b55854ee736e39bfd0319e62f9950277ab548df58334faac984d085a90a2dfc44384478e524f9711e1922acbc885116b92c6c7d34c0adee2600ecba866ac1b15114323e4e0dbf4d594bd2a4444c3d696fd96ea9887b630d2f97ffc6de553ae3b02fce6cc0101c3d0d7bab0c2f780f6287ec96927f130c5145323ebf3ce9d3631d5003dcfc94b95526ce7f131af5ad6462413938fd29b87f320c574d9a3bc9494cd9c177ca939ab1f35dabbbc47ce833608c205ec54fe7ab315f662b1c619f5d879d8d2bac892b3660c4af1b4494363406040db4816ca2494b446e8c857943308fc8602d8febf2ca93b6b44d477013c7dcf4e458f93fa06c90fc967ecf0da62aacc0b92261e6214727e0b8ee8fc892c55ad19fedbf5e220bbf7c9f5716c6625dde2b10ede948030bc40a4a687610bea0366428815a37f104e898860572753652fa86afd979bdc9be18843a12e9ba3fdc93300548c41de2300f2b810346bc3fc615b06135d71204dfd1a48d5cf8adf8667fc71db6faa459e8f9398bc5bba5b1fb3f1435f289a89fc5a975f1774e213573f81956e324412b598390ebd9c3854090d9f352445fbab1d6adbdc8f6271c4eb1016a313f621e0db3f9f7c016874678d0183349f31c22d7033b705ad6bdb02c1d71604a6bf9486546be343adeb6db22f5fc3294394b84722dba65f4d9f3bc76a847317aa540a17db467a662816022d310e9b0020dbe61a3180600dbf73b6c14fd243fa76aa485f5e9d3be136098721ad13668d04abcea1696cbc3f5d1934a3ea4c079086b356d92239c478675202c4201a99a29242ee11ed67ae2b7adad830f512255b76f358dd3094f1f7b5f27aafa2589f14a500d7de1480628d6084c3d42de382e0c8f62f6bbdfb269ef9984336da14a16fd5ac79d30cf610b08445c00467b21c2c7fe9ab0f00523bb0f19f0b1a293dc589a00bc06874c7fee39144c476902044d2f578fa417a1c7661195677353ad773ac441c7e9fa3faa6a42fa6992423bbb4704ead67dadf9fa1b2d67cd5c0f668df3f677e46e9a36dec28ca7ee582640f401788c0802d1e5ce3c83eb04766a1f651df0f892c9902ff29831e85771148f3c86ce7bef1ea00cbe292b3156f324549df1fa9112437ca8d91959a8c5ce22ad6e16914cb2be056f86122a4cf235d20b966debbbc6108c03cd61c185448622ec9963d0822194936c0d628501621fe7e0f558d7d34492b5ed37c82d0649a787ab4f8a162419cd23249921b57171a11c06d13979cca4dd949dac5695345156af9e1b23d962759cc6ff8f81acae245410b784b19a7ca10242088d13d6c6902cf560ecb61588a63bf6d52d3b86b18bc5f985f5e4053b234173a184dbc0eac22946ebe85f2c04609a7a7e82d4c297ed7a8583ae2e5fe260762a2af4a1d9695bc1e9ff6394129e9629431691fce5ee634a28e307ac8983802050410698262a5ccbed3f163850567512bad4679193e4d211f06320f4c588ac1f99ac37c6c315404837c7c128ad00114bf50afaf1f8c3df9cdcb2eb977b71b8f644936a98881527a2e044d02cad5e8fed8c36fa11356e270783e02e194b8d0b82e1d7946c1957610050b85518492b6c68d2d5094064b8a6a454ae7cd8d8aae831cc3507cc66662379417901e1a32fe40879efc45e67bbc809029f3bd1a4fb4d254f0a8f1f6dbee554ae1e6d6c2cae382d2a52818e90c0d5c14570f92542faa1be659573f171854bcedfc8d933fd04654dbc2a84e49901cfa7c2d50c4f5c5b4b878e0894dd7d42d98857f6e9fa8d47c9e32d4b499de1b85939a7a0e3d74bda69b9c399909835a4800efa7c4db3101c1cb644016489be3bc39b493c0287e9dcd033ef1ad364a5f79ac3c6d185d14c7e31b00ca4852fb91847190c1400c590f182806d663bad2a191fa8e02f680275091d0922786f4bd93eb591b8b1797c22b3f732fb1694ed0b01b3bc3b45da1c179f690977ae53de775575f395f186dd4eee01df4b348c0694ebb7947e2840125cca14a61db4c0181457181404cea78f44150ee446a129267e160e9c9ad20df620b5544ad2714bfc4f63d59d093d137477910de9a33acabae093143e206c2fafe391c0e04a8cdc94ea3a0190c1d75a6699157fceb979dd7e674349e3e044728c81ede410ca3a30286a1b82ffb1a0b05a71b76f8917935c60c5047b08467945b92df335d2d717abac48d062abfc73d634a0af44f5a8195dd8960c7b09d7f2bb89a9c694da2e5c05d9ff8ea5a63a4179bb514b738b8d0c8e3c93c4db0e8fe9ae8b2cee3dc9d9c849d4b0bb67dc4675553296cffe92ced7e3b0e219a08745459b691180c4bddfe6ec3e10c206c43fd9b1cde96877309fa0f545214f8208789d42c50c2e68da8284750a2e32345c66215136f83c9ac70a0496a6ffa04451b9a1aef8b5e59169a66d01f93b4edf7a0c32320af20cdfc48cd299aa02b3ed15cfe0e78e1e9dcb5a8a67200d4803cd1d103433c1c8ef4f9e54712691d06a0439f08de1d9c1606718671c2d1d4b2bf68ff81ecc50cd884549a507236406c7b38fb9acc571fd8f5e4406312361254e9883e5a77ea4cfc632cf08c7138d035799685956cd5f5ff8bcf6b3a0847aca760c132bee4482142c010225f3c4833614bb48bb0dea86df8fd2c1bb5ce3c2d827be94e38220ba9dfc62450a49daeb106ba8c869dd163a7552d831c4a2f81e1acec896b533ed1ea252856a5420ea62af40a78484154a8c922564ac2bf35e5b2e555d05095ab8409a06cea4b035387e00dcfa3d8777920f55e1e32a31dea3a3a2a3acac489663fd5f1a8c98c9f529a58fe528c194e273382de06602854f7bec3ee9be03ae987293bb9041d301aab9420c817d2bbc6430ba00acd3aa33452202844389bfbe4018242d638554ef5cba416cc225a1bd441ccd0363a9f73d2ffc1d9a66b312a19b168c66dd23ef787d50b29f33d085903d7afd524e696fdc6bfd70297f8f8b35f6c6a1dd82785dc20816211387020f785a972a263f1257125463c00fd5fd529acb924cf9c894bb1ad30c39e2b22338f97dfe4fa23ef4e06c19266dffa7b006bd6afc1dfeef8803e7307fc73113935e626b9a8e57a5a2dc9ca70111c5e57ca3e20ba3c6fd37472d42f26d8d99f2924b8013d6af0eac5f5aec72f090fccd11c770100242db2293c209e27400325f67b0eb418c34f9ea6c46c244ce0c91cf0f878ef5ff7e06d2c6c748cf49644b0dbf5745b4010739fb0039b87eb976129311b2cb7caf9db5544deb48fbb00d9c2cb883917844502ac3839b413e13f8066aaa673a4df657f9056c37ce0a84173144ef1e26bd3c0fdf75a84200ce3471f49475be006d303c5445d7812f43c319d32faa12bc74e2afe351d95bb9f74849caec53d71e43a481452fd870e6380d1a9a5010bc30ad16d451798f2e29e7f5668f64d00632f393662c08b6ba1f8bfb90f2fbdb440603c10a26c2975f7bca3b967610ea39ab15f9ff3a240410ead9b75298ad1d50bcf743459510b52f0ef05284828d193ea58d4f2a12b4c379e5ab396eb15d55331d8c0fd081680d50c5587fa7c717b77f50781bd057df886e5e57b3d73ce1862f82a82cd9d70e5ac6d72011d7caadcf442770b20fd6333734bfa1c3d318bd8ec986ae39f07face04a3fe1fce9d8f23b7c71def1a03c231d030c9bbc8b15ca8702ca09ec91867e6f845af80a34ba353fbc930eefd6290d1719075807a2c94c673c1a5e57931e1046c1c29017d2c2481e100428461311ebac12e9541c7fa8468e98885bbb3f6307f87b4b35016070d776c6f58dd5f31fa6836036eae9eeba20320ac552ca3fdf02a593d0fedda9f9d2e1c081d5e281241dfc4a8393121634b13caa9fb247a61beb81d7e6ea6fb541077d1436ec726cd9011b281352cbbc83465f4d7f527b520015f3fad13d2972c32c810537ed3c052a65a5a091abbda3b62d48b8cbee14181ec69913993b70c393b50dfff9158c0184c266a5ab92fcead4531a515be3a3202f1361b8fca5ee11065ba07d84e86b708033a4cc546b3afb84228188a546583aed619dc6e55cd27ac9b2e0209ee0f609c39d08de1169e79e0f01d6329a15464d874339dc90627e85530065051517361551c0c067be4614a059c5227c78dd308d4c54b5ae8ad1702ad2fa649d4818ff1467f946d2c9ced5792f2c2bbfc515b86de16fed95cdfef724fdea6c2433b2076e91750373afe8c2dbaba90ae1cd9d268d39337fdc0fc7bf25bb54c6ceee999c96c710479539605f59423d8d8c9366349126bdf1fef7e9bdef8f4b250cf8644fd37f5639e495114cdf1a5326c5bca962dec9cccd2991718b94a2fdf3e696b17ab479a6b08ba28ec0b17c7e00e2ece265cb3e9316e130701a0494dc90b28118c8aec968165702170976845742c9e919c46216fa80ccc05029d9666742d2fd891a55db6cd4210719b1b8ab4a93d3e308c19d6410d0c46cb6c89ed0e699b35ceaeb65d2f887cf52bf6460628b2480d03b8b4b8a98dee19435ef0f0d24805691458791046c1637fa30612b7a0710d618a1c96382427f18a0568fbc26b9f20752090ae4d7934e64c3b7b71689fddd724bc909f66afac6ff148d9c867c482f9e8cb8513c3dc4935fc08aee34b10cc4b0fabb8af0d6e9bbdec334a8b6930f39e6faafe8e1ae7f12329a34abfda247976571cd337a338a593d6a2cce41f171637b62781e2c3cac783b3bf2b418ebe320856f36784311f1d4e2cca9e725f82d8fa604bc40761f5388cfaf4d03ce8635f2ddb1cfa09f59e0dfb677f442d63b77f4d2e24c8b922f4a8a17dd8d987ddf4819e2f529951706d532632ae91647f6b622e3e6596984d4ac9e1c5b289984377b099d6809e691f863c19e91c45c386c48d3420648b3fd4daf2f7970eb781dc07c53f294f75edd8b6583c69391c3f0e3dff5993ada3262bf8e00ab6f68b26bc13bd45a3692724890db55ef257b6f76a8e1e3d6a322a9392457566a70ff74e031d1551950fe6f168bc3e19423305bcb4776a12e453b9dbb20c112aa6f91ccb38613b685e8010968563e3542b881a867305fb6e914e69c56d554929576f5c040c47f35d7111cac0ae2aaa3a32caacd4115a04be806bfc795ad6f82c83df371c7385c306ac4d04042a0f4e339a7308a8774126f8adc13ef7b11bcfbff40042c2667e8e2bf0a972caed27e525b826efe0a2807c5ac636b2d770935be147e7abf3c52dbb988374ed3d8085e012ce1fb319899e5bdd5f84ac18f45817203c931a019645839932de76079c7847762deb723451a1ec5a82aca776a3c3a87aed622354293b69056364ecd857e3bb3951dac9ad88ad283711034d4ca99c2cf79e78b962409fae8b34999c932039709c7f1c95767c336f86e5d3262b8a57d800a40d5fc095de06998803278ad125880f32b9aa9e5619b1301437c37041f9a766ea70a9ea684fa5853aa9bc0d96d4c5d2498ef118eaaa55fb1ff0392982547b0f98832b327cd531c39e715bcfa10e9a2d748b596978098ebee8f883b66bb3173e2f13a39541ca23d81f1d2734adcd5efce1c40c4be03640ac4a535cc9f7418b8eeba02984433e3925828fcf5bce8ad0ac0a562a204f0f366b5f998cde6b3812663c48a45a696000ae8fbd87981e609a5ed3b1087e71b3d23c69b54f36e55686d9ee5f812186104755ac31b4162bb5e928285f8e1e57c05d30e6705c0e87018dcd610c66cc9a7aa114252599091182bdd37ca686e59761ec31a661a45d129e1b4c2dd887575f579cdd4716986503bfd195268e7d792153c07be6c4c384ec965add4ca2f260bf9d62e5b8c6a104798f47916f0285237bec41827ca1022647e601173c6b848c2efc5e5e6c72a0d2f3c9ee4694669573f6208eb75e140623e93dc89ef7d9ee19f78ed51a4265eb8e83d890fb8900ee795580e28be9aeacd6c16be07b301fea3608d8d612e1fb34261a073ea91a74424539a1b1329528a4e3822baf8aef28542941c80298a3c658312d17780f255d13e538a90d8869e6197e5d7322d586d6bd1bc3dd25b497ae198b92ad4fcd0e1286b0577cc35ffec9927e6b1cc5ee2ca4e5e42c6503f43e672b03013b4d09a01bee7b2209537836d16e15534b643681018f754a47d0561ad094d3979da23d3dcc1983ab27417766e31daf3c5a95534f368221a3802cadefe9650ea077969be6dda27e5e6f8baa1deab498e53522a13e350b2b22a159392a9b4cd3cc936fd16a50862b728eaa5b7f9f0f8a110a36bd8f6d25427ad7292f9a0ef56df16a104ae29536cb67d45951d38c65895a083fbde5543f9bd81b6f38be5953728626cc8605e7b245bb742ab0aafe0ed0a78171e2f5cbeb60bb9f9095397c2079f84aac101658674f7782670f47b1fedf0d9e05487dc7dfe695e1e055a797e267be8fba2c44ae63ea2a2fac54ef3ea2cf250cce77a6687fa41eed85476d4a3d64fda4e92278c000f2cc5a818d058c08ee9a494d64f2fa724ca045726273143835686151000f82affe595f295c6da214649d1030ab50bbc5971d67d69f429c415ef0fda887eea39db6a29a8f6f109624c5ea4bf147ff8195de69e13214783e3486d202e0df4417539e24d908151e2bccc51a425f45a7f86f85fb756a416383adfb139f3e5ae2d6cdbb559dc0229c942d387b403ac748144469259e13dee3d198c1d62cfa645c6fc6b027e2626e8f16f2cbd94566cb7e442a47ff642be3ba41e1b41d16bc7778259acd644a31446ed125c4e61a8deba2d1eb62db03cacdf73d0b566e4c9c0e208c28726e9dadec1d2d47c9365ecdc4867752481d9d9aefd9a51520e3e6f80cf86664d63d5e2aaed4dd9c4484d851f081f2e78804073408503c35e20820ec5a874aa9ef1d66bf2e010dc43bb8df3337f0a5e80ce62a1d80ebca16149dfa5de349409e0e2814060a71fce5561e7380664e25795066f9f6837a91da1087a5db520502f1026e6420b9e5d1a90db8914eb171e4af8cec6769746aa20598e776c3382341d78d5df002552adcd231cf17c488054c3d39c41197d19bd86eb1654d77251ffa9a8c024249f807f0af8a2a7b38cccb0bd07f5ebd6fd3c2ab36c4b9ba963f0be8bbffcdb7feae34b2ee7084f1a64e3d8bd600120d0d0a3301e7289cffd54b1f36e60117e959d30c080eecaecd94f3f9ed822ad4cb64bc2177cbff11e2ea9f4c6dfad55554c7fc3daa024cc64f79db0189a14f4e81a7751e4303f51dbb5454d8159d61928cf853a86a9d35a7b4479052739c751788cc92282e3ada7fbc7b6eaa9cfa4ada6d9bd16a6cca53222824ad0e8127cf34ff8bc373435b5677c23489e3931f291e0cb653d740109581670aa1d54b328d7e08004812cc0dc88b740b7e1984e380d4177a652e5db347e83edfa3cc692712cd6c51e08fd2969f11d76c87a19bcf0331e0035aa91e350cf046f309c6e9d993ecc18efd5b068ac6849bb3fab5a590e65b36f8ae5a96652e0d8f50d65cb41196517ec45a72e9ea9f0b593bc7f0ab1e0b53f363c918e8fec6959e54d0ad78e6df0c0c50fba08416f8cbb2fc5edd45430020bca0a8130b7ff33735c8b9473d5b7c7a41f94bee8942a1345e7761e1e228c2c096121b2e872078283396f38594988db7cf33a7a88b874aac4e41d58efbbad1586971c0b866df5d745e22fc20e2b11713361bfa01fc86b353f754796776088090b7990b60d2f3f6dfa45bc5751bfc29bfec3e49bbc250fa985675a40f2787fe5cabd5c8cce591d1c4dca421a1f0839f7292d4e4230bf9a4a14adc3e807c4e384d891e594678438944b2062fad80b9b98a6c4d5e4dfb6d865a0fd57cc3032d2719fcac1ef41dd2fe28b2d29d3ec052e2e6f80a3af6f941d1b523099c8707ee62989ae88b02c5bac9b5284b1ada62b6556d263eff83c3c82cf30c67dbf6036be1405ca21e559832078efbc32337b4fe0bcf2e080070ce8170bd2118233fd5ef2503a8b03580f6c45fd0d355d033be69aa51b166e6fd22cfb30dd86bf979484818d4e3a638131c4580926c96b3595034122bba238c4e19b64b90e13e46595244bcc323848bbe003e140b0abfc2c967cd0a0c3f31da021bd50222226f100f301856f98eacd997869837dcc21e5c51957f0c1555112d8a2d729a9b7784450507a96940a7dc911befd31be91858feca7ad582de3f0a9fcf08fa4549ffb07867f1620b5513a9511bf1d4534b1a7f8de25415094cf5038881109a9aac10fc29f3b071091f29a9a12a18be43358aed72de47f43b16bba5b095a43f04f8624dcd2d5782b19afdcef32679f7cd55bc486690353661f304b1dca0fc681ecc90ad284dd35eae1ba46e19e9a7ccb68ecb4e16dccdd04d7884a3f0aa2a8a736264e153efb44735db6d1bc05e8705e5f3c135485e838c58e37c568ad49970e0755bb1bf4108c4fcbb579f21e080d06c84ecc9f8f75833d2be6e8887cd4809bd9275b1d460623e0f9b51382c403bb671d98ff7cecd2d806ceb2c3aafd66c93a9a695b3afcc666a038603a3afa598e14be5c5308d9c70061f6a07d623e13918be78aa70448764fa02834ad7be133711e1c4e7b874f4be7cf359f82c342608da2e03bc4df0ece2433ed4fa597aa0ce9945c3e2074cc1981839ba87a158999a643c44c96a8032beaa9f8fd13356cac333f11b5fece20fa00e2b4400bdff95def38a895108fffb352175e062eceb2d43d75c49239a09288d9a57a89c927c496ad82ffdd4c26b77986c08001c1b70a67f885bf85948f95cb74165bab06c35b94f5cdf1740904e291ae123eb69f74a7dd437a9305ac7ba115978e295a0d21c3626f681206fd7a4093e46e4bcbe0b5cf74be9a8a5db35e79f9bff83e41a262df5086bb9c99ea4d2f4e4996ee9f55f124e61a4ded6381e0915bc94152e4840aef534f1ec86121b15c32f8aaf5c4826283408162f668ef28638b6ed5b06bfea638ca72f68f15f31ed3e22a87ceb1fc612937c0588805f575135147422feb71770bca9c10dcbdd399d75c6dcdec9bfb6755bceed29913ffd1bc44dce4ddfae88853350d125f78e2715eaeee9c215a5c9ff5f84b995237fd1e26a92d894cc10bebfd4800ffd646d42bb2772a4fd566a57d82ebc0da32fbf11183a0ff02f94df1b70926749b014cf65d49d5e687c30c9c130e88f1fd4d0277e9eafff9fedbad4b32f9b6eb5e39357b34ea106e3ee47da3c351b23930dc1e6ac237cfce28769866f52debff8ccc7a6d8418b8aff2c2acdcad0df8930a421d7b973b71d2a416f3bdcbd60c228bc0e4e0ddf8473e33f62720faedba5718497d35c680e36328587f70c7befc72742b2ef071f9b0088baf12e9f9a85607c13e4c80c3ecf8fe0f06bbb2bfadf89de3af4437887cf9d1232ff306aa612c8c42a153b275025a1f61e1cca25b80a4fe0e450bed0844ddb5bd7cc74b7a0dec337812acaedeaa6a79818c31de6d6d973f826b1c10bc2206f4a2311ac815e7f5291e5c3bd56334e262d3383f64d06821dbc863a51eece35197a0231a404bfe9b039e2e744e15d6d7408fa51995b281834adb602c2a56857fb26a6c15928555b0d3104c5727241998c9ab42a4909cc909656599fe5f75286460c95d453658a798ca77d134c999993af0b54b74110bb817cd048c7a69e92e750659149ed00164b6395735cfffcbbbce0c6e33bec01d45d9213a42679ec4cd1f39ed17173755a83a21bd7495871a4bd9603c84339ecf4a15889757513c5712b99b621bdaf49b08791f0ddf63269d221b560837587e94752ef70bdd84a9875f440f2f3e938502a2690838ec93177f595ade5bd27f5366175ce273321db4c7805b39c1d499237495535f3a86a8b30c218c668ad9afcf86246b3876a467641d00b3a022792abecbc6e930271b02674503cf36f1c938e86c089baf75f44b44db3479dea33b1bffa1f41181b3921d9f82998519382552393828fa4b4adabd2cdb7954125b5e53671cbcaa686429bd96881f2caa573e0a5fede44b4d94dbc1f698902d1538050ec263641cb940bb7c2cd5748772b6e19f49942f1090d6aa4142fe113b93e33466234c4571354bf9bb1cc811c7693682d144367346037b1b360c202ba1af1e60164de6e29b0395a3629e3a0c97a2b9a555e3629ec6e2e210e3978990594b407239c20ac66f688b76c52e4f3a0719a12b453c3bc1b2937bf93b9b86c522a1e0c01d7201e399170004c9c2827f6382a66841399dce3439db429a7fcb6ed5fc3f70b4cf49760723605eeda9b30d8867e1b5410fab85355f174db3f97983f194880cf8dbc8c172fde0d6a1d6593321a256fff012ddb865c532bbf7654ec9c0d1ac8f884a44c87ecc627c4afed4e6a4699ad6a38720047891b40546523ccefed294a325de5baa5c7a9401b031e201f1cad0fd0070b4b5b1b49db0cc27d7a16f4664ebc284ce053c83547b26971ee72ee5dcbfccb35a5a00b39606f9a1575c0088a176052740113bff25983b73f417f17c7d8adf8a1a121d2e2251b6b4a6ec7227312f47a06e5b9352e40af6b28a306050527be7ffb313768edc7ec4ddb422904d0a0b1132e499e6498b8ddd4931319fcdb27ed3d8663f6479e7144bbf130654b8cb7e777800b9f220204c499c124a525de064e780de04d70b10802731be4fe605297fa5e7470b7fe6edd1c25d3aa982488eae64d29f99bfd5d8880216c3565fa1502a3f8224e11a3c79c26d30b8a8ef0773163bbf640674eb9a09402eddbb99b6cf319ab63ddfeb7b5a5a627a3545b6b94daa3bf205b0ac3131d38a29a0d2d5f3356d7f50af7d11e508f85671923ca8444a3f276b077c2b487b1f709c7c9092b7a1f4ecfdd9c0c4de12b7505c8f6c9e16e39481ae3c7f7dfd015f49335f6f4107a2062d9df8581b8bfcdcfdba7c7138a9092426b2ec131823eb767c2ac63e316f027627a6153ad644e73df14de74d74a90f69bb1e7a69e895adde1651c0b5c44ab6ad29f16c706235fe1320d4b3e3d478c2cd87908afc91e9d68ee147927fff33fe960359505d00622874d31bed3df12a2e3c23cb2a8b828bad6714b28ad312a46948e5cbf30e5e270da1f0830c39b02b18b267c5c2e24ce84e00a0a5f777d1f2ac077f2570d2edd31920464c46b9ab09109cd034b400eaacde54e1ffeb9a6a5266abfe441f880ebee68d5421780a8b94150f5bf3258a015796cec7b88d4ee7b6d7440a185ea7d5ae02af08152cbfe0d69d8c9dd79db5b09cb14b3ff1fd25f7ace0c7be0dd39874efa250546fe25fee819b2f682da20f69838256b9b08d4b7ecf8aeae1ac05192ce9466a8e47e172010693e81eed36d63fab1ac58d943c3d7b69c7e4aac9a1e78e64487ec435ec19e9a3a277d313bd92b18681169ab2fb5dccf434ae89e12ec32423ad0925d529fd26b820b92bac4a50f94612cd372162aac294e1770fe855082b860567f3e33d081ef83e69371c9ba7ad0fb17f1c6e8bd05457f2d25ef622e322eccef9d02950840cda8b91f891218014d63bde8937e8e3899ff144bcbc7c49b813f520a382f931234409f457b2298b79f1de953489917092336d22c64e609a67a86915fe33a87a7033fe76105128dc4d17e6388cb88b4ecc70a144e9a01de03e4910473bd3f99f8ccbcd5c0711c4ecb2e84d45967506ed579c0f92b58a0cb7069233d8f0a1729d131644d1d9970da3dce410178cb16d2682915805f9ce1c71d35f736d3cb56628e74e78a8c1572524682ba839a5b2221334e081c9eae8430b8856b5df503724aa735a4eaeadb91ac91658491ddc764ac7bc233e4d48aa7f0b2ba85e9289d8be6fa48724360075de3885f315766b490b371feb36a1c39e7c8a6e70f8e2572815a7d9dc099914ef02b4a768da81b65960b67bf423676087805d502207a67d66c734d779744a5d96b46fd5b65e5fb9638905fc5ff98c3068eee85e8491403423e96200634a380d5bef04a241b452f702d6231cdf4dc6c45987bf2e1a63d0e940780e55451562d3f9cb311e70b69119d47981d98f53e3c1f0d0c089fe0b7cf78acca23f3b8bfc7f6774c24ed98513ed28c367ab193b3077a990ae1b7a0af8f52789dfa54847452c35d05e04514b284d8e6a51740eb1387c56cb0ccb5fbf18fabef4375168f70a4e0220ef7cff284a5898fec4e3ced0c4a2d5babf4339c5cf5a639023732e9d8684ecd60bea74456dbae67b9ad9d762b21e590cdfe44ed80770ce87d04141b2badac86c9ad98e838fb6eb852d94530b427024cbda8d5c5c7d878f42d25d3136a4ff5d6264938f7ce2fea195e3a0b4043a60ba64e26ab30ebb93a56adbb8bf7dcd0b413ba9ae77df2b901ef2769d86bec72da66d5fa2bd5d41bef3e24b61a9d1b5720f4d05c66eda0846b08b4a25b4dd1fdb3175ac9191a01b11a4e1e2a849a71aaa5407e1bc413ab22e2bf19243ae2d5a227f8cd69abae4bc23fe0f36577ba935f4f4a1d5cb9dd20ba0a62de9ec042558a6137274acbcc8332a6f18977b07937836a3d59041446aa243ca441b4836e59eed0322463c631fe84615df9dd9ab0bb9337c1af64c421978e3b3149bfe8d26af2169ce83df2b71514953548fcb03a277b8942ad99c989f720c3e39918e93f74d74526ba6859cf355a3db109929ae4666b396cded64f9133f339bbd83eab56587456195269de521ef75ac17142154c3a23f6dd2a00d35eef7b95db41e2c0d57f2997138bd4075dbb948e4ca8133fea02ae27038f5d3aa2b058591d12adfb5a1185f4a28d367be54e6993ac03408d8ea152ce51947354e064ef7d0ad780d161f12bd04bb3c5d5a322a599044e6fc6c3fc16929ff0500c2c4e2169b06c2787a9f3735cdd6ddde71aa28c0eeacf0fc84bc01fa67028e9438483a6dabf7997beb195edc4da781a4628b8dcc5590dc006a0b4305ebbe4129216c40e7328639bfab0943ce45d648682530b51339d484388f82b087ed5267e38eec0b98f1b64771139384498e3d4a548e3efb73527fea5a7e588075568b02e5536da870768b285e30e8e0c631a889ddfb252d79dd129795738e9f210e874747d6d2ad40cf82db13daadc0ae5fa3916577c0e7345f5abaafc82042cc186d7771e89183792f8517f148b19c7aa0aab56053326b46ba220a1b8af8082eef790c65f068fa51e386a5717dff8281d58eaa09d4800541b4d9ec3e491b84277f1cacdea781aaba0b0785067c410899009bb9ec03f3fe7b96ed935b55cd3c56e975463bfa4485dc3f82f3a7fa9bf3a32c0c22feaaf960740ae8850990b98047b4fca8bc58f7e45591184ecb61662e80e424cd4f3905e4052bd96c1d4f7e694187ae627787a44db80f459e6703a1f2fbe50c2bc01aa3f40dff054e87dbc1057b498446800a34404b1ad0950e79d2ac2dd7609174b917bb090a0025d93c51c67a74ebdbb407d8d477d12ade24332dcb29cb00e448f940442b43c036530519163774545a072f099a498dd421057f777d6e5b958dfefb02f969d8189cc88c8aa3884ab9436446af22b3cf8437e0d9ede5167b469db020c403b156e582554be8d8366982c1bc1d87d15ebeeae346da2f19bd9d048298c0fd38b25b2744d139341efce1ab083d5a5c4d6202d8502aadaf707215acf74973c5c1595d96dc98e0a1dc465216eb36f5bcae46e30f61f91afd5489c4b25540478ddeb58092b4918e3c1b282d8dbfb6f1088eccad3214961ec0b524289f3ddd2e16d48e0f818c46c79d58b13be2071284e26913e08956af3794f40b485ce47446a76b5e9738cf0170dd779b37d607ed7377f1ae45199165e804a018b42928b64110fb9b8792020b22c927a446fe5663198fed2b5dc3af69f4ec1d7491241775ccf2087d5b416fd1d14dc6d226f33a40f111a4a1233e0708620f2211fb11f9620cf9f00e34bd962eb011f240ca4b6137db92dae121ba0cdecd24fd17c01a35f95ce875ef0ef1430f8c06d470cb42d5e44e6c3fa79625410c643c5a0924c870a06cdc4dae29ab492ac70d5c664ee15c548a2a6bb5b03d7a01d760369f89f4843af98deb5a667e4d6f9c0032dbdbd86a943ce6ab9f6c37357bf7cd6a5b00388c283b410e78d748b41da634ad6c1c87106be8f3ef18181df3d878b7809209405885ddb19a1b770fe92f132015ef6cfd6d341d76d903842d6154b76fc633afc0fa7e3232bb14980c9c81b223bddc764caeb19a2d2d6661470d1460b5d83de57a2dffc35dae2d9c614118eb6a4ab9a8b6bc0770d56fd0f5c109f577b31ede0b06493445a10689c58f7a3e2095b97445a1a622f8cb22e6b49b4f7c07ea9a89e209933d1013426397072d345d27f8a4097a00eb75280e72898f6cf6f79bd01129aeab6673007eac420ede54dc3625262b3228fdd4b40e084a4cf12e25ae05428b7c512f65d7d9c1f7bed750af5d3877fa455d59b400dc1290cc0bba3d6149612b429e82dbc287b1bdf24fac3a51d25a3df4adcfbd79cb588d5bbea99f9d34d42756b833405ae73b232d5c7b7bae818e83f067a9963634515d21472476a29b2ab649c34dd8fe0ef204d618db7cec867816294c225534f3155d59e702eebc2043dbd8ba929b0458054a995e2c435687582e3be001431f68eaa099252400b2a70e84a942c265f1094d79ee90a83a632f95ed5420f1d407f0b3d1d99cc11a0f8c82a54eb006308057c2673af6726ee29f2485c5307ee46a3a237f9af3de0b7a29d738c63bbe918ff6a0dd2c3caade77cbba2abf2c8311708083f3fdc6895f88108e0e7e44374e601c1616d7ac4d3e7ef10c9cca8d9ce1e2940eab42a7c52fc980518ccb90372c041fb9b356093ab3e11b9c52f67f70908baea73cf0cbf73e34533d7d797d9d9c98933e10a473de3e215ae1bc1f5275c74bab381e47e5851aa1a927b9366372d2b90fb9b4341a75a1fa2f988e52fa491ab4e5f09789bbe22283d0959553096e98b8c70a255c6fd98faf452e87ab8d04775512d3a93397a3ed3227b46cad08a217db3e8f09098c11d8a0330ee66882798cf50b04ce46981d514f810b55e767cac25a43fb621823f1535e23b0185ef240ac077fe30b1c7c31cef0c37a7eef3e184285ce700e25c0479a739a08b595b44131fc528fb7c09f7761accffd55202b3b295c4c91190f3f12a585069a0331ca040d1f3fefdc4844822db17cc7dd1406dd88477c43c5d5f1e07c0dd9eef02caa2e4e23e17343a315fe26e02a6679afde788b7c48341687f47d448994ef70ed5e75676e22272664076811c426acfc01253c7f71da71343dcb7567a096ee49c574c2c86bb18ae40c2fcf0f4949262fd9d60235c9c66f102127b0b3a1f5505bc7f81c2e9bc0620456d4749b015fc3334c27b2e894381cb293d414f59999ad14b4b458d9308f4f006d07728b8310ec071018a181012a0bcb5c0133ab24a846356de644b8ff32b311913ed5f428ad5f7c9c0294fb29522c8ced5b830c230c6bfb8be30424338054b108473849641c4ad7b565f33caeaffc7294e7aab93883c63e35f45c18dcca5cb60bad37b5dbf197fcf8bf2ad901a55f08bd915e4a970a9ee4d16b4fdeb312b116a0a005ec0605f3755e9730824db7f15b253a4bf563fbc842e25b494c9cd2602c8458910579791670d04ffebe97ec8b0888ed5226490d31b6af297e70fda8dd36e0ef2a057c9e8b708dc09a5aecebb2f78f47fab690fbe8f8b51de112e1d50b78681f9b8fecbdb141ce6d95d9e03099d1d9f7d9865719b4e20efcf903d415083624b803fb0a6eb0401f8843c357114d8ab0fed8a8a001aec405b830bf7839ff5f55944b0a6fb03b270b62a06a6d079d098a4c37972ca5deaccee9d7110a5a5128b96b6f54aff13f238a998fefab6a5f412da22790d5d44aa785c5791a70948b573754c6e2a49ef168948b6192ba017fc186ea9f70694f3ef46ea175bf912d938da3b36320139c928faa318c5228addf21a259e41a8b822a76c7987e41acc27c29d8e4a30be969dbdf28629f66d91d1fa9dfc50cece52ec71732efe9c245109266e5da57c612826b3c472539f0397974e4f4f5c554973012de0f08ccaa605d0ba28912e2fe87f78f9529d88cc0083bd62377f7edf5a4c731146b6a2ee9db174e4c79e0250cd51071ed408d95e7166a941b420ba9a3d1f7500e076ac38c5c33283e542522de21f614aebe33caa91a1d689985406423204857087ddf26503f5e6bfe61e4f338ee2e2466adc3f5b5a724f8e1695c77e4a2472a9a6c21ef5b091338cca1ae87b6db8ba5bdb0862ab90f8e0f51d12e07872bbeb864060e05bee3c433c14b57b959b576472f75604632e110ce62a25ad387cbb038647b1886acd6794ce3433bd379ae49ea9f3ddd718b1b069714c10fd83f7bb5fd9750e622d112649a166cae1d7915ceba7210f9307dfbac1d851c2181f30549706a2084b566873fa8f9a056225ac1ed1ff280f08dfdb3690821b99a1dbc48c97aabac52bdf52dd11444a250069e9a457a5545e8950e3cd715b83a44258036f15ff305a4e02a49b8b0df1be18da9a9ebd33fb682c36df281a2e2fd71f03cab7969d985c85431f17ff7a9474a232a866fbfb43d2fd55b5fae5b84db7d0b8e94a335d4ed750f3134a0b1c3908c37244a7737a048067286cb301d1e5b80bdde60fdfc9aa751be09acd0f7fc202edb0039bb0b823ede0d8f947df2da9e5132f9e65a38203df8cfd3b486c541c2bf2a5f13f9f71e10314ec500236e360670242c7654eafd4b57a107eb1ef2b74b66aa7d08396e741b61a48fb38ea5e74f72acb0c7f56a246dd00bd3017bfdd2eaeffaad10f732363fd08760594150efdc8db1a9a151a4a4c9aca6818c0aa14314b0337bec10091ad2c3b651407e36ee464e58477b2b793894400500701474b525b4fcaa1bb38870db8f047a76c89bbd06c3c628a0c894642bd2ac490ec8bda076d1812be656ddde974c7c952ea230e9c196aff2e3f41b4385c7cb89735d6a4e903a96e174b7af1ec79403f14bd638a2c45801f214dc1c0ad5735f89572b7736d717dd620b913cc9e5f1709dd2381c191d34d5b56c6c74c77b1da2491d70f7bae7ef82437ec5663cf346ce2cc095fb4ac60df2d5f3583886c3ff5b1fe81f86f09d94656c918ead68177f120fb12d0b6c43e3fd3d373b7c31a27db82062080f149db0cb2dfef00485c9856bde8b7edba9fbff045a6c852c02a8fbe530d6677b6dc89cb53666b04d799f77b932e05f04eb8beda94d4cac4a43f4818eb72218d6981823aabe181d8289b7ce48f11543b9c2bb7987412f558f33f08c4d5cb259d9c8a7872f35211e41c213c450079c2b28cfccb5435d9c7f54549ca5fd83d1b3ce6105fdb0ad51c0e21eef7a726d95310fe54883d46c8271b7de0095ea139fb4fc5a836408eec6599d757d95a153d978e1f2fb5d8019fa4329b57e8d25d620c8dfba6f1fdf39f9012e828c00b27e9c5a0583da395d3869b04f99ed23b7ce5676ad657ad42bbeae71e1c0e36fd052313ad5e43a6c196daf7fd1c5c99a5b2692e57d444ac566cc1469792880f9710f5c9b8387588b7fc8dc6bcb31f6e58025f45d92bdf7f439cf033af073d4f451d4e856aad6ce6a1a39d79777b62e55589650e6136f1a8b6a8aa401f41a904284fc518c25b836dabd50b06063fea9f92a08f2307e42000bc2b89857c385607bd896664a2649f77fe79342cbd1fe79dce36e0c6eca9309d45b1e3368cdd93c562763a497b385bfd891bc210bd59c79c7dadf9e334dc8aee23d997cf41c102dcd2cfd7979301732af91a7e8b672c87a3b5bfd590ae89f37edba6b09e0dbc32086c0081c85b1c70ea15f48cb8807fb4dfed0cb12535ff6e2d1be5e745eee12000250b0edc155e3f310e02c1a0664a2620ba145386be44a23cf52dfa8ad13464ce395767db1c0aa49e5db7aad52d5bcbe6317d963141d0f600c6225091fc513c665df00bde82a68c8c384c6745dd9a91c3681228832fa799e8404e2c2ee64e2829dfc4b334d0074e3b79e5ab530b870f07c6466c2a50f69921644ab32fca530ce21d536a9ca1c97283eaf131e0436dc0b9a82cd770ba697a3f4bad3107db757a7eaedba86ab026d3bac207981e3cb1159695ad3a06961a773760cbc243778158359838592b7b36e954ac6006741bfcdf7456103a742c3976c29fad030b5583d6a12691728382fec03c6daa373d032f8dc71b3dab20ab070ef7e567f8eb7703573c65192297851af7b0a2a5657deb0e0fcadb4f7bbe6014a51f392092e97c57f2f1b32a0b0e2ef44fef326bb356678a1fb99e2c3941285fede75a768152297ac7f18ea6cbb1d3ac3bbd48c22a89d1b99a86b08ee4fa5c193450ef89373786c1791b830d828087fae85b21898f9e289d951c1a99c75dee20a4890206bde147d5336492aff0f8f9670a6cd86e821128c7284e94cf03fd911273d51850db42ee19e4580fb4960c4778864bc35adb5be91db909b2b4ab943ed07accbce6a9f2d1b843c54e115b3b49cecf4a6ee3fdb5417c49337fbf0bf336d93b9b9039e7aaca7dadfd19724432dea0a7f873cf623015373d4ea9b8557d6e2ccd2df50689abe132619a5139580019c166209eee066df7ea69a429c4f85b0c77c938641f4df6e6dc20e5ca50beeb7065750c32fcdbd00026e20a5c26dfa3f3cb5d0dc7f53317980e6fa378c6cd71a1aa2c10a300765aec2d8af65f5fe9df3e6f42e1669426c50b928a57c74302375416eff11702bd660def1e3d81159dc7071ed187f5f733024b77239655338c6c37848648a4eae15f81896df93004febd432eb534a71735f85944089653477f21feabd33758f4fc8628dc08023cbba1e1a1cc0498eb7d0b93635b49a5626f8a3c8ddc34ffcb368d26e186374bdf76862fb3ba6e5e047a353f743a74d0ec9acba0db82b2d8ece05738336c6e20d5209186ac9cb8bbaf0b5e8e4ade65adb9bb617897390916e88628ad88c8a586cbe30367957b60c152ca4643d4a8aabbcceb83127721fe976cce211e79c4a8d64926e1a42be7504f44e8f19a7021d4af0ab33d442adf309cbd83051c5637906ea8b132d34c6ca2b1c2a41cf545ad9e21f29747286fb368e2391306e8d27a21371f2a523e81660a88c97b6edec12eec7156e30491a95bf6329b7b43e850bfbf5c5b48ad8dd0081299394b32b9dae211ce3831a67f82065b2d4c952e1d9cbfca496b1590a70a74809a0cfb151c1c3d06cfee12a090f9f26c50d0a56a0e6de8ceb97a8480f54dc16d212d6498b4707a81a424753c693052861995f771e5d22c6ff019bf1e86cf03170759acfcad4d6703e7ba44c8bd9869716db2171159a0b009c153735e937dd180f94e35a1e567abd74e17c179628355f329d7622ede656bc22c156f1904b99f249571d8e8f60be6e5dc1cfff16628e0035c3e770071668c4811496ed02db2b38016c675230367ab33978963055c5ebed7032b5be2793029e04ee1319f87bb92efc896a1a6f6ca8c23c51d6ca5731b4e9c0cf90ebba94b970d278f58ec643b6cce03fd8292d7b96307cefef26d387a9ed836beafcb3c52b9a509b3462e6a320e4e5d3600eee1a528f40f055eae0fffce6d3c1dbf392659e6345b7e63e15e87b90440d64372e64213ba8e4623093e67d517a6fe711220631f7b3d54c9219c114ca1fd0857b1dec6f5d1aef8d921f919580e6f5b1bbaee1c1b86463b3f77b6450c1e9c6924ceb87555479e03747128cdd2350888beb94e6cf91317e36cb59e011923794a5f0ef35e6f78f25425d2637f9d5c46a1d29369283c9d48fd25ac619cffb71a5e9703448003db8d0d9d5714d267beb2150fdf72dc8d3c6e3e728042817c03ea93631a3648ce0d1d82d2e07d64bceb8edfea725bd089eadb571609e8a1e68112e0a9e88fa5291c46a12681f4121004e6c23a6f45692d46bc6f271527f017fdd5432718665c1a4f818d769254c4b9883c579368c2b9bb03b55b83cbc505716dae83c634d3393c3967ad46809380dc83b29f9b10d0c8b7c64b0d7cb2d79c0685047cc6791c04b7e0b4f39414f9ab4c74cee285a454e29bf2c18cac9a3c03cdc62ab45a679b8d9f3e7fb9e49336f9958a49926de0b444c85262c59ce6e52acd243072565a0ccf2a87d5276082e112fc3ef9c03b8bfb9be185727d29b746fca712dd131ab73146a1b0ccc23085db0f64cad4646b6eb136914a2b30c228f143f2d3dfd5db3d871b4a4cac456a3e38323756d089f2b1839187e38cbcec75fd7aedc05fa92f61dc0a919d965717d92a70d9eb563c9e054ca82083cc80440be05c5446060d7c8380fdc24689b862490a2c862c182f5a9cf70dbcc1430cee2f4c97a2bb695b6062fd51389539a937d582cacdd839d552a09b8842a00458bb9f9a6f96040dafa799ef5a839dc80d53ee24d2df4c2baef4a2842200502d2ea86c489d8bc14dbc061fd1bb0558fef980b621cd566e33fa82f66e8344c0f50052cad61df4cc17850c89028476a6a34d508e3e718d5e975b6ef5a19fc1c2028d9ff571f4cb57ead0d2f83433bf54507f5effefd9b37c87568b6fabd546127a424342f15ed3cb03c713c51484bf646a9440b25538eac912c60e4827fbc8dc380d7d6ef15314a6d3d7d9bd41707d4e32327afedbe5e1f88774b7c3400f160b2980f35f0334590469147cfb493c250627203f9857ddb34426b8de120317a1f6e773c24d4f02472d0d82683b66fdb2c78cddcc41175190c5a1764246cc2b861743bd09c9baf3005150e9b8829051aec306cda4ad613d121f9882d5f474767ad4a3f22908f0c265728a7060401985b71e55416df0a0555c595ad44e4d2c88fca039ad299fee0a13252e53affc295f7c3ec9371016c198beeb13204d4080236e8c1d29bddfe8bb66e605363a1aaea3ee275c5b5ff5bfae9700602b75bc5f7b911d108b823e611384aba533d535583817020298329cea6da27f088cb075a071171b86846895d199df550a3424f26d9ad27633e4720e249bfec827aa5ada04cb1e117d5141ff2e3e00e1bdf329d321722fb2dde6162fe62b8bcefe2dbeac4f458972b233c4eb4b1d312d886e1e109b505faa246c767b84348ab2640bb5456effe6627d4846628f71e786348c80e7bc12d280b9a9ca46a1a11f7103b92633d3d5aab2027264040660f44f4f63868444fd8127eb74fd525f7b76a176e0a9ec3964296205a0618cb76dbe0d9e6de407b3967e0d601615aa3ff978b1f7a0ceb6950b26c9ac73fa22311c4562e7112805b995a1b4250f5cd37b8d29d7e6dd9c402a5ff52fe410509003d06b3fe1b88eef5207d314a3b26692d0b5ac4d0c08d2101b1f8207dba088c1bc1471f7644be0adbbbb2aab2580322905b3749f595d0869beac588d462a51d8f90b0501da75d93f3106afc0bb0be06db24b122c22a6c3556504aad60df26c7cefa380e49be8d39207dc3a4004e9883146ce04c18e4bf1ffcb9c154093c875d00ab4c61b0f41140c380afa24aa2f3eb301a011b9e073b8d821d54c88648c4719addfbe9d797be14708d72388d0d81cf94c9981f4d47e0a5cc2eb5a42d2d33e851d5d401e3c1892b7a871d56c475134ced97c08b6f6fd8e02cf0e2fed0238729b92aa1be2436c4a18d234e7980e8701c199120cabe46c0c58e42563b63d58ce13409aa34382fced7ee33c7774f7e77d0a6f3c172ba238d7a9426bca83813f30b62e429133e46e9f09586f5bc117ca8df743c71ce8684eb01625c2f882999cfa82a4c7bd180ee501ae9525fcb0b4d640b0072bb4622154731342f940d07a9678a9696be31506656b62e82e594c2f11e122ee6d43ee58daa3dd4bed5286160d860fc3312d0332ca2930cd8990129556d152826400b92c44afb37a3a420fc271dfdcb7cd0b675be5f2a8c1343bcd67504b366dee7c8f2f65def59f1be30f324659ae0eb53490dbbb3152f8dbb230f993845b21dc19eb36aa7c146952b4629b444302658713b1efd8360f83a75ab4fca6ca0e8417b07e5ae2d2dcd586c4d8487a4d64343584fc5bc479b87bc314f30d71f4f53cf40a396b41c514e6bc193dc7b62f2925fca2dd0704e79dd39853d03f4b84c609ca372104a711bfa371aa58d7081342e8acb2b5260dae685d52e22e5c360bfc8f50a6668246cdbe502e9ca3945c24b7be720a01fa9377720f2a0b21c8ac6c2b1f586246dc9d98093114b817a531c60c3e8d46d49bcbc241f6ec23c182cee2944466732fe1ed11ab189a51c31a03d2e7f44312273617720cd31a020532d41e8f08c28925aa8456ce2471b5a8ae41561bb402f28b8c32fb14ec64893b11540b6e1fee0187f3a487685e4a0dcf7614cd46086ad74d74b66af23614e54d60576af9fb7c2148bd689e04ce0d4d8d289b7899afe1877bd2f5e4e8fa439854eb27bcb68d5e71281c1cbc2e05afc39866996f3d6aaf17a85094c812336955326eb5754e29e301df8a91b2cb03cd88f813ebacd31a51d3e3e032252db0521a2d807ee79fc97e811976b42f2aaf3d8e354012740dcce9edaef54cc744d405d4ce537224a6ee4d0a66f3959803ad08e35e87e482e1c5dce626dd82faf025ff6c51039ffc866d85e214ff34c0aa2c7186eca4ffadc7b90bae453d7273569d1cfd378030b5ea08e6922aff8356ac84ef7edcb66b5cf116f5ca6c7764c73d3fa3cadba1d1768e322782654e72bd2480f80276506d2fee197af5043fb3cfcd7411c53dac667783c3957bcf8d0bd08812fee8baa96aa26d3b5faf0b8953815484f5df5bb4fbb3b4a48277bb54370a310e981812f48783223c4778e54043be4d49daf299eb7e8ba116b4c20537d5bec0ecdafd37542f040832ed111352131164704cb6167d70c57e2b08d677c9675655c74a45b036b7a044b7a205ae792dccca94d1332e0107c3eec93a77985061c55b5994db45471c2a558cb67fae8a1170bbe3e3d65699102dbcc89b638d634e231e2822fa4eba17f4e6ed66764ca8f90a9bd3ec3b38af37a806531a7ac845ed65bd90e864dde971debed002402e24ea8af00c8bbecda5e1a39d13a28494283cb73438ff026d9bdc762c5b3649147ed9b0d53c8ca28ff48ca4a66181a5e6a27a24f04ce1658fc2fa74092edcfb205260d2cfb84df868e6b7e1d5d231f8e51b9924b4fb47899e3cc7dc5e011106abd8393600dd750eca8eac611b8399ee1d500f831ef72672c1b85421683f5a49ee6a103663b260e2857974ac636388ab27bc771979456a1fcf3052151c6b9286719c8c725408f4b29f970170d362c806fdb3c5a800e110d51759c4aa82468653b848fb17a8631fc563e51b79fffb74b72c8d2c2281c31b2adad3fbad0e134c183ac8ad5d61078bff204cf5b53817b0aaeb6ad84a10c80704e77cce68fdd20442baf998015c30f7d7d83195850f1a3d2d6c1590f0e9440914c92c6f7fccb1ba5b248e6a6f50081b4c076be2d536751aa3b619ead1a08bcd10ac2e036f384c7b052f992bb79762a4596603b333b4bd4f57bb7ad4ee2f3f1166a34ab41019a45474e002ff801c24f685cf47605fd1460909725e283350fa8050a3e3b1099eff1f64906acd3a18f4b54fac7bac8e21b82459302d7c1ef098b8b4f9aea23e31979d48eb5348fabdb4a0afda76d52ce92bcedbfbe6f510b37d6293cc5b96ab3819e7ac4f0aeb7e8590437095a1075733a83dcb40587a6a357ace4db0f38a0dee95ad1a211900a95c67994455925295fdac63b06ceb321ad603e3017dc5eae988e88ec72a534c5aadffcfdf2580dbba0c9f5e61fb1f208dfb4b2d51291456d7ffbc2eda4000937a823e9f57b2da137c22e9cabc814fc8e7a107dacf7feac22a3b102b94f9e825d16e7fb471851e2e27d2a2ab4b7a3380b5e67f25eb7d79a20c639db30294f94cea5942e52a3aa5ee00d3056a139c54fe5ca1be0e908ed5bcfe57978f1e8feb1ee9c2e12a59a0a7700e21147e4e971e68c3ff2aaeecadb52a0eee9ecfa4aee6a5af84237cfc04baf76fc254305e15251df95cdc504e8ac296c8de330aaf588c166486a90ea69c5b96581b6ba0e17d87706fbcff5ae3bdb7e67b55bf98c7a438e6e907c8cd5e8c69ae831d23bf19434419cbcffedf766b841042f6de7b6f2977cd0b2d0ce50b0de39b1b83ebc20a0f2744e9ef320e47cadf17e052735300cc7d3502705b5e4c07d342d4fbf0f7e752f1cf6e96871ac55a34a9b4a3b0168dea067a3730ec66aa2b37b456d38f426d362cbbc5dcad7528866118761f34fbaa696fa007f37ade4bff99eabb7937efe6e601dd6031081b36acf6e29a667a7c400f0600efbbf7a3503662b6981fbe5197c3ecced17a33005898e4fd6045d6c59fdd4cf7f53c9898e4bdf429adf1f558588c4987159d8e774be3e1ae90909010d40110f7f318e7260837dd841123cf892449a0e0f1014fc4fa5e1986bdc09ec6511a453197f9803234beb2c82b52c46abf8645a1025127139dda2494dbdb64e276a75e4e6b9182c229b806866a70d7201446f5e58fe8a65a458741584f9d3896955acc1bfa4d6a71fbd6851e8c2ce25efa424e16119245842ebc302c6be2e3738146f58b704ee8bd52688c08da40a3fa18088203220d41e2764da3fa44dcd08b9000a22d0417ba8c888c078f2270b6a38be0e3c2053939ef0e17422b84b7b8ac95b7d063998e9597f170602875eac51eb027331eed7abaccaf6890fab091998ea72347f7a0f1bd4819b458152afd55ea940a1396f50b964a8da254a33510c2770545312a5496973ab5c2a4a747f69005fc843253b21d910ca4174b767666b6c39da12487521c544c183cd2a83eebf8a0b23c3ef670f8d24ab663dad38ff9d8c3ecc16eed2223227ece179815654b964fc6f9cbd119274f9e93f9071146ff6ca951a20c5aac488d01cc7408611e5028889ee51b0ca2a3217af834bce2e7a73b0583ae67baab844d77651001ffac18312c5b79d478381dd1c7a231aac1a5e02194464535385450a752de8f3e0f46ee44280ec78cdf4cfd528a06a140214783073877e751ee1cca9d3fbb13e4683414ea267fe44fa7e8fbf28a1124619886e9547a1cdc50ab62e1c44977b42a86a1e21187c34109e2a0f78eccb8570a86b17c3b3c127f9530ec09d0c64090c639e95928a4d442e8333af589e103c3fab049836be01afd171a84e268b4c1a1fa30c98a756137930a4c926261928bed00aa0106411d7844c5c51378ee3679008330092b26eb1514038330083b5d3882415b753718b4c1b81b0ce228ac050cbab06f1293170c824152c2a008a70e1bd0fa5e9a1d0ef728d54e123e8cd218ffe07bf492978f6294ca1829b3dd4270fdbaae1917d2f9868e06957187ef3d4aadc9f0bd4bbe24df2b3d5b69713909f45c103e8e11c2c71c59941dcb2ca5d9b1d7d1b107113d1361b64689fcc8a21e6317d91a13462fed06a4b31c2fd39c0c66635ed766c3ef7a673a3404340acba9f35b4c8c10c678fd8887d584ce31e9fe83a9b9b1cd715904602ca8ee6e3136e06378cec7f082708b1595115ad66559a2db4ce66466c0833f582dfab9ea14ba0cdfd331617cbda9b36f543487c4fb1e5f4c53d4c78385c624fa56bed79c5845e58418842693c331df6361e9ab2b1a5cfc25b5bb5691849feb98641174fb30d755810423d7653a8254fe595c626620c0e178b79a8c501fc346f561430ae19c3152aba272cec850e7c26c078c3bcf99cb7ec08826b81ac277f6aaaa9a555535a5c208d08c4eb1dc575221bd96341befbdbc17686306aa0bf5fae6e202a4fbc7f4de7bcfd2f17e8d34d1813c184aeb4f6cee691449e31e0d0988a43e4757cb3c98119ac0a83e1b79d9a191ecd41a797607cc0447a399670a75e3225c840987836f7238de143bb8b94c698c3446795931461a23a5945208e955aab5aaaa8c52282f0b66188f6531cf145df466dac64fb87de8e2988752082926e56561ccd329139d150fc75859d58c31c6c8236a5314083108534f545642ddea6d78eb13d541298a1074b7ba83c6959f95e7c9aa689c53ca29a5ac9d8a704a0807770d5b523748b176efbfe780a8adb9dbb051575c4e92b04695aef7f83dca9d73f549f71dbeeea8bdd37700f8f706e0ce356cc38fb5e0ee3bbf26c123c10420ca06eca6d1024a9e7c97d7e9b31d7ca96419a5a35a8d12fe1296ccf5cb5d18e6321d1f90c075ec1270329755c2c7b208a84001329835bd97f7bf97772cfb515db61b10152840e6fa75eb653eea95e9a0d7fd62408e175f3fd1360151a6831e667da80da101e90c5a2c0b75733eee0c3789232006bed7ae3a904ebdbbc90f2135202ce0d1a351efd20269d47bc503082aaebc939619528692ca49b19c2aa57d9c11317f50226e05af98937972ac20b3e409adecc7bcc482108488c9c3c6ad8ee5d4cab204d229be3d8600e21cd008e7728eda787faf2e9d7a60b8af0a2b7eda86834d3c77c50763ec5345a7e4d7edfa75cbe072770ed5a9fec2eddf0a4033ea092ac642b818febda0c6c80c6bde63e69f2643f860e41ff075adb8bf20a59432d229257c1062cd9cfd7841a12c4919e99452cad2056549cae82e239d524ad9921ae1eab7e93eecbeadde64e4bee785fb4cb73ec7059d12beaab2aa6a0556744a08e183f03d197b0ae17aa731fd86b6fa9cdc0d8310422c2e8490623dcc0c063a1cb1a4457f3d8c07a846b1b41e842c3329218458cce229bdd381b32e7d3124d4d727eba24a9ce83a29945471e99f26baef31ca05cac6a4357f615735a9b460df4a7315b5a41575eee6222b59457e717383231c410a2a702efc73198feb4229980cdd77c8e3a2871448053b3b5908e148cfa57ff78ad90ec863e6e00c5460c3750e05851c10b5643529cd4ad583c151729df404a8d452cad5990e1e70bd489a93e19b957e75dfebf580b054abcb1fa5b412a393018e063d8b69c32eecf1688c169c10da1c8dea2321dd6514681992edf15eaa142d48bff4554a777346a8250d481ba07f71c680ca0d47aebbae34e7356756cd79f5e894e9561a7c5c61a603a6e0324a462edb715d3ce921a5743e4a23a574624dd477dd146db8e2be570d7380fd551a96838b62558ffaa761e71cb80c09c97acc74f418c222692c533524a4abd89ccb5445c82a8b15a923ed1ae229a9fc74542edb71659d5da42890342c0690f42bcba0a84522d125c2f8895bfd69d98d998e29da90e456bf4670341ab928da3e701f3c10abc710b4c79cbfa8366755d569495a55a3c78f1eab3a82550f0845583503e57c7ac01921a6a2391912164fab8aa43999a7399f1e3964f57337e7e34a3d467d345cce65e322912f6b525a91bed55b21153d7b96fd659abb22259094f2145c86f4e893b9584505493d49cbb90ce9291a4e0a2e437ace65ea2feb62bf297d29af0f42ba0ae9d40649a14fb1d738891a4d8faaf45389da86e2312fd890a31469eecec71866c2acecca84ccf3703789ecd873348c18fd9b1a9683206cd8a08c432702626298d2360c619735440f1f1717d5b018c41d940139188771228f29315771553153aac50b73a20ee0ba2abef0732749ab511284f44ab240de4bff915efbbc3927b7759c2ca0f2fa202927a53cda202aafb64649bc8a45e2bd644904a94fb13518782ffd6a23e0648cc87e5437da0d48bd25e36448cf20696ebd4d80b20b9a711ad518112ab558919a45ebba29da00c55dd78571b42cea02c61161d39a389442482f4b42181f1fe768ce49b1632f6558c5e2cfdd1887711847d42d3817678f5ab29a94623ab5e9d366a0a87581903932733cad667c10b205adccb2e07b94d2c81974b55ad48c21f4f5a7613180f049a85d5188e28a7baf9841805e9a458042eb828a1aa58fb4823056da29b324ad2a382ffafcac28bd88cecf1827fdec8e749e52aee22f8ac5793a238d6fd239e79c74be18a7ad25fa4e4d5a61d9334b56d7e32fecbaae97e275839eca92159b6270613a950fb18a9ec68ad2cb9a945658457f512132c618afbb6c5e6ad3d2e2bba53d781df050461e548e5348c1c47597e72f8b3fbfb96cf4f34499dfe33b196610baebd229d3cd9c9024b067cf4e35bef314ce3f9e1a9603e8483ce79c10d2caaa2e4b4e4a1f4fe9d3b0262a5f370516a2dc7728594c26170e0f77de5d54a3a686c5a0c26096c999edc8b052e501ab8c070c3a61841996697c67106a84bf5c3fe6f75c445b082e8cb6525bca5c5042a0d9b45891cacf744c818534b87b5d13e38c077d7c89fe653bae4bffb4eb72f60e35d40cebc297252be301039e6ff603c6858fbeb40585d80b2a85905251f5eab224a5f1d9e3239d2ec29eef65cfb2973a959d6f653ae8b426a0bbcdf94e612fa8f4fcf3293e0ab7eb9bf5edb2e4a4547397238df17663cad7a5d4f47c9b4e4844849cf18b2c76b43108b8d2f5c4f8f62b835c247d51bad26ea59f9ea23190a3d19dc34331ec46875e687c4d31bca3848abd7f651adfea9c63a49fa85b27e1e92448bc97be465a2769549f879a876ebf97d466c2622834e6b9a253d9e57db813da40a3e4a1e683ddd8c915b2984802331da7c7e4c7123150411486ae88e52478371a3774659ce543340c07c140c33899df1c3662dc7b3922e66a9626e69a66381c3dbcdb1e1173675a5ebfbde59bcbad5eb52de6d673a6a38526e6cab70055adc56271f93f681810e2f7a03947c34593a11de186aec8663a58acab0f098891a1cae778254d8979379b9b3b23fabc11d98d6faee8f359f618297cd3a9184fc1789b1c0ed2fbc53b55ff7297a31e0c001e002764868d26e6aabcf4d24b77f121925c5819ea469bdcd3fb8a2b8f6d0e8b8b9dec8be66068ae512fa48b9226af229f224fd29ca311004dfe0779178da5dd44cf0e451bbc96b6b92776c8bcf250e832642897ffb421f37250a35adad87ef17309f11f1b1e0922ed2e86c41096dcfe10f8980812442044624837a16e184da7640bb0408536f4b131d4cd0955397573422da403764e3a299d734e3af90d8398111b5ff1e40a7602b485289dda780b8e72874a3fb76ffa8d6366768fa537c6713861a858bd01ba9ccbf67a6ed729faaf07732a911a66d199743e0b3d98d1f99dd32e70385ec0430fa6be6f367629b8c37517811382714e3fe24c9224e0b393065f404581731ee3b0107c846bf4f98a0227792c44d724b75fbd26eec647ba331e6d4f3b2edfaccaaca3efa5b1ca99c0d2581a756516207d64973851806546679991cdc16448cf813238a3a780cad48b3496e12c089421d9149c6024337acbf4b604bb1b179091ad6e5c55ebb48ece3bd2a8bed0be26184886b32184b82d2efda4f3f6f5604ddff073535005724e9cf4388929c56c2e44de4bbf88f7d2312f45639d7e1042667e90073771a45334ab23d195914a17d4b1a2421e1ecdb5cbdffd584ca6c8499c6858825520d59188becb49441a337134fa194b810a264c6e3f36764c83971b65d1aa897924d2f9661c51baae5115916732d5fa72dcf8ea156132d53a836854ff3da621ffd416a50189210d733d3704d42fd5e599a79c534a49c358348a9999331e583ce9ea0cf460ae5716d2f4ce25357ec24017148c0563617d8346806e9f7dc0b071d0dd3888b1a016b5ae5695a5e99d1fcf084ce0e04a891dd13bd73db91b477143508438a8515fa8fc8d83648f8cc3c532ce65eb292ae7f486552a8460a499c8a2b2e39c0dbbcd399817ead6388c93d330879dc249f2e4bdf4e1e5e6004e75f7d1412604e6a93c2543e5e7784e8b1fb0b9ee0ab8fd3fa0db5424b9b9dd3d788bb9ccdf5c48cda15cfe3431d4ed97dfddcbaf1022fc9e2cea6d589134b9fd128b92091f066e8452f2b4a5860d1b0e89974b26ae3fc16bd49eb08b0875ab4755131b02501d1541c0e26ef06e2c8e8a205c718d7058093f0c4032ccd9084ed751a184237034ef5c668c11c21cdd1d2333337c101ece781f3c7574dd903b3a860d9f3c965323fc013f7f546ca3061bc51a651a3cd8d201dc12840ca78d12cba9fc864f3a295d37454f187400658a271ca162075818bd07637ca707e37b8e4ee95a83fd72bcce8880d90ff8f768b146b9eeb37b6100a27140d61686c0052d5c7366252b54175f8ab6bb790071fbaeefdabe927bdd158dd33939a1e398fda8601f663fdefbb2033af3313322de61dbcbf89aafb08522304629a394743e27234be960f7bb7d9952c835d87537ed78778ab06143086177940ddf523ae82484ef54d1f99c73b2eb0edba79d6adbd63674e14a0b52cf6f8ff99c454631c006931ea8208498ef478085a0ef6855118542a1e60e015ccaa4d220ef9db5e750d3bdbc9e37bacf6eefb19a8ff35cd90b34aaa74de29356b4b26f521ad90ed183018dea20455ca051335094524a29a540a3a06b8a1d14a9a2086d38d7e002f01b83bc4e8a2c74a82892040b1f6f8b3cd82081e2ba5f479401044a38a1bb9b99b99bbb9b00a2cd2649c564876b72851dae0aa1dbdfe1e2a086db6880e2f6c7c00331dc010d4e70583871db0bce0a2658e10ace0b6110831d9ccf4d09ee9057bacf6e364d10c301c6700016c438459b0f2a7ae0ac60450d2b5071e061cbd01204141c62a0c00a920d5a80e0a4b829c11dd2a5db76f3c015d0866843020aa41aa4c4709bc705e5522ebc7e966803c1cf3483a4c1ed8883db3c0d904cd02df07847207c1042f81e84ef3d08d90ad176df126e7fd4731bc76d2be46db1c5b5793c8110742dac532fbb9cc351dd867eb6ddfaf212978e9ef1e145770e47c61965219467a05582effbf63edfad271269271f56673cf8ced190a7dae987f5eb99ddf86622fa916362f5a460e85a7f237e4ad05bbdb2990f48ed464f83e3325756c8bb238db7601b99dc98e0baec016e467691fcbb71342cbb31c369e9061d26e2a984d1c3a0c6b451cc32b8f87550c24ceaa003267590019c420a2798d401051a93a1394829e51383e93a26436268ba0526434db886fb0c2ee7c242f41d15374c5c50360bd1570057be2b5beefb26e3ba1591bcee8e8598dffac2c69854c92e8d7a8e86abe16a3c9616172fbb6db779e86ad03b5763ebc3993e1e6adb247e27105788db220f57be796cf67a4908e12d6e124ac84b3f845e210ab8d4de0b62dd001b695fe6a3ef63eb824d51377a5f8d49b89ccbc6bf0d83749d8bb4baeece0981e21afdad6fa4ee8a3e11233ab5b9a11a0c6ca0530c84534030a3e836e336b93d6306ad71fb9b00ec0c2336b0cd782fc6f8e28c1933207d8ffe4d7eef518d676040f5a65fbf9a0baf9f2eaba2b025c3d7ef0fbec7840be361ca44c878939a0738f571903dbb839d6afad64e5432a5dd97554129a964287f402ad2808dd00d32eb03d1b3f8ade483ba61976fd47e207af606b0b92a9e10ea25bde40736646892050ad8e0a3c380ec9d3520038e01d91357da19f90f480b82960b9b50d336b46113ef3a29d4800cdddd2d74bb5b8a2204f1e0c29600c5f5d2e4a9f00c405c291f1d20a34d646e82559d3ea1ca0381a2ce1edb35a74531eb72ceebba3ee7b430e64bab7e415acd8b5ef3ca7e54a787a70ff4809f6f80bbd7e73f0082963bedcc25b1d358f7fa35b31ff3629f190f878624dca0c9100e0d49b84115ee6561d622c2ba979d16ad4edf29ba4d0023a2e2fa82cbb95c1964ec05f0c6ec98766269cd63b7b453cb9fae4afe34a5655919264f3eaccf5b360a0b8abac1d379f9a7d59c8097b0649e256567ef779715614486078a2affaee7c3ba99dd80dc0b9b99975cba51f2b84e41781f7727403eac09756b09522d7d2f9de21fb4d47780f83e8dcceff515d78569a6f742511045132e5a01278359125c773239fa8accd3d71b52b103880be77561ee00a63780579950af77b6635efed26aaad28f1801970581d1040d49b841133424e1061911d68dd66e402ec4e06b4684b60179554529e5f97ec8cfd74e653e2a11c63382a567998eba84eaae93e9f32ba51a95b4a2748b0d2395159df2f19776eadb3cfa47dfd90e202e7db474f2b0412d26e32d24a50bef5c307977aa3e1d8431483cf5d9ab89fb3efa7b437147bffede7c9b5ceca427985c1bd755f10413b72b28823a433af68df47ec7b21e394824d273348d75e567e7789288796a9194dc45927399925dc2925122ff80481fd5852e78d736c634a96d6c73e78f13f7bdfe1d624c75403b53653c2aeb97855dd7e5e3fae857e6e31dab7c589536830d610305b8cd67eff74876a3335b5fda8de4813b9345e04bc84111b8608628aad09171ee45745a69332351b663f4f74745dad61f59f056c35b5bf5f7976df098dde0af1c0fbe5f76ab2a6d263e04f7e24c878fead1ce9034f8e4563ffd80afe2ab2baee87549942bba7c02e75697990e95432de599369322ef1c0dcb923023e2f555b6031e7b8e074f3f46871f9d94cd4ce550137d66f4ec97ab2eed162fbab427959f7c903e3ae9a38f549e69f150131d498e73372e5bba8ab6c3e6d2f7c24fd150a71fa28f2efa282623a2b233226e800d149668808dcd8550c8783cde7878f2016b2e09aeab0eb525aeab6ceeb386c89b8410055cec43e41532af733446180984b4dceaee5ae252cb5696da2d7e663d725ce766ec983ee6cb58895a5d5785159adc67a94ad726f81ec1892bbaae8a1d28dcab350c5e71fb2c8e65ded75571842c6edc88dc218fd6a89b2996de61a8e20850343b489a166b94b4551c01ca8db6c4fd9e08de0176ea41d72f956289e52914f93adb01ede907859fa56e93a9512521d0e7863b9b257c0fc90d8a40c1b96cf382105a94524a29e5c1e3a128109310b2986ec05a74ca0a325e3ce530b4edc525a5a592dd5628adfe4af4c555ae337d2bcdac3cdb5e589ad25db1a563f5c6d24643aa99486299063fb5ccc686c446e4d21a4c9bc1869a8daa9175a7d665a6c36a2bf311e3b30f540e454f331ff0c74b2256ae06fd10d3a5d8909bf9682c6a4a643efae6c624e287d03bbaa544e5684825fac62a04d7a28e06fdcc7444fa57d199f9a0d7b297e6022fc25cc3c838768ac999f8eb56e623fe8294695559f983faa0e4093fd755719d144f50c1a5af643c868c5f2e8330c665684ae4abea1757da7c1a44a3626c388bc46dd068a156df5c50a5c5386b318dea9d4bd33b5706d12fd0861141c4b0dbb8a76eb487cb780fb6060821991e2ec388dd9ca5e99d4b6a2ca6d2c996f8c79dbf3116ce2269b9a2a75ffa3d9ca3878d18198dea021617755d4f174c707bba00e5ba9e2e4041e3ba1e2e6ce1dab8e1606cf01829821472f04416a2c8cc6bdf8b31f670c10af041cb65782c30671917e70701d2a917bf7060df4697375176390631372e3f0e62189bb55d0e47ca0463c4e343ea70a89c5f391c296d4d8d627101f86ba58e3577afd32fde86f0690e4f6e8cd31bb96c69a34e3b5c565595460fb519fa18f774ce7cd0c7f8453596fa58621c6a558c9893e5bb18cec5609e3959095c4373af345ec2355e9db29079e73cd5f89536ffe26ee53034177f30fcf38c7830361ea5b59a4c0f0a800b727158a23769eed66a56af66aa57879cfd90e797ae10474fb1a82fa03842a39fa275c0d16837dc7e7dc37570c8e2f62f8c93f44b83c0f1ce4f135da0c1cf0b7c1805b7eff070db0833d48d7b80ee46833594d579b85bdde6ba8d08298a2a45145bb0b3b9ac0d79775a2e03d78848643c47976458fe792fbdf18f929d9eba314dbf34ff18b186bad9e8f78370a786110517c589217ae7b23184314a39e79c13a31a11ada60dd0bfe4704da16eb0422bea2585ea323ecf459bb91db3d595d3a48c13d257b5c5adc17739abba44ec68b8cf375c8e063c937ee95f581a92e0ab05be3e7c7d3ac5aea7537c7722ed98f794e268f44e15f5c1c037d792a3c20917dc7e33e91437c9a95b6dce71349a1bb2ed9ead760fb450070c15468143f1068681084e367ad95a0c420c8685788b48a994f054c6c7bf911d62a623527a5d50caf88ecff1e2658c100a51f8f8985eeec8eb52a317fd85d14c072572adcb29e52dcbda96b85372d0352f29b9d25b1a03710d7a22418da297dabc7e4b9398c5dc3582593dd6a9d18a94524a6945bf441aad43fd68b4d9ec78f195d774ca7aa3ee8bbb28a9bc78ca8b93aeebb4b599eb16762a64deec1232af45e9d61793f496b499761dd3ac5b3eaa8f603d14bdaf8d205cd80d35e32e2ad68a44872d129d2dcbb61cc11e5162a0d87872b1b8c2c668a86e36ee4613c49d936297951dc3aebb2cbbaa53fa8a7e42285c43ded2a61bb88694f254cbf1a8062f969165855d38e172f0f1bdf7de7bef7d9a43af6dda2de64e98ecf1eec188228ca81b3f03d35e25d2766c33eeb3b12f2d6c54db5784cbd1b8a03b4fef98999999f908177467f8cf6e8f9e87bbbda36ff3a5d33a1a1774dbd2c9cd73b90d40082184fc5cb3932ba0f30102b9772e07803ac251c2bf71e7b2cd9fe8e3fe2e4fd328243ef32c124136feb4d4621387046b3446dcd02a1b4183c473cf86b0e12ddef8d34da85b43a10da5537c9f4ea9e0f2b7d884e50d8b0fa219efc72b3a0523c6a51db69043110fb7378943e39bccb9bd49267ef8268dc823dab76abf452c06f0007c8b5050dfa2d0cbb7184500dfe2b7a8c5ed634f8e743cd2f1c84e917b391ab57b32e79c7b0f06aec612016fe52ae4b9b8cb4677239802a4fcf192bbb2f29e6d183ced45e6e839ba731a0695d55cbc456f17c0a08b744a7417efee11558d73381e813aaf8936c2f731719db35da4516e1bd2eff5283ebbb9b82b3de5ae49d1c73dd290774757f9f56b1bf22ee9ef53e7b2db10b86237aa9178aa0b3b045e575334124f2dd94d653ee5f03a3b531dc773cab7218f77e2e591fb948f7792a46d18340cb8c67b9ae817fd7b26dea11d61ba33ee2a297f19298a0a8fe4060ff8eb61b14400adf68be94a87e4c6e10df9a9d146c9a76858a3e40ba0d57e31a95c12e0295ae9538b779a145d6ad8ab8dc6d4399d7257f486010be1aee85de432a166dfe615b6e8139b1801e5f669a20005c526b71fd7aae52d8fefba4b795df90ae9a2975cbce42eefcedacbeedc5dfcc4261d2fefb4c8cfd1d39e1a60739dfc80cd8516042dd7d9d3e833f0ee234b8aa2426bc4e51b71e5e5e7ad373a2bd172e3e59ddd68a63dc153fc0c9c1f69db3c463a24dd59124f8dce699144e2a9d2d2982a6767488776c6c5dd694cd79d4d77c5745d1ccbee5caca45ce5ce6ea25249a5a6bca6d4eab42aff66bd7b29f52af54eda6937950f79b77e7357b5d755ae3bcdbde7aef2e79e734c90b72b677bf2919d9f9ded66dd3813df22b3b7646f794b8bb6c58f5a5a1e4776b3b9a3436d331971672dddb8a1b976247b7dbefec6e6e2706a34a691a644e623fb91bda5c56ef12397a3a5845a3d6bf3c1cff0e7214feb5eedbb4fdb5cdc34ba949f79e73feb1ac576e5d08eec4c7ce6c2d2986eb42d32b47a9d49b3b4cd888bddca74a8d098ae12ecc3c5cd7e5d18961dd32416d249a39ca3b98cc4e521442ed75c9658f0d41e27f1490c824c1a25eb3cc36979ea6433ae9c3a94d63a75e490934e6121813ac5a4c34ecd936e9c74d2fbddd0e2937ee1ca7dad609c35c074f92bd90f016c61085bb0b96ca4514f1a86a55fb853ee9b8b932bbf0d71eed9e8b384da44a7b6786468ce9c3b6db898ae0fb0878666fdf0c369dc653b7e388de7681affe196f6838d5b2eb7546ebd76aac6ad973a05c4ad5b67f97374adb5d68af9701f5e3a6ba563dad6e362779da25835750ac98db79cffea5b3c42e372552ee32b736290ce232c979d26fa4cf9cc748868b04b1269f0cea04ead742a8514a5532ea2169dcaace3e813848d146d45fb8c2dc67415cd5dac9aee83cb4d8f2e5caecb5d643f5878e06086eb02af8a8ba58db26c6858a3acd7d06aa3ac03a1951a65fda4991a659d6a2c26fe41db7e7bf8d392a8682811ef4c0f6fd9d1c367c49c78c474620b006de30b803b6d3ae8e2917ea2562d36f1e994e4c74f853b854fba33dd6903b88a96a2915ef48931740cc0baca3106a0c1eb4200fce2505a3f515f00de8f90d1165b6c71a949fb713eccccccd0bd67b7b963febfa2a6c4bc33f327d9cde6e692cedabcd3d83509e24a21bcc5165b6cc187b25eb69bbbf61b224d32f17930124abfb47492599fcb9634be2b00182ab1953977042404a7ceedcf9da93393dcd0a21359b5a9239d589674128f4cf6e11bbd327cb80c69a4512d9990451ad592a7536f48bff07fb810128de24f0680b6c99b8d5e17bb492836d9c4edcb904353473a31645e4d73b7d2b89f0ba3ba85d7fab56dc88c47a213cd73e4763442801b9df2b9930723e4d3f360b807e504f604f5b8a0168d044b4ac43b537a4b4be9d91de974468574d2b1be52b427a594f288981b9ff21c9d128fea9412f1cecc774a8802b6d8628b5bffeed2a939ba0ba251ef9a8d46bd5f2e736e737648bceea846bd697946a362eeb3f02a8fdad6234507e9393a4553b1b051a4639aa951a48b34161fd2db6ea54bfacad935ea97644bcff1727449ebe12b5a0f2d5a0f568646a45158dcd0764e9626e6be0703f33e84c2e178111ff3fe0b7a30a907f13e34f2600810c37417df5c10fc714ee092dbdb0bf02de6de80a12931affbe5421b3de6ba38bb30599a98cb37f9b8d04c164e1e47234683491920880218b97d0268eebddcd07666aa5bbf2ccdbd5f77dabbf5e8139d74ca458989c3514432d12992e4e9143c0ccc8df7e3cf8349c5a107538039756610d18700d1c70cd81f16a3ffb8aac19ea9936553e7fdc09edb7f3f9d5a716d452b5a0945aa23d72ffc18845600cdbd17bbbd57f066b027fab82b86cccbafd8f6cfcc74b4948f9979b371e7b44f9ac9edeef9c1c850f9d9adaf48e206dd9e3f359a023e5236c72c83b451d933d16ba3e270fbdb008440c2d5e86f2fd71d732cbfcee53aec491da25ffa94d62a6db4dc653e6262aedcea2d9560aa9fa8a36fae49e951eb27aaca37d7c4c9d295fd42ceb8f22b2eee1a46d4f29266fa0b6d45db5037bb0b6db371337b9a98fbb71c663a4cd65569e2d1f20a83b288ec13aeee4a24ee933666ef2464567478fbab9a81b7395785a495b4cd31b9a5bbde50011c86db775990db2c51c3d75d66318b93021a32d8735464aeb78dbb95eec65894aeca058283fdc2b1d978dfe4fbdbd86e4bdccc01ce0cf75924d6dfb0b50b6eae112ec080d07b45811a5d90620b99eaac6d35976768dc932b0f5d1543d8b943f84a7b847b722d12122ce1ac282263d99b57bf17c42c0b382b8ac854b6c67a7d2f5ae9caa18925f060093b40d7b7ea642eed061201063f30810fa290c149010d99ea392a32d67152d032d7731220531dd372122073d500f9a9511224c77be920d72b5bf39eea9bfb717350c11490b4e10b432043166e43832651ec60075238414216324ec60af21c1d8dd0c280811918b843e2859716f58101876f1a451202b313269dd5a8fc83f3c8f0937239170f6c996cdb3bfb34aa998905031ede0d211cd0ce7df6cd8cee30030fcc04c7ddf8ecd384cf4d1ad56726f23a2a96c8e16ecce4dea1533eae878f3819f7e4ddb013bc64e80ae914992d334594be699c24dcd4bbc70c13896a565d17e65ead6bb63529e79493ce29e794cf70ca9bef6ccd8d1bbab921a2215121dd38e79c63be79cd8d1be21b8da40d8157242291f8a67ef4adbea66a7cb3b9a16af966740d11a9b939edb829e721f08a2ec9d260f7b2b97494a22129bd56128944228d46156b9410142d7888a3342aa7716eaebe7918113b5415ada60e757343d750bde850379e43257566c51563edee6e264f0a17c586ead6455e4ed545a6c3c13deedcab98937b751b991afaf82034c657b6c6442dad6c59877b302d4b2bf3a6a8dbcb792fe7e5147939cf0548c87151eec645b8081729d2a876d6e5d8c6c97193060f8337af639320b62062a8388ab293a2dfb0c52020c7119ab87d0634e99b167ae77adf0bfc2948fc7bb48d85201a55371b282467a01a84d07382c6dcb81acac6cb7d2739e75c75dfd9c1d0f7e29c0c9f80b01497f496e0662c40a14e6d310650e846e979c2a449113bd48dde9e1e2878ba50e34d09e090500e1f9d1e1edac341b0484f317289f0ce5e3d805a7f8cee4c1f56f75e30660cbba3e16e86bac167860af958db8d74bb29841036bf7e73a87edecdad69d423e268bcf91ccd73dfa5cf7b76368ffc792f50e8a6466ace3d28745b28428941cfc22753070a55374f9ec0201b044523429d82413b4e4089820dd53ecc79414f801c8dbe33426a4e4e40f2f04124fcb43531d286b3318910649c4cb451e7bd1c469df7e270c4248e06f4792ff14d51b70eb2d1415de4bdd0a16ea819dcb00b90b908bc16b7ef32c3f5b04eea0404007777170240fb7b6925fc7097e96077fee19d195a31ae13c692c910f5306aa98f2e485a8a55f141ae945ca6e94545619cedf6fee0512ef881090e3361a4894e6da7eec5595871203b171facf6961e64b09c62c030bd5871e184a810454925855447a20cbbac8a4e19dd755cdae25180c9083e7c69a57ee943ecd27094c45fb0ce5f1a1297c65fce7dabeed673b4a953ce562f69b59a9c1d022fb461c22e0da7d692a965e6ece9d92711ef10d86460820e9c28727373a323036d4ca3fa29a146f8a20d68628bc6193e69a7f2dd757f6c21e6261a1c6c1453e7185e265cce255629a59436ab384aa28bf63dc6208514ebbf2f6cd5082a8c50e4f637e0a0b8790dcba2fe30ca28232ce20210e24c479fa9c2392d06dc4bdf64aa0f088b0310d7f7abe23f0d899278f737e31edd4e7577f745379db310dbe875d65979053a145487b4a045b8e2f6b1529149df83104238676d5437ea39b741eedee284a7773e9a60580bf7f98d87e679a853d37626e35b8be8f630925159aecba7cbd6e5189755be5597615c7a9e975f5c963f7c5bb9f1b28bcb8730f805078099749dc371b19ced36df378dd262c8713ce421e72c0b35aa190a937ad3292d3ad539f6d5280100c00f2e3e58ed2d3dc86039c58061e217da0adf05bfc457e13aca46971d0dea68f0109fc8901bea8aa364f64d1ba0bf992e39fc5822ced9cd0a75e30ff1d4de638c0f563744a4517d074749b8aa9365a93bcae110d2e7e12ecad1e03b93ebeb3632ee2e2ed55d5c581ac567b90ca77c5d52e7e567a46d145b9a7b2f5d5f920b47c9730e73b209e61d47a39f75da48c7592643001a1d19254943e589dd8921a3242f7e7b1f1d1925192569543b9a910edb5192d19151928f741ad59cf35ef82fc9e8080ccbf421ff9d388757505db6271f7c77ab43835d9beb3268af4b8ada39272056bcc1d89ef8a8eb35d8b367b791794cf6fa0838996a6ba8ed1813e9e5dbc82411c4faf50838195bd38362b79c5c17fdb22720382abf7e0a82fd2c23b24a8e9039450b044774eb225ba304886523b3391fede67aca5d06e4ba5101a2624f41acab581b194c0b92f2eb2996d4430196c9ce32d961a6c3d9133c0bdddcdcee11f53c9893fb759f079372ec1450f9a5724ce524c09e726773130420a801094ba2a0422788881b22425571e270087125cf7b7138a88ffcf9b9efa56aa324ef85f48a43b227575f33d279b5352327efa5b54bf339b99fdb0ebb3b86bd5a9e5192d1ce6df84b088e467f64dd7b913c52e89ebf8d7464304ad74083b846ff92a28e7c30d2e11aade370f4c0e1606ca4f360ae5a4da6ffda19ead448e7bd68d130a325160d1ae9d0a0910e0d1ad120cb6ef45e3dda8942dda613239d07e39e2c69287a46ce391aa4c3d1a009c5edf3b87644d8150747a37f0961217a220212fd889c88aeb8fd4bc8e180831382cf12be16cd243d9dda793097d07b61d230184ed31942dbb523b45d3bb7dfd84da368d02574fb34880ad1a1eaa6caa98a40274e808284e0905011d1d0e86694332a3232b29373fb27f8d1cdedcb131c0d39914824ca6c746c6012b803791c0ec62e29ea26ea713856e08488ef8f7aa08d7cc0351a2ed9d169140693dc2343e8355244662f1d0ad0f7226f3d6a32c332edd2792f9da32ded32f25ebafedcfe361a821f8d7446571aea4847544454446444a4734494a453ce891dd112118fc3c151ea66737554f78a52e537794aedc9451d495077518f36128a026434d211f98898344ab4491ea0a00773a280f5cb3a90503624ba11e5585a0d761298c03a664f14200156631db33266402c2b8180684aa01535dbc976329eac27f3c97e3227d94d969315c98c643a59929f1f27185090d090c3f186ba613a18666936321886613eb574e370b8189d73ce39e79c73ce39e79c73ce39e79c73ce3967391e9365e5ef48679464b433e219f58c7ca493f7d29cc342a4dcdc3e1b71f64ae268f4b3dbbc8cdcbee47930d289e441d1a0f7d2a794064127b6ca19e9d0a0238d6a9d51928d7b7eaabbd08afa749e907b42ce6e242c08dd910e97a0304ad2a98d0651288e8a22e8dcfee8c868a453756a13d58875d2cd83b1de27fd90b0e0604442efa5efbe91bc709b4486db6a28327232d2e1a022d4c87b69c9e3e4f64542cf64aad58984184a9ec93397cc9dfe2f5b514fefdc8d06f1589ba8a7efb3efe9d4c63c2226ce3214960d6d1c311889be84efde773cce31f3302f69140f5be679442e85ff80cd75575ee734fe7387dae6b80c2f091dee1fe0fbce3b3c6e939706bb4f874586c3e1c4d35c935a499377cac042f075d7e16abff4cf934411a28fce77ae4706c24661670a88d80611bd7ea4d500212433face88b464b4f360de0ee9edec8cde8e56c36bd89119bd1e554d2307c04ba983bef71e842f938799b52eb578558e5bb4d95c79d1959ca327d3c3fb4e70b17767670448f07359a7512fc9755b2fd179309d24c64e2f8901bff592277c65c13877e39d7a61de12286e771675670d6fa797b0ce92db679d91936a529ad8168df5f196264b45945ac8c9b88cb7d95c3d75639977b9aa22e03c61e25e17bff7de7b97abb18f9e34d3f51729b55b8c17b4792f829e9fcc4cc77bd77b4f32bbb1e9a73390c9747ed2a9d34def66e29e8a68541cbd78245785b1fc45a0a11b2350941b23508c403102c508f4a451919fc0edb230a24697f1cdf4f8e2aedfeec91dd98d566781cfc1f266b1316c0f89a7c2b034d885b17296eaa447138c95579fa18717869d5989a4eba4f7bf731946f1157dbcb17a51517e72b343ed242ad5602b8fda11a53bf3bef2abe55c5fc8d05ade8386651190f1965f107b9856576eadbcd090b87e81af34aa61fd525a39fcc52db0070d47098ce778a75b9a86499a5211656021dc3dddf1130c4af683bb92b295188f2f3e5af9e9306044086fa287da0f58c10a42fa019bbbd1dcf78de6d2d394f79468b992087a681d56f664d906d85c69495154fa0e576ead9c5a124f8510aed05b96c45329112bb7ec8ce990a6b4514b53ba2c26686281763bc180417af1d18b8f5e8c46511bd1c36af4f86274d2e8f19d5676238de25f8c31b2314e8af1a8c538b41b57d221e9f02e228c2e3028f5f4e3d23f3bb3f2d24d2b2fbd672b2b34cb56560e4b2b55755df0eec980768311aca1a8848cb6b5450e3a4533000000143314000020140c074442b170442c14a58d7b14001096a850705099c7590e530819430c01001000100010080009000048e884e362954a0ed0f922675ce3fb3a0cd0f11c29df6149be3f9fcab46d24bcd50dd1ed7a417ad53dc76140bdf56a189106d8004bbda5ff2a3cf860bcc953ce40b52a09efe43548b332de1eaaced6c26883f69151f27b5eb21588d1486cf4446c56b815711f73257565619175d6d603edc6bce8559c07316ce028b1ac4c4756104659bcfb267b6fedb3beda2f807f2e3a95c6f128ae5a7dc9687ede84031e388fb4d35a869b0eb84042827c3ff5c0e3b527b36082ae550bf7ec85b0a2031a8e06d8e907efc61a7e504b48bb6c77519221bf05783e75c601ad62cd91f326e9ffab05e6fa2ecdaf36a78d57132c7ec1e032590bbcc96865e154a9729946d7633c4028e463299e7fe1cadfbacda33fd93db2f8638a4492c35150208bd06001c940c0fa622c26409d42bcce12de640e0da02b7e01439ec517523100caeded3f01eb0088ffc7afcbf2e163f59a18460eaa9c02a9da4168514542782a1c1fc69959573c2e6f913f02f5a18761fc230ca7d8722203666429ae2f40c25d81f97133ddaf4a655d2f557b2d010a60ee8582e9621ba91902b474c509c20f7a077a51cfce349db3dbaffeae27047a24e837c7ff53719ec685b4d877d39150077e53822c5c8ea25e8a827d754d7e30840a0da217e80deb35fc80047228f7817555ce6add72aab7d3c9a693bc28a2e680bb530aaa00d704229c3978d45cbd4320aedd6f041646401b04983c18ac2b8c0b83759e471a6af2e1752e3ef3bb12bb1f356be455115a62a5bd68200d0dcb49c09fcee891cfa5ed949d1cf2cf66f25d3f457448dfab872541dc20cdcbbf67809b09fc865abbb067be73bb56fd6f9eca7ec0dc98cc2ddb177b21547a8392a56b0391b0d586da8f2a19221ee63685db4724db674a0426ae8fa09118fb69b38e188973caf60a88acfef1d4e031573edaa101027babcde0d9533b1a5dd45841d075cd303f3142371568bfe962f76cccb09a969c36c997f02e94fab2628683efc54d4eaf00748082f23dcf0a6400d73143cfce3dbb2b7b9f47c9e9d4e1f38f60f559aae1ec85415744dc89d7ad77f0c55dba7adae1ac144da10288b98cac8084b69ac46642844545aa82f71ac94fc314e430a01b0cc298dc8e6fa67679e5a17ab802ed05b0f26a8ab7c6a91048bb054e32467a305d7bf740962e50820522c0c00c7c811e8d19322503f84a712ff5af515e5d9496fe4b45a24399a8f974f4787515a44799d80ce527697c9169345edd0bf457a50c546e7e4582437a1908ca0088bc3c4338268b0b2a018d063bc2929618b8005b341dd88ef5e0bbcaa3fe2fd7c66959dc6e6c2f8a6b14681abd58a62569382b51d32986b2422fd0bbfe53d8668ea00abd785a046a1a10cc7d96e6baef4b0f96b69a5ad7841075254ab313ab563ae93757edb1dd7c293c9a0994d46458cef2ddab8a499bc6b70d9f84f0b87c94a29423ebf5664f9af8e432247618958ebd53e1217bc36ce647ebb26f466afe7c9e6c60996635f37d4a6f7ad588368ecf05ed28a72283760ee2f19e5f9a8d57069367ddbb1880b38bacf88ef0b6ccfa8dfd9fa534cf4dbc9a04dbd0e359c8acf8dde0e94ec0253e0c6a16320743e8c8383aefadd5c16e268ac73752febd5bbefdfdeaf62c0011f5202f902c7baaec62037efc4de07bf6f1d3d963bc976f8342438f9acfa9a4971d2bceef03dc8eb4b7507e0951981cb55eabfe20aef5307cd48b272570540df9b910dcdd40e3700f4c167d3927e1c021cf3aff487f6e2489ee5130c355c904aff0ce231693c55a6797273ebe86dcd749db07da18fc14f6d5e262993ba1725a71b38800236dc9ae9a547dcd6321cf879e435e888cd08ba963a7c3ad266b0156b0d639766a765db842018e6cd2aea63644a6a6b8dd0fc75e05c207252827051fe3134e6edcc69588037fc6bbb4efeb9342a3a409e589a7a3bbe74355331a8be45075b2f816574ab0c8a6fb3612bf1d8a7a5f5ff2cb473e6864252b7af9de383ecced083ab79c128d176403a8528fc80767db007346cb6c0cc51ac0e979b782464529908d6ccbda4d57b3b0444761abcabe76b276edb3ffca2c9d9ef061c560b50e5160d6fbeac5b1aec2bdba1aa1a6b13fda2c8cfa34ea31d699842621624982bfdc0e3fb5638635e727e5e4793c36ca37f38c106f4fbd8aa849335ed9f4eaa55a64bd4fd75e4e10c8c59dcef63d3926fa1278bbddd5e34d59abce6292770f1122516110bc7a02053734893f9396d7f2b24c3bd387982a00ca72dbec6c8811b2e52ea73d4f0a2023be583354ffd80ed14f7a250f052b384d6f451a385b6ac93c530820afaa72ce14eaa049ad6204a455c0b12d7e9bd95e12b9c29a24d6e129ecf576689e694a9117cf8994ab26aa0d173b15153801fa295574a9ca309baa8d9b5ce1e632e31001f3fc6a180c3f394f516a524fa2bd703d3eb5eba5db089d4f3994834e583a06206bd71d4092d179e32bc49836dd84738cbc042389b1d5d992ff58f6e68323be8a9fa590337f92e5603ef5d317d4b84ff903866efcc2acb0bfbc5766f2d4c195dfb03f6b53e7c42130ba908fbe8afe916d1a0b1071798cce497ce7e8d29f3eeec2b0b77cb87614f823a17f777ee8f810f280a83cdb4fce5a7cac83d781b473041c39f9e09a4754517f2758ed62da35c6019a3306de3b747e6f85c1835efb64a0b9625a96361528e6fd9e0c422bca20722e6eb97da3a760595ea63f84850e8a82c1b96b66d287f0f94442e3f83005c0ac6e5eb56a849dc4c837806f48fed11a4486a6a9db87754d454a46c7e9ef73766f0defeaab5f6fb251a6304c4965c1a65307872b77c800b603899d143aa59cdb3b318c0e12c077be099f12a10a98687156c8edb4fc61e231fa2424ff421cacd499669d718ba5d822a1958bb5196e90f969d2c624ef766d91de33d37b5cd3355afe1129dd81d50ba42db1955a60baf27767c764afa37285c084abb10204f16d4327b909a6222e27e175b38e2d07f96fa0933c41d22780df372838ce64ff17872642dde9ad00cb145e20751aeb4b117d6d09023c1583c58e17bca31f45313c1d3e3714c710059929b73519e3f0fb8dae0b3f22c9854f6a9f6a8a339a9e127c0890a9af00b3aaaf90ec116e6fe24b5232d0692545887a14d4ca5e05211e3f1fe0e5e47979e729c64901dc57640a38abbfde44759f31e48b411539ea8eeda3a1e559d31ec1ed45aec4b39e6442a5037c3163a1e48432c69aae7058bac0b9a9483bc20602aaee41dfed562ff19fdf086f7238c49ba0ea4b37c9ec79c5d6bfa9a8e2b9702f97e3decf1f2823edff5e7be91d02841a48de18831697d8c2805fe9594d2435f835abf85f24487c7ae4aeab13a6aa7e6c0a8515e5b7d87bdb49ccfa842336b3aee35175903609b8d411d513b411fbfb98890be81f6dd8e856cfa78275308c56acc98a51b147988004aa0f15a62ca7849af462a5dc8165930f5993c6eb47f827cb124e852a1f72b364a1d6295b053f43d3cb0eaee085f22677e3d3335cfdc0edb4e8b5f6117a22b7be935fd57c90727facb95b32d62453adf09b1c08720d1550de71e2c32ad7640cab09e079cd67aea162cfb4bc21c69d41f395b34e008a091af4986a969357ca48410f13db9137c8be186596733dc39a12055690b078573bc63e1eb30641360b31cea43ce4693d7cfc6683c0813521d89434ec2ec38ab2c416351552be41e4c00d798d875a44369682eac7ec077462b737b8f45285f1bf1721dd9f4381872c1653539027773ff4205444089d913061c8179239f439311133b3d1a56d8c1e1ea6299863504e8cc1e6cc7ad776428c648bd1c0b6349fcd784cf69c720851bd1d51bea75671f6fa3bf3bb518945f2223357496e1f663eff6bff3780fc001e4708e3be67d6c9a2ead2e8180682b2521c34cb7a76a5659f83faec9b96f1fbb7cad1b0ddc9d0792c9bf75ff5049ab88de34042f502eb3d6df6e982d3ed23a81c8e9ff82c06e426dffb2715c38b419bedb72a1e284fffd879a7e9156e2e5f50ed1b6dd107e3be1e74a52dd86ab2d54a89ec825a0625fe2b646689caff403effb6afc3408bff0b418e1ff5b0bc9e68d77fcc732c9350d7a9d8ca6c22823b18e1514892b5003536c2bde4afabc79057b0ecb1d1ecf09683ffb239416d8d191023a4e8c98c81a44a870e5a447c86d187d5648da3f8d7f57417351ac869d6bec40e4bd7d5a592fb84ce65d09a19460a499f5593b468ddab5f7f826bcb350c17d04b5285860333007b2a3076165a8700d3531c103fc9bf2f48f4ad45f00f91addfa1af277b0db0d492958124098d83e2753daf1eb6cebb7752a07610b322919eae38b88d22d3c5ea00eb8bc84409621475681e27e5012987f316563e9a3fdcf361b569d103681d2a0944bc64b131cdb743a56cdd836fc29382ec2374da0ce776a2b61c65d5aaec829b922c135a8f66bbb8dfb0429eccc3c770a2f34e12fa34aae24dd404649b0c2df3420814c396d3b0fd01fe0fa1e42f48742f534caac58d9a02f83bfd1e056fa36e4bdf16c61b07c2914d8fab97807b08915253a6e52d92c6d54da1193dbdf518fa23dbd7ada6f40cef487535762859abdfaf38e50091c47fed780db90171ed4be4da851fc56c35b073003f4a9fca5559042af3eb820d251543489058721aa38dc0b86013a6176b4c71d2f047148dd7a4c642bef263d9d19dd951dc1333f92bdfe677fd43d4692628b2fadc19112a9dd4451dad1b08c291014444b28cc2a2258696b53505af33ecc736d9858f1ed39710161c26975fb09f6387f53b15fd6446e5c9b9e3ff242d5e448dfd6a401419eab303791fd3ab137882e9c4ae50579891e5578d77a834245d98d8f442d50c3fd6e9a6fa7f956056e892f47f3d27fe32d01d5e5d329f39842939bbffa6bd1dde3f981ff65a9d2b4e3ea6830d613b5688763ab08d38921111af2e935b3835e34ed8e41f146f8ac97c9bf9cd0a88295bbbc653373ba6b3192b6246cb72a4144b22bde67a2ee5481f1a6ab4095d1892e731379f117d0fe8b7483d4fbcc122358abb76c354ffb4b285a1bd94ba4a0db4ef2baaa584c38bde48469f6ef34476c0c1a280035a3b60d8af825aa647bc4b0a9b44a8a88ed32785534716462d91a8610c0e3cf6af931b20b8e1d0f1fdcabd24335ebdac383765b052fc798b3a0093dc9f233f37e624cb1b522f9b2884f29a054f10bd6ecf743f8441bac71968b0d414f192ad0048de370245549e2bfcb50a1bdde96ab8238e835ca20ff2043bba66fb5f8dbaecb392d1d4f8750a52ca31e15f613f653102047ac4154bdd46f713985df61d2bf94b5c77b9733e9597a23d099c9242c4143c7c27dde992aa8b672a42f7a6512af8eaf3e69b94a10203dfe96b4d7fb916fa29689255ff847813c30fbdfb5af1601334a381c316ca76e43005a4a0078f9e52013890e5099295670875fcf7e958e9d1749884c9481ff3874d8ed2c1d756af8bf4e5d0d43eea0795ad1f65897fcc78d45c353d1b64600b7c311116bdfb5507115e9e3fe665e93b41f1242584368b6e831219b82e5e50a2127bfb453083a8ad4a1e4284c43bdfc242d134d874b3ffc1b43233113fed6164a8237e769e5f0aacc5133e8ba4a3ad2433bcdecc474f1e25ab5f7e05b9168ba58613831956a1451768cc2876b1bcce89fdc53cdb646fccec3b454e8dc47cbf9dc198a81302885f9983ea6b416d09a066e6eec6bc6c4cc8a6a2f73e20ab4dda46fb602dcf8863d7fd976f4e95b0603c8e33a422826a37e3f6216ba7da904b1158da5c28e360d0b2cfe532acc35d51fe74ef411b089cc4f1a680fb30582338e9e3b5f10475e41c3f53f0fc1cfaff8ba5f6ffb211b336e5b996f14ee89d2e1569c02d081885685e10d3effd4b726a75912e6699bfb6489260ce3ba371e181fa52b6b9b160d054876cc3b6fc1bec606beb6d7220c5a406d3bfe6b48b4e719ee62a5942f4316515ca12d0d928586500bfdb8e393fcffeb919b3b206d0c77cc5617dc808c9431fb5a3d4ce4fe42c3ec5fb0d0cd8aa548e31cc559744025cce321632f75625033fd29589acf465867cba1decc819cd9555e2920467ea0fb7fd36c79b1b78cef808bab16748cd9b84284fc4c9a6c17cc1a31b5b596232f877e3d45eeaaff0e47bef3c9cb735855557c2b48a49925b54b6309764712b6c4a478a1a12050a941a78cad7bfc7f3430c14534545c9e2c091da7f05f85db133dda99aca14a594b9e1961a8a2b08d0b4afb628c9d65895818efce427334075fa3020f9f850f7d58f2f88ce45c12cbb3cafecae1c8fc35a41915c1cd02ee38608a31c2a1398904b36780386f0cd44e88166168f015fabd0e1146baa2028131d503a24d49051fcdbc22b243e07dc21f5afa9268bb04476e9512b17f6f9493f671befbe2971f97516639b14883b0b3e199ce10e611662d6a606dd3cca0b20122062e82c3f0e35ae194ef1a33272953b731e06996faa7f38e6220cce53901812551798ae73cdd9588ce0d215d70d4f6b423ccd9970861d5f96f6d25bc1a99b15bc4d33b044b27f9127cf16463625dd0004999218f736a566a6ca4b2b4fa360282957f65bf2f0cd95882b71a445cf928f5ed34ce5366217339370c3f0bf7d1e7144432455ae0526e324c320f25b75f2cad115ef2477bc17362740ae5182a62d088c46bd05c25679e116b4d93a91d7f1b1d86e4c89d70a0de268fe28eea92d89c90e440d63d5993c58f68bd707ccc3ae5dd31a811d86a6818bf5a420568af0318beb78edf089e7d238b39ea61131f68f4404b83130a9690142cecd858cc5fcff0c0944369f284357885bbadc11506d51c8d4029f503b0ad2bd80f9206e25d135e351dd2f13c5125d5d9ea837371811d60c7ffefa35a72813d389f9c2a102647de524c47d2ea0399f0e894565e8b05d5c4c22c758edc989fdc33dd4a65d9a980e1d3bfaa98507fdc159444937b7abb67110ae276a929c1eeb91ad401da27676ed20eae32cd40eda3f9dd7e0226329d24fecbac1b4640b4027af207120923f39c1b6a53cf7c62cffa375bdf3ec9df6ba1bdefbded2ee38cd92cd6f376fae9a18c04fdf7e0a5392c0828fccc7155c80090fa14ba8503d4ae08f3b47c6917c4345aad86fd17de10d02be1adf7218acdf18f34f24976659a6d5e678cd38f7882fab86ae031fde69df2f041ab612f4ccce812723684b7389c6ec2cb9704d33036a13aec32dc73fdaef2a318e0c8cab9a57091d3e9f14b4097d94186743a877729c9da890eb1c0a23ca73fb98b806d094f19dbbb13882928b142f47f0f588ec6964dd81167b9dd21694d90c5d6c19a3bcdfe6873b94908c8c8cc1d3f860d4c59ffb0125b663c41ee456a545a25486162a61fb83acceb0aaa428b212cb03db703355d5c8cd2d92c71d3b401824df6916bf52d1c205559cbefdaf488b834c4a589326472a77b6121c422d486ce1c0848eeee8c46680a9c75432c008650a971e9ec2bf0f3cdfdd189d0ebe88709afe2c93f49b281eee7b0706f22ca6d9909e627d0010b8e2be31996015263855490562e52a64304234f3e0ec92345e78ddfe947a03cb15b6b86d02a1119261599928244f3f05f045d7b07503675d0360bcf7f59b4dd984976cdb08739884bbe2fcb97d555db83befbe6840c9674f26cf57333ff76c16c04218947ca942e4429489c921a8cfd71a8c9e9c5997744c3f093071c4c2e04af32d7c553b244475054bd3460702f5194adb18adce61653370132d1313f4670e86235275e2ff71ddfd1beb74cc4177e476fa909e15895c28d38e4f721067ab9281a9d050d58b9edd855b41e365465eb6a50701da5cec11fd89cbe561d98d42dfe2b75ea78b151e2e81b860a61fdaa5f8fef29234b0d6ce5f508bfc54bc23a78f703d75f194892fc68ea5572c4c2d58887989b8648ddc1e71091d9cd04bb00e353d1b285821e6125bcc9f119e0b2c31c54ec044f9c3f51c11d31e122aeae8dab6bf1cbd136f7d15069051a658d4a1911322589a7c142430cbab857e0c2c3571041bd8f68a9e16bdfef7c9eabfb6175f910890c22aba62d80cfb66d4c0362daf11812e5ada4fafa55158a20209fda9ab2c32d5a944458fc435d55ea68143b771ae5d1f50748e7f8044c6375afd48c4776e7f99aecb1f4353a2ba266cfcbb46f16cf7fc4850eb4c536e933cd46c65031ec8a9585af35fe859e4b3ab5790a2fc20141ca467b237a12e5d4e3ebd7d06a6f3ab3faa843198546070ef18bcabc19ee9d21e8e166077ff673565d8c9e47b2315f09465f39668af1fdc171de1559043f216e2b0d1ca99b9b9a60e63bb9198d9e17debf76ec48c40340ac13dda456586697cf5b66314710991dbb8a8495f023571b6646aec526893b046a02da84c28c966f305bd09baa671b1100012cfe4d68624e457c6c69db522640bf6db57ff2afaa14e9df7121b41a6a0b56353214868d243dc7ddf4737d4ea0ac0691fe05ad615b09b4d8b05621d1f48641cd4ecc822700b5fa7bba09533116ee81c17ebcb75b8d468ba1da95c8781db4fd0ff1bc31594c630f71b611866de57b90b1cd34addd2a173478a037adb76a34258a605324f26d87b13867315a86ef859d1134afb594f194dbff622c7012ec2de71b1f8b703426bb4c079edef6da9db5067f739f5729e45ecc0f042037caf5778b54b2880ecb02667a53c73f83f9fd971220689155cef2d3dd0e5e3f5c92d56dd82e470a2800dea190cee28001b16abcf813dce437c8a10b7ca706abc38c8e07ab85e696b4a23c15e4be5728c12fce7edd83a11d6b38c3f0fcfa2efe374e46f732f9789a3235f274279c7d017817ef7de1e573033e465751b1c57590991ba07cda6a352f8da3f641a1c3b733c56831dd8f60a211b58edf0eec3e22e91916eecfcdbcba464ba2ce23d252e1409628fee0045628aa3990895e197c6cbae8898430079e03a24fc5749787bbf849b89f0e64da52c4391f1dee59c5ecd949206b3170245362726a2ccaa00a893c903c5f4d4cf192ddb52a334b7e8f86534bc9fbdb634e188ec5b55e722f384398d8587737107e1e86f992782622e3ec17d6db51fdb3ee6ad1dd090071e84c6e6068953f363c549ea39a45286a595aa914125d30bae99586fcb64f6f08b0e5f20a2e298da16f778433e1064d84d3ebdaad7d44876dc20da7bdb6b714166e6a3f734b0b3a5d9db17e94626000ba53dabfc490614fa98283e80c5ecafd3543b11f03e6ffb2ea6834eeaf2315ee52ee663ef21eb0c4e07cd0edf12fdf5b01c22251837f58a06df19a3818e8170db2bed2732aad512bc89c24af301fbd5484f89156a761c98a4788db72941b402353c2c28e782b6843874fb1d8052906ae0f5777becd3ee98f1885e491daaa517198d9966d498cc1b047c872196e9400963cf2359433fb00b61784a66c109459b9c403c38a8f8e231d67bb9295a2997eecade31df763791c68ae37ac9211a9680c92184fad1616eda0ef2d5b671ad2bebf3cac1211af65c2cd39a40d090c1e8227ab34e86cb312870080a2fba5de2a6e9bedce9196a429e65f51f076547baccd4566b09004bd965a36a0d4612f9ce8646ca73e99f22796b1a237b1ce077ef07d1acce56f5e0f9829da59f2b96e0edc1de15d8ebf9dccda143d2ba0668bcf09b78038ba01cdf7262bac76cb2e88488b879c71a6f342420e6e920e0746b7633530ddd786711b8b65b7e2d6152f2e58dd9218659b9b0ca9de1d1f50b8048cc85b0acc2fc60928a92bcbbd9718ebf82ae7387ebc16661dab3fc44ff262e37e29e7977736ba2b7a95184a1eac8bb99e669a7aeb64232b13929a1a1074d13c3df937911e0ed6678293b93c4d6a2dd967d25b93576cf89feadd12d62b96c697578ac86c4975b1ef020aa8137275448c2c3667cf486c6a0fd73feb0e0082e15e66042165ad7ce6a6a45f61c9073993e6313baf460c47dee301dc8537732743437dd9523aba07634547e2bb968726eb92174596a0df79583197cfa1341e3849b76c160ec7540e77cf38a8b640ed9ad2aee912678273bbad711a8d8c43b3abd364e310daaaaef12c97c4ad6776dd3383d8d0a66d665990b6fdf667e051914045c41327b5302695c67616a56160937c5ecb92ff73c2b96b126e34b5e0c80a87e619e6074f110125fb3a18d06ce832b603448a3b3e5544ee5c1e2120ed83063366c5268211cd08fad39581a624e29ad8934c3fd3a36726d2cc0ceb29f092939278130701df48d5c8f934f880bc6e1a9ad9cc2c5245ff9f3e25eecb3de8f296e23201c676590c4aa4246a83e841d7d6a65e82c5655631aa62d6757040aa1f986bfe38c63342caa82e83da0ca9559af1836b0a917cf6c5a8a20143aa25c87a326b882cd2508caae7f805246ffcc0ac6f7cd428b89a3df734c6431ac9d1024d53489790e4b3aee0b4e1626d6750835eb5f362ce4bce2ea17571fecd4fffb22a9bf7a7dae1257b2ae765c6e902464892987dbdc5dc71cedcc4a1fd7da65612d53e9caaf33919a813546267bacc3b12cc78f04af59d056bb21b6537e24e079edf2e337a8585438942dcfce90cebae8e2078b0795f3a4835f80f62c863935d90abdb60f1adff0dd03da33583fc7172af7cccddcd30f800b7c80a407680c94581d47bb069b84c555635c082c8337ce84065e283a58a71db433bc9a712facb6383358202ec176ef53de41c5f4cee08ef17807bf07151fd10547b411ada15a133803f11954d4efe9527a145cced215c0f4d4b03311cca6db35d25fbeaf4511505f500c50ae24d39fea87a5286729c315f62ab9b0d593b2f7b768fda703c0a9dc3113f184b29b7ffa8c699bd79c624704158d2fe0cb198313245f84cd82f2bf0ce0ac2349f9ea2fae751e07c671bce69dbc33cb01cc6520bca625a8fa4284419952b0eaf6bb9f8119cf03b63be47f7b417e0c09f74ef7acd00c7aada679bd795a43f94655e6d3886ffee8c50af40bbfa2dd222aa4cc81f53021ec73a8bfe688e2898897d68b6acfca202a67be7a8743a24e81fafd11ed4cb40bb8e54ce92d323f3b663279ebd97f995e43f92567048ae8ce6c7a21c99d271de3c4dc649e134565d8469c73959175d81d29678f1cee64388193b0df99946ba1fdcee6232b1a46fb602d0118559bec208d7e288bdc913abbfe5d5603f9887270fa74b9d4bab7a06f54db89e66218446d245433e515534f5b933fd6143e9d6272848463994a41bb3fb897733f6efffdcd4173ccab3ee8e6331442c40542a0c1f004c0b395eebe1a6570aa2c01b6acac28db385801d6c35c62720e4ca5d07b8c8fd8803aa9ab696dbff9bd562d6fd0186157d8057a30472135b421dbd1936a6d2d64bd0fcb7f872b853f2eb68189b8f755369b533dd157bb747b0e170d5982780c8e35b987031073c8421e52644445d534fba2b1b9679c4fdb4d24f53de4db638884e08e48d731e0f98f0bf18463d3eaa57c408052c19402c8370fa36e87b2d5e59251ba298857194db2f2871f03f8779ee8470c12fedd5f1806cd0af80155c172f72e97b96d8ce3bd8b3294e6b8f5d0fa11282365af7689a3e63efa097eb02770c4e15e44aec055e047be897be1b2a4bc2ba563e398a527780a83f19e2b0cd35c6eae0c5fba7a5eba3062e85ad7c2209472f2fdb9f2cfd1a100a93b6c11e80289b9fa171d19cf7549b21c96edc0630a46c986ccbc463676b6fce38e01b2e4d489610b88753103bfaf56c87845da78249b4bb76d06f448a8833ec9ca1214a3623352745407a9a383c9ef29d182aa695c90d229723fb224688d67e87fdaaa47600eac8a0b9f62f11a0e075250b32906a2fad50f181d4892e5d0148191c8965679c4511d194c12809bf593eac05f532f3ac6d4961848ab60d2f51656a51300b392f8d2917c3705d038a910b5322d7d4b81b3fa0c45603724752ae6d0f8ca4482cadb8242fe7c33470bdcd1f838d35dffb84b7e75c2273cf71c89dc22d16b247244625785c47cfe2f237ef9fe5fd8959ffe8d324040c392c015408c883ee492a10defc32b41c625de3aaa6141be3e15e20289401694160c074151c57514121cfd478eb777caff50bf4eb67062d78720cde003a9ca7bf97017aa0627045fb3da01397f6d08c9eba042f005ddfb46281a4fafe718d91308a7b24ae871e7c61dec09ccd70b0ae244d579ce2efb11ed07f0d1c468ec4b31c8e1e1f6394eba48d8a9381ed1ede0cabc2b0a017a44e930bb71cf7f7b31631a19498a95583c1322ca94c0ed2fb4898ed813a3ac5d701ebd30085226131328616ef118232b93b8e9b6bb0401af693574e6285b53d1cf78583d8c3e931123cdb094f07e0dacb4d9d75f0a934d3080af7c7edd04f143857d2afea379e99a7f10a0a24d6d0cbc4f8606d23bbb6f064784868c77078e4059e8f4dba32b03a25e45270b99039dd5ce401f3a0e8487a87dadad902ddf817eb8ab28636635da70d82c883442caa1a804b3407069ae490d7c8eabc0f288f8083bb3c9a3faed6b5e0f28d726d69d958ff2b964f78058a87d011683b23b0dfe4f2eba16ac3b84c579fecb33231f371fe850a9af2b69ae34d2620c19350602afa6394f5d05ad8bc9496c28c19d8c905321b4b71e61fc0d8eb7ca90088de9966468f90ac067495234f346355d1d42ce70d2924d36abecc22dd3daae464f0e6a7432d8af37854c092d9c100185bf17cd8f7336dcacd31362597f3b2083e1bfb65f653bcb07aef74b4b32df5510e3a35fa16f232f5a112af966827da8d4a2470ae79013da4fa7f29ff729965ff1482cbc41baa003ed7e78112e9a1936c332bbf639e49fb2a2cbd0cc686f55e875997cb0fc8e3fd8a1ad1d90ff561116777d10cf64f7f4017d6ceab0dbf8078b670102c7466184e95046bca9ff7ebbebec2ad14a717f217c983efd966d9fc7f7258eeae01015434168c09ee1d94d7e109cc2572762f234ceb5730c1446500efa941ec1244e66aec963d660cbe3151dee0c22f4a354a947a0e208897a0744ca1c96297a70a8c6a152b53baf4d0fd68810efa240417b361e3364645f33463e2553d0ba5d8ad1a5bc9cf005ff49418aa22f991b2d3560289eca5b963986c9291cf0bbf3053385654a77fb30aca326176f9d88c1371c4fcc3561a36c608955795ed892ce9d97023f6c98e800e2446786d96083e36f6ba45fce80e81b20beb563f99b59e7171a447e3610c8f94217391457bc3f404e7e2be4685336f4280c65f43de1483aea21939efc7ac356d76237db4d6b5ff04581d37f806d8c2c31fbd185415c7d8fa79e4ee176eb5052a1619bac3f7a3aac1db104a5ed97a79b2534c4411b4e281e1efd7a1a174608a8cd07324a7d93d3bce542c7cd5f866cce215e8b493a06d3a86e966f6bcd5e33c484ee3a2a1ec70e1302e59bae9837813a0db8ad493dd27638fe81e6146e6e84680bc759ff554e3ca3e36cbf8576a7592e784f7294b1c1ef4ec9c6708677d29e7cc6ad5bbbc7b60d6738ebdeeb64a27b2699bd535077697f0c84f73ae9cc517340c8320e3c4c6ef7ff60deb32e58bcd2d3d19c69a5474e224ece2e6ad81d41504c3910880e70dfe24531891e48b04a354d0ced2e5bd08e6569634efa39529936bd56a2e66f25200005562f675b0e8325699d19bd3fc5845433c6a5d39d7e11a3fcbc5ac3eafb33e2339dd0d3070d46fdc1e92a3d6bc7eb435c2fc090b489b9b2ab26d332684f56af025bb3e6c6431d320a55ccf72a81e1e4e433d48e5fff8a9b7b23987e2b44744e04240822268884de644c0eff29df7f7e8e9cce9211499ec9fbc8a57a446977cafcf42fac6575b2882f62c900a58b0222f772f09d18b6212525597d2aa121536e2ea61fb07794847de0405a3485c9a9782261c626e51fe19726cf10d9f19dfcd021b1da1fa2f034899b39dec05e862f271be5c33e809233e2e889899ab7fbba2f8e08ce5a5854a9ac0746e51c0c21cce066158f236cecd6cde7e111c7e04be2d061b394e2249865774718520a6a269cf8364f88b322d2010cd1de31027620a9878a6d1a28b14260b139f78943031f27bfc6cf7761b0b25b2fd73b25170127a399a61525089706b5304d733fdc97eb37b4600afbcf76722486d5f07e0a2171eb65f830bdf3e9bbba30fe960d7ea647226bbde7511f59c8d691925f388b4336dff9a757e59cceaaeb76a80ad2addf26529334cb118066fca8326ee864dcb65f00d915e09e5b513e3926062704a437959d3f164c0fda0ff4a9d6655fc924cd109341a5a8a03cc07fccd0421c2ac58410e9887b9eba579db890c3033a201a58ed524fe6f2c7c8951fc28558f7930043527b874d7c656f8310e11ad39d08ff350b1405f0ec83293f7a607dc5506cca4f928992fd53dca7a47419bdae2275c0308fd786d51e10940c38c561a02de4e1579853311ed96bcee331f4ecc0613bfa088b44427f3beadfbec72948047a9889e318a083f0619b0944b8e387a1a030f4f74913dee38d56c9eb39b1cb988f18495c72ebc3ee0742c5d0f800ecdc334758cd90e705882739bd8d042b41ee19c353143044072540a6d677db182bd75d3d1d31f9ec8d713942d5675cbf8ea2f7ab33437244d600964730f7556f35378ef5514647c6776839250d458595f8cdf469fa6c752d82142f72fe1a6f592725e67d84c22d32ce06f67273125cc0394940d57c4cfe7209c89c7f131508087775fef21527c298879fce03296fbf1b54f2f6dce2b17a817db8511066aeaf94ee6fda2f3b279f6b971981bda2cc6eccb56ed310653fd6f231a67bb6a5e9e54723d96a48affff9ceccdd0418a167329efcc87ae7784d3e9639ab4b05c07e757d07f791d52d65b7921911f6c58d35211fd8102f1b37e40cee686b7b4d96d639b83c0c161a67def7f1a00a56af934ebcd6d808027c06ade67d2e942c8d6791db5496c6c35e75c549118ec519d972590a31332abd179722afc5697007d4c2f052b529c2a5ca7c61b3056bd1d633e642b3b1869b76ad23130320c47552313f8dedb275c7482e2fbebeef6016aaaf4f884da14971c5931794f127bb1d380af1072affcd8bae44ca6fff407810d8b18e15e7d58b5e5eb9480d44c0049162d8faa324042b2a427202c07336f71062ce157a21edb7b428fa9eafd6e136955fc2dd634b1ac38b60c82e93951708676b22e095a3d8060d2a1726a2d8c603bfbcb0cfb089c2cc3462a518d62645e78e7ed8025f8d8d2202f260a9d14865f33db3f74dc5558bade12b989e40591d384a0947098565b4cd8e2c4eec087b30d306ee67e306a2673d6972124627e62079e7e6142fd87513d30815cfca2bc860cb5c6d34e603097062ae73486d9f70fd3dd0739a40690f8919a0b59e26b415bc22d878f293ef9d17795d941b37938eff3c0ca6011427cfa0e137d929d210db7c2b56cc9395c8716689cc55a71a312a682d3eafc6d970af39386030b3613a82b948e5b8110a5f17b15f52fd5508fd2098a8b0fe3292ebb21806d5f0bf99a2de79ee11f430a0deb1aad02c5c9035ae463d0c8cffc6d3df36221611e5d96906f60f7eb2be598602e5c763aad024d1f6db286d15ca5157b0fc90be7d3dd264928ce7c7c877fe61309166e7c67cbc0ca62ab9e9af995f5bdc9d7f95cb9170be881960fcc24a5adb0c590dc32494fca1418d6d7c966b70cc995e5c0b6290296ca67a5f58620df8cc94fa1a2ae47160ebf122897d6165517dac613edfee177f03de69fc6c100338d33ae2d376030e89929b56763275a991d9ebb20543615ce9abe7327d83b5fe21cdcaae26755b5a45cd70066618b6993763d15f25667b18d45c61dbe59e9c1ea923d135f649ec1ea4564d060d52268749dd6bdf5f314fa41e50052b4f26386408b84b2146f98835d92c9193168bfe392506a5475aefc46004fd7afcb3d89a0ee1ec872668b56caf264cd1862e300ccce62f457106941a30e015d2268b88db1b986d3f3cdfd11d1c2bbd6a4fc80733717cf5fc34bcd387b8c56027a667174a0d489f76fde9de0571cd77cd1cff7725ba0d3dd17ac6d1ceb2b404e6cd724fb524c22a03288a0645c82ab91e39c8bdc80190171031d13d71c4036a5c0331abfbd89ee988635cdd1e2e64cc0ddf7e49b17003dd4889112d8c823e713919d7037bce8a72940c8830e6f8b16fe23790d90e9e1d90b8aaf3273d2dc1dc9285057b34b8784df66b002e5b50d679f00d22aa2caf51e5ed33e13c84f72c7e802c18d1b72a0feaf92448325f2c5a8c0dcb0f79bb87f7027c3e96b3c0b73d1b6b32cfd2fa61840ff95ff69777ac574cb1f9c66fa990f2c2f1c934a43ff35ec3023cb72340c4a6a34bd5e34fa158006a82a0693011190b9e734a68184d80f53c82ae6b275facaa066dfab5b33192c3de965b1ee8b06a5c59a7380128c612e28c6df9fcb845678a6fc5c710913087c894e848872d22eb1db2394bfe968d7db50060c5a279665af2701f66901c7f0e81bb51603cd82d36641fae1c64bf4ba188576bb903c296d6064eaa1c38bf987a84f4dd6ee58e82e8efcec07345142ffc67c3f82916852e4e319d40f56e046924549cc0da8bc154efaa67019a7589a7653f5cfe461bb553c6836da4d45439b4f500e614f60340cd2dfd01db1e469fec14dd2e061a8c5825f06cb566bf8ae76d15d46fb76f3c4e9050ba0d727c123d7910028b17d8266442727344efa022587097eb1602419b52679140fa54d997cc2a7adb6dc5dbf2ebef77c5d857145b88fdfe58b294889c39c1ed02b3601697d6ea798606264cb3f44fdb8800fa593f198d9d3ffd46f47c82b04b5311c4204181482befb8fb745707bc48688cf1081231c8069ae3baa8837d845e87105f2ff66b9d36cd405a8f90634e6c3adca40682f8789c09dd4fca0c4460bf14e3ffead1641b88a62cc03ca6692094bf532731900bba157590a70c443a93e77bd200a54f5bb8076e174831815ca2172075086999c657b6a179516c27093e410c78cca7363bd40b4a4cc9202e708f1319a5a1e7cea06d1d719a624e0b11cfff873dc3ad954bc8847482bf12c6f37adcb04b78e9e5d31581c67598102c46ad058129078415ebe0370ff4a420772cc08d135d58e5701814cdeed13ee9a717ece02d40a7364bc162a7524d0dc0386a4b5a0e9a0df959b1a2d91b7bf76e34053fdac98c88a26d43a1d88b823c341915e3b663dd70bfa34b6f2e6218284ddbfac87d241851b3a7f9423944f5a41c02f3025d583014c1ee7939539ad6e2258e09d4b2c4e2c87b01f95285a9838259eac307469747245f1090e1be116efe2270dc1abcb05c9752987788f9b620301a16c9f08ffdfd91a714e9eb65398ec42cf1b1bd829b831fe2ccd69a9569f7408cfb539d6c4bb6b7e9c5f8b60215b6096ccb1bf4252131777f5d1178bc013bfc91bde8a45635299f56343e749ac3c87c9fdd7261ac89e7be9279978f19295e70e834b32fb82053ae5b607151e98231c3ec0e5ac0a50205216c424c8474521bc55d7f89ae2d0f20cd13d6db4ecb317026deb7b0830abd55b02ed3158fe33f350bc4c573380a1d5ed9a6cfab381a435259c2a1cf0e43a6751b844e7295c5472b1b2661481c7071e619b9a345eb3d4ee12b92363723781170591d0b476e36bdc1445ccd1cd82f47762e19ede548066490c572615890ea4cd7117e91e039fb1091881c25f93643db46ee1358ff5804911406541aab9fa9168fd7ca057be0b87adc5e21751cd7225c41eeea0996d2553ccc7820059c2e829eb71adcb68a88c785195838f5570a9746cb4dced53ae9eed09457b665c19cedb5fb6ca3978cf6f779ab650add7019e41ca30b4191b5047027b8c9cd1f77202f2d28f1320ed3b500eba4a7ccc15aa624ee1d0e10d5145c0fb4c189a50628b8024394fe04cb1980f04a2b824468529777d6daabbc62cb4ae7d85db55e11ec3e61ec9dc0be5e06d1647fbc2c88a7b221cc46a6e7cf8861325e7912a81aa91493117e91115e294442371c41764b49914b85ab042aa3cfb1ad1d59fc429bc395f84ec2cec301a60c9a91eb6e50a87f57cb3bef3c87e61169b0878f2cafe7802c7dc650b1e9cf08670ff6494f9f927fe261d22124070d17ed9c7ba89c515cced1a052b3f8ca12a57762eeaeba0b04c62ec01b26a9a767f0e29fe828b85fa281fd53f54b1778647b9da626031811afc3d2553b62edfb62807dbbfbf1a802e43651fb35668ef7ba4900fb5ed6a9e00a1f727aef5470ddcfd753c18eeae80edfb46aefd1009a547f23ac2a2f940814d145de0d714234b539c01458c5602322880a49ca506337fa1f1b5e3eae8e0762f3535c1b77d0bcdf2855aae5199207ee780e3839acc5a6a02108d60717d9586256d84c03055a09a4008f7c803f9697745513a816712ce9a9a1e9821a21a883c548cefdfd6af880e38eca3139f39167e276decc1eca5d07b4148da20135eed36dc00a4228864a857580c7bcfeb73fafc5f1effae3b6af2953429c3086aa3dc2d9ffed60862134a8d13a98ef8d20a5c47285dc0094be3dc6073a515021485a5f2fae450a20c7a08b6a5e3fb3688ff241e0d885028adae0b865cd07748f009410a27437349c9a6c053b04c82740584efa9ac45b1209f81d0a6d4c0085cd25cafe07555fb2eae07fa2eadd0a58b08cd4ad93e216fef518a502b3d66159efbde2c43e4d6c06204f34e01afc04ce4965c8fa490b99b8d65fb9399145df3f83bbfffbd9b3a261e9412eb0035920689af3d7105f1cb62e5f57419ae83e9edf5d9bfe85fb721938887496ebde388d8cd8abdc3d682cb921217d61a4259d93b1e7080d521e9562fc83c73c10f9a1ea994ca2293c085c6ffb4f89cf10e0432e4442b72df585d984cae1214e84a259d6edf8111a85657339b4e4575aafefe3a791696339d99b4f5e3888939ad8914ad35d901447f38aa343e7b7e04638886103d41c3e12e06350ff5ec8cb09fa1634e4d5544d1736556564594cea3aedfa3250196dfbea499254c15a9baa72f66a0f02896934152ce4a76a5b2c184fd73b2d7195882e1ac98f468b8b1e79dddcbbd949c77534007a83b3c6a4207b36ebbbe504f973af4f374551467cedce53287dfc91a009995468a3176a5d13dad7e7eefdd1785581bd3d1afae31ffe063b43291e55aa9c6f8d066da7d455576b2862acfc0c551465e547048a6a88abd3a3b5f138b07d0da7aa72e72cdc58016f184196f2a5a3737def33e9af1bbae4e449e906db438ad85a49aa39ff5f4458d2e483ccb9b08b55ff5ca2fb1b412e0e3029bc5c57337e8dc4a5d9ff7fc7833eda0722a5a0a2c5e9399419eed5098f60eb5e72d0b2881900ae1ed27ec90d68c445ba4f79d92b57d276d2959a1579b560e73a8a99eb9093c99b72bd6d99e251b0d4b7a0eda5c25443a992d9d2c2252f6c126bf773dcc87f1546a27171d2a03461a975e304a6c33c78ca2f14cec348233e1d08c257b58282ca48ee15367091d3eadb4257e8afc2dfe452d0d0906659ba72309e798323a2c02d5740959f5e9b800120a86b1fed0a5e005f504df5eca686082c27b29b7c673316d544226ac6f19ea0e86fa8ac88eecf60f41d8f6fbbd35669814e8f0470030a95279e05d59c68dbca5c8ef194e9b4bbaec3bb5367fbb8a874d813c05a746535416f8e470d7ad17b95a9adc1f6edb33a36759aedb7859951a0c8722333a0368e07339d4a924c54816d12068699cf653540668615cb65cbcbd4bf997a0c9536992e453472184c37719a63012040e4c7267c146586ae89d6c5b16b8c0d157c72589cf088806fdf68ddc5550d5735c2d099b862d6ea8196b99cc66d0f664d10bbb2f0e237489b21e4168a283a13ac1e90a00fabf27b39dc31aaf6d7f05a93625a8c458338fafac2b8eb267927fd33e633200549dca66ad2c9cfdf4d73180e55a14c3a1cda93d01307c445eb9f65231121d4514d50dd736ec24f6d46c8b2f0b0d073ba5fc8897ed8a718688885e8e25c58babae0fc70e430cf061bc49c3d16788ede58381a10be282676b5b7daed2fd5a85f0eedc15aebe8b8577a6ebc5c75904fa6a6a3e37469d1d14301aa58794901d557420953ad8e0eeebb963d81a3237db2ebdb7b4c2bfd75e78363b84ec9691f8cc26e320b40976489f5a7e32adef9b7dd497fbdddbd047fca092804de02cd7e9076425ecddc2ed56b6e78cdd28e3c86a5bc2c0dcab0e80d0009eb10683404b159393404a346f22c295e2618adbe00c8067c69436d19cbd168c8a401f666d684c6d588bccdf14312d38ca385f5e8aaad8e65596030fe1ebb27a265eba9e6c18a87d4b556c9fba9cf0fbb7b5b4c2af631420030fda3a0bb02ba61c0269d2b240cd4994cae002a5e28b12958dae21d271d1f72cbfe8aba05e062bc24fa63a86e54901954cbd7324858f6df0287655e7ee803d99601c778269fdac366bf8e865ba285d89f7a83f34274dd49cde424c8f2ed9aedcdd3d5e84167588bcf9b916e608106dd8f24e07176d8c9a5969d04baa04ae4cdd91fd7a19bdf39ef17b3409370288efbd8b2cea1b62f430ff5b4161d7d70a0b1f84be657cbbc879fd7ccfed528666234fc32cb61cb229f3cb54185e62ce52cc2b1d16c5562b0fbfcb668837c7bb1edb4c416a83069807a4f0c925b252a4cf77cf30154c527f4e02b0b9d046b102b842cd7a2e21e16a2dd5a611a700d818ce4e431bd1a7d28b4a51ab0a1d0afcabd5643a74a3049747ce7e216eb6a3dc306e4504f6f168d10c059b7b0b06297d87d52a25940a4b9057410ec375d2f49b3d088726a2f49349ced416fa5698ae9b648af3d451ce8d070b1d4d170d2da71d27e39c21c2dd10a55ac857d7021d4ad954605b254ef8e7f3368ea13c03b8e3f7cf0fcab6e6424e6614b5028c375d14485fa822b050f0bcd69ede7d7387a86b1b04afcba71f0d1db5121255536c47258285263f76a0ed77b027d054d927c4eebca4725efdcad23267b1d5cf075a54f6255c9721f97ff445a3f3590af12a6e399b541f84d2de4bf76c77683e92b8103ac0c57898158993ae058d98f026432f0950c2a672397dd8338953dde406a39247459255c3716af849261a740662c226568da946e4aca12379e9ab2a4051bc830b26f474028f0b0f58b619aa537e3ff2e9ba043d390107382cb33e2fd6f98a4fd99bed432a0376085a306b3c88a733a40cba822b0caf8e76e5b3590f128c302a29b818970c3446bb66c755fedb8b70783c0fd5c200c027cc588cf2da6113af36aa38eb31cd1fe8376c9847abbe68dc04903fee26777612838b529e632113ccfbd8089e14b9b263b112f04a5647d09d9460a21bb6704e0cc1e46c9c95c98b067afba0f38709cf52da6a50dd5221dd4c658dd83de1965dfc92923dbe8275e2e63e59659023fa8d85be2fb3f7b57c2c08dc93ca85dcca34976e9f60dbe4b59dd5e2644b8db9182f2255047e6005f90e91a540ad0a52959ea27035b7bec468ec6029270915bfff07c67372a0d487ba75ca9f3023c0f1a352a1a604783eaf406ce6f3087d79ff32433568ab72f406321de35206f76a929187981246bf64b125e4aaaa416efb871f6b12f614421ec8e90b7ce0ce5444cd3c2e5b8642de894206b5791fea2271e152d11e8e9b0527b230016b6d94d1b5349efe86c67a3bb6272496dd4b5ca0d79b92e07639b7c51e256891a78f2fdd498b7c7d4d1a7e14f46ff1a5afc6f33c3ba46e5a4a2407920be8e16ae7cbc899186fbccda2847961f1debe246c32f8caf07ee51838cb0c54b03a7818954b27141e14176d9e8164f0c4293ffb2114e62d105d071029ad16f2ea3bc58a05db42c68691886bec094263163ae675a1662ff8c17d740bbfb1a8698ea101aacdc6cba4c5874de83cbd43a4c397964f4ff0bb152148ebc46befdea35b36e7b4b0d710d88f7e839781b0dda5b5656c04e096e91cc9f5b627e935a47f0bd0d95a1d05b3adae1a8ea0ee8d0c380b28745c4f69b4fa77318199d21b92f795e3e049f2c97e4cb9e7118af74d6f3c8c580f4f8aba7a84b0a21b13002a38e54cfac90fd5d026da3589f88e518fcc4f9ad0315b186829ddd470a8cb748f9f2a73edfe116fc71f418759d4f9d53d423fa3a83c78eedbe0dd78e77bd16a48ddfa0815958e268191f1ab8ffa56efb990bcd6758c436cefd800f530912320aa9260340e8a13a4bf8fd03d7b6be18de54578312daae396641140be32426c8d866892528f771e254869a4e02684727d6261fb0d2b0c7baeed3dfa08dcf1773bcbf50eeab0de4a45d22036024f5ee2aec11c9025ebb87c9c4433e00bcaa34ef859ed7ec3618b1dd05f38f60c236c92fd6e984897e83159ac1f4c27b8a93a1943d8eb2033aaefbfe433f817d5f96f37662e46cd3ccad7c6f8492c409ce83d5a2e92b96c1920b9455cde630bd6117cf4f0bc923fa88b3577c0ba552c7036c8864f00d044bc16943c18f345325aa9ffbf3e23b34b923529eedd342516420b3d49ebe0b1fc4b5f53f8089451b292fdc0086023f7a56cdaaf209848dd41af71ca52b1264c066578a52aa422e476ad50757b178bb6fc8d96d5c2ff98097f517fabfef6230d95ab6d2fcad9874de1f4f75b66ac9f891fe20a053a836b8b8aae8310dc842b5a7977b4ddceb5569e5989bc677b96cfae13157b59f150e413802a49d3c665e29e861793a0f37cdbc732775e74ad774e7020a944fc0a4cd78668d20297f03b85b17f6f8f7b27b33cf027b3b56a0fca69cc9702a6254ec9f099e51bc0596ed8b8682e63bd6c707ca7134aaf88b8d7bf7182588430d6cc826c30c1c777a737a30df22958b7c6fe2f2545359342c625469c23b2367d56deddd6f52c3cb7c53aec80d8cb04c845837b0bed2635e35bc9581626d0c401d921f70791fcedfe468b3c0ca411c6aa7731ddc38289b504d5c7b04b56539aa32a8ba1dac2306ec569471cc99c62a6ac3bbe7c53072116833d27bb738bf241a57d324801974d9ced71de5bd6f86fe87d690c09d19375de49fffa506e0faa6a5d70db902dc8738ff9cec9cc8f98eaee2352a2fe31e96a41e0b8fbd3d9930bfd0991ba1bd063a916b9948dad3ebbe05ee3838c8181bb8b5913bd7cc87ed140377c0239df8cbe7a4e8c3fcfec0832691b8978ac509649e3a9c002492ece7f634bddcd5c666b3cabd09cab9542301805c4c44852ced8558479338d025f9a866fd13ab9860f21688349d423252e2b8ad396d694e675e66c71be464aa7937fdc28ce007d0d068a40c32922a4f1bf44e4e1ee151ff022fe1e98a2f27f7cbb8fdad0d4622034a068e5e2b0f35621e6f08decb325b2127b9c3a67b49b045eee91d63595a2414f301692613776589126ebdeb48ca3822c2e7c82ee4055a7e2a77a924d6c16016b3d3fd1a0943a05365a8b6a36746cb60b3c091486c509d438f0edc7ca21dc9b9ffb9dca55568ee1d904c164748d1c9135381179ba66970be8e66b52e159d136a58548e1b7906864ed1bcb36fa2133ec68e89384eea0614eeccaaf092ddaaf0ecd27207c12580e75613a99362b3c45a37955a2465b518aac14e84dbfbfffa0edf03d5792530a9f1301d782a18aee31badb1f73e55a75688a83fad529e3c170a10e68103d3350c10b751f92c6e966b51f67ab50a71b8f9ad7df0f9f6466cc4a7a136decabe35bb9da17ebb9fdc52afcf964ebd829f54b568a82226380524a6b7864d4bb8b49c14b914fa1c3988288c0952822cdd558b6f666f05ce665974f08ce12d7886540b984ab0d49b82bf97a78fe9ce67952b5ab69e445a3a11d014bfab9179e9185c8fa08e4a89aac5b352b90923c289856e402acade19cac9e873701178a238cfa6342fb9531e69c4d910e9a6028913e02b125cbe32b92bf75413a79b242969ccee669baaa750360e999b6b354a62c7c255545ba8f9364734525a51f01dc5ca217b2895a7245178c3d9a8ae423791c48cdf952e45afbc8e13f033da741f112b022bf11b83f09b5233e47bd08e26dca8fc799f4012360ffff40f39799f35d6cd58f3cb8e24f723733e98ba5a1cacc847eee42d66fae88611dcced38e2a4b9f3b90060ba16f7fdf47b059b4e9e9136f26ef83cb0e3d5a38c7c918e7b3eaff6591b2611c70f257a78afee3df0fe4a7d0bd139f1525f38f7dff42cff779fd80b56a866129dfde163d109a6ffabed98584e9c63b59fe5bd8627380488c7ccb7ee5fb4f689a2ce2ee2a60798652614739a03e55b14810515102bf482c4a874ad26dc12c6148547bc08e6a30b250a4922ab6f0e028edd561dd94154f653e5e5e6f7045ffa5707e8b6878a3ad80c4218eb30a81e59b88894b20156099b781a5f0cb0a68779848c29b5fe5c9ea21c7caa6806bc59062c553403bd0f95432b20448550224cebec7af7da562c18df9bde84a0083deea7f4d33df716b96321b6a85a49bb124b160e31ce9e66667b55cb5acbf6a328ed4227f67e0229e87bcf84bfb87fc51f9ced5c01f94bc8145dc00ca3bfd55a210f066911f141bf73535d25517940cf99a490decd780d90b056b40f0de065c81d5892bca9203c5a389c81055e074d721f9b5650a56f14c87da62192afa506929873c1b7400e474afb50c5aee331b99171bf34701ba3f2e7437882126bec4879a88d008a9e15a9d966a0ba99168ddacf1dc580071b342905a860211bbec7fc228f0fefec663c2a47833bedc681d431f2acc9a8d49dde5d3a80c7a121978878bac61d287e71fd1073ab2049f891c9284c64775686142943151e18a43e8478c79afb732e00dcc4723c39d7462c214d17b1c962e292c5bfb523b8fe2f69557b172b3d75ea7bc8c7ca6b39baa2b7df39c60b24f55489b717643a13c7a14d12ba7261d51a6eff2ec902218ba1f0b2f1cfb565321c2ac52bcfaa2e8a04a926a6bb68798933c341e27b849ada502c6f7ddf519d44a4048adf8cc33142fe0aa37ab1432ee0a8dd0d13f818fdcd43cf094b530e4c6b03ded5e7618ffa0077101cfa774e5792fc035935c5409b96c651a171bae7b6dde75e8f268dbf5ce9fd45a8df82cbbba09a1fde250c7cf0c38ffc772164763346eecc70561250ad11425ebcc18f242b60f4284233d4192de62b18bcfbc334108ad625244f59b85d6f832be877f6ee27ffb0aa193ecc9840fe9d31ba649ca37a628f4021bb54fb0d0d51e765e2b8ed0ebfe651584389e243c92d3925bea33a40440012bb4b3cc6a0cd04ad399191b3e604792c0bd8e852c47b0cc82fb4ed93987d2e690c783582768b4bae6dabf39ca6c52ab54d06cecab50e57363ce9a15d3daac7108f7d453339b506d4082ac3ffdc72efe31d2f09641c1f43fefb2352c459eff533bcaa8128d64ba92adf7f386bb66746d42bf86fc5a3dbb3594e0f1f70f1855a5eaf5278d89039ad6da673dfcd85e3bf0486941cfe804787b669a14b2953e51a2010b939c55c0cb88bc45c1797fdd98b183679c7827cde82330d8748e619843e27b39f409ac8d3d7f2ed4722d32a107860a7a8d65a3c2ea9c7d049151d67e7249361fe976704f5c608f182fee808fa2d13d45444b4c2637e232d1d345f6e81bf0a99de933c905eff11a0e814411c6fbefd1b90865441f35f0f22b3830e5ed82ccfe31250b58e141863c8f195c8b362b1f8fa6fa22969ae7d5791e7baaf6f76d91ca417fe6d09b677137a58fa3f915fedede635d9b00eccf2372f96e629cff38d37fcffc1e55b979fd25d7884227810aca21b81c1033868d6785498e8965b984316cf92461642657bc3eb022fb9d5816b87695b0ae9b588794e0d1c367ad1c13cbffe513d925b140efb9810c2b7aa43f8f6566e836b0e533bab39501e42833e3d45174de19762082c1d3f6ae67215c4ad82b89a52e9fb919fd39a8566c7215cba37a66b75eb77cd510f0b7195d03408eec61ca2dcfd58c0686b06f25ce47fe7434b7a0d83288ebea5835be03439948ed34667a0a669e69920a49c2528954ea900454c85e95480783e1e108c62d7a129cd64d47982cc231cfe5889ed0a5e3269954fc52a1fcb9d9f445e5dbcbcd1acd5f7c28fb3b1afc7ecab505621d065785e42a2f8840013a3981c59c5a69850b511f8ed1ec1fc5d278f62b8df52ecba2615f95cdd93363ee66b0353b203a62f078d53371f4c543e75afab5097a6784c09b821d707c724f72b9521cd002306c0e689190f73175c32f7e2a0f7765a0cd6cc0d792f99458500e77fcdc05603b4368ba6c475ea5f52bf5a15b5399ac5550c5d29022cc5e78a7199477c8aa187a73bc9b4e11279c07966d767e1222d577524212a2bb4af7ace66bbbbf66c208c93a6e7f273e643a5d1fed25aada76af16c8af328557a168746a21e96c8fa4104494b06fc424bac3c527cd34d6e05645a3bb64730209ee3f4e511a1e0d8b4ba8b611a6396e18d19831a37ce7eece038ad0b047af40167d1baa8fb1a714443049f6050cf4e5bbe66c702f6b9b1ea8fe315a770a61d6071070936b82a2ee3fcf122c3b99030844c03383486b2b4360ddb3bceef47f509262d6b893431b917fc6bba49e0d29cd9e7cca93f86ab901e036efcfb090257800fa8f75a9605358558b29d1e9dfe9599e81a9f1c0fbf18efe08e5ae2b30581d3b30a88d0fd0015bd2287524f22605a2349a0f0afb8d1ed51d4217b4af7a5b436d1a82208bbe876e827027d86f9efd2decce42431521bbc7116a1a96706690d5898ee4885f408d9d7e2c9d9e70c5f80e2ce86cd7cd5219ace07f3ab0d57440f8a9995238b8317dec0e70e2d5501e652b774f9e38ef2e85b14dbc2f5973572c2334fb5442e95c49df2c3eba0da97480d1be59edbef0c5fe7493ab493d2fb1577223a837baf115fea93ea46ec003055f441cd7a7802519e26117fe96e3cd94b89432aa569f01345275b999a82deaa782c82596bf64136997016042742b8470c7923e4e53b4cd6f67d65d3876c814ec323370717bd007704f4d31b7e4a2f30c779ca50182a76ea05976e80c9b8ad43a379874b9f232add1ae11425f9ab31c8a2e61c194d9652014f76bd45eef1d637a329861aa1a5d4d9d9a0a3a9c0bc9958a5de3bb8ad562551f060c19f53b7a9d5075827c96df545d54d072ea2102a09f9c35242faacac9b7ae6f9c3a18ac8766cc7e86058c21f506593b8d3951c5fd66d45d0d68feb294fa665aeb5729b2b91e69dbb996bdb135224b2ddc01f10784587fe159e9b94021c6ef12e992e287707abb57626070b3efa112e0931396a2d0638f9828bf97b35b916ce3579e75cc57bd32b71c67057427c1fea0d1c07af3f98e7ae167ba8ad2672dd8031fd4c68138bc1281279fad9f396489832e504fbfa7878ff3a3ba45548ef35f998f9c72fa7fa15a12a31b72413c4032ee23afe2c259440300ebbfad58b1ddf2f49f49e548d2b33f57b6e264ae4a436d6f45b164515e511909cd0be588aad1b6b0b12a3b39f8c5adcf4c3f97a5db00c9570984aa88c6541dfca9a59ffc47f349d0afda1212a5e5de9a8e3a2650c70d49ea0bea600c690f12fa7a7a374ce1efefab1700b18767dffdf7a18d745dfa503607b5d80db8c2b9979e0193e293fdc7e5f56ad7075a6b2a4fc9ba558daa82876803604528be9b680c536c299abb69e1ca0ffac0c368609fdd8eca873530ac1c0fcf8b7859064626e4738ba6bf5c0d975e1ad20e6d8470af45b5647cc482bd5d72c17bc2738ae2f546ba7b1955c4104abce588a188469b7b8d6d68213db506c9528a0e0097a41ad48884fb1c277542396ae4b1556bab97c8b43e7e151b3b934734af5a0641c50cfd5692eaf406a43303cd3332f5ed9bc7a86f211a9a3199ddeddec168a2543e65d62ceb0bf9adc8416fe55ac414bc45248ab18f108a2b8b91abd43bb9e51fecf59cda11da210c64f4b1ca5699d4c88001d36f6e454bc18e681fb74f25b11e0b78a1ec2871b295878504caa6a0a4c2f9f73372e6135e77a935a2aa02257b5bf97731fc810133f1b94d1361e0dd6ab4bfd192dc2ee19784d56d89aa88d8a4e3d1dee1ee8e9c46204a90efab2c487bd34ef123545c78dfe6b2c43644778e4623b2569c5d2fb15313c29ed4c67da4066f9d7a477579c51a1fc1a664cbcb70d756eefff1fa0caae113454f680ab1abc608d5bfac567eea515982f3d1c704dc0bc334186c31f7c3a15be805a102242f7b74d3cba5d44b2864d2cdaaa35aa9557031fe80a505dd6a1e38d07ce141cb3eaaae773572ad0ae0da150232b691ce5e1966ecfb8172dc8d4ebdd561903a89845d8f7d0ebf451f48b091843a25717e288ebc41d1d386c49a21ca52b08b2947ddf08c0e8dddb0b19e5a81c79feedb4a6d7a79789786643cc2b15c6197c8eeafeba37c8fb048aa59b5c1459f246c9dd2b060029af2f4bf2d9488f1b1e6066b10007efb8a1c881e282f1d94e40bd3678abf225452f0cfa14205b50c00b646da57f422a812f4a6d06f104bae096fb8b602b44e9edbb9364483e64160f0ac9d6372f5b9fde46ddd66720dafcb2c72bb20ae5bdf2e4b393fb766253a421cb626150b5ab29f82e5ecf6bccef0ac41ff29daf88c0e41f17470d07683caf2390a4faf343ee27f62ce47a2cc7516f67043401ad2b4e78e27eb45008968aef6b428694dc94e91c21d4134a92d8df7c58bff56c5ec09566f0ccc536af90f7443dfdbe46ac68d6af05adf6c5930898bd67fb59dbad6fc3848c2d9fa0ecb680953aa86622944bd444e1d37c3f2a9aa8e941e3d818795c289241f3daa9f10bb677f8f81f28dacb241903e50ba62087d490f5bcfa33e14e2da02005141938a9d21659d4efaa1111ca12f436d1af6951bd9206fd6fa3e6f081fabf2e761f773e3e06c286ae894fe384c2883a9a47ae029ed300c2d5c2b0fac36ae4dc627c21311bc8751497c16d80562d155fa24ebb632220e2a34eb00f4606b9d9ef9e9b36d10d620e8f86109936b15705b0bcf063dca51439eb924945517ab7c58d9a977fda244abdea86ec0f23813573928a61e023e405690172ac051f1edb7b24c5c6308ba9d1a11d27fae767e30397674fbbb62ce0512ad44a2854b0cb4e489cdcf4486c3f8a9e7d95beda4a7c72190ef54d4fd619af3947ecc448574183668b53a558f8fd06eeeaf4745414a438966053191bf9fc361c5179435d352e60543947915b005ca1580958c45e9ab6e214e6ae8d496a03ef756e0654e6cee3243584b44ecdb323883235e71c450748ec639bb855935022cf885d948ddfa36c217e0df07d6e396eed5df4cd1e558aa3586e7ccfe2113882919ab5c8a61c67a5213ca2d13ada10da23ae3351db006ddd2fa7ed1a099d40903caf0a6af099b09ec1c2278b8f249e1e4f2ed1b5684af4785fa479d6871e14b928367d72530e08efe01254323e4300851f16d49e499f488b770c29daddd2d03d11663bf45d9559c40b5650822192146ca388726c78abb37e31b030551704c449cd0b8b5e0de70e095b695026be5cfaf42e8f70ae107c27fbd1712de3651da635d705f6db16c5ca1df54dcc16fade1abc3dd780d95d7847e91f61f08e0be20d437869e747b3d08e9816d1b7283c4591d01d895934eb9532fc197b80f726fee4dcb6aeacb794d0141bca3f897a9677fc3a07dccb2b9b45b0b34f0954e293f33d551751f488040b1332a054f6180dec1a9bc00067d477496dbc51fb0dcafd3ed9e398f86dc2f8ca0da10ce315e9756a134225ee2de27ccaee666122bbf705979017e9ac7d0df6a8a71876ec183912b8dc85c938afa65075e812880e12dd8e9016dab64c6e1513951957c369385d31a37b447151845d80acd7ab713782005b50750cdd72561a1fb20c5c403c03d70994672734dee1ee7ad7ea37c40454a08e45b327f5abe0ce854ef6b7420c74548fe60810b5d77cfb3ae2929392a1283e1aad0e4932ba0bd643e445209c8592e92e108738601ab42c45d4b242764024c93d5aec86b5badb670d3298184df1af9b448eda102e69899c20462ffe6a77d41c0eb487f94429b01994431d62d82a233dce7570590141e5941210157f418f55590fcb91b67a8429d6c242bcaec5f1ef1a6322e558fef7a4d35a6313c3f1d3b0aed195f795b5214e3dd518d5f2eac58d62ca1ec03e986ee001459b39c59e852d09f4f0a8ae93b87dd7b4b2fd9f099e3cf76241427a8417639151cc9f059cced80689ae8c820ccbb43a78e897b4249fbbe68da16426ea8b80e55564158a0b3d4279f4b2c25dbed9bd8ac9ca404a9442b6c029162c9ed5cf92ad594b05f9359ec11c4e7f033b12a4b0c7e4611446b1cba90e9663209165ea4e08e438303e9c41756ce2088543809738cd47d69124becc2bf3543c73a0a20f34aa28c2092343b341dede07bb9be582e4eae67d6e3e28617ca1017c0ab709b1883150e00173d796b669344c99d4af6321db55c6d4e37573518ffa9130cc5059bfc00e26a992436dd472eb472c92c1aa135761037529379c70f6208b0d0cbe361d96f01438b7a470cdde62d57a21f68c2903b80659e3ecee18c6ea09a93212fd1022013820a848887e2c793ec6b08603822d240446209e0106208f8250d9359d38418bd9ef5127340f7b02d5dac2ec9de7ead21ac26778ea7851cdedcad5388c38b09cff59c5b20df53c9c3d5b722e1aa7f2a054b07230653f6257575d272d2866ad450df7ef308bcc90256644072f33f6fd4c45b0f43e113c6581c23323efb489636139c4bb6446855c32107c79cff206900a40ad869e5406ff76078a0584fd120e986e74f31f58248ec11957e12ecdbff31db9f0df83697907ceb658a103880bc10bd094c09a4fc8bb67a08d583e1b6030410de0879ad4e27d3b01d1ee7d9498467296db57649b1ba5715b8c5f02a377046ac2c145744c4f2c86460ee7cfc401eb70b7b8d5c6a89e46631673c9a92c72a6ad646de1357d1151f78959ea6663aaa4e3baa106945964c841da56769722235959aecd4f41f71d7b0a9e67e768e9076f5f6cff78636a56ef50461b206fcb51aac831a88b3d620e96ea97fafa84148c996e0cb438f92dcd7c73770403a700cd7d6265ae4bfa9146fb92a91920765c017fe32470629f9a0c8d270435e84ccc70fdcb5f9e60e08f67892510a73f0dad9ce881645a9289f6e825cab1bd9f17a6a50dfd34307494bd802f6c0d33a9223f6607fa105d2d266b0e8a000b4c19680c4e48197c9f946bdfe12ccb36cf1b1d872e39bf66e820cf87e5502c3cc619f498353d4f68b8f955437c862beb4536e05cd334f91276b538076416d7383377b9683949762bfc27b4527cc689c10eb0b4dbd2fc36d86eb8c66040159493af985fee6e453a19b231d9ef4bfa7255135eb69bebb3ab118622fa564b675877c228628f990dae594c0b0ce4450e2f1e8a79cf952006556a8fa595fdd69b27f138cb824225f7d051fe284871082015e02f88b7181d42da7273924833e83a7f25bd0e756e446660daa451315cc6d3873fccfb44c48995b2312f5cd1b7888c3e9ba4e994b4d0be0172e86255bad46b6a0882b72a058ecb33a98178e5dbf0574cb3e78051e33311ba8ace1aaf6749eb8d6f7ee6acabe78ce83787fac7ededb6e97cb27b3ebf079855d9d038e58a396d976c317fc12a22cf1f825482525700a9116a6608d34be072f1fff4b354a12a8011e118b7398f5b313f93cd890a8fa37ebbe0000d7c79b1c73494f3adb65710cf7c07530e6f84b50ccf78f07a892582c0038c9a4a5f54c378ab481df92bc52efb4c4622ae599e624eab23dfd01390e368016f51ef16565f8b705719bca931b903c2ed67a0b5499d2f0e80d70f1ea6ca8f0e4b00f3001abd3ab019dabdb0d27a5b8371645a2bc3af272956123954ef44fc4d374525b8870caaa406d6a1a02a036076b94951b47c7a85613c64566a7e062a281159c5eab1e1aa4f762d7d4b80741c4db5d2621b5cdace15766e3c7d3a45d622777d515438260d0dbfee7c20aeaff1e871cc49f14f34d9af756c770de3d97030bf404598cdad7ff769d2d988ccf32c4e2d1c05b692072b29a9a4bd7931a48e6920ccf8dc67b388b85707c42d0f97000b679003809dc1dffc801f78cf283d417810009adb263a7492038c96f56883040981399ce7071fafa18f824b282d78e181fa9d6fca04c0b490e20da53728f1fae0ded7aa0a70926901516ac21b6278d7789cb7d5f3de697dbccda521586c4784cd5d03232c81bfcc1435543f38051a967a268b7f5b0657216196905931d67e55d0f0fdc422b03ba133008cd80560806e4f10bda3045d05988836b26fbc63810c72ba8834222abe9e5de15d42c576aa49055cc9e6ae3f8f779ba3eebe1efe1f4c8128a379f45bc644b55a187d89398f36b0ce18a1bf4a792eebc56be9f0d4e78ca87b71050a7cf7854a46852369ff8c6200cc48077f718e0780f4844f4baf37b83d4ee842dc185ec7c9bac80b1d92ecdfec0e61d5d4ce7f993f4203924b0a3d189bcf2f0895e4546851825ced8f4cd27b17e395b5c99f55e9c6764bf75c0b859b6b3938fcb0cd874412041870d5caa627e01deb83473338aead441020352234099c2d0b02285b81552ffb7c97456e7cd95c0ed0f5629488739cba6b3df6a59ab56e762735408ae8644c9935476a936414395d32680c016a78c1cea7abfa592af008e5ca4998b1fbaa70855649bc600eb2db47fdefd6a9b9940709b6845d7445e63e4e876e419ceeeb75dcf867d10b7f84676346acf51bd95350e7cdd6db1d5412feff19478b42b397e215e97836212d4298952df4c07ea3cc10c98a1cbfae25df2c10c9bdccbaad690b95af5ddee84596cce91f43a6c0859ad5a6861fb5e3fe5ea2fa4b771089800300ac3481bd18025f2c50224cf976069686edad15ff15ffbee442aa2046dd612b16ff186703fb4d12c48d51c46d740855352ad68078c338ec3392a08fbec0d6a8ca57a9b5f1705eea98644d2123c2b75f08068775311560ffe8241719c111061ae510a8eb560ddf16235ced4fbd75dfe27737a1ab97508ae774da84e15c377c8803510053224aeb1a14947be7215b9528c8d8ea637b99ee9b643ba88cf40ffc29b0635f2c4ac02761a6836328a3134497737633a3897547669d69b060e8bf6916aeeaa162561e7ad6976526a27107e53c2e6db9f779d9e29fd2433940751cde87ff4b51a1e13c939a0b8954f3505c61a2af540f8fc855efb2140b6a48702e3a679d2a8e024698ae926cb102e19c3e4ee542ee275f23f5869245cd7469548ba9f27392497efa0ca77cef27609c1af71d8cf765d850331139cbcf21e1281e276be55caf1eb77d9dff6f2e60bcf387f8fd480e0bf5b672bda984846747fcb4b4e70059463388655d9307e025cd29965df2598fbe9492ce58cf0772778a2ae8208a23a0d127cfeb473fc45cbe9e9cbf09351db30e7604e23e80e363df5065e63448e0f557a62a9ca4e437e015a7ffbac6f325f9200e02bad7bc9474fd4ebec493ee445bd69cedd2462d0d3dc54801fb6513dee3c98590b18ec1db32871e9d0391d3b5ddcc08309e773b3d7e95d22d620013ad78956082966a3a96d42a9febd31644142b425717798a3805e29eb74ae72f96ff22dfc6125395d9c0dd48eaaadd57be16baff298698c5c1147b7bf2bdf68d68edbc0cebadab4af70bd28f3040fa95261ebebb034bdb4da1587c6bdbf5d55b5915d50392b82161793e046f641a9b1a3c5755cb40d0d63c8edba7aa85cf34c1a39455f6fab912341830b642a999b2e6f097b45bb1b8b58d2dbf64d23c075c6f4f861b1636b4119330d5216a16bdeb69b335f3b5c026b608961c7fcebdd552469bf6565f23b6f8fd2e37cb7f1c8690684872e4199541127d7451504fc16484d2d55ceb8c01b044cd5e59fd218578852a642def896ebe032e7f145efebc9acfa0a2718c9cf05c46e61ce72a14bfa95eb5938753f54d198398f482a5ac478a73e85547bf4c795e9dec49a9dee61112f0c56bda049916cf87a08a9725b4e36bea35dbcd7a04660b93293281443bd146223893d30c6001aa84d6fdfabde8e4e9e99f982eda84d6c415ca00f09abcf2dcbac570b8487fb3ef7c02b901257d7126800c774e2dbcebc86c194ffa51f3b5e535a99e9ec49f87f838f43fc08798f5cde58862d19b60a562c6481dd5d8327740780e66bfa9355f987cb4b3994e2ce95f69f07aedc85e6de06947844eacf6a6cea5a8a35e7984409ebf820a210e06c9c38e415f18003bb4f14ca5f5676812f7ecc7f80a0792b92b4b09defe4d55c3328bb56069166713a63b66e3732ec88fa552e82d1f194c3032e83044ad6cee730ce42868104a2bb56100283abe16fcdc1b39eba2a49e56505e2b41b184a924d6460f482ee832f87e7adb26101491d398752a51b0b47d35ea882ffc9b5d35c0353adab8b8e4e6e03fc0c7b5d24c3007b52914c81a0c0a5581815f4212d678ed378b8359f5995e1102e9437a1dd6f22e999a8a03e5c1eeeb91a11001890ea72207762f63ee1d5f9b97cbfc093f8b501d916ed015a5485f418009a4a6f0065740afd2355cfaf4ac877c5e366cea61587cf8ef0b3986fe7a550f5b2e9dbcb1de625da99923482828348cb31ea532b9a5d6f9da20f16368276a3727bd9383a5008b5c03fac55c4acf09fd243056004694b075f044049a82c82fb8bf1b4038582d8b70fe1ba995b08ae9853c19b2024fd204bfd9442d18592873986058d73e6274a709c300128fe18a6b64e92c57b3fb947005a450f34f0c2f67d13c41e0d0445ffc4728ffec155fa60fd1230394056e61ad1032650961e6b92b42922301a3223a02afbfa99177f3245a7ead074d4e1bb47a28fd4ccd104b0c6deea3547e73fe64385b2a4573a48c6798ab5a8ccdf1daf7a2009955fb645669fe5bac7eb699debeaa67062600e46e5535430303649d6e0dda4104ff28394e383fabd9de6625530aa49a9dc2c04cbfa2eb10e6d55c485bbd094746e07e9798fa5eb247c62f455164049cd7ee08f462d4e305dae8e7940cbe2612f75083855976b35b2d3aebccffa4143a0a3c4bd121cd2d96be91fe85cdabeead8548c3596a25ea49b0102a0b283b15edc4d87408c6b71f2444bdf3bc58e451e424a06c7eeadb58bfaca6068f35ee49208e9519dffbc0beadf6eee63aa35778d3f34fe158cbf0cf9996f41a0d8e1734ec344a8e194397093b9297cfb55670d1b75f576470d536af2536ab634e937044c50fc00a89b69994cc1c14f1429ed4288bbdc35c442d2f2074ebc352aa39db90bd322b9e30d1e0c3ed07d076d512616d11d98340ea5d3ff103c48cb48e1e0bb3eba1f22240c710ddfb2587193dc3a1ee2b30afa89b3b3b08812cca3bec66d42ed0514a2427a391ea0ca1cd83783eb0fcd80ea82dcd1bc048b24ea80ba677eeeca055c160796e3a796c627e75953b5f801a670bd5c271ffd43971bb2636d977338536d3c19ce356b31dcf1dc83ba8aeb7f575b9d57c39c60e9181c4420bc6c8c0c7c9cf19eae9f8b8f2894cf79f1e0a6065f7e0f240a78d887a86999045d185251b9d0ecd91656ea3879930ed919094da8f68191cde2ff2798393986e56342e641407916c8ae71e321305385051f0503e5dfc7d13c3ad4c32c3a6b0f48f5511d2b174be17f5b5dc7bde841f946ca2ae142d0a0714c94e532310708340c97551dba5e6daf652219e5c2106dcb6802d8a9a72dbf900935b10709ca25300c2745dd3f5c21d354c39d6b3e812a75f38a6c1d6007f8620a93c64e07e96b582e93a6f4199fda4d5d6207aa67bb31e524b18bbec64e09527ec206c0a1212b24063c536482d83e14407eb084763ff876870f45fd3780df76822419362a11e48cbc6edf6d54dc2715d1de9c2e5161c0128f76e88d90fad0dfad5c7f351f85f2299c1489f1a05da80cb08b19a0ea19a89c5a9144b1748e3b44b4dea6d7cdc7639fc27f63560eca7a9f4fda8e835c3a4e54fc7266f5537738fe463888345cf57af99c362afcff90b0b20eda6dbc1b61e5dd886db41c7dd7647f22157c74b6e4f0cfff0a5af6e9d4a7028757174e50003a27d81f6968e15e3cee55c6bde2e2e62f314e175409c00c57079a37fe0cc3c8d741e4b86e1d291114ecb2778966d877aac0dd8fdffdcabbcf8db8f925e9b9c8af12f86bf3da01002b7c70fdab9f42de9cfd530106f5c0ab70b6d55e0480c0bf104072e2be4c1ff3f66c6442a36f028416074847009752f1c77233fe97c4aaaa8441626ad6165525c0b840e5658f82656808e104ab1b079c22304fab905d3c1f0c188967ac3a21ac097bc7cd7b70515fae700c2f179740af76f31d35c7a0d20beba184016f6528682860ec6a6fc238c63fac0eb80435b83ad67a999b84264eb9220a7111dcbf49cffd446f4a0b81702936e5c174de6a3ee8f25505b4b7b0ededec2890188df26d6d61034d7187c1d0ac9110f47c5f83c749815ae0e359faba3ab22f0060131b8108040c679bae78428329bc383401de932a92392eb6a13e40f0e16af2c6e16feadac01b147aac951e982c006592a943c8712acb077978abd12b73939098c9ba46fdf9a06e020062a0df90b49029fdb060d3a3475172f6c8e6dc0140a216d4932c20822f4db1dccf49d9244a0054e40241f22745a9ad298e25071920352162a0b6ae13ffdf92e08fcbe811a3a23b6eefb25ceece001e21608b963e97fb25c4b54c680333e01a0cd38a35f822ad10382b640f7d4f7b0e5ffd45a6a5af97e33a7fda158f2908874ebdcadfe42d966deac94feff8cbf93526d4fb77d20857bc94d631a2ca1bb9533ae064b4d9b53d19055effb7bc9c53e5d2d56affe4dea5c587c373392a15e592bd4c931ec87dcc16b0d74e2e08de6439f738853fe9226f42bb214bedc6221cbcd5effef17c9551b43ce2019eb62a4a73e42f4597034ac915633d99f95e45299a8e8fe3dd2fa1e28b98ad71c16910e12fa0790c576810c1af21a3bf7c42720874ad8b4cbe5d6bc8ab1f2d291b7f3557566d2396fb080ffa47ac353e0eb278929a012c3766fa08b7a984e0438af5fe7076017850a6bcc0c080c30b406b9d15060ec146527c58ee47baeaf21f9ad709d5825e42d5bac6924d1f5da75ff8bb0cd1444b95782a0466141f41f98a76d5fc777dfa0289692788adf309acbb3e6191c8f1677427fb0b20dad3c4c0660267fc078f63f130eb45d8cc3c6c4791ee0dd38d06f1f8aadce7c6d621fb7d9f3c4425cf45b19ea7ecdf93389a96e766006c309ef7a5ced5700cf02690d55d31734b46d467b20624618c80b9dca3229cdf5ca4a7264043c235f1f878bddcb1c0f98d839a6415188d1bf4eabef6ff83dcaedd8d07281a7ebeae63bf56ef6569f08e3e75aef6614dfd947813b0f12bbc3d297cefe32064661888fb1236673f9e0a63188c20f50db6b62b79f1a681247bfb7aa32fae3c26ccb9ef331a52c751cf5d70ece7263606be260e34e07fccbab4efea766ccf753f02fa2170c2cd7e999ac13b3d92de2b0ce6f08fc6cda2cc2df3011ad575936f6e3c7f1b8d717118d9b6747378577b6b3711d759732c37e1ed27feb36c05431fd1d82b487bdf380f7070149ad587b100b85780f0a9ee20f5caa248ef7a728af3ffffb91816a5ada92798a7d24813c413c5909c3be6740c68f0eee94400214eea5174387fa2da33832e919551463831a5331e0132c0fcbd9ccb75adba555f23044cdb18ad4e65c79638a2a0443e4956154a8b8d4730ce683af93dfacfd36b5ff6b23ee7eac650f1565ae64e2b3b4ada84bf4fcdfaef406ff6acdb03372482519049a3aa510709768f3d8295c068f1565c2ca299d29bfb1f78b1fbb21f62540d6fb5b05426cb27d91c61eaaa6d424a379b1768f6d0439ae26793fe8489465dcb4a38ed7d3d1ac46300479f434952685289c5e956740f4bd34269da25aa71ca412f3010d12e6e89c672407e9579dedbe24b07fe3965343dfe8f9716c0d5aa1c8ed0a0a2a1f971e0234c29736f70fe7f685c59b48f052f171a12d5dc976a8d28ae11ee6297da967aea3cd5d940623cd2e3327bdb4413b638cfd386899db406bfcdd259f1142978e107698493779da879bc095c45bfdb230470ffca730ade87216dda25c7c1098e3241dca2a462f301af548251ba8f5d1df800287a844d9839f50e26a2c1273756155a4a11eb4decd1cf47e1cce338832769f291de856e42440a07be89ec09efb6407f3bdcc46b67dd188b0b28132811268ca0457623cd0045e39621244151a0fa98ba96a01d706e30dd80e83dbf60c6f96132f8fea1a4038e4b17c56b7c0a87f109a1071c51b526a8fbe891df1204fb5880f1bfbf373ad757538119126c8ddbd1401fef014209bf513830924ea97c64ed9289f02c22efc7e8a35d654126e0b6b763e419b6c010cfa66d096e18268cead3c2c8f57bb33ffa090f12f2357c47e5bcb770d825b9fbaaa67468f204d65ce0fe5b0a647c62c52f5dec6ddb0f90ce9ae1ed6216a7d6ed6113dbcaf1545632a897d123a57059fc147ae770bdae4f161f0bc6efc30437cce4ee83470f8784b22f36645be7a286233ea4caa6cdad244b8fe4937ad466156518b93528f124e72b13cdd617315ec2ab4ead89c1953df6758809beab4f4865098c56cbad6fcdea90886da8fde165709c7c794fbb7b06b8a01207eac0eb7ddbf90c6a8023bfec35d84fd49fde56978cbf0109636b4b20d082b202ce10a949159bc303af7bf185513c0337499f187f39d69075b901e830c63b6498b11c69a4fb61df97df9857a14035cbe62a443c6399460280e727c82328576a0803aa0b2f9b7fabcc699a078d56aeb72a5705483e5bfda9f47bfdddf371ac3f7fde06c3538422252c3ea6807993566017c628efaf68c27bec7c5488ee01b0105dfac2c826ff2cd54973fecb1567d148eb8565a295cfd21f3cf32526e50160d18e306c9ad713f4638971d5a80ffc798935dcbd688e2435346d39805ca6ba62acfae513e42a1e224e49fd53332294be983b547b1f11b766ea47e0ce0cf9333c53819d6ffe1e4050eb97444331cb6ffd7a5476181b71d8fddaff015eebfaa75b49f4e824a9c80fa01968f79bce74618a14f41b6a0114f87b583d48932508fdcb037906617ecd2918a84e3a854b60c3f63d340388932b162334c8d5fa21a9d05653ab6a2d716b86289598273642eb53b50a828f4d51378e0b15edcc83be1f08fca8627eed992aa4e7dfb75aabfd71af4385f4e1d627f47e18ebd3e971201f502c1781677eb35cae178bca39a3e7c859f872b24b588c5ac317199e55a094b3a01c3fede01a58e91e20c1e41f8e7ed7fdbff5d9a029f07ae66802f9bafeaa72ff1a80f4ce1c770c32201c88a62a04871ecde5582110ec460fd14a1c5d831a8ed48b2c128833e651bef3582645f4a2990a3763ad73f9682f016228578bda49b1ceba155de42730d3aac02f9893dc864b1168cb67923e48b822069b231fd59b5fc507ae0006774077662be61511ed4fc8d4778ac098210deb8262e372fb50bd8716ce33a4e019a75f0cb7e7026231545917abc2cddc9dde4f8654c4a14298f7027dedc9b39c0fe21fa2488f240d4a2d0cb788a90e92d8b46a868d509e1a4ff801e173aa84371ce08c935d6317980e05e969854d6ce240cf049d8300bd5653c3c2ecfdc8b2a615fb97cd57c66c0d3f636ceda9387379c998b0fd152e509ed88c6a650b80c3216acd4dde2b73026caab270804917cd7c80cff9b6bb6c8fa5cb69540c88545a869ab185c9df9d8c5c9e05cbd4812023053850bc2e1a26a4cc71e42781c33a19846e312188dd38336039b930343bbf57b0e3826140a4a62029626a6b31113ada9ccfd287b75d170eb5cf53011623528bd90502b33c58dc29f4579238bb4809e3895468c8be5c529b0132d6a6972be3780af7e53ee1d2bfd776385eaa5e810e207cec3f77bc3003cc38f8195b557c7eb116ca9209e3301683b56d1cc310d66c763513386193f829c8b1d2e8d75de0a68399467352ee8bb0c299236534d1344fa2355f35d05ea2d4e631b402caf5d5212dcdf41ba046baec354a8b7862b5a196eb2b0fcc3744e67787212475ae0b822637f2f80c989e7827b0c3a9f74759587e8a0e9890aaec1a379a929b607dd1c2debd9079d1e75b16309c2ade61b721d1897d06f733e3030cf504fe1204a10c5430e054690e31e5b827f058c78f6b834956bd5fb4c5c5986fcff5a1b82a8ed79077789eee98e81cf5312d9d9c927077ca45f4e9be3bb8b57e5a33dfbee591ee47239971a4958dd1b6ed16b2724b2f6d22a54c5206e50cef0cf90b8d7368c30615bc10f4a15d452a78c1883df26d59382c61ec210bcea148455de22134d8ab63af1ece543628524d16960f452a914aa4ea726c30362827c5ab06db67b674a8d62e9fd982a5edacb53653cd9690aaf1cf156457269f056af00aaa29dc438508f1b01fbb0c12f4d1892520f98f3c417cde48a7b43de2f97827de80e3c41b6e5a682d9ab801ea6dbc008e3c7c3032dfc58d59f9f47cb807fb602151dc1e3866d513b3eaa9b5e21cea9030f2d32104ab03b647e48bfc76d48776d56275021c663fd2ae268695019b38f61617069b0099cc1416b2ea53286356431a8c59adae0e30fec13966b5ba78a250f3d2575c1879d2b112a22266d5d327d933c40209e916bbf2b17e1a8cdfe5903ef6783b9fd138c7ac56437c7a52dc98558fcaae563e26d2afa03e8517e891f4299ca8ff62563d49e44b7c22e2c73ec5cdc76cc9e16ab815909de8122710fe8153380887e2783cf7137243be3d86884e9f66928fcff2c510d1012ab931458082f469dee1e3af7c3144821c21dd98224794e8d3b4c3c757f962882821c4bb314584fce8d3acc3c707bf18223f868c6e4c9121387d9a483efef7c510c1517537a688aa479f261d3e7efe6288f4e811dd98223d4480f469cee1e39f7c31448018c137a688111e7d9a72f8f82cbe18223c78b81b538427d5a719f4f1577c314452abedc6145911c9e9d38cc3c7f72f8648cecfbd31457e88f8e8d384c3c7dfbe18223e7cb41b53c427a84ff30d1f9ffb6256413ea28b2ce283c88e3ecd231fff7e3144760009dd98224088dcf469bae1e3db2f86c8cd4e76638aecaca2e8d36cc3c70f7d31ab287e6037a6c80f237d9a6cf8f8da17b332c2e3ba3145781ce9d304faf8a52f6675a447749131457a1041f569aee1e38fbe1822289d7a638ae800f569aae1e3675fcc0a0887de98223848fa34d3f0f14bbe98151225a28b2ca2c42a499fa6918fef7d31ab2439d14516c9b1e9d344c3c7277d31446c824417f945824495f831446afa348b7c7cd11743a426155de417494595f854899c6ed1b1c3486c69a090f6f876cae39bdaf48ce27b22f99e475e0c7f87d9d3a78ea2c19984f83277c81520536706993b7de2e9936ae6fc98a9e852fd5b22113553054ccc1e2b3c77e09b2fc92d5ab470520e71a7e2665903cd8e62ee902f19c779517ccf1dd8cb72dbea176a196e35527d1a1452756a90ba5379b21ff952971057da6b13bebdcb7e6aaa02d1437cc1de7ef7f0ed178e7cc1395f3f882aed389785c79cb8755df88c76b195dc762bfcba97bd3ad545f10ed5b1209eaa77098687f8dd7d887cf90891bf1f3c962cdc43a957ea75d33ef92c34ebc3a51216125fa4f2011df4b0047a8841cb2ac95422954825526d2aaec2e2e18aca07c48a4a100e848aafa8b0a8a8b0b8498585c557f0856159c9a149450588201c0815151515eec2a850cdf3a895f9643d22950d65aad06d4075ead995a1349b6628c5421e0bc13e02f010f708000b11c0877bac13ff546809b52ccb2a718b9694505a4255688ab77f3ffce03ffc6079cd3207c001e0566a29992a2553693253599946e4d247a30b836575cb7da51c6acf79e5e4a571e2dc0baf1f0b8f973eeec142f0c7c24b9ff502ebd277e2f162b73c76aad93284f7cd2adba966a712e28b3105c6376362170100404ed1be90eba141ace850f5c3ee028528dfc5838a2e51de2270d0ef9c528ad3b98e5aabeb78fc85fe587e21065e7afd30e6d4bb4fc76399da40aed0ae41587878f9b01707b79c936e2699fad8e7e328b28b4f7622efb0688489eb781b256052e23abee4253ee4e4238f4e119ee7093d5d1cbe7df49147070a9e27ecf8f002f1e804fd45f22d059f57f9c823051f6fa3e481c30b64e2e144fda841685748009236c0e1f59bcaf9f67be4065d24d32b1458f0f9fc110a2cfcf8274c60a3f6c40a76de4610e0f8046aa7e7e62b143d355f5d6a505821e72b786138006812775d97998036d8610d769ac4d55390e4c2482f7dfe56e2d1d5d3a7128c6374e1b6e4bb7abe4b55e2e1c573f1f88cbe4023ec1ab9c8e98d715114b0c957af57c6e4afdbd64a1f9513654f1e63ec1463ec768ab4063d0f2fd0051a5d247dd2bcfd26e953f6b36121fecde3e6de171b247d3a7e7a77bd06e7e8c3431a5c01cd96ec67a664c72e17e19f29a25a903798fde41c622b7288b39f187feadc47ddbb3023ec25153c768a49c7d8b70b83614256bb30d5b34c3fc9ed77c3039aadd9a499aa87137565a46bd954f34cd984f20492b954a778b4bb34fab193a8473dea5112a703e3e382740abd010eb39f6c487bb6ea130dc93331441779837bcd495fa886037084c20a385ffd499f24e1ef26b9516ca47c91781acedc0f524792a45ba8e351f80810f61740a0974a1866036ca27906947e4591af1517e60aaa019edec968b073179ffced5e201ed8e4231430e8f9f0027531583a77dc755de732eebce8bb471aecd972833aa5635c3964610141af198573386fbe3dc505a201aee9535853a83e85978f9505ea537ca246890fb51d2db8a74f511405dcc22f1f4a748b483553da7d7ebce807884815a4732bf1c8472291ab1f6191aac1b6f3e3781afce277a806bbeb9458f5f8f98e870d4b60f3d44fa51458c6962ede1a32733d9ccf9d01a6b9481a6cc721ffce00d348c721b5908bdc6a9fbb286412699f492472f0350f89b44df32812e550c78bbcdda4f929f4736136939667c0cfa21d37e82221b9742bfadce5877ff01019e010ff98a4e833495fe1eef1d23049075fe42493cc32e845ded2453bfae42237893cc43f91e4f5c33f0d9a4479067c51cdd3ef59b7eaa49f2fa9be9a297dfd7c58829b97abe9a28c792d81cd4b276dc0868f6280af9c7c29d16077a86f80f4eaf7ca5497d26bf59a437ecda21d3738bad4ce638b5dc594f6aeb3abceae3abf31c0dc4f835d53f8ca408185a09f5e6736cd80a29a210d760ef9338b7634d89213d2201097c6895b7efd48243624128bbfebe71202039c1dfb8cbe2fc021fef9c62c882a3d5136387f978f44f2b873f02bd07772e2695f53f09588ea7d613821dd12fb4473ce27bc822ed03dd2a710a7e27a6cc9710935519dd26e6975199ff60ee81393f850b5d3efc6750389979ed74f76cd36c09f8ebd8fbe8d52768c7523b9141264e5ad396296bf40348718d8c95c9f94ed0a852a79ec92eaaff67d1e6f4cfde9a3c49b5e17e09a69442e72e9e1157da218552c4b85e32ffee8a37fbfd0fbf00279f4b32bfb85dd44713d3f2c1fc62461093cf954b8fdb4d14745357df9f4f593ea16ec555577aac10f6baa8d10b2e0ec9a72e8bd8987f1c7ed1545019b38ced3a641135f0286db22268c1c678a8d403af524f08900bd79e9f7c260979f89cf98269de3ef5e5114f0059aa80b7481ac3c6de6cd058a497a481bf9f66aa5e491a03e59d583b60bd4e09c77070e276ae2f429be5dd9153645b14106441510d7a13a54ad18775dd775185b9d8a2cfbb94752e40b04c3310cbf2d80f1897a3a8506e63cec2394848079807f6cd0827f644a0b016308939201c89a92988722882f978f5c69979e449f4835484497ce2df6cc2dfe443ea21eeca1482552d9d515547de4cbe543ae74c9b11cf68b540db69452caaea7418f2484cbceedc84d33d269ba1b1c9ac7ac87b0cb820b34511768ca974e2557c20ba4ba7c20d1a936c7b8efe62a924f92d3ce6d97641386956003f808f09ddc999297f88a0b538261a5dcdd945038ec50261f0c9614b0c0f021867049c410308618800b0163086f223a9c01605ef2167e69e878cc4dbeb0dfc4e5a581b9104374387dc23c0a119a7829773677a6c431ff2e0c964b7277d360df150479f5bb2c8f4495f6ab022cc4d744743898f4ce46c69d1179e72c2e4c973b9cdcddc49831314e7284fc501e09659292bd402295a8a74f599f4e24959853ece3c5b24946015d7733e3a600871dea22c97eee917b77e0b0bb40dfdee15860b6a0c422a48c973f305ebee86487faf62e68a5e50f2048401a11bd568ffe0410211f77c46003c4c72780fc78146c6bc536c3a123c60b0bd2c39241a6d4c169b03df0584a29a3dc913a335a1f0eba7383ab875267c7b70c79387dce194ac2fa6c684f6916a30cb213e3943bd2da693048741983ece8c82c77a28752c746832dddda9198e556a5f1d9030dd27c5180430a76cb06664a4b1133a514d28f52099f53feaca656c4b44aa54ffe489e219dd213a8e78d7cbb65dd197901948f34d54fd5c3e833cb1c993a3382a71f696296407e00016d000838944a0051e247caf278fa1d21a58fb752ce2feeac0055230a820151a288131931e4d38ce2399081c1882143bee6021c9e9c38ca9d1edb2ba9f0fa324047c9a33c9ac841cdc72670a2f8958f4de0f4bc1cd2a799697400d23d1a8c7ec9184f161a92d00638fc5f1e4a9e6bce39e7134b50cd39bf0c34b8073d5c549873ce29e79c524a292510aa874986b8a26ec93ee02764617e360a1c4a1ed933cb30a5cc69d0af705e975f32d34c0f3dcd257548999074eae1cc61098650343c95332b61a0421f5e96e02dc76aa6b111b570f33473206a214ce2ad1cced7c2cd35e79c53d4c49c73ce7615b287467a104f5387449723587e89cbf2612e7c444dbe9556a8d85c7e1c0bdf363f49e94031042340827ccf748e7dfb3a177da1075ec4893cc723dfb09765461b46e1481ef6ad73916fa36cf2b24ce7a21c5d3ab739e90b45ee5dc9c679767b498b699b03ba2cd2c1387bde324d897799e6c4b78c83ae70eec49d856f272b4c3c856f396c91c2c4c4c42445093b2772ace41b76cea4e4291c2bf98645394c5162b1b9dc4ca56cf21426df3e13c76d9c4b9cc2b9cd5338e73f6cdcb6c94c72994d322c5c3a8bd1c85db0701771e42e7c46b370176ea31123f9851e78d97559e42f1f27397ff962a77024972e1c8585bbf078653c7791c3917be1c83db7de17f6bc35a66d2b728ee33807c2bd8f66e45fe79e8cccb84152d7659a91b3702ee3a03fca2edc7dc6fce1c3bfc507e20bfddd51bed00329320b19d2b370f7ebc204e12c7c4607f179370d7a9bbb5b970266c1c2f17f9f8ebf509e77e3f1905c6ef7be307ea1f09c41a32416881470903e85a3d4a73c20a35474891378c08b672f3e978dd5f2bd7eddb8e6b45b5e5c732d8738e867df7573d9648d8db0c0d6282ea3332699eb99df9c819c833e7dec6220d52090ebc65e289b4aedf8783a12889c3caa3e490fc8f452369502627582f4e9091873cd2da7e1ba43d6c81aacd5cf71607f040e9cd3f7ca6c92522a21aaa4e40a13b1095088297dd5e09d7055eda48d449552489c99725353482f55a70c03a0e670a12a2a4886cfee9b25ba6badd4ad2b2809109035e955a96335630cbbd4b33ab232eaa16ffeac7c7c42a14bdd560c5573bc1bb962793741323eb963001d1d1dcb4416c4fa60d54579bc1b6c06df6eb52ac343181156904cfd8c963e3e08e5a939da65dd6b3ac11e963e06ce5482cf2cdafa657fa15225ef97cfa6a84f7c51ebe96799884d88537828624a7b4a6e410915dba9b6bec8b8fae9675332be4844168550367d9eacd0629f7a7cd4f9b8f32d67f7c059c1a2364d514aeb7632e9a5494c439ac47462d4a730ad9853c018a5740eb8c587b22ccb7c46d31bacb93dc0eac747b5a3930ae5bc58f64ecc7e4b18c02d03e42564b87d993a4bcc544a3577765ebe48849c1726bb9cf649f3eb0af9e556ea6af1da37854c9f54c811b14e488582c8d85b488287b92e14b074ebc260d4b2dc6be656d372dbdacbb7552a2d1717a6e6cb042cdda20051efc49937f326c7870f162d56f4079bfa0108f0b2498282266ad6cc9a152c260c0d4293d892d87239076c4d7c854298635ae88bfff28588b09678f16b657cfdd33e2fbd6ab0669b611e0a5dd865592794d215006c0062d65b7df92e220010c2f4c5ebfcbaa7554fc0d2c3567d14a25d647a91b237c70a5d2698b052b4e5c5e7891356e8e0b0a29e456d026e16b7775a842578930a3c1a799bf6f251186b522bcb2e6b6dbe4cb83ccb564060ac4943df8d12d4fcbc4bc032155da84f9ba249f89953a4ccad8054d4b35ccd6e5bf516fe4d494bb3e5c5a33b8a0c9732fce59bab25e6cf4cf9994133058809d9945229e567699db57e5953d95403e996177fc956aa6d0ac6ba4ae8596b4c429db3bd544abe584a902d3547a6f48bd723542278a9aa042f7591e0a55a66173f1042ad9475e4089611ac1f45c061cda93947e856dd050a107cdea62e11ee1072ee125708370836155de8550208f70718955d58babdcc2bbb32ac9006049424c90d0f1c9c24373c70e485095916ce6c39801bc05b35797476a28a958a8e4dd5b709b00ed6c18d5558858bc495765c04a721e23460200c848150c0179452b7115d38a7ee011c7dda9c6251175d3c92fc892e299cba1c62e294523a722ac2a1945e284a29a5a214a554d443a90fa5f408a54194d2ce86d2ee8652dafda0b44b514abb1e4a7d28a54728a53488d2910da57474434744a0d44538b34554c3152087dc87229c6f17f1902f2323c49576237c7b28b2c1b78b52a21ef9e21d11f5f8cc969a23f2c9f139225f3c20a023df2e0a9a2d15350051d010a2201bf9e21911c2e6db4535b3c5f361c9a29a9a9bd9e2a5566e0470f343be783d3ddfaef2e3dbbbd48b5400523df2c5fb01801e9fd9320a4af101bb23f2c5b309221ff9f62e68b68c7c2610b90b1ae510ffc846be8c506cbe7d74335b46a94904f932ea912beda39bd1cd7f990898e710b32c7cc8ac0186d51db5a6d6603cb22c0a9a244946835008090da241a119bcb4402cad7e67fc6ba6409873edc26cae5d6dfb7260d6ad1edc66f520baec20be784ba09c08032c6100a02415256b8e5ce979cd0b7593c3c34804db02057dab224fdcd18940624fac28f962a910577a8810941315557368dc1560e992626b646580cf06e9149f1e6e53c2b76bdacdbd34ea6fdb85b17cbb31fdd5729abd6e3525fa6461564aaefcb07c105f2e94bc5040f8764ae910fa437fde27aaf4bc04b1e48be5425cb9505e48f556ade55e802f8dfa2291486423ea2e4cf5ae73c15b387daad6a7c19e06657c76a753daef67833408c4ea44950b35ad15ac13b0b0597e5926ea678134a8d3759a1d89ba91472ad10ef049220cf021e033c156c7e4da6c2aaa7427b5d085b252dea551dfca548877a98f8f877a0bc70709eb215daa22ada084f2d09d9d1215e4dc25a80f1f41a88e8e663fdaa7b8e32d1fb3e54586cbf8288a2e416b5035dfe3529c1a3427f87584a441fb79059a2dde0dd05754dfc897512aaeb47c19f9c4957e718c514952373ff2e181334272320a4ad2a7396fdd2a57ab7fb1e542bdc87003b81be093f1bd7cb13bb7f31b251921090a9a39a19cb94cfde6b085f55ac6dce6100be59a3b623e63d2ef8851d008c928c934b9309d4aa5ae7267b8153841a4cb338ac8a3e2ce243389c4f49a35b330cd86b6cb75225c2279262529fc4485b3b831d6af38b18ea46cca948fa83029c901ca710f28458954519e11938a2aa928cf88973d9fd962a53c1f9f14e6498fe48db24dc94e94ea9e9e9df7743c2de0ed7a373fbc9bebdd783f38cd0679365bd00df26c4299e573c4c7e70866751a04929a2d36488f7cb149c872687ba6e5f94e59267abebf5e1f902e0c3569d6f6e4c09ee2cc169e9cb74170befde4d2a86f53a915272d63c956fd933f0011849b4baab1e5a4b527ad12458b7d8a32c95f1298279ac49a9f5c98eb98dfef425d3616f3ad4f18664397cdb5038c7978a150f233bf5994c25a7cf9f2da1c7196c9644e83a54c4bf912bf6bd09a1aeb5af693391a9ccf4597cc3bcb56da5474891ffabce76c8a26e499db13cd7e31bb6cae1bec004efb002fae692f9f0582add0bc948eb5291b36b5011aa1d087e387eb81209b6ab034b5206efd01a617caa66c2ab3a9d4954a2df1e236f502009b4a02001789009452a88874ab1eef0c0d1de00b9baaf3b2762c9d3958a920150614a11240f6d777dd20d2205e31882e8860bbb5041dc451907cb950570b4673f8f615bf799c9aeb64a10a60b933ac10425c987a3dfd2e14ea12a2ca10e2c68831440b70fd9678f1f9e2566ab65c17ca4a793c6078371e4e9f6af55297553f0fc7e371f5f06ebc1b2f355b6a4e57efa449ecf168b0735072dcab4dbd41bda828f0a600bcf17878383554512a0a0c0e949e66009785e5d2e44761c090df2400011a65337f80b10f2fd427bf7e97cd75d372a15a6e8c5086c5f8eacc0cc0a5a1a26136d3ee9671191675d9c8cb48598949cb016c978a750581c18d207153028ca03efd221136c7981282eca9fc26206b6a6ab45c6307e8178af0d35409c4d7f1212dbb3026085cd79c023753208a944013815e07113a8890c97658d7be1d9a2701df88491ec7fc284458dda251fd0aefcc00fe726b2f4dab5775abb3ee5077dbbb613947ff55ef95997dd1edd2c046c8d17fe5cefa6a22867fdd9939a3430a4d3c758ca65a350b85051042567a67744c2152e1a987ae4cdcb1b6659c325239cc5aaf2b9b34a4a77686324d8bd1a5926229b660ded159660b965150b04d8bd1a5944214de99397381efe9ac6fb9e8b66ea11a64639f2e1d4fed152338d33eeb4fbfa8cbd09a232c3f624576fd64ba2647bfb41c0746bdda90d65786be3593c4d3d095e9db80a6a1e32f8d6b59b84f3466cf3070dc34623aa53e6708a3b4d65a2b267d605868ce6bdea94d3babcfcbe7e633cb4228de27a559a7f74454a942548162fe00539e6aafeb4376887ecdf8c2609e65b5d20c73ea180d6e503a124992cc24745e57c6df023f58adb3abc473665e20f4a734e38cdde69a10e40940b21a6cf32e3e52810b40df859dff3e52a10b3d1edb7c6857e1c5f3dda793ec4214fd3df1f0a1f63aa99d54163ef23ce1c8db958f8f3c416ebef091070a4e78bbe2220a12b226091ee090ea7ca43f7680638a001d113244c5e22baee2a07fde638467f5e3e30348fcec272bdc7d73ce6ffcf8f18bc48f1f3fe49a973c7e0c91cc4bdc73928b3c7efc683249a77e79cc6ee2d15960f4495ab876a889d22791aa41514fa744c79f5d4d2379b02b8c7f6a4cf01867bbb24970879a2dd9cf4fcdcb3b4827706857dd92a932ede7f10f0e71d4c72ef3b583d674aa0660bf7e1aac76d5a93a55a7aad91ab140f8e784f3122730827418ae859daa53753d7d32d597d9bb7c5c97b5d6c268893d5e1af3c2601b4f23abcfe80e35310ce352c91d043b548782c1ab3ed1585ebdb1530c8f591beab0daf5d54b394e24d242df8c60b411948ecd16ee44be748b9726d6552d5a1bb051132d46178bce9626406c57e636cf768ce068b7ecb8a2472ae9cc94694fe12642de9a236ea7ed3461b0d26546a3c480ac17f8f83c7c165d2215690d7a7cb0290f2376c79e221a8c32a6944e586be37589bb29cd68668139e79c28a17df044338acc8baf2e29cfa43c73d226e88e0568139467529e39e7c4a8cbdcd78591de97358f50daf348d6d96cc3049a6d902bd2abcf23d93c926559671dca4e9c7eb4564aa90f0edbc887366a8faf2e25a5339a52bfaecb6a193d2c5943460ee4ca473f19387225f74a460ee48a8c6f9168193d1a94d7b7f5c5ac06eb0849ce08f66a018733baa7c148a9b5ba36587dd21652dbe76601db3caac198055cbff38e8f7e7b70d8361fda983d2716ba268a2456c09d431b8d29f1a10ded5ec16dec3ca35f2b1061c8cf909f28cb6063dbd8d6461c1cdd76474adbe777cc9b49867e7b7fe70f04fdf4e936d658bde5d547f52c3e42e144ce370ef9d32d2b0b7976b97565ea311e7183fb1cf435c741bfccafcf6479c8b3ea570de55b167669489ff13b46972372d0bf0da86ec5eaa619f0e52934551a29dd54a9a97e178c65b5e531870674bc7429dd8439f896cc262ccf806fb995411afab217964b6992594619e068b23c7a683968e519f0b11c23f732aac8170d463aadc48060e3e36018c625efa7e7215b9d7e5e8366c0a1f7d3443d1375cc2b75f0ad5bd95e34a48732b7f4b33c4461accf98a14fbafddaed9c58ca5337814fb38c06e785096d65c0210fbd2b93f28de510fcd0f3c0af9f0cfaed40a17d7e52ac4fb7698326c0f4de4c5bf0e363163e3e81f2791b1ba4ce021f8f1ff3cdefe697f3bb39966d7e33c736c75468770638948f79b8d1edfae5dc75d074336c7393f48dfae55c68a20ee32fcc966383a60b73738c131cf269728b13930a151897326f10fbb06d8933e65eba30389bb2cc33ab62cfdc625ff50c9bb2ccc1ef1c9bb2ee86cc448edd665f752c3261bfbcc30ebee72213ce33e07b7914339177590b4d598a0b63caf20cf89657edef9de1de7a8ba2c52befd72f398edbb8bbddedc39876bf50fe8685421ea2dbb66df7ba77616ed83c3f3d6c0fb52c6c5b1edd065cdfb66ce29aa74fa64dd53d9d69cb33e06fbe6d5b8e11a44f93e6cec8cd31c7fc6618cea43e494f5d5bf1d47f78ead6ad0b0efb746ff3e9da9dd8b9edd6d8366ec3b8cb6d58b328daa57fbff0ce6c8f5d19ee31533fe7f3e5dbd932d2f9dc17a34fd2743dc686f975bb7dd5ef87d5eb7e26ceaf8b73f0af6326ee37180d4a9ca9592daa50bf37c01776914ff7fa4483b51b5d30bfc1bdc8b18667b20e7b0ef9385fcf3ed11766ce79e8ce6c8e797665b2eb587ea2053d3ebae1e3132c48bd058016fb64d2a28af4f8e1bd64e68725f8ec98cf07c286322080a89b6d2ceb0de790049f65eca3d3aa146bf072d7b79b31edc3f909f4a16683e53318b43b99cf60234a68adbebabdaed962edcc14cb2dacc32eb2f2e5d82d8d2e292ccb395bd2a0f8f1d44d4c3f2a2be83c750b736ec71bc1f97513bf26265ec25c3ec1e3a57b2eb8f9c8858f4fb420880bedfae45bd045cea10af7162d4e5052a43071d3853129b98949c96462e2e09f78c9e4c4f4e1a09f2286e9c474f2c957719222458a143edd9402c502a1c9abc2dc6bdea7ebd1a44285fc128e73ab7467a465995c19ce2dceadefcae85d1afd263996def218314caedfeb9a977c74614ad974f30cf837bb2abce4f67e99973e155f6c30f32b03acb9a9046a2507dfe42a726cf0665329cf806fca293e1cf44d3c46549126d974433ec973d037f11bdc9b78ff7513139f3eba35a89b64f0d220f9c82d00346ba754c278e7afedf2eebafcf2e0900af9ebf23208e9d314427ffa3424bacc50b7bca4248725aec2694f9fc2ebd62f9aeb9c5f4e557de2ee8ce441f29873ceb9f5c5dfaee55ccebc3c4ecba2c9e872a1c061dfe45c3937e4737e6f80356f1f299ca6e426d1c5e497f70eecbd9ec24ba64f3ebe354cfc9af8bd267e7d469b4a9e437e899443154ef24b0ec0c2d0a27cf9eb03b834486e5734195da263994984250be80d4dc971c837711559b2e0f2c6e95329df9478f4c924d394dcc42fc726ce5d9992498b26a30b0d1a92e3902c98b70ad7e5ad449fba479f42cfad5fde3efa44e3b9f4cbb9edca783936883b24e3ac1bd6a0e5378717eb93e598165db81fde5a11496ff98d2d7235536eb6c1f2d6e9873d81adc7440cc36030524853f017a57e4dbfbc6dfa14ffa229882997bfe02fef7288d5e02f17e58beee02fc779457744952b6fb9c7fdda4783d5b5dc3d1aac3ea3ab830d520cafbecacb439e5d7906fcc77f5d1786859855338d5592b909415f63f9e0a97a81f239d3b5d862e5601346b74422a2c74955da0c7a2b67b648a04e69b772420c77cdb7ca041f621fb6cd8dcc6dc383e313353a1fd2206d13a44f3b7da2ee1f84e2501cb0c15963f9dc90411fa30a75533b675ff42952f0291656df79067cea7e5d3497cb3eb54f9a30fc807479e5d0ca37328df49e3e6d6c7c1c277e9cd8f196935a142101b7476fcfa18150c7b7eb78d30cf81d9d4a1c1b8da0bee5bcbfe5968b28787a9c2f47e0c05ca301ce5f97d31912fce6f40bbbba058ef87048b06d785d965f34d77406d45be37b2b83180367a984310ae6e10936a37dcaaff77ae657e6a676ebede1753d71029d47f9f8c40978fc8e8f1f2975ab5e7ed2e0ac9139cd95251438043ff34903947569d40b739dc021bd4de01003bf65ceb9615e4ded21f7f78a32b5ec0da29c6cdb9651a8804d3468c8c7b2a91da32fdfba4630cdf2e9783a29ba504a29a5d4e793425e7a883e3103d5b34c9f4fcc60e7e79cb305778a749aea38e4d3cc2f9a8c054674c17c3a01a28b656135a85bd42d8b5a4c2d19eba64393da1c3cafaa0f39c5b11816b5cbb5abd6ee1c760e25776bd8afeddb8dc16eb5d7f8b52deba2aff3ebb33efa2e973522abfb01d7c76aeb5e8b2a416fe61d659ad93ea3a90de48b4733fd7a6891dc22658af3a26c67579ad90db899da209bb0963b5b16e2dcda1cca5cd6bd578ce7ad7925935c9249d9d62cd2c1d635efbe1b539dc6caa10c55ef9acba9553bd7706c99cba19cb39ca9263667cdb39340c2b2b2121f5ac7d193f2902fd52fdae3431cd6cd34f4d69839ecbf5eede536c4d139a4386fddb233d3b45fb7bca68d5839c48161315ade8ea35edfc4aecdd579c895abf3d13792370d5e3e5b646aa6500c94baaec1c06c913b34ae6a8eab9db80a8570871c679aea9cb7e75072a150366d3ef2cbbb66b6c89b9b52a9fd0a45a32cd2c1218eaab97563424ec3e550d65b43fb90cb9bd9423b85ebbed82097db45724543580b31d77e51cde985d1eab6d940be5c33c4115afde538ea7399a686970ac796193385c6184339ac5ffdaa7593b15dc22f31201c5ad940bedcf4295abf96778e98e2f8873253a8531097628af4886915939836d80b885febea6b0e7150abfae5f6bba665591e13bd7e5fe8f76a57d49c86fc69e9316c5e98da12574a8dd03eddf2183afb8624817882444271700317bc0443ac91d3e35d3e427185d55baad11a238db5d25ae9bd010ee987d46b749934d46f704fe7ad61f9b47cd26f5ed6375fbc38627a688a2e7dc63c037ef498416a5d1960d3f470fa049f3e8e1733f655370b0d9ceae347f9d240dd866f0fe7cb0c237766ef4820f922e38a049ab453da2b4b8e3b6d9d5456c65e0983da56b9aacdfbd23672657a284ff0f39753cae510f796c3d2f4fb491e9b906b39ece9f6d2349008dad371f769bac5e46cb91355a6df1d6cb99535f646b18f7b9ae5fc422ae39c5b58028c0c76064fcc00685edaf5189ee0972ffa64f9636083336b588e669151c404306003891d373d7280ecf4cffe761821f4c2ce3b6379e7303f754d7a21ed8b3f2fc6befebe30367b3b095ef429e4d33d94c106a7d39f975706ff0df9d6e5d5b199ab5af33600cc218d72053587fc503e318324df6ec5e0c8774d8d76cb6fdcd4d4d4a48088e836a4d52f267ebd9cdaeb9a2cbede98167fe51b2d9eba86bd6870fa498373fa9d020e51be1483ff860c7a2a579615695bdf352dcbf2b6a665c93ec4c83efc95695c2eff008fdd1804fc956f20e0a94fd974abf903fd392c1955e444b93613a5bdd4ded52fcdfb440f25ec03bb317ae2d39ad567b5aac7010db3cc28d888127a3d5128148a5ed3d230ebe3e3d1a86e47a512c6b44a8b594c6659d0c85e3b38086f7767c1c29d4566e12e02d4e3a43252c9d2207db19a3761e879ee2315c0b0054b637ed12028a341ea3162748cdb065cdd52409f62e24b06f4c9e40f583df50974066cf4298c43be9d0e89c28994ce7fa0ec11552e1b30d8a728678b8c29edd9b7875894821a11e591d029176c7ad84001831e0fc50b787c8ce223142ee8f1968ba74aad24348f19d38b1b4c4212814ded8b4d80a761616eb944bde5364a262ccfd4c71ccb965bb4da18f298f8a16cb24e93a7b7eb78eb0bb55086381e73fb85147beb9a3a1efb6c687f656b06b942ddfa664ccb49258080e3f3f1091ae83ccbc72768a07a4943238243194fe3169eca533b9df15bba0ad972ce7cf2adf0749f25587e43afcbb251ba2ec77268a364af2f7eb441cf7114b06722b9e7d5adcf44f2915b24af7f3926c5244fdf734bca262bcfd4bffccad44a9c21d1a0e7f51b7d5673cd6d73ce556b5e3994a35bc3bee5dd8dc944da7589fc72d185b93a57f149c7bece577c243ff1cd3b85e9cab393c018f7b8b0351a358f8a476e851a06f205f3aa2294c31cf68fc2bef98a31ce98e535e424effcca337aea945819cb2fbf4817f619135f1dc9b3afbb31d52f512579a9c4c2dee11b63390daea1bde553a7fab43553eb7252b75d18ae8eaeeb721b6dd5ae2fc43f3b09ab7e8f0f3ba756e724cb49cde3c3aee21cf6632b48def5cd778ea3ffaa13679acb6d94acd4eaac2592755d9787b02e5bde3653c5277dc5d779463f0b676653de72ec1cd2aca44aaad86c993a27b9734c39a7fb4c8e39e7f2b3a61cca0f4d9da9fa39bd61c0719ef54dc340be609666e4d22d6fc0485aba650d03f922376a31eebb309cdb2ea3728479f74589899cd33e661c1f9a2a48ca217ecbda3ecdfb3ae77cc5e5f19a9110c630ee2b71bb61dfb0e5592227652b87126bdc8db17e9d5aaeb9a659ae591247a75122d387bd495f883b89aeeb822c93f591aa7b5eae9de75d0d4e2b225f4e3cef9cb4a206b72287fd9e6b46be7af513cbb96755e7328d97358f64e21c3b163a7692d57516c931ecf6e36ecc0ab732cdb6029b50958a14d80a8cf9e617bbb602a32a8ca948612f0fcff22ccbfb2ccf2d1289e4da8a14f421d6916a6decda4abe540de8438cd479b97aa819c1b0639e87384839d48c90dc735248f2328de59d933c2c79d83658ce93484ebb45e274ca7412c9e395e9dc721b25dcd1f73ecba5731d96ab6b2bb9c289dcf4752abe10a7f8426c7ac94487eb1c5209a9d692926cc2de3977d3393d3f7deacc16893353a64f8c757e5aceb90a910ec93dce37bf483994d6ada17db5e1e5b06d9ef3b07370483e5d2b6279479aa41cdaa81766ab8fbff8d66311fcc8edd4b42222e7dcc628be5a1e4e5414342317b9e56169c61a3937a28f45ee7d61dbcc1d6fc5f896862dd78ac817a9191169dd1772eec35cde18db751726e45662cc5aa971db17eaf8cdede80b250cc93bfcd52fdcc0571f7dd667495f4c7c92f72d221472528e0d6212c963e293b2a9731a925fde91fc7252be1c731d3faa1de7231d5f3f1ba51765ad885ce11c7f33a66fdfb521cf42d827d2c1d725df244f6fb8de721105ac5794b2e5479878cd804314943e69d86baf4922189f7a8ca70414410f118c1f3e4a9d6290521463b6599020ad2353fd04990ad24122a5d246da48945ca1deb205d2a6067665cd2057be8a87583344bbb2ab21f2e6d8040e6ddad0340f603d503ca5045ac710f6720147ceca28658f2a47891d35ab21417470782009f2e9f99183da6164b513a4074e1224427c585e4495488f541e1f3d7ed325e6d3f32387a2e80e23ab9d20b3c7c4498244884fea47a3808cf0ecc81e5124893f423efae0ce7e7d70cc5105ac31288c065f7cf5c6c86fa71b4b54896ea515675f7533c958df4ebf735802ad897e4202118306078b9a73d6ea57ad94966634129ff99c219fd17d93612578eca662f5bafc6b1bda30902bd42f8c7e7556f77861a663d5aa3099ac43b05647e963c4305266241a8c2e4433a215d1568dd28a344a33a219c97e6444c872301f1911b08944f663663f1aec39b1ecd50c3fb833fec1ed21d61a94a80f6dc428681868832c7db02ffae47284f4eed859ebb05693f4a65e290e204ba39c528936611e73c5c46cca413ffa0dee63f690cb6cba217f7a0efad26f703a3ee61cf2db65ce217f86b07c651a0b87fc8a437ecb28584844f8421158e68d56654a78b2636a3730cb5b1fc5a20af5793b3509145bb49852bdc61a6b7cf1550e91ab4b0856d32d31cea06ed1513d7689de509cea584baf4118cc597eae42bae36bf56c26e994dab54fd1d25a6610ed96beb4d812a366cda0e9329b269206ab376a569b675083f7ab5f17e0cba7dfd83283664af5ea5a74a15e29eaab873fe490d67cf57045285bfa7941b3a55132a57aa9846bf54c9ba29005c552abcfcf1b255f62105b36afde2e88a7ccabe621677279d8e2c346cd78c594cbe777bfcbfb5efe967565166659d3bae6ac3e576b6ba0914eb52b2595dff4d8914ab9848d28b6c6c4ba3f8c520fb5d38cdf9e07aa3da5d3bb7a0dcbb18c6d80e577f61aa4edf3c3dab5be58835f9cdd269825f922e38043ad145dfaa9fc1cf2dba98f1866344c31e58c35ba44c732baf4a4e13f6711aacb39ed75c98cf98c6eaf9ee550be9c31b12f9a6aa6dcd37c837b39041b25605d16adb50f807222348f07d0f249a7d8984b0d8642945a9a5f97f7090551aacf6a3fa7f7ce8ce02fb7e69571c6421ebaaeecd1e0591dbb2c4a670d62a3043ccf3b89f1ca5a7ca6e0a827d657f23c8fd52fc72ec75cc69fe6f24a27b3b6e8431ffa2073548cded86a69251c2f7975566bb6906a715935569831fefc05f27d8ffb14bab164ff20a26a8d0e4d01ad70e68daa779204e5481f3fb4d3e697bd1d61b5667461284dc28a88a8d6785747755644e824592bd185a1375306949bab923a6f705321480edbbc31a1bdc3b52a48ca81de0b334b3368f3217da005a5b817869221d67c842c79aa429b9f89ce39db634a5ee6c89101fb98b26de68ffa8dc9de930137066e0c9c183831ccda848d40609c8784f24723cbe22669d2aeab358b1220914623d7f17613a5644aaa66ca74efca1d4cba4c88449e6844ea481de67016ba12931e2657e28c66a90958f268707a69de703f248d0b4389c68181fb42499358c433a58043d18e0a151746b4d3e0cc225583d3791a34a5e038ad6a5bf58460ce0b2a34ae0b261613f52137f990f0c074131f263e98c52c66318b596c94bd1bd14e57329d746746deb97761ba924ba38efcca9a152c542db2cc080bfc640003f96e80858bfb80af93407d1af9f4ee93311530036536509f8e748b27c47ff07cae0ffcc34dc10f17fc61f4c5a852ba3322af6e7269e8f891d7ef874be3d3382eacf285118d465dd775dde807d1a8ab3e5d36f1a19274a673929b5c185297b15149c465c18526b1c85d8c465dd7759d68e433da0510a29168c4f9f8fcf492af3453a68397891f80d0ead74004a1d54f06016af58b5c16b0f470db79916252424af146295dcac9567dd4b997453eca9746973126bbab559d09b9854fb616668bd73fb320670a28e57a288705ca5d21053852a7dfec46526f2430b3108d12c3ae904653d1e5ab947256b82cabb54f4d74a99c0a576db646c46613a004184196634c699ff30b33ac2d9061d9b7231322cbe8cce82db7214ae90c69d4a511ca40e79c2d8d544a8dd0b113b2e2b4bce5cb559a4194664a1741fb149a5186faaf56fbf328c6b408fa8422a28a8c2e326a7786d2681a2cb308b900e900190109b6631f05a24a3bcb6ca9405cc319be7dfe405544b4649473abd60ca5a1e369584e2badd572593386993b590ce87c2a5f269d73c092604da89ae658bd3cd35ea0684eafcc62167667349f5f08fed4bc6a3534af8e695f88819a7567464067a40d529a83f5ccae6661811143c60c051411dbb1b8e139a7470f799db9809c1373397b622fe6c415cc50507c2c9b8bce59ad1158b557469fb0d9c2f946afcc7a7414f0857cb13cba0ceec318e63385469df6937fad873c7467accf2fccac574dc67af5cb76752c94298b0c86852af5fa5a572bdc286059a534838d289735adc5421b8dd1050b794b8c6e01674a3bca47af51a8c5b00b93398651166c9be6d79db14edda2d5adf5d47af5398156807c91892bed7526f2d0a88fe52266508a882aed72c7029261d151a2cb8c71b1fc30e79cb9c05f8e7df1e47df8d46a85b08b466d25c43c23c16cecba2c95ef9456c0172f4c70356d8ec32d5f2c8f3324b87ed25cde94fa15ad8f7ad3395b8725db07b55a218ca7d66a85300cf3ccbd6e89a53a824a83b9e511b3602d4a27138aebb21cb3b4795dd78de9286f8c102e0bae98a486f5d6b1add765598f435f9631ef0b835d9775558065a64860f18d3b94c9f87282491cf2d085f94c0bc93b3367680e2fa75e69aa4fcc63aba0a8548c75b6518e4245331000006314400028100c87c462d1704022ab9ae91e14000e8aac50724e9ba75110c41442c820030821230020200020984d12007edbe83ef2ad79ea7765142410d3d519564fa420beb0d495b4b654b649ee800e8dc080d8128c2817148e2df79fda37b68f5ca5d3bf6a136c6d2163642a3d3d305d2b375c0df8089f25cec3d0c91754b87185d90ae7167e160414bf25c3dabc26b4f7b5c6213f268337ac2afc500e9bf000f6206415b8531a4e294d4f1faeec329d3f9890133ddb7a52e2081fc429a4330736dd755bc4da4d2f29bacfea90a36d9039519cf6be8a60bf02707527c2b52bcb9a34e5abadb7c92f680bdb193edb9f259c583c1090e506c26e37982542448e7045823fbc68a70619f70903729d546ec0a292fba8369f0e46dec20716b0751875f1494a05acae7a4c54f431b567469edd004f7b01b37a3ca08f399ae5831b98022a6f3c9b6d8460623cd3106e3b164d4eaf990f7af7cbb5f6dc910bf1dd9b137420a4fa68e6b57bb47700c5f139043bb97fec41ed05e1b17ab9db6064cb0eb69698f9e6d1170bf4755705f934dd0d2d091b2c7e2498305fd15d82b2e3b41cc3185f0855379657905cf335f6ae7d5f86a0988679aa836105408223e355dc01b3f3a09a48c8f2c908c9531afebcc98be1c09c918755089b8a504849a804bd65654978c63d8772a77a5437dff13d480b772cecac8a4c68accce6d7647dfb89802dc7f519a2c47118d1d60f2974020b15f6cf14fba5aa0d535bc6541557e808a6f938e1929d3d677f79e05224a504ca608619bbe9193cc8cc263289490c26c173a2f54b87d0e6bf27c0b804bc5d69f8fc5b12ef6b78aaa97d5faba49f589b36487cf79482c429d8f516bf3ab3dffc3e7b3063af6919b0d626fbc5eb25dcf690efc612231ed95aee9e49eb0a3de87179d187ef974506145dfe3c9b7f212eb5448a30d0d0e3755102bbf8ef6c881f2a39af708efecb0fbf3003a205141128ec298f98f4611f790e86a976261a5d841efd6dc9cef6724109fd411236f7114df5b647ce2f8c26b79e50add431d569d992435adb2c6a05d025c0ddafc31ef4d474e7c9eac2ad42f57c352f56f7117363628140c86c26ff85ef6e402b0eb0303dabc5dc90f08d2b545679cb3316485f0a21a82ea977aaa33ab0c42108c5edb9d41c00aef4e9ca4473479a805029477f5aa34fc25d52c1d830665bbde316e06370fa010e35430072d00c10c79594a81c8b99185c06edab4eb9451d836c212ea58ab1b7049989c44f2551213c3476f0944f96bce0572e64ad2a58984593b5f110719ced2c19217b25611291e76d07cbf6abedadaaeec697e4048381f7af0113d705ff623b84d437a28988d51002cf627109229577e0cd90834cc08bccea442a06eda867d21e40325401a0038b1e476efc6af61315e4121bdc7448c351e4d0c6c60d28305ccd4b28459ff8cc00d3a183e90bd4fd0cf91dc8c491a7263ca389987f25f77d45e40acab19134a9382964e8a85c133e2161276ad2f0d14d69939b4d5a850143fcf203d47924c4d701e9d0a39114542d298181aa99c10ffb748a45c498194596474371162baefd21e2ecec44c70c07948e1c1552d061bcae983a70a6d9ce5fe993829308c7c50ff133045dcb0cc99a33cb3273081f30412efce01a00cdc94bc4ea49ba1cfaf25385efe6c47fe4405abdd013b0b8240fb103b8f912fff4143d119922c353a046f78b8f945be77c1feadf1a1ba6b240aced2030e1c243a976ab50e613239ee30d5b80f58a573c48d4b9aeebcd7e98652f1659bb776881f2ea901cd97a4521d8b0ac03970aa98761599630201cf967a157f8f21b5b80e8e46e7fbf0849d26abce98ee4dbc12ea95b43671e403e18738bf53b181fa0f35c5d7b469efe8a234bfe9f2f438d625075e421af56b066a6cd087ad81dfc35321f11eea7168b1bbe908e69312ce7f0fd6a23315580fe06db43e4e187a4a747bcd47c2928ddd3d4e5910ad3607d2e1e9950a48a5ece16677820b7dea6b84933e4216b3536a17ba6bb1e42d0efad8cbeabed977585c8a08fe1c61eb1efe9ac6f4d927bb0c4cc68244a224a644117a20790f7c6c550f60ac5b495e9792476fe37bdc7b88808df9cafff0553c95af161d4a696cea3ac65a72b737266ef7e4771e0f101cc881499045262f80680fe141470c3e5e7151d4a81010b6b8588cdda5a647462a218638d91ee1c9887d1f01d9c713ec3128602e15fc0b0267e1c701c797b3da70b02ba59e886aa32fe5710d1a4ad1c8bce7f82da55733496fe8123168361db17eb097d1d6d7363c4a1f90ce990e3e4fb76bb84e1fd4174003c506358427ddcfd89a9554afbc27211af118804afe404a389daa695391de5ce399660f6a89c43c89bb86504fa8dc4279568c4f2d5d6c66ce518abc4b1310d18b48f14613a28960bfdbe57529763f7c974b4c26353152f32a52842a4c92b27fcdd4204417ecff4dac70b18d8b8eed13f50f53831a68702cd4c449183c5b66b23004b85e1f415084d2dfc19688334cef4a6532713dd6d447ed8c8a8e6e73e3ca99c7c716ec3e94c326a4330a7936c6b0d6322ca2c32964b38be6cbaa8dcd23ed9980baf2c5f3d9a2694e37490ee1b1848a0e27af54bc557656560c365479b788267e961ff1a67f406c4198f86e3f50ca09c333dfd3f0d79922ce89ce25f60c9e4347eaaafcf2b38986dea3d9dd94f87ccc65ccb22aa7ff8bcd121884007c26bbfa79c080f660b9ed40551a3fe80223c22e232ed00eda1f5a2a891535f835a3c013a490b96f3e7514379d82cab7fcd487d0468c1e1f60e922daa5140d5ed4f6faa117deb0603baa25ce9aefbafbd8fdf056a1ef74892015981d2c076b21db48c2519564516fd4f3d812d96469d8f9dd895f505c424189eff847b4af0cea716e5e565ef5c7862ab0a59fc5ea11ea69f5ee59916556668d5f018c08f1d2136f33d505beaba5364de6719ce613eea8c66fa7619ea9afce006b54b9502d84e4325dc1a05f05f518f08714d07ae2b2151f06b856878ed2fb7966814a477affe4cefe8be83ff29ffb67d9ce1308ca1b320ee3b2f3acb9046bcf6d3ac24d440744ed335bf53fe830956d0903dc0d1ca6473526806ec86bb2822afb7f4fed8c34465640f0f3ccbcdf2d832ac52adc9bce297bc11e59db738c9b2e075b20d40b04533ae8570bb6d97025f04b73d5f64488e24de212d7823258b38334a79688a21db409e350f3c3b73ea15c40f7a422ba261e9e93e57df5315cfdbd60f78ecffffc6ed2e60f990f974d91f2322d0fece60d91cd7680281ca132558fb5d9cac0bf644f3486a21f2157a6ef2217a1cfd4e67ecb0c7100e308a6af8a964fb765efd5c1d679e4c8c838d29d176423a545d9ebff0b49479aed76f7800a9a8aabe059bb2dd35e0b5486be04dbb480cbe6c5895071b4e827f3a549cd2fb217646a3e431b26350adb9de81dc5a3c351720b770cbb53d9694f12409db5f187e5dc01e0c345f055a4069890af2ff57f478e66335bb5d2e40c1bd052086f09928b8bb83caef2a7043db7e5f9a55f2f44729b1c8ee970c17480c2abd42b449ca0ed048e854d1577f9758f9494341232ffd04377637f50848fb33af6f64041ceb1d14eed3021be3f68c14be68288cb8bd97404863875e818052e9fa870945f4b37f2c695bbe59403a89ae8b5140af3d6bfaa369a4d3023d34fa7187edc0f729f437c5b0c78100e04ee3d035b77c93ddcb34d940340ae9a3f7c766b346f6c4a4c54f8c6f8460237ab6eba4661b30f1aeacc49f9291f165c52a772d0a7926f081d6e8210fd10519e3107ae0abfc1c7d40c57c5e135dd186a26a65255d7456871e70fdc87cae9f0b4d3edb453b2b1866a87bcf5a30fc9c915c6ec6f0463c1b53a3a5fd4d8a9d25f02dee95836ffcde95137ef8b09f03bef3b5470efce1bf478efbe3cb8e9cf0e5cbfe49be9e91fa8431b102ef313012c9feb563a8464cd623dadb533efba3d31958e22ee9ce10ffa9b5c4c7fe3914f72ad92fc7bef18dda6c20d056f6ce910ffca0e6bf95c6a20f3eefcac97b3501436c38d0a965ca630862d3517503e73e35c7fe7561d721f23ee03ee0034168dff6d3bae9b2e93d58b40c26eb9d13ae63052a8e616b9faf6f9157ed8167f3d9b4c0852e45c9dc3a29bb4b104432c2a3d0aa0c06dc72bdab44bf6e0104b67ce4468f80ef1d0666b59d4617c1cc602c305ec8a39da4bef51ad8072dda8467a965517fe82f49326d6e0f499b24f305ec7e8a34dcba8a0258d8d107750496228beabfccf2bf7d62489580807add801db7ba030879366fdf797425492f1f16d4d6fba6341dbaed4979e78481767bc92ef3ad1f4ed6c4fc04159acb02ce664e2ddb86fb78906c97f3d786f9b9ad000fcb168f5a9484dc5fde6a6608b51d77135123d212fd24b96db04854b41df733f59ea43873dca408fa2818ffee99a88512c13836e1572617d58412a1abac4e0bc8e7f1be20151dc71fed424d30887197ca05c569a61c963d0a9e8d7f3bad642a6485577e3debb06d2aaa0c52ae9251f1e235d5823af78bdc63fb023c3776c1f1d80b3ee780e37f4597b75c84bc6110af1d5dcadce81efa6640d75c31c440814607fd042f3637f7ec48f34f846fd418b71e817eb8853cf84a42d75a31c443a14677fd042fb16d442dcf95a59100347cecd0118ef467eeb7b6357da54b5e55004adeb64e596e58ce1b511f7638611ed68ae38e7d4b2cd70dcfe9343f5e5dea6925c389603e51fc59aec96006e007f369fac28cb3dd670213e87c9f83ce4cf496cccf5841f733ce3747d2fbd00a4e331de23c7ec9e798ab16be053ab0c13a3c5afe44ce1e31b71a369397e8cb9ba556d627fd68abc54083eb7d77c89b47c97ab48e2f749d6d3b876762b9b5cf2b3d687d4c098a0f7759bdc42d44903069cab82171ac96ada318773a743d0df11ae8395706feb417a745efada0127ce304ab2a0ceba59105c5cd1f772ddbca67e950122057df30a2f4a8098d771cf2b3b6e818dcd439562de8bdc43507bcbb545a628b077aeb07f60a37aac89c3bdc19289c37cbf770de1f8b6fd23bbab5ac1cf49204eb3e2052e9bcd0e3d0178f96c9f768c23630e294e73292ee7e361089810a799c426ab9d9610d377ebb5e380ef780c7c4f1b40f8502fdf915b657505a9593c7db43fa550342dd2adf319c171e02b7837fd2e12134949641f508c56cfb1044b12132806608b3d7b6737f237c38090ac7aede2e54ffc9c2e8faa69646ffa6bb2170a31e28e58d55a58d6071b1a6df59f14b04c955289d47b11cec75c5b554b2cd324f9ca5677d4986ca8584d12982cc1ed1bd60268337cebca9489eddb47b17eda870c231fbcb755e48e8d78c2e38be10bcb2e55660c08c81f631fb1a68ca4f7074465b144b82f231ca65b1ac2c67d4ff4c8faa4c43f1fef318c6c19845e2b7228659b1e32a83fbbf5122dcc81cbbd09402a14632372dbbd350f43dcfd5de5790bd2b9292583bd58c8e035db6531f393952939efc015733509483a91f288ee5134ef68d352747b19a59f511417d1b0da8256aa769e58a2bb4ae99e4e846a5e4738a343c282f626fc7e53b09b1979bdd2d32aa27e55c626251107cc20a696f20478b9f5e8ca0831bfca6231b82fdeb342de5f589c02b145012192f5b70f1ac78b12fc16febd3ca3c79f4b4e5516d32eddd17f828200472fdab07d672de4e676d3423ec315c3f45b193a3abac2c4e2e1060e2a5c0641cee1b9494d3d5583b434171e8befdb90f5d96bb44f5ef741347a70149fe938c1ee1a530c2166c24d9d8f01b9b32519b1aa0b11617b11449019739abbb01319c21041d7ee88e302e8bdcec5b19f6f101875935878b56a21e290a2c0f22c1c2ef5999e4fca472e608f0de4da35262876c079ff5008426f23da132c499611a19d5dfd5ad39b830363a8c761d19df76ee9e6c48b3def265b3d0223f0145f26c90d83202fedceef703516a18d6629d1fc051b792815b1aa1e59d6e4406b507b51ed2121bff6c239acf14507023f5d2e41f4b43d0080fdc3d798662b786d4bbc88872aa89d4eae03b290d9ea5adeb3245a079994a02550a5653c5ead62c006314bd06adb456fa5d23d1eb7387b52c972ca9c85559f44c7a190663287337872df96405746ffddc56e3c9d56acee0de8dc4e07095cb251e6c748187517821ec16485af717254031b584479038f8ef70a6220e4e7720382ca8331924a1f15b7895db4c32968f2dc7c9fdd2533cf0c2d1b8c532d4e502f92cc614196752464d31531f5c491413201a4e9a4ec8e626979d99443f01ea5415cc001cfbc61b578b02f2c1a522e24fe2fbb43a81a0bba6a76d289668ed8116c1388db1e3ec7d835d690f9fc25bbe828ccdc479ec3f906fff01343ab8ba2a48994afd048610dfc81ab2f930cac26e0c8596652153ddf959ba887b4ebe4ae7e9b18aa44abb885cf3d49baa955da474930a60f72a9a85f0628c9fec2c18ff5f104d3c7f0e291279ae763cf89c1eef98f7783980805da02de7757902b54f06db0fc0b6654bd0ddd16e1a96498c91added0dcb1168077da98ab452f5673afccb95f9bf458a3d8d34ad651a61454b3bb0af3957ad78eec87541053eb794f09ce154dd911dcdd8230a1972378e7f456d0aca63f72676ff78767914288f9f304d90e1f9378f84247dccdf19ac2fb5ac26c02455823bee8cf0e194acb5a0ec23df4893a7d1119a7ee44e7cd60524434119bdb1cd348b6546e5c8fda5917ce658ed951efbff0c326461810238d3c71d66ed8f3f1b4c3134f693cbb3f5e5455721894299a289fb7bad00b7d8276bf3cf3f47258e40ce12ed55d0b774882ac4c81b0a8b6ef84934f94af90760839d94839b44d2e9f07c631389dfb3ab344d0d29743d057f559f7eee832aef899729cc89d1e628aae941b9e4a2543386937487c97815dc56d94b589990f94a8529ec81a6cb596977809cc8c6d5db29d1224efc5b8f2409f5f476a6ec08a3a9525754a9a2d9b1ab6f1f42f68218e912f178e56acf71257be6c70c045506a1f52c162dcd7e0797e4087539d5ba639180ad7b91a96b55b26da7d1660549210d2822c4c2802925d4679abcac2ccc70e51333448ac9e36c8edde17f48fd9ee2728dd7343fcb2635603b75cebb0d237a4fb0eee272118b782123aec2a7992268131400bbcdaf652e759cc89145caa7284d2bc0d71ef1c8b63ae4138544007f3540f8031e82b93d095db9b18696b42fa2949b4048bac3f3bb1f6dc011333c51e83b08c8473377fae1f7ef16680557228a1ae9b37824623c0cf1c1a8ec992cb561eb4d795d50bd766d8a5ed14467449c5be958adcd0ddbb22571cde820467c43f844290841a3be5181e1fc6b3386369c12af27dcf99b9a7103b0002ca4ef0e748784e2d10f27c8f74154ee693f42df8aa917d213f04d3d841eeb7460820361ea3544991bda54de6e4ae2d7e896b5816258a650da91aacd9381373a141d48936137aa52b29671bc6c68fadadde7ae27a9bf676177a9c7a517545c0040f29ebc1f6e7a6371bea7ae9d03c47b05c5710993261a1ff46a69cb2b728ca98527c7eed6b011876d5782154a9aeb470b4b247779ab1f69b5c114dfe05775cd704c2f5b3e189a76a79b22c5a26fb661117979087f88e6e86652f458e6e4434a55736f6fa07ebf3a67b69c99b04edbfa48f19ac0c8b0467a81d2cb19e37099dfa2fa9380989df86d3a1a2879fb219a7d9439f74e3005ed2a7fa12405313237d71c764f82d4868d2214ac13e82fa187b85cf2e558d235da922665a6cfbb54830093758ded0b7e90d5323cc35e8c0810379a40d0514a08b05e097ecb031c124e45629758a4f5d1d51e979b0dbbb85bc1337de739d1c43527976be78b10974341a1cc110efc8f7374f7eaf0ea0b9b56600b2a334a939937443242d76594b5eb2960e8590b436a0e89d4d2168c8c0fc457f435502f6ec4b9b55569a574cd947f9b2e053792da5a699c55f519c18b2959663126e93aab52fda80ac75006915308f6728155a45c16561acbf1924a1a01c134ea152be746d0c93983da4c58ef97641733b9741d40362b4f91607d933c424607a8dc96f830e54a132d6e0955a0631def15c18c27b50ab4e4c884d6032638849b87c93ba645a19163980b5d2938d788722531093405589e873302f609bf7bd93d2b2d801adb76157111fbec537eba6b7d1df4ac019bbc238e9281d6b12d6d8f9266248248eeb4c42b96095d00e2a254a2edd232e9374434be8a87e083f8a22b8e4c8465e4d820e5a9b14ed5e3d41e35d1af22203ce89f0b5a5c402c5dcb625e0cca3c89dec65c509f4772e16486e7b4b69a8c225f90a7278b4546e5866711c037c69128cd9dea75a4504b285eca02178564765920a6a3b04cdee499604f507067eb106abbe641bb26fe973935a90898a814b5278918b3c97d8ff6e0bc77d9132980a710f51bc4c82a07d9c9c58beb49c063cf3257650a053748dffe916760337e12800d6cc9c5514a694bc0b2ce1b9fe96572640d9c84c587065d24edfec01747f2116fe33ec1c7a9cc29cd620ac3b8a08613526bb46cf8f2e70a3728b1ac58d6b0fb905ba475b9a7ccb0b098105f8601490bdfedd66db8c66bae8ab9224465f8fb8f90905edb78e0e86df3642d52373c53378be557726d80ec467d5144b1dac43cbdfafa006d75531f4700bcf5b84d52a9b0e4445f36248bd08c404da7c7e2ea40f738baa48a5839bdeec3674087a9c07ea97bce99a85f40a9d2b4b7e5de22a352a3e2729c5ef7d6930bd5d0c808f83d0b401117792e4398019e665e20717bdbfe495c07e8580c1933d0751e40bb524a9a5bf726f537dee814d779f1c29471c881a5e3f35df6507f87820ed4ff7e8f9e489b30791a7fb47f8dccd8d95f0189c0aff015b1fc1379b233b56655eea9918eed4886bd26256d9765922b249889e7e3d670f8b590430dd10bd3c85b4d0bee9dc032f46e6e2a2a04f6b9ca8f7fa5aaed5ae8cf74cba3bf05368e9e5c9c59f21dd81e76c2ebefe84316b28623f710752e2220d03759397387a996e072c3db9a040c4987a993381f3756563970e5f4afe8e026ffb04f1fd0b4569bb758f255111d1ef617239852e2b10282ef0ae5044fe4fdb00fcb1cfa403fb6e627a46011937f8478fc6786ebe9eaca5120abd2fd8ed43bb030219940694ffcdb88108d47198f1ef6509e46bbdf717affde55bb91daf412162e0f68974a500d1cd9f53b59f6a4732b8ff1a5721be9e31b1ad6579e50075b4d44fbcde8ca1665d92b4b6ad24ed22f41ab559a6fac703f37a19b81c89291ab554b3a0e2a15bf734507c09c0d6e6f3fd35a947a617aca189c238d00a51b6889ed52f0390dc86a7c05d739cd5add1efdc337ed28a3cdfb325887f07f8f27aafe42c24cd7763f74c4d1b52a32fddb5381c4aaf2f3864b974fd2061d56c45ef43a9b671e59296b1548649172be859b48a4f84797bf5b63478f707d5ac6cb2390e9b10a2ec9dba282c9a4bbce290961d55ce8ffc8d9cba5978ca96b089d49459ce26c00acab6586626a1e63e80e2c83d6f761c6c6add543c5225876cddb69f1f8dac0c2b0a959857d8f093c7b2b315bf32d256290e57b7fea8e480c61b78f2ea4f5976a9118d6235baa488dfefcba7fa3207616e8a428f5d486139b5003114f6c0400aed6127531e7e2918bf688a11a0409893a8b99bfac55db98249fa00ce2f12efca6997444477d421e4fab7e92c6b1c4315a5487810457407bde2224aa0504efe096a47cdcedfeb87722c8b3c61831a7e681740d9f69675857972826ab3baa05c8f8f9df0ac7e643276659342ac57e9276ac90c879cc43aacde20c7d8f1ce62db8db6025545401a3cc557062e8cab3b1f5ea8d0fd455fcd85c71e8735d5af9d07f8cc5f46133b7d1d037b577c044cce37092b3b84250e70e08c58b14b2335a74e88e9386e4540802ec9ef08f49a34c3d34e1561a1ad2139963497a8cb4b25ecce7544bc065428ea092e7c0106850cf6fe254db598d7aa7e292a09b0ff92e963880d3e012ca2ce13db4d7eee98383c14e33e871fd2139d5a83255056ee78b78014fea1911e9a84e3b946d0d9df05f6ce3aec49f80a9bd6041f85d1e41c62e99101d27f1ba2ffd3848bc8346fb37c452668d95f35d291f38630d8a1043613a0385f9eff748e5beb21e08c137daf27d418167602ba278626a11c7f3087e919c53a79a77f746f1bc22c267107cede511e6501f166f06b887f72bf54d57921d27c4996397dcdd7080a516f4137c8064ecbef26f15dc46c42d3c157795c38f671e93b060be5c490e39174a097588976838a0c22b2cb26d8c42421699e4b6f0784805c5ba008fd0c82bc84e15b2a09b73b6afdcad8d9db01cc745c34de2b9e1af0fd83c7e49b98127d871dd9a03e43ff8b76a092b7b5a6d1df6693e6facdd639967be78a349fc725f3ea562508fe521005f0f5655dc5a8f76755a20f6af08bfa19e56055f2ba8856cd54a203e49cb281e54b7ec2324431a6ff00bac872f1a005ff31255f0b55bee780c1a307498ecc980ace302215d5be193fc9d2b03f5fd45465d649b94e82ddd4c20a6471eebbba48e712ad0a36e4e5d970c99fdd8088ee14a142bee39e63ef0c3fff13374e9d16beed8d404a47aba2a8b13229e9a6c3c7ae782311d7d153f644d00d877621984e33e76301c2f4f334f6c32b7536527d4fcda09d4a1677acb8d9929d105d8059e894e8ae5fd24fb53bebaa71a8c2905602533a39bcf0a70d7fb44c4c5ae70d48a4a7f575e5f4b7a1a9d1afef5d2621c9356cc22942ecf7b741c9f4b6fa5b0308012607923f198e19a4246403421c7ac651dba9caa70fdf44c433f843ab1671dbbe8730276f988fd2dc289420b78c19e7aa7a686a044e55c8760047e7a90c15551d182217c2ee79a41c658ce306ab13b49a647f170422dfd88c1a89ea90ebb6f1583c0a2cb754173adf7b372fe831ea2c711c88be8260d29b3f04017434feb9f83c5196613963b57c6ee776f467d632c018c386299fce1a58a3c6d94301f9b6fca6dd2c1983e381bdccf55ddc83eb260333ac6b30ffb19ba6a38f84b91dc4e9c0339188934e7c317b4009078ceb288d012975cfcb68c839f49284b9e1778092613e2831155105dbb65131d070a6a7d12ad0cae1081163aa24029437d2a931fe520a7a5e645538f49a360d7b401ddbb266fbbc958933ea10c9cf6ad3ca511857d93d37d4fd76b254f199994f10be014135874f9ebf0fadea6d4a7a666c20186a52240912bf2ad1bd111329e86876f9f49e67080b9cc91dd3f05148328430f5206767810b551f73a33843e038938bfa57e9312f29527496bd323aae1eec5b60faded115548e14a3e7d35d90a9f5be3b1b3ac8e159d420bbca7e7cb4a47df2e93cb901e519e31ab04680ae83428fa1155cfe6889cfa4c13e4f313dab4aabb62b61de5fc0639aa2dc8e83f618593bf27166197ed84ab01de10109c809d3b4262748cebbb6ae73671281d318b17e5c1c7a821806244c6dd89d9b73a1b940ae1f74d4cb77149f46f16d672a2c178ec3cfcf06b2907ad728c02a1fb1afc1eadaba0d290191dec82ea518a15c0c76d75257a6c9dc941fe62b48f38679cfd09b590f1ceb819df8f358b78892ad6e716fb6dbdeadbcfb0d26dbe02f622b1783c67640a18d5a35aae7cfe21d2388c41902ee90e09a0863ca5352918ea0dcb1faad7b7a01178a6f15ca9c1d91e9fecf431dd7b25b0a092ecda044c443111f17fb1fada02d87a32792625ce2d61dc51c11feb8009d45385c15da4dd22e7a3a15d5485ea495cc06dd05f7dbc68f50436300d9dfe0095c60dfd01d3df76e8f1b8591daf1c4f829180a4146f774decf2ea020cd6bec817229681ca10be889408a85e0a42524d1428892c0c92fb419049e37894cc15041964f6820dcbc2944e784617733ca617235bcc37b2a9e5e30798168d9d6de199ec5c503bc70312de262caf0e2d413a1235d275c9e4545354a1abd2bfce02ce0083f874015f33d582e244803fe9b7a7be4bc6dc8c0198f14008e86cdaa8a509e837af6a98e77edb939f47ba6629c5a377b63b6da00713052fcaf6c2a0b07115a659821443b09a75ce3b142712419e9fa2ce89d61cecf3bace02dd341daadcb93bcd1601bf909827086b061b7fb725226e641540ab3465dbf3091c024d8443ba9569d671e834d08c9a9b5c09c7bf2f0cbdd6c1e123c803cbe1efbb4181c2479940b87f491b74c1969873ebfbc7558e83979ef957aa5004b8cc66ec45b1486da24df498e0594ee7126142ba023510e7aeeff35b48a546adf898c7846a58910e9e6fb157a3bce28de59a9b480a914a3de79910eb23927f1a952b079fa015dc13e353f18b9d2f1211194dc5cb9ee98154d7a3148ef3be7e19babeba470f03b2db7d4600936db0c1b9f7699bd5bda72f3cae1bd8df9f5e7d0ce65a7950fdccecde2dc427037506ca4144174734adb2541c5befd80aa87869a39c5a70aa9bd1cc90464bcb68fb85922eaaa5f59df622c8bfde6499aeaa09f333cc09fce234a0939735873d810a855e5d815c4d987077eec3351ce8693bc1b4c7ff3bf6e4ab72ab45deaee97c9a4da96affebf3cfb29ac91781c0e5bf9ce05d9921ed7ee8690566eabfb690d27a07861af6d8b4f06f4a0bc99bf4a2f24a4c51a565b1557ac7b8cfaa1b6ab86cec8539b662b86b048274c94c82bf2e25c71b38c5de0fae562dd96af48b8ed553078a6a4ad59ccca78605e20ddf5ffcf2216c27a34b04ec2c37bccc3133659342b0c64b6f88f08a2ef6299b92e9a7e5713452791e0ddba3302a252029e4c9f66b1b0588ae6f053910f45d4d203c11acce0c5967c365fdb032f893e8b4074ad71d51bcdebe70b01a115121600cc81c0fdec9aac198478284735aad9d5ef982112001d967c8ae7c228cc032d5555cf853151d1f5f121040030d05a6b3e8fcffe9940439e2c1daf3bcd3108e18e8aff65f2ea1acc89381a4f40446b5f06999bd8281fc1d0da2deac1a117dab5a21b9ad69b733b36174761dfab890e5248a4a7a10dd9506a9d45c13402833af9902b2d15fd4a31ca8256454c069de5f78eaa9ca55c8c6eb182151386e8dc63c7f739b8f20ca2db890aa7bf01c1f8bebea8e2a427f72af8c27bcfb3c71371b4082dde982d28db5fb76b34897884a8ed500ce4ebadb16fff2893471a014a0fcb06289ab6c94eb43fe0509a184350a70f1c6a82f73d04b34ea6db6324ee0c0564b4df78e126ce21969cce770f16d2c33e4d12669f4866ed1fe3f3b4854cc7b705e328902ee16cb4d24341dabc3b39e110bd90e0cc51a4022f3aa52afbdcbead4c970c73d00ed77da881bc12df08a3c11bbbe2b03b06eb832175b25fba6f90ce51309898b34fdb42ce3975e8690a9334cff880ac34c081b301e9e06ce3df6b682a669c62e560469671a9c002dbdae4268272bcb073fdfdf5359d998f08602c969e4ff78ba20444e08432b1fd543f861fa2ca62f4913a2dd09fb07850905324cb9be0f778410c390fcb6a826274bd009707627f95d13bacb1c3ad6a1fc959d16c357d7014ff5369081f4f8e15f8141b3517cd8a4916f6ace321c4eb612163a407413ef525df459dadda6fd634dd95c1ba856b48f47ca91297c8c520abc2c41978736026bb5592afbd6f3e60946eaabf719139c6ff189965cd0f7913380af9ae278678ab060c2a0bae3da875e00306a8fbd911f94861f1c0f05f7b630110da7120d2c08bb0cc19b670ed783624138b295bb4c5fee6175d7557ba24376e3508e4cc374836d343d2a15c65771cbba5ba8d19830d3c23f264041b545c02636286d9210eda511e3932f0fd0183769067eece98544eace805e95e2f72e6da8ddcfda83052708c491268cf82690b297a4f807adfc6daedb64adeed6b1c25ce86c9864e80d09ec95dd7d6882ab2f0ccf95a6e2882ab0580b16de57dbc3208a2613a8fd45b5401928cdbcd9cce9b7c8743e8c1e94137fc1acdf454d322e3accb38d4cc3c3d9deb2790434d335813d624e1277509af57d8862f62b5432f7866aae0119c64e452e5a60a6154034d729a8c5a4721d15093d24112fee6351191bacce273608eec735d0b46b9b0676517542d489469b0ba3d394494813a0b0e7449643cd684ecf0302219fa2d972314b8e75dbc317f62c89436c906eb868258c660ecda321de417f8501177e3e58ea5880020b3d68e1544813b4d2fc6b6d7045c04ffc562281daba36854db99b2ff420d9e5e6059a689452d8f2a6d07d3c8cad01a94a3c0de92901501c65195c2b72f3857b05973fe097b1b04a2ca93af664d9a7b656eb9b540cbc4ec0677ca09313730744371272b2f88412d498e08d367612eac8bc60bb09fb54dafd42918821acc82302d7aa8f1a14c0aa819af26ab5c2aaad02c89f4b298c73e09a6ef9dd07d77d9ec43d8184ed7ec37496ce3fac717179523021a7224ec1cd383820088ff85ad9d669493d934ed15847afc1089b778ed35e6c50029ed836c3e033e6e703fe840871cbf53c2c0bcba6e515136a8d97702e4a277e37ad0a5c59e27c5b54aa268d0aa7bb946f4d8a066d19c03d049ad943475e6d37706d52e4754d26cdccd0195dd2307162fe9df72e392f7e04a5848ba4ba94a25c204d0e8544745a6e0181c30a9399598887d3d14fe1196294405f61d7f7f709a2e93b4e061070fa35037facf6f8926df2990197e5b76e78f18e2e35fc0b8475c49c3171646ad71c2a4957b2b457d21ff2482c85b106c419aaf3a6e55db591e8136495dbc862077a14cd308deb51b56c1aeb1574164ce6ab027dce259b473a19755e3101659167069b44b5a3951188d3abdaa435c440caa73df03850b0e6696d19590b7edd77e13cf8610428e01dab086724165819f113195aec0bb76cd1c2184ab3909818372544563d7bd4d08f91e00b11a8f6de1fd9a5e89203315d6502bc1a7a7688d1139018cc0d5a90e2dad4b4be772c038326047452f732fef890ae5a0313ef5d3d3c78c03b60274d8f45879cb278dd45514f48ef3663b081c6e5c7ee8c3297cffa6e8d6a882cbe1635c2f8ff551040bfccd195473e32095f80fdc82db350de9f21f463af9fd08d1b2cb848f32c5d95645ab718dc0003c0c3910014dbced2d2df8d977203afc0a21cc9f6e4d6e2cdc6abd467e100200041c91c2440c42fee4a9cc70afd159807dffae0351f123b2128a4887494d99cb027e44f37e3ed7e547fa5d531f89431dfe262613ad2e6fd3350ef5795a0bd9e3af31bd8e7e10b90b0b3d4e93a863fbebc43ac8c093dc6c13a3109fbb0aa1cda2f7d7dc40819cd093d9e1be8be12b1e22a1ec3666a2831d689ce7dcacf426a3634c5d5c6ea07a32cd46ce8a3037e5449d53a30bcf88286f075725465fc8ec47267415d8b28c944bfc5a384dedadec4d00fe42f140cb36010c501015fc39e370359855d0924df5cdb8b724fcbf19efbced4c91ac7ea3e8b8dc72a9e73d1b2524b90188c26cd6dfbbfd70d2a2800381c713b1b5ff33eea4918c838c6a31347bb1b5fc0a099407b482c230eafc19a84fbb0bc2a57b18254b78c52faf05dcaf63b47dba9beeeb170229f10517172ee7453b9f5ba2fa656ed4f0c7fffb3fef03cfe69dcfbb57381ebe91b7b6ea43ed2079ffc0d3384a58bcc14fdd32dc622dc85e4b3334c125fa492d71968d054cddb938dab140314d7029eabc0c8f62b72a13cc0f0679a5e151d2c593a8170a383ca6c3fcb60614e8ffadf55db1711209ec2ed72c7e4ccbef6c36faea53599e77bb660d330750ca5f5a712299d9a82f9a2567dce3f5429e406f1a5570c674bd450595b88e72df49d7af11aef1bd632c1147c07bf76f327ac2f2b4354b08da7f9c1677e8a814ed6aa145eae501db135aa9cd64483f855387009a76a5e4efc00a1c415ae61223c960c4e9f7a1a92a94d6da5a765f411cb294cec72dd733a98a32d21fd48d37c6fc10111c76a90907306899560541d7c2b5ca4a03fd398843cedce8b9e910e53c6a05a91c7113c4d174a69401b9332d795f4cff0b59a0a28396501f4792e9c9ddd8455fcd74dec4b7c825a6beb9a20098e764fa26e5d248911c44007a31ccaa3cc98e6c3ed66b172a0ac82bc2de74ea1e17c3e9294d818a6f376323a1c669be3925c0a32d8ccc61b45253202ea455d0e8f75db576b92c071128741fa2d09d8fa89afa20129ca438ae4072cd0a89ec089b1c82854d55888accf850102f7ca145bed2c5c7b7e1c820d989621174229ebe84a0bf9834fe111d6d4bfd399bd54ef0207ca61627b66160b61cbe5008e13786afa4406ca5c0ea27bf5a89864b43e1c15ce8b9458a35434a00244ab0344466caf3da023f86dc324d525d5f1063944db82dec8060b72b85d38596c60ea6f98fcf57115f87b253d558640701577c916b7b867872193728a516301b53c9171b6975025fd09a54d2224f347abbb2691cac0d42fac5166b0a50515d45127400cee3b2759629f42c97ebed172a076f8dd0fcdcff7b8b9c9c60c38304bd42327da653108783e679e5de4ff2858be654d71ed93604610d95e8a9ca1ce050892b8e1fd802c3be1b67bab9c7f2040635aab083782c9492a15c290d4a3abb21cb52e562a331921d5b3fc409d0f195abc74c92d7e56142664535b5e6048b1c376587185f574e4d02847263eb820de1b54767a64732f927e2c066fcce5f6906eb46694fa29a0e8bd692273fcd94476d277c9e71e43405fd63c2522a88aa9ae4a8564bdb20ef729cedcc72e52643b3519a16be10c7c28b466d7ecfcf4dea296e9deda68d7e0f32f59b4203c5e30cea22a023f6b9c2f448ce12c40178f0137333f32ca47ffdf11ae73fe7982148c8881a09e9eee0a6e4815f629c165a8b9009c9d3ff7ec8be63489cca5a91e5161ac7ffa1f92f23202e0a323d50124d39046ebd68e344335e019228f321f77f12ba156693c845447c8875200772ba3458493d417fa5c0f0f823b6c6fd853d5a6c501b011b097ea19e9c8a4ce968d76878a43bb11a6804ccb59769f980068ce397f2c1e6bdb8adf21e3d23934652a77d3b6e71183d7ac8a1171e2314ff6f5e2f06e6ffd63b6756c6182781753b61cbbbbf547b1953f2baab7fc8bbd425b78e472bc0712df7d560ce365109c72f599ea6e49ea07a439c42b8284a676e166c3f0aae1c943e162e3504ee5231b8c908bdcf68e4021c75639fe59b242576c0ac5806702011ae825a96822f5d558bb47a7055c23d0649d1fad2e5ccec20a8f1cb2429e9482751264503e9fa625b9a0e3d8ef5781485a6b0706a542733381d90d2236cc5193ec4db5d475024fd196ae1a81d3d9b8a32e7604c5f5cfdf6e5c36f515d650d90b406f062fd264b814a2f22910a55a1c02a4c7e50f7ea78ed380f9f31daf76dfe5a1cdae913a38de7a104483f8028121f130d5eaa814e78861de5217a5fc79d8c04b811d0e555ed61160a316065c8f0535f73f03d4a15c09cd892617a9b331269218234f1e64f06d144c23149cf7fa185ab6e61153c66081682d5de5f27d4316ce77070a0b48f8750602b156e972abfe0d89d5d7c9c19be9ddb8cf19da8257556a65f1a3546d6819c6dc34a8de9ce156b5045beb41186daa369a617dff9a00159693670723e564edb7c63cc790776062582c939565b3353ba23228c7b6b88028b90de31b7ade25b7b9e18e957ad2158d0ec962f9a70488398014ed71267663f717b455d48366b08ac3981baf2980abb8e999eba27abbb28813452e63b0fda40b82080f2a6b47b8a73dfe814dbce4a10f54930563907ab522636a7d091d85250d4623b62e0124ef86a7f22e0f28c9ed25642b3658e95441034644b322fd842586bb276e933c2d208a46cca2d9b54b8cc5a6fc2b380f15c0e10ea15e3f824fc6f1a583734a16093fa923823e3fa343dda56c08e942b57c7a33427471ad0d297f37962ee7fb9249d8a5fb14a4ec8ab3c33155b4d8c7026d9cc44ea066189eb4c51d05cf012668ac2dc336c5faa60ca0b6465b404d465e92958cb8910854b68a0c6721f3deb4b848e3ba6536aa43f79108ac22ac58d10f75c2ca15634c67720b1864db8d32cbcc9867554e968135fc0404e652761d759028a8498fa56466276ae90f3b74ecc9fdca846e5bd170e5442b0e58544f21a930c3716580a531b2c0d61c56330cd9126ac0aab3725d5f3e63ab44561e7079f54ea57577587ab1b9ccee63fde3dee693fa47287a3eaad693107961939e0bb26e904bc4f20fa3e855ddd1069500bf5e4fe36388091300a0a8575f3710ddd55743bef66b38d9f3031b029815d5e57bf7063cad5beab3722769dfe6d87f5031cd0e20b7f31c6e5f02b5b95dcf62434ae6dfcc3cb79148f58f50a5fb77786ef8d926e9ddec41a0689f789b3abb09722d477f11db37af0ed905929be6b10a34a15bce9d9fe8effd63c928171ed1313b8d04815041b8362a5ff8c97dec6bd3b865b09e8ab2aec6fb1bc9bd079f74d7f3b73b1edc14c952de227246f0f78596c5494ea02cc258da6bca8baff0d48ff70e198963fba962d3b0a1536e0b81217f68abde398706a7d458e14876211537699464bd0eeffbd81419bad1143c7928ac2219ba69ea1b6d21c1176dbf6cd016e43acfc328fec2daa04023eee97e398a4ef854edfa3a27bc977a8820cc13b30f4de54af5a0a5b9ce0447de576de2cc71676542e035f1a71f6e7b25fd115338f6b2b5b3bbef53c35a18473a75785071e2c19ba1defff552e29dd16ac917f427aaf5e96796ccc23897a3a25352afbfdc661d342273fa30a84b92f9b7771f0e343c422a74a19d2314497bf3c12da36b822e8dfc2fbffb190ff44ee6042755d157bd3d075df54294b9264d59f33454cc8574f626d194e48d0f72db43c1e998758c2bf048059d742a9647b5dc496494517f221d85e7f68d06f92e77ef39b5bf5188516c1971540f03d6fe40b71cc9041e08c82aea44d8941810c7ebfe358f4cb0ee745a4b8430dbe68f14d195b88c03335f3621d48590d2aecf1904548068bb295a1f17422e601bdfedfbcc0b374eed15f4ce120436c5787ffc38934a32cf04e927b3551fa409039cbc67f5477e6264266026a18d9844db1f68109c999b81c92c9ff4787c6a0aebbad9d1f0dfc973286b320e590660a8c7fa689d64e0b8fdd8027209833159006ffbf63b7d681c2bc014d9c3733f410ea49c5212b73a7bb67bba55abfaf11f68b35cc02bdda03acf2f2aa13c6bb6f439d7a0b7997cfb4be902117e811604707e306f9cfa3ce78075b3de2aa0446162f306bfa1c7dc116222a964ef56907b20f830bd1f8ab6ea9a9567ad834f455167e21d0c5f8da1236db886dc3622036027d853a253142e092e1e0bf62de33230bdc9a5f616b9cbaf64a93c24f4aa41f41906157f47290431b079c126058fa9b7e9bc021222fc727f6a6e54d212518380ddc9b6385c11ff52de0b3b9ea4453c519e344336c063a366d81575e7308ee087a27d7d63db9fe4b70879ed8f8873979df5de173485c545a5ecbeb6b61c112ee0dcbf98e38fdba15a9ed96a5eed82a0d2e26e7426b1ca300a28da899baf37f6658ea2281c53984f83f910648a10fcea22b50596b4bec8ccbd73d1564b71c2f5fbfee610ee6108b60c4a3da521d681668d4020b425cdf653af6215bc00de22c254d38c994c7fd1ec9bde3ad3c9e776e0779613d43b3cf15be5aa62c156293103320b7c4caf79b3762d8c37bf85965c0ac8ce7625d6cdb6a35504560cb7ca5209b2657af5973ec64e003458b200276b4eaed2f06a95869248d0f9c850d62095b77f24645b6c37fc67cb91065fee3de90610837f7f11410200280361164db6da78f80192a77f08a04a0b2bb1727c381f5f23394399e5d1a7f9b2d8850b3fca6bfbf07c2fa7263e6f948ff990b46d5018daea238f22216fdacfa8eee99580ea0e895a5c76a3d9d823f17d7a8a55a838bb61665a4265f1711d86f183977bc377c3032880c973d03be776894ddcbb2131efb772524f96a2fab7910cb487f4c1faee47a91e4cf1f0b5e2759849ce89c13b9184d777c1907eb4045ce8fdb482fcc73401873a410f57c161d212efe896e217f0a5ec27a4d280fe54ecc5078ec7e6a879b25f7998109a392c8cc6baaee754209b1cb613e766076e01392b673d4118d09f4efb534453c94f9f9931475ccdde89bb9a9fb54b559b07748598cdf23f76932c23f80a217612af91386b23d8d57103c7e9a7baa7db45b88f22fff52adda0040df8419d63b99d915680af107bd2864e822e05d4d4b84baad8d4aae411608c11b7dce75fa44e94405822d318716f2021c168d3c42fbc05d700e100205a64073dfa7c01b411c3b3548c18a2716244d69541d0acdab0026a1264bee7f5b3e2e647893498d0354b7c32fbed1daad299368cd181b4fcb790d89a83f51f1818ac0c575a2187288dd9e8769e4012654da1a00b7e7a3e474dbac795b9c9a00d609461d0c45bb8c6eb1576e9faecf2ae99bc0c989b541c577ac9ee88a77f142af4487490bcfa2728c925846949755c5b722539c210e42d77c0a5cb06780758921da98cabfb921e72c44d90d03e6622a91c0a97fd7a20d01f762829fbda8e3f626694c53d327636b26a21e0427f6fb83719624928dcb2c23ddd437fe2ecdc0e9afd2348f0db4061a3cca4b4e13cc21e73b1eaeb2b7b427b6c3b12cd062f556c03b383e14ceda4462f2325dd93f4980e9d7e52c47565b63ccfd98df00772ccf874bae7c104b9902b2948190b9c9f51605f48c19c233a768913646e147df82bcea785e06bccd808ef1873ed733f8d053daba84cc6a6e36ac5e67302e5baec2e4a541d2190d5de758ed45d2a49e428eab9fa2366047db16911324a70a650f074f3c13cf7d34676a906f463e195ed786985d6b47145d5838c3299674dd0b00be0e080f76d51023e0436bc89415264719c000af24ed7586e16533f33d20b6c574d261f83da9848f919749bf1570d73f6c23fe6baa0bcd8a0d3a4ae02439f66ea25d8cbf64a3f3293b82c0a84c603f798216745d4e1f105ba8b90808aa5f59c085bc1348e87b137a776e6234fadb76e3e9a35f77f00617fd7b42236410459980ff8c4ef098e1e6897e8746df54c4b1fe3d41e864c6e3594f2065a13db6c7d0d0b3aafca78facc0325cd1230e3da6521724679e4e11c10dbfc126c76d2d9f4cabb7a8ab928a9308bb78c14c70ba47b1f90cca52c7a6fb1a7a1a6425fbcf0002c78375374b40a3b5e7dc3f4549cfae1fad9fd09b63d719b9954ed7bccbfd2caecb14faf7c4852822b50430368e5e5b3686d0f39e5cc2a0e6ed67a51bad34fd6d3f50befb594365cf3a1a88cc2d27fcd300091dc2162b14d4c3b13cde3335f824b7e1ad254805343b79bf852f1591825c6068acb53a255e8dc36169f3a971fa3aa66c397a12f12aeb6a040591fe3d29c212180fc6899a9d4c5f80819cdf0ad19ce59a6974bdcc37b001837e108e7ef60ea51b914d3e5f25c034999ac3b29fa04c80dcf8053480f45b5d4fdd0062ff9e14230a5a9816fda9c94cf9d48ecc89ac4615b0961350345b53f8ea6609f8612109d28c936e02d10a8e5ed1e727ea59e610bf4bd06bddc51f7b9a52a97fef9f8dd98b17e0cc1c36b869a44170fab49f24e2e93c64d3877a050390abf3372854f67eaeda116e8264d9faf6e4c9dd08cd04d9786c2ce0df66092b3d2d53d9b815dca6254e62804db325d1fca41de0085ff3b95d192362d045fba085d91fe9903ecc04e6f10d302230bf26edc6a5830675a24a0140ba7559ec95a205b934f72d14990060046f51c25c3271b3cf8da43ac518588c8d28a728e333b7027bfddd51f0099d99817e1dfb500c91774ed64e6c3cf8e48ec31e37e9d96e9d374e82c2288746704b401134a8bc6205724c10c903ccf0826f5c17dd72a72c6d0900cc40a17829112b9784ebc8ce201647aafb57f9238bd79998a3fa47fd3d29f37045272a2e2adb153fdeae93a99979a1c4e2fff0dc5bbb3ffe70cbe61fd139e30213e8db3aec8e9358bbdf06266cb3c2573f472f389e6c54a63ec23acf25a5ad16d3cc1f9fac1a319ae2e720fb763ce195a4259f7f12f68d9d6a9840b7e8d28543fc5c8a0365688dde459e40fe73abab74f459baec67ea6067cb8f3484d040bd00280938b10c982b86f56ecfb0a359a863f9194f1948d93aa024a0470bcf4f047ffefa328d613303cd41ffe7ef533ac0831d5f9d81c06abdbf374289ad46de7ac6bcbcb8c6f0ec87b5f14ceb11bb39fcd642e30876203ad893a5063f80e2062fa1f7a85056f007994fc528b54f444b94e3d95f48b7941f8e0010134025bb32b46694ef70178f24bbc9911f1bddfe0eda0acf760169608003f21a20051f9bab001e1d3bc62a20a7c01b1ccd05ec668623e3731138028db7bdd192edb1c72ebb32fa5ac3c4b4bc060d03e30e79d88a947b377f495ca6c1295fc729fd3bdbb1215f035df3b2d245406aefe0c5f54d6c40aea666936f3dedda625d0a5207f5c4ad3bbe0df00252f9b15c5ec884afc18980bf637384b8a0bc5bcfa9317c4a9fdc5910371e8dc4c6fa9bb246c4a48d0da1033eb379f8c2693189356fae2bacfbd842333776e19a1d6dfe8305dfdebbdce2ff051773bc292d51304a9bb13c8789f5be68509ff594eef65e5031a1c6d2efbe068e7b9117ca4d9019af222bec592b1bcb0d12e0aa28ea9358d8fe4796a52cb6096dfb7b4414509e08007fa8530c7f82e865a847cbc47d361021b2c4e2725f038f23084b4463a97eb140d0dda49ce1038730f8690feba47fcdf1130dd12ba150cb2a6b58d6acd45d295a4ec3723110b940b9adfaea7bf2f1c36708b8b6ee4756d89a955fd0eea18433a0580073c06520381833addbcff38f3504ee0176845218e9df2beeec03b0fa829f833c59f8fc9167d3deaa78421e196221c2ba2ab250a80ce31647e634c783a8f26fee9da1530ae727757226dbcfeb5911c6c0bb23f0919a3b879a60bd8caa6caa12688430bc9d66ca1d74daacf37ea434d79a01d06a9778ea19e8732876248215387f905af2a673b827ff59e977e3b76106ced2af270a09dc871df1fbff655e8751a27ea6083cac8e099ab411f85ac91aee4c416f7766b7950d8da2a33eaa7f0c31f79e073aa8998ea179e6f6c9aac3f981bdf2e56d8213c88b5f94d9a202bdf9747f8e8d723a15a7a0fe7ec9602e68fab323368cdd302679b8de0fbce7cc80eca87741891b62816814010b5bf76e9fc8930cc5673f94c98e94e464f82990f7aa977e15932d87c75b6b8d5304d4491fb40de2ef2b5cc125ec0840325d97cbf228620029a3e4b300085f9dba5a46d9ca64ce8c6e027038f57fed226a478a2eaf8480a1eda66dc16c056a1189df248b80ab4e7be73a3ef333fd0cc3f7d5f5b1104e4480a05ba60ea2022d6799166bbf1bcfb894420703f1f6e983311bee00ba02acea0f01d9f2d28626a69c128a534e310a817fa13789df47293658bbd04454b90cd2f2c8c412bb59d7c9166368b6613227fad9d998406e208f292690512f2af740aa19bde8b1e398900a387c4585025a5c4dcc55599a0f4a7bdbf088823b5fbd04358077f0cbfca244f88efa3742c8bd28dfc1c1023f9c4045bc3f3cf4807ce54f83d363c40f01322822c73764d7f354ae171144af575d9d62413aca69078b418411c2fc45c4147b948b7f69ac290a77eff8794802dc781ddce8f60dbd97df88e01f60ae69eeb3cabf79aa2760f31b6f538f1eb36b4235daea5d001616ec607668ef85d0dd766465dc1c4804220a52c01ddf346fd824c5f0047c5a2ea58b12a5d6960e2f8de90c42ee9cde1bdfd7c919b2547f9ca1936515bc0ab6441c83dd59d2e001c4aa325fc8bbc6d113986e8ee21df865c589bb976f24fe528ee2255f5202047fee39da7d29ee0e77cd3f3911e7ae51bed7145886e5dcac07938d6bd967d8eb493b139e375422f81df052256b621aaed2ea91897cb047aafea11241e76b7a913adbab7b90ddd2270d8ee4bd201a9508a2baf03cddb81665484a0e1d4806de53e086c680282c11200cacdde579400323d43ba54875944a12c9be7f206511a0324eba51a865e3dbdbc5fa6417d3c0fd3a53c84460d48670412040b5c00583a3c3d8a13230e81daab76a7f0d0607671441014609ec2558d48146d41bdf7988709a3d054400d0f885a9b0842a03a84d1c4e18dca0e73c0ec21f2f051b5aa031a0ae26095375956bea6972143e78833b45fa62316eac1db4b29bd0307f511dc89e94a71970010344200c8832e7800c50ea2acc0c6cf5e0a261a294d5eb0321efcac26c04a00a83d8d4c3f493d3bf896dbb208840d1e015df96ff6b328580b14acfe6bdfd95e3962a60ba709b0c404b24a601ced4ee47a3ecdd0a84e12f9d8aedbfb7ef9517fa2bac27a30a43733e770e306e002833033e1d1a18508a707adb21e014db702b38fde0b638b0ba564307ecdde58488a53d48e38b6af001d509ea41eeb47b2270d2fdde21f599818020543279dbfb87766796580ed5a42942dce83e34b3436eb68ff0af90a1866ca8c98e2112f1589299256c7bea941b3a330002d87f251a0a60b53812b7b39375f61dd26a3dadfd8078830105860f2494fdb80921ffd5c3baa9fdf061f5e9c5153abefba40dfa38d199716256204c45869df6d494855d975fbb7f75c29a67503449f82bf7977ed37a0d8a092dc2b4df44a14e0696ae2c213d32f840980658dc74c98afbce82d30f40945c486fc7fb04266fe179bf9873cc0a88ca384b770955767f22cb242793c1c3055d1c18a8b27be2260cae678c09421e4097dce39d671b506c388d404ebbce157c81245953bc099026ff572f66b3f1e998cbad4cc8578bddede0a93462cd023c1d81cab1486177a1ad0b50e560a7a0661402a09404f0bb669f21e06b2d73a8edef0eec484b01cf92b4fe0793d760d452d2003a7c90dd8517ebb54b13bc5229098806ee75f517b2349b0ffe6805dd3f1105042a198e96190bdec382341b4413596e861247d0c05eb53191b771a2c275e531876c179448aa95baf04de0961c492df810cf21760531d78f81a53e21aa008e769a983c90e23ecdbd30a7f44d7473c0a04a308c6db442e21514aaf92dd7e999985ffc9affdd5d693ec964b164b30ca228be646e6b516885fe327d8fb4914366a30ec4e92871411c84394fc78d03bbb9b2205bd83f890d33d816eaf51d17b6fa33c59c22e5b9ffdd78c035c5b18862a06a529f137261354f55cc887c3a6d9c53ba7ace860765fca1607c479e68802412663bbf1bc613c0b99add0fe711067609336a012021f4008957c53c03261d82ee07f008c02e003c213983005b22729e433087f8bc81e2f8dcff56bbb0e39b0d22a613fd2d12c6ff9c3e992c6d10d3b60fe8e7089f763f191f180e324c9fe899eafffed46fcb90dd73fff1c87f11f402bd7200bb126dd60726d97a12ae60703a2c0be0256c7ec372131e44e87fd497a06362e6d01470d278419eecc7d223a821b616c7488543b3f620b905e3fdd69c50301b8b8f34ddc4dc9436f96c31af080361035c6c38cb43ecfe035b98f305fd9e529340336cdfb6dd54436ee8a0b996100cb4c49d5da46f383f14f7066cab51084e22b0bf1b06ade7826f03c77ea5a0b8a54bf12667d8170020c10fb5d22eb216c84c2e740d813a6d269f95eaf7a1111d7590e93d49b6980afb80c9df8bb595f2689bb6e883557a476f0d6f7d9b224024ab6c18198f44c4fc94faa88d288d86fe84361486cf97b52b9739927eef032175ceecb47a153bf11174cca63e3b042cea5eb933682587685034609713604d9face52a5f97faa70164341a14a440b37aa11f9b943b4ca6ae864e439c489aa0665fd05272eacd2581404bc9b525b8bbb030341d766e24c3ed0b5ce12f56e5c213fcb8ec7fc07da3290477973da0a5c455f23707002f81cf634d60605702734cdc48d7b5deb2157384883020c173a4790fda6daaae17fe90de3bf6be90d07e79ecc01f823126e1d8218bb0e2230521161cfcba186eff78c05a4c0c41caaa40ba4b0f76da563462fcaf46e131c71bb06d33b72e0304371c4e2322d0305907dce9a32b84a19f7403a8bbe58c7d9c6dd629b70fce58b30bb20cfcbbe1f86910f39a579e7bf8f5848572aba262937528b19713a278d348c581086b40b50e0d21b039cc14420fe98d4b497251ed935263096ffb6ef7c270cbd2997e0cb0fcfd42fb9b8996018c63cf2d012a9593ab2c3acb7a801a82682002843997da22d0912058437ed029b39712e714a2506b20c07da870caf815b3928bb9450205dbb4739b5b9514cbd6f2f4339297dd4bc62bf53d5b9b8d1c430d6c9b533fbba00f52073acfc9044047121c0b62a90d433cb4e9681911d58a625b5702a09006fd59d8348ccfe404c3c337bc21405430cb7acdd247e14def8d3158cd3102abdee328160e45b8ca9f2c0e0cb63aa3f7f06a31d3be0033747f491a1c29b562520843ff57e697251505ddd5bc64f7469c1b7f61476c9a9e56d8a0432e585fabc1f81ac5e16f012872538caefe9072f69c666601806ec7e909374c582ef13ac2c663fc0cc6813f58d0537a4c7a123edbd52c0054f39a9a42bb091e100b7a2387c2b3436196c4b46ed0382b5feb71e75f35138efd2ebf5aa9430aed0d28bcb1ed2fda4d9dd7d18111d3315c131abbb6232e5603dac56a07242f1d40360c8fb19c19753c780f1d19b0cd02e0e6ada6329ed65b63724aa9676fd22e9f7f28619645301b70bea5416f1c6ede447ab10b2bf9e99a5900c8865e714084b3604b722cdd23e940b62bf196680ba1fd580e92578a31c9a56ad1f864c1bd88aac3d9d9469284be61d73ee57466ff0610764455cea8ead03910768fef52d6e73c36f80f93cd942e438e3827c5ec941d6f53c52ac47de6a53c500747d7f15d1df2580bbce169b01c3fd29519356f670ed52eeffbc95cfb4e0901b8ac12a13c0b1569cb39c96abc6453aa70690ad53d27aaa40b9c215c6a383222477ee29048b812354f42619006818a3cee5a59dd5b07f99ba89fd16122f75258ee90594001403f5eb4029012ffc385397f9ac90be8fdf04e1f407d40978c4d0a3325e4e05680b6661ac186259ab5b7bc68948d8661b2773659a116c7286dd8dce631c7548652a3da7ca6141b02ba884a7bbb8313e377e39a36080fc41f47088cb3e3c3cb0034f12031fdc89286d9600e7f807556d67dcd26831fbc2e431b8a1a79f8bace2801f0ec9bd15873d03f046508e47595ffb417b9930ac5011a582606775ffd7be0920504000238423c0aace9e5ff7cff6fdfc6aadc438423ff97c6042700dc006e22cb333caa39acd560d1bd549fd29788cac5bf5a512516039c53e2d265bf784dd71872e2e7dc74c48032dd9cedb5045ed7745f2a80eb0dbbe9d5c6abfe38e7a1ebd28d1b206b34d0e2475fd2708151147f68b43466b3a21502912088d1206b7ef912b286be35735342150d20ba1a5ff464a5b4d0ca4a5e86a43183ff08e0bea10ca76edc0318ff9ced859121387843800b456263dab5657002e70c30579bdcf31ee230fd24eab90258ec5d1d40642260fa89563804bf30650645ca6802361f219536217bb95660ca60c1cb51e914ba6a15c0b92672d9dc513554c8e62d33f22487e919aafea478f6af1edcb91c726e53b06d837561b2d9f301776096c2d95225fe18a97c5a4014428afa96190964471fa1c563cf6e4e1c9c31a2629239393dcb57470552cbccb6ebf1ea59c5981aa1414a95e90f50d205fb095ab3d36d7dd8d3b8db3ffb3196a58f4083c74d0719f57664edf0af9fb4afc6dafb810381d32cc829b841d995d702e360137c03911c7399929e6ac931cf4d69b8a1ed4df1880f5f8bff89551a6f782843a7744e95643073ccd86a5dbcae84635270994b50170c12c3099601f9eed78cf46b6f55cd1dd07876f8b0b5543ad5a19247f6d897fc4f42b2aac96b2d85568ef8e18c8dcd8328b1fab7e2ac006896509b37abe35d102a79c9220910d98aec93ca651927aee34b40f91d38cd7bb03f736ed637be043405981b7996a223099f4ed316f3689eac6cb996356a5e7df5a9e98662d336b6f0845fc33fe177dc7128a31593f56d09089120169c0ba3ab851a609bb80e771affcbf0915a67aa0d30dc58bf8fb6a314784dcfd5b342a682d21a2e819f02cd96a1ab17e33eb7864be0222022d301af7b983b25285c835b888eb5328274bde518f1612e9a484e874b403804aed2ddfe5d09f4b219f40d84eac023c7662c6a7161d5ec631c5bbd93bbd1cb3b5d39b417664c1c3853b29e69977d2f5e28cc192aa6ed58f16bb0561f73644b6ba1ae067236903e3b5574e90d13b3ccbace076176ec352147331f34d9b82a0e9700dd9d2773bbe9495cbdcadeb5eb906124577ec275e43acc71bb6101e9d90bd764e8f5e3c2884963009118f83a6868a9ee8ecee93c9b10edb062289b9ea7d2ffc70583c69b1c7d4ed92e1755844b00fdeb5e1de24bb3cd2d475ec92555853cb16f7d84e60fb5d5c51eae1bf13a2ef1e410c5b975394715064804b97784a30e5ffd2d815636fd12027a60abfba00426d042b4e4cb0a798b07a455f6d323b28d25e823a29ea8d006092ab4f08c006fda663e084dbcaee434b0ccd4183dde7edae714baf1921d042c930ccd975c2ac0ac40128c79625a0177130680123514be3f063d13da90982b0b27e35217f6dfb4d5ba0802330635d33884fe1614280e7277ebb1f0e2693c2a97a50a0471ee6f5382a8da171f6611606b7a9546fe28a327c5b5c71266b770510fa5022fef235abb95326c4f08833c6b20714cfc5c5fdd01b375791c3226f55efb37938ac04c276b1e3ba45c1856e066ccfe85cad2292602b19e1d02557de5caaea49efe3aa6a7f4dd702b744ff99854c3ee25bad1a04b9db8ed2870bab9f230169373d8efb1e0283125bc85015548e7caaa328f39f191101c00e0887649af36dbbd18b0911ad7b120d743ea6a5c34b5ea34227a2c44d971184b31ba4e73dd5e9da24268ac5ec7eddc9d1b82d6e8196af69955344caf665eebf390c795a321fb1683a9f76ea4fc94c4ad7dca24a92466ca4bf6d337378bce6c2cd5e8ce9e9f508707414e88558545ae0bbf4f33926b1da4cd7236c8b46080284d0231c3dcdee3e16f8b1f50fc6cfd230d0d52360b66e9b5dcc31a9ae542ac0197147f68e2434805b5682d4f38e2c741b102634c99a355440cee4924982ec04a036c96110078bc692b4d6e8b6b50e20eeb24c110587532cc524190812f1a473f6ebd128d164a4514ef008269b66385c2aa28459b00a67f3346a401719d7f5c608c8b53f4496b7f943c06444714032497a9cd668c07bdb9618f3dc5e954bd23752f86b61348fc0a379c3069deec1d6766602b579cd210bfaae7c546f3cbb74713eeb8e6e02e6a3310530f31b4598ee0ff9a8972ed976a14ef3da27fde0195cd7c754603e9274c1562819d713caf9be33d2314063aeaa1abf602c2b1ba55df525562126c4a813fd0f64a9ecd59d9fa7c8a0baa5ccf72215ccb6c99d79a89563f6ddfa71aa17615ec2adf2c0a12517cea47a12614feff28b1fbf457b11f5a3e55c56db8b03959a075e13b0f8b08903674be2768f3c006553bf31f412f4d018927832ac97ff113d67628bc8ba5439a44953b5a623e1cac2b43ed5195ff9004cd20e35185f7223e2e7dba2a820de3a80548d02265bdc3573e5290d80847e31470f49a4a5552c0aa9441dddee512337251e5823a7ebbc8daf8b51515bf4008e8b841892d9da85d92a26b066d853213511585aa603bae879d5f1d916acd73db2188e33dc2ca0356c41c5d53956cef084555f62b6a65115a8b5682b52a989ee49020cc9b7545dec3a96d1ef5be82afb34cb07000016a3a168482962a2ee489d4dfc5dd80664a4c4cd1d60d6f7bb4d23f429c52750b3384e17444247a597b856196ed133fdc5c9383aba967f62c599dddba53f94ed25ce59c9897ae78f9eaed6c0eb1943dd186e39ddc991a656af5af98d6baa9ac2fd0fd677c52e198f095cf8b3af5f3f0b14dda01f4ff831f2d57b3409741df6d4ca6049ca99590ecc9149474039d3e5cb192c2bb138b1e53772aac27a685836058233019fc734dd3137f6252da0b6db058e2e1d460b1bb2c89da4c59f519a9471d863c836c026010880c67ed013b93dd1101224416d0c13122356d239eecc47353d9fd07653ed9f8801d770d274db9ec1955d79cf0db166e9d9303fcd079e5af1b62effa023b765d157030cfcf04a12853cca6e71e9a758c336df2c451222f1cade783926f56e5973400dfbdcd92c176ab71284c23a23f156b508c7408497ccd27f423fb67e4ca6a1298f8f3dd0d38195330dd0126fd304ceb3e7ca94c64412d6e693472d6192968b7735460f0115f2a0c5489513d7914884860b543104f9d5a2b30f0e186e6db49db6f57bd079a232b10a585896ded0e6b3d374c3ca39f68a3429d3410ef09c9b1a7bed01a84fa7ba3b0002c615e99953029d720cf29ac9ab1f35dddfda872d2b50ee83368801d2770f35403cbe88e26624bf52097baf6a8e686bf66301352c0d5d34839314b302e951400158b3d305ddb0130021e3a5f6f5f88bf584c817403ce92abf8e1153cbb751e28c555dde49248f0d3f5c2c911c420be4bc4b6e1ffed2e0ab3c55220ead4a7dc677411c6c0ca8cee129230e791735b1e6166a0c47248a59120411a3bce442bed856fd37f9c5b4fffe9d14600f099bdabcb79f19c05c517d4dde820f09856806b756e08c615741b80e4db0935937fba3656c51343723999d74db8bd5a163f3d49787fd9b04660895b4845824baa085bb5ae3850c57f2ac632409c39546ac7ba462956a7f483c01d6094cb876f5d9cb0855163879a61c5b543f25a6efefba3339cc753ea5cb05f79aa741a1ab8925655f90eaf4b55abfb8d0db506eb2aa2ff02c0d110a862eddc9bbcba72a7941e6a169410889028d251ef54bf27a8148708969ce8b5de42e71454ca4f0ae8c7d5b6270215b75b6432d9e051e371cc7d365821541b252277fa579901c1503050a9aa0a74b376224f9ac03823a04249135331bda0055aaf38280c6d876fe9bfb59bdf17485c8f75a1832c7c2376df50535eb8d8cb0903a1d9102a550d4b9824b0e7df9077e274bd4792384ff22421c36ac55733793e1f28e511d790a3a00160c4d38e9abb528f2b1ee0b9dfed9adbb77af4c02bbf64ce8a7c2d2c721b024ae246cf411de3a8fab4e2d3c4ec8ad3641dc5ff4ec9421de138926ec456b838b4b91874191374285f407e3453ee672e27a4303aee6f194884314eff29f396edf4945d651b8c47d225418d4bfccdde252f6d605c56cccb2eff0e8b56db97f5242542350fc0a727063ce565c0396fd30b01a3062690567c61eab7f0ec474288e505e25519108d4f4743a9595345d1ea38e9e8c7486f24902922941fe6847a802eed98e4982ef20c40614695a3456ce866e8b2ddcd7bfae4ee1cd7e9c774925e6a6d609e92a45d93f22fd6b2015580536890d7fe8f482fccc0135372fe7a45459898e43c4eb374dae6eb86bf2dd516807efc6948ef612a51ce4e186f62375287070756d4c1bc20a0c0ad7a50cb4a2e0ef2bf2dec55b1fca79076f1f3d18fa2ede2459a4db0537c746dab90578fc86a41062b5f067743ff04a66b6b40689feb4e3ec1d83bc7093de3f9b9478744b0d86f729195a42573ed5b1b1649967514b0e9cb195f384d44fc3e2da00c932e41a8498fb12fda9364b088d7e9318949b90e86dcbd8d6831b94a7b459b55abeb259d5c03a58a0c5f70f0acb0408f7b963256e9b5d192c8212c04577b0921fefdc99e4514d804243d8e093b575ec49e64bd0b8a6cbab480a12ebc32dae58b8cbb80a83662e8b1e842030c5d4e5c0771ca2b23c6ecbc02a79a41f0a9570771155720e67665c4b360ac0ba4cb504ea1840f96b45a116bc97697fc8e428c96f7f86e3c8a2101c1ade2bea3206a2df9df7ab3abf502a9986f607aa41a9070f84c337edc42ceba4478d18bf4d48337763a0e23ec673fc6053754f894232f8e089c8d50a6e9968320cdba5bdf51b5fbf515cc11ac9da4ccc74a072b0684e9107a32d58287a83587461415ffd6ebdc64360c80c2d4a6c64747502534dd2bbfc5a8c148ee389ef5ab50e256af4756d667433c03fd9e290614dd26fd11251a0f580ae50f154a54f7cdd17af61a992d73c2e70434cc6431e504eca4f9b9d887f24d09eea30206e8a2a86b4bf6ec1c0a00bae1b628795a50ead9a71853eea7bb35101b92aeefa1c407bfc704b43528c97babf6135c87c53b0f8064b3df4507798029d8f6104a6b54fbd322ac023f4df55e0ce7868abb5edceac2a23d71550b2081f4b342c1e6ecacb9372b281fc064c198a1280bb49db9206377d28c30d0d5219b9eea58ce196c584449b197ac9a4b266870f4fba1a7507fd34a0448bef8c8841f74dcaf0f1c3d64cbcef8b04ae9c3b6899bb9c859a82897692004040e5b79fe7404da62382e8511d84dec8932a0f5117b0a4feb35fb913d26d71d2263c7e10bc59bb3e96d7c5fd375e614720e7eb2b902428b2005dcf641d1c21f41529787743640fbf2f235df83698c44a3c5ce8a40009036b230fefdbd09560dc9bb25bc4b54258d9436689638e45dec1210d5f2b538703e7d900809826674bef281cd1a474690c0ad4d6233b5a93a395d25a2c460fd5485993cf10c4f48c70e25752e5c60e98b7435dafb6a4f104a7d9d50ffc43e2c3bd8dda3295f1c9400570ed24e06cdb5bc83a36ce79b536e0cb813cc7e0610110072b89f88751fedbf0b96a0a7389a1c31e4901aa9b29056ae514998d418bdf889545fda927db125d521fe1b2e6b43dd912affaaee0841e2c442edc807102b1131f054017d1659d602caa2421b66d8a3f42a4a49f3026837912122886427f49e692caf0e5815ebc9e42c50e68d051914b681af244e51a4fb6b2833a732861a2fe111d00141b6bebb34fb9608f569dad2fca759ecdbd8c0d09dad848570a3e01f8cae33beabf01d2bc6a5800992ce1b9fc9ddd3b993bf424e9328923ea86499593622f438693ee09377d51371d6068412767bbcd870c5812d141c97d8f1ea068febd27b7fef3e558497b8f49ca3cb8ad5f84fa358d253bb5b02ab77dd7bfe865f475c846512d20e30980f39550082dbfb17cc903198c040a2a552d8fc2a2ac15b9687c6b6128a0f3a8d4d210535832d25948300f349f03340b4b6122be9396b9d5997f3d32337022fdc3ae79dbdfa1b91e32076c431df86d913c68901a0321aa631c032ab12d137f4fd4265f616f834a56905590f07c6df19297a9fc542c9740b59e3cd38813b294611f56ad0c6ae6c2c68a53a3c76d76a3772ce3adc0e97682957c9449c62fec7eac2fb3f711079126be5d7943b67772987f2a2131b23c72a0855923bb107acf422e43d8a3bd3ca8837b1d35a05b413432c660512af8ee06848d909e4ba5b56e0ae9989e69608c825a3b3ef79984edbf33cda553ea47982c7af6bb0904380073c8e06824d91e9caa1d10652d64c6d9936af31111d0b10ecf52e92152978f5b37d4137d51e111109807d534bbbb5529aaf2efebd9ab6873cdbb0026836726cd99400ecafe7e9dcb13c15002b0031dc99c17e67b3eb84fa39e7170bdcbb85cdd38ae7e42bcd602fe5a4a266d44c46147150cf0a37c0ba33c814c7408a260cac35949581319850467efb5d0ade83213b94a004ae37abcca97e3b082503c744e48fc7e0d375fa4389d167e5b16b163810fd70be3921b7b09eafa0978cf2f8c1d9302050996b74c5d936f1538c240f600124aa9a889e015cd8345f48f0c88aab7e126cf4ba0fbe5c9e365200e8c8d18f82368723b121d4440ffb7938c7a60f0be4691e34ebdc4d5a5da843fb4929d33b448b830cf6faa0250f7db2fe805cdb98f2b6166c98855c5d2ca0d8a58e955ed39a13e0a3c5600f67393bf477972ecdc249609186a13c61a5924f901cdb719899811ea2841311a836acdc52ad5f4cc39b5c6884a65363237d37e54a2565134aa8c825e96846a0d48a02092dcc12d83991fe136be922c5bfbb8999374dc447077586d42fa6f2cd6cd40768b6a542391163536a077ab3299b5a8230136cf4fbfb51af0c2928a27508e6ea428650998aca446eadcfeb1ce758367ccb7b4355b30ba87769d1b1b049370a925a46ad3fbfd58627d50eec392650ec8163f35aee751b647422e367b5c9e39fbb5532b71eacfa7364dc79b9b298dee7ab80f338dcf428aa039dee9ada215f459b57b2651171363ff5bda57d5d220c51a666e5fbf6cc35b90af8594e446ad3e13ee36e4b838bdb19674f22b509f591128d2fdcff04a787440707795610865996120aeb78e42887f8c590101630a77b3f4c44859350a0507425d3ef634739b86a0a43ff6b5bd0e2e171cb5d2e496710ca32b1cd6e82157d86cd901025e4dab898ae541edf7b5e92c4dd7e8fa78bdb64a88f3b82428e88bd36137d08644fd7481be4d0533cc2306856e798fd053ac4e24ec58d317d91f53b8c104577d5a896b76781581db80b183787569457ead264b0e61ab255cbd803b6ce04472cef1aa4a32032c51c3ccc6eb9964dede885f5b735675b017a866b3ca1a4854e83203250d9be16683e884c3e0e52abb269770d8684f3a91514060df20788a2ffd53f8203926b27dda9dc4b197187f26e653fd5221ac3549a5b431ba0ea3dd100a35c05757dd4d8c247668fe9a230fab53032e6e87af9dbd7944be42871f285894df340f83276eb12cfd947384d30cdc53d52cf5c1328f710202d6f2682839328592c70f26f9b1de0aa29a332c781f6e0975769ff0e5120e29ad98fef68f70232091cd1e8aa3d75b22875bbbd905cbc5f31089b6ab0ce84bcafe413bcee1d25202537ec96d0b26866032a7fd5dda7c0e52f6efb9b8df248b580f2dcddc15a680959bc24c0afb98c407fe9682d54da6a9d9f7f1b449302dc4c584d3a2363d48a295b2279026d7d93d119187232f1c41ad22b585bfd12f52945ffd657ef0064d099369debc2e2ec04a778c23c2aa35f4d090fa8c4c03f822e9471993514005c6598522e29bdc1209a12acf1f5061609919826952db00439c6e80a8627de78e4d246f6efccb6a6b4ea96f9adc85f1a12f2f9047b24109fabe56d378a42d115273e1abed445204028b0574f14ce05953d290c02f0f7019a17872e59e2afb0d573faf74f440b4cb6b95235422dc2cc1527e8dc60cfce826aadbdda1d0cd35430fa9bd8779cfbf178b611baece44484966d76cb273b368d196bf80b2b528443dfe43dafd95aff8f3ba11b69899eb3fdc6f2d02a17da677fe162653f98adf3cab83c697bc168eb4568d7e28bfabc2808802b7f9a21d6f1eac4a2b759241605eff0240266d284724be04d698a44ab040d3438de98b58369e97e0bbb81d25dcb11efbdef418652674e3ef8202a480bc0029d4530b07f015ed323d7a41bd05eae6cffb57aae22be50be19276f8d6476a08935699b82c317f8cef1097213afa95f64560d0c9d63d33e22fb093ee36fbff6febe7145d44d0ba14204183925b22591d2d610073eeda8971623b9e068c4e9d7c1d8ee192dc46ca9bddeed3ad8c444eaf322a75426ecbfb29cebd57f2995798ca6df1c47b5cc6569d8fd02fd796488a1e2da901d174212c642cf6863f4e0c3d5997085b13f49326957a53a2049dcea4dae389a670684763847095df7ec074d92ea5fb773ae4968c8ca3a7e227b24ef2aa67aafb30adb70ceb444b17db8270fab2c87ea73b59112cb9d323389adbf370d6299190f1b8048bf5402051d56fcfc574f61aef9fdc2a47e2e17326d47da52ad5895ddb4a2bda0cb1114d1dc510991c25eb12b01960539b427d869e1bf6fad781627be89cf1337fb6d67b5a625b0bcd4491fd62242265fcfb6921ce83c4f61cc84d0d847764db2838081a5fc51dccb2ed1e362e4856032bb726c08a0de2fbf50244426f385437054160813238e1035ff99f7d2162177b84e51ea24e295638b72e26854ceec61d805841f193bc41f2fe0dbe572d2e63f7801ede0ba71d63b7a566f19eb80b53075e8e218599d1c079ca323ddb9f4ec4e84ae189287fc4818e6191530313ced40c78a80b9bffe6ebb938e5089285cd8fcf83b1a5a158c50a486c217c76621e5d0d92b1a305cc0fd290d39f65880081b7e5df2fa62bea23d5301b543a610061ad13ac9d1bab9b774afd12708a8f6c3cbff878d37dc86b84e4b5541faf9893da9bf4eb6c81408517d80036717c067b6cb0b83680f9f466982fb0eb147ca08e2a39b06ae412751673a8234c019003ad67703e2d498a6b5d2c4b3ae2ec4f993c9619b96ff099047d2877b5a4f141d79f8c0dbdb29a4716f120c4ae4f340d0406c7fa06c3198560fcb39efe0dbe12997f793d1a101b166521b5481d648ad7483f38c580ab75f596c59fa32f60f0990c8c76cb1b562f1ea2246547cecb357074554b9d0a3401a0744b4c20d30e161c8254637730caefa4cda42f9d665940a50f83e7e286e0810ac18633ec8b83bd940f33b33feb6e23b28f9527756b227f57cc0b6da236940bc0dc10c851ebd568a4f52f9d09c58079ca30f6c60897cacd4860d7ba3840a479223c9af788aa8e1adcbe92bacc7e5e4a58e72db37a8e6a2e27002b18a1a83c8555760ffbdbdcef7452f439ebc4d5e657ef175e418a3271f3535f1eca709868d221aca431edece17542653463999933f29b7e5561e58986aa89bc6e7fe037263bcffbfb42ebde2012cff02f40b0052c862bb1fb7b9ea35ef3f2da9c2de8974947fb3bb6942a161623ba0bc4f9f5a8b39dc4a687e87055f37a9398694835c4e2dc202ec1b93ffd4e2de0d1cf8447d5c328bd49a914be21f0e760baff036d4a1e46c3af93556f8683b2cf42b6d146c654afd0e714993c2208a74ae35affe3c2db639f499d82371813451e569728b360b81f921e294b41e09f23b252707322aaf044fbbdd067a8c7dcdc777ea81015bf0ef29c99ccc9022d15419f64298a355f2a237a00eb009ebffdbdabdff66d03c80763b6480bcbd3a67eff7bce9b8f03fd80b514994108a17322be112912f71b3677f8441db195083751b69c27ff17b9450282a93692e62475c40b733412d74b066b56bfa5faf68aad0b937c80e4ecbafb36232954cb17f348620e4eceb62fcb3e46825be169571592ebc71666e02f20239e20928bf0633bd61b251b430e1d7545894720de315ce069b6741bd610dc890a249ae66d7ce09cb605d1db377b3eea3e1cdc02c6fa094ae6366bf782213b3a638e3d7d96c0eb5de9f7328fb9aaab90dab35bf2db5a682980ad337b936651d4fcbd9082fe196a1344e6cefee738604c8dc5562d1102385b5f4fd23cef113c32abf3c9dc1032936e12f8a71287685d8f0ab72fa4bd997df18cc4d6a983746a6aa107a31587463a4467531225aa3c3ff7d28c00a6d9b3e8bda4fd21d5b55d5e4a6c134f31d71cdb4ff76f8649e1daaaaa663240a42c47eae123f3c376dd4e39bc2e35aa88e957090e328567ce902607db8550347eec7e5e5cd17f988e13c2c842c79a1680281357a18ecde987f42ad9004be7819776568c2b79edf5697a31048f5c3840876fe51c6086cb6f14f59578ff2d6c1d3805759fc3a183f32ca488bcc3efdf2b79ad7a62d939d964354252c01a8574a1bb72a315b6045fb213b9f450a2efd2c54b05e6c1768eec6935acba2a80ae9260505bf4c5dc778748450e970737e6224c6b0b1a0d5133da86374baad00b8853a1eeccfc5bc4503c79a6b6940f73ce862e5739d53a355e166deb6281d29ec7e5ba1bef80bc1bff9118ebf0c611d104f522cff532832154d99937727d4c8f177baeb127e50de1d7ee5809ce55279124e981a519a5edad39302231256fe1da3337edee9d8b6d757b550435847effba9c3f3307590c161fb28e1474e4bc8aea46b879b79742abb47aa6c71b2f54c0b6969c70aef28144fc0ec63470e48a5081faaff9549c43bf7ef5d9cbe03c322e390af14f6a00ed2a80f51b906acbf0e395c17ccdab253add447e61a7de02d7a91c7dda4d16f5d7e5b2b251e75bb4e4f43aa7f44fb3fbb5ceeff15b9c402e6ac9a0a0d67d0c8dea4a460c92f5fe19779ad6719206e4bcac70be61d6ac9b0dbd2b61fe700215992c2ea3569dc49c62e5c973f030fb6026fc598c239c34cb75fc373325a05f7b7697657d3fdee64d77007dd4e3941ff72c909ff09b30d00aa851d31ea158abf1901b5d20a909bf0f4a3b3f7c1e0bc02e5eabef132f00fae910c3a65376e6cbf50da67d7918b274f50a4a55b2066fc59cfa4e741c9974a1619d741b7258c5be7c802917a7f59d80dbbf4acf7bb657c56f9634234740768fe71384b855103d44115d8cca5d347761eebd73767937bc5cb70b353656eeb1c5efbc9f68d364ed3ca6fb00c5df7901192ea2a8081dc9a3e1479ac4f332cfd5209d3bf770d9faac3f00331e54ba1c046abb9a4b522dfde3857bd2ababdc0e23122616a188bdcda5d8945ea2bd611972810babacedcaf1c1b0cc318a53a38f6e38343e3b4ab4355f69924a421087c038a471461017d55b3bcff56f8d3b9188e5b3218757522038064bd78f9b743278225ead0d73851868808e183110636ae8173d5957a1210f43227325f204b608cd2489fac026819e2bb8ec6b9684810f7739375a717742baed5bb7e6109ff20b59548b6ed72053e830b1dac6e536754fe536accd2958f9d2691ff095a681a96c85cb9cee7f16a7a79901ddc0555edd1b4167caf4ae83b7c191b85f81cbbca8b944c91078f75d46620420894f058691288d8bb1248608fe936ac661298dcae5ff885981376a1d9568001c54c02f3e02b9f4d092551451e2d50443b91d9d2e20a2bd0ece2e50fe3d8c56ae46ca0ca73c30a05f8c4fb676f8b4b5dfa266ecadc2ac39de92c278d6da70e3cc74c00a8a1b7a7c2a7e17b5b9cfd65be3021c2eeeedd9906322f7c50eea899fdcc9ac4ff0a0b7ed80839df862f2d5e48d334fde05b3300153ea3e7e8513d4ff935c410058ad4efb755301e15a539283800ca2d96c7032eaefa788306738d38dc01231f8da961e2cf73f9162b9e1d7c58f5765f43772707a0000043816dc33d40bf7c38740a4770cce2b108033e6a08f691b5d6ecee26a47523dd4448c80e3c0b690baf0b91c76990d480398e9892fae402bbd9dc9b9d6e096a2df3346df6ea0a336186f06e33544e97f08a48d594a5a82473674db030567813595cba21a968bee909ead45a2d34ead4ddf360a74beb2ef7ab15a2a3fc73b76747a2ebf2075eae78f80ccd225ca2f886cca2b364c7aea8b5eca16b5bcb3e121dbbbd31a35f93052d0b5b16de66288f7619ddd1515050b0c70a83e1d268f4110a0ab646a3d75aefd5334219a1a45c976bca2deb29f5a25bb75e641de5ca0a539d759165a558960e2aa7bc46d71497eb51ae75d14df990666b59663fb7777448b3232caa529465fd7ab6ca6ad549fd82ff7c742afb8cfea98101a1a0a03cc665145c4a790acaeb51529ebd26df8d268b9e7251acd10ddd55d1e8d66451e873eb039ad19efacc8c1efbf5eaaf47b11a6b72655116454daa9a9f7735282cf3e80fa6288202ad4dd3e51c7d523402a837758a82b0a9f71af38e3484198f9dd1c9374db03261e1df15f00c0f44be1bc8bb8f89fb6e5a9b0e6b606bf2f407e4e6316115d02e323205235377ad4da71a42f862743cf955ea9aa64f30b66744d34e323241bc3d20cf486b33b09b5b92a7ed1a689718c7811987e4081ab7448abc3d1f3972c09767a48d5435d81cede21e307d0822da057efae10bf5a2702dd3b390a75b5dc8d324863c3d23cec574236d83e5f4157dfe12fd82b523cd6eadfd0c2997b2ec99b515751565bdbf0ce7c4de93ce3b51fbf7de6748393b84c970c9dadb77bccdf429cdae9ed62895dd4d890c29c6de9762a7b78fd10ad35508e72847412ccbde56b531b3fd955d99ecf6f843655685b1d7d6a139b3ec9f6b5db631a6823e51d151a0488a44a3d1689472d005bad7536ef56b9a52deed43d69e68dbeb4a89b50e5374bd47ba7cf846965db8f4ac3febf6d63fd5455dbd22bb89b28cf463b3ebeadbbfdb0f3a6b6b0dfddd7e765d46b91dbdeaee8001e12b0557a7ad499437b29f8f28cb3e19d6418942ef59c8da8c62cf2efcbb941e3b4cb52acc8e7afa18534f0f7ab706a4349022ecd6fbd6a4ac73c84e298515a6de5afb18972d2e7d2e633fffb19fb7789e7a4d7e34f9fd73edcd5e766bf2c3e89bbe06ac146347625161511d269e22d15331429cd48a32cd58acae75b9d64a293d46dfebbf6ae3c2f1954515b1f3d6658cd75e7aa713ec2692138c844c2ec7b2419c1cdd74e7ac3bb05b0b09d25a04d21a8e943dadc4f7e3f9a0e883ddd80605bb3510d1d6f3a21b5f77b0759006f204bbbd1e39fef5c079345e8e4082d81a4350a0887e6920dd12dfc3c439c1c9d1451777a0c82992412fa263f5f8670237e3f368d8cf1ecb84e4e4c91044f48be8d6a456a4f15cc60623c7defe5003c1b978b7b783a0d52bbaef567585e457e43d1c9b2704a7b5778863bb1232ed3b6cf897f1755def5a5faf0b57f4aee964a57dd6c1679f21e112c4376412ad658fad658fd4390982efc8ce0f5385424c9305c53abdc6d05c33a43c65ee374ee2571dc2e50997fa3254c5605f49f2f5685f6a840e4660bd32f5d7f15403d6f90be6c2e9b3d680b526bf6a83fe7a99a597dd0113c2f378ca2e4ca25e5807b52991af77bd46bf6b8af7e2c1f75e55981dd65f8da962acbf3b5bb3750e3942729c5558f8be7d3606d78b4f2256d160e10f1f0f03c88b0cdd31e65c9861c3328c7119860dac6eb09b2847117c4309a30c9a58117c1526be4884cbb16445ecfcf478583f13a56ebd83953f827a9225455190aa76b0d75485c0dc67aa307dec73ac2b4c76501fd47dfa9a4cdd8d5677a3c9d541d7ded5aab2d829ec53de620f4876a7d7052fcbbab37e3a170198e55b932cb03731e1aa6e205d8e252b62e32721b7264ff70d29922fd8350230cf1eeb943817ee3c8850122289bb4c6118130c87ef60dbe0ba9e7d596d62fb0ee2588525cc905d06a6cd2f9ccbf584a3035f1ed4eca765affd7c563ebf7e414bed21b5ac5f0e73622f77b9cc097b511a2f897d6c6b552412954c91e3afca056b6fd55c57ccf54fa4ae1895e05e607c428e318a1c63167274f6be9cd6e29027a485401f393e2142b0f454d770335c7eae45e5aee269956ff148ab6c335b3bed121f8f00f1f84824dac7c789c42a69202211aaeeb071b3c55a6b2a67f9e5ee27c3425cfffc837f5942d4640bb3b4a6f20bd2d754e556456d1122598ead3e5f55c1d5e1595cfcd8d6e8e1fbe567032729a824ed42b168d323c3eba27c957c780a5e261cedb2d73fbfaeebbd0af3c39fcda3c1f2f877828702e702c48bf66de6919d31ba732f946bb93e3a10173aed3acbcdeef596ebda75eb7a9ffbd9f9bcfab99ebdba5b75966399005a040004109fb3e0ede5cc018144ccd67564d902405b78fbfcbacce7982500bfa824949294005c1601dcaea2b5f896db4fba25c6d6ae5b77b6763d8976f9e07896fbf27d399f27e409794222103bfda2f1481193f470233922c9315e36128944e24e869188f9ba8e2c5f1800df261c1cf71746e2e5cf5d15e2737c94ecf1934ebb5c44261cf8127360122ccf6402709a9781380b7e2cb05bcc09c005e2c62119dd692d52229148d684ed611a926366f50ecf74a45d7a884472911c8de4f8cf1f0b2c96f91c5f26eab41687b416e3272191e54c6e37e99698934945dece2392131f00bccd9859f013e2c35abf229176617954297de5d7455ddfccc982279ce9c6dee4f8ea026bfdfa0f807b446bf1f8d2bc7c318d739f0967c2899f62bccd2c4899c59d63f11b776291d6228c0907cef9c136ee071c89b4163f74c2b1b926215c7f38e0af479c7611d22e39d72b245eeee1b2b89b8da5096713a910c9f1f309d91ecf46512e4e8e1fdd1d940d14e1086ad131ba54a7e8cb842d0af87c8b18d7af0c5b18bb6fed59b13579a96d2c93912baac8d48dad4d1b96d650e0a4c84162901ca1d041915c1f8b7ac8a456204d60ec866ba83506f4e924c844e4609376893d5d526bef089882854e4299fa73ce27dddd5341277305aee1d1b83ea71e306c92e3ababe3f5194916250213c335e0a9647d5a1f9efd703ad24473aa2ad864d21ad3c3fb8af3476c33abdcf58bca0f3e6016789b2ce6644d558da987c9a2846966670c725997059bc7343dc99b8c95fc3c64f268c44fe864ea6fb0095c831561932c4fd3b9a75b74b2b04cbffaf46d92734eb08a97852393f7427ee5569755ee76f91a9384b799e1cda6e0cd52bc91483fdeb4bc391eed537daaf8b952365972e16d86462c43fd37a8c7cf0797e8b3971ffe08f8726c713200befddf48f79bc5df2600f0c3399ec70eb6f465752c8b555563e265aa8963218c35c3f0d64b72f58d54e19e7671b99eaa3095a29735ab53971445ab1f7169e64d8207847f3eb65e92fb4ee4e01a1e8d172fe7af18eff61c8df83ca192a76864ae305cc37bd124bfdad859d824cb7bc4a549a2ca23e6ac0f03143a39b97f0821bc8b9fec10d0410a37318fc43cf7c42cddd28d6d6f2d92c1ba23393e3fe00d3236775360e1db490e133efac55f523e476bf2d9eeee86f9cdb426e324e539d09a8c87f28f89769193a65d625c123ca47836f2af87bcc9f20246b4cb74b784082d4b2988d0a6065f4020a5fcd4308d7321cfc4f6a73f1cfbb13b89d6a47455e050489826225a9b86680d1e6658446b9313439ece90a736e4690e79aa806b9928e05c4cd3347d8a71cf47bbfc68971275973767e4ddf48b0b627a383656e424784c0f686dfa7b9ea6074cdf26357ff8f252e05aa6bf204fd3abe9e92093f2c4af877331bd46e7cac44e772c1396793a3bd3c59a939cd6c4ac5b13e7684deb73ce6759b7e6acf4f5dd0d2184f0125eca48e905a95593ada9bec6d6aceb8ea2284a29c5a57a197af84a0f4f699d77a38f265baf975a35d9ba333457b872e2bd983ebd06a4823c20930b985022e525a5942143b66cd92d2fa594527e93e15cc85734d8386118adc9cb333816097bf06c5a682cad5dc6682dde252047072383c2c2f706c325ca1ba1011a1eefd12571f2e42e6e5a96d82fd260064fc6705530b02e01ae255e0ad60a32cc2003b6c4cf69032f2f0848d339944367d1a81a4de3aad0022bae09fafc6a41936c2360ff91992500ce25c4e266c48a83a55d76f4ab37581e8d1d2fc35f37dc1557e4104cc9c61322745d86feb9a50b7ff2e730217cbdcfe72e40af42ca6ba29f8b44e7cf8e0f086471c4e734d067b521b3cd1ff8c1ceb90061225acb01f3664f446b395e76a2432bba33c64979d99b04c88acf37160bba4d880eba4df810dd1e44b00745eb4ce4c04841b704fab3bc2590bd25107e19ba310cd12ef12c125f7bd89a2d855e0a81b0fb80fe8864ea33346b180b2c4bbb487ad85d61e4596a8b0c371819c6c8508602dae573583faf1808047a8ccb20d0e5432050c8397b379a6c1fba5b9cd96577d5dacc6131eab42c07a9d76891731445e1e93e3d92038c31c6e85a0a90dd1390dd6f3bea5c220b8b6fa49eb1b1c03732308cec360094be69b984fb06b6c0eb68347e4bde0b7807380dfe097d1ff684224ee89e68dfc09718d7b2e4d1a8f15ec0c331e47c2143b8850c83702ee0b787860c2fba5e8d79a010aeaf70fb80797a8d7e4bdc8ca9c2cd04f684b56a74bccd846b42e8ef3ce771df3817f0d88e753d31cebbe381ee16e55db2e3dd9bad5f85b98e729f92d660c49860f3d5d25db65eb7b770f416a9804e7acaca53a66b05c80d7c1906b9511d91a94841a02677fc404d9e18043fe4c6a5d065e6fb21dc476031f488371d362351cad5e32471854bd565e6a957f8f3044b51b72e2b11a0cfcb3ac4cba037feecd8aafb56a00a7f766cbb8a03f4c632a04f2cb3f29e304dcc202c576e5cef77b5b182636b2af83525a58aee6a04502e42b99b125924ea2b92940845249a28d14503b3088fbaf61de14d4a29e53f2dfbf3c69800fac3251b8f32d5ed757bddda5fb7f6953c22c7d07c69961e4ba0c7bbd144eabab578ab3e852050334d38f479458f979a9789a73e45320bc947f998411a0814c232d5af982b6c6fbd06fc5c9b1bb35e55985a61a88d51bef0eb99c209f6c11bd133f01c6817246e7b498e641863e7fd7051c48049863f026eef05193e1a64f87490e1034210dbb349b24d19315e7c78e9db64d1830fb807ec1c0b8c6338172d2486c1a2350fa90b2084f82c74b86d0f224c7c81d68c6804bcbb53b70825b8ec43c80b42245e0abc209de30d96c2afc7b399701235868824d8cd297941a02817015f68b7bcbf39d94822fd9a0603c62683881ced22ff9e2d42766c5cba91212ed99864a60c2dbc11adbd778c480d58a38dc09260379adfdfa988058c18fd721b1b1b13bcb820de1fe0728582c718236d1779986486c1b5b8bc65314b48e15a5c8e33057620378c6f789a92fbed0326983c813c3992727c4e9c3811a227dd0133e7dd73025f5a8912252eb712184f224d27b0257e4e6b1f90f79c3879cfc9bb4d58fc92545745df7db9959048d6d626f6aa6060dfb77ed2b1b1ed21e99c26d2393da49fb819ce52053ce9da7f77c7cb0ab89b02727c02dc4b3f712df130ed62042730d4a091e1602071c15a21c7c6837b79405ac5c11882f683e7803403eb821cef1c0a72bc39b6e458801cdfad247e2ac95192d3039d1d6c054e9e7492ee1cf8d24a604bfc9cdd52b486a4b524f005de00f64008f00839beb1927eb2023b5b8a7641823d292ff96622ba3ae4f8b83d24d97bf2acb0e91e3f1aa75d90cc89a4ed90e313e722ba274a9e4bc519f274e60ecf3c529fdd18f7ee249e447242822ffd04b6c4cf49ca5105762359e08a20ec7c32a7b5fd24471bdbc40bd22d7d3bbb8e7f4edc93e7a45daa784f7a10611ebbb0ee076b764f727c08dc0c7898aaaaafa60a736118f61897315cca322c7b77a37d379a48eb0ccdf4dd20756216ad917a6c633bc5e929b09047050a9763d9a2931c7f479de5941c812fb37b5a498edf1ac99cd69248bfa69162bcf56a249d9a854192711eed480f2490894e28a5a47207595cd35a07be40267dc3026f3056f0e690a84026ee48096f27e1cdf188f026a3f1666184b0860449139448a1271e43428e100cc9448204097c8137178e427286c01bf8d247702df14d90428e9fb8ad8004bb451c1c2131a75d5caec9fd58fb51a00826178827426b012346bb5c97383efe01434caf89136845b4893ee24d046259f0889b01a7b8247d2997ef1969971a7fd17649c292d69d8dadbd8f0a62ebb7175341dda38f60b777044b41fc3be2c337183d7cd3587cfbca3792cafb06860f78837116783bca57f046b2f6c6b9171862c28454dada49dfc0978863e1bec13738382297a5a8304d91589c1c577034128fb416239258c2dbc422124cc76e90498e8f3cd148bb7c3e97d5b5f6e9c70dde30d12e5bbf21066917264da01358057c02ad8836edd2c347bba83cae3c1e1e8950de51df3817958975ad91b053d237edc2c421907e7941c44321ede2260a0a4adb1f8e427ae9c6b86c7de56e2e499657b925eb2ebb4a52417cbfc1cd80f3ce3823e52925bd46537a0c234d6c52f914cbb2e4279596b4e4ad8650cb923727bf8afd70ef4d8e5bdff411727ce86e04c8f1a0ecc6b8ea95d622cf888ff7a35f5e90ee27a4df10b93d2297f53e850cc9efe5e9a293520300be702df073bec8f3050a6c3f56a7f4b4aaaaf9c3eba4a3dcde2d744b22552b071dfb0ccda4bb15d2491fbd3ae9d2d6441f5d516ba263d7b626a24f39e9a11b4349f791cea35d42a1a3a05014145c0aa11c855294cf136c0ade62261d9230fde79392f22c0b65d953525294c82495eb7246e95d256d2fa3644739bdd95148b72b4c86725dce32151229cb48580795555450b2932e3dca55111d74379b45b335518d165df6860eba29bf9c0b9bfd825474eb1d6cf52368955515cd6446b3ed82af525e7db2904824229148245c52b90ce9d95548cf4ec2d93f28285721a5a0d4649451e8346455d92bd02b0b64fd931dbbb5d8d92cb31976555ad1aab2aacbb22cab3a32b10cbbdbf50abb76d43f6d1569b522f44d74b7d1af50689ef4cf513e437348e6d0434f117d7e3e74532eca7d445ab32eba9f1d5b8d6205ab0a8b6696556f5455b17e9e6045788b39f47ea8ba48740c1b61d84522257208ab42a00c7bf6ea62cfaa57d5b31086bdaa4055750c0b814019f6d0ad9e5decd7659045245bd67a425ab36e8dab6af537612afca6f5695996855d5f8fc28c657ede8cc3e558b6d6c9d6acf2aa3bb097ec4bba819e4e538c415ab34af03519ced75bba6a13bb13a735a435ebd7906cbd5eeaf509a1d6651502070ad6b26e84d5356065b17636966cbddf4e6bd6b327d8b61eff47c365eba46be5b363afd3d7ebbaf02d2cd33ad41ca018b224bba4946f092bb7d89303ce3d897112d24d4fbe4b52a69eb0ae344def494e3209f844566199cbeef9a0f1730af16bc571e58ad698eb3e5af07e5c158652bcc58adeb22c0bc8ab300c575007c43218cd0e691ab32c8cbdfa36436730c5db83b8145f4f55a778b3519d56aff4d0faec58aa66a75756ab2cab31d565605555b3081629a5d566336c1e10422079a32a5aff7c3c1ad52ba520bd85e1adaaa730fc1c9e1ea3f4d1a214fe5cfbd7026a5916f63e27f6e86b342ebdd753f414f66de6d3d432d118ec32134fd85f0b1e0d8c7eaa7772cacf6ac1a341a98cf5798a236059d6e98df9bd46bb4ce9cbf4fd2ca55e6badb8845da61e1eab87af15a3f496455fef3633ad2e4c4dac44d7600d6202e4aa2986e42b83772c1bb499d3af79c55f100a79093d571f27ce540d691707e3559dbaf5f66ef052788aa22c0b3b8b65acca8947e37367a10541d590ecfed9669cbb054fb9433c1d2ecdbf57ee106f36dca1fb8314ac5e0ce8b0821ea5f055e50ea235c65d266247e3b10b7278fb4065559829b225270b4248c1e6b1c5b893b7ca59d5dfab9c47c3fd553a79aba28565e02b550dc9d57b45b96a48868fa71eb1938195a856105687af6a4c75d84537747babd78a27ce0aa5d6b36cde399cb938b18cbcf5181fad2cc3a5f9eab072e2d188db56bf4d21f9baacf0f662c541b3156fec96ea18ae9513f114febaa829cfca8947a32757df665c8f5555d6145239f1f818ef2e1c925f5c574e4c9c6a488eeed066f4858b88e770337238178e243a0b8d05868c18b45fec8b0143d40083e599babbbbbb45d30cb028f46b75fdfa75adb55e53f4b5a44ef56e36c3e6d1b5dbc2baabbba680b5bbc7c32816218635a5e2a2e2a2e2a24205cec5ab87a2288aaae151b9ead714d42bca65aaa2ee1ca41ca41ca4204d3f8a7a5c5255d50427dd328cb2be426158866118cea87feee7a087401f9b6114de2aa6179e71564c223aab88efc526cd24537149bc7aacb556c66b89bd7aae9e8b8acd66d83ce214b18975d714dbb5245e3dcfc7f3d13d9e8f8c464a2f2a2e2a2e2a2e2a2e2a94e0c4aba786c7d573cd39a95bf5d55fd36b05a51803bdede7a0982d86dd0af66d5aaf28ae2ecb077d1715f4a24244f9a056e623c3dbcb9a64f065a73d30363555d6adeb9d655966615956ab759aced995756d8b9a248d30a3583746b3faabb1a66d611727dea8293b96a67c40a4e77af7749a3ec2ce116e32112459cbae0a44728800c906c8ae0a4958b235de6a782891fbeda304b94fb58c73d23aa0ac42d44417b18b0e6fd4214de7beba1fe1a3fa1445515fa19e619fded557ae77d3ebfa65af7a9d5ed7754aebe9f5f9157a5fdda2675776d1ed874019de303c43cad5a7ae783a0bcff469194434eb6aa7481e40765558c24d5e824d7e4b645705224e72f6d92eb4fa057a14086f30bf534eba49a62413bcc54b2b46494df32b9f1dabaad42fd83c4020d0f63ecf9ef63c1aef96b22050c58181e25fa4b6fe91ab3ff979763f786b2b9f04e1f72c9e8fd7e5b11bf38537ab9a6819de0bd184317d365415145d52b50fda53512774095de2ea3120b085fa9c177c7936967aebf436d35476ca826c3e55f5cfb577e2ada5a2784e0c5ba7b8baf00c2953d6a798ad46c6c2f11dbecb2942ac539ba2020f131445614b61179da02e3904bb75cea4726475bd2769cf7b41d132bcf69133442767888e1072789c88140f2dc37b4151269410727488ec14e1d1c9b1ef491d223b4578681972748ae40c2142cb407b64fbe81c0ba949517052148996c1492fd13f7a891b9a6e1b1b1b1b9afe820feb615286d7a4457817f587adc808766b1c8b0db132f3f2a22afa4b3ebcd55077f5c2cfdd8879e21a50e28723e4e5e03c21392e4aac082e47792dc9ef1811ecf4f77672b021f6e5ad71a81ed22e950e91deb9a8782f2a8caba023c50687348e8e1121432e2ade0b0bcb2c7d58dd0347b87d00e6381d2a2e237688028f123664110b27efd909c3b1eff2a57e5e4614f17e8157fdfaabd661a4f8b363fb16f15e6f8e10d23927f08924499abe279a3922439a24328f2c7bc70ed15a536a45d4ab7913f5a6fe82dd34175b64adb57e1aedf9523f2309bee4a852d81cf0a545b7342419e10373c0882fd8f7cdf5bc0a06ee4108217c2cb17483e6e507bd60e4ae75e0cbc48d036395c2e2c0174804eac03fa8f374f22bc1ebe4b4f6b6d6b142e7e626e6eddde4be7c7fbd03a4b160df8f24fc98c2f55d9cc0e5fac5f18cac404213f7e101a2136d6484bfaaeabaae0ba316adb5caa12accac168414428b52eb75be5e7a552f48654d872209413a26524f48ee27cf467aa125ece9b138cb05dc5c629b64f77e025f5a098494d6dd556544df886ee9deb125ea35998240de33a20776eb27aee791723f812fcf8a49b5154facc8f01335f5d48d674803e882135de820adc18ef9bd572bd8f728eabd2bd8ed5981f5c06ecf8a67d3c3c78febd05d4082d0c36338767b4494e46eaadbf5f4cd99de7bdd4278606d70b01dd8cdf548966c047c794aba057e4e253d4f8990ce796259cb7a7ed22dd0ea7a83c50e1cccf0a43518b37b8da14dc5c20eecf6943c794f7a8a1ed9c4c9ab02d381853e5a833ab0d437e823430ac31eadf59327b06dda481f7940aa123bbd22c24ee68011ede2a020824e86d74013ca481268d39ae37947fa496b48dac5c26d4589823d7e64f75a090ee4c199388df3492423203b28867045bede63a25be0df1092339237d7f382e0c017f821dd026f84542443088964a8644e6b49a45fc341817db90fff248eb7466b0f5e4f65054ea2c82e0a1ae4649c5e22bb286ab084edd7c0e6471fe9914276503811243bf1233b2884d0236fafc7eb23b92722da25879bf172381773561405ef2af5e82084d510274f5df878a5ecbf9de7c4cda8ceb9a85ab05b8bfc1e0b0b52d4220b29a2e2145d13165108a16dedf5c06860e3bb2d28c0085f8c31eec83723cafd4e723b8916a4c84e8a9af71e912ae2ab2994ca2929e10c29671546ba7ed1ae5dd803618dc620863d105ea5a67c967e89e20f8f18468cd59800bc87c71e8ec1b370994509922e80fbc3479774202ef5778657507ea0c8110a8acafb874a33215146584665450281a2822280a3ac0800050505058fe2e5caa993604fc8874849141494930270b7970370acc6c8c32adf4b904e915070f4b40fbd0445b278545656aecb2a1005368f28650751219d8a2495d123894445126974791200be8958a48d8b004687273daaac8c4838f68ff762f4c381b8a407e006e0baf6216de0cbcb61c1db740100fee12f07bef43d7e39199694a4507a604bfbf0dbf0387e74d2ca4717fd87eb5630e913ecf1a1a9d1439824125d5618118a8aca48e521958f42afd12a2a8f376614ec315a51ac3128107ee5d25518d048253e95214824124945e59242b8976071d1ed01976e29e52eb3d052282d27774f0ecae9925088e79289bc499b1e9dfe216de08b464d40b4ebba45d29ef6f18496a10a2b74604fb7f46113497a94d0322411d132944270d444cf68898a88c54baf1ea9e62caecba5bbc11c3a554b2b2b948ca5f895a3dcb50b85b71a94eb5a5b5959e95cfaab2938f60f958f46a38b6e0abcca45418937e592eeaa04b2ec54a67215780ac38356aecae82ab726935246387e2eb5a2d62296d178f5f454c8a4070b82dd62cf466a6923ea89935a1189c55974806802e42ca902a65213f4d181602f1331076648a04bd33022d2b70991c0968e33a41c0ae152c41009ec51291eba7c548ad83dc99d8f60936f7655d099224995c2c2b780b77a43dc23ccc708ce858e8fbc45263e20e92d9dc096ebba85e6889948ea304e11238e4aa274025f20926ee9374462041eb9a8780780ef43239802fd033539c2b829916ba010bbb511accb2223b819eea17c7a2929a1cf632dc5d6a0c8ee90717437ba144ea2350893a830150ff61049c324ed92894446c0c9af0bd941418423a2db1ad02f38fa28c66c14aac9d2da0fb64d901e49d8750be65ec54a5a6b88a4b57e0bec366b400a91e8e47ed542ba0c212ec153a2ec2240446beea51bd205a56c503af4031b2c06d6e52399ab306e5ad590693aaa929573d49c1475a58e375f5d98f92952310ec7ed05c918bb1112b770f51dcb4abdc35bc2e435794feadf95e44891a1180293eca070c24996911d1443b8c9580cec73cff268ed552bd88d25bf37456b52496bd0b22c8aa228eaf218fe3cc156afc9f0560ac6681d5ad6750fbbb1afca1992e32960050b317e3f5e8f1ed7ab3142dee1151e9049318905bccb0ebbb12f6f4f48766f1b1c5cd812d6ba1ccb66f38b7d7515a650457651c4c0c93c0ef71aed6e138e32e2233fbb2a40c1245f6e034a704cb9659df45959b241fdd43451b889795795a04110213ede88f1700b4dc8c1f5a37be4f7ee91fb477efd6e9c36c3ea73c23764a6103e08a173b1a57cbb4f9374ceb96a88bccec907646acc0516be549287f23247899f4e7e3bf911c1ae7a131516d21bea3df9caa24d9cf2d3d590fb541c97bbad31d6e523743d598571f23a68759c60f348a26a7255d514940c45c5a3815d56f6eac9f272aae4aba9aa20bea8a066686982f00e07fc74b972746635c6de6176ba5308fad41a032f23b13d85202a5fef8742774b2f7a434d54059b07944df24621e5e6af291e0df8792dc91b9532ae82749e56f0eac95462f96d46ba5b971595d2cd8a52d3bda6b06a8cf5ea140bfdbaf3d68d543c1a19cea4941496192e4d9f8712fb5463a8cb50bc3d893dfa9829796bceea8ae2346d22a0d436e3f0fcdb66689e24768717158fc683f6eac91d418d6283a95f445ab7c0d03f87d7e7b6b81a4be82f84a3cd30167489007d4207dd213eb7c7f1c132f6203c44a8055364fb21e00b8b85436c76e5fa16b20f7d63c958e6737b99d059b6cf4136742f96ebceb984be5dd765aeb3b468976babf5d5eb647489dd5bd3a8df42a77e856e0bad7328d4b988bfee8437ba8fbdd9dd483f648a77bc6c5d516bd3b15b61d25b9b86c0e06037118f7621a2b5e9d36b0d6c093e962065d12e4cf2f4e95b0f306254f79203b64c8fd5cd6819d34976b00efbabf98d89873866226e2df5696396d6a6bfb5e939e0cbe8d33637bb91f2483467083b07c27152408c18ad6d77b351423961252532c45413c1d864e4294f9513358b08c039e79c30c73945ea238ae24792bc3924eed373b4cbe4261cdad4063e72b65d8d4d61bbfb38dc13c37578b1c0be3bfac9aecac04e7fcf7c58f87ef512c437b40949d1b57cbc3437e3cdd0f19ccc6f2d993a4bbbc01901f8ebede2583437c365eac2a5f989b579245e9eb8458e38b1c39ad8e9d2f1c424fc309263c8287e148959be450a8e6479185246967fcfbd59a0fc3abf3f37e315211fb173129d0b79cc88955b8b2c61b4cb365bb8199de57350c0cd7059de3917efefc11d543da7b524127452b6689716adb59bb1a3aac91bcc854b0f5fa7feaeebee86a7aa5f4feb928e2c57b82b8ef80a23f15e2cddc8342f470d82e1da6236bfd160191cdfa38c4ccb404cbf84ad91efc98045bbd044c7138f5bd218fa7a45081f9d5d2feaca50979092126e9de12155035235f9f4be2caf6b629a78a301f2314bab8a91d4a5ac282931455115e5f2342739639ca80af3a6384d719a9efc7b943e0b0b4d3edda4489ef37a3ab1c82302faf676b27e02d3e99d22d159d87dffbc21de66f398a6c9e9f48e9baa271e8d2c7ebaeb221ffc72aca6a99f9895c3a50a06f5e2346dd67b9d3e8758063a8be1adf7aca6b0557593594dd83c5cef3c1aee48deac47e7ace6b1e6d445acf713d6d445328c2e5a2b0b4dd6acacf7ce7b013f6b4cfd74eb3afad0051dbbd8d3b1f0c57dba593f2dabba552dabc2fdc47b31ad12f563f71101ddd82d146f154f189026f613b89f783ad74599a91c310d4dfcf41927dc4fc8081bbe7ee2d1785d24c74baded141f543002051446a0b0c211f8ecfa95c934a18443120411268415bc131edd0ac68fee7439d0da055a7346b4e6269b0c8f55c1968498d93917304f8ec745b1800c1190e15b6c2e327cca45b9a38b8b90c2ce4f70073e2b7e6686c21b896427dcc145b4467d2696402413adc1d8ec748bcb30363cadb9bccd9b03e2e89eadd00974d20f420c9bc81bf8128964b7b204d78b76477cf579695e9e87b7baf3b1c8255f55d72b998ac967c571f315fb5df4ed875ca554947b893c87ce391a71c97bf15ee1e74ff0b9884526cec57b6c1297d05b21bc597a505c226f221379e36654cf0a6ec64dbb94ae6387a5eb17864b5aab1c54a12a98ab0c67fde76e3f640aef78994626d3e55f13a36ff29212474e55e188456121907e21b919d553363b827803b888d6261c5b4b79030e4a1d46f88a31dc00992e236524229f8358f51a2d1f9b40b137ee4cb188c571c7c783181521d2029b7d8b44a6f72c7b25188510c9ef5148bb6c32d6871ce1e198266ba220c401d25a1089238574cb7b04726157b070a789d16b349ca27e84e1ceab31b08ab8348fe1c93de2b40bc470a7b5d704b6c1bdf91fae9237d8e49337d8a477aabcc126d31685e4f72e856058b0f02ebb198e67abda9b75be496ffb8a541b2d192ea95c8052486bd2487e6f69a43189e35cbc4fc1ca4ca7794d601327ed2233ab804fece06654d791cdea167c055f55f0154f3482c4cb138e475a7bc7ae60b79879f2fb7be4d91161916864497e7f3cd22e70471a9147240e7c7972702def0f0f1305d605a49e41c31d4dde48262490695f86b19feb25ad35c1b5bc57f363a10ae987fc98f48b0bc2e5656c0a76833b15fc7b2e4aac6443c7bb226f3c72f5d8daeb1de7e25d73c69c11afc8dbab262c53b2319fe1351652b09345d62a08712ccd50190979012338b0f2d1b1ef8adcf6c3d21afdd63b70a75d4ad6b7e7a348bb942c7c43cbd6adf7a3351e280537e329c1b978395abb4e2febcf75efc097d93f8934a7b5538abcf58ea30177de8b778a7252e4ea1d25476ec915e65113c408268e2e0a46e75113c468c76521577f5bc8d52fbc53e1ad46276f330e49964102e6264630bacb30c7088f5edd098f2ebc1b096cf2347a4ac8efa38b821a1b1e9c1b1f3d4e60029b20525013447432ba5fe0021289a85dd8e0722cd79b60e4e90dc341479d8b493b9ddee202cec5f4225a9b8e69c16e742a69988124797a91015b8e01478302efc5f4e946b819d4a769b3970c0a4cd3e4bc90a738797a08c98ea7071352d0c9ae0a52c099ba4e3a6fd4c1236f57965b123636362e18c265e966c07793821839c68811a35b628b69df512c0001f9ddb9166ec6cbef1c8014ee5f904619b20837c1fc700ce7223a0d3817f14664c16e3136c7e488191920727c7c07e204710cad058c182cf13da880fcde3720f2d4c2b978bf99d240bd5c71fab3d675e3297c443422f691f7de9b3c36fe92af91a7f6f66e4ed6bd300cfb0cc9ba93c68a61ef599665d9f969a7697af3ba288df1650fc7cc937cad75e78a5d94dea2a777b9ab10f0f47f39ce2ccbb21a4df3f8928dcd03a4fc6846e91d764a2bb5515cc22c4a3f27a5749ad464599ff7dd9a71fabb734ed5ed8cdd193d40ca32140b31d5e479a9e9d69c60ad26ed7ed3a52cad691683c2e5582cabd4afc98d3b93aeada63a04e8979bc0bbf591ed3f8e2763788b19fbf5b9f6d7b316fc4a16ebaf7a99ab9e946bad78abdbeb9df59547bbbcd67a4ad5acfeaa9802568629c16e3322222870e3ddbeab8d7a8bb7cfeb86c92ccbb28c443a096f312eeb785764b73b678f93841d8997b15f15b5b7a93bfa75d1eb8e9eddce16b537e5d98599745dbe2ab542c07c597becf6ddb2dbdba75c18fb586360ce5e6b8cb331129f6b2cc3251b9db177c6aeb922fafc7c11ed321f7fd51847c37ec3322cb323c6e5f9685b288a24c9623ac532b5aa8505e38a416554056039c6e51aadc5d36439c6e507b48649271dc474bc2ba6e93d59281695dd2793e95042de3aca2dd9e863df917d749d7361fde5becfcff6fe80e53e86cd795d9b9d734ea403203959be4611e48973617f6f13055945d8d917015b282abad543f765d07d3cd9ded7670003f2ab1576b6880b6778be4653cccee40cbb9b28eb7857e4ebb2e28076c364b0c9de78d8070b415f8388f31a0c1939f264eb6e7deb1d45102619c35675ec83d55fd8bc349dab67dfac3dacb2efa6e3e57a245ebe2e5fade918765863b23bddba9d2376e55f1502e6ecee78ce05769a98afbb2991eb658ccb18266b8cc3f375ceeea68409f2b3a9c24d4f77aeaf62b5a7deafe8a28398bdbd3ade15996e4a64ebd67d0fab578712d9dedd1dd9eb2fe7026b800fd9de071fb2bb2267b7648301d99e01d9deba0cc8f3cf3afad8314cca9f7f0e2bcca7bacf16c330ecd867bee2b879629f58bfeb8d7efd85eb2dd9a8c75eedaf98ec98b3614fbfcd88eaa5ceba1e10e56d264ff87df44a4c1c8e9dfcf2cb979b4015850e8ee42965e02b05b4f62076b1b2b4cb83d346a06a31d148c4be36c3cd7befbdf7a494f2bd185f4fcf63b284d743e486404b8031157142778a6b81f6c5130164f81f98cc003917f0db5b42869fde0b853ec7d843b7c270083f25a14704c36f098c6989fb3cc6183350cc0e0ae1d7833df40d46c66e087477bc98bd9ea784cac9a13e512d1dce90a733293d6643efbf7723f598bc26a1f77644ce5ef6407f0f813efd4f1f0402bd6ccbe2e70f0bbd71c7528621509331d033d0b383b27efcdc2dfb640fb26f1c3f31cc868e85dea7b50765211b27aa7aaf5eac428fd97be8b10a6578aba8573dacaab1fe61fb874b3630ec34748a9dfe0add0f754a8fc55802bd745fdcacd0e3e929deb087fe0985f0e7dd0c822ef39e1d240a65bf3e0f852e13ff1e7ad61e643f58063b758b615aa75b5335290a3f9d21d812725e5c10cf07ce057ed4100224c31b888407deb9171fbe393164e8cee0da90a19b838cb8058c9b174ec0a07059d5a40ec385de9f8ff0595ca9a5a16befeafb7c3e8f71f9f3b9fc5cfe83e5a9b5a08fbd99ed27de8b7a58af73b1888b93be2871ae9aa844cd55e5510b1991060000b313000030100c07c482c150382ed9663d1e14800d9eb25670501887418e21630c2100000000000000000000000000c06606080bd671306782279cbe1a578ce152abd4b863a05c6fd9efc7af0623790058111d1b3679a2fd7e7e223d34685e45d31aa43cbc03210bc41697b37066994e535681ec1b51ab0cb3902d1e80f330a10eeb1591592063c1abd4a7806b31a7ba6a5e67528b644d7f0b7a93f36f1f28fcd27f13e8a0f65fcb08dc8c5da3107bf3266c2210cfcce0246fc602978f8ea4f856e319c55836b1f010497dcb3817868be1d0b07149f9a2c08075ee76779aa1e520b1101fab5546ee489cbd139ed7d28ecbbeccfbd4bda6bba9d1d1190b1f4c83453164d716f4cc98940e62fce6c18c743a860e7dc252557408740fbc431abc10f0ff81f7720f2c3bab97f5abcb7ecb8e1023f751cf1e8852ea976b5a80d86ee66aedfabbaf2c7a6484b9d7f41250bb274dc59727321afdfdfb7164f71e6efe7d0e47c61104e9ddf8b14d07a1d8e748dcd0da1da86c0e594365ab667dd5f184b64538c25ed02f559a59e9a65c747be30a309d1d32cc5f0651f47cfcd01377a96e8c1f276c2c32c6388553b392c1154caba546833fc5c0d4ef0245f82df0916a3c8626483e940f49c7d7c3d6a50fb8c7a5903939209e490807448de70d11ce31b5a2b5762be3e7ff37d90e7de474c51a50a2a5a50238d7866b98a807c91721cb2a913d8229299e638d8271c4a6145d18d0b237f91030e47eb9217c9ed7cef550c7e0b94990f39e4c8b9afb7da20bf33f17a5487ad2c33bba03eeed6d127c9e2891164a59177bb5dfe57ec28f7c4059b2c960bb8db33a28f83252c4d24e8dd6aec72765b7b3ad1dbd5f38c024ba81a9bba94b95676f6811accf1d5673d318895a9c2f9a080fc4f210c78da23adbfa4debf0d13c040ba92fa6a9c8149d733bf268668baa0b9d37eb49bff0085fd7ad7197a3e98bb550f5db8115f7f7da4a791b9b211b9c1ce36d370e844d7390747b868af4148efef3ab4069d7efc351bf86e9f80021139ca2837a0eba429f98a538da059f30aadaf10c689d262345f131d1f13a465e8bbd9f32e34246860c2f0b12aa1e18863d9b0b28a4576224b9cce3d941ebaaf6ad574e97a35b80148d3db5ad3fd6e4807c23d547260266c87a4e35bb5474d1607717d0f6e1c8dcc8e836a0029458447f23a4f22590d561dfde22339730cc32ecfa2b842327d88ca9967f7e154bd96e29a0199f4e5777ffa3857289f540d9594fc961a0815b310d89887bd7154f3e2d0a4c6b42cb16ca5423ce008a06d94532a71b50b8949ec2ae70d6017ca08d5a2df1f6c6777ec53ac1960575e25a8192dfd589ddbfd213adc17d4e80e28ce1a6260848c163ebb1c7c04180e954f7a41033eb4b1c95782ca442577a1545dc3ce1a334fd074da475fea4aaf0d3e4243fef499e6decb49f29224a0c610b4585813173a2d2a900cbfe12e44d1f63de5b083825da3cf287f392510947ef1ea10fd0109fb00918c16262f3a9c9a6acc99767d92ebc9c4941da3e0cf1e0be4f54401961485820ebb6e424101e1d4fb086f376e6fcf2f61a69a54fe78fbc1aa42439c97aae6afb606255149d27f2cc35a61f86db818b08b6a7619d8d6a079260376266c46db4c59f9fb8456442d7127626e2b22db750005610c93c6cb682fe09c414e6d08a93c01ceb8f18d7951bfaf0ea50b6223377b67a735f1d8483cbf21394def9e1b17fe4e4179f71d49be853c633a691c1ec42503c849561f012f6fdb96bf2e0a8138f4ced59fc0e82d931a58409b783d7b4e94493125a4b6350fd2d19b07a853612c7b39bc7ea47f0f092ef95af4b413425e82526b5a5a032fdf6b43027b9defa8442779403efec885a543e4fc77e86d9c98052a2d560ee3de0f6e6c9811405e09fe50d7c51788e4166af8227610ab4a6c1c4038a880ed5806d164783c19169b7c0f21c12b48d2d56f4d5ec18bb4e04e1a7931e1e08babc29a5804ab8f268325e981321c565a7473ddc2414f4d45ca658e90d1946a513717ae64942e0ee039dc59cc7b3c5c9291517a7290e00e58bdef8233e33dffbf7e0bcdc643f0d1c1c8478374b753cf02d9a762ad14ea8c28f28ac15f35e75d8e71165c3d9c0f3a182216cca9f02546e184a1570e9e9a837003e5a7d9c23fbc70ec78f264479ef127effba65f3cef266cf1521069ae87ef59451f3f7832233585d1f0edad7914242796a28f1e6db64942414876f7c23f610836ea0214b4ee3bc3cd859b23f5819100be051b698f05ed3013ef024ddc35c5b6134b08b245607abd1109fc91682de1e6fd95d757231560c9c62b02348e979ec045a193a60171405097bb953d102e5102023344e92d90bf0e30afdfaf6e0c8a4bf49c2c951650e68785182c5993a365944a25c2763862bd11e631793efa1bc442eaf6552c9c74253f727803740b696c7f766627cdd39189c2e397fef31ffb149068060803db59554f648e76ccc681e23a936da21e90c0588e2cfee227c386822c8bd27b19f516eeec8f8c07b65d5de427519d98ddf6eef3c380643cb90e74240f06089ca1ca9b6bc070a5e0baf966525c7a0b5ae80702fd04b1211b60adf5e86b21132bed07f8fdf1bd9f5d6c12289776bc63cca3cf84851424bcd830381d2d9cb6d798f11d4e3f57eb50c2fd22c68ae39115ccd18c3219197269407995b2027145abbce00d0760af7ea52b196aa1c28c6ee4599f9623162f7ea7e816eeb3aea7ddc14958edd9b1a237350d49fcf792d453514f893c44b100477271c58aeb63d560d5123626cbae3df91dc68c53974804e3ea76fad9fdbb185cdb740e4221c13ace3b4a340441b72b8f864505c3d8180454a8185e82464c96ab8094c83f2c7b1d60d7c3d762eda73b34d64b7178084b687bf06ae5fc8dcdc415dbbd61e8082891281670d6ae06d74223d376a5cdb7a2a5a2dcff692e76c741eb6a95dabf25f746acc8323bda83c1ad7b21bb79010332eec41e6cb75682fd6ab3dbc4ac193be915aa89b46a80f27ad3dac69b966079533078a9a4c6090cba8c93ef7d8d37bead79424eb7334db80d6daeba9349d2f32d63252a02336359b7ea6bdb52ecca08f69efe292e6097ed8245d1fad6f39b0c278cde3c3e46100f28df44aa0bc45947f0f8cc70024ff0f4363c8dec4a7525629e0470898197b775a5cb5d324b04a3160b11485d0ab12f46a96cf7c1e0a2e458a25badcba53fa8b1a40568b172d7ef5995e44b926acfa882e5ddc187be57177b659cdd32f46519868e16b5c653f12a441a223b77dcb1777ea5e0a217c3404c36e7de0b35284b6602658b7bc7b9cec8c4b8c8be4b038622c4fa424265098e9630610e35afe97540d8fe7db585db540d5ce3d09bafddb797ad2f60ed6157300724de21f8475ace0d93c1f7cfbe3abc3f040d4027d92365e1aac9f5ac41fd0b540ed837467f27674646c48cf9c295fac2fc7f438124c9303ea2db193101ba46d21fbf1fd067a3b5cf9f85e7a775efefcb48ca49cbdec51358f12cf5ab378a5e65d18fc2fc457f10b275074ea1529ddcfca4308e2fbdb42cad9a1ff4b9a5aa69a77af943222781b8989b6556185b65d24e84cc59cc9b0218641a83e607dba3ae57dc093a9870e55af8e11343454784bc5096a20bfadbc6e0f8de56baef6b417453c05281a8aec27df8d1eb00859897b0553fd1e05fe4dfb4e0239ddcc9d07eeb6d9026e19917a745600e40adddd89b8e72176d6f99e510414d31ef0433a6272a7f958ded7a112afa25a439ccc0053480e42c5b56cf301b78fad74b740503832f2e9843fc6b55ed444a2f7bec3e77fc4d080ed80ae5fcda09d768d697b9676f4c593013703d63e101546f6440cd1a4f9f89093f99f2e317fcd210ea34f2811a0447c0218a6ad252418d2815b4814ce327af3227c0c6b7ba7d8c5e954f57ad0b7c273f5f953aff85c7fbb45179ac35573b394f7384e672fb68bb7acbd7025b05dbf005f2301147462a59ba956ee77a043f6937c2b842be43f1cd8c976c847dd0a63d0eff5a37b4134f2c7f21a3652d8f9e252b5ac450afd050cbfdbdf1454fea2a6130f01b066116336c1968ebd55749c8871d35b4839d16846767260a2b42f830a75aa902a0d0253f1928a0071c60a7ff808625f90916507f34c3616125f510d1e3b3c444c45b3c83007c55d6958fdfdc9e39e1ed318ad750bbe8fc393d4cb1ffafb77d36d94922a0b8bddc023720212a6df3f36128e2b419565a1b6e821a2177865570630d6ca404c0d780e2087888adb88f3e5ef3140e44142415a4d2640ebdf8523a14fa9995951527fd355c3f514e1728aa94c6165cfbc50b147460518f80150339dfa8dd3237bdf3257e08dbea28a407d8ccaaa9a39a438250b629e282905747817f5cbf6faa2f7ff4e98b32300a3500ab8119cdbb1740479198caf74c4ad20c85c607200b8d8d3973f7320c90d14b0077043034c16f9cd87a2cad166d4075d40ad1d99dc2a08a9d9085a1c0f5730fb3681bac4fa6bca1bdffcb4a7d88d63d30561b2a0870bd97aa8a80774459b2596657c8a16663a345526b138b57fad237b31ebece0d2ac04abf28a459063def787366350f56f90b755e9f1cd7818ebd9af4b843690c14beab38209155d71dcb53e1a1b0d4277ee6c8c9090ab1f5bae39eb1453778e316e4191ce28d243e7bf8a9f404a9ae9fcfeee08de8c84782688928bbdddccbde271b1b98f48ed520ed0f882437393aa418b29bdc5c376df18aa3a126453386b5c6acaab60787f37fe403956461d214ef00ea92bd5aad2acb538873c27df004166a7dac83d1ad74357bbd35f597cf70a34ca60bfbb345ddd8444b17c0e3654fedae13907530e7afcadebdd788436cb7b9476035faffa883badaeb9c8b93ddd14e48bf9d0a48493a5d07f68b46ad8a60583ddc0b5c728079afe7b3192ad410af8f46b6610c2f347dfff434f3c0f9796fa9d1689fad5fccd8af0722776de7949e1d4d92be11589e67a222f2f6c6283d026478841f09b221304fe0def19b91d97b27f82b9e96445da2418cac327411fb7c194caa304bdc9b6d52083301a7ead2307290dbef593e8a2bd6f391229ceb94c211859f23dc6a83af66db7ad8ecd7826fa11b0fe56e888b49f2d07f17c6164107a3ebee982864429c159f3fa2b126dcb3e016d2f7c186008c61e8fcf8323503832862295720b82eb7d005df4d14cf3ee1d5e286a2cde10191fa010d29fecd4b779c12a1978cc5da5b985dc4d5ee288b1cbacefefc7b2723a2718028efba20260f9517a61a924100c02dcc75c980d868f88dce0c64b55030494b56f42f050a829b456400f040885654932aabaeb9755287a1cafd98205effe6c050ab1b023fb2094532416b0af0e229b994c95d7ba54c78d7a6eef0527c26e2a61a50d1c61b3e9e0186257e0a44884a3722b0c315987ae9276d73f0796ad999a403873b97068e0d81290df0c5e4663b409b8f0c4e47af70c4e6939cc577e813991e6fb24e97fb7a9c195d8fdf367d5cf5a5045a4c239e693a24a8377430161a53463ff7221f04030b829a9d5fd46674334f379c83485a4437cec39b9c4c8ffff7faa8c3acc25f998e5a88b60e04d86c822c9d117a42721455d9add4e076de26382a1993b3623f5f996e1c00d4e55431de9324d95a4b9dc636ca7a60353257154dddde97cfd977fa30e2b2b8d955f7d8cd69159656166ed18b05275f5627e66fa00e11fec077af8fb772473c0448aa8bb06206aa866c50a40ed6f7f9f0988c302ca7b8ea25a2145309a115c7272c7ac548821eea80ee9feb9fc38ba223d09481215b7ea2ec99e04903aa84b4d9bf159aaaeb60415ba51222526f16b5f9f993b03dfe3cc3eaf3ff52f559d43c86ff2c135ba7e61a98a7399833813b7d7300fc049515d3f535ea15d4670961f81e87ac33bca122645da754667fbf6578428ab92675df22cd19e0eaa836148f442b2a0d46b8bd6fdfb1c432a8f13497c217e7eb017b68fd20b7d98a9cd19ce959673c45e848ed64e57a78ff53eb50b4cf7ec166864c1a89de11d0139cd363d789830f8adc6d6af195f4d7bcef7abd8e05d542f617e6767846c9af30dc6048a4f853203e562816af6829851e1c8d8ac27f3a5ddeb6b113f5c94195fa01e14637bb005248bbcf05e2092fd64f07dad239558173b98201fbae2e63e7909dbcbb8df19c75bc374f0a2b59d952acfd8960fb88c394e4ce182fd067f24c5114f04bae66354a70a6879ef8ea1a0e6f23a2fa675ccd9d60036f27c1062659ada4fe0821b544e4c77e954807ad2bab11657a5aeca32ad002694e48b82e02d7ac6424a0d6de1c1f36fa0a4fe558558c317319e0bf7e457ed7752dd0f782b549f80778373108a69d3acfa8100f97d2b4a339cfe4494ff3a75d7680fb6e5e703203d728da5301e7b31a4d3704f07d51a51b80f4a73ec592910326a4a6664582f7dd859059913d6e55fc69a7aa83c66c7f20b1e4b63c868867d3dced4062adfe3dae406f5d5a89a106acc91ad56fdf446cdc73e1170bd201a321b68f04ca51f5ac619b1dbd736f65233620ceecac5eb04b87e2324b40f44602cc2baaef9c5484824e83606b2868ba005dd34eb0d17737a9432ca02be33f71840e5e74de21be2b9c18dbf98ae887b24b159c6b1e3bfd65879dffc61eb19cb0c9d6e8b6a2c3696e262a28bf092c27389bd017df495e9d95252b35f693aa79180989b102076bc23244aa059c19f736b8444d7ebad5a03cca2c2a1e7cbd95fa5a8a455310e2b753140914af4e0ca13aa2039902a1110fe9c89493e437d73cb1a2914a75b784e1e519d5d6dc0b301196254ce1322c3eb6bade772b56b3c2935256b5e23f247aab2688671d10fcf93a754a7d4dcc3f1b60c78d45d360ec3ce91bb824fdaf3bbaf997b76a78aa809d6681ae8a3cd13a1881acfd9ba6032aee7c2c56661c5a1707a95954d6792b4667cf6cdacf2748962056d740533d9ad4fe55e3e932c37a4ece8f4e21641dc6228703ec270c420d1ca35a5db35a938a26663139481187b402e780ba81541427d6b1d772f0cd811242e62b78257c3cd208ad15642a7b875acadd032def697976b12c4252d17a6ddb528fba02d2c07b4be7a24033cb2added4e6fcad1c32562d14687066ce14b4cfa21d9b7abe34a037ac31bda14704b612c85e431fd0502d58a575e714aae7138aaaf0d6f08ce03ee78b234d13fb3f39705a92cc780edecc55fd9c57e81b403e997965997b5081d07a7d21fe9a125fb209d900a91cae1a4f18fef46ed18897210c3333101da7ab82959a6b5be8326a786cd5de30cd7e9072baba3660e39695bc45446ffd25721963be3f43c8a859feb0dda5d9719100b95ee3df84455ab78df2d7821c7b520e73e719b7dd1223037c515d83b1075e7ec347480caa1231849be71f5de6e46ae5c4d601b3f99334259f5828b29dea57ab13cec80fd213596fd8595c52de9d1ddcf17f0cbc2667de85c1324873db4c9cc04a332e902850440dc59f633d3ed16a4db36cc27e6e2031504c6b0b1eb230240e23376f4491ded4acd188b428347f5688a368d0012e0841c5a0440488f019069d17a00372c08d5ab1f696e62d1b983924f301156c6a0620618505bdb07b1b119e414caccc21810c8d4b6654597ada14b4d1e7f60ab3a6d230b1037a2c92d893c9d7051f9c08abb0395a7a37caa63cbce0a43ca5d286c8af31a05b359454637064197af22b9f37752e590918bc3583bc25f77ac8971f76f1b8eb8483c59f21ee6d1fe03e08cf8f51b0b30c3e73680e6502b1b2081550cbedd61f3d0cc6801d626d535188c6dec0fa03400fe4744581cc73295dff56781e1b5518f5c01dc59111ba12ec217df3bde18b75c7c981a7ce1c3831fc32ea2b564c28e2856780712362b67ee93efa0b3d556ecbb8f4da5ecbb037c53d178f640394f2c7f1f2f5d8e2fda1a5e3a418e20f7c29bb20a06e0e77487437d321eb4ace0ee1f7387db120426417bd1e77f86a12a95c527fa0cd78c11435334214a5b3c66965eca30939f0937de29c30ef538c29aa78cfbdf57eabb08465b6b9571d069678cbd4f632b88c1653621866c0fe53038f24363d22e6e337f19065d4b3ea0e938df66187d03451201e320c4f49236532ae6d0aa46590406163060b1a53828f1a2c1578653889c7822c5cfe20d93e3d122f00c409a453f72d691e20d599aec6b1064f13dae29828a8cabafebc178c3d5453cbf1a371210a59f9672675aa4844086a47621d5fa89d38b315020b84923b452dbb088c34412a5a5acdd23ff618a8bf25ee28a12d3638d7b84b459eb29c0617a42286a87001832192cf03a2eb78fd21df5da56e711246bd1c46bc99c42731a55999f1159527549ccc9660e6b6615e78dc4bf84e7b19aa38e0f5b1a901973aa845d911d7d94c0eaab75db10200728eb73e3507afda075dc92bca2031ed4518b7a7126bf6177b00cbeceff35ef4e13f73ac8fb005b787460c5a6e51919d16b68d5e76d0160f710446b1fb515a2dbf91792848f2958a687e2e725352e7ef69fded61f7bfa3261a589fa286772749a68e764c339007f98a81b6715d0ba12420b34d5272a9362965df84c9a05307ca1576459710b7b2f67f4eeaf3a6431024f967b88ee94ac3a9573e3ef4567d51ce23a4edd41b39da1dc9f9c031723a9191b8665384b1282b5f92a3171a11db60a49a03d33b347cbc787c2018cb1748cc5b616320d67d4df993c6b49fe7a196b7d551c3f074b7745b239d444665959408e2234f034a485ff53a8081a77f9780d45060da525ae70fb1e7b5d21298535c57e11b47b3a947b8415e1aa4fb5a7ab87b45a68d885ad07eb43cafef4b3747131a7c8f6532da4322a0cfe7daf181811b3e441cd4b1d46e1aff21d05e7ab46224da7f461cc2407f2422c9387afda12089f099d1f989cffff82ca5476c6c2fc0ecda6c0ceb54a9d42e64009d19cb5a9338e88f6a8cc72d42c81ac5490a93cae08078a1e3fa9268ce3bab464bcb02ff8bafe44d4a4f558f7826b657b6c54be2397e441b650106a80557b64b22704a58dc9e9ba300288487490c5d9a8a77a2ebd24f03d6c010e2c88001693389dc17f85305610d44939f5ce6d044c6bbdad13299f5c8c8c94e4ba06d14394da2210849067cd49290630b2310fa09a9a94974d5ac9f4ede8f3b05c999858454e503d0405195090e2aca913e00b594c703be82634106edd10a67f63edfe178d2fd77d77da048408fe03d741e3450b4029d2898f269e24f8c0449447910fc919ead4c60ade4b24fbe81624a3596835716076ea0c8ce84474eacdc883c82b8fd40d18a76ca7e7674b60c143e708d6f717c84c1a4bd77f653e0d011b3ea0fd823408c83301828b215614a84f29647068ac16e9417c758dafe31dc1ceb78324f5fa30af40e1d1842d2e5cbe98df2590afd7de661d18b93e6f5c79f5e03c5fb86e121d6a9f8fcdc91e46618182f50e2bf0d149fab4c652bcad851a6bf25f433df87028980d727d632509c0dc23c4d80df7d4ad3191a28f20d7f4023c800fa0f0112cf232624ac2d3e23e93f5a1e4e1c88d1ff99130b54dc5a3f80e2517a2a0709fd15e35443f285ac0b0e05687f97081d2826314b15f69ea3a93f503c3ae2b02f7370b8f06aa2915f2352ea6a44dd011438dd3e2d3d49d412be9f7290a00d19cc99213117899d6fea81a2b384f2a3c263a9686b295b6f031e0ab0026977a0884f3b4ae30c148fc307e30482e57b73332a2c81cf3817f797ab394d516a27cad7a1a514097e263edc65575271c915e94a1f00c21556ecb388866141ceed9f71396168721ff6bbabe786b844b8ae37c2c38aa688241863c543dfb794cb8a5f86a4ecd98a3fb7d566d757bfde265a8ccbbbe202c93f1fde2fedf5812c73f8dedb3351f865463c14830d5d01cf5bb7fc67fcb54e79d99607fea454cb5171b3c7e1465990d08743f30bccd13796cf80d388ff2f33d960769db1113b7e9b92b308562db4a98908a29899090d93a766c02fa7bcdbe3dede2934c267540950319e1708fccee254981c21161b56c212c58e8a589753898b5a4774c55af0863009e6eb681aa26c1f426adb1b6456179444ac621d61de55d5ac8b5e26d3fed2cc0489ed707f99606c2536c0e4a7ea7e2c8313b0d5fc04419036c4108c62f6114785b3902f9d2e5f999ba7319062e24648244ac4a330fe244d83ad36eae1308f3cf4d12a1be6438ec297a3c30c3dae1e7d4854b4118c7d8888e9322038be796d0af31331e7973d290640999d9121e6d05ba35be0ab1e158167929629548baf289027ed79411202aa1a2e5dfe44c27192825d64553bfac553a0ed6034d1231eb25b59ed8adb50c767028cc29f8235dec6cdc6c97711e0e073714af09ac3083080a6802b17660846c7bc5d1842a01d9894842930b627a6904a4290eeb906063a059a36801a03a1f5fc7df8c3c4037f92988211817edc377cfa5b300c07e009618547f0c17feed0eaa0d24e07a2c90f2efc3ae6d387189ed4e97774e178269a6632fc69eb36c4058687572e89979692cc9459980ca7dfd5d606e11473bb4d5b3fdc6a7d281df6e26d3c9ac022415e3c7989fb45cbff7f0a4b0bc103423db24128be62bc86ea0b10108253b88cc6e5bf4716bda5020c7697313ecbb507603cf2afb7e2c046ce91c425c005ad8ff183f6fb22cbbea2deb70e51454ac5c0e97d93d538664f940cf6bec645bc4b96a1102de112031c2680d1fba6dc5269f34d306156f734824089e2457193f0db806acd69c486ffce2e1343222709c53f798a4fec57355de36fa2c9b9f88ff3a58368f56b629d80c2214a057993df25af5bf113105a26f271063ae9bf75e59f11d171e5dbb59ba98e71517bdb203c1ab1d9f24e44d789431c89def9e95444ed38f380f96a6c717822650d5cef0e3cda45f53ae363db0d9f80fbf92479978d8d55e698fc872ba3c3c6a7cbc714f3da3a2c7e63f5db44fd43c9f04387bf98302421a2f142e14a97ca5f2f91bfdc3fb1612ffc9a8cb9f74c007c6cc3f0b197bf14cf626f8f5675e911ecf1b393c6f7268dcb4ff23eebc84bad04f8332a1b3e2a0569d51d68d5128a6e7fc4389d02356198b8b7d9fc4862463a9648dd62287202b7c42ddcb60eae1d62556dd3e08a36b28dbc629c0a09426783fbf7d5dcf05a8d51ea413695ad4a5c7ec704ea846aa2f3cb0ebf3b0cfee86a02eec02a263988044e741cca5e757e6494d0d0d8d7b1c0b3c1729c6dbbc1f6447b0050746ebe00ab67270be3cbe0e8d0aa2e9f910066c8811512b59375231b463311b45ef3d5626421a032c8823317f4c31de86b6138530ea62ad290286b3fd43d8521e597399d41d1092238938ea8b56fbe81dd7d7e0c08f1b899962464d81e2dc5fc982ed8681071b5d5d3e8199aaa2dd94cfa9d9d5e83ce46ca72fa77ca78f17d21423ae6a0217031d2f3d01c406cecf7929c41a04138e56c01088b4c0c06b99da614c812b1ab6285a21f04661b9f248cf997fdf46829cf2bfa4531a50b41b92e1bb76f6eff56891d0210896371f5827deeddf1b503fb124155f75927313dace82eceecbb0c7823a6c12a111b58f24615a6711b6f34b444ac57af46b7d3c32b8049f0d67ec5a0c2cc2ba226d49bc85918bfa70b877c05c0185e1b0ebf59a5e1874bc55461ce95a141462c12aa0604b4f99037c13f00aa825a19e9ad156ef0c2fa259201bd4963d387e46fc2015baa275067a26f63321eb383a6e2e3404e57b4467e2fbd42fce93ac305bb53ea0ab045da4b42c38802d9c9e7f4bb1a968e3761805ab255d152a4cfde2fcc361866839defb0e1930b7301aeb3dc0b3e20c3c46017839f3c59f0d0ac2d0dd52834c046470a8b7e31702903e6219e2ce79e516a023632e0b5c963e0b24c1bbea6ac3c2f9059529653a5e1f5455b8564f27e9b9920aea3159fb8d341d09c6d4888bf771d6d61116039b0a54de59e005ae161d71ab72107af3504b81cc23a7681a3d20ff113cb508c1ba25aeda77a14ad1b7d54fafc7181c10eba03604c85e25932e15acb37b0095ffff196420710c6d34641966c8b1cfda63d70d2a8724c3b32f301fcafe07bfb1d9a5bfab85e1b47432b092303f621c160e9c0b0d0fd9638b3cb739e9fe09d0e64f10cf67f8215c47276ab52e4666e968ca7ce8a063233068c6a29b0c50d01c64e02060744608a2465d59ddcadc8f253aff80be564cd89ba601c2cf16a8f961cb8850ada1ccb044ec6b37c625a32e520b8936bff758c5d92ce78e660128b2a6141c799b45db3b45cf93ab609e379a9d14ad973fd352739b83ed0e56fa928a2dd74cd1a751cb929cd167b4157cec4138e51106a2fe4f5e1552dd6e48845faf8ca4c75a6a7a02ff581502cd8c5105e685c8b724909c97d8508e9a42ce10554d8e0de5e68a91165ddf83ac5d09eeb10f432a4d9636dfbaec293a8e855b47ae463e46f94f1c59e4fbe414102b9f072380c987808e5bbd8f1e1e5a884944b135cf30df452fae0b731dfde08b112839719a83ea422e2be944a57a5d6e0b68c22bdfb013475dc14014c22de311d7ce7c8540b4ac0179609fa56927b299587efd9cb9537d6ffd26b3e22058162ba22c0cc8a5f7e5349ee11dbbc69afdc242b23edc5c0230ceeaf2f6a31aa7f73348a044d4491e215e8d881aa32114e4360bce47b425546d2de92cd24ca8136d363c441036c69fed028fb90199b0307ca7eaf05f8d765a00405da19986004051f2ccb586ddbbdacca41caea5aca2a08b29b6e83b8ce9e772551d8d3d400c3e65a917a1262414ba30cbd2eeeb0f94ff8291caee4721ce4a270e3fbcae2b0c6c4ec2d73c5b0821d0afac772e03cee54348dd85e4078fdd0ff62212c93665a234e45f54651d57eef0facfdba42590ba3d9fb7f0daed0ef57886b2c4cb792e5c6eff1c08cc9367f258410f605c4b75ae70d2d0aa85804348426eb6817cfc90822b2dc40a9d5a0138b78f8b343356f94231682433b7904a098055547028b78456339b08268d099351c221f2c61020ec3c4b603d0091f0e919569314e6218acc0b08d54553c44cc860579af50613752fc025988a612c511781270496c6ebed37cd3286491196268c957640b57d058cd10f66830ffd315b9c9031c4949b8e4e5316b11efb60fc1e61c881ee38a56b04c134514ee8a5549dc1fe802f85f021071789b87914fb5d6629f2076d067e4891364207a6f55b017fd903c8e09d42d2df272af07fc0bfc05f1131cd5eb78c2e19cdfdcfb37a49cccf67ed4ff00cd02fd8a45cf10c5878a18805ec1136236a096e14e6657072ec870dea61f90852bcd1200c6b19b822d8c2c45356ce00bf19990c4267852e411d5f152e1f0e58c0971f3ab8a8417b75b5a548cf1eb421e6003a70c2142f5e7e881576e9ea33f7867185b01a84d953f4940d1fd32d690ed890de62964207ac33b4cd073a03071c27b69af3f4741a3e8323e58d05eaabe06cef15b60e276158adba7e16b107c02720dad309c13b39aeeb1bb08012eaa1b95a977b522555b8a90e128b59ad1b52cb7c872396e272d9049712cd606df4e780da8bb11c4db96df45811c8f79aee842e4a7c477cb653a0650befe62e32fb620f3cecedf9dae4e0e449854aeed928390b79c180a259a2fbd0aa06f3ef938169384c7d5602513c8d23d2d9507568cb3c747d96792abc90cc9af7b09a50a26e8c82eb6878b96e7bc392f4223a018a73ebcdef16ee5f5954ae1bd9a0b8857eac28574473b65f2eb3595d2ef3798d191363250832409227185236fddb66f1a08fb7b754ffbd21a3a40bb2710365cffc254655a1a5bd604e4b78f318fb37be3a2403c53f1c52d2d8d635da048df5b103671f65cdc44c026a11e4ac694b828e1d7ecfca6b936dccf137061206d3901326578fa8bf935ad6f6b2bc2958215a1f47915aecd8e522b726d8d129970f360e0507fa7c246d5612a34551e78870d02fb5b443f015a2f7beaf3a6acc8d622a7d904218e329c9da94419c2d52cf49905c293f0b44c8d8d1da3b50b1dc8e40b3cba6e336ff9bd3c4570e6617abbe1885afc60c289cadedc09ed11073adf29e3df3d83e3eb7499672f8da76123450457937b1ece91a5a46dc320f12a9df32581d13722409e1743fe73e07b0f69b909e5d1bef4260302231014209fa871bb7cbba6a416b930311dfee72ccb80408ff60b36185e38576dcc7d6693ce12ef76b99200e5a2b50ebb61c3c9cc910725abc1d983d823a6952f85c2ac1ea4b6906ced0a7356a726764a10a5d44248dfa58155e1be09851343a367ade7d39c5f72d2e0eea6cd2e85ea55d2176847306fa8123de9bbf7083d36b3f7d7d500c0878921fe29ba03b642135dd3781febcc6e7e08d731d468d3b19e32ad090ac8e21c968d8fd211d750e9f52907f445ca5e6bbc9afd09d16f2286f1f07cbe063dfa7062b7238259f3177af8f85187fc6d895ddcf3c14383a9a6d20789cf92ecc446fe3b8f9ab501580e2303d703de36c4342560858dc24fcc329ae736c5b3d5a552646abe8375f9b068c0245d0ad86bd06dd5cbb8400a92c16dfb75623da729e04e741daf88bf8a0ffef4673fae91e544d32656a0f9231626f5d35495504907ad74d73a7d80387057369a443616cbec9be6fbaa3e192742f8bbc86718ff6c393bb3d1d69873e901b86b1cab63edef228eba8484b1c7e2209c899fc0131a8fadbfd41f23033bb4db00733a56446284fadc774978c23a2f3c14a4fe4364505d6cd5356c6193e177e6b558bd6e1b01dcb45698868e66704ae1f55161dbe50d377cc431cc442ab74ac27bad6760ada194ec41bb3b709f036fb0ecac6e7cf1ec35469dce571456d1716a3711379eba07190cd21a181003070b96a74a6af67c98cb53bd4a786315b78e5d4726df14130fc3ca75600ad5f15b5bdee2a9ec0447b73a8a46388495f11711817d81e4a99f0496ec57cc309efa5879446684d4b4f8632d72e5a9729332a9b9d83ee1a878a0b54c9e852b951fb460fb02e609dd2692ad29ba215698945ac41d8197e8c9ddbcc732423fc0dc17141cbfc5cb185de6703df359b849a13dd149e5b665f9b7e8041d933986bc1c569322093e545aab0ca83aef98ff6ae92495c4508ebf3e3dbafe896c65862185c9358f2ad882bd35d1b179486a72c6c9cd2375a8f0c50f071f1062c18d0adb0b76704a4e929cc68af0f597490376c7fa10c446c9a103d3d5e7cb0608654da3c835668ffccc48707e29ac1bec94690184567c1ba7206a8e9da27317b5f5e39831f42f911d7282ca6d972ccc5f9d874897e239e04e5c532a0038a5f830f191004544923062a30ff902fdaaee216f8ce38d559519508784c748847600f6d36bdcf07e017953fb75f0ae286d22c612ea84acc4559ad051a7d0233124d2f47e617bc71eb528e5279e8f53b08006d9ce3fd2d67422ccafb40d879e337855b1cb6b25985735145a9b521f10ff9604e3ccb2ada72971e484817647484bf2af6f7266ea973c68785647201cc225594771e55a68764af4d04f53419aedc990d65babba7c655b85534760739c8fcacd4b0f685e82a500ff88232ee1777b44e08e8e6a68efa486dc68fa755d5406d0bd89b6db80fcfa737cad53c15ee8e053e1b21e5b2515891c5eb68edbb0801440bdc01f5161cd38ec3ea836c288c8a771ac34684499a243847f78b8ab9cfb3a3b300bed17385600bc7aa865fa46ab718810febf8f0a7655d4ebbddb52240a83cbc81d81eb9ba39a580f0e3b692a7d83d319f7f36317ec777739535c74678ea5393442aa420d8cccb5d826a821b3544c1ecb845add57e58b5f182ec914a6007e551185efcdc9e94a9d95b5cd15860f967e7914515977108e62e72a6c7049547f09b7697bab96ad8412088cee72eb6a5030f66c7f75e2560143afd4bc7f1a0085cd032029c8c504ccae783b1627280d5779ee10e1a0e24063843ff7871713a0eab7c671a4d34e088f557b59b4b9cba37c71e174f349b2e83879d4c1d45f444e1d59e78c2e3f1f8cd04da1cf774a2cb7611116ed618d2886f6458f51ab8c45c004a81d480006b9194a3dead807929b8664383bdc8b5c20421d4eda33ec244f07a03850c56c6738b821a872ece0bf57f78f37a3f00a6939f012d0501ec09a3a568664e476407458e7247a00d4787edb051e9e1929b937a4734ea84d8b3a236990854049c6c5061520a82682756c27733abd4220989604915ac1a27a760931c55b5479db7251a3b8b49b7e08cb76dc7d0a43838aad52935ccc618d33f5c16066a63687d9812b16b69662fe5f81ef034528e1c234a13953daa11f62de8f6c43d0af8863bea6a70b5eadf6d84bf851de2860624c25d5a5f2712cf8d3688d99c95ec5e16e96be0e24c43b3319242aa25e313d3802c020b9ca990000ec6d53141e01c131f6a9fe0aeb19ec24bd27794d7586d0e3b81e166ee5888b47fa99658af98dcd55f8f2279c36984b4a2d4e6b6722fba25e93ceabfb4acc76be7c56581ac6096f99688f21b438635d17ed33570a0ca4e6a62bc34a78676842134be15e63eed0cd1b9a00b0618ef1c06ddc3f45d9280f7c120ae897fc56b17c18c778bea846f3bfc02805a90101c28b8b86f32c30900602dd9ccf04a54b7b9ed977c183fc72aee48a2680f8e635a352754d8e3111094ff1af8ba6bf2b37146567bafc28ca416d77c662ec27cd513e9bf72d74ab7ce38f3acdfcaf85a3849890eb77bcbfd18caaffc4dca53690a0f6d28919796be81e07cf5b31b82eed7efa45d507c157022092f96536de3f6c179544f45a43c7393770c840684c6420ffe7bce7b4e8f2ad35f9fd6555f650e49b3d65ee2177d876daebd0552b3ed8e77c13d12859220f07577c9d1552a118216346de5c0338adf532b32ba6dad6738c95056e56fafb4174152dcc9cca65b0dde591ef7b0b35370abef0dbfffab64af644a954599020ba0ea2466b1688588aadbee22c494b4266fa8a3199c1548a2b74d036e3d490efa7543041b44a2d32ffbbe6802c0a4e583e1fd038f0f7b81f16d311666db02b42b417299f6591bc06908433b2e144634a2fae0624c3c6bdd025210c0d053990de6ecb065a75566f88e08d09ead8af2521ba3f4fa5f9c65535681469b9efee80c6408419f6431b2eaa3da31a319d5e660799e3f0fb1145874b8c6d450211dc51b2e4b7d2c9e9dcc5aaa6c3b68dc1d1b16cf3b32d48080cbb76b80058bd7c847424ce3e81357fdeffad7d395cd6e7cffe001262219e36e4d645e1af4cebce7b03bdf10068418bd2550a71b059a1327a65bb7fdcbf64ef5ac072892bcdc5cba9373b4e4b217208cb257f71a0be8e353492fc2fa766c4d4956c08ecc0af46e9751c3f2e61b0a5e4a81e29dfd2afdf99e227af42862d776d05a282d71385000d812d27d4ed26e2cfa13fbe77070159665c429ccd2505757d15f66a2593cd3009d7786718ec37ac16b557d46289169b6df2373ba3ee0b292877205db9745838501b18f04d6cd2a2104b48d9aea89a8ae23a83436e6b5f8dffccebfdf580b97224220ddc3e216010d335210a2e7942979010f49388c9263e2bafb210143c2c836a8eeafc6fcd0388cd3083bf7b74a158a43a52fa92155d189fa15e4797d7ed33b95761d50c820173729e997b5ea2732ddb51c7fe4d4ae07d04031bd6f83ee81d7e330186d2246ed7893eb16dbd8cc3cdd881010897ed15c614865aa6eab4a62d2ace73f15953cc9bf9ca2102d2d27839c41ae60f0f55943cd32f85bd0f1ceddf95992a8372b69cb65e12201ec1040b40eb1716b4952b2f2ab28635b4e95ccea5438c98b721d883820b0fcdeb2948fb2cb30d012f97e6d9be048d8a1e75e036cb4a8d6ac1e94049f63dd7a67ce7c4a985a451b2d643977043c55bae0543f5fc16af05e61053f699e4f8e1a5497a76f2a994113a5094faa87a25b8e1f24e1f94003a51c036ff5011b85a40a7eb666abe861768f702dda9f3f7488a5db37476c1e2d721c0078315f570817308323ba222aa10701f1099c04011d9b4f2eeece79018d0200931aa24075aa44aaf7832ed5f298002535b1d9b944a8a298cf62478e5652627deb3caa536c622f488033e40a012447b7cbdef7a2fc91eea4097a3d2754ad4b638ab634859ee201c8be619bc7662a9f76e628d942d3c18256b768f7e105842b3d085d9e4be3c390c40ba26918a8004fd5fd5d38fdfe2d6b245d43e672ef4e4e0b216f15ca070e141050c4e2f0c9e24333244ffb8a2571029f19dde053d519a37a32179fee36187139a76ccbd48f95851b268510ab14948171162be7cd61cb598857f0aab6c62031285827070c166202c78244740e9bff2b10e97138dd5246754c23cfa639c6f3c55bf74112620359a5cfdef0c29e9b699492583bb7d627995f50ffc5056e022bbd38b1e33854e5599d63451dbbd2c9f007faa2481514826a082735d902caac739ea29f1b2dec0ebe12ca3478536582e09a75ee11c80d580b87e1e3591792bfc490cbea1261d5dede4630239e44222246ea39e81705686433f4a11d44f56343948aeb2a891d91c5ee47f6ced4946dd735525deedd7b89618a787000dc96f7ccdf9f6b364120e220a8c42707dc4aabffea5e7fd0bf183fc09daea20fb37639904a16c39c1bfa8517138d49e58739bbff137c55a23a251b888e1495ac71e42a2feab5c8fa08f4bbe42a4fcfa07289db43ab476d15f9a9f9be90a49bb0ed8c737dac1210fad3cb239f09e694bb8e00bf8dde2eae0c6aefa6c66a0b2f2283612985c545180d2567852debe7061336f7a3f6a06c594b512a8dbe1df244690fec71444e1f19a7575bf65c6edffac176a353b0b631a88276f738863ec590181b9bef4d0993a04268499f09615458e8509e24b747f317a6356cc75c49bbfb2df339baf211fe6043017811f9bd41996f999220f2f2c95c838129699082f43f7132550b396f7e6d84aa18f473f05a8821de3ea673468c7594556205bd03bb0a5f4541266f80264d4c737e3181b6f36b4d739af326fc776c14e07d20c11d750ad002f0524ffcc0cb1bd7862054499b91859ad6bc0fd3431923d1a2834271018045840cda5d349ef8285299a8e185b6e27611ca4b1c87aeeeae6dc589bca667db5a356dbf66271e7e033649d01ac0238baf3e914748191ebfc2aacd26a5776b9c79d53d0b503b644162e603d166ac3747db1b3206b03e14ab03905dd8929a4bd0ee26834eab6b56e455a5a9dc99451b1a10356f29e1b1ef67f60d564d6760bf0d6fa806f2b57ba65a0cccba510e327ecb3296d53607fdeda588054005c3c6023f4ec0f2261434785644d6ccd8480e3fe827a879d51f548a8d5f74581edc7b5332c336dada185659bbc3bd80d3d4815f907d6c201ad56ca7091cfc3b3d64cb64f0b33a8212e84b898382b175cb0f500e580e16f4ce773ab82822dac0e02659e5d79110a3ca30fb041f7a8ef552260711edeab66e1407eaf59786ba16927ccd388e3ef01d8081857c51beb338de886c9c0a70c03ac6156f55c0667a11a3bdd40ea75c285b1ee3ff4eca259f3cb9845024981f781109b436589a3691b03ec6854baba6355311d6ebb30ade01eddcaea03ffaa66fe2e2f0be3e44a6af7b0b499fcb9ecdbd54ebaf731d35d444f42804630eef0d693e35110a5b4452926a53a3ae70c68d6214ead48be3cade3b41f516abb431bb26528621e32b6f34e4955c2bafd03474bececd0394e15919d8150c82e4ad17d2d696e0b3995791504ff51b1fab2176330af55c7a3d30dc06488c93852749f2c8ad1faba11e08abe2d6f0a64b15035f4e60023c21a4f2431181213d2d0cb5f15fc75c305dfa8d5aec4611bc4282d5259116b4586552ee6fe9f50c4da0d936f040ef728012904dc5715708a1247d4436fb0ed16b891ca1ac9aa2c998e8ab98d855d2426babe7f46c2caece3daf9ec3038743b397de2d3b4653d64055d4d92b9fd3649678729dc6bd343a2df5aafbcbb83e07520f4dc795517ff9be65a8ff7bebcac358b09babbcbefc0fc78a72ce4dd3d980e34bd09c5991248de41434e56c8c6d133f1b18b6ed83ff227373f8c44b72e41dd197b3f53117642229fafed26a10b3f2fcee5c2bb7343d7ddfdc302a1f31b8abe017fea6aaa125e029242fe191372918e7bc45d16c74b270820be81e289f03cfd12b0627cc4aa62d064127dc00444f124138021cc668a9ea0f3070572976518f4ea29b5ead181171fc27e350e484fa3cc66801a133c89b900c72db956e3562fa403782114df18f752ddfa0a6036e7c50d7c272c1fe9cff0dfb5a63ce72cccfc7a6ebae3cc3564b20c73a3905184bc0c2f9e1b763200dd95a4892a4e93d1e9b7a827a66fbc83b6c8b795c6727d96ef0e273f811d7f6a5a2003f532cdc7089bc3e1124fddd50b82e78126f2839f3265b90b5891c8369a19dbfc8fc12e2f24b108de6b2a52632e5c2cac5bd1a8f2f6468443036f2abee753707695d2e87c93bbb9b2bef647473d44bcc4d748df47214950f30772c43eb3b2df6063a98f2f3d4b6c695319fc5afb621126b13b3f549b75d25c9f4e41fce956bc634fa3dc46193af12d3f07e4ab921bc268742aa8d39d8dbfba6fe9c20ad5a98aacefeffa4f5fe0b451daf9ee3b7ffc80c7f145c1f13cd55175c848c0461bc7ad45d178bc60f67fad6ce541de867ddbeeb1ffab288be3615214fa357d0701f904da728a97337bfb9ebdb6b8001627aa2f1d4b1a73bde398d566c3cbee38926506cc4329b8d4b6c4830a774a8b5a4e39f36c300cf849029b92836dfda43e52cf9d92e21a1fc9216397d0bd8239fcd13d05cda4dea5cfc84954481de5a391d46724558832944a06990379b8f942a5af6de1653ed39384085b852b3d022a068f217498e2a4996db6ade602ad55e10a4140cb1959da14a754164bf38a30757a5f0f8617e803bd89c8048435b1dab9722cd1296e47cca93eeb85535d762da07c4df03b364964acc870c6d3e6b107a9566435405bf1d180cb8ba62d7172a786379c1e494b0a87ec22dbae85d073a2382254420b8592823a9150d1da4fa8c34da346ac4549bb9d375c6fc5576d03823b86bc7bb17ac6553a8fefe2f1d33321dee6d7e4029ae52c941f6a9d639a5231d373f64a9961fae6039b617e4c3051cba39c34f8c619749ab44d0237ce7c53df5b8cf6575211f587834b5d5619df83b64d4536702da05398335400b1d5431e630c1f058f0eee3bd6601d853ddeaa0ea6572bd185876a98e19acf1fb4fa21bef13bc56cf5215c56588c16639674ec9350629efd4f375463598d85bfbde1b80edc6be5ba9df2a06f302a0c30cacba68aa50e960167eca7aba1fc09484e0ecab27203f19ba9a8e66a7b2e9311f880358f9d9ed14ed74154cad93740ae4b3d782ebca9bdbc502f318e05b32e8c99bffb8bb7b2e11b055bb80b105d877fa855712e956bed1ca843abc3d481aa1fe75ef8e0e6c0ab28f7520277c523e775e0acf17ed0914aa05438f81ce87fc9d3e67d72dbaf006f8068aba9998010807715680b18361cc433d7d85504e8e081a960c933e73bf91fda06c2b9dd36116af10543993ed8909f6e6a1b16f9b4278eb74dbca6ac8a4818397a66fa369620bf170dcfd34711aef8c863cd00c9f864c894e76f9f541018b8f941b22966939767a35b1f7bba9ad2d889908c8d308ba8955f589aa0ca7123e5c20de8b0134b938250d1d5d3dd5ed01ee22ef6acdfe8d215d965a3e3c08ffd4e3265c2f1d1a349c0c24f2285ab2c6a86a8dda515a0077fcd03089297038be45177fa22b5b1ea4fa893814b16fd46f5fc5d40db7d23d804ddb0e9d2bea6564d178b7bc9df1083aa4370c1e5d4b187801eae9f32bf5eea46be3c24067cfa1acf1cee2f000a77d507c8ea22784e428c247d9688ba54874472f7dedb5e6b9c1a0607920dd7a5740694b7059408f839c82f92ce12da5ba21a2a177d3b2c83d6c0fa0bd9b867532d40153bb82a22ef13d7c985aad0b976ee62f3c33431dbac51709f9d63159f99cf09ad34ffe9822b1426a5b434de6d3b83c2855d8542949fb84eccdb29069d9185bf814468885041bb88046a242476e740e14ffeb6e350c9f50fb4d99e4424284cad5f68f97816128d2d64aeee54c4237af68e09192f51b5778193f30ec24705c80e8fa697c5e030929b2d7da415fa140297083860bd1ccd22abbe9e757723f3760bc80b8e978c56233b4a930579b83cfb8db0ac01c22ae75021cb9d3193eb5253a633de0da99aa747b52da3088039baa3f1aaa427b9d7d44ed72ef685672482cd2d04218101798ede5e3870ba9fb31a43520deb0fb2bc892af2ed2203b51558453a177c4f512038646cbe34d952b9b1156d71489d19ac2a9322769a2d0f9b23da1d984107552dc867e688958e4f10baa38b3a178116e4da202fb52fb1a4c3462aea0e0726e9a174db6e37a0d7602a02544434da948b64ab957dc8f853f6581fb3835c5b0f28ff4a4e1a198aef92d28e0a767c1c2a316a8ac308a987eb22cd66aab62ce2d50ea10deca600a1091a7c3e842036a60c23775213b75093652b581c7616b900e73c8145ea3696bbbaffbf4a50831a798346bfa8c6415d83927d7b29c05b5c784b61dbe87880abc016c82ccee68f966bc49d91cc821d941ac8b9abc7daf950ede0fb177b7316e73b7b8ca865abfc359dd5d4da11458304afccdb3314d3b5a708e3642cfcc3dc929c6819e7e2662b851a89485c7fa3a3df869dfe1212ed98225f493c07596c68b6b8053bde98bce4d5a236d9d47b4593aae5422f2c0833ad607b6001e6fcbdfeba8004a1cc7aca43e7f72adcfce9dbf0b74200b05efe56de04ef4736481a91ac85917867a096367187a9829124ebc42740adad4ab2c85e481d718e0a565072aced38a77e612788d072dd1b88a0e8ba6d0228db6d64ac589e251809663efb9b6a6aae7e42198686a898f04f7c460d55887bfda557f3beaa3fd9d95dc19c2d56bc901d7e98223d29b64c5bf4581f3dad0a95ac537fe98d00aafae3ccdf113a939e5ce21b5e2985d06ddcf06c92cd09bd56af7d069dffa11d33b0ef971c9b7d14b3e2d6371c68f509b3fb4ef7aadaa49e20ad181e84b7890a1f7c9472194f013b21430887d03155e0adf747c5cc5fbca64f58f9f45a04280e74a5a624d941371f1cdc5b3fe080da8fd59b3e6d734e605d210781b7feb82302f800a8230f9c9a1092617731f48c3a0be162bb5e50c74c472652ced3f76be2aee9b0dfa0c09af07ad0b50664adb9a9595a1333e99d4d64dcc2314f1da1192efff76be724656df819943d6c4184582f667ec8fb5c0a24aed73c9b7d728974acd788586444ee823cb8dc121d1eea39986ab85e63da26d233e4bb66133c659859848720b23ec696a56f952717237be176a116cf538e56bdafee6e0d13e3aa53585f6f71eccba76279bf5575fa8b95e08cb0cc8ec1b53906d4657b07a03e6d2f83ee7c5b0eb8dd1e10130da86fd589eb332b42784c22ba8e2b9be64a1d0fe3609c76f8c6b8e381b6d3532e84d170243ce8ba36c3c69f564de5930216a762aca93045077c2ba3915fbc3f8ac8f30e90a8cfcd2559e18e85be3abc906130bf7e311dfc1381942e1dfd431c2058d30a3f8f340ad93dd6f87cc5e735fceda6dfb76fff3fd011e8a222a5296cca50a560e0cc0787dc96a22f39c1c98e7709cd705e295b4762a36401fe9fa190e1c0c6fe090654f70a4b9329fba88aaef1ccf35c5033b368830fc9d818a624eaadd61cf29beee94e9c0aec0b3885f8438647458526521d795c4e05932c160b0b96cf59a5793aa2f396bbf2be84b872e05ceab9ccc9ed77bcd14983988a398fff5a9d64a9d37ec53121423029c35ddf75e30839997af966ed2f1de84e0d9b954e30511e69570d925462cf32a6eca1d3825759b417e824425cf9965a87d54f43bec725e1a3581520a5b41d114134e01a29cffa07e70737013b79359f41802e650d733b32c61c729b498e2803680c008ae09fb716f62e9c4d571efe25fd7647eef29697cba454ff95637b934cd03a237083e705d28dca89c95b5e10dff2072bbd277f9972c638bba9fa07509bb56e15f640c208c40248e5db701fc85ebf75d43825b5fc33ece15f13af078a8858f105f36e259970bdb4398cda27c0f95742255e59a3792e005355d4398ad35b0502f0af6d3d7683a47e5164dbf900b2617b5ddf26cd30edefbab8d884debc5a7e4824611ddf9d3d89359e484bacc6f6070efa6cc67cb93bf9e5f2dee90461b44a0883ed759b66aef163b7084103301a28a0c55ae1495319544fa805609c94939a9d7b76d8c87e2d087718fa66b196b43deb250010d85ecba55e6e710b80a704a62800fb6c2660c1b8813830d85edf6cef00b84234a99ad69a2c924b1a143e1c6a66cbeae75f5aa290f1e6c665ca13a055cf0d143e1e1383c840e0d2bb5a9a38be1e4498ed6d9d89e4ba16253a483dbb99f131c99592cb3e5399812b18db9d886a402b315591aff084d28708bf84e32b65e8269df0f2b41665ee11a0bbe026392525ef3bd11fbc8e672ba576483331cf04a1c5292bb7e8002739ef54260824f0fa37e49e8602546b3ff2d549d3f5645c1fe4e16ca1f594dd29b8822c0af6a09818d7efaff66bd1173ef0bff6332dd62069ea6111806f0db274fa3a09d520ede6f651fcf4087783013270ab60d7a2a458080fd7b750265a9c8f0525434affa63ef6109fab4e422ef2b62378869df7e77e9335ee562357b51e0a9a698c26119b92a96a98a43c1a884a3a236099946182904947a59d2fc559954e8574933e308d588045307b413f546ffc4fde864956ee69840a49fc7f5c9cf1dc3e57a9998e6ccc7917ad8f243325395de893aa3f4045754d74e5de32f9310b836f17504605b78c8e8ef47aaeaa32bf419cd415183aeede8e3281b98726b6f739084f3bbdf4926e10b3abdc094f27f0b92cacfd0b22f171f59b819ed434710525499e365ca77e3aee8d9a82dce91d176fbf3a55196be3d0f55911e5276266b599e958b9ac9e5050b86a200193a8eb47a7d1f35169fc6c80a0f435cbbbc4dfb005a6f6eb59f27cdfee5806a70c1e1a03ce113959b8282247a9022f469e3238758b52e1446ac138f5673463701170fd898349560003f29139ae1457abe85f9e848bb06559388643c7f80d978dc7bd78803726fced1149e110d4bffb8609ab3daea02e3f47743f15f2ac48795579ab6d46b9c3d2e1b58a8c6cb37f7fc17fd05c9c264a4e52a6e146f10e1d0d62de0e5ad03dd59ad780b0488380368735d5ebcba4d0365b439df6190ef8720dd912a57b121b2714575bcde67901c9035caa655d0abf9e0a9f51e10d239151c928fcc1f32977aa197a767a9d943210d6906009de90d045c7cd26e2241078b67170d618c1906e60b56d5d0c3eb6dcceea993fe9688fe128222baa682f8563f74843539a06f200966675274e26143ede4746f3291e8071b4f6eccf5004c85aa13809e6664afb640dd6af7274221febb9d152df2d8fe319f1e1466e5b572e01c6865b5b1a6a15d995d86664fc35c3a62339e300b1b925e899f00d607399ae832732d10266ed9542296eb20f7580b7a87a3f43053431ef3a48d9785fef9ebf97f9c066c62fdb3e888f7ef81bbb15da3a192717195d077036c56f948efd951c4354c38013ff91c2ac510d9507c44a4d30bd80998daaa158735fee67ec2f0683b89e56552baedd7e9a38b24cb47fb3103fac3d76fded6be38056c095310c204520ee230432665dc5ca1615013947b3a4447a995423447e926f0152b821c8d038ea2d678494929aff9229829b977d20800e42e72262eb0345f01cfbc0cf3768cddcc047d2d0f086cef5fdea4040583aba3c52c39c390477a7b65e2676aed81f378612120d408f48d092bc6366292bba68b9fac3addb40f533af85f9f3561f9c69112f1d016e097f1d274871a7604aae75ac694fb31e7292d0d19596af2985947704b7b480c6b768596971fe609f552bf6da9ccf1468b02d5d006aca4293c11ef484e63853025c2dff70e936661acd674075aac3d0c9c2db2312c44b37775e9b4b6ea03eb24705ab916ea85a72867db63f3f551ed9dfab934ea52253fbd3718f89f1ffba8bed7b31d20d4903dc18344c844336b7d0b263739c6d20ab5732103f7691e4a2af3f14ce91b0046490023aa7bca115efe7b55554c9c6866c77dbb2b1820908a67ab141539cdb9ca68e8496f8b5926448dc371bca2e50fa1db1b0ff25d0297a86547fb7dcb08b4692b3c10f74e29db1ea66fe79eb4a9a11a84e2933f18443c596a82e65be0b452447309a7a952e69612a9bd61db9ed39b2cf411eac912d6853a4e13b3d24f9ebb6bdc074d2fcbeb1bc7b84280b114e50accbb211ae057e3678b0113a2b3c28432af8e9431385d10057d8116fc823a1576a84530ce262238514a6f32b16ecc8d43dcf6ae992ec1c7ae945258ddd48ca1fdf7eee01967fa32facaa9638dae08adb77945ce09943aa82af7b1cea37661b1966abffb9265ed3df062c46600166db8431fdeaeedf087b47e9493dfe7ba3a84f3135ba2174d701b2608c754616617596c74ea030f58aa3a3daf1c937b25c6da44e714bd63623b79a628910de5f1481f2564bb65c1208c66cfe016dbc629ddb8f1d7fe3f6efeb863562a8996428c8ec67affad88d18bdefbd088620b9eeadfa4cc1d130004402b28184ed4f3edf7536a973c351ffa9763c0cc04a5e197086462932a1e27107dadb5bf0f89d2177eab25024131dda4f64d9e8d03816619d4cb8d7c2fbdb560538c1162eb2a3fabdfd18fea1e83201669e0875277c2edea19152dbbc2c598d843dfb3df791863f090957d86d0424f21632bad6c866098a2b5f5fc38660fec55c06ceb5954761393169b88b5b365e77a272d2f118b82068425db581612c951b107580e71232ea0cc81564333b9843d2fef9701d77115130123e377d62fa4af03de56ecd9250ad03e6f870e1d44e2375610d340b01567d27c0be030b7d44ad4d7f094c0c3a9b756390fff1af6f28197d50caf4b0a15aef5234a90a7f49539b666e54e3ddcef5eb5eae06fb9c64ec29e6484689bfae17db91a5ef80046344151c0d8589207d393e881867a15fc1a5f08c2fd59824b214de2620b332530b9e364688f5031fcd7c0a7a75286fbd3c68856deb02ff4bc374c50ee2132a2d024348ad71a15f869911673cb7b3f983c3d7a97e5f873ffbf965bd941baa3934cef45f439f48abb0f131125e5c584522ec57a60e1b3ca58cb34e665e2f6402c8fed64057ccd1528d01dc1f7998c4585b51ec382449cf3da58c6f24bdc74ab849b1db1605c3337e9ea176fabc8974e7168e4084d4048687e6cdbeabd44a9f75dbc4188764425890a10919e39a97910544555aaefca6b804f1f38758253b62466499588948e7020b967fa6b8b8a77b45838dfc33d8be88092f5a38bd652af6a19c86c6f1cdb2edc215d2bd5df21cce91414a4eb38a6bc1c072b64abd49affb6481741ddccaeb8289dad928381ee846f579b7993518ca8cb76dc11cf86d4d4e8f7a04482129dcc2b791aaa427a029a9aa1228582a10aaee15e0f18f3ba41178d22515ac657dd8e4c380ef54e1a6921091f00788b3d685382332c1b0dafabb10cbdb7d11082898284a59a1dcb1e2f41c0978a9470c6a83ad1e9fa2e31e79ac6fcc062c6cd7b590a96f1cdc422f7688ced1e7f487142a6d80b4840039e2aab9a4d886b2ab1064d0aa5b7d95696c479e1881a5d54fc0bbde4d5d9145b2a4c0d4c30660ea0a9422c4bc4fda1a1afee9ad0fab8526aa39ec085fff6eac76baaa29b942bb3d31a6bd595c8a11f302ef973641d404e463121d639b4902ddff92e14f38bddf719923c16f988886070947088c32523152ae405ae88efdf0b865b45308748f9ea7321d5ea1ec82dcfa1c87bfe16515eb86bf1528303b03e110eee5fcb8efa91367bba1f4534ad51ff33b08bd98f69f8544ec7bfef547367c4cddb20a433607722dbe14395058b1d405c9d46c739d0a8a0eda84680ee6ed3537acd4dc5398050558a275d3608b5e242d021b81b0be3cb2b3e988822cdb6d5ecbeab2ce6e9846b4d3138b93127cbc720dbb98f7732c83108c123515c551a1038d19e4cbb1718c90e91d3e2bc507769884d51582e54845dd56951260e0d6abf87a2e1145581e5251f3e97a125bc13e159fe8f2cc7d527bd5832eabe9069c284e24aaed9a55c4db4ec54161a56b0c60916859ff8af7b081b97582e828bd38158dfbf7b2f6cace23c1eb655dbe9ee2fdc7ae2d7270ef3164601f226cc2f1896ee142d1a85473b81f1a3f55a50cbea9689f1f09a2918900e251a6602fca4eb5aa4be28cb53dfddb0dec785113be055b79bad784bf7d5d6cb9d6c02a5746a719c76b2383e0642ffd457e027788ea28648fb2cabfa07621d47ecfb8612c9d6005bdae2cfc583b04fa3d32bda48c246dc915c12e3354f15fa8c33d68988748308917e019abc3eb8a71ce66859e0e2224bfc1f6f16e0a3d8ce2d26c51a356a3889f3bf0493b9e1915413a1e06a16927b81ca7ffe5be2099bac66cf54e3d07fe192108966b9a25c392d75864f6afc2da6a38c6b9d65852b253f3cad6ffa9d690254b9cba537eb97c6e17fc84bacc5bf5130b66e430ce61b87c3b08ab403ddd62715bc52d9bd662d92ae467a0467501c6513659172c28aa130204ee006309d157c3b5d6414eb04a4aad935aa3967776f2a863b359985b2f8c0dabf98fbe7ab4b52d8598020b1cb47c10f474e741079716e7534689299a5a00a3303c4f73d9092ff9b9b80c8315684100c33659756befd7ffeacf0ea39e7666390314239dee6c1890880beb392d603cd38c92400eaa8d4a864ed49372354c032ef8589439e098d5f9fd052b2a944658c900c98d4c0f1ba3070ac2a2d11d966ec81486d6d01a5dc072d578ec78f03dfbd0ec29f0ab774c54edd58e8c7efcc2eb2d63845fd31df72d8dd50bc05fbe0b5375215f5356b47710647126413a69955838ccf1c4c17af5089ca3558dd9c440b1606861cd0a7b45b2a9014f56505c62faae3f8f7574b45d15073e33027769f49904d4b302bec086156592de218e8b6154a2942f46f3240bc8e4b307c84ec01d38e0caeda5827d6da08f0cf7aa9db635482e3bbbd512945dbae78ef4fb99306383b8b3a9b619b2ef07e2eb92a95691cb3eeb3d9f1131f1ee9f3d96040326d41d761aaec89222c284d197ed71125a6468a44c82d29dba61ae390445589c8d46b78c5a8229a90e22dc9a50bfe85730c20d80ab9896adae5fa83250f7f30568186c11e8e2d2a999a13931154dd41e6c3d2dd4331f1c65bc07b9bc2347185e70aacccf987aadd0b4fbc7f68c461d831fa16c43740d5d79f0419c9002d16a19ea205a1c9de0271561214f9d5c2414c9c2ea2201cd020a408c96f35722f462e74b2412c9f034add40a4405a1d570ba7ccd4087c8bba83fbeffa42c5922044258865a1fffcec429d791fe44b83b9e8d4743dfb1814321316e55ef71c00198f49c1c67fee4846304e2f2d3e7eb33d467c7b30482960920f191e8ce51dd3d854e5b32c979602ac1ae38b209db432acb18108bb339151260f627a4d0e04ee7f78963eae76ed232b8f5b7e52ef4a486baf97864318b745dfb5e9262e267160308136bd7ba79765b0d64aec269e0f2cc527389d6a3ae83d6d557ce4e15a39553a22d86ac38039420542e2cb4bf43874fa79378b15a94ec879574a27a83f9f15f3d8aed45c6dec340a0d4ac557120c87dd1e6cc05960416180505318c7428cbfc39c5eaa1c3ce2430c4db1bd29ff7d8e49387b9f5d87a339d9a70e117e8f77164493caa7816f392b67fb20b13bd2dc2aa1217333f9404911b0a64eea2186600ba0c3897fdc2841f5c953df39b1ebb109503e296ed891838e824c4267dab9bda0e27038380964e920f53bcc239794440759a9dfbaa88cba44287777de40fe4674f8c83934e61781d37197ab5857d47da9a107e38013a98ec35b9d8db4a8ea7bfa742e99069d3696457c19d2c8ff0cf6e489438899bd1511459e930e69dbac3f5d365ae012e975106e47cb48461c20efe1b5795a54877306a2c2e5527a81b56c227c58a18850fc63efb24c7750b11cee1a05e4dfd7908ccc72e80ddb45e12b19384f1bad58aaea29636949532ade4e34736f1f09120f965f10f9306d993becb5e1ff994605b351494bee70173e81abefdfdebe66aee780c2db139cedb585645c1d28818b1ba1beb4d5416dd40da58039dc787999d21c439fc196159f3a5b870bd3cf102c1c7a6c8b21b6ff2ef54ddc746aad7f9851bccf7ec23d605f96ed98a1e523757d1304070a2f702b682fbc3499a3cbda0a69af6948237902684afbdfa713cb6e9eb8d1c7721b22bff637d2490d02478a853a7c60ec38414faed1a1a025ae7317c2148eaf0eb24a5472fdff186799648af4a195015b22fb03e7a91ae49212051aeafe58b80f5efc94e5a75060e7d454af4309c1bf7c62597170ae81c8e912ea2afc0ba7d33efa8b88ef992c93723955400d808afe1ebb40a7eb6338c004b420aa1933d480c7b49f143cc4db748a55f28fae13625478150fe3b191b343c28ac616f9df5942f8deebaa54ae1035a9f74444578ef8ac51f59fcaab599053ec6f615256707d0c7da3e273c140d64120a38c41a9628cd7ae22232a460ad3af355f1e5fe00f8012bbe6f026b7374a38c6207db0dec270a4c8c7c25502b7dc651b9bfa1f644d4ba00f89dc7f440d7054ca88c07e6466d4f9cdf0bbb76a5fd16ce415194175d8004e018a8ad978205f35bedd2eec1369e05f60aa4f3c8e7df65fad1aba9bbe1aff94e1778648fa1209be604da98813f4f2fca5a00d84a3536a089ba5e6e6cbd5e8c594dd400f259fc149b35bf3ad377f742d6ffe7dd082dc54f8cb8e00c397ee0ce9572ca272a9f9a22c9116716870c5c7de4ad512c53cd84e5da596bae20dd67db0cc7f199777575de484e6bb497295fdd5d87a640828fa9269c698c16a6774747335a48b549eb3641b051011e3168a713f6ee03fc5d6b4fc6507058a0fc4cdba7418b11b236995d696e24d64e871baec953a1deb23228e9c57a83718f00543df7bef4ad5d217b29840d4ac338dd39fd19992773de85d9b02823a941bbfcd94115c6d03f2d987eb680abc2414960acb2d5d857b999b037b34b37685a1e6b64ce7d71657b12d8df74276ee74a888e7697389e7a45ba5e45b94585f469652d5ab8fc323080e46f25eab1822fde31b6188d6bc35be0ad8bf6b679d756a694fbad213bb0c3b39c449dbebe9f397860f5d929b1569fd09844cef26c135fb9ea584305015f1e7b1096739fd6251354e9aa836030003c002b6b9a698a7203918500fa4d22e70da02f603ce2fd37ed813e024018bea86ff09a34258c47e15503e82170ab410bac775fcbb17722bebb11f972df721acf26be6f7999bb8fbfba737362e77d261906f3238df7fb826b516576a422637862020e54e2f18efd04c055885ebda499bbf2871aa8917acbc9dc5a1e709bf18bdfb43d04a5f912f69b7a70cb4eba131923114d6641c299605bb366951b3896759ad3c276935f336c03eaf7c5baff285325bd56a331391b8a7517e19aa77711fc3127d7ab7935ebbfb64cef6fffb4e70258cc016678c3192fe5d59d726c02603b0ddf519ab10e1998060006498898edf9637a83f5cc646e3888ea2dbfcc3ea18ce9b11985561bdac2926e00df11382f37a6e92fd284c85b12a60c5c189ef324e8d54846845928a61113e40f40804757f909cfd8d8ee78318ee104d934150cf3fc9fcc02991aaf1d3ff18ef76857eaad08736434a9055b3f86e965877c6ccdacd3c03f3bf1f74dfd1d8ae01de6b0501ae9b604595fd01ce35f5e16790072ad84b38584a4e5b19ce6ce31d6f6017e93fd0ec6d79cb8967ca9207033e40fe4fa36539bb28e5ef2edf62090ddd3d9d355851bc8c2fd2fc7618aeaefb6df5b64479caf4fb088bcef49c466eba2029452594c9bf603203131817568c32e3fb420c2712d858d0de8e3c1bf6a83064ed6493164bcaab5c0e5b3caa515d4fb69f48722aa39ab4dfc6938513edde32a1796ea1b09a7bb149b0db9f3c6d4424031ed50fe9f475ff3bae99cc819e6835a1d61388d01e70b97ed8c98425e32620c0e643bf35e27d0b82125b2f702320334ca0ea4b46c8bc0c6d3392f781cc763801161e4372c211fedede5e6d07689521487f4e90ce3f3c86979a509d93490fc8d48d8eed900924975a0a00f4ffd7b169b23b285261e0e021518436d52b4804a17dc67ed268d4681140fa3321e6964d0e5c08e1193bd2f5404b3b2f620395cae3114e64ee0017cdbfc94edab4bd3f8f561ec2cbdfb0bfd54cc7f8bad577ed5563bba082d9850acd97678facc4b6923c390eea66a4c4a9f1d4fd696dd5478ea04968a31c49cfcf9599ecc8c07fb2157a2d0ac8fcb2e087baef0fa4c84b0983aba38fae219af7673bb1f7ae67c4a7245950536c8bfbfb31a0ebd171080b3c1008d6511c1c39c2ce366fd704b10787efc86174519e0f7d91de470304b7ec89dc25ee892796e3eb8090cf24e81397852e2b543bd5440ff06e6ad6859c9d83ec882c3b5f3d99b16454143141e86f17456a1302287f0f440b79f5219aac451d58edb30d2bbe4a556a12fa1446e42a7a3331425b07f0c93be6045ff40aba453dee18e96b414360f803409e2d5c7c48c9562a2c5736a32515e79f9dce0175db18d891bfb7eb07cba8e288045dde202924f1c4002b8cba772648ea4c508b236ce7ea1d60eb8d9126437571220c347e667f97d4aaa24dd2c9a4bf0039ee9c11c175a214a6e16f690dc45ae0b4c97b9638b40ce281969f930a0c58483a0494f9925492d367c117867503e1455467ae94de18f1a122bcf45f45bd5525d9548622e6df3617dc975261068e6e9d097d8e0d0fde23947c6570461209160b25f5d6de721a60c061a255fc8b30e5d6539e54536c7b12d73edf5e0fabea6287248c357bdc6660db66a511508a26cc768698ad9cc306090801e7a76467efee463cc6b4a52821e3e3c979b0c24537d5886faa79945eb20848c67b63be7771d4546180393e14911141921ab43867510498e1f140923a118836baf30c77c93693e3404ef8a8c99dc61e267986559e13cc559633ce843f28f404e1e095cb817364386c8e7ed0553e0b6503b0a7a88da9f8e6ec1dae898ba845c03b70d4cabcc0bd58517458cb06806f3c35cecff9e7fd67b2c830bbd56b011561541a4cb6fcf7d3bb69a94cfa51313ee43d86a366bdcd31bd564de6631a7a3f92a33595b81986a8e0b77ddc781392e6a1a860078c4519d8f1098c8c6a8bed79a307c497043e057c591ef8fe012da87d81f17824f3f03f2dabf72ca0e9227f37cb6c2b276bbc37604db3f0561dde2f9e909a07be2291eb75d8cb91a1795a281a6c6fd01560a9d1f1959c95d237521d80b75364001d8977a37bf41afe688fae9335e4a20a2e7de490af4bdf76f6cfe674afd4e6595077594b9f250269c0d80c5560ada9347cf96db538c59d6ef01cdb3d8e8a6995df7775c121bd7f025797998769afbb040d2929d26a380c9dba64dd8679776e601bbd2520f50108006a6ac294a309117465b70f03c6a174398234ae68fdfda05ee66c1d770dff9aeb70c29e936918003c469c20240aa72e2417f9e182f332e1a990f6e2c7cbf1ebd4251d24e425b0500918418a2607fbd4da2ea512054cdaefa05ac91f175b03334a41af897c95ee8321c1e53a1ade80ec9890f0edd0021ce4999f05e30cf5303027f0a98f8a703d8234f62aa86009430fbe66083a11a57d474988e0cca855fda965ab96e7949524444392efbd2307b058d44d4ba4af8bdaf4eed3f674b198d4251b794d5826351fb37675da99cbb572b300a7f480a253d47245181702f68299b0da09f2d6e8d74d07a8eff7cc1ecd824d62c80222dac2edbca97aa723eaa43088e238ae711d5c7ecf631fcebb3608234e13326f815d8b606d17b92de241d3320b27e58fbe99020e4f7567f4ed91144b720bc55fd77123bdd02c98908abc38b460e162bd7a5c565068465299b943441c63e7174ee6003bdd5b1d8ec7814d7bfc8ad5239a78f545438dc286b39e572c1562b508af4f6394cb4e6d581080c55f156bb1efae078e022e0bc273c2fa5a764e4aa46a74b0ddb3417feb07034a9cd038e4bc2611723d42d83963b5a94b17b0965d21a6167e0358e2a1da53f2d1ff2f613d2f9a44d5cd67a1fe118aa7180d4094c167da29c61e405823d71ca1b0d6e9fba6bd94b0b5dc5c3ca46089a4340e4cfc0bdff2945a05ebccf02150a65c2ec448efb744ea04c7e96534076fd5d419dd695160a25c0a5c3398e2f6dc37be7a3d0f89cfb0c500046e01685e689cb6c5684e631a4c85da5736e9708e90118b072268c09e16797a588d1befd008a360a7adca5b1fc2e8aa92a3f7beeb8962d5abe6cfff001d0b6ec26cefbde0320c8d46e6fc834849642f8278d6f843bfdbf92b6f4406ca79d95295031dc37fe8033fdcab45ffbdeccd349ebdb05290271ecfcd090e55162b767e7bc9c71bfe9a8ced55d2f6f36c816aeebb16013c655c8dd0c2ace9d204a5e9dad247172e0b3e0415025d88baca29204bdcd99af3aea5895cc4d0b285bda13de8b2fdc6a059ed917d5478a7a51268561a4d592090a54144bacd2be30ff2539a3be85c8d3d966282b069abd48ecb734ae885a3d39b8e878df0992b322ec23313e0699c36f16ef0da5436bbf8d6bec2742eb1f7ad304d501ad98ce946d3757eb7fe655c612d880c3c8579e5659a11e4530c43cbef56aa383f9509b3e7f8752d722227ebdeb3fcc68795a704a0ea568eaca8082dc15bfcc3a2e10376ec93ae0b3bc901132d6a14ce89d22dd70f8610dbe8fe485310ec93e1cd9a6452c9337163df137c25b892344c44ac664c4acc396170e4e252542d26684d00cb680b92d4c18c202c685dacd2de993ecbd97bee82350485867b8c77975329a14e38193c80a467f93e857a8d30fa7902205de4a051ca5ff14c0d92954a048a8c2ae56523ff7426e5f79b17433efac78736b6795644163185e6d5c6abd358dce274fbd19cb3c73b0bee225b4aa255497d434873f8ccc8ac5182783fce2cb62483fe3c0c8e78d0e70d6ec3ca7fcfb00b97d72d0519cc70eb61425828a55ff8e17e8f78173ffbc50d2d8eef571fb08835d4622a567e390ac5fd9abbdac922cfc3f5604d3ca85281a55c582636c8c08e283af89eae5a3e38874dfc4a80fbdeb682a59b4c48d8838a1728b24426786eb7654f1328e5710d154ee1bf5705a58da9f001832440ff78d1b84af11a03beb5f44a7536b0b06d8c8fc03c56440a517d550fc6a0266f0e3910902d00b9c4a6fffbf1d40eb3569218f3616c40d2d3e2c4844a1ea6726f7cf1728c5c87c0d84c46a931c0ba378f11067a32fc32d9dfe8a1770d51e0e6db67c380cda9da1e4be4d459e17949c1b944282ae7742aa53588ef94451188596067276aa7bc5b4314792af41f7d1b8fa7f01321c1743a19b43675f0e44067fbaeb92ac60c9b6482e562d0217222a356a7e8b3f3dd7f72ccbb948f7fdad01e0e2454804b67868c43b12355ace82ff5b9de386b0a9e157049ce2e4bd2e33dfcd8b4760dca79880a85f597a064815b18cce06952156cf104222f82f0c1d5aed7dc3ea50092c4c1a3d49d0bfa228a837405d56b11931852f6845fb6e5b41330a9a522c4dc96988cdc25cc159d77964e74ffffaaf3029379ec1bb046298f1d576e17c2463ce4ee500dcc3a9b3a46724d80b0e76324ecdf74bdefbc04057392e8b0134707ad9f3cf78a888f5c11b70765dc27ce70f108b8f2c5a3d33c77d404de66a525276eb10cdc62a85d0a0b6d98a94a4f2b98ee6ff176138b287a8e2f242b4451ab243c528dab4419a58f9982debb0fe168e598c9c92d64ca3eebd8f82361a4d966f65f23321c80f44411b29396b9fcc7594e6e906636e93fc3c5034ac98b2791b3c4952287738c12944c7235b638a30de22f29d84e5c4238b02be2ac4029aae482b8c916302b4218d38c496cf51d95eccdb6510c777af40f57081eaf5719fb57689f63f1f39b28e754aa289d5fb5900c23dd3b054d3ecbc2b73e0569133b11b15258f844d0270f40f826a0a269a384c5274e4476b7a9a82e472f751129e95b6d155d2cc0441e7c63a57a63dc23dabcffa999c6c84ecc7fc2d39f737c999f57d1f7cc32c548008a49e4641adb27aa4ed3105c0864b1d8e18d57cfd71da9902334330394ece014ba082f6e219ce85506c11d6d96c5edba9a7cd9f90c350d998f42fd64fa47eef0d911892bc649547ccd5cab8001cbf2442238b25936d6932c081b5a717004809d048024dadd99f83262944eb5ad9371260d1bfb9c8a357584882a403b7adea8e67987b2a618e80186d983d8e78156fedf1f6e61bd29717e2659b7711af37690f9b5927e94ba076d5d9d2cf32e90b5578900bd91f55634a11601c546636ec239df463159751a49824b931be9cb8b66db701de9e39e6bdce1fda54f406a84230a13742678e6ecd0db732840c88d1a6717e6e7270707a1685254134b6f5b2955310db69e25bcf8de0cd42fcda21c2b5b163b2e07e1b992c9064e1496a71f79b3c335c2cb255f25f47f63c4bd8861ca8496c24d19e2bb2b2e3e5393789a24fb69a45bdd0bffbd083e76bc21d1935790ab72dab981ddb28fe8b311f6819efd1c42ac9c8932eba3fd40237657f4174ca414c9832d76a25e96a4d6bab5c5eae8a46d34723b06bd7d6def406a05ed56b05e2748dadda49b5dafac7219089a2a42436eab316ab6564bac3f34a080bb8b36b0458e262916e18e2d274f4648d18935b006f510f895e5c7cb4afa52fcdbcc18bc0e7297dfca65eaa4d1ef5e867992ec77e61fc98e104a825cec032ca60105b55acf8636020377105a6010399ee07c47de8c9593946c8c7c7afd5b37cfd21e2aa90c27c4210d148328df7cc2317a2efcd7fdbe2e86d29c924c6f38bfef37ea70a719c844b1b2732c3799f39896759fc130a58e921f93b5067579512669e4b6acf07046db011327299636ae9c9bec67367a29ce4eebd1c8f8610214899d8f6b2658e75e7372bc163f6d40f30e833a116008c87f6d6fe81b600c75184170d42b2a9d7b77000079a6dbe4b2645818e09981a16123702607d4fec20033b10d3cf1beae3c3cb0613c73572a3db5bdf245a526d1e8384d8e22039d044b0432759a710a3da43c128b1d96af33f919525209fb9a73eaf7ccb743038002664510866bb4edd118f0c6296ccdf0b582c28331ae53611dd7e8b57731d5cc544b9799fe5a60e39872688e2eb8f70e9c283116ada9245fafbdd78bb15683c01b6d89939e42d41ff06548d758f509b0002f867a0276f722371b1ba422e870870997978ee5ca7283bba862c89161f4b89e765191af0b80f9fcfe8acbbb9488304aeff5a23627439aaeeae9e216d785e9d1621b8b13c6d35f38aecf2b29041dba3dce6dd4929fdee82897ddd5932dbc8e4d2b260a762db08e5514e1e7e1830813ca334fd7b52b9d12c4307024579cd82dd728ed7698fac3584877dacfe831c2e4cc88f4d8c3de5d5ff5fd7636f1fa3827b8190cdb225cbd00fb0b70a0fe795572a54d866a1ad0c85760c3a09d8d007b12ba79eea1b5cdeabba65bea1f4171e802edafcadf50c50d111bbff9fa0657e7a0f4be2c7e8472897604b7b97f33266f6fa978730edaaf90c3de69548045ec34d2c43483ef520b6632d495fe4d433ef5de896a937538b49a2ff1bbc0c2215797e416fdedfb4abef9ea32ed76dd2a9449d4b8541fabbadff3e880df775bda619e7fb4e6a3c55571a902275f99730e349e61bf25972ec96b2dd7ae736f1c1c4eadfdf38bff2a6add7af26f1e53647f7179d0392163b03a43fa3746f6f75764243e48d210292c2a2aa9bf27f5795325785975ec75ede631bdf30dbe3c05906ce4a773ecf5f671e94e57f8daedbd31fdd9b2c407648e30418334ba5607ae170f050036642ab4aac3b89a16df5179404f8f379096ef7a6398ed6f6d3b597d14d044255c968258b16f4538b16a410583df27cbce976bbc8d126ae41de0263a8ed685f83392229f6978b8d94b69fc6cb9dc3873e3f37524f4f30cefc636d15f255990780fb1637b2f561d7ddcc575da42fa4d436b8c4d6b1a6065ea4aae5cca713096efa618444550aa7cb2404847a7fd054f69a2eebfdc27421bccf3d6d88a30655793e5c8265e5dfe00f97eb7f9cf1446abb1b3778941c185d62cff1c2cab1c32e7a6e2a2760a0552db6a605d41f3220eb13efbf0c7d023d376546eb3a6c2c292aa4f9d37c84a1493a839a36a085873716ee3117d81d86dd855d4ccb8fb4a0e3ff029508b0dc76eefe4ba886da2600248c9a2120483cd8c1452d3293209abf8aa8148cdc46443a37cbc28c48eddeaff6a481b00cd468e1508bcf5b2dce74084a5ff9886ef94411a47fa3314b002e6d525d5bd47a6fdeb715645a2d7d0e8ab444c05600f3e025f41db49d2dfdc290eb9295c4bd9bda6cc188d9b29c87709a86b1c6d59be275b7b30cbcff41f70c086163580f35b7bf6244978f598c1e309dad6efc6ace847625e3cea036f432b50c3265de7f2ffd70b7221dea789513d174233a3920d1a714ecd2aff4e9501025afe5be7709656fac797d13fa70257b2c86b9f4a8d5f70cf4e91e419f2da13d425cdc59d90b6f17bcf5919279854643ac61703518c17cb0725149d21ec1d59180adcf3b151c2506abff11e02e00ac5cc6b41347f275ceabcd2c3369b6e52112f7c3cf2f81d61c0c1621468e27bd2b1eec3569d917e905820a2d7ed65432ac98600d29affb25b7a6565329b55f1e47f68112a02dd58628bbfc6f04082266de262a984184f392a87ae985e441fe7974b609d6aef42ec3b1792b90467a9c3aa239a10051eac3936a2aaa5305fc9c65aaaf0a5c85f6348e2a880ae0a2c27f5c76051f06ff154bbcc0010cea76c01f4b91080eb52e1ab715f2e5b099de4f07d05f6146bdbaad1d13dc617a20916e3a5204585f18a3f7bd4226a71952c69bbffce8a58e9aa8ecee4657a55a853940384f6c3540a4d838c49262ba9b202c54f10b272c68554ab2097a6b23f9eb1744ea090b7937c65853f15bc81863887069f3cb468dcc7fd11727815b1ba94ee7055006bd81741c7301cb4740ee110623857d0f29945bb5a92270d53bcf03a011093d8ada9a711daa84e953b7d6b43d4458127a16f671483e7cedd398682e15c225c711acc6244eed45f61720822c626c12a6ab549542f761333a644b7c67d086cd7432a443c6c0a57174d961037ff4945c0bf6509b878db5280b93e052346c70814e951a28183b7f48ec3e728f1bc315f9c12eb4c4a944b92c5640a1e583b6a94220a55fc2952df86e293124ce7e69ac2472fc26951341c30e70884e78502581d03f5e30c28dbd688090a02e7a2a75ea710cd107ffb0e08a13719af0bb205e7958f81c4e53e855c8a0c49989b035f8c48ea495ccf8b0d70c1202ccb165cf2146bbec9a25ed0102028288ba6002dd3086158d62fa8752ae1496a6df86ad072d2e364669f5f52c64fe46d252242e74611e2e7a73c137a6cf94a42a1a9183490b97a75e47348148177063d4422e036b3be7a273a38d75e94aab8813cad7427b025f0728d9606f3a1a512db2520c5b9c06036c5628671722c86951c1f262fe588b950d937299125b052b255de8de2d8f49c88ec5c621033d058141d1c06edfb19c9437820144ec4bc6b2ea3e1f8bb5fb7ad344c55d0838aa5c6a819b41dfe84bd324ea31429fa8ab5f820f491aab445e808b0c17cb3c8f415e80e088cef796a21a6f572e635916cccccd9ee9429223a0c419e1d336aa7729e63b072bbd875590ca1ea7873eaad6e21365e78e35f9be4c0670bb1a80a6836a0b07797a99cad0f04e70c3b1c2e50c7cd16aba7ab24e6e628f30e6a500c0b59fe03181a57d6145cbfe03284a3ee108e28550992a5ae79b9c385eb13cb75c6a0ef181294fae967147ec4223b3ab032a3cd5d378a878372322681bc203c7b438c70d67e14b8a23ccdce4f2df1f5b9a6d8ef257bef4490be96aaa0fa9bef8c7cbca1620617100c3b20586b363aad17e8bed17ad892a35d1e3020e2b328c7c6be79cb15fc8ab2dfa3a40f238ec8209d169c64578176533420ac84720be9e3fbee8fe801661b80558680122877fdf151dcaf2b96c5c4ca3112600d4d4164b5dba6ca8ac30c0e1141c9a52a51c510d35cc30cdfd0eede32db8dfca6b1999016c83081f2489034db5ad46db7fa53a19a0dc9ec35f00fb4553a0df265b6366c7dc17f932ebe9d5212aeba71776afdb6a3ca0044e7630f32d9a174dd2e0e6238a148d5e0b949dea1fcb4d551f78d345eda94a7c61f21c90869ba02a2addbcfa25d4b461baa310e9abd023350dc918d41b26dd79d896a3670a599dd275a31153cbfe9a91a70b0d0d70cea5b18b6a60fff9b13088d67678b93687212cc294368a902d11295f5791d09789178866fa0eccc993098beee9be432a9dc689458470e80f955a2bc6f3d8e152577fc67082bce1fdf171d19c14379f933cd7f20bf8b86807e9b268b1a978358af0fb31b16bafc285338ea4860c04e8a5635873c4e46d34555c730eef4ef07aee4c5e3b226dd6b5d4e53bc9c810facc0786f410da0f6afca7116d06695783a037b3a169fa846f03fcce22a159c3579b4164e6b7b2c562b388be057ac1b0ddd8ee04ccc085662a6742aee70469349cc0551e2ee85a2ad42c6b1c3e408c409c688909b3307c5f3ae4f232308cbf21d4076e7cd82056962c582ebb99ed619d2ceca3e35e2503916f0b64154907cb7c074b8225848c46a612506fe7c6c4a9d40d81d646235590f53df1aa52282a1ec44b55b1aa29aacc4894c836e41d4d71e78adffa36c4eaa0e45b58f31072cdc3238073a30af3d2e40d16cb4bfa1b02079ca814902f35a769ac80e7d72b348e91bcd4afbc73c448fcc38e24dcb35799bc931a3fd9aca1cbbcc2e803e13721826ba4835dd5899cdbc256ca3a99dee67dce936cf5bb2581d3418ab8687c86ec9eac929b5bba8dd1c4b072e44195063b3e33b963a17663b1be8c66c1b795b29d29a4479c4756498dc144bbdd4d136920e27b0a49db0575bda145d3d93d8339d614bb15c42842637cceab13b41de0d8db1d289c7fadfcc43ce181310b9426db3c5949303adbf65a742ae46464266da2dcf1197ab870c53eaef6a3513ea6dc37ce469785d73cf1e62d0cfc8f9d2a6bd7ca3307672c717f278f799d74b41f8cc938d4882a7e75aa8d76ea33c3463e74338553e1219a3b7b4c08b29153616ab81affaa46e9c4e33edbbaf54d24db5912183e4a181ae8a0a82a40a1529b068c2c8044f4b2b302094170034c01b198aa52e2adbc441c00022f84c690145ac193874a15ad8a8d0834ce6f2c09fc8c11ecd0d635adc220831094b043bc896288a2cce93a85953e9108b8212ae5ab06d57b408d4a0202b074ea69277b7d4f01a1849cadd681ae991c28a7f84b938c1744c5710a6b468e0aa480d9f10b4f857f388f15527cca1ec709b6fe6a8dd17daf613dcce6e1620f3c0ca31bf4309bd89544362a5e4000b81dd8adc58564aee831f61a3f1fb60130d3203529cca91f08142cf1306e8b7a9709174e1c72727ce145e087762d5ea07c73e059543ba780f287465b938b9491a445681aa4a27a49213763fd13e1fa8e41c4e5deb7c1860261d84f9f2b8fd27abf70e65b9320414ddb6f783b13a2bd7f39d61cc047f60fcd01cd36b72436e6a021aea4e1166929f2e1fce730e6cf7d74f8affccc54db3ae143d9a459723e7f8e0d5d801845b1124c835d09f93ff0ed1eb4967e37154fbc74231b6cd10aa37856aa886d93423eafafa196f6d1d075ba35265b61fa486d718447dc81e93357109a6201fca01fb43b36c98038493c274e4c2dfc0c043a7e17b057305e514d07a95302531d24bca04b7a30247993401b5b02b071aa1860f1662007a3ea59c5f58fc1ad825bcfad908700169158a6f3738ac5f9e638f2c8f8fb00e2aa5631cbb664e2e790616351ecb6f7de7b4bb9a54c49cace0a710bf70a4e5d3393457dee8bab688beacc15ff1c7c08077128859ac935c4d1748348434411a9fc992c1f1afa792cb3d8f37866b1c7c787aa100afa69429e3f3a195050788062404040b1ec79060ec633cfc0c98902f672dd4014458c3b3feedc59978fcbe704cb1ce304cf1c83c650193ac48612a1345404ffa26de9d8966dd9964e965946100c3cb38c20d9156643598811439452a5f3c9ccd2cc66334b63c232bf78bdf0970fe1c787c78b7072e245624431221758e6930b3cf3c989788db7c05d9ee337fe82172fc4d9d9a4b97c3497e6d25c3e252c738bd9cc2d664566701a97711834984c445a4b476be964cf263e339bf8c4ac1892013da1450bb127cfcea59946339bcd6626e99cd6691cf0ec7491d641a9d43831a2582c46949d44c23dd34edc744db79a042424e208ee1c56974f75555775f9c8660665425a802922e242188ab5a5535bb5555b3ad9473ba3b93361538a093463b3671e0154e9eccd2acd6c36ab34dcacb1993028a2833363e079628c28168b111515cd9734ccd69c99316be03871d6b9a32e1f97cbe593fd6d4cfe5c41241414857bc596abee8b539dc992a6a6366c78cfe4214f2bb24895d999f35e4eee8ba7b1bc7a50f725bf804f08fb61b2f77d416965bf339ee78f160ba321a14b8b057b6f84affb62816a9eaafbd229961c8044cd97ae22804c01d5f72fbb5c429ef54199eb8d3c7f34e4257b7d25b6972c6708c1c09c29184327512cedc9f3b907b2fdf8813cca1fe05f28487eacff7ddff7f96f227d014e219412c8cc20163dc5092153c62a823eb6079fcbf5c19ba54c8da008c471f4fc12205d6a1219ac3f1a89e3fd72f86862ba841fe6f031223231f249087119f94efb80a8df55ed4f231f193c8d441ea83c024520db8fdf8ff81811994842892c8a600531a0610d6cc8c265e4870871191187903efc4420dbd7e7fcabc8f4df95982e4cc82c5c48c418172e23a213233f448921425cc2919191919191916fe286cbc82b3122c2c0c0c4dc70093f06e5123e4c8cc925fc98eb123e0c4c13d78589d1ece5c5e4c2842d7a7971091fa609940b13de861797f09bb8e1128aa0144257a1c4400a56c4e040e68927b8f49b5c4592c1c214402f90620b3b2e426c60980033039ea22cf4b85c1610e10926c00210b070c2a51f6c0e0ddf13da8d56993c65bf8150e4a3ba65764b07e129fb752befe5739335aa92253d4abb5dd6efc71b93edcb0d1c3d90511e7d3fa1857ea29fe827fa09999be616799dc6c9d61219bdef463b3c71dcc1fd489cdc37f795fbca7dfd6ef33c2c3b10cfb1bec6fa1aeb6bacafa0fb01d1b9a75f3de4aafa6aa16ee9bf3d0b5f2658bfbcbec67ed9ad5ffdda5eafeeeeeeeeeeeeeefe72747d7df763ddd9d122f71fd08ee987e5a651f64f55e481ca2e3acf7f23ed67addcd7ca3df73d188590fbae31e530c542ae64d14881b235814dc621cb294a30cbdd6844e2cdd06d1a95cadbf7f6bdbdcd1e96198c7dafdd8c1e457b7b4d6f3426555e72d55529680daa6fc1d61ef4eeb9eeb9eeb9ee4720f8ddf7fda984f4956137343c8a43e9f555fd1d0890519eaf7d44556c91055a11fed8de9b9e78bf1ddbe8678f061dd5fdf8a61b72f5e5e2c8fd145bf4be11ea0f6494fd351491a731c9841acc8429ca16a6892a4fb6d527db4dd3344dd3344dd3344dd334edc32f87863dd4b723952b4ca3402cc2fedb281eae5a4fb9d2cfed849d6a9384b93bc77de00ef0d32af813067cffee477fefc719b3c3c571147ef79d0d71fd0ed30763e1585fda1141420e7b238c4498e97715c6bdff58a9c8af6ef11edd73b5c2481f8fee394f741960a78154b137587846dffd380abf1d23118999c3efa210b6d7c8d527ca40aea44db750dfe91658b73c8a6903a1c0715fa7cef401c74d1d6eea705387738efbb6f136eaf618899e88c4cc9f38721f36ca723f3cf147f841b83f82d29f4eac30509052586ed05957a36bf97957c3fbb0cd1da6318a059502109d15bd34ca064189625428dbefb65a89b2ad54f0d4a1b17facb36c458a83972b17529e86094658525fb21e41a2bef059304d217cc2589790edf64dfd025235656a7bfb44c815f8db2681b61f47b61f37bb214133f840467e7b80df89a40f88203383a227a3f585a5c4a398fe98781d508c06d1188d8d344677c02bc2edc74963f9e69f46d15811f8f67df6612dd39a6e41319dcc284c8a7d6d6be1515c774c1e872b511d6a94505759854915fb1eae544815fb235c953057786a0f46e2e64fac3b41d87fb931e14cb65a4bab417935aa47ae6cb65f63ba65f3fca1a9be3c65bf9f4ec9a2b12250f209099ac30732cae16f3faaaffa92ab9bedd71db9d2b20f0599a8105ee15664fb63ad225b2bea14d9befbc8b10a119984c2a0d17f86b27dfb721a33bd3c501c6bee8f65fb83fb0e258822667462b3a866a76532393e3e232257af1c62efebd7e3fbcdc507840bafcb20cee1bfe1cae10ae3783a653f88e5c7a7dfdbdea4ee4815fb14d717c804f095eddf46e9f8c17df7db8c7d9e78dafe139d27db8782e42d274241a252a57f4412512c37e7e57af5bcf88068f107f84166e6fe13a713e1d8309d78dadef31ad18a5ed328ebde2d8527a6ef1f51f6bdc6552c9dea57e8353535d97e97f2c0fae19abd99797caecf6193abea4ea7ecf3bc0fea27db1a94ada90ae51ffd3b9b1836cabedd9ee6ed8a52eb9a47083adadf2f13a87bca86e88469145d42aec8d56442aa50205e409f700cf324c2fddeb069cbb54f08e988d728edbba5fe0d7bd6d09d2ce7497b4f61029ddc1ac01b6814edf952c54944a951fe531b3135ca8846cd7fa9d38601ec6902b88526c8c03080605802ca4867b2fd6ec61c71f2a7b73f65e64cb7bc78fb93a65b5cbcfdd9aae996166f7fdacc9b6e29bdfd89d32da4b7246f7fea4cd72c325f7367c226cfec993e33367fa6fd299b42adea184fd99f9944ada25c0393adc97726dfddef7beff777765a713f4663184f37acfd87b79313ee18bf378c798163986053d7b0b6dcdb7e0165dbae58cfcd3e93adedfef4630765fb9e099ef9e4f4dbd7c3e4bbbf1f0f93ef4cbe3331f1bc18d8755c4560600a13d329fbb3d3804cc9d595565a698f98f9844ddcb1cc277cb3bfb0efc2bec9be89fd16f64bec97ec93ec9360f7f118f61fec4020f620fb9b7dcd7eb545f4a551f665600ad329fb1ea63258c7cceea2e3bcecb88dd7f84d4ab67f1f4773f71ba2ff727faf38de7bb98ddbaee3d807b3b0383cf6fed855e4d764bfe571afb596ce987a7485e9ddf4ae7d6bdfda771c8d39c2ffe4f42ebe7e1331f9dae26bc9d7d257d25792af235fbfebe8ebf74eb7805fbf61cdd33dedd3b16eb15fbf7fbae57efd066a958cafdf329f216aefa25c6b72addf759a16e33718f63bccbd87bbffbe90048f9430e94b70e95be092373179aac44bd6fe047b8cdf1bc6e4138ef12ff0e95de0176fc22edeb19421e341278432adba9eaa6f536ba53ef48a5c1fb422f43fa9bfc9306120469cfcf7f530bde9dd954def3c4cdbf631b0e3e4e8c0c05e445f62e44a3b62e613d36c77d0ff045fed84fd05f69c46d577815da751f54dd85d8daa6f82bd48a3eab7c0fe6a54fd12ec3b8daa5fc20e6b547d12769e46d527c1ded3a8fa23d87d3c86fdc7813cc865d8857cc8897cd6a8fa32b01775aafe8629cca889d0699050b7c87ef2e8396b9f8a82085769a0bb6755e96f08b7c17e934538d29818b799a9f489fc2d436744da33a6d6d9cab52b8fee39e79cd6ceeea7432f9a6407ddec1667c83243f9d3c76f4665d920952a9f1512374aa3e4cf30a551d24696de9352f0b4638afe844e4bcd8f7e4737ee4647a5489b45fa8dec8d9aef415a4522475ecaf0a51ca59060964ed1bf382dc59336c4a9089b351109cf35c7484c17bacaddbd8a2cdfadb556ca1d46c993e5dbef3a71342273db468dc8e366e94b991a3fd02f656a0b2ba5946a9aa659777777774d1c425ffbeeee1e7a1893c8e3f69c943c6caf6129771885e4717317a5dc61fbf9e998d2c579cccce3666efb24b41fa2844a11b4108511c460c50c622e550921504421046ab8c10b6cb084135cdcddc5219a88030ee5a2d75a6badf59bb8e1527f4812f79d307119a28466adb5d6da6fe2868bfd2149703190810f5aa082222ca1c625892b2a2163376c18e21374451b70e0127a6ade7befbdf75a3a247c4dd3344dd3b42a0e919d12afa7a6bbbbbb7f13375c5c1c220a71256c9770d075b3e50441cf9e73a2a04c2784f47faef84b1865f956105259f1d8b9bf4985e4fa67e864ee4639240aca444995295572cccea31c234308fb1b25e99cb3a79d7eff4891982dee900aa86f61d9be5661ae7c5fabdf7f59585872274f3c503925df8ca34772100299b96d00a139258f2cef43e98e539ab57754a629fa3796e2ee2c637e40b7f4882b6c54a65efdb19ff6475bfca1fdfd8a47ede95ffbed00329198d9ce1f736a3ea3d94eab66efc675de078ec2111252a9a48589c9c58bd32b5694ed7fee60788efae677313c6d047e2f5f3fef63923cad968499466bc3619e72f99dc382463d73a5beecc689a92455ea7338942a5f32ea99ac11b9529f251cf558999559a156ad60ed29780358034481a907411a20ca839d2c4606e207bd1870c6831d0c068381e2835d5010087637373737208c07bb9d1d50c6835d8ca76a0c0882311eec6c6c409093c96432d0c5831d0c089e1ee46030180c7cf12017140482dccd4dce8d0bfcb6e17676409307b9181004c1160f72363620b8c964b22110e4604090f4e00683c16020c9835bd05ca94120b8dddc80db0db8dd80dbef80db0eb8c580200882cf01c9da6cec836f4411723582c160e0fb68d58579aa0681bf43b26ed0cdcd0df829adba37a03fb8033e0dc9ba3b1fca8e016f0c084ec9ba36207d100453a81a37d04011b5108e70e7c578339d0c7732991793ebc798018a1dac0b023b18ee60b0a05c1fc67f30ba9b6e07ec6e607437373bb9fe890c2f4617d3d9805d0c8c2ea68bb1c9f54f279d0b4ed6c1809ccc0527937530b9fe8b13f782837141200773c1c16041b9be0bd356c2dd703b207753c2dddcece4fa2693db828be16c402ea6848be1626c727d93921dd9641c0cb8c94636998c83c9f55b9034920db605811b6c6483c18272fd92b06edbcdb6e3e5984ce176b3ede4fa25ab852d66b3d962b6982d66b3c9f5bb11067f7ae204419a5c9de6c28260305850ae3f72ecef4df166e7e6e666a77357729b1de6aad0ad6caed40f430f6f0762cfe5e9606fc6551e112bc3c9d5c67a21d70783c2f1ca6457a8553a7cfd6e8667d32a2bc321071a5fca7f1726f5a1fe7bfad22a3b03e586afc67f36267e3fadb2ad56504be8fbaccef7ae5669b3d96b06fb3e4bf37def34add262312712b3f93e8de83b196a95e672cd5cfef27d9acff77df3b44afb5a3fdfa7e97cdf3b7265faafce663933d7f76934dff70dd3aa1a8bb54cac69beaf127d25a05655974be61afabeeaf31569556db57678be9fdf4f1fc8fafdd491abf0bf9fad56d1d96cd6cc6e3efb5fa529fa9e03924589be37a25b46ff7d1172355d3217d0f73e5a455d9eaadfef902ceab3f343cb46fe3ea555b4f57d107245ff7b54b774638dfc3d0dc9a23adfdf56f9cc53f5bffca1c815f8dff79f48d710d24c5f39c5ca2e0c0dd9e7205a190eb2947861727d89ca6016c2510d6b633788a3898688225ad70c5114eb933018c35adbaa4ff29f8c99a599c598cdea8fc0f05ec488622f62b1fae14977d25c9acf8bfa23175c0bada5e9d4074d9bc94ca399b598d5ff4a2e498c2846128bd5f74a96545d3e24aefadd88766babead4e7c26a67aef25a95c634fbc4994747344689bc9c5c4db13056ff6621f43cda1feaf271b95c3eef77563086b67468ab3ea6df0d593a7e4533be1da90c12913fb5fce16ff611792d8fa6a85395487f43de02695a20119786a35dc8879ca85bec5714736d6e4ccc35d191cde8a6553a7c45f49b758b9d2b780675aa7ee75e5f9927633285e195554f06c4fd147f451e8dcc555eab6526cb7b22c7640abba640aad41f3d13e4fa9c378385d01bf5843294a11cf5d41ff5e4fad32de3376483baa55aa06e99dfd047d42a95afff15812f204ccdde7287b9ca9b710f9aac70aed4776f06e6de0c2ce8de3034995eb672fd4fe53bcda595a87e6fc6555dd81e0c7225b572e53e1d9fadbc4268412b422854fefe14c78f28d7ef6e4f97b782fef575c033d6a9fa17971a555f059b3a559fe2476954ad137cf1a1fa1535a78b618c47d32a6fb9e7cd94bc995cdf5beeaa8fa85335cc3553f11b6a547d1284df50aeef8d8050fdc3059ddc5eb2b707545259204ff95717b8dbd65cf1b740ae92adc9b22e576b76722451d375fa84d028a552ca2058595d2c2f5d936581e60a50c523497aa1ef25a0144b4df362f28de996979617bf32197ba61ffdd19070a4421ac80006502e3083080b5440a4c00424108187808c0738a0013118b0000524e00401271f3d0ef06208217818c0b4c324081d40b4c8510002b8943209c70004301200921f58abd087d10d55cbd7030078b0b103e8752c2b3aa8e09043cac6dd14aac60d3434ab810c60e0024458a002149880042200810738a0010c5880021280001f3d0e3084103c0cb023081d40e42800015c328e010820003fb0563edc50b5f460830700ecc0b2a2834a4a0e38a450356ea03103cb8cc283c2d343850c28b6041a3460e28d788353839d223a50a0a0d8cc9081657e181978e6878959a2a635c3c48c192f318062000551c18b8884acf0ff23e384653e2972c2339f14790121d60313820c19ae17ad172d222ad0c1b161c1c909cdc94434b389683686999897329c4e432d7a5af4f8542114f4d3049389c744e6124e0927270ad8cb750313931b122cf348cc488ccc101b22342228956048706792cc61d0cc6190ec0a2fb3a12c904840ad56eb7b7daf9d21cc1f1f9e22741ec9ec11f188d4b4c09573f382d1a8359bc92233d0c8c0a0c1f38838dcb9bb3ed72766c5900ce8095dd7c3a2b1d850e1d949b1d4f38fda48154b067ea43818f891ea5ce06b6020594210f1238dc14b950f0bfc48632af0239da140bf1775cb8ceff73448561113f8d1d790fb7d2657f8fbfd47aa8c90c08f1e14811f5d08023f3a518c2b246bca3ce0478f828f5c9db8a46ace38e0477f35e0478731e047efc93226ce02607cbfd348d5cc51c08f4e24013fba0d02fa7dc64b205993c7c78f7e82dcef3272651a92aad9d3e3c79e1de0477f19a24f5d90ac2924c48ffd05995cb9e091aa39c4e3c7f631c08ffdb3a3bf7b0ac9ea207eec24e4fedee91ba9ea191dddae1c6dd33290acc629c08f4d83dcdf357255f2fd0d23559d43801f5bc6e5c7a6c9246c90ace6c1f1e3cca248ae4220a9ea9e01fc386502f8710e05e0495848560bfdf0e39c424cae468a48550fb17e9c3bab1f278f0f3f5dd30792e53273c56ffc387f90db932a9f51fd386b6e7af8d1dbf871a620f7cf2724cb71824895e778ca475904001e587e9462c8fd1c902ce79991abeffb8d2842aeeeca8f52a6c38f1248e57d4895f778ca537e944bc8fd3b24cb85e68a7fea47b9f3430e3fdac8fd291e845c6ddf8faa91bb51355e8ea64c03e547f74c43b2e80c8a5c554ad36f6506c4f162401cc37c017134652126cbfe10e1c35516c8023d15e8a180389a7211936563139042d94a20021090992cebf300716cdb728038de06cc30600667b2acce0270725ca5cd149093801c04e4f04c96a5f1c1d3e32a2dd6a3e7003d4388a3690a4d9646248438b6ff1cf2e7211a40dc218ea6acf904a1b57488e3056226c70cce64693a05c0c95e6704c871c9c9393c93a5d1f8e3e0a9314f0da047003d01e8c9fe2d3459f507a1ec2e3359d5675c551d67b22aab87563de4430ff964ff1b3257258eb7451cc31e70725c4567ce33599586c65c68b228d15cf1e7210700393be4b0f0ac88a34907710c559c28fba788630778f41ae4fa00c0a34791ebf38047c741ae6fa37e0f78741fe4fa2d787420e4fa2a3cfa1072fd1b787429469f62742a46af22d7978147b762f42b46c722572ae42a24e3a719d39a5a63aa797cb3074d167dcd95fa4e05c9d26c6cacf02357242c244bbbb99942ae2dbe6a3838b5b342b2b49c9c27f4c8d5568564693a3a4de0912b8e0ac9d25cae25c0e48a4e21595a912249d8912b1fa4902cedf53ac24baebc214896b6b353842272758120591a0c26845cdf5dee03c9d278e64a7d9e1fe4faaea3f5f4541c4896e613856469b11a4896f62303c9d28070e4aae446ae4a367265522357261f1dc46ef003457d87816469412d902c4d3657aa0a244b139a2b4f48963634572a11b93a9dc478191f1403d90b845850bfbe33519fa5cde64ab55fdf874896563457aa5ce1afef4b4896cbeacc49e022c8f5bb1818c6cbc0319e845f7c8b16ff58c66fb87b0e6f4f31f73ebc28ce9881824283c60d37d4a88142a55238e090430e29292a2a3ae8b0b2c2c2b2030078b0d1430f30fe844fde844f6f62d2d8c593e0920731c96b187c8cdbc3f77dc0de5b8cbfd6d8afd27e4eae2e51a42f9b19336c6e50506e7068d0c0c9b9e1861c9d1a35745c2894ab482a55e485030eaf9d1c72d881a5a4c0785454787a74d0a1c76765458bb13ccb8fadfdec000480afaf05f120b321d4c3eff00300a0cc43900d590f42b9be36d442a49addf0a16548457463e6c3bbf0018f5885c72fdb6fc13de0b124dbb78179c0238c6c1f0078c4d9fe0e78a491edb3e0153caa64fb3ae071876c5f058f3d64fb2978f421dbcf018f01c8f671b09fc2a39c22db47e1515291edd7c0a3ac22dbbf018fd20a7945b68f82478945b63f038f920ad9be884779856c5f4a15a9621fe32b55ec3ff6a48a7d1938942af663e09254b10f039ba48afd137c2255ec9ff04b15fb268c2255ec9b609454b15fc22952c57e0966912af649b00da9621fc437a48a7d0dff2055ec5f8ca300414815fb1c1642aad8dfb00fa962bfc30b902af65be0074815fb243c01a962ff052642aa584f366d0a2621f4dc71342b8d43995cdcbb33637925cb294c10943b5ae94e0ab573d2190517233667a751a91601eab1c945de26904c552f0b52357b2053f567cc389d48a43c4ea02e8f1348e52f9daa2fc71be1cb04f317fa6a54107da1841efc42a8cdf24caec0afa2cbe0740bc99754a7c9537495cb64370c4da6d7142798c9967252701492edfb8bbf5e42cf31dd78bd78bc8aeda5bdc4bc549e46d59e4e5519c854fd196ae86b6ac2b1d64471771002c12445ae393652a5baac06b97e2d227a2193acf244618a1f82c907210eac586b402f849d54004c76025846c9728a13d864ed5bfbd6be3bd626f0c9d26422f9197216fc72d057a376ba05c663e4ca09b962c14cc19c992cad68ea6066423163906b91a4d603bf21acf516dec2b638fd864ff4ed6fdf999cb4df3428861842ac355d2467c7555fa2c96fdf6d26db6b4f5dbcc9e6c2848f00b7a79a6f1a487bc476d8b72fa91d7f947c10922779d2f78cfdfa5a919d99b1b628db22504846bc92bfd12d249237f974dfc91f9aa404c4a33c410c82f803b107e20ec41c8837105f105b106bb10a623a673c1701b610418e857423f7cf0fb97f6be1cd90936f43aee8d7677940a3e6cbcf86aafd310053e46adfb2cc1e72fd8edaa7b67b6ed2924f878937430e8442adb1f9b22cc912469634b2dc7e4c919dec4c9ef4de620b040dca54c4e116178192ed771687b9c3a33c894815ee47b9c3e00ccc1dfe7cbedc612fc7cb1dee5ca6cb1de6645ceef0b6b3e50edfaeb9b9c3b6488b75b8ea609ae78c54a1b9c39eb9c7d1414815ee51a40af7de0c67100c9946b54ef54e5f0e7798fa23df3292abf0ebd728e48a0b350bb2c59bacc6c9020d3362c8152857181f0fefade8fdb0d6366d6a4f1d6220f3d48f4ffddab7cefdc9f55d786c1d7ff154bdf9a9f1a96914ec5b41d82ffd23df10fd5af8ede8d72815775e7225855cf58f0bcb404619ecfe9e40a7ae3c3c8da2f77b221c6b0da52a047c9dfde3e3c7f6e9f163f31c60888a00b1d688281fff3d4ca60384e110a2ec54ad358db2a94278a20d52f008fa408cc8dab782d03ef763ade9ec274ff76bcde9fe8802e2743bb1a2320f903994ef7f2c08ebd3d764c95c9fee740b67bf7e85b54aba5423b2942aa4af07c9104dc57ecba3dff65b4b3190513e1d41f338839098a30784d40ff2e5b0afe1d3c554ced46f9cee9a835c519deaaeb502e5caa26db8b39a9ce28966ab552141741248e8244a37657a038daaf55d4a71c648da2d448a5d53d9a07c293a056fd41aca9e2c63a815125d966f23c4e1a94a1a1151428267ee66ae15ec42e8df6993a651617ddd70b1a06aa954223d09e949484f527a5a7a3a0262116a241e9697f4deaed28f0d845cbf754aa552e7944a2591d40e6bef698fb503b5cbbe1c5e6832851875a954718a49a4ef40effb74509d46b5804dd8abbbfb65e150a92bf1e650916e19f197fb8cdfd0f4281910e93b13cfbfca157f1c28ee2f4fc464618124bcd6bd091b9a5448955c8dbef425a49221d28491268c3461a4091b91462412e925494462e69138965e2b69f2d341127f8c3ec8cca42f89d3897044124fa42f91441ea85c1247f09c6994ec140909090909c9a4f1b0ec6ef48be7ea33de2279d28fde825c9f24ca467d3d484fe23334de922bc983249248c4dba2593aa773729cc6e65afa4a242c994e508a94e4f037e592efa889c4e17df5bc59826d097bee7ca30d8e4a64179da65f7a09a96af1f5fb45aefcebb7c053a64abeb3efa2e4c791927e2929297920a35c52f2520afdc8cc28877ad82ff97694ccb0611aa72bf148301237938488726da9ad03d43f1d6b57a36a112226ab775079ba07120bf9e612b173b8104ab9d25cadea174fd5bfaf1066eaf958f69916386d53a9d34895fa33558473c24a4ff2b25b4a222cc4fdd2301da3651c55c674d22ffd52f2232f3f207cc65b4ed32d24e2a9f424a752e3e4fa4750542d19e917199a5cfbc5a65f70fa45a723cd507e3d52d98b34aa3e180b3ba6d22ff9b19dc83d4a4f5fba05a663e4eae3517a4a7bb003c922f9faedea30962357a4afa39ecee9b03e2d29615af24a70cd38c2fa5dd225253f7ec9639f6994f6d389b0fe3861dbff287d909949fc49264fb758f164df7b285a7c3dd9920885c9d727e10d7ca10b41d2449c342d4422d726c431e97c91aec47bca920a8222f834839fa801a952df9b79eec8152702b1894166ad9a787d68a40500839c49e7f4ff8c67feecdfb4566f56ef1f90a026875dd828f73caf412c84b251fe2c2693bb6c548ffa95e5c95ca15e4569546399277d191416962bf4c739648654a13f4e11e429feecdc9372f6d36e9728eda5abacf654d3eab7ac3778316b1d656f5a6977ff74b1410a2940c15deb11435112a44043fee4ea8a2109525821971a355f7eb27b098972fbeede7cefdc7b31e20a6fbfbd4fbfb3d3fefc0e20b3951f10fdf62b3ed59f8fcafd9a886ad47cff42107b5f5e5e825894f1eb7bdf5521ec645b21d4bae5c777ddf77df5ea21c96791c3ef68be9ab7739c26deff84e46479c76eeb1e08dd44245c642ca50a10fa1ef871df3bdad1d7bebb7fbfbb22906da4fb2284e48c5e7215fd3bef7ed4d5ef7ebcf5bbafbe29912f8c6c43162ea46722c6ba0c11e24212a538fa778fc31d88679145166dd080ccac8d3a640662e9e7cf01419ffbcee37e149293c7fb4508c979209b88848b0cfee8d735d2dfedb13b4913818022144e907e48a9542a954aa552894422914824d23771c385f44a90c4d053b3542a954aa5d23771c3a5249a402984f3b997f99b734e295346ee3d6e1492932b0affe83fedd8b2fbe784dbcf2cf278b534ecd8c42b4241fafea08879179278da41b3f637d3dc019bb507b3267a239811cc88891113a3108c42d09a7767f77d5acfeebb9c77bd30bce0bdf7deefb71fbfd9772212d3fb6d13b9f7bc3f4febd97d77f472efbdf77a9bfd76b4f7e7756bbd39e749cebbf7d2b7b9dbeebdf77a93e55dbff7dead73cff3fe82dfe1dfb96d1f6adebddb76c5d1fbbb79e1779fd67de09d3704bf4e0bc3300cc3300cc3300cc3300c3f6d761ff871dc58893ccf03bd7b3f0fa4dd772ff7f7727f3bee7623f0deffae37591e159ee7819ee7791ee879df0809c9c876b1cdb3fb5af3ee055289366fdbb66ddbb67ba9b8debdf77af77a9ee7ddeb6db3a975dcbd1dc7719c177a5acfeebba3970b6e7f41eee3388ef3428edbfebbf7de7befbdf7de7befbda1f777dbfed3b28773b4b6f9874d05d76dff7d4783ccecfd8c754b289eee7b239b0805c9db1f812423def6dcbdff6dd7bb5ebd325f1fd4e57a4714464b78a2cd527e06e826bc9fde4fefa7f65d1842d928edf3ea4b3084f4fd4ff3347bce4fd33c4debe4bcf435542aff10f3bdef978f8292abae13537c504cbb41a1b03e45d1a0d0c9f9293628a594527777ea943ea5d429a594524a2925d2cdf6ac08bb5fcaa0bd93a32cb2c4a3124bece897f927faf847154f54dc219fbe7c2a2201248b2c91d075339492d6502245b3988f4e8ecfb88c900cb663d335fd52f41373e94c9a393354044f10283de1045f723f11964ce7acf6a512a1c9414dcd10fee491d2b446bdb2b84abaf44f2057ddbe28209a964793fbbf21f79e74fa952ea70ca97defb66a52201284604e3743f93dfa7a4299442751c60a64f2f12b77dabb25d44dadc95d5bd7bb370c296ca6d157ca250a6951963018990c156a158575aae9ccef6dcd6acba9b7a6fd142190d596adad9a536f7c6277f3898d3c0a04ab9f8eff40589eaff1e409cec2f1e6eeee0c6fa39aa5519987c47203a6a1984e286c0952a57fb4b4a7534da9902bed201ac231cca011408feae869d47c1bca4c3f1d9d7dd2d76797edab1f9d557fa6f6f584206d79fd99acea3357dad472b536f127f75797abe8acfa4c568d99a6466152a59f6716bb9c389a5c1c3b836808b5c7d11577be1896e22348abc80482b9e1e9211a57e7958d5c75d630ca9fc25c4565cabf4f647bd9c5edd3f1b9b65a7c9a89a751ccc92f048d108e347b05bf09030a9d4419ef906ce591e612bcb234429653c814e50e3c433845f73143387a59479d31e17c7f7ff90cea1ae4357bcc7d72bf0e9f46f54f91dea133741205240a472fb77c89d2a8aeeb7f65edb5cfc932f3d4e4cd2bb705a1d9fbec07a199bafb73eed7baf754d38e003287321274a47fc24066e6e6e6e3f6739baf7dbdbf5d0d8f287cf217531ae5cd6a05a99b1e388670943de0fbb8d7650ff7232a4b594b542c4e492fe5cb92af12db1f611df2ad7cfb56082140505bc2f547a31eb13889188a139c1ef8fde5b03f64f02df62f878d46598947afcaf63a2b06c910caf70c52a10ca232f81da593caa6e2a844b6228e7694aceef5e577166f2fb1a47e9228f9dc6ff251d94a54a6d947380812d78a29f5edd32aa240a9f2bd58ff645ffbef3d7cb2e21fd1598a477406a2c44bf6e47bf88819e466fb9d1447946945ed6b1128f9c423ccda87f471f4862f1e2790edc892e5a43d25a5b251d249b383c18b274b934f965926e1f593a5fc21ba97dcf395bbb1e7a73904d310ceef2c5f99caca83d1fbe8dd7f7e48c34cbb19721d95aa7e7d12e4095f3245ff13c72b6ae268a22eb3fde59e7a8ce9439378edc8ce4b46bb907f04d4d27f247a4aa35af47cb4543550cb8026504ef863fedc69d4145d26f446aff11af757a3a813a141ee8f90fc0c7f7efdf5d7e211dffb571c64e6faf57fd4dedfb577a7adedeb81b3f6b4c74d7a4c26d947d92d7d7f9792be8b39849f694baa504a648886f474c8d43ff3485fdaccf4691ac0efa046edb88cac811a55fae9a234bf6454226b79bed6d8fb123ccac825f1889949adf1e6ce85fc49461212ec422424ef418d6a12d183fca974507b1297b50b75cb147f447499078d200c3de871f4c4a1e8428deafc4911cef6f9fddd926bdab469b77d528494f33e29c2f6911c142fde5ffcc43ffcfb197d33df3a72184fa37aa84fa77a605d0349285e3c8e76986e89e916cf552f44a0066a20fca33f884fac5b7ed41fa02bc8957bf5cdddbd3d6be2497b50e4e996299e34ad0706b212914e448e64f188e9449ec6ba65749f4c27c88690becc3e041685429ecc651ca68868861df4ed48e523e8f469140a51a328ed912a74d27722dd32fed07ebed3f728e4aa9f6adf9cf37dc8db676b0847a7c9943e7d1f023a4d91072acf9f4dd42db36ef1be5f2053f469706f90690f32fd1642a641dd4032405d2494e96bd2899d86b6bc1a45dfc3b3c88c54a14fc8147d192e4c73a6e5310d34449fc264fa94d29fb94ca3684f1efdb51eb9b242ae268dcdd9a3dbf19cee3f9ebc9b78cec874cb26a6b4377d236848ddb5aa55771c500064d2b492b32c60949f15e1d521246b9a0eedab46e90ef25aa986f0e7670021b98a9752fa5bb59163136d784f2beda844a6a2dc445b82a03ca2c6520acbbd61c9b46d2cd9deefbbdd97404b74a3f912a54be916fbd65afbb6dbdad6ee8ffa415810d23adffee8dd99fbeb61edb41bad4f5d09ddfdd1b1f6ed5a3f2a6f2fc553dfb19f8e2959fe40822b4b24dc649965ee5aaad43fd16ff14447796757a135a73806b15294b95126d3e8f59c3646c014e01a49d922ed39733a8932da2484a30bc9fe2255b42cb39c22d460ae4cae34b9beb684645522443426727deda5b241d62c72fd5a54d320eb1a72fd3a932b33c88a865cbf12d531c85a865cbf0e5530c82a865cbf0ac9551764fd42ae5f65550bb27221d7af41f50ab27e05aa5490d50ab97efda958c8fa3556af90350ab97ef5a956c8fab5a75621599a8f5ca9b509b97ee5a954489616932bf5abc91492a5fdfcd424e4fab57e9542b23420b952bf1e21d71a5fbf0e41b2b420b9526b1172fd5a44ae6ef8fa150892a5c9e44afd2a845cbfbae48ac6d7af3e902c4d48aed4af3fc8f5ab8e5ca17cfdba03c9d286e44afdda835cbfe6c8d58caf5f7120591a91ac3ac8f56b143872257ead37c8f56b0d244b2b922bf5fb0a45ae5f6920932b7f59c21baf8e71e162e423fc210e65fc888c91180f23068cd3af4e3f765ebd9427cf3af9b133eba5fce18797320001782905208097720003782971e07829737e295d5c5e4a0210a0002f650172bc943980782981782975e8086287017808f15206f152ee78290df052f2782985f8cec33efcc51e88b527c1e09760922f956ad4781af88647c1347e06467911cff80f8bdff8fbc7fd42e0d1654533a221215910d04fcca78707b6f32ae2d2c9c1b9d16c884c531812c93574a1e64238b3ff586b34fa9275a7ca7205bf10be5a5cd6289926bb227dbde8ab5bad564b26f34b4fe02e93a9fad2bf20553405b95212982813b90e95e106676b4edb7dd578fd7bc3b0a66b8d159d108eb5a60a5530e4faf5d52aa7c116b20cea8bafb2076a594f9150e7569cd18f3c1db24f4da630b443b9be9abe9abe5a1c3d4708cc031b00efc041601d78d464b93e1078d48272fd1cb80078d47e727d0260179c310e3c002c001c00fc0366e1151e359d51d37046ed66d46cc64ae46617ff6187c11ee33252a53e0af619ec3452a52555ead7c08ec247cc9cc2a30fc9f571c0a33b91ebe780472f41ae9f82477f627415e4fa3ae0153c3a0c469741aebf43a52da8d46b576c8b205488460000000002d315000030100c064442d1601427c268db0114800d829a4e5c5a9e89c42486a19032c6208400001800001000000044040093c10e003ee68609e71937eead61dc3b318ec1c63e1de63d89e3336eec8ec1dc9b688fe1c67d3886bd13f3346fb0e48d32ff4f5e8cbff6f6d4fb8ff8dea2621c99ec23fb336ed01dc7dc3bd11ec30dfa700c7b26e631ded00f83b937e17cc68dbdb518f726c631d8b84fc7bc67e278cd0d957803c0798f8b01985e4f8bcda8f703db2b85bd83c1dc33839b647ba53675d571243e77e8cbcd422107ed6498fdb2f86a2202ab01c352d0af08930c3d7a9b98c9fb7bd3d00eab294c1acb522a3e79b2a7d798e88bd1470988b405d04009d35802e2002d6a7c4b688e2b8676fc3efa09608e4f786846c304f314f0c98540cd02074655f43349992640effff04d543d399a30896953cafe2d2ded70ca987dadfc144ec915f004f01e082134d623dcbbf04deda2096798528f81534001e7074f0a85aae69bda014c78b5ff6c93bb3e0a903e4ad689dd0135b553ebb6e3e8699752b6431fb1db371396df6ebb0f56c90d25bfce110884a2b02e1efc0c926d771767a4db653830f64a72071070e65646dcf5a2e287eaadcf76357c010fbe255c6b87aa1fae2903e5b3fa53f0ff7e7f473728b71897ee48ec401d7c8e5579d2edaa71832087a7dbc9f362c65c7071f51e14f6d56e1d92e12bb7ab4bd3709151e80ddaaec76daa41a506a8e6cda2c108d1b056004b6cdfed70fba286f021723a7b336fc720668c4d94b4397db6fb6c9b46eef410e989dbddc00c941d8a04773e13e73c7588523417395220d13003f92fb102dcd5e472eb1f1e4b2969603e627db78d4119c05dde67c17b11fac56a98efdc4a81381bc0c7ff3d1fcfc6aa75727a131470abffcf92d9900fe22eb51fc686ab836103bcafe6b63989e4d5c5f35479b64b8757c2e0cc90ef17b956a5a6ecf0b1948524e40eb55ff0f55a1a4375ffd9963f6cce88ec19dcef90bbb9e20e980e737f0da3261ee6d2a053dfb18db9d49fb6c38d622235d394d13e0957a42d53f6b506c95dcbdfcf52e373e6e2522c86d9bf3637f27e4397e40e05455868ef2ce80147e2f643c9350715db7fe63413af9308c91d6ec3b669e24617988657f6b5bd618890fa8027f32bc53e8119437fa3506f6751a0ecf8c891f70fe1f912ad3292bb4512ed294073d0301dca05247705928e24a8baa64c12f948854aaa80635dcbe4f6d081fdecd3d48a5cb5d92a8192dc61d01b605b8aea627a39c9dd8f613db955dabfb1af5dcc38a70a8b8df55633352a925ba180dba2489497ad6f0967aff962ee908b313910ee33c7d4ccf036351c0a3ca070c272c8cd2156a5c5e0128c143e3dc6d2a7c3b6fe4c774d932ce424fb7a54f778ab184e0e999093e191cf1388a91d6fabcb073c65680b2e9d9bbd28f3655608f24df7aab706403f635b7d98d73de039d3f86698b12089d1aa309366d48fb3d57940cf34a410a2cc5d19070109ec162b664192a99a64ad198da1704e9e80370a55d3dda8dc860f3fbabcac9f13d7787ca76a396ccaffbad520a4f5b999a959d3d6d8e608b686481952cd73ac7c3d8d8f13deae55acab79c0c33f704f7206d37917cfff94b47a78da0a306c768b5e8eda87c8940865757f337a7eba0f07abefac71f064caf4ff7d6aee83db91482fd7682cfd9ed4df770e6595e2758a5bc697bd555655c7350b0d50a7e7ee611e159b93a5a69a134a4d074ec0d098a12f77bf9da71afa79c34c767100d2a5136afbbe1685ac4f56e9b4be0297700702c9968a17a23968d7d75b5f277f7703012697fb220541ee94483f2e77f61c958786eaf5f90c065c88b103936fd46e11b4fe058ad6a9f006b9702dc4a1d067e11cbbbd4b610f853899b484f33709d79a84e424128bc76e23d9e4f40468525a665174c2eabd70189dc783bbe0a270cf75dfc676dafcc3e352ad68ed27f115356804116b7d14dee45c03f12834c75ba40703ca9a859a510b58a0d8f017ce15e3063c859a6f68232842274430b43035941b9cb81761b673057c5b814d6d25ae1df7ab73c005c920ac840e465501961f7bb61c4a1bface7ff993661b90e5c66a141aeb22f29ccb4f6885570cfa943165a92514782a8cfb816c2b27d1728a13f5f237a67956db76ce0efb5a508c70f449271fe2c3fb2c1df09f6230c20d978562a8194213e077a19015c60bb95fda0c068c6ef480249afc5a97532c6971900a347d5dfbed06c2b27966d4ea00642997e658a24cb777e6d7dbd2bbc575588e85764323bd2fec4c59e9e92d037a4c0a546196526196d3c6bae8a00e37195e80a390062bc173adf1c9de1150e304b8593aea3abb6fd9beb9c4dce6977e3832e1d83ec31d2aa2d79b4ff628bf634f9635ce579fb7776f4499531dbcdcfd8265ce7846dda11064873c7892c19ab1bc031094a1e3f4c99610e959451b1317b96d9cd2c1165a6f93f535d9a69c74dec1516878c0d4c021449d7ba698131a66c78e4a454a3c8b17ec14323d6a65b91d21275cb73b35c67d2b77ddba7660821493fd03a3fa598a28629b0f81c79a0f457d88f595b71f078cbc0d69b4ce8205d51e34240606c06d6172bb104d373bec36b5eb6a1b6f331a83ed7f1b00b882785c8377e9c9236b4c79b842255870b56182388b916e764714a83d715c1829fea5955a2a125856d218f098b1c37f3346ce923f078246ac0b51b7a6a9c9c6a451040c45a8fbbfb679a05c826837a3c4e5e150c5f45dfa90ac98bcb01f79438c76690fdd450b3671441fe65e41325da414c99eb1521d29dea9f1bf86e2bb8671bad0c2efcb99ffcc464bfcb0e22010ee9b406b1964a00436da92fb7b913302fb5ab1340838294f18ef767a72ced7b29fe5b4ad29c3575374564d1a53c1f5acd8804d73e913c1017b4e78498ac0d1cfb6e0c61277fa22fb07e259709b20500e84265801faf8a09823831a4ddfaf64e17469d0ea49a70a71e573dded4ba6481da1f39aa243c5f54a1a7fbe1b233f3a63631299230f0cbc1831caf7c52805914caeb877d5df2e36d4e1914894d8fbbe70b3c92d253fbde537dae48e72e2ae7f20f548c8544852e8dc0226023d791e179dcebba42458860a5920ea3f15c34ba882b245956ef49730475a4a258a54b85fff75450f0b1e192b835ff825249208bc1552474256f7cb681a038b465d23576e252acc9a3ea648684b5ad9615793fc186c8a2bae33562dddbc337ba3465551483699f7d4eb92192fb206c77613efb04c635f5d2369b76ded3bf274e09ed85f362747b8c7b1adc8c6cdeeeb21f32af2f57712c57176b7912b84c4995f5308148244d1b5969ee2a07cb8373a6b3bcaad8cb2e724658f5cd454a97f584a50deaa27fa7d4755d2d51a893061a7211f6c5ee2e9b9d383c8f21aa9bae6732fc0221a169eb3432ad24ad81116c47485dfad0c23af2b8ab3255f849628d0879838aa497e82092744d8d0b97544084de6d7499adebb784990f592a612176dbe876e66fca9d9b15a70e554d713271e0dbb95aaa6639a14cef725e6b5200bf941a614011d5883c4c969653f4882b9d0f03609c2c32fb314244807586fc6ed1ba35f59332e28017e44f573f179f175db420d30438f83d903f04b0b496b7f0d66eb92dd678a65d5c417ab232740f73eb253fc611be931d9aba3af5bdbad5457b633dfa416fb7e16381abe7ca6f93a6c56e4d010583eebc6c3f3064424a3009419a98610e532f53b3769865e6fa2b0dadf2ff3656cdbdcba2d5dc69c77f8a13d691fbb40641a6902711788b0d3568c5c64b71d40793a5bf0b0a138d3f020619bdf05624abc8edc2801392d15668b598ce8d6a51c328287d1e4d1d5780a230f9d4f873d18033a66e66f228d284ec69f1be65067dcb615800dd198514711a11b3a53a3dbf510914d2626c80a7d2b39a942aa303d3dec953cd24d2b2af744e6cbbbcadf7ae6dcaf6d3f1980802d9765387a3eddc164307f2e370ac6ae43ec87a95470448d7550cf06ba861db913f7411d4629a2f08b117da63a19730cfe4d81446e0ca3b7b7483517bdfd68a0fbe87d8bb6e9c77c43955a0a39d0d80da5cac27d14485221682ffdaff91f79f484c79eec4cce3cd545ff2b81ef5a231054d70abf91f374a540a22573f2800a0f3188e2ebea384a1f2f1fdbde45244282b893c00e32adcac50a0116e87919826256dd9524aa21b3a44f016d1a4b1d59a5095250437a2b2f169a43087a29c659f4bbd47a22d5b13bd9a185ac874028788a7e481b46c4f5f090e6d447a8fd1f16b1c3c1036688888266b49025115214c722587c0df29d2818f581eab85dbdbe528adc5cf0a8172b403f77ce192570c5d139091e174894b5faf02564cd758eecd1dc4f7cf57ba22a29cfa8b95f355c76e5021a9eb6289169f3d634dc5e57339c40f75a0fe846957827715ef5045d871cdb728379983411106e931e1d13baeadd86b7e6f2cd10d165b9e630a2b110a9bff8127eaf3daba0a15844b69d41e2b7fe93a8338e0e4f8f2141b3e8b00e3ee0288b286dfb7015f776a14d0e2d341c9c9077e5e4fcd98a6c63b3ba3bf0ef32f924a0fd7ef42953ccb17664388cde92e9746a2542eec3970bb12e659e24821c5934387918acd03a26c74428a52834359c6bd95dc44b257ab26db0dd07e688a6cbae0e0741735fd98f9ebd80dcc701e234cf0721350037917ab24fa235b0d67b6543cc1efa897534ceb6e26c13ed07ddd6b68a8c39e90311493b4f493d9c37f3ac0b845b40725ff5fa89f830bd501f3fec2a38e42af2e8ed2c7f7821e581986ad6d7b581e59aa21ca2452071fd237887c457216006beeb8eefd831b896c487f1bce5f4776f58952c592dc1c78025eadc9199571fcaf864d5446be44f7c511ff682439b5c3e9622a8b7963acded73af460a60f7a14377766df4a4ab01cd1f7fe24662c398833f4cd8b808054f0cc69a3c506aab1df4bd242e7e533dc0cb0fc14dec69831ef9eb22cd94ab13b0520915852666430f05051a240f8102f758c177fc252472555d07a3b77b7e79305878eedb417e3a17d5202da54bb674b2758710fc3c5ec7f0502a90f02e2849d2c800b4bfd7950a0dbcf97e262c751e635d27c7fd22d9ee10c5bcaa60eb0bcac6b6619d74594c3cebf55a897eed6dcb56f444beefcbaf8fe6667464f1c0b97d88131b5d708a3aab462b2c6aa31b7e4dee08c3ebbe2f68d12c89c8b98e9e18d9e8069a4d197440512280280edd0f621201d0e21d189dfd53c877418d6fc258d4d9d8265d20b3d8ffa9040421bc2156425d9bbe71e201df063fdc5e49c49df1ddaaea06ae11633dbe52926256409cb9beab1e94a7a4428078283f8308b72e1a0aff6e878f457e5189905520ac9e8d6e8bc5c9d0bf81da3ee9931c25ac9d66d79243931ca90261c991ce3b60bf1d0526f92777330f2da955f2155bd3725ad50e8fa8dc1fc08935f15a0af580104a7e179e4b239017191e62cfbd41ef5b2f957f589703064e49aec01345010ba21b97f4c443c295cbac99ef3ad4528e707a884b544c607a5318e236bdf1975009db3b0c5ac2573471115437efd165130e731830b2fec723ce5f1cfc24de6d4961ae9bf81948e724eba0f471a29b837c4c552e8f567677adec85c8021cf214ae1bbeff7f257912c8c8b3021f7c062d88ff96d334a67f38e7ec0456b931c96ed103c2e1a8d7f21aeed88bc25a14c4f60c68dbd59eed22db28cba1d504d7df91d8a14d3a95190b3dd5d3da312cb70e612e974ee3d3e66d437b135386e586887c417dfa840aa08934e71d2f4cd2c468997dd75ccc31d1424e0b8ba438464eeda410d1aaaddb552e38acc4c91fcc7c6fe16fbb2ddd554b42279bad0066f8aec4ecf8c451e29be8e656d51427849e772afdd3330bcd2d504444260bb7dd6deee99f5c4f66c714f60c67ca85842f2b3d443a62c947473e5abb9ce0b21b8018292c2e7b5df3cb174c5f5ce84bd4d6c34f6b65fb66e2b1ad0680bf94cfcc797a6e513e0280287d63593e3bfaa1e5b0df0b0ea6669998df9d6bde00b7acf29da6609198ca648b43e6badc885465201d7938fcbbc10d34debb16629177d934576506a4f12337cdcd3651db6896178e2ed050a4e32cc69dece4571458a8aea28f48bb747d162d2e91fc7db83cff8b10300f71c373eff903800e468b48a1d668ab45925da13ef002d55dd0cf84eae1175ffe1226dc78a5a76588c3e05b4d04288af12f6c9c2ba549454b1b580135ecaeb743d98507f422660ac0494305c44fba28c80b6616a93fec0d4e4095d285a2985a9414a3602874741678b7ce779bdb273299a05172461a1555c614b96c870fb992d8bae69a4573cc904d3a3091e7f9ee0d492c3d24290247cbcbdb8f9410271e08b3664f6f8c20c52c6bfb60ce2d76b05a70c33a434e2627c46383ea7f516230fb0683508c36f771facb8363539d77d22b9d1150ed0a046610bd1a0414cd5659c16bfba11df600cfad50699632b10ae8dcd50abb7f5caf0bb979931293f3f554c901ba47779b486c1b7c17731155b61d05d49d75b383956619a35d38f6cc231246fb9532b7fa5d31ba9d271b802c3783cc910420404740a4743f66366c5be6813f29631c8071a1bdcafa3b45dd3e1cb09f4bea4a52f10b3363fd30dab22bdc508bc344bc448933d85435e3edfcbf37887aad4e0a2621f402575a003d8700ab8526ae44b2d40de834edb227133aec47200642aa62e9d934302884d5e78d4ff411ac56b12a54261dd4392cccdd2e362cc79ddebfe120cba62e5fb334beacfd176766731e0966aba995b059234eadfc75895cf76db330edf12de0dc5a28d1cc8340d48fa94062c539577e929899ea4aef014a1049f0bc7b12dd7792c548cda66ed9ca82197aa4d20b6e633f14ae6afc121c04fdaceed5ff88a0aea93ee18e945db75bcd9054c251196c2a9631a155a3762e95d1f026609ef6feadfcfa11a3322425a09d2961ade5b4bfedf90877439c4ce88aaf31e494cafe71d22739765fa0366581aad7209178ac052953671eb5f326e88e51309bd0c8a881b30c34d2e1ea3e904c06d9fc40bb42721c55a8f7a626a3ff395bce403170eba29ca2a4cc748a12785de61f0c740e39e0cf9f14d20ad298ace36391c62e6a6c31c103f7cb84a27b142178fdd5fc2177a6d34e47a12663c80bdf45a050013d19dfaa22584bd9da9d65907455bd65793a931f6cd2158f86f654c232912b75b3f1110b55d9ea06dfe30d6bcca7d5ddc3d298aab4c7a2cfb40f4bcce0859b119b72bcad5b17bc93e098faf93f35b8f2c38dfb13a79755a13a040e8f347a0c99b69f267aa441f07e6720c864eb36a07f2158e3d375e1fe6caf0ee1a0c110bd07c0f53f20541d424bb6e04566667a54ed55d6560c9ddaaf6a0bc91d718da80b198ba151800afc7bda0bbffbefcbd8a8a1b5000fb88469160a904271e2de044c1da4a248cd3e2a4adf79d7ffdb71c5513aa89bfd6ef9829cefdfa8f98b239aa1ed0da9ab90937cb41d0aeefbbc61df600e0433c73c1a84fb2484cb9e345135d9f25c89b2fe44f992e0823e238a554148a553b20e96448fb051cf9351354961a0061a60b7f1c683360af2a57f02216b20b6b41d83b1551a1329406db0a17128bf71d96c023f272986317c951065ded26a3c4890ee4bd3023302763721ff84205e2566a08675636c1cd7247849b302a8c7da38dea0ea099bb46057084521ef00359862fa22c807a80cd068e94eef6bcd656b3600d80cc238d09cb65854905352b3814f1d86613ef13961f5178d065ea32ea67f2d264f693b004437e5a1923fb0ad0059eb4e5acea9ec3a3bc8f377243023e0b56e2e93784c0c638d4360d66378ac9b59fcd36eaebbcd23e9770eea41ff5dd3ea5b8e83d8930e14f98fa55e45004a3066a4ef050da1b090d818bc126df426b7b35cac7b70fbbf940bb50c2b788517ac11b6b32fc6b7940b7455c230be5c02c8a3cc2b352e3fcc2d2b6349cae53849c4085e2ea60a905728440e21b25c8e87dc8258cac5529ff10ac587214497cb6188bb17bf5c2cf529af50681442e4d3cb1ad001c6c33d2dc29b5d8a05218e60d0cd24c9fed463265e75b2ff13cbf82d9ac01dfe03cc4ac39f5dbc8b3fb688317ec452a07989894b65bf664c08e33fc9db388da0ce89698251171ae6041e5a5afe01bbbd2205aab037809de4cea2ef30e26e9af307e819caac0e06811e40c89fb234f8c3a6a0b4ae29b8bb79d44b507d056ba661fb45b8791983d6e22926b87da93d92a3f9b304794b80c5e7b97602b42eaf42dce286c91c8bcf2098380e3d4cb3870a368be7cc058b6a229dac40134c6cf387711418028d3da10a13bb3142b25450045a88fff010e81024a151a77211d58682db4c1128c82e08b0296c912416f33eb8451a0412fd2df8d93f7e70482f850ba3eaec894e7103e96eab48b63cc4b43c1f22a6c2c604ea699d33814e3b83347d5fcc07a2e300184c6635f7ec8a3375b99420bb49ca3b5d6d17241ee4dab713dc31f35d8b2f15f58ec8509d40316b725d24d3519a0550b2d424bdea36564de354324d133acdf61eff002590277277a5f38d89d49f606e644ed336322389e3d125235959de253532c535397b82a6a7045bf89586d83470c567fab3f96a5062ef2805c62fcd480530c44d4743e0b42884b6cf4188e88b2d6f625eac4a065e5d5a5c720a60d25b6eaeed00bba6a0110e816e0fa86ea3734abd57212b59b38c2b9aa914698d17423b981722cc3d8c4762a4e6965307425dbae4c8441a97d8ac90c5daeaf22074ba14eb725e9fb480b4e28eed065c6993feea2e2aa086946aab447e0260e86febbfe4b12073e6446154062409bfb7343a27e8cf40e7ab685f7e3788aed8fae910eb4d35b036733e9bf883710b9afa6280624b255798a35ebd05bcc4638019a935955cda31fc2da2f4a611eac084b47077d5d16956918b287057f21ca37460813a98b461127487b0895e4587a92e8bfefe0d4a3eee2092282a5048fc10d266d9bcf7a12d4aef1210c9693eca1cb2e0eaa4910f6fdba0a46073bec3949348d07d7e6eef083b5bfd3396c9e2ab5746e46e60ec698c15c7ce2883503799b8ff35ac299bc0f8805558836b0ac08eba6a0029c66385eb6411520eae413c3a83d0e540885cbb1ec70122cb5a8e6008eb33ebac0b7d6032259b677ab6368b6170f8279291152a5fe9697d53c22e1635de05042266b3989c20d928058edc0062f99db92b038a7ea038d83b35c56f15f7bf0b54b16b45ddd7ea7cb74f76417ce1ec9af63de982e8f677c7d567138fce9da8f0f184005d23fa280e44e84085fd914bbbc58c64c79944707a1bebbaed1552bc048010f3986fca684b2fefe831d7aaed87fea9362fc33040344bff61ae1e75cda49720c3f91f184b2ab6359e2d972d47bab1aea6226d67e8d9650ce2f811638799471ca9683b503142afe3c70a5c1e460470d033e3463053b639b0819350ed34e31b8b509ab8b35a4d472331b08b1c1b09d09f400c12e44a273f58ac09f24c51a22fb68470c93d02493f3bb35d2b7757395cf58251e2850828d13cf3b5f2bf5402050b24172c81cdb544b8ffb35584f00b92961fe023c7ffc1ebf4af851866abb4fe0c4e5bd2a23ff19cc982c2afab438181b4265c6b3402d327776bd204499bc2add51f4c6dc2b14e4720b2d9336360ccbb4d6dd27486d32097e55d273837a8863e33cfa395c94b6d63aa3e1eaa9edf0b086d6ff591f5ef4b6b5d0867d7bb2b0e9291642e16792d9c4ce6f8fb6c54fc4079d3214fbd12528d2e83c4e661a1413cce9260879cdab6ee829ec047cd6da404b35b839143e310ca893072c545a1b4147c444e6e0282119b10ef3552a92006e6555cf88c0046189b2fed7401bc8eda2343ecf330f5f8343c01313dd316699c8e12412627685760c9768ff9df31d5f7a3211708df4088c96f90b84d4f948b8da2d24d2a0af0d3a166abbf2a31aafd847ea834a8a79332c981a66303f32f08775ab66418b8073ec4704290667f2d2d93c2a2a430332a72558c42e635f0f36a5d216477d5870675d446ea5f7d25c01831ff1cdf73e55165fc6c44590de395f02092fcad0d0f2e07628350ac98c62638f01aa935ce81912c966500e079066b06dbe1618fe5383092ff91831ba3a11318a106d6f945703fdda94323c502bb08e4f455564d33cb2e63ccd1ca55e9116a2eaa88b9cfdfb88b480855181bc83cc561c484fb7bfccb4f3652657a7eafa8c8ebacee37841e1b38226f5f1b6fcbb7fa4ede2e88b0dd5e9ab363820c86bd7a584cd10f492065b28ba86a490cfc657ec1ab1d5b2384a1e5d513263bc70ba5a22b1c924a36f8429e43554189dc1a6e3d879359905eca5c788ada6a7eb945ac4475db7ee083b18726175661cc8fa9f9b6f84e365971f4c8ca9569ce249e43c9216c622e0d7049fa98b602474bfb29a9d29f1bd5803f9f22b37c7647885e20a56057fdae29848b0771e1d5edbdd73faddbf6dc60b3596ec5e3c74d57e621bd5e124dc8eb699ec40276c484bc033680504971238e0901c830c7de5a07f6a75c6bd60d331d3139e2a0d6f088d9221a00005f254909ebffc73504e513b280fd21531e7e353bb41622e6168ba344f6ff9736729be2cf66d1c003f0797082f4503caffe6b80384002b58dee7812237775bc5546bc4228f40a7ad7c5191e8168fd8608247af1123c13a90be952ab408a08340b5b58898b6a33e463a665611d1e6d69ed10610dbde1b55e7ee5ef28d2c1241e916ece2a1128145df80055563e6cd2d7b9311beaeb78c1bd87d3c2de996b343a08f7a99bca81f77b04abeb7d729f6e509345b8f5d7d059a567600190179113bdc9897f6bd9b92e8a8ca8e676cc64be08132378ee682815385293157549c78f63720410e4511ef7e525f91121d2aad13ebe1130f3bf4154660c4fa362e4c69caf109beda35f56f7ded188ee68135e9445fef23098921efb3f00e0b84b961459688bf29885af8d30eea45fbce53881deac8f671648d0eb1ad925c93157a0abf52aa1590adae53c8b3e04b58e889de41991687bb1f0a9fa61c05d5f0779b31494279d10d6f48b1f6c1dc9315175e9976e93353b85e26a653425923d473ac1eaad37dabbeab03e44769790a00a40f056fa75970035f56733ddc75d33e7b0eb5ab6631703b7620b1e04b652c9ec0631118ea9d89e8d086f387e8bf966aee2fbff5cfcb7c6c4b3e0c4b0a36ee02bec8e1d5fa4f7bce07a610a577f936b2a40065e37990452f0627001c32e084154776fbc483d8cf72ea48fb68c0f68146717b3bde0b18d4bf46b546775ebd13460757772426af5227b7a3eb2593a97ac1d8d5d764243295919fd252d52e7a86d1057890be175bd7d989354fa2e73b897d7abbc359271bedba18beaeaf0837e078c0fa841cbc7b233c1b359af96019615da8e9a41bb8a670ffec60e8b6c2e3c2b97bc8328d1cad15c70a3440dc15fac69a0727e056147ef0c2cd38382c283e44374a56b80608b841f67709676988e771be1cb72cef58adb5b0b4fa0ca094db73e9edd09a3555f33f5e1320d680b8f7feca6c52253e8ac1f553621887587f9fd81a8e66a1fb5985a5874a73d57adb4ae7877f63815f86526906068840a74c961fba0269af61c5bc02c95368716d0ef759a61d238019ada802f8f32ab66c13b128c9669586b130482ebd5190766a9b0fa8ae50d48e8d38dd0a60309051a5720831415d1d4f236c8c8e31b9453d666ba572d7e813c660b686826a6287be0add01d0b31ecac0d495ee9611f730231af6d5a2e969d3425d80191f25e55691a772152f21bb9a20c421eeaa6649da089c08c1c672683d4745a79d2c6575e392c634fe50c23ca32bb90c4ab5afb7ee4988bd70a9a5fd8619030d617e97a401a9094d311cced9e9f36b55910b1302fb0dff79c39bd20fbdecaf3c6164011f28946933bb6b1dfb1b7c5e7ce78ee16440d8c84eb1ed00559338be84ea76ff9045331874eea3b49c3f66449432924c3c4879cecd2fc23b3ab33fda4a51669cc6d1b1b9264d2c3064d5c8c8fdb00722bc9cfb9a483dd2e4e587b8e8d9de1cd417cbb405f324ccaf9df8e1667ecc3d769da267d60d0d6cbee35891bbc0ff781b08eef3c1fac5826eff67677dd83b703a4cd22c87c6caa551443a610567c0ecbaed54c85f8f3573396afac8d851827b72b0b3d662b306f6c9a1dc9627ee37f7017c4389fc1101e30155baf86d7206e306867534636a54e0254a19144b3fed9ff0a486f45597a4b5ba5d4c36d3e1b00a8063414db5e1048b958a991f8588a6a43e9f5469dfc74196fc09b33413ed63536d7ce69b147bebdf2b6482969ece0192c8e95a8a769d5f763593f579660a47b53096d066f21e2d1c07be93d7d981711168feba295d51e1f02683aeb2e31eb20c5457058db1ae41295153a8119089f1d9abd5bf48c235fc902f6860fccf7991003e8f747a7bd2e920daea457128ed84dba5c02889855cda83646a85fbf24bac04de7aea20472b8cc2dad1f99832950691cdb03523eb8189885cf7fca66045372ab3c726a122854cd9947d667b87e68e93ec10bf21e47c686a9eb51f261ffa4774f1a63528943b30536eced1395bc7bef7aac857bb0a94a745dcb8fc759fc83a3affd6d0f0b9f9121c0dc398b3156973d34785c3c40997a9c658107f58e1e9f7e816d77a9fa3c82aba167de8c27bea680e027c6a0d4b18b7b5ff62b01e653c82c0df590bb458280cfc87bfbc185a47f557370f8aced2f6921591d8c60583bd419b54c6b3dd9e6dc93ad8bcfc8e23d885fc61583e33ea4b8f7175f360662a4b291c64e25649e3574fbf79485f779f0fce3a3fc01fba12b72d83033c6e3aa53d5d98d583ae7fe706c053c05c1699043c1cd237dc0495784876ebf542a7f1d2c76b8701b55493f194574f3ba244ef1528231ebf62078141964c87004e42b53f527933b2c5d00c7442f52e5175db4e1431a37a60f47fc12877e757be7e906e49934702703de8055940537d9d233306adf6817bdd521760b1ccbef30b8583b811125fc2ae13fad643bd688053e05a1f88be8ec24767ae9c146c9815e314fde788365718e7a268eb7ca51a64c5434afb3d39fae2ddf231763f0cd6de148190a5286813b950b71ac31be747393ad871f6021dc9e7b2477a642046b84cbd8c1f153a0fcb568777c4c9c502e360a0e21e58c69323831bf0a5c6fc535ac1fdd6145d1190dc4927b74fda3112b67ca2e76444f9c1ddb872a970e15dc2934f37671a4e709da333cbc1c50599306bde8ca63632e8ece9daf3923991acfec1af006114c7e4a1dcbfce378219afccc9de7e9dd92350970e3b276ab7b020853e0e0978db96d755f99566f0a02599434919f2027a29ab2fdc161d3fc70ce3eb6229ab8c62dae85634dccf9a7d69fe0977a45e25599226bc71df14c00233cebbcc64c84c473bc97bee590b3b2d0cc6e4cde17da141d8695d04d3c5493dc553855e6147e893d196b1222cb3df89bebfeb1e74c1048a22de1563723a4b5565a914acc0df6c842eb2876b889185d10e02bcf8c2cdadf273df544727fc311324d537bb1f0c0c93aedd5ef233d87edd8e758a859b6d546477795d09d266a7c01f5c8a0b02baa12f8908969668c75299c0e14c4deb8bd28d2274c137af65564c7d6aecfcbcba4cf37ef0c6fde3ef046092f18225bd33ecb8a82f21cbdea89f1be662997172dc3bf432a04456b3adfc0023484199f69524b48d6bba7d8b9ad7f1a765d33a011c38828ddfadb184c22760c69b67b9cb1f87f65b332ca6379dfec702e5dc2197eb4b4d16106700d05e84cbe9253543d32cb88113af979c9ec1dc6366941ac68946738909a987da4b223f1fba39f92e7fe25024328a1322fdf1d7610fcc050178a8ebc13877d298644f8b5592d803d4827f175c36021540724e9da3cfd52a02f1de2f32cc25de6e8921411167a227907afec7a086b7ad7a51ce93007edbe01b63f0e8d758fe07c23f9c1bf56ceb48183d1c95b97c0c4ca1cb23ffa80997617adaa1762e62c6c73e056697dbddc9bc8c4e1c2ed963d40869cdd273303dfd4e8077b8c40e5a1a961369d1335a394b62b7ca5fffe28bbfbb626757cf876e04840f5f67c07aa6546450fe74a1f87448f1496b8c9a12324413fa9e07406fa863be1f4af6c6f5b000885815afff781434392a7c4614387e99876bbc3048b26fc4bedcb82aa48ef948eff14e9f0708966d81478c3f759f361423c87bf80fa93b85763d6c304acb49c9cfc20080c6d393b3c0c5ae12b56272c01ea8afb222bba814a564f654069bda202c1a1c78a0d52455a51e1507b3ae08557609a86932a6cc2161bbcad5e110a2393effea9e0e701bc5e38aa1e07e2568fd6eca0cf7b51fb4dfe49b51e83dadfb4010e031d8239f9ebf1087e2d1029e5b36248578cc38789e2e84042a7a0f29da0b0abf04a9bf4ee1584724e68034de81b3ee5069afd97fd570130aebacc12ed6c915a5a40bad524c2532f7725feb15170379924e791e70b87fa9adbc30d5c6dd0d8fcd3f52f40defcf6eab1f26267c204c555fb8fbce5aea7760a9f3a8d42df138ba94b09141bd9e9f438542d8c949f775b4ba0a292e0d30a09d252e3e4d110a19f4e6a9ed7930642e46141df4f0c99fc774ff2e13fae05a19ac5c0f19399c5888aef25a7c2c10837ed1f2fc88f083eda844ea48cb89dcf87a52f68aa006cbe82e165c587ad7988cb3def31490d07b7e76184816d8fa45f674aff0c0657b6ab564de5a2cb4dc11446e0f01bfe67d13b9313ca313fc85f9abaad9fa90cc5ef2ac061f11792ea43b4c24c318a169acfa2a4f599312c34b9ff1e83782a894161f5a129fe90a3ca7ee658188a5af37937f93bec5399eba3fe6a333be8b0a88f10b5e914dbd7bcf0c5365f037138630cb9f0f42098217ca9c231dabc62b23de8e1b99f23421f1c0d762a92bab817801878be1414653e526a03e16ac6f88251327ed6650ec8573600395e27e0dbb232a100d44f0ef2ff5637635a499c2308ec56ff1c1ab3932626385988a89a0697d12f74fc0f2d1ac6df2cc73822009bfcf66840554f10df9766b3854abf4395aa771474ce662c929b9a3d4de3d7ebefd1c92837e7d61ad868f5d0e3b9190d41993adfa709664658fbbf93f7685220d4f6d160945162be59e9cf54d64be840296d2efa9966f07ddc044fb8ceba4c06655b73d70b1724451543c74ffd5eab937742dcedfaddae94705b1dd183bccb4d8a6c93300f5d66f9121d00ada0982afe309087bae605351ec36a8d45f1bab2946f4018df06a6ae12a8c1dbec9edbb7178f3c4d3464bc121c2c7c4f30fa8a458d73cecee72746567e81e7467cba15146be85f9c33d1ce4d9baeade25679bd635a88f839f97df8d009dbb352092a67815a277f9bc90cd69aa6d52e33e611c06460c4c1a5fecf38739279a9a4b2ecc8aa672ebf528b4e5ef25c2c9bc2e03a49883a392822263a563cb292cc2eac22d08bda9959fa69be2d493e8f230fc1c63d12f3415b1ec73cd15016b32c4fd8c8c5fa59b9350e27f0cb810514cbe3f9a265bdc31d8cfe4efbbc85747e7e9cb845383e4e485e96aef0453f2e3a7d189d45ec0032853e419b0076644cf9ba0f0bd1bb50947a4be37477893d9f1571554c49482f01a5e456147ed0d3ddce08e47490824c8b259cdba2b8017085d94786201d10fcdd4f08dd34d98cedfe2e9f5c155ab75ff5fa7e3b4f9f41337d65cd679e7f36f23ce9ea048d58413a5f7bf120ae518b3866e7c5254671a0b378e02170245ae5cfa3e805bbf652171ac71a37d42885609f688e52d5e8a406c857dff3a7c3e8f5248f52eb8e3074ffffdbb846d0fc1b7c20a984a2c0364ee17d4bf8723a51d9c2db246d327eb3885b83dcfd19a5849878320a207f6d4319545ec5f0811456134ad876ded20c2b31f2f23ac1f8ed0856c3af33f6e69093bbdae12bf0cf680ba94f7dd06d91dc00a1fbe0580887269063a8eb9f20680bfc747873530b179292b248eb0a25ec2151703c902c87f9f0ebab124a7f7a931417f2aa9099a416befa5d6b34ef4089ab2ca450292db221c5d4bf2c054b266d7cb23424fc745d81632f3d02a1632c8eb46405fff7cea102394e3f9521f52c0f1eed572b7893441f98dc22bdd58eee56e01584d82d0637e3a1d1c52074d0f9e541832f305b2b8ea407a80518748f71ede3950cb5fb6906a605100de40c2f06007a739103135d29c60c7fe0e3685f59b861255ef016cf5a2a22c59638b6811f30dfcf087ddf8ee0970e955e806aa4b8b6eb8beb4c00152d8b3cf77fdd99a390dbdd043a851736061783fc45cc953c4b835f72f2c288c1f8a65f102194c857f4aafb26913c06cdbb8c3e28cfc8b7428d21e1d2f826dafd466c602b8597ee9c6e8e1769f6172cdaafbeeacc6d6c1461901ce4b69efbefdcda36441519ef3c5af7c636e72454eb01539d9b01eb5d3ac4a7f8d2c60a0cafb513d3d3058ff29875dbf9940e50001dd3f93101e84daa3ffc135ad7578afcf5384b0ebd8128c2d336006eda72a19c3765fa880cc942177ad81b15e534cb4a53a8d04673e3a30ecef95a6577f02d6b1553a8787b1f8583e1cb19e9824606e2f1b20438ee65d1e7ae098b8d9ee700fd6446b4c8fd5c699220aaa94cb8ec92e4de8c8e842aacf53c11a05e13d15345ecd7ebf5779a3d0514d90d4072ff4fbd717fc027afe553d9f948b133d62ea26920653424fcbcc369d598559c63b1ca1e8f9061c6e73472a4fbac4ceab88a15f1e98c5bf5620a95ea94cf97fcffb3997708131b1500615e90a2c3deb57507e598453c154384e7c01653d77a4beb1c32fd795e4a4830afbe1cac8fd746138652a2a0490b626cbf2a9211077cf4137bf9ac13b51fe8f54c72bc1a6906b797bc50a833d72d516466606faeeb57993d63215dd97fe511ddd1770933c602cffd628aa1a4ff5e02c9ce252bd42e17448599f55cdd40ed70ec5bb583e4e5cb7002c6afd28dbfea5d296a68620a80833a72eb8f3dd21c79a9f0ad325e5df1f588a12e0a43b0ce58596e49f084b83fbab1bd6a0e0a8e06b81a71593976befa8175f788c614ed398e4f433af3887011adddd3a88e1fcfe4ead0f4ee450b84a96384014731b6181ebbe39491f9409400bcf709b68810d8937e1186e82c9788036a01e19f4ed1a27419ebe1920d5650d6e392f33bfd667c14fb6fbcf5e46f66aa86463e9991a7892360585b23f7e0236faa165ee70b28d8f97512d595bf1180eac82ba6d036488f847cbabc475097fcae23bfa1e07af793c37e0c0f5763784ca557a46db740561ec9e5e35228f3f77146be124a3b7847bfeb3f2ca54532c0eb39e6e91018904080269899ed1cd1242dde787a1e4b2721cd8d5f6f69b69b1b9bbaa2229d63b59ac36f26ed24ca5b8b8b10918f689dff2515b6bf920d1d36a3a27852ea245bb85f6e5012938e8e6ab22f757135bca42310eec5f0b60c36d7c151db7f8a7ccc054105d8fb9c219f5488b2e8c2eecebfb08573f233c359575a65c5e6438c69e8452ab7c0e6c268c4cb2fb8eee2a25ebf35179b9afe69ee4ea0d8efbf13e97ebcb070742a0db27e423fa0154ec381eb194d02fd16d8bf3be83bcaee08b0b5f7d32b45abdbad8dd2db37abd664cbed1e65f553020aeeb1755a5317023a92d0f541a7742104beb90d64d18e9e5e38db1cf88c2e8d230ea0f9dde4e62dacaec4bd91bfab911e27991000b5107f34f827e47acee6356f47cf578f152c889fb6562c1cdcd949cfba8963b68653b3650b784b6e95f909b38f0623dde640372eb29fafb0a18248329a640bbcfb7cf0366f3464b61612111d44efede84fa6235bb50ef4789d670a3d474184a242efae6320f6b2b2a013eada26e6f40c021c3c158833a23b69d9e580c9dff881a92edb42e1b7ac82cc200ae0b2ea3388b237adb083e1348571611b23e169f2d5f21db7e75dea2c8f477a45d3b578175525f7d7e66b2179a86eb0ca59d8e55eb6d080e0b3cd25ed5adf1f9e00ab13b672e2564dc0da09ac31a1f513b67662564dd0ca84589fb0d5132c56f589c4aabd20db513348716b7200503bf30cd3949a9a1d1c0be97665e5188cf8d89b9105fd609073c98387f322d57e87f6af7afc3513bf4930064486f9e3684593b570db861c7692b23562235b374501dc02c3180e274033f1f4327351b09e8625ee2efd017f0887befa2213120414831993244806d572f0d58726d4274a25920e3259b151d952dad8295d7d30e78b1c43cdbdc128616f1f0571af1976db65eacb8f768c9d1445e449bc6aad4f5b0b20680df22e8f99d5d643a39ec7da325ba0f0ab0bc96396a6e8679a694665dec865c30e4ec89c110f1f7064f7bb96dcfb6fd24c8b6646d255c4ec6e17f9263852deffe94b48dd77b9620f2581219120e7f935fe646e2c885cff737dfb2778e4cf826b2e88cae5170d420b222afb9374fd4752f34ff06f17446ef1c7594d23881a01197f1d05e02d1268d041cd0b80f5682a88caf4f77e840c222ae1d734f07712ed2fe35ee89b922fa03c7605d1e7de3b06760fcde57ee7ff5f1071df5f9a891b4450a9f91ae72fb312c4b93f493bd9d52088e2f77f2b353903e6af48a482017193d8a2800e6e2f888ac82f49620b220afda3082282ee80db94e0e0f4204af16e5bb6871284ad20fa64ff8a837fdcd3fd2472eb07515199e5bd204144cdd848e43c40d33183c8095c801c9d35f8d96808bd680b8e3a30641605cea239202e41c439d87cb4e9a496f28aaeb29529b23d24195b4b1644e514b0511c44947fc8dc29ca07f5ed26e263cb2042d2af9cdbf1843dbc29a2114448b51506794a87eaa88f5a3a4a0c8a7a9127db42780e22d167f36461208db8a1f6d0b7548ee30aa51351d63f320d3de4d2a251bac97b353644bf2fb11525e1a73a9474544a9519c16285588e60a7a7117157a1b64b77a4c9ff883031cd62727d2629e265e249e82119b44f3812cad77fd80405e4323f4c3c1b28b99b1205f15abfd4725132cae24c3e9fef2de76db536fc540c15a78e0971b5abbc2905288ef918ef95804b6f46e501a5077e5b3626899afc808659023c36b810e251184862cc3807cf3def966a27661698d5468c0c77352a652b9881fbe5d3bcad0a1a7466f21d9c3be6657eda5b84b398913a8b8a4935b4311ac72f0321039b0743e08b6576c5e82eed46865cf28d360c338cb2711e13232bbf1b9093cbf04049447cb66d5e84188768e0aa076c2b3deda9d145c58880290896d2193ca529c1073296b936f70c44f27e9b04c9f52d900d189ada810af32a36c5bc597a43ac36ebe8d06cb72e8726a676c4792ef57a9668632f224694266628c9930a01d5003fdc41c0e59ee1fe2bc1056b6bfbb0845c10a9c0bb9cfe471b3c0f6f3f4ab2c4dd3810032c8db0d0fec2006bf19dffeb28a1121ce1c038e14a30a02e9dfe3df902e6ea18be265f8bcc37d5e4fa999f8fc8d2938099f83f917fbf84809a0e046c244f3f3bee03a8f81660eefcbf0b008fd82600b5759c6b1cbf4c0d6094ed11c0a6fd4f3180de15001c1cd2e765caf1bf5380ec9a5eff59e9471103927e75ff5773b7c4c0a8b511e75ff1714707dbbf2e33d838acec0f3e7f057cd049a53f80b3232dfb3e9da73f331d683cdade0fcaffaa57d8d5afb6bff0b424328ffe59cf8203610efd20f5574186a8a1f9233896b0e731ff78c000e2db14fedeff72f50129f0fb16f88d681a4c5d5a0a87c83ffe1f3027ed13aa8bc09d67f87496a008d93d625f760b5bc026767d43a40a9a7ed7ae34af202a1b7dadedb38374a0532d0ecfbae71d9ed68ddddbd880f35fc711ee8a8d98ffea80cc6d6cf02afff54fc46ff84f2991d1e1b6a401ab998f5605b7041570110f6fcce16bcaff3542405bb214280ac5a0b9cd24023c171f3780e60cbd292359fec0d0333baaf720c97650f3634053cdb6db6795b1ff406088c16f80e1365574df2c2956b1b7229d1a5edae8334092f648bd8d7f9a35da31b49f68aefe10a9c6d99ee1da197c554bbac228d4f488dbefefe461ccb2ecaca7d2d8e48890b6119e46d5bbd6cbec952abcc75b973f4cc3ad2ec99f277bf726495521c335c1447f79ca6f352c7c68022500c50272dc7c85fcb507814f6e21a3830190c086eff9b65159d4278fb2f0d597cab2c97a9aef2fe2757537bcc0cf40b8f2320a1bbda0b8ecf4a21c2879ed7c53e96cc6c8b2ab9ca560343a01fe13c3fefcd7d39086d9e36dafa0d075bd47ad4018371f4248e178cfec90993168260cb3f0103b1fa499a8b070713f69097bf8ecd2f6ebd79bc3ecf782839850f796e427c575d0304a377c05044d737c6f429fb369ce4ff85ea93f515e7868582e98b03e94568c59571fd2687d52b0d637348fb66f106610dccfe781f6697923e63fdf7d28a92a5dc975b8688df3ab261a8a4b128f39afd003a8d0df729d1a571a5ce8a3b600c8d036f15040f53f71ec9ecafdea52afacb7aac2a98caaca02089fa02b6094a6c672509ee9df71f718a7d047da8300b0aad128c91c5669d6bd881d20cd7a936501f7716a3f1082245a46b42f3eb9aa2d8949a78f1136aa62a77e4568aae44280328e9823bb9e507b0a446c5fbd5045ca59829abcc44dd214b8e908833626d1fcaa0a00fd560bd080dd5f973aed3ccfce792444400190d6e42c66e0405ad19c81bc129d48d3fb14de70a4648a46e0327860415c5897c818eb2ce56c5b1054d538c2453509fddcfbd4c1125fd745677fd8ab18267b9c2d5332670f8727cbfa7c8412bc48365e24390a0fb53ef49ff574994ff6c902d6438cba7a9323a072ebbe67f5c3dc06f3cb563d587c9350de106564153354255119784a0fa4e023e84e99d0dcfdbe70e3818fe5281b372a75ea0d8b45f42854be3b05259a76358c29e16b2651f02d055d657bf6d6916bd745bfda579c7dd2efb0080a47b593f85263c43fed0d222a942b61eec55568d3a57af03cb70b0dac4993651dfeed93fd23ceb1756bc88d6c3e9276b223511b555ee4457d90c6b58e38abedcf659e2ec8bac00e31ab04efd226253f041256102f87d70b09c34b19bdfc504776d2e2de1ff4abebd24fd79a50da4788f2adba632ea428b76f4eebd9b3277fbbef1064ac06ea4e1c84482e59b50e0da435fc34af1bdf490784b792d436e6282d81ec26a004afd7d09dd0f5ccef804863bda8a46fe46940dd3a80cde0033cb730087e34524af54ede8dccd969374cd63ff2f9009675405ab646678560dd6915446efaba2fdf369fdd6711d9f441c8b97a990fbabbf7494f7cb81e310bca078a2539f795a05fe64bcafd395c82910004c70cb0fd8c479f7a5b94f5c8e928cfef049db1fea31b801f29584a10d5f048a968f52bc229fec3c1c5c3960c6a7e88a19a1849b1a6d1b09a44c61e96ea2863ab47ba8da0464e9291073c6cba4efaec24c94118fd577291cbdff4102dc9de0a1dddfa2139c71ccab645e8c0a0e95ab4c781d91763b8bfc046d2e6f7b533f050057da1a5af8ad2ff2ab984ef3724a0a22e61921106e0fa151a1ec0afb83d03c58a8b8c53a2bfe3722d123153da059807c038785c7c12cdf20e189babccf9b8935e62dcdf82de33f75bcf105d773b10cea4dd649ced445eacce77169fab68a07ea4c1be9fab8d2b6f16b4b1dae772814a7d8a50c2e78251662ca991b4c02438ed6233e1bd04a7b6d8ccb8a5272e8d3abb91fb031d138a8fe68b43a57e77a45ec04685e40339e3a9a9dd8a9427a00121fa38ef782a6a3623e7037828c43e75ed500281c34dedce745050867a13831ee8f80509e92cc78ac9cb1daf9064734751ddf9049a594fd4ab276e70c77dbff7636e7effeef52137400fe8fdcc2da327ba3ee396e8051d1f722bfa8cee47dc043da2eb036e88bee8fd995b420f747ec62dd06be1fd30f7ccfac065a63e30e0fd91cb997f8a5b667e60e0f139f74cffc8a5cc3e914a9f9279b815ac9c2fd6a185dc102f4cd33d56b1e7332e942b1c90eadcf4743a2a7a964702378c4dbd2c42b2e21cdcdd5a3ad76a8aa148214ef4b626397e15bb22015ea7b7071772426e18ca76b74b4d55cde2525d882857d3d3492aeca0339f8a273cb23cd4e4cdc1851c903b86b2ddcd92795da3981457a28a65fa74520a22e1cdbc454f4541848c20fa6a0e4415167af06fb72f272be0acbcbf01a3d182af2eaa2370c919a31b6745fcb7ca67698dded2c804761dc29a0b0575c2cf43d144dab47b61c120cf217954fc44e042d5447fc2968602020c3d45300479cf892ccc4edddc3dba6f0080d5cc8da1c5cef83d45e99adf2f18bb40468ca2f328d0531a55f0d6473a04ca6247ec3ecd456fc525920a4b0fb46b8ae41288a579d7ce575d7430c85a28b0eaca2d5001186de5962b0ba043cca190728ad4e08b8ed290db64b331394e3dca5b58fd15651465d95126136328e90c4bea8c7fa1334f13721f819694fe1c01edf98f7bcb8cd18dd8a12fac63e30bd61cc9df7f61d5e3f705eeaa2ee4c8cefa342c37fe6855aaf99e917de123085bc71e274e8509e65ee77c03237194a861ec5d2aebb0a4c920f7add1bf1bb411cc4564cded905c3f3220fa77148b8a8a43cc802723e32813a02552d00572bc348182270eef0a8f4562cb1486ff67d5f82510fe7438e01febb1aeca66f645c95cee0b49f78409352aa615f94507315dad9dd9e99a3966cfaa4ab2fa9bb8244a8998aeb86f6be9950034e8645ff1f3c7df81482fa82707d1e8f1081032c88e567df295c98a766ca42c1cc4ae0514fecac8a2f609ba7d99f7e13d21087206310d20626f99667eb58dd32e562ab799552008ec85fe7c12136d5c1aa9535fa938dd851cc878dadb66f7300e709798240c4d086c535d34a4b6baca6ca7389b9ade97ae1c63562abadf763695c82a53c92365c80346566fe0143ac23b6463013bb582600280006a980708aaf5b9662760bca2b37448be04a81ea37d8378c41213750cfed86fe0036829ecc5b9dd1adc5bdf92e21dda1865017f2207e459d610989cb1741adb001be882c566b61af23200835a1a04786499c77c9b6aae766283d8005c30875c5e2b0c888ee136d1f7c1b29e83b146d3de90a57fd1d006da3415e97cd11e073869112e1bcb313ae898d4c01988d561043eccf2d2788f33c4361d86c410d48b118b1cf2c4b6c15b67d5459e6b473b19b89d30d6b1ecdcc183c2298ed3e09644ca0b3686d4b8da52cec52c0dc87b3aa76d4dea6ee6f332ba31ff6ec542c8878c6c8203981b74eee2c6211c76366eed465636d66760dd888811365db4dd3d9df8801e0b517822db6c858f25f9c58c23a75b88283100476b079703121df8a235397835f0d880d21513cf331deb06c03e113b295cfe948127761a0a3962bcf864489fb4c8606101c9f0f2c820bb76faffb18edf12c2fd67a36b35824709a06de93bcc35481d03060fc00e4e58e519950e5e4177afaa78b114f9c7b121a24a064a292c964551293d0a910cdd9ab1904634d246eaee21e8d799c3319e85f62fa00e304ec92f7c08bc8e7390169c7ad3584716909fc3ac516f1eb49798508b4d02368843bd1a595ea51ea37fa7f61edebc8fa48fb3560a7ad0d01e9758049a94ab3f2f6acd0a3f3f5c5293c1668a2a6f4ec119fd130821c03d4a21a828af48acf8f3dccd9616a50e39add5387ae3c2248d5ebc79f1b9377c6c0b3f0f6848603812352bdfbd73ec47d47d01ebc0d0e28e028d0a82efbf7f9fb954a0038df1c8e42698b41c1a993775eece1693876f6486469dd6dd4cf452aabe3f03af00a982e19e17c2395517982ac2c0e95c0d7a46f0466261e9274537573db7d0dc527f0f2434aa2b925d3ebe35e57574ca460bbf97e79b8880588bd96bf8682b7f0f6dda7dbc5d4edf7200ea251775cb33f307017703c29296173853fe59be9a8d487e0eb106be799a3401e0eba4ad06dfe6bee86670ccd64804e044fdece5f50fa47b94079f442ff830c2e8df400c70ba48cd5354e0e854d5a298dc0db8c0f95115ed56fa4d60fa4e6cde8d45bf947e2b903c903d196c3f50ef3d6bfa48d5644f2e02dfa6e6e0acb3f05317846673bd0514c17fbce9e8f6f2eb80860686b4ac82165e3ca670260ff138368d47a5b07f2eadc9b0de44d59b4f70c3df011bb006e63e2e17718308446fcf72e1fbb3cadf5b891e0b200c9c87c078fdbfe5bce587c3d1f03509669b849cf6c7024a9a70367be7207262ea3ecfc0266364b3968e1badb163f2b41f9f338c59b899b6e8488a1636de76ee03429005d5064c72a32c4984f7025eb951d6b06819d6dbb768cd2ea4b1e68307bce8cd43938fb6564f5478d17c5a6ff6b4d1f80de5e935e806b59bc22801afaf84d97803ec81ae84a40a894ad9d71f23101d6c749cda2c7069ef8df8a73b5a4554419d7c413410139dda68877349874e9b7617f90ba15c35a46a4da6224e867ab2c279b1ad5aef190984684799d3229f765f1cf7d53c9f7fa52377c85414455f7e7cfe33715136e7047ab6b05b9453388c5c913698095ed06e05865d43e9a55a124a27d5655b7daacec6a669012dd2fad0aeabe068cd76be9b539e8a66dad633218cbf3cceccc504231de67878f8850d003ede2561246161e37d333372670944e4255a1b69c7ec823127bbdd345673ff7d29b209139381d7b3f5431f5a9bc4de7003f3be7fdaeae844e68cdd9774462c3c88120effd68d15afe120dd87b800b30885b253fcf89f52537a484350b4d16e533d7a7673c8dd6e8ce8b53d5401d549a75e48765c0f362f10ef898471d04d630134cdd0d26e81c2f137a8ea83cab39580ee944f0eb1f5bcbe8fc36b6fdee39cb7e44ab0e7572b803bff7413fed3a81d3ab563af0f9f4972b4796b42540388d5a5995f86e1cc1c4babcaedfe449d5f149cf70e0f31d2c2c4e003b32e990dcdf7dacb10f6e4728cef1371ee404539e90ba29ef426814fea03ccf7b6fe2694dfeaeb0a1bb7d78e18a4f8886ddc62a2df25bb06d2ccbb99cbd83854f213855dcf2eae509880935e4aafc1465f0b0bf65aff752dd81434c270b19ea6770fb71efcca8fde6be0592e7499aab834cf8252ba7bbf5fdda8bda79632cc3377c63082bb5ae12c05e2b0cf528f09a8333df8f44c8514d6c436b3405abfc30eb7d77cf60d096160cffbf50edd8d527844d6708dd4cbdfc44731e3636218c46e9fdd95f672aaa0f49f3825fe87e96d7093d4b3d6d08ac527869b77e62ce9cc47be33f5e7b7fd2d6fbedd1d4efabeb80e15f9c3eee4c1fc182190aff03897997aa492b538188bc5e21c1a80cfe2c8c65ca3b3bb7590685f178dcdaaf3be9cd7f31d4285c02135f286d6652cb7a88d96695d0ef459e7743e01e7c37af78e7319efba01180ad9c2679975d16368bc64d66b6489d2a3c65c0b12ed681481ad654b5da3d91571b4625c2eacd2085cdc3d9f19422be13a693109185cedc6e0c29f86519e3023a18b912af1b034609600000a553742d4abb673373718546c1bab2d125f14f7ecd10d978fea583a52c85388ca17ceba92618dcf71b95830737d6ce30e3493a0b733e4ee3f0df2ed1c464c0677f08dea2d4debf756aaa3121284117d7d9bbcf3d8f2207ecf75c1df16aec0cd196f0b6bb4a693dba2dcb42b5889d8c01add25b593be682acf5abd099da94378f10f308d6fc54b993f56b837c4b7b3fd73071c048253692adbc6f4a790281e63beae5ba91049e1027d5693a9857683be059b67b1586f02730e2d1bf5348765d53340543bf5048dd99701daa54875c4c890e45944939a9921346196efd3ad54778b63149876af7f87e5328d98c5e4c99e446bb044f8bef52e5b2bb38102df886f2f06c803757fe621432a04b5fd8976792689ce99689f4004a228fba0e5324a02e552bd1896d63f28d7bbc81644132807516ed813ca00c9fd2a8588113bb66be4c19484f478733f51728af25ca50124a72e0e7c03132e549350ebe850231b458720dfc6ecc6b9a20e540fb1bf6bb7dcbb2f123eb590cd094c2520d080f3d96942011b5846afcd90b8719d0807272607909d73fe58db13ec565f05b55d64ba00883a5b463f2949af6f8111c9e1e821aaacdb425209f42c7e04afe6be0494878fe3c5380ad6ba4bc1ef43a8e083f69c0069380149e36408663edc400e68af5bc639665c9a2b413e89dfd43c50dc92b42f5a0cdc260ea03c760d0c1d4d9c893d100026de78eb65c6049b0539c324c50256279bb51ce487df3db5440222e46a8b2baac51d1d81ba252fe09266739dac72504987c7bf2b70da5575b3aa822ab9f31dfec0a2246ca46349467d807716b74eca467199622b0c406b8e307695e693afb98d76f401bf03d7314c60b7044c9a303c8ef6c4d4c3b94b8cb11e57e9bbb54baadf852f2884db132d0f5f54058e64a57902bbfd8a305073d68b4cb2fc03b1aad3bd271b91d9fb370812ec08d328375a0fc94204dd7a75d19dfe3f81d80d2278912cd9a6a4b7ba0956322895f608dc45a239ceb84d238aa2b75d2f66aa439814c72dfddb07dbecbf41ac9819f0a6f4f2b51bf87a2a5670ef1b0726a02c7f72a2f334e6a45b88e6dc49c7dd82c586f82aed202890d509429a4fcb0eca49aec87d41bce7b92925369be9cf09b123a70788bfbe1449c9c73359b453b2241a1985c8566f284f2dc3fac801b9c81652b86111b4a6f9ac5d80fec2d00da8a064f5e8976387640028b9f95e2112edf416797bb36483f6ea74ce10c8098ffd5bd4641ce6dea12a4c294895de6bcaf7ab198d53afcca111921b1e3e97ad077fefb4b048c3383aa5648de27c8468620fe7bff45f248b2966cb2d49369e6c1e2880102a658fd472c640b6bf80aa4d4101332497d4bd399784fc10b19a4ef5b6ca6ed121b704c72eb022bc60bd8a7d403eebfa10adbf7d0701c99dabd6df9f05e224d8e04c0a9e48a0b93b4f17236579b8cd3448c21fbe836b1051a13d223810c9e541363c3df1c6ba744479120ae0d863149023724c5dfc0f71bb3741616361a42909d9ec9862be54e36b60af1897e7390b77f70c295dc0c4fb91c9b2424b59198313e091ec5b93f5eb29b7b7834db97d6f45ac400ccef6764db57ceacaf594e6a993e565e11e255f404bda02032cb76b056c523667c6cd5361e4545aed67c1c39c2dd3c6f38de20f3e12ba7c8060502e58f24a0d7e96bec762126714725332fea2c68b74d02af0ce8de26e8777fba1e27144017a045d744e9ba4af31059d38827b9e24bee3621cfddca9b6103eea3b90f38e6f97b66c400e1411e1238553f85ad65e858e0c5e9e9da5a8e3b678ccfafa85006f286410a87421d3881082f745d0a5b4f6eb0a55a8c8d1e58d8638ff5b6e1fbd419c4ecd34305019ee4415574e68820ab536d0441100a1636690e59376a59fb14201850eb8c3945d99889041fffe6709b01ceb3fe541c4df0ac4733287e976ec586ebe70dec2e43f4d50b178a42c64c98e497eb3e904ea992efebc38d1473edcd9beb9ff0e27e03dc2794bbf5930daa200ab818b048d6e54e0c62bbb72e38167af6a2f13c85bae9fb835cf4fd0dbcf4fcd342245e601076e80991a91090a806a0c98793a98493629c36f5fe545801ad1bcbfb2d7595827148404258e7ba44d23409fc2ed86dd50b7ea5c0621b78e7ef3c51b34a26825d1ada89166e653ef15240ec43739fa95a37121c96d01b705b7d27e4ad9b02f06433739e742f4f0671a3bcbd8f5fb167477c00d0df6b6d9b03052b0c8ba9d74733f15d8e429e1d61858ade2664a883a618c73eefa9dd9cf8f082acb4daacb4279458ccc22568f1edbc9a76a242eb9be815dcd45dd7a2c0f0417ed9a8821d6c94ef8eb5b4b84296c21ef20c3ed5fa7bdabeba089e5b91621779b9645474d10b95526e870d1e0c955695f9d478b61d3eb871bcd50b3f07611d05aed674307c1f74f654be65413576c0d6e5ad561a2329c8926f9f02320286859969cb34ccafa135b7cc2711efde3c2d80b1a2a6d7236e18caa07b197a44102e32a1357ea88102538a1229709891cc4b9ee2d5b00db6865992be639589bc1b683eb01f33dfe702b23fce8aca4efee3802a3dbf1f73bdea0359d12aa41cfd09f3fd12624c6687f8d42e42cc148e1f7e05ba41c7a2df13225076d0353483cd02379c83dea38c44117d7c38f013a98dc8cf05932a76cdf7ac945d7e55b1db5d90209a6dac1c131fef3b815448ec9ca91c44606334138f64d59119fb6e31a6492f64d23d31d1f65b5ba34d4802a72ccbec53a4e630f705668a8fd4ef7030645f618ecb4f69c5b400d600cdb049a035ce05ff70d0ad6a5e42c3f5cfc436cce1853ea5e91452cbe72e80034841feaf24ec75c357638e34cb6f9531ec0245435a0e5a04f4e0262010ff33fd84a56a5bbd65cfe1038f84e6b4c45b4f71d13c7aa304c3a9fc4baa6ee87b684982c1608bff45d0783f5f358ecb4b8460d74f3e68c7052d987b9f2e2e1868edc28bba25f5d9034f4711e708e7e8d3c7189be1898b7787501f4862e0cc2a6d5db42b42d454658f0bc92b4ccc9bac7429a4a08287d043c7dd4c04e1e168b34350c20c849c63f2a63bb282f9b810bda4c1783600f3833dd76840eb1a3999275a05dcf9e66d16b3dd6a79acde8e17c8620e14cec8c1ef7e4fe14df74fa313d3f5c09741a4ce4c61f5dbc279f6786afd28be310e8be9612b467d2f6f6da66a11c6d70003ab0d26620e2d4840e32022053065f2899c40e401adab9f254dbfb42a93271dda8a6413f922b3dec5c2c8b70df75d3cd06c9201b28b7223e34d8d44ec2fbf11834e9a1e4a5102b7deeb1630a61c70a71f1baa62f2e72e3cfb7a94354ae74e091138003b35eecb0bb8102291c670f25e8d0965b27fba2d5e75545680cf41979d0a762243d2a3c418c31082b07413abc0f0d99b3e3ec21acd92fcb99b63006df117f48cc724dc45af4cfb68f7cd11d276f0ca6def81821ca04ba739dacbd71b1b1f9abc93a9bc6082b19ddd6f2d117db03e979177bdcf714f83e84b2158677a0fcde307b9f715f79af386a5f4333f06df26d87af1bbb57a0d7ceee85068981635f5f388fa32815011ed3631b931702181b27e9d3f9133437eab032ff7e5d572a4d434e5b2e675abc4681e67266f91ace52a3106b4e982f9a4f194b0d9e773cf02a82d317da2c815127aff486e79c6a86801543a8c648e7bb0536115f23c737a135c0594ae439ad2653e6add919332e9b08898ae5a9a629d54cd9492d5ed6da7a7eee4443f30157c4e340591a0af1f72f2dbe51e54bbe80308cccde668f4283e0421745e0152f05a1d2772abfee540a692183b387422a8b64f7bc51c3d7f06e89f1034540d69d046581540a5ea3b56b49d0f1afb3011209872cb01b9a5137edf2dceb670338e921ff0fef0491212ec78cc5fd37920f8dc5176939278e5902809c5a60b46a9d8441de747f089058cbb09aa2d859ce0be0be0848c03a4827082bc6411ef3dcc1a9057f9336aac6e1651471d44aafeb297d97e58e56a0d50754b047f49b398b837b0ca9c12b3c2d6e5e1b323bcf757310eab23800021f91c201d3e94800a366cc62f5d871c4b1aa4fa94f4fe0bb95560ada2aeb32bb64de0a2d5282d2c2a61d083b6f9f32db168d3d5d2506437420546c67a1a9eaa481c2a1b80f788b956c62e065585625f5f6a9daf63ae7bb85f7fc25d919c32dc89ccd00a0e13710b8003fd0b414b7186d568ddb395e2d5eab268505154df51698dee2df9fbb5a22b4e9c11001bb39366960eef3521fd521a83b586eba8187d471724b130b885b6ee68d3559c9e5bd14d01f780f8b3c56041a8f7e794e9d8cd4b3ba270edc3dcb413a98121a1cd70dcf0c928bc2b719a6b085560581c0f630656eb99647a45a58ddf683267edade2fd305b263502d7c725288acc273d0aa07569fad69296ca503246f27143cedd0eaaa79f6870d575fe9efd4298db5cba6b0e1e215461958ef048ce948ad4255c58d9b6c028f0b703d6d1714ec4c4cb7b9b385db5a5703f71cf2ab4bbd357e37d60668be62c17b118dd2914fff9af1a867f71b11080f81139bd9d48fef82708e5d906e6d0e63283bc02a0663b57d18473502748718b564b39f06ed774b7e370ada276a45a42974d270c1c0b8bb80f011e90db9f1ce3b557c7ae5d71beb769f367b2b50a24118bb970f6f9f979741211779773755187119902700ccedda8a620ca376959278305cd22c85635ea27509250a31011fa167d695c0ae99e8126e226fb311c411d5ceac94dd15eebb975cbf6644e17dda2f6f4c409cddaf395bb68a68fd602d396f7217c84752e326be8aaea1905b4ba38e40e56c189e4c8435ed1da37f55dc07a8f1151c7cb06ca536906d301aa0e508a0196426a20b0171b8a3c2fd80ba743eb3124aa2ac8544ddf2208d72421683a28d901d0a1d1f0aa0cd485d1b8755dbb7cb26ccc1e55eebdccdd8f11ef4264163b86f0b00da6d2f47c6e6e096e926dde4086437c3bd1ec039763b62100d975559d623582c79b6d02f2b6fe8eb9b20c794c8324c039231527bdda5011dbd8fbc997581ebc603f4631f015890c38bf2e21fc35a93db98dd43869fc5beb2340a7feb3b9bdd94b7d67c97d5061fca09f065cecbf12837d077bec28f220e5d4ff32a6f4cef7faa4e035123992404418e5a215290bc2b4effbf367d5489d9e7ed0f95c7b0cc80348f202a1a531c898ccbfed7c27ed046a2c87d9dff266a59014ffdc8f0b15d7a38e2f6110e0eb1b6447c801faeffc0f9f0daf0095d0cb474094851ea11fb04f65ff1af79a865108c6eb803dc60ee81501669e43b300e8dc7467e7ec42c81dea383160400eb2b5c33972ec9b510b51f3d13d526dcb120dff775cd3902e4c16d32154fb71706a74ceddb7cbbd9c09b490745dc59b26b6ec90424b2b43473d2b6433701bcad021ae049605a309a19a703cc07e256dcfda22e4770fdc243dc96c8e2ecafe8e8d79d234fa0bf434d8e88439d4d5f2b9d009d1b6dab00b7222dc7e48819fda1d47103f42e3f13f026c316869a94e01f4b7e814fb46477db5bee2df796522629039205b305d205f333b0f14c79cfe8f7ddf7cd1b17a70e627ab71bbd94228922bbce202aa270d428632e476599919d7d06e47b49b34fcb0f5801a15d3682347b21280e4907e128fa59f67ad121228f91428a0d38d9dbffbedb9d77de797fcecf7ad03b868edae656a9377ddcff71d415ba9789fbe334b2ddfb1e848bf7bebdf725bd39bf376d7b2ce5a893423deefbb8af6949f0931c47fedffb97e2602dbd66defb62df973357a726ab8a49a77fd3cb21e79c3faf8b73fcad8d42ec00fcf9d98fed992ec8e6cce675d1d6fbdb3bace5eebe5cc0b6f769966b97b73582e50c16f100c401a22285fef233db775bf653523b646bee3ea59c53ca6b7f661a54eb3a172b10ee87e47699adf485ada8de5052f8f233e28be9e5907be2ec7382f6b1bba761eaf56cef759f7da7b3bf187b1d9e9cf61c4c1bed6fd67d2c00fde76b05207d5995907d2b00c7996de78b3a8699b4dac118d01fc35affaf8bd3eb0dad82fb8db3f86381b7b3e3f6588f82408e7bb9396edcf48c6d725ffbfbd9a0377491e422bda0cb51fb4d6fdadfeb57d3fe5760bb6bf5af05a07b767998bb7391deda555abdfbf76edb6b3f64db2cf270e7a3db188a7d9f06e5b28bdac70230f47a84203431c5defefe563f148063bddddcb91ab73d2e8d5ae0e6db6f579b3309f2a50a76df3f646779fc9d0991fdacfed6ee3a75b0f2dd4fb1fd798e6cd36adb9758cba4c70ef3a7f3535ebbfb9ca0c703ef4e6bf3bbe701920e937b3ecf1147755ab76daf92fd3f9836fd83691a363fd8b6da7c8ee33a158e83f1b0b8fb1c8f137367e53b1e75a4798eeceebb5fc1f5efeceeec348f13a61a7aa2aeebdeb5cd3d1b2b3fbd150ff518b2e8d7a6fdcaf863e9779e8dee531aeeb495524cebfcd39ee6a6b875ce39bbaed394ec71fbca691a0fd0dce9de79587bdc341a5e3d1e578f23cf91bdfd76354d8f3c47f6f6f7b5eeb54d9bcfbdf6da762f086adafc6dce39bf729bee320f90c35680284ee9456becdf15a094a7e12b1c936dbff4a238a510044baf6d9f44f227a4c74f2201ad046dfba518c50155b4c67e189662a5d8b6cfe3c4974641fec4a94fffe68f4095f5f9a0e67182ced75eb5addda26c8b789c983bda9c53fbb047f637fab0473eff495fb32889ba9d44753f355cb78ffe64e3ec2bee5ca83e7e1e27e60ecc51f731c64f419e23397c7971f1da11c8e59b3b29823c47b6d67579e37eecde348f13d346abd2b4caab1a657e5db25d177befd17777777f3dfa7821ab7620f0cb255e14cc7d899b7f43439792e9a544bdbc6f528fe1ae8f7195200882ce42e968347a075944f5077387e5bd458e303c447af40d3e8cae4bb6bfe81f4349eb50b3887e445da453a3075fbeffc8b549f4f3b99f2bd30b4c8ccc7feefe5382dc147936e453cfe331bf97d1638b180da3c7f0459b74691464fa5152c1bdf72b593af8b996b9498f928afa038e8b32128d4620750c48595f40786522472d9cd7259b7b31bbdce894e4fbcb147d96972f97f01a837465a14afadccb4a65f6918885d3d555ca29ef42fa96ccf2659b5761b66515d740dcd09bd1cdae2f256a6cb1c79b9497144788160f0361558ee4684ec9f2a217a2c57f18ca17bdd8f3471f2ff6e85558f42808fcf9a11e157dcafcd448ce9735f4eb0df2a64f278bea5145f429de0361bd1e0ffc460b6cf007df8b5e6cf9a3a4a2480e740afcefe97f3f74832f029f7a386cdddff7d5714095a40f44fdc1dc999f00ede957b02e719fefd267c876cf5ac29074a37af4f162832f1289e69cf3b9d1cc63153df889f07bb9d3d5c52bb91ee9b6529d651f645e71e54c7d99a72760edeb77ef7d1b725f6ddab679373d62bbf46495f987ee4cd71f4c9b5ab510c65a4a9491234824c9677cd69ac4b7c457e45b7d1ef83ef07de0f3c0c78474207c20f8561f085e9f12577c4a5cc1e45382c9a704139f79515f6c1bbd75bb895e71ffd4213b8f406cfbf35fb4d83f756fb6ee4dea1451b7a98aa6fa828e3730d8018f1fa7a8d40df4a84ad414d412c8a1aaaa1aa8aad3a9aa4e5575fa31dc2c58fce969685ea52252e47efa471bfbeffb5c7fe5aa88d8189d79a771faefebe394939e4b80238dd1641a25bba891e1c70d65bd20c52a83b6609035f5ab0601900bad448dbe62d71a9b365d9030ea8f3686ff5e1bbfb9245e40e4cbf974eafbfc293a44a3a874d246a9a7a769f9d199a83070446cc6ee93eea1123a7ddd4aefe2b0d0c2c293b6310b3399fe5a988dc9bcce39f9350f0c8c8dd92147c97c7d6b85ab788a8ffc4526572245a8876dacaa6c4c3251d9300c556b63368a1dd240181b73c92369acaa5d3f1462c963b792c7b03e290fa1e57c37e8d54d341ae769c2fd3a02eba675307a6b076a58ca99ea5febed6a360d69ed7cd73ccb418098b20f5da4efda94d17091561db87f62db36079a8e24732452c79fbeb3207b22c897d55ab31d3dfc405f44a7c8ab681cb26f1401a58b3a38ea858bf56f3893d0c3ed1e094093a6c54cea46405b970978be283ead4f5f0a70bea4d9d61a7a2f088275fab894d2bbaf8b72eb269da9ee750a4c29a594ce37d9d0a61f09633e1144489db9271240b5d6eaee8d76006e7fdff504b2b1fd3cb2a3fee70267fdb9ea01c45126e9e4e78859c4ab6ad79a512b80638e1320acb5d5ff66944d7e905478444818f3b32bb0d77ae387888af3bdbcd9527cc2a23f502231f068f55a7588a9e769928b2f198645716ed01afa1704c37fd14ed6a6599665598db948b5101257e24fe04b6977e34614ffc310a47abcd3a7b7dcc9f67a32fdf500ac6fb1d665d9e3bcbd6603a87dd5ba9b9d7de7e9587981125eb0339daaaf693b5cecb4cbf2f833774a4c51bf2e729df4344dd3a63ff1335d898bf48aed554818f43323607dbb75d2ab557af6f6c6dc26ed539bc6f1fea4c73302ce212457fb1317298e0be5dcdc88e23f12243e0e130148ca5eb369ea6ebb0d698549c8891471203ab4a91bd9b4e629348f5ccf0aa0909c4e7ec282a6ea5e10a4d42f4829152f9427b289c3fc67eeb86a4b97e4b9e159af42d6d0b7c2a65cf0306cfa52fb9df465a52c1816785680f5a926ef7ccff344f6b50972ddb6e10b8731febeefd326f053df6b0f7eaffda73551f6f2f8b3bd07bfeea5e73d0d1fb2bdeca3db9de69e06a5e198d339734dd3b26aa4ee2560397384a36a18ca305cc2d9fcd4fef6f6b76a5fdb5edb326e07ed8a2baeb0c2c341c3e7f6d2e3b48eeb4952e46be0b73909f6b79e8fffe67fa7a52fec6a4205b7717780f9becc72cf29a9a42df8e69c5f0bc0f984e80b54d49a793df5b76ba975d916c59950e8f5df3c1b764f216d0a59d676e912dbb22c4af1a5dfa235501cc742b169dba25350262efa832d6a5b74896d653fd225416e71b06bcb5150c8fdb5e622e6be9da66fd5565a12f03703709338ddbdb404d8e55966a7cf2560397329a51aa5da270370fc9f719cf02ba992f4add509fcb3042829963ea64ebb8fa52e62bea5da679aa49432731f0ce1b5b9ef3c1b3fd77b4feb103b2afae037795e0e3277983cea6b936a1dd94b1a54fb6aba5af622d3295bbdc91339334e896787c32be50e478df786a37af82108fcf8c9c6186350ca4964684b285b4a4184898da777f5acb69b325c9c6ea5bd5ff795f8f14b6d44926203437be4d9f831b831ea7570943ffe1b4f6948717749a0a42fd83b0f09a0f6f73dbc736e7f03ff0eac3d7e929cc11916b2c71508c561e95e529c95c72c79259332b55f453130e53dfb76eada7b9eaf651bfe7307121207ccbc899db9097626c1ec41ad697ae38f29b6d6e77d5291994db3b7b7be265f145177ca07f9768a069f8a3e4517e19ff2d7b20a65ae44d7565fe25178b02fc5552a7a04b54a943b29d222f4ba0d9fd90e8a7eea223a9ac715caf53835bef709701467142e667f4371a6cb6bb2ff7006cd209afde702f0e6b31f67100ee9b38c8923e38d23719483365c027a14d98f32b28c947ba035d97b8e14e9cb262da025ac29349781b7ed830128851247762685124676ddd9bb2a7326a873e07e963da53a08396da52e03b4d6a0066dd1d736740570fea8a9b8965bf1a256fb9a54d1ae5c4fedab7a25c26893d39d65ad943a60c6caa0687554ea549c8d48d6a2acac4559598bb2b216655156d6a2ac4d95b57c6e2deb047f39aa88d4a93fdf9b380e488547d9d38aa92dd52c0c885e212840545e4a82cc02711990d700b958040e48cbb213d0160da250d04c658decf145b4278e26696b6b62b757b7ddbabda883996b5bb9b677af4c05aded4fe2017d2b65adb58a3b24c9cca9080bdb61deb6428e022d14c7b151bcc63f0a116b051344b6cc8c30b1e511235b5239b59d7e81b68d356955b1b1b6d72aa59492f200f4fbf262e7f692026548bcc6bfb2860066dac784e5931e719145815c542283b09f5d47594ebbd56587bbc5d6ae665d052fc591356a266d958d6f5773cac65b57f36863aeab19dc1b7ba28dbdae666f771b7f5dcd7863b0ab99db38a5bb59d6a879db776395ae666d63525773b6edc62c5d4b5733dd3834b5b0ac90545246e027f23a19964aa2aa4109c37fce5801f8d9d538ac69100576d751a6eb28992db7eb43ac674fbe2a2aca6660b535a7f509519b840dbe2bf80e9e3cb18215be21c45aba6f0b5ab05247ee2c40819b4eabcdb4bb71b8f3441f384a5121adb0b4842e25538ccc773096aab662605e4c2597cf3074c5b22f58d8c2b24252c9fac55d2cba52491981fa649aab2cf689bcee34539a2bcd15ebb4191a97f99a5bd1b008f785edad0563d95bab095bab65b3e0162cb525756238d50a0d54b4cbbaaf53b5286ce572815eb3cb99bc6fa4b292c3d24b0c8b7ca27121ba806173d3c5e86466742a67b9126f05c55c2e97cb455b2a7fa9542a95ca9dc084624331302fa652abd56ab55c54210b9491d81d527911d1815c4c5e4e602e97cbe572ad56abd56a45612d67b55aad56cb95b43cc85dce248cc562b1588bcbe572b95856abd56ab5426ab55aad96ca0b1c11b82693f972021372b956abd56ab5a2b0186cc8555ec4572a292e97cbe51aad56abd50aa4300aa3301a72423057170864854abe2584cbe572b95c2bd76ab55aad2693971398d02772b95c2e97b75aad56abd5f6ef66d8d08410526868aa6a2c168badaa4b7c573881266b2a99ad19d46197cbe57271393aab9b9bc372b198b0e48b55634288e27f185e213016be4a73aec6b4ccd618adb15a633a1d75813b20d8a320d05fdbdfb26e44073dc7e58b52f9bae0d563a8e931ccf4f87a14f7665f5800e78ff6b5fdf1ce07ee05c130a438a3e8830590c7035b3ebea1f397a33611a51dfe4c950308b84a39a59c330918bb6b9aa67de6f5689a96f7befbcb7ddffd7ad501fdbbd94d534a377aab0fb4d7aeb1ef823666553414c1bf9c9ea8f047a7f931d37ee44e3f8a4c3fa6a0b81f59b67c39716a7e2c61dc83e2c8f0b5e65b5c7c09bfcbcb80df05fe1a5cbb30cf3c975d1edb8a8444857a46d3684d9f386dd24462e0f21cf81a27ae9359763bca712a1512b2d2fc5893d07eac4b9c7eac1e30fd589b70f9b18e6057138495db43408e7beefbfe7ef3bfec5f4212d8dd19fcf75617bfc759265f68a7d3e94dcf23fc13f7df873f85ada5b1fae17f8f35cdf3b2cee299d74212d8891dfe30bedf07f9f6a74d8f7f7a3cc2ffa4e7c3e14c639c1e3ffd9c86bb2e89ceebc19ecf1cc2618c394c84e3f28d8b1c7e539612ff294b298a23cfc64f935d1b3f0e738e14f1cf68b2f1638efb3ecc392ee297e122be177fadb556263acc61fc9bd7c3619de362fdbeee4f8f19d8e3dc33df0909fc95a68586e6bf9e93be71f1d339b9762fa550e289fab54aa197a3c6174ddc89a346db83a6a482396ae62bc9c5ed6972929a07f3cfdca1dee16295bbc76dd1e3056b3457c33ca5f83d4dae52c4afe5db4911bf2983922445fc35399435f831c675cafaf57c40d911476d481cd58956bb3e911cfef0c53038763be92e9de04fe6a7d713f337e6cee9ebe7903adcd7d7215134352417a44b223dcde9baa8f93e7bffd4e27fb3f8df429bfbdff8034551146d8b162fb2a0a1f9ef4f234973398ea31e8f7feefb3e9f661e6b481c45f3f535258e53f3f52d7eee699453a6432dafa9cf227b50142efb9df2dc1f7bb4b1ed5f438c1972860c193031307428668d481ded5dd39605131343064aa266cc98539b1a0b981819cd124611850e742075b256f6440685d4a14b5c5486e7e36dd8ec028ec4a122130e6ac41e6d0ce5f5cca959e1a26faa6dcc45d7b28dd92876c85ae1a82866d00c729fe1f978dbb2604c6cdb8202a5ad12d6e68ff27cb60da36d1014c00eec3ba1c099ad1cb46a96984772bc09348190b0584f4c25337445562b193a4dc745a7254f1d6fc2e8645c3219bff3838b3d24010208adf1dff17aec783ae8a6d13b32313132313676f3cc6b796805f34d018ed91363164516858de22897f7b7313be49277dca03c37dce0f564313132b8864ed3a145e720cc3231370040c629cbadf3bbc8c4c0bc985842db324ba86c599206ff69f3362309486b5ca435f6c67c6cb655f2841c5ae30f00efc6451938cc0869b47c4740b9648bc445ffeca6e55972117787da2a71d17f7b9675047006182f647051935f06877e76b394dcf822f23accbd50278ea2c2515a134ef60d5b4ab1c2c1b65185d0b651056c63afd2c902420a2c67461a95b3a4e643fee721cb3c8f7c3f003f40820ce123e5430078e0f16301291f02c0030f4cbbcf3efcb458eb5eb2917d61e9b47c33e445eb1c813f6509fc0c19027f931ff036b9014f4376c0ef64068c8fca13f81ff9022f23f7f8185988d48f148d4ef2030236367b9c408f0b4820972200815c7a402e3920971a904b0cc8269510d944646606ce0ccde9477625499ce52c0ff296cbe9a21a6a085b4e9e0fedb43cc30d0d363b31668480d3c232e3f99c1690733e654d8af475c652a47f9355a4489f86dc2245fa36d92445fa3b5946c2a09f14a9d6b180bcb3a799e964d82d6c962c2b2d685ce49c59d46cad750b719f4e27b19b917b1e27e7002413f13f3ef2113f4436e2b5dc2236839ae9c961a6c6b6cb8f2b432a5a5384d6ac80663c1f4d28671c46fc8c5ce3513907216f23bf3c4da66f4386799d9cf5bc8f5cc40f9189782db3f89e9789b1274d8303f67d4a5620c78d462008822098226482a2d24303079a1c4a4230f4c5f3c1548027cf87653741cba7196c6c6c3a99ccc3dfccc3e69cc75ad65be769c51cf25cb513c36d98b8a1a18619dcc8aa8618316a48f948a57e50018c11c39b31173df6a528520063cc88828c4ee703fc2923e067c806f81b023c0db900bf930720809fc90a78544ec0ffc816781939888f917f308002b24c2b01598615840510700003005100020c209b543f6413119c1902c0c9a133e849a366e8b40c8363d369f92547cba54d434e22e46df464428508791bbc24d3456bfc85e4e9e4358526cc4598cbb649527a062599add9ca8fb96e0572dc686463b347eda5bdb457b85bb0d889c123e3062f68e7471672e3836423de46ce81e37572467d642254e80b06928f7823f2acf1936792b9dafe47e4a904df20a78b02f0d90723b2acd1e2575f84d6bc642921b4058f90542ae5638820407e7264622c49d35b080d1cb21c3c9f984db54c8b0a3b36d80080d10e3b74e4c07143871a6faf6603d5d1c1a1cbb9071d3b1e27ef9003c70d1d6abcfddbc30e3b74e4c07143871a40e07e7b8d28820821299d1a356ad448e90c8941080b9ff96813a5f485a7c3bef632783d9afe8c90fa51c3f3b17b065d12aa020fcfee72dee1473e224826e2816421ff93533c5e26f320f33eb2113f442ee2b52cda326f5f1e08c78f36e2872fed35fe0bc97612735fdb61878e1c1c2786d1d0087c1c7ab411c5ff3004c1074ad9d974443641f11a97c925211c0070034f4f0e5733a2082284a47874d041871c745e32dad319381af5314410203f3a7c55c821954af5b4a800f2a0b20ceb4502c14a91952329e048241289446289456939e5921094284326223e33b6ce18384295f364c29b456ee01c740ec21f20040eafc78f5c82e2a91f3f3e870c5d126281325cf4bff9916924c9a135393de4f06ee4f074d0edf33e302f3e43367d2d8fbeb3cfe1f9d07f79ece373e3471602f36253a9d447049927a92ce384d6f8d710c3d0a6f12978202c08d96cee71d0a30df7af4751468f1fa3c7f09e62db350d002137f04898172924754a504a319b1d3b7e86ef9351f2a314a534142397ac90a81a6e6cbc1e8dd21d3a7e640a5420f5e37d0c110448052a90f2f1c3470528904b503c9f983da365808440934b421286bf0c74fec833783ab2d71e86d7a369179e0f75e123bbf0c01d3c1f9b5d8a4d2195455ae3bf83f7dfd369d979f0e978dce8ec2997a0e85c8af5907549e8334249c807182f78f0d03c5a585678905478f0c872cbc464b92dcd1ee65f7c320dbee86b5299a2c834514529f60489254c8084295e4da63b0d3864023e5510038e521a9989a8442d7865b9648846040000284315002028100a0704a3a14894c4b030cf1e14800c83925464541ec8c22887611046410819640c00001000040c0c8ccc4c6d1000908d82ead7855cc99d30943253ab432f2362077e83dceb5186f532895ab1f6054da2e4c1481d282159699cb191d6d43cbc668bcbb0069a3911739416e40938a48f460a546c344776073096d10ecb8491d8d19dce8bcbf0d27a9d3433095dd16c15900ae30b0d55960990c4a0ec76e4c71dafa4fac047df5169e154506e2f28d80bb45307684e357db03d0dbdc12c48d6a0984270325688a2bd5773cf1d9b429e2d7b57c29c60254cc238e9d5d310dd3d23d1118cb87bdc21c3ebe15e2d35eb57dc120dd404e391f90c839b5c31ca5ef28d758ca3d0a9ebad27eb471c438a1cb1605bdc2731a71e07559314ba7cc1b673339bd9ef53de8139a5343c003bd1d06248ca7ea0c4fa425d50721919b2d2d0f617c05153ea0a2b6d932ddd982dae09499a4c38eb9a0bb3861c2a9257d31570d3cd8a21bc158fbb820a7fb52f431ac869e5ffce443a029b8eb427a60b987059281fd7c12117d2a1dd7b54669da28b88444da332824713c948c8e9caebabafa95f9a45233302bacbf378e1b77c41d7209b383d614a2e3d12b0c716c34ac092bfa43599dbc6db09476ad075d45863d80c55ad6b2078a040621d57a4686af30ed613faaaa862d4425fde5912bb6955172dd76fd645cf8334fa9c3f32236c8b9ef1d04e719d819f39330e00c70125915530e10dc2e460350d3ab1e44f00c298e1dd87a10c25a02d279d3c456ac6718e6c71d90453b140e0282851935181d58601c9bd3b2b830e62ae89924b86ea0cdc1b1d0f0470d11dc5a1e5dc4e67ca814f686c1345d50548fc1b7afc0994f59f9317ed0922e5d1468a2dcadab9c1e84d331b46dc25fc48611d5189749334c814fa4099e189effc9a7f2eec1a9bf646353819000ddcefe50cb5c4540d72bfe81084863c57cf6ca5103ac2f3afe6a974f9cacca99c9c8e497bb4fe2a67c07756901ec8b1db91199a95e8f3115423056a676995dd5e1ad2dc4c5461c64c22cd03521f3389a9aa260fd333c3e3db2a87c18f1f31aafc00ee55597d0c61762d25c4802db02a5d8df244abcc3a21965c5856b8185d5ec278c36ccf0d69f35c7c912971e2a929aab04569dfaa70125005d692689519fd9ae424abf2f2d87363bb85209e30602904f80832baf97c26b11cb13391f5a6ad14cd70a185cd66ae400338ed2841b03a6c2fb9a4c16aceb74e68fb7cd595e526addba76b2cc93b15bb7519776c7a09e590768bdd090bcbfde1fcb0cbca7005da3cf99c7eed6b9c6817212393a457a1eacb61510fd9bb5e62c66c704f3e90cecfbe4526d3e2c856e9e17496232df0f03083d2ccc1aaa74f6f1432361c09c546ecffe1e79cc4309e04eb930d98db4046d620ade22949966375e3462b104cb80f1160a7c32fbd0094c5e543223f01fc0b09c574bbdc903e86b10c570c0e80f36af60f0a509224bb6288ac5b4753d6f4959723b9b5a173648d78cd8a82da230aed492c5011c587f0ed977a20dfc1a440b70bfdfce5628283124abff77202fa2efaf027899d0e28050e6e2b97902c92d1ab615024bf6ba5fc2512ab9692a5a3c4ac0d626226dcec1c53752d08e085a913c1033174cc13738ec7150b2c1b7e029aaf629e98877e0eafa087597c996ee374a0cdd97403a4a907ba120f7d77732ac587f4aac139ef4f1522915d31ba08e6813a6b60de1efe01f460987c670333b1779ad8ed35ae8790a112af27c08158b14b46ac101d65040d1d2635471f4d6c19c53d91ea44fed034c14128489f372a4002ddee6666b87de92c1b40d001a9bb0ce9a2d24f586e9987febe4c1641ad4f220d9e93664570a2e561e365a8bf596288bb0428b19737528f7919ea40c0a8ddd5da16abc8db0e672f43dd46b48e6a752bc2e7a0ba10513abfeacb7041aa358e42c0af4ad9f0dd18834b04dd64fc328ca20a1ea7ca0bd90c4762feeee46fc463205eadf7512760b948bf0945adf9645e136b98f492e13b146365ea075e9cc00c9d04475d61fc54654655f41e98e1220337c7a207c6dbcddfb949664a8188bdac72caff0ffa0477e2716cd3788cf941e6f9880cf3912cf19b4cbe4ea68cb2c83c20e700ecb6547a7294ed2e003090b4d8eaac327bf41aa9725611102362de3f57046ea2b319cce5955e2fcb31a8b7f42dadd82370d28503fc03e23953d25b5ac92ea8ce37aab23f151b7d12bf2529768e1d2a8f31ecfd0099fcfeb4837a67a4e2ffdb417e07f156e1500f982f20427fec5cc06e35c626136b6c8c53ab9f7be576541150815343de18f09a143a9b28bd35f0a2355e94a99dbd30c37bf7398f958d1766a8858ec0cce3b94a84fcb02acc10cf5f043f7b7a173a8e001d9bdf4c6646dd6bae8109db861ccdb08694e1159b7f8619d20772a0dec055cdb60b2c893a8cfb1ee57bd2a5e13a42a67ad07f7ec01f638697ff1b04dff2bc9c41e0f67109789c15f6318433295c6062f79ec60c93445543876ccc50784f164e651b2a1e1bce166ae567b40c20a156e8102b09010d1cc9b8de92cc2ea29cbd0074b70eb28b9c4db0588c7e49f6144ad96fcd585d26224573974ed3698b08e1c83a5633b85c2471b01ec46c939cc24582a88c95b2e44cc627602053321f28fa40f62d596bcb4604c52e774b85ba5f86521cac476556e7cfc68c588988ceda8a3e18cea66797191e3858a2e50e99d1676e10be0552cd550ef49c7325400fb3371393bbd6aa5c6b70a1bf7b4371ac22f954f6633201dfb095192e1f1bbe4cdd0f873e3b7bb66583a0c5c8178ed5bfae919519f6bdee0e4f76fb87a91d3a5012a8cad14dec7f199e8897dfa79989da0663c953c93761c60daa57e327cdbe17455172e69e4f7b78a0520e7e17aae37157b7530a3be9232b6cb132c345f2ad8ae52c470e9f5e955558da445751d058832d0864864e7304098d7c61b4e4cd434697252fb66c6bc893f039142f84b61acc5f3ab038640941444832d8cd81061c7a40220a5a086a63f260699a19f6f72e74ea41fdaa3c697e74d270a68305caa68d1ae67d17dced85160ffc858a558d3e206cee30443e33d41962200c6cd3af8593faf05dd48cc1467e954facc7da7f6678779e1ebb875c41404a6b84271eeffa3fea13e0526803e7ccede0591987976c5285c8c648b90ca095babcc9e414b4cab8445460ea36f3192b5621a1d00cf5ee3c33bdc2082e785f4d105e06bc4a2f9a61659cfc187f877c267259a3f4c3f22e5f10d4caff6c28cdd065f69e5d9a61170b44949861d68a818f8db0bfa08ec2f8e40858889171a9a609451a494bfe9afd9aa59cb9c821514c2003e0e2a16a92a43e547bdcc820495615cfdc34b837949ae19dc6c790150a288916edc310faa03524bad73095ccd1fa823459ccc7f7590393ead3222108658146eb8d6446090d81c74c280d480895a49c2c9776e82cb4858a77ffa7e2e29a2199bd5b687b7556bbec36997448a2aa6253e641facf37867becb786417ade7bb40550ef9aa3b3b60a6338e330b8c5282d4e6022b91ea86a96e84f017fe961f093b0e04a50b4a11f42707344b9416b86ce4a6e23ac88a978bd97f84e0320dc3448751eefa8a92c6afc42c235430764433c85a5b32e59a062a286b47f47ffdc3aed2d9191adb1c5e0b7a4a6675d289e342d3323319314c71863d027c632d668d4f8894c515f3f2623d19c78822e344d5f4982d25b8c708e979d6a41ac16ddd4ef6d0463faaf7b0c8f8095d4ce93905ce487f984020b3226f2113e55dbbee8bb3db52d53cdbee002169a6103b55643f4276da0b0477cd8c0e504884459b5d632ee997310ba32d3ed7ee2fdad57deb88d091572f43c098699dfce6905bc806ea6c22ab3f387d397682cd3c521fe9cfd878511b3b8b16c626aff0b2a8c474f7b9905583842e1f192ee4157286fc62d99e163c1921874c9c6bf156d2e65a64bab8d93ffa101c4da3296fcb5eac5ced67461e9d11ab45d4375a517b75fefdba267962b1a1f1cd8d251f13c865e5d719c1691dc18e556e5214e0652302ff08444cae442f5548f9f79cb6c1a352af4c8ca86e57f4e0651274d084c2d5c9d5a25edbe6628821a9b447def20ca706ff3981208925a5421b78f2de5328873eb4c240364cd7a8aa221c6a404bee854442138f158598c0ab5720e9b984da141177a1fdbc61a19aa2476b3062a38f276664dcb54e4faf4b2c67e60d84b2cf02792ee4b764ce4f9105fc698467ccbe34898b5b857cbafaf6682b96149663a3a9d4295cc0a822dd43875a5871344006285bb396c99f7e6e1f572de44af77d1a362d2c49aee5e7be30ecc8d3fc3617c08350a13ce53f189847dfe379c86b48e25c88ab3d6de48c8cdf6ad020da2559eb3284b0ab17666241ac82cee4d0b3a4bbd3a8e72556aa015a28e692500e8ef03338cb2364c7102c68b2448d10890a6f528a2f58df20701df93d13f7202f00325c42593449501556fe64c9aa8908300ed6ae8af27f5c21e29891f90a0625abd37a74b810de36fd1ab38a085cfd3ba2464e417234894cde72223b39cc1c71b1565d691caf1d615b07bf64e04e140b3585e3e93d58be546c094af68381e1421565b266d012f245a6c328f3a283e81471c03e3cd8be726e249a924b326e739e5df9fb957e24f0f024fecbf99131f95a45fee00b9a6c07f2f7114404f47b4c6bf0b7c2239800bc57c0f3066b9b722d58222745118663749ea81902a2144258cf530147eea0f0940f25a3cbf4a950dabbea7eebaf825dc21a3b2475078888229ce20877edb91b1ba3749a56db84d4e7efabc5f3fbcc59d9f5d482f38371ebcea9247d8b26dc29b261ab31d4a91a0dc07e06cbc8d27a8c3f165110d5f8f61c0a22342c7cb6a77b2f2ec03dde3abce7037798512dc476c5c71fa3738658adff54d9b5b5c39ee4cd255daaf7a27b293402d1e05c1089c8c59a2e768725b9f133a919a8aa5cc81cc5a44d2024b001e8e84a48e3647688b2821a8b9ebad703898dda74e6ae58d3f5ded3bb2b10fd532796e05d13683507b6120c67d205658eb1d0864d08c5090884cba93e019054af05a7f1ad2d61f1879b55c21b9f34ad4dd712c7c8e6d291ab602f9a59240b25e44a4b2936204e41b37e530bf904bffa9e157c5fa072fb112eaec06b363389d5ba82b73e33e7bed3ea67c6abf583a81166259d20d685f40fc5b396c4ed7d7e9b7a1fe0f6f23cbcd1cc162fdca936f38596439950312ede3d0e63794d915d99649e0e35f606747cb16645b17195bbaf666f05dc6ed8716e9a9528aedaa05e1e4f40fd73c36cf1e965eb67816f02964efba060914990ebda8fe324c04ba27bbce5c62c2af37da3dbc2933c41693d7aefb8c6337c63152907faa9b3798b177a5158181dd30d375a65d076de875fcb6d69e69fb3dbc943e5c94ea9888f4becd75e692f00812fa11bc98a93ab6192628152aa447d53d871b10f6e2b462af50307788fb2ef5bcf2f623f38f122f9cf8cd1dc085c334473c472f192d85b588377badb0ecf87f67c195235e7c7b7e671b8811efbaf4df2c4e27628978ec727de520660a8233ba390b3220dee0e02dca610883937925008db0b3f644c384ff7580985de7101a8041831b758e596d936a7d907d15ea12b1692def60899df88847c4c596d858d6e1ec6439ce29e4c783cdd70909854104be920974a02ef09c072e84bf1ddc517a9c4917cd1c795d721f63ddac3938c46f62c110eb35943f1b49ece866dbb5db42411b3590285131183f25be81920c7c67dc4d01058e81bdfb6e1c61d56020083c7aa899c9f03bdb69f2ebd95311af7a2883746962ee8fbf29fc7d754d9a7f92a1ecfd1dfafe44bc405f95dc7f1265e82d55c0c9b82db1ed2334c890f7313a9a736866f2d8ccc87435ed914180bc25643fca9710b126ddbf1a24608c19c3541412b65dc427bcb59073b071f07785946225b654bc0a67a1b616fcbe51635a6d339a81fc96e6f45773c57fbb0ece2f6a7ac069f4dd05a49922ae0123b7d66472584099b1d8042eabe8678898c2693d4a161113f95ba0c9b6e193f283bda0fa2fd2b6641baa2d7ef09512138189bb625de107094492b4bdbeec97664f2a44449902676621abcc12439622c62874ca00ddf2bd85ecc602e4a204f7e4a937eccf905a1165a25052c65d17524e7e23ea8042a710d61dbfbb4a9900d45d80c95df7431c2ffec61ee7d1fbd5bbc8c96ab6c0e78dba8a351469af7411278d7d9d98836d8b3dc535257d7727a39910ae19bfc435e47d31b389d7c269e10994ba92e00a6528840d4cb2859b3439678df756b30f0a16c0ae3dd70f1b6cb13e9ffc81efabcfc1ad9c0f7ef8e2f47f330172c9319723066c7a78f3de3936e8714f9beed5197a87ba149d5e0dda4906b783c5882973665a75707acd28b6f7de272d262eab4974301e964c74329ad801bf4b1bfc9efb4ec304d69a0b0363f522b86ef0b929202791b7413bcedc2e33e0f7ee312d9359e27157afeb776c08af2d706e3fbed335a155c7424daae87e3c7594eef21d0918bbceb51fe7a840d7e9783c161db72ee93faae09f4bce1c8de844eb59849302b5a87d80151e5bcfef3d411a384c9a24fb5e29cc485ace6804ddc119450c7c915199df2fed26b0a8f61461947997d199488d815fbe34407d38c1c056e00fbc2ce33c0e8ec3c27dee2365546168cf78e041e6b8c8f83f6011f0a53bdb51e425ef42c3327b35ef8063dc9823d1af845da094adf1cf07be1c0fc48cb87aade198ada363203603d279943fbebb951c1e2fd6429689dee630238c86cc7432ae04a81f39f206c232b5671a4d6131a7e5fd6600934d4718f1260e525db246006d9f84891c9fa43e864a5c7fe724334126fdcf850a28ef4ca9cb04dd73df55563ab72d5955fb951b319b7f7a549ae04fed79aa8d0650172ddde837fef5d672091b2b5da9fc22445e0dd61e6f84aa85bddcae5361bfb17fd7eca6590c8133eabeccda9f86a658e6487eaa6bef68c3301ec3a33cc62c0294b69d7e361b4e475e4b2f1d78160830b754d60efc6b1597bc38ae53468fc91573c1e199824714e418123f29bae2c59fe2bb6bd8e0accc33c8d74c4392efae102bfb8ded1eae744da4eb52069951d88b1bbb118117483950274852f6f31ce37f4a1cdcb9499465c21995c21cbf5548537c4e92e6c46df710a8e72446779fcc591f83db5b69b85a6181b508016d15614855a3e6f82a5a75ca93569a0faab90e1a2074ce25cd0eee4e68da05efdb181bf4a8f58ed20ca933d22925ddbed9e2582445d850736e7d5879fd2ac32ec20c7b26974ea5dd03ab1512b744a25651611dd479abca21561fa490da0dc0b9d3c343d57b79e80d6324531f5c18d3b3ef749f0bbddca4ed5b826a0a4baf645a0ed2308ab9d88c68b0f15ac3ad74e0e02a8eac7d01d2e7d20b4a97faaa9781d57b10ee7d78b1de100068770da3d3814662bdce546fa05b8895b97919ff39a2257f3be8a3c44fcf7db96c8213a3eb33da9ae188be403bb1406d80fa480398e21378d4c678b9522078b39ffa06d3ce76ccb4ad3dc9e0b4ad50ddb8e77ba6619c952b7520ea6f87abb66a4c6a21708f111e44a36f5fcfb57219b631cee0030c22427ed8c12a33eefa1e7e52420c28615b13485976525c39b45d76ceff35e99c4102a2a20b11d0ccb4c3de3006f41c2b0b07ae7ebf2ac4d2bff7aa2bb578cbf91f64c2a492e0756474eb9ed5133dd0311fc880e8a1c96640208d7d366b885e4f82ef456c95f309d4e3a2c5a048e8368db3a4d644992d77751fd609d35f5dbf0673bdb9c85847ecc3eab64eec8ac86f380437d0f2bf0a2b8d96d86ec01013e28da154faace544024342dbeacc0201b2374fcc6f03ab49eab5a5aada270aa394499d2d7edf8c4cff120a9a335b64007dbf35af573c83b2597a5d050ea0f58e21c609f84684208c403893b8efd87314261e53c8a994b6fb5aa90541d82c3f5973a89581cfd14f02f92e2e0dc52200990eb358fbbd118e231a6b9f4d49f6d9272bdc239f2946df59c71b8eabbb125617b34db0a642b13af2c84355935c4f4480b96a62b0ea8bff86734cc4b13f33ceec9e0ba2122592f68d7413fb7221e311849102dd6400d5b54c3048a651333925bbfe666585f7819e4b6b7faf80439cd11159a7959d8c9dd50582b8b921a2cbf3173d6a4d68cd1e7c8ec5b2514aa9c771deff5b60aa4729a785a53b0f9079013d08b6a77e532f42ac9d7c52207fa9b8e5e94612f31f8a5209f387bd077c172a9f4decaba314ab375192a405f61817dffdab9bac005000bcceb3167cde73a1a709d46390230de56ff9886a913172c5db74ee9cc179c9793efe8c774713caa754f9fe59e4eeeacf31f64d759e2f4aa75d55c932fb81a0361283acc9e752bc1fc0e3c573c31a25871528a9e378580d8ed4fa2571b3bc86d53cacb43e4edad74e5f7378067c5865d7f36e66d313fd77d17e9131715875447d3d29ec5789528b63cb86fe1ad286fb7d269a81c31a729cffc3a1717de355b7dd15bdc85bd83e992dce94c72a4cc7c934d07cef93672a21f17dc5489a4e9d8dcfefbeda76b26aa3d7aa8f8e4500af137bfbad14ab101a5a536119ab82ad82a5f368e31a34dafff006fb02ba48732b3095ad6723567afb9e8edb21f5243f3b89bdb0f587436ebed58e516442b2d99d2da675e96bff06a5a7e44f5b3c29168b72a324f17697e8b187cf37fdcf75f1d91f89d184f4d133607cdad2110bd3a8ba8659b6847c2ee58994cf5841cd82a690a22eebd3ef8e4fc9e79839f39e9049fb2e803f067d5deef8789ea8911318355e5efc739135f50cd37eb1304577871c03430c8a53889bab5a777cf6bbdcea1dc29cc899d03c3eeac38f422877b10697ec5e200656fd86ec78397b7c6a0613fe6d395220f52f3cff31a1cb2e74f878fd2b01a84e4c8d03464b88da1f999b58d79e9b554e5a9f4f1ef1d02007df073eed9d4d46c011ae2455caf917db720f8ee0c34be5433ac80ed45f0e16c8f4c1ab69081e30e8737eadb46d3c4cb1f47f13197c60d5f8392c814fb1346f1af83822f46279af778c52a0ef0f6757e565ab9db0c960008eea082fdcb77b3ca1ee503f1c646965b82011c3f44d0ce63011b2734ab893119d7c904c1315a0c4b60a777c36979322b0ba261cb5af560ab914be35668edf34ca59d5499bb6db3df42913a8e4a537e97fe0a7fa8a8036244513ca1e890b654dec2303bf070a4901267a10bd6b061f90d4bed03a437ad83a39dea4dbde59acacea0d53a0d7f61a62eb97b3690242a54cd7d3d340a9bc4af470f7eeb67ae2d2bdffa363424f212192d506f0e5db3ece7047246ebfe36292c52551231267f2edd8aa468a0aff3eeaf4e7aeb0ffc47c545c30b6d57abff092a14494f32fcbe049e6ddcf34982bf96c6c9de8da43914ddf0c9e8df704127524b0bd08fec4747d20f87072f398d0234143e57b346727749ee47d4d7524a69e709fbff68056e938319a17fd831637a8b385975644d4d74538db276c0f8adea5bf52840abaab54ac8697d52298c84b5383319dc678eb5f69bfd409e87b8c192b04ef1da80388c3e4817e0a4aa54a1842d81e0dedb50e2bad442008538da963d67413015868fb5d9ab33e3f67efa7f2c3b1f212f4a1744b0d124299baaa0bb8cde4f6dccc3af029f27030a5209e3878f616c0330b4b6c8f98c90262ee0b37f0d9628807412ec8af91f854db231f7357d5993df7b2c17b755dda1e7c30c5407f9dd73828504e78b6c4e11e3ad963b1e6ed2a9ededdf231ae55bacd7cf59b1d3ad850b79ef3fd88ebb110e69e987dab7c69ee5141de6f7714204c6caef243490c11874d262a3c3857b9b1aa5b4d72d91a160062d33da1fc52ea9475b09ad7d8747defb7a2dde364f056cbf890748a4140bd18a85632bc9c5cb708b5b809a2cdb4724e8f9127a93ba673e1501306cddff2393a6441832a5c8e2f18af46174e2c1a5bb0b5e11462c3449c51989134b9b4249cafecea70a9d843c5767105ffbfd679d455efb457a2ff4643220f3267f1c93edd6588681ac4c88d8e88ef05357a4dd9ba382c0d312ef767c189fb1b2aaa53604c7a7251c1ddea0122bcae32c39cb65726376ca8c1396cb41f2ab12426d3d66cda56226361b22ba3302b899dd74cc8bd75e539f8ca45cfe8e1b4365adb8e2184db68f2fc26e2c66d445d0e7ce9304d06ca3131185a1911d49173abb93afcb3b6a94285c36fccf9e1611cb69ba2b0bdac397ddbf6e2ad9013750be904098bb9169c1caf01876cc77702268f0932e6ebd1823e0de609e753c0ffbc2252cfbca85b882d0699bda322590b4e3b5173488930901015f6f3b7edd134f554324856687ca0c68020fb38307d8023b3f4f1a3d7649b8692cd11d3a736184e053f2e894526a5c64b855905d8f653e521a197a2b7fc23c605e37d3392808f4f4c359e5946d79d1a8fb92809a639f43dc2a2101db0faa082ab6ada2d0a7938c54b791a0fdb4744e903656a9f3d9d61085bb5ad7b37aff6f316a91c963ffff9a1665cc9fd4911ea07db31f7ab26c30e3cfb29f154ed9566f1d8b89664645b7b9d1ce85d2babf7f6b28ff2acd5a88ca9ea88638223100790ef8f6de48d4a7d5e59c309ed7017aa8ac45af1685dcc99f374151baa28793f113d35ebcb4a61c0ae89aa3d2681a18d49d0618e1e03dd50ba35f67c3f98b117b31341941a5fc6b8e03e4af147544a6ab6e6dff00507015c8d0c40a105c457ad5bcf8006140b9fb29f7d105318e1de4abccc3535cbfdfdc23dc1f26ca7d041f01fca2cb9d501b4f2a437c946804015cf48d2022101035029ad3e60040bd55b6012e9c0c4aad1d7588cb71631ba56a272264aca8644542c5e3f6a4502dc29be4c7c7ca7b93f82d08b4a01d07fb5754c1c72358f6acc8d903d79069ad64eca2f422071a85a9c5f8744c73706d1052e7dc22c89f82b94d661085a0b45bf2e48263b107a869eb0a6ca4a26a5c7b95c9cb35908622b5093dc1e43783d360e5d1d739bb5b93b847c3c5996dabffcad78a0abdba9341b63905ed9d7c50e9d58689aed40de3625e6ebe10e341c888b1d6261bad5a865bce5f59239349eb72b8739348fadbb810aa48b280e04f4294d3bb10e9ed1ec4328a98c4baec1fbe7f0d4661e0b44cde2a5a3bc0d3d47f36cb60a3b514021e3d599a7be4343e7c5eb6611b8098904e1d6d8a50b91b8b396ca5a91d70594338dec6a5a71fb396cf9ad76d87ced032435488acc4d5b5af99742f663180c9b8a3411491e27416a84279d10824419cd17a92427ebb7b5080d83f039493665b7b510235385f6e3a6b6d0a17dbb22fac11ae79ba8aad6a77fb05654570a2577c901a9f784271a18c3a3a1c2faf9bd12926fb7232a0dfd4e5b0a08b38c73c4c66c46625440dc65c10b5464faea7246893613d97a46a6d8d683cc49ee68154d373be97993b751c9351111ea7e61eded33f4f38a4be9761c93f9b86b9d38807a13bc92043f618ac94fa47e882000ea6c10e4ab60cf0d2fa078f46d77bf88128540bb98a8941c345bac3b12c0532ad016b693e332d607b44fc12e64154423277af32793698b4e825c135bdd0cf499bc437043ffe74c860b9272066cf950c9a25004d1e285ebf6517f155d30e70a45e82ce7d3679f2b098226ec3687e46b10c48f28bbf4e03379fd5948ea751e145bb3fa2ec381e525dd454ed028ef17580cef60b62519485cf22170b734bb4213ee05ef7d314ca358998404f5d0f85e842e70026864aab54f692dab34cef2ae9bd768f2cbd59fef6efcf9ae8e1ca9614fd4960ecb9d09714b1eee9192c6b1696af123d350613fe22a2ea33922193dac26b70b9399bd0dc6d81be5070c24a9c1de824f67261e19edd10abfe47e0d151b6098afb9db03ba010d08e5b8855bebebf37a044001d02b15b2a23b568f2e060c1a7d149ec94f19373f9fe215045b64534bb0c03e741855c1377979b09242ba09beb0401a8d3463f03ea61886801e3c542e6880b553e278138ed7f8c5f42d17e1dccde16081872f8f96bd2b7de66fa61af75f942b3a879fb47501efa4b3ea99d03ec2f630d40f1e765b137c7b6d0fa7289c27ce97c92a6f90a7a7d42f8f23cac6740d3f9a1151345dac863c84fdcd2c1cd35424909df8c276c9575d73899b83d3db39d0b54f9edcfeee00eb057df4563213afe8026a1520a4d4df50cdf684b152e556233e3cca3f13b45feaa32f1d8aeb30b967c7e6eece68a706071ac665636cc342d420631604446dccddf06210327d7c4e560e5a1843c9f0e3204c53a99f225732e3581648070dbafa9c6733033c7698759c9054f34129ab4ea5f08d5d1ec7bbf3e0fcf37509ab773cf7e5e4f71d09d193cf09a81de3a924d59c9ef20bab208f2456c1d8e4d2d3e369af630d1504d0299edf08ad13a92c2cba562f8ea268598cd9cc8aca8df70714ee2361d0fc0144ad78f9907c349a3821407de28067b290202cb7111fa5a925d960639d4a6ee201cccf2327c89aa10463d11eda4e4565382c4b3e63fd81ef7f3568e64e940072eb9a830723040531e1f9941afb71a9f4b78481c815451348af7d9abd588dd81b3a925a3d56649b6c948c3f4a5fd5e0ceb28a1e2b0b43159d49cb1104ec3d90cecb1cec7120862ede5a543dec279d2838c53e578a576b5f5b843e49815dc6af83791f72aba38c8cb76b0dc186ac83281e12923544004d45c8d441158c211b7fe12bac471ceb04017523b34bbf728c8e447033f48b82a06ef28e0e262b08ba4969614dbb230f6441003ebaf77fdf45e7387fb0dc79375a3b64757a658b67e5db81b2a9156d620f542fea8474c5bf772d5a34370583459a93bfdfeae1013f596369c3f0e37e7f085f91cf8b916f53b8f780039ab71865d172397d834d2660883383106acdf75ff2255323c00c03ebf159c03d814ce2d6fb6c801876d74d630e3042bd16ec28018495039c73af755fb28c0990233ba07c2fd71029a134eab3c4127f0b59bc49969b89efe400a2389cd17285f2ab289e7ba62b48f840d469e5cb8e5d944b985e7827c9c9aa278663deee96dd7f5b3520fe21afe8a4123db607eb93762a9ae3f9a3be4d2528ba32027de21fe5cff23d2ccc8718bfa832154b39a7ff03459da55abb357edd12b4444245ec3191fb5dae804cfe252c681862ffc206cd93dd9ad18980643500d92320651b96e58e3b4c947e3d0bd6823a3e4d186aafeb85f3403028268d8dcceba1da2361ffb7823ab2c98eb8fdbe58df280c69f661cc4834026629a052e6c16fa64c1f31e88dd09b94155ef162fbb10e18eaf6cfd971bd06c7acf64715009f74196d5b7cc0683fe8be8355c001d4dc49265e4f32b0c303f494b07fb8cd10a421566aa332b288b74ec4d55fadb7f2bf8028f05cf62f5c416edf455d03d6eef00ea85255706a94d35a4f5b0c0b92a9ec67715c794f1993b66a52f2130b6017350580ea1e6beee794199c6c9fafb8679340301383730fa39c31fdf6949baa07d2ad42846c8eadc8ccff9e24ba58a03ed0a2f075bc108325d43864b727fcdb796c7a1002f8baf1412f257e6571248df6bae06a475a770e808ea4fbb21e050e457d24e5a8a3944d8fe15bf877cef5d32d97b2ff141e4efa328b08768b6a44074551b8981ccaa2401125aaa9468ffafb948ad8368f7ecab30c06333794e8e5c9c1032b2247f786a29479bee4da8ff4db45745acef8c366722ec93fafe3fa5dc3e4ff681cba83eef9a0a160915da0f548129ad7f56d17c62e77407f58a7d6feaa6322e365611e70a8d9e0efdb7dfae1a6a27ff3a5fcecb957366217192e370911e738f0b644a725f7c4307473dc2f221189af9e170fed23570d945517a8b7fd3c667f5b5b8d367b601a8cd8985e4a158640abbb79f4650c6e9e6aca022bf8704ae1fb18c9ea53811b3dbd6405e65dd815c7a85a28b7fbc509651e409f42e2dcbd67058d34db05545ba585b9f74e4da615a6af32bf29aa9c773bee2f3e241bc92e01c313ab39613b995ed8baba868f4592bbb8cbedca93caac8cac1a6e36143828f45dd2f7ad88cffd87cecee9bdb90e68f8e119cd5e553941f287f22c1452908cc48adb360628eee8346565cc4c1ac686699798257e07cf1c124b9a30252b29161ff61b5be589b02192354c2f3c87a41cce4d3128dfa30e0ac5453d14fd8dd42863d8610d52d756c1660aa3c65bc5c2954b4948ef608c6b2dbcf5dc1b58e9d31019bdc0ecb1d529b55c16c4372788a224e51f40f1b77af1478e9d4194a9a85e67bf917f5e0f7669ca89fb12c489b91caff22e923fc7cfe15db41c3a7e3b938a0950748f87c7c92884bc97c018ff589e07bfe2c372ad8c1fc7afc2c01a664dbb1eb44c42843d48da248dc578e1fb3af84e1a44c2637c3fe8fd75fbd79b12aecc6dde834345ee5acec7ce3d6cb4002d8e6593cfeaf774fd0a296129008dc28af0630c852abb085974b0459cbc295d54958c6d8faa6f8defe3480e91af5b5a2bd4a039169934c2f84288f9ed6f2e826de42d385c881978744f0389690433395bcb0eb7abd30dab6796f80b92522aebf08ff0b8960ff5b2707e041ebdae870909f023fe1c22164187e8e808e8ff038a7cab592228f9ede81744fd9e55462d09a98d6fc0fdcd74a896cb3873dc88ce81bbf873c45e81d49403c09074bcb75a6074d919c83452de6713ab3dfa4ae00f9c97c52bc9f18445b782434725d28c19ac170948c995593407f72193ffa59505e2f4f88a0f5fd857d932e1e29b640a8400ec379704c6737f16eedbafa6361795d10ca4211f753482b85ef96d416338920ed1e2eb5f4a3b3f0e98d4970bdfb3be43974b77424c1c0c9c3ed074434687b71f5147e22690371bded966eb400330a4a2c485d7e06e036e540c70d3bb12a0cb21712a701e93f4759b5209ffa83806a4de5121553c75f2bba1e6d122dc6c916a930cc842dcf77f7b4d2383fd0aa470b25247255fb730536cbb6d50d642e66afdf6288b71c06a73db7f0335185df57f46b11d809fc06479955bad09012b30086c0e68c88f61b6488b3c67f8e31a8d322dcb9bfb041af7e47e6790fa5f3cabeaf627ea2a7bc63f232cb8a4c749a9eb4373bc66e168b04687c9a63dbf81375bb25c01c470e340dbfa70694e3aa0690c76346f00ea0c820efa2d43906984dc3b429ae646a7e30fe77e4466c3af4721d4083e77051c521ca34c339e54298851d8ee6c62b565db604f038e60a1fb13809154e8ba61a3e48a269f71a0742518c9f44b55c69aacc8905152bc7fc3d9aa9212e7451332086c5566039fc3ae6e6827f189b97b4c2574c35b667ee6338b5bacf2fc93f8e08426aff8a1aa871e8230e7c5008733b10ff0ac832575ec6d64e26cc395811736438b28f6594381f1a9b19911c6e83cd88cee3c28e4b96d50f83ca9b054cb7bb36a0ac497f31304b46b143a0ea3691da654ee360095a482b93c894db0154acb805f68381288fc3c35547909ee550509272b9cd7302de3b3f110ab2e860077c66b4761a29d504d96e1f9be67e53a59cdcc0d71076bcc776afa55441b42f122eaaf2ec01bc8baa2d818a52b0b600c54f65455cd5f04ee18ba0316361b6782c4465d7a8f6eb7503438a23835bc0aa132e42f2e6f3b777a0fb114bdc7a85906624a19b59fe6e9199ec6a37c6c181fc2dca0fe88d6dcc137182a7632b17b05ef3bd9bc7fc943bd4f816d2ef4a2b8c1deae56dd919ecffd9f327c0fd32ec5eb9ea97898a880e62139e4fbc6798b26786f6f57502be825f283607dd6a9cc1283b1b97d4dcb0a02270cd2025e509f409738479d4af7041927b89267412db4b9be045835be41197389a9b045b707698bea99ca3771ebcc2aa274985426ad1ebd54ed63b5efbd6f1ab3635b889f0d45dfd0b43d78e77c0c9bc8dce38c0bcde6636804db6aa2bceae9671a7871b8a14d72de4c0d1d9f4f408fe1b8a1af02f0616a7578d54f83f40829e14d4f25aa7c093ca14e1069d004a82ce38c777a480cf1b76b1548c39a330998a46b4094fc7c268bb9e8e99b093f223616fc6c9acf6ee4117823550a2f14b1ea9870e807fda4a8985993331234091f17c0e79335af5dc9098c00a491b68b2b5c0d52bfa8dc95e84c4461032a613d230d93df3c2bbc2a82b61068fcf7a6d0e478dc47ae9f4d3e10f7589123f8d560a2dc082463f9998be4a1c6a2a06c162dafa71d54a4403d2487200a73cbcd6b7c769502d4f1733f89dac33b4a948e62839dd1c6976548f4335ebea1c927f809c4584f7d9997330c1f497ecd3440f1225d484967723c932a1e1083a49f014e84fcce70d9cffc8aa553c04c06e51e8fdfd9458b464f6678a965634c8de0f20e35d1fb3c384545597937721801d31d200d4ece19159fdb40dc3cb8d19fd8757d537e680b1cdad083e2e44cf622c592897d292d92124fc4bba3688168dbe3db2d661bab8d45a4bcfc09846e03b8ee5236edaa3dee2fd30864f5cd8c11dafb582955c677bbb2241ac76c798a9c1b624c6e0d7effa589a9a6fcefcb41005c40814abb8a3124382683b0a4c9cafd79bd4f0958dbe4f7ecb4ea64a8304b35faacd5aa3ea9ff904b8430e608cb36f6e1995c61ba2de11f74a01a87af1a4489b2630becb123c490c47946ce28058239baed74af82958db0d3b8472a92d24df3f125b96196ae13e69617945dfb695e8ecdc2f7dbc55fb7e904f004cb7069a73edf936c36b3817c4b49911f087c5be6def27ea82c622c46894382e978c45a31d52f474f58d1f2044d210de0d4d49d1f447b2965974098c84c562eb2e08e2a18ecce15142749731aae97a49835922f5c808c06b24bc969a437f5b943903dcf7028c0432ad787afa5970f203108ed3b09ee32c7f825b07f6b7674c90931ec68c659c88d776af86f6fcb73f7ade5975b43134aee60015988ffaa24a1f6f20a6c5d89b9dd880c12165912250410bd9b4e3dcada28daa72a05e4026756c7123a72ae0e023874af50a3cf02bcb3bc7d3621979a2c3b584af5944a3827d1f4973a89182a6a0e60883dda13c00f07a24335d200d90aa54785c1dd3108fdb82658fb5e2d5ad3272d52df21297e8e1455c3b2bfbfccbe6e07db90fdc5ed17e2bf48786b904d987fa3c275ef5875fb8af3be6fd255ce00cb7ce4ddd7e28e5be2f3537a0cebe7d31498662c6b11683566c8479364fe9642eb9564584a23eb5d4dcc3906eb35affcd00469293a8257d6dd836381fcac4f3201b280e3851cffdd41ccb1dc5229b2c388d6a946b764df33897bde8bc42df8846d6a8d71789e236cbc18b23e298e3d47d60d0e1c4de73dfb8772a9523b27c478b68a7e176204376b885320bd18ee5fce3993f10b00db98d760500f7d3cb217510ff661018135210f88e6cca09f61b28c4bbcf507a94c865aac33750b28e52d116b98ad1208a74a64f30fcbcdffc0733152c857930f2864609aa56f7d71ac5fcc3c2f4c717ed506d1eb6c06e131a6a7725670becb942423686651295433e24a36e2620682c829dc306ad8e1b837f3ab6cda95408eb0b48f149aa420c56423f7234530339a004790b579f20fb4423e90a470f1ab4ab3c7c7e340e9790e976c143d6f7dcf86b018b3fdfbb41d7ebd1226c483f3e6a91764f97daf247ac5ac9b6e92e284b969433e0f471de347a9c2f5f38b2a4aeb375e2adc92553039713e08719de00c2fd6fa86f31f4cc4c184d27236a25052cfb0a6e43520211f7f9dafc2840320d7b6c973cba1b2f3fe7f964942353a60bce1e835c6fb88d1a2301149313091c768a754716cc3703a2f9559cdf376a833118213304c60e3e72c95af43838817bd804b656381cf6daec1e05b70364566d5ce4b5b910aa37163c2f6b75c84f51a126f62a4955d910177c50cce9e6f418fe325c1a81d88c196def46f2bc018cc0160f3798449759df4f8da80df66f7fd19e56be807ec36dea5c443cfc2c600ecd2677968984b01d7448bbb6f5f1a7fc334cd95c123f3c15ed52fcee4148572f806d54fe4aeaaecac65872eb97181a0cfde4c316f6441a557b8721e0f3b328f0f0f5cdc195291159b972abe44737c11a6144c3fe4c69178cf474dfc16e2201452a1265e1a1a88269f17e05810c67d2c0f95a21a9821ff16786336a0d8c95a2e7cfeb36c1da9a8803305f80d5382c73758a4ed4daa061896b9ed044396e6738e464493c259835a3ef39fd75ea35ffadff49f26c787fe85ab4665b7092c48cdc6654668fc771cdeadcf440358500626e170b2841b67c8297e0a0c25773ae99d2f55011347c8d2903bc1951407cd77485674c507d88e197da50ae4680935b39fa856f301f45732df37ae851624fa5a80986e51b686679d0302525ba4a4a9049f10cc925f2c1a0106aaf1b53f795f76f6646ef1f012b20720cdc5a4f6906b8ec4de667f1547fd57f9963057c6791a0cf8ed1103a45bc94965caedc12b97364db3ed11ed76b0d3d03e32d438230e17d4a1f7beb032918b4cdc7e74641ec93d7951ccf0e40ebc7b9f66a98771c045f8011991afd5a1b2569ff3514f3c946cab67c99928616e285780ed3042e80aefc36871abd5dc052a8d4dfdf0554cbf752c66a70e213f4e4ae077acf57f95330041e7ec342c15d1ac1ec2c3bd9ec2240392d47caa3bd72cb50916753ca16dafd432523bc00accfa629b62e4df88e7fa629b65b2f45d5f3d4de9dc3d90deea1108ebb8d019009f0eec094994e1d704dbac6ba626294159e40262e9308485617c668414494dbe7be7d0949d0a88cc1fbec502f631d4c2723b00dcc4e26e6a216047890987b7aece5e540dfc48ed5c75d6bdf8a15435d0a35a0e41f92dfe04f7668e7f5820ecf6a754c069cbee02e0e2430c980c7a1851d61c43af64315132815c63c58c2f2aaf4650b420b7ef82edaa16e861612fc9cb260d35083aa8464c617b20946858a5bbfbf8053ef0718f010ca1cd0e33a83fde613f0f851af2f71dd8be2cc275c0ba1e874a99f65cf11779315b3c9fb820810a066a1c716ed284f29757a9378c1945e1f3daa6989fc33ad51d954ab6356a075aafabb23747d1e7e39efa616beaef2fe0ffe2a4da8956df2aed90284b3793698ad04615e4966f25467ba68d989aaff1149cd00466f73424634e75df6e101ce4ee55b99b0d958f040dfa35e24c9d6b769ee8fca88cb4c50bfb53fa032d4609415bc51acca90f4032be1cb043bb73d0872903811f63dd943e5783601399e103500346be0d4183661c1611d925bc82ead8d46a60935c38ef6a1462e2fed235aea411507f35cb7038ce9070d06097b4ea4a56be52d43f07a706d4a4c95f38f2736a613b9ca0d93935b731b0926283c785557b9ab4736a31666bfcfcfdb8baccedfbff1305f39488eb4b53adb9fc26e9ff4b665872982da3f6b7388e501b9e3251ead07f2632ed4372dc14277d7ad38aa2430831459392cc1c237addec628963d4c06c8c15fa349e65db651b917c2d714a2f580b6a4bc4b3a11d9d910ec7e11ad2f8d84963303c04b2f381cf205a0aaab05b8d1857bcb78402c12c7d2cc4ee6132385d1282643529202bbc5e455518efea872f141db4f9fa0c6e17c29e10bd97de7f0a90aa8d5888530b122ae238c0a55f1d20fa949e0a8720e205a44a484023d0ce8207d33c991a453871aedb85d3955d490dd0e79b92e452f59b0fc3581d95e58f1288c4af62629145ed904e6471d64527ad0d372a746e72c21e15531c1b9a468f5cd8807fda04de0fe38caaa697650f1ce3973496a362ad4a66fa188f6a510ebdc0ed0e6c44d01ac7209cfd7e313ed6e89acb9f53d7b876264f97bf9aff51be1873375608ea8d1ffcd8426288d4eb9775be0e53ec73258d6e5c9a1558731c028c18c824c9ec8f59f8956234b04714ade34b5bf2ba80046dd02f6ce51944f3d9018c3eba35b36b2a2009dd08b03fb554cfa1e2d2b2b9fdecda60eb0a5954316e79fc43ef1c409ca1725ab00ba562dc8fe6d947697cc450862a5c975d940797196ea78c0030597942fb38aede05491420334742248c309d11bb2d15f4030e1d7fcec2ae6b81b96149942b153c0e4a49f6697c285464cd4408295b714bb696be1742ae59c226a7243a5b269071d491f5beb0011c52a102ea23c9585874636a63878462bfbaca34dc908dc8283c0cc18132f4395d5069490a8f71932526a9001c2e6e4c2e85f4fcdde96a475c229ec87c04ca882b1c99ca7a7b10dd856d14bb516783cd5b7e8dd9058f1fb5b884cd44669120a0a087b304705fb4b81d766374a4d4015458a483a0ecfc822e94e7cd7a92c424ac46c25ddccf0f485e0f87b5468b0dd3fdbcfc896698152bd30a013641b6d65dfb7febdc679445c8d365a033dcbdd4112f7ca9cb013398fac6bb57db039d746b87dd8298f7bb49d5eea555fcd539385b9d8ee01ccc270c1cba0d8f6eea83543bfa811154b8a8b4a79ae12200453a3b786b0eaf6c6555b547fe8893408dada444226cf35e7b78aae34286b78b6e552bc874cd30575a6954ef851acc407a563d4cb1d7094c37f752455ffff2dbc6dd7a2fd656335d5972968f594a656c45b263d087165d5eaa35280bec5a658f64ba5ba36f84057c6bac822d316ec61837dfc6b20b76f7bd9e7ed3d2332cb944b4d1d15acf8d048035e5318672eccc4aa14b5a29dd7d617ac17a2c09fc7f43159fdc4578bc0b33a5bc137b534ac24075c482d75330a686bc96871065d5f3a1261abdbd88ffd1406b3c78a37896d6098819db8cb6275eaa36746642a6b601b65b20e88333183cab5ed1474c6fd268d01b7c5ef7ac0df4a653b245f38f9e75d3cefc91acfeac8ea4b86f371d527be9406a51d82783469e8743240319225e084894092e88ecee01d05c24f811732b39a2248789f24bdaf96a42a697a7c0a33659bc201b567559dd48bdf90d6fbdcda837ba89d9382cc6393878f0f428228141d2a9295e48973cc7ac8ff53563dd4e8950b104b75716ade38d9c38c2aebce572bc9b91b379b00684ddeb758637b13a9747238ef530a961db676b218631940f52f902b34d47d0a1986ac87176e5b71409450e871fee866a4fc3e517d464e0e5e602a5f2da37d65c0ac038a65c9e43ec0ad6e1ddab5c00e34ba33fe117ce260307ada3afb82b1440857798a9224b0f3216f9f63f5492150160059822ed81a09390a82a9acc76f422531ae1764d3873cab714827de54c321a1546b5b31def0f2ae62625f37b3acf6a60148e9fbd9512339204431a03954190dea4bb39729cf73d7bbed208b1aec40b9e123a2eb4e91de3d53754ceb1d708da19d4254a03a0b7f58d2385776ed2b8c8c2baf17ba70367203a728050daee9a9a6e935dfb609330c875fe2935698f3c7090319a3caaec40d3efe52713a922cbdac878d70ea4107ca5519d9f1bde143ed9e187283de520501ee5f6b8a71c603ce12b03dd07d11e58655f28c1429901b6efa34913f46dd81722390d44410a0fcc884d9a46cef079c6e440ae6029da627a11a901443c9f4c70d4ed49f24b64e409f41a84b121c5c808eb22b9eea9b30a8794f938747484a4d0bdd6e05cf53e6bc86e5dc8939c4926dfb3f1dd5e23a72b94a1f7cf46d805a7c3c908a567beb2ec18bc6897333481c7cac289227d001e3fba08bbc797b9634521dcd08f871da6e3ba4fe5a979980a7e900df27158f49791bb06b1d566922b2f2e0d1518bfce901121ceda75799f1f9dc1e9e7882a72bc28177f421b331903b140d33292f269e20972f7859f734332fa0dd2415f1ad3b48260f13dbd463afbce8dde40ce4a72434f3eea990b4c4f85f0ef15af0c1bb58969c701f6aef01dc0cd48f6ba9abdc4ebf67fcc06a007a55c7f09917e19c5fec5e83ddaad579353a231eb7f81108b2fd26f80bc32c3f4320afd0da4c636877a3ca5ff62f70ad28d12a21ad02e85305548608159d53f2d710b339b7da72ed6e172317fdaa3cfdb653f3637ca2a3a6348607b1b4f8c98595fcd0290fd32e8ebcd61f8c750093d2df8c30ae51edeaec0a00a6b336e7778eab1a8acc926d04f565de90bd2759fc537083e3c3068400ad6eb72e2d3a3d9a1bd124eaf57b091c66fe5cbf916cd7b678747f0d825815c085650f5abe07e385046f867effabf2daca098a2cb3d8bcf6222d8503bbdacd228d5e2dac3916cf08aa0670561c817683f840d886d8f6364fa789ec2fc45aff2a6837636d82c7df06d488037e4613d334cc29d6ff779669232178ab2f2da233016e1591e7e26dbe9a1cb633e690ba67717fb02be02dc92036ad16a93572092b29a0e17907fbb2b74b487d5596efcabcee0eb35d251563ecfec91cefa5b11dd5c913c7b034ca47937a2d66eeeeeee46a9deb838f1b0abafd5b4b7f7bb9346bbdcea561c360d4db8167b49db5bee2db79432499902600929090e0998c803c142feb10abf838b605c535f07db08b1a9c1460df8d6cceb9000042000b1d7b752282282fed8d13c0031228a04c0944600ae911f45707e727e7e346c73c49640c9218006bc090ef81becc4e61768e4800f6acd7a8c8a3420c883418641de11cf2c3c0b36bfe31da979ed8104793ffe357f7f7e5ad002865b43425ab3d6ac35d4220272e4c8179a16203a38b845b403e4c80040131e8bd6ac356bcdfe01f873007e566b86d49a21b56648593e01bc1f20b945c483061040c3b200d605a0cebed09ac1a0e3c305175ca09991716115e3820b0dc01db33635353958b4a8d420e7084442b460091f80b1854b09e655a221a78563e2b67063704ccec335f96b8b84715e643846c3383638a62c35aec979b8277f7991b04b2443361a7639ee5eeec979ee2592b07ba334ec5eee5e1964f917490a69bf4ab0707f47a9998e9ca76767a7c7793a5634ed2d92b09d9d9d7b37ebb3d21daa34a5b84ada19a9992f758d524a279e46360d771b77649d1659eb49d1c9d877a2a64b9a67e694e08e9a3ba279ec4b0bb2b826328b8b4e9351a26a9d9e425a0389697693a97d70a8656a1f2a2760913bcebeea9b1912493ab83521a5c1050e563dc6f41f7f9fe326072606a7c5c2349f63874b874d4d09218eeab1ccaf9e069bbfcf819bd060b37dfc571aa056eb527cb289a4029259aff717c88307c8e3ef0b0f59f1225eef5ef4f22971cc3cb02319f9911fb9927b9e345bf2fb916d95393ffc6f7de49b1cec66b477e91e74b846b3d170f497d60142bbd1a17cea5efcc933216bee9afd7a4c9122c56eaa170f44441b0713fa1077633a15c6790d97c0a4654b131a494a56c8389ee4d3e501536323dbe866e0a58d1991f773b9ac3a0def981149a15265ce237f79dea4611da832e2c7fa9467a4f5377e34a0ccf21db39a691a2e2c99540b0dd924d5f83ecf080cce2dec4d8e86abe14fdfaa8dcf0aa9c452793f9e61c02e1ace7982f17e5eb905bad31938d9f6e8f1b7071eb222a570fd4a26b9563231ad1cab9b1b190ddbbc4029c54812f94b12c9222a32945424d28c278f8e62b091eb5d05ca597c8a4f2c162bb611bd7861ddd4e48851b90d4c2a4b4b474c8b45b3c3a5e38a1253161c7388e3162dfe8a4e4d4d3660ebc6d5691826ebc0530bd31547430d2f48df4a8a1b7fca91d3393838aa2c4c4c3970be3f27e7d366702211be88ff3b4ce5169e48576478e34d30390f3688b11a3bf0f4225f52b0a8cc548289230e38e0985ea61ab30d2966c09894f4de8475b40e108c518b5a097422cd2b530a192ac97026b5cc4aca1f2f37c5606f722757a30d3c67fef22243e9713ca970e79b637044221e1df550b92b568171e1d80c83230f3e401e313c60787416e7780dd73c43831d0d1536220addc99bbc85664a80110c3c4af09a28c13be2f906f412c290f56127d889cd33e04c92a1f42c715c91b0d5cb9f483369a585f18bf9976704266647c5ea9637c5bc54463cbe858dc0df8127920bcf243c95e6ccc2e359343a7ec66223a230e692018d74fc879dd8bc028d5c7f79e0212b73c7f780017b07f6a63e417e0f139a60f0965f4dedd39ff5d7b358483f40b23b91b1138628481352883e4ed01c0d9537fd30e1252414922384664646c82a4688904f93ee9a83a1f596ebc50c82e87a8b33d1d926f4f20525a6ecac75d6c723719627585d46476de93ee719da9eb32c0e77ec4e5c028f1daebce1271c98cd2a57756430e3cfc1c971936fd74ddcb2c91795faae2a66e3c050260c6356323334ac9acea6759303a7cbf96e5eb09b73ce39c16e9d8e6db86342b4aa55a73386d22b9d4255d63528d59e0891666249d8023a6cc17920e02fc901b98086cd40842f94b02d370c43db6b2f3da06a1f071bdb1e02cef3017fc9483443c362940ea351bf94e813e47fc07962910ca3342c12d1a1fced8a0cb2fc882485c88f47528c56a2bbc8f263191f95a296b8c5bfc81495fc356dc762918445a56dfba844f36c2f3f1a6dd88845a44dd03b427f3ee74fffc60d13d99805dba567d4d2e7242280203d03a7934a4b86e7b3fbdac712c1379a47e974ed5fc3374fbcb2def745227cb22059622e6f9848186a1aeee62663d3a97193038304c912e6699506e1b64bd65a60db2440fb4c2232dcb2833d3d534a2a6ebb80d41f45a477f9ac84e9e4a6807cc9db1798a9a7df7dabef29a5f45d12860a2f4081237cf365dd0401d4e538d9514c01285e67f4fdc719a504205f45e8ef640a864164b853c42140810b4870499869989578475a293b486403c2f9a86dbf22feca2d9c71bb548fdc0f01e7498145e44bfe4bc28068d5935b19d7e70cb2b343a4a7675277fdfb08432a572c7ba9bbd51c0c6aefc671dbb59dc5b6bb7d6ab70f75097363c2ff2cee7ec1a85e41da110fe42dcf146ad575fbdccb5d94f75db9edf2adf3954cee9df0f59855b301354ff7b27b80922d46fe5ed65d7ca04d811f0a64792018defc81d6e13233f3a91ae6df588a04a079baeec00fc8977c2df2d30e9ea6081a3641225ca4942d391405573132de4f4c6f30b91fb70f7d8a65561aa8bd6b41dabb56d3ced359f73a6cf41fdba7737f0c4cabdb27d5b3e2f20613f3a55437393a550d7ef5384ff776ebbe4f85f25239daaa6a7078b722ced3b10bca2c072a6d4af9034f474f474f477ef4f401e7c95878419403296650716241968f2aa294baddf5bc1f9c9caedb67e66e56e3bc8e933454d0ff81675cae1d3ac01b9b6ffda8c9c1c9a1c3e572b9306be67fd0502948f8c75bec427201798b5d7fe7adae1c9c19d6ca8f84ac6472e02dc7ac5632312bacf049acf035d8575ee445ab89c4cc3b4fc79032edba23324dcb9d86858a41152a54583529184c9122a5c6e63be28188c8c60c556774836b15b016f94b3e7949a5867cd6c22dd40cfef13407e6f136d8c783f8f537b8c7e7e0f07170095f834d781afc2d1d332c1a96c023c50506eb9861511a9ad6dbdc80353938397278ab9cafe6df33429343d359ec62e5e0d4b09e460d193a39c936a4172f187b46c0bf342d688ce91106415a944532311549efc2aac9c1c931c3a2343acea8e954c0432a3c6c6605449e858d6020d1c7887cbd44f0901569c50ea9a4b483474789a2c15b760dd9d145abf3c14f38e0f3557c78d0cc58eccaa9c1110206f9d0339252fc050b3d892443241c2160a0247a79467236ebd123a7d3701c4fac1e36b8c60758c3d2a1f1a0c801e46b70679cd37a9b1b30078e8e19560c8e718b130dc862fd87e1ce0e1026227cdf5f1e9d863136827d0e5c89ea900d02d2272fa9d40da645d8608ae46590f55ba649659c80fa1b53e814504041c70f6068f11350f0fec6201d2485145c52b230317d0146dd1771ef8db88edb1ee5bde3ee29666284fe2831427de7254eb21107a65efb22de3dc7c550bf8133ce7113ff1852bffd757c643ac94649406fc6c53ad04b8007ceb858ea3970c6c5b897d9f19113b2cca8fb254255b2d1d675cf759214ea3970d668c4bc2c99649e8d8858886d47bcd13a9d6572c8cf114f0da47383b2e941f57bdb365cc3f6508462df08ac7848aec4380e86fbde0bd54b400a4264896da00d0448a30921b2a4d184105bc4a2506e0f4889d1f69d9788201bcd686c7b6126d7f730047ac75f5f4391ad5bcb87fa0ff58156fc5f852c731293977416467491eba73e05d6c001efa6ef58f7e485bb02075e325572c473bf1398bc643ad520f76402c37d7f0f8fe0be07c3132842311cacd0178a6defde9115327d4f4a54f217f79e16ae4969d3d2148b62919497c5a21aa008c5acf8ab98f81a64d25fb28c4856e224262fc97122074bd4600549f7a89fd52067a0b725834332b67dc738b0fda5a4411a66aea0ac9f824a808472bf93555e62001740c9fdbd44022db9c156bf07b2649907b4c8fd24c6b8df300e32c68143967bc74d8264ee97a1635c3ff74c525f72ff656a1f9d2cff7a1745e4563b6e511006ee7b69893872abc10308c5b6efa5259e722f2d51846c808e6d3f8d8882c4b66f2f8627fde56936dbbe88e4f013edcb972f1b6869452da17945d8140ab64e076e42b9bd84744367dff0506aa0badc4b4a34a5bac64b491421af3e992d51f24ca94a29c1cc59cc97222c06f2c924d3aa48c2a42c42c23c09794909a22c5f4a30b27ca9d43ed16a5de96c971106e5df91a26423e89756489338635c24df6c993b5739b775befbd56a31f6f8788fef4f3193787fce153ed5cbbacc924e7cddefccefadfd3e7fa1ac87bf2f187e97e20bd24047f0e5cb17a64c393cf3a6819e4c41251325a7917cb72c096bf71cd44a330ca7b34bdd97ad72ea26a740194dfdf6a84b5333e5fdf4bc2f7a3fee200dd98b928ccb8a9e91f64219d6aa2d7165fa319461ff4739670b6951a6d10353041627e86135b5b457deef90859ca73ec6583fd6d457d4bd1ed6aa773b72ecdec633c2799e7724761c9ed93d779893b9c3a88b65ae7c35d8e7cfc9ac607ef515cb5c9e91954ad56960c5df3af561700a8e4ff6b16458ff43793ff7511773607fa84f2d71e5ee53de919ba42174c95d7378c67c539d873b77f8c32b19c2f1b545b95a277ec5b24fa6fe7c196ce5cb3f7a3ff5b98d7be7b6cde2586db533fb66c48db163126974ab722f251146124b431edf3d3618f495d0a720280019d6220290610b41ed1327561261b8880c6d37ffc2e0f9aee8a97ee2cedbcffbad23f3efcf7bdfce57927afbc27dd97cfb57ebaa6744f513a3fe46c2ecc7eedd7be13e0565f685ed27281342dd48d80465ab148d7999ed637e4b3dcca7fe46c22c28dbdecefc8c120fec406eb3334a47cd541b72fe4e7b14fc9179ae6435c1953135ad9cd9e77b509cb80993f6329b651a60f2ea9d862eaf38906713262c64d93ab4c9f634d4bcfaf68e3c608599e4306121c7d66112bfe4f9dded838232f9f4b112f933752361add81e015ebeffdf1496ef3aa24a9edf3a227fc6fc5c42820723aa9732f929ee056a44f58ebb1b096bef677bf75ea02f08519784f58f50febe880c9212a37e855baa87f9cebf8af9156e1dd13ee6b58f015baa6f1d8179144af5def946c2ee47197f4a1f067fafc23f42f973597f452528f03dacc45ffb0ec4328df6c810cc787e0529787b035f2f218184f5062044857c3bdb6786981d44e5186999aecc862c85e283321bdce7945856037d49410d341abf4468092fd9410d7c0d0e1a696f438e3504394e97d590230539e0a0510dfd36a4b41c4d318b69e00c88183698c5fa657b0af85ccbed6fd7a473d632ff5610971d6492cafd2d46de01fd657fc92dc306427b1991a69527ceda038263f55d7103a71820ccf015c608a28b1af8a0051662c448914417252d44434e3c725f71031b6498dc57dc00c90bd519329e0186110e44edaa653e9d746a404c9f3e507bbbaf53a9b81cc1195ec22abd15cf6c238dd94a2a65b64e7d563016b58e8cedd33a10d16ee2da59c7b18acd6673e3149b16c7a160a2ac76884520816125b3713a14d97a7a7a6054333333345d4bd8fc561169e5eeab203b3b3229d4474343639d67636df861dc1dc64718c678de918ec5f29804cd1cc119589e91f912365f5bc95c3916c5586441593c8a483926cd1bfd73f78fa264b1c8687bedf5a964ec79b9ae664a581ceaa1bb514993302cb844a6f8643d44f249c260758c0ee5cb66ad5b6428ab1b5dbff409d58b0cc7685865a343f9d50d2cb9ab681e6d4a2c92efda14d786341cb27c6d569f6a93f3b8185fbc8b2ca79292962dce443f67aa5ebcdaea45c25c73af5ba59a4ba5941a2f28da02462501972d120c19caef07947d35f608667665e7dd0d0e80aca9d16af0ffd871813a6492aba344e9e8b06840dcfd781d58ecd0cc801d0b32e4c8c131c1478f20372f9b1a970e1e2c58dc33323c5c3dfe7f870e4ee181608c79c0c080a0e6f403dbfc0eae3901d747017b090ff0a2c3a6468717073c40865b45474849210f21360fa03284d33ff14844510070d3a2d6a5c28ed98e591c71101982ca109ca3d43a47518800e074e3d472a24ed64925a487670598abc6d59aed20b24761f8efeea335d3a1f33f7efce82836f216ff7c037005708688640fb035932754a142a5085984020a27a4b42d296d8b0ab766326442218ef8f4d4fa160f32f49ca30b0a34e7955f608b2805dca2d2026e5581fd0082f1fc701da0101dd80f1e1f393d7a3ca3c8e42fa6d8b44586714bf4e22f279eee06776ee5e024f92bc987eb8eb6a4829042c457f4a5b6a4b6a4b6a4b694c0c3078521291079279fee07ee79201800ffd8e777f0909700c6c09f80893c0a58854f01fb07c135ef030bf91ed8a67e0ee6791cbcc253005be027802fa0836feab71c0343bc268e64784464d63a1203b86325bc05f017f94b0522437c3000704e520fce4172fa271e29260a47c4c1bcd33bbdd33bfdaa352443221e704b8a0ca750695571fa817dbcc8a780eb07c136ef03c3be0716f226e015fe856b3e07f73c8e0ebe69ddf014b9b1e14963484f8f0a140ff1b969512d175181c8109f9b16d57a880000002b20edf06107d18e21be97bf83ca977a22e023a57e08f848a9f779a41bb0854401103f1f9f3d23267c7ee5e4f89b75b051039e36e06d0a40c2f09fc5fa3e2b67a4d8a32cff8605f72934bfc021438614e972e0fc3f02ab1b272c2220a264c741336b5dae78d24e9b75e676c94dda2a6e903f7da118a5ecba20495d8231ffd0761929d8f4e9aa8bd739ae155f49bf7f74e901d5560cdad747b2bd7d4d28bb07445b0768ceaeb3767a40de42a27d7d1934903a90ccee5e3d075bfe123bc991afc43d0803f9d31650cf6c3df77eb8e628d80ada662b68b3610a4d798fb8377e524a19a39431ca30cb1ff91a70a4ac448213ec2212f2a7f862fca60cdb5b6ac96ea4bf2c3e59981ba85bc767ad1367f871f0477b922ea7d33929a5b5d65ab568f996bc78515232f22d7db1005adbf936cf5650966c64a94586548a3bbf3b4669ad74075b1671e5dfd4870510fdf992de9085327d25524583f9526edcc7350ff395f4c78e3b746c08caf6f6a1dc560cdbdb47b281482e2775f0d6691d27f2e6b7d0cd39670e326c483610b4feaade748136f580bcf95e0b9d27487fde181f088c5fb2e601d58fde0b156c054d6df38062b679b35bc70d4533fd92db7b4103b7353a5aeb01fda05cfe955386ee01d99e4031ccd5ca592cf9adb2377dcef97353300c4179414919e9bad487e5934d0f482604243f66b9f516a54f5ab50d45bf82948b19e78dd22928317739ae15c4a5e6d4a2c7389d4787d93eb375e2f747f9719d9dd6796037c618e50d5276b81594dd3f355b415b9e1fb3108d9c134d68f7fd2ec9e37dbe92c34356e4bbfd3be7adf549ec6add0db1bbe1f031ee65e31d2924fe0d9289f886679ef8deb859993a44229983e3d03d894322d88486cf092b6b2f6f682032994c9323cf502b5989dfaafea6e6792be8b3fdcff63b3ebe1d87676c1d6b3994cc861c41db3a11f7e06b5156ab5cebc4bfd8724c6c98650bcefad1b9c55cc128565a2cb725cf8c75cc67050386b002208ee082042f404307a11500014a9726b218a208478891a8d2d96f84b154049425907428c12422e3073994e068882a4b5c786e2c2610c24d208497715be7fb1871ad747e58a60cf95bdf01f2f9b840bfcc212bed5044964eb983c0128507eb2ef38c6cdbb6812dee956c4f9fdbb8d75e28472ceb72a421d7b7160b654d09fd39bbf265326f2f6bddaaa6fde4521ea56fe9574db359c353c34259ce18cb98b4ba3b9113d9dd6505110c5c444108319bd617e4004b0e6d64d71173772888c8be5ab1c0a82e6dbabbbbfb74874245cc9cf38951e71f46978bc321142b59e6dad9247d818552c4c6ad589f15dff67483f243cc5669b51b1428dd534abba12f559b736a619e7326e7dbe9754808aa6d1bda816adb86c2b0db36a4856edb501675db86aad0b66d888a6961726319eaa183826ad5e79c3a74c953cb737ac48167973e2d8a5b9da81406f36b15789ef3fb81a503942e669715734e2c4445c8f3cbd365450e44642e3796276db40e8c20e2074c1801c90b21e01003920aae80a2043ee0c044acbf972ce9c288411847745eb7e8da1b36bb82f4ec58b1e33bd6b522d09d70ee54ff8630e96a82ac8fa5b9d34a296da76ddad6dc977840065777641a841134cb7de5c09da5344ea0a21f4486f173dc69a59d20f215df89be56ee251e5491b9dc4b3c8022cb7672b4d2038294d267fff8e2f712b7dfeba9826108ca08c880a2450a142c64a0116b7f41c95e3071822c4e4770a306b16e55045b86a19451dac873863e76e24ba5285a3ccc92c85455b093210595a23865bfe1eef8845c3a40fcd107e59413fccf81ecf7d7afe8cbf9b41fb2426b121cebc69723c7fd89f9a6a54d6abb147cadaf554116f8fed23ef497a669df946b53f3f5bc2cd65d41f27c5182345ffc22d2861855b83331472f886a9a5674db356588bba24fe6a36fddf85631de5df6ef1d7c19fa3b115f6c9dd6fc56ca614a6ea51c8ab24f504ad0a876fedfea26ca0ac67c4df72766986fc84aabaeeaeba9f94f9a5b734ac12611348a2fff63498994fa9c275a59973d362ce6d9604b1691421e630e3b994aa34ab1fd52f07d5f6b822cf0fd453ff417fd212b51884ff67dabb8c34d7da88b861412df1bbf88fa86ac44d04a21f287ac74fcfe11e6f8394e9961f28bdf08b8010bcc08a208072a3892130a01baaca103204001c71a2b88f5b71229e750092c80028412185c68f1825835c2155b4a30c529092ee28e2cebc9fedd5740a91204367e2842ea414c16e6087401042b547890c10f49b176cafe33fbbf0fb2bff597ad948dc4a4cfe6336c6e8ad3054f283d5142e8072580f0c1172450638a2e1b404195192481441c447861c11703d9971b16e6cefd30788f6f91c40a7eb041183f883146acbf67f8aad31b96ce98e5e437c6a0f2451240c8410c76a63019b1851b5b9061052af792193ec89b1b8f97b890925732542a81093293996f5ac7bf3daf75a49561742f4b29b03c91945732048b6efddb5776b4ab57c32c3b96cd9e6630c53ccbfd4998e4799e8fc96f41c26cf62cbb9bcfd5adb07d92676b59a2f47b22d7dc4b6674c182b24b634cb179b997c65822a77263799204ef96b8b5754b6bd596524a99ea7af83ac7ee1e7cdce374e79edbac16279dde59e6e0fa6b9ae65e446aeeda9409e5d965194651a69d8c4364fe34cc2cbb34bcf713bf64e7deeff5ecf7dbd60bca8062e6c0d611aac48bc8eb71488bd6cf45bc3d23b106cbfdb593d398d4a051fad67ec9db92184a5a17c03b229bd9655dba19f856cd5a62424b96b152fd4176b8989265962428225ff295be279facb36409010220b90003962c3131cb457ae96e71e526f7d21654b68092c50d57eea52c5f2c65310a927b490b3696b4286385dc4b5ab4408b1c62da4803b5a5cb32050a490531b997b2d8a14b420d0f6cb468e55ec20269090b2a79e90b2d5d83aa3b2c3151246552292f7531254b293c46d91ebda34ce6b69a05bb89cc4e7232fd7e27395bee017497dc7f95b48f4ed6fe6a9d63fa321b726b0099fe4e96c901c8dc3da4b92065cdca526c7cb206653db76e4a22cb9cfe4ed6be3d17fcd9f83450269bdf5bb7d099824d6436da5ee6d33cc89a6c525026945b50659751dc4ab2c5b68f5976896ddf49b41ec4b6ef24cd850fb1ed3b49ed8105b1ed3b890a435468b9bd24f30c24bec4727b49808851832db797c4b900e1045c6e2fc98e2225b05cd946156374d1e5f692e4e8e1055358e0e5f692482286d8c8828a2433642cf1c42995db4b12bb28f1032384f8f225b67de32fb797446506194624e1490d56fcb12a532cab4d8ecc167dffd60ffd160c4f626c631203f7db3791a163f74789457518c5e1cedbbc93daaadd3c8b35ad46b5d9355d59cc41f1eb97fd2e9572ffa4b128f7cb3e227c1f11be8ef823c2d7ad201b41599e48add3ef4df815bf92fded1791cbd2a3597a9e657ce54bdd5dc618636c7a5138df94cc9df864d369fe5a27e24ce78380be2ad34927a5ab1863ec016debb8bc393aae39e6883df7c8d7a594ed17251f949b8c6db5229e0843fb410350fc5085192d68220aa5f86d4570c516659081c589085c6cb094c4952cfb729cac1b57dcb827a498b1294b0aeb594f24cdc99a2921bc5862888b10829086942f8a2f8630c41b5c52f005f5a26eb48a123135b58f349675ad0a103034954e19a352aff2246686e3869832fdb90f2e2930c10a8ee8e28317c4a61a4b48d0c1071e884e30041bdd8a0731617c305850aa28ba6f092e35c4d1869af19020d30101e67249715864b61978f688949602176c35054436d40dd5e48420a3c669a5ac3c57b55621a0f0e2744ba5ed55151bb484b01b9d938a0fe606dacf87fc0496998acd79c6162886900419dcf00320627325c51082d8a1c90a1fc8884d168bce39e79c73fa745149c3432a5bb6b8d66505152c9c06fd9d81a4356e0992bc2671dc808b1a284082135954a00936da30411533b4c021a907b5d65aeb4d814fbb518152ad4f9f9bd75a6bad4e8340826d2a3d785a6e2c4f6411c49559a78b1a41735aa1d0e88d7d16cbdddd9d096d722104db587831042a92a0c11551d8a07a51c50741306305570c8184dfd867b1967ae051185165c7b2ae26cd2bbd71964b4e11c40dd9dfe51e850a362f37962092506b248501336f58fe5663398fb542e51debb3f6bb126774fa152b6f28eb65e854f8d809c30b0691c229ed01db938eb490b513e630881294524aa48748e83c419288a03d619020417a6c96054145b1332d4d1ac22be80aff937a40d9241c342d09b6b271e9f7039feefe8419d9fddf45c37f2e54da1ee253c04124061a4e387125661347952a9ac2f0a1063784da3b75a771e5feb5babbbb7b2861fd4f7831c3f8fdbfab66d9ef1461de74e8846fb362990117d7db3fbe11f7f98a65c47d5d29128db3e9fc348d524a29a55aa5148b0aac500a7edf6acbb2ef6b14252e6e5e8550575f0f6ecc2090b0423854e1830d74c084920f94a8a2cb0e7ce8428b1454a1f1208330a2d6608232b82ce1c5131c7c0104082450b1850c4ec0021cde3aadb5ce5a6bd5a20481c32720871e86a664f1061b5fa2a871821abce19445125b4ca7d3afa0412678056ca71305b5d65a61b9d65a6bad730720d0d89814ad51822ee078dac11442242042891be460e8074fa400c1c4f2e1ca629e31cb6ebcdd9df42ee256d0742f28ce66b91db431c6281b01d7f9a6642d193a56bf86568a81803e08eaabe66c36bff3f45a0dc2503bb74cf6f708ca6a7210288b91d28f14b45ff61c6531b706d0fa918f5b41329a5bb62337686568656c6d43c56c83149ad246b9dadfdbad2503711c68258d91765af758db693df97217487bed39cf9b1d0aa5a5bc202ec8e3342da569cf719e06b6b8e7501e120d48d3522c56caf38022675fb6717f5332954aa552b12d8a03f2acdd82bce75ec3409e7dee72dc6b9cbd9a26f380b4cf7e5e90671f7b1e50fd3aad1543ea518f0489ea03a2aab9ad384e46e676b1ebbaae8b5979dd6fdff564be0f859a61b16650290fe88bdff77d1fb7f280b6e7b897f1baf73ccfe3369049fcd2813f3773e055794052d5a53c23dd8c8704c603d272f717a6e3305067ee69bc163a73a07dd917bf641a4f86d4a31490b2771557abd56ad5d2a2643c20eebb6ec6f35ee571ddd6bdc6813f37771e50cc401c077a2babadc00e6fa0acbe6ccd07f28f2f94636e4d30c83fbe3f27615470653657d957bfb60565ec81795b9fab8f721505651fe67027ef380cee1d7c1594b590690b49c9b2fe253d9e92130951ba4f3ae7a494d25a6bad3f4edbf8ac9353762a4ee5e9291eb9e619a16d7cfdf2fb29122efd4bf1d32f4feb797ef7f49f1a9d4041d16b21e13624567b23330001d559ad8d9a266b754ae79cd4bd4aa975146b3b7c31cbec46c35086fdf5bdbec4fef4a76626f14bcdb5d621bce2f657f4987cfeb28392064e548eb2cd35cfdc3f51f0c9bf51becd57765173cab146273acb333e6539a596a71ce77b7cfa8939cb8f475e22d75b6467b1f1a712ea0d4b679488126933a936393951a732a592648a5e7294f229b7278ddc56503f7da0ee39e7ec397b0ae5abb9774f2a29a5944a6b83802608ae2876393f0fc8764fea3533895f80a8d66d378ee3eeb671d603eaf11e7f6d1cbd2cd64542bb6da39c07c9a797ce39a99cd4b64eecbe9c272596418bddf7759e0cdad7ce43323dd0e75f8b9a40f37a41f4e7bd3300dd3cdf6fa62093f8e5e7c68c81625fef67cb4ce217eb511af204bf2837ce7ce720f78d8203374986f2354d8c0bc8308a8629e950fe962ce52c01ce7393fc252f9354d2b0abc54dda822b15fd923f83eb13fd927fb568586da243f915498654d429dae7befc4a83f6e9b8ede5d71954286a52fb3c719364d03e304eb44fccaa89f64131d13ea925da07a97d549f1455207a2b8810edc4645e5697b2fcaa24579079294416491089ec11b27ced0df95b1c593e8784247cc9f27d853bd03af263b093d6910f833d80559849ebc8ff70133c030dd388682736f3171b11f5c4669edb66de3af99cb0f2048d66c04e4116a1e490e54712b410f997d2ff00963af26be6cc8830c8b25af964910a9284c5973ee4cbdb8358fb7d0eebae961879d5c0c195a13efd963fbf6fc6287fca7fe90d74b33fd0cdf26595d6b2cbb86d6e51feed59ca99652af9b54e7c097eadd32be9054d10e3d681b1af3d14ae15837ded91588d032d68fda59a314e0fe893b227a764b0af7df59058b047037baacc02a1bace9af723231fa8b30437097e5e7c9c83978b96931c6a32895f822a05c1160d364c212a351ec42083071463f4ffd1b2cf39a78c16e51f5f8af7b87d6c0fcb795e7bd4f7dfa1ee71b0b1212b4d9ad0f051eebd8f9fdd624cc3dd3a16cbd6b11fb18c7918878f59b973c2640e3fc6ada343f9285c024fbf58c8d2865afbb28fdf8cfd3287a7b6cd8cb7d8ad5d876d4c8b5ddb75b8c668ec725d8767cc63b7d33a0eb535c6ab8cc2edaa8cf2702abbf6f98b95a7f598005d14f6d88d1d0acbd895b1a59ca8960606cd7bb9cebebc40ff0aa0eea8446cd9a97a14325523000000040316000020100c878422b158960552a2d91e14000f73b64c60509608644912e330088214320019430801001063c04ccd0c15002140bf32b94354e9cef9ca09c11388f6dabc4bb9d37844dc08dd67f7dd8cdf71f300822fca21b5f12af2d402e0afdfa2e1e3b2f8d2ddf87b2db62853c81d3477701c19a744df5333b8e0ece04be68816ffc1520e27e23469c2a40cfe4c1992234b15286fc1bb6db912056982f376d942c82b24b8eb672c3db5cb771d29d8455ba4e4abb90023bfc23482da7beb4b0226df717dd8538cf34d01132492b07ada2bfaf747c5641e886ef97bb371e829ba05b7f40670523bca8598db9334004f4f711f3ba993f439fa7bf84e573e74ed33688bf3cd8c8db435149a07ca7d9471611568fd6050e02a2a8532461dcf02f05f4f19aab1593fc3d5d7332e150e741d1f9f0fe1974ef911b2219f14de9c44e7b8c2153c24d955333a84630a650e3d25ad864bbd89153392e0890a9f3a9508417dd8de48eae70eb0fae935615d74c75b8709c9c4c2903481d56fea2ef06e9fa0bcb2a129d04d3fca4658e0db530b0b084cede8e3650f7ff2f4b6dcdcb33aad90a5f559d5550dc55d6842c7fb2e6239416fc022a397ec995c82473d92360802345df98bdfa842733581bbbbd8ef5b9fb8d9f3309996547f71a674c484bfe2dbfa64e05c155ae08f15f3930a9ce97961363e125e1976de844253024df49562da58183a4a5ff15dba24d4fbd1be8204f0144a029626dec07c5f8ecb5429b0ed1429451cf8de6c9f635f33f1050bb3b3fdf0a57f0bef8a027cca498654c3675c592ed4fb3b7b7fdddb901d12b8d565dd9081772d75f4e32c5352e7226cc8e6ee8ceb25d898be97c104b4414e9b0d6c38073f76e1e6c097ee42422fab0e9898daab4cd5836ff05d4842a610bdfe67ef8707d11652577c3698b583cf063dbb4810854afc9416920ef4b7d982eef085995a16d4862f1ed4f221ddc3a5e716847870368730518b02d970450bb420f8045f18a065d12db806166c71c80cf0c2ceaa75ebbb8cc9fd88e37141d444e3f29dfd588c78dc0027983f11366aea10fbc54b212a852610f446adde19068f05d83800311a7a515134b7fcd183f243247475c42392b9c274f3fc21a94f92898639ba3c868c499213266df8eadc980b760c334f5db4c8c4d6e15ee254a9e20eff052858695bcc1a472fe126555f9c40333033bd854979865524a508488119f323cc9a5fda31474748fa44158a1ce77462abfb853b6c40de5a5214e4456a7c07a59eb61e738e5ba684df1054c3c9bbff960514ce3a0e85a7bb7df18990e2df256a9cbfe2661dc667a7b1221b759bd36c2daba01bcacf6d9d0fd1e7670373877f0a90da8aa174ed73857113c582435813408fc4f2faa91c60a9f80023c23ae35bb82164c6f1ad32c3abe3d24faddc001784c7168a19b19dbf4050ed97a3cd779d422f9a11ac0c0f48016cfcae54690881481fcf7e20e84efcb4a1f00f42853b5f4015aaab71ef98ecb55a10cf05132bdc0b38ec3bbc0aa211f2aa2f541e38b608aea179d5a41c039b25a1cb682abefdb4796035d96b4a4fb2c8de7b8c1f313db9ee2704b08e9e0c0000ca58e241cc8c5b42aa0df932e7edcd5e64384ac4ad7136061d9b41f8489659c7826e89fc2be340daaccb3ff12b7bbf5c098b1234fd4b93880207ae7561a18f9aa3e6998a149b5388ea2a7edcd579c557849d984217827b76f238c47d4a22698f0c0efbfcdcb624d6f659d01cba30110defed84820d588164b3a54ffcdb7b0a311d32036e1e3f91a8d809e1a6aea93dbf629d8185c6234bc85ead1caf6fd562540cf02051b73cc6928ac7af45892d21153231ae9c81b3044ac04294a8357e5dcfcf91f77e03bb586b60d6ceb6d268b8aeed1ebbf3b60bb939c605d2973e5feceaae4bd5c5f5422b5ead45550cfb444d1fa6734f666d845a7b3a32a6aab3703f0640277fc26412feaef79f7c67d3b0e59c930869022d0b3fc1171b98968acf0bb7625dd2624219e1d843e1583bbfd1492f7c0aeb9d124beefa37e176731453a584408492d83dbfe5abc21690a1347120aebf7e8324432e31b411b203cef35017e6410b6491730c163ab32271f7de7ae868e825fbf71f84f51285774d0ec2df8b3c34be1ce9f6f776630cf575e410e17cf15872ea2a1498fcdf709789a08ce41eaa0a74bb5f1f54b6da48a045613e97ceb7efd261dbf0589aec82bb060782c70ce706e0f783a8cb317821147cd6e56f350c0352d26482aec4b51f65b105fa62eb4db179b2648c240b1b15894080086ffd0b02c87330808212aa5b6fd26a525d61d9d69bb0eee931559229af08811c5a8f886802f403e0ff7b229e05480840b56e34950d14233819b94e546999459517c21c7ed6df12a401dcbb2720a6a44bc1cd44592e218bf4ce5f740096959febf49851d6aa00b80706c4d3cff3bb0cc84b2807f26a79d197bb45f0796d51a8bf0b767fd7814af582b7a2a70a442685e3fdad104999b9aa7337944dc0c1997ae9d1e78c53c0135cc3bee16e57af8a6bf2f617eba18541ec86831d36c08840885f35ad9eb5b41eaf9a02f47a9f836634fae621bc69a138dd437c0d5398dc4857b16ef1afab8257d8436974aba84c0a6861671623e02d3f917463458b19b251e5d3b246522c4d46003f9802eb5ddca2e429a6eb5d57cb6f19f9a6a6191a71c8c614d26eab8dc75bde503113235acec1055869438a8fb9cdffa015a3e5483a07d5ee47ded016f8ab3ef42cd24d5d1056e5b9b674c90595896a00b73357867c54b2c1f675af9f7229673fc52a97fb97c2d0f0d862167a3b66f5d112e016a404c5b4b920fdf7a11b7b05f553a2b6d8e22c01d6575df7c84d27bef465f64a241eea9cbbb87ca780954a2eafbef4932b937c9da3ba6705034921720af300d15ac8c620ddd0febd6c30603f465e88af81452e4e2c1b37ffa3b51c01617059706bf7ac15b2450abef66c98d9de7d866e3fd458d0666010df03c5cbf92a7f81b4c0d3606624fbac0701cd897e5f6d793d195386c1cd264e719b4ffba6afadba0f7dc8312a2c7b10d8deb997829ce12543e2b41714da9affdaf5978da23ed998b3ca192082617484f24d8eb381773e7ee7748f45f446ca454bd75dac8fd51ffcaea2d66e3f0c9280b1673b58c5f31826b69ea36e61d36601087401c9b9ec48c613ef96b432c284f528fbc7912a92e76e7004d63de70f9323af15fc2d1ac86254b4d0743b2d46e7bcebb58bda2de36dc1c346e24eb345c7acdc1d180685e9b081039ca38c970d65aeba00dab0b645726a0d7fc26bacf30a246e5d07552dc2f769d7e2d43e258879e4d0aa9ab572faf2b94036a4a134186d5a4eba0bdeea069c372fb09b95999c1488b98446648e944f8ea29d9e84373729477f31a4467dc19996849934bca1e6ed6c0028336fe61f05f2bbc418e128bf1c8490290a23f790f9dfb45fd3d33231e3369158a0a9fbebfcd55565624fa126e817e438e052f6840c6585b3009446252d8881330ddf53ddaffdf1d4cf9563bd5d70add50ef575353bad066a2556af885982f2edd1ce6333572f631d11041bd10198a01ad126f47461d725ead27ff6c6bbaf561231f8d08b10ea794e6d07c3622c18f3053add5f7fe17726460f6addcfc5954be09e90b85107d79d3120d060b64e1aebe669d3cd3100932b138c62382cba0b40c0406481e82d55391ed8a0b4795f1586e5a5b83351d72b0e9e09f009f2a0eab35613dfeb4cd1eedfa33f35b8fdd7460c6449fe11d6bc009e21479b43ed9df6174e6aa42465c7e5142a2123735ad691449c8ed94121f9f992fd8b6f2246ca52148725aa74cc0d751e24578179ddf9371e302f00a15ad6df97bd2accfd451cec96589cd3432cad74674cf029485f00a2fdafb08299b5de4a0c64700983301ac946e24b964117d079ebade7330d44f53f9ac6df50d12d8abf56308f2e86413d8e68b97ba7113f88378144843da2643d4b6ff075acb42b3651fb750899c86a7fab4ffbbf805216bfa90e85a3a397a476eeeb9e005f4218d4cadd9ae2bcfcd0a5ec3bf501aadf294f14d6555bbc748095dab2ad15e47b7a9c6e15aeed5a0979f2ba4adb1ab015dc423547754917c904cd0621e78f0cc561c0349ae99238ebb7ba3d25780584723bd7bf177882f0453274ffeb069cf85f22ab83c772c2dc4b1a195472cab48d9695387d7005474e8eb9524ea6e55f1a1804a8fe5d19136aa27122077aa50b18b3da8920da78141f4215b84fa689317e6273fc82d8a0a40747d723196ebb760967ed79d5776e7b12d31a646507817e7baa871e17021534de5b523c644731bb8366d91906664c5f133099fb39ed0eba5f861c239cb22f59e64428d5fd8a8961fe1f22579bd07ffd824e4984feb973b701243d25da7cb7942b40d844ecdb0e045c261062009ade08b3b5e119db2dfe4323be25d30366872dc3bb1a40bf045797caee8a08cddd36b2c7fe71641db62de4eb7ef9d108a27e7b689fb34b72f3ddd96d02f75b46ea1f89193d469d9aa947a62f7f2e89e954f322e8121918a02fd29650eea9e5998d004f77ff68eaba06cc04da07162a41c83a4ad173011777ea5725594929281b0a2c755cfcb4f155ce624529181a85c4abde160370021e9c6189a6bf041c54304b999ba7a7983fd962bbc6af2e8e836f43a9dc78bccd6d608b0d3e697ca98c3bbcd468354c50dbcdeb714650ab5e469913e966db313f4f3167844737a30ea12803f964d025199054a042b67c4f39396491255fd9435c7fc92c0493c01ed541466592d8a96a3220914c76c3101b805e69ee2e92ef7eb6bb6f2d29965c0c4a2b7c984f13c84182b02cae7c32ac42aed81db276caf18e058b5a3abeb8a0a715717fe9a9558d7cc19e28708f45b74e5a382c05f385bd4030fdf517d3f8caa60d8d1d410e9b8edec0f3c33c1d615999a25f41c23ee29bbeeecc52de728bf9c450ecd1a5e3da697d20a1a8910cee6c01e4eee05f8b109c0aba4e59bbedf9f28ee67d5368ea8c46f43092ec7d7538017c9514e18dd717f0afb9ec9c7c3cf491b9f8e03eb3efbb796331715010780103339a4026d6e4fd5e25810502de214ef8959a0e61bbe8765f19a4a7ee84ef9c04c3ffa7e58c017cca79a819ae1e7bf07b6552832b84114a4fd85c77288dbf1d37066e1bfe8520472dbbcb5bd7be96beb73591a3d2426a3019430408c9865e1f6754e1142b3a6e1340eaf761ed6e18189835ceba3d02d488dddbec1df25fe57b30d778adf8ca4ffa08992624f0d0e30aca2f2f1f775af64cbd44871160c80bd26206d746fa9a902b821ea64c128fda13708c235ba2e0885b2f937f7eb5866ed33bbb416c2a5851da03b91a28feb5b6b47535dcdcaa7223ba2e701d57c3b4f5443627d50d78ec1a920924e3b86e20ce2eecb67df6107167bdc4ee1853290958060b07c439feae50fecc626574fda22d27d3ad7b4479050f300cd3cbcf06906853e47e8b47952feeeb2ae38d3c4425d5550139a9c09fe68e121a0595ab2ef067e71c78c2da228e0f86944d381dd44025b8490e75f9b2a3e61b04840dac8c684be0be405cb81733ff842478df00ea752bd48ec2f59cf083e03ae79934e965823dc9320586a96c28d95c5e3b2c58631000d97224f6c1e285b67696e25e3f966d1b8ef6d8cc9432a3053221f722d3f0d042adb82d5746941468ff770ab4c2c20e90c0f7c57309ebd5adab612d01294b71dc190dbb65c068b6af127333d071cf78edaa9e8a6e79b8a87750044a46471866d7712d5245ae7d204810f96c43048a4b3ab5c0d0080ad84e5f7f12d124a318c8cfc202dcaf09bdf1a9d31c22718c85e5f055bab0f5d4e2bb37ea85f7dd0369bf960461986932595cf853658d0d267ef74da2b8857423b7a7c290bff1047f5e81b6ef494e0e3c5c49b2a09d28c860f1cd4e9abd16afce2b7bd72bc23799661473aa3668051e2a1943f9462321f6876d02c2dee02125131ff4a11ff72f326a41dbfcf061a6b6c7dcdc30ab218f9461b3e7d284da4176284be3a3c2fae49ba4bc4f667b70975e3141841312b3365eb748e4ae394428729222520ea3df91741b72fcd060dbfb99f5274b80b0a6c24fc022131e66db12e0089e15bfded883a284d6d05417d7488815184ffa8ff68869f6ce377f2551164e463fa20233f64bf12d289d01a1339ac242e5d00f1e0c74836aada5b9adad2b18921a28570032bf3cb5e3e603dcbcdca53b85a481d84b1e3f2e9f2fef175d65cfa2ef4c3aaadb5250eab0aa132cbfbfd3e41e469576940e58744ba730287f76e597568c75a7b30f16610a06f3e82ffd81cfb12e206b3573e7e6073f1301303149f9a945373996e89879a3ef87151ef0d0b148860e219bf8ac7b18fe9c9511764001504b01b98ff6318e48f1381026b8c38f87de71fe4dea353d72f65405fe569e57b2974685e322f3589158b2683949318eced21632b43f13bf3880ff0f3a73ef37d592c5ebb905b61bb7bbc9b07321223239183fa8fb9b4cacee17b42f990ee5dc17ace15b62d572630685812671fc275534446aa1009dcc5ba6a5a433f6b3c4adbbc95a3579b2dbf45ef3d2072c1d167799379c9730ee183a02ff454d479cd7cdd7155de48350e9da614257c85b262a30478fdfa1597712bf854e47341807e6889b44a43118982aa5f5c2035109ae71eab90cc1b1d54f53d801981e9d17284b00a03d9519c5764191faa8788f971655e4eba1fc739149c8b2e32431739699b910c1f030a46c5dc95a7010429c29441d0a43c54605c56ae65591fbb4b2990ede48c75171f821ae018e0fd15b424483e3ff5600abc67280afc3cd1c4ffe697d1cf9a6208a472b372283e8f0b2f33fa510a6f264effb31aea43edcc7001900a85aa0f6330f55bec8ce97c6aa5c0453d5010c767152bce625ee77f73a0e80005da8f7d9164828a654d1d69edc769c0d1388ae174742b806877e68a8ded7459d0a7777d2ab1552a279d94d1d37521711bf61b3304ad3475d0071cc792f3a1fb44e98211396e3fd3f875571af3aaf9112060044e814704000eab4e1ca9129485b395eb691d0e7715787de4e7339a4d06c30e638a2b651eaadcfb8c52d6911e36441874fa9f35762782e1771e445275df0c4080db8ac5c9d4e81a1050e6a866ec7de6e440bd204cb33a75ee06ef6f7d1b8d163665de6e57509bcefa0c40013e9235d39c245c86c0d3960ef149d8219da04a3e3dfc84edbf4e93cf7862000ceb10f7b8800b6426fe6b458d695c9443b070db89005e71bcf70cd31f323638a0c69c6ea1267278953829e0774bacc4b9baa95b572a353d56841daa5adf5130a21a2c90f7acc0d5a27581381caaafbdfbde42e3fb8f1b9b466093415f4b06583755c964254626962f4ad4b06ce08d7704d0535d79c718c219ee2cb14448e77c89757ffa09b78e14432dd683856e3648536251d47aaa1f0888e5459fa46cde70ae4ca40c26903e9d7ad7af2bb792f6320346816fb127e2ae41c42b025a6bcaff01b90a20ad115e587cefb6e774383ddb6c2938880364b1a191bc9ff6d532b17f8cef64b42faa1d6901e588842f219441e6d2058af0a234a8affd84ce5bfdd0cbcaa50244842792b2a3e2288a42fff39050c18622791ffa944068c188eba669b44a9f80b89340bf2bab5d9f2db1c5129ee2bca52f362e47dc668b0bc70b4417934c9525c194b7363aa826254cbf4de338655a506f45b8880dc5d31495e7ba63c32897f1bfdc6b93e73dcbf3c98a0d78650913763cc634cb5cec203874cac17766fc0f7073eef9c1ef952cbaa6aadac319dddbc4140d5696e3ac2410ec192d14881dd877d51411d4a391ca083e0501efd79ebb7e47d102d6ae08e501420473816943bb4f730293443b9f213f68578e7b98515748d810e8cb5f817ab747ef60eaa61c903d867b5dd0bfdd7b5587a3885a8cd823c34b48bda3d2cc24dcb89108d340170594b4110a90cd7afd066dcb182252689b141b9ce78ccd7845609fd99f2710aead56b00887f755c61f82f84f8f4428c0f07a0cff0fed7c768410630092acb38ef203c89bf6bf096536a0115d191e2c58c5be47022d7753333f70aa92547207f7360eca0a14e62fc896b9ceaaf2d066383dc4bc8e6e913c6684f4bc55f8e756f6eb8ddd79b1d10f4be8b5429117e770ae3091fc687728bd41a951c0b2dd5828f1bf62ede448a56119aa7867fbb6b2081a363bed29ed670cdea02db641e49c446ab83e146d90e9e19c4c8c89aaa5145d2cd7e3702cc754ab7d573c10a454d61b0c69f8532667584d97bb23793c22a646ca312cdf3ef0f82d3b9e9f6a3c6e88e9d160df47f06a943765fa3c23ac9b0fb829a8774cb200adaad7a6f229c5058c31883fb8d6a97cf3133f004e2802d9c4ef0da14e615745d86fccab054843485db67d45ff85b5f696f34a16095fe32efaf8893efed67424619a847c2fdf8e58c0be8f57f57e376a90045dbdb5aa50ac027400ef5616bfd90105c887912d77394662fdb8dd71ef6e06d3df64563744799bb99bd92e22e78b0837073ded2e0fc547d91b4732335e0079f481ec1bec60db0b6e48968a2d48d5437b74681bcc6971fd46fff8c54c325748e85d4eb891ea315e27ce38124667ee6f325a6801eb6e74f2895cbd6b5e24accf70725d371d71cc7e2f2001dea30493c4d80d8a7715a0e5b0b6bbe7b57a894159d5473eeaf14005672ac6d5b5fc3e5a6592dd300c965eea1a0e958248fd86796c49bd524c68f716848f78a97a396cd6ad9717e1d2ad81d7c1da2618ba1ca354bf7417b1c00a12a96615a4eb09f20c7a8e764a33a8435eca85f9ce04b611a72bddf2e9475eeaacf73933ac154b8dd8fb5046b03eea6888964bdf63a1cbf99503e9254c29a0f4365244ff9958bd93564bb04c5cd3b05c2fd47cbf52cee5580d31f79977b751822e3ddd507ac3193d79c6063762817c997c08053de7da4caea373cb4164b2781358c40d17719719026028940aa53ace41563cfa15892bbf962859a85a40f191104575628db88b95dde57e0c52054e6568af10b98d1e6a0080eaf95dac5f15ea2cbe5835dc0a9c8f68cda3b0c37fc3e1189314ab65f96af3207f681ef7599c8bf4f5c565c706934bb35419f3721d6e22137866da7b2794c0c0341c9cf6d6c0e7a6e9452ebaf2cc0f97e75e01a8687709378599e573d1149b8835139abfc24280e5a971d5334035074f953d03de8f1d723a26ddb5c2f9855a220c628f73615ed5c58dc930123ea7610d7c36f73ad3a1f050847531f2ce088ce9c90aa50cb0c198e85af5ee70373671857c89439232df082672a6e57535a8c9843bae3223b6e8af89a03ae8361d3c6ad4582b638f8c9102116d365921b296ed6e8666f317de8ad4bbd2d002dcc6517b6f41509ebfd2391180ba883ea9b3d2b5ac6701a48e488e1ebb6034fc03c594ceb84067b41e191269e998571defa0553ff6280933b31881005a246f98f9950f6a4847e36142c0b5bb8232cd084f7c2d50f13093359529e318c017dfffb482cb220a93d347cdc124f65201531e17682ad6c7a01b90a9e1a26cea526b3608780b2577c5ddb781c7f4a123f5402b0f021558b7bcb2b3ead9591f9500cc8b54a083cfe040182f903003e4fa2c07a1a27e4dd240a74f928865d82dd72a1090413cfb2b4902f7ef6b32b8e7adc19752c21e73bb61e5b66507c37ced8acc3123372eaa8b09eacb1e9370408316939625d9dd33b88445ee45ff088906bf1eda45204ecca47dd9ee50023b6e95e3001581cf9c907d69ab91ab2210d065f291c2152c656a3f6e2ef082a1afeb9c057a9466e604e914512bb79b68d64e01f93315a12be99a309b8b6214760809bbc5c2dc0dc150d8da884dcb152618827a9cd761fb2d92b857e5293e9ff64477dc4810287730261ca920847021e80da7ce9754d188782c86e2fa882e19796beb7c77caf3938c4ef07399c5489f04b4b2e2c32dd1b47fc521ae31cc6afccf51fba4268ef6badaa46aceb770aa6a2bd27cf088fe86363e63dad885bd33ddede44c89b720e81ee009097fad116fea8f92ee51fe3c3313adf2eadc2e7f945df844625145a84c6595c7129896990a4839af306a2a3a99d866fe2f60ef2f93ec1fba9c4c1cf02232aa24d34e4ed1d0a9a83ff198b27a6f569402bd4815c866650169e312b2b1821a0a07e59f7b49e334bf7fb5890545093c8ee7665420324eccae8c6fd9e814eb562f8203c958b494adad5f34923bd13e24942ee2e3d3d772c59dce14c3c8632924a0ceda5c13442bb213ede27839e6a17a98ad33749d7760b0979cedfbfb551046c4c522030b27d23d234cdadbfc36489d1db7f298194c0f8b908f5293a6e64686d9f8ae575193b9228535f1732fa2b8f8a5b2f3a759cbb54342ea4853d14c43cf4506aac4b5cf4d3d5231964997a098bc7dfc4fafe848d07b1ad500ec25d2b5a27157004ee504e2c4cb9e2d0144db4177904095bf49fe708f6ba53c4e3614510463d57034b9df713c0100d78510aaf03e4d0e083512e481d77a5de643f4ff1adfffdae0eb885654659db1044b324567a74eb78aa5a889b93f6632c3cd34e64019d4e450a85530a4ce145116915f71562389b4005b0e06dc4a4b7a1c84c8abfcbf9a003351c03b0208a9edd7c48aece37c706a568ae9b2a25513dd33bd7fb7545726d66851f0b6b5e4a940558645f54ecee56e9f1fedf1309e3c5b58cd97521ebeca3147019b78a883f70449e8b9683f47fe88bc163267a6c99d1da1e97aa5609a7660158c11c5020cf5e81c415943aba53174f954b414ac92cc9db24fecaf51af5c77be74e0a1a936ed34929969effb0ff8bdca0ddd3f23fa1c117719abc73c3a39b30ea5bd78dcce89e70072dc5b443be21725621ef8b182be4f322e415f22c11740f0d4f849a29a432b10248ebdd2dc02d4b49c243047028d88547c86c7fdd79f22cdd1eb35e7e1729f319deb3bdeb4d963e1af18501bb4ac7f92153a2053b6f29b80d5edc534bc11db3773d6ec66157d88aded24b7730e2f9e37df396b586b4aabb96ac1f675be5e13d2a5de27a35d9ae2be04ea3bd177cf9dbc260b2bc4e1c65ee9ff6528fe842008a91c86991db18f0b02fdd7d1221e762e070ad1e1d5f8499acce0999594fac2e1c1d39d43789e64430a593037b8599c0a03f6fdd4cc021dba3e88fd9387568cfee5b33f41e507136340c189f1e3370485f1e5336dde057b6d39cb0b03ac33483dafa4a2c7413edffeb4ad51f8fe4af2a11ae556254ac49010444fc0bcb6b9bfaf475f8884861b5083e94a6eb4fad8e7ea29e881cd1c8da464dffdf9d19a5a3db67978cd38edb7aafd0a2c44879ca2d77b19508076da166832329adb5dbf0171c5e143c1b7a707d81d267421cb2fc136e1e4e9bb3ed5606673fb486521f65effaab64b2b985fbc2dcb24225c2147c87c65ede43f1725c2b05a3091c049ecbbc63fe30474246536d589cf2480148f9d7df357be39428903c1200a7b23d73e5afe85c3f25d71d5b0fc4f503a761683a146facbdc2411f0956871765de54f0f95ba66208c50db79a4c0aa7a02c4696256d4288819e35f6271cf107dcb9a6288ec9ee66aa46c1f1883c29e9abb03f60dbef2fd93feadd0cac5bd3e13d8db16bb8da7a3826baab75e4783c091bb163dc7e5c2c57c443fa3addf3dbfce24686d1f0b06be36f68deadfde3b2153e38b4d10a9251f540fa130eab13e21646605859dd42601b488b432641f0da7d31ef05af031f8295f4945dc3d0aa32c8991a827fafc28efaccba0bc06c78e634dc4c6e4ccda0952831c7b6472658d916a3d72ac9612c507b90ab75b209205360bcb085a9576b0ad2f630b448d506bb417539c3260bbdc808be2f4c39c28cc3bea7c090262c4d513d38cb415522523adbbed11518df0438365b32d3cfae9a3ca6a0de7067870d9c943704e69879a049ecb9448952616740c274637ba56823428edc0957ad8ba7ff6e3d1af3fba10f8387e184a2bdd30657280e5ee8ecc6639202af6f0f51c4a2e98e16b6ffd3d5e58c1e5758e6612d072b375ee35c03d2878b9ca72042c36537d16e31f3b7f810c143a3dba6de4e9937554a188590dc0c88114ee8c52a3378705518f995e4264c720f1c8ea91ed602830f3e7370103150018248c136ff0825c4ae8e39f67308590525388fdd051d420823f938c0d9781b6d31142be8fa4c6971010c052d75d829c5a0c58c3ce0c16c9051844b644736b8b018e9419ed05977d42f9334ae851625500b4a8da2bc382209432f895a32d7e5df23afb248f6c1fe02585dde3bbe3a638b2d66488db05d3220d785f2f01552d00b082b04f08095deb4ee202097db9d100439bd144cbc6f2d72567cc3e02ca9958aa11694d7b71c2f7cc25088e9ceb391fb93feb9e13f5e1755d0550960e56adb184be90069b67f0ce6fdf02a0fa04f0ccd2b5cb578137299637c72b234b4b5f6697e194ecb5cf9343f14ee9e5badf224b5af1885815622735652705e8115743a41135055902768025ba77a638200c3979882f1b95bc75f67a57d6ea2e9aae5ba04c8aba422da274ac016325a41b674e9cd998371f1fb9cf7ff4ef9e3167f9e68d1be7a322e9af554092ca248fa306d9b706a2e82486e6f94f520c7f20aa66a2e7cf4d5cc6fedad3a185ec6544423fa5d7b623e02729201a7c741c27e1d2e46add9cf60684d13a603bc40856c36795b64f21811c429e0a9e3c6e43263e7a7bfd727bdeea4055dd116394d14c1d2427dc06e2c7d1b75074c7ae0b0dec8c4a2c84846e4dad4ca1a9143dc465609a5bba77703f3a1bc77176a74db79582ec407b99021792a4e2fa641226ac45737458761887ba5322184f17abe4b4a578e5e25550d138c1d695aa102692b943d49cc025cf97a41a955842379d3ff45df4ccdc432af29c709ade6be223110271009886c48258523fbdb5e01f8772eb5d594b909046a7576dacceaad5485ffddf428e9739feb390046ddbdcb514760781dab8b78ff5a4f8cac1859da6dedd31d027968625ce1ef1ba7c07586644e6c8204926995cb1bcc52b10d9c59810389841154dd34bb863b29eeac807eb16862c25243cbc3758d88dde9e410bed185a7d5bef18a00897c1c9f01bd6db035d59fd670c7b513a801b11ed517aebd41fcc28e98830d8f02173597ca914660f786f4b6de33308b9944ffa5bdd4e5f11ae9b87acc47c7cae1266a503fd6f1884d5d06ca1a9c61dcd66687f3f42fa5ee8440a6de93694f61fa0b209b42be9f0aa87533409919c1c2278852c042a447364466e5ba4db398e0d4c5331052df374668c9b137ea2403054318e731709ad82afabfd46c1523c29e98af8b7e7d4f38243e4c1c224fd91330f5b3ec53186cdd0c8bfb942628e92489c57de634fe60fe6901153ac67d90a00fa8b2a17ab9af49f0dfc76426bfe1fbee71faa9ea524a59132098fea2c56fe7cb024df0905b8dc66aebd4f3b069423f8a97c33c6ba6d88e622db47c0d842b16f4d05947d6fc8d76800a55e59b0303105f4f50d1b370da919ed707b5d043867e850bfc21fe77b785a3873ddaad19c5ccc2324ed9124b8608606c624760ae3e90e57fab4804a674ca05a9ac843d5d2e64a410abf241a6745691ebf646045aff89dc877c102ae0bab61977cc7203d4ebf8b1c0bdfb13294782ec80f90a1152bfb57a7151d5d0921962ca05e41145240a10931f41e5190ffd433043436155e14d6660e3d711150dae6491f64efd87f5e7b2a54e5922fa870a090459c2445aee983f00f811f585b870066263945a63f6ab1d105f284dfed882474df28879c370a002f33c50c25e1b2d6d0c06baa6419b87ce07e9c2e0220bcb3581402a55a7eac4212169e55575f14ce7ba772c260534135d94ada861c9a42a084c2adfb451d8d7cb8e09c68aa2156a4d98546eae27d2b03ba653449ca7ec57eb6db5af229702bea413e2399a53d5a023439c22a373540fad50bba5851f80185c037652393d8ec2d782df10a2baccddd6a4750b9e4f1dd304fa668ae22180fa029ced49773ccfbbeafc309e4987cedef451126c65e1002643a831d309c25de25c0c30545dc90320666fd887e6297f276c7f3c11c9c00fba6d12540029122237cba14706a5a62e66a0e112bd7fe071e59c06f71ce19e8286c67b81fcd3256173c7b7ae18d806aa4e9e15330b39138fe3d6780de73d333cbc74898395de9758ae641cd8d3ada2a7ef7cc8694237cce641a384451be090cb9d1a07c2ad02fb370fefd4092a6d29011a57e15920e0ae605154e2e9d161489a74dcdd3c788c20900f2c60b2d0cac09a1367d4aa112d8355cfff40e62c5df24a235f39eebae969f4a2c41cd2fa1ffcdf50d0ac154fb67ce50f3ad6631b74d01a46e68b5e3e5b6b30be43de35943ee30999e959e220b7118431629ace5467ef5e0b140f02800dd2cafbd5b95ffd0cc0456d41c915b500b92966405e2b02502f7e8b265cf644ed472ebce8e5338abe03192654c85cd27725aefa93654e215d05bed1e352a8c19521e922846d4dda80eba53e0dcb31a2a9b60f547ddbe054717b0e4175a77709802d41f183e29c3be5226b371426a93e11552d1130ad83bb0ba0fc54052c27bea731a788c67c3e70220eda6b51642e891435add0d8e6c1d941c1cb4e6ead47bc935cffd12a51312bd3259d44834aaa65af741c3bce950cca73a87a599975374ab63196521279cfe9dbff112fe243364819b317ca030a5b9e6a3797479b3c8e6e228542714bc51bd62b0596df5e8423f08aa0b14c8f5b0dc52fb0b4065f809ef9f9aae67c9cd88cfec89eaae43061b7c21091099d85d2e8b96510f19ccacf39c42a04bd64dc61f3252a5b4188db361057402b1c6cce9e1e8e22218b42058dd937dd954e8da856d17de5d64be883a20acb0d6cb6a4021d8006aedc032436c7cacf1bb22b013b161c7fe3c867f36013e317ef49f15371c30c08392d0f5a54c089cc80d00225fcff5d23d202cacd9f355747878852cc3aea686bf213f740f3196751b9d57aa929328f4335b85afa3d6303edf836b341c82f87e1975790a7da185680afe28cfe755ceab7fce044bd950977022d497c6f9ffee6a6bb054fcc3c496796e8a03afdfb23c5e7a8bbbfd359d40e08021fc764e5f1919c7f889f1c93a7dd57278d154565ccfc4289ae15aab00fc63d0cc5167addfb36325bb9cbe92695cbd87c433792acca07ca1da1cc45cf67c6b6fc18ca2cf2a68f3cbaefe7583f32954627bfdc82b35c01744949535355c1d873d9dece4a3795474502e2a0a4ac3222efbbb2eb5f319899be1fd400ab0853c3f385b3a554019ccb70e5fc286bf3d754380b9228778e2f940afe17e4e41e42a008ccbc75d04050146f5f00d7a7364ec877db3258c96f58d6bf9723b17ded0bdd7d5ed6f910d5b754c244ee5787e58f47ff034493ebb71c0fb1ff83c4bf37b98b4266ee9737745412ff1dcf2d52409348ee8647a6537d2c6ec0e7dde2504dd44aeceb1722b437096d2d047c95fda795e2ee18d5d6698d6433c9662050b0d7b25fd9f682e24be99340d8716cfb079147f895c719fe8e9fa9989cabdc03acc2dfbd4248738533d59a34a3f56d0130547f332b800d0c72fa343c24715d219cea330bfec53132c1f3ffb1cb3bc6427d503d1044624995ef7dafed1be5f9843a5841f7727683a87631e0caa17c39feee6fe867c6e50772273fb2f06b80b0591cef477a20252e90d631b32f9c80e6a6a4b12651861fb75a6a576468583c4cdaf67dc93f3d75c3ece2741dcc68297e1e3a6c6ed8c6b15d200853325dbb50d5ea0a02ce7ad2fc7dbf79787f7cc2f01c136801c5e2f82c0f449c443725a3f5e477d55e1c25fb15d46dc36ec2c8e9e44f2da7492e40d89f1796e853d1fe32a413bb987cc5af8682173bef0d90c5360d5fbf9c1020def9fab3b8586cd4a72538c4605d7b633db62ff6c9332329d36fda1002000119c8352810a640b229a8d99850fe6f729744e07a411bff33b6fa16514cd3eb34812cfb36db704081a0b5b40e94ebcb25f11b1c4b36afc4ab394247e32277570b51eb8df94ad74f891e1eef9c9f08f0c0c2d40addfee33608605321dd7985338c6d16d6b48486b32d364bed87cd3c594625d5351fb7d85d28bf73cb340788c740ef2e8d3030b43f1f9e12b3d01f39de893c74e8ac9c909830532f4c3a8e46b15597d0076381b983bcdd31c46ebf6c840a2a562918b928583875c3b0d60e69ef8783aa5458ef4228f5f892aee01d79f7b00d0b886bcd6bd804c08fb5f010b53dfe21007cad06c2526d757707f0348020fe251b74326136276596a78ca1c00d81c5b24de7700d1b7717374459f126d321f12357aacc068ae884157d819631214fe6c2d962f42f068daf2d5750ee1ab818d7cc5abbc7f0b0eb3090201bce04d5dff43b482b1a321489ba8fb4446a93f0801a0881d9e1a10aebefa6b7b1514fbeedf091882382e8069e4eeb37827b1ed64fe4d9c5a4affcd9a984dccb3870fd2bf0f5be16ea2f7205f8e963e051bdf0164c8354220e327d305121042abfb58f7ded59be899ef08e05b910f0601b1fbacda2f125dcff3181d0b3251ffdb7be32723bf65544988081724f4e004c5de380a63efdcf9cf7b7459bb4a104a3882f7e64555b28329bad2b2ab0a33e56a67c981180a227ee9d2a9882f2c8fc701f80a70d851f6cb38811a03178aeab6d89e15ba9bcb0d5a0861616c1e7db7c4cd482b02c9bcb4e73a90d52d0adc2b131511fa1f3c19b08887168ca85981546d3dc00d9c947e5939eb68479811405ea71a605d5e5984dbd7322bc62e22922e98dab086a31d7320fcaf070e66e9ec0ea987796064f1b946d570bc81191e09a81887b8795c8ca3a3446665eb4b17b7a2c64a22ed785a8f6ef071d0b0937e59e862b583ff50c74cc8007bccc2cf41a6a9134bb397ff751f03298feabb24223e01f2a838803b0f667582b2392d05055ab88452808f2b99a310aca55436f50360203b964905d8e4f8902cfa56b76dba4d4c2c29fc361ceaefd339071af087db89c737294ec5afcb6436fe137010cc463ec416aca1556582416e98d1acf105033c43a99063720eb64230b0370c2d13fc7b9524fd984588d12b2ddddec7c46b2b2094d85c6020c299ec19130498c75a2988e663533fb4c085a632af9dbc51560298cc6e4ca606dcd8d5d10add4b107d56c235ea3167b025b9668f0baedd3f7fe80be54e5ed8549462d29e5507b4b18a0ca6e443af3c0bd38b5eb29e5d1f5952f17262176d343c15a2caad402783f9493ed86adb252d29eb8366091ee0d07dfd5ccb7552bba4521aae7bac78e273e9f900d6babcdddf7f3f4c29045eeb0300424a18520812a08fd342acd7c1685fb68ecdcf5171aecd5a22aa0519b1faeaced63cd58d9480df052b4a1d14197143cf54130aa5c84deb3410539a905a11fa4e3a8eda71f8ea6b70e3f9eab0fda0fa981295ed6f408d038df8286f8bc4267bc9e5c4592151d3c432388ab48bef380c48b1bed0dc3b96131c403a24708278ead7a88d7e425abc53e00463fad0792fd57f084b758066113b10831049ed93b80e669e6d5044e2bf04cf8874d620adbc9eea5ba7fa8e7d59fe2463e5f94e5d819f0210b5e7388bc4e78e299765c9bb801530227bd781b845271831fa36967db08b825b41aea4d6ee2468b0dcc07c67700966b5972a5b5aa726e07da48193d26ab4e03e3060956d8147231251036ee1879bf1fde46116ffbcee6ea37a01ed3dea0208dcc6da4abb793c79be67ef7f0a73ef996889f497b6c6200d18ed60301f419e9c4bc8305bdf06e535cddc077c07ea31462dac14681165aa713ad0c2cdce4853afaa02cb9db37d78ed02c7ff166395884d5baafa88f0dde7644f57a8bfd358b17f6608f68f4a6d255bb805860ec6ddeb3acb9b78d21d715aa61f6168fecdc2ef6794cfd989eab2f44a526e447266a3295e8fb6f7c24b7ca15cecabb36c70f76505e8e50caacc9f0c5b2e2943442292a47974412b9a1ea4192c80c5ae93627062a63412d96d18d79dc25f75cdb1bd4b8510a7930ece299579e6df47c2c44f52fa42ea12f79761ef74e4806bc23043436b02cc53fdcd25b1b455969ff039a26ecc11da08385121001c405f9e1a45c6e88767094ce68ccf3f2405f4cafe1f9ee3ad09afd62e7e58ac9c8d89b19c23ec302fc169888e4dc64940776535412d1812d68166f04df5eaeeb9b0b91bf618a22f0108217af3391c047728e52363734de5254a49aeda410ffdd4e19120edba16536a2e2f841072b00f625787158952d6ef026054d8df430676c7e8337a81cc5a623e82952342ad511c586887f2aca50e653e0d08de0f9866075f03b0c579e792cc7f1453a6c3ac920467de49571f5432cfeb84546e138be630caccb62c22825090fb2260318168a53685609313930215de9eb093603497fcc5166b8121a881bb0ef702823d8b195644240fe1e2c145ac9661c7622aa2286332f9bc24c10c31cb235814a4fcd3a9b770ac40ce66bae61307ae301efaf473a785f1329b41abceb65f2c8c56152cc650c84b1df90ebaac816a4b179ad319fa9133fd76d550ba3c0aa40564abf53d2617e28d53a1e4ac058c6d3d410c4df38f3b8576a010674c6a90565f4ced0554a73127135d967d7540ac68deb1affa736855bc0f0a65f9426ef10d0abb1841b0803d5530530557771dee049580799beaf8dd2873523f3b9bb90419b4f2167932c920f314a48e088b64478a5cd5ded168ea13cd138f02687909b9d5ee18c27c71a63607e8390dd84501cb9bcf1c2f148148c29f6f5c12246173f19b98df66a148056e152263d2b8a8661331f242b9185dd930d5b434b2dcbb2fcb01dbfbb02254a1342e384489f3f3e785744a002454bd349bb4a8ef45fc3c95e62d07ef0f8b511a71a87d095ce5ed447765bc04e5ca06b70eaab656cbf80d9cb71be49c2e24805b37a16784537180f4a3da738854995a88e10ea40b3e19b8b6e93005fe69f90ad1c6846e32b5d59821fb206c4ef0384d501226a2d0546411282c69fe56b02289f2f11378598bd70e3237d3166a9278ea49f89ccc289662f802ee4048ad5b23914e24129e595de90a17db434212f7c5f4e14bb913fd34f59a9a56857fdafd2a7cd7fc024a20cd998d55579c81f408e1714217e949cae349563dd4161868b36c57aa1ad78f6a9fd718da2f3fa1de4a17c3d8bed2810e7cbcf7b01d690e1628f04f8e75d8c30785440bb5507e69f145aef67066fd6d3c7982699b6934762e89f48f4f8a7fbab870a15d4dcb3532080cc4d7212f13b9c59a20532fb27a52571987386a463c12277a5c6dcab2cdaf0b3553ad99203bf996ef8d9d9dad0aafc639f9f47ff7de53ff3f2a368a4afcc513f47a496cddfdaa038b89993ea3f1b64aef322468b100b7b02b8ef3df7b8f79b5ab08d4750e49e54c9cbde36a296a05c5785653109ba826f0dc537386ef84ee586f26da2492f7c23292af8694df5a7fa3ebbc2811eef2bdc8d2f14bd3e80c53de7a2915ab254c438049de574e37bffa0584b8a6cd748619f9f1e073df5dd9d4552880ca2fd16fb5dded0426171d7ed32404271f8ee8730bc8b6ebd5827a216c88c7fa9496cc948aa656ce734fa48409d90f74466d87a09151ee674eecd4b1836c298b1b6cf90c90975390e8dbee7b1626e69344eeaedc56a1419ff7b78324f1f08fe76094eb0ddf711f275f0bc406af5da119f4c7c7cc98ce84a0a41b57f5ed17babe40a0fe502151636bbc085a5fca903cfbd2aa500e459f847e1d0a03be7a7a9382a59f11e062284683aa600be84a844c5a01adc4722d20e72f61f123a4a841cee6cfb5227573f1842b7650913181eee4e45a7e0c7e974162deaf8c2313ca4ef40d8759251b4dd345431487d915162ea2e1b1eba6e2f41edc005a72d8962fa924603166245bbd5a223a8c343777322e31b0f11eb40ae2674990b0533c839cf45c4bf19166cd14b54665e4f0ebfcd12b045290e0c0ca38bd8ab45d1dec655bf60ec5cb70d2448bf08a7c5963006371163d6c0f31740fd52e52a08fe089b7278adcaa50f959789f02308423cbcf4d5f8669f67e71b79b0336371b26bd41321a71a147a1d4fc25e9367c18887b73ba2cc1243a830d4497396f6631e4814f7333a9c2cea727a97c7bac4e769202ed6793f12f5c80d6b0d661622ee6791d389eacb423d12b16ccf16107dd1061810c8cad850559c170e6a39e41ca93cd5d528fa989339d3807c30f953efd90f554c548fdb6fd7dd3cd54a89874b79551d9c564bdf5dff9ba065f6459afefd2980d69e70d4e0d3703f6beb33a8ec60823c122a970d688987cb54912c4513dccec98288ecd6d9a92fcbba8754bece3b506e90e86404dc0112e33c25d9eb88cf2cd8cbdf24e66831389cf45617b0ee61de4f0d48069ae1b2a591803a16a654e19794d7848f976da91b9f66af16e0e989f01c58b36e43129c7e22e6674b4bee06285cd4564610ca1584b3cca3b79e6e29c1fb5911814086679c18736881316dd2c1205ea3400cdd20bfb5be75e942fc74f7adb53e96d87b6e99d8cdb6ff6cb711e33a3fe53d63509b677fbbb4d0406fe917b9ca843540cb6a2cddc778789a0c3c372779dc43abf36c5f07a26a0e5b80b43e7b083c1aae1502efa91f83a754e49927dc8a07ad5890f14a87e3f381ab7b351f60cad23452fb2e25a5c4d00cfe4c5770b52764e32b94420d3132a86aafb9c984dbb025ade25f78f93b2abe9be00e715435ede8673b42d0fd9a21093d9f1145d1a10672b72ffd11397cb7ce6c707ee97da44ce361d7ffd4cd11715174e4620962d432fc63bf21ebfbd9e570b5cb9c9d133501eda5a1a6f1b0828a564816ac688a52b88d404b0b84da38e25bd51c0f0b63def89a46bc8930e9c003f2e16d22bca0e0bc8457fd1122936d94df94d71454ca9f54edb5c14a65b2aa0dd4b7d0653e1ed2318cb2458f9b68bcd8e970da9f1399d4e41f3f35dd3c1cdf02485f11d24305590d43784743daac5e7f062350b3dc395250d04c94656d6933a0784d5fa417f270f10ce99c9d135b6a2531b6e392e8c46c4a13b8f60828f230ca6f6e0bcd9fcc9fd2ecd7b177cfed2f07a5f9a8402d7ce2fab3eed80360501fdf36f39006138e480b2bf713bdafa6ce494de4b5b680961e913c5467a0f75d7f04721c7ddc196b00903ecd6e5b76e4df2a76cf1fabc9436789f00793a322276a7847e6b9a3d043e53d3241c00b9fef1cd83a59ccfb720529260f273c980836898a54f9e652ac13d9c4925ad40eb53b9ac3c3d465e31c689387a52e9f4b0d59c12f0e555f982448d1a2f2b182080164366d0aa07279a1d228f5a6c720fd4c1797261c851000f9bc59a4ca354a353842edc938d4128f1e1d2a2f81401eaa8660f66a5fbe97aacfae264b52d4fef3b436412c9fd1c1d232f167a70349b4a261c9ff36f741da06465c71bd8a33f023ceda6338f78f84f37ddf7db2af5a136f6eb9f3c2358e3fcdf829e26c42f1fd38358c15007eecae099c0db4d230679178cd7eb3691f2e7eb6d4cab2c603c520efd6f858e413e18261b70b67e129b036e3a8957f8e35a76d652ececf620384583fef71868776542f334bad4b86cc368e6e98e7e6cc30ced91aa0153b0360a1335324cc697147c3015dd61e26386e0e7436feea1ea8c693aadf4789483bddb22145a2dff4381b54a7e5ac70b90c6de6a3473b24f53c7e38535b74acf4269da8096f89c3bbdaef8f1d06fa8c2206d6d7eb1aa8007d341a8ddabb2f232a401d1870d87f3e5dae36a13ce549531b8128e51b659ff8cf9c1bcdc32fc15b37065ff8aff176afa1a1c11bcc4e16bc5d96f99325b8d42bb49e4ab8342b15022d02cb9fd1691602eff62d49d4d8aa4215ab51499b7151daab68aa2bf1b251d41021d7a981022680450475217e4d013080b64a4862257e74434bf4ca32ee59c3480a563dcc1d888196ba7de16bd31613d8bbb434903632da23b6cdf6088be7b8255bb1dba6bb4569fcbb1f5f79f3b7c6b70930d017c365e02e8d40e04dacdb3fbfc80a3d541100ce95765ce96961e1d3a2c9518d2f84371c22953033a0ade48132907cee237abbfa4c86d01f9fc87817f099be4d207a096906e19d679cf7cc63deb3276dab55cf92ceb7d2f614330d502068d3c5890ef61ff451e4528b982dbf5f71811378d651348d9a3511f54ca3437a2cf5ebc0ed048273f9b0310d37b1447de4b5a17c9055d808857f1c35f2187b7f1b08aa1e29b99b7ae684ebff7a38dc7aa8bed4326efdd010c0a83eb62d0373f9f8b97ad0b2a92f92a56c208eadfad970fb0f64c9b15c755314169277d14703cb4c236e7d4c553fca8fb6055475575529ddf61be181f94cf6f6f76991f67545ee4a573d29b2ea5cdd437f291b8e5abb57efa127cb0f735e396a9b714a2fdec0259f846249624030985bdfde70e2f1d951e6fda811df6e5374e159c1f8291950db02ffb5383cd15aac8ae87d6d645d80e3c17fd8d3f0afd0ca41856cfde6a9d8dc7678b68442701371cb04a51d1738d16d75c9b22afef6cd925a508e76e4c57590b27d7d275b9a06aae5844fdfa6e029e668f544bd6c1b111312667a47c79fd14578f8e7e82812dc13b3d1206287d33068ccd5f3459f951cc11defb032c70611567918785cbd5e460e18c3ee8bd1876974154a29da1b1a7372689849768d2ae8e20bb695aae233441cde73e8d5e46ea4d58e8c72f8694786690cf0eaaf5bcb29e5803c397e8cb28f631df0d9de33d0d465efc7428ad53fbd51081875a05df54a336b5b2720b03653ff32eee573f79a895890f1f7a9b17c30d34ab8442dd6bd4dc65f0e1654490086b6d5cfa9a39b20a7ccc3ce883971f64b86c05914ea8654ddac2916642013f4118605adad82f0d7b7ab2d6253353498f2a8ad8baa67817d4b4461559bb6800cc0bcfe9b4403a3d17fa09c5fa5a5a4a4fc3e499640d221477c3edbf702d94d95c2eaa7d3d992c54ff5d76b7a8351c9677f3ea42a881094844a516651c3a242db464d3a4336ae203015d637db467d9b2ba2d301746770af3b804dc265d187ab3ced36c688d60baddc32e213cb5208d36dc1030c2df3e641fe549f5e37154197ac4fd46888d8960de08b1c0434bddfc798eb86549080e25183698454178dcf6eb63f65142ba3d7cd548a01cd429ddecc462f936f36c822484664a4a5c884fd82a9a249fa5c5a5ff526024000d55bf8aadbf4c513d9c39d15a8cf0617b5c49dc87d6b6f36ea233f9ebcf11c4f990ebc83d08c2265babd91ca9103b266213f1a70b433f618e77041d369f83ba7e6f4e3a2746a2b0b8b02000336e8150e103bb3a8a4f966c428e41a2bf08caf94a95fd28c821e00ffa573a4edcd1afd80c711062de0fc0918a9fdf61089415d7c43ff95f8830400550843c1ed196548aa50eb8b8bb4e3c19baea89a4a1db186bcfb7434338c94edbdc11906e1a745dff5c6ddd4f84dfda9d9c56e1fd3620a88294c37d1aea378a66fa0f96577200a55805c189b72be8a61afee220cb8e3660027c1ccef20a1818e2f097e7d0c659ce537c663f1526f479edb52661f5279998578842da932c3973a36c331f406849e766181c143430a5924e034d8b815092c7387fb68fa5477a2b13b215279fcb91ba20d001be6a6d01770faa732c86caf5372b44aceb1d384a16d369f8c40d019f6050a20a608a3bc237b872f797a2d8654b5691f8b40a50f6b14bcf6a10ec8872960fce1418fdc221ce1a5af433431722f74b9405d60977db264a77a16068d6b33712ffad10f851ebce351813625d95a7cf92b4aa4bd8956b3d7233d2febae25deb0fe70099fe74f22b0efc5ec9f4dbb695dd6abd49f5cf01b63b6cc3ff18b12300b68391c530d1b26fbb2ef6eb006fb8ad02fa09d20307bcefe5439c433d011dccaed256aaf6addb4fd8cc248eef6b4e67b9422d5d394e60d5ee33ccdb3de00ffa0d78e90bb4aecff3f473db93eb1fcddcf2b8143bd718a74cef87c735b6b271c2953a22e5b33bfb61b1a5d05de95c7cc16a441a5900fa4d3ade3f13a7c9677babe59e1a12a58296a54e8c416964effd372ee0fba067aeae3225c78d0cad302a0fbf026dc84a11b7173b23348c2dde6276ad7259ca39d0adc05883da3cbb63bb029f821dfd6be7e57f148b5263ce86b1e73256da47a718d14c905ad463c894569b05e5578f2bf9c17cf4acacba183a17d424dd490b124f72091318f2893e361b379cc8a2d5e8dd9f86b9bf5050d5c1d96424977d785d2cfd6b1ee85bc97015aca06348b8b20ff6636da994569cde643d371f911bb772205da04f8325b18255ace6eae126f666db133c35bdaa4b2800f2cc3abd701d17c74bd7463b5ddca92b79ef8bbb290f95f5986cf0e5f9d2758266bc40f933d5b270e1e22de73b118c7fcc00b4113e36b529b57a63d39f8d7b61f8eec05b6b89ef5ed6a44fff2b59c538fa51e1969147d2281aa2ae5a2f48fe3d86b681fb8cabfdd8cb1210ca8cc7ad857f6d4e4fadc05a6be12b5c0d0ff69a14377b5c4ead75f3a408047a3de8d0a540d6b089fcc5be9e3a806c5c4faef85184c2832e6316812146d7530f8dfff0e227cb4b27f9b442837e951535b6e749e84812153b6810854d97674fbbd8211596ee8c27444cada114263886855dec8ae656924a1ac900e15996be1b42d955ca64d64b7fcaac0dee2ee4082122f15727561ec413a05f8560458d3ab84605f0998422bd6bfb31fdbd2c32daa893cf342d5b30aff1e2961122e1aa1626d98cb1fbfc03f54e222280f1c50ace928e0b7eacb1529f041314f4818ea18dc222957174612963f766a869c1a9d92b6e3ee2b8f7d58cfadacf3cd7a1655d33bb1d5a35ffa2e50db512217b1e6e5ea6558d5c6e50e775ef903bffff3e1fae107122c1a66bd76bc5233e995b289173685ba9e2164c261e9106660a308c41390471e68495a6c0b7d9d4a99c8f846e4aad01c772c98c4f90564d73f1c6540f2b9fd8099d0babe6b8b17ff77e752f62351da6a84d1fd4eb3a940c6e513e21a498699acf72919c57fd8580734710724c6bd6b61b1f823e1035fb0e94ccfc2a7fd3e76f591a997e47a1223ee1c0a911cdb9a3980681ed90c2c48701821723fad5c0354b3007281e9d9369bab293f989a39494138c1429e50988c55ced01e78ba908c00fd5a24bb3a4c87e62cd6fd9b8214814ca4c20be439e51ba5fdc7f5b3bab3fb849e007f65183d73238e122a4de8cba39b90b7d6562c27c2c09a260d606727fe5847812b5aaa0c63bc32f75f33389382444f77f1b71e7c6cfb363512248dd31575a78ace42bf061a15da78ea5cc73f92af05f062c37182f9621e2742378a097d52a1589fc23568c2a0632b821be3a9255f30e0131a2129b6616f1a3ee78c55ecc04ba9c997ca3a2ac4361df21137722560907cf844dda067385943328c2f32fa0915ce8bd1fa8635b8a5bf0aa14b9413ee8bd49791cd3ff31767b1039b882ef22983c807646903b8b6d757a25c1efb542448888a6f52a5f3d64cd07a7748d5f474489ee6ac5548b31c22de45d86c026a3c305e7a91ebe444fd6e1ab87039af04cb77fa2033c4c218f31c88faa6b6dba02cc2ec5f359133a3e14562a44502120edb58377e5c6f9082e197febb5ecb8e087fabe36d8ab92e3fae195a1e71f433c42ecf175f9a977c527cccf2a9f6accb95782e378d02778313b71a69cd0b64708db569e24048fc68c9d2ac6d0f604a34031b96d0bbf24c09273aec76b1a344bb06767fd428de9ad11083a0bc07caf1c0ea8ce5e13499fe4b7804896bf29d02919222129cb6b976133c577332e30cc8fc587981919bce8c2cec964360e663ca6ae24f1e5a089493ac8b57d1d4c10749d13a7c9f4aeef8a09a92f411d538d4825246e7f6081303103f8a142014729d7dd5481d7d9909ba72d1dd71604a33d4a5e3a29bc485206d86a0d8ace035c2274638046d4507e87f96622fe2468fa72b01419e43c31f9f1115c282b45dba8c0f026a5cdf61c0d45df9363743d27f2ecd1d280e221ad369f5785398cff2e54fa97f8875107a1aadb3941d5e9cfe597d4b580dc6cca92fc2faaf996c7c50bb574ade95a0c98423c94c07b101da9619094936a12c117b818cc34d9aa45878cf76f35ec6f7017cc518f12c3b8323be93f219d11f549109e87f7a88a5fd5d0267fc7338562c84500c3299f69a557ecf7f64a9498daf699de2c81b1cd80fd47ed0f8e794269014bd54e43a5049128fbc47bca067850e421bbf2fa2b78816c4fd64d4c84b9a5c0e6a478c9b3a455a9d61e51d1bcb8e24226ce058b3e9a5652d529e59959fce9386ef1b671abde8ccd7f638057ff85d11c7291b6709ec11256360230543234a6c42c7e0b09ab0e3b9cf32b297e506dfd991b584efb6ec078b0282b07658ad532f4af566cb5e4730cdbe48cf2c9535fb58cecf337d3f87f76e4bbbd097f7ec1cf26170d2a91cf191e815b5d6bf6e5c15539d2c671f4dc2fa91a486f67534ea7bddc33353cde01d9bdeeac8d25fe494a4a0d98b9f1b102fb335078a9136c0b968f076d0e57a403cb23693c919b513d208f0ff0e42a10ab70aac4a8f4cd07f72775340ad13c6c9d18148c0627c03a1a023bc59d4835e7f433401772264519fb88acea7869b62c9883b3ebb91a97cca820fb0b20af6b17839df090384a643e67c48e516eb0e1b079fac6619780ed90104d724d1beec412db14911126d754134cdf070afb16bf69543f0c37a00dce0ac1a75e4c98894f4d0dcde2255aa2e42b91f88f50417f53f5d8f18960a433f334a84ce584d78f4d71914b3d9d3d6a0a42553aeaed60fd724977eeb4d2912f8bd046dafe7e767089e419836067d267bfc4c3d3278b987e455ee64a6692dc6da31f155aa9257a270bbf8a3d6b0ef8a1c3d8f7da2d6f9ef99d1ce909c0c170c22af387e19de726f0e0bb80d716263e035dcc81cdbcb15a5441e1bf8849e64459dda4ebc01c34ed2de7d958c9acc6e20b6013c2e38c84d5cac1774cff9dcab906962a2d2d2244959af859519c84550a77edc74564d49bc11d9cba473af9a73cfb05365a60a1823c3929a50861d4bd9329e1c3f42188cdb47632c7636ffc1ae4ccec4d71ee8b431e4e830273190cefa47db8fc12f8527b23d11e87293f6e897cbc523bee992f7bacde76c11f4f88984442afb52a80dd47050afb051a6936c951985c87fc57e16058daa9093bad1525cf2d16bb1a5b2373a4fd553b4c59678c2e09a0555f56485aedd0be21aa30bd1b97cca418e0191d613a703f24858995c19b6c960e9a2095aba8d94645a8004fcf71e610ac23e25c4ca9a495eb3860cd993d93675205a31e5086c2954e796b94984e2b624af3ca0e180b3a0f5cb4acdc59ecc3475fea51f18cda2094f87de401459f070186ad6835cf6c335cb84a3d463b64c7526450b7208ba95cbc1108ed62687f33361de7ba99b4816f1f32ec8dc170b9f5bcef44f440595d61778c126fdfb649f12e5cc21036decc96c0e5b2c08600ac909bda8a06b0fec8f8d99d7869018010af6317a6034e400106750fd47bac98a624856cab132a45e45412a2b3aeb55c32900032f119ded5ba7ee7e8decf0fde794cac32ff10150d65819d280487a03698fbb09d03e6b677b55a4591976f40163a42156889708561800f9099ee09953a7c5a457466c8e8395e681b2e11311da83371b346e5c25d24e0fa68e3820f6a842e77ee9b3b78af1557d833ff9b9a66973e58759928ad88474ce743319da6705d05e199e9fb4213ec069c709b13884740ce82410bf32ec1384a1fc844db988f5a6eed7e1de2a9775ffed8f915e98ed235eb8695f4d4b4abc932bdde1e000f4950b25bade5231b8de67483c7804fbc0af0a4fe354c4104f7d28a3ac635a152684860c7c3ddf856298dd635da4d0961bcd7b4e8ca9e2d435e1511a0a199ade0d0898c2e94ca3158edeb1d5a9bd9255a74d323c264561c00477064aff66a0d0ebcd0e6c6005ee4945731155106858351d00b4ec6714ade0533e7d1542fc4111e692b2c86a4b47f63186ffb9f99db689dd52b26e29b7ffd28da4dd2d7a80ae2adf325cf125e1f1c457aba3aab2acf64b60d6af60d663994e193836eb2642f6de29a31387104c1044b4522718563f459c7e50040dcc3b8d2b18210762cc008c167851d28e949802d4a3c31020b69e61860e1b58322b551040100da521833068400303729c78009108fc00075f7471821174600a1d9f001c506606e0fb84255ce1810dc25841088628c0009800b1846868872234ccd0020e2db218a2031c288010301962b09586981ff8c0c9d894c615a200052641b480a385832e588082138c40041e60c00210001bc00f4a888682be90010b625002278cec40075f74b1821398408a28903080114bb882031b9cc0045a7c204ad38741402d982b4418c20f5ab0822cac5c5185120af810360026412809020ae2b269c17c2183165040024a44f970003c0415010ad24418c2173270010bb2b0728507aa586201519af0103424880b488f1d36355aa74e830843f0810b5ac082156461c503552ca1c40214102508b0264c74d0f155810a477a9089e9410c5392406289921c281480c48dc90a9634bc2b74564051a19bc24a03a70c74557453703add0e2f87fb5c703c1c3cc7414317030783ea05cf052ec7a9050e078b8d1a353cd649e6258605f3c28b0b2e31b4b45854ab94aa4ba550288feb54949600bca049c5a26239c5ac7ea8583c195e908818a259d5a45a563ba71719eef492829d6c1e8cd73a896144049a52319d8ae5740323c30e079a5a92bc84a79b1822ba1e30a0909cb8949014340d39f2d332a23bb59c8ed844a1a90845dc0006111ccad5a2810811dde9050f87e466785038d8a02995a2a1280a0b323d06f07620e2004d2eb02879b9f172e3491116c8156e81429390ccb2436c09968ac58341299d10900411ab534c4a064e860711468082e5861b4459a1f18098ce0545a4a5cae1d4c30751a5081ba89a96222d02483139dda0caa108123cb143111960000e564e1418f04802b8687229671230024647864b12831744e5c20f225a5439a45a56311e90a01d3c195a8a40a1a945150473b432020350911616232f47a1101234b570baf9d19272d98049b5b800d10c408aa696959119644834a13c168a4bf9906a410549ddc0d3e352b2d8b032926a51b900f3c352c36988a5851b3108e0051c37ac70e8c1480044b4ac3e150f181e40828a542f27d50b2897f783e5e564831351b1a85a4e3a503d9c7688702818bc19148e1c9615e8e5001424c7aa05070b10227c865c7aac6a4e359c6a4865a0e98698d30f0a07d40c2c359c6290d9e1c95044d3a783f3661ecec943118066000d3081ad1de000185b54e94006a248c010423061a5d983b1013001624912244745477830e2dae15123800000c04f2c58411a679461041d780240f3820b2a2a3180c11452f800a4834b06165e5c525c6861c50a2aa688e2024f50a100921a1b747c128800d2511a5570820c8833cc3801ccb69660009df104695151a1c414202c3910a30a2a5c39fc68e1864bcb4a15030c231001930f898678082a02e4eab1c3a6462b06e654810520e00035c6f051820e70200343284902e4478e1ba71980d184243c437eb0a8521e38c2ca07a0704003902080052a500106100df104b1e185a38237850e8dd31537a323e364840e08a831381d7438e86ec062839730ba19745f745a741ef03ad0299d92b81a381a5632a0628071a16ba16381bbd1b281aad1b538d629a67341b5742cab55a752a1ba13e7715caabb5bfa473f69964b12a26ce64a2fa9e3430bd25ade74732f2e2f37e3161e60eaa966b558d1ddab66b560a0bb05d0ac9621582db9633eb36ee335ee1f52cfd483dceebcb306faa49e6fb2292d3eac004cd1dd31cd6af1d131a755fa359bdbd40c11e295d25ed6e4fbbe206ab44ee550d3a822dd9dbb058131fae475cde1d0ac150556522acdf843af81798e9e6d75fc36749b9a076f69f12a6985d461fd3c1daf7a84f559ab4e75c56b99565ff499f507a988fddad99d19fbd3eabc2c5fb3f8386de0e6c7e29c7167e4b0c379fcde12bcc15dddb4bbc966a95818cbdaa4e5a6a5a6460817a2e5a68500ee047077f7ee8e4877a74377c733241586bba870a2ddc54a95a9017437ab59a9a16e77b5b82bcf5e746f89e51a8d5ef73c7b3186e79021a91a566ad5ed98e652e68ee9d7b2bb70584b2bf399f55c5aefee84b4b83c4f0f5f09a47592282add5f8a73c9635fed50cca757eb319b1a725e5b2d5dae6a692689621ebe284e30085777bfd02c149416653397236ab6bbc297f92d9c7fc5994ba16bef05b2d97b6d0f4a48f7f7a99a858aa1f5832dda8508f11d358eef17da51471e677f2dc8d2ddb8bb9734ebf46a191577e0e0cfb77cad6ce674339d6ae8ce6172cad1dd4c6ed458c262794ee8ae591e19dd34b2ee2e4897437707759f5a272f005a743bdd1d90eeee4777e7a3bbebd1ddf1e8ee74babb1ddd5d0e47b64b3717a5bb816896f7f2a1053fccf97c7c39383b3eb04cc76c474e8e8f1d5326d3d10981ec98fdc0b1f4c39fe705bfdbcd98d2feeab0b43b41229aeed3aabb07d02c1962e8b4e84c45127b2ccf188e66753dbabb46b3ba29dde25cb2e3dfd614acee6ea159dd00bafb46b33a1eba7b08d38795dec7953aa6355e5f7421fe320fc5d2d6b0db74b74e37577677edd60f78d0dde9e8f6ee2eeceeb1591c13799c7134e86eaebbafafbdf7bd4e0fbfde32ffcdf5c717c9917aa6f366a7559af31caf9df96c0e8aeeb6cdea0974374cb338ef6e169ac5b97477f8229d33c79f6f32f799f56a699e7ebb76fc11dfaf51dd9dabbb9d34ab737437be1ff84b30db04ad284e1db73baf8c8a3ac08b75c8a828a3a28eb2d27098ca4a459c6ab355a996383d7ec07eec30e1e47c40807c3b74e04a670d14adcd3ac832e3f2eab81f8ab494e9f8d1d674747738dddd4d778743779fbafba5bbbb4135c61497dea2a9e9d59e4a080970716969a184f213cd09a8079776e2a1c4559422887875de9dbcd3c90b814b8b041cc7cd2871ae72e2bd584ea71350e72957b19c54de27cf572be7bc24efc4717ef2bc53bb74509238f75e4e50963a5f65cf57bef2565054ab2ea5524ab1d028a54e3e44d210afa4ce39a59377e79d12e7eda82a2ead11705c52e7de4cd2cabd97ca5d5cba834e379f3cb5d479ca51a71927395e4a29ef1747d3cd38c9c1e21c8c11ee75f2a4158d13282f158d12cabda4138d937ea520070bd5ba82757217827a99e97a8ae8b81eafc97b79e95eba7e49d1c05044e5292515cd14b7ce533453dc3cd701554494933779ce79d7b1780753c4e2a7992842b838c7f232445393cbcc113af478de3213450ad7e3f9113af49cdc7b29c273ce6166a2c0f86a264a93e71c4d94223c6ff123bcc785a6c9f3169a2852bce7a5c9f3ce5f668a80f1c173cfbba2ce89742d335238295c0f8bbbcc443942879e1697c2f5b8f8e9e58546089728aba3ee79a111c20a2417e75c88961e9719d64c7b362c29e77a5a1e6039e9345e291aa5296e9d4f713bf9a9a83b79e7489e73be82293a79c31479ee5e9732c265e5018102a5009d7b5e00ce4f0ec50a2495b738942852bc07859472941780c557de0ee508ef41d14029000b8d14efa12940ca573452bcc773154d01529ca3688c90f1bcbd0029aef6703333524e51a41cb1f28074ded3cd1c6185131db899234e51a458e144879792e7fde2fc88138d154e747879de5b28716965d1450aa4f030440c9ac078b5735bbcda51535eedaa2a2f947331a022e3d2ed5a82010504038aa67da6872cc409a1689286802145d343d210a8a621d23907a3042b9f5e2a2195f7cc0dcaf329459334440b7142279aa4215e298f06061a1838ef1c060ee5298ee6e6d57ef38221e23cc5a114e0826a998181f318ef1e97191999178fa11122c65f58de3d42b8388c276971168d1032dee2302d2e44cb5dbc7bbac76506068e2626c687e881f2e2323430703430f8e0e2328ee422d3e230345064bcc5a1b4dc8585c659390c9cbb67e3f9103d2f33ac550e1867d14081c27a19a2a73503c50aa496cbd040b122690a194fc29911021734f50851009a261997a1298010449ce52d9a1ccfc673efc557342c160dabf3b1f21897f1a41c10c8d0147936ad10b0bac7e341129868ea81f1175fe27af05e9cbbbccc704d2f0e4303054a8c86c9b3f1fc7aeec4a87b62dca87b60b8a697be02e3711c156e0a1527a87431fe3223c3e2a7198e668993f1181a2850623c8686abe2d9782e4363d43d51b818194fc6bb27c673229ecc4cd7d3f332c3f5340c4773a371569e0a869373ded3823ac538305655d838916183b381721b9c0d1b9e8d931352366cd8f05319366cd83891712ac3860d171b2f302f2d5e0d964ccc8b8dae46d7ea589d4c17439354f3da39997132e364460d4e4583540ba9540b9ef25319a9540ba953195570422a954a9dc838957172422a956ac1934e5752a9540b3b3b383ce5eeb16043d552c9a86054aa93ea458699f6629869c1619869c15f9869c15d98c931d39e4d0b34493868c49d170b5d8b2689a5cad1a9bc5ba956669ccc3899911a0247c732011c1e1373220347153a349c1013131383c21173ba72bae23132aec28103879fcac0518557524c0b078e1319dd8f1867f9a90cd6c909afa418cfdd0b474be532323858aaa4d395578cb823eeac5839b8140e9a249913e72cd024ad6ed024b1689272d4a049f21a8e2629e775324366e66406a723e664c6c98c55143230305c950e0bcef302e5c9745ca4bc0e8a4c8b4c4b8c8c0c0c4c8bbfbcb8c8bcc0bcb4a858fed2f21799176fcdb4c79a79b922e625e6e4c9c0b84b4c4cd759e9ac74563c55125d5c3146b7ab5443407149a9dc45e5d2c140e592c47ab9a8fcc4858b4b950e8b8e0b17179794abdcc5259f5cdc25e549f9f4828bc7a854aea249baf14305c3ea60dc467b406c783630312c57b1582dbf310333d35dcd0b8df8f26271171a1b35665a68f2e9c58272afb37283d5ad44a0aa41a38241e705172c168b85eab860755b745b74563a2bac1ade6151a383418d1a5c1519efb090e9b89071199aa46e8b174ac65335689238191996abb82bb82b3caeb3d259e9ac74564e329dca072e4935c8b0f4a94505f7b25a7535ad2a584939fde266faa54a82a5c4e2a85752ce2ba7fbb16a81f931f232d3302f5c8b6ac5b1d074565435b8ccd4dcb8dc34152cee9594d32f946a8a1a6e926071afa49c97e72c2deeb910223f9d154ec70b4d4bcb0e0c727a08e9ac703a50de55ba1ad48c152e88eaf46aa141a13a2bdc162c2d5e38978e45012e5dc389252927e687cceaa5a501550b8675a3b50156528fc1522dc152927154cb65605835bcc5712d8771186f98968ac5c1a864684e66702a1aa4254c909630e97ea45a032f94ea64462b03ac94731d0613d5122cee95d461f703c665dc73b1fbc172cfc9ccd4fd88f193199c0e168d8c4c525721167619273364684e65703a5030312732ba1a94195c10154dd2103baf2494e3215e294fdac9f1429394e3429354a362a149ca9163075583c7e258ae90e30203e4e2e2f202d34ac0620acdd28dc37ac0090b1790e101cf89ce89ce8b34babbd1280247450c5c3cafc5b8c2a59be338ca3a55c149c085e33aaf552bd58a0a1a701cc7ad58a60083e5c5f33c8fa5450a19a050a816170cc040a552b9bc44e1021616169617980b78e1e2e2021303050b62382fe685f34e4f64c1c54c165b748c10a8bc925e325637e195b47ab59525ac24c0f39858358e4c0ccc8b4b0bcb4a95429dbcee859ba12c9857d20b4bbb7a0b249ef094e0d2ce751eea84ea3348c0711c2a75b280ea039ee779299537830da85e5028144ab5f2a29085142a95cae341d75d64418114cb085e3aaeab4017589cc1719dd74c30427b42a0c0124e1378259d64ac93045eddabbb18c10a46b005d7dd7d8a401727a8b204178e09ac1619308a90ba8e8bd2af554ed2ca739cc8385de9f68e68086ea6c8090f085267d37917258765d5798a73e28677cea55cc5e2e21e8d0d191a989a97c7d1b07865703a54273292f08621a2e1a438d1f98a268995038626c9bde6e56406c7834ba5382e9534c4ab850156527f91d4601071ed8590292cce5134274f120254a36b59810bf7d2ed554179c300f1681c2f47e752e401910112e31e0c8c67d3d1acbc852689d3e2d5e2e23133ddd9b8d0247157bc5a54de7155683a2c381d1d4dacabf15c45534493d46210a149ed7456381e9d7bde030acdca8ad7b5a46025f1d8e287ca79cc5881d5b97b5c5f498971bac262d5a8e1cd24ed2475ee711eeb745a01b9ecd0af7ed568f96985c36db80158f01b9e8281c1071c6ec39170d020b140d39ecd0d5fb1f86be535645a35dca5c6ea85aaa17aa16aa45ea81aa817aac6e985aae1bd5035ba17aa460de75ea894b75a3b7435645aac1918d68c8c8b3b2f189a93199c0ece89782f334550af95bbcc14f15e2b6f9929c2bd562b1a56e378ae9af13c6563e694d47a795e80d310321d4a0a4e4020d50429619ce00c2a0ce18b22f49c9c8042037e70050d2041c5078696100532a45842120978c2093318d0c4194ae8c20a4d31c08215c6ce155fecb0b870f10c38018a2928f0012b5e40c6cf04b01803094aa8a00351600001de03649e7004171081822018b144030ed0c5042650440ea8587123078bc5f2d389e5d55d5c40060c68e2010f58a952a893d7790e78f52d022a08114ee044142588b20029ab0d38408681287e30832f2e6004287a52537cc08c2b2a90032c9ec0041cafeee2021c60a20926aaa47820011c30c1040f40c0075c29a802892f1c60842a0680a106677400056094b104289898010e46c04415c2984204a21081a3549df29ec8c1ce15ac309ef8e20a16172e3b57705a3cb1c5134af019a2cc800a1834e108474c7139a008498082023ea002057a76ae78c2074f88e095c45a1dc06b51b1ac2c3081138bb73aa5542e342c2f07d7e4290f6581255aba1ba7d32985429d4ea7d309e5a1bc23a0b8d3ca6bc089880ee4800b2b16a083055c810839c84006ba3002093da71b2c142b868a133638c2069a58420735b0fc7472afe105890fa4524948715a2c9142830a2ba0c008495461014c0818c000317ad045026a9014188778d6eaf4a15667f8a33dea41003c884173fda5fbb9ac73f4e9d78234cfd1e672d6e9355a696f2dfbcb309d3eb33c5ca0bb919ac50351101add5fb564395f66f3cdcd1dad13a020167477926605916e6dd6dee9e1acbd827c6005adfac325266dc6aec48817dd1dcebf7e0b7f893ef846469ae8af4e9f591fe9147de82cc2ff452a929bd1cc473e887416f141aaaf34911e1bdd9edec2f9d8a822c9f58f5e456cd0dd9deb2fe5fa4b40dd0da45945a6f4173e8dc51ca9c84eff786d9e34d3904eb274f217a9c8aabf59b5140806dde05fff1a121008ba5b49b38098d83cb37e9b36b7d5a7637abf3ecd6ff0b42101e5f0a2fbfe6c56ca1e9c24f6997525708a3e1d93b376bfe8abd46d6895e6d2deef3f5ae35fe25ab6fea3c58ea70de987ca4fd16723e4b3c467f652fc21f54b32bdbf2cd38ff67dcdcfaabbbfb0967636f3fc5a7eb17caa7cd5866eb3d730b5f9ed6d4471d678b55921cdf2a9d11ba166f9b0ae9dd98cf3b7878cef96c9f1737dec2feb71a2745cf34cfdc14a310ccdea21419c6a2b0fcdeac9d1d8e21c69d66b8b5b9d7eed0cd39957ff1ad24bcae75809b462ae21bd6a94402bba28d28cff7e2dfbe372822ff3f01f044b6c6be943b3887881ff6736f49bee9ea15944a8747f9808119fcd68c82292a33f1dc4b8d539bab5483a50e94ac7fbf9435b41c7361869960e455ff8f92fe92f1365b1af21e950f38d657d8fc5fc6b48e1873c23b884c7a646c8082ec9f84358b5a3cdb8047930cf9337a4fb33f296f1ff6cfc4c61ff3358cef8431e1e519c3e3c8ea723091132824b6e775290cebebe8c470893ede10983986c4f48619f6568d6102a43c81ccd1a52f435890dc909ebe321dd42c4b8a15942a8b084905f93988e6609d9e9af494cc88a15048caf492c08155f93d88e6605a975778f66050940779358512c08ff3faa3f614fde328c56292e61239d22080b1f36824cb01c569f66e580c5f7a2789ff45ce7745ce9ccd5fadcaf369b6b69c77979fcc666476747e937e187b5b43d18c44c53e83e9d05dd1f4b5cad4f782df846478c72697fa4f931cfdfa777fc259ac109c3f47e582d89ffe6c963b353facd4e597383bdec36c75b38df88566958bfe876bff4e1bbb6e6e294597107ff6cc7bfe12b8156f4bf13d36a59c4bf5b9d3ea37ef3b499b1631b460733bd965ed2a7e3e9e1e31a486b36bf093f93e3e73cc3fad8a7e779df633a3cca98d7e937e1e70952912c7ffcb3d95f898a367f9b7f33ebe3d7b2873f5e3babd4dfe6b83ef8d733b6a3e3e937f86975d6c05b5ab7d9b1ff9d79b47756c3f75f9a35517cf9e7d87169e99d4b6eb355c9df868f8414570b625bb339ae5fcb7e330bf2cf3175219f256eb3fff867238a33539f51dfe1199749252e6b3c7c929c37635acbee33fba53893f877e924dd2686631efe92dfd81a3c6bfec3878eebbcd21465b7efc13f4cce9b3d3fc6d36995e2a7d57eb7fba51391cea5e9197f9e229038417b04e38ac40849fe0b56eb93edd063243528aca52d92ed9012688f541afdfb4bf628dba1175602ed119b3d92af255f7ca45c1229815ff3095f462f8865e5911d3a12ce9ed95383707e8c5fb73a85ac45bab606d6f9463eb74c8e402fca68bebf5483b21dfa1165b322116991bfa5486791b0fe11927f42f1588e94698e36bb384b27ff84e2bf8ea9734a681aba9b34eb26a69b467bac45b243a258168159d8e7127ca6bf393b705e7c8c732df8609d25c614967199144e587e8c7fe93efe5b4b519cbf44c50923ff9560b007af9dd5f2fc1066b3a23861f767998a34973058025adddc00ba9b36cbc60a439aa5230d6cef3f715aa537b15a0fe78b6eb3e34c654e67b3127bf879466b383fbe31d99beb63d9eb07f63b603099eeb6820e16726814d32c96a30e0f1c1c5aa522adcef076e7482f095bb29724272cfc4b49a6271f04e7b5b3fc38dfc7252c0c272d9dc84a2c2bebdf309c38149f861406ce9fc1f26398f8609d202d2c9360197fe9e4ef9d2f82585662587e0ccb8fef8485e1dc9169ae1f529c79cb19c60ddd1c0dba69ad207081549f24b2433ebe9c6f076704995cae116482d95033a40bea6e6e078ea7bbc366d9e474871fd2251b7e68c1fa7e13679ea13b520e39331e697eda2d497c7b25587eac836535e885ef37e269f3a183f3e09ce1e09018df4fb6a383438e33e31f697e35ab6608ddfdd7565a5098c14ffa9beb8f0fd6d2d63aff96e42d49d8e7f917c44f1f168613df4f7663b297c409613791246709fb5cce5c7fe9614c74f6971445aaa4047e0d84e5d2d632b5c1f263fcb449c2c8f935587eac74e7f817df4f26000de0d25fe6e3e7a0d1e63a6b453098bb3eb43570feada5877f3fd77757a5e087d6a7bb6dba392bba9bd62c01d434be5f0edf8f8f070ece8b13c438b74cef24bb0004e088f62142dc5db198673c497fb0cea5b04e1e9e9d5208f849429128ac2f9c9f762752ae7f6fe973c3f7e98c663bf4e11ffdd3663ea251a16a29be9fa4bbc95bc31678da5c1427f8d3ac1ac8ee1d9a5583cf9a4faf74fc4cfd455aa5d85f2c0090d1dd1fcea58c05800dd81a02404a772369160078747fa2f8363cfda6fe12a539add28c2f9df9ccba7f37512c670e863474fc3eb38e5fe117193f078d9f83f0b5f702d53255f222395a9ff03fd7a01a98674fae4fc1fada91043f9e2fbe11d38735a8bb6d68168dadbb879a4563d3fd65fc345a893dd76874f4276f797e2ddf7c2dd71f4bda9dfeb5fcd90ef9177ecdf1ac3976db0f554b31fe23db0ffdf0a1f37f6b484a1304bf871f3e7466ede8870f1d6b437aa529f6e8f0b04da31d3c6cd32887876d1ae5a0639b463b3ab6690444c7368d7ee8d8a6910f1ddb34eaa1639b463c746cd34847c7368d72746cd328871db669b4b3c3368d80ecb04da31f3b6cd3c8c70edb34eab1c3368d78ecb04d239d1db669b463876d1ae5609b463bb66904c4368d7ed8a6910fdb34ea619b463c6cd348c7368d76d8a6518e6d1afd4f1088a9d07f91eeaee99ecdf230badb362f8f1d5f0e4eb5d92babe1598ba1596e4577dbac38bbbf64ddbd86a7115aa5371fb95c3012677092a3f571b9c00f2d686735e8c379692412a38ce7119a690d7a42c5596911a09b48922f1e3c4510769f649a23d31c7998e608525ce9cd4c0f7b3c65f78bb0121e1d1ea5108ca7e8f373bf08bb5c30ee8a0cfebd16f46cabff48732dafcfa8879fc12799de3175ff1a920789c1ae85855fc3b4d299f774770cd8ac1992f097b9cd1e7ec69ffdef749cc572ce3ef4176514d73fc2f7c3998e395f0e3a383846ba9beb6e870ec5a73fe41a8dbeedfe125126cb7bad4f9170f6fcdf2220a6a3cb85eb2bd9ec7261972b5f4b922586fd38ef0c9779e6a307eb1467cfff0c866f93bc5d9b6758a79038ef9344e21caf1dc2b759e4127fe94598cb0ee1ffa102852fca26a63f4076f49839ececf8219b407466b21c9007109d1d70f2e891c3cbc09c1d1f3c76f0983aff618f1c1df0757efcd4f901fe0873087bf0d899813d7cec00c294a3f3e97c3f70706ef3de59c3d348a5b3c6c323ce5ced137a6d8d4708f84966d6dea22397ebe9cd3012cf3cc35cff7ead521a1e7ff284e5fab80685f36b434aa0157baacd86b0f0317ed05e92e743eb93c4e512f2a105612115e712ccf6951caf1121e027e141f2232cbcd3469676568344d1fa84417f2d08c3bf44331de9ad94462fd17f9eb4bf34da0b96f11433a64c0fc3e2ac348ab14867fe9fa35c12fd58e2bfa4bd6128da1afe5bcbb0b03e894b3cf3fca1d159b53e5f7ee94377df2669958a327af43c36e1d3427a65af1a21e027c1308cffd22abd650a4e72c2befcd209ac06e649621a86f36b30d0c908733286479f673ea2554a9b18bf50a595cef16fb6354cce8a619f67c6b0db14ffba5ce3677cc33012965f142709c3a5588e25c8e33761d0bcb348083985fe2f520fad524bcba58591e3adc1c8b1ccb5b4208c86471839963071669b3fcf9044e2438ed7484bd1ddb7594c8ebadba85939b0e89ec1f3ce22afffb6fc327f3cff8295fa4d142748abcdb6dad16d761cfe5725d08a3227fac2cf998ea305391e94c029b3d75fe675829e672d8313d779b3d727addba1940dcdca91adbdfeb4796dc6dd4cb35ae0e2be6c2c2be9b729be11d7dd2e34ab8529dd6d43c56f430fe7d73cd7bfb3a8bb71ba3ba7bb93e8560c4c5f7e4c331567bd9f99dec56a2f5daa404ac0800d1ed84007288e4084165091032141b0901a621582965608be50c2b378d2d5268820060308018b00c818e0080466a6c0e83441812baec061420e07e490810daaf829000d901230c407a93e2316d627c919f35aa636bf4fc53bdfc3afff32c736dc6eaedf0d81ee26a25bfe13be130f92ebe3bfa4bfcc332e4717bfd659039d1c6fcdc3bf53043fcc74a4627757a4bb0382d2ddfd7477916e9504ba7b886ef5208cee1e7d7aba57779fba214a280da0fb4a53ac55a3aa4f7d1ac2b58a0a17a25a9caa6538140e2e865be99c80b4c7c4690655b75aa14e1ec7750bddd75d733e9c74e05a1504d53229ae398e43a17a742e5d731ce77546384ec5751ce7a59e745cc7a93a1c461c97e254dd71309fd7a552352dce9dbaee34853b715cd7c23da1e3581ac59d3aef392ea9e3b86e85a483a1e3bad4a9e392d0715caae37ce05ae038af5ba900e5a962381c1cc7715d0e1d0b2b8fa300d771a98ee33a0fc675a716961edd0bc7b3e2503578433855d79d76782d5dc7d9c8fce00870dae152c42beafad41d06b893577342759cd7a826b82070dd358bc56aaeb913d7716670abee068eeb505de7711c0f4e07b7e25ebaaeeb523e9c0d5d77eab81cef73716125000ea6c60bdd033854e70277e23caebba2ce5bb8537734d51ee314e0f58c0ab826c282cee352dc49c5c1709e135db77279dc8ec775ddc9bb17961a0ec571aa9cd30e1a72b8ae25b5ea541ce7e2ba6e075e8a63e94ede1247d3951ce7e1702c2e1db752711d916e866e864e06afbde664b854d77130279677ea5ce8ba8e4b71a719c75203e782ea3814170304862e48e5c2d271b2d58baae37ce0561ce775dc8933c2c9d0e5d0c5d0b974aa0ec5795cc7759c8b93a173e95427ce43791c0784f3b81a8e857be1382e083743d7e2a9b8d589eb38cec571291cdd0deed471a8139743e7c279dd0c9ce775dda93b7139741df742d7c2b170ab53c7719c8b93a16b5179dc6975e2380e8873396e5809f17040711ec7c29d72702c2c2e972060988103c089ebba54a77a8ec5428e8b118eebb88ee3389c5635d9400cc15df9c18cf61f9e20038c1f668650869219040e2001c2188d34a3833e7a62067f34bf68a327b4a080d18c25f2e189a42b45b30791234fccd0042333869c1c9ec8a1238719a301204080a901c84c0bf703089c157ec4d2a8c00fbf427f312acc8013a3a2bb19430a7be33937e040e5e60c1a62fe4283b750a3a9d868d2319412ed2723babdee6e0074f7acc64f37014a26f87e4f4e107f37932eea6e219ad58a62e619c260643c183c13349d14d0743a92bac139d513886049d18442bda804a003224e4fec38b5f01285065209683a1d61c1d112430e239a587e3801e1cde092532fab22ab9d548b1724470bea054d2b2c9a6e3021820588470126bcfca009954613eae5851b34bd641a1334b1e488e95441503a504c4861c1c5850714714aa249664666c6c369e958e568a551040a87d58e4a8827e3095945d1e409edd0916a496da0c97349bd70ba71d9e171a797d34d4b11140eaa1f522fa45a52492f375e6eb0e460c1a1fac1b34211a917bc2a45a0bc680a40d2697458006438b95e6452ad13e7e1783ea7a29716529d4a890a06d5108a85182ee5e2a14e2f9ee7759eca5b792c2d319c3c1512550d99a296962aa5e23cd5a9c749c7895bb9e0c140040b0d280d34c56c207ff39b570bc6b0b5404a4f5b0b8cbabbddf6331e9e0c837981c5d724e6c5946e2f625ee8e04c6bf9d9dcd4cc2c0ede23e6dfb5b389eb93d6668f588ba484690a21a1557b95d8a15b83ee8797e8e9b5b62f622d921d0a1f49a804dd7d2d8f905ca9257fa448f56b369e17d314c295ce910b928b1cac8231babf582c53cfe483e0544195bf2aa829592b8675aa602705504841153ac79b02292920826b3c5ffb4a410c2f149c8102276c70a637953a9e35eefe4241ccd66cb1170a6ae8246dde028cee2fac334ff2b545d3dfd96b8b22253cb7a0e9efce7b93f9cfad28fcbf45e11f85af6497de48561ef9e43f3a419b600c1338d1fe855f03e75f13901fad9ac048f7575f696613d47409c02841951234559ba57e5482a2ee2ffc12e4e86cab1663fc8f35242db470e227488b25f8b5d8e90fcb4accd2c2bf48166864414577b5151f6521a5bb6f7988950599454df757a7e35b826016accf0a18a19529df9718d3fba41511b7b85c3c8ea7cb4accc334471e1eda5f1e242133cf1f1e9b99e78fdfd4c84a4c821a2caedcb0c0020b297ded0c8b255f931816352340a34720c6d724f63f822a23685a32829cfe9ac446b012c118fd358989608baf494c044e888014011111e408c1951078d15f935808a88480a96b01532c2b71770784dfff10e75627ce374c7436fbc29fd86995de7cb4effd6666fd9629d3d0c834851c334da127716425fee8cd5ee7fd09c236807306ca4afcf9769f04299e3d5f7b46beaf540ce787e493656e1273b982b8f978c1fc7ee337379be39b48c3c748729d36bfd159ae16bce5fad58a959c2316f2efd2af655c665bc9397a6c4634692c26fae3fb4bb9ce6b6b361ffde5b1985bb05a9aede82205abdfc457a2628d38433c9d46657eb3433733faf2afca4aecd886cc748333bdd1c84a4ce3bf44b3789f2cb137d11404ef81086709911224fffc76e76803bdc945fc9b55c734c881b21db243d8c80e21c976e8e7da225f6d44d90ed9a1f07f89ee705302693efa2f92ed10969547dc0d8e4b77e3d27eb5f702953ed8ec91cff36746944ba2d0da1daacdd61e5e944d3a8bfe8bf060bfb97656a99dd5a05bc63c304da16a69fe9f85e2d32261909dd520a31713139dcdca23fcb4e8c6833fdf7a848cf6fedf99ed2b8685a2b5b98aa2af0a1aa800a3bb3f8cade8b9b45e6995deb8cd8a2ff35cca6c544c692a8a3e9b908a9ca782f5e529b4907df8a35953e0299cbce5b086d5828fa5b8327a21c594eeefef4c0a22fdd94891c3da8b5dcdc20018dd6d738d871868ba9fa7486b3cf66180c817051a9fcdc574f46b67517821eba15951901dfe056f62b12876baef05aed01d3e786d91f06db386147ef84a61a5207ea3a0f08d8e7ac2cf950a855fc3533cba79926f14666c8f645cfe846fb36105ba408e1a8dd6a020832ae980628b9e5984a209fb92bf5850ec7c4f6734b4234fb39eb8d2fdc57842d11a411232234a128ab6f60416dd5fec0929dd5f2cc6f48e9f20d2fd84382f1531babf2febdbac48a58a1e1e21e027f9bf3398edeb4fae3f665c2685af4b43970b6966f1432a355f939805d0680b78c1b2c0140bc4da023b165855a0074115784163a629f4557196215da261a54c53a8324da18f66fc3ebb097de6abcdf4967669cedcfeb8eb2b157db439db1effecfd2794fa2d2d5d9ab597e5a7dde6884b7b0bf2efc8feb84f274baf4f5a9712a6f796f6e679bac56ec3fb4b6fb3463c163eec6f093eae34f65e277863ef3fa17ec34d14e7df1bbcb4379f05f90c911110103ef43282e44640482f20a31d1ca8c7918e9008f9579fb4958e5e19330e058a87ef43781c0a14b7a9c1f4fbbf33ffb0f5f0c739e49fd2df998737b223d16ff5e55f198bf974fcb8c436fbedce9beb8714d3aa15a7c7628e1fdfe0d9cc7e386f1e7b3c76142bed0f1e8bc55c08771e1e2133cf1fbfb9457ec33313ff48c8486b47e4147ada1ce2ee4e00777f9192a3056d8dc562400e00b75924c7c9f4a1cf8cf38771a6b5747b1f8899c5ff093fcfff09c2ffe4ff16c1a1688dccff79b10cbf061ed12acd5836c3fa241310500dc2f466fc46b74c435a3aa1b42446cc3c7f8c5e619012688f607c95407ba4fe137b67ff7716d2a6588436c5197ef2965daeae87a60931ba7bfab52f3411450d4f0b3411fe4cd144cd572dc50013643c8089dcbf447198d8e9fe423cc4448efe78986c0f9ee204a968ea6e40ac013bdd0d602da184fe9ac46a99da264c1427ec97689e4bf47e08c313f6e1f06b609ea2387d7a02391b98b212dfaa5882d53f41434a90d1464a54c134b4434a4cf966d65ed2495ac32c2588747f73a48e6dc8d42df69b5fa2e2f7a0c48d99b53db85c4853c6e82956bedb2be1e0b130e87b88dd5e69c6aa8d39ce4ff31ce45f0dbcf3b1dfeeac54467d4ed05e8b3d93aff44b2148f1ed95b00d8fb12b0969ed5a908eb65af09d56e94dbc1fd6d2bf96fdc37608dfbe073b74fb1ec23028fc3c2968317ed9a16ea64391d67ccaf02b92f02b8d1e656b8d6cd3a88a7374c1604344414746404960403bbc8c88908e603057cfb0924802dfef3e297e0d06c3f961497fe13bf14fe95a9bdbec496eb35f1bcd4a22a7dd25b3a2cc87888e789801400d0b09276eaf04132768bb94a235d00b58220164f4f8767c3b703e497787a4bb43eaee8eba3ba3eece87eeaee85a98cb55ed385a1066c3a9245af2952c79cbb6667bd8df92bc360108b8d2dfcd4d4400182f0424f1b7cce56cbe1090040134747f619db3a451d938310d6fb25b92fe320ac35718fafc2d8bc297420595b204db30a5e4dc98a6cfc4c71efa4859e1d701c6c8b6fa01a8dc446bf3cd8cfa4d770335eb0045ddfde1a7bd4897e8251d673a41fca2387d8ea8427737bed6c34762b23df7ab6d92b886a711519c3e3f76943c3c4842664464a964ed11970b89e98f2072841b000d6a0030a8b401980c50f4358919c0a3a011258ce69af8d11e3986d168d25f0f92e33c1e24fc1a2e49cfb8e461e747ac86a79198dfc4625ee767f3630779cb351ef3e9b5596f53fc10e7479a8b98a6d037f3aca92f7e36359fe3e03a3c62ae34b3c7aecde02c7283cb1a927f387f499fb198cf6cc6b51449d7e11186e1f4fb249e3ddfed9562363533fb4f281667184eaf985e3bbb81337b7ba51a0fe7388a5ff39bbfd3317eca642f3913ffc80e8515c98bb25991643bd4d550e3d29cacbb6bb30ae085ce978383c387c1603a6ea418daf136334cfcf0c91b52b5b3ffb931d95b54c3b35613e7dfd7974e682d5dae0f5d2e243bab41428cd1ddbd452ce6c44b08a67e0951d4dd5dad5722216aba1b4f5b7d09d1f2854f4e5b7d851fbed18b22edc1f643e1e71962fc20581e4191f24131d23d095006ed9697b4f79fe0540238314e02d8210218e96e59f92409419af5a44a777f31ebb1bfd64714274881ba5b87663d09f1ac85ff21056979f484c8edca67fbfae093b72efa088867fc4c796edfc3ed956edf03bebd92acc41f5af056d47dabe9af498c098daf49cc0b262a4c4c2c2622cdc4b29161dbc21681a21d1f4747f7b3d21cdde64a6513cf37f20f7c12fb2ddf27ed259d66c707c634e014c9bfbee3e38846f187d886dfc1653fdf2bd9a54abd4e70e619d6f034127e2d2fe9197f28da1a27ff9546173aa2e4c35fe22973171680fbe7b11fed51ccbfd8ffade56cabcfa8dbd4f8637116f10f538fc52cf6b0bc4819cf99f5d88fb3c6627f6d3ef22f632be2b29669347ab365fa6ac1198dd97c466336373c4842ae2d5213abe19fd99fcdec88699d35f04770defcf466c7bff42215419c5ca351d1314833a6a2f812823f83bfe4f36f8dfba4554a9253c8bf8ca9e8311e2421311fc3f9b7dac713f44a67162797d62dcdb3ad4cb6c7bf7f42314ea5feb43b459f496c11a6b6a0bf76367ea62e2024a221ffb05b2c4ebff9f1cfbf58fe23ff9c56e9cb6cb00d75ac71ec1fc6e414bab95fcb4fb3d973498ad3c62bc516c9bf9fa03bc512a4f94ed245b19ce1a74d929c5f735524e1d32aad653b248ad3e747eb53c17933139045b24838fc1ffa710e814f1e7932a48ed0ddb16609a00ab57162bf6568873eb4a0eb46da9bebc3e6cd95ba6a781a71b9906a78d642eb83414adafc78d68a1ecf1a896faea5f599bd11b6b3fbf79fc0aaade09398298f0f563be472213d586db582ef17d65702a70cf6f4e61f3cbe1c9cafb406ce110787c4e1db8aef87efd37ba74ce73ed775449c9784b14fd05f3bfadfd954d8e1052ba92340bdc02f920e02169d40778bf371778026d12277795331d37d811917e7e34a67b7ee4e6a16132174774fb36a4874b70eb1190bde0faf755ab5d7e67e839f4ea3f205eb586855e8ee2f7471d25c1fcf173d4f7fd9add672d7c20d4b342ebabfee6ea8bb579dd0b595c22a1def97d726cd1196ebb485952ac1c4c9639b37cc3469049784749215099e547c8194b420252d52fea30fad10fe2f92ffe845d97c24d78a13df5fa215c9b5a238419cb11d6179c298e6782dbd9f6f7e259066cc44cbf52949c1bf1509137de53fba2f86751af9235c8ae42d8fc25acedee856f6d02a0d456be4331390517d10ffcf9d2ffad02ac556f4c9e483e0ab3e4964fb2172bc463efc4cb125ffc7366f58cb54094f7933eec1638947c8082e71b974964670092c047a5156da1f4abe94107d949c7e5fe95a1b6c36b3df7d253cb3e3cc66d6654c8d9f7e3158b531afd4f3e320b0260f66c2a743e88e74f7a9bb21f8697e314d21970bc92b7e313f769fb79c308ecacc2196ad7814f36c453cb37e2da64b34f419fa4fb104c509bacddedd5d4337876f36d0dd199cf561617d0cb361981fe39030188c1c4126b23d0c2455eaf8366de21ce79d24101e3c76507cc32e17fef263b719977e13bb4f9b750ac5fc2613ed50734098a1558a311d7d479212743ddc006f3081def14577cb9a75430e584d7c98c11457ea398fe72d674e667a3bab34e39d2f0707c78a185686e1ccf573a5b05f9a2fe2090bab95c16e609e210deb0461a26cdeaf62b9545d99e961230cdfbe878f92d3bfdb2b390e6e9b978ab00fa4a2ac24818ebc7c8e3cfc3cebe709560b1af9873db633cb65cc432abe6cd2250f1fdb6b73cef871b6b757f2cf3b78f89fadbdb05c92353c8dd4f0acc1327e1a0d6b781aa9f1dc0fe71b5102ad2873b940da53c3d3c8cc339cf98598906e998a335f2bf460ae3fde66d1d3eeacf9dc0feb1fd9fb40543ba4043ef8e411a6f8ebef90a3a3c3c336c3a089f1df307cd12c964299c9655299cc98c898306142c3a4bb8974af667874afbe944b77c39aa584a9f1fdee631efa34250249c8e8b163c797f3edc0a91627c13ebc8fef05522202324ac2829f4d4cabf436492297eb470be6d28ee0921a9eb51a9e464670494861f87f268a1364fab0f288b3cce0ccd882b0eede6916120c206162f3f55a598d678aed575f4860204db17d450a02297554a5bf5c5a0faff59bd027cdd46fee2f7dbdcd97f96dcea32536b1115c12d63f8af9e799ada9f76d8eff8b1c05c0088c5aa6b669442500cd329219bdf880457f311bbf0983627fcbec4bb3e6b59c95619098284e9f984fb7b9522f593ec4fac361fdaf593ed4747f455128a2d29f284ed0564cfd6f0cf64a33c768952acd5b38736856d111281045219638fcfcb4b0acb396fdd21c53a22792d162483c6ad6900b5c7ce187ff55662fe9987abdbf646942febdcc4591da6ea28c626c49ff5ac6a5bd4fab33b4c9a5bdd9ece1fc4babd46b1cd31ad277137169e9d25ff226abd4c307a7480a2511eacef3c818fde5faf8c8114772c252042bc516749ca98775d2d797a9877f6db5351b9eb51ea0d003953596301cc09209d160095550c10822123e4d1091aac107379e0061070eb0e00204201001ef1f3e748cb49488604c39f95ad9ab88a786271eabcd86a2b5208c698ef749d85856a6397e68eb8bb0db85d532f9218c698eb44ac17ac3d3e5fab184c77967510f586ff8956483131274aeb820081c42b22b9205019850031686c45e70831e3c91c010233c81c8164c5480084216b081db42c60928b0820cacb8386308a1171d9c618226f018e1293c18729d48220aa06f7143b6c008201f3e64e87c31661c9660e4871fd50af0162827371ea8f2041780c830849341045620c50494e8501350441a476222802263c2141448c58e1636208001265460925c3c34c02260c40f29a0200c1a703087a06406536840006e62c81bac9c2388c0832fbc21fc092a58faf101118610a5b100316c5ea02300426060e289272f01c0a007469e06be12e4b83889810b9536a25bb32b8def5761f733858df6ce60e0935807f84b4088d2dd61d087d6c7e5aabf94eb2ffd52486d1297eb4918acc5e5ba413c3742ae15a78fcbd5e272c130ac7e18d21a8c921346621c840e8d738c80f00205ac26422d20ac005105100f0022014010a0bb53dd02a2ec115c9299ecac061d7939f8018dd60f528eb4bb7e60b5bb96f0c05d51b86b89e8ae253dbaddb5e4d4ee520246b7bb9438e12e254cbadd856faf045352e3ae5692329260e1ae244dee4a72a3bb918ce1ae16928c4488bb82e81692182d242added2e24b1db5d2da4d55118edae23b15ba8e5c38e91133e7c3fff38abcb6d0e670fe8627ad8bce51c41263b7b1dcd5bbe3213d0bce58b1cc12523b864049720b9b605dd9d173a74601d3ababb2eba3b1674772be0a2bb53410a50d0dd6d7182ae9c0c0f38ba9b6b110d81288ca61b9cb1030e48605b0115babb93d121760942680012badbfb8134d2800d6ea4d184ee3e510145154536b2f082d361892088a62080025a2091041a6c2e4a8806babb3b02a4031190b001892874b7c70203c030e3014e684183ee4e45a00934b8305c5720d2dddd109ea004942a3c400840bafb2481980a02b51724fda0bbb98771c185428c12e0a0bbb930643a2b60a200273d681c978ba788245f16c9e5aac1f11442047a782d8869282b6b99d2eee74c6df96973a43c4d72743cc84e198490fbb44a673092468a748ecf446f9ea3ec6db729929886b01749fbe4ede29b75dcc87ea29f90c5b85b109bee6ed2ad21961d5f0e4eb6f6ce3732ef2c0222c76bc427881caf111f237186f38d6e3757a070be9150b6d6480b136851022db2e8ac74952ea4dd21c156049d297f497327ad55c2d2f2f92dc2d2f2e1d90f4d6c48620c2ff4747713b81b9fe6a8c0a57ad0ed0231a6d80cd4dd91e0e462030af49376c2832e9e04814358a4918612234b3d9676983ee695fa7457bb995262004b523974465daca99a8f56edb5f9876525ae8175de9ed41435bc194e4eb0a981613ad38da0bb13c14a55f4a1edee42d0dd5d0182eeee03162892a352b1d2249c15a02d3e26afee07d079731071da5cadcbc6f282355c92309b2b851561058bf54eff252b0bfad00ef5fc3cbd481ed6e972c9c6f2fa4421d2398be5728cfd920d6f6bf98c81ef776d9ef733bd0206373a3054d0ad1e30fa56e7eddaf1c7329c5694f9045b3d13e8eeee3cd0dd55b971cb7986b6ba5c1fdaa1ae03dd1d07b80b1479311da015841ccd3db784e62f7993715074312d22531a664577b7814e03dd5d06babb2aba3b2a3a29ba3b0c74efd0eaa20adddd5d14dddd05bafbd441d1dc900ed498d0841ea31b08dd3d806ee990c3a737eb7c3938b210e7e9cd0d0ac562b13c61eea595d9ec4d62381684dfc4bebfd6e7cbf3662c2b8f28393df6d7fa7c41fe75774f50e9f05a1bc6b3e895ed502dcfd267c7073e0a67cf8e8fc7b3279c3d2d2135ddad297a84acda5b41caf09b582b880aaee86e6ceb2d8330d1b1d504504c58d9d0a245e92e6a1f51ba158690eef0c59967911f6c8121af0ac07c2d170e43847848c78e021c0d26d0dd49a0bb8b0004ba9b922f519c3e5f2ec94afd471bfb2f122373f93ffb623fbb71a7e4ab9669f5c5ff19cc6618ec7f76bb62662a9de81e40854d8d928561fad960876203f31b5c2300287e53737b25d84d06837d27230c19e2b7044f49cc30ef24513760b07e6dda50415a3f7ebadb5654cbc7147a2c2bccc918f2d4f034d2f211fbc297b57cd4b47a90f1f59032bdd5c388cdad1e31ed473faf222d1edea4ce95ee6e3bd4d2c1a2a5536be9e4e86eed10a3fb0b6f22b543ad1d2468eda87db5bc6e33b561ea3b6a94b7c433cf1f2c729a3a07e96b129b79861d4b2b67d53f9dcdab7ba5ce76459f17ed37333b14e496f6c5fcff28e633eb327ffd7c2d635b29ed6db356da8a84909048eae7dfbd13741cfed2c41ed6194e2b3a9e365bcbf3fa28fe1ded748b637f83345f9cfee5d9f4fcb8466b20bd3f7e8d7c5aa574f4d924160bc2c33a31c5f5a543b54afc7b7ab30db9496c665f56ed049358d0f6b509d00f918fcb0513c5b7b95c2e1789836812847fedc6712ccdf12f515ab5163b7ef917564b718873a5137c12fff599754cfe2b7d4d624ad61ac191d95f0aafb5790df25c6798edd02bdba25b4ff899ca68cdde223c03cc63bbaf04ce8bd4133e4652cb21b58575ca60e1e392c7c8bc93e409e7d75e7607319c5f9b304b838530b0bea2108b750ee8ee9ae8ee98e8ee1ad0dd2dd1dd29d1dd4de9ee18d0dd2da0bb5340779744778744779780ee0e01dd9d94eeee00dddd11dd9d01babb28dd9d11dd5d11dd1d11dd5d53773744775780ee4e88ee0e4a774780eeee497777ebee98ba3b5b77b7d4dd95dd5deeee7077773fd0dd8513b2fb059daddd475e4843444004191119f19810922118d00eafa3232021d88fd14b491112d8929750cc6360f7920c37fa9ba1babb86efd78a01cf0fadd087766c2e490508103ecb7ba54ec9d783f5fb1ad237b34ed47ac10b1cde696b23bc10a59568245a2ff0408f5ec8d1722105fddd6437ff33af81f426e6ffb3d7873149a9488e33160bc9b194f9b447fec5489a2d1671e9416c70f0207e83e3e0353ec4499a61f9c5203624cd35a03841d810bfe51775e8a037199737168bf947d22cf31b1b92e61ac7b989778a74ace5c5c9d5de99c7c4395e5c2dadd49234fb9c59667f068bc5627ea9b53424cd972ed1f8e7921c2d188bbdfc7f0613e7ffec7f067b91a49987e7bf48d0cf60ff45ba4b2396d3b6af61b532a639b66e5c69ddf0c289eed68d5a7f4d6237765a37c6682bb351a565234a7f4d62b0b05a998d1cddb2b1fa5ead1a6374b76a60617b6a4cc1b61faa61e4266bd5407537926eb5aaf4971f13395e9a35fcb5dc6a4db149deaa753752cb47f74752a5232461b53db61fb2fd50187e18be6c3fd462d59025085a7f91d66c8e467fd4866ec978d1df4f90b764a6fc1199584b86d58ac1a2bbbb1513ebc636284d1a53d3dd2d9831accc6f600b060b1829dd8259d282c98169bd88f14285d7e9b98cddbdea84ba3ffc68f8e348fd16736cc1f0996811ff9ac4624184d4bf1b9e47fc9b37199d5e29a64b4c5f8e311cc331ffb0db847f73cbb496e368c19a9779f83714c441d5f68cf61631c2f488450aca7628db214b45a01b564eb456b1eed62ae76b126badba5baa19f4d72406838d20530dcf5a4b45437fd86791909111afaf5cff085cd24a8dd14a4d699c69b5add4777f21d314fa3c4f723689c58290d10be20f67338bf36190db54b23939393b6a6ccdc7bf0769b6353f4da47376cbd94d9c592c478afd26f3ff998736cfb090cf12a7555a94413ace7a0b419ce9b489d6cafc6f99e2cc205fade7e9f8ce7cf0f0e1e387ad6eb397e6782c95f8178b794c892836eb1467e9c4dab2b5e0cb7c665f14e788fd1fe7fe92bd73ac81f56548b169c5db24e793b618398562f76bd9e222219f255e696814ce1e99210499607b3f74b7d82c18112e17f84b2e5798318cc47f2d9806912b5ce90fcb4a1cb37d8df91536d0b21287f50ab52bf05818ecbb76e6b1984f1fa7e46bdef2356f9927a65971fa4dbeb686b3adb3da0d0d8d5f5ad2fe4e190e0e2a7add44315bd2c76b674145af6ac9125627f8cdc4c7993ec5f36dee3796a70f19e231ec250ea95d9a20a559f16556c459a4a50c07cfb1c478fa4d487b917e48fc9b496c51285a9ba536ec6fcb99debfb3fed2fd23c7b7fbb4280a8d79b0a05046f708851484000a54741856dbe38489eef6d12c282ce9ee9910090a3b50c8419b4fb8c01390e86e7c69385fe9ccff5a21ffbe743249ebb79cff925f437a02901392e004279cd04313a4d0041c74f787a7bfbfac5a70fecd3e08dd70135fb4f8869b28fe889b50a5fb7bda249b80cbd16dd648f8b88435814877771358dd1dfbe14307c6842ab16b63e59d20a6e194eefe981063c210a3a7cd22205aa54ba8d2dd6d2fe9af2530752fe1c815285c8141ffcdf897c4c7e3d7f24df89febbcb3a479beb3069647573e702514ade359bb5758dd1ffeb2d2faf90c2fbe4a27ed0c26fa7b7a060f33d0e8fe6895de6ec6f925d1ce718299da4a9a1103da2c7ada249fd023970bc90cd20c9e56f2ddc41876b970acb4b79bf894f6220eebbb4d4df8a5dffe469c20ad79a5b50c5622ffea044b5066837d66b138336e129b11d5679a42b1202a9d39a6362bfa4d14e778edecd27f42b1b5347f3915c9910679dab47992205dba34db74b7b965727c9c6a6b4d5831fdf9258a6f89cb5924ca447ae7b5e4f848a11d7ada2cb2432fca2639df28dba12023b8a6e680f8ace8eea5bf6466fa5a29ed653c3c42866e6965351afd0abb85f32fd38735e869b328accfc3f34fe891df08d1e181c4e61ae4373b2539bf96f114ff093d22e71bf927f4e869b3880c23748741d5fa7c5f7ee9e47e9e854fa34da4ef562715adbd4ffacb702c9671a593361de3aff7976e8943af77be682de9a1ade577a61cceb7f6fe138a614b968431cd11061b329231c5f78b789084e4c7b0ff9b652e57fea319d17db23cbab68892b53d78463423ba7d0f99e987c29f79fecc88b0ac3cba5f6d44b7ef0193f8fe675bfd66562ff2af0c3d7c7cedbd6fc5fae368c1bf3758a473e6d77a068372497e886f757ab53889f67d2d16cb41f869ae33e6406002021220d4d0dd84c90d0b082d63eca0bb09130fa907c1e4ebc4319c88ef3b1c77cba0bb959a953484a6e46bdef2f5cd5bce6b317ef0ab0715bdfcc34f61d98a384feb373771def2157ab6a277774a3cbe0689781e3912ba7d0fa1f5c97f14528c4770090e9f56c3d348586dcf0d9c6f047422e464fcf923a2f8b2b31a94ad78749f8ae417c94c3f34f3fca996fee027c76b043f2547704999b13532f3fc09e71bf1994b2fbe91ed87747820093fa4f7fe121139824bc2a070be9107e9d2fd23a53bc7fb792a21c76b0449487ff0cffddbfd250b62faf35fe4767f898aff63b343b7293efe25a05c7fa4fd512dc22f92f42259a430637aa48174493be86003dd9dad88290e1f48074d3530cf11d3d195033460ef7656a9cfa6104a33a6eeae354b0c2764a6a7dd5d8dd6ddcdbabbefee42b0bb9375779315ebee6054babb0134f7447797cdc2419506eb63fcb0e6fedad90d0b0b0b4b4b9d3db77c69e87f89bcce4cafcd5f366e10e5c3b54c6df7c7035988e0022d16e4e85c49b0d80a5e20460fba2648685afdbfa1486b60cc52b3c260023fedfe9265427ec64a4e5a24aec8173a25a7c794402bf6c01c7f36fbedce25fc655bfd5ac7efa50892e4cbeb8f56c8e542b2d94a8318be26b1fc58358330eccb3896198c8d7984dc64451fae36d379cbd74d111765145f6bc325ae5f1fdb1ae8b7b43ff887cb3cef24c9e9a1bda4df70c39db6e9dfe31f0e2dbd4f5a5bf3d8cf60d5d6c019d60f6d8d7d7973cc6d5e52e2df83f54b278f7d767741e8f8ccce6e5a3793eeb6e996d22c19f4c0f7fb1a384125d08a38555841ab9456690cb6e82f065fed8f16c7a0e8035f31a8a1bf5ad2c2177d6c3f04832dfabbd67560d0d41f0c725e16e40563c83cdb50ddf6822adfccf305313bf4821a1780d18de94dac442ea0d29fdbfcb5638ddfbebaa0d62e705b3d07a86aa8d4d0cad5a19c05195368664400080000b311002040281a8ec7e351c1704a7b641e14800471b0668e58194ac42488610831630c0042040006400000301b0601b0b474da5bf7ace97a393b2257e592880058f14be396fc924ac108c3402f8bfe42a25a2df0377d4e1bd5f69e7e924116dfad3419d1cc872a9f513a5e11024092988af1f3f3487e019352ce2467eab06b3b952846512ca5d33e54988214ab128680d695c310406ebac91bd34d3506d736280600d7784744f89a7b6a2e1c0a0c3fe008b8bf4ac9353467b4efb447385f269f9231fb0bfb1b26aa17d62df28308bd187bd9fe8096d38eec77ccb4d53c1abaa8ff996de9e4696885852aa66db3abc2c8ccb43e4f91bd4ff864b69def21e174e07ac2fd7c807e130c1ef6dacd7187d4f75622498bfcb678dc1a641fa038a2dd7dc240e25ca43f130c6eb612fb94082cc098271f6f5611c355d0997b56fbc1e5513b1215184dc912e6d39729d09af9f013ce08ef26d9163f9e218d422c70bd2c8b39ecdb9c99f54e3a5402002ff7a9749f6590c81ccd8f2cc0b879f08765c6a23386e830108e6d226afa09122685a2f5ffc9c0894aaa53816915b57332441b76b51134e0d75be6362d3f40b2031b017336392969df5e3172acd1377f2a19fc4a186aa38253ab80538a36dcbeac1370cafd64538a3755bf4786a0b32ff488127236050f2331a70ed90127e26c179d80e2cd5e72dc883cf5343894059c05b8e903eae4ef0e5b19fd88986d512580b8d777d0146874755102d609536bc7bba793bb029052001332db72bf7f91f2682ec4e099e86c7d03fa50c48f8bd6af61b26f8847fabfa76c4e78a04d436ab261d7b409246df0b82dc9f5d8122cc8b7def50136935490215340fb43640f8a7f7ad1551d9113326ac2a126795f2405989f7df834bf51b12a487f92f9a01bb65ab37d2dbcce2342fd66f5dd5e75b4efd28c5b353abe9c85041202703101ad1d6f516100703737dfd702c6caaed3d58028cdbe9a8701e50ec0bafc2ce5730845ff53f83e0bbd327769b941fa61f9c5eaacc0a68ac04cd30001e3a3869c5b21d225e4056bf261fbda4e31f36cd831ba6e663bfa12836a118ea81f0ddc19dbe5fb66d668877441b3021a54489713cfc1a49a0116e8656237254251de88a650f8647ee82b95597e799f84df54226dc1c4f6b3bac61071bb19d555d87400197733cea657664e846fba2e0c0b7011339543e2c7fc262b831f404935f7fabefd547692afd3cc59c74fbae2a50d9b1661bc0707e73995b9664baf2ce64ae66420d19c2cd840f21d708d69dc685beb357d1a543e050675a6c9f64ffc5aac5ca4dc16270d7544af004fb3f13f3ac4274f5414e8917c30a544bce8385632ffd2cbc5120455745a70c2e7ba63faf2716d00e9acd47cf83ab83bd48babf5a85269147d10629f756c97357363a9fec24ba5c5e8fd0efd0848564a779267ae2c1a15edda38093ffd82a5617f9257307ffef070181962c620c9997becac0e6274449196fbceb6fb5e368cdfbf3bea8d9a530de468be9e19625d08d005b2a07bb2e93c41ce335d8eb617e87aa1cf1ebc0c7cdd35ccbb2867c546a17f836e8448d149c46119921ebed2cf43fff538d3cfe3faac25cdc66f3a60932c2b274852ff1b65fc7f00c807781d74d2f8a3e99dafec8e81ab38524abd29c12532bc1512b18f5d7f5cf1311d4f4f712bef1df10530bf3af720198a36e40759ebf2a31e99802ba0a72d73dec97c36a20fcf9e20ca6753cb31ee1d30ec1032f61721e657acc658895cb022df6b966b5d1bae1db0b12879fbbe26afaad88361710e435816488ce8824c168afa11dc14480bc70dad8d75440e5aafb80136249a14d21cbd5ab3329ed4a91172a817599272ac00447592e5e43d297a3e73b8205c41c622c12b1ce988bf7f35cba16b396574eecb35a455d65fca2f1fa6f3780a8a60dbc177e7f45488905ceeac2d37094cb032b473b2486cf185548ee05bcf8b43cf2a54dd47ca3c55439eaf73c3301983e1bb9e8d11e7e96920e3862c64045adb2da6f85f9d64ca5e5d36cd4fb76b3f9c51ecc8fac836949d93d6c7335f79850635642edc9acbe5dccd3d064cda86f7ca6e7c79263b62de6874c917493b20eb62c92ecb3a3d4a33bc2ec683beff45028e86910c4f4a64c965bcfd20d7070a77c547d6e82e99391bc94f21e91e62f6f7c0a9dfab332eb248250bf5f7270df41c71fbbe8bd76010331a9e8bcc6e2c67ee67407fec804ca6aa9d27e7fb1b4b66fb25b944d87fba009429b6e50e608169a01879aaa981540221b666ba3f38afdf9cc4ab158ead4cdf01273382dcdc6f552d0e3d83f225feb8159bf26350ceb90978b9821a4d3afc1c5e4c10acff4807b87f43e79f6434fdaad73ecf5106b16583f21c1895b479ff5a642228ed4dac476e9cff1a9d3ba994ff65b0366be6a0e5b531e0e5c8d83193a9c106bdcf0c895d877bfbcce376dd8ef0bb5cc556400b625903d497bd947ebf621621e8e80310d4605118e9b8fccbc8fd3e31809135ebf8f9c6797d69e1fbeac9efc1c0197bb5efdbaec73e6ef4db51e6f3112286fbf6443f583e3b8a1b0aca771c932533003c2d91f09a8e465e68467c6d98ae497714eac504fdca063b1b385a3b3ad26f8cd3c19b0bc38260e0969adc0e7daf3b48ad1f6bb68dfaf30ddc48f0ffe5e2a90ad6a78e89ca55aba0cb53cb6eeaebb559515a8433bfc2eb3fe5b95b154e4067607596557274ca818c33e1fa9bb2c74c125818adf3e98b3cb8bfec1942a7920925a6815e94ba188fa5693799a4af8d11c84cc3883e084dca00aa68b235d10fab43af83c957b5e56fc6da51c4d928cbf868ae8c31bc0322654253f21cc064f4e144e4d9692ae55fc773e39fb3cbf76c29416039da94212b37f7de08e2552b309139a21644650fe1f3aca69afeeb34159f2f32f2234ca55469f33602845b2a270d31459f64b352656b8360753e2f358732b729b32b2d58fb6e410ec8216f67213a4c274dfc2c480cb7253e6d474c1d3564686a3aa10838cf0b2301e9e960ae57c415e6e9d815905446a2a12b06660f037a3d53de1095f2a62742e4a9fb037a900fa769f0b089cd16211a413b8c0689e2aa9d1c0e406bb9161c640f611c672866825b6f0fa533d07919564cf23f1f0a31c3719fb8bd0b2cbca7386e4f0878cb51b82eaa4e088353b1f59b2afe7a9a717c452a83b798e3191ea5e3c2f618cf1082bf66575a7d40147f2754a1a182b01a0670c3aca407765661867a9d21bfe42075075df028d916210e80bb68da32a1ba3b58ace97fc6802d4ec5966c51838d1a0c383b21b1474330675a72872eb24ff21c082cc6398f24136ebae22cda1f495b762b947f2dc35a3b66e25119333753cf71c91a13e9b170c522d519d64872dfc29b8dd1ed1d9e8e5b8f47d872e221332bbfbb47b3f4c15e30efd94ad5ca68a42506c8bf9de382d494dc40be794d17ec3d3d7938a3a0e4af7421de26bdacdda2fb41456e3d69cfaa3f3a6ad849ae049c2186b333a1f162c0a254a97e5d11a7d5674a6326a85484f11e4be1df7a6aae18a5fef57a6da1f5a423f93ae0f277e8ac2cef7d2d934a45fd8a1214e44da9d9e281799eeae1fc546550bc42e6a8961ce1ce93d3939236bafc5ab15ff22e64758800b5e5420df201945c724937f01867fe12b8752e734acefb4f47b6c30661e67141b556bf7f9914243ed07f25311f9e6f57f13db3b60182c266a83a9842453bd7a3e912b5d182138cf8ca00641e46f11699d01b5daf76df5899ac2b937bd1e9511c368b1a2e242336b5e988f3c737f3d60507079d28ee011cc57b5d4c034d2fda06ead06c7b51e1f9e20f28dd48b43f096d5f64573128a3861998469f27ab70c681ff0febc9921eed0ddd1a91ac26670f3838666ea720519dd6e37580f5ca5fc6a8dd5e9e3672f6457e074b661717eb5c4d6f57126b87a01a315d81a0d07570ed72ce8885bd609df22e11e41093d69af34518d8952bfb0a9a8abfda1d112d134cddc1467967a8f63e31685f92ebc6dd07a1cfeb91f82c16cb57e9decadf0f242050cb42f7362846b9b5d24d36c20efec1c339da776c237aef9712eaef7b40a65134a67e3d3c69c1ce380f59fc2abccc75f9aa4255d059ef80504fa36d7a58f3cbbba0066be2a461cb80a9a2d9ac20f771d5e909a87ec2bfe542e7737b0295aa2fb00370f2d20d8b3668292804ef670c03cabf8fa06dcdb518919ff89796b8f7fe66c38e37fb0bf4752af707041e0f53edd7084800526f5f8c92d6d33181f783c34e4384f16bbaeb69ea6b358349c617a965a730d346ed1dcad5b49c0ed54109928a3419d8c36863e6ec52d553d00320154bf297ef742cb1551e81ae6b6815f177796c3b297e8931355e78e2ad8488882941fa2629f69dc344822594e8d2dcb443468eb4723a668e22e31380fa02a620694e5d49655a121944a18c8ff8ee5f6f82f310e304e1156746aaf6566e978c10466889ea6c0c5c9f9f5332463138d75ae0cc56e462ae7e010c92d86d4bb5e947b9fda992b36b6b229e9fdc1478207366262121c709ffe41652f5c24b4e7562715e47bde0972a77822a511c633afe34d7996e66586831adae7f734cf7cea00dc3d62441b10e519460db14df28dd515bf0173aeb2d53b67bc8295135aca149bc53db6d44d46cd771941264d895511c43790d854f525723494107a35135d7a6e438dafcdab54888967652ada411f362c2b10fcc9dd55c28dca03defda839cc565bfc7c23fb56743cfb5b939f2f289d1438f357965b11134b5160b403996bb6c6fe0dcd26788568025b471bf27742646e887a916c3b5c77352b3b9f8c1426d54458e9908ef26f32773e16ab5ff6832ce9cdb8abb0130ba8260c0a7bca25e1a8d12bf1be9c61cd7f6131c91dde4d229c1df080f8333863ecd4fd453f3be0a34919405488ae74d51d20ee301a15883704eb819fdc8c955570506c1525bb2bedf3944e523e241c174823f284d35346aec73081586ee503b16f0e7c925cb4e5ed1e302a0dfff76d9b9331f63f4ba77016471a0568246f72200810d5b0bc231eede8e6b989b52b2748c1b4749055b98926b05d180b3ac977f163fdcda0220e1af1c336dfa986155ce873321e06f885d9bc8806d6add82ce3e796e1305e6ef0cb3ccb64d09dabf64c5710ba67298067a912ba77ab83dc6a626d5376c06ae338eea61a8d8925ceaa97c2ecef9810bcd2722563d0e64d181f9565904ad39e233201ca3c8b4ad6837a1256c1e0569552ce99cda050615dcb80ccf7fe7ef0f8968c2bfc29340f26ecf058d9e7ee76345073509b1d7cb3e431fcebafd38f642011508290eff34494cac23a4b650fce45edc62bb50bb0f36014bf52c29001dca8407bbf397c4133d377b3e4b9e48f0bd3149451a1c7d98f16533f97b05ff71fd5a5f319ce651a458afc9679d1ed4829f50380e9f3e8e5000d17ccfe83426fa32c0b29c1c4297612ea8ebd835d80ca384d82e5e81b17c8b8f9c9b98cd07621cce6b240b28e3f05db6612b80c44548f4727b805d808b0b9da2925000aec4afb01197fdbb71fd09b29f26ff16cb24f04633a2abda5404392f79880003065e7ba27ca3c559c403dbfeacf90775475b856d31618dfb85821100700691fdd8f10631c50face8a4939010297b86c34cd2ec7370bb682314c22a7ea34797d2f12a2bd59a2986a207c0ae3c048b63d95bea90b85290d8be02e18bf8e2b5b64e52ecdaf5730a1e3c0fff09bf2da35c5460dad3f3b53f82836959f3839aec3550e2110e3bc31d1a0d22524af882c2362093b32098c560eba48e97cb5a61ba120d4d685a7fe5186c8e7c8cd7012d52c7bda79b658f472d252b07fc380872b1f990c151944bfc284710eaf948563a9a06f8a89cc78cbe3b0e81bb785d294c98a4cc7746dfa801024ce503c939bfe2eaeed2be1afcf2e571ffed1a7f41f099738eada1dbd56857cad7dfa0a8c733442edbf2d6aed4df0bf71781d0a6c18fc604e591732a03771f14ec7a81d0efc98565d7c204e7f3bb9b20cd32e85dddbe4eabb3c9cd5c8ac36182cfef3817bf13b1135ef2547b71c037e7c1b3f876e8ef7cdbac1f05cc0f5f5857b56d40d65ed6fcc48083f028d6518478ee170efb487a810404edbd7175bd28927547f446d0b79c3159f1ccf4d048c01c94ee617972d0a101ba43f2bc4cedc057a45de685995db63e46825904dc047179202efab92f5c5f239a2942be58855c78979749946f3439d1645f95a721e155ae4b01dc89fd268ff9ea1b0656d75210e64a6abe825572bebcea84e25e6caff95cb25304f039bcc64e452faa8437de3f24f6a4a05228180c41b786898dd021707eb611304c4aff8927ed82e26827e1115c83356cc8b6472596078a8b66f3b54d09a4efe779f09024979da996fbaf83d658b6f696ae16c091d9573831e3a9ada9b0dde701e6666d336c04c0adbb2f518ea43b5ef73606b86179da8b7fc8dcbc2a31349ce4d3a78001aaf4fad227ac4578e315048b85feab60b48660a22cab548d65cbae13bd8d389a0ca73bee445460f09dcfba43eca9dbc69293e19062b84c13604487e9e04b8b6c1205daeba0d7297c8b275b44cf7644e4abec031e02faf5de4dc51abf504f803f5daf7389ffdab2c848b47805feb87f3bf019fe9ede8054068dea2f3763371feaf559ca912b96814ecd592a7b65621ee3b663712cf8545176366a4de49ba4dcbb3759dfce5b726db1ba3ba185b074889b8cb0ce85352cce1b5c23720d8c85f347e433b3e6385260cc67df48b65860574dd77a9ee14ef5c6d2d4fb7ab3223c542cc3f686a649b5134eb172a88ae2a819c6727fb3669eebfc8e7584918d77204aba2cd977a8ba3d0b12225d4eed3b6221545b59e783b07dd83c302bdcfa028eebc96f7d19acb1b726086df71d8e89c5dec68131e6ce856b64f1b17f787f99bc10a45c0e0011b0ee6570c7eb02a5e5d12425d4bcf06faee368e2c1829fddd145cef546b0c60ec420439dc76cf9fb0bdf12cf2c4f9629d3a341c7d4bef34c5086973845fa3db7592b37b2d385194104a424a2375e7e005ba9a7652303dd1feeb875bb2dfb08fe98329c8de93c6a62711520f6e512aa90379834fea8164fc23b85900c55a765fe4785ef7e921efecfe94b9b50ed2aa7a9805fc77b3206ae510a9a095459957a638fe133433b3bf5d01c9a25a308879e19da0b92b3f724a85e4936e3c6f303191396b534395364f74568c9a998647c2121ffc705403317b31564fc88d1f973f45427d47180202c461a21f11f15b6c22c3708acddad725ace747bf7692500b7931e09652a2273eb954f5a0bbda41a0a237f977d008907a6f68a8471782b9349d846237a9b9fe710ceed1e560e1089588a88ee975d7f485146eae4a0246996f486ea849e8049b5179817d3cebac5de00c4fc53b32cbb67cee5ae8b287031f20019bdda8cdd2540673a0e39e6417d506517f4eb2b3d3e1c5e1076a4477d1753d5c035394b639e4dc5f412082cf233ad53cdaef2cf2c14dfb2b361942896272fbeaaa77424a1f91531496ed57911c26d68816f59c5f6cc00d38b6d1f93c38067ad209ef5de26d3d728d04fda580d6c2c5511e7b80c28eb4b0d96d9458c69c17781d03e6db27c3a74879a8ca8a24253221b7b6d716dd59506618dc025d93e3a4d5c780623203fa78671b9893fad2f59d583e531a61e43bd5e73817ee30e7254e8b87b78633aaf039b348bfcaea272b9e85a848ce22fd1333ded9b30952eec941c70176c5700947c4938a1bb2203f582724f9077333ab3cbb8078cc24acc016cf636fc508ce4eef7df19d56a8fb37b278bd67b28c635187066c21cf06eef3b9c2c0770ba2758221e81ec62d41327fb7f0c2f93f5e1136b1f7d8a461130ba2e43b109f3b6aae6dcd932103df9051a76f59fad7d11bf3e4ef3e50ff97301bf32788019993d1ec6c7d8dd450c6e8d7679fe5117ae72129820796e1ff77af02ab48436f4daa4cc59b1fd21422231398a196b22e8e1d46c91bd121844b0ad895c5742ca0f5c0caf4760f54495229c17b4c2e694de5fef70247caf7e6090bd7a48bab17887f4202d7ea98fda088cda64d61c21b999a101e7754c1427d246f2c447fbd7427630487516cd3d3a0e651ece865ae9551b580a1017d0f8ade080988bf974d2b5418098dcadf2b7b5e8bc77405b91a91ee12662432ead852e4ceb2b1418aa303762755c95b21884c039ecb4967074ff29c8043bc19dfc559afa93b0be6b3bb902d50a5f6dd4b8b5c96f0aa27f8d58d94e1aaeeb779c8823eed198b638d35c88ca3bd5f46267a601e540975514b4adce7bd96329193294b837904d903586becbcd953042a78ff2a5d8cdae4256c8e34eb64f6d2a217ac353215e6baccd45d161d396ba6ff2fec882421bcd75c2f45bfc1a66e678c183b74592d4e59ecb874f28957df2ba9931a0417106c94c8041355a6c76b83a1bbb41425584d66eb52a344a35fd28d40baf27eecc8d2a16eff159001e018321eb78539c300d051ce6f66d8c415a1027320457d3297789303ca5e949b007f6a2b576e2f8633343ad18351ebda2fff43b3922c3dbdd850a42f01b1f000764e3feb3b93f16cf1f7b86afb977fac925aa793993dbbb417513767853e2edf7346f9bdec5d78c7223e85b36e9ac5eaeff48ff9c7b30fe57a19ae04f8a779861223ebad761a00cfd60cb036fb8cb98e973c1ccd48af440d1b7bb1016cefa64459257406af8fc61b605390760aa3fba99018352e3a31ec3d8b071819e21cbe49e83af8ea9ce6ddc6ea480fe2ca7ac6b2bdf86376b7f84f849ab2bfb1f1129c544bccd2d6938dcad6139ea82626b4cc619c5f734022071ab04f4e25ff8d8a98fb7791662f455b58b4170ab371bad05aeaf98cb32df810e8ec757f92b864727b32fbdacece3b476ec944300630241cd20b6f2cd675be490967cf034338e7c1da88881c3b43979213f068c06c3c41f9069f202486f3b4f195efe8fdf9a4255314dc517ed5840908647feb42063ba82b995cc83f8a43ddb8fabd6c602ed931f42a956681673f979532c81be29ece049730e723f5af636740740be89f16160efa1b378ea5ca1dc0667b9e29c1513c58dc356f833bbf73d2f3396f37bb876c2db31be5767287dca4d857d0d356065a4224fd8f60a35a4cc2d9b7ff947b14080ed1660b716806cdf0a3019666623b1e1661b3ff5a767e6e0620ab013f8ae6910c29c983137c0f832d7f8173e88b12c48187e445063742ca8db5b458e0fd3eb09035025615a55b5d319e441a646423ceac4633988adc63362ced60a93d3e96c3b43016b5090fd661cbd2792932b317fcf18e4175889972fd4ac219d2630c7b6b2ed036c24369e7af49de3863f32c39afe1a10f66745ea6014d2f3d2f64c4be50636213105b48fc68951c66de8b076137d33a9bb30c7c8540cbc05ede8b8f1c6100db7f92162eece3e39c45277672393062225aece4c1e49e310575b5d8206201024520258f89d2eb993710cf423e701a07828b5c94ee10d89f2f46133dddb32a160bf67858827c8a4b5bb6e95c5d4c1fdac0af279035152dc4f6d4350cf9857786ed4634313a5231b4991d542998c9052232390de75227cab49702f920b4a89e7236957cca786e3b2f9bb0adce7c42b373b1357dfab35e49d319add6e0675c958c3d0eec2fc0d311d75d27bcc12fa0401dd6f17718da1e48d2a9c9e73b161411bb17a538fed43e6c2bae01343c35553010ead4db63877627b2f2cea9cc2d434de55b661ba02557b4d5b8b3ccf550bb5cd413b087ecae011531e35ed670998e118b55fdccee6f64c0ca8cb6ef12c65d788ff70210ca8db7c1be036b4296bf0c6afb36d623e9b88a79631be8356207ee0a286ea50eff52b9a1688cd95ad698e58f023665494324b05e6588be44a521b0509215d0545e0d72ff62209b7c6ad78937137549917e66394dcc8a062fd213f0957e39c6b14bd5337b30a661f6696c0fbacb200c4450a56be60958ffcf97b173dfb46b8577150e313f2ed39cab4d4c4e55e9635b7f9ab9f6ed7d9351110709a462fa669236c50d36e5a992904aa03d85bfb490a8cb3a29a3af9b29f4eabc5eaa68e44dc2930e5f7a0c923ddd9bae5a11ca683089271e947f2f1e7bca0ac44b8efbac0d70eb771abe7eec94cda0499331424d7feeb2adfb89aef798ed3de66c5f6599537fc5c20df81a75e39f04c2b745811e3a7aa97b81212ce563d99ae362b46d079de13ba6a73bd7c03eab841473236215a991f219495c4b6ad804c9662240ae9f0f288f4ce9c25c2cd30c01ee665d47f6b0f83e35d3988c15455aaa59dd0b91863345d5c1432da1385ffda484df55fde3f9ce2bc4686af89058b4200e3ed4ad38bcfe7c340d2138d1925226b591fed1b3468a236d4464281c4f51265d0fe880bf06d7cad6179977b63e67119e8fc3086a0370de05e74501f2de1f8b9a79789290d662e75dc4e09fb8e337c599a11560f9aec0b138f902b7ee8bae04a55fd2f4ac7e78054dbe12eb44a174d4b250e67e77237604721983f9b2c7ce04406b74f86044d57fa1104ae03f18b8ac7972169b23ed20c7b6efa81f891bc132febebb8be5a16d690b68671c276b2ce9a62b2b6c6cc8b06af74936a47a137d477a650ba28fa8a00d59f7e46988239c80f0d6296842bd44a2a796c8e55b1a51152a2aee08555ba345f4f0c7aaa3e18130a37b624173253d8b83fb6b570e08c28f3b0f1e3803d23151b0e7611495ca7a77d45697e6ef1257c7f3576ab4a013e04a43bcf44ccb07b9c6660d9ad2c2dfb4e3de415b0548a871e35f3b317b85a77f08a44feae1a5354a5396a0e077f85013f258d6e02abe78e74af5d8253b4101c7c19d20df71a262b44ff33c43a5b44f195d0aece6d28c5cf3c2dc8f58ddb60a600417c0aed112b0cd3eac04df47068f9f65f4868e619e9331c07335c323d4e93a6f3a3187174089fac67227866da441e7cca1380dc3ac4be6cce8896b588c97a36e078f88eabada0915f44b7b546a0579cb627c8065609b1c0390358997668ba8ddded2d6ce83703b2ed993aa24b26368a9f66974f124d68cf334d63258d1a3c923617e36b719fcc46537cc87b1158d2172f44c9ac30667cfc84ed09df0db19e26c81d94af621601794449445680edebf78ddb62b965c6eedc74f1463043aa0403bbc0c12a820ac2130d7b54b114873b53ae7daee38d0d56d404acd83fe496498473ce68f56d27f7a55f04a69e3a68a43bbe00a8e304a71c750a7196b0a8c24b9ce9ca3c513b4dc37e1166b5114a15369db348d9aa8c39feac981608b27086d26e266992ae08e1de470e770b541b3f8a9f2c0486830dcf3941faf7f20ac2f254b2f1e824dab39ae433c676e4758001c991a668c0daab4e24959cb62a5afff00ea8d207ce13e30248c14f3af508640d5ff3604145b80612b3aa4348defb3e292710d4ccb0d07f0b647bfb1190fa4d0689ecdcda1e0ccb03ce90a497b2b5724907eb05e1cafa82988756492d0c6f04102548f16b403fd4e2e68552be9ed084017cd1c463bcce601144803147b97ec05af169331e0016dc1cb62b813c735ac2c8224b9410deb3a5bf5ccf00a71ee235a9da81c7023fde982556f3bac1334c34341bed019f443ee97098bd6b9cf1b8c10434f96a677bb25f38df84b59e3aac6b3dd18cbae7ad784682bb3e91364e0f0e647d65c2857a603ddc97beb5b52243a37e3a663e1141ef4653ecac157b9bfa917431d80e28ba0696138ca2c7cf62d00130c3c88dc8ad38497d0ddd139c18689385dbbac34bfb52de6abd07d29ad7cee09fe5acdfbdc5b4e26cf29bc065c5d300d0165a3b0fb98686871767dd4756214e35f17ddc1ef7933f8dc4259e1a61d875fc04aafc0f9c675e61334ef825063f3c64da870e2df46dac79d56862cdda3dfb2c32285e6f8d8dd36fdc67cc6e6d244060d622a60717693d32a00dc4149e2a11401616eca3f762017e3e80dde172af5e1ac0fefc1d38f2b273adf27322a5e36e539240f2a57053fc29d6ad5bd5055dc44a2afc879eac501ce09a4da5be1d3735033b0c3e21235f10f63dacc2f2bdc872ef18b5539fdf9fa832d364d36d4eb2c294ca90139b8a07fa8f2964c84211ef376df592b9ffacf73321c87c3103d47ba36b81be6628e5670a90b081ba79c5b50eed57bb98fb08cc5404f9851dc039e72985cf35e8d7eafc06cb625540f5b833b14309a8b5b4158f3b8b7e213a3510e4c2ad81b881f3264e339eb18f46a1a31aade0b68fec08697c56823b08741f01b5631dee42e9e71db88e230fbee3b6c4b6016d792168a7c383b433bd1b7aa39dec63831b26f314ca0e3ad79371ebf5d12291b5f9ad6efebc49ffada7cdfe36329187e35d05e1d7296e736729d540f5813a7617165804eac2d010f2f49fe0044688f4d4400ee4df23e27bcfe919921ba78bcbb1f1b89c5e02aca45ea4c12746ffb7e3d1165f72091261f28c3e93bdd9dc8abcea8ef7a9781fd829f53634c4d0bfe7e1ae860fe69b4ecf7d2a2e65bb923cc7fda2ef7a378ee58e72c0c8ad3e4d95dcbd2facb12094e73f1b782f84092dc2e45aab58ddff2f9eb01ba0bd8e4d96f7c9d664d1566a8ac65161d03d19ba9aaa282636fb3a8aa19ed37145fffb11484bd54faa7721291182f7988005829ac2330cc4b0d2ceb2f410f9cf89f156094dd559c72be7ad1d27e2c375902b87e4143a787c52f2eee340be94441afe95fc2a6c9fcdc6ede989fa58859441bec60ee5aa5925b3451a400d8760b660736a6db5a8c847fbdfee763f3cf6ae729b4a978bd45ce2f9b541fd2d1a9efad3456e35dd017b86f3d6918b2359704e5a9999a88f7ea3209f20e8d0f13042d61d068da49f836985b17055b59b59c75b11ca59468b9524c73207eb3a1158b2db4fe339b85faabafe70936bffb4963667bfad77c89221ba1b56c2f3520791c22728a54d75023748dc62d1cf744e2378f373ea2516b8999fa52bc9fe63951ae8f2a6237d878b64c09d254a643e2910a0c0eda761f0683d6d1ebf18c300cdf2c60bfcbac5300c30b877f0001a971c32cf79c3e81308cf57f0dfbb665c7e1bf3a3f62030b4d03b355c369ce0f5a287771d4e888b83d4a3edb476bdc4d84f78b6a5bb54d8a6476d8c83d5487f725e5e1559577ba1e20dec15eec601222c8024b564f42fd86401565cdfe303c84f5d0625a51bd9ffc7563ec9ef2cc1ed5d66ab338049cec93433b83c2d5c70463ae5b54fb51ec8d83135c2d11234daf64721335da751407079ed7107c87bbbeb5e739d6e79bf3b5432a52a3a7ec51b01346c81aff041dd7d1dc72d3f891ab841b7e5079f7f9af236b66c9ddf2794694635a21cacb474420f72e3a8d1ecbedd1fe3e9ed7d46e8fae50748d3dc2dfbbf3fc0dd93c5007936a6450e54fbe5658bb479c7011fbf615bf9453550f565411406af59b7d3d98b199c2c210741f238056b85d900551cbe1fce736b0338492c5f12d9e271572ac89aca311f235fe5c32c1a845f94a8cd93976970cd59f9a3575b99072cc4ea45b0ac893d963a9aed964389807a7ca2e13e212c84686e46fb0e99b9ad4b69d9d0b895af80860b5cdbc27ca15a94010858e9ea0d13c928d1dd91c4b1a735054d5f4bb62e939d68d6322eb8a18b7f739c1caafc8d5bbd22997faa0ab635e21764c24f6375648ce45bbfa3a7e9fb95b745b535b9d5534e7d674188d8d13c223852345f633aef9fa318dd508386a8cc50c9a05a34935c17ee6bb34ddd82bc4b147202b593cf2f2e1e08a5b02e5145cc3af554427cef5736774afcd9e5e2eec714c34c9e1abe5f337a8a0638d4407191507cc340628d6d8b920814f1fdc4413667c4bdb6cd100318b545f7ebde24457277bc97f647b473089b57764bb52d9de6e254e5cee906dcea1bd40b17fea6234e374be0a793f3cc98095db502fbee33cc42f3437a4fb440ba9717e4a5b76f4097a4b02263bf382a33d9a7f976ef40a3169f8e09b1c58c9dd537f0fcc13e312bb6a14555c41b741e07fd90d666d0e0c7e23b2b816c30ecd328c84d0762ad37a01b2c42761566c1e927518a361c50788c1b5945c7d8fb44c71c189df912ff244edbbe3be3e644b3f3b3aa4150db1581414afa829105bf3a5e0406ab08685f38529613d6525f2b0dfa38601c4909fc2e13479b2419f5bb885892da98720b5008d379612c836837ab8b51ce795ed7328de2dda6c71e0c3a33bbc2219748676fbb427e427c32bd2e947d4cf7d9b7a07ee8175dad73e83a76166fe9c9a94b521594347d9bb3e8239bff0d829a0fc6e27365a842fcfa5d38cd78fdf698b9dc8041d0d96eea51d92368c9fffc5d0d541d663d93a70e3fafe1c24c20ea538bdcbf9a51ea8973d18a87920bde4a8c9a66029907419972e4b8a0e1bd7636d7df45fc45bdf3ff59e5903fa4bcadd97cfb81cd68d4221e714ad422c52ca4edc0964ee1cd64ece352889702ecb7008327602c8a626b8ae1305128b541d040884cc71b40343453b0bc931d0c0e1e469fd19ff76f22377b6ea7b0e969c5fb94fa7b1826faeee78c65f40481ca2ca4e040adf85073b447d53be248d88049c4250c32cde643c81a962fc7052246fab5d192014ee4974e5639748fab8e976bb1d9502cd624878066f170af51fe31e4468aaf863f72777cea83db381f78fa6678dc888751b793ca24071118784c23da53ba6785b9f803b3ad238b59e0b0967e9dbfee0c6e3eb62017490415efa6afe0c621ba483c5f6398053d09b9691a1bf4d50e2f9f12cb8aa0addb01206263b3c042849170a3adec867b70072c4c4025897e9f67d9d39c91da5a1863df2a022d55df6bf26a20353d012d63ab8edc4a5d8ab4c1a22b4e0e97e022601f9641d4c5d858b0f3cdd14b72f6a5ef57d2fd3d03d45fc87a4f4a932ac6b2724f1835032313d5422185062dca52ea64d2cab1301f6d1050c6e88de1ed8fe22f163966156eaa7075c69750cda0102caf1f5b1570838fb4fba8e08737e22b7cc82f161e6c289fdf1c5b0f42122b09989b6a960d50e68102c14f4e207295fb4161ec60073e386f8475c3e8ccc87a5765585291cd1d3934bdad266c1a2792a9bfa52925f99916c4e040b6f123485b3b43f8d411188b0f09d885b48d1ed45fcb4d09f94f4d47b30032ce8c7500bfcc70b929725cedc15f1ac3f7a1825be0e9dd5877d0293ece63dca4517729708a8190163b4901db85bf9464192fd7947aabca00fc10930b658b5f1865b4486c69cdcca042eb20a54f5d1644a73e84e258072212fe36ab0dc9ef6bbda8cb590fc6c13a86c47f2cfedbf4b4ef9593be54df02d20f34b31eb381c19c3334b9a9e95d3ac0e195b3e4416028f4dfb04ccf90affcdf0a4b9934ff9d7f6280f5da8651655671cbc823db869ea79b07e1295899cad3818d8157ff5d53a62c517c3ed927a4be8b1135c5a7987fa2cf6e57b283ef010edbc98346e17f98c45c95642e3c5133f6fdff3f81d01bc9bd76366ee2026917aeaa3436c575dc0d08838a1d9315f84d1b8e3cd249f84dd79e2d41bedec0d3ba0c3013929ba9f17e099d88cfa6e421910cb811fba1c4d4642e8761233a34f72dc7502f428fed96b9ec6920820cdfb3b7e9c7760882f2afa515453c1e23f165bea2db0f5c67ebf63e25042b4e8a09d4ea3884403c8704bad6c601224b2628f773b9c6f5377854452acc24ad60a44e5cf7480b37fb9690675b7d7aafaf5c0adaad4e02da72e856aa988af70e088a1190af65382c43dab77f3e14ab3575b9bf7867697952b64093083005a3a1c7deb3846d8057a4786ab4ca831f274fe1a290537e24d70cbe43fc3ab27f84b5fa20338d016e7a9580535751b1a5454dd3dd3934680f9c5148af792315262de77cd0a7806d57f6c49773b81fc7a0dc6841143e04f46608d56e932469211c393e426abaabc58c253a702dbad4262f6d59a87d9dfa189b85e6a65ddeed0e4d3de5e277a6abe2752242e357d952d6a2f6f478d4cb372c48a9cda7a2139d8057a065bca95162213cec18df04cfbd4f8034f3ce5880b85f8d01961a25ac0361f405ee335fad3b32476e00331549bd8be6ec5c0e19d7f537e4e6867e8e2b30b3c969ccf414d22325c299daef6f79fcfef50196aee810a674543fee9ce67b48892dd5fc104c96452143f9d04960e208f7a96a748dd5410137de4cde164e531bc2c0b514ce45e2dd9ae91a2d56ad0a0c5ef6d465f5a1f870714ff0b12ac8aea811385f82b8dfef39d38d1544457c7008091de3c1017972d6a79e25f8ce9132450b89d61345cc5d790a4ac9318244bd4c5851bb9f5e2f04921212120b9c25bf8287c6e8944544864fb25726a60b3a4efeaa542f2af25c599e90c6f07823c01b874d8aeb1c47101f3c468301de80cc76fae9ed5344b2bdf49bf6cd1233a92fed7560eb897b68812c680b86265bbcfb5b0a12ed7079ea25dcd9eb4fb48905733ce07af1dbbb80666586a31889acfd160425ad6822ba955edaa727c203fb63ab3ad17a7a6e903a94080a8d0cc73fdceee51373298ebc33bf78b288adf1b760e67a6f8f91e8340692b8dd6552e2b406989eaa7e33673a58bb57a8df46bfbd1fa53724397b2e11625235b461ff8f667670ef42b68753c565903d2f780b2892cf5f620b0c4f92f4ac425c9248abb219eeed5be2dec7356ef76e2ca7bb41fc68a514bfc618af02fdfce46ed9bc9951c4d3b21a6e58df62cb239639b488bf1bc75e3e92f5745078dbfd76c261eec4a09912b76d5324dbafe1cd265f1acc74b51bf4cfc3c30609ab869718c84b307554338a7472f9274edf08de5f71aa28575a37e7239d40f08269a89d6ea1812048b80e0de069b20e43750a2d337959b8b58861a266ad964a519b2073415342e72e5279d84f3d2b82e415c017ae2be418e3161f38429b037ed863fa8f1419f681beb3b9e201fa03a648e6510a07c30de7a669fe432a2db5ad482335fd9974a3d0ddb62dc978ca7c50fa0ebded751d2391da9760192b8540c981363fc42f3cc98b56f541c61b1ba0dbcf90d1d1440e58e90a1ccd650bb63f2e382b6babba7b056cc3a0749fc101c7a9b6d681a8407b60369bcd3560f5a7087dd1247bb842b104c105fecd0c5a53ff72d8728664f31ac2611d7830c89b15a3c6103331338e33890401a7fb580d62f465159a8f97a6d1c0af54b4f1cfd9a9d0ddf4f9b929d8427cc38c39fcbe5730ca35aa3dca7fad8668da3efbc898595cd917197272935cdde4550fc4b4c0261b08e8a583a152cf459b3ee9e1840f7b518af21ea2b00da0b949ea4d9bd2b22726aac636ba51da8fec25c0e4e3a94d26f977372c5d5792780a4d05c9e8d60156a9ee21a26cf48f2b1029a9f3e3883af3feef8f9c8a5732165873d5071f8bbf505641e547fe7edb007b26bd72fde75796f8fce215fb63358a75bd364d0bb77fafe26de0a492f3aaab08070966703bb3bf1ba8d0c9df6094c6cb1af0b176784f1997559052697bac7eac18a87e2b7ca617bdb491ee97ea71427a5923a1515b32b17d691262f8cdb6b6eb640d4d6bde5b689e1ced58205e615f535d32658ca9260bc83c9026980ee43e85044f68cf84ca1190a18f9189dd2ea2834cfcb2e4e872f5af36cdb70439327de0feabb5d966eeb8b83c6b72e1b28a445881fcd9c49d356c90756492cdfc149a47bafdbaadb5ee800d9309635f2c26bdb4eab2d8ee39446f7b6beef79bfca1e8dd4254c45d668654ac840972f129c6e12cb9f6994cf1740f0f1214df820bc9f8c072b70cde59b6cc2d48a3aee1094ad0473827ad42f64310b8b55e03063307218ca349db2fe208dc4b50f0085f90af8d6bd3cf464ae5b45ec44dd6512af3574fff304972723cfba29d3bc2700b11078209c11b8f546326faa44cadf9579bb5d5677c429b07b54015e81e3cbae8f0bfac301a21926287436100fd07708434fbf7356b0420851733c76b4442f45bc6b3549eeeacf6f0489eea07b78f79abc5bd56f7610d8432da1151b49cacf96cca9922182c0e9d07ac37a63ea9f972eaade825734284d1cb41dddfc8d6477ee3ecb6ce2834ad213612cfa35d88ba27f5c94d9a8a2e2ca2ddd43b801bb1a68289c16f486a50cd7a9da987a61a7b0770a43124351bbbe6715856ce70c80077aa9419a09420b65ee53c64590c73dfe84e6ab15e4a30276e66a6aec0f466805f4ec42635b839de94cb0c999325f2eae6268a216e4b1af68298d1ae9584b643ec8abc573fe1186be9f865f0336a3c0e0cdf7770e030c2ec12d55c24c9a345edbbcfc44fe9cabddcd3919688c6c0af96223174272107201d3dc81f516324d0b7aa10c5f40197096adc341d8f08e088077478d87e624e9daeb1d2d01e343c174fc02dbadd1499cff53db1c5bbd252e520e3d6c023e96e90425ffac5ce8e046982c141a836e5526e64304099565268fc049edee73e4fd8962e2556ef207fb531599a0d7a394206b19853b21140ed145bde994f6833dd3c060d3d0ac19e90d06f6e44ac8b55a5084d5c3a9eab90852e07ed3f3beca55a4be648dcb43ce1e1a4a50ae7ec735b3e78e25c71d11e661d32720a8ef10e82edfa8984e239aa855a02cccec27d68b718b003baf03d56dbbd414957394412ed943c92fd59b13cfcc6ce86790e4e386f9d2fdbd086f91dea141cec8c79f3bc2edce824c5f4908dda99c70bc34a0f6ed1ff0d59bf2d99ab490fc5970a33130e84a0d55bf369768f0f6b1ea920db53b2a48d46abb6a8d687cbd2ad1d76462ba55ff310ff2d196df5d3a5317942d7d4867a5d9076a7e35a811242671b1d454f8b8843a0d60d90f94e6f5f4208644eedb8b318c0e6a218feae622dfd440d1c3fa0863a443c39760d41c4996eeab7f589e9b1ff69bbc2f7e498fcc856dc8607a1b74365763cb1ec43d6055295a3b4e93e13cadc5fe7327875ba87fcd29d11c287bbf8ea0daf33458fa06335f6b00f87f863fb097df643678286bf2e5280d1444ff685b313801bcd742c28737fa8cb40ee0c330b3b51896d4b144ad36751f01346c452d4dc27e5c13a423c0b0369c464cb9ab55fb18699991c25ca1485338dda3c09f3c3ca9302e23731249cf21d6c795101d412722514c317bbf7a41059058cb407779b53b5abd24d7471add955d77ec941b8010946c2d5895bb19b63998fbaca077bd24a6c7fa45c3e374998647c25d2b6e2a79a3066487f8253e83e5552054a346d43a742287843cf0eb33ee260aff0449c6f34c1a6a0e587c356bb326cbe0532e41f4cc12231a37f394fa9dfb3dd22515c0e8766fd874d63ad09cc1389192990d538dff1a71e79475a000a5ebe65641db9ed4ab2dd72a3dbeca82e2caafc5bae7a466e09518ee48a9ade18824a0450cbcf1cf4bd34e96d26d6fb9e38bd23ef6643e4b57687c7993f3240040ec08381a30bb17d3fa81d1aa096d3d0b83ecf0a7c3d323f15287e15373e92a24bb1c84d42e3ed608c34cd0d2e441fe500b94b4796d9f9fb343f1c41ac6f1ce50027dba4bfd060ef9fd4d3dbdc87e2784357aae859cd3f92b939fa20698223ce46ac87104e8feb50efe2790b06d710461d27ac6975817b285d9ca5709fb4347ffcbd7384d85df68527f7a0aaacef6b1ab19c012a67800f98a4099b3cb7673fb3f55a8542d83d2529107b820575c3c1af32e20fa7722b68127d6a90e2d0a71a1af3292f1d360f7ab6932840c7c0f4ada91366eb2cc13c26d3cf38ab845c0b2d2616c1a54c2eb83c4dab929e58de0da31aff80aa6ea199fc83bd43b7e594f9393394e054e50f028e85be9be327636b9aa072d763cb56f1ca2113e96457385957d1925b52b8c6d40582821de8df69055ea6b656a289467f4850a9243f6e4d1a4b4ac95775b25dd0684ed38682cc43338f23a147ba542689678fd76015cc37a70a58ca4aeb49dd564cbb5ab4d87b1539585dcceba0dc436522e77899e821e46de83c87b7862e739e119cac9746c23b74ccf16c04b2f999362ea0694e6ed0bdb75ae2f23875454d83cbd20981aea2b0cd440a2644f720dd43e463e4127f2e3534e675d9376e659f4effb642e71a3707ac136cc0d40392cb45e5fe7aeebb422368c0dc0ee44d8653d054894257401aab9a9c3692b13d6b2a5907b595ff3289cf491157ea42a7caa9add4a0af10e2a9c049811f2b28b5aa0e780f7a6b31257f5987958a2fea881cd822c72879f3e777b7a41485b407588f0a07dafdac32976f77485f17cba4f64a6cd3cc6496e41500726b9edb00d6971fb132122e689be5736cda91f85e8623e630c4784405df4ceaa81083d07f8a0de10044cbbc01174aa96913ed7d2cb3fe391aa04fac609ded5bf8a4972cbc87564946c159c8d2ad7e59eb1627c16dec6bc64832f7c9ac30d7ef2e57c7e564591364e4a8374d43fc44c0e773a43190937d0710c02f954df10508920b1fca016af825d78174651420980a323261441d100f5da07bec07b4f1f08a2a96460635f49c86fd7466808871469aff3df10b966d8e9c79d431fc39242a04c83392249b6b8f4e3950e1d52aa8f52fe1ab449926f7d73a2a1152905a1bd5e5dfa2e258f48d85fc731199439c0bd6af16dee7db747e861f41534fe50cc0f18476cabe28aed7b9a2e508a55c87178220427ebe06e542677bc2ac9c230b42cd763d6c4079292b6a6beb009badbb7ad602477a1e1585fb8b6bc0c566713503ee989a0760fa4cdda5b546852c65be2a555fb12306fbddb6d2cb96438e1321b3bd4ab7e731df62dfe06c954d92be61407272bc21fb0fb39a0b55627915c0cdf80f8795efd0fde8c709981d5488990738b892ab47823ba997746ae34994c6516e9a5cac1968e9843de67e42b597a4cc304e256b8a16257015c4e352e075c64a58f60860dd16fbff636afa8c230f12ce8fb466618503101fdaaec55b176f50087a8b41dc460e0c6841356a90daacd874842373923cff5dba61fa42d219130956dac86d6e195db223268675ac86963b4c81428e2a2e29a94c15a12c49bf92972260b945d71690f5a84de0b45a7c7b3cbeafe550737ee732ad56dd9f941708298cd93326bc098f97aa08b370d2d7f41939387ce3dd16b59e9ad856d45954fe6ae26582f109666504fd2b346d0993df5e7613d1ab91333e44dd45305b2ffceb88b1e2e2be1e19914db85d777b84d4a344df990c0f6c2046f622b20adf95b942fad0d75638db9130da017841fcf702bf4b9d2710976854b7e60b3c3cfd02213e5e185b2305676c28e589ace20dabb6492dd27f706ed2fbb33f07b97b5341f3590db6bf8145f100404257a3387219a9b94ac052cf9c203f393519db8746f8aa93f6847a9a598ba19cd87c9238fd819697c98e274da1f85ba1d04d9ad9e05e7bc467e6c5d2ccdc3d9c5f68ea2220b3dd1e3cab9063e695d43c4c1ec73edc65c99e8c1c77009da0af27780610850ec3334a52169d5443b014cd94d92d341d136800ceb5bbe7965549da07b460eb3a606c9399c6d59230ab9f2282b65145dfabfeb06602fe5f201983ac94a7ec246a70b6accda752a7d46730df3f3ba3ac54367fe30da87bd198e48fa2385b541469d2ca158c11913ef582242479c4e7e5907d1f0b1922bfbcabe501c81da5b4af10734bf6c3308430678cdbfb133226d5db9e67a1db5d8a4b14f377679639ed6e1acc4e8321f953fbc79e7258537106c7aa6b74ae5035eff43c0fe9700f556ffb0b4a704592a43ffd42db0a241c085f4c82112b348b81548defeecbaeda1d11f89f4cbf61c729f5f8a7eb97c4a687e12915d1e010033fc2898907c7fbaf497cc5ec1c1c1d030427086828b4cd267b2b1f26f258e855599416e29558e5d4cae1b0c85ff42c5a9cdffba655027f5e2a7368b84e03444c2b29d8cd1eb7d20b2ca72546d3a038ff10c4245e5bfde5c7d0358489d513adb8d80106401d339ebe6ccb7bc72bf8d3015ea06a64248c4c83a394cfe7f94a78da087cb5421cdc46e36b5fc33a3742980ae766c81918105cf4cabcf704238376b75b31322e4461bdd79c2ed9ee81647e897f536994971a55f540084687b0c09b151b194ccdd87389d6426d2c95982445db9b65846da12a0424094ad2844ec7ca1b0dcf868c30e52a6b31ee239e6706afe4d1b1bdb60bde12053d8b298678ad481351f22833d1a08b25b0a1b58f97da9f0b83e3c1d18e45d58bdd123794606390cd4c7e5759025f4ced69bf1b113f5c3b1bed7f3c09a763aef8b56de15365b4d8e84fa30d32f7557b30cdb4cc2f5e59822168bcf126cfd70c0c041602f4c9f9c9e87f5a3d3fa8013d97269dfb4e7af54af3f1a4da2aa11d9c3e7cbf456290240243f57138a2aa32f756d2607d30e89f8077b57ab9c1b3f40e31e73c6670e2f3a91eb5b847e597b2b6b72b413566c76795518c950e772b1ef1efc5880b2d12d576fac783586f1dc9c7df040a1753dc7da32be62c508f9d5d5812719ebcba7fd8f49009dbdf655c4e80158c06535e1d1c6bbd9cb09549f9506ea7eaa6375e40fd7c04a3eff563eb06021751f0d7906fac1c2e6d6b24b9feb5e8bf2fc2553f97b3fb3b41b6cb1ee769c9178f504715b05352bd14ce3b4be7225cee0515d3d29bef202c9e5fc85597ef24544422b29cd7e0e029f6a1a031fd7e98a3a3b356717da1953b6cf493a3c399a472a4ab0b4edec74ad28451a42c94ed1d1a7e471fbd95827ec8692b20aa56ce28f235eb3b2285d38c469b0b0d6b2e400081dccbffb28cfa3da3429c96c4d2931483521521a75c39ee573c1122ad51579991a8ef9918fdbba3b81a7386ef22b0212b7df10f6d8d1cbd82cbeb3ab9aea9ec0d8a1e6f1e1528d479363d8d9ad330f7b5ab7330af41439825b0c51552a89022eae1a3635964d4045a968dc4db3957957aff6e623a6ef3f09498af8a848d58f34ff962d05e9764848f591c69b36f861f06c32d8791b42d74ea1a692052925d917b84d341e42c6c2ed2e6f4d41d9952f65e0f7594ebf88ae6271bcc131fb45e66f88cd6605b050a08173c9e5528bca22f8130605bc7802efa5e8db985ec4145083c690cbec3969d102e1af7d48ee9ee03db9d9b9341bec3300f4d35658c5f4272e687806ad05f16f0a961dc2a45e2c8323f852f3f361b8f55359735e8efd2024d5f11b124a97693cf8831f3ac94fac3c807b0784c95aa038aa492de23acbf655f3c6a06f636bfb4bb719de1b052887982b66f0980ca952e57ffc8958bca66ff5202a984f9a99df8e09c33ab820397293580a916e8d66cc020ee581402046ef784f60631b7d5fc080dec1f14aad9f832af56142a8c1ea628caf2ccb46bf13c0d5c3c7359476151bb414ea72bb8e9ba4c81c0bc554e6c8b51b373c32cc943fdadfc575982cf79eef7636cb3d80bdfd32c2999c5a9c0a27e99d40c81940a8f2ed044cd4996f22b8417362f7d83e1424da9cfc5b4885b23af9eec43cc234f1350f6c190eafd693885bf16512a891cecd3e7ffa773682753efa68771802522e8e73d3069e535a0d5992503cff8d438a7473f7146bb4ca65e6255664251c5a46cdcf166bbe28f15aa9d512c1a21d8cd89feba7f1cbaf5ac6630b6177e0eada7dcddaf540051095abc5ea8c0f4e724d9a6a4f084018f869b995a1874eadd52147dcd1e03bc94610e58b9e80190bbc9f53b19c4ad8cb41938a2e54251967f02065f54af09312824d82834bf92363d1e0bf8d288699bb877f8c130f77513fb71b93c9c96c773d9de36212a876a035337e8b9831a75fe11e6a837070e0c62625b22ff1be70906d146ebd837e29a4b0b81202d3811dd5163e10a63a15593f22bdf204ee68aefae06954c5b6e8f637910c87ba3712e321b63d3bee8374940fccf4304c7235ae9b5732100c2a38af4450e9c01c0ac6f78970adb0c962a0935e4e3cca037a9c28cdfbd090f862f81a5cb62cfa21d7361cd04fb9b1a8670864f5795494b03e6b2bb0c26e0011d5a63126b72a36d1c01d50ef34c230c45ded2be6d2848be2f0032d904e51d93fa562a4f198fd333c217551b9fec23dfea84ff8f948b2433dfaa457cbd6cbe6ee94f29ab6a7592185a20fa65efd19bb74c400ae5cc1874a816241d493fcebc38c999f1fc208e16ffe4fc225c126a795daf9e89e7c041d2ed440c3909fd30ebd37daada1e97ba5ba84a3c6f061c45c41ce652cf478df0596937663e69071eb3dc4778f86fdee47daab7adce092035a1d5c0f9cf14d828cad7fa9d01ec7856ebdc14bb06a0a8d7131d1e9a537023383b1d0da717defa7e76a978859f3287b16ebea3236496b10b1856d240018d33eeb319caaa394b888566f328eacc1ea6a4433670745a68bf2357d065bf33d8959ac1bea082a36d40ba91b511b22efb06a1d1aa5d8dd0e19418ad6b7b69f94d0be29f2b863ffd77897de35c76affc58d337e0c1ae58537b530ae883d8150d62bcf129407a726b55c51f1ab142f0bcc0c485847975682b25f1430ad668bf05b501670216f11e8e940d63db2d6bf02e127106239d27a34b1f2787937e9c882c977fe3c24ffd2c0584863095332bc4b92c958e66f80063a5f55a9da78c07369c2c91a0a45ae0af0829d33f20d39c12fc3a0317e06729f9d724aadde53d4473d6241c9d24b4bf90d79446f091045f9102f206e17b2841513e1fe6b3134d64c3e2d41c4f81332606960556cce83d3a65b230454812b362a6f7a836077aadde1a6fd99528c3c52fbf2e10714da507dbf2d82939e78d3f1abd33b0a1b8ff26b22dd4c00307be15de4bd2d35185f085f33667706f2020cd217b66a8655d5194d7efb738498eb62bd23ec84addd4aae229edc32d2306d132c868994421495382ba5e9e2b7865ed8e76682946f0a4e5f76ac08d3561940503f19acbb1ec1775a36f8d14c9ebb89c28298934ea6c0c6a6e5cb5e84e176c832d4f88dead066717693c1b57d322960516066c4374570dcc8d7d17ad449ce09a8535de73089cd08322a5965b2b09011cc4ef82abe837164680966453e8e0bb60d72a9682362ae6bea196ac1c72831427a20c388ca0f98741546ed80cbda622e0fb04e385fc09c415636e844401c8ff219594932b13f73447179dc574c80ec9dfd8f95efc403e168cb70bbf2956fa6ef8fd19fced43bd5a02a7afd8c4eb172ec861350868b0320a33b787193f64eff9593786126825677a2942ab88ccb9d23b119a003727e5d4ac41c8989e507da4bcc7638cd906278dfac562bc41bc86535ef4f8d2e3045a27dc4bf4c38ea6a2c0811b108591d93ec199f7104da1600c27c0d173900a14110bf3bc65330abe9a9050bbdd93a6d6413ce7e0e06f5ef8c448ecf9d6f5a60fbb6411292dec488c63440cf00c1b154684fb1bcc6684ccf4db298914768088bbb296ac1dc51aaaf3c344bf160792c7c89544cd59e12f2c3dd644bb197e73cf432ce362df75d5e2be10625f9b0043f3f6064e4604070ba224aeb7d16eeb61e3ea3fdf85e0796934a9cca8587abe85a46bf151b0806b0eaa8904f6415e85eeba61ebfbb94b39990c1fbb570c1a79c3b45ecaa4e552adb11807cf3029833c904c0c03754116d0dc7492808e4fe66e9faccd768c382c7ed1d4b84ff1730688e9f1d8512562fe862b0efeee3698aebfac54a33774beee49fab5e7969dd8704f7426592811a97fcfde510d7fe14e890f45180bc58af336a615a3b6476767b0fb01ea2909804e4ba89d6b60221818035ad3b94a52e7ef03d9c2daf2d48bd5b66f1868bf006f38d5a41984f73652b9f17ddeb162167ecfcc1e3e50b7c55903c21bc979b09bf194a9ae44dc71d03f48f1ab173963dd55a4943a4481fe1f4a89a248c11049435136f4098b0513641e64f6157871d47206466558a153642ef71cab12e404b2392d23d7076bd2d3698fc769c6d09b3ce390f3807f10f5b75fd6109e56393dfe76076e72b479349803b8cebd892f2c857f428459c366da19f017bebb4ae18d8e0b12a849f8c403f218fa192615b956b92c58fb6970114755b3ea8d78b14d262b9a33451755a95a6bfa4d38dbde9a6b64f67b8817567d777052b98fdcd2399f80119410530c26c9c2ee48b82b6d436802d3652c9801ae9ee5cf5ab4abad366fb60e9b0212974f4ffc49c0ff12a81c1f6fe48798c8cc3d09c0dd9dfdbec7b78b261bc0b837cf5a4fe9be2ca9113e4809c741c60486bfe3a041d5918c279d61dff1c6006a447bcf14eb6aa2ad1548b0e8c8ed54ab7669bcff67f6f3494eb944e047ddf5f047003e106fc8326beb7cac86328c47e51e18107ebfb87139ec1150df818aa2f288e6493ee4a00f448d53c5a9ed91e934f3aaf9c9e4f74b2a7e5722777d95730d7d4de6c5ba4c56d33f45966d2dc6197ae60ed35ec88fb657b8fe508ec111057c09339a5b979619bdf0658e4d2af4021b686bacd87550495aa7b36dbc9cff55ac151051bde20d4919425d8427afd0eeb18b18941330481a76c267cee151f0392f4d54960d9ba7b14bfbe673dfd4c7939a746589b1fcbe806d873e59cbb53bf0c681ba66fc7f71436eac35e9d94f4967992bd2e02123cd2de59212ca151e40ed78bdc6090144891b13c44bc6ed1a3bc9ccc4941a1a8fd7e7af67388cc5d3729d2431711d9d3818bb4ded2e22c326689df1423f93c660f92ea632214900da830726b03d806ea3f89f139915d402843b1635a6e699cb0263ec5f38e610726b5f888cac5237d6d098711c4fa7bf90c0aa86d0385382a46422d1fb99c5c9460403ef11b369375375511e7a154f43ee8c0d7260c9cdab81d3b5550cfdec413787ac37d5796778e92913e534bd211c652d97c5bca01d87f04dd8762daa25015901a20f0c2e04a8207df8adc5f34ff7193b809e2c12ea3904922e8a8d4f631443c33b5bf4ff72b65b96ac44bc190a4d6aa5c9427ae9e5a528ed1f463ae753361bd06da523a35971cf1b76e84b9b6be1d15ede47ffec79871a6013cdfb7212351e493fb0fafd50cfef949c58058e6e1b8e3f0292ed1e82d2a2b57d0db4e9ccd86c14631f8d15aa0bcd3cf0cc46570e8ac631e04479f09a43d4160d4695690df09160bd32175bbb05344bc53ab3cdc63201f87b464371eb5c1fad84067dc511d28881bdc93bb499eb34129911d7b8c0045047a7207ded57b6e1501ada01a21bb363c630194e60ed097421b0bbfb4cfcb014e60fdaf7bb2b36603528064558dd38220a506c9c60a29a2874c92a716c64a0647a0df923d9bee9956487f1086ff1667ced8ab44734f58a6768c6dc07355b95180238cb2373e1560029959355cb7669efc88d9ef3d595699502e6b234e81c69be60e8cd9b0383eed0acb2b14cf96dd178698edb9d6d47935159c898496fc46738937f773a0a20e0102662157324f4d94bc528152da7d41309f343ef532978fff2fd1e71ddd134f13a606c33edc02ace3b975327b9de97d7231d59108a731d0a09f6df0fc20a65ab2d00bf17e53ec019310eaeea1f45b90502b4bf6139118f52ea4cb690d9c688562733941e78a0dec85ec07174646d790926ceeb0d76899aef4d27b1f754b304b1accb45603af4fe61622dbd53e2331a62a3e6397b7bd97a0ed4ea875c3cbaed1f6a15820dff6b48402c26fa094afbd867c8be8a39e4f71472d557f90b514fc2c49fc0e974bf60071f45dbd6b6f17199ac2ef73b6189b699da16b6097bc8501f6231e34d2745ef8c7e1a78508e59bde348e9cd9c86bf333cc39c2b16e94fd6b3361a3c859b9126242522a5b02534dc497efa96c1f6dd9f296f83714792258cd79cb4e8782b7c709519420b60c01fbbbd3c6fa9f48c355a5faf2f8e72150cdd5d0b7d197e13bd341ddd0ecde67fa4f39067378c5c4372eda7b44d6e7945b22beac583c247c4f7e9d49e4171bd5dfb2bd2c32f1f939f8cab72803cab2194aedb62788b59a8bb246709adfa9eb452aa5ace2dd50b75c31668b0a47d2f609903f4aaa1364b1ac72110d4d6623a4a0faeb6997533359462c35c61509f9ac56289beddad88606714afad42fff9c3cfd6048109fdfd71963dca474623a441c41bb73be309b4f5089652b752293279170072e6beba772ebc3b41b60bbeeac9e319cfbbff97d9477f2f383e6321d5c8c8478e5d28c3406361bac51b89d26d5133af279f6bf3aa90035318b0b95540deec718d532e68a32604155af194493bb856b5cbf24088cc75b0046435b3709808455df44f1e284dd90b9dc9d5fee137ff5962e1de2cbf34af6389ecb052f43498adb307b6e1e8504d41a7e1751ed374cc244269928076f8a0407ef9177b9caf466b54844e45f50b47156e588a5ba6e65c8ef5fa235ea75be6466271834c97429b1b35acf72f45985e3342f97fad77e922ba0720e3fac2eb4f81d6f904480444eb57e9ec0cd6afb5c597f286758385d01b30cbc2d61f17e9c28a806174215f72ed5307006fe6e23ff8dfcbfeb5b995877f09f1a53b3c63087b45a92a5583b4f323ad5dd9a1d950cd43219a843306e6620cbb8e90a17f580df86b25f913acffad1bb0f761b0e1ef14f8dfd8602f9c346ddf5e708e31623cf9b9671bdce0863ea543f3d970a8b1e01f9ba6dabf2bcfac538b6d6ee50d7c4a460106ba19d7dbcf6b1f73231afab1674d0fe11e42c16a35690ace08812bec7f34f19f9844afd0563ef64efc3e11d63fdfec0842cf5bd2f4496af9005dd512466f1bba30c300a2a37b54fb3003dc1afb6ffe63d77ebd41a2518e5d62f73ed9764e3996967c5765fec54b6feeb99483ba1639b65043e2106a243f5e25084bb383df619c79c3f1a7119c48603ff25167c81d3f900da8bb726dd5c3f09363d31b7022f0bf7333920dcf40726dfdd6ddcfd1ab2cd4eb7e53f3e5f0aef9337cf7ee1d62f9fa27b64a68a1f0eb00a3a6d976866d66400d3deddbc8f7661ad093b29dd7bf81f3e0cb5c6eaaf3193ccd9226fb0e8d82ed891b61af4118d6a32d8f4b68f892a25b8ce2a2cd7de5547ae1104a6051babf0bbaddd4794d47e6da6b7b0eb6f3dfc49ede99106f92bfac04636d9e7b57f9f384341aa188679e1b10c7caeccc4154f1a8c027c5e96452128f892fe73eae5d0cbcd4f9667bc46bb15e07defa27cded318d62f6913ac89f7f30357f1a405e6e228082b3e660daab1ad277a76d338a842ac2143f67b85ca0a37ae7b2e3bc091e9ee7d3340fd9d3138b9eec3b9ecef3f89d3afc92716cc164aa9b119d016a451de282870e6a091db60a60b5cfb52092d312120c0630a54f50a4af66aa4b8fc998a2c6d45330fb9b304f228e73702dc2d62ce1066c9be9380bc7e3ac6bd2c60492a9e712969352328514220f3e2ce1c12decf3a12631c4409f0aa3446081c0553eeca6a148ab06b6bd00208396427c568e2c1eee6216cb07c8e6702cf0a40e3432626dccc7179541971e1148e2b54ec52b649e50c7e6ae53a887ae8eb1eed26e0f65222512f89d8770b4a27f7f6c152b51684368bd504d2e67a32ee62ee41e8c7b9343db5f12c3e2d4f12067b71d57898b66ae8499a106c5c75a3017e4d6cb0239b15abfd6fc23ac224950cec96a99825cdf9d508b6da84fcd41c6ad390afb432c9ba837ea6aa7a9c1098ba294f34e46e7e2c78b676e0ccd17a5a2bdd7f56c110e5df8a2f92da42a5ca4106f2a17b45a86672b2bbff4bb310146942089ea42c328d8d971623bed97e7dde09ce63e6e0e24822316db2a6c17f855e9adbb1f7c9711d2e232372b56d2479c5fbbcd351cb2d2e401a7634d4b116205422cef53321bf8a5757e79bb5af0188ac5ba6f49c5a2a6402098395f92a0bd09e106eb81aed01d70bcb4af379949410e034c7679e1a7179b242d242ad0814120f1d448e39b449c9d32113c383e1c3de6fa65af4a0dddae42dc40461b2d6122a1993d51d2e6f6dd45cdfa0ab0331a695db940aa7474b390ba2faf8f79bc0c28ba38bc7fb4d621569158535878fcb315212aebdc7a39a0b8bb6e5c2cf7f79098e8f7291335edb9954ec903d26cfbfb7bc0b189feab2eb11bef82f31c47d97e308f86ded86259d9a058f4d7ce88254a3b378910ae787c8695cc4cc54bc2f9d9d2d6067d34dec6bb83629c47353809c3630442d09bdf6ea9360160061bf216d0e67525353f9559291794df671d435d06f04861846e20b478d064506e73aee0dea5675385a1036e765e55d0dcd942d011b16c19b71f6747fdecc3c98371601b0593cd6107ee193f11d125a1e6059725cf8d61c015b423fbe5e34a5ae8a259f9c2e4acfa68fb5364c361d051d85bba3cde576b23ab98394cb5c43cd2e8f3ff8c8f85f7dc82e4224a670be6abb578aad09f5c69a6301a97c6c7526df3e721a54034f532483400c0db0b79b795b12f20c54897002d527b3166079ce94daf5d4cbd5d8451d85e8ec6a33b2ad9783aaf6c8ce092685291fe1769a1a37bb82089225a8127b4cd35e47be9cd60f4e38489162d767d14b8b3a28fbf3bc320b9741c3229acad0c8354d0a1eec67684b940deee8fa1c783c15203f51463021cbeedabf7266673deb2306ddbd7014f2e861959c73819365411017defcfb4ca4f4b153d8e81bebaeaa31c163b441609a67168d5a58a56561276c612029da94aa74ad7ad4f9bc074d8515a380cb47a6d83147d15800772474490bc1bf95fa12a995c69682dbe8e3f86775a477e993f512547d6f5466ece70a627602912bde9a0098e346874381cc50a868bfe74e8100ac6efa3941261c4d996ccd2a0d73e7ef68bd34fa4738bca01891d1c0a6bd6e01aa4de052215b7269457c3894b8c1031b4052019b1ef895645a0fd22fd042308d8414f6074a7ad2bad28468817ffa897550879366bdc45bfbd2f68cda81cd528dd458d9cf642b9abdbc5ae95e5a511cd9253f26218cca2ab787ce17d42e6e85a1f77e61b89b2799ce72687aedb48e5349ba3336b12f1343c30b6ca8596b356109448f774859464059b58c31c412a826526dc097c3df9bb02fa3e3cc913af1aff640b2df16419c284b206b07f8004ee96cf918255499a9c35cb914713000bb439c3a3136b251bdd16797633612c9a958184a08d248cee04322e55d20832430dab6f1c430f5329db894dfc87f7a589ece67d3099ace6085617a2e2987841af8e915615a9c8825ddd0f40295470460a22669850e54834c946928836f4661238dbcb5295022274b55cb32853119e2625c45ba560c092beed48c5c8e84cced0d77370144c41e853846ef30489c490abda2ccf14c06a24d632f03b278a50a9f8a29c75b0bb399e8945f58e0d3eb6fe96479b389181781f46978e2625db315a0a0b8b22b4c0df987adcd10fcadb26f40594ccc065e2318cfa9d70868c96f4c503d74a52f9920cf23e69e3e8a65adba788dc11456c16a5d623348f676541a3830252c349cc400210615243f1a98afa447b6e2de33b01e9683f92c060b40e0b7c408668e2871831b5e2a54df1c0c3ef52cff5080f8300be0fba464dd479911a886bae8607f725181a8f3c15cdd116b38ee75649a2b95a3b3c225d4182aea400b932c01c1456b6037754ea4a5fcf1da2962850dcd0b8b86a020084029981ed228886bab009b8da48dbbe9b9724a53e9856e1a54763d29b13d944e89744ee4dc72753526623f3de6517b0f207969ce44ddf251a660bd0e93bfad2d5d68200d256471dcbc25b27d4715bb64a07f73b44c99e05674a9ee5fbec24d0ec462c12fdc2f58fc247d53e45ba35c8157cd33a220d2ac60a1b53371714d9720a46b4f6b0d290267430873f9c24f4a3f85137b9a45c0a83efe375ee9be929e4ba9ad93427d3ede54c17a5d6f455f638964b9f8cd0c6e634f9e9ab1c13198a4c2c5569ca8d0b29422d9a4c0b79d6f6b39ea01e7283cc0828fef0f5e4c6e83767d5267a637bb30120370e47725028c5f8b8711c18612b39af7483299aa81ca58da38e19d65739f89084d6d48906518bff769e620ab3dcf30fca6bc048a9f893c6828b539c87f378c2746d8d4651e6720e62fed215cb8dd1b340a321191b835b22ad8c91fb27367ee4d91b62066091dc382ebe03c54655c2cf5b20bc310edee61752dd8ce548d04829401fd5281ad1c0911baf3976b7940a230a1dcc1d3776bf3f690242fb9137a3e45897a76021565e6843c91660e7eb1542ddcbb8316ac62f0a46e326e14e6b39b40aa462e747213449e2c605ab3ea4ce56aa73a92f31e71f9ea270549047dc183d0f4d1f9754eba54b726e9639b2976ff665a4399b1166d1bd2a59d95d2c71633496c4ca2baec95b579ce812d9b7b7132b9b26b63b9a236533ae39a87f01f0b77249968ccb8d0be54c51b70b04a0d5de958b5e2d4e8716e92430a2439cc7b9e826af43cca3d0cabe65e791af091bbff3c628d4ff58b1c8f1e6be242b22a3ea31ade736ecb46c5bc690d906dd9d370e6c037f4410aeca8c1ccdf7686e26d7cc4509621080e42572e5420b37f3cb4bae5cd6650b75e56029037b0dc5aa3f9bac9e92e6cc6aa0a8c3c355b2523bf61c1adfb6339f0128452cd6faac21b86a5599451b76adda0e122258557ed32457f62c01491ee3199015672ae1f1c66b4b02ca70e38dc3ecc2c8955c39eac68de5227240a311dce662b50dfb74d75cc285a473d1f4ab19ce7c7b6cd57befd1ef12eb443500179fe0bc7154d7bcf1320e01bbc98ceae22deeedaa337c1e8f2b6f6cece326955ac08fba7254e33ff36395073558b0a89bf8d5bc25616d4d60713d0d4653a70ab2efac94321dee9d849280314e1e12c0b984c1c3b56352b868eccc01ab01b142d36c95ee80739126ffa7ee03e1e1b08ec099123dce1c48af120968f0158c6024ac69552b13b8210da4d79def74a22ed8e1412d0d84a232666a20de98c15e67ed1c17800e2f1b6a98a8399391949995f14f7b82c1d1756603010344fb9466a2767d408b3a00655f7e7fe6bca9376727459844831b29e12c9a990fe30e22d70466fef4416bf3425876e704edb50b71b76a584daab2cc15c9580df60833dc149470638dd9e41e9dc3fa850b1748d916ddc4d34d2d510735bb430967474cede4ab04d0e005dbaba9602994e63a9475904f898b979be382efabd0c40bf667c9d597e26e1f2a5abe48d9ac1ace0b30b956fcc1889e4ed4e2f282e4e2873873ba62f6fb6c95b97c2a9b5d34dfaa46a96dcae0c83745e1949c86749b30ca4703e46cbe95d410bd26ec16120b45b74f6b8d8f6bfa8a592795fb9eaf4b95e3a3ca585862041df0a6c7510e9b2ed2168f1995839754e78fcb237ca24d9511595cb1dd51152dbc5cbf73e72718252649a165470f7a154eb33a622401bf63f9295e04eb979a199d696b9345dafd9d83ca5c913ad09c7a3af6e50bdb7a56f8a2aa2408d99e2a1c05b089cbd7a3234034a635c0578a88c555e6aaecd87da3911133227deb7e0cce5717a809573079b6cb0ff97ebd00012a6be4e5da17ca05e111776cf1318154e96676e4ad4ca35f7eacb9ca2ae3cb0c8451cd65b9205a965576c0d21611f23ace53852e43c34e2e75cf15153e516413f484ac7ee0d94ccb910b9cf4cf78bd59a2f32d3c781efecc106f1f1c407ff2776a6bd7b2ca76b4b70817ed13b333f2c8027adb0eb010a5fb0a0b180e2ef06e46f9f8a788f2418ca14d4de88de4c679656e28cd3cf2dce3f9db2af525d742be27d1445ee5ab641bbe28b8f2a3c41d7e1146c8839a09e2855d920f7cea8cc71106bed64f2c228f8490ee6a7ad5e428b952594de9c39869138e2e457f10d522f05540d1f6980ba1bfaa9dd44514c2ddce966486520c392ac82d750049ba65f78ffc93f80f141c9922287497caa92d9ad17a20922cc74cfd07ff1df901dd66caa07474354d054fbdec1fd1218e975a0697025919b0be9f691287772d079786acc220c60adbc127f488f4992d4f4a03562b97c3aedd39a06a2c9f63b9502aba9a06a7ef5ef28b6888c66b2d8b4b87ac809eb46f570fa88de1672c034a8b5a6108ac24fe81ee9505f12c199288785fb419370f22edf9c20bf869b1949bd388d74cd19f8382b7d66f24873a57944fa7555e4173d109fd2bf93806785c6e5f0adf4327e8c785f59c9a61551688032def42ce4be340348ac6b4516e441a8b46da286e2c8d4663db28372e34aa9f74e12c29828b742ea84a05c1b3dd467524fce905a84ea5019447ba4103ed3ec56e26e08c7c416ad9711d47dcfbc7f81348498fbf33148c44bec042d06fb1328bf0cc89473e287a9ec74195a494f22083881516999f5303498d0f5dc9b774298f39960061dffc90e832d043d4631a1b36d328aff0d80856c155aefc0c5d51653c1c0c7cde94a5f908154c46afaf135407798bb2a28e64ad2f97d35e587f05c770a07936250986dbfe349a6e9f9b465bf2fd66f138d4adef1ff346554789f9ed8ffdd79a61d9e214e08eb542fa93b0a61dc8325370b8a9cab98be7ac7dad7bcb16e82e92e69516c081679c6f365ef7d7a75ad8d7b28354f4528af219406d1b62c4c00f9687f29d151ecc3bafc6e67a7c0caae85aea0a7aa47a8c1e721535c088d1a70e17504b909e40354bd7a4a43676c1a358beb35b55b1f71c612eb1ac626435fb5200ba79c23c22be9064ee54fe4a5580341633fbcda44855ceb1aefeb460385176622a243b4988f21a27ece4955c394ff5f969f28e47e3e361a548cab4be22e3ebc83aaa7a4af42c4909b1d31bdd3701ba12314cd43c8aa89e2341280005ef44ffc82d291d9cf172de664c383c9d102a9712f6379bdd763c1b05ef5a76db94ab3fa3922ef8fdfa1c338a5d58798f3deb889c110aac73630da0b8f8640358576a2192d0205ab470c2e4355f12a909ea93a21be5052f418d9486bffddc3570448a529bb0dc55d8978b436998dcb1c1cba2d8885c23fd2da2f4a7ef27865ded2d9b6803c6e18e4076bcf51a80c7e50a3d11f8885500555962abc55f0388327c8382841da5f554326635a024375c8efc0120223e761db7526c1ad8b80b5265c4c77c344b2bd897f920cc36e5580a28d4b12c50c27f9502b825588e071a84ab55903230af0fb74baa55ab943e18baf5e1deb570f0d053806e717d7ca4bb664b6b63e3518dc8a046bd65648f376cccad12e906c1a43f97e371f72f9cf91f0c636d24451a7d1b6659a252192220dbbc7b69912c278552dba97b86331be6127b252450a1f59cbbcc2b9827cc30b77b4852ae813c12f89da854311388e3a3aa87506e2bd3a038faa7c3d8c2c9131dffe2ca065f16feadec1665b121129175cae941d36f16ffc56ffc425d990ba8c0fcdc8a7ce90a571bf5ed944ee54c93a26da95a660feea2ec31201e602d20234709e62ab31c89c2c17147507cb865ee37b819bbdcd68590d3a89d4dd4c2c292445ffc55b7e6ba32eff9e6d2bdbc204e31b69bf7f70bd045ec22dd294e466d1ebba99ae6e022f5c6c1d1cfdba54b5a1e0beba4a06b666b66c7c50977a3c352ac8ae9167a1a91dd4f148ee882777634d739d2db45cd473b789dc56a119ded0d8fecf9e53cc9b2089cef754aa918511bc57169ed61a699937475a219956490ad956c8ad9a3544550ecd52df0cab2f468bc4b6f3116007121022605d71982683573bbdd915c8356cb2414e3f5b1d142152f2669cf4887e0856b5d4ecbca5d6248dbab229a5f69d589cd0e3464a2ba48750429e2e8508f3c661645d86ec1cb23cc05eb6c4fc748edc6e4ad617fa8891cfd5c1e0e8944990ac68c089ee45c35fcb4cd6ed3c5ef121694efb836e82e4b8b48c38272a13c8e8499656cc90f463e702418102b41d23402b6afb5ff1c1a16240d6f1a8d6330bb6ee2c67dcf443334784d8308735f962cea1850684b6b93730f3c791153e300568d7f18436c7c1d7a9f0dc672bc60be7d75e736ea5c51e5f47aed4826eb45bf874f9b2bb4c6bb9daa43ce2acea761cf4715b24b5128c23df20ed2cbbd14e912ad6e236347e4145ef21c04ba00f178b96d2371636406d518e47c6a841f1d11049534596d32777d627e20eccbfbdf72a0b9f796fb3e687d0d6c45006cded2297ad7a493d1063658e14a71f0a0c2642d496f383896977d5f583de6d89f2fc5635ea7778347fa1d57ce8f4cf04ac781f766ab739176666981c845651c6972418ece4b65bcf64dea41660b95d2a5b7ab6fd20a8e24df3200c412783f1046a1d00c15415d0270c0ccd0a26100290fe393307ecb9806f3124c5d96f4833a056500e4f52cd4127a71d3eb2cfd5e7a7fe8cd6b6f2f7bf3d2dbc35eb2af7e0025512cf4fd1f2740063a7cc7a22b5f0f0bd5229f68e404cf9f0d38327cb0ca3bd994847ec0cf4e71d6d3559bb1457fa4456c3673ddd920303cef7cd1ca5d4f45752813841e70f671c1ba6f1c129f8d44056ea538111e8caf4ce8b7a4855f1e9cf39e44d7ffe8e15eaa1352bafce158d99a3ed416471138d9fcb24013cacb7feb9b00c948b08a40d3ed568b5b34332202c935f6d3538d351108a2acb36561d8dba6103080358e1371810c96d7c11030cbdc67239f44d5f55e1310ab248f1d34ea9551b500a563de3826a0247c444a562ac311dbc87b7b2c65fc5f53b361f3e06420b24c9cccffc450e83fc57bd63da04d3589efc693ba11bdacf5c0f847fa7adbe0ddad5b6bd43d0cae092b8f066a577dca9f360a9921a42cd42d9ca0d96703eaf2d49a022f4776e2de797447e2e30e4077142985c098eb82a7ba88bda7f4f1dc068f765d2306b2ddad594485e5a33c8680cd2879dbbf691c2937b6aae11bf889da62d4b892cb64a75854ba497bd8492e513021b8926427382328cf0dcb95d538d3241b39512d3a1bf9034870afe488cec918ea3bac166abefbc001d38383a9e7be97745840a04424975cfd85b6f8eac367be0b6bf9db16fe7421199d43166feb8b3c3c77667b74056601909a62e655ab7c34acc829270b20a956a196efcea415170258d7fe2a00764a5ec28f0ce965c8e5c3f972b8a3b402704282af1051be8a4e1769b9e753cf4f9ae06d15fd95123c56c80882d3387799593ed21768336c9e1510cf79c29ef013f542aae60ce283799f51cafefc57be886a69d63c70cb267256309c07b452055e1448521c3d104879720a0850ada2fef62fcedd48b387becaef40701747056afc8d20d1884694e0a6a4342d215ee94d5859666666e6c4ec9bc18a678b611e6b6b76079a23911f05173a2c48434345991c8012f01ae11320406482d8d07069949f4aaad64c7a8182963081900ca24192f518d2c00b2f2654684903c42218aa24a9d2c1abad39821e6dfaa2a1018735c4480f23675eddf9c268eb138caf027ec543001d405cd89185d10521a80a542272a8204793142d27a8bc989fb87af852e38214115fc00039d9051204c7450613c4303143873e62a7023541e400923170f8032bba8450e506196ad8f0711513b86077c87ef1f006040290b8a087868e137604b211a7558c446246750d49734982b2e44285008618ad0082cea92d19e0185078c047203f5db07c30ee2995a241ae1ac8aab0c4d454213443c69658a26e66e0206007b0000f271c865eb15053498410562851234ea93f856a50e4e815980b997c8043a28729de0d6bae18b2e0fa19344ca83206cce6dd10c803396504b0e1d1c2c557215d9582a38a0d0e635e1e7801081445d078f110e9069e0130b0b9c3000b5e93666c7281ac8c87025517287e2a504154290f5a0452db340667ac060a5f50c64420a29108c5a2569f6e097b484b902f2db49b1239342ca01296f0094a0e24ec906d31724198085c80299cc4425f76d081d025159e3520364881c77aab1a41b2e2c68d950296d83829af1cb6d50d9c062070c0810332344d6132c3c5a72853840079350ad30639826873c2183a290a812162478b44e30119295167698279c1a9800b086950b8384c400630d26445c80b324ec0517a5630c1c80228bacc1d10c840c5f4009f32b5e787e823e404566fd620f162f5e49643189a269868e0306080441e11306acc8835ea73c1d099dfc311c1ec00516168541d34df0758ec294765ab84a8b0f485054d984b5f04621e2962e1cb8dac3f01c090e442ab4295441068ae92e82083a5e1cb0a1e6339e05cc9d4dbe8c86445c52342aebe24d9450a8801c3444f9b2f505f2ebdc91283b7c6460988552164f087c92546865864ad68f44201ba201ab14292034f80f006a02c30eb050d153a5a586070732580110688a245460d293c6ce89001cf81882b3f27582924589001b245700021183b6c60ab41b178ea01b6274a9b0d9cf0b0c24a086dc8e08c511265c02005a484e806551420029190896ba55874f2c465c44dd5096ab72d7962510182060a16a372e051e8800fcba854a56235b043c90c371cb4c082aa0b063b32252941c98724815aa43db026aa842a3530a2414f1a073a4648412181b535b1008417b146919df0001b344f348c4059514aca9809eae44469c163a062b908e1070c096094fa04daa189130aee1cba204517141d5964c844c3042b3c35212b5a05f2e21a22b6c4813f4a2e387203871d90021d5ce0410b016a83a5304006352f1f740b984951464a3ef4a237032a508f04a8c04d90ea1502d2814590a3503998baa3e9031f5fab173e3e199923ea6c0352407ed8c1836d472c05ac252168681285d40296e6bcc1e105a8335e94d23025e18119214897213b29460edd179e5e9c027352e504dc211e4ddaf45113450519238a82c8d080a5b03840f0f4dc8006f635f1cc59e1c80e4d4b804cbac18400366a30d0d41aa13c50d246d00a263c18b3c158aa155b574c8910e6a3479b4d244880358a051dbeee2079f288149dce018a1e7228a12a81420c1d63242410401b4c1aa4d191c8130d615858c1e68e9c1d09706062149622a9500bae009808316353827860d7c4158a10ac02c1f83138f12e2b3a32386308851148d41044410f162a606007c4c668b5caca22c2541b0f22d81723aea20c69c7074ba4d020c81466808c367d58b990c4e4c683031c80ea12a58e26167a6e7894028f02647a5021c6a103c0348002ccd422333f2e2a8537112b5a39812469861941769cf0022750da0525746883a8835e052d6a017b7cd8e960c0a700aa64ca71268e1a8a2ba750e9b071022a523910be165d01109801c4171823412a200e44256132870023435a48e204094fc54180084d7a28e0e7013c18850ba072991b8a98a4502288588aec14bad120c18c874f480a4a720dd8be0fae66c072b10788f3d92d1cb4996302a21fb28cba20c512433c8a25950d3dbe5082b44bcd40e1f5e306ab4684dae030c995ae855237b438e1e2d5891c3230e509d40c8d949c2901689592142af00c608103195383eca4e132a96ac481571e5623a0100b9f9ce000151cc09c60478417774aa032020f1f4e2c55e048c59bcf15012a90c42290e4bc9241d307381a48d01f1ef81261c00698bc28ad20324018213d7080b00303888f4a67c8be16f9c8b26403970b0a28505109960fbbc9447c658744d8a5146a42bed228790450fc90800e083d5e00f1c2b2028c527f5c00334305070a1895a3874aa48210b3c2490e33646991270854321651e0238d052a7cc9b06a452122a55ea91145c70389ffc010a382196c46e8c2db11048d11cfcc0e80d4ac98a145ce150f570ab0a4c3ef21eb8d2a34b023810a90e3c80154240e40f3680512abd8c81022484f041bd886d0b0270f8f50449c7c61816b73482b29606bd4024b241d6a41060ac0128dc012294dc61d44bd1f3c10307144888c1ed4f8a6425cce14125edda0418c902d163e603115e80a592714915e296ae12b0b2874b0c0418a1812a24eb820f99a0d8e629821c88da01d36203c2e163095019c19cee0b468c301231b6404142ad24bc482cf11553b147a6cf8246c8a01e70ccb06940680556f68703931e2069548493ecd314b21a2c408a25cd4d8a12512a44794f912c198e620928c1430de9860d321120f4b39ce6c2411b228af1524284d41e16c47006b54211002812c22b828c1d31f14ec9aa84e1f941992e98f255529e8b8885271443a8524d587c10820718017981abe1c413953a413abe4830c4c7ad07861254b842e2861521893a7080f14b4c83255a8c0c007ae211c5f6026049620815ca0e045838a0a6e06306e816a170b2d8b04d51741e4b0802a201888d50004b93631ec1013a6824e01321cb083cc9c94a7e83eeda042992f1011a8348c2e6085062990014be0880861e86174098658566a1cf15143265063789a56c0a8d4e7ab8e8a430078437f4cfd1b6b5f2064bac1448a2f2c1b9c71540209a722901343491b14474838c42fc08429129c4e7b12ade0408fda0b72268011484ded8cc7029124b018665a751aeb7344ca043816d9f00449c7f8c1cccd1c4dc2033730becc3131333562614b94a953a41e106472e0e6a74b231007d646d21f511bf40dbe5eb98072c00a19e02003508f088f0b36f036c86105a58498490db05874a30549615cc888c00f964e1cd0798183bd52644203510409a65484c6c458f37268067f140a8e0fbbaf05c4989a14c9c6a21c5a30d9b24101573c70ea4307875cc6c13342b1338b20dc7104ddb0c6c6840a7490f169c850db9376e705a13057bee20d8680c8d442140332560d525145d2913e55a2b4da41030a1b44896ab46a88113c3c6e300252e026041a33e4c9718d8016e46380082711a7c288e042cc121d32c822450b1db3410a175249a22085ff5145d412b01158c11ae567ac00b4205e4d24a8263e3ca8a055ac1903b0b080587de1c2862d2e82007203235816dc19634ae390a28608d4a49864890d9d602b28dd00849897a1b3b34eb91f226cf02003a85325e9d48b444552cc1113053051468188422f64f052b13179e9f4ea8400ba740a82078e1110807cc13091c2185f270f0f84b296288046e583c1c018112af080631681193b822881b2f1a91825f1e491b44b8469851505e84c857d5f680e2864088f15491e7440031a2a1094f96284d8734319852400a73c3e04ab0ebe6f051744aac6ae68d0a501434dba2668f27d5f3940a2c28298241204110708570fc8f7c50821530f2d248109a5a3501d3c89588179f4094f964326dc28524812961ff00b17a45a512ad357f0f41b3a38bad201a33f24b0da0f44c0c31d2432c21a40e030b4820b3856e42134e301ae26ce0c16238c493183e1b72e712ad302cc150b6b61a04430e38112b27791963c804004704c5db2736a93035b60369102f2b5e013aa06914977b2044af37d28463874a82bb7a290095cdf4743821cd4881824e664129eef73f1838ac6e3c606195e28f3a5909f7e20a5690007fc7c9fd701a576d0003313d57c9f8b18ba88828526ab14902f45201e10cdad4ac2f779b5c191c386561c39dff702ccd85a0c19180afb3e153ab422d580a487329f972a16422c818182ef83f1425426503934f93e1520c80073c80d90ef6b8aa82c13f8f83e1828a0692193f47d2baa94349bf07cdf97a335fca9cc17830b2b03eaf7ad28e1d18c38df0716ef8c02f2c9782147cff7b130e2c5d4f781e1b9857d322cfdef63a109cdf7a100bbf47d33d09469b105851a1a403e175f8aeffbbeeffbbeeffbc0feac70e3e80c10a311535c25a9682ec8c302ca0c3b7828a229152510b006597b4c504106024f12d0818aa55363ec74a579e084cd01b004527479d91416029cb050bc2e47c49858312986102fbcb1e169da10a3c9123b332c10c3012646d6102ab4221607dd6f2d17fe9725421244397122d50dd28c1925e4794463451019aafe50e0820dad10ede1c345057b008c16fe59a62f6a44a9ed81e315a284465dbeb8271f2280436f2645b0b685cbba8a4085223a3e009080efd0800d521ea519fb620a060e31258ea42902e145a84087f8c88102caeb8737070422a5b6246d052513a8897148900a2d09d5c90286c75296a743a70808e5e245046364c41a81c5914f88140e3c20193c79c9118489910b2624c073c30ba419378ce09282044e11e822619222e80b5ff8f42101c985a9056e242101d222244026cd89d3648511bce4248185b218970c27e655986986a6f041063776838031505a1938b089d100655c28f124e4c9aab3149cce0c99c1442a1b3cd1c0c302cc94aa222fbec456a000f3d38114148f4c7069c1821c6948449080122a3f7c10b538041622052010b9c1e2e50f52739af819a0a2a8f912668faeb7c8c71b3c3c3a1083628917154800f5f91ef440644d4c8ccf0a2dc25cc02003292d2880826e6281a6e3d203336e88303324500a6f367d2bc830b236440f580c7b9aa870839db5225f194c4b92a8408a451c131dbcead348ca0527e409f14983451bc8e88191f9f80a018731aa2619b900125a08305c9b3452089c784117458ca59b035c622cb4042da171c885496d72c80082c84a978e188ea87cd2c281114538b4128b334a4202f3f5438b1c402009a0d64be485131c05629058014603dca4551c4c2dd89062026a229489182b5248a100ab0600049dca213298ecb4f0f526013b3888995821582dbe74a1f274153aa8f040d79723b44faf34504169863525c0c8f9a80141947a848a8f2e3b3f5c0084c09718295b01ab4f81344122c00c004392d85191a3d1c887555362588208d03ea28201332f26f5298428ee3891a7003a6b76aa8850c3cb0411979c8c9161f608d11840592d08c1900287365c5a6474f0c88e62404a160334207961038714ca8c1280d51803876e404184399dbca8c8b19306c706094860c842aec064f849a173d54849083b94ba93075658ab287b54b868e3c3f4e409843c675410f381cd41568546aa1e14a0a3c9ce1a518b64a078e0cb568582159552269042e1da7a9839249b0c6e8b1d066d4178ba9c4954c804443e70187286c68e2b615e189140d40759047d0170733a4021a64ea526685304d930c3592c1b1b4b042db07c0a7835823875f044cd8f1dd60c999ad103c2c4871f4e132cc16d2103a1458d1a2844782021602309834b5b48171d3a155158a880c987291d615040b35681006054e0f447c622082cf11c439751745a5dcaf46a82286016f48060e91194da31e6075010301026138b1e5f4d7a25987d02820112ec1cd0838b363248a8e860025468a8f0b8b82c1d566d900acc001c2090814e448f785616ce63ac560cf2c0d404b162fd4361cc044913a26c8718575ca8210299580ad48861800948153965fb4ab4c52a8413b122508b6408881d37ec8065850d34ab665410cbec89d6a3813065e30146087c7690cab1c63c101180a430a07810fa62d2a7530ea1366073a42bc42225963c082a389800062e334470260311236778be459c6094f08204920b1f180163c12902491f0e0b78959a94aa8421472c91203325a7d327e7898a014ad8c205ca15206435f4244a0184042d2fb6209026048e424df69f24b83a4d4aa1e0f580154a00a741603e489c18e086294e0c29f4e810eb8616aeaca8b404930d8822d9132e28092263aaab500c32bb48070590a0e5c48bc692133a6cc1d181853b9520d1a9204f2fc501614f7018c24b357626960f3ec0ac69c14534853335278e0d2ff8583d28c90408a421a6c6991a8958891d19a3c0aa4f25a4444121c84ec0ca500c3750aa523e9c012082052dea34ea8157e3041a2a5bf2f410c1880e129871023574e254055f9702929e8d4a02204191c39aacd9e10e8d2f03999a3a64d490b50861c303d28d1da080931a381879dac267001082184d6d50e870c2003f87749a86fc00f68885377c44b8c1cb07607010faa4dac460e1656bd7c4f791f32493c5c2288b8e50329ea0211325a685351ca5468776647d05ac6c12225b410faa1ac85021e4e30c019c56d8a000163d0f24e11383a728c34da81b2ec5e0008e255183863871a20421a62a8629ae2f52da8c01ac28bda872b1d05901f6b5b5e216a986b4423e3ea972c44310325830670c462b1262a8f449002a3978d8a10798033ce47c8579c146580911524a70c1e2b791a0830c54c4b066da381100da1f1a587ea4ea22c023184c508d0901ccd20815a5ea1b3f022c50bfbe56edd891e48402020d2a13030e234e50501145f703103306ed701123cf891e272d5d5e0022e18a0452577878ea939e38f9ca5ae2f527020752ec70d501250db0bc8002099e4b293299597191800f02c04ac3ea480b1947b85e94d822e4894d0f5f630ed8d96140cc0ee855e40126088810830aa936f5c9f322530d255250e1a07ae8208e0f3f6f5a58b3ddd42173a15364c7d012434b54cc34a848c881911e1a94fc3c90670f90a50a9903420fb880c00685272413743083a119321c30786e8c2d11a1852619d7040a502888148a2289ca19408c4609516726b880d3052db2e4080364ed061a0a9c094c3c62a9d9e0440f189570494810d54208180493186d81e402891a8c1e60c0032c339cd0b1e1c00e104c049650e9780f4e7a60ab34585b92411b2e659f0a1590410cabd6b01804018b3c5ca988e068e12987ae26695c64dce993c11a22373419da80c529481db88100cc8ca9466a9438197029546e87120cd0f4028b1a7c158a1f9706a5898104042e510249314e0101c226136866e0d3b158018320c4030cacd3925223508c2805eb0515128b52a5b0828e9c3c592849a9b45fe1c9812cd0162f317cac4d1a01960e4661c48c40a0c69414507cf1399b81428319e8007a534022565f1ac8b1c20b80022e80a0824574088170230418a8b4822147442a581e6440af3478c1030d3683d4be7aa49973bb41c0d98c072e6daa4004414754b4e1f38308abce8a1840401a13237440458617a9e290e00715999203a0d0496829cd89170c2f7834a4ca41670e2546abca0009a98a00b657a60f102d6ae00708aab2342173c20b25aea094282b00102713566e00e33718e495060386030cd17123c2882b1ea51fa15648e1a6860a5df22cd20560055f32c1c49d462e4040408c890072a8506987ae47385030b68397000dd86c40b202eb041663765234b0e54b0aaf185c85d06a07586932529889010350850b155e60681ccac184126254f434f93255a151a5cc820338e012088e8eb400446591d001c505bd4e9b2011a064cd8130c40a65fa7c693186021564ad00d1a0230319350f84b5503643838b819e43740e1083e9b645cf075d1fa0a8012a6c0fa131600471d0809e3d1f70c086052a0c3c6041060d2b3829a9d286498a126732489979a2350aca9d389ebad86ac4702202b0415e2ed4c00214339cdaf0290186481f1c62714585084e6338402d608b734642cba92183be48e2e5022b0c4685a9119800c0150e300554e54ea1592bc3c2599617708f8048b482851bd8ec29804e0d5b45d6463cd0038f0f629c2915882cf72a86212ea424095568d2932f4c699516e862a08f0a7032c1c935c1f2c28a2225627d22222cb08856a24744a91d81b1e992023f2ee8a4729461042406160aec7c6979e0800868fcd0c970e98b9aaf440de451810227842b658204a64773b6acb8f1620d0ca9088ba3b016de44524bf4c84ea18d80572c152feaf0f080ab1e1fc849544393188a54fce0b48174e100511b175cd950a703024ebd7813c7559c3725f6e424f9e03f017288f2aaf3c2ce0f532295fa3eecfc2823a2c789148cb6101f52b9aae187045a26c2024882c2450eba02443846a0f2260a99c083129d3479d9334138095308fb40c30d3b234404d02785105140687191a5ab950a4359949008d0c4c5130d7800f13402003d1906d0fa11234e07343c4823a905167c4638f9b24188250e0c594231e949060f088a94820b49e4a4132058e06ba1c80334a1aaac21322203568fd867149019a728e01343b628e045016228f568818638289cd8e9d3820620b08852721b84d842e0458b03844c00294495198240434224d2195635cc90282e8e40073909a8b9b369cdafa2024603a02c50867a08628fe50024f1d4a2050b0ba46859e155d10a0f245a3323130d3264347043a31d78a070aa11016608846ee6ec0143e3b9d2e449982444194051188b4e11b01071c987253e4a91d1f2c7cf0d53325eb02106a5ae9b45827d0b62811c43bc0024047cc10a15248829576450032b86aa4552c8a2ac187307ac815c0f8c8b8f0e72dc1068005431668cb17881e48b45080104c8076cda127e516043aab41c12415d928db116a5849451d1aa832d25d674a21244a2f9c4d30028c260a84980060ea0b2410d9832c154261ba498faba016617032543a99674f9b200040b64d241052f139c383ff4dcf8640986024a915212e8c5a54ab44786c410952c24baedc9d326af4664f0d818946a10223c2ba4c9000154a552aca2a16676c27165d41aad03b8bc90aae1c58e525b1490d3e361062a2183529083a78e1716557ab49430a945054f46086ac001515952a89d51cafa030403231c50e9c58b2b32c0b8a020d2ab462b7eb8a1641486679558261aadd65ecd3b23388a6582cdd4a41da948a0f801d00f05f2ac0a432653a705c42ce221520b575296b0c0268d0a8d4a72f674d2d1c5150d1c688031b4123a2d8c4860a10ccc0a5f503c50a9aa2076400a7834bd30801c4a660268d1c9040922904434428049375cc1da4b663870036b60d318314a0b24d00323c6151e140228e2228116b24cb1c052b04189ac042e162de0209286420d372668f2b28355834b978c147a9b4ae8c065472a09ca4e2cfad30509cc8e19002256807ac5aa45d99a4c6c42dd612164cc880a31ec2cda31600648898e3b676480bdf022080e1a015aa1072e1637bdc50118028fe274224505501695466fec9cd143648508870ea9b8a9629135884e219ecda618bed7a882e886188d6228000bd3ab406fbe4c58c40a4c08ab2670b5409f586334b8595b6143db65f234c8cb173e5fb15264da41074622112cb8a173478e0a38168ce1444291035f65a0194434a03083046b269c3052048a4dc5075140159069f764d0261d15bca082630d060a401880a0c484a90c4ce4569f725063e17543924f81e0ac5980db0885c10d733ed8a1e20006366d5ac5654fa24231ccf4518422cc8b2e17ce894804ce8d5b283d5f1f6ca2e205412317666372b85893211598248003720a0f761b38ea8488c626273664caa012a1102e30c129620806b1522b68e94009a840a7225e04d471d34217f6aae18304c29f22a794a4c841465517113430b800e283150b5c88f2e4026f7043880b0c08e036c1dd358087011634d244504ac48f1d6e5aa9a139f1428d5a06280af5547d033a8df0e283dd9ab439471e6881c7498e3169730c18e0090b08544a6427d40c4921220d4a24d27de041a31e18d9204406143e80cc46917848a3c288354e588749935a9488a3006f4d873e3dda6c6a9242aa1e09cc386188499329b92f128256f0adcc2821019b7489100578880a7d960089f5420f0d132c1c18d2a9871454bc5a206c821f3520e183498695282a7a5eac38b00777e84d9d0c9a400046458525dc24573a4aed2e6b615a4eca56e4600548a320beac6d6d49134e8b940d31fe4caf558ae64821414d902542b6f070f0752a16920ba0f888e2022b1b978e081912aef88c64fcd8e206909a1d785c562880a10c8c5836516fba7668fab3e3440abb2f6c3c7d911143b6c9910f522abc3293410b1b112aa9f07445cf1b1e04c822a684474e82a0793230a4504a9390d7081920c02a6c41d2220dbe5694612096203f3c9ec05a9631195ecea8b812e685110650bc78a820658427a25e107b51c8861c5d995365471cc0810f8e861139bc40707525854114f402a54aa1268b4d0a25a490a155a15631107dc56056c74f160e5f107801c60a0f0fa1446562f2e84bf100ee05565e49624053c784ae4b074e654ab46026cd8b140cb0308a91930022e51608f3eaaa815416175ece78da6180046b4746e1520050e5e6038d17c432fda02a6abac34023172362ab1e5088a213e24aa61910a0a04833d96302063fb4e85442241e48f0b1b1c3010007706c4146a0a10201573a988013c21b436bdcec5c613200055a020edc0f5cb05027c2d02ad20c044c6961848d95214fb28c911143428d041772d8665401b4830a5c2ebb5d217b62a3482b0de60320164b360155519d41ab5ce5f8010d0b1134c90823e12ceb541919b115185d808c0edc88c2958aa4f5c3c4c4d828457260106c3281429c193699ba628520506ed8c3c2c61bb02eac50ac79ca8a61a6853f5a5688b103213a267653a64441a1eb21b222080a602c04c13203e00bbe9906908b251b5cacc8000313b68e40e9da8aa422132414fa2d0698858606861f224346904109982e25e0af3f61a2761320e1726ad59a13948aa8d0b342850d32e0d80c428c1ac4e589092d642001c34ef7abb6b88c6c1003021b8136f8b2f41564060d1635800ad4bbb810c3d9192e612a26f87c3a71b232a50432232c44ea0034f5821523af394d76b0a1da326c91208114ee15039d2d589cd0c1f244860d504f7c88a2818a13af86f0d0c747ed238af7c2540f67e08b274158205b2861a6854e706ae0a00210c5ab27a693a71a7d5a2000c71a0e213e3416a49820032851502d900217562d7045caec51e442042e3f80f8187021edc0c00354b2b080c3ebabd1a93fa53481ea21a581af2f33986920c50aba274a0d96385142a302030c2ee489c1058c13de00c220c802638c0c4a92c4081b2634f8a010a49cd0c29e4e9c666060c60b40a150642048e765d31900fc78302911a64b804cb448f38244d91b0e26cd7112c48d08d068fa9ac127cb9b1b212851f4431f45d486b044261407043185d3091d2b073c80e1ebe2495208163a00a1f3aa20c90e32b81000903752120120235518242f8c3023070a2255ba4408070fd6801d2a917bb40a051445257cd2d2d535515870266cbcecd884000e402db64864fdf92383472834148a56b0aca4ccd8b2d468c51cb0212b88e58092a50612a2ec1a65a943366345a6ac40780b092f0e007462e703a33c7b62b479a0451a504b16427450301121073626be3449264f9264b9938375f685098a0847562879c5818d19d9111b1300584104150132c0f8e483c40e0540e2010d4d8d0c9bf6dc80e9ebcc8003be48b933e591575ba3458ba12d1591761082a2e34c8e95180a50a110240f178c0c44067ec0884212c20cadf810a26004f8b5958110366dde0802418517926c408094540c4153810edbae8a9512f284e19188c58f1773f620f2218c09553bfc99b469122e0301bc3288800220941c8942e70f030788a981db48f1c8862f89685801e4c3024b080b954421b8e88100183ce833e92801c2910c43836edcbc74a6bf064746833832a8ec1027950b19521849020b55a30d20a8628374f6061e272eaa30008b4fa1225a50d031431bad9e082d3406f56042932c2e82105a7b12c8d157006550ac80c1dda1c789492650bda0a4ce293e715a3402f2481025148a9cf099743be137a93376b8c48a93984d7e0e68d200a241633a0882424aa33a1ebce9b3650a054b9cacc0c4800f5ae82804042c840586a412e1531f33a0543d99a1c3bc583030ca021cfe8216b8154038b050860230618789184e633c3dd0692d090a2ceee42941811002b02c30834e97a349034232ce4c6222022640754fe8a347180120c002b6c3d202a6b13f784add4881958cf07e6061821511222f58b06600ec49930255e2b88a8129043a9bd6948080591a4d964e488bc1b640d0a0541415760658c5e7d38d31288cad406307823c08d8c93a9516e7024a43970a6214d00877c48b9946bf842113806412c0872f3a75cce838f5e582cc01aa7a309546922215afc8d4b0e6e7dc68d00187aaa70f8f53301cf01483a645891065128433436981111d7cd948983220939c1048e9c85a8006181f0054acbd0115dd684f1b8c10644fa889899a179a76c0a004108c4889808f1313274840c0162f48adf01cc241826b28d2aa2379caad91c287436a42e154a1d9ae54c1802037f6e7c505852ac6588e3d61c09cd5083cad44833d6e7943de738d7ed7cf8f6490df8babbfd276cda90e2395f7cafebdd02318fc9b462ae79c57631d7568b3e94b56e5c62f88c3ef6ff6da724da7bc578a052bbf61e6fe66a97158e2786a69b79d2ebc5da9a482dbf29c39f617efca29ad382c771bd7a3bc51d0566c698fdadbeb63ac1f87e5aec7bbe9df33db49b1d96cb66cc9aadcb04c30d24aef9f365b8fa7d6518b1affbc4d2d59959b1309fa19f1adda7f8db7dcdce2b0bc5179670427b5fee7283fbeb6d6887158da1e5a480dd8014a39a78db56f9abbbf1fe3b0bc4d797b6821bdb61d4f6d77bbbdd001dee8fbae5f563d7f8ef2e2b0fca2db69b9c99903ac76d788fdcfb1c7f96dc56179f3b9adf37262b1b2226241c4c6c4cac4ca9488f51fe5cb76a2a920005629f3ec17fbd863fd75fb9cd9979c9a2e77cb92070ef05f7ce3c7f1e61cefb49e8a9515119b546da70b6db61e5a68138b534dec2c0dcd4cac4c89d86dda3434338aa696a33db490f21b683addedccef6eb71136c0fe43cf658cb3f2bb2de7382c7b68e14d4bce8c8bfe3b5daf64556ef10070e6af799456eb3fc33f310ecb1b96fd7bb97dd93b6cc9aadcb201f2a63bd3a23b73514098580d02e82dd63860001b613668e49871800c1c31228c1b2f0e70c173d1e2050b2e7df3b3331d8e157effdd0b15607e2e85bc7d2d2e50485e0c301b5f33ad86178c13328689020498fef7c07c1c25e4ede36290087b6a371901749e195a8d115384bc1100c044d3f5dbdfdd6e92cbb533ad7fde0d8798d195a70ce3aaf70646f7a52763eba6256706e68769c9dbdf8531b2e40dedb6d3f576ef81f933be2f13be16adc9fa683bc7f5b5885115125f245072827c5aa1be4f0bd4f769d5f07d5a9fbe4fcbd3f769d1f07d5a9dbe4f6b86efd392e1fbb4387d9f560cdfa7b5e9fbb4347d9f56a6efd382e1fbb45ef83e2d17be4f0bd3f769b5a0c5c2f769adf07d5a96b4a468a121f145fa1427c81782c6f705f9be4cf8f37d99b0e5fb32e106111a067c2cf4970960dfd7a24adfd7a209beaf45bbef6b9191ef6b518def6bd1d6f755c20ddf5709297c5f25b8f9be4a3813d1e4fbbe265f8f427d5f8f327d5f8f207d5f8f147d5f8f0e7d5f8f3ef8be1e65f07d3d9ae0fb7a94e6fb7aa4264089af4091beaf4a84beaf402688f0dab1c2e7b543d2e7b5c351578a016e7c5f039cf8bc6698ef2b0c88ef2bccd4d7881fbeaf11a7beaf1199beaf1190beaf1181beaf1178beaf1167beaf11b8ef6b8493cfeb4d89ef6bc4d8f735227e5f23627c5f23bcbeaf11dff725c286ef4bc4262f2f0160b028f1e5f5e67e5e6fe2e7f58680effb4c7c7dbbefebe3dfd757f6799d9af3799d62f3799dda7d5ea7b27c5ea7aa7c5ea7907c5ea7827c5ea712f8bc4e45125e21209df8bc5208e2f34a21d5e79542a8cf2b05195290f479a550e8f34a21cfe795429ccf2b05329f570a699f570a59beef3b01768244091264de7c5e64ce7c5f97b67c5e64c27c5e64749f17192d64a6647d9db0e8fb3a81cff775c29beffb4e00fbbe1895fabe1811fabe18b1f9be18a57d5f8c9a7c5f8cf0f7c5e8fbbe4cf0f47d99a002020784e14fc514af4a5607f8bc2a009f1755f57951549f17f5f47d5d92f279d14a9f173de1f3a2227c5e14cfe745e17c5ed4cce745bd7c5ef4ecf3a2b7cf8b3af9bce890cf8b2af079d1033e2faae2f3a2027c5e54ebf39aad4ebce82af14542802e3012747a9f179dfd79d179f279d129f27955053e2f3a3d3e2f3a057c5e745a7c5e74427c5e16acfabc2ca0e1fbbe11264eb0005371262bd4f76555f07d5963dff7ad7ab2c525624b6b0b00ad10008420116280af01e4f7c9053ee90508c7e70528ecf30284e2f302c4f579fd59f579fd09f579fdd1f479fd71f479fd11e1f3fa83e7f3fa73e6f3fad3c0e7f547edf3faa3e5f3fa13e5f3fa73e4f3fae3e3f3faa3e3f3fa43e3f3fae3f579fd09f179f909e2f3f2b3c3e7e5e7d4e745e6f679f9d974c22bc50a11df674280cfeb44009f083c254aa0789113f379e5ce3eaf5c96cf2bb7c0e795fbf179e506f8bc72007c5e683e7cdf37054fce1ad4d505e4fbb6e2f77d4322283286cca766030dbeef4e0446be34467a3bb32542be6fe7ccc21298610584e5fe59ee9f5951a67cf4c0c317e803417e5f9d381f9c285f130542957024949133cecd440c1131440d2d55160e9c166595c0dbd5f91196b6fbedcbcfad10221598ef73419cd4d0824a9bd4d1bf97b3b38d36c2d96e07e6b4989ded1fc68cd753d3ad6d258300e0bb1d98252c17c00cb6dc1aefcbd309babe0bf27d3ab4b39fb65676bbc5f9a26104dda8effb4488fab4e4cc6e679bc60fbc9d2d6da3e52978ca9d62a7d42974ca9c22a7fcdd3732e995b9aeeddd2e88ff39b3f0fb1c949c59ef9f9da909f93bdd58ce4cd77b6642bcd90d4d0d94a2ebbaae73e273bfc7fb5236dae6a925d97dce65a723b2b62684d7cbfddd4eebed76da4eedacc9da598ec9ee6dbff693ac7d2e71aefd7ac17c9fcb4d4a96dbb8b2bfd32dd1a5e9764d7abcfd95f482a0e972dfa3e9723b22bcdde7687fc9ef66ba286b3b35defebbb29c199a0ff279b91ffe2167dbac9b793524673c332d5f09aeff3270f7b998e5744b7a663928fe885ae8cd3e9aeec9cfe9c8f4482d1654b2feee122426801c397cfcf01176038c37b3d10bbf121e3c7a382d664e764c7ae157121616161640036160bcd9ed6bf1ffa624460818214a74f2f123fc6733f8e8814058ee9fdd6efe4cb7a52a072e1000d8d1e3f9301f3ec2fa8ec763d88f2e04be36717ddff7bd2e1756080419a6c34718cef6b5f8ff95f9be3489f17654ca8e8c056912642cac4718186f9695f695e9ecdb91b6d10ef8be2f921d030c088b613e0608eb7d5e18eaba7e74e930c080301f3fc27cee7f5c8db80c71bde142c27584cb0857025f2fdcb0d3f176fc6e76f6c2a58f2788b7b3fdded662e665e776ec08c10515783bacb79b501e0f1e957ae1c16356ca83c7f432a3541e3ca8177fa446a151fce7a9f1e051b1647c6da5f30716199680951e7d1784078f7ae4c711de8fdc57f2bd8eafed2539b55dd9931fb9afe4a36d32307bc94e973343dbe9b66cb48fb673ba3077844bdfbd27ff8cef2139284d74645cba4e49ef2fe9e9c878ff1339db7f888e0c0ce8cfd474523eda26fbbcb34d043c3b5323e2ffd95910de0f0a66a3f9236bfefb9f83c283c78f1b3926e59d9da991ade582a0f529bbcf537b4296334bc24b43db09f1b81c94b4bd2bfb5ecc78b99f0606d4e9ca78ff13d1923323fb6aba5d0e4a6fa74b1bf2c3983de181fe4c67e49ff12d4557e6b790b3339edaae97839283c2c49f9949f9444226676741cece82f83333293f7a3d08962b3396fafb11ae3b13d2dbed266b5e27c41f59f33a213f7e4cda6352283d5e4e3796a754cb6af5aa6199d41fe1b2d37de97db249d5b6f0e031bdf0e081e4fbbeadef8b120294c2be0fad8331a3029def091993236563645090dca60459d2bf971b97fdbffc78d2bf97322efb4bf93db3dd17bfff2ed4e976b7f0efa6171e3cd2fa8fc2334b0bc2334bdb5a726769686669bc2fba1ebcef75b91b2fcb0749694bf6dff578457e785d2e521e371e65cc34e000a40ebe6ff7789f06bc9d6d4400399dcfedb09fd305f93ead1144e44f2dcdf77dbaefebd1a99b907fe6cf7446723f8c6e47e4f7f6df8d7dd19d7d216b67392650723a33f06caf6d253a32273d3334332543988c918137274c9c38016fa0122843982cb94d09b284078fcdf31d7c02ea74fcf7b6969da6dba529f1f2b5e474673fb4ecb226479eeca0e8c8767e0bf15b088f07f67666a14eb76be2b790bf53eba9f5cc82e43e175d198fa7235bf2a347ff51785a74673d9dce499adaff3c27bbbfb6cfc2ce762ec84ead77dd17dd59980bc2dbe9c8fe999ad7a1856d21696a5c743cb38d96d3a971de0fcdfcc6f5726a617fb789f0726a5acc78fef7362f01335b58186f7fbecfc2785b6de3c0fcdece8cf7dc0ecbf11b36c2765a98ae17963b82e36d237bf7f70eb4d65a6b6dadb5d65a6bad94524a29a594ce39e79c73ce29a594524a29659c71c619679c71c619679c716aadb5d65a6b9d73ce39e79c33c618638c31c6f7de7befbdf75a6badb5d65a5b6badb5d65a2ba594524a29a573ce39e79c734a29a59452c618638c31c628b5d65a6badb5ce39e79c73ce19638c31c618e37befbdf7de7badb5d65a6badadb5d65a6bad95524a29a594d239e79c73ce39a594524a2965943ae36b2b9d7209da918fca5721b1dddfd1cd8efc9c99961c78b6ff93bf6b82f67b47d6f8f745e8d2f7656142e3ed2f613a2395783bdb1abff1783bed76b67b3b5cdb5c74bcb01c3dd278519278335d1426f57a2c6095234f6e43c02a47940439e2047c72039d40290b4256e4c893cdf3fddb6cb64f6bcad7203b83eadcb0d96c36c240274db27df224cc1a982f5ed47abc5d97345d0e8dcb16339c962a54a6488902a5ec8993059a902d51920489912244c021634282ecb0e9c8112b56acac50d74b5bdbf59daeeb48e355f07d68ba1d91dc3fbbe97ab734b55b0e7b50cece36da139dae4c880fad0c1a919055a9854b5f827a0ce9e9ce9ef07aba335e4f77f6e49696b679f0d0d2dab9e3f1e0b1776afbc7aec7e381c6db5f7ae876bfca9a909cce8c67e6c56c88cf41c953421181e70bd009dff7f1e0714be38585ed7461a01b665ecc3c6f67bbf90fc62cac534737eb7d345ccf6cb775a46d79900544cb4696a62fab054b599238f8e0136f67f3fbefc03e38dbe9fa0e9dd977456d17aae57a5c2dfcbef3ad33beb6d229a3d6092d125a5a5f161059adfa78f09d9da9e5befa94f9be2f4b96b05ece4cd727cbf7d5a77edf97b6d6fbea1302161f1fbeefbb11c623470e1b5f7c60e053292c87a676a6eb61613f6796d3ed421d6fa3a1e9c07c9c16dd6ea7c6fb39ddd98d3067a6630702613b5d18d9260bebe9b4d808fb3db39c5918d6dbbab390f77b3ab52e6139b3cf65ed73b94144c6a87645478c31aa5db1111696a586182c39b2f0c8d2ff16ae0bcbeddd4e23c204245343a555695049c243065003347a00ea51325e248d5281803837c808e1b140ee811812911100070c515c70e28cd154162ac6a51da7317afc304102c40aa831689d92f8f92ad81a8345f550f3a9dd19b315e395aed36a909815e448ba72c38d1231177c093168001f0188e9147039e1059d04c4ec6802288ca3830f0eb81220ca083c1e2239f028803f901ce1c80ec411b1860b0b653f3840a58f10222d22e8c101b1342b681026c4ad01555c481002222b6920d1104f67109080480369b64869adf181a981262ca0b23ce8c8d0c00b52e000ada8301066541a2e18398a900943085f070838fb611a68c3264b0ea40b436444026574487113e60b4f10211b40696b33d448017c09b3648d4f9c0d12a040928fb59d004a76788047aef50085824c8f3eb006000c2195aef49005069303360d09035ac0640046cb0860260e9206f06ae4ea0bac23406a290dae7c91c4a38ea31bc60cfa52014f7167d134e0cb6dab4c071067d2bed860c14b9b1d77aa78591503027adc8851e185110a9eb684d98abcacd1322a9524b21d5eca9ecc9882e7871b2f6142c60452022b93daa9ad57a052a8a0839a211660917d1171825a193e9f0405685ad49070f082a72620905053c1a413191ea31ebd1a4a109a8d62f5de9f22664eb57081063d354c2b40e98569d10ba2d6a5530f9252f448e410c302297c20e0659a12eacf9a132e5e9e4f22d488d4bbc43bc36011d7aecc0cef828a97ac78b1c4d3229380e7c08dc4ced29f1d9ddca435727858b3e38c00c833d6c30b3b1db0767636d0e1499756b01aad7021c10c5d1ccd4a43184da1babc1121d1ab09a870e93285821d32488a285d08f0a00014be4e5f6939f07476aa1c1bd248b861800a477ecad2d084a11a7247cd585a9334178e4a2823e90a199da83b1658425b514937c8872e0c177128c419393a10060f1c5cee78d1e5c8c9c03580981fb6a7dc9e3ba5e80061864cae4b2e013f33a88e1d9c5c8801083562d3a8045a0b696877ca5628a2201429b4f0833f9384304185360788c2c30e8f80b3575a04c4f161047c1609d219057112619023b57086e5ec0047632e091f96cb2a2d4a0ce83c0e2e266ca1e11599107eb8a88142464e2e8ab83cf972010f36a2442e340a4001023eaa946c31852a2ba79120802d8572a83a700722b690e16d3982c6850b61b8c0d962f6a9ac0d0ecf904c667ec23c096089870e66bd522d3469e255cc808cf00910203210cc0240414b98404808e160d0920688453054c1e5a1a1018735c4088ecb5701bfe22100cea212914305399a70596a5c9022e20b0a2d954cccd0a18f182a2d167409a1ca0d32d668d93cbc018100242f68c99148cca8ae2110843d0041e7d4960c72d8887b4aa56890ab50822db144ddccc009a584355cc8e4031c123e7e2a1a2654190366ff115c7c15d295a97fa6081a2f1e22ddf0c936b94056c64bf15b0452db340667cc875ad4ead32d713d081650094bf814c5af81f143f0586f552348de2b876d7503b7418fe15394294280c0ea1d140243c48e96d9d33a0f324ec0517a58e85dfc12b7536346ac51df0bfc73b98190e442ab42b5c486b427d857786b6c9480e0611bd0e88502744138cab24396455922c8e2248b0c18a4809410e1702bd5c9139711371d6e85588cca8147a1036e63dc70d0020baaddcd88951b0a09acad89052030e0c9089415a5a49061e083107ec090005689815e17141d5964c8c40010ae21624b1cf8c3c0083c6821406d302c2fa861c11302d28145d0232c5b7c7c323247141a960a6b49081a9a48c1f2514ac39484b77645054f2f4e8139b172658e0357f6c433678523f10a02686a8d501e2cb1d2831514ee2079f288141d2b6e748c919040006eac44b152438498b129413c50e5064ebccb8a0e0daa20aaf2c01723aea28c5965499b3eac5c486252850a28538bccfcb8a8444550901d27bcc00944050c953114a89800e20b8c912076ca26499c20e1a938a670e002a85ce686a2293a12cc78f884a430457399c2b5c4108f62c941caa51f37583522e4464a9d572772c8c0d427295e059e012c7028232546f9e14e095446e0e1234a0a48621148726045994084011b60f2a214c50a101f95ce90814529008a0e69943c02281480a2080a044e7298214b0b3d50169061d58a42444c4129235516c2914005c87124a64c4c599132b04423b0444a9bf1a4530f6a7c53a1fea48f0f584c05bac2e4094f070b1ca48829f1e48720378276d8819e8868c301231b64624e5cd063c32761930c271b3831735261bc31c1a6432427df0293c22c20878e8b281587a405b280a9e1cb1114b4057034d084950a0c7ce01ac29b9cd084cdad09143a05c870c00e324d060011814ac3a80e13544c448051a9cf579d154ccc3051c2840c1419a01fccdcccd125c8bed81265ea14c90799903692fe88dac09309b044d3123d4b724b32932d1b1470c56ac9d6ce2c82704fa08405196a7bd2eec050728192ae64471257491ea14812e78324558224115b01892a248990a441b20006488e8ce21d2174e5880347881c5162648635237cb6915d29233ef61801a008a6221a14c1852a4289c022228948052c88dc88dc3805f2009e00aed102cb2c1836c19053430e0d293324c990b11ac6fe8c79190b3256424826217a84a009c142b48258b2f483d0d18426480f62435306880f5b3e208ed47c40e0000142e50322f62900a7ab7723f7cf6e5f7ecec6da6edf761bd76f3c335b78f6795a5a214c8430c1dbd972bbdfebedb41dbb1e2f00fa7d5fdaf755270b6f675b3b531b42896c938539331d61ce2c2cc70595b2881875c192ef7baca52ddaa48eef77bf67d6cdced2c07c2f616ab99fb6d1c2e85cfac2e818eaf5bddbb87ec1f77d59c2765fcbefe5ccbee890c91296dbff4ca7e36b7bb7db7b0901bb3874b876552c2883ebedb41d6b3d6a93d23675e8c29c990e15784ec270bd9d1676761684070fae04fe74214af17d5d88beefeb7d5f753e7dd116b5ce1aebabadae9aeaa9a58e5ae79c71bed9e69a699e59e69835ce18e38b2dae98e289258e58df7cf1bdd7de7ae99d57de78b5cd16db6badad96da69a58d56d75c71bdd5d65a699d55d65835cd14d34b2dad94d249258d54cf3cf1bcd3ce3ae99c53ce38b5cc12cb2badac92ca29a58c52c71c71bcd1c61a699c51c6185b180a80ec8631079c80a941e6b27470c60b85a3047651ee74351d657898b2a4098e130d0d5238b32852971a232b3c39a181429c7a7091a4852bcfb8d00020e1855a70a3011f52f4e8c38004aab6d89c8d40e00a980176fce015d38cfa41e54eae84176a0ccaf2c28d2417a57ae479754587981f70e05a12c6a901488a115e91f9238b607129b703270298f9bc2a78b5a9090f581f06181fcef4346110be8080af2a34011d1108f07002a63237387ca8c84c6e5ce98171210a162d26fc8c50e4bd5c6dc221549c2c1f7c8972b21b200f1701c60d2b2b72a92c1367429c6a6259e092a2221108437e70a2e5070b59118f01052ea02f5ec02549416e980a6499c34a542c0654c1b8d4c30c10b3351b627d39e4f002254a2f5600895ac24491527d0ca8b8f1d3434f035f9556a5412569031d4ed550838fb2576493623440a307a01e8df1672b0b08453651bd159246a940401c1ba14ad0d212c41c46327080f15820f7400c89984cb20341f7130b891a19386088e28213878c073e68eaa2e10f11804559a818977631c44a94051017a153773481f961820488951aea6c52aab6505023664d46a7247ebe0ab4e06c9170b1c11d01e6bc6121aa879a4fcdce9e3b226a50e280263f9b50c478a5ebb40201d120005e7040072c071433e448ba72c38d122a2e3a68752686aa058d165f420c1ac0c7884f35344941950c359f5ca0a080cb092fe824f0620927bc0a890ba4c2d06802288ca3830f3856dc583583061a9a70b81020ca083c1e20a9f9526463d418329eca5204f00792231cf98c531061882e08958c798958c385853257aa40882f72aca14abb2efa0821d222821e968c72e128cc953fa416a548b38206614230d001a2222ccc5991032a15bcb8902004444e2282ab100af801c32807185e0cf174060109401ddea4fd4e12c4225543c51629ad353e5a78434282194834c54245e6c5022acb830ea74d237c108261adce1a122f488103b4a2c280016a3b1ec8c1062b0fc054a4e18291a3c8983438dce0183d694582075ff83a40c0d9c7507c84d539e16a3f81d1864d961c2867422a49d5860b385a34568c48a08c0e296d28eda01d3d29b2c78e922f3c41846c00e93259d96627ced038e023468d14c097304ad820c08209322900d0615b116783042890e42381280afca8e4d5b5c804300194ecf0004f0c6248ac9f56a2057f410c5028c8f4a8990af25ca047490d4c00a8c20286904a577ac812522a101907a5766746c01cb0694818d0822341ca2e060936a2e09151819a427ee29e9e1546317e4081931865f1018f1b65ab10c539f20749b293c206476038481ac0ab912b203c109d80c7012f455a338e00a9a534b052a7951b2d5a72949a2083163cea38ba610c08826e1ca4b981801b7a6350f01477164d030a70c1448328670888e3696c95e900e24c5a025642b040550b98cae469c18297363bee5099b4c2069813fac880c10b143120a0c78d981478688020d207c111232a68a0e0694b989d0883571d74c1204d0d480d175a46a592446603e5cfbc5161c0234a96e2c98c29787eb431d5c4ecc71727174e172f216302298195e9092f829f23480a0abc79b1f50a540a1554a549b32717e88af88352b0008bec8b8813b6b4b040015218409b6180179f4f8202b45011a73e0b801152250ad50b0e5ef0d404840020e1d843868649a91700a960d2890c8f51022d2842b4dbe2d1e814e5ab04a1d9285637b123051e5880a38410188c22664eb5708106625cf448342a8414646754605a014a2f4c0b9f22ce41f3c6cb9efba5d6a5530f92509c19abf2753731706a021839c4b0400a9f341a60ba71484c02efe781155342fd5973c245c644048f64f8385543ebeb93083522f52efd3dda6c10ac325359313058c4b52b2ba3270f2bb552afcaf898ad00152f59f16289ac54ab0920f0632394819149c073e016c01e086e7d3410e14e055e64206900d50fd28f9eb43a25a4988e1116b1c84d5a23870735665605e2a0ca1929906081310220cf580f2ed8b921e510282d401b4819b0767636d0c1c90432305a6396c1275822b080d568850b0964e8032a569e0192bcf80ca0989586309a40dd70a8ce12433d54c81835438444af26a0c2250594462a70c891ca82045a50b043064901e5031a2a0d52a414302408141e1480c2d7e9ab8c90a3516080645c9a4483a7b353e5988ae0011744c8f8b4c3901717370c50e1c8cf930deccc10628c921d7a7894220cd5903b6a84905a7144034a698d6e5b5e2fa05c12a359980186c7289d0438255b2e1c955046d29531740c78a447890a1ea01652dcb1c012da0a4a80686c1d1ae1a791aa3e5e62c02e62603dd6846086250f14dc0910bce0220e853823e7c419192669d5085203500183070e2e77bcec5808054035556fe4f1022527550962108053e2d0065820883300190cae01c4fcb03d2529a20656100194b14754dc294507083364746768174cd2f3e1c8f385c489a720b4d42940959440391814ca12187e66501dbb371cbc2800d4911466a40ab56200428dd834227148109a43005c1ad5c6d497a137368115748fda648065034e0bf44814a36c85220a429102009434a7521c333f385af1c19f494298a0d25c78be64e29435c70c6c80283cecf06ac8d92a54019891ac216532b40888e3c308f889a6b1800831d8c103018b6a031048ae52984147087d5208038a1426b0388930c8915a7040072b246c9174288d9a0c43581cbd29f1c07e2087151a04c82181542c1c8db9247cd8172bac09d582440193daa0d0a2c480cee36860538e0911dd02b91133b6d0f08a4c083fb713068dd1d4e281231eb48042464e2e8ac858a2120b2c4cd60b10a0f872010f36a2eca8d32545251392b2bc60a251000a10f051a5c486a1542890c4604787262d5065e534128c488463ca095d0a07501000450e5507ee000049037c2a31a553c4208dc6a830235442ec6260a1d2a737364338c85ca071e142182e7052f0f151a68b093da8f991c24c901049514a78a181f5558985009ee955d606876748265e06177c462ad520f5e6459827012cf15035421006849c3e614d4852946aa14913af12b3ea1ce2b0fca23be3ed825cd9736bbcfa636b0e486358e396597f4f73dcd666b3d96aae72403ab1bed9ca3f6f9ebf724a2b00bbee58c6e8a5a7b54e9df36c5100468be7d514fbe9b1f79dc6d99a00acfde38a6be4f8875947dbe3fdde5662662b0ec8690d6978f1afb5739d2f68b3add98203d6ed69eeb47f3d33cf33e39b14b3f506c411c759fbe6fbf6df799700a47edf184379e7bc546f7fe7961b507b1dda1e31bd917e5cf3561b307a2affbce19d396b1b6dac6eb6d880f15ecc7378f3f5d177afd5ec96ad3520d521bd914bcdf3fdf6d72d3560a67cf38afb951c63ce7304e0e59c66bc31dd38fbee3f0460186a99ed9f9ac7dafd0fd38057ce9cc32f6938435f774403526ff5bdb5ff1e79c5d99f01bfafd4eb9837cf9cd21f1f00a5d4bbfb3fabd5d47baacd80d473ee73cefc76cca9c65b65c059fdd7619c94fad053ab6381114f1f73fc5df77eafecb0c0f0471dbbdfdccaaf39afafc05a77b6de5fab33d53f0601c66833a621bda1afb36309045873ac7e7ecca3e695cbfe03b8bbbc596a2ce9d6917bec0ad491d730e4ffdac9f3e7d207f052eb3f0f23d775f75d712b3063aab1fcbfe3bc6f8fb60790467935973a947deb3d4356e08d91877c4a7cf7e7344a1ec02ae3a721c5fcf66c3da5abc019ffa4bdd24b6d965dca1dc0e8b5f5776eabf3ae6168750077dc34e4984aaafbc7ddab0263a419df1d6e1d39ddb7e600722927e7526769ffcf39a7027f28afaf92d37aadc7019ca1c7e1a579fbcda7f4378091eb7086fcfb4bab95a14405ca1b46ef75f8abb575fe790a9c75febdfdc5e19ff56f6f03c831fde18c557aa969f7d914f871e4f5fe2c73d4bac748c629c5a5c0587395f576dc6d582f0f49819386d4f3def7b73ffb2b4781f9736b69aff7637bb1f4382caf5859919d962850ef3ce7965a761af24cab0e05761d4a7de9e63dbc3d0c4350a09d3ec434dbdc73cd33773b81560de09c99d63c29df21bd3f6e9bcd66cb3769ed2790d2ae7def58877e67ce330e4bdd43cb13a86fc6fd662a7596d2527e76b68948f965efa826ab7203b5680031fdb9ea2b33bd9bff5e3359959b9a562730eadf79ecfae788bd8d7906d0fb6fc3886b48b3c4db4e365a3280fbdf69ff0c431d669d25e66d5c137c936b5a9c401ffe8d33de745e2aebff31809feb69f9a7f8e21c3bbe761358ebf63e86a18cb9d72aa52690fe9b75d619879302ad4ce0ee3f76df7dcef8625f6100c3ec75a6d8f37fa595bee79bd60b60f436e25fafbe37bc3f47b22a379c960b20adff4779fba454cb3b3181d17bbef3aedb564eb9ee382c6d369bcd96d36a01f45cea50cbd0fe6d7ffff11258c3f97b97b1d6397fe4de6693f22633095a2c809bdb10c7ab69a4944f1fda6c9d678666b3759e195abd3790d60a60bf748635d4becb6f7bf4369bcda6631b2d4b20deb3465ee7e55a521d6d9ea35509cc36dfed65adb3f2bd7d88c3b29733c3dd7e18331e25b05a7ac3dfb9e731ceb8376f9f8d690bb45400abf6f8773ffd8c96c67b796bbb89cd76939340feafcd975e2c2be7f6cefa434b122839efe10f69eeb37a5ec5ca8a88a5e976436cb634ddcedb6c3cbfdb6a45767f6d2b39db91c0ebe7a495c78c2da6f65a219aacca2d8c1624b0f7386ff8adcf9edf1f770a60e679de9cf38f76eb49350ecb23bb1d91fed39ed86cfa11e8a9af9e7b9939d6b4cfba7115c7db467abbe77463f582d17d29b3d970bc8db3d9b22370731f62ebe7b7f786ff2667a65a8d40deb7fd93f2bff1ff39d625365bbe490cb45000f3ac54cfceb7ee7d4eb94f00b58f369c5dff2ca59d35a3ed7444d076ba70ca0eb41881f187dd4ebdf79c7dda69b369bbeba5512c5a2680f55f7df5957fda4dabd553c8aadcaa682d027dcd96c6df370f2dfd796a9500ea3ac3d0873b4abc6d0ff725ab72f35a8ac05b678e34df4ba3e7d14b1c9668ba5d58fd97552b11b8f7ece1af77fb9fabfd5a8b0430ee1e75d53a87ff5f4aa5d6086096974a8c3fd5584e9e6feea62567b6e6a3dcfdde115d2f77c4ceb48d46ad6821027de4b2c74a39ef38d27a4bdb68d8889608e0af34f6dc6318464e33f7382c7fda5a8fcab55dce43b40e81dedfdfef8e14d3d9e3ac382cd3e84ed744d7cb1db1d9f01a2d43e0f5bdd6fbbdbe5f7e69b355e9c566d3bb5e1a15a35508fc3f7fdfbfff5d775979c5118fa01502b8c379bddff7cacc6b94578b1018250db78cdade1cede7b986a035080c29aefd46bab3fd3f87140410ef29f197b2c77f69cd5c10a8f9fc9f735975973f8c52ac4c8958965a8140eef3b7dd86b6561beaa8b50081377bed23cedd623eb7ef7fc0a963c515cfe9a3f5f44a9b2ddf2495b8022d3f609d3e57bab3b4de662f2d99945290d60720ad3f571af19e575beea3cd864bd0f2000cb7cf5f86724a9da394be0fe863e7beea68b5b53453798b68f1017bddf687d9729c69973aee01bf943e638ea5e661e82b8df226ab1fad0e40ee7fbf3dcf7ab9c5de76af1b8b72911607a09c72f23dc3dbb3a5dd66445a7a407cbf0de5f63cff2d71d631a7d1ca03fe1c3bf65a865277fb75d5c203caa979886dde32cb5be5d7da00bcb2ea9fb3e7d6c7bc6dd799e94c474b03d08691464e35a59b671b6a9b8dce0c406bc36c2dddfa66adf9c4bbb787d86c1968dd0143bc7fe8ab9d37ac5c87fb4a2d3b20d7594f6efda6f27e7ea93dd3c200ec3cb479e2f9bbfd35e7af5507f4bc7efbf9b4dd87d2d7f96d367d9376b42e00e58d91f650cbff3ffd7aeb9b9c70b4e880b1c65fa5fd1ad7305ef9e7b400dce1d5bbd6505b5f278f73c0dc6bb51267deb996786abd81961c305f2f630d2bafd3feaaa95605a0a4f4e68ee9c7d6ca8a2f05e0b6f96e1e31fe35cabfa9d604604867f531df186568433925d08a03766f3de69b5b9c6bd43ec301fdd561e837e71c7f4ef99ff30d483dcee1c4995b7f6ff6df4f9e64404b029057fb7b28779db4d31eb91b90ee18f59ff9d64df19e77ce3620b69def9b7d0d2bc754ebac81161b70ffb9a38ef2eaee69ee3572a0b5068c7b8615dbec43ac7df4540db8799ed9661d568b71bc92f2d18a008c35739a6db7fe675d6706a40501e879b8b7fffb6a8ca9af346ba501f5efb75fad678cf7ffabf568a101f3debdfe78f7b6d7fe4db5ce809fdeb8fbfcf87eeced9c3346a0f50048ad9c368621b753fb4ca7961990622baf0ebdb7946bec311ead32a09e5dc62c2ddefaf65a732cf062ae6bcf3ab459ff50760a0bcc745ad9fddf5bd7fbb3b693f50af4b44bbfbdcf73cefdb5cffa266b1060d6b84baeedb69ecf107720c0fd6b0f3be71a57dbff9d1364fd00ee19abb6defa4cafbdd215486be457daabffdef16bef0328e7d7dee61ace1cfa39652b5072bf2ba776ca6f37ad3d9355b9f5b27a006dcef5e21ee38c12dfbb7158ce6b598161482bdf37fc36521ec050664b35d5756b3c27adb736c95a05fa1beb9eb5ee1843fc73c46199ed95ac1d405dc31843ae75b6526e6ba90ea0bed65e3f7dffda72aa7d5505deecadd41c4f9d43fc25af3980f47fac6fe87bbd7af21dda6cf4e654e0ad3aea5072af310e5b8c03f83de7ddcf68779d366e7a03c837e7bbd6cbaff4d8ee8f0ac47fdeab6397b5cf3ee33d05f65047dee5fd9f6aadc36d0328b5d5dbe7987d98fdf5d814d86d9d14e75af1def2f60d28ab1458398d72f69f431a33c72129f0676b69b421bdd8cf99e528b0ceec43d9fba7547b3a290e4b318fd5c4fe4e8da73beb621cab89ddf23c214b14783fbe5946eb7bd69edf8ec3321458e5f55edb4ffb0cb39cf24f1628f07e1bfa8c439bedb6f9661c9634ef7a6935803d6f4e77bf93f7cefba59fc0ad6fce35728d3bd75c63ed09ec5b87d17aeaa9ded8cfad69006b9ff4f690e71d35e598d6da098c366f1ae2fda5f65c87d766ebd9aabec9dee7f1b6ad1764cd00521b6b8e3df21f6e2bf1b6d9a2bcc9cc41960ca08dd6fa7b75ee546e9fa5bec99c278b1378f5ecbff79d33cf387b4c690ca0a777865dcfcc31f621f637a9e926307f2c35c619f7f97b78a3cd46e5646902add6fa66f9a3e59aff9b6db64c56e5e6252b13b87da8e3dc3e869fd73939a53080a1f4517f4971bc1a84ac17c0d9299d565f1fe658431979365b9437490565b900ee5f431f4e5db9e7587a2b5656448c89c664556eb92c4c208f9fda2e31f7da52796d0be0ec78d73f6dd73ebc73629b2dca9b8c727e907509d421ef9ccb5ec3e92996210e8b355182d6a7d86cf3ee7a69344b160ba0967fd7c9338edc6e4df90a20f795621efa3e7ba873f5382c713db32f154b4da525705eaeb3b6b48719cbe9ff59ff5e6e5ada2cc9aadc8864550271edf5534f29d794c09ba9a69e471bfeeec3986b5501c4b462cd23be21f6185f4c6f924e02fb8f14638c7bbfbe57fdcfceb6589912319b6d7ab1d9a4bcc9e965ca3d599240bd270e710e37ef3494ba9e8e047eebf9bf35fead79d421c561a9257743eb60e24cb393accacd481624d0e7dc75fc33865de71e670a60583faf21def65789778e8f403b25e7d7461fd2a8a7b6369bcd966f32eda27530794e9623704ebb2ff53a879dd77bb7cd76bdd86cfa26af977927ab11c837aff1df89e54501dc5186bcff4c7f9fd65a5f667199ac13403c3faf33621ce61a7fb8f14d8ec96204d21fe6905f7da5945277dd29cb04b047cd33cf36e6d87ff8afcd56e54d9eca5a04e2792f96f6ff1f739c7e36712226446c4c2c88581325552dab0490f63c3f9753ce1e6ff75211f83dc578e22879d5b1d698080cf58c7b5bf96fdcdbe3ef8fd86c369bbe45c9d393acca2d6b50160920fef54bea77c41aff58250e4b9e5996bc33b39e9a8fac11405ce9cddc6a3f77bc5ff728b334c842f469ed3ff47d474e239697b244002dedf87bdb7bf6d87b3fabe59da5a199d943e0f63c779ba9beb8d638a921d0eb70f71c3bb631737cfbf27357e27abb49c5f5765a5deb4d215985408f6fa677c75b67d4f8731c96ba30a7c5ca8a88a5ad95d96c3739e54de617b2420043bab795bdee7f75aef9da6c379b2d4a37598440cd630ebfec7fef7c35d6544cd620905f7d6f97ded71bf6782f0860d6fac7faedd5767bbdb52030f49fc769b7e431cb2bc34020ef78c659abbf3863aeb3cc640102ffa776df8eebafd9729b6798ac3f6038edcdd2cfaf73d73b679e1f30cb3dbb953e6b7d3b0eeb0720efe1be955e2ce5cf33bc1e80b3661ef25af5e5f35afb7158cab5ac3ee0ef566f193996dd8732731c96b4812c3e20b63d5e6a4369679869ff382c230593b5070c6d8cb9cbf0dfcc7dd41887a59cbaac0ec01946abb3ad974e6a2d0720f5dedbf9f7a5da5f6b270e4b3b79597ac04eabee7b87b3eb1cb79f382c7b3a5d1e5066bd2fff75e25df7cd1587653ecbc203ca3f779ff1d2ac63d77ce2b0d468591b80f5f39a739474f3f87fae5a34006597bcc6ce43da738ef9f3ee96ac0cc05effe575ef9afb8d39e3bfe3e9705856592597ac3b20bdbfdbfa27adfc86e1cc76402e63b63e6e9dada797460cc05b39b793ff8e6dfdf88775c0ed71d75ae73e25f6315b5bc9aadc90645d00c6cb43ac2dcddbead0df5c248b0ed87faef1ef7e2ffe3f87d702b0caccbbf755d61af7967b0ed8398d1f6bdd6bb4ba6a8dc39266c901750ca39c3487de86b4f61087653621ab02507a1af1cd35dc3886fa4e6b49c8a200dcfaf63fc32923e7fce78cc3bbff717d8fd96c6311d75d2f6dcb55d60420dd31dc61d86bad5b661bb3e280e1a4f7e36f6d28659e75c2b173c6aeedaf5bd7ac3760b777df6d370ffd8cda4f09c048770c69f7b26e8ff58d5156c97203ca18ca4f71965a7fed71c76179ed3462b3899529b1d924def5d2b2da80734fdf7778a9d55fffaa71589e9ded5e7344a6cc620376c979c65647dfe90e2dcde9d0d0749dca6e76f6e49f716e4666b3d96cb74a7977596b4039c33933ed9a6b6d73fbacc9f73ab2cc3d26ab72a3596ac01dc63bc3cb71957bfa9971585eb1b22262365bffde66b3dd6e324290150138afcc3bfb2fe3f699f7be166a04c7db4670bc8d0345c88200f4da637d73975b6eb9b7c561898dd86cdcc9989811b131b12762654ac47cee73e95bc837db95d9d06ebfb7a724ab72cbd2939506acb2e7f863ff9ffbbb79c56169b3f5ef65ed27c1f1b6913131214396d86cdcdfc4ca9488dd7266b734ddcee32b77bd349aa542169aae33917506acf2ff8da7a59c6689678d334e49c9aadc70590f80b6fbcfb5e79a4a4c33ce382c6f3a9fb164990143ab338fe1977873cfefc56179659855069cd4676cab0d6ba5f8ea89c3f26bf17ff7d1ecd49101abec91f34f7b687bb6939feddfe3ddb89ca9712bc5ca8ae8c6b4e4ccf4fc5e97ab7f0cb827ae93de6db7ed3cf28ec3d20b975bb8ffd9ad4ab1b222519284fb2b1913eba92db1d9769fcb6ea385dfc9f73a3223363bad945a96c48072c72f43aabbcfd46235dead536973bab3319b2d8ad8985812b1322562365bda46b3d96cb6abc9aadcb2de38005eeb7f94dd5a2f732831c56179ebed74bc9b9dbd9d8e5785340076ef23e5df661a52ebf9350b03de1ff628b9c59a8795569c9fa0e976446c369bedef74174d2d67b6064659b3fe9a5e3a759e5e83f94676ff8b4e090360c069238f7bf64ce5fd9ecf7abf80da8716e31d7a8a73e4d4e3b0fcf27374f79fec7a4fa4c4bb1e8f6a2f60acd547dca5a49e7e2ff92df7cfb05c4bd3752be6ad9a589918b76a627a7e3530ebfc39beda779ce5ec9f1ae981d3fa5abfef94cb3aa9be1b478407ea18b1b69447bb778d3f04e38f88952911fb5a72db89cdf6b5e4f6ce66e3a5e92426ab72f33b10675c7fb8e3e578d3ff6f5aee7a3cdb05ac3986e1df7c731f6edebdd5380dec9cf7aee30ef3c69beb50ee7a3caa03c398fbde386f5ce30f7b60fe4d8b1afffe76a5585911b1ddc64d1ede6d5caf4f72601871c8bd949f6ea9fdc63d1e8806fedd7518390ea5d53f94f54ca7e395accacdc81988b19ed4738e379e5a668cc3b25eb5dbe7ed765a95950ba8f5c758633effceb46a9ca74453cbd1db1670cbfcadd5d163ebf1b43d9e99126d06da9e3ded3fc6ebb5ce37feb334b5b298000eac13eb4ff18fd176abafd502d27ab99efaf7f9edac928620d6daca30fb9aa7cdd2d20f5aeca5d4b55ecc79ae3434f36088abe661e7d44f297fc6bf97b67d10bf9bd86cf99255b969e9608865fe1b6f59398ddd3e9192b7fbb98716f6e0e0dd917add7dc573f21d5a1c9639eca9695eafe75d8f47e9067bd43fc63d79fd71f7bd7158de220d92059435668a6de7ffe249c38ac3f236e7d9d9ef55df40aa6795feeb1bc6196acb2f5995db1506c03e7b28399d5ae718edfe36dbd9d9eff19255b951c102ee18fe5f6fc51ff7fee59f40ae80f67e7b3ffde1bfd3765ee3152b72da40eca3ee7bc63ba7e633d4bb0a983f0e69f8a3f454e27d6f18339e9a589912319b4db7fb6172ba9ccd86a696c398acca6d0b15f06a8dadaed6cb3086796a5e103d650a9825d674767ea99737d659ab14f06f3fb9f73d6b9ee78f928813b12251c08ebba5527a3b7b0ff7af3a012860dc596ade7bf7f86e8c2b0e4bbfd3a8ad0cf49dc6308637c4f14b4eb9cffd2ae40928c35087d8efa837d73b8c382c271127a0fd1fe76a35ffd5d2f06cefb4fc9d36e5eef788e81e5a48f302a0ad7a76cefbedded6293f0ecb7ad56e3db4103701b7b51af34ee9c43c4b4d71583201bdec36ff3c3de696774e7158e6fed9ed4e6b8790813ebc7aca5de3bcde73bc71587ab3b55c109b6d2db7cb2e25ab720397dcbe94803ddc11dfeabdfed1e6687158fe9ed3e9df734c72446ee6a5e96812f0e32e75e5397bbffd9e53462420c5f3f73d338f35fe9b379d47c07b2f0dc36e33a53e7eebbb596ff778534a23208e9586774f5aa3acda863a1701a3b43647aa431d869e5b8cc3d2eb7810017dc7754fbc69d67663fbbd0e0467d83df55a867c6b9b6b947108e8ab9eb6632b7118770e330e4b3ac7403ef9ceded73a75d83db5534a2160f82de69ac61ae7cc315e1c96364710b0df9e6dd86bc69bc6905f1c9669ba9d8f1a089867b8a3f7b1d31cf61f3f0ecbb33434b39bffbb8de365891500719d389495feb8adce71ea7a7326f0039c12d76cf38f9ef750da8cc3b2b7d3f14d800f70fab0734aefbc9ddab8258e87e5ed0156bd43bb31f6f1d66be7c661f973bab37ecb7d2fffa643d3edd2e4b464556e3c1200436c67f69efaaf436e7dc56199ff4e37b6db51f8acf29255b9611ee0ed337b9ee7e7fbea4b6b18ea6e3db3b3b47b42d0017be49ef68ca38fd76e5ae905212c0065ec97db1cb5ccb45f1dabb420c41cd0d6d06bcd77bf3a7bde6d083960f87996f2733aa5fd32f6101580d3c65d7bfcbc575a299514806197d7ebdebdeeb5df9bf104600c3da53d8619dfcce5c721e280327a9e379d9afbaa779e70c0c8b38ed2f39eabd4364b2aa79c106fc088fdbd7c8756f26c77a4212400398f5852ddc35babf5219a1a8fabf1b095bc2521dc803bfb70cf6cfbb679d22ba36c03ce6e37d697c71b62dd2ba541846003ca8d69a5fde3ffad8e91af01799d79e249f1e53bac3e875003e6c863973bf37df38f38cc858800cc39e72969d41b4300661fcea9c3ca634f036a5d39af38ff79edcf94864003ca4b7d8c61b8a5c5da5aaf25c419d06acb69ae1573bff395943609f100a8270ffbb53e8cb7cf2b3b3503d2db431f6bc5d16f1b5e39439401eda574d61dea70ceeacfd4d06ef589cd66b3a1e98a7577e454562db795b040faad0cefefb76f8f3baeaf404eb7bf78721ef23f2fa75d4180f46a4ff796d8e2197f8c4080be7a2bb1d5fbeb9d79f63f80f877abb9949fde6ce7b4bdafe5f67e114a5dae408e37eedcfb4bfff67efef01665970f608f52fb3bc330631cce1de1f1ba5a81fb471c711875bc95c630c46159a54d4bce4c6c6cb7446c4c4cac4c49daf6fbf6aaab07107349fd0d6fa621bd7f7b1c965f6e3c33b4dbffe1ff44c4c6c4785eccf6ff44b4e4ccfe0ff1a52e5660d7d7ee8f7bcd57fb3933c575f100529e6bf69367cfb7ad7dfa9c9a6e775be3b74a7b663c4f79e35559a56b15586ff775e2edeff7da4eb9037833c576e78f7f58b3febb76e900fe6c6f0eedbd32da7de9c46199b677526c3609ba54813c4fff63a843e975e7b5de1cc0b8390e2faef863efb997351528f5ec185f59a9a61e5faf9ba10b07f0d2ab7b9661ff37ce783d0ecb49b3da581325624d94d4d97503e8e3c518672bebde52e38fc372d7e3e5cce8a454ebba5081325b4ebfcd7ee2194ab94f8154c659378e137f1ca9969cd999cf6d23bcff89d86c5fba6c006f28b595316fbeafb71b779902ef8d1f4f4df5cdf167cacb42ba4a81fd631e86d6c62ee9f4b99ff67769536652e09758d7cdfbbc92cb7da348ba4681dcdf3fbf9eb5d330dac8bb4481334a3ae9cebf723975de79ba42811d77d927bf59736ef1e5edb3080ad4735bba37a6918e89edcc76131eaff7793cdb5503183de6dc6fdfe34cebfa04c62bafb7ff7eff7b98e3f667666a9c4e5e972730f65f31f59cdb1e2d0f3526ab72e3d24503a8f1d7bf5aabe5ccdceadabb3a81fdffc83f8f5ce61863e5382c7197ae19409943d92bffe1a657ea9d2ebc51c943eb5370d825034823eff5fb886948f974710277e613eb9ff5c799ff6d7974c600f6ff3df57e775b6fbeb8cf4d60f6a1dfbb56dca797d46ea909a49ffe7a75de36da69a9cc7437b691ceccb1bc5d3080bb4e3eefd421f59263fd0238ebf5164b3c39bf9853ee023873cff6faeebdafbce3df8509dcfd723f7bfd78f3cd43cb40570b20ceb4865bc61f7e1c759e75ba2e8151478abfcd56867b461f65075d2c8031478c378f7fe6d0db1e5700a7ded5667ef9ef5bee79df74590233e7d6cf6cf1973b7fb999dce9aa044acea30eb30f23a51dc7c9a68b12b8c37e6fd4547fde3dc7714cac4b05f0f3febb8c156faaf58fe1241053fd79d86f8e7fff185649e0cfb94a8a67a8af8d32f248e0be7c764ce7c573cb3e272430fed0fb18e9a6f1e36a3b13b13225623d9dcec9c5a02b05505b5a7d945bca89b3f73d3a111b138b0b883551227665d723708632ce18b1ad78877b524760df5aeaedeba7f2eeee7f2330f2f0d770761f3df67a5f1440fd7388e5ec72cedd239d27803bc45887b1636bbbaf76ec16a7589912319b4d6c8c5e485d8cc0cd7518eae9efce36fc56e3b0a4f288964f0625c7c49653bbe574361b9ad998d8eef77465365ba664556e5b1f7499005ed97d0fa9a7bc6e1dc65af1742d027bffd2728a7fb7f37b4a71589600e6ab2dfe35ff6cbfd7d72b0239cf3e625bbdbfbe8717270273eff6f22c6dfe56fbd092006e69e9d691feb06e7a3f1e01e4586afff9b672eb1087181178c33f6db8f7b713c7a8250e4bafe59e2e11409ae3e4f3efad39ff12cb43e0ceda86b9d3f9a70db1c486c0d076e9fd9cf256fb3dc65d85401be6b9afd7f98696863d86006edfefdd52d27a23f63734dbfd2864556e665d844099abf561fff7475d33cf83c03def94da468d3508e0bc357f5f750d27ed7a7e41a09c34dbadc35de3de360c0381b9c7bf7ff731736de74daa750102a5deb8d66af1fd33fcb2e2b0fc5c76af52d0f507ec9e873d72c97db65fea8ac35d7ec0ee73b792535a65ef51e30fc0297fdfd4cbdd77fc3587382cfd9444ba3c006d987597358751febaabed03ea5afde5397a9c438ca7e703f2ffe9cd9256eaadf53dee01f10e6bf65387f986374fde0188639772dfdb3fa5a1b69e03904ba9a9bd52869aceeeaf163da0ad1163cdb59d3de21e2d95ae3c60b521bd55deda3fd7114f3ca0f5b4634c63ef36ebfa2f0ecbbf534b9b726ec0a9ec51cf3bfbbd1a809efb9fb9b5bcd7abbfd40a746500867d5eafb9cf51ca3d69bd03de18cedf75b775f7ccad1d1087a1cc547a2fbdbf5a875d188039ac1e7b1fe7c554f62e715886ba1b8eb78dd488bbea8096ceffff0f73bd3dec3e775d00e6e8b78f35c4ded6ddb7eda2036a8f2d9d196b5ac3f92dd6f9aca5cb02d0f25f437af3cc78734d7dd71cb0ebb0d6fdeb8db34b0ee8fb8d55fbdbe9a7b2d33a93888d89c93d5d15803fc432871a675929951d5300525c67ce5afa1f43bbfbeedfd2361aee9a00945ef7b0eb7ca7e4d9fb108775571cf0cfcdb9f4b6cb90fb1bf5955ebae0807e773f7be5f65b4eb5e4382cab1dd3f506a4f25fd9ed9f54863d873a8b50970420967ae6d0fff8e3be3ffe0a45d7cb1d21ab726bd2e506c47b86fbee906b4e7f9d1a87cfbada8095cb3879c436e790d3b131319bcd66dbfdf07b9bedef2eab2e3620b59bf2ee6bfc335fab23075d6b40fd79d6d5c6d0ea2e3560e414d71ef196745acde9ec8a00d4d2671efe8c73b5be5eaea40b0230cb306b9de9be7e726d2d98df5343cbf2d69506e4d3d7ae31b5b4eb2df1e64dd08506d461f6f27e9efbec7c769d01adffbae28f759c5d0f803ce2bab5ae51c6ecf1b4d96cbbdf33b3d97e373be389e93203da6b77dc72f7fd65d737a42174950169957bcb70eb1d23a5d71ee18a054a1b678ef9dab0c6c83bc66169f3172e5860cf7bfebfeb969c7e3f3bd72b305b7cb59e3a7f6d43196a1c96776c89d898149f53d309d185b923330890479df5a737b43b7fdac17c2b2d1060d84329c35b79c6ded35fb97e006558a70ebdd5547b3a3dcfba02adccfc4a6dfdb721ee36fb00d21d622bfddf1773e16a056e9cf3f5f36f1fde29f7cde76e3e6716563a86ab07708679e75b6d78ab8e916a5660886f951df7db2bedfa4a2a7900e3979a7bef7daeab406beb9575fa6f3b0e4bb40e2663ae1d40cd69b8bbae57da3c733867d401d412ff48a3e69deed96b9e697bc995691b2d4aaa0aa4fb87b66fcba5ed9edb9a03c8a7ddd45bcaffb7b8e74f538199fb5de9a7716ec9edac1307b0ff1de5ecdd57cbb59f5493e1ba01e458c69c31a65ed7aaebc561f9773bedefd2b444eb537eac5e26132e5460d4d58772635b63c736aca74019ff9cfcde9837fdb8471bc069abcebf664ef7f5b55b5320c5325a9db9b47bf3eba917ae5260b6b27ffba90d6d8de1d7379dcfa4404ec3edafe45ae398ffefc3350adc3b4e8bebfe91622e29c66189458135f788b3dff2cb3937effff0d6a516ae5060be54e39d6dfc3bb45b6e1c96bb9fcb7db52e663928b89e99149bcd66a3800b14e82f9f3af36eff0c77d5bc0690733f6db65fc6f9c3de3f0ee34b56e5c6b93e8176f210f36827df576f2b69f504f6daf58dbf5adbe3cf7b7aa101e451da18fb9cfcd2f0d32c88ab1358718ebae66f270ea35ddbf5d6766533805deba8c3aaeb8e3bc62ebf0b78b6cd7a6968199355b96de19201d494565d7b9491faee3de704e24d39b6d9f39e65b43fc600fef863e756eea97fa421de044ebe437d7fe6dfd24b2ffe5a348121ded4dbaaa595fae650670269d6f8d2f97518d27d430c03286728e9b63ffcd4e7fe290ecb5bb52f80b4ff383dbd99fbffeff52070b900521deb9f7577ac37e6d5e2b08c920b13287fe633efda238fbf52df02a871bc31fc31ef2c6fc7512ded12f837aed5efaa6bde948695059086fceadca30e2bdefccf15c0d9bd0d279661b82def35e3b0bc80cb12d835f755ceaa63ff73cf5b09acd1ee8ca9e65872510263ddde4b1ca3e49eeeab82a5767aad2fe6fecb49e0bc59f258b5de5cda904a0239fe33c4f286df62196716ae48e0ccba6b1e6adcfbe73d6a1c96bc9f19e08204e67fafbefb87f2ca8dfd4d018c6118659e3dfbfe08bc618cd35ffd7ff521b7d3117823bd37f43ef454869cee46a0e731a4d2fa89436ef70e690a5c28803f943d7a1f6a2f3ddf1be3b004a77cc2750258af8ed7e71eb5b7dcdf908b1118eeca75b4586b6ae7dc12e3e13201e458676fedb57c7beae55c04debe370ee3e5bafb3827beaded7a519600763fefcffe768a378f619d87b81481d8da8f3da691e6d05ab48bd3361a4d045acdb7b5947eeca70fd5f4d83fe3dbcbeef3e45ceb5a187091007eaf69f8fdb6725fa9b19e806b04b0efccf3fefbf24bbff6566a44a0a7a1ef93e2183b2ea79613e01201dcbfd61e2b8eb46f1c379a9c693bcab48d466f5c87c03094747bba7fd75ad63f71589e71b3aee6ffaed77f9acd36a9d49ac36508a47787fd861dc71feebe290ecbfc7bbba7b6e4e23043e12a04fa1be64b31f67cde505bcb1502a87db8c38f63f831b77df31c215086e10df3f43c4a1c961ad7e11a0466ad635839af9c7e9fa50401ccd4eb5d43ddefedd8dfaca53dc32508ec1d87f5631b5a8c75b63ff6b8028192e6bb63cf386359f34de76f182e4060a67d622f3fb579ea697d4fce3fa0debb63bcedff7e62ec3d0ecb28b1ceec96c6b364556e92cb0f78e3d4d3febbe39fbefb8dc392cacaf50158e5bcf66e29b90e3bc63f8b9515e9fd275c1e80b77f2cafec7de3acc3b0e3b0bce576ef03fa7875b8759e3d5f395c7cc01ccae9a7fda1bce1d631eed478b92fe5e4da03e63bf18cf852c9f9ace18f8bb83a00a5a73ce2f865c5764f4d75242e0e401ecab06bbf73dd7ac0886fee928621e634721fe6016bd6f3d71ebbff318638e2012bdd39ceb0d258a7e6b2ca465c1b803edcfa639bbbccd547293b7169005a4b7f28f1b7219fffeb9801883be5f25becfbd6d5464e51e0ba03de5bbfbf5756eb23be38d65989cb0e9871fc5a4ffc7788e3c53343e2c200d4347e2f73e53f679a6bb5d211571d30cfcab5bd38d689af0d2dc5c47501c8e7b4d8f63affb7d487616ecd5f4a07fc3d671df670f6d8abaf9fcb0290fe9a71be78e3ca3507dc21af55fa3eafa45a5ecf2507f4f3fad0e6ffbfce3b6ecd5501a82bb5218f38df6d73fc128765de715100fe29b7a6a1af79566dbbc76169a5055c1380b17a1ba7b4be67ee25f559b8e28078f6693bf65cc61c7948d1b8e08093db5c7196347e89a3bcb10dd71b506f4b7dd778e2b9fb0e773de392008c38dcbdee49bbb47a66303a21369bff6074fce21c971b7066cd37cd7f572fe3ff1c87fd919a65e06a03c68bebdd568713ffd077cdc506fc7a66b9e9cf7c77cdb75fe3b73973506c36af876b0d18c3a939fd7d56eaa30e310e4b6c854b0d88a3ad9ce328ebd55c622cff70450066ec678c12476c67b7d7e2b0cc99accaad012e08c04c75e87de4395f8eedbc5c6940fba58c595e7dedb5fa865c684069a7e6347389fda5f6c738b9ce80787feae7fe7846ad01d703a0f6fbf7acb3a6f4f33c7dad6fb8cc80b2c76ab1bce19d91e7b0522f18709501770fad951cdf5be9fef5521a0bb45e7e2d6fec1b537a2da5141658738c21afb67aa92bce7acead57208edfcec87938ebf4328300b9d43f7bfb7fac5d5e7e810067a5faea7cbbc5b97ea97f00f1dcffd67f71ed97d26a5d81da761f71dfb85fd9e9c43e80d7f2f9f1af31624c2ffd5badc0fff1f5d86a5f79b438c45b3d80da6adb37e6fb5b2f71dd94b20263cf21c7fa62ce6fb695f20062fbf3df5f7acee3c7f66ead023fddd5eb1a7a4ee9fc916bb0b503986bced44bfd6b6823b65a0770caac7d96596a8da9df7a4b15286f9fdcd2bea5f63fbc340730de7aa5ec59723ef3d63d15d8a3d53be75b71f8ffa694e200fa28e50db5bf7fcab9f1b7d96eb2ca382d6ddd00cebb3f8e52dac9afa77e6fa102710ee58e35f63df9e73d3c05d67df90d69ae3f7e297d9f6fb66c0079bd33ec998698873d46bec1962930bc955aaaefce3dca5b6929505e7cf9ac786eafc3106752a08c73536bb9f4726a2ff12870fe3879dcf1567eb5efdf6613057e8d79cd92eaf0ee7d750e054a8cff8f18c7f83fb67d0b14f8b1bf5dda1ce9dd7ffe70ab0650f69a318eb4ea1fb3ad590bd9fa04eadce996d257dd71e8a5c5b3e509e4155b7a7b9434ca90eebc450318b5a678d3fdb90d650efd934ea0f59cdf1abdf53bd0d60c20bd3fe4b5e2ab67fe3f6719c09bbdfc1d772c69a7dcef425b9cc0fa31be9d577d3bf661cd60becf8d5179935342da8a01ac98f2ca6dbc61edb1ff58d6fb4fda6c6d02fbaddfd6905a6e7da86b68a7a22d4de0a7fd875b5f9cf7dd714bd9c1562690475f2fcedcebfba7ef130610535de5c4dcef3ba5fe98cead17c05ff7d431777963bd164b17c0d8e7ccf252ca63e7e13cd361023ffe59ea8dffec3af21b5b0027ae5752fdbbfdddf7eb2f81587bce29bf92879f46eac1160b60cf98cf19766b759edc731c96f583ad15c0e875a45a6fdb71b6b9dadbecb365099c3646af656869bd3ec480b62a813bfcb7ce1effcdbcfa7eff6c5102b1dc3ea4db4e5af50f6ff68b1633de945b2a80b45abfe9ccd4526b433db726817dc72aeb0f7fa86f48c3b92509fcdad3bd339d1ecf4aebc461a91b6d4502e3b77d73fe6db53ceb1fe2b0bc92d11624f0d71a86b64b1ba7f456ce475b2980384b9f7795f9ef90e28b71586629565684f7055714b61e81f45f99e5d73be456fb786fbdaf05cbde2f22c1962330fb1a650c71e43e6aee7523b072eef9e4b7f22a5656444ccb1fb65000adfe725f8d31b7515b3e4f00f79c3ab4f74b1ce2f0162310cfabb9e43bd6fa6f9e7bcb047077eea50ff79d537e19caad4520bfdaf66fb1c671ab0450cb105f2c7bc696fa7b2dcedebea508e43ff67dedce75f76835c7e1bceba581d94a04ea1f63d4e1c514532faf4e5b2b5b0353c11609e0df736b9929f7a194b54f9bcd66b3d9a4c47ba7b631085b23809b6a9df997f97abcc3d86d73b6108177662fadf672778be7b422807577da77ad3ed7e9b70f0f81fa725a2dd6976f7ead5660cb10d8e9bc925f99f1f7564a9e36b25508c4755fea43dc7dfefefa8fa558599121b7ddb7356c8500dafff9bd58ff3d3ded7ce3b0bcf5ae142b5322f6bdf44aa71c138ba0581325629fb60881b3f6d0fbb873cd36fe2c8bd89d4e0f02759d39cc17ef304e4eff9d720b04104badc3da2de65976cda720b0523fedf651c61a330eb70281976e3dfffdbbce59a95443cb3db5253922760b1028bbdc31a4da531f8618cd0cc7b3798975eb0f88e7f43a721e72bf35bd1a87e5df4d69b6776570b6fc80d977bae7ae5d723fffd51f8074e24bbdcdf76ed9ff0ed77a369bcd76e9dcb3e50118f5fc21b5decffa3fd5b677e36a3c3cc5ca8a1ce903625fe5ccf747eec3cab1c46139256f8b0f48c39ef1ccfbcf6ff38d5b7bc030deacbdf4b65719ee98d5d23a00f1df7fe690ce5e359677e3f00dcf2d0e40eba5a77c77ee750c37f5552d2d7784b7531be3fdde9a104d56e5b6b6a507a4985a497bf77fe6cf79b6d9764cd6f69916b330a7eb37b40ec666cbe978574adaca035e3e65b6a1ddbc66faabdfc203c6beeddee10ce5ae5ce3106f0086da531eda1dd62eadd637a6d9d200c477d228ebfdf5f7c9b7d651ce560660def6d3adabccdd66deb7ee807ff23971f4b35babc39fb7ec809e66d9b3bcd1c61eefdfbadc1606a096d7ce195ebbe9ff31d6ad3a20df5f62cc7bc636a43d8c126c5d00e2bdbd9575d6ffb797986fd10135a531df4967f552eb1cb72c00abc5fffa958fa8a44f8a15692487510cc4400c025088f34a034002f312003028241a0e07e401a14cd9a60d140005526a668c382e138a23c1483492c3308a8220066218846120c420639473cc2a3dffe16c02a38512936881111b37b7fcb4f4dd213a88209f7390eef4ce1c19b7f3775e1c179d1d4e7f93d902bfa9a1d6b083ce98bdb95d5091a06d05503e067ab6269a3ee85cbdcc6cbafd9291d3c99ec99c45ee3175937e24bfb0ed6d27560c36233cd54484d38c551fd95e429196ad8474871e59397afe01493801d12514dff6fd96164cdefd8cc491b003526b3571ed877185d860322156fe295b1ea2fa7d1c11ac2caa0a3bd5e0785db3165fa78f413108b4676aaa6147792289a8d81e81d7fe866fc0fa887a8c876bd090066b5e68739169b7620dd933da5ce0efeaa5308599da000cc5f4a7cae00c00e303e7ae6cf1611379c1d38a08e7e9cf8f61de53f4b4f13b2b43fa32666577a816a7f3f091a40aecfc200e44811579a6fe241bd8f1d4a74156757ac87a176b506b1d173e3740935fa54873c4eb7d3c5e606722cd126e70311efd94bd1dcbd9ea1b094962023321a297865242d63c164d4df183cf8326f74cc277330cc301cc43a9671f17c3b4fe882cf5563fe85762be246ea26233186fd2c530191dd5a89bcb88d8894cd37f0c9776a8f5c3ca21ff5f674b0950ec13e41abfe86befc4857bb24d9cabd47a8c527ef7afcb39abb1abc7464f54f0437e72894d3779429ef8813cbb750eedb39d8c026717b28b15d5bb844a87001e3887c0f5a63e77b4bdbe8dcf343084661894aebed3be9e181491841136d5b878b846907201451880f2f84587ac84f5f3c960b3bce8c7263b2fc314a85cb60166ffe8ac6a2bb4e91ec43edfb00dc9056648f5fbb8fe44f44a0b1c7305cebb1c38da0e8418c0f18708599b3e807da807b208d1b3ac4ecf28a4df05de22a05a6bb840ede1ba7426624cc755f4093510a60124a5b1b9588ba32c11026d79c484db9304f41a8d36214b84e442ffb71a82b139f8722e0485639916a743efa89e3ef5511834f7d7bcadb01c61681e7866cad8d89ceb08449979793d1ae8e83aaff2a8f75308a94eb1335699f4de438131d4b71ad9701519d6c260ad63eb7be19fc7e570f81b8c00782cb4769487bbd6c8c8be5603fe6862f908c81c0c81f2588df58f98c59bf7425f52e0e9ac8619a929a70962ec7cc6a367eedf8cdd5dd023809ec2cd051d32d1e95d7036dd04a9edd1003da1437400e6426a985e0da96f12fc51d9e0d0540e5e74871839273d6285846b85be77091dae77a07db17c48db5245bd1e4817ef19f83250a111abdcd25e9938ea0d3ab8a1f0424c88da551916ca902a3195e2bf1eaf22f6da64dd66687002612fbf55a9c4564e73eeacb1c2309f499481d98bf05c3875f18ec10bd8376db736bddf39aaa4c99941495c53b53fea46039ee49a2068f85473dfc63001b5a20bfb9d7654ce8b7d0d42a00b0595819b107454f12ddcce4c3ed5875e880ba68473e9fa50eb8b845c0b5fe8c9c08112a5fc2c1f00698d0c7c92dac89bb7b781adda8c74320b7f0540994f6b1052cb0baf015c758d82951dba7787b87cb39aa5f3a27ba107bb258df7af9b20c8f3e3dc926387fc1fe5765b3668fd043409e6589b0a12217cb8a004336da411f9ae4cc4462ea4b992229e81e148e310b64835f75851ecbdb7ebe28dc79b2d5af63ff75681ec0ec8e14f702479558e4c68e245bee263aab638adbe9c863e77f0bdb60ad1cbc3a763e84fa1cd45a10988809b0c8c66ba251732021b0a044c3ea69164b3b8086fedfa36f2cd05b66e7cab32df983e81492164e36574731c1d4f65a54f1e61bbe7524fd6745a8af245a877abb50f5f946a68f7bc901899b986d9e029820f0f1000c212376908b0da2041d13faa928df0e56a350000f6c80b1f44ed89c1737c92b10086323541fa5bf32c3add154637e929761fe2147ba807912ac7a46d090e4356e8911aea3da0cb5150db2308e04b82f1507486388c21cc63eb2c2383b41ce70ac801b2940972066d045bac82e943e6ad460f9733ad8a0f7f935e04d06f358a6d5211865002e14543602f82871304ac14b0775e081802dc2e039ae980aaaa5b897c1640f987c058ddea6ae0a1418d20ad2906c1dfd17d87f85ddd8a6ca08451d30263ff42b1250363647e6122219da2b428f6251ef598efbfa06fc1532efa184b2b6076f708989e0da05da5828cf6d04558f0c46b36d3145031255ba7c18e5e2d5ecfa508f3d65ad6afc6d4c9957df15ed1d1c92d400bd9d082f684de8025be148ec2cd686effa489ea9ed60265a9cbd71a526441b20e7cd4226006ea05f53219a26c83d3d7d6a460f7852d29d14c948058cda96cd67a10dc63faa6bd05babda1bb7de5c1cdb6168886d96d1588f0d8d1c0a9394842a4e3dffa0dfa8d733f08a7c985a024fa10652a0d64726bb9092142a58b2ce41b80be898ca9391acd5f520e2a018fa7264342ed6b23ed23c41a83aa887766fcac8fcfc40371b4728c3595cb7609d66c310f86e31146437366ddd81738c4c61faf4531a455aa7701fcab1cc25ac1e518759ca6487f6abfd0fc24379a87bf9be5d8613b382a823fe583a06777dd59f28e8d6077b627330881ceaeadbc0e56554e9c0be77ae18cb3f888e0c38526f237c623ef3f68efd22ffe4f58b0134b49969acee5ed3160a22f251357e47886b2e5fdd70d0836d9262c7b6a145575066d314bf4807562e0385043f5bae9e5a4a94b3bb3025ba682dd1916759a6bfff6c09dda3231b39a3c4a00016e650620aba2f667aa150902408cf2b77114e2591e89c755a4f4b15aca0edc2fb034ae011258ce0fd229ae8b5bff3ca5cdd240de9fb72d8c94fc4612b142882d8da827785a2aa63e386a364339fca8af3e9fd4b2978ac84218cf2ac6189ce81d734799e12c44d10c8633204c5cc7bb25af48323ab4b03bdc57cf27251a8a1c402581f7b4cdc852da25321a2dd23754cb348ead11bee35bd0b20b92669744c24a4880868a1d685a0b6ee55b826991fae5d2dcb44f647e3ba293ce198d88d9e8380519e9953b4a5dc44d30b60480466c4f151dcef1a1dd4649c43db26a0c28ca0b07aced17f153fa42634e3ddf58ce9229531b4beff5af26118818003469f5d92b0e27b2b9fb14d3cf70adf9db8a7a9d272be30e64dd9d82bc68c877a9920185bd00ea8c5b2dceaecd90e4d8f2c13567e1b09fcf61056db70553d8da10ee9b789bbcf88edec1dac22b34571e49223f74929670819382be785c2dd8db1f2706f92a1239d0d8a934bb08c4b580544248583ad8222fd81c3cf55e5e6bed008d5a58901ae854f637039479dfef914b3f780ee3e3c9c59a03d6fa186247b017b9c44d6f52cb8dbe6a22b82b166673aadb562ebb647b2a6dbbcf4c47b400ccd6f1a4f29aeb7cf5e5ff5a43253613d9748b5882dce2c1cae146dbd5b995f418af0428d61d73b71a0747a5c818222d2d20cc959c23bb0d99cee78f71b1a5a0a5e294fec3ea9c245ec675d5fa8dc33fe842c462990f435f94ed1b650cf053d3c84bc1ee80a2665e038dfb4996e9983207b9c8cb910ad7916e0815b76c6b57954eb30b3aeca26bf5a8d07450c0abee0bb7f880a7b1db86734664c7951086be0d7705dad946eb445b97567e71f05ff825d9ca1089d492395a5fb6d2a5671004798b65b90111f0c0987aa6a785f301321df7e260e269aefe4addc6bb9baee778f5f2a03750f9cd1e74e96e7cfd074e10c9b925740d9166f3af521df56471e5b7975df248e80a7c5fd78630eb8e007b9fb140f04735c348564c73c566c10160c49d1652bd351e23c95b1aa43c40615f6c7a8b6eb840004985a8665ee498c4aa46005fb0ab200845c1869a66cc514cf1eb18462ef7683ffe1382b37873cec9388792f2089f7a472436184a2ce9fb8666c4638963955c7846e54e10af89526c526766743fb39597ccef2a7c8b06083bb6656f690bee466ef3f6862178ba5bc51990b442f820c405cadb1b7839db3c047a23ba9578cc9ed09bbfc84618a6cddc7234fc0d8b04a0c10e1ec36d31b907f5f2e1dd1f7089b3a46e4ebe8f4a0aef2ffa5e0419071eb6e3bbda0c28ba853ed26ad3aa3152059cf364776877a7afd3f63ead878861ed2c9b2d69cb209e691593f709a00230a48f0bf84a85e80537e45f58e8ba0b1461939110c06cdf4a5f0a1d6215720edb944ec2ce8eac6a0720ef7a49ed894ce89c17e05fa07dd0fd455ee5c236df04ed6178ba5d50afaa689ce8bb913eec006500b7eef5ccbd4c6dc65d5b3b28d643bb0d91865c19b50573cec1d9c63c6c23f364104f3467c57fa1c204d18859f4e4e8859dff78d6035b99c99f05bfe6e17d48e85304929c242657321a9c51103e71918e8b0d04bd61fc0421bda41024cf3c8904303a209cddb9f27cf2b7ac6fd7f576abfcdd9fb53f532a020c0b2ca846e1245564e3d3e10cb2a7a31c3f3b1c8b16d1cacf64400c93f2817797c167ff49e1be8cee3ab6492f23aedc0929f7d5c1887f6e1447b5ba7831818534b560beb429014fa765860f8a06bc9163f555f45e85f45fcd90d74c0b123ec807ea040d3f72ee540b401c298c60f738b41bcfbd2be507891b77282c67bd2f4b9b7e8b0645b6e99f194634c065c6e328deeceb5ea8f3c94e3c565d6dfacec5c392c221d9e1ecb84bed747aa35b0a30d7257a9c198934c3246bc5c1053b41ef6b59a2e6be6e8d61b891c8d70d553db2e3330801b8598475f2e2f35eee3e3a43b8576438b2417103973c6193e66242714874e59ba20d642aa76860cf29a4e6d32e7f06a4fe0281cd73993e0e64c5bcd4511309838c49979930ba0272410060d9d245ffffeca101276a940e7896829134c4fbb6659cace75877cf4e2055b6e5a7aca6e0cba536f403e4cc9818ccb8a7e2f1fddbe3a39da0a5176592ea581170041841b6e54f4f2785df9e553c0332ca25161d93de014621a7181d19898047d20d56899264922236a976e40b853c17ea9034da186311de0555e4c63869406c021565da68abfbbe68a93b09ed29984f4441b238a529cc859b523a41773ecc770a513a79b4c0845e0077f8f427d15c91ddee935bf5cd93316e014028caf6b625c6c3b33787b26e3e71ed57685a32e241208812b21859e643af149677f66c1846f00e9a112da1f3b1008981dafaecefd73bed40b95703022813ddfcf8faf325aa0d8950a14efe0a3083baeb2eea15a97beee548ecb8811d8383608a1bee9eb52f746574ac7e5939e2db87574ef2f8cee35459ed2ba4394efe979cf5710eb42e4ec5a79c6003231464ece0a102702e9da079a6a64874454ee64d1b2d5a644982d9109eec2e23bbb42861ba641fa80af82cc3f4f6e29d7f908a1f589b01a1dd17987ec25f88c814963e6a8599ad31f251789d8daad0da7978a83e7e195d95d54db67933ad375099ee503fe95a822e3c6ac5a512ae9566bd546caa38fdc3ff80d3df761df2a41bc39765212b361dbff2f9642aae57c10dcc5f90b472aaaacb86003c35577a5ce8e10eacc2b89795993fa80e7f3bbd922ead505772d1b0fb0e8d603f7079353280d4cc0864793782c18c6d2ab6482c2151f8985e54c034e718f8ceed1637ad0f3a216957dc710702ec86e163fcd04242b12b5b2bea484f810ab03d85964e814ffdc303961d017e318b6677d756de03a92fa808bc95c73bd6f94d29fd084b8b6e80074dc6d49d6107efedcf51e6c2392486d7baca615ad79485bbc78862d2b0371a7ffbe6fdb85eb8a1ef422fd602a6950f23161a37b7ed4a6637ff2a022da9dee355cbe5100d88324eafd13e1305c3c16609a4bc9390ae1026ba8f2e78ce4871ce5306deca2d21c58f9d9b3c199696e873b62c8479880304dc682bb07eebc23ff6707a7b32283c663495a224d6178d9e79dae81e807105bac7fcd3d24080d23cc229da73e0406c9e38c9198e77a2a6a3c7e1f6d9e105ca05787e617134e33a279ba1cf19088b2f76b619fae04bb8601c5892f4211bae01f393ed6f84d208d036b5518636ee14c6e4c34c1361a60344b46e1db54b49dd3d282f75484b730fb31cba78bc2b0e1c1b6e7d3f7aa9a87904b86903003a96ab88edd5423b574092cabb196ba42952e66dd6675549334b188bff621b0b69bde11eda9dae03ee9101fa12764af2e0ed64340403f8d899f0cb327f4209c666e238d4598c64b055665808aacb6dc1685e78e7c190f5a60b7a65c4c6c3f191eebdfeb97bfcd46d6fd6e356c36cd4b9f1f8acb75b10a5fbc63c722b7767707ff419f2651704f71a2e35a05ece6c5b99bad615596045e692e3767983ca47c65cceca5b179b414fa8e91505c75b659648dbb8f84ac6db3371721ee9c7491d9d666fe265f6697c31bd89bdff5f4552abf2fcaef787e1258d1aa0b29931268f1048d58ebd416f984115bdee7d166c78034d8fedc71456bc4ab54877ef2b848b8921e8974980a9319821710f18333c61fbf75bc8eb1d7a4cd38a073aadcdfcb467e1562f0bc246acd9bcf8d38d77ce4b90214f0d175aee17c77911626c611061f675aa82f9d7615f35cb5c79f150aacf3e0d39b40ffb90f5a9cb9d7e537676f388c0dc7a7769a71563d49e3b4beb962ecbcdcdc57d13a9525e9138cf5e90d47f3ca40637ded34b770b3cd02600e8ddc12d265b5be7e88022707288769cc82638b4463cb272eb696d1c494349502deddbaac4b41defdf312c3f3e1241f98976c0c4799b9a1049ffe8415b90ec81c89ff8e79f4a0fa94e31b5b23c729b02d33718c880088d82c3882e5028eeb06e4251b987c241f15ffb7b031871455061c35028702982112308899bf6e5be7f01249a93b507564e156bfcca36b4a779596cc9e993556f47894e892d642b064a7257ec93fee3a7cf66461b381b9ae93fb17b91bc6bf4966c90b7ec58da6f07b9326973011003f61a36e69991d81e1f872dcef41dd3b99400e6018d92817727e6d304bbc055022e5b691f5aa5671bf04b838903e30501f1128415609cba134e1f39de92284a83348e30e6718c8ec27cb2fab661967a15d222fc43bd792c58e8ec2c8b2a108f811355f4efb89b902a7f26556ef7fdf6a37bee1d0b0955051b851ba0e60c1a3c73840c303a1d121bb49c4be716ffa753b93d0767d1edcd72e64e4741d656691d310ac0104114046e4338b8fcfe269040e706a54a5256b503dee830524ca9c0679a5cd2949e84ffbd1592018c2712def4680a36323ba9c81184c5a5fe23f6f74cec0ebf373e59e770584ee8a847b66735489e2c9ffa7a3f9eaa6976fcc5f38fe02a60dfaa35849578c4c21390f0120d54fe3d6bb1acad93582935a083a8038fe01976de91d31c684ff6e7980922e151dec55f02b8d46185ac471e79abc8d7e0f80ee7946669e12ebe7dca7762486660e5df6ca7d66be3768662e781f80065fdc0bf96fc65da3ebb3496eb810cd1129ec4de265003432848889f35aaa724dcb4d01d86699ab2ec61f1f9c210a00f6f149a91247d0cbea705e13ac8c9962c1d91ab229010d6a61a5674eea1103d53bffce840cb53530192a7080918369702058975089ae11158d629200637023365415105220bf691f25e1b84e290a440c376242e62850d59a48a8101448cbbf3ae210c255daa2351d671acd347f0a3fd89e816e06881eae5a7730570fabf620d0a3d14ec3588e358566e9a5f02bf15e06287167d30da2d97b08b45575c24012bc61e9cf33b4deb06423a8de2869f2ee2a9a56334ad033f9c1771cc7812cf0d85a737a46c7ca8c8b26ac3689d82eb556469f0b9906286788828607e06f9c026e8e39522ecaa5d8c6d9d0a56f4f39c75722b37c85e5e908b108c821d5f80224d3793fd9a5f376f7990e5e4ca10289a5a8166c746d41b0475595ffec9b988c23c90603da86ebd89b2a3ab4d283b5390889bfc1646b2670c6457977f2c76f4d6da1bb3b62ca4b238c19ab1d6feea093c34986ec6774d2a238586df2d3f1b7884dc676113b0dd36fc20e69582e52e1408fd367a14d1270fbf0a600f265b66f9cd00d236c8003f98baf710e0e988b4c23c5462e05f03a9269c72d10dc1239b295c517d2c826027c45633761ba2c5b21800564574cc826605a11ef069fa91ffa810756307a17a70658baf31948c551276d9584bc62c0b635958655d280bab2cecb298b524cdb2319685551a436958e561978fb504ccf231968055228612b0cac42e036ba99159fe3f96a755228612b0cac4be0c8ca0c5a21e2de9b1e5a9efc26ada3835e0a493e804ea7d00ce380374d44146a545416c9d138fa92f979851a97328cf8332a4093ed96c0404aa63ccb39890f5bc161f74906474bc0f571d587d150f087bfe79076842a2ca3c10c6bb3e6ff16062dabc8cffa55f02ac8c706afa21c16f74a6f36260b6653a218d03f3038eca5308c61c4fd4e35d60bf1450fa873af13f61d491bf0236169a8306dfa2ff16ad199aead1d7cb905b4835a1259e2f3117c7a930004e848a8cce908bc284d3c935463cc7ddbdc216250f5ccc8cc10b4064244975c90814c1a51ba1688e72b02c9354b10172dad15f1af12c481aebd2f422dfa3ddff9f344f9b50e3232f1cb5731d99fc3d514b0ee5e3d1236802537ed265ca0c5094f31015768243ade16a2f498c687c5becc1a0105f3e240ed0d2f12103b40e95bea863c6bb6057b2bffe7e6131214c552ffb3290a45e0b8fc94539e1034dc00ceb7ba321f382d091818508cc585ab7b96a17af0179c70d05793607ba4c31e0966bdcb03f2a910f8f1bbdc5b0c748dae6753da78c1bf9a5918ab2d9a52b83574cdf6f532801b4d9ad65b980fe674e74395f931644c8ca46e8fe6a5096e42a3d059b5cd3c3c03bc35c9ea1233592fdccc9f691bb2ee384caf4b635acfa9d872c79dc1967130baf5fd281103f35aa8dfb51aedf1134e126ca0c5d8a17d445ed9e26a75d96f6eb5a39e83919395b9567323a1f7e76e9f6d8407ca8abdd159611d0a579601104969bb09695d94eb818349812261ec83dee22618b27dc2b24c577c7cc7bd296dca1bed0caaf0df309379f0cded6593c91a1c7de93f6dcd423bed1e6366406308b45241ace5352e17bdfcfb3874e28d56fdd778bb89e6c836a88fcb139869c474e4cc30e9d2e3f327446aa85207aec8568faadbc3d49e87c5af00a23d0cebe29797a3852d839ce6d8b36abfc19f2f3d29f0761483fee84f754913c9f19b926e5fe68bfbaa2bbd433578decca5d7e313882be153ef7e5d1207c62938f4f0e2d2435815bc0b529fea152f4ace4bfe69a872541ba7f8af01c829da975c84f8effa56de23401823396516c0bbf0a0a6240aca09f39d15951120a2b9538740bbf3fb3b513f472b8db612670a908de8d2a28313346c6462bf9c8b512d398ab892b766198c42d8b5b620043fea6be9f4c12163b36e1f6ce8953079671287ea1e1187b6b49ee2b8763670ea6f0e581e04e8807c6b8fec21ec815b36558f5969a7cbc5d6f0a23a126e71b6ffcbf7a629647403e60ed84be1a10c327c0f28bfa9b0104643f1e492c27019b39a871184a34949390c544089374fdacd85f8ab83e65874fdaa80eb0121ab4a0a8166eb8391b2e5f9285a7403c4601dd9a0d1d3bd96c0bc47e91429cfdc14cea869ee94fed721179ac5ad97b0b5c8aeef3aeb1b4b18e5cf7d508b41de7a3d27a28feeca5a5c7d3992fdfaaa7140a192e1abe991483e7e7294bf0bec6e5cf4c58223c084fa2797035e2fef4993cba50e85149ab20c8da0951a490b99441886b94be632bc692588583e11e33d22b1a7e97320b31e0e9c879f169909f1d6897ce88fa1701d685cf43e8eb02273e8126570713024fa8c8ce49e37a613f1a255284aa20c99fc21a2cc5a988f587f1c9788185b3156646cf64b1e3593168d844ddc61a4cfe12bfb3df50dfbe38df24726520f2088dc552439a8a1016230c0c92494406e765e8ab71a158c935a19fe7bc8292bcc93150f94c149986c5992c527ab72899a505cd1ffe2e273d14a27ebda1109ee7876bd1c416b816f8ce84da1cb42d4253c788e5edf29bcc877b639231180020890caf6df89649ee6eba07c0bd06911f5d3e822072fbe0a0c6535daf261c39472fc018ee93835456e561960d08092da4ff615a3735c73aec3161df5709317e4cf5ca88a9ef73b03a178008fa6f84df20459429f0319801650c1fd9f053d728b4f97a82230ac6ba40b1ba0998519d58aad5643f473ce5139872b678c0b3243630388aaba092a5d32dd46802e9112b4a4da7531b024f95410302e74a4447cfe57549786216ce728cbb0b5697375b61a17c62be51604a3057cae9e00d9eb2c1d82686af921a2e919ca29eae967dce0c26c13c3ffa870321d3ce88341d223c7a77ffaae98430f10d4d05cf40146aa3a3e8dd90d0cf987aacb8558b7849cd5438a1dc8819d681b457d4c73bf24ae4458e2d4b3c702cbc072d4b9a5a910e24c372dc72afc82eed6d0625cf327dc4e69a19af265faced334d0d331e6201bc59d57edb65b460058b7161d9e1f1d7c5e3901a9472911fb1ec878a1b10102e5a9419caf06dc92854274a6613ca89ba0c72653ab0cc7bc3c03764692f2aba2cd868ea7c1f275f99a7459b68cfd9e88cc084beea80238840e7f6747a592fbc1679ab17e9d36b8736b1fa0f63b71cb69511a7d13c5edf935f475dd7f40b45064646d21a5ca5b1975965c5ca15cd0dcaf8f33cbb8d6d543afd0d27316e511401b031c4e6a7ad01a2172dd6c5aea1a8d08f684b93991361b7d1280cb43e05a2d53f8d21538fe6e62f4acc525410941168d222bdb0bd0c7c31391bcb1d55d6a7717d6f4b24a3ec3bb06efb16a1aa12ba7597a4011b98cfbc1b91c00e014f5c03ecfaa121f32804018d3a013eb11679f61dd1f18444134c00cbee7bb48f6cfae46984988257653b2a06a03f447941fa94498abd53a867092c09bacd3521654b2b22cdbc0a41b0bea4aa8dd0005894c769a290cdb97c3a1d107e8c92e76a9e01d982786d7e47bc20e6b5153ba34ce007e972e5de2f767323fd6207ff86cf7374ab21c39611d5d0bb9c8e9ae360760b93e3f948248d283dab2cf88840e49f9f23fb803a02693a339e61626e6c941538ee5ba999976e49ec1ccb829e7d04746c4d0ded2805e90a3ceaa49653384fd515f1a9ce56cfa8f24e6720e91082c25370fb67caab845208b48a3aeda3532a583f1133a579c14c32092f3cca002fa8b2204feb39d604ecb6bb9f0f2b55f7f700e0717c675117ef563b2fc59e906bb24a5c2cd5d9d219f47e86fd173d1715296f442fdc82bd50dfc4b216e4484049d942166850dbda4b92b094ce614c8ecff0a27940467d58709946b45627c704a155a3c54098e4101849602a46099b46074226b3d74e29e53f3fe46e598da78f3accf9a094dab71248d13a3d2b40214dff8a818f898205a12dfc84cd086353dae5397f2209f964af9829dc211da506cf842ec03f8bdb98e2db8400fe58ac69d3034b9ce2eddaa4b46eef2f8453208661d4aeb6f75ddb6b6001323db9f2d5df86161794d432574f097d438bf8e8985306683c38aedfb4bca61305df2683b29694c81193ac5321295d1e4e2970d496253867a7ec8e162f1bc186c5305bd40605ba84ca55d2fd4681374739ec0dd2f11746b5067940fa0fe24e7791c622eb2c5e5751345ceb48e10e200776eefac39d20fa7608db664916b30ccc93b9dabe4c73436dae147aa3efd6d4a1b39acf81c8480ac3a5c96f09d733baa25a8f89d649b878346c102efc3337ba9b443088d9a7d2c15f5b4de35ec00e3fa21c7d5a1c0a1f5b4b331a2a49124903f483dce3053ee148f56c98f850186b1c563cf1b5398a4a2d9540fba19a972e228b6f193916a930f951cb5c2c7ab1cb622e08fe7e3c531f578c0f01314c360d1825734785ccd85a7ca9780153e724038f1449c3c412f8150a315ca626f15fc6d5ba822232afc91a3511d58031dbf73462e8a25568a3401b8ba26ada693e55eae3129d5b0ef3b64cf06c2087f77cb7054620997de2a1e8dc7d0e34c0b5104c5ae65099e4ae34340c731aeb3f880449b1b4361d3fd362aab6026b2b7b6aceb2bad6260ad117981e9b2cae3b45b712c260ea79038d5574ab97833bc8a63bf4725709cc11cd5be50b0771b23c567a57ad6109dff0ec7fc2e80b622935b107a9e6e1112bc1c135f9dd6e244390c74c184956f8ef553947597687c8d1114c217fbe902d3a4bddd90a3103be704fb22ba1d6893436056466e881cbe3aef27a1aa1f1633cfb272a995f2c19ea48cea75e9b7e4b2489764c63835c3d1ceaeb456ca80db9bdda1dc5711e7f13c5d81fed24eca8b753d0943990dc52a8fb8c3476847d9a57184846eebd7953ed55ea4f4247da120f6c1dd13bffef9db03238fb414cf5e95daa78850af815642aa690cb1a6f007cd35903209d6cbee03ff6e7d37bc5a08e29a48fe3f2286722dc733bc202dfff58c3a5c88bc255f482f17f2e4b20bb8211972afc5b9ffd6cb6439232269d1adeaa63c225b7271735913dcca223b3e22f95426ee3415ffddf5cccd7cc04edb023964818fd72f5c43ac1998e367cc9e130fbf3e9d6450926798f2b5c5a46eea032eac3725f8168182fccd348164b86ef8088a94c905cf2661b97204d8e3493052021837609a573947647635e5aa08383921cc86842061431087c780d23d33865e33a1e95635396c508c738ed88c43dc85c5d4dbd4716b3be4934370965a123d82a844a8c7571b9789f9d642006a90b057f9020e4f8a17971ef75571c7f6a0c48980b120a867cda949ec3683881c69f630f70b6a9e7b55ae856e6c9f5f5f7938391a2cb142051f134eb7e3265998ac5fb2ea1f9140b9987ca0346b984a4e6150f3538637475888e3fca21278224c52a539923cce79fde6d784309b7d3561b7e1d5ce806750b3d3f616a7ca8898966ac2f1068229fc217aea5dabe425a704d68b8e85dfff9a2ad635e8c719f4653ac21f7bae570d458256a25e269af6227b5f9ffe85a29926252402926aef3efbdac347fdcf417c754d2c370253a54d80c57506400d4b51e16fffcfd7f1063b728d7422bb920845069f8bfa5a7d98a3adc282c0e6bee5f758adc3c0530cf13b0ff3981fcec703718b25766d34c98f4e3957072b0bc116246e6a584e3a4f0cd1a930257c5bcb72fd947072a352eceeddeb32677f0b6e0264cadf9760d93eece14243e13a9e81bb4726349436bd8fb2acf97c994a9e660bc67cb51ef163ce8ab99dcc0342947775070f02045538e14fbe4eee8153497f6ddf05aa77a8ef0e6ac16e2cf14037fb24be42b8432f880a70d7e5bc7a15f960e71d801b0846339130b2aa8aab014130f0448e80f1d242e71ce402db03494d8f51d5b37117c372683733f92a394176d5548229935e3a4edbca23e182d3cfe5fd258d70c3cebc9b2f53fd27d8b823b4a9e312ba86cc62e922472c754626363dfc49d60dd85392d3147d2a9e74cdd770d884e95cb899b1354e48f7354d8d90576cb7cb94034d15052fed2eb5fa619a194a659b9bde384246d01144418158b6c9bf472609c998ea378c73322e0642217b7d6899c8967535a4a9c9e1ea73e89d0de076d9c743fc665bf44273284590553b80666ad03ba4b7f059aacd8687b7b7155844632e1a428d38cc549002ea921d95da331551530141707274db19147f982dad05ac850e6e8624e7eb8349c972d55d5054b8c863c0795485397e9ad275248caaf77326412e78d3515f83b51ac41f0045e855a29d46cda2bb4f3d106a51aa059d74e2f9c480e78ce8df2d57e6dab011de98123c06e6a6e0a74d2ab911efe1a18c5a32cdf54ad3476369843907c7ecf9eea07b92a5df4bd27fbb2cfffd8f24091fb0fe30d68271894572c5153e3fd171dae6f28cfd84c1d18bfff498b658e5927317d20ea512f33de744fbd8faf2c16034eb712b30ffadef6be25114cd3701c40fa10fc70222c074d63039c0eec34266d8be8eb6f21a2c719b6b1da692638b0f1b767ff8db1fccf4b81ad8f46a8efb0686debfbd48905a6db5d006dadf02d13e33521832088f01c39534ff5f942d64f000e87a9b614fd0831c36cc0423b67a5334a592cd63072f1bb7fd8e4267c447cdc2e32e5bc0877755ed4d690c8baeb9861c8693b3d2fb67b91f72580fe0da0b7cafe9e45eb5ee83e0883b93170d814ecf5215a60242a6295a42579e03864ffdbbf8649c34c90d1b641af4018225b211b4f6aeb4cbdd6f94e7b5d13945f567e6c162feedbb51836015fae42ee936f6086ce81b07f3f0da1b1ed5d9ae89faeb3c7633b147e975bfc26c93ea34d9d1e05a2a0f14aad9f3192d7c4685e697688a48e9667b699034bb75b84d5ecf978f8f9cc110aada8c3383fe1bceee66e9960c080c2939057ddf1abe3ff9fe7f2bc968dad3b3f30e89e8970d77d6817b675e3ec6233dc30cb3320cfe1f818a84f229546f821fce4df7b7ef36f395e6a0a93e4648ff7deb0e45c9f3ed21762ef619d345ef50bd5c3538cbe3a6863d84fdf2e7fb3c25c554feddac729ad41d37ae865c96bf38e53e8b2496b97da637cd9e4c16b7ebf6901f45a40398fad2ec0bae855ffbdab63e6cf7bb05383b4be2ce3fd15f33b7f4dac25ce57d5d7f743623f368ea06f1211bd70ff6857256b6f00cc584c7ae32bc3fbabfc73fa36f7383caf05fef353848ba6f68b6c0ba3a3e618e346461a3718ba1ffec8ce4358b57f6158493f6070cba20dbf9e47558f5c3cc174b6826b89415636f014505ce1d6e8eb23f2fcc31bb2d8fb8bb4bbe86d15c7df6b230cf1aa386463a881c8bcc6f6c574186f8731f681abb6c8020691a8bbbd19d503d953c7b3aa9b81db9b3387f1c24528ef7a7ac57ceb6c7a70890f98c16a5212aaf6eea33342cb6a338992cc895bdfec39b6c8e930f3e8a55a35edce7f0e06fb7dbb58fd98fbcd598cd6ac56107daf3a9b79f8f0863f78e1f536bf0d74d45ac6fe63d22e0f0ae014cab35fa686eebaec89f4f899db2d0cef9e195818c7ccbc8a261af4c54eb5131b0ec99d9a1a61ef95c8ec62410e6a7b62b460ad23ad7b88a762c6fec0dc780895120b6f8ba1a1ca6a7ae788e0372ac43346b0b0d4f029ed51da496c5997131fffdb797a2affe72b9f81579de8f103e87c842ce1f743f8a60293fe8450882a49468d43179ee743262b7f063e519dcab6476f462b6e21364f657c5dad9dd2b7763801ddeabfba2bde9ce49b5b4bb60b5a70fcace3f3c4070d35b67eec987f10ea840943eeafe730c1f37e7c88ac550612a254501d60b5ffb45b76d18f72eeebe531cc23a5559eecd81e72cbbb4a37f65c9aa08222097d15cd0e8217879e346cd854d886034eae890b380d1abb30ed2100d5da5747bf8ff1fcf19d5f14067afcf8f11d76d6e5c20b09986952a98185456f3d1d9dccf66f69a33bdf80bd2a1c2edeae4e1ff5e03b56588166a62959dd981f497f3f1c8199b33c75bc3328db17d13b1efd26905bb6ce4a4323387f058b7ecad4350c7d2a8b3b8715f6a3c4965846513e2b9b17602dd9ea6ea116d767f005290d573a22868347392a1b3dfc599346d95e60f675ef5572b4fde41d08fb4ede1f7975625d8174f4f23b7e02b376e18b581af050f3f5e78f06bbc1c275e9ce96756d4b03de903eb5bffdba1a92c6c7e403393bc26fc6f5b49fbb876030fe4bb6f93d1fcdc92e85e9bf4009661bee55566ebbd451fe0d95728cfac40f3a2ce435e1996fe7de6d240e8f14e141a7bbd39bd63e3038f023bc6e048431c32f99b75e24ac78d0735b7fb36df78b9c6b6d1d3f4e9cdf6cd31b87d0e7749c7024d72a6d3c9a442136302541dc7688a3a1a0e21c68c36d7eb2931433473bfbc475fdc07a1214069c5b2e0348195d8200e699332cc683e2b3fffafffcb1945361001cd19121737974da75f03ed6b58f5bfd5c8fd3787640f1998209543a0a6eee3cdf98bb85277f55dc5d2d1b90df6f15438cf7a8d08ff926f7f60f4393b14e5abe876d6f9ee93edce1e6aa22f77a2f7cc384978de66f99c607dd2a3f6aefa925257fe9d53c9614f30fc961d22da1837f533719a2b75fb9544f116f2a403890806920c305e406f09b055ffab9406972938e5412bff57aa737f4b7ec41f6b7aa0935ac7e235ebebde1e1f7229866bcd27dea68a9901db299807a2e3de2b560485d1c19b69325abd7c959a281d25546ec51a295d03a2fb15376312805e14e3e69b88d935d51319608c1ef3654ee89a682d6abebcfa7171a1a8078e41d04486265610a8534c5989895615d7c097765c064fe6653d5419a100e9894e8db5d91aca0c8686bb7752811128b64a58c83e740c543577e4e0477afcd23c17e3f1c387b543180f88f9802bd603800bf13901bf668568c223b375ed10b6448a344c573a5d9844f1f0e4ded76a321d342d423b6fcc4a7e5a1b907bc4e69be1a62e35039f93ac27a1aa3c12c9b4b37d9f9219e7512ef1a2c65423aecb4e8e36dc2634019ad321ea1d5a2536dfdeb2a634d5ec8c96e3d2054e51e345d4fab2246ef4aa9a08477f55de1e7d1c47dc6891bf1c03efcbe355c74178efc48aba69cccf63ef960092b3131305f8940a9480d71d0aa8433af1cab3501337f5ac21cc36b42c28209f31e51664b40401631039108c79b9e4ffc1bb3218b1547351ff34cf10a41bd0d876d6cd0647ab32d6555014dcc97133f68b9c09d6b5db163d3215de79b30c00f6dda52790ada4986d615c5d08ebad636751bb27a4fde5084928fa493f5113a920230eb564b6f1b5569d7e4d89dc1969f1ec0db384db4f0c2e22a311c47e6d8ccc47f23bed1fc72b6962c987bb929b6c7fc1f686e57eab1bbef819fc6baec03353e022311acb6fd7ad55309b263d6711533381f735bead01a408ae15ef7eeac8e92b3d2140b00e49017d3707d4c85685c880455fd82f3b7a68a7a88554a4ef606ed17806929b196b46a7aa68545fd75b659c8b5f1195c3d8d26d5ab603e7fae431d52f5065054e642552930e7a226c1c9e8babbb23281b08d6b09ffff66acd7a25847eaa8a7cde6dc84a6cb2a4f6d9a743abe83847d20b8d50d2c8dc9552f529866102b03707c8778e31725ed060e59d1c35d0e9864f73d617ebbda8392a7a1466958e42e1bd7e39fae67984785e3070ebb3b0627fe3d50176ab66de3df0bcb1b7fc2ef776717b9b43c8c275fa5bdd6c3ee70d5819401f041122d3802e5f517e0dc508d9867573c434c839379623945d99d8e98dd1ea2ed140e247d5a7107f6f5c068671d4ffff927bb3429bc66dcbfb09496c1c348dc2201af1825652b8f02f21bfbed02ff83e543c006731c01ab5c084188e3dcc39a8020147c691c4014f03caa52642890471e617e931dd9970eb220e518d54b960bc6bb68905c4437c517b8504714790e95994a825069efafb383257000ee7dee22c8384a41d2263448d48a3b53d12009954bfb82e23562f84997e5b86e1e37c782f1fc2c6184456cb32946e23b0b70049b9a66e4ad16d8954c928e552dbbf1e4718da101d3b09ec6cff5d0a7ba755af063faa54b1dc91d6b3d7c33c09d3db6082b507650342332fb834e0da62b2f4eeebf83c30cf4a9d70a0a98b143ed8ee284ac29fa374f522e85ee763acf234f457154459e6ec831777e245f51e543be8204d385fe68cec205adc4b7bde05f3eee395111d1f952dc2d14ca813125bdb075be08b5022d6c4357d7ad53c79c92fc89da194bdfb9b608e04f92b8a18882d6521411f2ac05df013625b0129fdd560582e292b2399b5b5716c66e3cbb91bd638a3f73554209d4d1913f43aa678461011ca4d68103a2343ad797686d344651c8d74ed54a218d4e149fe029ffc93e6b7cc9388434abcb9680e69c582260eba2f30efc0f91313d8e266e4d21e937cf4049bd1aaf38f45298bf7cd6719e72e9005002598059671d9462226ac0781a62e88555a74594d2bb20c870634e672735bd3ad3423c8d9794e143962ffbf9678800f83d22634f412080d5e242caa23fc172ffb6968daf1e2256311c2e1740f54f78c14480a052699e9a5eef3178e4e138fd0431ab3c4d610d8072ba9be2642fa7217c646a5fc9b4385a1442eef8e1108bd23b9d9acca7ce4356212ab4d364abd344f7361f1769188e90567e37c117bc86383e4e82492c6ac7de1d677325fda13aef95596d38109cdad5835f3436282da5ff8bbbb2cbc51f9c211d5bbf5a4f4b3b0c2979d52d43b20723e8d824c952812a745451cd1407d6f169aec6dd8596ddae27629c144aacf5f55a33351708bdef8d7df80a20355f153534b2808a16eb4ee4623832e3cd6cb275a8d5634dca946a3091bf1e46944d98898d5a3a4285d0a9b732099e7c60e56b392beb3aa0e2a56a0bc9188eae6325474e02acdc564133a51c56c4a4bf1e716637a1cb8b2d0fe1766edbc1da2ec97681ede86b5758bb5aed6ad486956394fa9bd5103fea5c9d049f7cccfab3cd6949f7836bfc556e86660c8facc75e924c3240f56d75e827550e2ee2a7be33fe58e4b2d3fde938ee022fb359e6835623aec8931a52fcb230ec4f35a723d103bfd9bfb213e5246923a321fe3dc9393d6ee6622e5f287350cf06158ed560677b26931247afa823743634961f4fa224b6d01281ab068e7097c9c456367894e52ec521e24afb44f9ca89fd39288b2063347de2677a9c1beb94cc6375f4cff40ad7cbb1d1a8da8641ebade8dc9465986ccc788e3743992802a8689d72ebe5ca71f6469075ebfaf77f10799f6fac8dd066f0149b5b2b98a0b4d468aa30feada3c29248256db25d1ed365e38f2265e55cab8f0d25771d716e556b4fbe0749406d7ea40e833ec43dfe2055da712010530d231fcd787dd1d36beec7d3cae54b852c148643dfc3026bfc471fb233f82ff758287c13f7b45528fc0e31a86e75a8a47039c8b08aff7b57e6dbf7efce8831be6b30e82f9b38a78c8d4bfb93072dd02c4c84f7b3ac09dd869500fbf22663b394478fbbcee3094717ced42a5cdb066dc13df782cb0099b31245ab7c40119c593dd0ff01963d0468a02e4af2c6f72c6230769c2eaa2752682cc7b90434727af3ac39f4d36a2c7fe036d1f220c3df0d7b3deca253140dd8ed87f9dcf8c3b409327ae0d2ac6559989c9ecd22473cf8d5325cedc8fa19b1a304204733ed3aa72f9ea313def3b60861ee133a82ce69aef7684058d94f5681072d8a88d2806230e399b05942801d59020b205f34349e88bbdf36e2dd423ceeb03a37a5e6784c3bd41a2d45760c343fa56da7e994bfd8bfcb2b2f338876e726983cfe369cb0e9ce742e56b59711b34ac88ffdee61a8b2b5d7d391961b4165a6d3aeb5f2ce752a6dad46e034bcd7ce9a2967708196fdf80c44c65352ecad80ee5e38be6393d70f6126ec6382900c58e71d3b3cebea8ad55135a15fe180bf4246115e70023f34cef9c52a121a4e1b967f730382b04529a292b48d029c31764537a2894de1ea25a658185a2e3145b71c2f2db72d0546e7157c2b189cefb8fdf9f80dbd844ab50313c6b816c049d264cf5e27ba0f85b751fbc4205712c453299c512a04f0daa6c1bb6d00264aafbfe8d1b261d8fde09e3e57e93378deb9896316f20ae3c8743bc0e4be4a06adde5433eafe4d4e8c4c5aa0475d965f60b881c694c7c90c4a4f6bc4d904b519f3cb4e3d45213ca6fb052902f8a749cb7ac2c49c34757bda84307592eec9d37002552c85b24691025aa5562b53ecadd2a9d66bc40adb7f9a25f7ca353ae2553efdb2049530f051b12d0a19c36e4a56f44ece50bdd234614fd80696d2d63e75333be40d6402b76f4a158edb4ad7d5a50ecfc44eb26a37a514bc9e9267a89a07d190bdcd091f7bcad789f421116a9fb8f56fa602d858010dd34a20306470e73ac810216c590991580ae5a885e3288635aaa165e410487ab8b920f255110b4b222269a2748be27c54b1a92c1a3a5d04a230eea23272a23496a88d68114731a9e3e8f258687d34904042ac90eb2a917cd1c8d68b24865452483239249daca0509a492981492af7d14ac611cbf26a898b5c4a885ece2198dd2aa6e59209ae9ab9b3687252358b2d9bc8e8a6ac8473e02a67ed5b9ab18a67fc7926c3b0e1a7a9c6ea859fc7bc58fb6fd9bf0aef99b26709a3b813a214f00c3fa7f59c3f3ed32dd81cd3a573ec0e19379531f1bcdc976568fc768aa5bf1bf47c590ee34c6995a6cc542d4a8455e2a722220fa5ca82b261205365e88ccd2d47128063c127c4b140ee90cb54ef23ef444adc2889b57ed056e6cd125b4b6998a10b99c7cbd650053a877140ee88029d2549b03305b48d7c1d99639a1b4138e0deeeb3d32c1aafb964b2e3aee194a75fe13f6a1ff2b469bcfb4d7dc7522148cf289181fd5c45fed823d2fdf0fa3851cc03970779fbf9a2ef1b5adcd27ed08be0a1a5d35be2d9fbe078f4b151c487aa287f2b82799064c97c257883cdb701173c22e06e144060330eac2dcefded5412b0f948b4fb5bca60281f8caaf2c32248e5317b393a711cbed86c82e5b025eba4fc41b4aabcb3218eca5910ddd48360a7a22256312b6eb00f9a20138c77ec2b9147e6d2e80139913a024702806225e5db440af57ba9f6c773196e1c3825dccfa414e38cfbf90324dc02de8104dc0b3540b026c3013c0c3c0c3c0c3c0c3c88bdc5b781b593524aa5727a330c236f1c92324999524a4c3f8403511300ff71d1885a53870c02ad0b9f0b9a0b77a7ac511d0e61cd0ca646085d41f64983086131534b9ecfd353371c8c9dbc76976326d8772c1f183b4177c73c54fce960fac5faf3e97a3be376e4e48bcdf3d62f8c30151eff5eece43cfe6b96a94356ce8b4d8e6a1a352787d098ed624d7a16554cf6e9cea78b3d4f93758c653ac94c34176b5a6dc299883ea1ffc2c53a276797dd4427c9dcdc62f5ff8a5134e5c7a431b65834f96356ce42c60b5e2dd66b62cef1622c0b56315aac22339bb059a559ac2664c526e8daf0e744167bfeacf899a895339b138b3d878a990aa1b7191658ac1a462bfe966a885f5fb15cd4d5265ec9a91c992b365542fd9e1cf514634a2b16694a6d2c593061f9c28ab5639afadc4969b41222ae625d4dcf51461334e1496955ecd153b2f3109b34769e8aedcbe2e2c52842c59a77fc3229932fff82758a752e69e28df6a98b1d35c5f644d94c529ab44174dc0e11344ab146adb99c9d22cdb65362a0418a65fbbcf377c9f4dda48c62799228d3b39f74c54c45b1f69de84c21e3565e5128b67872c6943cabf4e51c28d60ec273aed155ff315a65a0f1894dffcaee8811a29ca4a1e189bd4b9c3273c2937eed47038d4eac355f4fb0af7e62661439b108331594fecb218d4d6c3a13ce6448cd34b1aae81b1dbbf3b4b52266624f2a6a507ea2497ac6c2c456326377a53c1decf4e604342eb1050f213da853b2c49a53654596f40a9a54322bb18fa694425c9c1d1537741adb93394933d198c4fe254a66cce1cc49a64243127b38dda4b2ca9d2fe74323129bd78f13a3a7aae817d280c45eca63e99c197399a4d378c466bac9e1ea093e4afca2e1886d53fcee7e82783ebda1d18835664493423ff1721384683062bf112563f3aa970e3a1a8bd854144dae9229ff3ce913040d45ac9ac3a76edc141e43a8093412b1d586e87977758ce2d140c4666719fb942668a54ccc21b6dffa8a953613a3c9d1107b58beecd1612fc47e33c26e3513a445274d83108ba75c0e1a54a7d304d920d60b993339c7bc729c29884d66568e99fa9e244526109bbabc31052fd549afa60188c552fa2c196d82ba98d2f8c31a32a24768f213763fc60f8b5f8eeef4cfb94949131d7a64d0e8c3a6b3e44fc598aa1c3e1a7cd8c62b6c4c57c1be0923099440630fcb6f92b6bb22e2d7370d3d6cd2474de8a4c6e3c392872d55dc6ea957562687f1b038313d5493e3d6e53ddd618dcb759b2913b6c31e3a85580a3daac396e2be92503217d413a5c35e96ac3aaa2693d9f41cf614fc9bf05d224a3f4139aca1bfa496c7e82b953f3dd088c362973906557aa3ad8a7058e46c0cbd27af39313d79c3661b4b85bfdf0d8b0a5b6b7147d67f4725251034dab0a74c7aefbb47f59cc686f5faacd66213538793352c2a534d36319467c6e60f34d4b01333b4c91f74ec344a4cc34e26ae7d12671a346c7a3449695f92d5189d6119212343f6dca638511a665844063b21c3a893bbb90c6bc8e8badb580e38e065ec608c1e5e4619a9c70a689061ebb1745268257981c618b62f0ff1d59d3b8812698861cbabb2f31c4f4fbe9a4618f6aecef15213946e12752168806139d53be7dd293765fc8535e7133e3f557a0cd5d1f0c29a615494c80a6ac3976874610d72b4831272610b573933e44917738cd2d8c222ae4c93f3d9a565728e070d2d2cb699f04f72623dc9367dd820d82103d40a1a59d88499fc27860aa7a2c43132c608467c68801634b0b0494f2a68e25cd2bcd14b0c34aeb07ed0a942c6b4d48d69852df3770e22944cc1495b15763249082f4d15a9b0e614d43f66a6e908d1cc078d29ec41992635b9f25f6aa22885e5c9774ac6106a1a51d84474f4338b3d11d280c2f6dda4ace8739733d7131621839a2777a9306f52683861954fafcb18f304bd6c60648c116820078d26ec2747a6a0474ffc5830879af750671d64a04003c984cdecc3eff87f8c9a66033496b093e2e3c81db5789265d480b18718268c169494d050c2da3173fd959c4c52ab96909050814612f6db0b229b7cdb24d10469907e1c13d040c29a54274d9aa03776fe3ac26a16936793774e576b1a46d892f224b6c7647710368d222c664a4fee06916a4a1f6478490f1e5a8c06117682cc23623b34060b4f7af028512ba787ffd09138680c61919f9ebc1e4f3ef9096a1a42b06346c2634630965ffb54f9399cf4a3eda0c78f1dfb6347e2a4827365a41a2030d65253b9522e7ec5709598a51d626cc087b9c48c5fac75dba7bcf2ece6635fec04f3542ae534eac5967e169a942f36f93f2887da998f335e2c32ffcd9346c720edd4bbd8f249ff176ae509964c39d44a78fca83166e8628bb994d0983975bdf31998918b2d3e991c2d1364d00c5c2c1a4698c6243a84af9920d871831c28601de6166bd25b27c70e8d196acaa1e683c7efd0a1016466666666e8a829a6f030c3167ba55c6f9e257b05336ab1868d9d4b8858ae8a195a6c17966a3b934e964ab11c5a52cccd2cd614460527139f7c39946e862cf61457f22ca5cd3b32931c6a322899118bd5f632399c389ed4cf120fc0625f0b6245e731afa4b1b2305a50e28319afd8746572ccf67767069d1c7a34601e23a081fbf091c26841490e66b86227c5a4bb47f4555c4eb7620ff3a9492c590829a763c5269774bc33f53b8f7912121292b2556c41564acac99d2baad8342d2615c79e98ceae54ec99e0a7f6a7a51ecc40c516ea2bd6734d12a59a1c6a778acd5474cd49b918a94e66986293edae78322a87cdc49462132b9a33991c9310a2723934083348b1934c3e83525b29939aa41f3e0a1c668c620f619684caa1ab3a947ef8c81e6f98218a55840a75caba83885285621d4bd3bc64d1fc330614eb28cd236e930c2a55555430e3138b26a5d839d9586f8e9978623d7d72df5e063995d4199dd853d253358f5d4ff9cba1a69c584f884b294e12d799f31c6a3336b137c1e4133c7526c80b639a628626d64bf5b3b0944919844e83f4e3acefd232b18d500d7e49d493827a87fb484cec7d9ab04d8a39313a3d0e0f665c628d29359309239a70f942e786199658ce4229fd1e15641c4d1a5a25b6f50f1d630eda3994520e35db210609c90e1e6c984189fdc7621373e67b12db9de5f39927679c7d5c982189fd9405f929569943ed6860462416dd26edcde9aecb3bcaa14646aa41ead1011292623320b1a7adcb34cac6848a4c0eb5121f3e9c6130e3118b7e32b99b1c74dad2a43d5330c3118b8ac9abf2362687dae4063e4adacd4e30a3115b89b81ab9b9d8102514cc60c45a39d9f7dde954524e39d4d046c6f0c00abc033cc6707f121212121f3f76a445ac1a9398add169c7e2998ad8c929be7212de441376222662d1a3d4a268d7c5cadd841988583f7e1e8b223e3de3109bb05a2d15847caf6f3e708621b68b37a33275250f9692430fcd60462136a54176ae3027572ca11c6a3e4a7aa4b1600621f618cdc332dfc65927a9e506b1f8a827869e0ee92164d6811982d8497eba6e6da4e9e4971e3fca9811882dcc26bf287e9952ef8058e3fe4ef5c79119af8986d182129ff1874df9dfe514e63f97b4197e583e660c4fb4bcfd840ff5614d96c9319726771ed394197c584b9dfa91b3a03b3d46c28c3dac6741d47c75509a498ca887c584f2cc3927280f9b2eed946541f8d9cf9978d892fecf27592608a13293197758f67a66c332a78f2934c30edb09ab642ac3354167139c518745a3a535273e298e74061db6149c94a17a44079df71cd664a973266ccea9bff3298745a8cffb2427e72757fe66c461276fda154b33369a244b14c30c382c6b420827e89cd3a720f486ed09b7e1351bae2b99d00d7bde51c2f2ce47d37e0edbb0fea8d30db28916c4c5715c09463032830d3b2979e729551dae611faf950f39224d39c9f3c20c356c9a4fc6929c26a8bca0342c735657f1c48f1266a0015f512a8eefdfd881111c2320f0b1841967d894e59895e6532e13cd196658f3e68d934d0e2a75d095614bb2ec2f34c668be3ce530830c8b3ae133f35e765dcacf18c3729a64fae238296db29118f6ca18368cced298548a37230c3b29273d2ba6934f980186e573a83ae163f6eabbcc195fd8b346ef93ac134bb41fc30c2facbb673e7f99d85b4fca8c2e2c76e9634a2bdd0edf11186670618bb1ffa3c45d3cf93c5998b185cde27446c47833b4b06da8f993493fca28ad026ae62999b1899f62cac1627182123db63b97af9bbc624de9697ecd53c9ab8d2b168f418c524226dd496c5ab193c49755df58cc9e9815cb578ae66713e6899998552c1a7ae3988e1397bd56166aa862b924342c288b7adf64928a649c2b9576638f8a355df8d0a15a7a463b798ad5e2c3895a1e699f4f4cb16d502b6b554d4ea732650c354ab1874c4d4af1c46462fe3f2fd420c52a7f4e38bda5e76c3b5fa8318a6533d6e778c2a9ffa0944ea8218af5f2a720f5e474355642b158934a2fa69039a978aa27d400c54e8a31269d897f22aaf727d660624dceab58bf41e589bd3f7d339cb2955327d64b55312ae5d759984f5aa106275627752693ba4c89e7686d62d96c6287ac29d326c8d9504313fb933bfcc493c3ce3e3199d84244ebc9dde4ebdc5003139bafdd68f29863ea4be150e312ab1334f9d4d29ff20d13b950c3127baed227a9138fff41abc44ede3c3daa2e36f7384194d89edc39c7602a068d4e90e1f13b74dcd598c4eac42dcba91e362dd490c45673159393629336f12e79a81189352d7e769dce9d3fe520b1a8c69cd6b22e7aa8f188b5642cd9172c6a458d3152a8e188e5e2c7c868b6e3679d462c17aaf5e42f935e323d851a8cd893c851a79e337134715cc44e588b1e4f0a931f3ca688359a902ba615643579670d3512b17f363147767a5290238388c5924e9a1ce6d458e6d021d6f0e4a4f00f997fa3c6103b31c42851eaaf4ac92685d8abe47759080d4d99302116595e576b39a65f8e0d622768d8678a1bf6636f412cf6974929be2f25d6871a8158744c25ae494e4e274d04c496d6c2e493369349ba43121231d4f8c34e8a51313fa9c2a698fa9131463052c30f7b6792c92be6b49073eac3aa22e244cb71f2615927d836719fb019e33d8263e460216aec610d5f7d3298dc29959ee861b9f9cce9e9cbfa76c53182e3eca0461e16df942c77edf39e486be061f30b9ef752c7183f9bdc61b3b413539a944d2a1dedb09edcf92ea146a6bf531d36a189f1534e3fc13357627a881a74d8ca492a775653fe12fe1cb6aa123aa56f0e5b3efac1830c14909070e141868f63841a72d8bc4c35ef9626782669e2b06a32cf50762642c7740d38ac1b63e677522b0da1c91b96dd184dd20c9b6935ba61d1d3772bda29e5119d36aceaa44e93b3612c689a0d3b99981ed3a38979dc6b0dcbf826eb1426bb252f356ce1bac33f51b54b953e0d7b139c20eaa4e59d4d51346c32c32fdd13947e5e2676a8718665d4e213cfe3932b784f424242626a9861cf99e4b7bf199be64732d428c3beb1e12bb6d335c8b006b3defacfa731ec716f75ecc23ecccfb6851a62d853ec526a63da0e3e26186a8461cb24bbf0b80fa67e93c0b08d524ed80dcbda3cfa1a5f58b4c9a41c3fc9b8973a57c30b8bded061a2397fc6e65dd832e1e39b983fa7e9f81d276a70618fbd972bc49826a5316d61d310ea2c1347c6a049792dd1c2de954b9eeaeb6fe2655a623e8250230bdbaa67922ea5472c2c2a2e9972e2681374ca11438d2bece36482ac8cf13c4ab42bd4b0c2969a98c9339f820515ae2a6c3985f3efee8450830a9bd28e69f66482ec299d611d8a851a53d83265728a5916d44a7739d478fcf8c1761fa821852dac9b141a4613fd2b1e852d9349a6dfe41cce2fa5061436a9d67b31bb43efd24f58afe3733ee550ffe9490d27ec648250be9baf928a712f31e33d8a50a3098b565826286969831ad379078f30ba5083097b55e89883f49c46a6688d25ecf9fc3a858b23537c9f43ad878e1233dd1a4ad862f63e6352ffcff3276115359bfbbf644e4a5440a88184f5c48ec9f0a837a753358eb013e4aa3eef929898d208cb393995beb4b1d1c257a3089ba6a42773de6a10614f26846ab89cf96ec33586b0dea94f3a76fce98c293584b0c995c74cee2769a5de141b630423618ce018116304c7c8d6a0078f0709c6b6d6e31d9d14964f1c81b19f98b4b4a46250bf1fc82ff60f1f523e9906d1044dde4f40424242c23a488400e28b35c613ed4b2e8812fa8420bdd8b43279fd3365cac9d4284078b1e77e5279c5bc23949077b19e7d3641d95d3ccb1272024417cb5ce62693c2da65f8740eb5121dff2556460d40b0830df8f0914a40424252d2c380e462afcc1e0b6a0f4070b1e6265a26e8ff550c1dbac51e9bdc04f1f1c2da3851105bac4fcc39e8bb2677dcdae450d3e19200a9c5eae3c4124e8e27c9ee4a8bd5e663830765994c2c9559acd564f247ed0b214ebc64b128fdf989de29a50b3282c462513aceee4c25253b74a019800081c5b64df24c1e254e68c598821f898c0e9090ecf01f3f12199fafd82a4779cc07d3f94f8e2bd6b2a859669a9ca4a8510e352702482bd61b19a3ae93d420ac58738998caa48dffbc106415cb3839251d7ce4c924a115314054b18919cb94e34ace5d34153b997c990997365f299aa062d9d95419cbcb43c8934c05c829564f7a37ff678e2cf5839802d3f9cdb94e32a5586ee3e858a3d2c5b47152ac6b693d05cd63c9bcd6030a9051ecbd372ac85341f76a2f8a6d4fa6b09cbea9731a9050ecb9323963c6510f5e8362bf4de75f7ee1f33441904fac7b39c43c39833cb1850e71d7a4bc13cb5c6dfa8d0e1a4ed27362b9af4c8e974bed67528963040a209bd889f56199643a6a294bc23172785500d1c422a646fd9f7ee9e029904cac4146d30ba133c1c4329b294a5787766e52402eb1cc278d162bee58f2b1412cb1588ebb689b7a6ae6aac43afb245d27d483da314128b1133798b4984bebac0ac824f6d817e328b5f431971c44129ba6f5204ec9a082e7244824b6ca4b27da363987aa0e12cb88dda86e074dda4a7ac4f69920cb539c0f17220271c4722a86267c1addd4c102d2882ded939e207ab6acc4c90e19e8f061664018b168fe267562f89e276939346d115ba5b514f994812862cd27894b4d924bf6b76e002411cbcd13d4e556e79c322088586636285d6bd2432c5a4f50954f9ef527e8f4f01f371801075e8cd4038821d6cd5cf109ca6326a9be1462134d524b19422b540a42884de3d28709957efc9283583449b3d32bafbf8d0b628dbb9fb64dde1863f4ca28f9914a4afc078f1d368263c479a00305324825d04b208158b62b4bc5f26c7207930002882d6968e2a5d34ca64f500eb5121f3ad02940feb0491365e5abf9cac9ede06164b06387183cc6e0d1a3470a4314207ed83705a1e35d06994a8c207dd8e2cf52dcd164f29e86f9b0789c9372bca4416f5083ec61ff931e345f88207ad809c249312f4625b9d441f2b0f7686888cde4a72ec6c3964ef24a4aa520b34a78873d6f76cceb131f7e2509897102c40e5b1a7b8292f17240eab0867e0e15446807081dd61c94d09c3c6f56509acc61af3425c5ff3c83cc190af0d871608640e4b0afe7689b8926ab01240e3bb93cf5a8cfb91c6a06040e6b10423ce9cf045581bc61516531eee94caa894e0e881bf6df98e4c80b25e5c9a4a43b919094e12640dab06e4c6a9f682a64f3e9b0612fb1d9a183decfef6d0d8b86c739bf26c5a0426ad854934f9d0e4a733ce9679034243223d96e635c6e48dba44939ca1880a061f9cb63627744c9fc39c3a69b184e7e6259a913961956db4f1e4b5fb00ceb6d8ad7e61dcb295d40c8b0a98f1ad4967dd2b018c81856cd2842c7936490f9ee40c4b006953fddf66693a395206158e7baa3c7a04e8a399440c0b07fbc0bbff2e7bda202f9c2963e5684ac5142c50e8278614b157dd474bef2ffab328074619373f7c4f029c5474b775cd834b93bfc49f5992c66700babaf950c177b4534806861cfd5dd57d7214fe514310b9bc7d4747afd21d66c442cec51ab64a8ab27ddd6e50a5bd0cb55b1b2fa29132e2056d8bf092a5739885bf1384815360bdf04b1391b791702a1c22a6a9a761bab376509640a5bfcebaddc0f52bb9352d87379a598921cd1b14e19859d9c92faccba2b7db2630510286c41e58929558ef5219f3c612ffd5fdf4d95be7f272c6b3256a16b4fc4fa26ece1f449a9f2e44cd0a831611f2d354ac994dbb1f42137802c61158da57e9549999c3d2b61cb5941cf529b70e9c924095b65acde0a3e4ac7fc8320619b51b3314ee6dd8cf1115675729a533d9e9426ea204658bb4ca719256ca3af0652846d4e091935ba629f200321c2b2b7e92c013e9938f649f341031e3c3e1000478470628f29faa5a072d2dbf074137b8acfa47df286ad135b19fb85104dec6ba9cac4974a4f3a07a94789fb97146b42482616f59d5ff167c56b2f56771f8289fdd2aab39ce07937c4975833878f19f310a1225a23422cb16a7ea74ec1b7539825a412cb96b2f1f3bd28b188878f1aaaae492bd60e3118113289456f858da576cb4265330891c4629ae2e6faa0f549f49048ecdd9d9ff4697308392a48ac4fdcfc716175f2d47cc4da1763d927949e0a25329a0c1e2c831d3a68c083c7a4c4602b421cb1e71115fde1099bd3d981138cf821a411dbdac6f60899368fd52584113bc149361a34be2ecbcca1766410b288353e517caf4fd67768c083478e12325ac75d1b4214b1afa9c992f95673349543cd8797e4e92024116b26c9272995f36f640a22368d9998177bf6c470928758abbf94142997478466ce1062883d28191e9e683139d4500c1c397ee858810f32c8d0c0088e91111c233846b22ca4103bb1bcc9bb31aa3941e573a8851062d3295a65a5c7fc33f21c6a2183d8f22b73b86d62ce0962cf1727d6abefcaaf0bc44e121bec42e78d9dfb54c26841490a4200b166251dd2ef67532bcea1962108f9c31e2b3af7ad7c36e10986f8614d1e3a7aedc83fcf7c1ff6d43d4256e5860ffb8f996657d25b9e7442f6b03f493b939e34b21e561d399a4f4e2947e5350f7b96c7579c94b34f3a7858a36d523a9c242b7aec61981da41e3d52f11e3cb806217758ed77bf2ae54a695bd961151d33f94b274da7531dd6ee4b396fe8266a9525840ecbc5b193dedb37be66c81c36ab3093612da6fcff87c86175f267c8185398edb899305a50e2829038ec41a9cb7d9aa0e26e92e5d0aa2ab34784c061cd7b1bd3e8e8184eb042deb05513c73331d596be12ddb0977aae589d549b18e49036ac4167fb2e66f2fe5dd8b0c7308b4da254e934969035ac23a3796656c74bde8d8ce181d4c342d4b069a7ce23be62ee72e03bcc46c6f08085a4618f21949363dfc94c1e51081a16d1b09dabd2a80e1f0e39c3e6a5af497fa3aa15ad1033ac16457f8c41a86cc57c640c0f9090f8a0010f1e39a8105286b563f8b1b8f194be23c3164d63c8c9a44e4a691f21640c9b8f9598fa281b9a2016c3262b86f73a714676e8a0302cb246336562bc5879342160583de6ed947be65258f40b6b65ea8db173d8f14a7961cf93e51953904dd8afbab07f3c19563c47acc9cc8575c6d68395c652419c6c6175f2e75b531adcc14307c95989c521440b7bf6e90e4a3c3cb1734a481636ad4c0c6ad9373ac70787064a2c0d2158d8095ea2475c528b4d52b9c216b7f4e93c298758610b4f925d31e69364e74d4815f624ec9fd4e76b93e13a3ad020e8c187102aec29ccc8a6a0c4e49a3e640a6b89b5a457422885ad2b75567a5032775b14769225bb8a567dfa3a238440615116aad777ef9e4ccedc3d61279f54f364236b326fee8e9b5293548a87d38921a409ab99d2311dbb89c94b7467861026ac66355fa966c593267617b2844d344b5fe7123f97eff3428812b650b1d43c630c33592f24098b124289d80b1f3104096b051b719d3c26e4089b7a7888924967dc94821862849d5432cc3f29a698cb9268d00a214558af544ab9bb0997797575218408db56933a78e6998bb132630819c2222c7d9992c92b2ca520860861ed13dfafb1fe27790731187b66c8b03d3f30f618d7f7f6fc92fceb177b133d4d06bfb8f1c5a6957163eb95372f895eb0d18b45d36c52a1494d26eda720f262d50a9bc313ab2ee51977b15fc9b14f393e5ac650176b28cb4cba27d44ea573b197e9da2eefd8ff55c1c5221a42a7df342aeff699b7d847a4c7d19dd3d346c9165b756e26c57c6af3d4558bad332dfcc8c527dcdf88b4d874fa4ed9d2a13366328b3d5d7c4ef39fc962d11fd57c4b3b96d231b5118b7535f93649a5d891276a051bb0583f8f6fd814274aa5199360e3155b6e2e0f0d55eb7b1e57aca5e1c453d0fcb890a926d868c516224ca920762d65dc062b16758212276f8d7f90a1c3041babd89f5897fcd459585f136443155b9ae52a3fbda3794c6bb0918af534693659d8db2467848afd525476ac463629c7530a364eb15dac0d7ec2492263bc4cb193e7c6433579d513735a070f15945829d60a1aab9b3b3ce1f23fa45854e86e3cd5bf365f8e62335d1e9684ee0c1a2b68a2d8aaab9b5c1e339d3cc150acf317723fcd58591841b193d225eb20635262d77d625329c61021a32fae62ec0d363cb1ec55c75332a6d11d6c7462b3246635a7ba98e466d2634948520fd7618313cb8d75ccc4cd693a7736b17a12a23fa59c237ed21084c186261695cec2599ebdec90c9c4969ec26e0a1b353e6e30b1a7137b9f94d995c2fd9758af33a9732e07f5495d2cb1fea6d9ce38233a9895d83e99cc70419dfde68f129b13ec53e9be4c62ff4b53217a667139f9041b92582c49f90ad959c192e7041b91d82c3729aac7f86a268a90d8f3a69489414306d93f3f628d931b7f522a159b703a625352439a34156d4449265d369f9a45c10623d6266cdc202be8a620338b58747a087df9af494d4e15b137e19b9c1d3f13ad3a471c9c116c246291a37d3af3236b7747c46aeb41475a1079974f0eb19362f8988fa115743a0db1c5103bb3cf9b2af3492116253fffdc7fb88e4b126219d5e4a0994cd060fea741ec79a127f22aca3fc1b621884593d6f858dace13ae40ac6a33ca644c328058a4df09b1d91bd59adc1ff6b0995469d509237fa37ed894e7aa4f9d4e9858d1088e91111c2720848d3eacc1d2469d68271ff6bdcda4e84d109d56447bd89c1433c9ba103aba9ad8d0c3269498cd9c3406f1b9491e362bbd39c54784d2d2f0b09c6f596c8ed91db6203d08a9997fa2b3dbc1861db612a3d33fb1746784b80edb5fa9265efe289d4a890eab79ad654c193f5799cc61539bb55a253341546539acf76144a7ecad9cd31387b5cc93aa9299dc797538ec31f6e6ab2e283d4dec9458116cbc61ad91996066559e7230b960c30d5b6eff7952278c2c5151000cc1461bb6a819162f7c364109990d8b4ed7a53b7a13453cbd862dc6273f9ffd4f41687288b0a1863dce79f0ed267c8ce2a761534d3c25c64478a6d1d0b0c891a6fe534c731ed119f674e92eee6295c775d21a681098c1861936e5d9993f4e84b227a50c3be9e42aec3cc9b73687071b6458e3667ce9cb8bde978e61cbcaf09c572f3a793e45061b6258c43a13f46786e7c620e3c34618163b4d5e93cd527d42ce820d302c2afe244d4cfa2ed8f8c27af2979b63aed4396c2f2cdb4f32699b091bb3341b5d586d45345144c6558ae162830b5b1347a5a036a3e45ac7c616b69ce7b2e24a69c7b58960430b9bd23e95c46f98fe8ab2b0870d66179db0b196c2070b36b0b05c3ce1f74c54909e355e61f31da561357f12a52510786004c74807708c7000c7146c5861ddb51c4a54b4e0e41cb7518555edbb44834e3a997d7bb0418575c328f5fed84f1f6453d863dff5c8987ec2f37f051b52589d1c3ff36d3741c8ba8ce058848d28ece4f3b20a3ac7cef4260f36a0b093d4a62693a346d9d5f009ebed7c2ed1a9e44a869db0f7cf13837e62e534ea4913d630552999e99c49aac43a26ec04f558252d97bc27996c2c61edf1ed78a3e2cd7c9dc347fac0088e91306c2861ff0e5a9525f389fa1d1b49d84ac5a05b952f5cceab0d246cc2473c932c65242427041b47582ffe7e44556828f1a7111c384848b20e368cb0e9e4e9a4f82135cd2b2596031b45d8894e323be9a15347b04184fd2bcf9fcc133b9332c8d4c0c61036550b3a896932154d723b79891538d810c266a13349cd4815b1617382b1e8dff94ea59a3b4507189bd0b539e7d3f849b539d4f8c5767d49c369888bc94a5f2c42888db7212521a1438d5e6c6a3fcfa524fd937de7fe5083177b5713d62a3c5687d0d979078f1dc6871abb582f5365d3e5dc577141440d5d6ca364c8984f7592124341d4c8c5164daa9fc54c50425de78751440d5cacbfa937fe5e98c7f38fdd623dfda9e994c68b976f12121292f35198a8618badcf4f93ae82ea7c9352468d5a2cfe49a94cb3de0f13cd236ad0627182de5aee70999c9ab01a42a2c62c960f427dcc8f9f2c7672505aa9c377a8cfa463b10137871b94584f6eccca8fde056e4c620f97c9e4cea1d37ddc74470ec450326e486293b2e7241919b393f2dc88c4a2bb64e5fcf1afe4a518246e4062f95c42a428d51baf128c60240e371eb10975997063a1fa744810d570c3113b41898ea54e93644e153762139e41ae5f5471d2a9dc60c49e6b693178bee4c4987ac48d452c4fee3ff59fc264f88622f66ff23fa937c74c993925118ba9d81cbd831e51a249f710830a3710b1f68d3a39fe71fb74d2217652eadac5106a3fd5cfa156d28347490e528f628658c364e736670eea178f1b8558e3b79a9c93133627bd413fb841883dc54cdc2463ca5c39a79c2b1bc426d39363c990a2e309bb181ff01390dc10c4a242fda9545fe6ab23433702b193a467ac904b4d8cf159c20d402c1ed7179da44ca78bd91f36d9a498a99ca4643474ca70c30fdb099dd999aa313613eec322369ea0cc1b7c585783e69893cc743bd21b7bd83a7be4c57c9a89a92e3d2c7a2989ad5e75d546f3b0e8f029f5eb6efd74100fc6242b3e5ed7931b77d832395e69b43ce1861d1665bedff19da3981ad56113d2ae36ca88dac64987edc9dfe3e539c8768a73d8738c53f684cb17ba397fb82187653f935392574188f92671d83699f2f484bf18eb04e1b0e7e719d341869427ca6fd892ec537a74a9dc696d3714e3c8dc8bf5999440dc68c33697ce893fa2ce4e986cd8aa49ea6468626e3eafd6609db4185aa14952c3a2f649172ee94cf252a734ac3e9f2ea6e095666246834fcee4b49b5277864586894edf1cd48610cdb0685269c85cbf29c3f6a4f4537521d5c30d322ca2cfe309d9f1d2e47f630c3b398d34ddc43d9d15a71b62d8f7bbccc9212f3db811863da5a9a856ea3143c70200c60d306cb9e9845a4d191212921cc0126e7ce173c2e78ff66f7c8841428266851b5e58fe52dcad0e6ac1b4d2853d5c134c698a19d2fabec18545d599ec3f6566aaab123c1bdcd8c2a274133433f6bea185c5c96b4fd04f66612713545899cab44f1c991af80f109090a41e3d344042d281138c909090e10616d6cd37324634a99be420f54043c18d2b1cd38ea65362e3ee234170c30aebc9a492c7942fd606158e8ce1811ca413983b1fb85185edf35e0eca368e9dcc4722c35c05c98e0334831b5458e737c7f4153b1596f80e1e761a503b61b4a0240437a6b09f6ca9709b4f255df3d8516263dc90c2264e06fddab9c6e8800846708c7c000737a2b0878f1e0bbad9a493f70d286c4a5f5433f53138c1571e3f4a5670e3097b5e8c8fbf4f0afb4146034e3f50d0c35cd90b6e3861cff941ecf43b1219657c4008379ab07f9233a39c38323ecf1b4cd8be64cc4c0a31a1beb5186e2c614fba89199f259f9c447e43098b05dd3be19998c9a1393792b05fcd69c2cffe9fa9bf103790b06a25159c749ad63a539070e308ebaae74f69799e5c99bf61847d2cd7e3da7808b93887de2802016e10410137863072430812100946890083c7efd051f20b5f4440a4175b0a153b8fbe581e4fc60b0888ec629341e6c653e39d524ee962db14e37ce67052de28172536287180082e1820728b0688d862d9f097f6099fa2772795ecf8921af848392871c45a2c3f5fb3f2a749e99326a90cb4121d65f830115af8d881d3a0e40122b37880882cb65cb2d1ac9ee004714abe43c7a4063cf8ccc4a2440222b078c5fa4fac26498f9d893da3882b76d2d587ac8aa731d42ad28a354533aa7e8a150e105985024454b1250f4d38d17b92976784104945c902445091009153ac7b613f56b6a9828829f68a7f566a7490b57de6501329c57a32be15629e30d6c13c84082936253ae59464c53329338ab4001151ec41667f2e19534e4c354d0520e8a1a3240d07d8434749193e52241425051001c55699bac4d9887d121964a412bd32bc7520f20904887862115f9a528aee67f8f40f229d583429e3688226c7edce0c1e4438b1a58f4f4ca91aa346587a10d9840144342122928912088860e2002297284180882576f2c56a82c70dab4f994a6cbba3497732a746bdcd8ec43e28b188096b7238cf183fa526b1ade710955334c9f02992d874c5134ef6c7f2a44d22b167eccb5fa14c486c295f1364645e46ad8421f2882dd595956f3419694f1cb126199652ee94e2dd75237692f8a5747ffaa7a15356341061c42633c8929d9e63848816b185ea68aca7a5882d86b57e82d225624f624ecde505db3423624f67a77bbda9095ffe21f624463f9a120989d9c1293e7ca0882136155f32b2429a5cd97170bc39205288d5745af31493139f2242ecbb5a7a633488909f19c4b61b6c5313849079649c432d032282584b94853fbd497aef29103b41e43ea963cc83d02754810820b6be1cf1444be1c2cc78f7874537a827c6d0d14c873d521969de0fcb8ffdea05cbf46a92227dd8f64995fbc13744d308cd45f8b066fe26fe89ed8d5b53063b7890b187c5895e1d736c9b545fe550d381270944f4b095d49864e6d53df1b8063ad83b0f5bc9c9ea5c496692e674f8d081834201113cec21b7639ed299f804f972a8e11dd6942e95f8fd640e351aa41a94d961adeaa8f5a432a1332c86ef10630c913aecb1cb9ba4213c8ff747876d44cc6c652e3e69ca1cf6249f83ea24355a0c961c1695c9aefb491b54544b0eb5331b380ad0118943be3232c94c0cc361931b34c9a3673ce531f58665740c279e4c7a823cf3dcb0c9a8a52f37292f3c35394c1069c3621f9f9c968450fd311bd60d3a7deae8f459cde5503bbc4149b9410d463a7082119135ec299edc448d7e999452031e3f7cdc800d0c44d4b09c0cfda7837fce904e44d2b09332fce83cb2c9bd7241db2182863d8cb453cfa4b4d7dff9e8914a70b60191336c31d6f199b89b1c6a253bd8918c19f64cde2e9dd2e279fa67226558e553dee4ff049d1c226458fdd38f494f9b53df273286ed8999e06462eaf5e69e4a74a0e3cf2c20228645855395491e52538ca78f30ecab6993b0cad5a46c4b30ec24ffd552aa9b1d4a2787f60ed715f9c24e3efb8ccb448b49787a032f6c767ba262ae244a9c920c766011e9c296848a7de727e51364e4c29a9ebe53beac18bb996c61d11b46a99336fa548a1656b5b4deb9ca64929e9a854d5afefcc64b1aad662cec7741a7114fa6c9a1a62bac31e768a86e52e7265f56584f56eb54c584bc91a9c24e92714147133276af4c85fd3c3341556c642599390a4848bc49484c640acb7e9d13539ca49af40e1e64f0e0098f630211296ca5730e5ab9943a61fb9262dee7ddc355240adb46b9d1c493b6634a8946c6f040f21374f21e24242250589466723cf1b1897a4f34879a0ed28f1d3bf0614a31224fd8f6c2549ecc7452264777e828a1817b197a02e7809df79540c4098b28932737c994d7973887daede04d053d787c0919ec5d52868f905c883461abdc4cdab1fc19df4c84098b7872224b30c548562ba92993c9a5499ae2278d1151422946c22249c8622427892061d31493a5a0734a496a9d231039c29ec22775fa4f25bda18811d614314e26ea9e5067ba4811f62baf98eb94621d148810618b3b2a7658cae49bd393909ce263223284456ff4e079fa11434408ab473d277826ef9728c70f1d39283e7c94f0f851ee141f2840220809c66a9998ee9b4c8a227cb33b738a8f4986d182920f840063f313334ebadd53e7999ce2c3c79940c82fd65451a5c357f0f230e75043282021e9e13e4848427cb17a65d8209696ef82ae8ff5701f58bdd8720e3a474b2772316fbc583656ed9d50dbf909e22ed6f8b121f364f29a2668a28b4d136d733ef1935262938bcd37af9f58762ace2e5cace984d3279c6cf81493b7d836f39346944c41d5c616dba5e80f6aa6c944df580ec57183b31e3fca401b5008a9c51e548892f7e9bb893d86d062fb9d8fd17b32b2999bc5629e9b249f9c42ff8a13592cca82e690fab779b33fc10e314670c4624b9ae0c40a21f537ee3f7498f90f10203c84c0622713ec84856a08a13629879e8d8ce1011e3f7680a047c82bb64c26edcc3d2974b7e321ae58e35e7d127accdb43482b16273ae983d21cf40935b1620f16ba09e66492fc4bd72a169d94eafc266c8ac75f155ba9a4334627e6a6ed52b1a81437932b576f1a1dc748956004c74889318263a4c218c131522418c131522318c131522218c13152211829c1080811828a2dca7a6af2134b5ee918ad43c829d660e19be8513c553f648aad9a941dc4e7d1c9c4588aed42448fe58a22af47a458362f56795a86fc4d46eb28d9c14341b0438c0ba30525012044c828d63161397fcca428f66b7252af9d51551ec54048287632e93431ecc9d9e99c68054240b1e57932f1f45857f65a3eb179ceb14906d3a4caca78621195c3c9990c5ee34047e3c8181e18c1810e0e9090e4f8a1a3472221f141464827d6028f33a8045286b2481c0c0502410cc3000087f6e300831200000010140d4603f28844289cb80f148004443c2852343a242a1a14168e876281481c0c0443015118140804c3401445712853c29cab03f002de5f1005c13d419b60442bfa751d22992c3bb84c0a5d21f161672de39989a545a6aa7fffcf72bde1253b7c2b3f720cabed2afaa8b1ab2311c51d08252ec556d72faf0e17f021e9c9abb366b8e2fda76e0842f3599ce0e98438401a011f86be770cc7bf38433abba3415817c075057daf07f3c18b16044cc861440df08b4d5810d557ff32ce94e690c47baa2690525d86b1199101bcd6b17dcd1f2dc345d317adf50134f51480eefde33e15b4f388e74b40afc5ff318736a03c0c7aae000439382afb73eede83eba86a0bec9606b08e62d6f2d03a2cee82d141cd1be20b82f37cee4e9db3bdb44c7dc3ca62b492820a138823e0e78b5a9b7d59a4ad81ad32533784f2dd805fb77662216cd3f7ec1166503d47f4a65e72434f6700dcab7d8a273251ddb5600210ee52e3d836ee7c2ca650abdc6bd641f6250e8bf7dc4e19b804d3d3114c835045be72718e0a535176c4c8f0e89e76c9376e29d95b3809c1bf2ca278dc1748fa8acab51118582dbdbaf51d8ad4cd13787c153272382968583c25ca8dba193599a88b149c2a8c72189b3333373165ec8430d11b0b6085c6e8879cd7ceeb7d385af66b3c5226ed0b018348272d73410283a8c86d249c6010b03361fe073a7c51d4896fd5e36e80793183064f5377918d38ea2e6e28a21fbf1d0b32b088114b32092714eedceaafe138ca5003f714133b25d84ad8d7facf050dae5ce832863513588c134cc56a966bd480523e0e8bea7fcc0de719fd388d62a71bae11304c9d386de9a42e4f7bf33a4684634ed4b24f3f9e013e4b61cfe95784c268b69264f267b384618593da5a1050c75d77f2c0f5893aeaeff01834ee610cc87c2c48717c8678ec6b163b8572c5569bbc62248e58992628e08f62b453223f99d1ce1f9a5097107c469361918c9666265478f5419786b67aa1dcde259953402f4406c949947309dc0af40ae1119d6e2aa6287abea1e543fa30255737259704b72abb5af8f745b1d0e1c91d9e98df275de2596f6f75e87dfa9feda8cc00549a43473a59b229e5624ac9dd8b1336e53c85ef265964e166771b1abd32ca05c928f3c6733ecf3990456666da0b11704cb324821165466b704f249419f4f4f638e2daec00af6d39eb8875193cf7d2638fc04b016962508c911b764b1624c352960128b29bdefa988c9a653b94227484a8d243288aea110e8d1414700747fbe56c09804412b09a0783c444c5611dc06d3e87341c3e2cb5ec2822e15b8ca41a6731696852e4c645d6aae4ab1693ac9d4013b7d70d9680e78d3190d7c680b736b265a3491d597a4ce823676c2e94654a3afd365561539f60e11edc143f5a19142a7bc0ad1b2df36739eacf4cdbc4b1125f966bcef610d1667038e8fe345acf4cceddf488316422ff530d25021c35ae77822c3156d14be193f48b15402b4d2b9f0263196401cef79a8176398669d6166be9d0c0b47b93095c22fe4e4d62866f9724417da7129e44770fe87663fb4506e195138255ade6eab526d8b7160890a7792d0c923b18477212c819bac10048084ce43a65484d42954dbe863525fe1b24c49ce4cf17c921053c35a4cf23b125e7532bc93798383aaf20aac52e93b4e48b11ba05f9b7ea31f20404e2f6f9a0d17e0071f63f8f02bd12ca26ef0a1b022edf1f7eb015d025f56a08a92543b008c8e912c8bfe532d3f20305fc1c4bd8659f427ef1008d1eeb9102e9c9a606ad5dcf201c8a2111fd8c84464b6507fd49557464ee573fbb98c49464e8bef3a2660b7462490e409199064593bb81fa8fc7b112aca5670afbc31420e0260cad83a22692b78eb899edd4a20edf43d5faba75eaef28a1f7b83e8917d83030108d6cf7357a8e7040283e57dba34e3f556e3c4196f112cc3443c4f47c937314173566c651c1423fbfe8192fed93833610b01ac461ff9503db1d3ad84ead83cba5d2ee8cf3d9da4a38d2f1c14e48e78685fbe049088027f48c8308bae5c314b66bb7ef9dd05529b0f2d90edf7238c10d4197b043f0926a005be020ebb31c4357c32da7493c290e1054d99a105c68cefc01622be28fe504e672fcc8c1d6a6467898313d3cde1b5cd08f7c5e4c62a662300f58b4a5912ba618308a71d067b41dd4c675cc4972ebb1ce4cd2d40030490e0839935c7837568123c563b0c0031724353400d049cf2d27c3a58e2db141eff89ed97044b6100e8df1745aa928829b0a85342cb97931f64318cf44f298d543a6e6c64c5941b208e82411428b3dc11ba7eb91dc0317c1f9b9c78ddbe2ec689188f2db189502050ba02595fb06677e8b690f379992b1f81a49a4d066190ced091fa28f3d20db11d84015777bf074c4b4c8719606e0ec4692e210b17e32bf4a2b044b88a615f4fcb1357aa032046f34a1f9f1164e896907158c935bb8a09efa1838a22455fa278bd9a241bbbab3f36f7f86d76d54f2d9db2efedd0930aaa1e824e26476423423b124fc123e9226c35624594cc572bc37695a34ebcd922a420da4d6ceb513e873e5988d80d8652ca2b340b3acab2cacae532bd5d24b27407f51c64346f426c66e71e6f99be5c6d85f8ce6c66f1e9ccf12ea82f4477e7f0f71f3b9ae48a42a4b695269118fe48363e2e59e13f5e7a11f84442ecc5b3029f90b9e1cbf49eab425176ef6f323355fd852e6d9ce7a99aef11b853fe79dfbec1efc7cabc316a339f8014c4272ef06d15d668d7b5db1fa83944bb8f575e802b9faa56739875809de415a97919a73d802f25b723aa1c7a5019f804b30f5748750be338b450cbffb73ce17f10857daebfa09696e3c508537627fd376422ae9627f2c00165b5be65dc6298c4d17b47f99d3759cf1804e9e0a39434483e00e1804edf014adadeee38c2b8faaf484fd82136306ffdc59fe2abcc961a80569394e3a4832941eea296b6d66ed0527b0a3cd6940c01592b189069509d6ce49b226972f62b97c656f42b64e261dc9a9dafdb281a2cc590b5f08cc56dcd7d2adefb1941c0e868da2856c0b6bdcef641ea325830ac380dcc3b59e7a0db87870dc033229cafd6f72df880625be65b4f602805db6896a17771f7e7110f72cafcb184dc63573188b5a96685a9c09b1ab7273cb29f16962522aa5b1f231ea9f7ae66b8585d37a08755e042425f06ee98ad7a00ec91ab7040c501658ac3af433a8f27b76819c2afabda684aaaafa521aafa53ee6c30a6ea5c3cadd0ac34a7747e9b1cc7e2040181b88f1f554442c5cbe229e71fd7100a1f72979b4461472a821dac9fc047015567a53bae049ce8fc88eb4c62471120bb152431a473f3f863582486d6186fc7d8896b201850f2afc81e3046410713003d7335c3e9374b1b4e06c22d0065f3b7ac833f60f7636324ab01ceff1098e24bcead3aa77e1dcf06af52583390b65eaa0f7ea0df723763643c57e53eb0435571d7651b765d22428b73e5ddb8ed8ce8874adacae9c5d0dae0e0c67216617dd34a1965b761717ac7c9b13d288f299e822678002fbaf12cc9431666422ed926c48445dfa9e30f6a12c1af29fcd019bfadef9616716c9994d980b134fa8c52cb68c9b9029f61fa85faaeddeae7e44b1383c01fe6257bfd1a143e03e75080450a3a40babedae82dcaf9461410f41c9b4a85c36cb050cf821adeb1f764a325bb1c63420f87fcd3095ccd331f71fb6d048b0bc6ca29fcb6f5b90f0ad0050127fb1460c609ffd501c2d12fb05b6dcb4eaeba879ee24a39cbedea5f358eecca4d917f56c4fa1d364b3be19a5d0c39d5f6a825b9253d3c65c430fffdb96198839d49e38d0a8d3d258d46e91001702acf101e82080413b809cc9bbacf0ba6809756fab088d2cef3ccf83d6001a626feda6bb08af20771c9b3e900a1ee2a3d3b1ce1ea0a7b1d59b1f282094a13190df2796649890af121316b52512c70644301f1afdc92253ec8a32846bcaba29a25f84f020d176a6c2b82c9cdf35aaff406e7ffa620e14e46cb6b7ac27ad38724d38e561635b931043765aa37341042074f52d7de6a0ac6cc4dd06bdee4b5c34d6800c1b041df8339079800a8d86f840dfddc8605881570eae86c0819f121d806efa893a8ef809d82da0a5d8c4466834b5fad352359cc1140da10d05bbbf6ffa6e92cead8cff7ff9a17ae1aa79ecc242fa6a07e39805e41a80c657a8305efd8adda5231bb48ac806df27491db48225289c77239d7f398db5d33a45d1a1888b4c4de7c3e8802ddb6989fae2e9e1aaad251119b6759d9a13128fd4dd0b5248cf781158dd78aabc6a92155eef40fe1ae2ad78b992f7f3402d271efb4380c033a442df6d43429322c88dc56316327804d533af95373f618e8007b7ffd35649056b721017623adf92fc61cd665da3f1e72e1874780d13380544ac682c5d7c3d1b00728a00c890eda90ccec95b07c0c04a8c28dd7f1097d88200c0f0dd2211b09ad003ff859b72c1410aa94994dabfdfd9d7324a53825d833f61f2bcb356de9d16587663790cc74ae0fac56f656a7463a491975a72681f51ab1d1033eb58ebe7f3ad3a4a4ff10a70b206c741bdd451d2596c9d551f10011484ad9aa958ff4c9b84ed85bad557a447dbd15c9364d791784eff060480c82259113de2c14e77f1b76d77e3f29ce7a6550b1efc9200b64e040958c5f678e4de024f94deb6e7cd0211e3075bc54c813e443b81d1f5ff11dedb5e834c5446fee50d6b3a4d709963b2bdfcaa30214f226d0713b5b030302c23a86832ffdeb3a6a176ce0a07992f65185528e2e442a638febf251a7ab6e63170424df86876662683efb7f8307943eaa4686de02a78a391e70ee96e1e53d5242a8b98787fe8b18e1449bd735a7d1ab723e26f6eb5e6020444bd2f241b99aeea210ef444a321a2eff274a465f8c5a81bd1db9dc2d937b30d111ac7f8be04f2cc569529b537cc1ede09269500f5b231d42b0c42db2dfeeb9acbd0704ba977fb89bccc17e6f1ce35a06bddcf67e8e8264d3dd17e7c6219f582dc822d6a182649b66bdb02e20a04bbf9ac9e8cb83e1fb92382a90c4926d2a2f1ee861522e5db86e95364c082bf0bad7afac1a468e51399f1a369bddc458156e80083d249ce50b99497be26953416f45195ea61f5852e5cbeae1444dcda4f80fa9aa04509455c77d4af9f2369324500d4263ac0c2a6d1ebbe2ed3e54c5eda15d10c399f9688b982a030b1ba9c2245bea2a265f1955110addeb6ebe047d2f5fd8a6c8f57cda8d93f71afcf924321d5501acb0911dd3a2587420389e152455ffc7dc9bb888dee9cf121ee1b17e8438ba46dd9894f87cb9c61f25c325883c9bbb3d204cbb4782e8294c7b9376df47abc959bf38bb5d34708e474355dd154d103994ade72a1b9a0ea7b9524c12421166e5189274601bf2962687343ee220b91d2c17bddbeb1f8c44dcc06be71419f6d21e158d0a8d30a1a182a2e722e315257e6bb3d4dd6176d6213a755736bd8cbf558e5d559513740073ef768f305b1d79ccd0b603d7dc134bd510142fc457b566ac089ab074a3e77ae3f8c70bb4badf723b659bc7a9b382f05b998783846e99109e71979e2d7a08155375690287d5fd0e13ab5d0a89dc47b22741008f21458cffa3ab1e2ff2e4574c83650070cb5d802fa739c898ba83ae7a8ed24f86e4a03a4fead9c2913b4b805d2ee3540c15c1a7a1998cda15bde1e0d0c08342057076f0badd740a17e18cc392e62a0b2c000d164c6bf9b47634603e2186b10f95d4f24aca00b6bc335a6efa4db5686538c55edd3693c0ea119d5f99eb260245f8354dfd2857347f11c96d95bb2808384a352eb5d1cfe9d51922ceaacdce18151bb4fdc842e66384f3bb63d8b6f598ca9b1d01ee2e740be64b7f06e56f460e78df6b0c640c933a4cf406a05ee27c9309d213b9e0dcf14e274967821eaa4abef560c5f19aabcf97ad91d57480cccb0190ce3ed2302ca955a148557e6642f15c99cf65b74327e432083bf68311fb932f6ba0106ecd5864c417783ecb6ead9133554bd0cd6533ff0fa5c28fd0651b1d3dc49c5a68077a8c5be0c3ab48159f1d6cec2f497253d3bfb6cba1b7d200dffe7cc43de3e45ed53265cc0faad1a4b0e64a89bce36e259f39348c1aa288953f8ded0208708e2b44d380528db9d848946b37769d4df229016419fc363d5b1c758104dc1b29dc776aa63f9c2a2330fd4a71ea050fe1ac7f9266294af6e6c874faa1d517e919ed8639bb3df5f9b91814bfefd9555c24b87774009cfc2f241abb654e30abc0b0678111d80ea8d55bbb91429c865f3530abd111288e0dc8a6af1ea65aba2e68a2d7d0cde8d38ccc4d610600d5270626de2f6a135c55f953d1c8082edfafba064d3141c3c619aea7be4391f04998d0cf1618c044617a0a75f0ba79c18254fa91e215c4f6c74de34969b41007e2a9d0efdac702cc3439fc12417253907f270d788642de5211d7537a3ae509f252395380c7d7143b18b4be18007d23e803c465295059bf975d40f454d8f7ce42b8a50383a4b064e6c2c2267d5b777ddb66dc441da4d0e75436eefb0d8e1e7ca7e72572c3f7b6176fba577c74615c56030d2aba09fcf3c029cbff2ba9b425ab9ff58c5cc97972bc92190c743b16d7d350b0b3074919a14036cc9f366780c0171fdd0c2f81a810b761e35d7562fb1d2688478ba538d3f16fb6ac7ab9a326d543c0be3ce839373385c4c1d2935d1c0cebbf720446f4ab192d445ce3fe18df05a0f6a8e7748a158e183d4061fd5e24f583cad09c3a8eefb2317d70098782bdb2d8650e328831d910ae710995e44b330eedad3fd78bea97363e99c4252fe8c3a3771821f130a4b4cfb874873eb1223cef34b1161ba708473e1e32a35a4f0060f4645f066a79c7003e1ed7c1603540ac2d0c02cc416cab49c0c9855a979af8421c6261e88b0b05bf929a6af9cc81655c814c36c1d65ad05d28b16bca958555fe64566b1d5c45117ae022804398e888922e8ae1780c303afe24c22b444318909d11271108d1038f73a48f8e048f41d34186386784f9e758732980ce54a072a2e78993dc83659df2acb3fffac17f46160bab04e76c6b7ca6fd164deb26bf07c87ba221aedb4cd481b782f20ee26a4058e7b9d32434f6cee1af0d0c507681259e0af47d3fd88d8327ab8e7545b3f3bdc6995341f9dfe688ad3f00aeea24b45d609c903832a19bedb49cf318c089253729b322a44651352a92655cf274bc8e3bf00ed7c49caf0b736fea61d0817be4aeb9a8a72a51253c205e62cd7a1555ef912b5b8777983434bf4aea6063926e76a7195b630978b987e521c7d9b0ce661c0c48637def1d25c30385ba43404bbc9b272f60ab0be2000c9a7d688bd68fd76ce490b208e6e6cac4e15576fa9c5663ccb0751b2503be6471359dbb6a6c869dd5686c9a721ae14cb0be900a129958651ff74d08bc7cfebc8c129c2e4fcf009ec9e3358a7ba0c7ccb3a35237d6d3739973872aff172b4a5ec299c04ea12d330d5f0dc17f44f66cb746fddad21183a9f3faba0c4e9b27b098b801a2daf80ace7c6cdfe6670c07b22c1ce1f9158b60c79cab10f952dcdebc4cf566082ba873a6489ec14aec7956cf4dd830bf9b69707771871968e711467eea4d1a91f9a4e2deba6e4ac181436ac5f567d8ed0477cb8f3cceed3155218acbd6175408787b752d86ec14b623c0630651bb6efa536f162a61d50a9b54116f8f0f40d80d41343959c87ed4c7a2c952fbf816a18c175f9edea3142a50421fc096fc2f121443b438b9a84d11d508b32ee8436178760a171ec64c5ff320b3d09bc6be023b8f2dba1890a58fac8852805b669bfd9e5e7dfe3185e43ad036562b471c0428271a54390f5b04cb004209aa0ff686615a533f9c255b77b83a3dc32b333b59f19e61cc40171ddf1539f705c1a1cac61f94e5717a682775b3c1667b140b7179bbdccc3f0f71a21a578f76e876118789d9b61c13609a1f1af97c51f8cadb8df3849b8f01885cbb56642df0e6e06c5c18a1aaa7ced8ede6d90bd37cc454394726d29bb1d2e4982dc41a4ed59f13040b5f90842513ea749c89989346e38b21cf83d452102670c31c12b8c57d1b7f31312bac62131e184f4e52c43ae33b9465e8cf74046e6bd9fcf3bac20c64a8ad0686eb041dd7da1f13b2b039280a59baa6be0b1dd423c977f7231842e138df21d3ccfb2eedd908e0300103420c43f86b9d82d700eb1221111b409e661859a2dc2abdc8ebf42e87afd57ae2dfcb8477d6a0d7b105489e6f27d3ee879f93508154bb6a429b0295a60579eab95314f6eaf791fff39d11e107d9bc68a39c9ac7fe378ac5865ac29ee9a89f6fc2cba680717473384aa7b8a22db0adcc05b10dde44867ac72cae701c75c731e35408baae07a122a158c46002d84b41b0b40cd1b93cf8c294d1cd011181c3444dc66cf6cb5cf50590f523c409f67ac41a2b1926b5e795b239929fc98e055c634e5a952641a43908655942093c9c749b009d12d1aefc15d22eef639c25962961589305a2cb46b1c914136f3319be79c35baf402a275eac80e803c09b2ed8e8999675e7184dd9dbdff4a27793cdad27c93d0ae24c1734825e982bc130a584fdb3b696802088c96647a062c5d33859c1698fc02c2544a1c0628e4215018a73ce5cfd3f8342229f912287eef4f8352d8744e891a01a8a0326730f23e8e8675bc6ecb978c2084f1314a9336e1292091bbdbd99044211385fe35936a241508fadc4c4627100020d1c011bc93a267cc6890d0190dd36b77bbf15b30c3f00d019240356194287be21429a6674b7d808492c8a2ad41c918a6132dad89aff8cf91c7c2cbd503dae097a03216a164742931109e344e9896c48aae1dee24092ed2ac2f4ac2c94cc8969e09928467d1bc29458e896cebac89cb2ea49689a81981ed18fc151bf73e157da30baf56e731eba7cfe49f9016dfb45f31896e46a58bab2396c66ade4f5ac4eb4c46ba7864288047aeaab487896aec9e791a8da946483d6553c677955787be34c8a45208e0e4579ecc0a895c9f0140c83bbc3ed575ce7d61e6b412e7ba7ae117fb4921f5a19379459d278021552d71529c852729106f2a3d74abb2a6d147db05b8357ef443b360727c4e6c69cf817166d8f2f1211be9b15028f3f4e107de8912bf101e7c31ced047a41343a47a7db8ab41c0be178cc1820c32e852e16979f567811a10040699c7b3416b74da86c308719a1b932a2eb7cdf8bcf8b80d3c2ece080da638289f461b8189c402bac6bebc79362e63dfaceda8e7eb08029fcbdfeae0460d16d288d0e8d1f8a0814e636f83183af396f91eebdc90bd3be601215f6118a7b886e86596574d8d255169448768a9638d0cdf630d2836ea69fcc9ad622112ec00a2174129ed71316632ccda97472292f7dc24e977b11bb6396d58304cdaa29c042e26f5c8bff821e7ff86e3cf6c0a4a67662de826760b1a9de97bf0db685afa2de707a45178cebe04e35f461699d59d496ad5000f237aed77153b7c04674c8cd22b8ed98010c25330e88f8cbc0985e3dc8ae452c50ce4edf5238046883a34199f7c0fcb281da9cea0fa3caa90fa19e42c3bd333596ac6ee10709a3d206a8d5fd2bc5158972dabccc9b8d8bfd1dd170bfced2fbd4de81df8e855ea8346d269d4a61bea886e1af534eae88e9ba6be613d43b1b1eb885bac87815072b894a76f527c80bd41d0bddf4099f04450ed397fd9ce40f71c79b2c6d8837e4ce56b1296b6eedd31619ddad622c7b26db0e68f6c71e015776ff5a668eb061d85a1130c3f7a1da1a9b3c8c21f409102e84f3d5ffa04f216e21b42933176acb9e470a53bf476556331e9c9ef9fa50557ab96a70d152d6cf4ce8e9906c3939cb6f669e0074d8c3574b72630106a9a634440bef8af246e8976b33c593735a054f1d15462261983abd4417435246ac668fb0e77818c97b02976ccafe52bb9c93f8f59104c40345020e5087f06becc3f93e164cd72baa6d06afaf7e498894a4acf1c9a2357127a1400abb74e294f3ee0bb4b83e960bc176f66032d8bafbba0b75577fbfe04fa09e5cda58e202253f89771aed3d02d805fd93bbc1be07870717b0a924782fc3aaee6a6415691a7ceeca1a6dc8c11829f5bc43ea84a90378903054f46b75c76248041d16e6beced50c855506ea670d22c6038e486df9f5c05f988e72236534897a1ac332cb14d22858aaeab685fec5b5fe802343154f86672761ad2b7986afe04fdaf90077eaa3567de196377fc8f211f28e44080b844b0daef2f4fb74ab072357ae4a43d2fcebdc17448c5af2572b32b3f083d85e525a68dea29cfa3f69c47bbe0c82ceebd774e725adc2a886eb282e13eb5978564b86696deffb3214552a2805965926408a32084d0a1de2089858ae0234d8077e1262200a883dc181e6717aca51a4345129528df23d88de428d7279869abbb7ae5d318cd03e93663054d9fdc7357ef6e156085e35938fb3048fc6c34ad01dd5320a751a85a00a70edadab6c4c3078c13ceb8b6901b1534ca13df6c80eaeb2d55268ed6259c231faecc44aec5c80ea18018c036857aa2de6501744c07406da159ebddcdba7aa3f5a22753925c4c0c64be3cc3444027deda8460a04542d300a7a2c7b5120974e4d2a79b0375688416880d4ef13b4288f034e08773a15c0fdabc32fa9dbab4cb88d5cc490834dee911921c7efda6410f3242f998d8927255e2e11a8502100e21e8486109c29fc5d937641c399ada7bca160c2ae59597b41002d437f357821e43a18ee987b0d51db5fbc297f99d2b674391786444af195106cdbbed5f1d42d482ab243fd5bd84d7b22b7f0011a86c412b608ef287b661f85b17926c6f736d27b68a5317fe69f171017894a9e86c2004fe18551bfc997269317ab47f7feb77c548acd38591bffafd19ce3657e6d41e02a0102bedc838dc2b8fc5e3b0f5b23ced23ca0abdfd8a139b23edade29f2dba162e19231e014d4ac645f2edfe926a47a72eb19a0a92211e82e0a8512e7970cf0b8103454499289a1ae9a031ed3c5fe909a94a78a7162538d6cf65cd647534e66e422900adb70e26facede83d5c0f74a58e1c0548caf3c394cd28311e25cc1d9c4550084b46a268fe9b5804af223575f28c3f528896e8038d68edf1aaaa36b3b4817023a3dbc68a69777103bbe534ed26752a078270d61d19d938c2a281095bf1e7466581a28484aecb5d80befe2e0a98c42422d06171f6de6c9b93391483035725f80695d4c727a688fd66a3152d40c38e0e444ba1d51d275bc1ca44754b6381620f71a88059a880a4356047134f066dea2b58beea817c0ebef12c8efac2faf64cf01dde0544e63c41654bf768431fcb461705600e3329af9c80bbdf54cfd893b9df23326f73ae6f68f2d61b324b96690157cb573ddae1e1b8d5e56ba3f593bcc1c051475d53fb74e7e3c2b31f59e814422926668df80b0ad9791daba2f99fbbbcab2db6842bd3925a3d913787fa317cf4a1d24b16bf16449395ec5e9f20b2c510bfb6f72e346b300781d56c9a824280ee41a5dcec4a98e864b2dbcbb7847a43f81d4601ada23e740ffb14f00e017c9f08384878bb4a7cf3c5d04feba2fea7adf30ce2f2a9d28c4b05375606e1a32a3fc8feb711af27d53dbe69e9bf1f030eb11fa050609b6ad46547a06a79c45e81fd4cb335701a8422afa3dd182253a79088f25b57e74941c91d96608025569ea85cb9e2ca43080be45b16ad759b72d2a757317e4a8490417e1cac6b27f836bf91b3a32a29e836beeff2ff9dd09e27b659fbabda53722f5a7a097b6e380b0dbee9e1ff9682f73324d8dfdd1087587ed7eb96ad2468a1da258b069819a0dd916591e2350c910795ef99d9a51ab5e7e528a061556d7effde0f5d085f445012d6ebd3bc7e2994eae111699bafef291c8a2558b1524d0438b06a43b0bbb0b73000f201d38bca0b65ba282b1e8adacd34e5840e2ddc04734b278970a069d780fc3726344956dbddaaa5b7f52e26b27c6b8aae2538d55b3f3cf14e4d0864cd5270ea33f17a634d1efca09cc8d778fcdf3ca83df3a4dd9d79631cd513fab00a7477655e67fb081b89f1c76158255fa493a8ce7c257f154ec42b76bcb5da0ec927ee3c1013388165b5589d018d73ff1ff288a809394db48cef4a6ff8517ca39a8e0cc76559a391a9720fe889f855f9ff4620676597a4c697617317ef9ebcdc085abd3081b34a346f600da8936a425ed975729a5f2ce7adb66736906ed233ba4dd0de82b9cc01667eb05b75e3be60fcb5852bff69cbd3e494f058532af2cf03a6643d7ad7d077694b010b4e3c2fa2d0671cf53084718e1cad7091955ee69dc26c087bf0dec6028c300040654c02871e7ff9f151cd7295aa7f53aa1d54957a761eb74c35b277d9d0c554eb043391b5223789745bc58e42a25d73bb168c4aa23d65bc4186839349c38489d4082a6537a818de221617921cd052998c1653bc894d19e662e0038c8dfe37c25f5b9462c858f98aa78da29a1e96d8ae74f589506421ae6a213b29e98e0e55caf17f63aeaa2d7f694329509106f8adde2490ed0c311d48eec59da748a37a6f23c131cde7d0cabf719e3ba76150aa0f91fe2bafcdd865be3aca11a73bc555838cf8241874cd8b0abdeca96f922aa62780de39bb3148814de60caba335b07a4530ecc0a961b062f884581e5202fbafee812a16b115dd5a871a16b145ded59457804a784a81fbb70f9a76a1100883fccc97e8d25673dd981c5e40039630316143249b7cc5c1e23076e850a7ddf1200d1f3340879b5c8fa218449e5e40546130df869992c48b1187f91c72f2589176374cb3e1d9bb33c37ddef082dfd8a977453838025fbe34f5d834fa1f0af13da4e633da71f39d966a02e3ccd63a102507d3bd5dee90b27f570fa2d1a0895063fea6ce8e15416ce5b2d63fb9638932d78f7a2145c0356979149d1907e2e2e23c540b254ca2946fd938b16831d894805a2dcea071ae1ab4667c94b8b18e85b7e8f06055f1162a69644a13b226d70e6ae0960fe9af1e2524dce6b624c396a71965bce532de9b803cd4de8038b12bd1334a4c2071afcc2cea40929589fc27cc08ec78326e0f9ae7487a15130a5e071f1bf9ebd45fe11b3e60193fb2d88ce77a3e16c4db707735afc44c07ad5070d497b7337cb7a1c9c658db557e3aa671ea8db296e40bf50a803ee0332b4ad4c13ff20f95f3b818fc4fdc05c8158a6b401f274450b80103d4bb980299fbcff9b15155b0aa27dbc01363b27f07ec4f11eb6453ec58a5d0276f104085dc176f2762c8cdd57a20d2c73f5b716afb1de70a01f4f4f9961375233a1f81f20524761a2c790d63cbe76b3fb465dbb5e34ae9c2e9299f2f22adf8b4f7a7caea779a10c861666a34bba9c5aafa25734b874e0442bab47b4af6ee10f2751a6f6a6c099ba54d9c05468556a38ad577f8451e532133f6cb3e780bc5a59e6112760e641371abb3599a406c1e4fcc0fc6d10e0aadc513dc54528f3ca9379c655079e9c52151dc6f8b7f043e73e1b3dbb743948de1feb2bdf737e4c7a7c0d01bf8ec71bb1226a036383d6d629cb5277b21224bfad0a969a6f7effd2dfa7cbe2cae5d82ebd7302122ab82c027aa5ac420a006bd5a41dbe8fd7adf59f3f7a50a23e632690f1f72a6959a0530c82227f4fc3a788e85912938914dc1bb24a5c7e551fc8414dbe97a8b4e49b34b7e4190c8322fca457e314084ab701d77472e0b224e8f54d140427a75d72e31893eb148a100ec19ac095b8d07d206b0d6e1a83e199be92d0e0077989d8f291bccba1a180759068d93e9b6142a57662b3a8326a367198911d95549400e15007501a141d0485abe7a41ed68b05166446ec1e4eeba7a1e458d8465944b330e53f0930819cb6742cb8ec43e1e767a370a1f291908ba587918b0306b73be71a95d4b8b8ec873eb3f38ed9c5ca60004882dbd9169e4c33e4b0e8f22a56a96a7f4a40a2aec7fefbc3faef83f3dfbc8b54093a463ef90f215a0e9bfdeaed7c98b628e33f97030f85e907585de19188a386880fee10c1e154a7f783a5c6cd95309dbd329edf93a9065f1d92080b5f21f0567b67d64299e56542ae888674b329fbc85c8590c9f6ce5d24b59cb9209ede1f6c35c2e089e38dcb45a1f6db14bb2f737d9b96c3967f1492bd7caabab3468e7ee4efe1d060d0fd847424afd77bd31aa307ae9ba89dd70b1071678347accf144f73057736d4b5428e43d923c88d45141d0c0c1ce14206b7a05d13b02148040620f51ea3f7c9528b0fe68959101a4c7b1004dd5e5008be34517ceba6cc390f339599c9f56f498c0618882c2b8a450392bb9b7a8189e9c680bdcace5bb9635cc59d777e7d482b5ce5c9f47b9c52d4902434d52f70e0396c77a176451a5f4a326129cfc14cb39b82289f4d7a8a52b04b3630d8143d2e4c93a977c4aa9a1bc9f4d8ab57f0b4f8cfe02d4ea5612e7efa2e7679e75786a105e9468baf39697a46744c0a38576d12bbddbfb17525468f858bdfeb7685d596b170597f241e95c5196aecc61145a2630761535e354c9a45592427b09ca015687ff28273566c36137462324cb4988c72e669ebe18e1658916f783516d0d654b9270d1e1ed3368b11d81090101688bfc44d9ddfba905fb444c4081b5349fceaa5beb0459fec5ce293c7166fc9ca96fb852d3a564f00ac1fea4d720aa17b7b541f916757f3f5ca76e9a445ce5257ddfc0e5b634f30d435a13329b50ee84874211569afa5416668f7f63b57dfa97ef8ae7476e87023d35d923cdbdffb299c5187b3d263264e6fc78a965b921c6cf1c1232a87ea719849d237316cbc4b0be3d498798509f9023ce20c96ddfb7790a0b733cd2c573dfe596be78ad48a064acec653a9c32cd0805c790c786ce381fccf9fa9d505acdb6e274443a6bade0ec230225e895f8e1e80b1b0e64e8b76913f70baa6385c654199e6ba58162bd9a5007713cc6073fa4f0307dc43127f957365364938503452e9e8b59d9e25434eb611d821b415c68ba2446b108c82e9c4a21dba9ff6cd206aa15c4fccfe48a9bb32bfc69eb50a6594161858b1e3619d3178c48e7e11fa7330b27f5ca8f58fe89cd0ae0003326076123422ce39d112ed30cf3f72152908815c8b1597d5be0c2027d9359dd3c537f3034b8fad11c19655250e90f9cfb725a9dc2038e25f4ecee7c146a05bec0c45ad0264d8ca9b6b87ea4b4e2e6df4295c5dacae8a2d3e6af714e9a64ad80c776c493d0d815922b779248a748ed64caa392445c2d40464205783c11642a9898590a5486ca66607ab9daebef2768b11bafaf4013cfd77daf1f07551d2cb692351e9ed567404361067ee2f83e126a2d08a7e9325a2b0609c0f5214c9d4ba8b3af43e39c5ccb2d040b91a949a636273f996eae271f556320844f7b733c318066bd4259d6f4c0543805316db757992dfa7736bd6cf9c8809911e98c89b3f0fa10d1f2b0e2e6579cfb8a024b980d5521c3fc0890e99962d63b697183b478d205aab035c80d67a6062f16366e132a1a49b697422bc93d1457bbcec84c7c538ac1e78f1b592187eae7c3335ebcc2dfd227942deaa37ac1e03b70b356286ca5506979c4a3e348f33ab2d89082219238fb0d4a06bd41e4c66257770ab8e761395a5ca85416621dd37a56bfbb20dd4990651e02d89d2e8b074b63553a3a2f16615385d6e94cc89848456c4587f2041ecfa832ee793b1c72891ac699f693a9a7837e67bc580fc99c438564829ee52412d4d61abf9a33643e5e46383abd92f802a367b9e1868f248f35a62f5615bca8300e21116be350f075417c7100455b681b88893e21755c65907773f3a60834cc33014e6f4a4986f9daea24f5638da155d68017a56f6613b50b024e9f3ffffffffffff3f5a75dc64e6ffd0b64dd8d6acaf362153da64aa6631da90022ce04a99524a294536ad1406c4a981e02e36428a3d3f0f06061706f1058ad73a465d840b733dc943b361a46b121b5c90193a6a82877e3ea8129aa3c8327ff1401acac0e40e7d8bba724c4678f64e0b2e4cecd0bbcad4f8f27c0a7fc9aff182bfc0f226b0fb1a2f78f7581e062804933af4599072ba2d83b89e488756095fed59a86a128fcea1952df643a5143ae88f4ee4d08bef52f6730e4f3249993874f2a6ab5f060e9d8717fd15ea29ab93ded0885efd5aea8b9d64e41337342b5ad46f3ab233c6b30d7d5e2d335a284dcfe93fc848171ef8c0840d9d66a96ba65cea76195b43635a45965c3dda2a9a0fe6f32520395143fb514f4e680721a2828c9498084cd2d00beb9d62a25cc7930f0d9d346d328b392f68f925cfd0eac7b8ae55468947a5312666e85767b9da41630e71a2084988c862b687cad00bbf794d9614ba7d5532b4a51d7dbe53b3ec388fa1cf168312359ddf5f1cc5d04a55d5f9e596bba490c3d06f901ea4d4cf926f5482a11132fb08d78e599149932fb42e2274901b9b78a153ca75d26e4a8b52b38bc8a40bbda437af4cf12e848c41132eb44ac82c42eaf30f15ae2df45f72765bb75cb8a434d142fba3944cd3e37ba6d52c34a2fea9556749ca9ad2040bbd1ccb3b79f6ea67d2b16072855e0725e389ae0af3193b3a3a3aacd0bacbcc46ed498c6cb70a9dd8e052e876143daa35a1422ff473cf9f1ca185782753e8437b3cffcb31eaee4e071329f42ec89cb74d55355f7c12854e262d782617f78d7d49486a0114daa4cf5c2a29c2b5b48eb1c1e409edbc283e6a969c0e959138a10d231fd34535bbe99080c8f812908f2674ae33ffcd3465a8463d4a9ee447ee823061421b2ac36a59e7b3ec64d610f1f0f4239325f4f12d2af45cd854cff9605b194c94d068419c6f1c8db85dc824098dcee26761b2e775d626c81719c6648c5e6bb9cbabfefae2437ec430091324743ae48328b9bf224d76845eb5e4ad2575d4a183c7088df4d0d3a14ffec1c69322f45a56ed417f943ac3bfb6081322b4657222366657995933376332843ee7bebcd232b48e5aeb1c612284f63f89d16aba07a191528971793799f6aa1320347a72a2c4f774e8fcea1161f2837ec5bc65253754cc073fd846448cdd0a263e684f6c8c9959ec7c215b931ef4a54bcbe8c27772bded079788888c1c274c78d089792d6a2dbe8ed9735c111729f0b8c90efaf78e975a854ecdb2d89cc80ad900da62a2835e162d29d99895ae7b38c941fb237e85074fd79c632662b45aeeb1f2a5cca19a9fe0a0cdb298b5b01b57338a15321f4c6ed08abed635e1aaf55c09c598d8a04fdda76536317152cb59b42a345cd45097e5a3288b36a372316a17ef4909cf079bde5a09188f29d88845f3a9616526d129e4651016bd2cf36521ab3d42bf70e3157dc76f10675ace862bfa8f614597c8d5b2d0ac159dcef3da84562d4dcb723ed858d1896b41e643afdcdebc193656d168c9792db5d259cba1df05614315ad77abbeacbaaefb632b79a099b154f43147917b72e265dd59c0b0818a46f3aeaab869ef368cf722644fd1269961b398f93b88464dd199eae0a66567bb12a92c452f751027a58f16f4810d52b47e32c7c858dee92fe3283a2d89462fd91fa3cb9c45d1e7a885ceac75d2ab1fe30e6c84a271c95f983395396816f40d5034c2fd05f1e1e3cc85fbc1f6246668bdf1896684f4d52eb398926fc1838c743163c3136d0ea2f3a96275923f29d7c0838c74d1d1619d685e90fde2c6fc59a94b192bd9e044a75f12f2a949ab68eb3fd8508c8d4d74da348b1ef4cf548a93183634d1bbca756132686b2313ed661162a4ebec18265a1975788d139bd57479894e676741c755a2d7df3bc3c18625da98c5acfcd31663f490b4fc88c72134b751894eca75298f0d4ab431cb82e8d4397b5b6a7db0cd606312bd996cd93a7cd0c2db013dc4b6c6450a3c7e4312adc7183aeaec22b2257c12730f5202f2838d48344a879694106252ac4cdf21d1cbee2e9a72cd1a4b4b7c44ab3c66a5b6a842cb3c2a8f1b112921c17bd020b1400918ddd15102c63361c3118dcfcb6a9a65fff2331bd16892e526dce3b3987540d86044eba6334ef6282d67e245b4b9559b6f96595e753c031b8ae8d493ff887f5916dbc28968c4c9a02e8ae78f223d685bb08188fe73d62f17f6f376ee107dd0123a3a098d516718ea960fb691f3024a1c9a976597c5165a647ecd53e0d0c8a485724dcdae7df49437f45e9e746e7a10c50d7d78f9e3925c2d78f60ba50d8d6e0dd14a4a4f9ed41783c28656f8ebf8599775bb9cd305ca1a3aedfea8e49ebc901ea586364ba6265ad659c9724179064a1a1a29b37cd9cae38c4641431fbac77418214e9e8ea29ca1d1ebc24baec9fdd18d628656f59674a9528a6d15ca4821237c65e8e5782dc5ac661695a9c8d0ecae282d4b526bf125a9179431b4da3ca354a6525dd0f91a1431b4b27c64bf1ef92547138656e8f5cfd225e4bd5f4601436fa2b75c5c5466f6057f2f682f50bed08b62736cf0dcb14bb35c012fd29f809de28546e7f120a3c89432bac993888cfd00a50b7d8b55a149745d6495346a7878212222e398081cb005320a171a31e5aa5d9406d33f53b6d08eeed396a4ccd14227b4167f54479565328b0ee822031808e33b40839285e64c9c0725a3ac89080b9dfe18d5620a2d8f7bce15fa8def396cfe97655cc60a6d5611277794a8bfd0f2c136e3f90b4a15fa36752d79308d0b142a74bae598e8d65a8e52949429f4b2f239c8ac5bfbbf16302852e873c44f7a7ac69c6f69b428b4639eb3fc13ca747586840285f665adc5cc30de4a674c1eca135a552e6810cf6f1756fb6053ee41928c9154c00217f05f120d1284f11da001c509cd2bbda2a2cce583294d687d63521a94d48f88d821284c588330dd428608ca12da9c05f528856c0ba237e6284ae89314f285d01e2f0b32ea838d928456c6ffe924ef2221c708ca188d8869be16bc119105040f32d2050509fdc930ae32e7f883cd0e0a94233426640799b525d7f9c44668d344e4bc2ce3ebf911845284d6c5cf7221e7c3b91013a1ed141fb598bc853284fe5ffc9be76d9da95d1421f471b5357aa9164f36a604a173416a665fdfd91793284068c34573d145e70fb68e0e559303e5079dfa7bf40ceea15ad4a1f8a07f97a5662d683840e941a35dee8f525ff424b5a4f0a017859259ebbc3d6507ed7ffb8acf7216942aa9c92d42e001076060040e30435074d00b9e517514fea65c67e5a0d7af5b7416856a25834c11a34f1eae7ace5f8e6669808283de7ffefde5d311eda2dca095ee723c8b593299942836e854b43ba6cb2afdb3f02c5af973f19dff4af42f8b5669181d85f2a0492c7ae14569d2c55d6dc17cede8e8e820a121328145f3fe1d3c5f9974a935af685d6b69e51944a3a7c84e161357f4b27a5e8be9721798b4a25f99c5d0a76b4a756b9885092bfa6ca1a6a3b7fcd2e34835601598aca26f5fe1f3da2d6e327f8b0719e9c2e005c007135534264c8af6743954eb2a5282f62ec245be8b84e499a4a2cf50ca4babd851d12759f57449b6382b5f9353f4ab42bcb8dee1aeb733455b1abbb4bc7c2932c931a5e847b4bb9065ea321b0cf94264041352b45a4c615afba877ba46d12a7d4983c993e2494b5d0726a2e8448b91cf545197b3ac199a84a2571a44746a8cd692f983cdc343928c74088a66a5ee9ae9be7abf663bf944bb5a4b4a76569b66c31019292179674ff4797577f36731c4688449275a1defdda67135b5ecf931e144ef627073f11518206b13bdf2289ba54b5a3db88e26daa4da7242695988143d137db94b26f36bcbe6ce269868ff3c4797543256e7d52797e8c3e5c64b95d76d416b44c4e34340648480e4c412edb6d219a554fdd49031a944233f4fc37b67972bda84126d8e21de94c6cd161d1d9349742e4b379f96c4ebe857129d9a89ac36e97a5def129844a28f195a7cb66c9ab4d89940a251253aa97693b947cb8fa47531e281c923da759d5f4bfa6435ff693f48d2870b268ee8d47b83588f21ee1b73300c9346b4b9725bb607e1df529321e2c1c6885e4ebaf95f16a5e77897b660b2883eee69e145cdc977a488fe375f6b29364bb21bff60fb16842491124b44bf713a1e462899b4271d062688e82529f2b5d662cb31662ce40b11c543f43a5354aee96bf1a56a8b0f92a4817334d20c43b44a6795426bccd0129a49217a935765baa9f253c7413c0c041342f4497bcefc749599f3a88358687cd9b925931144739efd3b5fc768dd54203a73b147cfc50bad5180e8e4b4c93871de1fdaf8999592aaf9a1f3e02d74925aac0ffd4a25aefb65e9e92fc48756a7ef8bf30f32c8eca1d55276eb6c41c8d8440fb70ca6066f9ed470250e98cb6a51c3f94f83460d38b42d5b099fcfe2e12120eb2a6fe8c3b6b8419ca72a6ee85f33645d6c8d0c2a6d68e527a5a23543e7ea9e0d6d9e4e4fe932c7d3111a43650d8d267193a24946b6cb78580c2a6ae8e514191964338316afa4a1f75622f4c969ea1751050d8dfea9c6d3799e33345ad07a4d89485dd2257df0032a66e85bdc997f93d5318fab5286c6c73b5d63540b446490e17d39cbed59461fa88ca1dfd62952b78568699e268646066d5278c6534a942103c48b30f4efb248d1827849c991220143eb310ad5d4dd4a88f7bed02a132bcd65e46783ee853e33aede959e54b58ba3a0d2855674ebd37271a1dd2c890e9e26e59918b7d04af3111d574fb67ab9828a163a99c157f775140f427626a864a14d9a4c66cce0a2705989ad8b0a16fad4d75188bb98d56222b9421fabb3696f69525a9659a1cf2ec7922d26ad65f1af0a8dee8c32b3e83266d682a8d0ac0a511e63bf781d4b0c54a6d0e8d6e2bb2ef19c6b1e0e54a4d06b6608e9fe3acae4adae44a15155d74968959eb4ac8142a7bbc46a10f15f3aeb8e95a0f28456b949f1397ed209bd1cb39c4a13dadc5ace1b84d07234a505418509bd2c5234334b9a31b91c5d82a97628a1d72ab6cf65d3598b62e5e1b1a7814a125af5f0df17fb22843e6d8dca18bdd817eeab594568b95441422f74d054a5d3ad1ca197b33e9785d2c246e83bbbcd657d52ab9679111a999ef5e9a7ea2c491d5788d0ae56aeb9e36b2d26a32a4368b3706e1e42988cf29b8e0ead4145089d0cc295779b57094223457634e653a5538e60bc063a3ac0f88f0a10daac215384ecdcd8d06283ca0f3a31cf4a68950fc2c5aea3032b3ee8a590ab596e9df12c8bd28e4a0f5ad3e28feed714a35aeb3d4644acc2833637e648215dc8b860fcc7486507ad7bb78b59f8cf26f47e828a0efa159bda452d64a9872d05951cf47efadc63f706d99a71912d2a62b4d9a4d0de6288eade4e05079d3231152965cea531566ed008cf4d2ea96b9da6bb1011120e546cd0c87c66ca44cb7810266516bdb81debbaa3c8d4a0c9a2f31fd37e4a6584abba012e904089459f31e7cb29fbf34a8f29b0e87454b9ea5b9e63962ee5156dfcd272cb2ce9384a8a2b7a59b896fe6a1ecf534b2b7a5950de92eca8d6aedbb35058d1acde8ddda37f8466e147494949a2aca29131492dc8b85296961445156dbae4b2d145a60220945434daa3d2a05367ec7ca92384828ace35773d55c77239c81209094849489a71c22829f9111a5b238c11e422051e14a09ca2f10e716155b6b9644a53f45ad249f99be90e797a4707ba81528ac647b32077bc34cb1a2545a75b4b837a986ba9df283aed10da93d4625b4ebe285add12f54edde2579442d16972a5e5ceea624b0acd1528a0684468164b5b6741a8ceef3ed1aad2ef496759ce2964eece13ad8c424d5b88b8f83ac24eb4be42b8aa4c5dbadc855aa070a26da9dbc5f692afe5aa1037d19b698eaea3fc786146889a68658c763d29f23fa30c31138dce6edaf3a4fc9ce50b265af7d1e19b4109efdf2997e8fc833c2db98a96e8b524dff733474a257ad995942bbc458bb22d0a257af11eda1e5e18a9e25126d1fbaa6b8ef382fe8e67a624dacfd332e7b973efcf9168633cada75faba8408144eb4a988837d1ca4f4a0a944774da47c62c8c78139b521cd166c8ac0bb332b569e5189446b4512a3d5acee28e14bb6144ff2dbc9fc986295dba45342ecb75c8cea8758b1e452c65534bfac5d3424944bbe5e1e5b976395c932888e885cfa25e6d619494e1443944e3f3f24b775c9ee74931442ff38c92396bf26462a714a2195d4f5a6c8f93ea61205008d16a952b7f535c94f90e6510bdce8f152b4a8582688376a96511cdec778d12883e892fb127665200d1e6524a7bcf33c9869cf287f674d0b2a02fa60e5a2c297e683ef797ac6b52527d8548e943ab9d84674bcde042af287c685fb91c27ab95900c287b68449778b1e57466f1f27ae8b32c5f946a930ddd581eda962195d6390a653abef0d0e68cee311d77d7f1a2dca16d2da58ce2292ff66987563c658fabd8122f531d7aad5a16a327391f3af4dacb4c7a7e96b58cba9d43a7310b5722cba15fd95ad41d21c54b10608b009a08e4996bc0256473fff48006980ee60e63c05242526384005a84a12290002d48467ea9e122233410300112101a29015a7401005a004002140b00808444750c2000a924790701b44825c945463a0c0000012c0001235f12022242c3c38b25959078785000a085020c4063461c5a9793d69ba33438b4a9ef2d8e7c990aeddf60000e37b4415f5615ed1da588cb8c9fc1cbd18630682c6ce8c40997abeff2ca673496193f6367fc0ce558038294b8c80c352048090c40bc8687070238d2d0694144d344888fceec83cf598db3193fe2010310f7f08f0108b2921010240901291101071ad0e3001c67409092910f12191e1e08e030038294c8f820491e1e08e02843c993ccf81905e020439b3d458bd1b9f4cbd7738ca177f1efa084afc9f11322a121e26124c021863669e98d7d490a069f3212c80c919030648884e01f03e74830c011865e5e7d42655052f7cbc0d07f488f97265c5f3eff0bc817d5f1674fe9855e74295aabb864a6e51247175a1959dac445dd0eed7270a1d1d8b2d705973d298538b6d0e8bc3aa70ee2f2943cb5d077389de7c842ab63cb8f855edd5bcc3265fee8a62bb4418911f1cfa3d2756885fe738b579a7b5c6e515f85e694fae6aede1783cf54e8f5ee480f1dcd29b4264a9a877c415c7a96147a1132b347b4cb3a891885d6b3d0329c5c5da18510141ad97c416a19261b75fc09bdd6ac7c7eb4907b5ac8097dcf790c328bfac596d326f4e2af4e4a999c99d089f220b5e7f19660d0f192b6e4f2f8ec0b2e0e25f41e1a9e9527d59effc2918446e6ee66c94c5cebbc02e118a31323564f95aec69117a90b0e24342f079de4bae8cb5a8e721ca1f7d7d07e71f445a145cf54c061845e143a07a97465337527111c4568a58e598731a93db7441c44e864b82c29e51a442721e218422b2b574bd9ec9a4c9e520c0e21f432c4b48e3266ff65c911843e8b788e22f3b9a0b5280710da972532a7ca98839c0ec70f9a7f5f9df145950ffa9843370a97d94a8cb4078dd09be2a4c676e5a7e2412f72c5cb2d73f8fccc0e5a57b9b21da412abcca383e6f4775bf2ffa042ec39e864a4d25ae4fcc778e210a3558f3a26a5c425a565eed81b70e0a0cfd9a5de98d5d6ad8584c07183de5d0bda723c06cfe297040e1b74ca3beaf3d4963474668d5099455fc25c4621325ba7d03a48a8c8a219257bda9289bea434b1e857dbc4b9ac4d266dbd028bbe5d35af4c5abcc7ea9cca2b1a712275872f174457f4920c42ef6ba815cd997cfd26741059d166dd1e34094f269a52c455b45a842edf1ebde1ff85a88ac645758cd2b2a0e3670f31157d7c3926ade57841aca0a2d7b2acf398f438f2f93b451b857ef01339c2654c229aa2f3a037695d132f9d5ac452b461e5695c8d597ed54947a890a2d1199a2f0b22324bc21f452bc74db69c55b1db491551f44908152f2195abb7d009a1128ace6414bbe34acbafc5a4ac80a297b5fb6b1ccd42954ff4fafda75d7e522156ef895e6bf9da64aeca70c24a273a7962bef283542ec75ee144fff2e68e39ea6c13bdd4abe56e5127dda0a2894e5f922f7e5e1f6dfa729968438c8b1f9bafd23358c1442fc56a614dfef36b59ae72893e34bb187358cd9f65eace18154bf4ad237309df8f23e3a55289468a589d5dc9925ae75229d14baeff5a6ac984cbab328955d4519144e7ade6ab5d2e2d329b2261c8aca3fd5913abbbb253664b530512fd0621dbf9c7057d3ae5e1cc45e5119dbb7770dd1ef24447c711ad68fa7a76cb3157b570237afda8b143e57458d92e235acf7149f5abd03faad245341f334761fea6227a414891da724cc24a22fa24aac545354544f42e63b2b5bd93a9ac9543b451bc2c7e4c42688866bb648894274c15540ad186f595a5322aed925222219a0df26ae2c3f5e795421e449f2194d4d69f3f14441f5dd6e5b73693215b25109d784cf91679edb208106de806a97390a10f361210351382ca1f9a9323b5a0ea223673be8a1fda9ce18571f9f3953e742295d2f25c169be538e2c3314aed727b686695d0152133a232bc8a1eda7c1d21a2b56e772da792873e7e78e69d4fe52374100a153cf42b7377163b7c73d8633d50010b80c00319d83bf4627c699a6bdff0b2961d9a3f8fbdbfeb316490a943fbb15cd47de1e5cee37f21b24547c7172223237468feb352fa92fe9539f471736672e51b7ab48c1cfad66a62728700a8f3d7698e632006621806411044b16a297483124820182c1e8dc6a3f2906053753f1340410a64219128180b846114885110c7400c8330088220088418438c525049087d8c42284a9a5a80ce09c8f14effa35b1e5c0cf00fd99918a166d2e623d4fd31b0718d1fc19476a6f6f26340333cfcd205073f20cfd84930b953ba864c9560b497a0b478355c6098d00e44d00222b480b6b5cc54ae3d846dde73eeea6d9bcf80cf5e4320642b8ff07b7e53349e555ffca1bbc4295f7026d25abec0eb67c9624d453efb08594575ad7d86aa7c70b0210bc95211e6a1928a3672e9bc0978f1ce41cd2abbf40950e78fb39b125c1a754557cc4dd0875dd3cd4b41d53ad6d32b0bda68fd3769c3faa6f8d5edb4b9d3d24af350db83c5fa3566cc41a1da5c602f7c152e65ac0b176c3b5bc67a0884475d878e93de09c475201875df6ec959307df679c3720bdbd817c0f003a22c38fe1cc55c4b156a572f69490454e9953e1d8580742d29d20279465b33746ab312084c0ff55e2591fbca6d4c1a894dce061e5fea6907ddcccb397f5fb5a3241b20256067105e40e40f589b855f04a1a85625762390f25ef7908780a8e26731d53c91a005bdf765d48dbf8dacf0c780f80ac6f9c9e4ec8e52c58d4d0d31fa3f6ccc3cc15e6b4af5d33d04a946a7c90669eb9e20fbf6899354e1a5e9f9a2bf32d1a797260cb3787a1944cafd4f37f5547f8e55925d8bfed31dc2d681092575788e6384e501c8c9c354b71ce6f3c64a176e2303f1a95263743adb163895541b422b646a82d17519fd4b5348698c7d3ff45ab961104d68fc09083619b4b1cb43bfe873965164df7ab2416dce64e7cfc5300b80a93604134c9d86551795d2ba172cfb8a3021e01b6eeac39d602cb75f33050b210f574eac108ca730a094b33e338c7cdb0b44ec6f5f9f37b6eb8b76eebec1e6efb0ec82d58bb4654ae553dbbf2a7561190ec657286d13648c93d3927c0c816ef17c720a665e66819ec7c0c27e8201e215fbbcc0e7c5cdb7a4beb5d6e0614e48b3c95a668b6fa1a033b1c381307372e266bd9553da8aa43d49395f5d0d87756a24c3d70c70802de043304def10e516d82982f16628d008782471daf0a884de9ef933d6cd0113edf8233b9e7f29ef863f8077eb1c997d7f288a90f3ca9b9a29bd482ac24f78fff2524d9cdc283d7e907464670251172e85026735fd130337a2d367d08e7f29b87a62606a9a6ff7c78443b3b8fe8a1668107dd26fc802c14a435dc2f8a6f8d9b4d8c8c4fb95297e5f4b052c154e618e685350dfc075617607438acf71cb1466d758e5fddf4e45a8949450ee868cd5548e6896267740e2c7f068b4146dd8a6b7638bd50a438c19c7c5d350a2e2c4a7b6b406c5082dca7e42cf4e38c2ca08066ba4a933fa44f5e601c63bcb28096e46c5e5cc17d08576d17ea464d51e00739ad4f7df00400734594683cf23175c73d90e9495965d9ab000bef38ac00891389e5164d95084912ad9f87052ec61042091484ffc18b6926af68970c502b92fe83c52cfced0f24b1026f1735f144c8cd9c4101e4383e37ed0be3adf3fd939adda9d43cfdade02a688a89a66caf157617667747aa30433090dd89efe5600cadea8e43275106c72f39d079b9c8517d02583d3dd95e24a04670c8f202a9a0b026f32ed0ebd761fafebb7261375c848d7552246fffc821b4bbd56683a680fddb80f8676ff302371888d10e44ca4de4181836d4a908513b487d6f6cae53428c098a678eeefbbc2953ea9abb91cef264a61a5be777370e5139ba0803b36ae7d046205249ffb245a1059bfb34207166bd88001f7a33844b7de95532ac2d034196d081435b291b751ad1414362d5e29c8954ae1ff4258409b512d9a31364d18d5013f81c4fb70e9ba92d8642a7a6bfddbd5f999683fbf65b6e26813e72fd7a536a222347bbc3b849ecd1c65164bd2b208b9c0099d96685cc7cb7119a8b0cf8cea201c066a9e439e9eea7381dc299faa272d042b1b059646803b1d412d00f60177acc9047f44f16cb56068a646c65fa0db6903a20d9bc578e9f6fd79bda20d3a013af4e064cde8ac33b782b3cf10e4c4f8cfd5acd8834b249457194eedaa9f45944a40d3418922c96d8412cfdce54d263a8f19523c9124bb3cd008d100dd9cc28689123ecc6f520811727a7f342e3116dd4c8b0e6df1f4b5265ffd0ccb7fa19ee33c1a4523e35cdbeee225f736d1c3ad1063d9c95b92f5ee226d2de60aa1a1f9f1702e9597920cdb1f22ae3af2d017d406acda32818d593a43fe3bbe5d9b2c6080fe89ef8ff880d190666e9a182156398b7d084c3e1d8ac4a652ead291fc80270d6d0a70da12e92a50021d06dd7046c003a21841e8905ae1b0526aa4611a93390ba4f1fc4068c23353324628232058dad265055fc16935ff8d32b15605ed139ada7304214b5d9e077ca66d04a8120686f38d0a2b2f2c7d35f06f6cfb63e9c9407f4bea7f66257b5c5253ac772145736bb92e1519272335486343c401e3834f833119a9bae58804056ebc794ac47674bb643e4f1a5a97767c8997e12aeb294f2c2ce720dda9df94739659964cf2bceb4425e0116932ee737a3aac87c9a5d8ccf48603c19bae19676ecb9a54bdee176573fa196df720c58fc3c8023af80d3528c74d5334ace3bfba6db2e5f8cab477d76a89e035e7000179a56812e9aba40bdfd9429aa6f61f30e6c2ea9952b212b3a29b9e756a5d0eae834ac4d239d3a97e3c823f7ecd93b38a18a20d2fad3b8c8896e2b56745522e452b8e31a48133e7132cc63a9f038a34e6f69cf20f02bbc59162e421ac3d5cab2e14cd24d54950088273d8526fc9b8a9682e0ba8d2d99e28c1b69624fd042e55e6b5a82d4147e7662c5fd6f98965e961c7fe02f42e64bad67f51ed87f2e5b6ad9e28653006faabbe35caed86f1fff6aa0f7e17134710dbe53bda3626cb45c264fa95c82446f055d8160312e8f5643a5e46e3951baf3ab76b2771b305f429cec92ad8c85f7d840ad420c43710319422c19d2c0ec7bd329f76e0c5ecdabbbcb087ed83803fbdc7ad942bc7daf61305c81383f640af89cf15356268ca0b0a33a8649199f34f51a3b8ba5aa455c36379fcfded20fa89d55cb83dfe3b79443e877a87c53669ab8e8679b849918de8127c8d110fba5773d3316f6384784b4ec3a1fc13387dd737ca61b5b24ec0441cf78b49af0b62202d7900d452a36f66d46cadea0039bd97b75ecbf9eb6832b0dd69bb8b4e35a7e842a3ab28ba7e1d450d5128104e5a6921487c8bdc5dcce0ccc446aee3bf70123258bf44a0d5b12d41304d2cc90cff2885088dcb4e3602c0f467f5e84ac3b70d3da35fdfccb063206964a662db020dc12741959586a57515b7ba5fb5d230e94f4cf208643b233f759bb011e4c3281fd090894c900c14f5d828e03d8907caba45b0417061411298019522f061762c16d18fc9ca895380707409f7421ebdd5d7ce8e7522bf82ca92866161329a6fe9b8da926ef3dedbe6097cfb3119912b29a7f668b70123906f073d8a93b263e582cd07bd9f67ffc856b6668b4160ada286e748b6a6dd8c6f66103b86ae668b4ca7e1ae7560112ba2c5020fd0c40815cb312ab4071f3b6c65e8b4de298a5f7253c24f2fdb6ce22dc615fd07f6660df59340eebaee2c5df108b5bee781bf506968598c6d058d9e5c6433a6b596121413d8f29f374f07a41b1e23ed06ab60f939ab91e8f4199361bce396e002b9d3b9f6f0eca5d9d2cba910935b65b7c80f1d56a630568f528dadc45afcb54ead42c7e0ed30fb618a1c0a29d37152d4480f3a8e45794d0a6f8e25c4efba283bcc40206e727203c42ddfe8fd5f5b32eb506adcc6be860a7a14486cad48d3bdfb678252a09b0abc2c6cc129280925aa1e21224208b8858880066d9c43d47323335bf32a6f0e0eaef159ad5a0edcef19ff808f5553416839cb011a2920de7d84ce660fc97f42183e1c3d2f25a50f2f40280bd03a4882f6ea94473c602b4d781220593442116d58a7b060a31145d5338fa6d5fe1a3f28083bf7d5d2316995f2a0c25ee450268bcee0c422108fe00b787a8ed771dc09caa8a19e544155931e27a21a41af6c9e0da7aac85ed9f3231f19fb37755c27e5388fa398ed58a2779eb32c883b8b4cbbb19074d717401261ac140e30bf4d8e5595a28df0d16ef1837538686b7b0068a1c43d9d98297c1c27e6b4fe53623c181876aa00195dc252bc00c59e9903aefc70179a6d8f041938116ac79d10ee3053561f532d4ca0d4fa8ee0d07391c10d320025960e04413329ec44642929e0209309b156ebfc3a523802c76e97cb60c3f9ed14cd24504e750a40f9fd19cf55c851874ebcd0155f4d5c9b6a552c00026a0ed9502f73e0a61937b3ec62e31109e13fb149ba42c354e02b48017800af2fc093da1af277904e69b7d35519276303ea29cb93cc55b341879212c2d19775815c1ffed7a876aed6918a881c2a3c60411c39d7762c4430ab6335c6a63ba703539865dbb72c1b901c3473d38cc613edd91e6bdec19baf9471eb85799ab59b7c414f55c352c3932716cffa96206a540210b5e1a2fdff0c8e96347fa2a0386381a8c442ef0dd12845b7d5a44394d70c70356e5a59aa2d9562acab773217d3a5a995f9da3cf77c646950bacd3c1cfbc0a5207dd893b80ada78b5443a0e4e8b9f03d94b69d73f2204837a2732348ce2e7c3c882b3a097aaac57e50a59421a23a90cf935f54e04cce6525628a9a32d72047c09160021ab3800cecd21d69c819978aadb9b022949a4f64e2c58eac3965834984898b78fedaccd90ca9657f17a4e060a60d20b88b262665440cc3ea9538cdb481adb511a8bb1e77d7c32df1ab024b47792a490b94d59be789fa74e224a8a84158b0f59757b5f940d530a0f17228cfddad8fd244380a3f29e44eef1414fbd9595cf55b5facbb448fd064b866d3e2cd2e323da471c8a58153772a8a90c705d6782052093579e9ff8ad43dce41f9761135756be85a8f56b0a721612e6f1ef0a1952038ee6ce3aad87102315a21f931029ab1c69032342e1d5dc2db3ad0db122851ff15538b526cd2ba530065c9853b4881f9eb6adb50dec8424009ac088181fb129d09887bee4a80a9f6a2f087f4056545ed921b640993f22a786f29a4a0e4df652674cea284f0dd0ccbdc9caf7ac72dc28a68bb3937f4b91a684e55ad6d2de6a2bc5343a0872401dac70ac8fd04d175a64fe080a42856d905d5904ea5d9703bade82a32b0edf2530049026a3a3698648fa58315190909ec6c6c8bec9105ef0e42d4b60cff90232eeb69426d6c69c16a0aa89b9caef1987b78b76e168800584ab39dab625c2246cad95258463e668dfb9f07e7cbdeb69e9c3ee062dcaddf393600308d4cbc3c0a023dd9f5d426d188bf226cef27feb0459652f8cc6ea0d45e50366ea7218a53ebee918f0d459bbbf814d78050cf36139e0f67ac975d3d5302fe6610ad1e6b7ba3067daf178d8e94fb01ab949a451a4f243a052ea513a531570cf917711cdcf0be1fcae6459fc16d76a454c7e9d4a42b5f2fc1e0d7a860382d687745a5cedb6d2159295bafb054598c825e34f0ce3e0e6f4d6d9826eec0d69ca4f01dcc73b060de25b139b1a6c5bde6b25e32fbbd1f6ccd887d056d95830549b16a734ca8084c80271ae13476cfa3894b08498fd3792beab33a630ba0753598f69a3dc49a5ba40b68721bf7a83224684a265e8cd8c1da58f4d2aab9e8935e44d76f69007a5d3015aeb45f880a8d56540add2f1dfd0603c07b6a6b1919d3b3718ac9ae6739e4061cd01bdd125496d3cedbe3c30aa70b9169604fded10ee39ce1721728e7c0cc58406a0ce9fa27b48fda069660a9013d1204c38ba97daeb64205f8e5e17d9d230426dabb02ed0a28a39b617a43edf07e67e63e48bd526be19b41799ba284cae479416a672c6f23f5d45af0632fda329402eabc9cc9abce400aed3303a41f88e98c2c38a5abf6992047f3dca192dc49832712f771a7774719348cce5d5cde86c17a33238f55182a9a6b2934286a4bfc9984aa9d4ebb0d2d5e64474ba22afc371045dd9b74011b696e071603d71c3d18e6bcdaddc892aacba3227d1698503136b94049c940d4afb54d84e86368d49348b087c0675f1128d48b7d1af5748271f1ac745e7e69ca61af535c26e4d647273506d04b571ef8dd4fd19df2850ecc41d15f02497a7c8a55b821e61af08a36c31bae8af97cc5befa9958f42e1ec3b52fe31434c227f501c26ba6bda3cd5d25f130f0f0ef9eaf9426bdc0acab81a0d6fee554c741c03b21cdc635156d0acb0ab833776e236b6aa20db9b92118f1215472362b0d9c92d8798ee0a3e9fc2edc4133a6fd258f34dd2639cc01ebc7e4bdc735032ef4b7288cdeef2be7c8224689ef8a0cfa631ed89c7ecaa70373cccc2f88fddda0051f39d022228961d7f36fec1fe5150b47db8dec2cb11f9c84c10561446529f27abe5b8c61516625720d45980306d6e5801a4ae0ee88062d95670bfc255da801ffd8ca4f1533c46f9fdb227f3c43df4bde2d2031ae83d34e5a3befcabd580c0b2e0f68e62133f8fe9f413063c0783b8af300f27c8fc8099b3d2b0d18260b0d17ebee8575a82202cb5adfe58215f9ae5b630f79a09a28fdb6d2253d9e9a88890e6d9b7301e6e0308d46f6ebd90004431b0ccad3766ae687bca3754c66156dce32361f22d19a22b41dafb789e52419284f8970c11458aa29cee23c04a60d28a481678b9bfab05c69d2d3bca24784d24df9ffacf64ef2e58c6cc422b64fa8f94acdcfe7358d162dcaeef2f2385c5d1100472b6c5b4a39e2409645981e032ac4f4804615c609d557266737058dfb4df7e02d677f8268ceb8021d046e0e6d36939f33248e4836ca5971d9ead54b663ba78bad79db9ec2363ad84eee7965122f8c72bccc8c77e58ead69b7f75269bfa7a4ebdc0a8534a6a85463c508ae3c7b6ceac5098da26bbbeac2d0c4aa1350eac571dd8509c422dbfda1d620bd8e555150aaac5b45d14cf4330642961886caf4aa11e38870ba4ddbd5ebc6375da1f3161a272fde3850f17f9df4b22771ea04eebda35722991cb395eba03d4c1ead9e1c201cc2565d6e2a4f962c98608c316e8a5eb752ccc896c5372fb060a36f06fac899de4f5e321af499244712bb8f956c77be54b0bac8b95f82402060baaab4ba56dc6aaf169954f94061d016c1f3baa31ed36f6068f4e11f8ae5b31d3611526127c74eb99c12e473dd6e40b117eb232a21e162ca540fa5507273534d2a8945b9f703eb6327553a191fc7c0f1eb5ffcf7375a62ad474025829389049092851fed7900e99423283b38d197828c024bcab3f68d932478a143cfc44e08355a473221f2d2a596103ecf4adcbb0ac35945f5c825e17509d9c495b82b4074af63c08", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0x3c311d57d4daf52904616cf69648081e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3c311d57d4daf52904616cf69648081e5e0621c4869aa60c02be9adcc98a0d1d": "0x1c6cd3a8447dd3cbde9a566f35589b7cf25e924bf194b8fca62f3f6797170afe0816dbd3631a6f0aa8831db582ef298fed529d4233253948c4660b47956c4dc01f9686fc719cfdcb5fd9ca74f36f149730171b5f307144ae40db51c3aeb506fa285282d2eafa50e9f77c6089baf9bd1a042d623b28151999ee24ed838e33ca6b64dac2170cc094d7a47fa2b1b8844d40f1a5c9b82358997809f4fa08b1c7e92d7b54461b86f1d81ae23ee86265efac1db524bded8f3eb443d059ab0dee2804f951a483fa77b505877527c4a44ee2ddd246ad66ac6c33e4349d4e83742d779b3a41", + "0x3f1467a096bcd71a5b6a0c8155e20810308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0x3f1467a096bcd71a5b6a0c8155e208103f2edf3bdf381debe331ab7446addfdc": "0x000064a7b3b6e00d0000000000000000", + "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0200", + "0x57f8dc2f5ab09467896f47300f0424384e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x57f8dc2f5ab09467896f47300f0424385e0621c4869aa60c02be9adcc98a0d1d": "0x1c6cd3a8447dd3cbde9a566f35589b7cf25e924bf194b8fca62f3f6797170afe0816dbd3631a6f0aa8831db582ef298fed529d4233253948c4660b47956c4dc01f9686fc719cfdcb5fd9ca74f36f149730171b5f307144ae40db51c3aeb506fa285282d2eafa50e9f77c6089baf9bd1a042d623b28151999ee24ed838e33ca6b64dac2170cc094d7a47fa2b1b8844d40f1a5c9b82358997809f4fa08b1c7e92d7b54461b86f1d81ae23ee86265efac1db524bded8f3eb443d059ab0dee2804f951a483fa77b505877527c4a44ee2ddd246ad66ac6c33e4349d4e83742d779b3a41", + "0x6dd12b3ae7975bb95f841f4505bc193c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x7474449cca95dc5d0c00e71735a6d17d4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x79e2fe5d327165001f8232643023ed8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x7b3237373ffdfeb1cab4222e3b520d6b4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0xb8753e9383841da95f7b8871e5de32694e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00000000000000000000000000000000", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb32d8a863b519f21b700f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127": "0x6cd3a8447dd3cbde9a566f35589b7cf25e924bf194b8fca62f3f6797170afe08", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb33e342a1016938ec896c8cf62f725741306fa59a240c8a721f1daa9a6374897e4530a33e5e0b0fe5b": "0xdac2170cc094d7a47fa2b1b8844d40f1a5c9b82358997809f4fa08b1c7e92d7b", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb343359886e6935e4e1a306dd46ee404511af11b9123964dbf1ec087aef60b766f04de5d9489101977": "0x9686fc719cfdcb5fd9ca74f36f149730171b5f307144ae40db51c3aeb506fa28", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb360dca291bc2a34a308c4107d205432aa56d2018d46daa0b820669d068b11abb5588584f05d493507": "0x16dbd3631a6f0aa8831db582ef298fed529d4233253948c4660b47956c4dc01f", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3a191710c236b8b5ce89a539b16e7cf02cde91ab273572701a7fb7bf712212a75d2d48cde142e2b40": "0xa483fa77b505877527c4a44ee2ddd246ad66ac6c33e4349d4e83742d779b3a41", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3a7500a3d1398ee4caaff773e3f134d4e0c90ef5e5f37adb603bb591b7aadf67a2a757101a1b70302": "0x54461b86f1d81ae23ee86265efac1db524bded8f3eb443d059ab0dee2804f951", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3d30ebbc69dfd05038eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0x5282d2eafa50e9f77c6089baf9bd1a042d623b28151999ee24ed838e33ca6b64", + "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19501b00830156003f1d617572618054461b86f1d81ae23ee86265efac1db524bded8f3eb443d059ab0dee2804f951": "0xaaff773e3f134d4e0c90ef5e5f37adb603bb591b7aadf67a2a757101a1b70302", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19508c3336e1e98c9dc261757261806cd3a8447dd3cbde9a566f35589b7cf25e924bf194b8fca62f3f6797170afe08": "0x00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195099bf45ac30c1b87961757261805282d2eafa50e9f77c6089baf9bd1a042d623b28151999ee24ed838e33ca6b64": "0x8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d68ba9279abdf09a6175726180dac2170cc094d7a47fa2b1b8844d40f1a5c9b82358997809f4fa08b1c7e92d7b": "0x96c8cf62f725741306fa59a240c8a721f1daa9a6374897e4530a33e5e0b0fe5b", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950ecd6beec3c27dffe6175726180a483fa77b505877527c4a44ee2ddd246ad66ac6c33e4349d4e83742d779b3a41": "0xe89a539b16e7cf02cde91ab273572701a7fb7bf712212a75d2d48cde142e2b40", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950f03f94250d72f548617572618016dbd3631a6f0aa8831db582ef298fed529d4233253948c4660b47956c4dc01f": "0x08c4107d205432aa56d2018d46daa0b820669d068b11abb5588584f05d493507", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950f2cfd82322aea31461757261809686fc719cfdcb5fd9ca74f36f149730171b5f307144ae40db51c3aeb506fa28": "0x1a306dd46ee404511af11b9123964dbf1ec087aef60b766f04de5d9489101977", + "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x1c00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c12708c4107d205432aa56d2018d46daa0b820669d068b11abb5588584f05d4935071a306dd46ee404511af11b9123964dbf1ec087aef60b766f04de5d94891019778eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b86896496c8cf62f725741306fa59a240c8a721f1daa9a6374897e4530a33e5e0b0fe5baaff773e3f134d4e0c90ef5e5f37adb603bb591b7aadf67a2a757101a1b70302e89a539b16e7cf02cde91ab273572701a7fb7bf712212a75d2d48cde142e2b40", + "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x1c00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c1276cd3a8447dd3cbde9a566f35589b7cf25e924bf194b8fca62f3f6797170afe0808c4107d205432aa56d2018d46daa0b820669d068b11abb5588584f05d49350716dbd3631a6f0aa8831db582ef298fed529d4233253948c4660b47956c4dc01f1a306dd46ee404511af11b9123964dbf1ec087aef60b766f04de5d94891019779686fc719cfdcb5fd9ca74f36f149730171b5f307144ae40db51c3aeb506fa288eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b8689645282d2eafa50e9f77c6089baf9bd1a042d623b28151999ee24ed838e33ca6b6496c8cf62f725741306fa59a240c8a721f1daa9a6374897e4530a33e5e0b0fe5bdac2170cc094d7a47fa2b1b8844d40f1a5c9b82358997809f4fa08b1c7e92d7baaff773e3f134d4e0c90ef5e5f37adb603bb591b7aadf67a2a757101a1b7030254461b86f1d81ae23ee86265efac1db524bded8f3eb443d059ab0dee2804f951e89a539b16e7cf02cde91ab273572701a7fb7bf712212a75d2d48cde142e2b40a483fa77b505877527c4a44ee2ddd246ad66ac6c33e4349d4e83742d779b3a41", + "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd5e1a2fa16732ce6906189438c0a82c64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xe38f185207498abb5c213d0fb059b3d84e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xe38f185207498abb5c213d0fb059b3d86323ae84c43568be0d1394d5d0d522c4": "0x03000000", + "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2aeddc77fe58c98d50bd37f1b90840f91f7f3f3eb1c2a69978da998d19f74ec5": "0x10014c4bf7f93d0a5ed801ef778f8e7ef58201bdd7e33e167faf42a01d439283cb430000000000000000000000000000000000000000000000000112ccb53338ac0da571d3697548346fb5f0b637ac9412f8abbf6d13588be7563200e876481700000000000000000000000000000000000000010a8a307ef15b9f928697fa09dcc72a2c19a266c1d32fa158f9916b8b804e1621000000000000000000000000000000000000000000000000016c0197856b35c7f631f5aa8d9cfd83f7e75202b0d821e67d2f344e4e4b5fc10f00f2052a0100000000000000000000000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2006493592d2d556b7464ab5a2010fa1015e447e7b7a849d4489323a706823d8958db184c8c16713f1a6a49009f6da96e": "0xb66fc334c7c76cef9cc5ceac0f4b0837d845453a2e0e84703b280edf202c8f760b5554584f2d5374617368", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20068043e94d92496a755395960c92c01727dba627f34c210eba395fc7f60d28b3a58c5dbd6b63fb4f9788ee764b2702a": "0x443c76dcde19df9387486ded7845c6d85ec2a4c17f38f8b1e7a0a14de7968d7d0231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf200864d7856330936babd0f2130910648369e319d0c1e71ffecfe16b614f13253549f2ff0c6db558dafae395ebd5a300c": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033433", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2014724ea0c978db0335470e92fd7ffb0dc559c88e35aa258566bd616d0e31fac0efda3d881b52055a31b35892086bb1c": "0x08754abb6afba51a2f74f0b97bbcdb383f579a02a5e4541fee736710af562c6c05f09fa6be", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20151be65ed0e15cd19b69f1cf8e8d9c8587b19cc62e01cdc18a1499cda27b0fb33264d0c3817668609bb58f762012698": "0x4eed7cf3f4f6560d58db4eb78bd24b655bbd1d7a5c6b454e77c8dc5e2721a54d09434f554e54414348", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20173c5fd326bb9c3ad63ef958ba861ecaa29d60ab7eeb8c8ad1e7d37f8438bb02cc86c40c28218d4f183f110067cd368": "0x7059b7d9ad6e9f5bab40385874989cd04aabbe821094b7e45b5e4de5ecf2bf4a00", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2017d70cc4f902c5ec93f5a80574ca9bc6621dd4e5cdd0ba737c572710c13df35b316d39ecd12c1ae1320bd6db069a07a": "0xc5c184f0565e2192d6aedae584ee736cef875f0e1c558ee3ede26869acd0b4d6085368616e6e6f6e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf201b6d49bf7335fd2a47d244d1d3f6c8d2c2a55b4fc257dda3887031ea29fbe2a8ea74764db0f3ff3ee746b58365ef325": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033539", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf202166ab613084a5ece818b570161ed8642efa2e57a813989da4bac4551e4010ee45003fc3f360f5202a958b2b1a29918": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504636336", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2035cb8990d4714a895e508b555712507627922c9fc2d2f0b5707a3f0cf870fcbbc62ab06f4e4991cb04d376fa3593d6c": "0x922bc16cff1acfc4a08cb5bfcebadaa9eb182cd47a51b8b047a0202ae9624a1c05f09f8c8d", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2050f0042bc78abf5811c975c536e15fc520aefaa9aa8f2c237f96957bc1858cce594c62126484c3cef56600e11580a77": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763131", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2057979cabe7de93da0abb82a766b3b7efb944d7e992958540251fca02926063121bd452d21e0c20fedad450db1e5f713": "0x2c0d08e42e58247b57421f3239e0e192b21edaed4bca2458028c981634bdb607064f6d656761", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20583b3d9b1ed1c173fc50aac0402490d1234713d080856dba6865b23424e21b5fb50108669bc5762f955d207eadde13c": "0x561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d640233", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf205ada907b9b2192a05c5f90bb405301e8b1789281b38392a08ab0516a21dee49fba6f5e55381106ff7ae190563cc84f4": "0xd0e903b51697fa10a7f8c7e5c750fb3c2f90424b749be8b6c0f0b120f5d7277c04313030", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf206a3f85b3cc4345cefeb37661d6ee78b9a7d620473c31a9f77379b12b25920a83b43e0f5700737d9e370dbdd9738c84f": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c41305504f4f4c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2070e3179221114d6c84429bef95284b31de154f8bc16e4a7bacf303141c913d91b1efdf4ae06c3a227f0a2877a19e90a": "0x1c6d8b40be9990c19e993d238e6e3613cfc6cbc51979d5fa61dd6ea2593856090232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2071560eeff6ae3b3b71d8401212feb5dc4826569e68b7eee1b5b93406e4951fcd7ab6b40be519a7db5c6732f66da1149": "0x9a8ead39ce1b44f37d16e98496441be79018e910d5f58c0fa1518d8fd7749550165354414b494e4720464143494c4954494553202331", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf207d4eea50bc542582e7bd7f9ebd88127788bb550f52a25dfdfe1db243561efb956924b8f0a76c16c62ab47b9f6d1cc53": "0x766efcb5851054b4aeef694a3ad03d7bf36980a2094c07cb7b7cda28bde5e1490550563031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20871a5e64051b560e2cb21e54593d5b50361fd2cac02d6b4d6b0f31f3abe70104f00924a85bebed0b17267990b38c88b": "0x825872f7d324c8d97a9d5f5c94d918eea93ef783f305767b768a76f9fede7b4a08e29aa1efb88f32", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf208fa00eaac2ff041dec4370d628e56932c2a55b5c60139f9572ec1f88f541744ad98ca9cd09b5641e84088ab0ab3920c": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033039", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf209aafad24b64b451542eaa9e53554c972acc6987e6d1c5087d414d72dd8ce665458dfd16d7447ee4b0336ee869e4d0f8": "0xb22f88d05ca6fdb70235b599b99c69f927ed71163c2ebc11c9649d678a8ba84e074265726c696e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf209b22346bbe4c12f40735b38d11550ca7c2496b1630a27a7946e2ac806c8e0cbc7d12702175153cd0eddff10898e9036": "0x4eed7cf3f4f6560d58db4eb78bd24b655bbd1d7a5c6b454e77c8dc5e2721a54d1447414c4c4152444f20434f4e54524f4c4c4552", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20a1820457ae2a0ec47f77b0c4fd7a90ef4fb68640df03aeecaa19d6eefddb11516c42b586d0ac0e56f354b43b70fbc22": "0x0ecd029fdf9259e900f08996fc9862ebfe100d49c439f19bef7084b258175a1e033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20aa75245f349ccc902ef28c8972ce22f2c2a55b59871ad2237ac147d19b7ebbbda39957b7d5881df0e5c849392556776": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033933", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20b1475172a54dfd1e398dfd955cac12f3e5e8732b01a310abc9d0ee2173b6e7f3d00b291f1ecb2f424499c353e4ad955": "0xf0de782e8bad3c663be60812f0a2ac63464f5da3ec448c73334c07d71ef27f2c0c636f6c6c65637469766573", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20b484764ce9ec38ca237e5e7ae691f50c65de6003709aa5a6b81354c00fb13e281ac05e852cb4194c69f78566e8ac828": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504636332", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20b484d192ba61eb0a18c66d4510298951857d6c0ec8d629533be85a017b52214c8c4954769fce002fdf6073c0b177f3c": "0xfaff1c4b5a94649e0338783122b93a469eb9e2e375bb71c92028a8fe9b6e2f460c4e6f6d696e61746f722331", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20b601d35d778df8c9ff4b4c804a7d416dba5e754de5c965c3a904ece87b7a0304c642664457bd2159f108cddd56d781a": "0x1c6d8b40be9990c19e993d238e6e3613cfc6cbc51979d5fa61dd6ea2593856090236", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20bd1e276fd50ae93a76137748b58f99c021ba8ef466ccb7a06bfdefdba01817e620ada5954c34f617f6662e267dbda15": "0xd46cd0ae6eeab8ef19bf289712c9f2ae4272c663bfe0786d7c10d4ada52100030235", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20c14f56cad3a694385e575ddb30439ad3ef37fce11457ebc5f00b154996471fcd30eae17efe69e5a39b12b76034dc7e4": "0x922bc16cff1acfc4a08cb5bfcebadaa9eb182cd47a51b8b047a0202ae9624a1c05f09f8f9b", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20c5eaa45a3c7028471bb1543db1960e4ba43c53c2e9bf804aeb0e0f3172195defa009198441a21cec8738d366105e447": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f88370574656368", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20ca3ef735298d802137e17dbe7c40b1f2e544de1fc9198d35dd459c8ff8bf0a2c86eeaa4ed8ad9b32fb8d016e696ad77": "0xce44b6b392394133943e063102b113e0577108fb9cb3000fe04faec3a3ad393409495354414e42554c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20cc58ce868ad168d4f875a2ae0be02522c2a55b5de12066d4ae584016c6d556d36f74984dc2fb33b372e568e404c0967": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033236", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20d219ed091a75e123785a4792909748d90ed88ee1c02bcc5938985c79cd8bf1a048a9a01f6eecf8a3aec9a83aba8a20d": "0xdc3aee12519c19be02628b0f808bddda9aed564027ad809cd392c13e9b924b65095452454153555259", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20d2fc0f5be215d7720544394b53b3ec54cd38181e02e880a53114ccaf987a6f51a10bc1d172d509bab6f7e9d6eb2e00b": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f4382863638140230", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20da96916551e4252c33923bc9c88439a189ef65d1a77acbc1492c95ea3a58cb70b9ffff211190368bbfa21198c734c2d": "0x5ee1e16ea093ea043d7f67a6f34f440c5fb921b56b54e81c177898d348685b510a76616c696461746f72", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20dd3532aa765f11ae61cff441b8f57e89442a11a7247822dfa83dd5c27ea7249cf2458e60ebf82ed760f9e6d6f99bc7b": "0x5010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f024a", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20e8997eb93d61a3983e2dc3309b6a1d99af6c56d6840c84e110de42cd72b4f82d0387238d2302a937026c23940d01241": "0x6495827bfe0b07d16c549eb10d7e45997e95788be44a3f277af6befec99fe62f033033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20ef5aa56fbb147fd809ec0d34de57fb2a8fc72d20690562e27bb10f078d2de104192ad2d0cbfb4eec4eef42381f53739": "0x3a5e67c5be0b1a151232d17929a6479d7d7187544a40c059664c6315e94c977c0554696e79", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20f03b4daae0b5edd7cb1f9a43a5789f9221125c5934d75259f335bfdce9d6aab9e4c6b57724993f32f555ffc4e75d070": "0x7600c0c74304812e4928a503408a68c782c69a6af2fca55fcd9e48e095b448630653462d3032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20f538ec6d619042352eb413d7b99d47f6011856d19723839074419b4519ab65554fac975017c6f08293f0a0ba8ec9838": "0x0a4a3233cb4870d9f20b5c55e77f839ce96a26f0fd773d01917919757664476500", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20f695c13f73b9c2be723b0c2e989b4ac3562230e5122411d1c436c426567ebbe517120ac76620baef8d5d78b8a2db938": "0x32d4d1b0dcef676d9a72f6abf9aec55e129ef2de135ac172e19c28a9adbcad0b04303031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf20fefac2555c2ed69906212c204ff33badce2db9e809d506f5bb1523baa031c25f48d3448eee538a23766c2686a7ffe2c": "0xbc1deacfc7e5c6e5f0373560c14fbce156ff2a0ed7e208d049ccd985dec85545065367722041", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2103db706f058fb143b85c4947343cea709e125c99b3b07749c78742584a990e78457e6424814870b165823547d3d6e29": "0x00004bcdcc7d30d597e19c7f87652b69736066b729f7e8543231a10760f8157a0230", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf210ad3343fea5177276e8c0477a2931dec2eea43fd0e45e0e756130e01667533bcaa001e29e0192501e7ce2186ec3554a": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd070233", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21123dca0f5481ee652641df8f8902ce6a6e11253e1600d4a54c1c233df15bc0a8b600e01f85ab764233da2dda657d439": "0x4eed7cf3f4f6560d58db4eb78bd24b655bbd1d7a5c6b454e77c8dc5e2721a54d14434f554e5441434820434f4e54524f4c4c4552", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2117d80b0e5656b05c7ae68162e784be2a83a9d38e1fcd76d6e82eaf458c9b71eba96a9e6540f39213fa01366fe0fb64a": "0xb4d599b32c954b0a9e554c96b248f3e66046a82f46ac914fc675938f771f8372035031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf211dc5a1593ab45d7628a26ac20ad6ea7c97b296323e5b62a72504892a3ccd4fc0b1b206f4de9ba0e9764d9ed9ebdc276": "0xaef6619eea08f01aef7e47d460f0730b02c075e446308892e55af56af15721680872657365727665", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2129c6c4278de6ae0e63d1213dce8b06a6cb7f28f67bdf8862d6ca0ca68932257d665d966222ff24f800291451121676d": "0xf3b15e49aede08aa29d7400da8d3b4fdbd284b5bb1f1af1a53a8c47398f1f0930650524f5859", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf212b57268458e88863db3a3246f664fb7e4fc83d05b1d8ac627cc89bbc95e7379aa395cb168db459fd18c6bceb6d15234": "0x986a4fdcd53d0e438f9694c9a6bb76665bc50acf76180049117caf3ca9bbcc6a06474f4b554e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf212e1ebe0cf66f108c421c8aaec37e4f620430a70d2db1bf57c424e5e81de47ade7f1f0ceae2b568b9182ecf9b025aa35": "0x26171b3ad75723bf624a27485e51e4c2fe38f4b2d24ee52b86a979fb772c513b1354616c69736d616e20476f762050726f7879", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2139adafa33f6c18088709ccbcc3b42887a33d68fb22e4ae32721ff41285cc493d664753a7d71234c77dc2eefc5782c0b": "0x86f68361d0a346a62be267558e72dfb9e3b5a04adcc2c9e46fb7b9482f7c876f06626f726f6e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2139e455f66860eb4ec2dc909d5e10310adcea185416af2d3e8df8c1c8ee8a634bf1c3275b3820cb6d935300d42c73b2a": "0x5a5f7eb7050fb96d8d7895d9afce428a064ce66e3b094805bcad9a8e68cb993419426966726f7374204465726976617469766520566f746572", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf213c2c879ac14d17b989296183c4c2e8f6881000dfa449822280d217523794e016588e4a6c4d9c14e79161ecfc085e72f": "0x1c6d8b40be9990c19e993d238e6e3613cfc6cbc51979d5fa61dd6ea2593856090234", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf213eccdc1d0e24a1cf5c23ef55b31ddc4a082ef6765a3eef5cce291b2507c5ac3d6ffe5e10ecec2525d1554b8d2db1440": "0xe043d8f7872cd895f8957c9179c4264816be3e649713cb3bdc523f752602cc3a03320a", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf214478ae78e9c797f4e889eb575b26a1cbc1729a527ac8770c18456f142dc57b24069c9ff1032d6c3a1572d84b811ac7e": "0x5c5062779d44ea2ab0469e155b8cf3e004fce71b3b3d38263cd9fa9478f12f2804763038", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21480139f39d0cf727f90e16e6e9c79252c2a55b573dcfe35afeb7bffb31da83e9df9ce87ca0ed20d9751b2e823963342": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033535", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf214a23f710bf657cbbe2e97111e100040927d335e751bed3dd0da74a10e610fd2996eea79b625bfe519e8b9d2b96ec75a": "0xfaff1c4b5a94649e0338783122b93a469eb9e2e375bb71c92028a8fe9b6e2f460f506c65646765204163636f756e74", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf214b9473e6abfab914fc53e79bf75c3c614860e374c2789388379dc1cdfeceac093c9cee7a2ae8579736323c589c59e42": "0x3c017930b46ab5a4413bf3153b001287ed5ff7fdbd2734cf69abc843f4ee044711f09f8c8a4164726961746963f09f8c8a", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf214d858e92dd9a7f8ec1fa8d00a47f0439c14dbe4982ae73a084bafe1f7eea2d51f3819088f08a10eb2fd7a1343c5140b": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504636338", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21536cf1c289719ddbcd93b9c88453e80f0cab0c194935f449b3baef742e992ebb801fe38ab5c345d7a6195740399cb50": "0x561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d640d56616c696461746f72203033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf215932a8a0140c5ca04b27c0de3c002e22c2a55b5b35dc5768afba71d0baf2377ee6f8e235c27d04ed90d75e0fff80625": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033332", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf215c6f0e69b0626090d1d8a1d5b91572deaeb8cfd46b3d6a0abfdf3961d572ddf2303545b03f0b1870563d38c69de4f20": "0x7a895955042cb3fe863f3564e7b30e9130e37b6d905be11c0cf91064de1cd83a0530312d63", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf215ce3a42d473cc74319c2a7c9c1fc302be2daf84705de34c1930370f53566524b08145b4d192d6cdd5a35cc71930e240": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb3893450563633131", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf215f325f62b3381ffbe480cb4e965c038daf47069cecf5c31da9a1cfde3732d2b701bf9f94c77ba7e99335d6b30ad080a": "0xdaf47069cecf5c31da9a1cfde3732d2b701bf9f94c77ba7e99335d6b30ad080a074d756e696368", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf215fb270af184e461f951197eed4f0365b4b3a27f1b0da0e787c0863ce2307f5617f485a8c27f0b17c2d3176e788c3e6e": "0xe21727312b2b7ce579dc0f82f129d4edecbcf00abca607d73ccf16e8352cc77705706f6f6c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf216373727b49bad30b97a966db68486122c2a55b50302080081e20a336b40dac9b5ae2ae80692b653c2b38131047d797a": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033438", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2165f2504526b6cb7a840e50f0c3dba82d8c67a57859b28434a12f1a078e2979d8c1dbb2404c0e15fe75c1c94e39b2061": "0x2c81fc1faa024d3d1a727d953187860b45bcb185d046631ab98261eb6f2b8d5416f09f90b620536f6e206f6620612042697463682d32", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf216a16a003b0fb10dc916b9dc13c064b804587d42f37709c7342f3e915b5a1c6f64ca916b9fa8279480c8602c600889bd": "0xc804531378242158a794bd06c1e9ef0e569f60bb32758597af80e5e70c07a64f037031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf216cb2cf29041c3fe48a99e396bb1021e2c2a55b5e1efa4babefcdae07c4ce9c4682b1f57b0f3080ff2ead5ea06624b6a": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e08436f756e63696c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf216cbfd899588e8acb3f3f52a415f933ee40db41d7f07b2b867fae0d7b8ed6767d23f7b53b032710dc5b5043474bf1d11": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd070235", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2176d35ae3da51e17d64a315dfabcd4fd2c2a55b56da37e71236c50ab586ba5ba55e3479c375ff4b9246082b5d21b4b7b": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033534", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2178451b572b6a54120a14059926a330bbf10d082fee91854f7a6a6c56ebf8bf0d65bf914a683ce4ae1db5ccece06ad27": "0x7042bfc6e75e1277fdefbea917f6b7ca8c36b3e0ad7286d66974c8f2b4cb96720d434552554c45414e204f4e45", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2182542d27fa1883f8607a2f851a2a82e4c4bf7f93d0a5ed801ef778f8e7ef58201bdd7e33e167faf42a01d439283cb43": "0x922bc16cff1acfc4a08cb5bfcebadaa9eb182cd47a51b8b047a0202ae9624a1c07e29c8defb88f", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21833ae47b521ef27e9ceddb1aec06a2e0d92a2524d501daccb88d86720079731f9b53038e0aeddefb8828afbafa5eacf": "0xba57da1251d785b2d433ca687c510a82c284826b9b79859b5774a84f7000e9281e5b315d206269742e6c792f6265636f6d652d612d76616c696461746f72", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf218b2fe7149f81b3bd02be7ec2b961d783856d90462030b27957c425b33bf8f8f669ab715580e7f64f36999a74cea3936": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf218d2efa59b90164b82e2b35a855561252c2a55b5cf8d00511ef54ac7a60773810e906befb1b322f2d58199963dc97307": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf219694aa2e266a931182f4a75c123a8f22c2a55b5b1da68fd2ca56d83bafdbee8ed436709542031e625723d4cb0a01b31": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033431", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf219a6b776ce41fe6ce413efbfcdd807c5a61fb8e0537d0f6c8d4f94d0466859215b77e5e53c44981253983062b7a46f72": "0x6610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21af083704411c31490ba60a373f73db91c6ea9855f8f85002cb80858c01488a8ec5f459b1265248752b967a685eec446": "0x589e41f29ab08f2d0e4e4bdf3d1c8a868162c720f9c31e22733f8d633fba901e0b4d6f6869742042686174", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21b35ff62ffdb14bcfdf065e0f57d762356196c14df0a7036b943ecd01396685e799f786c0f131796c06850ec9342ff01": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd070231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21bbbb23d04ad5dd2d4e8508f5969106846351b08b39356e218524151eb16da7d6678c6066a831d6ad9b9ceea25d6df07": "0xbe776c10ad0e1fe7f606cfe42448e02cdb640c21214ea6c0c99df36ec0ee3d0d0233", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21c045bf7d075ed7c91a36c40a14cb9263c235e80e35082b668682531b9b062fda39a46edb94f884d9122d86885fd5f1b": "0x6464f15335eee136dd7c216b994ea6ac3394eb723590e9e065fd9061e05d00350b46656c6c6f7773686970", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21c06bc31ba96a7f758a0e14d5ec4526428599b35f8830b27f465e77733dab096524c56d03921532c474f06775af121fa": "0x1a0eb7fdecee073651f1a21b9779b7bf5670493a0e35a53ab83b3cabab814a77033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21c12ba9744db9abc0b25d514a40f6fa0ae60938e514a0fc95200df7940bd0fb0f983090e91043b76f3186c6300ed7437": "0x5e34b19d7230f1ea5da6d4b8fe6ede9d05c2e55b0189f75b967862c1c43d9a1f06f09f92b032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21c36c43f322a7b65e863e6f4038d8093e272df04c4d1eef779341d14da26699e3832ed5a73ceda96a206ef1c4569f477": "0xf85bd4ec9a558cde3f05e33b0b74f9e732cc41f904894873247d4c435a3b8b630c53544154454d494e542d31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21c40f05bfadfec08638bee9d955d3ee1eeeaf12276eba3a08a26a606cf4c7cee9c3c0cf56a781899afa463104cdcf842": "0x3e8faae4c5713c72aea65d52aa1616d3e918dee3819fbbe08cd4c76dbd754a5005504f4f4c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21c75c2a4ae6a5d39f23cd563ff56276b2c2a55b58a05d949b1ebe3d18f1d3f4d43ff93cd8005b9cfdd884a901761ea3b": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033733", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21c80938d9854f378c6fe5a24ce100925b6c1880096a49c2719e0d6d8e863ccbd075aea064a47cf81c96ca5ba5d45f83e": "0x6e53696350731ed439f8c353b0b69b40b324fcdcb435ba225fbc22a33c9fe15407444f542f3033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21c83a483946d3f1c3aa060b653146cfac2de6256e9ff0ba9b97f862290f69ec0f72b31fa0a6843e0175e9e81a0165d6b": "0x96e24e9b5ab82dc275b82318a52cebed1cae3e25be096d5f288229b256359e43054f50454e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21c8fa480da0eb301487c191a5a50264cbc61f6c2ba5b42da936c12c7e5c33f6cc573988521e5350de5887b20ae9cae49": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb389345076c646f743032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21d0913a4692809429ee2d8bc2051cbd15c5c5a4a025f3d8be84fa9ea36383c2cd168c8a018d50c88a34154afe9ecf04f": "0x09cb20eb25e42c2fa106d75ee4d44b9c2bac20323a540529e9f4f40da5337bb304474f56", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21d302f9eb23c25a19e4b78c34d8dabfe2c2a55b4f956b33fc6d43013d320b87c2eddba31b9ded535099734d6f3aba80e": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033533", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21da37359eeffa7892d596bd8440f1f52d7264854fc5645d065341b90947bd9be1564df8220073eab4043f4022e633e8f": "0xf8f0244b95932b7a67caffd31de91edc5cfb3aa2a13f6199f127ce51c558204a04313032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21db168782360a02e0679d89652545d7eed6f8c70207598bc9899581a491dd8577e82f0ab65d5a78318b74bf51315841e": "0x0bb5e12d2f6ada25c3b64171730f1c9efcf76d3bbccff01abd9f92a9aaac10a7085354414b494e47", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21e28beeb7fdd0adb92e733b92ace02f951dad69a99af405193f3eec7593e9baff022b7fa55de147d1b4f9d2147103885": "0x2c81fc1faa024d3d1a727d953187860b45bcb185d046631ab98261eb6f2b8d5414f09f98ba2050757373792047616c6f72652d34", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21e3c9fa5071901dd03fecb41ee97416cfe08b40ca50585eceadf8c27c0711d8cad57d20cf68114b8f7fd6ab32534dd25": "0xb0b000e408509bb033443af0bc700ec11894f81c090d58d7dc2ae3174c54902b0232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21f5caf66504f9b5c55c013d59b368bb3d44533a4d21fd9d6f5d57c8cd05c61a6f23f9131cec8ae386b6b437db399ec3d": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f88370235", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21f69f16bffec7fdeb53ba4f6835f36450a2178b090386d0e2645866868143abc5bf6c91af252b92c0c38174200f42904": "0xb204051d55c2c80cdf6e0d3346944e921fc97f0b86a4cc5eb5547982fc475d6803434c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf21fe6c0847625867e501f08fac1f82bba698654b9b352da92d00844ba8bab505a2cefffaa6269cdd2d407cc866440fb88": "0xbcf647b00211b2d6c8cdb9e091b95ba03ac8bc6a4dd0124f1f09f8c917d1d2700d44454741204953504f203031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2202d3104effde185b2c83268c09720f15a482942379643a03b8eca91f12ef9ed1baf5437080664727117fd2ae6e7dc56": "0xdae2b867564f01654946a095e69f0f49df1eb5c0efce5730cc3d1d83da3f4b0912303520f09f908a20414c4c494741544f52", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf220670ee6720bc60b6e5d4994782c129d0792d0467f5422f1617840e11eccfcc8bd24bab23b0ea04de920c1109e1e14fd": "0x00555ee596210f641d2d48b9a1e2b258aead3fe8de4e973bcaf5f24674044367033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22105fc080f09ad9a9af4ab5073f49241b023d129d9a0cb9490d097dbd3ca947d4830d3a6d7e0fa9975ff2789d9d97352": "0x9eee1e58c17ff7b037f2da5766e9bc78d5568c58d45cf363b9630ef32b2ccd79033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2210a4cf516e742ca4c85e9446ad650b40a7f29211d50461588ec3c6857c9ca25474c650c7d2048ef2283a2245ceaa831": "0x6c695c4e546c6889ca591b582eee8b49ba68d8485a3732e08d232b2f28f2342e032034", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf221f2017acea08193896886ba2d42fc3652ff057f98f0c1bed31b2fd1ccd8de4d4acf957e79b3f71eb69820bf0dc1d22d": "0xd2eb07f02043788e254d9e2df57be11566d241c56302b91199b4647947af30200232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2225a8729dee127dd905734cc5723d49f2cce76137e2e2d9bb63e081d4074cfa4688d7d9781d2888f36634f646da62561": "0x02385caf9a08b92ca458a0b817a8cc303cefd5a0c6e108cb939e04242b9e007d14537562517565727920486f742057616c6c6574", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf222642465200f772b3b2b1edd1bf3e5b4e33f4520b7954cb5fee8778b44283deb0948c23c6b432058b21ee84e92085f3d": "0x7cdc1a6a5a7f23437b6528edcdf553d0685f940a4e6e85579727ef3dc574563a04563031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22289e37568ffd6e589cedbae8f8bd08b50ec868243f5ec5af29a7c679163a34978815b6f1d6e2b871f1f361cb7a1f905": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763036", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2228f19ef28c6437f274bbce95bb9abc52c2a55b5a609baff13899d4ba4bafec105038d66a716494968fae1a849d2dd5a": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033138", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf222b1830f288a869371d356dff4b9de2d2c2a55b572c2850205aae1bb28cb0f6fe1c0e606353f9dc420b3796317bac454": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033935", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf222bdda5695f76397063f5274d21e8eef58cac93e1f29e4fac5351cd9879164bd90286cfb45af827fc732fbc462d2eb2b": "0xfea885b4e897d4fe908db6abeb39365ccc4689c2b6437dba675a4a0a0c0a6610033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf222bf1f493b44e5bd3fa974b18a6f8902c4bdda1c96506117589bfc216f9e5e79510c179b367f31401e01f1997b47181b": "0xaa6646d5b85790bc0ab869d80385fdc10e1f4befa7f8a4bf31848f73012d2823036e31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2231fca99ee3c9a15da8a33fa1566cfb508eae5dfcdcc8e1890cb16ac515d4669ed0653e98623be435327545c72f5aad7": "0xac133ebfde441672c055b79ca9a6059850984eead1ae036f48ca1230e7f0556f08436861726c6965", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2232e0329385e5d391761079dea3d56802c2a55b558917616097dfee7c9142497c66c77b194cd9113d0e65a11ba27660b": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033436", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22362b72d0d51c3061db69726c0af97b5e27cf0ba46292cc95548cd67e3e29036b0eaf1ab66a4a6f5d7065408d50b9759": "0x986a4fdcd53d0e438f9694c9a6bb76665bc50acf76180049117caf3ca9bbcc6a054d494e54", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22410999d9e4425e03a0412208d1d1c260238a0a2b0989bb426df8ac92118b4228a81b354d0c87d8acd25c8de509f2226": "0x3e1630f099540e76e98b9fb0002c3a5ae3f40b6ec180df68fe5cd9bd2088fa18037031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf224d956ca739cac656cf377d51f15ca192c2a55b5e3110687784ba7063753757ba409052b2989ab9def09a684e8aefd6d": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033234", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22534923a0207bc8d69878844e0a5a08da4c5b68728f7fdbdb4426714a385017a471d5cd22a156acd30a40277e6417b60": "0x127a30e486492921e58f2564b36ab1ca21ff630672f0e76920edd601f8f2b89a07415a494d5554", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22586d346a46a84b96f49520a635b61a9b0583167a388bf2ec1c4ce53d17be27a15b23abdd97265571a5c65bdeabd24a0": "0x1ea80b0dde0e207c8ec57ac05fbec636502cb216bb423642919679fe8f074051164e6f74696669636174696f6e7320626f74204b534d", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf225ea69f6ea3dc893c1ccd4a4f289aa91c83b0bba37f25f365e26efbe6c9ecfa7905dbdc0b0e3ae60b29980b42c509c6f": "0xa471c55caca4be7b4e60c6e94b20f9028883f8c64287d4454130c657383c3442035633", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2281ddc7d5bf747f585a24d4c7fe7a0bdf6be65cc16c65708bb6a0e4b9958ffe23d1c56ee5683670a69dbbbb70c10d507": "0x127a30e486492921e58f2564b36ab1ca21ff630672f0e76920edd601f8f2b89a0a4c554341504f474749", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf229093cd71dab62c83c7998ad273215375c2721c659829b4fd0d827879873fbd8763969b48a7724633b1e3d4c3b360961": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033330", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22914b827b96f4be7c6ad605f529ea76f0a3c9a0dc2a4578d4b58cce45258b3ebb0644502b355ce1241f8a38bbd1c0a72": "0x8e07d43b19d901badf3a7f57155ca84f2f835448e93a141bbbd33eac4b767d150233", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2292060b3ec84919e065fc880f1789cc72c2a55b53f73b4d26ce6c62b0082194560e4a186f3491e8347218c775dca4830": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033530", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22921ebe021aa3387530a48171fdd40bfa26d8e7561ea8e813da3ee0979be1b5190fcc5d2d1375dd5c2e6b2f417aa8153": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033532", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2298cfc99ac1dde155aeae1c27164ea68b64c29324eb942fab6b41cc041f0e099f35d5c7fec824bae17717c5fa68cb83e": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763039", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22a27a2cef808327c413d2bb72bec75b92c2a55b50699f7db6f499718d572de81b0d1843c146624daa99edd9a6609ef64": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033730", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22a7915e444abb79d13c0ef789c8ba605822058fa0d13ffcf079d48b96c07f71f6cfd7fc9abfadd85e205ae7a18d7bb3d": "0x1ecec4f3062f61988b13e9dab318860bd0fffe5b7b37880d50a614a0a20c250215504f4f4c20343220f09f9a80f09f9a80f09f9a80", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22aa8d84b13d7d95d38edda8f7ddc26dc8413fca2f93b3526794f655fc3504b40e0dff0648a4999f10f8eca7c21d1c21e": "0x5c3739d60301126756a7510e34f9d656d4435cd4fe64bbd001f1f3473bc9c33304763031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22abcecf1355c686248adb72f696863f73091c08dc07b367c41b57585aeda74490c1850166bcfac4081ed66117f668361": "0x561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d640232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22b4ed39fe63efba483fb4613a76d2cb644d9acd86b036eae410384c8fa0d073d47e74712a5787451463fbd717770ec57": "0xf0673d30606ee26672707e4fd2bc8b58d3becb7aba2d5f60add64abb5fea47100ff09f94a520486f7420f09f94a520", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22b6f378e7c033da5bd805ebedf69877992a8511f5619229d4e1bef8344cd9a5f85468e238fd95f843dd555b204d0f00b": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c41304554b32", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22bee25df560006547c2bd09e33dc95698818f1b289df88876f0199aa1fd723dbec6e7bbc5b08e5eabb89edebfbe3983a": "0x807e371d9df95983e2b9face8efb67f962816f1a0c6681cb1e5455127ab45c09033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22c4857f7714c26e3c24e22139345ecde482a064f119738425180e442727eded171c8091bd90182c3071f37199d954158": "0x5010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f0242", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22c8bf0692a8248b3dda3221d989cfa3b607262b83b9349efcad72b5f2a0cbc80b1fdb622aa81a861a56209014e568337": "0x2cd49434353de1d51598b44df15fcfed1e26224e055923acc9f9af881dc31d5a0232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22cd9bd13911d8c2e382a171aa28b50c480bb3bb99df51400d9aabd8e0e6b6610d3d4f5512ae4d46e03f20ed14eb0cb3e": "0x08ec72cbf62bb66f416f46f988e130585c834a381efdbb0755e30f47a2a0da5a0c46555455524550524f4f46", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22d0e467198929274bef8f7dd5e84b23796cd6f382ca54348f9bc846929ae16bd49da6ef76b19211da8afc0a1f1c31d32": "0x7e878e54c1374b30df335d3a193f3e6b1f84db6c2270ee634cec769d7e33e2440e434f534d49432d474c4f42414c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22d80678c14147d5cf8aab732d29ca19802e358bc31b7ccb578dcc5c36ba2908f311ac5ab2ba8c1483595268a7189fb02": "0xc5c184f0565e2192d6aedae584ee736cef875f0e1c558ee3ede26869acd0b4d605476d6248", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22dba51d56b92dc14d744bb0a1b35fee52c2a55b59c3999f441a596b34e41f0d55a88c0f9fb7af4c76241a6f5d3a3e514": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22df2e6889d45ce8ff1467a26a17375ea4a8de2c6b6c1690a27aaa4cb64e0168bced54ab568958965187646f06442be6d": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd070232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22e83214fba4f42a6cbbffb08454263dd3cb64a5829fe55a733bbc427b0489da973cbd6b3bbeae722ad4fa58eb8277a30": "0x43fa61b298e82f9f207ddea327900cee26b554756c4a533f36cd875e3e7bcf0612506f6c6b61646f7420416c6c69616e6365", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22e8b2c14c8f8828884c50fc3909c081c2357d8ee622168c14c8d3b65f311c42d67023935eeda34bfe81c8ed779103238": "0x2c81fc1faa024d3d1a727d953187860b45bcb185d046631ab98261eb6f2b8d5414f09f98ba2050757373792047616c6f72652d31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22ea1ef09f4cda3cd0c101bc367eb16b508104ce4b326f4b31735dfd06fdfb1ccfbad16740364d8bf0f917f32c090f362": "0x3e8c72ac1b710bbce4e7c190568bf560d89416696ac2a4700406487cde98df04104d454e47204c4f4e472050524f5859", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22ef8756fadab2eae5b9e3cc461c747052c2a55b59f26d98880fb604cdd7848e4c1576a990b0ba8468dd4222aa27f9e22": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033238", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22f76dc97329983ce3d527493564453774cb0f7f17953e529b8106c743f78d238e3dd6a90f421d32bc920ce120682c801": "0x7682e4e399a94b0dfc4a9bc476e0bc69f25413687d2b1c29bb7139dcc8e8714a0231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf22fb04932bcad4f50f2b7903a3446987e2c2a55b5f1b5e1c45738e2352b3d00673a40f451add21cf8e6a0c3eae6bb6824": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033336", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf230323bbc825966297d4132e73e9ac0f09ed22cfc6877c1961ac2cdbe5536684b0761074b8ea475d0c2f173f5989be904": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763135", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2307e1831a66e34d18182de7ba5a1fd84da4c4087c6c624a12ce0c066b04f71d81735ed6a252c0f63e55188be16be4f47": "0x5010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f0243", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf230836429701e9ef6dd3cb5826f47654444a1336854e44cdbfa929ad12e913e4a1870c590a6dc5e3983a6fd416b927f53": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf230e8e0ed55084fb5c09720fc6145dc130277ce02b2ac78ceeb9ae4fa0a595005489bf3f5f77898415e32a3e9504a5314": "0x544e2e588c90a2e53e051d2f87d40e222e1f034913a30f95a9a2f39114e5be3818546578617320426c6f636b636861696e204e6f64652031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf231bdd1ee816fb4ddde9cf92d60e77172c3590205a4e4844653590740c64ec31e5d2af126cce71bc60df95bad639749ea": "0x7e569787b1b323854ac9a8c40914d400b6fc23a2fedd24321f814ce7db7f65630231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf231ebf87d898b03dceba03d3e98e6a91ce4afcec3862a48c276628c97bec157c563a74bd8ab3c7abc9d7fae99fff38268": "0xb22c2075548019dd268e74f3aa69c9703b129e989d230f935797975d5ed9247d033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf231f763c6f1ba726e99814b8a2b7487df2c2a55b5d7c788d0d4bd1f7cab8c7e7a6d131e3ec396056d62d1376d21177941": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033330", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2321a3c23e0895caadc2cafee7f5a4f3942cbafa9669c25868ae5ef66a62b96169f9d56f9b1dd27aedb241d86bcb2dd68": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033235", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf232a159fd0bd78dc27bc0e043b43ceb7e9effc7fb904ca3d8cc4f45b464ee4dac705c89888d298bc9dfd2ba563d5b3e3f": "0x5c5062779d44ea2ab0469e155b8cf3e004fce71b3b3d38263cd9fa9478f12f2804763032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23368a94490cff214e159dba0f70e115d5ed6f4b68a60a117a32059e96678f22fa086c505f7a8ece13c7a2e78b4788f15": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f4382863638140236", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2354c7e586c4f812b2fcc1ab9f7ff6951a64c092cd19a2779a2fc967ea1be4727ab3b29858a2a6f46af009daa35d84121": "0xd46cd0ae6eeab8ef19bf289712c9f2ae4272c663bfe0786d7c10d4ada52100030231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2357614d1d04105c49a3c0b1b79cfa4ab58a0a27aedec30fb5c0fa2dfabff0b7370f69e2a8505d714f7b2ce37a99f0d07": "0x3817e559bdeb521169c4801d70a1dfefe94175d15666a1ae70719a3594a57c1604303030", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2361f1f219be184ea435c0d209db0f9546a6d9f13f448628beeaa0e4c8e47341163ce0eb14c612d882799b9eb4152d71b": "0x0cf6b6cdababf69c2af37a41a2f360820bb7dff2f61c20bb61a9503a8901ee20067374617368", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23652b8b9660430d578d4209eacf59280ed959fe3d9afc671f1b0cde07de829b2120d7da25d5f84f4e0a86e3bb940bcc1": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d073034f09f9a80", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2366434e22dc11ed36c7fc4483b89f3cc364c29bfbc9f06a42b5cf37ffd831e91c843cc25d8b90071546810ecf279e458": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504636334", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf236b45fdc27494fc1783947e9703bf7442c2a55b5c7a1c48e176aaeaff69a017d85b27a49f1fafc160725a3376f417d00": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033233", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf236d99ec400101b8d759bc939b72d57f96a7fbe4110d7ebecc8cd2101113549d90cd477c766e02a52856dfb5d9977030f": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033430", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23786e839ef62a0a379d944c73fbb9273a471c7aa909cc665212bb36003c52c5d3eeec39f96556a8242e861c5dd7dde41": "0xa471c55caca4be7b4e60c6e94b20f9028883f8c64287d4454130c657383c3442035631", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2381907fcbd0ce09177388618368116ed28996c52694155d7ec9082650fbf108f69da60c44a4b2565fce4e03f9bbb0178": "0x6c695c4e546c6889ca591b582eee8b49ba68d8485a3732e08d232b2f28f2342e032033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2381ca18820b278a00faeab03d52440be00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127": "0xe64bc946c10a1f75e683f236e72a44d6d61b3bdcf72ffd8c738e488efc6e15670c636f6c6c65637469766573", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf238329bbba5c700a76835a1f661b6e4ab6eb3bf6204b55f63a2ef87543b26d158230eebb149a8b9df86555fe3f02ecb6c": "0x561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d640d56616c696461746f72203038", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2391b5b0353cc7a179d9964a856db5830768659892ca9fab8d64efd188054b2280efcc36942c84616653265d417fe966b": "0x8e07d43b19d901badf3a7f57155ca84f2f835448e93a141bbbd33eac4b767d150232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2392e34504353df51b35b300a5914a6fc7883180d8cea922c0648533d6b2be8949765483e833ec49e8dfd10a0ea61a03a": "0x5a33766207d51925e4b9a2302870bf737305cddd5bb2df2dffa379963e5867710474776f", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf239358f6e6a083b53a55ab46a8012eac1ccdce2bc61518838d34314c620b7c88040c38c784e0eabe838c17192be7ed91c": "0xa66e0f4e1a121cc83fddf3096e8ec8c9e9c85989f276e39e951fb0e4a539876309466561726c657373", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23937d53bd543669ab4a709f7aed69b582c2a55b6094cf557bc85fa401951b4d4844146211f845e1c212640685d42057b": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033135", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf239672122e445e0134396dc1a76a9b84f20e1d9db00b7eaaac9d3f4a95ba206feeb6f606e16f9af3f2ac24c1c57941e2e": "0xd46cd0ae6eeab8ef19bf289712c9f2ae4272c663bfe0786d7c10d4ada52100030234", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2399b9287f0e2d363ab2d35f07b9843c6eae07a7b7adf896a2332b5bd2e366474eab97e137c155f8741a9c6b4d30db700": "0x726a98c27f84bf42fc842794a925418335fc9fe3badb1f535dce9c9d9efcc02d0231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23a0ea3a6a115e71c72a824c36d1b9c4e8a1ec46479fec3c43eea382d637de8f295ccb2c0b6f6fdd4c5d34a687737a601": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504636339", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23ad1e8e1d94e40076e37461627e008dae6d97bf878b1012927ae6afb7e092c541a5abc3904656981beaefb9ebb781d1c": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23b3ab2b6fd4850a8142d624d091576c6c235b0e23ee7b2bcceb64fd0e126b965204f7069aa3b4fcbadb8d658e2ecf86b": "0x58a74372b1f0a88b1df304584326ff69a68e9470d42687e12bf5dfac375d49110231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23b42f4d2e5019b0e65ca1514f08ff2868e5df47c25340b48d443389c64abf88909ded6b6dd62c01548840970cc4f14a5": "0x1c2a5f648afea2a94286c17f6c60d16c9ef8511fa4ae88a54ce2748b6c8fa90f17476f65726c692044657a656e7472616c2067476d6248", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23bd81eff8cbbab65308778f5254bf30df836649df542b24a1b63d80916ee743d4734640aa796648649685dbdd430c362": "0xa02f7333e25590e4f568ae8a2ddc93a879e92e48fa3cf1666ac56e020c106d55094752414646495449", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23bf16ed79df138132a2fccefacbb1d0cc28804ee5c2389c20b837475af5118a2576b88baf216e87684eccf9c282d8b2a": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033333", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23cc78501874040a79e85e92a504fb0ac92a11c7b27f2e0db0137d0745780fe21467bfad9bb6ba3e523b8e2dced7d60ff": "0x086de7162fbfa0b91a67eee94b697646028edcf484ae78fdc0627e7eef1b224703444a", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23d68e0cc11f62b9bdd663bbbdbcdd049326020faf1443ba7736e9f4b7a442db507e4a2054ec3a19f985d2c44c30b3043": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033236", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23e1813125abf1cbff4abf7ed4a1da61dd19f8df3cdb194db075979039a6fd4d912298356bb2931c08c35db4903f53345": "0xd824263a2ba0b39e43b2a1fb591c68743ca504c2ce6c3c2aa96e58b6269d3115035631", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23e609f9530b24ec687f468ddffcfbef674eb76dfc265812c8f4f9b98b54e25c319719e1a3593b7f0e743cf8525206178": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033136", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23e7237354e5ba368cc299c75ac95d8722c2a55b5d10904ce8daf9095a573253bd66fec7e96557836f664a49e5b89553a": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033239", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23e78ea26acf41810d7291f8cefd34b89b4c24dc7c3bc7ef831df7637143bb554db602d209f58c93c3dc2af04dc386669": "0x5010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f0249", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23eab38c7c2943807005f335cdcd134507a2f97c76dd2d0ee3baf2ddf3b0aa1942b47ca79a3d1e2178ac5c1c37a056139": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837067465636832", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23ed7e94851c7256b8c7e2359d8fd78a77ad1704a69927d8dde4807b1c67fdaf26718755a742f7994158c8f79749f3a04": "0xdae2b867564f01654946a095e69f0f49df1eb5c0efce5730cc3d1d83da3f4b0904424f54", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23f0f1b51a25ea0c3f24d3db2aa987b456b973df13cf0190a75f17633f8a391020df8bac9aa9e4c50670e0dad135bf726": "0x182fe099d0c1787bb1b88de855537dcce095204028736ecfde09dd115c498f2a033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23f639b045975e0b1c2a3ecf5741b894446779e4754580b4e94ac22e5da499aa39cf1aece35d5162db15003b14110f31b": "0x4cf2e774e34c3603b2428a690c058d2ab826b39a2eba4e22b3aae85f9bfa780209444f542d312d4e4c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23fa2ea1116374127e8e095cb6bb5bdc24431708b474e888318f65c93f0049fa3043df9d7f1ec1f5f3d7d5e1493359e58": "0x2c81fc1faa024d3d1a727d953187860b45bcb185d046631ab98261eb6f2b8d5414f09f98ba2050757373792047616c6f72652d32", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23fd4d7d6d83dba0ba49ae52ea3646d56783770544556ce47aae6e9e9c6cc32bb84a3df7ab11a5d30b0618e00e109d66c": "0x561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d640d56616c696461746f72203035", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf23ff1515342297e95f6481a8b7d2809312c2a55b60a32545c89a8d2ba5d04cde08adeb0e91bc2d0cd909d166db15e0f7c": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033134", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf240042a224d4954573fe0c30db26f93b2207c5841ccd565eff0f8e15ed5d3b1752749e2fc9e3f574c8434bd09d7f8f815": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd07033131", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2408b7bb25867493ede69fe026e67eddc2c2a55b510651d026a38ce4568ab7ce806bb2983d1846191cfd29b6b06e10946": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033538", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24094d87ff94b8805f0e07825cd1029aacd0b6229844999d1cde6c2e74ff90057d24e5875b891f645e2fb4a47ab90745e": "0x22b7a9ef681c4a3c1743e5c343ff5b6f8aec3dc43bb0d49e29243d79ed406365033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf240ddde62c43dba8cbdd11a9179cc340a9695d7445e6e6b293f6fcb0aba94ba7b76431447abab31430eb99a63934cdd3c": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033234", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24153db0616bf10bda91bfd32053b04aeb7972e09702baf7eda528c6894408b1e9c702e5f54674bcf6de1b55b3aa16365": "0x985be267f05f586da7ab52e4430c2f6d461f396b9d6cf4f1eb5362066f7cd82f094a616c6170656e6f", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2423272c97dd2f222f8da3fb92989a8512c2a55b5d133442ac56db7e5ebfe739897a98a37c258accbe3697d0d7f29a373": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033331", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf242504f570a11c91b58fca3962a3aae5e3619289cb2e660dbc1c5747506a37df7a4d311aac8f29c69be8b710495e51623": "0x0cc0424184702e27c49fb859c93f07d8bb0adf2a0824ada9f01db9bf76b7025f0548454c31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24272dd00c8ee9debc33f31257bd6e25c2c2a55b57ef9ffffa47f4d6fc6750f15ae53863a1d0fd99d8386a70054d55e4b": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033839", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf242d47606328d06ba64f3efc8e8d02c29ecdd548c83457ab43caf7867e2bef91ef783025db9659afd89794ec1220acf29": "0x2cd49434353de1d51598b44df15fcfed1e26224e055923acc9f9af881dc31d5a0231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf242e42af440c994fbdf3465d43e6c485820857206fde63ea508a317a77bc1ca2a795c978533b71fc7bc21d352d832637c": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd07033130", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf243068a2209c8aa06281b5dab90c6aa7109b072ba1658a3946b1f7a82a7c135c33a41ac8e6ac11407d910d4baad3e6c42": "0x09cb20eb25e42c2fa106d75ee4d44b9c2bac20323a540529e9f4f40da5337bb3064e504f4f4c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24365577f7ddc1775c40ee9f59dc1d3372c2a55b4f7af0b8559bafd9332fe78fef55b0979b53217b3f8355382ba989031": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033437", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf243a8420256bfb245551d2d45f48f58a404926296ae6c9155557a6c5aac98d9775664efd8607e894ef210fa2c80b65941": "0xbae335c017512a43fcaed23efec97d80e088bb8f7b93ee837cba5416ca51037f05f09f909d", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf243b718225680fd482895b25914d18193508f2cb4567caff040d0a47db244d7ea791d9ec23f0d5d33928a7c6a62b3077a": "0x7a895955042cb3fe863f3564e7b30e9130e37b6d905be11c0cf91064de1cd83a033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf243ccfa90dba62c7eecb208771c4ab15f2c2a55b5b7e13a772e0b693c3b351d2fb5e5b4da18ac379ebdb2f1f2e7559776": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033139", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf243d2a5c1e1b0125a45b283222ed4d54be693f8c8c6043a5d8c8ed64d56523d157625011947a8a79881987d9e9100963a": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f88370232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf243edf09e07e0b49da67583ea78c544158e2499f22749aef04333796fe92b73c06cf4e358a552604ff3e550725774f924": "0x74db9a1104a31f8b431ece5bcabbe3e508d22fe13670d32875ff6347a88d138807576f75746572", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf244175ab241b696d9e6a1246873b367047d80b43f7b596676b4a6714cc5034708a9d651e55c030c2a0b04d8152a8437b5": "0x4eed7cf3f4f6560d58db4eb78bd24b655bbd1d7a5c6b454e77c8dc5e2721a54d0947414c4c4152444f", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2446fb9c1f64231ba01e6885df09d999442d1b7759bba592a85972e3c6a9bbb419de5df76c57324d98c2d11810bbf4f13": "0x604189f1ea74bc439b18060c58f352db2880dd4c835df7ebb26e020bf11e7969074c6564676572", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24472ccb3bb0aa8cb14f001497cebaaefa09b87b34880c5375ecca849557dc87a00a6243938d5882017fa0d1f60193815": "0x0a4a3233cb4870d9f20b5c55e77f839ce96a26f0fd773d01917919757664476500", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24490823f910054ad342b544618de7e4d545e8064f8898a29d4811e09b207cf3302e5cefef16615f8580fcd8fa63a624e": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f88370237", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf244e4289bc529f199c16215001de50d3f7600c0e901b4341203f21f410f3da01ae4a45d194fd2c0693c6e0ec5980f4c21": "0x7600c0c74304812e4928a503408a68c782c69a6af2fca55fcd9e48e095b448630653462d3035", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24514434f629431aa53550a7da9668f472c2a55b5a6392305c60fbcfcdf77a4041507f2bd1976c05dc149d2239d158221": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033335", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2452ad5e8e4e5b8cd84739b98245393fefc80cf0973d64eb59f1fb930f3107e9f8ced4aa69907717312f0c5015706f458": "0xb22f88d05ca6fdb70235b599b99c69f927ed71163c2ebc11c9649d678a8ba84e08466569204c6975", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24554433becff001e411b2f26115efeaff0ef72b01055276dae281f6da1a1dd14c6276142afd3aed8f8b6c94b36d2a717": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033138", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2459e87ab7d28a91cc6a000806c52034eb0adcdefee88f852a85ce03afe5d4403875fcbabc350bcd5d1b94c7ce68eec02": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f4382863638140b6163616c61706f6f6c30", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24680ced073e9aade1b5605cc0f9b86f9ccd841dcfdb3ca84d8e995a2df2660d35f56952ec7a5cc18c8b0e33a71e34965": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033438", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf246d15db4941658f2122c77a23a5c36b81aec17785fa10a655d77b8850a0b7c5ac3ce5c0389555ec7d8f303092552cea7": "0x09cb20eb25e42c2fa106d75ee4d44b9c2bac20323a540529e9f4f40da5337bb307424159414b41", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf246f29a2f89305d9c14a12e64e837772b2c2a55b548ad46013efd1c045ec47aa4ef52b075a2fa3de11c486dee807c890f": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033632", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24748debb1a2e9d09a1bc28aed9474a55e8b6a4eefeb4942bbca2bee6baee73280d49c1b7aff8d1aa7616d06bc173ad7f": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd070239", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf247714dd6a1914aa7ee510444367f24eaa459c65e21e9f36c344f9def7e0ef28e96f6e1fe02f8e3aa798a8fe9cf906453": "0x986a4fdcd53d0e438f9694c9a6bb76665bc50acf76180049117caf3ca9bbcc6a064f4e452d54", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf247cd033aa5760530bb024ffc1b8d150482c35d711666a25aeef2933e8c52be35e409e288120a6555139b0c45d90c140c": "0xbe776c10ad0e1fe7f606cfe42448e02cdb640c21214ea6c0c99df36ec0ee3d0d0232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2482fc4f6cc89ea50673ed04de1d8b8dab2a9dcb35e27b71b75bdabf1cc706c567e50bbb20631e4f94d5ef6aad740fc6d": "0x3674aa73951219dbd27b3e3fc5847b806c68c1de38fd4f22f9493a461c80e90311535745455420484f4e455920f09f8daf", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2483a52eefaac1601602ae57282cc8dda2c2a55b52b81262e103c0657041660ac9f500aefa757a2c8343326d0a525f512": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033638", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf248724e6c4a1e4a8d0d707fba3df2abe0ca44000a7a16d0e5c56d22a707e7cefdbb237b9d20de2c46a53b551ec0cf1c40": "0xb42bde3f29708150bd47382f10fa4eba1c27a068653cfc4e3787b7fe05fe6e7d035631", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf248958a4a6b513199ef2b775e8a369e4b7cbfaaf0fedba11f23780e8b1bfcac5e92a85f56835027e83bd203bab86ccb07": "0xfcba1bf303b55be82bf6e48504909dfb85be27bcbbb2a330e9f4fc1261d8f02a0544413031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf248b7f5f33dc48b7f64a284b87211b9bb8ad3ba81e44bd11349a8d48bb168d583decd0257d3237df7d55bba5051f5254a": "0x9e4e7009937c56d267338762a60ed004293afd40e7c2081847c12cb63c76a8180841636164656d79", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24997c7a30b4f80e1cc80fbe180533c4706e11fd0d4df6c4765eb346aac47682cb7871da9ecfd235255f6eadb8392b20d": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504636331", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf249ae6790c8b76029052974dca38bb2e254730499c6c53dd16d1e3f8007b64be019cc9229db22d36a12e44eff1670cf5f": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763136", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24a945be2a7e7f62c4371175cf087af052c2a55b595b09d23cb9ea051e7655f90215123b6cf4edc6bbfafc3a99e366708": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033836", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24c0254aeb0f63a7ef97ee7249090c66d94211c46d7bb07c67c2bc80e7d5ba4623f8ef0d565d266723ec60497f0375b3b": "0x8a56b1da8dc3f4bd58630c15f5754ee634528a007ab510c86a7e1fe6e62f4166065a454e4954", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24e1c5184bacb25d01d56ef92471cb065dc46b7f9c2debfe58700f30be615154d6e38f059a682ca0b5049285180675c0d": "0xb204051d55c2c80cdf6e0d3346944e921fc97f0b86a4cc5eb5547982fc475d68065354414b45", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24eb15877fe5eaa93352c331f64661d75b4fc817716823ab3322f35702e26df4fcff8f577aea26e05bce9bef8b51b6707": "0xf3b15e49aede08aa29d7400da8d3b4fdbd284b5bb1f1af1a53a8c47398f1f09305504f4f4c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24ef54d965f89550dc64fc5d503df3c647c426559413ab646ae9152a606fce1486f754412a7e48f81e41786dcee8b7800": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033339", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24f66cceda2fa463c6e061bfeed16278cf68fafdda61f708ae4dc2e384c8012719774376e478bdc857f7191ef449ab522": "0x0a4a3233cb4870d9f20b5c55e77f839ce96a26f0fd773d01917919757664476500", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24f98bebdfef8ac2b8ccfa999893dd1076cecea2c48271687c926a72814cfccede993dad2b803ec0d546d2bafa586c11d": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb3893450563633132", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24fdc47e9772b90bf5fc792f782ada6e82c2a55b50e45380d2db51edfa07ff46d01bbc34f908f7aa6b470c1593576ac35": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033835", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf24fff7d12b0ff46b399caefface22445ef54df5a1ecdcbdc84dee4ff7821578632b9d6d884b9bfa5c52f7164c57d3fdee": "0x9ce3de1cd55ad6f4ab351ab212431d94cec798e1727176cca174fa661c8f636e033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2501083fd03ada04313b2fdf14a1772cf48b2cc621a25ed86391676c3686bc2cf76f06edc66a4c3c21e2452618ee1bf4e": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763035", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf250487c54a0b45aff5e09308bec36ffba6e28009eb2b8b7785246c452948c27169c4e3ed258ad7a707fb64beb9442fcfa": "0xac1aee6b509bdaa6aeb4718c2218bd7e78a3d6704bf886375e4c243b77a66b34033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2514d805b025f0256fdbcb8d66348bcc4ae34148366325c0ab46d5c086e05c87c76b58125490dababb7899e9efa41f53c": "0xf0de782e8bad3c663be60812f0a2ac63464f5da3ec448c73334c07d71ef27f2c0a53746174656d696e74", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25276c817edfa8a3bb8e54024fa886eefbec6a380acb8489f21891545cfb9b4964bf0f3170c5deddea166cd8f87bf2078": "0xd46cd0ae6eeab8ef19bf289712c9f2ae4272c663bfe0786d7c10d4ada52100030238", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf252b88b0a68d17f8a6cf5d3c4a232d3c3c88bc26c3cfb3797c1b2e2c9e7fd0320d9899271ba9255a2408c4feeb33cd425": "0xdad042c036fcd9897945a880458b8e7104b30617a9640eaa22b7e23e675e0a02035031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf253ba16c02ca30fef215fb6400b506655c804d9b3aa2f285568305291efb2e0a6b70aebdd5b04ce6a5506c0a8678fe604": "0x986a4fdcd53d0e438f9694c9a6bb76665bc50acf76180049117caf3ca9bbcc6a0550555a5a", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf253deffa69e616326106cf9583610d7cd82c38ac61bb075ef3c6f745af4fc8675526b69e7a66c05e777e15bf3df99302f": "0x5010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f0248", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2540ef2326634c6ae2404af890bc2c8b13e1f45bd8fa191f3441574abbe1f4ef3bc6eb07bd224560f30e45909eb0c8e45": "0xdae2b867564f01654946a095e69f0f49df1eb5c0efce5730cc3d1d83da3f4b090e303220f09fa69620542d524558", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2540f37eabde2631c3d6fca30b6983017c60f58cfada1c6dd1a8ad7625f8a184e6424e37c0ffaa604718981c1454ccf2b": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033531", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2541cc1ec3fed671ce229b12b481e90b0d0ed5a8ce0c20c3da0dade9db4969d52b6bcbe1f8914d3d9a2019526c9cb0b27": "0x2c89cb8652eff8c73266de06baa3760534e7f37fecff971c028c2910efd6a9470a4c6567696f6a757665", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2542904a84f009bbd8242464f4a4171c347500d616a78d59bd887bcde41d61f4ec880d0496fb4d9482f6f637f55cc1b29": "0x4026c99cba12d64d1d0e7a7a620bf54374603fcc056e1601e3713f992167112017747769747465722e636f6d2f706f6c6b616c75636b79", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf254aa3c4c23bf218e87a074430435c29182b224471e6d4cbe8d9d084d445bac45c91e88679cd3c22937d5e56da60e2bb3": "0x5e34b19d7230f1ea5da6d4b8fe6ede9d05c2e55b0189f75b967862c1c43d9a1f06f09f92b031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25526967f8d1abc061473b8c8ad563b9338f4dd8e0bd0a47c6263b31add7a887956da435cceec4750540743f0235c003e": "0xd401f460e0251ed41d7fb32ca463b5233b620cb9569eef5327def27fbd7c7b570b4341424c452d58203031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25539e7dce3e215002ae5e289a9015e183f24c61f66f5c798cc97adf5258664937f3c16d0b35121a8b45cc81611bc8e59": "0x3898b6f62b50749101446132f77fa6dd77a3895674fa8dac87e6c375ea85234604313033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf255e5c4a4b17e55b69ffb6138c9e216f82c2a55b52aad5c3cfb8bf4c7c478aa2b21b8fd9597fe2706a11457d23e10323e": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033736", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf256752f9f8d5ce6b947829aafc1b19e944e1f1d2881471357ea697093e5e68d46712d2b0e5b650945c4ecb571ea43757b": "0xd6c29a7c39cee45b0e045a94081bc188ef73be2be086d66aefd850fc7eeacc45164f6e46696e616c69747920486f742057616c6c6574", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf256d246a63af0871e9175cf3322bd75e15485545d9937d865c9fe34b5b3723ce78729178a292681d43bcfd35076a656e4": "0xd2a0035cec74b2f90f7e72cab1fb16b6ba8317631976b138f7cced3e00668b0b134d61676e65742d5369676e65745661756c74", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2576ff602f27ea4445af62da6e6b627aa2c2a55b5a0ac2e467c72c205ae9252a4e8a0c206703950f095c31ea3f022493a": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033035", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf257b6470a1a16508b722b109c9556b41d2c2a55b517d1c1a30c70133ca54050b2d409c86e87e4ba23b564c185f1d05978": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033636", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf257e15394857cd91d6fd1d468f950b52c447e00d54ea8610f6d515b7d7964b1aed5838c2971e585143807084c2a799311": "0xa2b06daf782fa5142e365d1fea0f4a418687f53f201f185bf314c37581677b37044d5058", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf258720a657c68526a344d9154d2ff4f772c2a55b55169b48ba48e99dabb61807463c1f91bce2ec9919c16f0fdf3616166": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033637", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf258e8f00f39a788bcc479b89a304f1f9c24f8b3dbcb13ea214b670cb611fe7939e20a23db19647485e01206502e64ef7b": "0x86f68361d0a346a62be267558e72dfb9e3b5a04adcc2c9e46fb7b9482f7c876f0748656c69756d", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25993b1b6f0559e550406e90d54b7e2cf7cea95ce431976642559cd8c64177ac1a51ef6c8dba625d3d021fa8807c1a137": "0xd46cd0ae6eeab8ef19bf289712c9f2ae4272c663bfe0786d7c10d4ada5210003033131", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25a5a0e9efbd3efac0a11d330fc0c4a3b4d0887549eed4b4479973f77c05a3f40c7d182e983c3bd873789a519f5cf7ea3": "0xc48d1c4fc44dbbc10b86e40db24f95f8924efba2b31eecc6a7c32bf2b8a4481a074c4544474552", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25ac36e6880bdee3cfad25d4a3dba84866feb764cf5265d8f491f921d51c20318870ffb669b059c7a5b951d9291636b8a": "0x86484f63c9e0ae1f460dec3b53307478f5ce3ffab22a1334d34a52da7527ec6408f09f9181efb88f", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25b50d8b53d5b8f5ad90ad0587de5e8492c2a55b528d6e2e0e0b7d4538eb005a63202ab1bb9fd8e23cde888bc573f6714": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033338", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25bbbb7f85791609f5509511a24485ed6962f61510310bf29eb0c0170a23fcfafbabf3d0b088e69d1982b2a31b0d7de46": "0x2c81fc1faa024d3d1a727d953187860b45bcb185d046631ab98261eb6f2b8d5416f09f90b620536f6e206f6620612042697463682d33", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25bdbb7dc8567ef315019102aaff7c7bf2c2a55b59a282fb173b7faf6e82984686f6fb83b0293dd498db6329464a45d1c": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25c1039d7b772c17eae4a70c176a843f266ced89b9a76de4d3dc384b45fdf0bd2f1629f15dd3e44ea14c31ba16181a255": "0xec05f950e080aa04f8ef335280637c8c80fa6b8bf54d5a3bbfa746b9f9da586005e29e9556", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25c44cc24df1b8c918f3f4b7debe6c94800831e9a6121a6d5002d53a89ce1d209d1e3359420a90620a022b22947d41140": "0xb2e07be4d6d82f546ec91d6009ee215bb736be5b4362e66e7b466ec72d47624f0b476f7665726e616e6365", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25c9c3f86fc5cccdc2784b49fd64761b446059d2f1a61eacb55d5c4c211d0b35cf3c3f4fe1f2601cd8bf49f30ac370c49": "0x09cdd094e9a51d26bbc6cbb71d3f7c5b8edde629402e3e5370e7f6904512fc4a033033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25d00a9a66b9b640a8446bf2336b62596b4a302cab1fb0489c4211db08beae0bf7b5432416b299f57ce785b037c297a46": "0x74db9a1104a31f8b431ece5bcabbe3e508d22fe13670d32875ff6347a88d13880641726a616e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25d523d8838a6068649028f12e0c439042a1202907ebf57434db992641143ac78c8631a1e412d8934e5167153caed0645": "0x807e371d9df95983e2b9face8efb67f962816f1a0c6681cb1e5455127ab45c092020726577617264732e61706572747572656d696e696e672e636f6d2f646f74", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25d56cc1f2a7db8fca1e344d75c83e6e814bca285494baba8c77a19f94848063b287f169a57d42ed48b409eb5554d1d0c": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033435", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25dcc9fffed71542cca82764f881633e74a45b84757c48776f4c5d887e269e8e6c3f2fc55f75adf222662f129f9fa5b40": "0x986a4fdcd53d0e438f9694c9a6bb76665bc50acf76180049117caf3ca9bbcc6a0752414944454e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25ece685d5cd33482a8021f353de43ec1acb22f070631059ce62f6e0536e561bb5d470d2bd236985b0dcf42cc3e446c68": "0x2eeb1a6884bf369c7d1ab3f9ff75b79dd0f6c6a9792834e026a8d2f7ef049e4d033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf25edb035a75e00aea3effc3d3db4468fb084361c7e80bd43ac0aa2917200339e51e45284349a264184ba0befe3e2cfd52": "0x6c7fdb8b8eaad1af9faaf918493606e1a3e8c20f9d852773ab5ebfbb93bd19480957454233474f2d32", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2601a9b0124023ec68b73d6fd9f54dc32b0614de4de8cfbd7c760fba99b446a030b0ddd8f00a0dd84dfc88c7875d80c07": "0x726a98c27f84bf42fc842794a925418335fc9fe3badb1f535dce9c9d9efcc02d0230", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf260cf06aff75e3e7a5b9f00bc5ef1d850a225b0e7cf9cf454c3ecf0d3477cd774c612795c312dcba6d09beea92aaf2a6c": "0xc804531378242158a794bd06c1e9ef0e569f60bb32758597af80e5e70c07a64f0d7379732d636f6c6c61746f72", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf260fd70358772eea75f1b119e85ed70f812c8663dbe1a18335ef5b7731a7ba8c0f25c0fcc761623af5159ec6eea586b28": "0xb8e06dc2e6bb6bd269319ace4cf8f663338f8f285a0564d6a139063c985d23100235", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf261ab7a6d5c53256cf112113d5307a1362c2a55b5feb0eb28fc697566384dab127170f08c1c2a6febdfb4cb3185ef8917": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033136", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf261c54c7bd272264261bce1472d00c41400cb68a2c03e666f346a277036e8f27c912baee3c7b41c5c19123840f3a8e8fc": "0xfa5e6f955d973efaca30897c4a3e4fbec88186ae72b8b331408804d73dfc275e037031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf262f7d26efd475a6f12c57341aa4781bfd8b5b26b1cee228c6ece5a4b44db258985abbc9f0168950d744269bee80b1852": "0x8488bea263878e7e16be0a8c4705f9729a5f25462bff7dc7f2d8346370c8ef651453504143455f494e56414445525f5354415348", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26396f848202862f75e3181d145e563470a776e233546799e6f48a26bf32c080bd40e35986f608353c5b61b3076503472": "0x6e53696350731ed439f8c353b0b69b40b324fcdcb435ba225fbc22a33c9fe15407444f542f3032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf263c67dc518c9a200dad59de0615719ea76729e17ad31469debcb60f3ce3622f79143e442e77b58d6e2195d9ea998680d": "0x5ac4bbe840e332da2656c0e760904003fa7a2123a216a33e81b8531400600304033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2645b62e54b242fc3fbbe28cb0b35e2ee90cbf37b94fbb758804f3f06510840b6649c0a2cacaaadda6849c9dcb766b767": "0x664513c046d4497ba05c19efac47cb0dbe498e20b089dff25aa08d1a77ec970b0232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2646b77305fb63b2adea883a08333b7f4fea1c72d488cbe5955b1eb68d746792321b1eb06616bab281f5d4838e0421c6a": "0x3296c9b3a6546d2319a764e00e1126215b776cb27571db2ef0392bbfbc66d45f04203032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf264cfee90d2adb3f3d5f42f2d9bd586e86492e3ea0e62e4e357e57c5b6732776f069e996133a08953b1c1ebcf967b647b": "0x2c81fc1faa024d3d1a727d953187860b45bcb185d046631ab98261eb6f2b8d5416f09f90b620536f6e206f6620612042697463682d34", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf265a8cf2580aa4be041af3cdc5c472db5ad21c2f113facb0631e7145ab7f255c3e46b49904587d6aa6116c56ad615fb1c": "0x22b7a9ef681c4a3c1743e5c343ff5b6f8aec3dc43bb0d49e29243d79ed40636504423032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf266608206f5eaadf361eb029c69e6fcec2c2a55b50279b01fc376a1b5e4e6670a8c247f00ca6c1569a9fd92ae18fd1031": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033432", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf266b542cb40878ffaa3b6eb0ca5973488eadae1c87f39ff2b60465c9640a1af260a815725f9ce3fdda291f92f7769e3b4": "0x0eb83479fa34dc63024ed44efa464427375a44de486e8d6007c7842b45ce817f037031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf266b6113765a3ae804d0c0a367181ecd9d66dba2833b102712151bbcfe1286506937ed28809693e730b622b3adb93e36e": "0xfcba1bf303b55be82bf6e48504909dfb85be27bcbbb2a330e9f4fc1261d8f02a07444130322d43", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf266f1255f7aec0e5f2f52ef440812bd4056613c69103858e51c79e3525fcfbc8315d93383b9280f0acd551bdc75a91463": "0x80d8a3f4317249a895e4b49badcfa7293cfbd215d6e552d1c07024d36acfbd5d08636f756e63696c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26792e700996f4c48444a1154cb56660d16b94e2d5d12d60c7314cca383bf185ddda83f413da740a121601e3277d3083e": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2686e757f35e373de52861a594acb4c7dbee56ca36a0a5393bf9bfbe5d2079e31d4359d35388df257e23793c7b195b855": "0x4056f76b206c306712a75e10ece4cffe091b1a49b0ba4c505f2ae4ee673c576a0e5a4b56616c696461746f722032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2697e24555d8adfb67c5a99ed9ef04acbf50556e30130b7c23f4ec520332817629963b2a69e5b024aafc29fe7bef6905b": "0xc8a5bf93006b7fd50ffc2abaffd57ef06c67f2171b5097070892fa1a195d920f044d4331", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26989b179d681f768d5f039a62c4b362a24aa9e7b57a0e49b834fd499464cc0c83135679ac384abd9e6d7f07a8def164c": "0xe21727312b2b7ce579dc0f82f129d4edecbcf00abca607d73ccf16e8352cc7770232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26a845598a4beff519823c3245a375f96b8815e0e6280e1a3ff96f62dda149363592236eec3df9d48aafe8796c22e1d41": "0xc44ee45000531bbf1aa4c23540940eb10599e14b4fbed5267c42a0ebf5d09f6600", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26aa91e2f85720c7b9e54841b60aff4b62c2a55b59ed1954e80bf1349ee5882d429d261cf9962bc5d88a1fc176e60c918": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033237", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26bd12d2e9c4446aec63d3cab87d3cfa246b00fc11146ea6ce12405e83ec552c9f3d66dcf81ac4fd874e24db1484f4041": "0x0cc0424184702e27c49fb859c93f07d8bb0adf2a0824ada9f01db9bf76b7025f055a524831", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26c03b239a4bd4aa6d90d5ee1c3bfdcac0edaa0d08e8b21d6e8f946eff381ad4be29aa63569a54a6a75c91b878b463033": "0x127a30e486492921e58f2564b36ab1ca21ff630672f0e76920edd601f8f2b89a064c55434b59", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26c34abd7525397291b54e3a7714bfd4030e9bd57ca72b2de9928df65ac7974ccc0fe678a64c2df03e2159aebb4e8a525": "0x82c5c0e26848d49b32a7ae09a67f6822f2a18310b69a3ed9c31ee021440208260b636f6e74726f6c6c6572", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26ce021714ba84dbec5f5b95b28f2a4d306cf58e932ba9179e8493d36dd0c02918e37649bfff2bcc24d4fc19d47492564": "0xa2b06daf782fa5142e365d1fea0f4a418687f53f201f185bf314c37581677b37064a5542414b", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26db4c875f9716d75c900918cfca41e6b2c2a55b5f19e53bd8bb08c1cf5d540f88c25b52a10c7c5816bf906b0ce20877c": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033934", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26dc4fe62186bdd4aed984c5be39192ef2c2a55b50d3c4b8b607d14c312d6e25fbe0a9a585bafd68f597ad37989f9c406": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033639", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26dc8711e6d710264dd5e8322faf0ca872c2a55b5e347cbea568da1dc296c13b681ee4b655108e78bb9c33980d45e2500": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033439", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26e10ffdaebfc791b4782085c4ee3e7f42c85bbe77e464b97ad4d36a49bc2b84566c38b9bf6eff49aaf005c672fafe752": "0x3e3a490c516b2a3582e6400e33f3eec42a12589958cbb86c87b23bc94710d21b0231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf26f250de59e16551d7912972b2d280a21d8a03bce1c529217f76264985b1f082f154dad529677f85dcadc3c04c4c77d59": "0xb204051d55c2c80cdf6e0d3346944e921fc97f0b86a4cc5eb5547982fc475d68084e4f4d504f4f4c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2704a0ebe4aab907b0f82f628ca6ddae62eef2aee654d4975535f2701af86ba6d169c2c9a1599b16635a2a5e4640db94d": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504636333", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf270ba6779177c38560ef3de1244a7ad2c8e9b0b6b26839418f4baad18a9c3ffbfa413d65cde8010bf7b9b9db0dd23e005": "0x5c5062779d44ea2ab0469e155b8cf3e004fce71b3b3d38263cd9fa9478f12f28077061796f7574", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf270ca2b9faef23df18a77926ccd75e1027c8c3a92d8feb9d27f32f3ec67bcc6792f8496f7ed86d1b249c54205a39ee30c": "0x4056f76b206c306712a75e10ece4cffe091b1a49b0ba4c505f2ae4ee673c576a0e5a4b56616c696461746f722035", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27149ed8fe6bcc8cbeba9ddacf8638fc13ab79a63ae81a8f096efbfb44d958cdd0253e97775b908d73de31a5d9cb00e44": "0x08ec72cbf62bb66f416f46f988e130585c834a381efdbb0755e30f47a2a0da5a0d5745415448455250524f4f46", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27157164a73e51400806e78b5fbcda4f9fab573fbe3296563205563eb39965933d33ea5a591c45af07f0eee2272ef6723": "0x7600c0c74304812e4928a503408a68c782c69a6af2fca55fcd9e48e095b448630653462d3031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf271bb90452019d586af6778148333f6e9c0afed691a6eaadcf94bda09fc7b713aca337f9eea5e7a02b1a6aafbb1a44873": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033432", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf271c97ac8ca71c03bc78ea90c3fd225152c2a55b554384ce603e7018c1bd0df5cf1a8b1f6154a761132c003486a723e27": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033738", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2722834a39ddb7b18e6c5c20e02957f4652057768741d83d391203c6abbce429b05bb6d148239c08fd104e6b65c531251": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033535", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2728e20fd18f59a839dcbd25812fe9dac1e503909a89514337bf9ce931876a08003396a9def52f867e123cf1420a0f70b": "0x7a895955042cb3fe863f3564e7b30e9130e37b6d905be11c0cf91064de1cd83a094173736574487562", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2729cdb4350b5c69a2d206102f47ac9497c2241b8ad2176aa340dea400bd84fc389091a7511086bbc78fa98a7356e630a": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763037", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27356514c77e1f6a759420b0469e0b00b26a81cc7f1e72380949491cb9538d125d40de48e631e0e8bc40964fddee59bcc": "0x4eb32a5fbbccb1d97d31237172fbfd92945caa6822d5f8afb558aa7b89bc5a110230", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2746e45dd2f0eac81eadc8e0f40c69425da0afe4e85d09d168759ffe74af6049d1001ebb6d16a0427e40a4e494a7372c3": "0xfa5e6f955d973efaca30897c4a3e4fbec88186ae72b8b331408804d73dfc275e037033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27488fef533c1b07f5346bdefb6485a4b8c038403fe48ee0068a652cfe2593d30d5701f508e38ef676f392fdc85f80658": "0xc09033af1dd99c3727bd222c35d9d34e8e4403441792399b08df0d60544fbd4811f09f8d8020444f5420303120f09f8d80", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf274f64102879491d317fbfd224ac8cb2303bcad4ae89b033d823dd32ad149177e99e47f1c1edbfe9e5281f585bc406558": "0x2c0d08e42e58247b57421f3239e0e192b21edaed4bca2458028c981634bdb60706416c706861", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27575fc2108aacb1baa021fc45f5592b85ae7010248daf19a0b83b3d131f63a693785222293af4354035b8dce851fb02b": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033135", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27649915cca519cd532106f5a0c5794e8487a87b4140abf956405e80acb86bb44586a8089e79b476516c6ff9b601dbc38": "0xfaf5f68fe828f5af8d69c116efd937d90f1956b0edd94e05d8b5285a8eb2a66305706f6f6c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27678b5c55f424bb242a9b9a65fb5cb3f9e2691c29d062502fade8144a0eac25be3369a271b3fdf2208a9d86cfec6f948": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033332", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27720e1b29336a317dcf0f7f2c7cbbd2d640574072818008b0ffaa91a3d5febf7cf106a9285e35003fd7b55e2c1ae8b6d": "0x9a2cb674ea2f4866664769a1663fd6aa321d9cfb89b67c402c881891700c0f57104d757272617920526f746862617264", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf278125fd27774a31e80ce4957881386190a016b7d735f7a0c5e7987f99b8e46137b6668a6593c064d42d050979935793b": "0x140be3ffc8865dd47a8d044916b26936a579433599bebca9d3ba1d6eb772271004313034", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27854176f581ccba42b32ced2bb079c5666d0425af5558b202c277f282e0d55775eb9fe23e0c68b7a10cfb7f59202b402": "0xaa6646d5b85790bc0ab869d80385fdc10e1f4befa7f8a4bf31848f73012d2823036e32", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf278d66265f3f443034a03e439baa8fc6747e503b630c37057023c04ea57149dc70ae19f186db24f59881c55cb61da522f": "0x09cdd094e9a51d26bbc6cbb71d3f7c5b8edde629402e3e5370e7f6904512fc4a033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2793f9ce6c95d41e649650c39a2ad8b08078447732a649b7bf6970bbbbac8df97ce628b139a8407fd7d051f40a0258f93": "0x1232508adcaf57c6e78a850f9d715e3694b52000ce537832eb55c7a59f859e1306646f743031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27970d47b90813e971a8a0f16041df7cb00b03b23766d70d0445943b290606521acaefee7660d521950faf2801c79d428": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f88370234", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf279ab4cc0637bfa6a0af4122c141e61372c2a55b585dd3e7bf5cc92d3eea96cd13018755382f0ba1be21ec1866b043212": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033833", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27a319d2a73687c9bfa3b053e9103dabb1a356596f667e9330b60d055872640198b86485ac1721c37fdfd468157a17a45": "0xb22f88d05ca6fdb70235b599b99c69f927ed71163c2ebc11c9649d678a8ba84e0c56616c696461746f722d32", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27ae2b908a33c5ed18881a29cc36193b5b0ec35caca1aab56df7814b80ea0585eae79ec6f58cf03a8be0c40a5c6707711": "0x1280a479ee3beca7af1636aca17582f30829782e2c9b1b9c72aaf8060563ab37033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27b71a04416fffbce603e09de5a8178ce02aa5256d804b33717f1d338eef9901e89682b80c81e3b1138a02079e0848aa0": "0x30cfdb48ff7f33b08499dfc618a8ef9699b8345fa65f0b1339eb8eec3c0e45550231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27b855f3aa400faf901f706ee3942185d2a211b13ae9c29f805070d21d5e5e007db8ab2566e1031b6ec22733f1f3c0877": "0x465c6be30d314cf647a8fa10212bcb84318394659c46fe75d03640cebb39595e1a416c7a796d6f6c6f6769737420636f6e74726f6c6c65722030", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27be9a3f8da53c454bff90ae9ba01dd96b4de0e0553f854c72746045b90c8e5c67d74f5d8a52d4134b259ff562e4b1409": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb389345076c646f743031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27ca1f946b86dafb5047b0c68ebb6f07378c505721568ecb57fe677b4c24e670079e8c342cfbc7b312c146067a2dc02a4": "0x6610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f380777616c6c6574", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27cd11db5821a7f19322ac30eaeaec8192e001e3f827e6eed45a60d131d97df1a5b429ed1d0f89fdedf3eda0a16502d3a": "0xb346948ec9e4cf84b965ec17a752b3e8eff098934aaad42ec50a347dd7936583064b616d696c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27d31ff5a817bcf68f902fa99253a4268f8720c905d7ac1acab25c4f353df9eb759e0141e4732540d163ac444260f0177": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f4382863638140234", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27e65087dd946c27bb1e415ac33130b0fa65bbeb4425c55a611da4116e848b0cc39686a11f88dee6aacceac6bc5eca657": "0xda9f7fd3d9612a68d2ead69dde53297b172b7db514d0d261e7c5be987df7f32a04314b56", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27e7a14ff98ceda0098f284ce8eafbddec880ad522ee62f59ec14aea01214fb737df562d1a9bffb35d70d35bfd2c72432": "0xdae2b867564f01654946a095e69f0f49df1eb5c0efce5730cc3d1d83da3f4b0910303420f09f909420434849434b454e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27ef31002060ecd1375d54d35f3c7afdb22e84530cce98a0af194e12c568c8923fe6d138dcd6e19fadcece6c0a5f10e87": "0xa24fde6343e2bf0aefa296afcedea6f16d37c5e1c0d8b6511e7055cf7282b60c1053756c74616e4f665374616b696e67", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27fb6191430b9e8731f77bb822cbcb63631918cb9b9c9414a2cd4dd7f720cb98fe98cf852636fc4860845767989127e7d": "0xc5c184f0565e2192d6aedae584ee736cef875f0e1c558ee3ede26869acd0b4d6054e617368", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf27ff5e411dd09d48e564e8e5d339786892cc16da9d1f7271475075aa8eb5c6667714426b8c41dbecf92bdedfa462b7163": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f88370236", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28000519116f8a1f19043cd513af0b2abc07244af8ac9b81030b3c47c23cd5b10701d4a5490790e20d12a583e5e606823": "0x825872f7d324c8d97a9d5f5c94d918eea93ef783f305767b768a76f9fede7b4a08e29aa1efb88f31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf281043fcb3cfc5cb4e0e03206a24d135d2c2a55b5f64fedb5e9a535e4358cf539e294ccdddc753610fbe8c25ed72f9409": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033235", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf281b653ca735cfdd41d4e90fab3490af3d68d71f02028d8c8d8a76dcf0eeba7f93ad6b2a6e2f8363ccdad5fa580705211": "0xe4bd04a7052f76425c60648c528535285bc2a23ab28db060db34f7c5e5746aa909434c41494d424f54", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf282576488c56e00b214d585a717339dda8c5917b51ac796f4dbb53a35008c1e0cc5a8e3aaa1300cc2e845f8e7702cd4e5": "0x3c84767978ada6355ac3719ea8f978244b16d7e8c00552c3a189760b55eeb404033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2829d6f3b7b3efa8f10f9077fe36a82e498a8cd51a12a19dd5440fde5e43cb50f9d48d95ea5c5ee3618eb0b2945f02f21": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763132", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf282a5417b0e8372e7cfa89e428887784f2c2a55b5e1d0f2cabfbdca681bac43c1f684748d18c03b0e04e28b2fae9cd704": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033939", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf282db2fcb58fad99c289f7a86656931e12eab66a1c3116f15f55dd2996db419e367106043a4c5491a5eeab1d33a17460b": "0x02bf32e061073c44300056b416cd66a4fde1e6c120dbc0089bb65134f5693a3b14636f7265626c6f636b732d6d756c7469736967", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2831ad22a3ac7b99bbe1efce6cdb4db35aaff773e3f134d4e0c90ef5e5f37adb603bb591b7aadf67a2a757101a1b70302": "0x7a895955042cb3fe863f3564e7b30e9130e37b6d905be11c0cf91064de1cd83a1050656f706c652d506f6c6b61646f74", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2834a4e0a38c3e40a86c37d60c5bf2b1f7e74e295c040927de4200c770994a314185d3ee447be3d70c79ed056fdd1ac53": "0x5002926543d8b8887044442834d4fd5fe3d6eb257719daefd3f2aed28eeaee690231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2836c70c471ff3bcb9c9b3970765eb06594ebb855993bf0568b71fbf4197fdee4cb44a39bd46fa5969bc5c372ae101367": "0xc09033af1dd99c3727bd222c35d9d34e8e4403441792399b08df0d60544fbd4811f09f8d8020444f5420303220f09f8d80", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2842bcb5bee0cfd16df02dc65ce8fb10a785c76e65b2f38d3dcea220f148f8d4b42046ddd61eff8af6828d24f633a9c47": "0xd46cd0ae6eeab8ef19bf289712c9f2ae4272c663bfe0786d7c10d4ada52100030232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf284840437a62edbd7601338ddd95027f4a9695441f301ba78bd00ce015f2f1da14eb8914531fa38695502369de72c256f": "0x8c085e6bde4ac25702f54f46fa3c1b0a6170bd346103c2c6339911e00306a0540231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf284cce806eaee977df2ffe64300cbac9fd071e04ec30e105db26f05b81646219bc57909c674c2a081e48906e604f9867d": "0xb2e07be4d6d82f546ec91d6009ee215bb736be5b4362e66e7b466ec72d47624f05506f6f6c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf285190df364d7a748b874cc7ee3c24062fe9d714ec9ee0f74e33282dd9e411fd0f47c1aa17553392642df99d1440df951": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f438286363814033130", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2853aafa32e9618dd96acd68a704bb74bba318e50a4896c8228b14dbdca63c64b9a4fe82ee967a41612c4a55909daa960": "0xf0de782e8bad3c663be60812f0a2ac63464f5da3ec448c73334c07d71ef27f2c0b706f6f6c2d61646d696e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf285f2ea0dc6f1ba18380d529259c4a2982c2a55b58f78423fa34f63da52ccb699e46952f94053123661ab8316d4f78e07": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033731", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28602d0517cbc0f0b26b18babded8bc622c2a55b5e4935526d6400d729ce52f5065327b414a971c69fcb85eef3d5a9401": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033931", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2872b81b0d92a731a6d2ce1955c86f1342c2a55b514ea318202f6b920073d878c2c9c94fb38a48e4c7b3d4874f755c06e": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033339", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf287ac14ce59b2db48f1386aa4d8361162eea674f13649b25e552a59b65dd203fd45055ae7f39b5a6551cafc9d4eab3776": "0xbc1deacfc7e5c6e5f0373560c14fbce156ff2a0ed7e208d049ccd985dec85545084a757069746572", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28954782c43b8bb9c87f90ccfa4352c1c7061c3799bd2b1d70aa6c8014a0012ea494f4455d60e182dd1cc393d37f8604f": "0xd45548e42d7f5d5aa35ffc16ff29396c99936f0c1eae84d452f1daac87c3c566064d65646961", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf289551daf6fadbe86a54a27a1280f9027149fd573a4ad8eb5dd80a1b70e89362e72f0fb2512075b4d9a52e7d23c0c776f": "0x3c84767978ada6355ac3719ea8f978244b16d7e8c00552c3a189760b55eeb404033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf289a625ffd867d7cfcc361cdf39c00b28c215be73d91712a74db57cf18209ec172e9a3215ce6ef5cf5b0292177d3e1140": "0x7cbb0ed8bf228935241774290753bf282020d73e45f6724b0196c97b3bd534620a627269646765687562", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf289e9e9cdd1da3bea187471c581cd43572c2a55b5d60f8f51745d9b979ea7dddb0f79692d51cca14c4791e72da59ac963": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033932", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28aefb3ff9897f97f9a756efdd5f1f2cb2c2a55b536cb66c8bf2894af968ada12c6e7db4bb028a57b2247333440458469": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033831", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28b37f7b59891ba4b1fcc3d9e618d854618c0445577a970766dfbbd43589ffdbfb1bae33e8f286969539300c6a49d0962": "0x18c044557335d26c3a538ec7a2699ef5665cca1d17755cd6ae53d41f5bf316230b476f7665726e616e6365", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28b7ea082adc3324e5b78fd6df0b54fb4c2975211d5ab22d276be7dc90646aaac121341cd5c033b2ad6e1a43d23d2b43a": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033439", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28bc458b9c4ab0df59fb5255f34b4bb9c18cfd7be6a32e4e3ab369f7be8a880a70f42a0ef260ae11a740b1feb7dc47969": "0x1c6d8b40be9990c19e993d238e6e3613cfc6cbc51979d5fa61dd6ea2593856090b6e702d746f67676c6572", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28be0e4e81f83977ffa5879c873f9a3af9890fd3e365f6954b5bcab3c8195024da5188beaafb1b13d954c0281647ba535": "0xfa5e6f955d973efaca30897c4a3e4fbec88186ae72b8b331408804d73dfc275e0c636f6c6c65637469766573", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28c1df885366c4853b057f114e18be01ab6ac93bc22957dcf5ea1a84b1ff7e60ad872eaa73f3d176ecf7b980cd33b8b00": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033230", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28cf1ec94779b507b452c5a8d569635a52c2a55b5079b0d798a442d87e4bf664a01d50c11af9ca230ee4738b19aa95b6d": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033737", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28db0ce9a26f228a1fee649279def89540aeeb3fa38505a54f76f2a27321f9b5b875635d3b05a0f4bd92710105f377f01": "0x6e53696350731ed439f8c353b0b69b40b324fcdcb435ba225fbc22a33c9fe15407444f542f3031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28e23857eb94353aca0e54d2139f418bf2c2a55b5baad165d64eace5f75642ac4612ec1ef12bbc1cb27a0137473788257": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28eb702b000ac07c484f721d86ebcac24c63d29b9669894bd812dffb0a0206bfaddbf728741b3219e9ade9937bfb1874b": "0x5010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f0245", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28ec2e6f95318a0783a464b6de2c96a7a5ac7f6af5aeb5364188840d02f0e74e813e6d9cc0398d6994b66727658a4fb30": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033130", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28f901de1e12520d1a72618f666f08716f2e78c673a855ce341b8d431b3e2ce293e812236e2e42682047796fe599bb734": "0xa80ea94af8a39eb7ba8d9afb913147e67eae84f48aad7b9ce6ee05094fe0394e07476f4f70656e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28f9f94d9cb3fa7cc5c1578d471b1dc673a92d9b2a48ee9adca77ffd658b1e273434924a7b3b456693219afbc32279e10": "0xb8e06dc2e6bb6bd269319ace4cf8f663338f8f285a0564d6a139063c985d23100234", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28fe0ba25b107b745ffb6f32ca8f1ae85c85cbec6e7576580deab57475b75d3456c379f8c4abb617969fbf99aa4e8c076": "0x1280a479ee3beca7af1636aca17582f30829782e2c9b1b9c72aaf8060563ab37033033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf28fe3f5cb994e72633471458e82ff1381aca4f84ed6959bdee967c0bc4d289bf3fad8671f5ce7068072a7dc34964a8d0c": "0x561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d640234", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf290b45f1b25bc12b37bbd4713609287d62c2a55b5b05723585c7421428d9ef451313e33e13803424b0f8cabd383d0df37": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033034", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf291300ad34b9ea09a8d8d4b867e87c876ad0992e51165d995e649d0a90b3c349ec9694d84ab1c3294e4a9c58839e7b4c3": "0x1eb38b0d5178bc680c10a204f81164946a25078c6d3b5f6813cef61c3aef48430b46656c6c6f7773686970", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2913678ec80e3bfbee95e5895fe707d35e0ca65cc737d7d170d9f7b09247f9cbc52142c62a6bcb4d8a5bbf29e6cda7e05": "0xaa72c321b20bbd5b78b14f6fd800017bca47190956eb42ada2a4d8f8a8ca994d164d6174746572686f726e20436f6e74726f6c6c6572", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf291b7d14915b81b93ff85d756391526573ef0a4136babea739fdcdc300f622f23d9a7fd229dd90c8f3c9e3d75fdeeaedc": "0x1ecec4f3062f61988b13e9dab318860bd0fffe5b7b37880d50a614a0a20c250219f09f9a80206a6f696e20504f4f4c2034322026206561726e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf291e0b9d1df879c873e96ce1f8fde9d7a081c5466574f932ef5e1469e984d5d39ad5946468f0ab9d06c454f74cfc2f16c": "0xd4539a7dceeeba9999b6387e9431e83c53f4b884edf5cf049623110eae5701250545505631", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2924d38edce0e39108ea6be45077495860a8a307ef15b9f928697fa09dcc72a2c19a266c1d32fa158f9916b8b804e1621": "0x984e16482c99cfad1436111e321a86d87d0fac203bf64538f888e45d793b541305f09f9383", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf292e1e7a922ca653422e42711cc29cc538cda73070bddbcd243f2d1f25982dbee2a0961b277ec209756bf794c2d0f7f78": "0xb08b555a5a3b2725e01ba15eb40aa32dc5b781532854b797808ed45e752b047c0231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf292e313c38979b8bca120e2d5471df17d6af358e5650b61943ba709efc5dbc501405e04d5de5798087d6c727027511a65": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f4382863638140237", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2930c4917f16a6c61684103d1d981796c2c2a55b5ffdca266bd0207df97565b03255f70783ca1a349be5ed9f44589c360": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033132", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf293ccb275d05874df082d79db1d3bdc3076c26a1fb9acbdd56be00d4c44901856929b9d2a879caad6119ad0417e994949": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f88370239", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29432d823f39f0985388d21e1330c779a2c2a55b582572ea3a02d92d1f303959ed8d301f20cf7377795bba4e77ca5f059": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033739", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29499859860e76d338d9a9e56fce67bdc780946289b4befff0da9911c013aacd2b280ec0529a759b94c67e4899b9b7058": "0x5c5062779d44ea2ab0469e155b8cf3e004fce71b3b3d38263cd9fa9478f12f2804763039", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2951d5278a8e74b67f202716b3fe4e163049a9687c22bf19c419cfcc79a77b60b07faa3df2034d7cfa4635350571cbd32": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033239", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2953380a1930e1a615abf34008ca176ee2c2a55b5af6d74281de0813a60beb2a89ef39c6b2a10a35427c683d61e98f170": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033434", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf295475914bf4f6e47667439674af275652c2a55b545afbf04bb687cbfe5f7a4fee66f0f0e9036072dc3ef88be6f3c6c36": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033837", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2959646c7eb21bfaf835cdf5321d10c972c2a55b572dd8f780415f21ee812b5bb32d6baa791ce4c8fdb04a13eb21d3f09": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033337", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf295f3374a3a2708d729b249f44f5d74f7c3405f63dee12641abec6c12b29453b7065da04a95c09998673aca6f0c4a164e": "0xa6c5f0595d6ed85d6260bc682d4a68a4b4e605d43c67d56f2765d19698549772065374617368", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2976d81795a66d60beee052e684e8cd4bfa5be617ad31f9c9a41041cac3ad8ace2c550c28c73afc8e34367d64b50a6679": "0x561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d640d56616c696461746f72203036", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf297bb9c1b69660dc90493b5840f176cc32c2a55b5451bb1d573ede48bcf7ebf76950aeb780120ecdf3d328467d62fd929": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033537", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2983e6a30e5b52f58c129cfe267c0323b703bd4afb52b696047f54d9c991cc8fa8d7a7734f034a008c630c97f95250568": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f883706706f6f6c31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf298c6979151fb437f7d051660f84e2a6bc08d5de7a5d97bea2c7ddf516d0635bddc43f326ae2f80e2595b49d4a08c4619": "0xbc1deacfc7e5c6e5f0373560c14fbce156ff2a0ed7e208d049ccd985dec85545094c616e69616b6561", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf299339913f2ea3776e109aac28a60ea1d80d57608c732427386079d29d65035cfc02b3221ff32cfe73b540d849aa00462": "0x561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d640d56616c696461746f72203031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf299647f0ce03184e7b26905d6dd31c6616a66d0c75a897e62aa4e9cdead9f50760db6e7beae858c1bc3dcb1d1ce601e58": "0x86f68361d0a346a62be267558e72dfb9e3b5a04adcc2c9e46fb7b9482f7c876f086c69746869756d", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29989d353f6de2de2c9299baa910c36663c92bc22c4934341504e8b6f0755bb76906171bcf8a55c49a78e2055ddd28802": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d0a506f6f6c20f09f9a80", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29a4392a681901e7e99bb41ef9de7150e888f666828e8328a33647a47bc97574a6a5671819270cc01e66c7139a1a6911a": "0xe8c7ad65c15fa3ba64424a61b177382a0c5468135aecca9ca454f5e7ce4d305b00", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29a756a8f12d432fa50b944ce913ad2c2e417170e6d77f90f6d7b308bddb8a414f44a87623704da628229ba777b644647": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c0cf09fa69020534852494d50", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29a8b6cfd4ad6e629f434a37da2ae59fdead7c523761f8ca72780d241f11dfb4b3543c4a1e17263274d34f468001d571a": "0x8e07d43b19d901badf3a7f57155ca84f2f835448e93a141bbbd33eac4b767d150231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29b341a8e37405326ddf25244c70c5aa24c1bdac31e30cd50156586f5009d576c2efdc103be5ef0649d55d3b53941760e": "0xc6477bfd57c12587b1075a80d944c7829784eed61a5c8b8255817e1d62d1070e0c56414c494441544f522d32", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29b6792186b130db777b313c57c8c6c156e306a119e947513f180b430e3e75bc8c476a8b61e8348d1f87e490121601b5a": "0x5010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f0244", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29b6c7d30389e104491c2397c994076d94877511245f8954e48858da743b9eb3544681c27ffd8802c8ea1669e961a2b61": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763034", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29b6f49c1c3cea48fe030ea8f448a6f542c2a55b5a6413894e13836fb0165e6adce7d77c06dccf42b3b288397a27ddd3b": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033130", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29c3d2817eeefc70b333c5e44e079e6ba8ae437cc2420c617f2cdec05405db6c449bada7d2b2063eadeae636a25c5ca79": "0x22b7a9ef681c4a3c1743e5c343ff5b6f8aec3dc43bb0d49e29243d79ed406365033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29c56a115b77627119d1abd9375e625892c2a55b5bb604a28edf5e8b24c72f0c851a6440c1889346d8ccfcd298b601e25": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033433", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29cd1fa0b313309eb252fc2216d869b058595dbf64624ef80da7a916f139b607fe8ac19aed219da7c7f9990be2c214d1e": "0x5efbe83a561d19b1d21126af5f1608ed28e56f4f70251cf965fe3dcb4901a26b034b31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29cfe8a4e14b7806a1c3022948146ec15a160a7305cb5b47a2903f6648fbb6ba1ad24d72fd49c39ec7f9d21e184f3dcc7": "0x6610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29d62ec2f1c1a1b440508c50c06d174f058ac509e6e93bcf6ac0800a070f28bd477fb9e9717ff7779d035094e361b1504": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413063151554944", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29d63ac56016d275d23e7b3d913045a0cef7550d2801399e7f9b263d92c33ce0eef338eb5527760235e4ff5219bd44c9e": "0xb45f7d4b6bd58cb550270c1ba3d9d97b6766787966b0b5c52c79b4d3b51d9a43033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29dc4559e1187878664e74a61da9447c632f5040e4ff22a9cea43f7cfd1242e3ae0e57140c2f4b985e83ffde5e5c4492c": "0xa4731404eb64407b76d75dd815a3267e2dce24d8c2054f3d45d83ea11c8d707a056b656669", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29dc6f90ae9ad151eb3ddf552adbff75e5e348817abb98cb962fc0780a47ebd471d9c318395fa80b4529a64cfabb2e32c": "0x4056f76b206c306712a75e10ece4cffe091b1a49b0ba4c505f2ae4ee673c576a0e5a4b56616c696461746f722033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29e4ca2c4c5877d06609b129963d870f744e2fc205de7ec0dfc49f2e05c64555cbfc897602413c712c93967b59257e537": "0xdae2b867564f01654946a095e69f0f49df1eb5c0efce5730cc3d1d83da3f4b090f303320f09f90b220445241474f4e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29e758ec7459726d3a561d0e141080d46d4171a4d884070585e1bc36f234a9db406e6a89575ee9e5ad24605e146dc4c28": "0x3296c9b3a6546d2319a764e00e1126215b776cb27571db2ef0392bbfbc66d45f0a504f4f4c2023203134", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29e7a3f75224238db6e3758836b8f9461a8e87388e083b3f1a9dfeef27977d883cf10e7c94acdf0c60f57f0a9621d4539": "0x4056f76b206c306712a75e10ece4cffe091b1a49b0ba4c505f2ae4ee673c576a0e5a4b56616c696461746f722031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29e870d2b0cec845a2caf420bd3b7ddec8a64dca67c64d9361901a1415bfb3469b000d0bf7f1d439824cec71f87022159": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f4382863638140233", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29e8947cdd87ebb2864e85a33f3dddc376e32fb9cce7edb56991320b049369ce553a5ba93c5d262dcbe796a9b9f3f1524": "0x5efbe83a561d19b1d21126af5f1608ed28e56f4f70251cf965fe3dcb4901a26b034b32", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29ef9cf0b7a6ab68867f5055b6222b644605fd1308af1ce85bab5ba3fb19b330ab7dac29e01ad501420560f44df7e0e1c": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504636337", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29f8a1dba08e7b6c0bb2ad1343251c54b1aa604e30e2baa8547c93923bf8be8c08efa12efaa6b444e214a3d631ed54c04": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033338", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29f905c1baa64d9a27dfd9b4ca546b0de2c2a55b5364338fadf45981d173acd75651f8ef41b57931694c3870cbcc54870": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033732", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29fc537374bc9891d40a39204c7093f312c2a55b505d8d997b80d747395c1471ad1e1e8aa57319c5727c6f178bde2422b": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033838", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf29fd0b6b8355915ce3ffa826e5b38279e7e726af71a51eabcd429888bbd0e46ce1e63b3322d0683994ca825dad3d3716b": "0xb22f88d05ca6fdb70235b599b99c69f927ed71163c2ebc11c9649d678a8ba84e04466569", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a0505d22540321748210e91a159ca996faeb0076ed1094419bd13e852c388586de314e8437d9c0e315884c10f1b5520f": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033534", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a08e557c63b7a8007ff6689842110e61a28942a9d2e8c8501860b847eecedc45d602e614b8b0849b959607d0dec3d071": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd070234", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a15ff7725017f89eb1a3e7fd12423158468eee3d896dc822a496e712d1116e0731235c54bcec12e41eb133bf9c98cc15": "0x3c82ab06b794c99f14a161973be7aa6012568b1c491d45ec969ed7420bcfaa5904563033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a1d210e875e95a48beeeb7de06a63a8dae1595f870cc27a34374a6b819a554e242997efeb760433c6fdb4372c2f28204": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb3893450563633130", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a31f5cbf4475bfc9efafc13b1b0c25c7c0a05f52c96d2af9862c7b89bd23a92c7e8345b92f1115d368b6548a58aa8f28": "0x8e602e63afb364ac583747b0a8bab092d5b20ee98f0495ce7562a8c992ac9f171050617468726f636b4e6574776f726b", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a40eb48b4152b4473297c29ec09686691a7eb7be90a60ea3c4b467df2f8fd89288ef44f581426bd98cc34a0b67bb959f": "0xe64bc946c10a1f75e683f236e72a44d6d61b3bdcf72ffd8c738e488efc6e1567033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a41f147666b3bc8614da9accb6345eb3640d12a59e7ac3ab24ad5c33dada639c058006c59fba147ce8caac535e801415": "0x091bb84ac5b08adf128e855e1cb079ed594be42af19d09d680dae982ac209ffb04474f56", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a447ce4afb0ae4367d7392a01823a74b6c9e3102dd2c24274667d416e07570ebce6f20ab80ee3fc9917bf4a7568b8fd2": "0xf65f3cade8f68e8f34c6266b0d37e58a754059ca96816e964f98e17c795050730767677770657a", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a47564e9fd9e709bdc7f4b0f17b513d19a9c5706a3b70b507dcf6b013c69ef08af652b1997d6fe82d5ba6975a2581b21": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413064354524c31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a5ed7d2724d100d2d05bdc1789317d1965819df33c22d72346bb95afd79e55414f35acbf0997c6bbaeaddd1d3688506a": "0x7ccb1907030863dd708764cc88a4b4d09dc5bb0f4c9ef5f4e73cfc0aa4bcdf3c0c426c61646552756e6e6572", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a61143348f4f5d9480149096130d207f1baa453966c043ca367ccfa19f450244447b9d32f4b7af2d9749e55a57ac09cc": "0x09cdd094e9a51d26bbc6cbb71d3f7c5b8edde629402e3e5370e7f6904512fc4a033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a712d143d4d87ce437f368647a2bd84d0cc89d59de520e70fc4b9bb9a43b41b2b748ab0dd51ea18326e8ce755931ea0f": "0xac133ebfde441672c055b79ca9a6059850984eead1ae036f48ca1230e7f0556f06427261766f", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a7c5daa2edcf3e3f0564c7916ec616c22c2a55b9020732ceb7db3ed3ad35a2ada31a65bbe0ac94ee072d92cd27a38f7b": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033938", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a8241236ab722210dcb57860ad26dfc420df777c881c5f4eed3f1c75e29c65fa681a63dc612cfabc5217f4308924e62f": "0xfcba1bf303b55be82bf6e48504909dfb85be27bcbbb2a330e9f4fc1261d8f02a0544413032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a8e619e1be259276e1931733fd74468913be73ce92b712d00931d4980713bf4be8974255e1e51a7ed71ed2ea37f035dd": "0x6ef40ac7b3e092d0eca77ad072cd317683ab9d24aca4025788421d4276527d57064652414e4b", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a8ee49496c11e988c1de658b6e0d91892c2a55b527a77c6ce5976953b4b0bfd3f7db8a96d27fae160f24a0a6759cd466": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033735", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a90be8e2b61f9112e1ead4d7fa120b6cc676e6031d0f7fb3ead234afe813ecec2d6e3b47fcee702981feb2c168e2c37e": "0xc09033af1dd99c3727bd222c35d9d34e8e4403441792399b08df0d60544fbd4811f09f8d8020444f5420303320f09f8d80", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a921bc99ba337c424ac2e016f6ea4d330aa18c1ff67dccbb98cbb86ea9808b63dc72582c602fb0dcd7e6716dd9ed9c75": "0x09cb20eb25e42c2fa106d75ee4d44b9c2bac20323a540529e9f4f40da5337bb3074b415a414b49", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a92f96adef1c22eca20f96605e019b3132aee225f2714c573eec965a9dd1e1ca399636d9158ce068842f0558f360a435": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033233", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2a92fa064bb4fe97cfea5fa57b0ef07ee98d7bbcdd3c7fe6e9bf7de42bc97968143fb02ce4c7f2382552237dd13982559": "0xfcba1bf303b55be82bf6e48504909dfb85be27bcbbb2a330e9f4fc1261d8f02a07444130312d43", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2aa0d24943c155775d7f5956b228aef940ec3f7291f82335606f98e16f5480b38b3da95d2e7ee9489a89a8a39f9dac956": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd070238", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2aa1d8432f3ae2dbfe11172e994ab5a6e223188d5f28ee27f7e9067e89bc52fca8f1da20c6a7548a21cef18d8934f820f": "0xf0de782e8bad3c663be60812f0a2ac63464f5da3ec448c73334c07d71ef27f2c124c6974656e7472792d636f6c6c61746f72", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2aa452ceef83f65de0ade28ef358d5b37102df685c4659f9c242ff9ac8a4ee5305770ede106db7a3cd5d3e8823e33d001": "0x3c82ab06b794c99f14a161973be7aa6012568b1c491d45ec969ed7420bcfaa5904563032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2aa534b6d5039aadd4851037521484c4c2c2a55b5976eb2e7893f7c3f875b3b1cf3d653df30a0a58125d9fe0f8b87b832": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033930", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2aab63b090fe2eeecd42800f5b691cc732c2a55b56936ce0a4d5292c66857a725cd7b30a6d305922f4631380666e5ff2e": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033634", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ab89d60f1f71d97cf57fc21e5c05f15f2c2a55b592b0caf0f0440b89aa8c1d713be7b3a16d79b3c56eed19d7acdb6e08": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033830", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2aba55d6a30b782ecdad58fac612e470c24f7bf2054bd95b8304900a908f8b298ddf79ccc09931f514305c490d047a63e": "0xcc52156f09978540d3163b798f65a8788bd04d828f366674bf9ea1f88e96f5190a444f545b315de28fa9", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ac1d7c3437476e392cb67606eda3c34a30606b4c1b89b4e562efafe76bec80154ac8b3e16e04c2e0f619bcdc0a5eef52": "0x3c82ab06b794c99f14a161973be7aa6012568b1c491d45ec969ed7420bcfaa5904563031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ac40882e593b56d2274898a5cf71e2542caa5b3f92b80dc21b417252e93eb55ae8c6e6ea96c7424bc80d00cade786627": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033431", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ac82a1e1a0c89cbfabecf8bd0837093f24172a563943291c97d252def71e17abf467a1626bca358728a90a82b3de3118": "0x6c695c4e546c6889ca591b582eee8b49ba68d8485a3732e08d232b2f28f2342e032036", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ad6a2ad55ab8a74da4f11b3b65d69ca2be4c6b203c1e605511cfb8db27330ffa7abbaffcba5836a0d6f3151ee8eb993c": "0xd46cd0ae6eeab8ef19bf289712c9f2ae4272c663bfe0786d7c10d4ada52100030233", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ad805490353f543cfbb0ed9476d0ee357ef6c750071ec4ea673adf3a02c82062d042aca83eab159f599434894946423d": "0x3e8faae4c5713c72aea65d52aa1616d3e918dee3819fbbe08cd4c76dbd754a50033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ad8d5ecff0cf8666df907974af5f0c9a866d1837675e6c261632fc872c66e779df05bca99fed82db1f5cc4c329beb520": "0x8b63cf648fa2a43a82671762a02da41dc6abbd1e62a376b3af7c2d09abf5120c09476f7373616d6572", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2add6d6b7da83a91122ecef9b7f1f8ed22c2a55b53b42cea8ef1a2b6f7f66895b8904a4764bcc5da3d23f941953076c48": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033536", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ae064ed9a95815109a28d1341b38f77f0e49baba2f2c359d846db58da8cb83c820aef3fbaa7b2164444d4946dc047e08": "0x4421050207b47ba1bddef66d1e1deee5b27d27d7fc526cfd68e4be18a5b9b146094b415252414e5a41", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ae25c26c4d55e92a8baa81c8ccca2202bc81d1b8a3c50fedf5323698e1fd52fecfcda7436b8ac0ac6f663e146f4d4f5c": "0x8e602e63afb364ac583747b0a8bab092d5b20ee98f0495ce7562a8c992ac9f171070617468726f636b6e6574776f726b", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2af2cb88bec0fd6d9d870ed0cb9e6b44dd62a2b80ebcda1b2f14d2a903088759ce56482401fb4130cde32775d6d210a6a": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f88370233", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2afa7012257eee9779458ee194e0b6a5144ab70adf9b1a6402cf14b3c61f98acf5bccabaf0030d537510166a21ee44d16": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f88370238", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b030bcfd4d04ca2771966077d88e8ca4824f5bcb1f267ea5a74e1a1c444c937eb29cbaf0fc98f293eab5275aeff5dc51": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d073033f09f9a80", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b035f15b888e0ad82dc423c5e7b66dbf6b05609a9079e0e03bcb50ab1676122a5fef12c48bab67b95d4d0ffe58ba7df7": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d14476f76204465706f73697420f09f8f9befb88f", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b06d49d722afc851487d64d0443da49c128865fced1fe2ad870d0f1ef6ac3c73c78012dbaf73ee9db06ba403cb73a523": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd070237", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b09e1ef1249e67d9b5b08826659bb27a442afde41c8d0cff9680849824712996d0cd96906dba9697aa5110cd6d025e15": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033134", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b0e45cea75dd9a6a2fb59626aa0d17fd7c73d0b870509f5c2ce7ef8313ac58becd9dea02162a270f9913116d690f0904": "0x20b456b7a8651f0b81f4517ffc79737cc392230cdd92a5b4bfa09ac728a0b10e033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b0f6b2e285b92b32889b0381bcdff93a2e2409b5ef509e1e584584edc945545f42fcbb3f288f3355e9194206b4ce773f": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f4382863638140231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b112d8e01e5b200440094fbd1752bdde946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f438286363814": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f438286363814033131", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b1bdb2a978b70c9c8ed058b2e17b652b2c2a55b5b86d232309cc2d8aedf4c647656fc6a40e2cdc299020174154a7fc42": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033230", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b1db55e6d201f24c25972d3b93600bfa2c2a55b5c4fa92b88356b2666fdbf58840b50813143d93c886f60a39b8eb204c": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033137", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b2022ec7aba6817b8cd6c3743dae52dbf3717672dfd677afa541355ced152c72acf081107fbecbc9e0acef5b54b51223": "0x0c618307d00b3354999ae280858c01fceddd77a25b5bc665fbd4634bf86a41780e4f70656e476f76207374617368", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b2e8ff0ead7eecbc8150d40440d9a34fccbf40cc53a67fedaff6111ffcf4d618af1ef8258560609082989fe66911233c": "0x6c42f37017f31d6c9ba5dca62626e6fb434d6edc31bfc9aa49f001a6ced27876037630", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b2ff9f39e4b4f2dbc2910bbe4598c2732c2a55b5f917f0ac6055619baeff392603206df4007063317d30f823537ef234": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033333", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b340047c4e17520d4d652bdd615fa3c8802bcb2d54cea58ca2d0af13a85daf28ccb873d31de2a277ca03bb185e41a25f": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033530", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b36890ca82312edfce5360970c393151f09bafa226f3e27f549d4fe85723533c0f0e54e366f3a87614ca08443b06cd54": "0x522e16fcd83f6d6e08fe2b44d56b5d0bdaf33527b49e8832928d003d8097ca630676616c2d31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b393c14ba1c4a4bf502f1689557b3313ca67e0639a4112b0f7cdc7a7cb9ca5d55b8fdb35862eaee46c8bd34720783d73": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033139", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b4010745c44b5b43598903b7096fe882c4896d44835fe17827b4af77172094f91a1b17ec3524949c2d626ce7314a440f": "0x1c6d8b40be9990c19e993d238e6e3613cfc6cbc51979d5fa61dd6ea2593856090d6e702d6e6f6d696e61746f72", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b401f35f10e20d172e577821098954092c888948530df2b5b3322b8f999dfbeec62bebb555351839993a10c2412c387e": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033437", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b47bc046571621dc5753fd0b932bb7ac74edbae75103b891bf37c39e91cc489a6f68f7b6756b32875d5c5e28c1730272": "0x984e16482c99cfad1436111e321a86d87d0fac203bf64538f888e45d793b54130232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b4a894d95a70cf19ef095db1a9f9f8f89e447cad47afa21f342083e9ef18cf04a27d81fbd0cd742e8ec36528c4514269": "0x1a0eb7fdecee073651f1a21b9779b7bf5670493a0e35a53ab83b3cabab814a77033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b4abbd8736dc65a25c47575bb9ec8e1c2c2a55b5eabf99708c2a9f1e21a3bed0fa589b18286225ceb1bc9e28ff06a04f": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033133", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b50f5b869e807abf7cb2f1ee7ef6845c2c2a55b5285b5050bfaf7c602b3748dfcb3306896ae8429c2c0ad8104c503401": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033435", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b5642c854a53817aa071ada8e9bd62a4a100d0eb4c4ad076d10849f54ddfc448b83596f24f71cc49ad4c0390ac489010": "0x22b7a9ef681c4a3c1743e5c343ff5b6f8aec3dc43bb0d49e29243d79ed40636504423031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b57102bf290cb640a422734d07fa643ef61b8042ce3e6f4c3731471c6a57e4a8ae30a1ffa5870db5043b933dde89af1b": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033436", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b5f83990f376bfc67e6b72cf9245fb42600ff061b1d97f28da91ad5b3eb7cc5dedc5431b2935a0440c02a6a82a3dd49b": "0xccb38aa8821510b7cc609cd55fd19c0f9c3594d49fe8db355bb2e7fb2324c948033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b61448b0855c8703eb3b665287ce3c226ca8fe9e98a7d7fb4269fe93c638a2e388c6085e74c18bc220c125fd7f0b1b68": "0x3c017930b46ab5a4413bf3153b001287ed5ff7fdbd2734cf69abc843f4ee04470ef09fa59753616c6164f09fa597", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b625f92500c01f436f0e71f0a9bbf2a3209f9f598ee545bbecc4993b7dff86f89d4b3a1dfffa7dae7e31f32c4b9d2c47": "0xc44cd5285e160a2184f3f5801cdce8517ebffdb591177acb17cff2db2ffd9371033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b6eb1d049af0ffae47c2af03b5b04ac9d6b0e114db8fce63ffc070a452ffa1f47f0f7ca51f32809a624323f51c146d2d": "0xface99d3401cb9b45ee1bc0ec52f4cb35914dc5ad27806230534230eedb8413d034444", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b6f2c4862edfcd63814ea4570e540b357091f937fba948654220a41ede536b0d62cc30d20274a28005b8026564db8d25": "0x0a4a3233cb4870d9f20b5c55e77f839ce96a26f0fd773d01917919757664476500", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b78425e582538dc4c288486af28286bef43e114e579d7503e86bc631c58415cd1cdf864d0fcf512bdd5cfae51aac062b": "0x2c6f57e9289919d242aa985c1963f2b4040ddc57df3682d890657d130c0355760c63686f7275736f6e652d31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b7d7f9b15ea68fc3a3bd7ac0e0bde394320145f6b95e9a687429cab758eea7c9cf2375c09ee12b7973e85c7f8476da7e": "0xa4848ac04c2ad298cfcbf27ade0ddebb2cc3b37b090dd933a1e988b7a3ead0750243", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b811ae831b53a9fcc4e215d67815d9ae1a58ec699c897903d28984e42202dc216e2e8f7023c846926179c0c38562e4e0": "0xb2692080bd814373a7c780dcc62922ba2e770c11a50bb1bb38fd3e69f0192a7114416e756269204469676974616c205374617368", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b85ced93d80542a7b97d4ea4e8ac7e90743a504cd053c4bb4b70c29cd59bf38cc92d1f51784969c0094d010525b54145": "0xd573535a40bb01903e616a383deed22b5e3ff30e552017d2395e3e75a8e786130232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b8ca7b51ee7f82178fcef96f9c679abd34e6b8b209b93f5f68b7abba7c3e17a84af77819e98b05a0d828f0b9b2b92578": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033131", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b8dc0e3687e1477644166ec179afebfa37982501b1c242d5d23a353d2b44e7ef342d32adc991306632ba6f3c61d4487d": "0x1c6d8b40be9990c19e993d238e6e3613cfc6cbc51979d5fa61dd6ea2593856090233", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b9ef0050bbb06da457001b37138b12922c2a55b5a116a4c88aff57e8f2b70ba72dda72dda4b78630e16ad0ca69006f18": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033036", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2b9fa89d1dd18608cf05ba581ebb5bf1a9819a1a845833b4fbfb1a911e2332fe3abb3e09acdff60b492680c6189629b2b": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033331", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2baf6acf18406a0ac71738890c9e1ad0dd4bb93b20f1a0f4d41cc2b066ec844343f3a6748e75b3c5a4018533d0c882675": "0x18a5d639662da95bb4924c48441fb432047e77613263cf1438ce8a14fc34c432033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2bb9a49de5d2039aafbeb82b8401e673020ac6c23e69518f5c048cdd4341f431d23f1bdcba3abfaf7349241db61ce1317": "0x5c5062779d44ea2ab0469e155b8cf3e004fce71b3b3d38263cd9fa9478f12f2804763033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2bc3a7a7e8db2a65f1ce6ffc95b13a5b662ed2bb3fe57066f0c015e18bf443b1d384119ae9a0eafd276064062e73a1b31": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033237", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2bc71f2cf237219176ab15a40a6712bbb2c2a55b6068e3b8626608321044a89b82fc4898ece34524659f48aa72aef556c": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033131", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2bd33bc5082da8415d4508b956b762f6b2ed9b8035e0bb64685f561677349c54d0ae8806f1ea86b74c12b51dc8154bc9c": "0xb64a86ea0e91047a3ffbbc68c36ea9ab318ee06c8bb4a354dd3a716956c9eb550231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2bda61a4f13fc3742a03a47f801b0279ab4dc28082fccb1f21f1728e29c178004bde0e64385194ac7600cc222b8bb9a20": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413064354524c32", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2bdfa0648392ee33f09ad376deec2286e2c2a55b5c7e135ae443c6ed951338d4b32be6cf61ac5bd000b7a11c26b307820": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033038", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2be55a78a142cae9a56448e35fb0f3180760f9c3a5299a87661dd13e267572ef052d2c2383fa8f77f45c25567a6e27f75": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d0d3032f09f9a80202d20314b56", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2beaf3d87df59f54760ad106c3f97757a40e1ae3e3d1e6fc5dd9c3a0bdb1c0d03772a3c69ce5b4efd1d8653ece57cf539": "0xa4a7835edb8c6b0d10b32c76ddd9560feb4894fcfb61593accd8c7451e96562e05706f6f6c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2bf1b1a4220f2551ed3b4c82cbcb2cb6d38a295559d8977464fd8cdd133f8805f2388e42a6e009219247048a27d9ac06b": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504636335", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2bf8b91717fc4b3b889245634525a2b2498bb3be2e039d66d581ffe74cb449689ac74428512b67e033855de5d75b84d08": "0xaa72c321b20bbd5b78b14f6fd800017bca47190956eb42ada2a4d8f8a8ca994d0b4d6174746572686f726e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2bfe1e70f1c70d00b88827788b71b101774065a60fa33d4c2c8926d0d761d133a305f500880fa95ad86acd865f274414c": "0xfa5e6f955d973efaca30897c4a3e4fbec88186ae72b8b331408804d73dfc275e037032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c015e16e71fed001efbbd731b1a14ffd70119b7481293f5e919dd9f0c078a5b285f9ab78fd05fe73b9b4dedd595b5c38": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033238", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c0acadddf3aeb1876f2c7c88daf7ca952c2a55b5629e69cf9c08b4b926e7e8503cf4d337fc62677f40409a68fe9b9a1a": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033531", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c0e57bca92853c3bbd9179917407577d9208af2fb9f1511facbd517ba7ec0296d6fc9fd010896eec0f43a415b68b3706": "0xc290e73cd4a89b696210d9f712410463c89368b6fc4d22ce10207d8f31e5e73900", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c0e772fb88e288ce6c003ea0435930d6ca0c59a8141101a8f9c99e3f8a85c77b0ccb57ca6121cf9edc436092e9bbd17c": "0x628f36dddf8cdb0242104a2531e7d3efd4860a9a4633be69aaf30f63ccb25a5e09696e204461766f73", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c15da76d7063a20d2105a731e85b952b55ce3d06cfa21735e2f8b652b7886a9a91a53c2e6ddf1bae4ee86ef2209fd0bd": "0x8122315b758fe65626b7d0f7c8f5d0758b235f22df56cbf73610916a88520efc0646554e4453", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c2abee6fe4293d17c3395bb0ae324cb8ca38730919998d8ddcf5e729a8a275769b1ae064d1e0f5528db03eede05d5e24": "0x5c5062779d44ea2ab0469e155b8cf3e004fce71b3b3d38263cd9fa9478f12f2804763034", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c3440f1ad4617fff4320dbe3d17d539988072cc906cf4513caf8aeb6ae323c8b7e57bf5581da1aaaa8d1363dac664266": "0x561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d640d56616c696461746f72203032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c394f9702c31720b76d50596bf1a0b3e8b7602f67d9d61682964f3b5989f357752f75dfba430603da1384059b79f1535": "0xa23809a947f2c06542cbf2fafe17fd7d84c460d51fdd69d01f53a5ca050ee7090231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c3abad1b49578e95c1aebddc2b5893dea82456d51e83aa6dbf7a473b5776454fa175d5dc9775629aa05bb09b28fe4509": "0x5ac4bbe840e332da2656c0e760904003fa7a2123a216a33e81b8531400600304033034", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c3cf4e2a0ff55f643892e2a52a20693e0c789dbc85d3a4a498e13f720f71110577461e834d356ca361d49a45ef609b6b": "0xf40cd7a2181289e32776625b9f3a193f749e33ce1bdeb76cfaabece606c7324c1968656c69787374726565742e666f756e646174696f6e2f32", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c40abcd97ec4dd18da9ecd6c8d6af226a37a1021f6eab9a2658f2f6a5e08f5851de80230d270662dfe648c0c7bec2e46": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d073031f09f9a80", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c41de75982653ee203e85ae8b3491a626cf627b193bf62d66c08afc7b08095f2c12178597925fac26265bf801079ab22": "0xcea3dabe52b2a665b1e19bf8c6913a5d54e06d6413ca3ddbec8f9a22415ec4770248", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c4cbbc94ab26be6a5bdb9daee5cd70725ecc1d4e60a92262c1bec62d034c979f42cbd3fb1c28570d5baed6e5ed20d533": "0xa471c55caca4be7b4e60c6e94b20f9028883f8c64287d4454130c657383c3442035632", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c51e4cb74629a513ed40913f5fd048062c2a55b5e0241fe92a75c23c49cc15b355a4f52c934719b1b7382c5c2b859a46": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033734", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c58ed9577b53cfdd09c2a60325ae4555cc2dde4403d477d784abb0486ce18908af209f2c9d8581d33f7e847608b5d124": "0x5c5062779d44ea2ab0469e155b8cf3e004fce71b3b3d38263cd9fa9478f12f2804763037", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c5e24722f48f114eb319513abecd93415010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f": "0x5010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f0241", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c666dd6bd1e6196f18935a711ca0128c484cdc76e0b6b2cb4e30850327cf37e717d91e343a62bbfaded38aa8133cfe34": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f4382863638140232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c673d8e3bc8bb8b1aaec7e4f1da7b00004704197ea07678709bffe4d7cc4a0203e5f92da2f681cc5e5ebd399b83b3949": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033335", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c6972e264d3a6b5fea949ee6deb8b99a388bf0fc0110c1b18dcab471725083bc6b0b52edabeab0c3e73e506a54f9e04d": "0x5a33766207d51925e4b9a2302870bf737305cddd5bb2df2dffa379963e586771044f4e45", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c6d5accb29b732a3b28e7e07c00e51f404db71328c96f1654885de2d1f577621c524e4515416b0f861dfce55a3f72167": "0x545b7958451cd22afb0367d6c99af1360190813571c47b8a94a055d575d38249033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c6e6930f38fc96ca1527a23b891a6d69982aa00fdf3835f109ab98a569a0476af2e87c92bbf3cb6c399254fd9b31c900": "0x4056f76b206c306712a75e10ece4cffe091b1a49b0ba4c505f2ae4ee673c576a0e5a4b56616c696461746f722034", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c7a14b30fea636224fddd038c66ceb362c2a55b56f24b35dda6da7aef24974dd6501d9c857f8d41d66cf88d5bd053044": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033635", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c7a412119faf128c3bcb5450745613970ca0db0283dbf8d123602a2ec334ab5c3fd9e2540577e0955eaec679cefa4f0a": "0x6c695c4e546c6889ca591b582eee8b49ba68d8485a3732e08d232b2f28f2342e032035", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c833d839589168d31a513ba57eac40f9aee72821ca00e62304e4f0d858122a65b87c8df4f0eae224ae064b951d39f610": "0x6c695c4e546c6889ca591b582eee8b49ba68d8485a3732e08d232b2f28f2342e032032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c861e9da3cf1b67318847bb703c39631d84ea2d7257eeb646fb4e0d2aeee35a3eeaea22b897d8ec95f42d5ffd2251430": "0xb8e06dc2e6bb6bd269319ace4cf8f663338f8f285a0564d6a139063c985d23100232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c89deaedb1f96fd0140b01c7396300216202474a3d821e3d2df95b4eb0f8405f1a086cbbbd25c4425bc978ce8da1b3ba": "0x22e8d22a7fe2768c944dad8af7829d96eac8644f06643ea8ae68bc3c1e9053060231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c8cc9ad6de4a02498a86b05233f8b7642c2a55b5c69b5e131fb0f65ac7ca707f4bc53e4d991a2d1971ab5e702f69f45c": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033037", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c8dac5d09880fcf7669cd1f88ee32646005fa73637062be3fbfb972174a5bc85a2f6cc0350cb84aa9d657422796bfdf1": "0x264319ed6a0895c04112917fc9bdc0771f4a4773aae014a99d25bbe06fa1057a0243", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2c91aeb4ffae7eeb153ce2284e565a46d2c2a55b4f853af67dd7217983b8928934bedd57f2aa6717b4570ff9cf8c55d33": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033834", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ca2bb1fdef2defe515e9aba1f30778f91e7aedb71a237b95e3b805a77c031dac6a8a675441390e66bc1ddec7a41a1e54": "0x1c6d8b40be9990c19e993d238e6e3613cfc6cbc51979d5fa61dd6ea259385609086e702d726f6f74", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ca49b8ecb7983e1ccd3083c5f08607620c7fa2b79d310ce32237db73b1e3d581a979b3bbd7f19ec44ff1d37b87dd3750": "0x3c82ab06b794c99f14a161973be7aa6012568b1c491d45ec969ed7420bcfaa5907424f554e5459", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2cb1b50885d1cb48212e41c3140ac8ae370519f42b7684d1ef3dce95d87895e447b390bc1089606d1a499c18d08bc511f": "0x08754abb6afba51a2f74f0b97bbcdb383f579a02a5e4541fee736710af562c6c05f09f8c8d", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2cb689f2fd08e34f3e90238d841086e91d8151ecb8e8d11e6c6bd81ae49216b9ef92d2b83b3ae39702a397922f1477768": "0x7a895955042cb3fe863f3564e7b30e9130e37b6d905be11c0cf91064de1cd83a064672617a7a", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2cb7824c3c4a36969f8b1065499761b832e6991a3223f09ddea0e0b1fffcffbed3f768d40231db602a030b7fba52c7d08": "0x465c6be30d314cf647a8fa10212bcb84318394659c46fe75d03640cebb39595e19416c7a796d6f6c6f676973742076616c696461746f722030", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2cb9b5eb59ddf49669ec0c9df7987f371aa41228830918cc1cf16e50df86ba154a483d77ebe3182bacfb876af4fe9ff6b": "0x3c82ab06b794c99f14a161973be7aa6012568b1c491d45ec969ed7420bcfaa590e434f554e43494c2d50524f5859", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2cc71e745770be3a4327ac3e6e61c637a3e184eb35acff823bcf5199a3eec9d0a6aadcad642fe08451772a51c4215583e": "0xd45548e42d7f5d5aa35ffc16ff29396c99936f0c1eae84d452f1daac87c3c566074576656e7473", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2cc80e8459e5fce01f1c58b4e2a8953ce0af17b8dab92e7ab018e1189cf597b4e2ca38e0d00716172adb26073867ab92d": "0x561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d640d56616c696461746f72203034", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ccf2e9d8958e5b865afe2e0cbf30ab902c2a55b5a3e5f8bb0fd6155ce85668d8e606d7defec7044d8615225bb9769a6e": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033430", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ccf825de56b61fddcde1bcf452e38a1fa2fafeae641e6e264d77723c00ab05f503db48ca3597cb3242c2b54d90abd01d": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd070230", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2cd2a7f639619333a601b9e944ddbd08568f26829ec470fdad2e63d97741a88df1243ea147bfb4639b97a4c816f9605fd": "0xd016feaa68e25739dbc4035faf2577d17ec9d7ec2125a99a6770b5be093f6e3204445331", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ce122712cd338ac4433aaed5fe788634f415f3ade9353f49fffd2f3f2ebab33ab5f1b94390ad5758fccf5a00e9441e01": "0xf0de782e8bad3c663be60812f0a2ac63464f5da3ec448c73334c07d71ef27f2c0a627269646765687562", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ce59cabc41ff067a260befa5b4d22c06d97d35c31f6d0b75bfa93ec1eec8823651d1582368679c44f00ded1f5401b5e7": "0x7a895955042cb3fe863f3564e7b30e9130e37b6d905be11c0cf91064de1cd83a033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ce9c51078fd9ce38eba0e7f6d7324d0fe0855069a59fa0ddf72205213ba6d7bc3bcfc44316af9684bb215f815fc0113f": "0x922bc16cff1acfc4a08cb5bfcebadaa9eb182cd47a51b8b047a0202ae9624a1c05f09fa6be", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2cea748c3b2ba59842163a012dd3fa089b3988a6af22247249717bd8602b7ac3e0fc63f469ae9ff8e9d022aa504d61b5e": "0xa4a7835edb8c6b0d10b32c76ddd9560feb4894fcfb61593accd8c7451e96562e033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2cec0021475b40ca2d74cb08a8f4168e0282a194090fd6715e06430d8a6e9c682f021eaf398830b10db94ca8c27c9ae4c": "0x5c5062779d44ea2ab0469e155b8cf3e004fce71b3b3d38263cd9fa9478f12f2804763031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2cedc7b9f5e185052276ff40cead4c032149ccce9d526a65ba54fccc24f5d1dee62f9b87915d4004eb932959d468a9e62": "0x18d54ea25ad26acd7094fba6bdb278e769f9ac350cc36b2f631da13fa92bed79124c6974656e74727920636f6c6c61746f72", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2cfae23c09122f09f79bb0e3306b2b74836fd9b64e99689363368298680cb35750a594103048bffd839af770fe5536c6c": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f4382863638140235", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2cfbf707024d3f7f987d6231d10e0e1a23e9520e4e74fd47da1dd509ef04dc9b14215a35e7d207fdb2c99f732ff5cac16": "0x36aff2ede1563784631d6149024982108f661b079b6b79f3d042041a9da11e2a094c6974656e747279", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d03c012a3925866fc83b45604288b687114f710e9094d174ee537a86a7eb9ab2004e6405626a843d4ffea72694b9e23e": "0xb690df84dc61bb1b04bb3e5483678390a44eca9ee4a748e98f526e126c19c7020258", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d08ab4f04d24a26306b6e8849841da8792e594db8b26bc07fd45cc783db49313f17183491f1826d417155a95d5f7d85c": "0x986a4fdcd53d0e438f9694c9a6bb76665bc50acf76180049117caf3ca9bbcc6a05504f4f4c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d0e7cf7abed89ce05a639e6d088a035544ebd2b934606a30469bfd509293c34174336f974709964a18bf40d99a0bed0d": "0xbc267fea33668e3515a7c01f4acca67d73d30574b600a404d2b7210aaac855690de29d84e29d84e29d84efb88f", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d0f86a770b0362a67e0b80f83a93995472284f32719a49037a79da881b91b44bf642395ecba92b241619e21fb1c8a57a": "0xf65f3cade8f68e8f34c6266b0d37e58a754059ca96816e964f98e17c7950507304626f74", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d13aaa4ce8a3a68e5b852c21463efb531a9490a6fa5ca6353651e0384ff371db3aaa0d2fd20e6ba91c8733a17f581602": "0x5010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f0247", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d16685a34b78f937300cd3a3c3bfe0639f958e6a1f4ff8a986a4e7d76c417bcfe8affda4182e66b9952dee757d7e577d": "0xa6c5f0595d6ed85d6260bc682d4a68a4b4e605d43c67d56f2765d196985497720b436f6e74726f6c6c6572", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d1b99fe0be3362b9f171c5d2737212fc2c2a55b54dba6c93fda08f30f8fd6be0e47bf66f3fd31e2e1d72b970b3ff1154": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033631", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d33371148f6e156ce9e4018a98d91a4208a73c9c3083971fa2ec757d38c8e7858df8076aca40ce176faef81fcbfbe258": "0x984e16482c99cfad1436111e321a86d87d0fac203bf64538f888e45d793b541305f09f9396", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d3a35414cfcf6a6cd5be058a4734f75974fd218ac454949ceee42deee0ed129de80fad49f579870d7ee9d4a2d1e0e27f": "0x7269f7eab2e5f91f9efe10d0dba0e7256d9433230a8f9fdc1c4af10981853476067374756479", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d4064446373efe5e2d35657a95221e10a06c4e59af8d86d8b552887762255c830d79b847a6648210ca6b24d0dbba0e2d": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763038", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d41bc04620b2161b34eb34e6a47af25a3a337becdfacffca71fe67fec208733754f035958d349d36b30225fd47798f6a": "0xf857311106c8d7b0daf6e096db9a0d759b52403e439ab23fd6559780a8b1c803064249534f4e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d421cfb48fad0754e91335eb0722ee11d05490aa747179f2b895c2c5171e9cb10a474fd07d1a8069389678e165369e56": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c41304554b31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d43a2b5bacb2a5a913735d81b9183d6a730800477a5fb0302fa9ff4b5623662a27719a304dedb3e12eb004f48ecf6b29": "0xa4d813e676d81d97479f9f15572d9eadcaf4503b654c0f6e7baeb49e84510e690231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d4eeb7647278f476f14736144be90ffdb6612cc74066b95fb08eb5978781b15169170cce9f5493f6d8f74013ee4e6e4c": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033533", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d5912e8857d3fd846b9b7afb3d401568d091cf86d04141b1c17c70826c08d074cae1b00d6f82de1b8a5406ea10ce723b": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c095452454153555259", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d5c42076011c041f810f01bb5ecedede01f8b3c479e6400cdaaba7b54357ef912ee1ce5b803db86506805054070ea176": "0x1c6d8b40be9990c19e993d238e6e3613cfc6cbc51979d5fa61dd6ea2593856090235", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d6227a681a80f70d5b679ffc134fc506c0532217caad133c16b24a5e3b2da932b83aae63356a787e1594e71317df7cf0": "0xac133ebfde441672c055b79ca9a6059850984eead1ae036f48ca1230e7f0556f05504f4f4c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d71c5e81f0ab3d464dc034191d86d5f5908626870725d87736e1476482cc7df7bf32f03f83ed8cb6db40a830067d973d": "0x08754abb6afba51a2f74f0b97bbcdb383f579a02a5e4541fee736710af562c6c05f09fa6bf", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d8558f24642f1ec38c8f05a6a65118884793061e5121c9561a8dd25c2e64df912f5939a096bde10bf9c5cd06f753f6a7": "0x463a9cd3e7cec50ccc93515557ab58221d20c67a409583428ad67caff9415107065374617368", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d8ad31739c149623bd78d39f54c5f51b2c2a55bd3b0028726bf7764e3a604421e98e930f851a2bfafdf5d29eb694d275": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033937", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d8c5bfd4bc0ef3b8e44f737ad8be140f78c7dc6b311bc7e44dc95793f0b8e1316ab1447073728470fe08e0b4a8dd243b": "0x264319ed6a0895c04112917fc9bdc0771f4a4773aae014a99d25bbe06fa1057a0242", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d96660089dd600c4539c167feb8a18a42c2a55b615f9b64ac6a84fc4513c431a7b6e0bd3812a4821392a462d049aaa3a": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033334", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d989677538850769066111c948cb30682c2a55b4fa84e9b7e85c6a3bb37f4bb3831bb51a5bbb1666ff92c8b874dcb42e": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033633", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d9c47225dc4b565f343d7e58210a682c224963ab92b30f5a7c91256507aa6375320e32514e54eb988386ccd3654fdc84": "0xfe1f35d3b712d9b87e9dce926d8772267bbbaeb776e9b2a8615b29d6594c4621033031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2d9c62ffc145fb75512af8ff50ac058ee48e77b8c0864277405a333a644bb283a03ade9c351473f7c167b309ba4cda308": "0xb204051d55c2c80cdf6e0d3346944e921fc97f0b86a4cc5eb5547982fc475d68033032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2dadb90787d37d7ac651f6e2941b2d550981d5e90031eac279276782d0d15ab97fb30898a69abc4eefaa797c572823b2c": "0xb4d78a8bb35a7b0ff8ae6a7808f9b17c83efd28b8612c2388034d0e35ce513760232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2db0e6c637e12549ff30cdb5c7a3b2532be6c22b41a47d782268a2d1eecf5e623ae6b984591db92f77de07a27a447f87c": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763133", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2db818395f4d30882473aa682f1d36e9b5613fa8d949d17f4bd3ecfd6b7cb550072f4559c4c3f01250379945e44cb3235": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033434", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2dbb2c86ebf000e28b914ca30a0c394591600e09e1d8a1324934f83d55d5f6f503e2d91bf4270eeaefd462f24e4487e29": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2dbd0604a8ca46a0f5076f8140d75ac7d4a1585474299b9deb690132a6d586e8bedfe4d8e75ec70c2dcbd2dba24269215": "0x040d1324a05bb9a0b86d9eabd01245ffcf277f4611f9750dfe465761481ba053033038", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2dbfa7057505189b4af689b9ae4d384128a4c3a7b3f2b3cb525ce315f859de83c51410fc29ac88734be346709d0798a20": "0x3e3a490c516b2a3582e6400e33f3eec42a12589958cbb86c87b23bc94710d21b05706f6f6c", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2dca1cd7196a14a851b5a275585eb732bc203687a31c85ac0dce32725c354a515d8c027681db49edbbda4e3804e5dec63": "0x1a21174f3333c2bb416072b2c31d8f1e60f8e4ad3cf9546ab47e5b6fd060d3030e6b757a6f207374617368203031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2dcdb7e8983f1dc27a7169e03dc11b6dfa2b3963c9d349de4363ce38f5f9854fccc0636768d947108b7f219c240837855": "0xa02f7333e25590e4f568ae8a2ddc93a879e92e48fa3cf1666ac56e020c106d550a627269646765687562", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2dcf24a1420a437a2315ff698af59a553685f836cdb8f2749ca5a225297e1a8207fed4511b575448e09bec5a870480c6b": "0xd46cd0ae6eeab8ef19bf289712c9f2ae4272c663bfe0786d7c10d4ada52100030230", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2dd287caf54b991ee49d16dfccfc25d2c10a767cf483ca0629c6cb5dbfd20f5f9d8468b153b90bf3c40443bfba9354a14": "0xf0de782e8bad3c663be60812f0a2ac63464f5da3ec448c73334c07d71ef27f2c0233", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ddc46103873fabdcbfd43afdd5fbdc87724e0032275bac5598878e5dee08149d11c44700c9c4626d1f339ff1be715f30": "0x6c695c4e546c6889ca591b582eee8b49ba68d8485a3732e08d232b2f28f2342e032038", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2dde3f4916d4134ee96557d32c2798be38e6c97add8c5563d923d9b702f854b15ae59ecad39578845a04239c594046044": "0x64e6db572dcc9e5dc57445ff7c68ce85e42527aa74b2ed5d1e2bceefd308ed05033230", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2de1810815b4181d78b34a31b9a5f85e4ba3e9b87792bcfcc237fa8181185b8883c77f3e24f45e4a92ab31d07a4703520": "0x3c82ab06b794c99f14a161973be7aa6012568b1c491d45ec969ed7420bcfaa5910544543482d46454c4c4f5753484950", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2de2150bff9da75d3f86fe07f2279f5192c2a55b52656178628dda04be1e16aab15775638cfd3bac25a59164631a06b1b": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033532", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2de4b76f552b266b54b14e30d6052fc61e0512205b8cbb851e7b9d862d204b1df2d7598e03041c7e70b373ac45b1e6cfc": "0x64e6db572dcc9e5dc57445ff7c68ce85e42527aa74b2ed5d1e2bceefd308ed05033130", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2dfc40f350cc4563ad9ddff5ad92944278eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0xa02f7333e25590e4f568ae8a2ddc93a879e92e48fa3cf1666ac56e020c106d550773797374656d", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e053e352d49371178913f8a7c80be39b6a4fe76ff9e27c148bf2c24e2f85fe56b4eea5bbcdc159430c18036e0f940f27": "0x5010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f0246", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e0894c7505c4ccf600b4e887725ebd7b8c23324b0cb29e4fd1a68cb08febe58b50e39d8afdb5f752d6c26c8ba52fc002": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033132", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e12b82edfe2c5df8bc459adc5dde162f44e9eb2333d2ba5032cb999fc5815802139a4f0b1abbdc6fc9669fa0d3e9ae55": "0xf65f3cade8f68e8f34c6266b0d37e58a754059ca96816e964f98e17c7950507304686f74", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e20a5666049eb6eb1d793e56dbbd540994c8f2aa65dff23f542ee99d7191e88eecf45b3a61b7350dc3d48804107e393d": "0xf0de782e8bad3c663be60812f0a2ac63464f5da3ec448c73334c07d71ef27f2c0232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e243d9597e1ff02bcc83e65fe97ebecf9970b2d4d682818fe75bf20d33d350798f984ae009b8e8e127c1847461357ec8": "0x5002926543d8b8887044442834d4fd5fe3d6eb257719daefd3f2aed28eeaee690232", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e287427a545f07fa7bf625be98536e33b6f0f10eec993f3e6806eb6cc4d2f13d5f5a90a17b855a7bf9847a87e07ee322": "0xdad0bc6a0aadf06c56416e83bf75e865d41ccb5ffd74eabf9e81d47574b430491143554c54555245444f54204d2e532e58", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e2cb581353e109d4244df722361162fd828ad82b9738a507bf481d6c3feb4e50855378b588a653e3033f5eab788c335b": "0xac133ebfde441672c055b79ca9a6059850984eead1ae036f48ca1230e7f0556f04537973", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e3178c61a0f52ecfd1d1b69fbc8d1582c9108836cc5003906b107d31fe5c7b5d1211a4f71bee14f068d2fa04551387b2": "0x06c7ea7684b6aac6cd63cf88c92b0f05398bc3e13e0b0c5936c3027b8e0c7e2f00", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e3c3bdfcaf035a202a03474869a5028cce79383a5d77f974ba22bada735faa2075716e442079b7d58ff1fd898b63251d": "0x7600c0c74304812e4928a503408a68c782c69a6af2fca55fcd9e48e095b448630653462d3034", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e44a61acf949dc5e6246ad785d8ef21cb054f58645b2774a05e70ab4f9b59dfa46ed60acd0668713e921324a933e2027": "0x5ac4bbe840e332da2656c0e760904003fa7a2123a216a33e81b8531400600304033035", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e4598a627b033d5dfec8bc9280097cc1f6b2256178f0210557ddcc138e8b264204e4674fb2d73a8190faf4ac3724722f": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd070236", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e4b0112f6842e05fbfddd3e76ff53f58d6ce187276961ed56ae9a29566279cf443df42f82d9890b02de9c11e4d288e13": "0xa0db8c6cb723c474639931cae07e095e6e8d9b870c90f5b499bd8e6fdf4bd54f064368726973", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e4c4990573c19a77cfc77c7f474604153ac9ec7da0888dc010574b11d9d0dfd62446ca38a63819937aa91f40f6901d58": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c0df09f9099204f43544f505553", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e4eda3996223cfeedc9689417aa91cd35607fa03519626bba53d0d71b008694a205e566c653b766c9f1c60edee39ec22": "0xa4848ac04c2ad298cfcbf27ade0ddebb2cc3b37b090dd933a1e988b7a3ead075035331", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e4f5cdd468e167da392ba6e10b53ee3e6a0da15516a63ecc95cccffecec2a2aed962e92cab661af4b1f76b65e429cf79": "0x5c5062779d44ea2ab0469e155b8cf3e004fce71b3b3d38263cd9fa9478f12f2804763036", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e5992f79ed312340ce56189dc4f562739653bcf18e30531092fdc1c52afe06cf61f56fb1fa5d719078cd6914d395ed0f": "0x3640b7b7fbbecf967f99ec9516a74f9e255efa5c8529751a383afccfe936175e042f3234", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e59be3727eef8dea84201471915ad11a80833b98deaa7ae201c9d2aabba6d7cbdc14ecfe1548c52b8ac4907acb14c863": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033133", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e6c96e874922674a2cff73001f49441ade1894014026720b9918b1b21b488af8a0d4f15953621233830946ec0b4d7b75": "0x3eabb0f32cb0695fedb8d25f6299fe51f90b1e26ed858c1064aeeea0977c1e670243", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e7379ab3911997c3aa7bdf8347caa100e44b76584cf228a713f627f36b58ebb282be3a162e6f24d54a15d1082006a208": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033334", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e78c9f13a47a426fd81f851d5e4a4e328a32f59713f0a129fbc395dbc853f51ab53d45d1684c4bc8ddad89fd55fc096f": "0x5c5062779d44ea2ab0469e155b8cf3e004fce71b3b3d38263cd9fa9478f12f2804763035", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e7ca06f4e64071a61cda312f78f7a8548823139e30d401f7a9422e68208ff3ad2f8e40e92c83af26e4002734760cb87e": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033337", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e8071600e92b47d24031ea725c59eca4e07628deaa9c6fbbf2288f879396ff3566871c0dbce85c9e23764d15b810657f": "0x9a8ead39ce1b44f37d16e98496441be79018e910d5f58c0fa1518d8fd7749550165354414b494e4720464143494c4954494553202332", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e8360b2b0337af76720b8a8c4bf432a0e23e7ebb9b8462643b8df28f53dc2dbdebb20bd0cf8fcb41d7d09db59d46a505": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd07033133", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e87b7c876df80669b1ea78e67a83b8bdd6d1d0ca917751d6148063b9e1fe8a22f74396e63a2f72e149b39e44a0e77c7f": "0x321d847f8a53927fe1132754a7708c61049b2d4816ab6ce7195a8308e97c3d410ff09f8d80415448454e53f09f8d80", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e8b6c9ec21445bfdd558ec3ed91799202c2a55b51baa94ff2dc4feae28c5ecccb774c622545b0a127dbd5ae8e2c8e115": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e8ceffcd95ae97cf0d36a8aa052be87d782f3f9bd84fa8b7b659bae702b90ee402c45067cbad3d97c89ada7db6e0fa67": "0xb204051d55c2c80cdf6e0d3346944e921fc97f0b86a4cc5eb5547982fc475d680e434f4c4c454354495645533031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e929174dc9c4928bf7afa39c5375dfa326a944c20d0609837ab990ebb516c30f230f1d29be7d63f7c368c9a499be4f22": "0x466a9934facf9723f7ecc237cf0afaca23a6e832780616e432f3eaf19d8ff94904303031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e963eb3a31c188236411e5403a65a1f6bac6ca95205b7d29a9f95adf28d5f94ba010ebb51f414163a48747436c9f9cab": "0xa2656a3bbbea71626facc2641a9d9f744c87c393778020354714974894b7b27a0231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2e9c8e89fbc1f9352f2bb567add0aefa4add1d40ef104fcb24be637eaeb9704064663df235fea0799829e270333774f3b": "0x74b0c404b20bac28ded8d662e2eae7ddc52988cd5a22d5de09259258395dc45d04313031", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ea718457dfeae31df87972c5e1528d84e081dbce8eb0905bfe321ca87d5a426435e7cf499020c644e9fbcba9a0345fa7": "0xf476394d38a2202be9ac84607ce7fa0a7a53981920999c231210fda7fca3604104505631", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2eaaaf45051dff93df16e63c768d773fe11d3e37f3547a962068f9d46c046759e8a866b64c844b0fe7426d95fc1b599c2": "0xd016feaa68e25739dbc4035faf2577d17ec9d7ec2125a99a6770b5be093f6e3204445332", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2eb2b549672b19b67afbd9e3fc4b568ebd00de863a4e1cc8c2b2050ba4be784b313caa1e51167abacf392c1f07270834e": "0x5ac4bbe840e332da2656c0e760904003fa7a2123a216a33e81b8531400600304033033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ec0d25e0e8b30cf77df449e358e3b18556c031a86027cfcb114d40812f06d9f462a8354171478b00dd075d9271db3872": "0x6e53696350731ed439f8c353b0b69b40b324fcdcb435ba225fbc22a33c9fe15407444f542f3034", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ec5037705c8d82366c308213ec87dd35fab574220e94c5e2a4f8fcfc9f0e7192116b24eb5df2632396d94dd43401a252": "0x7600c0c74304812e4928a503408a68c782c69a6af2fca55fcd9e48e095b448630653462d3033", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ec8413880bb125c95625f5a40e86558852a63150e02f15706d9ccceabf476a79dcfae5bba86e66ece74aa031da5bce36": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd07033134", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2edb14628d7e37cbb87c519385e4e16a9a4dab34de26e570e8fb301f9c947c6f5f862dfbb27c9de5cf473675cc244213a": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f4382863638140239", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ee9be1d4255e938947c92e123cf2d82f2c2a55b508174009ea498f934dfefc1a023e9d03b43bc601a2f7fa0844126160": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033630", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2eefdafbe6888bb66d3584aebc2c916a4f5c0dc71bff8cbdce6c32ae5260549ecce90af19512e7da031f2dd02fd920689": "0xfc8163c92cac3dd1d5c2c0a049c3652a8b3b8b6cb8a0867a5e494c650b4de3710231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ef9c24010a7d87b2d6696a19e87491d2944519212916c8838efd27a63122a176c83e9e0dc68ad20c29a1257166d7ec17": "0x561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d640d56616c696461746f72203037", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2efacfb219480b68325adacca873693d62c2a55b55a05a6a02b7b3ba6539f3744bed7a3ebf4a86a75986e404553bf0635": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033832", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f078cef6bb5ade7a68f37d2cf9e834bb34fa4e10f24341696e46d61b982f279969608b00edd5d32298f3cab0afbb695a": "0x2c81fc1faa024d3d1a727d953187860b45bcb185d046631ab98261eb6f2b8d5416f09f90b620536f6e206f6620612042697463682d31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f0c0dc3560195888d3828d9767ed964be02374bdfa92d9969c3c6f8c313bed2e8165040d3ef604001ba1d9c838e9dc5d": "0xa849437f5f8b602fc9a4210d6a9834af4adc6ce7492861bd0f5b88d11919cd7b0656414c2d31", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f183a365116212f639e2d31af122c16e92070ebf24c4c84a47db97b62d308834c3f258a9d96aafd6bd11eca52bd6ce4b": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763134", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f18978cb1bed20a6759302de04ffe38d0e2d58494df50bbe553b779921de400483c550c57a9c7b5e7ef9735958228e34": "0x6464f15335eee136dd7c216b994ea6ac3394eb723590e9e065fd9061e05d003504486f74", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f20562184074892fd5273476a9079b73907559ce75b9e6531397c0050be2cdf9d982a2db041a099526baba91b8d25409": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033336", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f3759ae1aad2e4accd167f7617254a8d2ac73c24bb740376a5b0f44814e8ce8b34f23be0650e99b7ac81e1159f9c3151": "0x1c6d8b40be9990c19e993d238e6e3613cfc6cbc51979d5fa61dd6ea2593856090231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f3a2ea4f7e87660b8de6205e32768eed14307eae2db6e460778426604acdb1215d1a38ae54aaa0ee1664646776557d05": "0x3c017930b46ab5a4413bf3153b001287ed5ff7fdbd2734cf69abc843f4ee04470df09f8c8a504f4f4cf09f8c8a", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f3fc4d95dda4fef3c64f3e37dcfca37c60971394debc5f36af7ce9cc93a3f5cfdc400c830b8a8510d105de4364e7cb0f": "0x86f68361d0a346a62be267558e72dfb9e3b5a04adcc2c9e46fb7b9482f7c876f07636172626f6e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f4e3472ccbcc90b51cd9bc48554ec8297c103bb33a2f4f6ff86f2f6e11e9488d6eeaebe52bbf31291fa00e46ec26ad60": "0xb8e06dc2e6bb6bd269319ace4cf8f663338f8f285a0564d6a139063c985d23100233", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f623b20438f6af5d88c978d9cb8470307677c2a4deb2689377319a8e830d5d9ce0ae32b95f096a7447f8f711b7f0333f": "0x946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f4382863638140238", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f69833f1ae9dbbb00dae666f139773905009e192ec169788c9c1f0202fe7c2bc79405ff8b6e1d1ac78fd6152006e606d": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c08e29b93434f5245", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f6a8ba389384cd6e3068fb6cf37e87dd18fab09febf634c47cc08a08a95988723b43f5ab499c4925d08793f3f701df5a": "0x82ecbf12e0965d08387b8aca42993392dbe5ddd0de48393b96075c641f2c940804443054", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f754bdf453d4e84560fe79ab039139fec4e4ff6adcb360ec9eb50d5e04ad47aec66a30055222dc13c6215b5f2db59767": "0x6c695c4e546c6889ca591b582eee8b49ba68d8485a3732e08d232b2f28f2342e032037", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f7c3b18f75237ef0c0b19cefd274264582fdd15d55ddeeda8100a274f8872503fda6e267e345f4edd9fad26229604c0e": "0x796b851c8164a129b23282ca4b3bb694364b0bfb504e98b5d2ffc5140d58078e0c636f6c6c65637469766573", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f80f919e92527ff0b3f38a865198b4c5e05c542ad7369509f498cdea5f5c427f2fd0b3051884184b294d9ac10fe64e34": "0xcc95d7d061fc6a655b795794b9b1614d0a240e13e81de9dce3f2b184f653da7c0231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f8c2f2927526f62e9c9ec02461c15d20dc0293434648638559e1a4cf30e829f17d2695980d5a3374af8d663bd5214905": "0x86f68361d0a346a62be267558e72dfb9e3b5a04adcc2c9e46fb7b9482f7c876f09487964726f67656e", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f8daa14742583f2f5c0f9fe03ff22b9d9c6a3401d06cef30fdbb33901328f3611dae8253708779a5d66179c967582635": "0x96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837033137", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2f8fcbd529f3814b1082ca36f9bc8e31f4108a93c4704dcaa30f7b5f245de48d736164bd554743cd4bcfdf1ced6f852ab": "0xb22f88d05ca6fdb70235b599b99c69f927ed71163c2ebc11c9649d678a8ba84e0c76616c696461746f722d33", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2fb35f965a2e1b5ae1f4f332ab4843aface3268d415f136dab575d25dc0b1932b736c7cc785a1ccc04f0474b458115f0c": "0x666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd07033132", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2fbe7c8aa7eb18460ce203ce4c80180417a813a8832981068f9b921dded7aafe83cf89eba4c91ac5460c65d76a61d465b": "0x6295f0214abb5522cf3becf82504ef7c87036236e6b9afd263697a99bc5d5a301631efb88fe283a331efb88fe283a336efb88fe283a3", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2fc0e0ce33806f3824459bc37c3bd7122f2497cdc525e2b482ddec24761aeb31826701c5a6e35919787bfcb455c62d774": "0xb2e07be4d6d82f546ec91d6009ee215bb736be5b4362e66e7b466ec72d47624f0c534158454d424552472032", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2fc3ef9f2768893bd98dc2460ab9d9ae1a0209c10c7d6633de64e166a68d9b45e7f02aacd9497f5c552e577abcdb17d28": "0xfaf5f68fe828f5af8d69c116efd937d90f1956b0edd94e05d8b5285a8eb2a6630a76616c696461746f72", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2fc742294b2823241347b13728377c9e288ac52f4800ed8569280dd140faae0b0ed5613aa77e57a592cb9af8127dfae2a": "0xfaff1c4b5a94649e0338783122b93a469eb9e2e375bb71c92028a8fe9b6e2f460d56616c696461746f72202331", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2fcdf79da273ebe1bc5b7ff0707cbe95c34bc6fb5ba6e2150087c96fd4852ec188aba74a5a383a22ef66b12c588cea00d": "0xe04ca25cf1cbc516ed1c138492a7f2e606f60c5a9ac96cb405eaa3914fd129730231", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2fd2862f10100ae5b231586b302448ef6bf7105cdc60a8125bc7d465c5587c26d9954572b8579bd176052bfccd2847506": "0xf58927b296a23cbf25794b7cbb8fe31c5e68f2bcd2c2483e9c9cd0712216f2320547545332", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2fd525643583f149f8e68c3d89c7cc0782c2a55b5f323b09d3dc6c092ed0e988463188613232235e6d6cdd03085885529": "0x2c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e033936", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2fe257e46e780cc22f4c52b7d3917a0788c2bae6068de838d1fc684db669d5ad1dfe26f37887ce815734145764b7e7124": "0xae0d1db9082bdce75480ee80c3bf3c6496de1ef8171951de2edfe49fdbe30a6705f09f8ca0", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2fe7a2f19451f39e782aa158587f5104091e1331ab2af4b510ca29f5e5dcc8e71d1fc6cc70ab24e2cf8513d540fa388ee": "0x2c81fc1faa024d3d1a727d953187860b45bcb185d046631ab98261eb6f2b8d5414f09f98ba2050757373792047616c6f72652d33", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2fe873b3279a034ae64f9fda42b000854a81b8a2a03afd92af18f0338e43afc504c6018aca6d9197c0d3a149ce65efa0e": "0x0a4a3233cb4870d9f20b5c55e77f839ce96a26f0fd773d01917919757664476500", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2feac0f58276d8e619cf093b7ecccc1efb05cb292c73a8d41d50181bbd829f14afa9464b6b710cf241ad1b801b0b1e654": "0xb22f88d05ca6fdb70235b599b99c69f927ed71163c2ebc11c9649d678a8ba84e0d48616e77656e204368656e67", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2feb19c835d73b7b4e7772d1ceb85a24d88d74924b788c1f7ec64a54c63eccaddca748f588f67c26e5595870acecd9259": "0x36da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb38934504763130", + "0x2aeddc77fe58c98d50bd37f1b90840f943a953ac082e08b6527ce262dbd4abf2ff9f5c222ec1d9624cb2b58612a8f347588ab6bb059d351e9de8b2e3daa0f7dca462a8ed9ebd49ae10c3f753fe2dc84a": "0x86f68361d0a346a62be267558e72dfb9e3b5a04adcc2c9e46fb7b9482f7c876f0a626572796c6c69756d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e00123d83b036c82d5c5062779d44ea2ab0469e155b8cf3e004fce71b3b3d38263cd9fa9478f12f28": "0x0000000000000000000000000000000028282a194090fd6715e06430d8a6e9c682f021eaf398830b10db94ca8c27c9ae4c9effc7fb904ca3d8cc4f45b464ee4dac705c89888d298bc9dfd2ba563d5b3e3f20ac6c23e69518f5c048cdd4341f431d23f1bdcba3abfaf7349241db61ce1317ca38730919998d8ddcf5e729a8a275769b1ae064d1e0f5528db03eede05d5e248e9b0b6b26839418f4baad18a9c3ffbfa413d65cde8010bf7b9b9db0dd23e0058a32f59713f0a129fbc395dbc853f51ab53d45d1684c4bc8ddad89fd55fc096f6a0da15516a63ecc95cccffecec2a2aed962e92cab661af4b1f76b65e429cf79cc2dde4403d477d784abb0486ce18908af209f2c9d8581d33f7e847608b5d124bc1729a527ac8770c18456f142dc57b24069c9ff1032d6c3a1572d84b811ac7e780946289b4befff0da9911c013aacd2b280ec0529a759b94c67e4899b9b7058", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e015b6bef29d45538bae335c017512a43fcaed23efec97d80e088bb8f7b93ee837cba5416ca51037f": "0x000000000000000000000000000000000404926296ae6c9155557a6c5aac98d9775664efd8607e894ef210fa2c80b65941", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0173de88215548e9f65f3cade8f68e8f34c6266b0d37e58a754059ca96816e964f98e17c79505073": "0x000000000000000000000000000000000c72284f32719a49037a79da881b91b44bf642395ecba92b241619e21fb1c8a57a6c9e3102dd2c24274667d416e07570ebce6f20ab80ee3fc9917bf4a7568b8fd244e9eb2333d2ba5032cb999fc5815802139a4f0b1abbdc6fc9669fa0d3e9ae55", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0264d5f165d70fd8604189f1ea74bc439b18060c58f352db2880dd4c835df7ebb26e020bf11e7969": "0x000000000000000000000000000000000442d1b7759bba592a85972e3c6a9bbb419de5df76c57324d98c2d11810bbf4f13", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e02c2da93a5b6fce008754abb6afba51a2f74f0b97bbcdb383f579a02a5e4541fee736710af562c6c": "0x000000000000000000000000000000000cdc559c88e35aa258566bd616d0e31fac0efda3d881b52055a31b35892086bb1c908626870725d87736e1476482cc7df7bf32f03f83ed8cb6db40a830067d973d70519f42b7684d1ef3dce95d87895e447b390bc1089606d1a499c18d08bc511f", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e03456c855e9a00037e878e54c1374b30df335d3a193f3e6b1f84db6c2270ee634cec769d7e33e244": "0x000000000000000000000000000000000496cd6f382ca54348f9bc846929ae16bd49da6ef76b19211da8afc0a1f1c31d32", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e04322a3587052539a23809a947f2c06542cbf2fafe17fd7d84c460d51fdd69d01f53a5ca050ee709": "0x00000000000000000000000000000000048b7602f67d9d61682964f3b5989f357752f75dfba430603da1384059b79f1535", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e04d5e25d603189573898b6f62b50749101446132f77fa6dd77a3895674fa8dac87e6c375ea852346": "0x00000000000000000000000000000000043f24c61f66f5c798cc97adf5258664937f3c16d0b35121a8b45cc81611bc8e59", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e05d8bd6067f6ae29c804531378242158a794bd06c1e9ef0e569f60bb32758597af80e5e70c07a64f": "0x000000000000000000000000000000000804587d42f37709c7342f3e915b5a1c6f64ca916b9fa8279480c8602c600889bda225b0e7cf9cf454c3ecf0d3477cd774c612795c312dcba6d09beea92aaf2a6c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e05e989f2a5137a8f22e8d22a7fe2768c944dad8af7829d96eac8644f06643ea8ae68bc3c1e905306": "0x00000000000000000000000000000000046202474a3d821e3d2df95b4eb0f8405f1a086cbbbd25c4425bc978ce8da1b3ba", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e078c7addc0fece8f18a5d639662da95bb4924c48441fb432047e77613263cf1438ce8a14fc34c432": "0x0000000000000000000000000000000004d4bb93b20f1a0f4d41cc2b066ec844343f3a6748e75b3c5a4018533d0c882675", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0823dc59ec2f7a204eed7cf3f4f6560d58db4eb78bd24b655bbd1d7a5c6b454e77c8dc5e2721a54d": "0x0000000000000000000000000000000010587b19cc62e01cdc18a1499cda27b0fb33264d0c3817668609bb58f7620126987d80b43f7b596676b4a6714cc5034708a9d651e55c030c2a0b04d8152a8437b5a6e11253e1600d4a54c1c233df15bc0a8b600e01f85ab764233da2dda657d4397c2496b1630a27a7946e2ac806c8e0cbc7d12702175153cd0eddff10898e9036", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e08db713c2bbf4320a4731404eb64407b76d75dd815a3267e2dce24d8c2054f3d45d83ea11c8d707a": "0x000000000000000000000000000000000432f5040e4ff22a9cea43f7cfd1242e3ae0e57140c2f4b985e83ffde5e5c4492c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e098de700b0bd1b5ca2656a3bbbea71626facc2641a9d9f744c87c393778020354714974894b7b27a": "0x0000000000000000000000000000000004bac6ca95205b7d29a9f95adf28d5f94ba010ebb51f414163a48747436c9f9cab", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0a6e3cf2b2ed68b7f476394d38a2202be9ac84607ce7fa0a7a53981920999c231210fda7fca36041": "0x0000000000000000000000000000000004e081dbce8eb0905bfe321ca87d5a426435e7cf499020c644e9fbcba9a0345fa7", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0be961ab1f92f2a95002926543d8b8887044442834d4fd5fe3d6eb257719daefd3f2aed28eeaee69": "0x00000000000000000000000000000000087e74e295c040927de4200c770994a314185d3ee447be3d70c79ed056fdd1ac539970b2d4d682818fe75bf20d33d350798f984ae009b8e8e127c1847461357ec8", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0c7c6da4442b90353296c9b3a6546d2319a764e00e1126215b776cb27571db2ef0392bbfbc66d45f": "0x0000000000000000000000000000000008fea1c72d488cbe5955b1eb68d746792321b1eb06616bab281f5d4838e0421c6ad4171a4d884070585e1bc36f234a9db406e6a89575ee9e5ad24605e146dc4c28", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0d9954f2f20d04e36495827bfe0b07d16c549eb10d7e45997e95788be44a3f277af6befec99fe62f": "0x00000000000000000000000000000000049af6c56d6840c84e110de42cd72b4f82d0387238d2302a937026c23940d01241", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0e032212504d7709ba57da1251d785b2d433ca687c510a82c284826b9b79859b5774a84f7000e928": "0x00000000000000000000000000000000040d92a2524d501daccb88d86720079731f9b53038e0aeddefb8828afbafa5eacf", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0e6e902c97ee9286b42bde3f29708150bd47382f10fa4eba1c27a068653cfc4e3787b7fe05fe6e7d": "0x0000000000000000000000000000000004ca44000a7a16d0e5c56d22a707e7cefdbb237b9d20de2c46a53b551ec0cf1c40", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0e7a2d3832f0ab5118c044557335d26c3a538ec7a2699ef5665cca1d17755cd6ae53d41f5bf31623": "0x000000000000000000000000000000000418c0445577a970766dfbbd43589ffdbfb1bae33e8f286969539300c6a49d0962", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0e9a3c3e586a908f8122315b758fe65626b7d0f7c8f5d0758b235f22df56cbf73610916a88520efc": "0x000000000000000000000000000000000455ce3d06cfa21735e2f8b652b7886a9a91a53c2e6ddf1bae4ee86ef2209fd0bd", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0eb83f2c7db87e68e043d8f7872cd895f8957c9179c4264816be3e649713cb3bdc523f752602cc3a": "0x0000000000000000000000000000000004a082ef6765a3eef5cce291b2507c5ac3d6ffe5e10ecec2525d1554b8d2db1440", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0f16205ef17fe25d2eeb1a6884bf369c7d1ab3f9ff75b79dd0f6c6a9792834e026a8d2f7ef049e4d": "0x0000000000000000000000000000000004acb22f070631059ce62f6e0536e561bb5d470d2bd236985b0dcf42cc3e446c68", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e0fd49b70acd3290232d4d1b0dcef676d9a72f6abf9aec55e129ef2de135ac172e19c28a9adbcad0b": "0x00000000000000000000000000000000043562230e5122411d1c436c426567ebbe517120ac76620baef8d5d78b8a2db938", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e10a8fd5f177733e23640b7b7fbbecf967f99ec9516a74f9e255efa5c8529751a383afccfe936175e": "0x00000000000000000000000000000000049653bcf18e30531092fdc1c52afe06cf61f56fb1fa5d719078cd6914d395ed0f", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e11ce0c03039f6a489e4e7009937c56d267338762a60ed004293afd40e7c2081847c12cb63c76a818": "0x00000000000000000000000000000000048ad3ba81e44bd11349a8d48bb168d583decd0257d3237df7d55bba5051f5254a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e11ec48c6e9cf67fb4cf2e774e34c3603b2428a690c058d2ab826b39a2eba4e22b3aae85f9bfa7802": "0x000000000000000000000000000000000446779e4754580b4e94ac22e5da499aa39cf1aece35d5162db15003b14110f31b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e143c9b2402368042140be3ffc8865dd47a8d044916b26936a579433599bebca9d3ba1d6eb7722710": "0x00000000000000000000000000000000040a016b7d735f7a0c5e7987f99b8e46137b6668a6593c064d42d050979935793b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e148cd021e35e23e4f0673d30606ee26672707e4fd2bc8b58d3becb7aba2d5f60add64abb5fea4710": "0x000000000000000000000000000000000444d9acd86b036eae410384c8fa0d073d47e74712a5787451463fbd717770ec57", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e16ed9307fff94f1f0cc0424184702e27c49fb859c93f07d8bb0adf2a0824ada9f01db9bf76b7025f": "0x00000000000000000000000000000000083619289cb2e660dbc1c5747506a37df7a4d311aac8f29c69be8b710495e5162346b00fc11146ea6ce12405e83ec552c9f3d66dcf81ac4fd874e24db1484f4041", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e18b47e3024d66dfe628f36dddf8cdb0242104a2531e7d3efd4860a9a4633be69aaf30f63ccb25a5e": "0x0000000000000000000000000000000004ca0c59a8141101a8f9c99e3f8a85c77b0ccb57ca6121cf9edc436092e9bbd17c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e19627e3b0a1fe44f466a9934facf9723f7ecc237cf0afaca23a6e832780616e432f3eaf19d8ff949": "0x000000000000000000000000000000000426a944c20d0609837ab990ebb516c30f230f1d29be7d63f7c368c9a499be4f22", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e198703ff0791c2de1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x000000000000000000000000000000001858ac509e6e93bcf6ac0800a070f28bd477fb9e9717ff7779d035094e361b1504d05490aa747179f2b895c2c5171e9cb10a474fd07d1a8069389678e165369e5692a8511f5619229d4e1bef8344cd9a5f85468e238fd95f843dd555b204d0f00b9a9c5706a3b70b507dcf6b013c69ef08af652b1997d6fe82d5ba6975a2581b21b4dc28082fccb1f21f1728e29c178004bde0e64385194ac7600cc222b8bb9a209a7d620473c31a9f77379b12b25920a83b43e0f5700737d9e370dbdd9738c84f", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e1a0b756d13ed752dc09033af1dd99c3727bd222c35d9d34e8e4403441792399b08df0d60544fbd48": "0x000000000000000000000000000000000c8c038403fe48ee0068a652cfe2593d30d5701f508e38ef676f392fdc85f8065894ebb855993bf0568b71fbf4197fdee4cb44a39bd46fa5969bc5c372ae101367c676e6031d0f7fb3ead234afe813ecec2d6e3b47fcee702981feb2c168e2c37e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e1a61ab35fd1e09085e34b19d7230f1ea5da6d4b8fe6ede9d05c2e55b0189f75b967862c1c43d9a1f": "0x000000000000000000000000000000000882b224471e6d4cbe8d9d084d445bac45c91e88679cd3c22937d5e56da60e2bb3ae60938e514a0fc95200df7940bd0fb0f983090e91043b76f3186c6300ed7437", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e1c6684bc5e5975f29a8ead39ce1b44f37d16e98496441be79018e910d5f58c0fa1518d8fd7749550": "0x0000000000000000000000000000000008c4826569e68b7eee1b5b93406e4951fcd7ab6b40be519a7db5c6732f66da1149e07628deaa9c6fbbf2288f879396ff3566871c0dbce85c9e23764d15b810657f", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e1e681645b60ebbd6a2b06daf782fa5142e365d1fea0f4a418687f53f201f185bf314c37581677b37": "0x000000000000000000000000000000000806cf58e932ba9179e8493d36dd0c02918e37649bfff2bcc24d4fc19d47492564447e00d54ea8610f6d515b7d7964b1aed5838c2971e585143807084c2a799311", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e1fda62d62fc07febbc1deacfc7e5c6e5f0373560c14fbce156ff2a0ed7e208d049ccd985dec85545": "0x000000000000000000000000000000000ceea674f13649b25e552a59b65dd203fd45055ae7f39b5a6551cafc9d4eab3776dce2db9e809d506f5bb1523baa031c25f48d3448eee538a23766c2686a7ffe2cc08d5de7a5d97bea2c7ddf516d0635bddc43f326ae2f80e2595b49d4a08c4619", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e23484ecafdeafb8d3c84767978ada6355ac3719ea8f978244b16d7e8c00552c3a189760b55eeb404": "0x00000000000000000000000000000000088c5917b51ac796f4dbb53a35008c1e0cc5a8e3aaa1300cc2e845f8e7702cd4e5149fd573a4ad8eb5dd80a1b70e89362e72f0fb2512075b4d9a52e7d23c0c776f", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e2398aeb1ef43f542c44cd5285e160a2184f3f5801cdce8517ebffdb591177acb17cff2db2ffd9371": "0x0000000000000000000000000000000004209f9f598ee545bbecc4993b7dff86f89d4b3a1dfffa7dae7e31f32c4b9d2c47", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e2492dee6806c086baef6619eea08f01aef7e47d460f0730b02c075e446308892e55af56af1572168": "0x0000000000000000000000000000000004c97b296323e5b62a72504892a3ccd4fc0b1b206f4de9ba0e9764d9ed9ebdc276", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e24df16db48272ad68c085e6bde4ac25702f54f46fa3c1b0a6170bd346103c2c6339911e00306a054": "0x0000000000000000000000000000000004a9695441f301ba78bd00ce015f2f1da14eb8914531fa38695502369de72c256f", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e25b20afe8011ef7d040d1324a05bb9a0b86d9eabd01245ffcf277f4611f9750dfe465761481ba053": "0x00000000000000000000000000000000044a1585474299b9deb690132a6d586e8bedfe4d8e75ec70c2dcbd2dba24269215", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e25ecccdf058700718b63cf648fa2a43a82671762a02da41dc6abbd1e62a376b3af7c2d09abf5120c": "0x0000000000000000000000000000000004866d1837675e6c261632fc872c66e779df05bca99fed82db1f5cc4c329beb520", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e26968cb24b23a08fb64a86ea0e91047a3ffbbc68c36ea9ab318ee06c8bb4a354dd3a716956c9eb55": "0x00000000000000000000000000000000042ed9b8035e0bb64685f561677349c54d0ae8806f1ea86b74c12b51dc8154bc9c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e28047368e108b3b06ef40ac7b3e092d0eca77ad072cd317683ab9d24aca4025788421d4276527d57": "0x000000000000000000000000000000000413be73ce92b712d00931d4980713bf4be8974255e1e51a7ed71ed2ea37f035dd", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e28db6c1e91cbc43002385caf9a08b92ca458a0b817a8cc303cefd5a0c6e108cb939e04242b9e007d": "0x00000000000000000000000000000000042cce76137e2e2d9bb63e081d4074cfa4688d7d9781d2888f36634f646da62561", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e2a679b6bc1ea7234ccb38aa8821510b7cc609cd55fd19c0f9c3594d49fe8db355bb2e7fb2324c948": "0x0000000000000000000000000000000004600ff061b1d97f28da91ad5b3eb7cc5dedc5431b2935a0440c02a6a82a3dd49b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e2b031c075ea343ce443c76dcde19df9387486ded7845c6d85ec2a4c17f38f8b1e7a0a14de7968d7d": "0x0000000000000000000000000000000004727dba627f34c210eba395fc7f60d28b3a58c5dbd6b63fb4f9788ee764b2702a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e304630d144e59358fe1f35d3b712d9b87e9dce926d8772267bbbaeb776e9b2a8615b29d6594c4621": "0x0000000000000000000000000000000004224963ab92b30f5a7c91256507aa6375320e32514e54eb988386ccd3654fdc84", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e31053eabded94fa87600c0c74304812e4928a503408a68c782c69a6af2fca55fcd9e48e095b44863": "0x0000000000000000000000000000000014221125c5934d75259f335bfdce9d6aab9e4c6b57724993f32f555ffc4e75d070fab574220e94c5e2a4f8fcfc9f0e7192116b24eb5df2632396d94dd43401a252ce79383a5d77f974ba22bada735faa2075716e442079b7d58ff1fd898b63251d7600c0e901b4341203f21f410f3da01ae4a45d194fd2c0693c6e0ec5980f4c21fab573fbe3296563205563eb39965933d33ea5a591c45af07f0eee2272ef6723", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e31ef141127f82186d45548e42d7f5d5aa35ffc16ff29396c99936f0c1eae84d452f1daac87c3c566": "0x00000000000000000000000000000000083e184eb35acff823bcf5199a3eec9d0a6aadcad642fe08451772a51c4215583e7061c3799bd2b1d70aa6c8014a0012ea494f4455d60e182dd1cc393d37f8604f", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e32ac541eb6aa1242ac1aee6b509bdaa6aeb4718c2218bd7e78a3d6704bf886375e4c243b77a66b34": "0x00000000000000000000000000000000046e28009eb2b8b7785246c452948c27169c4e3ed258ad7a707fb64beb9442fcfa", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e361d0f16c34b17763817e559bdeb521169c4801d70a1dfefe94175d15666a1ae70719a3594a57c16": "0x000000000000000000000000000000000458a0a27aedec30fb5c0fa2dfabff0b7370f69e2a8505d714f7b2ce37a99f0d07", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e363df5d8dc0e8fe77042bfc6e75e1277fdefbea917f6b7ca8c36b3e0ad7286d66974c8f2b4cb9672": "0x0000000000000000000000000000000004bf10d082fee91854f7a6a6c56ebf8bf0d65bf914a683ce4ae1db5ccece06ad27", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e37cd5a422db43856985be267f05f586da7ab52e4430c2f6d461f396b9d6cf4f1eb5362066f7cd82f": "0x0000000000000000000000000000000004b7972e09702baf7eda528c6894408b1e9c702e5f54674bcf6de1b55b3aa16365", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e38533548d44cc8d84056f76b206c306712a75e10ece4cffe091b1a49b0ba4c505f2ae4ee673c576a": "0x0000000000000000000000000000000014a8e87388e083b3f1a9dfeef27977d883cf10e7c94acdf0c60f57f0a9621d4539bee56ca36a0a5393bf9bfbe5d2079e31d4359d35388df257e23793c7b195b8555e348817abb98cb962fc0780a47ebd471d9c318395fa80b4529a64cfabb2e32c982aa00fdf3835f109ab98a569a0476af2e87c92bbf3cb6c399254fd9b31c9007c8c3a92d8feb9d27f32f3ec67bcc6792f8496f7ed86d1b249c54205a39ee30c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e3b8fa2e75a1b9c5bcea3dabe52b2a665b1e19bf8c6913a5d54e06d6413ca3ddbec8f9a22415ec477": "0x00000000000000000000000000000000046cf627b193bf62d66c08afc7b08095f2c12178597925fac26265bf801079ab22", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e3c096cf3f561e3834026c99cba12d64d1d0e7a7a620bf54374603fcc056e1601e3713f9921671120": "0x000000000000000000000000000000000447500d616a78d59bd887bcde41d61f4ec880d0496fb4d9482f6f637f55cc1b29", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e3c790f30d60af3f382ecbf12e0965d08387b8aca42993392dbe5ddd0de48393b96075c641f2c9408": "0x000000000000000000000000000000000418fab09febf634c47cc08a08a95988723b43f5ab499c4925d08793f3f701df5a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e3e843a37d45e41d726171b3ad75723bf624a27485e51e4c2fe38f4b2d24ee52b86a979fb772c513b": "0x000000000000000000000000000000000420430a70d2db1bf57c424e5e81de47ade7f1f0ceae2b568b9182ecf9b025aa35", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e3e9ff1da508f2a3c7ccb1907030863dd708764cc88a4b4d09dc5bb0f4c9ef5f4e73cfc0aa4bcdf3c": "0x000000000000000000000000000000000465819df33c22d72346bb95afd79e55414f35acbf0997c6bbaeaddd1d3688506a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e3edfdd48fc779cd5a80ea94af8a39eb7ba8d9afb913147e67eae84f48aad7b9ce6ee05094fe0394e": "0x0000000000000000000000000000000004f2e78c673a855ce341b8d431b3e2ce293e812236e2e42682047796fe599bb734", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e42f5d87995df46aba849437f5f8b602fc9a4210d6a9834af4adc6ce7492861bd0f5b88d11919cd7b": "0x0000000000000000000000000000000004e02374bdfa92d9969c3c6f8c313bed2e8165040d3ef604001ba1d9c838e9dc5d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e4316433e1f50b4c3a66e0f4e1a121cc83fddf3096e8ec8c9e9c85989f276e39e951fb0e4a5398763": "0x0000000000000000000000000000000004ccdce2bc61518838d34314c620b7c88040c38c784e0eabe838c17192be7ed91c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e44646f06f20d51883c017930b46ab5a4413bf3153b001287ed5ff7fdbd2734cf69abc843f4ee0447": "0x000000000000000000000000000000000c14860e374c2789388379dc1cdfeceac093c9cee7a2ae8579736323c589c59e4214307eae2db6e460778426604acdb1215d1a38ae54aaa0ee1664646776557d056ca8fe9e98a7d7fb4269fe93c638a2e388c6085e74c18bc220c125fd7f0b1b68", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e45d43cb8401e3564f40cd7a2181289e32776625b9f3a193f749e33ce1bdeb76cfaabece606c7324c": "0x00000000000000000000000000000000040c789dbc85d3a4a498e13f720f71110577461e834d356ca361d49a45ef609b6b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e4692967bd2332c08b22f88d05ca6fdb70235b599b99c69f927ed71163c2ebc11c9649d678a8ba84e": "0x0000000000000000000000000000000018b05cb292c73a8d41d50181bbd829f14afa9464b6b710cf241ad1b801b0b1e654fc80cf0973d64eb59f1fb930f3107e9f8ced4aa69907717312f0c5015706f4582acc6987e6d1c5087d414d72dd8ce665458dfd16d7447ee4b0336ee869e4d0f84108a93c4704dcaa30f7b5f245de48d736164bd554743cd4bcfdf1ced6f852ab1a356596f667e9330b60d055872640198b86485ac1721c37fdfd468157a17a457e726af71a51eabcd429888bbd0e46ce1e63b3322d0683994ca825dad3d3716b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e469f989f6ca51c58f3b15e49aede08aa29d7400da8d3b4fdbd284b5bb1f1af1a53a8c47398f1f093": "0x00000000000000000000000000000000086cb7f28f67bdf8862d6ca0ca68932257d665d966222ff24f800291451121676db4fc817716823ab3322f35702e26df4fcff8f577aea26e05bce9bef8b51b6707", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e46eb4c71c2ad15c3f85bd4ec9a558cde3f05e33b0b74f9e732cc41f904894873247d4c435a3b8b63": "0x0000000000000000000000000000000004e272df04c4d1eef779341d14da26699e3832ed5a73ceda96a206ef1c4569f477", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e46f59e6d908bc0ec8488bea263878e7e16be0a8c4705f9729a5f25462bff7dc7f2d8346370c8ef65": "0x0000000000000000000000000000000004d8b5b26b1cee228c6ece5a4b44db258985abbc9f0168950d744269bee80b1852", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e4825a3675ceeb1c2561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d64": "0x000000000000000000000000000000002c3091c08dc07b367c41b57585aeda74490c1850166bcfac4081ed66117f6683611234713d080856dba6865b23424e21b5fb50108669bc5762f955d207eadde13caca4f84ed6959bdee967c0bc4d289bf3fad8671f5ce7068072a7dc34964a8d0c80d57608c732427386079d29d65035cfc02b3221ff32cfe73b540d849aa0046288072cc906cf4513caf8aeb6ae323c8b7e57bf5581da1aaaa8d1363dac664266f0cab0c194935f449b3baef742e992ebb801fe38ab5c345d7a6195740399cb500af17b8dab92e7ab018e1189cf597b4e2ca38e0d00716172adb26073867ab92d783770544556ce47aae6e9e9c6cc32bb84a3df7ab11a5d30b0618e00e109d66cfa5be617ad31f9c9a41041cac3ad8ace2c550c28c73afc8e34367d64b50a6679944519212916c8838efd27a63122a176c83e9e0dc68ad20c29a1257166d7ec176eb3bf6204b55f63a2ef87543b26d158230eebb149a8b9df86555fe3f02ecb6c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e48b1dce0478b5f30daf47069cecf5c31da9a1cfde3732d2b701bf9f94c77ba7e99335d6b30ad080a": "0x0000000000000000000000000000000004daf47069cecf5c31da9a1cfde3732d2b701bf9f94c77ba7e99335d6b30ad080a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e48c106532e2fbcf5ec05f950e080aa04f8ef335280637c8c80fa6b8bf54d5a3bbfa746b9f9da5860": "0x000000000000000000000000000000000466ced89b9a76de4d3dc384b45fdf0bd2f1629f15dd3e44ea14c31ba16181a255", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e490130ac826416f286f68361d0a346a62be267558e72dfb9e3b5a04adcc2c9e46fb7b9482f7c876f": "0x0000000000000000000000000000000018dc0293434648638559e1a4cf30e829f17d2695980d5a3374af8d663bd521490524f8b3dbcb13ea214b670cb611fe7939e20a23db19647485e01206502e64ef7b6a66d0c75a897e62aa4e9cdead9f50760db6e7beae858c1bc3dcb1d1ce601e58588ab6bb059d351e9de8b2e3daa0f7dca462a8ed9ebd49ae10c3f753fe2dc84a7a33d68fb22e4ae32721ff41285cc493d664753a7d71234c77dc2eefc5782c0b60971394debc5f36af7ce9cc93a3f5cfdc400c830b8a8510d105de4364e7cb0f", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e498085158358a41ed824263a2ba0b39e43b2a1fb591c68743ca504c2ce6c3c2aa96e58b6269d3115": "0x0000000000000000000000000000000004d19f8df3cdb194db075979039a6fd4d912298356bb2931c08c35db4903f53345", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e49aa36a5cacb5456d4539a7dceeeba9999b6387e9431e83c53f4b884edf5cf049623110eae570125": "0x0000000000000000000000000000000004081c5466574f932ef5e1469e984d5d39ad5946468f0ab9d06c454f74cfc2f16c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e4b11703c47136f3474b0c404b20bac28ded8d662e2eae7ddc52988cd5a22d5de09259258395dc45d": "0x0000000000000000000000000000000004add1d40ef104fcb24be637eaeb9704064663df235fea0799829e270333774f3b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e4dabfd58e995e46fa4a7835edb8c6b0d10b32c76ddd9560feb4894fcfb61593accd8c7451e96562e": "0x0000000000000000000000000000000008b3988a6af22247249717bd8602b7ac3e0fc63f469ae9ff8e9d022aa504d61b5e40e1ae3e3d1e6fc5dd9c3a0bdb1c0d03772a3c69ce5b4efd1d8653ece57cf539", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e4e5736534181ad78b690df84dc61bb1b04bb3e5483678390a44eca9ee4a748e98f526e126c19c702": "0x0000000000000000000000000000000004114f710e9094d174ee537a86a7eb9ab2004e6405626a843d4ffea72694b9e23e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e4ed12f7f95496d053674aa73951219dbd27b3e3fc5847b806c68c1de38fd4f22f9493a461c80e903": "0x0000000000000000000000000000000004b2a9dcb35e27b71b75bdabf1cc706c567e50bbb20631e4f94d5ef6aad740fc6d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e5010d8218a994822c290e73cd4a89b696210d9f712410463c89368b6fc4d22ce10207d8f31e5e739": "0x00000000000000000000000000000000049208af2fb9f1511facbd517ba7ec0296d6fc9fd010896eec0f43a415b68b3706", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e505d0ff0b2ffbc1700004bcdcc7d30d597e19c7f87652b69736066b729f7e8543231a10760f8157a": "0x000000000000000000000000000000000409e125c99b3b07749c78742584a990e78457e6424814870b165823547d3d6e29", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e512ee0fca2d67ab7d573535a40bb01903e616a383deed22b5e3ff30e552017d2395e3e75a8e78613": "0x0000000000000000000000000000000004743a504cd053c4bb4b70c29cd59bf38cc92d1f51784969c0094d010525b54145", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e54aeea67769129fb946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f438286363814": "0x00000000000000000000000000000000344cd38181e02e880a53114ccaf987a6f51a10bc1d172d509bab6f7e9d6eb2e00b2e2409b5ef509e1e584584edc945545f42fcbb3f288f3355e9194206b4ce773f484cdc76e0b6b2cb4e30850327cf37e717d91e343a62bbfaded38aa8133cfe348a64dca67c64d9361901a1415bfb3469b000d0bf7f1d439824cec71f87022159f8720c905d7ac1acab25c4f353df9eb759e0141e4732540d163ac444260f017736fd9b64e99689363368298680cb35750a594103048bffd839af770fe5536c6c5ed6f4b68a60a117a32059e96678f22fa086c505f7a8ece13c7a2e78b4788f156af358e5650b61943ba709efc5dbc501405e04d5de5798087d6c727027511a657677c2a4deb2689377319a8e830d5d9ce0ae32b95f096a7447f8f711b7f0333fa4dab34de26e570e8fb301f9c947c6f5f862dfbb27c9de5cf473675cc244213afe9d714ec9ee0f74e33282dd9e411fd0f47c1aa17553392642df99d1440df951946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f438286363814b0adcdefee88f852a85ce03afe5d4403875fcbabc350bcd5d1b94c7ce68eec02", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e55ebf22a3378f22de4bd04a7052f76425c60648c528535285bc2a23ab28db060db34f7c5e5746aa9": "0x0000000000000000000000000000000004d68d71f02028d8c8d8a76dcf0eeba7f93ad6b2a6e2f8363ccdad5fa580705211", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e56305c2c3bf8f20e465c6be30d314cf647a8fa10212bcb84318394659c46fe75d03640cebb39595e": "0x00000000000000000000000000000000082e6991a3223f09ddea0e0b1fffcffbed3f768d40231db602a030b7fba52c7d082a211b13ae9c29f805070d21d5e5e007db8ab2566e1031b6ec22733f1f3c0877", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x0000000000000000000000000000000018a37a1021f6eab9a2658f2f6a5e08f5851de80230d270662dfe648c0c7bec2e463c92bc22c4934341504e8b6f0755bb76906171bcf8a55c49a78e2055ddd28802824f5bcb1f267ea5a74e1a1c444c937eb29cbaf0fc98f293eab5275aeff5dc516b05609a9079e0e03bcb50ab1676122a5fef12c48bab67b95d4d0ffe58ba7df7ed959fe3d9afc671f1b0cde07de829b2120d7da25d5f84f4e0a86e3bb940bcc1760f9c3a5299a87661dd13e267572ef052d2c2383fa8f77f45c25567a6e27f75", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e5749edf76d87f942544e2e588c90a2e53e051d2f87d40e222e1f034913a30f95a9a2f39114e5be38": "0x00000000000000000000000000000000040277ce02b2ac78ceeb9ae4fa0a595005489bf3f5f77898415e32a3e9504a5314", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e5a54b00d6ab21bbd58a74372b1f0a88b1df304584326ff69a68e9470d42687e12bf5dfac375d4911": "0x0000000000000000000000000000000004c235b0e23ee7b2bcceb64fd0e126b965204f7069aa3b4fcbadb8d658e2ecf86b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e5ae8d02fc1cc6b946e53696350731ed439f8c353b0b69b40b324fcdcb435ba225fbc22a33c9fe154": "0x00000000000000000000000000000000100a776e233546799e6f48a26bf32c080bd40e35986f608353c5b61b3076503472b6c1880096a49c2719e0d6d8e863ccbd075aea064a47cf81c96ca5ba5d45f83e56c031a86027cfcb114d40812f06d9f462a8354171478b00dd075d9271db38720aeeb3fa38505a54f76f2a27321f9b5b875635d3b05a0f4bd92710105f377f01", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e5b4c4e58d29949859a2cb674ea2f4866664769a1663fd6aa321d9cfb89b67c402c881891700c0f57": "0x0000000000000000000000000000000004640574072818008b0ffaa91a3d5febf7cf106a9285e35003fd7b55e2c1ae8b6d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e5b6407bdc635903a666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd07": "0x000000000000000000000000000000003ca2fafeae641e6e264d77723c00ab05f503db48ca3597cb3242c2b54d90abd01d56196c14df0a7036b943ecd01396685e799f786c0f131796c06850ec9342ff014a8de2c6b6c1690a27aaa4cb64e0168bced54ab568958965187646f06442be6dc2eea43fd0e45e0e756130e01667533bcaa001e29e0192501e7ce2186ec3554aa28942a9d2e8c8501860b847eecedc45d602e614b8b0849b959607d0dec3d071e40db41d7f07b2b867fae0d7b8ed6767d23f7b53b032710dc5b5043474bf1d11f6b2256178f0210557ddcc138e8b264204e4674fb2d73a8190faf4ac3724722f128865fced1fe2ad870d0f1ef6ac3c73c78012dbaf73ee9db06ba403cb73a5230ec3f7291f82335606f98e16f5480b38b3da95d2e7ee9489a89a8a39f9dac956e8b6a4eefeb4942bbca2bee6baee73280d49c1b7aff8d1aa7616d06bc173ad7f20857206fde63ea508a317a77bc1ca2a795c978533b71fc7bc21d352d832637c207c5841ccd565eff0f8e15ed5d3b1752749e2fc9e3f574c8434bd09d7f8f815ce3268d415f136dab575d25dc0b1932b736c7cc785a1ccc04f0474b458115f0ce23e7ebb9b8462643b8df28f53dc2dbdebb20bd0cf8fcb41d7d09db59d46a50552a63150e02f15706d9ccceabf476a79dcfae5bba86e66ece74aa031da5bce36", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e5bd6a3e1e8406faa3c82ab06b794c99f14a161973be7aa6012568b1c491d45ec969ed7420bcfaa59": "0x000000000000000000000000000000001c30606b4c1b89b4e562efafe76bec80154ac8b3e16e04c2e0f619bcdc0a5eef52102df685c4659f9c242ff9ac8a4ee5305770ede106db7a3cd5d3e8823e33d001468eee3d896dc822a496e712d1116e0731235c54bcec12e41eb133bf9c98cc15ba3e9b87792bcfcc237fa8181185b8883c77f3e24f45e4a92ab31d07a4703520aa41228830918cc1cf16e50df86ba154a483d77ebe3182bacfb876af4fe9ff6bba3e9b87792bcfcc237fa8181185b8883c77f3e24f45e4a92ab31d07a47035200c7fa2b79d310ce32237db73b1e3d581a979b3bbd7f19ec44ff1d37b87dd3750", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e5c66629676bdc5f4766efcb5851054b4aeef694a3ad03d7bf36980a2094c07cb7b7cda28bde5e149": "0x0000000000000000000000000000000004788bb550f52a25dfdfe1db243561efb956924b8f0a76c16c62ab47b9f6d1cc53", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e5d3dec38e02e37251c6d8b40be9990c19e993d238e6e3613cfc6cbc51979d5fa61dd6ea259385609": "0x00000000000000000000000000000000242ac73c24bb740376a5b0f44814e8ce8b34f23be0650e99b7ac81e1159f9c31511de154f8bc16e4a7bacf303141c913d91b1efdf4ae06c3a227f0a2877a19e90a1e7aedb71a237b95e3b805a77c031dac6a8a675441390e66bc1ddec7a41a1e54c4896d44835fe17827b4af77172094f91a1b17ec3524949c2d626ce7314a440f18cfd7be6a32e4e3ab369f7be8a880a70f42a0ef260ae11a740b1feb7dc4796937982501b1c242d5d23a353d2b44e7ef342d32adc991306632ba6f3c61d4487d6881000dfa449822280d217523794e016588e4a6c4d9c14e79161ecfc085e72f01f8b3c479e6400cdaaba7b54357ef912ee1ce5b803db86506805054070ea176dba5e754de5c965c3a904ece87b7a0304c642664457bd2159f108cddd56d781a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e5fc4a5c686ea5a40b346948ec9e4cf84b965ec17a752b3e8eff098934aaad42ec50a347dd7936583": "0x00000000000000000000000000000000042e001e3f827e6eed45a60d131d97df1a5b429ed1d0f89fdedf3eda0a16502d3a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e6277dfd6b7b21ca2faf5f68fe828f5af8d69c116efd937d90f1956b0edd94e05d8b5285a8eb2a663": "0x0000000000000000000000000000000008a0209c10c7d6633de64e166a68d9b45e7f02aacd9497f5c552e577abcdb17d28487a87b4140abf956405e80acb86bb44586a8089e79b476516c6ff9b601dbc38", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e62a40c29b1fd2c93b204051d55c2c80cdf6e0d3346944e921fc97f0b86a4cc5eb5547982fc475d68": "0x00000000000000000000000000000000140a2178b090386d0e2645866868143abc5bf6c91af252b92c0c38174200f42904d8a03bce1c529217f76264985b1f082f154dad529677f85dcadc3c04c4c77d5948e77b8c0864277405a333a644bb283a03ade9c351473f7c167b309ba4cda308782f3f9bd84fa8b7b659bae702b90ee402c45067cbad3d97c89ada7db6e0fa67dc46b7f9c2debfe58700f30be615154d6e38f059a682ca0b5049285180675c0d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e62d8d4af4eadb392cc52156f09978540d3163b798f65a8788bd04d828f366674bf9ea1f88e96f519": "0x000000000000000000000000000000000424f7bf2054bd95b8304900a908f8b298ddf79ccc09931f514305c490d047a63e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e633914b3a4ceda021eb38b0d5178bc680c10a204f81164946a25078c6d3b5f6813cef61c3aef4843": "0x0000000000000000000000000000000004ad0992e51165d995e649d0a90b3c349ec9694d84ab1c3294e4a9c58839e7b4c3", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e657bab39f52fddf58e07d43b19d901badf3a7f57155ca84f2f835448e93a141bbbd33eac4b767d15": "0x000000000000000000000000000000000cead7c523761f8ca72780d241f11dfb4b3543c4a1e17263274d34f468001d571a768659892ca9fab8d64efd188054b2280efcc36942c84616653265d417fe966b0a3c9a0dc2a4578d4b58cce45258b3ebb0644502b355ce1241f8a38bbd1c0a72", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e668bbbac68a19cb7a02f7333e25590e4f568ae8a2ddc93a879e92e48fa3cf1666ac56e020c106d55": "0x000000000000000000000000000000000cf836649df542b24a1b63d80916ee743d4734640aa796648649685dbdd430c3628eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964a2b3963c9d349de4363ce38f5f9854fccc0636768d947108b7f219c240837855", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e66eaee046d4f02542c89cb8652eff8c73266de06baa3760534e7f37fecff971c028c2910efd6a947": "0x0000000000000000000000000000000004d0ed5a8ce0c20c3da0dade9db4969d52b6bcbe1f8914d3d9a2019526c9cb0b27", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e6773244509d0e7c8d46cd0ae6eeab8ef19bf289712c9f2ae4272c663bfe0786d7c10d4ada5210003": "0x0000000000000000000000000000000020685f836cdb8f2749ca5a225297e1a8207fed4511b575448e09bec5a870480c6b785c76e65b2f38d3dcea220f148f8d4b42046ddd61eff8af6828d24f633a9c47021ba8ef466ccb7a06bfdefdba01817e620ada5954c34f617f6662e267dbda15bec6a380acb8489f21891545cfb9b4964bf0f3170c5deddea166cd8f87bf20787cea95ce431976642559cd8c64177ac1a51ef6c8dba625d3d021fa8807c1a137a64c092cd19a2779a2fc967ea1be4727ab3b29858a2a6f46af009daa35d84121be4c6b203c1e605511cfb8db27330ffa7abbaffcba5836a0d6f3151ee8eb993c20e1d9db00b7eaaac9d3f4a95ba206feeb6f606e16f9af3f2ac24c1c57941e2e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e682174f20abcb7fdfa5e6f955d973efaca30897c4a3e4fbec88186ae72b8b331408804d73dfc275e": "0x000000000000000000000000000000001000cb68a2c03e666f346a277036e8f27c912baee3c7b41c5c19123840f3a8e8fc74065a60fa33d4c2c8926d0d761d133a305f500880fa95ad86acd865f274414c9890fd3e365f6954b5bcab3c8195024da5188beaafb1b13d954c0281647ba535da0afe4e85d09d168759ffe74af6049d1001ebb6d16a0427e40a4e494a7372c3", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e68b2359cc10fae926295f0214abb5522cf3becf82504ef7c87036236e6b9afd263697a99bc5d5a30": "0x00000000000000000000000000000000047a813a8832981068f9b921dded7aafe83cf89eba4c91ac5460c65d76a61d465b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e697ead568e820ccfd2eb07f02043788e254d9e2df57be11566d241c56302b91199b4647947af3020": "0x000000000000000000000000000000000452ff057f98f0c1bed31b2fd1ccd8de4d4acf957e79b3f71eb69820bf0dc1d22d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e69e34466005457c2c48d1c4fc44dbbc10b86e40db24f95f8924efba2b31eecc6a7c32bf2b8a4481a": "0x00000000000000000000000000000000044d0887549eed4b4479973f77c05a3f40c7d182e983c3bd873789a519f5cf7ea3", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e6a2d4f462a713142b45f7d4b6bd58cb550270c1ba3d9d97b6766787966b0b5c52c79b4d3b51d9a43": "0x0000000000000000000000000000000004ef7550d2801399e7f9b263d92c33ce0eef338eb5527760235e4ff5219bd44c9e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e6c4d2026575763437cdc1a6a5a7f23437b6528edcdf553d0685f940a4e6e85579727ef3dc574563a": "0x0000000000000000000000000000000004e33f4520b7954cb5fee8778b44283deb0948c23c6b432058b21ee84e92085f3d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e6c5088428455187bcc95d7d061fc6a655b795794b9b1614d0a240e13e81de9dce3f2b184f653da7c": "0x0000000000000000000000000000000004e05c542ad7369509f498cdea5f5c427f2fd0b3051884184b294d9ac10fe64e34", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e6c7206770c39d9295a5f7eb7050fb96d8d7895d9afce428a064ce66e3b094805bcad9a8e68cb9934": "0x0000000000000000000000000000000004adcea185416af2d3e8df8c1c8ee8a634bf1c3275b3820cb6d935300d42c73b2a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e6ec642500076ba25a6c5f0595d6ed85d6260bc682d4a68a4b4e605d43c67d56f2765d19698549772": "0x0000000000000000000000000000000008c3405f63dee12641abec6c12b29453b7065da04a95c09998673aca6f0c4a164e9f958e6a1f4ff8a986a4e7d76c417bcfe8affda4182e66b9952dee757d7e577d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e6f73628ca952be18dad0bc6a0aadf06c56416e83bf75e865d41ccb5ffd74eabf9e81d47574b43049": "0x0000000000000000000000000000000004b6f0f10eec993f3e6806eb6cc4d2f13d5f5a90a17b855a7bf9847a87e07ee322", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e6f83ccad3981de4b7e569787b1b323854ac9a8c40914d400b6fc23a2fedd24321f814ce7db7f6563": "0x0000000000000000000000000000000004c3590205a4e4844653590740c64ec31e5d2af126cce71bc60df95bad639749ea", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e704dea62a3fb9e66a471c55caca4be7b4e60c6e94b20f9028883f8c64287d4454130c657383c3442": "0x000000000000000000000000000000000ca471c7aa909cc665212bb36003c52c5d3eeec39f96556a8242e861c5dd7dde415ecc1d4e60a92262c1bec62d034c979f42cbd3fb1c28570d5baed6e5ed20d533c83b0bba37f25f365e26efbe6c9ecfa7905dbdc0b0e3ae60b29980b42c509c6f", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e71352c2eb5ded5f286484f63c9e0ae1f460dec3b53307478f5ce3ffab22a1334d34a52da7527ec64": "0x00000000000000000000000000000000046feb764cf5265d8f491f921d51c20318870ffb669b059c7a5b951d9291636b8a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e720cd28dcf99c07dac133ebfde441672c055b79ca9a6059850984eead1ae036f48ca1230e7f0556f": "0x0000000000000000000000000000000010c0532217caad133c16b24a5e3b2da932b83aae63356a787e1594e71317df7cf00cc89d59de520e70fc4b9bb9a43b41b2b748ab0dd51ea18326e8ce755931ea0f828ad82b9738a507bf481d6c3feb4e50855378b588a653e3033f5eab788c335b08eae5dfcdcc8e1890cb16ac515d4669ed0653e98623be435327545c72f5aad7", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e72111003b96bc266a24fde6343e2bf0aefa296afcedea6f16d37c5e1c0d8b6511e7055cf7282b60c": "0x000000000000000000000000000000000422e84530cce98a0af194e12c568c8923fe6d138dcd6e19fadcece6c0a5f10e87", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e72f593a2e278eb75522e16fcd83f6d6e08fe2b44d56b5d0bdaf33527b49e8832928d003d8097ca63": "0x0000000000000000000000000000000004f09bafa226f3e27f549d4fe85723533c0f0e54e366f3a87614ca08443b06cd54", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e74782af8e47bfcfc0a4a3233cb4870d9f20b5c55e77f839ce96a26f0fd773d019179197576644765": "0x00000000000000000000000000000000147091f937fba948654220a41ede536b0d62cc30d20274a28005b8026564db8d25a09b87b34880c5375ecca849557dc87a00a6243938d5882017fa0d1f60193815a81b8a2a03afd92af18f0338e43afc504c6018aca6d9197c0d3a149ce65efa0ef68fafdda61f708ae4dc2e384c8012719774376e478bdc857f7191ef449ab5226011856d19723839074419b4519ab65554fac975017c6f08293f0a0ba8ec9838", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e754cb56cf6c9489ed016feaa68e25739dbc4035faf2577d17ec9d7ec2125a99a6770b5be093f6e32": "0x000000000000000000000000000000000868f26829ec470fdad2e63d97741a88df1243ea147bfb4639b97a4c816f9605fd11d3e37f3547a962068f9d46c046759e8a866b64c844b0fe7426d95fc1b599c2", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e75d112c2582d6bc3922bc16cff1acfc4a08cb5bfcebadaa9eb182cd47a51b8b047a0202ae9624a1c": "0x00000000000000000000000000000000103ef37fce11457ebc5f00b154996471fcd30eae17efe69e5a39b12b76034dc7e44c4bf7f93d0a5ed801ef778f8e7ef58201bdd7e33e167faf42a01d439283cb43e0855069a59fa0ddf72205213ba6d7bc3bcfc44316af9684bb215f815fc0113f627922c9fc2d2f0b5707a3f0cf870fcbbc62ab06f4e4991cb04d376fa3593d6c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e7629d0fa8c9ac2292c81fc1faa024d3d1a727d953187860b45bcb185d046631ab98261eb6f2b8d54": "0x00000000000000000000000000000000202357d8ee622168c14c8d3b65f311c42d67023935eeda34bfe81c8ed7791032384431708b474e888318f65c93f0049fa3043df9d7f1ec1f5f3d7d5e1493359e5891e1331ab2af4b510ca29f5e5dcc8e71d1fc6cc70ab24e2cf8513d540fa388ee51dad69a99af405193f3eec7593e9baff022b7fa55de147d1b4f9d214710388534fa4e10f24341696e46d61b982f279969608b00edd5d32298f3cab0afbb695ad8c67a57859b28434a12f1a078e2979d8c1dbb2404c0e15fe75c1c94e39b2061962f61510310bf29eb0c0170a23fcfafbabf3d0b088e69d1982b2a31b0d7de466492e3ea0e62e4e357e57c5b6732776f069e996133a08953b1c1ebcf967b647b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e7642074451d24466b2e07be4d6d82f546ec91d6009ee215bb736be5b4362e66e7b466ec72d47624f": "0x000000000000000000000000000000000cf2497cdc525e2b482ddec24761aeb31826701c5a6e35919787bfcb455c62d77400831e9a6121a6d5002d53a89ce1d209d1e3359420a90620a022b22947d41140d071e04ec30e105db26f05b81646219bc57909c674c2a081e48906e604f9867d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e76d10d82508ddae096e24e9b5ab82dc275b82318a52cebed1cae3e25be096d5f288229b256359e43": "0x0000000000000000000000000000000004c2de6256e9ff0ba9b97f862290f69ec0f72b31fa0a6843e0175e9e81a0165d6b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e77300f589b9251ff6c695c4e546c6889ca591b582eee8b49ba68d8485a3732e08d232b2f28f2342e": "0x000000000000000000000000000000001caee72821ca00e62304e4f0d858122a65b87c8df4f0eae224ae064b951d39f61028996c52694155d7ec9082650fbf108f69da60c44a4b2565fce4e03f9bbb01780a7f29211d50461588ec3c6857c9ca25474c650c7d2048ef2283a2245ceaa8310ca0db0283dbf8d123602a2ec334ab5c3fd9e2540577e0955eaec679cefa4f0a24172a563943291c97d252def71e17abf467a1626bca358728a90a82b3de3118c4e4ff6adcb360ec9eb50d5e04ad47aec66a30055222dc13c6215b5f2db59767724e0032275bac5598878e5dee08149d11c44700c9c4626d1f339ff1be715f30", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e78a2b18d6db0a15e0bb5e12d2f6ada25c3b64171730f1c9efcf76d3bbccff01abd9f92a9aaac10a7": "0x0000000000000000000000000000000004ed6f8c70207598bc9899581a491dd8577e82f0ab65d5a78318b74bf51315841e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e78caef8fe8bb73f2a4d813e676d81d97479f9f15572d9eadcaf4503b654c0f6e7baeb49e84510e69": "0x0000000000000000000000000000000004730800477a5fb0302fa9ff4b5623662a27719a304dedb3e12eb004f48ecf6b29", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e794c7db721956af68a56b1da8dc3f4bd58630c15f5754ee634528a007ab510c86a7e1fe6e62f4166": "0x000000000000000000000000000000000494211c46d7bb07c67c2bc80e7d5ba4623f8ef0d565d266723ec60497f0375b3b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e79fa24e506fa3936321d847f8a53927fe1132754a7708c61049b2d4816ab6ce7195a8308e97c3d41": "0x0000000000000000000000000000000004d6d1d0ca917751d6148063b9e1fe8a22f74396e63a2f72e149b39e44a0e77c7f", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e7b1b6d2a1ef31b0a3e8c72ac1b710bbce4e7c190568bf560d89416696ac2a4700406487cde98df04": "0x000000000000000000000000000000000408104ce4b326f4b31735dfd06fdfb1ccfbad16740364d8bf0f917f32c090f362", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e7ba825eccf7c1a59b66fc334c7c76cef9cc5ceac0f4b0837d845453a2e0e84703b280edf202c8f76": "0x000000000000000000000000000000000415e447e7b7a849d4489323a706823d8958db184c8c16713f1a6a49009f6da96e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e7c4a65539d0e9a75fc8163c92cac3dd1d5c2c0a049c3652a8b3b8b6cb8a0867a5e494c650b4de371": "0x0000000000000000000000000000000004f5c0dc71bff8cbdce6c32ae5260549ecce90af19512e7da031f2dd02fd920689", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e7c90c43e925f2226fea885b4e897d4fe908db6abeb39365ccc4689c2b6437dba675a4a0a0c0a6610": "0x000000000000000000000000000000000458cac93e1f29e4fac5351cd9879164bd90286cfb45af827fc732fbc462d2eb2b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e7ea9886926039761182fe099d0c1787bb1b88de855537dcce095204028736ecfde09dd115c498f2a": "0x00000000000000000000000000000000046b973df13cf0190a75f17633f8a391020df8bac9aa9e4c50670e0dad135bf726", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e7f38100bed09f866b0b000e408509bb033443af0bc700ec11894f81c090d58d7dc2ae3174c54902b": "0x0000000000000000000000000000000004fe08b40ca50585eceadf8c27c0711d8cad57d20cf68114b8f7fd6ab32534dd25", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e7f6122436697b141faff1c4b5a94649e0338783122b93a469eb9e2e375bb71c92028a8fe9b6e2f46": "0x000000000000000000000000000000000c927d335e751bed3dd0da74a10e610fd2996eea79b625bfe519e8b9d2b96ec75a88ac52f4800ed8569280dd140faae0b0ed5613aa77e57a592cb9af8127dfae2a1857d6c0ec8d629533be85a017b52214c8c4954769fce002fdf6073c0b177f3c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e7f88ca6ada2372bc1a0eb7fdecee073651f1a21b9779b7bf5670493a0e35a53ab83b3cabab814a77": "0x000000000000000000000000000000000828599b35f8830b27f465e77733dab096524c56d03921532c474f06775af121fa9e447cad47afa21f342083e9ef18cf04a27d81fbd0cd742e8ec36528c4514269", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e816f363193f343a643fa61b298e82f9f207ddea327900cee26b554756c4a533f36cd875e3e7bcf06": "0x00000000000000000000000000000000043cb64a5829fe55a733bbc427b0489da973cbd6b3bbeae722ad4fa58eb8277a30", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e8179afc2191e064c0ecd029fdf9259e900f08996fc9862ebfe100d49c439f19bef7084b258175a1e": "0x0000000000000000000000000000000004f4fb68640df03aeecaa19d6eefddb11516c42b586d0ac0e56f354b43b70fbc22", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e83da9cc355d0681680d8a3f4317249a895e4b49badcfa7293cfbd215d6e552d1c07024d36acfbd5d": "0x000000000000000000000000000000000456613c69103858e51c79e3525fcfbc8315d93383b9280f0acd551bdc75a91463", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e85c4e925378eb4916c42f37017f31d6c9ba5dca62626e6fb434d6edc31bfc9aa49f001a6ced27876": "0x0000000000000000000000000000000004ccbf40cc53a67fedaff6111ffcf4d618af1ef8258560609082989fe66911233c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e8640e8e247a0e20f30cfdb48ff7f33b08499dfc618a8ef9699b8345fa65f0b1339eb8eec3c0e4555": "0x000000000000000000000000000000000402aa5256d804b33717f1d338eef9901e89682b80c81e3b1138a02079e0848aa0", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e87258a23c1cc74c20c618307d00b3354999ae280858c01fceddd77a25b5bc665fbd4634bf86a4178": "0x0000000000000000000000000000000004f3717672dfd677afa541355ced152c72acf081107fbecbc9e0acef5b54b51223", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e87537aeb7813f75e5efbe83a561d19b1d21126af5f1608ed28e56f4f70251cf965fe3dcb4901a26b": "0x00000000000000000000000000000000086e32fb9cce7edb56991320b049369ce553a5ba93c5d262dcbe796a9b9f3f15248595dbf64624ef80da7a916f139b607fe8ac19aed219da7c7f9990be2c214d1e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e87d2f749afa8b991984e16482c99cfad1436111e321a86d87d0fac203bf64538f888e45d793b5413": "0x000000000000000000000000000000000c74edbae75103b891bf37c39e91cc489a6f68f7b6756b32875d5c5e28c173027208a73c9c3083971fa2ec757d38c8e7858df8076aca40ce176faef81fcbfbe2580a8a307ef15b9f928697fa09dcc72a2c19a266c1d32fa158f9916b8b804e1621", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e897931e60764d4ea8e602e63afb364ac583747b0a8bab092d5b20ee98f0495ce7562a8c992ac9f17": "0x0000000000000000000000000000000008bc81d1b8a3c50fedf5323698e1fd52fecfcda7436b8ac0ac6f663e146f4d4f5cc0a05f52c96d2af9862c7b89bd23a92c7e8345b92f1115d368b6548a58aa8f28", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e8a3ab59566c3860f1ecec4f3062f61988b13e9dab318860bd0fffe5b7b37880d50a614a0a20c2502": "0x0000000000000000000000000000000008822058fa0d13ffcf079d48b96c07f71f6cfd7fc9abfadd85e205ae7a18d7bb3d3ef0a4136babea739fdcdc300f622f23d9a7fd229dd90c8f3c9e3d75fdeeaedc", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e8b299234c2604004face99d3401cb9b45ee1bc0ec52f4cb35914dc5ad27806230534230eedb8413d": "0x0000000000000000000000000000000004d6b0e114db8fce63ffc070a452ffa1f47f0f7ca51f32809a624323f51c146d2d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e8d47493ee3a600117269f7eab2e5f91f9efe10d0dba0e7256d9433230a8f9fdc1c4af10981853476": "0x000000000000000000000000000000000474fd218ac454949ceee42deee0ed129de80fad49f579870d7ee9d4a2d1e0e27f", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e8ec37554e12de10ab08b555a5a3b2725e01ba15eb40aa32dc5b781532854b797808ed45e752b047c": "0x00000000000000000000000000000000048cda73070bddbcd243f2d1f25982dbee2a0961b277ec209756bf794c2d0f7f78", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e90f81dd1ff30118bc44ee45000531bbf1aa4c23540940eb10599e14b4fbed5267c42a0ebf5d09f66": "0x0000000000000000000000000000000004b8815e0e6280e1a3ff96f62dda149363592236eec3df9d48aafe8796c22e1d41", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e912655d634b54e9a086de7162fbfa0b91a67eee94b697646028edcf484ae78fdc0627e7eef1b2247": "0x000000000000000000000000000000000492a11c7b27f2e0db0137d0745780fe21467bfad9bb6ba3e523b8e2dced7d60ff", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e92a9b8874561dc036464f15335eee136dd7c216b994ea6ac3394eb723590e9e065fd9061e05d0035": "0x00000000000000000000000000000000083c235e80e35082b668682531b9b062fda39a46edb94f884d9122d86885fd5f1b0e2d58494df50bbe553b779921de400483c550c57a9c7b5e7ef9735958228e34", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e93d0e54cd18f21273eabb0f32cb0695fedb8d25f6299fe51f90b1e26ed858c1064aeeea0977c1e67": "0x0000000000000000000000000000000004de1894014026720b9918b1b21b488af8a0d4f15953621233830946ec0b4d7b75", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e94770065de5e75f5d0e903b51697fa10a7f8c7e5c750fb3c2f90424b749be8b6c0f0b120f5d7277c": "0x00000000000000000000000000000000048b1789281b38392a08ab0516a21dee49fba6f5e55381106ff7ae190563cc84f4", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e95e5354b81f0f460da9f7fd3d9612a68d2ead69dde53297b172b7db514d0d261e7c5be987df7f32a": "0x0000000000000000000000000000000004a65bbeb4425c55a611da4116e848b0cc39686a11f88dee6aacceac6bc5eca657", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e98d18fa62c6f4741589e41f29ab08f2d0e4e4bdf3d1c8a868162c720f9c31e22733f8d633fba901e": "0x00000000000000000000000000000000041c6ea9855f8f85002cb80858c01488a8ec5f459b1265248752b967a685eec446", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e9bd6b99bd8180aace8c7ad65c15fa3ba64424a61b177382a0c5468135aecca9ca454f5e7ce4d305b": "0x0000000000000000000000000000000004888f666828e8328a33647a47bc97574a6a5671819270cc01e66c7139a1a6911a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e9c21b6ab44c00eb3127a30e486492921e58f2564b36ab1ca21ff630672f0e76920edd601f8f2b89a": "0x000000000000000000000000000000000ca4c5b68728f7fdbdb4426714a385017a471d5cd22a156acd30a40277e6417b600edaa0d08e8b21d6e8f946eff381ad4be29aa63569a54a6a75c91b878b463033f6be65cc16c65708bb6a0e4b9958ffe23d1c56ee5683670a69dbbbb70c10d507", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e9d430d0bdbcfdbc6f0de782e8bad3c663be60812f0a2ac63464f5da3ec448c73334c07d71ef27f2c": "0x000000000000000000000000000000001c94c8f2aa65dff23f542ee99d7191e88eecf45b3a61b7350dc3d48804107e393d10a767cf483ca0629c6cb5dbfd20f5f9d8468b153b90bf3c40443bfba9354a14ae34148366325c0ab46d5c086e05c87c76b58125490dababb7899e9efa41f53cba318e50a4896c8228b14dbdca63c64b9a4fe82ee967a41612c4a55909daa960223188d5f28ee27f7e9067e89bc52fca8f1da20c6a7548a21cef18d8934f820ff415f3ade9353f49fffd2f3f2ebab33ab5f1b94390ad5758fccf5a00e9441e013e5e8732b01a310abc9d0ee2173b6e7f3d00b291f1ecb2f424499c353e4ad955", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e9d8014dd68872e0de21727312b2b7ce579dc0f82f129d4edecbcf00abca607d73ccf16e8352cc777": "0x0000000000000000000000000000000008b4b3a27f1b0da0e787c0863ce2307f5617f485a8c27f0b17c2d3176e788c3e6e24aa9e7b57a0e49b834fd499464cc0c83135679ac384abd9e6d7f07a8def164c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e9da48123ab54b499c6477bfd57c12587b1075a80d944c7829784eed61a5c8b8255817e1d62d1070e": "0x00000000000000000000000000000000044c1bdac31e30cd50156586f5009d576c2efdc103be5ef0649d55d3b53941760e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e9dcc277753735b96e04ca25cf1cbc516ed1c138492a7f2e606f60c5a9ac96cb405eaa3914fd12973": "0x000000000000000000000000000000000434bc6fb5ba6e2150087c96fd4852ec188aba74a5a383a22ef66b12c588cea00d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e9ea70d098f006161264319ed6a0895c04112917fc9bdc0771f4a4773aae014a99d25bbe06fa1057a": "0x000000000000000000000000000000000878c7dc6b311bc7e44dc95793f0b8e1316ab1447073728470fe08e0b4a8dd243b005fa73637062be3fbfb972174a5bc85a2f6cc0350cb84aa9d657422796bfdf1", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37e9f053c2746e722456610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38": "0x000000000000000000000000000000000ca61fb8e0537d0f6c8d4f94d0466859215b77e5e53c44981253983062b7a46f72a160a7305cb5b47a2903f6648fbb6ba1ad24d72fd49c39ec7f9d21e184f3dcc778c505721568ecb57fe677b4c24e670079e8c342cfbc7b312c146067a2dc02a4", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ea0425165102d0e25091bb84ac5b08adf128e855e1cb079ed594be42af19d09d680dae982ac209ffb": "0x0000000000000000000000000000000004640d12a59e7ac3ab24ad5c33dada639c058006c59fba147ce8caac535e801415", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ea0b881b0063a363a02bf32e061073c44300056b416cd66a4fde1e6c120dbc0089bb65134f5693a3b": "0x00000000000000000000000000000000042eab66a1c3116f15f55dd2996db419e367106043a4c5491a5eeab1d33a17460b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ea11a320b9a8feb01f857311106c8d7b0daf6e096db9a0d759b52403e439ab23fd6559780a8b1c803": "0x00000000000000000000000000000000043a337becdfacffca71fe67fec208733754f035958d349d36b30225fd47798f6a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ea20d0b846e3d907e00555ee596210f641d2d48b9a1e2b258aead3fe8de4e973bcaf5f24674044367": "0x00000000000000000000000000000000040792d0467f5422f1617840e11eccfcc8bd24bab23b0ea04de920c1109e1e14fd", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ea3f67b4c0527d154dc3aee12519c19be02628b0f808bddda9aed564027ad809cd392c13e9b924b65": "0x000000000000000000000000000000000490ed88ee1c02bcc5938985c79cd8bf1a048a9a01f6eecf8a3aec9a83aba8a20d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ea6eb394e364b8f9c7682e4e399a94b0dfc4a9bc476e0bc69f25413687d2b1c29bb7139dcc8e8714a": "0x00000000000000000000000000000000044cb0f7f17953e529b8106c743f78d238e3dd6a90f421d32bc920ce120682c801", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ea91237ad3fc3e223ae0d1db9082bdce75480ee80c3bf3c6496de1ef8171951de2edfe49fdbe30a67": "0x00000000000000000000000000000000048c2bae6068de838d1fc684db669d5ad1dfe26f37887ce815734145764b7e7124", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ea93c6e207f351cebc5c184f0565e2192d6aedae584ee736cef875f0e1c558ee3ede26869acd0b4d6": "0x000000000000000000000000000000000c31918cb9b9c9414a2cd4dd7f720cb98fe98cf852636fc4860845767989127e7d02e358bc31b7ccb578dcc5c36ba2908f311ac5ab2ba8c1483595268a7189fb026621dd4e5cdd0ba737c572710c13df35b316d39ecd12c1ae1320bd6db069a07a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eab7e3e98c718729f7a895955042cb3fe863f3564e7b30e9130e37b6d905be11c0cf91064de1cd83a": "0x0000000000000000000000000000000018508f2cb4567caff040d0a47db244d7ea791d9ec23f0d5d33928a7c6a62b3077aeaeb8cfd46b3d6a0abfdf3961d572ddf2303545b03f0b1870563d38c69de4f20d8151ecb8e8d11e6c6bd81ae49216b9ef92d2b83b3ae39702a397922f14777681e503909a89514337bf9ce931876a08003396a9def52f867e123cf1420a0f70baaff773e3f134d4e0c90ef5e5f37adb603bb591b7aadf67a2a757101a1b70302d97d35c31f6d0b75bfa93ec1eec8823651d1582368679c44f00ded1f5401b5e7", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eacdb8f7d99b9b4f82cd49434353de1d51598b44df15fcfed1e26224e055923acc9f9af881dc31d5a": "0x0000000000000000000000000000000008ecdd548c83457ab43caf7867e2bef91ef783025db9659afd89794ec1220acf29607262b83b9349efcad72b5f2a0cbc80b1fdb622aa81a861a56209014e568337", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eae60b338fc29997e986a4fdcd53d0e438f9694c9a6bb76665bc50acf76180049117caf3ca9bbcc6a": "0x00000000000000000000000000000000184a45b84757c48776f4c5d887e269e8e6c3f2fc55f75adf222662f129f9fa5b40e4fc83d05b1d8ac627cc89bbc95e7379aa395cb168db459fd18c6bceb6d1523492e594db8b26bc07fd45cc783db49313f17183491f1826d417155a95d5f7d85ca459c65e21e9f36c344f9def7e0ef28e96f6e1fe02f8e3aa798a8fe9cf906453e27cf0ba46292cc95548cd67e3e29036b0eaf1ab66a4a6f5d7065408d50b9759c804d9b3aa2f285568305291efb2e0a6b70aebdd5b04ce6a5506c0a8678fe604", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eb045191cd50fbf997059b7d9ad6e9f5bab40385874989cd04aabbe821094b7e45b5e4de5ecf2bf4a": "0x0000000000000000000000000000000004aa29d60ab7eeb8c8ad1e7d37f8438bb02cc86c40c28218d4f183f110067cd368", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eb1580a59c1acfdc54421050207b47ba1bddef66d1e1deee5b27d27d7fc526cfd68e4be18a5b9b146": "0x00000000000000000000000000000000040e49baba2f2c359d846db58da8cb83c820aef3fbaa7b2164444d4946dc047e08", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eb2867f14046b21d05a33766207d51925e4b9a2302870bf737305cddd5bb2df2dffa379963e586771": "0x0000000000000000000000000000000008388bf0fc0110c1b18dcab471725083bc6b0b52edabeab0c3e73e506a54f9e04d7883180d8cea922c0648533d6b2be8949765483e833ec49e8dfd10a0ea61a03a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eb2c78530b4a796f2f8f0244b95932b7a67caffd31de91edc5cfb3aa2a13f6199f127ce51c558204a": "0x0000000000000000000000000000000004d7264854fc5645d065341b90947bd9be1564df8220073eab4043f4022e633e8f", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eb49940857135462fa0db8c6cb723c474639931cae07e095e6e8d9b870c90f5b499bd8e6fdf4bd54f": "0x0000000000000000000000000000000004d6ce187276961ed56ae9a29566279cf443df42f82d9890b02de9c11e4d288e13", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eb4a5da87e533f5dff58927b296a23cbf25794b7cbb8fe31c5e68f2bcd2c2483e9c9cd0712216f232": "0x0000000000000000000000000000000004bf7105cdc60a8125bc7d465c5587c26d9954572b8579bd176052bfccd2847506", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eb51eff5e9260eafe64e6db572dcc9e5dc57445ff7c68ce85e42527aa74b2ed5d1e2bceefd308ed05": "0x0000000000000000000000000000000008e0512205b8cbb851e7b9d862d204b1df2d7598e03041c7e70b373ac45b1e6cfc8e6c97add8c5563d923d9b702f854b15ae59ecad39578845a04239c594046044", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eb67813127799c8e920b456b7a8651f0b81f4517ffc79737cc392230cdd92a5b4bfa09ac728a0b10e": "0x00000000000000000000000000000000047c73d0b870509f5c2ce7ef8313ac58becd9dea02162a270f9913116d690f0904", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eb85669beeb21dd0baa6646d5b85790bc0ab869d80385fdc10e1f4befa7f8a4bf31848f73012d2823": "0x0000000000000000000000000000000008c4bdda1c96506117589bfc216f9e5e79510c179b367f31401e01f1997b47181b66d0425af5558b202c277f282e0d55775eb9fe23e0c68b7a10cfb7f59202b402", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eb8a74d4a444db6f1bcf647b00211b2d6c8cdb9e091b95ba03ac8bc6a4dd0124f1f09f8c917d1d270": "0x0000000000000000000000000000000004698654b9b352da92d00844ba8bab505a2cefffaa6269cdd2d407cc866440fb88", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eb9aa88162953b359807e371d9df95983e2b9face8efb67f962816f1a0c6681cb1e5455127ab45c09": "0x00000000000000000000000000000000088818f1b289df88876f0199aa1fd723dbec6e7bbc5b08e5eabb89edebfbe3983a2a1202907ebf57434db992641143ac78c8631a1e412d8934e5167153caed0645", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eba7663c3b3aaf51b09cdd094e9a51d26bbc6cbb71d3f7c5b8edde629402e3e5370e7f6904512fc4a": "0x000000000000000000000000000000000c47e503b630c37057023c04ea57149dc70ae19f186db24f59881c55cb61da522f1baa453966c043ca367ccfa19f450244447b9d32f4b7af2d9749e55a57ac09cc46059d2f1a61eacb55d5c4c211d0b35cf3c3f4fe1f2601cd8bf49f30ac370c49", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ebac296575a7da3090eb83479fa34dc63024ed44efa464427375a44de486e8d6007c7842b45ce817f": "0x0000000000000000000000000000000004eadae1c87f39ff2b60465c9640a1af260a815725f9ce3fdda291f92f7769e3b4", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ebc7126812bf1df493e1630f099540e76e98b9fb0002c3a5ae3f40b6ec180df68fe5cd9bd2088fa18": "0x00000000000000000000000000000000040238a0a2b0989bb426df8ac92118b4228a81b354d0c87d8acd25c8de509f2226", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ebd45ee52acd85afa825872f7d324c8d97a9d5f5c94d918eea93ef783f305767b768a76f9fede7b4a": "0x0000000000000000000000000000000008c07244af8ac9b81030b3c47c23cd5b10701d4a5490790e20d12a583e5e6068230361fd2cac02d6b4d6b0f31f3abe70104f00924a85bebed0b17267990b38c88b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec0095c67a6440edf74db9a1104a31f8b431ece5bcabbe3e508d22fe13670d32875ff6347a88d1388": "0x0000000000000000000000000000000008b4a302cab1fb0489c4211db08beae0bf7b5432416b299f57ce785b037c297a468e2499f22749aef04333796fe92b73c06cf4e358a552604ff3e550725774f924", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec029ee460555e12cb2692080bd814373a7c780dcc62922ba2e770c11a50bb1bb38fd3e69f0192a71": "0x00000000000000000000000000000000041a58ec699c897903d28984e42202dc216e2e8f7023c846926179c0c38562e4e0", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec038a43990a07bb81280a479ee3beca7af1636aca17582f30829782e2c9b1b9c72aaf8060563ab37": "0x0000000000000000000000000000000008b0ec35caca1aab56df7814b80ea0585eae79ec6f58cf03a8be0c40a5c6707711c85cbec6e7576580deab57475b75d3456c379f8c4abb617969fbf99aa4e8c076", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec07bdf560316d32b08ec72cbf62bb66f416f46f988e130585c834a381efdbb0755e30f47a2a0da5a": "0x000000000000000000000000000000000880bb3bb99df51400d9aabd8e0e6b6610d3d4f5512ae4d46e03f20ed14eb0cb3e3ab79a63ae81a8f096efbfb44d958cdd0253e97775b908d73de31a5d9cb00e44", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec17b80d313e3c78cce44b6b392394133943e063102b113e0577108fb9cb3000fe04faec3a3ad3934": "0x00000000000000000000000000000000042e544de1fc9198d35dd459c8ff8bf0a2c86eeaa4ed8ad9b32fb8d016e696ad77", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec19ae875baf61fab3e3a490c516b2a3582e6400e33f3eec42a12589958cbb86c87b23bc94710d21b": "0x00000000000000000000000000000000082c85bbe77e464b97ad4d36a49bc2b84566c38b9bf6eff49aaf005c672fafe7528a4c3a7b3f2b3cb525ce315f859de83c51410fc29ac88734be346709d0798a20", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec1e0ca80539b15c8796b851c8164a129b23282ca4b3bb694364b0bfb504e98b5d2ffc5140d58078e": "0x000000000000000000000000000000000482fdd15d55ddeeda8100a274f8872503fda6e267e345f4edd9fad26229604c0e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec237c8c9d7bd4904bc267fea33668e3515a7c01f4acca67d73d30574b600a404d2b7210aaac85569": "0x000000000000000000000000000000000444ebd2b934606a30469bfd509293c34174336f974709964a18bf40d99a0bed0d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec2a5b45c043e387bd2a0035cec74b2f90f7e72cab1fb16b6ba8317631976b138f7cced3e00668b0b": "0x00000000000000000000000000000000045485545d9937d865c9fe34b5b3723ce78729178a292681d43bcfd35076a656e4", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec3aa97983f6ecffc96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837": "0x00000000000000000000000000000000e4e693f8c8c6043a5d8c8ed64d56523d157625011947a8a79881987d9e9100963ad62a2b80ebcda1b2f14d2a903088759ce56482401fb4130cde32775d6d210a6a00b03b23766d70d0445943b290606521acaefee7660d521950faf2801c79d428d44533a4d21fd9d6f5d57c8cd05c61a6f23f9131cec8ae386b6b437db399ec3d2cc16da9d1f7271475075aa8eb5c6667714426b8c41dbecf92bdedfa462b7163545e8064f8898a29d4811e09b207cf3302e5cefef16615f8580fcd8fa63a624e44ab70adf9b1a6402cf14b3c61f98acf5bccabaf0030d537510166a21ee44d1676c26a1fb9acbdd56be00d4c44901856929b9d2a879caad6119ad0417e9949495ac7f6af5aeb5364188840d02f0e74e813e6d9cc0398d6994b66727658a4fb30ba43c53c2e9bf804aeb0e0f3172195defa009198441a21cec8738d366105e44734e6b8b209b93f5f68b7abba7c3e17a84af77819e98b05a0d828f0b9b2b925788c23324b0cb29e4fd1a68cb08febe58b50e39d8afdb5f752d6c26c8ba52fc00280833b98deaa7ae201c9d2aabba6d7cbdc14ecfe1548c52b8ac4907acb14c863442afde41c8d0cff9680849824712996d0cd96906dba9697aa5110cd6d025e155ae7010248daf19a0b83b3d131f63a693785222293af4354035b8dce851fb02b74eb76dfc265812c8f4f9b98b54e25c319719e1a3593b7f0e743cf85252061789c6a3401d06cef30fdbb33901328f3611dae8253708779a5d66179c967582635f0ef72b01055276dae281f6da1a1dd14c6276142afd3aed8f8b6c94b36d2a717ca67e0639a4112b0f7cdc7a7cb9ca5d55b8fdb35862eaee46c8bd34720783d73b6ac93bc22957dcf5ea1a84b1ff7e60ad872eaa73f3d176ecf7b980cd33b8b0016b94e2d5d12d60c7314cca383bf185ddda83f413da740a121601e3277d3083e3856d90462030b27957c425b33bf8f8f669ab715580e7f64f36999a74cea393632aee225f2714c573eec965a9dd1e1ca399636d9158ce068842f0558f360a4359695d7445e6e6b293f6fcb0aba94ba7b76431447abab31430eb99a63934cdd3c42cbafa9669c25868ae5ef66a62b96169f9d56f9b1dd27aedb241d86bcb2dd68326020faf1443ba7736e9f4b7a442db507e4a2054ec3a19f985d2c44c30b304362ed2bb3fe57066f0c015e18bf443b1d384119ae9a0eafd276064062e73a1b3170119b7481293f5e919dd9f0c078a5b285f9ab78fd05fe73b9b4dedd595b5c38049a9687c22bf19c419cfcc79a77b60b07faa3df2034d7cfa4635350571cbd325c2721c659829b4fd0d827879873fbd8763969b48a7724633b1e3d4c3b3609619819a1a845833b4fbfb1a911e2332fe3abb3e09acdff60b492680c6189629b2b9e2691c29d062502fade8144a0eac25be3369a271b3fdf2208a9d86cfec6f948c28804ee5c2389c20b837475af5118a2576b88baf216e87684eccf9c282d8b2ae44b76584cf228a713f627f36b58ebb282be3a162e6f24d54a15d1082006a20804704197ea07678709bffe4d7cc4a0203e5f92da2f681cc5e5ebd399b83b3949907559ce75b9e6531397c0050be2cdf9d982a2db041a099526baba91b8d254098823139e30d401f7a9422e68208ff3ad2f8e40e92c83af26e4002734760cb87e1aa604e30e2baa8547c93923bf8be8c08efa12efaa6b444e214a3d631ed54c047c426559413ab646ae9152a606fce1486f754412a7e48f81e41786dcee8b78006a7fbe4110d7ebecc8cd2101113549d90cd477c766e02a52856dfb5d9977030f2caa5b3f92b80dc21b417252e93eb55ae8c6e6ea96c7424bc80d00cade786627c0afed691a6eaadcf94bda09fc7b713aca337f9eea5e7a02b1a6aafbb1a44873369e319d0c1e71ffecfe16b614f13253549f2ff0c6db558dafae395ebd5a300c5613fa8d949d17f4bd3ecfd6b7cb550072f4559c4c3f01250379945e44cb323514bca285494baba8c77a19f94848063b287f169a57d42ed48b409eb5554d1d0cf61b8042ce3e6f4c3731471c6a57e4a8ae30a1ffa5870db5043b933dde89af1b2c888948530df2b5b3322b8f999dfbeec62bebb555351839993a10c2412c387eccd841dcfdb3ca84d8e995a2df2660d35f56952ec7a5cc18c8b0e33a71e34965c2975211d5ab22d276be7dc90646aaac121341cd5c033b2ad6e1a43d23d2b43a802bcb2d54cea58ca2d0af13a85daf28ccb873d31de2a277ca03bb185e41a25fc60f58cfada1c6dd1a8ad7625f8a184e6424e37c0ffaa604718981c1454ccf2ba26d8e7561ea8e813da3ee0979be1b5190fcc5d2d1375dd5c2e6b2f417aa8153b6612cc74066b95fb08eb5978781b15169170cce9f5493f6d8f74013ee4e6e4cfaeb0076ed1094419bd13e852c388586de314e8437d9c0e315884c10f1b5520f52057768741d83d391203c6abbce429b05bb6d148239c08fd104e6b65c5312517a2f97c76dd2d0ee3baf2ddf3b0aa1942b47ca79a3d1e2178ac5c1c37a056139703bd4afb52b696047f54d9c991cc8fa8d7a7734f034a008c630c97f95250568", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec562a9eed81aa2dadae2b867564f01654946a095e69f0f49df1eb5c0efce5730cc3d1d83da3f4b09": "0x00000000000000000000000000000000143e1f45bd8fa191f3441574abbe1f4ef3bc6eb07bd224560f30e45909eb0c8e457ad1704a69927d8dde4807b1c67fdaf26718755a742f7994158c8f79749f3a0444e2fc205de7ec0dfc49f2e05c64555cbfc897602413c712c93967b59257e537c880ad522ee62f59ec14aea01214fb737df562d1a9bffb35d70d35bfd2c724325a482942379643a03b8eca91f12ef9ed1baf5437080664727117fd2ae6e7dc56", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec5b2faae0caa0f972c6f57e9289919d242aa985c1963f2b4040ddc57df3682d890657d130c035576": "0x0000000000000000000000000000000004f43e114e579d7503e86bc631c58415cd1cdf864d0fcf512bdd5cfae51aac062b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec78dbbfac53841cd2c0d08e42e58247b57421f3239e0e192b21edaed4bca2458028c981634bdb607": "0x000000000000000000000000000000000803bcad4ae89b033d823dd32ad149177e99e47f1c1edbfe9e5281f585bc406558fb944d7e992958540251fca02926063121bd452d21e0c20fedad450db1e5f713", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ec7b59ed3c46118b33a5e67c5be0b1a151232d17929a6479d7d7187544a40c059664c6315e94c977c": "0x0000000000000000000000000000000004a8fc72d20690562e27bb10f078d2de104192ad2d0cbfb4eec4eef42381f53739", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eca08516f7be365cf1a21174f3333c2bb416072b2c31d8f1e60f8e4ad3cf9546ab47e5b6fd060d303": "0x0000000000000000000000000000000004c203687a31c85ac0dce32725c354a515d8c027681db49edbbda4e3804e5dec63", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ecbd0c872a0daf479a4848ac04c2ad298cfcbf27ade0ddebb2cc3b37b090dd933a1e988b7a3ead075": "0x00000000000000000000000000000000085607fa03519626bba53d0d71b008694a205e566c653b766c9f1c60edee39ec22320145f6b95e9a687429cab758eea7c9cf2375c09ee12b7973e85c7f8476da7e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ecd624b47e3069b393e8faae4c5713c72aea65d52aa1616d3e918dee3819fbbe08cd4c76dbd754a50": "0x00000000000000000000000000000000087ef6c750071ec4ea673adf3a02c82062d042aca83eab159f599434894946423deeeaf12276eba3a08a26a606cf4c7cee9c3c0cf56a781899afa463104cdcf842", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ecee83f43ea0dd41a22b7a9ef681c4a3c1743e5c343ff5b6f8aec3dc43bb0d49e29243d79ed406365": "0x00000000000000000000000000000000108ae437cc2420c617f2cdec05405db6c449bada7d2b2063eadeae636a25c5ca79cd0b6229844999d1cde6c2e74ff90057d24e5875b891f645e2fb4a47ab90745ea100d0eb4c4ad076d10849f54ddfc448b83596f24f71cc49ad4c0390ac489010ad21c2f113facb0631e7145ab7f255c3e46b49904587d6aa6116c56ad615fb1c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ecfa782accd3d75bebe776c10ad0e1fe7f606cfe42448e02cdb640c21214ea6c0c99df36ec0ee3d0d": "0x000000000000000000000000000000000882c35d711666a25aeef2933e8c52be35e409e288120a6555139b0c45d90c140c46351b08b39356e218524151eb16da7d6678c6066a831d6ad9b9ceea25d6df07", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ecfac989e84b5ad935ee1e16ea093ea043d7f67a6f34f440c5fb921b56b54e81c177898d348685b51": "0x0000000000000000000000000000000004189ef65d1a77acbc1492c95ea3a58cb70b9ffff211190368bbfa21198c734c2d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ecfc0cc32c14f6b1b1c2a5f648afea2a94286c17f6c60d16c9ef8511fa4ae88a54ce2748b6c8fa90f": "0x00000000000000000000000000000000048e5df47c25340b48d443389c64abf88909ded6b6dd62c01548840970cc4f14a5", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ed1067d55a33c02831232508adcaf57c6e78a850f9d715e3694b52000ce537832eb55c7a59f859e13": "0x0000000000000000000000000000000004078447732a649b7bf6970bbbbac8df97ce628b139a8407fd7d051f40a0258f93", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ed4f4dfc6da461b20aa72c321b20bbd5b78b14f6fd800017bca47190956eb42ada2a4d8f8a8ca994d": "0x0000000000000000000000000000000008e0ca65cc737d7d170d9f7b09247f9cbc52142c62a6bcb4d8a5bbf29e6cda7e0598bb3be2e039d66d581ffe74cb449689ac74428512b67e033855de5d75b84d08", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ed509c5c59195b1ec5c3739d60301126756a7510e34f9d656d4435cd4fe64bbd001f1f3473bc9c333": "0x00000000000000000000000000000000048413fca2f93b3526794f655fc3504b40e0dff0648a4999f10f8eca7c21d1c21e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ed7906ae903234547463a9cd3e7cec50ccc93515557ab58221d20c67a409583428ad67caff9415107": "0x00000000000000000000000000000000044793061e5121c9561a8dd25c2e64df912f5939a096bde10bf9c5cd06f753f6a7", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ed9cef13374a70e655010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f": "0x0000000000000000000000000000000028da4c4087c6c624a12ce0c066b04f71d81735ed6a252c0f63e55188be16be4f475010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f482a064f119738425180e442727eded171c8091bd90182c3071f37199d9541586e306a119e947513f180b430e3e75bc8c476a8b61e8348d1f87e490121601b5a6a4fe76ff9e27c148bf2c24e2f85fe56b4eea5bbcdc159430c18036e0f940f2782c38ac61bb075ef3c6f745af4fc8675526b69e7a66c05e777e15bf3df99302fb4c24dc7c3bc7ef831df7637143bb554db602d209f58c93c3dc2af04dc3866699442a11a7247822dfa83dd5c27ea7249cf2458e60ebf82ed760f9e6d6f99bc7bc63d29b9669894bd812dffb0a0206bfaddbf728741b3219e9ade9937bfb1874b1a9490a6fa5ca6353651e0384ff371db3aaa0d2fd20e6ba91c8733a17f581602", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ed9e0312b39961bc6726a98c27f84bf42fc842794a925418335fc9fe3badb1f535dce9c9d9efcc02d": "0x0000000000000000000000000000000008b0614de4de8cfbd7c760fba99b446a030b0ddd8f00a0dd84dfc88c7875d80c07eae07a7b7adf896a2332b5bd2e366474eab97e137c155f8741a9c6b4d30db700", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37edc6d83874526905136aff2ede1563784631d6149024982108f661b079b6b79f3d042041a9da11e2a": "0x00000000000000000000000000000000043e9520e4e74fd47da1dd509ef04dc9b14215a35e7d207fdb2c99f732ff5cac16", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37edc7d270f233cb625a6ac5af2b37a6bb6d5c9cbb7fa56748fb1c9cf9ad1ef43334efa76a431aa3d22": "0x0000000000000000000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eddd17007ceeb939fb4d78a8bb35a7b0ff8ae6a7808f9b17c83efd28b8612c2388034d0e35ce51376": "0x0000000000000000000000000000000004981d5e90031eac279276782d0d15ab97fb30898a69abc4eefaa797c572823b2c", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ede4e384e5c5bcda65ac4bbe840e332da2656c0e760904003fa7a2123a216a33e81b8531400600304": "0x000000000000000000000000000000001076729e17ad31469debcb60f3ce3622f79143e442e77b58d6e2195d9ea998680dd00de863a4e1cc8c2b2050ba4be784b313caa1e51167abacf392c1f07270834ea82456d51e83aa6dbf7a473b5776454fa175d5dc9775629aa05bb09b28fe4509b054f58645b2774a05e70ab4f9b59dfa46ed60acd0668713e921324a933e2027", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37edeb3e9bfbd8d3c590cf6b6cdababf69c2af37a41a2f360820bb7dff2f61c20bb61a9503a8901ee20": "0x00000000000000000000000000000000046a6d9f13f448628beeaa0e4c8e47341163ce0eb14c612d882799b9eb4152d71b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ee050fe4b1877288206c7ea7684b6aac6cd63cf88c92b0f05398bc3e13e0b0c5936c3027b8e0c7e2f": "0x0000000000000000000000000000000004c9108836cc5003906b107d31fe5c7b5d1211a4f71bee14f068d2fa04551387b2", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ee18d41e2249b70ec4eb32a5fbbccb1d97d31237172fbfd92945caa6822d5f8afb558aa7b89bc5a11": "0x000000000000000000000000000000000426a81cc7f1e72380949491cb9538d125d40de48e631e0e8bc40964fddee59bcc", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ee26d187c65071fe036da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb389345": "0x00000000000000000000000000000000781600e09e1d8a1324934f83d55d5f6f503e2d91bf4270eeaefd462f24e4487e29e6d97bf878b1012927ae6afb7e092c541a5abc3904656981beaefb9ebb781d1c44a1336854e44cdbfa929ad12e913e4a1870c590a6dc5e3983a6fd416b927f534877511245f8954e48858da743b9eb3544681c27ffd8802c8ea1669e961a2b6148b2cc621a25ed86391676c3686bc2cf76f06edc66a4c3c21e2452618ee1bf4e50ec868243f5ec5af29a7c679163a34978815b6f1d6e2b871f1f361cb7a1f9057c2241b8ad2176aa340dea400bd84fc389091a7511086bbc78fa98a7356e630aa06c4e59af8d86d8b552887762255c830d79b847a6648210ca6b24d0dbba0e2db64c29324eb942fab6b41cc041f0e099f35d5c7fec824bae17717c5fa68cb83e88d74924b788c1f7ec64a54c63eccaddca748f588f67c26e5595870acecd9259520aefaa9aa8f2c237f96957bc1858cce594c62126484c3cef56600e11580a7798a8cd51a12a19dd5440fde5e43cb50f9d48d95ea5c5ee3618eb0b2945f02f21be6c22b41a47d782268a2d1eecf5e623ae6b984591db92f77de07a27a447f87c92070ebf24c4c84a47db97b62d308834c3f258a9d96aafd6bd11eca52bd6ce4b9ed22cfc6877c1961ac2cdbe5536684b0761074b8ea475d0c2f173f5989be90454730499c6c53dd16d1e3f8007b64be019cc9229db22d36a12e44eff1670cf5fb4de0e0553f854c72746045b90c8e5c67d74f5d8a52d4134b259ff562e4b1409bc61f6c2ba5b42da936c12c7e5c33f6cc573988521e5350de5887b20ae9cae4906e11fd0d4df6c4765eb346aac47682cb7871da9ecfd235255f6eadb8392b20dc65de6003709aa5a6b81354c00fb13e281ac05e852cb4194c69f78566e8ac8282eef2aee654d4975535f2701af86ba6d169c2c9a1599b16635a2a5e4640db94d364c29bfbc9f06a42b5cf37ffd831e91c843cc25d8b90071546810ecf279e45838a295559d8977464fd8cdd133f8805f2388e42a6e009219247048a27d9ac06b42efa2e57a813989da4bac4551e4010ee45003fc3f360f5202a958b2b1a29918605fd1308af1ce85bab5ba3fb19b330ab7dac29e01ad501420560f44df7e0e1c9c14dbe4982ae73a084bafe1f7eea2d51f3819088f08a10eb2fd7a1343c5140b8a1ec46479fec3c43eea382d637de8f295ccb2c0b6f6fdd4c5d34a687737a601ae1595f870cc27a34374a6b819a554e242997efeb760433c6fdb4372c2f28204be2daf84705de34c1930370f53566524b08145b4d192d6cdd5a35cc71930e2406cecea2c48271687c926a72814cfccede993dad2b803ec0d546d2bafa586c11d", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ee38e649e5f9f7676b4d599b32c954b0a9e554c96b248f3e66046a82f46ac914fc675938f771f8372": "0x0000000000000000000000000000000004a83a9d38e1fcd76d6e82eaf458c9b71eba96a9e6540f39213fa01366fe0fb64a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ee5018c1728c62231664513c046d4497ba05c19efac47cb0dbe498e20b089dff25aa08d1a77ec970b": "0x000000000000000000000000000000000490cbf37b94fbb758804f3f06510840b6649c0a2cacaaadda6849c9dcb766b767", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ee5a27d3189e99969dad042c036fcd9897945a880458b8e7104b30617a9640eaa22b7e23e675e0a02": "0x0000000000000000000000000000000004c88bc26c3cfb3797c1b2e2c9e7fd0320d9899271ba9255a2408c4feeb33cd425", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ee61549e518cc68adc8a5bf93006b7fd50ffc2abaffd57ef06c67f2171b5097070892fa1a195d920f": "0x0000000000000000000000000000000004f50556e30130b7c23f4ec520332817629963b2a69e5b024aafc29fe7bef6905b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ee7b5d2aa3f9d8295b8e06dc2e6bb6bd269319ace4cf8f663338f8f285a0564d6a139063c985d2310": "0x000000000000000000000000000000001012c8663dbe1a18335ef5b7731a7ba8c0f25c0fcc761623af5159ec6eea586b283a92d9b2a48ee9adca77ffd658b1e273434924a7b3b456693219afbc32279e107c103bb33a2f4f6ff86f2f6e11e9488d6eeaebe52bbf31291fa00e46ec26ad60d84ea2d7257eeb646fb4e0d2aeee35a3eeaea22b897d8ec95f42d5ffd2251430", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eea0e1b22a44d19352c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e": "0x0000000000000000000000000000000091012c2a55b59c3999f441a596b34e41f0d55a88c0f9fb7af4c76241a6f5d3a3e5142c2a55b59a282fb173b7faf6e82984686f6fb83b0293dd498db6329464a45d1c2c2a55b51baa94ff2dc4feae28c5ecccb774c622545b0a127dbd5ae8e2c8e1152c2a55b5b05723585c7421428d9ef451313e33e13803424b0f8cabd383d0df372c2a55b5a0ac2e467c72c205ae9252a4e8a0c206703950f095c31ea3f022493a2c2a55b5a116a4c88aff57e8f2b70ba72dda72dda4b78630e16ad0ca69006f182c2a55b5c69b5e131fb0f65ac7ca707f4bc53e4d991a2d1971ab5e702f69f45c2c2a55b5c7e135ae443c6ed951338d4b32be6cf61ac5bd000b7a11c26b3078202c2a55b5c60139f9572ec1f88f541744ad98ca9cd09b5641e84088ab0ab3920c2c2a55b5a6413894e13836fb0165e6adce7d77c06dccf42b3b288397a27ddd3b2c2a55b6068e3b8626608321044a89b82fc4898ece34524659f48aa72aef556c2c2a55b5ffdca266bd0207df97565b03255f70783ca1a349be5ed9f44589c3602c2a55b5eabf99708c2a9f1e21a3bed0fa589b18286225ceb1bc9e28ff06a04f2c2a55b60a32545c89a8d2ba5d04cde08adeb0e91bc2d0cd909d166db15e0f7c2c2a55b6094cf557bc85fa401951b4d4844146211f845e1c212640685d42057b2c2a55b5feb0eb28fc697566384dab127170f08c1c2a6febdfb4cb3185ef89172c2a55b5c4fa92b88356b2666fdbf58840b50813143d93c886f60a39b8eb204c2c2a55b5a609baff13899d4ba4bafec105038d66a716494968fae1a849d2dd5a2c2a55b5b7e13a772e0b693c3b351d2fb5e5b4da18ac379ebdb2f1f2e75597762c2a55b5b86d232309cc2d8aedf4c647656fc6a40e2cdc299020174154a7fc422c2a55b5baad165d64eace5f75642ac4612ec1ef12bbc1cb27a01374737882572c2a55b5cf8d00511ef54ac7a60773810e906befb1b322f2d58199963dc973072c2a55b5c7a1c48e176aaeaff69a017d85b27a49f1fafc160725a3376f417d002c2a55b5e3110687784ba7063753757ba409052b2989ab9def09a684e8aefd6d2c2a55b5f64fedb5e9a535e4358cf539e294ccdddc753610fbe8c25ed72f94092c2a55b5de12066d4ae584016c6d556d36f74984dc2fb33b372e568e404c09672c2a55b59ed1954e80bf1349ee5882d429d261cf9962bc5d88a1fc176e60c9182c2a55b59f26d98880fb604cdd7848e4c1576a990b0ba8468dd4222aa27f9e222c2a55b5d10904ce8daf9095a573253bd66fec7e96557836f664a49e5b89553a2c2a55b5d7c788d0d4bd1f7cab8c7e7a6d131e3ec396056d62d1376d211779412c2a55b5d133442ac56db7e5ebfe739897a98a37c258accbe3697d0d7f29a3732c2a55b5b35dc5768afba71d0baf2377ee6f8e235c27d04ed90d75e0fff806252c2a55b5f917f0ac6055619baeff392603206df4007063317d30f823537ef2342c2a55b615f9b64ac6a84fc4513c431a7b6e0bd3812a4821392a462d049aaa3a2c2a55b5a6392305c60fbcfcdf77a4041507f2bd1976c05dc149d2239d1582212c2a55b5f1b5e1c45738e2352b3d00673a40f451add21cf8e6a0c3eae6bb68242c2a55b572dd8f780415f21ee812b5bb32d6baa791ce4c8fdb04a13eb21d3f092c2a55b528d6e2e0e0b7d4538eb005a63202ab1bb9fd8e23cde888bc573f67142c2a55b514ea318202f6b920073d878c2c9c94fb38a48e4c7b3d4874f755c06e2c2a55b5a3e5f8bb0fd6155ce85668d8e606d7defec7044d8615225bb9769a6e2c2a55b5b1da68fd2ca56d83bafdbee8ed436709542031e625723d4cb0a01b312c2a55b50279b01fc376a1b5e4e6670a8c247f00ca6c1569a9fd92ae18fd10312c2a55b5bb604a28edf5e8b24c72f0c851a6440c1889346d8ccfcd298b601e252c2a55b5af6d74281de0813a60beb2a89ef39c6b2a10a35427c683d61e98f1702c2a55b5285b5050bfaf7c602b3748dfcb3306896ae8429c2c0ad8104c5034012c2a55b558917616097dfee7c9142497c66c77b194cd9113d0e65a11ba27660b2c2a55b4f7af0b8559bafd9332fe78fef55b0979b53217b3f8355382ba9890312c2a55b50302080081e20a336b40dac9b5ae2ae80692b653c2b38131047d797a2c2a55b5e347cbea568da1dc296c13b681ee4b655108e78bb9c33980d45e25002c2a55b53f73b4d26ce6c62b0082194560e4a186f3491e8347218c775dca48302c2a55b5629e69cf9c08b4b926e7e8503cf4d337fc62677f40409a68fe9b9a1a2c2a55b52656178628dda04be1e16aab15775638cfd3bac25a59164631a06b1b2c2a55b4f956b33fc6d43013d320b87c2eddba31b9ded535099734d6f3aba80e2c2a55b56da37e71236c50ab586ba5ba55e3479c375ff4b9246082b5d21b4b7b2c2a55b573dcfe35afeb7bffb31da83e9df9ce87ca0ed20d9751b2e8239633422c2a55b53b42cea8ef1a2b6f7f66895b8904a4764bcc5da3d23f941953076c482c2a55b5451bb1d573ede48bcf7ebf76950aeb780120ecdf3d328467d62fd9292c2a55b510651d026a38ce4568ab7ce806bb2983d1846191cfd29b6b06e109462c2a55b4fc257dda3887031ea29fbe2a8ea74764db0f3ff3ee746b58365ef3252c2a55b508174009ea498f934dfefc1a023e9d03b43bc601a2f7fa08441261602c2a55b54dba6c93fda08f30f8fd6be0e47bf66f3fd31e2e1d72b970b3ff11542c2a55b548ad46013efd1c045ec47aa4ef52b075a2fa3de11c486dee807c890f2c2a55b4fa84e9b7e85c6a3bb37f4bb3831bb51a5bbb1666ff92c8b874dcb42e2c2a55b56936ce0a4d5292c66857a725cd7b30a6d305922f4631380666e5ff2e2c2a55b56f24b35dda6da7aef24974dd6501d9c857f8d41d66cf88d5bd0530442c2a55b517d1c1a30c70133ca54050b2d409c86e87e4ba23b564c185f1d059782c2a55b55169b48ba48e99dabb61807463c1f91bce2ec9919c16f0fdf36161662c2a55b52b81262e103c0657041660ac9f500aefa757a2c8343326d0a525f5122c2a55b50d3c4b8b607d14c312d6e25fbe0a9a585bafd68f597ad37989f9c4062c2a55b50699f7db6f499718d572de81b0d1843c146624daa99edd9a6609ef642c2a55b58f78423fa34f63da52ccb699e46952f94053123661ab8316d4f78e072c2a55b5364338fadf45981d173acd75651f8ef41b57931694c3870cbcc548702c2a55b58a05d949b1ebe3d18f1d3f4d43ff93cd8005b9cfdd884a901761ea3b2c2a55b5e0241fe92a75c23c49cc15b355a4f52c934719b1b7382c5c2b859a462c2a55b527a77c6ce5976953b4b0bfd3f7db8a96d27fae160f24a0a6759cd4662c2a55b52aad5c3cfb8bf4c7c478aa2b21b8fd9597fe2706a11457d23e10323e2c2a55b5079b0d798a442d87e4bf664a01d50c11af9ca230ee4738b19aa95b6d2c2a55b554384ce603e7018c1bd0df5cf1a8b1f6154a761132c003486a723e272c2a55b582572ea3a02d92d1f303959ed8d301f20cf7377795bba4e77ca5f0592c2a55b592b0caf0f0440b89aa8c1d713be7b3a16d79b3c56eed19d7acdb6e082c2a55b536cb66c8bf2894af968ada12c6e7db4bb028a57b22473334404584692c2a55b55a05a6a02b7b3ba6539f3744bed7a3ebf4a86a75986e404553bf06352c2a55b585dd3e7bf5cc92d3eea96cd13018755382f0ba1be21ec1866b0432122c2a55b4f853af67dd7217983b8928934bedd57f2aa6717b4570ff9cf8c55d332c2a55b50e45380d2db51edfa07ff46d01bbc34f908f7aa6b470c1593576ac352c2a55b595b09d23cb9ea051e7655f90215123b6cf4edc6bbfafc3a99e3667082c2a55b545afbf04bb687cbfe5f7a4fee66f0f0e9036072dc3ef88be6f3c6c362c2a55b505d8d997b80d747395c1471ad1e1e8aa57319c5727c6f178bde2422b2c2a55b57ef9ffffa47f4d6fc6750f15ae53863a1d0fd99d8386a70054d55e4b2c2a55b5976eb2e7893f7c3f875b3b1cf3d653df30a0a58125d9fe0f8b87b8322c2a55b5e4935526d6400d729ce52f5065327b414a971c69fcb85eef3d5a94012c2a55b5d60f8f51745d9b979ea7dddb0f79692d51cca14c4791e72da59ac9632c2a55b59871ad2237ac147d19b7ebbbda39957b7d5881df0e5c8493925567762c2a55b5f19e53bd8bb08c1cf5d540f88c25b52a10c7c5816bf906b0ce20877c2c2a55b572c2850205aae1bb28cb0f6fe1c0e606353f9dc420b3796317bac4542c2a55b5f323b09d3dc6c092ed0e988463188613232235e6d6cdd030858855292c2a55bd3b0028726bf7764e3a604421e98e930f851a2bfafdf5d29eb694d2752c2a55b9020732ceb7db3ed3ad35a2ada31a65bbe0ac94ee072d92cd27a38f7b2c2a55b5e1d0f2cabfbdca681bac43c1f684748d18c03b0e04e28b2fae9cd7042c2a55b5e1efa4babefcdae07c4ce9c4682b1f57b0f3080ff2ead5ea06624b6a", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eeec11b5fca7cddef5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x0000000000000000000000000000000010e417170e6d77f90f6d7b308bddb8a414f44a87623704da628229ba777b6446473ac9ec7da0888dc010574b11d9d0dfd62446ca38a63819937aa91f40f6901d585009e192ec169788c9c1f0202fe7c2bc79405ff8b6e1d1ac78fd6152006e606dd091cf86d04141b1c17c70826c08d074cae1b00d6f82de1b8a5406ea10ce723b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ef1c80fdef53d785909cb20eb25e42c2fa106d75ee4d44b9c2bac20323a540529e9f4f40da5337bb3": "0x00000000000000000000000000000000105c5c5a4a025f3d8be84fa9ea36383c2cd168c8a018d50c88a34154afe9ecf04f09b072ba1658a3946b1f7a82a7c135c33a41ac8e6ac11407d910d4baad3e6c421aec17785fa10a655d77b8850a0b7c5ac3ce5c0389555ec7d8f303092552cea70aa18c1ff67dccbb98cbb86ea9808b63dc72582c602fb0dcd7e6716dd9ed9c75", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ef3a8cb14bfca5d3d82c5c0e26848d49b32a7ae09a67f6822f2a18310b69a3ed9c31ee02144020826": "0x000000000000000000000000000000000430e9bd57ca72b2de9928df65ac7974ccc0fe678a64c2df03e2159aebb4e8a525", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ef3e3959f84b063bcd6c29a7c39cee45b0e045a94081bc188ef73be2be086d66aefd850fc7eeacc45": "0x00000000000000000000000000000000044e1f1d2881471357ea697093e5e68d46712d2b0e5b650945c4ecb571ea43757b", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ef5d0ba7a9e0e043b545b7958451cd22afb0367d6c99af1360190813571c47b8a94a055d575d38249": "0x000000000000000000000000000000000404db71328c96f1654885de2d1f577621c524e4515416b0f861dfce55a3f72167", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ef78baea38e12315a6c7fdb8b8eaad1af9faaf918493606e1a3e8c20f9d852773ab5ebfbb93bd1948": "0x0000000000000000000000000000000004084361c7e80bd43ac0aa2917200339e51e45284349a264184ba0befe3e2cfd52", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ef7f8fa46e9ed81a89ce3de1cd55ad6f4ab351ab212431d94cec798e1727176cca174fa661c8f636e": "0x0000000000000000000000000000000004f54df5a1ecdcbdc84dee4ff7821578632b9d6d884b9bfa5c52f7164c57d3fdee", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37ef98bc77ab23b03eed401f460e0251ed41d7fb32ca463b5233b620cb9569eef5327def27fbd7c7b57": "0x000000000000000000000000000000000438f4dd8e0bd0a47c6263b31add7a887956da435cceec4750540743f0235c003e", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37efa4279b211bd820ab22c2075548019dd268e74f3aa69c9703b129e989d230f935797975d5ed9247d": "0x0000000000000000000000000000000004e4afcec3862a48c276628c97bec157c563a74bd8ab3c7abc9d7fae99fff38268", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37efad7d2ce54667f8c1ea80b0dde0e207c8ec57ac05fbec636502cb216bb423642919679fe8f074051": "0x0000000000000000000000000000000004b0583167a388bf2ec1c4ce53d17be27a15b23abdd97265571a5c65bdeabd24a0", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37efb0bc5a64f81e0ca7cbb0ed8bf228935241774290753bf282020d73e45f6724b0196c97b3bd53462": "0x0000000000000000000000000000000004c215be73d91712a74db57cf18209ec172e9a3215ce6ef5cf5b0292177d3e1140", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37efc06d1b8b9ab844be64bc946c10a1f75e683f236e72a44d6d61b3bdcf72ffd8c738e488efc6e1567": "0x00000000000000000000000000000000081a7eb7be90a60ea3c4b467df2f8fd89288ef44f581426bd98cc34a0b67bb959f00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37efc6a42b3b802fc9518d54ea25ad26acd7094fba6bdb278e769f9ac350cc36b2f631da13fa92bed79": "0x0000000000000000000000000000000004149ccce9d526a65ba54fccc24f5d1dee62f9b87915d4004eb932959d468a9e62", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37efd9785f8772870809eee1e58c17ff7b037f2da5766e9bc78d5568c58d45cf363b9630ef32b2ccd79": "0x0000000000000000000000000000000004b023d129d9a0cb9490d097dbd3ca947d4830d3a6d7e0fa9975ff2789d9d97352", + "0x2aeddc77fe58c98d50bd37f1b90840f96ee5a0b09e7e9a96219dd66f0f74c37eff643fe3320aabbafcba1bf303b55be82bf6e48504909dfb85be27bcbbb2a330e9f4fc1261d8f02a": "0x00000000000000000000000000000000107cbfaaf0fedba11f23780e8b1bfcac5e92a85f56835027e83bd203bab86ccb0798d7bbcdd3c7fe6e9bf7de42bc97968143fb02ce4c7f2382552237dd1398255920df777c881c5f4eed3f1c75e29c65fa681a63dc612cfabc5217f4308924e62fd66dba2833b102712151bbcfe1286506937ed28809693e730b622b3adb93e36e", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471400123d83b036c82d5c5062779d44ea2ab0469e155b8cf3e004fce71b3b3d38263cd9fa9478f12f28": "0x04010000000200000000000000000000000000000000054a61636f1644616e69656c204a61636f627573204772656566661a68747470733a2f2f6769746875622e636f6d2f6a61636f677219406a61636f67723a6d61747269782e7061726974792e696f00000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714004b6afd076a2dc82e33c5e14a53e874caa8e7c6d30bd20f6c51cda7dafaad1c465ca004fe61a63e": "0x00000000000000000000000000000000000976616c656e74756e0000154076616c656e74756e3a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140069066ec36235d3c278f83039076e64ffb94cd1f3333887f8c91d99a1b979db5138c60533776b06": "0x040300000002000000000000000000000000000000000c63756a6f72616d6972657a0000001a666162696f40746865626c61636b646f75676c61732e636f6d00000c63756a6f72616d6972657a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714008f3c4dd5a156f727dad3917fef51edd3bf9c675241fcb86d296a690fc3d909a1195758b831cdf6": "0x040000000002000000000000000000000000000000000c646f746265726b656c65790000000000000d40646f746265726b656c6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714009c7552e843cd3a5ac75bc572e195144b05ded3548b218377034460f9d7506f4a36b57b837d9974": "0x000000000000000000000000000000000014506f6c6b61646f74204053585357203230323314506f6c6b61646f7420405358535720323032331968747470733a2f2f776562332e666f756e646174696f6e2f001761646d696e40776562332e666f756e646174696f6e20000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471400d8c318b5f5bd3a609438d7aa30cbded1219230de200a98951d9ecf005606eb8415f386148a4279": "0x000000000000000000000000000000000007426f6764616e194768656f7267686520426f6764616e20546f6d6f6961676100001a6768656f72676865626f6764616e74407961686f6f2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140138f06b2816b46fecd06f79f6e28d47c2bcfca46173aa3a12d950f5637a8d65bda37e283c983e5d": "0x0403000000020000000000000000000000000000000005526973680852697368616e7400001672697368616e747374657240676d61696c2e636f6d000011406f6666696369616c6c795f72697368000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471401443af96cf425c84c48963fc0f3c072e7a3eae4c1014e6bf7333cb7513e7bd0c2042fe86784812e": "0x040100000002000000000000000000000000000000000c41574f524b45522d30303200001a40636f6f6c6c696e656d655f676d3a6d61747269782e6f726715636f6f6c6c696e656d6540676d61696c2e636f6d0000094062696a69617969000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714014c4a8a6cb20d5f04740a3f003b7aa86a3d40f9b65c2d650035580220c2525a19f3b251c0956e40": "0x040300000002000000000000000000000000000000000d616e616d617269655f636f6d0000001c616e616d6172696a612e6265676f6e6a6140676d61696c2e636f6d00000d616e616d617269655f636f6d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714015b6bef29d45538bae335c017512a43fcaed23efec97d80e088bb8f7b93ee837cba5416ca51037f": "0x0400000000020000000000000000000000000000000014f09f909d2043525950544f424545532e58595a0000164063727970746f6265653a6d61747269782e6f72671e63727970746f2e6265657a7761784070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140167d7d8c6597960f482d165f7921558a0bb9b28f66ff64161ba6576f1b854fe4fcf825c00860407": "0x000000000000000000000000000000000009416c65782d646f7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140173de88215548e9f65f3cade8f68e8f34c6266b0d37e58a754059ca96816e964f98e17c79505073": "0x040000000002000000000000000000000000000000000b4f4c4956455220e29aa100001d406f6c697665722e74616c652d79617a64693a7061726974792e696f126f6c697665724074617374792e6c696d6f0000000767677770657a0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714018d717cb9219f862cb783d5c0ddcccd2608c83d43ee6fc19320408c24764c2f8ac164b27beaee37": "0x04000000000200000000000000000000000000000000056b6174611041647269616e20436174616e67697500124061647269616e3a7061726974792e696f1a61647269616e2e636174616e67697540676d61696c2e636f6d0000000a61636174616e6769750000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140191249b095df776704f6c600c82c4c1048f35c857e92484df9315a7e0bc298c405f808676869a7b": "0x04030000000200000000000000000000000000000000084b434d574d50520f4b6174686572696e65204d6161730000106b634077616368736d616e2e636f6d000009406d6161735f6b63000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471401e66931ebe7cc802a82fb6c3dd0269f6977b022fc3abfa2f1ed9783de2d7f26672b7eebf4fa783e": "0x0000000000000000000000000000000000057072657300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140200a531a02ceab0f2f2ee56200c11091ffd9e394076723bd1e948832da89bcd746eb2e3868e8969": "0x000000000000000000000000000000000010e381a8e38282e381a1e38283e3829310546f6d6f6b6f204e616b61676177610000136d6a68707231353640676d61696c2e636f6d00000d40746f6d6f746f6d6f43454f0009746f6d6f746f6d6f00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140250733e6e15b29ba4385c957aa511e5628d26e48cfaf2e46d3313011823a9272ed754019a67207f": "0x00000000000000000000000000000000000d45726d616c204b616c6563690000184065726d616c6b616c6563693a6d61747269782e6f72670000000d404b616c65636945726d616c0c65726d616c6b616c6563690000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140251fe16ba336a2a1eb4f7030a34dc1c72a29f00bb174eae301af0ec88fb34557c084cac25da977f": "0x000000000000000000000000000000000008486169204c616d0b567520486169204c616d1c68747470733a2f2f6769746875622e636f6d2f56554841494c414d00146c616d76683238313240676d61696c2e636f6d00000d4048694c6d56373134373932000b6861696c616d3737333500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140264d5f165d70fd8604189f1ea74bc439b18060c58f352db2880dd4c835df7ebb26e020bf11e7969": "0x040100000002000000000000000000000000000000001b436972636c65496e7465726e657446696e616e6369616c4c4c431e436972636c6520496e7465726e65742046696e616e6369616c204c4c431b68747470733a2f2f7777772e636972636c652e636f6d2f656e2f002174726561737572792b706f6c6b617373656d626c7940636972636c652e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714026cd546f7bcbf83ecfa264fd1da282eeb7b06b8c7fd5634e9f5da8eb1163c79b789d943c310ed25": "0x040000000002000000000000000000000000000000000c53757065724475706f6e740000184073757065726475706f6e743a6d61747269782e6f72671774686f6d617340626966726f73742e66696e616e63650000104054686f6d6173525f537570447570000e73757065726475706f6e74343400", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471402c2da93a5b6fce008754abb6afba51a2f74f0b97bbcdb383f579a02a5e4541fee736710af562c6c": "0x04000000000300000000000000000000000000000000075061726974791d50617269747920546563686e6f6c6f676965732028554b29204c74641368747470733a2f2f7061726974792e696f2f000f696e666f407061726974792e696f00000c4070617269747974656368000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471402d1c4ec984b618c46807e1b5ba42007b46e58f24fae00ade4115e1b15680337f99137a5333e0e43": "0x040000000002000000000000000000000000000000001e54686520426164676572204c61622056616c696461746f727320436f2e00000016696e666f407468656261646765726c61622e636f6d00000c406c61625f626164676572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471402df35b98c238fb36c3722bb683f6247d01da39cdc1055f07d24c1c0b5aca199ef3b0ecbfc11f414": "0x08000000000202000000020000000000000000000000000000000021f09f8cb453494c49434f4e2042454143482056414c49444154494f4ef09f8cb400001a4073625f76616c69646174696f6e3a6d61747269782e6f72671d73625f76616c69646174696f6e4070726f746f6e6d61696c2e636f6d00000e40534256616c69646174696f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471402e989611aec18925220f15324663f1bb0bad6212a344c7f4deb40aa6e3f489a56db71d545507e19": "0x0400000000020000000000000000000000000000000008696c347231343100001440696c34723134313a6d61747269782e6f726712696c347231343140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471402fbd3dd4be7ed351c90e3dabd3fd0f6bc648045018f78fcee8fe24122c22d8d2a14e9905073d10f": "0x040000000002000000000000000000000000000000000b6b69616e656e69676d610d4b69616e205061696d616e691668747470733a2f2f6b69616e656e69676d612e6e6c16406b69616e656e69676d613a7061726974792e696f0f6b69616e407061726974792e696f00000c406b69616e656e69676d610b6b69616e656e69676d610000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714030d3f604ca958c6a47c8ebe79ceebec1a2e5dbb3b631e5b80cfe39ac103b3fce2d1a9c8579f510e": "0x040300000002000000000000000000000000000000000a56697274756e6541421256697274756e6520414220285075626c2900001268656c6c6f4076697274756e652e636f6d00000a56697274756e654142000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714031326492a87a9c14631954522a90e4361e6b9bb3876cf6213bac251f99d456ed4703b47f289b95e": "0x08000000000201000000020000000000000000000000000000000008446f746361737400000019646f7473616d61706f646361737440676d61696c2e636f6d00000a40446f74636173745f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140321b5acf1a4323638a88c73e729a0e8a2161f95c844392baa81dc70c8e8d39f76efb0342971591a": "0x0403000000020000000000000000000000000000000007426561636f6e0000001368694077616c6c6574626561636f6e2e696f00000d57616c6c6574426561636f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714032271bc6af67408e20cb98236c8a2d833d8fb6544ce4f5809b14d5e7f5f9ad742f975f787953f61": "0x0400000000020000000000000000000000000000000011f09f918b203739616e766920f09f8d80000013403739616e76693a6d61747269782e6f7267133739616e6476696b40676d61696c2e636f6d000008403739616e7669000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714032bb7554ff00e0c868f95d29b6e5290c6492e6540c3f46d900961f7c8d063dcd299cfcd97462b22": "0x0000000000000000000000000000000000194b494c54202d20506f6c6b61646f7420416c6c69616e63651968747470733a2f2f77336e2e69642f6b696c745f696e676f1068747470733a2f2f6b696c742e696f000000000e404b696c7470726f746f636f6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714033093647d016eaf3860c2799edfaca5866dd49f38c6180806224b1b893e77959a3cc2d4ca1602cf": "0x04000000000200000000000000000000000000000000106b6e696768746f666d616c746131330000001a677569646f2e6d616c61626f63636140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471403456c855e9a00037e878e54c1374b30df335d3a193f3e6b1f84db6c2270ee634cec769d7e33e244": "0x0401000000020000000000000000000000000000000019434f534d49432d474c4f42414c2d434f4e54524f4c4c45521c436f736d696320476c6f62616c204e6574776f726b732c204c4c431668747470733a2f2f636f736d69632e676c6f62616c1840636f736d69635f746f6e793a6d61747269782e6f726717706f6c6b61646f7440636f736d69632e676c6f62616c00000e40636f736d6963676c6f62616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140383e568f18fc74ec66041adc9730fd89001350628c4d92ce2cc734b487604cd9786622591106872": "0x04010000000200000000000000000000000000000000195175696e656e63652f4d6f6465726e46756c6c737461636b135175696e656e6365205074652e204c74642e1d68747470733a2f2f6d6f6465726e66756c6c737461636b2e636f6d2f001c636f6e74616374406d6f6465726e66756c6c737461636b2e636f6d000011406d6f6465726e66756c6c737461636b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140390ba3a7443c05e4eef513781a2a7c94110928e39de53dbe50f9adc5009de3db0e2acff37f0c756": "0x00000000000000000000000000000000001157656233476f2d4d756c74692d736967001368747470733a2f2f77656233676f2e78797a0000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140396382deadaa8ee485d0b693f66f1785f35ba1010d7e7528a0116ec424454f58c6e500c3c66a520": "0x0000000000000000000000000000000000144d6574617363686f6f6c204f6666696369616c0e466174696d612052697a77616e1668747470733a2f2f6d6574617363686f6f6c2e736f0015666174696d61406d6574617363686f6f6c2e736f00000e4030786d6574617363686f6f6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714039ea54cb812a324847194325a12bc4f4faacb6aef973eda31658195c26b934318b960aa69050819": "0x000000000000000000000000000000000009646f746875622d3309646f746875622d33000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471403fed7fe7c184ba490bd3d091b8837f2f41c38b6e3bebd28a31ee280f82d15e687f95d798ef41c17": "0x0400000000020000000000000000000000000000000008457a696f5265640000000000000940457a696f526564000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714040dea48ab93d760f034ddb0d301f22e5084439a96ec38e7636544ac137d0514e018fde67f2b03dd": "0x0000000000000000000000000000000000074c454447455200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714040ff78a24cd58c71ebd2c29909eb603331b960308a070b839ee78e80fe12ef05e4639a176ab743e": "0x04010000000200000000000000000000000000000000074c697374656e074c697374656e1268747470733a2f2f6c697374656e2e696f16406c697374656e5f696f3a6d61747269782e6f72671173696c766572406c697374656e2e696f000010404c697374656e3136373831393338000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471404322a3587052539a23809a947f2c06542cbf2fafe17fd7d84c460d51fdd69d01f53a5ca050ee709": "0x040000000002000000000000000000000000000000000b56656761735f6c696665001668747470733a2f2f76656761736c6966652e696f2f1440636372697330323a6d61747269782e6f72671876656761736c6966656d61696e40676d61696c2e636f6d0000094063637269736c76000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140451688eb2d3683ce8b53683b6ce7ef4d1c7bf913580bde6ea10b6fdc790fac42b77955c740d1301": "0x0400000000020000000000000000000000000000000013506f6c6b61646f74204e6f7720496e64696113506f6c6b61646f74204e6f7720496e6469611d68747470733a2f2f506f6c6b61646f746e6f77496e6469612e636f6d000000001040506f6c6b61646f744e6f77496e64000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714045d4b9be19df388bc6c08ceb638f3fc5a0fecdfd83e909fd7ae1721794f2925ca0f5094932c3769": "0x000000000000000000000000000000000008616c6c6f6368690a416c6920416e776172000012616c6c6f63686940676d61696c2e636f6d00000008616c6c6f6368690000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471404632aa45f1b73c60a6a339260fce0551ac29fd893232409f6f1182daab8e1021b3861e6a5101332": "0x00000000000000000000000000000000000a6b65697461303932380de6ada6e4ba95e5a58ee5a4aa000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471404d5e25d603189573898b6f62b50749101446132f77fa6dd77a3895674fa8dac87e6c375ea852346": "0x00000000000000000000000000000000000b4441524b464f5245535400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140501142b6fdb56404e7edd090cdd7a52eb0e50ca77b86a41835bb60755a109e421252fcb6a373624": "0x0000000000000000000000000000000000064b43435f3100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714055dfc6d34f916957640aa86730cd7fbff361db495405a4affecfeeb6581370b07ff5abb023cab06": "0x040300000002000000000000000000000000000000000a506f7765724c61627300001640706f7765726c6162733a6d61747269782e6f7267146d696e7a756b76696b40676d61696c2e636f6d00000940564d696e7a756b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471405b079eb94353bd94adf51a47b72795366d52285e329229c836ea7bbfe139dbe8fa0700c4f86fc56": "0x040100000002000000000000000000000000000000000e536861776e2054616272697a690e536861776e2054616272697a691968747470733a2f2f736861776e74616272697a692e636f6d1940736861776e74616272697a693a6d61747269782e6f726717736861776e74616272697a6940676d61696c2e636f6d00000e40736861776e74616272697a690d736861776e74616272697a690000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471405d79e4283e265f4f422f57b2b12e96bfc2f8e8416bd768da1c4526f803622f7ef7536d4db29e970": "0x000000000000000000000000000000000007686f72696d6900000012686f72696d6137406e617665722e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471405d8bd6067f6ae29c804531378242158a794bd06c1e9ef0e569f60bb32758597af80e5e70c07a64f": "0x040000000002000000000000000000000000000000000e43687269732d5374616b696e67001a68747470733a2f2f63687269732d7374616b696e672e636f6d1240636c616e673a6d61747269782e6f72671863687269734063687269732d7374616b696e672e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471405e989f2a5137a8f22e8d22a7fe2768c944dad8af7829d96eac8644f06643ea8ae68bc3c1e905306": "0x04000000000200000000000000000000000000000000066b6f757469000012406b6f7574693a6d61747269782e6f72670e6b6f757469406a6b76632e6465000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471405fa9b980f99404c1e8cc9421ed7548a68def07a5c4c6d205cd3b84892c4b58c0e35badbae992d6a": "0x00000000000000000000000000000000000a504f4c4b415741444508474d20436f6e671368747470733a2f2f676d636f6e672e636f6d00156576613036313230313740676d61696c2e636f6d00000d40785f636f6e675f77616465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714065d9c0c5a79e8cef73baa66d4746e8447877fe051d6dffa85811dcd14c6dceeb29e011b1514f23e": "0x0800000000020100000002000000000000000000000000000000001654484520534556454e544820434f4e54494e454e54000000113335393637353237344071712e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140666dae771739caea0f7cb4fc2c83488aa67396520178b74e13298b0cc3d89b5e7fcc0be290b406b": "0x000000000000000000000000000000000007436c6f76657207436c6f7665721768747470733a2f2f636c6f7665722e66696e616e63650014696e666f40636c6f7665722e66696e616e636500001040636c6f7665725f66696e616e6365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471406829105dfef3f40a6b0e06e21e9c82af02f3aebb597d0fb4cf6470c1de0278f7a26a301680fca6e": "0x0400000000020000000000000000000000000000000009436861696e326d650000001872697461636861696e746f6d6540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714068d2dd8a39691885e09d262efefa86090446d0cc0fd6cab48b79b667e499beba1e72a0ddf16e523": "0x040000000002000000000000000000000000000000000b4161726f6e323436303100001b40696e6672617374727563747572653a6d61747269782e6f7267186161726f6e40696e6672617374727563747572652e636f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471406a3b4543b0cbdd8b609cda244e45076c572478b1b3b39636a96b7626be093cf39a222e0073cd017": "0x000000000000000000000000000000000005766976730f56697669616e612053696c657373000000000009407673696c65737300087673696c65737300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471406d7541597a81f1b6811dfa0e269a620b63ccbdb616e8e12619d504cb470324ab3ff1aeeeccbff59": "0x0403000000020000000000000000000000000000000008426974446173680000001a6d65726365646573736f72656c313040676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471407063321357d092326e1f0717f99bfa5eb959816dd1bd8b79b686ca351fe16c1dfc929d55e49f247": "0x00000000000000000000000000000000000a48756f6269506f6f6c0a48756f6269506f6f6c1468747470733a2f2f7777772e6870742e636f6d001468756f6269706f6f6c4068756f62692e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714071427015841edc7844b0f0b00cfcb99ce376e5f836bac3cd801af422979f32a4bf2d32ac3e5483c": "0x040100000002000000000000000000000000000000001c43696d20546f70616c207c206d697373696e672d6c696e6b2e696f1c43696d20546f70616c207c206d697373696e672d6c696e6b2e696f1968747470733a2f2f6d697373696e672d6c696e6b2e696f2f00136869406d697373696e672d6c696e6b2e696f000011406d697373696e675f6c696e6b5f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471407821119981623015ef2b64a77e076b7f284c1e8c2afa826bf2ada04e58b757e2f9dfb816a250a66": "0x040300000002000000000000000000000000000000000e4a696e73652046696e616e63650000001468616f797565406a696e73652e636f6d2e636e00000e404a696e736546696e616e6365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714078c7addc0fece8f18a5d639662da95bb4924c48441fb432047e77613263cf1438ce8a14fc34c432": "0x040000000002000000000000000000000000000000000d554e4956455253414c444f540000001d696e666f40756e6976657273616c646f742e666f756e646174696f6e000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471407b7c5e9987b5a061650c532ed1a8641e8922aa24ade0ff411d03edd9ed1c6b7fe42f1a801cee37c": "0x040000000002000000000000000000000000000000000d446f6b69614361706974616c001c68747470733a2f2f7374616b696e672e646f6b69612e636c6f75641440617772656c6c6c3a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471407badfa66414d7abc4e7d5a63d8e887932bb6dc505dd204005c3ecfb6de5f1f0d3ac0a308b2b2915": "0x040300000002000000000000000000000000000000001b436572657320426c6f636b636861696e20536f6c7574696f6e731b436572657320426c6f636b636861696e20536f6c7574696f6e730000216f6666696365406365726573626c6f636b636861696e2e736f6c7574696f6e730000106365726573626c6f636b636861696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471407d711054306c2851292a778cf74e0573db07ff3ff3738a4f0c44ffa0f2226e821c4dc3eb7d21300": "0x0400000000020000000000000000000000000000000018f09faa9e612073206820f09fa799e2808de29982efb88f00001c40626c6f636b636861696e637572696f3a6d61747269782e6f72670b314039373130342e646500001140626c6f636b636861696e637572696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471407dad820c63897d8924bccc2bf4d18656da14ad467c161370e43c2bc7ebc83d7f1e6ba4b6acd1010": "0x00000000000000000000000000000000000965746865726e616c00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471407eaf323b98489af7febf816c3fee7de2f843afd83a03e4b7138c81f655ad20e31c27ec35bdbcd23": "0x0000000000000000000000000000000000104b6f746f207820466172626b696e64001f68747470733a2f2f7777772e70617472696b2d687565626e65722e636f6d001968656c6c6f4070617472696b2d687565626e65722e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140802b46abd9ac8a9386a4f5a0311a2834e28c84daa299fe14414137807e201a1941e502c7a784467": "0x040300000002000000000000000000000000000000000a646861726a65657a790944616d696c617265000014646861726a65657a7940676d61696c2e636f6d00000a646861726a65657a79000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140803dc9d292420d2565c14e67b353eca54a9736c1fb110f72a753c3289399674e011785af19eeb04": "0x00000000000000000000000000000000000474616400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140823dc59ec2f7a204eed7cf3f4f6560d58db4eb78bd24b655bbd1d7a5c6b454e77c8dc5e2721a54d": "0x04010000000200000000000000000000000000000000164d4f4f4e204c414d424f5320f09f8c9520f09f8f8e0c4d6f6f6e204c616d626f731768747470733a2f2f6d6f6f6e6c616d626f732e6f726717406d6f6f6e6c616d626f733a6d61747269782e6f72671976616c696461746f72406d6f6f6e6c616d626f732e6f726700000f404d6f6f6e4c616d626f734f7267000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140874e41bb7db2c8904f9fe095c3f7e02a2ad48e58b7524cd3353a1a09097882259c1cb8b8fbc722e": "0x0000000000000000000000000000000000114672657368437265646974204c616273114672657368437265646974204c6162731968747470733a2f2f66726573686372656469742e636f6d2f001d66697261732e6b61646461684066726573686372656469742e636f6d00000d406672657368637265646974000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471408768e841e1a6e64bc9f6b302d5f299048a1d3aa10d6ea182f3db0d15fb42d0e76c086a13777dd35": "0x040300000002000000000000000000000000000000000854686520546965000000116a6672616e6b407468657469652e696f000009746865746965696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714087a5388d9e5a204ea149bdf5123e8cfade6a4541bc205d1c1bed6c92c876b21731bf4ac7984c67d": "0x04000000000200000000000000000000000000000000095468654775696c64000000197468656775696c64736f7572636540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140890dc820c706f1454824b885099cbba0103d1a671b0aac36c6b8b0e80747f3f111300d41d1cd55c": "0x000000000000000000000000000000000008426c7565446f7408426c7565446f74000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714089372171cde85a1a67f727210edec1922631f1fdd095c40c9b434c864c239968d26755cd982504e": "0x00000000000000000000000000000000000d506f6c6b61646f7453686f740000001b626f6574746765722e7468657265736140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471408a0e5fe9e4b1f842a27dd26f5f3fe4f48fc67cddb54a8cdb0f3c6e4b9c8cf751a59466771dc6144": "0x040300000002000000000000000000000000000000000f5269636861726458636176617465145269636861726420486f756c6473776f7274680000137269636861726440786361766174652e696f00000f7269636861726478636176617465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471408db713c2bbf4320a4731404eb64407b76d75dd815a3267e2dce24d8c2054f3d45d83ea11c8d707a": "0x04000000000200000000000000000000000000000000096d696368616c6973001b68747470733a2f2f6269742e6c792f6d696368616c69732d696e00176d696368616c69732e66724069636c6f75642e636f6d00000d406d696368616c69735f6672000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471408f29307d17340ac7e88d4cdb23702f9bd21d16a968463ac7e1f5272835d72b05e2224612d46222a": "0x040000000002000000000000000000000000000000000749736162656c0000000000000d40737573755f63727970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471408f8525d1428358322a7a0d6570fae451f9ecf96564485fbbd451d2d1512e0c3e8b865ad4702091a": "0x0401000000020000000000000000000000000000000005496e676f0b496e676f2052756562652168747470733a2f2f7777772e6b696c742e696f2f7465616d2f696e676f2d7275000d696e676f406b696c742e696f00000b40696e676f7275656265000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140906d8a2ac7d524584a61bd5f070a257326ab2b012a19d36b9ff5b53471a44420687484ad2070828": "0x04030000000200000000000000000000000000000000084c617572656e74104c617572656e74204b6f65686c6572000017616d6972616c6b727970746f40676d61696c2e636f6d00000d616d6972616c6b727970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714092e30bbc1252c51b031bd2bb49eab8e971d4734110faca76ae52998450b0f23ef46029277a1e539": "0x04000000000200000000000000000000000000000000054f544152134f746172205368616b617269736876696c690011406f7461723a6d61747269782e6f726700000011407368616b617269736876696c693237000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140932ed16a6ce31dbd08450b51426556a9e61b8e928b97c6075d95cda58b433fdfca36a2b69d9766b": "0x00000000000000000000000000000000000d43485249532043525950544f0000000000000d404348524953435259505430000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714097e95a7fbccc3c012d761ac11e70c35595d382a0c860bb0501ab0690df6c9f96ea7179b206c1d40": "0x00000000000000000000000000000000000a4669676d656e742038001368747470733a2f2f6669676d656e742e696f000100000b4669676d656e745f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714098de700b0bd1b5ca2656a3bbbea71626facc2641a9d9f744c87c393778020354714974894b7b27a": "0x0400000000020000000000000000000000000000000012536f6e6465722056616c69646174696f6e00001d40736f6e64657276616c69646174696f6e3a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140998feed74b871672c1a1173d90dfb10b0dce7544381ae4b6e7d46ec1506ec6052eb221a62d1dd18": "0x04030000000200000000000000000000000000000000074f6461696c7900000014636f6e6e6965406f6461696c792e656d61696c00000d404f6461696c794368696e61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471409ba869b7efed68f24973457b12e9d08e7dc0b0f255ab508e1829c66f6d2e6c4cb94acda5a00ab5c": "0x04000000000200000000000000000000000000000000094d6f72706865757300000015696e666f40706f6c6b612d626c6f636b2e6f726700000e40636861696e616e646d6f7265000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471409cedc84fed5609d6c1c40574832dee06228c10e43537fe6b3bc4cc78cdb7d34d1586d8904d8fa7b": "0x040000000002000000000000000000000000000000000447696f1147696f76616e6e7920476f6e676f726100124067696f79696b3a7061726974792e696f0e67696f407061726974792e696f0000084067696f79696b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140a2cec7af82c6d22d22f4b3f7a9f0878a49954e7b4491ca841f68831c2e8aeb383cd79dcdc00295b": "0x00000000000000000000000000000000001042494e414e43455f5354414b455f361042494e414e43455f5354414b455f36000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140a4cb53b517606bc609164cfbfa54bc46f2e6421980f9e50a7b21d1e2c4b8a9ecb82e08d94555190": "0x0400000000020000000000000000000000000000000007444f54696e730000000000000b40446f74496e735f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140a6e3cf2b2ed68b7f476394d38a2202be9ac84607ce7fa0a7a53981920999c231210fda7fca36041": "0x040100000002000000000000000000000000000000000a456e636f696e74657216456e636f696e746572204173736f63696174696f6e1668747470733a2f2f656e636f696e7465722e6f72670013696e666f40656e636f696e7465722e6f726700000b40656e636f696e746572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140a7e8087590f806c0ef2cc1000f878a3880a09d698b5375f20c4ab3d8b3a1b783c8150faca3da65a": "0x04030000000200000000000000000000000000000000054c756379194c75637920416e6e204265726e69636520436f756c64656e0000166c756379636f756c64656e40676d61696c2e636f6d00000d406c756379636f756c64656e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140a8ec5c60a74c1b9a65626bf6bd3d70916fea48c93358cbf1e97f4f7f5ce8d5a292719e2555de66b": "0x040000000002000000000000000000000000000000000e4c6561726e506f6c6b61646f74001a68747470733a2f2f6c6561726e706f6c6b61646f742e636f6d00146c6561726e706f6c6b61646f7440706d2e6d6500000f404c6561726e506f6c6b61646f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140ae0ef8597bfb78fd6ebcc75c7ea9a0c4459162b495e90c7ed5306e3a27f73125d6fbd2a34601323": "0x04030000000200000000000000000000000000000000064a6f7365701c4a6f736570204d20536f627265706572652050726f666974c3b373000019736f627265706572654070726f746f6e6d61696c2e636f6d0000094a6f736570746563000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140b3a777104037709f221049df41595b4296f7cdf47a38b5b7c3187f9cad55c48ad60277ec92ce869": "0x000000000000000000000000000000000009646f746875622d3409646f746875622d34000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140b5c5327ba564a7c86fc56ba95671116870c422992b82a701b9c1c877ec7f6c6adf080ca8be17d7f": "0x0000000000000000000000000000000000085365766572616e0c4ac3a16e204b6f7ac3a16b0000186a616e2e6b6f7a616b3031303740676d61696c2e636f6d00000d536576693532313535353036000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140b70260c8fbe02026c4013c3ada90b00a5c2241cec6165e96958ef1a9884c15a981f86e48b0fc102": "0x0000000000000000000000000000000000094d61787061696e6e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140ba79531536753bb707c94e3ad62ed919cf1eebeffe3381161c4daef849a306d698539931a08ce14": "0x08000000000201000000020000000000000000000000000000000011576f6c6645646765204361706974616c0000001768656c6c6f40776f6c66656467652e6361706974616c00000a406d6f68616b616772000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140bdbcf3faf64bc3f787be6b11ece98c192f5e1fa41c880db0318fe885f614eddf69ffa040b3e9031": "0x04000000000200000000000000000000000000000000044d54430000001b6d696e647468656368617274696e666f40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140be961ab1f92f2a95002926543d8b8887044442834d4fd5fe3d6eb257719daefd3f2aed28eeaee69": "0x0400000000020000000000000000000000000000000009506172616d69746f00001540706172616d69746f3a6d61747269782e6f726715737570706f727440706172616d69746f2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140bee7f7b3d6f8bb6dc1bc0568b4f02bc9e8105e9dd9543235232d321b4b46da86eb8f94bb4de8714": "0x040000000002000000000000000000000000000000000d4272696c6c69616e74696e65000000156461726b6d697361343340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140c1cc8d36c3db95466f2c9fd9d1c3919443e6d6312f7c281d32ce1978c4a72c0dd6518ccd3cef503": "0x04030000000200000000000000000000000000000000105468696e6b57696c6443727970746f13456c697a61626574682042726f776e696e6700001c6562726f776e696e672e636f6e74656e7440676d61696c2e636f6d0000107468696e6b77696c6463727970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140c20ce18ec5a3b0022cd6deb3ac44803aeca3cf6543a69eaaab049069eb05231f598c324c2ef0b17": "0x000000000000000000000000000000000016506f6c6b61646f742050616c6c6574204772616e7415524d524b202d2050616c6c657473204772616e741168747470733a2f2f726d726b2e617070000c626440726d726b2e61707000000940726d726b617070000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140c388ae01faeb42e58be8a938b93a1a251a20a38240e235a5d62c1093c0cb9290e4567fad087606f": "0x04000000000200000000000000000000000000000000064569676572094569676572204f791268747470733a2f2f65696765722e636f2f000f68656c6c6f4065696765722e636f00000a4065696765725f636f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140c41e5ffb19e12ae30a11a36a48739b8fa4bee6844af919e22aa50f114f9e395a1caec59cc157102": "0x0000000000000000000000000000000000055365756e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140c4559f7a17cb073568191edc1aaf4bea93b17cf53ea49ab78e2d25d83dec8581854be93d3bc9609": "0x040000000002000000000000000000000000000000000f4c6f72656e6120426c6f636b7961000000166c6661627269733139373440676d61696c2e636f6d00000a40424c4f434b59415f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140c460df68c43e5ce000356a196f8dfaf9356be2697eaaf5e37f110668bb3caa503a52a223bc0ecd7": "0x00000000000000000000000000000000000c526567656e63792d3030321757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140c7c6da4442b90353296c9b3a6546d2319a764e00e1126215b776cb27571db2ef0392bbfbc66d45f": "0x040000000002000000000000000000000000000000000850696f6e656572001568747470733a2f2f70696f6e6565722e6d6f652f144073616368696b303a6d61747269782e6f72671c70696f6e6565722e76616c696461746f7240676d61696c2e636f6d00000e4049686f723037303534383635000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140c7d3455667b833b7e5992abc613a36e614c1d17c32342d365f9a66d57adeb96092e94a8cb7ba64a": "0x0800000000020100000002000000000000000000000000000000000b4e6f64616d61746963730f4e6f64616d6174696373204c74641768747470733a2f2f6e6f64616d61746963732e636f6d14406162633a6e6f64616d61746963732e636f6d13616263406e6f64616d61746963732e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140c9893ff555d916a508e92a4d4441736fba8bf0d9ab8ab258031112be9bb9cd8e91cd11d2f806512": "0x040300000002000000000000000000000000000000000a556e636861696e65640000002173706f6e736f72736869707340756e636861696e656463727970746f2e636f6d00000a6c617572617368696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140cb42537605572168a8bc181a8b2a5e27a7c66051fe9119875f4c5718cc808aaf2258b4b2fa37832": "0x0400000000020000000000000000000000000000000005616c696e0a416c696e2044696d61001040616c696e3a7061726974792e696f0000000009616c696e64696d610000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140cc8f64bbd2e71ea820050e114404eec82932c59bedbfb6c1b58981e8f85af37e5d4f26a34226960": "0x040000000002000000000000000000000000000000000d6d6173746572737061726b7900000014642e6a2e626f6f726440676d61696c2e636f6d00000940646a626f6f7264000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140ce0f91c97c65aae744be50accad162e5162a2499a897f5cfd792e0ebf9ca6ed7d13b5e404b36007": "0x040000000002000000000000000000000000000000000b536e6f7762726964676500001d40776861747265676473666f64726a6b673a6d61747269782e6f726713616964616e40736e6f77666f726b2e636f6d00000e40736e6f77666f726b5f696e63000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140cf87334458780db58a47ac1485804eba1da4f4bad5d245535ed92c81aad8a15ce849f7474685604": "0x040300000002000000000000000000000000000000000e4361726c6f7320536177616b69214361726c6f732053c3a97267696f204d6f74612053696c7661204a756e696f720000166361726c6f736177616b6940676d61696c2e636f6d00000c6361726c6f736177616b69000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140d09678fc53ebff9b6ab520cbb6fe9b6bc5433fa4193074bd1aae212f0dc969d6af584e140a08a5d": "0x00000000000000000000000000000000000d52616d70204e6574776f726b0f52616d70205377617073204c74641568747470733a2f2f72616d702e6e6574776f726b184072616d706e6574776f726b3a6d61747269782e6f726715706172746e65724072616d702e6e6574776f726b00000d4052616d704e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140d670abd53527a0c5839fa7a9242c404eaf4128cc218e3ca90ebb90c542994a19c3adcdd0bab4257": "0x040000000002000000000000000000000000000000000a4163616c61204454520000001468656c6c6f406163616c612e6e6574776f726b000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140d8e58a290cc0d407460ac178015d2a7c289bb68ef9fdaac071596ab4425c276a0040aaac7055566": "0x0000000000000000000000000000000000087a6a623038303700000000000000087a6a62303830370000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140d9954f2f20d04e36495827bfe0b07d16c549eb10d7e45997e95788be44a3f277af6befec99fe62f": "0x0800000000020200000002000000000000000000000000000000001052414449554d424c4f434b2e434f4d001868747470733a2f2f72616469756d626c6f636b2e636f6d0015696e666f4072616469756d626c6f636b2e636f6d00000d4072616469756d626c6f636b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140dbcaadfd532d03b7a0898a186215e4295a37c12f14b3c092ac526e0d3f132112fe33983e11f7a62": "0x0000000000000000000000000000000000066b6974616900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140ded91512786b3a62ccd1ada7e7fc4068a8d69ce621b8355378b362608bdd3f65524c1e401e6433c": "0x040300000002000000000000000000000000000000000b546f6b656e67756172640b546f6b656e67756172641668747470733a2f2f746f6b656e67756172642e696f0016636f6e7461637440746f6b656e67756172642e696f00000f40546f6b656e67756172645f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140e032212504d7709ba57da1251d785b2d433ca687c510a82c284826b9b79859b5774a84f7000e928": "0x040100000002000000000000000000000000000000000ef09f8fa2204d49444c2e646576001168747470733a2f2f6d69646c2e64657610406f6b703a6d61747269782e6f72670f68656c6c6f406d69646c2e64657600000a406d69646c5f646576000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140e08049fd14356105076e12e0065c3750fbeb83fa35368cbb09af42967eb6a473ff3a357b2b51f5d": "0x00000000000000000000000000000000000c6e616d2073616e676a696b00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140e21e0ca3b18ddaef4c583b1fe10e634547f909f678b4b8f5a98bea24645127b2eb9fd7b3e6c2f5d": "0x0000000000000000000000000000000000096d6f6c636861696e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140e22e33a70eea1beac11e708ac83a57b6e870a4ac9e5b58bd1d247f0183810abacf548469140884e": "0x00000000000000000000000000000000000970617374614d616e164d6f6368616d6d6164204875736e692052697a616c1968747470733a2f2f6963616c31302e6769746875622e696f19406875736e6972697a616c31303a6d61747269782e6f7267156d6f63686875736e697240676d61696c2e636f6d00000d406d6875736e6972697a616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140e23dcd64ffcda7f562e0d94ffb8e2492e12fa0f02bc1afa0d2cec3b3309a19337fd653ec4d81e64": "0x040000000002000000000000000000000000000000000e4e65756b696e6420546f6b796f0000001168656c6c6f406e65756b696e642e6a7000000c406e65756b696e64696e63000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140e6e902c97ee9286b42bde3f29708150bd47382f10fa4eba1c27a068653cfc4e3787b7fe05fe6e7d": "0x04000000000200000000000000000000000000000000064b495a4f53000012406b697a6f733a6d61747269782e6f7267156b697a6f734070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140e7a2d3832f0ab5118c044557335d26c3a538ec7a2699ef5665cca1d17755cd6ae53d41f5bf31623": "0x040000000002000000000000000000000000000000000a5a6569746765697374001668747470733a2f2f7a65697467656973742e706d2f001361646d696e407a65697467656973742e706d00000d405a6569746765697374504d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140e8195af2fbb9bd3b491f0fd140dfbae6ba14572b9b7e8bea724b9336dea4bde7f0eae9082e2682f": "0x04030000000200000000000000000000000000000000084648455741534d084648455741534d0000177465616d2e6668657761736d40676d61696c2e636f6d00000d4064616f67616e6774616e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140e8757f9140fa0d4f4c6e1ea78fa82de3a28d27e20c93b4b8109fd93489d864a73fe7bd4eadf2061": "0x000000000000000000000000000000000010496e73696768742046696e616e6365001368747470733a2f2f676f7374616b652e696f001577656e6a756e6438343940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140e9a3c3e586a908f8122315b758fe65626b7d0f7c8f5d0758b235f22df56cbf73610916a88520efc": "0x00000000000000000000000000000000000b43656e74726966756765001668747470733a2f2f63656e747269667567652e696f0000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140eb83f2c7db87e68e043d8f7872cd895f8957c9179c4264816be3e649713cb3bdc523f752602cc3a": "0x0000000000000000000000000000000000075374616b6564001268747470733a2f2f7374616b65642e7573001073616c6573407374616b65642e757300000b407374616b65645f7573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140f0643da4d7eac3d003c529dadd597907deb794cab14c863e0cfb1b47af09120efdda16fe432ca54": "0x00000000000000000000000000000000001443594420506f6c6b61646f742057616c6c657400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140f16205ef17fe25d2eeb1a6884bf369c7d1ab3f9ff75b79dd0f6c6a9792834e026a8d2f7ef049e4d": "0x040000000002000000000000000000000000000000000a5354414b4550494c45001a68747470733a2f2f7777772e7374616b6570696c652e636f6d16407374616b6570696c653a6d61747269782e6f7267147374616b65407374616b6570696c652e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140f5707a84539a93930e3f26094a2536e02b6879a2a92752493302d84029b80ff449eba8b9dd80d1d": "0x00000000000000000000000000000000001042494e414e43455f5354414b455f381042494e414e43455f5354414b455f38000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140f7af1c09d518a3034d65588960a9500acf14d5d58a4bfff1a29d55e99e3079304028f13fa462a6d": "0x0403000000020000000000000000000000000000000012416c65782044696d697472696a6576696318416c656b73616e6461722044696d697472696a65766963000014636f612e64696d657340676d61696c2e636f6d00000c40616c657864696d657337000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140f848aa6535dc8a065a99d8b861ad966394cfe1fc291e7cbd3a0392b293ebaf2ed64f6bbd7294c4d": "0x000000000000000000000000000000000015506f6c6b61646f7420496e746572204d69616d6900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140fa2c1f54780ff56b08f31c5b83a67cd2b4014b513dd4cfa4ecfd32c22f0cd35a8d0d1639c3a3472": "0x0000000000000000000000000000000000154372797374616c696e202d204d6f6f6e6265616d00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140fc514eab423f9a784434de779e5fcbe6da08b123b0d1556c5ded43cccd6a9b1f6efdc9ea4942032": "0x040000000002000000000000000000000000000000000f4b75732056616c69646174696f6e00000016686579407468656b7573616d617269616e2e78797a00000f405468654b7573616d617269616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140fc7663bef7fa5771cd527641e7070529937a78c02fb4de29f1d210908ad654cfc32c1ba930c6d01": "0x000000000000000000000000000000000005636f6c64001768747470733a2f2f6172626973797374656d2e636f6d0017636f6e74616374406172626973797374656d2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047140fd49b70acd3290232d4d1b0dcef676d9a72f6abf9aec55e129ef2de135ac172e19c28a9adbcad0b": "0x0400000000020000000000000000000000000000000012506f6c6b61646f7420476f204c756e617200001a4063727970746f676f6c756e61723a6d61747269782e6f7267196e6f74696669636174696f6e7340676f6c756e61722e696f00000f4043727970746f476f4c756e6172000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471410045954da3b77a48c73df5e3b5d0720799fc9d422df981bfcdb7e2daf5fda64098f433317f66481": "0x000000000000000000000000000000000010506f6c6b61646f74204d657869636f00000018646f7473616d616d657869636f40676d61696c2e636f6d00001140506f6c6b61646f744d657869636f5f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714102a55fef2f92ecdaa6411ebaa64db799c310fc729b4bbfe823f8e5365370d8905db1782495e630b": "0x040100000002000000000000000000000000000000000750617472696b001468747470733a2f2f6170696c6c6f6e2e696f2f194070617472697a696f5f626e703a6d61747269782e6f726715636f6d6d756e697479406170696c6c6f6e2e696f00000e4070617472697a696f5f626e70000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714103932d4af94b6477a3da336bc10786809ad364ed466f2ac0b224e5979f428cc2d420fb2c1643c45": "0x00000000000000000000000000000000000c506f6c6b61646f7447435600000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714103c17ca46c08000ea05f87e141ce5a6f38702929411405799a18e69128fafd24e849444072e5d6d": "0x0000000000000000000000000000000000084176656e7475731c4176656e7475732050726f746f636f6c20466f756e646174696f6e1868747470733a2f2f7777772e6176656e7475732e696f2f0010696e666f406176656e7475732e696f000010404176656e7475734e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714104b7f3de11760cb22f82385aacb628dfcf268ed37fc6d846b03eb3fe766d31de012d888ae4a1265": "0x000000000000000000000000000000000015506f6c6b61646f7420706465782077616c6c657400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141056f8490f45c33c4671360a95c3004648c6a4e52966ed2b097e68847bf729be9f8438f6973ade59": "0x0000000000000000000000000000000000056d62616a0b4d616369656a2042616a0016406d616369656a62616a3a6d61747269782e6f72670000000c404d616369656a5f42616a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714105cd78f2accd77f44899146a600cab633113021d077356a6c362899a4d40ee426fd3ab22d2ab301": "0x040300000002000000000000000000000000000000000777696e746f6e1b456477696e202857796e6e29204c656f2042617565722049494900001477796e6e6f7277696e40676d61696c2e636f6d00000b40426175657257796e6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714106a66f6a055b1e2d0f222915d0e0b32a9137413a0cf64a3fd7676b8b73236f73619147195ca5204": "0x040000000002000000000000000000000000000000000e416c7068612041697264726f700000000000000d40416c706841697264726f70000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471410798f54edbd929bd60281dbd0933b5cbb571fc3177c5803f7b12f9b8193f83787713dfcc9f52a1f": "0x0403000000020000000000000000000000000000000007536f7261696100000016736f726169612e7273723240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471410a0936c3defd2dc4a154ce100d43672e3cab61d2196621d0d69dccbf86d432595f2d0fc4eb5ee61": "0x00000000000000000000000000000000000d50656e64756c756d416c65780000000000000e4050656e64756c756d416c6578000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471410a8fd5f177733e23640b7b7fbbecf967f99ec9516a74f9e255efa5c8529751a383afccfe936175e": "0x04010000000200000000000000000000000000000000114d61737465726e6f64653234f09f94b10d4d61737465726e6f6465323418687474703a2f2f6d61737465726e6f646532342e64652f1540616c65786b6964643a6d61747269782e6f726719706f6c6b61646f74406d61737465726e6f646532342e646500000e406d61737465726e6f64653234000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471410ab4532df9af262e83e7463783cc45e410d91594a73c2ab7daf49c3cc7886c7df454bd948d1f158": "0x0000000000000000000000000000000000076a75616e363200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471410baf040e7f1de1d5a2a286ee3c24f270564b3dfee5bcc553022e4affe2694c62101f00773a7d25d": "0x00000000000000000000000000000000000a73696b616d6564696100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471410bb1f59d0b43521b2dc691fc657bb1b8a732fb6887984ebde8885f652ef6df372b564ea30e4c07f": "0x00000000000000000000000000000000000d4361726c6f7320476f6d657a0d4361726c6f7320476f6d657a0000166361726c6f73406361706974616c782e6d65646961000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471410fbb58c9784f416deca8beea42c6361e77eb489db4e951bb1c63f0eb85afa7827075962fa46537b": "0x0403000000020000000000000000000000000000000015546f6d61732053656e6f76696c6c6120506f6c6f15546f6d61732053656e6f76696c6c6120506f6c6f0000127473707363677340676d61696c2e636f6d0000001e68747470733a2f2f6769746875622e636f6d2f7473656e6f76696c6c6109746f6d6d7939375f00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141108a7f2fbcca12cfc9fd7446ec46d27262587240fe98dbc5855bd390714f3713ef561729052154b": "0x040000000002000000000000000000000000000000000b414a204153544552494f00000016616a2e706f6c6b61646f744070726f746f6e2e6d6500000c40616a5f6173746572696f000a616a2e726563616e6100", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714118564695738fc77fc3f5a249cbc860ba90c0b7322c615b0b47e360e376d85cf366ce6d7f8d9bc75": "0x000000000000000000000000000000000006504f4c4b4100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471411ce0c03039f6a489e4e7009937c56d267338762a60ed004293afd40e7c2081847c12cb63c76a818": "0x040000000002000000000000000000000000000000000744725733524b00000016726164686140776562332e666f756e646174696f6e0000084044725733524b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471411e075ceba8e3f4524112dbb17e6f83bb832ee26149c8b00cefac96a1a668ccf0645898c3c271d04": "0x00000000000000000000000000000000000c54696d204a616e7373656e0c54696d204a616e7373656e19687474703a2f2f74696d6a616e7373656e2e63727970746f00177468776a616e7373656e383940676d61696c2e636f6d00000e407468776a616e7373656e3839000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471411ec48c6e9cf67fb4cf2e774e34c3603b2428a690c058d2ab826b39a2eba4e22b3aae85f9bfa7802": "0x040100000002000000000000000000000000000000000a414354494e4f4c49580a414354494e4f4c49581668747470733a2f2f616374696e6f6c69782e636f6d1640616374696e6f6c69783a6d61747269782e6f726719626c6f636b636861696e40616374696e6f6c69782e636f6d00000b40616374696e6f6c6978000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471411f19977cb6420ff7852bd4de2d3d4207ca8bb3fd9954cfe3c6ca91e1acba438e8a70df5fbbdd91e": "0x00000000000000000000000000000000000c597572694e6f6e6475616c0000000000000d405975726970657475736b6f000c797572696e6f6e6475616c00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471411f91d41b705cb0e442758c71011edc49ef5f9297c81f905dc3e847acdee4edb87b3bda628a02044": "0x000000000000000000000000000000000012456e67656e686569726f2043726970746f0a4775696c6865726d65000020636f6e7461746f2e736f75656e67656e686569726f40676d61696c2e636f6d00000a406762746332303038000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471412060f617a53b90efa99c57636163b80492e7748882c27e82074cc76ae723804e0c8f222aa1c9879": "0x04000000000200000000000000000000000000000000144b726f6d626f70756c6f73204d69636861656c00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141211d1890e7a3711586fd1cdd673dec2a5c3dea4f1f2efab11c15c6008698c3adb139a876bc0354e": "0x040300000002000000000000000000000000000000000d404f6e6c79446546694775790e416e64726577204c6966666579000017616e647265776c696666657940676d61696c2e636f6d00000d404f6e6c7944654669477579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471412255c95905b3078f2681118b778f641d6064047cb36d943cbe0761bca515f07d762768bc0205405": "0x0000000000000000000000000000000000044a6564044a6564000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141241731d14d7e2624e438b5a8c79cf1f158bae626b3fcbae0d60e4fe0a783a92a51c7a06c81c1a07": "0x00000000000000000000000000000000000b446f74456e72697175650e456e726971756520527562696f000021656e72697175652e727562696f2e646576656c6f70657240676d61696c2e636f000009406b696b65727562000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141249720e20450dc46645e03be465e70990610e0d6c978da1866dbd9c9828f6e19ef1f0e3b1d02f11": "0x040300000002000000000000000000000000000000000b7969616e6e6973626f7410496f616e6e697320507361727261730000147969616e6e69734070726f62656c61622e696f00000b7969616e6e6973626f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141257e95afd3f3529fabfc151a66d7cbd2cfebbdea1954d13fa4721a568a8086386f445c2cefa261c": "0x04000000000200000000000000000000000000000000114c696768746e696e6720426c6f636b7300001c406c696768746e696e67626c6f636b733a6d61747269782e6f72671b636f6e74616374406c696768746e696e67626c6f636b732e696f000011404c696768746e696e67426c6f636b73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141263f06b8f0929949c665073980c9bdbd5620ef9a860b9f1efbeda8f10e13ef7431f6970d765a257": "0x040100000002000000000000000000000000000000000f526f636b585f506f6c6b61646f7406526f636b581668747470733a2f2f7777772e726f636b782e636f6d0012737570706f727440726f636b782e636f6d00001040726f636b785f6f6666696369616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471412d4691f7bf1f08098650252aba2203808fa1dca1f57dce2e9927b88d66f193bab53f4910b9e1c59": "0x040300000002000000000000000000000000000000001038426a4c55443366376144473766781457696c6c69616d2043726f6973657474696572000019694d723338675a3754637537786f40676d61696c2e636f6d0000114057696c6c69616d3533363436363531000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714137178bb7584275bc64c6a15649acd3f96cf450ca06d53059b6888d67456f8e7ec31c8504eb3125d": "0x040300000002000000000000000000000000000000000e477520496e73616c75627265730f4775737461766f2050726174657300001a6775737461766f2e7072617465736d40676d61696c2e636f6d00000e696e73616c7562726573627463000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471413c8717f740994a86aa8cd24ade14ce3debcf13ff5d3ce5fcecbfb2acc1c0c9c0c7b25d79d902d22": "0x00000000000000000000000000000000000567686530001268747470733a2f2f6768656f2e746563680000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471413fba91067ee782aaefd2727ec65732fec6ebfc4a824345adb093436abfd5f53e0fe421c6f5cdb0b": "0x0400000000020000000000000000000000000000000006445053544b00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714140fdd346d273b4bce5395a6819c6edb4a8679a1f1b9c809de142dd5b84805e4d0a2ad428476d606": "0x040300000002000000000000000000000000000000000868616b6d61726b0d4d6172656b2048616b616c6100001768616b616c612e6d6172656b40676d61696c2e636f6d00000968616b6d61726b5f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714143c9b2402368042140be3ffc8865dd47a8d044916b26936a579433599bebca9d3ba1d6eb7722710": "0x00000000000000000000000000000000000b4441524b464f5245535400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471414754c6ae34414e51eec92559de5be4295be18caa79400d49466b8b06a4c819e766a7b79ad3b846d": "0x040000000002000000000000000000000000000000000b526f73732042756c61740000000f726f7373407061726974792e696f00000b40726f737362756c6174000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714148446d6e27ad179566511e3d396022cf986a5a86d09bf77db45af3ddbae12a8bd78948072505f4a": "0x0403000000020000000000000000000000000000000009546172656b6b4d410b546172656b2041746961000013746172656b6b6d6140676d61696c2e636f6d00000a546172656b6b4d4131000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714148771da2618d248c4962b64a7f16b6af320177d041e2229ede9d4efad694b74d02be3f1cbbfdd74": "0x04030000000200000000000000000000000000000000084f6e6c79444f5400000019646f74696679746865776f726c6440676d61696c2e636f6d00000c404f6e6c79446f744e6f77000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714148cd021e35e23e4f0673d30606ee26672707e4fd2bc8b58d3becb7aba2d5f60add64abb5fea4710": "0x04000000000200000000000000000000000000000000044761761544722e20476176696e204a616d657320576f6f641568747470733a2f2f676176776f6f642e636f6d2f0010676176696e407061726974792e696f00000b406761766f66796f726b0a6761766f66796f726b0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471414b9317a39b5587ca81adb32eb83fa9f6853129997532384eb874facafcf0f717c8eb952df30491a": "0x04030000000200000000000000000000000000000000164d75466920506f6c6b61646f74204163636f756e740a4d75466920496e632e0000117370656e636572406d7566692e61707000000e406d7573696366696e616e6365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471414df282ca6fd7e90be66c34c519a045956620b6c6962fa9440e79b96ee839b93b4a188d961693401": "0x00000000000000000000000000000000000653574b696d00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471414f4cb7e8e1dd98fa423f953c39a707184067af6963efe1143a155f57aa003299c45b9be12f02642": "0x0401000000020000000000000000000000000000000006417266616e1c4d6f68616d6d616420417266616e20417367686172204973686171000012417266616e6d6940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714152bf2356a6365ea3ce185c80846549d014ef1b12ab7ce5ad8931c7913208ae3fe5b206cb8d5480b": "0x000000000000000000000000000000000005444d523500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714152cd44728673f349e7c73972c8cf09884b6647f957d945a8c9109ccd4e742e843e89b343a43651c": "0x040300000002000000000000000000000000000000001456656c6f63697479204c616273e29aa1efb88f1156656c6f63697479204c6162732041471e68747470733a2f2f7777772e76656c6f636974796c6162732e6f72672f00196e69636f6c61734076656c6f636974796c6162732e6f726700000840765f6c616273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714153e9a548981fe578ce2fb4076ef92d3a5cac7689f4bf9c589296b99d5aa899511aff9e2be5c7a40": "0x040300000002000000000000000000000000000000000653616c61640d5361726168204c657374657200001573656c6573746572313240676d61696c2e636f6d00000f74727565736172616873616c6164000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714155306eb29d41fe65cc0cc69582f48b38baa0c7acb297580c4facad35056691f999bf3c53ae08a44": "0x0400000000020000000000000000000000000000000012736e6620646f742076616c696461746f72001c68747470733a2f2f696e666f7365632d636f6e73756c742e636f6d001d69687562616e6f7640696e666f7365632d636f6e73756c742e636f6d00000a40736e696666736b69000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141596b645522e66dd2c4f99c0f8272e38f7169e264234dc13a4da815517564a538382e5828f61fc28": "0x000000000000000000000000000000000009706172616c6c656c00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471415b4508e0114da171f449a820b2c7718597a61ee2aa3928e53404e89779f8d97f7a3fb341ea68ff1": "0x00000000000000000000000000000000001f506f6c6b61646f7440436f696e6465736b436f6e73656e737573323032331f506f6c6b61646f7440436f696e6465736b436f6e73656e737573323032331968747470733a2f2f776562332e666f756e646174696f6e2f001761646d696e40776562332e666f756e646174696f6e20000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471415d31b7bc7b1054023f71995b4441f187fa21b4020e925b820950705c5e4a602a16c57baf551b3d9": "0x040100000002000000000000000000000000000000001541697264726f702e636f6d206d756c74697369670c41495244524f502e434f4d0000166d61726b6574696e674061697264726f702e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471415dd65723b63fdc2f6d19b31316879ce7470aafdcb908d04dfc5c156f8de974bdd582086d0ff6030": "0x04030000000200000000000000000000000000000000075458536865700d42656e20536865707061726400001962656e736865707061726478797a40676d61696c2e636f6d00000854785f73686570000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471415eee78443792d00346ebc3380be6816f828d1d1df372c51fbe99c95a321d7510403bb98f067695e": "0x040000000002000000000000000000000000000000001c50726f6d6f5465616d207c205765623320557a62656b697374616e0000001c706f6c6b61646f7470726f6d6f7465616d40676d61696c2e636f6d00000d4050726f6d6f5465616d5044000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471415f1d58f2dffcd5d7a23f1a7ce3801006e4c449d66fa97c61b6f45fff10e365e08710c0432930c39": "0x040300000002000000000000000000000000000000000653706963790000001d4d6973686f2e6b616e617269614070726f746f6e6d61696c2e636f6d00000d4d6973686f4b616e61726961000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714161735619834be83b8be96d986897797d117987e4368640ffdf32bc967ba7467d012136c22dca33c": "0x0403000000020000000000000000000000000000000005636c307700001140636c30773a6d61747269782e6f72670b76406c6572792e64657600000740636c307735000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471416214d11e8d53abe10f8035fd1cf87ae493a030edb707fef7c92df6a343489d06c0e242dfe43b35d": "0x040000000002000000000000000000000000000000001b4a4b5242207c20506963636164696c6c7920f09f87acf09f87a7000000196a6f656c406a6b7262696e766573746d656e74732e636f6d00000a404a6f656c4a4b5242000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141627a7ca07c7aef778dc106e241524180b2594b639d2f0eca4d41b1eb7f9e973c253402a7cd2ed4f": "0x0000000000000000000000000000000000076d6179616b610c5a696c69616e67204368650000156368657a696c69616e6740676d61696c2e636f6d00000a4030786d6179616b61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714164c857f81c668cfbc27ad8fa6f2ea276c5657bdd2c6e1c810c96975dabcdc0e4a48fe1b7c6f9900": "0x00000000000000000000000000000000001155736f206465206d756c746973696e671853657267696f205261756c204d6172636865736f74746900001473657267696f4061646c69626e65742e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471416632ac54302ab493a06ba1dcdd93d70630fc8cdb68ac3e69505c4570d43ef1f110c82e0af756678": "0x00000000000000000000000000000000000631574542351757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141666dc35836cd0a48e41df7864847ec31fc0168967dd5b7912c21f4a597438c697f7f4c1a29c4d57": "0x0400000000020000000000000000000000000000000010526f636b585f506f6c6b61646f743406526f636b581268747470733a2f2f726f636b782e636f6d0012737570706f727440726f636b782e636f6d00001040726f636b785f6f6666696369616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714166a46eb86ae1a6b0f1ae3ffead748e1f440e6d1a76ec495f2f9e1670fd15e737ddc2ab913b8a149": "0x04000000000200000000000000000000000000000000084c4f47414e5447000014406c6f67616e74673a6d61747269782e6f72671a6372697374616c2e726f737369383840676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471416709400d558465ddc20836f2e4b88c1858d1e3f918e7358043b4a8abcd2874e74d91d26c52eca2a": "0x040300000002000000000000000000000000000000000547616265184761627269656c20466163636f2064652041727275646100194061727275646167617465733a6d6f7a696c6c612e6f7267186761627269656c40696e76617263682e6e6574776f726b018550a42403112eb7e49d90abe036e1980b988ef01768747470733a2f2f6962622e636f2f66595a5a7959310c4054696e6b6572476162650c61727275646167617465730b74696e6b65726761626500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714169763ff321610ad14fd4519281e3bfda326702a0f55da78d8cff3213553f65d7bdceeff8bb136a1": "0x0400000000020000000000000000000000000000000009434f4c4f53535553001a68747470733a2f2f636f6c6f737375732e6469676974616c2f1d40636f6c6f737375732e6469676974616c3a6d61747269782e6f726716696e666f40636f6c6f737375732e6469676974616c00000f40436f6c6f737375734974616c79000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714169ece2ddc0ce13e205addba4075b267f6ab1c5f8308743fe060d0aed47e018bcfbefd0f197ab52d": "0x00000000000000000000000000000000000b4c756d6f73204c616273164d75736875204d65646961205076742e204c74642e1a68747470733a2f2f7777772e6c756d6f736c6162732e636f2f00146b6161767961406c756d6f736c6162732e636f00000b404c6162734c756d6f73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471416c2d8d550a452fb203795f6b0bc5cf58ee69bc38f4d82278d7ae40d5c7b7ee7b6d2e8e04e2b4952": "0x04030000000200000000000000000000000000000000047377620d53657268616e20426168617200124073657268616e3a7061726974792e696f1173657268616e407061726974792e696f00000e4073657268616e776261686172000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471416c32812353e2b741090ed109e1c7cdb689467b8b9a48daeb19d21473918cc6fc5632f11a261d80b": "0x04000000000200000000000000000000000000000000054475636b00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471416d1d6d8171d77ca767806fa170bbfc9eb86c0b65bb265c85f86e5e0bfb82525c1149a38fc6d613f": "0x0000000000000000000000000000000000054a4f4d490000000000000c406a6f73656d6d6d323337000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471416ed9307fff94f1f0cc0424184702e27c49fb859c93f07d8bb0adf2a0824ada9f01db9bf76b7025f": "0x0400000000020000000000000000000000000000000015f09f8d8120486967682f5374616b6520f09fa5a9184e6578757320496e666f726d6174696b204475727265721768747470733a2f2f686967687374616b652e746563681640686967687374616b653a6d61747269782e6f72671e686967687374616b65406e657875732d696e666f726d6174696b2e6368000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471416f70b3f811d00f5f08cce486eece7d4a7a3d614b7cd85586d657893f2be9df6e8e3c4f7b181bd52": "0x04030000000200000000000000000000000000000000114d696368696b6f20576174616e616265114d696368696b6f20576174616e61626500001b6d696368696b6f34706f6c6b61646f744070726f746f6e2e6d650000104d696368696b6f506f6c6b61646f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714173972b961cdea90c49a03d9a47482fda70a5879e99da5159670949018884c6a239ec04c89f9a160": "0x00000000000000000000000000000000001244616e736f6e205477657369676f6d77651244616e736f6e205477657369676f6d776500001764616e74776573696779653440676d61696c2e636f6d00001040706f6c6b61646f74417275736861000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141774ee3fbd2aa269e0cbc3810429380ad335eb242f47acb1f4b609001af240dcbae281594d4ba522": "0x040300000002000000000000000000000000000000000f756269717569746f7573446174610f756269717569746f75734461746100001c756269717569746f7573446174612e636140676d61696c2e636f6d00001140756269717569746f7573446174615f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471417a3c17fe29895d0b4074510d7de2fe2dc843dd1f2eacc53b0c5a54b876e27af0b4eaebae9bc340d": "0x0801000000020300000002000000000000000000000000000000000947656465416e74611c4920476564652050757475205261686d616e2044657379616e7461000015616e7461406d616e64616c61636861696e2e696f00000a40616e74616b657061000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471417ca96d717d13d8f86409335c78332d6a06f712f4d3ded1e7849cb75ec083ee71f23e5477630420c": "0x00000000000000000000000000000000000b537461626c654e6f646510537461626c654e6f6465205074652e1c68747470733a2f2f7777772e737461626c656e6f64652e78797a2f0014696e666f40737461626c656e6f64652e78797a00000c40537461626c654e6f6465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471417dfa67fb23d9e4d093472713dd6fbe16b514cf07d445644396f2881d8237acb70d92055a1aaed77": "0x040000000002000000000000000000000000000000000c56616c69644f72616e6765001c68747470733a2f2f7777772e76616c69646f72616e67652e6e6574184076616c69646f72616e67653a6d61747269782e6f726715696e666f4076616c69646f72616e67652e6e65740000104056616c69644f72616e6765444f54000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471417ea45d57a104fc3f41c1aa075742a552affbe40871e68e62aec936ffae12b497e7b38d32d3eb70d": "0x04030000000200000000000000000000000000000000135a6f6543727970746f4769726c73436c75620e5a6f652046616972636c6f746800000c7a6f65406367632e78797a00000e5a6f65436174686572696e6546000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471417f2428f049676136029b5b2d1d0ff3a0a4aed8721f480386c8c866d79d86386e5780a6454cd7f24": "0x040300000002000000000000000000000000000000000a5f65636172646f356f0a5f65636172646f356f00001a65636172646f356f406b7573616d69676f732e6f6e6c696e6500000a4065636172646f356f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714181900f50b9dad51f855f52621dad2bef24b708644e05d48391ba124f93bb81149ac7f2faac64138": "0x0403000000020000000000000000000000000000000007566963746f72000000136d657461766974694070726f746f6e2e6d6500000a40566974697265756d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714188e91cee61b2febd6f3fc13608eaccd3e3c75789225fbc23963888890d5e7f806b0ccaaf90caf79": "0x040300000002000000000000000000000000000000000b4178656c4265636b65720000001963727970746f6265636b6572313740676d61696c2e636f6d00000e406178656c6265636b65726f6b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471418b47e3024d66dfe628f36dddf8cdb0242104a2531e7d3efd4860a9a4633be69aaf30f63ccb25a5e": "0x040000000002000000000000000000000000000000000b537769737320426f6e640000001c7377697373626f6e64706f6c6b61646f7440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471418d26b3f2b895c6dc448b1c0c946fe680dc9d237c6ba6bbcda519c42dfbea7ef74b0b73f7972a666": "0x04000000000200000000000000000000000000000000095f7061636865636b000000176272756e6f2e757073697a6540676d61696c2e636f6d00000e406272756e6f70616368656373000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471418e9b69f585d7a7d3c10da0654ed968b149f27cf9db0203337c79b6d86f6741f156598fb7699ab78": "0x04000000000200000000000000000000000000000000074c6179657258000013406c61796572783a6d61747269782e6f726714696e666f406c61796572782e6e6574776f726b000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471418f0062e466404f0976ce4203c844a11164d1b3f8342ad16a4cc5d99ac8eaae5cd74f4b1cc68c764": "0x04010000000200000000000000000000000000000000085355425343414e085355425343414e1868747470733a2f2f7777772e7375627363616e2e696f2f14407375627363616e3a6d61747269782e6f72671168656c6c6f407375627363616e2e696f00000c407375627363616e5f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141908028815102f077027fd39de5dece15c83271c7bed94615b69d3b170a8518b056ec951ef51da46": "0x040300000002000000000000000000000000000000000c446546695f5374616b65720000001d66616b656d61696c666f727370616d6d657240676d61696c2e636f6d00000c446546695f5374616b6572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141909140e98d4fcdfd880b45154006c09eb568c69f1febc0dadccaa59723dcd058cdae45c9c13ae68": "0x00000000000000000000000000000000000a4669676d656e742033001368747470733a2f2f6669676d656e742e696f000100000b4669676d656e745f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471419522f0465b284216cb2a6e7b4c6451a1a1fcf05a4545dfbda0215f15ad77851be9f7f7e94171e6c": "0x04030000000200000000000000000000000000000000104775737461766f204a6f707065727400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141954fe85b0e70adcff2ce23cb974ce041c73878aaf0d84454faee2b798569727d81703996c370fc7": "0x040000000002000000000000000000000000000000002057656220332e3020546563686e6f6c6f6769657320466f756e646174696f6e2057656220332e3020546563686e6f6c6f6769657320466f756e646174696f6e1968747470733a2f2f776562332e666f756e646174696f6e2f001661646d696e40776562332e666f756e646174696f6e000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714195552a14ebe4e24a6fabeb7a687bcc4705cd5d8cc3ac91adb6460d20d4d65b90ec3ce20be966554": "0x040000000002000000000000000000000000000000000659616f7169000018406a696179616f71696a69613a6d61747269782e6f72670f7940616c747265736561722e636800000a406a696179616f7169000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471419627e3b0a1fe44f466a9934facf9723f7ecc237cf0afaca23a6e832780616e432f3eaf19d8ff949": "0x040000000002000000000000000000000000000000000b5354414b45204c494e4b0b5354414b45204c494e4b1668747470733a2f2f7374616b656c696e6b2e696f2f16407374616b656c696e6b3a6d61747269782e6f7267116f7073407374616b656c696e6b2e696f00000d407374616b656c696e6b5644000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714198703ff0791c2de1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x04010000000200000000000000000000000000000000094741544f544543480d4741544f54454348204c54441468747470733a2f2f6761746f746563682e756b15406761746f746563683a6d61747269782e6f726711696e666f406761746f746563682e756b00000d406761746f746563685f756b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141998ecfe5c3f796158c941d90f219cf41ce169f540c96936193fbfee601c0e9ae6e415c75a8f80d8": "0x040000000002000000000000000000000000000000000f426c6f636b7365656b65722e696f001768747470733a2f2f626c6f636b7365656b65722e696f1b40626c6f636b7365656b65722e696f3a6d61747269782e6f726718706f6c6b61646f7440626c6f636b7365656b65722e696f00001040626c6f636b7365656b65725f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141a0b756d13ed752dc09033af1dd99c3727bd222c35d9d34e8e4403441792399b08df0d60544fbd48": "0x040000000002000000000000000000000000000000000f4c75636b794672696461792e696f000018406c75636b796672696461793a6d61747269782e6f726714696e666f406c75636b796672696461792e696f000011404c75636b794672696461794c616273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141a2ca5d095126893b82dc797d2ab47107b7b50397e46dfeb1d962737c349863f8682a86e1fd2630b": "0x00000000000000000000000000000000000e53616c616820506c6b61646f740000001273616c6168313540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141a61ab35fd1e09085e34b19d7230f1ea5da6d4b8fe6ede9d05c2e55b0189f75b967862c1c43d9a1f": "0x040000000002000000000000000000000000000000000b554c5452414e4f44455300001740756c7472616e6f6465733a6d61747269782e6f72671976616c696461746f7240756c7472616e6f6465732e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141a870c80f78975df4411ac181d7ec3d63ac2c53bbcc41d8721f9f99f6090c51f463cf91b74eefe57": "0x00000000000000000000000000000000000d79757669616d656e646f7a6100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141aa91021c24f2547e098dc986d4fd3a45b360314403a0baadce5da87c260eb1422da793fe623b16a": "0x0000000000000000000000000000000000174665726e616e646f206375717569206d61727175657a00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141acc24036266de3356937386a873382cb01abd0d0b223ae40790ebe0e97d8a06b7236640c865b730": "0x040300000002000000000000000000000000000000000e4372757374204e6574776f726b0e4372757374204e6574776f726b1768747470733a2f2f63727573742e6e6574776f726b2f0016627269616e77754063727573742e6e6574776f726b00000e4043727573744e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141b08960e386dd5cbd55ec415b6703ddf7bec9d5c02a0b642f1f5bd068c6b3c50c2145544046f1491": "0x040300000002000000000000000000000000000000000652304755450d523047554520494f204c54441268747470733a2f2f72306775652e696f2f000000000940676f7230677565000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141b0d7ea318c4d6a0223599480908ad0aa988a0b81a5799a32d32b2e81c79cea35ad07f78397d7b17": "0x00000000000000000000000000000000000b646f746163636f756e7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141b324457916228d02f944a1d1c5688dd06bc0335fb0bb058fa7fb2805a4247047dbbbc6c15121bc9": "0x040000000002000000000000000000000000000000000c506f6c6b616c79746963730000001d746f6d6d692e656e656e6b656c40706f6c6b616c79746963732e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141b4a951e7e2e12e5120f229aaff8334243bbf1867e7ac4f3d7e016ca3af627bd551380f3dd756456": "0x040300000002000000000000000000000000000000000a426974736b77656c6100000014626974736b77656c6140676d61696c2e636f6d00001e68747470733a2f2f747769747465722e636f6d2f626974736b77656c61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141b6b2384d0a5895de2978ce4a089bab55731feb84c485c99b42362de314df66dd9afce844283432e": "0x00000000000000000000000000000000000e504f4c4b41444f5420504c4159000000166e69636b40706f6c6b61646f74706c61792e6f7267000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141c24b3930fc8ca702a93f3f741525a17c26c491b229fac443dc2f185f69923b19d3041955c311056": "0x040300000002000000000000000000000000000000001344656e69732053756b686f7665726b686f761344656e69732053756b686f7665726b686f7600001d64656e69732e73756b686f7665726b686f7640676d61696c2e636f6d0000000007636b636e696b00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141c6684bc5e5975f29a8ead39ce1b44f37d16e98496441be79018e910d5f58c0fa1518d8fd7749550": "0x04010000000200000000000000000000000000000000135354414b494e4720464143494c4954494553185374616b696e6720466163696c697469657320476d62481f68747470733a2f2f7374616b696e67666163696c69746965732e636f6d2f001b696e666f407374616b696e67666163696c69746965732e636f6d00000c405374616b696e67466163000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141cb765d78beead8b7682d70550e153859a2f48dbaac25db251799fcaa8fa084eed3ab73c0ce5273b": "0x000000000000000000000000000000000013444f54205374616b696e672077616c6c657400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141cca28a5e0eadb1e008bf964db02d6018d0f97514170c4418103da670129723ca8547f5e4afa6536": "0x040300000002000000000000000000000000000000000e63727970746f766171756974610b766963656e74206d617300001863727970746f7661717569746140676d61696c2e636f6d00000f4063727970746f76617175697461000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141cd490d2ce0d345b5ec41f11b9d98e7422174148cd748dbc7c5eaffb239b0946de4c1bf43db72f23": "0x040100000002000000000000000000000000000000000c61646173747265616d65720f457567656e65204b68617368696e00001b657567656e654063727970746f70726f63657373696e672e696f00000f40657567656e656b68617368696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141d035207463779a168272c6f3753f3c797d830406e26815ed5c71ab453d6312d92e53651cf8d2c11": "0x040000000002000000000000000000000000000000000854726162616a6f00000000000011404e6f74696369617354726164696e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141d1cefdb9f26986204d7b7a1ea1cca728d31978c3822f4ddb7b2deb804107e7e709d6ee451c11633": "0x040300000002000000000000000000000000000000000447756b124765766f7267204e6172696d616e79616e00001e6765766f72672e6e6172696d616e79616e393040676d61696c2e636f6d00000e406e6172696d616e79616e3930000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141d3399c309aa965a62bac995e1409297460e9ba42a01bba4c01e2740fcaea01950402ee0a51f8022": "0x00000000000000000000000000000000000b426c6f636b6368617365000f626c6f636b63686173652e636f6d000000000c40626c6f636b6368617365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141d458f26330fbe568079c5a78a4d63fea543b4c9feece4cc9fcd4aaa2b94ac5761807ea945bd2d05": "0x00000000000000000000000000000000000d53696d706c652053617261680653617261680000186a61636f62736172616836323640676d61696c2e636f6d000010404a61636f6253617261686a61636f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141d4a91a318b45894505a23b381677c7a05a7fe603b023c341b955e2ad03a1398dfd9b85f3c3ef855": "0x0400000000020000000000000000000000000000000009526f73616c696e6400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141d7bc19ef441935b0e03cbf394247c7def2b4020688ed95a6eb61c538eefd92963c448e06a50671b": "0x0401000000020000000000000000000000000000000009434f5645524c4554001468747470733a2f2f636f7665726c65742e636313406164653030373a6d61747269782e6f7267136c756369616e40636f7665726c65742e6363000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141d8084584a6038ad15a31019bfea7696aa19e004843f73dfd52cd68e9c26a3e29861e55adb900af3": "0x040000000002000000000000000000000000000000001d536f6369616c204d6564696120456469746f7269616c20426f617264001d68747470733a2f2f7777772e706f6c6b61646f742e6e6574776f726b000000000a40706f6c6b61646f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141d81ee99b6c993e4ca88fe90d5326a97bf01b05083be12b10bd75699ac4107e65c1de1744d209679": "0x04030000000200000000000000000000000000000000155068696c69707065202d205461704e6174696f6e135068696c69707065204c656e6f726d616e640000217068696c697070652e6c656e6f726d616e64407461702d6e6174696f6e2e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141d8662dbe62ffc1aae145910fae8147b2d325cdbf3a537a12be32f3874815d076d4820bfe97fc63f": "0x0403000000020000000000000000000000000000000006546f6d6d7900000012746f6d6d7940626173656461662e646576000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141db941c89bca443c40ff75e9f6e5eea6579fd37a8296c58b0ff0f0940ea873e5d26b701163b1b325": "0x0400000000020000000000000000000000000000000017536e6f776272696467652042656e656669636961727900001d40776861747265676473666f64726a6b673a6d61747269782e6f726713616964616e40736e6f77666f726b2e636f6d00000e40736e6f77666f726b5f696e63000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141dd4b5b7695b46cd6cd1ffb6ec4677378b3f276ccd6d84e9eb156dc4105eb7e997d9a3604b75420d": "0x040000000002000000000000000000000000000000001259756c696120f09f91a9e2808df09f92bb0000001973746172747365762e79756c696140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141de25a6a6996407006e496c8f09c262953449758d1ac4299ab148c657d2b41eebe82a3c89b576b20": "0x0000000000000000000000000000000000104142524148414d204d554749534841084d55474953484100001c6162726168616d6d75676973686134303140676d61696c2e636f6d000008404162757a7441000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141e1b6c66b87b23257061ec410d336d832362341e983a7e48d0a68898840cf5e94e08aab0fed41010": "0x040000000002000000000000000000000000000000000e4d4f5449462e4e4554574f524b000019406d6f7469666e6574776f726b3a6d61747269782e6f7267124e494b404d4f5449462e4e4554574f524b00000a406172746e61756b61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141e681645b60ebbd6a2b06daf782fa5142e365d1fea0f4a418687f53f201f185bf314c37581677b37": "0x040300000002000000000000000000000000000000000b436967636f6c61496e63074a756261204b0000126472696e6b40636967636f6c612e636f6d00000c40636967636f6c61696e63000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141e7f051b9f457ae97cd5336d50ec2652a90f89edaa708f0952cc7b4e95f16d3e632f4488a4e42423": "0x040300000002000000000000000000000000000000001154686520576869746520526162626974054d696b65000018746f7272652e6d696368656c6540676d61696c2e636f6d0000114074686577686974657261626269744d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141e9ee92c6f734ec81a6c1bbf22454cf45f27a51e603ca304242ddac6f1013af1c9611a01a3910115": "0x00000000000000000000000000000000000b446f742077616c6c657400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141ea22a62585a79397e2880bf2b632d5832327b8237a447ed48dca728c2afa80fca610b3477c35abd": "0x0400000000020000000000000000000000000000000013506f6c696d656320466f756e646174696f6e13506f6c696d656320466f756e646174696f6e1968747470733a2f2f7777772e706f6c696d65632e6f72672f0011696e666f40706f6c696d65632e6f726700001140506f6c696d656350726f746f636f6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141f09c884077a085e9277a4f83997f9675c5554349860c719769f71b634a9ecd0bf63dae7cff0ad69": "0x0000000000000000000000000000000000027700000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141f111b5054813a93cca76bb38fecfc6a03024af5ae8c655763f9eab8fff50719ac43ff0c59b0db5a": "0x04000000000200000000000000000000000000000000116c696768746e696e672d737472696b6500001440736d6f6b6532363a6d61747269782e6f72671c616c6578616e6465722e6672656562736440676d61696c2e636f6d000011406c6962657274617269616e313937330012616c6578616e64657273686174756e6f7600", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141f4321336da7ceadf4d95d4c5c0131969148d3a16b3d95ab3d051771d971a1955d7e745b0a3a4f16": "0x040000000002000000000000000000000000000000000e5044505f56616c696461746f7200001440706176656c64703a6d61747269782e6f726718706176656c2e627574656e6b6f407961686f6f2e636f6d00000b405061756c4241636944000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141fda62d62fc07febbc1deacfc7e5c6e5f0373560c14fbce156ff2a0ed7e208d049ccd985dec85545": "0x040100000002000000000000000000000000000000000d47656e6572616c2d4265636b0444656e1b68747470733a2f2f63727970746f2d6d61747269782e696e666f194047656e6572616c2d4265636b3a6d61747269782e6f72671767656e6572616c2e6265636b40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047141ff990588b6880e4ea9f463588e498f46933a45b834e10273cfbcee3d2e44c8d15072052b669b143": "0x040000000002000000000000000000000000000000000e4a6f656c20f09f87acf09f87a70b4a6f656c204b656e6e791c68747470733a2f2f6a6b7262696e766573746d656e74732e636f6d00196a6f656c406a6b7262696e766573746d656e74732e636f6d00000a404a6f656c4a4b5242000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714203ca9c500dfca7c383d052b70007f5382342d61205c50e75336c57eda16b84db766278420ada25a": "0x00000000000000000000000000000000001144617669642053202d204c6f67696f6e000017406461767363686d69747a3a6d61747269782e6f7267156461766964406c6f67696f6e2e6e6574776f726b0000094062655f64617363000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142057c2e1894e9a819e92823bda563f6821bc3b7af00917608d454dfa89de101414ee0b51ac3ab57f": "0x040000000002000000000000000000000000000000001054755072696d6572426974636f696e0000001a676572617264706c616e656c6c657340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714205f8d2645ea3f1a78dae380bd96b96f6218851036432b300b0fbd65763f51278903922ed7163844": "0x00000000000000000000000000000000001777697a61726420616d69676f73206d756c746973696700000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142067c893ca23078d2899477496b6c390ee6a83d1e533d860d1ed9d3093e9f74103185879c65de25b": "0x000000000000000000000000000000000007726f694c656f00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142074c595e55b069dda0feab49605a4b3cc1344b4d926d20852341d1796566a524d83781bb3111053": "0x040300000002000000000000000000000000000000000a526f62696e486f646c0000001e726f62696e686f646c36393432304070726f746f6e6d61696c2e636f6d00000c726f62696e686f646c3639000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714208f193ba8c59a98eca90d099f8bbfc41261759f0474d3f613d1d83f0a6cb910e15abe2f95cbab4f": "0x040000000002000000000000000000000000000000000844465f5465616d0000001464617070666f72636540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471420a737060c3110efd67d1cbe5bda2d5975130656f1f799398b018fd40a09a5c86b2026f69fdacb56": "0x040300000002000000000000000000000000000000001043687269732040204772616e746564114368726973746f7068657220576172641668747470733a2f2f6765746772616e7465642e696f00146368726973406765746772616e7465642e696f0000094063616c6d726174000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714210b26b3351f6a65f06a37a8628769bafba50c0ecd085dd7fe574949e013ee737c9d6671e59b8513": "0x04010000000200000000000000000000000000000000084b726177696563000015406b7261776965632e3a6d61747269782e6f72671c6b7261776965632e76616c696461746f7240676d61696c2e636f6d00000f404b7261776965635f7374616b65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142132d2b9d69b1260a8300f53d9cda28b136491a9b18b937eae584f7f08fbb6aac29e0ea38e38f864": "0x04000000000200000000000000000000000000000000104c65747261734372697074696361730000001a6c657472617363726970746963617340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142162588dd88d3a33aa48a9775be7af0521acefce054dc7e9e461814dc167a5cabf52aef8534d8249": "0x04030000000200000000000000000000000000000000194c6520436f6d7465206465204d6f6e74652d43727970746f000000126d6e746372707440676d61696c2e636f6d00000c40506f6c6b614d61726f63000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471421a2450da5997cedda95b7b5c463b7d00700c34294c283a21235f8d31377620dcd1a56bf60f3e320": "0x040300000002000000000000000000000000000000000853494d2d444f540000001873696d6f6e6a6f73656c6c40686f746d61696c2e636f6d00000a4073696d6f6e787065000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471421c356d070c125b4b4c0a3bbd567cf9fe5e5ad607debd6e89fb31a294288f94ab2490aebd03ed306": "0x00000000000000000000000000000000000750617265746f0750617265746f1368747470733a2f2f6a6564736164612e696f00157265616c70617265746f40676d61696c2e636f6d00000c407265616c70617265746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471421f2e65cb5d0a0be1c95e1f32ddc271b9161da11a343f31619b38dc9f3d966d2624376549ae1da18": "0x040300000002000000000000000000000000000000000b4f5253454e5f5345414e0d5365616e204f5265696c6c79000012736f7265696c6c79406f7273656e2e636800000a5365616e4f5253454e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714222fa12fb4681a4d0e47ff458bb2f80c4bc89a6a7bd022a555e9c1bc719726f475f8a1841428832f": "0x0000000000000000000000000000000000097261676e61726f6b0f4e6568656d6961204b72616d65721668747470733a2f2f706f6c6b617269636f2e636f6d1e406e6568656d69615f736f72616d697473753a6d61747269782e6f7267186b72616d65726e6568656d696140676d61696c2e636f6d00000f404b72616d65724e6568656d6961000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142230ad1e2bae6213b218a07558705ee2b86d4f2988e708454452b7d2efc237b95e139630e04f6116": "0x0400000000020000000000000000000000000000000009437269735f5061700000000000000b40437269735f50617038000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142231d8f9634359403e2a0c8a13611c5eb6921612ecd1a2d7d6c1525a3723956c1c6193e3e2901f3e": "0x0000000000000000000000000000000000134b657672657620506f6c6b61646f742e4a5300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142237e0f9b0ff0758161378b7c016bd381841b01d1f20e66cc5ba9ead421638045c5ba754dbd5a10c": "0x0403000000020000000000000000000000000000000009636865746861636b1143686574616e7920426861726477616a00001263686574616e7940736974612e6661726d000009425255484457414a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714225b1e95c870e114d86cce0d391087696b6123aae807e8da8ae365eeeebd43f857c6b5c4eee76975": "0x040300000002000000000000000000000000000000001c536c38746520506c6174666f726d2028546f6b656e5472617878290f416c6578616e646572204d6f737300000f616c657840736c3874652e78797a00000c40546f6b656e5472617878000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142264b5df53ebf1f498f37af89509e846463f6874745b6f8c4e02a941d2c9a83b7d60d90ad5b81863": "0x00000000000000000000000000000000000d706570656172617563616e6f0d4a6f73652042656c6f7373690000166a6f736562656c6f73736940676d61696c2e636f6d00000e40706570656172617563616e6f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142265233f75ee737e4dcb5e6cb19c4034a66e1f5e10be70f0ac356e9beff459e1b334ce7351a229cf": "0x04000000000200000000000000000000000000000000086c75636173766f104c7563617320566f67656c73616e67000000000009406c75636173766f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471422b354403ceb2cc3ceeefbffd1cab85b87e1469bf23bbd35b83fd508cd41e17b0fd3a81b8ffe4467": "0x04030000000200000000000000000000000000000000105468655375737365785472616465720e5374657665204875626261726400001a74686573757373657874726164657240676d61696c2e636f6d000010746865737573736578747261646572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471422c52d6bb34054bfa8fc69f4bcc2ee09a91d633b59b82ca325027ea9567b1b47fa5912c4edeb9d6a": "0x040300000002000000000000000000000000000000000963617274616769610000001463617274616769613040676d61696c2e636f6d00000a436172746167696130000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471422f8d0d571261afe14d6965cd9bcce81dfe785bd7642bbaf4caa8f203de34459ad12d7a9e501bb6e": "0x040300000002000000000000000000000000000000000c50726f666974206c696e6b0000001b67617263696170726f6475636572393140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471422fa92e09f76627f5a2160296c65f838252b9da9f51b9691950ece465e1a71b444d286d91316dd24": "0x040300000002000000000000000000000000000000000b526f6d616e204d2e4b2e10526f6d616e204d2e204b656d7065721868747470733a2f2f6a6f696e7765627a65726f2e636f6d174072656b746f726d6f6f6e3a6d61747269782e6f726713726b406a6f696e7765627a65726f2e636f6d00000c4072656b746f726d6f6f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471423484ecafdeafb8d3c84767978ada6355ac3719ea8f978244b16d7e8c00552c3a189760b55eeb404": "0x040100000002000000000000000000000000000000000a4d616e74726144414f0a4d616e74726144414f1b68747470733a2f2f7777772e6d616e74726164616f2e636f6d2f0016636f6e74616374406d616e74726164616f2e636f6d00000b404d414e54524144414f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714235333782a45d3df229f2896c2bd6f30162c9cc0f5899432515f2cffed36a4dc6b42d48bd2b2910d": "0x00000000000000000000000000000000001044454144424c41434b434c4f56455200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142383a648795d6d96f093fdd7214faca57e8f0a10e8ea4c03c4602af97676242b933a757824ca8335": "0x04010000000200000000000000000000000000000000054e4f56590000000e7374616b65406e6f76792e707700000f406c6f73745f696e636861696e73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142398aeb1ef43f542c44cd5285e160a2184f3f5801cdce8517ebffdb591177acb17cff2db2ffd9371": "0x040000000002000000000000000000000000000000000a4541524e5354415348001a68747470733a2f2f7777772e6561726e73746173682e636f6d16406561726e73746173683a6d61747269782e6f7267146561726e737461736840676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471423bd63bc18ca71967478e4ccf706567c80242c11e0c11312b7b8ed3db3af63eebd94167aba9ac1a6": "0x000000000000000000000000000000000010504f4c4b41444f542042524153494c19506f6c6b61646f742042726173696c20f09f87a7f09f87b71e68747470733a2f2f6269742e6c792f706f6c6b61646f7462726173696c000000001140706f6c6b61646f745f62726173696c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471423d0615e8f4e163856b5a7ba509762854581e36acc727a27983e3005731931bc31be21c1df4b401f": "0x0403000000020000000000000000000000000000000010504241205347502057414859554449096f576168797564690000136361726e65677a73407961686f6f2e636f6d0000096361726e65677a73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471423d5be68d79bf6613417721ef067cbae6a29f3ddd4d5df9659d5f007e23152b96c2e5b939af7fc68": "0x04030000000200000000000000000000000000000000094f6e65426c6f636b00000018796178756e6368656e3139393540676d61696c2e636f6d00000b404f6e65426c6f636b5f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471423eae731efa0ee7a3aa151f9bdc2bb6f8e27d492446ace702955f1eed20efa5ee31a59dafdf3354e": "0x040300000002000000000000000000000000000000000e686f6676656e736368696f6c640000001c6a6f616368696d2e7269747466656c647440676d61696c2e636f6d00000e686f6676656e736368696f6c64000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142411ce5d2c7e203b500427a67b8b8dbf1b569c99d1d0a3662359b5aeaa4cbead3d603857271a3670": "0x040300000002000000000000000000000000000000000841754167656e740000001574775f70726f665f6e654070726f746f6e2e6d65000011404d6134363134333938323637383737000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471424883d27e305ffe5f89d4c543e6cd62e62136614f7b98a423cedb9fd863584a2f4ff887259432437": "0x00000000000000000000000000000000000963796265726e657400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142492dee6806c086baef6619eea08f01aef7e47d460f0730b02c075e446308892e55af56af1572168": "0x040100000002000000000000000000000000000000000b696e74656772697465650e696e74656772697465652041471b68747470733a2f2f696e74656772697465652e6e6574776f726b0018696e666f40696e74656772697465652e6e6574776f726b00000f40696e74656772695f745f655f65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471424ab767106e162e17a9749cfb7256e7e48edca7a6911018861ec6e26d9eb2c81bbf037628fefcf04": "0x040100000002000000000000000000000000000000000e527562792d4e6f6465f09f928e00001240746174616e3a6d61747269782e6f726719527562792d4e6f64654070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471424c51cd1777082130630cade4d018323ce09989df4f43749469506631ec446b3322c5b029283ef03": "0x040300000002000000000000000000000000000000000e43727970746f2042616e7465720e43727970746f2042616e74657200001a706f6c6b61646f744063727970746f62616e7465722e636f6d00000f4063727970746f5f62616e746572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471424ddab62ae2ee0f5da1bdcd2db52642b3be2dd7d474a865c85ef5db7b76d6732b132c80de1750d28": "0x040300000002000000000000000000000000000000000f42726176655f4f6666696369616c0f4361726c6f73204163657665646f00001e69742d736572766963652d706f6c6b61646f744062726176652e636f6d00001040417474656e74696f6e546f6b656e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471424df16db48272ad68c085e6bde4ac25702f54f46fa3c1b0a6170bd346103c2c6339911e00306a054": "0x040000000002000000000000000000000000000000000b56414c49444154484f520000001576616c69646174686f7240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142506f1a04a86e00ea2793a1de617bc5ff2e0fc73334fb1792f1e6849352ceeab2cfe0a4f43b77608": "0x00000000000000000000000000000000000b4c75636163727970746f0000000000000e404e4654736172656d796a616d000b6c75636163727970746f00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714251429e29e5bdc418aff8f8e6f78afd4fd4d5c59242373cc0e5ecbd5595578ad042e6cf97d53ea1e": "0x0400000000020000000000000000000000000000000018506f6c6b61646f742045636f20526573656172636865720d506f6c6b61646f742e455249002140706f6c6b61646f742e72657365617263686572733a6d61747269782e6f726715706f6c6b61646f745f657269403136332e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142522dbe7f104bee21ccdf130995bda0c195a1c383532e4704f289b31d3923c18f76ff0ef6626f21c": "0x04000000000200000000000000000000000000000000084d616368616c610000001c6a6f736875617a696f6e666f7274756e6540676d61696c2e636f6d00000e405468654a6f73687561736f6e00096d61636368616c6100", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714252e878992d864fb960d75eab8e58bffcedf1fa51d85e2acb37d107e9bd7009a3473d3809122493c": "0x040000000002000000000000000000000000000000000957444d41535445520000154077646d61737465723a6d61747269782e6f7267136d6f6c7465732e6740676d61696c2e636f6d00000d404d414b53494d3738383736000b6d616b73696d3133343900", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714255d8383c400c461dcc1b944a156b60b0adaa4a2a59dd97804c1ec4fca4751a3efb6bc0434ddf051": "0x040300000002000000000000000000000000000000000e4265616d737761705f5465616d00000011696e666f406265616d737761702e696f00000b4265616d73776170696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471425b20afe8011ef7d040d1324a05bb9a0b86d9eabd01245ffcf277f4611f9750dfe465761481ba053": "0x0400000000020000000000000000000000000000000011416c6979756e2d76616c696461746f7200000012746f6e7473696e40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471425d12cd63f2c7151f66dcadc693afcce9876c486304d77941b2084677b0e9b7804935da272dd3625": "0x00000000000000000000000000000000001342617262656ee280997320536c617368203200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471425d728fd5c84d7cd8c5671a38e6cc4175311cdb5280fc86d2ad25d197c14682153a30b406a029e75": "0x040300000002000000000000000000000000000000000c6b616b6f6f7a617669616e0f4b616b6f6f7a61205669616e65790000166b616b737669616e65793140676d61696c2e636f6d000010406b616b6f6f7a617669616e323536000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471425ecccdf058700718b63cf648fa2a43a82671762a02da41dc6abbd1e62a376b3af7c2d09abf5120c": "0x040100000002000000000000000000000000000000000a436861696e5361666517436861696e536166652053797374656d7320496e632e1568747470733a2f2f636861696e736166652e696f0012696e666f40636861696e736166652e696f00000d40636861696e736166657468000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714261d16493407cc0ac587192891bca929ce36a9004a6b1beca795e27a0dbf10b14d68cdaa962f15a0": "0x00000000000000000000000000000000001c506f6c6b61646f74204d65786963616e20436f6c6c6563746976650000001163646f746d7840676d61696c2e636f6d00001140506f6c6b61646f744d657869636f5f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142622a69ca095df152e33a240cb3adb22310f77b1d8f6e105e325077f4b35ffd8bf35780221fcc1ef": "0x0403000000020000000000000000000000000000000005566c6164001868747470733a2f2f6769746875622e636f6d2f72636e79114072636e793a6d61747269782e6f72670a6d654072636e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471426237f16f577beb4108750788ae77403993574ef05638bfe9cff80729c2ca6ba310f6aa65dd0b901": "0x00000000000000000000000000000000001450617261636861696e732041756374696f6e73114d69636861656c204c6120466c6575720000196d69636861656c406c61666c657572686f6d65732e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714264c10224bdb241eee938b6b034193875fa93d617a9543084ef41f40790898fa1eb4118e6361e950": "0x040000000002000000000000000000000000000000000946726f67f09f90b8001968747470733a2f2f66726f677374616b696e672e636f6d2f184066726f677374616b696e673a6d61747269782e6f726715696e666f4066726f677374616b696e672e636f6d00000d4046726f675374616b696e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142650651beda2cb6ffc644cc7758af1bf8dd4974d1e920fde4e1cc71b5297571368559b48f180917e": "0x00000000000000000000000000000000000c733066746d616368696e6500001840736f66746d616368696e653a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471426968cb24b23a08fb64a86ea0e91047a3ffbbc68c36ea9ab318ee06c8bb4a354dd3a716956c9eb55": "0x040000000002000000000000000000000000000000000f4155524f5241205354414b494e47000018407374616b656175726f72613a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471426a2abbc21cd770cd88a4b558274e57737ffd3fc9741848596199a29d77fe511804d20810cb76050": "0x040000000002000000000000000000000000000000000d5374616b65506f6f6c323437000019407374616b65706f6f6c3234373a6d61747269782e6f726716696e666f407374616b65706f6f6c3234372e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471426e7f4ebcd01d4dae6dd0a94c6dc8091d357cf0092af2f8e108daf432d02d27dcb7ffd019d98a509": "0x0403000000020000000000000000000000000000000007496e64696b6f00000019696e64696b6f6a6f68616e73656e40676d61696c2e636f6d00001040496e64696b6f4a6f68616e73656e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471426f6a2ed87b420011ad2ce70e114cbd2d3611571bb2c5b15b2186460de419ca0c774305b62f5cc33": "0x0000000000000000000000000000000000076461696167690f42617275636820466973686d616e00001164616961676940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714271fca71d49496c2ccab328611b1ace9fc6fa0e67bab07922427c5cf6f0291c5b5235e6868f9f310": "0x0800000000020100000002000000000000000000000000000000000d4c4f43414c43525950544f53164c6f63616c457468657265756d20507479204c74641968747470733a2f2f6c6f63616c63727970746f732e636f6d0019636f6e74616374406c6f63616c63727970746f732e636f6d000010406c6f63616c63727970746f73656e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142730809323ff3aebc29ac3c17a8bb1145487c9f80bc86e6982b95bfb522b6e191ab95ad8f248f07c": "0x000000000000000000000000000000000005486f6f6e09486f6f6e204b696d0000106d61696c40686f6f6e6b696d2e6d6500000b40686f6f6e737562696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142777a559c125897fb8897a746ceaa53376946a3da353c1c987df8c0caa4395ac0eaf0e6c74874054": "0x00000000000000000000000000000000000b4a656c6c6965644f776c00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471427c5cc778df57308e8bfaeea37b4d2f39cafa194a0758805af9a1b9253dc27dc2b7880d06f312d32": "0x00000000000000000000000000000000000f4b696d7320436172204e6f74657300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471427ce23d8a102db2306e6e94eaa021535d3632154bb1c00bbc511d692a33bd5816d518367421932f0": "0x00000000000000000000000000000000000c526567656e63792d3030381757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471427d82cb3e937516ba0aa6672877b1924e7a86c9ec99bf1cb93797123b14ec80c0effe2a8685e1002": "0x0403000000020000000000000000000000000000000009444f54414d494e4100000016646f74616d696e6134323040676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471428047368e108b3b06ef40ac7b3e092d0eca77ad072cd317683ab9d24aca4025788421d4276527d57": "0x040000000002000000000000000000000000000000001ff09f90b05f2e2de3809020435259505449445320e380912d2e5ff09f90b00000154063727970746964733a6d61747269782e6f72671468656c6c6f4063727970746964732e6c69666500000b40637279707469647338000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714285588f89e26dc6c2ab6a0d5885b1debcb5f089ce73d3abe16792cd01d63d788609f8d859fc1fe01": "0x000000000000000000000000000000000017f09f8cb74b494e4420535452414e47455220f09f8cb700000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142895ffb6b45282d85a093ae9a82c3b93551017c2f3e6eb796a3cc532da826a9f30cc51d28cc8a601": "0x040300000002000000000000000000000000000000000a476572616c726f636b1f4765726172646f2052616661656c20c3816c766172657a2052616d61796f00001c6765726172646f2e616c766172657a313840676d61696c2e636f6d00000b40676572616c726f636b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714289ca7552a5b54db20af269d7bab673454a2f802bff3917cb7e693fb9857affc0be384560cd5a2b1": "0x04000000000200000000000000000000000000000000085a65726f44414f0000000f6c6565406d656c6f646f742e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471428a876cad533fcb536677e8e5d84c1a25bdac951e9055abff3df4c7a656abd841197a8726ff29a3f": "0x04000000000200000000000000000000000000000000094c656e677569746f0000001530786c656e677569746f40676d61696c2e636f6d0000094030786c656e676f000c4c656e676f37233236363400", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471428cc3c2610e32eb246d9719d59eedbef886f837bc68f5afe35eb594ed7546c9507c5d55992c9f035": "0x000000000000000000000000000000000009506f6c6b61726f620e526f62657274204d2048656964000013726865696430373740676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471428d20381664d55c37099f4d19d4f37dea1e2ad9be9f4948f837fc391cf9e9af0b458e960dd102a76": "0x00000000000000000000000000000000000762616e6b343513416c656b73616e6472204e6967616d616576000016636f746d6f744070726f746f6e6d61696c2e636f6d0000084041524e5f3838000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471428db6c1e91cbc43002385caf9a08b92ca458a0b817a8cc303cefd5a0c6e108cb939e04242b9e007d": "0x0400000000020000000000000000000000000000000014537562517565727920436f6e74726f6c6c65720000001d6a616d65732e6261796c794073756271756572792e6e6574776f726b0000114053756251756572794e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714293cf013489f4e2d9afa0d65d7fa0d1b9240ed167102fe95be2b87c6d585917f6c4ee40e45cb6e5a": "0x000000000000000000000000000000000009477265617468697400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142959c4bc29b0128ea4496cb0c04854a6634444b262642d22f70b361cf9e45842bae6e92b42294d19": "0x040300000002000000000000000000000000000000000a43757272656e63797800000014656574696365706f7440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714298645876ad18694bee606a879569e5675d379121f832a04d3e378618ee6d12244646d2d00bdf420": "0x040300000002000000000000000000000000000000000a6a6f69655f70696e6b044a6f790000146a6f792e6f7369766540676d61696c2e636f6d00000c405465616d5f46656f6f68000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714298ad014cd66be3404aec61601a366f706a5dda2bdb5987555ebea5347bf32080bdfde6f33849d05": "0x0400000000020000000000000000000000000000000009726f636b6e6f646500001540726f636b6e6f64653a6d61747269782e6f726715726f636b6e6f6465687140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471429a96dcb25143c65cef75691f5bc9f7552778942bc996e7dc0796a3b431d6894e4ee36c168168222": "0x040000000002000000000000000000000000000000000a736f72616d697473750000001974616b656d69796140736f72616d697473752e636f2e6a70000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142a251205d65518b0e6b912626c9dfa3cd9e65b4412b19eb9d123edb1aa22d492a58a88091c483a7a": "0x04000000000200000000000000000000000000000000034d4b0000001b6d69636861656c40636f6c6f7266756c6e6f74696f6e2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142a3ffa5c696e8f33fc5bf1b734c6224841177fb6111b9fc879c86cabd506a7c5b5f84bd66bf1d37a": "0x04000000000200000000000000000000000000000000115468652043656e7465722050617274790000001974686563656e74657270617274794070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142a65c9799fef41adec3d304da58d77a4384fced1c59dd3cbc9618dd1ec92e6feb64293147272a21d": "0x0403000000020000000000000000000000000000000009526f636f536170650f526f636f202f20446f7463617374000017726f636f63727970746f343140676d61696c2e636f6d00000a40526f636f736170650009726f636f7361706500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142a679b6bc1ea7234ccb38aa8821510b7cc609cd55fd19c0f9c3594d49fe8db355bb2e7fb2324c948": "0x04010000000200000000000000000000000000000000096c7578382e6e65740d496e66696e697465204c75781168747470733a2f2f6c7578382e6e65741540616d6133313333373a6d61747269782e6f72670e696e666f406c7578382e6e6574000009406c7578386e6574000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142a6803733e06edd5682740b90bc8be47e739a0b66b3bd84eac0962a708078b238c8c08945fb2024b": "0x0000000000000000000000000000000000044d2d4f094b656e6e79204e670000146e6f7665783839303840676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142aa976bb8726f15e2e5157fa386365bb2a60fc6f415bf070acdae47078be1396e3dc47ccb837d459": "0x040300000002000000000000000000000000000000001d4e696b6f6c61204d616e646963207c204d565020576f726b73686f700e4e696b6f6c61204d616e64696300001d6e696b6f6c612e6d616e646963406d7670776f726b73686f702e636f00000a6d616e5f6461613231000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142ab0d54695f8d579c6515b55efbd99301b103862a649266e6f4a08ed9f2f9e653faa4629710dd6d2": "0x00000000000000000000000000000000000d746f646f646563726970746f0d746f646f646563726970746f00000000000e40746f646f646563726970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142b031c075ea343ce443c76dcde19df9387486ded7845c6d85ec2a4c17f38f8b1e7a0a14de7968d7d": "0x04000000000200000000000000000000000000000000064b414d454c0d6b616d656c7374616b696e671968747470733a2f2f6b616d656c7374616b696e672e636f6d19406b616d656c7374616b696e673a6d61747269782e6f726719636f6e74616374406b616d656c7374616b696e672e636f6d00000e406b616d656c7374616b696e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142b2d37123329b9ba08bf2f84a9f268c10adb63f3446fa65d680c7574d794c4115b605ad40d26cc42": "0x040100000002000000000000000000000000000000000e4252494748544c595354414b45001b68747470733a2f2f6272696768746c797374616b652e636f6d2f1a406272696768746c797374616b653a6d61747269782e6f72671a636f6e74616374406272696768746c797374616b652e636f6d00000f406272696768746c797374616b65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142b2d468ee1c0d6cf28ee86c98db79e06b3e25509ad654b002720aad45713da761bae4f0ae813387d": "0x04030000000200000000000000000000000000000000054761626f000000216761627269656c6e69636f6c6173676f6e7a616c657a40676d61696c2e636f6d00000f4063616d62615f6761627269656c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142b2f2f9bc8a6dc303020182b284d6ac805b1382502a771eb140c80303e128cfefa992a62160a7728": "0x000000000000000000000000000000000008426173696c696f0f4d617474686577204261726e65730000186f6e656e6174696f6e3134393140676d61696c2e636f6d00000d40436174616c797374557365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142b53fe0d3b6de98ab40270f026c5a4841078cc77a050c780f88e5dee8436e569972c93fa56d9a020": "0x00000000000000000000000000000000002077616c6c657420706f6c6b61646f7420706172612070617261636861696e730a66646e74732e646f74000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142b6e818ad6aaf2194425333101d35f55eccfe16b28f0e65007a0076ca42eecb050405f6e0ac4737e": "0x040000000002000000000000000000000000000000000455414900000018616e6172636879636861696e734070726f746f6e2e6d6500000f40616e6172636879636861696e73000f40616e6172636879636861696e7300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142bac4814c142df16f89d49e97071dfd7f8971ed816c0fc60a34aa6a8d0b1af8f7c6922659dfbd789": "0x00000000000000000000000000000000000d63796265726e6574776f726b00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142c08340921a3d8d9981e0c8001f0d80ef450c0c0561f9e4b22337af479023b9d79851381ffbe0348": "0x0000000000000000000000000000000000083130313538313400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142c09493faa9e924efc5d04e7ff3965c8285a2c23aa573117deeed886bbe5e3be0974f1cf0a2ff216": "0x00000000000000000000000000000000000b536861776e2059616e670b536861776e2059616e671e68747470733a2f2f6769746875622e636f6d2f526f6d65726f59616e670015736861776e40706f6c6b6177616c6c65742e696f00000c40536861776e5859616e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142c3ee724586db259eaab0cb55c147ffaf184a4c00513e85f6d5bb6416994fbdd0dd168f3c59a291b": "0x040000000002000000000000000000000000000000000c437970686572204c6162730000001d6379706865726c6162732e636f6e7461637440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142c4d0f13e5d38cd42a813834d9532d047dcc1c87a3abde12724abfa8b681edaf89d59dc4d69d3e60": "0x040100000002000000000000000000000000000000000c686f6e65796261646765721144616e69656c2042616572697377796c00002064616e69656c2e62616572697377796c4070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142c5adc2eb80036fe5e217ee74a4735c3e66dff90cc3ec8dd2185411ded94590173ec8cd773cd1071": "0x0000000000000000000000000000000000114a616661725f436f6d706f7361626c6500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142c85c1028c0265f1d0ee5462aa1f69fbb13d1bbba3fe774167ce82a5cd36da733f2f2db0e7e4ad4d": "0x040000000002000000000000000000000000000000000d5542494b204341504954414c00000015636f6e74616374407562696b2e6361706974616c00000d407562696b6361706974616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142c9114f608ac39a9a45d1c3020272172fdb2238d4b68c1f8d6178dfbf4a80404dcd01da024dac33d": "0x0400000000020000000000000000000000000000000009476162654b6f696e0000001c6761627269656c5f626f6e75676c6940686f746d61696c2e636f6d00000a40676162656b6f696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142c953664c84e500ad4d71d52cefd70e1668f6b908e3196d664e2fd5e541905966c514ab660b86966": "0x040300000002000000000000000000000000000000000757616c7669730000001277616c7669733740676d61696c2e636f6d00000877616c76697337000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142d1be0f377b4cffe7c1207030eebbff1aa8d1d3fb13ef4a44251f81480ff627240d45a0e4547c639": "0x040000000002000000000000000000000000000000000b5354414b4543524146540b5354414b4543524146541768747470733a2f2f7374616b6563726166742e636f6d15406e3174726f67336e3a6d61747269782e6f726717737570706f7274407374616b6563726166742e636f6d00000c407374616b656372616674000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142d51e6ba205c01881e8f1a5238c8472f9059d189db0d25ae5bfd6380b2ec5325d302689d96bfb479": "0x040300000002000000000000000000000000000000000e4e6573746f722043616d706f731e4ec3a973746f72204e69636f6cc3a1732043616d706f7320526f6a61730000196e2e63616d706f732e726f6a617340676d61696c2e636f6d00000b6e65735f63616d706f73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142d5da20efbc496e3401ddfae657e8222bce22c9787be36b1ae9906840546c992fa4b4b9201be1776": "0x00000000000000000000000000000000000d496e73616e654272612e696e05416c79781568747470733a2f2f696e73616e656272612e696e0012616c797840696e73616e656272612e696e000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142d6a11aa67106d2d8ae51d44bfb6e10f175e6e86e829e1013589f4f1049b3a98f648cd1a9d8d3007": "0x040300000002000000000000000000000000000000000b416e67656c696e6138380f416e67656c61204b616b69697a61000019616e67656c612e6b616b69697a6140676d61696c2e636f6d000009616e67656c615f6b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142d70dfa84c8df520b8b3f33a5d67eaa3bd04013795e3f6ab9fa3d6851ab3005ba7cacdde440eb053": "0x0000000000000000000000000000000000066a657375730d6a657375732067617263696100001c6a6573757367617263696167756169646f40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142da517d91ffe519dea08e3af45c5a91e4f22bf4e5e6491e5927538296954e4fbb450bd5db3ec543e": "0x000000000000000000000000000000000015486174656d207c20446f7442656c3361726162790000000000000e40446f7442656c336172616279000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142de8d0b38098bb8dde607c18f59d4b90b26ee90bb1e7fe10d4ad22c871ea17414ddc9a9d355c255d": "0x00000000000000000000000000000000000a42615f73733335383800000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142de96dce1f9cc98a04aae2b4b67be2cfee31d6cdc0f777f4b8e6ba330f93978f9d1aefe9af61374f": "0x00000000000000000000000000000000001d5374617368204163636f756e742031202d206e6f6e206c656467657200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142dfcf93c305276e4327f10a5aa41f8e64ca4dc6cb07bf883e19354851979be53c77d1d65a66fa765": "0x040300000002000000000000000000000000000000000f78696e7869616e6764657369676e0978696e7869616e67000013646f6e677831313140676d61696c2e636f6d00000e78696e7869616e675f6d657461000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142e0a85ddf8e6a3e3d24f805ca7bafb11cfdb0ace585a9d0e6edf83878dc7b42946c33c2378211464": "0x0401000000020000000000000000000000000000000007414e414d495807414e414d49581468747470733a2f2f616e616d69782e746f702f1440646270617474793a6d61747269782e6f726714616e616d697840706f6c6b61646f742e70726f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142e346820d7be398158598801d9659099c1437f879e3003c0461affdcb378e9462824a1b7e5cd4c65": "0x040000000002000000000000000000000000000000000661626761720000124061626761723a6d61747269782e6f726716616267617262617273656740676d61696c2e636f6d00001040416476697a6f7254727573746564000a676172696b3537313500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142e764a24050b5fa2022be0daf8bcff3056f4e4e1c4296d436184ec10c21a4f9bedc57d2778674e7c": "0x040000000002000000000000000000000000000000000852686f6d6275730000001a72686f6d627573706f6c6b61646f744070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142e7e56460089fc7a54c473bf199d05b878ab34e9a37d17d0a8bf70498edb5c759672e984fa38b432": "0x040000000002000000000000000000000000000000000f4170706c6965644243204c61627300001a406170706c69656462636c6162733a6d61747269782e6f7267186170706c69656462636c616273407961686f6f2e636f6d00000f406170706c69656462636c616273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142e8429dbeda5cc3f2042cbe5632c5f2d92c0f91144fb9f02541d75f721881337e5ba8d5f13691506": "0x040300000002000000000000000000000000000000000f5472656173757279204775696c640f5472656173757279204775696c6400001874726561737572796775696c6440676d61696c2e636f6d00000f40436174616c797374537761726d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142eb239de2237ff4464c92fbc08d8d3594de7a9eeae4ef24891cf7e8330faf07ed804cd0045e49e6d": "0x040000000002000000000000000000000000000000000a53686f7274795f33350000000000000a407273686f7274656e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142eb8e564ba3255aa8ed07ac253ff90568509ab1e98620aab2d27739c088425500b1555c200fc5a6d": "0x0000000000000000000000000000000000085765625a65726f001d68747470733a2f2f7777772e6a6f696e7765627a65726f2e636f6d2f000000000d406a6f696e7765627a65726f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142ec71cb46d9dac8c4061fa23b882bde2f44bbbf50b167a0540bd1f34cdfa015199e099ff610e9a54": "0x04010000000200000000000000000000000000000000094f4e454352595054094f4e4543525950540015406f6e6563727970743a6d61747269782e6f726711726f6f74406f6e6563727970742e696f00000b406f6e65637279707432000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142ef8fef75807c63dc8d6404482643a279bc4a12ccb233ae1be9cc911b65313415ae1f6e99ea6351d": "0x040000000002000000000000000000000000000000000950726f706f736572000012406a6465746f3a6d61747269782e6f72671a6a6f7365706840636f6d706f7361626c652e66696e616e6365000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142f05080e250453cc88609d041e06db09ff8a0bea7122bceb122630591856f71285ff8215cdcaa95c": "0x040300000002000000000000000000000000000000000c45737562616c657720412e0f45737562616c657720416d656e7500001b65737562616c6577627573696e65737340676d61696c2e636f6d00000a65737562616c657761000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142f3b5813e65122054cbd158ed5b37e237e8a790a020d9a67032b56318dd7f674de1e5a002f8b5a52": "0x000000000000000000000000000000000004446f7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142f4bb305762822d5d4f47497fa575d0be94b1dc8833b9ffee17058bdcb32e4aaaf5f7d846afdbc11": "0x00000000000000000000000000000000000e4c6972616e50656e64756c756d001b68747470733a2f2f70656e64756c756d636861696e2e6f72672f00146c6972616e407361746f7368697061792e696f000009404c6972616e416c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142fb53ad11aed254b52582c7985dc8002e718841421ee5059a92ed9a4e38c0c33365ee196c96d5e0a": "0x040000000002000000000000000000000000000000000a4972696e615f56455200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142fbc5fa5095a463cfa9fb4eccab4919417e2a6072c77e55b7d1b69a9682629a09953ce699260b564": "0x00000000000000000000000000000000000a4669676d656e742035001368747470733a2f2f6669676d656e742e696f000100000b4669676d656e745f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047142fccfd4d73aaadfef8c309d6472fab1a89e619867d57934db759e5d76d63b9e67968e36f02787335": "0x04030000000200000000000000000000000000000000084d72457863656c134672616e636973636f20416c626f726e6f7a0000176672612e616c626f726e6f7a40676d61696c2e636f6d00000f406d6973746572657863656c7733000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714304630d144e59358fe1f35d3b712d9b87e9dce926d8772267bbbaeb776e9b2a8615b29d6594c4621": "0x04000000000200000000000000000000000000000000076b734d6f6f6e000013406b736d6f6f6e3a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143046396b7c34c0b3eec4bd650a277342ebba0954ac786df2623bd6a9d6d3e69b484482336c549f79": "0x040000000002000000000000000000000000000000000664617678790f4461766964652047616c617373691468747470733a2f2f64617461776f6b2e6e6574124064617678793a6d61747269782e6f72671264617678794064617461776f6b2e6e6574019484ae9c0de026adf6a15a538f550699515c84cb00000664617678790000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471430731e299628f086987179a4c458ad0333ddf074031278a4e40cbf3d07c3c93bdaf1d4e720e25b70": "0x0400000000020000000000000000000000000000000016506f6c6b61646f74204e657773204368616e6e656c000014406d6172796c65653a6d61747269782e6f7267156d6172796c6565626f7840676d61696c2e636f6d00000e40706f6c6b61646f746e657773000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143081f00d680b8e9f7824673613d28fd0e00381e7a257a670c9fe3caf566f5ea3b4eae0190c3fd06a": "0x040000000002000000000000000000000000000000000a7374616b657468617400001640616e647265697369643a6d61747269782e6f7267167374616b6574686174687140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471430a9041ab751137e14b3cf31ec26dcd596285c2afbdd19579b3634a78bc987dad71d91ecf436847b": "0x04030000000200000000000000000000000000000000114a617669657220456c69205265796573194a617669657220456c6920526579657320436162726572610000176a6176697265796573636d3540676d61696c2e636f6d00000d404a617669426c616b654352000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471430abe05ab6e2104d2e84d8f02b52516e31d4e588ef866a26f721896ea42fb351a63d58a418cf9d42": "0x00000000000000000000000000000000000d43727970746f64696e676f3200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471430d3bc09d2bfcfb50c7f10142a81fedec753f7c556f5b93a400c280805e7fcdff668719637b13434": "0x040000000002000000000000000000000000000000000a47656f7267695f5053000000126a69673737303940676d61696c2e636f6d00000b406a696763727970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471430ef5160a1f04948b2e8980baff3a96bacc15e3f314bdfa64112a67afc736beee37f5d2e1b907462": "0x000000000000000000000000000000000006576574657a0014687474703a2f2f7777772e776574657a2e696f0d576574657a2077616c6c657400000006576574657a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471431053eabded94fa87600c0c74304812e4928a503408a68c782c69a6af2fca55fcd9e48e095b44863": "0x040000000002000000000000000000000000000000000ff09f90a0205354414b4546495348000013406d34646269373a6d61747269782e6f72670e6869407374616b652e6669736800000b407374616b6566697368000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143118317c1a74f34820a72747f8fa2e88160c65260e1dd461e775a75e05fd60c0ee07c929411bf611": "0x0000000000000000000000000000000000094b6574757432353600000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143185ee0b1cadbcee56ca8c23e3988aaddd0fe5deac26eef5661547ce8ae71c4fbe07014b7052e71a": "0x04030000000200000000000000000000000000000000064d6174656f0f4d6174656f20436861756368657400001863686175636865746d6174656f40676d61696c2e636f6d000009404a5a656974616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714318ee29c136c23ba24d6d7cd9a0500be768efc7b5508e7861cbde7cfc06819e4dfd9120b97d46d3e": "0x000000000000000000000000000000000012537562736f6369616c204e6574776f726b001a68747470733a2f2f737562736f6369616c2e6e6574776f726b000000001040537562736f6369616c436861696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471431c949d064ba830cfc60f1dfd95340dfe375e3e49d846e35283e3fdfcce5a83aa09c8a4718d64f7d": "0x040000000002000000000000000000000000000000000b4a6f657267537475647900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471431ef141127f82186d45548e42d7f5d5aa35ffc16ff29396c99936f0c1eae84d452f1daac87c3c566": "0x040300000002000000000000000000000000000000000c446973747261637469766510446973747261637469766520496e630000156e6174654064697374726163746976652e78797a00001040646973747261637469766578797a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471431fa432a48d8271ecc17542453f0d5e9caa6e8351612ecd3fe895c023eb93cbccf0ab0457728b87c": "0x0000000000000000000000000000000000074d75727068790e476572617264204d757270687900001567736d2e6d757270687940676d61696c2e636f6d00000d4064657370657261646f676d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143203618ac806a5094866c7c5c93a0984394d5279fbbecb07c1e48368b042566ebb632c2055698b02": "0x04000000000200000000000000000000000000000000084e6f646561737900001540637261626265616e3a6d61747269782e6f72671577656e7a686968616f406269746f7069612e636e000009404e6f6465617379000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714320eb468db7ea3c7aeb9fc068a3340edead118ac3cf3decc6743724cd5fb11edebaa98b540a08f07": "0x040300000002000000000000000000000000000000000a59756e6720426565660000001a79756e67626565666269676261677340676d61696c2e636f6d0000104043727970746f436f77626f794f47000c79756e6762656566342e3200", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143229ab61e8335194fcbc52e3cdfb14d85daa469993d85fd39f8f52fd8c89c53afc067e327fd2da5a": "0x040000000002000000000000000000000000000000000f6c6f67696f6e206e6574776f726b000000147465616d406c6f67696f6e2e6e6574776f726b000010406c6f67696f6e5f6e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714324c8ff3d7043f733a47ca4e579b94d1edbf9a82eaf0e5b1d018ef37f84d85bd96e415147378e048": "0x00000000000000000000000000000000000e57616c6c65742048697473636800000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471432566a7e7c573a80b2cb9187546fcaa8c721b02805a6ed72fc162c0cba916e4af26e95b624668d47": "0x040000000002000000000000000000000000000000000970756e6b726f636b0000154070756e6b726f636b3a6d61747269782e6f726715706f6c6b61646f744070756e6b726f636b2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714327fdfd66e528b9662d8c4e1c6fbab57ba4df15b8120db4cec5c150371d0755d8ee5312382f47f09": "0x040000000002000000000000000000000000000000000b437269734e677579656e00001740637269736e677579656e3a6d61747269782e6f72671e7472756f6e676e677579656e3139373139393940676d61696c2e636f6d00000e40637269736e677579656e3939000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714329deba10a6ffdb9f64d8174827dde573fbc8bd2d0b184a7e38fae4957d1ba577e98522de9bd8e52": "0x040000000002000000000000000000000000000000000b4d61746a617a20e2a793000014406d61746a617a733a6d61747269782e6f7267126d61746a617a406170696c6c6f6e2e696f000009406d61746a617a73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471432ac541eb6aa1242ac1aee6b509bdaa6aeb4718c2218bd7e78a3d6704bf886375e4c243b77a66b34": "0x040000000002000000000000000000000000000000000d45786f746963205374616b65001968747470733a2f2f65786f7469637374616b652e636f6d2f184065786f7469637374616b653a6d61747269782e6f726718636f6e746163744065786f7469637374616b652e636f6d00000d4045786f7469635374616b65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471432e1c9cc0499e7e2dbcd8d24233cad535726ee1613bc044d6c376af651779deee9721338d12d0458": "0x04000000000200000000000000000000000000000000125350414e49534820424f554e54592056320000001a626f756e7479656e657370616e6f6c40676d61696c2e636f6d00001140426f756e7479656e657370616e6f6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714333ed71ba3fea8299a248664937e01d8e4856eb89c980637b2d88c705fb69307d7925ccc61022b53": "0x04030000000200000000000000000000000000000000074a45455045520000001378636a65657065724070726f746f6e2e6d6500000a4078636a6565706572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143351d9da302db1e80e86d53dbd64cb20f18ca85d0e78ea35331f734bedeeafa0166bf06c7224d100": "0x04000000000200000000000000000000000000000000124b6f67617261736869204e6574776f726b00001a406173685f77686974655f6861743a6d61747269782e6f726711696e666f40696e766572732e74656368000011404b6f6761726173686943727970746f000c417368576869746568617400", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471433c674ddb7d7206854d7aa0bba9a5dbb1bb77973f344625df346f6a65840b8534ee22e93fbad767a": "0x040000000002000000000000000000000000000000001af09f95b5efb88fe2808de29982efb88f70676f6c6f766b696e0000164070676f6c6f766b696e3a6d61747269782e6f726714706176656c406e6f766177616c6c65742e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471433c7d151629670075e7fb7c001173521bcbfdca5b2087290de094c04f9e1ce7491b836cd408c125a": "0x040000000002000000000000000000000000000000000e4c61626164616261646170746100001b406c6162615f6461626164617074613a6d61747269782e6f7267186c61626164616261646170746140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471433e2754f2553aa4e5068e605e8d0653954d518d7b9025d374b2e374731bbe800cfa6ca89743f7053": "0x040000000002000000000000000000000000000000000865736b696d6f72000000000000000865736b696d6f720000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471434089e43cd4989a9dbc5fe127aa5c0a6df694aa2a9dc6c68a1acd76e61f8f238bc8e71fc8d311636": "0x0000000000000000000000000000000000117363616c6577656233202d206a726d7200000000000008406a726d723932000f5363616c6557656233233731323300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714342d10fd36c3ac43cec9484c231e2d686bc8932300191a43fa515f95c02ceebda09ad8d8f5fc5305": "0x000000000000000000000000000000000015496e7641726368205465616d204163636f756e740000000000001040496e76417263684e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143458891c8e3214bfc070c3303fb82d487442202f6a3270f8a19af8691f9b6bb5b23dcf1745f02d1a": "0x040300000002000000000000000000000000000000000e53616368696e204775726a61720e53616368696e204775726a617200001773616368696e686c6f32333240676d61696c2e636f6d00000f73616368696e6775726a61725f34000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143461fb0d1f2c29fe9088b991261091d43c92ce790d4c8fb007b7e8d19c9c619a7d722c6fbdb55f04": "0x00000000000000000000000000000000001142494e414e43455f5354414b455f31331142494e414e43455f5354414b455f3133000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143494187c44a6e4fb8efd9cd4b3a223ff29da836fd6f01ce2fecadafaba8fa460c360239f2b084eb9": "0x0000000000000000000000000000000000124445442054656368204d756c74697369671b446563656e74726174696f6e20547275737420436f6d70616e791968747470733a2f2f646563656e74726174696f6e2e6f72670000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471434c90aef7cd6c087500774a5e6eb480dcabecc949e4c2508d7329ea62a1e68aebf76b819da6b864e": "0x040000000002000000000000000000000000000000001c616d6974726f76696368207c20556e69717565204e6574776f726b14416c6578616e646572204d6974726f76696368000012616d40756e697175652e6e6574776f726b00001140416c656b73616e64614d6974726f31000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143510bca3b55859f7b8296995e76034c70f1f703618da7493b946e24b590c53cd7bf5ab0c4cadd35f": "0x00000000000000000000000000000000000d746865626c61636b6861776b074948204b494d000014736f6172696e677373406e617665722e636f6d00000b40736f6172696e677373000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714355270ed8e9758bf26283680a763407ce7914d1a67d460d0d1f44d51097783ad17f161678218062f": "0x040300000002000000000000000000000000000000000c436f6e6f7244616c7932320b436f6e6f722044616c79000014636f6e6f7240636f6e6f7264616c792e6e657400000c436f6e6f7244616c793232000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471435668a15706f1b1bb42eda141e4c2c67af7180747b776be512c3fd83e5c359e7ca4cb3fc2d3a545b": "0x000000000000000000000000000000000011506f6c6b61646f74204163636f756e740c4a6573736520426c61636b0000176a657373626c61636b36343440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714357accb7976ec983aa479bfcbf35b48d8e5755c7a34bb367ce63261b6a0a51f24de1978c711f813a": "0x00000000000000000000000000000000000c526f79616c6167656e63790000001e636f6c61626f726163696f6e657340726f79616c6167656e63792e6573000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714359ba2c3f9bfa49015d7109f79f5bbf8a34fe2b2c9e68f0076f2bb900fb3cadff0e3e0239d804a70": "0x000000000000000000000000000000000014426c6f636b636861696e2052696f203230323400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714361d0f16c34b17763817e559bdeb521169c4801d70a1dfefe94175d15666a1ae70719a3594a57c16": "0x000000000000000000000000000000000014736f7665726569676e6e61747572652e636f6d00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714363df5d8dc0e8fe77042bfc6e75e1277fdefbea917f6b7ca8c36b3e0ad7286d66974c8f2b4cb9672": "0x040000000002000000000000000000000000000000000d414c474f207c205354414b45000016407368616465776f6c663a6d61747269782e6f726713696e666f40616c676f7374616b652e6e657400000b40416c676f5374616b65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471436614a3e28f4b5c8ca7c4d5563b5f38c3a5d1e7d348ea2dbe06332f55f17dd0150d606bcb8777258": "0x040300000002000000000000000000000000000000000a4e6d61727368616c6c124e6963686f6c6173204d61727368616c6c00001d6e6963686f6c61736d61727368616c6c31406c6976652e636f2e756b00001047616d696e675f6f6e5f436861696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471436623b12ed037a52fcbe4bd03bed7dd7ad734f50e0c283d9af0a129e71e78ea6128021f9cbf5af05": "0x00000000000000000000000000000000001e506f6c6b61646f74204272616e6420426f756e74792043757261746f7200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471436d8007439bfb5ce4aa76d977ed253365d18637af3d4159ca3ef710933143d8db9b99faaa48a5a52": "0x00000000000000000000000000000000000e466c6176696f20e29895efb88f0000000000000e40466c6176696f5f6c654d6563000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471436e262e10cb9b3db9f96f2a47d349d32da9e47f6206196b6869fce0e614fdb0fc46bc95fbbb60ecd": "0x000000000000000000000000000000000018506f6c6b61646f7440546f6b656e323034395f3230323318506f6c6b61646f7440546f6b656e323034395f323032331968747470733a2f2f776562332e666f756e646174696f6e2f001761646d696e40776562332e666f756e646174696f6e20000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143721f074825b67fc14e5b21d2eee0865adfb8783ac900540d67f0a89eb6881e77dda91d509398809": "0x040000000002000000000000000000000000000000000b476f6e74616a6f6e657300001740676f6e74616a6f6e65733a6d61747269782e6f7267176172747572676f6e74696a6f40676d61696c2e636f6d00000e406172747572676f6e74696a6f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471437626cdc63a152498a0e3784748694717e84559e00e0876f456fa206db3ab70327d512a24730ec37": "0x040000000002000000000000000000000000000000000577696c6c00001d406d6574616b6f736d69613a6d61747269782e7061726974792e696f00000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471437a32ce68f563a4a0a820dd53c6ea693d41a8d11910443a34fdc13a3d54cd111682b39d90ef57c38": "0x040100000002000000000000000000000000000000000e7374616b652d706f6f6c2e6575001a68747470733a2f2f7777772e7374616b652d706f6f6c2e65750013696e666f407374616b652d706f6f6c2e6575000009405374616b654555000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471437cd5a422db43856985be267f05f586da7ab52e4430c2f6d461f396b9d6cf4f1eb5362066f7cd82f": "0x040000000002000000000000000000000000000000000b5350494359205441434f0000164073706963797461636f3a6d61747269782e6f72671a73706963797461636f70657070657240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471437fb611aa391843a0617c14e4ba86d011c84b8a2a5b3f18758bf40915608a130518fa89b0a47e169": "0x040000000002000000000000000000000000000000000e436861696e20736572766963650000001564656e7665727437383440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143831c8a7af466e1a968ed5a157452dab7ddc6950d423e78edc9780da648cf7c009982b6bce956e7a": "0x040300000002000000000000000000000000000000000e43727970746f566972616c6c790000001868656c6c6f4063727970746f766972616c6c792e636f6d00000b4e61737461476c656e6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143844e5cda0e40f06f4f08248be088e83f0fd179f3b32d48c4540adaa119630979292e8b24b12144f": "0x04000000000200000000000000000000000000000000144a6f726d756e67616e64204c616273f09f908d000015406d6f7274677261793a6d61747269782e6f726700000010404a6f726d756e67616e644c616273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471438533548d44cc8d84056f76b206c306712a75e10ece4cffe091b1a49b0ba4c505f2ae4ee673c576a": "0x00000000000000000000000000000000000c5a4b56616c696461746f7200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471438713841aa089048252a69cb3b782cfc3696fed0fe67bd0c92ca21f2767482af41a902e5a8281145": "0x0400000000020000000000000000000000000000000008566978656c6c6f00001440766978656c6c6f3a6d61747269782e6f72671268656c6c6f40766978656c6c6f2e636f6d00000b403078766978656c6c6f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714388926241aaeef7a34dc4d6e6c7520791be90da8875a9a40653b4845ca03811c317cdcb034ce9e3c": "0x040000000002000000000000000000000000000000000d44696f6e79737573f09f8d87001968747470733a2f2f64696f6e797375732e6e6574776f726b1f4064696f6e797375732e76616c696461746f723a6d61747269782e6f72671468694064696f6e797375732e6e6574776f726b00000f4044696f6e7973757356616c6964000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143890df58c23c48913a04ac816fac137bf9ea324fbe8aae47b1169761f94c9dc943ed2e4b80620f4f": "0x040300000002000000000000000000000000000000000c52616a2052616f72616e650000001872616a72616f72616e6535373640676d61696c2e636f6d00000d4072616f72616e655f72616a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471438e49f900e62691ecaafae0aaa6333fcf4dc193146945fe8e4da74aa6c16d481eef0ca35b8279d73": "0x0401000000020000000000000000000000000000000005616b727500001240616b2d72753a6d61747269782e6f72670d6d61696c40616b72752e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714390c74f4052dd71642a8f4af92bc51e83093eeecb73f2aab526a11c41735ff54d9fc7de54ace5c6d": "0x040000000002000000000000000000000000000000000d50617261636861696e626f790000001e6a6f686e72686f64656c626172746f6c6f6d6540676d61696c2e636f6d00000e4070617261636861696e626f7900106a6f686e72686f64656c233831363200", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471439335e2f28a7aaec85a72cf77774526be03a8e0759b18af9c2ca15954050ce4098d48996a8e716cc": "0x0403000000020000000000000000000000000000000013506f6c6b61646f742e456475636174696f6e0000001b636f6e7461637440706f6c6b61646f742e656475636174696f6e000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714397f26eed6dc09cdae3abb16e30166db6c77a2f24b6e19cc63bd95fc08529bcb05bc71273a762b29": "0x00000000000000000000000000000000000e746d64765f706f6c6b61646f740f546563686d65646576207361726c1568747470733a2f2f746563686d656465762e65750012696e666f40746563686d656465762e6575000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143984bb08caa099b70cdbc545f7f5439e9eeebcf2ba592a87fe0da381d75a0bc8abf9f3b8ade4884a": "0x04000000000200000000000000000000000000000000044a4a4200000000000008404a4263727970000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714398ac470c132d05746cba586963ce742f0f17d705174df4317e773788ebdd7244df0a0ddedcc645c": "0x0000000000000000000000000000000000144379706861646f63f09f91a8e2808df09f92bb10416264756c617a697a204b616d696c0018406162756a756c61696269623a6d61747269782e6f7267136379706861646f6340676d61696c2e636f6d00000a406379706861646f6300096379706861646f6300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471439ae776eda0085e120f67bb36605f9ed8f7718d0b1c2ae212a5de9560662ba0814ae8ddd3bc9be57": "0x04030000000200000000000000000000000000000000045465301154656f646f72204b72617374616e6f7600001474656f4073637974616c652e6469676974616c00000e4074656f6b72617374616e6f76000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471439c5f2d53febd2c2f8c56648d781e59233b49323a09ee6a8b0410a4049d62549d27240f691893872": "0x04030000000200000000000000000000000000000000135468696261756c742050657272c3a9617264135468696261756c742050657272c3a9617264000018742e70657272656172644070657272656172642e636f6d00000840546974693150000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471439fc7b224a1750e8dcbad707002e3836a201fee988a917a528e2eb37ed99c74cb8a98819a9330406": "0x040300000002000000000000000000000000000000000c57696e7465725374616d70124c656f6e6172646f20437573746f64696f0000156c656f6e6172646f40637573746f64696f2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143a0f07f5bfdc2f192a45a8d99f3141ae7e1c9c636bead309c3bf3ee50d72a948acdd3d0104bb5608": "0x00000000000000000000000000000000001a446f742d437270746f666f6c6c6f776572202d20537461736800000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143a183e641b31567800859d2281b19e6650fd9bcc6dee02254ea1590ce4b8b831617dd79336ba4d36": "0x000000000000000000000000000000000007436872696e6300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143a2a13c271740b976af2f79717e1a5b28437221e8b526ec3a23d9b83c45baeba67ae76812ea07636": "0x040300000002000000000000000000000000000000000a4d616e64616c696f6e0c41647269616e204b65657400001c61647269616e2e6b656574406c696768746174776f726b2e6f726700000f4061647269616e6b656574333630000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143a47757951924bf938236315632f2c74a59daeaeb84b2f349771ed1dd9018b421a8ed32f69d0750a": "0x040000000002000000000000000000000000000000001050727565626120506f6c6b61646f7400000013646965676f4070726f746f666972652e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143a4998c1a7d8244c6819b9b3dab3439825f9b076ff1be1f248fe246f6be57b131d7d10e38b08fd00": "0x040300000002000000000000000000000000000000000b4761746f724b6f727073115374c3a97068616e6520466972696f6e0000176669725f70697261746540686f746d61696c2e636f6d00000a40676b31385f646f67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143a5db923b142979614acd07ac36ad489a295216ed144b8b0ed0c46a5f8a74c44b1948793f015297a": "0x04030000000200000000000000000000000000000000084d61745061756c000000146d61747061756c303340676d61696c2e636f6d00000b406d61747061756c3033000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143ab053dc6b6a71e1b4f7f03bebc56ebe96bc52ea5ed3159d45a0ce3a8d7f082983c33ef133274747": "0x040100000002000000000000000000000000000000000f41757265766f69725861766965720b586176696572204c61752168747470733a2f2f6c696e6b74722e65652f61757265766f69727861766965721b4061757265766f69727861766965723a6d61747269782e6f72671078617669657240696e762e636166650000104041757265766f6972586176696572000f41757265766f697258617669657200", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143ae48fb4d4a27b38063f5eee28e6df57ff122966e99dc82b479be3be5be9d627c10c5ea4db744d53": "0x0403000000020000000000000000000000000000000008446176696443431144617669642044616c6c2741676c696f00000c63656f4065766f782e676700000b4461766964456e656144000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143afb6326da17f7c2b27005300822ca95d14188dd385d93db13a6f76c4fc434c192ed476d353fc9df": "0x000000000000000000000000000000000008696e6b2168756200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143b04e47e14f8c9afcc472f8aa3afdab9a0f1abb66088817448690426bacc0af6f509241ae452a65c": "0x00000000000000000000000000000000001142494e414e43455f5354414b455f31351142494e414e43455f5354414b455f3135000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143b1767a9bb0967a7c6b17325907642f63b7e30a9636cb7db2ed22aec146ccad2e0a8991b5568607d": "0x000000000000000000000000000000000005426f7661000000166368656e62616f4063727573742e6e6574776f726b000009406170706f697070000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143b484b0dc727b45354825d8d050d0ac046e52f333e820999ac3dacb75f29306eb8233c0caf8ef012": "0x00000000000000000000000000000000000c446f742e616c6572742829001e68747470733a2f2f646f742d616c6572742e676974626f6f6b2e696f2f000000000b40646f745f616c657274000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143b4dd5f6332fce89f835522ecf3341305a26c938e8ad09fdf91dca4b9a98b8b0daafccd68ac4a505": "0x040000000002000000000000000000000000000000000ff09f95b454555845444ff09f95b400001a407475785f696e5f74757865646f3a6d61747269782e6f72671774757865646f2d77686974654070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143b8fa2e75a1b9c5bcea3dabe52b2a665b1e19bf8c6913a5d54e06d6413ca3ddbec8f9a22415ec477": "0x040000000002000000000000000000000000000000000644617669640000174064617669643a776562332e666f756e646174696f6e0000000c4064617669646861776967000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143ba0d8c6411833131ac5e7c54f557fcef02102186c742fce89cdda3c1073bb3271d8e91557877c70": "0x000000000000000000000000000000000009627261796f323536124168696d6269736962776520427269616e00001d6168696d62697369627765627269616e393640676d61696c2e636f6d00000e4062726963727970746f323536000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143bba269358278e21860054009a10c600b2d7cb347d672e1b27906a15e26955cc014fe868317a497b": "0x040100000002000000000000000000000000000000000e4669676d656e74204c6561726e0e4669676d656e74204c6561726e1468747470733a2f2f6669676d656e742e696f2f001379616e6e69636b406669676d656e742e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143c0955c5290aa69b8b1cf3b7ea2354781b6355660bd1481fe2e68d5a7a9c7692b54eed3c278e5b73": "0x040100000002000000000000000000000000000000001443727970746f50726f63657373696e672e696f2143727970746f50726f63657373696e672e696fe284a2204f4f4d2e41472049451c68747470733a2f2f63727970746f70726f63657373696e672e696f0019696e666f4063727970746f70726f63657373696e672e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143c096cf3f561e3834026c99cba12d64d1d0e7a7a620bf54374603fcc056e1601e3713f9921671120": "0x04000000000200000000000000000000000000000000114c45505245434841554ee29898efb88f00001c40706f6c6b616c65707265636861756e3a6d61747269782e6f72671b69726973686c65707265636861756e4079616e6465782e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143c29f0e466429f37964df9daa6cae8b790469f83dd63d0650bfc76ae16a67c4cabefdcf25d8d172c": "0x0000000000000000000000000000000000074e6f74696669095061756c204b696d1868747470733a2f2f6e6f746966692e6e6574776f726b2f174070696b616e6f746966693a6d61747269782e6f7267187061756c2e6b696d406e6f746966692e6e6574776f726b00000f404e6f746966694e6574776f726b00174070696b616e6f746966693a6d61747269782e6f726700", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143c790f30d60af3f382ecbf12e0965d08387b8aca42993392dbe5ddd0de48393b96075c641f2c9408": "0x040000000002000000000000000000000000000000000647335251300000124067337271303a6d61747269782e6f726719673372713076616c696461746f7240676d61696c2e636f6d000007404733525130000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143ca7f22618c737cbdedcb9123b5751a728ba18380f2c53b9cfb307cda0a6afdd85a30a3e9205385b": "0x00000000000000000000000000000000000b50617261636861696e7300000018736b6174655f62636e393540686f746d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143cc9562cc3c280496e92f87f2434cffb3afa90a740ede218ffd94189a9a5f216b89de7c74609f047": "0x000000000000000000000000000000000005536f74610e536f746120576174616e6162651768747470733a2f2f61737461722e6e6574776f726b2f0013736f74614061737461722e6e6574776f726b00000e40576174616e616265536f7461000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143cca7e3b1fdfe91c74d294ca861109c5f994b079460265f593e736010dff119e1449a2c1519c6186": "0x0000000000000000000000000000000000174d65657475707320426f756e74792043757261746f72001d68747470733a2f2f74696e7975726c2e636f6d2f6b3376366338626d001568656c6c6f40646f746d6565747570732e78797a000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143d38025ab9dda7b272e03b44235444cf15cd1c6f4b028ef714605714a5eb4be2a4013c649bc5c97d": "0x04030000000200000000000000000000000000000000194672657175656e63794e6574776f726b50726f706f73616c0000001d7068696c6970406175746f6e6f6d6f757370726f6a656374732e636f00000e6f6e655f6672657175656e6379000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143d4a76d67fff41d458ea543dfaedae9fe811b7c8cef72335ac41180e0c13d2d9e0bb89c506d44049": "0x00000000000000000000000000000000001061736f6c696d616e2028426f64612914416264656c7261686d616e20536f6c696d616e00174061736f6c696d616e39323a6d61747269782e6f7267000000000b61736f6c696d616e39320000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143d50c330808d23bc48bb96e509af35ac3308a94a3ac2db6e47f0197cae378cd4ca86430bc5dbce8d": "0x00000000000000000000000000000000000631325745421757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143d606081b82ba5aa0851213d7b0d4ef3edf4f62f7b0d3dc92edaad32daddb3c51b4ec205ec9c7779": "0x0000000000000000000000000000000000084f55544b415354001d68747470733a2f2f6c696e6b74722e65652f6f75746b6173746e667400166f75746b617374736e667440676d61696c2e636f6d00000d404f75746b6173744e465473000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143d6510d934b941c5feba71d41f307c6b4c59481cfb3a5d732175f7766aca821e98b1ffa8ea9f355b": "0x00000000000000000000000000000000000a4669676d656e742037001368747470733a2f2f6669676d656e742e696f000100000b4669676d656e745f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143d7b57c9bab601df742f10a2b57e5ec3247c0ae6da2a2fdb4a731324dc7a2edfe0f4fc761e1a4d3f": "0x04000000000200000000000000000000000000000000094d696368656c6c65000016406d696368656c6c65783a6d61747269782e6f726700000000001336303833373436343939373331313330313400", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143da7764530213aeaace846bae7cfbaa4ddb3d59d4bbaccd5d0b23bc3fc2cdf2194600bdab81b2972": "0x0000000000000000000000000000000000056e6f776100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143dc70605572f3709b8db36235621ae2f05e3e35e61a8101af409113d06b17417db649dab460c5c2e": "0x000000000000000000000000000000000015506172616c6c656c2046696e616e6365202d2035001568747470733a2f2f706172616c6c656c2e66692f000000001f68747470733a2f2f747769747465722e636f6d2f506172616c6c656c4669000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143e0a61ef993a58f6b099640796b5d19532ac152deb098b161a0b0d23c6b95c5a72f0ea27e696db3c": "0x040300000002000000000000000000000000000000000d4564676172506572616e746f1d45646761722044616e69656c2053616c696e6173204c656465736d6100001865646f67612e73616c696e617340676d61696c2e636f6d00000e67656e746c655f68756d616e6f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143e843a37d45e41d726171b3ad75723bf624a27485e51e4c2fe38f4b2d24ee52b86a979fb772c513b": "0x0800000000020100000002000000000000000000000000000000000843686576646f720e57696c6672696564204b6f70701868747470733a2f2f7777772e63686576646f722e636f6d144063686576646f723a6d61747269782e6f72671263686576646f7240676d61696c2e636f6d0000094063686576646f72000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143e9ff1da508f2a3c7ccb1907030863dd708764cc88a4b4d09dc5bb0f4c9ef5f4e73cfc0aa4bcdf3c": "0x04010000000200000000000000000000000000000000054572696305457269630014406433636b6172643a6d61747269782e6f72670d6572316340747574612e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143ed5ac38541b55b1cf68aca8c6afda0f57323e21f3aee8bc2630736c2a85c6213c79160b4d711283": "0x00000000000000000000000000000000000e414a554e41204e4554574f524b1c426c6f476120546563682041472028537769747a65726c616e64291268747470733a2f2f616a756e612e696f2f162040726f786f6e746f783a6d61747269782e6f72670f68656c6c6f40616a756e612e696f00000f2040416a756e614e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143edfdd48fc779cd5a80ea94af8a39eb7ba8d9afb913147e67eae84f48aad7b9ce6ee05094fe0394e": "0x0400000000020000000000000000000000000000000005416c6b6f00001340616c6b6f38393a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143ee48405c88be7992aa791b10dcfbe7ee482dfc6700da33db52b3ac81fc8822dee527f8aabcad613": "0x040300000002000000000000000000000000000000000b536972576f6c636f74740e506574657220576f6c636f747400001a736972776f6c636f74744070726f746f6e6d61696c2e636f6d00000b736972776f6c636f7474000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143ef4b38f8ce032ce06b8e579e2c0a3a3b87f1070181254ea66f8e083c00220215b111d117565334a": "0x040300000002000000000000000000000000000000000d446f74204c696e6520446f74164a61696d65204665726e616e64657a205065c3b16116687474703a2f2f646f746c696e65646f742e78797a001a646f746c696e65646f742e706f6f6c40676d61696c2e636f6d00001140446f744c696e65446f745f706f6f6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143f340ae0b8ad2cdc2017a0afa42b831ea0c10e34b8b208eeccfe324169a8d6776921510cb42a897c": "0x040000000002000000000000000000000000000000000e4173746172204e6574776f726b000016406173746172323030363a6d61747269782e6f726713696e666f4061737461722e6e6574776f726b00000e4041737461724e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143f4e772ad45522ecaea3ca653928298cd4d1d64cf916aacf72e4e2ca435453941a73327a8dd0a00b": "0x040000000002000000000000000000000000000000001056696b6b202d20476f7620f09f8f9b0000164076696b746f722e762e3a6d61747269782e6f72672176696b746f722e616d6261737361646f724070726f746f6e6d61696c2e636f6d00000b40786356696b746f725f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143f542ae0e8a4948cf48ad80b7cac41b7805a33923cd8357e7d179c16db083494bddab65845707b6f": "0x00000000000000000000000000000000001b444f54204a445020506f6c6b61646f742e6a732057616c6c657400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143f632b1c99a04492568b83865c932b2538214e98c5b8e4fc4779b9f0dd63788c8911d933b3b53f63": "0x00000000000000000000000000000000000b566963206375656e746100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143f6dc540984e16754c1383fd768ee83c1cd53eee57d10f21bb61eb6d37360fd34565ce35e5c5241f": "0x000000000000000000000000000000000008427574614d4d5300000018736f6d6564617967726f77757040676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143fa0b657ef7bbd841e5db30d360532b3bb6a9c0eb447f439bd16fa7be3002bdac10d2a6ce6e03567": "0x00000000000000000000000000000000001141726a756e207c2050656e64756c756d001b68747470733a2f2f70656e64756c756d636861696e2e6f72672f000000000a4050656e41726a756e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143fb71bbdb75b9a868cd54ac24629880ed1f3e6c8d5858ea08b5a7fbaa2c210c79e04223f3f05f35a": "0x040000000002000000000000000000000000000000000a526f62696e686f6f6400001740726f62696e2e686f6f643a6d61747269782e6f72671f726f62696e2e686f6f642e76616c696461746f7240676d61696c2e636f6d000000000c726f62696e2e686f6f6f6400", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047143fd48a75c1748c9c8e1ad9a11d5366e823c79dfb0b4416853734464245fb93d9ba62e818cbffe516": "0x040300000002000000000000000000000000000000001143726970746f204578706c6f736976611c46656c69706520416c62757175657271756520646120526f63686100001e6d657461766572736f64617363726970746f7340676d61696c2e636f6d00000f66656c697065726f636861747631000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471440211a7f8ab7a9a03c757ba7f937e877ce2171c04b729b5fcdf7bb4d9f11c46dfc50212664e5cd08": "0x040100000002000000000000000000000000000000000f5a6869786920496e7465726c61790c5a68697869205a68616e670000127a6869786940696e7465726c61792e696f00000e406e696b6b695f73756e73657400175a68697869f09f8f96496e7465726c6179233730333500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471440935ff2a98db16d44cb8281a79f264f66e42069baa12cb82c312afc38e4c09258b246cfc5752f4f": "0x00000000000000000000000000000000000a44616d6a616e444f541344616d6a616e204b727a69736e696b204d2e00001a64616d6a616e2e6b727a69736e696b40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471440a080abf9c0d1738465abd5a02f5b42b23e323b73e52416514460b9a3e7c34c94ed8d9f986c3d6e": "0x00000000000000000000000000000000000c536b616c6d616ef09f90a200001440736b616c6d616e3a6d61747269782e6f726713736b616c6d616e407269736575702e6e65740000000a6472736b616c6d616e0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471440b2bd2f7def6a3b80be87a5561edbd79c1ac4606901d84d1b4e185c26ac139cfc4ac12413d26d6c": "0x00000000000000000000000000000000002042494e444d494e543a20616e792d77616c6c657420616e792d62696e646572000016406e6f7474696573746f3a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714410472dcbca97371353b66bebdd4061939ffb1a79127e46d4c262bb8548e4a052f2087706cd4cc62": "0x00000000000000000000000000000000000c436f6c6c61746f7273696f0c436f6c6c61746f7273696f1568747470733a2f2f636f6c6c61746f72732e696f0015636f6e7461637440636f6c6c61746f72732e696f00000d40436f6c6c61746f7273696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144137b87771b2fd4e981bc48f19eab52de7f8c981822cc15f26990d5e90faba03e15894c0daf39759": "0x000000000000000000000000000000000015506172616c6c656c2046696e616e6365202d2034001568747470733a2f2f706172616c6c656c2e66692f000000001f68747470733a2f2f747769747465722e636f6d2f506172616c6c656c4669000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714415c4423d7d007052511a0306373926c509df110cda6e83fe525b6cc5564a9cf397ecefeeb57413e": "0x00000000000000000000000000000000000c526567656e63792d3031301757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714417aa25e24e3a895b46b21246e25a4882615af17b38863ad0b757ec4242f7fa58ce70462c0c4b246": "0x00000000000000000000000000000000000e4d7920444f542077616c6c657400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471441c47496ae1126564a8646cd9813bde9f62216e463129227292ac57963a50e3f4184220ef8163052": "0x040300000002000000000000000000000000000000000b506f6c6b614d6172696f104d6172696f2053636872616570656e0000136d6172696f4073636872616570656e2e657500000f6d6172696f73636872616570656e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471441e29c1629f394e1e4a77c09a5e7cca4340cd6ce8dd3cf6dfffacadaab7bd6581b7ca50959834271": "0x040300000002000000000000000000000000000000001553696d6f6e2040204b65792050696374757265730e53696d6f6e204869706b696e7300001673696d6f6e406b657970696374757265732e6f726700000d404b65795069637475726573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471442322010c0d1a89d36f6612bdd0a99e13fc4b24f30fb4522c13e2c2dfc3cd3fdcb44a2eb8d94c972": "0x040300000002000000000000000000000000000000000a4d722043727970746f0743727970746f00001969736161636d636d616e7573313040676d61696c2e636f6d00001140497361616363727970743433393534000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471442817abe7768122ad62aa3f00ef7f4e99f6dc2b160ed54d22142d890e4589effd93159596db40a5d": "0x0403000000020000000000000000000000000000000005416e6e610d416e6e61205275626c6f76610000167a756576612e616e6e6e6e40676d61696c2e636f6d000011406d6f6f6e7072696e63657373616e6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471442e8554be4de706545f4c317ada07d49eba49a3c0efaaa1e59283fbb081aca1c62b1826031ab68b8": "0x00000000000000000000000000000000000e42616e642050726f746f636f6c17576f726c64204461746120436f72706f726174696f6e1e68747470733a2f2f7777772e62616e6470726f746f636f6c2e636f6d2f001462644062616e6470726f746f636f6c2e636f6d00000e4042616e6450726f746f636f6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471442f5d87995df46aba849437f5f8b602fc9a4210d6a9834af4adc6ce7492861bd0f5b88d11919cd7b": "0x040100000002000000000000000000000000000000000d43525950544f20504c415a410d43525950544f20504c415a411868747470733a2f2f63727970746f706c617a612e65732f00177374616b696e674063727970746f706c617a612e65730000104063727970746f706c617a615f6573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144316433e1f50b4c3a66e0f4e1a121cc83fddf3096e8ec8c9e9c85989f276e39e951fb0e4a5398763": "0x00000000000000000000000000000000000a67696c6573636f70650b47696c657320436f70651968747470733a2f2f67696c6573636f70652e6e696e6a612f000000000b4067696c6573636f70650a67696c6573636f70650000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714431c4c64db68d8bc2211d6fce26d7140904357fce2aca5d1e63b362f1789bb6d7dbd848d3df79c65": "0x04000000000200000000000000000000000000000000084d6172636f526f000000196d6172636f726f6d616e6f77656240676d61696c2e636f6d000009404d61726330526f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714431d3bd5ffac15fd908006776baddcbd6ee1037b124dce04923c13176eca8a467612a1bfe471184c": "0x000000000000000000000000000000000006757262616e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144346ad63c2bfe5464174ed6a137e6df4c9dbbc1ef6fd102a67295edb937fe7446b0a38930927bf16": "0x0000000000000000000000000000000000084d75726977616900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471443496d81af6ed7735855f6f9634a68990a917f22cda160e53f14a0bab251926d946650b4df53412f": "0x04000000000200000000000000000000000000000000217370617a636f696e5f4368616f7344414f5f496e74724b696e745661756c7473000000137370617a636f696e40676d61696c2e636f6d000008407370617a7674000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714434ad746326c40551646bd1c80f91d01409be58ff6e426f857aff02f544540e2b5c7b54c6d4a3e16": "0x040300000002000000000000000000000000000000000753756d6565740c53756d656574204e61696b00001568656c6c6f4073756d6565746e61696b2e636f6d00000a73756d656574776562000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714439f39607719f9fb7026f145d6eb23143d729bc300e35203a831fb08b315235032e2527a659f6c29": "0x00000000000000000000000000000000001a506f6c6b61646f7420526567697374726172233120546573740000001d63686576646f722b72656774657374646f7440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471443c450490ea5a0dfaa1e41806e32999e0754be06b02cf2cd7397d7449111cd16757b6a846b9cdc54": "0x00000000000000000000000000000000000e636f696e766572736174696f6e057061756c19687474703a2f2f636f696e766572736174696f6e2e696f2f000000001040436f696e766572736174696f6e5f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471443d4cb05677396f618812f78e7c7d0769e2050a07e54d7d26ef922f2ab3e45d66f0289d5dd1deb08": "0x040300000002000000000000000000000000000000000754696767616800000014746967676168406d6f6f6e73616d612e636f6d00000f407469676765727363727970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471444000dd1889b03b1be415da93670251b3c219aff9d1069ef99d27a453620c20d9b4dde4739caa55d": "0x0400000000020000000000000000000000000000000009706965726f20672e000000000000000a706966726167696c650000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714444f277f5dcf70d79848dbbf16e1a48ac57884a4d2a3d64ed6784969927e04129066877ad098a009": "0x0400000000020000000000000000000000000000000015526f737320616e64204a6f656c207c204a4b5242001068747470733a2f2f6a6b72622e696f0010636f6e74616374406a6b72622e696f000009406a6b72625f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471444646f06f20d51883c017930b46ab5a4413bf3153b001287ed5ff7fdbd2734cf69abc843f4ee0447": "0x0400000000020000000000000000000000000000000017f09f909f59656c6c6f7746696e2054756e61f09f909f00001940626c756566696e5f74756e613a6d61747269782e6f726713616e74756e40747574616e6f74612e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144473d1741e91e5149a137cacb7c5e216b12fda7a62c853c15ff93d8f0bd0c8b19520c3ec689df366": "0x04000000000200000000000000000000000000000000084d696c647a73750000000e6d696c647a737540706d2e6d6500000000084d696c647a737500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714447e1613a9d973af08d48a00b01356b80f40e50f281c2cda9e858f45a1bc8e989b3eedfb27dfe044": "0x04010000000200000000000000000000000000000000094252415f31362d4400000016637074636f7272656f6f6640676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471444807460d777b1493446e1bf0c0eb4682d4b7269c0b0459685dd3cdb024467ec4f01f6e878df113a": "0x0401000000020000000000000000000000000000000013546f626961732041697264726f702e636f6d0e546f62696173204b75737465721568747470733a2f2f61697264726f702e636f6d2f1440746f626961736b3a6961627369732e636f6d15746f626961732e6b4061697264726f702e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144497480ac18f14a6cc267824fecaa355e8b540102fc0386dc902d068e1099f5d64f9bdfad8e6b936": "0x00000000000000000000000000000000000b53756775616e677275690000001773756e62696e674070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144499fd1a049d4b87923e75ccccb33e471161db9558583ef4668de361bdf471e674256e8fe0748706": "0x040000000002000000000000000000000000000000001d50726f6d6f5465616d207c20416374697661746f72207c2052756279000000166f70656e676f7662726f7340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471444ae3a94bfb51fab3e5ec4b057a0f4b697e048bb02918c5c07998f08ade9e7b6a31ca6ddf3956442": "0x040300000002000000000000000000000000000000000b416974696a696127657200000019616974696a696165722e696e666f40676d61696c2e636f6d00000b40616974696a69616572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144567817f570fd2a7d4780b9e5750a1a1772e61c81d02fb63c88815de6c2f5e1123a2915a4dfa1143": "0x00000000000000000000000000000000000873616e6368657a0e46617a65656c2055736d616e6900001966617a65656c75736d616e69313840676d61696c2e636f6d00000e4066617a65656c75736d616e69000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471445710aad85286ba2f61e1319acc0189207ad27455845c7d7b60ded1be7c50279c04cea4d89835759": "0x040300000002000000000000000000000000000000000d4976616e206f6e2054656368104976616e204c696c6a6571766973740000126976616e40696c686f6c64696e672e736500000b6976616e6f6e74656368000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471445a26c6e9f4fbc9e1a93333836f4c417e6a25d826a06d994d688a791f31a2ffe77eb6f0b11074575": "0x00000000000000000000000000000000000c57616e677368692e65746807e78e8be58d8100001677616e677368692e65746840676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471445b8ad3d042acdd1361d8fe50e87561a6623c67f9ad530973749b54f8015c29ce0b234de9f942e45": "0x00000000000000000000000000000000001044617669642059696c696e67204c691877656368617440446176696474686550616e6777616572000014646176696440666f757269657270722e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471445d43cb8401e3564f40cd7a2181289e32776625b9f3a193f749e33ce1bdeb76cfaabece606c7324c": "0x040000000002000000000000000000000000000000001768656c69787374726565742e666f756e646174696f6e00001b4068656c69787374726565742e696f3a6d61747269782e6f726711744068656c69787374726565742e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471445ec95bcb8dd897248d5f568124b09fefe504a22bbce2ccee0fcae9262c639b6b2ebaf95b5c8fe77": "0x04000000000200000000000000000000000000000000175368616e6b6172207c2045646765776172652044414f0f5368616e6b617220576172616e67001a407368616e6b6172776172616e673a6d61747269782e6f72671b7368616e6b61724065646765776172652e636f6d6d756e69747900000f40576172616e675368616e6b6172000a7368616e6b61722e7700", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714464cfe5f5087e90778875e66122277dc5609bad6a10ec246d89c1bd30c549ef3d839d2bc2f78af1a": "0x0000000000000000000000000000000000094a6179506f6c6b6100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714465cbd752cabb7fa409b7555f513abf3af9740d09b7a47d7c2ce84dd3b146a791dfd28d8e0abae75": "0x000000000000000000000000000000000010706f6c6b61646f742e676976696e67001868747470733a2f2f706f6c6b61646f742e676976696e670015696e666f40706f6c6b61646f742e676976696e67000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714466588a6e1073ed4aaa806fd8ff8e363c4f3139c07bd115f3879de52f9961fd61f58eb1610ef6836": "0x0000000000000000000000000000000000074c5020444f5400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471446720693a520effa96f8ff6a5803682aa883aebdb426940851c7de4cc302e540a4822d63e1216c71": "0x000000000000000000000000000000000014476d656973746572207c2045646765776172650e432e20476167616e2042616275000019676167616e4065646765776172652e636f6d6d756e69747900000e40687573746c65346c69666537000a676d6569737465723700", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144672db3439b14d041e5d733c17f21e33af639896e0e959ed44cdd05f44d67f60e8311022dc90d51a": "0x040100000002000000000000000000000000000000000f51494e57454e2e3136384e6f6465001468747470733a2f2f3136386e6f64652e636f6d001771696e77656e40776562332e666f756e646174696f6e000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144692967bd2332c08b22f88d05ca6fdb70235b599b99c69f927ed71163c2ebc11c9649d678a8ba84e": "0x04010000000200000000000000000000000000000000094c6974656e747279094c6974656e747279000012696e666f406c6974656e7472792e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471446955dc79999f928ddd3e0632b74f5e8d57de6814a16b6451366eb903a7943ed00051e7cca5d24d2": "0x040300000002000000000000000000000000000000001050617269747920536563757269747910506172697479205365637572697479000014627567626f756e7479407061726974792e696f00000f706172697479746563685f736563000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714469f989f6ca51c58f3b15e49aede08aa29d7400da8d3b4fdbd284b5bb1f1af1a53a8c47398f1f093": "0x040000000002000000000000000000000000000000000b424545465920f09f90820000134062756c726f673a6d61747269782e6f72671f73766562697261642e7072697469736b6f76696340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471446a18621916cf2627efce294aceebeb808ff4ab99930030a5182be918fff1261ca5c5b41acbf6f11": "0x00000000000000000000000000000000000c526567656e63792d3030331757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471446a8b45a25e83bcdea16fb2015c85c938d41f946c5fcf6c310872e30ec541c81fd2108e7d1b89813": "0x040300000002000000000000000000000000000000000f4d65726b6c6520536369656e636521537461636b7365657220546563686e6f6c6f67696573205074652e204c74642e0000196e69726d616c406d65726b6c65736369656e63652e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471446b9cd597eba686fe4a48b16cd6a6c6c44e4c9542e923726dc861077b6a4ff3df1969b13a6868b2f": "0x040300000002000000000000000000000000000000000d5061756c6b61646f74746572000013407061756c6b613a6d61747269782e6f7267177061756c6b61646f747465724070726f746f6e2e6d6500000e407061756c6b61646f74746572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471446cfb8d6f019ac35a002a7d2c93a1e4c5f3ed5e95b06862f91cc8f914c64ba3fe844b35ce36ce151": "0x00000000000000000000000000000000000c4369746164656c2e6f6e65104369746164656c2e4f6e65204c54441568747470733a2f2f6369746164656c2e6f6e652f0014737570706f7274406369746164656c2e6f6e6500000c404369746164656c44414f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471446d4ef367550b68d2a0783588d2ec76c02517d1465e08cc3e5964ae9b3b3215bb598489989f62119": "0x04030000000200000000000000000000000000000000075a454e495448195072616e6176204368616e6472616b616e742050617761720000197072616e6176637061776172406f75746c6f6f6b2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471446da05f48ba08b97e60b065719963027baac7cb4d68145539a0b428aa74048ec1c32b6b255530646": "0x04000000000200000000000000000000000000000000084e45574445414c000015407061726964655f663a6d61747269782e6f72671c6e65776465616c2e76616c696461746f7240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471446eb4c71c2ad15c3f85bd4ec9a558cde3f05e33b0b74f9e732cc41f904894873247d4c435a3b8b63": "0x040000000002000000000000000000000000000000001d4d4554415350414e2028616c736f2074727920504f4f4c20233138290d6d6574617370616e206c74641568747470733a2f2f6d6574617370616e2e636f6d000000000d406d6574617370616e5f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471446f3d1d0510badd5763735fed6d0ce2b2909c3365eccafaeb1ffcc2a10af6060a637b1dacff01616": "0x04010000000200000000000000000000000000000000184c697361277320506f6c6b61646f74206163636f756e740000000f6c697361407061726974792e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471446f59e6d908bc0ec8488bea263878e7e16be0a8c4705f9729a5f25462bff7dc7f2d8346370c8ef65": "0x040100000002000000000000000000000000000000000e53504143455f494e564144455200001a4073706163655f696e76616465723a6d61747269782e6f72671b7370616365696e76616465722e646f7440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471446fca39721ec26e31ee933d8dc66947b2826fa57de49b62e7d9201326cd9cc01abcd8bc06c35c02b": "0x04030000000200000000000000000000000000000000084269744c696f6e000000196269746c696f6e32314070726f746f6e6d61696c2e636f6d00000a404269744c696f6e33000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144702815fa4f5b7fd02251a6e0194f2ee97f988a8f2e779a06a73b2ee3e1a54f413a3b9f6f8d04f6c": "0x040100000002000000000000000000000000000000000e6d75737465726d6569737a657217467265646572696b2047617274656e6d6569737465722168747470733a2f2f6769746875622e636f6d2f6d75737465726d6569737a65721a406d75737465726d6569737a65723a6d61747269782e6f7267186d75737465726d6569737a657240706f7374656f2e6465000010406d75737465726d6569737a657232000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714470f8592fcae215df03039aaa27dbd35081f359f8a6aa39ab5dc098c19937617389dd8cd05e4dc3a": "0x00000000000000000000000000000000000b6b736d7365616e6e657700000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144719a17064a7613bb9f26de7808ffcf087492fc04a2c00178c3bf9ae512d1fc817f8430f60093801": "0x0401000000020000000000000000000000000000000008434f534d4f4f4e00001540677265676f7273743a6d61747269782e6f726715636f736d6f6f6e40677265676f7273742e6f7267000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471447207f7f4037875fca91a41c974862cae390d507621b3cd9d96c2d8624fde2481d518a61c6b4cf3b": "0x040000000002000000000000000000000000000000000b53796e61707469636f6e0000174073796e61707469636f6e3a6d61747269782e6f72671a73796e61707469636f6e4070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714472c11c45d19e722cfc5e71e4781885c9ee4420c55496b1a1b2d95b5cc0eeeaf4459c4ebc5bb8fe9": "0x00000000000000000000000000000000000c526567656e63792d3031331757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714472da44c2d8c63974ebdc7b636074ac97be407593236b00bd410c19fe56ab1b6d1c5db782f0bc17e": "0x0000000000000000000000000000000000044b6f5400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714476ed96b663d19f9d20ee72ee9744e1669971043d3ff9fe7a7223fc3334c87de6b1c6c0b36555c4e": "0x04030000000200000000000000000000000000000000084d6573736172690b4a6f686e2050757264790000106a61636b406d6573736172692e696f000009406a707572643137000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144822d22761cb98521237b750e00e3d48fe0b1629f2a4cf0630971cbf489dfca33e75235347196474": "0x0403000000020000000000000000000000000000000008476b697269746f08476b697269746f00001467756a6b697269746f40676d61696c2e636f6d00000a4047756b697269746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144825a3675ceeb1c2561077519794c195b22f5058bd1195f6847aa6b5a749053d44468ba5db872d64": "0x040100000002000000000000000000000000000000000f506f6c79636861696e204c616273001e68747470733a2f2f7777772e706f6c79636861696e6c6162732e636f6d001664657640706f6c79636861696e6c6162732e636f6d00000f40706f6c79636861696e6c616273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714484069a0f1cb9062b8d71afc98c6256dfcb316ff7ef0665270537c85ff081db98255159abb5fe017": "0x04000000000200000000000000000000000000000000124e65774f6d65676156616c696461746f720000154063656c726973656e3a6d61747269782e6f72671363656c726973656e40676d61696c2e636f6d00000e40506c61794e65774f6d656761000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144874818619a271dd4725fa85860f87917edff507310721c0cb930595fe16c829f536fb1289e4a852": "0x040100000002000000000000000000000000000000001177656233726479206d756c746973696700001440776562337264793a6d61747269782e6f7267136d6973636137303240676d61696c2e636f6d00000a406d69736361373635000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714487b7409ae4dd68d8e706d0bd734b0cf21ccbfc5ca90cd9dc03ef4d0be9b932bd23727869445105d": "0x000000000000000000000000000000000004414a4d00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714488adbd6698f281d1eeb54e0e7e13080dc9dfeb2cf5de7170fc7c84f4fb70cf5c2ca0a5c93b25735": "0x00000000000000000000000000000000000c7433726e2d6576656e74730d7433726e204c696d697465641568747470733a2f2f7777772e7433726e2e696f2f000c6f7073407433726e2e696f000009407433726e5f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714489667186afa0e91ca5bc1915da74aba3aadd7ce7b809045d5eb5b73559259755fdcd85a40a5dc6e": "0x00000000000000000000000000000000000f4a414d20e298a0efb88ff09f908d000013406a616d31306f3a6d61747269782e6f72671768656c6c6f4073686f6b756e696e2e6e6574776f726b000008404a616d31306f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714489b5689523a93fdf4f5911143092b1788c3a91f96d192b35e4fb201f05658f47b677e7dc9a1fd5d": "0x00000000000000000000000000000000000c52484545207c20415041431053414e472d4859554e2c2052484545001140726865653a6d61747269782e6f72671472686565756e696f6e40676d61696c2e636f6d00000840786352484545000a72686565756e696f6e00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471448b1dce0478b5f30daf47069cecf5c31da9a1cfde3732d2b701bf9f94c77ba7e99335d6b30ad080a": "0x040000000002000000000000000000000000000000001856462056616c6964696572756e6720f09f87a9f09f87aa001b68747470733a2f2f7777772e76616c6964696572756e672e63630017636f6e746163744076616c6964696572756e672e6363000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471448c106532e2fbcf5ec05f950e080aa04f8ef335280637c8c80fa6b8bf54d5a3bbfa746b9f9da5860": "0x0400000000020000000000000000000000000000000006506c7573560000000f706c75737640706c7573762e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471448cdedeb02681e28ac891d5a6e2f56c923dcff8c05d0a535c6695a118bf3de7ed3bab92ff7db641f": "0x0000000000000000000000000000000000144a6f6e617468616e202854616c69736d616e29000016406a6f6e7064756e6e653a6d61747269782e6f7267166a6f6e617468616e4074616c69736d616e2e78797a00000b404a6f6e5044756e6e65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471448fd29f3f7e12961b4ddcbdb50b550740253810978125ded4acafacccfdbf9e2cb3bd031c9291388": "0x000000000000000000000000000000000013636f736d6f76657273656d756c746973696715636f6d706f7361626c6566696e616e63656c74641f68747470733a2f2f7777772e636f6d706f7361626c652e66696e616e63650018696e666f40636f6d706f7361626c652e66696e616e636500000f40636f6d706f7361626c6566696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714490130ac826416f286f68361d0a346a62be267558e72dfb9e3b5a04adcc2c9e46fb7b9482f7c876f": "0x04010000000200000000000000000000000000000000094b6565704e6f6465001968747470733a2f2f7777772e6b6565706e6f64652e78797a11404472756e3a6d61747269782e6f7267156472756e2e6d6167696340676d61696c2e636f6d00000a404b6565704e6f6465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714496b2311f8412ae280a4a5a4e8e8c8ce6b19bd926a83efa02d70a6137d9896c627eea987e10d3655": "0x0400000000020000000000000000000000000000000007444f546f6d6900000019617374696b61696e656e746f6d6940676d61696c2e636f6d00001040546f6d69417374696b61696e656e0014546f6d69417374696b61696e656e233632343900", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714498085158358a41ed824263a2ba0b39e43b2a1fb591c68743ca504c2ce6c3c2aa96e58b6269d3115": "0x0400000000020000000000000000000000000000000016f09f9a82205a756769616e204475636b20f09fa68600001840726f626572743a776562332e666f756e646174696f6e17726f6265727440776562332e666f756e646174696f6e00001140526f626572745f48616d62726f636b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144980dd5ac2100210047e68bf811eb225dae8c88461df5edfc581c80c460a31672c36d53ca0164b0b": "0x000000000000000000000000000000000008617234766f6c6b00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471449834f6d0d98c2caf4f7e8c9dcf45daa322fa14585d8f14a37aceca839106ed3414bb04778696145": "0x0400000000020000000000000000000000000000000009636c616e67656e6200001240636c616e673a6d61747269782e6f72670000000009636c616e67656e620000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714498684a38db4f62054c56b5010033b3c17a5677cd9bc45b2d6c5d24b7590b7681bceac60b0f3833f": "0x0403000000020000000000000000000000000000000019546578617320426c6f636b636861696e20436f756e63696c19546578617320426c6f636b636861696e20436f756e63696c00001f6c6565407465786173626c6f636b636861696e636f756e63696c2e6f726700000e7478626c6f636b636861696e5f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471449891bd977a475d4fa150742c53997a227cb5648c21029d8a6a087ea1c27c502903aa60ef7701823": "0x040000000002000000000000000000000000000000000d45524e2056454e54555245530000001c65726e63727970746f76656e747572657340676d61696c2e636f6d00000d4065726e76656e7475726573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144990ff033d5343d5a85e37331d7b706f90745bd7e4ad7ae82a83beacf5a585f69fb4afd10704a71d": "0x0000000000000000000000000000000000085261626173736f0f4a6f73652046205261626173736f0000146a667261626173736f40676d61696c2e636f6d000009407261626173736f00156a6f73655f67656e65726963636861696e2e696f00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471449aa36a5cacb5456d4539a7dceeeba9999b6387e9431e83c53f4b884edf5cf049623110eae570125": "0x040000000002000000000000000000000000000000001f45617420507261792056616c696461746520f09f8db4f09f998ff09f96a5000013407978313178793a6d61747269782e6f72670c4f5456406570762e6c6f6c000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144a3ab523f50a954b22d05d25884419493436b157e4f7ca725b885d6e328f7c09849c7c12395aec50": "0x040000000002000000000000000000000000000000000650524956490000144070726976695f6a3a6d61747269782e6f7267186a756c69616e40707269766970726f746f636f6c2e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144a46ea11348a56200a66532a23c418cca12183fee5f6afece770a0bb8725f459d7d1b1b598f91c49": "0x00000000000000000000000000000000000d44617277696e69612044657600000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144a4e353d6c92b4f6fc5f90e8fb39b1656b5a018d5fa169f80a4ab3fcd03b3d46e2acdb3ae3fc8174": "0x00000000000000000000000000000000000f4a65676f72207c2050617269747900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144a69dd5ac19d5ad6743037278f95ee81a2cf5a59518d3dd440611989cf8dd826811accf784a9ff2e": "0x0000000000000000000000000000000000044a7572001068747470733a2f2f6a75722e696f2f000d68656c6c6f406a75722e696f00000c406a757270726f6a656374000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144a7360f6b774d3fda84ef8c0efdd519e001cb3f5b6351725178283edf1b122a464f0a0f425699759": "0x00000000000000000000000000000000000b4c75636b79205465616d0000000000000b404c75636b7944617070000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144a7a8224146595f932ab6289334629c2011e3bbcd7742c9aaa5501d440a5ab5d76b42704ff2baf6f": "0x0000000000000000000000000000000000074d6175726969114d6175726963696f20417274696761731768747470733a2f2f7777772e6d61757269692e636f6d0010696e666f406d61757269692e636f6d00000b404c6c634d6175726969000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144a894c423d88cda06cc6b537b36e5f7347ad0daaebd9f5fa22be1e4b8bc68a58c8ee57d43b4d2e70": "0x04030000000200000000000000000000000000000000116d6968616a6c6f5f7061766c6f766963114d6968616a6c6f205061766c6f76696300001c6d6968616a6c6f7061766c6f766963343040676d61696c2e636f6d000009404d70636f6e6e30000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144af14bc1b4b0280418d30040a8245c5ff17afc9a8169d7d0771fe7ab4135a64a022c254117340720": "0x040300000002000000000000000000000000000000000c456477617264204d61636b0c456477617264204d61636b0000126564406564776172646d61636b2e636f6d00000f406564776172646a6d61636b6a72000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144afaabe16b988c8244f1bc09ae85c878c320783337f5991e4a172d3daded474eaf772069ac48b811": "0x000000000000000000000000000000000013414d4920426f756e74792043757261746f72001b68747470733a2f2f6772696c6c6170702e6e65742f3132313731000000000d40616d69706f6c6b61646f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144b0852ee2d8116607454e2120a4f7b4eddc39c681fea50a8366b39c176e7393913bbfaca5649a450": "0x0400000000020000000000000000000000000000000016506f6c6b61646f742041504920446576205465616d0000001c706f6c6b61646f742d6170694070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144b11703c47136f3474b0c404b20bac28ded8d662e2eae7ddc52988cd5a22d5de09259258395dc45d": "0x00000000000000000000000000000000000b4441524b464f5245535400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144b33ce1e975a619404d3773ac89f099d5cd7257a72a53b7877935fe84065dff6ae95c0ed1ce17e63": "0x04030000000200000000000000000000000000000000104845524f49435f6f6666696369616c0a4865726f696320415300000f68617267406865726f69632e67670000096865726f69636767000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144b688debd0defa3c60c02d8df757b0af6de882ebc51bf2bba0cdeb949e6ad76331628768fccbd743": "0x00000000000000000000000000000000000b506f6c6b61646f74203200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144b6b6385bf015c077a31de6245e807c4398025994cefc7ceb4bdbe252c4590e732c99698691b666f": "0x00000000000000000000000000000000000f426c61636b626561726420444f5400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144bc239eae76c6ad1fe5cd339cdd38f39283d20c2352716e68153deb3267c7a5d543c394814e88e04": "0x04030000000200000000000000000000000000000000044a61790a4a617920506f70617400001b636f6e746163742e6a6179706f70617440676d61696c2e636f6d00000b406a6179706f70617430000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144bd6e2ebc6fe2e047b72f47f4a630d6a3c03d7499a70092a183cd5a2541afc72ab8301ba640f4821": "0x040000000002000000000000000000000000000000000645617379410a4561737941204c74641268747470733a2f2f65617379612e696f2f000f68656c6c6f4065617379612e696f00000b4065617379615f617070000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144bed375fad94bd6b2ccc353342d8ed955553be5ae666a0b2f05db798bb6a213ded13f25b7af7bf54": "0x0000000000000000000000000000000000054841534800000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144ca0ff14934456cd32cd9b54bb73288503c7969f219ab74a83cb5b8bc2672ef472b7b7c43d866533": "0x0000000000000000000000000000000000124f6e2d636861696e204964656e74697479064a45524144000017627573696e6573736a33383640676d61696c2e636f6d00000e40627573696e6573736a333836000b4a65726164233130363500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144d2dafa5e265f20094ecf114c292f2a24ff6dbc3dbf8311be37ef53f7e78c9d1a59d0c5762cac672": "0x040000000002000000000000000000000000000000000a66696e616c6269747300001240617269666b3a6d61747269782e6f726717696e666f4066696e616c626974732e6e6574776f726b000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144d35c327c115f99b08745476e8a2fb16504c77a75b2dd20b6f56cfb71c87125f1707a702753af24e": "0x00000000000000000000000000000000000642726561640000001762726574746b6f6c6f646e7940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144d466d1a5e9c13dfc483215a3e72d171dd71c55d3885dfa73a40385682f9fe4ced1e811320265714": "0x0800000000020100000002000000000000000000000000000000000c56616c696461747269756d0c56616c696461747269756d00001656616c696461747269756d40676d61696c2e636f6d00000d4076616c696461747269756d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144d4d81d0048d154f680529ad3b92e92ee8955e6a50109c5f12c1b50be62fa4eb82f66ef62400ccd9": "0x040300000002000000000000000000000000000000000f547261636520416c6c69616e6365002168747470733a2f2f616c6c69616e63652e6f726967696e747261696c2e696f2f001a616c6c69616e6365406f726967696e2d747261696c2e636f6d0000104074726163655f616c6c69616e6365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144d51d2cbac595fa87056ac3306e0fca5ef9784e7d34b1f72af8e2650a29f0b3ce50f86df501ce858": "0x040000000002000000000000000000000000000000000c7777772e6973672e64657619496e666f726d2053797374656d732047726f7570204c4c431d68747470733a2f2f7777772e6973672e6465762f706f6c6b61646f74144069676e617465763a6d61747269782e6f72670d696e666f406973672e64657600000d40696e6673797367726f7570000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144d538289bdc31db0d05099da3d810eceb0001840b3ebbeac4b8b8b230ecbc4988a5b2f131aef4835": "0x0000000000000000000000000000000000135354414d207c2054454143484d454445464900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144dabfd58e995e46fa4a7835edb8c6b0d10b32c76ddd9560feb4894fcfb61593accd8c7451e96562e": "0x0800000000020100000002000000000000000000000000000000000c45726e7374204b696e74730c45726e7374204b696e7473000012706c61736d616a61636b406d652e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144dd03705d9d21ba9efbb245a877c2ee64cf822cf8ce58907ceec0d4481746da50d533a386a8c4cde": "0x00000000000000000000000000000000000c526567656e63792d3030391757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144df55d839183b50360b5acfd88341f7df867518c44929c114775a3457f3496d29dd231cca9499143": "0x00000000000000000000000000000000000f43727970746f2e536865696e69780000001963727970746f2e736865696e697840676d61696c2e636f6d00000940736865696e6978000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144dfaf303c80f7947c618838a7cda0fba033eac4b3df3e2b31e1b304765bf98004eb72690fc117f09": "0x00000000000000000000000000000000001142494e414e43455f5354414b455f31341142494e414e43455f5354414b455f3134000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144e4f1bf6b4d19f047a3f515334d24cd37b65d4b1c5838cc7c34951a67f3ae2da041a6e2385d8da05": "0x040300000002000000000000000000000000000000000d427261766542726f7773657213427261766520536f66747761726520496e6300002069742d736572766963652d706f6c6b61646f742d334062726176652e636f6d000007406272617665000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144e5666c0de8b6c84ac2752e0009c6ffc103fd1a723726affbd91d6b8e65ddabbeff28fe14a93b678": "0x0400000000020000000000000000000000000000000012646563656e74657265642e6a656f7269630000001a6a656f2e72696340646563656e74657265642e73747564696f00000000086a656f2e72696300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144e5736534181ad78b690df84dc61bb1b04bb3e5483678390a44eca9ee4a748e98f526e126c19c702": "0x04000000000200000000000000000000000000000000064c65676f73000012406c65676f733a6d61747269782e6f726711706f6c6b616c65676f7340706d2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144e5ef662a865543dea9196cd611c7e79bbca036076b2ffd2f684421ba82134c29e84a1e40479b309": "0x0000000000000000000000000000000000146e6963686f6c6173207c20646f7420706c61790000001c6e6963686f6c61732e646f757a696e617340676d61696c2e636f6d00000a406e69636b646f757a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144e6885d639fed568e0d1e848ba9cf7ec704b12a0f1e2381e42c8a4a4d55f40625de6d60c39ebc032": "0x040000000002000000000000000000000000000000000978634b7265736e61000013407368613838383a6d61747269782e6f7267196b7265736e61737563616e64726140676d61696c2e636f6d00000b406b735f736861383838000c4b7265736e61233333333300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144e883ef525616aa09096cbeb77c7810fd11cdaf9e53f64d20e43e41a437ecf8db2c5a7ad98c89c24": "0x040000000002000000000000000000000000000000000e546563686e6f4172746f726961000000186172746f7269616d617374657240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144ebb87c523e02c41a87f7dc3c8fd5d0b2d229e4fcb9aacda9427025218f69627ba1a2b82a07bb451": "0x04010000000200000000000000000000000000000000104f6d61646f7965204162726168616d104f6d61646f7965204162726168616d0000196f6d61646f79656162726168616d40676d61696c2e636f6d000010406f6d61646f79656162726168616d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144ebc48c63a5dc09fa4ac69b3ea42acebe4f70767ee894eeef5eef1f011f0dca45097a2e6c40c5366": "0x04000000000200000000000000000000000000000000036635000017406b7573616d61323233333a6d61747269782e6f72671e6b7573616d616b6f736d6f73696c613232333340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144ec6dcb09516ac9cae463a6613cae63a77d1c391bb1e00a974ccd178ebd91e00268608f3a570f84f": "0x040100000002000000000000000000000000000000000d5061736861426f7563686572000000177061736861626f756368657240676d61696c2e636f6d00000e407061736861626f7563686572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144ed12f7f95496d053674aa73951219dbd27b3e3fc5847b806c68c1de38fd4f22f9493a461c80e903": "0x040000000002000000000000000000000000000000000b472d444f542e54454348001368747470733a2f2f672d646f742e746563681740672d646f742e746563683a6d61747269782e6f72671467646f742d746563684070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144f50c6bb1b68e3b5944f4742584f6194f1ca08a3dd92d00d059439abb46629f6ea6bc0f6202dbb58": "0x040300000002000000000000000000000000000000000e44616d69616e20416d616d6f6f0e44616d69616e20416d616d6f6f00001464616d69616e40646f78642e6361706974616c00000e4044616d69616e416d616d6f6f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144fcc35394dbbfdb0eac8ab5909257a63ef3726ca5824a68902205beb1e58d578ceaba30ab3dfed65": "0x00000000000000000000000000000000000a464c5546464833414400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047144ff1355416158dcf7c4e85307ba0bb29b098e2f405c673f39b76d77fb012ed5f5c61fc68e1cbff80": "0x040000000002000000000000000000000000000000000f466f726b6c6573734e6174696f6e0000001868656c6c6f40666f726b6c6573736e6174696f6e2e696f00001040466f726b6c6573734e6174696f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145010d8218a994822c290e73cd4a89b696210d9f712410463c89368b6fc4d22ce10207d8f31e5e739": "0x0000000000000000000000000000000000054c58333800000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714503214bf4f8e7195eec846c98971c15deb2504433f2946dfa0c01f310d1e7b777b79fe0a1a279054": "0x000000000000000000000000000000000015506172616c6c656c2046696e616e6365202d2036001568747470733a2f2f706172616c6c656c2e66692f000000001f68747470733a2f2f747769747465722e636f6d2f506172616c6c656c4669000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714504497b5432c58213aa53e50cea84e455aaf2f9172db100c8068658ee1234f9bcbde84e5bfe52e54": "0x0403000000020000000000000000000000000000000007446f6c70686100000013636f6e7461637440646f6c7068612e636f6d00000a546865446f6c706861000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471450532746b82002af1ecfb64d5f40dbe9c02df58a8570cd2335acd41a107fe5fce9b3fb4f8d537d58": "0x00000000000000000000000000000000000773757975656500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471450541acb5b3adfcb783677be3528d7c562df11a31317f6138279cd39ef96abba3dd1f38e9896a37f": "0x0000000000000000000000000000000000127733662d7374616b696e672d6d696e657200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714505d0ff0b2ffbc1700004bcdcc7d30d597e19c7f87652b69736066b729f7e8543231a10760f8157a": "0x040000000002000000000000000000000000000000000a5354414b452d4f5053000011406876616c3a6d61747269782e6f7267147374616b65726f707340676d61696c2e636f6d00000a407374616b656f7073000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714505e4ce25c0327113230fd173f1cbd58c493f4f6488a8bd7d8594e167a45052ce8eb51f697e9e914": "0x00000000000000000000000000000000000b4d617961204163616c6100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714506b81d74d617d923ce9d3e9979b7600344c225b00d470e268d6c38616bafea38027a373ae3c3f3c": "0x040300000002000000000000000000000000000000000b61644c49425f4d5f4d531353657267696f204d6172636865736f7474690000146d6172696e614061646c69626e65742e636f6d000011406d6172696e616d6172636865736f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471450848286e4980cc86ef58fbe5043add10a359ead21248eb5575bd560ec489382cea3a7e360aecb71": "0x000000000000000000000000000000000005482d4d4100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471450aff8b6edbd559bf2529946850f8dd66c794a795a6b01a911f25df007e4cf5f97f38a037380f250": "0x040100000002000000000000000000000000000000000d446563656e74726174696f6e1b446563656e74726174696f6e20547275737420436f6d70616e791968747470733a2f2f646563656e74726174696f6e2e6f7267001872616d73657940646563656e74726174696f6e2e6f726700000f40646563656e74726174696f6e73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471450e0223a7f06f4edee2804a63951212e1342a2c01ea12f20153f27b6a5a649ef2e6c7b20d7859f68": "0x0403000000020000000000000000000000000000000015696b68616c65643238207c205761674d6564696100000014696b68616c6564323840676d61696c2e636f6d00000b40696b68616c65643238000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145126d11d32941450dc41188d0dcd6722402508e1eb7601ac5d225c923114ff63aa085f20756fc371": "0x040000000002000000000000000000000000000000000f566c61647950726f6d6f5465616d00001740766c6164796c696d65733a6d61747269782e6f726715766c6164796c696d657340676d61696c2e636f6d00000c40766c6164796c696d6573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714512ee0fca2d67ab7d573535a40bb01903e616a383deed22b5e3ff30e552017d2395e3e75a8e78613": "0x0401000000020000000000000000000000000000000010f09fa49620506f6c6b6153746174730b506f6c6b6153746174731668747470733a2f2f706f6c6b6173746174732e696f16406d6172696f70696e6f3a6d61747269782e6f72671a706f6c6b6173746174734070726f746f6e6d61696c2e636f6d00000c40506f6c6b615374617473000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471451affb7adda940b6380329063444b0d709fe60a6bd0ee966c85ee1da8d62faf0bf33c58d8a15f93a": "0x0403000000020000000000000000000000000000000005544f4e491c4a75616e20416e746f6e696f20496e66616e74652043617361646f00001d746f6e692e696e66616e746563617361646f40676d61696c2e636f6d00000e40546f6e695f426974636f696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471451b04076756292749ea8a333af594a76a48fd053a4ac0c48a87cdc5908f5099cdfb053d05cbddc3e": "0x04030000000200000000000000000000000000000000104a61636b2048616c646f7273736f6e104a61636b2048616c646f7273736f6e0000176a61636b406c756e617273747261746567792e636f6d00000f6a61636b68616c646f7273736f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471451b7261dd3a3db7140d9be44e5bbd9bb656741d05142d47c08ffbfd1f0157b5e79626dd51fc5270b": "0x0400000000020000000000000000000000000000000010416c6f6861426c6f636b636861696e00001c40616c6f6861626c6f636b636861696e3a6d61747269782e6f72671a616c6f6861626c6f636b636861696e40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471451dd6eaed401198dc44209e13e42fd156130e708866c28b0ac7cbd5b650011411b8979e5670f3a7a": "0x0000000000000000000000000000000000074b534d204c5700000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471451fe2259dc584cad041148d9102c91506d9e3e75297536084c9e3c3259ff78e4651ef4c464a04377": "0x04000000000200000000000000000000000000000000064e6f646c650000000000000e404e6f646c654e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714522288fcb9f80cd6c81b22d9b1c398a968215a07a4d62af23b6db994b0c6d6e7b8cf8bdd8c57e82a": "0x000000000000000000000000000000000004444f5400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145268dc052973ededba3ecfd7483cdcdad1132af7d1e8067816009cbd77fc0bc30eafe8d2218a1971": "0x04030000000200000000000000000000000000000000066e696b6f73124e696b6f6c616f73204b6f6e74616b697300164077697265646e6b6f643a6d61747269782e6f72671477697265646e6b6f6440676d61696c2e636f6d00000b4077697265646e6b6f640a77697265646e6b6f640000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714527efcd187a9fe40aa71da79c896004229d1592df63c4091b31356787a87c960e7a58ade7021cd77": "0x040300000002000000000000000000000000000000000a437574655f576973700d43686f726f6e67204a616e670000187377656174706f7461746f313340676d61696c2e636f6d00000b40437574655f57697370000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471452c8ca65b329df6b02f87ce6d066323c92f56d284faaa856ce083f9ba81635464f04fabec453c822": "0x040000000002000000000000000000000000000000001a4368616f7344414f5f4e6f6d696e6174696f6e5f506f6f6c730000000000000a404368616f7344414f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714531481d95ba52af5b2a75e17655fc2753328a8fe1ae22a20c8bb0ae8b7a7c809e8d84289571bcd1e": "0x00000000000000000000000000000000000b4a6f68616e44726f69641a4a6f68616e20416c6578697320447571756520436164656e611768747470733a2f2f6a6f68616e64726f69642e636f6d17406a6f68616e64726f69643a6d61747269782e6f7267000000000b6a6f68616e64726f69640000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145322942d92e4b0ac628524da485cdb93551dab991b7569b96ec32ea8dfab885c0ff21d76f781cf7a": "0x00000000000000000000000000000000000b44415955e88a82e782b9054441595510687474703a2f2f646f7465722e696f14406a6f69656375693a6d61747269782e6f72670d6a6f6965634071712e636f6d00000a40646f7465725f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471453839e0f2882afca84b58d6f589a104d2f4bdd8b76317d55c1576464e22dda803dd94162ba40c398": "0x00000000000000000000000000000000000f4c414f53464f554e444154494f4e164c414f5320436861696e20466f756e646174696f6e1e68747470733a2f2f7777772e6c616f73666f756e646174696f6e2e696f0000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471453a80b1cc8a5fa031212f18a064433a237a56022beffd0ca0f0baef317b34c7f6f12b19968f10233": "0x040000000002000000000000000000000000000000000a7464696d6974726f760000154074737665746f6d69723a7061726974792e696f000000000a7464696d6974726f760000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471453c13c992a5e1d2c9a201b14e4207ebacf5b729ff166ffa224bb4b5cfca02bdf9f3823a0e2979064": "0x04000000000200000000000000000000000000000000114c656168404f414b204e6574776f726b0000000e6c656168406f616b2e74656368000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471453ea4f8fb6ccab21e6f540b5dd5f15bf2d3f0eabd392a35bfe76c58cbb36227fb4c29785714e085b": "0x040300000002000000000000000000000000000000000e466c6f7269646157454253454f0d486f706520476f6f646d616e000015486f70652e436c61727940676d61696c2e636f6d00000a486f7065436c617279000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714541213aceb4fb60c1859cdd686b9d08a9fb3bc6b4c20b794339e028aa7300454deaaff5b5860200f": "0x0000000000000000000000000000000000084b4a646f74343400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471454259ba3fa998087a0d16c9a998cb8884bb937cc6192b7e962151cc0c19529d7aac7491f0617457c": "0x040000000002000000000000000000000000000000000a53746173526f766572000016406b61346f6b313333313a6d61747269782e6f72671873746173726f7665723133333140676d61696c2e636f6d000000001373746173726f76657231333331233632363300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145482a19c5b8df37d1e727755b4af4ed6b10e91b550ed5e30bfcd1d3eedec76e159e137ddf5fe9223": "0x040000000002000000000000000000000000000000000e42616d42616d2057616c6c65740000001662626d6f6e65796261677340676d61696c2e636f6d0000114042616d42616d527567677942616773001542616d42616d4d6f6e657942616773233738303300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471454a7cd8dd3cde7aa5ef0603406fea5cef09155727ee3f76c528d688ea48a5f0183bfb06bce5f7b20": "0x00000000000000000000000000000000000a636861696e796f646100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471454aeea67769129fb946d9ac73a5f11a32e8c8709d631304d2db1ebbbca156fc86b9f438286363814": "0x00000000000000000000000000000000000c426c6f636b6461656d6f6e001868747470733a2f2f626c6f636b6461656d6f6e2e636f6d0000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471454d7e633409fc98ca61cffec64bd3d89d7195fd423998178d8a4e6a0593ea5ced602f43d2e5ca346": "0x0403000000020000000000000000000000000000000009437361696e74303200000014637361696e746e303240676d61696c2e636f6d000009637361696e743032000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471455484e82afa6b522260092c95b675be8db8490bc0f55d7357a1cbb066f5198fcb7aa2d7f05377331": "0x00000000000000000000000000000000000c526567656e63792d3030341757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471455538dd590c80f13507c2d0784ffc68240a395784f16cd29f99f974a58876fa9e948fe4ffcb70334": "0x0403000000020000000000000000000000000000000021f09f8d80204c75636b7920467269646179202d204f70656e476f7620f09f8d80001868747470733a2f2f6c75636b796672696461792e696f2f00147279616e406c75636b796672696461792e696f000011404c75636b794672696461794c616273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145557cf88d37091a7a256aad87b40568d82450352060cb44fd4f22d491cda208f2f7da5b23ff23d01": "0x00000000000000000000000000000000000944594f5244594f52001c68747470733a2f2f6769746875622e636f6d2f64796f7264796f72154064796f7264796f723a6d61747269782e6f72670000000d4064796f7264796f72636f6d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714558060fa90d1941f87d108bb4f19a365b10f0ce5a87be412bfc89c359c4e6a34d9ab83532622e949": "0x00000000000000000000000000000000000c526567656e63792d3031321757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471455cbcfc5ff45de9288befc38a0a30b69022eac9e133fb589defc7a8c6b7f0dc601ded9804bd7e114": "0x040300000002000000000000000000000000000000000b4d656c612061644c49420c4d656c612061644c4942210000126d656c614061646c69626e65742e636f6d00000a406d656c6131303030000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471455ebf22a3378f22de4bd04a7052f76425c60648c528535285bc2a23ab28db060db34f7c5e5746aa9": "0x040000000002000000000000000000000000000000000954616c69736d616e0000000000000f40776561726574616c69736d616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714561df2dc51ef7baf3e89f959d4ae263361a6d7d0b87f1be73ad01356f5b982189f19c3be93fbe554": "0x00000000000000000000000000000000000d4f616b205365637572697479124f616b20536563757269747920476d62481c68747470733a2f2f7777772e6f616b73656375726974792e696f2f0014696e666f406f616b73656375726974792e696f00000d4053656375726974794f616b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471456305c2c3bf8f20e465c6be30d314cf647a8fa10212bcb84318394659c46fe75d03640cebb39595e": "0x040100000002000000000000000000000000000000000d416c7a796d6f6c6f6769737410416c7a796d6f6c6f67697374204f791a68747470733a2f2f7777772e7a796d6f6c6f6769612e66692f0015636f6e74616374407a796d6f6c6f6769612e6669000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714563affe7df4a40c094c7373d1e335e281b2a80565b3370c5de9d6b2d87fdf0cddac5641377a24b50": "0x0401000000020000000000000000000000000000000010526f636b585f506f6c6b61646f743306526f636b581668747470733a2f2f7777772e726f636b782e636f6d0012737570706f727440726f636b782e636f6d00001040726f636b785f6f6666696369616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714564cb6671f34ac580c30d360adcf621ab5e8754a9481cc15c1827574958fdd9fd15c5d4d526c7909": "0x04030000000200000000000000000000000000000000104179657662656f7361204979616d75104179657662656f7361204979616d750000166179657662656f73612e6a40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145680c43f6b36d90ffecaf47bf20372df1eab92aa6781caef84c728ef14a18ebc4c39e35789f8a23b": "0x0000000000000000000000000000000000044a6179044a617900000000000e406a6a65647361646131393937000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471456ae9d419c0660f83235f7d984058bb410c163fc1d7a90e5475c0917aad77deb241093a50b4f683f": "0x0000000000000000000000000000000000064272756e6f0e4272756e6f20c5a06b766f72631768747470733a2f2f6272756e6f2e6574682e6c696d6f0020747261702d706f6c6b61646f746964656e7469747940736b766f72632e6d6500000a4062697466616c6c73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471456b5cfba8996c0c084671f7cec13e5359aad22bffb62ca545c385e1fd68f0f3de08b74c15e58c25f": "0x0000000000000000000000000000000000025000000018676d2e736f646572626572676840676d61696c2e636f6d00000f4042616c6c616e74696e65733837000f62616c6c616e74696e65735f706d00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471456e0891e915252b140975dcdfcd7ecc14eb7f986e0dac66349e19973a66dfa6be7b61cd3fbbe143a": "0x00000000000000000000000000000000000e43494b6f6e746865626c6f636b001f68747470733a2f2f7777772e63696b6f6e746865626c6f636b2e636f6d2f00177465616d4063696b6f6e746865626c6f636b2e636f6d00000f4043494b6f6e746865426c6f636b00084a53233638373600", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471457130fa04591b33060283de9f5beb93ac69b24ca8c62f60f39b8c80758d807aa244532c66b67bc3c": "0x0403000000020000000000000000000000000000000012706f6c6b61646f745f636f6c6f6d6269610000001b706f6c6b61646f74636f6c6f6d62696140676d61696c2e636f6d00000c706f6c6b61646f74636f6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145721ebba28d18ffd9c84f75e0b1b92f6b003bde6212a8b2c9b776f3720f942b33fed8709f103a268": "0x000000000000000000000000000000000006616e6472650d416e6472c3a92053696c7661001740616e64726573696c76613a6d61747269782e6f7267000000000b616e64726573696c76610000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x040000000003000000000000000000000000000000000d506172614e6f6465732e696f001568747470733a2f2f506172614e6f6465732e696f164070617261646f7878783a6d61747269782e6f726715737570706f727440706172616e6f6465732e696f00000b40506172614e6f646573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145749edf76d87f942544e2e588c90a2e53e051d2f87d40e222e1f034913a30f95a9a2f39114e5be38": "0x040000000002000000000000000000000000000000001c436861696e487562202620546578617320426c6f636b636861696e00001440737269766973683a6d61747269782e6f7267186d654073726972616d7669736877616e6174682e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471457707f9d1b18edb4729324ff6798093939a73546e0f3d53a9cd7d4e938d238145c9422ce9f0beb07": "0x0000000000000000000000000000000000205374616b65444f54732e636f6d202d206279204269736f6e20547261696c7300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471457903c6184bae82f5a090c88f0438b46b451026597cee760a7bac9d396c9c7b529b68fb78aec5f43": "0x040100000002000000000000000000000000000000000d5365756e204c616e6c656765000018407365756e616c6e6c6567653a6d61747269782e6f7267197365756e40706f6c79746f70652e746563686e6f6c6f677900000d407365756e6c616e6c656765000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471457a5cae663c515ca00ecaacb451648a3660fe122ccc2c32cfd9459ca6dac9f10cbf7a0ab60c61318": "0x040000000002000000000000000000000000000000000d4a616d65735f4167656e6461000019406a616d65735f6167656e64613a6d61747269782e6f7267176a616d657340686f6c64706f6c6b61646f742e636f6d00000e406a616d65735f6167656e6461000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471457f0c0a9a6359c3474e4867b46b4d8ca428315963427c002b2e78d0faf10f6f7ce28a3d963cbc65d": "0x00000000000000000000000000000000000a626c756570616e646100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471457fba32d45d674cc3a8ec57f1e3455033d4e4a4d92afbafedade5b15bd8a9569eb77157b8163af4a": "0x00000000000000000000000000000000000577696969000000196a65756468666a74794070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714581f77b10468b1425440f21f8078b780afad2b1f6604939ee2d3d4cdfc2ed48eb1422e76a54e291b": "0x040000000002000000000000000000000000000000000d506572666563742d6e6f646500001940706572666563742d6e6f64653a6d61747269782e6f7267146b6174656b7261737640676d61696c2e636f6d00000f40706572666563745f6e6f646573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145833bff9f455466ed63a7eba107bb1e70778603a7c1a10288284b5acf296c15f0b616c19df2b8641": "0x00000000000000000000000000000000001352616d61207c20456467657761726544414f1152616d61205368616e6b6172204a68611568747470733a2f2f72616d61766174732e646576154066726f676d616e783a6d61747269782e6f72671872616d614065646765776172652e636f6d6d756e69747900000b4072616d615f76617473000972616d617661747300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471458397d7e3fd835eec870531deb0be32ec5f13db615776a199622cf2b2a8d5061be0a30d560ac5413": "0x040000000002000000000000000000000000000000000a4162696c657820414700000011722e7a6f6e69406162696c65782e6368000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145839ea98689b2889bc6e12d7ab70abea4c08db7055e84f16bab817b5fb359088ad5190422df9dd1d": "0x040000000002000000000000000000000000000000000e416c657850726f6d6f5465616d00001340616c65782d6d3a6d61747269782e6f72670000001040416c65785f50726f6d6f5465616d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145874725d77d3161042fe55f037d3acfd9073a0110f9a241c7c0b484ee39ebcd77e7952582be5463e": "0x040300000002000000000000000000000000000000000f48656e202d2053706561726269740000001368656e72794073706561726269742e636f6d00000d40537065617262697444414f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714587f0b18f53b18b02aa10e36f0d84091e5850da2564bf99d14d5b9837c1ee2fa8bc068c995994379": "0x040000000002000000000000000000000000000000000a4578747261436f696e0000104079726e3a6d61747269782e6f7267187961726f6e736b694070726f746f6e6d61696c2e636f6d00000c404578747261436f696e5f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145901a7d03ac32e8c460411e07f93dc4bc2b3a6cb67dad89ca26e8a54054d13916f74c982595c2e0e": "0x000000000000000000000000000000000006436c61726111436c6172612076616e2053746164656e00000000000f436c61726156616e53746164656e0f636c61726176616e73746164656e0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145955584d4460250b9d00cdd9010cddfdc119c7ce69b4e122261ee68848e50daaaed117df2a7b6f7b": "0x0000000000000000000000000000000000174f6e72616d7020426f756e74792043757261746f7273002168747470733a2f2f74696e7975726c2e636f6d2f444f542d426f756e74792d320000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714599af3f3a616204088116da7569ffa5b8634742c63b9eaff0d5334e8f68588bef54e28e6ba64d04b": "0x04030000000200000000000000000000000000000000066b617665680000001d6b61766568746865626c61636b736d69746840676d61696c2e636f6d00000d6b6176656874656872616e69000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145a0dda79fdf58ba93657cdadd375b91ff01e797249907eeaa2d28e7f882f4176d43140767162111f": "0x000000000000000000000000000000000009627962742e6f726709627962742e6f72671168747470733a2f2f627962742e6f72670011737570706f727440627962742e6f726700000a40627962745f6f7267000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145a322d41358015c5820e859e96c107c3dc5e0b110d8e5a7fd2bf312b511b00a5a45e59eeec47d741": "0x0403000000020000000000000000000000000000000009436872697342636b00000017646f706579706875636b40686f746d61696c2e636f6d00000a4042636b4368726973000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145a53642bcce8d5192055808c210d863dfc372ec85beafa8fd3a8ff497f8eaee401ef05bf27d3065b": "0x04000000000200000000000000000000000000000000214a696d6d7954756465736b69202d20506f6c6b61646f74205265736964656e74000016407374616b656e6f64653a6d61747269782e6f72671b6a696d6d7974756465736b69407374616b656e6f64652e64657600000f407374616b656e6f64655f646576000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145a54b00d6ab21bbd58a74372b1f0a88b1df304584326ff69a68e9470d42687e12bf5dfac375d4911": "0x040000000002000000000000000000000000000000000c53454b4f5941204c4142530000154073746577617274763a6d61747269782e6f726713746f6d4073656b6f79616c6162732e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145a7e0322bf0bc3b7eec3f748ce7ebbe4bd41e991a04eb92b050d5e80667e60938c6a5224d66cd464": "0x0403000000020000000000000000000000000000000011546865204772656174204573636170650000000f676d40706c61797467652e636f6d00000940706c6179544745000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145a9ac6c6fb60e3add06b046f9929cd9ec3494f87b7ba1b533f8313c8132896639b110063c81a605b": "0x0403000000020000000000000000000000000000000011506f6c6b61646f7420496e736964657211506f6c6b61646f7420496e736964657200001b706f6c6b61646f742e696e736964657240676d61696c2e636f6d00001140506f6c6b61646f74496e7369646572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145ae3d02694d32b74b4154f12cdef88338edfa85cbdd64b61c410187e4f7971057aef32cdafdfb702": "0x0400000000020000000000000000000000000000000016f09f9bb8205a6f6f70657220436f727020f09f9bb8001868747470733a2f2f636f72702e7a6f6f7065722e6f726717406a6f686e756f70696e693a6d61747269782e6f726710636f7270407a6f6f7065722e6f726700000c407a6f6f706572636f7270000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145ae5cf79085c900374799c49548c591db342eb29f486980d7e28e687f9f88f9f602f050e8e106c64": "0x040000000002000000000000000000000000000000000e544f425553494e4553534c41570000001b746f627573696e6573736c6177406a6b6c65696d616e2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145ae8d02fc1cc6b946e53696350731ed439f8c353b0b69b40b324fcdcb435ba225fbc22a33c9fe154": "0x0000000000000000000000000000000000074f6e656d6169001368747470733a2f2f6f6e656d61692e6e6574000000000c406f6e656d61695f66646e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145af116176166f3dcdc891490870515c71014938641e9b09cfbfadeb502b16d67d2cff9145aaa9a75": "0x0403000000020000000000000000000000000000000009426c6f636b41544c09426c6f636b41544c1a68747470733a2f2f7777772e626c6f636b61746c2e636f6d2f0015636f6e7461637440626c6f636b61746c2e636f6d00000b40426c6f636b5f41544c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145af59607ad06416224714636e0203b3a39bdf5a84df455ee05f6449e2df03fbbc7ed49be08182d7b": "0x0800000000020100000002000000000000000000000000000000001a6e2d667573652056616c696461746f722023312053746173680c6e2d6675736520476d62481668747470733a2f2f7777772e6e2d667573652e636f154076616e74686f6d653a6d61747269782e6f72671563727970746f2d6f7073406e2d667573652e636f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145b1cb8848fc74ed707b738ebac4b30c502415704c8a06c9194c3df1f2529855f959b1dbd43377554": "0x00000000000000000000000000000000000631335745421757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145b301c74acab4e7e12ccb53338ac0da571d3697548346fb5f0b637ac9412f8abbf6d13588be75632": "0x0800000000020100000003000000000000000000000000000000000d5265676973747261722023310d5265676973747261722023311868747470733a2f2f7777772e63686576646f722e636f6d144063686576646f723a6d61747269782e6f72672063686576646f722b706f6c6b61646f745f7265673140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145b37a7061f92e1981e7c14ac9433d4637fecd8b61cfe017b31145aeba9742370c8ac715fe009ff41": "0x00000000000000000000000000000000000b6469707379764d41494e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145b4baec7933bf780a85e51387dd437dd83dc7f9c2a7e59bf0590e9dd48758b6abbc463b3e26e5163": "0x040100000002000000000000000000000000000000000c4a6f6e6e792052696e676f124a6f6e204b6f64792057696c6c69616d731f68747470733a2f2f6c696e6b6564696e2e636f6d2f696e2f6a6f6e37313100156a6f6e6e794064616f61636164656d792e6e657400000f406a6f6e6e7972696e676f373131000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145b4c4e58d29949859a2cb674ea2f4866664769a1663fd6aa321d9cfb89b67c402c881891700c0f57": "0x04000000000200000000000000000000000000000000084c6962657274790000001c6d657461706172616469676d4070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145b6407bdc635903a666c474e2f859bb39716a333ad449122df3605cf2d272ab876171d0a61cbdd07": "0x0401000000020000000000000000000000000000000008706f732e646f67001068747470733a2f2f706f732e646f6712406d616f79753a6d61747269782e6f726711706f6c6b61646f7440706f732e646f67000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145b98628a4ffdb492fc5c731996ade530d0289dd11a6ccf4e246ea5054119d2fe15a2da5c2ef01062": "0x0000000000000000000000000000000000000000000000000012416964656570616b6368617564686172790000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145bab10ce7448709e7c6781a204088ce17d88592c07355af5a72a81a33011ca9c0b8f79329050ee0d": "0x00000000000000000000000000000000000a67696761686965727a0c4c656e6120486965727a690000166c656e61686965727a6940686f746d61696c2e646500000b4047696761486965727a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145bbcb49931f6e14912ec4426bd4c3d506b2322b8817ff42bc670792fd1aa4cfa662548fdb0e60b15": "0x00000000000000000000000000000000001047616c6163746963436f756e63696c00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145bd6a3e1e8406faa3c82ab06b794c99f14a161973be7aa6012568b1c491d45ec969ed7420bcfaa59": "0x04000000000200000000000000000000000000000000044a6f650e4a6f6520506574726f77736b6900000f6a6f6540706574726f772e736b690000000d6a6f65706574726f77736b690000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145be461a0929fc6dc1da1e4d7d2a6a1e28f8778ca2d924672d83abcb6c5a2240b8186d4aa5cce2569": "0x00000000000000000000000000000000001c546563682041636164656d7920436f6469676f204272617a75636100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145c2c88f13736061bb6ca4dd8f15039b1550849ac495b65fe2e1e93daf4be62c4c4adf440ae155d68": "0x040000000002000000000000000000000000000000000a57696e6b727970746f00001540736c797577696e6b3a6d61747269782e6f726713626f626f4077696e6b727970746f2e636f6d00000b4077696e6b727970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145c66629676bdc5f4766efcb5851054b4aeef694a3ad03d7bf36980a2094c07cb7b7cda28bde5e149": "0x0400000000020000000000000000000000000000000018454c444f5241444f2d544543484e4f4c4f47592e6e6574002068747470733a2f2f656c646f7261646f2d746563686e6f6c6f67792e6e657415407061756c2d6769653a6d61747269782e6f72671d7061756c40656c646f7261646f2d746563686e6f6c6f67792e6e6574000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145c6942d78c3e288a0842fe19dee2029fe63ae78b250fb0ed99ebcc6a72b36e7f56160150c230f237": "0x0400000000020000000000000000000000000000000008636d616c697a6500001640636d616c697a6530313a6d61747269782e6f7267166d616c697a65636872697340676d61696c2e636f6d00000e4063687269736d616c697a6533000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145c6b1c34b6431f3840b9259dde4ecf577907b60e73ac636e896ac881e1f44c1bab1062fce8edef10": "0x00000000000000000000000000000000000e42414a554e204e4554574f524b1c426c6f476120546563682041472028537769747a65726c616e64291268747470733a2f2f616a756e612e696f2f1540726f786f6e746f783a6d61747269782e6f72670f68656c6c6f40616a756e612e696f00000e40416a756e614e6574776f726b000d6461726b667269656e64373700", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145c74ede8dfd9a44fd833a6cfefacabd9675f88e02953e4341badc949471b18f4e54b07e7383d6fbe": "0x00000000000000000000000000000000000d4f4720436f7265205465616d001768747470733a2f2f6f70656e6775696c642e7774662f00186f70656e6775696c647465616d40676d61696c2e636f6d00000e406f70656e6775696c64777466000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145c9b7dbdbf5f8eeb76446963a70e86cdaccc02ab22bf59bca9b5eb5fdb0353b144fbbddcff8a3a69": "0x04010000000200000000000000000000000000000000124465657020496e6b2056656e7475726573174465657020496e6b2056656e747572657320476d62481a68747470733a2f2f646565702d696e6b2e76656e7475726573174067656e6573697364616f3a6d61747269782e6f72671861646d696e40646565702d696e6b2e76656e74757265730000104047656e6573697344414f5f6f7267000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145cf461f932bdcdbafcfd589d8df6da23f65a51de867fac9490ead3ffbb36ce8d1946cec1789a9a46": "0x040000000002000000000000000000000000000000000d43727970746f4c61622030310000144079616f6873696e3a6d61747269782e6f72671779616f6873696e4070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145d2b112c788bfa92d474d9a6e955d7813d04f0fe2b5d35f0986837b7943975699d13ce159067082f": "0x00000000000000000000000000000000001648696e646920456475636174696f6e2047726f75701648696e646920456475636174696f6e2047726f7570002140616d69745f68696e64695f656475636174696f6e5f67726f75703a6d61747219616d6974736861726d616c65616440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145d3dec38e02e37251c6d8b40be9990c19e993d238e6e3613cfc6cbc51979d5fa61dd6ea259385609": "0x0400000000020000000000000000000000000000000007416d666f72630a416d666f72632041471368747470733a2f2f616d666f72632e636f6d00137374616b696e6740616d666f72632e636f6d00000a40616d666f72636167000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145d6b2bca719d97c7ee2019dd45affea8eafaa263f346c25b477cef6858bd732898994e464cb57211": "0x00000000000000000000000000000000000c484f544249545354414b4510484f544249542045786368616e67651668747470733a2f2f7777772e686f746269742e696f0012737570706f727440686f746269742e696f00000d40486f746269745f6e657773000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145da2ed96282667842cf42c1d5eca2e17a00edde3c54ce01a2c1e0c070ebaa11ad9eb85083263b877": "0x000000000000000000000000000000000007444f542e6a7300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145dfe5da7f34068cb40668c14ac6f4e09373373ed8cc2fc6c7e092b10769b571af5c02bf4765ecd53": "0x0403000000020000000000000000000000000000000008416c626572746f0e416c626572746f205061686c65000018616c626572746f2e7061686c6540676d61696c2e636f6d00001140484552435f48414d4d4552444f574e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145e30111214665710be033afa19dd93fa4303e4c72b00b2c4102ff8c9b2fd75292774b56ba44de566": "0x00000000000000000000000000000000000d56726b3364735f537461736800000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145e47309d56ee80fcfc1af31cb4678204d2f643f29290d127fc9fc4ad0f148e258ceb48764763e062": "0x0000000000000000000000000000000000044e614e0013687474703a2f2f7777772e6e616e2e78797a000d6c756b65406e616e2e78797a00000a406e616e5f78797a5f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145e680ae5b0223aa782a9563f0050686a13dc52d6a266b9cf19325ce9375f6cf13ae4712688011d52": "0x040300000002000000000000000000000000000000001070726f746f636f6c776869737065720b4368726973204d61746100001770726f746f636f6c2e65746840676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145e68d454836a65b5902795e8d37421da1234a1a8f330fa910581ef0bfa3204d5bda6840890c6d23c": "0x04030000000200000000000000000000000000000000086c6964616d616f086c6964616d616f0000146c6964616d616f406c6964616d616f2e636f6d00000c426573744c6964616d616f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145e9f43767f234e4e0eb07d0fb791114259b3ae2ff2c186ffeb9f3c0cf05b5008a31994553b544c46": "0x00000000000000000000000000000000000558696e6100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145ead34c4d42568f9c0880e0e0918866661f9918d66b53287c95269d24e99121ee80618ca5f253666": "0x040100000002000000000000000000000000000000000c686173687761726c6f636b074a6f736875611868747470733a2f2f686173687761726c6f636b2e6465761840686173687761726c6f636b3a6d61747269782e6f72671a686173687761726c6f636b407068616c612e6e6574776f726b00000d40686173687761726c6f636b000c686173687761726c6f636b00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145eb695b4e8f904c91c2dd1f8db4faa532a9904dcfedc1c53eedf6307b54cbabe0b5b96c52d886c6f": "0x04030000000200000000000000000000000000000000115469676572204d6f6465204d656469610f41647269616e20526f6269736f6e00001a61647269616e4074696765726d6f64656d656469612e636f6d0000104074696765726d6f64656d65646961000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145eb9bc0505da44278aa29a2af6e85eb0bb2d2f00a6894ce0e83ebca01e2830da7678516cb7e84416": "0x00000000000000000000000000000000000b546f6d42726f7468657200000012746f6d676162724079616e6465782e7275000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145ed1cc4a4cfee745c427dd8b136f4c7463ab9a5761b8b4d7b1280e0d14ef070958db8641cb41b417": "0x00000000000000000000000000000000000a574152414f4348414e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145ed4004f0c9e60f942aa0cf9b476762d4948072578e972faa9462a11b5ba17b1db8fcd8cc1d0420c": "0x00000000000000000000000000000000000c4235382046696e616e63650e423538204c61627320496e632e1468747470733a2f2f6235382e66696e616e63650014636f6e74616374406235382e66696e616e636500000c4042353846696e616e6365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145ee8405cff42241194339db8b404ea216d60433f00ed67b0cdcd9e29d21355615d967161db0cb04c": "0x040000000002000000000000000000000000000000001f5354414b454e4f4445207c2056414c494441544f5220414c4c49414e4345000016407374616b656e6f64653a6d61747269782e6f72670000000c407374616b656e6f64655f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145eeade208c2c3d2213aa0eae4c9244da2b6bb58f13febd792db76c932b3bc64d0035df4735c8db02": "0x00000000000000000000000000000000000c526567656e63792d3031371757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145f3b96e8f7e196621cfbfde369c6f58b4df770cc3ba47d6583c0e9603007338779cbad7426767578": "0x00000000000000000000000000000000000b4d5843504f53504f4f4c001468747470733a2f2f7777772e6d78632e61692f0000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145f8f8fbe3a4a3e1256ae9b2d74ab4bb07306faf10549792549c52d4d092169530151a2aaeab28a0f": "0x040100000002000000000000000000000000000000000f446f745363616e6e65722e636f6d0f446f745363616e6e65722e636f6d1768747470733a2f2f646f747363616e6e65722e636f6d001561646d696e40646f747363616e6e65722e636f6d00000f40546865446f745363616e6e6572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145fc4a5c686ea5a40b346948ec9e4cf84b965ec17a752b3e8eff098934aaad42ec50a347dd7936583": "0x0401000000020000000000000000000000000000000006716472766d0a51756164727669756d1168747470733a2f2f716472766d2e696f14406b616d696c73613a6d61747269782e6f72670b6b40716472766d2e696f00000a40716472766d5f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047145fcf6e3e8fa7f7d4544eefa255546c84bf702f6d81c6828033cc49f7043e62043a3e83add44c6209": "0x040300000002000000000000000000000000000000000662727a64730d42726164204472657966757300001462727a64736f6e747640676d61696c2e636f6d00000962727a64735f4744000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714601c869e08b51af7b04b58ffedd058a81a625819d437d7a35485c63cfac9fc9f0907c16b3e3e9d6c": "0x0000000000000000000000000000000000074b68656f707300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146028af9a7bc6edd8d4c9b5341b6040e6f218c5476e5bb87a26fd80e03a0ac65b92d4d3eb917c8f22": "0x00000000000000000000000000000000001042494e414e43455f5354414b455f351042494e414e43455f5354414b455f35000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471460c30e5ee0f16de5405a662b1e68b18eca577e175d2bb0b12a71d7c46bdb9ad0a546ab81686b245a": "0x0000000000000000000000000000000000056a73647700000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471460e96a9630dce3d89c7e01287164e51a07f34b17c26c5eff66e1954d347310da015ac23e895a4150": "0x0403000000020000000000000000000000000000000014456c6973615f4576656e74735f426f756e74790d424f544c61627320476d624800000e656c697361406b696c742e696f000010656c69736166726f6d6265726c696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714613c4c21e4ca890ad2e5f1942410cb763e8418f7c2c9afe20141d6ac4f780c4d1ce3dd2205426865": "0x040300000002000000000000000000000000000000000964616e6963756b690e44616e69656c2043756b69657200001364616e6963756b6940676d61696c2e636f6d00000964616e6963756b69000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714617869f5844780e5f84795f2f68a2c43453b25ab4814c460f257d9bb05d04596499fd3211821076a": "0x04030000000200000000000000000000000000000000084d616861766972164d6168617669722047616e617061746920446173680000176d6168617669722e732e647240676d61696c2e636f6d00000a32374d616861766972000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714617ca8c9a7bf519bdc156e868d00cf92479a2417ebcdafbf0ce336f4cf3a818be068c49ee0562e79": "0x000000000000000000000000000000000008436c656d656e741e436c656d656e74204f6c69766965722044656e6973204a756d656c696e000018636a756d656c696e4070726f746f6e6d61696c2e636f6d000000000972756d656c696e6500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714617f389a0cb0d26a8659ed6f5a98bf0e1718fcc89e8e55a550e220da0f5de46f356297ea2af72c4f": "0x040100000002000000000000000000000000000000000f426f756e7479204d616e616765721467616c616e6970726f6a6563747320476d62481a68747470733a2f2f67616c616e6970726f6a656374732e6465001a626f756e74794067616c616e6970726f6a656374732e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714618fd93a58f35389e2680e2c991a18cf7ba4710a51ba147b6856879a3047a03f8c647c69b953d630": "0x040100000002000000000000000000000000000000000751696e57656e0751696e57656e1a687474703a2f2f7777772e71696e77656e77616e672e636f6d184071696e77656e3a776562332e666f756e646174696f6e1771696e77656e40776562332e666f756e646174696f6e00000d4071696e77656e5f77616e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146191809a9acac4bb8c529de435a764457d6247ef93b5cc72fd73b922e4903834dfb43dbf64013365": "0x0800000000020100000002000000000000000000000000000000001243525950544f4c41422e4e4554574f524b001f68747470733a2f2f7777772e63727970746f6c61622e6e6574776f726b2f154074616e69735f33373a6d61747269782e6f72671b74616e69732e737461636b4070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471461d35c4872d484b694c860705264b96854acc3cb307365132bd131524ef83a7c014378ed79373723": "0x0000000000000000000000000000000000164d65786963616e20436f6c6c65637469766520484100000018646f7473616d616d657869636f40676d61696c2e636f6d00001140506f6c6b61646f744d657869636f5f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471461e96b98dbac9ae8cebcf37135a013069cdbafff3cc1074943f5ce992bfa90c58dec50c1440dad12": "0x000000000000000000000000000000000013506865656220506f6c6b61646f7420352e31132f722f506f6c6b61646f745f4d61726b6574000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471462075dba595e3a2898a570ec356d2f77d890fb7643ffbf7a92c442af3f45dbd87a5ca8f17ff5b679": "0x00000000000000000000000000000000000f44657672696d50656e64756c756d0d44657672696d20436574696e1b68747470733a2f2f70656e64756c756d636861696e2e6f72672f001964657672696d4070656e64756c756d636861696e2e6f726700000a4064657672696d6277000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714625e8e7181f526d87a202a62e71ca9d711e66c2e8587d830388f723563b782611849824b42608542": "0x000000000000000000000000000000000009504f4c4b41444f5400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146277dfd6b7b21ca2faf5f68fe828f5af8d69c116efd937d90f1956b0edd94e05d8b5285a8eb2a663": "0x04010000000200000000000000000000000000000000115374616b696e6734416c6c20f09fa5a9001d68747470733a2f2f7777772e7374616b696e6734616c6c2e6f72672f18407374616b696e6734616c6c3a6d61747269782e6f7267167374616b696e6734616c6c40676d61696c2e636f6d00000d407374616b696e6734616c6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146279b764d4a4ad2eaebfc0f0232c4a23c29dba65a33f7611db55e924e4b0a65950694b857e33764b": "0x0400000000020000000000000000000000000000000008636179626163680000000000000d405469656e43797072657373000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714627f8fe7f248c866bc19869b7dc67efaa0923dd74b337c57baaedf41a1d002534f70cf4520b9b642": "0x040100000002000000000000000000000000000000000549505345001668747470733a2f2f697066737365617263682e696f000b686940697073652e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146281ccf16900ef3cac00a2740247063b6f890eea8309fa99d0fedeeb27602925e1de7e58b0ec6f6a": "0x00000000000000000000000000000000000d50617261636861696e333538134b415a554e415249e3808049574154414e4900001d776f726c642e6f6e652e696e766573746f7240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146282c39a18dfcab98e5d5f02fdd82b7c0ab38bd6093d17261f4c862f22caededb3a808c12a7904f4": "0x0000000000000000000000000000000000096770657374616e61001568747470733a2f2f6770657374616e612e636f6d000000000a406770657374616e61096770657374616e610000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146293ae906f82830db8a2ad752a280c8f9d461025191b195f8c1f60ea080604c24c1aebe1ebf6415a": "0x0000000000000000000000000000000000075279616e486f00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471462a40c29b1fd2c93b204051d55c2c80cdf6e0d3346944e921fc97f0b86a4cc5eb5547982fc475d68": "0x040000000002000000000000000000000000000000000b5374616b6520506c7573000016407374616b65706c75733a6d61747269782e6f726713636f6e74616374407374616b652e706c7573000011405374616b65506c757343727970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471462b931cf40e0dd8a4278dc8473c1319b51e5e326374b22f4b290bbf716f1d5a6e6bec98e2ac42c03": "0x00000000000000000000000000000000000967616d613238333000000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471462d6ffba2553d09e31e11c530aee0dc228f734335dba8e77607b187c46b1c7c5827df020f94b0400": "0x0403000000020000000000000000000000000000000016446563656e7472616c697a6174696f6e5f6d6178691b416c7661726f204d616e75656c20476f6d657a205a756e69676100001461676f6d657a34323340676d61696c2e636f6d00000c40416c7661726f5f5f677a000b616c7661726f6734323300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471462d8d4af4eadb392cc52156f09978540d3163b798f65a8788bd04d828f366674bf9ea1f88e96f519": "0x040100000002000000000000000000000000000000000b48595045525350454544000012406c6f6b616c3a6d61747269782e6f7267146c6f6b616c40687970657273706565642e6175000009406c6f6b616c7070000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471462e3e6769daa8cb66aaa8240cb6f2f8c0a86cf27c3f39f980074e7ec3d000bc0248978936bb9a83c": "0x000000000000000000000000000000000020524146207c20506f6c6b61646f7420416d62207c2050424120416c756d6e690c4a656f6e67536f596f756e1c68747470733a2f2f6769746875622e636f6d2f636f636f796f6f6e184070726f6f666f66796f6f6e3a6d61747269782e6f726714636f696e796f6f6e4069636c6f75642e636f6d00000d4070726f6f666f66796f6f6e000e436f436f596f6f6e233631313100", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471462e49551b36f932cd8d4e647a65738b7ec76c4e59af4b40d167270e55d008acabc172759d51e0e7b": "0x040300000002000000000000000000000000000000000c5468616e6720582e2056750c5468616e6720582e2056751268747470733a2f2f7468616e67782e7675134073696e7a69693a6d61747269782e6f72670d6869407468616e67782e767500000c407265616c73696e7a6969000773696e7a696900", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714631b52e3f40c72ed64a020cd2e215de928d7f9defcd9380a7e2415b8c3e22444ed2219cf95fa2e1c": "0x00000000000000000000000000000000001141204b616e65204d792057616c6c657400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146332adb00a8dc85a427150f6dee04d6e06f06a62a3509ba0b0cfa0ac01a69558616402b7498f1247": "0x00000000000000000000000000000000000d536f7461207c20506c61736d0e536f746120576174616e616265000011736f7461407374616b652e636f2e6a7000000e40576174616e616265536f7461000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714633914b3a4ceda021eb38b0d5178bc680c10a204f81164946a25078c6d3b5f6813cef61c3aef4843": "0x040000000002000000000000000000000000000000000e416c69636520756e6420426f6200001a40616c6963655f756e645f626f623a6d61747269782e6f72670000000f40616c6963655f756e645f626f62000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714634b6c2676d6ffce12751adf007e19834923db0f540c0f255901febc74640782cff748e94c65c72d": "0x0000000000000000000000000000000000096461746170756e6b00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714637afeda6c31f8ce54857fcb60606080a3ad974ef8d8673f899b3e1983f1c512f2f671c79f2b4a55": "0x0400000000020000000000000000000000000000000006416c696e6100001940616c696e656865727a6d616e3a6d61747269782e6f726714616c696e6140616e74697363616d2e7465616d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471463b1d194ccf414dfa4e5f5b5bfc485119954be727698c6eeb542b909a2d40014822d9dd0ebf91a1f": "0x0000000000000000000000000000000000084d617175655f3100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471463cb40b01c7b706af03a9fd09c8d3f8b134a20a873380efc542f5b85606cf62ffaa7fa302d7e0e23": "0x040300000002000000000000000000000000000000000c456c656374726f636f696e0c456c656374726f636f696e000014696e666f40656c656374726f636f696e2e657500000e456c656374726f636f696e4555000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146419f57ea3f84915121b12be1a2b918b9d01ff05c3d2fe5a28769cb747ff9fd7ecafc320dd5f810b": "0x00000000000000000000000000000000000f446f744c65617020456469746f72001f68747470733a2f2f6e6577736c65747465722e646f746c6561702e636f6d0013656469746f7240646f746c6561702e636f6d00000940646f746c656170000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714647269eb11608c5734a12ea862a1818defeb3fcde0962c89b7e71184e08ebbfddca97b9954b43144": "0x040300000002000000000000000000000000000000000944616e69656c20431344616e69656c2043686d69656c6577736b69000017636f6e74616374406463736f6674776172652e78797a00000964636f6d706f7a65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714647a84ffd21ef58f323e0ef6182a1c24b15bad084d75a03ba5aa787e7af723327287f6df292e6f76": "0x040300000002000000000000000000000000000000000e546865205765616b2048616e64000000197468657765616b68616e64646f7440676d61696c2e636f6d00000f405468655f5765616b5f48616e64000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471464c511948fee226142c119baba24ac08d19b6f23bd0752b81f5169c331d39481519795bee26dc350": "0x0403000000020000000000000000000000000000000004536f770d4d6177657961746120536f77000014776579617461736f77407961686f6f2e636f6d00000b40536f774d6177657961000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471464eea8ed6e0c669be2baaa3eda02d880fa4ed98b9de89e16e2a064d649231f53c00c27403fc6ea7c": "0x0401000000020000000000000000000000000000000011546967657250726f204361706974616c001c68747470733a2f2f746967657270726f6361706974616c2e636f6d1540746967657270726f3a6d61747269782e6f72671c636f6e7461637440746967657270726f6361706974616c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714651097b4998394e146d1b7fd733a68d1c3e53d6bfd7134a5803fe5a4033c2dc9eba2e31dc21c4a65": "0x0000000000000000000000000000000000076272656e7a69000013406272656e7a693a6d61747269782e6f726700000009406272656e7a6935076272656e7a690000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471465326f57f6604b32cca044696fc5f11711266f7da0da4d0b94e34420cdcaa9f3e9e06939e551d27d": "0x00000000000000000000000000000000000b646772656174616e64610b416e6461204461766964000015646772656174616e646140676d61696c2e636f6d00000c40646772656174616e6461000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714653768733bf980751cb8badbed1198dcece2e14047133b22e64bd06a9cf74ec5c8f94f668d1f2720": "0x04000000000200000000000000000000000000000000164b656e6e7920284d616e7461204e6574776f726b29094b656e6e79204c690000146b656e6e79406d616e74612e6e6574776f726b000011407375706572616e6f6e796d6f75736b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714657bab39f52fddf58e07d43b19d901badf3a7f57155ca84f2f835448e93a141bbbd33eac4b767d15": "0x040100000002000000000000000000000000000000002163727970746f7374616b652e636f6d20f09f87a8f09f87adf09f87baf09f87a60c43727970746f7374616b651868747470733a2f2f63727970746f7374616b652e636f6d001661646d696e4063727970746f7374616b652e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714657f579f3442148eea0ab1b08b58a3708b50ba9928c4e25ad71d68efcbb868a2f75b987d0e8e4108": "0x040300000002000000000000000000000000000000001063617473776974686f75746861747300000016736869746c62674070726f746f6e6d61696c2e636800001063617473776974686f757468617473000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471465b47ab9f3aec5ec06e3ed1e088da56a1e7ea6b57a856a0ead9e03bfbbd1ec74b33153e35015f10a": "0x0400000000020000000000000000000000000000000008546f6d69747a7500001440746f6d69747a753a6d61747269782e6f726712746f6d69747a754070726f746f6e2e6d6500000940546f6d69747a750008746f6d69747a7500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471465ce07032d38638e0453c20a8f8f0b374ec6d08c489e06e74686a2233bcf6422f345dfaab11fda26": "0x040300000002000000000000000000000000000000000b54656e7462616b657273001768747470733a2f2f74656e7462616b6572732e636f6d0017636f6e746163744074656e7462616b6572732e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714660e4d7cfca9e1ef4263436c1c3bd22e1a1e09401982bb2df2e6e6c449417a5ad157d07ab2319243": "0x0000000000000000000000000000000000074a61766965720d4a61766965722056696f6c611868747470733a2f2f6a617669657276696f6c612e636f6d12406a61766965723a7061726974792e696f000000000a7065706f76696f6c610000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146619f3097120b33a1bbdfb8e2904d640a26915c57200ac20ee110d6490d5e5129271b09d2ebf2c36": "0x040000000002000000000000000000000000000000000a446f6d694e6f646573001568747470733a2f2f646f6d696e6f6465732e696f001368656c6c6f40646f6d696e6f6465732e696f00000b40646f6d696e6f646573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714661f86092c2992fefa8bd5aca42a81a5b5e5d82a75204214606e67517458dfb94363da5cebc42c15": "0x040300000002000000000000000000000000000000000a526563726166746572000000157265637261667465727840676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471466331c5b2aefc4da60e469433111edea77c7b0075788c8ce009ad00aeed8529b9c26f26552e81948": "0x0403000000020000000000000000000000000000000007e8b5b5e4ba9107e8b5b5e4ba9100001873746f6e65737461723139383640676d61696c2e636f6d00000f4073746f6e657374617231393836000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471466334165bb2310561082b1a911dd8e9d01ebec53598da502cf118af543db458ce6d0e60b43a5af66": "0x040000000002000000000000000000000000000000000c546974616e204e6f64657300001740746974616e6e6f6465733a6d61747269782e6f726714696e666f40746974616e6e6f6465732e6e6574000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714665d1a1f97f0cb0ebec5d127b5bc86b320b802f368a9cf427b3682d973b9fe41a9e6f5bff108432b": "0x000000000000000000000000000000000011426c6f636b636861696e20506564726f12506564726f204c75697320526976657261001c40626c6f636b636861696e706564726f3a6d61747269782e6f72671a426c6f636b636861696e706564726f40676d61696c2e636f6d00001140626c6f636b636861696e706564726f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714665d24a2e48cf277dc50817f1905a0dbf31de7065eb1891100f8bb18005786b68082ea625d6f6325": "0x04030000000200000000000000000000000000000000096568696c64656e620000001f657665726574742e68696c64656e6272616e647440676d61696c2e636f6d0000084072765f696e63000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714667070096dd1a56f92db463a407b5818299a44b9db0727c16e9cd6b64dde2db70a592421be9f9b29": "0x04030000000200000000000000000000000000000000084d6f72616c6973084d6f72616c697300001168656c6c6f406d6f72616c69732e696f00000c4d6f72616c697357656233000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714668bbbac68a19cb7a02f7333e25590e4f568ae8a2ddc93a879e92e48fa3cf1666ac56e020c106d55": "0x040000000002000000000000000000000000000000000b436f696e73747564696f0000001a636f696e73747564696f4070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471466a0ec80cc3122c082bf733f44a840f0a5c1935a002d4e541d81298fad6d1da8124073485983860e": "0x040000000002000000000000000000000000000000000b53616d20456c616d696e0000164073616d656c616d696e3a6d61747269782e6f72671273616d40696d6275652e6e6574776f726b00000b4073616d656c616d696e0a73616d656c616d696e0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471466ab20bec2c8842b5c7a60ff74811eee72747ef1f1ae376eda2d3c8aab129f6c2cc76abaf59fb87c": "0x040000000002000000000000000000000000000000000a486563746f723c423e00001840686563746f7265737430363a6d61747269782e6f7267156862756c676172696e6940676d61696c2e636f6d00000d40686563746f726573743036000d486563746f7242233936313500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471466eaee046d4f02542c89cb8652eff8c73266de06baa3760534e7f37fecff971c028c2910efd6a947": "0x040100000002000000000000000000000000000000000a4c6567696f6a757665000000146c6567696f6a75766540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471466ee39ba0cc8fa55f4e0ed785c7c3f9f3efd38a15ac1498383070f1d7154c2ed5769652045609d75": "0x0403000000020000000000000000000000000000000013506f6c696d656320466f756e646174696f6e14506f6c696d656320466f756e646174696f6e2e000011696e666f40706f6c696d65632e6f726700001140706f6c696d656370726f746f636f6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471466f191e02c9f27205e13ed8ae098f9a3cedd297c6a019aff1cd744b56546dec7884c5ddac1090e20": "0x0000000000000000000000000000000000064167796c65000000136167796c654074616c69736d616e2e78797a00000c407374696c6c6167796c65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471467065dcde6578a8344eb828e27b43fd3e8cefd90f96aa467bd629dac03603b8355ffe434b865c50c": "0x0000000000000000000000000000000000134b415445207c7c204e4f544946494741544f001b68747470733a2f2f5757572e4e4f544946494741544f2e434f4d15406b6174656761746f3a6d61747269782e6f7267156e6f746966696761746f40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146724d23e0f578016d7e81893f6494f953ddc28c0565d5d82c7f57426b59f35a779cef72804b37086": "0x040000000002000000000000000000000000000000000d4f70656e5a4c20436f6d6d2e000000156f70656e7a6c406d616e74612e6e6574776f726b000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714675a4d3970c551d5acd87357aaf3bc58b5499d10d6c03092ef4fa89ac4a9c2fe7a80f6a6cdd7b060": "0x04010000000200000000000000000000000000000000094d494e5477617265174c65726e656e206d697420646572205a756b756e66741468747470733a2f2f6d696e74776172652e63680011696e666f406d696e74776172652e6368000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146773244509d0e7c8d46cd0ae6eeab8ef19bf289712c9f2ae4272c663bfe0786d7c10d4ada5210003": "0x040000000002000000000000000000000000000000000c4859504552535048455245000011406876616c3a6d61747269782e6f72672076616c696461746f72734068797065727370686572652e76656e747572657300000e4068797065727370686572655f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146773f65a3ce78ec9f673019128278eb8e7483c1c8b16506a89d140ad1dedaee28b6589306e98b28a": "0x040100000002000000000000000000000000000000000963656c616461726917436861726c65732d45646f75617264204c41444152491568747470733a2f2f63656c61646172692e636f6d0021636c2e6b79632e646f742e717439386c4073696d706c656c6f67696e2e636f6d00000e4063656c61646172695f646576000963656c616461726900", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471467f3ca507a532ccb2e1884c53071526483b14004e894415f02b55fc2e2aef8e1df8ccf7ce5bd5570": "0x00000000000000000000000000000000000970657079616b696e00087065702e777466000000000a4070657079616b696e0970657079616b696e0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714682174f20abcb7fdfa5e6f955d973efaca30897c4a3e4fbec88186ae72b8b331408804d73dfc275e": "0x04010000000200000000000000000000000000000000174245535456414c494441544f52207c205a55524943480e4265737476616c696461746f721a68747470733a2f2f6265737476616c696461746f722e636f6d14406d6f736f6e79693a6d61747269782e6f72671868656c6c6f406265737476616c696461746f722e636f6d000010406d6f736f6e79695f7a6f6c74616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146850837ac8d1be3236b8df7482c47933d4409ae2aea62f412be6f2af44173bb32d49e9abf3858445": "0x0400000000020000000000000000000000000000000007676c692e616c00001240676c69616c3a6d61747269782e6f726710706f6c6b61646f7440676c692e616c00000a40676c696f63797465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146857f90ade375e178c1860117351602843d192a9b4eb3b3641da38ab14c7974398761e7b7f3f3a14": "0x04030000000200000000000000000000000000000000054c75646f124c75646f76696320446f6d696e6775657300001e6c75646f7669632e646f6d696e67756573393640676d61696c2e636f6d0000084b726179743738084b7261797437380000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714687ec1d74f1c4464947c43d52db657492fed9368dc326e667d9c7bbf8df33c50974df47d2941ec42": "0x040300000002000000000000000000000000000000000e3078436f70706572736d69746811546962657269752043617a616e67697500000e6f776e61626c6540706d2e6d6500000e3078436f70706572736d697468000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714688f2c242370997680f9384b92e09042571a9e5cd43d9656d62acfeb0324ff44698bb2cfe422b36b": "0x00000000000000000000000000000000000e506f6c6b61646f74204c6976650e506f6c6b61646f74204c6976650000106940706f6c6b61646f742e6c69766500000b405a65616c6f745f3078000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714688fa9c16c2592d347051c516138c028eb5de9449fcd8fc9b657540f7592f822359f26b29092029a": "0x040300000002000000000000000000000000000000000a43727970646f756768000000117a6163684067697665706163742e696f0000114063727970646f756768646f74657468000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471468b2359cc10fae926295f0214abb5522cf3becf82504ef7c87036236e6b9afd263697a99bc5d5a30": "0x0400000000020000000000000000000000000000000014f09f91a8e2808df09f9a8073706163656d616e0000184073706163656d616e3131363a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714693a853cf5be87fcd81bf7c20643158bc6957827b344bde86cd260676dffa382afe24896634aa16b": "0x00000000000000000000000000000000000844656661756c7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714694ea541e2c22fadd6b8ec23dc68f20b5d315007d9c1a6706f9bd5c883319181129e76a89e978155": "0x040300000002000000000000000000000000000000000f446f7420506c617920416e6769650e416e67656c612044616c746f6e000020616e67656c61407369676e756d67726f77746861647669736f72732e636f6d00000a4064616c746f6e616e000d616e67656c6164616c746f6e00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471469576573c28ce38722e2d9f05aef8aa9965e0798d0789cb16f21ae1610b5695d20340a0e61bb4fad": "0x04030000000200000000000000000000000000000000125761674d65646961206d756c746973696700000017746861746d6564696177616740676d61696c2e636f6d00000e40746861744d65646961576167000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714697ead568e820ccfd2eb07f02043788e254d9e2df57be11566d241c56302b91199b4647947af3020": "0x040000000002000000000000000000000000000000000846415241444159001968747470733a2f2f666172616461796e6f6465732e636f6d1940666172616461796e6f6465733a6d61747269782e6f72671768656c6c6f40666172616461796e6f6465732e636f6d00000e40466172616461794e6f646573000d666172616461796e6f64657300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146988e63c3f5238944c1391e430e3bff9da68fb549a8c47881bd0fc7ed745031f57fa02495cbdbc7a": "0x040300000002000000000000000000000000000000000672616661630772616661656c00001672616661636162696c6c6140676d61696c2e636f6d00000f4072616661656c636162696c6c61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146996157961e3bb9397722aac08a16eefd9a6d17b5d69874b7d84ae30eb523c09054f677e976cc8c5": "0x000000000000000000000000000000000014496e746572204643207c20506f6c6b61646f7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146997b214126ecad17e45841538166aa927c3e1c2bdaa3c0d1aa1938b1ea21710eb3a2c9e6431b93b": "0x0403000000020000000000000000000000000000000017526f636b585f506f6c6b61646f745f4469616d6f6e6406526f636b581668747470733a2f2f7777772e726f636b782e636f6d0012737570706f727440726f636b782e636f6d00001040726f636b785f6f6666696369616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471469a5e82dfb57babbb40b08703caaacd889ed6521b6cd76db8c017652dda03f7239484e10e49ebd1d": "0x040000000002000000000000000000000000000000001d4a4b5242207c20546f7765722042726964676520f09f87acf09f87a7000000196a6f656c406a6b7262696e766573746d656e74732e636f6d00000a404a6f656c4a4b5242000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471469c81f40482c7fe2ac561538f2f61fdee2e15d44a6df7b310fcb0428da93ea0d5373a4ba14408d52": "0x040100000005000000000000000000000000000000000f4e696e6f202d204170696c6c6f6e001368747470733a2f2f6170696c6c6f6e2e696f00106e696e6f406170696c6c6f6e2e696f00000940676f7375313238000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471469d27efc78020c0bb2e2af76c0926af438cb5c3a2c0f534da4c25ab3bed006df4eca319ad3bf8874": "0x00000000000000000000000000000000000d576562332d446f7446616e7300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471469e34466005457c2c48d1c4fc44dbbc10b86e40db24f95f8924efba2b31eecc6a7c32bf2b8a4481a": "0x04000000000200000000000000000000000000000000064c65776973145368696e672059696e204c65776973204c41550000156c657769736c737940686f746d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146a205861e5fd90b2ae3a9e0c24a161ca065e6af741e35dcfa1cb3c0c00d820f9445e1dcd1b36ef09": "0x00000000000000000000000000000000000450345500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146a2d4f462a713142b45f7d4b6bd58cb550270c1ba3d9d97b6766787966b0b5c52c79b4d3b51d9a43": "0x0403000000020000000000000000000000000000000017f09fa681204c454f5354414b452e434f4d20f09fa6810000001974656368737570706f7274406c656f7374616b652e636f6d00000c6c656f7374616b65636f6d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146a4f96f9cd70bd9472e7ccd983b92c5ae60481a636eab0fe42269c9f43c302a74f9dc65e0fbe202b": "0x04000000000200000000000000000000000000000000175375627371756964204c616273204f6666696369616c135375627371756964204c61627320476d62481568747470733a2f2f73756273717569642e696f2f0013736f6369616c4073756273717569642e696f00000a407375627371756964000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146a6c65861cbfd4f690ebd94a58a07211dcf05c4164ebab8c3abaca45f16f793fbe34072ae7b9ba08": "0x040000000002000000000000000000000000000000000a4d61726b205279616e0a4d61726b205279616e0017406d61726b2e656d6265723a6d61747269782e6f726700000011406d61726b5f656d6265725f7279616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146a89e104b2a237cd50192a1801fb73df626b690ff6e92a6f502ab5289437d799927cf486cef84319": "0x040300000002000000000000000000000000000000000b4245454659204d454d450000001377656e626565667940676d61696c2e636f6d00000f4062656566796d656d65636f696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146aa8b880a0c4b9171275627409e1eca0f9aa7e08fe1f1a26988cb462283067fdd30c13fcd7981424": "0x0000000000000000000000000000000000134d44726f75676874202d205374616b696e67000000166d64726f75676874406d64726f756768742e64726f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146b14621db5a14421ee995f41ea8d34445ee36e73b1f44437ea7f758bbb1f1e89c9894066b013ca3d": "0x00000000000000000000000000000000000761736c696e6b00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146b1f8277766d740bc622132c1da0a0c45d91cb970fb5332e5c592a45f6c7e223767fd8c7fb849c58": "0x04030000000200000000000000000000000000000000125348412d74776f666966747963687269730b436872697320436f63611868747470733a2f2f7777772e646f7469736465642e696f001a6368726973746f70686572636f636140676d61696c2e636f6d00000b406263315f6368726973000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146b9baafd26f9d78929f107a6abccb303dcdd5d05569389f87a411d192e8463829a9c84a95a5b3ed1": "0x040300000002000000000000000000000000000000000c4d69737465725f436f6c650000001e6d6973746572636f6f6f6f6f6f6f6f6f6f6c6540676d61696c2e636f6d00000c4031393238333734367a71000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146ba30fff03a1847c98acccca7675aa832373fa4dfee14c0c237c1e547ae91981b63b4a0a9810d270": "0x040300000002000000000000000000000000000000000b5265616c566973696f6e175265616c20566973696f6e2047726f75702053455a431c68747470733a2f2f7777772e7265616c766973696f6e2e636f6d2f00186163636f756e7473407265616c766973696f6e2e636f6d00000c405265616c566973696f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146bf602d4ccb0664d3c235e80e35082b668682531b9b062fda39a46edb94f884d9122d86885fd5f1b": "0x00000000000000000000000000000000000000000000000000097270686d656965720000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146c4d2026575763437cdc1a6a5a7f23437b6528edcdf553d0685f940a4e6e85579727ef3dc574563a": "0x04000000000200000000000000000000000000000000114365727448756d204d61785374616b65000018406365727468756d2d6a696d3a6d61747269782e6f726715706f6c6b61646f74406365727468756d2e636f6d000009404365727448756d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146c5088428455187bcc95d7d061fc6a655b795794b9b1614d0a240e13e81de9dce3f2b184f653da7c": "0x04000000000200000000000000000000000000000000097279616e686967730000000000000a407279616e68696773000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146c617259271d955c48b2a90dced600b59871b53ba285b33b16ad830ea6877ffea3ea7469a996b054": "0x04000000000200000000000000000000000000000000074655545552450000124031667574753a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146c7206770c39d9295a5f7eb7050fb96d8d7895d9afce428a064ce66e3b094805bcad9a8e68cb9934": "0x0401000000020000000000000000000000000000000013426966726f737420466f756e646174696f6e13424946524f535420464f554e444154494f4e1868747470733a2f2f626966726f73742e66696e616e6365001668656c6c6f40626966726f73742e66696e616e636500001040426966726f737446696e616e6365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146cbe0677de752662eebc7887720ec1ce8b759629cb425df5f15011f0d455bfe1a22ad4cfb36d1a3b": "0x00000000000000000000000000000000001142494e414e43455f5354414b455f31321142494e414e43455f5354414b455f3132000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146d1925fb53c401a7aa926dd1d0ac5fd7a08808a687e8ab73137ea8355c32c397b4b8b1c2db7cd752": "0x00000000000000000000000000000000000a4a61736f6e20547365064a61736f6e00001576616c76656368696e6140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146d2ec78ce0df90db282babe83a600ea252085b952e5df448a09e3b859d510d21900a77549f39b66c": "0x000000000000000000000000000000000008656b697463686f00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146d3350ee036daad1f21779c6e9e861c7af89d8edf7077a73c54d16ee419d8080e3539937bf565e4f": "0x0000000000000000000000000000000000114d79436f696e7461696e65722e636f6d001968747470733a2f2f6d79636f696e7461696e65722e636f6d001761646d696e406d79636f696e7461696e65722e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146d34ca1ecaaeb6174471abb5438fa95f8c85b8d5a417df0a6a38b4372874f27f30f20645f3263830": "0x040000000002000000000000000000000000000000000b5975647573204c6162730000154064757979756475733a6d61747269782e6f72671579756475732e6c61627340676d61696c2e636f6d00000c4079756475735f6c616273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146d5ba05b78a2e080daa4917008c6339bc42960ff491ffb03c3a6ddfc2b2b045d1c24112383398252": "0x000000000000000000000000000000000007686f646c337200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146dc100623401ea343e7064033033db59b89b05afc0408800e1c6a900e5ef4465f6493d50e5070031": "0x00000000000000000000000000000000000f4b65697461204d6f726979616d6100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146dc2070d9c7fd08ed88e71e550f7c318065fe4ce9c7d58430af17fa534f87d27195dd93cce667719": "0x00000000000000000000000000000000000a4669676d656e742034001368747470733a2f2f6669676d656e742e696f000100000b4669676d656e745f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146ddcb34ad887603a8283bf2db7f36e834b16ce3b301f978609b2eefd855f01f6c7f16209c0a9147e": "0x00000000000000000000000000000000000d4d6973636861277320446f7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146dfdc0beae27efb8ee5b5133284a68de459de2373129497bf4bad758619ebc6b0b6bc77fb1ee2862": "0x0000000000000000000000000000000000085052585920434f001368747470733a2f2f70727879636f2e636f6d0012636f6e6e6f724070727879636f2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146e0bda57536f0f7afe8ed746b2f0fafda336e27346a75f2f03db0f73a3e73e1ca6deb3676e14d139": "0x00000000000000000000000000000000000a4d61747453616e746f124d61746961732053616e74616f6c61796100001b6d617469617373616e74616f6c61796140676d61696c2e636f6d00000c406d61747473616e746f5f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146e282abb12705aa32837c15322c7d358dc07ee65dd3ab42a3d70b6c4577a015b2479100bbeacda51": "0x0000000000000000000000000000000000064368696c6c000000166d6f6c6c79646f7430353640676d61696c2e636f6d0000094055534348696c6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146e4e14c3138d789d92f2999a6efaa86285a96c5d0ba78f11ec72b33c6973ec6fd5afb7b8b0e4fb45": "0x04010000000200000000000000000000000000000000085a656e6c696e6b115a656e6c696e6b2050726f746f636f6c1468747470733a2f2f7a656e6c696e6b2e70726f13406c656f67756f3a6d61747269782e6f7267106c656f407a656e6c696e6b2e70726f00000c405a656e6c696e6b50726f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146e586fa9f032427be49ad2f6a7346dca526a39aeea90aa0231717c77eaf257b0733faed3e1953f19": "0x040100000002000000000000000000000000000000000e414a554e41204e4554574f524b1c426c6f476120546563682041472028537769747a65726c616e64291268747470733a2f2f616a756e612e696f2f1540726f786f6e746f783a6d61747269782e6f72670f68656c6c6f40616a756e612e696f00000e40416a756e614e6574776f726b000d6461726b667269656e64373700", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146ec642500076ba25a6c5f0595d6ed85d6260bc682d4a68a4b4e605d43c67d56f2765d19698549772": "0x040000000002000000000000000000000000000000000b5374616b656c792e696f0000124069696363313a6d61747269782e6f72671161646d696e407374616b656c792e696f00000c405374616b656c795f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146f4309182c6e307ed4e8bfb1c924dd64e33ecfbb35d90061bb83b2dde667e58588780068f9fc1471": "0x00000000000000000000000000000000000b506f6c6b616469726b730f43687269737469616e204469726b00001843687269737469616e2e6469726b406d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146f47cd4490d5c355aeb3b1fa02364f2a474a149c1ce5b80c177857c0cde909e140d8d88036dde539": "0x00000000000000000000000000000000000844414f4e676f6b001868747470733a2f2f646f7261666163746f72792e6f72670016737465766540646f7261666163746f72792e6f72670000094044414f4e676f6b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146f73628ca952be18dad0bc6a0aadf06c56416e83bf75e865d41ccb5ffd74eabf9e81d47574b43049": "0x040300000002000000000000000000000000000000000b43756c74757265446f740b43756c74757265446f740000146a7562616b4063756c74757265646f742e696f00000d4063756c74757265646f7431000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146f74c55a2ace0819606aa47c4cbf48834774de4e02bed163e76c54e50415f5a0cb103e6a47511c0b": "0x0000000000000000000000000000000000054c616e6100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146f75ffac66bf6b9f1028da734244eb60a38d63f97c96f89f4f9630bb98376631a4d24c2baf1ccf6f": "0x04000000000200000000000000000000000000000000064d61727461114d61727461204d6f72616e64757a7a6f1968747470733a2f2f776562332e666f756e646174696f6e2f17406d617274613a776562332e666f756e646174696f6e166d6172746140776562332e666f756e646174696f6e000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146f83ccad3981de4b7e569787b1b323854ac9a8c40914d400b6fc23a2fedd24321f814ce7db7f6563": "0x040000000002000000000000000000000000000000001452656b7420537472656574204361706974616c0000174072656b747374726565743a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047146f9c17a3ac4a6c898a3b9446cadec292c5064f0572e8752007bd8db06cc58feccaf23dea69da58cb": "0x04000000000200000000000000000000000000000000076765726d616e001468747470733a2f2f6e696b6f6c6973682e696e000000000c40736b796d616e5f6f6e65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471470391fe33aca6dcaf6a7abb3dd0e36f9f77e31e10462ea14b10c909f37cc0db83ee8349dc531d840": "0x00000000000000000000000000000000000b4461636164652e6f726711556e697420552b3234363720476d62481a68747470733a2f2f756e6974382e617065756e69742e636f6d0012753234363740617065756e69742e636f6d00000b406461636164654f72670009656d696c2e61706500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147049f2f99a873c94c00f86f5f5421e98f7ba345ccd996c53412f39308ea854053fe650ca7bf44f75": "0x00000000000000000000000000000000001242726967687420496e76656e74696f6e73001d68747470733a2f2f627269676874696e76656e74696f6e732e706c2f0000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714704dea62a3fb9e66a471c55caca4be7b4e60c6e94b20f9028883f8c64287d4454130c657383c3442": "0x0400000000020000000000000000000000000000000018f09fa78a2049636562657267204e6f64657320f09fa78a00001940696365626572676e6f6465733a6d61747269782e6f726716696e666f40696365626572676e6f6465732e636f6d00000e40496365626572674e6f646573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471470d495db70840cac599742dc389cf9dbb6a67d8cbd22170dabce386c06df1ab4ad8c8285f351f2af": "0x040100000002000000000000000000000000000000000b476c617a657363617065001768747470733a2f2f676c617a6573636170652e636f6d001568656c6c6f40676c617a6573636170652e636f6d00000d40476c617a6573636170655f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471470ea89be94fe51a442cc5215992b80128fbe79ab6b9e9ef599e18a9fd3aabb62973d80e66cb40d0a": "0x040000000002000000000000000000000000000000000667626163690000124067626163693a6d61747269782e6f72671d67696c626572742e73746f7279637261667440676d61696c2e636f6d00000840676261636958000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714710936ab2f44516c185002001ae350c3815b9414a1113f6a6eaefa51a52ad3bfa09183c197c23f6b": "0x00000000000000000000000000000000000c6861727279646f3339373912446f205068616d205472756e67204861750000166861727279646f3339373940676d61696c2e636f6d0000104068617272796265617574795f6f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714712aaca2b02746086883c9953ae0dea90b0b1db721a34e5ab73f948f428c403ca23bfca0f514594d": "0x040300000002000000000000000000000000000000001cf09fa6bef09fa4962049766f72794e6f646520f09fa496f09fa6be0000001769766f72792e626c616e63614070726f746f6e2e6d6500000e4049426c616e63613439353736000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471471352c2eb5ded5f286484f63c9e0ae1f460dec3b53307478f5ce3ffab22a1334d34a52da7527ec64": "0x04000000000200000000000000000000000000000000084d656c616e67650000001a6d656c616e67652e7374616b696e6740676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714713faaeb6b134411e9d6e3adda6c3383cb1f5e92d6908f7d792fc1b4a80958341e05f53fee624836": "0x0000000000000000000000000000000000154272617a696c2042697a44657620426f756e747900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471471570c7baf07c52daca9b971305af399221a92e1bf8c58307fb1057f0c8f6c3d226cf7402897b837": "0x00000000000000000000000000000000001048656c656e61202d205075626c69630748656c656e6100001668656c656e616a77616e67407961686f6f2e636f6d00000d4068656c656e616a77616e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147164fea0aaf340daa6075a9968b174ce67edaab5fbdb011bd625e2b4728f953e6444386a36b8722a": "0x040100000002000000000000000000000000000000000b54504b4c2e45617274680c546f6b656e506f636b65741d68747470733a2f2f7777772e746f6b656e706f636b65742e70726f2f1c40746f6b656e706f636b65742e70726f3a6d61747269782e6f726713626440746f6b656e706f636b65742e70726f00001040546f6b656e506f636b65745f5450000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471471807145847ce7520a9e6fb48b9423ae5939f07fe1d7f41a6c1ebe3d2135e94af94b7fd717a47418": "0x04000000000200000000000000000000000000000000094d6161726d617061000015406d6161726d6170613a6d61747269782e6f7267106d6172696f40626f796b6f742e636c00000a406d6161726d617061000e4d6161726d617061233830343800", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147180d79828e0ec897e6a7945c3108fbc35ee60d6f338f4472d4658249d96493086994e02e7dd3a11": "0x04010000000200000000000000000000000000000000055855414e055855414e0013407875616e39333a6d61747269782e6f72671b79616e676a696e677875616e6d61696c40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147181755808f2aabbc83e489ed94fbe1d1b6a71e8393d59583910463dc95bfd00a93ce47cb1465928": "0x0403000000020000000000000000000000000000000014f09f8fb5efb88f20464c4f5745525354414b45001768747470733a2f2f666c6f7765727374616b652e696f1840666c6f7765727374616b653a6d61747269782e6f72671b666c6f7765727374616b654070726f746f6e6d61696c2e636f6d00000f404265466c6f7765725374616b65000c666c6f7765727374616b6500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471471bd36a7be01ed70461b8eab2552590c5860de5271e7dc81fbf3fde9c772509fc4fdf67fcdedef61": "0x040000000002000000000000000000000000000000000c747275737465646e6f6465001768747470733a2f2f747275737465646e6f64652e696f0014696e666f40747275737465646e6f64652e696f00000d40547275737465646e6f6465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471471c3ea80d7b0dfb82a60c7682b7a16f3e6502873a523a3c55703f5df93d297fca9c5fd8779d05a28": "0x0000000000000000000000000000000000086a6572657a5f5100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471471c705b1381272e69efc992685aa35aba0942285fefffe37246b1fc16ee417ce28d1be8ca4e20030": "0x000000000000000000000000000000000006737461736800000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714720cd28dcf99c07dac133ebfde441672c055b79ca9a6059850984eead1ae036f48ca1230e7f0556f": "0x04000000000200000000000000000000000000000000094d696c65f09f8c8d000016406d61746865726365673a6d61747269782e6f7267176d6865726365674070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471472111003b96bc266a24fde6343e2bf0aefa296afcedea6f16d37c5e1c0d8b6511e7055cf7282b60c": "0x040000000002000000000000000000000000000000001053756c74616e4f665374616b696e67002168747470733a2f2f7777772e73756c74616e6f667374616b696e672e636f6d2f204073756c74616e6f667374616b696e672e636f6d3a6d61747269782e6f72671f73756c74616e6f667374616b696e674070726f746f6e6d61696c2e636f6d0000114053756c74616e4f665374616b696e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471472491867cdbdb72a5071733a41703f6cf3fe43daa2814861b512205451a2f33a60a8b3d739eab20e": "0x04000000000200000000000000000000000000000000084c554935444f540000144077696c64646f743a6d61747269782e6f7267000000084031784c554935001a4c5549352f57696c64446f744170706561726564233230303900", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471472695126ff943f9e38d442452b078203a37f178906e67650b19128ad687051f1aaca4c0c428b3c04": "0x0000000000000000000000000000000000086d75686172656d00000000000000086d75686172656d0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714726da8e64fe5cc017269960f17b619f90d534da2bfb1506b14ee66307459a066289218d2bbcd2414": "0x040000000002000000000000000000000000000000000a4554205075626c6963000000000000094065743930323636000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714728227c4dd30620ff826273e80a776cf79c96ef33dbde5ae91a843fc3f36401c6f878447f6aa6315": "0x0000000000000000000000000000000000104b4a4320383320446563203230323100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471472f593a2e278eb75522e16fcd83f6d6e08fe2b44d56b5d0bdaf33527b49e8832928d003d8097ca63": "0x04010000000200000000000000000000000000000000094e4f5354524f4d4f11446f6d696e69717565204ac3a4676769000014696e666f4073756e646f776e65722e6165726f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714731ba4e11a7d5feb8c77062e797e4a1bd08a3d9ca4feaa8917d02315dd3ecee23f3649246e7ea84d": "0x04010000000200000000000000000000000000000000054449434f054449434f1068747470733a2f2f6469636f2e696f000b6869406469636f2e696f00000e404449434f3033323739373034000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147331c534ba659f84f47f6c77a0720b820074d7f48abb22ef868ba2ec24159bc850e1423dfa1bd43a": "0x04010000000200000000000000000000000000000000094b534d4348414f53000000136b736d6368616f7340676d61696c2e636f6d00000a406b736d6368616f73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471473364b7f8cfc00b812c039004da5e1e846aae808277098c719cef1f4985aed00161a42ac4f0e002f": "0x040000000002000000000000000000000000000000000874696d777532300000144074696d777532303a6d61747269782e6f7267000000000874696d777532300000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714735f4b854a56e790d063d0325df758d4e35a9ba4c07526410fbba664c0b61fe163c4405f5b38dc66": "0x040000000002000000000000000000000000000000001950617269747920546563686e6f6c6f6769657320476d62481950617269747920546563686e6f6c6f6769657320476d62481768747470733a2f2f7777772e7061726974792e696f2f001470726f706f73616c73407061726974792e696f00000c4050617269747954656368000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471473b93bc1a743680630ddff6cf0557f5ec5929f4ba1ae54279c028f94251e4f4cce45d72c5a9c58ca": "0x00000000000000000000000000000000000c526567656e63792d3030371757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471473c1f960c73bd4b5a40ccb73fa01d3dff2ed46e235fb2fe8eedac1b4e3dea68c76ac2deecf942022": "0x04030000000200000000000000000000000000000000086d5f73686565700b48616f79616e67204c690000166974616c792e313834323940676d61696c2e636f6d00000d404d61676963533836363433000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471473c700cacfaf7b1e7cc47cd222c6c30c2856e545ff74e6b31ed1725334b0e31cd43a52730e9ad66e": "0x0403000000020000000000000000000000000000000011546865577269676874537563636573730e4173686c6579205772696768741e68747470733a2f2f746865777269676874737563636573732e636f6d2f001a696e666f40746865777269676874737563636573732e636f6d000010407772696768745f73756363657373001674686577726967687473756363657373233531383700", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471473fe9dfb9a1dc2c1c41be0c2c5e5ad66d0d3a1ed02ca8ccf056d3b92a84df2429dc1e957eeb1152a": "0x040000000002000000000000000000000000000000000830786e3030627a0000144030786e3030627a3a6d61747269782e6f72670e30786e3030627a40706d2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714741edd192a56853bd2413cac426040b67e93948afbd069b37ca700049a2f170d05b16418ff8da032": "0x0400000000020000000000000000000000000000000014536d6f6c646f7420646576656c6f706d656e7400001540746f6d616b6131373a6d61747269782e6f72671d7069657272652e6b7269656765723137303840676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471474782af8e47bfcfc0a4a3233cb4870d9f20b5c55e77f839ce96a26f0fd773d019179197576644765": "0x040000000002000000000000000000000000000000000b45584e4553532e434f4d0000001576616c696461746f724065786e6573732e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714748c854ac41713f81ae3a39de384cc366c0c0693e82a9b38397a35b201ffc33e383469df052e4227": "0x040000000002000000000000000000000000000000000c556e6f205374616b696e6700001740756e6f7374616b696e673a6d61747269782e6f7267186f70657261746f7240756e6f7374616b696e672e636f6d00000c40556e6f5374616b696e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714748e26e4416abae0ccd87fa65729f7bdaa8305581a7a499aa24c118e83f5714152c0e22617c6fc63": "0x0400000000020000000000000000000000000000000005416c6578000000000000000661746865690000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147499a373d4b2f70f8419a4ac409fa4e7bec032c696c526fac10ff9114d928df56ca0bf9e9ae29149": "0x04000000000200000000000000000000000000000000104d41415254454e207c204153544152000013406669657865723a6d61747269782e6f7267166d61617274656e4061737461722e6e6574776f726b00000b4068656e736b656e736d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471474b0c239e26aca84a6c70805946002d87f9a6a2c5ec0900cd31ae4e27fd375e71dd6d75c0c264b68": "0x040300000002000000000000000000000000000000000864656669677579084775737461766f00001461737461726465666940676d61696c2e636f6d00000b40646566696775793232000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471474c3ae49c1416014a0afbe96d21f06c21f01a6e501750fe36c0e4697b8431fab6f94a558541b6446": "0x0403000000020000000000000000000000000000000005323037350000000e6d6172636f407a65726f2e696f00000a6d6172636f6261686e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471474c5981ba60102c7e82d3b7fbff7f5ff1010b279435012e0ce3b68dc387eb2827fc2e9d5001ada7c": "0x00000000000000000000000000000000001062696e616e63655f7374616b655f321062696e616e63655f7374616b655f32000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471474d6aadd494e266842a510b82f5075233945c7d705a187bdd4d36745a3ca90f36b860f8b3247a27d": "0x04030000000200000000000000000000000000000000084b6c617374657200000011696e666f40706f6c79636f64652e736800000b6b6c61737465725f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471474f1e1edd85aece4106a53bc7d39e4116f4bac46cac7c172269fe76e7757be1094c32d1243ab920a": "0x0403000000020000000000000000000000000000000009436872616c7439380000001b636872616c742e646576656c6f70657240676d61696c2e636f6d00000a40436872616c743938000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471474fcea92fd9411873eaf71e6279d9b9666930f468b7204050c448bfe4593762f44e5f04ffc00f968": "0x04030000000200000000000000000000000000000000184d61727320456e7465727461696e6d656e74204c4c432e0c53696b646572205161697300001d6d617273656e7465727461696e6d656e747840676d61696c2e636f6d00000c4053696b64657251616973000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714750f6a824e75e9801a1bae27765f9be012e6d1a2307afac2b32c82dde3027c9d2b4978029f0c5171": "0x000000000000000000000000000000000007417679446f741541726368616e612056616964656573776172616e00001761726368616e61763139323940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714753931c0b2ecf25f803166343a21e65218ead92135f73df970ed63e5aa18b329d9542b47ba9fa219": "0x040000000002000000000000000000000000000000000747757374617600000013677573746176406477656c6c69722e636f6d00000c406775737461766e697065000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714754cb56cf6c9489ed016feaa68e25739dbc4035faf2577d17ec9d7ec2125a99a6770b5be093f6e32": "0x0400000000020000000000000000000000000000000009446f74536b756c6c00001540646f74736b756c6c3a6d61747269782e6f72671268656c7040646f74736b756c6c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714754cdcbcd79de061288197fe1d9f6b12ee7f9656663791366fd5ea4ec01999d7a7f8b84e22a0591b": "0x00000000000000000000000000000000001062696e616e63655f7374616b655f311062696e616e63655f7374616b655f31000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714754f0164b9ddbc7048a94c9ca2c21cb5f258f5873bbb5beceb4c64df4662d43c3c289b4d67a56f06": "0x00000000000000000000000000000000000531304b4100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714757a8da264c88695a6b1c75426a3170fdd0b13bd2ba35e0f80cf2d1010973a8cea518ea78995bb54": "0x00000000000000000000000000000000000c444f54203120284578742900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471475846ad69bb139bac2828538511e926bcfe09150d03638cff3f4243d1d74b9fe0cc150567802b45e": "0x040100000002000000000000000000000000000000000a4254434d494e455253084c7563696c6c611668747470733a2f2f6274636d696e6572732e66722f001a6274636d696e6572735f70617269734070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471475867a568daa1785baedf2e50d3732045c8b24d42ad3167b994e318707f4ae4cabb7ef212f5e3860": "0x00000000000000000000000000000000000f547269706c4569676874f09f8dba0e44656e697320506973617265761e68747470733a2f2f6769746875622e636f6d2f747269706c65696768741a4064656e69735f703a6d61747269782e7061726974792e696f1e7069736172657664656e2b706f6c6b61646f7440676d61696c2e636f6d00001e68747470733a2f2f747769747465722e636f6d2f44656e69735f507374000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471475aaed01d33aff0f6a276c8c59606dbc8515f3bbde2bbf30956ae793689cf5c003d7e595d1ecee64": "0x040100000002000000000000000000000000000000000c506f6c6b6177616c6c65740c506f6c6b6177616c6c65741768747470733a2f2f706f6c6b6177616c6c65742e696f184030787468726565626f64793a6d61747269782e6f72671568656c6c6f40706f6c6b6177616c6c65742e696f00000d40706f6c6b6177616c6c6574000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471475bc7afdeb88fc9d0aa041bf62d5b52adee07e7cbc0687ace86e77308a982c07db871ef2dab63901": "0x00000000000000000000000000000000001042494e414e43455f5354414b455f371042494e414e43455f5354414b455f37000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471475ccfb98243b897452cadfbd70405e7a8fe22235a0d0eeaf7cffc9644b3a3ccd8b5177cbb56c3c57": "0x0400000000020000000000000000000000000000000009616e64657273656e00001940616e64657273656e303730373a6d61747269782e6f726718616e647265693037303730313140676d61696c2e636f6d00001040416e6472656930333334333837380010416e6472656930373037233131353900", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471475d112c2582d6bc3922bc16cff1acfc4a08cb5bfcebadaa9eb182cd47a51b8b047a0202ae9624a1c": "0x04000000000300000000000000000000000000000000045733461e576562203320546563686e6f6c6f6769657320466f756e646174696f6e1868747470733a2f2f776562332e666f756e646174696f6e0015696e666f40776562332e666f756e646174696f6e0000104077656233666f756e646174696f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471475fe22e7da8bab122ee58d589be14b8d4fdf4dea986983733c837b6af4b99a8a550da9fbb1aefc77": "0x0000000000000000000000000000000000054e31626f00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147601c8c0075205c282f81b71f17b13f87e58ca1444fc00604907342aed7dbfc828872a8db645032a": "0x0000000000000000000000000000000000063231446f7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714760aecf664bf053f863ba022a0315ac70e6881db2f4906ca51d2864c43800cc5601acddc05b0d903": "0x0400000000020000000000000000000000000000000005426c617316426c617320526f6472696775657a204972697a617200000000000b40626c6173726f647269000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147629d0fa8c9ac2292c81fc1faa024d3d1a727d953187860b45bcb185d046631ab98261eb6f2b8d54": "0x00000000000000000000000000000000000e41626c652057616e646572657200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147642074451d24466b2e07be4d6d82f546ec91d6009ee215bb736be5b4362e66e7b466ec72d47624f": "0x040100000002000000000000000000000000000000000a534158454d42455247001768747470733a2f2f736178656d626572672e636f6d2f1840735f736178656d626572673a6d61747269782e6f72671468656c6c6f40736178656d626572672e636f6d00000b40736178656d62657267000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471476593933a2785b22f0cd49d0588323d08bdcb1c1e4b71539b8998471df15f41584f98cb9f5008c23": "0x0000000000000000000000000000000000074a617273656e074a617273656e0000126a617273656e406a617273656e2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714768c2c58b90681dfce28672fa749577e198bfe03ec3972b22b47dbbfa9996dca0be76ff05851012c": "0x04030000000200000000000000000000000000000000104d69786f5f5374656c6c6153776170000000136d6562407374656c6c61737761702e636f6d00000b5374656c6c6153776170000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147691ee6d858f48473eb7ee31abeb77dc61aac4a18390bfbe97131da7426583e9df30b3b4e5f5ed71": "0x04000000000200000000000000000000000000000000086d6172316465760000000f68656c6c6f406d6172312e64657600000000086d61723164657600", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471476c0068d9d7b7dea7081fe1fe013c00fbe52543ccd437d45fc26b0d49b7df5c117de02c4193c7d12": "0x040000000002000000000000000000000000000000000b6672616e6b7977696c64000017406672616e6b7977696c643a6d61747269782e6f726700000000000b6672616e6b7977696c6400", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471476d10d82508ddae096e24e9b5ab82dc275b82318a52cebed1cae3e25be096d5f288229b256359e43": "0x040000000002000000000000000000000000000000000b537769737320506f6f6c0000000000000c4073776973735f706f6f6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471476f5243ff4ce86095604806a738c7e4d516625d47db7a4ac3197142b84203c1495b6689b066d9d44": "0x0000000000000000000000000000000000094772697a7a3337350000000000000a404772697a7a333735000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147724a5ea734f20f7bad6db470dcfd5ee86f6e3abc0e5351fa654cbb5528f4daf2f0d393f4a46b421": "0x00000000000000000000000000000000000749555244414f0749555244414f0000176975722e70726f4070726f746f6e6d61696c2e636f6d0000084049555244414f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471477300f589b9251ff6c695c4e546c6889ca591b582eee8b49ba68d8485a3732e08d232b2f28f2342e": "0x040100000002000000000000000000000000000000000852796162696e61001468747470733a2f2f72796162696e612e696f2f144072796162696e613a6d61747269782e6f726710696e666f4072796162696e612e696f00000b4072796162696e61696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471477364a8cd584fbff7ed6682224da03023f194d53bbd806050575d0794e73f6cf8b99be9ae1bbb230": "0x0403000000020000000000000000000000000000000007696c617269610e496c6172696120456e616368651b68747470733a2f2f6769746875622e636f6d2f696c61726961650017656e61636865696c6172696140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714777b3c12eea4a22494b1ee887c35565f8d72777b3e5d8a7ccfda76b370dcdaa105df7bd6e49b8841": "0x0403000000020000000000000000000000000000000007536572686969115365726869692050726f736b7572696e00001a706f6c736b612e7374726f6e6b406f75746c6f6f6b2e636f6d00000b6d6f6d735f6d696e6572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714779e501e142783af7caa3fa184254cc0cd46a0a4fc1be30b87afc70d2b290b9545dd4f7af46dd70e": "0x04030000000200000000000000000000000000000000087765623364657608574542334445560000176163636f756e7473407733642e636f6d6d756e697479000009776562336465765f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471477a0350acc7abcb9f66311b1720ee0da02af2b95c703f1db4e2db091296b598b466ae15c9ea29344": "0x0401000000020000000000000000000000000000000009536e6f776361706500001540736e6f77636170653a6d61747269782e6f726716736e6f77636170652e696f40676d61696c2e636f6d00000c40736e6f7763617065696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471477a8f518e90d0d2cbe1c8831d018fed582688b5e75be3c9983ac1761fb24a916451c6999c0c66f5e": "0x000000000000000000000000000000000009646f746875622d3209646f746875622d32000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471477c114f494effd6f109914286fe2fa65a85cfcdbcd4562010745b1fa12e9f02b62795b740000e541": "0x04000000000200000000000000000000000000000000057074716100000011676f6431373540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714780625038e82798d9410c1acc9de3fc8a2c36e5b1116f435b745ce96335ce5d2e133dc3d27268152": "0x0400000000020000000000000000000000000000000011456173794120476f7665726e616e63650000000f68656c6c6f4065617379612e696f00000b4065617379615f617070000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714783efdad4741c0a9a1e99029c8d8deca2c762dc353047586d9a46e523bdf3b4ddbd9fed1ceef71fd": "0x0401000000020000000000000000000000000000000011475241424249545920e29ca8f09f9087001568747470733a2f2f67726162626974792e6e6574154067726162626974793a6d61747269782e6f72671368656c6c6f4067726162626974792e6e657400000d4067726162626974796e6574000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147854d7d5ed8a10ef807561257b836e4a4367cd5fb433621781f882de92a97eb03977e092f64af57c": "0x040100000002000000000000000000000000000000000c50444d204361706974616c1c506f6c6b61646f74204d6178696d616c697374204361706974616c00001370646d6361706974616c403136332e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471478a2b18d6db0a15e0bb5e12d2f6ada25c3b64171730f1c9efcf76d3bbccff01abd9f92a9aaac10a7": "0x0400000000020000000000000000000000000000000004474d5a00000012676d7a40636f6465616666656e2e6f726700000940676d7a61626f73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471478a4b4280c25b8bdee540d3a73580cf5e0ae2d80ac9d98dc27847f5518d62b652a6561d46c16b553": "0x040100000002000000000000000000000000000000000af09f838f205368616400001340736861646f763a6d61747269782e6f726715736861646f767365726740676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471478acd82014dac4867a24e507ab6be3b521af33bebbece8f57f9b8c20ae23a87986ec0518c1ac1d2d": "0x04030000000200000000000000000000000000000000104869726f50726f7461676f6e697374001868747470733a2f2f6c696e6b74722e65652f74796d61741440616564696769783a6d61747269782e6f72670e74796d617440707269732e696d00000840636861307465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471478caef8fe8bb73f2a4d813e676d81d97479f9f15572d9eadcaf4503b654c0f6e7baeb49e84510e69": "0x0400000000020000000000000000000000000000000011566972657320696e204e756d657269730000194076697265736e756d657269733a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471478db440095822ae679ad7966c6eafe858c375d0ec2d0717c59c973ccd7c7c82131d48a76dbc22d85": "0x0000000000000000000000000000000000076775706e696b0d4e696b68696c2047757074610012406775706e696b3a7061726974792e696f00000000076775706e696b0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471478e05e6ced85f9d7180364775e32443f308fc34a8917ec40227726c736c2817ae56c5b982a17165a": "0x0000000000000000000000000000000000086d6364616e393310416e64726577204d6344616e69656c0000126d6463616e393340676d61696c2e636f6d000009406d6364616e3933000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471478fa03421f9ba61e726ef17b500f436cf596935acac782c71a423d35a959f3e6831f13c34db9d71d": "0x040300000002000000000000000000000000000000000a58594c4f44524f4e450000001978796c6f64726f6e654070726f746f6e6d61696c2e636f6d00000a58796c6f44726f6e65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714790543fc3e74b681e0f527663883fadc99963738cc81dd244d866241b296cb6d54596c296170534b": "0x00000000000000000000000000000000000f4573706163696f2043726970746f0f4573706163696f2043726970746f00000000000f406573706163696f63726970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714794c7db721956af68a56b1da8dc3f4bd58630c15f5754ee634528a007ab510c86a7e1fe6e62f4166": "0x040000000002000000000000000000000000000000000a414c46415354414b4500000010616c66617374616b6540706d2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471479f659a7c9788777c4181e01fbb27d6e4792397efe9becf4a3e4c535be6112f60b5ece445a76552e": "0x0000000000000000000000000000000000157061726974792d7374616b696e672d6d696e657200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471479fa24e506fa3936321d847f8a53927fe1132754a7708c61049b2d4816ab6ce7195a8308e97c3d41": "0x0400000000020000000000000000000000000000000015f09f8d8041524953544f5048414e4553f09f8d80135079746861676f726173204361706974616c1f68747470733a2f2f7079746861676f7261732d6361706974616c2e6e65741b407079746861676f7261732e632e693a6d61747269782e6f7267207079746861676f7261732e6361706974616c40747574616e6f74612e636f6d00000e405079746861676f7261734349000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147a03e16a44321a127421cac8b6fb5e37a318f997074319584b2be9ca7f9ded1f7c999e27f7eeef02": "0x00000000000000000000000000000000000753504143455f00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147a246b025078730ed4fa8184e7ea69dbb3cbee71cfd7490803d457140d5d57d0e53a49a887bc4a06": "0x00000000000000000000000000000000000c6578706563746368616f730000001a65787063746368616f734070726f746f6e6d61696c2e636f6d00000c4065787063746368616f73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147a2939f6c5b0bc2542a463a7a8813a1272eb8605af0c83660ce65b57d3d5f85ffa5eeff31793ad21": "0x0400000000020000000000000000000000000000000013416374697661746f72207c2053657262696100001a40616374697661746f726e6f64653a6d61747269782e6f726718416374697661746f726e6f646540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147a390aecbef66d7830ef2d9cf069b213123b970d5b3f1cbba939a6a5f4af7424e0672751b8a2ae1f": "0x00000000000000000000000000000000000f73696c69636f6e7072616972696500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147a4ea65f46c7d37580919233b08e12a121b170ef3622bd400ca60df99cd7a4cac5eae6f06025380b": "0x00000000000000000000000000000000000d6f6e64617869616f2d646f7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147a8b32f16affc46690c0525e98b52472f3330771f76feda69ddd01aeec5995085b96df4a0d6abce5": "0x0400000000020000000000000000000000000000000012424420426f756e74792043757261746f7200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147a92dffceaab6643ede4ad3403c21a44fff398a158e058f9212e92444bdcbc699ef01962578ba4f5": "0x00000000000000000000000000000000000750414e4b414a1150414e4b414a2043484155444841525900001d70616e6b616a63686175646861727931373240676d61696c2e636f6d0000000b506f6c6b6176657273650000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147a98c3963c233c3248d3348d85e6e65def6ce7fdf16f9a8e7c7e3290ee2fe99ffb568a1ad86e1676": "0x040000000002000000000000000000000000000000000553696465000000177376657a3038334070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147a9fdf3a1eeb5b7d52fad643426e11575fa0a9069bf4d383b04fd311150ad974af8f5d4b3a6e5b1f": "0x000000000000000000000000000000000010537461726c61792046696e616e6365001968747470733a2f2f737461726c61792e66696e616e63652f000000000c40737461726c61795f6669000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147abb02eb1d54cf5dd5b89078eed9b9dfec5c7d8413bac0b720bad3bd4078c4d8c894325713192502": "0x000000000000000000000000000000000008427261646c657914427261646c657920416c6c656e204f6c736f6e000019627261642e6f6c736f6e2e35383740676d61696c2e636f6d00000c4062726f6c736f6e3130310f427261646c65794f6c736f6e36340000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147abc5f242c0220a7ec9579e168a1b2200cc3447f5e3c29f74883683ab134cac6372f378dee048745": "0x040300000002000000000000000000000000000000000b4561676c6520f09fa6850c4b656c6c7920496c6d6572000017636f6e74616374406561676c652d6e6f64652e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147b1b6d2a1ef31b0a3e8c72ac1b710bbce4e7c190568bf560d89416696ac2a4700406487cde98df04": "0x040000000002000000000000000000000000000000000a4d454e47204c4f4e47000000176c6c6835393132323138323440676d61696c2e636f6d00000e40444f545f6d656e676c6f6e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147b232746110617a52cbc67180b8230618e420c16395099c9f44fb825b8c3c649d670e6aa409f5539": "0x00000000000000000000000000000000000d4a616d657320576f2044464700000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147b5162449d2d3491bc5db9edb63d7b17fabfab232318e1b087066c0f23b06553f9080fb61a523616": "0x04030000000200000000000000000000000000000000104144494f532041205455204a45464518486563746f72204a6573757320536f736120476f6d657a000016686563746f72736f73616740676d61696c2e636f6d00000c686563746f72736f736167000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147ba825eccf7c1a59b66fc334c7c76cef9cc5ceac0f4b0837d845453a2e0e84703b280edf202c8f76": "0x04000000000200000000000000000000000000000000055554584f00001340766f6a7463683a6d61747269782e6f72671b766f6a74613135392e73747564656e7940676d61696c2e636f6d00000a403078566f6a746368000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147bbbfadb8086c23baeb6173b1b6d5933c79992954d1469e845a89a5c754f91a9cb2f7589d78b9970": "0x04030000000200000000000000000000000000000000084d61785f4849430a4d6178205265626f6c00001a6d617840686172626f7572696e647573747269616c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147c44d632272a194b12b8bb5de35aa1722e1996065bb42a0fca24449625d4f72cdc483e682b22832c": "0x0403000000020000000000000000000000000000000005444f544117444f54417c444f543230206f6e20506f6c6b61646f7400001367656e697573756e40636861696e2e70726f00000b646f7432305f646f7461000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147c4a65539d0e9a75fc8163c92cac3dd1d5c2c0a049c3652a8b3b8b6cb8a0867a5e494c650b4de371": "0x040000000002000000000000000000000000000000000d504f5354434841494e2e494f00000011646f7440706f7374636861696e2e696f00001040706f7374636861696e646f74696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147c90c43e925f2226fea885b4e897d4fe908db6abeb39365ccc4689c2b6437dba675a4a0a0c0a6610": "0x04010000000200000000000000000000000000000000073330383072611f33303830205265736561726368202620416e616c7974696373204c74642e1468747470733a2f2f3330383072612e6c74642f13403330383072613a6d61747269782e6f7267107465616d403330383072612e6c7464000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147d28858f34005cc8b6761e35250c1155f4bd6eef565b7d28f7ccca539fd8f678ec0563427c9d9d46": "0x000000000000000000000000000000000009534147494954415200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147d2e715ea1e6005feacf33e37aff83d33c7472ff69683a322ab320d9de25586311e75b6ac8270f5c": "0x040300000002000000000000000000000000000000000b4361726c6f2053616c61124361726c6f2053616c612047616e63686f0016406361726c6f73616c613a6d61747269782e6f72671a6361726c6f73616c61674070726f746f6e6d61696c2e636f6d00000d406361726c6f73616c613232000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147d37f03ec99fdcbb1caf1e15eeb0d3a899f1c3e1aa7d5178d4b3e4f29c89667e7c4560172371660f": "0x000000000000000000000000000000000009506f6c6b61646f7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147d3c2944c766ee6eaa328d54b650d207564ecb9d0267e1bc52c18e22abe6366af6014ca53d06cb33": "0x000000000000000000000000000000000009706f6c6b6164616e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147d652feaf863f69f94546ff56643b8c0fed386347d7a8cd0b995383125a0fc0f0e45f0e33a6c5827": "0x04010000000200000000000000000000000000000000196365647269632e414141f09fa68a7c20616a756e612e696f001268747470733a2f2f616a756e612e696f2f1540726f786f6e746f783a6d61747269782e6f72671063656472696340616a756e612e696f00000a40526f784f6e546f78000d6461726b667269656e64373700", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147d6d64049c8aa12d29cf01efa70d0f6f4758a5b5374469b0b4d1cd239ce2e5ee2d38fb4b70efdd1b": "0x00000000000000000000000000000000001847696f74746f20427261696e20476f7665726e616e636500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147da0ec7e951876bb62b8fd03e30e4f98b0da0e2a99caa7efff8b951990ead650ae3bb9ae36982651": "0x040300000002000000000000000000000000000000000756614b614e6f0d41726c6579204c6f7a616e6f00001661726c65796c6f7a616e6f40676d61696c2e636f6d00001e68747470733a2f2f747769747465722e636f6d2f56614b614e6f425443000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147da931f7536ac4ab484d62a228121a1800e26e8b0139995703309a81ca4467308d734b4678da3d40": "0x00000000000000000000000000000000000a4669676d656e742032001368747470733a2f2f6669676d656e742e696f000100000b4669676d656e745f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147e5babdd52e35979dc308c3ff0d8cecba66c3198e955d6986b577f2778da300964daa5c05f31310d": "0x040000000002000000000000000000000000000000000a56616c6c65746563680d56616c6c65746563682041421568747470733a2f2f76616c6c65746563682e65750012696e666f4076616c6c65746563682e6575000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147e8c7a3ed6096e376c38ee38bd0943c66fbec7b8222ce117c0feeccdb9c5cd9ccddaafe82bab4c40": "0x000000000000000000000000000000000009506f6c6b61646f7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147e954ecfd56b24afeea0ee586e9b430c8b3f0fb625bd8ef5f9f6447e72d8c7e84a24c1a34abe9753": "0x00000000000000000000000000000000000c5375706572636f6c6f6e79125355504552434f4c4f4e5920434f52502e1968747470733a2f2f7375706572636f6c6f6e792e6e65742f17407468656d61726b69616e3a6d61747269782e6f7267186d61726b69616e407375706572636f6c6f6e792e6e6574000011407375706572636f6c6f6e795f6e6574000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147ea9886926039761182fe099d0c1787bb1b88de855537dcce095204028736ecfde09dd115c498f2a": "0x040000000002000000000000000000000000000000000c503250205354414b494e47001868747470733a2f2f7032707374616b696e672e6f72672f001a7032707374616b696e674070726f746f6e6d61696c2e636f6d00000c405032705374616b696e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147eaf54d33fb6eaa0e45255703a954986602a77b7103cd062576f33811e5b5b853de2ec1d120ea609": "0x000000000000000000000000000000000009616e64793230343609416e647950656e67000017616e647970656e673230313540676d61696c2e636f6d0000000009616e64793230343600", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147ed96b7d3f8a3e92083857fb5e068b7253110088fdc0431964252a7d2f46622d6b3cfc66be587c30": "0x0000000000000000000000000000000000087361746f73686900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147f1f1924b3cbcec77a2b3317472cfc9a20318bf0054952052ef8df169438af1b188eeaedb2bc030e": "0x04030000000200000000000000000000000000000000084145436861696e0b4164616d204576616e7300001b6164616d2e6576616e7340636861696e616c797369732e636f6d00000a406164616d65766e73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147f38100bed09f866b0b000e408509bb033443af0bc700ec11894f81c090d58d7dc2ae3174c54902b": "0x04000000000200000000000000000000000000000000074c4547454e4400001a406c6567656e64373334313231363a6d61747269782e6f7267156b7572746f736973407961686c6f6d692e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147f6122436697b141faff1c4b5a94649e0338783122b93a469eb9e2e375bb71c92028a8fe9b6e2f46": "0x08000000000201000000020000000000000000000000000000000016546f74656d204c697665204163636f756e74696e6711546f74656d204163636f756e74696e671c68747470733a2f2f746f74656d6163636f756e74696e672e636f6d0019696e666f40746f74656d6163636f756e74696e672e636f6d00000d40546f74656d5f4c6976655f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147f659dc8b63c4bfa16bf09f62054218ff31e36e042d5c0e56aea26366bfd8c75cff2835ced5afb41": "0x000000000000000000000000000000000012f09f9890207c20f09faaac2067686f7374000016407265706c67686f73743a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147f746099fc9e6cff3cba3cb96173c2dfff3f2bb0cf5e3c70784cffadbf02b1b2be4fcdd1f78e4374": "0x040000000002000000000000000000000000000000000c42697420436174f09f90b1000013406e69756e69753a6d61747269782e6f7267186269746361743336352e636f6d40676d61696c2e636f6d00000b40426974436174333635000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147f88ca6ada2372bc1a0eb7fdecee073651f1a21b9779b7bf5670493a0e35a53ab83b3cabab814a77": "0x0400000000020000000000000000000000000000000008316b766e6f6465000013406461766534343a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147f9e55d7623ce6876ae93e7162785a77d3a2c0413a9ee04af1b948ba5df9ac191552b72e1dd49b71": "0x04000000000200000000000000000000000000000000064372616e65000000000000094030786372616e65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147fa9548e769cc523a237fa87e569e6b5aed61aefe41eacafe0fd7000ca90f888e8517e77be1f3c78": "0x040100000002000000000000000000000000000000000c457175696c69627269756d001b68747470733a2f2f7777772e657175696c69627269756d2e696f001568656c6c6f40657175696c69627269756d2e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147fc4f80a616861525e9ec2d24e959cb9053751ac65b19ef3bc380ed2c24fa23a6bfa44c36b58d928": "0x040000000002000000000000000000000000000000000d4c4155524f20e298aeefb88f000000156c6175726f677269706140676d61696c2e636f6d00000c406c6175726f6772697061000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147fd837e50e26922d265013803cbe5f9f3ef7b38ad278b6d097d3be3ed79248030f460ba93d164a60": "0x040000000002000000000000000000000000000000000b5354414b452e5a4f4e45000016407374616b657a6f6e653a6d61747269782e6f726710696e666f407374616b652e7a6f6e65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047147fe9c3b575119fc9bea849c82dc0da19b799de164e15d83352559a2a6ca5fee6bf3f35056a997a21": "0x040000000002000000000000000000000000000000000f506f6c6b61646f7420496e64696100001540616d69742e776f773a6d61747269782e6f726717616d697440706f6c6b61646f74696e6469612e6f7267000010405f506f6c6b61646f74496e646961000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148015f9c9dfb7b49a281a723802b50fb25a874e19d4f643168f4016aa759bad298940eb455cc87249": "0x040300000002000000000000000000000000000000000e506f6c6b6157617272696f72730f53616e6720486f6e67205472616e00001868656c6c6f40706f6c6b6177617272696f72732e636f6d00001040506f6c6b6157617272696f72735f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714801d924a92a5fc1918bd4fb6b90f5088bdc825c3d674bd72e705c6f1163e86f960eeb7969ab4833a": "0x040000000002000000000000000000000000000000000857315a53505233000016407265706c67686f73743a6d61747269782e6f72670000000b407265706c67686f7374000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148031c0fb42aa475ed26298f9adfd81b0194a572bd49c0a8469acdcb585586dfc22bd1deef8a8e705": "0x00000000000000000000000000000000001b496e7465726c6179204c7464204576656e747320426f756e747900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714803d5aecf59783b17628a5be63c4d3c8dbb96c2904b1a9682e02831a1af836c7efc808020b92fa63": "0x0400000000020000000000000000000000000000000006626b63687200001140626b6368723a7061726974792e696f0000000740626b63687206626b6368720000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714804e9334b8d21cf9c26ccc67ae0f9051eb2f2b1795b8f166f46536a29b6651fe7f6bb1f8b550047f": "0x040000000002000000000000000000000000000000000c5368616b7572686172756e000000167368616b7572686172756e40676d61696c2e636f6d00000e40416761736869486172756e6100115368616b7572486172756e233837393000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148078a26b9e8719c946e2508c379dc61858e00b9fbf1424b12b64d5120d2f16c525822f94acc0f435": "0x040000000002000000000000000000000000000000000a4d61676963205461620000001679736e61796e6e3230303240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471480815510d147d8979a0b9cef028c95b4635cd22fefd9001a544d17eae85fbaf1857fda4d9f4ba75f": "0x040000000002000000000000000000000000000000000c6d616c697a652066726564000017406d616c697a65667265643a6d61747269782e6f72671663667265646d616c697a6540676d61696c2e636f6d00000e4066726564636872697374757300106d616c697a6566726564233234373200", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471480903a9113fc7d1e24be4391bcf17d95b0ccf03c2c1374f0e20c7a886eccfb3a77b373f3ff97166d": "0x040300000002000000000000000000000000000000000d44697667756e2053696e67681344697667756e2053696e676820536574686900001764697667756e2e736574686940676d61696c2e636f6d00000874656b76797979000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471481032745bb8fa3a2ae2d99c29e603ff76a9705ac23371d47b7245bc3bf953ca7906347b952390d78": "0x040300000002000000000000000000000000000000000b416c657820486f75647a0000000f616c657840686f75647a2e636f6d00000b40686f75647a5f6b656b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714810532057fdf6829669618142822c01d88debd960eaa1d3dbfcb243ad965b8a5ded1c7b7c15b2226": "0x040300000002000000000000000000000000000000000c504241202d206d72617374134d69636861656c205261737461646d6568720000196d69636861656c2e72617374616440676d61696c2e636f6d00000a404d72617374313131000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714816c2a63e3c205f4981c728cb2461814ec4e6cb50906eded0e4f51e5513018e28753337cfb971d70": "0x0401000000050000000000000000000000000000000006546164656a00000011746164656a406170696c6c6f6e2e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714816f363193f343a643fa61b298e82f9f207ddea327900cee26b554756c4a533f36cd875e3e7bcf06": "0x04010000000200000000000000000000000000000000114163616c6120466f756e646174696f6e001668747470733a2f2f6163616c612e6e6574776f726b001468656c6c6f406163616c612e6e6574776f726b00000e404163616c614e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148179afc2191e064c0ecd029fdf9259e900f08996fc9862ebfe100d49c439f19bef7084b258175a1e": "0x040000000002000000000000000000000000000000000f507265737362757267204c6162730f507265737362757267204c6162731a68747470733a2f2f7072657373627572676c6162732e636f6d001d7072657373627572676c6162734070726f746f6e6d61696c2e636f6d00000f405072657373627572674c616273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471481bac95793d75f5ac6d1e9633ce74e1a09f9ca37470de256f8d1cd977ba4e4eb994258dc078f4429": "0x040000000002000000000000000000000000000000000e4265696e67205361746f73686900000017616e616e74406265696e677361746f7368692e636f6d00000e404265696e675361746f736869000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471482a2945d18dc6de7685dc052a755ac3cecc0b0bd0d1405ade433d8463a3cabc1e5b7eedb13c08871": "0x040300000002000000000000000000000000000000000552656d791752c3a96d79204269656e2042616f20506572657474690000126c65696d69303640676d61696c2e636f6d00000a40736578796465666900086c65696d69303600", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471482b81636ee5910ca452de33977b421766e2191acd0b79dd6760fb5e56aee0b00a02422df3f35b26b": "0x0400000000020000000000000000000000000000000010466f7274756e654e6f6465732e696f000015406769736c656d6f6e3a6d61747269782e6f72671668656c6c6f40666f7274756e656e6f6465732e696f00000f40466f7274756e655f4e6f646573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471482b8bce0892920f9ca3d904d81a1b1ba11bd6e391daa897b907ff89c5c5aebfe6f2da23292b8500f": "0x040300000002000000000000000000000000000000000d5765796c616e645f46756e640000001a79616e6777616f406d6574617072696d652e6e6574776f726b0000086b6f6461646f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471482d187c7f127ad6e6c49e0b3f2b674374ad5d80a75f1e60cdd012e6e8e3739a10cdae9cb76ef857c": "0x00000000000000000000000000000000000c453230205374616b696e6700000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471483253a02c1f77c53a0c4d9864d312099740db16ed243615447e5ca42aa689fee0109c195c65fa91e": "0x040300000002000000000000000000000000000000000a43687269737469616e1143687269737469616e20436173696e6900001e63687269737469616e636173696e693139393340676d61696c2e636f6d00000c4043687269735f4e696674000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148328836b7b67fa4fdab8f87f6dfc47a03ebf8901b36039ebfce8707a2c5d3f4ff099d788ca6a02fe": "0x040000000002000000000000000000000000000000000555545341000018406c65736e696b5f757473613a6d61747269782e6f7267176c65736e696b3133757473614079616e6465782e727500000e406c65736e696b313375747361000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148348313aaefc25eca801051533332369ef5a24d9d7d13709896d3e170297851c63123b26a4034137": "0x040300000002000000000000000000000000000000000d53657262616e20496f7267611253657262616e2d496f616e20496f72676100001473657262616e33303040676d61696c2e636f6d00000a73657262616e333030000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714839f1a74e283a0e1b26cea7b8424d659bd6ee2dc3176d96dc7d646ebff25fa7004c88c68770d584f": "0x040000000002000000000000000000000000000000001b50617472696365202d2054656e64616e6365732043727970746f0000001a74656e64616e63657363727970746f40676d61696c2e636f6d0000114054656e64616e63657343727970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471483c5f86634d0a9e146190509288f1d1dabe6de4917e2a5765eda06bbaa156d19d55e2b7f813dd416": "0x040000000002000000000000000000000000000000000c4d697373426974636f696e00000014646f74406d697373626974636f696e2e636f6d000011406d697373626974636f696e5f6d6169000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471483ce59511097e6b62add0af948eba3b1fcd5cacde1f6fcc70f11ef75056f88ca4d11dcc5b080220e": "0x0400000000020000000000000000000000000000000017416c62657274202d2049204c6f76652043726970746f00001840696c6f766563726970746f3a6d61747269782e6f72671e616c62657274706f6c6b61646f74737061696e40676d61696c2e636f6d00000f40495f4c6f76655f43726970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471483da9cc355d0681680d8a3f4317249a895e4b49badcfa7293cfbd215d6e552d1c07024d36acfbd5d": "0x0800000000020100000002000000000000000000000000000000000f53696d706c79205374616b696e67000000197374616b696e674073696d706c792d76632e636f6d2e6d7400000b4053696d706c795f5643000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471483dcba70a0b190a2a2d23b8643531f6e7ae2dd88ff990de4edec8ec0c655002caca002756d1b2704": "0x00000000000000000000000000000000000d4461766964204172636865720d4461766964204172636865720000156461726368657237383640676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148407ca5b0d3d9daf59a32cbcf20e6fd03a2044525bf4b0676c4d0f1fa5f6f15699c607aa705f1fec": "0x04000000000200000000000000000000000000000000056c616461000000136c6164612d6b6d764079616e6465782e7275000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714842f41c0d4632e2e769ab3ef0ff669144d7046a0d9f11b76ec2efa8aa76bdbb0fedb69e8126b897b": "0x0400000000020000000000000000000000000000000019f09f939c20486f6c7920436f6e73656e73757320f09f939c000015406d6f67696f6d616e3a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148461b8e5b35b37172450c3f4fb88d8cf3e0a4f044b4ad1d327ecd3e6b1d7dfbb999713f04ee1e87d": "0x0400000000020000000000000000000000000000000011e5a4a7e59684206461697a656e2e696f000013406461697a656e3a6d61747269782e6f72670f696e666f406461697a656e2e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714846cf02493758816a4cd4dd151f0106d8157bdf02bfac75f9abe8e635ecc6498b8a8f6acc1f5e674": "0x0401000000020000000000000000000000000000000014f09f908b696d546f6b656e205374616b696e67001168747470733a2f2f746f6b656e2e696d000c626440746f6b656e2e696d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471484738c96361adf35624b8f2e5a08aa4e0dc556fa9745a1509c91a8c17dae991bcfdf89133ffc5b00": "0x0403000000020000000000000000000000000000000012506f6c6b61646f7420536176616e6e6168002068747470733a2f2f6c696e6b74722e65652f776562335f736176616e6e61680015696e666f4077656233736176616e6e61682e696f00000f40776562335f736176616e6e6168000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471484d3812801bbbf15b1725c0de514e0df808b19dbfca26672019ea5f9e2eb69c0055c7f1d01b4f18a": "0x0400000000020000000000000000000000000000000021416e74692d5363616d20426f756e74792047656e6572616c2043757261746f720000001a616e74697363616d40706f6c6b61646f742e6e6574776f726b000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148510852a75250a28040a61ca8223dcb3c203b27167fdee611801375fff6c6f25c71c3e3ca86cea65": "0x040300000002000000000000000000000000000000000d566963746f72204f6c6976611456c3ad63746f72204f6c69766120566964616c000012762e6f6c6976614070726f746f6e2e6d6500000a40766f6c6976615f76000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148532f472372d0e04e83a05a23704fc96cae39473196633a53d1820656f8b81f7bf30637ce41bd965": "0x0403000000020000000000000000000000000000000004414a501b41697a61204a6f7963652050726164617320536865707061726400001661697a6131376a6f79636540676d61696c2e636f6d00000a40616a637073686570000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148549b7fd2bfcfdd9bc0525c374a8198f3288a0733918321dfc26532e253d94da3a6a27a4c3e31760": "0x040000000002000000000000000000000000000000000b506f6c6b612048617573001c68747470733a2f2f7777772e706f6c6b61686175732e636c75622f0014696e666f40706f6c6b61686175732e636c756200000b40706f6c6b6168617573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714854f02c37bb0a17d4653f0d351f8bca69b7be83c41b90fcee17c7ac2b285bb01a95b3755a6101a3c": "0x040100000002000000000000000000000000000000000b52616469616e63655f3905416c657800124061636964783a6d61747269782e6f72672172616469616e6365395f7374616b696e674070726f746f6e6d61696c2e636f6d00000c4052616469616e63655f39000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471485776350491a535bcea1d92de6550af9c490706f76cb9e12458c4632ad594cd66784630ad2e1ef34": "0x040300000002000000000000000000000000000000000a54686520424c4f4b431254686520426c6f636b6c61627320496e630000127465616d40746865626c6f6b632e636f6d00000f40746865626c6f6b6367726f7570000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714858c0d6078e97ec2863010a4400694804bb594ed57a7d55e6149ce2e8abe2657b601fe2a7fabed00": "0x04000000000200000000000000000000000000000000145061766c61207c205061726974792044617461000011407061766c613a7061726974792e696f0000000a406461656d69613130000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471485a31ae8c4d5be8eea99c22436f5014c366a3e94a05c3410b997e436a414f7697af3f8206fa87e5a": "0x04010000000200000000000000000000000000000000114175726f726120506f70707973656564114175726f726120506f707079736565640000196175726f72612e6d616b6f76616340676d61696c2e636f6d00000e40706f70707973656564446576000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471485a3d2fc56fa42403616337a83344f0849510571f384a48a55563b6618304e4da6162c4b7cc48e53": "0x0000000000000000000000000000000000186f7869786f20706f6c6b61646f74204e585f424c41434b00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471485c4e925378eb4916c42f37017f31d6c9ba5dca62626e6fb434d6edc31bfc9aa49f001a6ced27876": "0x040000000002000000000000000000000000000000001df09f8fa2204d696e6973747279204f6620426c6f636b7320f09f8fa20000001778406d696e69737472796f66626c6f636b732e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148613e243ad5accda52a6c52dc82940a36fefd1474cc0778517bb1a56b7bda0e308b6c19152dd7510": "0x040000000002000000000000000000000000000000000b4f70656e537175617265001f68747470733a2f2f7777772e6f70656e7371756172652e6e6574776f726b1840776c69796f6e6766656e673a6d61747269782e6f7267166869406f70656e7371756172652e6e6574776f726b00000d404f70656e7371756172654e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148621671899b86161726ac63a0a6a700ad7e1178fef89a87620bbc152a19f74708defc7f08bbc6556": "0x040000000002000000000000000000000000000000000a426c6f636b4374726c00000019626c6f636b6374726c4070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148640e8e247a0e20f30cfdb48ff7f33b08499dfc618a8ef9699b8345fa65f0b1339eb8eec3c0e4555": "0x08000000000201000000020000000000000000000000000000000018f09f949273746174656c6573735f6d6f6e6579f09f9492001d68747470733a2f2f7777772e73746174656c6573732e6d6f6e65792f19406161726f6e7363687761727a3a6d61747269782e6f7267194161726f6e2e416e746f6e6f706f756c6f7340706d2e6d65000010404d6f6e657953746174656c657373000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714864262e04bb1d055588b5b3b691acdcdcbbc78158f60b97398185efb643eed0bba3e8ad6ac24b545": "0x040300000002000000000000000000000000000000000f4c756e6172205374726174656779134c756e6172205374726174656779204c444100001a696e766f696365406c756e617273747261746567792e636f6d00000f404c756e61725374726174656779000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148665682395484a7f5e5e31bfd996593554b54016676af00cc0ac2015b12c8824e5a50315f1bfe05a": "0x000000000000000000000000000000000012426c6f636b737061636544657672656c730000194073616368612d6c616e736b793a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471486a03b31f22c207f6607dc83a7fc1f33c13e978ee3527fdb2c908ae6b5c0d2dee81bf30a01808263": "0x040000000002000000000000000000000000000000000c426f747469636577736b69001968747470733a2f2f626f747469636577736b692e6172742f001b626f747469636577736b694070726f746f6e6d61696c2e636f6d00000d40626f747469636577736b69000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471486e69567b2fc7d85a44fe0211e518d4a7e0d647cb66979cafcf322d3427972abf875d29c7c76d501": "0x000000000000000000000000000000000013776f73732d303031407375627374726174650f44616e69656c204d6172696369631068747470733a2f2f776f73732e696f1140776f73733a6d61747269782e6f72670f64616e69656c40776f73732e696f00000940776f73735f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714870976f0672085c5f8ff75032359f0de13264a134c3e88089cf6a2a31e5cf3cdfe405a4e272f0508": "0x040000000002000000000000000000000000000000000d547963686f204d61736975730000000000000e40547963686f5f4d6173697573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714870de10fb6114007fed7d1cc37fe7a63ac7e5882194d3684e12a17df4d2848a4f4b7e5abefed92c9": "0x04000000000200000000000000000000000000000000125969656c64426179205472656173757279000019406e6967687477696e672d79623a6d61747269782e6f7267166e6967687477696e67407969656c646261792e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471487116eaed0a9a16e7e29f9518827a1bfb2e3b6a8a153cb136f9405f68c728f9c8a38c7971897b77b": "0x040000000002000000000000000000000000000000000f434f494e205245464552454e43451b4a75616e204d616e75656c2053616e63686f20506974617263681968747470733a2f2f636f696e7265666572656e63652e6d6516406a6d636f696e7265663a6d61747269782e6f7267166a6d66696e616e6765737440676d61696c2e636f6d00000c40436f696e5265665f636f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471487149495530dd8d3f81550f147455d286246994dbf4bfa37961d235b96ed8d1189b7445c18680f50": "0x000000000000000000000000000000000006646f796c6500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714871588d7f6c0fdd3002f931bb0cf405212de02243756d8ff665710af7fb234bfb1a50bb78ae1327b": "0x040300000002000000000000000000000000000000000b52617669204b756d61720b52617669204b756d6172000015697473796f757261766940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714871c07918bd122cae4a15453d67f7353ee3d4eca8f465b2337e675b5efdebebe8209b6179af0bb43": "0x00000000000000000000000000000000000f5a6565205072696d65204c6162730f5a6565205072696d65204c6162731368747470733a2f2f7a706c6162732e696f2f0012636f6e74616374407a706c6162732e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471487258a23c1cc74c20c618307d00b3354999ae280858c01fceddd77a25b5bc665fbd4634bf86a4178": "0x04030000000200000000000000000000000000000000057469656e115469656e204e677579656e204b6861631268747470733a2f2f7469656e2e7a6f6e6513407469656e6e6b3a6d61747269782e6f72671b7469656e2e6e677579656e6b6861634069636c6f75642e636f6d00000d405469656e4e677579656e4b00087469656e2e6e6b00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471487537aeb7813f75e5efbe83a561d19b1d21126af5f1608ed28e56f4f70251cf965fe3dcb4901a26b": "0x04000000000200000000000000000000000000000000084b686173746f72000014406b686173746f723a6d61747269782e6f7267127374616b65406b686173746f722e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148756caab39a157be040f4dbfe9b3333f7faf9304bf2ba9af0b4f850bab4e716759e0ebc8c4703c8c": "0x040100000002000000000000000000000000000000000a434f534d4f54524f4e00001740636f736d6f74726f6e763a6d61747269782e6f72671d636f736d6f74726f6e76616c696461746f7240676d61696c2e636f6d00000c40436f736d6f74726f6e56000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714878042bdd9d96dd3f07260abfd43d442e54357c409f75680478b2e97f348126e199537eaf964b342": "0x0403000000020000000000000000000000000000000007536f757261760e536f75726176204d6973687261000016736f757261762e6d2e627440676d61696c2e636f6d00000a3078736f757261766d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148795674870556252585e982d74da4f4290d20a73800cfd705cf59e1f5880aaee5506b5eaaf544f49": "0x040000000002000000000000000000000000000000000c446f6f7264617368636f6e0000000000002068747470733a2f2f747769747465722e636f6d2f446f6f7264617368636f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471487ac7128ed93fd445e50e6e8499b921414360c3da7de7ca78544e38412bb6dc313383aaceb7c2068": "0x040100000002000000000000000000000000000000000a5072656d6975726c790e5072656d6975726c79204fc39c1668747470733a2f2f7072656d6975726c792e696e2f0015636f6e74616374407072656d6975726c792e696e00000b407072656d6975726c79000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471487b8d0a4f87b32a47c3f190f0abecf2b39643a21a67df302a024487d84128d5bc68fcc445ac23a06": "0x00000000000000000000000000000000001142494e414e43455f5354414b455f31301142494e414e43455f5354414b455f3130000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471487c09bd19ab0b74df0802c5c92cc72abd8e7b849f32513f21d05bea7f60ec3f92a20854def08f247": "0x040300000002000000000000000000000000000000000b43727970746f5769736500000015636f696e6765656b323240676d61696c2e636f6d0000104063727970746f7769736567757973000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471487c5eb005741995b4a729cc2bb0da0787bfa545a498c261969623c3042b3099ffe331c5728f94125": "0x04030000000200000000000000000000000000000000084261727265746f0f4d617274696e204261727265746f0000166d746e2e6261727265746f40676d61696c2e636f6d00000b6d746e4261727265746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471487d2f749afa8b991984e16482c99cfad1436111e321a86d87d0fac203bf64538f888e45d793b5413": "0x0402000000030000000000000000000000000000000004576569095765692054616e671268747470733a2f2f7061636e612e6f7267000e776569407061636e612e6f726701bfa78a598c9343be661a1ee6e175faa5093a30680000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148827ff7e5656ef4ab1dca1726b672e75cdbdf56f990fc4170e44d460821e91a89d846f2359b45fb3": "0x0400000000020000000000000000000000000000000010467265736843726564697420496e6310467265736843726564697420496e631868747470733a2f2f66726573686372656469742e636f6d001e6465766f6e2e73686967616b694066726573686372656469742e636f6d00000d406672657368637265646974000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714885fb110161cbc6f4227279d29a93cb842f44a0bc9c29f4041d903011031bf155e7cb64814e37a6c": "0x00000000000000000000000000000000001f436f72706f7261746520506f6c6b617373656d626c79204163636f756e74001e68747470733a2f2f7777772e70656e64756c756d636861696e2e6f7267001c636f6d6d756e6974794070656e64756c756d636861696e2e6f72670000104070656e64756c756d5f636861696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714886cf5449f57c7b92488bc636ae2d90c355a53620f8c6dc36c8f30837f805ea399710bc2a43d6d18": "0x000000000000000000000000000000000006535441534800000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148873265c60b23b3ce47c0672b1389c3456de496556d40ad8f2923595d54eb1f69b617febb8292501": "0x0000000000000000000000000000000000094b6f6c6b61646f74000000156b6f6c6b61646f74383640676d61696c2e636f6d00000e6b6f6c6b61646f745f6d656d65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471488a1a127cd77d12b806fc8e477a0cab0bd93e76f7e9f73fdee31ffe7dcd7d052037c39554f212e04": "0x000000000000000000000000000000000006532d646f7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471488ffc163fa9907f076f9c831b885b8f1a216a27064fa793733b162ee06afb502a8cdbc2ccd6cc536": "0x0400000000020000000000000000000000000000000009594a52656e6175640000001c796f76616e6e7972656e6175643634363140676d61696c2e636f6d00000e4052656e617564466562726573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714894c10549d53016ad8290537d6e31fe1ff165eaa62b63f6f3556dcc720b0d3a6d7eab96275617304": "0x040300000002000000000000000000000000000000000a4a616e2042756a616b0a4a616e2042756a616b1a68747470733a2f2f6769746875622e636f6d2f6b6f7574652f0f406a616e3a7061726974792e696f0e6a616e407061726974792e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148954d9b6c7704b5c0a912cf4f0c7894598d81d26f2c24f6e5c2541f312462bb576593e9dc549146d": "0x040000000002000000000000000000000000000000001051757069642076616c696461746f7200001b40717570696476616c696461746f723a6d61747269782e6f726719717570696476616c696461746f7240676d61696c2e636f6d00001040717570696476616c696461746f72000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714895c460a54a2e543ea803ae6f4cabebc87af9678cfecfa3c302244b138e0d691947bdae74b4a8c6e": "0x00000000000000000000000000000000000c50696e6b446f6c7068696e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714897931e60764d4ea8e602e63afb364ac583747b0a8bab092d5b20ee98f0495ce7562a8c992ac9f17": "0x040000000002000000000000000000000000000000001070617468726f636b6e6574776f726b0000154070617468726f636b3a6d61747269782e6f72671f70617468726f636b6e6574776f726b4070726f746f6e6d61696c2e636f6d00000b4070617468726f636b32000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148980acab3e7bb9bb1035801fd00144e10a3933ed859f8236bbffb93a7ac515bab9f1ca53cbb3f776": "0x040100000002000000000000000000000000000000000b4a7573745f4c75757575000000166c75752e6b6f6461646f7440676d61696c2e636f6d00000c404a7573745f4c75757575000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471489b10dfbde827732504ee3d36525d0ab821bef5161d7dc32eab7015fcf6f9c913d8b7178f9a5d261": "0x040000000002000000000000000000000000000000000b57617465726d656c6f6e00001b4077617465726d656c6f6e6e6f64653a6d61747269782e6f72671a77617465726d656c6f6e2e6e6f646540676d61696c2e636f6d0000104057617465726d656c6f6e4e6f6465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471489b9a5bd12ebd0af56220569726127d988e0eb1dd10defd87548de2579edcb6490af1214e568232d": "0x00000000000000000000000000000000000f4d65726b6c6520536369656e636521537461636b7365657220546563686e6f6c6f67696573205074652e204c74642e1f68747470733a2f2f7777772e6d65726b6c65736369656e63652e636f6d2f00196e69726d616c406d65726b6c65736369656e63652e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471489d14e177f11397c9b62bcf47bb081dc8ecdf2d205b84d7c37261d3b5713b8e303b43537ca3a18ac": "0x04000000000200000000000000000000000000000000194d61726b6574696e6720426f756e74792043757261746f7200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471489f6ea03f6581d565675d0d624354179fa260c34540ac9284cded48d4c2951ff17265c4517a2a747": "0x000000000000000000000000000000000009426974677265656e1c426974677265656e20537769747a65726c616e642056657265696e1f68747470733a2f2f7777772e626974677265656e73776973732e6f72672f0017696e666f40626974677265656e73776973732e6f7267000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148a0b7cb67b16efc758ca37c5cd96fe3b250d9cdbabf28b891a93fd9ae9296e0e66732bfb57206918": "0x04030000000200000000000000000000000000000000084c696d696e616c0000001e6d696c65736272656e74706174746572736f6e4070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148a3ab59566c3860f1ecec4f3062f61988b13e9dab318860bd0fffe5b7b37880d50a614a0a20c2502": "0x040000000002000000000000000000000000000000000a2a2a2a2a2a202a2a2a000000196c756e61722e706f6c6b61646f7440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148a452b72d725a7f78a676bf14fbdb4da5cd987dde8b7bfada8064a24aba2775bb573c20e5479e06b": "0x00000000000000000000000000000000000a4344524156454e523200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148a8347ca23548ddc8afa8cba08ebd8a15eec2017ee6c73292be36384ac5666d4a6680ac0bf53e97e": "0x04030000000200000000000000000000000000000000094d61726b65646f7400000015636f6e74616374406d61726b65646f742e636f6d00000b406d61726b65646f7474000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148b0889a31b9f57b3e287c7494655d636a846f5c3347ad2cb3c462a8d46e0832be70fcc0ab54ee62d": "0x0400000000020000000000000000000000000000000008736b756e6572741153656261737469616e204b756e65727400154073656261737469616e3a7061726974792e696f12736b756e6572744070726f746f6e2e6d6500000008736b756e6572740000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148b204d64b79dca3e8a449fac4875a24774175651ffcacc851939eff231fcd2e270e1e29429902059": "0x000000000000000000000000000000000012436f696e46756e642f47726173736665640000000000000d40636f696e66756e645f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148b299234c2604004face99d3401cb9b45ee1bc0ec52f4cb35914dc5ad27806230534230eedb8413d": "0x040000000002000000000000000000000000000000000542494c4c0000164062696c6c3a776562332e666f756e646174696f6e0000000c4042696c6c4c61626f6f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148b5245b925036264bf6429fc1003a7db6b560bcd9a9a5942e05c0d2193190d5739c17eabbf6b61c9": "0x04030000000200000000000000000000000000000000184a5553542042657465696c6967756e67656e20476d6248184a5553542042657465696c6967756e67656e20476d624800154061726e6f6c645f623a6d61747269782e6f72671861646d696e406a7573746f70656e736f757263652e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148b64e7386b807a4c244a3fafb7a625ae311e31f2f83f470228a573ae21e011031a8c0721f86fe844": "0x0403000000020000000000000000000000000000000015f09f90bc50616e646157617272696f72f09f90bc00001a4070616e64615f77617272696f723a6d61747269782e6f72671970616e64612e77617272696f72444070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148b6d998b612fe91d80c622c5213c9ff2cc3ffc7872409b6678e1dae72f63405698e8acfa2db2f06c": "0x040300000002000000000000000000000000000000000c6e69636f5f63616c63696f0c4e69636f6c6f205a696e690000166e69636f2e7a696e69313440676d61696c2e636f6d0000095a696e694e69636f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148b886522779ff3151d426e21be4fd135a6d4a02d9c0adbc43b24a428738c2bad59d722ea6f3e2f11": "0x04000000000200000000000000000000000000000000075a6f6e6461780a5a6f6e6461782041471268747470733a2f2f7a6f6e6461782e636813407a6f6e6461783a6d61747269782e6f72671068656c6c6f407a6f6e6461782e636800000a405f7a6f6e6461785f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148b93860429ecf0c99c1d315cb41447f88f1da0f976f9ebca8970536df07af241ace21b5757383c6c": "0x040300000002000000000000000000000000000000001149204c6f76652043726970746f2042440000001e616c62657274627572676f7340706f6c6b61646f74737061696e2e6573000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148b9d575479a1dea6000e1c0ffb6a29c80083a8b1c4dc4d70fb2fbbd9c1319b8e9c0173aaafbd2963": "0x040300000002000000000000000000000000000000000767616e676f760f4b616c6f79616e2047616e676f7600001267616e676f763140676d61696c2e636f6d0000076767657a7472000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148ba29f0652612f2cc4494455e738c0a47b5690708ab81dc9264395046089cd8aa613bae176c4880d": "0x040000000002000000000000000000000000000000000f44535347202865782d44414f5f290000114064616f5f3a6d61747269782e6f72671964616f7465726d696e616c636f6d40676d61696c2e636f6d00000d4064616f5465726d696e616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148bd90fa5b38a5d2d74340c22162ba8ddd49d836d01b693aa4cdc07eced946b0701173e0cd6bc7d69": "0x0000000000000000000000000000000000134e6f6e46756e6769626c654a6f75726e6579134f73636172204672616e6b6c696e2054616e2168747470733a2f2f7777772e6c696e6b6564696e2e636f6d2f696e2f6f736361001c6f736361726672616e6b6c696e2e74616e40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148bdc67657419bbeb22ce51e9b096db10638d2889c4a847781e0360f6fd3adffa6280107ef7260f62": "0x0400000000020000000000000000000000000000000018506f6c6b61646f742048756220696e205370616e69736800000016706f6c6b61646f7468756240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148bed542ce020de9fd40d213230a9218267b54e293b86a4c6954d4e89ad60f9553ef9a63ac45dbaaf": "0x0000000000000000000000000000000000094d6f6f6e6265616d001a68747470733a2f2f6d6f6f6e6265616d2e6e6574776f726b2f0000000011404d6f6f6e6265616d4e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148bf84080a1ebdf7ca6f56e262666adfbbd80486024bb3255e8a5e31cf474ba64ea31a7c698833461": "0x0000000000000000000000000000000000084765726dc3a16e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148c508a69b80c90ec54b3e6a7a51343d32931c6f6820a84d8c0a8231906c4171d03e1dba0f0b50746": "0x04030000000200000000000000000000000000000000184d43207c204d43536572766963652e696f207cf09f92a50c4d61726b204372696e636500001a6d63626f7577656e7365727669636540676d61696c2e636f6d00000c404372696e63654d61726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148c56ef07b1e70c6e9227d3657bf3452543e8fcc6861134deffaed05ce3dff0ac63059aa0997c32cc": "0x040300000002000000000000000000000000000000000c434841494e414c595349530c434841494e414c595349531d68747470733a2f2f7777772e636861696e616c797369732e636f6d2f002063687269737469616e2e6d656e646140636861696e616c797369732e636f6d00000d40636861696e616c79736973000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148c7ff1340164f7757d1c361b50b0963e2a3d9533bf324bd7898729d41c82f483117f02af181d8fd6": "0x0400000000020000000000000000000000000000000014496e7641726368204173736f63696174696f6e0000000000001040496e76417263684e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148cb4556b548f669b787d4175bfe47411f2c567fbff326dba11aa94216c2fe904ab2c56b567b6220d": "0x04010000000200000000000000000000000000000000094861727269736f6e0000001b686d63676c6f62616c2e63727970746f40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148cc04fabfccee3139a92efbf31781a4ef54d55d10220a3025f65bed342c53c2b8fc3061f3d09c041": "0x040000000002000000000000000000000000000000000c4d794d6574617665727365001768747470733a2f2f6d796d65746176657273652e696f00147465616d406d796d65746176657273652e696f00000d404d794d6574617665727365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148d1ba1cf54c412597876aa7c5c9e71fd79444ae3af051244cd66e2700b875faeb1c280d3026b3d2d": "0x00000000000000000000000000000000000e54656b6f6e7978207a65616c7900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148d211992716663e682635369d20e36ccce82c98283410bfa4a4153e31bedb52b33cbce5543cba21b": "0x00000000000000000000000000000000001042494e414e43455f5354414b455f341042494e414e43455f5354414b455f34000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148d23d124156802cc9e25b58a4b93c0c51a65333999fc9f857deb2eace23e3c83ed79b2c08287343d": "0x00000000000000000000000000000000000a5374616b6542616279001668747470733a2f2f7374616b65626162792e636f6d0016636f6e74616374407374616b65626162792e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148d2b0ff93d409b20b9f14d73888b5029adb7dedd443f88d2d56088a315626a1d79507deb24a41d42": "0x00000000000000000000000000000000000f4b6f6461446f74204d696e746572001468747470733a2f2f6b6f6461646f742e78797a0000000009406b6f6461646f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148d47493ee3a600117269f7eab2e5f91f9efe10d0dba0e7256d9433230a8f9fdc1c4af10981853476": "0x04000000000200000000000000000000000000000000066a6f6e6173000017406a6f6e61733a776562332e666f756e646174696f6e19676568726c65696e2e6a6f6e617340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148d60cac4e5cdd86746ce78ca56ca1114d6abf7cb86879839ec6bf9c0c027ef318af3a77ac661401e": "0x040300000002000000000000000000000000000000000e416e6479207c20426561636f6e00000015612e676173736d616e6e407061706572732e6368000010616e6472656173676173736d616e6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148ddffc86aaebc2d41e5c61cb6941b247d22fa14392fb8710a23493db5857c2904a76b3bcfda7d217": "0x040100000002000000000000000000000000000000001e5765623320456475636174696f6e20616e6420496e766573746d656e740844722e2043616f00001261686a7863727a40676d61696c2e636f6d0000094063616f5f6c6162000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148df58d11b9c4504b7a748920fbf8b122f81c3b7d63f1bd116c75c3741f4aeb3b3d89c7bbdefb2339": "0x0403000000020000000000000000000000000000000018506f6c6b617373656d626c7920476f7665726e616e63650000001668656c6c6f40706f6c6b617373656d626c792e696f000009706f6c6b5f676f76000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148e1506cd6e1c1ab8103da75e835e84c21fd864cdea4484538e7394cdf81d607ae496168e587e1b47": "0x0000000000000000000000000000000000064861726973001668747470733a2f2f6861726973626f73732e636f6d00184861726973626f737340686f746d61696c2e636f2e756b00000c40696861726973626f7373000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148e28ba2eeb3d7c40f4168b94d0932bd7f917c00abb58c08e9d951e2718484d1e43ecabc506bd7c3d": "0x0400000000020000000000000000000000000000000019436f6e74726f6c6c657220446f7420436172626f6e7974650a436172626f6e7974651568747470733a2f2f636172626f6e7974652e696f0018446576656c6f7065724079656c6c6f77737475642e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148e46b19352a48af22483ec13c881ea5921623c6bc3b792559dd59df31eb5d42163dc1bb6505d453e": "0x040300000002000000000000000000000000000000000c626964696269646962756d00000012626964696269646962756d40706d2e6d6500000f40626964696269646962756d5f78000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148e534b2433a5f1501096e62e5bef031b7189ac027fcc3eaba9e1743c314e5fa2e7357b45636d7d41": "0x040300000002000000000000000000000000000000000d53657267696f61644c49424d1353657267696f204d6172636865736f74746900001473657267696f4061646c69626e65742e636f6d00000b405365725f61646c6962000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148e7257b4702ef819d2982658654fb6785977f89a5b1aab885021365dbb4b006b0ff550c219d98d33": "0x0403000000020000000000000000000000000000000009446547616d696e6709446547616d696e6700001268656c6c6f40646567616d696e672e696f00000c646567616d696e675f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148e7879bd31890686c879bc272c2afef97bc36cb48543598dd460776f082e51d5239efda3ab7fff15": "0x04030000000200000000000000000000000000000000074875626e65740000001f776f6f6c7374656e68756c6d65616c6472656e6140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148e92984083d101c32c8feeab5bd9a317375e01adb6cb959f1fea78c751936d556fa2e36ede425a47": "0x0403000000020000000000000000000000000000000010436f6c6f7266756c204e6f74696f6e0000001b736f757261626840636f6c6f7266756c6e6f74696f6e2e636f6d00000f636f6c6f7266756c6e6f74696f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148eaabd6d5a651ac5f45684f68ec654742c29fc6782be9921004192a1851c51154cf7b1cce2c6b518": "0x00000000000000000000000000000000000547656172184765617220546563686e6f6c6f676965732c20496e632e1568747470733a2f2f676561722d746563682e696f001368656c6c6f40676561722d746563682e696f00000c40676561725f7465636873000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148eb4ba0034502556f87dc01c2e647a407ec091e8ba718d691562d85ba49a97d55b9f7a7b56e81023": "0x000000000000000000000000000000000009536f7261746f676100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148eb8c14e9575686dc6201c7b373207514df98b17daa489a8d206b474cdcad455201b8b6efa936b0d": "0x040300000002000000000000000000000000000000000a506f6c6b61736166650a506f6c6b61736166651768747470733a2f2f706f6c6b61736166652e78797a2f001468656c6c6f40706f6c6b61736166652e78797a00000b40506f6c6b6153616665000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148ec37554e12de10ab08b555a5a3b2725e01ba15eb40aa32dc5b781532854b797808ed45e752b047c": "0x040300000002000000000000000000000000000000000742656e64616b0000001862656e64616b7374616b696e6740676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148eccee83ae40fd3db6167d010df1931f8440f02a199bb30d7d00440db494ead7a66c9d564202aa35": "0x00000000000000000000000000000000000f526f636b58204f6666696369616c1d416c747374616b6520546563686e6f6c6f6779205074652e204c74641768747470733a2f2f7777772e726f636b782e636f6d2f0012696e717569727940726f636b782e636f6d00001040726f636b785f6f6666696369616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148ed3cdd8f85271b0a215a54a9ad2ae7acb7ce71d6bf95e950bb20323b4d8153ba0059e20b5fd2923": "0x0400000000020000000000000000000000000000000016456e646572627920456e7465727461696e6d656e7416456e646572627920456e7465727461696e6d656e742168747470733a2f2f656e6465726279656e7465727461696e6d656e742e636f6d001f7269636b6440656e6465726279656e7465727461696e6d656e742e636f6d00000c40456e6465726279456e74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148ee7853e903b944aec9da8307266c9d21b5c5abb97429930acd24d53fd2f8212c40822322248406c": "0x040300000002000000000000000000000000000000001250617363616c207cc2a0416375726173740000001370617363616c40616375726173742e636f6d00000b49416d50617363616c42000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148f3e547c92a9f976e48cf28184fede6322720d00b8cdd7f8505499460dc19dc36280b430cc160a1c": "0x00000000000000000000000000000000000544475f3100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148fcb6335f1a565451cd6b5692fb62af477d385a940bf36ccb9dd085a20f5e01bfd4904fef461f5e3": "0x0000000000000000000000000000000000046f72650e4f6c6976657220456e6369736f0000196f6c69766572656e6369736f303740676d61696c2e636f6d00001140656e6369736f5f6f6c693136383630000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148fedd41107e5eb65720f2a91be2692cee676ba11405101ec67422a5dbc749c501c607dbfc585c27e": "0x040300000002000000000000000000000000000000000c6368726973676c6f62616c000000166d616c697a65636872697340676d61696c2e636f6d00000e4063687269736d616c697a6533000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148ff4d63ea65c2c7cfe6e0141f1f9c11b8ff9298df8ba96169aee6dfeb3b2e57d27c9ae2b029d6f11": "0x04030000000200000000000000000000000000000000076672616e6a730000001b6672616e6a732e6672616e636973636f40676d61696c2e636f6d00000a406672616e6a733237000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047148ff6163e3cfc08ee04e8b8b6a5c974f15c784fcabd18a4bf8fba4c5ade55210aa159c106009dfd3a": "0x00000000000000000000000000000000000953686f6b656e6e7900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714900a0b9f77ddf8434c108ee4a7fc5ad38e5af9d408486d66055a9a7893c644789d081040cfecff61": "0x04010000000200000000000000000000000000000000104465766f7073204d6167617a696e65184465766f7073204d6167617a696e6520507479204c74641c68747470733a2f2f6465766f70732d6d6167617a696e652e636f6d1c406465766f70732d6d6167617a696e653a6d61747269782e6f72671a6465766f70732e6d6167617a696e6540676d61696c2e636f6d000011406465766f70735f6d6167617a696e65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714908ee65340aa835e9e1389d5ddfa43eddcc269e5b95bc69e72be36c43882da45cde097dd7703a217": "0x04030000000200000000000000000000000000000000114e61636869746f204167656e742331340331340000196e61636869746f2e63727970746f40676d61696c2e636f6d00000c406e61636869746f657468000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149098a1473c99bf7e9a5e3760bdedaa34c256e0b28279d358b68a28cfd5967bd0f8cd5555496fa655": "0x00000000000000000000000000000000000c726567656e65737475726b1b456e6573204d61686d7574204fc49f757a68616e2054c3bc726b00000e656d6f7440676e7379732e696f00000d40726567656e65737475726b000e656e65737475726b233030313300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471490b7f1fb67016ea9caaeb20361e77d9114bdd85dc196c33e15da72f4c28699085c388a3ecaa17f1e": "0x04030000000200000000000000000000000000000000034c56000000124c56406461626c6f636b64616f2e636f6d00000b404b7573616d614e4654000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471490dd85b65e5cd020a40c551ab8f3bd70b9afc4bdc348f71e3403deb9b7dafe384ad536a5b42e7107": "0x0400000000020000000000000000000000000000000007726f64696f6e00001a40726f64696f6e706170613030373a6d61747269782e6f726718726f64696f6e7061706130303740676d61696c2e636f6d00001040526f64696f6e3034373039393331000f726f64696f6e303037233535353300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471490f81dd1ff30118bc44ee45000531bbf1aa4c23540940eb10599e14b4fbed5267c42a0ebf5d09f66": "0x04000000000200000000000000000000000000000000055245504500001140726570653a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149103dcfe925d4e52b2e6d8f0b56e1d493dc573448b702fed2b4318dcc46896527099cf5fc9e0d247": "0x00000000000000000000000000000000000e4c6974656e7472792f7465737400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714912655d634b54e9a086de7162fbfa0b91a67eee94b697646028edcf484ae78fdc0627e7eef1b2247": "0x040300000002000000000000000000000000000000000a53756257616c6c6574000000146167656e744073756277616c6c65742e61707000000e4073756277616c6c6574617070000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714913e194223487e6ad60cfbed680a5afbf2888ea18911a5d10abbc7398f19d4507cced58c8fc0d575": "0x00000000000000000000000000000000000c4865726d65735374617368074865726d6573000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714913e6dae982908ac300192a29bd4659b0f05b2263af3d602ea7960160d9e864228dd2f1697809b67": "0x00000000000000000000000000000000000c526567656e63792d3032301757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149187e9b68a6b4d794ef86b506a3a8023b78b27e079f19ad8d15a31924879678fd610bc2639b2bd81": "0x0403000000020000000000000000000000000000000006566965742e0d416e6856696574205472616e0000187472616e616e6876696574786440676d61696c2e636f6d00000940767472616e3037000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471491d20a44a8276c2a3241222d562ffe7f7a5fd8a4b13bdd035647cce34c6eeaf4ab43e431a3268349": "0x040000000002000000000000000000000000000000001d5061726176657273652054616c69736d616e2050726f706f73616c73000016407265706c67686f73743a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149244fde1a07e17b6ec6fb19f22456336dbcaf2ce4d8da72a8d092f60cd123cc9cb972cc1e42e97dd": "0x00000000000000000000000000000000000a616e6465726d61747400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149253b68a74ca5ffce225f681546f6e540ebb18c2b217652e13abb443aaf5dfc8496ec2b0cb883200": "0x00000000000000000000000000000000000c6f7363617274657275656c196f736361722074657275656c2069207065726164616c74610000156f73636172747031304069636c6f75642e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471492595c8ed770133ebc500ab02f6e42ccd4300f6e68a0eaa60a88a5caf900ef6be81d8ab559e7b939": "0x0403000000020000000000000000000000000000000005416c65730e416c6573204a75726b6f766963000018616c65732e6a75726b6f76696340676d61696c2e636f6d00000e40416c65734a75726b6f766963000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471492856427f3f5a77c7c649a13f87c0d5b9b2b934e867a10304249752e96eebd4fe5ec6ff9fb82c078": "0x00000000000000000000000000000000000b616c6172636f6e736a631b4a75616e204361726c6f7320416c6172636f6e2053616e746f73000017616c6172636f6e2e732e6a6340676d61696c2e636f6d000009406a616c61726332000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149285fa22c885aa248e29df67564eddc299a2aa1cdacbec3c1ae061cf0835c87e5b46b920f7573a77": "0x04000000000300000000000000000000000000000000124a7574746120f09f91a9e2808df09f8ea40e4a7574746120537465696e65720018406a757474613a6d61747269782e7061726974792e696f106a75747461407061726974792e696f00000f406a757474615f737465696e6572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471492a9b8874561dc036464f15335eee136dd7c216b994ea6ac3394eb723590e9e065fd9061e05d0035": "0x0400000000030000000000000000000000000000000004524f4212526f626572742048616265726d65696572001b407270686d656965723a6d61747269782e7061726974792e696f0000000a407270686d65696572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471492c85c6e1d4ad1da20426d3ac752d4b9c92c4c9090fb1115c379982fa665ab6f6914552adedd1a10": "0x04030000000200000000000000000000000000000000064b75646f73000000176b75646f732e706f7274616c40676d61696c2e636f6d00000c4b75646f73506f7274616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471492da06c1a6bb3bdaa0e1c76edd8151d4841665925d01a029fe176eecc24297bf931d594502293159": "0x040300000002000000000000000000000000000000000e537472696e64626572676d616e0e537472696e64626572676d616e000019737472696e64626572676d616e4076656e616c6c2e636f6d00001040737472696e64626572676d616e73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471492f879f180f6a02408b712a589f5cb71cd7094809785ab0a924358d3cb52b27efd4933b6efc14963": "0x000000000000000000000000000000000010446f6e446965676f53616e6368657a0000001c676176696e776f6f6469736d796775727540676d61696c2e636f6d00000f4053616e6368657a43727970746f001140646f6e646965676f73616e6368657a00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471493b1caaf799dd1859bddc94acc7518e753f3ca6710e2777a0dbfc5dd06a67b02a5f731c9621fb36f": "0x000000000000000000000000000000000014456467657472696275746f722053756244414f00001a40456467657472696275746f72733a6d61747269782e6f726721656467657472696275746f72734065646765776172652e636f6d6d756e69747900000f40456467657472696275746f7273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471493c499446dff130948b985dae79adcb9becca19ca2eba2aada6416020dc01abd59f8b4419cd8a002": "0x040300000002000000000000000000000000000000000b4d41442043524950544f000000136163736579696e6340676d61696c2e636f6d00000b406d617474756e636869000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471493d0e54cd18f21273eabb0f32cb0695fedb8d25f6299fe51f90b1e26ed858c1064aeeea0977c1e67": "0x040100000002000000000000000000000000000000000b4e6f727468776f6f6473001768747470733a2f2f6e6f727468776f6f64732e636f2f0016737570706f7274406e6f727468776f6f64732e636f00000d404e6f6178656e6565646564000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149405477f74a11219ba1c4070dc40c8c4f8619ad619382655a6f9764f454954457406f10a87ae164e": "0x040000000003000000000000000000000000000000000850617261646f78001568747470733a2f2f706172616e6f6465732e696f164070617261646f7878783a6d61747269782e6f72671470617261646f78787840676d61696c2e636f6d00000b40506172614e6f646573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714943fc2ac54f4c37ccc10dd1946b0fc65c8993ff7f47052713e9aa4b1cb72c913bd397c34adf4f949": "0x0400000000020000000000000000000000000000000009416264756c62656500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149446885b0b18680ae8e0ac0aa68a0c138a3aa84813f0accae4ed7d6ae4b4905495026f16eda4e069": "0x04000000000200000000000000000000000000000000097370617a636f696e000000137370617a636f696e40676d61696c2e636f6d000008407370617a7674000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471494770065de5e75f5d0e903b51697fa10a7f8c7e5c750fb3c2f90424b749be8b6c0f0b120f5d7277c": "0x00000000000000000000000000000000000b4441524b464f5245535400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714949975f6dd9018661c9855eb44b83e5916954e5dc4801b8e7333bffb9f5788a1f7245811421b600b": "0x04030000000200000000000000000000000000000000094744617661737369124769616e6c7569676920446176617373690000186769616e6c75696769406b796c69782e66696e616e636500000f6578717569736974757339343134000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471494af0873e9a60a1b025b4abda214f95f673f89f6d7139894f0b9486cb3f28e5c56956288b4876c69": "0x04000000000200000000000000000000000000000000066a756c696f0000001868694077686f69736a756c696f73616e746f732e636f6d00000d406a756c696f73616e746f73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471494bfb37d43938cba9cdbb4a6e2756511dfe6028fe26c3ca3fb6123753e7e222978b5183581cc0c75": "0x0000000000000000000000000000000000174765726172646f2050697a6172726f2047656f7267650f6765726172646f2067656f72676500001f6765726172646f70697a6172726f67656f72676540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471494db11932c4b24d4224eba2123974db88ab28f55b7dd4f8f330013cb431d5a9c121b3384bea81020": "0x040300000002000000000000000000000000000000000646656c69780646656c697800001666656c697834737761726d40676d61696c2e636f6d00000f40436174616c797374537761726d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471494ed5c295f9bfa2588f28e17671ba1808d7b02cd3caaf80113066a467127666f4d80afc50bfbc127": "0x04000000000200000000000000000000000000000000067a6f656d63000012407a6f656d633a6d61747269782e6f72670000000a407a6f656d63666f78000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471495034dce89216a0306a7f8d0b05138414746c1fd2c03695ada9d503e888977e0763fc427f50f7156": "0x0400000000020000000000000000000000000000000013457567656e696f2047696f76616e617264690000001d657567656e696f2e67696f76616e61726469406d6f6f6e69612e6974000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149508d34e1b2941b94a4081d3f77f3ae9304f78983183b8f015dacb620ce7eb0444e733b85422d931": "0x040000000002000000000000000000000000000000000a70616e6472657339351d5061626c6f20416e6472c3a97320446f7261646f205375c3a172657a1868747470733a2f2f7061626c6f646f7261646f2e636f6d174070616e6472657339353a626c6f7175652e7465616d15686f6c61407061626c6f646f7261646f2e636f6d00000b4070616e6472657339350a70616e6472657339350a70616e64726573393500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714953150ed314b45c2b2b3d7f25a51033a9fd84198e78c72fac4e9f23fc149d82200e0b423f1418671": "0x00000000000000000000000000000000000b507572706c65546f616400000017707572706c65746f6164303940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714955ba6c28b709d2f7a0a276b0053aa5d6291575d123aceb4efbc34b98102b967590c008b4cf54872": "0x0403000000020000000000000000000000000000000008427261696c6c650f427261696c6c65204167656e63791568747470733a2f2f627261696c6c652e7774662f0014636f6e7461637440627261696c6c652e77746600000d40627261696c6c655f777466000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471495b0575164a947673676eca79c8b6ff6a5b544274fc7d1e38b38ca8211ae59fd73b8c1073f64fa27": "0x0400000000020000000000000000000000000000000016f09f97bb20416c70696e654e6f64657320f09f97bb000016406b72697368313636323a6d61747269782e6f726716616c70696e656e6f6465734070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471495e5354b81f0f460da9f7fd3d9612a68d2ead69dde53297b172b7db514d0d261e7c5be987df7f32a": "0x040000000002000000000000000000000000000000001453696b207c2063726966666572656e742e646500001540646576305f73696b3a6d61747269782e6f72671a73696d6f6e2e6b726175734063726966666572656e742e646500000a40646576305f73696b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471495ffe83b7e4e53745ac11043321b5292fe5c644feaf53ff448ca444a527d51f380c799ed16565c89": "0x040100000002000000000000000000000000000000000b58656e6f70686f6269610000001f77656c746c6963682d696e6e696e67732e30684069636c6f75642e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471496841c11a7beb5d5c2fe15c1d94dc26f396102556264826f7a197ba897ae75aa5e52d220153cb679": "0x0403000000020000000000000000000000000000000012736f6d656b696e646f6667616d656465760000001c736f6d656b696e646f6667616d6564657640676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471496dad83abc32ee034c260b3a7196660071ca1b27e79bf234cb7efaa125300a696a0a42ee686b1734": "0x040100000002000000000000000000000000000000000664616b6b6b0000124064616b6b6b3a6d61747269782e6f72671464616b2e6c696e757840676d61696c2e636f6d00000840646167696465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714972228f5d624edf372e9d0af25b9e826acc65a8d4233ca1149d57eff4a7d78e620889cd37174c57d": "0x00000000000000000000000000000000001c313335204361706974616c20496e636f72706f726174656420303210416e74686f6e7920457566656d696f0f68747470733a2f2f3133352e696f000b616365403133352e696f00000f407265616c4143457566656d696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149730aaa91834c4fe403e9da23891aeb29efc4ba2785edc0ea8f4bc67f0f1942dfb5295191373a47b": "0x040300000002000000000000000000000000000000000c546f726f734167656e637900000012696e666f40746f726f732e6167656e637900000d40746f726f736167656e6379000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471497389e32ef925ccd6c0197856b35c7f631f5aa8d9cfd83f7e75202b0d821e67d2f344e4e4b5fc10f": "0x0801000000020300000002000000000000000000000000000000000d506f6c6b617373656d626c790d506f6c6b617373656d626c791968747470733a2f2f706f6c6b617373656d626c792e696f2f001668656c6c6f40706f6c6b617373656d626c792e696f00000a40706f6c6b5f676f76000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471497caee6b287aa129f6cb33068e0f82887ed35a5e17e77021cd796902e5e2939c325b4f92d4f3957c": "0x0000000000000000000000000000000000114a4f49452028657874656e73696f6e290de68891e698afe9b8ade593a51068747470733a2f2f6a6f69652e696f14406a6f69656375693a6d61747269782e6f72670d6a6f6965634071712e636f6d000009406a6f6965637569000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471497f3d4c5777373935275676a7d8983923233084beb6faac0ea3773730c8abe70eed4795ee2815441": "0x04030000000200000000000000000000000000000000165279616e2044696e68207c2053756257616c6c65740a5279616e2044696e680000137279616e4073756277616c6c65742e61707000000b405279616e44696e6838000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471498426f2bcd57c422f4c8bc5da2f0d0e2f6e040005f783c023b85a8cb21df5c1f509c144fe1887202": "0x040000000002000000000000000000000000000000000858616c616d75730000144078616c616d75733a6d61747269782e6f726715706f6c6b61646f744078616c616d75732e78797a000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714986ea6df1fdbb2a51c20046a7e79f6a36c1f8f74a8caf1e573c448f7f2ca4d7e75928579ddbc134b": "0x040300000002000000000000000000000000000000000a6c74667363686f656e1d4c756b652054686f6d617320467269656472696368205363686f656e1d68747470733a2f2f706f6c6b6176657273652e636f6d2f406c756b6516406c74667363686f656e3a6d61747269782e6f7267146c74667363686f656e40676d61696c2e636f6d002168747470733a2f2f6769746875622e636f6d2f6c74667363686f656e2e706e670a6c74667363686f656e000a6c74667363686f656e00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714989747ec8e57b3315e7b86bae8d18cd7470cc4c03d47875d002cee651566030fea5312bf0e69562a": "0x0400000000020000000000000000000000000000000010477572755374616b696e67f09f91b3001868747470733a2f2f677572757374616b696e672e636f6d1840677572757374616b696e673a6d61747269782e6f7267167374616b6540677572757374616b696e672e636f6d00000d40477572755374616b696e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471498a44c6de372836c145d6c503d0cf97f4c7725ca773741bd02e1760bfb52e021af5a9f2de283012c": "0x0400000000020000000000000000000000000000000008546869626175740000124074626175743a6d61747269782e6f726700000007407462617574000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471498d18fa62c6f4741589e41f29ab08f2d0e4e4bdf3d1c8a868162c720f9c31e22733f8d633fba901e": "0x04030000000200000000000000000000000000000000064d6f6869740b4d6f68697420426861741968747470733a2f2f6769746875622e636f6d2f6d6263736500126d62637365353040676d61696c2e636f6d000009406d626373653530000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471499004306bcbce5e41a4642fac1519da8a909bba8a1cc4ba6c6d096fac4da4e9a0f5dbe7eae16360a": "0x00000000000000000000000000000000000e6c616e64617320676c6f776e6500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714995d906364498e519ab4ee81530865538477c5f9fa76f57f2058f4d97476d028ccbb4bfe44a0f626": "0x00000000000000000000000000000000001a70686f656e69782d63727970746f2d636f6e74726f6c6c657200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714996b1fd9ab500b524ab52bb8245e545fc6b7861df6cf6a2db175f95c99f6b4b27e8f3bb3e9d10c4b": "0x040000000002000000000000000000000000000000000344460000001464617070666f72636540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714998cd9a07d23d28d50ef3cbba6eefa5127e662a1286c69c3f8cd10aa328d394df9e69919af449b45": "0x000000000000000000000000000000000012496e73696768742046696e616e63652031001368747470733a2f2f676f7374616b652e696f001577656e6a756e6438343940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471499c40e700afa99fd3e9303650f53c8624593711265f153cea49411b1966da5d31142b6b88e721c38": "0x040000000002000000000000000000000000000000000a506f6c6b616e65787400001640706f6c6b616e6578743a6d61747269782e6f72671570726f6772616d40706f6c6b616e6578742e696f00000b40706f6c6b616e657874000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471499d7e43eb6d40f70b6ad8860b2acbb26cf3caaedc7deb8c5d7146ac5bbe77bf34a592a97e377a046": "0x00000000000000000000000000000000000644722e20540e54686f6d61732048616d6d65720000187468656469736375736d616e40776f682e72722e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab870471499eef26ba196a8a8b089dedc24a15308874dc862b035d74f2f7b45cad475d6121a2d944921bbe237": "0x04000000000200000000000000000000000000000000135448454d414e574954484f5554414e414d4500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149a449d74fb4e4f6f80cac1fb723163fc032ac4db84fec2102f6897afa7dac42e379a7d41ad586b5e": "0x04030000000200000000000000000000000000000000094f74746f546f6f740000001e6f746f72696e6f6c6172696e676f6c6f67363640676d61696c2e636f6d00000c404f74746f546f6f743939000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149a8e7db7192318209e634dd70cbdbd492c6874c5613bcb06f1d8dc85eed3051f884c5458e7205655": "0x0403000000020000000000000000000000000000000008416e61746f6c7908416e61746f6c790000176e617a6172616e61746f6c7940676d61696c2e636f6d00000c40373737416e61746f6c79000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149aadb0de0e3acc6ad0e409282db1aea4d91486a122492e7a130f681b7daa71cfd4db3db086ea7d65": "0x040300000002000000000000000000000000000000000a4475636b446567656e0f4461766964204c656f6e617264690000136461766964406a6f6273746173682e78797a00000a6475636b646567656e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149acc963ace4fd466a228a70c2496ae500d79de266f9c759918256444c352d76bcc4a0c32e2dff716": "0x040300000002000000000000000000000000000000000b43727970746f52616e6b0e43727970746f52616e6b2e696f000011666d4063727970746f72616e6b2e696f00000e43727970746f52616e6b5f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149b2f4bfc1eecbbb29003fe9042a2c3433ecbddf6b360c3f17993544f830dc75f73d9a13a8757bf26": "0x0403000000020000000000000000000000000000000006736875766f06536875766f0000166a616d62646b776c73333340676d61696c2e636f6d00000a40536875766f427463000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149bc7b64471beb6caa4a902266727d6859bbd3cd585e9f64460ed6289f95bd851bd441ff57573ab64": "0x00000000000000000000000000000000000b4461793163686565727a00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149bd6b99bd8180aace8c7ad65c15fa3ba64424a61b177382a0c5468135aecca9ca454f5e7ce4d305b": "0x040100000002000000000000000000000000000000001043503238372d434c4f554457414c4b001168747470733a2f2f637030782e636f6d1640696c6c6c65667234753a6d61747269782e6f726714696c6c6c656672347540676d61696c2e636f6d00000c406b61706c616e736b7931000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149bfeb795391ff04e92126d23f40d0707321f495a7ca4b97a86f2282cdf5229a4b315793e51dea246": "0x040300000002000000000000000000000000000000001643687269737469616e20436861696e616c797369731e43687269737469616e204d656e6461202d20436861696e616c7973697300002063687269737469616e2e6d656e646140636861696e616c797369732e636f6d00000840636d656e6461000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149c1d0530fd26bd0a347bf57a51b4924e76624612bcd7bbd269b3079fbcc4a2f72a2fcac55056286c": "0x04030000000200000000000000000000000000000000054e6f6f72104e6f6f72204d6f68616d6d6564204200001d6e6f6f726d6f68616d6d6564624070726f746f6e6d61696c2e636f6d0000114030786e6f6f726d6f68616d6d656462000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149c21b6ab44c00eb3127a30e486492921e58f2564b36ab1ca21ff630672f0e76920edd601f8f2b89a": "0x0401000000020000000000000000000000000000000017506f6c6b61646f742e70726f202d205265616c676172001568747470733a2f2f706f6c6b61646f742e70726f14407265616c6761723a6d61747269782e6f72671368656c6c6f40706f6c6b61646f742e70726f00000d4070726f706f6c6b61646f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149c2d3779d2f3e4d20ae756ebc2b9527cb5634924b518af5a0edbedcf16e7d581648d770e4cb27844": "0x00000000000000000000000000000000000e54726962652f537461636b55700f54726962657820507465204c74641568747470733a2f2f737461636b75702e6465762f00136e6963686f6c6173407472696265782e636f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149c54b3d06134b2da7c2754f11fe0d5e6b3c2174eacd1fe36d2738bfbefb541fb4f56eb298dc1491a": "0x04000000000200000000000000000000000000000000064f4e44494e000012406f6e64696e3a6d61747269782e6f7267136f6e64696e37373740676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149c6b526cb73c7a0f38e4240801ab3b93aa2312497116b9ced3dfaea754a6e18c45f4605ee3af490d": "0x0000000000000000000000000000000000075374617269740c4a616d6965204368656e671368747470733a2f2f7374617269742e6d652f18406a616d69652e6368656e673a6d61747269782e6f7267157374617269743139393240676d61696c2e636f6d00000c4073746172697431393932000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149c74feb394678e854ae840a749725558ddb56d21b296ea057d8295ba7c7ded83fa0285afa24cc600": "0x00000000000000000000000000000000000f506f6c6b61646f74204a6170616e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149c7aff229a5b7073468ae00d6e49251a2c0809df7e3283dd05fe8232e010734b0de316c5ab07431f": "0x040000000002000000000000000000000000000000001c4b6172696d207c2050617269747920496e66726120262044617461000011406b6172696d3a7061726974792e696f0000000b404b6172696d4a444441000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149c9cab497b19e1dad837b7a3a768b47b1210fc36f3157793bd95820092148f973e5f297a05412e5a": "0x04030000000200000000000000000000000000000000076b756d6173690f45766572677265656e20207961770000126f6c696337343740676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149cafa1da45543da296c952e57d77a6acc899f39e7f043839309cc8527b089c5ca76d24c2b63ebb4b": "0x04030000000200000000000000000000000000000000096a736c7573736572154a616d65732043616c76696e20536c75737365721868747470733a2f2f7777772e6b75726b756d612e636f2f15406a736c75737365723a6d61747269782e6f7267146a736c7573736572406b75726b756d612e636f00000a406a736c757373657200096a736c757373657200", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149cdf78c3a20b94bb2a86561dc118ec7d56488f5615de6b325f6dd97e6801aa8b3aa10aa687e8347a": "0x04010000000200000000000000000000000000000000064b6f74616c001168747470733a2f2f6b6f74616c2e636f0011737570706f7274406b6f74616c2e636f000009406b6f74616c636f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149d04e8142e8ca463bc94245f476d6d2f02c5c8c12a5914444a4b22fa38f2107025b1a092b376fd79": "0x0403000000020000000000000000000000000000000008416e6e79306e6e1c4672616e636973636f2056616c656e74696d2043617374696c686f00001a6672616e636973636f616e6e796f6e40676d61696c2e636f6d000008416e6e79306e6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149d18dbe99b9ed7df4f4097d2ee14bfbac178efc25e50fda49b96b18d6f48958f179041a00c126ec8": "0x00000000000000000000000000000000000c526567656e63792d3030311757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149d263a3ca965563a56b8d05d9dd7fef64c1cac6d69a674c5bea54c270a48170c6abebef698337221": "0x040300000002000000000000000000000000000000000b4e69636fe29aa1efb88f104e69636f6c61732041726576616c6f0000196e69636f6c61734076656c6f636974796c6162732e6f726700000d406e69636f6c617265733238000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149d3d32c9a2a70e509a6d8b9ce3536600ca5e3235162058682c565f1b7c071d00bff49acfc7489e3f": "0x000000000000000000000000000000000015506172616c6c656c2046696e616e6365202d2033001568747470733a2f2f706172616c6c656c2e66692f000000001f68747470733a2f2f747769747465722e636f6d2f506172616c6c656c4669000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149d3e6d4e7e999db42ecef72bcacac7b6f05920ea3593b27d3a8ae8ec55f4925eaf1caccdb57e5225": "0x040100000002000000000000000000000000000000000f4d584320466f756e646174696f6e154d584320466f756e646174696f6e2067476d62481468747470733a2f2f7777772e6d78632e6f7267000e68656c6c6f406d78632e6f726700000f404d5843666f756e646174696f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149d417fe5c3fd7281028b2fce5e73610790f841442376a9bfd38c2494cc7831c5481db451be550d5c": "0x040300000002000000000000000000000000000000000e4e494b48494c2052414e4a414e0e4e494b48494c2052414e4a414e0000176e696b6c6162683831312b3640676d61696c2e636f6d0000086e696b6c616268000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149d430d0bdbcfdbc6f0de782e8bad3c663be60812f0a2ac63464f5da3ec448c73334c07d71ef27f2c": "0x040100000002000000000000000000000000000000000d5374616b6572205370616365001568747470733a2f2f7374616b65722e73706163651740676e6f737369656e6c693a6d61747269782e6f72671368656c6c6f407374616b65722e737061636500000d405374616b65725370616365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149d5c0a5ff6bacb086ebe9137ac7ac32374b6459c8633314440b21be4356a718a408b10c9ab520e70": "0x040000000002000000000000000000000000000000000b636c6f636b636861696e00001740636c6f636b636861696e3a6d61747269782e6f726717636c6f636b636861696e687140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149d8014dd68872e0de21727312b2b7ce579dc0f82f129d4edecbcf00abca607d73ccf16e8352cc777": "0x040000000002000000000000000000000000000000000e444f5a454e4f4445532e434f4d00001240646f7a656e3a6d61747269782e6f726716636f6e7461637440646f7a656e6f6465732e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149d87ee52b783a152bed497470a04ca4c13caccd69c7827e3ddc64473fd2d7c5d496c71061f452b05": "0x040100000002000000000000000000000000000000000b72656470656e6775696e0000174072656470656e6775696e3a6d61747269782e6f72671a72336470336e6775696e4070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149d967298cdbfa73f664a2b3e8bd3b4ab1878c278d5b10482bd2b590ffeef1552e3c78fe045045a7f": "0x0403000000020000000000000000000000000000000010526f68697420536172706f7464617210526f68697420536172706f7464617200001b726f6869376e732b706f6c6b61646f7440676d61696c2e636f6d00000f726f686974736172706f74646172000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149da3e68b063e9e3fee046115156933810cf762fcb5928e5a750b25eb9cd7932f425972b94f17960a": "0x04010000000200000000000000000000000000000000076b6c65766572001268747470733a2f2f6b6c657665722e696f00106c6f756973406b6c657665722e696f00000b406b6c657665725f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149da48123ab54b499c6477bfd57c12587b1075a80d944c7829784eed61a5c8b8255817e1d62d1070e": "0x040100000002000000000000000000000000000000000d4b495241205374616b696e670e4b69726120436f7265204a534314687474703a2f2f6b697261636f72652e636f6d15406b697261636f72653a6d61747269782e6f726716706172746e657273406b697261636f72652e636f6d00000b406b6972615f636f7265000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149db5c1943eccca8bc422b6d1cc9c0e9ad3e29b03134c8f1e45210f3f404ec3daa05f3aefe8257205": "0x040300000002000000000000000000000000000000000e5468652044617070204c69737400000018636f6e6e65637440746865646170706c6973742e636f6d00000c746865646170706c697374000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149dcc277753735b96e04ca25cf1cbc516ed1c138492a7f2e606f60c5a9ac96cb405eaa3914fd12973": "0x040000000002000000000000000000000000000000000b626c6f636b736361706500000013626c6f636b7363617065406d7761792e696f00000f40426c6f636b73636170654c6162000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149def61665adc4306fec2c5ffb46bfe1dbc7935f493a5c9323520878aad74b5a32276a7a268abdb32": "0x04000000000200000000000000000000000000000000076e616667363900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149e21ce449387a9e130d6f231bc79dc0b10d4b09c38f9deada991ddf9234054b5bf8791a576c18a50": "0x00000000000000000000000000000000000d6320f09fa59e206c2065202100000013637874726f6e636f40676d61696c2e636f6d00000c40636f6c65735f5f626167000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149e5b705cbdcc776ab4410d33f13c053dca87be657a0ae3cc87655baf43f7efdd454ff74e3a9d8a2f": "0x0400000000020000000000000000000000000000000014f09f98bb205374616b65204b617420f09f98bb00001340666d6f6e7a613a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149e6f51a51fd3dacb6a7d56a801015edae2ebdeafb80bd4191554965942141024a2014ea2a11c8477": "0x0400000000020000000000000000000000000000000007416e647265690d416e647265692053616e6475000018616e647265692d6d696861696c407061726974792e696f0000000973616e647265696d0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149e8bb46f23ea823584c5a40e2f5f6e6740ad9656099af2946938802c6f42d42dd6d40d7930a20e2b": "0x00000000000000000000000000000000000c7371756972726531646f74000000127777617264363640676d61696c2e636f6d00000f407371756972726531726164696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149ea70d098f006161264319ed6a0895c04112917fc9bdc0771f4a4773aae014a99d25bbe06fa1057a": "0x0401000000020000000000000000000000000000000014f09f909120484f444c2e4641524d20f09f9091001268747470733a2f2f686f646c2e6661726d1640686f646c5f6661726d3a6d61747269782e6f72671068656c6c6f40686f646c2e6661726d00000b40686f646c5f6661726d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149eb58bb1718cc7793e44001b8695199fa100fff9057e6e086eaf496a15bd82b71d2a66ac148fc46a": "0x04030000000200000000000000000000000000000000064a6f6e48510e4a6f6e20486f6c6d71756973740000186a6f6e68716175646974696e6740676d61696c2e636f6d0000076a6f6e5f6871000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149ebab134ded1549916a96314a4b60b23596683eebea29988aec9fae81a7b8d44887fd4c07a70981e": "0x040300000002000000000000000000000000000000000d57696e746572737072696e670000001477696e7465726b736d4070726f746f6e2e6d6500000b4077696e7465726b736d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149ed60c0f92e374f686e4a0805aa9cd02bf99cfe96bb9b84d1727949eca4b740bca8c1701ae931406": "0x0800000000020100000002000000000000000000000000000000000d537469722d4a502d426c75650d537469722d4a502d426c75651568747470733a2f2f737469722e6e6574776f726b0012696e666f40737469722e6e6574776f726b00000e40737469725f6e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149ef4be7403d4d6000c20e1797f6f0e239f7784c6a63a6c319836d9ecb89e1f32d05f0827bb1c7f64": "0x00000000000000000000000000000000001d5a756d69746f7720285370616e697368204e6577736c657474657229001568747470733a2f2f7a756d69746f772e636f6d2f0013686563746f72407a756d69746f772e636f6d000009407a756d69746f77000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149f053c2746e722456610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38": "0x040000000002000000000000000000000000000000000e5374616b65776f726c642e696f001668747470733a2f2f7374616b65776f726c642e696f17407374616b65776f726c643a6d61747269782e6f726713696e666f407374616b65776f726c642e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149f1252e4c39b7f7710a3bcbb259f08e2f9bfd79197e1a63ea44f47c7790139dcdc1faa9567130f30": "0x04030000000200000000000000000000000000000000055649585900000017736175726162686772696e6440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149f1d97820c576865f25418311f6f3b70b3ebe74fc714aba44e177d104b6c2defb27f9b9936da17b9": "0x040300000002000000000000000000000000000000000d4d6f7361696320436861696e001768747470733a2f2f6d6f73616963636861696e2e696f000000000d406d6f73616963636861696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149f1f101b199c8938e69cc993ca5061e9991dfa141144902af69c09a63077294edbff30f6876acadf": "0x040000000002000000000000000000000000000000001b506f6c6b61646f742048756e6761727920436f6d6d756e697479001568747470733a2f2f72622e67792f3736796f7865001a706f6c6b61646f7468756e676172794070726f746f6e2e6d6500001140506f6c6b61646f7448756e67617279000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149f29f67769840c143eeaa160b12b6e319d7f6b2bc34cc2a402dc845cea0bf7b959b6917ef1568078": "0x08000000000201000000020000000000000000000000000000000011646f742e7374616b6572732e7a6f6e6500000011646f74407374616b6572732e7a6f6e65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149f3937cb0047bfe065de063644e59d7eb67ac8954c00df696957f7422bd64e57ffe08eff305f4e2b": "0x040300000002000000000000000000000000000000000e43727970746f706f6c6974616e1243727970746f706f6c6974616e204c4c431a68747470733a2f2f63727970746f706f6c6974616e2e636f6d001a636f6e746163744063727970746f706f6c6974616e2e636f6d00000e4043504f6666696369616c7478000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149f749b777c55719970584fa4834e2a822dbfeda69c24bbe1ffce611be40ec0fb13dac0ccca6bd077": "0x04000000000200000000000000000000000000000000107461736b6f6f682028506c61736d29000017407461736b5f706c61736d3a6d61747269782e6f7267167461736b6f6f682e757a6940676d61696c2e636f6d000009407461736b6f6f68000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149f8acf5eb9cf86b918bed2613c80d8905c60c4f1b55d262ae0fcebbafbb2b9f60068d26db6801206": "0x00000000000000000000000000000000001c313335204341504954414c20494e434f52504f524154454420303410416e74686f6e7920457566656d696f0f68747470733a2f2f3133352e696f000b616365403133352e696f00000f407265616c4143457566656d696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149fdd3c97960b9cbce12bea7fcd0ef144e784045ab007184e56f1dbea9dde0f496086946206911be9": "0x040300000002000000000000000000000000000000000e4d616e64616c6120436861696e00000015696e666f406d616e64616c61636861696e2e696f00000e404d616e64616c61436861696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149fe12e720a82efc1fcdafa5a5ede1d56189d95430ba0125029978fea782a3f5480606ab1a2b1466f": "0x00000000000000000000000000000000000b6c65697465725f534e490e416e64726561204c65697465721c68747470733a2f2f736f7665726569676e6e61747572652e636f6d001b616e6472656140736f7665726569676e6e61747572652e636f6d00000f40616e647265615f6c6569746572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149fef0c4e3ed2df62246fd50494a2ad83cc5a5c8dee9db659ffb78a5b04d271de44f0a86f334adf2f": "0x04030000000200000000000000000000000000000000144c69666520776974686f7574207374726573730e4d617274696e6120476c69626f0000186d617274696e612e676c69626f407961686f6f2e636f6d00000e40476c69626f4d617274696e61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab87047149ffb07437f7fb055b4351de5a0c28c5d9183126191dfbe2a0a9a709988e37727ec5d41594c17ab59": "0x00000000000000000000000000000000000850617274796679001768747470733a2f2f7777772e706172747966792e65750010696e666f40706172747966792e6575000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a026c6eb8dc072361858ef0a9f0778513333b5d761cd65e3e3e8fcc58c980fa2c4efe9ed03382c7b": "0x00000000000000000000000000000000000b5468656f20444f542031001d68747470733a2f2f7777772e7468656f6a616d696c6c65722e636f6d000000000e407468656f6a616d696c6c6572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a02eb03d19a027d89608a774c479dd1908bb33e1f18529418c3b6d5e19f956887f892ca2c0902237": "0x040300000002000000000000000000000000000000000b4f4720547261636b65720000001763687269735f7061703840686f746d61696c2e636f6d00000c406f675f747261636b6572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a0425165102d0e25091bb84ac5b08adf128e855e1cb079ed594be42af19d09d680dae982ac209ffb": "0x040000000002000000000000000000000000000000000c48616e647943727970746f001768747470733a2f2f68616e647963727970746f2e696f184068616e647963727970746f3a6d61747269782e6f726714696e666f4068616e647963727970746f2e696f00000d4048616e647963727970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a04f3d43f3e99aad8c00226393a2a05d5d65bf7590e3c091feb897f8303a7de52cd637106dee2b37": "0x040300000002000000000000000000000000000000000b7068696c6f6e6961726513547567756c6475722042616967616c6d616100001474676c64723035313140676d61696c2e636f6d000010456c696f74426c6f636b466f726765000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a06e6b0fa55b7aa4d00e1d7c3523a41757d5698f367496356c8c7c3613f3ca796feb3491899a7b16": "0x040300000002000000000000000000000000000000000c446f7420416d706c6966790e416b65657261204d634361696e0000166b657261646d636361696e407961686f6f2e636f6d00000c40446f74416d706c696679000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a07b7c28fde24902742a3196b00c55823f0923395fc97bbba54268dfb0c04a22eb014dcf7afd2465": "0x0403000000020000000000000000000000000000000019496e66696e6974792057616c6c6574204f6666696369616c0000001a706172746e657240696e66696e69747977616c6c65742e696f00001040496e66696e69747977414c4c4554000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a097d201c62d3578f486ec0728d4c1ce1b6dbca881396f3a3bb9f6e890572a400d3064423f889747": "0x0000000000000000000000000000000000135761726c6f636b456e74657270726973657300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a09af68755800aad94ef44028b5c2649905e23e0abe33d4542688a2ed40bcc0f169f0d535816f43e": "0x0000000000000000000000000000000000094e656f4e756d6973001668747470733a2f2f6e656f6e756d69732e636f6d2f0012696e666f406e656f6e756d69732e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a09f49890c6bdef1acbfbc894060d93d772850a601b3f4b69d02c8026b2e7d99433b4067feabeb29": "0x040000000002000000000000000000000000000000000f4a756c69616e6120436162657a610000001b6a756c69616e61636162657a61407961686f6f2e636f6d2e6272000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a0a010fabd2edd968a59a7a725d58dd2a2e3599dd3195c72dba5c8a6f701543eb9aaec7712dc137b": "0x040300000002000000000000000000000000000000000d42697363686f66665f534e491c536f7665726569676e204e617475726520496e69746961746976651d68747470733a2f2f736f7665726569676e6e61747572652e636f6d2f001e636174686572696e6540736f7665726569676e6e61747572652e636f6d00000f406361745f696e5f6265726c696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a0b5a3940af12c68a3483f31027939520c2bf25b1c7b9c495a442dadc10c393336d5a33951af31c8": "0x0400000000020000000000000000000000000000000012646563656e74657265642e73747564696f0000001a6f70656e676f7640646563656e74657265642e73747564696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a0b881b0063a363a02bf32e061073c44300056b416cd66a4fde1e6c120dbc0089bb65134f5693a3b": "0x040000000002000000000000000000000000000000000a6d7564646c6562656500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a107a21bb039f18f3b746c85a1ee00a6b0db9065182d8787c587cf843018c421a095a18b137828e1": "0x00000000000000000000000000000000000e4566696e697479202845464929084566696e6974791468747470733a2f2f6566696e6974792e696f2f000000000740656e6a696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a109a005bbf30981660ee2e14416b8a688b7de8db55c6dd3a2955ade58adf88156156969f584851a": "0x0000000000000000000000000000000000086a61636b6f6f6e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a11241f2be748c8c5a56c4e36507ff2f3efe7796aade67c1e62c5c013d88f7e9f9452ae9d1e7a41e": "0x0000000000000000000000000000000000064f7361617400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a1139026a8f4e1487447aeb48d61f3ecfcd98a44cf81cedafe80db40a95f80671b01e7c0fa531327": "0x040000000002000000000000000000000000000000001df09f8cb2f09f8cb3506c616e7420412054726565f09f8cb3f09f8cb200001940706c616e742d612d747265653a6d61747269782e6f72670f6c6a7564766140747574612e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a11a320b9a8feb01f857311106c8d7b0daf6e096db9a0d759b52403e439ab23fd6559780a8b1c803": "0x04000000000200000000000000000000000000000000095374616d70656465000000197374616d7065646563727970746f40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a13589ec6bd328c6beec9afdb165a47264ed88e3fcc907da93142ed4e0578578fa44ab2cbb14170e": "0x040300000002000000000000000000000000000000000e4b796c69782046696e616e63650f4b796c6978204c616273204c4c43000013696e666f406b796c69782e66696e616e636500000d4b796c697846696e616e6365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a16e9742ca98244e0cd1574cf7fc649a8571d82427b42732e83bcd62f0e3192e92f202ca06f0d53c": "0x040100000002000000000000000000000000000000000662656b6b610d41796f6d6964652042616a6f0000196f6c7577617368696e6162616a6f40676d61696c2e636f6d00000e4061796f6d6964655f62616a6f000862656b6b6b616100", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a17e0d5970ac01707fceddeeb78dc1b3c9b8fff33b20fe9f7600edc6eaf21c8aedcd2451301492f3": "0x00000000000000000000000000000000000c526567656e63792d3031361757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a19b37e844707d72307183930b2264c5165f4a210a99520c5f1672b0413d57769fabc19e6866fb25": "0x0400000000020000000000000000000000000000000006537a65676f0d53657267656a2053616b616300001773616b6163737a657267656a40676d61696c2e636f6d00000d4053616b616353657267656a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a19bbfd9c78f6c5fba9db007fd692b3d1aa851ebc27e880f6f5848be07aa84cd16862c4ad3d86e1f": "0x04010000000200000000000000000000000000000000096d696b6562206364000000136d696b654063756c74757265646f742e696f00000e406d696b6562333030306e7963000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a1c377baa224441cfe5d84e3593dcbacb0a489b98a8be3d9e68af5d38dde584c93689e7732c7d40e": "0x0000000000000000000000000000000000084a656473616461084a6564736164611368747470733a2f2f6a6564736164612e696f00176a6564736164612e696f406f75746c6f6f6b2e636f6d00000c406a6564736164612e696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a20a8de9dde43fecb463a68a6b0a2d857dd0b11702d1d6a3b1b07ec76fbabaf807fdb545d62c9cde": "0x04030000000200000000000000000000000000000000054970706f134e677579656e2054686169205068756f6e67000017746861697068756f6e676e3140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a20d0b846e3d907e00555ee596210f641d2d48b9a1e2b258aead3fe8de4e973bcaf5f24674044367": "0x040000000002000000000000000000000000000000000842696754756e610000144074756e616269673a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a215c23d3f30e28acc4fac060bf7e92c94836df38993b1851e3c2a9728335340fff42ded89ea2326": "0x040300000002000000000000000000000000000000001e416e64726561202d2048656164206f662033546563682053747564696f10416e647265612056656e6472616d6500001d6465762e616e6472656176656e6472616d6540676d61696c2e636f6d00000e4076656e6472616d655f616e64000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a21fc86d22b382fcc8f54a0dcd5310e3d4d099ab7972a5fb6ca284666651d4302459c92de4efaf2a": "0x040300000002000000000000000000000000000000001441637572617374204173736f63696174696f6e0000000f686940616375726173742e636f6d00000841637572617374000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a250353c7dc8311b99f2f82db75c3af6484741f264039ff2e24e4ae7096516dae4ca77efd0da2d13": "0x00000000000000000000000000000000000c526567656e63792d3030361757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a30ff791ba0dd28618c7f5a8530d6aafc1b191156294a9e27bb674128607896f3fd5914282fb196d": "0x04010000000200000000000000000000000000000000046b6d6c104b616d696c2053616c616b686965761168747470733a2f2f716472766d2e696f14406b616d696c73613a6d61747269782e6f72670b6b40716472766d2e696f00000c406b616d696c5f61626979000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a337657934f576d5e2d126c3dffe301385f3d6da21756147d8f58040a9021e4196a89fffa2800a48": "0x0400000000020000000000000000000000000000000003416c00001a40616c6973746169723a776562332e666f756e646174696f6e00000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a3b0c3cd1f399efd62f1e96782eec4d59ff854b9fa623984b80decebea69292fa92659093421e120": "0x04030000000200000000000000000000000000000000084b687564656a610d4b687564656a61204b68616e00001c6b687564656a615f7368616862617a40686f746d61696c2e636f6d00000a404b4b687564656a61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a3e66c1866cb9999f4fa98b7ec05e11f0f3c5cef7a750e22acccd75cefcfa72c00b5af6346e2e47a": "0x04030000000200000000000000000000000000000000086b6f7065626f79124c6f72656e7a6f2047696f76656e616c690000126b6f7065626f7940676d61696c2e636f6d0000086b6f7065626f79000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a3f67b4c0527d154dc3aee12519c19be02628b0f808bddda9aed564027ad809cd392c13e9b924b65": "0x040000000002000000000000000000000000000000000d504f4c4b414348552e434f4d001568747470733a2f2f706f6c6b616368752e636f6d1440736f6e676875613a6d61747269782e6f72671368656c6c6f40706f6c6b616368752e636f6d00000b40706f6c6b615f636875000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a3f97862b7d49be68cd2c9b46e07c20f2263540b993c2b25b3cb995ab873314ac10e8d5f1f50a55e": "0x04010000000200000000000000000000000000000000154554484943414c2056414c494441544f52532030134554484943414c2056414c494441544f525321687474703a2f2f7777772e6574686963616c76616c696461746f72732e636f6d17406576616c7561746f72733a6d61747269782e6f72671d6574686963616c2e76616c696461746f727340676d61696c2e636f6d00000d404556616c696461746f7273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a402b003cd008ec5ba38a1142964f0e049d842c9a932ce34f21017dd4e674aeb8a5bd67553f1846c": "0x000000000000000000000000000000000008426966726f737408426966726f73741368747470733a2f2f626966726f73742e696f001168656c6c6f40626966726f73742e696f00000940426966726f7374000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a40acfeba897a3054831c976778f05db5b115be6b85ed7ba7099de3ec01b096c8a91d6074daf2746": "0x0400000000020000000000000000000000000000000004414d4200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a42915ffbd1e4d678e156836bd7dc0639ea54540eb6ec55aec2a3793876208bf5d71ff89eb746a07": "0x04030000000200000000000000000000000000000000064d6172696f124d6172696f20416c74656e6275726765720000166d6172696f40616c74656e6275726765722e6e6574000011404d6172696f34416c74656e62757267000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a4298ccda952a9cf843ed05213492579c701c1cbca6f2d9622be202d81f305d01d37fbedbc99046d": "0x00000000000000000000000000000000000856696e63656e740000184076696e63656e746368616e3a6d61747269782e6f72671976696e63656e746368616e2e637640676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a47a74229619b37bfa515f4f9d5b9394fe8e5a71db0f44fd1c4dbbb569b6205ec5af981cd3e0794e": "0x00000000000000000000000000000000000e446f745f69735f467574757265064d6178204900001774686563727970746f6d617840676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a47a994958e0ce59ae58ba526977ac8c888aaec71da2be93459c2c6ee4a33f0881da7bd585b43a66": "0x040000000002000000000000000000000000000000000f546865204b7573616d617269616e00000016686579407468656b7573616d617269616e2e78797a00000f405468654b7573616d617269616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a490d8d4ffa73e2c52659f398c17705033d14b42950667700d2e50dcbf1cad45881fba6934fe1361": "0x040000000002000000000000000000000000000000000b426972646f20f09f90a50000000f697473626972646f40706d2e6d6500000b40697473626972646f5f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a4a63b3ca43f4645fae67a2277ba75c89fff533a2cd9574791255b0dedb578234bca6935a6bc6c2f": "0x040300000002000000000000000000000000000000000e4d616e7461204e6574776f726b00000016636861726c6965406d616e74612e6e6574776f726b00000e404d616e74614e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a4e8003fe2fb58faba58db6c7bfc75aa2b8ec1c9b2624172c81f6d2391180047091dcd0cea5ec45c": "0x04010000000200000000000000000000000000000000054a4f49450b52454e4a4945204355491068747470733a2f2f6a6f69652e696f14406a6f69656375693a6d61747269782e6f72670d6a6f6965634071712e636f6d000009404a6f6965437569000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a4ed3b905ddf8085bee2c0254a1998a65fc2787a82cbecdca2c0a675be63ae9c5148e32ae753d01f": "0x0400000000020000000000000000000000000000000015546865204b757320444f542044656c656761746500000016686579407468656b7573616d617269616e2e78797a00000f405468654b7573616d617269616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a53864ffd30a765388d1505492274985d6049cfae833ce8ce11597aca19d0f06a29ddb0a7a5fb97e": "0x040300000002000000000000000000000000000000000756616d7073790000000000000c4076616d70737966656172000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a551a248cffb7fab926d3318e308ddc7abc0757edbe7661e6aa17f8e269585ca53556423382b267f": "0x00000000000000000000000000000000000e4e6f74426974636f696e43454f0c5365616e204c6179746f6e001a406e6f74626974636f696e63656f3a6d61747269782e6f7267166c696e6b6c6179746f6e3240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a56d11c227ad244b78e4c82b1882d5cfa1d4b47a04b8906c00110e28b764b92f674b26e02061de7e": "0x04010000000200000000000000000000000000000000114b52414e412e5620f09f9a80f09f8c9900001b406b72616e615f76656e74757265733a6d61747269782e6f726717737570706f7274406b72616e612e76656e7475726573000010406b72616e615f76656e7475726573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a59acabf7cc5c2db3a0b67c6e4b35133a18ff9c3b56d6cd28662f9e47f38afbfc508543087966870": "0x040100000002000000000000000000000000000000000c43525950544f4e495441530000001e63727970746f6e697461732e6365727665726140676d61696c2e636f6d00000e4063727970746f6e697461735f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a5afff021978b2b7b6c683f2280b9cb98172008de3fd6d0ef5e40b746bd2f58424850e48bc33233c": "0x040300000002000000000000000000000000000000000352341244616e69656c20526976617320536f746f00001464346e6e792e736b7440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a5e1ae44678815b9744d9a778b5c53b54eb743dd93cc90079261a6e7fdffd8798e6406817d125d7b": "0x040000000002000000000000000000000000000000001144657574736368652054656c656b6f6d1a44657574736368652054656c656b6f6d204d4d5320476d62481968747470733a2f2f7777772e74656c656b6f6d2e636f6d2f001a7374616b696e6740742d73797374656d732d6d6d732e636f6d000010406d6d735f426c6f636b636861696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a6391f7854f254e8164c165c3dd34122f5005b617dc4941fed61fdfd1806ac31176c9a28d290ea06": "0x040000000002000000000000000000000000000000000b375f5468756e6465727300001640377468756e646572733a6d61747269782e6f726716375468756e64657273323140676d61696c2e636f6d00000c40375468756e6465727332000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a6433a6dcb5d8737833b5ca84aebe493b7e9bcab4e1b1cfd6d2eca6246dc8ed68fa9a31b87ae7166": "0x000000000000000000000000000000000012446966666572656e7469616c204c616273002068747470733a2f2f646966666572656e7469616c2e746563686e6f6c6f6779002164656c656761746540646966666572656e7469616c2e746563686e6f6c6f6779000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a687dc3f738f7410389373f82baeae3f3cf28a85284180ee443f41220de6901b5df8dc270e635215": "0x0403000000020000000000000000000000000000000008616476657269630f41647665726c6976652053726c73000010696e666f4061647665722e6c69766500000d61647665726c69766573726c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a693de5eabea317ab630005f93d977d7f3e58be6c8027af52620da1b35e7ad4b7c000ff303222f15": "0x040300000002000000000000000000000000000000000a4175646961726d6973114175646961726d6973204761726369610000156175646961726d69736740676d61696c2e636f6d00000a636173695f61756469000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a6eb394e364b8f9c7682e4e399a94b0dfc4a9bc476e0bc69f25413687d2b1c29bb7139dcc8e8714a": "0x0400000000020000000000000000000000000000000012537465616b20e299a8204e6f6f646c657300001e40737465616b5f616e645f6e6f6f646c65733a6d61747269782e6f72671a737465616b616e646e6f6f646c657340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a76c3d2bf5e8dd46d223e97cfb5260aa90a5f3cf5cb993f2658f07202847da92c712623e6c5da31d": "0x040000000002000000000000000000000000000000000a5374617475746f7279000000196d697368616b656c6d616e37373740676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a7ad5ea2e1e25da12ab2efc45e91ccb7b8e116388d12dd3be3a57d65bf8c0e6e9f70fd17685d8f14": "0x040000000002000000000000000000000000000000001cf09f87a8f09f87adf09f8f94205377697373204d6f756e7461696e0000001370657465722e63727970746f40706d2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a7b516d797914c7022eac0df731345052fd1e5b11e2b6d7fba33192395c37dc98ecda764fac0a05d": "0x00000000000000000000000000000000000f4b65726e656c4b656e6e657468470000001c7a68656e68616f2e676f6840636f6d702e6e75732e6564752e7367000010404b65726e656c4b656e6e657468470d6461726b61727469737472790000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a7c46d5435a1e2d86ad3c3fdb53ec38b9f865b8e2a836fbac2da234f4e72d660c9935b42a3e4173f": "0x04030000000200000000000000000000000000000000194d6f6565696e76657374207c2054656163684d6544654669001b68747470733a2f2f7777772e74656163686d65646566692e64650014696e666f4074656163686d65646566692e646500000c406d6f6565696e76657374000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a88c5f2dbe33d41afa9773cfd85e5d96165adec4ce7cc9265a267a57a241194cc868dbfa7e0c4301": "0x04000000000200000000000000000000000000000000084d61635a616d500000001763726970746f6d61637a616d40676d61696c2e636f6d00000f4043726970746f6d61637a616d31000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a8a3ea9a9a3cbf30d21eb80113b2f57759d263c0eb1f02291dbc81a41d5a98029ad55f941eea3153": "0x040000000002000000000000000000000000000000000d77656233616c6572742e696f001668747470733a2f2f77656233616c6572742e696f2f164077656233616c6572743a6d61747269782e6f726712696e666f4077656233616c6572742e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a8aceb78297feebd48c689eebc3c38ac671e2d6604d6490c7ad4261b2c157af30443bc297784795a": "0x040300000002000000000000000000000000000000000b50726f6d65746865757313426f6c7577617469666520506f706f6f6c610000146f6c616f79656a6e7240676d61696c2e636f6d0000114049616d5f5f50726f6d657468657573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a8d8c5df784c714204fb9dcaa194ca3f66a6c695aa7cb84c07bb5706202e5891d46cf6087422e068": "0x00000000000000000000000000000000001d426c6f636b636861696e20547261696e696e6720416c6c69616e636500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a8f0bd6de05e2315fa2847dce28d6741e63a14509aa8aba663935f6d257b49434b202a026c672966": "0x040100000002000000000000000000000000000000000562656172096265726e6172646f1768747470733a2f2f6269742e6c792f334749576265421b406265726e6172646f3a6d61747269782e7061726974792e696f1a6265726e6172646f617261756a6f7240676d61696c2e636f6d00000d406265726e6172727272646f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a91237ad3fc3e223ae0d1db9082bdce75480ee80c3bf3c6496de1ef8171951de2edfe49fdbe30a67": "0x040000000002000000000000000000000000000000000c477233336e4861747433520000001b477233336e4861747433524070726f746f6e6d61696c2e636f6d00000d40477233336e486174743352000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a91aecbe4d3bb95ad3816c3f7974b12327fb40c8ec5dbc5b79ab47f114838fdac54ddc5f7e890a3c": "0x00000000000000000000000000000000001e4d75736963204576656e747320496e6974696174697665204d756c74690000001b68656c6c6f40706f6c6b61646f746d757369632e6576656e747300001040444f544d757369634576656e7473000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a93c6e207f351cebc5c184f0565e2192d6aedae584ee736cef875f0e1c558ee3ede26869acd0b4d6": "0x0400000000020000000000000000000000000000000013f09f8c8c204e6f766173616d6120f09f8c8c164e6f766173616d6120546563686e6f6c6f676965731568747470733a2f2f6e6f766173616d612e696f2f1140646179373a6d61747269782e6f726714616e746f6e406e6f766177616c6c65742e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a957c1bfaa0ef8df8692dce19b16ed49226830ab9d2bab753c1118e797bf69fe63e00eceabd91278": "0x040000000002000000000000000000000000000000000a426c6f636b4465657012426c6f636b44656570204c61627320554700174067617574616d646565703a6d61747269782e6f72671467617574616d40626c6f636b646565702e696f00000d403078426c6f636b44656570000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a9b541eb27d45610a4f59749234ff94a7325d0770303f2dd834b6e82fa91a7f7f6afe41bc6ee473f": "0x04000000000200000000000000000000000000000000104d61726b65744163726f73732d424200001c406d61726b65746163726f73735f62623a6d61747269782e6f7267196d61726b65746163726f7373626240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a9d8946d2ec5efc6f618b6cccdab512073431509d2c2d4cc92d188c18b6bb968a0869ad0df4ca05b": "0x040300000002000000000000000000000000000000000844614b616d7065144368726973746f7068204b616d70697473636800001a6368726973746f70684073637974616c652e6469676974616c00000844614b616d7065000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a9d916ee238e56e058e0e1940ab2089ce4f65dc63abad1a6b654561a9a0b09fe5cc679f0ded3fc28": "0x040000000002000000000000000000000000000000000a4e654e6120f09f8cbb000016406e616d6574616b656e3a6d61747269782e6f7267196d796d696e647365746f6e796f7540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a9ef632ace16178262fda88d72053d9381fce1d6fa240c5264be8f884995f6f6dc8fd6b76b41fe19": "0x04030000000200000000000000000000000000000000084465656c616273084465656c61627300001364616e69656c406465656c6162732e78797a00000b6465656c61627378797a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714a9f08d3911f890e17a0625bffca33ce094f74f399580b16674ae3e0cc8ef259cde618e60e927ae53": "0x040000000002000000000000000000000000000000000d4b525950544f53434841494e0000000000000e404b727970746f73436861696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714aa51aec5596c16c9be84f63b9e30f438711f49b4a2f3e251c541f9a6e43b0e9df4f64e8394ba517e": "0x04000000000200000000000000000000000000000000075374616b696e000014406564776172646c3a6d61747269782e6f72671168656c6c6f407374616b696e2e636f6d000010405374616b696e4f6666696369616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714aad0604ad649cd85b6332bfd1e18c86739c3c5e7e76c95e7b0e95339005066cd34d89b842ca57d69": "0x0000000000000000000000000000000000064d617820470000000000000d40477261766974794d617878000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ab17ce0890ca2cdce229054651147bc5f6f0ab32ca19ce2c679aa663c212dac43c84ed2c3e494d27": "0x0800000000020300000002000000000000000000000000000000000a416c6578204265616e10416c6578204265616e2043617361731c68747470733a2f2f6769746875622e636f6d2f416c657844313053001d616c656a616e64726f6265616e636173617340676d61696c2e636f6d00000b404265616e5f44313053000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ab3f37c5fb7156fd86d918a899c95866c7a826e103307a9eb27318642550a300d56838d08c93e653": "0x00000000000000000000000000000000000c464f5552424c4f434b533300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ab53275528e71c4a1fd7775fc942283e61155bb5aa033ebd62e76cdc83a9d4b7dc4c611d7082da80": "0x04000000000200000000000000000000000000000000064f4e595a45001a68747470733a2f2f7777772e6f6e797a652e636f6d2f656e2f0010686f776479406f6e797a652e636f6d000010404f6e797a655f6f6666696369616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ab5eb6225eb473c4c4b00316a66db95fe01dae8970cad009688713c1a4432c2af716ddd364535008": "0x00000000000000000000000000000000000a564c414449534c415600000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ab6dc514ec367fe662f93efa125cfb7f0ec984625f8fc53cdc2f8aa936fc7d1b846831d132e4f701": "0x04000000000200000000000000000000000000000000105374616b65f09fa7b24d61676e6574001c68747470733a2f2f7777772e7374616b656d61676e65742e636f6d18407374616b656d61676e65743a6d61747269782e6f726715696e666f407374616b656d61676e65742e636f6d00000d407374616b656d61676e6574000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ab6e86b3ee5baa49d26a9a27ddedcc27b12275126b023f4cd1d0bc3eb533c220a6584a26b18c2774": "0x00000000000000000000000000000000000a446f72614861636b730a446f72614861636b731968747470733a2f2f6861636b65726c696e6b2e696f2f656e0014737465766540646f72616861636b732e636f6d00000b40446f72614861636b73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ab7e3e98c718729f7a895955042cb3fe863f3564e7b30e9130e37b6d905be11c0cf91064de1cd83a": "0x040000000002000000000000000000000000000000000853544b442e494f000015406672617a7a6c65643a6d61747269782e6f726700000011406672617a7a6c65645f64617a7a6c65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ab8afcc57061038068ecba10b973c7673eb761ade1ba23e48d03c35e9f42f80745acfe234565936f": "0x0801000000020300000002000000000000000000000000000000000e4d6f6f6e736f6e67204c616273124d6f6f6e736f6e67204c61627320496e631a68747470733a2f2f6d6f6f6e736f6e676c6162732e636f6d2f0019636f6e74616374406d6f6f6e736f6e676c6162732e636f6d00000f406d6f6f6e736f6e676c6162735f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714abe1d2fca3693953ae931623d3a530f2286c06b91e6f82a09cb757f6cf5b8d3d0f3dcbd60f1ab26b": "0x0400000000020000000000000000000000000000000005626962690000000e74656368406274616e672e636e000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714abf0f8ad2f6106869cc09a0521e12de2e2cca2ba86feb5df7506d9f9123e0f2687aadf171a47bc5c": "0x00000000000000000000000000000000000650726f746f1d416c746f726f73204c4c432028206462612050726f746f666972652900001c79756c6979612e6d75726173686b6f40616c746f726f732e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ac05ff7688083e8697b2ad8a1a194bf8104462fc283cfe8ea0cc039eee8ca312cd58abf65a2105f6": "0x04000000000200000000000000000000000000000000094242435f42616c6900001c4062616c695f626c6f636b636861696e3a6d61747269782e6f72671c61646d696e4062616c69626c6f636b636861696e2e63656e74657200001040626c6f636b636861696e62616c69000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ac220ad36e69b7cec697d17a1916f9313f9894d1db4016b2e1d64384f1e9cb49a735ea6a86328d01": "0x0403000000020000000000000000000000000000000010416c656a616e64726f5f52304755450a416c656a616e64726f000013616c656a616e64726f4072306775652e696f00000a40616c336d6172745f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ac39f36e309d037db40f4abb8698fdab096b0dcbe258ca86e64cd7cdd21633393b54fd74f22b1818": "0x040300000002000000000000000000000000000000000f4b61726f6c204b6f6b6f737a6b610f4b61726f6c204b6f6b6f737a6b610000146b61726f6c2e6b393140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ac6535d22692ff19365a58294c023e466c88ba2dd910a4a3c622c666c6a0c9f523c13feeef02ec21": "0x00000000000000000000000000000000001c313335204361706974616c20496e636f72706f726174656420303110416e74686f6e7920457566656d696f0f68747470733a2f2f3133352e696f000b616365403133352e696f00000f407265616c4143457566656d696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714aca973e143819fc644f6dd8841fc39979ef97aedf99a72c873c44d799938c03d96ef1e1fb2624c14": "0x04030000000200000000000000000000000000000000174f70656e20496d7061637420466f756e646174696f6e1b4f70656e20496d7061637420466f756e646174696f6e2050434300001c68656c6c6f406f70656e696d706163742e666f756e646174696f6e00000c7768657265736164646965000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714acdb8f7d99b9b4f82cd49434353de1d51598b44df15fcfed1e26224e055923acc9f9af881dc31d5a": "0x040100000002000000000000000000000000000000000843757272656e741546696e436f2053657276696365732c20496e632e1568747470733a2f2f63757272656e742e636f6d2f1b4063757272656e742d63727970746f3a6d61747269782e6f72671363727970746f4063757272656e742e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ad18028edc6538a02cb70c06950d8d61663a960c7e6664203696390388882bd305600fc1aedf520f": "0x00000000000000000000000000000000000c43727970746f6e617574730000001a636f6e746163744063727970746f6e617574732e73706163650000114043727970746f6e6175747353686f77000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ad2b158f9740a61618e66fcce7649125ff39ccf9f6ab0b1b88d1ec6b7bbd6824d04879a669589026": "0x040300000002000000000000000000000000000000000747617574616d0f47617574616d204468616d656a6100001467617574616d40626c6f636b646565702e696f00000e67617574616d6468616d656a61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ad3e18f67bdae596e8d0e24aa19d8b502a4b778f6172d6ecdc11bd3b9d320c70cec262e291d4a540": "0x0000000000000000000000000000000000055f5f5f5f0000001368656c6c6f4077656274687265652e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ad78c5b50e5a1535aad6496e2b96928ca236cb15b3ec0c43305b6edc3418296a4f01812378996a40": "0x0000000000000000000000000000000000054b6f656e0d4b6f656e20437579706572730000156f7074696d6f766540686f746d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ad874555459d7afb60621cb801a995bd99811fb64c3736affcf9ec823badb45c5ee6a812e292ca6d": "0x0000000000000000000000000000000000096a7573746475646500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ada7fdb39f37a1913c8664b0c41d35ec1c68c290d3b2a4e6c323e3c6199a1a3d5a301ae134a20344": "0x00000000000000000000000000000000000b746869636b34726573740a4a696e68616e20496d0000146a696e68616e2e696d40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714adbd010c539216a1b8fecc72fb024b6102c53a3924e8b7e2c163cfe6e5b84299c4481efa0580694c": "0x0401000000020000000000000000000000000000000006696c67696f001468747470733a2f2f706f6f6c67696f2e636f6d1240696c67696f3a6d61747269782e6f726712696c67696f40706f6f6c67696f2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714adc47ca95984db1b2cae6f34fcc3b96db7ffcd6ec5f6a91df93adbe09bcdc2adaf6bb1b8658ed05c": "0x040300000002000000000000000000000000000000000a4173796e63476f6b750000001a7961796f696b7573616d617269616e40676d61696c2e636f6d0000083078676f6b755f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ae10508827bed8b3385a3a535bf10808c4d6b1609d36042d9b867bb9446b03f4e6503245dc0c9e40": "0x00000000000000000000000000000000000a5869614d69506f6f6c00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ae3f2963b6c6f88ad4b61f6445b374d12ae0c94fa138ab0241beb18edb8a270118ad474b42fc062d": "0x040300000002000000000000000000000000000000000a796a686d656c6f6479094a696168616f5965000017796a6834363534303236333440676d61696c2e636f6d00000a796a686d656c6f6479000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ae60b338fc29997e986a4fdcd53d0e438f9694c9a6bb76665bc50acf76180049117caf3ca9bbcc6a": "0x040000000002000000000000000000000000000000000f747572626f666c616b65732e696f001768747470733a2f2f747572626f666c616b65732e696f1840747572626f666c616b65733a6d61747269782e6f726717737570706f727440747572626f666c616b65732e696f00000d40747572626f666c616b6573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ae6704a5589ed96274b1b6289e6796fc444ca7848a000ff1033fdb8aa867891a3b6a3580d312af58": "0x0000000000000000000000000000000000094d6f6f6e204d616e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ae820f0cf7b3fc26522523da8f16bc0b51cfd6e8b113f65f6be19f19681d5d6269cb980f9582c334": "0x0400000000020000000000000000000000000000000007746f6d616b6100001540746f6d616b6131373a6d61747269782e6f72671d7069657272652e6b7269656765723137303840676d61696c2e636f6d00000007746f6d616b610000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ae92f7865e89af5b3881c20170b3cfadb60b8455333d313ef17296f86321ef1c7b3a72e10693a00b": "0x040300000002000000000000000000000000000000000a4d61676e6174726f6e13436c696e746f6e204a616d6573205377616e000017636c696e746f6e2e7377616e40676d61696c2e636f6d0000104070697a7a61333134313539323635000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714aea3907ec456c507e23cf25bdabd9cf5532e11922e9c4d5b8188ded994a0c5647f16d5e325624158": "0x040300000002000000000000000000000000000000000c426c61636b4d696b4d616b1443687269737469616e204e746f75746f756d6500001e63687269737469616e2e6e746f75746f756d6540676d61696c2e636f6d00000b40546f75746f756d654e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714aea617318e829b5f12ae4cc150cef3f9e224d7b6cb10383e91a355a9c9052e21c1c638dbebab9921": "0x00000000000000000000000000000000001457696c6c69616d207c20506172617665727365000016407265706c67686f73743a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714aedcaf5c1d328d6a4a5e6693907b2e0647ddb0706e89f7227064060c1de7d10cadee84da22cd8360": "0x04030000000200000000000000000000000000000000086477756c6636390d446f75676c6173204b75686e0000176b75686e2e6f6e2e6b61736840676d61696c2e636f6d00000e406b75686e5f6f6e5f6b617368000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714aefad5055f10fa735e1d3caa2ac17c3d9376b773bd112e70edbeccef39789d70d41f9dc468181a01": "0x000000000000000000000000000000000021506f6c6b64744d6f6f6e426d506172616368417563437277644c6e474c494d520000001d696e74656e74696f6e323032354070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714af2fd699547c9f4bb4fd1795608e7424be43ddfaa50cf1f80621630df192fd4f9f02fa0e40308a7f": "0x0400000000020000000000000000000000000000000011506f6c6b617374617274657220526561000013407265615f63683a6d61747269782e6f72671472656173636865676740676d61696c2e636f6d000008407265615f6368000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714af9ae6e4f434181d9c322cfa42b80ffb1fa0a096ffbbe08ff44423ea7e6626183ba14bfb20c98c53": "0x040100000002000000000000000000000000000000000c456e73526174696f6e6973001c68747470733a2f2f726f626f6e6f6d6963732e6e6574776f726b2f1840656e73726174696f6e69733a6d61747269782e6f7267166c7340726f626f6e6f6d6963732e6e6574776f726b00000d40456e73526174696f6e6973000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714af9bf5521f86ae142ea24923f4561c7393dfee80dbf7d069efda507c76dbc2a160a6f02021ae017f": "0x00000000000000000000000000000000000c54696d204a616e7373656e1f54696d2048656e72696375732057696c68656c6d7573204a616e7373656e2168747470733a2f2f7777772e6c696e6b6564696e2e636f6d2f696e2f74696d6a00177468776a616e7373656e383940676d61696c2e636f6d00000e407468776a616e7373656e3839000f74696d6a616e7373656e3139383900", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714afa63c93073adc978e06bfc989509d6d625c085209adb405867bdbe4f167ded7e61ec126c683165d": "0x04000000000200000000000000000000000000000000135361736861207c2046656c6c6f77736869700000000d68694073617368612e696e6b0000000b4061677279617a6e6f760000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714afb2682abe1821512097f59f6298ee6903bf8b0f183a5a2967fa2d790e94d62aab7ee87247dd346d": "0x0403000000020000000000000000000000000000000017416268696a616e61204167756e672052616d616e646117416268696a616e61204167756e672052616d616e646100001a616268696a616e6172616d616e646140676d61696c2e636f6d0000104072616d616e64616268696a616e61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714afc50c1d950a2badd46e3e3ed41155f5f6971639b0fe8fb1afb792f9a7aaf267dc4c61693bbe2645": "0x0403000000020000000000000000000000000000000016426c6f636b636861696e204865616468756e746572000000146d2e73686c6179656e40676d61696c2e636f6d00000d626c6f636b636861696e6868000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714afdda45f48b9d6ccf0a0ba50ffb1d6b6af46b0ae93a21f6943ffe046c9e0423e7c603c46c6696f16": "0x00000000000000000000000000000000000c444f54205374616b696e670000001a6d6174746572686f726e4070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b045191cd50fbf997059b7d9ad6e9f5bab40385874989cd04aabbe821094b7e45b5e4de5ecf2bf4a": "0x040100000002000000000000000000000000000000000e416e736f6e202620466162696f16416e736f6e204c61752c20466162696f204c616d611668747470733a2f2f616e736f6e2d6661622e696f2f1d23416e736f6e26466162696f3a776562332e666f756e646174696f6e16616e736f6e40776562332e666f756e646174696f6e00000c40416e736f6e466162696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b07ceda0d965ecfb8233864aaf64f98f44f718a1ea60ebffdd0d4997de67f4b5e7f3b01f1d712d1e": "0x04030000000200000000000000000000000000000000064446472031000000126a616d65732e77406466672e67726f7570000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b09f0dc1cd5bdd13b0a08173d9322251d85475ae44c1325bfbb0254f20318b0aade97b250af5c010": "0x04010000000200000000000000000000000000000000084d6574686f6435001468747470733a2f2f6d6574686f64352e636f6d0011696e666f406d6574686f64352e636f6d000009406d6574686f6435000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b0a07ed5a19f8b6a90fcde4b37a2b8100dd2dc66cc9e195587e3bf396cb376c4589921d1bcd11905": "0x0000000000000000000000000000000000135354414b454241425920444f54205045525300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b0b594ec0cee6911fb9cdfce8071d94d5226bb1746bcafbd3bff4ef7ca9f923f9ba24d6c501002fb": "0x00000000000000000000000000000000000a4672657175656e637900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b0c28bc153cfcb785e71bc8d34c9270330e524544df34af4113a66bf1a300cfd6e67e6bc522cdd1a": "0x04030000000200000000000000000000000000000000064d6174656a0d4d6174656a2053747275636c00001773747275636c2e6d6174656a40676d61696c2e636f6d0000097363687472756c65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b0f623591c049c48d238ccb2dae20c1ebb4ce235085255a5d5d0d7bc55bf8b312fdeabac027daf3c": "0x000000000000000000000000000000000009436861696e494445074b75646f77751568747470733a2f2f636861696e6964652e636f6d00146b75646f406d61747269786c6162732e6f7267000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b156c95d777eb874c4d118037ecd231792bbc725102956f2f0488cd30ffd46c2f3dd5b1e17502137": "0x04030000000200000000000000000000000000000000074d626c6f636b074d626c6f636b00001576616c696461746f72406d2d626c6f636b2e696f00000f404d626c6f636b636f6d70616e79000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b1580a59c1acfdc54421050207b47ba1bddef66d1e1deee5b27d27d7fc526cfd68e4be18a5b9b146": "0x080000000002010000000200000000000000000000000000000000104555534b4f494e2056697a63617961104575736b6f696e2070726f6a6563741168747470733a2f2f6575736b6f2e696e14406575736b6f696e3a6d61747269782e6f72670f6b6169786f406575736b6f2e696e000011404575736b6f696e4f6666696369616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b168e3b84020d6822dcd0e9ec909fc8e672d8708223330eac4613042b2c63c8d913fc6a12bd99fd8": "0x0400000000020000000000000000000000000000000004524a5f00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b18604d7d7a798faa40aa369106391b44027cbfd19b4e8e475971c90f78717f85bb6a5976299567f": "0x04030000000200000000000000000000000000000000094772696d66616365000000126e6f7461746b6940676d61696c2e636f6d000007403162696e31000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b1a455bf3d4031c6b422d3e099417a341702fc9e4dd318da3c2993adcb81a3cf35ba18679724b36e": "0x000000000000000000000000000000000014f09f8c9f20616c6578616e64726120f09f8c9f0000000000000b40616c7868656c6c6572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b216fc8c00c1967038406fc5553270a770f2b9afe29ef48028738645f7bc0f221460634d1a7c9a07": "0x040000000002000000000000000000000000000000001f4d6f6465726174696f6e205465616d20426f756e74792043757261746f7200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b237135b4b47977700321713f671d3c1fb706209062ffd05e74bb4a42470156d8103a25d4dc0201a": "0x04000000000200000000000000000000000000000000064b414e4459124b616d616c6120496d6d6163756c61746500001c696d6d6163756c6174656b616d616c686140676d61696c2e636f6d00000d406b616d616c61496d6d6163000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b23d190eeefbfb6f7a9bd53c257e7c227609b012a33e9e17537cadc7740b22be73883942c673b34f": "0x040300000002000000000000000000000000000000001157656233204173736f63696174696f6e1147656f726765204c6f766567726f766500001968656c6c6f40776562336173736f63696174696f6e2e636f00000f4057334173736f63696174696f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b26d5289874157380c300de9970a72be7155e43892cc29b4d4b4c8a571b61cfd5430a52afa1af85e": "0x0403000000020000000000000000000000000000000006637962696f11446f6d696e6971756520536962657564000011637962696f40686f746d61696c2e667200000940446f6d69536962000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b27baa39147ef36e8ae3ad2c81ef018e08ea05d0422b9cf276d6c909936b04105c1686cb3eb2fd0a": "0x0400000000020000000000000000000000000000000014506f6c6b61646f74205068696c6f736f70687900002040706f6c6b61646f745f7068696c6f736f7068793a6d61747269782e6f72671f636f6e7461637440706f6c6b61646f747068696c6f736f7068792e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b2867f14046b21d05a33766207d51925e4b9a2302870bf737305cddd5bb2df2dffa379963e586771": "0x040100000002000000000000000000000000000000000b5374616b656454656368001868747470733a2f2f7777772e7374616b65642e746563681340766564646f6f3a6d61747269782e6f72671b7374616b65642e746563684070726f746f6e6d61696c2e636f6d00000c405374616b656454656368000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b2c78530b4a796f2f8f0244b95932b7a67caffd31de91edc5cfb3aa2a13f6199f127ce51c558204a": "0x00000000000000000000000000000000000b4441524b464f5245535400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b2d135d5db3b036bce4761ea5d8d29dd92a78dc606246d8146320a5e9292de240ab15f60c7802e4a": "0x00000000000000000000000000000000000a534e5a506f6f6c2d3108534e5a506f6f6c1768747470733a2f2f736e7a686f6c64696e672e636f6d0012686940736e7a686f6c64696e672e636f6d00000c40736e7a686f6c64696e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b2fb008da08ac54bf6dddfb2f535dd62fdac8b70b241d590e7838c5f553e66cf8bbbafd47fce817b": "0x040300000002000000000000000000000000000000000b4c61726b2044617669730b4c61726b20446176697300001874686563727970746f6c61726b40676d61696c2e636f6d00000f4074686563727970746f6c61726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b340199e34eab193aa612c6fb0748838d721b854a70cb1838cc4fc0814e80d4785dab72125e8a010": "0x0000000000000000000000000000000000086e616e6173736511414e4153534520454c2048414e414e4900001a656c68616e616e692e616e6173736540676d61696c2e636f6d00000000056136653700", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b3b2f5054ad51dabe830cd17659a15933def85ed0afa8f681e5db64551c1ea411b5e9be3e7349e1c": "0x040300000002000000000000000000000000000000000e57414c4c4554434f4e4e4543540e57616c6c6574436f6e6e6563740000176a6573734077616c6c6574636f6e6e6563742e636f6d00000e77616c6c6574636f6e6e656374000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b3b4ecc4022f0e1630a3eb5333cc487d3754d4c76346343611dfbfb5eb55c15045daae38282a3939": "0x0403000000020000000000000000000000000000000005526f77690f526f7561726b204c65657264616d000019526f77695f6c65657264616d40686f746d61696c2e636f6d00000c4c65657264616d526f7769000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b3d2b9874fe0df30cc7a6b245f0617fa48c27e56589cd6954c0448ced7c54df2325b007ee5b4ca02": "0x0403000000020000000000000000000000000000000007636f6e7232640b4a6565796f6e6720556d000011636f6e72326440676d61696c2e636f6d000007636f6e723264000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b3dac7c4c99b7c0d8e851ed992228f2268ee8c614fe6075d3800060ae14098e0309413a0a81c4470": "0x040000000002000000000000000000000000000000000b427279616e204368656e00001340786c6368656e3a6d61747269782e6f726714627279616e406163616c612e6e6574776f726b00000d4058696c69616e674368656e04786c630000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b409b598d1b08171e0a38871f4a1941fdd3af5a049c463a4cec1df86f594c61dda778d07b4693c7a": "0x0000000000000000000000000000000000194d4554415350414e2028706f6c6b61646f7420706f6f6c290d6d6574617370616e206c746400000000000d406d6574617370616e5f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b43789f78727cee3c885a011dbc63183416fa77f78a967ad0ffbbbb62e444e9eb490abd2fc67d326": "0x00000000000000000000000000000000001042494e414e43455f5354414b455f331042494e414e43455f5354414b455f33000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b471bc360e5a3e9548020d0712411b7ebf7f757f9f0d3f69f1660d636fb2c9811d81140cf84f561d": "0x0000000000000000000000000000000000074341505045580743617070657800000000000e4043727970746f436170706578000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b49940857135462fa0db8c6cb723c474639931cae07e095e6e8d9b870c90f5b499bd8e6fdf4bd54f": "0x040000000002000000000000000000000000000000000a4c696d65436861696e000000126869406c696d65636861696e2e74656368000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b4a5da87e533f5dff58927b296a23cbf25794b7cbb8fe31c5e68f2bcd2c2483e9c9cd0712216f232": "0x040100000002000000000000000000000000000000000f3125202d2047545354414b494e47001768747470733a2f2f67747374616b696e672e636f6d2f14406761757468387a3a6d61747269782e6f7267166761757468387a4067747374616b696e672e636f6d00000a40475374616b696e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b4ab7a3aac34e144a8643e2e7c9f9bec10df82710ba1181b7b07b6cc15674584995f911ebd304661": "0x00000000000000000000000000000000000f4b656b6f7365206f66506865656200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b4c98d3a42ca87e8eadf02d15ffa95f56be8b5d91825988362d5f8918ba3070e258dea7b0c67806d": "0x040000000002000000000000000000000000000000000f506f6c6b61646f74204172656e61002068747470733a2f2f7777772e706f6c6b61646f746172656e612e626c6f672f0018706f6c6b61646f746172656e6140676d61696c2e636f6d00000f40506f6c6b61646f744172656e61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b4d353916a9420c20c65d7c1489d18dedea3b4acf5443bd0f088e6062d20f15e774ee860e39e3d2a": "0x0403000000020000000000000000000000000000000008626b6f6e747572124272616e69736c6176204b6f6e74c3ba72001c406272616e69736c61765f6b6f6e7475723a7061726974792e696f12626b6f6e74757240676d61696c2e636f6d000011404272616e69736c61764b6f6e74757208626b6f6e7475720000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b4e729082bc0effa0c691601793de060491dab143dfae19f5f6413d4ce4c363637e5ceacb2836a4e": "0x04000000000200000000000000000000000000000000064c65656d6f000000166c65656d6f407468656368616f7364616f2e636f6d000009404c65656d6f5844000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b508400087c4b7c3a800d94d09f32bd5d031bd76c9a832c34b291c964bbcc63a81ac3589b48f543d": "0x000000000000000000000000000000000013424946524f535420464f554e444154494f4e14424946524f535420474c4f42414c204c54442e1868747470733a2f2f626966726f73742e66696e616e6365001668656c6c6f40626966726f73742e66696e616e636500001140626966726f73745f66696e616e6365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b51eff5e9260eafe64e6db572dcc9e5dc57445ff7c68ce85e42527aa74b2ed5d1e2bceefd308ed05": "0x0400000000020000000000000000000000000000000015434f4c442053544f52414745204341504954414c00001f40636f6c6473746f726167656361706974616c3a6d61747269782e6f7267207262617272617a6140636f6c6473746f726167656361706974616c2e636f6d00001040636f6c6473746f72616765636170000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b5638bc24447b04d3213d525c169d24ac664b8fb61afb7ff9c5c542d0942a9ae620513e2b83e787e": "0x04030000000200000000000000000000000000000000064d756e61790c4f6d6172204672616e636f0000166f6672616e636f6865616440676d61696c2e636f6d0000086d756e61797475000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b575b6cc98247c63f866d0c3a34620c8db7cd658083de421bbbbe597794f2911c1cb53975f5e83a0": "0x040000000002000000000000000000000000000000000ce2a793204170696c6c6f6e0000001168656c6c6f406170696c6c6f6e2e696f000009406170696c6c6f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b5857832389789b78af348b187a2e94f7dfcacc1de5c71b55f6ab8a50e75f0ac1a15baeebfd92e03": "0x040000000002000000000000000000000000000000000b47616975735f73616d6100000015672e756e69743234383140676d61696c2e636f6d00000b4067756e697433313234000b67616975735f73616d6100", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b58ced053a478447f6f6cf81a81b368a0e419b9b6e205f3f9d2927a27e663e6601bad2e7c0df6276": "0x04010000000200000000000000000000000000000000074c7572706973074c75727069730000176c757270697340626966726f73742e66696e616e636500000a4030784c7572706973000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b5c07bdf818842cb2cedffdd95011af0a1c5683f8445a05006f30487f0dd25beac664c54bf55441d": "0x00000000000000000000000000000000000f416e696d6f6361204272616e647300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b622b6f540376f72543919fb30b3e6e49c1e95a77c05b6d87f457e5cc4e7275584bc1021f808ef65": "0x040100000006000000000000000000000000000000001b6e616c756c756c616c615f506f6c6b61646f745f5265706c61790d594f4e47534f4f4e204c45450000156e616c756c756c616c61406e617665722e636f6d00000e406c756c756c616c6131383534000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b67813127799c8e920b456b7a8651f0b81f4517ffc79737cc392230cdd92a5b4bfa09ac728a0b10e": "0x040000000002000000000000000000000000000000000a536166655374616b65001568747470733a2f2f736166657374616b652e696f1640736166657374616b653a6d61747269782e6f726712696e666f40736166657374616b652e696f00000d40536166655374616b65494f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b6d59c328d706cfb061f3d8a0b63df2c81c3a5c1fa616fb3a3695d64249dcbb01d3cf73621fe8e3d": "0x00000000000000000000000000000000000f4675636b5f6368616f735f64616f00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b6ecd013f449a75f58f2f7dd26682082ccd78611deeeffb89b38bfe97fe95be7e2047cd8e346ad1d": "0x0401000000020000000000000000000000000000000005474465650c47656f726765732044696200174067656f726765736469623a6d61747269782e6f72671667656f726765732e64696240676d61696c2e636f6d00000d4067656f726765735f646962000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b710ad30347143bdf2e76f70974fa08e941bffc14e3b2c8643a58a35f95bbe411b90ca62acd15a77": "0x0400000000020000000000000000000000000000000008537461747574650000001b73746174757465636f72704070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b7acf702b47d3804de064386a3512bba067ef6bd099e7f3a3e68fe6074a8c1ab872ddd7beb8c9002": "0x0400000000020000000000000000000000000000000007686972697368000013406869726973683a6d61747269782e6f7267166869726973684070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b7b376ac778360f7628da3d78e9d1d4af0ceba193397950ea8da074445f066aabc66c82a50fa4e4a": "0x0403000000020000000000000000000000000000000021506f6c6b61646f74204d75736963204576656e747320496e69746961746976650f6261736820617564696f204c4c4300001b68656c6c6f40706f6c6b61646f746d757369632e6576656e747300000b4062617368617564696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b7e8f2933e177fd140c32e6713cc47364b6998b554957eba420df2ab568e6d8e8c672086dd9d5358": "0x0000000000000000000000000000000000215b4d756c74697369675d4576656e7473426f756e747943757261746f727376320000001a706f6c6b61646f74406576656e7473626f756e74792e636f6d00001140444f544576656e7473426f756e7479000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b7ff6a0c4f676b75103fe40937daa62b317e7c8e87c32b0503f705461ea76fc4babcd0c4e2a30a2c": "0x0401000000020000000000000000000000000000000004726f620d726f62207468696a7373656e1268747470733a2f2f726f622e746e2f6376000a686940726f622e746e000009406772656e616465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b835555e3cf0e1b97645bce397d2046a059a06f0252ce845eef1eae32b9243a054d981ce11245c27": "0x04010000000500000000000000000000000000000000124d7974686f7320466f756e646174696f6e124d7974686f7320466f756e646174696f6e000012636f6e74616374406162696c65782e6368000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b85669beeb21dd0baa6646d5b85790bc0ab869d80385fdc10e1f4befa7f8a4bf31848f73012d2823": "0x040000000002000000000000000000000000000000000946616972204645450017687474703a2f2f666169726665652e62616c702e65750010666169726665654062616c702e6575000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b861b613b2b2cdab7a04046977ce345e873315f47f0a6cf6a3659abaa029d6cbf7135fdd53fb573a": "0x0400000000020000000000000000000000000000000018574f4a444f54202020ca9520e280a2e1b4a5e280a2ca9400000012776f6a646f7440776f6a646f742e636f6d00000840776f6a646f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b87e14d1620810750e8ed639d511aae6e8213a795a521b6e088b292e45b9ff1e2dcf31cad748e91b": "0x000000000000000000000000000000000006706f6c6b6100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b89d726fb1d2f438faac5c26d98f04b9b842e95e85244977a27fc7c93bc73679728d227172aa814d": "0x00000000000000000000000000000000000c526567656e63792d3031381757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b8a74d4a444db6f1bcf647b00211b2d6c8cdb9e091b95ba03ac8bc6a4dd0124f1f09f8c917d1d270": "0x040100000005000000000000000000000000000000000a44454741204953504f05444547411668747470733a2f2f7777772e646567612e6f72672f0011636f6e7461637440646567612e6f726700000a40444547415f6f7267001868747470733a2f2f646973636f72642e67672f6465676100", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b8bf8f1e86641005011b82ff2f3419c087efdfb9490db431028f2a638d5c6aeee59c4fb3f1a30b81": "0x04030000000200000000000000000000000000000000094b617a756e6f62750f4b617a756e6f6275204e646f6e670000166e646f6e676d6566616e6540676d61696c2e636f6d00000e4b617a756e6f62754e646f6e67076e646b617a750000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b8c65c55ccbce8a00ad902bdbcd4bb2f81f505fa223b63310cd34e5eeb715b0e50ad3b7e0b412e59": "0x0403000000020000000000000000000000000000000009307877617369616e0f416e64726577204c69757469657600001f616e647265774073756d6d657262726f6f6b686f6c64696e67732e636f6d000009307877617369616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b8ebbd021ec2bde2f068505a66e272d987c377f0ae0c0a795edbb4db40be62187205803920118a28": "0x04000000000200000000000000000000000000000000086873696e636875000014406873696e6368753a6d61747269782e6f7267166969656c6974652e6c656540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b8fe9945a99464405661eeafb5d22cc01c4dfe7ddaf4af6210da32aa407b91be8af3f75bc6589871": "0x040000000003000000000000000000000000000000000c4879706572737068657265001d68747470733a2f2f68797065727370686572652e76656e7475726573001d6d616e616765724068797065727370686572652e76656e7475726573000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b902e56a5ec842fbc8018be75da4c5757d622874c1dd478950b27baff9b50ca4c0e7670c237f626d": "0x040300000002000000000000000000000000000000000a77336e3a657269636b0c457269636b2052616d6f7300001577336e657269636b40686f746d61696c2e636f6d00000a4077336e657269636b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b9544e0ad8a886ccfcfb0d15de7d1812d75d043f0bf16879a129a02498b40726280770bd38d3cf46": "0x040300000002000000000000000000000000000000000e4c69616d2050656e64756c756d001b68747470733a2f2f70656e64756c756d636861696e2e6f72672f00136c69616d407361746f7368697061792e696f00000e404c69616d50656e64756c756d000a70656e64756c69616d00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b970ba0a552b2a1b5a1f26de64611bef588e2d98deebffdf026e87b35385c2eb3959c47e4e73c74c": "0x00000000000000000000000000000000000c506f6c6b61646f7420545018416e746f6e696f205061736375616c204a696d656e657a000016747061736375616c6a406f75746c6f6f6b2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b9744a1a4ac9720b28ce403471e5e1dcf0c84eaebeeec6abf5caecb0f65478c5a4e6e40414872e3c": "0x0800000000020100000002000000000000000000000000000000000c3238446179734f66446f74001c68747470733a2f2f7777772e3238646179736f66646f742e636f6d001a6368616c6c656e6765403238646179736f66646f742e636f6d00000d403238646179736f66646f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b9a0b6f1289fd337141f9165a69d007a81159ca9efe4b58aed0df50423fcb1bc90cddd9be417697a": "0x00000000000000000000000000000000000b4d757365756d5765656b001868747470733a2f2f6d757365756d2d7765656b2e6f7267002062656e6a616d696e2e62656e697461406d757365756d2d7765656b2e6f7267000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b9aa88162953b359807e371d9df95983e2b9face8efb67f962816f1a0c6681cb1e5455127ab45c09": "0x04000000000200000000000000000000000000000000154150455254555245204d494e494e4720f09f8e820000001d76616c696461746f724061706572747572656d696e696e672e636f6d0000104041706572747572654d696e696e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b9cb4016283da91d8686261e411fc4e31d3cb29074978a43851627965b4f4189c7777a83a624b11d": "0x00000000000000000000000000000000001d426c6f636b636861696e20547261696e696e6720416c6c69616e636500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714b9f7ccb0a5350c8ee185aadacee7bfae327e2f8104bb96e15d6e026bed15e1290d6f1f88ad681f23": "0x00000000000000000000000000000000000d6672656576657273652e696f0f46726565766572736520532e4c2e1968747470733a2f2f7777772e6672656576657273652e696f0000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ba371c7e28307a6d16a1fddb4cd14ff65b25f91ae81ba64df7e6318ed826746ed5298feaa38c6c1d": "0x040300000002000000000000000000000000000000000b506174726963696120410e5061747269636961204172726f00001e706174726963696163616d696c6c656172726f40676d61696c2e636f6d00000b40706f6c6b615f706174000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ba5082da70988302146fae5102954f580c20e67585bbd57aeb722bf29fe02607c8b876270706a901": "0x04000000000200000000000000000000000000000000074d65726c696e000000166d65726c696e6e6f6465734070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ba6da6ae5a6fff014e031ef11ea1fa0b7f1f0c07b0a5d36afeea49a8c094f6959d1c3702c5cd016c": "0x0800000000020100000000000000000000000000000000000000000e5869616f207c20537461726b7300001340787a68616e673a6d61747269782e6f7267127a68616e677840676263746563682e636e000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ba7663c3b3aaf51b09cdd094e9a51d26bbc6cbb71d3f7c5b8edde629402e3e5370e7f6904512fc4a": "0x0401000000020000000000000000000000000000000011445241474f4e5354414b4520f09f90b210447261676f6e5374616b652c20534c1768747470733a2f2f647261676f6e7374616b652e696f154064657266726564793a6d61747269782e6f726714696e666f40647261676f6e7374616b652e696f00000d40447261676f6e5374616b65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bac18b8eb37d5be29af7e1fc9dca350922e065a2ae8b3366f554e64c9519c55bf1dd7c8b2f8c2554": "0x00000000000000000000000000000000001054616c69736d616e20506f6f6c2031001568747470733a2f2f74616c69736d616e2e78797a000000000f40776561726574616c69736d616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bac296575a7da3090eb83479fa34dc63024ed44efa464427375a44de486e8d6007c7842b45ce817f": "0x04000000000200000000000000000000000000000000114255454e4f2056414c494441544f524f000017406275656e6f76616c69643a6d61747269782e6f72671a686f6c61406275656e6f2d76616c696461746f726f2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bacc123aba961dfdac7c228c0c2f9f8bd69a79694a21c0aaa11fa0bdffb8a24f8a2b2c7c71dd4464": "0x0400000000020000000000000000000000000000000008616c657867676813416c6578616e647275204768656f7267686500000000000008616c65786767680000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bb6fb2c1a1cf337b7ed3dd1132e1f216cb30c2440b46423faf32c6effd0a2d9a9f24e52f57af6677": "0x040000000002000000000000000000000000000000000b4c415552454e5454524b000000186c617572656e742e747572656b40676d61696c2e636f6d00000c406c617572656e7474726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bb813580522537309ac6f5d9af351a6a087753ae455455f640508159d2455ba20f245403a14a2338": "0x00000000000000000000000000000000000a42697474656e736f720a42697474656e736f721668747470733a2f2f62697474656e736f722e636f6d00196f7065726174696f6e73406f70656e74656e736f722e616900000c4062697474656e736f725f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bb8e58b3f0252518fa04117758ff4b850ed983aa317d44ad71f28a5cc36caca35d4b60cc28c02061": "0x040300000002000000000000000000000000000000000b436f6e6e6563746966790000001c78696f6d6172616268756c6c617235323740676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bb99afa281244f547837d6d2430ff5a0e6f8b7aa57f325f01ae204332d4cda591324dc09b5da1268": "0x04030000000200000000000000000000000000000000054d6f726b0000001e676f76406d6f726b6d6f726b6d6f726b2e616e6f6e616464792e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bbcb4a8bc42b334be86b14052f27742916a13482c39afc8d9a8f873d799c9aa070bbd045570baa66": "0x04000000000200000000000000000000000000000000114d69636861656c5f52657075626c696b00001d406d69636861656c5f72657075626c696b3a6d61747269782e6f72671861646d696e40676c6f62616c70617468776179732e696f00000d404750435f4d69636861656c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bbe20aee1ef9d6a1a8955315ec9f709c88644db0bfc9df67e13c44336d15899eae335b2e0b05346e": "0x040000000002000000000000000000000000000000000d43727970746f6772617068790000001963727970746f677261706879766c4070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bbe6cdadc6b5f81166abfdc8c3f01b4913bb09c1690b3ad15179ad20fb3e1f46d90e0104ea90951b": "0x00000000000000000000000000000000000b4e69636b20536d697468174e6963686f6c61732043616d65726f6e20536d6974680000126e69636b4074616c69736d616e2e78797a00000e406e69636b63616d736d697468000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bbee26bcd7ecf2ba823ef42d2d68197d69bab7d3cbade923c9ba19206656cfe238336e3df412581b": "0x00000000000000000000000000000000001250616369666963204d65746120496e632c1250616369666963204d65746120496e632c1c68747470733a2f2f706163696669632d6d6574612e636f2e6a702f00186b65697269407061636669632d6d6574612e636f2e6a7000000d40506163696669634d657461000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bbef2b0d9d60628cf627e295ecc92fc03a0f47172c8655f8d2a0d23df634fc94ce264807cf5bab23": "0x04000000000200000000000000000000000000000000074f7261636c6500001440616c6d6172696f3a6d61747269782e6f72671c76616c696461746f72706f6c6b61646f7440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bbf605712f9a94b89e2dad25d8b2be1adbae19a2063b835fab6a12c15bfb2c027420a5afc7381e7e": "0x04000000000200000000000000000000000000000000084d447564757461000000186475647574612e6d617269757340676d61696c2e636f6d000009404d447564757461000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bbfb887412ddd8c82e9bbb2cfebfeb22622c29ba56cd766025f4cf35c7737bdcc6762378b7cb207b": "0x0400000000020000000000000000000000000000000015616e6472656974612d76616c696461746f722d3000001540616e6472656974613a6d61747269782e6f72671b616e64726561662e7370657a69616c6540676d61696c2e636f6d00001140616e64726561667370657a69616c65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bc3a76766e184695e0584b8462ed4202de4277676f6b1d2b77115672493e6f6fff429b660a0ab865": "0x0000000000000000000000000000000000164465636f6465642056696577696e672050617274790e44696c6c6f6e2048616e736f6e1868747470733a2f2f7777772e646961646174612e6f7267001a64696c6c6f6e2e68616e736f6e40646961646174612e6f72670000104064696c6c6f6e68616e736f6e3132000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bc7126812bf1df493e1630f099540e76e98b9fb0002c3a5ae3f40b6ec180df68fe5cd9bd2088fa18": "0x040000000002000000000000000000000000000000000a535445414b43484546000017407374616b652d636865663a6d61747269782e6f72671868656c6c6f40737465616b636865662e6e6574776f726b000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bc9fa9104384658136e4b176f4f8c8e93d1f038c4ad53d6ce6308764888af81d0b2acc9903f59a3d": "0x00000000000000000000000000000000001850617261636861696e7a20556e204a6f6262656420544500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bcbd30ffb6d16c690237f4df962d9a15d5d2fa981f6f689fa4add977e746c01a07620a92caea5277": "0x04000000000200000000000000000000000000000000136c616e6465726f73207c205374616b655570000015406c616e6465726f733a6d61747269782e6f7267156c616e6465726f73756140676d61696c2e636f6d00000d406c616e6465726f7375615f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bcfdd67ea673f1f2641768da63aab1ad01b2ffed1a44b41c4475f3efdcb74d1e45cf3c490db0c11b": "0x040300000002000000000000000000000000000000000b7863526f6d312e646f74077863526f6d310000167863526f6d314070726f746f6e6d61696c2e636f6d00000a40726f6d315f646f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bcffc29008504fd75ca3d296ca48e252a8029882a8218a8c821bb51d80f4852a4b61e29b3d8d3e79": "0x040100000002000000000000000000000000000000000c426c6f636b636f64657273001868747470733a2f2f626c6f636b636f646572732e696f2f001b656e67696e656572696e6740626c6f636b636f646572732e696f00000e40626c6f636b636f646572735f001e68747470733a2f2f646973636f72642e67672f7a6571466e7755786b5900", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bd0c168620def27bea3a8ff4ae2930c102a7bb34d17374198a33d4250b6c5eb4689efc0035298107": "0x000000000000000000000000000000000016436f6e74726f6c6c6572204a6575206ec2b032202d084d6169734e6f6e000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bd3136476e89f2d1688a4b3d49b7fa3e1587f8a8e3b445c7c3e830d524a6dc0bfd89a0f8627a6f08": "0x00000000000000000000000000000000000741554b4c454e0000000000000c40616e746f6e6961796c79000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bd45ee52acd85afa825872f7d324c8d97a9d5f5c94d918eea93ef783f305767b768a76f9fede7b4a": "0x0401000000020000000000000000000000000000000009414c4c4e4f4445530e416c6c6e6f64657320496e632e1968747470733a2f2f7777772e616c6c6e6f6465732e636f6d0015737570706f727440616c6c6e6f6465732e636f6d00000a40616c6c6e6f646573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bde7f741d77b7e0ea2e36a2a787a3d59588d756dd54b5d1cf5a464496d7c29d31438f1a8a2a0a80e": "0x040000000002000000000000000000000000000000000a436861696e5361666500000012696e666f40636861696e736166652e696f00000d40636861696e736166657468000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714be03b2298e56f6b680118fb6d4f674aebcecccfa0640584af7004d155d17fd88e0b5d1b682460a2a": "0x040000000002000000000000000000000000000000000c646f747374616b652e696f0000001268656c6c6f40646f747374616b652e696f00000d40646f747374616b655f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714be084a2d67f3fe459800cbb128a37eb9e9ce630aebdaa4e3345ac67d75b766524cdf2e21de7b721a": "0x0403000000020000000000000000000000000000000006446f646f77000000156372797074646f74616f40676d61696c2e636f6d0000114066617368696f6e69737461776f6e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714be09d91e809b5850d61b7925b1cfbbd6c618187421191758888a19df2d04f622da9d941c6066b47a": "0x0401000000020000000000000000000000000000000009444f5445522e494f09444f5445522e494f10687474703a2f2f646f7465722e696f14406a6f69656375693a6d61747269782e6f72670d6a6f6965634071712e636f6d00000a40646f7465725f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714be3183951dd1f1d5e626934768e68509f3b657372165e6f98fdefe615cc8e669d5bbe033a6478556": "0x040100000002000000000000000000000000000000000bf09f9491204b656974680d4b6569746820496e6772616d1968747470733a2f2f6b65697468696e6772616d2e696e666f18406b656974683a6d61747269782e7061726974792e696f106b65697468407061726974792e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714be58ca2ba19c9036f8596d3d1bfe4d7b96b76a35bb52078edb437a5e5b932fad5f653bf0080e0d50": "0x0400000000020000000000000000000000000000000008414c455353494f001d6c696e6b6564696e2e636f6d2f696e2f616c657373696f6f6e6f7269134069726f6e6f613a6d61747269782e6f726718616c657373696f2e6f6e6f726940676d61696c2e636f6d0000084069726f6e6f61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714be88be4b51946ab0b45ad6a53e0a688752f3a40f49cf7e666169c787f83bf1c2ff8aa026d99ac177": "0x040100000002000000000000000000000000000000000e5354414b494e4744585f434f4d0e5354414b494e4744585f434f4d1668747470733a2f2f7374616b696e6764782e636f6d16407374616b696e6764783a6d61747269782e6f726713696e666f407374616b696e6764782e636f6d00000b405374616b696e674478000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714be90c353cce89dbc62838e85d6feea406b4544b851f10449c2f2376b9d3805a3b7cdb98ef2573f7d": "0x040100000002000000000000000000000000000000000c41576f726b65722d3030310000114066756e633a6d61747269782e6f7267156c69646977656e63686540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bea99ac32a4539bc724d262fd25c8cc975189c3ae4f0dee1ba2e17080cda69183412d0928b49db0f": "0x040000000002000000000000000000000000000000000b7375626c61622e64657619537562737472617465204c61626f7261746f7279204c4c431368747470733a2f2f7375626c61622e6465761a406f616b6c65792e7375626c61623a6d61747269782e6f7267126f616b6c6579407375626c61622e64657600000b407375626c6162646576000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bed90d4bbefb67eb8485f4b667f18f10d2c523593467680fb28b501f93d053ed08a1eed3c9e5c852": "0x040000000002000000000000000000000000000000000c43524950544f4d454449410000001d63726970746f6d656469616f6e636861696e40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bee71c5b95c53e49da92c32ab2b4e1a46bb659cb6fd22fc824611e4c2803fbedd93c246f97c67118": "0x040300000002000000000000000000000000000000000a566976616c646920300e476f6c616e20566976616c6469000018766976616c64692e676f6c616e40676d61696c2e636f6d00000e4042756c6c697368476f6c616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714beea5d629df9274d3ca902971765b01d4fc6e7b9a45d1e3e1f9ed351e5bd8554d3c812457f2579d0": "0x000000000000000000000000000000000012506f6c6b61646f742048656c73696e6b690000001b706f6c6b61646f7468656c73696e6b6940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bef0fe62add82534c03802bc59091c05f317f32a796a720027867fc14e6554a7be0c19d7f107b332": "0x000000000000000000000000000000000009506f6c6b61646f7408686f74206b6579000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bef1561c49f96ee16aff72a0c7b124579def4ebf4515e735b0fe3eea919c682e6d71ed9c0474da7c": "0x00000000000000000000000000000000001a4576656e747320426f756e74792056322043757261746f727321506f6c6b61646f7420436f6d6d756e697479204576656e747320426f756e747900001f706f6c6b61646f746576656e7473626f756e747940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bf00044145d9dee7b6e0dce13b0bd22e17942bfa320877be89092b7e4f482fc68e1099109ca42938": "0x040300000002000000000000000000000000000000000f50494e4b20504f4f4c20233235340e4a757374696e205365656e657900001d6164766572746973696e674063726561747273747564696f732e636100000d4372656174724a757374696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bf0ff86d35aedc7b309145b12c7144a895495d82d6ba5cefc34b841859d6732af2759287266a2b19": "0x04000000000200000000000000000000000000000000084d65726d61696400001a406d65726d6169646f6e6c696e653a6d61747269782e6f7267196d65726d6169642e6f6e6c696e65407961686f6f2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bf1fb55b1ab52a450857c62463c3671fa8ef7253b3380ff662b0963c86dd9d227c10428b2d8c746c": "0x04030000000200000000000000000000000000000000054572696b0d44756f6e6720416e68205475000016747564756f6e672e66747540676d61696c2e636f6d00000c4572696b64756f6e673731000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bf74188b125a3306308b59a947eeb792acf6de27fa47a92ad37d53a15a7b97cd25f11c25455ba253": "0x00000000000000000000000000000000000e4c6f63616c436f696e53776170001a68747470733a2f2f6c6f63616c636f696e737761702e636f6d00177465616d406c6f63616c636f696e737761702e636f6d000010404c6f63616c436f696e537761705f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bf95774f0918328726c943cbc77122d3e8b6a1b92179ee776b972eae6f333697f254f369e150473f": "0x040300000002000000000000000000000000000000000c56616c69644f72616e676500000015646f75674076616c69646f72616e67652e6e657400000f56616c69644f72616e6765444f54000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bf995359aa3940443a8389c4cbbce2c9761722bdcb9a53d1e15023f95af6aecd5deba6377003f919": "0x040300000002000000000000000000000000000000000a4d636f6f6b42616c69134d61747468657720427279616e20436f6f6b0000126d636f6f6b383140676d61696c2e636f6d00000c404d436361746170756c74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714bfe2bdce4846e722a0c3719d913e12ae573407a1b2584247c88a292ffbba8cc862211f7defd8f10f": "0x04030000000200000000000000000000000000000000064e696e6a6100000020706f6c6b617373656d626c792e71346d356440706173736d61696c2e6e657400000d4062616c616e6365626f726e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c0095c67a6440edf74db9a1104a31f8b431ece5bcabbe3e508d22fe13670d32875ff6347a88d1388": "0x0400000000020000000000000000000000000000000015506f6c6b617363616e20466f756e646174696f6e14537469636874696e6720506f6c6b617363616e1668747470733a2f2f706f6c6b617363616e2e6f72670013696e666f40706f6c6b617363616e2e6f726700000e40706f6c6b617363616e6f7267000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c00b6c66130a22cc129c10048c90abf494afa1f1d2794d6afdd7e62e46d8bc073114bf42d1d64b3a": "0x0000000000000000000000000000000000084c7563616d657300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c029ee460555e12cb2692080bd814373a7c780dcc62922ba2e770c11a50bb1bb38fd3e69f0192a71": "0x040000000002000000000000000000000000000000001c416e756269204469676974616c204d61696e204964656e7469747900001940616e7562696469676974616c3a6d61747269782e6f726716696e666f40616e7562696469676974616c2e636f6d00000e40416e7562694469676974616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c02a0fb78fe9c0b6e60ae0c90c135ea717c10d2568a2ef847fbd201108f3f8b1a50608e83183574f": "0x040300000002000000000000000000000000000000000a4379706865727475780e4d6178696d65204576726172640000196379706865727475784070726f746f6e6d61696c2e636f6d00000a637970686572747578000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c0357d417cbd92c04e4c31b4c8d28f356b320a3186b82c979647af58b29a1573774e4bc7db1b7b5c": "0x040100000002000000000000000000000000000000001b566f6c74657265204361706974616c204d616e6167656d656e74001368747470733a2f2f766f6c746572652e63681540766f6c74657265353a6d61747269782e6f72671774726164696e67406f6465726d6174742e636c6f7564000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c038a43990a07bb81280a479ee3beca7af1636aca17582f30829782e2c9b1b9c72aaf8060563ab37": "0x040100000002000000000000000000000000000000000e494f53472056656e74757265730e494f53472056656e74757265731068747470733a2f2f696f73672e766311406a6f63793a6d61747269782e6f72670e68656c6c6f40696f73672e766300000840494f53475643000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c053a54bef0f336a9c217bcbcd61459955b47bafddefd16d131156fbb235c55d25f6374f0cd04610": "0x0000000000000000000000000000000000085175696e746f7300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c07690bc374b92f796acfe70c04eae75f56d603fa55ea58adc1a5be6f7780f6bb8b55ca788ad670f": "0x0000000000000000000000000000000000135375706572636f6c6f6e7920d0a16f72702e001968747470733a2f2f7375706572636f6c6f6e792e6e65742f164030786d61726b69616e3a6d61747269782e6f7267186d61726b69616e407375706572636f6c6f6e792e6e6574000010407375706572636f6c6f6e795f7673000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c07bdf560316d32b08ec72cbf62bb66f416f46f988e130585c834a381efdbb0755e30f47a2a0da5a": "0x040000000002000000000000000000000000000000000f50524f4f462e434f4d5055544552134d6f6f7365204c616273204c696d697465641768747470733a2f2f70726f6f662e636f6d7075746572001a76616c696461746f72734070726f6f662e636f6d707574657200000f4070726f6f66636f6d7075746572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c08fd019b5ff673488b8f51942016c72e537611887d58b4e6a44e0f435480472362dfc5244f08038": "0x040100000002000000000000000000000000000000000538425443053842544315687474703a2f2f7777772e386274632e636f6d2f000f77656e647940386274632e636f6d00000c40627463696e6368696e61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c0b698fe85dc7f07f46a2cda2040566d6299f92cdb1132a231dc2632ff84b711e3db8634c344f93e": "0x040000000002000000000000000000000000000000000d47696f726765416264616c610000001767696f726765616264616c6140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c14003ed5bc24012503db62affc9c535c960ee60970a8357b7764be446b2dc100a3f77b4370ce165": "0x040000000002000000000000000000000000000000000c506172697479204461746100000014646174612d7465616d407061726974792e696f00000d40646f746c616b655f78797a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1470541a3b4a961448bfb9be2787bb39b122d5b1707e83e535899c58d919a7afeba26968e12382f": "0x0400000000020000000000000000000000000000000004573346105765623320466f756e646174696f6e1968747470733a2f2f776562332e666f756e646174696f6e2f001661646d696e40776562332e666f756e646174696f6e000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1587ff616dd25a0560d57708a28e8cbb8794c04d67b66541c191d2effad8596ab63c619f257aa11": "0x040100000002000000000000000000000000000000000d384254432d504f4f4c2f30310d384254432d504f4f4c2f30311568747470733a2f2f7777772e386274632e636f6d000e79757a6240386274632e636f6d00000c40627463696e6368696e61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1639ae30e11b073cc003ff85b9d4030c01000f725b0ca03088c45c8e9ab90853c56d1c7b6cbc470": "0x00000000000000000000000000000000000b506f6c6b61446f74203100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c17a5e2df8e467cbba3f7fae8b3156ca2a3b0b84d3b2d922938bc2ab1405aea7813740a02146750b": "0x000000000000000000000000000000000017504f4c4b41444f542d4252415a494c2d4556454e545300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c17b80d313e3c78cce44b6b392394133943e063102b113e0577108fb9cb3000fe04faec3a3ad3934": "0x0400000000020000000000000000000000000000000012f09f8f942048454c494b4f4e20f09f8f940000144068656c696b6f6e3a6d61747269782e6f726710696e666f4068656c696b6f6e2e696f00000d4068656c696b6f6e6c616273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c19ae875baf61fab3e3a490c516b2a3582e6400e33f3eec42a12589958cbb86c87b23bc94710d21b": "0x04010000000200000000000000000000000000000000134361706974616c5374616b696e672e636f6d001b68747470733a2f2f6361706974616c7374616b696e672e636f6d1c406361706974616c5f7374616b696e673a6d61747269782e6f72671b737570706f7274406361706974616c7374616b696e672e636f6d000010404361706974616c5374616b696e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1a0c9593f15c53958c46d422c8c3d1692f51889cf64e2e32cadce1d1d341ca6196a8d4b18a9a354": "0x04000000000200000000000000000000000000000000064d49444153000014406d6964617338393a6d61747269782e6f7267156d69646173676f64383940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1a153b6113c7dfb000b93d72dcc12bd5577438c92a19c4778e12cfb8ada871a17694e5a2f86c374": "0x00000000000000000000000000000000001042494e414e43455f5354414b455f391042494e414e43455f5354414b455f39000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1ab9046e485e6d5466ca78e64f7df8847d8c192f3daddf32426f72a3baa4c3e320082afdb884134": "0x040000000002000000000000000000000000000000001a624c64204e6f646573207c20436861645374616b654b696e6700001340626c643735393a6d61747269782e6f72670000000a40624c644e6f646573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1ba3367c853c0b7056fef6e642069325ec72887efa3e22034c6890beebceb935852d776a3d08a30": "0x00000000000000000000000000000000000c506f6c6b617573642e696f0c506f6c6b617573642e696f1468747470733a2f2f706f6c6b617573642e696f0013706f6c6b6175736440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1d50c9830174ca88c232c91ef2a9983ba65c4b75bb86fcbae4d909900ea8aa06c3644ca1161db48": "0x040000000002000000000000000000000000000000000c65636c6573696f6d656c6f0e45636cc3a973696f204d656c6f00184065636c6573696f6d656c6f3a6d61747269782e6f7267000000094065636c3373696f1245636c6573696f4d656c6f4a756e696f720000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1d7648a3e3194dab2bdb0d774986625498e0b5fce860c7d58103bdb6b7b348054d525fddc3f3e7f": "0x000000000000000000000000000000000008576574657a2d3200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1d8ccc3c147543c94575c5627d7661400a3eccacf5440b5f877fa6099f4797a321523e3ade7215d": "0x00000000000000000000000000000000000448616f0948616f2044696e67000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1d9151efd144bc7c6e2ca836b28b68978aa39dc41d5b7ef3a7b8630a3e432d8ca99f24fd86cbd05": "0x040000000002000000000000000000000000000000000764616d736b790000001c63727970746f64616d736b794070726f746f6e6d61696c2e636f6d0000104068656c6c6f69747364616d736b79000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1e0ca80539b15c8796b851c8164a129b23282ca4b3bb694364b0bfb504e98b5d2ffc5140d58078e": "0x0400000000020000000000000000000000000000000019416e74692d5363616d205465616d2045786563757469766500000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c1e288e289580287886286c58d67217bdd854832d5e9f1b218dec6a0ff7e0b7573147ca94a233a0a": "0x04010000000200000000000000000000000000000000204269742e436f756e7472792026204d65746176657273652e4e6574776f726b0014687474703a2f2f6269742e636f756e7472792f000f6869406269742e636f756e74727900000f40626974646f74636f756e747279000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c2118a9d986b584ea8a3f426b435bc9415d2bae037d416a81c5b02c8cd6751549b37700c720aab15": "0x040300000002000000000000000000000000000000000e59616e6e204d6f7270686575730f59616e6e20506f696e636c6f7578000014636f6e74616374406f726962696b792e636f6d000009406f726962696b79000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c22c37787c57611ecc23ed33549e874ae7c7653fc5d95b3242dc7df5742664b4809e337a13126433": "0x040000000002000000000000000000000000000000001be29ca8f09f918de29ca8204461793720e29ca8f09f918de29ca800001140646179373a6d61747269782e6f726714616e746f6e406e6f766177616c6c65742e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c237c8c9d7bd4904bc267fea33668e3515a7c01f4acca67d73d30574b600a404d2b7210aaac85569": "0x040000000002000000000000000000000000000000000de29d84e29d84e29d84efb88f00001740696365636f6c646e61743a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c2846271fb408595e02d02d96dcacc309d5fb57319b26ca2334ce9d413929bb8ebbfe2ca5e614600": "0x0000000000000000000000000000000000094164696c2d446f7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c28f8abd364479314411b54c486afdd65bbe6f482fab68a28443c845c004a1c5d141314577d67d0a": "0x00000000000000000000000000000000000a4669676d656e742031001368747470733a2f2f6669676d656e742e696f000100000b4669676d656e745f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c2910faeab8bbb21aa92898eb263b225b26d5af6d0899f4e628161089ebc308887c48716ba248d5f": "0x00000000000000000000000000000000000d557273756c61207c2057334600001840757273756c613a776562332e666f756e646174696f6e17757273756c6140776562332e666f756e646174696f6e00000a40757273756c616f6b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c2a5b45c043e387bd2a0035cec74b2f90f7e72cab1fb16b6ba8317631976b138f7cced3e00668b0b": "0x04030000000200000000000000000000000000000000074d61676e65740f4d61676e6574204e6574776f726b0000136d6167706f7274406d6167706f72742e696f000010404d61676e65745f6e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c2c055e89a151d2372eecb8803cc4c49b49b0897c51190c9e32f9509e0bb2d7ee174378c7ebf3c46": "0x040000000002000000000000000000000000000000000541636169000016406163616973686962613a6d61747269782e6f72671461636169736869626140676d61696c2e636f6d00000d406c65633238333531303833000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c2ce802d4a097bfe0cd0767b5aaddecbfd90c086a3ae9c5efd0d2ab21b7d574ba605ded74c226125": "0x0000000000000000000000000000000000105361746f79616d6120233120444f54001768747470733a2f2f7361746f79616d612e746563682f00197361746f79616d612e7374616b6540676d61696c2e636f6d000010407361746f79616d615f7374616b65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c2ceac9b196d4a9b7a44e4c663e89e2c161dcf2b0fa46ec92f4017ec832e681ae5ba5f917dc54b58": "0x0401000000050000000000000000000000000000000006746f656e6700000016746f6d6d792e746f656e6740676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c31003d453d9cc76b1451f8962f76c9748e3ebeb43d5ba6cc25af020f2eb2316d6237115a3de72dc": "0x040300000002000000000000000000000000000000000762656533343418416c626572746f204e69636f6cc3a1732050656e61796f0012406265653334343a7061726974792e696f15616c626572746f407375676172637562652e6172000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c31738bafd189046ecef3a91c92840fb38e4cd3e20a604c75992cda08df8135416e5f4504e3d681d": "0x0403000000020000000000000000000000000000000003534b000000147477736b6875616e6740676d61696c2e636f6d00000b407477736b6875616e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c32ddea12298867b068f5973c7e1c0739ff08393aeba931748af1156690cd6db3bdccbca7eb82752": "0x040300000002000000000000000000000000000000000642414e584100000017706172746e657273686970734062616e78612e636f6d00000f4042616e78614f6666696369616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c3361cdef8ce970506b2f22dc927017e40cb8834d89a7eeb49a598a965f00fa38ceea3b1c6ed6637": "0x040300000002000000000000000000000000000000000454696e0a54696e204368756e672068747470733a2f2f6769746875622e636f6d2f6368756e677175616e74696e0014637174696e3039303340676d61696c2e636f6d00000e406368756e677175616e74696e000c4063686173656368756e6700", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c34aa17cf274fb0fce72013d22d568d7101bb1ecb5f43bb0d327619eb37337afdf24c5893fdfc06f": "0x00000000000000000000000000000000000c526567656e63792d3031351757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c38be37ff19e3a8a7617b9c6475f887ba801cee49b322a4d888224c8d0791bb0d5c999b6605e251a": "0x000000000000000000000000000000000009646f746875622d3109646f746875622d31000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c3a245cb2d2a7e9b56e751974614540cafc9bc363a690d2b4196c79399dd6f9750c202f671125358": "0x00000000000000000000000000000000000b5042412044616e69656c1444616e69656c20506572657a2047617263696100001767617263696164616e79313240676d61696c2e636f6d00000f40446563656e7472616c44616e69000a64616d616e74696e6f00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c3aa97983f6ecffc96625a0cbd0931ad831add3bcaa6320950385aec23b3854c6ce987de1c9f8837": "0x04010000000200000000000000000000000000000000085032502e4f5247085032502e4f52471068747470733a2f2f7032702e6f7267000f6c657473676f407032702e6f726700000e4050325076616c696461746f72000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c3b628138bb91f0585ae10011f28f84a7403b3ad52c14fae856077d3be1cb18b88081577a52d1ea7": "0x040100000005000000000000000000000000000000000d446f747479447265616d657200000016646f747479647265616d6572406475636b2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c3d4753480e3c1e936f6c360f80a8a9d2674a52440e1b41088663475ee6676796ce14bf3bcb4ae16": "0x040000000002000000000000000000000000000000000f7377656e7468656275696c646572001c687474703a2f2f7777772e7374616b65326275696c642e636f6d2f0000000009407377656e77333100097377656e3737353000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c3fdb0ea29d362e6b83d19e4a3ad242102f94a4452381300ace74c5d50fbdd9675a869401d3bff64": "0x040000000002000000000000000000000000000000000645726e69680000001a65726e6968656e656c626f7371756540676d61696c2e636f6d0000094065726e6968626f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c423231fa12d1b91a0606395f5c1ec90f373c26ff05ce375bc584608549d0dca2338dbc7a0fd814e": "0x00000000000000000000000000000000000450534317506f6c6b61646f74536d61727450617261436861696e1d68747470733a2f2f7777772e6f6d6e696274632e66696e616e63652f154069636f64657a6a623a6d61747269782e6f726714676176696e40636861696e6e65742e74656368000009404f6d6e69425443000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c4278e2e719c876ab430a1d38186a28164facec9010e36b1289eb6d3ad0f03f328188fd52bcb333a": "0x0403000000020000000000000000000000000000000005526973680e52697368616e74204b756d617200001a5269736840706f6c6b61646f746e6f77696e6469612e636f6d000011404f6666696369616c6c795f52697368000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c43f93ee2474bcdbd0a26ba88664597d50d7a6cc478135d84a261d3efe0338de3a5c71b77b18ed3f": "0x0000000000000000000000000000000000094475627374617264001568747470733a2f2f64756273746172642e636f6d000000000a406475627374617264000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c451757b8ae3669366f108b7fa5e27b25dd980f303f110194181f5725f724db5258f497e3fd7c135": "0x040100000002000000000000000000000000000000000c536572706163727970746f002168747470733a2f2f706f6c6b61646f742e736572706163727970746f2e636f6d124070736572723a6d61747269782e6f726715696e666f40736572706163727970746f2e636f6d00000d40736572706163727970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c47e6cc596c775ea049386f24725f9bf05946141f7baab4c1976ff5396ae250af174a04ef54a1962": "0x00000000000000000000000000000000000b4a6f616f2048617a696d0b4a6f616f2048617a696d0000146a6f616f68617a696d40676d61696c2e636f6d00000a6a6f616f68617a696d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c48c8cf649baa1400a8933d3f2164648399cc48cb8bb8c915abb94a2164c40ad6b48cee005f1cb6e": "0x040300000002000000000000000000000000000000000967696f74746f6466000000127665726966794067696f74746f2e78797a00000967696f74746f6466000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c4b4dd7580fa785794b57fffa14cca7b965e2179f8ae5a94a84ba15aabfd0ef3f67f8ebf74e9654d": "0x00000000000000000000000000000000001345726963207e44697374726163746976657e154572696320416c6578616e64657220486f6c7374000015657269632e686f6c737440706f7374656f2e646500000c40686f6c7374626c6f636b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c4dce5c7e5fc1055a4d9ffc123ea0bfd66aad52c6bc6f6740b23bcb36548761c5bbdbf156cd7577e": "0x040300000002000000000000000000000000000000000a4942432047726f7570114942432056656e7475726573204c5444000010626f624069626367726f75702e696f00000d404d6172696f4e617766616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c4f3bb9a6a26a4b7249aca910e224a87c14afb90980ef0db0a6b12c9d6b48c1acae111a1dda36617": "0x040000000002000000000000000000000000000000001050415241434841494e532e494e464f0000174070617261636861696e733a6d61747269782e6f72670000000c4070617261636861696e73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c516af68b4b46adaa5c4954040dafae7716b80bc7d5069fcbad863fe020a518d2afa27086f205285": "0x0000000000000000000000000000000000064e696d6974114e696d6974206b756d617220676172670000156e696d6974363139393340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c562a9eed81aa2dadae2b867564f01654946a095e69f0f49df1eb5c0efce5730cc3d1d83da3f4b09": "0x040000000002000000000000000000000000000000001af09f8c9020646563656e747261444f542e636f6d20f09f8c90001868747470733a2f2f646563656e747261646f742e636f6d001661646d696e40646563656e747261646f742e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c587ad64f66fbec7eec4cf006491dfbbfcb46eb5c8adc1a7535d554061f7f01c87a1e80f55598f67": "0x040300000002000000000000000000000000000000001344656e69732053756b686f7665726b686f760000001d64656e69732e73756b686f7665726b686f7640676d61696c2e636f6d0000000007636b636e696b00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c58b75eb4a8be7450236cc1bd6896c2a467da5f74331cc91ad6175f58d67c4fc3f50b2f8aa070490": "0x00000000000000000000000000000000000c526567656e63792d3031391757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c5b2faae0caa0f972c6f57e9289919d242aa985c1963f2b4040ddc57df3682d890657d130c035576": "0x0800000000020100000002000000000000000000000000000000000b43686f727573204f6e650e43686f727573204f6e652041471468747470733a2f2f63686f7275732e6f6e652f001168656c6c6f4063686f7275732e6f6e6500000b4043686f7275734f6e65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c5dad8e8b3b4327c8cc1b91e8946862c2c79915a4bc004926510fcf71c422fde977c0b0e9d9be40e": "0x00000000000000000000000000000000000976696b696976616c0956696b692056616c1068747470733a2f2f76696b2e696e6b144076696b6976616c3a6d61747269782e6f72671576696b696976616c406b6f6461646f742e78797a00000a4076696b696976616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c60c7e71ad50cedcc07b6eec14cd9119097bbf9bad28692a99e1c351e529c04793836183ce20bc45": "0x040300000002000000000000000000000000000000000a50617472696b2045420000001a74686973697370617472696b2e656240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c6b97ff91a535e9758b2c6f6766aab985078011ec8e759a965ab99168f7399f201b375400b9b0175": "0x040300000002000000000000000000000000000000000b576973656164766963650d73756d6974204b61706f6f7200001373756d697433356940676d61696c2e636f6d000011407769736561647669636573756d6974000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c6c19190ae451b5ae25e8c141e674ba58ca7fd0043366f6903488f3c585ff1180c9993eb896a3373": "0x040100000002000000000000000000000000000000000f44656c6567614e6574776f726b730f44656c6567614e6574776f726b731368747470733a2f2f64656c6567612e696f2f1440636f736d6175743a6d61747269782e6f72671664656c6567614070726f746f6e6d61696c2e636f6d0000104044656c6567614e6574776f726b73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c6da84d4d27caae500a8facc16c587e6b038173c4c85bcc36e7f506265012172163c29c9c0d8252d": "0x040300000002000000000000000000000000000000000a757365726d616e653111456476617264204e616674616c69657600001b6d69636861656c6e616674616c69657640676d61696c2e636f6d00001063727970746f5f757365726d616e65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c6ebdb2259f1d7d39ed0c9d18434f72d143cd55ae6d8add183e570d99674e334780029618f59c733": "0x04010000000200000000000000000000000000000000047a636f000000157a636f38394070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c704657f880bd0c07a34c5f475b5018f0d759283ff6a559713e8fe4fe672df009f3c812853b2ea35": "0x040300000002000000000000000000000000000000000f4d61726b6574204d6f62737465720d417368204461766964736f6e000016617368406d61726b65746d6f62737465722e636f6d0000104d61726b65744d6f6273746572554b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c7289bc44ade334e8db5c746c14cf05e182b10576a9ee765265366c3b7fd53c41d43640c97f4a8b8": "0x040100000002000000000000000000000000000000001144617277696e6961204e6574776f726b1144617277696e6961204e6574776f726b1a68747470733a2f2f64617277696e69612e6e6574776f726b2f001768656c6c6f4064617277696e69612e6e6574776f726b0000114044617277696e69614e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c74631fdb69272c020db24b51cc17362ffbaff228d977efd5fcc799ee877b1c6b8f144810555d25d": "0x040300000002000000000000000000000000000000000e436f696e74656c6567726170680000001c61647665727469736540636f696e74656c6567726170682e636f6d00000f40436f696e74656c656772617068000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c78dbbfac53841cd2c0d08e42e58247b57421f3239e0e192b21edaed4bca2458028c981634bdb607": "0x040000000002000000000000000000000000000000000f5354414b452048554c4bf09f91bd00001a407374616b6568756c6b69736d653a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c7a191740d407ab0362e53ccb59d8ef4125dcea1beb4a046797486adb9a7fe7c7496c8e8fe775d50": "0x000000000000000000000000000000000009444c494e4f444553174469737472696275746564204c656467657220496e632168747470733a2f2f64697374726962757465646c6564676572696e632e636f6d001f61646d696e4064697374726962757465646c6564676572696e632e636f6d00000c40646c6564676572696e63000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c7b59ed3c46118b33a5e67c5be0b1a151232d17929a6479d7d7187544a40c059664c6315e94c977c": "0x04030000000200000000000000000000000000000000064d696d6972001568747470733a2f2f6d696d69722e676c6f62616c001368656c6c6f406d696d69722e676c6f62616c00000e404d696d69725f676c6f62616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c7bda7f8eb7fbbb7b45897c0b5d286cf13edcac05f1089d8d5427964bd7deefb3e025857880b0d7f": "0x0000000000000000000000000000000000074b6c65766572001268747470733a2f2f6b6c657665722e696f0013666565646261636b406b6c657665722e696f00000b406b6c657665725f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c894982e15d43f07fe8bc73363ddd77dbd0717156237bd0bf9b94036ba003fb6c938495a9002df68": "0x040300000002000000000000000000000000000000000c456e636f646520436c75621e456e636f646520436c756220456475636174696f6e204c696d69746564000014616e74686f6e7940656e636f64652e636c756200000b656e636f6465636c7562000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c89d000515601ae39c82e3f47021dbab243cffebb35e9b44bf2dd301f06778861ce3ab634f607001": "0x00000000000000000000000000000000000a53757065726d614e5a14466572677573204d617274696e20506f77657200001e646f6f646c65722e616972637265772e30664069636c6f75642e636f6d00001140706f776572735f746861696c616e64000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c8c057868e022eb34c1bdac31e30cd50156586f5009d576c2efdc103be5ef0649d55d3b53941760e": "0x040100000002000000000000000000000000000000000d4b495241205374616b696e670e4b69726120436f7265204a53431568747470733a2f2f6b697261636f72652e636f6d15406b697261636f72653a6d61747269782e6f726716706172746e657273406b697261636f72652e636f6d00000b406b6972615f636f7265000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c8c34606dfbf7e1d0776a6d2dc585e66545d1538c8d8db9a221e7b67591790941d049992973e360d": "0x040300000002000000000000000000000000000000001250617472697a6961207c20414e414d495800001440646270617474793a6d61747269782e6f72671b70617472697a69612e646562656c6c6140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c90131946f8bd67ffa9d746a03245f55825fbe051be34eb34422bfa23f0b1d9317cdef087182ea5d": "0x040000000002000000000000000000000000000000001b494e46524153545255435455524520434f52504f524154494f4e000014407961796f692d763a6d61747269782e6f726720737570706f727440696e6672617374727563747572652d636f72702e636f6d00000c40494e4652415f434f5250000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c9662fb2af085bcff6225c6c2ffe4074df65d2adc2dcdbc576e33ee43c4089b0c034966426ff4377": "0x040000000002000000000000000000000000000000000d4e69636b205368756c68696e001968747470733a2f2f6e69636b7368756c68696e2e636f6d2f18406e69636b7368756c68696e3a6d61747269782e6f72670000000d406e69636b7368756c68696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c96a6c22d4b03e5980d0f3b4ac7e29a54b4b4c6638d0a865aba5da8d2881ceac549c5e278e62a90d": "0x040300000002000000000000000000000000000000000931676e3072346e64134761626f7220546a6f6e6720412048756e67000019672e762e746a6f6e676168756e6740676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c970e8c26c18107fc6a04790e90ef5ac434c22f9b7eaf891738931a7b7ad14d1949303ec79433850": "0x00000000000000000000000000000000000f4752422054726164657220444f5400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c974eec21c6558fc0a6497cc5b43976b51f865da2ed13f750640abd317a44fc0be1d06ba41036732": "0x0403000000020000000000000000000000000000000009636f6465307866660c4a756e67796f6e6720556d00001a69616e2e6a756e67796f6e672e756d40676d61696c2e636f6d000009636f646530786666000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c99fde22c2a91b8042361acf91eb62232ddd85db70e0408d9812869a015e7dfc8cb91f685ff8e64c": "0x040300000002000000000000000000000000000000000f706c617970726f6a6563742e696f0d4e696e61204272657a6e696b0000166e696e616272657a6e696b40676d61696c2e636f6d00000f706c617970726f6a6563745f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c9a3d35454d01d9a63206239ed004343cce85027f05c7fea001f12b65f257f11f53a414a1c1ee9a0": "0x04000000000200000000000000000000000000000000114368616f7344414f204f70656e476f760000000000000a404368616f7344414f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c9a62f8a51ee740a70e4021e1c2df9e68b5d0c0cb0a69668e45601c3dedb732ba64e020f34c96231": "0x00000000000000000000000000000000000753442d444f5400000012646173697a696e40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c9ceb72bb306b765f621771ddf37d482210b8c59617952eb1c2b40cfec55df47215231365186a057": "0x040000000002000000000000000000000000000000000f527573742053796e64696361746513527573742053796e646963617465204c4c431668747470733a2f2f7275737473796e64692e6361740013696e666f407275737473796e64692e636174000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c9d264aa4e49a5a7a2e546a96f0a016b5103ca34bfec8fd77988fde0c1f427b2ed97b9af2934a27b": "0x0403000000020000000000000000000000000000000008526963686c796e08526963686c796e00001c636172756c6173616e72696368796e363240676d61696c2e636f6d0000087263766572677a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714c9d4715fc01dd3c55e91aeb058d11c897e1f6f510cfcb62b781b19bbcad30eacb81f4976d95e9cff": "0x00000000000000000000000000000000000f45422043555241544f525320505000000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ca08516f7be365cf1a21174f3333c2bb416072b2c31d8f1e60f8e4ad3cf9546ab47e5b6fd060d303": "0x04010000000200000000000000000000000000000000054b757a6f0c4b68616c696441686d656400114067757a6f3a6d61747269782e6f7267144261642e39342e697140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ca3402e483e6170efee3129dacd1d1b4820ea6f516d7bfaccc4b64b16730ac6282daf1dc476e0001": "0x04030000000200000000000000000000000000000000054d61726b0c4d61726b204361636869610000186d61726b2e652e63616368696140676d61696c2e636f6d00000e406d61726b5f6361636869615f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ca43fffc2927f1f788d340852d044285b8229e36012b665ca5b75412d5e2e83d66994b784b1f3f73": "0x00000000000000000000000000000000000b44616e20436f6d6963730944616e2047616b680017406765656b5f626c6f636b3a6d61747269782e6f726713646f6d313235646640676d61696c2e636f6d00001040506f6c6b61646f74436f6d696373000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ca550b4f7a101e7f344941abe7c5bd2da1688a7abf18b5244d0f524c5e085f5abb2257ca0c038f04": "0x040000000002000000000000000000000000000000000a683478407068616c610948616e672059696e1d68747470733a2f2f6769746875622e636f6d2f68347833726f746162164068347833726f7461623a6d61747269782e6f72671668616e6779696e407068616c612e6e6574776f726b00000a4062676d7368616e61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714caa0b0ec39cf59af4e1550920067048086a9f30f799a1749508e222ad1a9d999f586e6f3e782c932": "0x040000000002000000000000000000000000000000000b706f6c6b61776f726c6400001740706f6c6b61776f726c643a6d61747269782e6f7267177869616f6a696540706f6c6b61776f726c642e6f726700001040706f6c6b61776f726c645f6f7267000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714caae41ec132a0861a69de95059adda24b9b38dbe06908378c09ef0d917c7188992ed339e0e530076": "0x00000000000000000000000000000000000647756363690f4d617572697a696f204775636369000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cac1a4bc9a87003e757945316854910d449179fda9aedb37ab23a3dc9f8330ad77fcec3f4b18cc03": "0x040000000002000000000000000000000000000000001d4f524d4c20536563757269747920426f756e74792043757261746f720000001468656c6c6f406163616c612e6e6574776f726b000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714caf961ad1b6e226da072610f1e1ffade38a6d64df55a89e6b07f65ed2be0eb6efffdade4ca576c12": "0x040300000002000000000000000000000000000000000b4b6972696c6c5f6e6577000014406272797a67616c3a6d61747269782e6f72671b6b6972696c6c2e6272797a67616c6f7640676d61696c2e636f6d000011404b6972696c6c4272797a67616c6f76000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cb1015e06f27ef398638bcb5e5ae5e04954d7fa2d29ccedf7f323482573198732af0b3fe32f8da03": "0x040300000002000000000000000000000000000000000973656164616e646100001140646f6e616c3a7061726974792e696f14646f6e616c6d4073656164616e64612e64657600000940646f6d756972690973656164616e64610000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cb2e515a03279de32c2aabc0d8257e2dc928553a73d8bb18412665692e53505852328d0aa1126714": "0x00000000000000000000000000000000000e5068616c61204e6574776f726b0e5068616c61204e6574776f726b1668747470733a2f2f7068616c612e6e6574776f726b0016737570706f7274407068616c612e6e6574776f726b00000e405068616c614e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cb4a358712ffad148aee4e164d5d70ac67308f303c7e063e9156903e42c1087bbc530447487fa47f": "0x040000000002000000000000000000000000000000000b6c6f6c6d637368697a7a0000000000000c406c6f6c6d637368697a7a000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cb55ee48ce2e7cebb40d3324de0087ed64fa4e91ebd1ea92ad87e12c0903904285d5db76cff97f54": "0x000000000000000000000000000000000009586161535f444f5400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cb61c8ed9c1a667e3c672a7ed8c3771e42829418dd6781ed468ea3612fbf0512e847fed2a8995535": "0x00000000000000000000000000000000000a494243204d656469610d456e626c6f63204d6564696100000e617669406962632e6d65646961000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cbb6063367ee9c92e81006772a8947817db9e6676f0d67eae9cff6354f2d6ac9392132de4b2a1034": "0x040100000002000000000000000000000000000000000e4956594e4554574f524b2e494f001668747470733a2f2f6976796e6574776f726b2e696f0013696e666f406976796e6574776f726b2e696f00000e404976794e6574776f726b494f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cbd0c872a0daf479a4848ac04c2ad298cfcbf27ade0ddebb2cc3b37b090dd933a1e988b7a3ead075": "0x040000000002000000000000000000000000000000000d436170742d4861726c6f636b0e4c756361204d6172726f63636f1d68747470733a2f2f7777772e636170742d6861726c6f636b2e636f6d1940636170742d6861726c6f636b3a6d61747269782e6f72671a706f6c6b61646f7440636170742d6861726c6f636b2e636f6d00001040546865436170744861726c6f636b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cc2507f5d267891bbcb50c2942b0d5c8095f4f5ed87d2dba92bbb5c46b78dcdde51fe9fd58c5113c": "0x0403000000020000000000000000000000000000000008526567696f6e5800000015737570706f727440726567696f6e782e7465636800000c526567696f6e584c616273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cc59dc16328b27565822491aa322469edf3e2fe31839f3daebb226c62fbff8c1d5c0a79d18853e2d": "0x00000000000000000000000000000000000644796c616e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cc6a7f8747bd5de8a3a5b1cbef050a59893598ed0817fcc42c7f4faee9d0bdef4a9325a2ad4d3b8c": "0x0400000000020000000000000000000000000000000019444f542047616d657320426f756e74792043757261746f7200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cc6bdb23395a5660e623a613a8a3fd529aaf39b6041c5dc86cae0a8fed13606d621366d876c37773": "0x040000000002000000000000000000000000000000000f4d696775656c204d617271756573001c68747470733a2f2f7777772e6b696e6572612e6e6574776f726b2f001c6d696775656c40696e76697369626c6568616e646c61622e6f7267000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cc9df1794dc10741c11b9f12fa2747bff85d085211256f60824c4cc1d59459afec8119184fcbba15": "0x00000000000000000000000000000000001b53595354454d20434f4c4c41544f5220505552452050524f585900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ccb59fb10ff3b3c02e05b827a4ba848e1e43ae944fce00c7d0d4c96a38a01eb02cf867bfde7e6b5d": "0x0400000000020000000000000000000000000000000005434354460000124068657866663a6d61747269782e6f726716636f6e746163744063727970746f6374662e6f726700000b4043727970746f437466000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cd131804ccfea9ef22efc513587234d063cb685c5b68172893193192942a565b241626b207f27637": "0x000000000000000000000000000000000008476f6e7a616c6f00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cd50be1c014b9821d220801e6e3f65c962363dd64baefae8b383799b3247b29892b15288c476e336": "0x040100000002000000000000000000000000000000000c574f4f4b5926574f4e4b590000001e6a6f686e736f6e2e66696e616e6369616c393940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cd5bc0fba06a116f64cc5984790978efca45099329e5dec0365922e84982e8a277ca4c58756ddb48": "0x00000000000000000000000000000000000b4c616b65727320444f5400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cd624b47e3069b393e8faae4c5713c72aea65d52aa1616d3e918dee3819fbbe08cd4c76dbd754a50": "0x040000000002000000000000000000000000000000001450726f5374616b6572732e636f6d20f09f928e001768747470733a2f2f70726f7374616b6572732e636f6d1b4070726f7374616b6572732e636f6d3a6d61747269782e6f726718706f6c6b61646f744070726f7374616b6572732e636f6d00000f4050726f5374616b657273436f6d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cd936bdf7e1b9fa87ef3b54d0328f6a7928f8536c2dccb07e8796aca7f3d7e126659105d764d4d98": "0x00000000000000000000000000000000000c526567656e63792d3030351757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ce2ddd4fd1d67791dcba36567d6fbd47af19d77054475a0f4c56dbccab4c13dacd22410f8275da08": "0x040300000002000000000000000000000000000000000d4272756e6f2047616c76616f0d4272756e6f2047616c76616f0019406272756e6f7067616c76616f3a6d61747269782e6f7267176272756e6f7067616c76616f40676d61696c2e636f6d00000e406272756e6f7067616c76616f000d6272756e6f7067616c76616f00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ce2ff9c38ccd13f3964e98590170e1cd1b9a476ed5147431261ac43ac0a9931ac9a9593027619612": "0x0000000000000000000000000000000000086a616d6d61727300000013636363697272757340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ce8bead92c07b351cc83588b4681c7aa4e83598809e195fbce7b0bca4321fe09db96dd814de1fd36": "0x04030000000200000000000000000000000000000000084261766f766e61135061756c2053746576656e20436f74746f6e000017616c70656e6c6967687437304070726f746f6e2e6d6500000b40426564666f72643336000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ce9a50917cf77bb19681fe005baa3099a7d9c03a46e539b173d2b3de75b11e86cd5fbb7d4e92993c": "0x0403000000020000000000000000000000000000000009506564726f37373706506564726f1968747470733a2f2f7777772e726d74657272612e6f72672f00196c69676874736f6e656d7573696340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cedae9c81b8ef8ff42be75cb933073a967d8cb8c6c723028208a678c5a58f5e8f49a237eb33e1654": "0x040000000002000000000000000000000000000000000d4a61792043687261776e6e610d4a61792043687261776e6e610000196865796a617963687261776e6e6140676d61696c2e636f6d00000a40476c646e43616c66000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cee83f43ea0dd41a22b7a9ef681c4a3c1743e5c343ff5b6f8aec3dc43bb0d49e29243d79ed406365": "0x04030000000200000000000000000000000000000000054b494c4e054b696c6e1068747470733a2f2f6b696c6e2e66690010636f6e74616374406b696c6e2e666900000e404b696c6e5f66696e616e6365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cf1074d3968196ede68f570f13d4581940602ee3ea8ebb7c3fca3ee8e21ddf90084b3df3a3ae7336": "0x040000000002000000000000000000000000000000000653696f33340000124073696f33343a6d61747269782e6f72670f696e666f4073696f33342e6f72670000084053696f333437000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cf421cf5662793c2d407fda03feb041adbdd1bed331b9c92168c8d21f31ae1639d0332433d5e2b07": "0x00000000000000000000000000000000000847726162626572001d68747470733a2f2f6170702e677261626265722e6e6574776f726b2f00146e696b40677261626265722e6e6574776f726b00001140677261626265726f6666696369616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cf4ef5c9ad9aa87e3ac8adb41dbcf04f2d67294fa621940d040400987e05cff6326b1318939db159": "0x040100000002000000000000000000000000000000000d466f72626f6c6520f09f8ea00d466f72626f6c6520f09f8ea01468747470733a2f2f666f72626f6c652e636f6d16406b77756e7965756e673a6d61747269782e6f726711696e666f40666f72626f6c652e636f6d00000940666f72626f6c65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cf81097fae6d315cba35e9bf428de9355a0e6fd6ef56ab5e0dabad5f5a8eac3af6f5202e84ac6b47": "0x04010000000200000000000000000000000000000000104e4f54415241535042455252595049000015406b736368657965723a6d61747269782e6f72671f6e6f746172617370626572727970694070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cf8a21b0cdae6e2d1e6e3b7ca66e70b75a1841f3ee48553b310484d34321335e55479232449d6f50": "0x040000000002000000000000000000000000000000001064656967656e76656b746f722e696f0000194064656967656e76656b746f723a6d61747269782e6f726715696e666f4064656967656e76656b746f722e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cfa782accd3d75bebe776c10ad0e1fe7f606cfe42448e02cdb640c21214ea6c0c99df36ec0ee3d0d": "0x040100000002000000000000000000000000000000000f436f6d707574652043727970746f001a68747470733a2f2f636f6d7075746563727970746f2e636f6d0018636f6d7075746563727970746f40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cfac989e84b5ad935ee1e16ea093ea043d7f67a6f34f440c5fb921b56b54e81c177898d348685b51": "0x04000000000200000000000000000000000000000000095354414b452e5355000016406d722e6f776e6167653a6d61747269782e6f72671d7374616b652e736f766965742e756e696f6e40676d61696c2e636f6d00000a407374616b655f7375000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cfc0cc32c14f6b1b1c2a5f648afea2a94286c17f6c60d16c9ef8511fa4ae88a54ce2748b6c8fa90f": "0x080000000002010000000200000000000000000000000000000000054166726900000f406166723a7463686e63732e6465126166726940636861696e736166652e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714cfe3b56da886c4b6e23cf83af2b043696cefcbe2ffe680eba45ccf26d6d9a354d742bafd45aaf2e7": "0x00000000000000000000000000000000000c50414c2043757261746f72001f68747470733a2f2f706f6c6b61646f746173737572616e63652e636f6d2f000000001140506f6c6b61646f7441737375726564000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d094e3cac725c344ac1b1e12bfb60e8225efa102d2f6f74ccfa53d583571363505fb54feeacf8c2e": "0x00000000000000000000000000000000000d504f4c4b41444f54204d4143114d617274696e204d63446f6e6f7567680000196d61636d63646f6e6f75676840686f746d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d09eaf34f53d7c401480bc228ee751c1aca34061c4952efb304aa94beed8e38fd9c5e693f62c3f26": "0x04030000000200000000000000000000000000000000066365736172066365736172000016636573617267653133303240676d61696c2e636f6d00000a43657361725f476573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d0d3d5efedb29953decf71909282816105360e12c52694c8e39f30f82532be18b3e32e3e435dbf08": "0x0000000000000000000000000000000000064b43435f3300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d1067d55a33c02831232508adcaf57c6e78a850f9d715e3694b52000ce537832eb55c7a59f859e13": "0x040000000002000000000000000000000000000000000a524f544b4f2e4e4554001268747470733a2f2f726f746b6f2e6e657418406869746368686f6f6b65723a6d61747269782e6f72670d687140726f746b6f2e6e657400000f40726f746b6f6e6574776f726b73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d113bb3ee11e240450c816dc61aa6fa2afb1d48ab14435942be7296f10b310653c80325af700c166": "0x04000000000200000000000000000000000000000000134b6972696c6c5f5448452047656e6572616c00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d16f6c8ce2379b06a375190836f6df983719441a0f32b28a7448ed785bcfdb67c5f6061b9654335f": "0x000000000000000000000000000000000010416c69616e7a612048697370616e6100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d183e95ab708f0d6785f4c6690a6c2bde954d46c627698f5730072791cb5485116c678d89974230c": "0x040000000002000000000000000000000000000000000c417263656d204d617269730000000f6e696368406b61786f6e2e6e6574000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d1eaa7d142bf1e42a200be596abc1fb84833a9440274227b2f9709e573abf14d0c6b2fc58ed3a50f": "0x040000000002000000000000000000000000000000000d47726567205a61697473657600001540677a6169747365763a6d61747269782e6f726712677a40756e697175652e6e6574776f726b000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d1ee388cb4a93ade8c16e2cb1309a3ab9b697e3c80a5cad5dfded90b04e2d8943f2e0835af140a77": "0x040300000002000000000000000000000000000000000856616c65726969125379646f726368756b2056616c6572696900001473766d31393835756140676d61696c2e636f6d000011405369646f726368756b76616c657232000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d1f6cf7981174df67aa5506d9391c78c760cf7490a5f7412a1fea6cb4969119a11f49429818b1d97": "0x00000000000000000000000000000000000631345745421757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d25b2b83caf5ac06d857fcac7bd9bb03551d70b9743895a98b74b06e54bdc34f1b27ab240356857d": "0x04000000000200000000000000000000000000000000065465736c610000001b7465736c612e76616c69646174696f6e40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d27b00c2f56f7b336c31b105bf4566e9837d11fc54b523f7b3ec8993f8c880b1e0c283d7bcd0aa53": "0x0401000000020000000000000000000000000000000011e2999e47616d655468656f7279e2999c0000184067616d652e7468656f72793a6d61747269782e6f7267136d61696c4067616d657468656f72792e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d2f4c0196c97de9d14eb0f8e9dd9e3ad05eb774e3158b4c432bdbd5f64f0fb78a25b4d1863c0f468": "0x040100000002000000000000000000000000000000000f4c6f7569736520572e2052656564144c6f75697365205761727769636b20526565641668747470733a2f2f7374617961666c6f61742e696f00156c6f75697365407374617961666c6f61742e696f0000114041666c6f6174546178437265646974000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d2f5f529ff74e1246425fc08d539497b1f466d8528211fd89b2e222eda9d39f7bd1967bf9e6f5f16": "0x000000000000000000000000000000000006486572736800000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d343694d94be95e2744405c2356641a0d373433e220c5b03d3ffd2bc3e528574e19068b4c2490614": "0x040300000002000000000000000000000000000000000847c3bc726b616e1347c3bc726b616e2053656e656d6fc49f6c7500001a6775726b616e73656e656d6f676c7540676d61696c2e636f6d000009307863666c6f6b69000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d3a269b657e1dfd090a4c78f16c247b4b438a25734d0479b32c196cacb25ecc95a79480dfc6cee7c": "0x04000000000200000000000000000000000000000000077368616d6230000013407368616d62303a6d61747269782e6f726713722e7261616a657940676d61696c2e636f6d00000940307368616d6230000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d40d5206d8ab23bb285de1abe8f2e26bceb56db8969b332803ecfba0d4baf49e1e9c31c80748747a": "0x00000000000000000000000000000000000f55542046696e74656368204c61620f55542046696e74656368204c6162001940757466696e746563686c61623a6d61747269782e6f726712636573617265407574657861732e656475000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d435a9777e7ecc393c5862ed65c524b7bb3564776a81904218f44f3d7c35162a608e39dbadbcda05": "0x0000000000000000000000000000000000084e61742d446f74134e6f7220536166696e617a20417a726169650000166e73612e70796e7574323840676d61696c2e636f6d00000a40316d5f39794e3437000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d43703150f1fcc536a21a0e28cbaa3132b3abfdc4240eaf50bd237467690de245441612261e37571": "0x040000000002000000000000000000000000000000000d456c6f646965207c2057334600001840656c6f6469653a776562332e666f756e646174696f6e17656c6f64696540776562332e666f756e646174696f6e000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d44074de089225b9404fb63cfb2153551f4497165a2262d420453628d4c0f790c9b11ca4748bb139": "0x040300000002000000000000000000000000000000001ff09f928e20537061726b6c696e6720426c6f636b636861696e20f09f92bc1a537061726b6c696e6720426c6f636b636861696e20f09f928e00001a7a766167656c736b79657667656e7940676d61696c2e636f6d00000b40457667656e79795f41000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d476d3200736c2bcc42bd98a8029cf3787ee280a8980b4a8d5f152ebc2705cb48b4c7ee6daad0268": "0x000000000000000000000000000000000014506f6c6b61446f742043726f77644c6f616e7300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d4859e78fba22c21ac2091f6d5251183c62941cde5cd3e10ffcb6d418d42e03ebc6f1945a817f76e": "0x040300000002000000000000000000000000000000000a4d6574686f642e67670f53636f7474204d634d696c6c616e1768747470733a2f2f7777772e6d6574686f642e67672f0013706f6c6b61646f74406d6574686f642e6767000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d4a79ae0ef45ec63a455ccc77072570b273044973e0ab6f56d3325421584a479eed68f84603e034f": "0x08010000000203000000020000000000000000000000000000000007417373657458001368747470733a2f2f61737365742d782e696f00107465616d4061737365742d782e696f00000b40444f54417373657458001334313630333438343133393436373537313200", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d4ed4dd340a2cebbbc486ed2f394da6e6b58b130687b48d3d19f756ba6d0655d37bf58ff0f59f974": "0x04000000000200000000000000000000000000000000124164616d5f436c61795f53746565626572124164616d20436c6179205374656562657200154061737465656265723a6d61747269782e6f7267176164616d2e7374656562657240676d61696c2e636f6d00000e406164616d7374656562657231000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d4f4dfc6da461b20aa72c321b20bbd5b78b14f6fd800017bca47190956eb42ada2a4d8f8a8ca994d": "0x040000000002000000000000000000000000000000000df09f97bb4261736563616d7000001840776f6c667374726f6d32373a6d61747269782e6f72671b6261736563616d702e7374616b696e6740676d61696c2e636f6d000011404261736563616d705374616b696e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d509c5c59195b1ec5c3739d60301126756a7510e34f9d656d4435cd4fe64bbd001f1f3473bc9c333": "0x04000000000200000000000000000000000000000000055a656b65000017407a656b653a6d61747269782e7061726974792e696f0f7a656b65407061726974792e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d5150021507a071f86420f7843bff8fcee7d1bafae828d0d0c3668bfca8150820be3e774e8aa8c2c": "0x040000000002000000000000000000000000000000000b5472616e736973746f721c5472616e736973746f7220506f6c6b61646f74204e412c204c4c4300001179407472616e736973746f722e777466000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d53a56fa8630064970eb89fbedb6567f5061f23f2da6c73feb710676d618a205a28d643451c48a72": "0x040300000002000000000000000000000000000000000a616476657269636b790f61647665726c6976652073726c73000014696e666f2b646f744061647665722e6c69766500000d61647665726c69766573726c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d5ab2f60e5f823fd07cd066334cc9ed0e17a3a845a9efb144c25c9741d63102d88380c7f20ecd7b9": "0x040000000002000000000000000000000000000000000d44617070466f726365204d530000001464617070666f72636540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d5e66d23eb784c1150fa056fe8636d5041e3a460e63839603087bf789ce60514f00bb2473b728e4b": "0x000000000000000000000000000000000014506f6c6b61646f74202d2050432047616d65721e53c3a97267696f204f74c3a176696f20466f6e736563612053696c76612168747470733a2f2f7777772e796f75747562652e636f6d2f6368616e6e656c2f001973657267696f2e6f746176696f4069636c6f75642e636f6d00000f4061706f6c6c6f74686562756c6c000d63726970746f6d6f6564617300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d5fdc8ab7d97949ee424b2ee4c8a8fb3334248367a7a7e5c2d236205368cd4aee4e8ae274fc45566": "0x040300000002000000000000000000000000000000000c4b616d70655369676e6572144368726973746f7068204b616d70697473636800001a6368726973746f70684073637974616c652e6469676974616c00000844614b616d7065000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d6392158cf43d6cab4358371ed445b0650e6b0a3c749c3a8674db5eeccc75103beb78b0a903bf32b": "0x00000000000000000000000000000000000a4d6574616b6f76616e135669676e6573682053756e6461726573616e1668747470733a2f2f6d6574616b6f76616e2e636f6d000000000b406d6574616b6f76616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d64d4d86c0b29268f2931a66c7207e83365c6425ff0756499e2130ad00b384731c7290c8536c53ed": "0x04030000000200000000000000000000000000000000084445444341505308444544434150531368747470733a2f2f6465642e67616d65732f0013444544434150535840676d61696c2e636f6d0000094044454443415053000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d6ad5303bac6267692286c3c817a7beb00fd0398384e170dcdb5dfcccae635adc3d8426119594a0a": "0x000000000000000000000000000000000008546f706f6c6b6100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d6bff5ea3e68b58898185ec550ab27fe12285719dc1bfc83a8dd7d3e77a32c0fb4e12be09b79ad48": "0x040100000005000000000000000000000000000000000e41454d20416c676f726974686d0e41454d20416c676f726974686d1c68747470733a2f2f7777772e61656d616c676f726974686d2e696f0018737570706f72744061656d616c676f726974686d2e696f00000f4041454d5f416c676f726974686d000d6a616b7562736177637a756b00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d70ae159e1aa09aa28863c0c3e58d40a623b4cabef57d2ba807fdb703c4fb481d5fce365858e7224": "0x000000000000000000000000000000000006566974697300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d74bea4c3bf5621814d0ddb81faf43b169f397727a277d7cace318bdfce287d90314e72e4dce490c": "0x00000000000000000000000000000000000c4a61636b7974617572757300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d789194338d34070928263bfc144cfad6e7e62923f27cd0e8df2af4bee9eeb4784dbf7a48b0a296c": "0x00000000000000000000000000000000000a6d61645f616e676c650d53616368696e205465647761000019746564776173616368696e31323340676d61696c2e636f6d000000000a6d61645f616e676c6500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d78e3922994e68f87b1c17d66cb08fdf1d84ccca7767bb71ec36658ff95c522951b103c8cd8623ea": "0x000000000000000000000000000000000009596f6b6575736f6e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d7906ae903234547463a9cd3e7cec50ccc93515557ab58221d20c67a409583428ad67caff9415107": "0x0401000000020000000000000000000000000000000011436f68696261436f6e74726f6c6c65720e4d616179616e204b65736865741468747470733a2f2f6d616179616e6b2e636f6d14406d616179616e6b3a6d61747269782e6f7267136d616179616e406d616179616e6b2e636f6d00000e406d616179616e6b6573686574000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d7a036c9790a85a62c0b2cfdc7507c42c88c22c0eadedd30251b090cc8de670c436ecd91186b5136": "0x000000000000000000000000000000000003421900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d7a5e201687ab46302982c3a62acf008c3ac7793286c0f7b93efeac8de80e9ec9733bfd477148707": "0x0403000000020000000000000000000000000000000007616277726c6400000018616268696d616e797540646f75726f6c6162732e78797a000008616277726c645f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d7b596e66afccd2bd89ea70e822e338b7519379e4a4685595da674fd167b7bd12dbfd10c9bc2b50e": "0x040100000002000000000000000000000000000000000d44617265646576696c337837000000134372797970746f7040676d61696c2e636f6d00000e4044617265646576696c337837000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d7efccf788fb1a6528d50241999da5b300f01f3004a67a25a11854608f1f437ab86ed2e115243a43": "0x040300000002000000000000000000000000000000000a4b7261746973743073000000186b72617469737430736e66747340676d61696c2e636f6d00000a6b7261746973743073000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d8462e30b1801504cc3d040787c25df74513d303f228e590e9e0156cc54790d6720d607420e80165": "0x04030000000200000000000000000000000000000000094c6176656e6465720000001c6c6176656e64657265737468657232303240676d61696c2e636f6d000011406c6176656e64657265737468657231000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d84deb4759f2e2e9f6a7fa830da55dde09b411ff877ed0ee8fd1ceb2009067ab5bc0ffdc54af4065": "0x0400000000020000000000000000000000000000000011416e6b616e2028706f6c6b61646f742900001140616e6b616e3a7061726974792e696f10616e6b616e407061726974792e696f00000006616e6b346e0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d85c06897a6dfc1b8cee324d4a0f2bce600a681a3ba2f4cf507c83012008f01557f2c5e413b76066": "0x040000000002000000000000000000000000000000000e4441524b5f504f4c4b41444f54000019406461726b6c657373323030313a6d61747269782e6f7267196461726b6c65737363726970746f40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d883005234a96e92bc64065524532ed9e805fb0d39a5c0199216b52871168e5e4d0ab612f8797d61": "0x040100000002000000000000000000000000000000000741524b5041521141726b61646979205061726f6e79616e00134061726b616469793a7061726974792e696f1261726b61646979407061726974792e696f0000000761726b7061720000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d8e0253a5ce94a9a125858e5bcf61ff597c01f811fe12e66f34e0b2ff135e48a03c51c3234afe97f": "0x04030000000200000000000000000000000000000000084d656e696c696b0f4d656e696c696b204573686574750014406d656e696c696b3a6d61747269782e6f726713737079786d656e6940676d61696c2e636f6d00000f404d656e696c696b457368657475000a406d656e696c696b3300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d8e02c95f42c870728c57701aff086e2d4d00536a35ebb0dea7732d5bd285c63fac7197b6bf3e37e": "0x00000000000000000000000000000000000a4c7567616e6f646573001668747470733a2f2f6c7567616e6f6465732e636f6d0013696e666f406c7567616e6f6465732e636f6d00000b406c7567616e6f646573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d90301728ff0374ae4d7ebfe2f9bce87c9b49975fe7de84393c376b29ee297be934ac540d8a6381b": "0x040000000002000000000000000000000000000000000a434f494e53494445520000000000000f406a6f696e636f696e7369646572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d9275dddf1009186a0c8063be933d442a3453e47d22a9d53344c604f71cb0f70c1f67c0129cf6137": "0x040300000002000000000000000000000000000000000a63727970746f6d616b0c446965676f204d6172696e000015646965676f6d616b40686f746d61696c2e636f6d00000e40446965676f6d616b5f6d6564000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d9402b8cd99c1552705e3020123277ead705580b26edbab0e534d5941db23a8216a430962ada9519": "0x00000000000000000000000000000000000a53616e74697468616d00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d9cef13374a70e655010efb7049583595fbe3da57dc5db048e590c28f107e5e4fa2bdcc4b1293f6f": "0x040000000002000000000000000000000000000000000b43727970746f6c6f677900000014746563684063727970746f6c6f67792e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d9e0312b39961bc6726a98c27f84bf42fc842794a925418335fc9fe3badb1f535dce9c9d9efcc02d": "0x040000000002000000000000000000000000000000000b5473756b6920f09f8c9500000019636f6e74616374407473756b697374616b696e672e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714d9ebec847667f8a9dc028a7c1bfd7e24a64a11564b7384e571e27e33bddbae91f978ac1a6ead7310": "0x00000000000000000000000000000000000a5368756d6f20436875000000107368756d6f2e63687540706d2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714da23f4f6f4d9e6bc96140a201be6f41e63c5b3bf6b02f67da3f232c6715397302494f894f964ac78": "0x040300000002000000000000000000000000000000000d56696e6365436f72736963611856696e63656e74204469204769616d626174746973746100001076696e6365407061726974792e696f0000114056696e63656e7444694769616d6231000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714da40d28d53abbfcf16f9415c34da11ca5a35f3f18627af4ef312d90777ed086ea20e364b11656921": "0x040300000002000000000000000000000000000000000667656e67650d476f6e7a616c6f204574736500001667656e67656b7573616d6140676d61696c2e636f6d00000d4067656e67656b7573616d61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714da588541e0520af8802a229c888897bfe30ff149788e0dce8e078f2f4ebc0533dc44ca4c9960100b": "0x000000000000000000000000000000000018506f6c6b61646f74404574685461697065695f3230323400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714da6dfa4d6fcbad64768ac003ffdf15238a61c42e781d16331ca3cb2805e06f602469206d6a795734": "0x00000000000000000000000000000000001c62616c73656c6c732f506572646f6d6f2d70617261636861696e731e6672616e636973636f206a6f73652062616c73656c6c73206c6f70657a0000146662616c73656c6c73407961686f6f2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714daa67a441fadf00ca2b09b0a84ba632e3745eacd5cc7dde15c73b61255bd6376177a907b4e830079": "0x00000000000000000000000000000000000c4b6f746f2053747564696f0c4b6f746f2053747564696f1468747470733a2f2f6b6f746f2e73747564696f0000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714daa6f458469f7726be386bfcdd57a31913902c8092cbb4e721e9bdcd6babebe0831a4ff52c260431": "0x0400000000020000000000000000000000000000000004737077000018407370656c6c7765617665723a6d61747269782e6f7267147370656c6c77656176657240676d782e6e6574000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714daadc5f0b3a48319d4f44a50f35f9340aad2b3666011f78afa719f5e80a0c4f3969525729c35e654": "0x0000000000000000000000000000000000174a61636b20506f6c6b61646f74202f204b7573616d6100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714daf5619ede74e518e90ce79e0167f82e66250b1cfab388aa86a2e2b1e893907b19932e6751a08e04": "0x040300000002000000000000000000000000000000000c416c6c696e43727970746f001968747470733a2f2f616c6c696e63727970746f2e636f6d2f0015696e666f40616c6c696e63727970746f2e636f6d000011405265616c416c6c696e43727970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714db02485938505b5136bae49de1ebc06355938e2cfe64ae4b27d6741c070bf817042eaeeae226d741": "0x04030000000200000000000000000000000000000000065742526f620e526f6265727420486f6c6d6573000014686f6c6d6573726a406c6976652e636f2e756b00000d40486f6c6d6573726a526f62000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714db20fbbd4ec3b172c1df5c7e8ca56037450c58734326ebe34aec8f7d1928322a12164856365fea73": "0x00000000000000000000000000000000000d44617669642053616c616d690000000000000f64617669645f6f665f6561727468000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714db39fbf092d6577f4d5cfb0370c406b3cd3d9db1517bca918bdc96629af87092cfb09989a2d0388a": "0x00000000000000000000000000000000000f63727970746f76656e64696d6961001c68747470733a2f2f63727970746f76656e64696d69612e636f6d2f00000000104063727970746f76656e64696d6961000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714db4e290a8c8e6d7d4c8dfef612efd6af3b857c60a4350f446e795aaef08c419649d7f8f65955eb15": "0x04030000000200000000000000000000000000000000144a756d696e73746f636b20506f6c6b61646f74124361726c6f7320526f6472c3ad6775657a0000156a756d696e73746f636b40676d61696c2e636f6d00000b6a756d696e73746f636b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714db51125ce2e8c4560c2017a4f115c013d899b494c955a7ec4cc9786a3997f1823baacc213896a35a": "0x000000000000000000000000000000000015506172616c6c656c2046696e616e6365202d2031001568747470733a2f2f706172616c6c656c2e66692f000000001f68747470733a2f2f747769747465722e636f6d2f506172616c6c656c4669000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dbc32343b195f2c99256c8dc61df081bf36a5ee0f1bb8c987003aa1ed4fa3dcc93c05dfec013f27b": "0x00000000000000000000000000000000000b416c657068205a65726f001a68747470733a2f2f7777772e616c6570687a65726f2e6f7267001468656c6c6f40616c6570687a65726f2e6f7267000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dbc647e39c873964a0f2dda8e1a5b5f1820545ba2f4d33f1d99e259c9a3e8071329f84c07f40f514": "0x04030000000200000000000000000000000000000000064a616e6b6f114a65616e6d6172636f2052616e6768690000166a65616e6d6172636f696e40676d61696c2e636f6d00000752616e676869000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dbf7430d0eb6bd0486c273a2e537718a8bb986e964a0eb5e5e525ab6ee44e191d7ba645cac2efe05": "0x000000000000000000000000000000000017506f6c6b61646f74206961627369732067696c6c65730747696c6c65731568747470733a2f2f61697264726f702e636f6d2f001567696c6c65732e684061697264726f702e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dbfc69131dbd305c4c2ded7ca2dd19095123de090a46149c2047d0aa4c6ce195490563d881f7491b": "0x04000000000200000000000000000000000000000000085369726a657931000014405369726a6579313a6d61747269782e6f72671b6e69636b6f6c61736a6f73687561303040676d61696c2e636f6d00000b4044725f5369726a6579000d5369726a657931233237313300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dc22c730a7ef5a54488b87e574eee2f9b8e7810eb3567edffc303c4f9c76946da200ce429b444d59": "0x040000000002000000000000000000000000000000000a4d617843726970746f000000166d617863726970746f6f6b40676d61696c2e636f6d00000c404d61785f43726970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dc33802d5bdfa7529ef6dd302c66ad6ee5ee679f6fd7c3ff4b19489daad6680691aa209b719f2139": "0x040000000002000000000000000000000000000000000a6c756e617220646f74000000196c756e61722e706f6c6b61646f7440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dc5da19e1472ceec06dd5a888daa57d1bcb763f269e5cf4bfe73b44d106b34ce572dcee124057a6b": "0x040000000002000000000000000000000000000000000a414c454a414e44524f001c68747470733a2f2f6769746875622e636f6d2f616c336d6172742f1440616c336d6172743a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dc60ff49e9f778bbd24bfa7e01cb86e6eab810a04a61d7bc3c32c094afd5b4739194c9583a69d238": "0x000000000000000000000000000000000015506172616c6c656c2046696e616e6365202d2032001568747470733a2f2f706172616c6c656c2e66692f000000001f68747470733a2f2f747769747465722e636f6d2f506172616c6c656c4669000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dc6d83874526905136aff2ede1563784631d6149024982108f661b079b6b79f3d042041a9da11e2a": "0x040100000002000000000000000000000000000000000748616e77656e0d48616e77656e204368656e671d68747470733a2f2f7777772e68616e77656e6368656e672e636f6d2f164068656177656e3131303a6d61747269782e6f72671468616e77656e406c6974656e7472792e636f6d00000a40635f68616e77656e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dc79486ab3b689692440bee5d463a2cd234b5d8fb3b6eeb3f91b5fe9e26267846abaf7cbb154524b": "0x040000000002000000000000000000000000000000000b416e64726564796c616e14416e647265204443204472204c616e7a6f6e692168747470733a2f2f7777772e6c696e6b6564696e2e636f6d2f696e2f6164636c11406164636c3a6d61747269782e6f726717616e6472652e64632e6c616e7a6f6e6940706d2e6d6500001040616e64726564636c616e7a6f6e69000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dc7d270f233cb625a6ac5af2b37a6bb6d5c9cbb7fa56748fb1c9cf9ad1ef43334efa76a431aa3d22": "0x04010000000200000000000000000000000000000000175375706572636f6d707574696e672053797374656d731a5375706572636f6d707574696e672053797374656d732041471368747470733a2f2f7777772e7363732e6368000c696e666f407363732e6368000008405343535f4147000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dc87dbc53489aff444fd1d056884026869c3a2ca0d5f75455995962bbd5f5676b9aefed98d176c22": "0x0403000000020000000000000000000000000000000005526963680b526963682043617365790000177269636869656a636173657940676d61696c2e636f6d0000097269746368637379000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dc98458dbb1371bf08d31593110e775453dea202d90d97424b66f31b337467ecd5f3bf86329d3b97": "0x040300000002000000000000000000000000000000000a43617270656469656d000012406a6469656d3a6d61747269782e6f7267146a7361766f406d61696c66656e63652e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dcb7b0516f93e814720d807d46b941703ffe0278e8b173dc6738c5af8af812ceffc90c69390bbf1f": "0x04000000000200000000000000000000000000000000076f726469616e00000018706f6c6b61407265757361626c652e736f667477617265000000076f726469616e0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dcded909158c7cfe7cca49e0d4903aeefcfb804fb07b71195a216074f9194f611991ede515e8ba46": "0x0403000000020000000000000000000000000000000006417661746111417661746120486f6c64696e6720425600000e696e666f4061766174612e676700000961766174615f6767000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dd3ab7da34fe2f80d646eb13c8251f905355c49d20ff0fff32f649474a97d040d8b5165e9ec74926": "0x00000000000000000000000000000000000e4a6f6e6174616e20537461736800000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dd5ac43b991d5bfdf68fd95ffc9cc02f15e28ce9df041da32f3b564e249fb4a8caa1c5135b1fad4d": "0x0400000000020000000000000000000000000000000005496767790000001869676e6173692e616c6265726f40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ddd17007ceeb939fb4d78a8bb35a7b0ff8ae6a7808f9b17c83efd28b8612c2388034d0e35ce51376": "0x040100000002000000000000000000000000000000000c5374616b696e675465616d0c5374616b696e675465616d1968747470733a2f2f7374616b696e677465616d2e636f6d2f18407374616b696e677465616d3a6d61747269782e6f726715696e666f407374616b696e677465616d2e636f6d00000d405374616b696e675465616d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ddee1d127f978b69654acc3f002ced5c8d1fbce6268485fe0a2be3d624d32b6e53132daf8f28a380": "0x0400000000020000000000000000000000000000000018564953494f4e5354414b4520f09f9181e2808df09f97a800001840766973696f6e7374616b653a6d61747269782e6f726715696e666f40766973696f6e7374616b652e636f6d00000d40766973696f6e7374616b65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714de11e082d85cbef160f94710848d9dce161724f257a240494c901728bdf2fa51c138fc5580ee3134": "0x00000000000000000000000000000000000e4a6f6162204e697761676162610e4a6f6162204e6977616761626100001b6e697761676162616a6f61623130303040676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714de218ead5d0a168c9228177e3d9e1bfbd17975b3185cc99dc6b235a2bfd42609ab31f3f3059d6e6b": "0x040100000002000000000000000000000000000000000d6469676974616c6d696e747310542e20412e204d696e7a656e6d61791968747470733a2f2f6469676974616c6d696e74732e636f6d0019636f6e74616374406469676974616c6d696e74732e636f6d000011406469676974616c6d696e7473636f6d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714de4e384e5c5bcda65ac4bbe840e332da2656c0e760904003fa7a2123a216a33e81b8531400600304": "0x04010000000200000000000000000000000000000000095869616f476f6e67095869616f476f6e6700144072696368656e673a6d61747269782e6f7267113435353433393034344071712e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714de5652b31dd1f614c6aff670231f257996dcabb052286394791d85d4be9d8635cc14df0c11e67864": "0x04030000000200000000000000000000000000000000144a6f686e206f662074686520707269657374730f4a6f686e204d756c6c6f776e657900001b6a6f686e6f667468657072696573747340676d61696c2e636f6d00000f404a30686e4d756c6c30776e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714de570a3f6eb03219824c1f132f117e3bfad5f23d7d68cfbd3022c11214b4154ad0ab329351365547": "0x040300000002000000000000000000000000000000000a56616c656e74696e610000000d73636861696e40706d2e6d6500000d40307876616c656e74696e61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714de8c0cc61d322ab8b0a6788e02cfb0b5c9629a2153e9f26b9f1122ea8a03abf85cf8e3aacc76ec2b": "0x04030000000200000000000000000000000000000000076c696b6b65650e4c696b204b65652043686f6e670000196c696b6b65652e63686f6e6740686f746d61696c2e636f6d00000c6d61706c65726963686965000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714deb3e9bfbd8d3c590cf6b6cdababf69c2af37a41a2f360820bb7dff2f61c20bb61a9503a8901ee20": "0x040000000002000000000000000000000000000000000f4861736865642053797374656d73001268747470733a2f2f6861736865642e696f124074656b69743a6d61747269782e6f72671068656c6c6f406861736865642e696f00000f4048617368656453797374656d73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dedb59bd6fbcfbabc75c3b5f909a0c53c58fe9ab06b9cd336e82918b3275d402c2dd8501455f1769": "0x00000000000000000000000000000000001b5765625a65726f204576656e7420426f756e74792050726f787900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714deefe5d90b67b654b66953df31de525fd4c075b30b266f8fca1f29533c9dd7d45128408726d19979": "0x040000000002000000000000000000000000000000000f73656e7365696e6f64652e636f6d001c68747470733a2f2f7777772e73656e7365696e6f64652e636f6d2f0014696e666f4073656e7365696e6f64652e636f6d00000c4053656e7365694e6f6465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714df04aa6913361fbfc6c5094505321fb73284b9882d87d66bd6ba692faf1a4f5254d977dad8934801": "0x000000000000000000000000000000000011496e7465726c6179204e6574776f726b0014687474703a2f2f696e7465726c61792e696f2f000000000c40496e7465726c61794851000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714df081b6eb6159ca822e0586d9ca1db9e8e60a7eaea3fdbbe6fd5238477c91d04e1532b1ed251d251": "0x04030000000200000000000000000000000000000000085042415f54494d0a54696d20446f62696500001574696d40706f6c6b61646f742e61636164656d7900000a4074696d6164616d64000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714df3b8158c3a22b52c8660fc9479c8b741d2065985d0b77d00a36a50900438d83ba70ab138f528326": "0x00000000000000000000000000000000001054616c69736d616e20506f6f6c2032001568747470733a2f2f74616c69736d616e2e78797a000000000f40776561726574616c69736d616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714df56e27f047aded67252b4b1f8103acd6ff363c971c7dea21afc0285ad5ad75176dd53947a6aca4b": "0x000000000000000000000000000000000012636172746569726120706f6c6b61646f7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714df658b757ba3a7aa702baf94343fc34fc6b80b225c14758484c91816726a7b3951bc0ce1daae9f53": "0x000000000000000000000000000000000005696e6b21001068747470733a2f2f7573652e696e6b000000000a40696e6b5f6c616e67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dfdd2a5f61982fba4297a93d2faba768a0be3c2a69bee7a17d73264b9adebae51e28e7b37463f91d": "0x04000000000200000000000000000000000000000000064e3444524f00000000000007404e3444524f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714dfe38f3528e245c5c66d295453335e1fac0d88b96160f5643d0bc4ae9eeeb8402f26c866187b9960": "0x040000000002000000000000000000000000000000000d426c6f636b2042726964676500000016626c6f636b6272696467654070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e01ab7ee15ada6e3eeef86dcd6803a23da2d5a8834ce66fe3e9f705bc756dc0e835741d68f200762": "0x04000000000200000000000000000000000000000000056c6163680000000c69616d406c6163682e7077000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e02c902560555f54d44824ac8d1edecca67639ca74d208bd2044a10e67c9677e288080191e3fec13": "0x0000000000000000000000000000000000076f6c616e6f640d44616e69656c204f6c616e6f0018406f6c616e6f643a766972746f2e636f6d6d756e6974791264616e69656c40766972746f2e7465616d000000076f6c616e6f640000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e050fe4b1877288206c7ea7684b6aac6cd63cf88c92b0f05398bc3e13e0b0c5936c3027b8e0c7e2f": "0x04000000000200000000000000000000000000000000105b5279752d4361706974616c5d2031000017407279756361706974616c3a6d61747269782e6f726713727975407279756361706974616c2e636f6d00000c405279754361706974616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e08890d6de27a2900e117eb473a2617377b8ddcab2e411131969c226ba36618ea24514b6f736b77c": "0x0000000000000000000000000000000000094d6f6f6e4e6f6465094d6f6f6e4e6f6465000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e09d6b3fcb94c12410446dd351747cd3a9557e288b706aa824c74440006e9730a806a2b5d16f2e51": "0x040000000002000000000000000000000000000000000e4b494c542050726f746f636f6c0000000d6b696c74406b696c742e696f00000e404b696c7470726f746f636f6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e09e422aff2862656cb9c61a3d59451f446f191f7e41facb800ab8c789255af69fe0d6ca397e1238": "0x040100000002000000000000000000000000000000000b6b616974656e63757261000000156b616974656e637572614070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e15938b3e3caed8b5c0ad5714ebe5c2ff7ba60c09f693cfe2582ba7dd65e1feb74b1c99cccb75079": "0x00000000000000000000000000000000000f4c415945522043414b45202d2031001968747470733a2f2f6c6179657263616b656361702e636f6d000000000e406c6179657263616b65636170000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e16c7dc70b9d971f180f3232cf72cfbc3f350733ec10eb9ba8a135e3831d813259c3c7e3d46fca2c": "0x00000000000000000000000000000000000b43727970746869726f6e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e177989adf0462e92c02b3a008b94bf4a9e846002dba45ed38eb41ef3c8edc223403723b3301a331": "0x04030000000200000000000000000000000000000000065a65657665065a6565766500000e72617669407a656576652e696f00000830785a65657665000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e18d41e2249b70ec4eb32a5fbbccb1d97d31237172fbfd92945caa6822d5f8afb558aa7b89bc5a11": "0x040000000002000000000000000000000000000000000753616b7572610000174073616b757261746563683a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e1b94c49ca35815a7e9b1d7500a5e651f1faf14965e6993c10eddb1bdbcc1ca2e4d0812320c42216": "0x0000000000000000000000000000000000104d656e6e6120416275656c6e616761164d656e6e6174616c6c61682041626f656c6e61676100001b6d656e6e61742e616275656c6e61676140676d61696c2e636f6d00000000106d656e6e6174616275656c6e61676100", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e207ae5a1f676e495a39c793248e0a8cac2ec88172c7d1a3b2f10c6c2455e20c5dbb739cf3e9ea0a": "0x040000000002000000000000000000000000000000000959414f20476d6268000000176c756b6162616c6173686f7640676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e21241c1f77eea219c34bbbde6bff80d45a0e9f3500e7aebd52d558fcd919b2e0d788dd8728a047a": "0x0403000000020000000000000000000000000000000018416e61656c6c65204c5444207c2045636f73797374656d00001740616e61656c6c656c74643a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e25361ec814d346808ebc46e11c7f7ba99411e693af6481ceb7cc935cfc5b2344a9831f915f6555e": "0x040100000002000000000000000000000000000000000d444953432d534f46542d31310e4469736320536f6674204c74641a68747470733a2f2f7777772e646973632d736f66742e636f6d154064697363736f66743a6d61747269782e6f72671876616c696461746f7240646973632d736f66742e636f6d00000e4044697363536f667457656233000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e26d187c65071fe036da1284412c9f435d93ae01474705ea7f0ac3154103a0d08f3efabdbb389345": "0x000000000000000000000000000000000009436f696e62617365002168747470733a2f2f7777772e636f696e626173652e636f6d2f7374616b696e67000000000d40436f696e62617365446576000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e2bc3b02d70c5dd5bab650983063ab81171b9efc65665326b507438d99994d489f07ef16bcc93d6b": "0x040000000002000000000000000000000000000000000f535452415742455252592d444f5400001740737765657462657272793a6d61747269782e6f72671d73776565747374726177626572727933303240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e2de10fd49d4b23394472b8251f87622be5a6c44f9eb3fcd59038eda7f08163511cbe8aeeb584136": "0x00000000000000000000000000000000000b4d494b452d537461736800000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e2f69a0dbf9d98314642514ce6e9e7fb66ef4334728307f47be8b2509e6fa870a7685f3e560e3a08": "0x0400000000020000000000000000000000000000000007706569726f6e00001340706569726f6e3a6d61747269782e6f7267177461696368756e673939363140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e31041f839b1c98afe111b571b0ba64cb8365c7e9bba1e412d6fd57634a54bd1996314689e061a68": "0x00000000000000000000000000000000000843727970746f72002168747470733a2f2f7777772e796f75747562652e636f6d2f4043727970746f72000000000c4043727970746f725f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e31192e1956f8f3aa6996ba5fce10b5419b5a9fd55998b5d7fde4922e61f93b021cb2cf6dfd5707e": "0x0400000000030000000000000000000000000000000006426a6f726e0e426ac3b6726e205761676e6572001840626a6f726e3a6d61747269782e7061726974792e696f10626a6f726e407061726974792e696f00000b40626a6f726e77676e72000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e333905c7589ca5182526dcb428e7f915dc46c895f155ff1b31a93371b4f7c59aa89569beacd2b03": "0x00000000000000000000000000000000000c506f6c6b6177616c6c657400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e33504e56333a26eec8073c78e4291eadb0184b89afe2f9389c762e6e869329275dde3f729507251": "0x040300000002000000000000000000000000000000000748656e7279500f48656e72792050616c6163696f7300001968656e72796470616c6163696f7340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e35aea91533fa89ffc7995246b1a9c38001bcf09a7338b892dc8ce10cc201c4567ac4c56f4658846": "0x00000000000000000000000000000000000c43727970746f20415049731243727970746f20415049732c20496e632e1668747470733a2f2f63727970746f617069732e696f000000000c4063727970746f61706973000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e35b67b937cc75caa81dfbac142664eb6f7ff61c5c0b2c8a180059b27ccb68ccc6b9c152be120b70": "0x040000000002000000000000000000000000000000000c6669616c6b612e6c697665001468747470733a2f2f6669616c6b612e6c6976650012706f6c6b61406669616c6b612e6c69766500000c406669616c6b61706f6f6c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e38e649e5f9f7676b4d599b32c954b0a9e554c96b248f3e66046a82f46ac914fc675938f771f8372": "0x040000000002000000000000000000000000000000000942696762616c6c7a0000154062696762616c6c7a3a6d61747269782e6f726716696e666f4062696762616c6c7a2e6e6574776f726b000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e393f842833927bb48a751e3ab512dda0be14000c63798a628ee5c8bad55e947c356dbf38b0b0210": "0x040300000002000000000000000000000000000000000745737468657215457374686572204a61646520506172746c616e6400001369616d406573746865726a6164652e636f6d00000d404573746865724a61646531000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e3d6c0bdba37bbdc244c335b828809b83644239e5c6df4b01630483c410c6f380cd58aee02f6b830": "0x0401000000020000000000000000000000000000000015576562332056656e74757265204361706974616c15576562332056656e74757265204361706974616c1068747470733a2f2f776562332e76631440776562332e76633a6d61747269782e6f72670b686940776562332e766300000d4056656e7475726557656233000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e40a8cb9cddb8c20106261d48665f2a3f6f015d725285ae547e9b3e83bfd332a39b1ef8cf1a22a3d": "0x000000000000000000000000000000000007416d666f72630a416d666f72632041471368747470733a2f2f616d666f72632e636f6d0013636f6e7461637440616d666f72632e636f6d00000a40616d666f72636167000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e46168425cb085b326ef1ebd6770b8291ce61d9f3cfee80c6a459b7e564988cd9fe6e4be89c9aabd": "0x040300000002000000000000000000000000000000000f506f6c6b61506f72742045617374001a68747470733a2f2f706f6c6b61706f7274656173742e78797a001a636f6e7461637440706f6c6b61706f7274656173742e78797a000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e4643365f6f183049456976091da2f5b7483d689df155834018d42a99572e4582beda8f879f0a11b": "0x000000000000000000000000000000000008746861646f756b17417468616e6173696f7320446f756b6f7564616b697300001761646f756b6f7564616b697340676d61696c2e636f6d00000b40746861646f756b383408746861646f756b0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e4b32bdcfe8a21ed50b428a44aee6d7d7971bc278208f295b647bd1cd44985423c3cf405adc2e336": "0x00000000000000000000000000000000000b6d616c696b656c626179001c68747470733a2f2f7777772e6d616c696b656c6261792e636f6d2f0013686579406d616c696b656c6261792e636f6d00000c406d616c696b656c626179000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e4c774b7f47a03509d8c2041692eed6612bae5f84f15d6e5e3943d3ee38dabd26dd2ded79780e6b2": "0x00000000000000000000000000000000000c526567656e63792d3031341757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e4ccf4e65ffa766b5af0f6de364a186904df50d92e02723052cc2187692243e6dbeba9f7fa30e33d": "0x0000000000000000000000000000000000085044204e79747400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e4e4cd2e3863bdd6e4395b60d99030b923a52cd9ef3468a2a7ee4a21cb88a1fc276f763a67656618": "0x040000000002000000000000000000000000000000000f44617669642053656d616b756c6100001a40646176696473656d616b756c613a6d61747269782e6f72671868656c6c6f40646176696473656d616b756c612e636f6d00000f40646176696473656d616b756c61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e5018c1728c62231664513c046d4497ba05c19efac47cb0dbe498e20b089dff25aa08d1a77ec970b": "0x040100000002000000000000000000000000000000000a5374616b65666c6f77002068747470733a2f2f76616c696461746f722e7374616b65666c6f772e696f2f124069373439353a6d61747269782e6f7267127465616d407374616b65666c6f772e696f00000e4073665f76616c696461746f72000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e557553c94ed289f20e7fc24fdb992bc25566724e1533d7e861fe1a902394cf823a49f1a9c6d6520": "0x00000000000000000000000000000000001a4d616e7461204e6574776f726b206f6e20506f6c6b61646f740e4d616e7461204e6574776f726b1668747470733a2f2f6d616e74612e6e6574776f726b0016636f6e74616374406d616e74612e6e6574776f726b00000e406d616e74616e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e55dfe32a8ec457fb669bdbd25438dcf9a34ab730a518fa6594d888439187042e9607803e9fd756e": "0x04000000000200000000000000000000000000000000085269636172646f00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e57b6fff5aa31fc5dc73e84c4d039277ae7819cf959a0092683ea8e6e7e9d2447c918d8aa89d681e": "0x0400000000020000000000000000000000000000000009456c2050696e746f00000015656c70696e746f6d616e40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e5a27d3189e99969dad042c036fcd9897945a880458b8e7104b30617a9640eaa22b7e23e675e0a02": "0x0400000000020000000000000000000000000000000014504f5745525354414b4520504f4c4b41444f5400001740706f7765727374616b653a6d61747269782e6f726719706f77657240706f7765727374616b652e6e6574776f726b000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e5ec7a33cbbb8d9e04f3da939fa351c562c7e06e1e3716976b5e14230e83a45995cbad9086f49e17": "0x040000000002000000000000000000000000000000000a506f6c6b61676174650a506f6c6b616761746515687474703a2f2f706f6c6b61676174652e78797a1640706f6c6b61676174653a6d61747269782e6f726716706f6c6b6167617465406f75746c6f6f6b2e636f6d00000b40506f6c6b6147617465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e5efd10bd264dbe62c0adc4df234352b61fd60a32c2890fc64c2c0a3de5e33ee1c5bc9d8f581642d": "0x0400000000020000000000000000000000000000000017f09fa7b12053656974616e20426c6f636b20f09fa7b10000184073656974616e626c6f636b3a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e601d44489655af884301af9b7a04d8e8cbc7e13b463fd50bea892b3e7952d62c44cd70815e0567a": "0x040300000002000000000000000000000000000000001d4c414f5320436861696e20466f756e646174696f6e204d656d6265721d4c414f5320436861696e20466f756e646174696f6e204d656d626572000017696e666f406c616f73666f756e646174696f6e2e696f00000c6c616f736e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e60910119cc2ea203e87ac01090985a916808ebe0f45ed24ae099c59d163bc073fef792b1309ec0c": "0x00000000000000000000000000000000000f4f676e6a656e20416c656b7369630f4f676e6a656e20416c656b7369630000216f676e6a656e406365726573626c6f636b636861696e2e736f6c7574696f6e73000000000b6f676e6a656e3038303000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e61549e518cc68adc8a5bf93006b7fd50ffc2abaffd57ef06c67f2171b5097070892fa1a195d920f": "0x04030000000200000000000000000000000000000000184d43207c204d43536572766963652e696f207cf09f92a50c4d61726b204372696e63650000156d61726b6372696e636540676d61696c2e636f6d00000c404372696e63654d61726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e63ab25a74c3ba8458baf0f841ed4154e046e8567bff89dbb50dd8302c248394234a0ab191de0d30": "0x0400000000020000000000000000000000000000000007434543434f4e0000000e646f7440636563636f6e2e6d6500000b40636563636f6e5f6d65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e676ae0036688fbf1cfe7dfca152379d21d1fd8dd18698889590afa4abd3cd05cbf4f6d15cd5454f": "0x040000000002000000000000000000000000000000000c4576657279646f746f72670a45766572792e6f72671668747470733a2f2f7777772e65766572792e6f72670012737570706f72744065766572792e6f726700000d404576657279646f746f7267000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e6d19e9cf5f198f5205b1d0649f02b8d82c090a215995a883e0e4d709c4513e8e9890a110fb5420d": "0x040300000002000000000000000000000000000000000841757468656e61134665726e616e646f2052657920476169646f00001e6665726e616e646f2e7265792e676169646f4061757468656e612e696f00000a66726579676169646f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e768b256ec04939e689582a5e1411cd4bdc5086ec5604dd4dcbe4255339299de13d428335c46902f": "0x00000000000000000000000000000000000a6c61726b6e6574353100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e79ea902982619aa3283cc9f4408df3ccaef653fc163e56509619c1e0e46bb4e677d227fa50bef7f": "0x0403000000020000000000000000000000000000000006436973636f124672616e636973636f2041677569727265001140636973636f3a7061726974792e696f206672616e636973636f61677569727265706572657a40676d61696c2e636f6d00001040636973636f5f6167756972726570116672616e636973636f616775697272650000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e7b5d2aa3f9d8295b8e06dc2e6bb6bd269319ace4cf8f663338f8f285a0564d6a139063c985d2310": "0x040100000002000000000000000000000000000000000a48617368517561726b0a48617368517561726b1568747470733a2f2f68617368717561726b2e696f0015636f6e746163744068617368717561726b2e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e7b7e5fca7ee9bcc9607759a0532ec6ceda78a076d3a63eb7f7b7be9a87ace1f94b2f25df3d0702c": "0x040000000002000000000000000000000000000000000d4f70656e5a657070656c696e175a657070656c696e2047726f7570204c696d697465641e68747470733a2f2f7777772e6f70656e7a657070656c696e2e636f6d2f001966696e616e6365406f70656e7a657070656c696e2e636f6d00000e406f70656e7a657070656c696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e7cb8e1a29da70a064f4c7e6a5f3f25b4d063e5461bf3882569aad883b6db400695ccfd7c6e6ef22": "0x00000000000000000000000000000000000c5843417374726f6e6175740f44616b6f7461204261726e65747400001764616b6f746140696e76617263682e6e6574776f726b00000d405843417374726f6e617574000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e7cfb082496967f7ec22eb74dea33d78388ca084d35bb754ab5256d4d606d83818f639d0c63dd541": "0x040300000002000000000000000000000000000000000d4e69786f6e204a6176696572000000196e6969786f6e6a6176696572797440676d61696c2e636f6d00000d6e6969786f6e6a6176696572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e7dfd30137a8f95b6201961514cf5ad87f1c4dd0c392ee28231f805f77975147bf2c33bd671b9822": "0x0000000000000000000000000000000000106b697368616e736167617468697961114b697368616e20536167617468697961001d406b697368616e6d7361676174686979613a6d61747269782e6f726700000000106b697368616e7361676174686979610000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e7fa098460d3db9eb088c46557805c70d888174482b02e258482e5e3f5ae7a20bbdc333c9c046d04": "0x00000000000000000000000000000000000b696e6b21756261746f72001868747470733a2f2f7573652e696e6b2f756261746f722f0000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e819164bbb79142da4d26dd19482ab1dfae06825850bc2a3e3c82821977573c80eb9f19747b9215d": "0x00000000000000000000000000000000000c4f6b6f74206a6f736875610c4f6b6f74204a6f736875610000176f6b6f746a6f73687561393340676d61696c2e636f6d00000e404f4b4f544a4f534855413433000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e82757563afd35c4501f6db8fa8f98a21f0a62678bbd5f0f91be1c0a6c7215f2610a782d0d3a2458": "0x0000000000000000000000000000000000094a75616e6f444f5400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e85d0bcdbe07f1da309409e68f563d9e9160bc30f7418cbe5735e3e2dc9922db1826807029b8ba5e": "0x04000000000200000000000000000000000000000000104d61785f43727970746f7a696c6c6100001c406d61785f63727970746f7a696c6c613a6d61747269782e6f72671963727970746f7a696c6c616d617840676d61696c2e636f6d0000104043727970746f7a696c6c615f6d67000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e86c54857827324682635b83e254e0ed6438dd7d9bc229658cf164cebf4322cf4932815d4d7ad323": "0x040000000002000000000000000000000000000000000f47656c69746f202d204f6e797a6500000010616e67656c406f6e797a652e636f6d0000084067656c69746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e87135bfa99a9afc6ad7c1595e740abcfe3a9b2cc4243d13341ebdf1ddebfe5184822cbc4fdb6116": "0x00000000000000000000000000000000000f53616e6a656577612053696c76611850205355524553482053414e4a454557412053494c564100001873616e6a656577612e73696c7661406c6976652e636f6d00000b407373616e6a65657761000f7373616e6a65657761233830383500", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e876a25b4b1879f1746b2591bb9bf87e8b24cb5e4b265192187471475c775ed7708f7cab9f76617e": "0x040300000002000000000000000000000000000000000a4e334d55534e65616c0c4e65616c2050657465727300000f6e65616c406e336d75732e636f6d00000a406e65616c5f6e7065000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e883951f96e016e628c1641676e9e4f0c271a9b92fb49311bb7cfc00a0478dc92908fd30005a194b": "0x040100000002000000000000000000000000000000001b444f542056616c696461746f7220416c6c69616e63652044414f001b68747470733a2f2f646f7476616c696461746f72732e6f72672f1440706d656e73696b3a6d61747269782e6f72671b646f74616c6c69616e63654070726f746f6e6d61696c2e636f6d00001040444f5456616c416c6c69616e6365000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e88d81bf95788cc15a9616ee0f6737de3d1a75ee0c6a2101f70671cc68da4ceda653a18cd751ea3d": "0x00000000000000000000000000000000000a4d6178446f744465760d4d6178204b7261766368756b0000156d636b7261766164657640676d61696c2e636f6d00000f404d61785f5f4b7261766368756b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e8b5663905f9c3c0f861ce44b57a59f1f6953a922920de251b138c3a751b3f9adf075dcd4d2c9851": "0x040100000002000000000000000000000000000000000b506f6c5f43727970746f0b506f6c5f43727970746f0000196d7073686964616c676f3139393040676d61696c2e636f6d00000c40706f6c5f63727970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e8c5f28ea575985d2cbf07f54d172d80de65ac3f2707f660dd53f7ead6013a9d47921e881d258e64": "0x040000000002000000000000000000000000000000000f424c4f434b434841494e425241440000000000000d40427261645f4c6175726965000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e8cb1cf7b100db583c343c62a938e255060d46435233ab16bb11168adfa99c770fca459af08b0241": "0x040000000002000000000000000000000000000000001156616e636f757665722043616e6e6f6e1d416476616e636564204461746162617365204e6574776f726b696e6717687474703a2f2f7777772e4144622e4e65742f444f5411406a617a643a6d61747269782e6f72670c444f54404144622e4e6574000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e8edd00301de472cb4e98052ea4e41501850a90d99cd46fe92a91bfe039fff0f4e5c0d5eb69f5813": "0x0000000000000000000000000000000000097968616d61636e75001c68747470733a2f2f6769746875622e636f6d2f7968616d61636e750000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e8f8b7184f9a04c0ca72d01b6c36c383e4ba984681b7b467dafefec8f44dab1ed507ae6ab2704c30": "0x040300000002000000000000000000000000000000000930785461796c6f720930785461796c6f7200001630787461796c6f72406368616f7364616f2e6f726700000a30787461796c6f725f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e91edd91ccfc5f024617b7364d403c2b44cc4ae58ec9f2b3b412d3b9cdf57ec06e3cb878a228a70a": "0x04000000000200000000000000000000000000000000054761626f000013406761626f31383a6d61747269782e6f72671178636761626f4070726f746f6e2e6d65000009405f78636761626f000c4761626f3138233932333800", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e935216ece9e99210a44ceb024e05ccf8c9153a48c24dda24a5eb82db438cdc55b1b234c613eac14": "0x00000000000000000000000000000000000c4765726d616e676c657a61194765726dc3a16e20476f6e7ac3a16c657a204172616e64611b68747470733a2f2f736f6d6f7363727970746f6d782e636f6d2f00176765726d616e676c657a617240676d61696c2e636f6d00000d406765726d616e676c657a61000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e940a76f99d2ec9f40e8a79a9a76b954f2e08ffd985d6f6a3bd3fca001baee4e235cb67ed9cda635": "0x040300000002000000000000000000000000000000000b6a6f686e6e796b6177610d43616573617220446974616e0000156a6f686e6e796b617761407961686f6f2e636f6d00000c404a6f686e6e794b617761000b6361657361723030383800", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e9705190c69f75e984a135c14c92b678df586c20c8e3ca3511043f0dc8bfd88017d0bb97bfcff63a": "0x04030000000200000000000000000000000000000000054a617773144a61756d65204d6f72656e6f20526f766972610000136a61756d6540676f6c646a6177732e636f6d00000b40676f6c646a6177735f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e97f51eae0303cf7d07d209d7880ecdc39f636e4390f665c563274bfe9a981c6c31f2995c184b0c2": "0x040000000002000000000000000000000000000000000d4261677069706573204f7267001468747470733a2f2f62616770697065732e696f000000000d4042616770697065734f7267000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e999e16a19189c1f8c8897c6e9a74d582959e1d4273719c9edbd33d5c44815ebe162fe0ee8191863": "0x0000000000000000000000000000000000046d696f096761627269656c65000015672e6c756e676869373140676d61696c2e636f6d000011406761627269656c3037333531393039000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e9ea0dabafbeec8280251f075ec25c5b4fd4272d81fb78eeeeef9819356f102b5e7bde1154a73f2e": "0x00000000000000000000000000000000001d44757463682054756c69706661726d657220436f6c6c656374697665001b68747470733a2f2f6474662d636f6c6c6563746976652e636f6d001961646d696e406474662d636f6c6c6563746976652e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714e9fdc68bc008f11eaf24540bccc2a2661e0301d4e45b87ab8e961db49e4660ef217619bdcd5fc638": "0x00000000000000000000000000000000000e617263686973696e616c5f696f20417263686973696e616c20546563686e6f6c6f67696573204c696d69746564000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ea0e1b22a44d19352c2a55b5e0d28cc772b47bb9b25981cbb69eca73f7c3388fb6464e7d24be470e": "0x040000000002000000000000000000000000000000000c5a7567204361706974616c001768747470733a2f2f7a75676361706974616c2e636f6d0017636f6e74616374407a75676361706974616c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ea1425c366c9ab900a2831bc1a5cfdfd6aca13c77cf934fdf2a6010d041d4d76a62698cd58c87956": "0x040300000002000000000000000000000000000000001f446563656e7472616c697a656420436c6f756420466f756e646174696f6e1f446563656e7472616c697a656420436c6f756420466f756e646174696f6e1768747470733a2f2f63727573742e6e6574776f726b2f001168694063727573742e6e6574776f726b00000e4043727573744e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ea2794691c218be17657796360e40e5d980d7b7835108a8534538dc530bce1b77c49cde4b7f97b36": "0x040000000002000000000000000000000000000000000641726a616e0000134061726a616e7a3a6d61747269782e6f7267000000114061726a616e7a696a64657276656c64000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ea2c0371426ff011ea857aabed70020c0433dfba617fdd3abe9dc3b22b427f884b13f4f4dcb6481b": "0x04030000000200000000000000000000000000000000074261746d616e001a68747470733a2f2f6c696e6b74722e65652f5f6261746d616e1c4030787468657265616c6261746d616e3a6d61747269782e6f72671a30787468657265616c6261746d616e40676d61696c2e636f6d0000114030787468657265616c6261746d616e1469616d6d617374657262727563657761796e650000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ea92920fa526337e0c06466c9aab1dac1ca8bddbf02a3f0913358bf068012a0478d62c7cd076c12c": "0x04000000000200000000000000000000000000000000104672657368437265646974204f7267194672657368437265646974204f7267616e697a6174696f6e1868747470733a2f2f66726573686372656469742e6f7267001f63657361722e74696e616a65726f4066726573686372656469742e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eabad1af85b4f2da751b2f1dc8d7e182eb3615a1894330dfd769ca25e945004b9c28a0309a875757": "0x04030000000200000000000000000000000000000000087375626f7469630d4976616e205375626f74696300000d69407375626f7469632e63680000087375626f746963087375626f7469630000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eabd7ed4ca42f780a8ebd4a61d47391a24659b5762d9ef578229c4fc2186f5f88174f8e728f27f78": "0x0403000000020000000000000000000000000000000013546961676f207c20616464726f70732e696f0e546961676f2054617661726573000015746961676f6d6d74323040676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eac493d2c384d548f0f3c639e5a8ee634594037f1f6eaba8a3d06c791615f80fa892deb77c0bf346": "0x04030000000200000000000000000000000000000000094c656f205068616d0f5068616d20486f6e672054686169000016686f6e677468616970726f40676d61696c2e636f6d00000c406c656f7068616d5f6974000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eaf34109655642c20a6d7337f0454acdaf58ff349faf36febd6f9dadddbebd1198919523b91f6b11": "0x00000000000000000000000000000000000c4e6573636166657573657200000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eb00f7c148179177909f4d84481e5466f7af1143a7193f40bd8c0606d75eb089666c29ca0347606a": "0x040300000002000000000000000000000000000000000a4c75697320f09f8c80000000166c7569736567723331303140676d61696c2e636f6d00000d40456e72697175656b736d5f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eb1d5fd73e1080ee944ade7aa7487574933c8394cc8c74d9073c65de1d77534b7effcc6cf34aefac": "0x0400000000020000000000000000000000000000000010496e66696e6974792057616c6c65740000001a706172746e657240696e66696e69747977616c6c65742e696f00001040496e66696e69747957616c6c6574000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eb307eab2c6490695ee3b9f948b36e799b4190bd0a025c1cc5132f52b3701c0a0912296120176d30": "0x04010000000200000000000000000000000000000000066450726f70000000176176696461636f6e74726f6c40676d61696c2e636f6d00001040426c6f636b636861696e65723931000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eb374a353870a2b9bac27ccc2a336ec0f7920048d460197d4abde56e977771aec19b3c9c6d110c7a": "0x00000000000000000000000000000000000b53706f6f6e79426172640000001a612e6e746f6c676b6f764070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eba5f322b497160118a5657b88d667ab46d1070d58ecc1f15011f60df20f510ee0aac61754bb9e01": "0x00000000000000000000000000000000001142494e414e43455f5354414b455f31311142494e414e43455f5354414b455f3131000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ebdea675e3bab66142780fa79136b5a383ca1180c911553235f69e97edb9aa09b6f80acd437da754": "0x04030000000200000000000000000000000000000000064265747479000000176d616b65736974626574747940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ebee8c2dc7e37890e84b20a21cd1f35835bb85d8e27d3b6d02bf08300998555443ec4cd3206ec37a": "0x0000000000000000000000000000000000056a75737400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ebf17402f1a0c5314e936e1e0efeeb5090b1f8afdf530d798c7d54a747704755ea03b110144e5150": "0x040300000002000000000000000000000000000000000c6775746f6d617274696e6f0d4775746f204d617274696e6f0000197375706572647570616866756e6b40676d61696c2e636f6d00000c6775746f6d617274696e6f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ec14530f7667a130dae5b67f7237a4ecc7daf168ee45d8b9f2fcfa07e7ae255daad9e09912595d4b": "0x040000000002000000000000000000000000000000001e494e20535042202d20f09f8dbef09fa582f09f8db7f09f8db9f09f8db8000015407370626472696e6b3a6d61747269782e6f7267185350424472696e6b4070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ec3029bbd85364b1f4792917b47917519e2c05619763a4e7b45b84815f902f62e16f23e9f2b22653": "0x04000000000200000000000000000000000000000000104972696e61204b61726167796175720442513900000d6972696e61406271392e696f000010404972696e614b6172616779617572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ec49f5213562f6f6309c4389039f51c4d308dd49f39c28e4d4f5e51413f123d3f7ebf050c2307877": "0x000000000000000000000000000000000013426c6f636b636861696e656420496e64696113426c6f636b636861696e656420496e64696121687474703a2f2f7777772e626c6f636b636861696e6564696e6469612e636f6d1a406d616e617661696c61776164693a6d61747269782e6f72671c6d616e617640626c6f636b636861696e6564696e6469612e636f6d00001140626c6f636b636861696e6564696e64000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ecc49e9888c4723b003be57bf884dd5eb5ea5274429e80980c136324444c5771a4a4c67a5124106b": "0x0000000000000000000000000000000000114172747572204775696d6172c3a36573194172747572204775696d6172c3a36573205065726569726100001761727475724065616363726970746f2e636f6d2e62720000114061727475726775696d617261657370000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ecd1bc9bdcddbb405437eaad7661fc64223db26c26db0c8aa317f5c56e3c640830495b4786d6e635": "0x040000000002000000000000000000000000000000001d4d61726b205279616e202d204576656e7473205369676e61746f72790a4d61726b205279616e0017406d61726b2e656d6265723a6d61747269782e6f726700000011406d61726b5f656d6265725f7279616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ed229b4f9a288d7518790dd7ca07916547aeab9f8155af2cb219bbdc7df93974d04c101bf9f18a1d": "0x0000000000000000000000000000000000066d312e7663066d312e76630e68747470733a2f2f6d312e76630000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ed6de9d282688326da649a9855dbd7872b852ef786c270f57cb1462262ebdb67ee0729a3eef90122": "0x00000000000000000000000000000000000c54454143484d4544454649001b68747470733a2f2f7777772e74656163686d65646566692e64650014696e666f4074656163686d65646566692e646500000d4054454143484d4544454649000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ed7f24e195293ab9d638bd6f76c3b3aa2810fa97ca64a8195d9460c08c8a509b78e9a6ec82c6e920": "0x0000000000000000000000000000000000115a4f454d432d434f4e54524f4c4c4552000012407a6f656d633a6d61747269782e6f72670000000a407a6f656d78666f780007407a6f656d6300", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ed90d5a15a85da0268cfeae1986b9acaa08e6d3264672b32f62e784bc8447bb52e3c79c840b79877": "0x0403000000020000000000000000000000000000000008546865204847410000001b48696c6c47726f7774684167656e637940676d61696c2e636f6d00000c4048696c6c47726f777468000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ed956e0a56bcce19c5d607c5643cace51ad268171f8b62bc30a1913178c92393f3a7b1ef3283e316": "0x040000000002000000000000000000000000000000001146494e4f50535f5042415f50524f58591c506f6c6b61646f7420426c6f636b636861696e2041636164656d792168747470733a2f2f706f6c6b61646f742e6e6574776f726b2f61636164656d79001961636164656d7940706f6c6b61646f742e6e6574776f726b0000114041636164656d79506f6c6b61646f74000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ed9d129015daefd5b2d89b7dbf920e6cd64997f1b3ee49c4de09327342dbbba971a362062817a24e": "0x000000000000000000000000000000000008677564753136390c4c494a554e205a48414e470000177a68616e673438373733393240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714edcb62732d88d5a1983ac92a9005b595553a62a3522499a38af23ae77e0c71f3b617abf004147e5b": "0x00000000000000000000000000000000000f5041524954592054495020424f5400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ee00d20c4599b66920e43fc4c70fa7fd379f66551283b0b39b7d81a8934bc38a0eff954c13211039": "0x040100000002000000000000000000000000000000000449424f0449424f0f68747470733a2f2f69626f2e696f000a68694069626f2e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ee1637b31feeb6eb1250f6160df84e6e3a53754e1da799eaf11ab3fb77bf2958b8bcb14829a8a109": "0x04030000000200000000000000000000000000000000096a75737461646576001468747470733a2f2f6a757374616465762e696f00156a75737461646576303940676d61696c2e636f6d00000b406a7573745f61646576000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ee1ca131ec7060eeac214900c36399dc45f36261c677a53ea58cce1e5d113deed23227ab523fc53f": "0x0000000000000000000000000000000000125761674d656469612054726561737572790000000000000e40746861744d65646961576167000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ee2b9d3d30c267cd2a62706ffa8c0fa1406c5a61ff4759b0b0c450f8cca8e6cb337e5f9d8a7bc21f": "0x040000000002000000000000000000000000000000000853616d737439330f53616d75656c204a2053746f6e6500001773616d73746f6e653933393340676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ee3695245c82732db5bc42b793f36f2174681d45b224f6e5222d64508a0a28454ffc085bd3d33210": "0x0000000000000000000000000000000000104252455720436f6e666572656e6365104252455720436f6e666572656e63651c68747470733a2f2f7777772e627265776265726c696e2e78797a2f001568656c6c6f40627265776265726c696e2e78797a000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ee528ad5e6be0beb7a5e9031237026ed1babffdd92e6ef7c73e3212cdcd62c7559644596c3398175": "0x00000000000000000000000000000000002153796454656b2044414f202620576f6d656e20696e20426c6f636b636861696e001268747470733a2f2f73796474656b2e6169001a6a757374696e2e676f6c6473746f6e4073796474656b2e6169000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ee610577caaf8b311cb53a337df19dd608aa3e6bb703e8e2817ee1fbddc7536f097b90f3e1910f66": "0x00000000000000000000000000000000000a4e696768744861776b00000000000011404e696768746861776b506c7567696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ee704d3e290801f7c2c7f984de75f10a8ab85a9945e79890b776970fd15e39de1cbaa0816aaab920": "0x00000000000000000000000000000000000c617265636f6e742e70726f14417265636f6e742053657276696365204c4c431568747470733a2f2f617265636f6e742e70726f2f1840617265636f6e742e70726f3a6d61747269782e6f7267117765623340617265636f6e742e70726f00000d40617265636f6e745f70726f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ee710f5d44a205f970e6681d2a90ec58ea62e45205b7663bdcebf659aa162fe0b4d31474e5c24914": "0x040000000002000000000000000000000000000000000f637265616d5f667261696368655f0e4b617265656d2046617268616e1c68747470733a2f2f7777772e63616d62726f74686572732e64652f00166b617265656d4063616d62726f74686572732e646500001040637265616d5f667261696368655f000b6b617265656d3437353200", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ee977649867b775e26f6ba0b2c596d1a471444945c9fdbdfe25e3f8862096f8f6de11d87da1a7111": "0x040000000002000000000000000000000000000000000f546865205068756e6b79204f6e65000017407468657068756e6b79313a6d61747269782e6f7267147279616e406c75636b796672696461792e696f00000c407468657068756e6b7931000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eec11b5fca7cddef5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x040000000002000000000000000000000000000000000d506f6c6b61646f747465727300001440706d656e73696b3a6d61747269782e6f72671c706f6c6b61646f74746572734070726f746f6e6d61696c2e636f6d00000f40506f6c6b61646f747465727331000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eed6418d5b4cb40034fee14ee94a7350a0c839cd4a41dd465e77717b257b55265ce7f577188c8257": "0x0403000000020000000000000000000000000000000019506f6c6b61646f74204461707073206f766572204170707314416264756c6b617265656d204f79656e65796500001844617070736f7665726170707340676d61696c2e636f6d00000e64617070736f76657261707073000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eed972f494e31056d64ccc7e84c99634be22610d47abd531d36b7793703c129fa21658f27cf5c42b": "0x0400000000020000000000000000000000000000000011426c6f636b6f7073204e6574776f726b0000001d656e67696e656572696e6740626c6f636b6f70732e6e6574776f726b000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714eedee3a95fe6fb672e2a4419244bd15a7af48d6556acfeaccb5f2f54a9892882f243160eac6eef52": "0x040000000002000000000000000000000000000000000c457175696c69627269756d0000000000001040657175696c69627269756d5f636f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ef422814dc3e336a24cf826d945c3cfddd0e76c8b1f093bc5dbcfceef4e30817337887813567fb04": "0x0401000000020000000000000000000000000000000010526f636b585f506f6c6b61646f743206526f636b581668747470733a2f2f7777772e726f636b782e636f6d0012737570706f727440726f636b782e636f6d00001040726f636b785f6f6666696369616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ef5874a92f7239d9b2312d7e5b7f8209e6b7bcc4535401fbae5dd1098fdf5fe06b5f638620e6351f": "0x040300000002000000000000000000000000000000000844697461766961214469746176696120436f6e73756c746f726961206520547265696e616d656e741968747470733a2f2f7777772e646974617669612e636f6d2f0014636f6e7461637440646974617669612e636f6d00000b40646974617669616272000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ef6d8e4c19a55597bc42516c44e05faa2b4b905fb91fa1652c65e04d376414e39f79bad7dced6c55": "0x00000000000000000000000000000000000c426974636f696e696e757300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ef87b5cbae78ad371c7529b314d9e87a1c241cfe54312668a3159e1063a0aed9f70d3db1f03f8d10": "0x040000000002000000000000000000000000000000000b6c69616d616861726f6e000010406c69616d3a7061726974792e696f186c69616d2e616861726f6e40686f746d61696c2e636f6d00000c406c69616d616861726f6e0b6c69616d616861726f6e0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f003382b575752d0b88d9d28cd21ec20bf31abadbc04405045fc0781f5c13e63d8a369d371de2e55": "0x0400000000020000000000000000000000000000000020506f6c6b61646f742050696f6e65657273205072697a652043757261746f720000001f70696f6e656572737072697a6563757261746f72407061726974792e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f0291cc04e6365cddca89b135d1a6aee0a498610a70eeaed056727c8a4d220da245842e540a54a74": "0x040000000002000000000000000000000000000000000d66657272656c6c2d636f6465002068747470733a2f2f6769746875622e636f6d2f66657272656c6c2d636f6465194066657272656c6c2d636f64653a6d61747269782e6f72671666657272656c6c3939393940676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f0391ee85a4435190fe8ecbba7a1e216bd1c3bb7e749a98fcae77b4185c5522cbad8e826d0d92371": "0x0400000000020000000000000000000000000000000016f09f9ba1204457454c4c495220444f5420f09f9ba1001468747470733a2f2f6477656c6c69722e636f6d14406477656c6c69723a6d61747269782e6f726711696e666f406477656c6c69722e636f6d000011404477656c6c69724f6666696369616c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f065def57c0ed66e2e001e3f827e6eed45a60d131d97df1a5b429ed1d0f89fdedf3eda0a16502d3a": "0x0000000000000000000000000000000000064b616d696c104b616d696c2053616c616b686965761168747470733a2f2f716472766d2e696f14406b616d696c73613a6d61747269782e6f72670b6b40716472766d2e696f00000c406b616d696c5f61626979000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f0970b3f474766050ea760413255de0a565b08a056a1b4100de8df4d5135ae4dbe51ddb6fd23de7b": "0x00000000000000000000000000000000000e4465636f646564556e697175650f556e69717565204e6574776f726b1868747470733a2f2f756e697175652e6e6574776f726b2f0012637340756e697175652e6e6574776f726b00001140556e697175655f4e4654636861696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f09b8c6be35500bfea06e81f2bd502063fe57a0447f6c1b4576a2f5a0e3e5794757bb565abce363e": "0x040300000002000000000000000000000000000000000d6c6f6c616c6f766573646f74000000146c6f6c616c7576723140676d61696c2e636f6d0000097734743376726d38000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f0ad4ed5898f2b8c80af4d39c625c32b2b6ea0b94c341ac826c49fa28451768e71afb46bc7afac04": "0x080000000002010000000200000000000000000000000000000000034147001568747470733a2f2f76616c696461746f722e6167154061677831303030303a6d61747269782e6f72671368656c6c6f4076616c696461746f722e61670000084041477831306b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f0ef7d0b30432179d6ee0051f7d4642b88616d632ba4457ca6c1169501cb9c2513c6b168f4df1252": "0x040100000002000000000000000000000000000000000d706f6c6b61646f7420636869096368696469206f6c0000187765616c746830314070726f746f6e6d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f1024f494b9ebee98a1671bda9ae5c92eb8b3697a52b8f112222eeee80994306b8c6b8d817f22351": "0x0403000000020000000000000000000000000000000014f09fa6b420426f6e65205465636820f09fa6b400000013626f6e65746563684070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f13fcfe46ac782db6eb166bebe4af81c4b929fff65b437b18eb9c7a3c668dc6c53f39d895d00943e": "0x000000000000000000000000000000000008686f616e67766d000000167675686f616e673033303740676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f141bc71dafe807f7cd5f5ff9b7eefb11b8b32c1bb1e1fc63c08019d81124b3aa85f24ae8c779a31": "0x040300000002000000000000000000000000000000000f74686520686f646c6661746865720f41726d616e646f2043617374726f00001861726d616e646f63617374727040676d61696c2e636f6d00000f40746833686f646c666174686572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f18a58b0fa8cec93dcd35aa554e0ac5db78e83bfc51649fc9b550bec1b162c70509199c1b4c0cc1a": "0x0403000000020000000000000000000000000000000011506f6c6b61646f7420496e736964657211506f6c6b61646f7420496e7369646572000015726973696e676170616340676d61696c2e636f6d00001140506f6c6b61646f74496e7369646572000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f1c80fdef53d785909cb20eb25e42c2fa106d75ee4d44b9c2bac20323a540529e9f4f40da5337bb3": "0x040000000002000000000000000000000000000000000c4d65726b6c657472696265001768747470733a2f2f6d65726b6c6574726962652e696f18406d65726b6c6574726962653a6d61747269782e6f726714696e666f406d65726b6c6574726962652e696f00000d404d65726b6c657472696265000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f1d74521ceb0a868249bf7f403ca14e4d0b0f4ff5c9d447772ce079b9b7c389e86587a2589fd7558": "0x0403000000020000000000000000000000000000000007456e7a6f726f114e617267697a61204b7572616b696e61000015656e7a6f726f2e64657640676d61696c2e636f6d00000d40656e7a6f726f5f7465616d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f1f8c71902a2a288729d9e0636390cc1ec43a6f95d06475b0e42017c6726e5f17e8e70e67cf69d56": "0x040300000002000000000000000000000000000000000b5b6167616c6c696e695d10416c626572746f2047616c6c696e6900001a616c626572746f2e67616c6c696e6940676d61696c2e636f6d0000104047616c6c696e69416c626572746f00096167616c6c696e6900", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f1fa33e1c88a625cf278bbe8c33fe8fe03c6c684b44dfc26dfb5bcefb3ef511f5446530af859c112": "0x040000000002000000000000000000000000000000000e4d697463682d576172696e657200001a406d697463682d776172696e65723a6d61747269782e6f72671d646f742e6d696e6572732e736369656e636540676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f2005476fdc5028fb4ac19c9a723477c0b31ef41dd0cfd9539a3a8d763e6a6c4c96eba6eba5e4d7f": "0x040000000002000000000000000000000000000000000c7072656d61747572617461000018407072656d617475726174613a6d61747269782e6f7267207072656d617475726174612e76616c696461746f7240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f23d01c894e574b19abbbcee9ebaa3a753da127bf308e33cac065110734cc2b689cff009d94ee62a": "0x040300000002000000000000000000000000000000000e426c6f6b42726f6b657261676500000018626c6f6b62726f6b657261676540676d61696c2e636f6d00000e426c6f6b42726f6b6572616765000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f23d30d0a705bc35c69d8c568b24a3108b6f9604f118359c268804e5a0de2b415ce978160dec2359": "0x04000000000200000000000000000000000000000000134261626573506170657328444f5429636f6d0c42616265732050617065731c68747470733a2f2f7777772e626162657370617065732e636f6d2f184062616265735f70617065733a6d61747269782e6f7267166f666669636540626162657370617065732e636f6d00000c406261626573706170657300184062616265735f70617065733a6d61747269782e6f726700", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f2988908eea3feb7586b9f5a2e5fad9175b0a540b7bf2ffcca689f99ee06cbc207b4f15a26b58363": "0x040100000002000000000000000000000000000000000e4147475245474154452e4f5041001b68747470733a2f2f6167677265676174656e6f6465732e636f6d16406167677265676174653a6d61747269782e6f7267206765742d696e2d746f756368406167677265676174656e6f6465732e636f6d000010404167677265676174654e6f646573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f2a816b6bd3a924c32a54d822b72d04a74a61509cb66376129556412bfaec8fd8d41f50ed423c957": "0x040000000002000000000000000000000000000000000c564c4144494d495250524f00001a406772617465766c6164696d69723a6d61747269782e6f7267196772617465766c6164696d69724072616d626c65722e7275000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f2ad51c6c76e65c8d0bb4d4b0f530b756f58c53f814710ee17fc79324d6b21c3ae22398c1ed5dc1b": "0x0403000000020000000000000000000000000000000017546f6b656e67756172642042656e65666963696172791b426c6f636b20536f6c7574696f6e732053702e207a206f2e6f2e000016636f6e7461637440746f6b656e67756172642e696f00000e546f6b656e67756172645f696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f3194d5c012787bae6edf7a6551c93e9ac1c9bd0daf98b25abefc01eb7afa918d322539b997d462e": "0x00000000000000000000000000000000000b646f63746f726368696e0e7469656e20627569206d696e6800001c6275696d696e687469656e3139383475734067616d696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f33c3d7c430f900954eb2d8672a2aaa09a149fae1d2cca54cda463da043b276e246881a2f4e9e179": "0x00000000000000000000000000000000000b506174696b6120696e630b506174696b6120496e631868747470733a2f2f7777772e706174696b612e6465762f001267756c63616e40706174696b612e64657600000c4072697365696e5f636f6d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f3430f9486eebcb28423c2127c4f7199f134cad21d786dc788effc31b9164767bbb4cd8ce740ab29": "0x00000000000000000000000000000000000e43726f7764204163636f756e7400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f3a8cb14bfca5d3d82c5c0e26848d49b32a7ae09a67f6822f2a18310b69a3ed9c31ee02144020826": "0x040000000002000000000000000000000000000000000f486173686564204e6574776f726b0000000000000f404861736865644e6574776f726b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f3bb05b647c9705134ba460ddb05a10ff78533182868e683684300888b0029e09123a945f0823020": "0x040300000002000000000000000000000000000000000645796173750e45796173752042697268616e750000156a6f736874656368333940676d61696c2e636f6d00000f40457961737542697268616e7537000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f3d619806458ce0ea8070648f6e43b7d8a8f10418b696130d2046c10645f10085de80e6098b85f09": "0x04000000000200000000000000000000000000000000134875746368202d20436f6e74726f6c6c65720000001d70617072696b615f766f6361626c65306a4069636c6f75642e636f6d00001040746865666c79696e676875746368001040746865666c79696e67687574636800", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f3e3959f84b063bcd6c29a7c39cee45b0e045a94081bc188ef73be2be086d66aefd850fc7eeacc45": "0x040100000002000000000000000000000000000000000e4f6e46696e616c6974792e696f0f4f6e46696e616c697479204c74641a68747470733a2f2f7777772e6f6e66696e616c6974792e696f124069616e68653a6d61747269782e6f726717706f6c6b61646f74406f6e66696e616c6974792e696f00000c404f6e46696e616c697479000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f4137709d90899e9fb2d80eed7a8b28753f70afb95bda3edda074ffd7708116acb919cf484854777": "0x0000000000000000000000000000000000094c6564676572203100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f41c7a3cee29ba2d784c9c917350f814d7b3600e688da3ce67ad920cc321be65addb3e6ae0b51451": "0x040000000002000000000000000000000000000000000e4578696c65642052616365727300000016696e666f406578696c65647261636572732e636f6d00000e406578696c6564726163657273000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f41c81026445f16f0ad8516cbee7751114909ea4199c27ec84911bbc85c01449c3b418a754f91e69": "0x04010000000200000000000000000000000000000000075472616e73580a7472616e73782e696f1268747470733a2f2f7472616e73782e696f13407472616e73783a6d61747269782e6f72670d6869407472616e73782e696f00000a405472616e73583131000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f428a77ef581364ada86cee6a178b43ac0e605d8dac093106dadcac8f00f0a8a5b8431c4b56a775b": "0x00000000000000000000000000000000000e566963746f72204d657a72696e0e566963746f72204d657a72696e1368747470733a2f2f74726169742e7465636813406d657a72696e3a6d61747269782e6f726719766963746f722e6d657a72696e4074726169742e746563680000000a7472616974746563680000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f43858092a1274370ce14f14486f3fb3513a44a19970a0bbf4da8b642c9837cfa251b33384dec257": "0x040000000002000000000000000000000000000000000c477265656e20436c6f756400001840677265656e2d636c6f75643a6d61747269782e6f72671f677265656e2d636c6f75642d6b7573616d61406f75746c6f6f6b2e636f6d00000f40477265656e436c6f75644b534d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f4f0e33d9ec9b36d224872ad60345d726772361291b4cf7f3e1b8087f2277e2b5cc25a9b309f4727": "0x040000000002000000000000000000000000000000000753656e7365690c53656e736569204e6f64651768747470733a2f2f73656e7365696e6f64652e636f6d14406a6368697474793a6d61747269782e6f726714696e666f4073656e7365696e6f64652e636f6d00000c4053656e7365694e6f6465000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f53e59e4baae9e387082636ca5c22aaad30b64c9fb6af0c6b91055d1834f1a5badaeec7091dd8b8b": "0x040000000002000000000000000000000000000000000846696c6970706f1246696c6970706f204672616e6368696e6900194066696c6970706f3a776562332e666f756e646174696f6e1866696c6970706f40776562332e666f756e646174696f6e00000d4066696c6970706f77656233000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f5485e0e720d2144ed5f93b8f24ce936d3df4faad4c247e1e57503b2be42904520d0018162ebc76c": "0x00000000000000000000000000000000000f42656e736869676572752044414f00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f5912d36263b8f7806a2129ac63420996a943d410317bd705dad70b5073f4f8edd63901fe332d56d": "0x00000000000000000000000000000000001357696c6c69616d202854616c69736d616e290d57696c6c69616d204368656e0016407265706c67686f73743a6d61747269782e6f72671577696c6c69616d4074616c69736d616e2e78797a000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f5bcc47f0db2d6a236a561a21a65f5106286c1d983cfaf4da5115a90073d1c9c6e053d4bbf596d27": "0x04000000000200000000000000000000000000000000104865616c74687920506f636b65747300000018696e666f406865616c746879706f636b6574732e6e6574000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f5d0ba7a9e0e043b545b7958451cd22afb0367d6c99af1360190813571c47b8a94a055d575d38249": "0x040000000002000000000000000000000000000000001443525950544f204a41434b2053504152524f5700001e406a61636b73706172726f7763727970746f3a6d61747269782e6f726700000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f5d53ab33cd6333ae62fd2fd91105ee48911b567b815318f9a9c5f1002c88dc3c568aea2e0f8eb34": "0x00000000000000000000000000000000000c546f746f2043727970746f0e546f746f2043727970746f6e791a68747470733a2f2f746f746f63727970746f2e7375622e69641740746f746f63727970746f3a6d61747269782e6f726717746f746f63727970746f373740676d61696c2e636f6d00000e40746f746f63727970746f3737000d746f746f63727970746f373700", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f5e5a1e323e94d19728453ef7429569c384e3e39ffc0f147aabb8a71185dedca4ffab6fe44815500": "0x0000000000000000000000000000000000065452554a4900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f5f2eeffe9c9cf62a22ff027237d3f1f468e46ebaa49f910df2affac228efed30a62ab3daaa99e57": "0x0403000000020000000000000000000000000000000007763064306c3007763064306c30000013736f72616e6f64654070726f746f6e2e6d650000094076696e7364656c000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f609c73b2b467ef264c3558888b7bcb721cf6803dac859f125de0afa615c461bd1b2587ad461c81d": "0x040300000002000000000000000000000000000000000b53616c696e61204465760000001773616c696e6164657664657640676d61696c2e636f6d00000b53616c696e615f646576000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f6198f55b54f219a44a65df7135dd3fd5e958140e4800abdb5db615d7f4aefd21d5d746b7e456b34": "0x040000000002000000000000000000000000000000000944616e70756c6c6f0f5341424955204d5548414d4d4144000015736d64616e66696c6c6f40676d61696c2e636f6d00000e40535f4d5f44616e70756c6c6f000f5361686167756d656c233032313200", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f623c11edc7a4858d6c8c22c86dda329b85d76f7f7d68623ddeb1e9649819900e6b715967a0aff36": "0x00000000000000000000000000000000000c526567656e63792d3031311757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f6678cee6fb3557e047d9556653e9335812ba87a2463e04f455a68dd5391e22397d55d89cb8cb116": "0x00000000000000000000000000000000000a525542524f5353414e00000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f668ff8ab90e05ebf047b090d2e8e2406ccc97a60df3452fc8574333b91009c91e00ce8fe11551f9": "0x00000000000000000000000000000000000c4170696c6c6f6e204d4b54001468747470733a2f2f6170696c6c6f6e2e696f2f0015636f6d6d756e697479406170696c6c6f6e2e696f000009404170696c6c6f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f67278c03253af6c20f057efcf26c451d58366c5906a3468517b624fb5674a547422726ebea3eb16": "0x000000000000000000000000000000000008446f744b696e6700000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f685fe8bfef999d22074f20847843acac10a56948a6634e827bcaabb455b9c958d285b2ec9090314": "0x04000000000200000000000000000000000000000000094e5244204c616273000000136572696e7665373740676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f69bb097d37ec3ca7e406ae038214b907dfa08e190c4d61bf43d28be997541747e3465ada083d262": "0x04010000000500000000000000000000000000000000064a554c494f0f4a554c494f2043415244454e415300001364697061706f636240676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f6a51af30694c2c19a820a93a16c1def7154cdd57c063e1a4630411e076f1e1e1ce93ca953af333e": "0x040300000002000000000000000000000000000000000b41434f42204a61736f6e0f4a61736f6e2046726965646d616e00001b61636861696e6f66626c6f636b73406f75746c6f6f6b2e636f6d00000a41434f424a61736f6e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f6db4258f530c658ca676edbf98876ab4fd6ad742f7954773a0e234345d6551a8ee9738a8ae33d42": "0x040100000002000000000000000000000000000000000753524c6162731f5352205365637572697479205265736561726368204c61627320476d62481268747470733a2f2f73726c6162732e6465000f6e6f686c4073726c6162732e6465000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f6e7d7d57efb1158faf96a8cf4ac3d4de8b04b607a7a70cc4aeb5ea742ea2c59836057a2678dad64": "0x000000000000000000000000000000000017537061636577616c6b207c20506f6c6b61204861757300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f73159fa6ef16058625b21fc3cfe39c52cf4d753fe8ad5f3b2ace458d9d11d266f080216e5e885e6": "0x040000000002000000000000000000000000000000001d4368616f7344414f204576656e747320426f756e74792050726f78790000000000000a404368616f7344414f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f738bf8232b5657ec2401487a14072ba228693b006c7be226b17b2bf498630b41631f91065e17875": "0x040000000002000000000000000000000000000000001143726f75746f6e4469676974616c2d3100001540746f7861333333333a6d61747269782e6f72671763726f75746f6e6469676974616c40616f6c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f740ceb4be9720dce0f0f94962fc0a8c1a0f0527dc8e592c67939c46c903b6016cc0a8515da0044d": "0x00000000000000000000000000000000000a656c697a6162657468000013406c696c6574683a6d61747269782e6f726700000000056e6f6f740000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f77e7d1a38a5e4b2befe71ab1f2719097a35e93e9114427bcd71e5109c14d82b9b3d9080f16dec2f": "0x04010000000200000000000000000000000000000000045a4b56001968747470733a2f2f7a6b76616c696461746f722e636f6d2f001668656c6c6f407a6b76616c696461746f722e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f78baea38e12315a6c7fdb8b8eaad1af9faaf918493606e1a3e8c20f9d852773ab5ebfbb93bd1948": "0x040000000002000000000000000000000000000000000757656233476f0757656233476f1468747470733a2f2f77656233676f2e78797a2f0010696e666f4077656233676f2e78797a0000084057656233476f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f79f574773cdeb99eacf14f2850c8d4145f07936bad43325bb0d150b1111757e944d62ecd0abcce6": "0x00000000000000000000000000000000001bf09faaac2054616c69736d616e20476f762044656c6567617465001568747470733a2f2f74616c69736d616e2e78797a000000000f40776561726574616c69736d616e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f7a35e45eb5c8f006a4e76d530fa715a95388b889ad33c1665062c3dec9bf0aca3a9e4ff45781e48": "0x000000000000000000000000000000000007526f73c5a56113526f737469736c6176204c69746f766b696e0000000000000009726f73746164657600", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f7f56e24024aa32cc0aacd65996d35fa753d12fc099078f0357d5628950585143e953675c7aee712": "0x00000000000000000000000000000000001c313335204341504954414c20494e434f52504f524154454420303310416e74686f6e7920457566656d696f0f68747470733a2f2f3133352e696f000b616365403133352e696f00000f407265616c4143457566656d696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f7f82bb21d26a5d0647201c657cb2ea9a6f15069728a26a9bf195c93adfa947cace0e7df16d7e102": "0x040100000002000000000000000000000000000000001054726962652f416e67656c4861636b12416e67656c4861636b20507465204c74641768747470733a2f2f616e67656c6861636b2e636f6d2f00186a757374696e2e6e6740616e67656c6861636b2e636f6d00000b40416e67656c4861636b000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f7f8fa46e9ed81a89ce3de1cd55ad6f4ab351ab212431d94cec798e1727176cca174fa661c8f636e": "0x040300000002000000000000000000000000000000000856696e63656e740f56696e63656e7420476564646573001440766765646465733a6d61747269782e6f72671576696e63656e7440736e6f77666f726b2e636f6d0000094076676564646573000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f80b38f6992cd55c0e06a7b3013bdbcd4f649d47a82e646be1223be5ceb8d32e8847fb7dbd2ccf57": "0x0401000000020000000000000000000000000000000015535543482046494e54454348205354414b494e4715535543482046494e54454348205354414b494e470018407375636866696e746563683a6d61747269782e6f7267196465766f7073407375636866696e746563682e636f2e6e7a00000d407375636866696e74656368000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f81ec86fea69eb9bfb430ce27d4be287efa0c50ef42c36c3ef14e55ecaf40ac53e1df33b0720d176": "0x00000000000000000000000000000000000631574542341757656233204c6567616c20456e67696e656572696e671268747470733a2f2f6f6e656c61772e7573001073636f7474406f6e656c61772e757300000b4074656e66696e6e6579000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f898daa4756e63c5ca467889146a7467efffe20d97c077cb961d176dfa96f669857d3aaa8485fc0a": "0x0000000000000000000000000000000000114d696368616c40416c6570685a65726f0f4d696368616c205377696574656b1668747470733a2f2f616c6570687a65726f2e6f7267001d6d696368616c2e7377696574656b40616c6570687a65726f2e6f7267000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f98bc77ab23b03eed401f460e0251ed41d7fb32ca463b5233b620cb9569eef5327def27fbd7c7b57": "0x04000000000200000000000000000000000000000000084361626c652d58000012406361626c653a6d61747269782e6f72671e6379636c6f707373756d6d6572734070726f746f6e6d61696c2e636f6d00000f4073756d6d6572735f6361626c65000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f9a33b675b34b0c00a7cbce722b9e8f16e7c0a9bb209f761cc94e25191e7599cb0d0234d1ad11d66": "0x00000000000000000000000000000000000a4942502050726f787900000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f9ace18a7f38e38e94204c6fb288a7302b1ff34b6aafd5edf6de6b895db07f3d6ca1a75d1681f37e": "0x040300000002000000000000000000000000000000000f506564726f204c6f75726569726f1e506564726f2052616661656c204c6f75726569726f204c65616e64726f000019706564726f2e706f6c6b61646f7440676d61696c2e636f6d000011404368656d6963616c4f7074696f6e73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f9b0358c010b6eb69ecfdf118a26b3c2926e7ba57cb2c08eac9e981318d88b958c0e06e97e774c1e": "0x040000000002000000000000000000000000000000000f556e69717565204e6574776f726b0000001568656c6c6f40756e697175652e6e6574776f726b00001140556e697175655f4e4654636861696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714f9e4ceaaa31d1e273ec9398e00987bf25afda5d3f3e3a1a1f1b121d8728d7da7a30dd1b545eb3d49": "0x0403000000020000000000000000000000000000000012436861696e616c79736973204c6567616c0000001b6a616d65732e7061696b40636861696e616c797369732e636f6d00000a6b6f6e657368693133000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fa2d9158713aa2daac18d0d46a223a6576df578a0c7f7e087d7dceb3608cbdf792b279c532631c09": "0x00000000000000000000000000000000001367726568656920706f6c6b61646f74204772000013406772656865693a6d61747269782e6f72671167726568656940676d61696c2e636f6d000009404772655f486569000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fa4279b211bd820ab22c2075548019dd268e74f3aa69c9703b129e989d230f935797975d5ed9247d": "0x04000000000200000000000000000000000000000000125a6574657469632056616c696461746f7200001d407a65746574696376616c696461746f723a6d61747269782e6f72671e6f70657261746f72407a65746574696376616c696461746f722e636f6d000010405a6574696356616c696461746f72000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fa4c4ebe45297c58e8435412cd8649ed9d5212898af799270589d46d53af6c9b30dc6103b5917454": "0x0000000000000000000000000000000000064b61696f6800000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fa701643441fc0cc0a73047c4bca127582fa1541c886915b2a872c95fb538fcea099bab0a47b8f54": "0x040100000002000000000000000000000000000000001153554e5348494e454155544f53444f5400001f4073756e7368696e656175746f736e6f6465733a6d61747269782e6f72671c73756e7368696e656175746f73696e666f40676d61696c2e636f6d0000114053756e7368696e655f4175746f735f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714faa55b79d5b983962aad2d511e0a2ade151b1e3442675156b71aea1f049ce004311cf33c9d70c474": "0x040000000002000000000000000000000000000000000a53656261737469616e00001c4073656261737469616e63726970746f3a6d61747269782e6f72671b73656261737469616e63727970746f3840676d61696c2e636f6d0000114053656261737469616e43726970746f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fad7d2ce54667f8c1ea80b0dde0e207c8ec57ac05fbec636502cb216bb423642919679fe8f074051": "0x0401000000020000000000000000000000000000000015525454492d353232302028504f4c4b41444f54290f5261756c20526f6d616e75747469001c407261756c2e727474693a6d61747269782e7061726974792e696f177261756c406a7573746f70656e736f757263652e696f00000b406e6163686f72747469000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fadf532a0cd06f2c30bdeebf156313a98c5235fd02e6086502e320acf1791ec4390b8bb4735e5033": "0x04030000000200000000000000000000000000000000064b6f6b6f4c000000186e696b686f6c65636c7563617340676d61696c2e636f6d00000e406e696b686f6c656c75636173000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fae4899c3cb40a9ee6c87f4b9656543d64815b6626ba2ee48c8c54de210dfa98bfe53333fdf26744": "0x040100000002000000000000000000000000000000000d41646472657373204c6162731141646472657373204c61627320496e6300001b6861727269736f6e2e636f6d666f727440676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fb0bc5a64f81e0ca7cbb0ed8bf228935241774290753bf282020d73e45f6724b0196c97b3bd53462": "0x040000000002000000000000000000000000000000000b6f70656e6269746c6162000018406f70656e6269746c61625f3a6d61747269782e6f72671c6461766964652e6f70656e6269746c616240676d61696c2e636f6d00000c404f70656e4269744c6162000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fb2ad09a8a07f8da26c3fc13ac393359abbdbec7ad3de6f6dd110169e0ef401b19c693afcfc38f08": "0x040100000002000000000000000000000000000000001e545247207c204469676974616c20496e6e6f766174696f6e2046756e642054524720496e737572616e63652042726f6b657273202850747929204c74641f68747470733a2f2f7777772e726f6265727467726f75702e636f2e7a612f00184b6576696e40726f6265727467726f75702e636f2e7a6100000d40526f6265727447726f7570000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fb7fa862606e0cd75626fb92ce5aacc7ac4dd042dd55308832ba8dde38c557b140ae8740948fe76c": "0x040000000002000000000000000000000000000000000763776368756100000000000000076368756163770000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fb983efc82b2f0f392d5c737ce301d743f39f522a06af54bc5f37c1f9037c22c40722ec16a07364d": "0x00000000000000000000000000000000000b646174646f742e6f72670000000000000b40646174646f746f7267000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fbc4385a1a8a38b114dd7b536252ac1b8294062b9f93fbd0b8c2632010aef94753354a3721def21b": "0x00000000000000000000000000000000000f506f6c6b61204578706c6f726572001168747470733a2f2f706f6c6b612e696f000b6940706f6c6b612e696f00000940706f6c6b61696f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fbe4b7926dff6bda8c7b7c1af5f115225fb19bbba44e2b9b68acc26d2dfeff02d948183ffcdc0d0f": "0x000000000000000000000000000000000008484d204d4152530f48616e747573204d6f737465727400001968616e7475736d6f73746572743140676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fbeb0670b76a6ac8ea614ce8bc4baa3c39e8019636cc6f8d9a67291f19dabb2d898a308b6b93b72e": "0x000000000000000000000000000000000009616e67657765623312416e67656c696361204772617465726f6c000020616e67656c6963616161677265726f6c2e3037303440676d61696c2e636f6d00000b616e6765696e77656233000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fbef5ae8c51b799f2a0e4eb85f552a217a9ddd86b965229be3210d010f0f0f78398d8d9a5a5f0537": "0x040000000002000000000000000000000000000000000f395374616b65206279203947414700001840397374616b652e396761673a6d61747269782e6f726719397374616b652e706f6c6b61646f7440396761672e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fc06d1b8b9ab844be64bc946c10a1f75e683f236e72a44d6d61b3bdcf72ffd8c738e488efc6e1567": "0x040000000002000000000000000000000000000000000b4d61746843727970746f001c68747470733a2f2f7777772e6d6174682d63727970746f2e636f6d17406d61746863727970746f3a6d61747269782e6f7267116d61746863727970746f40696b2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fc6a42b3b802fc9518d54ea25ad26acd7094fba6bdb278e769f9ac350cc36b2f631da13fa92bed79": "0x040100000002000000000000000000000000000000000c5374616b652e576f726b730c5374616b652e576f726b731468747470733a2f2f7374616b652e776f726b73154077656264657639393a6d61747269782e6f726711696e666f407374616b652e776f726b7300000c405374616b65576f726b73000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fcbacdb7dda02f70504baa1f30e0267703f471e94de948bd9f09caaf3c3a2f4c2dad3685a79ee000": "0x0400000000020000000000000000000000000000000006616e76656c00001240616e76656c3a6d61747269782e6f726717616e647265792e76656c646540676d61696c2e636f6d00000d40416e6472657956656c64650007616e76656c2e00", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fcd27506d243061f2edf0fd8948ea642f135b314b1358c77ec6d0a4af83220b6ea18136e5ce36277": "0x04000000000200000000000000000000000000000000124368726973404f414b204e6574776f726b0000000f6368726973406f616b2e7465636800000d4063687269736c6932303436000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fce864e50b620f11ad65f8153459863f71364d90f27265da0084743373e0c75ae69d651efc95b62e": "0x00000000000000000000000000000000000b506f6c6b61205269636f0b506f6c6b61205269636f1668747470733a2f2f706f6c6b617269636f2e636f6d0000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fd0157da0fed5e2b4805c5f3b7f9abe9da4189cead8a9a8453ab2cfc637a0f9cd93d59661aba9954": "0x04030000000200000000000000000000000000000000074a6179436565164a756c69616e6120506572657a20436172646f7a6f00001d6a756c69616e61636172646f7a6f313740686f746d61696c2e636f6d00000c49616d4a756c69616e6143000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fd050f61009683f92e62fe384b5820c81695aa763b54daf138b67b014714c5c6f75d68e4d1b3785b": "0x00000000000000000000000000000000001944617272656c6c202d20506f6c6b61646f74204163616c6100000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fd0ec0420825c1bfce2490656709c33bdd50d72dc0ec562bb72db84945ef7a1be45be14bbc6fc877": "0x04000000000200000000000000000000000000000000047369780000124068657866663a6d61747269782e6f7267127369784063727970746f6374662e6f726700000c4053697854686544617665000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fd32583ae5aff69da2d756efc15354d37aafecf3a850808b1276d1cee16f14a14397e81b616e9a4a": "0x040300000002000000000000000000000000000000000a5368696b615f7770771657697061776565204c696d6b6173657473616b756c00001b6b77616e672e6c696d6b40656e746572746f73746172742e636f00000e40456e746572746f5374617274000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fd4f36a10fe246e4e87f578ff46b741dafc7ffd2b6e223f50269ea6b8260b9bde108d6e5870f877c": "0x040300000002000000000000000000000000000000000a64617374616e73616d0000001664617374616e73616d313640676d61696c2e636f6d00000b6b696e676c656172645f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fd64180d1412f211888c6e2c05a5db8fb2ce4a31ffc434049fac143401d77e862dea055bf5607073": "0x0000000000000000000000000000000000134d61726375732720446f742077616c6c657400000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fd71c324186304a590c5d23600de15438062c91c4976d001a14cecf539c2b8db9399c4b56e6b1166": "0x0403000000020000000000000000000000000000000008487970654c6162084a6f65204b696d00001268656c6c6f40687970656c61622e636f6d00000b40676f687970656c6162000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fd8929a3a5cb6d462fd09555017774e81bda25510ca97864e9bc350620523fd11508f99b8fdc8b7d": "0x0000000000000000000000000000000000145765625a65726f205632204d756c74697369670000000000000d406a6f696e7765627a65726f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fd9785f8772870809eee1e58c17ff7b037f2da5766e9bc78d5568c58d45cf363b9630ef32b2ccd79": "0x040100000002000000000000000000000000000000000a507572655374616b650e507572655374616b65204c74641b68747470733a2f2f7777772e707572657374616b652e636f6d2f0013696e666f40707572657374616b652e636f6d00000d40707572657374616b65636f000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fdc037f375e9e4c3961db3fef76ece3b513f6896e19fd09588d62f5c6a99d577275d7db8005e610f": "0x040100000002000000000000000000000000000000000d4175677573746f204c6172611a4365736172204175677573746f204c61726120476176696e6f0000126175677573746f406861736865642e696f00000d404175677573746f4c617261000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fdc296dd3da2e409e472fc53af693baa4199e1edd6a3d86badfc6d2d4aad3331d8a40fd6b18b751a": "0x00000000000000000000000000000000000d6361726c6974726f7374657300000000000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fdf70154c5cd66e448b3ed689e1d1c790052dfea406cff690cbf0354a3598c961c7e8054068a6c9d": "0x040000000002000000000000000000000000000000000a70616e7661645348310000134070616e7661643a6d61747269782e6f72671669616d70616e766164696d40676d61696c2e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fe0f1e8f0e428f0ffa859a32665b4dd29d647732d0d8e0b794cb21ae44862153bfca9522c14e9c71": "0x040300000002000000000000000000000000000000000a486f7065436c6172790d486f706520476f6f646d616e000013486f7065696f756d40676d61696c2e636f6d000009486f7065696f756d000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fe7066486cbde784e0d0031d0a450dfc4bb16333fe575dfb8452bb0f79c768181478190a5d9a653f": "0x04000000000200000000000000000000000000000000144c6974656e74727920436f6c6c61746f72203100000012696e666f406c6974656e7472792e636f6d000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fea0c4e647948252eccdb05e3f618a3da74db2ca0a4c635b94c388e3e620c7ef7093ef34f206ca7c": "0x04030000000200000000000000000000000000000000144175746f6e6f6d6f75732050726f6a65637473144175746f6e6f6d6f75732050726f6a6563747300001b7468656f406175746f6e6f6d6f757370726f6a656374732e636f000011406175746f6e6f6d6f75735f77656233000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fecc695c6a87d6466ecb07b7a8f63febc1c458983d4ec414e4cbb76ec3f128d9cedd5f4c4fd2965b": "0x040000000002000000000000000000000000000000001073306d65306e652d756e6b6e30776e000000000000001073306d65306e652d756e6b6e30776e0000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714fedff71a46bae1ee98388d733bfdc1edad482ef118803415e01703eb7e20e7822a3a322f999c3177": "0x0000000000000000000000000000000000084d756375323536104d7563756e67757a69204d6f7365730000126d6f656d75637540676d61696c2e636f6d00000d4063727970746f70726f5567000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ff02177bcafc5807e274c36b3b6efe4d0de454afa853ce1e7a4f789aaa2f3c9613a61fb365f9362b": "0x040000000002000000000000000000000000000000000746696e7369670e53746576656e20426f796e657300134066696e7369673a6d61747269782e6f72671073746576654066696e7369672e696f000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ff643fe3320aabbafcba1bf303b55be82bf6e48504909dfb85be27bcbbb2a330e9f4fc1261d8f02a": "0x040000000002000000000000000000000000000000000e47656e657269632d636861696e00001a4067656e657269632d636861696e3a6d61747269782e6f726716696e666f4067656e657269632d636861696e2e696f00000f4067656e657269635f636861696e000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ff68e68c8f4329adc0fb1e91315aff633f7889e1b32c46b2b33e534d059f29a4fda276ce4e59f83a": "0x04000000000200000000000000000000000000000000084469616d6f6e640000001776616c69646469616d6f6e644070726f746f6e2e6d65000000000000", + "0x2aeddc77fe58c98d50bd37f1b90840f9cd7f37317cd20b61e9bd46fab8704714ffc544aa04d3feb9a69484f2b10ec2f1dea19394423d576f91c6b5ab2315b389f4e108bcf0aa2840": "0x000000000000000000000000000000000005466162690000184066616269616e3a776562332e666f756e646174696f6e0000000d4066616269616e676f6d7066000000" + }, + "childrenDefault": {} + } + } +} diff --git a/cumulus/polkadot-parachain/chain-specs/people-polkadot.json b/cumulus/polkadot-parachain/chain-specs/people-polkadot.json new file mode 120000 index 000000000000..44fead1c49e0 --- /dev/null +++ b/cumulus/polkadot-parachain/chain-specs/people-polkadot.json @@ -0,0 +1 @@ +../../parachains/chain-specs/people-polkadot.json \ No newline at end of file diff --git a/cumulus/polkadot-parachain/src/chain_spec/people.rs b/cumulus/polkadot-parachain/src/chain_spec/people.rs index db8756e68819..6100a15018cb 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/people.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/people.rs @@ -63,8 +63,9 @@ impl PeopleRuntimeType { PeopleRuntimeType::Kusama => Ok(Box::new(GenericChainSpec::from_json_bytes( &include_bytes!("../../chain-specs/people-kusama.json")[..], )?)), - PeopleRuntimeType::Polkadot => - todo!("Generate chain-spec: ../../chain-specs/people-polkadot.json"), + PeopleRuntimeType::Polkadot => Ok(Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/people-polkadot.json")[..], + )?)), PeopleRuntimeType::Rococo => Ok(Box::new(GenericChainSpec::from_json_bytes( &include_bytes!("../../chain-specs/people-rococo.json")[..], )?)), diff --git a/prdoc/pr_5124.prdoc b/prdoc/pr_5124.prdoc new file mode 100644 index 000000000000..966761721fc7 --- /dev/null +++ b/prdoc/pr_5124.prdoc @@ -0,0 +1,20 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add Polkadot Chain genesis chainspec + +doc: + - audience: Node Operator + description: | + The Polkadot People Chain can now be run as all other system parachains without specifying a + chainspec. However this will soon be deprecated and `--chain ./chainspec.json` should continue + to be used instead. + + - audience: Runtime User + description: | + The Polkadot People Chain chainspecs have been added to the polkadot-sdk repo and can now be + pulled from there along with all other system chains. + +crates: + - name: polkadot-parachain-bin + bump: minor From 8c61dbadb8bd65388b459153b533777c55c73aa5 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Thu, 25 Jul 2024 15:23:28 +0200 Subject: [PATCH 014/382] Getting Started Script (#4879) closes https://github.com/paritytech/polkadot-sdk/pull/4879 Provide a fast and easy way for people to get started developing with Polkadot SDK. Sets up a development environment (including Rust) and clones and builds the minimal template. Polkadot address: 16xyKzix34WZ4um8C3xzMdjKhrAQe9DjCf4KxuHsmTjdcEd --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Oliver Tale-Yazdi --- README.md | 6 ++ scripts/getting-started.sh | 147 +++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100755 scripts/getting-started.sh diff --git a/README.md b/README.md index 92901d070db0..a070d61170f6 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,12 @@ forks](https://img.shields.io/github/forks/paritytech/polkadot-sdk) +## ⚡ Quickstart +If you want to get an example node running quickly you can execute the following getting started script: +``` +curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/scripts/getting-started.sh +``` + ## 📚 Documentation * [🦀 rust-docs](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/index.html) diff --git a/scripts/getting-started.sh b/scripts/getting-started.sh new file mode 100755 index 000000000000..57806280914b --- /dev/null +++ b/scripts/getting-started.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env sh + +set -e + +prompt() { + while true; do + echo "$1 [y/N]" + read yn + case $yn in + [Yy]* ) return 0;; # Yes, return 0 (true) + [Nn]* ) return 1;; # No, return 1 (false) + "" ) return 1;; # Default to no if user just presses Enter + * ) echo "Please answer yes or no.";; + esac + done +} + +prompt_default_yes() { + while true; do + echo "$1 [Y/n]" + read yn + case $yn in + [Yy]* ) return 0;; # Yes, return 0 (true) + [Nn]* ) return 1;; # No, return 1 (false) + "" ) return 0;; # Default to yes if user just presses Enter + * ) echo "Please answer yes or no.";; + esac + done +} + +cat </dev/null 2>&1; then + echo "\n✅︎🍺 Homebrew already installed." + else + if prompt_default_yes "\n🍺 Homebrew is not installed. Install it?"; then + echo "🍺 Installing Homebrew." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" + else + echo "❌ Cannot continue without homebrew. Aborting." + exit 1 + fi + fi + + brew update + if command -v git >/dev/null 2>&1; then + echo "\n✅︎🍺 git already installed." + else + if prompt_default_yes "\n🍺 git seems to be missing but we will need it; install git?"; then + brew install git + else + echo "❌ Cannot continue without git. Aborting." + exit 1 + fi + fi + + if prompt "\n🍺 Install cmake, openssl and protobuf?"; then + brew install cmake openssl protobuf + else + echo "🍺 Assuming cmake, openssl and protobuf are present." + fi +elif [ "$os_name" = "Linux" ]; then + # find the distro name in the release files + distro=$( cat /etc/*-release | tr '[:upper:]' '[:lower:]' | grep -Poi '(debian|ubuntu|arch|fedora|opensuse)' | uniq | head -n 1 ) + + if [ "$distro" = "ubuntu" ]; then + echo "\n🐧 Detected Ubuntu. Using apt to install dependencies." + sudo apt install --assume-yes git clang curl libssl-dev protobuf-compiler + elif [ "$distro" = "debian" ]; then + echo "\n🐧 Detected Debian. Using apt to install dependencies." + sudo apt install --assume-yes git clang curl libssl-dev llvm libudev-dev make protobuf-compiler + elif [ "$distro" = "arch" ]; then + echo "\n🐧 Detected Arch Linux. Using pacman to install dependencies." + pacman -Syu --needed --noconfirm curl git clang make protobuf + elif [ "$distro" = "fedora" ]; then + echo "\n🐧 Detected Fedora. Using dnf to install dependencies." + sudo dnf update + sudo dnf install clang curl git openssl-devel make protobuf-compiler + elif [ "$distro" = "opensuse" ]; then + echo "\n🐧 Detected openSUSE. Using zypper to install dependencies." + sudo zypper install clang curl git openssl-devel llvm-devel libudev-devel make protobuf + else + if prompt "\n🐧 Unknown Linux distribution. Unable to install dependencies. Continue anyway?"; then + echo "\n🐧 Proceeding with unknown linux distribution..." + else + exit 1 + fi + fi +else + echo "❌ Unknown operating system. Aborting." + exit 1 +fi + +# Check if rust is installed +if command -v rustc >/dev/null 2>&1; then + echo "\n✅︎🦀 Rust already installed." +else + if prompt_default_yes "\n🦀 Rust is not installed. Install it?"; then + echo "🦀 Installing via rustup." + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + else + echo "Aborting." + exit 1 + fi +fi + +# Ensure that we have wasm support +if prompt_default_yes "\n🦀 Setup the Rust environment (e.g. WASM support)?"; then + echo "🦀 Setting up Rust environment." + rustup default stable + rustup update + rustup target add wasm32-unknown-unknown + rustup component add rust-src +fi + +if [ -d "minimal-template" ]; then + echo "\n✅︎ minimal-template directory already exists. -> Entering." +else + echo "\n↓ Let's grab the minimal template from github." + git clone https://github.com/paritytech/polkadot-sdk-minimal-template.git minimal-template +fi +cd minimal-template + +echo "\n⚙️ Let's compile the node." +cargo build --release + +if prompt_default_yes "\n🚀 Everything ready to go, let's run the node?"; then + cargo run --release -- --dev +fi From 3b9c9098924037615f3e5c942831e267468057d4 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 25 Jul 2024 17:03:03 +0200 Subject: [PATCH 015/382] Dependabot: Group all CI dependencies (#5145) Dependabot is going a bit crazy lately and spamming up a lot of merge requests. Going to group all the CI deps into one and reducing the frequency to weekly. Maybe we can do some more aggressive batching for the Rust deps as well. --------- Signed-off-by: Oliver Tale-Yazdi --- .github/dependabot.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3277a6e4607a..12f81b04d3a1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,13 +5,17 @@ updates: directory: '/' labels: ["A1-insubstantial", "R0-silent"] schedule: - interval: daily + interval: weekly + groups: + ci_dependencies: + patterns: + - "*" # Update Rust dependencies: - package-ecosystem: "cargo" directory: "/" labels: ["A1-insubstantial", "R0-silent"] schedule: - interval: "daily" + interval: "weekly" groups: # We assume these crates to be semver abiding and can therefore group them together. known_good_semver: From 6720279fb3aac12ad525785d2366cdf30da4d78c Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Thu, 25 Jul 2024 19:27:24 +0300 Subject: [PATCH 016/382] authorithy-discovery: Make changing of peer-id while active a bit more robust (#3786) In the case when nodes don't persist their node-key or they want to generate a new one while being in the active set, things go wrong because both the old addresses and the new ones will still be present in DHT, so because of the distributed nature of the DHT both will survive in the network untill the old ones expires which is 36 hours. Nodes in the network will randomly resolve the authorithy-id to the old address or the new one. More details in: https://github.com/paritytech/polkadot-sdk/issues/3673 This PR proposes we mitigate this problem, by: 1. Let the query for a DHT key retrieve more than one results(4), that is also bounded by the replication factor which is 20, currently we interrupt the querry on the first result. ~2. Modify the authority-discovery service to keep all the discovered addresses around for 24h since they last seen an address.~ ~3. Plumb through other subsystems where the assumption was that an authorithy-id will resolve only to one PeerId. Currently, the authorithy-discovery keeps just the last record it received from DHT and queries the DHT every 10 minutes. But they could always receive only the old address, only the new address or a flip-flop between them depending on what node wins the race to provide the record~ 2. Extend the `SignedAuthorityRecord` with a signed creation_time. 3. Modify authority discovery to keep track of nodes that sent us old record and once we are made aware of a new record update the nodes we know about with the new record. 4. Update gossip-support to try resolve authorities more often than every session. ~This would gives us a lot more chances for the nodes in the networks to also discover not only the old address of the node but also the new one and should improve the time it takes for a node to be properly connected in the network. The behaviour won't be deterministic because there is no guarantee the all nodes will see the new record at least once, since they could query only nodes that have the old one.~ ## TODO - [x] Add unittests for the new paths. - [x] Make sure the implementation is backwards compatible - [x] Evaluate if there are any bad consequence of letting the query continue rather than finish it at first record found. - [x] Bake in versi the new changes. --------- Signed-off-by: Alexandru Gheorghe Co-authored-by: Dmitry Markin Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> --- polkadot/node/network/bridge/src/network.rs | 15 + polkadot/node/network/bridge/src/rx/tests.rs | 8 + polkadot/node/network/bridge/src/tx/mod.rs | 16 + polkadot/node/network/bridge/src/tx/tests.rs | 8 + .../network/bridge/src/validator_discovery.rs | 47 ++ .../node/network/gossip-support/src/lib.rs | 127 ++++- .../node/network/gossip-support/src/tests.rs | 315 ++++++++++++ polkadot/node/subsystem-types/src/messages.rs | 10 + prdoc/pr_3786.prdoc | 22 + substrate/client/authority-discovery/build.rs | 6 +- .../client/authority-discovery/src/worker.rs | 278 ++++++++--- .../src/worker/schema/dht-v3.proto | 31 ++ .../src/worker/schema/tests.rs | 85 +++- .../authority-discovery/src/worker/tests.rs | 468 +++++++++++++++--- substrate/client/network/src/behaviour.rs | 19 +- substrate/client/network/src/discovery.rs | 67 ++- substrate/client/network/src/event.rs | 7 +- .../client/network/src/litep2p/discovery.rs | 25 +- substrate/client/network/src/litep2p/mod.rs | 69 ++- .../client/network/src/litep2p/service.rs | 31 ++ substrate/client/network/src/service.rs | 24 +- .../client/network/src/service/traits.rs | 10 + 22 files changed, 1511 insertions(+), 177 deletions(-) create mode 100644 prdoc/pr_3786.prdoc create mode 100644 substrate/client/authority-discovery/src/worker/schema/dht-v3.proto diff --git a/polkadot/node/network/bridge/src/network.rs b/polkadot/node/network/bridge/src/network.rs index b31359f48a56..1f438df2d148 100644 --- a/polkadot/node/network/bridge/src/network.rs +++ b/polkadot/node/network/bridge/src/network.rs @@ -204,6 +204,13 @@ pub trait Network: Clone + Send + 'static { multiaddresses: HashSet, ) -> Result<(), String>; + /// Ask the network to extend the reserved set with these nodes. + async fn add_peers_to_reserved_set( + &mut self, + protocol: ProtocolName, + multiaddresses: HashSet, + ) -> Result<(), String>; + /// Removes the peers for the protocol's peer set (both reserved and non-reserved). async fn remove_from_peers_set( &mut self, @@ -240,6 +247,14 @@ impl Network for Arc { ::set_reserved_peers(&**self, protocol, multiaddresses) } + async fn add_peers_to_reserved_set( + &mut self, + protocol: ProtocolName, + multiaddresses: HashSet, + ) -> Result<(), String> { + ::add_peers_to_reserved_set(&**self, protocol, multiaddresses) + } + async fn remove_from_peers_set( &mut self, protocol: ProtocolName, diff --git a/polkadot/node/network/bridge/src/rx/tests.rs b/polkadot/node/network/bridge/src/rx/tests.rs index 392ff7391a1c..601dca5cb8a3 100644 --- a/polkadot/node/network/bridge/src/rx/tests.rs +++ b/polkadot/node/network/bridge/src/rx/tests.rs @@ -124,6 +124,14 @@ impl Network for TestNetwork { Ok(()) } + async fn add_peers_to_reserved_set( + &mut self, + _protocol: ProtocolName, + _: HashSet, + ) -> Result<(), String> { + Ok(()) + } + async fn remove_from_peers_set( &mut self, _protocol: ProtocolName, diff --git a/polkadot/node/network/bridge/src/tx/mod.rs b/polkadot/node/network/bridge/src/tx/mod.rs index 7b6dea748572..6c353195d41a 100644 --- a/polkadot/node/network/bridge/src/tx/mod.rs +++ b/polkadot/node/network/bridge/src/tx/mod.rs @@ -370,6 +370,22 @@ where .await; return (network_service, authority_discovery_service) }, + + NetworkBridgeTxMessage::AddToResolvedValidators { validator_addrs, peer_set } => { + gum::trace!( + target: LOG_TARGET, + action = "AddToResolvedValidators", + peer_set = ?peer_set, + ?validator_addrs, + "Received a resolved validator connection request", + ); + + let all_addrs = validator_addrs.into_iter().flatten().collect(); + let network_service = validator_discovery + .on_add_to_resolved_request(all_addrs, peer_set, network_service) + .await; + return (network_service, authority_discovery_service) + }, } (network_service, authority_discovery_service) } diff --git a/polkadot/node/network/bridge/src/tx/tests.rs b/polkadot/node/network/bridge/src/tx/tests.rs index 9265358196db..30b2c3421372 100644 --- a/polkadot/node/network/bridge/src/tx/tests.rs +++ b/polkadot/node/network/bridge/src/tx/tests.rs @@ -148,6 +148,14 @@ impl Network for TestNetwork { Ok(()) } + async fn add_peers_to_reserved_set( + &mut self, + _protocol: ProtocolName, + _: HashSet, + ) -> Result<(), String> { + Ok(()) + } + async fn remove_from_peers_set( &mut self, _protocol: ProtocolName, diff --git a/polkadot/node/network/bridge/src/validator_discovery.rs b/polkadot/node/network/bridge/src/validator_discovery.rs index f0ef038d5eb4..9accd56d86ae 100644 --- a/polkadot/node/network/bridge/src/validator_discovery.rs +++ b/polkadot/node/network/bridge/src/validator_discovery.rs @@ -92,6 +92,44 @@ impl Service { network_service } + /// Connect to already resolved addresses. + pub async fn on_add_to_resolved_request( + &mut self, + newly_requested: HashSet, + peer_set: PeerSet, + mut network_service: N, + ) -> N { + let state = &mut self.state[peer_set]; + let new_peer_ids: HashSet = extract_peer_ids(newly_requested.iter().cloned()); + let num_peers = new_peer_ids.len(); + + state.previously_requested.extend(new_peer_ids); + + gum::debug!( + target: LOG_TARGET, + ?peer_set, + ?num_peers, + "New add to resolved validators request", + ); + + // ask the network to connect to these nodes and not disconnect + // from them until they are removed from the set. + // + // for peer-set management, the main protocol name should be used regardless of + // the negotiated version. + if let Err(e) = network_service + .add_peers_to_reserved_set( + self.peerset_protocol_names.get_main_name(peer_set), + newly_requested, + ) + .await + { + gum::warn!(target: LOG_TARGET, err = ?e, "AuthorityDiscoveryService returned an invalid multiaddress"); + } + + network_service + } + /// On a new connection request, a peer set update will be issued. /// It will ask the network to connect to the validators and not disconnect /// from them at least until the next request is issued for the same peer set. @@ -222,6 +260,15 @@ mod tests { Ok(()) } + async fn add_peers_to_reserved_set( + &mut self, + _protocol: ProtocolName, + multiaddresses: HashSet, + ) -> Result<(), String> { + self.peers_set.extend(extract_peer_ids(multiaddresses.into_iter())); + Ok(()) + } + async fn remove_from_peers_set( &mut self, _protocol: ProtocolName, diff --git a/polkadot/node/network/gossip-support/src/lib.rs b/polkadot/node/network/gossip-support/src/lib.rs index 4dfdd1f7208f..cd327c11e408 100644 --- a/polkadot/node/network/gossip-support/src/lib.rs +++ b/polkadot/node/network/gossip-support/src/lib.rs @@ -69,6 +69,16 @@ const BACKOFF_DURATION: Duration = Duration::from_secs(5); #[cfg(test)] const BACKOFF_DURATION: Duration = Duration::from_millis(500); +// The authorithy_discovery queries runs every ten minutes, +// so it make sense to run a bit more often than that to +// detect changes as often as we can, but not too often since +// it won't help. +#[cfg(not(test))] +const TRY_RERESOLVE_AUTHORITIES: Duration = Duration::from_secs(5 * 60); + +#[cfg(test)] +const TRY_RERESOLVE_AUTHORITIES: Duration = Duration::from_secs(2); + /// Duration after which we consider low connectivity a problem. /// /// Especially at startup low connectivity is expected (authority discovery cache needs to be @@ -91,6 +101,14 @@ pub struct GossipSupport { // `None` otherwise. last_failure: Option, + // Validators can restart during a session, so if they change + // their PeerID, we will connect to them in the best case after + // a session, so we need to try more often to resolved peers and + // reconnect to them. The authorithy_discovery queries runs every ten + // minutes, so we can't detect changes in the address more often + // that that. + last_connection_request: Option, + /// First time we did not reach our connectivity threshold. /// /// This is the time of the first failed attempt to connect to >2/3 of all validators in a @@ -131,6 +149,7 @@ where keystore, last_session_index: None, last_failure: None, + last_connection_request: None, failure_start: None, resolved_authorities: HashMap::new(), connected_authorities: HashMap::new(), @@ -196,15 +215,22 @@ where for leaf in leaves { let current_index = util::request_session_index_for_child(leaf, sender).await.await??; let since_failure = self.last_failure.map(|i| i.elapsed()).unwrap_or_default(); + let since_last_reconnect = + self.last_connection_request.map(|i| i.elapsed()).unwrap_or_default(); + let force_request = since_failure >= BACKOFF_DURATION; + let re_resolve_authorities = since_last_reconnect >= TRY_RERESOLVE_AUTHORITIES; let leaf_session = Some((current_index, leaf)); let maybe_new_session = match self.last_session_index { Some(i) if current_index <= i => None, _ => leaf_session, }; - let maybe_issue_connection = - if force_request { leaf_session } else { maybe_new_session }; + let maybe_issue_connection = if force_request || re_resolve_authorities { + leaf_session + } else { + maybe_new_session + }; if let Some((session_index, relay_parent)) = maybe_issue_connection { let session_info = @@ -248,7 +274,7 @@ where // connections to a much broader set of validators. { let mut connections = authorities_past_present_future(sender, leaf).await?; - + self.last_connection_request = Some(Instant::now()); // Remove all of our locally controlled validator indices so we don't connect to // ourself. let connections = @@ -259,7 +285,12 @@ where // to clean up all connections. Vec::new() }; - self.issue_connection_request(sender, connections).await; + + if force_request || is_new_session { + self.issue_connection_request(sender, connections).await; + } else if re_resolve_authorities { + self.issue_connection_request_to_changed(sender, connections).await; + } } if is_new_session { @@ -324,17 +355,14 @@ where authority_check_result } - async fn issue_connection_request( + async fn resolve_authorities( &mut self, - sender: &mut Sender, authorities: Vec, - ) where - Sender: overseer::GossipSupportSenderTrait, - { - let num = authorities.len(); + ) -> (Vec>, HashMap>, usize) { let mut validator_addrs = Vec::with_capacity(authorities.len()); - let mut failures = 0; let mut resolved = HashMap::with_capacity(authorities.len()); + let mut failures = 0; + for authority in authorities { if let Some(addrs) = self.authority_discovery.get_addresses_by_authority_id(authority.clone()).await @@ -350,6 +378,67 @@ where ); } } + (validator_addrs, resolved, failures) + } + + async fn issue_connection_request_to_changed( + &mut self, + sender: &mut Sender, + authorities: Vec, + ) where + Sender: overseer::GossipSupportSenderTrait, + { + let (_, resolved, _) = self.resolve_authorities(authorities).await; + + let mut changed = Vec::new(); + + for (authority, new_addresses) in &resolved { + let new_peer_ids = new_addresses + .iter() + .flat_map(|addr| parse_addr(addr.clone()).ok().map(|(p, _)| p)) + .collect::>(); + match self.resolved_authorities.get(authority) { + Some(old_addresses) => { + let old_peer_ids = old_addresses + .iter() + .flat_map(|addr| parse_addr(addr.clone()).ok().map(|(p, _)| p)) + .collect::>(); + if !old_peer_ids.is_superset(&new_peer_ids) { + changed.push(new_addresses.clone()); + } + }, + None => changed.push(new_addresses.clone()), + } + } + gum::debug!( + target: LOG_TARGET, + num_changed = ?changed.len(), + ?changed, + "Issuing a connection request to changed validators" + ); + if !changed.is_empty() { + self.resolved_authorities = resolved; + + sender + .send_message(NetworkBridgeTxMessage::AddToResolvedValidators { + validator_addrs: changed, + peer_set: PeerSet::Validation, + }) + .await; + } + } + + async fn issue_connection_request( + &mut self, + sender: &mut Sender, + authorities: Vec, + ) where + Sender: overseer::GossipSupportSenderTrait, + { + let num = authorities.len(); + + let (validator_addrs, resolved, failures) = self.resolve_authorities(authorities).await; + self.resolved_authorities = resolved; gum::debug!(target: LOG_TARGET, %num, "Issuing a connection request"); @@ -399,16 +488,24 @@ where { let mut authority_ids: HashMap> = HashMap::new(); for authority in authorities { - let peer_id = self + let peer_ids = self .authority_discovery .get_addresses_by_authority_id(authority.clone()) .await .into_iter() .flat_map(|list| list.into_iter()) - .find_map(|addr| parse_addr(addr).ok().map(|(p, _)| p)); + .flat_map(|addr| parse_addr(addr).ok().map(|(p, _)| p)) + .collect::>(); + + gum::trace!( + target: LOG_TARGET, + ?peer_ids, + ?authority, + "Resolved to peer ids" + ); - if let Some(p) = peer_id { - authority_ids.entry(p).or_default().insert(authority); + for p in peer_ids { + authority_ids.entry(p).or_default().insert(authority.clone()); } } diff --git a/polkadot/node/network/gossip-support/src/tests.rs b/polkadot/node/network/gossip-support/src/tests.rs index 42197d00e6f3..09622254f523 100644 --- a/polkadot/node/network/gossip-support/src/tests.rs +++ b/polkadot/node/network/gossip-support/src/tests.rs @@ -119,6 +119,14 @@ impl MockAuthorityDiscovery { } } + fn change_address_for_authority(&self, authority_id: AuthorityDiscoveryId) -> PeerId { + let new_peer_id = PeerId::random(); + let addr = Multiaddr::empty().with(Protocol::P2p(new_peer_id.into())); + self.addrs.lock().insert(authority_id.clone(), HashSet::from([addr])); + self.authorities.lock().insert(new_peer_id, HashSet::from([authority_id])); + new_peer_id + } + fn authorities(&self) -> HashMap> { self.authorities.lock().clone() } @@ -809,6 +817,313 @@ fn issues_update_authorities_after_session() { ); } +// Test we connect to authorities that changed their address `TRY_RERESOLVE_AUTHORITIES` rate +// and that is is no-op if no authority changed. +#[test] +fn test_quickly_connect_to_authorities_that_changed_address() { + let hash = Hash::repeat_byte(0xAA); + + let authorities = PAST_PRESENT_FUTURE_AUTHORITIES.clone(); + let authority_that_changes_address = authorities.get(5).unwrap().clone(); + + let mut authority_discovery_mock = MockAuthorityDiscovery::new(authorities); + + test_harness( + make_subsystem_with_authority_discovery(authority_discovery_mock.clone()), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // 1. Initialize with the first leaf in the session. + overseer_signal_active_leaves(overseer, hash).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionIndexForChild(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(1)).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionInfo(s, tx), + )) => { + assert_eq!(relay_parent, hash); + assert_eq!(s, 1); + let mut session_info = make_session_info(); + session_info.discovery_keys = PAST_PRESENT_FUTURE_AUTHORITIES.clone(); + tx.send(Ok(Some(session_info))).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::Authorities(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(PAST_PRESENT_FUTURE_AUTHORITIES.clone())).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ConnectToResolvedValidators { + validator_addrs, + peer_set, + }) => { + let all_without_ferdie: Vec<_> = PAST_PRESENT_FUTURE_AUTHORITIES + .iter() + .cloned() + .filter(|p| p != &Sr25519Keyring::Ferdie.public().into()) + .collect(); + + let addrs = get_multiaddrs(all_without_ferdie, authority_discovery_mock.clone()).await; + + assert_eq!(validator_addrs, addrs); + assert_eq!(peer_set, PeerSet::Validation); + } + ); + + // Ensure neighbors are unaffected + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _, + RuntimeApiRequest::CurrentBabeEpoch(tx), + )) => { + let _ = tx.send(Ok(BabeEpoch { + epoch_index: 2 as _, + start_slot: 0.into(), + duration: 200, + authorities: vec![(Sr25519Keyring::Alice.public().into(), 1)], + randomness: [0u8; 32], + config: BabeEpochConfiguration { + c: (1, 4), + allowed_slots: AllowedSlots::PrimarySlots, + }, + })).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeRx(NetworkBridgeRxMessage::NewGossipTopology { + session: _, + local_index: _, + canonical_shuffling: _, + shuffled_indices: _, + }) => { + + } + ); + + // 2. Connect all authorities that are known so far. + let known_authorities = authority_discovery_mock.authorities(); + for (peer_id, _id) in known_authorities.iter() { + let msg = + GossipSupportMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerConnected( + *peer_id, + ObservedRole::Authority, + ValidationVersion::V3.into(), + None, + )); + overseer.send(FromOrchestra::Communication { msg }).await + } + + // 3. Send a new leaf after TRY_RERESOLVE_AUTHORITIES, we should notice + // UpdateAuthorithies is emitted for all ConnectedPeers. + Delay::new(TRY_RERESOLVE_AUTHORITIES).await; + let hash = Hash::repeat_byte(0xBB); + overseer_signal_active_leaves(overseer, hash).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionIndexForChild(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(1)).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionInfo(s, tx), + )) => { + assert_eq!(relay_parent, hash); + assert_eq!(s, 1); + let mut session_info = make_session_info(); + session_info.discovery_keys = PAST_PRESENT_FUTURE_AUTHORITIES.clone(); + tx.send(Ok(Some(session_info))).unwrap(); + + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::Authorities(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(PAST_PRESENT_FUTURE_AUTHORITIES.clone())).unwrap(); + } + ); + + for _ in 0..known_authorities.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeRx(NetworkBridgeRxMessage::UpdatedAuthorityIds { + peer_id, + authority_ids, + }) => { + assert_eq!(authority_discovery_mock.get_authority_ids_by_peer_id(peer_id).await.unwrap_or_default(), authority_ids); + } + ); + } + + // 4. At next re-resolve no-authorithy changes their address, so it should be no-op. + Delay::new(TRY_RERESOLVE_AUTHORITIES).await; + let hash = Hash::repeat_byte(0xCC); + overseer_signal_active_leaves(overseer, hash).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionIndexForChild(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(1)).unwrap(); + } + ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionInfo(s, tx), + )) => { + assert_eq!(relay_parent, hash); + assert_eq!(s, 1); + let mut session_info = make_session_info(); + session_info.discovery_keys = PAST_PRESENT_FUTURE_AUTHORITIES.clone(); + tx.send(Ok(Some(session_info))).unwrap(); + + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::Authorities(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(PAST_PRESENT_FUTURE_AUTHORITIES.clone())).unwrap(); + } + ); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none()); + + // Change address for one authorithy and check we try to connect to it and + // that we emit UpdateAuthorityID for the old PeerId and the new one. + Delay::new(TRY_RERESOLVE_AUTHORITIES).await; + let changed_peerid = authority_discovery_mock + .change_address_for_authority(authority_that_changes_address.clone()); + let hash = Hash::repeat_byte(0xDD); + let msg = GossipSupportMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerConnected( + changed_peerid, + ObservedRole::Authority, + ValidationVersion::V3.into(), + None, + )); + overseer.send(FromOrchestra::Communication { msg }).await; + + overseer_signal_active_leaves(overseer, hash).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionIndexForChild(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(1)).unwrap(); + } + ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionInfo(s, tx), + )) => { + assert_eq!(relay_parent, hash); + assert_eq!(s, 1); + let mut session_info = make_session_info(); + session_info.discovery_keys = PAST_PRESENT_FUTURE_AUTHORITIES.clone(); + tx.send(Ok(Some(session_info))).unwrap(); + + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::Authorities(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(PAST_PRESENT_FUTURE_AUTHORITIES.clone())).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::AddToResolvedValidators { + validator_addrs, + peer_set, + }) => { + let expected = get_address_map(vec![authority_that_changes_address.clone()], authority_discovery_mock.clone()).await; + let expected: HashSet = expected.into_values().flat_map(|v| v.into_iter()).collect(); + assert_eq!(validator_addrs.into_iter().flat_map(|v| v.into_iter()).collect::>(), expected); + assert_eq!(peer_set, PeerSet::Validation); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeRx(NetworkBridgeRxMessage::UpdatedAuthorityIds { + peer_id, + authority_ids, + }) => { + assert_eq!(authority_discovery_mock.get_authority_ids_by_peer_id(peer_id).await.unwrap(), HashSet::from([authority_that_changes_address.clone()])); + assert!(authority_ids.is_empty()); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeRx(NetworkBridgeRxMessage::UpdatedAuthorityIds { + peer_id, + authority_ids, + }) => { + assert_eq!(authority_ids, HashSet::from([authority_that_changes_address])); + assert_eq!(changed_peerid, peer_id); + } + ); + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none()); + + virtual_overseer + }, + ); +} + #[test] fn disconnect_when_not_in_past_present_future() { sp_tracing::try_init_simple(); diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index ee937bca05bf..4d27ac9b70e3 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -444,6 +444,16 @@ pub enum NetworkBridgeTxMessage { /// The peer set we want the connection on. peer_set: PeerSet, }, + + /// Extends the known validators set with new peers we already know the `Multiaddrs`, this is + /// usually needed for validators that change their address mid-session. It is usually called + /// after a ConnectToResolvedValidators at the beginning of the session. + AddToResolvedValidators { + /// Each entry corresponds to the addresses of an already resolved validator. + validator_addrs: Vec>, + /// The peer set we want the connection on. + peer_set: PeerSet, + }, } /// Availability Distribution Message. diff --git a/prdoc/pr_3786.prdoc b/prdoc/pr_3786.prdoc new file mode 100644 index 000000000000..0bb9e6c23f75 --- /dev/null +++ b/prdoc/pr_3786.prdoc @@ -0,0 +1,22 @@ +title: Make changing of peer-id while active a bit more robust + +doc: + - audience: Node Dev + description: | + Implemetation of https://github.com/polkadot-fellows/RFCs/pull/91, to use `creation_time` field to determine + the newest DHT record and to update nodes known to have the old record. + + Gossip-support is modified to try to re-resolve new address authorithies every 5 minutes instead of each session, + so that we pick autorithies that changed their address faster and try to connect to them. + +crates: +- name: sc-authority-discovery + bump: major +- name: polkadot-gossip-support + bump: major +- name: polkadot-network-bridge + bump: major +- name: polkadot-node-subsystem-types + bump: major +- name: sc-network + bump: minor \ No newline at end of file diff --git a/substrate/client/authority-discovery/build.rs b/substrate/client/authority-discovery/build.rs index 83076ac8c893..cdabc1a74427 100644 --- a/substrate/client/authority-discovery/build.rs +++ b/substrate/client/authority-discovery/build.rs @@ -18,7 +18,11 @@ fn main() { prost_build::compile_protos( - &["src/worker/schema/dht-v1.proto", "src/worker/schema/dht-v2.proto"], + &[ + "src/worker/schema/dht-v1.proto", + "src/worker/schema/dht-v2.proto", + "src/worker/schema/dht-v3.proto", + ], &["src/worker/schema"], ) .unwrap(); diff --git a/substrate/client/authority-discovery/src/worker.rs b/substrate/client/authority-discovery/src/worker.rs index 1f1cce160786..42994bbc7ea8 100644 --- a/substrate/client/authority-discovery/src/worker.rs +++ b/substrate/client/authority-discovery/src/worker.rs @@ -26,7 +26,7 @@ use std::{ collections::{HashMap, HashSet}, marker::PhantomData, sync::Arc, - time::Duration, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, }; use futures::{channel::mpsc, future, stream::Fuse, FutureExt, Stream, StreamExt}; @@ -34,16 +34,17 @@ use futures::{channel::mpsc, future, stream::Fuse, FutureExt, Stream, StreamExt} use addr_cache::AddrCache; use codec::{Decode, Encode}; use ip_network::IpNetwork; +use libp2p::kad::{PeerRecord, Record}; use linked_hash_set::LinkedHashSet; -use log::{debug, error, log_enabled}; +use log::{debug, error}; use prometheus_endpoint::{register, Counter, CounterVec, Gauge, Opts, U64}; use prost::Message; use rand::{seq::SliceRandom, thread_rng}; use sc_network::{ - event::DhtEvent, multiaddr, KademliaKey, Multiaddr, NetworkDHTProvider, NetworkSigner, - NetworkStateInfo, + config::DEFAULT_KADEMLIA_REPLICATION_FACTOR, event::DhtEvent, multiaddr, KademliaKey, + Multiaddr, NetworkDHTProvider, NetworkSigner, NetworkStateInfo, }; use sc_network_types::{multihash::Code, PeerId}; use schema::PeerSignature; @@ -62,7 +63,7 @@ mod schema { #[cfg(test)] mod tests; - include!(concat!(env!("OUT_DIR"), "/authority_discovery_v2.rs")); + include!(concat!(env!("OUT_DIR"), "/authority_discovery_v3.rs")); } #[cfg(test)] pub mod tests; @@ -159,6 +160,16 @@ pub struct Worker { /// Set of in-flight lookups. in_flight_lookups: HashMap, + /// Set of lookups we can still receive records. + /// These are the entries in the `in_flight_lookups` for which + /// we got at least one successfull result. + known_lookups: HashMap, + + /// Last known record by key, here we always keep the record with + /// the highest creation time and we don't accept records older than + /// that. + last_known_records: HashMap, + addr_cache: addr_cache::AddrCache, metrics: Option, @@ -168,6 +179,17 @@ pub struct Worker { phantom: PhantomData, } +#[derive(Debug, Clone)] +struct RecordInfo { + /// Time since UNIX_EPOCH in nanoseconds. + creation_time: u128, + /// Peers that we know have this record, bounded to no more than + /// DEFAULT_KADEMLIA_REPLICATION_FACTOR(20). + peers_with_record: HashSet, + /// The record itself. + record: Record, +} + /// Wrapper for [`AuthorityDiscoveryApi`](sp_authority_discovery::AuthorityDiscoveryApi). Can be /// be implemented by any struct without dependency on the runtime. #[async_trait::async_trait] @@ -283,10 +305,12 @@ where query_interval, pending_lookups: Vec::new(), in_flight_lookups: HashMap::new(), + known_lookups: HashMap::new(), addr_cache, role, metrics, phantom: PhantomData, + last_known_records: HashMap::new(), } } @@ -444,7 +468,7 @@ where .set(addresses.len().try_into().unwrap_or(std::u64::MAX)); } - let serialized_record = serialize_authority_record(addresses)?; + let serialized_record = serialize_authority_record(addresses, Some(build_creation_time()))?; let peer_signature = sign_record_with_peer_id(&serialized_record, &self.network)?; let keys_vec = keys.iter().cloned().collect::>(); @@ -495,12 +519,17 @@ where self.authorities_queried_at = Some(best_hash); self.addr_cache.retain_ids(&authorities); + let now = Instant::now(); + self.last_known_records.retain(|k, value| { + self.known_authorities.contains_key(k) && !value.record.is_expired(now) + }); authorities.shuffle(&mut thread_rng()); self.pending_lookups = authorities; // Ignore all still in-flight lookups. Those that are still in-flight are likely stalled as // query interval ticks are far enough apart for all lookups to succeed. self.in_flight_lookups.clear(); + self.known_lookups.clear(); if let Some(metrics) = &self.metrics { metrics @@ -538,16 +567,12 @@ where metrics.dht_event_received.with_label_values(&["value_found"]).inc(); } - if log_enabled!(log::Level::Debug) { - let hashes: Vec<_> = v.iter().map(|(hash, _value)| hash.clone()).collect(); - debug!(target: LOG_TARGET, "Value for hash '{:?}' found on Dht.", hashes); - } + debug!(target: LOG_TARGET, "Value for hash '{:?}' found on Dht.", v.record.key); if let Err(e) = self.handle_dht_value_found_event(v) { if let Some(metrics) = &self.metrics { metrics.handle_value_found_event_failure.inc(); } - debug!(target: LOG_TARGET, "Failed to handle Dht value found event: {}", e); } }, @@ -651,6 +676,31 @@ where publisher, authority_id, )?; + + let records_creation_time: u128 = + schema::AuthorityRecord::decode(signed_record.record.as_slice()) + .map_err(Error::DecodingProto)? + .creation_time + .map(|creation_time| { + u128::decode(&mut &creation_time.timestamp[..]).unwrap_or_default() + }) + .unwrap_or_default(); // 0 is a sane default for records that do not have creation time present. + + let current_record_info = self.last_known_records.get(&record_key); + // If record creation time is older than the current record creation time, + // we don't store it since we want to give higher priority to newer records. + if let Some(current_record_info) = current_record_info { + if records_creation_time < current_record_info.creation_time { + debug!( + target: LOG_TARGET, + "Skip storing because record creation time {:?} is older than the current known record {:?}", + records_creation_time, + current_record_info.creation_time + ); + return Ok(()); + } + } + self.network.store_record(record_key, record_value, Some(publisher), expires); Ok(()) } @@ -701,67 +751,88 @@ where Ok(()) } - fn handle_dht_value_found_event(&mut self, values: Vec<(KademliaKey, Vec)>) -> Result<()> { + fn handle_dht_value_found_event(&mut self, peer_record: PeerRecord) -> Result<()> { // Ensure `values` is not empty and all its keys equal. - let remote_key = single(values.iter().map(|(key, _)| key.clone())) - .map_err(|_| Error::ReceivingDhtValueFoundEventWithDifferentKeys)? - .ok_or(Error::ReceivingDhtValueFoundEventWithNoRecords)?; - - let authority_id: AuthorityId = self - .in_flight_lookups - .remove(&remote_key) - .ok_or(Error::ReceivingUnexpectedRecord)?; + let remote_key = peer_record.record.key.clone(); + + let authority_id: AuthorityId = + if let Some(authority_id) = self.in_flight_lookups.remove(&remote_key) { + self.known_lookups.insert(remote_key.clone(), authority_id.clone()); + authority_id + } else if let Some(authority_id) = self.known_lookups.get(&remote_key) { + authority_id.clone() + } else { + return Err(Error::ReceivingUnexpectedRecord); + }; let local_peer_id = self.network.local_peer_id(); - let remote_addresses: Vec = values - .into_iter() - .map(|(_k, v)| { - let schema::SignedAuthorityRecord { record, peer_signature, .. } = - Self::check_record_signed_with_authority_id(&v, &authority_id)?; - - let addresses: Vec = schema::AuthorityRecord::decode(record.as_slice()) - .map(|a| a.addresses) - .map_err(Error::DecodingProto)? - .into_iter() - .map(|a| a.try_into()) - .collect::>() - .map_err(Error::ParsingMultiaddress)?; - - let get_peer_id = |a: &Multiaddr| match a.iter().last() { - Some(multiaddr::Protocol::P2p(key)) => PeerId::from_multihash(key).ok(), - _ => None, - }; - - // Ignore [`Multiaddr`]s without [`PeerId`] or with own addresses. - let addresses: Vec = addresses - .into_iter() - .filter(|a| get_peer_id(a).filter(|p| *p != local_peer_id).is_some()) - .collect(); - - let remote_peer_id = single(addresses.iter().map(get_peer_id)) - .map_err(|_| Error::ReceivingDhtValueFoundEventWithDifferentPeerIds)? // different peer_id in records - .flatten() - .ok_or(Error::ReceivingDhtValueFoundEventWithNoPeerIds)?; // no records with peer_id in them - - // At this point we know all the valid multiaddresses from the record, know that - // each of them belong to the same PeerId, we just need to check if the record is - // properly signed by the owner of the PeerId - self.check_record_signed_with_network_key( - &record, - peer_signature, - remote_peer_id, - &authority_id, - )?; - Ok(addresses) + let schema::SignedAuthorityRecord { record, peer_signature, .. } = + Self::check_record_signed_with_authority_id( + peer_record.record.value.as_slice(), + &authority_id, + )?; + + let authority_record = + schema::AuthorityRecord::decode(record.as_slice()).map_err(Error::DecodingProto)?; + + let records_creation_time: u128 = authority_record + .creation_time + .as_ref() + .map(|creation_time| { + u128::decode(&mut &creation_time.timestamp[..]).unwrap_or_default() }) - .collect::>>>()? + .unwrap_or_default(); // 0 is a sane default for records that do not have creation time present. + + let addresses: Vec = authority_record + .addresses .into_iter() - .flatten() - .take(MAX_ADDRESSES_PER_AUTHORITY) + .map(|a| a.try_into()) + .collect::>() + .map_err(Error::ParsingMultiaddress)?; + + let get_peer_id = |a: &Multiaddr| match a.iter().last() { + Some(multiaddr::Protocol::P2p(key)) => PeerId::from_multihash(key).ok(), + _ => None, + }; + + // Ignore [`Multiaddr`]s without [`PeerId`] or with own addresses. + let addresses: Vec = addresses + .into_iter() + .filter(|a| get_peer_id(&a).filter(|p| *p != local_peer_id).is_some()) .collect(); - if !remote_addresses.is_empty() { + let remote_peer_id = single(addresses.iter().map(|a| get_peer_id(&a))) + .map_err(|_| Error::ReceivingDhtValueFoundEventWithDifferentPeerIds)? // different peer_id in records + .flatten() + .ok_or(Error::ReceivingDhtValueFoundEventWithNoPeerIds)?; // no records with peer_id in them + + // At this point we know all the valid multiaddresses from the record, know that + // each of them belong to the same PeerId, we just need to check if the record is + // properly signed by the owner of the PeerId + self.check_record_signed_with_network_key( + &record, + peer_signature, + remote_peer_id, + &authority_id, + )?; + + let remote_addresses: Vec = + addresses.into_iter().take(MAX_ADDRESSES_PER_AUTHORITY).collect(); + + let answering_peer_id = peer_record.peer.map(|peer| peer.into()); + + let addr_cache_needs_update = self.handle_new_record( + &authority_id, + remote_key.clone(), + RecordInfo { + creation_time: records_creation_time, + peers_with_record: answering_peer_id.into_iter().collect(), + record: peer_record.record, + }, + ); + + if !remote_addresses.is_empty() && addr_cache_needs_update { self.addr_cache.insert(authority_id, remote_addresses); if let Some(metrics) = &self.metrics { metrics @@ -772,6 +843,68 @@ where Ok(()) } + // Handles receiving a new DHT record for the authorithy. + // Returns true if the record was new, false if the record was older than the current one. + fn handle_new_record( + &mut self, + authority_id: &AuthorityId, + kademlia_key: KademliaKey, + new_record: RecordInfo, + ) -> bool { + let current_record_info = self + .last_known_records + .entry(kademlia_key.clone()) + .or_insert_with(|| new_record.clone()); + + if new_record.creation_time > current_record_info.creation_time { + let peers_that_need_updating = current_record_info.peers_with_record.clone(); + self.network.put_record_to( + new_record.record.clone(), + peers_that_need_updating.clone(), + // If this is empty it means we received the answer from our node local + // storage, so we need to update that as well. + current_record_info.peers_with_record.is_empty(), + ); + debug!( + target: LOG_TARGET, + "Found a newer record for {:?} new record creation time {:?} old record creation time {:?}", + authority_id, new_record.creation_time, current_record_info.creation_time + ); + self.last_known_records.insert(kademlia_key, new_record); + return true + } + + if new_record.creation_time == current_record_info.creation_time { + // Same record just update in case this is a record from old nodes that don't have + // timestamp. + debug!( + target: LOG_TARGET, + "Found same record for {:?} record creation time {:?}", + authority_id, new_record.creation_time + ); + if current_record_info.peers_with_record.len() + new_record.peers_with_record.len() <= + DEFAULT_KADEMLIA_REPLICATION_FACTOR + { + current_record_info.peers_with_record.extend(new_record.peers_with_record); + } + return true + } + + debug!( + target: LOG_TARGET, + "Found old record for {:?} received record creation time {:?} current record creation time {:?}", + authority_id, new_record.creation_time, current_record_info.creation_time, + ); + self.network.put_record_to( + current_record_info.record.clone(), + new_record.peers_with_record.clone(), + // If this is empty it means we received the answer from our node local + // storage, so we need to update that as well. + new_record.peers_with_record.is_empty(), + ); + return false + } + /// Retrieve our public keys within the current and next authority set. // A node might have multiple authority discovery keys within its keystore, e.g. an old one and // one for the upcoming session. In addition it could be participating in the current and (/ or) @@ -838,9 +971,21 @@ fn serialize_addresses(addresses: impl Iterator) -> Vec>) -> Result> { +fn build_creation_time() -> schema::TimestampInfo { + let creation_time = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|time| time.as_nanos()) + .unwrap_or_default(); + schema::TimestampInfo { timestamp: creation_time.encode() } +} + +fn serialize_authority_record( + addresses: Vec>, + creation_time: Option, +) -> Result> { let mut serialized_record = vec![]; - schema::AuthorityRecord { addresses } + + schema::AuthorityRecord { addresses, creation_time } .encode(&mut serialized_record) .map_err(Error::EncodingProto)?; Ok(serialized_record) @@ -876,7 +1021,6 @@ fn sign_record_with_authority_ids( // Scale encode let auth_signature = auth_signature.encode(); - let signed_record = schema::SignedAuthorityRecord { record: serialized_record.clone(), auth_signature, diff --git a/substrate/client/authority-discovery/src/worker/schema/dht-v3.proto b/substrate/client/authority-discovery/src/worker/schema/dht-v3.proto new file mode 100644 index 000000000000..547237573af2 --- /dev/null +++ b/substrate/client/authority-discovery/src/worker/schema/dht-v3.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package authority_discovery_v3; + +// First we need to serialize the addresses in order to be able to sign them. +message AuthorityRecord { + // Possibly multiple `MultiAddress`es through which the node can be reached. + repeated bytes addresses = 1; + // Information about the creation time of the record + TimestampInfo creation_time = 2; +} + +message PeerSignature { + bytes signature = 1; + bytes public_key = 2; +} + +// Information regarding the creation data of the record +message TimestampInfo { + // Time since UNIX_EPOCH in nanoseconds, scale encoded + bytes timestamp = 1; +} + +// Then we need to serialize the authority record and signature to send them over the wire. +message SignedAuthorityRecord { + bytes record = 1; + bytes auth_signature = 2; + // Even if there are multiple `record.addresses`, all of them have the same peer id. + // Old versions are missing this field. It is optional in order to provide compatibility both ways. + PeerSignature peer_signature = 3; +} diff --git a/substrate/client/authority-discovery/src/worker/schema/tests.rs b/substrate/client/authority-discovery/src/worker/schema/tests.rs index ef06ed7d336b..557fa9641f97 100644 --- a/substrate/client/authority-discovery/src/worker/schema/tests.rs +++ b/substrate/client/authority-discovery/src/worker/schema/tests.rs @@ -20,7 +20,12 @@ mod schema_v1 { include!(concat!(env!("OUT_DIR"), "/authority_discovery_v1.rs")); } +mod schema_v2 { + include!(concat!(env!("OUT_DIR"), "/authority_discovery_v2.rs")); +} + use super::*; +use codec::Encode; use libp2p::identity::Keypair; use prost::Message; use sc_network::{Multiaddr, PeerId}; @@ -65,7 +70,7 @@ fn v1_decodes_v2() { let vec_auth_signature = b"Totally valid signature, I promise!".to_vec(); let vec_peer_signature = b"Surprisingly hard to crack crypto".to_vec(); - let record_v2 = AuthorityRecord { addresses: vec_addresses.clone() }; + let record_v2 = schema_v2::AuthorityRecord { addresses: vec_addresses.clone() }; let mut vec_record_v2 = vec![]; record_v2.encode(&mut vec_record_v2).unwrap(); let vec_peer_public = peer_public.encode_protobuf(); @@ -85,6 +90,82 @@ fn v1_decodes_v2() { assert_eq!(&signed_addresses_v1_decoded.addresses, &vec_record_v2); assert_eq!(&signed_addresses_v1_decoded.signature, &vec_auth_signature); - let addresses_v2_decoded = AuthorityRecord::decode(vec_record_v2.as_slice()).unwrap(); + let addresses_v2_decoded = + schema_v2::AuthorityRecord::decode(vec_record_v2.as_slice()).unwrap(); + assert_eq!(&addresses_v2_decoded.addresses, &vec_addresses); +} + +#[test] +fn v1_decodes_v3() { + let peer_secret = Keypair::generate_ed25519(); + let peer_public = peer_secret.public(); + let peer_id = peer_public.to_peer_id(); + let multiaddress: Multiaddr = + format!("/ip4/127.0.0.1/tcp/3003/p2p/{}", peer_id).parse().unwrap(); + let vec_addresses = vec![multiaddress.to_vec()]; + let vec_auth_signature = b"Totally valid signature, I promise!".to_vec(); + let vec_peer_signature = b"Surprisingly hard to crack crypto".to_vec(); + + let record_v3 = AuthorityRecord { + addresses: vec_addresses.clone(), + creation_time: Some(TimestampInfo { timestamp: Encode::encode(&55) }), + }; + let mut vec_record_v3 = vec![]; + record_v3.encode(&mut vec_record_v3).unwrap(); + let vec_peer_public = peer_public.encode_protobuf(); + let peer_signature_v3 = + PeerSignature { public_key: vec_peer_public, signature: vec_peer_signature }; + let signed_record_v3 = SignedAuthorityRecord { + record: vec_record_v3.clone(), + auth_signature: vec_auth_signature.clone(), + peer_signature: Some(peer_signature_v3.clone()), + }; + let mut vec_signed_record_v3 = vec![]; + signed_record_v3.encode(&mut vec_signed_record_v3).unwrap(); + + let signed_addresses_v1_decoded = + schema_v1::SignedAuthorityAddresses::decode(vec_signed_record_v3.as_slice()).unwrap(); + + assert_eq!(&signed_addresses_v1_decoded.addresses, &vec_record_v3); + assert_eq!(&signed_addresses_v1_decoded.signature, &vec_auth_signature); + + let addresses_v2_decoded = + schema_v2::AuthorityRecord::decode(vec_record_v3.as_slice()).unwrap(); assert_eq!(&addresses_v2_decoded.addresses, &vec_addresses); } + +#[test] +fn v3_decodes_v2() { + let peer_secret = Keypair::generate_ed25519(); + let peer_public = peer_secret.public(); + let peer_id = peer_public.to_peer_id(); + let multiaddress: Multiaddr = + format!("/ip4/127.0.0.1/tcp/3003/p2p/{}", peer_id).parse().unwrap(); + let vec_addresses = vec![multiaddress.to_vec()]; + let vec_auth_signature = b"Totally valid signature, I promise!".to_vec(); + let vec_peer_signature = b"Surprisingly hard to crack crypto".to_vec(); + + let record_v2 = schema_v2::AuthorityRecord { addresses: vec_addresses.clone() }; + let mut vec_record_v2 = vec![]; + record_v2.encode(&mut vec_record_v2).unwrap(); + let vec_peer_public = peer_public.encode_protobuf(); + let peer_signature_v2 = + schema_v2::PeerSignature { public_key: vec_peer_public, signature: vec_peer_signature }; + let signed_record_v2 = schema_v2::SignedAuthorityRecord { + record: vec_record_v2.clone(), + auth_signature: vec_auth_signature.clone(), + peer_signature: Some(peer_signature_v2.clone()), + }; + let mut vec_signed_record_v2 = vec![]; + signed_record_v2.encode(&mut vec_signed_record_v2).unwrap(); + + let signed_addresses_v3_decoded = + SignedAuthorityRecord::decode(vec_signed_record_v2.as_slice()).unwrap(); + + assert_eq!(&signed_addresses_v3_decoded.record, &vec_record_v2); + assert_eq!(&signed_addresses_v3_decoded.auth_signature, &vec_auth_signature); + + let addresses_v3_decoded = AuthorityRecord::decode(vec_record_v2.as_slice()).unwrap(); + assert_eq!(&addresses_v3_decoded.addresses, &vec_addresses); + assert_eq!(&addresses_v3_decoded.creation_time, &None); +} diff --git a/substrate/client/authority-discovery/src/worker/tests.rs b/substrate/client/authority-discovery/src/worker/tests.rs index de7443d634fa..b49615382b8a 100644 --- a/substrate/client/authority-discovery/src/worker/tests.rs +++ b/substrate/client/authority-discovery/src/worker/tests.rs @@ -119,6 +119,7 @@ sp_api::mock_impl_runtime_apis! { pub enum TestNetworkEvent { GetCalled(KademliaKey), PutCalled(KademliaKey, Vec), + PutToCalled(Record, HashSet, bool), StoreRecordCalled(KademliaKey, Vec, Option, Option), } @@ -129,6 +130,7 @@ pub struct TestNetwork { // Whenever functions on `TestNetwork` are called, the function arguments are added to the // vectors below. pub put_value_call: Arc)>>>, + pub put_value_to_call: Arc, bool)>>>, pub get_value_call: Arc>>, pub store_value_call: Arc, Option, Option)>>>, @@ -153,6 +155,7 @@ impl Default for TestNetwork { external_addresses: vec!["/ip6/2001:db8::/tcp/30333".parse().unwrap()], put_value_call: Default::default(), get_value_call: Default::default(), + put_value_to_call: Default::default(), store_value_call: Default::default(), event_sender: tx, event_receiver: Some(rx), @@ -200,6 +203,23 @@ impl NetworkDHTProvider for TestNetwork { .unwrap(); } + fn put_record_to( + &self, + record: Record, + peers: HashSet, + update_local_storage: bool, + ) { + self.put_value_to_call.lock().unwrap().push(( + record.clone(), + peers.clone(), + update_local_storage, + )); + self.event_sender + .clone() + .unbounded_send(TestNetworkEvent::PutToCalled(record, peers, update_local_storage)) + .unwrap(); + } + fn store_record( &self, key: KademliaKey, @@ -262,9 +282,11 @@ fn build_dht_event( public_key: AuthorityId, key_store: &MemoryKeystore, network: Option<&Signer>, + creation_time: Option, ) -> Vec<(KademliaKey, Vec)> { let serialized_record = - serialize_authority_record(serialize_addresses(addresses.into_iter())).unwrap(); + serialize_authority_record(serialize_addresses(addresses.into_iter()), creation_time) + .unwrap(); let peer_signature = network.map(|n| sign_record_with_peer_id(&serialized_record, n).unwrap()); let kv_pairs = sign_record_with_authority_ids( @@ -372,7 +394,10 @@ fn publish_discover_cycle() { let dht_event = { let (key, value) = network.put_value_call.lock().unwrap().pop().unwrap(); - DhtEvent::ValueFound(vec![(key, value)]) + DhtEvent::ValueFound(PeerRecord { + peer: None, + record: Record { key, value, publisher: None, expires: None }, + }) }; // Node B discovering node A's address. @@ -515,21 +540,39 @@ fn dont_stop_polling_dht_event_stream_after_bogus_event() { // Send an event that should generate an error dht_event_tx - .send(DhtEvent::ValueFound(Default::default())) + .send(DhtEvent::ValueFound(PeerRecord { + peer: None, + record: Record { + key: vec![0x9u8].into(), + value: Default::default(), + publisher: None, + expires: None, + }, + })) .await .expect("Channel has capacity of 1."); // Make previously triggered lookup succeed. - let dht_event = { - let kv_pairs = build_dht_event::( - vec![remote_multiaddr.clone()], - remote_public_key.clone(), - &remote_key_store, - None, - ); - DhtEvent::ValueFound(kv_pairs) - }; - dht_event_tx.send(dht_event).await.expect("Channel has capacity of 1."); + let kv_pairs: Vec = build_dht_event::( + vec![remote_multiaddr.clone()], + remote_public_key.clone(), + &remote_key_store, + None, + Some(build_creation_time()), + ) + .into_iter() + .map(|(key, value)| PeerRecord { + peer: None, + record: Record { key, value, publisher: None, expires: None }, + }) + .collect(); + + for kv_pair in kv_pairs { + dht_event_tx + .send(DhtEvent::ValueFound(kv_pair)) + .await + .expect("Channel has capacity of 1."); + } // Expect authority discovery to function normally, now knowing the // address for the remote node. @@ -581,37 +624,51 @@ impl DhtValueFoundTester { &mut self, strict_record_validation: bool, values: Vec<(KademliaKey, Vec)>, - ) -> Option<&HashSet> { + ) -> (Option>, Option>) { let (_dht_event_tx, dht_event_rx) = channel(1); let local_test_api = Arc::new(TestApi { authorities: vec![self.remote_authority_public.into()] }); - let local_network: Arc = Arc::new(Default::default()); let local_key_store = MemoryKeystore::new(); let (_to_worker, from_service) = mpsc::channel(0); - let mut local_worker = Worker::new( - from_service, - local_test_api, - local_network.clone(), - Box::pin(dht_event_rx), - Role::PublishAndDiscover(Arc::new(local_key_store)), - None, - WorkerConfig { strict_record_validation, ..Default::default() }, - ); + let (local_worker, local_network) = if let Some(local_work) = self.local_worker.as_mut() { + (local_work, None) + } else { + let local_network: Arc = Arc::new(Default::default()); + + self.local_worker = Some(Worker::new( + from_service, + local_test_api, + local_network.clone(), + Box::pin(dht_event_rx), + Role::PublishAndDiscover(Arc::new(local_key_store)), + None, + WorkerConfig { strict_record_validation, ..Default::default() }, + )); + (self.local_worker.as_mut().unwrap(), Some(local_network)) + }; block_on(local_worker.refill_pending_lookups_queue()).unwrap(); local_worker.start_new_lookups(); - drop(local_worker.handle_dht_value_found_event(values)); - - self.local_worker = Some(local_worker); + for record in values.into_iter().map(|(key, value)| PeerRecord { + peer: Some(PeerId::random().into()), + record: Record { key, value, publisher: None, expires: None }, + }) { + drop(local_worker.handle_dht_value_found_event(record)) + } - self.local_worker - .as_ref() - .map(|w| { - w.addr_cache.get_addresses_by_authority_id(&self.remote_authority_public.into()) - }) - .unwrap() + ( + self.local_worker + .as_ref() + .map(|w| { + w.addr_cache + .get_addresses_by_authority_id(&self.remote_authority_public.into()) + .cloned() + }) + .unwrap(), + local_network, + ) } } @@ -625,9 +682,10 @@ fn limit_number_of_addresses_added_to_cache_per_authority() { tester.remote_authority_public.into(), &tester.remote_key_store, None, + Some(build_creation_time()), ); - let cached_remote_addresses = tester.process_value_found(false, kv_pairs); + let cached_remote_addresses = tester.process_value_found(false, kv_pairs).0; assert_eq!(MAX_ADDRESSES_PER_AUTHORITY, cached_remote_addresses.unwrap().len()); } @@ -640,17 +698,242 @@ fn strict_accept_address_with_peer_signature() { tester.remote_authority_public.into(), &tester.remote_key_store, Some(&TestSigner { keypair: &tester.remote_node_key }), + Some(build_creation_time()), ); - let cached_remote_addresses = tester.process_value_found(true, kv_pairs); + let cached_remote_addresses = tester.process_value_found(true, kv_pairs).0; assert_eq!( - Some(&HashSet::from([addr])), + Some(HashSet::from([addr])), cached_remote_addresses, "Expect worker to only cache `Multiaddr`s with `PeerId`s.", ); } +#[test] +fn strict_accept_address_without_creation_time() { + let mut tester = DhtValueFoundTester::new(); + let addr = tester.multiaddr_with_peer_id(1); + let kv_pairs = build_dht_event( + vec![addr.clone()], + tester.remote_authority_public.into(), + &tester.remote_key_store, + Some(&TestSigner { keypair: &tester.remote_node_key }), + None, + ); + + let cached_remote_addresses = tester.process_value_found(true, kv_pairs).0; + + assert_eq!( + Some(HashSet::from([addr])), + cached_remote_addresses, + "Expect worker to cache address without creation time", + ); +} + +#[test] +fn keep_last_received_if_no_creation_time() { + let mut tester: DhtValueFoundTester = DhtValueFoundTester::new(); + let addr = tester.multiaddr_with_peer_id(1); + let kv_pairs = build_dht_event( + vec![addr.clone()], + tester.remote_authority_public.into(), + &tester.remote_key_store, + Some(&TestSigner { keypair: &tester.remote_node_key }), + None, + ); + + let (cached_remote_addresses, network) = tester.process_value_found(true, kv_pairs); + + assert_eq!( + Some(HashSet::from([addr])), + cached_remote_addresses, + "Expect worker to cache address without creation time", + ); + + assert!(network + .as_ref() + .map(|network| network.put_value_to_call.lock().unwrap().is_empty()) + .unwrap_or_default()); + + let addr2 = tester.multiaddr_with_peer_id(2); + let kv_pairs = build_dht_event( + vec![addr2.clone()], + tester.remote_authority_public.into(), + &tester.remote_key_store, + Some(&TestSigner { keypair: &tester.remote_node_key }), + None, + ); + + let cached_remote_addresses = tester.process_value_found(true, kv_pairs).0; + + assert_eq!( + Some(HashSet::from([addr2])), + cached_remote_addresses, + "Expect worker to cache last received when no creation time", + ); + assert!(network + .as_ref() + .map(|network| network.put_value_to_call.lock().unwrap().is_empty()) + .unwrap_or_default()); +} + +#[test] +fn records_with_incorrectly_signed_creation_time_are_ignored() { + let mut tester: DhtValueFoundTester = DhtValueFoundTester::new(); + let addr = tester.multiaddr_with_peer_id(1); + let kv_pairs = build_dht_event( + vec![addr.clone()], + tester.remote_authority_public.into(), + &tester.remote_key_store, + Some(&TestSigner { keypair: &tester.remote_node_key }), + Some(build_creation_time()), + ); + + let (cached_remote_addresses, network) = tester.process_value_found(true, kv_pairs); + + assert_eq!( + Some(HashSet::from([addr.clone()])), + cached_remote_addresses, + "Expect worker to cache record with creation time", + ); + assert!(network + .as_ref() + .map(|network| network.put_value_to_call.lock().unwrap().is_empty()) + .unwrap_or_default()); + + let alternative_key = tester + .remote_key_store + .sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None) + .unwrap(); + + let addr2 = tester.multiaddr_with_peer_id(2); + let mut kv_pairs = build_dht_event( + vec![addr2.clone()], + alternative_key.into(), + &tester.remote_key_store, + Some(&TestSigner { keypair: &tester.remote_node_key }), + Some(build_creation_time()), + ); + let kademlia_key = hash_authority_id(tester.remote_authority_public.as_slice()); + for key in kv_pairs.iter_mut() { + key.0 = kademlia_key.clone(); + } + let cached_remote_addresses = tester.process_value_found(true, kv_pairs).0; + + assert_eq!( + Some(HashSet::from([addr])), + cached_remote_addresses, + "Expect `Multiaddr` to remain the same", + ); + assert!(network + .as_ref() + .map(|network| network.put_value_to_call.lock().unwrap().is_empty()) + .unwrap_or_default()); +} + +#[test] +fn newer_records_overwrite_older_ones() { + let mut tester: DhtValueFoundTester = DhtValueFoundTester::new(); + let old_record = tester.multiaddr_with_peer_id(1); + let kv_pairs = build_dht_event( + vec![old_record.clone()], + tester.remote_authority_public.into(), + &tester.remote_key_store, + Some(&TestSigner { keypair: &tester.remote_node_key }), + Some(build_creation_time()), + ); + + let (cached_remote_addresses, network) = tester.process_value_found(true, kv_pairs); + + assert_eq!( + Some(HashSet::from([old_record])), + cached_remote_addresses, + "Expect worker to cache record with creation time", + ); + + let nothing_updated = network + .as_ref() + .map(|network| network.put_value_to_call.lock().unwrap().is_empty()) + .unwrap(); + assert!(nothing_updated); + + let new_record = tester.multiaddr_with_peer_id(2); + let kv_pairs = build_dht_event( + vec![new_record.clone()], + tester.remote_authority_public.into(), + &tester.remote_key_store, + Some(&TestSigner { keypair: &tester.remote_node_key }), + Some(build_creation_time()), + ); + + let cached_remote_addresses = tester.process_value_found(true, kv_pairs).0; + + assert_eq!( + Some(HashSet::from([new_record])), + cached_remote_addresses, + "Expect worker to store the newest recrod", + ); + + let result = network + .as_ref() + .map(|network| network.put_value_to_call.lock().unwrap().first().unwrap().clone()) + .unwrap(); + assert!(matches!(result, (_, _, false))); + assert_eq!(result.1.len(), 1); +} + +#[test] +fn older_records_dont_affect_newer_ones() { + let mut tester: DhtValueFoundTester = DhtValueFoundTester::new(); + let old_record = tester.multiaddr_with_peer_id(1); + let old_kv_pairs = build_dht_event( + vec![old_record.clone()], + tester.remote_authority_public.into(), + &tester.remote_key_store, + Some(&TestSigner { keypair: &tester.remote_node_key }), + Some(build_creation_time()), + ); + + let new_record = tester.multiaddr_with_peer_id(2); + let kv_pairs = build_dht_event( + vec![new_record.clone()], + tester.remote_authority_public.into(), + &tester.remote_key_store, + Some(&TestSigner { keypair: &tester.remote_node_key }), + Some(build_creation_time()), + ); + + let (cached_remote_addresses, network) = tester.process_value_found(true, kv_pairs); + + assert_eq!( + Some(HashSet::from([new_record.clone()])), + cached_remote_addresses, + "Expect worker to store new record", + ); + + let nothing_updated = network + .as_ref() + .map(|network| network.put_value_to_call.lock().unwrap().is_empty()) + .unwrap(); + assert!(nothing_updated); + + let cached_remote_addresses = tester.process_value_found(true, old_kv_pairs).0; + + assert_eq!( + Some(HashSet::from([new_record])), + cached_remote_addresses, + "Expect worker to not update stored record", + ); + + let update_peers_info = network + .as_ref() + .map(|network| network.put_value_to_call.lock().unwrap().remove(0)) + .unwrap(); + assert!(matches!(update_peers_info, (_, _, false))); + assert_eq!(update_peers_info.1.len(), 1); +} + #[test] fn reject_address_with_rogue_peer_signature() { let mut tester = DhtValueFoundTester::new(); @@ -660,9 +943,10 @@ fn reject_address_with_rogue_peer_signature() { tester.remote_authority_public.into(), &tester.remote_key_store, Some(&TestSigner { keypair: &rogue_remote_node_key }), + Some(build_creation_time()), ); - let cached_remote_addresses = tester.process_value_found(false, kv_pairs); + let cached_remote_addresses = tester.process_value_found(false, kv_pairs).0; assert!( cached_remote_addresses.is_none(), @@ -678,13 +962,14 @@ fn reject_address_with_invalid_peer_signature() { tester.remote_authority_public.into(), &tester.remote_key_store, Some(&TestSigner { keypair: &tester.remote_node_key }), + Some(build_creation_time()), ); // tamper with the signature let mut record = schema::SignedAuthorityRecord::decode(kv_pairs[0].1.as_slice()).unwrap(); record.peer_signature.as_mut().map(|p| p.signature[1] = !p.signature[1]); record.encode(&mut kv_pairs[0].1).unwrap(); - let cached_remote_addresses = tester.process_value_found(false, kv_pairs); + let cached_remote_addresses = tester.process_value_found(false, kv_pairs).0; assert!( cached_remote_addresses.is_none(), @@ -700,9 +985,10 @@ fn reject_address_without_peer_signature() { tester.remote_authority_public.into(), &tester.remote_key_store, None, + Some(build_creation_time()), ); - let cached_remote_addresses = tester.process_value_found(true, kv_pairs); + let cached_remote_addresses = tester.process_value_found(true, kv_pairs).0; assert!(cached_remote_addresses.is_none(), "Expected worker to ignore unsigned record.",); } @@ -718,12 +1004,13 @@ fn do_not_cache_addresses_without_peer_id() { tester.remote_authority_public.into(), &tester.remote_key_store, None, + Some(build_creation_time()), ); - let cached_remote_addresses = tester.process_value_found(false, kv_pairs); + let cached_remote_addresses = tester.process_value_found(false, kv_pairs).0; assert_eq!( - Some(&HashSet::from([multiaddr_with_peer_id])), + Some(HashSet::from([multiaddr_with_peer_id])), cached_remote_addresses, "Expect worker to only cache `Multiaddr`s with `PeerId`s.", ); @@ -861,16 +1148,24 @@ fn lookup_throttling() { // Make first lookup succeed. let remote_hash = network.get_value_call.lock().unwrap().pop().unwrap(); let remote_key: AuthorityId = remote_hash_to_key.get(&remote_hash).unwrap().clone(); - let dht_event = { - let kv_pairs = build_dht_event::( - vec![remote_multiaddr.clone()], - remote_key, - &remote_key_store, - None, - ); - DhtEvent::ValueFound(kv_pairs) - }; - dht_event_tx.send(dht_event).await.expect("Channel has capacity of 1."); + let kv_pairs = build_dht_event::( + vec![remote_multiaddr.clone()], + remote_key, + &remote_key_store, + None, + Some(build_creation_time()), + ) + .into_iter() + .map(|(key, value)| PeerRecord { + peer: None, + record: Record { key, value, publisher: None, expires: None }, + }); + for kv_pair in kv_pairs { + dht_event_tx + .send(DhtEvent::ValueFound(kv_pair)) + .await + .expect("Channel has capacity of 1."); + } // Assert worker to trigger another lookup. assert!(matches!(receiver.next().await, Some(TestNetworkEvent::GetCalled(_)))); @@ -899,14 +1194,17 @@ fn lookup_throttling() { #[test] fn test_handle_put_record_request() { - let network = TestNetwork::default(); - let peer_id = network.peer_id; + let local_node_network = TestNetwork::default(); + let remote_node_network = TestNetwork::default(); + let peer_id = remote_node_network.peer_id; let remote_multiaddr = { let address: Multiaddr = "/ip6/2001:db8:0:0:0:0:0:1/tcp/30333".parse().unwrap(); - address.with(multiaddr::Protocol::P2p(peer_id.into())) + address.with(multiaddr::Protocol::P2p(remote_node_network.peer_id.into())) }; + + println!("{:?}", remote_multiaddr); let remote_key_store = MemoryKeystore::new(); let remote_public_keys: Vec = (0..20) .map(|_| { @@ -928,7 +1226,7 @@ fn test_handle_put_record_request() { let (_dht_event_tx, dht_event_rx) = channel(1); let (_to_worker, from_service) = mpsc::channel(0); - let network = Arc::new(network); + let network = Arc::new(local_node_network); let mut worker = Worker::new( from_service, Arc::new(TestApi { authorities: remote_public_keys.clone() }), @@ -944,10 +1242,11 @@ fn test_handle_put_record_request() { let valid_authorithy_key = remote_public_keys.first().unwrap().clone(); let kv_pairs = build_dht_event( - vec![remote_multiaddr], - valid_authorithy_key.into(), + vec![remote_multiaddr.clone()], + valid_authorithy_key.clone().into(), &remote_key_store, - Some(&TestSigner { keypair: &network.identity }), + Some(&TestSigner { keypair: &remote_node_network.identity }), + Some(build_creation_time()), ); pool.run_until( @@ -986,7 +1285,7 @@ fn test_handle_put_record_request() { let key = hash_authority_id(another_authorithy_id.as_ref()); // Valid record signed with a different key should return error. - for (_, value) in kv_pairs { + for (_, value) in kv_pairs.clone() { assert!(matches!( worker .handle_put_record_requested(key.clone(), value, Some(peer_id), None) @@ -995,6 +1294,57 @@ fn test_handle_put_record_request() { )); } assert_eq!(network.store_value_call.lock().unwrap().len(), 1); + let newer_kv_pairs = build_dht_event( + vec![remote_multiaddr], + valid_authorithy_key.clone().into(), + &remote_key_store, + Some(&TestSigner { keypair: &remote_node_network.identity }), + Some(build_creation_time()), + ); + + // Valid old authority, should not throw error, but it should not be stored since a + // newer one already exists. + for (new_key, new_value) in newer_kv_pairs.clone() { + worker.in_flight_lookups.insert(new_key.clone(), valid_authorithy_key.clone()); + + let found = PeerRecord { + peer: Some(peer_id.into()), + record: Record { + key: new_key, + value: new_value, + publisher: Some(peer_id.into()), + expires: None, + }, + }; + assert!(worker.handle_dht_value_found_event(found).is_ok()); + } + + for (key, value) in kv_pairs.clone() { + assert!(worker + .handle_put_record_requested(key, value, Some(peer_id), None) + .await + .is_ok()); + } + assert_eq!(network.store_value_call.lock().unwrap().len(), 1); + + // Newer kv pairs should always be stored. + for (key, value) in newer_kv_pairs.clone() { + assert!(worker + .handle_put_record_requested(key, value, Some(peer_id), None) + .await + .is_ok()); + } + + assert_eq!(network.store_value_call.lock().unwrap().len(), 2); + + worker.refill_pending_lookups_queue().await.unwrap(); + assert_eq!(worker.last_known_records.len(), 1); + + // Check known records gets clean up, when an authorithy gets out of the + // active set. + worker.client = Arc::new(TestApi { authorities: Default::default() }); + worker.refill_pending_lookups_queue().await.unwrap(); + assert_eq!(worker.last_known_records.len(), 0); } .boxed_local(), ); diff --git a/substrate/client/network/src/behaviour.rs b/substrate/client/network/src/behaviour.rs index 68816a10980d..9a6324dafd37 100644 --- a/substrate/client/network/src/behaviour.rs +++ b/substrate/client/network/src/behaviour.rs @@ -31,8 +31,13 @@ use crate::{ use futures::channel::oneshot; use libp2p::{ - connection_limits::ConnectionLimits, core::Multiaddr, identify::Info as IdentifyInfo, - identity::PublicKey, kad::RecordKey, swarm::NetworkBehaviour, PeerId, StreamProtocol, + connection_limits::ConnectionLimits, + core::Multiaddr, + identify::Info as IdentifyInfo, + identity::PublicKey, + kad::{Record, RecordKey}, + swarm::NetworkBehaviour, + PeerId, StreamProtocol, }; use parking_lot::Mutex; @@ -289,6 +294,16 @@ impl Behaviour { self.discovery.put_value(key, value); } + /// Puts a record into DHT, on the provided Peers + pub fn put_record_to( + &mut self, + record: Record, + peers: HashSet, + update_local_storage: bool, + ) { + self.discovery.put_record_to(record, peers, update_local_storage); + } + /// Stores value in DHT pub fn store_record( &mut self, diff --git a/substrate/client/network/src/discovery.rs b/substrate/client/network/src/discovery.rs index 3145b891a8d3..86c66c22701c 100644 --- a/substrate/client/network/src/discovery.rs +++ b/substrate/client/network/src/discovery.rs @@ -58,7 +58,8 @@ use libp2p::{ self, record::store::{MemoryStore, RecordStore}, Behaviour as Kademlia, BucketInserts, Config as KademliaConfig, Event as KademliaEvent, - GetClosestPeersError, GetRecordOk, QueryId, QueryResult, Quorum, Record, RecordKey, + GetClosestPeersError, GetRecordOk, PeerRecord, QueryId, QueryResult, Quorum, Record, + RecordKey, }, mdns::{self, tokio::Behaviour as TokioMdns}, multiaddr::Protocol, @@ -92,8 +93,12 @@ const MAX_KNOWN_EXTERNAL_ADDRESSES: usize = 32; /// record is replicated to. pub const DEFAULT_KADEMLIA_REPLICATION_FACTOR: usize = 20; +// The minimum number of peers we expect an answer before we terminate the request. +const GET_RECORD_REDUNDANCY_FACTOR: u32 = 4; + /// `DiscoveryBehaviour` configuration. /// +/// /// Note: In order to discover nodes or load and store values via Kademlia one has to add /// Kademlia protocol via [`DiscoveryConfig::with_kademlia`]. pub struct DiscoveryConfig { @@ -234,7 +239,6 @@ impl DiscoveryConfig { // auto-insertion and instead add peers manually. config.set_kbucket_inserts(BucketInserts::Manual); config.disjoint_query_paths(kademlia_disjoint_query_paths); - let store = MemoryStore::new(local_peer_id); let mut kad = Kademlia::with_config(local_peer_id, store, config); kad.set_mode(Some(kad::Mode::Server)); @@ -437,6 +441,31 @@ impl DiscoveryBehaviour { } } + /// Puts a record into the DHT on the provided `peers` + /// + /// If `update_local_storage` is true, the local storage is update as well. + pub fn put_record_to( + &mut self, + record: Record, + peers: HashSet, + update_local_storage: bool, + ) { + if let Some(kad) = self.kademlia.as_mut() { + if update_local_storage { + if let Err(_e) = kad.store_mut().put(record.clone()) { + warn!(target: "sub-libp2p", "Failed to update local starage"); + } + } + + if !peers.is_empty() { + kad.put_record_to( + record, + peers.into_iter().map(|peer_id| peer_id.into()), + Quorum::All, + ); + } + } + } /// Store a record in the Kademlia record store. pub fn store_record( &mut self, @@ -527,7 +556,7 @@ pub enum DiscoveryOut { /// The DHT yielded results for the record request. /// /// Returning the result grouped in (key, value) pairs as well as the request duration. - ValueFound(Vec<(RecordKey, Vec)>, Duration), + ValueFound(PeerRecord, Duration), /// The DHT received a put record request. PutRecordRequest( @@ -860,16 +889,24 @@ impl NetworkBehaviour for DiscoveryBehaviour { Ok(GetRecordOk::FoundRecord(r)) => { debug!( target: "sub-libp2p", - "Libp2p => Found record ({:?}) with value: {:?}", + "Libp2p => Found record ({:?}) with value: {:?} id {:?} stats {:?}", r.record.key, r.record.value, + id, + stats, ); - // Let's directly finish the query, as we are only interested in a - // quorum of 1. - if let Some(kad) = self.kademlia.as_mut() { - if let Some(mut query) = kad.query_mut(&id) { - query.finish(); + // Let's directly finish the query if we are above 4. + // This number is small enough to make sure we don't + // unnecessarily flood the network with queries, but high + // enough to make sure we also touch peers which might have + // old record, so that we can update them once we notice + // they have old records. + if stats.num_successes() > GET_RECORD_REDUNDANCY_FACTOR { + if let Some(kad) = self.kademlia.as_mut() { + if let Some(mut query) = kad.query_mut(&id) { + query.finish(); + } } } @@ -877,14 +914,18 @@ impl NetworkBehaviour for DiscoveryBehaviour { // `FinishedWithNoAdditionalRecord`. self.records_to_publish.insert(id, r.record.clone()); - DiscoveryOut::ValueFound( - vec![(r.record.key, r.record.value)], - stats.duration().unwrap_or_default(), - ) + DiscoveryOut::ValueFound(r, stats.duration().unwrap_or_default()) }, Ok(GetRecordOk::FinishedWithNoAdditionalRecord { cache_candidates, }) => { + debug!( + target: "sub-libp2p", + "Libp2p => Finished with no-additional-record {:?} stats {:?} took {:?} ms", + id, + stats, + stats.duration().map(|val| val.as_millis()) + ); // We always need to remove the record to not leak any data! if let Some(record) = self.records_to_publish.remove(&id) { if cache_candidates.is_empty() { diff --git a/substrate/client/network/src/event.rs b/substrate/client/network/src/event.rs index b518a2094d76..5400d11cb6ac 100644 --- a/substrate/client/network/src/event.rs +++ b/substrate/client/network/src/event.rs @@ -22,7 +22,10 @@ use crate::types::ProtocolName; use bytes::Bytes; -use libp2p::{kad::record::Key, PeerId}; +use libp2p::{ + kad::{record::Key, PeerRecord}, + PeerId, +}; use sc_network_common::role::ObservedRole; @@ -31,7 +34,7 @@ use sc_network_common::role::ObservedRole; #[must_use] pub enum DhtEvent { /// The value was found. - ValueFound(Vec<(Key, Vec)>), + ValueFound(PeerRecord), /// The requested record has not been found in the DHT. ValueNotFound(Key), diff --git a/substrate/client/network/src/litep2p/discovery.rs b/substrate/client/network/src/litep2p/discovery.rs index 6ff05e6af327..22285e7906c6 100644 --- a/substrate/client/network/src/litep2p/discovery.rs +++ b/substrate/client/network/src/litep2p/discovery.rs @@ -50,6 +50,7 @@ use schnellru::{ByLength, LruMap}; use std::{ cmp, collections::{HashMap, HashSet, VecDeque}, + num::NonZeroUsize, pin::Pin, sync::Arc, task::{Context, Poll}, @@ -68,6 +69,9 @@ const MDNS_QUERY_INTERVAL: Duration = Duration::from_secs(30); /// Minimum number of confirmations received before an address is verified. const MIN_ADDRESS_CONFIRMATIONS: usize = 5; +// The minimum number of peers we expect an answer before we terminate the request. +const GET_RECORD_REDUNDANCY_FACTOR: usize = 4; + /// Discovery events. #[derive(Debug)] pub enum DiscoveryEvent { @@ -340,7 +344,10 @@ impl Discovery { /// Start Kademlia `GET_VALUE` query for `key`. pub async fn get_value(&mut self, key: KademliaKey) -> QueryId { self.kademlia_handle - .get_record(RecordKey::new(&key.to_vec()), Quorum::One) + .get_record( + RecordKey::new(&key.to_vec()), + Quorum::N(NonZeroUsize::new(GET_RECORD_REDUNDANCY_FACTOR).unwrap()), + ) .await } @@ -351,6 +358,22 @@ impl Discovery { .await } + /// Put record to given peers. + pub async fn put_value_to_peers( + &mut self, + record: Record, + peers: Vec, + update_local_storage: bool, + ) -> QueryId { + self.kademlia_handle + .put_record_to_peers( + record, + peers.into_iter().map(|peer| peer.into()).collect(), + update_local_storage, + ) + .await + } + /// Store record in the local DHT store. pub async fn store_record( &mut self, diff --git a/substrate/client/network/src/litep2p/mod.rs b/substrate/client/network/src/litep2p/mod.rs index 4f3cc5789431..475046cb9dba 100644 --- a/substrate/client/network/src/litep2p/mod.rs +++ b/substrate/client/network/src/litep2p/mod.rs @@ -50,7 +50,7 @@ use crate::{ use codec::Encode; use futures::StreamExt; -use libp2p::kad::RecordKey; +use libp2p::kad::{PeerRecord, Record as P2PRecord, RecordKey}; use litep2p::{ config::ConfigBuilder, crypto::ed25519::Keypair, @@ -703,6 +703,12 @@ impl NetworkBackend for Litep2pNetworkBac let query_id = self.discovery.put_value(key.clone(), value).await; self.pending_put_values.insert(query_id, (key, Instant::now())); } + NetworkServiceCommand::PutValueTo { record, peers, update_local_storage} => { + let kademlia_key = record.key.to_vec().into(); + let query_id = self.discovery.put_value_to_peers(record, peers, update_local_storage).await; + self.pending_put_values.insert(query_id, (kademlia_key, Instant::now())); + } + NetworkServiceCommand::StoreRecord { key, value, publisher, expires } => { self.discovery.store_record(key, value, publisher.map(Into::into), expires).await; } @@ -819,19 +825,15 @@ impl NetworkBackend for Litep2pNetworkBac "`GET_VALUE` for {:?} ({query_id:?}) succeeded", key, ); - - let value_found = match records { - RecordsType::LocalStore(record) => vec![ - (libp2p::kad::RecordKey::new(&record.key), record.value) - ], - RecordsType::Network(records) => records.into_iter().map(|peer_record| { - (libp2p::kad::RecordKey::new(&peer_record.record.key), peer_record.record.value) - }).collect(), - }; - - self.event_streams.send(Event::Dht( - DhtEvent::ValueFound(value_found) - )); + for record in litep2p_to_libp2p_peer_record(records) { + self.event_streams.send( + Event::Dht( + DhtEvent::ValueFound( + record + ) + ) + ); + } if let Some(ref metrics) = self.metrics { metrics @@ -1026,3 +1028,42 @@ impl NetworkBackend for Litep2pNetworkBac } } } + +// Glue code to convert from a litep2p records type to a libp2p2 PeerRecord. +fn litep2p_to_libp2p_peer_record(records: RecordsType) -> Vec { + match records { + litep2p::protocol::libp2p::kademlia::RecordsType::LocalStore(record) => { + vec![PeerRecord { + record: P2PRecord { + key: record.key.to_vec().into(), + value: record.value, + publisher: record.publisher.map(|peer_id| { + let peer_id: sc_network_types::PeerId = peer_id.into(); + peer_id.into() + }), + expires: record.expires, + }, + peer: None, + }] + }, + litep2p::protocol::libp2p::kademlia::RecordsType::Network(records) => records + .into_iter() + .map(|record| { + let peer_id: sc_network_types::PeerId = record.peer.into(); + + PeerRecord { + record: P2PRecord { + key: record.record.key.to_vec().into(), + value: record.record.value, + publisher: record.record.publisher.map(|peer_id| { + let peer_id: sc_network_types::PeerId = peer_id.into(); + peer_id.into() + }), + expires: record.record.expires, + }, + peer: Some(peer_id.into()), + } + }) + .collect::>(), + } +} diff --git a/substrate/client/network/src/litep2p/service.rs b/substrate/client/network/src/litep2p/service.rs index 7d972bbeee5c..67fc44e6bfe0 100644 --- a/substrate/client/network/src/litep2p/service.rs +++ b/substrate/client/network/src/litep2p/service.rs @@ -32,6 +32,7 @@ use crate::{ RequestFailure, Signature, }; +use crate::litep2p::Record; use codec::DecodeAll; use futures::{channel::oneshot, stream::BoxStream}; use libp2p::{identity::SigningError, kad::record::Key as KademliaKey}; @@ -76,6 +77,15 @@ pub enum NetworkServiceCommand { value: Vec, }, + /// Put value to DHT. + PutValueTo { + /// Record. + record: Record, + /// Peers we want to put the record. + peers: Vec, + /// If we should update the local storage or not. + update_local_storage: bool, + }, /// Store record in the local DHT store. StoreRecord { /// Record key. @@ -253,6 +263,27 @@ impl NetworkDHTProvider for Litep2pNetworkService { let _ = self.cmd_tx.unbounded_send(NetworkServiceCommand::PutValue { key, value }); } + fn put_record_to( + &self, + record: libp2p::kad::Record, + peers: HashSet, + update_local_storage: bool, + ) { + let _ = self.cmd_tx.unbounded_send(NetworkServiceCommand::PutValueTo { + record: Record { + key: record.key.to_vec().into(), + value: record.value, + publisher: record.publisher.map(|peer_id| { + let peer_id: sc_network_types::PeerId = peer_id.into(); + peer_id.into() + }), + expires: record.expires, + }, + peers: peers.into_iter().collect(), + update_local_storage, + }); + } + fn store_record( &self, key: KademliaKey, diff --git a/substrate/client/network/src/service.rs b/substrate/client/network/src/service.rs index 550e00a6cf2f..71d0b45aa06d 100644 --- a/substrate/client/network/src/service.rs +++ b/substrate/client/network/src/service.rs @@ -68,7 +68,7 @@ use libp2p::{ core::{upgrade, ConnectedPoint, Endpoint}, identify::Info as IdentifyInfo, identity::ed25519, - kad::record::Key as KademliaKey, + kad::{record::Key as KademliaKey, Record}, multiaddr::{self, Multiaddr}, swarm::{ Config as SwarmConfig, ConnectionError, ConnectionId, DialError, Executor, ListenError, @@ -946,6 +946,19 @@ where let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::PutValue(key, value)); } + fn put_record_to( + &self, + record: Record, + peers: HashSet, + update_local_storage: bool, + ) { + let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::PutRecordTo { + record, + peers, + update_local_storage, + }); + } + fn store_record( &self, key: KademliaKey, @@ -1314,6 +1327,11 @@ impl<'a> NotificationSenderReadyT for NotificationSenderReady<'a> { enum ServiceToWorkerMsg { GetValue(KademliaKey), PutValue(KademliaKey, Vec), + PutRecordTo { + record: Record, + peers: HashSet, + update_local_storage: bool, + }, StoreRecord(KademliaKey, Vec, Option, Option), AddKnownAddress(PeerId, Multiaddr), EventStream(out_events::Sender), @@ -1440,6 +1458,10 @@ where self.network_service.behaviour_mut().get_value(key), ServiceToWorkerMsg::PutValue(key, value) => self.network_service.behaviour_mut().put_value(key, value), + ServiceToWorkerMsg::PutRecordTo { record, peers, update_local_storage } => self + .network_service + .behaviour_mut() + .put_record_to(record, peers, update_local_storage), ServiceToWorkerMsg::StoreRecord(key, value, publisher, expires) => self .network_service .behaviour_mut() diff --git a/substrate/client/network/src/service/traits.rs b/substrate/client/network/src/service/traits.rs index 69c9fbc3a618..bd4f83c7fd44 100644 --- a/substrate/client/network/src/service/traits.rs +++ b/substrate/client/network/src/service/traits.rs @@ -32,6 +32,7 @@ use crate::{ }; use futures::{channel::oneshot, Stream}; +use libp2p::kad::Record; use prometheus_endpoint::Registry; use sc_client_api::BlockBackend; @@ -217,6 +218,11 @@ pub trait NetworkDHTProvider { /// Start putting a value in the DHT. fn put_value(&self, key: KademliaKey, value: Vec); + /// Start putting the record to `peers`. + /// + /// If `update_local_storage` is true the local storage is udpated as well. + fn put_record_to(&self, record: Record, peers: HashSet, update_local_storage: bool); + /// Store a record in the DHT memory store. fn store_record( &self, @@ -240,6 +246,10 @@ where T::put_value(self, key, value) } + fn put_record_to(&self, record: Record, peers: HashSet, update_local_storage: bool) { + T::put_record_to(self, record, peers, update_local_storage) + } + fn store_record( &self, key: KademliaKey, From 18db502172bdf438f086cd5964c646b318b8ad37 Mon Sep 17 00:00:00 2001 From: Jun Jiang Date: Fri, 26 Jul 2024 01:20:15 +0800 Subject: [PATCH 017/382] enhancing solochain template (#5143) Since we have a minimal template, I propose enhancing the solochain template, which would be easier for startup projects. - Sync separates `api`, `configs`, and `benchmarks` from the parachain template - introducing `frame-metadata-hash-extension` - Some style update --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- Cargo.lock | 2 + .../parachain/runtime/src/configs/mod.rs | 4 +- templates/parachain/runtime/src/lib.rs | 31 +- templates/solochain/node/Cargo.toml | 3 +- templates/solochain/node/src/benchmarking.rs | 4 +- templates/solochain/node/src/service.rs | 2 +- templates/solochain/runtime/Cargo.toml | 15 + templates/solochain/runtime/build.rs | 18 +- templates/solochain/runtime/src/apis.rs | 295 +++++++++ templates/solochain/runtime/src/benchmarks.rs | 33 + .../solochain/runtime/src/configs/mod.rs | 162 +++++ templates/solochain/runtime/src/lib.rs | 576 ++++-------------- 12 files changed, 650 insertions(+), 495 deletions(-) create mode 100644 templates/solochain/runtime/src/apis.rs create mode 100644 templates/solochain/runtime/src/benchmarks.rs create mode 100644 templates/solochain/runtime/src/configs/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 630503f6041f..bce9bf3d0352 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19716,6 +19716,7 @@ version = "0.0.0" dependencies = [ "clap 4.5.10", "frame-benchmarking-cli", + "frame-metadata-hash-extension", "frame-system", "futures", "jsonrpsee", @@ -19758,6 +19759,7 @@ version = "0.0.0" dependencies = [ "frame-benchmarking", "frame-executive", + "frame-metadata-hash-extension", "frame-support", "frame-system", "frame-system-benchmarking", diff --git a/templates/parachain/runtime/src/configs/mod.rs b/templates/parachain/runtime/src/configs/mod.rs index 204e74a11e6a..607797e690ba 100644 --- a/templates/parachain/runtime/src/configs/mod.rs +++ b/templates/parachain/runtime/src/configs/mod.rs @@ -59,7 +59,7 @@ use super::{ MessageQueue, Nonce, PalletInfo, ParachainSystem, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask, Session, SessionKeys, System, WeightToFee, XcmpQueue, AVERAGE_ON_INITIALIZE_RATIO, EXISTENTIAL_DEPOSIT, HOURS, - MAXIMUM_BLOCK_WEIGHT, MICROUNIT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, VERSION, + MAXIMUM_BLOCK_WEIGHT, MICRO_UNIT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, VERSION, }; use xcm_config::{RelayLocation, XcmOriginToTransactDispatchOrigin}; @@ -162,7 +162,7 @@ impl pallet_balances::Config for Runtime { parameter_types! { /// Relay Chain `TransactionByteFee` / 10 - pub const TransactionByteFee: Balance = 10 * MICROUNIT; + pub const TransactionByteFee: Balance = 10 * MICRO_UNIT; } impl pallet_transaction_payment::Config for Runtime { diff --git a/templates/parachain/runtime/src/lib.rs b/templates/parachain/runtime/src/lib.rs index f53871bb1850..55714a124813 100644 --- a/templates/parachain/runtime/src/lib.rs +++ b/templates/parachain/runtime/src/lib.rs @@ -9,7 +9,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); pub mod apis; #[cfg(feature = "runtime-benchmarks")] mod benchmarks; -mod configs; +pub mod configs; mod weights; extern crate alloc; @@ -37,9 +37,6 @@ pub use sp_runtime::BuildStorage; use weights::ExtrinsicBaseWeight; -/// Import the template pallet. -pub use pallet_parachain_template; - /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. pub type Signature = MultiSignature; @@ -93,6 +90,12 @@ pub type SignedExtra = ( pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +/// All migrations of the runtime, aside from the ones declared in the pallets. +/// +/// This can be a tuple of types, each implementing `OnRuntimeUpgrade`. +#[allow(unused_parens)] +type Migrations = (); + /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -100,6 +103,7 @@ pub type Executive = frame_executive::Executive< frame_system::ChainContext, Runtime, AllPalletsWithSystem, + Migrations, >; /// Handles converting a weight scalar to a fee value, based on the scale and granularity of the @@ -116,9 +120,9 @@ pub struct WeightToFee; impl WeightToFeePolynomial for WeightToFee { type Balance = Balance; fn polynomial() -> WeightToFeeCoefficients { - // in Rococo, extrinsic base weight (smallest non-zero weight) is mapped to 1 MILLIUNIT: - // in our template, we map to 1/10 of that, or 1/10 MILLIUNIT - let p = MILLIUNIT / 10; + // in Rococo, extrinsic base weight (smallest non-zero weight) is mapped to 1 MILLI_UNIT: + // in our template, we map to 1/10 of that, or 1/10 MILLI_UNIT + let p = MILLI_UNIT / 10; let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time()); smallvec![WeightToFeeCoefficient { degree: 1, @@ -141,6 +145,7 @@ pub mod opaque { }; pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + /// Opaque block header type. pub type Header = generic::Header; /// Opaque block type. @@ -177,26 +182,26 @@ mod block_times { /// slot_duration()`. /// /// Change this to adjust the block time. - pub const MILLISECS_PER_BLOCK: u64 = 6000; + pub const MILLI_SECS_PER_BLOCK: u64 = 6000; // NOTE: Currently it is not possible to change the slot duration after the chain has started. // Attempting to do so will brick block production. - pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + pub const SLOT_DURATION: u64 = MILLI_SECS_PER_BLOCK; } pub use block_times::*; // Time is measured by number of blocks. -pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const MINUTES: BlockNumber = 60_000 / (MILLI_SECS_PER_BLOCK as BlockNumber); pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; // Unit = the base number of indivisible units for balances pub const UNIT: Balance = 1_000_000_000_000; -pub const MILLIUNIT: Balance = 1_000_000_000; -pub const MICROUNIT: Balance = 1_000_000; +pub const MILLI_UNIT: Balance = 1_000_000_000; +pub const MICRO_UNIT: Balance = 1_000_000; /// The existential deposit. Set to 1/10 of the Connected Relay Chain. -pub const EXISTENTIAL_DEPOSIT: Balance = MILLIUNIT; +pub const EXISTENTIAL_DEPOSIT: Balance = MILLI_UNIT; /// We assume that ~5% of the block weight is consumed by `on_initialize` handlers. This is /// used to limit the maximal weight of a single extrinsic. diff --git a/templates/solochain/node/Cargo.toml b/templates/solochain/node/Cargo.toml index 068284c6c3ea..6eebf3694e3a 100644 --- a/templates/solochain/node/Cargo.toml +++ b/templates/solochain/node/Cargo.toml @@ -51,7 +51,8 @@ sp-block-builder = { workspace = true, default-features = true } # frame and pallets frame-system = { workspace = true, default-features = true } -pallet-transaction-payment = { workspace = true } +frame-metadata-hash-extension = { workspace = true, default-features = true } +pallet-transaction-payment = { workspace = true, default-features = true } pallet-transaction-payment-rpc = { workspace = true, default-features = true } substrate-frame-rpc-system = { workspace = true, default-features = true } diff --git a/templates/solochain/node/src/benchmarking.rs b/templates/solochain/node/src/benchmarking.rs index d1d8c2ccabaf..1bd3578af689 100644 --- a/templates/solochain/node/src/benchmarking.rs +++ b/templates/solochain/node/src/benchmarking.rs @@ -105,7 +105,7 @@ pub fn create_benchmark_extrinsic( let best_hash = client.chain_info().best_hash; let best_block = client.chain_info().best_number; - let period = runtime::BlockHashCount::get() + let period = runtime::configs::BlockHashCount::get() .checked_next_power_of_two() .map(|c| c / 2) .unwrap_or(2) as u64; @@ -121,6 +121,7 @@ pub fn create_benchmark_extrinsic( frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), + frame_metadata_hash_extension::CheckMetadataHash::::new(false), ); let raw_payload = runtime::SignedPayload::from_raw( @@ -135,6 +136,7 @@ pub fn create_benchmark_extrinsic( (), (), (), + None, ), ); let signature = raw_payload.using_encoded(|e| sender.sign(e)); diff --git a/templates/solochain/node/src/service.rs b/templates/solochain/node/src/service.rs index ad917f1dd104..5e84552f4ccd 100644 --- a/templates/solochain/node/src/service.rs +++ b/templates/solochain/node/src/service.rs @@ -7,7 +7,7 @@ use sc_consensus_grandpa::SharedVoterState; use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncParams}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; -use solochain_template_runtime::{self, opaque::Block, RuntimeApi}; +use solochain_template_runtime::{self, apis::RuntimeApi, opaque::Block}; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use std::{sync::Arc, time::Duration}; diff --git a/templates/solochain/runtime/Cargo.toml b/templates/solochain/runtime/Cargo.toml index f06c80b9a032..9a1f7145c2ca 100644 --- a/templates/solochain/runtime/Cargo.toml +++ b/templates/solochain/runtime/Cargo.toml @@ -26,6 +26,7 @@ frame-support = { features = ["experimental"], workspace = true } frame-system = { workspace = true } frame-try-runtime = { optional = true, workspace = true } frame-executive = { workspace = true } +frame-metadata-hash-extension = { workspace = true } # frame pallets pallet-aura = { workspace = true } @@ -81,6 +82,7 @@ std = [ "scale-info/std", "frame-executive/std", + "frame-metadata-hash-extension/std", "frame-support/std", "frame-system-benchmarking?/std", "frame-system-rpc-runtime-api/std", @@ -142,3 +144,16 @@ try-runtime = [ "pallet-transaction-payment/try-runtime", "sp-runtime/try-runtime", ] + +# Enable the metadata hash generation. +# +# This is hidden behind a feature because it increases the compile time. +# The wasm binary needs to be compiled twice, once to fetch the metadata, +# generate the metadata hash and then a second time with the +# `RUNTIME_METADATA_HASH` environment variable set for the `CheckMetadataHash` +# extension. +metadata-hash = ["substrate-wasm-builder/metadata-hash"] + +# A convenience feature for enabling things when doing a build +# for an on-chain release. +on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] diff --git a/templates/solochain/runtime/build.rs b/templates/solochain/runtime/build.rs index f262c320393b..caac8518cbbb 100644 --- a/templates/solochain/runtime/build.rs +++ b/templates/solochain/runtime/build.rs @@ -1,6 +1,16 @@ +#[cfg(all(feature = "std", feature = "metadata-hash"))] fn main() { - #[cfg(feature = "std")] - { - substrate_wasm_builder::WasmBuilder::build_using_defaults(); - } + substrate_wasm_builder::WasmBuilder::init_with_defaults() + .enable_metadata_hash("UNIT", 12) + .build(); } + +#[cfg(all(feature = "std", not(feature = "metadata-hash")))] +fn main() { + substrate_wasm_builder::WasmBuilder::build_using_defaults(); +} + +/// The wasm builder is deactivated when compiling +/// this crate for wasm to speed up the compilation. +#[cfg(not(feature = "std"))] +fn main() {} diff --git a/templates/solochain/runtime/src/apis.rs b/templates/solochain/runtime/src/apis.rs new file mode 100644 index 000000000000..1e3dc452857c --- /dev/null +++ b/templates/solochain/runtime/src/apis.rs @@ -0,0 +1,295 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to + +// External crates imports +use alloc::{vec, vec::Vec}; +use frame_support::{ + genesis_builder_helper::{build_state, get_preset}, + weights::Weight, +}; +use pallet_grandpa::AuthorityId as GrandpaId; +use sp_api::impl_runtime_apis; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_runtime::{ + traits::{Block as BlockT, NumberFor}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, +}; +use sp_version::RuntimeVersion; + +// Local module imports +use super::{ + AccountId, Aura, Balance, Block, Executive, Grandpa, InherentDataExt, Nonce, Runtime, + RuntimeCall, RuntimeGenesisConfig, SessionKeys, System, TransactionPayment, VERSION, +}; + +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block); + } + + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> Vec { + Runtime::metadata_versions() + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + pallet_aura::Authorities::::get().into_inner() + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl sp_consensus_grandpa::GrandpaApi for Runtime { + fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList { + Grandpa::grandpa_authorities() + } + + fn current_set_id() -> sp_consensus_grandpa::SetId { + Grandpa::current_set_id() + } + + fn submit_report_equivocation_unsigned_extrinsic( + _equivocation_proof: sp_consensus_grandpa::EquivocationProof< + ::Hash, + NumberFor, + >, + _key_owner_proof: sp_consensus_grandpa::OpaqueKeyOwnershipProof, + ) -> Option<()> { + None + } + + fn generate_key_ownership_proof( + _set_id: sp_consensus_grandpa::SetId, + _authority_id: GrandpaId, + ) -> Option { + // NOTE: this is the only implementation possible since we've + // defined our key owner proof type as a bottom type (i.e. a type + // with no values). + None + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Nonce { + System::account_nonce(account) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi + for Runtime + { + fn query_call_info( + call: RuntimeCall, + len: u32, + ) -> pallet_transaction_payment::RuntimeDispatchInfo { + TransactionPayment::query_call_info(call, len) + } + fn query_call_fee_details( + call: RuntimeCall, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_call_fee_details(call, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(extra: bool) -> ( + Vec, + Vec, + ) { + use frame_benchmarking::{baseline, Benchmarking, BenchmarkList}; + use frame_support::traits::StorageInfoTrait; + use frame_system_benchmarking::Pallet as SystemBench; + use baseline::Pallet as BaselineBench; + use super::*; + + let mut list = Vec::::new(); + list_benchmarks!(list, extra); + + let storage_info = AllPalletsWithSystem::storage_info(); + + (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{baseline, Benchmarking, BenchmarkBatch}; + use sp_storage::TrackedStorageKey; + use frame_system_benchmarking::Pallet as SystemBench; + use baseline::Pallet as BaselineBench; + use super::*; + + impl frame_system_benchmarking::Config for Runtime {} + impl baseline::Config for Runtime {} + + use frame_support::traits::WhitelistedStorageKeys; + let whitelist: Vec = AllPalletsWithSystem::whitelisted_storage_keys(); + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + add_benchmarks!(params, batches); + + Ok(batches) + } + } + + #[cfg(feature = "try-runtime")] + impl frame_try_runtime::TryRuntime for Runtime { + fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { + // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to + // have a backtrace here. If any of the pre/post migration checks fail, we shall stop + // right here and right now. + let weight = Executive::try_runtime_upgrade(checks).unwrap(); + (weight, super::configs::RuntimeBlockWeights::get().max_block) + } + + fn execute_block( + block: Block, + state_root_check: bool, + signature_check: bool, + select: frame_try_runtime::TryStateSelect + ) -> Weight { + // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to + // have a backtrace here. + Executive::try_execute_block(block, state_root_check, signature_check, select).expect("execute-block failed") + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn build_state(config: Vec) -> sp_genesis_builder::Result { + build_state::(config) + } + + fn get_preset(id: &Option) -> Option> { + get_preset::(id, |_| None) + } + + fn preset_names() -> Vec { + vec![] + } + } +} diff --git a/templates/solochain/runtime/src/benchmarks.rs b/templates/solochain/runtime/src/benchmarks.rs new file mode 100644 index 000000000000..a42daf56b58e --- /dev/null +++ b/templates/solochain/runtime/src/benchmarks.rs @@ -0,0 +1,33 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to + +frame_benchmarking::define_benchmarks!( + [frame_benchmarking, BaselineBench::] + [frame_system, SystemBench::] + [pallet_balances, Balances] + [pallet_timestamp, Timestamp] + [pallet_sudo, Sudo] + [pallet_template, TemplateModule] +); diff --git a/templates/solochain/runtime/src/configs/mod.rs b/templates/solochain/runtime/src/configs/mod.rs new file mode 100644 index 000000000000..a5c12fbd79ab --- /dev/null +++ b/templates/solochain/runtime/src/configs/mod.rs @@ -0,0 +1,162 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to + +// Substrate and Polkadot dependencies +use frame_support::{ + derive_impl, parameter_types, + traits::{ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, VariantCountOf}, + weights::{ + constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, + IdentityFee, Weight, + }, +}; +use frame_system::limits::{BlockLength, BlockWeights}; +use pallet_transaction_payment::{ConstFeeMultiplier, FungibleAdapter, Multiplier}; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_runtime::{traits::One, Perbill}; +use sp_version::RuntimeVersion; + +// Local module imports +use super::{ + AccountId, Aura, Balance, Balances, Block, BlockNumber, Hash, Nonce, PalletInfo, Runtime, + RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask, + System, EXISTENTIAL_DEPOSIT, SLOT_DURATION, VERSION, +}; + +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +parameter_types! { + pub const BlockHashCount: BlockNumber = 2400; + pub const Version: RuntimeVersion = VERSION; + + /// We allow for 2 seconds of compute with a 6 second average block time. + pub RuntimeBlockWeights: BlockWeights = BlockWeights::with_sensible_defaults( + Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), + NORMAL_DISPATCH_RATIO, + ); + pub RuntimeBlockLength: BlockLength = BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub const SS58Prefix: u8 = 42; +} + +/// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from +/// [`SoloChainDefaultConfig`](`struct@frame_system::config_preludes::SolochainDefaultConfig`), +/// but overridden as needed. +#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)] +impl frame_system::Config for Runtime { + /// The block type for the runtime. + type Block = Block; + /// Block & extrinsics weights: base values and limits. + type BlockWeights = RuntimeBlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = RuntimeBlockLength; + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The type for storing how many extrinsics an account has signed. + type Nonce = Nonce; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// The weight of database operations that the runtime can invoke. + type DbWeight = RocksDbWeight; + /// Version of the runtime. + type Version = Version; + /// The data to be stored in an account. + type AccountData = pallet_balances::AccountData; + /// This is used as an identifier of the chain. 42 is the generic substrate prefix. + type SS58Prefix = SS58Prefix; + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = ConstU32<32>; + type AllowMultipleBlocksPerSlot = ConstBool; + type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; +} + +impl pallet_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + + type WeightInfo = (); + type MaxAuthorities = ConstU32<32>; + type MaxNominators = ConstU32<0>; + type MaxSetIdSessionEntries = ConstU64<0>; + + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the unix epoch. + type Moment = u64; + type OnTimestampSet = Aura; + type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; + type WeightInfo = (); +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = ConstU32<50>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU128; + type AccountStore = System; + type WeightInfo = pallet_balances::weights::SubstrateWeight; + type FreezeIdentifier = RuntimeFreezeReason; + type MaxFreezes = VariantCountOf; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeHoldReason; +} + +parameter_types! { + pub FeeMultiplier: Multiplier = Multiplier::one(); +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = FungibleAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = IdentityFee; + type LengthToFee = IdentityFee; + type FeeMultiplierUpdate = ConstFeeMultiplier; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WeightInfo = pallet_sudo::weights::SubstrateWeight; +} + +/// Configure the pallet-template in pallets/template. +impl pallet_template::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_template::weights::SubstrateWeight; +} diff --git a/templates/solochain/runtime/src/lib.rs b/templates/solochain/runtime/src/lib.rs index 9de95ac95695..6cbfbb879602 100644 --- a/templates/solochain/runtime/src/lib.rs +++ b/templates/solochain/runtime/src/lib.rs @@ -3,69 +3,27 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +pub mod apis; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarks; +pub mod configs; + extern crate alloc; -use alloc::{vec, vec::Vec}; -use pallet_grandpa::AuthorityId as GrandpaId; -use sp_api::impl_runtime_apis; -use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use alloc::vec::Vec; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{BlakeTwo256, Block as BlockT, IdentifyAccount, NumberFor, One, Verify}, - transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, MultiSignature, + traits::{BlakeTwo256, IdentifyAccount, Verify}, + MultiAddress, MultiSignature, }; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -pub use frame_support::{ - construct_runtime, derive_impl, parameter_types, - traits::{ - ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, KeyOwnerProofSystem, Randomness, - StorageInfo, - }, - weights::{ - constants::{ - BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, - }, - IdentityFee, Weight, - }, - StorageValue, -}; -use frame_support::{ - genesis_builder_helper::{build_state, get_preset}, - traits::VariantCountOf, -}; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; pub use pallet_timestamp::Call as TimestampCall; -use pallet_transaction_payment::{ConstFeeMultiplier, FungibleAdapter, Multiplier}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; -pub use sp_runtime::{Perbill, Permill}; - -/// Import the template pallet. -pub use pallet_template; - -/// An index to a block. -pub type BlockNumber = u32; - -/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. -pub type Signature = MultiSignature; - -/// Some way of identifying an account on the chain. We intentionally make it equivalent -/// to the public key of our transaction signing scheme. -pub type AccountId = <::Signer as IdentifyAccount>::AccountId; - -/// Balance of an account. -pub type Balance = u128; - -/// Index of a transaction in the chain. -pub type Nonce = u32; - -/// A hash of some data used by the chain. -pub type Hash = sp_core::H256; /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats @@ -73,6 +31,10 @@ pub type Hash = sp_core::H256; /// to even the core data structures. pub mod opaque { use super::*; + use sp_runtime::{ + generic, + traits::{BlakeTwo256, Hash as HashT}, + }; pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; @@ -82,12 +44,14 @@ pub mod opaque { pub type Block = generic::Block; /// Opaque block identifier type. pub type BlockId = generic::BlockId; + /// Opaque block hash type. + pub type Hash = ::Output; +} - impl_opaque_keys! { - pub struct SessionKeys { - pub aura: Aura, - pub grandpa: Grandpa, - } +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + pub grandpa: Grandpa, } } @@ -105,153 +69,116 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // the compatible custom types. spec_version: 100, impl_version: 1, - apis: RUNTIME_API_VERSIONS, + apis: apis::RUNTIME_API_VERSIONS, transaction_version: 1, state_version: 1, }; -/// This determines the average expected block time that we are targeting. -/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. -/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked -/// up by `pallet_aura` to implement `fn slot_duration()`. -/// -/// Change this to adjust the block time. -pub const MILLISECS_PER_BLOCK: u64 = 6000; - -// NOTE: Currently it is not possible to change the slot duration after the chain has started. -// Attempting to do so will brick block production. -pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; +mod block_times { + /// This determines the average expected block time that we are targeting. Blocks will be + /// produced at a minimum duration defined by `SLOT_DURATION`. `SLOT_DURATION` is picked up by + /// `pallet_timestamp` which is in turn picked up by `pallet_aura` to implement `fn + /// slot_duration()`. + /// + /// Change this to adjust the block time. + pub const MILLI_SECS_PER_BLOCK: u64 = 6000; + + // NOTE: Currently it is not possible to change the slot duration after the chain has started. + // Attempting to do so will brick block production. + pub const SLOT_DURATION: u64 = MILLI_SECS_PER_BLOCK; +} +pub use block_times::*; // Time is measured by number of blocks. -pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const MINUTES: BlockNumber = 60_000 / (MILLI_SECS_PER_BLOCK as BlockNumber); pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; +pub const BLOCK_HASH_COUNT: BlockNumber = 2400; + +// Unit = the base number of indivisible units for balances +pub const UNIT: Balance = 1_000_000_000_000; +pub const MILLI_UNIT: Balance = 1_000_000_000; +pub const MICRO_UNIT: Balance = 1_000_000; + +/// Existential deposit. +pub const EXISTENTIAL_DEPOSIT: Balance = MILLI_UNIT; + /// The version information used to identify this runtime when compiled natively. #[cfg(feature = "std")] pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } -const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); - -parameter_types! { - pub const BlockHashCount: BlockNumber = 2400; - pub const Version: RuntimeVersion = VERSION; - /// We allow for 2 seconds of compute with a 6 second average block time. - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::with_sensible_defaults( - Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), - NORMAL_DISPATCH_RATIO, - ); - pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength - ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); - pub const SS58Prefix: u8 = 42; -} +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; -/// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from -/// [`SoloChainDefaultConfig`](`struct@frame_system::config_preludes::SolochainDefaultConfig`), -/// but overridden as needed. -#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)] -impl frame_system::Config for Runtime { - /// The block type for the runtime. - type Block = Block; - /// Block & extrinsics weights: base values and limits. - type BlockWeights = BlockWeights; - /// The maximum length of a block (in bytes). - type BlockLength = BlockLength; - /// The identifier used to distinguish between accounts. - type AccountId = AccountId; - /// The type for storing how many extrinsics an account has signed. - type Nonce = Nonce; - /// The type for hashing blocks and tries. - type Hash = Hash; - /// Maximum number of block number to block hash mappings to keep (oldest pruned first). - type BlockHashCount = BlockHashCount; - /// The weight of database operations that the runtime can invoke. - type DbWeight = RocksDbWeight; - /// Version of the runtime. - type Version = Version; - /// The data to be stored in an account. - type AccountData = pallet_balances::AccountData; - /// This is used as an identifier of the chain. 42 is the generic substrate prefix. - type SS58Prefix = SS58Prefix; - type MaxConsumers = frame_support::traits::ConstU32<16>; -} +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; -impl pallet_aura::Config for Runtime { - type AuthorityId = AuraId; - type DisabledValidators = (); - type MaxAuthorities = ConstU32<32>; - type AllowMultipleBlocksPerSlot = ConstBool; - type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; -} +/// Balance of an account. +pub type Balance = u128; -impl pallet_grandpa::Config for Runtime { - type RuntimeEvent = RuntimeEvent; +/// Index of a transaction in the chain. +pub type Nonce = u32; - type WeightInfo = (); - type MaxAuthorities = ConstU32<32>; - type MaxNominators = ConstU32<0>; - type MaxSetIdSessionEntries = ConstU64<0>; +/// A hash of some data used by the chain. +pub type Hash = sp_core::H256; - type KeyOwnerProof = sp_core::Void; - type EquivocationReportSystem = (); -} +/// An index to a block. +pub type BlockNumber = u32; -impl pallet_timestamp::Config for Runtime { - /// A timestamp: milliseconds since the unix epoch. - type Moment = u64; - type OnTimestampSet = Aura; - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; - type WeightInfo = (); -} +/// The address format for describing accounts. +pub type Address = MultiAddress; -/// Existential deposit. -pub const EXISTENTIAL_DEPOSIT: u128 = 500; - -impl pallet_balances::Config for Runtime { - type MaxLocks = ConstU32<50>; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - /// The type for recording an account's balance. - type Balance = Balance; - /// The ubiquitous event type. - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU128; - type AccountStore = System; - type WeightInfo = pallet_balances::weights::SubstrateWeight; - type FreezeIdentifier = RuntimeFreezeReason; - type MaxFreezes = VariantCountOf; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeHoldReason; -} +/// Block header type as expected by this runtime. +pub type Header = generic::Header; -parameter_types! { - pub FeeMultiplier: Multiplier = Multiplier::one(); -} +/// Block type as expected by this runtime. +pub type Block = generic::Block; -impl pallet_transaction_payment::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = FungibleAdapter; - type OperationalFeeMultiplier = ConstU8<5>; - type WeightToFee = IdentityFee; - type LengthToFee = IdentityFee; - type FeeMultiplierUpdate = ConstFeeMultiplier; -} +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; -impl pallet_sudo::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type WeightInfo = pallet_sudo::weights::SubstrateWeight; -} +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; -/// Configure the pallet-template in pallets/template. -impl pallet_template::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = pallet_template::weights::SubstrateWeight; -} +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, + frame_metadata_hash_extension::CheckMetadataHash, +); + +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; + +/// The payload being signed in transactions. +pub type SignedPayload = generic::SignedPayload; + +/// All migrations of the runtime, aside from the ones declared in the pallets. +/// +/// This can be a tuple of types, each implementing `OnRuntimeUpgrade`. +#[allow(unused_parens)] +type Migrations = (); + +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, + Migrations, +>; // Create the runtime by composing the FRAME pallets that were previously configured. #[frame_support::runtime] @@ -295,300 +222,3 @@ mod runtime { #[runtime::pallet_index(7)] pub type TemplateModule = pallet_template; } - -/// The address format for describing accounts. -pub type Address = sp_runtime::MultiAddress; -/// Block header type as expected by this runtime. -pub type Header = generic::Header; -/// Block type as expected by this runtime. -pub type Block = generic::Block; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( - frame_system::CheckNonZeroSender, - frame_system::CheckSpecVersion, - frame_system::CheckTxVersion, - frame_system::CheckGenesis, - frame_system::CheckEra, - frame_system::CheckNonce, - frame_system::CheckWeight, - pallet_transaction_payment::ChargeTransactionPayment, -); - -/// All migrations of the runtime, aside from the ones declared in the pallets. -/// -/// This can be a tuple of types, each implementing `OnRuntimeUpgrade`. -#[allow(unused_parens)] -type Migrations = (); - -/// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; -/// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; -/// Executive: handles dispatch to the various modules. -pub type Executive = frame_executive::Executive< - Runtime, - Block, - frame_system::ChainContext, - Runtime, - AllPalletsWithSystem, - Migrations, ->; - -#[cfg(feature = "runtime-benchmarks")] -mod benches { - frame_benchmarking::define_benchmarks!( - [frame_benchmarking, BaselineBench::] - [frame_system, SystemBench::] - [pallet_balances, Balances] - [pallet_timestamp, Timestamp] - [pallet_sudo, Sudo] - [pallet_template, TemplateModule] - ); -} - -impl_runtime_apis! { - impl sp_api::Core for Runtime { - fn version() -> RuntimeVersion { - VERSION - } - - fn execute_block(block: Block) { - Executive::execute_block(block); - } - - fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { - Executive::initialize_block(header) - } - } - - impl sp_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - OpaqueMetadata::new(Runtime::metadata().into()) - } - - fn metadata_at_version(version: u32) -> Option { - Runtime::metadata_at_version(version) - } - - fn metadata_versions() -> Vec { - Runtime::metadata_versions() - } - } - - impl sp_block_builder::BlockBuilder for Runtime { - fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { - Executive::apply_extrinsic(extrinsic) - } - - fn finalize_block() -> ::Header { - Executive::finalize_block() - } - - fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { - data.create_extrinsics() - } - - fn check_inherents( - block: Block, - data: sp_inherents::InherentData, - ) -> sp_inherents::CheckInherentsResult { - data.check_extrinsics(&block) - } - } - - impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { - fn validate_transaction( - source: TransactionSource, - tx: ::Extrinsic, - block_hash: ::Hash, - ) -> TransactionValidity { - Executive::validate_transaction(source, tx, block_hash) - } - } - - impl sp_offchain::OffchainWorkerApi for Runtime { - fn offchain_worker(header: &::Header) { - Executive::offchain_worker(header) - } - } - - impl sp_consensus_aura::AuraApi for Runtime { - fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) - } - - fn authorities() -> Vec { - pallet_aura::Authorities::::get().into_inner() - } - } - - impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - opaque::SessionKeys::generate(seed) - } - - fn decode_session_keys( - encoded: Vec, - ) -> Option, KeyTypeId)>> { - opaque::SessionKeys::decode_into_raw_public_keys(&encoded) - } - } - - impl sp_consensus_grandpa::GrandpaApi for Runtime { - fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList { - Grandpa::grandpa_authorities() - } - - fn current_set_id() -> sp_consensus_grandpa::SetId { - Grandpa::current_set_id() - } - - fn submit_report_equivocation_unsigned_extrinsic( - _equivocation_proof: sp_consensus_grandpa::EquivocationProof< - ::Hash, - NumberFor, - >, - _key_owner_proof: sp_consensus_grandpa::OpaqueKeyOwnershipProof, - ) -> Option<()> { - None - } - - fn generate_key_ownership_proof( - _set_id: sp_consensus_grandpa::SetId, - _authority_id: GrandpaId, - ) -> Option { - // NOTE: this is the only implementation possible since we've - // defined our key owner proof type as a bottom type (i.e. a type - // with no values). - None - } - } - - impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { - fn account_nonce(account: AccountId) -> Nonce { - System::account_nonce(account) - } - } - - impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { - fn query_info( - uxt: ::Extrinsic, - len: u32, - ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { - TransactionPayment::query_info(uxt, len) - } - fn query_fee_details( - uxt: ::Extrinsic, - len: u32, - ) -> pallet_transaction_payment::FeeDetails { - TransactionPayment::query_fee_details(uxt, len) - } - fn query_weight_to_fee(weight: Weight) -> Balance { - TransactionPayment::weight_to_fee(weight) - } - fn query_length_to_fee(length: u32) -> Balance { - TransactionPayment::length_to_fee(length) - } - } - - impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi - for Runtime - { - fn query_call_info( - call: RuntimeCall, - len: u32, - ) -> pallet_transaction_payment::RuntimeDispatchInfo { - TransactionPayment::query_call_info(call, len) - } - fn query_call_fee_details( - call: RuntimeCall, - len: u32, - ) -> pallet_transaction_payment::FeeDetails { - TransactionPayment::query_call_fee_details(call, len) - } - fn query_weight_to_fee(weight: Weight) -> Balance { - TransactionPayment::weight_to_fee(weight) - } - fn query_length_to_fee(length: u32) -> Balance { - TransactionPayment::length_to_fee(length) - } - } - - #[cfg(feature = "runtime-benchmarks")] - impl frame_benchmarking::Benchmark for Runtime { - fn benchmark_metadata(extra: bool) -> ( - Vec, - Vec, - ) { - use frame_benchmarking::{baseline, Benchmarking, BenchmarkList}; - use frame_support::traits::StorageInfoTrait; - use frame_system_benchmarking::Pallet as SystemBench; - use baseline::Pallet as BaselineBench; - - let mut list = Vec::::new(); - list_benchmarks!(list, extra); - - let storage_info = AllPalletsWithSystem::storage_info(); - - (list, storage_info) - } - - fn dispatch_benchmark( - config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { - use frame_benchmarking::{baseline, Benchmarking, BenchmarkBatch}; - use sp_storage::TrackedStorageKey; - use frame_system_benchmarking::Pallet as SystemBench; - use baseline::Pallet as BaselineBench; - - impl frame_system_benchmarking::Config for Runtime {} - impl baseline::Config for Runtime {} - - use frame_support::traits::WhitelistedStorageKeys; - let whitelist: Vec = AllPalletsWithSystem::whitelisted_storage_keys(); - - let mut batches = Vec::::new(); - let params = (&config, &whitelist); - add_benchmarks!(params, batches); - - Ok(batches) - } - } - - #[cfg(feature = "try-runtime")] - impl frame_try_runtime::TryRuntime for Runtime { - fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { - // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to - // have a backtrace here. If any of the pre/post migration checks fail, we shall stop - // right here and right now. - let weight = Executive::try_runtime_upgrade(checks).unwrap(); - (weight, BlockWeights::get().max_block) - } - - fn execute_block( - block: Block, - state_root_check: bool, - signature_check: bool, - select: frame_try_runtime::TryStateSelect - ) -> Weight { - // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to - // have a backtrace here. - Executive::try_execute_block(block, state_root_check, signature_check, select).expect("execute-block failed") - } - } - - impl sp_genesis_builder::GenesisBuilder for Runtime { - fn build_state(config: Vec) -> sp_genesis_builder::Result { - build_state::(config) - } - - fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) - } - - fn preset_names() -> Vec { - vec![] - } - } -} From 6efa1ee85ad1b5d83ec69843fce85e8cd9a4d2f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 10:39:50 +0200 Subject: [PATCH 018/382] Bump walkdir from 2.4.0 to 2.5.0 (#5135) Bumps [walkdir](https://github.com/BurntSushi/walkdir) from 2.4.0 to 2.5.0.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=walkdir&package-manager=cargo&previous-version=2.4.0&new-version=2.5.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bce9bf3d0352..03290d630b51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22941,9 +22941,9 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", diff --git a/Cargo.toml b/Cargo.toml index ba0526b21c36..50c9fc88836f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1330,7 +1330,7 @@ url = { version = "2.4.0" } void = { version = "1.0.2" } w3f-bls = { version = "0.1.3", default-features = false } wait-timeout = { version = "0.2" } -walkdir = { version = "2.4.0" } +walkdir = { version = "2.5.0" } wasm-bindgen-test = { version = "0.3.19" } wasm-instrument = { version = "0.4", default-features = false } wasm-opt = { version = "0.116" } From 90b553376b4d9f787b2b49a8685ba9de5b56374b Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Fri, 26 Jul 2024 04:47:54 -0400 Subject: [PATCH 019/382] Add `from_mel` for `Footprint` with a Test (#5132) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We may want to construct a `Footprint` by taking the `max_encoded_len` of a type, without having to construct the type. This impl allows easy access to that. --------- Co-authored-by: Bastian Köcher Co-authored-by: command-bot <> --- prdoc/pr_5132.prdoc | 15 ++++++++++++ substrate/frame/support/src/traits/storage.rs | 23 ++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 prdoc/pr_5132.prdoc diff --git a/prdoc/pr_5132.prdoc b/prdoc/pr_5132.prdoc new file mode 100644 index 000000000000..f23574e04b79 --- /dev/null +++ b/prdoc/pr_5132.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add `from_mel` for `Footprint` + +doc: + - audience: Runtime Dev + description: | + This introduces a new way to generate the `Footprint` type by calculating the max encoded + length of some generic type. This allows you to generate a `Footprint` of a type without + actually constructing that type. + +crates: + - name: frame-support + bump: patch diff --git a/substrate/frame/support/src/traits/storage.rs b/substrate/frame/support/src/traits/storage.rs index 22fb28e4c0e7..a954af14d259 100644 --- a/substrate/frame/support/src/traits/storage.rs +++ b/substrate/frame/support/src/traits/storage.rs @@ -170,13 +170,20 @@ pub struct Footprint { } impl Footprint { + /// Construct a footprint directly from `items` and `len`. pub fn from_parts(items: usize, len: usize) -> Self { Self { count: items as u64, size: len as u64 } } + /// Construct a footprint with one item, and size equal to the encoded size of `e`. pub fn from_encodable(e: impl Encode) -> Self { Self::from_parts(1, e.encoded_size()) } + + /// Construct a footprint with one item, and size equal to the max encoded length of `E`. + pub fn from_mel() -> Self { + Self::from_parts(1, E::max_encoded_len()) + } } /// A storage price that increases linearly with the number of elements and their size. @@ -288,7 +295,8 @@ impl_incrementable!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); #[cfg(test)] mod tests { use super::*; - use sp_core::ConstU64; + use crate::BoundedVec; + use sp_core::{ConstU32, ConstU64}; #[test] fn linear_storage_price_works() { @@ -305,4 +313,17 @@ mod tests { assert_eq!(p(u64::MAX, u64::MAX), u64::MAX); } + + #[test] + fn footprint_from_mel_works() { + let footprint = Footprint::from_mel::<(u8, BoundedVec>)>(); + let expected_size = BoundedVec::>::max_encoded_len() as u64; + assert_eq!(expected_size, 10); + assert_eq!(footprint, Footprint { count: 1, size: expected_size + 1 }); + + let footprint = Footprint::from_mel::<(u8, BoundedVec>)>(); + let expected_size = BoundedVec::>::max_encoded_len() as u64; + assert_eq!(expected_size, 1001); + assert_eq!(footprint, Footprint { count: 1, size: expected_size + 1 }); + } } From 0d7d2177807ec6b3094f4491a45b0bc0d74d3c8b Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 26 Jul 2024 11:10:49 +0200 Subject: [PATCH 020/382] CI: Prevent breaking backports (#4812) - Prevent `major` changes to be merged into a `stable` branch. - Place a comment on backport MRs to provide context of what it means. Comment looks like this: ![Screenshot 2024-07-24 at 17 36 35](https://github.com/user-attachments/assets/6393549b-7b15-41e5-a804-8581c625ceff) --------- Signed-off-by: Oliver Tale-Yazdi --- .github/workflows/check-semver.yml | 63 +++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/.github/workflows/check-semver.yml b/.github/workflows/check-semver.yml index f65be00924de..f14533c2e2c9 100644 --- a/.github/workflows/check-semver.yml +++ b/.github/workflows/check-semver.yml @@ -5,9 +5,16 @@ on: types: [opened, synchronize, reopened, ready_for_review] paths: - prdoc/*.prdoc + workflow_dispatch: + +concurrency: + group: check-semver-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + env: TOOLCHAIN: nightly-2024-06-01 + jobs: check-semver: runs-on: ubuntu-latest @@ -16,32 +23,67 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: extra git setup + run: | + git config --global --add safe.directory '*' + git fetch --no-tags --no-recurse-submodules --depth=1 origin master + git branch old origin/master + + - name: Comment If Backport + if: ${{ startsWith(github.event.pull_request.base.ref, 'stable') }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR: ${{ github.event.pull_request.number }} + run: | + echo "This is a backport into stable." + + wget -q https://github.com/cli/cli/releases/download/v2.51.0/gh_2.51.0_linux_amd64.tar.gz -O gh.tar.gz && \ + tar -xzf gh.tar.gz && mv gh_2.51.0_linux_amd64/bin/gh /usr/local/bin/gh && rm gh.tar.gz + chmod +x /usr/local/bin/gh + + cat > msg.txt <Emergency Bypass +

+ + If you really need to bypass this check: add validate: false to each crate + in the Prdoc where a breaking change is introduced. This will release a new major + version of that crate and all its reverse dependencies and basically break the release. + +

+ + EOF + gh issue comment $PR --edit-last -F msg.txt || gh issue comment $PR -F msg.txt + + echo "PRDOC_EXTRA_ARGS=--max-bump minor" >> $GITHUB_ENV + - name: Rust Cache uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 with: cache-on-failure: true - - name: install parity-publish - run: cargo install parity-publish@0.7.0 - - name: Rust compilation prerequisites run: | rustup default $TOOLCHAIN - rustup target add wasm32-unknown-unknown --toolchain $TOOLCHAIN rustup component add rust-src --toolchain $TOOLCHAIN - - name: extra git setup - run: | - git config --global --add safe.directory '*' - git fetch --no-tags --no-recurse-submodules --depth=1 origin master - git branch old origin/master + - name: install parity-publish + # Set the target dir to cache the build. + run: CARGO_TARGET_DIR=./target/ cargo install parity-publish -q - name: check semver run: | export CARGO_TARGET_DIR=target export RUSTFLAGS='-A warnings -A missing_docs' export SKIP_WASM_BUILD=1 - if ! parity-publish --color always prdoc --since old --validate prdoc/pr_$PR.prdoc -v --toolchain $TOOLCHAIN; then + + if ! parity-publish --color always prdoc --since old --validate prdoc/pr_$PR.prdoc $PRDOC_EXTRA_ARGS -v --toolchain $TOOLCHAIN; then + cat < Date: Fri, 26 Jul 2024 18:24:22 +0800 Subject: [PATCH 021/382] Upgrade time crate, fix compilation on Rust 1.80 (#5149) This will fix the compilation on the latest Rust 1.80.0 PS. There are tons of new warnings about feature gates and annotations, it would be nice you guys to investigate them --- Cargo.lock | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03290d630b51..6de8fcfb1afd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4815,9 +4815,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] [[package]] name = "derivative" @@ -9448,6 +9451,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.4.2" @@ -15223,6 +15232,12 @@ dependencies = [ "rand", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "pprof" version = "0.12.1" @@ -22080,14 +22095,16 @@ dependencies = [ [[package]] name = "time" -version = "0.3.27" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb39ee79a6d8de55f48f2293a830e040392f1c5f16e336bdd1788cd0aadce07" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", + "powerfmt", "serde", "time-core", "time-macros", @@ -22095,16 +22112,17 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.13" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733d258752e9303d392b94b75230d07b0b9c489350c69b851fc6c065fde3e8f9" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] From 200632144624d6a73fed400219761da58379cb3c Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Fri, 26 Jul 2024 13:47:02 +0200 Subject: [PATCH 022/382] Umbrella crate: Add polkadot-sdk-frame/?runtime (#5151) This should make it possible to use the umbrella crate alone for templates/*/runtime crate of the repo --- .config/zepter.yaml | 2 +- scripts/generate-umbrella.py | 4 +- umbrella/Cargo.toml | 242 ++++++++++++++++++++++++++++++++++- 3 files changed, 244 insertions(+), 4 deletions(-) diff --git a/.config/zepter.yaml b/.config/zepter.yaml index 9b3bd9d618c1..24441e90b1a0 100644 --- a/.config/zepter.yaml +++ b/.config/zepter.yaml @@ -27,7 +27,7 @@ workflows: ] # The umbrella crate uses more features, so we to check those too: check_umbrella: - - [ $check.0, '--features=serde,experimental,with-tracing,tuples-96,with-tracing', '-p=polkadot-sdk' ] + - [ $check.0, '--features=serde,experimental,runtime,with-tracing,tuples-96,with-tracing', '-p=polkadot-sdk' ] # Same as `check_*`, but with the `--fix` flag. default: - [ $check.0, '--fix' ] diff --git a/scripts/generate-umbrella.py b/scripts/generate-umbrella.py index 0bdf160e63b1..a5f7d3b92567 100644 --- a/scripts/generate-umbrella.py +++ b/scripts/generate-umbrella.py @@ -5,10 +5,10 @@ This re-creates the `umbrella/` folder. Ensure that it does not contain any changes you want to keep. Usage: - python3 polkadot-sdk-umbrella-crate.py --sdk --version + python3 generate-umbrella.py --sdk --version Example: - python3 polkadot-sdk-umbrella-crate.py --sdk ../polkadot-sdk --version 1.11.0 + python3 generate-umbrella.py --sdk .. --version 1.11.0 """ import argparse diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 79e7c0c38eda..8d85e26d8fe7 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -540,7 +540,247 @@ with-tracing = [ "sp-tracing?/with-tracing", "sp-tracing?/with-tracing", ] -runtime = ["assets-common", "binary-merkle-tree", "bp-asset-hub-rococo", "bp-asset-hub-westend", "bp-bridge-hub-cumulus", "bp-bridge-hub-kusama", "bp-bridge-hub-polkadot", "bp-bridge-hub-rococo", "bp-bridge-hub-westend", "bp-header-chain", "bp-kusama", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-bulletin", "bp-polkadot-core", "bp-relayers", "bp-rococo", "bp-runtime", "bp-test-utils", "bp-westend", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "rococo-runtime-constants", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "westend-runtime-constants", "xcm-procedural", "xcm-runtime-apis"] +runtime = [ + "assets-common", + "binary-merkle-tree", + "bp-asset-hub-rococo", + "bp-asset-hub-westend", + "bp-bridge-hub-cumulus", + "bp-bridge-hub-kusama", + "bp-bridge-hub-polkadot", + "bp-bridge-hub-rococo", + "bp-bridge-hub-westend", + "bp-header-chain", + "bp-kusama", + "bp-messages", + "bp-parachains", + "bp-polkadot", + "bp-polkadot-bulletin", + "bp-polkadot-core", + "bp-relayers", + "bp-rococo", + "bp-runtime", + "bp-test-utils", + "bp-westend", + "bp-xcm-bridge-hub", + "bp-xcm-bridge-hub-router", + "bridge-hub-common", + "bridge-runtime-common", + "cumulus-pallet-aura-ext", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-parachain-system-proc-macro", + "cumulus-pallet-session-benchmarking", + "cumulus-pallet-solo-to-para", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-ping", + "cumulus-primitives-aura", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-primitives-proof-size-hostfunction", + "cumulus-primitives-storage-weight-reclaim", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "frame-benchmarking", + "frame-benchmarking-pallet-pov", + "frame-election-provider-solution-type", + "frame-election-provider-support", + "frame-executive", + "frame-metadata-hash-extension", + "frame-support", + "frame-support-procedural", + "frame-support-procedural-tools-derive", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "pallet-alliance", + "pallet-asset-conversion", + "pallet-asset-conversion-ops", + "pallet-asset-conversion-tx-payment", + "pallet-asset-rate", + "pallet-asset-tx-payment", + "pallet-assets", + "pallet-assets-freezer", + "pallet-atomic-swap", + "pallet-aura", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-bags-list", + "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", + "pallet-bounties", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-bridge-relayers", + "pallet-broker", + "pallet-child-bounties", + "pallet-collator-selection", + "pallet-collective", + "pallet-collective-content", + "pallet-contracts", + "pallet-contracts-proc-macro", + "pallet-contracts-uapi", + "pallet-conviction-voting", + "pallet-core-fellowship", + "pallet-delegated-staking", + "pallet-democracy", + "pallet-dev-mode", + "pallet-election-provider-multi-phase", + "pallet-election-provider-support-benchmarking", + "pallet-elections-phragmen", + "pallet-fast-unstake", + "pallet-glutton", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-insecure-randomness-collective-flip", + "pallet-lottery", + "pallet-membership", + "pallet-message-queue", + "pallet-migrations", + "pallet-mixnet", + "pallet-mmr", + "pallet-multisig", + "pallet-nft-fractionalization", + "pallet-nfts", + "pallet-nfts-runtime-api", + "pallet-nis", + "pallet-node-authorization", + "pallet-nomination-pools", + "pallet-nomination-pools-benchmarking", + "pallet-nomination-pools-runtime-api", + "pallet-offences", + "pallet-offences-benchmarking", + "pallet-paged-list", + "pallet-parameters", + "pallet-preimage", + "pallet-proxy", + "pallet-ranked-collective", + "pallet-recovery", + "pallet-referenda", + "pallet-remark", + "pallet-root-offences", + "pallet-root-testing", + "pallet-safe-mode", + "pallet-salary", + "pallet-scheduler", + "pallet-scored-pool", + "pallet-session", + "pallet-session-benchmarking", + "pallet-skip-feeless-payment", + "pallet-society", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-staking-reward-fn", + "pallet-staking-runtime-api", + "pallet-state-trie-migration", + "pallet-statement", + "pallet-sudo", + "pallet-timestamp", + "pallet-tips", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-transaction-storage", + "pallet-treasury", + "pallet-tx-pause", + "pallet-uniques", + "pallet-utility", + "pallet-vesting", + "pallet-whitelist", + "pallet-xcm", + "pallet-xcm-benchmarks", + "pallet-xcm-bridge-hub", + "pallet-xcm-bridge-hub-router", + "parachains-common", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-metrics", + "polkadot-runtime-parachains", + "polkadot-sdk-frame", + "polkadot-sdk-frame?/runtime", + "rococo-runtime-constants", + "sc-chain-spec-derive", + "sc-tracing-proc-macro", + "slot-range-helper", + "snowbridge-beacon-primitives", + "snowbridge-core", + "snowbridge-ethereum", + "snowbridge-outbound-queue-merkle-tree", + "snowbridge-outbound-queue-runtime-api", + "snowbridge-pallet-ethereum-client", + "snowbridge-pallet-ethereum-client-fixtures", + "snowbridge-pallet-inbound-queue", + "snowbridge-pallet-inbound-queue-fixtures", + "snowbridge-pallet-outbound-queue", + "snowbridge-pallet-system", + "snowbridge-router-primitives", + "snowbridge-runtime-common", + "snowbridge-system-runtime-api", + "sp-api", + "sp-api-proc-macro", + "sp-application-crypto", + "sp-arithmetic", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-consensus-pow", + "sp-consensus-slots", + "sp-core", + "sp-crypto-ec-utils", + "sp-crypto-hashing", + "sp-crypto-hashing-proc-macro", + "sp-debug-derive", + "sp-externalities", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-keystore", + "sp-metadata-ir", + "sp-mixnet", + "sp-mmr-primitives", + "sp-npos-elections", + "sp-offchain", + "sp-runtime", + "sp-runtime-interface", + "sp-runtime-interface-proc-macro", + "sp-session", + "sp-staking", + "sp-state-machine", + "sp-statement-store", + "sp-std", + "sp-storage", + "sp-timestamp", + "sp-tracing", + "sp-transaction-pool", + "sp-transaction-storage-proof", + "sp-trie", + "sp-version", + "sp-version-proc-macro", + "sp-wasm-interface", + "sp-weights", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "substrate-bip39", + "testnet-parachains-constants", + "tracing-gum-proc-macro", + "westend-runtime-constants", + "xcm-procedural", + "xcm-runtime-apis", +] node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] tuples-96 = [ "frame-support-procedural?/tuples-96", From 7eb946c7ba391e9b621f3e140d9b4043718b3c2d Mon Sep 17 00:00:00 2001 From: Joseph Zhao <65984904+programskillforverification@users.noreply.github.com> Date: Fri, 26 Jul 2024 20:58:05 +0800 Subject: [PATCH 023/382] Backport xcmpayment-fee tests from polkadot-fellows repo (#5100) # Description This is continue of the work to backport `emulated-integration-tests-common`, if you want to understand the full context start with reading [#4930](https://github.com/paritytech/polkadot-sdk/pull/4930) --- Cargo.lock | 103 +++---- .../emulated/common/src/xcm_helpers.rs | 8 + .../tests/assets/asset-hub-rococo/Cargo.toml | 1 + .../tests/assets/asset-hub-rococo/src/lib.rs | 4 +- .../assets/asset-hub-rococo/src/tests/mod.rs | 1 + .../src/tests/xcm_fee_estimation.rs | 286 +++++++++++++++++ .../tests/assets/asset-hub-westend/src/lib.rs | 4 +- .../src/tests/xcm_fee_estimation.rs | 287 ++++++++---------- 8 files changed, 489 insertions(+), 205 deletions(-) create mode 100644 cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs diff --git a/Cargo.lock b/Cargo.lock index 6de8fcfb1afd..05386203452c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -824,6 +824,7 @@ dependencies = [ "sp-runtime", "staging-xcm", "staging-xcm-executor", + "xcm-runtime-apis", ] [[package]] @@ -2507,9 +2508,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.1.6" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" +checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" dependencies = [ "jobserver", "libc", @@ -2751,12 +2752,12 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.10" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6b81fb3c84f5563d509c59b5a48d935f689e993afa90fe39047f05adef9142" +checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" dependencies = [ "clap_builder", - "clap_derive 4.5.8", + "clap_derive 4.5.11", ] [[package]] @@ -2770,9 +2771,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.10" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca6706fd5224857d9ac5eb9355f6683563cc0541c7cd9d014043b57cbec78ac" +checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" dependencies = [ "anstream", "anstyle", @@ -2787,7 +2788,7 @@ version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "586a385f7ef2f8b4d86bddaa0c094794e7ccbfe5ffef1f434fe928143fc783a5" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", ] [[package]] @@ -2805,9 +2806,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck 0.5.0", "proc-macro2 1.0.82", @@ -3613,7 +3614,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.10", + "clap 4.5.11", "criterion-plot", "futures", "is-terminal", @@ -3747,7 +3748,7 @@ dependencies = [ name = "cumulus-client-cli" version = "0.7.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "parity-scale-codec", "sc-chain-spec", "sc-cli", @@ -4210,7 +4211,7 @@ name = "cumulus-pov-validator" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.10", + "clap 4.5.11", "parity-scale-codec", "polkadot-node-primitives", "polkadot-parachain-primitives", @@ -4537,7 +4538,7 @@ name = "cumulus-test-service" version = "0.1.0" dependencies = [ "async-trait", - "clap 4.5.10", + "clap 4.5.11", "criterion", "cumulus-client-cli", "cumulus-client-collator", @@ -5802,7 +5803,7 @@ dependencies = [ "Inflector", "array-bytes", "chrono", - "clap 4.5.10", + "clap 4.5.11", "comfy-table", "frame-benchmarking", "frame-support", @@ -5894,7 +5895,7 @@ dependencies = [ name = "frame-election-solution-type-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-support", @@ -5966,7 +5967,7 @@ dependencies = [ name = "frame-omni-bencher" version = "0.1.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "cumulus-primitives-proof-size-hostfunction", "env_logger 0.11.3", "frame-benchmarking-cli", @@ -7363,9 +7364,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] @@ -8728,7 +8729,7 @@ dependencies = [ name = "minimal-template-node" version = "0.0.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "docify", "futures", "futures-timer", @@ -9221,7 +9222,7 @@ name = "node-bench" version = "0.9.0-dev" dependencies = [ "array-bytes", - "clap 4.5.10", + "clap 4.5.11", "derive_more", "fs_extra", "futures", @@ -9300,7 +9301,7 @@ dependencies = [ name = "node-runtime-generate-bags" version = "3.0.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "generate-bags", "kitchensink-runtime", ] @@ -9309,7 +9310,7 @@ dependencies = [ name = "node-template-release" version = "3.0.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "flate2", "fs_extra", "glob", @@ -9611,9 +9612,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -9652,9 +9653,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -12031,7 +12032,7 @@ dependencies = [ name = "parachain-template-node" version = "0.0.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "color-print", "cumulus-client-cli", "cumulus-client-collator", @@ -12991,7 +12992,7 @@ name = "polkadot-cli" version = "7.0.0" dependencies = [ "cfg-if", - "clap 4.5.10", + "clap 4.5.11", "frame-benchmarking-cli", "futures", "log", @@ -13851,7 +13852,7 @@ dependencies = [ "async-trait", "bridge-hub-rococo-runtime", "bridge-hub-westend-runtime", - "clap 4.5.10", + "clap 4.5.11", "collectives-westend-runtime", "color-print", "contracts-rococo-runtime", @@ -14835,7 +14836,7 @@ dependencies = [ "async-trait", "bincode", "bitvec", - "clap 4.5.10", + "clap 4.5.11", "clap-num", "color-eyre", "colored", @@ -14933,7 +14934,7 @@ version = "1.0.0" dependencies = [ "assert_matches", "async-trait", - "clap 4.5.10", + "clap 4.5.11", "color-eyre", "futures", "futures-timer", @@ -15075,7 +15076,7 @@ dependencies = [ name = "polkadot-voter-bags" version = "7.0.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "generate-bags", "sp-io", "westend-runtime", @@ -16230,7 +16231,7 @@ dependencies = [ name = "remote-ext-tests-bags-list" version = "1.0.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "frame-system", "log", "pallet-bags-list-remote-tests", @@ -17080,7 +17081,7 @@ name = "sc-chain-spec" version = "28.0.0" dependencies = [ "array-bytes", - "clap 4.5.10", + "clap 4.5.11", "docify", "log", "memmap2 0.9.3", @@ -17123,7 +17124,7 @@ version = "0.36.0" dependencies = [ "array-bytes", "chrono", - "clap 4.5.10", + "clap 4.5.11", "fdlimit", "futures", "futures-timer", @@ -18307,7 +18308,7 @@ dependencies = [ name = "sc-storage-monitor" version = "0.16.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "fs4", "log", "sp-core", @@ -18681,18 +18682,18 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.28.0" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" dependencies = [ "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e67c467c38fd24bd5499dc9a18183b31575c12ee549197e3e20d57aa4fe3b7" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" dependencies = [ "cc", ] @@ -19191,9 +19192,9 @@ dependencies = [ [[package]] name = "slotmap" -version = "1.0.7" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" dependencies = [ "version_check", ] @@ -19729,7 +19730,7 @@ dependencies = [ name = "solochain-template-node" version = "0.0.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "frame-benchmarking-cli", "frame-metadata-hash-extension", "frame-system", @@ -20405,7 +20406,7 @@ dependencies = [ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "honggfuzz", "rand", "sp-npos-elections", @@ -20918,7 +20919,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" name = "staging-chain-spec-builder" version = "1.6.1" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "log", "sc-chain-spec", "serde_json", @@ -20931,7 +20932,7 @@ version = "3.0.0-dev" dependencies = [ "array-bytes", "assert_cmd", - "clap 4.5.10", + "clap 4.5.11", "clap_complete", "criterion", "futures", @@ -20966,7 +20967,7 @@ dependencies = [ name = "staging-node-inspect" version = "0.12.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -21239,7 +21240,7 @@ dependencies = [ name = "subkey" version = "9.0.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "sc-cli", ] @@ -21865,7 +21866,7 @@ dependencies = [ name = "test-parachain-adder-collator" version = "1.0.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "futures", "futures-timer", "log", @@ -21912,7 +21913,7 @@ dependencies = [ name = "test-parachain-undying-collator" version = "1.0.0" dependencies = [ - "clap 4.5.10", + "clap 4.5.11", "futures", "futures-timer", "log", diff --git a/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs b/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs index 25e1cffad543..76179c6d82c6 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs @@ -69,3 +69,11 @@ pub fn non_fee_asset(assets: &Assets, fee_idx: usize) -> Option<(Location, u128) }; Some((asset.id.0, asset_amount)) } + +pub fn get_amount_from_versioned_assets(assets: VersionedAssets) -> u128 { + let latest_assets: Assets = assets.try_into().unwrap(); + let Fungible(amount) = latest_assets.inner()[0].fun else { + unreachable!("asset is non-fungible"); + }; + amount +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml index b4579da94cbf..f66a5f1d5fe7 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml @@ -28,6 +28,7 @@ pallet-utility = { workspace = true } xcm = { workspace = true } pallet-xcm = { workspace = true } xcm-executor = { workspace = true } +xcm-runtime-apis = { workspace = true, default-features = true } polkadot-runtime-common = { workspace = true, default-features = true } rococo-runtime-constants = { workspace = true, default-features = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs index eca358317054..6309c0584107 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs @@ -40,7 +40,9 @@ mod imports { assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, TestContext, TestExt, }, - xcm_helpers::{non_fee_asset, xcm_transact_paid_execution}, + xcm_helpers::{ + get_amount_from_versioned_assets, non_fee_asset, xcm_transact_paid_execution, + }, ASSETS_PALLET_ID, RESERVABLE_ASSET_ID, XCM_V3, }; pub use parachains_common::Balance; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs index 8fffec23d73a..88fa379c4072 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs @@ -21,3 +21,4 @@ mod set_xcm_versions; mod swap; mod teleport; mod treasury; +mod xcm_fee_estimation; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs new file mode 100644 index 000000000000..aa0e183ecdda --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs @@ -0,0 +1,286 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests for XCM fee estimation in the runtime. + +use crate::imports::*; +use frame_support::{ + dispatch::RawOrigin, + sp_runtime::{traits::Dispatchable, DispatchResult}, +}; +use xcm_runtime_apis::{ + dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, +}; + +fn sender_assertions(test: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + PenpalA::assert_xcm_pallet_attempted_complete(None); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance } + ) => { + asset_id: *asset_id == Location::new(1, []), + owner: *owner == test.sender.account_id, + balance: *balance == test.args.amount, + }, + ] + ); +} + +fn hop_assertions(test: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + AssetHubRococo::assert_xcmp_queue_success(None); + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::Balances( + pallet_balances::Event::Burned { amount, .. } + ) => { + amount: *amount == test.args.amount, + }, + ] + ); +} + +fn receiver_assertions(test: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + PenpalB::assert_xcmp_queue_success(None); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Issued { asset_id, owner, .. } + ) => { + asset_id: *asset_id == Location::new(1, []), + owner: *owner == test.receiver.account_id, + }, + ] + ); +} + +fn transfer_assets_para_to_para_through_ah_dispatchable( + test: ParaToParaThroughAHTest, +) -> DispatchResult { + let call = transfer_assets_para_to_para_through_ah_call(test.clone()); + match call.dispatch(test.signed_origin) { + Ok(_) => Ok(()), + Err(error_with_post_info) => Err(error_with_post_info.error), + } +} + +fn transfer_assets_para_to_para_through_ah_call( + test: ParaToParaThroughAHTest, +) -> ::RuntimeCall { + type RuntimeCall = ::RuntimeCall; + + let asset_hub_location: Location = PenpalB::sibling_location_of(AssetHubRococo::para_id()); + let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset { + assets: Wild(AllCounted(test.args.assets.len() as u32)), + beneficiary: test.args.beneficiary, + }]); + RuntimeCall::PolkadotXcm(pallet_xcm::Call::transfer_assets_using_type_and_then { + dest: bx!(test.args.dest.into()), + assets: bx!(test.args.assets.clone().into()), + assets_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())), + remote_fees_id: bx!(VersionedAssetId::V4(AssetId(Location::new(1, [])))), + fees_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.into())), + custom_xcm_on_dest: bx!(VersionedXcm::from(custom_xcm_on_dest)), + weight_limit: test.args.weight_limit, + }) +} + +/// We are able to dry-run and estimate the fees for a multi-hop XCM journey. +/// Scenario: Alice on PenpalA has some DOTs and wants to send them to PenpalB. +/// We want to know the fees using the `DryRunApi` and `XcmPaymentApi`. +#[test] +fn multi_hop_works() { + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let amount_to_send = 1_000_000_000_000; + let asset_owner = PenpalAssetOwner::get(); + let assets: Assets = (Parent, amount_to_send).into(); + let relay_native_asset_location = Location::parent(); + let sender_as_seen_by_ah = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let sov_of_sender_on_ah = AssetHubRococo::sovereign_account_id_of(sender_as_seen_by_ah.clone()); + + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner.clone()), + relay_native_asset_location.clone(), + sender.clone(), + amount_to_send * 2, + ); + + // fund the Parachain Origin's SA on AssetHub with the native tokens held in reserve. + AssetHubRococo::fund_accounts(vec![(sov_of_sender_on_ah.clone(), amount_to_send * 2)]); + + // Init values for Parachain Destination + let beneficiary_id = PenpalBReceiver::get(); + + let test_args = TestContext { + sender: PenpalASender::get(), // Bob in PenpalB. + receiver: PenpalBReceiver::get(), // Alice. + args: TestArgs::new_para( + destination, + beneficiary_id.clone(), + amount_to_send, + assets, + None, + 0, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); + + // We get them from the PenpalA closure. + let mut delivery_fees_amount = 0; + let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); + ::execute_with(|| { + type Runtime = ::Runtime; + type OriginCaller = ::OriginCaller; + + let call = transfer_assets_para_to_para_through_ah_call(test.clone()); + let origin = OriginCaller::system(RawOrigin::Signed(sender.clone())); + let result = Runtime::dry_run_call(origin, call).unwrap(); + // We filter the result to get only the messages we are interested in. + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::V4(Location::new(1, [Parachain(1000)])) + }) + .unwrap(); + assert_eq!(messages_to_query.len(), 1); + remote_message = messages_to_query[0].clone(); + let delivery_fees = + Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) + .unwrap(); + delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); + }); + + // These are set in the AssetHub closure. + let mut intermediate_execution_fees = 0; + let mut intermediate_delivery_fees_amount = 0; + let mut intermediate_remote_message = VersionedXcm::V4(Xcm::<()>(Vec::new())); + ::execute_with(|| { + type Runtime = ::Runtime; + type RuntimeCall = ::RuntimeCall; + + // First we get the execution fees. + let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); + intermediate_execution_fees = Runtime::query_weight_to_asset_fee( + weight, + VersionedAssetId::V4(Location::new(1, []).into()), + ) + .unwrap(); + + // We have to do this to turn `VersionedXcm<()>` into `VersionedXcm`. + let xcm_program = + VersionedXcm::V4(Xcm::::from(remote_message.clone().try_into().unwrap())); + + // Now we get the delivery fees to the final destination. + let result = + Runtime::dry_run_xcm(sender_as_seen_by_ah.clone().into(), xcm_program).unwrap(); + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::V4(Location::new(1, [Parachain(2001)])) + }) + .unwrap(); + // There's actually two messages here. + // One created when the message we sent from PenpalA arrived and was executed. + // The second one when we dry-run the xcm. + // We could've gotten the message from the queue without having to dry-run, but + // offchain applications would have to dry-run, so we do it here as well. + intermediate_remote_message = messages_to_query[0].clone(); + let delivery_fees = Runtime::query_delivery_fees( + destination_to_query.clone(), + intermediate_remote_message.clone(), + ) + .unwrap(); + intermediate_delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); + }); + + // Get the final execution fees in the destination. + let mut final_execution_fees = 0; + ::execute_with(|| { + type Runtime = ::Runtime; + + let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap(); + final_execution_fees = + Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())) + .unwrap(); + }); + + // Dry-running is done. + PenpalA::reset_ext(); + AssetHubRococo::reset_ext(); + PenpalB::reset_ext(); + + // Fund accounts again. + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner), + relay_native_asset_location.clone(), + sender.clone(), + amount_to_send * 2, + ); + AssetHubRococo::fund_accounts(vec![(sov_of_sender_on_ah, amount_to_send * 2)]); + + // Actually run the extrinsic. + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &sender) + }); + let receiver_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &beneficiary_id) + }); + + test.set_assertion::(sender_assertions); + test.set_assertion::(hop_assertions); + test.set_assertion::(receiver_assertions); + test.set_dispatchable::(transfer_assets_para_to_para_through_ah_dispatchable); + test.assert(); + + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &sender) + }); + let receiver_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &beneficiary_id) + }); + + // We know the exact fees on every hop. + assert_eq!( + sender_assets_after, + sender_assets_before - amount_to_send - delivery_fees_amount /* This is charged directly + * from the sender's + * account. */ + ); + assert_eq!( + receiver_assets_after, + receiver_assets_before + amount_to_send - + intermediate_execution_fees - + intermediate_delivery_fees_amount - + final_execution_fees + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs index 9401621c5ba3..060c3fb39254 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs @@ -40,7 +40,9 @@ mod imports { assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, TestContext, TestExt, }, - xcm_helpers::{non_fee_asset, xcm_transact_paid_execution}, + xcm_helpers::{ + get_amount_from_versioned_assets, non_fee_asset, xcm_transact_paid_execution, + }, ASSETS_PALLET_ID, RESERVABLE_ASSET_ID, XCM_V3, }; pub use parachains_common::{AccountId, Balance}; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index c01aa7825336..037d6604ea4d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -17,89 +17,95 @@ use crate::imports::*; -use frame_system::RawOrigin; +use frame_support::{ + dispatch::RawOrigin, + sp_runtime::{traits::Dispatchable, DispatchResult}, +}; use xcm_runtime_apis::{ dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, }; -/// We are able to dry-run and estimate the fees for a teleport between relay and system para. -/// Scenario: Alice on Westend relay chain wants to teleport WND to Asset Hub. -/// We want to know the fees using the `DryRunApi` and `XcmPaymentApi`. -#[test] -fn teleport_relay_system_para_works() { - let destination: Location = Parachain(1000).into(); // Asset Hub. - let beneficiary_id = AssetHubWestendReceiver::get(); - let beneficiary: Location = AccountId32 { id: beneficiary_id.clone().into(), network: None } // Test doesn't allow specifying a network here. - .into(); // Beneficiary in Asset Hub. - let teleport_amount = 1_000_000_000_000; // One WND (12 decimals). - let assets: Assets = vec![(Here, teleport_amount).into()].into(); - - // We get them from the Westend closure. - let mut delivery_fees_amount = 0; - let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); - ::new_ext().execute_with(|| { - type Runtime = ::Runtime; - type RuntimeCall = ::RuntimeCall; - type OriginCaller = ::OriginCaller; - - let call = RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { - dest: Box::new(VersionedLocation::V4(destination.clone())), - beneficiary: Box::new(VersionedLocation::V4(beneficiary)), - assets: Box::new(VersionedAssets::V4(assets)), - fee_asset_item: 0, - weight_limit: Unlimited, - }); - let origin = OriginCaller::system(RawOrigin::Signed(WestendSender::get())); - let result = Runtime::dry_run_call(origin, call).unwrap(); - assert_eq!(result.forwarded_xcms.len(), 1); - let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0]; - assert_eq!(messages_to_query.len(), 1); - remote_message = messages_to_query[0].clone(); - let delivery_fees = - Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) - .unwrap(); - delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); - }); - - // This is set in the AssetHubWestend closure. - let mut remote_execution_fees = 0; - ::execute_with(|| { - type Runtime = ::Runtime; - - let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); - remote_execution_fees = - Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())) - .unwrap(); - }); - - let test_args = TestContext { - sender: WestendSender::get(), // Alice. - receiver: AssetHubWestendReceiver::get(), // Bob in Asset Hub. - args: TestArgs::new_relay(destination, beneficiary_id, teleport_amount), - }; - let mut test = RelayToSystemParaTest::new(test_args); +fn sender_assertions(test: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + PenpalA::assert_xcm_pallet_attempted_complete(None); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance } + ) => { + asset_id: *asset_id == Location::new(1, []), + owner: *owner == test.sender.account_id, + balance: *balance == test.args.amount, + }, + ] + ); +} - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - assert_eq!(sender_balance_before, 1_000_000_000_000_000_000); - assert_eq!(receiver_balance_before, 4_096_000_000_000); +fn hop_assertions(test: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + AssetHubWestend::assert_xcmp_queue_success(None); + + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::Balances( + pallet_balances::Event::Burned { amount, .. } + ) => { + amount: *amount == test.args.amount, + }, + ] + ); +} - test.set_dispatchable::(transfer_assets); - test.assert(); +fn receiver_assertions(test: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + PenpalB::assert_xcmp_queue_success(None); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Issued { asset_id, owner, .. } + ) => { + asset_id: *asset_id == Location::new(1, []), + owner: *owner == test.receiver.account_id, + }, + ] + ); +} - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; +fn transfer_assets_para_to_para_through_ah_dispatchable( + test: ParaToParaThroughAHTest, +) -> DispatchResult { + let call = transfer_assets_para_to_para_through_ah_call(test.clone()); + match call.dispatch(test.signed_origin) { + Ok(_) => Ok(()), + Err(error_with_post_info) => Err(error_with_post_info.error), + } +} - // We now know the exact fees. - assert_eq!( - sender_balance_after, - sender_balance_before - delivery_fees_amount - teleport_amount - ); - assert_eq!( - receiver_balance_after, - receiver_balance_before + teleport_amount - remote_execution_fees - ); +fn transfer_assets_para_to_para_through_ah_call( + test: ParaToParaThroughAHTest, +) -> ::RuntimeCall { + type RuntimeCall = ::RuntimeCall; + + let asset_hub_location: Location = PenpalB::sibling_location_of(AssetHubWestend::para_id()); + let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset { + assets: Wild(AllCounted(test.args.assets.len() as u32)), + beneficiary: test.args.beneficiary, + }]); + RuntimeCall::PolkadotXcm(pallet_xcm::Call::transfer_assets_using_type_and_then { + dest: bx!(test.args.dest.into()), + assets: bx!(test.args.assets.clone().into()), + assets_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())), + remote_fees_id: bx!(VersionedAssetId::V4(AssetId(Location::new(1, [])))), + fees_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.into())), + custom_xcm_on_dest: bx!(VersionedXcm::from(custom_xcm_on_dest)), + weight_limit: test.args.weight_limit, + }) } /// We are able to dry-run and estimate the fees for a multi-hop XCM journey. @@ -109,12 +115,13 @@ fn teleport_relay_system_para_works() { fn multi_hop_works() { let destination = PenpalA::sibling_location_of(PenpalB::para_id()); let sender = PenpalASender::get(); - let amount_to_send = 1_000_000_000_000; // One WND (12 decimals). + let amount_to_send = 1_000_000_000_000; let asset_owner = PenpalAssetOwner::get(); let assets: Assets = (Parent, amount_to_send).into(); - let relay_native_asset_location = RelayLocation::get(); - let sender_as_seen_by_relay = Westend::child_location_of(PenpalA::para_id()); - let sov_of_sender_on_relay = Westend::sovereign_account_id_of(sender_as_seen_by_relay.clone()); + let relay_native_asset_location = Location::parent(); + let sender_as_seen_by_ah = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_of_sender_on_ah = + AssetHubWestend::sovereign_account_id_of(sender_as_seen_by_ah.clone()); // fund Parachain's sender account PenpalA::mint_foreign_asset( @@ -124,36 +131,44 @@ fn multi_hop_works() { amount_to_send * 2, ); - // fund the Parachain Origin's SA on Relay Chain with the native tokens held in reserve - Westend::fund_accounts(vec![(sov_of_sender_on_relay.clone().into(), amount_to_send * 2)]); + // fund the Parachain Origin's SA on AssetHub with the native tokens held in reserve. + AssetHubWestend::fund_accounts(vec![(sov_of_sender_on_ah.clone(), amount_to_send * 2)]); // Init values for Parachain Destination let beneficiary_id = PenpalBReceiver::get(); - let beneficiary: Location = AccountId32 { - id: beneficiary_id.clone().into(), - network: None, // Test doesn't allow specifying a network here. - } - .into(); + + let test_args = TestContext { + sender: PenpalASender::get(), // Bob in PenpalB. + receiver: PenpalBReceiver::get(), // Alice. + args: TestArgs::new_para( + destination, + beneficiary_id.clone(), + amount_to_send, + assets, + None, + 0, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); // We get them from the PenpalA closure. let mut delivery_fees_amount = 0; let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); ::execute_with(|| { type Runtime = ::Runtime; - type RuntimeCall = ::RuntimeCall; type OriginCaller = ::OriginCaller; - let call = RuntimeCall::PolkadotXcm(pallet_xcm::Call::transfer_assets { - dest: Box::new(VersionedLocation::V4(destination.clone())), - beneficiary: Box::new(VersionedLocation::V4(beneficiary)), - assets: Box::new(VersionedAssets::V4(assets.clone())), - fee_asset_item: 0, - weight_limit: Unlimited, - }); - let origin = OriginCaller::system(RawOrigin::Signed(PenpalASender::get())); + let call = transfer_assets_para_to_para_through_ah_call(test.clone()); + let origin = OriginCaller::system(RawOrigin::Signed(sender.clone())); let result = Runtime::dry_run_call(origin, call).unwrap(); - assert_eq!(result.forwarded_xcms.len(), 1); - let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0]; + // We filter the result to get only the messages we are interested in. + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::V4(Location::new(1, [Parachain(1000)])) + }) + .unwrap(); assert_eq!(messages_to_query.len(), 1); remote_message = messages_to_query[0].clone(); let delivery_fees = @@ -162,18 +177,21 @@ fn multi_hop_works() { delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); }); - // This is set in the Westend closure. + // These are set in the AssetHub closure. let mut intermediate_execution_fees = 0; let mut intermediate_delivery_fees_amount = 0; let mut intermediate_remote_message = VersionedXcm::V4(Xcm::<()>(Vec::new())); - ::execute_with(|| { - type Runtime = ::Runtime; - type RuntimeCall = ::RuntimeCall; + ::execute_with(|| { + type Runtime = ::Runtime; + type RuntimeCall = ::RuntimeCall; // First we get the execution fees. let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); - intermediate_execution_fees = - Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Here.into())).unwrap(); + intermediate_execution_fees = Runtime::query_weight_to_asset_fee( + weight, + VersionedAssetId::V4(Location::new(1, []).into()), + ) + .unwrap(); // We have to do this to turn `VersionedXcm<()>` into `VersionedXcm`. let xcm_program = @@ -181,8 +199,14 @@ fn multi_hop_works() { // Now we get the delivery fees to the final destination. let result = - Runtime::dry_run_xcm(sender_as_seen_by_relay.clone().into(), xcm_program).unwrap(); - let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0]; + Runtime::dry_run_xcm(sender_as_seen_by_ah.clone().into(), xcm_program).unwrap(); + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::V4(Location::new(1, [Parachain(2001)])) + }) + .unwrap(); // There's actually two messages here. // One created when the message we sent from PenpalA arrived and was executed. // The second one when we dry-run the xcm. @@ -200,7 +224,7 @@ fn multi_hop_works() { // Get the final execution fees in the destination. let mut final_execution_fees = 0; ::execute_with(|| { - type Runtime = ::Runtime; + type Runtime = ::Runtime; let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap(); final_execution_fees = @@ -210,7 +234,7 @@ fn multi_hop_works() { // Dry-running is done. PenpalA::reset_ext(); - Westend::reset_ext(); + AssetHubWestend::reset_ext(); PenpalB::reset_ext(); // Fund accounts again. @@ -220,23 +244,9 @@ fn multi_hop_works() { sender.clone(), amount_to_send * 2, ); - Westend::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]); + AssetHubWestend::fund_accounts(vec![(sov_of_sender_on_ah, amount_to_send * 2)]); // Actually run the extrinsic. - let test_args = TestContext { - sender: PenpalASender::get(), // Alice. - receiver: PenpalBReceiver::get(), // Bob in PenpalB. - args: TestArgs::new_para( - destination, - beneficiary_id.clone(), - amount_to_send, - assets, - None, - 0, - ), - }; - let mut test = ParaToParaThroughRelayTest::new(test_args); - let sender_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; >::balance(relay_native_asset_location.clone(), &sender) @@ -246,7 +256,10 @@ fn multi_hop_works() { >::balance(relay_native_asset_location.clone(), &beneficiary_id) }); - test.set_dispatchable::(transfer_assets_para_to_para); + test.set_assertion::(sender_assertions); + test.set_assertion::(hop_assertions); + test.set_assertion::(receiver_assertions); + test.set_dispatchable::(transfer_assets_para_to_para_through_ah_dispatchable); test.assert(); let sender_assets_after = PenpalA::execute_with(|| { @@ -273,33 +286,3 @@ fn multi_hop_works() { final_execution_fees ); } - -fn get_amount_from_versioned_assets(assets: VersionedAssets) -> u128 { - let latest_assets: Assets = assets.try_into().unwrap(); - let Fungible(amount) = latest_assets.inner()[0].fun else { - unreachable!("asset is fungible"); - }; - amount -} - -fn transfer_assets(test: RelayToSystemParaTest) -> DispatchResult { - ::XcmPallet::transfer_assets( - test.signed_origin, - bx!(test.args.dest.into()), - bx!(test.args.beneficiary.into()), - bx!(test.args.assets.into()), - test.args.fee_asset_item, - test.args.weight_limit, - ) -} - -fn transfer_assets_para_to_para(test: ParaToParaThroughRelayTest) -> DispatchResult { - ::PolkadotXcm::transfer_assets( - test.signed_origin, - bx!(test.args.dest.into()), - bx!(test.args.beneficiary.into()), - bx!(test.args.assets.into()), - test.args.fee_asset_item, - test.args.weight_limit, - ) -} From fc07bdadde1dfa3345913130f5209b8267816972 Mon Sep 17 00:00:00 2001 From: Alin Dima Date: Fri, 26 Jul 2024 16:07:15 +0300 Subject: [PATCH 024/382] runtime: make the candidate relay parent progression check more strict (#5113) Previously, we were checking if the relay parent of a new candidate does not move backwards from the latest included on-chain candidate. This was fine prior to elastic scaling. We now need to also check that the relay parent progresses from the latest pending availability candidate, as well as check the progression within the candidate chain in the inherent data. Prospective-parachains is already doing this check but we should also add it in the runtime --- .../runtime/parachains/src/inclusion/mod.rs | 16 +- .../parachains/src/paras_inherent/mod.rs | 46 +- .../parachains/src/paras_inherent/tests.rs | 640 +++++++++++++++++- prdoc/pr_5113.prdoc | 14 + 4 files changed, 664 insertions(+), 52 deletions(-) create mode 100644 prdoc/pr_5113.prdoc diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index a5bb6e6cc60f..115eee975530 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -638,6 +638,8 @@ impl Pallet { for (candidate, core) in para_candidates.iter() { let candidate_hash = candidate.candidate().hash(); + // The previous context is None, as it's already checked during candidate + // sanitization. let check_ctx = CandidateCheckContext::::new(None); let relay_parent_number = check_ctx.verify_backed_candidate( &allowed_relay_parents, @@ -717,7 +719,7 @@ impl Pallet { }) } - // Get the latest backed output head data of this para. + // Get the latest backed output head data of this para (including pending availability). pub(crate) fn para_latest_head_data(para_id: &ParaId) -> Option { match PendingAvailability::::get(para_id).and_then(|pending_candidates| { pending_candidates.back().map(|x| x.commitments.head_data.clone()) @@ -727,6 +729,16 @@ impl Pallet { } } + // Get the relay parent number of the most recent candidate (including pending availability). + pub(crate) fn para_most_recent_context(para_id: &ParaId) -> Option> { + match PendingAvailability::::get(para_id) + .and_then(|pending_candidates| pending_candidates.back().map(|x| x.relay_parent_number)) + { + Some(relay_parent_number) => Some(relay_parent_number), + None => paras::MostRecentContext::::get(para_id), + } + } + fn check_backing_votes( backed_candidate: &BackedCandidate, validators: &[ValidatorId], @@ -796,7 +808,7 @@ impl Pallet { relay_parent_number: BlockNumberFor, validation_outputs: polkadot_primitives::CandidateCommitments, ) -> bool { - let prev_context = paras::MostRecentContext::::get(para_id); + let prev_context = Self::para_most_recent_context(¶_id); let check_ctx = CandidateCheckContext::::new(prev_context); if check_ctx diff --git a/polkadot/runtime/parachains/src/paras_inherent/mod.rs b/polkadot/runtime/parachains/src/paras_inherent/mod.rs index fe4eef16f022..9d27e86ef901 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/mod.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/mod.rs @@ -1243,22 +1243,27 @@ fn filter_unchained_candidates>>, allowed_relay_parents: &AllowedRelayParentsTracker>, ) { - let mut para_latest_head_data: BTreeMap = BTreeMap::new(); + let mut para_latest_context: BTreeMap)> = BTreeMap::new(); for para_id in candidates.keys() { - let latest_head_data = match inclusion::Pallet::::para_latest_head_data(¶_id) { - None => { - defensive!("Latest included head data for paraid {:?} is None", para_id); - continue - }, - Some(latest_head_data) => latest_head_data, + let Some(latest_head_data) = inclusion::Pallet::::para_latest_head_data(¶_id) else { + defensive!("Latest included head data for paraid {:?} is None", para_id); + continue }; - para_latest_head_data.insert(*para_id, latest_head_data); + let Some(latest_relay_parent) = inclusion::Pallet::::para_most_recent_context(¶_id) + else { + defensive!("Latest relay parent for paraid {:?} is None", para_id); + continue + }; + para_latest_context.insert(*para_id, (latest_head_data, latest_relay_parent)); } let mut para_visited_candidates: BTreeMap> = BTreeMap::new(); retain_candidates::(candidates, |para_id, candidate| { - let Some(latest_head_data) = para_latest_head_data.get(¶_id) else { return false }; + let Some((latest_head_data, latest_relay_parent)) = para_latest_context.get(¶_id) + else { + return false + }; let candidate_hash = candidate.candidate().hash(); let visited_candidates = @@ -1277,15 +1282,23 @@ fn filter_unchained_candidates::get(para_id); - let check_ctx = CandidateCheckContext::::new(prev_context); + let check_ctx = CandidateCheckContext::::new(Some(*latest_relay_parent)); - let res = match check_ctx.verify_backed_candidate( + match check_ctx.verify_backed_candidate( &allowed_relay_parents, candidate.candidate(), latest_head_data.clone(), ) { - Ok(_) => true, + Ok(relay_parent_block_number) => { + para_latest_context.insert( + para_id, + ( + candidate.candidate().commitments.head_data.clone(), + relay_parent_block_number, + ), + ); + true + }, Err(err) => { log::debug!( target: LOG_TARGET, @@ -1296,14 +1309,7 @@ fn filter_unchained_candidates::force_set_most_recent_context( + RuntimeOrigin::root(), + ParaId::from(1), + BlockNumberFor::::from(0u32), + ) + .unwrap(); + paras::Pallet::::force_set_most_recent_context( + RuntimeOrigin::root(), + ParaId::from(2), + BlockNumberFor::::from(0u32), + ) + .unwrap(); // Callback used for backing candidates let group_validators = |group_index: GroupIndex| { @@ -2083,23 +2096,21 @@ mod sanitizers { ValidationCode(vec![id as u8]), ) .unwrap(); + paras::Pallet::::force_set_most_recent_context( + RuntimeOrigin::root(), + ParaId::from(id), + BlockNumberFor::::from(0u32), + ) + .unwrap(); } // Callback used for backing candidates let group_validators = |group_index: GroupIndex| { - match group_index { - group_index if group_index == GroupIndex::from(0) => Some(vec![0]), - group_index if group_index == GroupIndex::from(1) => Some(vec![1]), - group_index if group_index == GroupIndex::from(2) => Some(vec![2]), - group_index if group_index == GroupIndex::from(3) => Some(vec![3]), - group_index if group_index == GroupIndex::from(4) => Some(vec![4]), - group_index if group_index == GroupIndex::from(5) => Some(vec![5]), - group_index if group_index == GroupIndex::from(6) => Some(vec![6]), - group_index if group_index == GroupIndex::from(7) => Some(vec![7]), - - _ => panic!("Group index out of bounds"), + if group_index.0 as usize >= validators.len() { + panic!("Group index out of bounds") + } else { + Some(vec![ValidatorIndex(group_index.0)]) } - .map(|m| m.into_iter().map(ValidatorIndex).collect::>()) }; let mut backed_candidates = vec![]; @@ -2485,18 +2496,16 @@ mod sanitizers { // Para 4 scheduled on core 7 and 8. Duplicated candidates. fn get_test_data_for_order_checks(core_index_enabled: bool) -> TestData { const RELAY_PARENT_NUM: u32 = 3; + let header = default_header(); + let relay_parent = header.hash(); - // Add the relay parent to `shared` pallet. Otherwise some code (e.g. filtering backing - // votes) won't behave correctly shared::Pallet::::add_allowed_relay_parent( - default_header().hash(), + relay_parent, Default::default(), RELAY_PARENT_NUM, 1, ); - let header = default_header(); - let relay_parent = header.hash(); let session_index = SessionIndex::from(0_u32); let keystore = LocalKeystore::in_memory(); @@ -2617,24 +2626,21 @@ mod sanitizers { ValidationCode(vec![id as u8]), ) .unwrap(); + paras::Pallet::::force_set_most_recent_context( + RuntimeOrigin::root(), + ParaId::from(id), + BlockNumberFor::::from(0u32), + ) + .unwrap(); } // Callback used for backing candidates let group_validators = |group_index: GroupIndex| { - match group_index { - group_index if group_index == GroupIndex::from(0) => Some(vec![0]), - group_index if group_index == GroupIndex::from(1) => Some(vec![1]), - group_index if group_index == GroupIndex::from(2) => Some(vec![2]), - group_index if group_index == GroupIndex::from(3) => Some(vec![3]), - group_index if group_index == GroupIndex::from(4) => Some(vec![4]), - group_index if group_index == GroupIndex::from(5) => Some(vec![5]), - group_index if group_index == GroupIndex::from(6) => Some(vec![6]), - group_index if group_index == GroupIndex::from(7) => Some(vec![7]), - group_index if group_index == GroupIndex::from(8) => Some(vec![8]), - - _ => panic!("Group index out of bounds"), + if group_index.0 as usize >= validators.len() { + panic!("Group index out of bounds") + } else { + Some(vec![ValidatorIndex(group_index.0)]) } - .map(|m| m.into_iter().map(ValidatorIndex).collect::>()) }; let mut backed_candidates = vec![]; @@ -2980,6 +2986,430 @@ mod sanitizers { } } + // Para 1 scheduled on cores 0, 1 and 2. Three candidates are supplied but their relay + // parents look like this: 3, 2, 3. + // Para 2 scheduled on cores 3, 4 and 5. Three candidates are supplied and their relay + // parents look like this: 2, 3, 3. + fn get_test_data_for_relay_parent_ordering(core_index_enabled: bool) -> TestData { + const RELAY_PARENT_NUM: u32 = 3; + let header = default_header(); + let relay_parent = header.hash(); + + let prev_relay_parent = polkadot_primitives::Header { + parent_hash: Default::default(), + number: RELAY_PARENT_NUM - 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: Default::default(), + } + .hash(); + + let next_relay_parent = polkadot_primitives::Header { + parent_hash: Default::default(), + number: RELAY_PARENT_NUM + 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: Default::default(), + } + .hash(); + + // Add the relay parent to `shared` pallet. Otherwise some code (e.g. filtering backing + // votes) won't behave correctly + shared::Pallet::::add_allowed_relay_parent( + prev_relay_parent, + Default::default(), + RELAY_PARENT_NUM - 1, + 2, + ); + + shared::Pallet::::add_allowed_relay_parent( + relay_parent, + Default::default(), + RELAY_PARENT_NUM, + 2, + ); + + shared::Pallet::::add_allowed_relay_parent( + next_relay_parent, + Default::default(), + RELAY_PARENT_NUM + 1, + 2, + ); + + let session_index = SessionIndex::from(0_u32); + + let keystore = LocalKeystore::in_memory(); + let keystore = Arc::new(keystore) as KeystorePtr; + let signing_context = SigningContext { parent_hash: relay_parent, session_index }; + + let validators = vec![ + sp_keyring::Sr25519Keyring::Alice, + sp_keyring::Sr25519Keyring::Bob, + sp_keyring::Sr25519Keyring::Charlie, + sp_keyring::Sr25519Keyring::Dave, + sp_keyring::Sr25519Keyring::Eve, + sp_keyring::Sr25519Keyring::Ferdie, + ]; + for validator in validators.iter() { + Keystore::sr25519_generate_new( + &*keystore, + PARACHAIN_KEY_TYPE_ID, + Some(&validator.to_seed()), + ) + .unwrap(); + } + + // Set active validators in `shared` pallet + let validator_ids = + validators.iter().map(|v| v.public().into()).collect::>(); + shared::Pallet::::set_active_validators_ascending(validator_ids); + + // Set the validator groups in `scheduler` + scheduler::Pallet::::set_validator_groups(vec![ + vec![ValidatorIndex(0)], + vec![ValidatorIndex(1)], + vec![ValidatorIndex(2)], + vec![ValidatorIndex(3)], + vec![ValidatorIndex(4)], + vec![ValidatorIndex(5)], + ]); + + // Update scheduler's claimqueue with the parachains + scheduler::Pallet::::set_claim_queue(BTreeMap::from([ + ( + CoreIndex::from(0), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(0) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(1), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(1) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(2), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(2) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(3), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(3) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(4), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(4) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(5), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(5) }, + RELAY_PARENT_NUM, + )]), + ), + ])); + + // Set the on-chain included head data and current code hash. + for id in 1..=2u32 { + paras::Pallet::::set_current_head(ParaId::from(id), HeadData(vec![id as u8])); + paras::Pallet::::force_set_current_code( + RuntimeOrigin::root(), + ParaId::from(id), + ValidationCode(vec![id as u8]), + ) + .unwrap(); + paras::Pallet::::force_set_most_recent_context( + RuntimeOrigin::root(), + ParaId::from(id), + BlockNumberFor::::from(0u32), + ) + .unwrap(); + } + + // Callback used for backing candidates + let group_validators = |group_index: GroupIndex| { + if group_index.0 as usize >= validators.len() { + panic!("Group index out of bounds") + } else { + Some(vec![ValidatorIndex(group_index.0)]) + } + }; + + let mut backed_candidates = vec![]; + let mut expected_backed_candidates_with_core = BTreeMap::new(); + + // Para 1 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(1), + relay_parent, + pov_hash: Hash::repeat_byte(1 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(1), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + head_data: HeadData(vec![1, 1]), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![1]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let prev_candidate = candidate.clone(); + let backed: BackedCandidate = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(0 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(0 as u32)), + ); + backed_candidates.push(backed.clone()); + if core_index_enabled { + expected_backed_candidates_with_core + .entry(ParaId::from(1)) + .or_insert(vec![]) + .push((backed, CoreIndex(0))); + } + + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(1), + relay_parent: prev_relay_parent, + pov_hash: Hash::repeat_byte(1 as u8), + persisted_validation_data_hash: make_persisted_validation_data_with_parent::< + Test, + >( + RELAY_PARENT_NUM - 1, + Default::default(), + prev_candidate.commitments.head_data, + ) + .hash(), + hrmp_watermark: RELAY_PARENT_NUM - 1, + validation_code: ValidationCode(vec![1]), + head_data: HeadData(vec![1, 1, 1]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let prev_candidate = candidate.clone(); + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(1 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(1 as u32)), + ); + backed_candidates.push(backed.clone()); + + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(1), + relay_parent, + pov_hash: Hash::repeat_byte(1 as u8), + persisted_validation_data_hash: make_persisted_validation_data_with_parent::< + Test, + >( + RELAY_PARENT_NUM, + Default::default(), + prev_candidate.commitments.head_data, + ) + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![1]), + head_data: HeadData(vec![1, 1, 1, 1]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(2 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(2 as u32)), + ); + backed_candidates.push(backed.clone()); + } + + // Para 2 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(2), + relay_parent: prev_relay_parent, + pov_hash: Hash::repeat_byte(2 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(2), + RELAY_PARENT_NUM - 1, + Default::default(), + ) + .unwrap() + .hash(), + head_data: HeadData(vec![2, 2]), + hrmp_watermark: RELAY_PARENT_NUM - 1, + validation_code: ValidationCode(vec![2]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let prev_candidate = candidate.clone(); + let backed: BackedCandidate = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(3 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(3 as u32)), + ); + backed_candidates.push(backed.clone()); + if core_index_enabled { + expected_backed_candidates_with_core + .entry(ParaId::from(2)) + .or_insert(vec![]) + .push((backed, CoreIndex(3))); + } + + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(2), + relay_parent, + pov_hash: Hash::repeat_byte(2 as u8), + persisted_validation_data_hash: make_persisted_validation_data_with_parent::< + Test, + >( + RELAY_PARENT_NUM, + Default::default(), + prev_candidate.commitments.head_data, + ) + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![2]), + head_data: HeadData(vec![2, 2, 2]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let prev_candidate = candidate.clone(); + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(4 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(4 as u32)), + ); + backed_candidates.push(backed.clone()); + if core_index_enabled { + expected_backed_candidates_with_core + .entry(ParaId::from(2)) + .or_insert(vec![]) + .push((backed, CoreIndex(4))); + } + + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(2), + relay_parent, + pov_hash: Hash::repeat_byte(2 as u8), + persisted_validation_data_hash: make_persisted_validation_data_with_parent::< + Test, + >( + RELAY_PARENT_NUM, + Default::default(), + prev_candidate.commitments.head_data, + ) + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![2]), + head_data: HeadData(vec![2, 2, 2, 2]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(5 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(5 as u32)), + ); + backed_candidates.push(backed.clone()); + + if core_index_enabled { + expected_backed_candidates_with_core + .entry(ParaId::from(2)) + .or_insert(vec![]) + .push((backed, CoreIndex(5))); + } + } + + // State sanity checks + assert_eq!( + scheduler::Pallet::::scheduled_paras().collect::>(), + vec![ + (CoreIndex(0), ParaId::from(1)), + (CoreIndex(1), ParaId::from(1)), + (CoreIndex(2), ParaId::from(1)), + (CoreIndex(3), ParaId::from(2)), + (CoreIndex(4), ParaId::from(2)), + (CoreIndex(5), ParaId::from(2)), + ] + ); + let mut scheduled: BTreeMap> = BTreeMap::new(); + for (core_idx, para_id) in scheduler::Pallet::::scheduled_paras() { + scheduled.entry(para_id).or_default().insert(core_idx); + } + + assert_eq!( + shared::ActiveValidatorIndices::::get(), + vec![ + ValidatorIndex(0), + ValidatorIndex(1), + ValidatorIndex(2), + ValidatorIndex(3), + ValidatorIndex(4), + ValidatorIndex(5) + ] + ); + + TestData { + backed_candidates, + scheduled_paras: scheduled, + expected_backed_candidates_with_core, + } + } + #[rstest] #[case(false)] #[case(true)] @@ -3052,6 +3482,156 @@ mod sanitizers { }); } + #[rstest] + #[case(false)] + #[case(true)] + fn test_candidate_relay_parent_ordering(#[case] core_index_enabled: bool) { + // Para 1 scheduled on cores 0, 1 and 2. Three candidates are supplied but their relay + // parents look like this: 3, 2, 3. There are no pending availability candidates and the + // latest on-chain relay parent for this para is 0. + // Therefore, only the first candidate will get picked. + // + // Para 2 scheduled on cores 3, 4 and 5. Three candidates are supplied and their relay + // parents look like this: 2, 3, 3. There are no pending availability candidates and the + // latest on-chain relay parent for this para is 0. Therefore, all 3 will get picked. + new_test_ext(default_config()).execute_with(|| { + let TestData { + backed_candidates, + scheduled_paras: scheduled, + expected_backed_candidates_with_core, + } = get_test_data_for_relay_parent_ordering(core_index_enabled); + + assert_eq!( + sanitize_backed_candidates::( + backed_candidates.clone(), + &shared::AllowedRelayParents::::get(), + BTreeSet::new(), + scheduled, + core_index_enabled, + ), + expected_backed_candidates_with_core + ); + }); + + // Para 1 scheduled on cores 0, 1 and 2. Three candidates are supplied but their + // relay parents look like this: 3, 2, 3. There are no pending availability + // candidates but the latest on-chain relay parent for this para is 4. + // Therefore, no candidate will get picked. + // + // Para 2 scheduled on cores 3, 4 and 5. Three candidates are supplied and their relay + // parents look like this: 2, 3, 3. There are no pending availability candidates and the + // latest on-chain relay parent for this para is 2. Therefore, all 3 will get picked. + new_test_ext(default_config()).execute_with(|| { + let TestData { + backed_candidates, + scheduled_paras: scheduled, + expected_backed_candidates_with_core, + } = get_test_data_for_relay_parent_ordering(core_index_enabled); + + paras::Pallet::::force_set_most_recent_context( + RuntimeOrigin::root(), + ParaId::from(1), + BlockNumberFor::::from(4u32), + ) + .unwrap(); + + paras::Pallet::::force_set_most_recent_context( + RuntimeOrigin::root(), + ParaId::from(2), + BlockNumberFor::::from(2u32), + ) + .unwrap(); + + let res = sanitize_backed_candidates::( + backed_candidates.clone(), + &shared::AllowedRelayParents::::get(), + BTreeSet::new(), + scheduled, + core_index_enabled, + ); + + if core_index_enabled { + assert_eq!(res.len(), 1); + assert_eq!( + expected_backed_candidates_with_core.get(&ParaId::from(2)), + res.get(&ParaId::from(2)), + ); + } else { + assert!(res.is_empty()); + } + }); + + // Para 1 scheduled on cores 0, 1 and 2. Three candidates are supplied but their relay + // parents look like this: 3, 2, 3. + // The latest on-chain relay parent for this para is 0 but there is a pending + // availability candidate with relay parent 4. Therefore, no candidate will get + // picked. + // + // Para 2 scheduled on cores 3, 4 and 5. Three candidates are supplied and their relay + // parents look like this: 2, 3, 3. + // The latest on-chain relay parent for this para is 0 but there is a pending + // availability candidate with relay parent 2. Therefore, all 3 will get picked. + new_test_ext(default_config()).execute_with(|| { + let TestData { + backed_candidates, + scheduled_paras: scheduled, + expected_backed_candidates_with_core, + } = get_test_data_for_relay_parent_ordering(core_index_enabled); + + // For para 1, add a dummy pending candidate with relay parent 4. + let mut candidates = VecDeque::new(); + let mut commitments = backed_candidates[0].candidate().commitments.clone(); + commitments.head_data = paras::Heads::::get(&ParaId::from(1)).unwrap(); + candidates.push_back(inclusion::CandidatePendingAvailability::new( + CoreIndex(0), + CandidateHash(Hash::repeat_byte(1)), + backed_candidates[0].descriptor().clone(), + commitments, + Default::default(), + Default::default(), + 4, + 4, + GroupIndex(0), + )); + inclusion::PendingAvailability::::insert(ParaId::from(1), candidates); + + // For para 2, add a dummy pending candidate with relay parent 2. + let mut candidates = VecDeque::new(); + let mut commitments = backed_candidates[3].candidate().commitments.clone(); + commitments.head_data = paras::Heads::::get(&ParaId::from(2)).unwrap(); + candidates.push_back(inclusion::CandidatePendingAvailability::new( + CoreIndex(0), + CandidateHash(Hash::repeat_byte(2)), + backed_candidates[3].descriptor().clone(), + commitments, + Default::default(), + Default::default(), + 2, + 2, + GroupIndex(3), + )); + inclusion::PendingAvailability::::insert(ParaId::from(2), candidates); + + let res = sanitize_backed_candidates::( + backed_candidates.clone(), + &shared::AllowedRelayParents::::get(), + BTreeSet::new(), + scheduled, + core_index_enabled, + ); + + if core_index_enabled { + assert_eq!(res.len(), 1); + assert_eq!( + expected_backed_candidates_with_core.get(&ParaId::from(2)), + res.get(&ParaId::from(2)), + ); + } else { + assert!(res.is_empty()); + } + }); + } + // nothing is scheduled, so no paraids match, thus all backed candidates are skipped #[rstest] #[case(false, false)] diff --git a/prdoc/pr_5113.prdoc b/prdoc/pr_5113.prdoc new file mode 100644 index 000000000000..64563f7a735d --- /dev/null +++ b/prdoc/pr_5113.prdoc @@ -0,0 +1,14 @@ +title: Make the candidate relay parent progression check more strict for elastic scaling + +doc: + - audience: Runtime Dev + description: | + Previously, the relay chain runtime was checking if the relay parent of a new candidate does + not move backwards from the latest included on-chain candidate. This was fine prior to elastic scaling. + We now need to also check that the relay parent progresses from the latest pending availability candidate, + as well as check the progression within the candidate chain in the inherent data. + Prospective-parachains is already doing this check but it's also needed in the runtime. + +crates: +- name: polkadot-runtime-parachains + bump: patch From 5dc0670a85d575480e0840204c20a5771cd8f7d3 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Fri, 26 Jul 2024 16:33:12 +0200 Subject: [PATCH 025/382] BEEFY: Disarm finality notifications to prevent pinning (#5129) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should prevent excessive pinning of blocks while we are waiting for the block ancestry to be downloaded after gap sync. We spawn a new task that gets polled to transform finality notifications into an unpinned counterpart. Before this PR, finality notifications were kept in the notification channel. This led to pinning cache overflows. fixes #4389 --------- Co-authored-by: Bastian Köcher --- prdoc/pr_5129.prdoc | 16 +++ substrate/client/consensus/beefy/src/lib.rs | 104 ++++++++++++++---- substrate/client/consensus/beefy/src/tests.rs | 74 +++++++++---- .../client/consensus/beefy/src/worker.rs | 12 +- 4 files changed, 159 insertions(+), 47 deletions(-) create mode 100644 prdoc/pr_5129.prdoc diff --git a/prdoc/pr_5129.prdoc b/prdoc/pr_5129.prdoc new file mode 100644 index 000000000000..beb7eb4d282d --- /dev/null +++ b/prdoc/pr_5129.prdoc @@ -0,0 +1,16 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Prevent finalized notification hoarding in beefy gadget + +doc: + - audience: Node Operator + description: | + This PR fixes the error message "Notification block pinning limit + reached." during warp sync. Finality notifications in BEEFY are now + constantly being consumed and don't keep blocks pinned for extended + periods of time. + +crates: + - name: sc-consensus-beefy + bump: minor diff --git a/substrate/client/consensus/beefy/src/lib.rs b/substrate/client/consensus/beefy/src/lib.rs index a47bfe1dbe24..f7c999aad591 100644 --- a/substrate/client/consensus/beefy/src/lib.rs +++ b/substrate/client/consensus/beefy/src/lib.rs @@ -35,10 +35,11 @@ use futures::{stream::Fuse, FutureExt, StreamExt}; use log::{debug, error, info, warn}; use parking_lot::Mutex; use prometheus_endpoint::Registry; -use sc_client_api::{Backend, BlockBackend, BlockchainEvents, FinalityNotifications, Finalizer}; +use sc_client_api::{Backend, BlockBackend, BlockchainEvents, FinalityNotification, Finalizer}; use sc_consensus::BlockImport; use sc_network::{NetworkRequest, NotificationService, ProtocolName}; use sc_network_gossip::{GossipEngine, Network as GossipNetwork, Syncing as GossipSyncing}; +use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver}; use sp_api::ProvideRuntimeApi; use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend}; use sp_consensus::{Error as ConsensusError, SyncOracle}; @@ -49,7 +50,9 @@ use sp_keystore::KeystorePtr; use sp_runtime::traits::{Block, Header as HeaderT, NumberFor, Zero}; use std::{ collections::{BTreeMap, VecDeque}, + future::Future, marker::PhantomData, + pin::Pin, sync::Arc, time::Duration, }; @@ -87,6 +90,8 @@ const LOG_TARGET: &str = "beefy"; const HEADER_SYNC_DELAY: Duration = Duration::from_secs(60); +type FinalityNotifications = + sc_utils::mpsc::TracingUnboundedReceiver>; /// A convenience BEEFY client trait that defines all the type bounds a BEEFY client /// has to satisfy. Ideally that should actually be a trait alias. Unfortunately as /// of today, Rust does not allow a type alias to be used as a trait bound. Tracking @@ -484,6 +489,30 @@ where } } +/// Finality notification for consumption by BEEFY worker. +/// This is a stripped down version of `sc_client_api::FinalityNotification` which does not keep +/// blocks pinned. +struct UnpinnedFinalityNotification { + /// Finalized block header hash. + pub hash: B::Hash, + /// Finalized block header. + pub header: B::Header, + /// Path from the old finalized to new finalized parent (implicitly finalized blocks). + /// + /// This maps to the range `(old_finalized, new_finalized)`. + pub tree_route: Arc<[B::Hash]>, +} + +impl From> for UnpinnedFinalityNotification { + fn from(value: FinalityNotification) -> Self { + UnpinnedFinalityNotification { + hash: value.hash, + header: value.header, + tree_route: value.tree_route, + } + } +} + /// Start the BEEFY gadget. /// /// This is a thin shim around running and awaiting a BEEFY worker. @@ -525,10 +554,13 @@ pub async fn start_beefy_gadget( let metrics = register_metrics(prometheus_registry.clone()); + let mut block_import_justif = links.from_block_import_justif_stream.subscribe(100_000).fuse(); + // Subscribe to finality notifications and justifications before waiting for runtime pallet and // reuse the streams, so we don't miss notifications while waiting for pallet to be available. - let mut finality_notifications = client.finality_notification_stream().fuse(); - let mut block_import_justif = links.from_block_import_justif_stream.subscribe(100_000).fuse(); + let finality_notifications = client.finality_notification_stream(); + let (mut transformer, mut finality_notifications) = + finality_notification_transformer_future(finality_notifications); let known_peers = Arc::new(Mutex::new(KnownPeers::new())); // Default votes filter is to discard everything. @@ -582,7 +614,11 @@ pub async fn start_beefy_gadget( _ = &mut beefy_comms.gossip_engine => { error!(target: LOG_TARGET, "🥩 Gossip engine has unexpectedly terminated."); return - } + }, + _ = &mut transformer => { + error!(target: LOG_TARGET, "🥩 Finality notification transformer task has unexpectedly terminated."); + return + }, }; let worker = worker_builder.build( @@ -594,30 +630,54 @@ pub async fn start_beefy_gadget( is_authority, ); - match futures::future::select( - Box::pin(worker.run(&mut block_import_justif, &mut finality_notifications)), - Box::pin(on_demand_justifications_handler.run()), - ) - .await - { - // On `ConsensusReset` error, just reinit and restart voter. - futures::future::Either::Left(((error::Error::ConsensusReset, reuse_comms), _)) => { - error!(target: LOG_TARGET, "🥩 Error: {:?}. Restarting voter.", error::Error::ConsensusReset); - beefy_comms = reuse_comms; - continue; - }, - // On other errors, bring down / finish the task. - futures::future::Either::Left(((worker_err, _), _)) => { - error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", worker_err) + futures::select! { + result = worker.run(&mut block_import_justif, &mut finality_notifications).fuse() => { + match result { + (error::Error::ConsensusReset, reuse_comms) => { + error!(target: LOG_TARGET, "🥩 Error: {:?}. Restarting voter.", error::Error::ConsensusReset); + beefy_comms = reuse_comms; + continue; + }, + (err, _) => { + error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", err) + } + } }, - futures::future::Either::Right((odj_handler_err, _)) => { - error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", odj_handler_err) + odj_handler_error = on_demand_justifications_handler.run().fuse() => { + error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", odj_handler_error) }, - }; + _ = &mut transformer => { + error!(target: LOG_TARGET, "🥩 Finality notification transformer task has unexpectedly terminated."); + } + } return; } } +/// Produce a future that transformes finality notifications into a struct that does not keep blocks +/// pinned. +fn finality_notification_transformer_future( + mut finality_notifications: sc_client_api::FinalityNotifications, +) -> ( + Pin + Sized>>>, + Fuse>>, +) +where + B: Block, +{ + let (tx, rx) = tracing_unbounded("beefy-notification-transformer-channel", 10000); + let transformer_fut = async move { + while let Some(notification) = finality_notifications.next().await { + debug!(target: LOG_TARGET, "🥩 Transforming grandpa notification. #{}({:?})", notification.header.number(), notification.hash); + if let Err(err) = tx.unbounded_send(UnpinnedFinalityNotification::from(notification)) { + error!(target: LOG_TARGET, "🥩 Unable to send transformed notification. Shutting down. err = {}", err); + return + }; + } + }; + (Box::pin(transformer_fut.fuse()), rx.fuse()) +} + /// Waits until the parent header of `current` is available and returns it. /// /// When the node uses GRANDPA warp sync it initially downloads only the mandatory GRANDPA headers. diff --git a/substrate/client/consensus/beefy/src/tests.rs b/substrate/client/consensus/beefy/src/tests.rs index d8f5b39dbbaa..4b1dd447320b 100644 --- a/substrate/client/consensus/beefy/src/tests.rs +++ b/substrate/client/consensus/beefy/src/tests.rs @@ -30,13 +30,17 @@ use crate::{ request_response::{on_demand_justifications_protocol_config, BeefyJustifsRequestHandler}, }, error::Error, - gossip_protocol_name, + finality_notification_transformer_future, gossip_protocol_name, justification::*, wait_for_runtime_pallet, worker::PersistedState, - BeefyRPCLinks, BeefyVoterLinks, BeefyWorkerBuilder, KnownPeers, + BeefyRPCLinks, BeefyVoterLinks, BeefyWorkerBuilder, KnownPeers, UnpinnedFinalityNotification, +}; +use futures::{ + future, + stream::{Fuse, FuturesUnordered}, + Future, FutureExt, StreamExt, }; -use futures::{future, stream::FuturesUnordered, Future, FutureExt, StreamExt}; use parking_lot::Mutex; use sc_block_builder::BlockBuilderBuilder; use sc_client_api::{Backend as BackendT, BlockchainEvents, FinalityNotifications, HeaderBackend}; @@ -49,7 +53,7 @@ use sc_network_test::{ Block, BlockImportAdapter, FullPeerConfig, PassThroughVerifier, Peer, PeersClient, PeersFullClient, TestNetFactory, }; -use sc_utils::notification::NotificationReceiver; +use sc_utils::{mpsc::TracingUnboundedReceiver, notification::NotificationReceiver}; use serde::{Deserialize, Serialize}; use sp_api::{ApiRef, ProvideRuntimeApi}; use sp_application_crypto::key_types::BEEFY as BEEFY_KEY_TYPE; @@ -371,7 +375,7 @@ pub(crate) fn create_beefy_keystore(authority: &BeefyKeyring) -> Ke async fn voter_init_setup( net: &mut BeefyTestNet, - finality: &mut futures::stream::Fuse>, + finality: &mut futures::stream::Fuse>, api: &TestApi, ) -> Result, Error> { let backend = net.peer(0).client().as_backend(); @@ -391,6 +395,15 @@ async fn voter_init_setup( .await } +fn start_finality_worker( + finality: FinalityNotifications, +) -> Fuse>> { + let (transformer, finality_notifications) = finality_notification_transformer_future(finality); + let tokio_handle = tokio::runtime::Handle::current(); + tokio_handle.spawn(transformer); + finality_notifications +} + // Spawns beefy voters. Returns a future to spawn on the runtime. fn initialize_beefy( net: &mut BeefyTestNet, @@ -1020,13 +1033,17 @@ async fn should_initialize_voter_at_genesis() { // push 15 blocks with `AuthorityChange` digests every 10 blocks let hashes = net.generate_blocks_and_sync(15, 10, &validator_set, false).await; - let mut finality = net.peer(0).client().as_client().finality_notification_stream().fuse(); + let finality = net.peer(0).client().as_client().finality_notification_stream(); + + let mut finality_notifications = start_finality_worker(finality); + // finalize 13 without justifications net.peer(0).client().as_client().finalize_block(hashes[13], None).unwrap(); let api = TestApi::with_validator_set(&validator_set); // load persistent state - nothing in DB, should init at genesis - let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); + let persisted_state = + voter_init_setup(&mut net, &mut finality_notifications, &api).await.unwrap(); // Test initialization at session boundary. // verify voter initialized with two sessions starting at blocks 1 and 10 @@ -1061,14 +1078,18 @@ async fn should_initialize_voter_at_custom_genesis() { // push 15 blocks with `AuthorityChange` digests every 15 blocks let hashes = net.generate_blocks_and_sync(15, 15, &validator_set, false).await; - let mut finality = net.peer(0).client().as_client().finality_notification_stream().fuse(); + let finality = net.peer(0).client().as_client().finality_notification_stream(); + + let mut finality_notifications = start_finality_worker(finality); + // finalize 3, 5, 8 without justifications net.peer(0).client().as_client().finalize_block(hashes[3], None).unwrap(); net.peer(0).client().as_client().finalize_block(hashes[5], None).unwrap(); net.peer(0).client().as_client().finalize_block(hashes[8], None).unwrap(); // load persistent state - nothing in DB, should init at genesis - let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); + let persisted_state = + voter_init_setup(&mut net, &mut finality_notifications, &api).await.unwrap(); // Test initialization at session boundary. // verify voter initialized with single session starting at block `custom_pallet_genesis` (7) @@ -1098,7 +1119,8 @@ async fn should_initialize_voter_at_custom_genesis() { net.peer(0).client().as_client().finalize_block(hashes[10], None).unwrap(); // load persistent state - state preset in DB, but with different pallet genesis - let new_persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); + let new_persisted_state = + voter_init_setup(&mut net, &mut finality_notifications, &api).await.unwrap(); // verify voter initialized with single session starting at block `new_pallet_genesis` (10) let sessions = new_persisted_state.voting_oracle().sessions(); @@ -1129,7 +1151,9 @@ async fn should_initialize_voter_when_last_final_is_session_boundary() { // push 15 blocks with `AuthorityChange` digests every 10 blocks let hashes = net.generate_blocks_and_sync(15, 10, &validator_set, false).await; - let mut finality = net.peer(0).client().as_client().finality_notification_stream().fuse(); + let finality = net.peer(0).client().as_client().finality_notification_stream(); + + let mut finality_notifications = start_finality_worker(finality); // finalize 13 without justifications net.peer(0).client().as_client().finalize_block(hashes[13], None).unwrap(); @@ -1153,7 +1177,8 @@ async fn should_initialize_voter_when_last_final_is_session_boundary() { let api = TestApi::with_validator_set(&validator_set); // load persistent state - nothing in DB, should init at session boundary - let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); + let persisted_state = + voter_init_setup(&mut net, &mut finality_notifications, &api).await.unwrap(); // verify voter initialized with single session starting at block 10 assert_eq!(persisted_state.voting_oracle().sessions().len(), 1); @@ -1183,7 +1208,9 @@ async fn should_initialize_voter_at_latest_finalized() { // push 15 blocks with `AuthorityChange` digests every 10 blocks let hashes = net.generate_blocks_and_sync(15, 10, &validator_set, false).await; - let mut finality = net.peer(0).client().as_client().finality_notification_stream().fuse(); + let finality = net.peer(0).client().as_client().finality_notification_stream(); + + let mut finality_notifications = start_finality_worker(finality); // finalize 13 without justifications net.peer(0).client().as_client().finalize_block(hashes[13], None).unwrap(); @@ -1206,7 +1233,8 @@ async fn should_initialize_voter_at_latest_finalized() { let api = TestApi::with_validator_set(&validator_set); // load persistent state - nothing in DB, should init at last BEEFY finalized - let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); + let persisted_state = + voter_init_setup(&mut net, &mut finality_notifications, &api).await.unwrap(); // verify voter initialized with single session starting at block 12 assert_eq!(persisted_state.voting_oracle().sessions().len(), 1); @@ -1239,12 +1267,15 @@ async fn should_initialize_voter_at_custom_genesis_when_state_unavailable() { // push 30 blocks with `AuthorityChange` digests every 5 blocks let hashes = net.generate_blocks_and_sync(30, 5, &validator_set, false).await; - let mut finality = net.peer(0).client().as_client().finality_notification_stream().fuse(); + let finality = net.peer(0).client().as_client().finality_notification_stream(); + + let mut finality_notifications = start_finality_worker(finality); // finalize 30 without justifications net.peer(0).client().as_client().finalize_block(hashes[30], None).unwrap(); // load persistent state - nothing in DB, should init at genesis - let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); + let persisted_state = + voter_init_setup(&mut net, &mut finality_notifications, &api).await.unwrap(); // Test initialization at session boundary. // verify voter initialized with all sessions pending, first one starting at block 5 (start of @@ -1282,14 +1313,18 @@ async fn should_catch_up_when_loading_saved_voter_state() { // push 30 blocks with `AuthorityChange` digests every 10 blocks let hashes = net.generate_blocks_and_sync(30, 10, &validator_set, false).await; - let mut finality = net.peer(0).client().as_client().finality_notification_stream().fuse(); + let finality = net.peer(0).client().as_client().finality_notification_stream(); + + let mut finality_notifications = start_finality_worker(finality); + // finalize 13 without justifications net.peer(0).client().as_client().finalize_block(hashes[13], None).unwrap(); let api = TestApi::with_validator_set(&validator_set); // load persistent state - nothing in DB, should init at genesis - let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); + let persisted_state = + voter_init_setup(&mut net, &mut finality_notifications, &api).await.unwrap(); // Test initialization at session boundary. // verify voter initialized with two sessions starting at blocks 1 and 10 @@ -1316,7 +1351,8 @@ async fn should_catch_up_when_loading_saved_voter_state() { // finalize 25 without justifications net.peer(0).client().as_client().finalize_block(hashes[25], None).unwrap(); // load persistent state - state preset in DB - let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); + let persisted_state = + voter_init_setup(&mut net, &mut finality_notifications, &api).await.unwrap(); // Verify voter initialized with old sessions plus a new one starting at block 20. // There shouldn't be any duplicates. diff --git a/substrate/client/consensus/beefy/src/worker.rs b/substrate/client/consensus/beefy/src/worker.rs index 4a9f7a2d0e3b..ca714ffadd77 100644 --- a/substrate/client/consensus/beefy/src/worker.rs +++ b/substrate/client/consensus/beefy/src/worker.rs @@ -29,14 +29,14 @@ use crate::{ metric_inc, metric_set, metrics::VoterMetrics, round::{Rounds, VoteImportResult}, - BeefyComms, BeefyVoterLinks, LOG_TARGET, + BeefyComms, BeefyVoterLinks, UnpinnedFinalityNotification, LOG_TARGET, }; use sp_application_crypto::RuntimeAppPublic; use codec::{Codec, Decode, DecodeAll, Encode}; use futures::{stream::Fuse, FutureExt, StreamExt}; use log::{debug, error, info, trace, warn}; -use sc_client_api::{Backend, FinalityNotification, FinalityNotifications, HeaderBackend}; +use sc_client_api::{Backend, HeaderBackend}; use sc_utils::notification::NotificationReceiver; use sp_api::ProvideRuntimeApi; use sp_arithmetic::traits::{AtLeast32Bit, Saturating}; @@ -447,20 +447,20 @@ where fn handle_finality_notification( &mut self, - notification: &FinalityNotification, + notification: &UnpinnedFinalityNotification, ) -> Result<(), Error> { let header = ¬ification.header; debug!( target: LOG_TARGET, "🥩 Finality notification: header(number {:?}, hash {:?}) tree_route {:?}", header.number(), - header.hash(), + notification.hash, notification.tree_route, ); self.runtime .runtime_api() - .beefy_genesis(header.hash()) + .beefy_genesis(notification.hash) .ok() .flatten() .filter(|genesis| *genesis == self.persisted_state.pallet_genesis) @@ -845,7 +845,7 @@ where block_import_justif: &mut Fuse< NotificationReceiver>, >, - finality_notifications: &mut Fuse>, + finality_notifications: &mut Fuse>, ) -> (Error, BeefyComms) { info!( target: LOG_TARGET, From 7e4433e43072fda77f85cb5d0bc531fa255f3104 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Fri, 26 Jul 2024 17:26:44 +0100 Subject: [PATCH 026/382] Update README.md (#5152) Related to https://github.com/paritytech/polkadot-sdk/issues/5144, plus remove the lines of code badge as it was not working. --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a070d61170f6..34d657194daa 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,7 @@ forks](https://img.shields.io/github/forks/paritytech/polkadot-sdk) -[![StackExchange](https://img.shields.io/badge/StackExchange-Community%20&%20Support-222222?logo=stackexchange)](https://substrate.stackexchange.com/)  ![GitHub contributors](https://img.shields.io/github/contributors/paritytech/polkadot-sdk)  ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/paritytech/polkadot-sdk) - -![GitHub lines of code](https://tokei.rs/b1/github/paritytech/polkadot-sdk)   -![GitHub last commit](https://img.shields.io/github/last-commit/paritytech/polkadot-sdk) +[![StackExchange](https://img.shields.io/badge/StackExchange-Community%20&%20Support-222222?logo=stackexchange)](https://substrate.stackexchange.com/)  ![GitHub contributors](https://img.shields.io/github/contributors/paritytech/polkadot-sdk)  ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/paritytech/polkadot-sdk)  ![GitHub last commit](https://img.shields.io/github/last-commit/paritytech/polkadot-sdk) > The Polkadot SDK repository provides all the components needed to start building on the > [Polkadot](https://polkadot.network) network, a multi-chain blockchain platform that enables @@ -24,7 +21,7 @@ forks](https://img.shields.io/github/forks/paritytech/polkadot-sdk) ## ⚡ Quickstart If you want to get an example node running quickly you can execute the following getting started script: ``` -curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/scripts/getting-started.sh +curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/scripts/getting-started.sh | bash ``` ## 📚 Documentation From 72509375d17ce3d78ee3794792cabcea5e560a15 Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Sat, 27 Jul 2024 02:27:59 +0900 Subject: [PATCH 027/382] Fix warnings for rust 1.80 (#5150) Fix warnings for rust 1.80 --- Cargo.toml | 2 + substrate/frame/support/src/lib.rs | 1 - .../support/src/storage/generator/map.rs | 41 ------------------- .../primitives/runtime-interface/src/lib.rs | 3 -- substrate/primitives/weights/src/weight_v2.rs | 4 +- 5 files changed, 4 insertions(+), 47 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 50c9fc88836f..a3582fbda133 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -542,6 +542,8 @@ default-members = [ [workspace.lints.rust] suspicious_double_ref_op = { level = "allow", priority = 2 } +# `substrate_runtime` is a common `cfg` condition name used in the repo. +unexpected_cfgs = { level = "allow", check-cfg = ['cfg(substrate_runtime)'] } [workspace.lints.clippy] all = { level = "allow", priority = 0 } diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index 4864059167a9..e1e1e0153de0 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -869,7 +869,6 @@ macro_rules! hypothetically_ok { pub use serde::{Deserialize, Serialize}; #[doc(hidden)] -#[cfg(not(no_std))] pub use macro_magic; /// Prelude to be used for pallet testing, for ease of use. diff --git a/substrate/frame/support/src/storage/generator/map.rs b/substrate/frame/support/src/storage/generator/map.rs index b41f9c717167..7a74308fdeac 100644 --- a/substrate/frame/support/src/storage/generator/map.rs +++ b/substrate/frame/support/src/storage/generator/map.rs @@ -74,47 +74,6 @@ pub trait StorageMap { } } -/// Utility to iterate through items in a storage map. -pub struct StorageMapIterator { - prefix: Vec, - previous_key: Vec, - drain: bool, - _phantom: ::core::marker::PhantomData<(K, V, Hasher)>, -} - -impl Iterator - for StorageMapIterator -{ - type Item = (K, V); - - fn next(&mut self) -> Option<(K, V)> { - loop { - let maybe_next = sp_io::storage::next_key(&self.previous_key) - .filter(|n| n.starts_with(&self.prefix)); - break match maybe_next { - Some(next) => { - self.previous_key = next; - match unhashed::get::(&self.previous_key) { - Some(value) => { - if self.drain { - unhashed::kill(&self.previous_key) - } - let mut key_material = - Hasher::reverse(&self.previous_key[self.prefix.len()..]); - match K::decode(&mut key_material) { - Ok(key) => Some((key, value)), - Err(_) => continue, - } - }, - None => continue, - } - }, - None => None, - } - } - } -} - impl> storage::IterableStorageMap for G where G::Hasher: ReversibleStorageHasher, diff --git a/substrate/primitives/runtime-interface/src/lib.rs b/substrate/primitives/runtime-interface/src/lib.rs index d6dcb69958af..1de8543c461d 100644 --- a/substrate/primitives/runtime-interface/src/lib.rs +++ b/substrate/primitives/runtime-interface/src/lib.rs @@ -15,9 +15,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Custom inner attributes are unstable, so we need to faky disable the attribute. -// rustfmt still honors the attribute to not format the rustdocs below. -#![cfg_attr(feature = "never", rustfmt::skip)] //! Substrate runtime interface //! //! This crate provides types, traits and macros around runtime interfaces. A runtime interface is diff --git a/substrate/primitives/weights/src/weight_v2.rs b/substrate/primitives/weights/src/weight_v2.rs index 3c10929f433b..0f92e6448ca9 100644 --- a/substrate/primitives/weights/src/weight_v2.rs +++ b/substrate/primitives/weights/src/weight_v2.rs @@ -401,14 +401,14 @@ where } } -#[cfg(any(test, feature = "std", feature = "runtime-benchmarks"))] +#[cfg(any(test, feature = "std"))] impl From for Weight { fn from(value: u64) -> Self { Self::from_parts(value, value) } } -#[cfg(any(test, feature = "std", feature = "runtime-benchmarks"))] +#[cfg(any(test, feature = "std"))] impl From<(u64, u64)> for Weight { fn from(value: (u64, u64)) -> Self { Self::from_parts(value.0, value.1) From c39cc333b677da6e4f18ee836a38bf01130ca221 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Fri, 26 Jul 2024 19:49:38 +0200 Subject: [PATCH 028/382] Fix region nonfungible implementation (#5067) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The problem with the current implementation is that minting will cause the region coremask to be set to `Coremask::complete` regardless of the actual coremask. This PR fixes that. More details about the nonfungible implementation can be found here: https://github.com/paritytech/polkadot-sdk/pull/3455 --------- Co-authored-by: Dónal Murray Co-authored-by: Branislav Kontur Co-authored-by: Francisco Aguirre Co-authored-by: Oliver Tale-Yazdi --- .../coretime/coretime-rococo/src/lib.rs | 2 +- .../coretime/coretime-westend/src/lib.rs | 2 +- prdoc/pr_5067.prdoc | 19 ++++++++ .../frame/broker/src/dispatchable_impls.rs | 10 ++++- .../frame/broker/src/nonfungible_impl.rs | 9 +++- substrate/frame/broker/src/tests.rs | 43 +++++++++++++++++++ substrate/frame/broker/src/utility_impls.rs | 3 +- 7 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 prdoc/pr_5067.prdoc diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index 9fd0093840d3..37a6ea1d1c5f 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -832,7 +832,7 @@ impl_runtime_apis! { let begin = 0; let end = 42; - let region_id = pallet_broker::Pallet::::issue(core, begin, end, None, None); + let region_id = pallet_broker::Pallet::::issue(core, begin, pallet_broker::CoreMask::complete(), end, None, None); Some(( Asset { fun: NonFungible(Index(region_id.into())), diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index 7907f252cf8e..b6847dddb6c0 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -823,7 +823,7 @@ impl_runtime_apis! { let begin = 0; let end = 42; - let region_id = pallet_broker::Pallet::::issue(core, begin, end, None, None); + let region_id = pallet_broker::Pallet::::issue(core, begin, pallet_broker::CoreMask::complete(), end, None, None); Some(( Asset { fun: NonFungible(Index(region_id.into())), diff --git a/prdoc/pr_5067.prdoc b/prdoc/pr_5067.prdoc new file mode 100644 index 000000000000..9a11f96b5104 --- /dev/null +++ b/prdoc/pr_5067.prdoc @@ -0,0 +1,19 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix region nonfungible implementation + +doc: + - audience: Runtime User + description: | + PR fixes the issue with the current implementation where minting causes + the region coremask to be set to `Coremask::complete` regardless of the + actual coremask of the region. + +crates: +- name: pallet-broker + bump: major +- name: coretime-rococo-runtime + bump: patch +- name: coretime-westend-runtime + bump: patch \ No newline at end of file diff --git a/substrate/frame/broker/src/dispatchable_impls.rs b/substrate/frame/broker/src/dispatchable_impls.rs index 9e7a56e52812..a8ded084a801 100644 --- a/substrate/frame/broker/src/dispatchable_impls.rs +++ b/substrate/frame/broker/src/dispatchable_impls.rs @@ -128,8 +128,14 @@ impl Pallet { let core = Self::purchase_core(&who, price, &mut sale)?; SaleInfo::::put(&sale); - let id = - Self::issue(core, sale.region_begin, sale.region_end, Some(who.clone()), Some(price)); + let id = Self::issue( + core, + sale.region_begin, + CoreMask::complete(), + sale.region_end, + Some(who.clone()), + Some(price), + ); let duration = sale.region_end.saturating_sub(sale.region_begin); Self::deposit_event(Event::Purchased { who, region_id: id, price, duration }); Ok(id) diff --git a/substrate/frame/broker/src/nonfungible_impl.rs b/substrate/frame/broker/src/nonfungible_impl.rs index e272ecbe0081..acbcba1dae49 100644 --- a/substrate/frame/broker/src/nonfungible_impl.rs +++ b/substrate/frame/broker/src/nonfungible_impl.rs @@ -74,7 +74,14 @@ impl Mutate for Pallet { // 'Minting' can only occur if the asset has previously been burned (i.e. moved to the // holding register) ensure!(record.owner.is_none(), Error::::NotAllowed); - Self::issue(region_id.core, region_id.begin, record.end, Some(who.clone()), record.paid); + Self::issue( + region_id.core, + region_id.begin, + region_id.mask, + record.end, + Some(who.clone()), + record.paid, + ); Ok(()) } diff --git a/substrate/frame/broker/src/tests.rs b/substrate/frame/broker/src/tests.rs index 2a8ea24b447a..3ea68c5a74d6 100644 --- a/substrate/frame/broker/src/tests.rs +++ b/substrate/frame/broker/src/tests.rs @@ -240,6 +240,49 @@ fn mutate_operations_work() { }); } +#[test] +fn mutate_operations_work_with_partitioned_region() { + TestExt::new().endow(1, 1000).execute_with(|| { + assert_ok!(Broker::do_start_sales(100, 1)); + advance_to(2); + let region = Broker::do_purchase(1, u64::max_value()).unwrap(); + let (region1, _region2) = Broker::do_partition(region, None, 2).unwrap(); + let record_1 = Regions::::get(region1).unwrap(); + + // 'withdraw' the region from user 1: + assert_ok!(>::burn(®ion1.into(), Some(&1))); + assert_eq!(Regions::::get(region1).unwrap().owner, None); + + // `mint_into` works after burning: + assert_ok!(>::mint_into(®ion1.into(), &1)); + + // Ensure the region minted is the same as the one we burned previously: + assert_eq!(Regions::::get(region1).unwrap(), record_1); + }); +} + +#[test] +fn mutate_operations_work_with_interlaced_region() { + TestExt::new().endow(1, 1000).execute_with(|| { + assert_ok!(Broker::do_start_sales(100, 1)); + advance_to(2); + let region = Broker::do_purchase(1, u64::max_value()).unwrap(); + let (region1, _region2) = + Broker::do_interlace(region, None, CoreMask::from_chunk(0, 40)).unwrap(); + let record_1 = Regions::::get(region1).unwrap(); + + // 'withdraw' the region from user 1: + assert_ok!(>::burn(®ion1.into(), Some(&1))); + assert_eq!(Regions::::get(region1).unwrap().owner, None); + + // `mint_into` works after burning: + assert_ok!(>::mint_into(®ion1.into(), &1)); + + // Ensure the region minted is the same as the one we burned previously: + assert_eq!(Regions::::get(region1).unwrap(), record_1); + }); +} + #[test] fn permanent_is_not_reassignable() { TestExt::new().endow(1, 1000).execute_with(|| { diff --git a/substrate/frame/broker/src/utility_impls.rs b/substrate/frame/broker/src/utility_impls.rs index 9cceb7f970a9..5c66ebb9674b 100644 --- a/substrate/frame/broker/src/utility_impls.rs +++ b/substrate/frame/broker/src/utility_impls.rs @@ -94,11 +94,12 @@ impl Pallet { pub fn issue( core: CoreIndex, begin: Timeslice, + mask: CoreMask, end: Timeslice, owner: Option, paid: Option>, ) -> RegionId { - let id = RegionId { begin, core, mask: CoreMask::complete() }; + let id = RegionId { begin, core, mask }; let record = RegionRecord { end, owner, paid }; Regions::::insert(&id, &record); id From d3d1542c1d387408c141f9a1a8168e32435a4be9 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Sat, 27 Jul 2024 00:20:37 +0100 Subject: [PATCH 029/382] Replace homepage in all TOML files (#5118) A bit of a controversial move, but a good preparation for even further reducing the traffic on outdated content of `substrate.io`. Current status: Screenshot 2024-07-15 at 11 32 48 Previously, I was in favor of changing the domain of the rust-docs to something like `polkadot-sdk.parity.io` or similar, but I think the current format is pretty standard and has a higher chance of staying put over the course of time: `.github.io/` -> `https://paritytech.github.io/polkadot-sdk/` part of https://github.com/paritytech/eng-automation/issues/10 --- .github/workflows/misc-sync-templates.yml | 4 ++-- Cargo.toml | 2 +- cumulus/pallets/collator-selection/Cargo.toml | 2 +- cumulus/pallets/session-benchmarking/Cargo.toml | 2 +- cumulus/parachains/runtimes/testing/penpal/Cargo.toml | 2 +- substrate/bin/node/bench/Cargo.toml | 2 +- substrate/bin/node/cli/Cargo.toml | 2 +- substrate/bin/node/inspect/Cargo.toml | 2 +- substrate/bin/node/primitives/Cargo.toml | 2 +- substrate/bin/node/rpc/Cargo.toml | 2 +- substrate/bin/node/runtime/Cargo.toml | 2 +- substrate/bin/node/testing/Cargo.toml | 2 +- substrate/bin/utils/chain-spec-builder/Cargo.toml | 2 +- substrate/bin/utils/subkey/Cargo.toml | 2 +- substrate/client/allocator/Cargo.toml | 2 +- substrate/client/api/Cargo.toml | 2 +- substrate/client/authority-discovery/Cargo.toml | 2 +- substrate/client/basic-authorship/Cargo.toml | 2 +- substrate/client/block-builder/Cargo.toml | 2 +- substrate/client/chain-spec/Cargo.toml | 2 +- substrate/client/chain-spec/derive/Cargo.toml | 2 +- substrate/client/cli/Cargo.toml | 2 +- substrate/client/consensus/aura/Cargo.toml | 2 +- substrate/client/consensus/babe/Cargo.toml | 2 +- substrate/client/consensus/babe/rpc/Cargo.toml | 2 +- substrate/client/consensus/beefy/Cargo.toml | 2 +- substrate/client/consensus/beefy/rpc/Cargo.toml | 2 +- substrate/client/consensus/common/Cargo.toml | 2 +- substrate/client/consensus/epochs/Cargo.toml | 2 +- substrate/client/consensus/grandpa/Cargo.toml | 2 +- substrate/client/consensus/grandpa/rpc/Cargo.toml | 2 +- substrate/client/consensus/manual-seal/Cargo.toml | 2 +- substrate/client/consensus/pow/Cargo.toml | 2 +- substrate/client/consensus/slots/Cargo.toml | 2 +- substrate/client/db/Cargo.toml | 2 +- substrate/client/executor/Cargo.toml | 2 +- substrate/client/executor/common/Cargo.toml | 2 +- substrate/client/executor/polkavm/Cargo.toml | 2 +- substrate/client/executor/runtime-test/Cargo.toml | 2 +- substrate/client/executor/wasmtime/Cargo.toml | 2 +- substrate/client/informant/Cargo.toml | 2 +- substrate/client/keystore/Cargo.toml | 2 +- substrate/client/merkle-mountain-range/Cargo.toml | 2 +- substrate/client/merkle-mountain-range/rpc/Cargo.toml | 2 +- substrate/client/mixnet/Cargo.toml | 2 +- substrate/client/network-gossip/Cargo.toml | 2 +- substrate/client/network/Cargo.toml | 2 +- substrate/client/network/common/Cargo.toml | 2 +- substrate/client/network/light/Cargo.toml | 2 +- substrate/client/network/statement/Cargo.toml | 2 +- substrate/client/network/sync/Cargo.toml | 2 +- substrate/client/network/test/Cargo.toml | 2 +- substrate/client/network/transactions/Cargo.toml | 2 +- substrate/client/network/types/Cargo.toml | 2 +- substrate/client/offchain/Cargo.toml | 2 +- substrate/client/proposer-metrics/Cargo.toml | 2 +- substrate/client/rpc-api/Cargo.toml | 2 +- substrate/client/rpc-servers/Cargo.toml | 2 +- substrate/client/rpc-spec-v2/Cargo.toml | 2 +- substrate/client/rpc/Cargo.toml | 2 +- substrate/client/service/Cargo.toml | 2 +- substrate/client/service/test/Cargo.toml | 2 +- substrate/client/state-db/Cargo.toml | 2 +- substrate/client/statement-store/Cargo.toml | 2 +- substrate/client/storage-monitor/Cargo.toml | 2 +- substrate/client/sync-state-rpc/Cargo.toml | 2 +- substrate/client/sysinfo/Cargo.toml | 2 +- substrate/client/telemetry/Cargo.toml | 2 +- substrate/client/tracing/Cargo.toml | 2 +- substrate/client/tracing/proc-macro/Cargo.toml | 2 +- substrate/client/transaction-pool/Cargo.toml | 2 +- substrate/client/transaction-pool/api/Cargo.toml | 2 +- substrate/client/utils/Cargo.toml | 2 +- substrate/deprecated/hashing/Cargo.toml | 2 +- substrate/deprecated/hashing/proc-macro/Cargo.toml | 2 +- substrate/frame/asset-conversion/Cargo.toml | 2 +- substrate/frame/asset-conversion/ops/Cargo.toml | 2 +- substrate/frame/asset-rate/Cargo.toml | 2 +- substrate/frame/assets-freezer/Cargo.toml | 2 +- substrate/frame/assets/Cargo.toml | 2 +- substrate/frame/atomic-swap/Cargo.toml | 2 +- substrate/frame/aura/Cargo.toml | 2 +- substrate/frame/authority-discovery/Cargo.toml | 2 +- substrate/frame/authorship/Cargo.toml | 2 +- substrate/frame/babe/Cargo.toml | 2 +- substrate/frame/bags-list/Cargo.toml | 2 +- substrate/frame/bags-list/fuzzer/Cargo.toml | 2 +- substrate/frame/bags-list/remote-tests/Cargo.toml | 2 +- substrate/frame/balances/Cargo.toml | 2 +- substrate/frame/beefy-mmr/Cargo.toml | 2 +- substrate/frame/beefy/Cargo.toml | 2 +- substrate/frame/benchmarking/Cargo.toml | 2 +- substrate/frame/benchmarking/pov/Cargo.toml | 2 +- substrate/frame/bounties/Cargo.toml | 2 +- substrate/frame/broker/Cargo.toml | 2 +- substrate/frame/child-bounties/Cargo.toml | 2 +- substrate/frame/collective/Cargo.toml | 2 +- substrate/frame/contracts/Cargo.toml | 2 +- substrate/frame/contracts/mock-network/Cargo.toml | 2 +- substrate/frame/contracts/proc-macro/Cargo.toml | 2 +- substrate/frame/contracts/uapi/Cargo.toml | 2 +- substrate/frame/conviction-voting/Cargo.toml | 2 +- substrate/frame/core-fellowship/Cargo.toml | 2 +- substrate/frame/delegated-staking/Cargo.toml | 2 +- substrate/frame/democracy/Cargo.toml | 2 +- substrate/frame/election-provider-multi-phase/Cargo.toml | 2 +- .../election-provider-multi-phase/test-staking-e2e/Cargo.toml | 2 +- substrate/frame/election-provider-support/Cargo.toml | 2 +- .../frame/election-provider-support/benchmarking/Cargo.toml | 2 +- .../frame/election-provider-support/solution-type/Cargo.toml | 2 +- .../election-provider-support/solution-type/fuzzer/Cargo.toml | 2 +- substrate/frame/elections-phragmen/Cargo.toml | 2 +- substrate/frame/examples/Cargo.toml | 2 +- substrate/frame/examples/basic/Cargo.toml | 2 +- substrate/frame/examples/default-config/Cargo.toml | 2 +- substrate/frame/examples/dev-mode/Cargo.toml | 2 +- substrate/frame/examples/frame-crate/Cargo.toml | 2 +- substrate/frame/examples/kitchensink/Cargo.toml | 2 +- substrate/frame/examples/multi-block-migrations/Cargo.toml | 2 +- substrate/frame/examples/offchain-worker/Cargo.toml | 2 +- substrate/frame/examples/single-block-migrations/Cargo.toml | 2 +- substrate/frame/examples/split/Cargo.toml | 2 +- substrate/frame/executive/Cargo.toml | 2 +- substrate/frame/fast-unstake/Cargo.toml | 2 +- substrate/frame/glutton/Cargo.toml | 2 +- substrate/frame/grandpa/Cargo.toml | 2 +- substrate/frame/identity/Cargo.toml | 2 +- substrate/frame/im-online/Cargo.toml | 2 +- substrate/frame/indices/Cargo.toml | 2 +- .../frame/insecure-randomness-collective-flip/Cargo.toml | 2 +- substrate/frame/lottery/Cargo.toml | 2 +- substrate/frame/membership/Cargo.toml | 2 +- substrate/frame/merkle-mountain-range/Cargo.toml | 2 +- substrate/frame/message-queue/Cargo.toml | 2 +- substrate/frame/mixnet/Cargo.toml | 2 +- substrate/frame/multisig/Cargo.toml | 2 +- substrate/frame/nft-fractionalization/Cargo.toml | 2 +- substrate/frame/nfts/Cargo.toml | 2 +- substrate/frame/nfts/runtime-api/Cargo.toml | 2 +- substrate/frame/nis/Cargo.toml | 2 +- substrate/frame/node-authorization/Cargo.toml | 2 +- substrate/frame/nomination-pools/Cargo.toml | 2 +- substrate/frame/nomination-pools/benchmarking/Cargo.toml | 2 +- substrate/frame/nomination-pools/fuzzer/Cargo.toml | 2 +- substrate/frame/nomination-pools/runtime-api/Cargo.toml | 2 +- .../frame/nomination-pools/test-delegate-stake/Cargo.toml | 2 +- .../frame/nomination-pools/test-transfer-stake/Cargo.toml | 2 +- substrate/frame/offences/Cargo.toml | 2 +- substrate/frame/offences/benchmarking/Cargo.toml | 2 +- substrate/frame/paged-list/Cargo.toml | 2 +- substrate/frame/paged-list/fuzzer/Cargo.toml | 2 +- substrate/frame/preimage/Cargo.toml | 2 +- substrate/frame/proxy/Cargo.toml | 2 +- substrate/frame/ranked-collective/Cargo.toml | 2 +- substrate/frame/recovery/Cargo.toml | 2 +- substrate/frame/referenda/Cargo.toml | 2 +- substrate/frame/remark/Cargo.toml | 2 +- substrate/frame/root-offences/Cargo.toml | 2 +- substrate/frame/root-testing/Cargo.toml | 2 +- substrate/frame/safe-mode/Cargo.toml | 2 +- substrate/frame/salary/Cargo.toml | 2 +- substrate/frame/sassafras/Cargo.toml | 2 +- substrate/frame/scheduler/Cargo.toml | 2 +- substrate/frame/scored-pool/Cargo.toml | 2 +- substrate/frame/session/Cargo.toml | 2 +- substrate/frame/session/benchmarking/Cargo.toml | 2 +- substrate/frame/society/Cargo.toml | 2 +- substrate/frame/staking/Cargo.toml | 2 +- substrate/frame/staking/reward-curve/Cargo.toml | 2 +- substrate/frame/staking/reward-fn/Cargo.toml | 2 +- substrate/frame/staking/runtime-api/Cargo.toml | 2 +- substrate/frame/state-trie-migration/Cargo.toml | 2 +- substrate/frame/statement/Cargo.toml | 2 +- substrate/frame/sudo/Cargo.toml | 2 +- substrate/frame/support/Cargo.toml | 2 +- substrate/frame/support/procedural/Cargo.toml | 2 +- substrate/frame/support/procedural/tools/Cargo.toml | 2 +- substrate/frame/support/procedural/tools/derive/Cargo.toml | 2 +- substrate/frame/support/test/Cargo.toml | 2 +- substrate/frame/support/test/compile_pass/Cargo.toml | 2 +- substrate/frame/support/test/pallet/Cargo.toml | 2 +- substrate/frame/support/test/stg_frame_crate/Cargo.toml | 2 +- substrate/frame/system/Cargo.toml | 2 +- substrate/frame/system/benchmarking/Cargo.toml | 2 +- substrate/frame/system/rpc/runtime-api/Cargo.toml | 2 +- substrate/frame/timestamp/Cargo.toml | 2 +- substrate/frame/tips/Cargo.toml | 2 +- substrate/frame/transaction-payment/Cargo.toml | 2 +- .../asset-conversion-tx-payment/Cargo.toml | 2 +- .../frame/transaction-payment/asset-tx-payment/Cargo.toml | 2 +- substrate/frame/transaction-payment/rpc/Cargo.toml | 2 +- .../frame/transaction-payment/rpc/runtime-api/Cargo.toml | 2 +- substrate/frame/transaction-storage/Cargo.toml | 2 +- substrate/frame/treasury/Cargo.toml | 2 +- substrate/frame/try-runtime/Cargo.toml | 2 +- substrate/frame/tx-pause/Cargo.toml | 2 +- substrate/frame/uniques/Cargo.toml | 2 +- substrate/frame/utility/Cargo.toml | 2 +- substrate/frame/vesting/Cargo.toml | 2 +- substrate/frame/whitelist/Cargo.toml | 2 +- substrate/primitives/api/Cargo.toml | 2 +- substrate/primitives/api/proc-macro/Cargo.toml | 2 +- substrate/primitives/api/test/Cargo.toml | 2 +- substrate/primitives/application-crypto/Cargo.toml | 2 +- substrate/primitives/application-crypto/test/Cargo.toml | 2 +- substrate/primitives/arithmetic/Cargo.toml | 2 +- substrate/primitives/arithmetic/fuzzer/Cargo.toml | 2 +- substrate/primitives/authority-discovery/Cargo.toml | 2 +- substrate/primitives/block-builder/Cargo.toml | 2 +- substrate/primitives/blockchain/Cargo.toml | 2 +- substrate/primitives/consensus/aura/Cargo.toml | 2 +- substrate/primitives/consensus/babe/Cargo.toml | 2 +- substrate/primitives/consensus/beefy/Cargo.toml | 2 +- substrate/primitives/consensus/common/Cargo.toml | 2 +- substrate/primitives/consensus/grandpa/Cargo.toml | 2 +- substrate/primitives/consensus/pow/Cargo.toml | 2 +- substrate/primitives/consensus/sassafras/Cargo.toml | 2 +- substrate/primitives/consensus/slots/Cargo.toml | 2 +- substrate/primitives/core/Cargo.toml | 2 +- substrate/primitives/crypto/ec-utils/Cargo.toml | 2 +- substrate/primitives/crypto/hashing/Cargo.toml | 2 +- substrate/primitives/crypto/hashing/proc-macro/Cargo.toml | 2 +- substrate/primitives/database/Cargo.toml | 2 +- substrate/primitives/debug-derive/Cargo.toml | 2 +- substrate/primitives/externalities/Cargo.toml | 2 +- substrate/primitives/genesis-builder/Cargo.toml | 2 +- substrate/primitives/inherents/Cargo.toml | 2 +- substrate/primitives/io/Cargo.toml | 2 +- substrate/primitives/keyring/Cargo.toml | 2 +- substrate/primitives/keystore/Cargo.toml | 2 +- substrate/primitives/maybe-compressed-blob/Cargo.toml | 2 +- substrate/primitives/merkle-mountain-range/Cargo.toml | 2 +- substrate/primitives/metadata-ir/Cargo.toml | 2 +- substrate/primitives/mixnet/Cargo.toml | 2 +- substrate/primitives/npos-elections/Cargo.toml | 2 +- substrate/primitives/npos-elections/fuzzer/Cargo.toml | 2 +- substrate/primitives/offchain/Cargo.toml | 2 +- substrate/primitives/panic-handler/Cargo.toml | 2 +- substrate/primitives/rpc/Cargo.toml | 2 +- substrate/primitives/runtime-interface/Cargo.toml | 2 +- substrate/primitives/runtime-interface/proc-macro/Cargo.toml | 2 +- .../runtime-interface/test-wasm-deprecated/Cargo.toml | 2 +- substrate/primitives/runtime-interface/test-wasm/Cargo.toml | 2 +- substrate/primitives/runtime-interface/test/Cargo.toml | 2 +- substrate/primitives/runtime/Cargo.toml | 2 +- substrate/primitives/session/Cargo.toml | 2 +- substrate/primitives/staking/Cargo.toml | 2 +- substrate/primitives/state-machine/Cargo.toml | 2 +- substrate/primitives/statement-store/Cargo.toml | 2 +- substrate/primitives/std/Cargo.toml | 2 +- substrate/primitives/storage/Cargo.toml | 2 +- substrate/primitives/test-primitives/Cargo.toml | 2 +- substrate/primitives/timestamp/Cargo.toml | 2 +- substrate/primitives/tracing/Cargo.toml | 2 +- substrate/primitives/transaction-pool/Cargo.toml | 2 +- substrate/primitives/transaction-storage-proof/Cargo.toml | 2 +- substrate/primitives/trie/Cargo.toml | 2 +- substrate/primitives/version/Cargo.toml | 2 +- substrate/primitives/version/proc-macro/Cargo.toml | 2 +- substrate/primitives/wasm-interface/Cargo.toml | 2 +- substrate/primitives/weights/Cargo.toml | 2 +- substrate/scripts/ci/node-template-release/Cargo.toml | 2 +- substrate/test-utils/Cargo.toml | 2 +- substrate/test-utils/cli/Cargo.toml | 2 +- substrate/test-utils/client/Cargo.toml | 2 +- substrate/test-utils/runtime/Cargo.toml | 2 +- substrate/test-utils/runtime/client/Cargo.toml | 2 +- substrate/test-utils/runtime/transaction-pool/Cargo.toml | 2 +- substrate/utils/binary-merkle-tree/Cargo.toml | 2 +- substrate/utils/build-script-utils/Cargo.toml | 2 +- substrate/utils/fork-tree/Cargo.toml | 2 +- substrate/utils/frame/benchmarking-cli/Cargo.toml | 2 +- substrate/utils/frame/generate-bags/Cargo.toml | 2 +- substrate/utils/frame/generate-bags/node-runtime/Cargo.toml | 2 +- substrate/utils/frame/remote-externalities/Cargo.toml | 2 +- substrate/utils/frame/rpc/client/Cargo.toml | 2 +- substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml | 2 +- substrate/utils/frame/rpc/support/Cargo.toml | 2 +- substrate/utils/frame/rpc/system/Cargo.toml | 2 +- substrate/utils/prometheus/Cargo.toml | 2 +- substrate/utils/wasm-builder/Cargo.toml | 2 +- 281 files changed, 282 insertions(+), 282 deletions(-) diff --git a/.github/workflows/misc-sync-templates.yml b/.github/workflows/misc-sync-templates.yml index 6fe907c76aca..b53d129f5cb5 100644 --- a/.github/workflows/misc-sync-templates.yml +++ b/.github/workflows/misc-sync-templates.yml @@ -69,7 +69,7 @@ jobs: protobuf-compiler rustup target add wasm32-unknown-unknown rustup component add rustfmt clippy rust-src - + # 2. Yanking the template out of the monorepo workspace. - name: Use psvm to replace git references with released creates. @@ -81,7 +81,7 @@ jobs: [workspace.package] license = "MIT-0" authors = ["Parity Technologies "] - homepage = "https://substrate.io" + homepage = "https://paritytech.github.io/polkadot-sdk/" [workspace] members = [ diff --git a/Cargo.toml b/Cargo.toml index a3582fbda133..e07ec97be3a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Parity Technologies "] edition = "2021" repository = "https://github.com/paritytech/polkadot-sdk.git" license = "GPL-3.0-only" -homepage = "https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/index.html" +homepage = "https://paritytech.github.io/polkadot-sdk/" [workspace] resolver = "2" diff --git a/cumulus/pallets/collator-selection/Cargo.toml b/cumulus/pallets/collator-selection/Cargo.toml index b3512dc2ae6c..8d67db3daf8b 100644 --- a/cumulus/pallets/collator-selection/Cargo.toml +++ b/cumulus/pallets/collator-selection/Cargo.toml @@ -2,7 +2,7 @@ authors.workspace = true description = "Simple pallet to select collators for a parachain." edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true license = "Apache-2.0" name = "pallet-collator-selection" readme = "README.md" diff --git a/cumulus/pallets/session-benchmarking/Cargo.toml b/cumulus/pallets/session-benchmarking/Cargo.toml index e182ac45edeb..5af94434e0af 100644 --- a/cumulus/pallets/session-benchmarking/Cargo.toml +++ b/cumulus/pallets/session-benchmarking/Cargo.toml @@ -4,7 +4,7 @@ version = "9.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME sessions pallet benchmarking" readme = "README.md" diff --git a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml index 1a2737f3aa22..e16629302be6 100644 --- a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml @@ -4,7 +4,7 @@ version = "0.14.0" authors = ["Anonymous"] description = "A parachain for communication back and forth with XCM of assets and uniques." license = "Unlicense" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true edition.workspace = true diff --git a/substrate/bin/node/bench/Cargo.toml b/substrate/bin/node/bench/Cargo.toml index 6b061955184e..88ea908abc23 100644 --- a/substrate/bin/node/bench/Cargo.toml +++ b/substrate/bin/node/bench/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Substrate node integration benchmarks." edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml index 1856487ac9ca..5b827c9d718f 100644 --- a/substrate/bin/node/cli/Cargo.toml +++ b/substrate/bin/node/cli/Cargo.toml @@ -7,7 +7,7 @@ build = "build.rs" edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" default-run = "substrate-node" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/bin/node/inspect/Cargo.toml b/substrate/bin/node/inspect/Cargo.toml index 68769ffb4fa4..6c8a4e59f68d 100644 --- a/substrate/bin/node/inspect/Cargo.toml +++ b/substrate/bin/node/inspect/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Substrate node block inspection tool." edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/bin/node/primitives/Cargo.toml b/substrate/bin/node/primitives/Cargo.toml index de295fd59d45..87271439921a 100644 --- a/substrate/bin/node/primitives/Cargo.toml +++ b/substrate/bin/node/primitives/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Substrate node low-level primitives." edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/bin/node/rpc/Cargo.toml b/substrate/bin/node/rpc/Cargo.toml index fa1e96e67e98..2417496cde47 100644 --- a/substrate/bin/node/rpc/Cargo.toml +++ b/substrate/bin/node/rpc/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Substrate node rpc methods." edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index c1c470f1dcd6..2ad655883916 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -6,7 +6,7 @@ description = "Substrate node kitchensink runtime." edition.workspace = true build = "build.rs" license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/bin/node/testing/Cargo.toml b/substrate/bin/node/testing/Cargo.toml index 90c9ee0555cf..a5cec856717f 100644 --- a/substrate/bin/node/testing/Cargo.toml +++ b/substrate/bin/node/testing/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Test utilities for Substrate node." edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/bin/utils/chain-spec-builder/Cargo.toml b/substrate/bin/utils/chain-spec-builder/Cargo.toml index 083f2191f3c5..070cf1309175 100644 --- a/substrate/bin/utils/chain-spec-builder/Cargo.toml +++ b/substrate/bin/utils/chain-spec-builder/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true build = "build.rs" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = true description = "Utility for building chain-specification files for Substrate-based runtimes based on `sp-genesis-builder`" diff --git a/substrate/bin/utils/subkey/Cargo.toml b/substrate/bin/utils/subkey/Cargo.toml index 5aa013097c15..72677a2bd43d 100644 --- a/substrate/bin/utils/subkey/Cargo.toml +++ b/substrate/bin/utils/subkey/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Generate and restore keys for Substrate based chains such as Polkadot, Kusama and a growing number of parachains and Substrate based projects." edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/client/allocator/Cargo.toml b/substrate/client/allocator/Cargo.toml index 5a3b05aa8a98..a8b3bdc864c9 100644 --- a/substrate/client/allocator/Cargo.toml +++ b/substrate/client/allocator/Cargo.toml @@ -4,7 +4,7 @@ version = "23.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Collection of allocator implementations." documentation = "https://docs.rs/sc-allocator" diff --git a/substrate/client/api/Cargo.toml b/substrate/client/api/Cargo.toml index a64ee3ab4ce1..670c74684467 100644 --- a/substrate/client/api/Cargo.toml +++ b/substrate/client/api/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate client interfaces." documentation = "https://docs.rs/sc-client-api" diff --git a/substrate/client/authority-discovery/Cargo.toml b/substrate/client/authority-discovery/Cargo.toml index 309c9c542a0b..09381ec6b553 100644 --- a/substrate/client/authority-discovery/Cargo.toml +++ b/substrate/client/authority-discovery/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true build = "build.rs" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate authority discovery." readme = "README.md" diff --git a/substrate/client/basic-authorship/Cargo.toml b/substrate/client/basic-authorship/Cargo.toml index e3ae80e14f6f..cc2e0d8d04df 100644 --- a/substrate/client/basic-authorship/Cargo.toml +++ b/substrate/client/basic-authorship/Cargo.toml @@ -4,7 +4,7 @@ version = "0.34.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Basic implementation of block-authoring logic." readme = "README.md" diff --git a/substrate/client/block-builder/Cargo.toml b/substrate/client/block-builder/Cargo.toml index 47e3fc39c289..08392e18227f 100644 --- a/substrate/client/block-builder/Cargo.toml +++ b/substrate/client/block-builder/Cargo.toml @@ -4,7 +4,7 @@ version = "0.33.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate block builder" readme = "README.md" diff --git a/substrate/client/chain-spec/Cargo.toml b/substrate/client/chain-spec/Cargo.toml index b3cd4bd57db8..2e885240936f 100644 --- a/substrate/client/chain-spec/Cargo.toml +++ b/substrate/client/chain-spec/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate chain configurations." readme = "README.md" diff --git a/substrate/client/chain-spec/derive/Cargo.toml b/substrate/client/chain-spec/derive/Cargo.toml index 4ab8c849cc7f..ccd898447bea 100644 --- a/substrate/client/chain-spec/derive/Cargo.toml +++ b/substrate/client/chain-spec/derive/Cargo.toml @@ -4,7 +4,7 @@ version = "11.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Macros to derive chain spec extension traits implementation." diff --git a/substrate/client/cli/Cargo.toml b/substrate/client/cli/Cargo.toml index 1e4017c23af2..b7d29aebc3d7 100644 --- a/substrate/client/cli/Cargo.toml +++ b/substrate/client/cli/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Substrate CLI interface." edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/client/consensus/aura/Cargo.toml b/substrate/client/consensus/aura/Cargo.toml index 3a3d7ae18d71..98e8ad676be3 100644 --- a/substrate/client/consensus/aura/Cargo.toml +++ b/substrate/client/consensus/aura/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Aura consensus algorithm for substrate" edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/client/consensus/babe/Cargo.toml b/substrate/client/consensus/babe/Cargo.toml index bba60bc45ea5..af55e72a9b7e 100644 --- a/substrate/client/consensus/babe/Cargo.toml +++ b/substrate/client/consensus/babe/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "BABE consensus algorithm for substrate" edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sc-consensus-babe" readme = "README.md" diff --git a/substrate/client/consensus/babe/rpc/Cargo.toml b/substrate/client/consensus/babe/rpc/Cargo.toml index 1ef049c3dbcc..ce5b1baec0b5 100644 --- a/substrate/client/consensus/babe/rpc/Cargo.toml +++ b/substrate/client/consensus/babe/rpc/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "RPC extensions for the BABE consensus algorithm" edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/client/consensus/beefy/Cargo.toml b/substrate/client/consensus/beefy/Cargo.toml index b2031e0d1e07..900a44b95e04 100644 --- a/substrate/client/consensus/beefy/Cargo.toml +++ b/substrate/client/consensus/beefy/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true description = "BEEFY Client gadget for substrate" -homepage = "https://substrate.io" +homepage.workspace = true [lints] workspace = true diff --git a/substrate/client/consensus/beefy/rpc/Cargo.toml b/substrate/client/consensus/beefy/rpc/Cargo.toml index 7869f5a336b1..e1956dacf396 100644 --- a/substrate/client/consensus/beefy/rpc/Cargo.toml +++ b/substrate/client/consensus/beefy/rpc/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true description = "RPC for the BEEFY Client gadget for substrate" -homepage = "https://substrate.io" +homepage.workspace = true [lints] workspace = true diff --git a/substrate/client/consensus/common/Cargo.toml b/substrate/client/consensus/common/Cargo.toml index a6f59e600f26..77cd50ad784b 100644 --- a/substrate/client/consensus/common/Cargo.toml +++ b/substrate/client/consensus/common/Cargo.toml @@ -4,7 +4,7 @@ version = "0.33.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Collection of common consensus specific implementations for Substrate (client)" readme = "README.md" diff --git a/substrate/client/consensus/epochs/Cargo.toml b/substrate/client/consensus/epochs/Cargo.toml index 127cc9ebec20..c51671d6d75d 100644 --- a/substrate/client/consensus/epochs/Cargo.toml +++ b/substrate/client/consensus/epochs/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Generic epochs-based utilities for consensus" edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/client/consensus/grandpa/Cargo.toml b/substrate/client/consensus/grandpa/Cargo.toml index e49c7c9f0d7a..65ba39d34c21 100644 --- a/substrate/client/consensus/grandpa/Cargo.toml +++ b/substrate/client/consensus/grandpa/Cargo.toml @@ -4,7 +4,7 @@ version = "0.19.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Integration of the GRANDPA finality gadget into substrate." documentation = "https://docs.rs/sc-consensus-grandpa" diff --git a/substrate/client/consensus/grandpa/rpc/Cargo.toml b/substrate/client/consensus/grandpa/rpc/Cargo.toml index 0215fe2e3e64..86513ac5df15 100644 --- a/substrate/client/consensus/grandpa/rpc/Cargo.toml +++ b/substrate/client/consensus/grandpa/rpc/Cargo.toml @@ -7,7 +7,7 @@ repository.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" readme = "README.md" -homepage = "https://substrate.io" +homepage.workspace = true [lints] workspace = true diff --git a/substrate/client/consensus/manual-seal/Cargo.toml b/substrate/client/consensus/manual-seal/Cargo.toml index 3d74eda8fa01..49111434015a 100644 --- a/substrate/client/consensus/manual-seal/Cargo.toml +++ b/substrate/client/consensus/manual-seal/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Manual sealing engine for Substrate" edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/client/consensus/pow/Cargo.toml b/substrate/client/consensus/pow/Cargo.toml index f2a071ec25c2..bc89deb0b50d 100644 --- a/substrate/client/consensus/pow/Cargo.toml +++ b/substrate/client/consensus/pow/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "PoW consensus algorithm for substrate" edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/client/consensus/slots/Cargo.toml b/substrate/client/consensus/slots/Cargo.toml index 2b795b13f8e3..cc39575efe82 100644 --- a/substrate/client/consensus/slots/Cargo.toml +++ b/substrate/client/consensus/slots/Cargo.toml @@ -6,7 +6,7 @@ description = "Generic slots-based utilities for consensus" edition.workspace = true build = "build.rs" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/client/db/Cargo.toml b/substrate/client/db/Cargo.toml index c8372701ac32..5725155579fc 100644 --- a/substrate/client/db/Cargo.toml +++ b/substrate/client/db/Cargo.toml @@ -4,7 +4,7 @@ version = "0.35.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Client backend that uses RocksDB database as storage." readme = "README.md" diff --git a/substrate/client/executor/Cargo.toml b/substrate/client/executor/Cargo.toml index c10c60822ff8..2c24663bccca 100644 --- a/substrate/client/executor/Cargo.toml +++ b/substrate/client/executor/Cargo.toml @@ -4,7 +4,7 @@ version = "0.32.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "A crate that provides means of executing/dispatching calls into the runtime." documentation = "https://docs.rs/sc-executor" diff --git a/substrate/client/executor/common/Cargo.toml b/substrate/client/executor/common/Cargo.toml index e985c75ca908..58fb0b423f24 100644 --- a/substrate/client/executor/common/Cargo.toml +++ b/substrate/client/executor/common/Cargo.toml @@ -4,7 +4,7 @@ version = "0.29.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "A set of common definitions that are needed for defining execution engines." documentation = "https://docs.rs/sc-executor-common/" diff --git a/substrate/client/executor/polkavm/Cargo.toml b/substrate/client/executor/polkavm/Cargo.toml index 8b849209a07c..941c830ba16a 100644 --- a/substrate/client/executor/polkavm/Cargo.toml +++ b/substrate/client/executor/polkavm/Cargo.toml @@ -4,7 +4,7 @@ version = "0.29.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "PolkaVM executor for Substrate" readme = "README.md" diff --git a/substrate/client/executor/runtime-test/Cargo.toml b/substrate/client/executor/runtime-test/Cargo.toml index d132f47ff877..5ab92cbb9332 100644 --- a/substrate/client/executor/runtime-test/Cargo.toml +++ b/substrate/client/executor/runtime-test/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true build = "build.rs" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" publish = false -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/client/executor/wasmtime/Cargo.toml b/substrate/client/executor/wasmtime/Cargo.toml index e58b19bb1243..ef8e5da876aa 100644 --- a/substrate/client/executor/wasmtime/Cargo.toml +++ b/substrate/client/executor/wasmtime/Cargo.toml @@ -4,7 +4,7 @@ version = "0.29.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Defines a `WasmRuntime` that uses the Wasmtime JIT to execute." readme = "README.md" diff --git a/substrate/client/informant/Cargo.toml b/substrate/client/informant/Cargo.toml index aa54d9479f5c..87a4be320d68 100644 --- a/substrate/client/informant/Cargo.toml +++ b/substrate/client/informant/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Substrate informant." edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/client/keystore/Cargo.toml b/substrate/client/keystore/Cargo.toml index cf128016370b..d338bb1af61a 100644 --- a/substrate/client/keystore/Cargo.toml +++ b/substrate/client/keystore/Cargo.toml @@ -4,7 +4,7 @@ version = "25.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Keystore (and session key management) for ed25519 based chains like Polkadot." documentation = "https://docs.rs/sc-keystore" diff --git a/substrate/client/merkle-mountain-range/Cargo.toml b/substrate/client/merkle-mountain-range/Cargo.toml index e219d36d3f79..6639a10d33f1 100644 --- a/substrate/client/merkle-mountain-range/Cargo.toml +++ b/substrate/client/merkle-mountain-range/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true description = "MMR Client gadget for substrate" -homepage = "https://substrate.io" +homepage.workspace = true [lints] workspace = true diff --git a/substrate/client/merkle-mountain-range/rpc/Cargo.toml b/substrate/client/merkle-mountain-range/rpc/Cargo.toml index 5f856b4069a0..c2b29aca4601 100644 --- a/substrate/client/merkle-mountain-range/rpc/Cargo.toml +++ b/substrate/client/merkle-mountain-range/rpc/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Node-specific RPC methods for interaction with Merkle Mountain Range pallet." diff --git a/substrate/client/mixnet/Cargo.toml b/substrate/client/mixnet/Cargo.toml index 2b06c3eca234..b6c63406a541 100644 --- a/substrate/client/mixnet/Cargo.toml +++ b/substrate/client/mixnet/Cargo.toml @@ -5,7 +5,7 @@ version = "0.4.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors = ["Parity Technologies "] edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository = "https://github.com/paritytech/substrate/" readme = "README.md" diff --git a/substrate/client/network-gossip/Cargo.toml b/substrate/client/network-gossip/Cargo.toml index f1441e4a1cf2..94bc9a671f84 100644 --- a/substrate/client/network-gossip/Cargo.toml +++ b/substrate/client/network-gossip/Cargo.toml @@ -5,7 +5,7 @@ version = "0.34.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sc-network-gossip" readme = "README.md" diff --git a/substrate/client/network/Cargo.toml b/substrate/client/network/Cargo.toml index a0cf42eaab22..8ae3de72f796 100644 --- a/substrate/client/network/Cargo.toml +++ b/substrate/client/network/Cargo.toml @@ -5,7 +5,7 @@ version = "0.34.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sc-network" readme = "README.md" diff --git a/substrate/client/network/common/Cargo.toml b/substrate/client/network/common/Cargo.toml index 79326492159b..cd1bc1cfe8eb 100644 --- a/substrate/client/network/common/Cargo.toml +++ b/substrate/client/network/common/Cargo.toml @@ -5,7 +5,7 @@ version = "0.33.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sc-network-sync" diff --git a/substrate/client/network/light/Cargo.toml b/substrate/client/network/light/Cargo.toml index 52deaa93852a..34ba4f061c44 100644 --- a/substrate/client/network/light/Cargo.toml +++ b/substrate/client/network/light/Cargo.toml @@ -5,7 +5,7 @@ version = "0.33.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sc-network-light" diff --git a/substrate/client/network/statement/Cargo.toml b/substrate/client/network/statement/Cargo.toml index 4cced49fee79..43933f066edd 100644 --- a/substrate/client/network/statement/Cargo.toml +++ b/substrate/client/network/statement/Cargo.toml @@ -5,7 +5,7 @@ version = "0.16.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sc-network-statement" diff --git a/substrate/client/network/sync/Cargo.toml b/substrate/client/network/sync/Cargo.toml index 17e3e2119d7e..b29a9ccaaf1a 100644 --- a/substrate/client/network/sync/Cargo.toml +++ b/substrate/client/network/sync/Cargo.toml @@ -5,7 +5,7 @@ version = "0.33.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sc-network-sync" diff --git a/substrate/client/network/test/Cargo.toml b/substrate/client/network/test/Cargo.toml index 74b43173508b..ebece1762f29 100644 --- a/substrate/client/network/test/Cargo.toml +++ b/substrate/client/network/test/Cargo.toml @@ -6,7 +6,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true publish = false -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/client/network/transactions/Cargo.toml b/substrate/client/network/transactions/Cargo.toml index eb907b606d58..2ffd6f5f4660 100644 --- a/substrate/client/network/transactions/Cargo.toml +++ b/substrate/client/network/transactions/Cargo.toml @@ -5,7 +5,7 @@ version = "0.33.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sc-network-transactions" diff --git a/substrate/client/network/types/Cargo.toml b/substrate/client/network/types/Cargo.toml index 811ccddbef93..655f104111e4 100644 --- a/substrate/client/network/types/Cargo.toml +++ b/substrate/client/network/types/Cargo.toml @@ -5,7 +5,7 @@ version = "0.10.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sc-network-types" diff --git a/substrate/client/offchain/Cargo.toml b/substrate/client/offchain/Cargo.toml index 12e017317b22..4b5b04cca627 100644 --- a/substrate/client/offchain/Cargo.toml +++ b/substrate/client/offchain/Cargo.toml @@ -5,7 +5,7 @@ version = "29.0.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/client/proposer-metrics/Cargo.toml b/substrate/client/proposer-metrics/Cargo.toml index 98064049b297..b42e6329f991 100644 --- a/substrate/client/proposer-metrics/Cargo.toml +++ b/substrate/client/proposer-metrics/Cargo.toml @@ -4,7 +4,7 @@ version = "0.17.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Basic metrics for block production." readme = "README.md" diff --git a/substrate/client/rpc-api/Cargo.toml b/substrate/client/rpc-api/Cargo.toml index fda81b31ee50..3263285aa2b1 100644 --- a/substrate/client/rpc-api/Cargo.toml +++ b/substrate/client/rpc-api/Cargo.toml @@ -4,7 +4,7 @@ version = "0.33.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate RPC interfaces." readme = "README.md" diff --git a/substrate/client/rpc-servers/Cargo.toml b/substrate/client/rpc-servers/Cargo.toml index 2f51d42bc150..7e3d3bf55b74 100644 --- a/substrate/client/rpc-servers/Cargo.toml +++ b/substrate/client/rpc-servers/Cargo.toml @@ -4,7 +4,7 @@ version = "11.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate RPC servers." readme = "README.md" diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index 0fcf5fd34e82..1ec7895e3088 100644 --- a/substrate/client/rpc-spec-v2/Cargo.toml +++ b/substrate/client/rpc-spec-v2/Cargo.toml @@ -4,7 +4,7 @@ version = "0.34.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate RPC interface v2." readme = "README.md" diff --git a/substrate/client/rpc/Cargo.toml b/substrate/client/rpc/Cargo.toml index 545d02bb3043..e8d9ad4948d7 100644 --- a/substrate/client/rpc/Cargo.toml +++ b/substrate/client/rpc/Cargo.toml @@ -4,7 +4,7 @@ version = "29.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate Client RPC" readme = "README.md" diff --git a/substrate/client/service/Cargo.toml b/substrate/client/service/Cargo.toml index 724e2ddfe56a..f2fc65ef2439 100644 --- a/substrate/client/service/Cargo.toml +++ b/substrate/client/service/Cargo.toml @@ -4,7 +4,7 @@ version = "0.35.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate service. Starts a thread that spins up the network, client, and extrinsic pool. Manages communication between them." readme = "README.md" diff --git a/substrate/client/service/test/Cargo.toml b/substrate/client/service/test/Cargo.toml index ade7b3b4e6a0..0edfc5b19314 100644 --- a/substrate/client/service/test/Cargo.toml +++ b/substrate/client/service/test/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" publish = false -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/client/state-db/Cargo.toml b/substrate/client/state-db/Cargo.toml index be4f1ef973c9..db6944fdbd2c 100644 --- a/substrate/client/state-db/Cargo.toml +++ b/substrate/client/state-db/Cargo.toml @@ -4,7 +4,7 @@ version = "0.30.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "State database maintenance. Handles canonicalization and pruning in the database." readme = "README.md" diff --git a/substrate/client/statement-store/Cargo.toml b/substrate/client/statement-store/Cargo.toml index 1cb682f054d7..ef0bfc548673 100644 --- a/substrate/client/statement-store/Cargo.toml +++ b/substrate/client/statement-store/Cargo.toml @@ -4,7 +4,7 @@ version = "10.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate statement store." readme = "README.md" diff --git a/substrate/client/storage-monitor/Cargo.toml b/substrate/client/storage-monitor/Cargo.toml index 8c490284dccc..c017184ced66 100644 --- a/substrate/client/storage-monitor/Cargo.toml +++ b/substrate/client/storage-monitor/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true description = "Storage monitor service for substrate" -homepage = "https://substrate.io" +homepage.workspace = true [lints] workspace = true diff --git a/substrate/client/sync-state-rpc/Cargo.toml b/substrate/client/sync-state-rpc/Cargo.toml index 1cbaadb70fff..cbab8f4d7b0d 100644 --- a/substrate/client/sync-state-rpc/Cargo.toml +++ b/substrate/client/sync-state-rpc/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "A RPC handler to create sync states for light clients." edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/client/sysinfo/Cargo.toml b/substrate/client/sysinfo/Cargo.toml index f79345d67242..190e6e279b90 100644 --- a/substrate/client/sysinfo/Cargo.toml +++ b/substrate/client/sysinfo/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "A crate that provides basic hardware and software telemetry information." documentation = "https://docs.rs/sc-sysinfo" diff --git a/substrate/client/telemetry/Cargo.toml b/substrate/client/telemetry/Cargo.toml index a789ebc5f1af..f87e8b66f731 100644 --- a/substrate/client/telemetry/Cargo.toml +++ b/substrate/client/telemetry/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Telemetry utils" edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sc-telemetry" readme = "README.md" diff --git a/substrate/client/tracing/Cargo.toml b/substrate/client/tracing/Cargo.toml index f39c5ecb4e43..09571610a3a6 100644 --- a/substrate/client/tracing/Cargo.toml +++ b/substrate/client/tracing/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Instrumentation implementation for substrate." readme = "README.md" diff --git a/substrate/client/tracing/proc-macro/Cargo.toml b/substrate/client/tracing/proc-macro/Cargo.toml index 9162bdc1ad8a..4187235b4e38 100644 --- a/substrate/client/tracing/proc-macro/Cargo.toml +++ b/substrate/client/tracing/proc-macro/Cargo.toml @@ -4,7 +4,7 @@ version = "11.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Helper macros for Substrate's client CLI" diff --git a/substrate/client/transaction-pool/Cargo.toml b/substrate/client/transaction-pool/Cargo.toml index 95b391faf169..98994cc742ff 100644 --- a/substrate/client/transaction-pool/Cargo.toml +++ b/substrate/client/transaction-pool/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate transaction pool implementation." readme = "README.md" diff --git a/substrate/client/transaction-pool/api/Cargo.toml b/substrate/client/transaction-pool/api/Cargo.toml index 6fec613c1114..c55ee70b2cf5 100644 --- a/substrate/client/transaction-pool/api/Cargo.toml +++ b/substrate/client/transaction-pool/api/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Transaction pool client facing API." diff --git a/substrate/client/utils/Cargo.toml b/substrate/client/utils/Cargo.toml index 4053d77a13eb..6c3a2228952e 100644 --- a/substrate/client/utils/Cargo.toml +++ b/substrate/client/utils/Cargo.toml @@ -4,7 +4,7 @@ version = "14.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "I/O for Substrate runtimes" readme = "README.md" diff --git a/substrate/deprecated/hashing/Cargo.toml b/substrate/deprecated/hashing/Cargo.toml index 9db6cb64050d..ab7245a5ca2c 100644 --- a/substrate/deprecated/hashing/Cargo.toml +++ b/substrate/deprecated/hashing/Cargo.toml @@ -4,7 +4,7 @@ version = "15.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Hashing primitives (deprecated: use sp-crypto-hashing for new applications)" documentation = "https://docs.rs/sp-crypto-hashing" diff --git a/substrate/deprecated/hashing/proc-macro/Cargo.toml b/substrate/deprecated/hashing/proc-macro/Cargo.toml index ec5188bc53bd..99906d07a9c2 100644 --- a/substrate/deprecated/hashing/proc-macro/Cargo.toml +++ b/substrate/deprecated/hashing/proc-macro/Cargo.toml @@ -4,7 +4,7 @@ version = "15.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Procedural macros for calculating static hashes (deprecated in favor of `sp-crypto-hashing-proc-macro`)." documentation = "https://docs.rs/sp-crypto-hashing-proc-macro" diff --git a/substrate/frame/asset-conversion/Cargo.toml b/substrate/frame/asset-conversion/Cargo.toml index 4e867ece1bd2..10a118e95639 100644 --- a/substrate/frame/asset-conversion/Cargo.toml +++ b/substrate/frame/asset-conversion/Cargo.toml @@ -4,7 +4,7 @@ version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME asset conversion pallet" readme = "README.md" diff --git a/substrate/frame/asset-conversion/ops/Cargo.toml b/substrate/frame/asset-conversion/ops/Cargo.toml index 97a676fde10d..66333f973d7f 100644 --- a/substrate/frame/asset-conversion/ops/Cargo.toml +++ b/substrate/frame/asset-conversion/ops/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME asset conversion pallet's operations suite" diff --git a/substrate/frame/asset-rate/Cargo.toml b/substrate/frame/asset-rate/Cargo.toml index 4aeb6a39e824..514b6fa40c2b 100644 --- a/substrate/frame/asset-rate/Cargo.toml +++ b/substrate/frame/asset-rate/Cargo.toml @@ -3,7 +3,7 @@ name = "pallet-asset-rate" version = "7.0.0" description = "Whitelist non-native assets for treasury spending and provide conversion to native balance" authors.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true edition.workspace = true license = "Apache-2.0" repository.workspace = true diff --git a/substrate/frame/assets-freezer/Cargo.toml b/substrate/frame/assets-freezer/Cargo.toml index 97eadd8465a7..68bfdd7cfb62 100644 --- a/substrate/frame/assets-freezer/Cargo.toml +++ b/substrate/frame/assets-freezer/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license = "MIT-0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Provides freezing features to `pallet-assets`" diff --git a/substrate/frame/assets/Cargo.toml b/substrate/frame/assets/Cargo.toml index 802b8b9f39d8..fcb151298f09 100644 --- a/substrate/frame/assets/Cargo.toml +++ b/substrate/frame/assets/Cargo.toml @@ -4,7 +4,7 @@ version = "29.1.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME asset management pallet" readme = "README.md" diff --git a/substrate/frame/atomic-swap/Cargo.toml b/substrate/frame/atomic-swap/Cargo.toml index 033100630129..db89a58da8f0 100644 --- a/substrate/frame/atomic-swap/Cargo.toml +++ b/substrate/frame/atomic-swap/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME atomic swap pallet" readme = "README.md" diff --git a/substrate/frame/aura/Cargo.toml b/substrate/frame/aura/Cargo.toml index f9d745927638..94b057d665d4 100644 --- a/substrate/frame/aura/Cargo.toml +++ b/substrate/frame/aura/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME AURA consensus pallet" readme = "README.md" diff --git a/substrate/frame/authority-discovery/Cargo.toml b/substrate/frame/authority-discovery/Cargo.toml index 9d55a7fd5dcb..01f574a262ad 100644 --- a/substrate/frame/authority-discovery/Cargo.toml +++ b/substrate/frame/authority-discovery/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for authority discovery" readme = "README.md" diff --git a/substrate/frame/authorship/Cargo.toml b/substrate/frame/authorship/Cargo.toml index 8ecacca93e0a..74a4a93147a8 100644 --- a/substrate/frame/authorship/Cargo.toml +++ b/substrate/frame/authorship/Cargo.toml @@ -5,7 +5,7 @@ description = "Block and Uncle Author tracking for the FRAME" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/frame/babe/Cargo.toml b/substrate/frame/babe/Cargo.toml index 3dfda3b618be..f0a7f4648c0a 100644 --- a/substrate/frame/babe/Cargo.toml +++ b/substrate/frame/babe/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Consensus extension module for BABE consensus. Collects on-chain randomness from VRF outputs and manages epoch transitions." readme = "README.md" diff --git a/substrate/frame/bags-list/Cargo.toml b/substrate/frame/bags-list/Cargo.toml index 549c4334b55d..647f5d26686a 100644 --- a/substrate/frame/bags-list/Cargo.toml +++ b/substrate/frame/bags-list/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet bags list" diff --git a/substrate/frame/bags-list/fuzzer/Cargo.toml b/substrate/frame/bags-list/fuzzer/Cargo.toml index f8631be159cd..b52fc8848237 100644 --- a/substrate/frame/bags-list/fuzzer/Cargo.toml +++ b/substrate/frame/bags-list/fuzzer/Cargo.toml @@ -4,7 +4,7 @@ version = "4.0.0-dev" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Fuzzer for FRAME pallet bags list" publish = false diff --git a/substrate/frame/bags-list/remote-tests/Cargo.toml b/substrate/frame/bags-list/remote-tests/Cargo.toml index 45f2498aa88b..12d61b61c06d 100644 --- a/substrate/frame/bags-list/remote-tests/Cargo.toml +++ b/substrate/frame/bags-list/remote-tests/Cargo.toml @@ -4,7 +4,7 @@ version = "4.0.0-dev" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet bags list remote test" publish = false diff --git a/substrate/frame/balances/Cargo.toml b/substrate/frame/balances/Cargo.toml index 85734dce9e9b..44899e5b7d8d 100644 --- a/substrate/frame/balances/Cargo.toml +++ b/substrate/frame/balances/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet to manage balances" readme = "README.md" diff --git a/substrate/frame/beefy-mmr/Cargo.toml b/substrate/frame/beefy-mmr/Cargo.toml index 92e903af138d..672a7a68bc7e 100644 --- a/substrate/frame/beefy-mmr/Cargo.toml +++ b/substrate/frame/beefy-mmr/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "Apache-2.0" description = "BEEFY + MMR runtime utilities" repository.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true [lints] workspace = true diff --git a/substrate/frame/beefy/Cargo.toml b/substrate/frame/beefy/Cargo.toml index 089b817e226f..05af974e89a7 100644 --- a/substrate/frame/beefy/Cargo.toml +++ b/substrate/frame/beefy/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "Apache-2.0" repository.workspace = true description = "BEEFY FRAME pallet" -homepage = "https://substrate.io" +homepage.workspace = true [lints] workspace = true diff --git a/substrate/frame/benchmarking/Cargo.toml b/substrate/frame/benchmarking/Cargo.toml index 61bc706963bb..9ea350a1d290 100644 --- a/substrate/frame/benchmarking/Cargo.toml +++ b/substrate/frame/benchmarking/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Macro for benchmarking a FRAME runtime." readme = "README.md" diff --git a/substrate/frame/benchmarking/pov/Cargo.toml b/substrate/frame/benchmarking/pov/Cargo.toml index 6a449fb597d5..ce89dceed3c3 100644 --- a/substrate/frame/benchmarking/pov/Cargo.toml +++ b/substrate/frame/benchmarking/pov/Cargo.toml @@ -4,7 +4,7 @@ version = "18.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Pallet for testing FRAME PoV benchmarking" diff --git a/substrate/frame/bounties/Cargo.toml b/substrate/frame/bounties/Cargo.toml index cb9fa1f964b0..a272153fed07 100644 --- a/substrate/frame/bounties/Cargo.toml +++ b/substrate/frame/bounties/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet to manage bounties" readme = "README.md" diff --git a/substrate/frame/broker/Cargo.toml b/substrate/frame/broker/Cargo.toml index 7593a4e8da14..aead49013ef0 100644 --- a/substrate/frame/broker/Cargo.toml +++ b/substrate/frame/broker/Cargo.toml @@ -3,7 +3,7 @@ name = "pallet-broker" version = "0.6.0" description = "Brokerage tool for managing Polkadot Core scheduling" authors.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true edition.workspace = true license = "Apache-2.0" repository.workspace = true diff --git a/substrate/frame/child-bounties/Cargo.toml b/substrate/frame/child-bounties/Cargo.toml index 9c979b41462c..a250886b5e3d 100644 --- a/substrate/frame/child-bounties/Cargo.toml +++ b/substrate/frame/child-bounties/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet to manage child bounties" readme = "README.md" diff --git a/substrate/frame/collective/Cargo.toml b/substrate/frame/collective/Cargo.toml index 7d9a4c41c812..f6810f26f056 100644 --- a/substrate/frame/collective/Cargo.toml +++ b/substrate/frame/collective/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Collective system: Members of a set of account IDs can make their collective feelings known through dispatched calls from one of two specialized origins." readme = "README.md" diff --git a/substrate/frame/contracts/Cargo.toml b/substrate/frame/contracts/Cargo.toml index 252151fb9193..1aa3d3513a7e 100644 --- a/substrate/frame/contracts/Cargo.toml +++ b/substrate/frame/contracts/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true build = "build.rs" license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for WASM contracts" readme = "README.md" diff --git a/substrate/frame/contracts/mock-network/Cargo.toml b/substrate/frame/contracts/mock-network/Cargo.toml index 85d98206ea57..d6e2d51ef452 100644 --- a/substrate/frame/contracts/mock-network/Cargo.toml +++ b/substrate/frame/contracts/mock-network/Cargo.toml @@ -4,7 +4,7 @@ version = "3.0.0" authors.workspace = true edition.workspace = true license.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "A mock network for testing pallet-contracts" diff --git a/substrate/frame/contracts/proc-macro/Cargo.toml b/substrate/frame/contracts/proc-macro/Cargo.toml index 3651b172d75f..b2c5b4deacf7 100644 --- a/substrate/frame/contracts/proc-macro/Cargo.toml +++ b/substrate/frame/contracts/proc-macro/Cargo.toml @@ -4,7 +4,7 @@ version = "18.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Procedural macros used in pallet_contracts" diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index cb559ec88d28..ccd86ba7890c 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -4,7 +4,7 @@ version = "5.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Exposes all the host functions that a contract can import." diff --git a/substrate/frame/conviction-voting/Cargo.toml b/substrate/frame/conviction-voting/Cargo.toml index 6184b0ffd6e6..fdb4310610d9 100644 --- a/substrate/frame/conviction-voting/Cargo.toml +++ b/substrate/frame/conviction-voting/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for conviction voting in referenda" readme = "README.md" diff --git a/substrate/frame/core-fellowship/Cargo.toml b/substrate/frame/core-fellowship/Cargo.toml index 7ef6f9e11eb1..3d73ec58d613 100644 --- a/substrate/frame/core-fellowship/Cargo.toml +++ b/substrate/frame/core-fellowship/Cargo.toml @@ -4,7 +4,7 @@ version = "12.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Logic as per the description of The Fellowship for core Polkadot technology" readme = "README.md" diff --git a/substrate/frame/delegated-staking/Cargo.toml b/substrate/frame/delegated-staking/Cargo.toml index a7751e1d6c1c..1bd17c59f1e7 100644 --- a/substrate/frame/delegated-staking/Cargo.toml +++ b/substrate/frame/delegated-staking/Cargo.toml @@ -4,7 +4,7 @@ version = "1.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME delegated staking pallet" diff --git a/substrate/frame/democracy/Cargo.toml b/substrate/frame/democracy/Cargo.toml index ce5ffa57d579..3cfea8bb3129 100644 --- a/substrate/frame/democracy/Cargo.toml +++ b/substrate/frame/democracy/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for democracy" readme = "README.md" diff --git a/substrate/frame/election-provider-multi-phase/Cargo.toml b/substrate/frame/election-provider-multi-phase/Cargo.toml index 4d1f521d726e..ff2a997fafe0 100644 --- a/substrate/frame/election-provider-multi-phase/Cargo.toml +++ b/substrate/frame/election-provider-multi-phase/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "PALLET two phase election providers" diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml b/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml index 77ecbb1af98f..771376e06656 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml @@ -4,7 +4,7 @@ version = "1.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME election provider multi phase pallet tests with staking pallet, bags-list and session pallets" publish = false diff --git a/substrate/frame/election-provider-support/Cargo.toml b/substrate/frame/election-provider-support/Cargo.toml index 80fea6c5ca97..cae20d1b46a4 100644 --- a/substrate/frame/election-provider-support/Cargo.toml +++ b/substrate/frame/election-provider-support/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "election provider supporting traits" diff --git a/substrate/frame/election-provider-support/benchmarking/Cargo.toml b/substrate/frame/election-provider-support/benchmarking/Cargo.toml index dcfcb9c35cda..4a402c850269 100644 --- a/substrate/frame/election-provider-support/benchmarking/Cargo.toml +++ b/substrate/frame/election-provider-support/benchmarking/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Benchmarking for election provider support onchain config trait" diff --git a/substrate/frame/election-provider-support/solution-type/Cargo.toml b/substrate/frame/election-provider-support/solution-type/Cargo.toml index a254f6c9b5b7..30b00712b3f5 100644 --- a/substrate/frame/election-provider-support/solution-type/Cargo.toml +++ b/substrate/frame/election-provider-support/solution-type/Cargo.toml @@ -4,7 +4,7 @@ version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "NPoS Solution Type" diff --git a/substrate/frame/election-provider-support/solution-type/fuzzer/Cargo.toml b/substrate/frame/election-provider-support/solution-type/fuzzer/Cargo.toml index 2c7a7aea1ca2..86abbf9677e0 100644 --- a/substrate/frame/election-provider-support/solution-type/fuzzer/Cargo.toml +++ b/substrate/frame/election-provider-support/solution-type/fuzzer/Cargo.toml @@ -4,7 +4,7 @@ version = "2.0.0-alpha.5" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Fuzzer for phragmén solution type implementation." publish = false diff --git a/substrate/frame/elections-phragmen/Cargo.toml b/substrate/frame/elections-phragmen/Cargo.toml index 6d1ecbd07350..c1b12b3da4d8 100644 --- a/substrate/frame/elections-phragmen/Cargo.toml +++ b/substrate/frame/elections-phragmen/Cargo.toml @@ -4,7 +4,7 @@ version = "29.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet based on seq-Phragmén election method." readme = "README.md" diff --git a/substrate/frame/examples/Cargo.toml b/substrate/frame/examples/Cargo.toml index 0bb42517eb46..ee0f8df29cf5 100644 --- a/substrate/frame/examples/Cargo.toml +++ b/substrate/frame/examples/Cargo.toml @@ -4,7 +4,7 @@ version = "4.0.0-dev" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "The single package with examples of various types of FRAME pallets" publish = false diff --git a/substrate/frame/examples/basic/Cargo.toml b/substrate/frame/examples/basic/Cargo.toml index af547c7eeb21..f7e2b653c2d1 100644 --- a/substrate/frame/examples/basic/Cargo.toml +++ b/substrate/frame/examples/basic/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "MIT-0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME example pallet" readme = "README.md" diff --git a/substrate/frame/examples/default-config/Cargo.toml b/substrate/frame/examples/default-config/Cargo.toml index 81509c782a3f..fa376b4f9136 100644 --- a/substrate/frame/examples/default-config/Cargo.toml +++ b/substrate/frame/examples/default-config/Cargo.toml @@ -4,7 +4,7 @@ version = "10.0.0" authors.workspace = true edition.workspace = true license = "MIT-0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME example pallet demonstrating derive_impl / default_config in action" readme = "README.md" diff --git a/substrate/frame/examples/dev-mode/Cargo.toml b/substrate/frame/examples/dev-mode/Cargo.toml index c3dd7f26f21d..6625fb3a5851 100644 --- a/substrate/frame/examples/dev-mode/Cargo.toml +++ b/substrate/frame/examples/dev-mode/Cargo.toml @@ -4,7 +4,7 @@ version = "10.0.0" authors.workspace = true edition.workspace = true license = "MIT-0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME example pallet" readme = "README.md" diff --git a/substrate/frame/examples/frame-crate/Cargo.toml b/substrate/frame/examples/frame-crate/Cargo.toml index e5137526026e..f174c6b9054b 100644 --- a/substrate/frame/examples/frame-crate/Cargo.toml +++ b/substrate/frame/examples/frame-crate/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.1" authors = ["Parity Technologies "] edition.workspace = true license = "MIT-0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME example pallet with umbrella crate" publish = false diff --git a/substrate/frame/examples/kitchensink/Cargo.toml b/substrate/frame/examples/kitchensink/Cargo.toml index f1f9fdb492d9..68d3a31a7ff6 100644 --- a/substrate/frame/examples/kitchensink/Cargo.toml +++ b/substrate/frame/examples/kitchensink/Cargo.toml @@ -4,7 +4,7 @@ version = "4.0.0-dev" authors.workspace = true edition.workspace = true license = "MIT-0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME example kitchensink pallet" publish = false diff --git a/substrate/frame/examples/multi-block-migrations/Cargo.toml b/substrate/frame/examples/multi-block-migrations/Cargo.toml index 91d0a71bb341..98569964a9c9 100644 --- a/substrate/frame/examples/multi-block-migrations/Cargo.toml +++ b/substrate/frame/examples/multi-block-migrations/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Example FRAME pallet for multi-block migrations" publish = false diff --git a/substrate/frame/examples/offchain-worker/Cargo.toml b/substrate/frame/examples/offchain-worker/Cargo.toml index 179a658de312..a5664dd912d4 100644 --- a/substrate/frame/examples/offchain-worker/Cargo.toml +++ b/substrate/frame/examples/offchain-worker/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "MIT-0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME example pallet for offchain worker" readme = "README.md" diff --git a/substrate/frame/examples/single-block-migrations/Cargo.toml b/substrate/frame/examples/single-block-migrations/Cargo.toml index 2d524f2caa26..26a3a9fff753 100644 --- a/substrate/frame/examples/single-block-migrations/Cargo.toml +++ b/substrate/frame/examples/single-block-migrations/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.1" authors.workspace = true edition.workspace = true license = "MIT-0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME example pallet demonstrating best-practices for writing storage migrations." publish = false diff --git a/substrate/frame/examples/split/Cargo.toml b/substrate/frame/examples/split/Cargo.toml index 9542902bae64..22cd8116f670 100644 --- a/substrate/frame/examples/split/Cargo.toml +++ b/substrate/frame/examples/split/Cargo.toml @@ -4,7 +4,7 @@ version = "10.0.0" authors.workspace = true edition.workspace = true license = "MIT-0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME example split pallet" readme = "README.md" diff --git a/substrate/frame/executive/Cargo.toml b/substrate/frame/executive/Cargo.toml index 78d9ea6fa499..76d084f49d9f 100644 --- a/substrate/frame/executive/Cargo.toml +++ b/substrate/frame/executive/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME executives engine" readme = "README.md" diff --git a/substrate/frame/fast-unstake/Cargo.toml b/substrate/frame/fast-unstake/Cargo.toml index 2b188bad1dfa..c1d0e80551c2 100644 --- a/substrate/frame/fast-unstake/Cargo.toml +++ b/substrate/frame/fast-unstake/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME fast unstake pallet" diff --git a/substrate/frame/glutton/Cargo.toml b/substrate/frame/glutton/Cargo.toml index 5a73e8caef8e..6717176ffc95 100644 --- a/substrate/frame/glutton/Cargo.toml +++ b/substrate/frame/glutton/Cargo.toml @@ -4,7 +4,7 @@ version = "14.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for pushing a chain to its weight limits" readme = "README.md" diff --git a/substrate/frame/grandpa/Cargo.toml b/substrate/frame/grandpa/Cargo.toml index e24f9a51db84..86ace358d05d 100644 --- a/substrate/frame/grandpa/Cargo.toml +++ b/substrate/frame/grandpa/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for GRANDPA finality gadget" readme = "README.md" diff --git a/substrate/frame/identity/Cargo.toml b/substrate/frame/identity/Cargo.toml index 6794fbfbbf42..bf974221b857 100644 --- a/substrate/frame/identity/Cargo.toml +++ b/substrate/frame/identity/Cargo.toml @@ -4,7 +4,7 @@ version = "29.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME identity management pallet" readme = "README.md" diff --git a/substrate/frame/im-online/Cargo.toml b/substrate/frame/im-online/Cargo.toml index 2cb03b57d6ca..6c32c8ae898e 100644 --- a/substrate/frame/im-online/Cargo.toml +++ b/substrate/frame/im-online/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME's I'm online pallet" readme = "README.md" diff --git a/substrate/frame/indices/Cargo.toml b/substrate/frame/indices/Cargo.toml index ea1704815312..d81b2d5cabf1 100644 --- a/substrate/frame/indices/Cargo.toml +++ b/substrate/frame/indices/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME indices management pallet" readme = "README.md" diff --git a/substrate/frame/insecure-randomness-collective-flip/Cargo.toml b/substrate/frame/insecure-randomness-collective-flip/Cargo.toml index 977b9fdb6f60..1a47030812da 100644 --- a/substrate/frame/insecure-randomness-collective-flip/Cargo.toml +++ b/substrate/frame/insecure-randomness-collective-flip/Cargo.toml @@ -4,7 +4,7 @@ version = "16.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Insecure do not use in production: FRAME randomness collective flip pallet" readme = "README.md" diff --git a/substrate/frame/lottery/Cargo.toml b/substrate/frame/lottery/Cargo.toml index 0a33e54a825d..eb6e0b703d08 100644 --- a/substrate/frame/lottery/Cargo.toml +++ b/substrate/frame/lottery/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME Participation Lottery Pallet" diff --git a/substrate/frame/membership/Cargo.toml b/substrate/frame/membership/Cargo.toml index 0fc5ce02809e..67aa3503ac0a 100644 --- a/substrate/frame/membership/Cargo.toml +++ b/substrate/frame/membership/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME membership management pallet" readme = "README.md" diff --git a/substrate/frame/merkle-mountain-range/Cargo.toml b/substrate/frame/merkle-mountain-range/Cargo.toml index b8a9b6065c6c..e8b00f6e56cb 100644 --- a/substrate/frame/merkle-mountain-range/Cargo.toml +++ b/substrate/frame/merkle-mountain-range/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME Merkle Mountain Range pallet." diff --git a/substrate/frame/message-queue/Cargo.toml b/substrate/frame/message-queue/Cargo.toml index 0c3bbb2c883c..a6de61d70abf 100644 --- a/substrate/frame/message-queue/Cargo.toml +++ b/substrate/frame/message-queue/Cargo.toml @@ -4,7 +4,7 @@ edition.workspace = true name = "pallet-message-queue" version = "31.0.0" license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet to queue and process messages" diff --git a/substrate/frame/mixnet/Cargo.toml b/substrate/frame/mixnet/Cargo.toml index a9980ac268bc..bb5e84864566 100644 --- a/substrate/frame/mixnet/Cargo.toml +++ b/substrate/frame/mixnet/Cargo.toml @@ -5,7 +5,7 @@ version = "0.4.0" license = "Apache-2.0" authors = ["Parity Technologies "] edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository = "https://github.com/paritytech/substrate/" readme = "README.md" diff --git a/substrate/frame/multisig/Cargo.toml b/substrate/frame/multisig/Cargo.toml index c1571c6c0300..b24df856bcd7 100644 --- a/substrate/frame/multisig/Cargo.toml +++ b/substrate/frame/multisig/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME multi-signature dispatch pallet" readme = "README.md" diff --git a/substrate/frame/nft-fractionalization/Cargo.toml b/substrate/frame/nft-fractionalization/Cargo.toml index ca2e70cbe6ce..6a064204b895 100644 --- a/substrate/frame/nft-fractionalization/Cargo.toml +++ b/substrate/frame/nft-fractionalization/Cargo.toml @@ -4,7 +4,7 @@ version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet to convert non-fungible to fungible tokens." readme = "README.md" diff --git a/substrate/frame/nfts/Cargo.toml b/substrate/frame/nfts/Cargo.toml index e3ffd971a2bb..a97b49e56524 100644 --- a/substrate/frame/nfts/Cargo.toml +++ b/substrate/frame/nfts/Cargo.toml @@ -4,7 +4,7 @@ version = "22.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME NFTs pallet" readme = "README.md" diff --git a/substrate/frame/nfts/runtime-api/Cargo.toml b/substrate/frame/nfts/runtime-api/Cargo.toml index 4125aa1aab00..4d004875468d 100644 --- a/substrate/frame/nfts/runtime-api/Cargo.toml +++ b/substrate/frame/nfts/runtime-api/Cargo.toml @@ -4,7 +4,7 @@ version = "14.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Runtime API for the FRAME NFTs pallet." readme = "README.md" diff --git a/substrate/frame/nis/Cargo.toml b/substrate/frame/nis/Cargo.toml index fb6c25789b26..78e086d0ed12 100644 --- a/substrate/frame/nis/Cargo.toml +++ b/substrate/frame/nis/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for rewarding account freezing." readme = "README.md" diff --git a/substrate/frame/node-authorization/Cargo.toml b/substrate/frame/node-authorization/Cargo.toml index b56a76ce641a..82aecc21d0b5 100644 --- a/substrate/frame/node-authorization/Cargo.toml +++ b/substrate/frame/node-authorization/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for node authorization" diff --git a/substrate/frame/nomination-pools/Cargo.toml b/substrate/frame/nomination-pools/Cargo.toml index bfcc92edb7e2..aa90e4d81339 100644 --- a/substrate/frame/nomination-pools/Cargo.toml +++ b/substrate/frame/nomination-pools/Cargo.toml @@ -4,7 +4,7 @@ version = "25.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME nomination pools pallet" diff --git a/substrate/frame/nomination-pools/benchmarking/Cargo.toml b/substrate/frame/nomination-pools/benchmarking/Cargo.toml index e73a208926ee..7dd826a91224 100644 --- a/substrate/frame/nomination-pools/benchmarking/Cargo.toml +++ b/substrate/frame/nomination-pools/benchmarking/Cargo.toml @@ -4,7 +4,7 @@ version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME nomination pools pallet benchmarking" readme = "README.md" diff --git a/substrate/frame/nomination-pools/fuzzer/Cargo.toml b/substrate/frame/nomination-pools/fuzzer/Cargo.toml index 7c45dcef256f..e1518ed099ae 100644 --- a/substrate/frame/nomination-pools/fuzzer/Cargo.toml +++ b/substrate/frame/nomination-pools/fuzzer/Cargo.toml @@ -4,7 +4,7 @@ version = "2.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Fuzzer for fixed point arithmetic primitives." documentation = "https://docs.rs/sp-arithmetic-fuzzer" diff --git a/substrate/frame/nomination-pools/runtime-api/Cargo.toml b/substrate/frame/nomination-pools/runtime-api/Cargo.toml index 2f91e550bc05..6de9fc8c8844 100644 --- a/substrate/frame/nomination-pools/runtime-api/Cargo.toml +++ b/substrate/frame/nomination-pools/runtime-api/Cargo.toml @@ -4,7 +4,7 @@ version = "23.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Runtime API for nomination-pools FRAME pallet" readme = "README.md" diff --git a/substrate/frame/nomination-pools/test-delegate-stake/Cargo.toml b/substrate/frame/nomination-pools/test-delegate-stake/Cargo.toml index ea50dd6d732d..7940caaff775 100644 --- a/substrate/frame/nomination-pools/test-delegate-stake/Cargo.toml +++ b/substrate/frame/nomination-pools/test-delegate-stake/Cargo.toml @@ -4,7 +4,7 @@ version = "1.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME nomination pools pallet tests with the staking pallet" publish = false diff --git a/substrate/frame/nomination-pools/test-transfer-stake/Cargo.toml b/substrate/frame/nomination-pools/test-transfer-stake/Cargo.toml index 8bc5676cfe91..7398404c2351 100644 --- a/substrate/frame/nomination-pools/test-transfer-stake/Cargo.toml +++ b/substrate/frame/nomination-pools/test-transfer-stake/Cargo.toml @@ -4,7 +4,7 @@ version = "1.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME nomination pools pallet tests with the staking pallet" publish = false diff --git a/substrate/frame/offences/Cargo.toml b/substrate/frame/offences/Cargo.toml index 9cf5e911a9d3..98c320e1f808 100644 --- a/substrate/frame/offences/Cargo.toml +++ b/substrate/frame/offences/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME offences pallet" readme = "README.md" diff --git a/substrate/frame/offences/benchmarking/Cargo.toml b/substrate/frame/offences/benchmarking/Cargo.toml index eb97eb3d4132..28c7895180c4 100644 --- a/substrate/frame/offences/benchmarking/Cargo.toml +++ b/substrate/frame/offences/benchmarking/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME offences pallet benchmarking" readme = "README.md" diff --git a/substrate/frame/paged-list/Cargo.toml b/substrate/frame/paged-list/Cargo.toml index 597bdd37f57f..a680139c5fdc 100644 --- a/substrate/frame/paged-list/Cargo.toml +++ b/substrate/frame/paged-list/Cargo.toml @@ -3,7 +3,7 @@ name = "pallet-paged-list" version = "0.6.0" description = "FRAME pallet that provides a paged list data structure." authors.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true edition.workspace = true license = "Apache-2.0" repository.workspace = true diff --git a/substrate/frame/paged-list/fuzzer/Cargo.toml b/substrate/frame/paged-list/fuzzer/Cargo.toml index bfdacfd8dd03..d0108254ed2d 100644 --- a/substrate/frame/paged-list/fuzzer/Cargo.toml +++ b/substrate/frame/paged-list/fuzzer/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Fuzz storage types of pallet-paged-list" publish = false diff --git a/substrate/frame/preimage/Cargo.toml b/substrate/frame/preimage/Cargo.toml index 425c4e81aa7b..1356ac403d38 100644 --- a/substrate/frame/preimage/Cargo.toml +++ b/substrate/frame/preimage/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for storing preimages of hashes" diff --git a/substrate/frame/proxy/Cargo.toml b/substrate/frame/proxy/Cargo.toml index af2427da380a..40c1c9750614 100644 --- a/substrate/frame/proxy/Cargo.toml +++ b/substrate/frame/proxy/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME proxying pallet" readme = "README.md" diff --git a/substrate/frame/ranked-collective/Cargo.toml b/substrate/frame/ranked-collective/Cargo.toml index dd9b466e0f91..eca59cf7fc22 100644 --- a/substrate/frame/ranked-collective/Cargo.toml +++ b/substrate/frame/ranked-collective/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Ranked collective system: Members of a set of account IDs can make their collective feelings known through dispatched calls from one of two specialized origins." readme = "README.md" diff --git a/substrate/frame/recovery/Cargo.toml b/substrate/frame/recovery/Cargo.toml index 42493b435105..44335e8f575c 100644 --- a/substrate/frame/recovery/Cargo.toml +++ b/substrate/frame/recovery/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME account recovery pallet" readme = "README.md" diff --git a/substrate/frame/referenda/Cargo.toml b/substrate/frame/referenda/Cargo.toml index 578486714d63..32dba3436595 100644 --- a/substrate/frame/referenda/Cargo.toml +++ b/substrate/frame/referenda/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for inclusive on-chain decisions" readme = "README.md" diff --git a/substrate/frame/remark/Cargo.toml b/substrate/frame/remark/Cargo.toml index efc9917428c0..487bada593cd 100644 --- a/substrate/frame/remark/Cargo.toml +++ b/substrate/frame/remark/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Remark storage pallet" readme = "README.md" diff --git a/substrate/frame/root-offences/Cargo.toml b/substrate/frame/root-offences/Cargo.toml index dde264f3949a..f80fed11b971 100644 --- a/substrate/frame/root-offences/Cargo.toml +++ b/substrate/frame/root-offences/Cargo.toml @@ -4,7 +4,7 @@ version = "25.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME root offences pallet" readme = "README.md" diff --git a/substrate/frame/root-testing/Cargo.toml b/substrate/frame/root-testing/Cargo.toml index 96d861362622..ee3ce8011009 100644 --- a/substrate/frame/root-testing/Cargo.toml +++ b/substrate/frame/root-testing/Cargo.toml @@ -4,7 +4,7 @@ version = "4.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME root testing pallet" readme = "README.md" diff --git a/substrate/frame/safe-mode/Cargo.toml b/substrate/frame/safe-mode/Cargo.toml index b704818f1adf..e7f165ae67d8 100644 --- a/substrate/frame/safe-mode/Cargo.toml +++ b/substrate/frame/safe-mode/Cargo.toml @@ -4,7 +4,7 @@ version = "9.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME safe-mode pallet" diff --git a/substrate/frame/salary/Cargo.toml b/substrate/frame/salary/Cargo.toml index 9121f59ff462..9e4cf06288dd 100644 --- a/substrate/frame/salary/Cargo.toml +++ b/substrate/frame/salary/Cargo.toml @@ -4,7 +4,7 @@ version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Paymaster" readme = "README.md" diff --git a/substrate/frame/sassafras/Cargo.toml b/substrate/frame/sassafras/Cargo.toml index 0eefca57849a..7eb2bda96ffc 100644 --- a/substrate/frame/sassafras/Cargo.toml +++ b/substrate/frame/sassafras/Cargo.toml @@ -4,7 +4,7 @@ version = "0.3.5-dev" authors = ["Parity Technologies "] edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository = "https://github.com/paritytech/substrate/" description = "Consensus extension module for Sassafras consensus." readme = "README.md" diff --git a/substrate/frame/scheduler/Cargo.toml b/substrate/frame/scheduler/Cargo.toml index 29aaaec9aa40..1432ada91335 100644 --- a/substrate/frame/scheduler/Cargo.toml +++ b/substrate/frame/scheduler/Cargo.toml @@ -4,7 +4,7 @@ version = "29.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME Scheduler pallet" readme = "README.md" diff --git a/substrate/frame/scored-pool/Cargo.toml b/substrate/frame/scored-pool/Cargo.toml index 132799ead62a..d945ef42a47b 100644 --- a/substrate/frame/scored-pool/Cargo.toml +++ b/substrate/frame/scored-pool/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for scored pools" readme = "README.md" diff --git a/substrate/frame/session/Cargo.toml b/substrate/frame/session/Cargo.toml index 09bb93d8c1d0..b82112681e67 100644 --- a/substrate/frame/session/Cargo.toml +++ b/substrate/frame/session/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME sessions pallet" readme = "README.md" diff --git a/substrate/frame/session/benchmarking/Cargo.toml b/substrate/frame/session/benchmarking/Cargo.toml index 9ca3549f681d..264bc10a33f6 100644 --- a/substrate/frame/session/benchmarking/Cargo.toml +++ b/substrate/frame/session/benchmarking/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME sessions pallet benchmarking" readme = "README.md" diff --git a/substrate/frame/society/Cargo.toml b/substrate/frame/society/Cargo.toml index b6fa70c42efc..555dee68ba01 100644 --- a/substrate/frame/society/Cargo.toml +++ b/substrate/frame/society/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME society pallet" readme = "README.md" diff --git a/substrate/frame/staking/Cargo.toml b/substrate/frame/staking/Cargo.toml index 57eeec543634..a6a0ccd3b0a7 100644 --- a/substrate/frame/staking/Cargo.toml +++ b/substrate/frame/staking/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet staking" readme = "README.md" diff --git a/substrate/frame/staking/reward-curve/Cargo.toml b/substrate/frame/staking/reward-curve/Cargo.toml index acb819c3169e..8e60b3962a99 100644 --- a/substrate/frame/staking/reward-curve/Cargo.toml +++ b/substrate/frame/staking/reward-curve/Cargo.toml @@ -4,7 +4,7 @@ version = "11.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Reward Curve for FRAME staking pallet" diff --git a/substrate/frame/staking/reward-fn/Cargo.toml b/substrate/frame/staking/reward-fn/Cargo.toml index 5adbb8382da5..87b71ee80504 100644 --- a/substrate/frame/staking/reward-fn/Cargo.toml +++ b/substrate/frame/staking/reward-fn/Cargo.toml @@ -4,7 +4,7 @@ version = "19.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Reward function for FRAME staking pallet" diff --git a/substrate/frame/staking/runtime-api/Cargo.toml b/substrate/frame/staking/runtime-api/Cargo.toml index 624279624bc8..d67b1e173b28 100644 --- a/substrate/frame/staking/runtime-api/Cargo.toml +++ b/substrate/frame/staking/runtime-api/Cargo.toml @@ -4,7 +4,7 @@ version = "14.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "RPC runtime API for transaction payment FRAME pallet" readme = "README.md" diff --git a/substrate/frame/state-trie-migration/Cargo.toml b/substrate/frame/state-trie-migration/Cargo.toml index db8d10dabfde..8c82bc38da97 100644 --- a/substrate/frame/state-trie-migration/Cargo.toml +++ b/substrate/frame/state-trie-migration/Cargo.toml @@ -4,7 +4,7 @@ version = "29.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet migration of trie" diff --git a/substrate/frame/statement/Cargo.toml b/substrate/frame/statement/Cargo.toml index 000e5033d8f1..e601881cd720 100644 --- a/substrate/frame/statement/Cargo.toml +++ b/substrate/frame/statement/Cargo.toml @@ -4,7 +4,7 @@ version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for statement store" diff --git a/substrate/frame/sudo/Cargo.toml b/substrate/frame/sudo/Cargo.toml index 1a94753728b2..9b362019b29b 100644 --- a/substrate/frame/sudo/Cargo.toml +++ b/substrate/frame/sudo/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for sudo" readme = "README.md" diff --git a/substrate/frame/support/Cargo.toml b/substrate/frame/support/Cargo.toml index 560697ab710b..549059e261cc 100644 --- a/substrate/frame/support/Cargo.toml +++ b/substrate/frame/support/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Support code for the runtime." readme = "README.md" diff --git a/substrate/frame/support/procedural/Cargo.toml b/substrate/frame/support/procedural/Cargo.toml index aa0665a2250f..51790062b2c2 100644 --- a/substrate/frame/support/procedural/Cargo.toml +++ b/substrate/frame/support/procedural/Cargo.toml @@ -4,7 +4,7 @@ version = "23.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Proc macro of Support code for the runtime." diff --git a/substrate/frame/support/procedural/tools/Cargo.toml b/substrate/frame/support/procedural/tools/Cargo.toml index f672740e57e8..e61e17e8ac75 100644 --- a/substrate/frame/support/procedural/tools/Cargo.toml +++ b/substrate/frame/support/procedural/tools/Cargo.toml @@ -4,7 +4,7 @@ version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Proc macro helpers for procedural macros" diff --git a/substrate/frame/support/procedural/tools/derive/Cargo.toml b/substrate/frame/support/procedural/tools/derive/Cargo.toml index 2292c2a7c724..80cde38fd5b6 100644 --- a/substrate/frame/support/procedural/tools/derive/Cargo.toml +++ b/substrate/frame/support/procedural/tools/derive/Cargo.toml @@ -4,7 +4,7 @@ version = "11.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Use to derive parsing for parsing struct." diff --git a/substrate/frame/support/test/Cargo.toml b/substrate/frame/support/test/Cargo.toml index 82ac1d2c7475..5c12c082305f 100644 --- a/substrate/frame/support/test/Cargo.toml +++ b/substrate/frame/support/test/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true license = "Apache-2.0" publish = false -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/frame/support/test/compile_pass/Cargo.toml b/substrate/frame/support/test/compile_pass/Cargo.toml index 60896ca2a10f..9e0a7ff7c675 100644 --- a/substrate/frame/support/test/compile_pass/Cargo.toml +++ b/substrate/frame/support/test/compile_pass/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true license = "Apache-2.0" publish = false -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/frame/support/test/pallet/Cargo.toml b/substrate/frame/support/test/pallet/Cargo.toml index cee0eac6f1bc..f03377dc21eb 100644 --- a/substrate/frame/support/test/pallet/Cargo.toml +++ b/substrate/frame/support/test/pallet/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true license = "Apache-2.0" publish = false -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/frame/support/test/stg_frame_crate/Cargo.toml b/substrate/frame/support/test/stg_frame_crate/Cargo.toml index 8447cc12ef20..f627d29cd563 100644 --- a/substrate/frame/support/test/stg_frame_crate/Cargo.toml +++ b/substrate/frame/support/test/stg_frame_crate/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true license = "Apache-2.0" publish = false -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/frame/system/Cargo.toml b/substrate/frame/system/Cargo.toml index 3d056c894b92..38349c7edbd9 100644 --- a/substrate/frame/system/Cargo.toml +++ b/substrate/frame/system/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME system module" readme = "README.md" diff --git a/substrate/frame/system/benchmarking/Cargo.toml b/substrate/frame/system/benchmarking/Cargo.toml index dec68d20b699..d9b5e7083bd2 100644 --- a/substrate/frame/system/benchmarking/Cargo.toml +++ b/substrate/frame/system/benchmarking/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME System benchmarking" readme = "README.md" diff --git a/substrate/frame/system/rpc/runtime-api/Cargo.toml b/substrate/frame/system/rpc/runtime-api/Cargo.toml index 901a035b6476..8e968a536756 100644 --- a/substrate/frame/system/rpc/runtime-api/Cargo.toml +++ b/substrate/frame/system/rpc/runtime-api/Cargo.toml @@ -4,7 +4,7 @@ version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Runtime API definition required by System RPC extensions." readme = "README.md" diff --git a/substrate/frame/timestamp/Cargo.toml b/substrate/frame/timestamp/Cargo.toml index 25aecea7b79e..0eff0530c7e2 100644 --- a/substrate/frame/timestamp/Cargo.toml +++ b/substrate/frame/timestamp/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME Timestamp Module" documentation = "https://docs.rs/pallet-timestamp" diff --git a/substrate/frame/tips/Cargo.toml b/substrate/frame/tips/Cargo.toml index bec2ce0a47b8..7c7a2d6aa909 100644 --- a/substrate/frame/tips/Cargo.toml +++ b/substrate/frame/tips/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet to manage tips" readme = "README.md" diff --git a/substrate/frame/transaction-payment/Cargo.toml b/substrate/frame/transaction-payment/Cargo.toml index c405ea1e9439..4161a97f3cde 100644 --- a/substrate/frame/transaction-payment/Cargo.toml +++ b/substrate/frame/transaction-payment/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet to manage transaction payments" readme = "README.md" diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/Cargo.toml b/substrate/frame/transaction-payment/asset-conversion-tx-payment/Cargo.toml index b87d780b9672..e6a60e9c850f 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/Cargo.toml +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/Cargo.toml @@ -4,7 +4,7 @@ version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Pallet to manage transaction payments in assets by converting them to native assets." readme = "README.md" diff --git a/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml b/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml index ec8fb38dda39..8d39dea8c62b 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml +++ b/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "pallet to manage transaction payments in assets" readme = "README.md" diff --git a/substrate/frame/transaction-payment/rpc/Cargo.toml b/substrate/frame/transaction-payment/rpc/Cargo.toml index d2fb92a6bf34..f049e866c0d6 100644 --- a/substrate/frame/transaction-payment/rpc/Cargo.toml +++ b/substrate/frame/transaction-payment/rpc/Cargo.toml @@ -4,7 +4,7 @@ version = "30.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "RPC interface for the transaction payment pallet." readme = "README.md" diff --git a/substrate/frame/transaction-payment/rpc/runtime-api/Cargo.toml b/substrate/frame/transaction-payment/rpc/runtime-api/Cargo.toml index 1a384c74b31c..445759054509 100644 --- a/substrate/frame/transaction-payment/rpc/runtime-api/Cargo.toml +++ b/substrate/frame/transaction-payment/rpc/runtime-api/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "RPC runtime API for transaction payment FRAME pallet" readme = "README.md" diff --git a/substrate/frame/transaction-storage/Cargo.toml b/substrate/frame/transaction-storage/Cargo.toml index e57ee1e729c4..f5d6bd1c364c 100644 --- a/substrate/frame/transaction-storage/Cargo.toml +++ b/substrate/frame/transaction-storage/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Storage chain pallet" readme = "README.md" diff --git a/substrate/frame/treasury/Cargo.toml b/substrate/frame/treasury/Cargo.toml index 2727ab809320..55bdd4f7a498 100644 --- a/substrate/frame/treasury/Cargo.toml +++ b/substrate/frame/treasury/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet to manage treasury" readme = "README.md" diff --git a/substrate/frame/try-runtime/Cargo.toml b/substrate/frame/try-runtime/Cargo.toml index 228d96095ac9..2bd791f52385 100644 --- a/substrate/frame/try-runtime/Cargo.toml +++ b/substrate/frame/try-runtime/Cargo.toml @@ -4,7 +4,7 @@ version = "0.34.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for democracy" diff --git a/substrate/frame/tx-pause/Cargo.toml b/substrate/frame/tx-pause/Cargo.toml index 861a85881f2d..03c700ec053c 100644 --- a/substrate/frame/tx-pause/Cargo.toml +++ b/substrate/frame/tx-pause/Cargo.toml @@ -4,7 +4,7 @@ version = "9.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME transaction pause pallet" diff --git a/substrate/frame/uniques/Cargo.toml b/substrate/frame/uniques/Cargo.toml index 2b1794aa60fe..abd456d97556 100644 --- a/substrate/frame/uniques/Cargo.toml +++ b/substrate/frame/uniques/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME NFT asset management pallet" readme = "README.md" diff --git a/substrate/frame/utility/Cargo.toml b/substrate/frame/utility/Cargo.toml index f08232de9cd6..e2d35fc1699f 100644 --- a/substrate/frame/utility/Cargo.toml +++ b/substrate/frame/utility/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME utilities pallet" readme = "README.md" diff --git a/substrate/frame/vesting/Cargo.toml b/substrate/frame/vesting/Cargo.toml index 64c26174cfdf..f896c3962eaa 100644 --- a/substrate/frame/vesting/Cargo.toml +++ b/substrate/frame/vesting/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for manage vesting" readme = "README.md" diff --git a/substrate/frame/whitelist/Cargo.toml b/substrate/frame/whitelist/Cargo.toml index c48a15f21631..e910fc323714 100644 --- a/substrate/frame/whitelist/Cargo.toml +++ b/substrate/frame/whitelist/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME pallet for whitelisting call, and dispatch from specific origin" diff --git a/substrate/primitives/api/Cargo.toml b/substrate/primitives/api/Cargo.toml index d6c64a0ada28..e0a4d06b2d81 100644 --- a/substrate/primitives/api/Cargo.toml +++ b/substrate/primitives/api/Cargo.toml @@ -4,7 +4,7 @@ version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate runtime api primitives" readme = "README.md" diff --git a/substrate/primitives/api/proc-macro/Cargo.toml b/substrate/primitives/api/proc-macro/Cargo.toml index 7d7fc19fcf5b..659307e7b0f8 100644 --- a/substrate/primitives/api/proc-macro/Cargo.toml +++ b/substrate/primitives/api/proc-macro/Cargo.toml @@ -4,7 +4,7 @@ version = "15.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Macros for declaring and implementing runtime apis." documentation = "https://docs.rs/sp-api-proc-macro" diff --git a/substrate/primitives/api/test/Cargo.toml b/substrate/primitives/api/test/Cargo.toml index 6c159fc63d0d..121ce6b99938 100644 --- a/substrate/primitives/api/test/Cargo.toml +++ b/substrate/primitives/api/test/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true license = "Apache-2.0" publish = false -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/primitives/application-crypto/Cargo.toml b/substrate/primitives/application-crypto/Cargo.toml index c0508d377f8b..1161d43ded5a 100644 --- a/substrate/primitives/application-crypto/Cargo.toml +++ b/substrate/primitives/application-crypto/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true description = "Provides facilities for generating application specific crypto wrapper types." license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sp-application-crypto" readme = "README.md" diff --git a/substrate/primitives/application-crypto/test/Cargo.toml b/substrate/primitives/application-crypto/test/Cargo.toml index 43f9d3852005..5c19161bc870 100644 --- a/substrate/primitives/application-crypto/test/Cargo.toml +++ b/substrate/primitives/application-crypto/test/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true description = "Integration tests for application-crypto" license = "Apache-2.0" publish = false -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/primitives/arithmetic/Cargo.toml b/substrate/primitives/arithmetic/Cargo.toml index 4a9d5ba234ba..485656bf30bb 100644 --- a/substrate/primitives/arithmetic/Cargo.toml +++ b/substrate/primitives/arithmetic/Cargo.toml @@ -4,7 +4,7 @@ version = "23.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Minimal fixed point arithmetic primitives and types for runtime." documentation = "https://docs.rs/sp-arithmetic" diff --git a/substrate/primitives/arithmetic/fuzzer/Cargo.toml b/substrate/primitives/arithmetic/fuzzer/Cargo.toml index c978393af34c..126f2ed996e3 100644 --- a/substrate/primitives/arithmetic/fuzzer/Cargo.toml +++ b/substrate/primitives/arithmetic/fuzzer/Cargo.toml @@ -4,7 +4,7 @@ version = "2.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Fuzzer for fixed point arithmetic primitives." documentation = "https://docs.rs/sp-arithmetic-fuzzer" diff --git a/substrate/primitives/authority-discovery/Cargo.toml b/substrate/primitives/authority-discovery/Cargo.toml index 26e08b8504c3..ccfe199689da 100644 --- a/substrate/primitives/authority-discovery/Cargo.toml +++ b/substrate/primitives/authority-discovery/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Authority discovery primitives" edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/primitives/block-builder/Cargo.toml b/substrate/primitives/block-builder/Cargo.toml index ddc0c00a3be8..dcd6ba8a91d7 100644 --- a/substrate/primitives/block-builder/Cargo.toml +++ b/substrate/primitives/block-builder/Cargo.toml @@ -4,7 +4,7 @@ version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "The block builder runtime api." readme = "README.md" diff --git a/substrate/primitives/blockchain/Cargo.toml b/substrate/primitives/blockchain/Cargo.toml index bd0daaf63c05..93158274d98f 100644 --- a/substrate/primitives/blockchain/Cargo.toml +++ b/substrate/primitives/blockchain/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate blockchain traits and primitives." documentation = "https://docs.rs/sp-blockchain" diff --git a/substrate/primitives/consensus/aura/Cargo.toml b/substrate/primitives/consensus/aura/Cargo.toml index 3ae45062cb68..1d441845d769 100644 --- a/substrate/primitives/consensus/aura/Cargo.toml +++ b/substrate/primitives/consensus/aura/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Primitives for Aura consensus" edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/primitives/consensus/babe/Cargo.toml b/substrate/primitives/consensus/babe/Cargo.toml index 884fc6c25c97..4df5c8d46562 100644 --- a/substrate/primitives/consensus/babe/Cargo.toml +++ b/substrate/primitives/consensus/babe/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Primitives for BABE consensus" edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/primitives/consensus/beefy/Cargo.toml b/substrate/primitives/consensus/beefy/Cargo.toml index 49d907506049..f31aa5756ba2 100644 --- a/substrate/primitives/consensus/beefy/Cargo.toml +++ b/substrate/primitives/consensus/beefy/Cargo.toml @@ -4,7 +4,7 @@ version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Primitives for BEEFY protocol." diff --git a/substrate/primitives/consensus/common/Cargo.toml b/substrate/primitives/consensus/common/Cargo.toml index a5d9a8da1a9b..764ef1d97346 100644 --- a/substrate/primitives/consensus/common/Cargo.toml +++ b/substrate/primitives/consensus/common/Cargo.toml @@ -4,7 +4,7 @@ version = "0.32.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Common utilities for building and using consensus engines in substrate." documentation = "https://docs.rs/sp-consensus/" diff --git a/substrate/primitives/consensus/grandpa/Cargo.toml b/substrate/primitives/consensus/grandpa/Cargo.toml index 6ba5bb40595a..a3f9d76824d9 100644 --- a/substrate/primitives/consensus/grandpa/Cargo.toml +++ b/substrate/primitives/consensus/grandpa/Cargo.toml @@ -4,7 +4,7 @@ version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Primitives for GRANDPA integration, suitable for WASM compilation." documentation = "https://docs.rs/sp-consensus-grandpa" diff --git a/substrate/primitives/consensus/pow/Cargo.toml b/substrate/primitives/consensus/pow/Cargo.toml index d4563cf8e6af..8731015f7da2 100644 --- a/substrate/primitives/consensus/pow/Cargo.toml +++ b/substrate/primitives/consensus/pow/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Primitives for Aura consensus" edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/primitives/consensus/sassafras/Cargo.toml b/substrate/primitives/consensus/sassafras/Cargo.toml index 12bcbc1b3392..eb9dfd34c595 100644 --- a/substrate/primitives/consensus/sassafras/Cargo.toml +++ b/substrate/primitives/consensus/sassafras/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Primitives for Sassafras consensus" edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository = "https://github.com/paritytech/polkadot-sdk/" documentation = "https://docs.rs/sp-consensus-sassafras" readme = "README.md" diff --git a/substrate/primitives/consensus/slots/Cargo.toml b/substrate/primitives/consensus/slots/Cargo.toml index 9d881c3acd00..43f8c5514f7f 100644 --- a/substrate/primitives/consensus/slots/Cargo.toml +++ b/substrate/primitives/consensus/slots/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Primitives for slots-based consensus" edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/primitives/core/Cargo.toml b/substrate/primitives/core/Cargo.toml index 258caa06fcfe..51cbfa3bdfbe 100644 --- a/substrate/primitives/core/Cargo.toml +++ b/substrate/primitives/core/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Shareable Substrate types." documentation = "https://docs.rs/sp-core" diff --git a/substrate/primitives/crypto/ec-utils/Cargo.toml b/substrate/primitives/crypto/ec-utils/Cargo.toml index 1068787728ba..29e30133ebea 100644 --- a/substrate/primitives/crypto/ec-utils/Cargo.toml +++ b/substrate/primitives/crypto/ec-utils/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Host functions for common Arkworks elliptic curve operations" edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/primitives/crypto/hashing/Cargo.toml b/substrate/primitives/crypto/hashing/Cargo.toml index 461af269bf2d..55f5de07392a 100644 --- a/substrate/primitives/crypto/hashing/Cargo.toml +++ b/substrate/primitives/crypto/hashing/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Hashing primitives." documentation = "https://docs.rs/sp-crypto-hashing" diff --git a/substrate/primitives/crypto/hashing/proc-macro/Cargo.toml b/substrate/primitives/crypto/hashing/proc-macro/Cargo.toml index 68e865c7dac5..6f974a3e2c8a 100644 --- a/substrate/primitives/crypto/hashing/proc-macro/Cargo.toml +++ b/substrate/primitives/crypto/hashing/proc-macro/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Procedural macros for calculating static hashes." documentation = "https://docs.rs/sp-crypto-hashing-proc-macro" diff --git a/substrate/primitives/database/Cargo.toml b/substrate/primitives/database/Cargo.toml index c0867198e8b9..1795fece602e 100644 --- a/substrate/primitives/database/Cargo.toml +++ b/substrate/primitives/database/Cargo.toml @@ -4,7 +4,7 @@ version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate database trait." documentation = "https://docs.rs/sp-database" diff --git a/substrate/primitives/debug-derive/Cargo.toml b/substrate/primitives/debug-derive/Cargo.toml index 4f45d6525c4a..4979b89155ab 100644 --- a/substrate/primitives/debug-derive/Cargo.toml +++ b/substrate/primitives/debug-derive/Cargo.toml @@ -4,7 +4,7 @@ version = "14.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Macros to derive runtime debug implementation." documentation = "https://docs.rs/sp-debug-derive" diff --git a/substrate/primitives/externalities/Cargo.toml b/substrate/primitives/externalities/Cargo.toml index ca2f57c03b94..569d4298c50d 100644 --- a/substrate/primitives/externalities/Cargo.toml +++ b/substrate/primitives/externalities/Cargo.toml @@ -4,7 +4,7 @@ version = "0.25.0" license = "Apache-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate externalities abstraction" documentation = "https://docs.rs/sp-externalities" diff --git a/substrate/primitives/genesis-builder/Cargo.toml b/substrate/primitives/genesis-builder/Cargo.toml index d4345d17e7e5..285b214907ad 100644 --- a/substrate/primitives/genesis-builder/Cargo.toml +++ b/substrate/primitives/genesis-builder/Cargo.toml @@ -4,7 +4,7 @@ version = "0.8.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate RuntimeGenesisConfig builder API" readme = "README.md" diff --git a/substrate/primitives/inherents/Cargo.toml b/substrate/primitives/inherents/Cargo.toml index 1495287698d6..271308c9cbf1 100644 --- a/substrate/primitives/inherents/Cargo.toml +++ b/substrate/primitives/inherents/Cargo.toml @@ -4,7 +4,7 @@ version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Provides types and traits for creating and checking inherents." documentation = "https://docs.rs/sp-inherents" diff --git a/substrate/primitives/io/Cargo.toml b/substrate/primitives/io/Cargo.toml index 923efc3b8764..97940759a987 100644 --- a/substrate/primitives/io/Cargo.toml +++ b/substrate/primitives/io/Cargo.toml @@ -4,7 +4,7 @@ version = "30.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "I/O for Substrate runtimes" documentation = "https://docs.rs/sp-io" diff --git a/substrate/primitives/keyring/Cargo.toml b/substrate/primitives/keyring/Cargo.toml index 304440b52bac..27f7304a9358 100644 --- a/substrate/primitives/keyring/Cargo.toml +++ b/substrate/primitives/keyring/Cargo.toml @@ -4,7 +4,7 @@ version = "31.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Keyring support code for the runtime. A set of test accounts." documentation = "https://docs.rs/sp-keyring" diff --git a/substrate/primitives/keystore/Cargo.toml b/substrate/primitives/keystore/Cargo.toml index 71864a9c37d5..08b58c05ad11 100644 --- a/substrate/primitives/keystore/Cargo.toml +++ b/substrate/primitives/keystore/Cargo.toml @@ -4,7 +4,7 @@ version = "0.34.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Keystore primitives." documentation = "https://docs.rs/sp-core" diff --git a/substrate/primitives/maybe-compressed-blob/Cargo.toml b/substrate/primitives/maybe-compressed-blob/Cargo.toml index 08bc753bcba3..66ff533ede69 100644 --- a/substrate/primitives/maybe-compressed-blob/Cargo.toml +++ b/substrate/primitives/maybe-compressed-blob/Cargo.toml @@ -4,7 +4,7 @@ version = "11.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Handling of blobs, usually Wasm code, which may be compressed" documentation = "https://docs.rs/sp-maybe-compressed-blob" diff --git a/substrate/primitives/merkle-mountain-range/Cargo.toml b/substrate/primitives/merkle-mountain-range/Cargo.toml index b962c019dc3a..6f944a3f6a8d 100644 --- a/substrate/primitives/merkle-mountain-range/Cargo.toml +++ b/substrate/primitives/merkle-mountain-range/Cargo.toml @@ -4,7 +4,7 @@ version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Merkle Mountain Range primitives." diff --git a/substrate/primitives/metadata-ir/Cargo.toml b/substrate/primitives/metadata-ir/Cargo.toml index a56e51413cc6..d7786347dd02 100644 --- a/substrate/primitives/metadata-ir/Cargo.toml +++ b/substrate/primitives/metadata-ir/Cargo.toml @@ -4,7 +4,7 @@ version = "0.6.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Intermediate representation of the runtime metadata." documentation = "https://docs.rs/sp-metadata-ir" diff --git a/substrate/primitives/mixnet/Cargo.toml b/substrate/primitives/mixnet/Cargo.toml index d243f234fcc0..a6fc3ce857a1 100644 --- a/substrate/primitives/mixnet/Cargo.toml +++ b/substrate/primitives/mixnet/Cargo.toml @@ -5,7 +5,7 @@ version = "0.4.0" license = "Apache-2.0" authors = ["Parity Technologies "] edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository = "https://github.com/paritytech/substrate/" readme = "README.md" diff --git a/substrate/primitives/npos-elections/Cargo.toml b/substrate/primitives/npos-elections/Cargo.toml index f7168dcd8158..12851b1a917d 100644 --- a/substrate/primitives/npos-elections/Cargo.toml +++ b/substrate/primitives/npos-elections/Cargo.toml @@ -4,7 +4,7 @@ version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "NPoS election algorithm primitives" readme = "README.md" diff --git a/substrate/primitives/npos-elections/fuzzer/Cargo.toml b/substrate/primitives/npos-elections/fuzzer/Cargo.toml index 88de7614b70d..b203610392d8 100644 --- a/substrate/primitives/npos-elections/fuzzer/Cargo.toml +++ b/substrate/primitives/npos-elections/fuzzer/Cargo.toml @@ -4,7 +4,7 @@ version = "2.0.0-alpha.5" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Fuzzer for phragmén implementation." documentation = "https://docs.rs/sp-npos-elections-fuzzer" diff --git a/substrate/primitives/offchain/Cargo.toml b/substrate/primitives/offchain/Cargo.toml index 69db8f2b7be7..ad35a1b84519 100644 --- a/substrate/primitives/offchain/Cargo.toml +++ b/substrate/primitives/offchain/Cargo.toml @@ -5,7 +5,7 @@ version = "26.0.0" license = "Apache-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/primitives/panic-handler/Cargo.toml b/substrate/primitives/panic-handler/Cargo.toml index 7449cf3f8b88..395e788eb244 100644 --- a/substrate/primitives/panic-handler/Cargo.toml +++ b/substrate/primitives/panic-handler/Cargo.toml @@ -4,7 +4,7 @@ version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Custom panic hook with bug report link" documentation = "https://docs.rs/sp-panic-handler" diff --git a/substrate/primitives/rpc/Cargo.toml b/substrate/primitives/rpc/Cargo.toml index 194176ed9fc9..ea795cf07bd9 100644 --- a/substrate/primitives/rpc/Cargo.toml +++ b/substrate/primitives/rpc/Cargo.toml @@ -4,7 +4,7 @@ version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate RPC primitives and utilities." readme = "README.md" diff --git a/substrate/primitives/runtime-interface/Cargo.toml b/substrate/primitives/runtime-interface/Cargo.toml index 3f8ffe20970a..ee44d90fa959 100644 --- a/substrate/primitives/runtime-interface/Cargo.toml +++ b/substrate/primitives/runtime-interface/Cargo.toml @@ -4,7 +4,7 @@ version = "24.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate runtime interface" documentation = "https://docs.rs/sp-runtime-interface/" diff --git a/substrate/primitives/runtime-interface/proc-macro/Cargo.toml b/substrate/primitives/runtime-interface/proc-macro/Cargo.toml index 70b72463de71..3fd5f073f025 100644 --- a/substrate/primitives/runtime-interface/proc-macro/Cargo.toml +++ b/substrate/primitives/runtime-interface/proc-macro/Cargo.toml @@ -4,7 +4,7 @@ version = "17.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "This crate provides procedural macros for usage within the context of the Substrate runtime interface." documentation = "https://docs.rs/sp-runtime-interface-proc-macro" diff --git a/substrate/primitives/runtime-interface/test-wasm-deprecated/Cargo.toml b/substrate/primitives/runtime-interface/test-wasm-deprecated/Cargo.toml index ba09c700e320..9e456e9bcfa0 100644 --- a/substrate/primitives/runtime-interface/test-wasm-deprecated/Cargo.toml +++ b/substrate/primitives/runtime-interface/test-wasm-deprecated/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true build = "build.rs" license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/primitives/runtime-interface/test-wasm/Cargo.toml b/substrate/primitives/runtime-interface/test-wasm/Cargo.toml index 77e77c707d9e..9392930174d0 100644 --- a/substrate/primitives/runtime-interface/test-wasm/Cargo.toml +++ b/substrate/primitives/runtime-interface/test-wasm/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true build = "build.rs" license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/primitives/runtime-interface/test/Cargo.toml b/substrate/primitives/runtime-interface/test/Cargo.toml index 469a63f2473c..29ef0f6b4892 100644 --- a/substrate/primitives/runtime-interface/test/Cargo.toml +++ b/substrate/primitives/runtime-interface/test/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true license = "Apache-2.0" publish = false -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true [lints] diff --git a/substrate/primitives/runtime/Cargo.toml b/substrate/primitives/runtime/Cargo.toml index 83b9422ca1d8..55e9f4b31995 100644 --- a/substrate/primitives/runtime/Cargo.toml +++ b/substrate/primitives/runtime/Cargo.toml @@ -4,7 +4,7 @@ version = "31.0.1" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Runtime Modules shared primitive types." documentation = "https://docs.rs/sp-runtime" diff --git a/substrate/primitives/session/Cargo.toml b/substrate/primitives/session/Cargo.toml index b6b3a91a820a..6abf83505530 100644 --- a/substrate/primitives/session/Cargo.toml +++ b/substrate/primitives/session/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Primitives for sessions" readme = "README.md" diff --git a/substrate/primitives/staking/Cargo.toml b/substrate/primitives/staking/Cargo.toml index 9c92cba66c67..35e7e4f60413 100644 --- a/substrate/primitives/staking/Cargo.toml +++ b/substrate/primitives/staking/Cargo.toml @@ -4,7 +4,7 @@ version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "A crate which contains primitives that are useful for implementation that uses staking approaches in general. Definitions related to sessions, slashing, etc go here." readme = "README.md" diff --git a/substrate/primitives/state-machine/Cargo.toml b/substrate/primitives/state-machine/Cargo.toml index aa3ce2ad2b8b..e1c67feb7ac5 100644 --- a/substrate/primitives/state-machine/Cargo.toml +++ b/substrate/primitives/state-machine/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Substrate State Machine" edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sp-state-machine" readme = "README.md" diff --git a/substrate/primitives/statement-store/Cargo.toml b/substrate/primitives/statement-store/Cargo.toml index c6f8491367c3..aac676caedc9 100644 --- a/substrate/primitives/statement-store/Cargo.toml +++ b/substrate/primitives/statement-store/Cargo.toml @@ -4,7 +4,7 @@ version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "A crate which contains primitives related to the statement store" readme = "README.md" diff --git a/substrate/primitives/std/Cargo.toml b/substrate/primitives/std/Cargo.toml index 0b5276469efd..29792c816ed0 100644 --- a/substrate/primitives/std/Cargo.toml +++ b/substrate/primitives/std/Cargo.toml @@ -4,7 +4,7 @@ version = "14.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Lowest-abstraction level for the Substrate runtime: just exports useful primitives from std or client/alloc to be used with any code that depends on the runtime." documentation = "https://docs.rs/sp-std" diff --git a/substrate/primitives/storage/Cargo.toml b/substrate/primitives/storage/Cargo.toml index 3184ec010930..9341d7ac77e2 100644 --- a/substrate/primitives/storage/Cargo.toml +++ b/substrate/primitives/storage/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true description = "Storage related primitives" license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/sp-storage/" readme = "README.md" diff --git a/substrate/primitives/test-primitives/Cargo.toml b/substrate/primitives/test-primitives/Cargo.toml index e223e8937653..cffdf9801615 100644 --- a/substrate/primitives/test-primitives/Cargo.toml +++ b/substrate/primitives/test-primitives/Cargo.toml @@ -4,7 +4,7 @@ version = "2.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/primitives/timestamp/Cargo.toml b/substrate/primitives/timestamp/Cargo.toml index 7dccf741cd06..0fcd5be98e6f 100644 --- a/substrate/primitives/timestamp/Cargo.toml +++ b/substrate/primitives/timestamp/Cargo.toml @@ -4,7 +4,7 @@ version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate core types and inherents for timestamps." readme = "README.md" diff --git a/substrate/primitives/tracing/Cargo.toml b/substrate/primitives/tracing/Cargo.toml index c434016604ac..7874338aa616 100644 --- a/substrate/primitives/tracing/Cargo.toml +++ b/substrate/primitives/tracing/Cargo.toml @@ -4,7 +4,7 @@ version = "16.0.0" license = "Apache-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Instrumentation primitives and macros for Substrate." readme = "README.md" diff --git a/substrate/primitives/transaction-pool/Cargo.toml b/substrate/primitives/transaction-pool/Cargo.toml index 964fb18b533e..09d57be54ade 100644 --- a/substrate/primitives/transaction-pool/Cargo.toml +++ b/substrate/primitives/transaction-pool/Cargo.toml @@ -4,7 +4,7 @@ version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Transaction pool runtime facing API." documentation = "https://docs.rs/sp-transaction-pool" diff --git a/substrate/primitives/transaction-storage-proof/Cargo.toml b/substrate/primitives/transaction-storage-proof/Cargo.toml index f12b9ef118cb..a9040c1249a1 100644 --- a/substrate/primitives/transaction-storage-proof/Cargo.toml +++ b/substrate/primitives/transaction-storage-proof/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true description = "Transaction storage proof primitives" edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/primitives/trie/Cargo.toml b/substrate/primitives/trie/Cargo.toml index 1fe29f72014a..a28f29b01581 100644 --- a/substrate/primitives/trie/Cargo.toml +++ b/substrate/primitives/trie/Cargo.toml @@ -6,7 +6,7 @@ description = "Patricia trie stuff using a parity-scale-codec node format" repository.workspace = true license = "Apache-2.0" edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true documentation = "https://docs.rs/sp-trie" readme = "README.md" diff --git a/substrate/primitives/version/Cargo.toml b/substrate/primitives/version/Cargo.toml index 65a7c63f432d..0424304989b7 100644 --- a/substrate/primitives/version/Cargo.toml +++ b/substrate/primitives/version/Cargo.toml @@ -4,7 +4,7 @@ version = "29.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Version module for the Substrate runtime; Provides a function that returns the runtime version." documentation = "https://docs.rs/sp-version" diff --git a/substrate/primitives/version/proc-macro/Cargo.toml b/substrate/primitives/version/proc-macro/Cargo.toml index 1feea15b9fcd..35c49360b7f8 100644 --- a/substrate/primitives/version/proc-macro/Cargo.toml +++ b/substrate/primitives/version/proc-macro/Cargo.toml @@ -4,7 +4,7 @@ version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Macro for defining a runtime version." documentation = "https://docs.rs/sp-api-proc-macro" diff --git a/substrate/primitives/wasm-interface/Cargo.toml b/substrate/primitives/wasm-interface/Cargo.toml index f7a1d25d16bf..96ea8f4235d5 100644 --- a/substrate/primitives/wasm-interface/Cargo.toml +++ b/substrate/primitives/wasm-interface/Cargo.toml @@ -4,7 +4,7 @@ version = "20.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Types and traits for interfacing between the host and the wasm runtime." documentation = "https://docs.rs/sp-wasm-interface" diff --git a/substrate/primitives/weights/Cargo.toml b/substrate/primitives/weights/Cargo.toml index c9f4f39d041c..9b830403dbe8 100644 --- a/substrate/primitives/weights/Cargo.toml +++ b/substrate/primitives/weights/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Types and traits for interfacing between the host and the wasm runtime." documentation = "https://docs.rs/sp-wasm-interface" diff --git a/substrate/scripts/ci/node-template-release/Cargo.toml b/substrate/scripts/ci/node-template-release/Cargo.toml index 5ec665de05ea..d335dbcf3971 100644 --- a/substrate/scripts/ci/node-template-release/Cargo.toml +++ b/substrate/scripts/ci/node-template-release/Cargo.toml @@ -4,7 +4,7 @@ version = "3.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0 WITH Classpath-exception-2.0" -homepage = "https://substrate.io" +homepage.workspace = true publish = false [lints] diff --git a/substrate/test-utils/Cargo.toml b/substrate/test-utils/Cargo.toml index 71aac02ba9b6..4f7a70906859 100644 --- a/substrate/test-utils/Cargo.toml +++ b/substrate/test-utils/Cargo.toml @@ -4,7 +4,7 @@ version = "4.0.0-dev" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate test utilities" publish = false diff --git a/substrate/test-utils/cli/Cargo.toml b/substrate/test-utils/cli/Cargo.toml index 52642b9f62bb..3fbcf2090683 100644 --- a/substrate/test-utils/cli/Cargo.toml +++ b/substrate/test-utils/cli/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/test-utils/client/Cargo.toml b/substrate/test-utils/client/Cargo.toml index ecf04dac1a67..ebd1eab5980d 100644 --- a/substrate/test-utils/client/Cargo.toml +++ b/substrate/test-utils/client/Cargo.toml @@ -5,7 +5,7 @@ version = "2.0.1" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/test-utils/runtime/Cargo.toml b/substrate/test-utils/runtime/Cargo.toml index 5c8f49a6db85..1c82c73072bc 100644 --- a/substrate/test-utils/runtime/Cargo.toml +++ b/substrate/test-utils/runtime/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true build = "build.rs" license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/test-utils/runtime/client/Cargo.toml b/substrate/test-utils/runtime/client/Cargo.toml index 3c628d1e764f..5dd3c304f4a8 100644 --- a/substrate/test-utils/runtime/client/Cargo.toml +++ b/substrate/test-utils/runtime/client/Cargo.toml @@ -4,7 +4,7 @@ version = "2.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/test-utils/runtime/transaction-pool/Cargo.toml b/substrate/test-utils/runtime/transaction-pool/Cargo.toml index 073997da025d..b5dc034fed13 100644 --- a/substrate/test-utils/runtime/transaction-pool/Cargo.toml +++ b/substrate/test-utils/runtime/transaction-pool/Cargo.toml @@ -4,7 +4,7 @@ version = "2.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true publish = false diff --git a/substrate/utils/binary-merkle-tree/Cargo.toml b/substrate/utils/binary-merkle-tree/Cargo.toml index c0b65d731f7a..5f7e80549aa4 100644 --- a/substrate/utils/binary-merkle-tree/Cargo.toml +++ b/substrate/utils/binary-merkle-tree/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "Apache-2.0" repository.workspace = true description = "A no-std/Substrate compatible library to construct binary merkle tree." -homepage = "https://substrate.io" +homepage.workspace = true [lints] workspace = true diff --git a/substrate/utils/build-script-utils/Cargo.toml b/substrate/utils/build-script-utils/Cargo.toml index e1152cd9ac8e..60a14cf969ff 100644 --- a/substrate/utils/build-script-utils/Cargo.toml +++ b/substrate/utils/build-script-utils/Cargo.toml @@ -4,7 +4,7 @@ version = "11.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Crate with utility functions for `build.rs` scripts." readme = "README.md" diff --git a/substrate/utils/fork-tree/Cargo.toml b/substrate/utils/fork-tree/Cargo.toml index 2bb799c603c8..c8569a75ac3c 100644 --- a/substrate/utils/fork-tree/Cargo.toml +++ b/substrate/utils/fork-tree/Cargo.toml @@ -4,7 +4,7 @@ version = "12.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Utility library for managing tree-like ordered data with logic for pruning the tree while finalizing nodes." documentation = "https://docs.rs/fork-tree" diff --git a/substrate/utils/frame/benchmarking-cli/Cargo.toml b/substrate/utils/frame/benchmarking-cli/Cargo.toml index bd1a22affd03..4e88e3360e39 100644 --- a/substrate/utils/frame/benchmarking-cli/Cargo.toml +++ b/substrate/utils/frame/benchmarking-cli/Cargo.toml @@ -4,7 +4,7 @@ version = "32.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "CLI for benchmarking FRAME" readme = "README.md" diff --git a/substrate/utils/frame/generate-bags/Cargo.toml b/substrate/utils/frame/generate-bags/Cargo.toml index 934028c9608d..c37c42646699 100644 --- a/substrate/utils/frame/generate-bags/Cargo.toml +++ b/substrate/utils/frame/generate-bags/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Bag threshold generation script for pallet-bag-list" diff --git a/substrate/utils/frame/generate-bags/node-runtime/Cargo.toml b/substrate/utils/frame/generate-bags/node-runtime/Cargo.toml index 6dcbca4b97e2..3d5748647257 100644 --- a/substrate/utils/frame/generate-bags/node-runtime/Cargo.toml +++ b/substrate/utils/frame/generate-bags/node-runtime/Cargo.toml @@ -4,7 +4,7 @@ version = "3.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Bag threshold generation script for pallet-bag-list and kitchensink-runtime." publish = false diff --git a/substrate/utils/frame/remote-externalities/Cargo.toml b/substrate/utils/frame/remote-externalities/Cargo.toml index cc075da68c27..41a0091027c1 100644 --- a/substrate/utils/frame/remote-externalities/Cargo.toml +++ b/substrate/utils/frame/remote-externalities/Cargo.toml @@ -4,7 +4,7 @@ version = "0.35.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "An externalities provided environment that can load itself from remote nodes or cached files" diff --git a/substrate/utils/frame/rpc/client/Cargo.toml b/substrate/utils/frame/rpc/client/Cargo.toml index 1300fae9fff2..d26be3a13124 100644 --- a/substrate/utils/frame/rpc/client/Cargo.toml +++ b/substrate/utils/frame/rpc/client/Cargo.toml @@ -4,7 +4,7 @@ version = "0.33.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Shared JSON-RPC client" diff --git a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml index c8ff281301e3..104d53642eff 100644 --- a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -4,7 +4,7 @@ version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Node-specific RPC methods for interaction with state trie migration." readme = "README.md" diff --git a/substrate/utils/frame/rpc/support/Cargo.toml b/substrate/utils/frame/rpc/support/Cargo.toml index e20bae730c7e..82652c8fa262 100644 --- a/substrate/utils/frame/rpc/support/Cargo.toml +++ b/substrate/utils/frame/rpc/support/Cargo.toml @@ -4,7 +4,7 @@ version = "29.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "Substrate RPC for FRAME's support" diff --git a/substrate/utils/frame/rpc/system/Cargo.toml b/substrate/utils/frame/rpc/system/Cargo.toml index 4e866113fd2e..5757a48498c7 100644 --- a/substrate/utils/frame/rpc/system/Cargo.toml +++ b/substrate/utils/frame/rpc/system/Cargo.toml @@ -4,7 +4,7 @@ version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true description = "FRAME's system exposed over Substrate RPC" readme = "README.md" diff --git a/substrate/utils/prometheus/Cargo.toml b/substrate/utils/prometheus/Cargo.toml index 9f5516cd8d74..9bdec3cb8183 100644 --- a/substrate/utils/prometheus/Cargo.toml +++ b/substrate/utils/prometheus/Cargo.toml @@ -5,7 +5,7 @@ version = "0.17.0" license = "Apache-2.0" authors.workspace = true edition.workspace = true -homepage = "https://substrate.io" +homepage.workspace = true repository.workspace = true readme = "README.md" diff --git a/substrate/utils/wasm-builder/Cargo.toml b/substrate/utils/wasm-builder/Cargo.toml index 28fdebf209bd..15a1fd007ca2 100644 --- a/substrate/utils/wasm-builder/Cargo.toml +++ b/substrate/utils/wasm-builder/Cargo.toml @@ -6,7 +6,7 @@ description = "Utility for building WASM binaries" edition.workspace = true repository.workspace = true license = "Apache-2.0" -homepage = "https://substrate.io" +homepage.workspace = true [lints] workspace = true From d31e985e3a35477b8af2ac2bc9abc83ef3ea1497 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 28 Jul 2024 07:09:29 +0000 Subject: [PATCH 030/382] Bump spinners from 4.1.0 to 4.1.1 (#5134) Bumps [spinners](https://github.com/fgribreau/spinners) from 4.1.0 to 4.1.1.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=spinners&package-manager=cargo&previous-version=4.1.0&new-version=4.1.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 05386203452c..f02918a49abe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20852,9 +20852,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spinners" -version = "4.1.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08615eea740067d9899969bc2891c68a19c315cb1f66640af9a9ecb91b13bcab" +checksum = "a0ef947f358b9c238923f764c72a4a9d42f2d637c46e059dbd319d6e7cfb4f82" dependencies = [ "lazy_static", "maplit", diff --git a/Cargo.toml b/Cargo.toml index e07ec97be3a2..9d1f83d3f2cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1263,7 +1263,7 @@ sp-version = { path = "substrate/primitives/version", default-features = false } sp-version-proc-macro = { path = "substrate/primitives/version/proc-macro", default-features = false } sp-wasm-interface = { path = "substrate/primitives/wasm-interface", default-features = false } sp-weights = { path = "substrate/primitives/weights", default-features = false } -spinners = { version = "4.1.0" } +spinners = { version = "4.1.1" } ss58-registry = { version = "1.34.0", default-features = false } ssz_rs = { version = "0.9.0", default-features = false } ssz_rs_derive = { version = "0.9.0", default-features = false } From 10b8039a53ddaa10ca37fe70c64e549c649a22dc Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Sun, 28 Jul 2024 16:10:43 +0900 Subject: [PATCH 031/382] Re: fix warnings with latest rust (#5161) I made mistake on previous PR https://github.com/paritytech/polkadot-sdk/pull/5150. It disabled all unexpected cfgs instead of just allowing `substrate_runtime` condition. In this PR: unexpected cfgs other than `substrate_runtime` are still checked. and some warnings appear about them --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9d1f83d3f2cf..1950eb2c8d3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -543,7 +543,7 @@ default-members = [ [workspace.lints.rust] suspicious_double_ref_op = { level = "allow", priority = 2 } # `substrate_runtime` is a common `cfg` condition name used in the repo. -unexpected_cfgs = { level = "allow", check-cfg = ['cfg(substrate_runtime)'] } +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(substrate_runtime)'] } [workspace.lints.clippy] all = { level = "allow", priority = 0 } From f5d4e9204a0bd923e18907187d1e8fbf76b6b83a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 28 Jul 2024 21:23:18 +0200 Subject: [PATCH 032/382] Bump the ci_dependencies group with 5 updates (#5146) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the ci_dependencies group with 5 updates: | Package | From | To | | --- | --- | --- | | [actions/checkout](https://github.com/actions/checkout) | `2` | `4` | | [actions/cache](https://github.com/actions/cache) | `3.3.3` | `4.0.2` | | [actions/setup-python](https://github.com/actions/setup-python) | `2` | `5` | | [actions/download-artifact](https://github.com/actions/download-artifact) | `4.1.4` | `4.1.8` | | [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `3.0.0` | `3.5.0` | Updates `actions/checkout` from 2 to 4
Release notes

Sourced from actions/checkout's releases.

v4.0.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3...v4.0.0

v3.6.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3.5.3...v3.6.0

v3.5.3

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3...v3.5.3

v3.5.2

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v3.5.1...v3.5.2

v3.5.1

What's Changed

New Contributors

... (truncated)

Commits

Updates `actions/cache` from 3.3.3 to 4.0.2
Release notes

Sourced from actions/cache's releases.

v4.0.2

What's Changed

Full Changelog: https://github.com/actions/cache/compare/v4.0.1...v4.0.2

v4.0.1

What's Changed

New Contributors

Full Changelog: https://github.com/actions/cache/compare/v4...v4.0.1

v4.0.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/cache/compare/v3...v4.0.0

Changelog

Sourced from actions/cache's changelog.

Releases

4.0.2

  • Fixed restore fail-on-cache-miss not working.

4.0.1

  • Updated isGhes check

4.0.0

  • Updated minimum runner version support from node 12 -> node 20

3.3.3

  • Updates @​actions/cache to v3.2.3 to fix accidental mutated path arguments to getCacheVersion actions/toolkit#1378
  • Additional audit fixes of npm package(s)

3.3.2

  • Fixes bug with Azure SDK causing blob downloads to get stuck.

3.3.1

  • Reduced segment size to 128MB and segment timeout to 10 minutes to fail fast in case the cache download is stuck.

3.3.0

  • Added option to lookup cache without downloading it.

3.2.6

  • Fix zstd not being used after zstd version upgrade to 1.5.4 on hosted runners.

3.2.5

  • Added fix to prevent from setting MYSYS environment variable globally.

3.2.4

  • Added option to fail job on cache miss.

3.2.3

  • Support cross os caching on Windows as an opt-in feature.
  • Fix issue with symlink restoration on Windows for cross-os caches.

3.2.2

... (truncated)

Commits

Updates `actions/setup-python` from 2 to 5
Release notes

Sourced from actions/setup-python's releases.

v5.0.0

What's Changed

In scope of this release, we update node version runtime from node16 to node20 (actions/setup-python#772). Besides, we update dependencies to the latest versions.

Full Changelog: https://github.com/actions/setup-python/compare/v4.8.0...v5.0.0

v4.8.0

What's Changed

In scope of this release we added support for GraalPy (actions/setup-python#694). You can use this snippet to set up GraalPy:

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
  with:
    python-version: 'graalpy-22.3'
- run: python my_script.py

Besides, the release contains such changes as:

New Contributors

Full Changelog: https://github.com/actions/setup-python/compare/v4...v4.8.0

v4.7.1

What's Changed

Full Changelog: https://github.com/actions/setup-python/compare/v4...v4.7.1

v4.7.0

In scope of this release, the support for reading python version from pyproject.toml was added (actions/setup-python#669).

      - name: Setup Python
        uses: actions/setup-python@v4
</tr></table>

... (truncated)

Commits
  • 39cd149 Documentation update for cache (#873)
  • a0d74c0 fix(ci): update all failing workflows (#863)
  • 4eb7dbc Bump braces from 3.0.2 to 3.0.3 (#893)
  • 82c7e63 Documentation changes for avoiding rate limit issues on GHES (#835)
  • 10aa35a feat: fallback to raw endpoint for manifest when rate limit is reached (#766)
  • 9a7ac94 Bump undici from 5.27.2 to 5.28.3 (#817)
  • 871daa9 Fix the "Specifying multiple Python/PyPy versions" link (#782)
  • 2f07895 Fix broken README.md link (#793)
  • e9d6f99 Replace setup-python@v4 by setup-python@v5 in README (#776)
  • 0a5c615 Update action to node20 (#772)
  • Additional commits viewable in compare view

Updates `actions/download-artifact` from 4.1.4 to 4.1.8
Release notes

Sourced from actions/download-artifact's releases.

v4.1.8

What's Changed

Full Changelog: https://github.com/actions/download-artifact/compare/v4...v4.1.8

v4.1.7

What's Changed

Full Changelog: https://github.com/actions/download-artifact/compare/v4.1.6...v4.1.7

v4.1.6

What's Changed

Full Changelog: https://github.com/actions/download-artifact/compare/v4.1.5...v4.1.6

v4.1.5

What's Changed

Full Changelog: https://github.com/actions/download-artifact/compare/v4.1.4...v4.1.5

Commits
  • fa0a91b Merge pull request #341 from actions/robherley/bump-pkgs
  • b54d088 Update @​actions/artifact version, bump dependencies
  • 65a9edc Merge pull request #325 from bethanyj28/main
  • fdd1595 licensed
  • c13dba1 update @​actions/artifact dependency
  • 0daa75e Merge pull request #324 from actions/eggyhead/use-artifact-v2.1.6
  • 9c19ed7 Merge branch 'main' into eggyhead/use-artifact-v2.1.6
  • 3d3ea87 updating license
  • 89af5db updating artifact package v2.1.6
  • b4aefff Merge pull request #323 from actions/eggyhead/update-artifact-v215
  • Additional commits viewable in compare view

Updates `docker/setup-buildx-action` from 3.0.0 to 3.5.0
Release notes

Sourced from docker/setup-buildx-action's releases.

v3.5.0

Full Changelog: https://github.com/docker/setup-buildx-action/compare/v3.4.0...v3.5.0

v3.4.0

Full Changelog: https://github.com/docker/setup-buildx-action/compare/v3.3.0...v3.4.0

v3.3.0

Full Changelog: https://github.com/docker/setup-buildx-action/compare/v3.2.0...v3.3.0

v3.2.0

[!NOTE] config and config-inline input names are deprecated and will be removed in next major release.

Full Changelog: https://github.com/docker/setup-buildx-action/compare/v3.1.0...v3.2.0

v3.1.0

Full Changelog: https://github.com/docker/setup-buildx-action/compare/v3.0.0...v3.1.0

Commits
  • aa33708 Merge pull request #345 from docker/dependabot/npm_and_yarn/docker/actions-to...
  • 2d99e34 chore: update generated content
  • 4dab436 build(deps): bump @​docker/actions-toolkit from 0.34.0 to 0.35.0
  • 49a04d6 Merge pull request #344 from docker/dependabot/npm_and_yarn/docker/actions-to...
  • a6ade2e chore: update generated content
  • 2f2694b switch to Docker exec
  • 0a4bab6 build(deps): bump @​docker/actions-toolkit from 0.32.0 to 0.34.0
  • 2ad1852 Merge pull request #340 from docker/dependabot/npm_and_yarn/docker/actions-to...
  • 560ac46 chore: update generated content
  • b3a3417 build(deps): bump @​docker/actions-toolkit from 0.31.0 to 0.32.0
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bastian Köcher --- .github/workflows/check-licenses.yml | 2 +- .github/workflows/check-links.yml | 4 ++-- .github/workflows/check-prdoc.yml | 2 +- .github/workflows/check-semver.yml | 2 +- .github/workflows/checks-quick.yml | 16 ++++++++-------- .github/workflows/misc-sync-templates.yml | 4 ++-- .../misc-update-wishlist-leaderboard.yml | 4 ++-- .github/workflows/publish-check-crates.yml | 2 +- .github/workflows/publish-claim-crates.yml | 2 +- .../workflows/publish-subsystem-benchmarks.yml | 2 +- .github/workflows/release-10_rc-automation.yml | 2 +- .../release-30_publish_release_draft.yml | 12 ++++++------ .github/workflows/release-50_publish-docker.yml | 12 ++++++------ .github/workflows/release-check-runtimes.yml | 4 ++-- .github/workflows/release-clobber-stable.yml | 2 +- .github/workflows/release-srtool.yml | 4 ++-- 16 files changed, 38 insertions(+), 38 deletions(-) diff --git a/.github/workflows/check-licenses.yml b/.github/workflows/check-licenses.yml index b77135805409..a74986048976 100644 --- a/.github/workflows/check-licenses.yml +++ b/.github/workflows/check-licenses.yml @@ -16,7 +16,7 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Checkout sources - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/setup-node@v4.0.3 with: node-version: "18.x" diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index 090063e126b1..d10f34e6faef 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -18,14 +18,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Restore lychee cache - uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c # v3.3.2 (7. Sep 2023) + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v3.3.2 (7. Sep 2023) with: path: .lycheecache key: cache-lychee-${{ github.sha }} # This should restore from the most recent one: restore-keys: cache-lychee- - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.0 (22. Sep 2023) + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.0 (22. Sep 2023) - name: Lychee link checker uses: lycheeverse/lychee-action@2b973e86fc7b1f6b36a93795fe2c9c6ae1118621 # for v1.9.1 (10. Jan 2024) diff --git a/.github/workflows/check-prdoc.yml b/.github/workflows/check-prdoc.yml index 5df03f1044d8..69311c41dd6f 100644 --- a/.github/workflows/check-prdoc.yml +++ b/.github/workflows/check-prdoc.yml @@ -20,7 +20,7 @@ jobs: if: github.event.pull_request.number != '' steps: - name: Checkout repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7 # we cannot show the version in this step (ie before checking out the repo) # due to https://github.com/paritytech/prdoc/issues/15 - name: Check if PRdoc is required diff --git a/.github/workflows/check-semver.yml b/.github/workflows/check-semver.yml index f14533c2e2c9..7d9fb68b90bf 100644 --- a/.github/workflows/check-semver.yml +++ b/.github/workflows/check-semver.yml @@ -21,7 +21,7 @@ jobs: container: image: docker.io/paritytech/ci-unified:bullseye-1.77.0-2024-04-10-v20240408 steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: extra git setup run: | diff --git a/.github/workflows/checks-quick.yml b/.github/workflows/checks-quick.yml index 678bfb6f2058..ee4bd62a558d 100644 --- a/.github/workflows/checks-quick.yml +++ b/.github/workflows/checks-quick.yml @@ -35,14 +35,14 @@ jobs: container: image: ${{ needs.set-image.outputs.IMAGE }} steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Cargo fmt run: cargo +nightly fmt --all -- --check check-dependency-rules: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: check dependency rules run: | cd substrate/ @@ -54,7 +54,7 @@ jobs: container: image: ${{ needs.set-image.outputs.IMAGE }} steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: fetch deps run: | # Pull all dependencies eagerly: @@ -70,7 +70,7 @@ jobs: container: image: ${{ needs.set-image.outputs.IMAGE }} steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: run rust features run: bash .gitlab/rust-features.sh . check-toml-format: @@ -80,7 +80,7 @@ jobs: container: image: ${{ needs.set-image.outputs.IMAGE }} steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: check toml format run: | taplo format --check --config .config/taplo.toml @@ -89,7 +89,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.0 (22. Sep 2023) + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.0 (22. Sep 2023) - name: install python deps run: | sudo apt-get update && sudo apt-get install -y python3-pip python3 @@ -107,7 +107,7 @@ jobs: timeout-minutes: 20 steps: - name: Checkout sources - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup Node.js uses: actions/setup-node@v4.0.3 with: @@ -132,7 +132,7 @@ jobs: container: image: ${{ needs.set-image.outputs.IMAGE }} steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.0 (22. Sep 2023) + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.0 (22. Sep 2023) - name: install python deps run: pip3 install "cargo-workspace>=1.2.4" toml - name: check umbrella correctness diff --git a/.github/workflows/misc-sync-templates.yml b/.github/workflows/misc-sync-templates.yml index b53d129f5cb5..35e08fcaa131 100644 --- a/.github/workflows/misc-sync-templates.yml +++ b/.github/workflows/misc-sync-templates.yml @@ -41,7 +41,7 @@ jobs: run: | git config --global user.name "Template Bot" git config --global user.email "163342540+paritytech-polkadotsdk-templatebot[bot]@users.noreply.github.com" - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: polkadot-sdk ref: "release-crates-io-v${{ github.event.inputs.crate_release_version }}" @@ -53,7 +53,7 @@ jobs: repositories: "polkadot-sdk-${{ matrix.template }}-template" app-id: ${{ secrets.TEMPLATE_APP_ID }} private-key: ${{ secrets.TEMPLATE_APP_KEY }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: "paritytech/polkadot-sdk-${{ matrix.template }}-template" path: "${{ env.template-path }}" diff --git a/.github/workflows/misc-update-wishlist-leaderboard.yml b/.github/workflows/misc-update-wishlist-leaderboard.yml index a825c4641124..68625e5433ca 100644 --- a/.github/workflows/misc-update-wishlist-leaderboard.yml +++ b/.github/workflows/misc-update-wishlist-leaderboard.yml @@ -13,9 +13,9 @@ jobs: update-wishlist-leaderboard: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: '3.x' - name: Install dependencies diff --git a/.github/workflows/publish-check-crates.yml b/.github/workflows/publish-check-crates.yml index 92b4d13e9deb..e627987105a4 100644 --- a/.github/workflows/publish-check-crates.yml +++ b/.github/workflows/publish-check-crates.yml @@ -12,7 +12,7 @@ jobs: check-publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Rust Cache uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 diff --git a/.github/workflows/publish-claim-crates.yml b/.github/workflows/publish-claim-crates.yml index b6a84090f729..167a10a8389b 100644 --- a/.github/workflows/publish-claim-crates.yml +++ b/.github/workflows/publish-claim-crates.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest environment: master steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Rust Cache uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 diff --git a/.github/workflows/publish-subsystem-benchmarks.yml b/.github/workflows/publish-subsystem-benchmarks.yml index 1a726b669e90..e5b9db0836f3 100644 --- a/.github/workflows/publish-subsystem-benchmarks.yml +++ b/.github/workflows/publish-subsystem-benchmarks.yml @@ -23,7 +23,7 @@ jobs: echo "${{ github.event.inputs.output-file-path }}" | grep -P '^[a-z\-]+\.json' - name: Checkout Sources - uses: actions/checkout@v4.1.2 + uses: actions/checkout@v4 with: fetch-depth: 0 ref: "gh-pages" diff --git a/.github/workflows/release-10_rc-automation.yml b/.github/workflows/release-10_rc-automation.yml index f5c5de8d0da9..2d91850b82c1 100644 --- a/.github/workflows/release-10_rc-automation.yml +++ b/.github/workflows/release-10_rc-automation.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: fetch-depth: 0 diff --git a/.github/workflows/release-30_publish_release_draft.yml b/.github/workflows/release-30_publish_release_draft.yml index 20492f2d3a91..6d31ca7a7365 100644 --- a/.github/workflows/release-30_publish_release_draft.yml +++ b/.github/workflows/release-30_publish_release_draft.yml @@ -35,7 +35,7 @@ jobs: binary: [ [frame-omni-bencher, frame-omni-bencher], [staging-chain-spec-builder, chain-spec-builder] ] steps: - name: Checkout sources - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 - name: Install protobuf-compiler run: | @@ -62,10 +62,10 @@ jobs: asset_upload_url: ${{ steps.create-release.outputs.upload_url }} steps: - name: Checkout - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 - name: Download artifacts - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - name: Prepare tooling run: | @@ -133,10 +133,10 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 - name: Download artifacts - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - name: Get runtime info env: @@ -166,7 +166,7 @@ jobs: steps: - name: Download artifacts - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: ${{ matrix.binary }} diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index 63124a489e9d..cda10f2ebf15 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -80,7 +80,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 #TODO: this step will be needed when automated triggering will work #this step runs only if the workflow is triggered automatically when new release is published @@ -129,10 +129,10 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Download artifacts - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - name: Check sha256 ${{ env.BINARY }} if: ${{ inputs.binary == 'polkadot-parachain' || inputs.binary == 'polkadot' }} @@ -261,13 +261,13 @@ jobs: environment: release steps: - name: Checkout sources - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # v3.5.0 - name: Cache Docker layers - uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c # v3.3.3 + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} diff --git a/.github/workflows/release-check-runtimes.yml b/.github/workflows/release-check-runtimes.yml index 0e5ad104766a..930b8da772d0 100644 --- a/.github/workflows/release-check-runtimes.yml +++ b/.github/workflows/release-check-runtimes.yml @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Get list id: get-list @@ -56,7 +56,7 @@ jobs: steps: - name: Checkout the repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Fetch release artifacts based on release id env: diff --git a/.github/workflows/release-clobber-stable.yml b/.github/workflows/release-clobber-stable.yml index 643c14daa15b..50c20563b434 100644 --- a/.github/workflows/release-clobber-stable.yml +++ b/.github/workflows/release-clobber-stable.yml @@ -24,7 +24,7 @@ jobs: AUDITED: audited steps: - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Prechecks run: | diff --git a/.github/workflows/release-srtool.yml b/.github/workflows/release-srtool.yml index e98269fecab0..e1dc42afc6e9 100644 --- a/.github/workflows/release-srtool.yml +++ b/.github/workflows/release-srtool.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 with: fetch-depth: 0 @@ -67,7 +67,7 @@ jobs: matrix: ${{ fromJSON(needs.find-runtimes.outputs.runtime) }} steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 with: fetch-depth: 0 From dc4047c7d05f593757328394d1accf0eb382d709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <123550+andresilva@users.noreply.github.com> Date: Sun, 28 Jul 2024 21:03:48 +0100 Subject: [PATCH 033/382] grandpa: handle error from SelectChain::finality_target (#5153) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix https://github.com/paritytech/polkadot-sdk/issues/3487. --------- Co-authored-by: Dmitry Lavrenov <39522748+dmitrylavrenov@users.noreply.github.com> Co-authored-by: Bastian Köcher Co-authored-by: Bastian Köcher --- prdoc/pr_5153.prdoc | 12 ++ .../consensus/grandpa/src/environment.rs | 10 +- .../client/consensus/grandpa/src/tests.rs | 110 ++++++++++++++++++ 3 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_5153.prdoc diff --git a/prdoc/pr_5153.prdoc b/prdoc/pr_5153.prdoc new file mode 100644 index 000000000000..4f43b52d8edf --- /dev/null +++ b/prdoc/pr_5153.prdoc @@ -0,0 +1,12 @@ +title: "Grandpa: Ensure voting doesn't fail after a re-org" + +doc: + - audience: Node Operator + description: | + Ensures that a node is still able to vote with Grandpa, when a re-org happened that + changed the best chain. This ultimately prevents that a network may runs into a + potential finality stall. + +crates: + - name: sc-consensus-grandpa + bump: patch diff --git a/substrate/client/consensus/grandpa/src/environment.rs b/substrate/client/consensus/grandpa/src/environment.rs index 6199e8a97d99..a618b7ff07ad 100644 --- a/substrate/client/consensus/grandpa/src/environment.rs +++ b/substrate/client/consensus/grandpa/src/environment.rs @@ -1214,14 +1214,20 @@ where .header(target_hash)? .expect("Header known to exist after `finality_target` call; qed"), Err(err) => { - warn!( + debug!( target: LOG_TARGET, "Encountered error finding best chain containing {:?}: couldn't find target block: {}", block, err, ); - return Ok(None) + // NOTE: in case the given `SelectChain` doesn't provide any block we fallback to using + // the given base block provided by the GRANDPA voter. + // + // For example, `LongestChain` will error if the given block to use as base isn't part + // of the best chain (as defined by `LongestChain`), which could happen if there was a + // re-org. + base_header.clone() }, }; diff --git a/substrate/client/consensus/grandpa/src/tests.rs b/substrate/client/consensus/grandpa/src/tests.rs index 14708cc89e89..2aa1b5f6ee1b 100644 --- a/substrate/client/consensus/grandpa/src/tests.rs +++ b/substrate/client/consensus/grandpa/src/tests.rs @@ -1820,6 +1820,116 @@ async fn grandpa_environment_checks_if_best_block_is_descendent_of_finality_targ ); } +// This is a regression test for an issue that was triggered by a reorg +// - https://github.com/paritytech/polkadot-sdk/issues/3487 +// - https://github.com/humanode-network/humanode/issues/1104 +#[tokio::test] +async fn grandpa_environment_uses_round_base_block_for_voting_if_finality_target_errors() { + use finality_grandpa::voter::Environment; + use sp_consensus::SelectChain; + + let peers = &[Ed25519Keyring::Alice]; + let voters = make_ids(peers); + + let mut net = GrandpaTestNet::new(TestApi::new(voters), 1, 0); + let peer = net.peer(0); + let network_service = peer.network_service().clone(); + let sync_service = peer.sync_service().clone(); + let notification_service = + peer.take_notification_service(&grandpa_protocol_name::NAME.into()).unwrap(); + let link = peer.data.lock().take().unwrap(); + let client = peer.client().as_client().clone(); + let select_chain = sc_consensus::LongestChain::new(peer.client().as_backend()); + + // create a chain that is 10 blocks long + peer.push_blocks(10, false); + + let env = test_environment_with_select_chain( + &link, + None, + network_service.clone(), + sync_service, + notification_service, + select_chain.clone(), + VotingRulesBuilder::default().build(), + ); + + let hashof7 = client.expect_block_hash_from_id(&BlockId::Number(7)).unwrap(); + let hashof8_a = client.expect_block_hash_from_id(&BlockId::Number(8)).unwrap(); + + // finalize the 7th block + peer.client().finalize_block(hashof7, None, false).unwrap(); + + assert_eq!(peer.client().info().finalized_hash, hashof7); + + // simulate completed grandpa round + env.completed( + 1, + finality_grandpa::round::State { + prevote_ghost: Some((hashof8_a, 8)), + finalized: Some((hashof7, 7)), + estimate: Some((hashof8_a, 8)), + completable: true, + }, + Default::default(), + &finality_grandpa::HistoricalVotes::new(), + ) + .unwrap(); + + // check simulated last completed round + assert_eq!( + env.voter_set_state.read().last_completed_round().state, + finality_grandpa::round::State { + prevote_ghost: Some((hashof8_a, 8)), + finalized: Some((hashof7, 7)), + estimate: Some((hashof8_a, 8)), + completable: true + } + ); + + // `hashof8_a` should be finalized next, `best_chain_containing` should return `hashof8_a` + assert_eq!(env.best_chain_containing(hashof8_a).await.unwrap().unwrap().0, hashof8_a); + + // simulate reorg on block 8 by creating a fork starting at block 7 that is 10 blocks long + peer.generate_blocks_at( + BlockId::Number(7), + 10, + BlockOrigin::File, + |mut builder| { + builder.push_deposit_log_digest_item(DigestItem::Other(vec![1])).unwrap(); + builder.build().unwrap().block + }, + false, + false, + true, + ForkChoiceStrategy::LongestChain, + ); + + // check that new best chain is on longest chain + assert_eq!(env.select_chain.best_chain().await.unwrap().number, 17); + + // verify that last completed round has `prevote_ghost` and `estimate` blocks related to + // `hashof8_a` + assert_eq!( + env.voter_set_state.read().last_completed_round().state, + finality_grandpa::round::State { + prevote_ghost: Some((hashof8_a, 8)), + finalized: Some((hashof7, 7)), + estimate: Some((hashof8_a, 8)), + completable: true + } + ); + + // `hashof8_a` should be finalized next, `best_chain_containing` should still return `hashof8_a` + assert_eq!(env.best_chain_containing(hashof8_a).await.unwrap().unwrap().0, hashof8_a); + + // simulate finalization of the `hashof8_a` block + peer.client().finalize_block(hashof8_a, None, false).unwrap(); + + // check that best chain is reorged back + assert_eq!(env.select_chain.best_chain().await.unwrap().number, 10); +} + #[tokio::test] async fn grandpa_environment_never_overwrites_round_voter_state() { use finality_grandpa::voter::Environment; From c8e5fba12f321306c334923a07968119dc47ded6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 07:16:50 +0000 Subject: [PATCH 034/382] Bump ref-cast from 1.0.20 to 1.0.23 (#5097) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [ref-cast](https://github.com/dtolnay/ref-cast) from 1.0.20 to 1.0.23.
Release notes

Sourced from ref-cast's releases.

1.0.23

  • Resolve unexpected_cfgs warning (#47)

1.0.22

  • Update proc-macro2 to fix caching issue when using a rustc-wrapper such as sccache

1.0.21

Commits
  • 06c520c Release 1.0.23
  • fd3256d Merge pull request #47 from dtolnay/checkcfg
  • 473dc81 Resolve unexpected_cfgs warning
  • b4d78e2 Revert "Temporarily disable miri on doctests"
  • 24de24e Resolve legacy_numeric_constants clippy lints
  • 33b0b8f Update ui test suite to nightly-2024-03-31
  • bd301af Explicitly install a Rust toolchain for cargo-outdated job
  • b747bf7 Temporarily disable miri on doctests
  • 0b59588 Raise minimum tested compiler to 1.70
  • ad5cadb Update ui test suite to nightly-2024-03-06
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ref-cast&package-manager=cargo&previous-version=1.0.20&new-version=1.0.23)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f02918a49abe..6b8f0b74753e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16062,18 +16062,18 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2 1.0.82", "quote 1.0.36", diff --git a/Cargo.toml b/Cargo.toml index 1950eb2c8d3a..d59c2b790b35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1086,7 +1086,7 @@ rand_distr = { version = "0.4.3" } rand_pcg = { version = "0.3.1" } rayon = { version = "1.5.1" } rbtag = { version = "0.3" } -ref-cast = { version = "1.0.0" } +ref-cast = { version = "1.0.23" } regex = { version = "1.10.2" } relay-substrate-client = { path = "bridges/relays/client-substrate" } relay-utils = { path = "bridges/relays/utils" } From cb9fdb57faf02a7de3c2552f241e4e100ebfdcf8 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Mon, 29 Jul 2024 10:06:30 +0200 Subject: [PATCH 035/382] [pallet_contracts] Modify the storage host function benchmarks to run on an unbalanced storage trie. (#5036) This PR modifies the storage host function benchmarks. Previously, they were run on an empty storage trie. Now, they are run on an unbalanced storage trie to reflect worst case scenario. This approach increases the storage host function weights and decreases the probability of DoS attacks. --------- Co-authored-by: command-bot <> Co-authored-by: PG Herveou --- prdoc/pr_5036.prdoc | 16 + .../frame/contracts/src/benchmarking/mod.rs | 130 +++ substrate/frame/contracts/src/storage.rs | 32 +- substrate/frame/contracts/src/wasm/runtime.rs | 69 +- substrate/frame/contracts/src/weights.rs | 950 ++++++++++-------- 5 files changed, 730 insertions(+), 467 deletions(-) create mode 100644 prdoc/pr_5036.prdoc diff --git a/prdoc/pr_5036.prdoc b/prdoc/pr_5036.prdoc new file mode 100644 index 000000000000..e9f21f823b64 --- /dev/null +++ b/prdoc/pr_5036.prdoc @@ -0,0 +1,16 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "[pallet_contracts] Modify the storage host function benchmarks to be run on an unbalanced storage trie" + +doc: + - audience: Runtime User + description: | + This PR modifies the storage host function benchmarks. Previously, they were run + on an empty storage trie. Now, they are run on an unbalanced storage trie + to reflect the worst-case scenario. This approach increases the storage host + function weights and decreases the probability of DoS attacks. + +crates: + - name: pallet-contracts + bump: patch diff --git a/substrate/frame/contracts/src/benchmarking/mod.rs b/substrate/frame/contracts/src/benchmarking/mod.rs index 620e6544b08f..669279f12b27 100644 --- a/substrate/frame/contracts/src/benchmarking/mod.rs +++ b/substrate/frame/contracts/src/benchmarking/mod.rs @@ -41,6 +41,7 @@ use frame_benchmarking::v2::*; use frame_support::{ self, assert_ok, pallet_prelude::StorageVersion, + storage::child, traits::{fungible::InspectHold, Currency}, weights::{Weight, WeightMeter}, }; @@ -63,6 +64,9 @@ const API_BENCHMARK_RUNS: u32 = 1600; /// benchmarks are faster. const INSTR_BENCHMARK_RUNS: u32 = 5000; +/// Number of layers in a Radix16 unbalanced trie. +const UNBALANCED_TRIE_LAYERS: u32 = 20; + /// An instantiated and deployed contract. #[derive(Clone)] struct Contract { @@ -152,6 +156,36 @@ where Ok(()) } + /// Create a new contract with the specified unbalanced storage trie. + fn with_unbalanced_storage_trie(code: WasmModule, key: &[u8]) -> Result { + if (key.len() as u32) < (UNBALANCED_TRIE_LAYERS + 1) / 2 { + return Err("Key size too small to create the specified trie"); + } + + let value = vec![16u8; T::Schedule::get().limits.payload_len as usize]; + let contract = Contract::::new(code, vec![])?; + let info = contract.info()?; + let child_trie_info = info.child_trie_info(); + child::put_raw(&child_trie_info, &key, &value); + for l in 0..UNBALANCED_TRIE_LAYERS { + let pos = l as usize / 2; + let mut key_new = key.to_vec(); + for i in 0u8..16 { + key_new[pos] = if l % 2 == 0 { + (key_new[pos] & 0xF0) | i + } else { + (key_new[pos] & 0x0F) | (i << 4) + }; + + if key == &key_new { + continue + } + child::put_raw(&child_trie_info, &key_new, &value); + } + } + Ok(contract) + } + /// Get the `ContractInfo` of the `addr` or an error if it no longer exists. fn address_info(addr: &T::AccountId) -> Result, &'static str> { ContractInfoOf::::get(addr).ok_or("Expected contract to exist at this point.") @@ -1014,6 +1048,102 @@ mod benchmarks { assert_eq!(setup.debug_message().unwrap().len() as u32, i); } + #[benchmark(skip_meta, pov_mode = Measured)] + fn get_storage_empty() -> Result<(), BenchmarkError> { + let max_key_len = T::MaxStorageKeyLen::get(); + let key = vec![0u8; max_key_len as usize]; + let max_value_len = T::Schedule::get().limits.payload_len as usize; + let value = vec![1u8; max_value_len]; + + let instance = Contract::::new(WasmModule::dummy(), vec![])?; + let info = instance.info()?; + let child_trie_info = info.child_trie_info(); + info.bench_write_raw(&key, Some(value.clone()), false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let result; + #[block] + { + result = child::get_raw(&child_trie_info, &key); + } + + assert_eq!(result, Some(value)); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn get_storage_full() -> Result<(), BenchmarkError> { + let max_key_len = T::MaxStorageKeyLen::get(); + let key = vec![0u8; max_key_len as usize]; + let max_value_len = T::Schedule::get().limits.payload_len; + let value = vec![1u8; max_value_len as usize]; + + let instance = Contract::::with_unbalanced_storage_trie(WasmModule::dummy(), &key)?; + let info = instance.info()?; + let child_trie_info = info.child_trie_info(); + info.bench_write_raw(&key, Some(value.clone()), false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let result; + #[block] + { + result = child::get_raw(&child_trie_info, &key); + } + + assert_eq!(result, Some(value)); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn set_storage_empty() -> Result<(), BenchmarkError> { + let max_key_len = T::MaxStorageKeyLen::get(); + let key = vec![0u8; max_key_len as usize]; + let max_value_len = T::Schedule::get().limits.payload_len as usize; + let value = vec![1u8; max_value_len]; + + let instance = Contract::::new(WasmModule::dummy(), vec![])?; + let info = instance.info()?; + let child_trie_info = info.child_trie_info(); + info.bench_write_raw(&key, Some(vec![42u8; max_value_len]), false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let val = Some(value.clone()); + let result; + #[block] + { + result = info.bench_write_raw(&key, val, true); + } + + assert_ok!(result); + assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn set_storage_full() -> Result<(), BenchmarkError> { + let max_key_len = T::MaxStorageKeyLen::get(); + let key = vec![0u8; max_key_len as usize]; + let max_value_len = T::Schedule::get().limits.payload_len; + let value = vec![1u8; max_value_len as usize]; + + let instance = Contract::::with_unbalanced_storage_trie(WasmModule::dummy(), &key)?; + let info = instance.info()?; + let child_trie_info = info.child_trie_info(); + info.bench_write_raw(&key, Some(vec![42u8; max_value_len as usize]), false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let val = Some(value.clone()); + let result; + #[block] + { + result = info.bench_write_raw(&key, val, true); + } + + assert_ok!(result); + assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value); + Ok(()) + } + // n: new byte size // o: old byte size #[benchmark(skip_meta, pov_mode = Measured)] diff --git a/substrate/frame/contracts/src/storage.rs b/substrate/frame/contracts/src/storage.rs index 65e7129cdf84..c0201266c816 100644 --- a/substrate/frame/contracts/src/storage.rs +++ b/substrate/frame/contracts/src/storage.rs @@ -164,13 +164,35 @@ impl ContractInfo { storage_meter: Option<&mut meter::NestedMeter>, take: bool, ) -> Result { - let child_trie_info = &self.child_trie_info(); let hashed_key = key.hash(); + self.write_raw(&hashed_key, new_value, storage_meter, take) + } + + /// Update a storage entry into a contract's kv storage. + /// Function used in benchmarks, which can simulate prefix collision in keys. + #[cfg(feature = "runtime-benchmarks")] + pub fn bench_write_raw( + &self, + key: &[u8], + new_value: Option>, + take: bool, + ) -> Result { + self.write_raw(key, new_value, None, take) + } + + fn write_raw( + &self, + key: &[u8], + new_value: Option>, + storage_meter: Option<&mut meter::NestedMeter>, + take: bool, + ) -> Result { + let child_trie_info = &self.child_trie_info(); let (old_len, old_value) = if take { - let val = child::get_raw(child_trie_info, &hashed_key); + let val = child::get_raw(child_trie_info, key); (val.as_ref().map(|v| v.len() as u32), val) } else { - (child::len(child_trie_info, &hashed_key), None) + (child::len(child_trie_info, key), None) }; if let Some(storage_meter) = storage_meter { @@ -196,8 +218,8 @@ impl ContractInfo { } match &new_value { - Some(new_value) => child::put_raw(child_trie_info, &hashed_key, new_value), - None => child::kill(child_trie_info, &hashed_key), + Some(new_value) => child::put_raw(child_trie_info, key, new_value), + None => child::kill(child_trie_info, key), } Ok(match (old_len, old_value) { diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index 982d28540ec1..fee127f65852 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -255,31 +255,34 @@ pub enum RuntimeCosts { UnlockDelegateDependency, } -// For the function that modifies the storage, the benchmarks are done with one item in the -// transient_storage (BTreeMap). To consider the worst-case scenario, the weight of the overhead of -// writing to a full BTreeMap should be included. On top of that, the rollback weight is added, -// which is the worst scenario. -macro_rules! cost_write { - // cost_write!(name, a, b, c) -> T::WeightInfo::name(a, b, c).saturating_add(T::WeightInfo::rollback_transient_storage()) - // .saturating_add(T::WeightInfo::set_transient_storage_full().saturating_sub(T::WeightInfo::set_transient_storage_empty()) - ($name:ident $(, $arg:expr )*) => { - (T::WeightInfo::$name($( $arg ),*).saturating_add(T::WeightInfo::rollback_transient_storage()).saturating_add(cost_write!(@cost_storage))) - }; +/// For functions that modify storage, benchmarks are performed with one item in the +/// storage. To account for the worst-case scenario, the weight of the overhead of +/// writing to or reading from full storage is included. For transient storage writes, +/// the rollback weight is added to reflect the worst-case scenario for this operation. +macro_rules! cost_storage { + (write_transient, $name:ident $(, $arg:expr )*) => { + T::WeightInfo::$name($( $arg ),*) + .saturating_add(T::WeightInfo::rollback_transient_storage()) + .saturating_add(T::WeightInfo::set_transient_storage_full() + .saturating_sub(T::WeightInfo::set_transient_storage_empty())) + }; - (@cost_storage) => { - T::WeightInfo::set_transient_storage_full().saturating_sub(T::WeightInfo::set_transient_storage_empty()) + (read_transient, $name:ident $(, $arg:expr )*) => { + T::WeightInfo::$name($( $arg ),*) + .saturating_add(T::WeightInfo::get_transient_storage_full() + .saturating_sub(T::WeightInfo::get_transient_storage_empty())) }; -} -macro_rules! cost_read { - // cost_read!(name, a, b, c) -> T::WeightInfo::name(a, b, c).saturating_add(T::WeightInfo::get_transient_storage_full() - // .saturating_sub(T::WeightInfo::get_transient_storage_empty()) - ($name:ident $(, $arg:expr )*) => { - (T::WeightInfo::$name($( $arg ),*).saturating_add(cost_read!(@cost_storage))) - }; + (write, $name:ident $(, $arg:expr )*) => { + T::WeightInfo::$name($( $arg ),*) + .saturating_add(T::WeightInfo::set_storage_full() + .saturating_sub(T::WeightInfo::set_storage_empty())) + }; - (@cost_storage) => { - T::WeightInfo::get_transient_storage_full().saturating_sub(T::WeightInfo::get_transient_storage_empty()) + (read, $name:ident $(, $arg:expr )*) => { + T::WeightInfo::$name($( $arg ),*) + .saturating_add(T::WeightInfo::get_storage_full() + .saturating_sub(T::WeightInfo::get_storage_empty())) }; } @@ -329,17 +332,21 @@ impl Token for RuntimeCosts { DepositEvent { num_topic, len } => T::WeightInfo::seal_deposit_event(num_topic, len), DebugMessage(len) => T::WeightInfo::seal_debug_message(len), SetStorage { new_bytes, old_bytes } => - T::WeightInfo::seal_set_storage(new_bytes, old_bytes), - ClearStorage(len) => T::WeightInfo::seal_clear_storage(len), - ContainsStorage(len) => T::WeightInfo::seal_contains_storage(len), - GetStorage(len) => T::WeightInfo::seal_get_storage(len), - TakeStorage(len) => T::WeightInfo::seal_take_storage(len), + cost_storage!(write, seal_set_storage, new_bytes, old_bytes), + ClearStorage(len) => cost_storage!(write, seal_clear_storage, len), + ContainsStorage(len) => cost_storage!(read, seal_contains_storage, len), + GetStorage(len) => cost_storage!(read, seal_get_storage, len), + TakeStorage(len) => cost_storage!(write, seal_take_storage, len), SetTransientStorage { new_bytes, old_bytes } => - cost_write!(seal_set_transient_storage, new_bytes, old_bytes), - ClearTransientStorage(len) => cost_write!(seal_clear_transient_storage, len), - ContainsTransientStorage(len) => cost_read!(seal_contains_transient_storage, len), - GetTransientStorage(len) => cost_read!(seal_get_transient_storage, len), - TakeTransientStorage(len) => cost_write!(seal_take_transient_storage, len), + cost_storage!(write_transient, seal_set_transient_storage, new_bytes, old_bytes), + ClearTransientStorage(len) => + cost_storage!(write_transient, seal_clear_transient_storage, len), + ContainsTransientStorage(len) => + cost_storage!(read_transient, seal_contains_transient_storage, len), + GetTransientStorage(len) => + cost_storage!(read_transient, seal_get_transient_storage, len), + TakeTransientStorage(len) => + cost_storage!(write_transient, seal_take_transient_storage, len), Transfer => T::WeightInfo::seal_transfer(), CallBase => T::WeightInfo::seal_call(0, 0), DelegateCallBase => T::WeightInfo::seal_delegate_call(), diff --git a/substrate/frame/contracts/src/weights.rs b/substrate/frame/contracts/src/weights.rs index dc10a8aee773..25b36fc404fe 100644 --- a/substrate/frame/contracts/src/weights.rs +++ b/substrate/frame/contracts/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for `pallet_contracts` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-07-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `runner-yaoqqom-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` @@ -93,6 +93,10 @@ pub trait WeightInfo { fn seal_random() -> Weight; fn seal_deposit_event(t: u32, n: u32, ) -> Weight; fn seal_debug_message(i: u32, ) -> Weight; + fn get_storage_empty() -> Weight; + fn get_storage_full() -> Weight; + fn set_storage_empty() -> Weight; + fn set_storage_full() -> Weight; fn seal_set_storage(n: u32, o: u32, ) -> Weight; fn seal_clear_storage(n: u32, ) -> Weight; fn seal_get_storage(n: u32, ) -> Weight; @@ -137,8 +141,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1627` - // Minimum execution time: 1_921_000 picoseconds. - Weight::from_parts(2_003_000, 1627) + // Minimum execution time: 1_915_000 picoseconds. + Weight::from_parts(1_986_000, 1627) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -148,10 +152,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `452 + k * (69 ±0)` // Estimated: `442 + k * (70 ±0)` - // Minimum execution time: 11_364_000 picoseconds. - Weight::from_parts(11_463_000, 442) - // Standard Error: 2_141 - .saturating_add(Weight::from_parts(1_149_944, 0).saturating_mul(k.into())) + // Minimum execution time: 11_103_000 picoseconds. + Weight::from_parts(11_326_000, 442) + // Standard Error: 2_291 + .saturating_add(Weight::from_parts(1_196_329, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -165,10 +169,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `211 + c * (1 ±0)` // Estimated: `6149 + c * (1 ±0)` - // Minimum execution time: 7_565_000 picoseconds. - Weight::from_parts(5_041_009, 6149) + // Minimum execution time: 7_783_000 picoseconds. + Weight::from_parts(4_462_075, 6149) // Standard Error: 5 - .saturating_add(Weight::from_parts(1_640, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(1_634, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -181,8 +185,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `510` // Estimated: `6450` - // Minimum execution time: 15_894_000 picoseconds. - Weight::from_parts(16_618_000, 6450) + // Minimum execution time: 15_971_000 picoseconds. + Weight::from_parts(16_730_000, 6450) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -195,10 +199,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `171 + k * (1 ±0)` // Estimated: `3635 + k * (1 ±0)` - // Minimum execution time: 3_077_000 picoseconds. - Weight::from_parts(3_144_000, 3635) - // Standard Error: 650 - .saturating_add(Weight::from_parts(1_095_835, 0).saturating_mul(k.into())) + // Minimum execution time: 3_149_000 picoseconds. + Weight::from_parts(3_264_000, 3635) + // Standard Error: 559 + .saturating_add(Weight::from_parts(1_111_209, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -217,10 +221,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `325 + c * (1 ±0)` // Estimated: `6263 + c * (1 ±0)` - // Minimum execution time: 14_960_000 picoseconds. - Weight::from_parts(15_778_951, 6263) - // Standard Error: 1 - .saturating_add(Weight::from_parts(443, 0).saturating_mul(c.into())) + // Minimum execution time: 15_072_000 picoseconds. + Weight::from_parts(15_721_891, 6263) + // Standard Error: 2 + .saturating_add(Weight::from_parts(428, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -231,8 +235,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `440` // Estimated: `6380` - // Minimum execution time: 11_849_000 picoseconds. - Weight::from_parts(12_273_000, 6380) + // Minimum execution time: 12_047_000 picoseconds. + Weight::from_parts(12_500_000, 6380) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -246,8 +250,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `352` // Estimated: `6292` - // Minimum execution time: 47_862_000 picoseconds. - Weight::from_parts(48_879_000, 6292) + // Minimum execution time: 47_488_000 picoseconds. + Weight::from_parts(48_482_000, 6292) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -259,8 +263,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `594` // Estimated: `6534` - // Minimum execution time: 50_754_000 picoseconds. - Weight::from_parts(52_720_000, 6534) + // Minimum execution time: 52_801_000 picoseconds. + Weight::from_parts(54_230_000, 6534) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -270,8 +274,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `409` // Estimated: `6349` - // Minimum execution time: 11_459_000 picoseconds. - Weight::from_parts(11_921_000, 6349) + // Minimum execution time: 11_618_000 picoseconds. + Weight::from_parts(12_068_000, 6349) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -281,8 +285,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1627` - // Minimum execution time: 2_135_000 picoseconds. - Weight::from_parts(2_247_000, 1627) + // Minimum execution time: 2_131_000 picoseconds. + Weight::from_parts(2_255_000, 1627) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -294,8 +298,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `166` // Estimated: `3631` - // Minimum execution time: 10_645_000 picoseconds. - Weight::from_parts(11_107_000, 3631) + // Minimum execution time: 10_773_000 picoseconds. + Weight::from_parts(11_118_000, 3631) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -305,8 +309,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 4_353_000 picoseconds. - Weight::from_parts(4_628_000, 3607) + // Minimum execution time: 4_371_000 picoseconds. + Weight::from_parts(4_624_000, 3607) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) @@ -317,8 +321,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `167` // Estimated: `3632` - // Minimum execution time: 5_432_000 picoseconds. - Weight::from_parts(5_624_000, 3632) + // Minimum execution time: 5_612_000 picoseconds. + Weight::from_parts(5_838_000, 3632) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) @@ -329,8 +333,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 5_371_000 picoseconds. - Weight::from_parts(5_794_000, 3607) + // Minimum execution time: 5_487_000 picoseconds. + Weight::from_parts(5_693_000, 3607) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -351,10 +355,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `800 + c * (1 ±0)` // Estimated: `4266 + c * (1 ±0)` - // Minimum execution time: 247_157_000 picoseconds. - Weight::from_parts(269_252_698, 4266) + // Minimum execution time: 247_545_000 picoseconds. + Weight::from_parts(268_016_699, 4266) // Standard Error: 4 - .saturating_add(Weight::from_parts(729, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(700, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -382,14 +386,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `323` // Estimated: `6262` - // Minimum execution time: 4_575_784_000 picoseconds. - Weight::from_parts(207_379_459, 6262) - // Standard Error: 124 - .saturating_add(Weight::from_parts(52_392, 0).saturating_mul(c.into())) - // Standard Error: 15 - .saturating_add(Weight::from_parts(2_257, 0).saturating_mul(i.into())) - // Standard Error: 15 - .saturating_add(Weight::from_parts(2_263, 0).saturating_mul(s.into())) + // Minimum execution time: 4_396_772_000 picoseconds. + Weight::from_parts(235_107_907, 6262) + // Standard Error: 185 + .saturating_add(Weight::from_parts(53_843, 0).saturating_mul(c.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(2_143, 0).saturating_mul(i.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(2_210, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -415,12 +419,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `560` // Estimated: `4017` - // Minimum execution time: 2_306_770_000 picoseconds. - Weight::from_parts(2_462_908_000, 4017) - // Standard Error: 33 - .saturating_add(Weight::from_parts(898, 0).saturating_mul(i.into())) - // Standard Error: 33 - .saturating_add(Weight::from_parts(859, 0).saturating_mul(s.into())) + // Minimum execution time: 2_240_868_000 picoseconds. + Weight::from_parts(2_273_668_000, 4017) + // Standard Error: 32 + .saturating_add(Weight::from_parts(934, 0).saturating_mul(i.into())) + // Standard Error: 32 + .saturating_add(Weight::from_parts(920, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -440,8 +444,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `826` // Estimated: `4291` - // Minimum execution time: 165_499_000 picoseconds. - Weight::from_parts(169_903_000, 4291) + // Minimum execution time: 165_067_000 picoseconds. + Weight::from_parts(168_582_000, 4291) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -458,10 +462,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 227_590_000 picoseconds. - Weight::from_parts(260_045_588, 3607) - // Standard Error: 52 - .saturating_add(Weight::from_parts(51_305, 0).saturating_mul(c.into())) + // Minimum execution time: 229_454_000 picoseconds. + Weight::from_parts(251_495_551, 3607) + // Standard Error: 71 + .saturating_add(Weight::from_parts(51_428, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -478,10 +482,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 239_634_000 picoseconds. - Weight::from_parts(262_040_831, 3607) - // Standard Error: 103 - .saturating_add(Weight::from_parts(51_590, 0).saturating_mul(c.into())) + // Minimum execution time: 240_390_000 picoseconds. + Weight::from_parts(273_854_266, 3607) + // Standard Error: 243 + .saturating_add(Weight::from_parts(51_836, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -497,8 +501,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `315` // Estimated: `3780` - // Minimum execution time: 39_152_000 picoseconds. - Weight::from_parts(39_970_000, 3780) + // Minimum execution time: 39_374_000 picoseconds. + Weight::from_parts(40_247_000, 3780) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -512,8 +516,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `552` // Estimated: `6492` - // Minimum execution time: 25_143_000 picoseconds. - Weight::from_parts(26_103_000, 6492) + // Minimum execution time: 24_473_000 picoseconds. + Weight::from_parts(25_890_000, 6492) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -522,17 +526,17 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_406_000 picoseconds. - Weight::from_parts(9_056_753, 0) + // Minimum execution time: 8_528_000 picoseconds. + Weight::from_parts(9_301_010, 0) // Standard Error: 98 - .saturating_add(Weight::from_parts(53_110, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(53_173, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 659_000 picoseconds. - Weight::from_parts(705_000, 0) + // Minimum execution time: 643_000 picoseconds. + Weight::from_parts(678_000, 0) } /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) @@ -540,8 +544,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `354` // Estimated: `3819` - // Minimum execution time: 6_165_000 picoseconds. - Weight::from_parts(6_340_000, 3819) + // Minimum execution time: 6_107_000 picoseconds. + Weight::from_parts(6_235_000, 3819) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) @@ -550,79 +554,79 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `447` // Estimated: `3912` - // Minimum execution time: 7_398_000 picoseconds. - Weight::from_parts(7_661_000, 3912) + // Minimum execution time: 7_316_000 picoseconds. + Weight::from_parts(7_653_000, 3912) .saturating_add(T::DbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 723_000 picoseconds. - Weight::from_parts(793_000, 0) + // Minimum execution time: 721_000 picoseconds. + Weight::from_parts(764_000, 0) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 398_000 picoseconds. - Weight::from_parts(428_000, 0) + // Minimum execution time: 369_000 picoseconds. + Weight::from_parts(417_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 329_000 picoseconds. - Weight::from_parts(364_000, 0) + // Minimum execution time: 318_000 picoseconds. + Weight::from_parts(349_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 592_000 picoseconds. - Weight::from_parts(624_000, 0) + // Minimum execution time: 590_000 picoseconds. + Weight::from_parts(628_000, 0) } fn seal_gas_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 665_000 picoseconds. - Weight::from_parts(714_000, 0) + // Minimum execution time: 660_000 picoseconds. + Weight::from_parts(730_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: // Measured: `140` // Estimated: `0` - // Minimum execution time: 4_486_000 picoseconds. - Weight::from_parts(4_668_000, 0) + // Minimum execution time: 4_361_000 picoseconds. + Weight::from_parts(4_577_000, 0) } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 548_000 picoseconds. - Weight::from_parts(590_000, 0) + // Minimum execution time: 560_000 picoseconds. + Weight::from_parts(603_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 536_000 picoseconds. - Weight::from_parts(578_000, 0) + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(610_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 552_000 picoseconds. - Weight::from_parts(599_000, 0) + // Minimum execution time: 557_000 picoseconds. + Weight::from_parts(583_000, 0) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 556_000 picoseconds. - Weight::from_parts(600_000, 0) + // Minimum execution time: 550_000 picoseconds. + Weight::from_parts(602_000, 0) } /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) @@ -630,8 +634,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `67` // Estimated: `1552` - // Minimum execution time: 4_084_000 picoseconds. - Weight::from_parts(4_321_000, 1552) + // Minimum execution time: 4_065_000 picoseconds. + Weight::from_parts(4_291_000, 1552) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// The range of component `n` is `[0, 1048572]`. @@ -639,20 +643,20 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 468_000 picoseconds. - Weight::from_parts(492_000, 0) + // Minimum execution time: 487_000 picoseconds. + Weight::from_parts(517_000, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(310, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(301, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 1048572]`. fn seal_return(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 377_000 picoseconds. - Weight::from_parts(396_000, 0) - // Standard Error: 9 - .saturating_add(Weight::from_parts(431, 0).saturating_mul(n.into())) + // Minimum execution time: 318_000 picoseconds. + Weight::from_parts(372_000, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(411, 0).saturating_mul(n.into())) } /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:1) /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) @@ -665,10 +669,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `319 + n * (78 ±0)` // Estimated: `3784 + n * (2553 ±0)` - // Minimum execution time: 13_028_000 picoseconds. - Weight::from_parts(15_330_917, 3784) - // Standard Error: 8_260 - .saturating_add(Weight::from_parts(3_594_893, 0).saturating_mul(n.into())) + // Minimum execution time: 13_251_000 picoseconds. + Weight::from_parts(15_257_892, 3784) + // Standard Error: 7_089 + .saturating_add(Weight::from_parts(3_443_907, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) @@ -681,8 +685,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `1561` - // Minimum execution time: 3_367_000 picoseconds. - Weight::from_parts(3_555_000, 1561) + // Minimum execution time: 3_434_000 picoseconds. + Weight::from_parts(3_605_000, 1561) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `System::EventTopics` (r:4 w:4) @@ -693,12 +697,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `990 + t * (2475 ±0)` - // Minimum execution time: 3_779_000 picoseconds. - Weight::from_parts(4_003_836, 990) - // Standard Error: 5_409 - .saturating_add(Weight::from_parts(2_082_176, 0).saturating_mul(t.into())) + // Minimum execution time: 3_668_000 picoseconds. + Weight::from_parts(3_999_591, 990) + // Standard Error: 5_767 + .saturating_add(Weight::from_parts(2_011_090, 0).saturating_mul(t.into())) // Standard Error: 1 - .saturating_add(Weight::from_parts(14, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(12, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) .saturating_add(Weight::from_parts(0, 2475).saturating_mul(t.into())) @@ -708,10 +712,52 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 409_000 picoseconds. - Weight::from_parts(447_000, 0) + // Minimum execution time: 443_000 picoseconds. + Weight::from_parts(472_000, 0) // Standard Error: 10 - .saturating_add(Weight::from_parts(1_219, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_207, 0).saturating_mul(i.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn get_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `16618` + // Estimated: `16618` + // Minimum execution time: 13_752_000 picoseconds. + Weight::from_parts(14_356_000, 16618) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn get_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `26628` + // Estimated: `26628` + // Minimum execution time: 43_444_000 picoseconds. + Weight::from_parts(45_087_000, 26628) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `16618` + // Estimated: `16618` + // Minimum execution time: 15_616_000 picoseconds. + Weight::from_parts(16_010_000, 16618) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `26628` + // Estimated: `26628` + // Minimum execution time: 47_020_000 picoseconds. + Weight::from_parts(50_152_000, 26628) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -721,12 +767,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `250 + o * (1 ±0)` // Estimated: `249 + o * (1 ±0)` - // Minimum execution time: 9_176_000 picoseconds. - Weight::from_parts(9_121_191, 249) + // Minimum execution time: 8_824_000 picoseconds. + Weight::from_parts(8_915_233, 249) // Standard Error: 1 - .saturating_add(Weight::from_parts(292, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(255, 0).saturating_mul(n.into())) // Standard Error: 1 - .saturating_add(Weight::from_parts(31, 0).saturating_mul(o.into())) + .saturating_add(Weight::from_parts(39, 0).saturating_mul(o.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) @@ -738,10 +784,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `248 + n * (1 ±0)` - // Minimum execution time: 7_294_000 picoseconds. - Weight::from_parts(7_963_151, 248) + // Minimum execution time: 7_133_000 picoseconds. + Weight::from_parts(7_912_778, 248) // Standard Error: 1 - .saturating_add(Weight::from_parts(92, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(88, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -753,10 +799,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `248 + n * (1 ±0)` - // Minimum execution time: 6_978_000 picoseconds. - Weight::from_parts(7_741_355, 248) - // Standard Error: 1 - .saturating_add(Weight::from_parts(654, 0).saturating_mul(n.into())) + // Minimum execution time: 6_746_000 picoseconds. + Weight::from_parts(7_647_236, 248) + // Standard Error: 2 + .saturating_add(Weight::from_parts(603, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -767,10 +813,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `248 + n * (1 ±0)` - // Minimum execution time: 6_286_000 picoseconds. - Weight::from_parts(7_026_923, 248) + // Minimum execution time: 6_247_000 picoseconds. + Weight::from_parts(6_952_661, 248) // Standard Error: 1 - .saturating_add(Weight::from_parts(86, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(77, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -781,10 +827,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `248 + n * (1 ±0)` - // Minimum execution time: 7_597_000 picoseconds. - Weight::from_parts(8_706_785, 248) - // Standard Error: 1 - .saturating_add(Weight::from_parts(653, 0).saturating_mul(n.into())) + // Minimum execution time: 7_428_000 picoseconds. + Weight::from_parts(8_384_015, 248) + // Standard Error: 2 + .saturating_add(Weight::from_parts(625, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -793,36 +839,36 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_497_000 picoseconds. - Weight::from_parts(1_564_000, 0) + // Minimum execution time: 1_478_000 picoseconds. + Weight::from_parts(1_533_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_670_000 picoseconds. - Weight::from_parts(2_807_000, 0) + // Minimum execution time: 2_485_000 picoseconds. + Weight::from_parts(2_728_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_836_000 picoseconds. - Weight::from_parts(3_878_000, 0) + // Minimum execution time: 3_195_000 picoseconds. + Weight::from_parts(3_811_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_537_000 picoseconds. - Weight::from_parts(4_665_000, 0) + // Minimum execution time: 3_902_000 picoseconds. + Weight::from_parts(4_118_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_592_000 picoseconds. - Weight::from_parts(1_742_000, 0) + // Minimum execution time: 1_571_000 picoseconds. + Weight::from_parts(1_662_000, 0) } /// The range of component `n` is `[0, 16384]`. /// The range of component `o` is `[0, 16384]`. @@ -830,57 +876,57 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_101_000 picoseconds. - Weight::from_parts(2_481_218, 0) + // Minimum execution time: 5_250_000 picoseconds. + Weight::from_parts(2_465_568, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(242, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(201, 0).saturating_mul(n.into())) // Standard Error: 0 - .saturating_add(Weight::from_parts(300, 0).saturating_mul(o.into())) + .saturating_add(Weight::from_parts(223, 0).saturating_mul(o.into())) } /// The range of component `n` is `[0, 16384]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_059_000 picoseconds. - Weight::from_parts(2_426_609, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(307, 0).saturating_mul(n.into())) + // Minimum execution time: 2_012_000 picoseconds. + Weight::from_parts(2_288_004, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(239, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 16384]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_918_000 picoseconds. - Weight::from_parts(2_114_837, 0) + // Minimum execution time: 1_906_000 picoseconds. + Weight::from_parts(2_121_040, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(302, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(225, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 16384]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_759_000 picoseconds. - Weight::from_parts(1_959_995, 0) + // Minimum execution time: 1_736_000 picoseconds. + Weight::from_parts(1_954_728, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(147, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(111, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 16384]`. fn seal_take_transient_storage(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_759_000 picoseconds. - Weight::from_parts(9_952_099, 0) + // Minimum execution time: 7_872_000 picoseconds. + Weight::from_parts(8_125_644, 0) } fn seal_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `140` // Estimated: `0` - // Minimum execution time: 8_700_000 picoseconds. - Weight::from_parts(8_903_000, 0) + // Minimum execution time: 8_489_000 picoseconds. + Weight::from_parts(8_791_000, 0) } /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) @@ -896,10 +942,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `620 + t * (280 ±0)` // Estimated: `4085 + t * (2182 ±0)` - // Minimum execution time: 123_399_000 picoseconds. - Weight::from_parts(120_909_821, 4085) - // Standard Error: 166_830 - .saturating_add(Weight::from_parts(43_853_642, 0).saturating_mul(t.into())) + // Minimum execution time: 122_759_000 picoseconds. + Weight::from_parts(120_016_020, 4085) + // Standard Error: 173_118 + .saturating_add(Weight::from_parts(42_848_338, 0).saturating_mul(t.into())) // Standard Error: 0 .saturating_add(Weight::from_parts(6, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) @@ -916,8 +962,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `430` // Estimated: `3895` - // Minimum execution time: 112_350_000 picoseconds. - Weight::from_parts(116_003_000, 3895) + // Minimum execution time: 111_566_000 picoseconds. + Weight::from_parts(115_083_000, 3895) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) @@ -936,12 +982,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `676` // Estimated: `4132` - // Minimum execution time: 1_972_276_000 picoseconds. - Weight::from_parts(1_977_872_000, 4132) + // Minimum execution time: 1_871_402_000 picoseconds. + Weight::from_parts(1_890_038_000, 4132) // Standard Error: 24 - .saturating_add(Weight::from_parts(623, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(581, 0).saturating_mul(i.into())) // Standard Error: 24 - .saturating_add(Weight::from_parts(917, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(915, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -950,64 +996,64 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 899_000 picoseconds. - Weight::from_parts(10_963_972, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_355, 0).saturating_mul(n.into())) + // Minimum execution time: 966_000 picoseconds. + Weight::from_parts(9_599_151, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_336, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 1048576]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_396_000 picoseconds. - Weight::from_parts(9_404_986, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(3_627, 0).saturating_mul(n.into())) + // Minimum execution time: 1_416_000 picoseconds. + Weight::from_parts(10_964_255, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_593, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 834_000 picoseconds. - Weight::from_parts(9_749_716, 0) + // Minimum execution time: 821_000 picoseconds. + Weight::from_parts(6_579_283, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_500, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_466, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 756_000 picoseconds. - Weight::from_parts(8_995_036, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_495, 0).saturating_mul(n.into())) + // Minimum execution time: 773_000 picoseconds. + Weight::from_parts(10_990_209, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_457, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 125697]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 45_800_000 picoseconds. - Weight::from_parts(44_676_829, 0) - // Standard Error: 8 - .saturating_add(Weight::from_parts(5_315, 0).saturating_mul(n.into())) + // Minimum execution time: 43_195_000 picoseconds. + Weight::from_parts(41_864_855, 0) + // Standard Error: 9 + .saturating_add(Weight::from_parts(5_154, 0).saturating_mul(n.into())) } fn seal_ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 47_415_000 picoseconds. - Weight::from_parts(48_743_000, 0) + // Minimum execution time: 47_747_000 picoseconds. + Weight::from_parts(49_219_000, 0) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 13_437_000 picoseconds. - Weight::from_parts(13_588_000, 0) + // Minimum execution time: 12_854_000 picoseconds. + Weight::from_parts(12_962_000, 0) } /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) @@ -1017,8 +1063,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `430` // Estimated: `3895` - // Minimum execution time: 17_775_000 picoseconds. - Weight::from_parts(18_332_000, 3895) + // Minimum execution time: 17_868_000 picoseconds. + Weight::from_parts(18_486_000, 3895) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -1028,8 +1074,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `355` // Estimated: `3820` - // Minimum execution time: 8_326_000 picoseconds. - Weight::from_parts(8_656_000, 3820) + // Minimum execution time: 8_393_000 picoseconds. + Weight::from_parts(8_640_000, 3820) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -1039,8 +1085,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `355` // Estimated: `3558` - // Minimum execution time: 7_276_000 picoseconds. - Weight::from_parts(7_630_000, 3558) + // Minimum execution time: 7_489_000 picoseconds. + Weight::from_parts(7_815_000, 3558) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -1048,15 +1094,15 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 330_000 picoseconds. - Weight::from_parts(373_000, 0) + // Minimum execution time: 299_000 picoseconds. + Weight::from_parts(339_000, 0) } fn seal_account_reentrance_count() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 381_000 picoseconds. - Weight::from_parts(418_000, 0) + // Minimum execution time: 324_000 picoseconds. + Weight::from_parts(380_000, 0) } /// Storage: `Contracts::Nonce` (r:1 w:0) /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) @@ -1064,8 +1110,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `219` // Estimated: `1704` - // Minimum execution time: 2_711_000 picoseconds. - Weight::from_parts(2_941_000, 1704) + // Minimum execution time: 2_768_000 picoseconds. + Weight::from_parts(3_025_000, 1704) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// The range of component `r` is `[0, 5000]`. @@ -1073,10 +1119,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 720_000 picoseconds. - Weight::from_parts(389_111, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(7_278, 0).saturating_mul(r.into())) + // Minimum execution time: 766_000 picoseconds. + Weight::from_parts(722_169, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(7_191, 0).saturating_mul(r.into())) } } @@ -1088,8 +1134,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1627` - // Minimum execution time: 1_921_000 picoseconds. - Weight::from_parts(2_003_000, 1627) + // Minimum execution time: 1_915_000 picoseconds. + Weight::from_parts(1_986_000, 1627) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1099,10 +1145,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `452 + k * (69 ±0)` // Estimated: `442 + k * (70 ±0)` - // Minimum execution time: 11_364_000 picoseconds. - Weight::from_parts(11_463_000, 442) - // Standard Error: 2_141 - .saturating_add(Weight::from_parts(1_149_944, 0).saturating_mul(k.into())) + // Minimum execution time: 11_103_000 picoseconds. + Weight::from_parts(11_326_000, 442) + // Standard Error: 2_291 + .saturating_add(Weight::from_parts(1_196_329, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -1116,10 +1162,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `211 + c * (1 ±0)` // Estimated: `6149 + c * (1 ±0)` - // Minimum execution time: 7_565_000 picoseconds. - Weight::from_parts(5_041_009, 6149) + // Minimum execution time: 7_783_000 picoseconds. + Weight::from_parts(4_462_075, 6149) // Standard Error: 5 - .saturating_add(Weight::from_parts(1_640, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(1_634, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -1132,8 +1178,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `510` // Estimated: `6450` - // Minimum execution time: 15_894_000 picoseconds. - Weight::from_parts(16_618_000, 6450) + // Minimum execution time: 15_971_000 picoseconds. + Weight::from_parts(16_730_000, 6450) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1146,10 +1192,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `171 + k * (1 ±0)` // Estimated: `3635 + k * (1 ±0)` - // Minimum execution time: 3_077_000 picoseconds. - Weight::from_parts(3_144_000, 3635) - // Standard Error: 650 - .saturating_add(Weight::from_parts(1_095_835, 0).saturating_mul(k.into())) + // Minimum execution time: 3_149_000 picoseconds. + Weight::from_parts(3_264_000, 3635) + // Standard Error: 559 + .saturating_add(Weight::from_parts(1_111_209, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -1168,10 +1214,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `325 + c * (1 ±0)` // Estimated: `6263 + c * (1 ±0)` - // Minimum execution time: 14_960_000 picoseconds. - Weight::from_parts(15_778_951, 6263) - // Standard Error: 1 - .saturating_add(Weight::from_parts(443, 0).saturating_mul(c.into())) + // Minimum execution time: 15_072_000 picoseconds. + Weight::from_parts(15_721_891, 6263) + // Standard Error: 2 + .saturating_add(Weight::from_parts(428, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -1182,8 +1228,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `440` // Estimated: `6380` - // Minimum execution time: 11_849_000 picoseconds. - Weight::from_parts(12_273_000, 6380) + // Minimum execution time: 12_047_000 picoseconds. + Weight::from_parts(12_500_000, 6380) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1197,8 +1243,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `352` // Estimated: `6292` - // Minimum execution time: 47_862_000 picoseconds. - Weight::from_parts(48_879_000, 6292) + // Minimum execution time: 47_488_000 picoseconds. + Weight::from_parts(48_482_000, 6292) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1210,8 +1256,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `594` // Estimated: `6534` - // Minimum execution time: 50_754_000 picoseconds. - Weight::from_parts(52_720_000, 6534) + // Minimum execution time: 52_801_000 picoseconds. + Weight::from_parts(54_230_000, 6534) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1221,8 +1267,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `409` // Estimated: `6349` - // Minimum execution time: 11_459_000 picoseconds. - Weight::from_parts(11_921_000, 6349) + // Minimum execution time: 11_618_000 picoseconds. + Weight::from_parts(12_068_000, 6349) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1232,8 +1278,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1627` - // Minimum execution time: 2_135_000 picoseconds. - Weight::from_parts(2_247_000, 1627) + // Minimum execution time: 2_131_000 picoseconds. + Weight::from_parts(2_255_000, 1627) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1245,8 +1291,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `166` // Estimated: `3631` - // Minimum execution time: 10_645_000 picoseconds. - Weight::from_parts(11_107_000, 3631) + // Minimum execution time: 10_773_000 picoseconds. + Weight::from_parts(11_118_000, 3631) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1256,8 +1302,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 4_353_000 picoseconds. - Weight::from_parts(4_628_000, 3607) + // Minimum execution time: 4_371_000 picoseconds. + Weight::from_parts(4_624_000, 3607) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) @@ -1268,8 +1314,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `167` // Estimated: `3632` - // Minimum execution time: 5_432_000 picoseconds. - Weight::from_parts(5_624_000, 3632) + // Minimum execution time: 5_612_000 picoseconds. + Weight::from_parts(5_838_000, 3632) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) @@ -1280,8 +1326,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 5_371_000 picoseconds. - Weight::from_parts(5_794_000, 3607) + // Minimum execution time: 5_487_000 picoseconds. + Weight::from_parts(5_693_000, 3607) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1302,10 +1348,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `800 + c * (1 ±0)` // Estimated: `4266 + c * (1 ±0)` - // Minimum execution time: 247_157_000 picoseconds. - Weight::from_parts(269_252_698, 4266) + // Minimum execution time: 247_545_000 picoseconds. + Weight::from_parts(268_016_699, 4266) // Standard Error: 4 - .saturating_add(Weight::from_parts(729, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(700, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -1333,14 +1379,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `323` // Estimated: `6262` - // Minimum execution time: 4_575_784_000 picoseconds. - Weight::from_parts(207_379_459, 6262) - // Standard Error: 124 - .saturating_add(Weight::from_parts(52_392, 0).saturating_mul(c.into())) - // Standard Error: 15 - .saturating_add(Weight::from_parts(2_257, 0).saturating_mul(i.into())) - // Standard Error: 15 - .saturating_add(Weight::from_parts(2_263, 0).saturating_mul(s.into())) + // Minimum execution time: 4_396_772_000 picoseconds. + Weight::from_parts(235_107_907, 6262) + // Standard Error: 185 + .saturating_add(Weight::from_parts(53_843, 0).saturating_mul(c.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(2_143, 0).saturating_mul(i.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(2_210, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -1366,12 +1412,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `560` // Estimated: `4017` - // Minimum execution time: 2_306_770_000 picoseconds. - Weight::from_parts(2_462_908_000, 4017) - // Standard Error: 33 - .saturating_add(Weight::from_parts(898, 0).saturating_mul(i.into())) - // Standard Error: 33 - .saturating_add(Weight::from_parts(859, 0).saturating_mul(s.into())) + // Minimum execution time: 2_240_868_000 picoseconds. + Weight::from_parts(2_273_668_000, 4017) + // Standard Error: 32 + .saturating_add(Weight::from_parts(934, 0).saturating_mul(i.into())) + // Standard Error: 32 + .saturating_add(Weight::from_parts(920, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1391,8 +1437,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `826` // Estimated: `4291` - // Minimum execution time: 165_499_000 picoseconds. - Weight::from_parts(169_903_000, 4291) + // Minimum execution time: 165_067_000 picoseconds. + Weight::from_parts(168_582_000, 4291) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1409,10 +1455,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 227_590_000 picoseconds. - Weight::from_parts(260_045_588, 3607) - // Standard Error: 52 - .saturating_add(Weight::from_parts(51_305, 0).saturating_mul(c.into())) + // Minimum execution time: 229_454_000 picoseconds. + Weight::from_parts(251_495_551, 3607) + // Standard Error: 71 + .saturating_add(Weight::from_parts(51_428, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1429,10 +1475,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 239_634_000 picoseconds. - Weight::from_parts(262_040_831, 3607) - // Standard Error: 103 - .saturating_add(Weight::from_parts(51_590, 0).saturating_mul(c.into())) + // Minimum execution time: 240_390_000 picoseconds. + Weight::from_parts(273_854_266, 3607) + // Standard Error: 243 + .saturating_add(Weight::from_parts(51_836, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1448,8 +1494,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `315` // Estimated: `3780` - // Minimum execution time: 39_152_000 picoseconds. - Weight::from_parts(39_970_000, 3780) + // Minimum execution time: 39_374_000 picoseconds. + Weight::from_parts(40_247_000, 3780) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1463,8 +1509,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `552` // Estimated: `6492` - // Minimum execution time: 25_143_000 picoseconds. - Weight::from_parts(26_103_000, 6492) + // Minimum execution time: 24_473_000 picoseconds. + Weight::from_parts(25_890_000, 6492) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1473,17 +1519,17 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_406_000 picoseconds. - Weight::from_parts(9_056_753, 0) + // Minimum execution time: 8_528_000 picoseconds. + Weight::from_parts(9_301_010, 0) // Standard Error: 98 - .saturating_add(Weight::from_parts(53_110, 0).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(53_173, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 659_000 picoseconds. - Weight::from_parts(705_000, 0) + // Minimum execution time: 643_000 picoseconds. + Weight::from_parts(678_000, 0) } /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) @@ -1491,8 +1537,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `354` // Estimated: `3819` - // Minimum execution time: 6_165_000 picoseconds. - Weight::from_parts(6_340_000, 3819) + // Minimum execution time: 6_107_000 picoseconds. + Weight::from_parts(6_235_000, 3819) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) @@ -1501,79 +1547,79 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `447` // Estimated: `3912` - // Minimum execution time: 7_398_000 picoseconds. - Weight::from_parts(7_661_000, 3912) + // Minimum execution time: 7_316_000 picoseconds. + Weight::from_parts(7_653_000, 3912) .saturating_add(RocksDbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 723_000 picoseconds. - Weight::from_parts(793_000, 0) + // Minimum execution time: 721_000 picoseconds. + Weight::from_parts(764_000, 0) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 398_000 picoseconds. - Weight::from_parts(428_000, 0) + // Minimum execution time: 369_000 picoseconds. + Weight::from_parts(417_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 329_000 picoseconds. - Weight::from_parts(364_000, 0) + // Minimum execution time: 318_000 picoseconds. + Weight::from_parts(349_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 592_000 picoseconds. - Weight::from_parts(624_000, 0) + // Minimum execution time: 590_000 picoseconds. + Weight::from_parts(628_000, 0) } fn seal_gas_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 665_000 picoseconds. - Weight::from_parts(714_000, 0) + // Minimum execution time: 660_000 picoseconds. + Weight::from_parts(730_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: // Measured: `140` // Estimated: `0` - // Minimum execution time: 4_486_000 picoseconds. - Weight::from_parts(4_668_000, 0) + // Minimum execution time: 4_361_000 picoseconds. + Weight::from_parts(4_577_000, 0) } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 548_000 picoseconds. - Weight::from_parts(590_000, 0) + // Minimum execution time: 560_000 picoseconds. + Weight::from_parts(603_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 536_000 picoseconds. - Weight::from_parts(578_000, 0) + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(610_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 552_000 picoseconds. - Weight::from_parts(599_000, 0) + // Minimum execution time: 557_000 picoseconds. + Weight::from_parts(583_000, 0) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 556_000 picoseconds. - Weight::from_parts(600_000, 0) + // Minimum execution time: 550_000 picoseconds. + Weight::from_parts(602_000, 0) } /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) @@ -1581,8 +1627,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `67` // Estimated: `1552` - // Minimum execution time: 4_084_000 picoseconds. - Weight::from_parts(4_321_000, 1552) + // Minimum execution time: 4_065_000 picoseconds. + Weight::from_parts(4_291_000, 1552) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// The range of component `n` is `[0, 1048572]`. @@ -1590,20 +1636,20 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 468_000 picoseconds. - Weight::from_parts(492_000, 0) + // Minimum execution time: 487_000 picoseconds. + Weight::from_parts(517_000, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(310, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(301, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 1048572]`. fn seal_return(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 377_000 picoseconds. - Weight::from_parts(396_000, 0) - // Standard Error: 9 - .saturating_add(Weight::from_parts(431, 0).saturating_mul(n.into())) + // Minimum execution time: 318_000 picoseconds. + Weight::from_parts(372_000, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(411, 0).saturating_mul(n.into())) } /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:1) /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) @@ -1616,10 +1662,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `319 + n * (78 ±0)` // Estimated: `3784 + n * (2553 ±0)` - // Minimum execution time: 13_028_000 picoseconds. - Weight::from_parts(15_330_917, 3784) - // Standard Error: 8_260 - .saturating_add(Weight::from_parts(3_594_893, 0).saturating_mul(n.into())) + // Minimum execution time: 13_251_000 picoseconds. + Weight::from_parts(15_257_892, 3784) + // Standard Error: 7_089 + .saturating_add(Weight::from_parts(3_443_907, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) @@ -1632,8 +1678,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `76` // Estimated: `1561` - // Minimum execution time: 3_367_000 picoseconds. - Weight::from_parts(3_555_000, 1561) + // Minimum execution time: 3_434_000 picoseconds. + Weight::from_parts(3_605_000, 1561) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `System::EventTopics` (r:4 w:4) @@ -1644,12 +1690,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `990 + t * (2475 ±0)` - // Minimum execution time: 3_779_000 picoseconds. - Weight::from_parts(4_003_836, 990) - // Standard Error: 5_409 - .saturating_add(Weight::from_parts(2_082_176, 0).saturating_mul(t.into())) + // Minimum execution time: 3_668_000 picoseconds. + Weight::from_parts(3_999_591, 990) + // Standard Error: 5_767 + .saturating_add(Weight::from_parts(2_011_090, 0).saturating_mul(t.into())) // Standard Error: 1 - .saturating_add(Weight::from_parts(14, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(12, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) .saturating_add(Weight::from_parts(0, 2475).saturating_mul(t.into())) @@ -1659,10 +1705,52 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 409_000 picoseconds. - Weight::from_parts(447_000, 0) + // Minimum execution time: 443_000 picoseconds. + Weight::from_parts(472_000, 0) // Standard Error: 10 - .saturating_add(Weight::from_parts(1_219, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(1_207, 0).saturating_mul(i.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn get_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `16618` + // Estimated: `16618` + // Minimum execution time: 13_752_000 picoseconds. + Weight::from_parts(14_356_000, 16618) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn get_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `26628` + // Estimated: `26628` + // Minimum execution time: 43_444_000 picoseconds. + Weight::from_parts(45_087_000, 26628) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `16618` + // Estimated: `16618` + // Minimum execution time: 15_616_000 picoseconds. + Weight::from_parts(16_010_000, 16618) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `26628` + // Estimated: `26628` + // Minimum execution time: 47_020_000 picoseconds. + Weight::from_parts(50_152_000, 26628) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1672,12 +1760,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `250 + o * (1 ±0)` // Estimated: `249 + o * (1 ±0)` - // Minimum execution time: 9_176_000 picoseconds. - Weight::from_parts(9_121_191, 249) + // Minimum execution time: 8_824_000 picoseconds. + Weight::from_parts(8_915_233, 249) // Standard Error: 1 - .saturating_add(Weight::from_parts(292, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(255, 0).saturating_mul(n.into())) // Standard Error: 1 - .saturating_add(Weight::from_parts(31, 0).saturating_mul(o.into())) + .saturating_add(Weight::from_parts(39, 0).saturating_mul(o.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) @@ -1689,10 +1777,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `248 + n * (1 ±0)` - // Minimum execution time: 7_294_000 picoseconds. - Weight::from_parts(7_963_151, 248) + // Minimum execution time: 7_133_000 picoseconds. + Weight::from_parts(7_912_778, 248) // Standard Error: 1 - .saturating_add(Weight::from_parts(92, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(88, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1704,10 +1792,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `248 + n * (1 ±0)` - // Minimum execution time: 6_978_000 picoseconds. - Weight::from_parts(7_741_355, 248) - // Standard Error: 1 - .saturating_add(Weight::from_parts(654, 0).saturating_mul(n.into())) + // Minimum execution time: 6_746_000 picoseconds. + Weight::from_parts(7_647_236, 248) + // Standard Error: 2 + .saturating_add(Weight::from_parts(603, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1718,10 +1806,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `248 + n * (1 ±0)` - // Minimum execution time: 6_286_000 picoseconds. - Weight::from_parts(7_026_923, 248) + // Minimum execution time: 6_247_000 picoseconds. + Weight::from_parts(6_952_661, 248) // Standard Error: 1 - .saturating_add(Weight::from_parts(86, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(77, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1732,10 +1820,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `248 + n * (1 ±0)` - // Minimum execution time: 7_597_000 picoseconds. - Weight::from_parts(8_706_785, 248) - // Standard Error: 1 - .saturating_add(Weight::from_parts(653, 0).saturating_mul(n.into())) + // Minimum execution time: 7_428_000 picoseconds. + Weight::from_parts(8_384_015, 248) + // Standard Error: 2 + .saturating_add(Weight::from_parts(625, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1744,36 +1832,36 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_497_000 picoseconds. - Weight::from_parts(1_564_000, 0) + // Minimum execution time: 1_478_000 picoseconds. + Weight::from_parts(1_533_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_670_000 picoseconds. - Weight::from_parts(2_807_000, 0) + // Minimum execution time: 2_485_000 picoseconds. + Weight::from_parts(2_728_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_836_000 picoseconds. - Weight::from_parts(3_878_000, 0) + // Minimum execution time: 3_195_000 picoseconds. + Weight::from_parts(3_811_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_537_000 picoseconds. - Weight::from_parts(4_665_000, 0) + // Minimum execution time: 3_902_000 picoseconds. + Weight::from_parts(4_118_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_592_000 picoseconds. - Weight::from_parts(1_742_000, 0) + // Minimum execution time: 1_571_000 picoseconds. + Weight::from_parts(1_662_000, 0) } /// The range of component `n` is `[0, 16384]`. /// The range of component `o` is `[0, 16384]`. @@ -1781,57 +1869,57 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_101_000 picoseconds. - Weight::from_parts(2_481_218, 0) + // Minimum execution time: 5_250_000 picoseconds. + Weight::from_parts(2_465_568, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(242, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(201, 0).saturating_mul(n.into())) // Standard Error: 0 - .saturating_add(Weight::from_parts(300, 0).saturating_mul(o.into())) + .saturating_add(Weight::from_parts(223, 0).saturating_mul(o.into())) } /// The range of component `n` is `[0, 16384]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_059_000 picoseconds. - Weight::from_parts(2_426_609, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(307, 0).saturating_mul(n.into())) + // Minimum execution time: 2_012_000 picoseconds. + Weight::from_parts(2_288_004, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(239, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 16384]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_918_000 picoseconds. - Weight::from_parts(2_114_837, 0) + // Minimum execution time: 1_906_000 picoseconds. + Weight::from_parts(2_121_040, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(302, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(225, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 16384]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_759_000 picoseconds. - Weight::from_parts(1_959_995, 0) + // Minimum execution time: 1_736_000 picoseconds. + Weight::from_parts(1_954_728, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(147, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(111, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 16384]`. fn seal_take_transient_storage(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_759_000 picoseconds. - Weight::from_parts(9_952_099, 0) + // Minimum execution time: 7_872_000 picoseconds. + Weight::from_parts(8_125_644, 0) } fn seal_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `140` // Estimated: `0` - // Minimum execution time: 8_700_000 picoseconds. - Weight::from_parts(8_903_000, 0) + // Minimum execution time: 8_489_000 picoseconds. + Weight::from_parts(8_791_000, 0) } /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) @@ -1847,10 +1935,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `620 + t * (280 ±0)` // Estimated: `4085 + t * (2182 ±0)` - // Minimum execution time: 123_399_000 picoseconds. - Weight::from_parts(120_909_821, 4085) - // Standard Error: 166_830 - .saturating_add(Weight::from_parts(43_853_642, 0).saturating_mul(t.into())) + // Minimum execution time: 122_759_000 picoseconds. + Weight::from_parts(120_016_020, 4085) + // Standard Error: 173_118 + .saturating_add(Weight::from_parts(42_848_338, 0).saturating_mul(t.into())) // Standard Error: 0 .saturating_add(Weight::from_parts(6, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) @@ -1867,8 +1955,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `430` // Estimated: `3895` - // Minimum execution time: 112_350_000 picoseconds. - Weight::from_parts(116_003_000, 3895) + // Minimum execution time: 111_566_000 picoseconds. + Weight::from_parts(115_083_000, 3895) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) @@ -1887,12 +1975,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `676` // Estimated: `4132` - // Minimum execution time: 1_972_276_000 picoseconds. - Weight::from_parts(1_977_872_000, 4132) + // Minimum execution time: 1_871_402_000 picoseconds. + Weight::from_parts(1_890_038_000, 4132) // Standard Error: 24 - .saturating_add(Weight::from_parts(623, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(581, 0).saturating_mul(i.into())) // Standard Error: 24 - .saturating_add(Weight::from_parts(917, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(915, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1901,64 +1989,64 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 899_000 picoseconds. - Weight::from_parts(10_963_972, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_355, 0).saturating_mul(n.into())) + // Minimum execution time: 966_000 picoseconds. + Weight::from_parts(9_599_151, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_336, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 1048576]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_396_000 picoseconds. - Weight::from_parts(9_404_986, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(3_627, 0).saturating_mul(n.into())) + // Minimum execution time: 1_416_000 picoseconds. + Weight::from_parts(10_964_255, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_593, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 834_000 picoseconds. - Weight::from_parts(9_749_716, 0) + // Minimum execution time: 821_000 picoseconds. + Weight::from_parts(6_579_283, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_500, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_466, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 756_000 picoseconds. - Weight::from_parts(8_995_036, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_495, 0).saturating_mul(n.into())) + // Minimum execution time: 773_000 picoseconds. + Weight::from_parts(10_990_209, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_457, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 125697]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 45_800_000 picoseconds. - Weight::from_parts(44_676_829, 0) - // Standard Error: 8 - .saturating_add(Weight::from_parts(5_315, 0).saturating_mul(n.into())) + // Minimum execution time: 43_195_000 picoseconds. + Weight::from_parts(41_864_855, 0) + // Standard Error: 9 + .saturating_add(Weight::from_parts(5_154, 0).saturating_mul(n.into())) } fn seal_ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 47_415_000 picoseconds. - Weight::from_parts(48_743_000, 0) + // Minimum execution time: 47_747_000 picoseconds. + Weight::from_parts(49_219_000, 0) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 13_437_000 picoseconds. - Weight::from_parts(13_588_000, 0) + // Minimum execution time: 12_854_000 picoseconds. + Weight::from_parts(12_962_000, 0) } /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) @@ -1968,8 +2056,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `430` // Estimated: `3895` - // Minimum execution time: 17_775_000 picoseconds. - Weight::from_parts(18_332_000, 3895) + // Minimum execution time: 17_868_000 picoseconds. + Weight::from_parts(18_486_000, 3895) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1979,8 +2067,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `355` // Estimated: `3820` - // Minimum execution time: 8_326_000 picoseconds. - Weight::from_parts(8_656_000, 3820) + // Minimum execution time: 8_393_000 picoseconds. + Weight::from_parts(8_640_000, 3820) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1990,8 +2078,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `355` // Estimated: `3558` - // Minimum execution time: 7_276_000 picoseconds. - Weight::from_parts(7_630_000, 3558) + // Minimum execution time: 7_489_000 picoseconds. + Weight::from_parts(7_815_000, 3558) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1999,15 +2087,15 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 330_000 picoseconds. - Weight::from_parts(373_000, 0) + // Minimum execution time: 299_000 picoseconds. + Weight::from_parts(339_000, 0) } fn seal_account_reentrance_count() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 381_000 picoseconds. - Weight::from_parts(418_000, 0) + // Minimum execution time: 324_000 picoseconds. + Weight::from_parts(380_000, 0) } /// Storage: `Contracts::Nonce` (r:1 w:0) /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) @@ -2015,8 +2103,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `219` // Estimated: `1704` - // Minimum execution time: 2_711_000 picoseconds. - Weight::from_parts(2_941_000, 1704) + // Minimum execution time: 2_768_000 picoseconds. + Weight::from_parts(3_025_000, 1704) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// The range of component `r` is `[0, 5000]`. @@ -2024,9 +2112,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 720_000 picoseconds. - Weight::from_parts(389_111, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(7_278, 0).saturating_mul(r.into())) + // Minimum execution time: 766_000 picoseconds. + Weight::from_parts(722_169, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(7_191, 0).saturating_mul(r.into())) } } From 9b4acf27b869d7cbb07b03f0857763b8c8cc7566 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:11:40 +0200 Subject: [PATCH 036/382] Bump bs58 from 0.5.0 to 0.5.1 (#5170) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [bs58](https://github.com/Nullus157/bs58-rs) from 0.5.0 to 0.5.1.
Changelog

Sourced from bs58's changelog.

0.5.1 - 2024-03-19

Commits
  • 7d3c928 Merge pull request #116 from joncinque/const
  • d3fb50e Merge pull request #117 from Nemo157/criterion-update
  • 9038a36 Update dependencies
  • 13af427 Update criterion to fix cargo-deny issues
  • b6ad26a Prepare to release 0.5.1
  • e18e057 Move const-compatible API onto decode::DecodeBuilder directly
  • e65bfa7 decode: Add const-compatible decoder
  • 2b0d73b Merge pull request #113 from Nemo157/cli-version-bump
  • be42edf Prepare for 0.1.2 cli release
  • 6bdc4b2 Merge pull request #112 from Nemo157/cli-dep-update
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=bs58&package-manager=cargo&previous-version=0.5.0&new-version=0.5.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6b8f0b74753e..c7712363e79d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2378,9 +2378,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bs58" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ "tinyvec", ] @@ -7890,7 +7890,7 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "999ec70441b2fb35355076726a6bc466c932e9bdc66f6a11c6c0aa17c7ab9be0" dependencies = [ - "bs58 0.5.0", + "bs58 0.5.1", "ed25519-dalek", "hkdf", "multihash 0.19.1", @@ -13641,7 +13641,7 @@ name = "polkadot-node-metrics" version = "7.0.0" dependencies = [ "assert_cmd", - "bs58 0.5.0", + "bs58 0.5.1", "futures", "futures-timer", "http-body-util", @@ -14085,7 +14085,7 @@ dependencies = [ name = "polkadot-runtime-metrics" version = "7.0.0" dependencies = [ - "bs58 0.5.0", + "bs58 0.5.1", "frame-benchmarking", "parity-scale-codec", "polkadot-primitives", @@ -17974,7 +17974,7 @@ dependencies = [ name = "sc-network-types" version = "0.10.0" dependencies = [ - "bs58 0.5.0", + "bs58 0.5.1", "ed25519-dalek", "libp2p-identity", "litep2p", @@ -19254,7 +19254,7 @@ dependencies = [ "base64 0.21.2", "bip39", "blake2-rfc", - "bs58 0.5.0", + "bs58 0.5.1", "chacha20", "crossbeam-queue", "derive_more", @@ -20099,7 +20099,7 @@ dependencies = [ "bitflags 1.3.2", "blake2 0.10.6", "bounded-collections", - "bs58 0.5.0", + "bs58 0.5.1", "criterion", "dyn-clonable", "ed25519-zebra", diff --git a/Cargo.toml b/Cargo.toml index d59c2b790b35..85f21be900bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -648,7 +648,7 @@ bridge-hub-test-utils = { path = "cumulus/parachains/runtimes/bridge-hubs/test-u bridge-hub-westend-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend" } bridge-hub-westend-runtime = { path = "cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend", default-features = false } bridge-runtime-common = { path = "bridges/bin/runtime-common", default-features = false } -bs58 = { version = "0.5.0", default-features = false } +bs58 = { version = "0.5.1", default-features = false } build-helper = { version = "0.1.1" } byte-slice-cast = { version = "1.2.1", default-features = false } byteorder = { version = "1.3.2", default-features = false } From 0636ffdc3dfea52e90102403527ff99d2f2d6e7c Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:09:21 +0300 Subject: [PATCH 037/382] [2 / 5] Make approval-distribution logic runnable on a separate thread (#4845) This is part of the work to further optimize the approval subsystems, if you want to understand the full context start with reading https://github.com/paritytech/polkadot-sdk/pull/4849#issue-2364261568, # Description This PR contain changes to make possible the run of multiple instances of approval-distribution, so that we can parallelise the work. This does not contain any functional changes it just decouples the subsystem from the subsystem Context and introduces more specific trait dependencies for each function instead of all of them requiring a context. It does not have any dependency of the follow PRs, so it can be merged independently of them. --------- Signed-off-by: Alexandru Gheorghe --- .../network/approval-distribution/src/lib.rs | 350 ++++++++++++------ prdoc/pr_4845.prdoc | 13 + 2 files changed, 241 insertions(+), 122 deletions(-) create mode 100644 prdoc/pr_4845.prdoc diff --git a/polkadot/node/network/approval-distribution/src/lib.rs b/polkadot/node/network/approval-distribution/src/lib.rs index d48fb08a311c..3462aaef1f69 100644 --- a/polkadot/node/network/approval-distribution/src/lib.rs +++ b/polkadot/node/network/approval-distribution/src/lib.rs @@ -320,7 +320,7 @@ enum Resend { /// It tracks metadata about our view of the unfinalized chain, /// which assignments and approvals we have seen, and our peers' views. #[derive(Default)] -struct State { +pub struct State { /// These two fields are used in conjunction to construct a view over the unfinalized chain. blocks_by_number: BTreeMap>, blocks: HashMap, @@ -662,9 +662,13 @@ enum PendingMessage { #[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] impl State { - async fn handle_network_msg( + async fn handle_network_msg< + N: overseer::SubsystemSender, + A: overseer::SubsystemSender, + >( &mut self, - ctx: &mut Context, + approval_voting_sender: &mut A, + network_sender: &mut N, metrics: &Metrics, event: NetworkBridgeEvent, rng: &mut (impl CryptoRng + Rng), @@ -689,7 +693,7 @@ impl State { }, NetworkBridgeEvent::NewGossipTopology(topology) => { self.handle_new_session_topology( - ctx, + network_sender, topology.session, topology.topology, topology.local_index, @@ -697,7 +701,7 @@ impl State { .await; }, NetworkBridgeEvent::PeerViewChange(peer_id, view) => { - self.handle_peer_view_change(ctx, metrics, peer_id, view, rng).await; + self.handle_peer_view_change(network_sender, metrics, peer_id, view, rng).await; }, NetworkBridgeEvent::OurViewChange(view) => { gum::trace!(target: LOG_TARGET, ?view, "Own view change"); @@ -720,7 +724,15 @@ impl State { }); }, NetworkBridgeEvent::PeerMessage(peer_id, message) => { - self.process_incoming_peer_message(ctx, metrics, peer_id, message, rng).await; + self.process_incoming_peer_message( + approval_voting_sender, + network_sender, + metrics, + peer_id, + message, + rng, + ) + .await; }, NetworkBridgeEvent::UpdatedAuthorityIds(peer_id, authority_ids) => { gum::debug!(target: LOG_TARGET, ?peer_id, ?authority_ids, "Update Authority Ids"); @@ -743,7 +755,7 @@ impl State { let view_intersection = View::new(intersection.cloned(), view.finalized_number); Self::unify_with_peer( - ctx.sender(), + network_sender, metrics, &mut self.blocks, &self.topologies, @@ -761,9 +773,13 @@ impl State { } } - async fn handle_new_blocks( + async fn handle_new_blocks< + N: overseer::SubsystemSender, + A: overseer::SubsystemSender, + >( &mut self, - ctx: &mut Context, + approval_voting_sender: &mut A, + network_sender: &mut N, metrics: &Metrics, metas: Vec, rng: &mut (impl CryptoRng + Rng), @@ -814,12 +830,11 @@ impl State { ); { - let sender = ctx.sender(); for (peer_id, PeerEntry { view, version }) in self.peer_views.iter() { let intersection = view.iter().filter(|h| new_hashes.contains(h)); let view_intersection = View::new(intersection.cloned(), view.finalized_number); Self::unify_with_peer( - sender, + network_sender, metrics, &mut self.blocks, &self.topologies, @@ -866,7 +881,8 @@ impl State { match message { PendingMessage::Assignment(assignment, claimed_indices) => { self.import_and_circulate_assignment( - ctx, + approval_voting_sender, + network_sender, metrics, MessageSource::Peer(peer_id), assignment, @@ -877,7 +893,8 @@ impl State { }, PendingMessage::Approval(approval_vote) => { self.import_and_circulate_approval( - ctx, + approval_voting_sender, + network_sender, metrics, MessageSource::Peer(peer_id), approval_vote, @@ -889,12 +906,12 @@ impl State { } } - self.enable_aggression(ctx, Resend::Yes, metrics).await; + self.enable_aggression(network_sender, Resend::Yes, metrics).await; } - async fn handle_new_session_topology( + async fn handle_new_session_topology>( &mut self, - ctx: &mut Context, + network_sender: &mut N, session: SessionIndex, topology: SessionGridTopology, local_index: Option, @@ -908,7 +925,7 @@ impl State { let topology = self.topologies.get_topology(session).expect("just inserted above; qed"); adjust_required_routing_and_propagate( - ctx, + network_sender, &mut self.blocks, &self.topologies, |block_entry| block_entry.session == session, @@ -926,14 +943,17 @@ impl State { .await; } - async fn process_incoming_assignments( + async fn process_incoming_assignments( &mut self, - ctx: &mut Context, + approval_voting_sender: &mut A, + network_sender: &mut N, metrics: &Metrics, peer_id: PeerId, assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)>, rng: &mut R, ) where + A: overseer::SubsystemSender, + N: overseer::SubsystemSender, R: CryptoRng + Rng, { for (assignment, claimed_indices) in assignments { @@ -956,7 +976,8 @@ impl State { } self.import_and_circulate_assignment( - ctx, + approval_voting_sender, + network_sender, metrics, MessageSource::Peer(peer_id), assignment, @@ -968,9 +989,13 @@ impl State { } // Entry point for processing an approval coming from a peer. - async fn process_incoming_approvals( + async fn process_incoming_approvals< + N: overseer::SubsystemSender, + A: overseer::SubsystemSender, + >( &mut self, - ctx: &mut Context, + approval_voting_sender: &mut A, + network_sender: &mut N, metrics: &Metrics, peer_id: PeerId, approvals: Vec, @@ -1001,7 +1026,8 @@ impl State { } self.import_and_circulate_approval( - ctx, + approval_voting_sender, + network_sender, metrics, MessageSource::Peer(peer_id), approval_vote, @@ -1010,9 +1036,10 @@ impl State { } } - async fn process_incoming_peer_message( + async fn process_incoming_peer_message( &mut self, - ctx: &mut Context, + approval_voting_sender: &mut A, + network_sender: &mut N, metrics: &Metrics, peer_id: PeerId, msg: Versioned< @@ -1022,6 +1049,8 @@ impl State { >, rng: &mut R, ) where + A: overseer::SubsystemSender, + N: overseer::SubsystemSender, R: CryptoRng + Rng, { match msg { @@ -1033,10 +1062,11 @@ impl State { "Processing assignments from a peer", ); let sanitized_assignments = - self.sanitize_v2_assignments(peer_id, ctx.sender(), assignments).await; + self.sanitize_v2_assignments(peer_id, network_sender, assignments).await; self.process_incoming_assignments( - ctx, + approval_voting_sender, + network_sender, metrics, peer_id, sanitized_assignments, @@ -1054,10 +1084,11 @@ impl State { ); let sanitized_assignments = - self.sanitize_v1_assignments(peer_id, ctx.sender(), assignments).await; + self.sanitize_v1_assignments(peer_id, network_sender, assignments).await; self.process_incoming_assignments( - ctx, + approval_voting_sender, + network_sender, metrics, peer_id, sanitized_assignments, @@ -1067,25 +1098,37 @@ impl State { }, Versioned::V3(protocol_v3::ApprovalDistributionMessage::Approvals(approvals)) => { let sanitized_approvals = - self.sanitize_v2_approvals(peer_id, ctx.sender(), approvals).await; - self.process_incoming_approvals(ctx, metrics, peer_id, sanitized_approvals) - .await; + self.sanitize_v2_approvals(peer_id, network_sender, approvals).await; + self.process_incoming_approvals( + approval_voting_sender, + network_sender, + metrics, + peer_id, + sanitized_approvals, + ) + .await; }, Versioned::V1(protocol_v1::ApprovalDistributionMessage::Approvals(approvals)) | Versioned::V2(protocol_v2::ApprovalDistributionMessage::Approvals(approvals)) => { let sanitized_approvals = - self.sanitize_v1_approvals(peer_id, ctx.sender(), approvals).await; - self.process_incoming_approvals(ctx, metrics, peer_id, sanitized_approvals) - .await; + self.sanitize_v1_approvals(peer_id, network_sender, approvals).await; + self.process_incoming_approvals( + approval_voting_sender, + network_sender, + metrics, + peer_id, + sanitized_approvals, + ) + .await; }, } } // handle a peer view change: requires that the peer is already connected // and has an entry in the `PeerData` struct. - async fn handle_peer_view_change( + async fn handle_peer_view_change, R>( &mut self, - ctx: &mut Context, + network_sender: &mut N, metrics: &Metrics, peer_id: PeerId, view: View, @@ -1132,7 +1175,7 @@ impl State { } Self::unify_with_peer( - ctx.sender(), + network_sender, metrics, &mut self.blocks, &self.topologies, @@ -1146,9 +1189,9 @@ impl State { .await; } - async fn handle_block_finalized( + async fn handle_block_finalized>( &mut self, - ctx: &mut Context, + network_sender: &mut N, metrics: &Metrics, finalized_number: BlockNumber, ) { @@ -1172,18 +1215,21 @@ impl State { // If a block was finalized, this means we may need to move our aggression // forward to the now oldest block(s). - self.enable_aggression(ctx, Resend::No, metrics).await; + self.enable_aggression(network_sender, Resend::No, metrics).await; } - async fn import_and_circulate_assignment( + async fn import_and_circulate_assignment( &mut self, - ctx: &mut Context, + approval_voting_sender: &mut A, + network_sender: &mut N, metrics: &Metrics, source: MessageSource, assignment: IndirectAssignmentCertV2, claimed_candidate_indices: CandidateBitfield, rng: &mut R, ) where + A: overseer::SubsystemSender, + N: overseer::SubsystemSender, R: CryptoRng + Rng, { let _span = self @@ -1218,7 +1264,7 @@ impl State { if !self.recent_outdated_blocks.is_recent_outdated(&block_hash) { modify_reputation( &mut self.reputation, - ctx.sender(), + network_sender, peer_id, COST_UNEXPECTED_MESSAGE, ) @@ -1255,7 +1301,7 @@ impl State { modify_reputation( &mut self.reputation, - ctx.sender(), + network_sender, peer_id, COST_DUPLICATE_MESSAGE, ) @@ -1283,7 +1329,7 @@ impl State { ); modify_reputation( &mut self.reputation, - ctx.sender(), + network_sender, peer_id, COST_UNEXPECTED_MESSAGE, ) @@ -1296,7 +1342,7 @@ impl State { if entry.knowledge.contains(&message_subject, message_kind) { modify_reputation( &mut self.reputation, - ctx.sender(), + network_sender, peer_id, BENEFIT_VALID_MESSAGE, ) @@ -1311,12 +1357,13 @@ impl State { let (tx, rx) = oneshot::channel(); - ctx.send_message(ApprovalVotingMessage::CheckAndImportAssignment( - assignment.clone(), - claimed_candidate_indices.clone(), - tx, - )) - .await; + approval_voting_sender + .send_message(ApprovalVotingMessage::CheckAndImportAssignment( + assignment.clone(), + claimed_candidate_indices.clone(), + tx, + )) + .await; let timer = metrics.time_awaiting_approval_voting(); let result = match rx.await { @@ -1339,7 +1386,7 @@ impl State { AssignmentCheckResult::Accepted => { modify_reputation( &mut self.reputation, - ctx.sender(), + network_sender, peer_id, BENEFIT_VALID_MESSAGE_FIRST, ) @@ -1375,7 +1422,7 @@ impl State { ); modify_reputation( &mut self.reputation, - ctx.sender(), + network_sender, peer_id, COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE, ) @@ -1394,7 +1441,7 @@ impl State { ); modify_reputation( &mut self.reputation, - ctx.sender(), + network_sender, peer_id, COST_INVALID_MESSAGE, ) @@ -1526,14 +1573,16 @@ impl State { }) .collect::>(); - send_assignments_batched(ctx.sender(), assignments, &peers).await; + send_assignments_batched(network_sender, assignments, &peers).await; } } // Checks if an approval can be processed. // Returns true if we can continue with processing the approval and false otherwise. - async fn check_approval_can_be_processed( - ctx: &mut Context, + async fn check_approval_can_be_processed< + N: overseer::SubsystemSender, + >( + network_sender: &mut N, assignments_knowledge_key: &Vec<(MessageSubject, MessageKind)>, approval_knowledge_key: &(MessageSubject, MessageKind), entry: &mut BlockEntry, @@ -1549,7 +1598,8 @@ impl State { ?message_subject, "Unknown approval assignment", ); - modify_reputation(reputation, ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE).await; + modify_reputation(reputation, network_sender, peer_id, COST_UNEXPECTED_MESSAGE) + .await; metrics.on_approval_unknown_assignment(); return false } @@ -1573,7 +1623,7 @@ impl State { modify_reputation( reputation, - ctx.sender(), + network_sender, peer_id, COST_DUPLICATE_MESSAGE, ) @@ -1590,7 +1640,8 @@ impl State { ?approval_knowledge_key, "Approval from a peer is out of view", ); - modify_reputation(reputation, ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE).await; + modify_reputation(reputation, network_sender, peer_id, COST_UNEXPECTED_MESSAGE) + .await; metrics.on_approval_out_of_view(); }, } @@ -1605,16 +1656,20 @@ impl State { // We already processed this approval no need to continue. gum::trace!(target: LOG_TARGET, ?peer_id, ?approval_knowledge_key, "Known approval"); metrics.on_approval_good_known(); - modify_reputation(reputation, ctx.sender(), peer_id, BENEFIT_VALID_MESSAGE).await; + modify_reputation(reputation, network_sender, peer_id, BENEFIT_VALID_MESSAGE).await; false } else { true } } - async fn import_and_circulate_approval( + async fn import_and_circulate_approval< + N: overseer::SubsystemSender, + A: overseer::SubsystemSender, + >( &mut self, - ctx: &mut Context, + approval_voting_sender: &mut A, + network_sender: &mut N, metrics: &Metrics, source: MessageSource, vote: IndirectSignedApprovalVoteV2, @@ -1652,7 +1707,7 @@ impl State { ); modify_reputation( &mut self.reputation, - ctx.sender(), + network_sender, peer_id, COST_UNEXPECTED_MESSAGE, ) @@ -1672,7 +1727,7 @@ impl State { if let Some(peer_id) = source.peer_id() { if !Self::check_approval_can_be_processed( - ctx, + network_sender, &assignments_knowledge_keys, &approval_knwowledge_key, entry, @@ -1687,7 +1742,8 @@ impl State { let (tx, rx) = oneshot::channel(); - ctx.send_message(ApprovalVotingMessage::CheckAndImportApproval(vote.clone(), tx)) + approval_voting_sender + .send_message(ApprovalVotingMessage::CheckAndImportApproval(vote.clone(), tx)) .await; let timer = metrics.time_awaiting_approval_voting(); @@ -1711,7 +1767,7 @@ impl State { ApprovalCheckResult::Accepted => { modify_reputation( &mut self.reputation, - ctx.sender(), + network_sender, peer_id, BENEFIT_VALID_MESSAGE_FIRST, ) @@ -1729,7 +1785,7 @@ impl State { ApprovalCheckResult::Bad(error) => { modify_reputation( &mut self.reputation, - ctx.sender(), + network_sender, peer_id, COST_INVALID_MESSAGE, ) @@ -1831,7 +1887,7 @@ impl State { num_peers = peers.len(), "Sending an approval to peers", ); - send_approvals_batched(ctx.sender(), approvals, &peers).await; + send_approvals_batched(network_sender, approvals, &peers).await; } } @@ -1882,7 +1938,7 @@ impl State { } async fn unify_with_peer( - sender: &mut impl overseer::ApprovalDistributionSenderTrait, + sender: &mut impl overseer::SubsystemSender, metrics: &Metrics, entries: &mut HashMap, topologies: &SessionGridTopologies, @@ -2027,9 +2083,9 @@ impl State { // // In order to switch to using approval lag as a trigger we need a request/response protocol // to fetch votes from validators rather than use gossip. - async fn enable_aggression( + async fn enable_aggression>( &mut self, - ctx: &mut Context, + network_sender: &mut N, resend: Resend, metrics: &Metrics, ) { @@ -2058,7 +2114,7 @@ impl State { gum::debug!(target: LOG_TARGET, min_age, max_age, "Aggression enabled",); adjust_required_routing_and_propagate( - ctx, + network_sender, &mut self.blocks, &self.topologies, |block_entry| { @@ -2086,7 +2142,7 @@ impl State { .await; adjust_required_routing_and_propagate( - ctx, + network_sender, &mut self.blocks, &self.topologies, |block_entry| { @@ -2137,7 +2193,7 @@ impl State { async fn sanitize_v1_assignments( &mut self, peer_id: PeerId, - sender: &mut impl overseer::ApprovalDistributionSenderTrait, + sender: &mut impl overseer::SubsystemSender, assignments: Vec<(IndirectAssignmentCert, CandidateIndex)>, ) -> Vec<(IndirectAssignmentCertV2, CandidateBitfield)> { let mut sanitized_assignments = Vec::new(); @@ -2172,7 +2228,7 @@ impl State { async fn sanitize_v2_assignments( &mut self, peer_id: PeerId, - sender: &mut impl overseer::ApprovalDistributionSenderTrait, + sender: &mut impl overseer::SubsystemSender, assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)>, ) -> Vec<(IndirectAssignmentCertV2, CandidateBitfield)> { let mut sanitized_assignments = Vec::new(); @@ -2216,7 +2272,7 @@ impl State { async fn sanitize_v1_approvals( &mut self, peer_id: PeerId, - sender: &mut impl overseer::ApprovalDistributionSenderTrait, + sender: &mut impl overseer::SubsystemSender, approval: Vec, ) -> Vec { let mut sanitized_approvals = Vec::new(); @@ -2243,7 +2299,7 @@ impl State { async fn sanitize_v2_approvals( &mut self, peer_id: PeerId, - sender: &mut impl overseer::ApprovalDistributionSenderTrait, + sender: &mut impl overseer::SubsystemSender, approval: Vec, ) -> Vec { let mut sanitized_approvals = Vec::new(); @@ -2280,8 +2336,12 @@ impl State { // Note that the required routing of a message can be modified even if the // topology is unknown yet. #[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] -async fn adjust_required_routing_and_propagate( - ctx: &mut Context, +async fn adjust_required_routing_and_propagate< + N: overseer::SubsystemSender, + BlockFilter, + RoutingModifier, +>( + network_sender: &mut N, blocks: &mut HashMap, topologies: &SessionGridTopologies, block_filter: BlockFilter, @@ -2363,7 +2423,7 @@ async fn adjust_required_routing_and_propagate, peer_id: PeerId, rep: Rep, ) { @@ -2414,7 +2474,6 @@ impl ApprovalDistribution { async fn run(self, ctx: Context) { let mut state = State::default(); - // According to the docs of `rand`, this is a ChaCha12 RNG in practice // and will always be chosen for strong performance and security properties. let mut rng = rand::rngs::StdRng::from_entropy(); @@ -2431,7 +2490,8 @@ impl ApprovalDistribution { ) { let new_reputation_delay = || futures_timer::Delay::new(reputation_interval).fuse(); let mut reputation_delay = new_reputation_delay(); - + let mut approval_voting_sender = ctx.sender().clone(); + let mut network_sender = ctx.sender().clone(); loop { select! { _ = reputation_delay => { @@ -2446,35 +2506,65 @@ impl ApprovalDistribution { return }, }; - match message { - FromOrchestra::Communication { msg } => - Self::handle_incoming(&mut ctx, state, msg, &self.metrics, rng).await, - FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)) => { - gum::trace!(target: LOG_TARGET, "active leaves signal (ignored)"); - // the relay chain blocks relevant to the approval subsystems - // are those that are available, but not finalized yet - // activated and deactivated heads hence are irrelevant to this subsystem, other than - // for tracing purposes. - if let Some(activated) = update.activated { - let head = activated.hash; - let approval_distribution_span = - jaeger::PerLeafSpan::new(activated.span, "approval-distribution"); - state.spans.insert(head, approval_distribution_span); - } - }, - FromOrchestra::Signal(OverseerSignal::BlockFinalized(_hash, number)) => { - gum::trace!(target: LOG_TARGET, number = %number, "finalized signal"); - state.handle_block_finalized(&mut ctx, &self.metrics, number).await; - }, - FromOrchestra::Signal(OverseerSignal::Conclude) => return, - } + + + self.handle_from_orchestra(message, &mut approval_voting_sender, &mut network_sender, state, rng).await; + }, } } } - async fn handle_incoming( - ctx: &mut Context, + /// Handles a from orchestra message received by approval distribution subystem. + pub async fn handle_from_orchestra< + N: overseer::SubsystemSender, + A: overseer::SubsystemSender, + >( + &self, + message: FromOrchestra, + approval_voting_sender: &mut A, + network_sender: &mut N, + state: &mut State, + rng: &mut (impl CryptoRng + Rng), + ) { + match message { + FromOrchestra::Communication { msg } => + Self::handle_incoming( + approval_voting_sender, + network_sender, + state, + msg, + &self.metrics, + rng, + ) + .await, + FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)) => { + gum::trace!(target: LOG_TARGET, "active leaves signal (ignored)"); + // the relay chain blocks relevant to the approval subsystems + // are those that are available, but not finalized yet + // activated and deactivated heads hence are irrelevant to this subsystem, other + // than for tracing purposes. + if let Some(activated) = update.activated { + let head = activated.hash; + let approval_distribution_span = + jaeger::PerLeafSpan::new(activated.span, "approval-distribution"); + state.spans.insert(head, approval_distribution_span); + } + }, + FromOrchestra::Signal(OverseerSignal::BlockFinalized(_hash, number)) => { + gum::trace!(target: LOG_TARGET, number = %number, "finalized signal"); + state.handle_block_finalized(network_sender, &self.metrics, number).await; + }, + FromOrchestra::Signal(OverseerSignal::Conclude) => return, + } + } + + async fn handle_incoming< + N: overseer::SubsystemSender, + A: overseer::SubsystemSender, + >( + approval_voting_sender: &mut A, + network_sender: &mut N, state: &mut State, msg: ApprovalDistributionMessage, metrics: &Metrics, @@ -2482,10 +2572,14 @@ impl ApprovalDistribution { ) { match msg { ApprovalDistributionMessage::NetworkBridgeUpdate(event) => { - state.handle_network_msg(ctx, metrics, event, rng).await; + state + .handle_network_msg(approval_voting_sender, network_sender, metrics, event, rng) + .await; }, ApprovalDistributionMessage::NewBlocks(metas) => { - state.handle_new_blocks(ctx, metrics, metas, rng).await; + state + .handle_new_blocks(approval_voting_sender, network_sender, metrics, metas, rng) + .await; }, ApprovalDistributionMessage::DistributeAssignment(cert, candidate_indices) => { let _span = state @@ -2506,7 +2600,8 @@ impl ApprovalDistribution { state .import_and_circulate_assignment( - ctx, + approval_voting_sender, + network_sender, &metrics, MessageSource::Local, cert, @@ -2524,7 +2619,13 @@ impl ApprovalDistribution { ); state - .import_and_circulate_approval(ctx, metrics, MessageSource::Local, vote) + .import_and_circulate_approval( + approval_voting_sender, + network_sender, + metrics, + MessageSource::Local, + vote, + ) .await; }, ApprovalDistributionMessage::GetApprovalSignatures(indices, tx) => { @@ -2579,7 +2680,7 @@ pub const MAX_APPROVAL_BATCH_SIZE: usize = ensure_size_not_zero( // Low level helper for sending assignments. async fn send_assignments_batched_inner( - sender: &mut impl overseer::ApprovalDistributionSenderTrait, + sender: &mut impl overseer::SubsystemSender, batch: impl IntoIterator, peers: Vec, peer_version: ValidationVersion, @@ -2634,7 +2735,7 @@ async fn send_assignments_batched_inner( /// destination, such that the subsystem doesn't get stuck for long processing a batch /// of assignments and can `select!` other tasks. pub(crate) async fn send_assignments_batched( - sender: &mut impl overseer::ApprovalDistributionSenderTrait, + network_sender: &mut impl overseer::SubsystemSender, v2_assignments: impl IntoIterator + Clone, peers: &[(PeerId, ProtocolVersion)], ) { @@ -2658,7 +2759,7 @@ pub(crate) async fn send_assignments_batched( let batch: Vec<_> = v1_batches.by_ref().take(MAX_ASSIGNMENT_BATCH_SIZE).collect(); if !v1_peers.is_empty() { send_assignments_batched_inner( - sender, + network_sender, batch.clone(), v1_peers.clone(), ValidationVersion::V1, @@ -2668,7 +2769,7 @@ pub(crate) async fn send_assignments_batched( if !v2_peers.is_empty() { send_assignments_batched_inner( - sender, + network_sender, batch, v2_peers.clone(), ValidationVersion::V2, @@ -2683,15 +2784,20 @@ pub(crate) async fn send_assignments_batched( while v3.peek().is_some() { let batch = v3.by_ref().take(MAX_ASSIGNMENT_BATCH_SIZE).collect::>(); - send_assignments_batched_inner(sender, batch, v3_peers.clone(), ValidationVersion::V3) - .await; + send_assignments_batched_inner( + network_sender, + batch, + v3_peers.clone(), + ValidationVersion::V3, + ) + .await; } } } /// Send approvals while honoring the `max_notification_size` of the protocol and peer version. pub(crate) async fn send_approvals_batched( - sender: &mut impl overseer::ApprovalDistributionSenderTrait, + sender: &mut impl overseer::SubsystemSender, approvals: impl IntoIterator + Clone, peers: &[(PeerId, ProtocolVersion)], ) { diff --git a/prdoc/pr_4845.prdoc b/prdoc/pr_4845.prdoc new file mode 100644 index 000000000000..012d34ef090e --- /dev/null +++ b/prdoc/pr_4845.prdoc @@ -0,0 +1,13 @@ +title: Make approval-distribution logic runnable on a separate thread + +doc: + - audience: Node Dev + description: | + Pass SubsystemSender trait inside approval-distribution instead of passing SubsystemContext everywhere. + + This allows us in the future to be able to run multiple approval-distribution instances on different workers. + + +crates: +- name: polkadot-approval-distribution + bump: minor From fc10887d6c96ab5e015df08defa8d40aef301838 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Mon, 29 Jul 2024 14:41:53 +0200 Subject: [PATCH 038/382] rpc v2: `submitAndWatch` replace old messages if it's lagging (#4901) Close https://github.com/paritytech/polkadot-sdk/issues/3076 The fix is really just that older messages are replaced if the client can't keep up with the server instead. Because I wanted the same functionality as `pipe_from_stream` for both pending/subscription I added two wrapper types on-top of the types from jsonrpsee to make it nicer. I added a trait `Buffer` so I could still use pipe_from_stream but that abstraction is a little leaky but only to avoid adding an identical method/function with another strategy... --- .../client/consensus/beefy/rpc/src/lib.rs | 10 +- .../client/consensus/grandpa/rpc/src/lib.rs | 10 +- .../rpc-spec-v2/src/chain_head/chain_head.rs | 11 +- .../src/chain_head/chain_head_follow.rs | 22 +- .../transaction/tests/transaction_tests.rs | 88 ++++- .../src/transaction/transaction.rs | 27 +- substrate/client/rpc/src/author/mod.rs | 6 +- substrate/client/rpc/src/chain/chain_full.rs | 8 +- substrate/client/rpc/src/state/state_full.rs | 12 +- substrate/client/rpc/src/utils.rs | 349 ++++++++++++------ 10 files changed, 387 insertions(+), 156 deletions(-) diff --git a/substrate/client/consensus/beefy/rpc/src/lib.rs b/substrate/client/consensus/beefy/rpc/src/lib.rs index 66102eeb35c8..83477d223dd2 100644 --- a/substrate/client/consensus/beefy/rpc/src/lib.rs +++ b/substrate/client/consensus/beefy/rpc/src/lib.rs @@ -24,7 +24,10 @@ use parking_lot::RwLock; use sp_consensus_beefy::AuthorityIdBound; use std::sync::Arc; -use sc_rpc::{utils::pipe_from_stream, SubscriptionTaskExecutor}; +use sc_rpc::{ + utils::{BoundedVecDeque, PendingSubscription}, + SubscriptionTaskExecutor, +}; use sp_application_crypto::RuntimeAppPublic; use sp_runtime::traits::Block as BlockT; @@ -145,7 +148,10 @@ where .subscribe(100_000) .map(|vfp| notification::EncodedVersionedFinalityProof::new::(vfp)); - sc_rpc::utils::spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream)); + sc_rpc::utils::spawn_subscription_task( + &self.executor, + PendingSubscription::from(pending).pipe_from_stream(stream, BoundedVecDeque::default()), + ); } async fn latest_finalized(&self) -> Result { diff --git a/substrate/client/consensus/grandpa/rpc/src/lib.rs b/substrate/client/consensus/grandpa/rpc/src/lib.rs index 430525019dfb..a41b14299089 100644 --- a/substrate/client/consensus/grandpa/rpc/src/lib.rs +++ b/substrate/client/consensus/grandpa/rpc/src/lib.rs @@ -38,7 +38,10 @@ use finality::{EncodedFinalityProof, RpcFinalityProofProvider}; use notification::JustificationNotification; use report::{ReportAuthoritySet, ReportVoterState, ReportedRoundStates}; use sc_consensus_grandpa::GrandpaJustificationStream; -use sc_rpc::{utils::pipe_from_stream, SubscriptionTaskExecutor}; +use sc_rpc::{ + utils::{BoundedVecDeque, PendingSubscription}, + SubscriptionTaskExecutor, +}; use sp_runtime::traits::{Block as BlockT, NumberFor}; /// Provides RPC methods for interacting with GRANDPA. @@ -108,7 +111,10 @@ where }, ); - sc_rpc::utils::spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream)); + sc_rpc::utils::spawn_subscription_task( + &self.executor, + PendingSubscription::from(pending).pipe_from_stream(stream, BoundedVecDeque::default()), + ); } async fn prove_finality( diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs index a056b4d437c8..1bc5cecb205b 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs @@ -37,14 +37,14 @@ use codec::Encode; use futures::{channel::oneshot, future::FutureExt}; use jsonrpsee::{ core::async_trait, server::ResponsePayload, types::SubscriptionId, ConnectionId, Extensions, - MethodResponseFuture, PendingSubscriptionSink, SubscriptionSink, + MethodResponseFuture, PendingSubscriptionSink, }; use log::debug; use sc_client_api::{ Backend, BlockBackend, BlockchainEvents, CallExecutor, ChildInfo, ExecutorProvider, StorageKey, StorageProvider, }; -use sc_rpc::utils::to_sub_message; +use sc_rpc::utils::Subscription; use sp_api::CallApiAt; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use sp_core::{traits::CallContext, Bytes}; @@ -158,7 +158,7 @@ impl, Block: BlockT, Client> ChainHead { } /// Helper to convert the `subscription ID` to a string. -pub fn read_subscription_id_as_string(sink: &SubscriptionSink) -> String { +pub fn read_subscription_id_as_string(sink: &Subscription) -> String { match sink.subscription_id() { SubscriptionId::Num(n) => n.to_string(), SubscriptionId::Str(s) => s.into_owned().into(), @@ -213,7 +213,7 @@ where return }; - let Ok(sink) = pending.accept().await else { return }; + let Ok(sink) = pending.accept().await.map(Subscription::from) else { return }; let sub_id = read_subscription_id_as_string(&sink); // Keep track of the subscription. @@ -223,8 +223,7 @@ where // Inserting the subscription can only fail if the JsonRPSee generated a duplicate // subscription ID. debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription already accepted", sub_id); - let msg = to_sub_message(&sink, &FollowEvent::::Stop); - let _ = sink.send(msg).await; + let _ = sink.send(&FollowEvent::::Stop).await; return }; debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription accepted", sub_id); diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs index 6dc3df76bdd7..1d28d2071248 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs @@ -31,12 +31,11 @@ use futures::{ stream::{self, Stream, StreamExt}, }; use futures_util::future::Either; -use jsonrpsee::SubscriptionSink; use log::debug; use sc_client_api::{ Backend, BlockBackend, BlockImportNotification, BlockchainEvents, FinalityNotification, }; -use sc_rpc::utils::to_sub_message; +use sc_rpc::utils::Subscription; use schnellru::{ByLength, LruMap}; use sp_api::CallApiAt; use sp_blockchain::{ @@ -597,7 +596,7 @@ where &mut self, startup_point: &StartupPoint, mut stream: EventStream, - sink: SubscriptionSink, + sink: Subscription, rx_stop: oneshot::Receiver<()>, ) -> Result<(), SubscriptionManagementError> where @@ -632,23 +631,20 @@ where self.sub_id, err ); - let msg = to_sub_message(&sink, &FollowEvent::::Stop); - let _ = sink.send(msg).await; + _ = sink.send(&FollowEvent::::Stop).await; return Err(err) }, }; for event in events { - let msg = to_sub_message(&sink, &event); - if let Err(err) = sink.send(msg).await { + if let Err(err) = sink.send(&event).await { // Failed to submit event. debug!( target: LOG_TARGET, "[follow][id={:?}] Failed to send event {:?}", self.sub_id, err ); - let msg = to_sub_message(&sink, &FollowEvent::::Stop); - let _ = sink.send(msg).await; + let _ = sink.send(&FollowEvent::::Stop).await; // No need to propagate this error further, the client disconnected. return Ok(()) } @@ -662,15 +658,14 @@ where // - the substrate streams have closed // - the `Stop` receiver was triggered internally (cannot hold the pinned block guarantee) // - the client disconnected. - let msg = to_sub_message(&sink, &FollowEvent::::Stop); - let _ = sink.send(msg).await; + let _ = sink.send(&FollowEvent::::Stop).await; Ok(()) } /// Generate the block events for the `chainHead_follow` method. pub async fn generate_events( &mut self, - sink: SubscriptionSink, + sink: Subscription, sub_data: InsertedSubscriptionData, ) -> Result<(), SubscriptionManagementError> { // Register for the new block and finalized notifications. @@ -698,8 +693,7 @@ where self.sub_id, err ); - let msg = to_sub_message(&sink, &FollowEvent::::Stop); - let _ = sink.send(msg).await; + let _ = sink.send(&FollowEvent::::Stop).await; return Err(err) }, }; diff --git a/substrate/client/rpc-spec-v2/src/transaction/tests/transaction_tests.rs b/substrate/client/rpc-spec-v2/src/transaction/tests/transaction_tests.rs index 7ce85b9feafe..53c5b8ce3895 100644 --- a/substrate/client/rpc-spec-v2/src/transaction/tests/transaction_tests.rs +++ b/substrate/client/rpc-spec-v2/src/transaction/tests/transaction_tests.rs @@ -25,7 +25,7 @@ use codec::Encode; use jsonrpsee::rpc_params; use sc_transaction_pool_api::{ChainEvent, MaintainedTransactionPool}; use sp_core::H256; -use std::sync::Arc; +use std::{sync::Arc, vec}; use substrate_test_runtime_client::AccountKeyring::*; use substrate_test_runtime_transaction_pool::uxt; @@ -149,3 +149,89 @@ async fn tx_with_pruned_best_block() { let event: TransactionEvent = get_next_event_sub!(&mut sub); assert_eq!(event, TransactionEvent::Finalized(TransactionBlock { hash: block_2, index: 0 })); } + +#[tokio::test] +async fn tx_slow_client_replace_old_messages() { + let (api, pool, client, tx_api, _exec_middleware, _pool_middleware) = setup_api_tx(); + let block_1_header = api.push_block(1, vec![], true); + client.set_best_block(block_1_header.hash(), 1); + + let uxt = uxt(Alice, ALICE_NONCE); + let xt = hex_string(&uxt.encode()); + + // The subscription itself has a buffer of length 1 and no way to create + // it without a buffer. + // + // Then `transactionWatch` has its own buffer of length 3 which leads to + // that it's limited to 5 items in the tests. + // + // 1. Send will complete immediately + // 2. Send will be pending in the subscription sink (not possible to cancel) + // 3. The rest of messages will be kept in a RingBuffer and older messages are replaced by newer + // items. + let mut sub = tx_api + .subscribe("transactionWatch_v1_submitAndWatch", rpc_params![&xt], 1) + .await + .unwrap(); + + // Import block 2 with the transaction included. + let block = api.push_block(2, vec![uxt.clone()], true); + let block_hash = block.hash(); + let event = ChainEvent::NewBestBlock { hash: block_hash, tree_route: None }; + pool.inner_pool.maintain(event).await; + + let mut block2_hash = None; + + // Import block 2 again without the transaction included. + for _ in 0..10 { + let block_not_imported = api.push_block(2, vec![], true); + let event = ChainEvent::NewBestBlock { hash: block_not_imported.hash(), tree_route: None }; + pool.inner_pool.maintain(event).await; + + let block2 = api.push_block(2, vec![uxt.clone()], true); + block2_hash = Some(block2.hash()); + let event = ChainEvent::NewBestBlock { hash: block2.hash(), tree_route: None }; + + pool.inner_pool.maintain(event).await; + } + + let block2_hash = block2_hash.unwrap(); + + // Finalize the transaction + let event = ChainEvent::Finalized { hash: block2_hash, tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + + // Hack to mimic a slow client. + tokio::time::sleep(std::time::Duration::from_secs(10)).await; + + // Read the events. + let mut res: Vec> = Vec::new(); + + while let Some(item) = tokio::time::timeout(std::time::Duration::from_secs(5), sub.next()) + .await + .unwrap() + { + let (ev, _) = item.unwrap(); + res.push(ev); + } + + // BestBlockIncluded(None) is dropped and not seen. + let exp = vec![ + // First message + TransactionEvent::Validated, + // Second message + TransactionEvent::BestChainBlockIncluded(Some(TransactionBlock { + hash: block_hash, + index: 0, + })), + // Most recent 3 messages. + TransactionEvent::Validated, + TransactionEvent::BestChainBlockIncluded(Some(TransactionBlock { + hash: block2_hash, + index: 0, + })), + TransactionEvent::Finalized(TransactionBlock { hash: block2_hash, index: 0 }), + ]; + + assert_eq!(res, exp); +} diff --git a/substrate/client/rpc-spec-v2/src/transaction/transaction.rs b/substrate/client/rpc-spec-v2/src/transaction/transaction.rs index 723440d1b111..ac24ce960f61 100644 --- a/substrate/client/rpc-spec-v2/src/transaction/transaction.rs +++ b/substrate/client/rpc-spec-v2/src/transaction/transaction.rs @@ -30,7 +30,7 @@ use crate::{ use codec::Decode; use futures::{StreamExt, TryFutureExt}; use jsonrpsee::{core::async_trait, PendingSubscriptionSink}; -use sc_rpc::utils::{pipe_from_stream, to_sub_message}; +use sc_rpc::utils::{RingBuffer, Subscription}; use sc_transaction_pool_api::{ error::IntoPoolError, BlockHash, TransactionFor, TransactionPool, TransactionSource, TransactionStatus, @@ -84,16 +84,14 @@ where Err(e) => { log::debug!(target: LOG_TARGET, "Extrinsic bytes cannot be decoded: {:?}", e); - let Ok(sink) = pending.accept().await else { return }; + let Ok(sink) = pending.accept().await.map(Subscription::from) else { return }; // The transaction is invalid. - let msg = to_sub_message( - &sink, - &TransactionEvent::Invalid::>(TransactionError { + let _ = sink + .send(&TransactionEvent::Invalid::>(TransactionError { error: "Extrinsic bytes cannot be decoded".into(), - }), - ); - let _ = sink.send(msg).await; + })) + .await; return }, }; @@ -108,16 +106,23 @@ where .unwrap_or_else(|e| Error::Verification(Box::new(e))) }); + let Ok(sink) = pending.accept().await.map(Subscription::from) else { + return; + }; + match submit.await { Ok(stream) => { - let stream = stream.filter_map(move |event| async move { handle_event(event) }); - pipe_from_stream(pending, stream.boxed()).await; + let stream = + stream.filter_map(move |event| async move { handle_event(event) }).boxed(); + + // If the subscription is too slow older events will be overwritten. + sink.pipe_from_stream(stream, RingBuffer::new(3)).await; }, Err(err) => { // We have not created an `Watcher` for the tx. Make sure the // error is still propagated as an event. let event: TransactionEvent<::Hash> = err.into(); - pipe_from_stream(pending, futures::stream::once(async { event }).boxed()).await; + _ = sink.send(&event).await; }, }; }; diff --git a/substrate/client/rpc/src/author/mod.rs b/substrate/client/rpc/src/author/mod.rs index 975f66406a6a..2fc21a238bc9 100644 --- a/substrate/client/rpc/src/author/mod.rs +++ b/substrate/client/rpc/src/author/mod.rs @@ -24,7 +24,7 @@ mod tests; use std::sync::Arc; use crate::{ - utils::{pipe_from_stream, spawn_subscription_task}, + utils::{spawn_subscription_task, BoundedVecDeque, PendingSubscription}, SubscriptionTaskExecutor, }; @@ -202,7 +202,9 @@ where }, }; - pipe_from_stream(pending, stream).await; + PendingSubscription::from(pending) + .pipe_from_stream(stream, BoundedVecDeque::default()) + .await; }; spawn_subscription_task(&self.executor, fut); diff --git a/substrate/client/rpc/src/chain/chain_full.rs b/substrate/client/rpc/src/chain/chain_full.rs index 515c0f62c8ad..139c18b884ac 100644 --- a/substrate/client/rpc/src/chain/chain_full.rs +++ b/substrate/client/rpc/src/chain/chain_full.rs @@ -20,7 +20,7 @@ use super::{client_err, ChainBackend, Error}; use crate::{ - utils::{pipe_from_stream, spawn_subscription_task}, + utils::{spawn_subscription_task, BoundedVecDeque, PendingSubscription}, SubscriptionTaskExecutor, }; use std::{marker::PhantomData, sync::Arc}; @@ -142,6 +142,8 @@ fn subscribe_headers( // we set up the stream and chain it to the stream. Consuming code would need to handle // duplicates at the beginning of the stream though. let stream = stream::iter(maybe_header).chain(stream()); - - spawn_subscription_task(executor, pipe_from_stream(pending, stream)); + spawn_subscription_task( + executor, + PendingSubscription::from(pending).pipe_from_stream(stream, BoundedVecDeque::default()), + ); } diff --git a/substrate/client/rpc/src/state/state_full.rs b/substrate/client/rpc/src/state/state_full.rs index bda678c1b45e..7703936f8115 100644 --- a/substrate/client/rpc/src/state/state_full.rs +++ b/substrate/client/rpc/src/state/state_full.rs @@ -26,7 +26,7 @@ use super::{ ChildStateBackend, StateBackend, }; use crate::{ - utils::{pipe_from_stream, spawn_subscription_task}, + utils::{spawn_subscription_task, BoundedVecDeque, PendingSubscription}, DenyUnsafe, SubscriptionTaskExecutor, }; @@ -405,7 +405,10 @@ where }); let stream = futures::stream::once(future::ready(initial)).chain(version_stream); - spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream)); + spawn_subscription_task( + &self.executor, + PendingSubscription::from(pending).pipe_from_stream(stream, BoundedVecDeque::default()), + ); } fn subscribe_storage( @@ -457,7 +460,10 @@ where .chain(storage_stream) .filter(|storage| future::ready(!storage.changes.is_empty())); - spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream)); + spawn_subscription_task( + &self.executor, + PendingSubscription::from(pending).pipe_from_stream(stream, BoundedVecDeque::default()), + ); } fn trace_block( diff --git a/substrate/client/rpc/src/utils.rs b/substrate/client/rpc/src/utils.rs index bc566ed37f23..e2ff04c0baf3 100644 --- a/substrate/client/rpc/src/utils.rs +++ b/substrate/client/rpc/src/utils.rs @@ -23,25 +23,52 @@ use futures::{ future::{self, Either, Fuse, FusedFuture}, Future, FutureExt, Stream, StreamExt, }; -use jsonrpsee::{PendingSubscriptionSink, SubscriptionMessage, SubscriptionSink}; +use jsonrpsee::{ + types::SubscriptionId, DisconnectError, PendingSubscriptionSink, SubscriptionMessage, + SubscriptionSink, +}; use sp_runtime::Serialize; use std::collections::VecDeque; const DEFAULT_BUF_SIZE: usize = 16; -/// A simple bounded VecDeque. -struct BoundedVecDeque { +/// A trait representing a buffer which may or may not support +/// to replace items when the buffer is full. +pub trait Buffer { + /// The item type that the buffer holds. + type Item; + + /// Push an item to the buffer. + /// + /// Returns `Err` if the buffer doesn't support replacing older items + fn push(&mut self, item: Self::Item) -> Result<(), ()>; + /// Pop the next item from the buffer. + fn pop(&mut self) -> Option; +} + +/// A simple bounded buffer that will terminate the subscription if the buffer becomes full. +pub struct BoundedVecDeque { inner: VecDeque, max_cap: usize, } +impl Default for BoundedVecDeque { + fn default() -> Self { + Self { inner: VecDeque::with_capacity(DEFAULT_BUF_SIZE), max_cap: DEFAULT_BUF_SIZE } + } +} + impl BoundedVecDeque { /// Create a new bounded VecDeque. - fn new() -> Self { - Self { inner: VecDeque::with_capacity(DEFAULT_BUF_SIZE), max_cap: DEFAULT_BUF_SIZE } + pub fn new(cap: usize) -> Self { + Self { inner: VecDeque::with_capacity(cap), max_cap: cap } } +} + +impl Buffer for BoundedVecDeque { + type Item = T; - fn push_back(&mut self, item: T) -> Result<(), ()> { + fn push(&mut self, item: Self::Item) -> Result<(), ()> { if self.inner.len() >= self.max_cap { Err(()) } else { @@ -50,126 +77,187 @@ impl BoundedVecDeque { } } - fn pop_front(&mut self) -> Option { + fn pop(&mut self) -> Option { self.inner.pop_front() } } -/// Feed items to the subscription from the underlying stream. -/// -/// This is bounded because the underlying streams in substrate are -/// unbounded and if the subscription can't keep with stream it can -/// cause the buffer to become very large and consume lots of memory. -/// -/// In such cases the subscription is dropped. -pub async fn pipe_from_stream(pending: PendingSubscriptionSink, mut stream: S) -where - S: Stream + Unpin + Send + 'static, - T: Serialize + Send + 'static, -{ - let mut buf = BoundedVecDeque::new(); - let accept_fut = pending.accept(); - - futures::pin_mut!(accept_fut); - - // Poll the stream while waiting for the subscription to be accepted - // - // If the `max_cap` is exceeded then the subscription is dropped. - let sink = loop { - match future::select(accept_fut, stream.next()).await { - Either::Left((Ok(sink), _)) => break sink, - Either::Right((Some(msg), f)) => { - if buf.push_back(msg).is_err() { - log::warn!(target: "rpc", "Subscription::accept failed buffer limit={} exceeded; dropping subscription", buf.max_cap); - return - } - accept_fut = f; - }, - // The connection was closed or the stream was closed. - _ => return, +/// Fixed size ring buffer that replaces the oldest item when full. +#[derive(Debug)] +pub struct RingBuffer { + inner: VecDeque, + cap: usize, +} + +impl RingBuffer { + /// Create a new ring buffer. + pub fn new(cap: usize) -> Self { + Self { inner: VecDeque::with_capacity(cap), cap } + } +} + +impl Buffer for RingBuffer { + type Item = T; + + fn push(&mut self, item: T) -> Result<(), ()> { + if self.inner.len() >= self.cap { + self.inner.pop_front(); } - }; - inner_pipe_from_stream(sink, stream, buf).await + self.inner.push_back(item); + + Ok(()) + } + + fn pop(&mut self) -> Option { + self.inner.pop_front() + } } -async fn inner_pipe_from_stream( - sink: SubscriptionSink, - mut stream: S, - mut buf: BoundedVecDeque, -) where - S: Stream + Unpin + Send + 'static, - T: Serialize + Send + 'static, -{ - let mut next_fut = Box::pin(Fuse::terminated()); - let mut next_item = stream.next(); - let closed = sink.closed(); - - futures::pin_mut!(closed); - - loop { - if next_fut.is_terminated() { - if let Some(v) = buf.pop_front() { - let val = to_sub_message(&sink, &v); - next_fut.set(async { sink.send(val).await }.fuse()); +/// A pending subscription. +pub struct PendingSubscription(PendingSubscriptionSink); + +impl From for PendingSubscription { + fn from(p: PendingSubscriptionSink) -> Self { + Self(p) + } +} + +impl PendingSubscription { + /// Feed items to the subscription from the underlying stream + /// with specified buffer strategy. + pub async fn pipe_from_stream(self, mut stream: S, mut buf: B) + where + S: Stream + Unpin + Send + 'static, + T: Serialize + Send + 'static, + B: Buffer, + { + let method = self.0.method_name().to_string(); + let conn_id = self.0.connection_id().0; + let accept_fut = self.0.accept(); + + futures::pin_mut!(accept_fut); + + // Poll the stream while waiting for the subscription to be accepted + // + // If the `max_cap` is exceeded then the subscription is dropped. + let sink = loop { + match future::select(accept_fut, stream.next()).await { + Either::Left((Ok(sink), _)) => break sink, + Either::Right((Some(msg), f)) => { + if buf.push(msg).is_err() { + log::debug!(target: "rpc", "Subscription::accept buffer full for subscription={method} conn_id={conn_id}; dropping subscription"); + return + } + accept_fut = f; + }, + // The connection was closed or the stream was closed. + _ => return, } - } + }; - match future::select(closed, future::select(next_fut, next_item)).await { - // Send operation finished. - Either::Right((Either::Left((_, n)), c)) => { - next_item = n; - closed = c; - next_fut = Box::pin(Fuse::terminated()); - }, - // New item from the stream - Either::Right((Either::Right((Some(v), n)), c)) => { - if buf.push_back(v).is_err() { - log::warn!( - target: "rpc", - "Subscription buffer limit={} exceeded for subscription={} conn_id={}; dropping subscription", - buf.max_cap, - sink.method_name(), - sink.connection_id().0 - ); - return - } + Subscription(sink).pipe_from_stream(stream, buf).await + } +} - next_fut = n; - closed = c; - next_item = stream.next(); - }, - // Stream "finished". - // - // Process remaining items and terminate. - Either::Right((Either::Right((None, pending_fut)), _)) => { - if !pending_fut.is_terminated() && pending_fut.await.is_err() { - return; +/// An active subscription. +#[derive(Clone, Debug)] +pub struct Subscription(SubscriptionSink); + +impl From for Subscription { + fn from(sink: SubscriptionSink) -> Self { + Self(sink) + } +} + +impl Subscription { + /// Feed items to the subscription from the underlying stream + /// with specified buffer strategy. + pub async fn pipe_from_stream(self, mut stream: S, mut buf: B) + where + S: Stream + Unpin + Send + 'static, + T: Serialize + Send + 'static, + B: Buffer, + { + let mut next_fut = Box::pin(Fuse::terminated()); + let mut next_item = stream.next(); + let closed = self.0.closed(); + + futures::pin_mut!(closed); + + loop { + if next_fut.is_terminated() { + if let Some(v) = buf.pop() { + let val = self.to_sub_message(&v); + next_fut.set(async { self.0.send(val).await }.fuse()); } + } + + match future::select(closed, future::select(next_fut, next_item)).await { + // Send operation finished. + Either::Right((Either::Left((_, n)), c)) => { + next_item = n; + closed = c; + next_fut = Box::pin(Fuse::terminated()); + }, + // New item from the stream + Either::Right((Either::Right((Some(v), n)), c)) => { + if buf.push(v).is_err() { + log::debug!( + target: "rpc", + "Subscription buffer full for subscription={} conn_id={}; dropping subscription", + self.0.method_name(), + self.0.connection_id().0 + ); + return + } - while let Some(v) = buf.pop_front() { - let val = to_sub_message(&sink, &v); - if sink.send(val).await.is_err() { + next_fut = n; + closed = c; + next_item = stream.next(); + }, + // Stream "finished". + // + // Process remaining items and terminate. + Either::Right((Either::Right((None, pending_fut)), _)) => { + if !pending_fut.is_terminated() && pending_fut.await.is_err() { return; } - } - return; - }, - // Subscription was closed. - Either::Left(_) => return, + while let Some(v) = buf.pop() { + if self.send(&v).await.is_err() { + return; + } + } + + return; + }, + // Subscription was closed. + Either::Left(_) => return, + } } } -} -/// Builds a subscription message. -/// -/// # Panics -/// -/// This function panics `Serialize` fails and it is treated a bug. -pub fn to_sub_message(sink: &SubscriptionSink, result: &impl Serialize) -> SubscriptionMessage { - SubscriptionMessage::new(sink.method_name(), sink.subscription_id(), result) - .expect("Serialize infallible; qed") + /// Send a message on the subscription. + pub async fn send(&self, result: &impl Serialize) -> Result<(), DisconnectError> { + self.0.send(self.to_sub_message(result)).await + } + + /// Get the subscription id. + pub fn subscription_id(&self) -> SubscriptionId { + self.0.subscription_id() + } + + /// Completes when the subscription is closed. + pub async fn closed(&self) { + self.0.closed().await + } + + /// Convert a result to a subscription message. + fn to_sub_message(&self, result: &impl Serialize) -> SubscriptionMessage { + SubscriptionMessage::new(self.0.method_name(), self.0.subscription_id(), result) + .expect("Serialize infallible; qed") + } } /// Helper for spawning non-blocking rpc subscription task. @@ -182,7 +270,7 @@ pub fn spawn_subscription_task( #[cfg(test)] mod tests { - use super::pipe_from_stream; + use super::*; use futures::StreamExt; use jsonrpsee::{core::EmptyServerParams, RpcModule, Subscription}; @@ -191,7 +279,9 @@ mod tests { module .register_subscription("sub", "my_sub", "unsub", |_, pending, _, _| async move { let stream = futures::stream::iter([0; 16]); - pipe_from_stream(pending, stream).await; + PendingSubscription::from(pending) + .pipe_from_stream(stream, BoundedVecDeque::new(16)) + .await; Ok(()) }) .unwrap(); @@ -212,14 +302,16 @@ mod tests { } #[tokio::test] - async fn pipe_from_stream_is_bounded() { + async fn pipe_from_stream_with_bounded_vec() { let (tx, mut rx) = futures::channel::mpsc::unbounded::<()>(); let mut module = RpcModule::new(tx); module .register_subscription("sub", "my_sub", "unsub", |_, pending, ctx, _| async move { let stream = futures::stream::iter([0; 32]); - pipe_from_stream(pending, stream).await; + PendingSubscription::from(pending) + .pipe_from_stream(stream, BoundedVecDeque::new(16)) + .await; _ = ctx.unbounded_send(()); Ok(()) }) @@ -248,7 +340,9 @@ mod tests { // to sync buffer and channel send operations let stream = futures::stream::empty::<()>(); // this should exit immediately - pipe_from_stream(pending, stream).await; + PendingSubscription::from(pending) + .pipe_from_stream(stream, BoundedVecDeque::default()) + .await; // notify that the `pipe_from_stream` has returned notify_tx.notify_one(); Ok(()) @@ -260,4 +354,35 @@ mod tests { // it should fire once `pipe_from_stream` returns notify_rx.notified().await; } + + #[tokio::test] + async fn subscription_replace_old_messages() { + let mut module = RpcModule::new(()); + module + .register_subscription("sub", "my_sub", "unsub", |_, pending, _, _| async move { + // Send items 0..20 and ensure that only the last 3 are kept in the buffer. + let stream = futures::stream::iter(0..20); + PendingSubscription::from(pending) + .pipe_from_stream(stream, RingBuffer::new(3)) + .await; + Ok(()) + }) + .unwrap(); + + let mut sub = module.subscribe("sub", EmptyServerParams::new(), 1).await.unwrap(); + + // This is a hack simulate a very slow client + // and all older messages are replaced. + tokio::time::sleep(std::time::Duration::from_secs(10)).await; + + let mut res = Vec::new(); + + while let Some(Ok((v, _))) = sub.next::().await { + res.push(v); + } + + // There is no way to cancel pending send operations so + // that's why 0 is included here. + assert_eq!(res, vec![0, 17, 18, 19]); + } } From de73c77c3c33aa6172df33febeeec2e177381819 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Mon, 29 Jul 2024 15:42:00 +0200 Subject: [PATCH 039/382] Various corrections in the documentation (#5154) An attempt to improve [the docs](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/index.html) by applying various corrections: - grammar/stylistics, - formatting, - broken links, - broken markdown table, - outdated vscode setting name, - typos, - consistency, - etc. Part of https://github.com/paritytech/eng-automation/issues/10 --- Cargo.lock | 5 +++ docs/contributor/DOCUMENTATION_GUIDELINES.md | 21 +++++----- docs/sdk/Cargo.toml | 5 +++ docs/sdk/src/guides/async_backing_guide.rs | 4 +- .../src/guides/enable_elastic_scaling_mvp.rs | 9 +++-- docs/sdk/src/guides/enable_metadata_hash.rs | 6 +-- docs/sdk/src/guides/enable_pov_reclaim.rs | 2 + docs/sdk/src/guides/mod.rs | 12 +++--- docs/sdk/src/guides/your_first_pallet/mod.rs | 16 ++++---- docs/sdk/src/lib.rs | 6 +-- docs/sdk/src/meta_contributing.rs | 2 +- docs/sdk/src/polkadot_sdk/frame_runtime.rs | 8 ++-- docs/sdk/src/polkadot_sdk/mod.rs | 12 +++--- docs/sdk/src/polkadot_sdk/polkadot.rs | 10 ++--- docs/sdk/src/polkadot_sdk/substrate.rs | 16 ++++---- docs/sdk/src/polkadot_sdk/xcm.rs | 15 ++++--- docs/sdk/src/reference_docs/cli.rs | 2 +- .../reference_docs/custom_runtime_api_rpc.rs | 12 +++--- .../reference_docs/defensive_programming.rs | 6 +-- .../development_environment_advice.rs | 7 ++-- .../frame_benchmarking_weight.rs | 8 ++-- docs/sdk/src/reference_docs/frame_logging.rs | 4 +- .../reference_docs/frame_offchain_workers.rs | 2 +- .../reference_docs/frame_pallet_coupling.rs | 14 +++---- .../src/reference_docs/frame_runtime_types.rs | 2 +- .../frame_runtime_upgrades_and_migrations.rs | 20 +++++----- .../reference_docs/frame_storage_derives.rs | 11 +++-- .../reference_docs/frame_system_accounts.rs | 2 +- docs/sdk/src/reference_docs/frame_tokens.rs | 40 +++++++++---------- docs/sdk/src/reference_docs/metadata.rs | 4 +- docs/sdk/src/reference_docs/mod.rs | 19 ++++----- .../runtime_vs_smart_contract.rs | 21 ++++------ .../reference_docs/trait_based_programming.rs | 2 +- docs/sdk/src/reference_docs/umbrella_crate.rs | 4 +- .../src/reference_docs/wasm_meta_protocol.rs | 10 ++--- substrate/frame/support/src/lib.rs | 4 +- 36 files changed, 177 insertions(+), 166 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7712363e79d..407ac2b57ce7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14555,6 +14555,7 @@ dependencies = [ "frame-support", "frame-system", "kitchensink-runtime", + "log", "minimal-template-runtime", "pallet-asset-conversion-tx-payment", "pallet-asset-tx-payment", @@ -14581,6 +14582,7 @@ dependencies = [ "pallet-transaction-payment", "pallet-uniques", "pallet-utility", + "pallet-xcm", "parachain-template-runtime", "parity-scale-codec", "polkadot-sdk", @@ -14618,9 +14620,12 @@ dependencies = [ "staging-node-cli", "staging-parachain-info", "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", "subkey", "substrate-wasm-builder", "xcm-docs", + "xcm-simulator", ] [[package]] diff --git a/docs/contributor/DOCUMENTATION_GUIDELINES.md b/docs/contributor/DOCUMENTATION_GUIDELINES.md index 96811a2772d7..cc1082347d20 100644 --- a/docs/contributor/DOCUMENTATION_GUIDELINES.md +++ b/docs/contributor/DOCUMENTATION_GUIDELINES.md @@ -1,7 +1,7 @@ # Substrate Documentation Guidelines This document is focused on documenting parts of Substrate that relate to its external API. The list of such crates can -be found in [CODEOWNERS](./CODEOWNERS). Search for the crates auto-assigned to the `docs-audit` team. +be found in [CODEOWNERS](/.github/CODEOWNERS). Search for the crates auto-assigned to the `docs-audit` team. These crates are used by external developers and need thorough documentation. They are the most concerned with FRAME development. @@ -33,7 +33,7 @@ First, consider the case for all such crates, except for those that are pallets. The first question is, what should you document? Use this filter: -1. In the crates assigned to `docs-audit` in [CODEOWNERS](./CODEOWNERS), +1. In the crates assigned to `docs-audit` in [CODEOWNERS](/.github/CODEOWNERS), 2. All `pub` items need to be documented. If not `pub`, it doesn't appear in the rust-docs, and is not public facing. - Within `pub` items, sometimes they are only `pub` to be used by another internal crate, and you can foresee that @@ -88,20 +88,19 @@ sections](https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/ru we will most likely not need to think about panic and safety in any runtime related code. Our code is never `unsafe`, and will (almost) never panic. -Use `# Examples as much as possible. These are great ways to further demonstrate what your APIs are doing, and add free -test coverage. As an additional benefit, any code in rust-docs is treated as an "integration tests", not unit tests, +Use `# Examples` as much as possible. These are great ways to further demonstrate what your APIs are doing, and add free +test coverage. As an additional benefit, any code in rust-docs is treated as an "integration test", which tests your crate in a different way than unit tests. So, it is both a win for "more documentation" and a win for "more test coverage". You can also consider having an `# Error` section optionally. Of course, this only applies if there is a `Result` being returned, and if the `Error` variants are overly complicated. -Strive to include correct links to other items in your written docs as much as possible. In other words, avoid -\`some_func\` and instead use \[\`some_fund\`\]. Read more about how to correctly use links in your rust-docs +Strive to include correct links to other items in your written docs as much as possible. +Read more about how to correctly use links in your rust-docs [here](https://doc.rust-lang.org/rustdoc/write-documentation/linking-to-items-by-name.html#valid-links) and -[here](https://rust-lang.github.io/rfcs/1946-intra-rustdoc-links.html#additions-to-the-documentation-syntax). Strive to -include correct links to other items in your written docs as much as possible. In other words, avoid `` `some_func` `` -and instead use ``[`some_func`]``. +[here](https://rust-lang.github.io/rfcs/1946-intra-rustdoc-links.html#additions-to-the-documentation-syntax). +In other words, avoid `` `some_func` `` and instead use ``[`some_func`]``. > While you are linking, you might become conscious of the fact that you are in need of linking to (too many) foreign items in order to explain your API. This is leaning more towards API-Design rather than documentation, but it is a @@ -145,7 +144,7 @@ The following are a set of notes that may not necessarily hold in all circumstan You should make sure that your code is properly-named and well-organized so that your code functions as a form of documentation. However, within the complexity of our projects in Polkadot/Substrate that is not enough. Particularly, -things like examples, errors and panics cannot be documented only through properly- named and well-organized code. +things like examples, errors and panics cannot be documented only through properly-named and well-organized code. > Our north star is self-documenting code that also happens to be well-documented and littered with examples. @@ -272,7 +271,7 @@ For the top-level pallet docs, consider the following template: //! up> ``` -This template's details (heading 3s and beyond) are left flexible, and at the discretion of the developer to make the +This template's details (Heading 3s and beyond) are left flexible, and at the discretion of the developer to make the best final choice about. For example, you might want to include `### Terminology` or not. Moreover, you might find it more useful to include it in `## Overview`. diff --git a/docs/sdk/Cargo.toml b/docs/sdk/Cargo.toml index d3e48de5d181..2f85171bb93d 100644 --- a/docs/sdk/Cargo.toml +++ b/docs/sdk/Cargo.toml @@ -40,6 +40,7 @@ frame-support = { workspace = true } frame-executive = { workspace = true } pallet-example-single-block-migrations = { workspace = true, default-features = true } frame-metadata-hash-extension = { workspace = true, default-features = true } +log = { workspace = true, default-features = true } # Substrate Client sc-network = { workspace = true, default-features = true } @@ -107,7 +108,11 @@ sp-version = { workspace = true, default-features = true } # XCM xcm = { workspace = true, default-features = true } +xcm-builder = { workspace = true } xcm-docs = { workspace = true } +xcm-executor = { workspace = true } +xcm-simulator = { workspace = true } +pallet-xcm = { workspace = true } # runtime guides chain-spec-guide-runtime = { workspace = true } diff --git a/docs/sdk/src/guides/async_backing_guide.rs b/docs/sdk/src/guides/async_backing_guide.rs index f2f4dcabfd29..a9fda2c3aa8a 100644 --- a/docs/sdk/src/guides/async_backing_guide.rs +++ b/docs/sdk/src/guides/async_backing_guide.rs @@ -27,8 +27,8 @@ //! "scheduling_lookahead": 2 //! ``` //! -//!
`scheduling_lookahead` must be set to 2, otherwise parachain block times -//! will degrade to worse than with sync backing!
+//!
scheduling_lookahead must be set to 2, otherwise parachain +//! block times will degrade to worse than with sync backing!
//! //! ## Phase 1 - Update Parachain Runtime //! diff --git a/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs b/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs index bc4f36c271fe..939043b53529 100644 --- a/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs +++ b/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs @@ -1,7 +1,7 @@ //! # Enable elastic scaling MVP for a parachain //! //!
This guide assumes full familiarity with Asynchronous Backing and its -//! terminology, as defined in https://wiki.polkadot.network/docs/maintain-guides-async-backing. +//! terminology, as defined in the Polkadot Wiki. //! Furthermore, the parachain should have already been upgraded according to the guide.
//! //! ## Quick introduction to elastic scaling @@ -70,9 +70,10 @@ //! - Ensure enough coretime is assigned to the parachain. For maximum throughput the upper bound is //! 3 cores. //! -//!
Phase 1 is not needed if using the `polkadot-parachain` binary built -//! from the latest polkadot-sdk release! Simply pass the `--experimental-use-slot-based` parameter -//! to the command line and jump to Phase 2.
+//!
Phase 1 is not needed if using the polkadot-parachain binary +//! built from the latest polkadot-sdk release! Simply pass the +//! --experimental-use-slot-based parameter to the command line and jump to Phase +//! 2.
//! //! The following steps assume using the cumulus parachain template. //! diff --git a/docs/sdk/src/guides/enable_metadata_hash.rs b/docs/sdk/src/guides/enable_metadata_hash.rs index b9cbae853353..930afd4d8ff7 100644 --- a/docs/sdk/src/guides/enable_metadata_hash.rs +++ b/docs/sdk/src/guides/enable_metadata_hash.rs @@ -16,15 +16,15 @@ //! the metadata for one or more networks. The next problem is that the offline wallet/user can not //! trust the metadata to be correct. It is very important for the metadata to be correct or //! otherwise an attacker could change them in a way that the offline wallet decodes a transaction -//! in a different way than what it will be decoded to on chain. So, the user may signs an incorrect -//! transaction leading to unexpecting behavior. +//! in a different way than what it will be decoded to on chain. So, the user may sign an incorrect +//! transaction leading to unexpected behavior. //! //! The metadata hash verification circumvents the issues of the huge metadata and the need to trust //! some metadata blob to be correct. To generate a hash for the metadata, the metadata is chunked, //! these chunks are put into a merkle tree and then the root of this merkle tree is the "metadata //! hash". For a more technical explanation on how it works, see //! [RFC78](https://polkadot-fellows.github.io/RFCs/approved/0078-merkleized-metadata.html). At compile -//! time the metadata hash is generated and "backed" into the runtime. This makes it extremely cheap +//! time the metadata hash is generated and "baked" into the runtime. This makes it extremely cheap //! for the runtime to verify on chain that the metadata hash is correct. By having the runtime //! verify the hash on chain, the user also doesn't need to trust the offchain metadata. If the //! metadata hash doesn't match the on chain metadata hash the transaction will be rejected. The diff --git a/docs/sdk/src/guides/enable_pov_reclaim.rs b/docs/sdk/src/guides/enable_pov_reclaim.rs index 3c0c5fba2158..13b27d18956b 100644 --- a/docs/sdk/src/guides/enable_pov_reclaim.rs +++ b/docs/sdk/src/guides/enable_pov_reclaim.rs @@ -1,3 +1,5 @@ +//! # Enable storage weight reclaiming +//! //! This guide will teach you how to enable storage weight reclaiming for a parachain. The //! explanations in this guide assume a project structure similar to the one detailed in //! the [substrate documentation](crate::polkadot_sdk::substrate#anatomy-of-a-binary-crate). Full diff --git a/docs/sdk/src/guides/mod.rs b/docs/sdk/src/guides/mod.rs index 9384f4c82ab3..a7fd146ccdf3 100644 --- a/docs/sdk/src/guides/mod.rs +++ b/docs/sdk/src/guides/mod.rs @@ -15,21 +15,21 @@ /// Write your first simple pallet, learning the most most basic features of FRAME along the way. pub mod your_first_pallet; -/// Writing your first real [runtime](`crate::reference_docs::wasm_meta_protocol`), and successfully +/// Write your first real [runtime](`crate::reference_docs::wasm_meta_protocol`), /// compiling it to [WASM](crate::polkadot_sdk::substrate#wasm-build). pub mod your_first_runtime; -/// Running the given runtime with a node. No specific consensus mechanism is used at this stage. +// /// Running the given runtime with a node. No specific consensus mechanism is used at this stage. // TODO // pub mod your_first_node; -/// How to enhance a given runtime and node to be cumulus-enabled, run it as a parachain and connect -/// it to a relay-chain. +// /// How to enhance a given runtime and node to be cumulus-enabled, run it as a parachain +// /// and connect it to a relay-chain. // TODO // pub mod cumulus_enabled_parachain; -/// How to make a given runtime XCM-enabled, capable of sending messages (`Transact`) between itself -/// and the relay chain to which it is connected. +// /// How to make a given runtime XCM-enabled, capable of sending messages (`Transact`) between +// /// itself and the relay chain to which it is connected. // TODO // pub mod xcm_enabled_parachain; diff --git a/docs/sdk/src/guides/your_first_pallet/mod.rs b/docs/sdk/src/guides/your_first_pallet/mod.rs index da4624f5ac2b..3c74469e1768 100644 --- a/docs/sdk/src/guides/your_first_pallet/mod.rs +++ b/docs/sdk/src/guides/your_first_pallet/mod.rs @@ -1,6 +1,6 @@ //! # Currency Pallet //! -//! By the end of this guide, you will write a small FRAME pallet (see +//! By the end of this guide, you will have written a small FRAME pallet (see //! [`crate::polkadot_sdk::frame_runtime`]) that is capable of handling a simple crypto-currency. //! This pallet will: //! @@ -18,7 +18,7 @@ //! //! To get started, use one of the templates mentioned in [`crate::polkadot_sdk::templates`]. We //! recommend using the `polkadot-sdk-minimal-template`. You might need to change small parts of -//! this guide, namely the crate/package names, based on which tutorial you use. +//! this guide, namely the crate/package names, based on which template you use. //! //! > Be aware that you can read the entire source code backing this tutorial by clicking on the //! > [`source`](./mod.rs.html) button at the top right of the page. @@ -45,8 +45,8 @@ //! Consider the following as a "shell pallet". We continue building the rest of this pallet based //! on this template. //! -//! [`pallet::config`] and [`pallet::pallet`] are both mandatory parts of any pallet. Refer to the -//! documentation of each to get an overview of what they do. +//! [`pallet::config`] and [`pallet::pallet`](frame_support::pallet) are both mandatory parts of any +//! pallet. Refer to the documentation of each to get an overview of what they do. #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", shell_pallet)] //! //! All of the code that follows in this guide should live inside of the `mod pallet`. @@ -73,7 +73,7 @@ //! as normal `fn`s attached to `struct Pallet`. #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", impl_pallet)] //! -//! The logic of the functions is self-explanatory. Instead, we will focus on the FRAME-related +//! The logic of these functions is self-explanatory. Instead, we will focus on the FRAME-related //! details: //! //! - Where do `T::AccountId` and `T::RuntimeOrigin` come from? These are both defined in @@ -83,7 +83,7 @@ //! document ([`crate::reference_docs::frame_origin`]). For now, you should only know the //! signature of the function: it takes a generic `T::RuntimeOrigin` and returns a //! `Result`. So by the end of this function call, we know that this dispatchable -//! was signed by `who`. +//! was signed by `sender`. #![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", ensure_signed)] //! //! - Where does `mutate`, `get` and `insert` and other storage APIs come from? All of them are @@ -96,7 +96,7 @@ //! Which is more or less a normal Rust `Result`, with a custom [`frame::prelude::DispatchError`] as //! the `Err` variant. We won't cover this error in detail here, but importantly you should know //! that there is an `impl From<&'static string> for DispatchError` provided (see -//! [here](`frame::prelude::DispatchError#impl-From<%26'static+str>-for-DispatchError`)). Therefore, +//! [here](`frame::prelude::DispatchError#impl-From<%26str>-for-DispatchError`)). Therefore, //! we can use basic string literals as our error type and `.into()` them into `DispatchError`. //! //! - Why are all `get` and `mutate` functions returning an `Option`? This is the default behavior @@ -117,7 +117,7 @@ //! ergonomic. #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_better_checked)] //! -//! This is more or less all the logic that there is this basic currency pallet! +//! This is more or less all the logic that there is in this basic currency pallet! //! //! ### Your First (Test) Runtime //! diff --git a/docs/sdk/src/lib.rs b/docs/sdk/src/lib.rs index e211476d2514..bc0970c01f1f 100644 --- a/docs/sdk/src/lib.rs +++ b/docs/sdk/src/lib.rs @@ -13,8 +13,8 @@ //! We suggest the following reading sequence: //! //! - Start by learning about the the [`polkadot_sdk`], its structure and context. -//! - Then, head over the [`guides`]. This modules contains in-depth guides about the most important -//! user-journeys of the Polkadot SDK. +//! - Then, head over to the [`guides`]. This modules contains in-depth guides about the most +//! important user-journeys of the Polkadot SDK. //! - Whilst reading the guides, you might find back-links to [`reference_docs`]. //! - Finally, is the parent website of this crate that contains the //! list of further tools related to the Polkadot SDK. @@ -36,7 +36,7 @@ pub mod meta_contributing; /// In-depth guides about the most common components of the Polkadot SDK. They are slightly more -/// high level and broad than reference docs. +/// high level and broad than [`reference_docs`]. pub mod guides; /// An introduction to the Polkadot SDK. Read this module to learn about the structure of the SDK, /// the tools that are provided as a part of it, and to gain a high level understanding of each. diff --git a/docs/sdk/src/meta_contributing.rs b/docs/sdk/src/meta_contributing.rs index a029595254c8..e1297151b231 100644 --- a/docs/sdk/src/meta_contributing.rs +++ b/docs/sdk/src/meta_contributing.rs @@ -14,7 +14,7 @@ //! documentation up-to-date, as the overhead is reduced by making sure everything is in one //! repository, and everything being in `.rs` files. //! -//! > This is not say that a more visually appealing version of this crate (for example as an +//! > This is not to say that a more visually appealing version of this crate (for example as an //! > `md-book`) cannot exist, but it would be outside the scope of this crate. //! //! Moreover, we acknowledge that a major pain point has been not only outdated *concepts*, but also diff --git a/docs/sdk/src/polkadot_sdk/frame_runtime.rs b/docs/sdk/src/polkadot_sdk/frame_runtime.rs index 39255c8f51ad..8acf19f76413 100644 --- a/docs/sdk/src/polkadot_sdk/frame_runtime.rs +++ b/docs/sdk/src/polkadot_sdk/frame_runtime.rs @@ -54,7 +54,7 @@ //! //! ### Example //! -//! The following examples showcases a minimal pallet. +//! The following example showcases a minimal pallet. #![doc = docify::embed!("src/polkadot_sdk/frame_runtime.rs", pallet)] //! //! @@ -85,7 +85,7 @@ //! [`crate::reference_docs::wasm_meta_protocol`]). Notable examples are: //! //! * writing a runtime in pure Rust, as done in [this template](https://github.com/JoshOrndorff/frameless-node-template). -//! * writing a runtime in AssemblyScript,as explored in [this project](https://github.com/LimeChain/subsembly). +//! * writing a runtime in AssemblyScript, as explored in [this project](https://github.com/LimeChain/subsembly). /// A FRAME based pallet. This `mod` is the entry point for everything else. All /// `#[pallet::xxx]` macros must be defined in this `mod`. Although, frame also provides an @@ -119,11 +119,11 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(PhantomData); - /// The events tha this pallet can emit. + /// The events that this pallet can emit. #[pallet::event] pub enum Event {} - /// A storage item that this pallet contains. This will be part of the state root trie/root + /// A storage item that this pallet contains. This will be part of the state root trie /// of the blockchain. #[pallet::storage] pub type Value = StorageValue; diff --git a/docs/sdk/src/polkadot_sdk/mod.rs b/docs/sdk/src/polkadot_sdk/mod.rs index 124d391421b9..32cad72fba7e 100644 --- a/docs/sdk/src/polkadot_sdk/mod.rs +++ b/docs/sdk/src/polkadot_sdk/mod.rs @@ -12,7 +12,7 @@ //! //! [![RFCs](https://img.shields.io/badge/fellowship-RFCs-e6007a?logo=polkadot)](https://github.com/polkadot-fellows/rfcs) //! [![Runtime](https://img.shields.io/badge/fellowship-runtimes-e6007a?logo=polkadot)](https://github.com/polkadot-fellows/runtimes) -//! [![Manifesto](https://img.shields.io/badge/fellowship-manifesto-e6007a?logo=polkadot)](https://github.com/polkadot-fellows/manifesto) +//! [![Manifesto](https://img.shields.io/badge/fellowship-manifesto-e6007a?logo=polkadot)](https://github.com/polkadot-fellows/manifesto/blob/main/manifesto.pdf) //! //! ## Getting Started //! @@ -34,7 +34,7 @@ //! Repo](https://img.shields.io/badge/github-substrate-2324CC85)](https://github.com/paritytech/polkadot-sdk/blob/master/substrate) //! //! [`substrate`] is the base blockchain framework used to power the Polkadot SDK. It is a full -//! toolkit to create sovereign blockchains, including but not limited to those who connect to +//! toolkit to create sovereign blockchains, including but not limited to those which connect to //! Polkadot as parachains. //! //! #### FRAME @@ -82,7 +82,7 @@ //! //! A Substrate-based chain is a blockchain composed of a runtime and a node. As noted above, the //! runtime is the application logic of the blockchain, and the node is everything else. -//! See [`crate::reference_docs::wasm_meta_protocol`] for an in-depth explanation of this. The +//! See [`reference_docs::wasm_meta_protocol`] for an in-depth explanation of this. The //! former is built with [`frame`], and the latter is built with rest of Substrate. //! //! > You can think of a Substrate-based chain as a white-labeled blockchain. @@ -103,8 +103,8 @@ //! //! ## Trophy Section: Notable Downstream Projects //! -//! A list of projects and tools in the blockchain ecosystem that one way or another parts of the -//! Polkadot SDK: +//! A list of projects and tools in the blockchain ecosystem that one way or another use parts of +//! the Polkadot SDK: //! //! * [Polygon's spin-off, Avail](https://github.com/availproject/avail) //! * [Cardano Partner Chains](https://iohk.io/en/blog/posts/2023/11/03/partner-chains-are-coming-to-cardano/) @@ -116,7 +116,7 @@ //! [`polkadot`]: crate::polkadot_sdk::polkadot //! [`xcm`]: crate::polkadot_sdk::xcm -/// Lean about Cumulus, the framework that transforms [`substrate`]-based chains into +/// Learn about Cumulus, the framework that transforms [`substrate`]-based chains into /// [`polkadot`]-enabled parachains. pub mod cumulus; /// Learn about FRAME, the framework used to build Substrate runtimes. diff --git a/docs/sdk/src/polkadot_sdk/polkadot.rs b/docs/sdk/src/polkadot_sdk/polkadot.rs index e2dcca4dc7df..69532f323b9d 100644 --- a/docs/sdk/src/polkadot_sdk/polkadot.rs +++ b/docs/sdk/src/polkadot_sdk/polkadot.rs @@ -8,18 +8,18 @@ //! - [Polkadot Parachains](https://parachains.info/) //! - [Polkadot (multi-chain) Explorer: Subscan](https://subscan.io/) //! - Polkadot Fellowship -//! - [Manifesto](https://github.com/polkadot-fellows/manifesto) +//! - [Manifesto](https://github.com/polkadot-fellows/manifesto/blob/main/manifesto.pdf) //! - [Runtimes](https://github.com/polkadot-fellows/runtimes) //! - [RFCs](https://github.com/polkadot-fellows/rfcs) //! - [Dashboard](https://polkadot-fellows.github.io/dashboard/) -//! - [Polkadot Specs](spec.polkadot.network) +//! - [Polkadot Specs](http://spec.polkadot.network) //! - [The Polkadot Parachain Host Implementers' Guide](https://paritytech.github.io/polkadot-sdk/book/) //! - [Whitepaper](https://www.polkadot.network/whitepaper/) //! - [JAM Graypaper](https://graypaper.com) //! //! ## Alternative Node Implementations 🌈 //! -//! - [Smoldot](https://crates.io/crates/smoldot-light). Polkadot light node/client. +//! - [Smoldot](https://docs.rs/crate/smoldot-light/latest). Polkadot light node/client. //! - [KAGOME](https://github.com/qdrvm/kagome). C++ implementation of the Polkadot host. //! - [Gossamer](https://github.com/ChainSafe/gossamer). Golang implementation of the Polkadot host. //! @@ -45,7 +45,7 @@ //! > their execution and governance sovereignty. These chains are called "Parachains". //! //! * Shared Security: The idea of shared economic security sits at the core of Polkadot. Polkadot -//! enables different parachains* to pool their economic security from Polkadot (i.e. "*Relay +//! enables different parachains to pool their economic security from Polkadot (i.e. "*Relay //! Chain*"). //! * (heterogenous) Sharded Execution: Yet, each parachain is free to have its own execution logic //! (runtime), which also encompasses governance and sovereignty. Moreover, Polkadot ensures the @@ -80,7 +80,7 @@ //! Within the scope of Polkadot 1.x, two main scheduling ways have been considered: //! //! * Long term Parachains, obtained through locking a sum of DOT in an auction system. -//! * on-demand Parachains, purchased through paying DOT to the relay-chain whenever needed. +//! * On-demand Parachains, purchased through paying DOT to the relay-chain whenever needed. //! //! ### The Future //! diff --git a/docs/sdk/src/polkadot_sdk/substrate.rs b/docs/sdk/src/polkadot_sdk/substrate.rs index 69d74d86db1b..56b89f8c9c2a 100644 --- a/docs/sdk/src/polkadot_sdk/substrate.rs +++ b/docs/sdk/src/polkadot_sdk/substrate.rs @@ -11,9 +11,9 @@ //! 1. Society and technology evolves. //! 2. Humans are fallible. //! -//! This, makes the task of designing a correct, safe and long-lasting blockchain system hard. +//! This makes the task of designing a correct, safe and long-lasting blockchain system hard. //! -//! Nonetheless, in strive towards achieve this goal, Substrate embraces the following: +//! Nonetheless, in strive towards achieving this goal, Substrate embraces the following: //! //! 1. Use of **Rust** as a modern and safe programming language, which limits human error through //! various means, most notably memory and type safety. @@ -27,7 +27,7 @@ //! blob. //! //! In essence, the meta-protocol of all Substrate based chains is the "Runtime as WASM blob" -//! accord. This enables the Runtime to become inherently upgradeable, crucially without forks. The +//! accord. This enables the Runtime to become inherently upgradeable, crucially without [forks](https://en.wikipedia.org/wiki/Fork_(blockchain)). The //! upgrade is merely a matter of the WASM blob being changed in the state, which is, in principle, //! same as updating an account's balance. Learn more about this in detail in //! [`crate::reference_docs::wasm_meta_protocol`]. @@ -63,9 +63,9 @@ //! categories: //! //! * `sc-*` (short for *Substrate-client*) crates, located under `./client` folder. These are all -//! the crates that lead to the node software. Notable examples [`sc_network`], various consensus -//! crates, RPC ([`sc_rpc_api`]) and database ([`sc_client_db`]), all of which are expected to -//! reside in the node side. +//! the crates that lead to the node software. Notable examples are [`sc_network`], various +//! consensus crates, RPC ([`sc_rpc_api`]) and database ([`sc_client_db`]), all of which are +//! expected to reside in the node side. //! * `sp-*` (short for *substrate-primitives*) crates, located under `./primitives` folder. These //! are crates that facilitate both the node and the runtime, but are not opinionated about what //! framework is using for building the runtime. Notable examples are [`sp_api`] and [`sp_io`], @@ -86,7 +86,9 @@ //! //! Substrate-based runtimes use [`substrate_wasm_builder`] in their `build.rs` to automatically //! build their WASM files as a part of normal build command (e.g. `cargo build`). Once built, the -//! wasm file is placed in `./target/{debug|release}/wbuild/{runtime_name}.wasm`. +//! wasm file is placed in `./target/{debug|release}/wbuild/{runtime_name}/{runtime_name}.wasm`. +//! +//! In order to ensure that the WASM build is **deterministic**, the [Substrate Runtime Toolbox (srtool)](https://github.com/paritytech/srtool) can be used. //! //! ### Binaries //! diff --git a/docs/sdk/src/polkadot_sdk/xcm.rs b/docs/sdk/src/polkadot_sdk/xcm.rs index 58f540686424..20841b0b55b9 100644 --- a/docs/sdk/src/polkadot_sdk/xcm.rs +++ b/docs/sdk/src/polkadot_sdk/xcm.rs @@ -5,7 +5,7 @@ //! //! ## Overview //! -//! XCM is a standard, whose specification lives in the [xcm format repo](https://github.com/paritytech/xcm-format). +//! XCM is a standard, specification of which lives in the [xcm format repo](https://github.com/paritytech/xcm-format). //! It's agnostic both in programming language and blockchain platform, which means it could be used //! in Rust in Polkadot, or in Go or C++ in any other platform like Cosmos or Ethereum. //! @@ -34,13 +34,12 @@ //! but will be moved to its own repo in the future. //! //! Its main components are: -//! - `src`: the definition of the basic types and instructions -//! - [`xcm-executor`](https://paritytech.github.io/polkadot-sdk/master/staging_xcm_executor/struct.XcmExecutor.html): -//! an implementation of the virtual machine to execute instructions -//! - `pallet-xcm`: A FRAME pallet for interacting with the executor -//! - `xcm-builder`: a collection of types to configure the executor -//! - `xcm-simulator`: a playground for trying out different XCM programs and executor -//! configurations +//! - [`xcm`](::xcm): The definition of the basic types and instructions. +//! - [`xcm_executor`]: An implementation of the virtual machine to execute instructions. +//! - [`pallet_xcm`]: A FRAME pallet for interacting with the executor. +//! - [`xcm_builder`]: A collection of types to configure the executor. +//! - [`xcm_simulator`]: A playground for trying out different XCM programs and executor +//! configurations. //! //! ## Example //! diff --git a/docs/sdk/src/reference_docs/cli.rs b/docs/sdk/src/reference_docs/cli.rs index 5779e0f8d049..b9cdbd60e959 100644 --- a/docs/sdk/src/reference_docs/cli.rs +++ b/docs/sdk/src/reference_docs/cli.rs @@ -45,7 +45,7 @@ //! - `--chain=customSpec.json`: Uses the custom chain specification as input. //! - `--disable-default-bootnode`: Disables the default bootnodes in the node template. //! - `--raw`: Converts the chain specification into a raw format with encoded storage keys. -//! - `> customSpecRaw.json`: Outputs to customSpecRaw.json. +//! - `> customSpecRaw.json`: Outputs to `customSpecRaw.json`. //! //! #### Starting the First Node in a Private Network //! ```bash diff --git a/docs/sdk/src/reference_docs/custom_runtime_api_rpc.rs b/docs/sdk/src/reference_docs/custom_runtime_api_rpc.rs index 83a70606cb8d..083ed9f77e10 100644 --- a/docs/sdk/src/reference_docs/custom_runtime_api_rpc.rs +++ b/docs/sdk/src/reference_docs/custom_runtime_api_rpc.rs @@ -1,7 +1,7 @@ //! # Custom RPC do's and don'ts //! -//! **TLDR:** don't create new custom RPCs. Instead, rely on custom Runtime APIs, combined with -//! `state_call` +//! **TLDR:** Don't create new custom RPCs. Instead, rely on custom Runtime APIs, combined with +//! `state_call`. //! //! ## Background //! @@ -20,9 +20,9 @@ //! - To upgrade or add a new RPC logic, the RPC node has to be upgraded. This can cause significant //! trouble when the RPC infrastructure is decentralized as we will need to coordinate multiple //! parties to upgrade the RPC nodes. -//! - A lot of boilerplate code are required to add custom RPC. -//! - It prevents the dApp to use a light client or alternative client. -//! - It makes ecosystem tooling integration much more complicated. For example, the dApp will not +//! - A lot of boilerplate code is required to add custom RPC. +//! - It prevents dApps from using a light client or an alternative client. +//! - It makes ecosystem tooling integration much more complicated. For example, dApps will not //! be able to use [Chopsticks](https://github.com/AcalaNetwork/chopsticks) for testing as //! Chopsticks will not have the custom RPC implementation. //! - Poorly implemented custom RPC can be a DoS vector. @@ -50,7 +50,7 @@ //! //! ## Create a new Runtime API //! -//! For example, let's take a look a the process through which the account nonce can be queried +//! For example, let's take a look at the process through which the account nonce can be queried //! through an RPC. First, a new runtime-api needs to be declared: #![doc = docify::embed!("../../substrate/frame/system/rpc/runtime-api/src/lib.rs", AccountNonceApi)] //! diff --git a/docs/sdk/src/reference_docs/defensive_programming.rs b/docs/sdk/src/reference_docs/defensive_programming.rs index 9828e1b50918..82d624b01d97 100644 --- a/docs/sdk/src/reference_docs/defensive_programming.rs +++ b/docs/sdk/src/reference_docs/defensive_programming.rs @@ -16,7 +16,7 @@ //! [Defensive programming](https://en.wikipedia.org/wiki/Defensive_programming) is a design paradigm that enables a program to continue //! running despite unexpected behavior, input, or events that may arise in runtime. //! Usually, unforeseen circumstances may cause the program to stop or, in the Rust context, -//! panic!. Defensive practices allow for these circumstances to be accounted for ahead of time +//! `panic!`. Defensive practices allow for these circumstances to be accounted for ahead of time //! and for them to be handled gracefully, which is in line with the intended fault-tolerant and //! deterministic nature of blockchains. //! @@ -71,7 +71,7 @@ //! ### Defensive Traits //! //! The [`Defensive`](frame::traits::Defensive) trait provides a number of functions, all of which -//! provide an alternative to 'vanilla' Rust functions, e.g.,: +//! provide an alternative to 'vanilla' Rust functions, e.g.: //! //! - [`defensive_unwrap_or()`](frame::traits::Defensive::defensive_unwrap_or) instead of //! `unwrap_or()` @@ -127,7 +127,7 @@ //! - [Fixed point types](sp_arithmetic::fixed_point) and their associated usage can be found here. //! - [PerThing](sp_arithmetic::per_things) and its associated types can be found here. //! -//! Using floating point number types (i.e., f32. f64) in the runtime should be avoided, as a single non-deterministic result could cause chaos for blockchain consensus along with the issues above. For more on the specifics of the peculiarities of floating point calculations, [watch this video by the Computerphile](https://www.youtube.com/watch?v=PZRI1IfStY0). +//! Using floating point number types (i.e. f32, f64) in the runtime should be avoided, as a single non-deterministic result could cause chaos for blockchain consensus along with the issues above. For more on the specifics of the peculiarities of floating point calculations, [watch this video by the Computerphile](https://www.youtube.com/watch?v=PZRI1IfStY0). //! //! The following methods demonstrate different ways to handle numbers natively in Rust safely, //! without fear of panic or unexpected behavior from wrapping. diff --git a/docs/sdk/src/reference_docs/development_environment_advice.rs b/docs/sdk/src/reference_docs/development_environment_advice.rs index 9ba95dfa0329..a5f38bb280de 100644 --- a/docs/sdk/src/reference_docs/development_environment_advice.rs +++ b/docs/sdk/src/reference_docs/development_environment_advice.rs @@ -15,8 +15,9 @@ //! ```json //! { //! // Use a separate target dir for Rust Analyzer. Helpful if you want to use Rust -//! // Analyzer and cargo on the command line at the same time. -//! "rust-analyzer.rust.analyzerTargetDir": "target/vscode-rust-analyzer", +//! // Analyzer and cargo on the command line at the same time, +//! // at the expense of duplicating build artifacts. +//! "rust-analyzer.cargo.targetDir": "target/vscode-rust-analyzer", //! // Improve stability //! "rust-analyzer.server.extraEnv": { //! "CHALK_OVERFLOW_DEPTH": "100000000", @@ -145,7 +146,7 @@ //! } //! ``` //! -//! //! and the same in Lua for `neovim/nvim-lspconfig`: +//! and the same in Lua for `neovim/nvim-lspconfig`: //! //! ```lua //! ["rust-analyzer"] = { diff --git a/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs b/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs index db77547a4bf0..cf9e58791492 100644 --- a/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs +++ b/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs @@ -14,10 +14,10 @@ //! - improve documentation of `#[weight = ..]` and `#[pallet::weight(..)]`. All syntax variation //! should be covered. //! -//! on FRAME benchmarking machinery: +//! On FRAME benchmarking machinery: //! -//! - component analysis, why everything must be linear. -//! - how to write benchmarks, how you must think of worst case. -//! - how to run benchmarks. +//! - Component analysis, why everything must be linear. +//! - How to write benchmarks, how you must think of worst case. +//! - How to run benchmarks. //! //! - diff --git a/docs/sdk/src/reference_docs/frame_logging.rs b/docs/sdk/src/reference_docs/frame_logging.rs index 301fa7ef83f8..1b03c6a2e35b 100644 --- a/docs/sdk/src/reference_docs/frame_logging.rs +++ b/docs/sdk/src/reference_docs/frame_logging.rs @@ -34,9 +34,9 @@ //! //! ## Using `log` //! -//! First, ensure you are familiar with the `log` crate. In short, each log statement has: +//! First, ensure you are familiar with the [`log`] crate. In short, each log statement has: //! -//! 1. `log-level`, signifying how important it is +//! 1. `log-level`, signifying how important it is. //! 2. `log-target`, signifying to which component it belongs. //! //! Add log statements to your pallet as such: diff --git a/docs/sdk/src/reference_docs/frame_offchain_workers.rs b/docs/sdk/src/reference_docs/frame_offchain_workers.rs index 1ec9212e2306..b0aaf1789d4c 100644 --- a/docs/sdk/src/reference_docs/frame_offchain_workers.rs +++ b/docs/sdk/src/reference_docs/frame_offchain_workers.rs @@ -88,7 +88,7 @@ //! //! They can both read from the state, and have no means of updating the state, other than the route //! of submitting an extrinsic to the chain. Therefore, it is worth thinking twice before embedding -//! a logic as a part of Substrate's offchain worker API. Does it have to be there? can it not be a +//! a logic as a part of Substrate's offchain worker API. Does it have to be there? Can it not be a //! simple, actual offchain application that lives outside of the chain's WASM blob? //! //! Some of the reasons why you might want to do the opposite, and actually embed an offchain worker diff --git a/docs/sdk/src/reference_docs/frame_pallet_coupling.rs b/docs/sdk/src/reference_docs/frame_pallet_coupling.rs index be464bbbf835..a22137f979dc 100644 --- a/docs/sdk/src/reference_docs/frame_pallet_coupling.rs +++ b/docs/sdk/src/reference_docs/frame_pallet_coupling.rs @@ -30,8 +30,8 @@ //! //! There are generally two ways to achieve this: //! -//! 1. Tight coupling pallets -//! 2. Loose coupling pallets +//! 1. Tight coupling pallets. +//! 2. Loose coupling pallets. //! //! To explain the difference between the two, consider two pallets, `A` and `B`. In both cases, `A` //! wants to use some functionality exposed by `B`. @@ -74,8 +74,8 @@ //! pallet makes its own `trait Config` be bounded by another pallet's `trait Config`, it is //! expressing two things: //! -//! 1. that it can only exist in a runtime if the other pallet is also present. -//! 2. that it can use the other pallet's functionality. +//! 1. That it can only exist in a runtime if the other pallet is also present. +//! 2. That it can use the other pallet's functionality. //! //! `pallet-foo`'s `Config` would then look like: #![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", tight_config)] @@ -110,7 +110,7 @@ //! Crucially, when using loose coupling, we gain the flexibility of providing different //! implementations of `AuthorProvider`, such that different users of a `pallet-foo` can use //! different ones, without any code change being needed. For example, in the code snippets of this -//! module, you can fund [`OtherAuthorProvider`] which is an alternative implementation of +//! module, you can find [`OtherAuthorProvider`], which is an alternative implementation of //! [`AuthorProvider`]. #![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", other_author_provider)] //! @@ -135,8 +135,8 @@ //! general, it is easier to argue about multiple pallet if they only communicate together via a //! known trait, rather than having access to all of each others public items, such as storage and //! dispatchables. -//! * If a group of pallets are meant to work together, and but are not foreseen to be generalized, -//! or used by others, consider tightly coupling pallets, *if it simplifies the development*. +//! * If a group of pallets is meant to work together, but is not foreseen to be generalized, or +//! used by others, consider tightly coupling pallets, *if it simplifies the development*. //! * If a pallet needs a functionality provided by another pallet, but multiple implementations can //! be foreseen, consider loosely coupling pallets. //! diff --git a/docs/sdk/src/reference_docs/frame_runtime_types.rs b/docs/sdk/src/reference_docs/frame_runtime_types.rs index 1eed9857a1d5..e99106ade878 100644 --- a/docs/sdk/src/reference_docs/frame_runtime_types.rs +++ b/docs/sdk/src/reference_docs/frame_runtime_types.rs @@ -36,7 +36,7 @@ #![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_bar)] //! //! Let's explore how each of these affect the [`RuntimeCall`], [`RuntimeOrigin`] and -//! [`RuntimeGenesisConfig`] generated in [`runtime`] by respectively. +//! [`RuntimeGenesisConfig`] generated in [`runtime`] respectively. //! //! As observed, [`RuntimeCall`] has 3 variants, one for each pallet and one for `frame_system`. If //! you explore further, you will soon realize that each variant is merely a pointer to the `Call` diff --git a/docs/sdk/src/reference_docs/frame_runtime_upgrades_and_migrations.rs b/docs/sdk/src/reference_docs/frame_runtime_upgrades_and_migrations.rs index f9a69b892a31..065cbee25709 100644 --- a/docs/sdk/src/reference_docs/frame_runtime_upgrades_and_migrations.rs +++ b/docs/sdk/src/reference_docs/frame_runtime_upgrades_and_migrations.rs @@ -2,8 +2,8 @@ //! //! At their core, blockchain logic consists of //! -//! 1. on-chain state and -//! 2. a state transition function +//! 1. on-chain state, +//! 2. a state transition function. //! //! In Substrate-based blockchains, state transition functions are referred to as //! [runtimes](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/blockchain_state_machines/index.html). @@ -43,9 +43,9 @@ //! for example when the encoding of a storage item is changed. However, they can also execute //! arbitrary logic such as: //! -//! - Calling arbitrary pallet methods -//! - Mutating arbitrary on-chain state -//! - Cleaning up some old storage items that are no longer needed +//! - Calling arbitrary pallet methods. +//! - Mutating arbitrary on-chain state. +//! - Cleaning up some old storage items that are no longer needed. //! //! ## Single Block Migrations //! @@ -88,9 +88,9 @@ //! //! Prior to deploying migrations, it is critical to perform additional checks to ensure that when //! run in our real runtime they will not brick the chain due to: -//! - Panicking -//! - Touching too many storage keys and resulting in an excessively large PoV -//! - Taking too long to execute +//! - Panicking. +//! - Touching too many storage keys and resulting in an excessively large PoV. +//! - Taking too long to execute. //! //! [`try-runtime-cli`](https://github.com/paritytech/try-runtime-cli) has a sub-command //! [`on-runtime-upgrade`](https://paritytech.github.io/try-runtime-cli/try_runtime_core/commands/enum.Action.html#variant.OnRuntimeUpgrade) @@ -129,7 +129,9 @@ //! //! Suitable for migrations which could use arbitrary amounts of block weight. //! -//! TODO: Link to multi block migration example/s once PR is merged (). +//! See the +//! [multi-block-migrations example](https://github.com/paritytech/polkadot-sdk/tree/0d7d2177807ec6b3094f4491a45b0bc0d74d3c8b/substrate/frame/examples/multi-block-migrations) +//! for reference. //! //! [`OnRuntimeUpgrade`]: frame_support::traits::OnRuntimeUpgrade //! [`StorageVersion`]: frame_support::traits::StorageVersion diff --git a/docs/sdk/src/reference_docs/frame_storage_derives.rs b/docs/sdk/src/reference_docs/frame_storage_derives.rs index 3d9edef398a0..4fbfb4a5cc83 100644 --- a/docs/sdk/src/reference_docs/frame_storage_derives.rs +++ b/docs/sdk/src/reference_docs/frame_storage_derives.rs @@ -1,6 +1,9 @@ -//!
-//! In all examples, a few lines of boilerplate have been hidden from each snippet for conciseness. -//!
+//! # Frame storage derives +//! +//! > **Note:** +//! > +//! > In all examples, a few lines of boilerplate have been hidden from each snippet for +//! > conciseness. //! //! Let's begin by starting to store a `NewType` in a storage item: //! @@ -134,7 +137,7 @@ //! - [`frame::prelude::PartialOrdNoBound`] //! - [`frame::prelude::OrdNoBound`] //! -//! The above traits are almost certainly needed for your tests: To print your type, assert equality +//! The above traits are almost certainly needed for your tests - to print your type, assert equality //! or clone it. //! //! We can fix the following example by using [`frame::prelude::DefaultNoBound`]. diff --git a/docs/sdk/src/reference_docs/frame_system_accounts.rs b/docs/sdk/src/reference_docs/frame_system_accounts.rs index 523fe7043084..c93e1196ea7e 100644 --- a/docs/sdk/src/reference_docs/frame_system_accounts.rs +++ b/docs/sdk/src/reference_docs/frame_system_accounts.rs @@ -1,6 +1,6 @@ //! # FRAME Accounts //! -//! //! 🚧 Work In Progress 🚧 +//! 🚧 Work In Progress 🚧 //! //! How `frame_system` handles accountIds. Nonce. Consumers and Providers, reference counting. diff --git a/docs/sdk/src/reference_docs/frame_tokens.rs b/docs/sdk/src/reference_docs/frame_tokens.rs index 57b493fafa59..a76e524ceb85 100644 --- a/docs/sdk/src/reference_docs/frame_tokens.rs +++ b/docs/sdk/src/reference_docs/frame_tokens.rs @@ -22,11 +22,11 @@ //! //! On completion of reading this doc, you should have a good understanding of: //! - The distinction between token traits and trait implementations in FRAME, and why this -//! distinction is helpful -//! - Token-related traits available in FRAME -//! - Token-related trait implementations in FRAME -//! - How to choose the right trait or trait implementation for your use case -//! - Where to go next +//! distinction is helpful. +//! - Token-related traits available in FRAME. +//! - Token-related trait implementations in FRAME. +//! - How to choose the right trait or trait implementation for your use case. +//! - Where to go next. //! //! ## Getting Started //! @@ -56,9 +56,16 @@ //! //! **Trait implementations** are concrete implementations of these traits. For example, one of the //! many traits [`pallet_balances`] implements is -//! [`fungible::Inspect`](`frame_support::traits::fungible::Inspect`)*. It provides the concrete way -//! of inspecting the total issuance, balance of accounts, etc. There can be many implementations of -//! the same traits. +//! [`fungible::Inspect`](`frame_support::traits::fungible::Inspect`)[^1]. It provides the concrete +//! way of inspecting the total issuance, balance of accounts, etc. There can be many +//! implementations of the same traits. +//! +//! [^1]: Rust Advanced Tip: The knowledge that [`pallet_balances`] implements +//! [`fungible::Inspect`](`frame_support::traits::fungible::Inspect`) is not some arcane knowledge +//! that you have to know by heart or memorize. One can simply look at the list of the implementors +//! of any trait in the Rust Doc to find all implementors (e.g. +//! [Mutate trait implementors](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/tokens/fungible/trait.Mutate.html#implementors)), +//! or use the `rust-analyzer`'s `Implementations` action. //! //! The distinction between traits and trait implementations is helpful because it allows pallets //! and other logic to be generic over their dependencies, avoiding tight coupling. @@ -68,10 +75,10 @@ //! pallet may use [`pallet_balances`] in a tightly coupled manner, directly calling methods //! on the pallet to reserve and unreserve deposits. This approach works well, //! until someone has a use case requiring that an asset from a different pallet such as -//! [`pallet_assets`] is used for the deposit. Rather than tightly couple [`pallet_preimage`] to -//! [`pallet_balances`], [`pallet_assets`], and every other token-handling pallet a user -//! could possibly specify, [`pallet_preimage`] does not specify a concrete pallet as a dependency -//! but instead accepts any dependency which implements the +//! [`pallet_assets`] is used for the deposit. Rather than tightly coupling [`pallet_preimage`] to +//! [`pallet_balances`], [`pallet_assets`], and every other token-handling pallet, a user +//! could possibly specify that [`pallet_preimage`] does not specify a concrete pallet as a +//! dependency, but instead accepts any dependency which implements the //! [`currency::ReservableCurrency`](`frame_support::traits::tokens::currency::ReservableCurrency`) //! trait, namely via its [`Config::Currency`](`pallet_preimage::pallet::Config::Currency`) //! associated type. This allows [`pallet_preimage`] to support any arbitrary pallet implementing @@ -81,15 +88,6 @@ //! Read more about coupling, and the benefits of loose coupling //! [here](crate::reference_docs::frame_pallet_coupling). //! -//! ##### *Rust Advanced Tip -//! -//! The knowledge that [`pallet_balances`] implements -//! [`fungible::Inspect`](`frame_support::traits::fungible::Inspect`) is not some arcane knowledge -//! that you have to know by heart or memorize. One can simply look at the list of the implementors -//! of any trait in the Rust Doc to find all implementors (e.g. -//! ), -//! or use the `rust-analyzer` `Implementations` action. -//! //! ## Fungible Token Traits in FRAME //! //! The [`fungible`](`frame_support::traits::fungible`) crate contains the latest set of FRAME diff --git a/docs/sdk/src/reference_docs/metadata.rs b/docs/sdk/src/reference_docs/metadata.rs index 96f92ac0c412..485614088140 100644 --- a/docs/sdk/src/reference_docs/metadata.rs +++ b/docs/sdk/src/reference_docs/metadata.rs @@ -21,5 +21,5 @@ //! //! A few noteworthy tools that inspect the (FRAME-based) metadata of a chain: //! -//! -//! +//! - +//! - diff --git a/docs/sdk/src/reference_docs/mod.rs b/docs/sdk/src/reference_docs/mod.rs index c69c79365427..7f2edb08d46e 100644 --- a/docs/sdk/src/reference_docs/mod.rs +++ b/docs/sdk/src/reference_docs/mod.rs @@ -7,14 +7,15 @@ //! //! ## What is a "reference document"? //! -//! First, see [why we use rust-docs for everything](crate#why-rust-docs) and our documentation -//! [principles](crate#principles). We acknowledge that as much of the crucial information should be -//! embedded in the low level rust-docs. Then, high level scenarios should be covered in -//! [`crate::guides`]. Finally, we acknowledge that there is a category of information that is: +//! First, see [why we use rust-docs for everything](crate::meta_contributing#why-rust-docs) and our +//! documentation [principles](crate::meta_contributing#principles). We acknowledge that as much of +//! the crucial information should be embedded in the low level rust-docs. Then, high level +//! scenarios should be covered in [`crate::guides`]. Finally, we acknowledge that there is a +//! category of information that is: //! -//! 1. crucial to know. -//! 2. is too high level to be in the rust-doc of any one `type`, `trait` or `fn`. -//! 3. is too low level to be encompassed in a [`crate::guides`]. +//! 1. Crucial to know. +//! 2. Is too high level to be in the rust-doc of any one `type`, `trait` or `fn`. +//! 3. Is too low level to be encompassed in a [`crate::guides`]. //! //! We call this class of documents "reference documents". Our goal should be to minimize the number //! of "reference" docs, as they incur maintenance burden. @@ -52,8 +53,8 @@ pub mod frame_storage_derives; /// Learn about how to write safe and defensive code in your FRAME runtime. pub mod defensive_programming; -/// Learn about composite enums and other runtime level types, such as "RuntimeEvent" and -/// "RuntimeCall". +/// Learn about composite enums and other runtime level types, such as `RuntimeEvent` and +/// `RuntimeCall`. pub mod frame_runtime_types; /// Learn about how to make a pallet/runtime that is fee-less and instead uses another mechanism to diff --git a/docs/sdk/src/reference_docs/runtime_vs_smart_contract.rs b/docs/sdk/src/reference_docs/runtime_vs_smart_contract.rs index 379b0c11b2ad..4a0ba3ca48f5 100644 --- a/docs/sdk/src/reference_docs/runtime_vs_smart_contract.rs +++ b/docs/sdk/src/reference_docs/runtime_vs_smart_contract.rs @@ -32,21 +32,14 @@ //! //! ## Comparative Table //! -//! | Aspect | Runtime -//! | Smart Contracts | +//! | Aspect | Runtime | Smart Contracts | //! |-----------------------|-------------------------------------------------------------------------|----------------------------------------------------------------------| -//! | **Design Philosophy** | Core logic of a blockchain, allowing broad and deep customization. -//! | Designed for DApps deployed on the blockchain runtime.| | **Development Complexity** | Requires in-depth knowledge of Rust and Substrate. Suitable for complex blockchain architectures. | Easier to develop with knowledge of Smart Contract languages like Solidity or [ink!](https://use.ink/). | -//! | **Upgradeability and Flexibility** | Offers comprehensive upgradeability with migration logic -//! and on-chain governance, allowing modifications to the entire blockchain logic without hard -//! forks. | Less flexible in upgrade migrations but offers more straightforward deployment and -//! iteration. | | **Performance and Efficiency** | More efficient, optimized for specific needs of -//! the blockchain. | Can be less efficient due to its generic nature (e.g. the overhead of a -//! virtual machine). | | **Security Considerations** | Security flaws can affect the entire -//! blockchain. | Security risks usually localized to the individual -//! contract. | | **Weighing and Metering** | Operations can be weighed, allowing for precise -//! benchmarking. | Execution is metered, allowing for measurement of resource -//! consumption. | +//! | **Design Philosophy** | Core logic of a blockchain, allowing broad and deep customization. | Designed for DApps deployed on the blockchain runtime. | +//! | **Development Complexity** | Requires in-depth knowledge of Rust and Substrate. Suitable for complex blockchain architectures. | Easier to develop with knowledge of Smart Contract languages like Solidity or [ink!](https://use.ink/). | +//! | **Upgradeability and Flexibility** | Offers comprehensive upgradeability with migration logic and on-chain governance, allowing modifications to the entire blockchain logic without hard forks. | Less flexible in upgrade migrations but offers more straightforward deployment and iteration. | +//! | **Performance and Efficiency** | More efficient, optimized for specific needs of the blockchain. | Can be less efficient due to its generic nature (e.g. the overhead of a virtual machine). | +//! | **Security Considerations** | Security flaws can affect the entire blockchain. | Security risks usually localized to the individual contract. | +//! | **Weighing and Metering** | Operations can be weighed, allowing for precise benchmarking. | Execution is metered, allowing for measurement of resource consumption. | //! //! We will now explore these differences in more detail. //! diff --git a/docs/sdk/src/reference_docs/trait_based_programming.rs b/docs/sdk/src/reference_docs/trait_based_programming.rs index ace313880707..90b38f63a580 100644 --- a/docs/sdk/src/reference_docs/trait_based_programming.rs +++ b/docs/sdk/src/reference_docs/trait_based_programming.rs @@ -196,7 +196,7 @@ mod with_system { mod fully_qualified { use super::with_system::*; - // Simple of using fully qualified syntax. + // Example of using fully qualified syntax. type AccountIdOf = ::AccountId; } diff --git a/docs/sdk/src/reference_docs/umbrella_crate.rs b/docs/sdk/src/reference_docs/umbrella_crate.rs index 0b3445cfc4bc..1074cde37693 100644 --- a/docs/sdk/src/reference_docs/umbrella_crate.rs +++ b/docs/sdk/src/reference_docs/umbrella_crate.rs @@ -25,7 +25,7 @@ //! - `runtime`: As described above, enable all `no-std` crates. //! - `node`: As described above, enable all `std` crates. //! - There does *not* exist a dedicated docs feature. To generate docs, enable the `runtime` and -//! `node` feature. For docs.rs the manifest contains specific configuration to make it show up +//! `node` feature. For `docs.rs` the manifest contains specific configuration to make it show up //! all re-exports. //! //! There is a specific [`zepter`](https://github.com/ggwpez/zepter) check in place to ensure that @@ -77,7 +77,7 @@ //! ``` //! //! Apart from this, no issues are known. There could be some bugs with how macros locate their own -//! re-exports. Please compile issues that arise from using this crate. +//! re-exports. Please [report issues](https://github.com/paritytech/polkadot-sdk/issues) that arise from using this crate. //! //! ## Dependencies //! diff --git a/docs/sdk/src/reference_docs/wasm_meta_protocol.rs b/docs/sdk/src/reference_docs/wasm_meta_protocol.rs index 0e91e65c55e3..55b5cb204dc2 100644 --- a/docs/sdk/src/reference_docs/wasm_meta_protocol.rs +++ b/docs/sdk/src/reference_docs/wasm_meta_protocol.rs @@ -29,7 +29,7 @@ //! Rust to different hardware targets. //! //! This design enables all Substrate-based chains to be fork-less-ly upgradeable, because the -//! Runtime can be updates on the fly, within the execution of a block, and the node is (for the +//! Runtime can be updated on the fly, within the execution of a block, and the node is (for the //! most part) oblivious to the change that is happening. //! //! Therefore, the high-level architecture of a any Substrate-based chain can be demonstrated as @@ -82,7 +82,7 @@ //! //! ## State //! -//! From the previous sections, we know that the a database component is part of the node, not the +//! From the previous sections, we know that the database component is part of the node, not the //! runtime. We also hinted that a set of host functions ([`sp_io::storage`]) are how the runtime //! issues commands to the node to read/write to the state. Let's dive deeper into this. //! @@ -143,13 +143,13 @@ //! At some point, based on the consensus algorithm's rules, the node decides to import (aka. //! *validate*) a block. //! -//! * First, the node will then fetch the state of the parent hash of the block that wishes to be +//! * First, the node will fetch the state of the parent hash of the block that wishes to be //! imported. //! * The runtime is fetched from this state, and placed into a WASM execution environment. -//! * The [`sp_api::Core::execute_block`] runtime API is called and the blocked is passed in as an +//! * The [`sp_api::Core::execute_block`] runtime API is called and the block is passed in as an //! argument. //! * The runtime will then execute the block, and update the state accordingly. Any state update is -//! issues via the [`sp_io::storage`] host functions. +//! issued via the [`sp_io::storage`] host functions. //! * Both the runtime and node will check the state-root of the state after the block execution to //! match the one claimed in the block header. //! diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index e1e1e0153de0..bd571571ee24 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -934,7 +934,7 @@ pub mod pallet_prelude { /// /// # 1 - Pallet module declaration /// -/// The module to declare a pallet is organized as follow: +/// The module to declare a pallet is organized as follows: /// ``` /// #[frame_support::pallet] // <- the macro /// mod pallet { @@ -1047,7 +1047,7 @@ pub mod pallet_prelude { /// If the attribute `set_storage_max_encoded_len` is set then the macro calls /// [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) for each storage in the /// implementation of [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) for the -/// pallet. Otherwise it implements +/// pallet. Otherwise, it implements /// [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) for the pallet using the /// [`PartialStorageInfoTrait`](frame_support::traits::PartialStorageInfoTrait) /// implementation of storages. From cdbddc0b4b7b5f48dd9f942ec375869478e17e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 29 Jul 2024 16:28:24 +0200 Subject: [PATCH 040/382] Wasm-builder: Set the resolver version (#5174) --- prdoc/pr_5174.prdoc | 10 ++++++++++ substrate/utils/wasm-builder/src/wasm_project.rs | 5 ++++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 prdoc/pr_5174.prdoc diff --git a/prdoc/pr_5174.prdoc b/prdoc/pr_5174.prdoc new file mode 100644 index 000000000000..2c7a6983377c --- /dev/null +++ b/prdoc/pr_5174.prdoc @@ -0,0 +1,10 @@ +title: "Wasm-builder: Set the `resolver` version to `2`" + +doc: + - audience: Runtime Dev + description: | + Set the `resolver` version to `2` in the generated `Cargo.toml`. + +crates: + - name: substrate-wasm-builder + bump: patch diff --git a/substrate/utils/wasm-builder/src/wasm_project.rs b/substrate/utils/wasm-builder/src/wasm_project.rs index 63887389fb1e..a6eda078fde0 100644 --- a/substrate/utils/wasm-builder/src/wasm_project.rs +++ b/substrate/utils/wasm-builder/src/wasm_project.rs @@ -507,7 +507,10 @@ fn create_project_cargo_toml( wasm_workspace_toml.insert("dependencies".into(), dependencies.into()); - wasm_workspace_toml.insert("workspace".into(), Table::new().into()); + let mut workspace = Table::new(); + workspace.insert("resolver".into(), "2".into()); + + wasm_workspace_toml.insert("workspace".into(), workspace.into()); if target == RuntimeTarget::Riscv { // This dependency currently doesn't compile under RISC-V, so patch it with our own fork. From 4def82e7ff6cfacee9e33f53e20f25c10ce6b9e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:29:42 +0200 Subject: [PATCH 041/382] Bump serde_json from 1.0.120 to 1.0.121 in the known_good_semver group (#5169) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the known_good_semver group with 1 update: [serde_json](https://github.com/serde-rs/json). Updates `serde_json` from 1.0.120 to 1.0.121
Release notes

Sourced from serde_json's releases.

v1.0.121

Commits
  • eca2658 Release 1.0.121
  • b0d678c Merge pull request #1160 from iex-rs/efficient-position
  • b1edc7d Optimize position search in error path
  • 40dd7f5 Merge pull request #1159 from iex-rs/fix-recursion
  • 6a306e6 Move call to tri! out of check_recursion!
  • 3f1c6de Ignore byte_char_slices clippy lint in test
  • 3fd6f5f Merge pull request #1153 from dpathakj/master
  • fcb5e83 Correct documentation URL for Value's Index impl.
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde_json&package-manager=cargo&previous-version=1.0.120&new-version=1.0.121)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bastian Köcher Co-authored-by: Bastian Köcher --- Cargo.lock | 11 +++++------ Cargo.toml | 2 +- cumulus/primitives/timestamp/Cargo.toml | 4 ---- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 407ac2b57ce7..7466975fa428 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4301,8 +4301,6 @@ name = "cumulus-primitives-timestamp" version = "0.7.0" dependencies = [ "cumulus-primitives-core", - "futures", - "parity-scale-codec", "sp-inherents", "sp-timestamp", ] @@ -8588,9 +8586,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memfd" @@ -18893,12 +18891,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" dependencies = [ "indexmap 2.2.3", "itoa", + "memchr", "ryu", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 85f21be900bd..38ef9075a731 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1176,7 +1176,7 @@ separator = { version = "0.4.1" } serde = { version = "1.0.204", default-features = false } serde-big-array = { version = "0.3.2" } serde_derive = { version = "1.0.117" } -serde_json = { version = "1.0.120", default-features = false } +serde_json = { version = "1.0.121", default-features = false } serde_yaml = { version = "0.9" } serial_test = { version = "2.0.0" } sha1 = { version = "0.10.6" } diff --git a/cumulus/primitives/timestamp/Cargo.toml b/cumulus/primitives/timestamp/Cargo.toml index a50011bc3f0e..cb328e2f2cc6 100644 --- a/cumulus/primitives/timestamp/Cargo.toml +++ b/cumulus/primitives/timestamp/Cargo.toml @@ -10,9 +10,6 @@ license = "Apache-2.0" workspace = true [dependencies] -codec = { features = ["derive"], workspace = true } -futures = { workspace = true } - # Substrate sp-inherents = { workspace = true } sp-timestamp = { workspace = true } @@ -23,7 +20,6 @@ cumulus-primitives-core = { workspace = true } [features] default = ["std"] std = [ - "codec/std", "cumulus-primitives-core/std", "sp-inherents/std", "sp-timestamp/std", From d6f59871396e2a1ffd1e27d545d98d94c6e27ea0 Mon Sep 17 00:00:00 2001 From: girazoki Date: Mon, 29 Jul 2024 20:48:44 +0200 Subject: [PATCH 042/382] add possibility to inject non-authorities session-keys in genesis (#5078) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the possibility of injecting session-keys in genesis for non-validators. Currently all keys injected in genesis were considered as part of the initial validators set, this PR allows to inject a new vector with non-authority keys --------- Co-authored-by: Bastian Köcher --- .../pallets/collator-selection/src/mock.rs | 2 +- .../assets/asset-hub-rococo/src/genesis.rs | 1 + .../assets/asset-hub-westend/src/genesis.rs | 1 + .../bridges/bridge-hub-rococo/src/genesis.rs | 1 + .../bridges/bridge-hub-westend/src/genesis.rs | 1 + .../collectives-westend/src/genesis.rs | 1 + .../coretime/coretime-rococo/src/genesis.rs | 1 + .../coretime/coretime-westend/src/genesis.rs | 1 + .../people/people-rococo/src/genesis.rs | 1 + .../people/people-westend/src/genesis.rs | 1 + .../parachains/testing/penpal/src/genesis.rs | 1 + .../chains/relays/rococo/src/genesis.rs | 1 + .../chains/relays/westend/src/genesis.rs | 1 + .../parachains/runtimes/test-utils/src/lib.rs | 2 +- .../src/chain_spec/asset_hubs.rs | 1 + prdoc/pr_5078.prdoc | 34 +++++++++++++++++++ .../cli/tests/res/default_genesis_config.json | 4 ++- substrate/bin/node/testing/src/genesis.rs | 1 + substrate/frame/babe/src/mock.rs | 2 +- substrate/frame/beefy-mmr/src/mock.rs | 2 +- substrate/frame/beefy/src/mock.rs | 2 +- .../test-staking-e2e/src/mock.rs | 1 + substrate/frame/grandpa/src/mock.rs | 2 +- substrate/frame/root-offences/src/mock.rs | 1 + substrate/frame/session/src/historical/mod.rs | 2 +- .../frame/session/src/historical/offchain.rs | 4 ++- substrate/frame/session/src/lib.rs | 13 +++++-- substrate/frame/session/src/mock.rs | 2 +- substrate/frame/staking/src/mock.rs | 1 + 29 files changed, 76 insertions(+), 12 deletions(-) create mode 100644 prdoc/pr_5078.prdoc diff --git a/cumulus/pallets/collator-selection/src/mock.rs b/cumulus/pallets/collator-selection/src/mock.rs index 459b1cb5fdf2..d13f9e9d8c44 100644 --- a/cumulus/pallets/collator-selection/src/mock.rs +++ b/cumulus/pallets/collator-selection/src/mock.rs @@ -187,7 +187,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { candidacy_bond: 10, invulnerables, }; - let session = pallet_session::GenesisConfig:: { keys }; + let session = pallet_session::GenesisConfig:: { keys, ..Default::default() }; pallet_balances::GenesisConfig:: { balances } .assimilate_storage(&mut t) .unwrap(); diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs index 3a87322664d9..82f86e2b32ef 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs @@ -62,6 +62,7 @@ pub fn genesis() -> Storage { ) }) .collect(), + ..Default::default() }, polkadot_xcm: asset_hub_rococo_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs index 219d1306906c..fd84030ed13b 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs @@ -58,6 +58,7 @@ pub fn genesis() -> Storage { ) }) .collect(), + ..Default::default() }, polkadot_xcm: asset_hub_westend_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs index 12778215b132..3786d529ea65 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs @@ -52,6 +52,7 @@ pub fn genesis() -> Storage { ) }) .collect(), + ..Default::default() }, polkadot_xcm: bridge_hub_rococo_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs index 4be68e510f4d..a160d18d4cf7 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs @@ -51,6 +51,7 @@ pub fn genesis() -> Storage { ) }) .collect(), + ..Default::default() }, polkadot_xcm: bridge_hub_westend_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/genesis.rs index 6a28b1a9dddb..d4ef184ea392 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/genesis.rs @@ -51,6 +51,7 @@ pub fn genesis() -> Storage { ) }) .collect(), + ..Default::default() }, polkadot_xcm: collectives_westend_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo/src/genesis.rs index f72de52c4932..e0f035c368e3 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo/src/genesis.rs @@ -51,6 +51,7 @@ pub fn genesis() -> Storage { ) }) .collect(), + ..Default::default() }, polkadot_xcm: coretime_rococo_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend/src/genesis.rs index 222ffe7a63ab..239ad3760c11 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend/src/genesis.rs @@ -51,6 +51,7 @@ pub fn genesis() -> Storage { ) }) .collect(), + ..Default::default() }, polkadot_xcm: coretime_westend_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/src/genesis.rs index b14009933029..43d182facdd5 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/src/genesis.rs @@ -47,6 +47,7 @@ pub fn genesis() -> Storage { ) }) .collect(), + ..Default::default() }, polkadot_xcm: people_rococo_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/src/genesis.rs index d385fbebc821..0b99f19bc130 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/src/genesis.rs @@ -47,6 +47,7 @@ pub fn genesis() -> Storage { ) }) .collect(), + ..Default::default() }, polkadot_xcm: people_westend_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs index 450439f5ea30..260875088bbc 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs @@ -59,6 +59,7 @@ pub fn genesis(para_id: u32) -> Storage { ) }) .collect(), + ..Default::default() }, polkadot_xcm: penpal_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/src/genesis.rs index 074a1de5e185..9cb25b403600 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/src/genesis.rs @@ -75,6 +75,7 @@ pub fn genesis() -> Storage { ) }) .collect::>(), + ..Default::default() }, babe: rococo_runtime::BabeConfig { authorities: Default::default(), diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs index b9f12932b84e..172e6e0ac93e 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs @@ -77,6 +77,7 @@ pub fn genesis() -> Storage { ) }) .collect::>(), + ..Default::default() }, staking: westend_runtime::StakingConfig { validator_count: validators::initial_authorities().len() as u32, diff --git a/cumulus/parachains/runtimes/test-utils/src/lib.rs b/cumulus/parachains/runtimes/test-utils/src/lib.rs index 3fc3822a63eb..940aa1b734df 100644 --- a/cumulus/parachains/runtimes/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/test-utils/src/lib.rs @@ -242,7 +242,7 @@ impl ExtBuilder { .assimilate_storage(&mut t) .unwrap(); - pallet_session::GenesisConfig:: { keys: self.keys } + pallet_session::GenesisConfig:: { keys: self.keys, ..Default::default() } .assimilate_storage(&mut t) .unwrap(); diff --git a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs index 45920cdb6146..af5bccdc416f 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs @@ -389,6 +389,7 @@ fn asset_hub_rococo_genesis( ) }) .collect(), + ..Default::default() }, "polkadotXcm": asset_hub_rococo_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), diff --git a/prdoc/pr_5078.prdoc b/prdoc/pr_5078.prdoc new file mode 100644 index 000000000000..1805a27c3f28 --- /dev/null +++ b/prdoc/pr_5078.prdoc @@ -0,0 +1,34 @@ +title: Add possibility to inject non-authorities session-keys in genesis + +doc: + - audience: Runtime Dev + description: | + Allows to inject a set of registered session-keys in pallet-session that are not + part of the first initial set of validators +crates: +- name: pallet-session + bump: major +- name: parachains-runtimes-test-utils + bump: patch +- name: pallet-staking + bump: none +- name: pallet-collator-selection + bump: none +- name: pallet-root-offences + bump: none +- name: pallet-babe + bump: none +- name: pallet-staking + bump: none +- name: pallet-grandpa + bump: none +- name: pallet-collator-selection + bump: none +- name: pallet-beefy + bump: none +- name: pallet-beefy-mmr + bump: none +- name: pallet-root-offences + bump: none +- name: polkadot-parachain-bin + bump: none \ No newline at end of file diff --git a/substrate/bin/node/cli/tests/res/default_genesis_config.json b/substrate/bin/node/cli/tests/res/default_genesis_config.json index b63e5ff549ef..a2e52837d882 100644 --- a/substrate/bin/node/cli/tests/res/default_genesis_config.json +++ b/substrate/bin/node/cli/tests/res/default_genesis_config.json @@ -34,7 +34,9 @@ "maxNominatorCount": null }, "session": { - "keys": [] + "keys": [], + "nonAuthorityKeys": [] + }, "democracy": {}, "council": { diff --git a/substrate/bin/node/testing/src/genesis.rs b/substrate/bin/node/testing/src/genesis.rs index c79612d68444..7f5364744c66 100644 --- a/substrate/bin/node/testing/src/genesis.rs +++ b/substrate/bin/node/testing/src/genesis.rs @@ -54,6 +54,7 @@ pub fn config_endowed(extra_endowed: Vec) -> RuntimeGenesisConfig { (bob(), eve(), session_keys_from_seed(Ed25519Keyring::Bob.into())), (charlie(), ferdie(), session_keys_from_seed(Ed25519Keyring::Charlie.into())), ], + ..Default::default() }, staking: StakingConfig { stakers: vec![ diff --git a/substrate/frame/babe/src/mock.rs b/substrate/frame/babe/src/mock.rs index e193a2e3b645..912cb3e27cd5 100644 --- a/substrate/frame/babe/src/mock.rs +++ b/substrate/frame/babe/src/mock.rs @@ -319,7 +319,7 @@ pub fn new_test_ext_raw_authorities(authorities: Vec) -> sp_io::Tes // NOTE: this will initialize the babe authorities // through OneSessionHandler::on_genesis_session - pallet_session::GenesisConfig:: { keys: session_keys } + pallet_session::GenesisConfig:: { keys: session_keys, ..Default::default() } .assimilate_storage(&mut t) .unwrap(); diff --git a/substrate/frame/beefy-mmr/src/mock.rs b/substrate/frame/beefy-mmr/src/mock.rs index 3adef4f32bf4..1102f9677aaa 100644 --- a/substrate/frame/beefy-mmr/src/mock.rs +++ b/substrate/frame/beefy-mmr/src/mock.rs @@ -187,7 +187,7 @@ pub fn new_test_ext_raw_authorities(authorities: Vec<(u64, BeefyId)>) -> TestExt } }); - pallet_session::GenesisConfig:: { keys: session_keys } + pallet_session::GenesisConfig:: { keys: session_keys, ..Default::default() } .assimilate_storage(&mut t) .unwrap(); diff --git a/substrate/frame/beefy/src/mock.rs b/substrate/frame/beefy/src/mock.rs index a0880660d051..b423fa0bda89 100644 --- a/substrate/frame/beefy/src/mock.rs +++ b/substrate/frame/beefy/src/mock.rs @@ -269,7 +269,7 @@ impl ExtBuilder { } }); - pallet_session::GenesisConfig:: { keys: session_keys } + pallet_session::GenesisConfig:: { keys: session_keys, ..Default::default() } .assimilate_storage(&mut t) .unwrap(); diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs index 5c64f2a0bc20..d148c05e4d0d 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs @@ -573,6 +573,7 @@ impl ExtBuilder { .into_iter() .map(|(id, ..)| (id, id, SessionKeys { other: (id as u64).into() })) .collect(), + ..Default::default() } .assimilate_storage(&mut storage); diff --git a/substrate/frame/grandpa/src/mock.rs b/substrate/frame/grandpa/src/mock.rs index 5ba7da7f9fda..ae230a0209a7 100644 --- a/substrate/frame/grandpa/src/mock.rs +++ b/substrate/frame/grandpa/src/mock.rs @@ -235,7 +235,7 @@ pub fn new_test_ext_raw_authorities(authorities: AuthorityList) -> sp_io::TestEx // NOTE: this will initialize the grandpa authorities // through OneSessionHandler::on_genesis_session - pallet_session::GenesisConfig:: { keys: session_keys } + pallet_session::GenesisConfig:: { keys: session_keys, ..Default::default() } .assimilate_storage(&mut t) .unwrap(); diff --git a/substrate/frame/root-offences/src/mock.rs b/substrate/frame/root-offences/src/mock.rs index ab43b723e8a9..af073d7672cf 100644 --- a/substrate/frame/root-offences/src/mock.rs +++ b/substrate/frame/root-offences/src/mock.rs @@ -246,6 +246,7 @@ impl ExtBuilder { .into_iter() .map(|(id, ..)| (id, id, SessionKeys { other: id.into() })) .collect(), + ..Default::default() } .assimilate_storage(&mut storage); diff --git a/substrate/frame/session/src/historical/mod.rs b/substrate/frame/session/src/historical/mod.rs index fac580b49b3a..a19364d577f6 100644 --- a/substrate/frame/session/src/historical/mod.rs +++ b/substrate/frame/session/src/historical/mod.rs @@ -396,7 +396,7 @@ pub(crate) mod tests { frame_system::Pallet::::inc_providers(k); } }); - pallet_session::GenesisConfig:: { keys } + pallet_session::GenesisConfig:: { keys, ..Default::default() } .assimilate_storage(&mut t) .unwrap(); sp_io::TestExternalities::new(t) diff --git a/substrate/frame/session/src/historical/offchain.rs b/substrate/frame/session/src/historical/offchain.rs index 685a0be8e191..e9ced89a8f19 100644 --- a/substrate/frame/session/src/historical/offchain.rs +++ b/substrate/frame/session/src/historical/offchain.rs @@ -171,7 +171,9 @@ mod tests { } }); - crate::GenesisConfig:: { keys }.assimilate_storage(&mut t).unwrap(); + crate::GenesisConfig:: { keys, ..Default::default() } + .assimilate_storage(&mut t) + .unwrap(); let mut ext = sp_io::TestExternalities::new(t); diff --git a/substrate/frame/session/src/lib.rs b/substrate/frame/session/src/lib.rs index e1a2a31911fe..325758d54dd8 100644 --- a/substrate/frame/session/src/lib.rs +++ b/substrate/frame/session/src/lib.rs @@ -423,7 +423,14 @@ pub mod pallet { #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { + /// Initial list of validator at genesis representing by their `(AccountId, ValidatorId, + /// Keys)`. These keys will be considered authorities for the first two sessions and they + /// will be valid at least until session 2 pub keys: Vec<(T::AccountId, T::ValidatorId, T::Keys)>, + /// List of (AccountId, ValidatorId, Keys) that will be registered at genesis, but not as + /// active validators. These keys are set, together with `keys`, as authority candidates + /// for future sessions (enactable from session 2 onwards) + pub non_authority_keys: Vec<(T::AccountId, T::ValidatorId, T::Keys)>, } #[pallet::genesis_build] @@ -446,7 +453,9 @@ pub mod pallet { } }); - for (account, val, keys) in self.keys.iter().cloned() { + for (account, val, keys) in + self.keys.iter().chain(self.non_authority_keys.iter()).cloned() + { Pallet::::inner_set_keys(&val, keys) .expect("genesis config must not contain duplicates; qed"); if frame_system::Pallet::::inc_consumers_without_limit(&account).is_err() { @@ -676,7 +685,7 @@ impl Pallet { let mut now_session_keys = session_keys.iter(); let mut check_next_changed = |keys: &T::Keys| { if changed { - return + return; } // since a new validator set always leads to `changed` starting // as true, we can ensure that `now_session_keys` and `next_validators` diff --git a/substrate/frame/session/src/mock.rs b/substrate/frame/session/src/mock.rs index 25b81668cc08..745b57d1be41 100644 --- a/substrate/frame/session/src/mock.rs +++ b/substrate/frame/session/src/mock.rs @@ -215,7 +215,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { // An additional identity that we use. frame_system::Pallet::::inc_providers(&69); }); - pallet_session::GenesisConfig:: { keys } + pallet_session::GenesisConfig:: { keys, ..Default::default() } .assimilate_storage(&mut t) .unwrap(); diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index 7e6a87955b08..7cc8e865916d 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -535,6 +535,7 @@ impl ExtBuilder { .map(|id| (id, id, SessionKeys { other: id.into() })) .collect() }, + ..Default::default() } .assimilate_storage(&mut storage); From 71cb378f14e110607b3fa568803003bf331d7fdf Mon Sep 17 00:00:00 2001 From: Yuri Volkov <0@mcornholio.ru> Date: Mon, 29 Jul 2024 21:09:35 +0200 Subject: [PATCH 043/382] Review-bot@2.6.0 (#5177) --- .github/workflows/review-bot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/review-bot.yml b/.github/workflows/review-bot.yml index 0d83b75b3bde..80c96b0ef537 100644 --- a/.github/workflows/review-bot.yml +++ b/.github/workflows/review-bot.yml @@ -30,7 +30,7 @@ jobs: with: artifact-name: pr_number - name: "Evaluates PR reviews and assigns reviewers" - uses: paritytech/review-bot@v2.5.0 + uses: paritytech/review-bot@v2.6.0 with: repo-token: ${{ steps.app_token.outputs.token }} team-token: ${{ steps.app_token.outputs.token }} From 3886ed1d1f64b8a1c05a5cda299ffbe8d4c9052f Mon Sep 17 00:00:00 2001 From: "polka.dom" Date: Mon, 29 Jul 2024 15:05:29 -0700 Subject: [PATCH 044/382] Remove pallet::getter macro usage from pallet-election-provider-multi-phase (#4487) As per #3326, removes pallet::getter macro usage from the election-provider-multi-phase pallet. The syntax `StorageItem::::get()` should be used instead. cc @muraca --------- Co-authored-by: Oliver Tale-Yazdi --- prdoc/pr_4487.prdoc | 16 + .../src/benchmarking.rs | 108 +++--- .../src/helpers.rs | 2 +- .../election-provider-multi-phase/src/lib.rs | 355 ++++++++++-------- .../election-provider-multi-phase/src/mock.rs | 22 +- .../src/signed.rs | 71 ++-- .../src/unsigned.rs | 96 ++--- .../test-staking-e2e/src/lib.rs | 18 +- .../test-staking-e2e/src/mock.rs | 12 +- 9 files changed, 385 insertions(+), 315 deletions(-) create mode 100644 prdoc/pr_4487.prdoc diff --git a/prdoc/pr_4487.prdoc b/prdoc/pr_4487.prdoc new file mode 100644 index 000000000000..fb2bab2a57a8 --- /dev/null +++ b/prdoc/pr_4487.prdoc @@ -0,0 +1,16 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Remove `pallet::getter` usage from pallet-election-provider-multi-phase + +doc: + - audience: Runtime Dev + description: | + This PR removes the `pallet::getter`s from `pallet-election-provider-multi-phase`. + The syntax `StorageItem::::get()` should be used instead. + +crates: + - name: pallet-election-provider-multi-phase + bump: minor + - name: pallet-election-provider-e2e-test + bump: minor diff --git a/substrate/frame/election-provider-multi-phase/src/benchmarking.rs b/substrate/frame/election-provider-multi-phase/src/benchmarking.rs index 957ae51b8f1d..2a3994ff2aa6 100644 --- a/substrate/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/substrate/frame/election-provider-multi-phase/src/benchmarking.rs @@ -110,12 +110,12 @@ fn solution_with_size( assert_eq!(all_voters.len() as u32, size.voters); assert_eq!(winners.len() as u32, desired_targets); - >::put(SolutionOrSnapshotSize { + SnapshotMetadata::::put(SolutionOrSnapshotSize { voters: all_voters.len() as u32, targets: targets.len() as u32, }); - >::put(desired_targets); - >::put(RoundSnapshot { voters: all_voters.clone(), targets: targets.clone() }); + DesiredTargets::::put(desired_targets); + Snapshot::::put(RoundSnapshot { voters: all_voters.clone(), targets: targets.clone() }); // write the snapshot to staking or whoever is the data provider, in case it is needed further // down the road. @@ -137,7 +137,7 @@ fn solution_with_size( who: voter.clone(), distribution: votes .iter() - .map(|t| (t.clone(), >::from_percent(percent_per_edge))) + .map(|t| (t.clone(), SolutionAccuracyOf::::from_percent(percent_per_edge))) .collect::>(), } }) @@ -147,7 +147,7 @@ fn solution_with_size( >::from_assignment(&assignments, &voter_index, &target_index) .unwrap(); let score = solution.clone().score(stake_of, voter_at, target_at).unwrap(); - let round = >::round(); + let round = Round::::get(); assert!( score.minimal_stake > 0, @@ -192,32 +192,32 @@ fn set_up_data_provider(v: u32, t: u32) { frame_benchmarking::benchmarks! { on_initialize_nothing { - assert!(>::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); }: { - >::on_initialize(1u32.into()); + MultiPhase::::on_initialize(1u32.into()); } verify { - assert!(>::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); } on_initialize_open_signed { - assert!(>::snapshot().is_none()); - assert!(>::current_phase().is_off()); + assert!(Snapshot::::get().is_none()); + assert!(CurrentPhase::::get().is_off()); }: { - >::phase_transition(Phase::Signed); + MultiPhase::::phase_transition(Phase::Signed); } verify { - assert!(>::snapshot().is_none()); - assert!(>::current_phase().is_signed()); + assert!(Snapshot::::get().is_none()); + assert!(CurrentPhase::::get().is_signed()); } on_initialize_open_unsigned { - assert!(>::snapshot().is_none()); - assert!(>::current_phase().is_off()); + assert!(Snapshot::::get().is_none()); + assert!(CurrentPhase::::get().is_off()); }: { let now = frame_system::Pallet::::block_number(); - >::phase_transition(Phase::Unsigned((true, now))); + MultiPhase::::phase_transition(Phase::Unsigned((true, now))); } verify { - assert!(>::snapshot().is_none()); - assert!(>::current_phase().is_unsigned()); + assert!(Snapshot::::get().is_none()); + assert!(CurrentPhase::::get().is_unsigned()); } finalize_signed_phase_accept_solution { @@ -233,7 +233,7 @@ frame_benchmarking::benchmarks! { assert_ok!(T::Currency::reserve(&receiver, deposit)); assert_eq!(T::Currency::free_balance(&receiver), T::Currency::minimum_balance()); }: { - >::finalize_signed_phase_accept_solution( + MultiPhase::::finalize_signed_phase_accept_solution( ready, &receiver, deposit, @@ -257,7 +257,7 @@ frame_benchmarking::benchmarks! { assert_eq!(T::Currency::free_balance(&receiver), T::Currency::minimum_balance()); assert_eq!(T::Currency::reserved_balance(&receiver), 10u32.into()); }: { - >::finalize_signed_phase_reject_solution(&receiver, deposit) + MultiPhase::::finalize_signed_phase_reject_solution(&receiver, deposit) } verify { assert_eq!(T::Currency::free_balance(&receiver), T::Currency::minimum_balance()); assert_eq!(T::Currency::reserved_balance(&receiver), 0u32.into()); @@ -275,13 +275,13 @@ frame_benchmarking::benchmarks! { let targets = T::DataProvider::electable_targets(DataProviderBounds::default())?; let voters = T::DataProvider::electing_voters(DataProviderBounds::default())?; let desired_targets = T::DataProvider::desired_targets()?; - assert!(>::snapshot().is_none()); + assert!(Snapshot::::get().is_none()); }: { - >::create_snapshot_internal(targets, voters, desired_targets) + MultiPhase::::create_snapshot_internal(targets, voters, desired_targets) } verify { - assert!(>::snapshot().is_some()); - assert_eq!(>::snapshot_metadata().ok_or("metadata missing")?.voters, v); - assert_eq!(>::snapshot_metadata().ok_or("metadata missing")?.targets, t); + assert!(Snapshot::::get().is_some()); + assert_eq!(SnapshotMetadata::::get().ok_or("metadata missing")?.voters, v); + assert_eq!(SnapshotMetadata::::get().ok_or("metadata missing")?.targets, t); } // a call to `::elect` where we only return the queued solution. @@ -300,31 +300,31 @@ frame_benchmarking::benchmarks! { let witness = SolutionOrSnapshotSize { voters: v, targets: t }; let raw_solution = solution_with_size::(witness, a, d)?; let ready_solution = - >::feasibility_check(raw_solution, ElectionCompute::Signed) + MultiPhase::::feasibility_check(raw_solution, ElectionCompute::Signed) .map_err(<&str>::from)?; - >::put(Phase::Signed); + CurrentPhase::::put(Phase::Signed); // assume a queued solution is stored, regardless of where it comes from. - >::put(ready_solution); + QueuedSolution::::put(ready_solution); // these are set by the `solution_with_size` function. - assert!(>::get().is_some()); - assert!(>::get().is_some()); - assert!(>::get().is_some()); + assert!(DesiredTargets::::get().is_some()); + assert!(Snapshot::::get().is_some()); + assert!(SnapshotMetadata::::get().is_some()); }: { assert_ok!( as ElectionProvider>::elect()); } verify { - assert!(>::queued_solution().is_none()); - assert!(>::get().is_none()); - assert!(>::get().is_none()); - assert!(>::get().is_none()); - assert_eq!(>::get(), >>::Off); + assert!(QueuedSolution::::get().is_none()); + assert!(DesiredTargets::::get().is_none()); + assert!(Snapshot::::get().is_none()); + assert!(SnapshotMetadata::::get().is_none()); + assert_eq!(CurrentPhase::::get(), >>::Off); } submit { // the queue is full and the solution is only better than the worse. - >::create_snapshot().map_err(<&str>::from)?; - >::phase_transition(Phase::Signed); - >::put(1); + MultiPhase::::create_snapshot().map_err(<&str>::from)?; + MultiPhase::::phase_transition(Phase::Signed); + Round::::put(1); let mut signed_submissions = SignedSubmissions::::get(); @@ -353,13 +353,13 @@ frame_benchmarking::benchmarks! { let caller = frame_benchmarking::whitelisted_caller(); let deposit = MultiPhase::::deposit_for( &solution, - MultiPhase::::snapshot_metadata().unwrap_or_default(), + SnapshotMetadata::::get().unwrap_or_default(), ); T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance() * 1000u32.into() + deposit); }: _(RawOrigin::Signed(caller), Box::new(solution)) verify { - assert!(>::signed_submissions().len() as u32 == T::SignedMaxSubmissions::get()); + assert!(MultiPhase::::signed_submissions().len() as u32 == T::SignedMaxSubmissions::get()); } submit_unsigned { @@ -379,11 +379,11 @@ frame_benchmarking::benchmarks! { let witness = SolutionOrSnapshotSize { voters: v, targets: t }; let raw_solution = solution_with_size::(witness, a, d)?; - assert!(>::queued_solution().is_none()); - >::put(Phase::Unsigned((true, 1u32.into()))); + assert!(QueuedSolution::::get().is_none()); + CurrentPhase::::put(Phase::Unsigned((true, 1u32.into()))); }: _(RawOrigin::None, Box::new(raw_solution), witness) verify { - assert!(>::queued_solution().is_some()); + assert!(QueuedSolution::::get().is_some()); } // This is checking a valid solution. The worse case is indeed a valid solution. @@ -404,7 +404,7 @@ frame_benchmarking::benchmarks! { assert_eq!(raw_solution.solution.voter_count() as u32, a); assert_eq!(raw_solution.solution.unique_targets().len() as u32, d); }: { - assert!(>::feasibility_check(raw_solution, ElectionCompute::Unsigned).is_ok()); + assert!(MultiPhase::::feasibility_check(raw_solution, ElectionCompute::Unsigned).is_ok()); } // NOTE: this weight is not used anywhere, but the fact that it should succeed when execution in @@ -428,11 +428,11 @@ frame_benchmarking::benchmarks! { set_up_data_provider::(v, t); let now = frame_system::Pallet::::block_number(); - >::put(Phase::Unsigned((true, now))); - >::create_snapshot().unwrap(); + CurrentPhase::::put(Phase::Unsigned((true, now))); + MultiPhase::::create_snapshot().unwrap(); }: { // we can't really verify this as it won't write anything to state, check logs. - >::offchain_worker(now) + MultiPhase::::offchain_worker(now) } // NOTE: this weight is not used anywhere, but the fact that it should succeed when execution in @@ -449,13 +449,13 @@ frame_benchmarking::benchmarks! { let t = T::BenchmarkingConfig::MAXIMUM_TARGETS; set_up_data_provider::(v, t); - assert!(>::snapshot().is_none()); + assert!(Snapshot::::get().is_none()); }: { - >::create_snapshot().map_err(|_| "could not create snapshot")?; + MultiPhase::::create_snapshot().map_err(|_| "could not create snapshot")?; } verify { - assert!(>::snapshot().is_some()); - assert_eq!(>::snapshot_metadata().ok_or("snapshot missing")?.voters, v); - assert_eq!(>::snapshot_metadata().ok_or("snapshot missing")?.targets, t); + assert!(Snapshot::::get().is_some()); + assert_eq!(SnapshotMetadata::::get().ok_or("snapshot missing")?.voters, v); + assert_eq!(SnapshotMetadata::::get().ok_or("snapshot missing")?.targets, t); } #[extra] @@ -480,7 +480,7 @@ frame_benchmarking::benchmarks! { // assignments let witness = SolutionOrSnapshotSize { voters: v, targets: t }; let RawSolution { solution, .. } = solution_with_size::(witness, a, d)?; - let RoundSnapshot { voters, targets } = MultiPhase::::snapshot().ok_or("snapshot missing")?; + let RoundSnapshot { voters, targets } = Snapshot::::get().ok_or("snapshot missing")?; let voter_at = helpers::voter_at_fn::(&voters); let target_at = helpers::target_at_fn::(&targets); let mut assignments = solution.into_assignment(voter_at, target_at).expect("solution generated by `solution_with_size` must be valid."); diff --git a/substrate/frame/election-provider-multi-phase/src/helpers.rs b/substrate/frame/election-provider-multi-phase/src/helpers.rs index 8269b2cb73be..68facd098e65 100644 --- a/substrate/frame/election-provider-multi-phase/src/helpers.rs +++ b/substrate/frame/election-provider-multi-phase/src/helpers.rs @@ -28,7 +28,7 @@ macro_rules! log { ($level:tt, $pattern:expr $(, $values:expr)* $(,)?) => { log::$level!( target: $crate::LOG_TARGET, - concat!("[#{:?}] 🗳 ", $pattern), >::block_number() $(, $values)* + concat!("[#{:?}] 🗳 ", $pattern), frame_system::Pallet::::block_number() $(, $values)* ) }; } diff --git a/substrate/frame/election-provider-multi-phase/src/lib.rs b/substrate/frame/election-provider-multi-phase/src/lib.rs index 9ce8b3890a62..09248e77848b 100644 --- a/substrate/frame/election-provider-multi-phase/src/lib.rs +++ b/substrate/frame/election-provider-multi-phase/src/lib.rs @@ -746,14 +746,14 @@ pub mod pallet { let unsigned_deadline = T::UnsignedPhase::get(); let remaining = next_election - now; - let current_phase = Self::current_phase(); + let current_phase = CurrentPhase::::get(); log!( trace, "current phase {:?}, next election {:?}, metadata: {:?}", current_phase, next_election, - Self::snapshot_metadata() + SnapshotMetadata::::get() ); match current_phase { Phase::Off if remaining <= signed_deadline && remaining > unsigned_deadline => { @@ -853,7 +853,7 @@ pub mod pallet { let maximum_chain_accuracy: Vec>> = (0..max_vote) .map(|_| { >>::from( - >::one().deconstruct(), + SolutionAccuracyOf::::one().deconstruct(), ) }) .collect(); @@ -923,7 +923,7 @@ pub mod pallet { // Ensure witness was correct. let SolutionOrSnapshotSize { voters, targets } = - Self::snapshot_metadata().expect(error_message); + SnapshotMetadata::::get().expect(error_message); // NOTE: we are asserting, not `ensure`ing -- we want to panic here. assert!(voters as u32 == witness.voters, "{}", error_message); @@ -934,8 +934,8 @@ pub mod pallet { // Store the newly received solution. log!(debug, "queued unsigned solution with score {:?}", ready.score); - let ejected_a_solution = >::exists(); - >::put(ready); + let ejected_a_solution = QueuedSolution::::exists(); + QueuedSolution::::put(ready); Self::deposit_event(Event::SolutionStored { compute: ElectionCompute::Unsigned, origin: None, @@ -957,7 +957,7 @@ pub mod pallet { maybe_next_score: Option, ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; - >::set(maybe_next_score); + MinimumUntrustedScore::::set(maybe_next_score); Ok(()) } @@ -976,7 +976,7 @@ pub mod pallet { supports: Supports, ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; - ensure!(Self::current_phase().is_emergency(), >::CallNotAllowed); + ensure!(CurrentPhase::::get().is_emergency(), Error::::CallNotAllowed); // bound supports with T::MaxWinners let supports = supports.try_into().map_err(|_| Error::::TooManyWinners)?; @@ -995,7 +995,7 @@ pub mod pallet { prev_ejected: QueuedSolution::::exists(), }); - >::put(solution); + QueuedSolution::::put(solution); Ok(()) } @@ -1017,15 +1017,15 @@ pub mod pallet { let who = ensure_signed(origin)?; // ensure solution is timely. - ensure!(Self::current_phase().is_signed(), Error::::PreDispatchEarlySubmission); - ensure!(raw_solution.round == Self::round(), Error::::PreDispatchDifferentRound); + ensure!(CurrentPhase::::get().is_signed(), Error::::PreDispatchEarlySubmission); + ensure!(raw_solution.round == Round::::get(), Error::::PreDispatchDifferentRound); // NOTE: this is the only case where having separate snapshot would have been better // because could do just decode_len. But we can create abstractions to do this. // build size. Note: this is not needed for weight calc, thus not input. // unlikely to ever return an error: if phase is signed, snapshot will exist. - let size = Self::snapshot_metadata().ok_or(Error::::MissingSnapshotMetadata)?; + let size = SnapshotMetadata::::get().ok_or(Error::::MissingSnapshotMetadata)?; ensure!( Self::solution_weight_of(&raw_solution, size).all_lt(T::SignedMaxWeight::get()), @@ -1088,7 +1088,7 @@ pub mod pallet { maybe_max_targets: Option, ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; - ensure!(Self::current_phase().is_emergency(), >::CallNotAllowed); + ensure!(CurrentPhase::::get().is_emergency(), Error::::CallNotAllowed); let election_bounds = ElectionBoundsBuilder::default() .voters_count(maybe_max_voters.unwrap_or(u32::MAX).into()) @@ -1123,7 +1123,7 @@ pub mod pallet { prev_ejected: QueuedSolution::::exists(), }); - >::put(solution); + QueuedSolution::::put(solution); Ok(()) } } @@ -1256,19 +1256,16 @@ pub mod pallet { /// /// This is merely incremented once per every time that an upstream `elect` is called. #[pallet::storage] - #[pallet::getter(fn round)] pub type Round = StorageValue<_, u32, ValueQuery, DefaultForRound>; /// Current phase. #[pallet::storage] - #[pallet::getter(fn current_phase)] pub type CurrentPhase = StorageValue<_, Phase>, ValueQuery>; /// Current best solution, signed or unsigned, queued to be returned upon `elect`. /// /// Always sorted by score. #[pallet::storage] - #[pallet::getter(fn queued_solution)] pub type QueuedSolution = StorageValue<_, ReadySolution>; @@ -1277,7 +1274,6 @@ pub mod pallet { /// This is created at the beginning of the signed phase and cleared upon calling `elect`. /// Note: This storage type must only be mutated through [`SnapshotWrapper`]. #[pallet::storage] - #[pallet::getter(fn snapshot)] pub type Snapshot = StorageValue<_, RoundSnapshot>>; /// Desired number of targets to elect for this round. @@ -1285,7 +1281,6 @@ pub mod pallet { /// Only exists when [`Snapshot`] is present. /// Note: This storage type must only be mutated through [`SnapshotWrapper`]. #[pallet::storage] - #[pallet::getter(fn desired_targets)] pub type DesiredTargets = StorageValue<_, u32>; /// The metadata of the [`RoundSnapshot`] @@ -1293,7 +1288,6 @@ pub mod pallet { /// Only exists when [`Snapshot`] is present. /// Note: This storage type must only be mutated through [`SnapshotWrapper`]. #[pallet::storage] - #[pallet::getter(fn snapshot_metadata)] pub type SnapshotMetadata = StorageValue<_, SolutionOrSnapshotSize>; // The following storage items collectively comprise `SignedSubmissions`, and should never be @@ -1340,7 +1334,6 @@ pub mod pallet { /// /// Can be set via `set_minimum_untrusted_score`. #[pallet::storage] - #[pallet::getter(fn minimum_untrusted_score)] pub type MinimumUntrustedScore = StorageValue<_, ElectionScore>; /// The in-code storage version. @@ -1361,15 +1354,15 @@ pub struct SnapshotWrapper(core::marker::PhantomData); impl SnapshotWrapper { /// Kill all snapshot related storage items at the same time. pub fn kill() { - >::kill(); - >::kill(); - >::kill(); + Snapshot::::kill(); + SnapshotMetadata::::kill(); + DesiredTargets::::kill(); } /// Set all snapshot related storage items at the same time. pub fn set(metadata: SolutionOrSnapshotSize, desired_targets: u32, buffer: &[u8]) { - >::put(metadata); - >::put(desired_targets); - sp_io::storage::set(&>::hashed_key(), &buffer); + SnapshotMetadata::::put(metadata); + DesiredTargets::::put(desired_targets); + sp_io::storage::set(&Snapshot::::hashed_key(), &buffer); } /// Check if all of the storage items exist at the same time or all of the storage items do not @@ -1377,9 +1370,9 @@ impl SnapshotWrapper { #[cfg(feature = "try-runtime")] pub fn is_consistent() -> bool { let snapshots = [ - >::exists(), - >::exists(), - >::exists(), + Snapshot::::exists(), + SnapshotMetadata::::exists(), + DesiredTargets::::exists(), ]; // All should either exist or not exist @@ -1388,10 +1381,64 @@ impl SnapshotWrapper { } impl Pallet { + /// Internal counter for the number of rounds. + /// + /// This is useful for de-duplication of transactions submitted to the pool, and general + /// diagnostics of the pallet. + /// + /// This is merely incremented once per every time that an upstream `elect` is called. + pub fn round() -> u32 { + Round::::get() + } + + /// Current phase. + pub fn current_phase() -> Phase> { + CurrentPhase::::get() + } + + /// Current best solution, signed or unsigned, queued to be returned upon `elect`. + /// + /// Always sorted by score. + pub fn queued_solution() -> Option> { + QueuedSolution::::get() + } + + /// Snapshot data of the round. + /// + /// This is created at the beginning of the signed phase and cleared upon calling `elect`. + /// Note: This storage type must only be mutated through [`SnapshotWrapper`]. + pub fn snapshot() -> Option>> { + Snapshot::::get() + } + + /// Desired number of targets to elect for this round. + /// + /// Only exists when [`Snapshot`] is present. + /// Note: This storage type must only be mutated through [`SnapshotWrapper`]. + pub fn desired_targets() -> Option { + DesiredTargets::::get() + } + + /// The metadata of the [`RoundSnapshot`] + /// + /// Only exists when [`Snapshot`] is present. + /// Note: This storage type must only be mutated through [`SnapshotWrapper`]. + pub fn snapshot_metadata() -> Option { + SnapshotMetadata::::get() + } + + /// The minimum score that each 'untrusted' solution must attain in order to be considered + /// feasible. + /// + /// Can be set via `set_minimum_untrusted_score`. + pub fn minimum_untrusted_score() -> Option { + MinimumUntrustedScore::::get() + } + /// Internal logic of the offchain worker, to be executed only when the offchain lock is /// acquired with success. fn do_synchronized_offchain_worker(now: BlockNumberFor) { - let current_phase = Self::current_phase(); + let current_phase = CurrentPhase::::get(); log!(trace, "lock for offchain worker acquired. Phase = {:?}", current_phase); match current_phase { Phase::Unsigned((true, opened)) if opened == now => { @@ -1417,13 +1464,13 @@ impl Pallet { /// Phase transition helper. pub(crate) fn phase_transition(to: Phase>) { - log!(info, "Starting phase {:?}, round {}.", to, Self::round()); + log!(info, "Starting phase {:?}, round {}.", to, Round::::get()); Self::deposit_event(Event::PhaseTransitioned { - from: >::get(), + from: CurrentPhase::::get(), to, - round: Self::round(), + round: Round::::get(), }); - >::put(to); + CurrentPhase::::put(to); } /// Parts of [`create_snapshot`] that happen inside of this pallet. @@ -1527,7 +1574,7 @@ impl Pallet { /// /// This is always mandatory weight. fn register_weight(weight: Weight) { - >::register_extra_weight_unchecked( + frame_system::Pallet::::register_extra_weight_unchecked( weight, DispatchClass::Mandatory, ); @@ -1539,11 +1586,11 @@ impl Pallet { compute: ElectionCompute, ) -> Result, FeasibilityError> { let desired_targets = - Self::desired_targets().ok_or(FeasibilityError::SnapshotUnavailable)?; + DesiredTargets::::get().ok_or(FeasibilityError::SnapshotUnavailable)?; - let snapshot = Self::snapshot().ok_or(FeasibilityError::SnapshotUnavailable)?; - let round = Self::round(); - let minimum_untrusted_score = Self::minimum_untrusted_score(); + let snapshot = Snapshot::::get().ok_or(FeasibilityError::SnapshotUnavailable)?; + let round = Round::::get(); + let minimum_untrusted_score = MinimumUntrustedScore::::get(); Miner::::feasibility_check( raw_solution, @@ -1562,7 +1609,7 @@ impl Pallet { /// 3. Clear all snapshot data. fn rotate_round() { // Inc round. - >::mutate(|r| *r += 1); + Round::::mutate(|r| *r += 1); // Phase is off now. Self::phase_transition(Phase::Off); @@ -1581,7 +1628,7 @@ impl Pallet { // inexpensive (1 read of an empty vector). let _ = Self::finalize_signed_phase(); - >::take() + QueuedSolution::::take() .ok_or(ElectionError::::NothingQueued) .or_else(|_| { // default data provider bounds are unbounded. calling `instant_elect` with @@ -1602,14 +1649,14 @@ impl Pallet { }) .map(|ReadySolution { compute, score, supports }| { Self::deposit_event(Event::ElectionFinalized { compute, score }); - if Self::round() != 1 { + if Round::::get() != 1 { log!(info, "Finalized election round with compute {:?}.", compute); } supports }) .map_err(|err| { Self::deposit_event(Event::ElectionFailed); - if Self::round() != 1 { + if Round::::get() != 1 { log!(warn, "Failed to finalize election round. reason {:?}", err); } err @@ -1652,10 +1699,10 @@ impl Pallet { // - [`SignedSubmissionIndices`] is sorted by election score. fn try_state_signed_submissions_map() -> Result<(), TryRuntimeError> { let mut last_score: ElectionScore = Default::default(); - let indices = >::get(); + let indices = SignedSubmissionIndices::::get(); for (i, indice) in indices.iter().enumerate() { - let submission = >::get(indice.2); + let submission = SignedSubmissionsMap::::get(indice.2); if submission.is_none() { return Err( "All signed submissions indices must be part of the submissions map".into() @@ -1674,16 +1721,16 @@ impl Pallet { } } - if >::iter().nth(indices.len()).is_some() { + if SignedSubmissionsMap::::iter().nth(indices.len()).is_some() { return Err( "Signed submissions map length should be the same as the indices vec length".into() ) } - match >::get() { + match SignedSubmissionNextIndex::::get() { 0 => Ok(()), next => - if >::get(next).is_some() { + if SignedSubmissionsMap::::get(next).is_some() { return Err( "The next submissions index should not be in the submissions maps already" .into(), @@ -1697,10 +1744,10 @@ impl Pallet { // [`Phase::Off`] state check. Invariants: // - If phase is `Phase::Off`, [`Snapshot`] must be none. fn try_state_phase_off() -> Result<(), TryRuntimeError> { - match Self::current_phase().is_off() { + match CurrentPhase::::get().is_off() { false => Ok(()), true => - if >::get().is_some() { + if Snapshot::::get().is_some() { Err("Snapshot must be none when in Phase::Off".into()) } else { Ok(()) @@ -1719,7 +1766,7 @@ impl ElectionProviderBase for Pallet { impl ElectionProvider for Pallet { fn ongoing() -> bool { - match Self::current_phase() { + match CurrentPhase::::get() { Phase::Off => false, _ => true, } @@ -1771,12 +1818,12 @@ mod feasibility_check { fn snapshot_is_there() { ExtBuilder::default().build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let solution = raw_solution(); // kill `Snapshot`, `SnapshotMetadata` and `DesiredTargets` for the storage state to // be consistent, by using the `SnapshotWrapper` for the try_state checks to pass. - >::kill(); + SnapshotWrapper::::kill(); assert_noop!( MultiPhase::feasibility_check(solution, COMPUTE), @@ -1789,7 +1836,7 @@ mod feasibility_check { fn round() { ExtBuilder::default().build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let mut solution = raw_solution(); solution.round += 1; @@ -1804,13 +1851,13 @@ mod feasibility_check { fn desired_targets_gets_capped() { ExtBuilder::default().desired_targets(8).build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let raw = raw_solution(); assert_eq!(raw.solution.unique_targets().len(), 4); // desired_targets is capped to the number of targets which is 4 - assert_eq!(MultiPhase::desired_targets().unwrap(), 4); + assert_eq!(DesiredTargets::::get().unwrap(), 4); // It should succeed assert_ok!(MultiPhase::feasibility_check(raw, COMPUTE)); @@ -1821,13 +1868,13 @@ mod feasibility_check { fn less_than_desired_targets_fails() { ExtBuilder::default().desired_targets(8).build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let mut raw = raw_solution(); assert_eq!(raw.solution.unique_targets().len(), 4); // desired_targets is capped to the number of targets which is 4 - assert_eq!(MultiPhase::desired_targets().unwrap(), 4); + assert_eq!(DesiredTargets::::get().unwrap(), 4); // Force the number of winners to be bigger to fail raw.solution.votes1[0].1 = 4; @@ -1844,10 +1891,10 @@ mod feasibility_check { fn winner_indices() { ExtBuilder::default().desired_targets(2).build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let mut raw = raw_solution(); - assert_eq!(MultiPhase::snapshot().unwrap().targets.len(), 4); + assert_eq!(Snapshot::::get().unwrap().targets.len(), 4); // ----------------------------------------------------^^ valid range is [0..3]. // Swap all votes from 3 to 4. This will ensure that the number of unique winners will @@ -1878,10 +1925,10 @@ mod feasibility_check { // Should be caught in `solution.into_assignment`. ExtBuilder::default().desired_targets(2).build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let mut solution = raw_solution(); - assert_eq!(MultiPhase::snapshot().unwrap().voters.len(), 8); + assert_eq!(Snapshot::::get().unwrap().voters.len(), 8); // ----------------------------------------------------^^ valid range is [0..7]. // Check that there is an index 7 in votes1, and flip to 8. @@ -1905,10 +1952,10 @@ mod feasibility_check { fn voter_votes() { ExtBuilder::default().desired_targets(2).build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let mut solution = raw_solution(); - assert_eq!(MultiPhase::snapshot().unwrap().voters.len(), 8); + assert_eq!(Snapshot::::get().unwrap().voters.len(), 8); // ----------------------------------------------------^^ valid range is [0..7]. // First, check that voter at index 7 (40) actually voted for 3 (40) -- this is self @@ -1934,10 +1981,10 @@ mod feasibility_check { fn score() { ExtBuilder::default().desired_targets(2).build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let mut solution = raw_solution(); - assert_eq!(MultiPhase::snapshot().unwrap().voters.len(), 8); + assert_eq!(Snapshot::::get().unwrap().voters.len(), 8); // Simply faff with the score. solution.score.minimal_stake += 1; @@ -1972,30 +2019,30 @@ mod tests { // Signed Unsigned Elect Signed Unsigned Elect assert_eq!(System::block_number(), 0); - assert_eq!(MultiPhase::current_phase(), Phase::Off); - assert_eq!(MultiPhase::round(), 1); + assert_eq!(CurrentPhase::::get(), Phase::Off); + assert_eq!(Round::::get(), 1); roll_to(4); - assert_eq!(MultiPhase::current_phase(), Phase::Off); - assert!(MultiPhase::snapshot().is_none()); - assert_eq!(MultiPhase::round(), 1); + assert_eq!(CurrentPhase::::get(), Phase::Off); + assert!(Snapshot::::get().is_none()); + assert_eq!(Round::::get(), 1); roll_to_signed(); - assert_eq!(MultiPhase::current_phase(), Phase::Signed); + assert_eq!(CurrentPhase::::get(), Phase::Signed); assert_eq!( multi_phase_events(), vec![Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }] ); - assert!(MultiPhase::snapshot().is_some()); - assert_eq!(MultiPhase::round(), 1); + assert!(Snapshot::::get().is_some()); + assert_eq!(Round::::get(), 1); roll_to(24); - assert_eq!(MultiPhase::current_phase(), Phase::Signed); - assert!(MultiPhase::snapshot().is_some()); - assert_eq!(MultiPhase::round(), 1); + assert_eq!(CurrentPhase::::get(), Phase::Signed); + assert!(Snapshot::::get().is_some()); + assert_eq!(Round::::get(), 1); roll_to_unsigned(); - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, 25))); assert_eq!( multi_phase_events(), vec![ @@ -2007,35 +2054,35 @@ mod tests { }, ], ); - assert!(MultiPhase::snapshot().is_some()); + assert!(Snapshot::::get().is_some()); roll_to(29); - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); - assert!(MultiPhase::snapshot().is_some()); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, 25))); + assert!(Snapshot::::get().is_some()); roll_to(30); - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); - assert!(MultiPhase::snapshot().is_some()); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, 25))); + assert!(Snapshot::::get().is_some()); // We close when upstream tells us to elect. roll_to(32); - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); - assert!(MultiPhase::snapshot().is_some()); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, 25))); + assert!(Snapshot::::get().is_some()); assert_ok!(MultiPhase::elect()); - assert!(MultiPhase::current_phase().is_off()); - assert!(MultiPhase::snapshot().is_none()); - assert_eq!(MultiPhase::round(), 2); + assert!(CurrentPhase::::get().is_off()); + assert!(Snapshot::::get().is_none()); + assert_eq!(Round::::get(), 2); roll_to(44); - assert!(MultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); roll_to(55); - assert!(MultiPhase::current_phase().is_unsigned_open_at(55)); + assert!(CurrentPhase::::get().is_unsigned_open_at(55)); assert_eq!( multi_phase_events(), @@ -2074,22 +2121,22 @@ mod tests { fn signed_phase_void() { ExtBuilder::default().phases(0, 10).build_and_execute(|| { roll_to(15); - assert!(MultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); roll_to(19); - assert!(MultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); roll_to(20); - assert!(MultiPhase::current_phase().is_unsigned_open_at(20)); - assert!(MultiPhase::snapshot().is_some()); + assert!(CurrentPhase::::get().is_unsigned_open_at(20)); + assert!(Snapshot::::get().is_some()); roll_to(30); - assert!(MultiPhase::current_phase().is_unsigned_open_at(20)); + assert!(CurrentPhase::::get().is_unsigned_open_at(20)); assert_ok!(MultiPhase::elect()); - assert!(MultiPhase::current_phase().is_off()); - assert!(MultiPhase::snapshot().is_none()); + assert!(CurrentPhase::::get().is_off()); + assert!(Snapshot::::get().is_none()); assert_eq!( multi_phase_events(), @@ -2121,22 +2168,22 @@ mod tests { fn unsigned_phase_void() { ExtBuilder::default().phases(10, 0).build_and_execute(|| { roll_to(15); - assert!(MultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); roll_to(19); - assert!(MultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); - assert!(MultiPhase::snapshot().is_some()); + assert!(CurrentPhase::::get().is_signed()); + assert!(Snapshot::::get().is_some()); roll_to(30); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); assert_ok!(MultiPhase::elect()); - assert!(MultiPhase::current_phase().is_off()); - assert!(MultiPhase::snapshot().is_none()); + assert!(CurrentPhase::::get().is_off()); + assert!(Snapshot::::get().is_none()); assert_eq!( multi_phase_events(), @@ -2160,21 +2207,21 @@ mod tests { fn both_phases_void() { ExtBuilder::default().phases(0, 0).build_and_execute(|| { roll_to(15); - assert!(MultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); roll_to(19); - assert!(MultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); roll_to(20); - assert!(MultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); roll_to(30); - assert!(MultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); // This module is now only capable of doing on-chain backup. assert_ok!(MultiPhase::elect()); - assert!(MultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); assert_eq!( multi_phase_events(), @@ -2204,8 +2251,8 @@ mod tests { multi_phase_events(), vec![Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }] ); - assert_eq!(MultiPhase::current_phase(), Phase::Signed); - assert_eq!(MultiPhase::round(), 1); + assert_eq!(CurrentPhase::::get(), Phase::Signed); + assert_eq!(Round::::get(), 1); // An unexpected call to elect. assert_ok!(MultiPhase::elect()); @@ -2223,11 +2270,11 @@ mod tests { ], ); // All storage items must be cleared. - assert_eq!(MultiPhase::round(), 2); - assert!(MultiPhase::snapshot().is_none()); - assert!(MultiPhase::snapshot_metadata().is_none()); - assert!(MultiPhase::desired_targets().is_none()); - assert!(MultiPhase::queued_solution().is_none()); + assert_eq!(Round::::get(), 2); + assert!(Snapshot::::get().is_none()); + assert!(SnapshotMetadata::::get().is_none()); + assert!(DesiredTargets::::get().is_none()); + assert!(QueuedSolution::::get().is_none()); assert!(MultiPhase::signed_submissions().is_empty()); }) } @@ -2243,8 +2290,8 @@ mod tests { multi_phase_events(), vec![Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }] ); - assert_eq!(MultiPhase::current_phase(), Phase::Signed); - assert_eq!(MultiPhase::round(), 1); + assert_eq!(CurrentPhase::::get(), Phase::Signed); + assert_eq!(Round::::get(), 1); // fill the queue with signed submissions for s in 0..SignedMaxSubmissions::get() { @@ -2262,11 +2309,11 @@ mod tests { assert_ok!(MultiPhase::elect()); // all storage items must be cleared. - assert_eq!(MultiPhase::round(), 2); - assert!(MultiPhase::snapshot().is_none()); - assert!(MultiPhase::snapshot_metadata().is_none()); - assert!(MultiPhase::desired_targets().is_none()); - assert!(MultiPhase::queued_solution().is_none()); + assert_eq!(Round::::get(), 2); + assert!(Snapshot::::get().is_none()); + assert!(SnapshotMetadata::::get().is_none()); + assert!(DesiredTargets::::get().is_none()); + assert!(QueuedSolution::::get().is_none()); assert!(MultiPhase::signed_submissions().is_empty()); assert_eq!( @@ -2321,7 +2368,7 @@ mod tests { fn check_events_with_compute_signed() { ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let solution = raw_solution(); assert_ok!(MultiPhase::submit( @@ -2369,23 +2416,23 @@ mod tests { fn check_events_with_compute_unsigned() { ExtBuilder::default().build_and_execute(|| { roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); // ensure we have snapshots in place. - assert!(MultiPhase::snapshot().is_some()); - assert_eq!(MultiPhase::desired_targets().unwrap(), 2); + assert!(Snapshot::::get().is_some()); + assert_eq!(DesiredTargets::::get().unwrap(), 2); // mine seq_phragmen solution with 2 iters. let (solution, witness, _) = MultiPhase::mine_solution().unwrap(); // ensure this solution is valid. - assert!(MultiPhase::queued_solution().is_none()); + assert!(QueuedSolution::::get().is_none()); assert_ok!(MultiPhase::submit_unsigned( crate::mock::RuntimeOrigin::none(), Box::new(solution), witness )); - assert!(MultiPhase::queued_solution().is_some()); + assert!(QueuedSolution::::get().is_some()); assert_ok!(MultiPhase::elect()); @@ -2425,10 +2472,10 @@ mod tests { fn fallback_strategy_works() { ExtBuilder::default().onchain_fallback(true).build_and_execute(|| { roll_to_unsigned(); - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, 25))); // Zilch solutions thus far, but we get a result. - assert!(MultiPhase::queued_solution().is_none()); + assert!(QueuedSolution::::get().is_none()); let supports = MultiPhase::elect().unwrap(); assert_eq!( @@ -2467,15 +2514,15 @@ mod tests { ExtBuilder::default().onchain_fallback(false).build_and_execute(|| { roll_to_unsigned(); - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, 25))); // Zilch solutions thus far. - assert!(MultiPhase::queued_solution().is_none()); + assert!(QueuedSolution::::get().is_none()); assert_eq!(MultiPhase::elect().unwrap_err(), ElectionError::Fallback("NoFallback.")); // phase is now emergency. - assert_eq!(MultiPhase::current_phase(), Phase::Emergency); + assert_eq!(CurrentPhase::::get(), Phase::Emergency); // snapshot is still there until election finalizes. - assert!(MultiPhase::snapshot().is_some()); + assert!(Snapshot::::get().is_some()); assert_eq!( multi_phase_events(), @@ -2501,16 +2548,16 @@ mod tests { fn governance_fallback_works() { ExtBuilder::default().onchain_fallback(false).build_and_execute(|| { roll_to_unsigned(); - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, 25))); // Zilch solutions thus far. - assert!(MultiPhase::queued_solution().is_none()); + assert!(QueuedSolution::::get().is_none()); assert_eq!(MultiPhase::elect().unwrap_err(), ElectionError::Fallback("NoFallback.")); // phase is now emergency. - assert_eq!(MultiPhase::current_phase(), Phase::Emergency); - assert!(MultiPhase::queued_solution().is_none()); - assert!(MultiPhase::snapshot().is_some()); + assert_eq!(CurrentPhase::::get(), Phase::Emergency); + assert!(QueuedSolution::::get().is_none()); + assert!(Snapshot::::get().is_some()); // no single account can trigger this assert_noop!( @@ -2521,10 +2568,10 @@ mod tests { // only root can assert_ok!(MultiPhase::governance_fallback(RuntimeOrigin::root(), None, None)); // something is queued now - assert!(MultiPhase::queued_solution().is_some()); + assert!(QueuedSolution::::get().is_some()); // next election call with fix everything.; assert!(MultiPhase::elect().is_ok()); - assert_eq!(MultiPhase::current_phase(), Phase::Off); + assert_eq!(CurrentPhase::::get(), Phase::Off); assert_eq!( multi_phase_events(), @@ -2568,11 +2615,11 @@ mod tests { // Signed phase failed to open. roll_to(15); - assert_eq!(MultiPhase::current_phase(), Phase::Off); + assert_eq!(CurrentPhase::::get(), Phase::Off); // Unsigned phase failed to open. roll_to(25); - assert_eq!(MultiPhase::current_phase(), Phase::Off); + assert_eq!(CurrentPhase::::get(), Phase::Off); // On-chain backup works though. let supports = MultiPhase::elect().unwrap(); @@ -2607,16 +2654,16 @@ mod tests { // Signed phase failed to open. roll_to(15); - assert_eq!(MultiPhase::current_phase(), Phase::Off); + assert_eq!(CurrentPhase::::get(), Phase::Off); // Unsigned phase failed to open. roll_to(25); - assert_eq!(MultiPhase::current_phase(), Phase::Off); + assert_eq!(CurrentPhase::::get(), Phase::Off); roll_to(29); let err = MultiPhase::elect().unwrap_err(); assert_eq!(err, ElectionError::Fallback("NoFallback.")); - assert_eq!(MultiPhase::current_phase(), Phase::Emergency); + assert_eq!(CurrentPhase::::get(), Phase::Emergency); assert_eq!( multi_phase_events(), @@ -2640,10 +2687,10 @@ mod tests { // Signed phase opens just fine. roll_to_signed(); - assert_eq!(MultiPhase::current_phase(), Phase::Signed); + assert_eq!(CurrentPhase::::get(), Phase::Signed); assert_eq!( - MultiPhase::snapshot_metadata().unwrap(), + SnapshotMetadata::::get().unwrap(), SolutionOrSnapshotSize { voters: 2, targets: 4 } ); }) @@ -2653,7 +2700,7 @@ mod tests { fn untrusted_score_verification_is_respected() { ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert_eq!(MultiPhase::current_phase(), Phase::Signed); + assert_eq!(CurrentPhase::::get(), Phase::Signed); // set the solution balancing to get the desired score. crate::mock::Balancing::set(Some(BalancingConfig { iterations: 2, tolerance: 0 })); @@ -2662,13 +2709,13 @@ mod tests { // Default solution's score. assert!(matches!(solution.score, ElectionScore { minimal_stake: 50, .. })); - >::put(ElectionScore { + MinimumUntrustedScore::::put(ElectionScore { minimal_stake: 49, ..Default::default() }); assert_ok!(MultiPhase::feasibility_check(solution.clone(), ElectionCompute::Signed)); - >::put(ElectionScore { + MinimumUntrustedScore::::put(ElectionScore { minimal_stake: 51, ..Default::default() }); diff --git a/substrate/frame/election-provider-multi-phase/src/mock.rs b/substrate/frame/election-provider-multi-phase/src/mock.rs index 4532185b959c..32a099e1a26f 100644 --- a/substrate/frame/election-provider-multi-phase/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/src/mock.rs @@ -92,12 +92,12 @@ pub fn roll_to(n: BlockNumber) { } pub fn roll_to_unsigned() { - while !matches!(MultiPhase::current_phase(), Phase::Unsigned(_)) { + while !matches!(CurrentPhase::::get(), Phase::Unsigned(_)) { roll_to(System::block_number() + 1); } } pub fn roll_to_signed() { - while !matches!(MultiPhase::current_phase(), Phase::Signed) { + while !matches!(CurrentPhase::::get(), Phase::Signed) { roll_to(System::block_number() + 1); } } @@ -112,9 +112,9 @@ pub fn roll_to_with_ocw(n: BlockNumber) { } pub fn roll_to_round(n: u32) { - assert!(MultiPhase::round() <= n); + assert!(Round::::get() <= n); - while MultiPhase::round() != n { + while Round::::get() != n { roll_to_signed(); frame_support::assert_ok!(MultiPhase::elect()); } @@ -136,7 +136,7 @@ pub struct TrimHelpers { /// /// Assignments are pre-sorted in reverse order of stake. pub fn trim_helpers() -> TrimHelpers { - let RoundSnapshot { voters, targets } = MultiPhase::snapshot().unwrap(); + let RoundSnapshot { voters, targets } = Snapshot::::get().unwrap(); let stakes: std::collections::HashMap<_, _> = voters.iter().map(|(id, stake, _)| (*id, *stake)).collect(); @@ -150,7 +150,7 @@ pub fn trim_helpers() -> TrimHelpers { let voter_index = helpers::voter_index_fn_owned::(cache); let target_index = helpers::target_index_fn::(&targets); - let desired_targets = MultiPhase::desired_targets().unwrap(); + let desired_targets = crate::DesiredTargets::::get().unwrap(); let ElectionResult::<_, SolutionAccuracyOf> { mut assignments, .. } = seq_phragmen(desired_targets as usize, targets.clone(), voters.clone(), None).unwrap(); @@ -176,8 +176,8 @@ pub fn trim_helpers() -> TrimHelpers { /// /// This is a good example of what an offchain miner would do. pub fn raw_solution() -> RawSolution> { - let RoundSnapshot { voters, targets } = MultiPhase::snapshot().unwrap(); - let desired_targets = MultiPhase::desired_targets().unwrap(); + let RoundSnapshot { voters, targets } = Snapshot::::get().unwrap(); + let desired_targets = crate::DesiredTargets::::get().unwrap(); let ElectionResult::<_, SolutionAccuracyOf> { winners: _, assignments } = seq_phragmen(desired_targets as usize, targets.clone(), voters.clone(), None).unwrap(); @@ -193,14 +193,14 @@ pub fn raw_solution() -> RawSolution> { to_supports(&staked).evaluate() }; let solution = - >::from_assignment(&assignments, &voter_index, &target_index).unwrap(); + SolutionOf::::from_assignment(&assignments, &voter_index, &target_index).unwrap(); - let round = MultiPhase::round(); + let round = Round::::get(); RawSolution { solution, score, round } } pub fn witness() -> SolutionOrSnapshotSize { - MultiPhase::snapshot() + Snapshot::::get() .map(|snap| SolutionOrSnapshotSize { voters: snap.voters.len() as u32, targets: snap.targets.len() as u32, diff --git a/substrate/frame/election-provider-multi-phase/src/signed.rs b/substrate/frame/election-provider-multi-phase/src/signed.rs index fe07e477e1d5..c685791bbdd9 100644 --- a/substrate/frame/election-provider-multi-phase/src/signed.rs +++ b/substrate/frame/election-provider-multi-phase/src/signed.rs @@ -22,7 +22,7 @@ use core::marker::PhantomData; use crate::{ unsigned::MinerConfig, Config, ElectionCompute, Pallet, QueuedSolution, RawSolution, ReadySolution, SignedSubmissionIndices, SignedSubmissionNextIndex, SignedSubmissionsMap, - SolutionOf, SolutionOrSnapshotSize, Weight, WeightInfo, + SnapshotMetadata, SolutionOf, SolutionOrSnapshotSize, Weight, WeightInfo, }; use alloc::{ collections::{btree_map::BTreeMap, btree_set::BTreeSet}, @@ -405,7 +405,7 @@ impl Pallet { let mut weight = T::DbWeight::get().reads(1); let SolutionOrSnapshotSize { voters, targets } = - Self::snapshot_metadata().unwrap_or_default(); + SnapshotMetadata::::get().unwrap_or_default(); while let Some(best) = all_submissions.pop_last() { log!( @@ -418,7 +418,8 @@ impl Pallet { let active_voters = raw_solution.solution.voter_count() as u32; let feasibility_weight = { // defensive only: at the end of signed phase, snapshot will exits. - let desired_targets = Self::desired_targets().defensive_unwrap_or_default(); + let desired_targets = + crate::DesiredTargets::::get().defensive_unwrap_or_default(); T::WeightInfo::feasibility_check(voters, targets, active_voters, desired_targets) }; @@ -495,7 +496,7 @@ impl Pallet { call_fee: BalanceOf, ) { // write this ready solution. - >::put(ready_solution); + QueuedSolution::::put(ready_solution); let reward = T::SignedRewardBase::get(); // emit reward event @@ -565,8 +566,8 @@ impl Pallet { mod tests { use super::*; use crate::{ - mock::*, ElectionBoundsBuilder, ElectionCompute, ElectionError, Error, Event, Perbill, - Phase, + mock::*, CurrentPhase, ElectionBoundsBuilder, ElectionCompute, ElectionError, Error, Event, + Perbill, Phase, Round, }; use frame_support::{assert_noop, assert_ok, assert_storage_noop}; use sp_runtime::Percent; @@ -576,17 +577,17 @@ mod tests { ExtBuilder::default().build_and_execute(|| { // roll to a few rounds ahead. roll_to_round(5); - assert_eq!(MultiPhase::round(), 5); + assert_eq!(Round::::get(), 5); roll_to_signed(); - assert_eq!(MultiPhase::current_phase(), Phase::Signed); + assert_eq!(CurrentPhase::::get(), Phase::Signed); // create a temp snapshot only for this test. MultiPhase::create_snapshot().unwrap(); let mut solution = raw_solution(); // try a solution prepared in a previous round. - solution.round = MultiPhase::round() - 1; + solution.round = Round::::get() - 1; assert_noop!( MultiPhase::submit(RuntimeOrigin::signed(10), Box::new(solution)), @@ -596,7 +597,7 @@ mod tests { // try a solution prepared in a later round (not expected to happen, but in any case). MultiPhase::create_snapshot().unwrap(); let mut solution = raw_solution(); - solution.round = MultiPhase::round() + 1; + solution.round = Round::::get() + 1; assert_noop!( MultiPhase::submit(RuntimeOrigin::signed(10), Box::new(solution)), @@ -609,7 +610,7 @@ mod tests { fn cannot_submit_too_early() { ExtBuilder::default().build_and_execute(|| { roll_to(2); - assert_eq!(MultiPhase::current_phase(), Phase::Off); + assert_eq!(CurrentPhase::::get(), Phase::Off); // create a temp snapshot only for this test. MultiPhase::create_snapshot().unwrap(); @@ -621,9 +622,9 @@ mod tests { ); // make sure invariants hold true and post-test try state checks to pass. - >::kill(); - >::kill(); - >::kill(); + crate::Snapshot::::kill(); + crate::SnapshotMetadata::::kill(); + crate::DesiredTargets::::kill(); }) } @@ -679,7 +680,7 @@ mod tests { fn should_pay_deposit() { ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let solution = raw_solution(); assert_eq!(balances(&99), (100, 0)); @@ -707,7 +708,7 @@ mod tests { fn good_solution_is_rewarded() { ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let solution = raw_solution(); assert_eq!(balances(&99), (100, 0)); @@ -737,7 +738,7 @@ mod tests { fn bad_solution_is_slashed() { ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let mut solution = raw_solution(); assert_eq!(balances(&99), (100, 0)); @@ -772,7 +773,7 @@ mod tests { fn suppressed_solution_gets_bond_back() { ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let mut solution = raw_solution(); assert_eq!(balances(&99), (100, 0)); @@ -818,7 +819,7 @@ mod tests { fn cannot_submit_worse_with_full_queue() { ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); for s in 0..SignedMaxSubmissions::get() { // score is always getting better @@ -866,7 +867,7 @@ mod tests { .signed_base_deposit(1000, true, Percent::from_percent(0)) .build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); check_progressive_base_fee(&constant); }); @@ -876,7 +877,7 @@ mod tests { .signed_base_deposit(1000, true, Percent::from_percent(10)) .build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); check_progressive_base_fee(&progression_10); }); @@ -886,7 +887,7 @@ mod tests { .signed_base_deposit(1000, true, Percent::from_percent(40)) .build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); check_progressive_base_fee(&progression_40); }); @@ -896,7 +897,7 @@ mod tests { fn call_fee_refund_is_limited_by_signed_max_refunds() { ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); assert_eq!(SignedMaxRefunds::get(), 1); assert!(SignedMaxSubmissions::get() > 2); @@ -977,7 +978,7 @@ mod tests { .better_signed_threshold(Perbill::from_percent(20)) .build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let mut solution = RawSolution { score: ElectionScore { @@ -1038,7 +1039,7 @@ mod tests { fn weakest_is_removed_if_better_provided() { ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); for s in 0..SignedMaxSubmissions::get() { let account = 99 + s as u64; @@ -1085,7 +1086,7 @@ mod tests { fn replace_weakest_by_score_works() { ExtBuilder::default().signed_max_submission(3).build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); for s in 1..SignedMaxSubmissions::get() { // score is always getting better @@ -1132,7 +1133,7 @@ mod tests { fn early_ejected_solution_gets_bond_back() { ExtBuilder::default().signed_deposit(2, 0, 0).build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); for s in 0..SignedMaxSubmissions::get() { // score is always getting better @@ -1164,7 +1165,7 @@ mod tests { // because in ordering of solutions, an older solution has higher priority and should stay. ExtBuilder::default().signed_max_submission(3).build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); for i in 0..SignedMaxSubmissions::get() { let solution = RawSolution { @@ -1199,7 +1200,7 @@ mod tests { // because in ordering of solutions, an older solution has higher priority and should stay. ExtBuilder::default().signed_max_submission(3).build_and_execute(|| { roll_to(15); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let solution = RawSolution { score: ElectionScore { minimal_stake: 5, ..Default::default() }, @@ -1251,7 +1252,7 @@ mod tests { // because in ordering of solutions, an older solution has higher priority and should stay. ExtBuilder::default().signed_max_submission(3).build_and_execute(|| { roll_to(15); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); for i in 0..SignedMaxSubmissions::get() { roll_to((15 + i).into()); @@ -1311,7 +1312,7 @@ mod tests { // - suppressed_solution_gets_bond_back ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); assert_eq!(balances(&99), (100, 0)); assert_eq!(balances(&999), (100, 0)); @@ -1380,7 +1381,7 @@ mod tests { .mock_weight_info(MockedWeightInfo::Basic) .build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let (raw, witness, _) = MultiPhase::mine_solution().unwrap(); let solution_weight = ::solution_weight( @@ -1414,7 +1415,7 @@ mod tests { fn insufficient_deposit_does_not_store_submission() { ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let solution = raw_solution(); @@ -1434,7 +1435,7 @@ mod tests { fn insufficient_deposit_with_full_queue_works_properly() { ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); for s in 0..SignedMaxSubmissions::get() { // score is always getting better @@ -1480,7 +1481,7 @@ mod tests { fn finalize_signed_phase_is_idempotent_given_submissions() { ExtBuilder::default().build_and_execute(|| { roll_to_signed(); - assert!(MultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); let solution = raw_solution(); diff --git a/substrate/frame/election-provider-multi-phase/src/unsigned.rs b/substrate/frame/election-provider-multi-phase/src/unsigned.rs index 728ab9302389..4c56f02db526 100644 --- a/substrate/frame/election-provider-multi-phase/src/unsigned.rs +++ b/substrate/frame/election-provider-multi-phase/src/unsigned.rs @@ -18,8 +18,9 @@ //! The unsigned phase, and its miner. use crate::{ - helpers, Call, Config, ElectionCompute, Error, FeasibilityError, Pallet, RawSolution, - ReadySolution, RoundSnapshot, SolutionAccuracyOf, SolutionOf, SolutionOrSnapshotSize, Weight, + helpers, Call, Config, CurrentPhase, DesiredTargets, ElectionCompute, Error, FeasibilityError, + Pallet, QueuedSolution, RawSolution, ReadySolution, Round, RoundSnapshot, Snapshot, + SolutionAccuracyOf, SolutionOf, SolutionOrSnapshotSize, Weight, }; use alloc::{boxed::Box, vec::Vec}; use codec::Encode; @@ -188,15 +189,15 @@ impl Pallet { MinerError, > { let RoundSnapshot { voters, targets } = - Self::snapshot().ok_or(MinerError::SnapshotUnAvailable)?; - let desired_targets = Self::desired_targets().ok_or(MinerError::SnapshotUnAvailable)?; + Snapshot::::get().ok_or(MinerError::SnapshotUnAvailable)?; + let desired_targets = DesiredTargets::::get().ok_or(MinerError::SnapshotUnAvailable)?; let (solution, score, size, is_trimmed) = Miner::::mine_solution_with_snapshot::( voters, targets, desired_targets, )?; - let round = Self::round(); + let round = Round::::get(); Ok((RawSolution { solution, score, round }, size, is_trimmed)) } @@ -370,21 +371,24 @@ impl Pallet { raw_solution: &RawSolution>, ) -> DispatchResult { // ensure solution is timely. Don't panic yet. This is a cheap check. - ensure!(Self::current_phase().is_unsigned_open(), Error::::PreDispatchEarlySubmission); + ensure!( + CurrentPhase::::get().is_unsigned_open(), + Error::::PreDispatchEarlySubmission + ); // ensure round is current - ensure!(Self::round() == raw_solution.round, Error::::OcwCallWrongEra); + ensure!(Round::::get() == raw_solution.round, Error::::OcwCallWrongEra); // ensure correct number of winners. ensure!( - Self::desired_targets().unwrap_or_default() == + DesiredTargets::::get().unwrap_or_default() == raw_solution.solution.unique_targets().len() as u32, Error::::PreDispatchWrongWinnerCount, ); // ensure score is being improved. Panic henceforth. ensure!( - Self::queued_solution() + QueuedSolution::::get() .map_or(true, |q: ReadySolution<_, _>| raw_solution.score > q.score), Error::::PreDispatchWeakSubmission, ); @@ -1043,7 +1047,7 @@ mod tests { }; // initial - assert_eq!(MultiPhase::current_phase(), Phase::Off); + assert_eq!(CurrentPhase::::get(), Phase::Off); assert!(matches!( ::validate_unsigned( TransactionSource::Local, @@ -1059,7 +1063,7 @@ mod tests { // signed roll_to_signed(); - assert_eq!(MultiPhase::current_phase(), Phase::Signed); + assert_eq!(CurrentPhase::::get(), Phase::Signed); assert!(matches!( ::validate_unsigned( TransactionSource::Local, @@ -1075,7 +1079,7 @@ mod tests { // unsigned roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); assert!(::validate_unsigned( TransactionSource::Local, @@ -1086,7 +1090,7 @@ mod tests { // unsigned -- but not enabled. MultiPhase::phase_transition(Phase::Unsigned((false, 25))); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); assert!(matches!( ::validate_unsigned( TransactionSource::Local, @@ -1106,7 +1110,7 @@ mod tests { fn validate_unsigned_retracts_low_score() { ExtBuilder::default().desired_targets(0).build_and_execute(|| { roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); let solution = RawSolution:: { score: ElectionScore { minimal_stake: 5, ..Default::default() }, @@ -1130,7 +1134,7 @@ mod tests { score: ElectionScore { minimal_stake: 10, ..Default::default() }, ..Default::default() }; - >::put(ready); + QueuedSolution::::put(ready); // won't work anymore. assert!(matches!( @@ -1152,7 +1156,7 @@ mod tests { fn validate_unsigned_retracts_incorrect_winner_count() { ExtBuilder::default().desired_targets(1).build_and_execute(|| { roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); let raw = RawSolution:: { score: ElectionScore { minimal_stake: 5, ..Default::default() }, @@ -1181,7 +1185,7 @@ mod tests { .desired_targets(0) .build_and_execute(|| { roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); let solution = RawSolution:: { score: ElectionScore { minimal_stake: 5, ..Default::default() }, @@ -1212,7 +1216,7 @@ mod tests { fn unfeasible_solution_panics() { ExtBuilder::default().build_and_execute(|| { roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); // This is in itself an invalid BS solution. let solution = RawSolution:: { @@ -1234,7 +1238,7 @@ mod tests { fn wrong_witness_panics() { ExtBuilder::default().build_and_execute(|| { roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); // This solution is unfeasible as well, but we won't even get there. let solution = RawSolution:: { @@ -1258,23 +1262,23 @@ mod tests { fn miner_works() { ExtBuilder::default().build_and_execute(|| { roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); // ensure we have snapshots in place. - assert!(MultiPhase::snapshot().is_some()); - assert_eq!(MultiPhase::desired_targets().unwrap(), 2); + assert!(Snapshot::::get().is_some()); + assert_eq!(DesiredTargets::::get().unwrap(), 2); // mine seq_phragmen solution with 2 iters. let (solution, witness, _) = MultiPhase::mine_solution().unwrap(); // ensure this solution is valid. - assert!(MultiPhase::queued_solution().is_none()); + assert!(QueuedSolution::::get().is_none()); assert_ok!(MultiPhase::submit_unsigned( RuntimeOrigin::none(), Box::new(solution), witness )); - assert!(MultiPhase::queued_solution().is_some()); + assert!(QueuedSolution::::get().is_some()); assert_eq!( multi_phase_events(), vec![ @@ -1301,7 +1305,7 @@ mod tests { .mock_weight_info(crate::mock::MockedWeightInfo::Basic) .build_and_execute(|| { roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); let (raw, witness, t) = MultiPhase::mine_solution().unwrap(); let solution_weight = ::solution_weight( @@ -1337,7 +1341,7 @@ mod tests { let (mut ext, _) = ExtBuilder::default().desired_targets(8).build_offchainify(0); ext.execute_with(|| { roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); // Force the number of winners to be bigger to fail let (mut solution, _, _) = MultiPhase::mine_solution().unwrap(); @@ -1363,8 +1367,8 @@ mod tests { .add_voter(9, 1, bounded_vec![10]) .build_and_execute(|| { roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); - assert_eq!(MultiPhase::desired_targets().unwrap(), 1); + assert!(CurrentPhase::::get().is_unsigned()); + assert_eq!(DesiredTargets::::get().unwrap(), 1); // an initial solution let result = ElectionResult { @@ -1379,8 +1383,8 @@ mod tests { ], }; - let RoundSnapshot { voters, targets } = MultiPhase::snapshot().unwrap(); - let desired_targets = MultiPhase::desired_targets().unwrap(); + let RoundSnapshot { voters, targets } = Snapshot::::get().unwrap(); + let desired_targets = DesiredTargets::::get().unwrap(); let (raw, score, witness, _) = Miner::::prepare_election_result_with_snapshot( @@ -1390,14 +1394,14 @@ mod tests { desired_targets, ) .unwrap(); - let solution = RawSolution { solution: raw, score, round: MultiPhase::round() }; + let solution = RawSolution { solution: raw, score, round: Round::::get() }; assert_ok!(MultiPhase::unsigned_pre_dispatch_checks(&solution)); assert_ok!(MultiPhase::submit_unsigned( RuntimeOrigin::none(), Box::new(solution), witness )); - assert_eq!(MultiPhase::queued_solution().unwrap().score.minimal_stake, 12); + assert_eq!(QueuedSolution::::get().unwrap().score.minimal_stake, 12); // trial 1: a solution who's minimal stake is 10, i.e. worse than the first solution // of 12. @@ -1415,7 +1419,7 @@ mod tests { desired_targets, ) .unwrap(); - let solution = RawSolution { solution: raw, score, round: MultiPhase::round() }; + let solution = RawSolution { solution: raw, score, round: Round::::get() }; // 10 is not better than 12 assert_eq!(solution.score.minimal_stake, 10); // submitting this will actually panic. @@ -1445,7 +1449,7 @@ mod tests { desired_targets, ) .unwrap(); - let solution = RawSolution { solution: raw, score, round: MultiPhase::round() }; + let solution = RawSolution { solution: raw, score, round: Round::::get() }; // 12 is not better than 12. We need score of at least 13 to be accepted. assert_eq!(solution.score.minimal_stake, 12); // submitting this will panic. @@ -1472,7 +1476,7 @@ mod tests { desired_targets, ) .unwrap(); - let solution = RawSolution { solution: raw, score, round: MultiPhase::round() }; + let solution = RawSolution { solution: raw, score, round: Round::::get() }; assert_eq!(solution.score.minimal_stake, 13); // this should work @@ -1505,7 +1509,7 @@ mod tests { desired_targets, ) .unwrap(); - let solution = RawSolution { solution: raw, score, round: MultiPhase::round() }; + let solution = RawSolution { solution: raw, score, round: Round::::get() }; assert_eq!(solution.score.minimal_stake, 17); // and it is fine @@ -1525,7 +1529,7 @@ mod tests { let offchain_repeat = ::OffchainRepeat::get(); roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); // first execution -- okay. assert!(MultiPhase::ensure_offchain_repeat_frequency(25).is_ok()); @@ -1566,7 +1570,7 @@ mod tests { let last_block = StorageValueRef::persistent(OFFCHAIN_LAST_BLOCK); roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); // initially, the lock is not set. assert!(guard.get::().unwrap().is_none()); @@ -1587,7 +1591,7 @@ mod tests { let (mut ext, pool) = ExtBuilder::default().build_offchainify(0); ext.execute_with(|| { roll_to_unsigned(); - assert!(MultiPhase::current_phase().is_unsigned()); + assert!(CurrentPhase::::get().is_unsigned()); // artificially set the value, as if another thread is mid-way. let mut lock = StorageLock::>::with_block_deadline( @@ -1615,7 +1619,7 @@ mod tests { let (mut ext, pool) = ExtBuilder::default().build_offchainify(0); ext.execute_with(|| { roll_to_unsigned(); - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, 25))); // we must clear the offchain storage to ensure the offchain execution check doesn't get // in the way. @@ -1647,7 +1651,7 @@ mod tests { roll_to(BLOCK); // we are on the first block of the unsigned phase - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, BLOCK))); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, BLOCK))); assert!( !ocw_solution_exists::(), @@ -1730,7 +1734,7 @@ mod tests { let offchain_repeat = ::OffchainRepeat::get(); roll_to(BLOCK); - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, BLOCK))); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, BLOCK))); // we must clear the offchain storage to ensure the offchain execution check doesn't get // in the way. @@ -1768,7 +1772,7 @@ mod tests { let offchain_repeat = ::OffchainRepeat::get(); roll_to(BLOCK); - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, BLOCK))); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, BLOCK))); // we must clear the offchain storage to ensure the offchain execution check doesn't get // in the way. @@ -1809,7 +1813,7 @@ mod tests { let (mut ext, pool) = ExtBuilder::default().build_offchainify(0); ext.execute_with(|| { roll_to_with_ocw(25); - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, 25))); // OCW must have submitted now let encoded = pool.read().transactions[0].clone(); @@ -1824,10 +1828,10 @@ mod tests { let (mut ext, pool) = ExtBuilder::default().build_offchainify(0); ext.execute_with(|| { roll_to_with_ocw(25); - assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!(CurrentPhase::::get(), Phase::Unsigned((true, 25))); // OCW must have submitted now // now, before we check the call, update the round - >::mutate(|round| *round += 1); + crate::Round::::mutate(|round| *round += 1); let encoded = pool.read().transactions[0].clone(); let extrinsic = Extrinsic::decode(&mut &*encoded).unwrap(); diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs index aaffbb6681cd..0dc202ff2115 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs @@ -28,6 +28,8 @@ use sp_runtime::Perbill; use crate::mock::RuntimeOrigin; +use pallet_election_provider_multi_phase::CurrentPhase; + // syntactic sugar for logging. #[macro_export] macro_rules! log { @@ -46,7 +48,7 @@ fn log_current_time() { System::block_number(), Session::current_index(), Staking::current_era(), - ElectionProviderMultiPhase::current_phase(), + CurrentPhase::::get(), Now::::get() ); } @@ -58,16 +60,16 @@ fn block_progression_works() { execute_with(ext, || { assert_eq!(active_era(), 0); assert_eq!(Session::current_index(), 0); - assert!(ElectionProviderMultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); assert!(start_next_active_era(pool_state.clone()).is_ok()); assert_eq!(active_era(), 1); assert_eq!(Session::current_index(), >::get()); - assert!(ElectionProviderMultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); roll_to_epm_signed(); - assert!(ElectionProviderMultiPhase::current_phase().is_signed()); + assert!(CurrentPhase::::get().is_signed()); }); let (ext, pool_state, _) = ExtBuilder::default().build_offchainify(); @@ -75,11 +77,11 @@ fn block_progression_works() { execute_with(ext, || { assert_eq!(active_era(), 0); assert_eq!(Session::current_index(), 0); - assert!(ElectionProviderMultiPhase::current_phase().is_off()); + assert!(CurrentPhase::::get().is_off()); assert!(start_next_active_era_delayed_solution(pool_state).is_ok()); // if the solution is delayed, EPM will end up in emergency mode.. - assert!(ElectionProviderMultiPhase::current_phase().is_emergency()); + assert!(CurrentPhase::::get().is_emergency()); // .. era won't progress.. assert_eq!(active_era(), 0); // .. but session does. @@ -103,7 +105,7 @@ fn offchainify_works() { // not delayed. for _ in 0..100 { roll_one(pool_state.clone(), false); - let current_phase = ElectionProviderMultiPhase::current_phase(); + let current_phase = CurrentPhase::::get(); assert!( match QueuedSolution::::get() { @@ -209,7 +211,7 @@ fn continuous_slashes_below_offending_threshold() { // break loop when era does not progress; EPM is in emergency phase as election // failed due to election minimum score. if start_next_active_era(pool_state.clone()).is_err() { - assert!(ElectionProviderMultiPhase::current_phase().is_emergency()); + assert!(CurrentPhase::::get().is_emergency()); break; } diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs index d148c05e4d0d..e45452c1ddf9 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs @@ -46,8 +46,8 @@ use frame_election_provider_support::{ SequentialPhragmen, Weight, }; use pallet_election_provider_multi_phase::{ - unsigned::MinerConfig, Call, ElectionCompute, GeometricDepositBase, QueuedSolution, - SolutionAccuracyOf, + unsigned::MinerConfig, Call, CurrentPhase, ElectionCompute, GeometricDepositBase, + QueuedSolution, SolutionAccuracyOf, }; use pallet_staking::StakerStatus; use parking_lot::RwLock; @@ -653,7 +653,7 @@ pub fn roll_to(n: BlockNumber, delay_solution: bool) { // https://github.com/paritytech/substrate/issues/13589 // if there's no solution queued and the solution should not be delayed, try mining and // queue a solution. - if ElectionProviderMultiPhase::current_phase().is_signed() && !delay_solution { + if CurrentPhase::::get().is_signed() && !delay_solution { let _ = try_queue_solution(ElectionCompute::Signed).map_err(|e| { log!(info, "failed to mine/queue solution: {:?}", e); }); @@ -807,7 +807,7 @@ pub(crate) fn current_era() -> EraIndex { // Fast forward until EPM signed phase. pub fn roll_to_epm_signed() { while !matches!( - ElectionProviderMultiPhase::current_phase(), + CurrentPhase::::get(), pallet_election_provider_multi_phase::Phase::Signed ) { roll_to(System::block_number() + 1, false); @@ -817,7 +817,7 @@ pub fn roll_to_epm_signed() { // Fast forward until EPM unsigned phase. pub fn roll_to_epm_unsigned() { while !matches!( - ElectionProviderMultiPhase::current_phase(), + CurrentPhase::::get(), pallet_election_provider_multi_phase::Phase::Unsigned(_) ) { roll_to(System::block_number() + 1, false); @@ -827,7 +827,7 @@ pub fn roll_to_epm_unsigned() { // Fast forward until EPM off. pub fn roll_to_epm_off() { while !matches!( - ElectionProviderMultiPhase::current_phase(), + CurrentPhase::::get(), pallet_election_provider_multi_phase::Phase::Off ) { roll_to(System::block_number() + 1, false); From 839ead3441b4012cbf6451af5b19a923bcbc379b Mon Sep 17 00:00:00 2001 From: Pankaj Date: Tue, 30 Jul 2024 03:42:43 +0530 Subject: [PATCH 045/382] Remove pallet::getter usage from proxy (#4963) ISSUE Link to the issue: https://github.com/paritytech/polkadot-sdk/issues/3326 Deliverables [Deprecation] remove pallet::getter usage from pallet-proxy --------- Co-authored-by: Oliver Tale-Yazdi --- prdoc/pr_4963.prdoc | 14 ++++++++++++++ substrate/frame/proxy/src/lib.rs | 23 +++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_4963.prdoc diff --git a/prdoc/pr_4963.prdoc b/prdoc/pr_4963.prdoc new file mode 100644 index 000000000000..e274d2cbb689 --- /dev/null +++ b/prdoc/pr_4963.prdoc @@ -0,0 +1,14 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Removed `pallet::getter` usage from the pallet-proxy + +doc: + - audience: Runtime Dev + description: | + This PR removed `pallet::getter`s from `pallet-proxy`s storage items. + When accessed inside the pallet, use the syntax `StorageItem::::get()`. + +crates: + - name: pallet-proxy + bump: minor \ No newline at end of file diff --git a/substrate/frame/proxy/src/lib.rs b/substrate/frame/proxy/src/lib.rs index d681088165c8..016f2cf225e0 100644 --- a/substrate/frame/proxy/src/lib.rs +++ b/substrate/frame/proxy/src/lib.rs @@ -41,6 +41,7 @@ use frame_support::{ dispatch::GetDispatchInfo, ensure, traits::{Currency, Get, InstanceFilter, IsSubType, IsType, OriginTrait, ReservableCurrency}, + BoundedVec, }; use frame_system::{self as system, ensure_signed, pallet_prelude::BlockNumberFor}; pub use pallet::*; @@ -570,7 +571,6 @@ pub mod pallet { /// The set of account proxies. Maps the account which has delegated to the accounts /// which are being delegated to, together with the amount held on deposit. #[pallet::storage] - #[pallet::getter(fn proxies)] pub type Proxies = StorageMap< _, Twox64Concat, @@ -587,7 +587,6 @@ pub mod pallet { /// The announcements made by the proxy (key). #[pallet::storage] - #[pallet::getter(fn announcements)] pub type Announcements = StorageMap< _, Twox64Concat, @@ -601,6 +600,26 @@ pub mod pallet { } impl Pallet { + /// Public function to proxies storage. + pub fn proxies( + account: T::AccountId, + ) -> ( + BoundedVec>, T::MaxProxies>, + BalanceOf, + ) { + Proxies::::get(account) + } + + /// Public function to announcements storage. + pub fn announcements( + account: T::AccountId, + ) -> ( + BoundedVec, BlockNumberFor>, T::MaxPending>, + BalanceOf, + ) { + Announcements::::get(account) + } + /// Calculate the address of an pure account. /// /// - `who`: The spawner account. From 07544295e45faedbd2bd6f903b76653527e0b6cf Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Tue, 30 Jul 2024 10:54:48 +0200 Subject: [PATCH 046/382] [subsystem-benchmark] Update availability-distribution-regression-bench baseline after recent subsystem changes (#5180) --- .../benches/availability-distribution-regression-bench.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/polkadot/node/network/availability-distribution/benches/availability-distribution-regression-bench.rs b/polkadot/node/network/availability-distribution/benches/availability-distribution-regression-bench.rs index 6083a90e4812..4467941c849c 100644 --- a/polkadot/node/network/availability-distribution/benches/availability-distribution-regression-bench.rs +++ b/polkadot/node/network/availability-distribution/benches/availability-distribution-regression-bench.rs @@ -73,9 +73,9 @@ fn main() -> Result<(), String> { ("Sent to peers", 18479.9000, 0.001), ])); messages.extend(average_usage.check_cpu_usage(&[ - ("availability-distribution", 0.0127, 0.1), - ("availability-store", 0.1626, 0.1), - ("bitfield-distribution", 0.0224, 0.1), + ("availability-distribution", 0.0128, 0.1), + ("availability-store", 0.1733, 0.1), + ("bitfield-distribution", 0.0223, 0.1), ])); if messages.is_empty() { From 4ffa9b88a34a7d0b4d1feb086176e27eacb766ee Mon Sep 17 00:00:00 2001 From: Egor_P Date: Tue, 30 Jul 2024 14:27:38 +0200 Subject: [PATCH 047/382] [Backport] version bumps and the prdocs reordering from stable2407 (#5178) This PR backports regular version bumps and reorganisation of the `prdoc` files from the stable release branch back to master --- cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs | 2 +- cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs | 2 +- .../runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 2 +- .../runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs | 2 +- .../runtimes/collectives/collectives-westend/src/lib.rs | 2 +- .../parachains/runtimes/contracts/contracts-rococo/src/lib.rs | 2 +- cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs | 2 +- .../parachains/runtimes/coretime/coretime-westend/src/lib.rs | 2 +- cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs | 2 +- cumulus/parachains/runtimes/people/people-rococo/src/lib.rs | 2 +- cumulus/parachains/runtimes/people/people-westend/src/lib.rs | 2 +- polkadot/node/primitives/src/lib.rs | 2 +- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- prdoc/{ => 1.15.0}/pr_3286.prdoc | 0 prdoc/{ => 1.15.0}/pr_4097.prdoc | 0 prdoc/{ => 1.15.0}/pr_4522.prdoc | 0 prdoc/{ => 1.15.0}/pr_4563.prdoc | 0 prdoc/{ => 1.15.0}/pr_4566.prdoc | 0 prdoc/{ => 1.15.0}/pr_4663.prdoc | 0 prdoc/{ => 1.15.0}/pr_4738.prdoc | 0 prdoc/{ => 1.15.0}/pr_4755.prdoc | 0 prdoc/{ => 1.15.0}/pr_4777.prdoc | 0 prdoc/{ => 1.15.0}/pr_4839.prdoc | 0 prdoc/{ => 1.15.0}/pr_4840.prdoc | 0 prdoc/{ => 1.15.0}/pr_4848.prdoc | 0 prdoc/{ => 1.15.0}/pr_4863.prdoc | 0 prdoc/{ => 1.15.0}/pr_4871.prdoc | 0 prdoc/{ => 1.15.0}/pr_4885.prdoc | 0 prdoc/{ => 1.15.0}/pr_4888.prdoc | 0 prdoc/{ => 1.15.0}/pr_4902.prdoc | 0 prdoc/{ => 1.15.0}/pr_4912.prdoc | 0 prdoc/{ => 1.15.0}/pr_4922.prdoc | 0 prdoc/{ => 1.15.0}/pr_4932.prdoc | 0 prdoc/{ => 1.15.0}/pr_4935.prdoc | 0 prdoc/{ => 1.15.0}/pr_4943.prdoc | 0 prdoc/{ => 1.15.0}/pr_4972.prdoc | 0 prdoc/{ => 1.15.0}/pr_4978.prdoc | 0 prdoc/{ => 1.15.0}/pr_4997.prdoc | 0 prdoc/{ => 1.15.0}/pr_5011.prdoc | 0 prdoc/{ => 1.15.0}/pr_5040.prdoc | 0 prdoc/{ => 1.15.0}/pr_5103.prdoc | 0 prdoc/{ => 1.15.0}/pr_5153.prdoc | 0 43 files changed, 14 insertions(+), 14 deletions(-) rename prdoc/{ => 1.15.0}/pr_3286.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4097.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4522.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4563.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4566.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4663.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4738.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4755.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4777.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4839.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4840.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4848.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4863.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4871.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4885.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4888.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4902.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4912.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4922.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4932.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4935.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4943.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4972.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4978.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_4997.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_5011.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_5040.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_5103.prdoc (100%) rename prdoc/{ => 1.15.0}/pr_5153.prdoc (100%) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 83bc12cf9b41..7414adacc448 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -120,7 +120,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("statemine"), impl_name: create_runtime_str!("statemine"), authoring_version: 1, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 2d9de07f251f..62d3462038c8 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -119,7 +119,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("westmint"), impl_name: create_runtime_str!("westmint"), authoring_version: 1, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 062996ad4115..02ef0b9c0c72 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -218,7 +218,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-rococo"), impl_name: create_runtime_str!("bridge-hub-rococo"), authoring_version: 1, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 42c768861bde..d9f19afc6296 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -191,7 +191,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-westend"), impl_name: create_runtime_str!("bridge-hub-westend"), authoring_version: 1, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5, diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index d843d6f6f776..21206d26dd57 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -125,7 +125,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("collectives-westend"), impl_name: create_runtime_str!("collectives-westend"), authoring_version: 1, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 6, diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index 47ce6f3628ec..bf173fb618af 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -144,7 +144,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("contracts-rococo"), impl_name: create_runtime_str!("contracts-rococo"), authoring_version: 1, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 7, diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index 37a6ea1d1c5f..25324bf17764 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -146,7 +146,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("coretime-rococo"), impl_name: create_runtime_str!("coretime-rococo"), authoring_version: 1, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index b6847dddb6c0..a3051e4bf271 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -145,7 +145,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("coretime-westend"), impl_name: create_runtime_str!("coretime-westend"), authoring_version: 1, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs index 1b505ad3acbf..942e11e0b257 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs @@ -102,7 +102,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("glutton-westend"), impl_name: create_runtime_str!("glutton-westend"), authoring_version: 1, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index 4f007c3fc39d..77bfb99669c6 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -134,7 +134,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("people-rococo"), impl_name: create_runtime_str!("people-rococo"), authoring_version: 1, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index 1378324ce7b0..3343d2be749d 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -134,7 +134,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("people-westend"), impl_name: create_runtime_str!("people-westend"), authoring_version: 1, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index 660b504e97fb..24c69582731b 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -59,7 +59,7 @@ pub use disputes::{ /// relatively rare. /// /// The associated worker binaries should use the same version as the node that spawns them. -pub const NODE_VERSION: &'static str = "1.14.0"; +pub const NODE_VERSION: &'static str = "1.15.0"; // For a 16-ary Merkle Prefix Trie, we can expect at most 16 32-byte hashes per node // plus some overhead: diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 2a1cb58fc17f..2f23b889916b 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -166,7 +166,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("rococo"), impl_name: create_runtime_str!("parity-rococo-v2.0"), authoring_version: 0, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 26, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index e582598d9d85..eddb7cbd21ea 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -162,7 +162,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("westend"), impl_name: create_runtime_str!("parity-westend"), authoring_version: 2, - spec_version: 1_014_000, + spec_version: 1_015_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 26, diff --git a/prdoc/pr_3286.prdoc b/prdoc/1.15.0/pr_3286.prdoc similarity index 100% rename from prdoc/pr_3286.prdoc rename to prdoc/1.15.0/pr_3286.prdoc diff --git a/prdoc/pr_4097.prdoc b/prdoc/1.15.0/pr_4097.prdoc similarity index 100% rename from prdoc/pr_4097.prdoc rename to prdoc/1.15.0/pr_4097.prdoc diff --git a/prdoc/pr_4522.prdoc b/prdoc/1.15.0/pr_4522.prdoc similarity index 100% rename from prdoc/pr_4522.prdoc rename to prdoc/1.15.0/pr_4522.prdoc diff --git a/prdoc/pr_4563.prdoc b/prdoc/1.15.0/pr_4563.prdoc similarity index 100% rename from prdoc/pr_4563.prdoc rename to prdoc/1.15.0/pr_4563.prdoc diff --git a/prdoc/pr_4566.prdoc b/prdoc/1.15.0/pr_4566.prdoc similarity index 100% rename from prdoc/pr_4566.prdoc rename to prdoc/1.15.0/pr_4566.prdoc diff --git a/prdoc/pr_4663.prdoc b/prdoc/1.15.0/pr_4663.prdoc similarity index 100% rename from prdoc/pr_4663.prdoc rename to prdoc/1.15.0/pr_4663.prdoc diff --git a/prdoc/pr_4738.prdoc b/prdoc/1.15.0/pr_4738.prdoc similarity index 100% rename from prdoc/pr_4738.prdoc rename to prdoc/1.15.0/pr_4738.prdoc diff --git a/prdoc/pr_4755.prdoc b/prdoc/1.15.0/pr_4755.prdoc similarity index 100% rename from prdoc/pr_4755.prdoc rename to prdoc/1.15.0/pr_4755.prdoc diff --git a/prdoc/pr_4777.prdoc b/prdoc/1.15.0/pr_4777.prdoc similarity index 100% rename from prdoc/pr_4777.prdoc rename to prdoc/1.15.0/pr_4777.prdoc diff --git a/prdoc/pr_4839.prdoc b/prdoc/1.15.0/pr_4839.prdoc similarity index 100% rename from prdoc/pr_4839.prdoc rename to prdoc/1.15.0/pr_4839.prdoc diff --git a/prdoc/pr_4840.prdoc b/prdoc/1.15.0/pr_4840.prdoc similarity index 100% rename from prdoc/pr_4840.prdoc rename to prdoc/1.15.0/pr_4840.prdoc diff --git a/prdoc/pr_4848.prdoc b/prdoc/1.15.0/pr_4848.prdoc similarity index 100% rename from prdoc/pr_4848.prdoc rename to prdoc/1.15.0/pr_4848.prdoc diff --git a/prdoc/pr_4863.prdoc b/prdoc/1.15.0/pr_4863.prdoc similarity index 100% rename from prdoc/pr_4863.prdoc rename to prdoc/1.15.0/pr_4863.prdoc diff --git a/prdoc/pr_4871.prdoc b/prdoc/1.15.0/pr_4871.prdoc similarity index 100% rename from prdoc/pr_4871.prdoc rename to prdoc/1.15.0/pr_4871.prdoc diff --git a/prdoc/pr_4885.prdoc b/prdoc/1.15.0/pr_4885.prdoc similarity index 100% rename from prdoc/pr_4885.prdoc rename to prdoc/1.15.0/pr_4885.prdoc diff --git a/prdoc/pr_4888.prdoc b/prdoc/1.15.0/pr_4888.prdoc similarity index 100% rename from prdoc/pr_4888.prdoc rename to prdoc/1.15.0/pr_4888.prdoc diff --git a/prdoc/pr_4902.prdoc b/prdoc/1.15.0/pr_4902.prdoc similarity index 100% rename from prdoc/pr_4902.prdoc rename to prdoc/1.15.0/pr_4902.prdoc diff --git a/prdoc/pr_4912.prdoc b/prdoc/1.15.0/pr_4912.prdoc similarity index 100% rename from prdoc/pr_4912.prdoc rename to prdoc/1.15.0/pr_4912.prdoc diff --git a/prdoc/pr_4922.prdoc b/prdoc/1.15.0/pr_4922.prdoc similarity index 100% rename from prdoc/pr_4922.prdoc rename to prdoc/1.15.0/pr_4922.prdoc diff --git a/prdoc/pr_4932.prdoc b/prdoc/1.15.0/pr_4932.prdoc similarity index 100% rename from prdoc/pr_4932.prdoc rename to prdoc/1.15.0/pr_4932.prdoc diff --git a/prdoc/pr_4935.prdoc b/prdoc/1.15.0/pr_4935.prdoc similarity index 100% rename from prdoc/pr_4935.prdoc rename to prdoc/1.15.0/pr_4935.prdoc diff --git a/prdoc/pr_4943.prdoc b/prdoc/1.15.0/pr_4943.prdoc similarity index 100% rename from prdoc/pr_4943.prdoc rename to prdoc/1.15.0/pr_4943.prdoc diff --git a/prdoc/pr_4972.prdoc b/prdoc/1.15.0/pr_4972.prdoc similarity index 100% rename from prdoc/pr_4972.prdoc rename to prdoc/1.15.0/pr_4972.prdoc diff --git a/prdoc/pr_4978.prdoc b/prdoc/1.15.0/pr_4978.prdoc similarity index 100% rename from prdoc/pr_4978.prdoc rename to prdoc/1.15.0/pr_4978.prdoc diff --git a/prdoc/pr_4997.prdoc b/prdoc/1.15.0/pr_4997.prdoc similarity index 100% rename from prdoc/pr_4997.prdoc rename to prdoc/1.15.0/pr_4997.prdoc diff --git a/prdoc/pr_5011.prdoc b/prdoc/1.15.0/pr_5011.prdoc similarity index 100% rename from prdoc/pr_5011.prdoc rename to prdoc/1.15.0/pr_5011.prdoc diff --git a/prdoc/pr_5040.prdoc b/prdoc/1.15.0/pr_5040.prdoc similarity index 100% rename from prdoc/pr_5040.prdoc rename to prdoc/1.15.0/pr_5040.prdoc diff --git a/prdoc/pr_5103.prdoc b/prdoc/1.15.0/pr_5103.prdoc similarity index 100% rename from prdoc/pr_5103.prdoc rename to prdoc/1.15.0/pr_5103.prdoc diff --git a/prdoc/pr_5153.prdoc b/prdoc/1.15.0/pr_5153.prdoc similarity index 100% rename from prdoc/pr_5153.prdoc rename to prdoc/1.15.0/pr_5153.prdoc From 686ee99c7c5de56d1416d47e4db4f3a2420c6c82 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 30 Jul 2024 14:29:12 +0200 Subject: [PATCH 048/382] [CI] Cache try-runtime check (#5179) Adds a snapshot step to the try-runtime check that tries to download a cached snapshot. The cache is valid for the current day and is otherwise re-created. Check is now only limited by build time and docker startup. ![Screenshot 2024-07-30 at 02 02 58](https://github.com/user-attachments/assets/0773e9b9-4a52-4572-a891-74b9d725ba70) ![Screenshot 2024-07-30 at 02 02 20](https://github.com/user-attachments/assets/4685ef17-a04c-4bdc-9d61-311d0010f71c) --------- Signed-off-by: Oliver Tale-Yazdi --- .github/workflows/check-runtime-migration.yml | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/.github/workflows/check-runtime-migration.yml b/.github/workflows/check-runtime-migration.yml index 33da5a8ecd59..2b963b2230fb 100644 --- a/.github/workflows/check-runtime-migration.yml +++ b/.github/workflows/check-runtime-migration.yml @@ -7,6 +7,8 @@ on: pull_request: types: [opened, synchronize, reopened, ready_for_review] merge_group: + workflow_dispatch: + concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true @@ -27,7 +29,9 @@ jobs: # rococo and westend are disabled for now (no access to parity-chains.parity.io) check-runtime-migration: runs-on: arc-runners-polkadot-sdk-beefy - timeout-minutes: 40 + # We need to set this to rather long to allow the snapshot to be created, but the average time + # should be much lower. + timeout-minutes: 60 needs: [set-image] container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -94,22 +98,45 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: script - run: | - echo "Running ${{ matrix.network }} runtime migration check" - export RUST_LOG=remote-ext=debug,runtime=debug - echo "---------- Downloading try-runtime CLI ----------" - curl -sL https://github.com/paritytech/try-runtime-cli/releases/download/v0.5.4/try-runtime-x86_64-unknown-linux-musl -o try-runtime + - name: Download CLI + run: | + curl -sL https://github.com/paritytech/try-runtime-cli/releases/download/v0.7.0/try-runtime-x86_64-unknown-linux-musl -o try-runtime chmod +x ./try-runtime echo "Using try-runtime-cli version:" ./try-runtime --version + - name: Get Date + id: get-date + run: | + echo "today=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_OUTPUT + shell: bash + + - name: Download Snapshot + uses: actions/cache@v4 + with: + path: snapshot.raw + key: try-runtime-snapshot-${{ matrix.network }}-${{ steps.get-date.outputs.today }} + save-always: true + + - name: Create Snapshot If Stale + if: ${{ hashFiles('snapshot.raw') == '' }} + run: | + echo "Creating new snapshot for today (${{ steps.get-date.outputs.today }})" + ./try-runtime create-snapshot --uri ${{ matrix.uri }} snapshot.raw + + - name: Build Runtime + run: | echo "---------- Building ${{ matrix.package }} runtime ----------" - time forklift cargo build --release --locked -p ${{ matrix.package }} --features try-runtime + time forklift cargo build --release --locked -p ${{ matrix.package }} --features try-runtime -q + + - name: Run Check + run: | + echo "Running ${{ matrix.network }} runtime migration check" + export RUST_LOG=remote-ext=debug,runtime=debug echo "---------- Executing on-runtime-upgrade for ${{ matrix.network }} ----------" time ./try-runtime ${{ matrix.command_extra_args }} \ --runtime ./target/release/wbuild/${{ matrix.package }}/${{ matrix.wasm }} \ - on-runtime-upgrade --disable-spec-version-check --checks=all ${{ matrix.subcommand_extra_args }} live --uri ${{ matrix.uri }} + on-runtime-upgrade --disable-spec-version-check --checks=all ${{ matrix.subcommand_extra_args }} snap -p snapshot.raw sleep 5 From 03c45b910331214c5b4f9cc88244b684e8a97e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Pestana?= Date: Tue, 30 Jul 2024 17:03:02 +0200 Subject: [PATCH 049/382] pallet-timestamp: `UnixTime::now` implementation logs error only if called at genesis (#5055) This PR reverts the removal of an [`if` statement](https://github.com/paritytech/polkadot-sdk/commit/7ecf3f757a5d6f622309cea7f788e8a547a5dce8#diff-8bf31ba8d9ebd6377983fd7ecc7f4e41cb1478a600db1a15a578d1ae0e8ed435L370) merged recently, which affected test output verbosity of several pallets (e.g. staking, EPM, and potentially others). More generally, the `UnixTime::now` implementation of the timestamp pallet should log an error *only* when called at the genesis block. --- prdoc/pr_5055.prdoc | 11 +++++++++++ substrate/frame/timestamp/src/lib.rs | 10 ++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 prdoc/pr_5055.prdoc diff --git a/prdoc/pr_5055.prdoc b/prdoc/pr_5055.prdoc new file mode 100644 index 000000000000..2367bd5925f8 --- /dev/null +++ b/prdoc/pr_5055.prdoc @@ -0,0 +1,11 @@ +title: Only log error in `UnixTime::now` call of the pallet-timestamp implementation if called at genesis + +doc: + - audience: Runtime Dev + description: | + This minor patch re-introduces a check to ensure that the `UnixTime::now` implementation in the timestamp only + logs an error if called at the genesis block. + +crates: +- name: pallet-timestamp + bump: minor diff --git a/substrate/frame/timestamp/src/lib.rs b/substrate/frame/timestamp/src/lib.rs index ca495c5e24ef..78e2939e65b9 100644 --- a/substrate/frame/timestamp/src/lib.rs +++ b/substrate/frame/timestamp/src/lib.rs @@ -368,10 +368,12 @@ impl UnixTime for Pallet { // `sp_timestamp::InherentDataProvider`. let now = Now::::get(); - log::error!( - target: "runtime::timestamp", - "`pallet_timestamp::UnixTime::now` is called at genesis, invalid value returned: 0", - ); + if now == T::Moment::zero() { + log::error!( + target: "runtime::timestamp", + "`pallet_timestamp::UnixTime::now` is called at genesis, invalid value returned: 0", + ); + } core::time::Duration::from_millis(now.saturated_into::()) } From 7fbfc7e0cf22ba0b5340b2ce09e17fe7072c9b70 Mon Sep 17 00:00:00 2001 From: Parth Mittal <76661350+mittal-parth@users.noreply.github.com> Date: Tue, 30 Jul 2024 23:40:20 +0530 Subject: [PATCH 050/382] Remove `pallet::getter` usage from the pallet-balances (#4967) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As per #3326, removes usage of the `pallet::getter` macro from the balances pallet. The syntax `StorageItem::::get()` should be used instead. Also, adds public functions for compatibility. cc @muraca polkadot address: 5GsLutpKjbzsbTphebs9Uy4YK6gTN47MAaz6njPktidjR5cp --------- Co-authored-by: Bastian Köcher Co-authored-by: Oliver Tale-Yazdi --- prdoc/pr_4967.prdoc | 28 +++++++ substrate/frame/balances/src/benchmarking.rs | 4 +- substrate/frame/balances/src/lib.rs | 26 ++++++- .../balances/src/tests/currency_tests.rs | 40 +++++----- .../balances/src/tests/dispatchable_tests.rs | 35 +++++---- substrate/frame/bounties/src/tests.rs | 6 +- .../frame/conviction-voting/src/tests.rs | 2 +- substrate/frame/democracy/src/tests.rs | 2 +- .../frame/democracy/src/tests/lock_voting.rs | 74 +++++++++---------- substrate/frame/democracy/src/tests/voting.rs | 4 +- substrate/frame/elections-phragmen/src/lib.rs | 2 +- substrate/frame/referenda/src/tests.rs | 2 +- substrate/frame/staking/src/mock.rs | 4 +- substrate/frame/staking/src/tests.rs | 38 ++++++---- substrate/frame/treasury/src/tests.rs | 6 +- 15 files changed, 167 insertions(+), 106 deletions(-) create mode 100644 prdoc/pr_4967.prdoc diff --git a/prdoc/pr_4967.prdoc b/prdoc/pr_4967.prdoc new file mode 100644 index 000000000000..0ce4219daa1c --- /dev/null +++ b/prdoc/pr_4967.prdoc @@ -0,0 +1,28 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Remove `pallet::getter` usage from the balances pallet" + +doc: + - audience: Runtime Dev + description: | + This PR removes the `pallet::getter`s from `pallet-balances`. + The syntax `StorageItem::::get()` should be used instead. + +crates: + - name: pallet-balances + bump: patch + - name: pallet-staking + bump: patch + - name: pallet-treasury + bump: patch + - name: pallet-bounties + bump: patch + - name: pallet-conviction-voting + bump: patch + - name: pallet-democracy + bump: patch + - name: pallet-elections-phragmen + bump: patch + - name: pallet-referenda + bump: patch diff --git a/substrate/frame/balances/src/benchmarking.rs b/substrate/frame/balances/src/benchmarking.rs index e4229f2d7f0f..5740f8081c07 100644 --- a/substrate/frame/balances/src/benchmarking.rs +++ b/substrate/frame/balances/src/benchmarking.rs @@ -288,13 +288,13 @@ mod benchmarks { #[benchmark] fn force_adjust_total_issuance() { - let ti = Balances::::total_issuance(); + let ti = TotalIssuance::::get(); let delta = 123u32.into(); #[extrinsic_call] _(RawOrigin::Root, AdjustmentDirection::Increase, delta); - assert_eq!(Balances::::total_issuance(), ti + delta); + assert_eq!(TotalIssuance::::get(), ti + delta); } /// Benchmark `burn` extrinsic with the worst possible condition - burn kills the account. diff --git a/substrate/frame/balances/src/lib.rs b/substrate/frame/balances/src/lib.rs index ddca685aa012..87d2029d488e 100644 --- a/substrate/frame/balances/src/lib.rs +++ b/substrate/frame/balances/src/lib.rs @@ -408,13 +408,11 @@ pub mod pallet { /// The total units issued in the system. #[pallet::storage] - #[pallet::getter(fn total_issuance)] #[pallet::whitelist_storage] pub type TotalIssuance, I: 'static = ()> = StorageValue<_, T::Balance, ValueQuery>; /// The total units of outstanding deactivated balance in the system. #[pallet::storage] - #[pallet::getter(fn inactive_issuance)] #[pallet::whitelist_storage] pub type InactiveIssuance, I: 'static = ()> = StorageValue<_, T::Balance, ValueQuery>; @@ -452,7 +450,6 @@ pub mod pallet { /// /// Use of locks is deprecated in favour of freezes. See `https://github.com/paritytech/substrate/pull/12951/` #[pallet::storage] - #[pallet::getter(fn locks)] pub type Locks, I: 'static = ()> = StorageMap< _, Blake2_128Concat, @@ -465,7 +462,6 @@ pub mod pallet { /// /// Use of reserves is deprecated in favour of holds. See `https://github.com/paritytech/substrate/pull/12951/` #[pallet::storage] - #[pallet::getter(fn reserves)] pub type Reserves, I: 'static = ()> = StorageMap< _, Blake2_128Concat, @@ -822,6 +818,28 @@ pub mod pallet { } impl, I: 'static> Pallet { + /// Public function to get the total issuance. + pub fn total_issuance() -> T::Balance { + TotalIssuance::::get() + } + + /// Public function to get the inactive issuance. + pub fn inactive_issuance() -> T::Balance { + InactiveIssuance::::get() + } + + /// Public function to access the Locks storage. + pub fn locks(who: &T::AccountId) -> WeakBoundedVec, T::MaxLocks> { + Locks::::get(who) + } + + /// Public function to access the reserves storage. + pub fn reserves( + who: &T::AccountId, + ) -> BoundedVec, T::MaxReserves> { + Reserves::::get(who) + } + fn ed() -> T::Balance { T::ExistentialDeposit::get() } diff --git a/substrate/frame/balances/src/tests/currency_tests.rs b/substrate/frame/balances/src/tests/currency_tests.rs index fb69368a6216..2243859458be 100644 --- a/substrate/frame/balances/src/tests/currency_tests.rs +++ b/substrate/frame/balances/src/tests/currency_tests.rs @@ -407,7 +407,7 @@ fn reward_should_work() { ] ); assert_eq!(Balances::total_balance(&1), 20); - assert_eq!(Balances::total_issuance(), 120); + assert_eq!(pallet_balances::TotalIssuance::::get(), 120); }); } @@ -473,7 +473,7 @@ fn slashing_balance_should_work() { assert!(Balances::slash(&1, 42).1.is_zero()); assert_eq!(Balances::free_balance(1), 1); assert_eq!(Balances::reserved_balance(1), 69); - assert_eq!(Balances::total_issuance(), 70); + assert_eq!(pallet_balances::TotalIssuance::::get(), 70); }); } @@ -488,7 +488,7 @@ fn withdrawing_balance_should_work() { amount: 11, })); assert_eq!(Balances::free_balance(2), 100); - assert_eq!(Balances::total_issuance(), 100); + assert_eq!(pallet_balances::TotalIssuance::::get(), 100); }); } @@ -519,7 +519,7 @@ fn slashing_incomplete_balance_should_work() { assert_eq!(Balances::slash(&1, 69).1, 49); assert_eq!(Balances::free_balance(1), 1); assert_eq!(Balances::reserved_balance(1), 21); - assert_eq!(Balances::total_issuance(), 22); + assert_eq!(pallet_balances::TotalIssuance::::get(), 22); }); } @@ -542,7 +542,7 @@ fn slashing_reserved_balance_should_work() { assert_eq!(Balances::slash_reserved(&1, 42).1, 0); assert_eq!(Balances::reserved_balance(1), 69); assert_eq!(Balances::free_balance(1), 1); - assert_eq!(Balances::total_issuance(), 70); + assert_eq!(pallet_balances::TotalIssuance::::get(), 70); }); } @@ -554,7 +554,7 @@ fn slashing_incomplete_reserved_balance_should_work() { assert_eq!(Balances::slash_reserved(&1, 69).1, 27); assert_eq!(Balances::free_balance(1), 69); assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(Balances::total_issuance(), 69); + assert_eq!(pallet_balances::TotalIssuance::::get(), 69); }); } @@ -654,12 +654,12 @@ fn transferring_too_high_value_should_not_panic() { fn account_create_on_free_too_low_with_other() { ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { let _ = Balances::deposit_creating(&1, 100); - assert_eq!(Balances::total_issuance(), 100); + assert_eq!(pallet_balances::TotalIssuance::::get(), 100); // No-op. let _ = Balances::deposit_creating(&2, 50); assert_eq!(Balances::free_balance(2), 0); - assert_eq!(Balances::total_issuance(), 100); + assert_eq!(pallet_balances::TotalIssuance::::get(), 100); }) } @@ -669,14 +669,14 @@ fn account_create_on_free_too_low() { // No-op. let _ = Balances::deposit_creating(&2, 50); assert_eq!(Balances::free_balance(2), 0); - assert_eq!(Balances::total_issuance(), 0); + assert_eq!(pallet_balances::TotalIssuance::::get(), 0); }) } #[test] fn account_removal_on_free_too_low() { ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - assert_eq!(Balances::total_issuance(), 0); + assert_eq!(pallet_balances::TotalIssuance::::get(), 0); // Setup two accounts with free balance above the existential threshold. let _ = Balances::deposit_creating(&1, 110); @@ -684,7 +684,7 @@ fn account_removal_on_free_too_low() { assert_eq!(Balances::free_balance(1), 110); assert_eq!(Balances::free_balance(2), 110); - assert_eq!(Balances::total_issuance(), 220); + assert_eq!(pallet_balances::TotalIssuance::::get(), 220); // Transfer funds from account 1 of such amount that after this transfer // the balance of account 1 will be below the existential threshold. @@ -696,18 +696,18 @@ fn account_removal_on_free_too_low() { assert_eq!(Balances::free_balance(2), 130); // Verify that TotalIssuance tracks balance removal when free balance is too low. - assert_eq!(Balances::total_issuance(), 130); + assert_eq!(pallet_balances::TotalIssuance::::get(), 130); }); } #[test] fn burn_must_work() { ExtBuilder::default().monied(true).build_and_execute_with(|| { - let init_total_issuance = Balances::total_issuance(); + let init_total_issuance = pallet_balances::TotalIssuance::::get(); let imbalance = >::burn(10); - assert_eq!(Balances::total_issuance(), init_total_issuance - 10); + assert_eq!(pallet_balances::TotalIssuance::::get(), init_total_issuance - 10); drop(imbalance); - assert_eq!(Balances::total_issuance(), init_total_issuance); + assert_eq!(pallet_balances::TotalIssuance::::get(), init_total_issuance); }); } @@ -1396,7 +1396,7 @@ fn freezing_and_locking_should_work() { #[test] fn self_transfer_noop() { ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - assert_eq!(Balances::total_issuance(), 0); + assert_eq!(pallet_balances::TotalIssuance::::get(), 0); let _ = Balances::deposit_creating(&1, 100); // The account is set up properly: @@ -1410,7 +1410,7 @@ fn self_transfer_noop() { ] ); assert_eq!(Balances::free_balance(1), 100); - assert_eq!(Balances::total_issuance(), 100); + assert_eq!(pallet_balances::TotalIssuance::::get(), 100); // Transfers to self are No-OPs: let _g = StorageNoopGuard::new(); @@ -1425,7 +1425,11 @@ fn self_transfer_noop() { assert!(events().is_empty()); assert_eq!(Balances::free_balance(1), 100, "Balance unchanged by self transfer"); - assert_eq!(Balances::total_issuance(), 100, "TI unchanged by self transfers"); + assert_eq!( + pallet_balances::TotalIssuance::::get(), + 100, + "TI unchanged by self transfers" + ); } }); } diff --git a/substrate/frame/balances/src/tests/dispatchable_tests.rs b/substrate/frame/balances/src/tests/dispatchable_tests.rs index ebc9f1b1a369..77e44202e982 100644 --- a/substrate/frame/balances/src/tests/dispatchable_tests.rs +++ b/substrate/frame/balances/src/tests/dispatchable_tests.rs @@ -187,9 +187,9 @@ fn set_balance_handles_killing_account() { #[test] fn set_balance_handles_total_issuance() { ExtBuilder::default().build_and_execute_with(|| { - let old_total_issuance = Balances::total_issuance(); + let old_total_issuance = pallet_balances::TotalIssuance::::get(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1337, 69)); - assert_eq!(Balances::total_issuance(), old_total_issuance + 69); + assert_eq!(pallet_balances::TotalIssuance::::get(), old_total_issuance + 69); assert_eq!(Balances::total_balance(&1337), 69); assert_eq!(Balances::free_balance(&1337), 69); }); @@ -236,12 +236,12 @@ fn force_adjust_total_issuance_example() { ExtBuilder::default().build_and_execute_with(|| { // First we set the TotalIssuance to 64 by giving Alice a balance of 64. assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), ALICE, 64)); - let old_ti = Balances::total_issuance(); + let old_ti = pallet_balances::TotalIssuance::::get(); assert_eq!(old_ti, 64, "TI should be 64"); // Now test the increase: assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 32)); - let new_ti = Balances::total_issuance(); + let new_ti = pallet_balances::TotalIssuance::::get(); assert_eq!(old_ti + 32, new_ti, "Should increase by 32"); // If Alice tries to call it, it errors: @@ -256,11 +256,11 @@ fn force_adjust_total_issuance_example() { fn force_adjust_total_issuance_works() { ExtBuilder::default().build_and_execute_with(|| { assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1337, 64)); - let ti = Balances::total_issuance(); + let ti = pallet_balances::TotalIssuance::::get(); // Increase works: assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 32)); - assert_eq!(Balances::total_issuance(), ti + 32); + assert_eq!(pallet_balances::TotalIssuance::::get(), ti + 32); System::assert_last_event(RuntimeEvent::Balances(Event::TotalIssuanceForced { old: 64, new: 96, @@ -268,7 +268,7 @@ fn force_adjust_total_issuance_works() { // Decrease works: assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 64)); - assert_eq!(Balances::total_issuance(), ti - 32); + assert_eq!(pallet_balances::TotalIssuance::::get(), ti - 32); System::assert_last_event(RuntimeEvent::Balances(Event::TotalIssuanceForced { old: 96, new: 32, @@ -280,19 +280,19 @@ fn force_adjust_total_issuance_works() { fn force_adjust_total_issuance_saturates() { ExtBuilder::default().build_and_execute_with(|| { assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1337, 64)); - let ti = Balances::total_issuance(); + let ti = pallet_balances::TotalIssuance::::get(); let max = ::Balance::max_value(); assert_eq!(ti, 64); // Increment saturates: assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, max)); assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 123)); - assert_eq!(Balances::total_issuance(), max); + assert_eq!(pallet_balances::TotalIssuance::::get(), max); // Decrement saturates: assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, max)); assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 123)); - assert_eq!(Balances::total_issuance(), 0); + assert_eq!(pallet_balances::TotalIssuance::::get(), 0); }); } @@ -316,13 +316,13 @@ fn force_adjust_total_issuance_rejects_more_than_inactive() { assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1337, 64)); Balances::deactivate(16u32.into()); - assert_eq!(Balances::total_issuance(), 64); + assert_eq!(pallet_balances::TotalIssuance::::get(), 64); assert_eq!(Balances::active_issuance(), 48); // Works with up to 48: assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 40),); assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 8),); - assert_eq!(Balances::total_issuance(), 16); + assert_eq!(pallet_balances::TotalIssuance::::get(), 16); assert_eq!(Balances::active_issuance(), 0); // Errors with more than 48: assert_noop!( @@ -331,7 +331,7 @@ fn force_adjust_total_issuance_rejects_more_than_inactive() { ); // Increasing again increases the inactive issuance: assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 10),); - assert_eq!(Balances::total_issuance(), 26); + assert_eq!(pallet_balances::TotalIssuance::::get(), 26); assert_eq!(Balances::active_issuance(), 10); }); } @@ -342,7 +342,7 @@ fn burn_works() { // Prepare account with initial balance let (account, init_balance) = (1, 37); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account, init_balance)); - let init_issuance = Balances::total_issuance(); + let init_issuance = pallet_balances::TotalIssuance::::get(); let (keep_alive, allow_death) = (true, false); // 1. Cannot burn more than what's available @@ -358,7 +358,7 @@ fn burn_works() { who: account, amount: burn_amount_1, })); - assert_eq!(Balances::total_issuance(), init_issuance - burn_amount_1); + assert_eq!(pallet_balances::TotalIssuance::::get(), init_issuance - burn_amount_1); assert_eq!(Balances::total_balance(&account), init_balance - burn_amount_1); // 3. Cannot burn funds below existential deposit if `keep_alive` is `true` @@ -375,7 +375,10 @@ fn burn_works() { who: account, amount: burn_amount_2, })); - assert_eq!(Balances::total_issuance(), init_issuance - burn_amount_1 - burn_amount_2); + assert_eq!( + pallet_balances::TotalIssuance::::get(), + init_issuance - burn_amount_1 - burn_amount_2 + ); assert!(Balances::total_balance(&account).is_zero()); }); } diff --git a/substrate/frame/bounties/src/tests.rs b/substrate/frame/bounties/src/tests.rs index 7cd479826745..c152391d807a 100644 --- a/substrate/frame/bounties/src/tests.rs +++ b/substrate/frame/bounties/src/tests.rs @@ -241,13 +241,13 @@ fn accepted_spend_proposal_ignored_outside_spend_period() { #[test] fn unused_pot_should_diminish() { ExtBuilder::default().build_and_execute(|| { - let init_total_issuance = Balances::total_issuance(); + let init_total_issuance = pallet_balances::TotalIssuance::::get(); Balances::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Balances::total_issuance(), init_total_issuance + 100); + assert_eq!(pallet_balances::TotalIssuance::::get(), init_total_issuance + 100); >::on_initialize(2); assert_eq!(Treasury::pot(), 50); - assert_eq!(Balances::total_issuance(), init_total_issuance + 50); + assert_eq!(pallet_balances::TotalIssuance::::get(), init_total_issuance + 50); }); } diff --git a/substrate/frame/conviction-voting/src/tests.rs b/substrate/frame/conviction-voting/src/tests.rs index 78569fb3c9f2..37cdd7a5b338 100644 --- a/substrate/frame/conviction-voting/src/tests.rs +++ b/substrate/frame/conviction-voting/src/tests.rs @@ -172,7 +172,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { fn params_should_work() { new_test_ext().execute_with(|| { assert_eq!(Balances::free_balance(42), 0); - assert_eq!(Balances::total_issuance(), 210); + assert_eq!(pallet_balances::TotalIssuance::::get(), 210); }); } diff --git a/substrate/frame/democracy/src/tests.rs b/substrate/frame/democracy/src/tests.rs index 7d7066c8af69..10e5ee75611d 100644 --- a/substrate/frame/democracy/src/tests.rs +++ b/substrate/frame/democracy/src/tests.rs @@ -185,7 +185,7 @@ fn params_should_work() { new_test_ext().execute_with(|| { assert_eq!(ReferendumCount::::get(), 0); assert_eq!(Balances::free_balance(42), 0); - assert_eq!(Balances::total_issuance(), 210); + assert_eq!(pallet_balances::TotalIssuance::::get(), 210); }); } diff --git a/substrate/frame/democracy/src/tests/lock_voting.rs b/substrate/frame/democracy/src/tests/lock_voting.rs index 31f2e3f3dcc2..7d0b28b9847d 100644 --- a/substrate/frame/democracy/src/tests/lock_voting.rs +++ b/substrate/frame/democracy/src/tests/lock_voting.rs @@ -56,7 +56,7 @@ fn lock_voting_should_work() { // All balances are currently locked. for i in 1..=5 { - assert_eq!(Balances::locks(i), vec![the_lock(i * 10)]); + assert_eq!(pallet_balances::Locks::::get(&i), vec![the_lock(i * 10)]); } fast_forward_to(3); @@ -77,11 +77,11 @@ fn lock_voting_should_work() { assert_ok!(Democracy::remove_vote(RuntimeOrigin::signed(2), r)); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(2), 2)); - assert_eq!(Balances::locks(1), vec![]); - assert_eq!(Balances::locks(2), vec![the_lock(20)]); - assert_eq!(Balances::locks(3), vec![the_lock(30)]); - assert_eq!(Balances::locks(4), vec![the_lock(40)]); - assert_eq!(Balances::locks(5), vec![]); + assert_eq!(pallet_balances::Locks::::get(&1), vec![]); + assert_eq!(pallet_balances::Locks::::get(&2), vec![the_lock(20)]); + assert_eq!(pallet_balances::Locks::::get(&3), vec![the_lock(30)]); + assert_eq!(pallet_balances::Locks::::get(&4), vec![the_lock(40)]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![]); assert_eq!(Balances::free_balance(42), 2); fast_forward_to(7); @@ -91,12 +91,12 @@ fn lock_voting_should_work() { Error::::NoPermission ); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(1), 4)); - assert_eq!(Balances::locks(4), vec![the_lock(40)]); + assert_eq!(pallet_balances::Locks::::get(&4), vec![the_lock(40)]); fast_forward_to(8); // 4 should now be able to reap and unlock assert_ok!(Democracy::remove_other_vote(RuntimeOrigin::signed(1), 4, r)); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(1), 4)); - assert_eq!(Balances::locks(4), vec![]); + assert_eq!(pallet_balances::Locks::::get(&4), vec![]); fast_forward_to(13); assert_noop!( @@ -104,19 +104,19 @@ fn lock_voting_should_work() { Error::::NoPermission ); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(1), 3)); - assert_eq!(Balances::locks(3), vec![the_lock(30)]); + assert_eq!(pallet_balances::Locks::::get(&3), vec![the_lock(30)]); fast_forward_to(14); assert_ok!(Democracy::remove_other_vote(RuntimeOrigin::signed(1), 3, r)); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(1), 3)); - assert_eq!(Balances::locks(3), vec![]); + assert_eq!(pallet_balances::Locks::::get(&3), vec![]); // 2 doesn't need to reap_vote here because it was already done before. fast_forward_to(25); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(1), 2)); - assert_eq!(Balances::locks(2), vec![the_lock(20)]); + assert_eq!(pallet_balances::Locks::::get(&2), vec![the_lock(20)]); fast_forward_to(26); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(1), 2)); - assert_eq!(Balances::locks(2), vec![]); + assert_eq!(pallet_balances::Locks::::get(&2), vec![]); }); } @@ -137,7 +137,7 @@ fn no_locks_without_conviction_should_work() { assert_eq!(Balances::free_balance(42), 2); assert_ok!(Democracy::remove_other_vote(RuntimeOrigin::signed(2), 1, r)); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(2), 1)); - assert_eq!(Balances::locks(1), vec![]); + assert_eq!(pallet_balances::Locks::::get(&1), vec![]); }); } @@ -198,33 +198,33 @@ fn prior_lockvotes_should_be_enforced() { Error::::NoPermission ); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![the_lock(50)]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![the_lock(50)]); fast_forward_to(8); assert_ok!(Democracy::remove_other_vote(RuntimeOrigin::signed(1), 5, r.2)); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![the_lock(20)]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![the_lock(20)]); fast_forward_to(13); assert_noop!( Democracy::remove_other_vote(RuntimeOrigin::signed(1), 5, r.1), Error::::NoPermission ); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![the_lock(20)]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![the_lock(20)]); fast_forward_to(14); assert_ok!(Democracy::remove_other_vote(RuntimeOrigin::signed(1), 5, r.1)); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![the_lock(10)]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![the_lock(10)]); fast_forward_to(25); assert_noop!( Democracy::remove_other_vote(RuntimeOrigin::signed(1), 5, r.0), Error::::NoPermission ); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![the_lock(10)]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![the_lock(10)]); fast_forward_to(26); assert_ok!(Democracy::remove_other_vote(RuntimeOrigin::signed(1), 5, r.0)); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![]); }); } @@ -239,26 +239,26 @@ fn single_consolidation_of_lockvotes_should_work_as_before() { fast_forward_to(7); assert_ok!(Democracy::remove_vote(RuntimeOrigin::signed(5), r.2)); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![the_lock(50)]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![the_lock(50)]); fast_forward_to(8); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![the_lock(20)]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![the_lock(20)]); fast_forward_to(13); assert_ok!(Democracy::remove_vote(RuntimeOrigin::signed(5), r.1)); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![the_lock(20)]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![the_lock(20)]); fast_forward_to(14); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![the_lock(10)]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![the_lock(10)]); fast_forward_to(25); assert_ok!(Democracy::remove_vote(RuntimeOrigin::signed(5), r.0)); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![the_lock(10)]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![the_lock(10)]); fast_forward_to(26); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![]); }); } @@ -276,15 +276,15 @@ fn multi_consolidation_of_lockvotes_should_be_conservative() { fast_forward_to(8); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert!(Balances::locks(5)[0].amount >= 20); + assert!(pallet_balances::Locks::::get(&5)[0].amount >= 20); fast_forward_to(14); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert!(Balances::locks(5)[0].amount >= 10); + assert!(pallet_balances::Locks::::get(&5)[0].amount >= 10); fast_forward_to(26); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![]); }); } @@ -305,26 +305,26 @@ fn locks_should_persist_from_voting_to_delegation() { assert_ok!(Democracy::delegate(RuntimeOrigin::signed(5), 1, Conviction::Locked3x, 20)); // locked 20. - assert!(Balances::locks(5)[0].amount == 20); + assert!(pallet_balances::Locks::::get(&5)[0].amount == 20); assert_ok!(Democracy::undelegate(RuntimeOrigin::signed(5))); // locked 20 until #14 fast_forward_to(13); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert!(Balances::locks(5)[0].amount == 20); + assert!(pallet_balances::Locks::::get(&5)[0].amount == 20); fast_forward_to(14); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert!(Balances::locks(5)[0].amount >= 10); + assert!(pallet_balances::Locks::::get(&5)[0].amount >= 10); fast_forward_to(25); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert!(Balances::locks(5)[0].amount >= 10); + assert!(pallet_balances::Locks::::get(&5)[0].amount >= 10); fast_forward_to(26); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![]); }); } @@ -347,18 +347,18 @@ fn locks_should_persist_from_delegation_to_voting() { fast_forward_to(8); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert!(Balances::locks(5)[0].amount >= 20); + assert!(pallet_balances::Locks::::get(&5)[0].amount >= 20); fast_forward_to(14); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert!(Balances::locks(5)[0].amount >= 10); + assert!(pallet_balances::Locks::::get(&5)[0].amount >= 10); fast_forward_to(26); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert!(Balances::locks(5)[0].amount >= 5); + assert!(pallet_balances::Locks::::get(&5)[0].amount >= 5); fast_forward_to(48); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![]); }); } diff --git a/substrate/frame/democracy/src/tests/voting.rs b/substrate/frame/democracy/src/tests/voting.rs index 61b80cc97fed..651a2cc8552a 100644 --- a/substrate/frame/democracy/src/tests/voting.rs +++ b/substrate/frame/democracy/src/tests/voting.rs @@ -55,7 +55,7 @@ fn split_vote_cancellation_should_work() { assert_ok!(Democracy::remove_vote(RuntimeOrigin::signed(5), r)); assert_eq!(tally(r), Tally { ayes: 0, nays: 0, turnout: 0 }); assert_ok!(Democracy::unlock(RuntimeOrigin::signed(5), 5)); - assert_eq!(Balances::locks(5), vec![]); + assert_eq!(pallet_balances::Locks::::get(&5), vec![]); }); } @@ -152,7 +152,7 @@ fn controversial_low_turnout_voting_should_work() { fn passing_low_turnout_voting_should_work() { new_test_ext().execute_with(|| { assert_eq!(Balances::free_balance(42), 0); - assert_eq!(Balances::total_issuance(), 210); + assert_eq!(pallet_balances::TotalIssuance::::get(), 210); let r = Democracy::inject_referendum( 2, diff --git a/substrate/frame/elections-phragmen/src/lib.rs b/substrate/frame/elections-phragmen/src/lib.rs index 565ead4059d5..6d91448fd185 100644 --- a/substrate/frame/elections-phragmen/src/lib.rs +++ b/substrate/frame/elections-phragmen/src/lib.rs @@ -2195,7 +2195,7 @@ mod tests { assert_eq!(locked_stake_of(&2), 0); assert_eq!(balances(&2), (20, 0)); - assert_eq!(Balances::locks(&2).len(), 0); + assert_eq!(pallet_balances::Locks::::get(&2).len(), 0); }); } diff --git a/substrate/frame/referenda/src/tests.rs b/substrate/frame/referenda/src/tests.rs index 3f859636f7cb..9f851992496c 100644 --- a/substrate/frame/referenda/src/tests.rs +++ b/substrate/frame/referenda/src/tests.rs @@ -30,7 +30,7 @@ fn params_should_work() { ExtBuilder::default().build_and_execute(|| { assert_eq!(ReferendumCount::::get(), 0); assert_eq!(Balances::free_balance(42), 0); - assert_eq!(Balances::total_issuance(), 600); + assert_eq!(pallet_balances::TotalIssuance::::get(), 600); }); } diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index 7cc8e865916d..4a0209fc5b08 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -664,7 +664,7 @@ pub(crate) fn start_active_era(era_index: EraIndex) { pub(crate) fn current_total_payout_for_duration(duration: u64) -> Balance { let (payout, _rest) = ::EraPayout::era_payout( Staking::eras_total_stake(active_era()), - Balances::total_issuance(), + pallet_balances::TotalIssuance::::get(), duration, ); assert!(payout > 0); @@ -674,7 +674,7 @@ pub(crate) fn current_total_payout_for_duration(duration: u64) -> Balance { pub(crate) fn maximum_payout_for_duration(duration: u64) -> Balance { let (payout, rest) = ::EraPayout::era_payout( Staking::eras_total_stake(active_era()), - Balances::total_issuance(), + pallet_balances::TotalIssuance::::get(), duration, ); payout + rest diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs index 0b6aad4a1b08..ab2c00ca9ccc 100644 --- a/substrate/frame/staking/src/tests.rs +++ b/substrate/frame/staking/src/tests.rs @@ -2069,7 +2069,7 @@ fn bond_with_no_staked_value() { ); // bonded with absolute minimum value possible. assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 5, RewardDestination::Account(1))); - assert_eq!(Balances::locks(&1)[0].amount, 5); + assert_eq!(pallet_balances::Locks::::get(&1)[0].amount, 5); // unbonding even 1 will cause all to be unbonded. assert_ok!(Staking::unbond(RuntimeOrigin::signed(1), 1)); @@ -2090,14 +2090,14 @@ fn bond_with_no_staked_value() { // not yet removed. assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(1), 0)); assert!(Staking::ledger(1.into()).is_ok()); - assert_eq!(Balances::locks(&1)[0].amount, 5); + assert_eq!(pallet_balances::Locks::::get(&1)[0].amount, 5); mock::start_active_era(3); // poof. Account 1 is removed from the staking system. assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(1), 0)); assert!(Staking::ledger(1.into()).is_err()); - assert_eq!(Balances::locks(&1).len(), 0); + assert_eq!(pallet_balances::Locks::::get(&1).len(), 0); }); } @@ -3954,7 +3954,7 @@ fn test_multi_page_payout_stakers_by_page() { assert_eq!(actual_exposure_1.own(), 0); assert_eq!(actual_exposure_1.others().len(), 100 - 64); - let pre_payout_total_issuance = Balances::total_issuance(); + let pre_payout_total_issuance = pallet_balances::TotalIssuance::::get(); RewardOnUnbalanceWasCalled::set(false); System::reset_events(); @@ -3975,8 +3975,8 @@ fn test_multi_page_payout_stakers_by_page() { let controller_balance_after_p0_payout = Balances::free_balance(&11); // verify rewards have been paid out but still some left - assert!(Balances::total_issuance() > pre_payout_total_issuance); - assert!(Balances::total_issuance() < pre_payout_total_issuance + payout); + assert!(pallet_balances::TotalIssuance::::get() > pre_payout_total_issuance); + assert!(pallet_balances::TotalIssuance::::get() < pre_payout_total_issuance + payout); // verify the validator has been rewarded assert!(controller_balance_after_p0_payout > controller_balance_before_p0_payout); @@ -3999,7 +3999,11 @@ fn test_multi_page_payout_stakers_by_page() { assert_eq!(Balances::free_balance(&11), controller_balance_after_p0_payout); // verify all rewards have been paid out - assert_eq_error_rate!(Balances::total_issuance(), pre_payout_total_issuance + payout, 2); + assert_eq_error_rate!( + pallet_balances::TotalIssuance::::get(), + pre_payout_total_issuance + payout, + 2 + ); assert!(RewardOnUnbalanceWasCalled::get()); // Top 64 nominators of validator 11 automatically paid out, including the validator @@ -4039,13 +4043,13 @@ fn test_multi_page_payout_stakers_by_page() { // compute and ensure the reward amount is greater than zero. let payout = current_total_payout_for_duration(reward_time_per_era()); - let pre_payout_total_issuance = Balances::total_issuance(); + let pre_payout_total_issuance = pallet_balances::TotalIssuance::::get(); mock::start_active_era(i); RewardOnUnbalanceWasCalled::set(false); mock::make_all_reward_payment(i - 1); assert_eq_error_rate!( - Balances::total_issuance(), + pallet_balances::TotalIssuance::::get(), pre_payout_total_issuance + payout, 2 ); @@ -4171,7 +4175,7 @@ fn test_multi_page_payout_stakers_backward_compatible() { assert_eq!(actual_exposure_1.own(), 0); assert_eq!(actual_exposure_1.others().len(), 100 - 64); - let pre_payout_total_issuance = Balances::total_issuance(); + let pre_payout_total_issuance = pallet_balances::TotalIssuance::::get(); RewardOnUnbalanceWasCalled::set(false); let controller_balance_before_p0_payout = Balances::free_balance(&11); @@ -4186,8 +4190,8 @@ fn test_multi_page_payout_stakers_backward_compatible() { let controller_balance_after_p0_payout = Balances::free_balance(&11); // verify rewards have been paid out but still some left - assert!(Balances::total_issuance() > pre_payout_total_issuance); - assert!(Balances::total_issuance() < pre_payout_total_issuance + payout); + assert!(pallet_balances::TotalIssuance::::get() > pre_payout_total_issuance); + assert!(pallet_balances::TotalIssuance::::get() < pre_payout_total_issuance + payout); // verify the validator has been rewarded assert!(controller_balance_after_p0_payout > controller_balance_before_p0_payout); @@ -4205,7 +4209,11 @@ fn test_multi_page_payout_stakers_backward_compatible() { assert_eq!(Balances::free_balance(&11), controller_balance_after_p0_payout); // verify all rewards have been paid out - assert_eq_error_rate!(Balances::total_issuance(), pre_payout_total_issuance + payout, 2); + assert_eq_error_rate!( + pallet_balances::TotalIssuance::::get(), + pre_payout_total_issuance + payout, + 2 + ); assert!(RewardOnUnbalanceWasCalled::get()); // verify all nominators of validator 11 are paid out, including the validator @@ -4246,13 +4254,13 @@ fn test_multi_page_payout_stakers_backward_compatible() { // compute and ensure the reward amount is greater than zero. let payout = current_total_payout_for_duration(reward_time_per_era()); - let pre_payout_total_issuance = Balances::total_issuance(); + let pre_payout_total_issuance = pallet_balances::TotalIssuance::::get(); mock::start_active_era(i); RewardOnUnbalanceWasCalled::set(false); mock::make_all_reward_payment(i - 1); assert_eq_error_rate!( - Balances::total_issuance(), + pallet_balances::TotalIssuance::::get(), pre_payout_total_issuance + payout, 2 ); diff --git a/substrate/frame/treasury/src/tests.rs b/substrate/frame/treasury/src/tests.rs index 97b735928192..f38a06f1fdf4 100644 --- a/substrate/frame/treasury/src/tests.rs +++ b/substrate/frame/treasury/src/tests.rs @@ -296,13 +296,13 @@ fn accepted_spend_proposal_ignored_outside_spend_period() { #[test] fn unused_pot_should_diminish() { ExtBuilder::default().build().execute_with(|| { - let init_total_issuance = Balances::total_issuance(); + let init_total_issuance = pallet_balances::TotalIssuance::::get(); Balances::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Balances::total_issuance(), init_total_issuance + 100); + assert_eq!(pallet_balances::TotalIssuance::::get(), init_total_issuance + 100); >::on_initialize(2); assert_eq!(Treasury::pot(), 50); - assert_eq!(Balances::total_issuance(), init_total_issuance + 50); + assert_eq!(pallet_balances::TotalIssuance::::get(), init_total_issuance + 50); }); } From 39daa61eb3a0c7395f96cad5c0a30c4cfc2ecfe9 Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Wed, 31 Jul 2024 17:45:04 +0900 Subject: [PATCH 051/382] Run UI tests in CI for some other crates (#5167) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test name is `test-frame-ui` I don't know if I can also change it to `test-ui` without breaking other stuff. So I kept the name unchanged. --------- Co-authored-by: Bastian Köcher --- .gitlab/pipeline/test.yml | 5 +++++ Cargo.toml | 2 +- polkadot/xcm/procedural/Cargo.toml | 4 +++- scripts/update-ui-tests.sh | 1 + .../frame/election-provider-support/solution-type/Cargo.toml | 4 +++- 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index d171a8a19426..0103c6b76a2d 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -398,6 +398,11 @@ test-frame-ui: script: - time cargo test --locked -q --profile testnet -p frame-support-test --features=frame-feature-testing,no-metadata-docs,try-runtime,experimental - time cargo test --locked -q --profile testnet -p frame-support-test --features=frame-feature-testing,frame-feature-testing-2,no-metadata-docs,try-runtime,experimental + - time cargo test --locked -q --profile testnet -p xcm-procedural + - time cargo test --locked -q --profile testnet -p frame-election-provider-solution-type + - time cargo test --locked -q --profile testnet -p sp-api-test + # There is multiple version of sp-runtime-interface in the repo. So we point to the manifest. + - time cargo test --locked -q --profile testnet --manifest-path substrate/primitives/runtime-interface/Cargo.toml - cat /cargo_target_dir/debug/.fingerprint/memory_units-759eddf317490d2b/lib-memory_units.json || true # This job runs all benchmarks defined in the `/bin/node/runtime` once to check that there are no errors. diff --git a/Cargo.toml b/Cargo.toml index 38ef9075a731..703e1ebbcf62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1323,7 +1323,7 @@ trie-bench = { version = "0.39.0" } trie-db = { version = "0.29.0", default-features = false } trie-root = { version = "0.18.0", default-features = false } trie-standardmap = { version = "0.16.0" } -trybuild = { version = "1.0.88" } +trybuild = { version = "1.0.89" } tt-call = { version = "1.0.8" } tuplex = { version = "0.1", default-features = false } twox-hash = { version = "1.6.3", default-features = false } diff --git a/polkadot/xcm/procedural/Cargo.toml b/polkadot/xcm/procedural/Cargo.toml index a7db183bcdbf..83b35d19cf7e 100644 --- a/polkadot/xcm/procedural/Cargo.toml +++ b/polkadot/xcm/procedural/Cargo.toml @@ -21,4 +21,6 @@ Inflector = { workspace = true } [dev-dependencies] trybuild = { features = ["diff"], workspace = true } -xcm = { workspace = true, default-features = true } +# NOTE: we have to explicitly specify `std` because of trybuild +# https://github.com/paritytech/polkadot-sdk/pull/5167 +xcm = { workspace = true, default-features = true, features = ["std"] } diff --git a/scripts/update-ui-tests.sh b/scripts/update-ui-tests.sh index dedee8e641f8..d363e51e4041 100755 --- a/scripts/update-ui-tests.sh +++ b/scripts/update-ui-tests.sh @@ -38,3 +38,4 @@ $RUSTUP_RUN cargo test --manifest-path substrate/primitives/runtime-interface/Ca $RUSTUP_RUN cargo test -p sp-api-test ui $RUSTUP_RUN cargo test -p frame-election-provider-solution-type ui $RUSTUP_RUN cargo test -p frame-support-test --features=no-metadata-docs,try-runtime,experimental ui +$RUSTUP_RUN cargo test -p xcm-procedural ui diff --git a/substrate/frame/election-provider-support/solution-type/Cargo.toml b/substrate/frame/election-provider-support/solution-type/Cargo.toml index 30b00712b3f5..e24ed7f079fe 100644 --- a/substrate/frame/election-provider-support/solution-type/Cargo.toml +++ b/substrate/frame/election-provider-support/solution-type/Cargo.toml @@ -28,6 +28,8 @@ codec = { workspace = true, default-features = true } scale-info = { workspace = true, default-features = true } sp-arithmetic = { workspace = true, default-features = true } # used by generate_solution_type: -frame-election-provider-support = { workspace = true, default-features = true } +# NOTE: we have to explicitly specify `std` because of trybuild +# https://github.com/paritytech/polkadot-sdk/pull/5167 +frame-election-provider-support = { workspace = true, default-features = true, features = ["std"] } frame-support = { workspace = true, default-features = true } trybuild = { workspace = true } From 7d0aa89653d5073081a949eca1de2ca2d42a9e98 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Wed, 31 Jul 2024 13:20:28 +0300 Subject: [PATCH 052/382] litep2p/discovery: Publish authority records with external addresses only (#5176) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR reduces the occurrences for identified observed addresses. Litep2p discovers its external addresses by inspecting the `IdentifyInfo::ObservedAddress` field reported by other peers. After we get 5 confirmations of the same external observed address (the address the peer dialed to reach us), the address is reported through the network layer. The PR effectively changes this from 5 to 2. This has a subtle implication on freshly started nodes for the authority-discovery discussed below. The PR also makes the authority discovery a bit more robust by not publishing records if the node doesn't have addresses yet to report. This aims to fix a scenario where: - the litep2p node has started, it has some pending observed addresses but less than 5 - the authorit-discovery publishes a record, but at this time the node doesn't have any addresses discovered and the record is published without addresses -> this means other nodes will not be able to reach us Next Steps - [ ] versi testing Closes: https://github.com/paritytech/polkadot-sdk/issues/5147 cc @paritytech/networking --------- Signed-off-by: Alexandru Vasile Co-authored-by: Bastian Köcher --- .../client/authority-discovery/src/worker.rs | 25 ++++++++++++----- .../client/network/src/litep2p/discovery.rs | 28 +++++++++++-------- substrate/client/network/src/litep2p/mod.rs | 2 +- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/substrate/client/authority-discovery/src/worker.rs b/substrate/client/authority-discovery/src/worker.rs index 42994bbc7ea8..6f4fbac77e05 100644 --- a/substrate/client/authority-discovery/src/worker.rs +++ b/substrate/client/authority-discovery/src/worker.rs @@ -37,7 +37,7 @@ use ip_network::IpNetwork; use libp2p::kad::{PeerRecord, Record}; use linked_hash_set::LinkedHashSet; -use log::{debug, error}; +use log::{debug, error, trace}; use prometheus_endpoint::{register, Counter, CounterVec, Gauge, Opts, U64}; use prost::Message; use rand::{seq::SliceRandom, thread_rng}; @@ -416,10 +416,12 @@ where }) .collect::>(); - debug!( - target: LOG_TARGET, - "Publishing authority DHT record peer_id='{local_peer_id}' addresses='{addresses:?}'", - ); + if !addresses.is_empty() { + debug!( + target: LOG_TARGET, + "Publishing authority DHT record peer_id='{local_peer_id}' with addresses='{addresses:?}'", + ); + } // The address must include the local peer id. addresses @@ -437,6 +439,17 @@ where Role::Discover => return Ok(()), }; + let addresses = serialize_addresses(self.addresses_to_publish()); + if addresses.is_empty() { + trace!( + target: LOG_TARGET, + "No addresses to publish. Skipping publication." + ); + + self.publish_interval.set_to_start(); + return Ok(()) + } + let keys = Worker::::get_own_public_keys_within_authority_set( key_store.clone(), @@ -459,8 +472,6 @@ where self.query_interval.set_to_start(); } - let addresses = serialize_addresses(self.addresses_to_publish()); - if let Some(metrics) = &self.metrics { metrics.publish.inc(); metrics diff --git a/substrate/client/network/src/litep2p/discovery.rs b/substrate/client/network/src/litep2p/discovery.rs index 22285e7906c6..62d5f0fb6f06 100644 --- a/substrate/client/network/src/litep2p/discovery.rs +++ b/substrate/client/network/src/litep2p/discovery.rs @@ -66,12 +66,18 @@ const KADEMLIA_QUERY_INTERVAL: Duration = Duration::from_secs(5); /// mDNS query interval. const MDNS_QUERY_INTERVAL: Duration = Duration::from_secs(30); -/// Minimum number of confirmations received before an address is verified. -const MIN_ADDRESS_CONFIRMATIONS: usize = 5; - -// The minimum number of peers we expect an answer before we terminate the request. +/// The minimum number of peers we expect an answer before we terminate the request. const GET_RECORD_REDUNDANCY_FACTOR: usize = 4; +/// The maximum number of tracked external addresses we allow. +const MAX_EXTERNAL_ADDRESSES: u32 = 32; + +/// Minimum number of confirmations received before an address is verified. +/// +/// Note: all addresses are confirmed by libp2p on the first encounter. This aims to make +/// addresses a bit more robust. +const MIN_ADDRESS_CONFIRMATIONS: usize = 2; + /// Discovery events. #[derive(Debug)] pub enum DiscoveryEvent { @@ -195,7 +201,7 @@ pub struct Discovery { listen_addresses: Arc>>, /// External address confirmations. - address_confirmations: LruMap, + address_confirmations: LruMap>, /// Delay to next `FIND_NODE` query. duration_to_next_find_query: Duration, @@ -278,7 +284,7 @@ impl Discovery { find_node_query_id: None, pending_events: VecDeque::new(), duration_to_next_find_query: Duration::from_secs(1), - address_confirmations: LruMap::new(ByLength::new(8)), + address_confirmations: LruMap::new(ByLength::new(MAX_EXTERNAL_ADDRESSES)), allow_non_global_addresses: config.allow_non_globals_in_dht, public_addresses: config.public_addresses.iter().cloned().map(Into::into).collect(), next_kad_query: Some(Delay::new(KADEMLIA_QUERY_INTERVAL)), @@ -428,7 +434,7 @@ impl Discovery { } /// Check if `address` can be considered a new external address. - fn is_new_external_address(&mut self, address: &Multiaddr) -> bool { + fn is_new_external_address(&mut self, address: &Multiaddr, peer: PeerId) -> bool { log::trace!(target: LOG_TARGET, "verify new external address: {address}"); // is the address one of our known addresses @@ -444,14 +450,14 @@ impl Discovery { match self.address_confirmations.get(address) { Some(confirmations) => { - *confirmations += 1usize; + confirmations.insert(peer); - if *confirmations >= MIN_ADDRESS_CONFIRMATIONS { + if confirmations.len() >= MIN_ADDRESS_CONFIRMATIONS { return true } }, None => { - self.address_confirmations.insert(address.clone(), 1usize); + self.address_confirmations.insert(address.clone(), Default::default()); }, } @@ -563,7 +569,7 @@ impl Stream for Discovery { supported_protocols, observed_address, })) => { - if this.is_new_external_address(&observed_address) { + if this.is_new_external_address(&observed_address, peer) { this.pending_events.push_back(DiscoveryEvent::ExternalAddressDiscovered { address: observed_address.clone(), }); diff --git a/substrate/client/network/src/litep2p/mod.rs b/substrate/client/network/src/litep2p/mod.rs index 475046cb9dba..521f1a5fe0f7 100644 --- a/substrate/client/network/src/litep2p/mod.rs +++ b/substrate/client/network/src/litep2p/mod.rs @@ -920,7 +920,7 @@ impl NetworkBackend for Litep2pNetworkBac let mut addresses = self.external_addresses.write(); if addresses.insert(address.clone()) { - log::info!(target: LOG_TARGET, "discovered new external address for our node: {address}"); + log::info!(target: LOG_TARGET, "🔍 Discovered new external address for our node: {address}"); } } Some(DiscoveryEvent::Ping { peer, rtt }) => { From 6a5b6e03bfc8d0c6f5f05f3180313902c15aee84 Mon Sep 17 00:00:00 2001 From: Egor_P Date: Thu, 1 Aug 2024 10:43:04 +0200 Subject: [PATCH 053/382] Adjust sync templates flow to use new release branch (#5182) As the release branch name changed starting from this release, this PR adds it to the sync templates flow so that checkout step worked properly. --------- Co-authored-by: rzadp --- .github/workflows/misc-sync-templates.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/misc-sync-templates.yml b/.github/workflows/misc-sync-templates.yml index 35e08fcaa131..d22dc8724f37 100644 --- a/.github/workflows/misc-sync-templates.yml +++ b/.github/workflows/misc-sync-templates.yml @@ -18,11 +18,10 @@ on: # A manual dispatch for now - automatic on releases later. workflow_dispatch: inputs: - crate_release_version: - description: 'A release version to use, e.g. 1.9.0' + stable_release_branch: + description: 'Stable release branch, e.g. stable2407' required: true - jobs: sync-templates: runs-on: ubuntu-latest @@ -44,7 +43,7 @@ jobs: - uses: actions/checkout@v4 with: path: polkadot-sdk - ref: "release-crates-io-v${{ github.event.inputs.crate_release_version }}" + ref: "${{ github.event.inputs.stable_release_branch }}" - name: Generate a token for the template repository id: app_token uses: actions/create-github-app-token@v1.9.3 @@ -72,8 +71,8 @@ jobs: # 2. Yanking the template out of the monorepo workspace. - - name: Use psvm to replace git references with released creates. - run: find . -type f -name 'Cargo.toml' -exec psvm -o -v ${{ github.event.inputs.crate_release_version }} -p {} \; + - name: Replace dev-dependencies path references with workspace references + run: find . -type f -name 'Cargo.toml' -exec sed -i'' -E "s/path = \"\.\.\/.*\"/workspace = true/g" {} \; working-directory: polkadot-sdk/templates/${{ matrix.template }}/ - name: Create a new workspace Cargo.toml run: | @@ -123,7 +122,7 @@ jobs: cp -r polkadot-sdk/templates/${{ matrix.template }}/* "${{ env.template-path }}/" - name: Run psvm on monorepo workspace dependencies - run: psvm -o -v ${{ github.event.inputs.crate_release_version }} -p ./Cargo.toml + run: psvm -o -v ${{ github.event.inputs.stable_release_branch }} -p ./Cargo.toml working-directory: polkadot-sdk/ - name: Copy over required workspace dependencies run: | @@ -164,12 +163,12 @@ jobs: token: ${{ steps.app_token.outputs.token }} add-paths: | ./* - title: "[Don't merge] Update the ${{ matrix.template }} template to ${{ github.event.inputs.crate_release_version }}" + title: "[Don't merge] Update the ${{ matrix.template }} template to ${{ github.event.inputs.stable_release_branch }}" body: "The template has NOT been successfully built and needs to be inspected." - branch: "update-template/${{ github.event.inputs.crate_release_version }}" + branch: "update-template/${{ github.event.inputs.stable_release_branch }}" - name: Push changes run: | git add -A . - git commit --allow-empty -m "Update to ${{ github.event.inputs.crate_release_version }} triggered by ${{ github.event_name }}" + git commit --allow-empty -m "Update to ${{ github.event.inputs.stable_release_branch }} triggered by ${{ github.event_name }}" git push working-directory: "${{ env.template-path }}" From 776e95748901b50ff2833a7d27ea83fd91fbf9d1 Mon Sep 17 00:00:00 2001 From: Khaled Fouad Date: Thu, 1 Aug 2024 15:46:14 +0200 Subject: [PATCH 054/382] Replace env_logger with sp_tracing (#5065) This PR replaces env_logger with sp_tracing because of an issue with env_logger and gum #4660 --------- Co-authored-by: Adrian Catangiu Co-authored-by: Andrei Eres --- Cargo.lock | 38 +++--- Cargo.toml | 43 ++++--- bridges/relays/utils/Cargo.toml | 2 +- bridges/relays/utils/src/initialize.rs | 110 ++++-------------- .../outbound-queue/merkle-tree/Cargo.toml | 2 +- .../outbound-queue/merkle-tree/src/lib.rs | 12 +- polkadot/node/core/approval-voting/Cargo.toml | 2 +- .../node/core/approval-voting/src/tests.rs | 6 +- polkadot/node/core/av-store/Cargo.toml | 2 +- polkadot/node/core/av-store/src/tests.rs | 6 +- .../network/approval-distribution/Cargo.toml | 2 +- .../approval-distribution/src/tests.rs | 5 +- .../network/bitfield-distribution/Cargo.toml | 3 +- .../bitfield-distribution/src/tests.rs | 40 ++----- .../node/network/collator-protocol/Cargo.toml | 3 +- .../src/collator_side/tests/mod.rs | 6 +- .../src/validator_side/tests/mod.rs | 6 +- polkadot/node/service/Cargo.toml | 2 +- polkadot/node/service/src/tests.rs | 5 +- polkadot/node/subsystem-bench/Cargo.toml | 2 +- .../src/cli/subsystem-bench.rs | 9 +- polkadot/node/subsystem-util/Cargo.toml | 1 - polkadot/xcm/xcm-runtime-apis/Cargo.toml | 2 +- .../xcm-runtime-apis/tests/fee_estimation.rs | 6 +- prdoc/pr_5065.prdoc | 65 +++++++++++ substrate/client/executor/Cargo.toml | 1 - substrate/client/executor/benches/bench.rs | 2 +- substrate/client/rpc/Cargo.toml | 2 +- substrate/client/rpc/src/author/tests.rs | 4 +- substrate/client/statement-store/Cargo.toml | 2 +- substrate/client/statement-store/src/lib.rs | 2 +- substrate/frame/contracts/Cargo.toml | 1 - substrate/frame/contracts/src/tests.rs | 4 +- .../frame/merkle-mountain-range/Cargo.toml | 2 +- .../frame/merkle-mountain-range/src/tests.rs | 28 ++--- substrate/frame/sassafras/src/tests.rs | 1 - substrate/primitives/tracing/Cargo.toml | 5 +- substrate/primitives/tracing/src/lib.rs | 5 +- substrate/utils/binary-merkle-tree/Cargo.toml | 2 +- substrate/utils/binary-merkle-tree/src/lib.rs | 18 +-- substrate/utils/frame/omni-bencher/Cargo.toml | 2 +- .../utils/frame/omni-bencher/src/main.rs | 3 +- 42 files changed, 196 insertions(+), 268 deletions(-) create mode 100644 prdoc/pr_5065.prdoc diff --git a/Cargo.lock b/Cargo.lock index 7466975fa428..b89cdf7828c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1477,11 +1477,11 @@ name = "binary-merkle-tree" version = "13.0.0" dependencies = [ "array-bytes", - "env_logger 0.11.3", "hash-db", "log", "sp-core", "sp-runtime", + "sp-tracing 16.0.0", ] [[package]] @@ -5267,7 +5267,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" dependencies = [ "log", - "regex", ] [[package]] @@ -5302,7 +5301,6 @@ dependencies = [ "anstream", "anstyle", "env_filter", - "humantime", "log", ] @@ -5967,12 +5965,12 @@ version = "0.1.0" dependencies = [ "clap 4.5.11", "cumulus-primitives-proof-size-hostfunction", - "env_logger 0.11.3", "frame-benchmarking-cli", "log", "sc-cli", "sp-runtime", "sp-statement-store", + "sp-tracing 16.0.0", ] [[package]] @@ -10307,7 +10305,6 @@ dependencies = [ "array-bytes", "assert_matches", "bitflags 1.3.2", - "env_logger 0.11.3", "environmental", "frame-benchmarking", "frame-support", @@ -10985,7 +10982,6 @@ name = "pallet-mmr" version = "27.0.0" dependencies = [ "array-bytes", - "env_logger 0.11.3", "frame-benchmarking", "frame-support", "frame-system", @@ -10997,6 +10993,7 @@ dependencies = [ "sp-io", "sp-mmr-primitives", "sp-runtime", + "sp-tracing 16.0.0", ] [[package]] @@ -12861,7 +12858,6 @@ version = "7.0.0" dependencies = [ "assert_matches", "bitvec", - "env_logger 0.11.3", "futures", "futures-timer", "itertools 0.11.0", @@ -12881,6 +12877,7 @@ dependencies = [ "schnorrkel 0.11.4", "sp-authority-discovery", "sp-core", + "sp-tracing 16.0.0", "tracing-gum", ] @@ -12891,10 +12888,8 @@ dependencies = [ "always-assert", "assert_matches", "bitvec", - "env_logger 0.11.3", "futures", "futures-timer", - "log", "maplit", "polkadot-node-network-protocol", "polkadot-node-subsystem", @@ -12908,6 +12903,7 @@ dependencies = [ "sp-core", "sp-keyring", "sp-keystore", + "sp-tracing 16.0.0", "tracing-gum", ] @@ -13020,11 +13016,9 @@ version = "7.0.0" dependencies = [ "assert_matches", "bitvec", - "env_logger 0.11.3", "fatality", "futures", "futures-timer", - "log", "parity-scale-codec", "polkadot-node-network-protocol", "polkadot-node-primitives", @@ -13040,6 +13034,7 @@ dependencies = [ "sp-keyring", "sp-keystore", "sp-runtime", + "sp-tracing 16.0.0", "thiserror", "tokio-util", "tracing-gum", @@ -13193,7 +13188,6 @@ dependencies = [ "async-trait", "bitvec", "derive_more", - "env_logger 0.11.3", "futures", "futures-timer", "itertools 0.11.0", @@ -13226,6 +13220,7 @@ dependencies = [ "sp-keyring", "sp-keystore", "sp-runtime", + "sp-tracing 16.0.0", "thiserror", "tracing-gum", ] @@ -13236,7 +13231,6 @@ version = "7.0.0" dependencies = [ "assert_matches", "bitvec", - "env_logger 0.11.3", "futures", "futures-timer", "kvdb", @@ -13256,6 +13250,7 @@ dependencies = [ "sp-consensus", "sp-core", "sp-keyring", + "sp-tracing 16.0.0", "thiserror", "tracing-gum", ] @@ -13777,7 +13772,6 @@ dependencies = [ "assert_matches", "async-trait", "derive_more", - "env_logger 0.11.3", "fatality", "futures", "futures-channel", @@ -14665,7 +14659,6 @@ dependencies = [ "assert_matches", "async-trait", "bitvec", - "env_logger 0.11.3", "frame-benchmarking", "frame-benchmarking-cli", "frame-metadata-hash-extension", @@ -14773,6 +14766,7 @@ dependencies = [ "sp-state-machine", "sp-storage 19.0.0", "sp-timestamp", + "sp-tracing 16.0.0", "sp-transaction-pool", "sp-version", "sp-weights", @@ -14843,7 +14837,6 @@ dependencies = [ "clap-num", "color-eyre", "colored", - "env_logger 0.11.3", "futures", "futures-timer", "hex", @@ -14896,6 +14889,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-timestamp", + "sp-tracing 16.0.0", "strum 0.26.2", "substrate-prometheus-endpoint", "tokio", @@ -16214,7 +16208,6 @@ dependencies = [ "backoff", "bp-runtime", "console", - "env_logger 0.11.3", "futures", "isahc", "jsonpath_lib", @@ -16223,6 +16216,7 @@ dependencies = [ "parking_lot 0.12.3", "serde_json", "sp-runtime", + "sp-tracing 16.0.0", "substrate-prometheus-endpoint", "sysinfo", "thiserror", @@ -17602,7 +17596,6 @@ dependencies = [ "array-bytes", "assert_matches", "criterion", - "env_logger 0.11.3", "num_cpus", "parity-scale-codec", "parking_lot 0.12.3", @@ -18045,7 +18038,6 @@ name = "sc-rpc" version = "29.0.0" dependencies = [ "assert_matches", - "env_logger 0.11.3", "futures", "jsonrpsee", "log", @@ -18076,6 +18068,7 @@ dependencies = [ "sp-runtime", "sp-session", "sp-statement-store", + "sp-tracing 16.0.0", "sp-version", "substrate-test-runtime-client", "tokio", @@ -18291,7 +18284,6 @@ dependencies = [ name = "sc-statement-store" version = "10.0.0" dependencies = [ - "env_logger 0.11.3", "log", "parity-db", "parking_lot 0.12.3", @@ -18302,6 +18294,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-statement-store", + "sp-tracing 16.0.0", "substrate-prometheus-endpoint", "tempfile", "tokio", @@ -19456,7 +19449,6 @@ name = "snowbridge-outbound-queue-merkle-tree" version = "0.3.0" dependencies = [ "array-bytes", - "env_logger 0.11.3", "hex", "hex-literal", "parity-scale-codec", @@ -19464,6 +19456,7 @@ dependencies = [ "sp-core", "sp-crypto-hashing", "sp-runtime", + "sp-tracing 16.0.0", ] [[package]] @@ -22513,6 +22506,7 @@ dependencies = [ "sharded-slab", "smallvec", "thread_local", + "time", "tracing", "tracing-core", "tracing-log 0.2.0", @@ -24152,7 +24146,6 @@ dependencies = [ name = "xcm-runtime-apis" version = "0.1.0" dependencies = [ - "env_logger 0.11.3", "frame-executive", "frame-support", "frame-system", @@ -24165,6 +24158,7 @@ dependencies = [ "scale-info", "sp-api", "sp-io", + "sp-tracing 16.0.0", "sp-weights", "staging-xcm", "staging-xcm-builder", diff --git a/Cargo.toml b/Cargo.toml index 703e1ebbcf62..7ae7c3bd1811 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [workspace.package] authors = ["Parity Technologies "] edition = "2021" -repository = "https://github.com/paritytech/polkadot-sdk.git" -license = "GPL-3.0-only" homepage = "https://paritytech.github.io/polkadot-sdk/" +license = "GPL-3.0-only" +repository = "https://github.com/paritytech/polkadot-sdk.git" [workspace] resolver = "2" @@ -547,29 +547,29 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(substrate_runtime)'] } [workspace.lints.clippy] all = { level = "allow", priority = 0 } -correctness = { level = "warn", priority = 1 } +bind_instead_of_map = { level = "allow", priority = 2 } # stylistic +borrowed-box = { level = "allow", priority = 2 } # Reasonable to fix this one complexity = { level = "warn", priority = 1 } +correctness = { level = "warn", priority = 1 } +default_constructed_unit_structs = { level = "allow", priority = 2 } # stylistic +derivable_impls = { level = "allow", priority = 2 } # false positives +eq_op = { level = "allow", priority = 2 } # In tests we test equality. +erasing_op = { level = "allow", priority = 2 } # E.g. 0 * DOLLARS +extra-unused-type-parameters = { level = "allow", priority = 2 } # stylistic +identity-op = { level = "allow", priority = 2 } # One case where we do 0 + if-same-then-else = { level = "allow", priority = 2 } -zero-prefixed-literal = { level = "allow", priority = 2 } # 00_1000_000 -type_complexity = { level = "allow", priority = 2 } # raison d'etre +needless-lifetimes = { level = "allow", priority = 2 } # generated code +needless_option_as_deref = { level = "allow", priority = 2 } # false positives nonminimal-bool = { level = "allow", priority = 2 } # maybe -borrowed-box = { level = "allow", priority = 2 } # Reasonable to fix this one +option-map-unit-fn = { level = "allow", priority = 2 } # stylistic +stable_sort_primitive = { level = "allow", priority = 2 } # prefer stable sort too-many-arguments = { level = "allow", priority = 2 } # (Turning this on would lead to) -needless-lifetimes = { level = "allow", priority = 2 } # generated code +type_complexity = { level = "allow", priority = 2 } # raison d'etre +unit_arg = { level = "allow", priority = 2 } # stylistic unnecessary_cast = { level = "allow", priority = 2 } # Types may change -identity-op = { level = "allow", priority = 2 } # One case where we do 0 + useless_conversion = { level = "allow", priority = 2 } # Types may change -unit_arg = { level = "allow", priority = 2 } # stylistic -option-map-unit-fn = { level = "allow", priority = 2 } # stylistic -bind_instead_of_map = { level = "allow", priority = 2 } # stylistic -erasing_op = { level = "allow", priority = 2 } # E.g. 0 * DOLLARS -eq_op = { level = "allow", priority = 2 } # In tests we test equality. while_immutable_condition = { level = "allow", priority = 2 } # false positives -needless_option_as_deref = { level = "allow", priority = 2 } # false positives -derivable_impls = { level = "allow", priority = 2 } # false positives -stable_sort_primitive = { level = "allow", priority = 2 } # prefer stable sort -extra-unused-type-parameters = { level = "allow", priority = 2 } # stylistic -default_constructed_unit_structs = { level = "allow", priority = 2 } # stylistic +zero-prefixed-literal = { level = "allow", priority = 2 } # 00_1000_000 [workspace.dependencies] Inflector = { version = "0.11.4" } @@ -729,7 +729,6 @@ either = { version = "1.8.1", default-features = false } emulated-integration-tests-common = { path = "cumulus/parachains/integration-tests/emulated/common", default-features = false } enumflags2 = { version = "0.7.7" } enumn = { version = "0.1.13" } -env_logger = { version = "0.11.3" } environmental = { version = "1.1.4", default-features = false } equivocation-detector = { path = "bridges/relays/equivocation" } ethabi = { version = "1.0.0", default-features = false, package = "ethabi-decode" } @@ -1358,22 +1357,22 @@ zstd = { version = "0.12.4", default-features = false } [profile.release] # Polkadot runtime requires unwinding. -panic = "unwind" opt-level = 3 +panic = "unwind" # make sure dev builds with backtrace do not slow us down [profile.dev.package.backtrace] inherits = "release" [profile.production] +codegen-units = 1 inherits = "release" lto = true -codegen-units = 1 [profile.testnet] -inherits = "release" debug = 1 # debug symbols are useful for profilers debug-assertions = true +inherits = "release" overflow-checks = true # The list of dependencies below (which can be both direct and indirect dependencies) are crates diff --git a/bridges/relays/utils/Cargo.toml b/bridges/relays/utils/Cargo.toml index 2c3f37750486..beb03b9381d4 100644 --- a/bridges/relays/utils/Cargo.toml +++ b/bridges/relays/utils/Cargo.toml @@ -17,7 +17,7 @@ async-trait = { workspace = true } backoff = { workspace = true } console = { workspace = true } isahc = { workspace = true } -env_logger = { workspace = true } +sp-tracing = { workspace = true, default-features = true } futures = { workspace = true } jsonpath_lib = { workspace = true } log = { workspace = true } diff --git a/bridges/relays/utils/src/initialize.rs b/bridges/relays/utils/src/initialize.rs index cd0377caa3d6..564ed1f0e5cc 100644 --- a/bridges/relays/utils/src/initialize.rs +++ b/bridges/relays/utils/src/initialize.rs @@ -16,9 +16,15 @@ //! Relayer initialization functions. -use console::style; use parking_lot::Mutex; -use std::{cell::RefCell, fmt::Display, io::Write}; +use sp_tracing::{ + tracing::Level, + tracing_subscriber::{ + fmt::{time::OffsetTime, SubscriberBuilder}, + EnvFilter, + }, +}; +use std::cell::RefCell; /// Relayer version that is provided as metric. Must be set by a binary /// (get it with `option_env!("CARGO_PKG_VERSION")` from a binary package code). @@ -41,101 +47,25 @@ pub fn initialize_logger(with_timestamp: bool) { ) .expect("static format string is valid"); - let mut builder = env_logger::Builder::new(); - builder.filter_level(log::LevelFilter::Warn); - builder.filter_module("bridge", log::LevelFilter::Info); - builder.parse_default_env(); - if with_timestamp { - builder.format(move |buf, record| { - let timestamp = time::OffsetDateTime::now_local() - .unwrap_or_else(|_| time::OffsetDateTime::now_utc()); - let timestamp = timestamp.format(&format).unwrap_or_else(|_| timestamp.to_string()); + let local_time = OffsetTime::new( + time::UtcOffset::current_local_offset().unwrap_or(time::UtcOffset::UTC), + format, + ); - let log_level = color_level(record.level()); - let log_target = color_target(record.target()); - let timestamp = if cfg!(windows) { - Either::Left(timestamp) - } else { - Either::Right(style(timestamp).black().bright().bold().to_string()) - }; + let env_filter = EnvFilter::from_default_env() + .add_directive(Level::WARN.into()) + .add_directive("bridge=info".parse().expect("static filter string is valid")); - writeln!( - buf, - "{}{} {} {} {}", - loop_name_prefix(), - timestamp, - log_level, - log_target, - record.args(), - ) - }); - } else { - builder.format(move |buf, record| { - let log_level = color_level(record.level()); - let log_target = color_target(record.target()); + let builder = SubscriberBuilder::default().with_env_filter(env_filter); - writeln!(buf, "{}{log_level} {log_target} {}", loop_name_prefix(), record.args(),) - }); + if with_timestamp { + builder.with_timer(local_time).init(); + } else { + builder.without_time().init(); } - - builder.init(); } /// Initialize relay loop. Must only be called once per every loop task. pub(crate) fn initialize_loop(loop_name: String) { LOOP_NAME.with(|g_loop_name| *g_loop_name.borrow_mut() = loop_name); } - -/// Returns loop name prefix to use in logs. The prefix is initialized with the `initialize_loop` -/// call. -fn loop_name_prefix() -> String { - // try_with to avoid panic outside of async-std task context - LOOP_NAME - .try_with(|loop_name| { - // using borrow is ok here, because loop is only initialized once (=> borrow_mut will - // only be called once) - let loop_name = loop_name.borrow(); - if loop_name.is_empty() { - String::new() - } else { - format!("[{loop_name}] ") - } - }) - .unwrap_or_else(|_| String::new()) -} - -enum Either { - Left(A), - Right(B), -} -impl Display for Either { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Self::Left(a) => write!(fmt, "{a}"), - Self::Right(b) => write!(fmt, "{b}"), - } - } -} - -fn color_target(target: &str) -> impl Display + '_ { - if cfg!(windows) { - Either::Left(target) - } else { - Either::Right(style(target).black().bright().to_string()) - } -} - -fn color_level(level: log::Level) -> impl Display { - if cfg!(windows) { - Either::Left(level) - } else { - let s = level.to_string(); - Either::Right(match level { - log::Level::Error => style(s).red().bright().bold().to_string(), - log::Level::Warn => style(s).yellow().bright().bold().to_string(), - log::Level::Info => style(s).green().bright().to_string(), - log::Level::Debug => style(s).cyan().bright().to_string(), - log::Level::Trace => style(s).blue().bright().to_string(), - }) - } -} diff --git a/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml index 00cc700fbe83..9d4cffc98d78 100644 --- a/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml +++ b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml @@ -23,10 +23,10 @@ sp-runtime = { workspace = true } [dev-dependencies] hex-literal = { workspace = true, default-features = true } -env_logger = { workspace = true } hex = { workspace = true, default-features = true } array-bytes = { workspace = true, default-features = true } sp-crypto-hashing = { workspace = true, default-features = true } +sp-tracing = { workspace = true, default-features = true } [features] default = ["std"] diff --git a/bridges/snowbridge/pallets/outbound-queue/merkle-tree/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/src/lib.rs index 8c91ccd04d9a..d5c89b9c0987 100644 --- a/bridges/snowbridge/pallets/outbound-queue/merkle-tree/src/lib.rs +++ b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/src/lib.rs @@ -335,7 +335,7 @@ mod tests { #[test] fn should_generate_empty_root() { // given - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let data = vec![]; // when @@ -351,7 +351,7 @@ mod tests { #[test] fn should_generate_single_root() { // given - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let data = make_leaves(1); // when @@ -367,7 +367,7 @@ mod tests { #[test] fn should_generate_root_pow_2() { // given - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let data = make_leaves(2); // when @@ -382,7 +382,7 @@ mod tests { #[test] fn should_generate_root_complex() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let test = |root, data: Vec| { assert_eq!( array_bytes::bytes2hex("", merkle_root::(data.into_iter()).as_ref()), @@ -401,7 +401,7 @@ mod tests { #[ignore] fn should_generate_and_verify_proof() { // given - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let data: Vec = make_leaves(3); // when @@ -458,7 +458,7 @@ mod tests { #[test] #[should_panic] fn should_panic_on_invalid_leaf_index() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); merkle_proof::(make_leaves(1).into_iter(), 5); } } diff --git a/polkadot/node/core/approval-voting/Cargo.toml b/polkadot/node/core/approval-voting/Cargo.toml index 65985c0a5db9..bc0187bf4922 100644 --- a/polkadot/node/core/approval-voting/Cargo.toml +++ b/polkadot/node/core/approval-voting/Cargo.toml @@ -52,7 +52,7 @@ assert_matches = { workspace = true } kvdb-memorydb = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } log = { workspace = true, default-features = true } -env_logger = { workspace = true } +sp-tracing = { workspace = true } polkadot-subsystem-bench = { workspace = true } diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs index 64ae86bc013a..b912449baa4d 100644 --- a/polkadot/node/core/approval-voting/src/tests.rs +++ b/polkadot/node/core/approval-voting/src/tests.rs @@ -543,11 +543,7 @@ fn test_harness>( config: HarnessConfig, test: impl FnOnce(TestHarness) -> T, ) { - let _ = env_logger::builder() - .is_test(true) - .filter(Some("polkadot_node_core_approval_voting"), log::LevelFilter::Trace) - .filter(Some(LOG_TARGET), log::LevelFilter::Trace) - .try_init(); + sp_tracing::init_for_tests(); let HarnessConfig { sync_oracle, sync_oracle_handle, clock, backend, assignment_criteria } = config; diff --git a/polkadot/node/core/av-store/Cargo.toml b/polkadot/node/core/av-store/Cargo.toml index 4274c8b576a3..c867180e541b 100644 --- a/polkadot/node/core/av-store/Cargo.toml +++ b/polkadot/node/core/av-store/Cargo.toml @@ -29,9 +29,9 @@ polkadot-node-jaeger = { workspace = true, default-features = true } [dev-dependencies] log = { workspace = true, default-features = true } -env_logger = { workspace = true } assert_matches = { workspace = true } kvdb-memorydb = { workspace = true } +sp-tracing = { workspace = true } sp-core = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } diff --git a/polkadot/node/core/av-store/src/tests.rs b/polkadot/node/core/av-store/src/tests.rs index 04a223730bcd..958917a3104f 100644 --- a/polkadot/node/core/av-store/src/tests.rs +++ b/polkadot/node/core/av-store/src/tests.rs @@ -122,11 +122,7 @@ fn test_harness>( store: Arc, test: impl FnOnce(VirtualOverseer) -> T, ) { - let _ = env_logger::builder() - .is_test(true) - .filter(Some("polkadot_node_core_av_store"), log::LevelFilter::Trace) - .filter(Some(LOG_TARGET), log::LevelFilter::Trace) - .try_init(); + sp_tracing::init_for_tests(); let pool = sp_core::testing::TaskExecutor::new(); let (context, virtual_overseer) = diff --git a/polkadot/node/network/approval-distribution/Cargo.toml b/polkadot/node/network/approval-distribution/Cargo.toml index a85cde303b61..1bd3d51b5c93 100644 --- a/polkadot/node/network/approval-distribution/Cargo.toml +++ b/polkadot/node/network/approval-distribution/Cargo.toml @@ -37,5 +37,5 @@ schnorrkel = { workspace = true } # rand_core should match schnorrkel rand_core = { workspace = true } rand_chacha = { workspace = true, default-features = true } -env_logger = { workspace = true } +sp-tracing = { workspace = true } log = { workspace = true, default-features = true } diff --git a/polkadot/node/network/approval-distribution/src/tests.rs b/polkadot/node/network/approval-distribution/src/tests.rs index 2d08807f97b6..3ea722c51a92 100644 --- a/polkadot/node/network/approval-distribution/src/tests.rs +++ b/polkadot/node/network/approval-distribution/src/tests.rs @@ -50,10 +50,7 @@ fn test_harness>( mut state: State, test_fn: impl FnOnce(VirtualOverseer) -> T, ) -> State { - let _ = env_logger::builder() - .is_test(true) - .filter(Some(LOG_TARGET), log::LevelFilter::Trace) - .try_init(); + sp_tracing::init_for_tests(); let pool = sp_core::testing::TaskExecutor::new(); let (context, virtual_overseer) = diff --git a/polkadot/node/network/bitfield-distribution/Cargo.toml b/polkadot/node/network/bitfield-distribution/Cargo.toml index b1becaf319d5..6d007255c574 100644 --- a/polkadot/node/network/bitfield-distribution/Cargo.toml +++ b/polkadot/node/network/bitfield-distribution/Cargo.toml @@ -29,7 +29,6 @@ sp-authority-discovery = { workspace = true, default-features = true } sp-keystore = { workspace = true, default-features = true } sp-keyring = { workspace = true, default-features = true } maplit = { workspace = true } -log = { workspace = true, default-features = true } -env_logger = { workspace = true } +sp-tracing = { workspace = true } assert_matches = { workspace = true } rand_chacha = { workspace = true, default-features = true } diff --git a/polkadot/node/network/bitfield-distribution/src/tests.rs b/polkadot/node/network/bitfield-distribution/src/tests.rs index dc37f73ec8a1..4ed4bf6b38c5 100644 --- a/polkadot/node/network/bitfield-distribution/src/tests.rs +++ b/polkadot/node/network/bitfield-distribution/src/tests.rs @@ -137,10 +137,7 @@ fn state_with_view( #[test] fn receive_invalid_signature() { - let _ = env_logger::builder() - .filter(None, log::LevelFilter::Trace) - .is_test(true) - .try_init(); + sp_tracing::init_for_tests(); let hash_a: Hash = [0; 32].into(); @@ -254,10 +251,7 @@ fn receive_invalid_signature() { #[test] fn receive_invalid_validator_index() { - let _ = env_logger::builder() - .filter(None, log::LevelFilter::Trace) - .is_test(true) - .try_init(); + sp_tracing::init_for_tests(); let hash_a: Hash = [0; 32].into(); let hash_b: Hash = [1; 32].into(); // other @@ -317,10 +311,7 @@ fn receive_invalid_validator_index() { #[test] fn receive_duplicate_messages() { - let _ = env_logger::builder() - .filter(None, log::LevelFilter::Trace) - .is_test(true) - .try_init(); + sp_tracing::init_for_tests(); let hash_a: Hash = [0; 32].into(); let hash_b: Hash = [1; 32].into(); @@ -442,10 +433,7 @@ fn receive_duplicate_messages() { fn delay_reputation_change() { use polkadot_node_subsystem_util::reputation::add_reputation; - let _ = env_logger::builder() - .filter(None, log::LevelFilter::Trace) - .is_test(true) - .try_init(); + sp_tracing::init_for_tests(); let hash_a: Hash = [0; 32].into(); let hash_b: Hash = [1; 32].into(); @@ -550,10 +538,7 @@ fn delay_reputation_change() { #[test] fn do_not_relay_message_twice() { - let _ = env_logger::builder() - .filter(None, log::LevelFilter::Trace) - .is_test(true) - .try_init(); + sp_tracing::init_for_tests(); let hash = Hash::random(); @@ -658,10 +643,7 @@ fn do_not_relay_message_twice() { #[test] fn changing_view() { - let _ = env_logger::builder() - .filter(None, log::LevelFilter::Trace) - .is_test(true) - .try_init(); + sp_tracing::init_for_tests(); let hash_a: Hash = [0; 32].into(); let hash_b: Hash = [1; 32].into(); @@ -833,10 +815,7 @@ fn changing_view() { #[test] fn do_not_send_message_back_to_origin() { - let _ = env_logger::builder() - .filter(None, log::LevelFilter::Trace) - .is_test(true) - .try_init(); + sp_tracing::init_for_tests(); let hash: Hash = [0; 32].into(); @@ -920,10 +899,7 @@ fn do_not_send_message_back_to_origin() { #[test] fn topology_test() { - let _ = env_logger::builder() - .filter(None, log::LevelFilter::Trace) - .is_test(true) - .try_init(); + sp_tracing::init_for_tests(); let hash: Hash = [0; 32].into(); diff --git a/polkadot/node/network/collator-protocol/Cargo.toml b/polkadot/node/network/collator-protocol/Cargo.toml index d41fc7ebe8dd..8a7c384dcbe8 100644 --- a/polkadot/node/network/collator-protocol/Cargo.toml +++ b/polkadot/node/network/collator-protocol/Cargo.toml @@ -29,8 +29,7 @@ thiserror = { workspace = true } tokio-util = { workspace = true } [dev-dependencies] -log = { workspace = true, default-features = true } -env_logger = { workspace = true } +sp-tracing = { workspace = true } assert_matches = { workspace = true } rstest = { workspace = true } diff --git a/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs index 13601ca7a005..74a151c168dc 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs @@ -239,11 +239,7 @@ fn test_harness>( reputation: ReputationAggregator, test: impl FnOnce(TestHarness) -> T, ) { - let _ = env_logger::builder() - .is_test(true) - .filter(Some("polkadot_collator_protocol"), log::LevelFilter::Trace) - .filter(Some(LOG_TARGET), log::LevelFilter::Trace) - .try_init(); + let _ = sp_tracing::init_for_tests(); let pool = sp_core::testing::TaskExecutor::new(); diff --git a/polkadot/node/network/collator-protocol/src/validator_side/tests/mod.rs b/polkadot/node/network/collator-protocol/src/validator_side/tests/mod.rs index 44e25efd4dfc..86c8bcb6bdcd 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/tests/mod.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/tests/mod.rs @@ -156,11 +156,7 @@ fn test_harness>( reputation: ReputationAggregator, test: impl FnOnce(TestHarness) -> T, ) { - let _ = env_logger::builder() - .is_test(true) - .filter(Some("polkadot_collator_protocol"), log::LevelFilter::Trace) - .filter(Some(LOG_TARGET), log::LevelFilter::Trace) - .try_init(); + sp_tracing::init_for_tests(); let pool = sp_core::testing::TaskExecutor::new(); diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index c0ddbf7dcfc3..216aa10e8acb 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -149,7 +149,7 @@ xcm-runtime-apis = { workspace = true, default-features = true } polkadot-test-client = { workspace = true } polkadot-node-subsystem-test-helpers = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } -env_logger = { workspace = true } +sp-tracing = { workspace = true } assert_matches = { workspace = true } serial_test = { workspace = true } tempfile = { workspace = true } diff --git a/polkadot/node/service/src/tests.rs b/polkadot/node/service/src/tests.rs index bebd05071013..195432bcb75d 100644 --- a/polkadot/node/service/src/tests.rs +++ b/polkadot/node/service/src/tests.rs @@ -70,10 +70,7 @@ fn test_harness>( case_vars: CaseVars, test: impl FnOnce(TestHarness) -> T, ) { - let _ = env_logger::builder() - .is_test(true) - .filter_level(log::LevelFilter::Trace) - .try_init(); + sp_tracing::init_for_tests(); let pool = TaskExecutor::new(); let (mut context, virtual_overseer) = diff --git a/polkadot/node/subsystem-bench/Cargo.toml b/polkadot/node/subsystem-bench/Cargo.toml index 0325613d25f9..9384f8142a97 100644 --- a/polkadot/node/subsystem-bench/Cargo.toml +++ b/polkadot/node/subsystem-bench/Cargo.toml @@ -49,7 +49,7 @@ hex = { workspace = true, default-features = true } gum = { workspace = true, default-features = true } polkadot-erasure-coding = { workspace = true, default-features = true } log = { workspace = true, default-features = true } -env_logger = { workspace = true } +sp-tracing = { workspace = true } rand = { workspace = true, default-features = true } # `rand` only supports uniform distribution, we need normal distribution for latency. rand_distr = { workspace = true } diff --git a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs index 346a058b9796..153efdda4056 100644 --- a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -184,14 +184,7 @@ impl BenchCli { fn main() -> eyre::Result<()> { color_eyre::install()?; - env_logger::builder() - .filter(Some("hyper"), log::LevelFilter::Info) - // Avoid `Terminating due to subsystem exit subsystem` warnings - .filter(Some("polkadot_overseer"), log::LevelFilter::Error) - .filter(None, log::LevelFilter::Info) - .format_timestamp_millis() - .try_init() - .unwrap(); + sp_tracing::try_init_simple(); let cli: BenchCli = BenchCli::parse(); cli.launch()?; diff --git a/polkadot/node/subsystem-util/Cargo.toml b/polkadot/node/subsystem-util/Cargo.toml index 98ea21f250ed..a7157d1b5b7f 100644 --- a/polkadot/node/subsystem-util/Cargo.toml +++ b/polkadot/node/subsystem-util/Cargo.toml @@ -45,7 +45,6 @@ parity-db = { workspace = true } [dev-dependencies] assert_matches = { workspace = true } -env_logger = { workspace = true } futures = { features = ["thread-pool"], workspace = true } log = { workspace = true, default-features = true } polkadot-node-subsystem-test-helpers = { workspace = true } diff --git a/polkadot/xcm/xcm-runtime-apis/Cargo.toml b/polkadot/xcm/xcm-runtime-apis/Cargo.toml index 748d5af68a1f..9ccca76c321c 100644 --- a/polkadot/xcm/xcm-runtime-apis/Cargo.toml +++ b/polkadot/xcm/xcm-runtime-apis/Cargo.toml @@ -31,7 +31,7 @@ pallet-assets = { workspace = true } xcm-executor = { workspace = true } frame-executive = { workspace = true } log = { workspace = true } -env_logger = { workspace = true } +sp-tracing = { workspace = true, default-features = true } [features] default = ["std"] diff --git a/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs b/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs index 59ee17973805..e5dac7c7a04e 100644 --- a/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs @@ -41,7 +41,7 @@ use mock::{ // Parachain(2000) -------------------------------------------> Parachain(1000) #[test] fn fee_estimation_for_teleport() { - let _ = env_logger::builder().is_test(true).try_init(); + sp_tracing::init_for_tests(); let who = 1; // AccountId = u64. let balances = vec![(who, 100 + DeliveryFees::get() + ExistentialDeposit::get())]; let assets = vec![(1, who, 50)]; @@ -195,7 +195,7 @@ fn fee_estimation_for_teleport() { // Parachain(2000) -------------------------------------------> Parachain(1000) #[test] fn dry_run_reserve_asset_transfer() { - let _ = env_logger::builder().is_test(true).try_init(); + sp_tracing::init_for_tests(); let who = 1; // AccountId = u64. // Native token used for fees. let balances = vec![(who, DeliveryFees::get() + ExistentialDeposit::get())]; @@ -274,7 +274,7 @@ fn dry_run_reserve_asset_transfer() { #[test] fn dry_run_xcm() { - let _ = env_logger::builder().is_test(true).try_init(); + sp_tracing::init_for_tests(); let who = 1; // AccountId = u64. let transfer_amount = 100u128; // We need to build the XCM to weigh it and then build the real XCM that can pay for fees. diff --git a/prdoc/pr_5065.prdoc b/prdoc/pr_5065.prdoc new file mode 100644 index 000000000000..11fca2ab71d6 --- /dev/null +++ b/prdoc/pr_5065.prdoc @@ -0,0 +1,65 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Replace env_logger with sp_tracing + +doc: + - audience: Node Dev + description: | + This PR replaces env_logger with sp_tracing because of an issue with env_logger and gum #4660 + +crates: + - name: relay-utils + bump: none + - name: snowbridge-outbound-queue-merkle-tree + bump: patch + - name: polkadot-node-core-approval-voting + bump: patch + - name: polkadot-node-core-av-store + bump: patch + - name: polkadot-approval-distribution + bump: patch + - name: polkadot-availability-bitfield-distribution + bump: patch + - name: polkadot-collator-protocol + bump: patch + - name: polkadot-service + bump: patch + - name: polkadot-subsystem-bench + bump: none + - name: polkadot-node-subsystem-util + bump: none + - name: xcm-runtime-apis + bump: patch + - name: sc-executor + bump: patch + - name: sc-rpc + bump: patch + - name: sc-statement-store + bump: patch + - name: pallet-contracts + bump: patch + - name: pallet-mmr + bump: patch + - name: sp-tracing + bump: patch + - name: binary-merkle-tree + bump: patch + - name: frame-omni-bencher + bump: patch + - name: pallet-balances + bump: patch + - name: pallet-staking + bump: patch + - name: pallet-treasury + bump: patch + - name: pallet-bounties + bump: patch + - name: pallet-conviction-voting + bump: patch + - name: pallet-democracy + bump: patch + - name: pallet-elections-phragmen + bump: patch + - name: pallet-referenda + bump: patch \ No newline at end of file diff --git a/substrate/client/executor/Cargo.toml b/substrate/client/executor/Cargo.toml index 2c24663bccca..ca78afd47068 100644 --- a/substrate/client/executor/Cargo.toml +++ b/substrate/client/executor/Cargo.toml @@ -51,7 +51,6 @@ tracing-subscriber = { workspace = true } paste = { workspace = true, default-features = true } regex = { workspace = true } criterion = { workspace = true, default-features = true } -env_logger = { workspace = true } num_cpus = { workspace = true } tempfile = { workspace = true } diff --git a/substrate/client/executor/benches/bench.rs b/substrate/client/executor/benches/bench.rs index 86c769f88811..4cde8c2a4a64 100644 --- a/substrate/client/executor/benches/bench.rs +++ b/substrate/client/executor/benches/bench.rs @@ -147,7 +147,7 @@ fn run_benchmark( } fn bench_call_instance(c: &mut Criterion) { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let strategies = [ ( diff --git a/substrate/client/rpc/Cargo.toml b/substrate/client/rpc/Cargo.toml index e8d9ad4948d7..4a8f4b3ec630 100644 --- a/substrate/client/rpc/Cargo.toml +++ b/substrate/client/rpc/Cargo.toml @@ -43,7 +43,7 @@ sp-statement-store = { workspace = true, default-features = true } tokio = { workspace = true, default-features = true } [dev-dependencies] -env_logger = { workspace = true } +sp-tracing = { workspace = true } assert_matches = { workspace = true } sc-block-builder = { workspace = true, default-features = true } sc-network = { workspace = true, default-features = true } diff --git a/substrate/client/rpc/src/author/tests.rs b/substrate/client/rpc/src/author/tests.rs index 937870eb53fd..6bcb3e7863c0 100644 --- a/substrate/client/rpc/src/author/tests.rs +++ b/substrate/client/rpc/src/author/tests.rs @@ -89,7 +89,7 @@ impl TestSetup { #[tokio::test] async fn author_submit_transaction_should_not_cause_error() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let author = TestSetup::default().author(); let api = author.into_rpc(); let xt: Bytes = uxt(AccountKeyring::Alice, 1).encode().into(); @@ -279,7 +279,7 @@ async fn author_has_session_keys() { #[tokio::test] async fn author_has_key() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let api = TestSetup::into_rpc(); let suri = "//Alice"; diff --git a/substrate/client/statement-store/Cargo.toml b/substrate/client/statement-store/Cargo.toml index ef0bfc548673..e5087eae6eca 100644 --- a/substrate/client/statement-store/Cargo.toml +++ b/substrate/client/statement-store/Cargo.toml @@ -31,4 +31,4 @@ sc-keystore = { workspace = true, default-features = true } [dev-dependencies] tempfile = { workspace = true } -env_logger = { workspace = true } +sp-tracing = { workspace = true } diff --git a/substrate/client/statement-store/src/lib.rs b/substrate/client/statement-store/src/lib.rs index da0af08b4540..799246a96188 100644 --- a/substrate/client/statement-store/src/lib.rs +++ b/substrate/client/statement-store/src/lib.rs @@ -1022,7 +1022,7 @@ mod tests { } fn test_store() -> (Store, tempfile::TempDir) { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let temp_dir = tempfile::Builder::new().tempdir().expect("Error creating test dir"); let client = std::sync::Arc::new(TestClient); diff --git a/substrate/frame/contracts/Cargo.toml b/substrate/frame/contracts/Cargo.toml index 1aa3d3513a7e..316ea6813048 100644 --- a/substrate/frame/contracts/Cargo.toml +++ b/substrate/frame/contracts/Cargo.toml @@ -58,7 +58,6 @@ xcm-builder = { workspace = true } [dev-dependencies] array-bytes = { workspace = true, default-features = true } assert_matches = { workspace = true } -env_logger = { workspace = true } pretty_assertions = { workspace = true } wat = { workspace = true } pallet-contracts-fixtures = { workspace = true } diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs index cc2a69b5c419..c3b6e3273f34 100644 --- a/substrate/frame/contracts/src/tests.rs +++ b/substrate/frame/contracts/src/tests.rs @@ -549,9 +549,7 @@ impl ExtBuilder { self } pub fn build(self) -> sp_io::TestExternalities { - use env_logger::{Builder, Env}; - let env = Env::new().default_filter_or("runtime=debug"); - let _ = Builder::from_env(env).is_test(true).try_init(); + sp_tracing::try_init_simple(); self.set_associated_consts(); let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![] } diff --git a/substrate/frame/merkle-mountain-range/Cargo.toml b/substrate/frame/merkle-mountain-range/Cargo.toml index e8b00f6e56cb..4daa394a82d7 100644 --- a/substrate/frame/merkle-mountain-range/Cargo.toml +++ b/substrate/frame/merkle-mountain-range/Cargo.toml @@ -28,7 +28,7 @@ sp-runtime = { workspace = true } [dev-dependencies] array-bytes = { workspace = true, default-features = true } -env_logger = { workspace = true } +sp-tracing = { workspace = true, default-features = true } itertools = { workspace = true } [features] diff --git a/substrate/frame/merkle-mountain-range/src/tests.rs b/substrate/frame/merkle-mountain-range/src/tests.rs index b8c9d54db820..93e3d06eaa0a 100644 --- a/substrate/frame/merkle-mountain-range/src/tests.rs +++ b/substrate/frame/merkle-mountain-range/src/tests.rs @@ -81,7 +81,7 @@ fn add_blocks(blocks: usize) { #[test] fn should_start_empty() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); new_test_ext().execute_with(|| { // given assert_eq!( @@ -112,7 +112,7 @@ fn should_start_empty() { #[test] fn should_append_to_mmr_when_on_initialize_is_called() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let mut ext = new_test_ext(); let (parent_b1, parent_b2) = ext.execute_with(|| { // when @@ -191,7 +191,7 @@ fn should_append_to_mmr_when_on_initialize_is_called() { #[test] fn should_construct_larger_mmr_correctly() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); new_test_ext().execute_with(|| { // when add_blocks(7); @@ -222,7 +222,7 @@ fn should_construct_larger_mmr_correctly() { #[test] fn should_calculate_the_size_correctly() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let leaves = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 21]; let sizes = vec![0, 1, 3, 4, 7, 8, 10, 11, 15, 16, 18, 19, 22, 23, 25, 26, 39]; @@ -243,7 +243,7 @@ fn should_calculate_the_size_correctly() { #[test] fn should_generate_proofs_correctly() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let mut ext = new_test_ext(); // given let num_blocks: u64 = 7; @@ -418,7 +418,7 @@ fn should_generate_proofs_correctly() { #[test] fn should_generate_batch_proof_correctly() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let mut ext = new_test_ext(); // given ext.execute_with(|| add_blocks(7)); @@ -471,7 +471,7 @@ fn should_generate_batch_proof_correctly() { #[test] fn should_verify() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); // Start off with chain initialisation and storing indexing data off-chain // (MMR Leafs) @@ -550,7 +550,7 @@ fn generate_and_verify_batch_proof( #[test] fn should_verify_batch_proofs() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); use itertools::Itertools; @@ -598,7 +598,7 @@ fn should_verify_batch_proofs() { #[test] fn verification_should_be_stateless() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); // Start off with chain initialisation and storing indexing data off-chain // (MMR Leafs) @@ -646,7 +646,7 @@ fn verification_should_be_stateless() { #[test] fn should_verify_batch_proof_statelessly() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); // Start off with chain initialisation and storing indexing data off-chain // (MMR Leafs) @@ -699,7 +699,7 @@ fn should_verify_batch_proof_statelessly() { #[test] fn should_verify_on_the_next_block_since_there_is_no_pruning_yet() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let mut ext = new_test_ext(); // given ext.execute_with(|| add_blocks(7)); @@ -720,7 +720,7 @@ fn should_verify_on_the_next_block_since_there_is_no_pruning_yet() { #[test] fn should_verify_canonicalized() { use frame_support::traits::Hooks; - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); // How deep is our fork-aware storage (in terms of blocks/leaves, nodes will be more). let block_hash_size: u64 = ::BlockHashCount::get(); @@ -760,7 +760,7 @@ fn should_verify_canonicalized() { #[test] fn does_not_panic_when_generating_historical_proofs() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let mut ext = new_test_ext(); // given 7 blocks (7 MMR leaves) @@ -790,7 +790,7 @@ fn does_not_panic_when_generating_historical_proofs() { #[test] fn generating_and_verifying_ancestry_proofs_works_correctly() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let mut ext = new_test_ext(); let mut prev_roots = vec![]; diff --git a/substrate/frame/sassafras/src/tests.rs b/substrate/frame/sassafras/src/tests.rs index ec3425cce7bf..b3dc1ebed865 100644 --- a/substrate/frame/sassafras/src/tests.rs +++ b/substrate/frame/sassafras/src/tests.rs @@ -788,7 +788,6 @@ fn trivial_fisher_yates_shuffle(vector: &mut Vec, random_seed: u64) { #[test] fn submit_tickets_with_ring_proof_check_works() { use sp_core::Pair as _; - // env_logger::init(); let (authorities, mut tickets): (Vec, Vec) = data_read(TICKETS_FILE); diff --git a/substrate/primitives/tracing/Cargo.toml b/substrate/primitives/tracing/Cargo.toml index 7874338aa616..8621582c7654 100644 --- a/substrate/primitives/tracing/Cargo.toml +++ b/substrate/primitives/tracing/Cargo.toml @@ -21,13 +21,12 @@ features = ["with-tracing"] targets = ["wasm32-unknown-unknown", "x86_64-unknown-linux-gnu"] [dependencies] -codec = { features = [ - "derive", -], workspace = true } +codec = { features = ["derive"], workspace = true } tracing = { workspace = true } tracing-core = { workspace = true } tracing-subscriber = { workspace = true, optional = true, features = [ "env-filter", + "time", "tracing-log", ] } diff --git a/substrate/primitives/tracing/src/lib.rs b/substrate/primitives/tracing/src/lib.rs index 34ed088aed0b..21bba52d07ca 100644 --- a/substrate/primitives/tracing/src/lib.rs +++ b/substrate/primitives/tracing/src/lib.rs @@ -40,12 +40,15 @@ extern crate alloc; #[cfg(feature = "std")] -use tracing; +pub use tracing; pub use tracing::{ debug, debug_span, error, error_span, event, info, info_span, span, trace, trace_span, warn, warn_span, Level, Span, }; +#[cfg(feature = "std")] +pub use tracing_subscriber; + pub use crate::types::{ WasmEntryAttributes, WasmFieldName, WasmFields, WasmLevel, WasmMetadata, WasmValue, WasmValuesSet, diff --git a/substrate/utils/binary-merkle-tree/Cargo.toml b/substrate/utils/binary-merkle-tree/Cargo.toml index 5f7e80549aa4..087ec5fd6c6d 100644 --- a/substrate/utils/binary-merkle-tree/Cargo.toml +++ b/substrate/utils/binary-merkle-tree/Cargo.toml @@ -18,7 +18,7 @@ hash-db = { workspace = true } [dev-dependencies] array-bytes = { workspace = true, default-features = true } -env_logger = { workspace = true } +sp-tracing = { workspace = true, default-features = true } sp-core = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } diff --git a/substrate/utils/binary-merkle-tree/src/lib.rs b/substrate/utils/binary-merkle-tree/src/lib.rs index 0efab9186c25..030e9f69b9b6 100644 --- a/substrate/utils/binary-merkle-tree/src/lib.rs +++ b/substrate/utils/binary-merkle-tree/src/lib.rs @@ -356,7 +356,7 @@ mod tests { #[test] fn should_generate_empty_root() { // given - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let data: Vec<[u8; 1]> = Default::default(); // when @@ -372,7 +372,7 @@ mod tests { #[test] fn should_generate_single_root() { // given - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let data = vec![array_bytes::hex2array_unchecked::<_, 20>( "E04CC55ebEE1cBCE552f250e85c57B70B2E2625b", )]; @@ -390,7 +390,7 @@ mod tests { #[test] fn should_generate_root_pow_2() { // given - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let data = vec![ array_bytes::hex2array_unchecked::<_, 20>("E04CC55ebEE1cBCE552f250e85c57B70B2E2625b"), array_bytes::hex2array_unchecked::<_, 20>("25451A4de12dcCc2D166922fA938E900fCc4ED24"), @@ -408,7 +408,7 @@ mod tests { #[test] fn should_generate_root_complex() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let test = |root, data| { assert_eq!(array_bytes::bytes2hex("", &merkle_root::(data)), root); }; @@ -437,7 +437,7 @@ mod tests { #[test] fn should_generate_and_verify_proof_simple() { // given - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let data = vec!["a", "b", "c"]; // when @@ -501,7 +501,7 @@ mod tests { #[test] fn should_generate_and_verify_proof_complex() { // given - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let data = vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; for l in 0..data.len() { @@ -521,7 +521,7 @@ mod tests { #[test] fn should_generate_and_verify_proof_large() { // given - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let mut data = vec![]; for i in 1..16 { for c in 'a'..'z' { @@ -548,7 +548,7 @@ mod tests { #[test] fn should_generate_and_verify_proof_large_tree() { // given - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); let mut data = vec![]; for i in 0..6000 { data.push(format!("{}", i)); @@ -571,7 +571,7 @@ mod tests { #[test] #[should_panic] fn should_panic_on_invalid_leaf_index() { - let _ = env_logger::try_init(); + sp_tracing::init_for_tests(); merkle_proof::(vec!["a"], 5); } diff --git a/substrate/utils/frame/omni-bencher/Cargo.toml b/substrate/utils/frame/omni-bencher/Cargo.toml index f8f44cb4b438..89ef2a48e01d 100644 --- a/substrate/utils/frame/omni-bencher/Cargo.toml +++ b/substrate/utils/frame/omni-bencher/Cargo.toml @@ -17,5 +17,5 @@ frame-benchmarking-cli = { workspace = true } sc-cli = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } sp-statement-store = { workspace = true, default-features = true } -env_logger = { workspace = true } +sp-tracing = { workspace = true } log = { workspace = true } diff --git a/substrate/utils/frame/omni-bencher/src/main.rs b/substrate/utils/frame/omni-bencher/src/main.rs index c148403f2970..a8893b5a79af 100644 --- a/substrate/utils/frame/omni-bencher/src/main.rs +++ b/substrate/utils/frame/omni-bencher/src/main.rs @@ -18,11 +18,10 @@ mod command; use clap::Parser; -use env_logger::Env; use sc_cli::Result; fn main() -> Result<()> { - env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); + sp_tracing::try_init_simple(); log::warn!("The FRAME omni-bencher is not yet battle tested - double check the results.",); command::Command::parse().run() From 8ccb6b33c564da038de2af987d4e8d347f32e9c7 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 2 Aug 2024 14:24:19 +0200 Subject: [PATCH 055/382] Add an adapter for configuring AssetExchanger (#5130) Added a new adapter to xcm-builder, the `SingleAssetExchangeAdapter`. This adapter makes it easy to use `pallet-asset-conversion` for configuring the `AssetExchanger` XCM config item. I also took the liberty of adding a new function to the `AssetExchange` trait, with the following signature: ```rust fn quote_exchange_price(give: &Assets, want: &Assets, maximal: bool) -> Option; ``` The signature is meant to be fairly symmetric to that of `exchange_asset`. The way they interact can be seen in the doc comment for it in the `AssetExchange` trait. This is a breaking change but is needed for https://github.com/paritytech/polkadot-sdk/pull/5131. Another idea is to create a new trait for this but that would require setting it in the XCM config which is also breaking. Old PR: https://github.com/paritytech/polkadot-sdk/pull/4375. --------- Co-authored-by: Adrian Catangiu --- Cargo.lock | 2 + cumulus/primitives/utility/src/lib.rs | 21 +- polkadot/xcm/xcm-builder/Cargo.toml | 7 +- .../xcm/xcm-builder/src/asset_exchange/mod.rs | 22 ++ .../single_asset_adapter/adapter.rs | 210 ++++++++++ .../single_asset_adapter/mock.rs | 370 ++++++++++++++++++ .../single_asset_adapter/mod.rs | 25 ++ .../single_asset_adapter/tests.rs | 233 +++++++++++ polkadot/xcm/xcm-builder/src/lib.rs | 3 + polkadot/xcm/xcm-builder/src/test_utils.rs | 4 + polkadot/xcm/xcm-builder/src/tests/mock.rs | 14 + .../xcm-executor/src/traits/asset_exchange.rs | 31 ++ prdoc/pr_5130.prdoc | 40 ++ substrate/frame/asset-conversion/src/lib.rs | 2 +- 14 files changed, 980 insertions(+), 4 deletions(-) create mode 100644 polkadot/xcm/xcm-builder/src/asset_exchange/mod.rs create mode 100644 polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/adapter.rs create mode 100644 polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs create mode 100644 polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mod.rs create mode 100644 polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/tests.rs create mode 100644 prdoc/pr_5130.prdoc diff --git a/Cargo.lock b/Cargo.lock index b89cdf7828c8..8cca629e3791 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21024,6 +21024,7 @@ dependencies = [ "frame-system", "impl-trait-for-tuples", "log", + "pallet-asset-conversion", "pallet-assets", "pallet-balances", "pallet-salary", @@ -21037,6 +21038,7 @@ dependencies = [ "primitive-types", "scale-info", "sp-arithmetic", + "sp-core", "sp-io", "sp-runtime", "sp-weights", diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 9d5bf4e231eb..3ebcb44fa439 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -407,10 +407,22 @@ impl< let first_asset: Asset = payment.fungible.pop_first().ok_or(XcmError::AssetNotFound)?.into(); let (fungibles_asset, balance) = FungiblesAssetMatcher::matches_fungibles(&first_asset) - .map_err(|_| XcmError::AssetNotFound)?; + .map_err(|error| { + log::trace!( + target: "xcm::weight", + "SwapFirstAssetTrader::buy_weight asset {:?} didn't match. Error: {:?}", + first_asset, + error, + ); + XcmError::AssetNotFound + })?; let swap_asset = fungibles_asset.clone().into(); if Target::get().eq(&swap_asset) { + log::trace!( + target: "xcm::weight", + "SwapFirstAssetTrader::buy_weight Asset was same as Target, swap not needed.", + ); // current trader is not applicable. return Err(XcmError::FeesNotMet) } @@ -424,7 +436,12 @@ impl< credit_in, fee, ) - .map_err(|(credit_in, _)| { + .map_err(|(credit_in, error)| { + log::trace!( + target: "xcm::weight", + "SwapFirstAssetTrader::buy_weight swap couldn't be done. Error was: {:?}", + error, + ); drop(credit_in); XcmError::FeesNotMet })?; diff --git a/polkadot/xcm/xcm-builder/Cargo.toml b/polkadot/xcm/xcm-builder/Cargo.toml index 7702e2f9be07..671f0181277a 100644 --- a/polkadot/xcm/xcm-builder/Cargo.toml +++ b/polkadot/xcm/xcm-builder/Cargo.toml @@ -22,13 +22,15 @@ sp-weights = { workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } pallet-transaction-payment = { workspace = true } +pallet-asset-conversion = { workspace = true } log = { workspace = true } # Polkadot dependencies polkadot-parachain-primitives = { workspace = true } [dev-dependencies] -primitive-types = { workspace = true, default-features = true } +sp-core = { workspace = true, default-features = true } +primitive-types = { features = ["codec", "num-traits", "scale-info"], workspace = true } pallet-balances = { workspace = true, default-features = true } pallet-xcm = { workspace = true, default-features = true } pallet-salary = { workspace = true, default-features = true } @@ -43,6 +45,7 @@ default = ["std"] runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-asset-conversion/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-salary/runtime-benchmarks", @@ -59,8 +62,10 @@ std = [ "frame-support/std", "frame-system/std", "log/std", + "pallet-asset-conversion/std", "pallet-transaction-payment/std", "polkadot-parachain-primitives/std", + "primitive-types/std", "scale-info/std", "sp-arithmetic/std", "sp-io/std", diff --git a/polkadot/xcm/xcm-builder/src/asset_exchange/mod.rs b/polkadot/xcm/xcm-builder/src/asset_exchange/mod.rs new file mode 100644 index 000000000000..d42a443c9be1 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/asset_exchange/mod.rs @@ -0,0 +1,22 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Adapters for the AssetExchanger config item. +//! +//! E.g. types that implement the [`xcm_executor::traits::AssetExchange`] trait. + +mod single_asset_adapter; +pub use single_asset_adapter::SingleAssetExchangeAdapter; diff --git a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/adapter.rs b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/adapter.rs new file mode 100644 index 000000000000..fa94ee5f1caa --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/adapter.rs @@ -0,0 +1,210 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Single asset exchange adapter. + +extern crate alloc; +use alloc::vec; +use core::marker::PhantomData; +use frame_support::{ensure, traits::tokens::fungibles}; +use pallet_asset_conversion::{QuotePrice, SwapCredit}; +use xcm::prelude::*; +use xcm_executor::{ + traits::{AssetExchange, MatchesFungibles}, + AssetsInHolding, +}; + +/// An adapter from [`pallet_asset_conversion::SwapCredit`] and +/// [`pallet_asset_conversion::QuotePrice`] to [`xcm_executor::traits::AssetExchange`]. +/// +/// This adapter takes just one fungible asset in `give` and allows only one fungible asset in +/// `want`. If you need to handle more assets in either `give` or `want`, then you should use +/// another type that implements [`xcm_executor::traits::AssetExchange`] or build your own. +/// +/// This adapter also only works for fungible assets. +/// +/// `exchange_asset` and `quote_exchange_price` will both return an error if there's +/// more than one asset in `give` or `want`. +pub struct SingleAssetExchangeAdapter( + PhantomData<(AssetConversion, Fungibles, Matcher, AccountId)>, +); +impl AssetExchange + for SingleAssetExchangeAdapter +where + AssetConversion: SwapCredit< + AccountId, + Balance = u128, + AssetKind = Fungibles::AssetId, + Credit = fungibles::Credit, + > + QuotePrice, + Fungibles: fungibles::Balanced, + Matcher: MatchesFungibles, +{ + fn exchange_asset( + _: Option<&Location>, + give: AssetsInHolding, + want: &Assets, + maximal: bool, + ) -> Result { + let mut give_iter = give.fungible_assets_iter(); + let give_asset = give_iter.next().ok_or_else(|| { + log::trace!( + target: "xcm::SingleAssetExchangeAdapter::exchange_asset", + "No fungible asset was in `give`.", + ); + give.clone() + })?; + ensure!(give_iter.next().is_none(), give.clone()); // We only support 1 asset in `give`. + ensure!(give.non_fungible_assets_iter().next().is_none(), give.clone()); // We don't allow non-fungible assets. + ensure!(want.len() == 1, give.clone()); // We only support 1 asset in `want`. + let want_asset = want.get(0).ok_or_else(|| give.clone())?; + let (give_asset_id, give_amount) = + Matcher::matches_fungibles(&give_asset).map_err(|error| { + log::trace!( + target: "xcm::SingleAssetExchangeAdapter::exchange_asset", + "Could not map XCM asset give {:?} to FRAME asset. Error: {:?}", + give_asset, + error, + ); + give.clone() + })?; + let (want_asset_id, want_amount) = + Matcher::matches_fungibles(&want_asset).map_err(|error| { + log::trace!( + target: "xcm::SingleAssetExchangeAdapter::exchange_asset", + "Could not map XCM asset want {:?} to FRAME asset. Error: {:?}", + want_asset, + error, + ); + give.clone() + })?; + + // We have to do this to convert the XCM assets into credit the pool can use. + let swap_asset = give_asset_id.clone().into(); + let credit_in = Fungibles::issue(give_asset_id, give_amount); + + // Do the swap. + let (credit_out, maybe_credit_change) = if maximal { + // If `maximal`, then we swap exactly `credit_in` to get as much of `want_asset_id` as + // we can, with a minimum of `want_amount`. + let credit_out = >::swap_exact_tokens_for_tokens( + vec![swap_asset, want_asset_id], + credit_in, + Some(want_amount), + ) + .map_err(|(credit_in, error)| { + log::error!( + target: "xcm::SingleAssetExchangeAdapter::exchange_asset", + "Could not perform the swap, error: {:?}.", + error + ); + drop(credit_in); + give.clone() + })?; + + // We don't have leftover assets if exchange was maximal. + (credit_out, None) + } else { + // If `minimal`, then we swap as little of `credit_in` as we can to get exactly + // `want_amount` of `want_asset_id`. + let (credit_out, credit_change) = + >::swap_tokens_for_exact_tokens( + vec![swap_asset, want_asset_id], + credit_in, + want_amount, + ) + .map_err(|(credit_in, error)| { + log::error!( + target: "xcm::SingleAssetExchangeAdapter::exchange_asset", + "Could not perform the swap, error: {:?}.", + error + ); + drop(credit_in); + give.clone() + })?; + + (credit_out, Some(credit_change)) + }; + + // We create an `AssetsInHolding` instance by putting in the resulting asset + // of the exchange. + let resulting_asset: Asset = (want_asset.id.clone(), credit_out.peek()).into(); + let mut result: AssetsInHolding = resulting_asset.into(); + + // If we have some leftover assets from the exchange, also put them in the result. + if let Some(credit_change) = maybe_credit_change { + let leftover_asset: Asset = (give_asset.id.clone(), credit_change.peek()).into(); + result.subsume(leftover_asset); + } + + Ok(result.into()) + } + + fn quote_exchange_price(give: &Assets, want: &Assets, maximal: bool) -> Option { + if give.len() != 1 || want.len() != 1 { + return None; + } // We only support 1 asset in `give` or `want`. + let give_asset = give.get(0)?; + let want_asset = want.get(0)?; + // We first match both XCM assets to the asset ID types `AssetConversion` can handle. + let (give_asset_id, give_amount) = Matcher::matches_fungibles(give_asset) + .map_err(|error| { + log::trace!( + target: "xcm::SingleAssetExchangeAdapter::quote_exchange_price", + "Could not map XCM asset {:?} to FRAME asset. Error: {:?}.", + give_asset, + error, + ); + () + }) + .ok()?; + let (want_asset_id, want_amount) = Matcher::matches_fungibles(want_asset) + .map_err(|error| { + log::trace!( + target: "xcm::SingleAssetExchangeAdapter::quote_exchange_price", + "Could not map XCM asset {:?} to FRAME asset. Error: {:?}.", + want_asset, + error, + ); + () + }) + .ok()?; + // We quote the price. + if maximal { + // The amount of `want` resulting from swapping `give`. + let resulting_want = + ::quote_price_exact_tokens_for_tokens( + give_asset_id, + want_asset_id, + give_amount, + true, // Include fee. + )?; + + Some((want_asset.id.clone(), resulting_want).into()) + } else { + // The `give` amount required to obtain `want`. + let necessary_give = + ::quote_price_tokens_for_exact_tokens( + give_asset_id, + want_asset_id, + want_amount, + true, // Include fee. + )?; + + Some((give_asset.id.clone(), necessary_give).into()) + } + } +} diff --git a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs new file mode 100644 index 000000000000..4d9809e84f88 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs @@ -0,0 +1,370 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Mock to test [`SingleAssetExchangeAdapter`]. + +use core::marker::PhantomData; +use frame_support::{ + assert_ok, construct_runtime, derive_impl, ord_parameter_types, parameter_types, + traits::{ + fungible::{self, NativeFromLeft, NativeOrWithId}, + fungibles::Mutate, + tokens::imbalance::ResolveAssetTo, + AsEnsureOriginWithArg, Equals, Everything, Nothing, OriginTrait, PalletInfoAccess, + }, + PalletId, +}; +use sp_core::{ConstU128, ConstU32, Get}; +use sp_runtime::{ + traits::{AccountIdConversion, IdentityLookup, MaybeEquivalence, TryConvert, TryConvertInto}, + BuildStorage, Permill, +}; +use xcm::prelude::*; +use xcm_executor::{traits::ConvertLocation, XcmExecutor}; + +use crate::{FungibleAdapter, IsConcrete, MatchedConvertedConcreteId, StartsWith}; + +pub type Block = frame_system::mocking::MockBlock; +pub type AccountId = u64; +pub type Balance = u128; + +construct_runtime! { + pub struct Runtime { + System: frame_system, + Balances: pallet_balances, + AssetsPallet: pallet_assets::, + PoolAssets: pallet_assets::, + XcmPallet: pallet_xcm, + AssetConversion: pallet_asset_conversion, + } +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type Block = Block; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type AccountData = pallet_balances::AccountData; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Runtime { + type Balance = Balance; + type AccountStore = System; + type ExistentialDeposit = ConstU128<1>; +} + +pub type TrustBackedAssetsInstance = pallet_assets::Instance1; +pub type PoolAssetsInstance = pallet_assets::Instance2; + +#[derive_impl(pallet_assets::config_preludes::TestDefaultConfig)] +impl pallet_assets::Config for Runtime { + type Currency = Balances; + type Balance = Balance; + type AssetDeposit = ConstU128<1>; + type AssetAccountDeposit = ConstU128<10>; + type MetadataDepositBase = ConstU128<1>; + type MetadataDepositPerByte = ConstU128<1>; + type ApprovalDeposit = ConstU128<1>; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type Freezer = (); + type CallbackHandle = (); +} + +#[derive_impl(pallet_assets::config_preludes::TestDefaultConfig)] +impl pallet_assets::Config for Runtime { + type Currency = Balances; + type Balance = Balance; + type AssetDeposit = ConstU128<1>; + type AssetAccountDeposit = ConstU128<10>; + type MetadataDepositBase = ConstU128<1>; + type MetadataDepositPerByte = ConstU128<1>; + type ApprovalDeposit = ConstU128<1>; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type Freezer = (); + type CallbackHandle = (); +} + +/// Union fungibles implementation for `Assets` and `Balances`. +pub type NativeAndAssets = + fungible::UnionOf, AccountId>; + +parameter_types! { + pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon"); + pub const Native: NativeOrWithId = NativeOrWithId::Native; + pub const LiquidityWithdrawalFee: Permill = Permill::from_percent(0); +} + +ord_parameter_types! { + pub const AssetConversionOrigin: AccountId = + AccountIdConversion::::into_account_truncating(&AssetConversionPalletId::get()); +} + +pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< + AssetConversionPalletId, + (NativeOrWithId, NativeOrWithId), +>; + +impl pallet_asset_conversion::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type HigherPrecisionBalance = sp_core::U256; + type AssetKind = NativeOrWithId; + type Assets = NativeAndAssets; + type PoolId = (Self::AssetKind, Self::AssetKind); + type PoolLocator = pallet_asset_conversion::WithFirstAsset< + Native, + AccountId, + Self::AssetKind, + PoolIdToAccountId, + >; + type PoolAssetId = u32; + type PoolAssets = PoolAssets; + type PoolSetupFee = ConstU128<100>; // Asset class deposit fees are sufficient to prevent spam + type PoolSetupFeeAsset = Native; + type PoolSetupFeeTarget = ResolveAssetTo; + type LiquidityWithdrawalFee = LiquidityWithdrawalFee; + type LPFee = ConstU32<3>; + type PalletId = AssetConversionPalletId; + type MaxSwapPathLength = ConstU32<3>; + type MintMinLiquidity = ConstU128<100>; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +/// We only alias local accounts. +pub type LocationToAccountId = AccountIndex64Aliases; + +parameter_types! { + pub HereLocation: Location = Here.into_location(); + pub WeightPerInstruction: Weight = Weight::from_parts(1, 1); + pub MaxInstructions: u32 = 100; + pub UniversalLocation: InteriorLocation = [GlobalConsensus(Polkadot), Parachain(1000)].into(); + pub TrustBackedAssetsPalletIndex: u8 = ::index() as u8; + pub TrustBackedAssetsPalletLocation: Location = PalletInstance(TrustBackedAssetsPalletIndex::get()).into(); +} + +/// Adapter for the native token. +pub type FungibleTransactor = FungibleAdapter< + // Use this implementation of the `fungible::*` traits. + // `Balances` is the name given to the balances pallet + Balances, + // This transactor deals with the native token. + IsConcrete, + // How to convert an XCM Location into a local account id. + // This is also something that's configured in the XCM executor. + LocationToAccountId, + // The type for account ids, only needed because `fungible` is generic over it. + AccountId, + // Not tracking teleports. + (), +>; + +pub type Weigher = crate::FixedWeightBounds; + +pub struct LocationToAssetId; +impl MaybeEquivalence> for LocationToAssetId { + fn convert(location: &Location) -> Option> { + let pallet_instance = TrustBackedAssetsPalletIndex::get(); + match location.unpack() { + (0, [PalletInstance(instance), GeneralIndex(index)]) + if *instance == pallet_instance => + Some(NativeOrWithId::WithId(*index as u32)), + (0, []) => Some(NativeOrWithId::Native), + _ => None, + } + } + + fn convert_back(asset_id: &NativeOrWithId) -> Option { + let pallet_instance = TrustBackedAssetsPalletIndex::get(); + Some(match asset_id { + NativeOrWithId::WithId(id) => + Location::new(0, [PalletInstance(pallet_instance), GeneralIndex((*id).into())]), + NativeOrWithId::Native => Location::new(0, []), + }) + } +} + +pub type PoolAssetsExchanger = crate::SingleAssetExchangeAdapter< + AssetConversion, + NativeAndAssets, + MatchedConvertedConcreteId< + NativeOrWithId, + Balance, + (StartsWith, Equals), + LocationToAssetId, + TryConvertInto, + >, + AccountId, +>; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = (); + type AssetTransactor = FungibleTransactor; + type OriginConverter = (); + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + // This is not safe, you should use `crate::AllowTopLevelPaidExecutionFrom` in a + // production chain + type Barrier = crate::AllowUnpaidExecutionFrom; + type Weigher = Weigher; + type Trader = (); + type ResponseHandler = (); + type AssetTrap = (); + type AssetLocker = (); + type AssetExchanger = PoolAssetsExchanger; + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = (); + type FeeManager = (); + type MaxAssetsIntoHolding = ConstU32<1>; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = crate::FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +} + +/// Simple converter from a [`Location`] with an [`AccountIndex64`] junction and no parent to a +/// `u64`. +pub struct AccountIndex64Aliases; +impl ConvertLocation for AccountIndex64Aliases { + fn convert_location(location: &Location) -> Option { + let index = match location.unpack() { + (0, [AccountIndex64 { index, network: None }]) => index, + _ => return None, + }; + Some((*index).into()) + } +} + +/// `Convert` implementation to convert from some a `Signed` (system) `Origin` into an +/// `AccountIndex64`. +/// +/// Typically used when configuring `pallet-xcm` in tests to allow `u64` accounts to dispatch an XCM +/// from an `AccountIndex64` origin. +pub struct SignedToAccountIndex64( + PhantomData<(RuntimeOrigin, AccountId, Network)>, +); +impl, Network: Get>> + TryConvert for SignedToAccountIndex64 +where + RuntimeOrigin::PalletsOrigin: From> + + TryInto, Error = RuntimeOrigin::PalletsOrigin>, +{ + fn try_convert(o: RuntimeOrigin) -> Result { + o.try_with_caller(|caller| match caller.try_into() { + Ok(frame_system::RawOrigin::Signed(who)) => + Ok(Junction::AccountIndex64 { network: Network::get(), index: who.into() }.into()), + Ok(other) => Err(other.into()), + Err(other) => Err(other), + }) + } +} + +parameter_types! { + pub const NoNetwork: Option = None; +} + +pub type LocalOriginToLocation = SignedToAccountIndex64; + +impl pallet_xcm::Config for Runtime { + // We turn off sending for these tests + type SendXcmOrigin = crate::EnsureXcmOrigin; + type XcmRouter = (); + // Anyone can execute XCM programs + type ExecuteXcmOrigin = crate::EnsureXcmOrigin; + // We execute any type of program + type XcmExecuteFilter = Everything; + // How we execute programs + type XcmExecutor = XcmExecutor; + // We don't allow teleports + type XcmTeleportFilter = Nothing; + // We don't allow reserve transfers + type XcmReserveTransferFilter = Nothing; + // Same weigher executor uses to weigh XCM programs + type Weigher = Weigher; + // Same universal location + type UniversalLocation = UniversalLocation; + // No version discovery needed + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 0; + type AdvertisedXcmVersion = frame_support::traits::ConstU32<3>; + type AdminOrigin = frame_system::EnsureRoot; + // No locking + type TrustedLockers = (); + type MaxLockers = frame_support::traits::ConstU32<0>; + type MaxRemoteLockConsumers = frame_support::traits::ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + // How to turn locations into accounts + type SovereignAccountOf = LocationToAccountId; + // A currency to pay for things and its matcher, we are using the relay token + type Currency = Balances; + type CurrencyMatcher = crate::IsConcrete; + // Pallet benchmarks, no need for this recipe + type WeightInfo = pallet_xcm::TestWeightInfo; + // Runtime types + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; +} + +pub const INITIAL_BALANCE: Balance = 1_000_000_000; + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(0, INITIAL_BALANCE), (1, INITIAL_BALANCE), (2, INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + + let owner = 0; + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + assert_ok!(AssetsPallet::force_create(RuntimeOrigin::root(), 1, owner, false, 1,)); + assert_ok!(AssetsPallet::mint_into(1, &owner, INITIAL_BALANCE,)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(owner), + Box::new(NativeOrWithId::Native), + Box::new(NativeOrWithId::WithId(1)), + )); + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(owner), + Box::new(NativeOrWithId::Native), + Box::new(NativeOrWithId::WithId(1)), + 50_000_000, + 100_000_000, + 0, + 0, + owner, + )); + }); + ext +} diff --git a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mod.rs b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mod.rs new file mode 100644 index 000000000000..2a47832923f7 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mod.rs @@ -0,0 +1,25 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! SingleAssetExchangeAdapter. + +mod adapter; +pub use adapter::SingleAssetExchangeAdapter; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; diff --git a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/tests.rs b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/tests.rs new file mode 100644 index 000000000000..83f57f32822f --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/tests.rs @@ -0,0 +1,233 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Tests for the [`SingleAssetExchangeAdapter`] type. + +use super::mock::*; +use xcm::prelude::*; +use xcm_executor::{traits::AssetExchange, AssetsInHolding}; + +// ========== Happy path ========== + +/// Scenario: +/// Account #3 wants to use the local liquidity pool between two custom assets, +/// 1 and 2. +#[test] +fn maximal_exchange() { + new_test_ext().execute_with(|| { + let assets = PoolAssetsExchanger::exchange_asset( + None, + vec![([PalletInstance(2), GeneralIndex(1)], 10_000_000).into()].into(), + &vec![(Here, 2_000_000).into()].into(), + true, // Maximal + ) + .unwrap(); + let amount = get_amount_from_first_fungible(&assets); + assert_eq!(amount, 4_533_054); + }); +} + +#[test] +fn minimal_exchange() { + new_test_ext().execute_with(|| { + let assets = PoolAssetsExchanger::exchange_asset( + None, + vec![([PalletInstance(2), GeneralIndex(1)], 10_000_000).into()].into(), + &vec![(Here, 2_000_000).into()].into(), + false, // Minimal + ) + .unwrap(); + let (first_amount, second_amount) = get_amount_from_fungibles(&assets); + assert_eq!(first_amount, 2_000_000); + assert_eq!(second_amount, 5_820_795); + }); +} + +#[test] +fn maximal_quote() { + new_test_ext().execute_with(|| { + let assets = quote( + &([PalletInstance(2), GeneralIndex(1)], 10_000_000).into(), + &(Here, 2_000_000).into(), + true, + ) + .unwrap(); + let amount = get_amount_from_first_fungible(&assets.into()); + // The amount of the native token resulting from swapping all `10_000_000` of the custom + // token. + assert_eq!(amount, 4_533_054); + }); +} + +#[test] +fn minimal_quote() { + new_test_ext().execute_with(|| { + let assets = quote( + &([PalletInstance(2), GeneralIndex(1)], 10_000_000).into(), + &(Here, 2_000_000).into(), + false, + ) + .unwrap(); + let amount = get_amount_from_first_fungible(&assets.into()); + // The amount of the custom token needed to get `2_000_000` of the native token. + assert_eq!(amount, 4_179_205); + }); +} + +// ========== Unhappy path ========== + +#[test] +fn no_asset_in_give() { + new_test_ext().execute_with(|| { + assert!(PoolAssetsExchanger::exchange_asset( + None, + vec![].into(), + &vec![(Here, 2_000_000).into()].into(), + true + ) + .is_err()); + }); +} + +#[test] +fn more_than_one_asset_in_give() { + new_test_ext().execute_with(|| { + assert!(PoolAssetsExchanger::exchange_asset( + None, + vec![([PalletInstance(2), GeneralIndex(1)], 1).into(), (Here, 2).into()].into(), + &vec![(Here, 2_000_000).into()].into(), + true + ) + .is_err()); + }); +} + +#[test] +fn no_asset_in_want() { + new_test_ext().execute_with(|| { + assert!(PoolAssetsExchanger::exchange_asset( + None, + vec![([PalletInstance(2), GeneralIndex(1)], 10_000_000).into()].into(), + &vec![].into(), + true + ) + .is_err()); + }); +} + +#[test] +fn more_than_one_asset_in_want() { + new_test_ext().execute_with(|| { + assert!(PoolAssetsExchanger::exchange_asset( + None, + vec![([PalletInstance(2), GeneralIndex(1)], 10_000_000).into()].into(), + &vec![(Here, 2_000_000).into(), ([PalletInstance(2), GeneralIndex(1)], 1).into()] + .into(), + true + ) + .is_err()); + }); +} + +#[test] +fn give_asset_does_not_match() { + new_test_ext().execute_with(|| { + let nonexistent_asset_id = 1000; + assert!(PoolAssetsExchanger::exchange_asset( + None, + vec![([PalletInstance(2), GeneralIndex(nonexistent_asset_id)], 10_000_000).into()] + .into(), + &vec![(Here, 2_000_000).into()].into(), + true + ) + .is_err()); + }); +} + +#[test] +fn want_asset_does_not_match() { + new_test_ext().execute_with(|| { + let nonexistent_asset_id = 1000; + assert!(PoolAssetsExchanger::exchange_asset( + None, + vec![(Here, 2_000_000).into()].into(), + &vec![([PalletInstance(2), GeneralIndex(nonexistent_asset_id)], 10_000_000).into()] + .into(), + true + ) + .is_err()); + }); +} + +#[test] +fn exchange_fails() { + new_test_ext().execute_with(|| { + assert!(PoolAssetsExchanger::exchange_asset( + None, + vec![([PalletInstance(2), GeneralIndex(1)], 10_000_000).into()].into(), + // We're asking for too much of the native token... + &vec![(Here, 200_000_000).into()].into(), + false, // Minimal + ) + .is_err()); + }); +} + +#[test] +fn non_fungible_asset_in_give() { + new_test_ext().execute_with(|| { + assert!(PoolAssetsExchanger::exchange_asset( + None, + // Using `u64` here will give us a non-fungible instead of a fungible. + vec![([PalletInstance(2), GeneralIndex(2)], 10_000_000u64).into()].into(), + &vec![(Here, 10_000_000).into()].into(), + false, // Minimal + ) + .is_err()); + }); +} + +// ========== Helper functions ========== + +fn get_amount_from_first_fungible(assets: &AssetsInHolding) -> u128 { + let mut fungibles_iter = assets.fungible_assets_iter(); + let first_fungible = fungibles_iter.next().unwrap(); + let Fungible(amount) = first_fungible.fun else { + unreachable!("Asset should be fungible"); + }; + amount +} + +fn get_amount_from_fungibles(assets: &AssetsInHolding) -> (u128, u128) { + let mut fungibles_iter = assets.fungible_assets_iter(); + let first_fungible = fungibles_iter.next().unwrap(); + let Fungible(first_amount) = first_fungible.fun else { + unreachable!("Asset should be fungible"); + }; + let second_fungible = fungibles_iter.next().unwrap(); + let Fungible(second_amount) = second_fungible.fun else { + unreachable!("Asset should be fungible"); + }; + (first_amount, second_amount) +} + +fn quote(asset_1: &Asset, asset_2: &Asset, maximal: bool) -> Option { + PoolAssetsExchanger::quote_exchange_price( + &asset_1.clone().into(), + &asset_2.clone().into(), + maximal, + ) +} diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index 4cf83c9fc451..bec3bdcb05a0 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -35,6 +35,9 @@ pub use asset_conversion::{ AsPrefixedGeneralIndex, ConvertedConcreteId, MatchedConvertedConcreteId, }; +mod asset_exchange; +pub use asset_exchange::SingleAssetExchangeAdapter; + mod barriers; pub use barriers::{ AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, diff --git a/polkadot/xcm/xcm-builder/src/test_utils.rs b/polkadot/xcm/xcm-builder/src/test_utils.rs index 37a49a1b3dc7..90afb2c9a3d3 100644 --- a/polkadot/xcm/xcm-builder/src/test_utils.rs +++ b/polkadot/xcm/xcm-builder/src/test_utils.rs @@ -109,6 +109,10 @@ impl AssetExchange for TestAssetExchanger { ) -> Result { Ok(want.clone().into()) } + + fn quote_exchange_price(give: &Assets, _want: &Assets, _maximal: bool) -> Option { + Some(give.clone()) + } } pub struct TestPalletsInfo; diff --git a/polkadot/xcm/xcm-builder/src/tests/mock.rs b/polkadot/xcm/xcm-builder/src/tests/mock.rs index ac43d217ff3f..9f42aee87c9f 100644 --- a/polkadot/xcm/xcm-builder/src/tests/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/mock.rs @@ -695,6 +695,20 @@ impl AssetExchange for TestAssetExchange { EXCHANGE_ASSETS.with(|l| l.replace(have)); Ok(get) } + + fn quote_exchange_price(give: &Assets, want: &Assets, maximal: bool) -> Option { + let mut have = EXCHANGE_ASSETS.with(|l| l.borrow().clone()); + if !have.contains_assets(want) { + return None; + } + let get = if maximal { + have.saturating_take(give.clone().into()) + } else { + have.saturating_take(want.clone().into()) + }; + let result: Vec = get.fungible_assets_iter().collect(); + Some(result.into()) + } } pub struct SiblingPrefix; diff --git a/polkadot/xcm/xcm-executor/src/traits/asset_exchange.rs b/polkadot/xcm/xcm-executor/src/traits/asset_exchange.rs index 432a7498ed4c..f4b7135d4206 100644 --- a/polkadot/xcm/xcm-executor/src/traits/asset_exchange.rs +++ b/polkadot/xcm/xcm-executor/src/traits/asset_exchange.rs @@ -37,6 +37,27 @@ pub trait AssetExchange { want: &Assets, maximal: bool, ) -> Result; + + /// Handler for quoting the exchange price of two asset collections. + /// + /// It's useful before calling `exchange_asset`, to get some information on whether or not the + /// exchange will be successful. + /// + /// Arguments: + /// - `give` The asset(s) that are going to be given. + /// - `want` The asset(s) that are wanted. + /// - `maximal`: + /// - If `true`, then the return value is the resulting amount of `want` obtained by swapping + /// `give`. + /// - If `false`, then the return value is the required amount of `give` needed to get `want`. + /// + /// The return value is `Assets` since it comprises both which assets and how much of them. + /// + /// The relationship between this function and `exchange_asset` is the following: + /// - quote(give, want, maximal) = resulting_want -> exchange(give, resulting_want, maximal) ✅ + /// - quote(give, want, minimal) = required_give -> exchange(required_give_amount, want, + /// minimal) ✅ + fn quote_exchange_price(_give: &Assets, _want: &Assets, _maximal: bool) -> Option; } #[impl_trait_for_tuples::impl_for_tuples(30)] @@ -55,4 +76,14 @@ impl AssetExchange for Tuple { )* ); Err(give) } + + fn quote_exchange_price(give: &Assets, want: &Assets, maximal: bool) -> Option { + for_tuples!( #( + match Tuple::quote_exchange_price(give, want, maximal) { + Some(assets) => return Some(assets), + None => {} + } + )* ); + None + } } diff --git a/prdoc/pr_5130.prdoc b/prdoc/pr_5130.prdoc new file mode 100644 index 000000000000..c6a00505babc --- /dev/null +++ b/prdoc/pr_5130.prdoc @@ -0,0 +1,40 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add SingleAssetExchangeAdapter + +doc: + - audience: Runtime Dev + description: | + SingleAssetExchangeAdapter is an adapter in xcm-builder that can be used + to configure the AssetExchanger in XCM to use pallet-asset-conversion, + or any other type that implements the `SwapCredit` and `QuotePrice` traits. + It can be configured as follows: + ```rust + pub type AssetExchanger = SingleAssetExchangeAdapter< + // pallet-assets-conversion, as named in `construct_runtime`. + AssetConversion, + // The fungibles implementation that brings together all assets in pools. + // This may be created using `fungible::UnionOf` to mix the native token + // with more tokens. + Fungibles, + // The matcher for making sure which assets should be handled by this exchanger. + Matcher, + >; + ``` + It's called "single asset" since it will only allow exchanging one asset for another. + It will error out if more than one asset tries to be exchanged. + + Also, a new method was added to the `xcm_executor::traits::AssetExchange` trait: + `quote_exchange_price`. This is used to get the exchange price between two asset collections. + If you were using the trait, you now need to also implement this new function. + +crates: + - name: staging-xcm-executor + bump: major + - name: staging-xcm-builder + bump: minor + - name: pallet-asset-conversion + bump: minor + - name: cumulus-primitives-utility + bump: minor diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index a9dc30375e5a..d6671a45be55 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -435,7 +435,7 @@ pub mod pallet { /// calls to render the liquidity withdrawable and rectify the exchange rate. /// /// Once liquidity is added, someone may successfully call - /// [`Pallet::swap_exact_tokens_for_tokens`] successfully. + /// [`Pallet::swap_exact_tokens_for_tokens`]. #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::add_liquidity())] pub fn add_liquidity( From ce6938ae92b77b54aa367e6d367a4d490dede7c4 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Fri, 2 Aug 2024 18:09:13 +0300 Subject: [PATCH 056/382] rpc: Enable ChainSpec for polkadot-parachain (#5205) This PR enables the `chainSpec_v1` class for the polkadot-parachian. The chainSpec is part of the rpc-v2 which is spec-ed at: https://github.com/paritytech/json-rpc-interface-spec/blob/main/src/api/chainSpec.md. This also paves the way for enabling a future `chainSpec_unstable_spec` on all nodes. Closes: https://github.com/paritytech/polkadot-sdk/issues/5191 cc @paritytech/subxt-team --------- Signed-off-by: Alexandru Vasile --- Cargo.lock | 1 - polkadot/rpc/src/lib.rs | 6 ------ prdoc/pr_5205.prdoc | 18 ++++++++++++++++++ substrate/bin/node/rpc/Cargo.toml | 1 - substrate/bin/node/rpc/src/lib.rs | 6 ------ substrate/client/service/src/builder.rs | 13 +++++++++++-- 6 files changed, 29 insertions(+), 16 deletions(-) create mode 100644 prdoc/pr_5205.prdoc diff --git a/Cargo.lock b/Cargo.lock index 8cca629e3791..d37babb5bbc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9276,7 +9276,6 @@ dependencies = [ "sc-mixnet", "sc-rpc", "sc-rpc-api", - "sc-rpc-spec-v2", "sc-sync-state-rpc", "sc-transaction-pool-api", "sp-api", diff --git a/polkadot/rpc/src/lib.rs b/polkadot/rpc/src/lib.rs index 7d678ada5ff5..eb0133b6e8fd 100644 --- a/polkadot/rpc/src/lib.rs +++ b/polkadot/rpc/src/lib.rs @@ -124,7 +124,6 @@ where use sc_consensus_babe_rpc::{Babe, BabeApiServer}; use sc_consensus_beefy_rpc::{Beefy, BeefyApiServer}; use sc_consensus_grandpa_rpc::{Grandpa, GrandpaApiServer}; - use sc_rpc_spec_v2::chain_spec::{ChainSpec, ChainSpecApiServer}; use sc_sync_state_rpc::{SyncState, SyncStateApiServer}; use substrate_frame_rpc_system::{System, SystemApiServer}; use substrate_state_trie_migration_rpc::{StateMigration, StateMigrationApiServer}; @@ -139,11 +138,6 @@ where finality_provider, } = grandpa; - let chain_name = chain_spec.name().to_string(); - let genesis_hash = client.hash(0).ok().flatten().expect("Genesis block exists; qed"); - let properties = chain_spec.properties(); - - io.merge(ChainSpec::new(chain_name, genesis_hash, properties).into_rpc())?; io.merge(StateMigration::new(client.clone(), backend.clone(), deny_unsafe).into_rpc())?; io.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?; io.merge(TransactionPayment::new(client.clone()).into_rpc())?; diff --git a/prdoc/pr_5205.prdoc b/prdoc/pr_5205.prdoc new file mode 100644 index 000000000000..48abfe50ca24 --- /dev/null +++ b/prdoc/pr_5205.prdoc @@ -0,0 +1,18 @@ +title: Enable ChainSpec API for polkadot-parachain + +doc: + - audience: + - Runtime Dev + - Node Dev + description: | + The substrate service-builder now includes the entire rpc v2 API. + The chainspec API was previously defined as rpc extension where for instance chains would need to enable it explicitly. + At the same time, this paves the way for implementing in the future a `chainSpec_v1_getSpec` + method that can extract the chainSpec of any chain (including parachains) for the use with lightclients. + For more info about the `chainSpec`, please see the specification: https://github.com/paritytech/json-rpc-interface-spec/blob/main/src/api/chainSpec.md. + +crates: + - name: sc-service + bump: patch + - name: polkadot-rpc + bump: patch diff --git a/substrate/bin/node/rpc/Cargo.toml b/substrate/bin/node/rpc/Cargo.toml index 2417496cde47..d85998e3c87b 100644 --- a/substrate/bin/node/rpc/Cargo.toml +++ b/substrate/bin/node/rpc/Cargo.toml @@ -32,7 +32,6 @@ sc-consensus-grandpa-rpc = { workspace = true, default-features = true } sc-mixnet = { workspace = true, default-features = true } sc-rpc = { workspace = true, default-features = true } sc-rpc-api = { workspace = true, default-features = true } -sc-rpc-spec-v2 = { workspace = true, default-features = true } sc-sync-state-rpc = { workspace = true, default-features = true } sc-transaction-pool-api = { workspace = true, default-features = true } sp-api = { workspace = true, default-features = true } diff --git a/substrate/bin/node/rpc/src/lib.rs b/substrate/bin/node/rpc/src/lib.rs index 52cd7f9561d2..c55e03ee9d6f 100644 --- a/substrate/bin/node/rpc/src/lib.rs +++ b/substrate/bin/node/rpc/src/lib.rs @@ -160,7 +160,6 @@ where mixnet::MixnetApiServer, statement::StatementApiServer, }; - use sc_rpc_spec_v2::chain_spec::{ChainSpec, ChainSpecApiServer}; use sc_sync_state_rpc::{SyncState, SyncStateApiServer}; use substrate_frame_rpc_system::{System, SystemApiServer}; use substrate_state_trie_migration_rpc::{StateMigration, StateMigrationApiServer}; @@ -176,11 +175,6 @@ where finality_provider, } = grandpa; - let chain_name = chain_spec.name().to_string(); - let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"); - let properties = chain_spec.properties(); - io.merge(ChainSpec::new(chain_name, genesis_hash, properties).into_rpc())?; - io.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; // Making synchronous calls in light client freezes the browser currently, // more context: https://github.com/paritytech/substrate/pull/3480 diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index 638f30fad10e..ebe3f553f7c2 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -68,6 +68,7 @@ use sc_rpc::{ use sc_rpc_spec_v2::{ archive::ArchiveApiServer, chain_head::ChainHeadApiServer, + chain_spec::ChainSpecApiServer, transaction::{TransactionApiServer, TransactionBroadcastApiServer}, }; use sc_telemetry::{telemetry, ConnectionMessage, Telemetry, TelemetryHandle, SUBSTRATE_INFO}; @@ -675,9 +676,8 @@ where // - block pruning in archive mode: The block's body is kept around let is_archive_node = config.state_pruning.as_ref().map(|sp| sp.is_archive()).unwrap_or(false) && config.blocks_pruning.is_archive(); + let genesis_hash = client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); if is_archive_node { - let genesis_hash = - client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); let archive_v2 = sc_rpc_spec_v2::archive::Archive::new( client.clone(), backend.clone(), @@ -689,6 +689,14 @@ where rpc_api.merge(archive_v2).map_err(|e| Error::Application(e.into()))?; } + // ChainSpec RPC-v2. + let chain_spec_v2 = sc_rpc_spec_v2::chain_spec::ChainSpec::new( + config.chain_spec.name().into(), + genesis_hash, + config.chain_spec.properties(), + ) + .into_rpc(); + let author = sc_rpc::author::Author::new( client.clone(), transaction_pool, @@ -712,6 +720,7 @@ where .merge(transaction_broadcast_rpc_v2) .map_err(|e| Error::Application(e.into()))?; rpc_api.merge(chain_head_v2).map_err(|e| Error::Application(e.into()))?; + rpc_api.merge(chain_spec_v2).map_err(|e| Error::Application(e.into()))?; // Part of the old RPC spec. rpc_api.merge(chain).map_err(|e| Error::Application(e.into()))?; From 2abd03ef330c8b55e73755a7ef4b43baf1451657 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Mon, 5 Aug 2024 09:40:43 +0200 Subject: [PATCH 057/382] beefy: Tolerate pruned state on runtime API call (#5197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While working on #5129 I noticed that after warp sync, nodes would print: ``` 2024-07-29 17:59:23.898 ERROR ⋮beefy: 🥩 Error: ConsensusReset. Restarting voter. ``` After some debugging I found that we enter the following loop: 1. Wait for beefy pallet to be available: Pallet is detected available directly after warp sync since we are at the tip. 2. Wait for headers from tip to beefy genesis to be available: During this time we don't process finality notifications, since we later want to inspect all the headers for authority set changes. 3. Gap sync finishes, route to beefy genesis is available. 4. The worker starts acting, tries to fetch beefy genesis block. It fails, since we are acting on old finality notifications where the state is already pruned. 5. Whole beefy subsystem is being restarted, loading the state from db again and iterating a lot of headers. This already happened before #5129. --- prdoc/pr_5197.prdoc | 16 ++++++++++++++++ substrate/client/consensus/beefy/src/lib.rs | 5 +++-- .../client/consensus/beefy/src/worker.rs | 19 ++++++++++++------- 3 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 prdoc/pr_5197.prdoc diff --git a/prdoc/pr_5197.prdoc b/prdoc/pr_5197.prdoc new file mode 100644 index 000000000000..40e25cf70dd1 --- /dev/null +++ b/prdoc/pr_5197.prdoc @@ -0,0 +1,16 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Prevent `ConsensusReset` by tolerating runtime API errors in BEEFY + +doc: + - audience: Node Operator + description: | + After warp sync, the BEEFY worker was trying to execute runtime calls on + blocks which had their state already pruned. This led to an error and restarting + of the beefy subsystem in a loop. After this PR, the worker tolerates call errors and therefore prevents this + worker restart loop. + +crates: + - name: sc-consensus-beefy + bump: minor diff --git a/substrate/client/consensus/beefy/src/lib.rs b/substrate/client/consensus/beefy/src/lib.rs index f7c999aad591..30cdd494905f 100644 --- a/substrate/client/consensus/beefy/src/lib.rs +++ b/substrate/client/consensus/beefy/src/lib.rs @@ -32,7 +32,7 @@ use crate::{ metrics::register_metrics, }; use futures::{stream::Fuse, FutureExt, StreamExt}; -use log::{debug, error, info, warn}; +use log::{debug, error, info, trace, warn}; use parking_lot::Mutex; use prometheus_endpoint::Registry; use sc_client_api::{Backend, BlockBackend, BlockchainEvents, FinalityNotification, Finalizer}; @@ -451,7 +451,8 @@ where state.set_best_grandpa(best_grandpa.clone()); // Overwrite persisted data with newly provided `min_block_delta`. state.set_min_block_delta(min_block_delta); - debug!(target: LOG_TARGET, "🥩 Loading BEEFY voter state from db: {:?}.", state); + debug!(target: LOG_TARGET, "🥩 Loading BEEFY voter state from db."); + trace!(target: LOG_TARGET, "🥩 Loaded state: {:?}.", state); // Make sure that all the headers that we need have been synced. let mut new_sessions = vec![]; diff --git a/substrate/client/consensus/beefy/src/worker.rs b/substrate/client/consensus/beefy/src/worker.rs index ca714ffadd77..3c7f3b1b6efa 100644 --- a/substrate/client/consensus/beefy/src/worker.rs +++ b/substrate/client/consensus/beefy/src/worker.rs @@ -458,13 +458,18 @@ where notification.tree_route, ); - self.runtime - .runtime_api() - .beefy_genesis(notification.hash) - .ok() - .flatten() - .filter(|genesis| *genesis == self.persisted_state.pallet_genesis) - .ok_or(Error::ConsensusReset)?; + match self.runtime.runtime_api().beefy_genesis(notification.hash) { + Ok(Some(genesis)) if genesis != self.persisted_state.pallet_genesis => { + debug!(target: LOG_TARGET, "🥩 ConsensusReset detected. Expected genesis: {}, found genesis: {}", self.persisted_state.pallet_genesis, genesis); + return Err(Error::ConsensusReset) + }, + Ok(_) => {}, + Err(api_error) => { + // This can happen in case the block was already pruned. + // Mostly after warp sync when finality notifications are piled up. + debug!(target: LOG_TARGET, "🥩 Unable to check beefy genesis: {}", api_error); + }, + } let mut new_session_added = false; if *header.number() > self.best_grandpa_block() { From ad1e556ebfc7c2e8026b1c49a1ea3b5716d3dd7f Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Mon, 5 Aug 2024 10:11:56 +0100 Subject: [PATCH 058/382] Fix frame crate usage doc (#5222) --- substrate/frame/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index 3836e71cb00f..235d75a4b752 100644 --- a/substrate/frame/src/lib.rs +++ b/substrate/frame/src/lib.rs @@ -30,7 +30,7 @@ //! > **F**ramework for **R**untime **A**ggregation of **M**odularized **E**ntities: Substrate's //! > State Transition Function (Runtime) Framework. //! -//! //! ## Usage +//! ## Usage //! //! The main intended use of this crate is for it to be imported with its preludes: //! From 6619277b74ed7227ed9015e4a72b8579a0786808 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Mon, 5 Aug 2024 12:48:57 +0300 Subject: [PATCH 059/382] network/strategy: Backoff and ban overloaded peers to avoid submitting the same request multiple times (#5029) This PR avoids submitting the same block or state request multiple times to the same slow peer. Previously, we submitted the same request to the same slow peer, which resulted in reputation bans on the slow peer side. Furthermore, the strategy selected the same slow peer multiple times to submit queries to, although a better candidate may exist. Instead, in this PR we: - introduce a `DisconnectedPeers` via LRU with 512 peer capacity to only track the state of disconnected peers with a request in flight - when the `DisconnectedPeers` detects a peer disconnected with a request in flight, the peer is backed off - on the first disconnection: 60 seconds - on second disconnection: 120 seconds - on the third disconnection the peer is banned, and the peer remains banned until the peerstore decays its reputation This PR lifts the pressure from overloaded nodes that cannot process requests in due time. And if a peer is detected to be slow after backoffs, the peer is banned. Theoretically, submitting the same request multiple times can still happen when: - (a) we backoff and ban the peer - (b) the network does not discover other peers -- this may also be a test net - (c) the peer gets reconnected after the reputation decay and is still slow to respond Aims to improve: - https://github.com/paritytech/polkadot-sdk/issues/4924 - https://github.com/paritytech/polkadot-sdk/issues/531 Next Steps: - Investigate the network after this is deployed, possibly bumping the keep-alive timeout or seeing if there's something else misbehaving This PR builds on top of: - https://github.com/paritytech/polkadot-sdk/pull/4987 ### Testing Done - Added a couple of unit tests where test harness were set in place - Local testnet ```bash 13:13:25.102 DEBUG tokio-runtime-worker sync::persistent_peer_state: Added first time peer 12D3KooWHdiAxVd8uMQR1hGWXccidmfCwLqcMpGwR6QcTP6QRMuD 13:14:39.102 DEBUG tokio-runtime-worker sync::persistent_peer_state: Remove known peer 12D3KooWHdiAxVd8uMQR1hGWXccidmfCwLqcMpGwR6QcTP6QRMuD state: DisconnectedPeerState { num_disconnects: 2, last_disconnect: Instant { tv_sec: 93355, tv_nsec: 942016062 } }, should ban: false 13:16:49.107 DEBUG tokio-runtime-worker sync::persistent_peer_state: Remove known peer 12D3KooWHdiAxVd8uMQR1hGWXccidmfCwLqcMpGwR6QcTP6QRMuD state: DisconnectedPeerState { num_disconnects: 3, last_disconnect: Instant { tv_sec: 93485, tv_nsec: 947551051 } }, should ban: true 13:16:49.108 WARN tokio-runtime-worker peerset: Report 12D3KooWHdiAxVd8uMQR1hGWXccidmfCwLqcMpGwR6QcTP6QRMuD: -2147483648 to -2147483648. Reason: Slow peer after backoffs. Banned, disconnecting. ``` cc @paritytech/networking --------- Signed-off-by: Alexandru Vasile --- prdoc/pr_5029.prdoc | 16 ++ substrate/client/network/sync/src/engine.rs | 4 + substrate/client/network/sync/src/strategy.rs | 1 + .../network/sync/src/strategy/chain_sync.rs | 26 ++- .../sync/src/strategy/disconnected_peers.rs | 196 ++++++++++++++++++ .../client/network/sync/src/strategy/state.rs | 78 ++++++- .../client/network/sync/src/strategy/warp.rs | 65 +++++- 7 files changed, 377 insertions(+), 9 deletions(-) create mode 100644 prdoc/pr_5029.prdoc create mode 100644 substrate/client/network/sync/src/strategy/disconnected_peers.rs diff --git a/prdoc/pr_5029.prdoc b/prdoc/pr_5029.prdoc new file mode 100644 index 000000000000..d446ddf274b8 --- /dev/null +++ b/prdoc/pr_5029.prdoc @@ -0,0 +1,16 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Backoff slow peers to avoid duplicate requests + +doc: + - audience: Node Dev + description: | + This PR introduces a backoff strategy mechanism. Whenever a peer disconnects with an inflight + block (or state) request, the peer is backed off for a period of time before receiving requests. + After several attempts, the peer is disconnected and banned. The strategy aims to offload + the pressure from peers that are slow to respond or overloaded. + +crates: +- name: sc-network-sync + bump: minor diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index bb6e7a98a810..ee7576c22f16 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -536,6 +536,10 @@ where }, BlockAnnounceValidationResult::Failure { peer_id, disconnect } => { if disconnect { + log::debug!( + target: LOG_TARGET, + "Disconnecting peer {peer_id} due to block announce validation failure", + ); self.network_service .disconnect_peer(peer_id, self.block_announce_protocol_name.clone()); } diff --git a/substrate/client/network/sync/src/strategy.rs b/substrate/client/network/sync/src/strategy.rs index b7afcbdb3a78..72f84d1c286e 100644 --- a/substrate/client/network/sync/src/strategy.rs +++ b/substrate/client/network/sync/src/strategy.rs @@ -20,6 +20,7 @@ //! and specific syncing algorithms. pub mod chain_sync; +mod disconnected_peers; mod state; pub mod state_sync; pub mod warp; diff --git a/substrate/client/network/sync/src/strategy/chain_sync.rs b/substrate/client/network/sync/src/strategy/chain_sync.rs index fcda25907927..52870d5ba151 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync.rs @@ -33,6 +33,7 @@ use crate::{ justification_requests::ExtraRequests, schema::v1::StateResponse, strategy::{ + disconnected_peers::DisconnectedPeers, state_sync::{ImportResult, StateSync, StateSyncProvider}, warp::{WarpSyncPhase, WarpSyncProgress}, }, @@ -250,6 +251,7 @@ pub struct ChainSync { client: Arc, /// The active peers that we are using to sync and their PeerSync status peers: HashMap>, + disconnected_peers: DisconnectedPeers, /// A `BlockCollection` of blocks that are being downloaded from peers blocks: BlockCollection, /// The best block number in our queue of blocks to import @@ -378,6 +380,7 @@ where let mut sync = Self { client, peers: HashMap::new(), + disconnected_peers: DisconnectedPeers::new(), blocks: BlockCollection::new(), best_queued_hash: Default::default(), best_queued_number: Zero::zero(), @@ -1141,7 +1144,17 @@ where if let Some(gap_sync) = &mut self.gap_sync { gap_sync.blocks.clear_peer_download(peer_id) } - self.peers.remove(peer_id); + + if let Some(state) = self.peers.remove(peer_id) { + if !state.state.is_available() { + if let Some(bad_peer) = + self.disconnected_peers.on_disconnect_during_request(*peer_id) + { + self.actions.push(ChainSyncAction::DropPeer(bad_peer)); + } + } + } + self.extra_justifications.peer_disconnected(peer_id); self.allowed_requests.set_all(); self.fork_targets.retain(|_, target| { @@ -1541,10 +1554,14 @@ where let max_parallel = if is_major_syncing { 1 } else { self.max_parallel_downloads }; let max_blocks_per_request = self.max_blocks_per_request; let gap_sync = &mut self.gap_sync; + let disconnected_peers = &mut self.disconnected_peers; self.peers .iter_mut() .filter_map(move |(&id, peer)| { - if !peer.state.is_available() || !allowed_requests.contains(&id) { + if !peer.state.is_available() || + !allowed_requests.contains(&id) || + !disconnected_peers.is_peer_available(&id) + { return None } @@ -1656,7 +1673,10 @@ where } for (id, peer) in self.peers.iter_mut() { - if peer.state.is_available() && peer.common_number >= sync.target_number() { + if peer.state.is_available() && + peer.common_number >= sync.target_number() && + self.disconnected_peers.is_peer_available(&id) + { peer.state = PeerSyncState::DownloadingState; let request = sync.next_request(); trace!(target: LOG_TARGET, "New StateRequest for {}: {:?}", id, request); diff --git a/substrate/client/network/sync/src/strategy/disconnected_peers.rs b/substrate/client/network/sync/src/strategy/disconnected_peers.rs new file mode 100644 index 000000000000..ea94a5a1df54 --- /dev/null +++ b/substrate/client/network/sync/src/strategy/disconnected_peers.rs @@ -0,0 +1,196 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::types::BadPeer; +use sc_network::ReputationChange as Rep; +use sc_network_types::PeerId; +use schnellru::{ByLength, LruMap}; + +const LOG_TARGET: &str = "sync::disconnected_peers"; + +/// The maximum number of disconnected peers to keep track of. +/// +/// When a peer disconnects, we must keep track if it was in the middle of a request. +/// The peer may disconnect because it cannot keep up with the number of requests +/// (ie not having enough resources available to handle the requests); or because it is malicious. +const MAX_DISCONNECTED_PEERS_STATE: u32 = 512; + +/// The time we are going to backoff a peer that has disconnected with an inflight request. +/// +/// The backoff time is calculated as `num_disconnects * DISCONNECTED_PEER_BACKOFF_SECONDS`. +/// This is to prevent submitting a request to a peer that has disconnected because it could not +/// keep up with the number of requests. +/// +/// The peer may disconnect due to the keep-alive timeout, however disconnections without +/// an inflight request are not tracked. +const DISCONNECTED_PEER_BACKOFF_SECONDS: u64 = 60; + +/// Maximum number of disconnects with a request in flight before a peer is banned. +const MAX_NUM_DISCONNECTS: u64 = 3; + +/// Peer disconnected with a request in flight after backoffs. +/// +/// The peer may be slow to respond to the request after backoffs, or it refuses to respond. +/// Report the peer and let the reputation system handle disconnecting the peer. +pub const REPUTATION_REPORT: Rep = Rep::new_fatal("Peer disconnected with inflight after backoffs"); + +/// The state of a disconnected peer with a request in flight. +#[derive(Debug)] +struct DisconnectedState { + /// The total number of disconnects. + num_disconnects: u64, + /// The time at the last disconnect. + last_disconnect: std::time::Instant, +} + +impl DisconnectedState { + /// Create a new `DisconnectedState`. + pub fn new() -> Self { + Self { num_disconnects: 1, last_disconnect: std::time::Instant::now() } + } + + /// Increment the number of disconnects. + pub fn increment(&mut self) { + self.num_disconnects = self.num_disconnects.saturating_add(1); + self.last_disconnect = std::time::Instant::now(); + } + + /// Get the number of disconnects. + pub fn num_disconnects(&self) -> u64 { + self.num_disconnects + } + + /// Get the time of the last disconnect. + pub fn last_disconnect(&self) -> std::time::Instant { + self.last_disconnect + } +} + +/// Tracks the state of disconnected peers with a request in flight. +/// +/// This helps to prevent submitting requests to peers that have disconnected +/// before responding to the request to offload the peer. +pub struct DisconnectedPeers { + /// The state of disconnected peers. + disconnected_peers: LruMap, + /// Backoff duration in seconds. + backoff_seconds: u64, +} + +impl DisconnectedPeers { + /// Create a new `DisconnectedPeers`. + pub fn new() -> Self { + Self { + disconnected_peers: LruMap::new(ByLength::new(MAX_DISCONNECTED_PEERS_STATE)), + backoff_seconds: DISCONNECTED_PEER_BACKOFF_SECONDS, + } + } + + /// Insert a new peer to the persistent state if not seen before, or update the state if seen. + /// + /// Returns true if the peer should be disconnected. + pub fn on_disconnect_during_request(&mut self, peer: PeerId) -> Option { + if let Some(state) = self.disconnected_peers.get(&peer) { + state.increment(); + + let should_ban = state.num_disconnects() >= MAX_NUM_DISCONNECTS; + log::debug!( + target: LOG_TARGET, + "Disconnected known peer {peer} state: {state:?}, should ban: {should_ban}", + ); + + should_ban.then(|| { + // We can lose track of the peer state and let the banning mechanism handle + // the peer backoff. + // + // After the peer banning expires, if the peer continues to misbehave, it will be + // backed off again. + self.disconnected_peers.remove(&peer); + BadPeer(peer, REPUTATION_REPORT) + }) + } else { + log::debug!( + target: LOG_TARGET, + "Added peer {peer} for the first time" + ); + // First time we see this peer. + self.disconnected_peers.insert(peer, DisconnectedState::new()); + None + } + } + + /// Check if a peer is available for queries. + pub fn is_peer_available(&mut self, peer_id: &PeerId) -> bool { + let Some(state) = self.disconnected_peers.get(peer_id) else { + return true; + }; + + let elapsed = state.last_disconnect().elapsed(); + if elapsed.as_secs() >= self.backoff_seconds * state.num_disconnects { + log::debug!(target: LOG_TARGET, "Peer {peer_id} is available for queries"); + self.disconnected_peers.remove(peer_id); + true + } else { + log::debug!(target: LOG_TARGET,"Peer {peer_id} is backedoff"); + false + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::time::Duration; + + #[test] + fn test_disconnected_peer_state() { + let mut state = DisconnectedPeers::new(); + let peer = PeerId::random(); + + // Is not part of the disconnected peers yet. + assert_eq!(state.is_peer_available(&peer), true); + + for _ in 0..MAX_NUM_DISCONNECTS - 1 { + assert!(state.on_disconnect_during_request(peer).is_none()); + assert_eq!(state.is_peer_available(&peer), false); + } + + assert!(state.on_disconnect_during_request(peer).is_some()); + // Peer is supposed to get banned and disconnected. + // The state ownership moves to the PeerStore. + assert!(state.disconnected_peers.get(&peer).is_none()); + } + + #[test] + fn ensure_backoff_time() { + const TEST_BACKOFF_SECONDS: u64 = 2; + let mut state = DisconnectedPeers { + disconnected_peers: LruMap::new(ByLength::new(1)), + backoff_seconds: TEST_BACKOFF_SECONDS, + }; + let peer = PeerId::random(); + + assert!(state.on_disconnect_during_request(peer).is_none()); + assert_eq!(state.is_peer_available(&peer), false); + + // Wait until the backoff time has passed + std::thread::sleep(Duration::from_secs(TEST_BACKOFF_SECONDS + 1)); + + assert_eq!(state.is_peer_available(&peer), true); + } +} diff --git a/substrate/client/network/sync/src/strategy/state.rs b/substrate/client/network/sync/src/strategy/state.rs index c21cb22e40bb..ff229863a68b 100644 --- a/substrate/client/network/sync/src/strategy/state.rs +++ b/substrate/client/network/sync/src/strategy/state.rs @@ -20,7 +20,10 @@ use crate::{ schema::v1::StateResponse, - strategy::state_sync::{ImportResult, StateSync, StateSyncProvider}, + strategy::{ + disconnected_peers::DisconnectedPeers, + state_sync::{ImportResult, StateSync, StateSyncProvider}, + }, types::{BadPeer, OpaqueStateRequest, OpaqueStateResponse, SyncState, SyncStatus}, LOG_TARGET, }; @@ -78,6 +81,7 @@ struct Peer { pub struct StateStrategy { state_sync: Box>, peers: HashMap>, + disconnected_peers: DisconnectedPeers, actions: Vec>, succeeded: bool, } @@ -109,6 +113,7 @@ impl StateStrategy { skip_proof, )), peers, + disconnected_peers: DisconnectedPeers::new(), actions: Vec::new(), succeeded: false, } @@ -128,6 +133,7 @@ impl StateStrategy { (peer_id, Peer { best_number, state: PeerState::Available }) }) .collect(), + disconnected_peers: DisconnectedPeers::new(), actions: Vec::new(), succeeded: false, } @@ -140,7 +146,15 @@ impl StateStrategy { /// Notify that a peer has disconnected. pub fn remove_peer(&mut self, peer_id: &PeerId) { - self.peers.remove(peer_id); + if let Some(state) = self.peers.remove(peer_id) { + if !state.state.is_available() { + if let Some(bad_peer) = + self.disconnected_peers.on_disconnect_during_request(*peer_id) + { + self.actions.push(StateStrategyAction::DropPeer(bad_peer)); + } + } + } } /// Submit a validated block announcement. @@ -305,7 +319,10 @@ impl StateStrategy { // Find a random peer that is synced as much as peer majority and is above // `min_best_number`. for (peer_id, peer) in self.peers.iter_mut() { - if peer.state.is_available() && peer.best_number >= threshold { + if peer.state.is_available() && + peer.best_number >= threshold && + self.disconnected_peers.is_peer_available(peer_id) + { peer.state = new_state; return Some(*peer_id) } @@ -360,6 +377,7 @@ mod test { use sc_block_builder::BlockBuilderBuilder; use sc_client_api::KeyValueStates; use sc_consensus::{ImportedAux, ImportedState}; + use sp_core::H256; use sp_runtime::traits::Zero; use substrate_test_runtime_client::{ runtime::{Block, Hash}, @@ -465,6 +483,60 @@ mod test { } } + #[test] + fn backedoff_number_peer_is_not_scheduled() { + let client = Arc::new(TestClientBuilder::new().set_no_genesis().build()); + let target_block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; + + let peers = (1..=10) + .map(|best_number| (PeerId::random(), best_number)) + .collect::>(); + let ninth_peer = peers[8].0; + let tenth_peer = peers[9].0; + let initial_peers = peers.iter().map(|(p, n)| (*p, *n)); + + let mut state_strategy = StateStrategy::new( + client.clone(), + target_block.header().clone(), + None, + None, + false, + initial_peers, + ); + + // Disconnecting a peer without an inflight request has no effect on persistent states. + state_strategy.remove_peer(&tenth_peer); + assert!(state_strategy.disconnected_peers.is_peer_available(&tenth_peer)); + + // Disconnect the peer with an inflight request. + state_strategy.add_peer(tenth_peer, H256::random(), 10); + let peer_id: Option = + state_strategy.schedule_next_peer(PeerState::DownloadingState, 10); + assert_eq!(tenth_peer, peer_id.unwrap()); + state_strategy.remove_peer(&tenth_peer); + + // Peer is backed off. + assert!(!state_strategy.disconnected_peers.is_peer_available(&tenth_peer)); + + // No peer available for 10'th best block because of the backoff. + state_strategy.add_peer(tenth_peer, H256::random(), 10); + let peer_id: Option = + state_strategy.schedule_next_peer(PeerState::DownloadingState, 10); + assert!(peer_id.is_none()); + + // Other requests can still happen. + let peer_id: Option = + state_strategy.schedule_next_peer(PeerState::DownloadingState, 9); + assert_eq!(ninth_peer, peer_id.unwrap()); + } + #[test] fn state_request_contains_correct_hash() { let client = Arc::new(TestClientBuilder::new().set_no_genesis().build()); diff --git a/substrate/client/network/sync/src/strategy/warp.rs b/substrate/client/network/sync/src/strategy/warp.rs index 754f1f52bfd2..00855578695d 100644 --- a/substrate/client/network/sync/src/strategy/warp.rs +++ b/substrate/client/network/sync/src/strategy/warp.rs @@ -21,7 +21,7 @@ pub use sp_consensus_grandpa::{AuthorityList, SetId}; use crate::{ - strategy::chain_sync::validate_blocks, + strategy::{chain_sync::validate_blocks, disconnected_peers::DisconnectedPeers}, types::{BadPeer, SyncState, SyncStatus}, LOG_TARGET, }; @@ -240,6 +240,7 @@ pub struct WarpSync { total_proof_bytes: u64, total_state_bytes: u64, peers: HashMap>, + disconnected_peers: DisconnectedPeers, actions: Vec>, result: Option>, } @@ -264,6 +265,7 @@ where total_proof_bytes: 0, total_state_bytes: 0, peers: HashMap::new(), + disconnected_peers: DisconnectedPeers::new(), actions: vec![WarpSyncAction::Finished], result: None, } @@ -281,6 +283,7 @@ where total_proof_bytes: 0, total_state_bytes: 0, peers: HashMap::new(), + disconnected_peers: DisconnectedPeers::new(), actions: Vec::new(), result: None, } @@ -309,7 +312,15 @@ where /// Notify that a peer has disconnected. pub fn remove_peer(&mut self, peer_id: &PeerId) { - self.peers.remove(peer_id); + if let Some(state) = self.peers.remove(peer_id) { + if !state.state.is_available() { + if let Some(bad_peer) = + self.disconnected_peers.on_disconnect_during_request(*peer_id) + { + self.actions.push(WarpSyncAction::DropPeer(bad_peer)); + } + } + } } /// Submit a validated block announcement. @@ -490,7 +501,10 @@ where // Find a random peer that is synced as much as peer majority and is above // `min_best_number`. for (peer_id, peer) in self.peers.iter_mut() { - if peer.state.is_available() && peer.best_number >= threshold { + if peer.state.is_available() && + peer.best_number >= threshold && + self.disconnected_peers.is_peer_available(peer_id) + { peer.state = new_state; return Some(*peer_id) } @@ -650,6 +664,7 @@ mod test { use sc_block_builder::BlockBuilderBuilder; use sp_blockchain::{BlockStatus, Error as BlockchainError, HeaderBackend, Info}; use sp_consensus_grandpa::{AuthorityList, SetId}; + use sp_core::H256; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; use std::{io::ErrorKind, sync::Arc}; use substrate_test_runtime_client::{ @@ -860,6 +875,50 @@ mod test { } } + #[test] + fn backedoff_number_peer_is_not_scheduled() { + let client = mock_client_without_state(); + let mut provider = MockWarpSyncProvider::::new(); + provider + .expect_current_authorities() + .once() + .return_const(AuthorityList::default()); + let config = WarpSyncConfig::WithProvider(Arc::new(provider)); + let mut warp_sync = WarpSync::new(Arc::new(client), config); + + for best_number in 1..11 { + warp_sync.add_peer(PeerId::random(), Hash::random(), best_number); + } + + let ninth_peer = + *warp_sync.peers.iter().find(|(_, state)| state.best_number == 9).unwrap().0; + let tenth_peer = + *warp_sync.peers.iter().find(|(_, state)| state.best_number == 10).unwrap().0; + + // Disconnecting a peer without an inflight request has no effect on persistent states. + warp_sync.remove_peer(&tenth_peer); + assert!(warp_sync.disconnected_peers.is_peer_available(&tenth_peer)); + + warp_sync.add_peer(tenth_peer, H256::random(), 10); + let peer_id = warp_sync.schedule_next_peer(PeerState::DownloadingProofs, Some(10)); + assert_eq!(tenth_peer, peer_id.unwrap()); + warp_sync.remove_peer(&tenth_peer); + + // Peer is backed off. + assert!(!warp_sync.disconnected_peers.is_peer_available(&tenth_peer)); + + // No peer available for 10'th best block because of the backoff. + warp_sync.add_peer(tenth_peer, H256::random(), 10); + let peer_id: Option = + warp_sync.schedule_next_peer(PeerState::DownloadingProofs, Some(10)); + assert!(peer_id.is_none()); + + // Other requests can still happen. + let peer_id: Option = + warp_sync.schedule_next_peer(PeerState::DownloadingProofs, Some(9)); + assert_eq!(ninth_peer, peer_id.unwrap()); + } + #[test] fn no_warp_proof_request_in_another_phase() { let client = mock_client_without_state(); From f170af615c0dc413482100892758b236d1fda93b Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Mon, 5 Aug 2024 12:53:25 +0200 Subject: [PATCH 060/382] Coretime auto-renew (#4424) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds functionality that allows tasks to enable auto-renewal. Each task eligible for renewal can enable auto-renewal. A new storage value is added to track all the cores with auto-renewal enabled and the associated task running on the core. The `BoundedVec` is sorted by `CoreIndex` to make disabling auto-renewal more efficient. Cores are renewed at the start of a new bulk sale. If auto-renewal fails(e.g. due to the sovereign account of the task not holding sufficient balance), an event will be emitted, and the renewal will continue for the other cores. The two added extrinsics are: - `enable_auto_renew`: Extrinsic for enabling auto renewal. - `disable_auto_renew`: Extrinsic for disabling auto renewal. TODOs: - [x] Write benchmarks for the newly added extrinsics. Closes: #4351 --------- Co-authored-by: Dónal Murray --- .../coretime/coretime-rococo/src/coretime.rs | 21 +- .../src/weights/pallet_broker.rs | 38 ++ .../coretime/coretime-westend/src/coretime.rs | 19 +- .../src/weights/pallet_broker.rs | 38 ++ prdoc/pr_4424.prdoc | 20 + substrate/bin/node/runtime/src/lib.rs | 16 +- substrate/frame/broker/src/benchmarking.rs | 151 +++++- .../frame/broker/src/dispatchable_impls.rs | 69 ++- substrate/frame/broker/src/lib.rs | 109 ++++- substrate/frame/broker/src/mock.rs | 18 +- substrate/frame/broker/src/tests.rs | 288 ++++++++++++ substrate/frame/broker/src/tick_impls.rs | 51 ++- substrate/frame/broker/src/types.rs | 13 + substrate/frame/broker/src/utility_impls.rs | 2 +- substrate/frame/broker/src/weights.rs | 432 ++++++++++-------- 15 files changed, 1078 insertions(+), 207 deletions(-) create mode 100644 prdoc/pr_4424.prdoc diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs index fa0c2644421e..76ee06a87e8d 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::*; +use crate::{xcm_config::LocationToAccountId, *}; use codec::{Decode, Encode}; use cumulus_pallet_parachain_system::RelaychainDataProvider; use cumulus_primitives_core::relay_chain; @@ -27,12 +27,14 @@ use frame_support::{ }, }; use frame_system::Pallet as System; -use pallet_broker::{CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600, RCBlockNumberOf}; +use pallet_broker::{ + CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600, RCBlockNumberOf, TaskId, +}; use parachains_common::{AccountId, Balance}; use rococo_runtime_constants::system_parachain::coretime; -use sp_runtime::traits::AccountIdConversion; +use sp_runtime::traits::{AccountIdConversion, MaybeConvert}; use xcm::latest::prelude::*; -use xcm_executor::traits::TransactAsset; +use xcm_executor::traits::{ConvertLocation, TransactAsset}; pub struct BurnCoretimeRevenue; impl OnUnbalanced> for BurnCoretimeRevenue { @@ -263,6 +265,15 @@ impl CoretimeInterface for CoretimeAllocator { } } +pub struct SovereignAccountOf; +impl MaybeConvert for SovereignAccountOf { + fn maybe_convert(id: TaskId) -> Option { + // Currently all tasks are parachains. + let location = Location::new(1, [Parachain(id)]); + LocationToAccountId::convert_location(&location) + } +} + impl pallet_broker::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; @@ -275,5 +286,7 @@ impl pallet_broker::Config for Runtime { type WeightInfo = weights::pallet_broker::WeightInfo; type PalletId = BrokerPalletId; type AdminOrigin = EnsureRoot; + type SovereignAccountOf = SovereignAccountOf; + type MaxAutoRenewals = ConstU32<100>; type PriceAdapter = pallet_broker::CenterTargetPrice; } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs index 83e80e2e91e7..35708f22de20 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs @@ -549,6 +549,44 @@ impl pallet_broker::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Broker::SaleInfo` (r:1 w:1) + /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) + /// Storage: `Broker::PotentialRenewals` (r:1 w:2) + /// Proof: `Broker::PotentialRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `Broker::Configuration` (r:1 w:0) + /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + /// Storage: `Broker::Status` (r:1 w:0) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Broker::AutoRenewals` (r:1 w:1) + /// Proof: `Broker::AutoRenewals` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + /// Storage: `Broker::Workplan` (r:0 w:1) + /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) + fn enable_auto_renew() -> Weight { + // Proof Size summary in bytes: + // Measured: `914` + // Estimated: `4698` + // Minimum execution time: 51_938_000 picoseconds. + Weight::from_parts(55_025_000, 4698) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `Broker::AutoRenewals` (r:1 w:1) + /// Proof: `Broker::AutoRenewals` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + fn disable_auto_renew() -> Weight { + // Proof Size summary in bytes: + // Measured: `480` + // Estimated: `1516` + // Minimum execution time: 9_628_000 picoseconds. + Weight::from_parts(10_400_000, 1516) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs index 4f06e3e3669c..865ff68d4c65 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::*; +use crate::{xcm_config::LocationToAccountId, *}; use codec::{Decode, Encode}; use cumulus_pallet_parachain_system::RelaychainDataProvider; use cumulus_primitives_core::relay_chain; @@ -28,13 +28,13 @@ use frame_support::{ }; use frame_system::Pallet as System; use pallet_broker::{ - CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600, RCBlockNumberOf, Timeslice, + CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600, RCBlockNumberOf, TaskId, Timeslice, }; use parachains_common::{AccountId, Balance}; -use sp_runtime::traits::AccountIdConversion; +use sp_runtime::traits::{AccountIdConversion, MaybeConvert}; use westend_runtime_constants::system_parachain::coretime; use xcm::latest::prelude::*; -use xcm_executor::traits::TransactAsset; +use xcm_executor::traits::{ConvertLocation, TransactAsset}; pub struct BurnCoretimeRevenue; impl OnUnbalanced> for BurnCoretimeRevenue { @@ -277,6 +277,15 @@ impl CoretimeInterface for CoretimeAllocator { } } +pub struct SovereignAccountOf; +impl MaybeConvert for SovereignAccountOf { + fn maybe_convert(id: TaskId) -> Option { + // Currently all tasks are parachains. + let location = Location::new(1, [Parachain(id)]); + LocationToAccountId::convert_location(&location) + } +} + impl pallet_broker::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; @@ -290,5 +299,7 @@ impl pallet_broker::Config for Runtime { type WeightInfo = weights::pallet_broker::WeightInfo; type PalletId = BrokerPalletId; type AdminOrigin = EnsureRoot; + type SovereignAccountOf = SovereignAccountOf; + type MaxAutoRenewals = ConstU32<20>; type PriceAdapter = pallet_broker::CenterTargetPrice; } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs index d130b306f7a5..74b1c4e47029 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs @@ -549,6 +549,44 @@ impl pallet_broker::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Broker::SaleInfo` (r:1 w:1) + /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) + /// Storage: `Broker::PotentialRenewals` (r:1 w:2) + /// Proof: `Broker::PotentialRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `Broker::Configuration` (r:1 w:0) + /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + /// Storage: `Broker::Status` (r:1 w:0) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Broker::AutoRenewals` (r:1 w:1) + /// Proof: `Broker::AutoRenewals` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + /// Storage: `Broker::Workplan` (r:0 w:1) + /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) + fn enable_auto_renew() -> Weight { + // Proof Size summary in bytes: + // Measured: `914` + // Estimated: `4698` + // Minimum execution time: 51_938_000 picoseconds. + Weight::from_parts(55_025_000, 4698) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `Broker::AutoRenewals` (r:1 w:1) + /// Proof: `Broker::AutoRenewals` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + fn disable_auto_renew() -> Weight { + // Proof Size summary in bytes: + // Measured: `480` + // Estimated: `1516` + // Minimum execution time: 9_628_000 picoseconds. + Weight::from_parts(10_400_000, 1516) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn on_new_timeslice() -> Weight { diff --git a/prdoc/pr_4424.prdoc b/prdoc/pr_4424.prdoc new file mode 100644 index 000000000000..7131ebfca274 --- /dev/null +++ b/prdoc/pr_4424.prdoc @@ -0,0 +1,20 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Coretime auto renewal + +doc: + - audience: Runtime User + description: | + With the additions in this PR, any task that utilizes a core that can be auto-renewed + can enable auto-renewal. The renewal is paid from the task's sovereign account. + The two new extrinsics for controlling auto-renewal are `enable_auto_renew` and + `disable_auto_renew`. + +crates: + - name: pallet-broker + bump: major + - name: coretime-rococo-runtime + bump: minor + - name: coretime-westend-runtime + bump: minor diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index fd8597563a02..a94838cf20c0 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -79,6 +79,7 @@ use pallet_nis::WithMaximumOf; use pallet_session::historical as pallet_session_historical; // Can't use `FungibleAdapter` here until Treasury pallet migrates to fungibles // +use pallet_broker::TaskId; #[allow(deprecated)] pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; @@ -97,8 +98,8 @@ use sp_runtime::{ curve::PiecewiseLinear, generic, impl_opaque_keys, traits::{ - self, AccountIdConversion, BlakeTwo256, Block as BlockT, Bounded, ConvertInto, NumberFor, - OpaqueKeys, SaturatedConversion, StaticLookup, + self, AccountIdConversion, BlakeTwo256, Block as BlockT, Bounded, ConvertInto, + MaybeConvert, NumberFor, OpaqueKeys, SaturatedConversion, StaticLookup, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedPointNumber, FixedU128, Perbill, Percent, Permill, Perquintill, @@ -2115,6 +2116,15 @@ impl CoretimeInterface for CoretimeProvider { } } +pub struct SovereignAccountOf; +// Dummy implementation which converts `TaskId` to `AccountId`. +impl MaybeConvert for SovereignAccountOf { + fn maybe_convert(task: TaskId) -> Option { + let mut account: [u8; 32] = [0; 32]; + account[..4].copy_from_slice(&task.to_le_bytes()); + Some(account.into()) + } +} impl pallet_broker::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; @@ -2127,6 +2137,8 @@ impl pallet_broker::Config for Runtime { type WeightInfo = (); type PalletId = BrokerPalletId; type AdminOrigin = EnsureRoot; + type SovereignAccountOf = SovereignAccountOf; + type MaxAutoRenewals = ConstU32<10>; type PriceAdapter = pallet_broker::CenterTargetPrice; } diff --git a/substrate/frame/broker/src/benchmarking.rs b/substrate/frame/broker/src/benchmarking.rs index 33df56c95f65..595bf564f7e1 100644 --- a/substrate/frame/broker/src/benchmarking.rs +++ b/substrate/frame/broker/src/benchmarking.rs @@ -32,7 +32,10 @@ use frame_support::{ use frame_system::{Pallet as System, RawOrigin}; use sp_arithmetic::{traits::Zero, Perbill}; use sp_core::Get; -use sp_runtime::{traits::BlockNumberProvider, Saturating}; +use sp_runtime::{ + traits::{BlockNumberProvider, MaybeConvert}, + SaturatedConversion, Saturating, +}; const SEED: u32 = 0; const MAX_CORE_COUNT: u16 = 1_000; @@ -41,6 +44,10 @@ fn assert_last_event(generic_event: ::RuntimeEvent) { frame_system::Pallet::::assert_last_event(generic_event.into()); } +fn assert_has_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_has_event(generic_event.into()); +} + fn new_config_record() -> ConfigRecordOf { ConfigRecord { advance_notice: 2u32.into(), @@ -776,7 +783,7 @@ mod benches { } #[benchmark] - fn rotate_sale(n: Linear<0, { MAX_CORE_COUNT.into() }>) { + fn rotate_sale(n: Linear<0, { MAX_CORE_COUNT.into() }>) -> Result<(), BenchmarkError> { let core_count = n.try_into().unwrap(); let config = new_config_record::(); @@ -810,6 +817,27 @@ mod benches { // Assume Leases to be filled for worst case setup_leases::(T::MaxLeasedCores::get(), 1, 10); + // Assume max auto renewals for worst case. + (0..T::MaxAutoRenewals::get()).try_for_each(|indx| -> Result<(), BenchmarkError> { + let task = 1000 + indx; + let caller: T::AccountId = T::SovereignAccountOf::maybe_convert(task) + .expect("Failed to get sovereign account"); + T::Currency::set_balance( + &caller.clone(), + T::Currency::minimum_balance().saturating_add(100u32.into()), + ); + + let region = Broker::::do_purchase(caller.clone(), 10u32.into()) + .map_err(|_| BenchmarkError::Weightless)?; + + Broker::::do_assign(region, None, task, Final) + .map_err(|_| BenchmarkError::Weightless)?; + + Broker::::do_enable_auto_renew(caller, region.core, task, None)?; + + Ok(()) + })?; + #[block] { Broker::::rotate_sale(sale.clone(), &config, &status); @@ -833,6 +861,30 @@ mod benches { } .into(), ); + + // Make sure all cores got renewed: + (0..T::MaxAutoRenewals::get()).for_each(|indx| { + let task = 1000 + indx; + let who = T::SovereignAccountOf::maybe_convert(task) + .expect("Failed to get sovereign account"); + assert_has_event::( + Event::Renewed { + who, + old_core: 10 + indx as u16, // first ten cores are allocated to leases. + core: 10 + indx as u16, + price: 10u32.saturated_into(), + begin: 7, + duration: 3, + workload: Schedule::truncate_from(vec![ScheduleItem { + assignment: Task(task), + mask: CoreMask::complete(), + }]), + } + .into(), + ); + }); + + Ok(()) } #[benchmark] @@ -960,6 +1012,101 @@ mod benches { Ok(()) } + #[benchmark] + fn enable_auto_renew() -> Result<(), BenchmarkError> { + let _core = setup_and_start_sale::()?; + + advance_to::(2); + + // We assume max auto renewals for worst case. + (0..T::MaxAutoRenewals::get() - 1).try_for_each(|indx| -> Result<(), BenchmarkError> { + let task = 1000 + indx; + let caller: T::AccountId = T::SovereignAccountOf::maybe_convert(task) + .expect("Failed to get sovereign account"); + T::Currency::set_balance( + &caller.clone(), + T::Currency::minimum_balance().saturating_add(100u32.into()), + ); + + let region = Broker::::do_purchase(caller.clone(), 10u32.into()) + .map_err(|_| BenchmarkError::Weightless)?; + + Broker::::do_assign(region, None, task, Final) + .map_err(|_| BenchmarkError::Weightless)?; + + Broker::::do_enable_auto_renew(caller, region.core, task, Some(7))?; + + Ok(()) + })?; + + let caller: T::AccountId = + T::SovereignAccountOf::maybe_convert(2001).expect("Failed to get sovereign account"); + T::Currency::set_balance( + &caller.clone(), + T::Currency::minimum_balance().saturating_add(100u32.into()), + ); + + // The region for which we benchmark enable auto renew. + let region = Broker::::do_purchase(caller.clone(), 10u32.into()) + .map_err(|_| BenchmarkError::Weightless)?; + Broker::::do_assign(region, None, 2001, Final) + .map_err(|_| BenchmarkError::Weightless)?; + + // The most 'intensive' path is when we renew the core upon enabling auto-renewal. + // Therefore, we advance to next bulk sale: + advance_to::(6); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), region.core, 2001, None); + + assert_last_event::(Event::AutoRenewalEnabled { core: region.core, task: 2001 }.into()); + // Make sure we indeed renewed: + assert!(PotentialRenewals::::get(PotentialRenewalId { + core: region.core, + when: 10 // region end after renewal + }) + .is_some()); + + Ok(()) + } + + #[benchmark] + fn disable_auto_renew() -> Result<(), BenchmarkError> { + let _core = setup_and_start_sale::()?; + + advance_to::(2); + + // We assume max auto renewals for worst case. + (0..T::MaxAutoRenewals::get() - 1).try_for_each(|indx| -> Result<(), BenchmarkError> { + let task = 1000 + indx; + let caller: T::AccountId = T::SovereignAccountOf::maybe_convert(task) + .expect("Failed to get sovereign account"); + T::Currency::set_balance( + &caller.clone(), + T::Currency::minimum_balance().saturating_add(100u32.into()), + ); + + let region = Broker::::do_purchase(caller.clone(), 10u32.into()) + .map_err(|_| BenchmarkError::Weightless)?; + + Broker::::do_assign(region, None, task, Final) + .map_err(|_| BenchmarkError::Weightless)?; + + Broker::::do_enable_auto_renew(caller, region.core, task, Some(7))?; + + Ok(()) + })?; + + let caller: T::AccountId = + T::SovereignAccountOf::maybe_convert(1000).expect("Failed to get sovereign account"); + #[extrinsic_call] + _(RawOrigin::Signed(caller), _core, 1000); + + assert_last_event::(Event::AutoRenewalDisabled { core: _core, task: 1000 }.into()); + + Ok(()) + } + #[benchmark] fn on_new_timeslice() -> Result<(), BenchmarkError> { setup_and_start_sale::()?; diff --git a/substrate/frame/broker/src/dispatchable_impls.rs b/substrate/frame/broker/src/dispatchable_impls.rs index a8ded084a801..5fbd957d7908 100644 --- a/substrate/frame/broker/src/dispatchable_impls.rs +++ b/substrate/frame/broker/src/dispatchable_impls.rs @@ -16,9 +16,8 @@ // limitations under the License. use super::*; -use coretime_interface::CoretimeInterface; use frame_support::{ - pallet_prelude::{DispatchResult, *}, + pallet_prelude::*, traits::{fungible::Mutate, tokens::Preservation::Expendable, DefensiveResult}, }; use sp_arithmetic::traits::{CheckedDiv, Saturating, Zero}; @@ -487,6 +486,72 @@ impl Pallet { Ok(()) } + pub(crate) fn do_enable_auto_renew( + sovereign_account: T::AccountId, + core: CoreIndex, + task: TaskId, + workload_end_hint: Option, + ) -> DispatchResult { + let sale = SaleInfo::::get().ok_or(Error::::NoSales)?; + + // Check if the core is expiring in the next bulk period; if so, we will renew it now. + // + // In case we renew it now, we don't need to check the workload end since we know it is + // eligible for renewal. + if PotentialRenewals::::get(PotentialRenewalId { core, when: sale.region_begin }) + .is_some() + { + Self::do_renew(sovereign_account.clone(), core)?; + } else if let Some(workload_end) = workload_end_hint { + ensure!( + PotentialRenewals::::get(PotentialRenewalId { core, when: workload_end }) + .is_some(), + Error::::NotAllowed + ); + } else { + return Err(Error::::NotAllowed.into()) + } + + // We are sorting auto renewals by `CoreIndex`. + AutoRenewals::::try_mutate(|renewals| { + let pos = renewals + .binary_search_by(|r: &AutoRenewalRecord| r.core.cmp(&core)) + .unwrap_or_else(|e| e); + renewals.try_insert( + pos, + AutoRenewalRecord { + core, + task, + next_renewal: workload_end_hint.unwrap_or(sale.region_end), + }, + ) + }) + .map_err(|_| Error::::TooManyAutoRenewals)?; + + Self::deposit_event(Event::AutoRenewalEnabled { core, task }); + Ok(()) + } + + pub(crate) fn do_disable_auto_renew(core: CoreIndex, task: TaskId) -> DispatchResult { + AutoRenewals::::try_mutate(|renewals| -> DispatchResult { + let pos = renewals + .binary_search_by(|r: &AutoRenewalRecord| r.core.cmp(&core)) + .map_err(|_| Error::::AutoRenewalNotEnabled)?; + + let renewal_record = renewals.get(pos).ok_or(Error::::AutoRenewalNotEnabled)?; + + ensure!( + renewal_record.core == core && renewal_record.task == task, + Error::::NoPermission + ); + renewals.remove(pos); + Ok(()) + })?; + + Self::deposit_event(Event::AutoRenewalDisabled { core, task }); + Ok(()) + } + pub(crate) fn ensure_cores_for_sale( status: &StatusRecord, sale: &SaleInfoRecordOf, diff --git a/substrate/frame/broker/src/lib.rs b/substrate/frame/broker/src/lib.rs index 45c33c1bfa61..10745544fadf 100644 --- a/substrate/frame/broker/src/lib.rs +++ b/substrate/frame/broker/src/lib.rs @@ -65,7 +65,7 @@ pub mod pallet { PalletId, }; use frame_system::pallet_prelude::*; - use sp_runtime::traits::{Convert, ConvertBack}; + use sp_runtime::traits::{Convert, ConvertBack, MaybeConvert}; const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); @@ -101,6 +101,10 @@ pub mod pallet { type ConvertBalance: Convert, RelayBalanceOf> + ConvertBack, RelayBalanceOf>; + /// Type used for getting the associated account of a task. This account is controlled by + /// the task itself. + type SovereignAccountOf: MaybeConvert; + /// Identifier from which the internal Pot is generated. #[pallet::constant] type PalletId: Get; @@ -116,6 +120,9 @@ pub mod pallet { /// Maximum number of system cores. #[pallet::constant] type MaxReservedCores: Get; + + #[pallet::constant] + type MaxAutoRenewals: Get; } /// The current configuration of this pallet. @@ -176,6 +183,13 @@ pub mod pallet { #[pallet::storage] pub type CoreCountInbox = StorageValue<_, CoreIndex, OptionQuery>; + /// Keeping track of cores which have auto-renewal enabled. + /// + /// Sorted by `CoreIndex` to make the removal of cores from auto-renewal more efficient. + #[pallet::storage] + pub type AutoRenewals = + StorageValue<_, BoundedVec, ValueQuery>; + /// Received revenue info from the relay chain. #[pallet::storage] pub type RevenueInbox = StorageValue<_, OnDemandRevenueRecordOf, OptionQuery>; @@ -426,6 +440,33 @@ pub mod pallet { /// The core whose workload is no longer available to be renewed for `when`. core: CoreIndex, }, + AutoRenewalEnabled { + /// The core for which the renewal was enabled. + core: CoreIndex, + /// The task for which the renewal was enabled. + task: TaskId, + }, + AutoRenewalDisabled { + /// The core for which the renewal was disabled. + core: CoreIndex, + /// The task for which the renewal was disabled. + task: TaskId, + }, + /// Failed to auto-renew a core, likely due to the payer account not being sufficiently + /// funded. + AutoRenewalFailed { + /// The core for which the renewal failed. + core: CoreIndex, + /// The account which was supposed to pay for renewal. + /// + /// If `None` it indicates that we failed to get the sovereign account of a task. + payer: Option, + }, + /// The auto-renewal limit has been reached upon renewing cores. + /// + /// This should never happen, given that enable_auto_renew checks for this before enabling + /// auto-renewal. + AutoRenewalLimitReached, } #[pallet::error] @@ -492,6 +533,16 @@ pub mod pallet { InvalidConfig, /// The revenue must be claimed for 1 or more timeslices. NoClaimTimeslices, + /// The caller doesn't have the permission to enable or disable auto-renewal. + NoPermission, + /// We reached the limit for auto-renewals. + TooManyAutoRenewals, + /// Only cores which are assigned to a task can be auto-renewed. + NonTaskAutoRenewal, + /// Failed to get the sovereign account of a task. + SovereignAccountNotFound, + /// Attempted to disable auto-renewal for a core that didn't have it enabled. + AutoRenewalNotEnabled, } #[derive(frame_support::DefaultNoBound)] @@ -835,6 +886,62 @@ pub mod pallet { Ok(()) } + /// Extrinsic for enabling auto renewal. + /// + /// Callable by the sovereign account of the task on the specified core. This account + /// will be charged at the start of every bulk period for renewing core time. + /// + /// - `origin`: Must be the sovereign account of the task + /// - `core`: The core to which the task to be renewed is currently assigned. + /// - `task`: The task for which we want to enable auto renewal. + /// - `workload_end_hint`: should be used when enabling auto-renewal for a core that is not + /// expiring in the upcoming bulk period (e.g., due to holding a lease) since it would be + /// inefficient to look up when the core expires to schedule the next renewal. + #[pallet::call_index(21)] + #[pallet::weight(T::WeightInfo::enable_auto_renew())] + pub fn enable_auto_renew( + origin: OriginFor, + core: CoreIndex, + task: TaskId, + workload_end_hint: Option, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let sovereign_account = T::SovereignAccountOf::maybe_convert(task) + .ok_or(Error::::SovereignAccountNotFound)?; + // Only the sovereign account of a task can enable auto renewal for its own core. + ensure!(who == sovereign_account, Error::::NoPermission); + + Self::do_enable_auto_renew(sovereign_account, core, task, workload_end_hint)?; + Ok(()) + } + + /// Extrinsic for disabling auto renewal. + /// + /// Callable by the sovereign account of the task on the specified core. + /// + /// - `origin`: Must be the sovereign account of the task. + /// - `core`: The core for which we want to disable auto renewal. + /// - `task`: The task for which we want to disable auto renewal. + #[pallet::call_index(22)] + #[pallet::weight(T::WeightInfo::disable_auto_renew())] + pub fn disable_auto_renew( + origin: OriginFor, + core: CoreIndex, + task: TaskId, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let sovereign_account = T::SovereignAccountOf::maybe_convert(task) + .ok_or(Error::::SovereignAccountNotFound)?; + // Only the sovereign account of the task can disable auto-renewal. + ensure!(who == sovereign_account, Error::::NoPermission); + + Self::do_disable_auto_renew(core, task)?; + + Ok(()) + } + #[pallet::call_index(99)] #[pallet::weight(T::WeightInfo::swap_leases())] pub fn swap_leases(origin: OriginFor, id: TaskId, other: TaskId) -> DispatchResult { diff --git a/substrate/frame/broker/src/mock.rs b/substrate/frame/broker/src/mock.rs index 6b1d2bbf7015..42377eefdb22 100644 --- a/substrate/frame/broker/src/mock.rs +++ b/substrate/frame/broker/src/mock.rs @@ -32,7 +32,7 @@ use frame_system::{EnsureRoot, EnsureSignedBy}; use sp_arithmetic::Perbill; use sp_core::{ConstU32, ConstU64, Get}; use sp_runtime::{ - traits::{BlockNumberProvider, Identity}, + traits::{BlockNumberProvider, Identity, MaybeConvert}, BuildStorage, Saturating, }; @@ -180,6 +180,14 @@ ord_parameter_types! { } type EnsureOneOrRoot = EitherOfDiverse, EnsureSignedBy>; +// Dummy implementation which converts `TaskId` to `AccountId`. +pub struct SovereignAccountOf; +impl MaybeConvert for SovereignAccountOf { + fn maybe_convert(task: TaskId) -> Option { + Some(task.into()) + } +} + impl crate::Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = ItemOf, ()>, (), u64>; @@ -192,6 +200,8 @@ impl crate::Config for Test { type WeightInfo = (); type PalletId = TestBrokerId; type AdminOrigin = EnsureOneOrRoot; + type SovereignAccountOf = SovereignAccountOf; + type MaxAutoRenewals = ConstU32<3>; type PriceAdapter = CenterTargetPrice>; } @@ -246,6 +256,10 @@ pub fn new_config() -> ConfigRecordOf { } } +pub fn endow(who: u64, amount: u64) { + assert_ok!(<::Currency as Mutate<_>>::mint_into(&who, amount)); +} + pub struct TestExt(ConfigRecordOf); #[allow(dead_code)] impl TestExt { @@ -298,7 +312,7 @@ impl TestExt { } pub fn endow(self, who: u64, amount: u64) -> Self { - assert_ok!(<::Currency as Mutate<_>>::mint_into(&who, amount)); + endow(who, amount); self } diff --git a/substrate/frame/broker/src/tests.rs b/substrate/frame/broker/src/tests.rs index 3ea68c5a74d6..f3fd5234e4ca 100644 --- a/substrate/frame/broker/src/tests.rs +++ b/substrate/frame/broker/src/tests.rs @@ -1530,6 +1530,294 @@ fn renewal_works_leases_ended_before_start_sales() { }); } +#[test] +fn enable_auto_renew_works() { + TestExt::new().endow(1, 1000).limit_cores_offered(Some(10)).execute_with(|| { + assert_ok!(Broker::do_start_sales(100, 5)); + advance_to(2); + let region_id = Broker::do_purchase(1, u64::max_value()).unwrap(); + let record = Regions::::get(region_id).unwrap(); + + // Cannot enable auto renewal with provisional finality: + assert_ok!(Broker::do_assign(region_id, Some(1), 1001, Provisional)); + assert_noop!( + Broker::do_enable_auto_renew(1001, region_id.core, 1001, Some(7)), + Error::::NotAllowed + ); + + // Eligible for renewal after final assignment: + assert_ok!(Broker::do_assign(region_id, Some(1), 1001, Final)); + assert!(PotentialRenewals::::get(PotentialRenewalId { + core: region_id.core, + when: record.end + }) + .is_some()); + + // Only the task's sovereign account can enable auto renewal. + assert_noop!( + Broker::enable_auto_renew(RuntimeOrigin::signed(1), region_id.core, 1001, Some(7)), + Error::::NoPermission + ); + + // Works when calling with the sovereign account: + assert_ok!(Broker::do_enable_auto_renew(1001, region_id.core, 1001, Some(7))); + assert_eq!( + AutoRenewals::::get().to_vec(), + vec![AutoRenewalRecord { core: 0, task: 1001, next_renewal: 7 }] + ); + System::assert_has_event( + Event::::AutoRenewalEnabled { core: region_id.core, task: 1001 }.into(), + ); + + // Enabling auto-renewal for more cores to ensure they are sorted based on core index. + let region_2 = Broker::do_purchase(1, u64::max_value()).unwrap(); + let region_3 = Broker::do_purchase(1, u64::max_value()).unwrap(); + assert_ok!(Broker::do_assign(region_2, Some(1), 1002, Final)); + assert_ok!(Broker::do_assign(region_3, Some(1), 1003, Final)); + assert_ok!(Broker::do_enable_auto_renew(1003, region_3.core, 1003, Some(7))); + assert_ok!(Broker::do_enable_auto_renew(1002, region_2.core, 1002, Some(7))); + + assert_eq!( + AutoRenewals::::get().to_vec(), + vec![ + AutoRenewalRecord { core: 0, task: 1001, next_renewal: 7 }, + AutoRenewalRecord { core: 1, task: 1002, next_renewal: 7 }, + AutoRenewalRecord { core: 2, task: 1003, next_renewal: 7 }, + ] + ); + + // Ensure that we cannot enable more auto renewals than `MaxAutoRenewals`. + // We already enabled it for three cores, and the limit is set to 3. + let region_4 = Broker::do_purchase(1, u64::max_value()).unwrap(); + assert_ok!(Broker::do_assign(region_4, Some(1), 1004, Final)); + + assert_noop!( + Broker::do_enable_auto_renew(1004, region_4.core, 1004, Some(7)), + Error::::TooManyAutoRenewals + ); + }); +} + +#[test] +fn enable_auto_renewal_works_for_legacy_leases() { + TestExt::new().endow(1, 1000).execute_with(|| { + // With this test, we ensure that we don't renew unnecessarily if the task has Coretime + // reserved (due to having a lease) + + assert_ok!(Broker::do_start_sales(100, 1)); + advance_to(2); + + let record = PotentialRenewalRecord { + price: 100, + completion: CompletionStatus::Complete( + vec![ScheduleItem { mask: CoreMask::complete(), assignment: Task(1001) }] + .try_into() + .unwrap(), + ), + }; + // For lease holding tasks, the renewal record is set for when the lease expires, which is + // likely further in the future than the start of the next sale. + PotentialRenewals::::insert(PotentialRenewalId { core: 0, when: 10 }, &record); + + endow(1001, 1000); + + // Will fail if we don't provide the end hint since it expects renewal record to be at next + // sale start. + assert_noop!(Broker::do_enable_auto_renew(1001, 0, 1001, None), Error::::NotAllowed); + + assert_ok!(Broker::do_enable_auto_renew(1001, 0, 1001, Some(10))); + assert_eq!( + AutoRenewals::::get().to_vec(), + vec![AutoRenewalRecord { core: 0, task: 1001, next_renewal: 10 },] + ); + System::assert_has_event(Event::::AutoRenewalEnabled { core: 0, task: 1001 }.into()); + + // Next cycle starting at 7. + advance_to(7); + + // Ensure that the renewal didn't happen by checking that the balance remained the same, as + // there is still no need to renew. + assert_eq!(balance(1001), 1000); + + // The next sale starts at 13. The renewal should happen now and the account should be + // charged. + advance_to(13); + assert_eq!(balance(1001), 900); + + // Make sure that the renewal happened: + System::assert_has_event( + Event::::Renewed { + who: 1001, // sovereign account + old_core: 0, + core: 0, + price: 100, + begin: 10, + duration: 3, + workload: Schedule::truncate_from(vec![ScheduleItem { + assignment: Task(1001), + mask: CoreMask::complete(), + }]), + } + .into(), + ); + }); +} + +#[test] +fn enable_auto_renew_renews() { + TestExt::new().endow(1, 1000).execute_with(|| { + assert_ok!(Broker::do_start_sales(100, 1)); + advance_to(2); + let region_id = Broker::do_purchase(1, u64::max_value()).unwrap(); + + assert_ok!(Broker::do_assign(region_id, Some(1), 1001, Final)); + // advance to next bulk sale: + advance_to(6); + + // Since we didn't renew for the next bulk period, enabling auto-renewal will renew, + // ensuring the task continues execution. + + // Will fail because we didn't fund the sovereign account: + assert_noop!( + Broker::do_enable_auto_renew(1001, region_id.core, 1001, None), + TokenError::FundsUnavailable + ); + + // Will succeed after funding the sovereign account: + endow(1001, 1000); + + assert_ok!(Broker::do_enable_auto_renew(1001, region_id.core, 1001, None)); + assert_eq!( + AutoRenewals::::get().to_vec(), + vec![AutoRenewalRecord { core: 0, task: 1001, next_renewal: 10 }] + ); + assert!(PotentialRenewals::::get(PotentialRenewalId { + core: region_id.core, + when: 10 + }) + .is_some()); + + System::assert_has_event( + Event::::AutoRenewalEnabled { core: region_id.core, task: 1001 }.into(), + ); + }); +} + +#[test] +fn auto_renewal_works() { + TestExt::new().endow(1, 1000).execute_with(|| { + assert_ok!(Broker::do_start_sales(100, 3)); + advance_to(2); + let region_1 = Broker::do_purchase(1, u64::max_value()).unwrap(); + let region_2 = Broker::do_purchase(1, u64::max_value()).unwrap(); + let region_3 = Broker::do_purchase(1, u64::max_value()).unwrap(); + + // Eligible for renewal after final assignment: + assert_ok!(Broker::do_assign(region_1, Some(1), 1001, Final)); + assert_ok!(Broker::do_assign(region_2, Some(1), 1002, Final)); + assert_ok!(Broker::do_assign(region_3, Some(1), 1003, Final)); + assert_ok!(Broker::do_enable_auto_renew(1001, region_1.core, 1001, Some(7))); + assert_ok!(Broker::do_enable_auto_renew(1002, region_2.core, 1002, Some(7))); + assert_ok!(Broker::do_enable_auto_renew(1003, region_3.core, 1003, Some(7))); + assert_eq!( + AutoRenewals::::get().to_vec(), + vec![ + AutoRenewalRecord { core: 0, task: 1001, next_renewal: 7 }, + AutoRenewalRecord { core: 1, task: 1002, next_renewal: 7 }, + AutoRenewalRecord { core: 2, task: 1003, next_renewal: 7 }, + ] + ); + + // We have to fund the sovereign account: + endow(1001, 1000); + // We skip funding the sovereign account of task 1002 on purpose. + endow(1003, 1000); + + // Next cycle starting at 7. + advance_to(7); + System::assert_has_event( + Event::::Renewed { + who: 1001, // sovereign account + old_core: 0, + core: 0, + price: 100, + begin: 7, + duration: 3, + workload: Schedule::truncate_from(vec![ScheduleItem { + assignment: Task(1001), + mask: CoreMask::complete(), + }]), + } + .into(), + ); + // Sovereign account wasn't funded so it fails: + System::assert_has_event( + Event::::AutoRenewalFailed { core: 1, payer: Some(1002) }.into(), + ); + System::assert_has_event( + Event::::Renewed { + who: 1003, // sovereign account + old_core: 2, + core: 1, // Core #1 didn't get renewed, so core #2 will take its place. + price: 100, + begin: 7, + duration: 3, + workload: Schedule::truncate_from(vec![ScheduleItem { + assignment: Task(1003), + mask: CoreMask::complete(), + }]), + } + .into(), + ); + + // Given that core #1 didn't get renewed due to the account not being sufficiently funded, + // Task (1003) will now be assigned to that core instead of core #2. + assert_eq!( + AutoRenewals::::get().to_vec(), + vec![ + AutoRenewalRecord { core: 0, task: 1001, next_renewal: 10 }, + AutoRenewalRecord { core: 1, task: 1003, next_renewal: 10 }, + ] + ); + }); +} + +#[test] +fn disable_auto_renew_works() { + TestExt::new().endow(1, 1000).limit_cores_offered(Some(10)).execute_with(|| { + assert_ok!(Broker::do_start_sales(100, 3)); + advance_to(2); + let region_id = Broker::do_purchase(1, u64::max_value()).unwrap(); + + // Eligible for renewal after final assignment: + assert_ok!(Broker::do_assign(region_id, Some(1), 1001, Final)); + + // Cannot disable auto-renewal if we don't have it enabled. + assert_noop!( + Broker::do_disable_auto_renew(region_id.core, 1001), + Error::::AutoRenewalNotEnabled + ); + + assert_ok!(Broker::do_enable_auto_renew(1001, region_id.core, 1001, Some(7))); + assert_eq!( + AutoRenewals::::get().to_vec(), + vec![AutoRenewalRecord { core: 0, task: 1001, next_renewal: 7 }] + ); + + // Only the sovereign account can disable: + assert_noop!( + Broker::disable_auto_renew(RuntimeOrigin::signed(1), 0, 1001), + Error::::NoPermission + ); + assert_ok!(Broker::do_disable_auto_renew(0, 1001)); + + assert_eq!(AutoRenewals::::get().to_vec(), vec![]); + System::assert_has_event( + Event::::AutoRenewalDisabled { core: region_id.core, task: 1001 }.into(), + ); + }); +} + #[test] fn start_sales_sets_correct_core_count() { TestExt::new().endow(1, 1000).execute_with(|| { diff --git a/substrate/frame/broker/src/tick_impls.rs b/substrate/frame/broker/src/tick_impls.rs index 71a1286d7391..8dbd5df57166 100644 --- a/substrate/frame/broker/src/tick_impls.rs +++ b/substrate/frame/broker/src/tick_impls.rs @@ -19,7 +19,7 @@ use super::*; use alloc::{vec, vec::Vec}; use frame_support::{pallet_prelude::*, traits::defensive_prelude::*, weights::WeightMeter}; use sp_arithmetic::traits::{One, SaturatedConversion, Saturating, Zero}; -use sp_runtime::traits::ConvertBack; +use sp_runtime::traits::{ConvertBack, MaybeConvert}; use CompletionStatus::Complete; impl Pallet { @@ -263,6 +263,9 @@ impl Pallet { }; SaleInfo::::put(&new_sale); + + Self::renew_cores(&new_sale); + Self::deposit_event(Event::SaleInitialized { sale_start, leadin_length, @@ -334,4 +337,50 @@ impl Pallet { T::Coretime::assign_core(core, rc_begin, assignment.clone(), None); Self::deposit_event(Event::::CoreAssigned { core, when: rc_begin, assignment }); } + + /// Renews all the cores which have auto-renewal enabled. + pub(crate) fn renew_cores(sale: &SaleInfoRecordOf) { + let renewals = AutoRenewals::::get(); + + let Ok(auto_renewals) = renewals + .into_iter() + .flat_map(|record| { + // Check if the next renewal is scheduled further in the future than the start of + // the next region beginning. If so, we skip the renewal for this core. + if sale.region_begin < record.next_renewal { + return Some(record) + } + + let Some(payer) = T::SovereignAccountOf::maybe_convert(record.task) else { + Self::deposit_event(Event::::AutoRenewalFailed { + core: record.core, + payer: None, + }); + return None + }; + + if let Ok(new_core_index) = Self::do_renew(payer.clone(), record.core) { + Some(AutoRenewalRecord { + core: new_core_index, + task: record.task, + next_renewal: sale.region_end, + }) + } else { + Self::deposit_event(Event::::AutoRenewalFailed { + core: record.core, + payer: Some(payer), + }); + + None + } + }) + .collect::>() + .try_into() + else { + Self::deposit_event(Event::::AutoRenewalLimitReached); + return; + }; + + AutoRenewals::::set(auto_renewals); + } } diff --git a/substrate/frame/broker/src/types.rs b/substrate/frame/broker/src/types.rs index dcfa9a77e4f3..10e6756bc90e 100644 --- a/substrate/frame/broker/src/types.rs +++ b/substrate/frame/broker/src/types.rs @@ -311,3 +311,16 @@ where Ok(()) } } + +/// A record containing information regarding auto-renewal for a specific core. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct AutoRenewalRecord { + /// The core for which auto renewal is enabled. + pub core: CoreIndex, + /// The task assigned to the core. We keep track of it so we don't have to look it up when + /// performing auto-renewal. + pub task: TaskId, + /// Specifies when the upcoming renewal should be performed. This is used for lease holding + /// tasks to ensure that the renewal process does not begin until the lease expires. + pub next_renewal: Timeslice, +} diff --git a/substrate/frame/broker/src/utility_impls.rs b/substrate/frame/broker/src/utility_impls.rs index 5c66ebb9674b..e937e0cbbec5 100644 --- a/substrate/frame/broker/src/utility_impls.rs +++ b/substrate/frame/broker/src/utility_impls.rs @@ -17,7 +17,7 @@ use super::*; use frame_support::{ - pallet_prelude::{DispatchResult, *}, + pallet_prelude::*, traits::{ fungible::Balanced, tokens::{Fortitude::Polite, Precision::Exact, Preservation::Expendable}, diff --git a/substrate/frame/broker/src/weights.rs b/substrate/frame/broker/src/weights.rs index 4889c2577ddd..2f25fddc2050 100644 --- a/substrate/frame/broker/src/weights.rs +++ b/substrate/frame/broker/src/weights.rs @@ -18,25 +18,27 @@ //! Autogenerated weights for `pallet_broker` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-06-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-x5tnzzy-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `sergej-B650-AORUS-ELITE-AX`, CPU: `AMD Ryzen 9 7900X3D 12-Core Processor` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// target/production/substrate-node +// ./target/release/substrate-node // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_broker +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_broker -// --chain=dev -// --header=./substrate/HEADER-APACHE2 // --output=./substrate/frame/broker/src/weights.rs +// --header=./substrate/HEADER-APACHE2 // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -79,6 +81,8 @@ pub trait WeightInfo { fn do_tick_base() -> Weight; fn swap_leases() -> Weight; fn on_new_timeslice() -> Weight; + fn enable_auto_renew() -> Weight; + fn disable_auto_renew() -> Weight; } /// Weights for `pallet_broker` using the Substrate node and recommended hardware. @@ -90,8 +94,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_977_000 picoseconds. - Weight::from_parts(2_114_000, 0) + // Minimum execution time: 1_593_000 picoseconds. + Weight::from_parts(1_703_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::Reservations` (r:1 w:1) @@ -100,8 +104,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `5016` // Estimated: `7496` - // Minimum execution time: 16_880_000 picoseconds. - Weight::from_parts(17_506_000, 7496) + // Minimum execution time: 12_864_000 picoseconds. + Weight::from_parts(13_174_000, 7496) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -111,8 +115,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `6218` // Estimated: `7496` - // Minimum execution time: 15_569_000 picoseconds. - Weight::from_parts(16_123_000, 7496) + // Minimum execution time: 12_284_000 picoseconds. + Weight::from_parts(13_566_000, 7496) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -122,8 +126,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 8_962_000 picoseconds. - Weight::from_parts(9_389_000, 1526) + // Minimum execution time: 6_743_000 picoseconds. + Weight::from_parts(7_094_000, 1526) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -135,6 +139,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::Reservations` (`max_values`: Some(1), `max_size`: Some(6011), added: 6506, mode: `MaxEncodedLen`) /// Storage: `Broker::InstaPoolIo` (r:3 w:3) /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `Broker::AutoRenewals` (r:1 w:1) + /// Proof: `Broker::AutoRenewals` (`max_values`: Some(1), `max_size`: Some(101), added: 596, mode: `MaxEncodedLen`) /// Storage: `Broker::SaleInfo` (r:0 w:1) /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) /// Storage: `Broker::Status` (r:0 w:1) @@ -146,12 +152,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `6330` // Estimated: `8499` - // Minimum execution time: 27_119_000 picoseconds. - Weight::from_parts(47_930_900, 8499) - // Standard Error: 464 - .saturating_add(Weight::from_parts(2_940, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().writes(15_u64)) + // Minimum execution time: 21_120_000 picoseconds. + Weight::from_parts(40_929_422, 8499) + // Standard Error: 471 + .saturating_add(Weight::from_parts(1_004, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(16_u64)) } /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) @@ -167,8 +173,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `651` // Estimated: `2136` - // Minimum execution time: 42_429_000 picoseconds. - Weight::from_parts(43_538_000, 2136) + // Minimum execution time: 31_169_000 picoseconds. + Weight::from_parts(32_271_000, 2136) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -190,8 +196,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `769` // Estimated: `4698` - // Minimum execution time: 62_957_000 picoseconds. - Weight::from_parts(66_821_000, 4698) + // Minimum execution time: 44_945_000 picoseconds. + Weight::from_parts(47_119_000, 4698) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -201,8 +207,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `496` // Estimated: `3551` - // Minimum execution time: 16_146_000 picoseconds. - Weight::from_parts(16_775_000, 3551) + // Minimum execution time: 11_562_000 picoseconds. + Weight::from_parts(11_943_000, 3551) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -212,8 +218,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `496` // Estimated: `3551` - // Minimum execution time: 17_720_000 picoseconds. - Weight::from_parts(18_916_000, 3551) + // Minimum execution time: 13_075_000 picoseconds. + Weight::from_parts(13_616_000, 3551) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -223,8 +229,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `496` // Estimated: `3551` - // Minimum execution time: 19_088_000 picoseconds. - Weight::from_parts(19_732_000, 3551) + // Minimum execution time: 13_695_000 picoseconds. + Weight::from_parts(14_658_000, 3551) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -240,8 +246,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `741` // Estimated: `4681` - // Minimum execution time: 30_522_000 picoseconds. - Weight::from_parts(31_573_000, 4681) + // Minimum execution time: 22_623_000 picoseconds. + Weight::from_parts(23_233_000, 4681) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -259,8 +265,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `776` // Estimated: `5996` - // Minimum execution time: 35_833_000 picoseconds. - Weight::from_parts(36_830_000, 5996) + // Minimum execution time: 26_901_000 picoseconds. + Weight::from_parts(27_472_000, 5996) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -273,12 +279,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[1, 3]`. fn claim_revenue(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `859` + // Measured: `878` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 65_882_000 picoseconds. - Weight::from_parts(67_506_904, 6196) - // Standard Error: 49_386 - .saturating_add(Weight::from_parts(1_197_959, 0).saturating_mul(m.into())) + // Minimum execution time: 51_778_000 picoseconds. + Weight::from_parts(53_726_731, 6196) + // Standard Error: 45_279 + .saturating_add(Weight::from_parts(677_769, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -290,8 +296,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 41_860_000 picoseconds. - Weight::from_parts(42_478_000, 3593) + // Minimum execution time: 31_790_000 picoseconds. + Weight::from_parts(32_601_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -303,8 +309,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `604` // Estimated: `3551` - // Minimum execution time: 32_593_000 picoseconds. - Weight::from_parts(35_399_000, 3551) + // Minimum execution time: 18_465_000 picoseconds. + Weight::from_parts(21_050_000, 3551) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -318,8 +324,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `601` // Estimated: `3533` - // Minimum execution time: 41_934_000 picoseconds. - Weight::from_parts(50_480_000, 3533) + // Minimum execution time: 23_825_000 picoseconds. + Weight::from_parts(26_250_000, 3533) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -333,10 +339,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn drop_history() -> Weight { // Proof Size summary in bytes: - // Measured: `995` + // Measured: `1014` // Estimated: `3593` - // Minimum execution time: 47_167_000 picoseconds. - Weight::from_parts(54_289_000, 3593) + // Minimum execution time: 28_103_000 picoseconds. + Weight::from_parts(32_622_000, 3593) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -348,20 +354,18 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `661` // Estimated: `4698` - // Minimum execution time: 29_755_000 picoseconds. - Weight::from_parts(32_857_000, 4698) + // Minimum execution time: 16_751_000 picoseconds. + Weight::from_parts(17_373_000, 4698) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// The range of component `n` is `[0, 1000]`. - fn request_core_count(n: u32, ) -> Weight { + fn request_core_count(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_793_000 picoseconds. - Weight::from_parts(4_086_907, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(60, 0).saturating_mul(n.into())) + // Minimum execution time: 2_705_000 picoseconds. + Weight::from_parts(2_991_768, 0) } /// Storage: `Broker::CoreCountInbox` (r:1 w:1) /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -370,13 +374,13 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `404` // Estimated: `1487` - // Minimum execution time: 6_262_000 picoseconds. - Weight::from_parts(6_734_896, 1487) + // Minimum execution time: 4_598_000 picoseconds. + Weight::from_parts(4_937_302, 1487) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `Broker::RevenueInbox` (r:1 w:1) - /// Proof: `Broker::RevenueInbox` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) /// Storage: `Broker::InstaPoolHistory` (r:1 w:1) /// Proof: `Broker::InstaPoolHistory` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) @@ -387,32 +391,20 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn process_revenue() -> Weight { // Proof Size summary in bytes: - // Measured: `829` - // Estimated: `3593` - // Minimum execution time: 39_812_000 picoseconds. - Weight::from_parts(41_227_000, 3593) + // Measured: `991` + // Estimated: `4456` + // Minimum execution time: 37_601_000 picoseconds. + Weight::from_parts(38_262_000, 4456) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: `Broker::InstaPoolIo` (r:3 w:3) - /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) - /// Storage: `Broker::Reservations` (r:1 w:0) - /// Proof: `Broker::Reservations` (`max_values`: Some(1), `max_size`: Some(6011), added: 6506, mode: `MaxEncodedLen`) - /// Storage: `Broker::Leases` (r:1 w:1) - /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(41), added: 536, mode: `MaxEncodedLen`) - /// Storage: `Broker::SaleInfo` (r:0 w:1) - /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) - /// Storage: `Broker::Workplan` (r:0 w:10) - /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn rotate_sale(_n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `6281` - // Estimated: `8499` - // Minimum execution time: 34_576_000 picoseconds. - Weight::from_parts(36_303_629, 8499) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(15_u64)) + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(0, 0) } /// Storage: `Broker::InstaPoolIo` (r:1 w:0) /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) @@ -422,8 +414,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3493` - // Minimum execution time: 6_978_000 picoseconds. - Weight::from_parts(7_206_000, 3493) + // Minimum execution time: 5_391_000 picoseconds. + Weight::from_parts(5_630_000, 3493) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -435,8 +427,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1423` // Estimated: `4681` - // Minimum execution time: 15_063_000 picoseconds. - Weight::from_parts(15_463_000, 4681) + // Minimum execution time: 10_249_000 picoseconds. + Weight::from_parts(10_529_000, 4681) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -444,8 +436,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 126_000 picoseconds. - Weight::from_parts(157_000, 0) + // Minimum execution time: 120_000 picoseconds. + Weight::from_parts(140_000, 0) } /// Storage: `Broker::CoreCountInbox` (r:0 w:1) /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -453,8 +445,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_803_000 picoseconds. - Weight::from_parts(1_965_000, 0) + // Minimum execution time: 1_402_000 picoseconds. + Weight::from_parts(1_513_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::RevenueInbox` (r:0 w:1) @@ -473,16 +465,16 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) /// Storage: `Broker::CoreCountInbox` (r:1 w:0) /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `Broker::RevenueInbox` (r:1 w:0) - /// Proof: `Broker::RevenueInbox` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) fn do_tick_base() -> Weight { // Proof Size summary in bytes: - // Measured: `441` - // Estimated: `1516` - // Minimum execution time: 9_313_000 picoseconds. - Weight::from_parts(9_699_000, 1516) + // Measured: `603` + // Estimated: `4068` + // Minimum execution time: 8_897_000 picoseconds. + Weight::from_parts(9_218_000, 4068) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Broker::Leases` (r:1 w:1) /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(41), added: 536, mode: `MaxEncodedLen`) @@ -490,8 +482,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 5_984_000 picoseconds. - Weight::from_parts(6_296_000, 1526) + // Minimum execution time: 4_678_000 picoseconds. + Weight::from_parts(4_920_000, 1526) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -502,6 +494,44 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 229_000 picoseconds. Weight::from_parts(268_000, 0) } + /// Storage: `Broker::SaleInfo` (r:1 w:1) + /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) + /// Storage: `Broker::PotentialRenewals` (r:1 w:2) + /// Proof: `Broker::PotentialRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `Broker::Configuration` (r:1 w:0) + /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + /// Storage: `Broker::Status` (r:1 w:0) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Broker::AutoRenewals` (r:1 w:1) + /// Proof: `Broker::AutoRenewals` (`max_values`: Some(1), `max_size`: Some(101), added: 596, mode: `MaxEncodedLen`) + /// Storage: `Broker::Workplan` (r:0 w:1) + /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) + fn enable_auto_renew() -> Weight { + // Proof Size summary in bytes: + // Measured: `930` + // Estimated: `4698` + // Minimum execution time: 51_597_000 picoseconds. + Weight::from_parts(52_609_000, 4698) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `Broker::AutoRenewals` (r:1 w:1) + /// Proof: `Broker::AutoRenewals` (`max_values`: Some(1), `max_size`: Some(101), added: 596, mode: `MaxEncodedLen`) + fn disable_auto_renew() -> Weight { + // Proof Size summary in bytes: + // Measured: `484` + // Estimated: `1586` + // Minimum execution time: 8_907_000 picoseconds. + Weight::from_parts(9_167_000, 1586) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } // For backwards compatibility and tests. @@ -512,8 +542,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_977_000 picoseconds. - Weight::from_parts(2_114_000, 0) + // Minimum execution time: 1_593_000 picoseconds. + Weight::from_parts(1_703_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::Reservations` (r:1 w:1) @@ -522,8 +552,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `5016` // Estimated: `7496` - // Minimum execution time: 16_880_000 picoseconds. - Weight::from_parts(17_506_000, 7496) + // Minimum execution time: 12_864_000 picoseconds. + Weight::from_parts(13_174_000, 7496) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -533,8 +563,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `6218` // Estimated: `7496` - // Minimum execution time: 15_569_000 picoseconds. - Weight::from_parts(16_123_000, 7496) + // Minimum execution time: 12_284_000 picoseconds. + Weight::from_parts(13_566_000, 7496) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -544,8 +574,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 8_962_000 picoseconds. - Weight::from_parts(9_389_000, 1526) + // Minimum execution time: 6_743_000 picoseconds. + Weight::from_parts(7_094_000, 1526) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -557,6 +587,8 @@ impl WeightInfo for () { /// Proof: `Broker::Reservations` (`max_values`: Some(1), `max_size`: Some(6011), added: 6506, mode: `MaxEncodedLen`) /// Storage: `Broker::InstaPoolIo` (r:3 w:3) /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `Broker::AutoRenewals` (r:1 w:1) + /// Proof: `Broker::AutoRenewals` (`max_values`: Some(1), `max_size`: Some(101), added: 596, mode: `MaxEncodedLen`) /// Storage: `Broker::SaleInfo` (r:0 w:1) /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) /// Storage: `Broker::Status` (r:0 w:1) @@ -568,12 +600,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `6330` // Estimated: `8499` - // Minimum execution time: 27_119_000 picoseconds. - Weight::from_parts(47_930_900, 8499) - // Standard Error: 464 - .saturating_add(Weight::from_parts(2_940, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().writes(15_u64)) + // Minimum execution time: 21_120_000 picoseconds. + Weight::from_parts(40_929_422, 8499) + // Standard Error: 471 + .saturating_add(Weight::from_parts(1_004, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(16_u64)) } /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) @@ -589,8 +621,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `651` // Estimated: `2136` - // Minimum execution time: 42_429_000 picoseconds. - Weight::from_parts(43_538_000, 2136) + // Minimum execution time: 31_169_000 picoseconds. + Weight::from_parts(32_271_000, 2136) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -612,8 +644,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `769` // Estimated: `4698` - // Minimum execution time: 62_957_000 picoseconds. - Weight::from_parts(66_821_000, 4698) + // Minimum execution time: 44_945_000 picoseconds. + Weight::from_parts(47_119_000, 4698) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -623,8 +655,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `496` // Estimated: `3551` - // Minimum execution time: 16_146_000 picoseconds. - Weight::from_parts(16_775_000, 3551) + // Minimum execution time: 11_562_000 picoseconds. + Weight::from_parts(11_943_000, 3551) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -634,8 +666,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `496` // Estimated: `3551` - // Minimum execution time: 17_720_000 picoseconds. - Weight::from_parts(18_916_000, 3551) + // Minimum execution time: 13_075_000 picoseconds. + Weight::from_parts(13_616_000, 3551) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -645,8 +677,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `496` // Estimated: `3551` - // Minimum execution time: 19_088_000 picoseconds. - Weight::from_parts(19_732_000, 3551) + // Minimum execution time: 13_695_000 picoseconds. + Weight::from_parts(14_658_000, 3551) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -662,8 +694,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `741` // Estimated: `4681` - // Minimum execution time: 30_522_000 picoseconds. - Weight::from_parts(31_573_000, 4681) + // Minimum execution time: 22_623_000 picoseconds. + Weight::from_parts(23_233_000, 4681) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -681,8 +713,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `776` // Estimated: `5996` - // Minimum execution time: 35_833_000 picoseconds. - Weight::from_parts(36_830_000, 5996) + // Minimum execution time: 26_901_000 picoseconds. + Weight::from_parts(27_472_000, 5996) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -695,12 +727,12 @@ impl WeightInfo for () { /// The range of component `m` is `[1, 3]`. fn claim_revenue(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `859` + // Measured: `878` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 65_882_000 picoseconds. - Weight::from_parts(67_506_904, 6196) - // Standard Error: 49_386 - .saturating_add(Weight::from_parts(1_197_959, 0).saturating_mul(m.into())) + // Minimum execution time: 51_778_000 picoseconds. + Weight::from_parts(53_726_731, 6196) + // Standard Error: 45_279 + .saturating_add(Weight::from_parts(677_769, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -712,8 +744,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 41_860_000 picoseconds. - Weight::from_parts(42_478_000, 3593) + // Minimum execution time: 31_790_000 picoseconds. + Weight::from_parts(32_601_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -725,8 +757,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `604` // Estimated: `3551` - // Minimum execution time: 32_593_000 picoseconds. - Weight::from_parts(35_399_000, 3551) + // Minimum execution time: 18_465_000 picoseconds. + Weight::from_parts(21_050_000, 3551) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -740,8 +772,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `601` // Estimated: `3533` - // Minimum execution time: 41_934_000 picoseconds. - Weight::from_parts(50_480_000, 3533) + // Minimum execution time: 23_825_000 picoseconds. + Weight::from_parts(26_250_000, 3533) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -755,10 +787,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn drop_history() -> Weight { // Proof Size summary in bytes: - // Measured: `995` + // Measured: `1014` // Estimated: `3593` - // Minimum execution time: 47_167_000 picoseconds. - Weight::from_parts(54_289_000, 3593) + // Minimum execution time: 28_103_000 picoseconds. + Weight::from_parts(32_622_000, 3593) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -770,20 +802,18 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `661` // Estimated: `4698` - // Minimum execution time: 29_755_000 picoseconds. - Weight::from_parts(32_857_000, 4698) + // Minimum execution time: 16_751_000 picoseconds. + Weight::from_parts(17_373_000, 4698) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// The range of component `n` is `[0, 1000]`. - fn request_core_count(n: u32, ) -> Weight { + fn request_core_count(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_793_000 picoseconds. - Weight::from_parts(4_086_907, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(60, 0).saturating_mul(n.into())) + // Minimum execution time: 2_705_000 picoseconds. + Weight::from_parts(2_991_768, 0) } /// Storage: `Broker::CoreCountInbox` (r:1 w:1) /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -792,13 +822,13 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `404` // Estimated: `1487` - // Minimum execution time: 6_262_000 picoseconds. - Weight::from_parts(6_734_896, 1487) + // Minimum execution time: 4_598_000 picoseconds. + Weight::from_parts(4_937_302, 1487) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `Broker::RevenueInbox` (r:1 w:1) - /// Proof: `Broker::RevenueInbox` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) /// Storage: `Broker::InstaPoolHistory` (r:1 w:1) /// Proof: `Broker::InstaPoolHistory` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) @@ -809,32 +839,20 @@ impl WeightInfo for () { /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn process_revenue() -> Weight { // Proof Size summary in bytes: - // Measured: `829` - // Estimated: `3593` - // Minimum execution time: 39_812_000 picoseconds. - Weight::from_parts(41_227_000, 3593) + // Measured: `991` + // Estimated: `4456` + // Minimum execution time: 37_601_000 picoseconds. + Weight::from_parts(38_262_000, 4456) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: `Broker::InstaPoolIo` (r:3 w:3) - /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) - /// Storage: `Broker::Reservations` (r:1 w:0) - /// Proof: `Broker::Reservations` (`max_values`: Some(1), `max_size`: Some(6011), added: 6506, mode: `MaxEncodedLen`) - /// Storage: `Broker::Leases` (r:1 w:1) - /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(41), added: 536, mode: `MaxEncodedLen`) - /// Storage: `Broker::SaleInfo` (r:0 w:1) - /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) - /// Storage: `Broker::Workplan` (r:0 w:10) - /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn rotate_sale(_n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `6281` - // Estimated: `8499` - // Minimum execution time: 34_576_000 picoseconds. - Weight::from_parts(36_303_629, 8499) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(15_u64)) + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(0, 0) } /// Storage: `Broker::InstaPoolIo` (r:1 w:0) /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) @@ -844,8 +862,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3493` - // Minimum execution time: 6_978_000 picoseconds. - Weight::from_parts(7_206_000, 3493) + // Minimum execution time: 5_391_000 picoseconds. + Weight::from_parts(5_630_000, 3493) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -857,8 +875,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1423` // Estimated: `4681` - // Minimum execution time: 15_063_000 picoseconds. - Weight::from_parts(15_463_000, 4681) + // Minimum execution time: 10_249_000 picoseconds. + Weight::from_parts(10_529_000, 4681) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -866,8 +884,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 126_000 picoseconds. - Weight::from_parts(157_000, 0) + // Minimum execution time: 120_000 picoseconds. + Weight::from_parts(140_000, 0) } /// Storage: `Broker::CoreCountInbox` (r:0 w:1) /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -875,8 +893,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_803_000 picoseconds. - Weight::from_parts(1_965_000, 0) + // Minimum execution time: 1_402_000 picoseconds. + Weight::from_parts(1_513_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::RevenueInbox` (r:0 w:1) @@ -895,16 +913,16 @@ impl WeightInfo for () { /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) /// Storage: `Broker::CoreCountInbox` (r:1 w:0) /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `Broker::RevenueInbox` (r:1 w:0) - /// Proof: `Broker::RevenueInbox` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) fn do_tick_base() -> Weight { // Proof Size summary in bytes: - // Measured: `441` - // Estimated: `1516` - // Minimum execution time: 9_313_000 picoseconds. - Weight::from_parts(9_699_000, 1516) + // Measured: `603` + // Estimated: `4068` + // Minimum execution time: 8_897_000 picoseconds. + Weight::from_parts(9_218_000, 4068) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Broker::Leases` (r:1 w:1) /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(41), added: 536, mode: `MaxEncodedLen`) @@ -912,8 +930,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 5_984_000 picoseconds. - Weight::from_parts(6_296_000, 1526) + // Minimum execution time: 4_678_000 picoseconds. + Weight::from_parts(4_920_000, 1526) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -924,4 +942,42 @@ impl WeightInfo for () { // Minimum execution time: 229_000 picoseconds. Weight::from_parts(268_000, 0) } -} + /// Storage: `Broker::SaleInfo` (r:1 w:1) + /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) + /// Storage: `Broker::PotentialRenewals` (r:1 w:2) + /// Proof: `Broker::PotentialRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `Broker::Configuration` (r:1 w:0) + /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + /// Storage: `Broker::Status` (r:1 w:0) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Broker::AutoRenewals` (r:1 w:1) + /// Proof: `Broker::AutoRenewals` (`max_values`: Some(1), `max_size`: Some(101), added: 596, mode: `MaxEncodedLen`) + /// Storage: `Broker::Workplan` (r:0 w:1) + /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) + fn enable_auto_renew() -> Weight { + // Proof Size summary in bytes: + // Measured: `930` + // Estimated: `4698` + // Minimum execution time: 51_597_000 picoseconds. + Weight::from_parts(52_609_000, 4698) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: `Broker::AutoRenewals` (r:1 w:1) + /// Proof: `Broker::AutoRenewals` (`max_values`: Some(1), `max_size`: Some(101), added: 596, mode: `MaxEncodedLen`) + fn disable_auto_renew() -> Weight { + // Proof Size summary in bytes: + // Measured: `484` + // Estimated: `1586` + // Minimum execution time: 8_907_000 picoseconds. + Weight::from_parts(9_167_000, 1586) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} \ No newline at end of file From 0cc3e170d3c4ff7ba26054be0bc114fa287d9d69 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Mon, 5 Aug 2024 14:32:46 +0300 Subject: [PATCH 061/382] make polkadot-parachain startup errors pretty (#5214) The errors on polkadot-parachain are not printed with their full display context(what is marked with `#[error(`) because main returns plain Result and the error will be shown in its Debug format, that's not consistent with how the polkadot binary behave and is not user friendly since it does not tell them why they got the error. Fix it by using `color_eyre` as polkadot already does it. Fixes: https://github.com/paritytech/polkadot-sdk/issues/5211 ## Output before ``` Error: NetworkKeyNotFound("/acala/data/Collator2/chains/mandala-tc9/network/secret_ed25519") ``` ## Output after ``` Error: 0: Starting an authorithy without network key in /home/alexggh/.local/share/polkadot-parachain/chains/asset-hub-kusama/network/secret_ed25519. This is not a safe operation because other authorities in the network may depend on your node having a stable identity. Otherwise these other authorities may not being able to reach you. If it is the first time running your node you could use one of the following methods: 1. [Preferred] Separately generate the key with: key generate-node-key --base-path 2. [Preferred] Separately generate the key with: key generate-node-key --file 3. [Preferred] Separately generate the key with: key generate-node-key --default-base-path 4. [Unsafe] Pass --unsafe-force-node-key-generation and make sure you remove it for subsequent node restarts ``` --------- Signed-off-by: Alexandru Gheorghe --- Cargo.lock | 1 + cumulus/polkadot-parachain/Cargo.toml | 1 + cumulus/polkadot-parachain/src/main.rs | 5 +++-- prdoc/pr_5214.prdoc | 11 +++++++++++ 4 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_5214.prdoc diff --git a/Cargo.lock b/Cargo.lock index d37babb5bbc0..4f41f3ca1eb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13845,6 +13845,7 @@ dependencies = [ "bridge-hub-westend-runtime", "clap 4.5.11", "collectives-westend-runtime", + "color-eyre", "color-print", "contracts-rococo-runtime", "coretime-rococo-runtime", diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index b20d2a28fa7f..e1016cebb395 100644 --- a/cumulus/polkadot-parachain/Cargo.toml +++ b/cumulus/polkadot-parachain/Cargo.toml @@ -18,6 +18,7 @@ path = "src/main.rs" async-trait = { workspace = true } clap = { features = ["derive"], workspace = true } codec = { workspace = true, default-features = true } +color-eyre = { workspace = true } color-print = { workspace = true } futures = { workspace = true } hex-literal = { workspace = true, default-features = true } diff --git a/cumulus/polkadot-parachain/src/main.rs b/cumulus/polkadot-parachain/src/main.rs index cbb76fa214cb..84e41fc347d9 100644 --- a/cumulus/polkadot-parachain/src/main.rs +++ b/cumulus/polkadot-parachain/src/main.rs @@ -50,6 +50,7 @@ mod fake_runtime_api; mod rpc; mod service; -fn main() -> sc_cli::Result<()> { - command::run() +fn main() -> color_eyre::eyre::Result<()> { + color_eyre::install()?; + Ok(command::run()?) } diff --git a/prdoc/pr_5214.prdoc b/prdoc/pr_5214.prdoc new file mode 100644 index 000000000000..4dc8b28c5948 --- /dev/null +++ b/prdoc/pr_5214.prdoc @@ -0,0 +1,11 @@ +title: make polkadot-parachain startup errors pretty + +doc: + - audience: Node Operator + description: | + Changed the format of how polkadot-parachain prints the startup errors to include + the full displayable context. + +crates: + - name: polkadot-parachain-bin + bump: minor From 035211d707d0a74a2a768fd658160721f09d5b44 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Mon, 5 Aug 2024 13:48:58 +0200 Subject: [PATCH 062/382] Remove unused feature gated code from the minimal template (#5237) - Progresses https://github.com/paritytech/polkadot-sdk/issues/5226 There is no actual `try-runtime` or `runtime-benchmarks` functionality in the minimal template at the moment. --- templates/minimal/node/src/command.rs | 3 --- templates/minimal/node/src/service.rs | 5 ----- 2 files changed, 8 deletions(-) diff --git a/templates/minimal/node/src/command.rs b/templates/minimal/node/src/command.rs index c17f9bc55927..504be1ccc36e 100644 --- a/templates/minimal/node/src/command.rs +++ b/templates/minimal/node/src/command.rs @@ -23,9 +23,6 @@ use crate::{ use sc_cli::SubstrateCli; use sc_service::PartialComponents; -#[cfg(feature = "try-runtime")] -use try_runtime_cli::block_building_info::timestamp_with_aura_info; - impl SubstrateCli for Cli { fn impl_name() -> String { "Substrate Node".into() diff --git a/templates/minimal/node/src/service.rs b/templates/minimal/node/src/service.rs index e298d3dd6dc1..44b374fcc0a4 100644 --- a/templates/minimal/node/src/service.rs +++ b/templates/minimal/node/src/service.rs @@ -27,11 +27,6 @@ use std::sync::Arc; use crate::cli::Consensus; -#[cfg(feature = "runtime-benchmarks")] -type HostFunctions = - (sp_io::SubstrateHostFunctions, frame_benchmarking::benchmarking::HostFunctions); - -#[cfg(not(feature = "runtime-benchmarks"))] type HostFunctions = sp_io::SubstrateHostFunctions; #[docify::export] From 2b5ccbae193abefdc20727bc86af80a1d18d551d Mon Sep 17 00:00:00 2001 From: Pavel Suprunyuk <52500720+pavelsupr@users.noreply.github.com> Date: Tue, 6 Aug 2024 16:52:16 +0200 Subject: [PATCH 063/382] Github Actions workflow to automatically sync critical forks (#5259) This Workflow is not supposed to run in the paritytech/polkadot-sdk repo. This Workflow is supposed to run only in the forks of the repo, in `paritytech-release/polkadot-sdk` specifically, to automatically maintain the critical fork synced with the upstream. This Workflow should be always disabled in the paritytech/polkadot-sdk repo. --- .github/workflows/fork-sync-action.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/fork-sync-action.yml diff --git a/.github/workflows/fork-sync-action.yml b/.github/workflows/fork-sync-action.yml new file mode 100644 index 000000000000..69e9e93bf54b --- /dev/null +++ b/.github/workflows/fork-sync-action.yml @@ -0,0 +1,20 @@ +# This Workflow is not supposed to run in the paritytech/polkadot-sdk repo. +# This Workflow is supposed to run only in the forks of the repo, +# paritytech-release/polkadot-sdk specifically, +# to automatically maintain the critical fork synced with the upstream. +# This Workflow should be always disabled in the paritytech/polkadot-sdk repo. + +name: Sync the forked repo with the upstream +on: + schedule: + - cron: "0 0/4 * * *" + workflow_dispatch: + +jobs: + job_sync_branches: + uses: paritytech-release/sync-workflows/.github/workflows/sync-with-upstream.yml@latest + with: + fork_writer_app_id: ${{ vars.UPSTREAM_CONTENT_SYNC_APP_ID}} + fork_owner: ${{ vars.RELEASE_ORG}} + secrets: + fork_writer_app_key: ${{ secrets.UPSTREAM_CONTENT_SYNC_APP_KEY }} From 11fdff18e8cddcdee90b0e2c3e35a2b0124de4de Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Tue, 6 Aug 2024 17:12:47 +0200 Subject: [PATCH 064/382] [pallet_contracts] Increase the weight of the deposit_event host function to limit the memory used by events. (#4973) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR updates the weight of the `deposit_event` host function by adding a fixed ref_time of 60,000 picoseconds per byte. Given a block time of 2 seconds and this specified ref_time, the total allocation size is 32MB. --------- Co-authored-by: Alexander Theißen --- prdoc/pr_4973.prdoc | 15 +++++ substrate/frame/contracts/src/lib.rs | 63 ++++++++++++++++++- substrate/frame/contracts/src/schedule.rs | 10 +++ substrate/frame/contracts/src/wasm/runtime.rs | 8 ++- 4 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 prdoc/pr_4973.prdoc diff --git a/prdoc/pr_4973.prdoc b/prdoc/pr_4973.prdoc new file mode 100644 index 000000000000..20b8c94dd8a9 --- /dev/null +++ b/prdoc/pr_4973.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "[pallet_contracts] Increase the weight of the deposit_event host function to limit the memory used by events." + +doc: + - audience: Runtime User + description: | + This PR updates the weight of the deposit_event host function by adding + a fixed ref_time of 60,000 picoseconds per byte. Given a block time of 2 seconds + and this specified ref_time, the total allocation size is 32MB. + +crates: + - name: pallet-contracts + bump: major diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs index 093adc07ab48..7bb5b46cf527 100644 --- a/substrate/frame/contracts/src/lib.rs +++ b/substrate/frame/contracts/src/lib.rs @@ -114,7 +114,7 @@ use crate::{ }, gas::GasMeter, storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager}, - wasm::{CodeInfo, WasmBlob}, + wasm::{CodeInfo, RuntimeCosts, WasmBlob}, }; use codec::{Codec, Decode, Encode, HasCompact, MaxEncodedLen}; use core::fmt::Debug; @@ -670,7 +670,66 @@ pub mod pallet { "Debug buffer should have minimum size of {} (current setting is {})", MIN_DEBUG_BUF_SIZE, T::MaxDebugBufferLen::get(), - ) + ); + + // Validators are configured to be able to use more memory than block builders. This is + // because in addition to `max_runtime_mem` they need to hold additional data in + // memory: PoV in multiple copies (1x encoded + 2x decoded) and all storage which + // includes emitted events. The assumption is that storage/events size + // can be a maximum of half of the validator runtime memory - max_runtime_mem. + let max_block_ref_time = T::BlockWeights::get() + .get(DispatchClass::Normal) + .max_total + .unwrap_or_else(|| T::BlockWeights::get().max_block) + .ref_time(); + let max_payload_size = T::Schedule::get().limits.payload_len; + let max_key_size = + Key::::try_from_var(alloc::vec![0u8; T::MaxStorageKeyLen::get() as usize]) + .expect("Key of maximal size shall be created") + .hash() + .len() as u32; + + // We can use storage to store items using the available block ref_time with the + // `set_storage` host function. + let max_storage_size: u32 = ((max_block_ref_time / + (>::weight(&RuntimeCosts::SetStorage { + new_bytes: max_payload_size, + old_bytes: 0, + }) + .ref_time())) + .saturating_mul(max_payload_size.saturating_add(max_key_size) as u64)) + .try_into() + .expect("Storage size too big"); + + let max_validator_runtime_mem: u32 = T::Schedule::get().limits.validator_runtime_memory; + let storage_size_limit = max_validator_runtime_mem.saturating_sub(max_runtime_mem) / 2; + + assert!( + max_storage_size < storage_size_limit, + "Maximal storage size {} exceeds the storage limit {}", + max_storage_size, + storage_size_limit + ); + + // We can use storage to store events using the available block ref_time with the + // `deposit_event` host function. The overhead of stored events, which is around 100B, + // is not taken into account to simplify calculations, as it does not change much. + let max_events_size: u32 = ((max_block_ref_time / + (>::weight(&RuntimeCosts::DepositEvent { + num_topic: 0, + len: max_payload_size, + }) + .ref_time())) + .saturating_mul(max_payload_size as u64)) + .try_into() + .expect("Events size too big"); + + assert!( + max_events_size < storage_size_limit, + "Maximal events size {} exceeds the events limit {}", + max_events_size, + storage_size_limit + ); } } diff --git a/substrate/frame/contracts/src/schedule.rs b/substrate/frame/contracts/src/schedule.rs index 60c9520677eb..80b8c54b1e1d 100644 --- a/substrate/frame/contracts/src/schedule.rs +++ b/substrate/frame/contracts/src/schedule.rs @@ -89,6 +89,14 @@ pub struct Limits { /// The maximum node runtime memory. This is for integrity checks only and does not affect the /// real setting. pub runtime_memory: u32, + + /// The maximum validator node runtime memory. This is for integrity checks only and does not + /// affect the real setting. + pub validator_runtime_memory: u32, + + /// The additional ref_time added to the `deposit_event` host function call per event data + /// byte. + pub event_ref_time: u64, } impl Limits { @@ -121,6 +129,8 @@ impl Default for Limits { subject_len: 32, payload_len: 16 * 1024, runtime_memory: 1024 * 1024 * 128, + validator_runtime_memory: 1024 * 1024 * 512, + event_ref_time: 60_000, } } } diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index fee127f65852..984e5712ae06 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -329,7 +329,13 @@ impl Token for RuntimeCosts { WeightToFee => T::WeightInfo::seal_weight_to_fee(), Terminate(locked_dependencies) => T::WeightInfo::seal_terminate(locked_dependencies), Random => T::WeightInfo::seal_random(), - DepositEvent { num_topic, len } => T::WeightInfo::seal_deposit_event(num_topic, len), + // Given a 2-second block time and hardcoding a `ref_time` of 60,000 picoseconds per + // byte (event_ref_time), the max allocation size is 32MB per block. + DepositEvent { num_topic, len } => T::WeightInfo::seal_deposit_event(num_topic, len) + .saturating_add(Weight::from_parts( + T::Schedule::get().limits.event_ref_time.saturating_mul(len.into()), + 0, + )), DebugMessage(len) => T::WeightInfo::seal_debug_message(len), SetStorage { new_bytes, old_bytes } => cost_storage!(write, seal_set_storage, new_bytes, old_bytes), From 291c082cbbb0c838c886f38040e54424c55d9618 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 6 Aug 2024 20:04:21 +0200 Subject: [PATCH 065/382] Improve Pallet UI doc test (#5264) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test currently failing, therefore improving to include a file from the same crate to not trip up the caching. R0 silent since this is only modifying unpublished crates. --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Dónal Murray --- .../frame/support/test/example-pallet-doc.md | 1 + .../frame/support/test/example-readme.md | 1 + substrate/frame/support/test/tests/pallet.rs | 19 +++++++++++-------- 3 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 substrate/frame/support/test/example-pallet-doc.md create mode 100644 substrate/frame/support/test/example-readme.md diff --git a/substrate/frame/support/test/example-pallet-doc.md b/substrate/frame/support/test/example-pallet-doc.md new file mode 100644 index 000000000000..3935e7e8eaed --- /dev/null +++ b/substrate/frame/support/test/example-pallet-doc.md @@ -0,0 +1 @@ +This is the best pallet diff --git a/substrate/frame/support/test/example-readme.md b/substrate/frame/support/test/example-readme.md new file mode 100644 index 000000000000..fdee294df9dd --- /dev/null +++ b/substrate/frame/support/test/example-readme.md @@ -0,0 +1 @@ +Very important information :D diff --git a/substrate/frame/support/test/tests/pallet.rs b/substrate/frame/support/test/tests/pallet.rs index 6f8af949cc31..eed8a22e8e79 100644 --- a/substrate/frame/support/test/tests/pallet.rs +++ b/substrate/frame/support/test/tests/pallet.rs @@ -114,8 +114,8 @@ impl SomeAssociation2 for u64 { #[frame_support::pallet] /// Pallet documentation // Comments should not be included in the pallet documentation -#[pallet_doc("../../README.md")] -#[doc = include_str!("../../README.md")] +#[pallet_doc("../example-pallet-doc.md")] +#[doc = include_str!("../example-readme.md")] pub mod pallet { use super::*; use frame_support::pallet_prelude::*; @@ -1408,8 +1408,9 @@ fn metadata() { use codec::Decode; use frame_metadata::{v15::*, *}; - let readme = "Support code for the runtime.\n\nLicense: Apache-2.0\n"; - let expected_pallet_doc = vec![" Pallet documentation", readme, readme]; + let readme = "Very important information :D\n"; + let pallet_doc = "This is the best pallet\n"; + let expected_pallet_doc = vec![" Pallet documentation", readme, pallet_doc]; let pallets = vec![ PalletMetadata { @@ -1911,8 +1912,9 @@ fn metadata_ir_pallet_runtime_docs() { .find(|pallet| pallet.name == "Example") .expect("Pallet should be present"); - let readme = "Support code for the runtime.\n\nLicense: Apache-2.0\n"; - let expected = vec![" Pallet documentation", readme, readme]; + let readme = "Very important information :D\n"; + let pallet_doc = "This is the best pallet\n"; + let expected = vec![" Pallet documentation", readme, pallet_doc]; assert_eq!(pallet.docs, expected); } @@ -1941,8 +1943,9 @@ fn extrinsic_metadata_ir_types() { #[test] fn test_pallet_runtime_docs() { let docs = crate::pallet::Pallet::::pallet_documentation_metadata(); - let readme = "Support code for the runtime.\n\nLicense: Apache-2.0\n"; - let expected = vec![" Pallet documentation", readme, readme]; + let readme = "Very important information :D\n"; + let pallet_doc = "This is the best pallet\n"; + let expected = vec![" Pallet documentation", readme, pallet_doc]; assert_eq!(docs, expected); } From 6db203822418eee666f44ccaed3cd96fdfa27351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 7 Aug 2024 02:11:55 -0500 Subject: [PATCH 066/382] [Assets] Call implementation for `transfer_all` (#4527) Closes #4517 Polkadot address: 12gMhxHw8QjEwLQvnqsmMVY1z5gFa54vND74aMUbhhwN6mJR --------- Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --- .../src/weights/pallet_assets_foreign.rs | 10 ++++ .../src/weights/pallet_assets_local.rs | 10 ++++ .../src/weights/pallet_assets_pool.rs | 10 ++++ .../src/weights/pallet_assets_foreign.rs | 10 ++++ .../src/weights/pallet_assets_local.rs | 10 ++++ .../src/weights/pallet_assets_pool.rs | 10 ++++ prdoc/pr_4527.prdoc | 21 ++++++++ substrate/frame/assets/src/benchmarking.rs | 13 ++++- substrate/frame/assets/src/lib.rs | 49 ++++++++++++++++++- substrate/frame/assets/src/tests.rs | 43 ++++++++++++++++ substrate/frame/assets/src/weights.rs | 21 ++++++++ 11 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_4527.prdoc diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_assets_foreign.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_assets_foreign.rs index 5148edb0ee9e..c76c1137335a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_assets_foreign.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_assets_foreign.rs @@ -531,4 +531,14 @@ impl pallet_assets::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } + + fn transfer_all() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 46_573_000 picoseconds. + Weight::from_parts(47_385_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_assets_local.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_assets_local.rs index 4ee235830aed..cf4f60042bc6 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_assets_local.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_assets_local.rs @@ -528,4 +528,14 @@ impl pallet_assets::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } + + fn transfer_all() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 46_573_000 picoseconds. + Weight::from_parts(47_385_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_assets_pool.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_assets_pool.rs index df7ad2c63386..2cd85de00989 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_assets_pool.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_assets_pool.rs @@ -528,4 +528,14 @@ impl pallet_assets::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } + + fn transfer_all() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 46_573_000 picoseconds. + Weight::from_parts(47_385_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_assets_foreign.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_assets_foreign.rs index 52ba2fd6c40f..2692de9aeb50 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_assets_foreign.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_assets_foreign.rs @@ -537,4 +537,14 @@ impl pallet_assets::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } + + fn transfer_all() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 46_573_000 picoseconds. + Weight::from_parts(47_385_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_assets_local.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_assets_local.rs index e78366b91cbe..d2e12549a45c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_assets_local.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_assets_local.rs @@ -535,4 +535,14 @@ impl pallet_assets::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } + + fn transfer_all() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 46_573_000 picoseconds. + Weight::from_parts(47_385_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_assets_pool.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_assets_pool.rs index 65cae81069c4..8368f6e583cc 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_assets_pool.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_assets_pool.rs @@ -529,4 +529,14 @@ impl pallet_assets::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } + + fn transfer_all() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 46_573_000 picoseconds. + Weight::from_parts(47_385_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } diff --git a/prdoc/pr_4527.prdoc b/prdoc/pr_4527.prdoc new file mode 100644 index 000000000000..12056f87575b --- /dev/null +++ b/prdoc/pr_4527.prdoc @@ -0,0 +1,21 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Call implementation for `transfer_all` + +doc: + - audience: Runtime Dev + description: | + This PR introduces the `transfer_all` call for `pallet-assets`. + The parameters are analog to the same call in `pallet-balances`. + This change is expected to be backwards-compatible. + This change requires running benchmarkings to set accurate weights for + the call. + +crates: + - name: pallet-assets + bump: major + - name: asset-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor diff --git a/substrate/frame/assets/src/benchmarking.rs b/substrate/frame/assets/src/benchmarking.rs index 97cc04174a0c..8988323c1985 100644 --- a/substrate/frame/assets/src/benchmarking.rs +++ b/substrate/frame/assets/src/benchmarking.rs @@ -31,6 +31,7 @@ use sp_runtime::traits::Bounded; use crate::Pallet as Assets; const SEED: u32 = 0; +const MIN_BALANCE: u32 = 1; fn default_asset_id, I: 'static>() -> T::AssetIdParameter { T::BenchmarkHelper::create_asset_id_parameter(0) @@ -48,7 +49,7 @@ fn create_default_asset, I: 'static>( asset_id.clone(), caller_lookup.clone(), is_sufficient, - 1u32.into(), + MIN_BALANCE.into(), ) .is_ok()); (asset_id, caller, caller_lookup) @@ -553,5 +554,15 @@ benchmarks_instance_pallet! { assert_last_event::(Event::Blocked { asset_id: asset_id.into(), who: caller }.into()); } + transfer_all { + let amount = T::Balance::from(2 * MIN_BALANCE); + let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, amount); + let target: T::AccountId = account("target", 0, SEED); + let target_lookup = T::Lookup::unlookup(target.clone()); + }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), target_lookup, false) + verify { + assert_last_event::(Event::Transferred { asset_id: asset_id.into(), from: caller, to: target, amount }.into()); + } + impl_benchmark_test_suite!(Assets, crate::mock::new_test_ext(), crate::mock::Test) } diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index b9b5b2388dfb..e9b9a7b1e3cc 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -183,7 +183,11 @@ use frame_support::{ pallet_prelude::DispatchResultWithPostInfo, storage::KeyPrefixIterator, traits::{ - tokens::{fungibles, DepositConsequence, WithdrawConsequence}, + tokens::{ + fungibles, DepositConsequence, Fortitude, + Preservation::{Expendable, Preserve}, + WithdrawConsequence, + }, BalanceStatus::Reserved, Currency, EnsureOriginWithArg, Incrementable, ReservableCurrency, StoredMap, }, @@ -1753,6 +1757,49 @@ pub mod pallet { Self::deposit_event(Event::::Blocked { asset_id: id, who }); Ok(()) } + + /// Transfer the entire transferable balance from the caller asset account. + /// + /// NOTE: This function only attempts to transfer _transferable_ balances. This means that + /// any held, frozen, or minimum balance (when `keep_alive` is `true`), will not be + /// transferred by this function. To ensure that this function results in a killed account, + /// you might need to prepare the account by removing any reference counters, storage + /// deposits, etc... + /// + /// The dispatch origin of this call must be Signed. + /// + /// - `id`: The identifier of the asset for the account holding a deposit. + /// - `dest`: The recipient of the transfer. + /// - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all + /// of the funds the asset account has, causing the sender asset account to be killed + /// (false), or transfer everything except at least the minimum balance, which will + /// guarantee to keep the sender asset account alive (true). + #[pallet::call_index(32)] + #[pallet::weight(T::WeightInfo::refund_other())] + pub fn transfer_all( + origin: OriginFor, + id: T::AssetIdParameter, + dest: AccountIdLookupOf, + keep_alive: bool, + ) -> DispatchResult { + let transactor = ensure_signed(origin)?; + let keep_alive = if keep_alive { Preserve } else { Expendable }; + let reducible_balance = >::reducible_balance( + id.clone().into(), + &transactor, + keep_alive, + Fortitude::Polite, + ); + let dest = T::Lookup::lookup(dest)?; + >::transfer( + id.into(), + &transactor, + &dest, + reducible_balance, + keep_alive, + )?; + Ok(()) + } } /// Implements [`AccountTouch`] trait. diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index c751fbdcaf1b..af605c5a3c64 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -799,6 +799,49 @@ fn transferring_to_blocked_account_should_not_work() { }); } +#[test] +fn transfer_all_works_1() { + new_test_ext().execute_with(|| { + // setup + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 0, true, 100)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 1, 200)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 2, 100)); + // transfer all and allow death + assert_ok!(Assets::transfer_all(Some(1).into(), 0, 2, false)); + assert_eq!(Assets::balance(0, &1), 0); + assert_eq!(Assets::balance(0, &2), 300); + }); +} + +#[test] +fn transfer_all_works_2() { + new_test_ext().execute_with(|| { + // setup + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 0, true, 100)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 1, 200)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 2, 100)); + // transfer all and allow death + assert_ok!(Assets::transfer_all(Some(1).into(), 0, 2, true)); + assert_eq!(Assets::balance(0, &1), 100); + assert_eq!(Assets::balance(0, &2), 200); + }); +} + +#[test] +fn transfer_all_works_3() { + new_test_ext().execute_with(|| { + // setup + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 0, true, 100)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 1, 210)); + set_frozen_balance(0, 1, 10); + assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 2, 100)); + // transfer all and allow death w/ frozen + assert_ok!(Assets::transfer_all(Some(1).into(), 0, 2, false)); + assert_eq!(Assets::balance(0, &1), 110); + assert_eq!(Assets::balance(0, &2), 200); + }); +} + #[test] fn origin_guards_should_work() { new_test_ext().execute_with(|| { diff --git a/substrate/frame/assets/src/weights.rs b/substrate/frame/assets/src/weights.rs index 7886cd364d51..57f7e951b73c 100644 --- a/substrate/frame/assets/src/weights.rs +++ b/substrate/frame/assets/src/weights.rs @@ -83,6 +83,7 @@ pub trait WeightInfo { fn refund() -> Weight; fn refund_other() -> Weight; fn block() -> Weight; + fn transfer_all() -> Weight; } /// Weights for `pallet_assets` using the Substrate node and recommended hardware. @@ -530,6 +531,16 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + + fn transfer_all() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 46_573_000 picoseconds. + Weight::from_parts(47_385_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } // For backwards compatibility and tests. @@ -976,4 +987,14 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + + fn transfer_all() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 46_573_000 picoseconds. + Weight::from_parts(47_385_000, 3593) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } From dd48544a573dd02da2082cec1dda7ce735e2e719 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Wed, 7 Aug 2024 09:53:18 +0200 Subject: [PATCH 067/382] Add `ProofSizeExt` to benchmarks (#5257) I propose to have `ProofSizeExt` available during benchmarking so we can improve the accuracy for extensions using it. Another thing we could do is to also enable recording for the timing benchmark here: https://github.com/paritytech/polkadot-sdk/blob/035211d707d0a74a2a768fd658160721f09d5b44/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs#L232 Parachains will need to have recording enabled during import for reclaim, so we could enable it here and provide a flag `--disable-proof-recording` for scenarios where one does not want it. Happy to hear opinions about this. --- prdoc/pr_5257.prdoc | 20 ++++++++++++++ substrate/client/db/src/bench.rs | 5 ++++ .../benchmarking-cli/src/pallet/command.rs | 27 ++++++++++++------- .../frame/benchmarking-cli/src/pallet/mod.rs | 9 +++++++ 4 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 prdoc/pr_5257.prdoc diff --git a/prdoc/pr_5257.prdoc b/prdoc/pr_5257.prdoc new file mode 100644 index 000000000000..7a4cff671af0 --- /dev/null +++ b/prdoc/pr_5257.prdoc @@ -0,0 +1,20 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Enable proof-recording in benchmarking + +doc: + - audience: Runtime Dev + description: | + We now enable proof recording in the timing benchmarks. This affects the standalone `frame-omni-bencher` as well + as the integrated benchmarking commands of the node. For parachains on recent versions of polkadot-sdk this is the + correct default setting, since PoV-reclaim requires proof recording to be enabled on block import. + This comes with a slight increase in timing weight due to the additional overhead. Relay- or solo-chains + which do not need proof recording can restore the old behaviour by passing `--disable-proof-recording` to the + benchmarking command. + + In addition, the `ProofSizeExt` extension is available during benchmarking. + +crates: + - name: frame-benchmarking-cli + bump: minor \ No newline at end of file diff --git a/substrate/client/db/src/bench.rs b/substrate/client/db/src/bench.rs index 32503cf63c0a..3d590bd2bb76 100644 --- a/substrate/client/db/src/bench.rs +++ b/substrate/client/db/src/bench.rs @@ -179,6 +179,11 @@ impl BenchmarkingState { Ok(state) } + /// Get the proof recorder for this state + pub fn recorder(&self) -> Option> { + self.proof_recorder.clone() + } + fn reopen(&self) -> Result<(), String> { *self.state.borrow_mut() = None; let db = match self.db.take() { diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs index 305a9b960b98..cfbbab4df7ca 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -37,12 +37,14 @@ use sp_core::{ OffchainDbExt, OffchainWorkerExt, TransactionPoolExt, }, traits::{CallContext, CodeExecutor, ReadRuntimeVersionExt, WrappedRuntimeCode}, + Hasher, }; use sp_externalities::Extensions; use sp_genesis_builder::{PresetId, Result as GenesisBuildResult}; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::traits::Hash; use sp_state_machine::{OverlayedChanges, StateMachine}; +use sp_trie::{proof_size_extension::ProofSizeExt, recorder::Recorder}; use sp_wasm_interface::HostFunctions; use std::{ borrow::Cow, @@ -225,11 +227,12 @@ impl PalletCmd { // Enable storage tracking true, )?; + let state_without_tracking = BenchmarkingState::::new( genesis_storage, cache_size, - // Do not record proof size - false, + // Proof recording depends on CLI settings + !self.disable_proof_recording, // Do not enable storage tracking false, )?; @@ -263,7 +266,7 @@ impl PalletCmd { &executor, "Benchmark_benchmark_metadata", &(self.extra).encode(), - &mut Self::build_extensions(executor.clone()), + &mut Self::build_extensions(executor.clone(), state.recorder()), &runtime_code, CallContext::Offchain, ), @@ -365,7 +368,7 @@ impl PalletCmd { 1, // no need to do internal repeats ) .encode(), - &mut Self::build_extensions(executor.clone()), + &mut Self::build_extensions(executor.clone(), state.recorder()), &runtime_code, CallContext::Offchain, ), @@ -406,7 +409,7 @@ impl PalletCmd { self.repeat, ) .encode(), - &mut Self::build_extensions(executor.clone()), + &mut Self::build_extensions(executor.clone(), state.recorder()), &runtime_code, CallContext::Offchain, ), @@ -449,7 +452,7 @@ impl PalletCmd { self.repeat, ) .encode(), - &mut Self::build_extensions(executor.clone()), + &mut Self::build_extensions(executor.clone(), state.recorder()), &runtime_code, CallContext::Offchain, ), @@ -626,7 +629,7 @@ impl PalletCmd { &executor, "GenesisBuilder_get_preset", &None::.encode(), // Use the default preset - &mut Self::build_extensions(executor.clone()), + &mut Self::build_extensions(executor.clone(), state.recorder()), &runtime_code, CallContext::Offchain, ), @@ -647,7 +650,7 @@ impl PalletCmd { &executor, "GenesisBuilder_get_preset", &Some::(GENESIS_PRESET.into()).encode(), // Use the default preset - &mut Self::build_extensions(executor.clone()), + &mut Self::build_extensions(executor.clone(), state.recorder()), &runtime_code, CallContext::Offchain, ), @@ -707,7 +710,10 @@ impl PalletCmd { } /// Build the extension that are available for pallet benchmarks. - fn build_extensions(exe: E) -> Extensions { + fn build_extensions( + exe: E, + maybe_recorder: Option>, + ) -> Extensions { let mut extensions = Extensions::default(); let (offchain, _) = TestOffchainExt::new(); let (pool, _) = TestTransactionPoolExt::new(); @@ -717,6 +723,9 @@ impl PalletCmd { extensions.register(OffchainDbExt::new(offchain)); extensions.register(TransactionPoolExt::new(pool)); extensions.register(ReadRuntimeVersionExt::new(exe)); + if let Some(recorder) = maybe_recorder { + extensions.register(ProofSizeExt::new(recorder)); + } extensions } diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs index d05b52f1ac87..ebf737be1dbf 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs @@ -243,4 +243,13 @@ pub struct PalletCmd { /// use-cases, this option reduces the noise. #[arg(long)] quiet: bool, + + /// Do not enable proof recording during time benchmarking. + /// + /// By default, proof recording is enabled during benchmark execution. This can slightly + /// inflate the resulting time weights. For parachains using PoV-reclaim, this is typically the + /// correct setting. Chains that ignore the proof size dimension of weight (e.g. relay chain, + /// solo-chains) can disable proof recording to get more accurate results. + #[arg(long)] + disable_proof_recording: bool, } From 7a6e91c413b0bf1ddf86a9e58cf1be06c837bc1c Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:28:38 +0300 Subject: [PATCH 068/382] cli/net_params: Warn on empty public-addr when starting a validator node (#5240) This PR shows a warning when the `--public-addr` is not provided for validators. In the future, we'll transform this warning into a hard failure. Validators are encouraged to provide this parameter for better availability over the network. cc @paritytech/networking --------- Signed-off-by: Alexandru Vasile --- prdoc/pr_5240.prdoc | 12 ++++++++++++ substrate/client/cli/src/config.rs | 18 ++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_5240.prdoc diff --git a/prdoc/pr_5240.prdoc b/prdoc/pr_5240.prdoc new file mode 100644 index 000000000000..3622a6ada76b --- /dev/null +++ b/prdoc/pr_5240.prdoc @@ -0,0 +1,12 @@ +title: Warn on empty public-addr when starting a validator node + +doc: + - audience: Node Operator + description: | + This PR shows a warning when the `--public-addr` CLI parameter is missing for validators. + In the future, we'll transform this warning into a hard failure. + Validators are encouraged to provide this parameter for better availability over the network. + +crates: + - name: sc-cli + bump: patch diff --git a/substrate/client/cli/src/config.rs b/substrate/client/cli/src/config.rs index 283148a6d6ad..406d1fb264dd 100644 --- a/substrate/client/cli/src/config.rs +++ b/substrate/client/cli/src/config.rs @@ -172,7 +172,7 @@ pub trait CliConfiguration: Sized { node_key: NodeKeyConfig, default_listen_port: u16, ) -> Result { - Ok(if let Some(network_params) = self.network_params() { + let network_config = if let Some(network_params) = self.network_params() { network_params.network_config( chain_spec, is_dev, @@ -185,7 +185,13 @@ pub trait CliConfiguration: Sized { ) } else { NetworkConfiguration::new(node_name, client_id, node_key, Some(net_config_dir)) - }) + }; + + // TODO: Return error here in the next release: + // https://github.com/paritytech/polkadot-sdk/issues/5266 + // if is_validator && network_config.public_addresses.is_empty() {} + + Ok(network_config) } /// Get the keystore configuration. @@ -638,6 +644,14 @@ pub trait CliConfiguration: Sized { logger.init()?; + if config.role.is_authority() && config.network.public_addresses.is_empty() { + warn!( + "WARNING: No public address specified, validator node may not be reachable. + Consider setting `--public-addr` to the public IP address of this node. + This will become a hard requirement in future versions." + ); + } + match fdlimit::raise_fd_limit() { Ok(fdlimit::Outcome::LimitRaised { to, .. }) => if to < RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT { From 34e64c726fdb6401befcadc5348133db12ea5c29 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Wed, 7 Aug 2024 11:24:03 +0200 Subject: [PATCH 069/382] Update the wishlist leaderboard script to handle PRs (#5256) Addresses https://github.com/paritytech/polkadot-sdk/pull/5085#issuecomment-2265725858 Luckily, in the rest of the script, github API allows (or forces?) us to read the state of PRs the same way as we read the state of issues, so it works without any more changes. --- .github/scripts/update-wishlist-leaderboard.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/update-wishlist-leaderboard.py b/.github/scripts/update-wishlist-leaderboard.py index caa30bec846d..82d108514484 100644 --- a/.github/scripts/update-wishlist-leaderboard.py +++ b/.github/scripts/update-wishlist-leaderboard.py @@ -7,7 +7,7 @@ # Regex pattern to match wish format: wish_pattern = re.compile( - r"I wish for:? (https://github\.com/([a-zA-Z0-9_.-]+)/([a-zA-Z0-9_.-]+)/issues/(\d+))" + r"I wish for:? (https://github\.com/([a-zA-Z0-9_.-]+)/([a-zA-Z0-9_.-]+)/(issues|pull)/(\d+))" ) wishlist_issue = g.get_repo(os.getenv("WISHLIST_REPOSITORY")).get_issue( @@ -28,7 +28,7 @@ matches = wish_pattern.findall(updated_body) for match in matches: - url, org, repo_name, issue_id = match + url, org, repo_name, _, issue_id = match issue_key = (url, org, repo_name, issue_id) if issue_key not in wishes: wishes[issue_key] = [] From cd1a29f7aa859351daaaefb16e13d301cc4fb977 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Wed, 7 Aug 2024 12:27:12 +0300 Subject: [PATCH 070/382] Export more from sc-service (#5250) Follow-up to https://github.com/paritytech/polkadot-sdk/pull/4457, looks like more things were missing --------- Co-authored-by: Niklas Adolfsson --- prdoc/pr_5250.prdoc | 12 ++++++++++++ substrate/client/service/src/builder.rs | 2 +- substrate/client/service/src/lib.rs | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 prdoc/pr_5250.prdoc diff --git a/prdoc/pr_5250.prdoc b/prdoc/pr_5250.prdoc new file mode 100644 index 000000000000..2cac6b2383e3 --- /dev/null +++ b/prdoc/pr_5250.prdoc @@ -0,0 +1,12 @@ +title: Export `MetricsService` and add public constructor to `RpcHandlers` + +doc: + - audience: Node Dev + description: | + `sc-service` was missing just a couple of things in public API in order to make it possible to recreate its + `spawn_tasks`, specifically `MetricsService` struct and `RpcHandlers` didn't have public constructor, which were + both finally addressed. + +crates: + - name: sc-service + bump: patch diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index ebe3f553f7c2..d7fb7918481c 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -509,7 +509,7 @@ where }; let rpc = start_rpc_servers(&config, gen_rpc_module, rpc_id_provider)?; - let rpc_handlers = RpcHandlers(Arc::new(gen_rpc_module(sc_rpc::DenyUnsafe::No)?.into())); + let rpc_handlers = RpcHandlers::new(Arc::new(gen_rpc_module(sc_rpc::DenyUnsafe::No)?.into())); // Spawn informant task spawn_handle.spawn( diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index 63be296d1b21..89d563001cd6 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -62,6 +62,7 @@ pub use self::{ }, client::{ClientConfig, LocalCallExecutor}, error::Error, + metrics::MetricsService, }; #[allow(deprecated)] pub use builder::new_native_or_wasm_executor; @@ -101,6 +102,11 @@ const DEFAULT_PROTOCOL_ID: &str = "sup"; pub struct RpcHandlers(Arc>); impl RpcHandlers { + /// Create PRC handlers instance. + pub fn new(inner: Arc>) -> Self { + Self(inner) + } + /// Starts an RPC query. /// /// The query is passed as a string and must be valid JSON-RPC request object. From a986da2de60b0a15a72806c9f9cfae38dbab8f83 Mon Sep 17 00:00:00 2001 From: Lulu Date: Wed, 7 Aug 2024 10:37:29 +0100 Subject: [PATCH 071/382] Run semver check even when no prdoc (#5189) Co-authored-by: Oliver Tale-Yazdi --- .github/workflows/check-semver.yml | 10 +++++----- .github/workflows/publish-check-crates.yml | 2 +- .github/workflows/publish-claim-crates.yml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/check-semver.yml b/.github/workflows/check-semver.yml index 7d9fb68b90bf..d9d918b44a23 100644 --- a/.github/workflows/check-semver.yml +++ b/.github/workflows/check-semver.yml @@ -3,8 +3,6 @@ name: Check semver on: pull_request: types: [opened, synchronize, reopened, ready_for_review] - paths: - - prdoc/*.prdoc workflow_dispatch: concurrency: @@ -24,10 +22,12 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: extra git setup + env: + BASE: ${{ github.event.pull_request.base.sha }} run: | git config --global --add safe.directory '*' - git fetch --no-tags --no-recurse-submodules --depth=1 origin master - git branch old origin/master + git fetch --no-tags --no-recurse-submodules --depth=1 origin $BASE + git branch old $BASE - name: Comment If Backport if: ${{ startsWith(github.event.pull_request.base.ref, 'stable') }} @@ -74,7 +74,7 @@ jobs: - name: install parity-publish # Set the target dir to cache the build. - run: CARGO_TARGET_DIR=./target/ cargo install parity-publish -q + run: CARGO_TARGET_DIR=./target/ cargo install parity-publish@0.8.0 -q - name: check semver run: | diff --git a/.github/workflows/publish-check-crates.yml b/.github/workflows/publish-check-crates.yml index e627987105a4..9f96b92e0ce7 100644 --- a/.github/workflows/publish-check-crates.yml +++ b/.github/workflows/publish-check-crates.yml @@ -20,7 +20,7 @@ jobs: cache-on-failure: true - name: install parity-publish - run: cargo install parity-publish@0.7.0 + run: cargo install parity-publish@0.8.0 - name: parity-publish check run: parity-publish --color always check --allow-unpublished diff --git a/.github/workflows/publish-claim-crates.yml b/.github/workflows/publish-claim-crates.yml index 167a10a8389b..bee709a12076 100644 --- a/.github/workflows/publish-claim-crates.yml +++ b/.github/workflows/publish-claim-crates.yml @@ -18,7 +18,7 @@ jobs: cache-on-failure: true - name: install parity-publish - run: cargo install parity-publish@0.7.0 + run: cargo install parity-publish@0.8.0 - name: parity-publish claim env: From efdc1e9b1615c5502ed63ffc9683d99af6397263 Mon Sep 17 00:00:00 2001 From: Ron Date: Wed, 7 Aug 2024 17:49:21 +0800 Subject: [PATCH 072/382] Snowbridge on Westend (#5074) ### Context Since Rococo is now deprecated, we need another testnet to detect bleeding-edge changes to Substrate, Polkadot, & BEEFY consensus protocols that could brick the bridge. It's the mirror PR of https://github.com/Snowfork/polkadot-sdk/pull/157 which has reviewed by Snowbridge team internally. Synced with @acatangiu about that in channel https://matrix.to/#/!gxqZwOyvhLstCgPJHO:matrix.parity.io/$N0CvTfDSl3cOQLEJeZBh-wlKJUXx7EDHAuNN5HuYHY4?via=matrix.parity.io&via=parity.io&via=matrix.org --------- Co-authored-by: Clara van Staden --- Cargo.lock | 28 ++ .../bridges/bridge-hub-westend/src/genesis.rs | 6 + .../bridges/bridge-hub-westend/Cargo.toml | 15 + .../bridge-hub-westend/src/tests/mod.rs | 6 +- .../src/tests/snowbridge.rs | 307 ++++++++++++++++++ .../assets/asset-hub-westend/Cargo.toml | 5 + .../assets/asset-hub-westend/src/lib.rs | 14 +- .../asset-hub-westend/src/xcm_config.rs | 88 ++++- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 2 + .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 6 +- .../bridge-hub-rococo/src/xcm_config.rs | 4 +- .../bridge-hub-rococo/tests/snowbridge.rs | 1 + .../bridge-hub-rococo/tests/tests.rs | 13 +- .../bridge-hubs/bridge-hub-westend/Cargo.toml | 40 +++ .../src/bridge_to_ethereum_config.rs | 219 +++++++++++++ .../bridge-hubs/bridge-hub-westend/src/lib.rs | 51 ++- .../bridge-hub-westend/src/weights/mod.rs | 5 + .../snowbridge_pallet_ethereum_client.rs | 120 +++++++ .../snowbridge_pallet_inbound_queue.rs | 69 ++++ .../snowbridge_pallet_outbound_queue.rs | 87 +++++ .../src/weights/snowbridge_pallet_system.rs | 256 +++++++++++++++ .../bridge-hub-westend/src/xcm_config.rs | 51 ++- .../bridge-hub-westend/tests/snowbridge.rs | 202 ++++++++++++ .../bridge-hub-westend/tests/tests.rs | 9 +- .../runtimes/constants/src/westend.rs | 16 + cumulus/polkadot-parachain/Cargo.toml | 1 + .../src/chain_spec/bridge_hubs.rs | 4 + cumulus/polkadot-parachain/src/command.rs | 1 + polkadot/node/service/src/chain_spec.rs | 3 +- polkadot/runtime/westend/build.rs | 9 + polkadot/runtime/westend/src/lib.rs | 5 + prdoc/pr_5074.prdoc | 33 ++ 32 files changed, 1636 insertions(+), 40 deletions(-) create mode 100644 cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs create mode 100644 cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs create mode 100644 cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_ethereum_client.rs create mode 100644 cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_inbound_queue.rs create mode 100644 cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_outbound_queue.rs create mode 100644 cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs create mode 100644 cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs create mode 100644 prdoc/pr_5074.prdoc diff --git a/Cargo.lock b/Cargo.lock index 4f41f3ca1eb2..826bb9398ae0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1017,6 +1017,7 @@ dependencies = [ "polkadot-runtime-common", "primitive-types", "scale-info", + "snowbridge-router-primitives", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -1026,6 +1027,7 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", + "sp-std 14.0.0", "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", @@ -2110,6 +2112,7 @@ dependencies = [ "cumulus-primitives-utility", "frame-benchmarking", "frame-executive", + "frame-metadata-hash-extension", "frame-support", "frame-system", "frame-system-benchmarking", @@ -2232,10 +2235,13 @@ dependencies = [ name = "bridge-hub-westend-integration-tests" version = "1.0.0" dependencies = [ + "asset-hub-westend-runtime", + "bridge-hub-westend-runtime", "cumulus-pallet-xcmp-queue", "emulated-integration-tests-common", "frame-support", "hex-literal", + "log", "pallet-asset-conversion", "pallet-assets", "pallet-balances", @@ -2243,10 +2249,20 @@ dependencies = [ "pallet-message-queue", "pallet-xcm", "parachains-common", + "parity-scale-codec", "rococo-westend-system-emulated-network", + "scale-info", + "snowbridge-core", + "snowbridge-pallet-inbound-queue", + "snowbridge-pallet-inbound-queue-fixtures", + "snowbridge-pallet-outbound-queue", + "snowbridge-pallet-system", + "snowbridge-router-primitives", + "sp-core", "sp-runtime", "staging-xcm", "staging-xcm-executor", + "testnet-parachains-constants", ] [[package]] @@ -2279,6 +2295,7 @@ dependencies = [ "cumulus-primitives-utility", "frame-benchmarking", "frame-executive", + "frame-metadata-hash-extension", "frame-support", "frame-system", "frame-system-benchmarking", @@ -2310,6 +2327,17 @@ dependencies = [ "polkadot-runtime-common", "scale-info", "serde", + "snowbridge-beacon-primitives", + "snowbridge-core", + "snowbridge-outbound-queue-runtime-api", + "snowbridge-pallet-ethereum-client", + "snowbridge-pallet-inbound-queue", + "snowbridge-pallet-outbound-queue", + "snowbridge-pallet-system", + "snowbridge-router-primitives", + "snowbridge-runtime-common", + "snowbridge-runtime-test-common", + "snowbridge-system-runtime-api", "sp-api", "sp-block-builder", "sp-consensus-aura", diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs index a160d18d4cf7..f38f385db650 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs @@ -23,6 +23,7 @@ use emulated_integration_tests_common::{ use parachains_common::Balance; pub const PARA_ID: u32 = 1002; +pub const ASSETHUB_PARA_ID: u32 = 1000; pub const ED: Balance = testnet_parachains_constants::westend::currency::EXISTENTIAL_DEPOSIT; pub fn genesis() -> Storage { @@ -65,6 +66,11 @@ pub fn genesis() -> Storage { owner: Some(get_account_id_from_seed::(accounts::BOB)), ..Default::default() }, + ethereum_system: bridge_hub_westend_runtime::EthereumSystemConfig { + para_id: PARA_ID.into(), + asset_hub_para_id: ASSETHUB_PARA_ID.into(), + ..Default::default() + }, ..Default::default() }; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml index 6b83479eaf89..1f2d2c8ece24 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml @@ -12,6 +12,9 @@ workspace = true [dependencies] hex-literal = { workspace = true, default-features = true } +codec = { workspace = true } +log = { workspace = true } +scale-info = { workspace = true } # Substrate frame-support = { workspace = true } @@ -19,6 +22,7 @@ pallet-assets = { workspace = true } pallet-asset-conversion = { workspace = true } pallet-balances = { workspace = true } pallet-message-queue = { workspace = true, default-features = true } +sp-core = { workspace = true } sp-runtime = { workspace = true } # Polkadot @@ -34,3 +38,14 @@ cumulus-pallet-xcmp-queue = { workspace = true } emulated-integration-tests-common = { workspace = true } parachains-common = { workspace = true, default-features = true } rococo-westend-system-emulated-network = { workspace = true } +testnet-parachains-constants = { workspace = true, features = ["westend"] } +asset-hub-westend-runtime = { workspace = true } +bridge-hub-westend-runtime = { workspace = true } + +# Snowbridge +snowbridge-core = { workspace = true } +snowbridge-router-primitives = { workspace = true } +snowbridge-pallet-system = { workspace = true } +snowbridge-pallet-outbound-queue = { workspace = true } +snowbridge-pallet-inbound-queue = { workspace = true } +snowbridge-pallet-inbound-queue-fixtures = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs index 768b647a13fc..87ae9aedd6f1 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs @@ -18,13 +18,9 @@ use crate::imports::*; mod asset_transfers; mod claim_assets; mod send_xcm; +mod snowbridge; mod teleport; -mod snowbridge { - pub const CHAIN_ID: u64 = 11155111; - pub const WETH: [u8; 20] = hex_literal::hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); -} - pub(crate) fn asset_hub_rococo_location() -> Location { Location::new(2, [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())]) } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs new file mode 100644 index 000000000000..b4db9b365f39 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -0,0 +1,307 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use crate::imports::*; +use asset_hub_westend_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; +use bridge_hub_westend_runtime::EthereumInboundQueue; +use codec::{Decode, Encode}; +use frame_support::pallet_prelude::TypeInfo; +use hex_literal::hex; +use snowbridge_core::outbound::OperatingMode; +use snowbridge_router_primitives::inbound::{ + Command, ConvertMessage, Destination, MessageV1, VersionedMessage, +}; +use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; + +const INITIAL_FUND: u128 = 5_000_000_000_000_000_000; +pub const CHAIN_ID: u64 = 11155111; +pub const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); +const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); +const XCM_FEE: u128 = 100_000_000_000; +const WETH_AMOUNT: u128 = 1_000_000_000; + +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum ControlCall { + #[codec(index = 3)] + CreateAgent, + #[codec(index = 4)] + CreateChannel { mode: OperatingMode }, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum SnowbridgeControl { + #[codec(index = 83)] + Control(ControlCall), +} + +/// Tests the registering of a token as an asset on AssetHub. +#[test] +fn register_weth_token_from_ethereum_to_asset_hub() { + // Fund AssetHub sovereign account so that it can pay execution fees. + BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id().into(), INITIAL_FUND); + + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + type Converter = ::MessageConverter; + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE }, + }); + let (xcm, _) = Converter::convert(message).unwrap(); + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { .. }) => {}, + ] + ); + }); +} + +/// Tests the registering of a token as an asset on AssetHub, and then subsequently sending +/// a token from Ethereum to AssetHub. +#[test] +fn send_token_from_ethereum_to_asset_hub() { + let asset_hub_sovereign = BridgeHubWestend::sovereign_account_id_of(Location::new( + 1, + [Parachain(AssetHubWestend::para_id().into())], + )); + // Fund AssetHub sovereign account so it can pay execution fees for the asset transfer + BridgeHubWestend::fund_accounts(vec![(asset_hub_sovereign.clone(), INITIAL_FUND)]); + + // Fund ethereum sovereign on AssetHub + AssetHubWestend::fund_accounts(vec![(AssetHubWestendReceiver::get(), INITIAL_FUND)]); + + let weth_asset_location: Location = + (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); + + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + assert_ok!(::ForeignAssets::force_create( + RuntimeOrigin::root(), + weth_asset_location.clone().try_into().unwrap(), + asset_hub_sovereign.into(), + false, + 1, + )); + + assert!(::ForeignAssets::asset_exists( + weth_asset_location.clone().try_into().unwrap(), + )); + }); + + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + type Converter = ::MessageConverter; + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendToken { + token: WETH.into(), + destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, + amount: WETH_AMOUNT, + fee: XCM_FEE, + }, + }); + let (xcm, _) = Converter::convert(message).unwrap(); + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + + // Check that the message was sent + assert_expected_events!( + BridgeHubWestend, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the token was received and issued as a foreign asset on AssetHub + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, + ] + ); + }); +} + +/// Tests the full cycle of token transfers: +/// - registering a token on AssetHub +/// - sending a token to AssetHub +/// - returning the token to Ethereum +#[test] +fn send_weth_asset_from_asset_hub_to_ethereum() { + let assethub_location = BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()); + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of(assethub_location); + let weth_asset_location: Location = + (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); + + AssetHubWestend::force_default_xcm_version(Some(XCM_VERSION)); + BridgeHubWestend::force_default_xcm_version(Some(XCM_VERSION)); + AssetHubWestend::force_xcm_version( + Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]), + XCM_VERSION, + ); + + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + assert_ok!(::ForeignAssets::force_create( + RuntimeOrigin::root(), + weth_asset_location.clone().try_into().unwrap(), + assethub_sovereign.clone().into(), + false, + 1, + )); + + assert!(::ForeignAssets::asset_exists( + weth_asset_location.clone().try_into().unwrap(), + )); + }); + + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type Converter = ::MessageConverter; + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendToken { + token: WETH.into(), + destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, + amount: WETH_AMOUNT, + fee: XCM_FEE, + }, + }); + let (xcm, _) = Converter::convert(message).unwrap(); + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + + // Check that the send token message was sent using xcm + assert_expected_events!( + BridgeHubWestend, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) =>{},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + + // Check that AssetHub has issued the foreign asset + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, + ] + ); + let assets = vec![Asset { + id: AssetId(Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: CHAIN_ID }), + AccountKey20 { network: None, key: WETH }, + ], + )), + fun: Fungible(WETH_AMOUNT), + }]; + let multi_assets = VersionedAssets::V4(Assets::from(assets)); + + let destination = VersionedLocation::V4(Location::new( + 2, + [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], + )); + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + let free_balance_before = + ::Balances::free_balance( + AssetHubWestendReceiver::get(), + ); + // Send the Weth back to Ethereum + ::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubWestendReceiver::get()), + Box::new(destination), + Box::new(beneficiary), + Box::new(multi_assets), + 0, + Unlimited, + ) + .unwrap(); + let free_balance_after = ::Balances::free_balance( + AssetHubWestendReceiver::get(), + ); + // Assert at least DefaultBridgeHubEthereumBaseFee charged from the sender + let free_balance_diff = free_balance_before - free_balance_after; + assert!(free_balance_diff > DefaultBridgeHubEthereumBaseFee::get()); + }); + + BridgeHubWestend::execute_with(|| { + use bridge_hub_westend_runtime::xcm_config::TreasuryAccount; + type RuntimeEvent = ::RuntimeEvent; + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubWestend, + vec![ + + RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued + {..}) => {}, ] + ); + let events = BridgeHubWestend::events(); + // Check that the local fee was credited to the Snowbridge sovereign account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) + if *who == TreasuryAccount::get().into() && *amount == 5071000000 + )), + "Snowbridge sovereign takes local fee." + ); + // Check that the remote fee was credited to the AssetHub sovereign account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) + if *who == assethub_sovereign && *amount == 2680000000000, + )), + "AssetHub sovereign takes remote fee." + ); + }); +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 6b1bf769ace3..2f244a07e8f1 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -53,6 +53,7 @@ sp-inherents = { workspace = true } sp-offchain = { workspace = true } sp-runtime = { workspace = true } sp-session = { workspace = true } +sp-std = { workspace = true } sp-storage = { workspace = true } sp-transaction-pool = { workspace = true } sp-version = { workspace = true } @@ -93,6 +94,7 @@ bp-asset-hub-rococo = { workspace = true } bp-asset-hub-westend = { workspace = true } bp-bridge-hub-rococo = { workspace = true } bp-bridge-hub-westend = { workspace = true } +snowbridge-router-primitives = { workspace = true } [dev-dependencies] asset-test-utils = { workspace = true, default-features = true } @@ -134,6 +136,7 @@ runtime-benchmarks = [ "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "snowbridge-router-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", @@ -230,6 +233,7 @@ std = [ "polkadot-runtime-common/std", "primitive-types/std", "scale-info/std", + "snowbridge-router-primitives/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", @@ -239,6 +243,7 @@ std = [ "sp-offchain/std", "sp-runtime/std", "sp-session/std", + "sp-std/std", "sp-storage/std", "sp-transaction-pool/std", "sp-version/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 62d3462038c8..201698ecb7f9 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -74,7 +74,9 @@ use sp_runtime::{ #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use testnet_parachains_constants::westend::{consensus::*, currency::*, fee::WeightToFee, time::*}; +use testnet_parachains_constants::westend::{ + consensus::*, currency::*, fee::WeightToFee, snowbridge::EthereumNetwork, time::*, +}; use xcm_config::{ ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, PoolAssetsConvertedConcreteId, TrustBackedAssetsConvertedConcreteId, @@ -85,7 +87,10 @@ use xcm_config::{ #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; -use assets_common::{foreign_creators::ForeignCreators, matching::FromSiblingParachain}; +use assets_common::{ + foreign_creators::ForeignCreators, + matching::{FromNetwork, FromSiblingParachain}, +}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use xcm::{ latest::prelude::AssetId, @@ -410,7 +415,10 @@ impl pallet_assets::Config for Runtime { type AssetIdParameter = xcm::v3::Location; type Currency = Balances; type CreateOrigin = ForeignCreators< - FromSiblingParachain, xcm::v3::Location>, + ( + FromSiblingParachain, xcm::v3::Location>, + FromNetwork, + ), ForeignCreatorsSovereignAccountOf, AccountId, xcm::v3::Location, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 5ecfce18b6da..bf45e146e334 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -42,6 +42,7 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; +use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor; use sp_runtime::traits::{AccountIdConversion, ConvertInto}; use xcm::latest::prelude::*; use xcm_builder::{ @@ -52,9 +53,10 @@ use xcm_builder::{ GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, - StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + SignedAccountId32AsNative, SignedToAccountId32, SovereignPaidRemoteExporter, + SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -99,6 +101,9 @@ pub type LocationToAccountId = ( // Different global consensus parachain sovereign account. // (Used for over-bridge transfers and reserve processing) GlobalConsensusParachainConvertsFor, + // Ethereum contract sovereign account. + // (Used to get convert ethereum contract locations to sovereign account) + GlobalConsensusEthereumConvertsFor, ); /// Means for transacting the native currency on this chain. @@ -358,7 +363,10 @@ impl xcm_executor::Config for XcmConfig { // as reserve locations (we trust the Bridge Hub to relay the message that a reserve is being // held). On Westend Asset Hub, we allow Rococo Asset Hub to act as reserve for any asset native // to the Rococo or Ethereum ecosystems. - type IsReserve = (bridging::to_rococo::RococoOrEthereumAssetFromAssetHubRococo,); + type IsReserve = ( + bridging::to_rococo::RococoOrEthereumAssetFromAssetHubRococo, + bridging::to_ethereum::IsTrustedBridgedReserveLocationForForeignAsset, + ); type IsTeleporter = TrustedTeleporters; type UniversalLocation = UniversalLocation; type Barrier = Barrier; @@ -431,7 +439,8 @@ impl xcm_executor::Config for XcmConfig { SendXcmFeeToAccount, >; type MessageExporter = (); - type UniversalAliases = (bridging::to_rococo::UniversalAliases,); + type UniversalAliases = + (bridging::to_rococo::UniversalAliases, bridging::to_ethereum::UniversalAliases); type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; @@ -463,6 +472,13 @@ pub type XcmRouter = WithUniqueTopic<( // Router which wraps and sends xcm to BridgeHub to be delivered to the Rococo // GlobalConsensus ToRococoXcmRouter, + // Router which wraps and sends xcm to BridgeHub to be delivered to the Ethereum + // GlobalConsensus + SovereignPaidRemoteExporter< + bridging::to_ethereum::EthereumNetworkExportTable, + XcmpQueue, + UniversalLocation, + >, )>; impl pallet_xcm::Config for Runtime { @@ -504,6 +520,7 @@ pub type ForeignCreatorsSovereignAccountOf = ( SiblingParachainConvertsVia, AccountId32Aliases, ParentIsPreset, + GlobalConsensusEthereumConvertsFor, ); /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. @@ -627,6 +644,67 @@ pub mod bridging { } } + pub mod to_ethereum { + use super::*; + use assets_common::matching::FromNetwork; + use sp_std::collections::btree_set::BTreeSet; + use testnet_parachains_constants::westend::snowbridge::{ + EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX, + }; + + parameter_types! { + /// User fee for ERC20 token transfer back to Ethereum. + /// (initially was calculated by test `OutboundQueue::calculate_fees` - ETH/WND 1/400 and fee_per_gas 20 GWEI = 2200698000000 + *25%) + /// Needs to be more than fee calculated from DefaultFeeConfig FeeConfigRecord in snowbridge:parachain/pallets/outbound-queue/src/lib.rs + /// Polkadot uses 10 decimals, Kusama,Rococo,Westend 12 decimals. + pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; + pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get(); + pub SiblingBridgeHubWithEthereumInboundQueueInstance: Location = Location::new( + 1, + [ + Parachain(SiblingBridgeHubParaId::get()), + PalletInstance(INBOUND_QUEUE_PALLET_INDEX) + ] + ); + + /// Set up exporters configuration. + /// `Option` represents static "base fee" which is used for total delivery fee calculation. + pub BridgeTable: sp_std::vec::Vec = sp_std::vec![ + NetworkExportTableItem::new( + EthereumNetwork::get(), + Some(sp_std::vec![Junctions::Here]), + SiblingBridgeHub::get(), + Some(( + XcmBridgeHubRouterFeeAssetId::get(), + BridgeHubEthereumBaseFee::get(), + ).into()) + ), + ]; + + /// Universal aliases + pub UniversalAliases: BTreeSet<(Location, Junction)> = BTreeSet::from_iter( + sp_std::vec![ + (SiblingBridgeHubWithEthereumInboundQueueInstance::get(), GlobalConsensus(EthereumNetwork::get())), + ] + ); + + pub EthereumBridgeTable: sp_std::vec::Vec = sp_std::vec::Vec::new().into_iter() + .chain(BridgeTable::get()) + .collect(); + } + + pub type EthereumNetworkExportTable = xcm_builder::NetworkExportTable; + + pub type IsTrustedBridgedReserveLocationForForeignAsset = + IsForeignConcreteAsset>; + + impl Contains<(Location, Junction)> for UniversalAliases { + fn contains(alias: &(Location, Junction)) -> bool { + UniversalAliases::get().contains(alias) + } + } + } + /// Benchmarks helper for bridging configuration. #[cfg(feature = "runtime-benchmarks")] pub struct BridgingBenchmarksHelper; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 7a60a3f64840..f3e03aa74300 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -26,6 +26,7 @@ serde = { optional = true, features = ["derive"], workspace = true, default-feat # Substrate frame-benchmarking = { optional = true, workspace = true } frame-executive = { workspace = true } +frame-metadata-hash-extension = { workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } frame-system-benchmarking = { optional = true, workspace = true } @@ -159,6 +160,7 @@ std = [ "cumulus-primitives-utility/std", "frame-benchmarking/std", "frame-executive/std", + "frame-metadata-hash-extension/std", "frame-support/std", "frame-system-benchmarking?/std", "frame-system-rpc-runtime-api/std", diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 02ef0b9c0c72..c8face15627f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -145,6 +145,7 @@ pub type SignedExtra = ( bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages, ), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, + frame_metadata_hash_extension::CheckMetadataHash, ); /// Unchecked extrinsic type as expected by this runtime. @@ -1612,6 +1613,7 @@ mod tests { bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(), ), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + frame_metadata_hash_extension::CheckMetadataHash::new(false), ); // for BridgeHubRococo @@ -1625,9 +1627,9 @@ mod tests { 10, (((), ()), ((), ())), ); - assert_eq!(payload.encode(), bhr_indirect_payload.encode()); + assert_eq!(payload.encode().split_last().unwrap().1, bhr_indirect_payload.encode()); assert_eq!( - payload.additional_signed().unwrap().encode(), + payload.additional_signed().unwrap().encode().split_last().unwrap().1, bhr_indirect_payload.additional_signed().unwrap().encode() ) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 8c077470ba1a..92368b292121 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -397,7 +397,9 @@ impl, FeeHandler: HandleFee> FeeManager fn is_waived(origin: Option<&Location>, fee_reason: FeeReason) -> bool { let Some(loc) = origin else { return false }; if let Export { network, destination: Here } = fee_reason { - return !(network == EthereumNetwork::get()) + if network == EthereumNetwork::get() { + return false + } } WaivedLocations::contains(loc) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs index 5960ab7b5505..c7b5850f9ffe 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs @@ -188,6 +188,7 @@ fn construct_extrinsic( OnBridgeHubRococoRefundRococoBulletinMessages::default(), ), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + frame_metadata_hash_extension::CheckMetadataHash::::new(false), ); let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index ca0d89f038cb..e91837af0b21 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -65,6 +65,7 @@ fn construct_extrinsic( bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(), ), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + frame_metadata_hash_extension::CheckMetadataHash::new(false), ); let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); @@ -399,8 +400,8 @@ mod bridge_hub_westend_tests { WeightToFee, >() }, - Perbill::from_percent(33), - Some(-33), + Perbill::from_percent(25), + Some(-25), &format!( "Estimate fee for `ExportMessage` for runtime: {:?}", ::Version::get() @@ -418,8 +419,8 @@ mod bridge_hub_westend_tests { RuntimeTestsAdapter, >(collator_session_keys(), construct_and_estimate_extrinsic_fee) }, - Perbill::from_percent(33), - Some(-33), + Perbill::from_percent(25), + Some(-25), &format!( "Estimate fee for `single message delivery` for runtime: {:?}", ::Version::get() @@ -437,8 +438,8 @@ mod bridge_hub_westend_tests { RuntimeTestsAdapter, >(collator_session_keys(), construct_and_estimate_extrinsic_fee) }, - Perbill::from_percent(33), - Some(-33), + Perbill::from_percent(25), + Some(-25), &format!( "Estimate fee for `single message confirmation` for runtime: {:?}", ::Version::get() diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index 4326cbf926d6..a9381501359e 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -22,6 +22,7 @@ serde = { optional = true, features = ["derive"], workspace = true, default-feat # Substrate frame-benchmarking = { optional = true, workspace = true } frame-executive = { workspace = true } +frame-metadata-hash-extension = { workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } frame-system-benchmarking = { optional = true, workspace = true } @@ -100,10 +101,24 @@ pallet-xcm-bridge-hub = { workspace = true } bridge-runtime-common = { workspace = true } bridge-hub-common = { workspace = true } +# Ethereum Bridge (Snowbridge) +snowbridge-beacon-primitives = { workspace = true } +snowbridge-pallet-system = { workspace = true } +snowbridge-system-runtime-api = { workspace = true } +snowbridge-core = { workspace = true } +snowbridge-pallet-ethereum-client = { workspace = true } +snowbridge-pallet-inbound-queue = { workspace = true } +snowbridge-pallet-outbound-queue = { workspace = true } +snowbridge-outbound-queue-runtime-api = { workspace = true } +snowbridge-router-primitives = { workspace = true } +snowbridge-runtime-common = { workspace = true } + + [dev-dependencies] bridge-hub-test-utils = { workspace = true, default-features = true } bridge-runtime-common = { features = ["integrity-test"], workspace = true, default-features = true } sp-keyring = { workspace = true, default-features = true } +snowbridge-runtime-test-common = { workspace = true, default-features = true } [features] default = ["std"] @@ -134,6 +149,7 @@ std = [ "cumulus-primitives-utility/std", "frame-benchmarking/std", "frame-executive/std", + "frame-metadata-hash-extension/std", "frame-support/std", "frame-system-benchmarking?/std", "frame-system-rpc-runtime-api/std", @@ -164,6 +180,16 @@ std = [ "polkadot-runtime-common/std", "scale-info/std", "serde", + "snowbridge-beacon-primitives/std", + "snowbridge-core/std", + "snowbridge-outbound-queue-runtime-api/std", + "snowbridge-pallet-ethereum-client/std", + "snowbridge-pallet-inbound-queue/std", + "snowbridge-pallet-outbound-queue/std", + "snowbridge-pallet-system/std", + "snowbridge-router-primitives/std", + "snowbridge-runtime-common/std", + "snowbridge-system-runtime-api/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", @@ -215,6 +241,14 @@ runtime-benchmarks = [ "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "snowbridge-core/runtime-benchmarks", + "snowbridge-pallet-ethereum-client/runtime-benchmarks", + "snowbridge-pallet-inbound-queue/runtime-benchmarks", + "snowbridge-pallet-outbound-queue/runtime-benchmarks", + "snowbridge-pallet-system/runtime-benchmarks", + "snowbridge-router-primitives/runtime-benchmarks", + "snowbridge-runtime-common/runtime-benchmarks", + "snowbridge-runtime-test-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", @@ -248,6 +282,10 @@ try-runtime = [ "pallet-xcm/try-runtime", "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", + "snowbridge-pallet-ethereum-client/try-runtime", + "snowbridge-pallet-inbound-queue/try-runtime", + "snowbridge-pallet-outbound-queue/try-runtime", + "snowbridge-pallet-system/try-runtime", "sp-runtime/try-runtime", ] @@ -255,3 +293,5 @@ try-runtime = [ # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. on-chain-release-build = ["sp-api/disable-logging"] + +fast-runtime = [] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs new file mode 100644 index 000000000000..7922d3ed02b1 --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -0,0 +1,219 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +#[cfg(not(feature = "runtime-benchmarks"))] +use crate::XcmRouter; +use crate::{ + xcm_config, + xcm_config::{TreasuryAccount, UniversalLocation}, + Balances, EthereumInboundQueue, EthereumOutboundQueue, EthereumSystem, MessageQueue, Runtime, + RuntimeEvent, TransactionByteFee, +}; +use parachains_common::{AccountId, Balance}; +use snowbridge_beacon_primitives::{Fork, ForkVersions}; +use snowbridge_core::{gwei, meth, AllowSiblingsOnly, PricingParameters, Rewards}; +use snowbridge_router_primitives::{inbound::MessageToXcm, outbound::EthereumBlobExporter}; +use sp_core::H160; +use testnet_parachains_constants::westend::{ + currency::*, + fee::WeightToFee, + snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, +}; + +#[cfg(feature = "runtime-benchmarks")] +use benchmark_helpers::DoNothingRouter; +use frame_support::{parameter_types, weights::ConstantMultiplier}; +use pallet_xcm::EnsureXcm; +use sp_runtime::{ + traits::{ConstU32, ConstU8, Keccak256}, + FixedU128, +}; + +/// Exports message to the Ethereum Gateway contract. +pub type SnowbridgeExporter = EthereumBlobExporter< + UniversalLocation, + EthereumNetwork, + snowbridge_pallet_outbound_queue::Pallet, + snowbridge_core::AgentIdOf, +>; + +// Ethereum Bridge +parameter_types! { + pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39")); +} + +parameter_types! { + pub const CreateAssetCall: [u8;2] = [53, 0]; + pub const CreateAssetDeposit: u128 = (UNITS / 10) + EXISTENTIAL_DEPOSIT; + pub Parameters: PricingParameters = PricingParameters { + exchange_rate: FixedU128::from_rational(1, 400), + fee_per_gas: gwei(20), + rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, + multiplier: FixedU128::from_rational(1, 1), + }; +} + +impl snowbridge_pallet_inbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Verifier = snowbridge_pallet_ethereum_client::Pallet; + type Token = Balances; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmSender = XcmRouter; + #[cfg(feature = "runtime-benchmarks")] + type XcmSender = DoNothingRouter; + type ChannelLookup = EthereumSystem; + type GatewayAddress = EthereumGatewayAddress; + #[cfg(feature = "runtime-benchmarks")] + type Helper = Runtime; + type MessageConverter = MessageToXcm< + CreateAssetCall, + CreateAssetDeposit, + ConstU8, + AccountId, + Balance, + >; + type WeightToFee = WeightToFee; + type LengthToFee = ConstantMultiplier; + type MaxMessageSize = ConstU32<2048>; + type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type AssetTransactor = ::AssetTransactor; +} + +impl snowbridge_pallet_outbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Hashing = Keccak256; + type MessageQueue = MessageQueue; + type Decimals = ConstU8<12>; + type MaxMessagePayloadSize = ConstU32<2048>; + type MaxMessagesPerBlock = ConstU32<32>; + type GasMeter = snowbridge_core::outbound::ConstantGasMeter; + type Balance = Balance; + type WeightToFee = WeightToFee; + type WeightInfo = crate::weights::snowbridge_pallet_outbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type Channels = EthereumSystem; +} + +#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [0, 0, 0, 0], // 0x00000000 + epoch: 0, + }, + altair: Fork { + version: [1, 0, 0, 0], // 0x01000000 + epoch: 0, + }, + bellatrix: Fork { + version: [2, 0, 0, 0], // 0x02000000 + epoch: 0, + }, + capella: Fork { + version: [3, 0, 0, 0], // 0x03000000 + epoch: 0, + }, + deneb: Fork { + version: [4, 0, 0, 0], // 0x04000000 + epoch: 0, + } + }; +} + +#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [144, 0, 0, 111], // 0x90000069 + epoch: 0, + }, + altair: Fork { + version: [144, 0, 0, 112], // 0x90000070 + epoch: 50, + }, + bellatrix: Fork { + version: [144, 0, 0, 113], // 0x90000071 + epoch: 100, + }, + capella: Fork { + version: [144, 0, 0, 114], // 0x90000072 + epoch: 56832, + }, + deneb: Fork { + version: [144, 0, 0, 115], // 0x90000073 + epoch: 132608, + }, + }; +} + +impl snowbridge_pallet_ethereum_client::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ForkVersions = ChainForkVersions; + type WeightInfo = crate::weights::snowbridge_pallet_ethereum_client::WeightInfo; +} + +impl snowbridge_pallet_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OutboundQueue = EthereumOutboundQueue; + type SiblingOrigin = EnsureXcm; + type AgentIdOf = snowbridge_core::AgentIdOf; + type TreasuryAccount = TreasuryAccount; + type Token = Balances; + type WeightInfo = crate::weights::snowbridge_pallet_system::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); + type DefaultPricingParameters = Parameters; + type InboundDeliveryCost = EthereumInboundQueue; +} + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmark_helpers { + use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; + use codec::Encode; + use snowbridge_beacon_primitives::BeaconHeader; + use snowbridge_pallet_inbound_queue::BenchmarkHelper; + use sp_core::H256; + use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}; + + impl BenchmarkHelper for Runtime { + fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) { + EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap(); + } + } + + pub struct DoNothingRouter; + impl SendXcm for DoNothingRouter { + type Ticket = Xcm<()>; + + fn validate( + _dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult { + Ok((xcm.clone().unwrap(), Assets::new())) + } + fn deliver(xcm: Xcm<()>) -> Result { + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + Ok(hash) + } + } + + impl snowbridge_pallet_system::BenchmarkHelper for () { + fn make_xcm_origin(location: Location) -> RuntimeOrigin { + RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) + } + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index d9f19afc6296..7384bf2850fe 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -28,6 +28,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); pub mod bridge_common_config; +pub mod bridge_to_ethereum_config; pub mod bridge_to_rococo_config; mod weights; pub mod xcm_config; @@ -96,7 +97,12 @@ use parachains_common::{ impls::DealWithFees, AccountId, Balance, BlockNumber, Hash, Header, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, }; +use snowbridge_core::{ + outbound::{Command, Fee}, + AgentId, PricingParameters, +}; use testnet_parachains_constants::westend::{consensus::*, currency::*, fee::WeightToFee, time::*}; +use xcm::VersionedLocation; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -123,6 +129,7 @@ pub type SignedExtra = ( BridgeRejectObsoleteHeadersAndMessages, (bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages,), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, + frame_metadata_hash_extension::CheckMetadataHash, ); /// Unchecked extrinsic type as expected by this runtime. @@ -351,10 +358,13 @@ impl pallet_message_queue::Config for Runtime { type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor; #[cfg(not(feature = "runtime-benchmarks"))] - type MessageProcessor = xcm_builder::ProcessXcmMessage< - AggregateMessageOrigin, - xcm_executor::XcmExecutor, - RuntimeCall, + type MessageProcessor = bridge_hub_common::BridgeHubMessageRouter< + xcm_builder::ProcessXcmMessage< + AggregateMessageOrigin, + xcm_executor::XcmExecutor, + RuntimeCall, + >, + EthereumOutboundQueue, >; type Size = u32; // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: @@ -516,6 +526,11 @@ construct_runtime!( BridgeRococoMessages: pallet_bridge_messages:: = 44, XcmOverBridgeHubRococo: pallet_xcm_bridge_hub:: = 45, + EthereumInboundQueue: snowbridge_pallet_inbound_queue = 80, + EthereumOutboundQueue: snowbridge_pallet_outbound_queue = 81, + EthereumBeaconClient: snowbridge_pallet_ethereum_client = 82, + EthereumSystem: snowbridge_pallet_system = 83, + // Message Queue. Importantly, is registered last so that messages are processed after // the `on_initialize` hooks of bridging pallets. MessageQueue: pallet_message_queue = 250, @@ -568,6 +583,11 @@ mod benches { [pallet_bridge_grandpa, RococoFinality] [pallet_bridge_parachains, WithinRococo] [pallet_bridge_messages, WestendToRococo] + // Ethereum Bridge + [snowbridge_pallet_inbound_queue, EthereumInboundQueue] + [snowbridge_pallet_outbound_queue, EthereumOutboundQueue] + [snowbridge_pallet_system, EthereumSystem] + [snowbridge_pallet_ethereum_client, EthereumBeaconClient] ); } @@ -830,6 +850,22 @@ impl_runtime_apis! { } } + impl snowbridge_outbound_queue_runtime_api::OutboundQueueApi for Runtime { + fn prove_message(leaf_index: u64) -> Option { + snowbridge_pallet_outbound_queue::api::prove_message::(leaf_index) + } + + fn calculate_fee(command: Command, parameters: Option>) -> Fee { + snowbridge_pallet_outbound_queue::api::calculate_fee::(command, parameters) + } + } + + impl snowbridge_system_runtime_api::ControlApi for Runtime { + fn agent_id(location: VersionedLocation) -> Option { + snowbridge_pallet_system::api::agent_id::(location) + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { @@ -1259,7 +1295,8 @@ mod tests { ( bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(), ), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new() + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + frame_metadata_hash_extension::CheckMetadataHash::new(false), ); { @@ -1272,9 +1309,9 @@ mod tests { 10, (((), ()), ((), ())), ); - assert_eq!(payload.encode(), bh_indirect_payload.encode()); + assert_eq!(payload.encode().split_last().unwrap().1, bh_indirect_payload.encode()); assert_eq!( - payload.additional_signed().unwrap().encode(), + payload.additional_signed().unwrap().encode().split_last().unwrap().1, bh_indirect_payload.additional_signed().unwrap().encode() ) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs index 245daaf8ed91..9b7f7188782f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs @@ -43,6 +43,11 @@ pub mod paritydb_weights; pub mod rocksdb_weights; pub mod xcm; +pub mod snowbridge_pallet_ethereum_client; +pub mod snowbridge_pallet_inbound_queue; +pub mod snowbridge_pallet_outbound_queue; +pub mod snowbridge_pallet_system; + pub use block_weights::constants::BlockExecutionWeight; pub use extrinsic_weights::constants::ExtrinsicBaseWeight; pub use rocksdb_weights::constants::RocksDbWeight; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_ethereum_client.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_ethereum_client.rs new file mode 100644 index 000000000000..23e2a9cffb0b --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_ethereum_client.rs @@ -0,0 +1,120 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `snowbridge_pallet_ethereum_client` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-06-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `Claras-MacBook-Pro-2.local`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/release/polkadot-parachain +// benchmark +// pallet +// --chain=bridge-hub-rococo-dev +// --pallet=snowbridge_pallet_ethereum_client +// --extrinsic +// * +// --wasm-execution=compiled +// --steps +// 50 +// --repeat +// 20 +// --output +// cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_ethereum_client.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `snowbridge_pallet_ethereum_client`. +pub struct WeightInfo(PhantomData); +impl snowbridge_pallet_ethereum_client::WeightInfo for WeightInfo { + /// Storage: `EthereumBeaconClient::FinalizedBeaconStateIndex` (r:1 w:1) + /// Proof: `EthereumBeaconClient::FinalizedBeaconStateIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::FinalizedBeaconStateMapping` (r:1 w:1) + /// Proof: `EthereumBeaconClient::FinalizedBeaconStateMapping` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::NextSyncCommittee` (r:0 w:1) + /// Proof: `EthereumBeaconClient::NextSyncCommittee` (`max_values`: Some(1), `max_size`: Some(92372), added: 92867, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::InitialCheckpointRoot` (r:0 w:1) + /// Proof: `EthereumBeaconClient::InitialCheckpointRoot` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::ValidatorsRoot` (r:0 w:1) + /// Proof: `EthereumBeaconClient::ValidatorsRoot` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::LatestFinalizedBlockRoot` (r:0 w:1) + /// Proof: `EthereumBeaconClient::LatestFinalizedBlockRoot` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::CurrentSyncCommittee` (r:0 w:1) + /// Proof: `EthereumBeaconClient::CurrentSyncCommittee` (`max_values`: Some(1), `max_size`: Some(92372), added: 92867, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::FinalizedBeaconState` (r:0 w:1) + /// Proof: `EthereumBeaconClient::FinalizedBeaconState` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + fn force_checkpoint() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `3501` + // Minimum execution time: 67_553_000_000 picoseconds. + Weight::from_parts(68_677_000_000, 0) + .saturating_add(Weight::from_parts(0, 3501)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(8)) + } + /// Storage: `EthereumBeaconClient::OperatingMode` (r:1 w:0) + /// Proof: `EthereumBeaconClient::OperatingMode` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::LatestFinalizedBlockRoot` (r:1 w:0) + /// Proof: `EthereumBeaconClient::LatestFinalizedBlockRoot` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::FinalizedBeaconState` (r:1 w:0) + /// Proof: `EthereumBeaconClient::FinalizedBeaconState` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::NextSyncCommittee` (r:1 w:0) + /// Proof: `EthereumBeaconClient::NextSyncCommittee` (`max_values`: Some(1), `max_size`: Some(92372), added: 92867, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::CurrentSyncCommittee` (r:1 w:0) + /// Proof: `EthereumBeaconClient::CurrentSyncCommittee` (`max_values`: Some(1), `max_size`: Some(92372), added: 92867, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::ValidatorsRoot` (r:1 w:0) + /// Proof: `EthereumBeaconClient::ValidatorsRoot` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn submit() -> Weight { + // Proof Size summary in bytes: + // Measured: `92749` + // Estimated: `93857` + // Minimum execution time: 16_988_000_000 picoseconds. + Weight::from_parts(17_125_000_000, 0) + .saturating_add(Weight::from_parts(0, 93857)) + .saturating_add(T::DbWeight::get().reads(6)) + } + /// Storage: `EthereumBeaconClient::OperatingMode` (r:1 w:0) + /// Proof: `EthereumBeaconClient::OperatingMode` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::LatestFinalizedBlockRoot` (r:1 w:0) + /// Proof: `EthereumBeaconClient::LatestFinalizedBlockRoot` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::FinalizedBeaconState` (r:1 w:0) + /// Proof: `EthereumBeaconClient::FinalizedBeaconState` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::NextSyncCommittee` (r:1 w:1) + /// Proof: `EthereumBeaconClient::NextSyncCommittee` (`max_values`: Some(1), `max_size`: Some(92372), added: 92867, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::CurrentSyncCommittee` (r:1 w:0) + /// Proof: `EthereumBeaconClient::CurrentSyncCommittee` (`max_values`: Some(1), `max_size`: Some(92372), added: 92867, mode: `MaxEncodedLen`) + /// Storage: `EthereumBeaconClient::ValidatorsRoot` (r:1 w:0) + /// Proof: `EthereumBeaconClient::ValidatorsRoot` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn submit_with_sync_committee() -> Weight { + // Proof Size summary in bytes: + // Measured: `92749` + // Estimated: `93857` + // Minimum execution time: 84_553_000_000 picoseconds. + Weight::from_parts(87_459_000_000, 0) + .saturating_add(Weight::from_parts(0, 93857)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_inbound_queue.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_inbound_queue.rs new file mode 100644 index 000000000000..153c1d363be1 --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_inbound_queue.rs @@ -0,0 +1,69 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `snowbridge_pallet_inbound_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-09-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `macbook pro 14 m2`, CPU: `m2-arm64` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/polkadot-parachain +// benchmark +// pallet +// --chain=bridge-hub-rococo-dev +// --pallet=snowbridge_inbound_queue +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --steps +// 50 +// --repeat +// 20 +// --output +// ./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_inbound_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `snowbridge_pallet_inbound_queue`. +pub struct WeightInfo(PhantomData); +impl snowbridge_pallet_inbound_queue::WeightInfo for WeightInfo { + /// Storage: EthereumInboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumInboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ExecutionHeaders (r:1 w:0) + /// Proof: EthereumBeaconClient ExecutionHeaders (max_values: None, max_size: Some(136), added: 2611, mode: MaxEncodedLen) + /// Storage: EthereumInboundQueue Nonce (r:1 w:1) + /// Proof: EthereumInboundQueue Nonce (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn submit() -> Weight { + // Proof Size summary in bytes: + // Measured: `800` + // Estimated: `7200` + // Minimum execution time: 200_000_000 picoseconds. + Weight::from_parts(200_000_000, 0) + .saturating_add(Weight::from_parts(0, 7200)) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(6)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_outbound_queue.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_outbound_queue.rs new file mode 100644 index 000000000000..8adcef076e00 --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_outbound_queue.rs @@ -0,0 +1,87 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `snowbridge_outbound_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-10-20, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `192.168.1.13`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// ../target/release/polkadot-parachain +// benchmark +// pallet +// --chain=bridge-hub-rococo-dev +// --pallet=snowbridge_outbound_queue +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --output +// ../parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_outbound_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `snowbridge_outbound_queue`. +pub struct WeightInfo(PhantomData); +impl snowbridge_pallet_outbound_queue::WeightInfo for WeightInfo { + /// Storage: EthereumOutboundQueue MessageLeaves (r:1 w:1) + /// Proof Skipped: EthereumOutboundQueue MessageLeaves (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: EthereumOutboundQueue PendingHighPriorityMessageCount (r:1 w:1) + /// Proof: EthereumOutboundQueue PendingHighPriorityMessageCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue Nonce (r:1 w:1) + /// Proof: EthereumOutboundQueue Nonce (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue Messages (r:1 w:1) + /// Proof Skipped: EthereumOutboundQueue Messages (max_values: Some(1), max_size: None, mode: Measured) + fn do_process_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `3485` + // Minimum execution time: 39_000_000 picoseconds. + Weight::from_parts(39_000_000, 3485) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: EthereumOutboundQueue MessageLeaves (r:1 w:0) + /// Proof Skipped: EthereumOutboundQueue MessageLeaves (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Digest (r:1 w:1) + /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) + fn commit() -> Weight { + // Proof Size summary in bytes: + // Measured: `1094` + // Estimated: `2579` + // Minimum execution time: 28_000_000 picoseconds. + Weight::from_parts(28_000_000, 2579) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + + fn commit_single() -> Weight { + // Proof Size summary in bytes: + // Measured: `1094` + // Estimated: `2579` + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(9_000_000, 1586) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs new file mode 100644 index 000000000000..c6c188e323af --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs @@ -0,0 +1,256 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `snowbridge_system` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-10-09, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `crake.local`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/polkadot-parachain +// benchmark +// pallet +// --chain +// bridge-hub-rococo-dev +// --pallet=snowbridge_pallet_system +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --output +// parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `snowbridge_system`. +pub struct WeightInfo(PhantomData); +impl snowbridge_pallet_system::WeightInfo for WeightInfo { + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 47_000_000 picoseconds. + Weight::from_parts(47_000_000, 0) + .saturating_add(Weight::from_parts(0, 3517)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: EthereumSystem Agents (r:1 w:1) + /// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn create_agent() -> Weight { + // Proof Size summary in bytes: + // Measured: `187` + // Estimated: `6196` + // Minimum execution time: 87_000_000 picoseconds. + Weight::from_parts(87_000_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(6)) + } + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: EthereumSystem Agents (r:1 w:0) + /// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: EthereumSystem Channels (r:1 w:1) + /// Proof: EthereumSystem Channels (max_values: None, max_size: Some(12), added: 2487, mode: MaxEncodedLen) + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn create_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `602` + // Estimated: `69050` + // Minimum execution time: 84_000_000 picoseconds. + Weight::from_parts(84_000_000, 0) + .saturating_add(Weight::from_parts(0, 69050)) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: EthereumSystem Channels (r:1 w:0) + /// Proof: EthereumSystem Channels (max_values: None, max_size: Some(12), added: 2487, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn update_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 41_000_000 picoseconds. + Weight::from_parts(41_000_000, 0) + .saturating_add(Weight::from_parts(0, 6044)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: EthereumSystem Channels (r:1 w:0) + /// Proof: EthereumSystem Channels (max_values: None, max_size: Some(12), added: 2487, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn force_update_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 41_000_000 picoseconds. + Weight::from_parts(41_000_000, 0) + .saturating_add(Weight::from_parts(0, 6044)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn set_operating_mode() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 30_000_000 picoseconds. + Weight::from_parts(30_000_000, 0) + .saturating_add(Weight::from_parts(0, 3517)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: EthereumSystem Agents (r:1 w:0) + /// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn transfer_native_from_agent() -> Weight { + // Proof Size summary in bytes: + // Measured: `252` + // Estimated: `6044` + // Minimum execution time: 43_000_000 picoseconds. + Weight::from_parts(43_000_000, 0) + .saturating_add(Weight::from_parts(0, 6044)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: EthereumSystem Agents (r:1 w:0) + /// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn force_transfer_native_from_agent() -> Weight { + // Proof Size summary in bytes: + // Measured: `252` + // Estimated: `6044` + // Minimum execution time: 42_000_000 picoseconds. + Weight::from_parts(42_000_000, 0) + .saturating_add(Weight::from_parts(0, 6044)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn set_token_transfer_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 31_000_000 picoseconds. + Weight::from_parts(42_000_000, 3517) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn set_pricing_parameters() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 31_000_000 picoseconds. + Weight::from_parts(42_000_000, 3517) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index 7f94b76a005b..81705ee2dc94 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs @@ -35,19 +35,24 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; +use snowbridge_runtime_common::XcmExportFeeToSibling; use sp_runtime::traits::AccountIdConversion; +use sp_std::marker::PhantomData; +use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, - FungibleAdapter, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + FungibleAdapter, HandleFee, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, }; -use xcm_executor::XcmExecutor; +use xcm_executor::{ + traits::{FeeManager, FeeReason, FeeReason::Export}, + XcmExecutor, +}; parameter_types! { pub const WestendLocation: Location = Location::parent(); @@ -193,11 +198,24 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = PolkadotXcm; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = XcmFeeManagerFromComponents< + type FeeManager = XcmFeeManagerFromComponentsBridgeHub< WaivedLocations, - SendXcmFeeToAccount, + ( + XcmExportFeeToSibling< + bp_westend::Balance, + AccountId, + WestendLocation, + EthereumNetwork, + Self::AssetTransactor, + crate::EthereumOutboundQueue, + >, + SendXcmFeeToAccount, + ), >; - type MessageExporter = (crate::bridge_to_rococo_config::ToBridgeHubRococoHaulBlobExporter,); + type MessageExporter = ( + crate::bridge_to_rococo_config::ToBridgeHubRococoHaulBlobExporter, + crate::bridge_to_ethereum_config::SnowbridgeExporter, + ); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; @@ -261,3 +279,24 @@ impl cumulus_pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; } + +pub struct XcmFeeManagerFromComponentsBridgeHub( + PhantomData<(WaivedLocations, HandleFee)>, +); +impl, FeeHandler: HandleFee> FeeManager + for XcmFeeManagerFromComponentsBridgeHub +{ + fn is_waived(origin: Option<&Location>, fee_reason: FeeReason) -> bool { + let Some(loc) = origin else { return false }; + if let Export { network, destination: Here } = fee_reason { + if network == EthereumNetwork::get() { + return false + } + } + WaivedLocations::contains(loc) + } + + fn handle_fee(fee: Assets, context: Option<&XcmContext>, reason: FeeReason) { + FeeHandler::handle_fee(fee, context, reason); + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs new file mode 100644 index 000000000000..46a0fa7a664b --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs @@ -0,0 +1,202 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +#![cfg(test)] + +use bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID; +use bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID; +use bp_polkadot_core::Signature; +use bridge_hub_westend_runtime::{ + bridge_to_rococo_config, xcm_config::XcmConfig, AllPalletsWithoutSystem, + BridgeRejectObsoleteHeadersAndMessages, Executive, MessageQueueServiceWeight, Runtime, + RuntimeCall, RuntimeEvent, SessionKeys, SignedExtra, UncheckedExtrinsic, +}; +use codec::{Decode, Encode}; +use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees}; +use frame_support::parameter_types; +use parachains_common::{AccountId, AuraId, Balance}; +use snowbridge_pallet_ethereum_client::WeightInfo; +use sp_core::H160; +use sp_keyring::AccountKeyring::Alice; +use sp_runtime::{ + generic::{Era, SignedPayload}, + AccountId32, +}; + +parameter_types! { + pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; +} + +fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys { + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(Alice), + AccountId::from(Alice), + SessionKeys { aura: AuraId::from(Alice.public()) }, + ) +} + +#[test] +pub fn transfer_token_to_ethereum_works() { + snowbridge_runtime_test_common::send_transfer_token_message_success::( + 11155111, + collator_session_keys(), + BRIDGE_HUB_WESTEND_PARACHAIN_ID, + ASSET_HUB_WESTEND_PARACHAIN_ID, + H160::random(), + H160::random(), + DefaultBridgeHubEthereumBaseFee::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::EthereumOutboundQueue(event)) => Some(event), + _ => None, + } + }), + ) +} + +#[test] +pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() { + snowbridge_runtime_test_common::send_unpaid_transfer_token_message::( + 11155111, + collator_session_keys(), + BRIDGE_HUB_WESTEND_PARACHAIN_ID, + ASSET_HUB_WESTEND_PARACHAIN_ID, + H160::random(), + H160::random(), + ) +} + +#[test] +pub fn transfer_token_to_ethereum_fee_not_enough() { + snowbridge_runtime_test_common::send_transfer_token_message_failure::( + 11155111, + collator_session_keys(), + BRIDGE_HUB_WESTEND_PARACHAIN_ID, + ASSET_HUB_WESTEND_PARACHAIN_ID, + DefaultBridgeHubEthereumBaseFee::get() + 10_000_000_000, + H160::random(), + H160::random(), + // fee not enough + 10_000_000_000, + NotHoldingFees, + ) +} + +#[test] +pub fn transfer_token_to_ethereum_insufficient_fund() { + snowbridge_runtime_test_common::send_transfer_token_message_failure::( + 11155111, + collator_session_keys(), + BRIDGE_HUB_WESTEND_PARACHAIN_ID, + ASSET_HUB_WESTEND_PARACHAIN_ID, + 1_000_000_000, + H160::random(), + H160::random(), + DefaultBridgeHubEthereumBaseFee::get(), + FailedToTransactAsset("Funds are unavailable"), + ) +} + +#[test] +fn max_message_queue_service_weight_is_more_than_beacon_extrinsic_weights() { + let max_message_queue_weight = MessageQueueServiceWeight::get(); + let force_checkpoint = + ::WeightInfo::force_checkpoint(); + let submit_checkpoint = + ::WeightInfo::submit(); + max_message_queue_weight.all_gt(force_checkpoint); + max_message_queue_weight.all_gt(submit_checkpoint); +} + +#[test] +fn ethereum_client_consensus_extrinsics_work() { + snowbridge_runtime_test_common::ethereum_extrinsic( + collator_session_keys(), + BRIDGE_HUB_WESTEND_PARACHAIN_ID, + construct_and_apply_extrinsic, + ); +} + +#[test] +fn ethereum_to_polkadot_message_extrinsics_work() { + snowbridge_runtime_test_common::ethereum_to_polkadot_message_extrinsics_work( + collator_session_keys(), + BRIDGE_HUB_WESTEND_PARACHAIN_ID, + construct_and_apply_extrinsic, + ); +} + +/// Tests that the digest items are as expected when a Ethereum Outbound message is received. +/// If the MessageQueue pallet is configured before (i.e. the MessageQueue pallet is listed before +/// the EthereumOutboundQueue in the construct_runtime macro) the EthereumOutboundQueue, this test +/// will fail. +#[test] +pub fn ethereum_outbound_queue_processes_messages_before_message_queue_works() { + snowbridge_runtime_test_common::ethereum_outbound_queue_processes_messages_before_message_queue_works::< + Runtime, + XcmConfig, + AllPalletsWithoutSystem, + >( + 11155111, + collator_session_keys(), + BRIDGE_HUB_WESTEND_PARACHAIN_ID, + ASSET_HUB_WESTEND_PARACHAIN_ID, + H160::random(), + H160::random(), + DefaultBridgeHubEthereumBaseFee::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::EthereumOutboundQueue(event)) => Some(event), + _ => None, + } + }), + ) +} + +fn construct_extrinsic( + sender: sp_keyring::AccountKeyring, + call: RuntimeCall, +) -> UncheckedExtrinsic { + let account_id = AccountId32::from(sender.public()); + let extra: SignedExtra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(Era::immortal()), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&account_id).nonce, + ), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(0), + BridgeRejectObsoleteHeadersAndMessages::default(), + (bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(),), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + frame_metadata_hash_extension::CheckMetadataHash::::new(false), + ); + let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); + let signature = payload.using_encoded(|e| sender.sign(e)); + UncheckedExtrinsic::new_signed(call, account_id.into(), Signature::Sr25519(signature), extra) +} + +fn construct_and_apply_extrinsic( + origin: sp_keyring::AccountKeyring, + call: RuntimeCall, +) -> sp_runtime::DispatchOutcome { + let xt = construct_extrinsic(origin, call); + let r = Executive::apply_extrinsic(xt); + r.unwrap() +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs index 98a789eb4e00..a66c0f84240c 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs @@ -77,6 +77,7 @@ fn construct_extrinsic( BridgeRejectObsoleteHeadersAndMessages::default(), (bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(),), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + frame_metadata_hash_extension::CheckMetadataHash::new(false), ); let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); @@ -299,8 +300,8 @@ pub fn can_calculate_fee_for_standalone_message_delivery_transaction() { RuntimeTestsAdapter, >(collator_session_keys(), construct_and_estimate_extrinsic_fee) }, - Perbill::from_percent(33), - Some(-33), + Perbill::from_percent(25), + Some(-25), &format!( "Estimate fee for `single message delivery` for runtime: {:?}", ::Version::get() @@ -318,8 +319,8 @@ pub fn can_calculate_fee_for_standalone_message_confirmation_transaction() { RuntimeTestsAdapter, >(collator_session_keys(), construct_and_estimate_extrinsic_fee) }, - Perbill::from_percent(33), - Some(-33), + Perbill::from_percent(25), + Some(-25), &format!( "Estimate fee for `single message confirmation` for runtime: {:?}", ::Version::get() diff --git a/cumulus/parachains/runtimes/constants/src/westend.rs b/cumulus/parachains/runtimes/constants/src/westend.rs index 607d91e8808d..fec66cec2eb6 100644 --- a/cumulus/parachains/runtimes/constants/src/westend.rs +++ b/cumulus/parachains/runtimes/constants/src/westend.rs @@ -168,3 +168,19 @@ pub mod time { pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; } + +pub mod snowbridge { + use frame_support::parameter_types; + use xcm::opaque::lts::NetworkId; + + /// The pallet index of the Ethereum inbound queue pallet in the bridge hub runtime. + pub const INBOUND_QUEUE_PALLET_INDEX: u8 = 80; + + parameter_types! { + /// Network and location for the Ethereum chain. On Westend, the Ethereum chain bridged + /// to is the Sepolia Ethereum testnet, with chain ID 11155111. + /// + /// + pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + } +} diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index e1016cebb395..5d6548447463 100644 --- a/cumulus/polkadot-parachain/Cargo.toml +++ b/cumulus/polkadot-parachain/Cargo.toml @@ -175,6 +175,7 @@ try-runtime = [ ] fast-runtime = [ "bridge-hub-rococo-runtime/fast-runtime", + "bridge-hub-westend-runtime/fast-runtime", "coretime-rococo-runtime/fast-runtime", "coretime-westend-runtime/fast-runtime", ] diff --git a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs index 15e8a1bf11a0..3b7376d045b8 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -357,6 +357,10 @@ pub mod westend { }, "bridgeRococoMessages": { "owner": bridges_pallet_owner.clone(), + }, + "ethereumSystem": { + "paraId": id, + "assetHubParaId": 1000 } }) } diff --git a/cumulus/polkadot-parachain/src/command.rs b/cumulus/polkadot-parachain/src/command.rs index e867a41bee2b..3df90c889e8e 100644 --- a/cumulus/polkadot-parachain/src/command.rs +++ b/cumulus/polkadot-parachain/src/command.rs @@ -295,6 +295,7 @@ fn extract_parachain_id(id: &str) -> (&str, &str, Option) { let para_prefixes = [ // Penpal "penpal-rococo-", + "penpal-westend-", "penpal-kusama-", "penpal-polkadot-", // Glutton Kusama diff --git a/polkadot/node/service/src/chain_spec.rs b/polkadot/node/service/src/chain_spec.rs index 0358bc300ab0..f930476a5541 100644 --- a/polkadot/node/service/src/chain_spec.rs +++ b/polkadot/node/service/src/chain_spec.rs @@ -658,7 +658,8 @@ fn westend_local_testnet_genesis() -> serde_json::Value { #[cfg(feature = "westend-native")] pub fn westend_local_testnet_config() -> Result { Ok(WestendChainSpec::builder( - westend::WASM_BINARY.ok_or("Westend development wasm not available")?, + westend::fast_runtime_binary::WASM_BINARY + .ok_or("Westend development wasm not available")?, Default::default(), ) .with_name("Westend Local Testnet") diff --git a/polkadot/runtime/westend/build.rs b/polkadot/runtime/westend/build.rs index 8ff3a4fb9112..55ccd3640129 100644 --- a/polkadot/runtime/westend/build.rs +++ b/polkadot/runtime/westend/build.rs @@ -17,6 +17,10 @@ #[cfg(all(not(feature = "metadata-hash"), feature = "std"))] fn main() { substrate_wasm_builder::WasmBuilder::build_using_defaults(); + substrate_wasm_builder::WasmBuilder::init_with_defaults() + .set_file_name("fast_runtime_binary.rs") + .enable_feature("fast-runtime") + .build(); } #[cfg(all(feature = "metadata-hash", feature = "std"))] @@ -24,6 +28,11 @@ fn main() { substrate_wasm_builder::WasmBuilder::init_with_defaults() .enable_metadata_hash("WND", 12) .build(); + substrate_wasm_builder::WasmBuilder::init_with_defaults() + .set_file_name("fast_runtime_binary.rs") + .enable_feature("fast-runtime") + .enable_metadata_hash("WND", 12) + .build(); } #[cfg(not(feature = "std"))] diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index eddb7cbd21ea..838ba17e5613 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -156,6 +156,11 @@ impl_runtime_weights!(westend_runtime_constants); #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +#[cfg(feature = "std")] +pub mod fast_runtime_binary { + include!(concat!(env!("OUT_DIR"), "/fast_runtime_binary.rs")); +} + /// Runtime version (Westend). #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { diff --git a/prdoc/pr_5074.prdoc b/prdoc/pr_5074.prdoc new file mode 100644 index 000000000000..cddf15ffb47c --- /dev/null +++ b/prdoc/pr_5074.prdoc @@ -0,0 +1,33 @@ +title: "Snowbridge on Westend" + +doc: + - audience: Runtime Dev + description: | + Since Rococo is now deprecated, we need another testnet to detect bleeding-edge changes + to Substrate, Polkadot, BEEFY consensus protocols that could brick the bridge. + - audience: Runtime Dev + description: | + Like Rococo this PR enables the fast-runtime feature by default which is easier + for testing beefy stuff on westend-local. + +crates: + - name: asset-hub-westend-runtime + bump: patch + - name: bridge-hub-westend-runtime + bump: major + - name: bridge-hub-rococo-runtime + bump: patch + - name: testnet-parachains-constants + bump: patch + - name: bridge-hub-westend-emulated-chain + bump: minor + - name: bridge-hub-westend-integration-tests + bump: minor + - name: polkadot-parachain-bin + bump: patch + - name: westend-runtime + bump: patch + - name: polkadot-service + bump: patch + + From 8af9889f561fcf97ac7c1446ceb72f9324348dd4 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:22:27 +0300 Subject: [PATCH 073/382] Bring reference_hardware.json inline with machine used for weights (#5196) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since `May 2023` after https://github.com/paritytech/substrate/pull/13548 optimization, `Blake2256` is faster with about 30%, that means that there is a difference of ~30% between the benchmark values we ask validators to run against and the machine we use for generating the weights.So if all validators, just barely pass the benchmarks our weights are potentially underestimated with about ~20%, so let's bring this two in sync. Same thing happened when we merged https://github.com/paritytech/polkadot-sdk/pull/2524 in `Nov 2023` SR25519-Verify became faster with about 10-15% ## Results Generated on machine from here: https://github.com/paritytech/devops/pull/3210 ``` +----------+----------------+--------------+-------------+-------------------+ | Category | Function | Score | Minimum | Result | +============================================================================+ | CPU | BLAKE2-256 | 1.00 GiBs | 783.27 MiBs | ✅ Pass (130.7 %) | |----------+----------------+--------------+-------------+-------------------| | CPU | SR25519-Verify | 637.62 KiBs | 560.67 KiBs | ✅ Pass (113.7 %) | |----------+----------------+--------------+-------------+-------------------| | Memory | Copy | 12.19 GiBs | 11.49 GiBs | ✅ Pass (106.1 %) | ``` Discovered and discussed here: https://github.com/paritytech/polkadot-sdk/pull/5127#issuecomment-2258423469 ## Downsides Machines that barely passed the benchmark will suddenly find themselves bellow the benchmark, but since that is just an warning and everything else continues as before it shouldn't be too impactful and should give the validators the necessary information that they need to become compliant, since they actually aren't when compared with the used weights. --------- Signed-off-by: Alexandru Gheorghe --- prdoc/pr_5196.prdoc | 23 +++++++++++++++++++ .../benchmarking-cli/src/machine/hardware.rs | 4 ++-- .../src/machine/reference_hardware.json | 4 ++-- 3 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 prdoc/pr_5196.prdoc diff --git a/prdoc/pr_5196.prdoc b/prdoc/pr_5196.prdoc new file mode 100644 index 000000000000..3ed4fbdff3f3 --- /dev/null +++ b/prdoc/pr_5196.prdoc @@ -0,0 +1,23 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Bring benchmark inline with reference machine used for weights + +doc: + - audience: Node Operator + description: | + - BLAKE2-256 reference values were too low(~30%) when compared with the machine used for generating + the weights, so it was brought in sync with results on the reference hardware recommended here: + https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware + - SR25519-Verify reference values were too low(~10%) when compared with the machine used for generating + the weights, so it was brought in sync with results on the reference hardware recommended here: + https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware + - Validators where the `BLAKE2-256` and `SR25519-Verify` were barely passing, might received the + warning that they are not compliant anymore, this should not be treated as critical, but they + should take the necessary steps to become compliant in the near/mid-term future. + - Note!: The reference hardware requirements have not been increased we just fixed the benchmark which + was wrongly reporting lower spec HW as being compliant. + +crates: + - name: frame-benchmarking-cli + bump: minor diff --git a/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs b/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs index 5a4b7c797b6f..555e848f8ccc 100644 --- a/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs +++ b/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs @@ -51,10 +51,10 @@ mod tests { assert_eq!( *SUBSTRATE_REFERENCE_HARDWARE, Requirements(vec![ - Requirement { metric: Metric::Blake2256, minimum: Throughput::from_mibs(783.27) }, + Requirement { metric: Metric::Blake2256, minimum: Throughput::from_mibs(1000.00) }, Requirement { metric: Metric::Sr25519Verify, - minimum: Throughput::from_kibs(560.670000128), + minimum: Throughput::from_kibs(637.619999744), }, Requirement { metric: Metric::MemCopy, diff --git a/substrate/utils/frame/benchmarking-cli/src/machine/reference_hardware.json b/substrate/utils/frame/benchmarking-cli/src/machine/reference_hardware.json index c2fb4c7d4a28..cec42b8f245c 100644 --- a/substrate/utils/frame/benchmarking-cli/src/machine/reference_hardware.json +++ b/substrate/utils/frame/benchmarking-cli/src/machine/reference_hardware.json @@ -1,11 +1,11 @@ [ { "metric": "Blake2256", - "minimum": 783.27 + "minimum": 1000.00 }, { "metric": "Sr25519Verify", - "minimum": 0.547529297 + "minimum": 0.622675781 }, { "metric": "MemCopy", From 0fb6e3c51eb37575f33ac7f06b6350c0aab4f0c7 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 7 Aug 2024 16:30:33 +0200 Subject: [PATCH 074/382] Umbrella crate: exclude chain-specific crates (#5173) Uses custom metadata to exclude chain-specific crates. The only concern is that devs who want to use chain-specific crates, still need to select matching versions numbers. Could possibly be addresses with chain-specific umbrella crates, but currently it should be possible to use [psvm](https://github.com/paritytech/psvm). --------- Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 13 --- .../chains/chain-asset-hub-rococo/Cargo.toml | 3 + .../chains/chain-asset-hub-westend/Cargo.toml | 3 + .../chain-bridge-hub-cumulus/Cargo.toml | 3 + .../chains/chain-bridge-hub-kusama/Cargo.toml | 3 + .../chain-bridge-hub-polkadot/Cargo.toml | 3 + .../chains/chain-bridge-hub-rococo/Cargo.toml | 3 + .../chain-bridge-hub-westend/Cargo.toml | 3 + bridges/chains/chain-kusama/Cargo.toml | 3 + .../chains/chain-polkadot-bulletin/Cargo.toml | 3 + bridges/chains/chain-rococo/Cargo.toml | 3 + bridges/chains/chain-westend/Cargo.toml | 3 + docs/contributor/CONTRIBUTING.md | 39 ++++++++ polkadot/runtime/rococo/constants/Cargo.toml | 3 + polkadot/runtime/westend/constants/Cargo.toml | 3 + prdoc/pr_5173.prdoc | 39 ++++++++ scripts/generate-umbrella.py | 3 +- umbrella/Cargo.toml | 91 ------------------- umbrella/src/lib.rs | 52 ----------- 19 files changed, 119 insertions(+), 157 deletions(-) create mode 100644 prdoc/pr_5173.prdoc diff --git a/Cargo.lock b/Cargo.lock index 826bb9398ae0..54a01f12f35c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14179,25 +14179,14 @@ dependencies = [ "asset-test-utils", "assets-common", "binary-merkle-tree", - "bp-asset-hub-rococo", - "bp-asset-hub-westend", - "bp-bridge-hub-cumulus", - "bp-bridge-hub-kusama", - "bp-bridge-hub-polkadot", - "bp-bridge-hub-rococo", - "bp-bridge-hub-westend", "bp-header-chain", - "bp-kusama", "bp-messages", "bp-parachains", "bp-polkadot", - "bp-polkadot-bulletin", "bp-polkadot-core", "bp-relayers", - "bp-rococo", "bp-runtime", "bp-test-utils", - "bp-westend", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", @@ -14408,7 +14397,6 @@ dependencies = [ "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", - "rococo-runtime-constants", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", @@ -14552,7 +14540,6 @@ dependencies = [ "testnet-parachains-constants", "tracing-gum", "tracing-gum-proc-macro", - "westend-runtime-constants", "xcm-emulator", "xcm-procedural", "xcm-runtime-apis", diff --git a/bridges/chains/chain-asset-hub-rococo/Cargo.toml b/bridges/chains/chain-asset-hub-rococo/Cargo.toml index b765fbc57bb0..363a869048aa 100644 --- a/bridges/chains/chain-asset-hub-rococo/Cargo.toml +++ b/bridges/chains/chain-asset-hub-rococo/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/bridges/chains/chain-asset-hub-westend/Cargo.toml b/bridges/chains/chain-asset-hub-westend/Cargo.toml index ff89864fb2db..430d9b6116cf 100644 --- a/bridges/chains/chain-asset-hub-westend/Cargo.toml +++ b/bridges/chains/chain-asset-hub-westend/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/bridges/chains/chain-bridge-hub-cumulus/Cargo.toml b/bridges/chains/chain-bridge-hub-cumulus/Cargo.toml index 5609398385f9..99ba721991ee 100644 --- a/bridges/chains/chain-bridge-hub-cumulus/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-cumulus/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/bridges/chains/chain-bridge-hub-kusama/Cargo.toml b/bridges/chains/chain-bridge-hub-kusama/Cargo.toml index 605643b0a4eb..39f7b44daa55 100644 --- a/bridges/chains/chain-bridge-hub-kusama/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-kusama/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/bridges/chains/chain-bridge-hub-polkadot/Cargo.toml b/bridges/chains/chain-bridge-hub-polkadot/Cargo.toml index 97e36a19c748..3b0ac96e7cd3 100644 --- a/bridges/chains/chain-bridge-hub-polkadot/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-polkadot/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/bridges/chains/chain-bridge-hub-rococo/Cargo.toml b/bridges/chains/chain-bridge-hub-rococo/Cargo.toml index 5c9184703223..66848ba0e263 100644 --- a/bridges/chains/chain-bridge-hub-rococo/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-rococo/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/bridges/chains/chain-bridge-hub-westend/Cargo.toml b/bridges/chains/chain-bridge-hub-westend/Cargo.toml index 0b429ab9a0bd..24a196c1d70d 100644 --- a/bridges/chains/chain-bridge-hub-westend/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-westend/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/bridges/chains/chain-kusama/Cargo.toml b/bridges/chains/chain-kusama/Cargo.toml index ec45c1eddce5..aec4041f7d57 100644 --- a/bridges/chains/chain-kusama/Cargo.toml +++ b/bridges/chains/chain-kusama/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/bridges/chains/chain-polkadot-bulletin/Cargo.toml b/bridges/chains/chain-polkadot-bulletin/Cargo.toml index ea5f4d2e7759..aecf93142736 100644 --- a/bridges/chains/chain-polkadot-bulletin/Cargo.toml +++ b/bridges/chains/chain-polkadot-bulletin/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/bridges/chains/chain-rococo/Cargo.toml b/bridges/chains/chain-rococo/Cargo.toml index 49a1a397ee09..8a99267691dc 100644 --- a/bridges/chains/chain-rococo/Cargo.toml +++ b/bridges/chains/chain-rococo/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/bridges/chains/chain-westend/Cargo.toml b/bridges/chains/chain-westend/Cargo.toml index 5e27bc647bfc..cd6abe8abe6d 100644 --- a/bridges/chains/chain-westend/Cargo.toml +++ b/bridges/chains/chain-westend/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" repository.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/docs/contributor/CONTRIBUTING.md b/docs/contributor/CONTRIBUTING.md index 7d54b2681b41..d8f956b82d2d 100644 --- a/docs/contributor/CONTRIBUTING.md +++ b/docs/contributor/CONTRIBUTING.md @@ -82,6 +82,45 @@ Non "silent" PRs must come with documentation in the form of a `.prdoc` file. See more about `prdoc` [here](./prdoc.md) +## Crate Configuration `Cargo.toml` + +The Polkadot SDK uses many conventions when configuring a crate. Watch out for these things when you +are creating a new crate. + +### Is the Crate chain-specific? + +Chain-specific crates, for example +[`bp-bridge-hub-rococo`](https://github.com/paritytech/polkadot-sdk/blob/4014b9bf2bf8f74862f63e7114e5c78009529be5/bridges/chains/chain-bridge-hub-rococo/Cargo.toml#L10-L11) +, should not be released as part of the Polkadot-SDK umbrella crate. We have a custom metadata +attribute that is picked up by the [generate-umbrella.py](../../scripts/generate-umbrella.py) +script, that should be applied to all chain-specific crates like such: + +```toml +[package] +# Other stuff... + +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + +# Other stuff... +``` + +### Is the Crate a Test, Example or Fuzzer? + +Test or example crates, like +[`pallet-example-task`](https://github.com/paritytech/polkadot-sdk/blob/9b4acf27b869d7cbb07b03f0857763b8c8cc7566/substrate/frame/examples/tasks/Cargo.toml#L9) +, should not be released to crates.io. To ensure this, you must add `publish = false` to your +crate's `package` section: + +```toml +[package] +# Other stuff... + +publish = false + +# Other stuff... +``` + ## Helping out We use [labels](https://github.com/paritytech/polkadot-sdk/labels) to manage PRs and issues and communicate state of a diff --git a/polkadot/runtime/rococo/constants/Cargo.toml b/polkadot/runtime/rococo/constants/Cargo.toml index b67c36d71fd8..1d0adac44af4 100644 --- a/polkadot/runtime/rococo/constants/Cargo.toml +++ b/polkadot/runtime/rococo/constants/Cargo.toml @@ -6,6 +6,9 @@ authors.workspace = true edition.workspace = true license.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/polkadot/runtime/westend/constants/Cargo.toml b/polkadot/runtime/westend/constants/Cargo.toml index f9b99ea5284d..27d5b19b8e77 100644 --- a/polkadot/runtime/westend/constants/Cargo.toml +++ b/polkadot/runtime/westend/constants/Cargo.toml @@ -6,6 +6,9 @@ authors.workspace = true edition.workspace = true license.workspace = true +[package.metadata.polkadot-sdk] +exclude-from-umbrella = true + [lints] workspace = true diff --git a/prdoc/pr_5173.prdoc b/prdoc/pr_5173.prdoc new file mode 100644 index 000000000000..81ddcd9578ba --- /dev/null +++ b/prdoc/pr_5173.prdoc @@ -0,0 +1,39 @@ +title: "Umbrella crate: exclude chain-specific crates" + +doc: + - audience: Runtime Dev + description: | + The `polkadot-sdk` umbrella crate does now not contain chain-specific crates. The reasoning is + that the SDK should be mostly chain-agnostic. Please check out + [psvm](https://github.com/paritytech/psvm) to select matching version numbers for chain- + specific crates. + +crates: + - name: polkadot-sdk + bump: major + - name: rococo-runtime-constants + bump: none + - name: westend-runtime-constants + bump: none + - name: bp-asset-hub-rococo + bump: none + - name: bp-asset-hub-westend + bump: none + - name: bp-bridge-hub-cumulus + bump: none + - name: bp-bridge-hub-kusama + bump: none + - name: bp-bridge-hub-polkadot + bump: none + - name: bp-bridge-hub-rococo + bump: none + - name: bp-bridge-hub-westend + bump: none + - name: bp-kusama + bump: none + - name: bp-polkadot-bulletin + bump: none + - name: bp-rococo + bump: none + - name: bp-westend + bump: none diff --git a/scripts/generate-umbrella.py b/scripts/generate-umbrella.py index a5f7d3b92567..3293c30bc828 100644 --- a/scripts/generate-umbrella.py +++ b/scripts/generate-umbrella.py @@ -24,12 +24,13 @@ """ def exclude(crate): name = crate.name - if crate.metadata.get("polkadot-sdk.skip-umbrella", False): + if crate.metadata.get("polkadot-sdk.exclude-from-umbrella", False): return True # No fuzzers or examples: if "example" in name or name.endswith("fuzzer"): return True + # No runtime crates: if name.endswith("-runtime"): # Note: this is a bit hacky. We should use custom crate metadata instead. diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 8d85e26d8fe7..65ff9a81e474 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -10,25 +10,14 @@ std = [ "asset-test-utils?/std", "assets-common?/std", "binary-merkle-tree?/std", - "bp-asset-hub-rococo?/std", - "bp-asset-hub-westend?/std", - "bp-bridge-hub-cumulus?/std", - "bp-bridge-hub-kusama?/std", - "bp-bridge-hub-polkadot?/std", - "bp-bridge-hub-rococo?/std", - "bp-bridge-hub-westend?/std", "bp-header-chain?/std", - "bp-kusama?/std", "bp-messages?/std", "bp-parachains?/std", - "bp-polkadot-bulletin?/std", "bp-polkadot-core?/std", "bp-polkadot?/std", "bp-relayers?/std", - "bp-rococo?/std", "bp-runtime?/std", "bp-test-utils?/std", - "bp-westend?/std", "bp-xcm-bridge-hub-router?/std", "bp-xcm-bridge-hub?/std", "bridge-hub-common?/std", @@ -171,7 +160,6 @@ std = [ "polkadot-runtime-metrics?/std", "polkadot-runtime-parachains?/std", "polkadot-sdk-frame?/std", - "rococo-runtime-constants?/std", "sc-executor?/std", "slot-range-helper?/std", "snowbridge-beacon-primitives?/std", @@ -239,7 +227,6 @@ std = [ "staging-xcm?/std", "substrate-bip39?/std", "testnet-parachains-constants?/std", - "westend-runtime-constants?/std", "xcm-runtime-apis?/std", ] runtime-benchmarks = [ @@ -543,25 +530,14 @@ with-tracing = [ runtime = [ "assets-common", "binary-merkle-tree", - "bp-asset-hub-rococo", - "bp-asset-hub-westend", - "bp-bridge-hub-cumulus", - "bp-bridge-hub-kusama", - "bp-bridge-hub-polkadot", - "bp-bridge-hub-rococo", - "bp-bridge-hub-westend", "bp-header-chain", - "bp-kusama", "bp-messages", "bp-parachains", "bp-polkadot", - "bp-polkadot-bulletin", "bp-polkadot-core", "bp-relayers", - "bp-rococo", "bp-runtime", "bp-test-utils", - "bp-westend", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", @@ -706,7 +682,6 @@ runtime = [ "polkadot-runtime-parachains", "polkadot-sdk-frame", "polkadot-sdk-frame?/runtime", - "rococo-runtime-constants", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", @@ -777,7 +752,6 @@ runtime = [ "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", - "westend-runtime-constants", "xcm-procedural", "xcm-runtime-apis", ] @@ -803,51 +777,11 @@ path = "../substrate/utils/binary-merkle-tree" default-features = false optional = true -[dependencies.bp-asset-hub-rococo] -path = "../bridges/chains/chain-asset-hub-rococo" -default-features = false -optional = true - -[dependencies.bp-asset-hub-westend] -path = "../bridges/chains/chain-asset-hub-westend" -default-features = false -optional = true - -[dependencies.bp-bridge-hub-cumulus] -path = "../bridges/chains/chain-bridge-hub-cumulus" -default-features = false -optional = true - -[dependencies.bp-bridge-hub-kusama] -path = "../bridges/chains/chain-bridge-hub-kusama" -default-features = false -optional = true - -[dependencies.bp-bridge-hub-polkadot] -path = "../bridges/chains/chain-bridge-hub-polkadot" -default-features = false -optional = true - -[dependencies.bp-bridge-hub-rococo] -path = "../bridges/chains/chain-bridge-hub-rococo" -default-features = false -optional = true - -[dependencies.bp-bridge-hub-westend] -path = "../bridges/chains/chain-bridge-hub-westend" -default-features = false -optional = true - [dependencies.bp-header-chain] path = "../bridges/primitives/header-chain" default-features = false optional = true -[dependencies.bp-kusama] -path = "../bridges/chains/chain-kusama" -default-features = false -optional = true - [dependencies.bp-messages] path = "../bridges/primitives/messages" default-features = false @@ -863,11 +797,6 @@ path = "../bridges/chains/chain-polkadot" default-features = false optional = true -[dependencies.bp-polkadot-bulletin] -path = "../bridges/chains/chain-polkadot-bulletin" -default-features = false -optional = true - [dependencies.bp-polkadot-core] path = "../bridges/primitives/polkadot-core" default-features = false @@ -878,11 +807,6 @@ path = "../bridges/primitives/relayers" default-features = false optional = true -[dependencies.bp-rococo] -path = "../bridges/chains/chain-rococo" -default-features = false -optional = true - [dependencies.bp-runtime] path = "../bridges/primitives/runtime" default-features = false @@ -893,11 +817,6 @@ path = "../bridges/primitives/test-utils" default-features = false optional = true -[dependencies.bp-westend] -path = "../bridges/chains/chain-westend" -default-features = false -optional = true - [dependencies.bp-xcm-bridge-hub] path = "../bridges/primitives/xcm-bridge-hub" default-features = false @@ -1613,11 +1532,6 @@ path = "../substrate/frame" default-features = false optional = true -[dependencies.rococo-runtime-constants] -path = "../polkadot/runtime/rococo/constants" -default-features = false -optional = true - [dependencies.sc-chain-spec-derive] path = "../substrate/client/chain-spec/derive" default-features = false @@ -1968,11 +1882,6 @@ path = "../polkadot/node/gum/proc-macro" default-features = false optional = true -[dependencies.westend-runtime-constants] -path = "../polkadot/runtime/westend/constants" -default-features = false -optional = true - [dependencies.xcm-procedural] path = "../polkadot/xcm/procedural" default-features = false diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 58a5691961d9..07f1cbad1262 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -23,42 +23,10 @@ pub use assets_common; #[cfg(feature = "binary-merkle-tree")] pub use binary_merkle_tree; -/// Primitives of AssetHubRococo parachain runtime. -#[cfg(feature = "bp-asset-hub-rococo")] -pub use bp_asset_hub_rococo; - -/// Primitives of AssetHubWestend parachain runtime. -#[cfg(feature = "bp-asset-hub-westend")] -pub use bp_asset_hub_westend; - -/// Primitives for BridgeHub parachain runtimes. -#[cfg(feature = "bp-bridge-hub-cumulus")] -pub use bp_bridge_hub_cumulus; - -/// Primitives of BridgeHubKusama parachain runtime. -#[cfg(feature = "bp-bridge-hub-kusama")] -pub use bp_bridge_hub_kusama; - -/// Primitives of BridgeHubPolkadot parachain runtime. -#[cfg(feature = "bp-bridge-hub-polkadot")] -pub use bp_bridge_hub_polkadot; - -/// Primitives of BridgeHubRococo parachain runtime. -#[cfg(feature = "bp-bridge-hub-rococo")] -pub use bp_bridge_hub_rococo; - -/// Primitives of BridgeHubWestend parachain runtime. -#[cfg(feature = "bp-bridge-hub-westend")] -pub use bp_bridge_hub_westend; - /// A common interface for describing what a bridge pallet should be able to do. #[cfg(feature = "bp-header-chain")] pub use bp_header_chain; -/// Primitives of Kusama runtime. -#[cfg(feature = "bp-kusama")] -pub use bp_kusama; - /// Primitives of messages module. #[cfg(feature = "bp-messages")] pub use bp_messages; @@ -71,10 +39,6 @@ pub use bp_parachains; #[cfg(feature = "bp-polkadot")] pub use bp_polkadot; -/// Primitives of Polkadot Bulletin chain runtime. -#[cfg(feature = "bp-polkadot-bulletin")] -pub use bp_polkadot_bulletin; - /// Primitives of Polkadot-like runtime. #[cfg(feature = "bp-polkadot-core")] pub use bp_polkadot_core; @@ -83,10 +47,6 @@ pub use bp_polkadot_core; #[cfg(feature = "bp-relayers")] pub use bp_relayers; -/// Primitives of Rococo runtime. -#[cfg(feature = "bp-rococo")] -pub use bp_rococo; - /// Primitives that may be used at (bridges) runtime level. #[cfg(feature = "bp-runtime")] pub use bp_runtime; @@ -95,10 +55,6 @@ pub use bp_runtime; #[cfg(feature = "bp-test-utils")] pub use bp_test_utils; -/// Primitives of Westend runtime. -#[cfg(feature = "bp-westend")] -pub use bp_westend; - /// Primitives of the xcm-bridge-hub pallet. #[cfg(feature = "bp-xcm-bridge-hub")] pub use bp_xcm_bridge_hub; @@ -967,10 +923,6 @@ pub use polkadot_statement_distribution; #[cfg(feature = "polkadot-statement-table")] pub use polkadot_statement_table; -/// Constants used throughout the Rococo network. -#[cfg(feature = "rococo-runtime-constants")] -pub use rococo_runtime_constants; - /// Collection of allocator implementations. #[cfg(feature = "sc-allocator")] pub use sc_allocator; @@ -1552,10 +1504,6 @@ pub use tracing_gum; #[cfg(feature = "tracing-gum-proc-macro")] pub use tracing_gum_proc_macro; -/// Constants used throughout the Westend network. -#[cfg(feature = "westend-runtime-constants")] -pub use westend_runtime_constants; - /// Test kit to emulate XCM program execution. #[cfg(feature = "xcm-emulator")] pub use xcm_emulator; From 711d91aaabb9687cd26904443c23706cd4dbd1ee Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Wed, 7 Aug 2024 18:03:26 +0100 Subject: [PATCH 075/382] frame-omni-bencher short checks (#5268) - Part of https://github.com/paritytech/ci_cd/issues/1006 - Closes: https://github.com/paritytech/ci_cd/issues/1010 - Related: https://github.com/paritytech/polkadot-sdk/pull/4405 - Possibly affecting how frame-omni-bencher works on different runtimes: https://github.com/paritytech/polkadot-sdk/pull/5083 Currently works in parallel with gitlab short benchmarks. Triggered only by adding `GHA-migration` label to assure smooth transition (kind of feature-flag). Later when tested on random PRs we'll remove the gitlab and turn on by default these tests --------- Co-authored-by: Oliver Tale-Yazdi --- .github/workflows/check-changed-files.yml | 57 ------------- .../workflows/check-frame-omni-bencher.yml | 85 +++++++++++++++++++ .../reusable-check-changed-files.yml | 59 +++++++++++++ .github/workflows/tests-linux-stable.yml | 8 +- .github/workflows/tests.yml | 10 +-- 5 files changed, 153 insertions(+), 66 deletions(-) delete mode 100644 .github/workflows/check-changed-files.yml create mode 100644 .github/workflows/check-frame-omni-bencher.yml create mode 100644 .github/workflows/reusable-check-changed-files.yml diff --git a/.github/workflows/check-changed-files.yml b/.github/workflows/check-changed-files.yml deleted file mode 100644 index 657c05cd047d..000000000000 --- a/.github/workflows/check-changed-files.yml +++ /dev/null @@ -1,57 +0,0 @@ -# Reusable workflow to perform checks and generate conditions for other workflows. -# Currently it checks if any Rust (build-related) file is changed -# and if the current (caller) workflow file is changed. -# Example: -# -# jobs: -# changes: -# permissions: -# pull-requests: read -# uses: ./.github/workflows/check-changed-files.yml -# some-job: -# needs: changes -# if: ${{ needs.changes.outputs.rust }} -# ....... - -name: Check changes files - -on: - workflow_call: - # Map the workflow outputs to job outputs - outputs: - rust: - value: ${{ jobs.changes.outputs.rust }} - description: 'true if any of the build-related OR current (caller) workflow files have changed' - current-workflow: - value: ${{ jobs.changes.outputs.current-workflow }} - description: 'true if current (caller) workflow file has changed' - -jobs: - changes: - runs-on: ubuntu-latest - permissions: - pull-requests: read - outputs: - # true if current workflow (caller) file is changed - rust: ${{ steps.filter.outputs.rust == 'true' || steps.filter.outputs.current-workflow == 'true' }} - current-workflow: ${{ steps.filter.outputs.current-workflow }} - steps: - - id: current-file - run: echo "current-workflow-file=$(echo ${{ github.workflow_ref }} | sed -nE "s/.*(\.github\/workflows\/[a-zA-Z0-9_-]*\.y[a]?ml)@refs.*/\1/p")" >> $GITHUB_OUTPUT - - run: echo "${{ steps.current-file.outputs.current-workflow-file }}" - # For pull requests it's not necessary to checkout the code - - id: filter - uses: dorny/paths-filter@v3 - with: - predicate-quantifier: 'every' - # current-workflow - check if the current (caller) workflow file is changed - # rust - check if any Rust (build-related) file is changed - filters: | - current-workflow: - - '${{ steps.current-file.outputs.current-workflow-file }}' - rust: - - '**/*' - - '!.github/**/*' - - '!prdoc/**/*' - - '!docs/**/*' - # \ No newline at end of file diff --git a/.github/workflows/check-frame-omni-bencher.yml b/.github/workflows/check-frame-omni-bencher.yml new file mode 100644 index 000000000000..e9db2d912979 --- /dev/null +++ b/.github/workflows/check-frame-omni-bencher.yml @@ -0,0 +1,85 @@ +name: Short benchmarks (frame-omni-bencher) + +on: + push: + branches: + - master + pull_request: + types: [ opened, synchronize, reopened, ready_for_review, labeled ] + merge_group: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + ARTIFACTS_NAME: frame-omni-bencher-artifacts + +jobs: + changes: + # TODO: remove once migration is complete or this workflow is fully stable + if: contains(github.event.label.name, 'GHA-migration') + permissions: + pull-requests: read + uses: ./.github/workflows/reusable-check-changed-files.yml + + set-image: + # GitHub Actions allows using 'env' in a container context. + # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 + # This workaround sets the container image for each job using 'set-image' job output. + runs-on: ubuntu-latest + needs: changes + if: ${{ needs.changes.outputs.rust }} + outputs: + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT + + run-frame-omni-bencher: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [ set-image, changes ] # , build-frame-omni-bencher ] + if: ${{ needs.changes.outputs.rust }} + timeout-minutes: 30 + strategy: + fail-fast: false # keep running other workflows even if one fails, to see the logs of all possible failures + matrix: + runtime: + [ + westend-runtime, + rococo-runtime, + asset-hub-rococo-runtime, + asset-hub-westend-runtime, + bridge-hub-rococo-runtime, + bridge-hub-westend-runtime, + collectives-westend-runtime, + coretime-rococo-runtime, + coretime-westend-runtime, + people-rococo-runtime, + people-westend-runtime, + glutton-westend-runtime, + ] + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + PACKAGE_NAME: ${{ matrix.runtime }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: script + run: | + RUNTIME_BLOB_NAME=$(echo $PACKAGE_NAME | sed 's/-/_/g').compact.compressed.wasm + RUNTIME_BLOB_PATH=./target/release/wbuild/$PACKAGE_NAME/$RUNTIME_BLOB_NAME + forklift cargo build --release --locked -p $PACKAGE_NAME -p frame-omni-bencher --features runtime-benchmarks + echo "Running short benchmarking for PACKAGE_NAME=$PACKAGE_NAME and RUNTIME_BLOB_PATH=$RUNTIME_BLOB_PATH" + ls -lrt $RUNTIME_BLOB_PATH + ./target/release/frame-omni-bencher v1 benchmark pallet --runtime $RUNTIME_BLOB_PATH --all --steps 2 --repeat 1 + confirm-frame-omni-benchers-passed: + runs-on: ubuntu-latest + name: All benchmarks passed + needs: run-frame-omni-bencher + steps: + - run: echo '### Good job! All the benchmarks passed 🚀' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/reusable-check-changed-files.yml b/.github/workflows/reusable-check-changed-files.yml new file mode 100644 index 000000000000..47f0620439c7 --- /dev/null +++ b/.github/workflows/reusable-check-changed-files.yml @@ -0,0 +1,59 @@ +# Reusable workflow to perform checks and generate conditions for other workflows. +# Currently it checks if any Rust (build-related) file is changed +# and if the current (caller) workflow file is changed. +# Example: +# +# jobs: +# changes: +# permissions: +# pull-requests: read +# uses: ./.github/workflows/reusable-check-changed-files.yml +# some-job: +# needs: changes +# if: ${{ needs.changes.outputs.rust }} +# ....... + +name: Check changes files + +on: + workflow_call: + # Map the workflow outputs to job outputs + outputs: + rust: + value: ${{ jobs.changes.outputs.rust }} + description: "true if any of the build-related OR current (caller) workflow files have changed" + current-workflow: + value: ${{ jobs.changes.outputs.current-workflow }} + description: "true if current (caller) workflow file has changed" + +jobs: + changes: + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + # true if current workflow (caller) file is changed + rust: ${{ steps.filter.outputs.rust == 'true' || steps.filter.outputs.current-workflow == 'true' }} + current-workflow: ${{ steps.filter.outputs.current-workflow }} + steps: + - id: current-file + run: echo "current-workflow-file=$(echo ${{ github.workflow_ref }} | sed -nE "s/.*(\.github\/workflows\/[a-zA-Z0-9_-]*\.y[a]?ml)@refs.*/\1/p")" >> $GITHUB_OUTPUT + - run: echo "${{ steps.current-file.outputs.current-workflow-file }}" + # For pull requests it's not necessary to checkout the code + - name: Checkout + if: github.event_name != 'pull_request' + uses: actions/checkout@v4 + - id: filter + uses: dorny/paths-filter@v3 + with: + predicate-quantifier: "every" + # current-workflow - check if the current (caller) workflow file is changed + # rust - check if any Rust (build-related) file is changed + filters: | + current-workflow: + - '${{ steps.current-file.outputs.current-workflow-file }}' + rust: + - '**/*' + - '!.github/**/*' + - '!prdoc/**/*' + - '!docs/**/*' diff --git a/.github/workflows/tests-linux-stable.yml b/.github/workflows/tests-linux-stable.yml index 55addf11de06..da4bb40a2e78 100644 --- a/.github/workflows/tests-linux-stable.yml +++ b/.github/workflows/tests-linux-stable.yml @@ -6,7 +6,7 @@ on: branches: - master pull_request: - types: [opened, synchronize, reopened, ready_for_review] + types: [ opened, synchronize, reopened, ready_for_review ] merge_group: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -16,7 +16,7 @@ jobs: changes: permissions: pull-requests: read - uses: ./.github/workflows/check-changed-files.yml + uses: ./.github/workflows/reusable-check-changed-files.yml set-image: # GitHub Actions allows using 'env' in a container context. @@ -34,7 +34,7 @@ jobs: run: cat .github/env >> $GITHUB_OUTPUT test-linux-stable-int: - needs: [set-image, changes] + needs: [ set-image, changes ] if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 60 @@ -55,7 +55,7 @@ jobs: # https://github.com/paritytech/ci_cd/issues/864 test-linux-stable-runtime-benchmarks: - needs: [set-image, changes] + needs: [ set-image, changes ] if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 60 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a413d3306159..1be2dd7921e0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,7 +5,7 @@ on: branches: - master pull_request: - types: [opened, synchronize, reopened, ready_for_review] + types: [ opened, synchronize, reopened, ready_for_review ] merge_group: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -15,7 +15,7 @@ jobs: changes: permissions: pull-requests: read - uses: ./.github/workflows/check-changed-files.yml + uses: ./.github/workflows/reusable-check-changed-files.yml set-image: # GitHub Actions allows using 'env' in a container context. @@ -31,7 +31,7 @@ jobs: run: cat .github/env >> $GITHUB_OUTPUT quick-benchmarks: - needs: [set-image, changes] + needs: [ set-image, changes ] if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 60 @@ -50,7 +50,7 @@ jobs: # cf https://github.com/paritytech/polkadot-sdk/issues/1652 test-syscalls: - needs: [set-image, changes] + needs: [ set-image, changes ] if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 60 @@ -75,7 +75,7 @@ jobs: # fi cargo-check-all-benches: - needs: [set-image, changes] + needs: [ set-image, changes ] if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 60 From eb0a9e593fb6a0f2bbfdb75602a51f4923995529 Mon Sep 17 00:00:00 2001 From: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Date: Thu, 8 Aug 2024 10:47:24 +0200 Subject: [PATCH 076/382] Fix Weight Annotation (#5275) https://github.com/paritytech/polkadot-sdk/pull/4527/files#r1706673828 --- substrate/frame/assets/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index e9b9a7b1e3cc..6f8ad0c29393 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -1775,7 +1775,7 @@ pub mod pallet { /// (false), or transfer everything except at least the minimum balance, which will /// guarantee to keep the sender asset account alive (true). #[pallet::call_index(32)] - #[pallet::weight(T::WeightInfo::refund_other())] + #[pallet::weight(T::WeightInfo::transfer_all())] pub fn transfer_all( origin: OriginFor, id: T::AssetIdParameter, From 12539e7a931e82a040e74c84e413baa712ecd638 Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Thu, 8 Aug 2024 17:20:20 +0200 Subject: [PATCH 077/382] [ci] Add test-linux-stable jobs GHA (#4897) PR adds github-action for jobs test-linux-stable-oldkernel. PR waits the latest release of forklift. cc https://github.com/paritytech/ci_cd/issues/939 cc https://github.com/paritytech/ci_cd/issues/1006 --------- Co-authored-by: Maksym H <1177472+mordamax@users.noreply.github.com> --- .github/workflows/tests-linux-stable.yml | 63 ++++++++++++++++++++++-- .gitlab/pipeline/test.yml | 2 - 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests-linux-stable.yml b/.github/workflows/tests-linux-stable.yml index da4bb40a2e78..4a13f5318f7d 100644 --- a/.github/workflows/tests-linux-stable.yml +++ b/.github/workflows/tests-linux-stable.yml @@ -6,7 +6,7 @@ on: branches: - master pull_request: - types: [ opened, synchronize, reopened, ready_for_review ] + types: [opened, synchronize, reopened, ready_for_review, labeled] merge_group: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -14,6 +14,8 @@ concurrency: jobs: changes: + # TODO: remove once migration is complete or this workflow is fully stable + if: contains(github.event.label.name, 'GHA-migration') permissions: pull-requests: read uses: ./.github/workflows/reusable-check-changed-files.yml @@ -34,7 +36,7 @@ jobs: run: cat .github/env >> $GITHUB_OUTPUT test-linux-stable-int: - needs: [ set-image, changes ] + needs: [set-image, changes] if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 60 @@ -51,11 +53,11 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: script - run: WASM_BUILD_NO_COLOR=1 time forklift cargo test -p staging-node-cli --release --locked -- --ignored + run: WASM_BUILD_NO_COLOR=1 forklift cargo test -p staging-node-cli --release --locked -- --ignored # https://github.com/paritytech/ci_cd/issues/864 test-linux-stable-runtime-benchmarks: - needs: [ set-image, changes ] + needs: [set-image, changes] if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 60 @@ -70,4 +72,55 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: script - run: time forklift cargo nextest run --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet + run: forklift cargo nextest run --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet + + test-linux-stable: + needs: [set-image, changes] + if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ matrix.runners }} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + partition: [1/3, 2/3, 3/3] + runners: [arc-runners-polkadot-sdk-beefy, oldlinux] + container: + image: ${{ needs.set-image.outputs.IMAGE }} + # needed for tests that use unshare syscall + options: --security-opt seccomp=unconfined + env: + RUST_TOOLCHAIN: stable + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: script + run: | + # Fixes "detected dubious ownership" error in the ci + git config --global --add safe.directory '*' + forklift cargo nextest run \ + --workspace \ + --locked \ + --release \ + --no-fail-fast \ + --features try-runtime,experimental,riscv,ci-only-tests \ + --partition count:${{ matrix.partition }} + # run runtime-api tests with `enable-staging-api` feature on the 1st node + - name: runtime-api tests + if: ${{ matrix.partition == '1/3' }} + run: forklift cargo nextest run -p sp-api-test --features enable-staging-api + + confirm-required-jobs-passed: + runs-on: ubuntu-latest + name: All tests passed + # If any new job gets added, be sure to add it to this array + needs: + [ + test-linux-stable-int, + test-linux-stable-runtime-benchmarks, + test-linux-stable, + ] + steps: + - run: echo '### Good job! All the tests passed 🚀' >> $GITHUB_STEP_SUMMARY diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index 0103c6b76a2d..319c95ad6112 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -110,8 +110,6 @@ test-linux-stable-codecov: codecovcli -v do-upload -f target/coverage/result/report-${CI_NODE_INDEX}.lcov --disable-search -t ${CODECOV_TOKEN} -r paritytech/polkadot-sdk --commit-sha ${CI_COMMIT_SHA} --fail-on-error --git-service github; fi - # - test-linux-stable: stage: test extends: From 2993b0008e2ec4040be91868bf5f48a892508c3a Mon Sep 17 00:00:00 2001 From: Egor_P Date: Fri, 9 Aug 2024 10:01:55 +0200 Subject: [PATCH 078/382] Add stable release tag as an input parameter (#5282) This PR adds the possibility to set the docker stable release tag as an input parameter to the produced docker images, so that it matches with the release version --- .github/scripts/common/lib.sh | 13 ++++++ .../workflows/release-50_publish-docker.yml | 42 +++++++++++++++---- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/.github/scripts/common/lib.sh b/.github/scripts/common/lib.sh index 33ef2d3e7eda..bfb3120ad9bb 100755 --- a/.github/scripts/common/lib.sh +++ b/.github/scripts/common/lib.sh @@ -315,6 +315,7 @@ function import_gpg_keys() { ) & done wait + gpg -k $SEC } # Check the GPG signature for a given binary @@ -457,3 +458,15 @@ function get_polkadot_node_version_from_code() { # Remove the semicolon sed 's/;//g' } + +validate_stable_tag() { + tag="$1" + pattern='^stable[0-9]+(-[0-9]+)?$' + + if [[ $tag =~ $pattern ]]; then + echo $tag + else + echo "The input '$tag' does not match the pattern." + exit 1 + fi +} diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index cda10f2ebf15..f09ecf1c7998 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -45,7 +45,7 @@ on: type: string default: docker.io - # The owner is often the same than the Docker Hub username but does ont have to be. + # The owner is often the same as the Docker Hub username but does ont have to be. # In our case, it is not. owner: description: Owner of the container image repo @@ -58,6 +58,10 @@ on: default: v0.9.18 required: true + stable_tag: + description: Tag matching the actual stable release version in the format stableYYMM or stableYYMM-X for patch releases + required: true + permissions: contents: write @@ -74,6 +78,29 @@ env: VERSION: ${{ inputs.version }} jobs: + validate-inputs: + runs-on: ubuntu-latest + outputs: + stable_tag: ${{ steps.validate_inputs.outputs.stable_tag }} + + steps: + - name: Checkout sources + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Validate inputs + id: validate_inputs + run: | + . ./.github/scripts/common/lib.sh + + VERSION=$(filter_version_from_input "${{ inputs.version }}") + echo "VERSION=${VERSION}" >> $GITHUB_ENV + + RELEASE_ID=$(check_release_id "${{ inputs.release_id }}") + echo "RELEASE_ID=${RELEASE_ID}" >> $GITHUB_ENV + + STABLE_TAG=$(validate_stable_tag ${{ inputs.stable_tag }}) + echo "stable_tag=${STABLE_TAG}" >> $GITHUB_OUTPUT + fetch-artifacts: # this job will be triggered for the polkadot-parachain rc and release or polkadot rc image build if: ${{ inputs.binary == 'polkadot-parachain' || inputs.binary == 'chain-spec-builder' || inputs.image_type == 'rc' }} runs-on: ubuntu-latest @@ -102,9 +129,6 @@ jobs: run: | . ./.github/scripts/common/lib.sh - VERSION=$(filter_version_from_input "${{ inputs.version }}") - echo "VERSION=${VERSION}" >> $GITHUB_ENV - fetch_release_artifacts_from_s3 - name: Fetch chain-spec-builder rc artifacts or release artifacts based on release id @@ -112,7 +136,7 @@ jobs: if: ${{ env.EVENT_NAME == 'workflow_dispatch' && inputs.binary == 'chain-spec-builder' }} run: | . ./.github/scripts/common/lib.sh - RELEASE_ID=$(check_release_id "${{ inputs.release_id }}") + fetch_release_artifacts - name: Upload artifacts @@ -124,7 +148,7 @@ jobs: build-container: # this job will be triggered for the polkadot-parachain rc and release or polkadot rc image build if: ${{ inputs.binary == 'polkadot-parachain' || inputs.binary == 'chain-spec-builder' || inputs.image_type == 'rc' }} runs-on: ubuntu-latest - needs: fetch-artifacts + needs: [fetch-artifacts, validate-inputs] environment: release steps: @@ -179,7 +203,7 @@ jobs: release=$( echo $VERSION | cut -f1 -d- ) echo "tag=latest" >> $GITHUB_OUTPUT echo "release=${release}" >> $GITHUB_OUTPUT - echo "stable=stable" >> $GITHUB_OUTPUT + echo "stable=${{ needs.validate-inputs.outputs.stable_tag }}" >> $GITHUB_OUTPUT - name: Build Injected Container image for polkadot rc or chain-spec-builder if: ${{ env.BINARY == 'polkadot' || env.BINARY == 'chain-spec-builder' }} @@ -257,7 +281,7 @@ jobs: build-polkadot-release-container: # this job will be triggered for polkadot release build if: ${{ inputs.binary == 'polkadot' && inputs.image_type == 'release' }} runs-on: ubuntu-latest - needs: fetch-latest-debian-package-version + needs: [fetch-latest-debian-package-version, validate-inputs] environment: release steps: - name: Checkout sources @@ -295,7 +319,7 @@ jobs: # TODO: The owner should be used below but buildx does not resolve the VARs # TODO: It would be good to get rid of this GHA that we don't really need. tags: | - parity/polkadot:stable + parity/polkadot:${{ needs.validate-inputs.outputs.stable_tag }} parity/polkadot:latest parity/polkadot:${{ needs.fetch-latest-debian-package-version.outputs.polkadot_container_tag }} build-args: | From 87280eb52537a355c67c9b9cee8ff1f97d02d67a Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Fri, 9 Aug 2024 13:03:07 +0200 Subject: [PATCH 079/382] Synchronize templates through PRs, instead of pushes (#5291) Despite what we had in the [original request](https://github.com/paritytech/polkadot-sdk/issues/3155#issuecomment-1979037109), I'm proposing a change to open a PR to the destination template repositories instead of pushing the code. This will give it a chance to run through the destination CI before making changes, and to set stricter branch protection in the destination repos. --- .github/workflows/misc-sync-templates.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/misc-sync-templates.yml b/.github/workflows/misc-sync-templates.yml index d22dc8724f37..c06beb5e98eb 100644 --- a/.github/workflows/misc-sync-templates.yml +++ b/.github/workflows/misc-sync-templates.yml @@ -166,9 +166,13 @@ jobs: title: "[Don't merge] Update the ${{ matrix.template }} template to ${{ github.event.inputs.stable_release_branch }}" body: "The template has NOT been successfully built and needs to be inspected." branch: "update-template/${{ github.event.inputs.stable_release_branch }}" - - name: Push changes - run: | - git add -A . - git commit --allow-empty -m "Update to ${{ github.event.inputs.stable_release_branch }} triggered by ${{ github.event_name }}" - git push - working-directory: "${{ env.template-path }}" + - name: Create PR on success + uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v5 + with: + path: "${{ env.template-path }}" + token: ${{ steps.app_token.outputs.token }} + add-paths: | + ./* + title: "Update the ${{ matrix.template }} template to ${{ github.event.inputs.stable_release_branch }}" + body: "This synchronizes the template to the ${{ github.event.inputs.stable_release_branch }} branch." + branch: "update-template/${{ github.event.inputs.stable_release_branch }}" From b49509baf07a8938318e6e0b46cd266f935a936b Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Fri, 9 Aug 2024 15:45:35 +0300 Subject: [PATCH 080/382] Update BHR and BHW runtime versions (#5300) Updating the BHR and BHW runtime versions as a result of the changes in https://github.com/paritytech/polkadot-sdk/pull/5074/ --- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 76 +++++++++---------- .../bridge-hubs/bridge-hub-westend/src/lib.rs | 72 +++++++++--------- 2 files changed, 74 insertions(+), 74 deletions(-) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index c8face15627f..c65880771e08 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -219,7 +219,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-rococo"), impl_name: create_runtime_str!("bridge-hub-rococo"), authoring_version: 1, - spec_version: 1_015_000, + spec_version: 1_016_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5, @@ -1597,42 +1597,42 @@ mod tests { use bp_polkadot_core::SuffixedCommonSignedExtensionExt; sp_io::TestExternalities::default().execute_with(|| { - frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); - let payload: SignedExtra = ( - frame_system::CheckNonZeroSender::new(), - frame_system::CheckSpecVersion::new(), - frame_system::CheckTxVersion::new(), - frame_system::CheckGenesis::new(), - frame_system::CheckEra::from(Era::Immortal), - frame_system::CheckNonce::from(10), - frame_system::CheckWeight::new(), - pallet_transaction_payment::ChargeTransactionPayment::from(10), - BridgeRejectObsoleteHeadersAndMessages, - ( - bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), - bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(), - ), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), - frame_metadata_hash_extension::CheckMetadataHash::new(false), - ); - - // for BridgeHubRococo - { - let bhr_indirect_payload = bp_bridge_hub_rococo::SignedExtension::from_params( - VERSION.spec_version, - VERSION.transaction_version, - bp_runtime::TransactionEra::Immortal, - System::block_hash(BlockNumber::zero()), - 10, - 10, - (((), ()), ((), ())), - ); - assert_eq!(payload.encode().split_last().unwrap().1, bhr_indirect_payload.encode()); - assert_eq!( - payload.additional_signed().unwrap().encode().split_last().unwrap().1, - bhr_indirect_payload.additional_signed().unwrap().encode() - ) - } - }); + frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); + let payload: SignedExtra = ( + frame_system::CheckNonZeroSender::new(), + frame_system::CheckSpecVersion::new(), + frame_system::CheckTxVersion::new(), + frame_system::CheckGenesis::new(), + frame_system::CheckEra::from(Era::Immortal), + frame_system::CheckNonce::from(10), + frame_system::CheckWeight::new(), + pallet_transaction_payment::ChargeTransactionPayment::from(10), + BridgeRejectObsoleteHeadersAndMessages, + ( + bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), + bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(), + ), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + frame_metadata_hash_extension::CheckMetadataHash::new(false), + ); + + // for BridgeHubRococo + { + let bhr_indirect_payload = bp_bridge_hub_rococo::SignedExtension::from_params( + VERSION.spec_version, + VERSION.transaction_version, + bp_runtime::TransactionEra::Immortal, + System::block_hash(BlockNumber::zero()), + 10, + 10, + (((), ()), ((), ())), + ); + assert_eq!(payload.encode().split_last().unwrap().1, bhr_indirect_payload.encode()); + assert_eq!( + payload.additional_signed().unwrap().encode().split_last().unwrap().1, + bhr_indirect_payload.additional_signed().unwrap().encode() + ) + } + }); } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 7384bf2850fe..1c26742fd67f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -198,7 +198,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-westend"), impl_name: create_runtime_str!("bridge-hub-westend"), authoring_version: 1, - spec_version: 1_015_000, + spec_version: 1_016_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5, @@ -1281,40 +1281,40 @@ mod tests { use bp_polkadot_core::SuffixedCommonSignedExtensionExt; sp_io::TestExternalities::default().execute_with(|| { - frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); - let payload: SignedExtra = ( - frame_system::CheckNonZeroSender::new(), - frame_system::CheckSpecVersion::new(), - frame_system::CheckTxVersion::new(), - frame_system::CheckGenesis::new(), - frame_system::CheckEra::from(Era::Immortal), - frame_system::CheckNonce::from(10), - frame_system::CheckWeight::new(), - pallet_transaction_payment::ChargeTransactionPayment::from(10), - BridgeRejectObsoleteHeadersAndMessages, - ( - bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(), - ), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), - frame_metadata_hash_extension::CheckMetadataHash::new(false), - ); - - { - let bh_indirect_payload = bp_bridge_hub_westend::SignedExtension::from_params( - VERSION.spec_version, - VERSION.transaction_version, - bp_runtime::TransactionEra::Immortal, - System::block_hash(BlockNumber::zero()), - 10, - 10, - (((), ()), ((), ())), - ); - assert_eq!(payload.encode().split_last().unwrap().1, bh_indirect_payload.encode()); - assert_eq!( - payload.additional_signed().unwrap().encode().split_last().unwrap().1, - bh_indirect_payload.additional_signed().unwrap().encode() - ) - } - }); + frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); + let payload: SignedExtra = ( + frame_system::CheckNonZeroSender::new(), + frame_system::CheckSpecVersion::new(), + frame_system::CheckTxVersion::new(), + frame_system::CheckGenesis::new(), + frame_system::CheckEra::from(Era::Immortal), + frame_system::CheckNonce::from(10), + frame_system::CheckWeight::new(), + pallet_transaction_payment::ChargeTransactionPayment::from(10), + BridgeRejectObsoleteHeadersAndMessages, + ( + bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(), + ), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + frame_metadata_hash_extension::CheckMetadataHash::new(false), + ); + + { + let bh_indirect_payload = bp_bridge_hub_westend::SignedExtension::from_params( + VERSION.spec_version, + VERSION.transaction_version, + bp_runtime::TransactionEra::Immortal, + System::block_hash(BlockNumber::zero()), + 10, + 10, + (((), ()), ((), ())), + ); + assert_eq!(payload.encode().split_last().unwrap().1, bh_indirect_payload.encode()); + assert_eq!( + payload.additional_signed().unwrap().encode().split_last().unwrap().1, + bh_indirect_payload.additional_signed().unwrap().encode() + ) + } + }); } } From 32b87605182aabdc3e95aa1882ef7bca1ce34e46 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 9 Aug 2024 15:53:27 +0300 Subject: [PATCH 081/382] [tests] make emulated setup closer to ecosystem reality (#5301) In the real world, not all assets are sufficient. This aligns our emulated networks to that reality. Only DOT and USDT are sufficient "by default". --- .../assets/asset-hub-rococo/src/genesis.rs | 4 +-- .../assets/asset-hub-westend/src/genesis.rs | 9 ++++-- .../parachains/testing/penpal/src/genesis.rs | 4 ++- .../src/tests/reserve_transfer.rs | 2 +- .../asset-hub-rococo/src/tests/treasury.rs | 16 ++++++----- .../src/tests/fellowship_treasury.rs | 28 ++++++++----------- .../src/tests/reserve_transfer.rs | 2 +- .../asset-hub-westend/src/tests/treasury.rs | 28 ++++++++----------- .../runtimes/testing/penpal/src/xcm_config.rs | 4 +++ 9 files changed, 50 insertions(+), 47 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs index 82f86e2b32ef..5b70ed490c63 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs @@ -70,7 +70,7 @@ pub fn genesis() -> Storage { }, assets: asset_hub_rococo_runtime::AssetsConfig { assets: vec![ - (RESERVABLE_ASSET_ID, AssetHubRococoAssetOwner::get(), true, ED), + (RESERVABLE_ASSET_ID, AssetHubRococoAssetOwner::get(), false, ED), (USDT_ID, AssetHubRococoAssetOwner::get(), true, ED), ], ..Default::default() @@ -81,7 +81,7 @@ pub fn genesis() -> Storage { ( PenpalTeleportableAssetLocation::get(), PenpalSiblingSovereignAccount::get(), - true, + false, ED, ), ], diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs index fd84030ed13b..d20e059f9fea 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs @@ -21,7 +21,7 @@ use sp_core::{sr25519, storage::Storage}; use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, get_account_id_from_seed, PenpalSiblingSovereignAccount, PenpalTeleportableAssetLocation, RESERVABLE_ASSET_ID, - SAFE_XCM_VERSION, + SAFE_XCM_VERSION, USDT_ID, }; use parachains_common::{AccountId, Balance}; @@ -65,7 +65,10 @@ pub fn genesis() -> Storage { ..Default::default() }, assets: asset_hub_westend_runtime::AssetsConfig { - assets: vec![(RESERVABLE_ASSET_ID, AssetHubWestendAssetOwner::get(), true, ED)], + assets: vec![ + (RESERVABLE_ASSET_ID, AssetHubWestendAssetOwner::get(), false, ED), + (USDT_ID, AssetHubWestendAssetOwner::get(), true, ED), + ], ..Default::default() }, foreign_assets: asset_hub_westend_runtime::ForeignAssetsConfig { @@ -74,7 +77,7 @@ pub fn genesis() -> Storage { ( PenpalTeleportableAssetLocation::get(), PenpalSiblingSovereignAccount::get(), - true, + false, ED, ), ], diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs index 260875088bbc..38c94b34aa2e 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs @@ -22,7 +22,7 @@ use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, get_account_id_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, Balance}; -use penpal_runtime::xcm_config::{LocalReservableFromAssetHub, RelayLocation}; +use penpal_runtime::xcm_config::{LocalReservableFromAssetHub, RelayLocation, UsdtFromAssetHub}; // Penpal pub const PARA_ID_A: u32 = 2000; pub const PARA_ID_B: u32 = 2001; @@ -81,6 +81,8 @@ pub fn genesis(para_id: u32) -> Storage { (RelayLocation::get(), PenpalAssetOwner::get(), true, ED), // Sufficient AssetHub asset representation (LocalReservableFromAssetHub::get(), PenpalAssetOwner::get(), true, ED), + // USDT from Asset Hub + (UsdtFromAssetHub::get(), PenpalAssetOwner::get(), true, ED), ], ..Default::default() }, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs index 8b9fedcd4947..313fa953dd05 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs @@ -939,7 +939,7 @@ fn reserve_transfer_assets_from_system_para_to_para() { ); } -/// Reserve Transfers of a foreign asset and native asset from Parachain to System Para should +/// Reserve Transfers of a random asset and native asset from Parachain to System Para should /// work #[test] fn reserve_transfer_assets_from_para_to_system_para() { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs index f8190e11c51c..3320392b495d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs @@ -14,7 +14,10 @@ // limitations under the License. use crate::imports::*; -use emulated_integration_tests_common::accounts::{ALICE, BOB}; +use emulated_integration_tests_common::{ + accounts::{ALICE, BOB}, + USDT_ID, +}; use frame_support::{ dispatch::RawOrigin, sp_runtime::traits::Dispatchable, @@ -161,7 +164,6 @@ fn spend_roc_on_asset_hub() { #[test] fn create_and_claim_treasury_spend_in_usdt() { - const ASSET_ID: u32 = 1984; const SPEND_AMOUNT: u128 = 10_000_000; // treasury location from a sibling parachain. let treasury_location: Location = Location::new(1, PalletInstance(18)); @@ -175,7 +177,7 @@ fn create_and_claim_treasury_spend_in_usdt() { let asset_kind = VersionedLocatableAsset::V3 { location: asset_hub_location, asset_id: v3::AssetId::Concrete( - (v3::Junction::PalletInstance(50), v3::Junction::GeneralIndex(ASSET_ID.into())).into(), + (v3::Junction::PalletInstance(50), v3::Junction::GeneralIndex(USDT_ID.into())).into(), ), }; // treasury spend beneficiary. @@ -187,9 +189,9 @@ fn create_and_claim_treasury_spend_in_usdt() { type Assets = ::Assets; // USDT created at genesis, mint some assets to the treasury account. - assert_ok!(>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4)); + assert_ok!(>::mint_into(USDT_ID, &treasury_account, SPEND_AMOUNT * 4)); // beneficiary has zero balance. - assert_eq!(>::balance(ASSET_ID, &alice,), 0u128,); + assert_eq!(>::balance(USDT_ID, &alice,), 0u128,); }); Rococo::execute_with(|| { @@ -231,7 +233,7 @@ fn create_and_claim_treasury_spend_in_usdt() { AssetHubRococo, vec![ RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => { - id: id == &ASSET_ID, + id: id == &USDT_ID, from: from == &treasury_account, to: to == &alice, amount: amount == &SPEND_AMOUNT, @@ -241,7 +243,7 @@ fn create_and_claim_treasury_spend_in_usdt() { ] ); // beneficiary received the assets from the treasury. - assert_eq!(>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,); + assert_eq!(>::balance(USDT_ID, &alice,), SPEND_AMOUNT,); }); Rococo::execute_with(|| { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs index 15f4fe33bddc..9520659712fc 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs @@ -14,15 +14,17 @@ // limitations under the License. use crate::imports::*; -use emulated_integration_tests_common::accounts::{ALICE, BOB}; -use frame_support::traits::fungibles::{Create, Inspect, Mutate}; +use emulated_integration_tests_common::{ + accounts::{ALICE, BOB}, + USDT_ID, +}; +use frame_support::traits::fungibles::{Inspect, Mutate}; use polkadot_runtime_common::impls::VersionedLocatableAsset; use xcm_executor::traits::ConvertLocation; #[test] fn create_and_claim_treasury_spend() { - const ASSET_ID: u32 = 1984; - const SPEND_AMOUNT: u128 = 1_000_000; + const SPEND_AMOUNT: u128 = 1_000_000_000; // treasury location from a sibling parachain. let treasury_location: Location = Location::new(1, [Parachain(CollectivesWestend::para_id().into()), PalletInstance(65)]); @@ -34,7 +36,7 @@ fn create_and_claim_treasury_spend() { // asset kind to be spent from the treasury. let asset_kind = VersionedLocatableAsset::V4 { location: asset_hub_location, - asset_id: AssetId((PalletInstance(50), GeneralIndex(ASSET_ID.into())).into()), + asset_id: AssetId((PalletInstance(50), GeneralIndex(USDT_ID.into())).into()), }; // treasury spend beneficiary. let alice: AccountId = Westend::account_id_of(ALICE); @@ -44,16 +46,10 @@ fn create_and_claim_treasury_spend() { AssetHubWestend::execute_with(|| { type Assets = ::Assets; - // create an asset class and mint some assets to the treasury account. - assert_ok!(>::create( - ASSET_ID, - treasury_account.clone(), - true, - SPEND_AMOUNT / 2 - )); - assert_ok!(>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4)); + // USDT created at genesis, mint some assets to the fellowship treasury account. + assert_ok!(>::mint_into(USDT_ID, &treasury_account, SPEND_AMOUNT * 4)); // beneficiary has zero balance. - assert_eq!(>::balance(ASSET_ID, &alice,), 0u128,); + assert_eq!(>::balance(USDT_ID, &alice,), 0u128,); }); CollectivesWestend::execute_with(|| { @@ -96,7 +92,7 @@ fn create_and_claim_treasury_spend() { AssetHubWestend, vec![ RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => { - id: id == &ASSET_ID, + id: id == &USDT_ID, from: from == &treasury_account, to: to == &alice, amount: amount == &SPEND_AMOUNT, @@ -106,7 +102,7 @@ fn create_and_claim_treasury_spend() { ] ); // beneficiary received the assets from the treasury. - assert_eq!(>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,); + assert_eq!(>::balance(USDT_ID, &alice,), SPEND_AMOUNT,); }); CollectivesWestend::execute_with(|| { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs index 65d013a0eec4..82ef74fdab10 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs @@ -940,7 +940,7 @@ fn reserve_transfer_assets_from_system_para_to_para() { ); } -/// Reserve Transfers of a foreign asset and native asset from Parachain to System Para should +/// Reserve Transfers of a random asset and native asset from Parachain to System Para should /// work #[test] fn reserve_transfer_assets_from_para_to_system_para() { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs index 8cbce3e0d223..b70967184387 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs @@ -14,15 +14,17 @@ // limitations under the License. use crate::imports::*; -use emulated_integration_tests_common::accounts::{ALICE, BOB}; -use frame_support::traits::fungibles::{Create, Inspect, Mutate}; +use emulated_integration_tests_common::{ + accounts::{ALICE, BOB}, + USDT_ID, +}; +use frame_support::traits::fungibles::{Inspect, Mutate}; use polkadot_runtime_common::impls::VersionedLocatableAsset; use xcm_executor::traits::ConvertLocation; #[test] fn create_and_claim_treasury_spend() { - const ASSET_ID: u32 = 1984; - const SPEND_AMOUNT: u128 = 1_000_000; + const SPEND_AMOUNT: u128 = 1_000_000_000; // treasury location from a sibling parachain. let treasury_location: Location = Location::new(1, PalletInstance(37)); // treasury account on a sibling parachain. @@ -33,7 +35,7 @@ fn create_and_claim_treasury_spend() { // asset kind to be spend from the treasury. let asset_kind = VersionedLocatableAsset::V4 { location: asset_hub_location, - asset_id: AssetId([PalletInstance(50), GeneralIndex(ASSET_ID.into())].into()), + asset_id: AssetId([PalletInstance(50), GeneralIndex(USDT_ID.into())].into()), }; // treasury spend beneficiary. let alice: AccountId = Westend::account_id_of(ALICE); @@ -43,16 +45,10 @@ fn create_and_claim_treasury_spend() { AssetHubWestend::execute_with(|| { type Assets = ::Assets; - // create an asset class and mint some assets to the treasury account. - assert_ok!(>::create( - ASSET_ID, - treasury_account.clone(), - true, - SPEND_AMOUNT / 2 - )); - assert_ok!(>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4)); + // USDT created at genesis, mint some assets to the treasury account. + assert_ok!(>::mint_into(USDT_ID, &treasury_account, SPEND_AMOUNT * 4)); // beneficiary has zero balance. - assert_eq!(>::balance(ASSET_ID, &alice,), 0u128,); + assert_eq!(>::balance(USDT_ID, &alice,), 0u128,); }); Westend::execute_with(|| { @@ -94,7 +90,7 @@ fn create_and_claim_treasury_spend() { AssetHubWestend, vec![ RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => { - id: id == &ASSET_ID, + id: id == &USDT_ID, from: from == &treasury_account, to: to == &alice, amount: amount == &SPEND_AMOUNT, @@ -104,7 +100,7 @@ fn create_and_claim_treasury_spend() { ] ); // beneficiary received the assets from the treasury. - assert_eq!(>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,); + assert_eq!(>::balance(USDT_ID, &alice,), SPEND_AMOUNT,); }); Westend::execute_with(|| { diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index eca7c7bbc3c2..d0c421bccaf8 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -282,6 +282,10 @@ parameter_types! { 1, [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())] ); + pub UsdtFromAssetHub: Location = Location::new( + 1, + [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(1984)] + ); /// The Penpal runtime is utilized for testing with various environment setups. /// This storage item provides the opportunity to customize testing scenarios From b1a9ad4d387e8e75932e4bc8950c4535f4c82119 Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Fri, 9 Aug 2024 15:05:18 +0200 Subject: [PATCH 082/382] [ci] Move checks to GHA (#5289) Closes https://github.com/paritytech/ci_cd/issues/1012 --- .github/workflows/checks.yml | 90 ++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 .github/workflows/checks.yml diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 000000000000..054c7d786ca9 --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,90 @@ +name: checks + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened, ready_for_review, labeled] + merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + changes: + # TODO: remove once migration is complete or this workflow is fully stable + if: contains(github.event.label.name, 'GHA-migration') + permissions: + pull-requests: read + uses: ./.github/workflows/reusable-check-changed-files.yml + set-image: + # GitHub Actions allows using 'env' in a container context. + # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 + # This workaround sets the container image for each job using 'set-image' job output. + runs-on: ubuntu-latest + timeout-minutes: 20 + outputs: + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT + cargo-clippy: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image, changes] # , build-frame-omni-bencher ] + if: ${{ needs.changes.outputs.rust }} + timeout-minutes: 40 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + RUSTFLAGS: "-D warnings" + SKIP_WASM_BUILD: 1 + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: script + run: | + forklift cargo clippy --all-targets --locked --workspace + forklift cargo clippy --all-targets --all-features --locked --workspace + check-try-runtime: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image, changes] # , build-frame-omni-bencher ] + if: ${{ needs.changes.outputs.rust }} + timeout-minutes: 40 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: script + run: | + forklift cargo check --locked --all --features try-runtime + # this is taken from cumulus + # Check that parachain-template will compile with `try-runtime` feature flag. + forklift cargo check --locked -p parachain-template-node --features try-runtime + # add after https://github.com/paritytech/substrate/pull/14502 is merged + # experimental code may rely on try-runtime and vice-versa + forklift cargo check --locked --all --features try-runtime,experimental + # check-core-crypto-features works fast without forklift + check-core-crypto-features: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image, changes] # , build-frame-omni-bencher ] + if: ${{ needs.changes.outputs.rust }} + timeout-minutes: 30 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: script + run: | + cd substrate/primitives/core + ./check-features-variants.sh + cd - + cd substrate/primitives/application-crypto + ./check-features-variants.sh + cd - + cd substrate/primitives/keyring + ./check-features-variants.sh + cd - From 862860ecc89e077331b72eb66cd7305b931fb9e8 Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Sat, 10 Aug 2024 00:06:59 +0900 Subject: [PATCH 083/382] StorageWeightReclaim: Fix issue when underestimating refund. (#5273) The code do reduce or increase the weight by comparing `benchmarked_weight` and `consumed_weight`. But `benchmarked_weight` is the pre dispatch weight. not the post dispatch weight that is actually written into the block weight by `CheckWeight`. So in case the consumed weight was: `pre dispatch weight > consumed weight > post dispatch weight` then the reclaim code was reducing the block weight instead of increasing it. Might explain this issue even better https://github.com/paritytech/polkadot-sdk/issues/5229 @skunert @s0me0ne-unkn0wn --- .../storage-weight-reclaim/src/lib.rs | 48 +++++++++++++++++-- prdoc/pr_5273.prdoc | 10 ++++ 2 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 prdoc/pr_5273.prdoc diff --git a/cumulus/primitives/storage-weight-reclaim/src/lib.rs b/cumulus/primitives/storage-weight-reclaim/src/lib.rs index f48dd927ee96..5984fa77a2c5 100644 --- a/cumulus/primitives/storage-weight-reclaim/src/lib.rs +++ b/cumulus/primitives/storage-weight-reclaim/src/lib.rs @@ -165,15 +165,14 @@ where ); return Ok(()) }; - let benchmarked_weight = info.weight.proof_size(); - let consumed_weight = post_dispatch_proof_size.saturating_sub(pre_dispatch_proof_size); - // Unspent weight according to the `actual_weight` from `PostDispatchInfo` // This unspent weight will be refunded by the `CheckWeight` extension, so we need to // account for that. let unspent = post_info.calc_unspent(info).proof_size(); - let storage_size_diff = - benchmarked_weight.saturating_sub(unspent).abs_diff(consumed_weight as u64); + let benchmarked_weight = info.weight.proof_size().saturating_sub(unspent); + let consumed_weight = post_dispatch_proof_size.saturating_sub(pre_dispatch_proof_size); + + let storage_size_diff = benchmarked_weight.abs_diff(consumed_weight as u64); // This value will be reclaimed by [`frame_system::CheckWeight`], so we need to calculate // that in. @@ -294,6 +293,45 @@ mod tests { }) } + #[test] + fn underestimating_refund() { + // We fixed a bug where `pre dispatch info weight > consumed weight > post info weight` + // resulted in error. + + // The real cost will be 100 bytes of storage size + let mut test_ext = setup_test_externalities(&[0, 100]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 500 + let info = DispatchInfo { weight: Weight::from_parts(0, 101), ..Default::default() }; + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(0, 99)), + pays_fee: Default::default(), + }; + + assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(0)); + + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + // We expect an accrue of 1 + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + assert_eq!(get_storage_weight().total().proof_size(), 1250); + }) + } + #[test] fn does_nothing_without_extension() { let mut test_ext = new_test_ext(); diff --git a/prdoc/pr_5273.prdoc b/prdoc/pr_5273.prdoc new file mode 100644 index 000000000000..981172c6c13f --- /dev/null +++ b/prdoc/pr_5273.prdoc @@ -0,0 +1,10 @@ +title: Fix storage weight reclaim bug. + +doc: + - audience: Runtime Dev + description: | + A bug in storage weight reclaim signed extension is fixed. The bug was causing an underestimate of the proof size when the post dispatch info was underestimating the proof size and the pre dispatch info was overestimating the proof size at the same time. + +crates: + - name: cumulus-primitives-storage-weight-reclaim + bump: patch From 380cd21877843d7668139725c98762c6f617fada Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Fri, 9 Aug 2024 18:25:22 +0300 Subject: [PATCH 084/382] Fix bridge zombienet tests (#5306) Fixes https://github.com/paritytech/polkadot-sdk/issues/5296 --- docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile index 60698de1d6ad..55b9156e6a0a 100644 --- a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile +++ b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile @@ -1,7 +1,7 @@ # this image is built on top of existing Zombienet image ARG ZOMBIENET_IMAGE # this image uses substrate-relay image built elsewhere -ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.6.6 +ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.6.8 # metadata ARG VCS_REF From 47c1b4cd8dd673d25c37b802f44da30a23ec9b9e Mon Sep 17 00:00:00 2001 From: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:59:56 +0200 Subject: [PATCH 085/382] Move PVF code and PoV decompression to PVF host workers (#5142) Closes #5071 This PR aims to * Move all the blocking decompression from the candidate validation subsystem to the PVF host workers; * Run the candidate validation subsystem on the non-blocking pool again. Upsides: no blocking operations in the subsystem's main loop. PVF throughput is not limited by the ability of the subsystem to decompress a lot of stuff. Correctness and homogeneity improve, as the artifact used to be identified by the hash of decompressed code, and now they are identified by the hash of compressed code, which coincides with the on-chain `ValidationCodeHash`. Downsides: the PVF code decompression is now accounted for in the PVF preparation timeout (be it pre-checking or actual preparation). Taking into account that the decompression duration is on the order of milliseconds, and the preparation timeout is on the order of seconds, I believe it is negligible. --- Cargo.lock | 3 + .../node/core/candidate-validation/Cargo.toml | 2 +- .../node/core/candidate-validation/src/lib.rs | 134 +++++------- .../core/candidate-validation/src/metrics.rs | 44 ---- .../core/candidate-validation/src/tests.rs | 183 +---------------- .../benches/host_prepare_rococo_runtime.rs | 2 +- polkadot/node/core/pvf/common/src/error.rs | 10 +- polkadot/node/core/pvf/common/src/execute.rs | 4 + polkadot/node/core/pvf/common/src/prepare.rs | 2 + polkadot/node/core/pvf/common/src/pvf.rs | 20 +- .../node/core/pvf/execute-worker/Cargo.toml | 3 + .../node/core/pvf/execute-worker/src/lib.rs | 65 +++++- .../node/core/pvf/prepare-worker/Cargo.toml | 2 + .../benches/prepare_rococo_runtime.rs | 6 +- .../node/core/pvf/prepare-worker/src/lib.rs | 50 ++++- polkadot/node/core/pvf/src/error.rs | 3 + polkadot/node/core/pvf/src/execute/queue.rs | 52 ++++- .../core/pvf/src/execute/worker_interface.rs | 36 ++-- polkadot/node/core/pvf/src/host.rs | 106 +++++++--- polkadot/node/core/pvf/src/metrics.rs | 46 +++++ .../core/pvf/src/prepare/worker_interface.rs | 10 +- polkadot/node/core/pvf/tests/it/adder.rs | 84 ++++---- polkadot/node/core/pvf/tests/it/main.rs | 192 ++++++++++++------ polkadot/node/core/pvf/tests/it/process.rs | 80 +++++--- polkadot/node/overseer/src/lib.rs | 2 +- prdoc/pr_5142.prdoc | 26 +++ 26 files changed, 642 insertions(+), 525 deletions(-) create mode 100644 prdoc/pr_5142.prdoc diff --git a/Cargo.lock b/Cargo.lock index 54a01f12f35c..4db38311fe66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13589,8 +13589,10 @@ dependencies = [ "nix 0.28.0", "parity-scale-codec", "polkadot-node-core-pvf-common", + "polkadot-node-primitives", "polkadot-parachain-primitives", "polkadot-primitives", + "sp-maybe-compressed-blob", "tracing-gum", ] @@ -13605,6 +13607,7 @@ dependencies = [ "nix 0.28.0", "parity-scale-codec", "polkadot-node-core-pvf-common", + "polkadot-node-primitives", "polkadot-primitives", "rayon", "rococo-runtime", diff --git a/polkadot/node/core/candidate-validation/Cargo.toml b/polkadot/node/core/candidate-validation/Cargo.toml index 13ab3e3fba50..fcacc38cae65 100644 --- a/polkadot/node/core/candidate-validation/Cargo.toml +++ b/polkadot/node/core/candidate-validation/Cargo.toml @@ -17,7 +17,6 @@ gum = { workspace = true, default-features = true } sp-keystore = { workspace = true } sp-application-crypto = { workspace = true } -sp-maybe-compressed-blob = { workspace = true, default-features = true } codec = { features = ["bit-vec", "derive"], workspace = true } polkadot-primitives = { workspace = true, default-features = true } @@ -36,5 +35,6 @@ sp-keyring = { workspace = true, default-features = true } futures = { features = ["thread-pool"], workspace = true } assert_matches = { workspace = true } polkadot-node-subsystem-test-helpers = { workspace = true } +sp-maybe-compressed-blob = { workspace = true, default-features = true } sp-core = { workspace = true, default-features = true } polkadot-primitives-test-helpers = { workspace = true } diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index 1985964ebc51..103d29e8d269 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -27,9 +27,7 @@ use polkadot_node_core_pvf::{ InternalValidationError, InvalidCandidate as WasmInvalidCandidate, PossiblyInvalidError, PrepareError, PrepareJobKind, PvfPrepData, ValidationError, ValidationHost, }; -use polkadot_node_primitives::{ - BlockData, InvalidCandidate, PoV, ValidationResult, POV_BOMB_LIMIT, VALIDATION_CODE_BOMB_LIMIT, -}; +use polkadot_node_primitives::{InvalidCandidate, PoV, ValidationResult}; use polkadot_node_subsystem::{ errors::RuntimeApiError, messages::{ @@ -41,9 +39,7 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_util as util; use polkadot_overseer::ActiveLeavesUpdate; -use polkadot_parachain_primitives::primitives::{ - ValidationParams, ValidationResult as WasmValidationResult, -}; +use polkadot_parachain_primitives::primitives::ValidationResult as WasmValidationResult; use polkadot_primitives::{ executor_params::{ DEFAULT_APPROVAL_EXECUTION_TIMEOUT, DEFAULT_BACKING_EXECUTION_TIMEOUT, @@ -504,21 +500,12 @@ where continue; }; - let pvf = match sp_maybe_compressed_blob::decompress( - &validation_code.0, - VALIDATION_CODE_BOMB_LIMIT, - ) { - Ok(code) => PvfPrepData::from_code( - code.into_owned(), - executor_params.clone(), - timeout, - PrepareJobKind::Prechecking, - ), - Err(e) => { - gum::debug!(target: LOG_TARGET, err=?e, "cannot decompress validation code"); - continue - }, - }; + let pvf = PvfPrepData::from_code( + validation_code.0, + executor_params.clone(), + timeout, + PrepareJobKind::Prechecking, + ); active_pvfs.push(pvf); processed_code_hashes.push(code_hash); @@ -651,21 +638,12 @@ where let timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Precheck); - let pvf = match sp_maybe_compressed_blob::decompress( - &validation_code.0, - VALIDATION_CODE_BOMB_LIMIT, - ) { - Ok(code) => PvfPrepData::from_code( - code.into_owned(), - executor_params, - timeout, - PrepareJobKind::Prechecking, - ), - Err(e) => { - gum::debug!(target: LOG_TARGET, err=?e, "precheck: cannot decompress validation code"); - return PreCheckOutcome::Invalid - }, - }; + let pvf = PvfPrepData::from_code( + validation_code.0, + executor_params, + timeout, + PrepareJobKind::Prechecking, + ); match validation_backend.precheck_pvf(pvf).await { Ok(_) => PreCheckOutcome::Valid, @@ -873,41 +851,7 @@ async fn validate_candidate_exhaustive( return Ok(ValidationResult::Invalid(e)) } - let raw_validation_code = match sp_maybe_compressed_blob::decompress( - &validation_code.0, - VALIDATION_CODE_BOMB_LIMIT, - ) { - Ok(code) => code, - Err(e) => { - gum::info!(target: LOG_TARGET, ?para_id, err=?e, "Invalid candidate (validation code)"); - - // Code already passed pre-checking, if decompression fails now this most likely means - // some local corruption happened. - return Err(ValidationFailed("Code decompression failed".to_string())) - }, - }; - metrics.observe_code_size(raw_validation_code.len()); - - metrics.observe_pov_size(pov.block_data.0.len(), true); - let raw_block_data = - match sp_maybe_compressed_blob::decompress(&pov.block_data.0, POV_BOMB_LIMIT) { - Ok(block_data) => BlockData(block_data.to_vec()), - Err(e) => { - gum::info!(target: LOG_TARGET, ?para_id, err=?e, "Invalid candidate (PoV code)"); - - // If the PoV is invalid, the candidate certainly is. - return Ok(ValidationResult::Invalid(InvalidCandidate::PoVDecompressionFailure)) - }, - }; - metrics.observe_pov_size(raw_block_data.0.len(), false); - - let params = ValidationParams { - parent_head: persisted_validation_data.parent_head.clone(), - block_data: raw_block_data, - relay_parent_number: persisted_validation_data.relay_parent_number, - relay_parent_storage_root: persisted_validation_data.relay_parent_storage_root, - }; - + let persisted_validation_data = Arc::new(persisted_validation_data); let result = match exec_kind { // Retry is disabled to reduce the chance of nondeterministic blocks getting backed and // honest backers getting slashed. @@ -915,7 +859,7 @@ async fn validate_candidate_exhaustive( let prep_timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Prepare); let exec_timeout = pvf_exec_timeout(&executor_params, exec_kind); let pvf = PvfPrepData::from_code( - raw_validation_code.to_vec(), + validation_code.0, executor_params, prep_timeout, PrepareJobKind::Compilation, @@ -925,7 +869,8 @@ async fn validate_candidate_exhaustive( .validate_candidate( pvf, exec_timeout, - params.encode(), + persisted_validation_data.clone(), + pov, polkadot_node_core_pvf::Priority::Normal, ) .await @@ -933,9 +878,10 @@ async fn validate_candidate_exhaustive( PvfExecKind::Approval => validation_backend .validate_candidate_with_retry( - raw_validation_code.to_vec(), + validation_code.0, pvf_exec_timeout(&executor_params, exec_kind), - params, + persisted_validation_data.clone(), + pov, executor_params, PVF_APPROVAL_EXECUTION_RETRY_DELAY, polkadot_node_core_pvf::Priority::Critical, @@ -961,6 +907,8 @@ async fn validate_candidate_exhaustive( Ok(ValidationResult::Invalid(InvalidCandidate::Timeout)), Err(ValidationError::Invalid(WasmInvalidCandidate::WorkerReportedInvalid(e))) => Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(e))), + Err(ValidationError::Invalid(WasmInvalidCandidate::PoVDecompressionFailure)) => + Ok(ValidationResult::Invalid(InvalidCandidate::PoVDecompressionFailure)), Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousWorkerDeath)) => Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError( "ambiguous worker death".to_string(), @@ -1007,7 +955,7 @@ async fn validate_candidate_exhaustive( // invalid. Ok(ValidationResult::Invalid(InvalidCandidate::CommitmentsHashMismatch)) } else { - Ok(ValidationResult::Valid(outputs, persisted_validation_data)) + Ok(ValidationResult::Valid(outputs, (*persisted_validation_data).clone())) } }, } @@ -1020,7 +968,8 @@ trait ValidationBackend { &mut self, pvf: PvfPrepData, exec_timeout: Duration, - encoded_params: Vec, + pvd: Arc, + pov: Arc, // The priority for the preparation job. prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result; @@ -1035,9 +984,10 @@ trait ValidationBackend { /// preparation. async fn validate_candidate_with_retry( &mut self, - raw_validation_code: Vec, + code: Vec, exec_timeout: Duration, - params: ValidationParams, + pvd: Arc, + pov: Arc, executor_params: ExecutorParams, retry_delay: Duration, // The priority for the preparation job. @@ -1046,7 +996,7 @@ trait ValidationBackend { let prep_timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Prepare); // Construct the PVF a single time, since it is an expensive operation. Cloning it is cheap. let pvf = PvfPrepData::from_code( - raw_validation_code, + code, executor_params, prep_timeout, PrepareJobKind::Compilation, @@ -1057,7 +1007,13 @@ trait ValidationBackend { // Use `Priority::Critical` as finality trumps parachain liveliness. let mut validation_result = self - .validate_candidate(pvf.clone(), exec_timeout, params.encode(), prepare_priority) + .validate_candidate( + pvf.clone(), + exec_timeout, + pvd.clone(), + pov.clone(), + prepare_priority, + ) .await; if validation_result.is_ok() { return validation_result @@ -1130,10 +1086,14 @@ trait ValidationBackend { validation_result ); - // Encode the params again when re-trying. We expect the retry case to be relatively - // rare, and we want to avoid unconditionally cloning data. validation_result = self - .validate_candidate(pvf.clone(), new_timeout, params.encode(), prepare_priority) + .validate_candidate( + pvf.clone(), + new_timeout, + pvd.clone(), + pov.clone(), + prepare_priority, + ) .await; } } @@ -1153,13 +1113,13 @@ impl ValidationBackend for ValidationHost { &mut self, pvf: PvfPrepData, exec_timeout: Duration, - encoded_params: Vec, + pvd: Arc, + pov: Arc, // The priority for the preparation job. prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result { let (tx, rx) = oneshot::channel(); - if let Err(err) = - self.execute_pvf(pvf, exec_timeout, encoded_params, prepare_priority, tx).await + if let Err(err) = self.execute_pvf(pvf, exec_timeout, pvd, pov, prepare_priority, tx).await { return Err(InternalValidationError::HostCommunication(format!( "cannot send pvf to the validation host, it might have shut down: {:?}", diff --git a/polkadot/node/core/candidate-validation/src/metrics.rs b/polkadot/node/core/candidate-validation/src/metrics.rs index 28fc957ddb1a..1459907aa599 100644 --- a/polkadot/node/core/candidate-validation/src/metrics.rs +++ b/polkadot/node/core/candidate-validation/src/metrics.rs @@ -23,8 +23,6 @@ pub(crate) struct MetricsInner { pub(crate) validate_from_chain_state: prometheus::Histogram, pub(crate) validate_from_exhaustive: prometheus::Histogram, pub(crate) validate_candidate_exhaustive: prometheus::Histogram, - pub(crate) pov_size: prometheus::HistogramVec, - pub(crate) code_size: prometheus::Histogram, } /// Candidate validation metrics. @@ -70,21 +68,6 @@ impl Metrics { .as_ref() .map(|metrics| metrics.validate_candidate_exhaustive.start_timer()) } - - pub fn observe_code_size(&self, code_size: usize) { - if let Some(metrics) = &self.0 { - metrics.code_size.observe(code_size as f64); - } - } - - pub fn observe_pov_size(&self, pov_size: usize, compressed: bool) { - if let Some(metrics) = &self.0 { - metrics - .pov_size - .with_label_values(&[if compressed { "true" } else { "false" }]) - .observe(pov_size as f64); - } - } } impl metrics::Metrics for Metrics { @@ -121,33 +104,6 @@ impl metrics::Metrics for Metrics { ))?, registry, )?, - pov_size: prometheus::register( - prometheus::HistogramVec::new( - prometheus::HistogramOpts::new( - "polkadot_parachain_candidate_validation_pov_size", - "The compressed and decompressed size of the proof of validity of a candidate", - ) - .buckets( - prometheus::exponential_buckets(16384.0, 2.0, 10) - .expect("arguments are always valid; qed"), - ), - &["compressed"], - )?, - registry, - )?, - code_size: prometheus::register( - prometheus::Histogram::with_opts( - prometheus::HistogramOpts::new( - "polkadot_parachain_candidate_validation_code_size", - "The size of the decompressed WASM validation blob used for checking a candidate", - ) - .buckets( - prometheus::exponential_buckets(16384.0, 2.0, 10) - .expect("arguments are always valid; qed"), - ), - )?, - registry, - )?, }; Ok(Metrics(Some(metrics))) } diff --git a/polkadot/node/core/candidate-validation/src/tests.rs b/polkadot/node/core/candidate-validation/src/tests.rs index 86d855f78b45..55282fdf4ee1 100644 --- a/polkadot/node/core/candidate-validation/src/tests.rs +++ b/polkadot/node/core/candidate-validation/src/tests.rs @@ -20,6 +20,7 @@ use super::*; use assert_matches::assert_matches; use futures::executor; use polkadot_node_core_pvf::PrepareError; +use polkadot_node_primitives::{BlockData, VALIDATION_CODE_BOMB_LIMIT}; use polkadot_node_subsystem::messages::AllMessages; use polkadot_node_subsystem_util::reexports::SubsystemContext; use polkadot_overseer::ActivatedLeaf; @@ -385,7 +386,8 @@ impl ValidationBackend for MockValidateCandidateBackend { &mut self, _pvf: PvfPrepData, _timeout: Duration, - _encoded_params: Vec, + _pvd: Arc, + _pov: Arc, _prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result { // This is expected to panic if called more times than expected, indicating an error in the @@ -950,115 +952,6 @@ fn compressed_code_works() { assert_matches!(v, Ok(ValidationResult::Valid(_, _))); } -#[test] -fn code_decompression_failure_is_error() { - let validation_data = PersistedValidationData { max_pov_size: 1024, ..Default::default() }; - let pov = PoV { block_data: BlockData(vec![1; 32]) }; - let head_data = HeadData(vec![1, 1, 1]); - - let raw_code = vec![2u8; VALIDATION_CODE_BOMB_LIMIT + 1]; - let validation_code = - sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1) - .map(ValidationCode) - .unwrap(); - - let descriptor = make_valid_candidate_descriptor( - ParaId::from(1_u32), - dummy_hash(), - validation_data.hash(), - pov.hash(), - validation_code.hash(), - head_data.hash(), - dummy_hash(), - Sr25519Keyring::Alice, - ); - - let validation_result = WasmValidationResult { - head_data, - new_validation_code: None, - upward_messages: Default::default(), - horizontal_messages: Default::default(), - processed_downward_messages: 0, - hrmp_watermark: 0, - }; - - let candidate_receipt = CandidateReceipt { descriptor, commitments_hash: Hash::zero() }; - - let pool = TaskExecutor::new(); - let (_ctx, _ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< - AllMessages, - _, - >(pool.clone()); - - let v = executor::block_on(validate_candidate_exhaustive( - MockValidateCandidateBackend::with_hardcoded_result(Ok(validation_result)), - validation_data, - validation_code, - candidate_receipt, - Arc::new(pov), - ExecutorParams::default(), - PvfExecKind::Backing, - &Default::default(), - )); - - assert_matches!(v, Err(_)); -} - -#[test] -fn pov_decompression_failure_is_invalid() { - let validation_data = - PersistedValidationData { max_pov_size: POV_BOMB_LIMIT as u32, ..Default::default() }; - let head_data = HeadData(vec![1, 1, 1]); - - let raw_block_data = vec![2u8; POV_BOMB_LIMIT + 1]; - let pov = sp_maybe_compressed_blob::compress(&raw_block_data, POV_BOMB_LIMIT + 1) - .map(|raw| PoV { block_data: BlockData(raw) }) - .unwrap(); - - let validation_code = ValidationCode(vec![2; 16]); - - let descriptor = make_valid_candidate_descriptor( - ParaId::from(1_u32), - dummy_hash(), - validation_data.hash(), - pov.hash(), - validation_code.hash(), - head_data.hash(), - dummy_hash(), - Sr25519Keyring::Alice, - ); - - let validation_result = WasmValidationResult { - head_data, - new_validation_code: None, - upward_messages: Default::default(), - horizontal_messages: Default::default(), - processed_downward_messages: 0, - hrmp_watermark: 0, - }; - - let candidate_receipt = CandidateReceipt { descriptor, commitments_hash: Hash::zero() }; - - let pool = TaskExecutor::new(); - let (_ctx, _ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< - AllMessages, - _, - >(pool.clone()); - - let v = executor::block_on(validate_candidate_exhaustive( - MockValidateCandidateBackend::with_hardcoded_result(Ok(validation_result)), - validation_data, - validation_code, - candidate_receipt, - Arc::new(pov), - ExecutorParams::default(), - PvfExecKind::Backing, - &Default::default(), - )); - - assert_matches!(v, Ok(ValidationResult::Invalid(InvalidCandidate::PoVDecompressionFailure))); -} - struct MockPreCheckBackend { result: Result<(), PrepareError>, } @@ -1075,7 +968,8 @@ impl ValidationBackend for MockPreCheckBackend { &mut self, _pvf: PvfPrepData, _timeout: Duration, - _encoded_params: Vec, + _pvd: Arc, + _pov: Arc, _prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result { unreachable!() @@ -1149,70 +1043,6 @@ fn precheck_works() { executor::block_on(test_fut); } -#[test] -fn precheck_invalid_pvf_blob_compression() { - let relay_parent = [3; 32].into(); - - let raw_code = vec![2u8; VALIDATION_CODE_BOMB_LIMIT + 1]; - let validation_code = - sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1) - .map(ValidationCode) - .unwrap(); - let validation_code_hash = validation_code.hash(); - - let pool = TaskExecutor::new(); - let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< - AllMessages, - _, - >(pool.clone()); - - let (check_fut, check_result) = precheck_pvf( - ctx.sender(), - MockPreCheckBackend::with_hardcoded_result(Ok(())), - relay_parent, - validation_code_hash, - ) - .remote_handle(); - - let test_fut = async move { - assert_matches!( - ctx_handle.recv().await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - rp, - RuntimeApiRequest::ValidationCodeByHash( - vch, - tx - ), - )) => { - assert_eq!(vch, validation_code_hash); - assert_eq!(rp, relay_parent); - - let _ = tx.send(Ok(Some(validation_code.clone()))); - } - ); - assert_matches!( - ctx_handle.recv().await, - AllMessages::RuntimeApi( - RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionIndexForChild(tx)) - ) => { - tx.send(Ok(1u32.into())).unwrap(); - } - ); - assert_matches!( - ctx_handle.recv().await, - AllMessages::RuntimeApi( - RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionExecutorParams(_, tx)) - ) => { - tx.send(Ok(Some(ExecutorParams::default()))).unwrap(); - } - ); - assert_matches!(check_result.await, PreCheckOutcome::Invalid); - }; - - let test_fut = future::join(test_fut, check_fut); - executor::block_on(test_fut); -} - #[test] fn precheck_properly_classifies_outcomes() { let inner = |prepare_result, precheck_outcome| { @@ -1292,7 +1122,8 @@ impl ValidationBackend for MockHeadsUp { &mut self, _pvf: PvfPrepData, _timeout: Duration, - _encoded_params: Vec, + _pvd: Arc, + _pov: Arc, _prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result { unreachable!() diff --git a/polkadot/node/core/pvf/benches/host_prepare_rococo_runtime.rs b/polkadot/node/core/pvf/benches/host_prepare_rococo_runtime.rs index 97a03e6596d1..342128b7cca2 100644 --- a/polkadot/node/core/pvf/benches/host_prepare_rococo_runtime.rs +++ b/polkadot/node/core/pvf/benches/host_prepare_rococo_runtime.rs @@ -116,7 +116,7 @@ fn host_prepare_rococo_runtime(c: &mut Criterion) { cfg.prepare_workers_hard_max_num = 1; }) .await, - pvf.clone().code(), + pvf.clone().maybe_compressed_code(), ) }, |result| async move { diff --git a/polkadot/node/core/pvf/common/src/error.rs b/polkadot/node/core/pvf/common/src/error.rs index 7ee05448d3c5..b0cdba9501db 100644 --- a/polkadot/node/core/pvf/common/src/error.rs +++ b/polkadot/node/core/pvf/common/src/error.rs @@ -94,6 +94,10 @@ pub enum PrepareError { #[codec(index = 11)] #[error("prepare: error interfacing with the kernel: {0}")] Kernel(String), + /// Code blob failed to decompress + #[codec(index = 12)] + #[error("prepare: could not decompress code blob: {0}")] + CouldNotDecompressCodeBlob(String), } impl PrepareError { @@ -106,7 +110,11 @@ impl PrepareError { pub fn is_deterministic(&self) -> bool { use PrepareError::*; match self { - Prevalidation(_) | Preparation(_) | JobError(_) | OutOfMemory => true, + Prevalidation(_) | + Preparation(_) | + JobError(_) | + OutOfMemory | + CouldNotDecompressCodeBlob(_) => true, IoErr(_) | JobDied { .. } | CreateTmpFile(_) | diff --git a/polkadot/node/core/pvf/common/src/execute.rs b/polkadot/node/core/pvf/common/src/execute.rs index 46862f9f80b6..cff3f3b86e95 100644 --- a/polkadot/node/core/pvf/common/src/execute.rs +++ b/polkadot/node/core/pvf/common/src/execute.rs @@ -35,6 +35,8 @@ pub struct WorkerResponse { pub job_response: JobResponse, /// The amount of CPU time taken by the job. pub duration: Duration, + /// The uncompressed PoV size. + pub pov_size: u32, } /// An error occurred in the worker process. @@ -77,6 +79,8 @@ pub enum JobResponse { RuntimeConstruction(String), /// The candidate is invalid. InvalidCandidate(String), + /// PoV decompression failed + PoVDecompressionFailure, } impl JobResponse { diff --git a/polkadot/node/core/pvf/common/src/prepare.rs b/polkadot/node/core/pvf/common/src/prepare.rs index 81e165a7b8a4..4cd1beb30991 100644 --- a/polkadot/node/core/pvf/common/src/prepare.rs +++ b/polkadot/node/core/pvf/common/src/prepare.rs @@ -44,6 +44,8 @@ pub struct PrepareStats { pub cpu_time_elapsed: std::time::Duration, /// The observed memory statistics for the preparation job. pub memory_stats: MemoryStats, + /// The decompressed Wasm code length observed during the preparation. + pub observed_wasm_code_len: u32, } /// Helper struct to contain all the memory stats, including `MemoryAllocationStats` and, if diff --git a/polkadot/node/core/pvf/common/src/pvf.rs b/polkadot/node/core/pvf/common/src/pvf.rs index e2ac36a2406a..4019a8d8b0d0 100644 --- a/polkadot/node/core/pvf/common/src/pvf.rs +++ b/polkadot/node/core/pvf/common/src/pvf.rs @@ -26,9 +26,9 @@ use std::{fmt, sync::Arc, time::Duration}; /// Should be cheap to clone. #[derive(Clone, Encode, Decode)] pub struct PvfPrepData { - /// Wasm code (uncompressed) - code: Arc>, - /// Wasm code hash + /// Wasm code (maybe compressed) + maybe_compressed_code: Arc>, + /// Wasm code hash. code_hash: ValidationCodeHash, /// Executor environment parameters for the session for which artifact is prepared executor_params: Arc, @@ -46,20 +46,20 @@ impl PvfPrepData { prep_timeout: Duration, prep_kind: PrepareJobKind, ) -> Self { - let code = Arc::new(code); - let code_hash = sp_crypto_hashing::blake2_256(&code).into(); + let maybe_compressed_code = Arc::new(code); + let code_hash = sp_crypto_hashing::blake2_256(&maybe_compressed_code).into(); let executor_params = Arc::new(executor_params); - Self { code, code_hash, executor_params, prep_timeout, prep_kind } + Self { maybe_compressed_code, code_hash, executor_params, prep_timeout, prep_kind } } - /// Returns validation code hash for the PVF + /// Returns validation code hash pub fn code_hash(&self) -> ValidationCodeHash { self.code_hash } - /// Returns PVF code - pub fn code(&self) -> Arc> { - self.code.clone() + /// Returns PVF code blob + pub fn maybe_compressed_code(&self) -> Arc> { + self.maybe_compressed_code.clone() } /// Returns executor params diff --git a/polkadot/node/core/pvf/execute-worker/Cargo.toml b/polkadot/node/core/pvf/execute-worker/Cargo.toml index f24b66dc4a0e..6ad340d25612 100644 --- a/polkadot/node/core/pvf/execute-worker/Cargo.toml +++ b/polkadot/node/core/pvf/execute-worker/Cargo.toml @@ -19,8 +19,11 @@ libc = { workspace = true } codec = { features = ["derive"], workspace = true } polkadot-node-core-pvf-common = { workspace = true, default-features = true } +polkadot-node-primitives = { workspace = true, default-features = true } polkadot-parachain-primitives = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } +sp-maybe-compressed-blob = { workspace = true, default-features = true } + [features] builder = [] diff --git a/polkadot/node/core/pvf/execute-worker/src/lib.rs b/polkadot/node/core/pvf/execute-worker/src/lib.rs index 35858ab36cec..4b7c167cc9ec 100644 --- a/polkadot/node/core/pvf/execute-worker/src/lib.rs +++ b/polkadot/node/core/pvf/execute-worker/src/lib.rs @@ -22,6 +22,7 @@ pub use polkadot_node_core_pvf_common::{ error::ExecuteError, executor_interface::execute_artifact, }; +use polkadot_parachain_primitives::primitives::ValidationParams; // NOTE: Initializing logging in e.g. tests will not have an effect in the workers, as they are // separate spawned processes. Run with e.g. `RUST_LOG=parachain::pvf-execute-worker=trace`. @@ -50,8 +51,9 @@ use polkadot_node_core_pvf_common::{ }, worker_dir, }; +use polkadot_node_primitives::{BlockData, PoV, POV_BOMB_LIMIT}; use polkadot_parachain_primitives::primitives::ValidationResult; -use polkadot_primitives::ExecutorParams; +use polkadot_primitives::{ExecutorParams, PersistedValidationData}; use std::{ io::{self, Read}, os::{ @@ -85,8 +87,23 @@ fn recv_execute_handshake(stream: &mut UnixStream) -> io::Result { Ok(handshake) } -fn recv_request(stream: &mut UnixStream) -> io::Result<(Vec, Duration)> { - let params = framed_recv_blocking(stream)?; +fn recv_request(stream: &mut UnixStream) -> io::Result<(PersistedValidationData, PoV, Duration)> { + let pvd = framed_recv_blocking(stream)?; + let pvd = PersistedValidationData::decode(&mut &pvd[..]).map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + "execute pvf recv_request: failed to decode persisted validation data".to_string(), + ) + })?; + + let pov = framed_recv_blocking(stream)?; + let pov = PoV::decode(&mut &pov[..]).map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + "execute pvf recv_request: failed to decode PoV".to_string(), + ) + })?; + let execution_timeout = framed_recv_blocking(stream)?; let execution_timeout = Duration::decode(&mut &execution_timeout[..]).map_err(|_| { io::Error::new( @@ -94,7 +111,7 @@ fn recv_request(stream: &mut UnixStream) -> io::Result<(Vec, Duration)> { "execute pvf recv_request: failed to decode duration".to_string(), ) })?; - Ok((params, execution_timeout)) + Ok((pvd, pov, execution_timeout)) } /// Sends an error to the host and returns the original error wrapped in `io::Error`. @@ -149,7 +166,7 @@ pub fn worker_entrypoint( let execute_thread_stack_size = max_stack_size(&executor_params); loop { - let (params, execution_timeout) = recv_request(&mut stream).map_err(|e| { + let (pvd, pov, execution_timeout) = recv_request(&mut stream).map_err(|e| { map_and_send_err!( e, InternalValidationError::HostCommunication, @@ -197,7 +214,33 @@ pub fn worker_entrypoint( let stream_fd = stream.as_raw_fd(); let compiled_artifact_blob = Arc::new(compiled_artifact_blob); - let params = Arc::new(params); + + let raw_block_data = + match sp_maybe_compressed_blob::decompress(&pov.block_data.0, POV_BOMB_LIMIT) { + Ok(data) => data, + Err(_) => { + send_result::( + &mut stream, + Ok(WorkerResponse { + job_response: JobResponse::PoVDecompressionFailure, + duration: Duration::ZERO, + pov_size: 0, + }), + worker_info, + )?; + continue; + }, + }; + + let pov_size = raw_block_data.len() as u32; + + let params = ValidationParams { + parent_head: pvd.parent_head.clone(), + block_data: BlockData(raw_block_data.to_vec()), + relay_parent_number: pvd.relay_parent_number, + relay_parent_storage_root: pvd.relay_parent_storage_root, + }; + let params = Arc::new(params.encode()); cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { @@ -214,6 +257,7 @@ pub fn worker_entrypoint( worker_info, security_status.can_unshare_user_namespace_and_change_root, usage_before, + pov_size, )? } else { // Fall back to using fork. @@ -228,6 +272,7 @@ pub fn worker_entrypoint( execute_thread_stack_size, worker_info, usage_before, + pov_size, )? }; } else { @@ -242,6 +287,7 @@ pub fn worker_entrypoint( execute_thread_stack_size, worker_info, usage_before, + pov_size, )?; } } @@ -300,6 +346,7 @@ fn handle_clone( worker_info: &WorkerInfo, have_unshare_newuser: bool, usage_before: Usage, + pov_size: u32, ) -> io::Result> { use polkadot_node_core_pvf_common::worker::security; @@ -329,6 +376,7 @@ fn handle_clone( worker_info, child, usage_before, + pov_size, execution_timeout, ), Err(security::clone::Error::Clone(errno)) => @@ -347,6 +395,7 @@ fn handle_fork( execute_worker_stack_size: usize, worker_info: &WorkerInfo, usage_before: Usage, + pov_size: u32, ) -> io::Result> { // SAFETY: new process is spawned within a single threaded process. This invariant // is enforced by tests. @@ -367,6 +416,7 @@ fn handle_fork( worker_info, child, usage_before, + pov_size, execution_timeout, ), Err(errno) => Ok(Err(internal_error_from_errno("fork", errno))), @@ -513,6 +563,7 @@ fn handle_parent_process( worker_info: &WorkerInfo, job_pid: Pid, usage_before: Usage, + pov_size: u32, timeout: Duration, ) -> io::Result> { // the read end will wait until all write ends have been closed, @@ -578,7 +629,7 @@ fn handle_parent_process( )))); } - Ok(Ok(WorkerResponse { job_response, duration: cpu_tv })) + Ok(Ok(WorkerResponse { job_response, pov_size, duration: cpu_tv })) }, Err(job_error) => { gum::warn!( diff --git a/polkadot/node/core/pvf/prepare-worker/Cargo.toml b/polkadot/node/core/pvf/prepare-worker/Cargo.toml index 9e0d01fc438b..56235bd82192 100644 --- a/polkadot/node/core/pvf/prepare-worker/Cargo.toml +++ b/polkadot/node/core/pvf/prepare-worker/Cargo.toml @@ -23,10 +23,12 @@ nix = { features = ["process", "resource", "sched"], workspace = true } codec = { features = ["derive"], workspace = true } polkadot-node-core-pvf-common = { workspace = true, default-features = true } +polkadot-node-primitives = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } sc-executor-common = { workspace = true, default-features = true } sc-executor-wasmtime = { workspace = true, default-features = true } +sp-maybe-compressed-blob = { workspace = true, default-features = true } [target.'cfg(target_os = "linux")'.dependencies] tikv-jemallocator = "0.5.0" diff --git a/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs b/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs index d531c90b64b5..49b30dc33ceb 100644 --- a/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs +++ b/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs @@ -24,7 +24,11 @@ use polkadot_primitives::ExecutorParams; use std::time::Duration; fn do_prepare_runtime(pvf: PvfPrepData) { - let blob = match prevalidate(&pvf.code()) { + let maybe_compressed_code = pvf.maybe_compressed_code(); + let raw_validation_code = + sp_maybe_compressed_blob::decompress(&maybe_compressed_code, usize::MAX).unwrap(); + + let blob = match prevalidate(&raw_validation_code) { Err(err) => panic!("{:?}", err), Ok(b) => b, }; diff --git a/polkadot/node/core/pvf/prepare-worker/src/lib.rs b/polkadot/node/core/pvf/prepare-worker/src/lib.rs index ef33d11720eb..f8ebb6effcec 100644 --- a/polkadot/node/core/pvf/prepare-worker/src/lib.rs +++ b/polkadot/node/core/pvf/prepare-worker/src/lib.rs @@ -38,6 +38,7 @@ use polkadot_node_core_pvf_common::{ executor_interface::{prepare, prevalidate}, worker::{pipe2_cloexec, PipeFd, WorkerInfo}, }; +use polkadot_node_primitives::VALIDATION_CODE_BOMB_LIMIT; use codec::{Decode, Encode}; use polkadot_node_core_pvf_common::{ @@ -105,6 +106,12 @@ impl AsRef<[u8]> for CompiledArtifact { } } +#[derive(Encode, Decode)] +pub struct PrepareOutcome { + pub compiled_artifact: CompiledArtifact, + pub observed_wasm_code_len: u32, +} + /// Get a worker request. fn recv_request(stream: &mut UnixStream) -> io::Result { let pvf = framed_recv_blocking(stream)?; @@ -294,14 +301,23 @@ pub fn worker_entrypoint( ); } -fn prepare_artifact(pvf: PvfPrepData) -> Result { - let blob = match prevalidate(&pvf.code()) { +fn prepare_artifact(pvf: PvfPrepData) -> Result { + let maybe_compressed_code = pvf.maybe_compressed_code(); + let raw_validation_code = + sp_maybe_compressed_blob::decompress(&maybe_compressed_code, VALIDATION_CODE_BOMB_LIMIT) + .map_err(|e| PrepareError::CouldNotDecompressCodeBlob(e.to_string()))?; + let observed_wasm_code_len = raw_validation_code.len() as u32; + + let blob = match prevalidate(&raw_validation_code) { Err(err) => return Err(PrepareError::Prevalidation(format!("{:?}", err))), Ok(b) => b, }; match prepare(blob, &pvf.executor_params()) { - Ok(compiled_artifact) => Ok(CompiledArtifact::new(compiled_artifact)), + Ok(compiled_artifact) => Ok(PrepareOutcome { + compiled_artifact: CompiledArtifact::new(compiled_artifact), + observed_wasm_code_len, + }), Err(err) => Err(PrepareError::Preparation(format!("{:?}", err))), } } @@ -322,6 +338,7 @@ fn runtime_construction_check( struct JobResponse { artifact: CompiledArtifact, memory_stats: MemoryStats, + observed_wasm_code_len: u32, } #[cfg(target_os = "linux")] @@ -500,11 +517,11 @@ fn handle_child_process( "prepare worker", move || { #[allow(unused_mut)] - let mut result = prepare_artifact(pvf); + let mut result = prepare_artifact(pvf).map(|o| (o,)); // Get the `ru_maxrss` stat. If supported, call getrusage for the thread. #[cfg(target_os = "linux")] - let mut result = result.map(|artifact| (artifact, get_max_rss_thread())); + let mut result = result.map(|outcome| (outcome.0, get_max_rss_thread())); // If we are pre-checking, check for runtime construction errors. // @@ -513,7 +530,10 @@ fn handle_child_process( // anyway. if let PrepareJobKind::Prechecking = prepare_job_kind { result = result.and_then(|output| { - runtime_construction_check(output.0.as_ref(), &executor_params)?; + runtime_construction_check( + output.0.compiled_artifact.as_ref(), + &executor_params, + )?; Ok(output) }); } @@ -553,9 +573,9 @@ fn handle_child_process( Ok(ok) => { cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { - let (artifact, max_rss) = ok; + let (PrepareOutcome { compiled_artifact, observed_wasm_code_len }, max_rss) = ok; } else { - let artifact = ok; + let (PrepareOutcome { compiled_artifact, observed_wasm_code_len },) = ok; } } @@ -574,7 +594,11 @@ fn handle_child_process( peak_tracked_alloc: if peak_alloc > 0 { peak_alloc as u64 } else { 0u64 }, }; - Ok(JobResponse { artifact, memory_stats }) + Ok(JobResponse { + artifact: compiled_artifact, + observed_wasm_code_len, + memory_stats, + }) }, } }, @@ -665,7 +689,7 @@ fn handle_parent_process( match result { Err(err) => Err(err), - Ok(JobResponse { artifact, memory_stats }) => { + Ok(JobResponse { artifact, memory_stats, observed_wasm_code_len }) => { // The exit status should have been zero if no error occurred. if exit_status != 0 { return Err(PrepareError::JobError(format!( @@ -696,7 +720,11 @@ fn handle_parent_process( let checksum = blake3::hash(&artifact.as_ref()).to_hex().to_string(); Ok(PrepareWorkerSuccess { checksum, - stats: PrepareStats { memory_stats, cpu_time_elapsed: cpu_tv }, + stats: PrepareStats { + memory_stats, + cpu_time_elapsed: cpu_tv, + observed_wasm_code_len, + }, }) }, } diff --git a/polkadot/node/core/pvf/src/error.rs b/polkadot/node/core/pvf/src/error.rs index 8dc96305eadb..a0634106052d 100644 --- a/polkadot/node/core/pvf/src/error.rs +++ b/polkadot/node/core/pvf/src/error.rs @@ -52,6 +52,9 @@ pub enum InvalidCandidate { /// PVF execution (compilation is not included) took more time than was allotted. #[error("invalid: hard timeout")] HardTimeout, + /// Proof-of-validity failed to decompress correctly + #[error("invalid: PoV failed to decompress")] + PoVDecompressionFailure, } /// Possibly transient issue that may resolve after retries. diff --git a/polkadot/node/core/pvf/src/execute/queue.rs b/polkadot/node/core/pvf/src/execute/queue.rs index bb00a5a652d6..11031bf1074a 100644 --- a/polkadot/node/core/pvf/src/execute/queue.rs +++ b/polkadot/node/core/pvf/src/execute/queue.rs @@ -34,12 +34,14 @@ use polkadot_node_core_pvf_common::{ execute::{JobResponse, WorkerError, WorkerResponse}, SecurityStatus, }; -use polkadot_primitives::{ExecutorParams, ExecutorParamsHash}; +use polkadot_node_primitives::PoV; +use polkadot_primitives::{ExecutorParams, ExecutorParamsHash, PersistedValidationData}; use slotmap::HopSlotMap; use std::{ collections::VecDeque, fmt, path::PathBuf, + sync::Arc, time::{Duration, Instant}, }; @@ -68,7 +70,8 @@ pub enum FromQueue { #[derive(Debug)] pub struct PendingExecutionRequest { pub exec_timeout: Duration, - pub params: Vec, + pub pvd: Arc, + pub pov: Arc, pub executor_params: ExecutorParams, pub result_tx: ResultSender, } @@ -76,7 +79,8 @@ pub struct PendingExecutionRequest { struct ExecuteJob { artifact: ArtifactPathId, exec_timeout: Duration, - params: Vec, + pvd: Arc, + pov: Arc, executor_params: ExecutorParams, result_tx: ResultSender, waiting_since: Instant, @@ -293,18 +297,20 @@ async fn purge_dead(metrics: &Metrics, workers: &mut Workers) { fn handle_to_queue(queue: &mut Queue, to_queue: ToQueue) { let ToQueue::Enqueue { artifact, pending_execution_request } = to_queue; - let PendingExecutionRequest { exec_timeout, params, executor_params, result_tx } = + let PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx } = pending_execution_request; gum::debug!( target: LOG_TARGET, validation_code_hash = ?artifact.id.code_hash, "enqueueing an artifact for execution", ); + queue.metrics.observe_pov_size(pov.block_data.0.len(), true); queue.metrics.execute_enqueued(); let job = ExecuteJob { artifact, exec_timeout, - params, + pvd, + pov, executor_params, result_tx, waiting_since: Instant::now(), @@ -352,15 +358,19 @@ async fn handle_job_finish( artifact_id: ArtifactId, result_tx: ResultSender, ) { - let (idle_worker, result, duration, sync_channel) = match worker_result { + let (idle_worker, result, duration, sync_channel, pov_size) = match worker_result { Ok(WorkerInterfaceResponse { worker_response: - WorkerResponse { job_response: JobResponse::Ok { result_descriptor }, duration }, + WorkerResponse { + job_response: JobResponse::Ok { result_descriptor }, + duration, + pov_size, + }, idle_worker, }) => { // TODO: propagate the soft timeout - (Some(idle_worker), Ok(result_descriptor), Some(duration), None) + (Some(idle_worker), Ok(result_descriptor), Some(duration), None, Some(pov_size)) }, Ok(WorkerInterfaceResponse { worker_response: WorkerResponse { job_response: JobResponse::InvalidCandidate(err), .. }, @@ -370,6 +380,18 @@ async fn handle_job_finish( Err(ValidationError::Invalid(InvalidCandidate::WorkerReportedInvalid(err))), None, None, + None, + ), + Ok(WorkerInterfaceResponse { + worker_response: + WorkerResponse { job_response: JobResponse::PoVDecompressionFailure, .. }, + idle_worker, + }) => ( + Some(idle_worker), + Err(ValidationError::Invalid(InvalidCandidate::PoVDecompressionFailure)), + None, + None, + None, ), Ok(WorkerInterfaceResponse { worker_response: @@ -393,39 +415,46 @@ async fn handle_job_finish( ))), None, Some(result_rx), + None, ) }, Err(WorkerInterfaceError::InternalError(err)) | Err(WorkerInterfaceError::WorkerError(WorkerError::InternalError(err))) => - (None, Err(ValidationError::Internal(err)), None, None), + (None, Err(ValidationError::Internal(err)), None, None, None), // Either the worker or the job timed out. Kill the worker in either case. Treated as // definitely-invalid, because if we timed out, there's no time left for a retry. Err(WorkerInterfaceError::HardTimeout) | Err(WorkerInterfaceError::WorkerError(WorkerError::JobTimedOut)) => - (None, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout)), None, None), + (None, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout)), None, None, None), // "Maybe invalid" errors (will retry). Err(WorkerInterfaceError::CommunicationErr(_err)) => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousWorkerDeath)), None, None, + None, ), Err(WorkerInterfaceError::WorkerError(WorkerError::JobDied { err, .. })) => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousJobDeath(err))), None, None, + None, ), Err(WorkerInterfaceError::WorkerError(WorkerError::JobError(err))) => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::JobError(err.to_string()))), None, None, + None, ), }; queue.metrics.execute_finished(); + if let Some(pov_size) = pov_size { + queue.metrics.observe_pov_size(pov_size as usize, false) + } if let Err(ref err) = result { gum::warn!( target: LOG_TARGET, @@ -573,7 +602,8 @@ fn assign(queue: &mut Queue, worker: Worker, job: ExecuteJob) { idle, job.artifact.clone(), job.exec_timeout, - job.params, + job.pvd, + job.pov, ) .await; QueueEvent::StartWork(worker, result, job.artifact.id, job.result_tx) diff --git a/polkadot/node/core/pvf/src/execute/worker_interface.rs b/polkadot/node/core/pvf/src/execute/worker_interface.rs index d15d7c15426e..77bd6bedd75c 100644 --- a/polkadot/node/core/pvf/src/execute/worker_interface.rs +++ b/polkadot/node/core/pvf/src/execute/worker_interface.rs @@ -32,8 +32,9 @@ use polkadot_node_core_pvf_common::{ execute::{Handshake, WorkerError, WorkerResponse}, worker_dir, SecurityStatus, }; -use polkadot_primitives::ExecutorParams; -use std::{path::Path, time::Duration}; +use polkadot_node_primitives::PoV; +use polkadot_primitives::{ExecutorParams, PersistedValidationData}; +use std::{path::Path, sync::Arc, time::Duration}; use tokio::{io, net::UnixStream}; /// Spawns a new worker with the given program path that acts as the worker and the spawn timeout. @@ -123,7 +124,8 @@ pub async fn start_work( worker: IdleWorker, artifact: ArtifactPathId, execution_timeout: Duration, - validation_params: Vec, + pvd: Arc, + pov: Arc, ) -> Result { let IdleWorker { mut stream, pid, worker_dir } = worker; @@ -137,18 +139,16 @@ pub async fn start_work( ); with_worker_dir_setup(worker_dir, pid, &artifact.path, |worker_dir| async move { - send_request(&mut stream, &validation_params, execution_timeout).await.map_err( - |error| { - gum::warn!( - target: LOG_TARGET, - worker_pid = %pid, - validation_code_hash = ?artifact.id.code_hash, - "failed to send an execute request: {}", - error, - ); - Error::InternalError(InternalValidationError::HostCommunication(error.to_string())) - }, - )?; + send_request(&mut stream, pvd, pov, execution_timeout).await.map_err(|error| { + gum::warn!( + target: LOG_TARGET, + worker_pid = %pid, + validation_code_hash = ?artifact.id.code_hash, + "failed to send an execute request: {}", + error, + ); + Error::InternalError(InternalValidationError::HostCommunication(error.to_string())) + })?; // We use a generous timeout here. This is in addition to the one in the child process, in // case the child stalls. We have a wall clock timeout here in the host, but a CPU timeout @@ -288,10 +288,12 @@ async fn send_execute_handshake(stream: &mut UnixStream, handshake: Handshake) - async fn send_request( stream: &mut UnixStream, - validation_params: &[u8], + pvd: Arc, + pov: Arc, execution_timeout: Duration, ) -> io::Result<()> { - framed_send(stream, validation_params).await?; + framed_send(stream, &pvd.encode()).await?; + framed_send(stream, &pov.encode()).await?; framed_send(stream, &execution_timeout.encode()).await } diff --git a/polkadot/node/core/pvf/src/host.rs b/polkadot/node/core/pvf/src/host.rs index 462631d33b52..44a4cba2fbf8 100644 --- a/polkadot/node/core/pvf/src/host.rs +++ b/polkadot/node/core/pvf/src/host.rs @@ -36,11 +36,14 @@ use polkadot_node_core_pvf_common::{ prepare::PrepareSuccess, pvf::PvfPrepData, }; +use polkadot_node_primitives::PoV; use polkadot_node_subsystem::{SubsystemError, SubsystemResult}; use polkadot_parachain_primitives::primitives::ValidationResult; +use polkadot_primitives::PersistedValidationData; use std::{ collections::HashMap, path::PathBuf, + sync::Arc, time::{Duration, SystemTime}, }; @@ -108,7 +111,8 @@ impl ValidationHost { &mut self, pvf: PvfPrepData, exec_timeout: Duration, - params: Vec, + pvd: Arc, + pov: Arc, priority: Priority, result_tx: ResultSender, ) -> Result<(), String> { @@ -116,7 +120,8 @@ impl ValidationHost { .send(ToHost::ExecutePvf(ExecutePvfInputs { pvf, exec_timeout, - params, + pvd, + pov, priority, result_tx, })) @@ -147,7 +152,8 @@ enum ToHost { struct ExecutePvfInputs { pvf: PvfPrepData, exec_timeout: Duration, - params: Vec, + pvd: Arc, + pov: Arc, priority: Priority, result_tx: ResultSender, } @@ -539,7 +545,7 @@ async fn handle_execute_pvf( awaiting_prepare: &mut AwaitingPrepare, inputs: ExecutePvfInputs, ) -> Result<(), Fatal> { - let ExecutePvfInputs { pvf, exec_timeout, params, priority, result_tx } = inputs; + let ExecutePvfInputs { pvf, exec_timeout, pvd, pov, priority, result_tx } = inputs; let artifact_id = ArtifactId::from_pvf_prep_data(&pvf); let executor_params = (*pvf.executor_params()).clone(); @@ -558,7 +564,8 @@ async fn handle_execute_pvf( artifact: ArtifactPathId::new(artifact_id, path), pending_execution_request: PendingExecutionRequest { exec_timeout, - params, + pvd, + pov, executor_params, result_tx, }, @@ -587,7 +594,8 @@ async fn handle_execute_pvf( artifact_id, PendingExecutionRequest { exec_timeout, - params, + pvd, + pov, executor_params, result_tx, }, @@ -598,7 +606,7 @@ async fn handle_execute_pvf( ArtifactState::Preparing { .. } => { awaiting_prepare.add( artifact_id, - PendingExecutionRequest { exec_timeout, params, executor_params, result_tx }, + PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx }, ); }, ArtifactState::FailedToProcess { last_time_failed, num_failures, error } => { @@ -627,7 +635,8 @@ async fn handle_execute_pvf( artifact_id, PendingExecutionRequest { exec_timeout, - params, + pvd, + pov, executor_params, result_tx, }, @@ -648,7 +657,7 @@ async fn handle_execute_pvf( pvf, priority, artifact_id, - PendingExecutionRequest { exec_timeout, params, executor_params, result_tx }, + PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx }, ) .await?; } @@ -770,7 +779,7 @@ async fn handle_prepare_done( // It's finally time to dispatch all the execution requests that were waiting for this artifact // to be prepared. let pending_requests = awaiting_prepare.take(&artifact_id); - for PendingExecutionRequest { exec_timeout, params, executor_params, result_tx } in + for PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx } in pending_requests { if result_tx.is_canceled() { @@ -793,7 +802,8 @@ async fn handle_prepare_done( artifact: ArtifactPathId::new(artifact_id.clone(), &path), pending_execution_request: PendingExecutionRequest { exec_timeout, - params, + pvd, + pov, executor_params, result_tx, }, @@ -967,6 +977,8 @@ pub(crate) mod tests { use assert_matches::assert_matches; use futures::future::BoxFuture; use polkadot_node_core_pvf_common::prepare::PrepareStats; + use polkadot_node_primitives::BlockData; + use sp_core::H256; const TEST_EXECUTION_TIMEOUT: Duration = Duration::from_secs(3); pub(crate) const TEST_PREPARATION_TIMEOUT: Duration = Duration::from_secs(30); @@ -1223,12 +1235,21 @@ pub(crate) mod tests { async fn execute_pvf_requests() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov1 = Arc::new(PoV { block_data: BlockData(b"pov1".to_vec()) }); + let pov2 = Arc::new(PoV { block_data: BlockData(b"pov2".to_vec()) }); let (result_tx, result_rx_pvf_1_1) = oneshot::channel(); host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf1".to_vec(), + pvd.clone(), + pov1.clone(), Priority::Normal, result_tx, ) @@ -1239,7 +1260,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf1".to_vec(), + pvd.clone(), + pov1, Priority::Critical, result_tx, ) @@ -1250,7 +1272,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(2), TEST_EXECUTION_TIMEOUT, - b"pvf2".to_vec(), + pvd, + pov2, Priority::Normal, result_tx, ) @@ -1382,6 +1405,13 @@ pub(crate) mod tests { async fn test_prepare_done() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); // Test mixed cases of receiving execute and precheck requests // for the same PVF. @@ -1391,7 +1421,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf2".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx, ) @@ -1438,7 +1469,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(2), TEST_EXECUTION_TIMEOUT, - b"pvf2".to_vec(), + pvd, + pov, Priority::Critical, result_tx, ) @@ -1534,13 +1566,21 @@ pub(crate) mod tests { async fn test_execute_prepare_retry() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); // Submit a execute request that fails. let (result_tx, result_rx) = oneshot::channel(); host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx, ) @@ -1570,7 +1610,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx_2, ) @@ -1592,7 +1633,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx_3, ) @@ -1636,13 +1678,21 @@ pub(crate) mod tests { async fn test_execute_prepare_no_retry() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); // Submit an execute request that fails. let (result_tx, result_rx) = oneshot::channel(); host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx, ) @@ -1672,7 +1722,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx_2, ) @@ -1694,7 +1745,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx_3, ) @@ -1755,12 +1807,20 @@ pub(crate) mod tests { async fn cancellation() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); let (result_tx, result_rx) = oneshot::channel(); host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf1".to_vec(), + pvd, + pov, Priority::Normal, result_tx, ) diff --git a/polkadot/node/core/pvf/src/metrics.rs b/polkadot/node/core/pvf/src/metrics.rs index bc8d300037fe..c59cab464180 100644 --- a/polkadot/node/core/pvf/src/metrics.rs +++ b/polkadot/node/core/pvf/src/metrics.rs @@ -105,6 +105,21 @@ impl Metrics { .observe((memory_stats.peak_tracked_alloc / 1024) as f64); } } + + pub(crate) fn observe_code_size(&self, code_size: usize) { + if let Some(metrics) = &self.0 { + metrics.code_size.observe(code_size as f64); + } + } + + pub(crate) fn observe_pov_size(&self, pov_size: usize, compressed: bool) { + if let Some(metrics) = &self.0 { + metrics + .pov_size + .with_label_values(&[if compressed { "true" } else { "false" }]) + .observe(pov_size as f64); + } + } } #[derive(Clone)] @@ -129,6 +144,8 @@ struct MetricsInner { preparation_max_resident: prometheus::Histogram, // Peak allocation value, tracked by tracking-allocator preparation_peak_tracked_allocation: prometheus::Histogram, + pov_size: prometheus::HistogramVec, + code_size: prometheus::Histogram, } impl metrics::Metrics for Metrics { @@ -323,6 +340,35 @@ impl metrics::Metrics for Metrics { )?, registry, )?, + // The following metrics was moved here from the candidate valiidation subsystem. + // Names are kept to avoid breaking dashboards and stuff. + pov_size: prometheus::register( + prometheus::HistogramVec::new( + prometheus::HistogramOpts::new( + "polkadot_parachain_candidate_validation_pov_size", + "The compressed and decompressed size of the proof of validity of a candidate", + ) + .buckets( + prometheus::exponential_buckets(16384.0, 2.0, 10) + .expect("arguments are always valid; qed"), + ), + &["compressed"], + )?, + registry, + )?, + code_size: prometheus::register( + prometheus::Histogram::with_opts( + prometheus::HistogramOpts::new( + "polkadot_parachain_candidate_validation_code_size", + "The size of the decompressed WASM validation blob used for checking a candidate", + ) + .buckets( + prometheus::exponential_buckets(16384.0, 2.0, 10) + .expect("arguments are always valid; qed"), + ), + )?, + registry, + )?, }; Ok(Metrics(Some(inner))) } diff --git a/polkadot/node/core/pvf/src/prepare/worker_interface.rs b/polkadot/node/core/pvf/src/prepare/worker_interface.rs index 22ee93319d84..d29d2717c4b6 100644 --- a/polkadot/node/core/pvf/src/prepare/worker_interface.rs +++ b/polkadot/node/core/pvf/src/prepare/worker_interface.rs @@ -211,7 +211,7 @@ async fn handle_response( // https://github.com/paritytech/polkadot-sdk/issues/2399 let PrepareWorkerSuccess { checksum: _, - stats: PrepareStats { cpu_time_elapsed, memory_stats }, + stats: PrepareStats { cpu_time_elapsed, memory_stats, observed_wasm_code_len }, } = match result.clone() { Ok(result) => result, // Timed out on the child. This should already be logged by the child. @@ -221,6 +221,8 @@ async fn handle_response( Err(err) => return Outcome::Concluded { worker, result: Err(err) }, }; + metrics.observe_code_size(observed_wasm_code_len as usize); + if cpu_time_elapsed > preparation_timeout { // The job didn't complete within the timeout. gum::warn!( @@ -267,7 +269,11 @@ async fn handle_response( result: Ok(PrepareSuccess { path: artifact_path, size, - stats: PrepareStats { cpu_time_elapsed, memory_stats: memory_stats.clone() }, + stats: PrepareStats { + cpu_time_elapsed, + memory_stats: memory_stats.clone(), + observed_wasm_code_len, + }, }), }, Err(err) => { diff --git a/polkadot/node/core/pvf/tests/it/adder.rs b/polkadot/node/core/pvf/tests/it/adder.rs index 455e8c36c88d..1a95a28fe077 100644 --- a/polkadot/node/core/pvf/tests/it/adder.rs +++ b/polkadot/node/core/pvf/tests/it/adder.rs @@ -18,29 +18,33 @@ use super::TestHost; use codec::{Decode, Encode}; +use polkadot_node_primitives::PoV; use polkadot_parachain_primitives::primitives::{ - BlockData as GenericBlockData, HeadData as GenericHeadData, RelayChainBlockNumber, - ValidationParams, + BlockData as GenericBlockData, HeadData as GenericHeadData, }; +use polkadot_primitives::PersistedValidationData; +use sp_core::H256; use test_parachain_adder::{hash_state, BlockData, HeadData}; #[tokio::test] async fn execute_good_block_on_parent() { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; - let block_data = BlockData { state: 0, add: 512 }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let host = TestHost::new().await; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -63,18 +67,20 @@ async fn execute_good_chain_on_parent() { for (number, add) in (0..10).enumerate() { let parent_head = HeadData { number: number as u64, parent_hash, post_state: hash_state(last_state) }; - let block_data = BlockData { state: last_state, add }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: number as RelayChainBlockNumber + 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -94,23 +100,25 @@ async fn execute_good_chain_on_parent() { #[tokio::test] async fn execute_bad_block_on_parent() { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; - let block_data = BlockData { state: 256, // start state is wrong. add: 256, }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let host = TestHost::new().await; let _err = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -124,15 +132,18 @@ async fn stress_spawn() { async fn execute(host: std::sync::Arc) { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 0, add: 512 }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -161,15 +172,18 @@ async fn execute_can_run_serially() { async fn execute(host: std::sync::Arc) { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 0, add: 512 }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await diff --git a/polkadot/node/core/pvf/tests/it/main.rs b/polkadot/node/core/pvf/tests/it/main.rs index 9ad486657512..a4a085318957 100644 --- a/polkadot/node/core/pvf/tests/it/main.rs +++ b/polkadot/node/core/pvf/tests/it/main.rs @@ -17,7 +17,6 @@ //! General PVF host integration tests checking the functionality of the PVF host itself. use assert_matches::assert_matches; -use codec::Encode as _; #[cfg(all(feature = "ci-only-tests", target_os = "linux"))] use polkadot_node_core_pvf::SecurityStatus; use polkadot_node_core_pvf::{ @@ -25,10 +24,14 @@ use polkadot_node_core_pvf::{ PossiblyInvalidError, PrepareError, PrepareJobKind, PvfPrepData, ValidationError, ValidationHost, JOB_TIMEOUT_WALL_CLOCK_FACTOR, }; -use polkadot_parachain_primitives::primitives::{BlockData, ValidationParams, ValidationResult}; -use polkadot_primitives::{ExecutorParam, ExecutorParams, PvfExecKind, PvfPrepKind}; +use polkadot_node_primitives::{PoV, POV_BOMB_LIMIT, VALIDATION_CODE_BOMB_LIMIT}; +use polkadot_parachain_primitives::primitives::{BlockData, ValidationResult}; +use polkadot_primitives::{ + ExecutorParam, ExecutorParams, PersistedValidationData, PvfExecKind, PvfPrepKind, +}; +use sp_core::H256; -use std::{io::Write, time::Duration}; +use std::{io::Write, sync::Arc, time::Duration}; use tokio::sync::Mutex; mod adder; @@ -80,9 +83,6 @@ impl TestHost { ) -> Result<(), PrepareError> { let (result_tx, result_rx) = futures::channel::oneshot::channel(); - let code = sp_maybe_compressed_blob::decompress(code, 16 * 1024 * 1024) - .expect("Compression works"); - self.host .lock() .await @@ -103,14 +103,12 @@ impl TestHost { async fn validate_candidate( &self, code: &[u8], - params: ValidationParams, + pvd: PersistedValidationData, + pov: PoV, executor_params: ExecutorParams, ) -> Result { let (result_tx, result_rx) = futures::channel::oneshot::channel(); - let code = sp_maybe_compressed_blob::decompress(code, 16 * 1024 * 1024) - .expect("Compression works"); - self.host .lock() .await @@ -122,7 +120,8 @@ impl TestHost { PrepareJobKind::Compilation, ), TEST_EXECUTION_TIMEOUT, - params.encode(), + Arc::new(pvd), + Arc::new(pov), polkadot_node_core_pvf::Priority::Normal, result_tx, ) @@ -159,19 +158,17 @@ async fn prepare_job_terminates_on_timeout() { #[tokio::test] async fn execute_job_terminates_on_timeout() { let host = TestHost::new().await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let start = std::time::Instant::now(); let result = host - .validate_candidate( - test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, - Default::default(), - ) + .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) .await; match result { @@ -189,24 +186,23 @@ async fn execute_job_terminates_on_timeout() { async fn ensure_parallel_execution() { // Run some jobs that do not complete, thus timing out. let host = TestHost::new().await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let execute_pvf_future_1 = host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd.clone(), + pov.clone(), Default::default(), ); let execute_pvf_future_2 = host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ); @@ -237,6 +233,13 @@ async fn execute_queue_doesnt_stall_if_workers_died() { cfg.execute_workers_max_num = 5; }) .await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; // Here we spawn 8 validation jobs for the `halt` PVF and share those between 5 workers. The // first five jobs should timeout and the workers killed. For the next 3 jobs a new batch of @@ -245,12 +248,8 @@ async fn execute_queue_doesnt_stall_if_workers_died() { futures::future::join_all((0u8..=8).map(|_| { host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd.clone(), + pov.clone(), Default::default(), ) })) @@ -275,6 +274,13 @@ async fn execute_queue_doesnt_stall_with_varying_executor_params() { cfg.execute_workers_max_num = 2; }) .await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let executor_params_1 = ExecutorParams::default(); let executor_params_2 = ExecutorParams::from(&[ExecutorParam::StackLogicalMax(1024)][..]); @@ -288,12 +294,8 @@ async fn execute_queue_doesnt_stall_with_varying_executor_params() { futures::future::join_all((0u8..6).map(|i| { host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd.clone(), + pov.clone(), match i % 3 { 0 => executor_params_1.clone(), _ => executor_params_2.clone(), @@ -324,6 +326,13 @@ async fn execute_queue_doesnt_stall_with_varying_executor_params() { async fn deleting_prepared_artifact_does_not_dispute() { let host = TestHost::new().await; let cache_dir = host.cache_dir.path(); + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let _stats = host .precheck_pvf(test_parachain_halt::wasm_binary_unwrap(), Default::default()) @@ -347,16 +356,7 @@ async fn deleting_prepared_artifact_does_not_dispute() { // Try to validate, artifact should get recreated. let result = host - .validate_candidate( - test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, - Default::default(), - ) + .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) .await; assert_matches!(result, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout))); @@ -367,6 +367,13 @@ async fn deleting_prepared_artifact_does_not_dispute() { async fn corrupted_prepared_artifact_does_not_dispute() { let host = TestHost::new().await; let cache_dir = host.cache_dir.path(); + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let _stats = host .precheck_pvf(test_parachain_halt::wasm_binary_unwrap(), Default::default()) @@ -400,16 +407,7 @@ async fn corrupted_prepared_artifact_does_not_dispute() { // Try to validate, artifact should get removed because of the corruption. let result = host - .validate_candidate( - test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, - Default::default(), - ) + .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) .await; assert_matches!( @@ -652,3 +650,65 @@ async fn artifact_does_reprepare_on_meaningful_exec_parameter_change() { assert_eq!(cache_dir_contents.len(), 3); // new artifact has been added } + +// Checks that we cannot prepare oversized compressed code +#[tokio::test] +async fn invalid_compressed_code_fails_prechecking() { + let host = TestHost::new().await; + let raw_code = vec![2u8; VALIDATION_CODE_BOMB_LIMIT + 1]; + let validation_code = + sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1).unwrap(); + + let res = host.precheck_pvf(&validation_code, Default::default()).await; + + assert_matches!(res, Err(PrepareError::CouldNotDecompressCodeBlob(_))); +} + +// Checks that we cannot validate with oversized compressed code +#[tokio::test] +async fn invalid_compressed_code_fails_validation() { + let host = TestHost::new().await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; + + let raw_code = vec![2u8; VALIDATION_CODE_BOMB_LIMIT + 1]; + let validation_code = + sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1).unwrap(); + + let result = host.validate_candidate(&validation_code, pvd, pov, Default::default()).await; + + assert_matches!( + result, + Err(ValidationError::Preparation(PrepareError::CouldNotDecompressCodeBlob(_))) + ); +} + +// Checks that we cannot validate with an oversized PoV +#[tokio::test] +async fn invalid_compressed_pov_fails_validation() { + let host = TestHost::new().await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let raw_block_data = vec![1u8; POV_BOMB_LIMIT + 1]; + let block_data = + sp_maybe_compressed_blob::compress(&raw_block_data, POV_BOMB_LIMIT + 1).unwrap(); + let pov = PoV { block_data: BlockData(block_data) }; + + let result = host + .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) + .await; + + assert_matches!( + result, + Err(ValidationError::Invalid(InvalidCandidate::PoVDecompressionFailure)) + ); +} diff --git a/polkadot/node/core/pvf/tests/it/process.rs b/polkadot/node/core/pvf/tests/it/process.rs index b8fd2cdce0ce..b3023c8a45c3 100644 --- a/polkadot/node/core/pvf/tests/it/process.rs +++ b/polkadot/node/core/pvf/tests/it/process.rs @@ -23,11 +23,14 @@ use codec::Encode; use polkadot_node_core_pvf::{ InvalidCandidate, PossiblyInvalidError, PrepareError, ValidationError, }; +use polkadot_node_primitives::PoV; use polkadot_parachain_primitives::primitives::{ - BlockData as GenericBlockData, HeadData as GenericHeadData, ValidationParams, + BlockData as GenericBlockData, HeadData as GenericHeadData, }; +use polkadot_primitives::PersistedValidationData; use procfs::process; use rusty_fork::rusty_fork_test; +use sp_core::H256; use std::{future::Future, sync::Arc, time::Duration}; use test_parachain_adder::{hash_state, BlockData, HeadData}; @@ -125,15 +128,18 @@ rusty_fork_test! { test_wrapper(|host, _sid| async move { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 0, add: 512 }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -166,17 +172,20 @@ rusty_fork_test! { // Prepare the artifact ahead of time. let binary = test_parachain_halt::wasm_binary_unwrap(); host.precheck_pvf(binary, Default::default()).await.unwrap(); + let pvd = PersistedValidationData { + parent_head: GenericHeadData(HeadData::default().encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(Vec::new()) }; let (result, _) = futures::join!( // Choose an job that would normally take the entire timeout. host.validate_candidate( binary, - ValidationParams { - block_data: GenericBlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ), // Send a stop signal to pause the worker. @@ -218,17 +227,20 @@ rusty_fork_test! { // Prepare the artifact ahead of time. let binary = test_parachain_halt::wasm_binary_unwrap(); host.precheck_pvf(binary, Default::default()).await.unwrap(); + let pvd = PersistedValidationData { + parent_head: GenericHeadData(HeadData::default().encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(Vec::new()) }; let (result, _) = futures::join!( // Choose an job that would normally take the entire timeout. host.validate_candidate( binary, - ValidationParams { - block_data: GenericBlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ), // Run a future that kills the job while it's running. @@ -274,17 +286,20 @@ rusty_fork_test! { // Prepare the artifact ahead of time. let binary = test_parachain_halt::wasm_binary_unwrap(); host.precheck_pvf(binary, Default::default()).await.unwrap(); + let pvd = PersistedValidationData { + parent_head: GenericHeadData(HeadData::default().encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(Vec::new()) }; let (result, _) = futures::join!( // Choose a job that would normally take the entire timeout. host.validate_candidate( binary, - ValidationParams { - block_data: GenericBlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ), // Run a future that kills the job while it's running. @@ -342,17 +357,20 @@ rusty_fork_test! { // Prepare the artifact ahead of time. let binary = test_parachain_halt::wasm_binary_unwrap(); host.precheck_pvf(binary, Default::default()).await.unwrap(); + let pvd = PersistedValidationData { + parent_head: GenericHeadData(HeadData::default().encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(Vec::new()) }; let _ = futures::join!( // Choose a job that would normally take the entire timeout. host.validate_candidate( binary, - ValidationParams { - block_data: GenericBlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ), // Run a future that tests the thread count while the worker is running. diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs index 4e13d5eda76f..baaff9c7c9f6 100644 --- a/polkadot/node/overseer/src/lib.rs +++ b/polkadot/node/overseer/src/lib.rs @@ -466,7 +466,7 @@ pub async fn forward_events>(client: Arc

, mut hand message_capacity=2048, )] pub struct Overseer { - #[subsystem(blocking, CandidateValidationMessage, sends: [ + #[subsystem(CandidateValidationMessage, sends: [ RuntimeApiMessage, ])] candidate_validation: CandidateValidation, diff --git a/prdoc/pr_5142.prdoc b/prdoc/pr_5142.prdoc new file mode 100644 index 000000000000..4083e5bf53cd --- /dev/null +++ b/prdoc/pr_5142.prdoc @@ -0,0 +1,26 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Move decompression to worker processes" + +doc: + - audience: Node Dev + description: | + Candidate validation subsystem performed the PVF code decompression as well as the PoV + decompression itself which might affect the subsystem main loop performance and required + it to run on the blocking threadpool. This change moves the decompression to PVF host + workers running synchronously in separate processes. + +crates: + - name: polkadot-node-core-candidate-validation + bump: patch + - name: polkadot-overseer + bump: patch + - name: polkadot-node-core-pvf + bump: major + - name: polkadot-node-core-pvf-common + bump: major + - name: polkadot-node-core-pvf-execute-worker + bump: patch + - name: polkadot-node-core-pvf-prepare-worker + bump: patch From 149c70938f2b29f8d92ba1cc952aeb63d4084e27 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Fri, 9 Aug 2024 18:11:50 +0200 Subject: [PATCH 086/382] Add missing features in templates' node packages (#5294) Corrects the issue we had [here](https://github.com/paritytech/polkadot-sdk-parachain-template/pull/10), in which `cargo build --release` worked but `cargo build --package parachain-template-node --release` failed with missing features. The command has been added to CI to make sure it works, but at the same we're changing it in the readme to just `cargo build --release` for simplification. Labeling silent because those packages are un-published as part of the regular release process. --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Shawn Tabrizi Co-authored-by: Oliver Tale-Yazdi --- templates/minimal/README.md | 2 +- templates/minimal/node/Cargo.toml | 5 ++++- templates/parachain/README.md | 2 +- templates/parachain/node/Cargo.toml | 7 ++++++- templates/solochain/README.md | 2 +- templates/solochain/node/Cargo.toml | 5 ++++- 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/templates/minimal/README.md b/templates/minimal/README.md index b556a4536089..180c229e744e 100644 --- a/templates/minimal/README.md +++ b/templates/minimal/README.md @@ -42,7 +42,7 @@ packages required to compile this template - please take note of the Rust compil 🔨 Use the following command to build the node without launching it: ```sh -cargo build --package minimal-template-node --release +cargo build --release ``` 🐳 Alternatively, build the docker image: diff --git a/templates/minimal/node/Cargo.toml b/templates/minimal/node/Cargo.toml index 70b24c19f8e7..da5073ea687c 100644 --- a/templates/minimal/node/Cargo.toml +++ b/templates/minimal/node/Cargo.toml @@ -57,4 +57,7 @@ minimal-template-runtime = { workspace = true } substrate-build-script-utils = { workspace = true, default-features = true } [features] -default = [] +default = ["std"] +std = [ + "minimal-template-runtime/std", +] diff --git a/templates/parachain/README.md b/templates/parachain/README.md index 802d8586b39e..b7fa2bed0ed9 100644 --- a/templates/parachain/README.md +++ b/templates/parachain/README.md @@ -44,7 +44,7 @@ packages required to compile this template - please take note of the Rust compil 🔨 Use the following command to build the node without launching it: ```sh -cargo build --package parachain-template-node --release +cargo build --release ``` 🐳 Alternatively, build the docker image: diff --git a/templates/parachain/node/Cargo.toml b/templates/parachain/node/Cargo.toml index 7cf1f1fddc7b..c782888a3e89 100644 --- a/templates/parachain/node/Cargo.toml +++ b/templates/parachain/node/Cargo.toml @@ -79,7 +79,12 @@ color-print = { workspace = true } substrate-build-script-utils = { workspace = true, default-features = true } [features] -default = [] +default = ["std"] +std = [ + "log/std", + "parachain-template-runtime/std", + "xcm/std", +] runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", "frame-benchmarking-cli/runtime-benchmarks", diff --git a/templates/solochain/README.md b/templates/solochain/README.md index c5dc5db7f3b5..6a5a7853f9c0 100644 --- a/templates/solochain/README.md +++ b/templates/solochain/README.md @@ -28,7 +28,7 @@ installation](#alternatives-installations) options. Use the following command to build the node without launching it: ```sh -cargo build --package solochain-template-node --release +cargo build --release ``` ### Embedded Docs diff --git a/templates/solochain/node/Cargo.toml b/templates/solochain/node/Cargo.toml index 6eebf3694e3a..9dd1b144d229 100644 --- a/templates/solochain/node/Cargo.toml +++ b/templates/solochain/node/Cargo.toml @@ -66,7 +66,10 @@ solochain-template-runtime = { workspace = true } substrate-build-script-utils = { workspace = true, default-features = true } [features] -default = [] +default = ["std"] +std = [ + "solochain-template-runtime/std", +] # Dependencies that are only required if runtime benchmarking should be build. runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", From 0b52a2c19ebcf5d7a0d07974b70aec656704d249 Mon Sep 17 00:00:00 2001 From: Alin Dima Date: Mon, 12 Aug 2024 11:03:23 +0300 Subject: [PATCH 087/382] prospective-parachains rework: take II (#4937) Resolves https://github.com/paritytech/polkadot-sdk/issues/4800 # Problem In https://github.com/paritytech/polkadot-sdk/pull/4035, we removed support for parachain forks and cycles and added support for backing unconnected candidates (candidates for which we don't yet know the full path to the latest included block), which is useful for elastic scaling (parachains using multiple cores). Removing support for backing forks turned out to be a bad idea, as there are legitimate cases for a parachain to fork (if they have other consensus mechanism for example, like BABE or PoW). This leads to validators getting lower backing rewards (depending on whether they back the winning fork or not) and a higher pressure on only the half of the backing group (during availability-distribution for example). Since we don't yet have approval voting rewards, backing rewards are a pretty big deal (which may change in the future). # Description A backing group is now allowed to back forks. Once a candidate becomes backed (has the minimum backing votes), we don't accept new forks unless they adhere to the new fork selection rule (have a lower candidate hash). This helps with keeping the implementation simpler, since forks will only be taken into account for candidates which are not backed yet (only seconded). Having this fork selection rule also helps with reducing the work backing validators need to do, since they have a shared way of picking the winning fork. Once they see a candidate backed, they can all decide to back a fork and not accept new ones. But they still accept new ones during the seconding phase (until the backing quorum is reached). Therefore, a block author which is not part of the backing group will likely not even see the forks (only the winning one). Just as before, a parachain producing forks will still not be able to leverage elastic scaling but will still work with a single core. Also, cycles are still not accepted. ## Some implementation details `CandidateStorage` is no longer a subsystem-wide construct. It was previously holding candidates from all relay chain forks and complicated the code. Each fragment chain now holds their candidate chain and their potential candidates. This should not increase the storage consumption since the heavy candidate data is already wrapped in an Arc and shared. It however allows for great simplifications and increase readability. `FragmentChain`s are now only creating a chain with backed candidates and the fork selection rule. As said before, `FragmentChain`s are now also responsible for maintaining their own potential candidate storage. Since we no longer have the subsytem-wide `CandidateStorage`, when getting a new leaf update, we use the storage of our latest ancestor, which may contain candidates seconded/backed that are still in scope. When a candidate is backed, the fragment chains which hold it are recreated (due to the fork selection rule, it could trigger a "reorg" of the fragment chain). I generally tried to simplify the subsystem and not introduce unneccessary optimisations that would otherwise complicate the code and not gain us much (fragment chains wouldn't realistically ever hold many candidates) TODO: - [x] update metrics - [x] update docs and comments - [x] fix and add unit tests - [x] tested with fork-producing parachain - [x] tested with cycle-producing parachain - [x] versi test - [x] prdoc --- Cargo.lock | 10 +- .../core/prospective-parachains/Cargo.toml | 10 +- .../core/prospective-parachains/src/error.rs | 15 - .../src/fragment_chain/mod.rs | 1357 +++++++---- .../src/fragment_chain/tests.rs | 2156 ++++++++--------- .../core/prospective-parachains/src/lib.rs | 638 ++--- .../prospective-parachains/src/metrics.rs | 105 +- .../core/prospective-parachains/src/tests.rs | 691 ++++-- polkadot/node/core/provisioner/src/lib.rs | 8 +- .../statement-distribution/src/v2/mod.rs | 4 +- polkadot/node/subsystem-types/src/messages.rs | 40 +- .../src/backing_implicit_view.rs | 76 + .../src/inclusion_emulator/mod.rs | 114 +- prdoc/pr_4937.prdoc | 21 + 14 files changed, 2921 insertions(+), 2324 deletions(-) create mode 100644 prdoc/pr_4937.prdoc diff --git a/Cargo.lock b/Cargo.lock index 4db38311fe66..6ebacc9ec5a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13443,23 +13443,17 @@ name = "polkadot-node-core-prospective-parachains" version = "6.0.0" dependencies = [ "assert_matches", - "bitvec", "fatality", "futures", - "parity-scale-codec", - "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-test-helpers", - "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", + "rand", "rstest", - "sc-keystore", - "sp-application-crypto", "sp-core", - "sp-keyring", - "sp-keystore", + "sp-tracing 16.0.0", "thiserror", "tracing-gum", ] diff --git a/polkadot/node/core/prospective-parachains/Cargo.toml b/polkadot/node/core/prospective-parachains/Cargo.toml index 97da5a1e94a0..705014e67a05 100644 --- a/polkadot/node/core/prospective-parachains/Cargo.toml +++ b/polkadot/node/core/prospective-parachains/Cargo.toml @@ -12,24 +12,18 @@ workspace = true [dependencies] futures = { workspace = true } gum = { workspace = true, default-features = true } -codec = { workspace = true, default-features = true } thiserror = { workspace = true } fatality = { workspace = true } -bitvec = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } -polkadot-node-primitives = { workspace = true, default-features = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } [dev-dependencies] assert_matches = { workspace = true } polkadot-node-subsystem-test-helpers = { workspace = true } -polkadot-node-subsystem-types = { workspace = true, default-features = true } polkadot-primitives-test-helpers = { workspace = true } +sp-tracing = { workspace = true } sp-core = { workspace = true, default-features = true } -sc-keystore = { workspace = true, default-features = true } -sp-application-crypto = { workspace = true, default-features = true } -sp-keyring = { workspace = true, default-features = true } -sp-keystore = { workspace = true, default-features = true } +rand = { workspace = true } rstest = { workspace = true } diff --git a/polkadot/node/core/prospective-parachains/src/error.rs b/polkadot/node/core/prospective-parachains/src/error.rs index 2b0933ab1c7e..4b332b9c5de5 100644 --- a/polkadot/node/core/prospective-parachains/src/error.rs +++ b/polkadot/node/core/prospective-parachains/src/error.rs @@ -30,18 +30,6 @@ use fatality::Nested; #[allow(missing_docs)] #[fatality::fatality(splitable)] pub enum Error { - #[fatal] - #[error("SubsystemError::Context error: {0}")] - SubsystemContext(String), - - #[fatal] - #[error("Spawning a task failed: {0}")] - SpawnFailed(SubsystemError), - - #[fatal] - #[error("Participation worker receiver exhausted.")] - ParticipationWorkerReceiverExhausted, - #[fatal] #[error("Receiving message from overseer failed: {0}")] SubsystemReceive(#[source] SubsystemError), @@ -55,9 +43,6 @@ pub enum Error { #[error(transparent)] ChainApi(#[from] ChainApiError), - #[error(transparent)] - Subsystem(SubsystemError), - #[error("Request to chain API subsystem dropped")] ChainApiRequestCanceled(oneshot::Canceled), diff --git a/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs b/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs index b5fe70e76923..b060897d4391 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs @@ -18,41 +18,58 @@ //! //! # Overview //! -//! This module exposes two main types: [`FragmentChain`] and [`CandidateStorage`] which are meant -//! to be used in close conjunction. Each fragment chain is associated with a particular -//! relay-parent and each node in the chain represents a candidate. Each parachain has a single -//! candidate storage, but can have one chain for each relay chain block in the view. -//! Therefore, the same candidate can be present in multiple fragment chains of a parachain. One of -//! the purposes of the candidate storage is to deduplicate the large candidate data that is being -//! referenced from multiple fragment chains. +//! The main type exposed by this module is the [`FragmentChain`]. //! -//! A chain has an associated [`Scope`] which defines limits on candidates within the chain. -//! Candidates themselves have their own [`Constraints`] which are either the constraints from the -//! scope, or, if there are previous nodes in the chain, a modified version of the previous -//! candidate's constraints. +//! Each fragment chain is associated with a particular relay-parent (an active leaf) and has a +//! [`Scope`], which contains the allowed relay parents (up to `allowed_ancestry_len`), the pending +//! availability candidates and base constraints derived from the latest included candidate. Each +//! parachain has a single `FragmentChain` for each active leaf where it's scheduled. //! -//! Another use of the `CandidateStorage` is to keep a record of candidates which may not be yet -//! included in any chain, but which may become part of a chain in the future. This is needed for -//! elastic scaling, so that we may parallelise the backing process across different groups. As long -//! as some basic constraints are not violated by an unconnected candidate (like the relay parent -//! being in scope), we proceed with the backing process, hoping that its predecessors will be -//! backed soon enough. This is commonly called a potential candidate. Note that not all potential -//! candidates will be maintained in the CandidateStorage. The total number of connected + potential -//! candidates will be at most max_candidate_depth + 1. +//! A fragment chain consists mainly of the current best backable chain (we'll call this the best +//! chain) and a storage of unconnected potential candidates (we'll call this the unconnected +//! storage). +//! +//! The best chain contains all the candidates pending availability and a subsequent chain +//! of candidates that have reached the backing quorum and are better than any other backable forks +//! according to the fork selection rule (more on this rule later). It has a length of size at most +//! `max_candidate_depth + 1`. +//! +//! The unconnected storage keeps a record of seconded/backable candidates that may be +//! added to the best chain in the future. +//! Once a candidate is seconded, it becomes part of this unconnected storage. +//! Only after it is backed it may be added to the best chain (but not necessarily). It's only +//! added if it builds on the latest candidate in the chain and if there isn't a better backable +//! candidate according to the fork selection rule. +//! +//! An important thing to note is that the candidates present in the unconnected storage may have +//! any/no relationship between them. In other words, they may form N trees and may even form +//! cycles. This is needed so that we may begin validating candidates for which we don't yet know +//! their parent (so we may parallelize the backing process across different groups for elastic +//! scaling) and so that we accept parachain forks. +//! +//! We accept parachain forks only if the fork selection rule allows for it. In other words, if we +//! have a backed candidate, we begin seconding/validating a fork only if it has a lower candidate +//! hash. Once both forks are backed, we discard the one with the higher candidate hash. +//! We assume all validators pick the same fork according to the fork selection rule. If we decided +//! to not accept parachain forks, candidates could end up getting only half of the backing votes or +//! even less (for forks of larger arity). This would affect the validator rewards. Still, we don't +//! guarantee that a fork-producing parachains will be able to fully use elastic scaling. +//! +//! Once a candidate is backed and becomes part of the best chain, we can trim from the +//! unconnected storage candidates which constitute forks on the best chain and no longer have +//! potential. //! //! This module also makes use of types provided by the Inclusion Emulator module, such as //! [`Fragment`] and [`Constraints`]. These perform the actual job of checking for validity of //! prospective fragments. //! -//! # Parachain forks +//! # Fork choice rule //! -//! Parachains are expected to not create forks, hence the use of fragment chains as opposed to -//! fragment trees. If parachains do create forks, their performance in regards to async backing and -//! elastic scaling will suffer, because different validators will have different views of the -//! future. +//! The motivation for the fork choice rule is described in the previous chapter. //! -//! This is a compromise we can make - collators which want to use async backing and elastic scaling -//! need to cooperate for the highest throughput. +//! The current rule is: choose the candidate with the lower candidate hash. +//! The candidate hash is quite random and finding a candidate with a lower hash in order to favour +//! it would essentially mean solving a proof of work problem. //! //! # Parachain cycles //! @@ -65,70 +82,117 @@ //! resolved by having candidates reference their parent by candidate hash. //! //! However, dealing with cycles increases complexity during the backing/inclusion process for no -//! practical reason. Therefore, fragment chains will not accept such candidates. +//! practical reason. +//! These cycles may be accepted by fragment chains while candidates are part of the unconnected +//! storage, but they will definitely not make it to the best chain. //! //! On the other hand, enforcing that a parachain will NEVER be acyclic would be very complicated //! (looping through the entire parachain's history on every new candidate or changing the candidate //! receipt to reference the parent's candidate hash). //! +//! Therefore, we don't provide a guarantee that a cycle-producing parachain will work (although in +//! practice they probably will if the cycle length is larger than the number of assigned cores +//! multiplied by two). +//! //! # Spam protection //! -//! As long as the [`CandidateStorage`] has bounded input on the number of candidates supplied, -//! [`FragmentChain`] complexity is bounded. This means that higher-level code needs to be selective -//! about limiting the amount of candidates that are considered. +//! As long as the supplied number of candidates is bounded, [`FragmentChain`] complexity is +//! bounded. This means that higher-level code needs to be selective about limiting the amount of +//! candidates that are considered. +//! +//! Practically speaking, the collator-protocol will not allow more than `max_candidate_depth + 1` +//! collations to be fetched at a relay parent and statement-distribution will not allow more than +//! `max_candidate_depth + 1` seconded candidates at a relay parent per each validator in the +//! backing group. Considering the `allowed_ancestry_len` configuration value, the number of +//! candidates in a `FragmentChain` (including its unconnected storage) should not exceed: +//! +//! `allowed_ancestry_len * (max_candidate_depth + 1) * backing_group_size`. //! //! The code in this module is not designed for speed or efficiency, but conceptual simplicity. //! Our assumption is that the amount of candidates and parachains we consider will be reasonably //! bounded and in practice will not exceed a few thousand at any time. This naive implementation //! will still perform fairly well under these conditions, despite being somewhat wasteful of //! memory. +//! +//! Still, the expensive candidate data (CandidateCommitments) are wrapped in an `Arc` and shared +//! across fragment chains of the same para on different active leaves. #[cfg(test)] mod tests; use std::{ + cmp::{min, Ordering}, collections::{ hash_map::{Entry, HashMap}, - BTreeMap, HashSet, + BTreeMap, HashSet, VecDeque, }, sync::Arc, }; use super::LOG_TARGET; -use polkadot_node_subsystem::messages::{Ancestors, HypotheticalCandidate}; +use polkadot_node_subsystem::messages::Ancestors; use polkadot_node_subsystem_util::inclusion_emulator::{ - ConstraintModifications, Constraints, Fragment, ProspectiveCandidate, RelayChainBlockInfo, + self, ConstraintModifications, Constraints, Fragment, HypotheticalOrConcreteCandidate, + ProspectiveCandidate, RelayChainBlockInfo, }; use polkadot_primitives::{ - BlockNumber, CandidateHash, CommittedCandidateReceipt, Hash, HeadData, Id as ParaId, - PersistedValidationData, + BlockNumber, CandidateCommitments, CandidateHash, CommittedCandidateReceipt, Hash, HeadData, + PersistedValidationData, ValidationCodeHash, }; +use thiserror::Error; + +/// Fragment chain related errors. +#[derive(Debug, Clone, PartialEq, Error)] +pub(crate) enum Error { + #[error("Candidate already known")] + CandidateAlreadyKnown, + #[error("Candidate's parent head is equal to its output head. Would introduce a cycle.")] + ZeroLengthCycle, + #[error("Candidate would introduce a cycle")] + Cycle, + #[error("Candidate would introduce two paths to the same output state")] + MultiplePaths, + #[error("Attempting to directly introduce a Backed candidate. It should first be introduced as Seconded")] + IntroduceBackedCandidate, + #[error("Relay parent {0:?} of the candidate precedes the relay parent {1:?} of a pending availability candidate")] + RelayParentPrecedesCandidatePendingAvailability(Hash, Hash), + #[error("Candidate would introduce a fork with a pending availability candidate: {0:?}")] + ForkWithCandidatePendingAvailability(CandidateHash), + #[error("Fork selection rule favours another candidate: {0:?}")] + ForkChoiceRule(CandidateHash), + #[error("Could not find parent of the candidate")] + ParentCandidateNotFound, + #[error("Could not compute candidate constraints: {0:?}")] + ComputeConstraints(inclusion_emulator::ModificationError), + #[error("Candidate violates constraints: {0:?}")] + CheckAgainstConstraints(inclusion_emulator::FragmentValidityError), + #[error("Relay parent would move backwards from the latest candidate in the chain")] + RelayParentMovedBackwards, + #[error(transparent)] + CandidateEntry(#[from] CandidateEntryError), + #[error("Relay parent {0:?} not in scope. Earliest relay parent allowed {1:?}")] + RelayParentNotInScope(Hash, Hash), +} -/// Kinds of failures to import a candidate into storage. -#[derive(Debug, Clone, PartialEq)] -pub enum CandidateStorageInsertionError { - /// An error indicating that a supplied candidate didn't match the persisted - /// validation data provided alongside it. - PersistedValidationDataMismatch, - /// The candidate was already known. - CandidateAlreadyKnown(CandidateHash), +/// The rule for selecting between two backed candidate forks, when adding to the chain. +/// All validators should adhere to this rule, in order to not lose out on rewards in case of +/// forking parachains. +fn fork_selection_rule(hash1: &CandidateHash, hash2: &CandidateHash) -> Ordering { + hash1.cmp(hash2) } -/// Stores candidates and information about them such as their relay-parents and their backing -/// states. +/// Utility for storing candidates and information about them such as their relay-parents and their +/// backing states. This does not assume any restriction on whether or not the candidates form a +/// chain. Useful for storing all kinds of candidates. #[derive(Clone, Default)] pub(crate) struct CandidateStorage { - // Index from head data hash to candidate hashes with that head data as a parent. Purely for + // Index from head data hash to candidate hashes with that head data as a parent. Useful for // efficiency when responding to `ProspectiveValidationDataRequest`s or when trying to find a // new candidate to push to a chain. - // Even though having multiple candidates with same parent would be invalid for a parachain, it - // could happen across different relay chain forks, hence the HashSet. by_parent_head: HashMap>, - // Index from head data hash to candidate hashes outputting that head data. Purely for + // Index from head data hash to candidate hashes outputting that head data. For // efficiency when responding to `ProspectiveValidationDataRequest`s. - // Even though having multiple candidates with same output would be invalid for a parachain, - // it could happen across different relay chain forks. by_output_head: HashMap>, // Index from candidate hash to fragment node. @@ -136,63 +200,59 @@ pub(crate) struct CandidateStorage { } impl CandidateStorage { - /// Introduce a new candidate. - pub fn add_candidate( + /// Introduce a new pending availability candidate. + pub fn add_pending_availability_candidate( &mut self, + candidate_hash: CandidateHash, candidate: CommittedCandidateReceipt, persisted_validation_data: PersistedValidationData, - state: CandidateState, - ) -> Result { - let candidate_hash = candidate.hash(); - if self.by_candidate_hash.contains_key(&candidate_hash) { - return Err(CandidateStorageInsertionError::CandidateAlreadyKnown(candidate_hash)) - } + ) -> Result<(), Error> { + let entry = CandidateEntry::new( + candidate_hash, + candidate, + persisted_validation_data, + CandidateState::Backed, + )?; - if persisted_validation_data.hash() != candidate.descriptor.persisted_validation_data_hash { - return Err(CandidateStorageInsertionError::PersistedValidationDataMismatch) - } + self.add_candidate_entry(entry) + } - let entry = CandidateEntry { - candidate_hash, - parent_head_data_hash: persisted_validation_data.parent_head.hash(), - output_head_data_hash: candidate.commitments.head_data.hash(), - relay_parent: candidate.descriptor.relay_parent, - state, - candidate: Arc::new(ProspectiveCandidate { - commitments: candidate.commitments, - persisted_validation_data, - pov_hash: candidate.descriptor.pov_hash, - validation_code_hash: candidate.descriptor.validation_code_hash, - }), - }; + /// Return the number of stored candidates. + pub fn len(&self) -> usize { + self.by_candidate_hash.len() + } + + /// Introduce a new candidate entry. + fn add_candidate_entry(&mut self, candidate: CandidateEntry) -> Result<(), Error> { + let candidate_hash = candidate.candidate_hash; + if self.by_candidate_hash.contains_key(&candidate_hash) { + return Err(Error::CandidateAlreadyKnown) + } self.by_parent_head - .entry(entry.parent_head_data_hash()) + .entry(candidate.parent_head_data_hash) .or_default() .insert(candidate_hash); self.by_output_head - .entry(entry.output_head_data_hash()) + .entry(candidate.output_head_data_hash) .or_default() .insert(candidate_hash); - // sanity-checked already. - self.by_candidate_hash.insert(candidate_hash, entry); + self.by_candidate_hash.insert(candidate_hash, candidate); - Ok(candidate_hash) + Ok(()) } /// Remove a candidate from the store. - pub fn remove_candidate(&mut self, candidate_hash: &CandidateHash) { + fn remove_candidate(&mut self, candidate_hash: &CandidateHash) { if let Some(entry) = self.by_candidate_hash.remove(candidate_hash) { - if let Entry::Occupied(mut e) = self.by_parent_head.entry(entry.parent_head_data_hash()) - { + if let Entry::Occupied(mut e) = self.by_parent_head.entry(entry.parent_head_data_hash) { e.get_mut().remove(&candidate_hash); if e.get().is_empty() { e.remove(); } } - if let Entry::Occupied(mut e) = self.by_output_head.entry(entry.output_head_data_hash()) - { + if let Entry::Occupied(mut e) = self.by_output_head.entry(entry.output_head_data_hash) { e.get_mut().remove(&candidate_hash); if e.get().is_empty() { e.remove(); @@ -202,7 +262,7 @@ impl CandidateStorage { } /// Note that an existing candidate has been backed. - pub fn mark_backed(&mut self, candidate_hash: &CandidateHash) { + fn mark_backed(&mut self, candidate_hash: &CandidateHash) { if let Some(entry) = self.by_candidate_hash.get_mut(candidate_hash) { gum::trace!(target: LOG_TARGET, ?candidate_hash, "Candidate marked as backed"); entry.state = CandidateState::Backed; @@ -211,38 +271,18 @@ impl CandidateStorage { } } - /// Whether a candidate is recorded as being backed. - pub fn is_backed(&self, candidate_hash: &CandidateHash) -> bool { - self.by_candidate_hash - .get(candidate_hash) - .map_or(false, |e| e.state == CandidateState::Backed) - } - /// Whether a candidate is contained within the storage already. - pub fn contains(&self, candidate_hash: &CandidateHash) -> bool { + fn contains(&self, candidate_hash: &CandidateHash) -> bool { self.by_candidate_hash.contains_key(candidate_hash) } - /// Return an iterator over the stored candidates. - pub fn candidates(&self) -> impl Iterator { + /// Return an iterator over references to the stored candidates, in arbitrary order. + fn candidates(&self) -> impl Iterator { self.by_candidate_hash.values() } - /// Retain only candidates which pass the predicate. - pub(crate) fn retain(&mut self, pred: impl Fn(&CandidateHash) -> bool) { - self.by_candidate_hash.retain(|h, _v| pred(h)); - self.by_parent_head.retain(|_parent, children| { - children.retain(|h| pred(h)); - !children.is_empty() - }); - self.by_output_head.retain(|_output, candidates| { - candidates.retain(|h| pred(h)); - !candidates.is_empty() - }); - } - - /// Get head-data by hash. - pub(crate) fn head_data_by_hash(&self, hash: &Hash) -> Option<&HeadData> { + /// Try getting head-data by hash. + fn head_data_by_hash(&self, hash: &Hash) -> Option<&HeadData> { // First, search for candidates outputting this head data and extract the head data // from their commitments if they exist. // @@ -262,16 +302,8 @@ impl CandidateStorage { }) } - /// Returns candidate's relay parent, if present. - pub(crate) fn relay_parent_of_candidate(&self, candidate_hash: &CandidateHash) -> Option { - self.by_candidate_hash.get(candidate_hash).map(|entry| entry.relay_parent) - } - - /// Returns the candidates which have the given head data hash as parent. - /// We don't allow forks in a parachain, but we may have multiple candidates with same parent - /// across different relay chain forks. That's why it returns an iterator (but only one will be - /// valid and used in the end). - fn possible_para_children<'a>( + /// Returns the backed candidates which have the given head data hash as parent. + fn possible_backed_para_children<'a>( &'a self, parent_head_hash: &'a Hash, ) -> impl Iterator + 'a { @@ -280,12 +312,11 @@ impl CandidateStorage { .get(parent_head_hash) .into_iter() .flat_map(|hashes| hashes.iter()) - .filter_map(move |h| by_candidate_hash.get(h)) - } - - #[cfg(test)] - pub fn len(&self) -> (usize, usize) { - (self.by_parent_head.len(), self.by_candidate_hash.len()) + .filter_map(move |h| { + by_candidate_hash.get(h).and_then(|candidate| { + (candidate.state == CandidateState::Backed).then_some(candidate) + }) + }) } } @@ -293,14 +324,24 @@ impl CandidateStorage { /// /// Candidates aren't even considered until they've at least been seconded. #[derive(Debug, PartialEq, Clone)] -pub(crate) enum CandidateState { +enum CandidateState { /// The candidate has been seconded. Seconded, /// The candidate has been completely backed by the group. Backed, } +#[derive(Debug, Clone, PartialEq, Error)] +/// Possible errors when construcing a candidate entry. +pub enum CandidateEntryError { + #[error("Candidate does not match the persisted validation data provided alongside it")] + PersistedValidationDataMismatch, + #[error("Candidate's parent head is equal to its output head. Would introduce a cycle")] + ZeroLengthCycle, +} + #[derive(Debug, Clone)] +/// Representation of a candidate into the [`CandidateStorage`]. pub(crate) struct CandidateEntry { candidate_hash: CandidateHash, parent_head_data_hash: Hash, @@ -311,16 +352,79 @@ pub(crate) struct CandidateEntry { } impl CandidateEntry { + /// Create a new seconded candidate entry. + pub fn new_seconded( + candidate_hash: CandidateHash, + candidate: CommittedCandidateReceipt, + persisted_validation_data: PersistedValidationData, + ) -> Result { + Self::new(candidate_hash, candidate, persisted_validation_data, CandidateState::Seconded) + } + pub fn hash(&self) -> CandidateHash { self.candidate_hash } - pub fn parent_head_data_hash(&self) -> Hash { + fn new( + candidate_hash: CandidateHash, + candidate: CommittedCandidateReceipt, + persisted_validation_data: PersistedValidationData, + state: CandidateState, + ) -> Result { + if persisted_validation_data.hash() != candidate.descriptor.persisted_validation_data_hash { + return Err(CandidateEntryError::PersistedValidationDataMismatch) + } + + let parent_head_data_hash = persisted_validation_data.parent_head.hash(); + let output_head_data_hash = candidate.commitments.head_data.hash(); + + if parent_head_data_hash == output_head_data_hash { + return Err(CandidateEntryError::ZeroLengthCycle) + } + + Ok(Self { + candidate_hash, + parent_head_data_hash, + output_head_data_hash, + relay_parent: candidate.descriptor.relay_parent, + state, + candidate: Arc::new(ProspectiveCandidate { + commitments: candidate.commitments, + persisted_validation_data, + pov_hash: candidate.descriptor.pov_hash, + validation_code_hash: candidate.descriptor.validation_code_hash, + }), + }) + } +} + +impl HypotheticalOrConcreteCandidate for CandidateEntry { + fn commitments(&self) -> Option<&CandidateCommitments> { + Some(&self.candidate.commitments) + } + + fn persisted_validation_data(&self) -> Option<&PersistedValidationData> { + Some(&self.candidate.persisted_validation_data) + } + + fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + Some(&self.candidate.validation_code_hash) + } + + fn parent_head_data_hash(&self) -> Hash { self.parent_head_data_hash } - pub fn output_head_data_hash(&self) -> Hash { - self.output_head_data_hash + fn output_head_data_hash(&self) -> Option { + Some(self.output_head_data_hash) + } + + fn relay_parent(&self) -> Hash { + self.relay_parent + } + + fn candidate_hash(&self) -> CandidateHash { + self.candidate_hash } } @@ -337,8 +441,6 @@ pub(crate) struct PendingAvailability { /// The scope of a [`FragmentChain`]. #[derive(Debug, Clone)] pub(crate) struct Scope { - /// The assigned para id of this `FragmentChain`. - para: ParaId, /// The relay parent we're currently building on top of. relay_parent: RelayChainBlockInfo, /// The other relay parents candidates are allowed to build upon, mapped by the block number. @@ -356,10 +458,14 @@ pub(crate) struct Scope { /// An error variant indicating that ancestors provided to a scope /// had unexpected order. #[derive(Debug)] -pub struct UnexpectedAncestor { +pub(crate) struct UnexpectedAncestor { /// The block number that this error occurred at. + /// Allow as dead code, but it's being read in logs. + #[allow(dead_code)] pub number: BlockNumber, /// The previous seen block number, which did not match `number`. + /// Allow as dead code, but it's being read in logs. + #[allow(dead_code)] pub prev: BlockNumber, } @@ -381,7 +487,6 @@ impl Scope { /// /// It is allowed to provide zero ancestors. pub fn with_ancestors( - para: ParaId, relay_parent: RelayChainBlockInfo, base_constraints: Constraints, pending_availability: Vec, @@ -408,7 +513,6 @@ impl Scope { } Ok(Scope { - para, relay_parent, base_constraints, pending_availability, @@ -436,24 +540,29 @@ impl Scope { self.ancestors_by_hash.get(hash).map(|info| info.clone()) } + /// Get the base constraints of the scope + pub fn base_constraints(&self) -> &Constraints { + &self.base_constraints + } + /// Whether the candidate in question is one pending availability in this scope. - pub fn get_pending_availability( + fn get_pending_availability( &self, candidate_hash: &CandidateHash, ) -> Option<&PendingAvailability> { self.pending_availability.iter().find(|c| &c.candidate_hash == candidate_hash) } - - /// Get the base constraints of the scope - pub fn base_constraints(&self) -> &Constraints { - &self.base_constraints - } } -pub struct FragmentNode { +#[cfg_attr(test, derive(Clone))] +/// A node that is part of a `BackedChain`. It holds constraints based on the ancestors in the +/// chain. +struct FragmentNode { fragment: Fragment, candidate_hash: CandidateHash, cumulative_modifications: ConstraintModifications, + parent_head_data_hash: Hash, + output_head_data_hash: Hash, } impl FragmentNode { @@ -462,211 +571,336 @@ impl FragmentNode { } } -/// Response given by `can_add_candidate_as_potential` -#[derive(PartialEq, Debug)] -pub enum PotentialAddition { - /// Can be added as either connected or unconnected candidate. - Anyhow, - /// Can only be added as a connected candidate to the chain. - IfConnected, - /// Cannot be added. - None, +impl From<&FragmentNode> for CandidateEntry { + fn from(node: &FragmentNode) -> Self { + // We don't need to perform the checks done in `CandidateEntry::new()`, since a + // `FragmentNode` always comes from a `CandidateEntry` + Self { + candidate_hash: node.candidate_hash, + parent_head_data_hash: node.parent_head_data_hash, + output_head_data_hash: node.output_head_data_hash, + candidate: node.fragment.candidate_clone(), + relay_parent: node.relay_parent(), + // A fragment node is always backed. + state: CandidateState::Backed, + } + } } -/// This is a chain of candidates based on some underlying storage of candidates and a scope. +/// A candidate chain of backed/backable candidates. +/// Includes the candidates pending availability and candidates which may be backed on-chain. +#[derive(Default)] +#[cfg_attr(test, derive(Clone))] +struct BackedChain { + // Holds the candidate chain. + chain: Vec, + // Index from head data hash to the candidate hash with that head data as a parent. + // Only contains the candidates present in the `chain`. + by_parent_head: HashMap, + // Index from head data hash to the candidate hash outputting that head data. + // Only contains the candidates present in the `chain`. + by_output_head: HashMap, + // A set of the candidate hashes in the `chain`. + candidates: HashSet, +} + +impl BackedChain { + fn push(&mut self, candidate: FragmentNode) { + self.candidates.insert(candidate.candidate_hash); + self.by_parent_head + .insert(candidate.parent_head_data_hash, candidate.candidate_hash); + self.by_output_head + .insert(candidate.output_head_data_hash, candidate.candidate_hash); + self.chain.push(candidate); + } + + fn clear(&mut self) -> Vec { + self.by_parent_head.clear(); + self.by_output_head.clear(); + self.candidates.clear(); + + std::mem::take(&mut self.chain) + } + + fn revert_to_parent_hash<'a>( + &'a mut self, + parent_head_data_hash: &Hash, + ) -> impl Iterator + 'a { + let mut found_index = None; + for index in 0..self.chain.len() { + let node = &self.chain[0]; + + if found_index.is_some() { + self.by_parent_head.remove(&node.parent_head_data_hash); + self.by_output_head.remove(&node.output_head_data_hash); + self.candidates.remove(&node.candidate_hash); + } else if &node.output_head_data_hash == parent_head_data_hash { + found_index = Some(index); + } + } + + if let Some(index) = found_index { + self.chain.drain(min(index + 1, self.chain.len())..) + } else { + // Don't remove anything, but use drain to satisfy the compiler. + self.chain.drain(0..0) + } + } + + fn contains(&self, hash: &CandidateHash) -> bool { + self.candidates.contains(hash) + } +} + +/// This is the fragment chain specific to an active leaf. /// -/// All nodes in the chain must be either pending availability or within the scope. Within the scope -/// means it's built off of the relay-parent or an ancestor. +/// It holds the current best backable candidate chain, as well as potential candidates +/// which could become connected to the chain in the future or which could even overwrite the +/// existing chain. +#[cfg_attr(test, derive(Clone))] pub(crate) struct FragmentChain { + // The current scope, which dictates the on-chain operating constraints that all future + // candidates must adhere to. scope: Scope, - chain: Vec, - - candidates: HashSet, + // The current best chain of backable candidates. It only contains candidates which build on + // top of each other and which have reached the backing quorum. In the presence of potential + // forks, this chain will pick a fork according to the `fork_selection_rule`. + best_chain: BackedChain, - // Index from head data hash to candidate hashes with that head data as a parent. - by_parent_head: HashMap, - // Index from head data hash to candidate hashes outputting that head data. - by_output_head: HashMap, + // The potential candidate storage. Contains candidates which are not yet part of the `chain` + // but may become in the future. These can form any tree shape as well as contain any + // unconnected candidates for which we don't know the parent. + unconnected: CandidateStorage, } impl FragmentChain { - /// Create a new [`FragmentChain`] with given scope and populated from the storage. - pub fn populate(scope: Scope, storage: &CandidateStorage) -> Self { - gum::trace!( - target: LOG_TARGET, - relay_parent = ?scope.relay_parent.hash, - relay_parent_num = scope.relay_parent.number, - para_id = ?scope.para, - ancestors = scope.ancestors.len(), - "Instantiating Fragment Chain", - ); - + /// Create a new [`FragmentChain`] with the given scope and populate it with the candidates + /// pending availability. + pub fn init(scope: Scope, mut candidates_pending_availability: CandidateStorage) -> Self { let mut fragment_chain = Self { scope, - chain: Vec::new(), - candidates: HashSet::new(), - by_parent_head: HashMap::new(), - by_output_head: HashMap::new(), + best_chain: BackedChain::default(), + unconnected: CandidateStorage::default(), }; - fragment_chain.populate_chain(storage); + // We only need to populate the best backable chain. Candidates pending availability must + // form a chain with the latest included head. + fragment_chain.populate_chain(&mut candidates_pending_availability); fragment_chain } - /// Get the scope of the Fragment Chain. + /// Populate the [`FragmentChain`] given the new candidates pending availability and the + /// optional previous fragment chain (of the previous relay parent). + pub fn populate_from_previous(&mut self, prev_fragment_chain: &FragmentChain) { + let mut prev_storage = prev_fragment_chain.unconnected.clone(); + + for candidate in prev_fragment_chain.best_chain.chain.iter() { + // If they used to be pending availability, don't add them. This is fine + // because: + // - if they still are pending availability, they have already been added to the new + // storage. + // - if they were included, no point in keeping them. + // + // This cannot happen for the candidates in the unconnected storage. The pending + // availability candidates will always be part of the best chain. + if prev_fragment_chain + .scope + .get_pending_availability(&candidate.candidate_hash) + .is_none() + { + let _ = prev_storage.add_candidate_entry(candidate.into()); + } + } + + // First populate the best backable chain. + self.populate_chain(&mut prev_storage); + + // Now that we picked the best backable chain, trim the forks generated by candidates which + // are not present in the best chain. + self.trim_uneligible_forks(&mut prev_storage, None); + + // Finally, keep any candidates which haven't been trimmed but still have potential. + self.populate_unconnected_potential_candidates(prev_storage); + } + + /// Get the scope of the [`FragmentChain`]. pub fn scope(&self) -> &Scope { &self.scope } - /// Returns the number of candidates in the chain - pub(crate) fn len(&self) -> usize { - self.candidates.len() + /// Returns the number of candidates in the best backable chain. + pub fn best_chain_len(&self) -> usize { + self.best_chain.chain.len() } - /// Whether the candidate exists. - pub(crate) fn contains_candidate(&self, candidate: &CandidateHash) -> bool { - self.candidates.contains(candidate) + /// Returns the number of candidates in unconnected potential storage. + pub fn unconnected_len(&self) -> usize { + self.unconnected.len() + } + + /// Whether the candidate exists as part of the unconnected potential candidates. + pub fn contains_unconnected_candidate(&self, candidate: &CandidateHash) -> bool { + self.unconnected.contains(candidate) } /// Return a vector of the chain's candidate hashes, in-order. - pub(crate) fn to_vec(&self) -> Vec { - self.chain.iter().map(|candidate| candidate.candidate_hash).collect() + pub fn best_chain_vec(&self) -> Vec { + self.best_chain.chain.iter().map(|candidate| candidate.candidate_hash).collect() } - /// Try accumulating more candidates onto the chain. - /// - /// Candidates can only be added if they build on the already existing chain. - pub(crate) fn extend_from_storage(&mut self, storage: &CandidateStorage) { - self.populate_chain(storage); + /// Return a vector of the unconnected potential candidate hashes, in arbitrary order. + pub fn unconnected(&self) -> impl Iterator { + self.unconnected.candidates() } - /// Returns the hypothetical state of a candidate with the given hash and parent head data - /// in regards to the existing chain. - /// - /// Returns true if either: - /// - the candidate is already present - /// - the candidate can be added to the chain - /// - the candidate could potentially be added to the chain in the future (its ancestors are - /// still unknown but it doesn't violate other rules). - /// - /// If this returns false, the candidate could never be added to the current chain (not now, not - /// ever) - pub(crate) fn hypothetical_membership( + /// Return whether this candidate is backed in this chain or the unconnected storage. + pub fn is_candidate_backed(&self, hash: &CandidateHash) -> bool { + self.best_chain.candidates.contains(hash) || + matches!( + self.unconnected.by_candidate_hash.get(hash), + Some(candidate) if candidate.state == CandidateState::Backed + ) + } + + /// Mark a candidate as backed. This can trigger a recreation of the best backable chain. + pub fn candidate_backed(&mut self, newly_backed_candidate: &CandidateHash) { + // Already backed. + if self.best_chain.candidates.contains(newly_backed_candidate) { + return + } + let Some(parent_head_hash) = self + .unconnected + .by_candidate_hash + .get(newly_backed_candidate) + .map(|entry| entry.parent_head_data_hash) + else { + // Candidate is not in unconnected storage. + return + }; + + // Mark the candidate hash. + self.unconnected.mark_backed(newly_backed_candidate); + + // Revert to parent_head_hash + if !self.revert_to(&parent_head_hash) { + // If nothing was reverted, there is nothing we can do for now. + return + } + + let mut prev_storage = std::mem::take(&mut self.unconnected); + + // Populate the chain. + self.populate_chain(&mut prev_storage); + + // Now that we picked the best backable chain, trim the forks generated by candidates + // which are not present in the best chain. We can start trimming from this candidate + // onwards. + self.trim_uneligible_forks(&mut prev_storage, Some(parent_head_hash)); + + // Finally, keep any candidates which haven't been trimmed but still have potential. + self.populate_unconnected_potential_candidates(prev_storage); + } + + /// Checks if this candidate could be added in the future to this chain. + /// This will return `Error::CandidateAlreadyKnown` if the candidate is already in the chain or + /// the unconnected candidate storage. + pub fn can_add_candidate_as_potential( &self, - candidate: HypotheticalCandidate, - candidate_storage: &CandidateStorage, - ) -> bool { + candidate: &impl HypotheticalOrConcreteCandidate, + ) -> Result<(), Error> { let candidate_hash = candidate.candidate_hash(); - // If we've already used this candidate in the chain - if self.candidates.contains(&candidate_hash) { - return true + if self.best_chain.contains(&candidate_hash) || self.unconnected.contains(&candidate_hash) { + return Err(Error::CandidateAlreadyKnown) } - let can_add_as_potential = self.can_add_candidate_as_potential( - candidate_storage, - &candidate.candidate_hash(), - &candidate.relay_parent(), - candidate.parent_head_data_hash(), - candidate.output_head_data_hash(), - ); + self.check_potential(candidate) + } - if can_add_as_potential == PotentialAddition::None { - return false + /// Try adding a seconded candidate, if the candidate has potential. It will never be added to + /// the chain directly in the seconded state, it will only be part of the unconnected storage. + pub fn try_adding_seconded_candidate( + &mut self, + candidate: &CandidateEntry, + ) -> Result<(), Error> { + if candidate.state == CandidateState::Backed { + return Err(Error::IntroduceBackedCandidate); } - let Some(candidate_relay_parent) = self.scope.ancestor(&candidate.relay_parent()) else { - // can_add_candidate_as_potential already checked for this, but just to be safe. - return false - }; + self.can_add_candidate_as_potential(candidate)?; - let identity_modifications = ConstraintModifications::identity(); - let cumulative_modifications = if let Some(last_candidate) = self.chain.last() { - &last_candidate.cumulative_modifications - } else { - &identity_modifications - }; + // This clone is cheap, as it uses an Arc for the expensive stuff. + // We can't consume the candidate because other fragment chains may use it also. + self.unconnected.add_candidate_entry(candidate.clone())?; - let child_constraints = - match self.scope.base_constraints.apply_modifications(&cumulative_modifications) { - Err(e) => { - gum::debug!( - target: LOG_TARGET, - new_parent_head = ?cumulative_modifications.required_parent, - ?candidate_hash, - err = ?e, - "Failed to apply modifications", - ); - - return false - }, - Ok(c) => c, - }; + Ok(()) + } - let parent_head_hash = candidate.parent_head_data_hash(); - if parent_head_hash == child_constraints.required_parent.hash() { - // We do additional checks for complete candidates. - if let HypotheticalCandidate::Complete { - ref receipt, - ref persisted_validation_data, - .. - } = candidate - { - if Fragment::check_against_constraints( - &candidate_relay_parent, - &child_constraints, - &receipt.commitments, - &receipt.descriptor().validation_code_hash, - persisted_validation_data, - ) - .is_err() - { - gum::debug!( - target: LOG_TARGET, - "Fragment::check_against_constraints() returned error", - ); - return false - } - } + /// Try getting the full head data associated with this hash. + pub fn get_head_data_by_hash(&self, head_data_hash: &Hash) -> Option { + // First, see if this is the head data of the latest included candidate. + let required_parent = &self.scope.base_constraints().required_parent; + if &required_parent.hash() == head_data_hash { + return Some(required_parent.clone()) + } - // If we got this far, it can be added to the chain right now. - true - } else if can_add_as_potential == PotentialAddition::Anyhow { - // Otherwise it is or can be an unconnected candidate, but only if PotentialAddition - // does not force us to only add a connected candidate. - true - } else { - false + // Cheaply check if the head data is in the best backable chain. + let has_head_data_in_chain = self + .best_chain + .by_parent_head + .get(head_data_hash) + .or_else(|| self.best_chain.by_output_head.get(head_data_hash)) + .is_some(); + + if has_head_data_in_chain { + return self.best_chain.chain.iter().find_map(|candidate| { + if &candidate.parent_head_data_hash == head_data_hash { + Some( + candidate + .fragment + .candidate() + .persisted_validation_data + .parent_head + .clone(), + ) + } else if &candidate.output_head_data_hash == head_data_hash { + Some(candidate.fragment.candidate().commitments.head_data.clone()) + } else { + None + } + }); } + + // Lastly, try getting the head data from the unconnected candidates. + self.unconnected.head_data_by_hash(head_data_hash).cloned() } - /// Select `count` candidates after the given `ancestors` which pass - /// the predicate and have not already been backed on chain. + /// Select `count` candidates after the given `ancestors` which can be backed on chain next. /// /// The intention of the `ancestors` is to allow queries on the basis of /// one or more candidates which were previously pending availability becoming /// available or candidates timing out. - pub(crate) fn find_backable_chain( + pub fn find_backable_chain( &self, ancestors: Ancestors, count: u32, - pred: impl Fn(&CandidateHash) -> bool, - ) -> Vec { + ) -> Vec<(CandidateHash, Hash)> { if count == 0 { return vec![] } let base_pos = self.find_ancestor_path(ancestors); - let actual_end_index = std::cmp::min(base_pos + (count as usize), self.chain.len()); + let actual_end_index = + std::cmp::min(base_pos + (count as usize), self.best_chain.chain.len()); let mut res = Vec::with_capacity(actual_end_index - base_pos); - for elem in &self.chain[base_pos..actual_end_index] { - if self.scope.get_pending_availability(&elem.candidate_hash).is_none() && - pred(&elem.candidate_hash) - { - res.push(elem.candidate_hash); + for elem in &self.best_chain.chain[base_pos..actual_end_index] { + // Only supply candidates which are not yet pending availability. `ancestors` should + // have already contained them, but check just in case. + if self.scope.get_pending_availability(&elem.candidate_hash).is_none() { + res.push((elem.candidate_hash, elem.relay_parent())); } else { break } @@ -679,11 +913,11 @@ impl FragmentChain { // Stops when the ancestors are all used or when a node in the chain is not present in the // ancestor set. Returns the index in the chain were the search stopped. fn find_ancestor_path(&self, mut ancestors: Ancestors) -> usize { - if self.chain.is_empty() { + if self.best_chain.chain.is_empty() { return 0; } - for (index, candidate) in self.chain.iter().enumerate() { + for (index, candidate) in self.best_chain.chain.iter().enumerate() { if !ancestors.remove(&candidate.candidate_hash) { return index } @@ -691,16 +925,16 @@ impl FragmentChain { // This means that we found the entire chain in the ancestor set. There won't be anything // left to back. - self.chain.len() + self.best_chain.chain.len() } - // Return the earliest relay parent a new candidate can have in order to be added to the chain. - // This is the relay parent of the last candidate in the chain. + // Return the earliest relay parent a new candidate can have in order to be added to the chain + // right now. This is the relay parent of the last candidate in the chain. // The value returned may not be valid if we want to add a candidate pending availability, which // may have a relay parent which is out of scope. Special handling is needed in that case. // `None` is returned if the candidate's relay parent info cannot be found. fn earliest_relay_parent(&self) -> Option { - if let Some(last_candidate) = self.chain.last() { + if let Some(last_candidate) = self.best_chain.chain.last() { self.scope.ancestor(&last_candidate.relay_parent()).or_else(|| { // if the relay-parent is out of scope _and_ it is in the chain, // it must be a candidate pending availability. @@ -713,152 +947,239 @@ impl FragmentChain { } } - // Checks if this candidate could be added in the future to this chain. - // This assumes that the chain does not already contain this candidate. It may or may not be - // present in the `CandidateStorage`. - // Even if the candidate is a potential candidate, this function will indicate that it can be - // kept only if there's enough room for it. - pub(crate) fn can_add_candidate_as_potential( - &self, - storage: &CandidateStorage, - candidate_hash: &CandidateHash, - relay_parent: &Hash, - parent_head_hash: Hash, - output_head_hash: Option, - ) -> PotentialAddition { - // If we've got enough candidates for the configured depth, no point in adding more. - if self.chain.len() > self.scope.max_depth { - return PotentialAddition::None - } + // Return the earliest relay parent a potential candidate may have for it to ever be added to + // the chain. This is the relay parent of the last candidate pending availability or the + // earliest relay parent in scope. + fn earliest_relay_parent_pending_availability(&self) -> RelayChainBlockInfo { + self.best_chain + .chain + .iter() + .rev() + .find_map(|candidate| { + self.scope + .get_pending_availability(&candidate.candidate_hash) + .map(|c| c.relay_parent.clone()) + }) + .unwrap_or_else(|| self.scope.earliest_relay_parent()) + } - if !self.check_potential(relay_parent, parent_head_hash, output_head_hash) { - return PotentialAddition::None - } + // Populate the unconnected potential candidate storage starting from a previous storage. + fn populate_unconnected_potential_candidates(&mut self, old_storage: CandidateStorage) { + for candidate in old_storage.by_candidate_hash.into_values() { + // Sanity check, all pending availability candidates should be already present in the + // chain. + if self.scope.get_pending_availability(&candidate.candidate_hash).is_some() { + continue + } - let present_in_storage = storage.contains(candidate_hash); + match self.can_add_candidate_as_potential(&candidate) { + Ok(()) => { + let _ = self.unconnected.add_candidate_entry(candidate); + }, + // Swallow these errors as they can legitimately happen when pruning stale + // candidates. + Err(_) => {}, + }; + } + } - let unconnected = self - .find_unconnected_potential_candidates( - storage, - present_in_storage.then_some(candidate_hash), - ) - .len(); + // Check whether a candidate outputting this head data would introduce a cycle or multiple paths + // to the same state. Trivial 0-length cycles are checked in `CandidateEntry::new`. + fn check_cycles_or_invalid_tree(&self, output_head_hash: &Hash) -> Result<(), Error> { + // this should catch a cycle where this candidate would point back to the parent of some + // candidate in the chain. + if self.best_chain.by_parent_head.contains_key(output_head_hash) { + return Err(Error::Cycle) + } - if (self.chain.len() + unconnected) < self.scope.max_depth { - PotentialAddition::Anyhow - } else if (self.chain.len() + unconnected) == self.scope.max_depth { - // If we've only one slot left to fill, it must be filled with a connected candidate. - PotentialAddition::IfConnected - } else { - PotentialAddition::None + // multiple paths to the same state, which can't happen for a chain. + if self.best_chain.by_output_head.contains_key(output_head_hash) { + return Err(Error::MultiplePaths) } + + Ok(()) } - // The candidates which are present in `CandidateStorage`, are not part of this chain but could - // become part of this chain in the future. Capped at the max depth minus the existing chain - // length. - // If `ignore_candidate` is supplied and found in storage, it won't be counted. - pub(crate) fn find_unconnected_potential_candidates( + // Checks the potential of a candidate to be added to the chain now or in the future. + // It works both with concrete candidates for which we have the full PVD and committed receipt, + // but also does some more basic checks for incomplete candidates (before even fetching them). + fn check_potential( &self, - storage: &CandidateStorage, - ignore_candidate: Option<&CandidateHash>, - ) -> Vec { - let mut candidates = vec![]; - for candidate in storage.candidates() { - if let Some(ignore_candidate) = ignore_candidate { - if ignore_candidate == &candidate.candidate_hash { - continue - } - } - // We stop at max_depth + 1 with the search. There's no point in looping further. - if (self.chain.len() + candidates.len()) > self.scope.max_depth { - break - } - if !self.candidates.contains(&candidate.candidate_hash) && - self.check_potential( - &candidate.relay_parent, - candidate.candidate.persisted_validation_data.parent_head.hash(), - Some(candidate.candidate.commitments.head_data.hash()), - ) { - candidates.push(candidate.candidate_hash); + candidate: &impl HypotheticalOrConcreteCandidate, + ) -> Result<(), Error> { + let relay_parent = candidate.relay_parent(); + let parent_head_hash = candidate.parent_head_data_hash(); + + // trivial 0-length cycle. + if let Some(output_head_hash) = candidate.output_head_data_hash() { + if parent_head_hash == output_head_hash { + return Err(Error::ZeroLengthCycle) } } - candidates - } + // Check if the relay parent is in scope. + let Some(relay_parent) = self.scope.ancestor(&relay_parent) else { + return Err(Error::RelayParentNotInScope( + relay_parent, + self.scope.earliest_relay_parent().hash, + )) + }; - // Check if adding a candidate which transitions `parent_head_hash` to `output_head_hash` would - // introduce a fork or a cycle in the parachain. - // `output_head_hash` is optional because we sometimes make this check before retrieving the - // collation. - fn is_fork_or_cycle(&self, parent_head_hash: Hash, output_head_hash: Option) -> bool { - if self.by_parent_head.contains_key(&parent_head_hash) { - // fork. our parent has another child already - return true + // Check if the relay parent moved backwards from the latest candidate pending availability. + let earliest_rp_of_pending_availability = self.earliest_relay_parent_pending_availability(); + if relay_parent.number < earliest_rp_of_pending_availability.number { + return Err(Error::RelayParentPrecedesCandidatePendingAvailability( + relay_parent.hash, + earliest_rp_of_pending_availability.hash, + )) } - if let Some(output_head_hash) = output_head_hash { - if self.by_output_head.contains_key(&output_head_hash) { - // this is not a chain, there are multiple paths to the same state. - return true + // If it's a fork with a backed candidate in the current chain. + if let Some(other_candidate) = self.best_chain.by_parent_head.get(&parent_head_hash) { + if self.scope().get_pending_availability(other_candidate).is_some() { + // Cannot accept a fork with a candidate pending availability. + return Err(Error::ForkWithCandidatePendingAvailability(*other_candidate)) } - // trivial 0-length cycle. - if parent_head_hash == output_head_hash { - return true - } - - // this should catch any other cycles. our output state cannot already be the parent - // state of another candidate, unless this is a cycle, since the already added - // candidates form a chain. - if self.by_parent_head.contains_key(&output_head_hash) { - return true + // If the candidate is backed and in the current chain, accept only a candidate + // according to the fork selection rule. + if fork_selection_rule(other_candidate, &candidate.candidate_hash()) == Ordering::Less { + return Err(Error::ForkChoiceRule(*other_candidate)) } } - false - } + // Try seeing if the parent candidate is in the current chain or if it is the latest + // included candidate. If so, get the constraints the candidate must satisfy. + let (constraints, maybe_min_relay_parent_number) = + if let Some(parent_candidate) = self.best_chain.by_output_head.get(&parent_head_hash) { + let Some(parent_candidate) = + self.best_chain.chain.iter().find(|c| &c.candidate_hash == parent_candidate) + else { + // Should never really happen. + return Err(Error::ParentCandidateNotFound) + }; - // Checks the potential of a candidate to be added to the chain in the future. - // Verifies that the relay parent is in scope and not moving backwards and that we're not - // introducing forks or cycles with other candidates in the chain. - // `output_head_hash` is optional because we sometimes make this check before retrieving the - // collation. - fn check_potential( - &self, - relay_parent: &Hash, - parent_head_hash: Hash, - output_head_hash: Option, - ) -> bool { - if self.is_fork_or_cycle(parent_head_hash, output_head_hash) { - return false + ( + self.scope + .base_constraints + .apply_modifications(&parent_candidate.cumulative_modifications) + .map_err(Error::ComputeConstraints)?, + self.scope.ancestor(&parent_candidate.relay_parent()).map(|rp| rp.number), + ) + } else if self.scope.base_constraints.required_parent.hash() == parent_head_hash { + // It builds on the latest included candidate. + (self.scope.base_constraints.clone(), None) + } else { + // If the parent is not yet part of the chain, there's nothing else we can check for + // now. + return Ok(()) + }; + + // Check for cycles or invalid tree transitions. + if let Some(ref output_head_hash) = candidate.output_head_data_hash() { + self.check_cycles_or_invalid_tree(output_head_hash)?; } - let Some(earliest_rp) = self.earliest_relay_parent() else { return false }; + // Check against constraints if we have a full concrete candidate. + if let (Some(commitments), Some(pvd), Some(validation_code_hash)) = ( + candidate.commitments(), + candidate.persisted_validation_data(), + candidate.validation_code_hash(), + ) { + Fragment::check_against_constraints( + &relay_parent, + &constraints, + commitments, + validation_code_hash, + pvd, + ) + .map_err(Error::CheckAgainstConstraints)?; + } - let Some(relay_parent) = self.scope.ancestor(relay_parent) else { return false }; + if relay_parent.number < constraints.min_relay_parent_number { + return Err(Error::RelayParentMovedBackwards) + } - if relay_parent.number < earliest_rp.number { - return false // relay parent moved backwards. + if let Some(earliest_rp) = maybe_min_relay_parent_number { + if relay_parent.number < earliest_rp { + return Err(Error::RelayParentMovedBackwards) + } } - true + Ok(()) } - // Populate the fragment chain with candidates from CandidateStorage. - // Can be called by the constructor or when introducing a new candidate. - // If we're introducing a new candidate onto an existing chain, we may introduce more than one, - // since we may connect already existing candidates to the chain. - fn populate_chain(&mut self, storage: &CandidateStorage) { - let mut cumulative_modifications = if let Some(last_candidate) = self.chain.last() { - last_candidate.cumulative_modifications.clone() + // Once the backable chain was populated, trim the forks generated by candidates which + // are not present in the best chain. Fan this out into a full breadth-first search. + // If `starting_point` is `Some()`, start the search from the candidates having this parent head + // hash. + fn trim_uneligible_forks(&self, storage: &mut CandidateStorage, starting_point: Option) { + // Start out with the candidates in the chain. They are all valid candidates. + let mut queue: VecDeque<_> = if let Some(starting_point) = starting_point { + [(starting_point, true)].into_iter().collect() } else { - ConstraintModifications::identity() + if self.best_chain.chain.is_empty() { + [(self.scope.base_constraints.required_parent.hash(), true)] + .into_iter() + .collect() + } else { + self.best_chain.chain.iter().map(|c| (c.parent_head_data_hash, true)).collect() + } }; + // To make sure that cycles don't make us loop forever, keep track of the visited parent + // heads. + let mut visited = HashSet::new(); + + while let Some((parent, parent_has_potential)) = queue.pop_front() { + visited.insert(parent); + + let Some(children) = storage.by_parent_head.get(&parent) else { continue }; + // Cannot remove while iterating so store them here temporarily. + let mut to_remove = vec![]; + + for child_hash in children.iter() { + let Some(child) = storage.by_candidate_hash.get(child_hash) else { continue }; + + // Already visited this parent. Either is a cycle or multiple paths that lead to the + // same candidate. Either way, stop this branch to avoid looping forever. + if visited.contains(&child.output_head_data_hash) { + continue + } + + // Only keep a candidate if its full ancestry was already kept as potential and this + // candidate itself has potential. + if parent_has_potential && self.check_potential(child).is_ok() { + queue.push_back((child.output_head_data_hash, true)); + } else { + // Otherwise, remove this candidate and continue looping for its children, but + // mark the parent's potential as `false`. We only want to remove its + // children. + to_remove.push(*child_hash); + queue.push_back((child.output_head_data_hash, false)); + } + } + + for hash in to_remove { + storage.remove_candidate(&hash); + } + } + } + + // Populate the fragment chain with candidates from the supplied `CandidateStorage`. + // Can be called by the constructor or when backing a new candidate. + // When this is called, it may cause the previous chain to be completely erased or it may add + // more than one candidate. + fn populate_chain(&mut self, storage: &mut CandidateStorage) { + let mut cumulative_modifications = + if let Some(last_candidate) = self.best_chain.chain.last() { + last_candidate.cumulative_modifications.clone() + } else { + ConstraintModifications::identity() + }; let Some(mut earliest_rp) = self.earliest_relay_parent() else { return }; loop { - if self.chain.len() > self.scope.max_depth { + if self.best_chain.chain.len() > self.scope.max_depth { break; } @@ -878,113 +1199,157 @@ impl FragmentChain { }; let required_head_hash = child_constraints.required_parent.hash(); - // Even though we don't allow parachain forks under the same active leaf, they may still - // appear under different relay chain forks, hence the iterator below. - let possible_children = storage.possible_para_children(&required_head_hash); - let mut added_child = false; - for candidate in possible_children { - // Add one node to chain if - // 1. it does not introduce a fork or a cycle. - // 2. parent hash is correct. - // 3. relay-parent does not move backwards. - // 4. all non-pending-availability candidates have relay-parent in scope. - // 5. candidate outputs fulfill constraints - - if self.is_fork_or_cycle( - candidate.parent_head_data_hash(), - Some(candidate.output_head_data_hash()), - ) { - continue - } - let pending = self.scope.get_pending_availability(&candidate.candidate_hash); - let Some(relay_parent) = pending - .map(|p| p.relay_parent.clone()) - .or_else(|| self.scope.ancestor(&candidate.relay_parent)) - else { - continue - }; - - // require: candidates don't move backwards - // and only pending availability candidates can be out-of-scope. - // - // earliest_rp can be before the earliest relay parent in the scope - // when the parent is a pending availability candidate as well, but - // only other pending candidates can have a relay parent out of scope. - let min_relay_parent_number = pending - .map(|p| match self.chain.len() { - 0 => p.relay_parent.number, - _ => earliest_rp.number, - }) - .unwrap_or_else(|| earliest_rp.number); - - if relay_parent.number < min_relay_parent_number { - continue // relay parent moved backwards. - } + // Select the few possible backed/backable children which can be added to the chain + // right now. + let possible_children = storage + .possible_backed_para_children(&required_head_hash) + .filter_map(|candidate| { + // Only select a candidate if: + // 1. it does not introduce a fork or a cycle. + // 2. parent hash is correct. + // 3. relay-parent does not move backwards. + // 4. all non-pending-availability candidates have relay-parent in scope. + // 5. candidate outputs fulfill constraints + + let pending = self.scope.get_pending_availability(&candidate.candidate_hash); + let Some(relay_parent) = pending + .map(|p| p.relay_parent.clone()) + .or_else(|| self.scope.ancestor(&candidate.relay_parent)) + else { + return None + }; + + if self.check_cycles_or_invalid_tree(&candidate.output_head_data_hash).is_err() + { + return None + } - // don't add candidates if they're already present in the chain. - // this can never happen, as candidates can only be duplicated if there's a cycle - // and we shouldn't have allowed for a cycle to be chained. - if self.contains_candidate(&candidate.candidate_hash) { - continue - } + // require: candidates don't move backwards + // and only pending availability candidates can be out-of-scope. + // + // earliest_rp can be before the earliest relay parent in the scope + // when the parent is a pending availability candidate as well, but + // only other pending candidates can have a relay parent out of scope. + let min_relay_parent_number = pending + .map(|p| match self.best_chain.chain.len() { + 0 => p.relay_parent.number, + _ => earliest_rp.number, + }) + .unwrap_or_else(|| earliest_rp.number); + + if relay_parent.number < min_relay_parent_number { + return None // relay parent moved backwards. + } - let fragment = { - let mut constraints = child_constraints.clone(); - if let Some(ref p) = pending { - // overwrite for candidates pending availability as a special-case. - constraints.min_relay_parent_number = p.relay_parent.number; + // don't add candidates if they're already present in the chain. + // this can never happen, as candidates can only be duplicated if there's a + // cycle and we shouldn't have allowed for a cycle to be chained. + if self.best_chain.contains(&candidate.candidate_hash) { + return None } - let f = Fragment::new( - relay_parent.clone(), - constraints, - // It's cheap to clone because it's wrapped in an Arc - candidate.candidate.clone(), - ); - - match f { - Ok(f) => f, - Err(e) => { - gum::debug!( - target: LOG_TARGET, - err = ?e, - ?relay_parent, - candidate_hash = ?candidate.candidate_hash, - "Failed to instantiate fragment", - ); - - break - }, + let fragment = { + let mut constraints = child_constraints.clone(); + if let Some(ref p) = pending { + // overwrite for candidates pending availability as a special-case. + constraints.min_relay_parent_number = p.relay_parent.number; + } + + let f = Fragment::new( + relay_parent.clone(), + constraints, + // It's cheap to clone because it's wrapped in an Arc + candidate.candidate.clone(), + ); + + match f { + Ok(f) => f, + Err(e) => { + gum::debug!( + target: LOG_TARGET, + err = ?e, + ?relay_parent, + candidate_hash = ?candidate.candidate_hash, + "Failed to instantiate fragment", + ); + + return None + }, + } + }; + + Some(( + fragment, + candidate.candidate_hash, + candidate.output_head_data_hash, + candidate.parent_head_data_hash, + )) + }); + + // Choose the best candidate. + let best_candidate = + possible_children.min_by(|(_, ref child1, _, _), (_, ref child2, _, _)| { + // Always pick a candidate pending availability as best. + if self.scope.get_pending_availability(child1).is_some() { + Ordering::Less + } else if self.scope.get_pending_availability(child2).is_some() { + Ordering::Greater + } else { + // Otherwise, use the fork selection rule. + fork_selection_rule(child1, child2) } - }; + }); + + if let Some((fragment, candidate_hash, output_head_data_hash, parent_head_data_hash)) = + best_candidate + { + // Remove the candidate from storage. + storage.remove_candidate(&candidate_hash); // Update the cumulative constraint modifications. cumulative_modifications.stack(fragment.constraint_modifications()); // Update the earliest rp - earliest_rp = relay_parent; + earliest_rp = fragment.relay_parent().clone(); let node = FragmentNode { fragment, - candidate_hash: candidate.candidate_hash, + candidate_hash, + parent_head_data_hash, + output_head_data_hash, cumulative_modifications: cumulative_modifications.clone(), }; - self.chain.push(node); - self.candidates.insert(candidate.candidate_hash); - // We've already checked for forks and cycles. - self.by_parent_head - .insert(candidate.parent_head_data_hash(), candidate.candidate_hash); - self.by_output_head - .insert(candidate.output_head_data_hash(), candidate.candidate_hash); - added_child = true; - // We can only add one child for a candidate. (it's a chain, not a tree) - break; - } - - if !added_child { + // Add the candidate to the chain now. + self.best_chain.push(node); + } else { break } } } + + // Revert the best backable chain so that the last candidate will be one outputting the given + // `parent_head_hash`. If the `parent_head_hash` is exactly the required parent of the base + // constraints (builds on the latest included candidate), revert the entire chain. + // Return false if we couldn't find the parent head hash. + fn revert_to(&mut self, parent_head_hash: &Hash) -> bool { + let mut removed_items = None; + if &self.scope.base_constraints.required_parent.hash() == parent_head_hash { + removed_items = Some(self.best_chain.clear()); + } + + if removed_items.is_none() && self.best_chain.by_output_head.contains_key(parent_head_hash) + { + removed_items = Some(self.best_chain.revert_to_parent_hash(parent_head_hash).collect()); + } + + let Some(removed_items) = removed_items else { return false }; + + // Even if it's empty, we need to return true, because we'll be able to add a new candidate + // to the chain. + for node in &removed_items { + let _ = self.unconnected.add_candidate_entry(node.into()); + } + true + } } diff --git a/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs b/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs index 26ee94d59d8e..9886d19e5224 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs @@ -17,8 +17,12 @@ use super::*; use assert_matches::assert_matches; use polkadot_node_subsystem_util::inclusion_emulator::InboundHrmpLimitations; -use polkadot_primitives::{BlockNumber, CandidateCommitments, CandidateDescriptor, HeadData}; +use polkadot_primitives::{ + BlockNumber, CandidateCommitments, CandidateDescriptor, HeadData, Id as ParaId, +}; use polkadot_primitives_test_helpers as test_helpers; +use rand::{seq::SliceRandom, thread_rng}; +use std::ops::Range; fn make_constraints( min_relay_parent_number: BlockNumber, @@ -54,7 +58,7 @@ fn make_committed_candidate( let persisted_validation_data = PersistedValidationData { parent_head, relay_parent_number, - relay_parent_storage_root: Hash::repeat_byte(69), + relay_parent_storage_root: Hash::zero(), max_pov_size: 1_000_000, }; @@ -83,9 +87,20 @@ fn make_committed_candidate( (persisted_validation_data, candidate) } +fn populate_chain_from_previous_storage( + scope: &Scope, + storage: &CandidateStorage, +) -> FragmentChain { + let mut chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + let mut prev_chain = chain.clone(); + prev_chain.unconnected = storage.clone(); + + chain.populate_from_previous(&prev_chain); + chain +} + #[test] fn scope_rejects_ancestors_that_skip_blocks() { - let para_id = ParaId::from(5u32); let relay_parent = RelayChainBlockInfo { number: 10, hash: Hash::repeat_byte(10), @@ -104,7 +119,6 @@ fn scope_rejects_ancestors_that_skip_blocks() { assert_matches!( Scope::with_ancestors( - para_id, relay_parent, base_constraints, pending_availability, @@ -117,7 +131,6 @@ fn scope_rejects_ancestors_that_skip_blocks() { #[test] fn scope_rejects_ancestor_for_0_block() { - let para_id = ParaId::from(5u32); let relay_parent = RelayChainBlockInfo { number: 0, hash: Hash::repeat_byte(0), @@ -136,7 +149,6 @@ fn scope_rejects_ancestor_for_0_block() { assert_matches!( Scope::with_ancestors( - para_id, relay_parent, base_constraints, pending_availability, @@ -149,7 +161,6 @@ fn scope_rejects_ancestor_for_0_block() { #[test] fn scope_only_takes_ancestors_up_to_min() { - let para_id = ParaId::from(5u32); let relay_parent = RelayChainBlockInfo { number: 5, hash: Hash::repeat_byte(0), @@ -179,7 +190,6 @@ fn scope_only_takes_ancestors_up_to_min() { let pending_availability = Vec::new(); let scope = Scope::with_ancestors( - para_id, relay_parent, base_constraints, pending_availability, @@ -194,7 +204,6 @@ fn scope_only_takes_ancestors_up_to_min() { #[test] fn scope_rejects_unordered_ancestors() { - let para_id = ParaId::from(5u32); let relay_parent = RelayChainBlockInfo { number: 5, hash: Hash::repeat_byte(0), @@ -225,7 +234,6 @@ fn scope_rejects_unordered_ancestors() { assert_matches!( Scope::with_ancestors( - para_id, relay_parent, base_constraints, pending_availability, @@ -257,718 +265,695 @@ fn candidate_storage_methods() { let mut wrong_pvd = pvd.clone(); wrong_pvd.max_pov_size = 0; assert_matches!( - storage.add_candidate(candidate.clone(), wrong_pvd, CandidateState::Seconded), - Err(CandidateStorageInsertionError::PersistedValidationDataMismatch) + CandidateEntry::new( + candidate_hash, + candidate.clone(), + wrong_pvd.clone(), + CandidateState::Seconded + ), + Err(CandidateEntryError::PersistedValidationDataMismatch) + ); + assert_matches!( + CandidateEntry::new_seconded(candidate_hash, candidate.clone(), wrong_pvd), + Err(CandidateEntryError::PersistedValidationDataMismatch) ); + // Zero-length cycle. + { + let mut candidate = candidate.clone(); + candidate.commitments.head_data = HeadData(vec![1; 10]); + let mut pvd = pvd.clone(); + pvd.parent_head = HeadData(vec![1; 10]); + candidate.descriptor.persisted_validation_data_hash = pvd.hash(); + assert_matches!( + CandidateEntry::new_seconded(candidate_hash, candidate, pvd), + Err(CandidateEntryError::ZeroLengthCycle) + ); + } assert!(!storage.contains(&candidate_hash)); - assert_eq!(storage.possible_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.relay_parent_of_candidate(&candidate_hash), None); + assert_eq!(storage.possible_backed_para_children(&parent_head_hash).count(), 0); assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head), None); assert_eq!(storage.head_data_by_hash(&parent_head_hash), None); - assert_eq!(storage.is_backed(&candidate_hash), false); - // Add a valid candidate - storage - .add_candidate(candidate.clone(), pvd.clone(), CandidateState::Seconded) - .unwrap(); + // Add a valid candidate. + let candidate_entry = CandidateEntry::new( + candidate_hash, + candidate.clone(), + pvd.clone(), + CandidateState::Seconded, + ) + .unwrap(); + storage.add_candidate_entry(candidate_entry.clone()).unwrap(); assert!(storage.contains(&candidate_hash)); - assert_eq!(storage.possible_para_children(&parent_head_hash).count(), 1); - assert_eq!(storage.possible_para_children(&candidate.descriptor.para_head).count(), 0); - assert_eq!(storage.relay_parent_of_candidate(&candidate_hash), Some(relay_parent)); + assert_eq!(storage.possible_backed_para_children(&parent_head_hash).count(), 0); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); assert_eq!( storage.head_data_by_hash(&candidate.descriptor.para_head).unwrap(), &candidate.commitments.head_data ); assert_eq!(storage.head_data_by_hash(&parent_head_hash).unwrap(), &pvd.parent_head); - assert_eq!(storage.is_backed(&candidate_hash), false); + // Now mark it as backed + storage.mark_backed(&candidate_hash); + // Marking it twice is fine. storage.mark_backed(&candidate_hash); - assert_eq!(storage.is_backed(&candidate_hash), true); + assert_eq!( + storage + .possible_backed_para_children(&parent_head_hash) + .map(|c| c.candidate_hash) + .collect::>(), + vec![candidate_hash] + ); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); // Re-adding a candidate fails. assert_matches!( - storage.add_candidate(candidate.clone(), pvd.clone(), CandidateState::Seconded), - Err(CandidateStorageInsertionError::CandidateAlreadyKnown(hash)) if candidate_hash == hash + storage.add_candidate_entry(candidate_entry), + Err(Error::CandidateAlreadyKnown) ); // Remove candidate and re-add it later in backed state. storage.remove_candidate(&candidate_hash); assert!(!storage.contains(&candidate_hash)); - assert_eq!(storage.possible_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.relay_parent_of_candidate(&candidate_hash), None); + + // Removing it twice is fine. + storage.remove_candidate(&candidate_hash); + assert!(!storage.contains(&candidate_hash)); + assert_eq!(storage.possible_backed_para_children(&parent_head_hash).count(), 0); assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head), None); assert_eq!(storage.head_data_by_hash(&parent_head_hash), None); - assert_eq!(storage.is_backed(&candidate_hash), false); storage - .add_candidate(candidate.clone(), pvd.clone(), CandidateState::Backed) + .add_pending_availability_candidate(candidate_hash, candidate.clone(), pvd) .unwrap(); - assert_eq!(storage.is_backed(&candidate_hash), true); - - // Test retain - storage.retain(|_| true); assert!(storage.contains(&candidate_hash)); - storage.retain(|_| false); - assert!(!storage.contains(&candidate_hash)); - assert_eq!(storage.possible_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.relay_parent_of_candidate(&candidate_hash), None); - assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head), None); - assert_eq!(storage.head_data_by_hash(&parent_head_hash), None); - assert_eq!(storage.is_backed(&candidate_hash), false); + + assert_eq!( + storage + .possible_backed_para_children(&parent_head_hash) + .map(|c| c.candidate_hash) + .collect::>(), + vec![candidate_hash] + ); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); + + // Now add a second candidate in Seconded state. This will be a fork. + let (pvd_2, candidate_2) = make_committed_candidate( + ParaId::from(5u32), + relay_parent, + 8, + vec![4, 5, 6].into(), + vec![2, 3, 4].into(), + 7, + ); + let candidate_hash_2 = candidate_2.hash(); + let candidate_entry_2 = + CandidateEntry::new_seconded(candidate_hash_2, candidate_2, pvd_2).unwrap(); + + storage.add_candidate_entry(candidate_entry_2).unwrap(); + assert_eq!( + storage + .possible_backed_para_children(&parent_head_hash) + .map(|c| c.candidate_hash) + .collect::>(), + vec![candidate_hash] + ); + + // Now mark it as backed. + storage.mark_backed(&candidate_hash_2); + assert_eq!( + storage + .possible_backed_para_children(&parent_head_hash) + .map(|c| c.candidate_hash) + .collect::>(), + [candidate_hash, candidate_hash_2].into_iter().collect() + ); } #[test] -fn populate_and_extend_from_storage_empty() { +fn init_and_populate_from_empty() { // Empty chain and empty storage. - let storage = CandidateStorage::default(); let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - let pending_availability = Vec::new(); let scope = Scope::with_ancestors( - ParaId::from(2), RelayChainBlockInfo { number: 1, hash: Hash::repeat_byte(1), storage_root: Hash::repeat_byte(2), }, base_constraints, - pending_availability, + Vec::new(), 4, vec![], ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert!(chain.to_vec().is_empty()); - - chain.extend_from_storage(&storage); - assert!(chain.to_vec().is_empty()); + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert_eq!(chain.best_chain_len(), 0); + assert_eq!(chain.unconnected_len(), 0); + + let mut new_chain = FragmentChain::init(scope, CandidateStorage::default()); + new_chain.populate_from_previous(&chain); + assert_eq!(chain.best_chain_len(), 0); + assert_eq!(chain.unconnected_len(), 0); } #[test] -fn populate_and_extend_from_storage_with_existing_empty_to_vec() { +fn test_populate_and_check_potential() { let mut storage = CandidateStorage::default(); let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - let relay_parent_b = Hash::repeat_byte(2); - let relay_parent_c = Hash::repeat_byte(3); + let relay_parent_x = Hash::repeat_byte(1); + let relay_parent_y = Hash::repeat_byte(2); + let relay_parent_z = Hash::repeat_byte(3); + let relay_parent_x_info = + RelayChainBlockInfo { number: 0, hash: relay_parent_x, storage_root: Hash::zero() }; + let relay_parent_y_info = + RelayChainBlockInfo { number: 1, hash: relay_parent_y, storage_root: Hash::zero() }; + let relay_parent_z_info = + RelayChainBlockInfo { number: 2, hash: relay_parent_z, storage_root: Hash::zero() }; + + let ancestors = vec![ + // These need to be ordered in reverse. + relay_parent_y_info.clone(), + relay_parent_x_info.clone(), + ]; + + let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); + // Candidates A -> B -> C. They are all backed let (pvd_a, candidate_a) = make_committed_candidate( para_id, - relay_parent_a, - 0, + relay_parent_x_info.hash, + relay_parent_x_info.number, vec![0x0a].into(), vec![0x0b].into(), - 0, + relay_parent_x_info.number, ); let candidate_a_hash = candidate_a.hash(); - + let candidate_a_entry = + CandidateEntry::new(candidate_a_hash, candidate_a, pvd_a.clone(), CandidateState::Backed) + .unwrap(); + storage.add_candidate_entry(candidate_a_entry.clone()).unwrap(); let (pvd_b, candidate_b) = make_committed_candidate( para_id, - relay_parent_b, - 1, + relay_parent_y_info.hash, + relay_parent_y_info.number, vec![0x0b].into(), vec![0x0c].into(), - 1, + relay_parent_y_info.number, ); let candidate_b_hash = candidate_b.hash(); - + let candidate_b_entry = + CandidateEntry::new(candidate_b_hash, candidate_b, pvd_b, CandidateState::Backed).unwrap(); + storage.add_candidate_entry(candidate_b_entry.clone()).unwrap(); let (pvd_c, candidate_c) = make_committed_candidate( para_id, - relay_parent_c, - 2, + relay_parent_z_info.hash, + relay_parent_z_info.number, vec![0x0c].into(), vec![0x0d].into(), - 2, + relay_parent_z_info.number, ); let candidate_c_hash = candidate_c.hash(); - - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - let relay_parent_b_info = RelayChainBlockInfo { - number: pvd_b.relay_parent_number, - hash: relay_parent_b, - storage_root: pvd_b.relay_parent_storage_root, - }; - let relay_parent_c_info = RelayChainBlockInfo { - number: pvd_c.relay_parent_number, - hash: relay_parent_c, - storage_root: pvd_c.relay_parent_storage_root, - }; - - let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - let pending_availability = Vec::new(); - - let ancestors = vec![ - // These need to be ordered in reverse. - relay_parent_b_info.clone(), - relay_parent_a_info.clone(), - ]; - - storage - .add_candidate(candidate_a.clone(), pvd_a.clone(), CandidateState::Seconded) - .unwrap(); - storage - .add_candidate(candidate_b.clone(), pvd_b.clone(), CandidateState::Backed) - .unwrap(); - storage - .add_candidate(candidate_c.clone(), pvd_c.clone(), CandidateState::Backed) - .unwrap(); + let candidate_c_entry = + CandidateEntry::new(candidate_c_hash, candidate_c, pvd_c, CandidateState::Backed).unwrap(); + storage.add_candidate_entry(candidate_c_entry.clone()).unwrap(); // Candidate A doesn't adhere to the base constraints. { for wrong_constraints in [ // Different required parent - make_constraints(0, vec![0], vec![0x0e].into()), + make_constraints( + relay_parent_x_info.number, + vec![relay_parent_x_info.number], + vec![0x0e].into(), + ), // Min relay parent number is wrong - make_constraints(1, vec![0], vec![0x0a].into()), + make_constraints(relay_parent_y_info.number, vec![0], vec![0x0a].into()), ] { let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), wrong_constraints.clone(), - pending_availability.clone(), + vec![], 4, ancestors.clone(), ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); + let chain = populate_chain_from_previous_storage(&scope, &storage); - assert!(chain.to_vec().is_empty()); - - chain.extend_from_storage(&storage); - assert!(chain.to_vec().is_empty()); + assert!(chain.best_chain_vec().is_empty()); // If the min relay parent number is wrong, candidate A can never become valid. // Otherwise, if only the required parent doesn't match, candidate A is still a // potential candidate. - if wrong_constraints.min_relay_parent_number == 1 { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate_a.hash(), - &candidate_a.descriptor.relay_parent, - pvd_a.parent_head.hash(), - Some(candidate_a.commitments.head_data.hash()), - ), - PotentialAddition::None + if wrong_constraints.min_relay_parent_number == relay_parent_y_info.number { + // If A is not a potential candidate, its descendants will also not be added. + assert_eq!(chain.unconnected_len(), 0); + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::RelayParentNotInScope(_, _)) ); + // However, if taken independently, both B and C still have potential, since we + // don't know that A doesn't. + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); } else { assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate_a.hash(), - &candidate_a.descriptor.relay_parent, - pvd_a.parent_head.hash(), - Some(candidate_a.commitments.head_data.hash()), - ), - PotentialAddition::Anyhow - ); - } - - // All other candidates can always be potential candidates. - for (candidate, pvd) in - [(candidate_b.clone(), pvd_b.clone()), (candidate_c.clone(), pvd_c.clone())] - { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::Anyhow + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_a_hash, candidate_b_hash, candidate_c_hash].into_iter().collect() ); } } } - // Various max depths. + // Various depths { - // depth is 0, will only allow 1 candidate + // Depth is 0, only allows one candidate, but the others will be kept as potential. let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 0, ancestors.clone(), ) .unwrap(); - // Before populating the chain, all candidates are potential candidates. However, they can - // only be added as connected candidates, because only one candidates is allowed by max - // depth - let chain = FragmentChain::populate(scope.clone(), &CandidateStorage::default()); - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &CandidateStorage::default(), - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::IfConnected - ); - } - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash]); - // since depth is maxed out, we can't add more potential candidates - // candidate A is no longer a potential candidate because it's already present. - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&candidate_a_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); + + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_b_hash, candidate_c_hash].into_iter().collect() + ); // depth is 1, allows two candidates let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 1, ancestors.clone(), ) .unwrap(); - // Before populating the chain, all candidates can be added as potential. - let mut modified_storage = CandidateStorage::default(); - let chain = FragmentChain::populate(scope.clone(), &modified_storage); - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::Anyhow - ); - } - // Add an unconnected candidate. We now should only allow a Connected candidate, because max - // depth only allows one more candidate. - modified_storage - .add_candidate(candidate_b.clone(), pvd_b.clone(), CandidateState::Seconded) - .unwrap(); - let chain = FragmentChain::populate(scope.clone(), &modified_storage); - for (candidate, pvd) in - [(candidate_a.clone(), pvd_a.clone()), (candidate_c.clone(), pvd_c.clone())] - { - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::IfConnected - ); - } + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&candidate_a_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); - // Now try populating from all candidates. - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - // since depth is maxed out, we can't add more potential candidates - // candidate A and B are no longer a potential candidate because they're already present. - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_c_hash].into_iter().collect() + ); - // depths larger than 2, allows all candidates + // depth is larger than 2, allows all three candidates for depth in 2..6 { let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], depth, ancestors.clone(), ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - // Candidates are no longer potential candidates because they're already part of the - // chain. - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&candidate_a_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); + + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!( + chain.best_chain_vec(), + vec![candidate_a_hash, candidate_b_hash, candidate_c_hash] + ); + assert_eq!(chain.unconnected_len(), 0); } } - // Wrong relay parents + // Relay parents out of scope { - // Candidates A has relay parent out of scope. - let ancestors_without_a = vec![relay_parent_b_info.clone()]; + // Candidate A has relay parent out of scope. Candidates B and C will also be deleted since + // they form a chain with A. + let ancestors_without_x = vec![relay_parent_y_info.clone()]; let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 4, - ancestors_without_a, + ancestors_without_x, ) .unwrap(); + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert!(chain.best_chain_vec().is_empty()); + assert_eq!(chain.unconnected_len(), 0); - let mut chain = FragmentChain::populate(scope, &storage); - assert!(chain.to_vec().is_empty()); - - chain.extend_from_storage(&storage); - assert!(chain.to_vec().is_empty()); - - // Candidate A is not a potential candidate, but candidates B and C still are. - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate_a.hash(), - &candidate_a.descriptor.relay_parent, - pvd_a.parent_head.hash(), - Some(candidate_a.commitments.head_data.hash()), - ), - PotentialAddition::None + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::RelayParentNotInScope(_, _)) ); - for (candidate, pvd) in - [(candidate_b.clone(), pvd_b.clone()), (candidate_c.clone(), pvd_c.clone())] - { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::Anyhow - ); - } + // However, if taken independently, both B and C still have potential, since we + // don't know that A doesn't. + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); - // Candidate C has the same relay parent as candidate A's parent. Relay parent not allowed - // to move backwards - let mut modified_storage = storage.clone(); - modified_storage.remove_candidate(&candidate_c_hash); - let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( - para_id, - relay_parent_a, - 1, - vec![0x0c].into(), - vec![0x0d].into(), - 2, - ); - modified_storage - .add_candidate(wrong_candidate_c.clone(), wrong_pvd_c.clone(), CandidateState::Seconded) - .unwrap(); + // Candidates A and B have relay parents out of scope. Candidate C will also be deleted + // since it forms a chain with A and B. let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 4, - ancestors.clone(), + vec![], ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); + let chain = populate_chain_from_previous_storage(&scope, &storage); - // Candidate C is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &wrong_candidate_c.hash(), - &wrong_candidate_c.descriptor.relay_parent, - wrong_pvd_c.parent_head.hash(), - Some(wrong_candidate_c.commitments.head_data.hash()), - ), - PotentialAddition::None + assert!(chain.best_chain_vec().is_empty()); + assert_eq!(chain.unconnected_len(), 0); + + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::RelayParentNotInScope(_, _)) + ); + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_b_entry), + Err(Error::RelayParentNotInScope(_, _)) ); + // However, if taken independently, C still has potential, since we + // don't know that A and B don't + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); } - // Parachain fork and cycles are not allowed. + // Parachain cycle is not allowed. Make C have the same parent as A. { - // Candidate C has the same parent as candidate B. - let mut modified_storage = storage.clone(); - modified_storage.remove_candidate(&candidate_c_hash); - let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( - para_id, - relay_parent_c, - 2, - vec![0x0b].into(), - vec![0x0d].into(), - 2, - ); - modified_storage - .add_candidate(wrong_candidate_c.clone(), wrong_pvd_c.clone(), CandidateState::Seconded) - .unwrap(); - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), - base_constraints.clone(), - pending_availability.clone(), - 4, - ancestors.clone(), - ) - .unwrap(); - let mut chain = FragmentChain::populate(scope, &modified_storage); - // We'll either have A->B or A->C. It's not deterministic because CandidateStorage uses - // HashSets and HashMaps. - if chain.to_vec() == vec![candidate_a_hash, candidate_b_hash] { - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - // Candidate C is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &wrong_candidate_c.hash(), - &wrong_candidate_c.descriptor.relay_parent, - wrong_pvd_c.parent_head.hash(), - Some(wrong_candidate_c.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } else if chain.to_vec() == vec![candidate_a_hash, wrong_candidate_c.hash()] { - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, wrong_candidate_c.hash()]); - // Candidate B is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &candidate_b.hash(), - &candidate_b.descriptor.relay_parent, - pvd_b.parent_head.hash(), - Some(candidate_b.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } else { - panic!("Unexpected chain: {:?}", chain.to_vec()); - } - - // Candidate C is a 0-length cycle. - // Candidate C has the same parent as candidate B. let mut modified_storage = storage.clone(); modified_storage.remove_candidate(&candidate_c_hash); let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( para_id, - relay_parent_c, - 2, - vec![0x0c].into(), + relay_parent_z_info.hash, + relay_parent_z_info.number, vec![0x0c].into(), - 2, + vec![0x0a].into(), + relay_parent_z_info.number, ); - modified_storage - .add_candidate(wrong_candidate_c.clone(), wrong_pvd_c.clone(), CandidateState::Seconded) - .unwrap(); - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), - base_constraints.clone(), - pending_availability.clone(), - 4, - ancestors.clone(), + let wrong_candidate_c_entry = CandidateEntry::new( + wrong_candidate_c.hash(), + wrong_candidate_c, + wrong_pvd_c, + CandidateState::Backed, ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - // Candidate C is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &wrong_candidate_c.hash(), - &wrong_candidate_c.descriptor.relay_parent, - wrong_pvd_c.parent_head.hash(), - Some(wrong_candidate_c.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - - // Candidate C points back to the pre-state of candidate C. - let mut modified_storage = storage.clone(); - modified_storage.remove_candidate(&candidate_c_hash); - let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( - para_id, - relay_parent_c, - 2, - vec![0x0c].into(), - vec![0x0b].into(), - 2, - ); - modified_storage - .add_candidate(wrong_candidate_c.clone(), wrong_pvd_c.clone(), CandidateState::Seconded) - .unwrap(); + modified_storage.add_candidate_entry(wrong_candidate_c_entry.clone()).unwrap(); let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 4, ancestors.clone(), ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - // Candidate C is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &wrong_candidate_c.hash(), - &wrong_candidate_c.descriptor.relay_parent, - wrong_pvd_c.parent_head.hash(), - Some(wrong_candidate_c.commitments.head_data.hash()), - ), - PotentialAddition::None + + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash]); + assert_eq!(chain.unconnected_len(), 0); + + assert_matches!( + chain.can_add_candidate_as_potential(&wrong_candidate_c_entry), + Err(Error::Cycle) ); + // However, if taken independently, C still has potential, since we don't know A and B. + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&wrong_candidate_c_entry).is_ok()); } - // Test with candidates pending availability - { - // Valid options - for pending in [ - vec![PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info.clone(), - }], - vec![ - PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info.clone(), - }, - PendingAvailability { - candidate_hash: candidate_b_hash, - relay_parent: relay_parent_b_info.clone(), - }, - ], - vec![ - PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info.clone(), - }, - PendingAvailability { - candidate_hash: candidate_b_hash, - relay_parent: relay_parent_b_info.clone(), - }, - PendingAvailability { - candidate_hash: candidate_c_hash, - relay_parent: relay_parent_c_info.clone(), - }, - ], - ] { - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), - base_constraints.clone(), - pending, - 3, - ancestors.clone(), - ) - .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - } + // Candidate C has the same relay parent as candidate A's parent. Relay parent not allowed + // to move backwards + let mut modified_storage = storage.clone(); + modified_storage.remove_candidate(&candidate_c_hash); + let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0x0c].into(), + vec![0x0d].into(), + 0, + ); + let wrong_candidate_c_entry = CandidateEntry::new( + wrong_candidate_c.hash(), + wrong_candidate_c, + wrong_pvd_c, + CandidateState::Backed, + ) + .unwrap(); + modified_storage.add_candidate_entry(wrong_candidate_c_entry.clone()).unwrap(); + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![], + 4, + ancestors.clone(), + ) + .unwrap(); - // Relay parents of pending availability candidates can be out of scope - // Relay parent of candidate A is out of scope. - let ancestors_without_a = vec![relay_parent_b_info.clone()]; - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), - base_constraints.clone(), - vec![PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info.clone(), - }], - 4, - ancestors_without_a, - ) - .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); - // Even relay parents of pending availability candidates which are out of scope cannot move - // backwards. - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash]); + assert_eq!(chain.unconnected_len(), 0); + assert_matches!( + chain.can_add_candidate_as_potential(&wrong_candidate_c_entry), + Err(Error::RelayParentMovedBackwards) + ); + + // Candidate C is an unconnected candidate. + // C's relay parent is allowed to move backwards from B's relay parent, because C may later on + // trigger a reorg and B may get removed. + let mut modified_storage = storage.clone(); + modified_storage.remove_candidate(&candidate_c_hash); + let (unconnected_pvd_c, unconnected_candidate_c) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0x0d].into(), + vec![0x0e].into(), + 0, + ); + let unconnected_candidate_c_hash = unconnected_candidate_c.hash(); + let unconnected_candidate_c_entry = CandidateEntry::new( + unconnected_candidate_c_hash, + unconnected_candidate_c, + unconnected_pvd_c, + CandidateState::Backed, + ) + .unwrap(); + modified_storage + .add_candidate_entry(unconnected_candidate_c_entry.clone()) + .unwrap(); + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![], + 4, + ancestors.clone(), + ) + .unwrap(); + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&unconnected_candidate_c_entry).is_ok()); + + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); + + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [unconnected_candidate_c_hash].into_iter().collect() + ); + + // Candidate A is a pending availability candidate and Candidate C is an unconnected candidate, + // C's relay parent is not allowed to move backwards from A's relay parent because we're sure A + // will not get removed in the future, as it's already on-chain (unless it times out + // availability, a case for which we don't care to optimise for) + + modified_storage.remove_candidate(&candidate_a_hash); + let (modified_pvd_a, modified_candidate_a) = make_committed_candidate( + para_id, + relay_parent_y_info.hash, + relay_parent_y_info.number, + vec![0x0a].into(), + vec![0x0b].into(), + relay_parent_y_info.number, + ); + let modified_candidate_a_hash = modified_candidate_a.hash(); + modified_storage + .add_candidate_entry( + CandidateEntry::new( + modified_candidate_a_hash, + modified_candidate_a, + modified_pvd_a, + CandidateState::Backed, + ) + .unwrap(), + ) + .unwrap(); + + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![PendingAvailability { + candidate_hash: modified_candidate_a_hash, + relay_parent: relay_parent_y_info.clone(), + }], + 4, + ancestors.clone(), + ) + .unwrap(); + + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); + assert_eq!(chain.best_chain_vec(), vec![modified_candidate_a_hash, candidate_b_hash]); + assert_eq!(chain.unconnected_len(), 0); + assert_matches!( + chain.can_add_candidate_as_potential(&unconnected_candidate_c_entry), + Err(Error::RelayParentPrecedesCandidatePendingAvailability(_, _)) + ); + + // Not allowed to fork from a candidate pending availability + let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( + para_id, + relay_parent_y_info.hash, + relay_parent_y_info.number, + vec![0x0a].into(), + vec![0x0b2].into(), + 0, + ); + let wrong_candidate_c_hash = wrong_candidate_c.hash(); + let wrong_candidate_c_entry = CandidateEntry::new( + wrong_candidate_c_hash, + wrong_candidate_c, + wrong_pvd_c, + CandidateState::Backed, + ) + .unwrap(); + modified_storage.add_candidate_entry(wrong_candidate_c_entry.clone()).unwrap(); + + // Does not even matter if the fork selection rule would have picked up the new candidate, as + // the other is already pending availability. + assert_eq!( + fork_selection_rule(&wrong_candidate_c_hash, &modified_candidate_a_hash), + Ordering::Less + ); + + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![PendingAvailability { + candidate_hash: modified_candidate_a_hash, + relay_parent: relay_parent_y_info.clone(), + }], + 4, + ancestors.clone(), + ) + .unwrap(); + + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); + assert_eq!(chain.best_chain_vec(), vec![modified_candidate_a_hash, candidate_b_hash]); + assert_eq!(chain.unconnected_len(), 0); + assert_matches!( + chain.can_add_candidate_as_potential(&wrong_candidate_c_entry), + Err(Error::ForkWithCandidatePendingAvailability(_)) + ); + + // Test with candidates pending availability + { + // Valid options + for pending in [ + vec![PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info.clone(), + }], + vec![ + PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info.clone(), + }, + PendingAvailability { + candidate_hash: candidate_b_hash, + relay_parent: relay_parent_y_info.clone(), + }, + ], + vec![ + PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info.clone(), + }, + PendingAvailability { + candidate_hash: candidate_b_hash, + relay_parent: relay_parent_y_info.clone(), + }, + PendingAvailability { + candidate_hash: candidate_c_hash, + relay_parent: relay_parent_z_info.clone(), + }, + ], + ] { + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + pending, + 3, + ancestors.clone(), + ) + .unwrap(); + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!( + chain.best_chain_vec(), + vec![candidate_a_hash, candidate_b_hash, candidate_c_hash] + ); + assert_eq!(chain.unconnected_len(), 0); + } + + // Relay parents of pending availability candidates can be out of scope + // Relay parent of candidate A is out of scope. + let ancestors_without_x = vec![relay_parent_y_info.clone()]; + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info.clone(), + }], + 4, + ancestors_without_x, + ) + .unwrap(); + let chain = populate_chain_from_previous_storage(&scope, &storage); + + assert_eq!( + chain.best_chain_vec(), + vec![candidate_a_hash, candidate_b_hash, candidate_c_hash] + ); + assert_eq!(chain.unconnected_len(), 0); + + // Even relay parents of pending availability candidates which are out of scope cannot + // move backwards. + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), base_constraints.clone(), vec![ PendingAvailability { candidate_hash: candidate_a_hash, relay_parent: RelayChainBlockInfo { - hash: relay_parent_a_info.hash, + hash: relay_parent_x_info.hash, number: 1, - storage_root: relay_parent_a_info.storage_root, + storage_root: relay_parent_x_info.storage_root, }, }, PendingAvailability { candidate_hash: candidate_b_hash, relay_parent: RelayChainBlockInfo { - hash: relay_parent_b_info.hash, + hash: relay_parent_y_info.hash, number: 0, - storage_root: relay_parent_b_info.storage_root, + storage_root: relay_parent_y_info.storage_root, }, }, ], @@ -976,271 +961,418 @@ fn populate_and_extend_from_storage_with_existing_empty_to_vec() { vec![], ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert!(chain.to_vec().is_empty()); - - chain.extend_from_storage(&storage); - assert!(chain.to_vec().is_empty()); + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert!(chain.best_chain_vec().is_empty()); + assert_eq!(chain.unconnected_len(), 0); } -} -#[test] -fn extend_from_storage_with_existing_to_vec() { - let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - let relay_parent_b = Hash::repeat_byte(2); - let relay_parent_d = Hash::repeat_byte(3); + // More complex case: + // max_depth is 2 (a chain of max depth 3). + // A -> B -> C are the best backable chain. + // D is backed but would exceed the max depth. + // F is unconnected and seconded. + // A1 has same parent as A, is backed but has a higher candidate hash. It'll therefore be + // deleted. + // A1 has underneath a subtree that will all need to be trimmed. A1 -> B1. B1 -> C1 + // and B1 -> C2. (C1 is backed). + // A2 is seconded but is kept because it has a lower candidate hash than A. + // A2 points to B2, which is backed. + // + // Check that D, F, A2 and B2 are kept as unconnected potential candidates. - let (pvd_a, candidate_a) = make_committed_candidate( + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![], + 2, + ancestors.clone(), + ) + .unwrap(); + + // Candidate D + let (pvd_d, candidate_d) = make_committed_candidate( para_id, - relay_parent_a, - 0, - vec![0x0a].into(), - vec![0x0b].into(), - 0, + relay_parent_z_info.hash, + relay_parent_z_info.number, + vec![0x0d].into(), + vec![0x0e].into(), + relay_parent_z_info.number, ); - let candidate_a_hash = candidate_a.hash(); - - let (pvd_b, candidate_b) = make_committed_candidate( + let candidate_d_hash = candidate_d.hash(); + let candidate_d_entry = + CandidateEntry::new(candidate_d_hash, candidate_d, pvd_d, CandidateState::Backed).unwrap(); + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_d_entry) + .is_ok()); + storage.add_candidate_entry(candidate_d_entry).unwrap(); + + // Candidate F + let (pvd_f, candidate_f) = make_committed_candidate( para_id, - relay_parent_b, - 1, - vec![0x0b].into(), - vec![0x0c].into(), - 1, + relay_parent_z_info.hash, + relay_parent_z_info.number, + vec![0x0f].into(), + vec![0xf1].into(), + 1000, ); - let candidate_b_hash = candidate_b.hash(); + let candidate_f_hash = candidate_f.hash(); + let candidate_f_entry = + CandidateEntry::new(candidate_f_hash, candidate_f, pvd_f, CandidateState::Seconded) + .unwrap(); + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_f_entry) + .is_ok()); + storage.add_candidate_entry(candidate_f_entry.clone()).unwrap(); - let (pvd_c, candidate_c) = make_committed_candidate( + // Candidate A1 + let (pvd_a1, candidate_a1) = make_committed_candidate( para_id, - // Use the same relay parent number as B to test that it doesn't need to change between - // candidates. - relay_parent_b, - 1, - vec![0x0c].into(), - vec![0x0d].into(), - 1, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0x0a].into(), + vec![0xb1].into(), + relay_parent_x_info.number, ); - let candidate_c_hash = candidate_c.hash(); + let candidate_a1_hash = candidate_a1.hash(); + let candidate_a1_entry = + CandidateEntry::new(candidate_a1_hash, candidate_a1, pvd_a1, CandidateState::Backed) + .unwrap(); + // Candidate A1 is created so that its hash is greater than the candidate A hash. + assert_eq!(fork_selection_rule(&candidate_a_hash, &candidate_a1_hash), Ordering::Less); - // Candidate D will never be added to the chain. - let (pvd_d, candidate_d) = make_committed_candidate( - para_id, - relay_parent_d, - 2, - vec![0x0e].into(), - vec![0x0f].into(), - 1, + assert_matches!( + populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_a1_entry), + Err(Error::ForkChoiceRule(other)) if candidate_a_hash == other ); - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - let relay_parent_b_info = RelayChainBlockInfo { - number: pvd_b.relay_parent_number, - hash: relay_parent_b, - storage_root: pvd_b.relay_parent_storage_root, - }; - let relay_parent_d_info = RelayChainBlockInfo { - number: pvd_d.relay_parent_number, - hash: relay_parent_d, - storage_root: pvd_d.relay_parent_storage_root, - }; + storage.add_candidate_entry(candidate_a1_entry.clone()).unwrap(); - let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - let pending_availability = Vec::new(); + // Candidate B1. + let (pvd_b1, candidate_b1) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0xb1].into(), + vec![0xc1].into(), + relay_parent_x_info.number, + ); + let candidate_b1_hash = candidate_b1.hash(); + let candidate_b1_entry = + CandidateEntry::new(candidate_b1_hash, candidate_b1, pvd_b1, CandidateState::Seconded) + .unwrap(); + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_b1_entry) + .is_ok()); - let ancestors = vec![ - // These need to be ordered in reverse. - relay_parent_b_info.clone(), - relay_parent_a_info.clone(), - ]; + storage.add_candidate_entry(candidate_b1_entry).unwrap(); - // Already had A and C in the storage. Introduce B, which should add both B and C to the chain - // now. - { - let mut storage = CandidateStorage::default(); - storage - .add_candidate(candidate_a.clone(), pvd_a.clone(), CandidateState::Seconded) + // Candidate C1. + let (pvd_c1, candidate_c1) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0xc1].into(), + vec![0xd1].into(), + relay_parent_x_info.number, + ); + let candidate_c1_hash = candidate_c1.hash(); + let candidate_c1_entry = + CandidateEntry::new(candidate_c1_hash, candidate_c1, pvd_c1, CandidateState::Backed) .unwrap(); - storage - .add_candidate(candidate_c.clone(), pvd_c.clone(), CandidateState::Seconded) + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_c1_entry) + .is_ok()); + + storage.add_candidate_entry(candidate_c1_entry).unwrap(); + + // Candidate C2. + let (pvd_c2, candidate_c2) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0xc1].into(), + vec![0xd2].into(), + relay_parent_x_info.number, + ); + let candidate_c2_hash = candidate_c2.hash(); + let candidate_c2_entry = + CandidateEntry::new(candidate_c2_hash, candidate_c2, pvd_c2, CandidateState::Seconded) .unwrap(); - storage - .add_candidate(candidate_d.clone(), pvd_d.clone(), CandidateState::Seconded) + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_c2_entry) + .is_ok()); + storage.add_candidate_entry(candidate_c2_entry).unwrap(); + + // Candidate A2. + let (pvd_a2, candidate_a2) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0x0a].into(), + vec![0xb3].into(), + relay_parent_x_info.number, + ); + let candidate_a2_hash = candidate_a2.hash(); + let candidate_a2_entry = + CandidateEntry::new(candidate_a2_hash, candidate_a2, pvd_a2, CandidateState::Seconded) .unwrap(); + // Candidate A2 is created so that its hash is greater than the candidate A hash. + assert_eq!(fork_selection_rule(&candidate_a2_hash, &candidate_a_hash), Ordering::Less); - let scope = Scope::with_ancestors( - para_id, - relay_parent_d_info.clone(), - base_constraints.clone(), - pending_availability.clone(), - 4, - ancestors.clone(), - ) - .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash]); + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_a2_entry) + .is_ok()); - storage - .add_candidate(candidate_b.clone(), pvd_b.clone(), CandidateState::Seconded) + storage.add_candidate_entry(candidate_a2_entry).unwrap(); + + // Candidate B2. + let (pvd_b2, candidate_b2) = make_committed_candidate( + para_id, + relay_parent_y_info.hash, + relay_parent_y_info.number, + vec![0xb3].into(), + vec![0xb4].into(), + relay_parent_y_info.number, + ); + let candidate_b2_hash = candidate_b2.hash(); + let candidate_b2_entry = + CandidateEntry::new(candidate_b2_hash, candidate_b2, pvd_b2, CandidateState::Backed) .unwrap(); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - } + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_b2_entry) + .is_ok()); + storage.add_candidate_entry(candidate_b2_entry).unwrap(); - // Already had A and B in the chain. Introduce C. + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_d_hash, candidate_f_hash, candidate_a2_hash, candidate_b2_hash] + .into_iter() + .collect() + ); + // Cannot add as potential an already present candidate (whether it's in the best chain or in + // unconnected storage) + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::CandidateAlreadyKnown) + ); + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_f_entry), + Err(Error::CandidateAlreadyKnown) + ); + + // Simulate a best chain reorg by backing a2. { - let mut storage = CandidateStorage::default(); - storage - .add_candidate(candidate_a.clone(), pvd_a.clone(), CandidateState::Seconded) - .unwrap(); - storage - .add_candidate(candidate_b.clone(), pvd_b.clone(), CandidateState::Seconded) - .unwrap(); - storage - .add_candidate(candidate_d.clone(), pvd_d.clone(), CandidateState::Seconded) - .unwrap(); + let mut chain = chain.clone(); + chain.candidate_backed(&candidate_a2_hash); + assert_eq!(chain.best_chain_vec(), vec![candidate_a2_hash, candidate_b2_hash]); + // F is kept as it was truly unconnected. The rest will be trimmed. + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_f_hash].into_iter().collect() + ); - let scope = Scope::with_ancestors( - para_id, - relay_parent_d_info.clone(), - base_constraints.clone(), - pending_availability.clone(), - 4, - ancestors.clone(), + // A and A1 will never have potential again. + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a1_entry), + Err(Error::ForkChoiceRule(_)) + ); + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::ForkChoiceRule(_)) + ); + } + + // Candidate F has an invalid hrmp watermark. however, it was not checked beforehand as we don't + // have its parent yet. Add its parent now. This will not impact anything as E is not yet part + // of the best chain. + + let (pvd_e, candidate_e) = make_committed_candidate( + para_id, + relay_parent_z_info.hash, + relay_parent_z_info.number, + vec![0x0e].into(), + vec![0x0f].into(), + relay_parent_z_info.number, + ); + let candidate_e_hash = candidate_e.hash(); + storage + .add_candidate_entry( + CandidateEntry::new(candidate_e_hash, candidate_e, pvd_e, CandidateState::Seconded) + .unwrap(), ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - storage - .add_candidate(candidate_c.clone(), pvd_c.clone(), CandidateState::Seconded) - .unwrap(); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - } + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [ + candidate_d_hash, + candidate_f_hash, + candidate_a2_hash, + candidate_b2_hash, + candidate_e_hash + ] + .into_iter() + .collect() + ); + + // Simulate the fact that candidates A, B, C are now pending availability. + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![ + PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info, + }, + PendingAvailability { + candidate_hash: candidate_b_hash, + relay_parent: relay_parent_y_info, + }, + PendingAvailability { + candidate_hash: candidate_c_hash, + relay_parent: relay_parent_z_info.clone(), + }, + ], + 2, + ancestors.clone(), + ) + .unwrap(); + + // A2 and B2 will now be trimmed + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_d_hash, candidate_f_hash, candidate_e_hash].into_iter().collect() + ); + // Cannot add as potential an already pending availability candidate + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::CandidateAlreadyKnown) + ); + + // Simulate the fact that candidates A, B and C have been included. + + let base_constraints = make_constraints(0, vec![0], HeadData(vec![0x0d])); + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![], + 2, + ancestors.clone(), + ) + .unwrap(); + + let prev_chain = chain; + let mut chain = FragmentChain::init(scope, CandidateStorage::default()); + chain.populate_from_previous(&prev_chain); + assert_eq!(chain.best_chain_vec(), vec![candidate_d_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_e_hash, candidate_f_hash].into_iter().collect() + ); + + // Mark E as backed. F will be dropped for invalid watermark. No other unconnected candidates. + chain.candidate_backed(&candidate_e_hash); + assert_eq!(chain.best_chain_vec(), vec![candidate_d_hash, candidate_e_hash]); + assert_eq!(chain.unconnected_len(), 0); + + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_f_entry), + Err(Error::CheckAgainstConstraints(_)) + ); } #[test] -fn test_find_ancestor_path_and_find_backable_chain_empty_to_vec() { - let para_id = ParaId::from(5u32); +fn test_find_ancestor_path_and_find_backable_chain_empty_best_chain() { let relay_parent = Hash::repeat_byte(1); let required_parent: HeadData = vec![0xff].into(); let max_depth = 10; // Empty chain - let storage = CandidateStorage::default(); let base_constraints = make_constraints(0, vec![0], required_parent.clone()); let relay_parent_info = RelayChainBlockInfo { number: 0, hash: relay_parent, storage_root: Hash::zero() }; - let scope = Scope::with_ancestors( - para_id, - relay_parent_info, - base_constraints, - vec![], - max_depth, - vec![], - ) - .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - assert!(chain.to_vec().is_empty()); + let scope = + Scope::with_ancestors(relay_parent_info, base_constraints, vec![], max_depth, vec![]) + .unwrap(); + let chain = FragmentChain::init(scope, CandidateStorage::default()); + assert_eq!(chain.best_chain_len(), 0); assert_eq!(chain.find_ancestor_path(Ancestors::new()), 0); - assert_eq!(chain.find_backable_chain(Ancestors::new(), 2, |_| true), vec![]); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 2), vec![]); // Invalid candidate. let ancestors: Ancestors = [CandidateHash::default()].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 0); - assert_eq!(chain.find_backable_chain(ancestors, 2, |_| true), vec![]); + assert_eq!(chain.find_backable_chain(ancestors, 2), vec![]); } #[test] -fn test_find_ancestor_path_and_find_backable_to_vec() { +fn test_find_ancestor_path_and_find_backable_chain() { let para_id = ParaId::from(5u32); let relay_parent = Hash::repeat_byte(1); let required_parent: HeadData = vec![0xff].into(); let max_depth = 5; let relay_parent_number = 0; - let relay_parent_storage_root = Hash::repeat_byte(69); + let relay_parent_storage_root = Hash::zero(); let mut candidates = vec![]; - - // Candidate 0 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - required_parent.clone(), - vec![0].into(), - 0, - )); - // Candidate 1 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - vec![0].into(), - vec![1].into(), - 0, - )); - // Candidate 2 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - vec![1].into(), - vec![2].into(), - 0, - )); - // Candidate 3 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - vec![2].into(), - vec![3].into(), - 0, - )); - // Candidate 4 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - vec![3].into(), - vec![4].into(), - 0, - )); - // Candidate 5 + + // Candidate 0 candidates.push(make_committed_candidate( para_id, relay_parent, 0, - vec![4].into(), - vec![5].into(), + required_parent.clone(), + vec![0].into(), 0, )); - let base_constraints = make_constraints(0, vec![0], required_parent.clone()); + // Candidates 1..=5 + for index in 1..=5 { + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + vec![index - 1].into(), + vec![index].into(), + 0, + )); + } + let mut storage = CandidateStorage::default(); + for (pvd, candidate) in candidates.iter() { + storage + .add_candidate_entry( + CandidateEntry::new_seconded(candidate.hash(), candidate.clone(), pvd.clone()) + .unwrap(), + ) + .unwrap(); + } + + let candidates = candidates + .into_iter() + .map(|(_pvd, candidate)| candidate.hash()) + .collect::>(); + let hashes = + |range: Range| range.map(|i| (candidates[i], relay_parent)).collect::>(); + let relay_parent_info = RelayChainBlockInfo { number: relay_parent_number, hash: relay_parent, storage_root: relay_parent_storage_root, }; - for (pvd, candidate) in candidates.iter() { - storage - .add_candidate(candidate.clone(), pvd.clone(), CandidateState::Seconded) - .unwrap(); - } - let candidates = candidates.into_iter().map(|(_pvd, candidate)| candidate).collect::>(); + let base_constraints = make_constraints(0, vec![0], required_parent.clone()); let scope = Scope::with_ancestors( - para_id, relay_parent_info.clone(), base_constraints.clone(), vec![], @@ -1248,506 +1380,140 @@ fn test_find_ancestor_path_and_find_backable_to_vec() { vec![], ) .unwrap(); - let chain = FragmentChain::populate(scope, &storage); + let mut chain = populate_chain_from_previous_storage(&scope, &storage); + // For now, candidates are only seconded, not backed. So the best chain is empty and no + // candidate will be returned. assert_eq!(candidates.len(), 6); - assert_eq!(chain.to_vec().len(), 6); + assert_eq!(chain.best_chain_len(), 0); + assert_eq!(chain.unconnected_len(), 6); + + for count in 0..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count).len(), 0); + } + + // Do tests with only a couple of candidates being backed. + { + let mut chain = chain.clone(); + chain.candidate_backed(&&candidates[5]); + for count in 0..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count).len(), 0); + } + chain.candidate_backed(&&candidates[3]); + chain.candidate_backed(&&candidates[4]); + for count in 0..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count).len(), 0); + } + + chain.candidate_backed(&&candidates[1]); + for count in 0..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count).len(), 0); + } + + chain.candidate_backed(&&candidates[0]); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 1), hashes(0..1)); + for count in 2..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count), hashes(0..2)); + } + + // Now back the missing piece. + chain.candidate_backed(&&candidates[2]); + assert_eq!(chain.best_chain_len(), 6); + for count in 0..10 { + assert_eq!( + chain.find_backable_chain(Ancestors::new(), count), + (0..6) + .take(count as usize) + .map(|i| (candidates[i], relay_parent)) + .collect::>() + ); + } + } + + // Now back all candidates. Back them in a random order. The result should always be the same. + let mut candidates_shuffled = candidates.clone(); + candidates_shuffled.shuffle(&mut thread_rng()); + for candidate in candidates.iter() { + chain.candidate_backed(candidate); + storage.mark_backed(candidate); + } // No ancestors supplied. assert_eq!(chain.find_ancestor_path(Ancestors::new()), 0); - assert_eq!(chain.find_backable_chain(Ancestors::new(), 0, |_| true), vec![]); - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 1, |_| true), - [0].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 2, |_| true), - [0, 1].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 5, |_| true), - [0, 1, 2, 3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 0), vec![]); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 1), hashes(0..1)); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 2), hashes(0..2)); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 5), hashes(0..5)); for count in 6..10 { - assert_eq!( - chain.find_backable_chain(Ancestors::new(), count, |_| true), - [0, 1, 2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(Ancestors::new(), count), hashes(0..6)); } - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 7, |_| true), - [0, 1, 2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 10, |_| true), - [0, 1, 2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 7), hashes(0..6)); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 10), hashes(0..6)); // Ancestor which is not part of the chain. Will be ignored. let ancestors: Ancestors = [CandidateHash::default()].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 0); - assert_eq!( - chain.find_backable_chain(ancestors, 4, |_| true), - [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - let ancestors: Ancestors = - [candidates[1].hash(), CandidateHash::default()].into_iter().collect(); + assert_eq!(chain.find_backable_chain(ancestors, 4), hashes(0..4)); + + let ancestors: Ancestors = [candidates[1], CandidateHash::default()].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 0); - assert_eq!( - chain.find_backable_chain(ancestors, 4, |_| true), - [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - let ancestors: Ancestors = - [candidates[0].hash(), CandidateHash::default()].into_iter().collect(); + assert_eq!(chain.find_backable_chain(ancestors, 4), hashes(0..4)); + + let ancestors: Ancestors = [candidates[0], CandidateHash::default()].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 1); - assert_eq!( - chain.find_backable_chain(ancestors, 4, |_| true), - [1, 2, 3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors, 4), hashes(1..5)); // Ancestors which are part of the chain but don't form a path from root. Will be ignored. - let ancestors: Ancestors = [candidates[1].hash(), candidates[2].hash()].into_iter().collect(); + let ancestors: Ancestors = [candidates[1], candidates[2]].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 0); - assert_eq!( - chain.find_backable_chain(ancestors, 4, |_| true), - [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors, 4), hashes(0..4)); // Valid ancestors. - let ancestors: Ancestors = [candidates[2].hash(), candidates[0].hash(), candidates[1].hash()] - .into_iter() - .collect(); + let ancestors: Ancestors = [candidates[2], candidates[0], candidates[1]].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 3); - assert_eq!( - chain.find_backable_chain(ancestors.clone(), 2, |_| true), - [3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), 2), hashes(3..5)); for count in 3..10 { - assert_eq!( - chain.find_backable_chain(ancestors.clone(), count, |_| true), - [3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), count), hashes(3..6)); } // Valid ancestors with candidates which have been omitted due to timeouts - let ancestors: Ancestors = [candidates[0].hash(), candidates[2].hash()].into_iter().collect(); + let ancestors: Ancestors = [candidates[0], candidates[2]].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 1); - assert_eq!( - chain.find_backable_chain(ancestors.clone(), 3, |_| true), - [1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - assert_eq!( - chain.find_backable_chain(ancestors.clone(), 4, |_| true), - [1, 2, 3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), 3), hashes(1..4)); + assert_eq!(chain.find_backable_chain(ancestors.clone(), 4), hashes(1..5)); for count in 5..10 { - assert_eq!( - chain.find_backable_chain(ancestors.clone(), count, |_| true), - [1, 2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), count), hashes(1..6)); } - let ancestors: Ancestors = [candidates[0].hash(), candidates[1].hash(), candidates[3].hash()] - .into_iter() - .collect(); + let ancestors: Ancestors = [candidates[0], candidates[1], candidates[3]].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 2); - assert_eq!( - chain.find_backable_chain(ancestors.clone(), 4, |_| true), - [2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), 4), hashes(2..6)); // Requested count is 0. - assert_eq!(chain.find_backable_chain(ancestors, 0, |_| true), vec![]); - - // Stop when we've found a candidate for which pred returns false. - let ancestors: Ancestors = [candidates[2].hash(), candidates[0].hash(), candidates[1].hash()] - .into_iter() - .collect(); - for count in 1..10 { - assert_eq!( - // Stop at 4. - chain.find_backable_chain(ancestors.clone(), count, |hash| hash != - &candidates[4].hash()), - [3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - } + assert_eq!(chain.find_backable_chain(ancestors, 0), vec![]); // Stop when we've found a candidate which is pending availability { let scope = Scope::with_ancestors( - para_id, relay_parent_info.clone(), base_constraints, // Mark the third candidate as pending availability vec![PendingAvailability { - candidate_hash: candidates[3].hash(), + candidate_hash: candidates[3], relay_parent: relay_parent_info, }], max_depth, vec![], ) .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - let ancestors: Ancestors = - [candidates[0].hash(), candidates[1].hash()].into_iter().collect(); + let chain = populate_chain_from_previous_storage(&scope, &storage); + let ancestors: Ancestors = [candidates[0], candidates[1]].into_iter().collect(); assert_eq!( // Stop at 4. - chain.find_backable_chain(ancestors.clone(), 3, |_| true), - [2].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - } -} - -#[test] -fn hypothetical_membership() { - let mut storage = CandidateStorage::default(); - - let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - - let (pvd_a, candidate_a) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0a].into(), - vec![0x0b].into(), - 0, - ); - let candidate_a_hash = candidate_a.hash(); - - let (pvd_b, candidate_b) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0b].into(), - vec![0x0c].into(), - 0, - ); - let candidate_b_hash = candidate_b.hash(); - - let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - - let max_depth = 4; - storage.add_candidate(candidate_a, pvd_a, CandidateState::Seconded).unwrap(); - storage.add_candidate(candidate_b, pvd_b, CandidateState::Seconded).unwrap(); - let scope = Scope::with_ancestors( - para_id, - relay_parent_a_info.clone(), - base_constraints.clone(), - vec![], - max_depth, - vec![], - ) - .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - - assert_eq!(chain.to_vec().len(), 2); - - // Check candidates which are already present - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: candidate_a_hash, - }, - &storage, - )); - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0b]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: candidate_b_hash, - }, - &storage, - )); - - // Forks not allowed. - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(21)), - }, - &storage, - )); - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0b]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(22)), - }, - &storage, - )); - - // Unknown candidate which builds on top of the current chain. - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0c]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(23)), - }, - &storage, - )); - - // Unknown unconnected candidate which may be valid. - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0e]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(23)), - }, - &storage, - )); - - // The number of unconnected candidates is limited (chain.len() + unconnected) <= max_depth - { - // C will be an unconnected candidate. - let (pvd_c, candidate_c) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0e].into(), - vec![0x0f].into(), - 0, - ); - let candidate_c_hash = candidate_c.hash(); - - // Add an invalid candidate in the storage. This would introduce a fork. Just to test that - // it's ignored. - let (invalid_pvd, invalid_candidate) = make_committed_candidate( - para_id, - relay_parent_a, - 1, - vec![0x0a].into(), - vec![0x0b].into(), - 0, + chain.find_backable_chain(ancestors.clone(), 3), + hashes(2..3) ); - - let scope = Scope::with_ancestors( - para_id, - relay_parent_a_info, - base_constraints, - vec![], - 2, - vec![], - ) - .unwrap(); - let mut storage = storage.clone(); - storage.add_candidate(candidate_c, pvd_c, CandidateState::Seconded).unwrap(); - - let chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - - storage - .add_candidate(invalid_candidate, invalid_pvd, CandidateState::Seconded) - .unwrap(); - - // Check that C is accepted as a potential unconnected candidate. - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0e]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_hash: candidate_c_hash, - candidate_para: para_id - }, - &storage, - )); - - // Since C is already an unconnected candidate in the storage. - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0f]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(23)), - }, - &storage, - )); } } - -#[test] -fn hypothetical_membership_stricter_on_complete_candidates() { - let storage = CandidateStorage::default(); - - let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - - let (pvd_a, candidate_a) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0a].into(), - vec![0x0b].into(), - 1000, // watermark is illegal - ); - - let candidate_a_hash = candidate_a.hash(); - - let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - let pending_availability = Vec::new(); - - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - - let max_depth = 4; - let scope = Scope::with_ancestors( - para_id, - relay_parent_a_info, - base_constraints, - pending_availability, - max_depth, - vec![], - ) - .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: candidate_a_hash, - }, - &storage, - )); - - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Complete { - receipt: Arc::new(candidate_a), - persisted_validation_data: pvd_a, - candidate_hash: candidate_a_hash, - }, - &storage, - )); -} - -#[test] -fn hypothetical_membership_with_pending_availability_in_scope() { - let mut storage = CandidateStorage::default(); - - let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - let relay_parent_b = Hash::repeat_byte(2); - let relay_parent_c = Hash::repeat_byte(3); - - let (pvd_a, candidate_a) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0a].into(), - vec![0x0b].into(), - 0, - ); - let candidate_a_hash = candidate_a.hash(); - - let (pvd_b, candidate_b) = make_committed_candidate( - para_id, - relay_parent_b, - 1, - vec![0x0b].into(), - vec![0x0c].into(), - 1, - ); - - // Note that relay parent `a` is not allowed. - let base_constraints = make_constraints(1, vec![], vec![0x0a].into()); - - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - let pending_availability = vec![PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info, - }]; - - let relay_parent_b_info = RelayChainBlockInfo { - number: pvd_b.relay_parent_number, - hash: relay_parent_b, - storage_root: pvd_b.relay_parent_storage_root, - }; - let relay_parent_c_info = RelayChainBlockInfo { - number: pvd_b.relay_parent_number + 1, - hash: relay_parent_c, - storage_root: Hash::zero(), - }; - - let max_depth = 4; - storage.add_candidate(candidate_a, pvd_a, CandidateState::Seconded).unwrap(); - storage.add_candidate(candidate_b, pvd_b, CandidateState::Backed).unwrap(); - storage.mark_backed(&candidate_a_hash); - - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info, - base_constraints, - pending_availability, - max_depth, - vec![relay_parent_b_info], - ) - .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - - assert_eq!(chain.to_vec().len(), 2); - - let candidate_d_hash = CandidateHash(Hash::repeat_byte(0xAA)); - - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_hash: candidate_a_hash, - candidate_para: para_id - }, - &storage, - )); - - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_c, - candidate_para: para_id, - candidate_hash: candidate_d_hash, - }, - &storage, - )); - - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0b]).hash(), - candidate_relay_parent: relay_parent_c, - candidate_para: para_id, - candidate_hash: candidate_d_hash, - }, - &storage, - )); - - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0c]).hash(), - candidate_relay_parent: relay_parent_b, - candidate_para: para_id, - candidate_hash: candidate_d_hash, - }, - &storage, - )); -} diff --git a/polkadot/node/core/prospective-parachains/src/lib.rs b/polkadot/node/core/prospective-parachains/src/lib.rs index e4b6deffdf4a..ecb1f3a476ec 100644 --- a/polkadot/node/core/prospective-parachains/src/lib.rs +++ b/polkadot/node/core/prospective-parachains/src/lib.rs @@ -26,9 +26,11 @@ //! //! This subsystem also handles concerns such as the relay-chain being forkful and session changes. +#![deny(unused_crate_dependencies)] + use std::collections::{HashMap, HashSet}; -use fragment_chain::{FragmentChain, PotentialAddition}; +use fragment_chain::CandidateStorage; use futures::{channel::oneshot, prelude::*}; use polkadot_node_subsystem::{ @@ -41,6 +43,7 @@ use polkadot_node_subsystem::{ overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_util::{ + backing_implicit_view::{BlockInfoProspectiveParachains as BlockInfo, View as ImplicitView}, inclusion_emulator::{Constraints, RelayChainBlockInfo}, request_session_index_for_child, runtime::{prospective_parachains_mode, ProspectiveParachainsMode}, @@ -55,8 +58,7 @@ use polkadot_primitives::{ use crate::{ error::{FatalError, FatalResult, JfyiError, JfyiErrorResult, Result}, fragment_chain::{ - CandidateState, CandidateStorage, CandidateStorageInsertionError, - Scope as FragmentChainScope, + CandidateEntry, Error as FragmentChainError, FragmentChain, Scope as FragmentChainScope, }, }; @@ -71,20 +73,33 @@ use self::metrics::Metrics; const LOG_TARGET: &str = "parachain::prospective-parachains"; struct RelayBlockViewData { - // Scheduling info for paras and upcoming paras. + // The fragment chains for current and upcoming scheduled paras. fragment_chains: HashMap, - pending_availability: HashSet, } struct View { - // Active or recent relay-chain blocks by block hash. - active_leaves: HashMap, - candidate_storage: HashMap, + // Per relay parent fragment chains. These includes all relay parents under the implicit view. + per_relay_parent: HashMap, + // The hashes of the currently active leaves. This is a subset of the keys in + // `per_relay_parent`. + active_leaves: HashSet, + // The backing implicit view. + implicit_view: ImplicitView, } impl View { + // Initialize with empty values. fn new() -> Self { - View { active_leaves: HashMap::new(), candidate_storage: HashMap::new() } + View { + per_relay_parent: HashMap::new(), + active_leaves: HashSet::new(), + implicit_view: ImplicitView::default(), + } + } + + // Get the fragment chains of this leaf. + fn get_fragment_chains(&self, leaf: &Hash) -> Option<&HashMap> { + self.per_relay_parent.get(&leaf).map(|view_data| &view_data.fragment_chains) } } @@ -142,9 +157,9 @@ async fn run_iteration( FromOrchestra::Signal(OverseerSignal::BlockFinalized(..)) => {}, FromOrchestra::Communication { msg } => match msg { ProspectiveParachainsMessage::IntroduceSecondedCandidate(request, tx) => - handle_introduce_seconded_candidate(&mut *ctx, view, request, tx, metrics).await, + handle_introduce_seconded_candidate(view, request, tx, metrics).await, ProspectiveParachainsMessage::CandidateBacked(para, candidate_hash) => - handle_candidate_backed(&mut *ctx, view, para, candidate_hash).await, + handle_candidate_backed(view, para, candidate_hash, metrics).await, ProspectiveParachainsMessage::GetBackableCandidates( relay_parent, para, @@ -170,17 +185,32 @@ async fn handle_active_leaves_update( update: ActiveLeavesUpdate, metrics: &Metrics, ) -> JfyiErrorResult<()> { - // 1. clean up inactive leaves - // 2. determine all scheduled paras at the new block - // 3. construct new fragment chain for each para for each new leaf - // 4. prune candidate storage. + // For any new active leaf: + // - determine the scheduled paras + // - pre-populate the candidate storage with pending availability candidates and candidates from + // the parent leaf + // - populate the fragment chain + // - add it to the implicit view + // + // Then mark the newly-deactivated leaves as deactivated and update the implicit view. + // Finally, remove any relay parents that are no longer part of the implicit view. + + let _timer = metrics.time_handle_active_leaves_update(); - for deactivated in &update.deactivated { - view.active_leaves.remove(deactivated); - } + gum::trace!( + target: LOG_TARGET, + activated = ?update.activated, + deactivated = ?update.deactivated, + "Handle ActiveLeavesUpdate" + ); let mut temp_header_cache = HashMap::new(); + // There can only be one newly activated leaf, `update.activated` is an `Option`. for activated in update.activated.into_iter() { + if update.deactivated.contains(&activated.hash) { + continue + } + let hash = activated.hash; let mode = prospective_parachains_mode(ctx.sender(), hash) @@ -199,38 +229,34 @@ async fn handle_active_leaves_update( return Ok(()) }; - let scheduled_paras = fetch_upcoming_paras(&mut *ctx, hash).await?; + let scheduled_paras = fetch_upcoming_paras(ctx, hash).await?; - let block_info: RelayChainBlockInfo = - match fetch_block_info(&mut *ctx, &mut temp_header_cache, hash).await? { - None => { - gum::warn!( - target: LOG_TARGET, - block_hash = ?hash, - "Failed to get block info for newly activated leaf block." - ); + let block_info = match fetch_block_info(ctx, &mut temp_header_cache, hash).await? { + None => { + gum::warn!( + target: LOG_TARGET, + block_hash = ?hash, + "Failed to get block info for newly activated leaf block." + ); - // `update.activated` is an option, but we can use this - // to exit the 'loop' and skip this block without skipping - // pruning logic. - continue - }, - Some(info) => info, - }; + // `update.activated` is an option, but we can use this + // to exit the 'loop' and skip this block without skipping + // pruning logic. + continue + }, + Some(info) => info, + }; let ancestry = - fetch_ancestry(&mut *ctx, &mut temp_header_cache, hash, allowed_ancestry_len).await?; + fetch_ancestry(ctx, &mut temp_header_cache, hash, allowed_ancestry_len).await?; - let mut all_pending_availability = HashSet::new(); + let prev_fragment_chains = + ancestry.first().and_then(|prev_leaf| view.get_fragment_chains(&prev_leaf.hash)); - // Find constraints. let mut fragment_chains = HashMap::new(); for para in scheduled_paras { - let candidate_storage = - view.candidate_storage.entry(para).or_insert_with(CandidateStorage::default); - - let backing_state = fetch_backing_state(&mut *ctx, hash, para).await?; - + // Find constraints and pending availability candidates. + let backing_state = fetch_backing_state(ctx, hash, para).await?; let Some((constraints, pending_availability)) = backing_state else { // This indicates a runtime conflict of some kind. gum::debug!( @@ -243,8 +269,6 @@ async fn handle_active_leaves_update( continue }; - all_pending_availability.extend(pending_availability.iter().map(|c| c.candidate_hash)); - let pending_availability = preprocess_candidates_pending_availability( ctx, &mut temp_header_cache, @@ -254,16 +278,18 @@ async fn handle_active_leaves_update( .await?; let mut compact_pending = Vec::with_capacity(pending_availability.len()); + let mut pending_availability_storage = CandidateStorage::default(); + for c in pending_availability { - let res = candidate_storage.add_candidate( + let candidate_hash = c.compact.candidate_hash; + let res = pending_availability_storage.add_pending_availability_candidate( + candidate_hash, c.candidate, c.persisted_validation_data, - CandidateState::Backed, ); - let candidate_hash = c.compact.candidate_hash; match res { - Ok(_) | Err(CandidateStorageInsertionError::CandidateAlreadyKnown(_)) => {}, + Ok(_) | Err(FragmentChainError::CandidateAlreadyKnown) => {}, Err(err) => { gum::warn!( target: LOG_TARGET, @@ -280,119 +306,138 @@ async fn handle_active_leaves_update( compact_pending.push(c.compact); } - let scope = FragmentChainScope::with_ancestors( - para, - block_info.clone(), + let scope = match FragmentChainScope::with_ancestors( + block_info.clone().into(), constraints, compact_pending, max_candidate_depth, - ancestry.iter().cloned(), - ) - .expect("ancestors are provided in reverse order and correctly; qed"); + ancestry + .iter() + .map(|a| RelayChainBlockInfo::from(a.clone())) + .collect::>(), + ) { + Ok(scope) => scope, + Err(unexpected_ancestors) => { + gum::warn!( + target: LOG_TARGET, + para_id = ?para, + max_candidate_depth, + ?ancestry, + leaf = ?hash, + "Relay chain ancestors have wrong order: {:?}", + unexpected_ancestors + ); + continue + }, + }; gum::trace!( target: LOG_TARGET, relay_parent = ?hash, min_relay_parent = scope.earliest_relay_parent().number, para_id = ?para, + ancestors = ?ancestry, "Creating fragment chain" ); - let chain = FragmentChain::populate(scope, &*candidate_storage); + let number_of_pending_candidates = pending_availability_storage.len(); + + // Init the fragment chain with the pending availability candidates. + let mut chain = FragmentChain::init(scope, pending_availability_storage); + + if chain.best_chain_len() < number_of_pending_candidates { + gum::warn!( + target: LOG_TARGET, + relay_parent = ?hash, + para_id = ?para, + "Not all pending availability candidates could be introduced. Actual vs expected count: {}, {}", + chain.best_chain_len(), + number_of_pending_candidates + ) + } + + // If we know the previous fragment chain, use that for further populating the fragment + // chain. + if let Some(prev_fragment_chain) = + prev_fragment_chains.and_then(|chains| chains.get(¶)) + { + chain.populate_from_previous(prev_fragment_chain); + } gum::trace!( target: LOG_TARGET, relay_parent = ?hash, para_id = ?para, - "Populated fragment chain with {} candidates", - chain.len() + "Populated fragment chain with {} candidates: {:?}", + chain.best_chain_len(), + chain.best_chain_vec() + ); + + gum::trace!( + target: LOG_TARGET, + relay_parent = ?hash, + para_id = ?para, + "Potential candidate storage for para: {:?}", + chain.unconnected().map(|candidate| candidate.hash()).collect::>() ); fragment_chains.insert(para, chain); } - view.active_leaves.insert( - hash, - RelayBlockViewData { fragment_chains, pending_availability: all_pending_availability }, - ); - } + view.per_relay_parent.insert(hash, RelayBlockViewData { fragment_chains }); - if !update.deactivated.is_empty() { - // This has potential to be a hotspot. - prune_view_candidate_storage(view, metrics); - } + view.active_leaves.insert(hash); - Ok(()) -} + view.implicit_view + .activate_leaf_from_prospective_parachains(block_info, &ancestry); + } -fn prune_view_candidate_storage(view: &mut View, metrics: &Metrics) { - let _timer = metrics.time_prune_view_candidate_storage(); + for deactivated in update.deactivated { + view.active_leaves.remove(&deactivated); + view.implicit_view.deactivate_leaf(deactivated); + } - let active_leaves = &view.active_leaves; - let mut live_candidates = HashSet::new(); - let mut live_paras = HashSet::new(); - for sub_view in active_leaves.values() { - live_candidates.extend(sub_view.pending_availability.iter().cloned()); + { + let remaining: HashSet<_> = view.implicit_view.all_allowed_relay_parents().collect(); - for (para_id, fragment_chain) in &sub_view.fragment_chains { - live_candidates.extend(fragment_chain.to_vec()); - live_paras.insert(*para_id); - } + view.per_relay_parent.retain(|r, _| remaining.contains(&r)); } - let connected_candidates_count = live_candidates.len(); - for (leaf, sub_view) in active_leaves.iter() { - for (para_id, fragment_chain) in &sub_view.fragment_chains { - if let Some(storage) = view.candidate_storage.get(para_id) { - let unconnected_potential = - fragment_chain.find_unconnected_potential_candidates(storage, None); - if !unconnected_potential.is_empty() { - gum::trace!( - target: LOG_TARGET, - ?leaf, - "Keeping {} unconnected candidates for paraid {} in storage: {:?}", - unconnected_potential.len(), - para_id, - unconnected_potential - ); + if metrics.0.is_some() { + let mut active_connected = 0; + let mut active_unconnected = 0; + let mut candidates_in_implicit_view = 0; + + for (hash, RelayBlockViewData { fragment_chains, .. }) in view.per_relay_parent.iter() { + if view.active_leaves.contains(hash) { + for chain in fragment_chains.values() { + active_connected += chain.best_chain_len(); + active_unconnected += chain.unconnected_len(); + } + } else { + for chain in fragment_chains.values() { + candidates_in_implicit_view += chain.best_chain_len(); + candidates_in_implicit_view += chain.unconnected_len(); } - live_candidates.extend(unconnected_potential); } } - } - - view.candidate_storage.retain(|para_id, storage| { - if !live_paras.contains(¶_id) { - return false - } - - storage.retain(|h| live_candidates.contains(&h)); - - // Even if `storage` is now empty, we retain. - // This maintains a convenient invariant that para-id storage exists - // as long as there's an active head which schedules the para. - true - }); - for (para_id, storage) in view.candidate_storage.iter() { - gum::trace!( - target: LOG_TARGET, - "Keeping a total of {} connected candidates for paraid {} in storage", - storage.candidates().count(), - para_id, - ); + metrics.record_candidate_count(active_connected as u64, active_unconnected as u64); + metrics.record_candidate_count_in_implicit_view(candidates_in_implicit_view as u64); } - metrics.record_candidate_storage_size( - connected_candidates_count as u64, - live_candidates.len().saturating_sub(connected_candidates_count) as u64, - ); + let num_active_leaves = view.active_leaves.len() as u64; + let num_inactive_leaves = + (view.per_relay_parent.len() as u64).saturating_sub(num_active_leaves); + metrics.record_leaves_count(num_active_leaves, num_inactive_leaves); + + Ok(()) } struct ImportablePendingAvailability { candidate: CommittedCandidateReceipt, persisted_validation_data: PersistedValidationData, - compact: crate::fragment_chain::PendingAvailability, + compact: fragment_chain::PendingAvailability, } #[overseer::contextbounds(ProspectiveParachains, prefix = self::overseer)] @@ -435,9 +480,9 @@ async fn preprocess_candidates_pending_availability( relay_parent_number: relay_parent.number, relay_parent_storage_root: relay_parent.storage_root, }, - compact: crate::fragment_chain::PendingAvailability { + compact: fragment_chain::PendingAvailability { candidate_hash: pending.candidate_hash, - relay_parent, + relay_parent: relay_parent.into(), }, }); @@ -447,9 +492,7 @@ async fn preprocess_candidates_pending_availability( Ok(importable) } -#[overseer::contextbounds(ProspectiveParachains, prefix = self::overseer)] -async fn handle_introduce_seconded_candidate( - _ctx: &mut Context, +async fn handle_introduce_seconded_candidate( view: &mut View, request: IntroduceSecondedCandidateRequest, tx: oneshot::Sender, @@ -463,167 +506,163 @@ async fn handle_introduce_seconded_candidate( persisted_validation_data: pvd, } = request; - let Some(storage) = view.candidate_storage.get_mut(¶) else { - gum::warn!( - target: LOG_TARGET, - para_id = ?para, - candidate_hash = ?candidate.hash(), - "Received seconded candidate for inactive para", - ); + let candidate_hash = candidate.hash(); + let candidate_entry = match CandidateEntry::new_seconded(candidate_hash, candidate, pvd) { + Ok(candidate) => candidate, + Err(err) => { + gum::warn!( + target: LOG_TARGET, + para = ?para, + "Cannot add seconded candidate: {}", + err + ); - let _ = tx.send(false); - return + let _ = tx.send(false); + return + }, }; - let parent_head_hash = pvd.parent_head.hash(); - let output_head_hash = Some(candidate.commitments.head_data.hash()); + let mut added = false; + let mut para_scheduled = false; + // We don't iterate only through the active leaves. We also update the deactivated parents in + // the implicit view, so that their upcoming children may see these candidates. + for (relay_parent, rp_data) in view.per_relay_parent.iter_mut() { + let Some(chain) = rp_data.fragment_chains.get_mut(¶) else { continue }; + let is_active_leaf = view.active_leaves.contains(relay_parent); - // We first introduce the candidate in the storage and then try to extend the chain. - // If the candidate gets included in the chain, we can keep it in storage. - // If it doesn't, check that it's still a potential candidate in at least one fragment chain. - // If it's not, we can remove it. + para_scheduled = true; - let candidate_hash = - match storage.add_candidate(candidate.clone(), pvd, CandidateState::Seconded) { - Ok(c) => c, - Err(CandidateStorageInsertionError::CandidateAlreadyKnown(_)) => { + match chain.try_adding_seconded_candidate(&candidate_entry) { + Ok(()) => { gum::debug!( target: LOG_TARGET, - para = ?para, - "Attempting to introduce an already known candidate: {:?}", - candidate.hash() + ?para, + ?relay_parent, + ?is_active_leaf, + "Added seconded candidate {:?}", + candidate_hash ); - // Candidate already known. - let _ = tx.send(true); - return + added = true; }, - Err(CandidateStorageInsertionError::PersistedValidationDataMismatch) => { - // We can't log the candidate hash without either doing more ~expensive - // hashing but this branch indicates something is seriously wrong elsewhere - // so it's doubtful that it would affect debugging. - - gum::warn!( + Err(FragmentChainError::CandidateAlreadyKnown) => { + gum::debug!( target: LOG_TARGET, - para = ?para, - "Received seconded candidate had mismatching validation data", + ?para, + ?relay_parent, + ?is_active_leaf, + "Attempting to introduce an already known candidate: {:?}", + candidate_hash ); - - let _ = tx.send(false); - return + added = true; }, - }; - - let mut keep_in_storage = false; - for (relay_parent, leaf_data) in view.active_leaves.iter_mut() { - if let Some(chain) = leaf_data.fragment_chains.get_mut(¶) { - gum::trace!( - target: LOG_TARGET, - para = ?para, - ?relay_parent, - "Candidates in chain before trying to introduce a new one: {:?}", - chain.to_vec() - ); - chain.extend_from_storage(&*storage); - if chain.contains_candidate(&candidate_hash) { - keep_in_storage = true; - - gum::trace!( + Err(err) => { + gum::debug!( target: LOG_TARGET, + ?para, ?relay_parent, - para = ?para, ?candidate_hash, - "Added candidate to chain.", - ); - } else { - match chain.can_add_candidate_as_potential( - &storage, - &candidate_hash, - &candidate.descriptor.relay_parent, - parent_head_hash, - output_head_hash, - ) { - PotentialAddition::Anyhow => { - gum::trace!( - target: LOG_TARGET, - para = ?para, - ?relay_parent, - ?candidate_hash, - "Kept candidate as unconnected potential.", - ); - - keep_in_storage = true; - }, - _ => { - gum::trace!( - target: LOG_TARGET, - para = ?para, - ?relay_parent, - "Not introducing a new candidate: {:?}", - candidate_hash - ); - }, - } - } + ?is_active_leaf, + "Cannot introduce seconded candidate: {}", + err + ) + }, } } - // If there is at least one leaf where this candidate can be added or potentially added in the - // future, keep it in storage. - if !keep_in_storage { - storage.remove_candidate(&candidate_hash); + if !para_scheduled { + gum::warn!( + target: LOG_TARGET, + para_id = ?para, + ?candidate_hash, + "Received seconded candidate for inactive para", + ); + } + if !added { gum::debug!( target: LOG_TARGET, para = ?para, candidate = ?candidate_hash, - "Newly-seconded candidate cannot be kept under any active leaf", + "Newly-seconded candidate cannot be kept under any relay parent", ); } - let _ = tx.send(keep_in_storage); + let _ = tx.send(added); } -#[overseer::contextbounds(ProspectiveParachains, prefix = self::overseer)] -async fn handle_candidate_backed( - _ctx: &mut Context, +async fn handle_candidate_backed( view: &mut View, para: ParaId, candidate_hash: CandidateHash, + metrics: &Metrics, ) { - let Some(storage) = view.candidate_storage.get_mut(¶) else { - gum::warn!( - target: LOG_TARGET, - para_id = ?para, - ?candidate_hash, - "Received instruction to back a candidate for unscheduled para", - ); + let _timer = metrics.time_candidate_backed(); - return - }; + let mut found_candidate = false; + let mut found_para = false; + + // We don't iterate only through the active leaves. We also update the deactivated parents in + // the implicit view, so that their upcoming children may see these candidates. + for (relay_parent, rp_data) in view.per_relay_parent.iter_mut() { + let Some(chain) = rp_data.fragment_chains.get_mut(¶) else { continue }; + let is_active_leaf = view.active_leaves.contains(relay_parent); + + found_para = true; + if chain.is_candidate_backed(&candidate_hash) { + gum::debug!( + target: LOG_TARGET, + ?para, + ?candidate_hash, + ?is_active_leaf, + "Received redundant instruction to mark as backed an already backed candidate", + ); + found_candidate = true; + } else if chain.contains_unconnected_candidate(&candidate_hash) { + found_candidate = true; + // Mark the candidate as backed. This can recreate the fragment chain. + chain.candidate_backed(&candidate_hash); + + gum::trace!( + target: LOG_TARGET, + ?relay_parent, + ?para, + ?is_active_leaf, + "Candidate backed. Candidate chain for para: {:?}", + chain.best_chain_vec() + ); + + gum::trace!( + target: LOG_TARGET, + ?relay_parent, + ?para, + ?is_active_leaf, + "Potential candidate storage for para: {:?}", + chain.unconnected().map(|candidate| candidate.hash()).collect::>() + ); + } + } - if !storage.contains(&candidate_hash) { + if !found_para { gum::warn!( target: LOG_TARGET, - para_id = ?para, + ?para, ?candidate_hash, - "Received instruction to back unknown candidate", + "Received instruction to back a candidate for unscheduled para", ); return } - if storage.is_backed(&candidate_hash) { + if !found_candidate { + // This can be harmless. It can happen if we received a better backed candidate before and + // dropped this other candidate already. gum::debug!( target: LOG_TARGET, - para_id = ?para, + ?para, ?candidate_hash, - "Received redundant instruction to mark candidate as backed", + "Received instruction to back unknown candidate", ); - - return } - - storage.mark_backed(&candidate_hash); } fn answer_get_backable_candidates( @@ -634,7 +673,7 @@ fn answer_get_backable_candidates( ancestors: Ancestors, tx: oneshot::Sender>, ) { - let Some(data) = view.active_leaves.get(&relay_parent) else { + if !view.active_leaves.contains(&relay_parent) { gum::debug!( target: LOG_TARGET, ?relay_parent, @@ -644,26 +683,25 @@ fn answer_get_backable_candidates( let _ = tx.send(vec![]); return - }; - - let Some(chain) = data.fragment_chains.get(¶) else { + } + let Some(data) = view.per_relay_parent.get(&relay_parent) else { gum::debug!( target: LOG_TARGET, ?relay_parent, para_id = ?para, - "Requested backable candidate for inactive para." + "Requested backable candidate for inexistent relay-parent." ); let _ = tx.send(vec![]); return }; - let Some(storage) = view.candidate_storage.get(¶) else { - gum::warn!( + let Some(chain) = data.fragment_chains.get(¶) else { + gum::debug!( target: LOG_TARGET, ?relay_parent, para_id = ?para, - "No candidate storage for active para", + "Requested backable candidate for inactive para." ); let _ = tx.send(vec![]); @@ -674,38 +712,19 @@ fn answer_get_backable_candidates( target: LOG_TARGET, ?relay_parent, para_id = ?para, - "Candidate storage for para: {:?}", - storage.candidates().map(|candidate| candidate.hash()).collect::>() + "Candidate chain for para: {:?}", + chain.best_chain_vec() ); gum::trace!( target: LOG_TARGET, ?relay_parent, para_id = ?para, - "Candidate chain for para: {:?}", - chain.to_vec() + "Potential candidate storage for para: {:?}", + chain.unconnected().map(|candidate| candidate.hash()).collect::>() ); - let backable_candidates: Vec<_> = chain - .find_backable_chain(ancestors.clone(), count, |candidate| storage.is_backed(candidate)) - .into_iter() - .filter_map(|child_hash| { - storage.relay_parent_of_candidate(&child_hash).map_or_else( - || { - // Here, we'd actually need to trim all of the candidates that follow. Or - // not, the runtime will do this. Impossible scenario anyway. - gum::error!( - target: LOG_TARGET, - ?child_hash, - para_id = ?para, - "Candidate is present in fragment chain but not in candidate's storage!", - ); - None - }, - |parent_hash| Some((child_hash, parent_hash)), - ) - }) - .collect(); + let backable_candidates = chain.find_backable_chain(ancestors.clone(), count); if backable_candidates.is_empty() { gum::trace!( @@ -742,19 +761,32 @@ fn answer_hypothetical_membership_request( } let required_active_leaf = request.fragment_chain_relay_parent; - for (active_leaf, leaf_view) in view + for active_leaf in view .active_leaves .iter() - .filter(|(h, _)| required_active_leaf.as_ref().map_or(true, |x| h == &x)) + .filter(|h| required_active_leaf.as_ref().map_or(true, |x| h == &x)) { + let Some(leaf_view) = view.per_relay_parent.get(&active_leaf) else { continue }; for &mut (ref candidate, ref mut membership) in &mut response { let para_id = &candidate.candidate_para(); let Some(fragment_chain) = leaf_view.fragment_chains.get(para_id) else { continue }; - let Some(candidate_storage) = view.candidate_storage.get(para_id) else { continue }; - if fragment_chain.hypothetical_membership(candidate.clone(), candidate_storage) { - membership.push(*active_leaf); - } + let res = fragment_chain.can_add_candidate_as_potential(candidate); + match res { + Err(FragmentChainError::CandidateAlreadyKnown) | Ok(()) => { + membership.push(*active_leaf); + }, + Err(err) => { + gum::debug!( + target: LOG_TARGET, + para = ?para_id, + leaf = ?active_leaf, + candidate = ?candidate.candidate_hash(), + "Candidate is not a hypothetical member: {}", + err + ) + }, + }; } } @@ -767,9 +799,11 @@ fn answer_minimum_relay_parents_request( tx: oneshot::Sender>, ) { let mut v = Vec::new(); - if let Some(leaf_data) = view.active_leaves.get(&relay_parent) { - for (para_id, fragment_chain) in &leaf_data.fragment_chains { - v.push((*para_id, fragment_chain.scope().earliest_relay_parent().number)); + if view.active_leaves.contains(&relay_parent) { + if let Some(leaf_data) = view.per_relay_parent.get(&relay_parent) { + for (para_id, fragment_chain) in &leaf_data.fragment_chains { + v.push((*para_id, fragment_chain.scope().earliest_relay_parent().number)); + } } } @@ -781,37 +815,21 @@ fn answer_prospective_validation_data_request( request: ProspectiveValidationDataRequest, tx: oneshot::Sender>, ) { - // 1. Try to get the head-data from the candidate store if known. - // 2. Otherwise, it might exist as the base in some relay-parent and we can find it by iterating - // fragment chains. - // 3. Otherwise, it is unknown. - // 4. Also try to find the relay parent block info by scanning fragment chains. - // 5. If head data and relay parent block info are found - success. Otherwise, failure. - - let storage = match view.candidate_storage.get(&request.para_id) { - None => { - let _ = tx.send(None); - return - }, - Some(s) => s, - }; + // Try getting the needed data from any fragment chain. let (mut head_data, parent_head_data_hash) = match request.parent_head_data { - ParentHeadData::OnlyHash(parent_head_data_hash) => ( - storage.head_data_by_hash(&parent_head_data_hash).map(|x| x.clone()), - parent_head_data_hash, - ), + ParentHeadData::OnlyHash(parent_head_data_hash) => (None, parent_head_data_hash), ParentHeadData::WithData { head_data, hash } => (Some(head_data), hash), }; let mut relay_parent_info = None; let mut max_pov_size = None; - for fragment_chain in view - .active_leaves - .values() - .filter_map(|x| x.fragment_chains.get(&request.para_id)) - { + for fragment_chain in view.active_leaves.iter().filter_map(|x| { + view.per_relay_parent + .get(&x) + .and_then(|data| data.fragment_chains.get(&request.para_id)) + }) { if head_data.is_some() && relay_parent_info.is_some() && max_pov_size.is_some() { break } @@ -819,10 +837,7 @@ fn answer_prospective_validation_data_request( relay_parent_info = fragment_chain.scope().ancestor(&request.candidate_relay_parent); } if head_data.is_none() { - let required_parent = &fragment_chain.scope().base_constraints().required_parent; - if required_parent.hash() == parent_head_data_hash { - head_data = Some(required_parent.clone()); - } + head_data = fragment_chain.get_head_data_by_hash(&parent_head_data_hash); } if max_pov_size.is_none() { let contains_ancestor = @@ -925,7 +940,7 @@ async fn fetch_ancestry( cache: &mut HashMap, relay_hash: Hash, ancestors: usize, -) -> JfyiErrorResult> { +) -> JfyiErrorResult> { if ancestors == 0 { return Ok(Vec::new()) } @@ -1004,12 +1019,13 @@ async fn fetch_block_info( ctx: &mut Context, cache: &mut HashMap, relay_hash: Hash, -) -> JfyiErrorResult> { +) -> JfyiErrorResult> { let header = fetch_block_header_with_cache(ctx, cache, relay_hash).await?; - Ok(header.map(|header| RelayChainBlockInfo { + Ok(header.map(|header| BlockInfo { hash: relay_hash, number: header.number, + parent_hash: header.parent_hash, storage_root: header.state_root, })) } diff --git a/polkadot/node/core/prospective-parachains/src/metrics.rs b/polkadot/node/core/prospective-parachains/src/metrics.rs index 5abd9f56f306..78561bc878ac 100644 --- a/polkadot/node/core/prospective-parachains/src/metrics.rs +++ b/polkadot/node/core/prospective-parachains/src/metrics.rs @@ -17,15 +17,18 @@ use polkadot_node_subsystem::prometheus::Opts; use polkadot_node_subsystem_util::metrics::{ self, - prometheus::{self, GaugeVec, U64}, + prometheus::{self, Gauge, GaugeVec, U64}, }; #[derive(Clone)] pub(crate) struct MetricsInner { - prune_view_candidate_storage: prometheus::Histogram, - introduce_seconded_candidate: prometheus::Histogram, - hypothetical_membership: prometheus::Histogram, - candidate_storage_count: prometheus::GaugeVec, + time_active_leaves_update: prometheus::Histogram, + time_introduce_seconded_candidate: prometheus::Histogram, + time_candidate_backed: prometheus::Histogram, + time_hypothetical_membership: prometheus::Histogram, + candidate_count: prometheus::GaugeVec, + active_leaves_count: prometheus::GaugeVec, + implicit_view_candidate_count: prometheus::Gauge, } /// Candidate backing metrics. @@ -33,13 +36,11 @@ pub(crate) struct MetricsInner { pub struct Metrics(pub(crate) Option); impl Metrics { - /// Provide a timer for handling `prune_view_candidate_storage` which observes on drop. - pub fn time_prune_view_candidate_storage( + /// Provide a timer for handling `ActiveLeavesUpdate` which observes on drop. + pub fn time_handle_active_leaves_update( &self, ) -> Option { - self.0 - .as_ref() - .map(|metrics| metrics.prune_view_candidate_storage.start_timer()) + self.0.as_ref().map(|metrics| metrics.time_active_leaves_update.start_timer()) } /// Provide a timer for handling `IntroduceSecondedCandidate` which observes on drop. @@ -48,31 +49,47 @@ impl Metrics { ) -> Option { self.0 .as_ref() - .map(|metrics| metrics.introduce_seconded_candidate.start_timer()) + .map(|metrics| metrics.time_introduce_seconded_candidate.start_timer()) + } + + /// Provide a timer for handling `CandidateBacked` which observes on drop. + pub fn time_candidate_backed(&self) -> Option { + self.0.as_ref().map(|metrics| metrics.time_candidate_backed.start_timer()) } /// Provide a timer for handling `GetHypotheticalMembership` which observes on drop. pub fn time_hypothetical_membership_request( &self, ) -> Option { - self.0.as_ref().map(|metrics| metrics.hypothetical_membership.start_timer()) + self.0 + .as_ref() + .map(|metrics| metrics.time_hypothetical_membership.start_timer()) } - /// Record the size of the candidate storage. First param is the connected candidates count, - /// second param is the unconnected candidates count. - pub fn record_candidate_storage_size(&self, connected_count: u64, unconnected_count: u64) { + /// Record number of candidates across all fragment chains. First param is the connected + /// candidates count, second param is the unconnected candidates count. + pub fn record_candidate_count(&self, connected_count: u64, unconnected_count: u64) { self.0.as_ref().map(|metrics| { + metrics.candidate_count.with_label_values(&["connected"]).set(connected_count); metrics - .candidate_storage_count - .with_label_values(&["connected"]) - .set(connected_count) + .candidate_count + .with_label_values(&["unconnected"]) + .set(unconnected_count); }); + } + /// Record the number of candidates present in the implicit view of the subsystem. + pub fn record_candidate_count_in_implicit_view(&self, count: u64) { self.0.as_ref().map(|metrics| { - metrics - .candidate_storage_count - .with_label_values(&["unconnected"]) - .set(unconnected_count) + metrics.implicit_view_candidate_count.set(count); + }); + } + + /// Record the number of active/inactive leaves kept by the subsystem. + pub fn record_leaves_count(&self, active_count: u64, inactive_count: u64) { + self.0.as_ref().map(|metrics| { + metrics.active_leaves_count.with_label_values(&["active"]).set(active_count); + metrics.active_leaves_count.with_label_values(&["inactive"]).set(inactive_count); }); } } @@ -80,37 +97,61 @@ impl Metrics { impl metrics::Metrics for Metrics { fn try_register(registry: &prometheus::Registry) -> Result { let metrics = MetricsInner { - prune_view_candidate_storage: prometheus::register( + time_active_leaves_update: prometheus::register( prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_prospective_parachains_prune_view_candidate_storage", - "Time spent within `prospective_parachains::prune_view_candidate_storage`", + "polkadot_parachain_prospective_parachains_time_active_leaves_update", + "Time spent within `prospective_parachains::handle_active_leaves_update`", ))?, registry, )?, - introduce_seconded_candidate: prometheus::register( + time_introduce_seconded_candidate: prometheus::register( prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_prospective_parachains_introduce_seconded_candidate", + "polkadot_parachain_prospective_parachains_time_introduce_seconded_candidate", "Time spent within `prospective_parachains::handle_introduce_seconded_candidate`", ))?, registry, )?, - hypothetical_membership: prometheus::register( + time_candidate_backed: prometheus::register( prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_prospective_parachains_hypothetical_membership", + "polkadot_parachain_prospective_parachains_time_candidate_backed", + "Time spent within `prospective_parachains::handle_candidate_backed`", + ))?, + registry, + )?, + time_hypothetical_membership: prometheus::register( + prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( + "polkadot_parachain_prospective_parachains_time_hypothetical_membership", "Time spent responding to `GetHypotheticalMembership`", ))?, registry, )?, - candidate_storage_count: prometheus::register( + candidate_count: prometheus::register( GaugeVec::new( Opts::new( - "polkadot_parachain_prospective_parachains_candidate_storage_count", - "Number of candidates present in the candidate storage, split by connected and unconnected" + "polkadot_parachain_prospective_parachains_candidate_count", + "Number of candidates present across all fragment chains, split by connected and unconnected" ), &["type"], )?, registry, )?, + active_leaves_count: prometheus::register( + GaugeVec::new( + Opts::new( + "polkadot_parachain_prospective_parachains_active_leaves_count", + "Number of leaves kept by the subsystem, split by active/inactive" + ), + &["type"], + )?, + registry, + )?, + implicit_view_candidate_count: prometheus::register( + Gauge::new( + "polkadot_parachain_prospective_parachains_implicit_view_candidate_count", + "Number of candidates present in the implicit view" + )?, + registry + )?, }; Ok(Metrics(Some(metrics))) } diff --git a/polkadot/node/core/prospective-parachains/src/tests.rs b/polkadot/node/core/prospective-parachains/src/tests.rs index 221fbf4c4e60..14a093239e8e 100644 --- a/polkadot/node/core/prospective-parachains/src/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/tests.rs @@ -111,6 +111,8 @@ fn get_parent_hash(hash: Hash) -> Hash { fn test_harness>( test: impl FnOnce(VirtualOverseer) -> T, ) -> View { + sp_tracing::init_for_tests(); + let pool = sp_core::testing::TaskExecutor::new(); let (mut context, virtual_overseer) = @@ -203,6 +205,32 @@ async fn activate_leaf( activate_leaf_with_params(virtual_overseer, leaf, test_state, ASYNC_BACKING_PARAMETERS).await; } +async fn activate_leaf_with_parent_hash_fn( + virtual_overseer: &mut VirtualOverseer, + leaf: &TestLeaf, + test_state: &TestState, + parent_hash_fn: impl Fn(Hash) -> Hash, +) { + let TestLeaf { number, hash, .. } = leaf; + + let activated = new_leaf(*hash, *number); + + virtual_overseer + .send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work( + activated, + )))) + .await; + + handle_leaf_activation( + virtual_overseer, + leaf, + test_state, + ASYNC_BACKING_PARAMETERS, + parent_hash_fn, + ) + .await; +} + async fn activate_leaf_with_params( virtual_overseer: &mut VirtualOverseer, leaf: &TestLeaf, @@ -219,7 +247,14 @@ async fn activate_leaf_with_params( )))) .await; - handle_leaf_activation(virtual_overseer, leaf, test_state, async_backing_params).await; + handle_leaf_activation( + virtual_overseer, + leaf, + test_state, + async_backing_params, + get_parent_hash, + ) + .await; } async fn handle_leaf_activation( @@ -227,6 +262,7 @@ async fn handle_leaf_activation( leaf: &TestLeaf, test_state: &TestState, async_backing_params: AsyncBackingParams, + parent_hash_fn: impl Fn(Hash) -> Hash, ) { let TestLeaf { number, hash, para_data } = leaf; @@ -281,7 +317,7 @@ async fn handle_leaf_activation( let min_min = para_data.iter().map(|(_, data)| data.min_relay_parent).min().unwrap_or(*number); let ancestry_len = number - min_min; let ancestry_hashes: Vec = - std::iter::successors(Some(*hash), |h| Some(get_parent_hash(*h))) + std::iter::successors(Some(*hash), |h| Some(parent_hash_fn(*h))) .skip(1) .take(ancestry_len as usize) .collect(); @@ -307,16 +343,20 @@ async fn handle_leaf_activation( ); } + let mut used_relay_parents = HashSet::new(); for (hash, number) in ancestry_iter { - send_block_header(virtual_overseer, hash, number).await; - assert_matches!( - virtual_overseer.recv().await, - AllMessages::RuntimeApi( - RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx)) - ) if parent == hash => { - tx.send(Ok(1)).unwrap(); - } - ); + if !used_relay_parents.contains(&hash) { + send_block_header(virtual_overseer, hash, number).await; + assert_matches!( + virtual_overseer.recv().await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx)) + ) if parent == hash => { + tx.send(Ok(1)).unwrap(); + } + ); + used_relay_parents.insert(hash); + } } let paras: HashSet<_> = test_state.claim_queue.values().flatten().collect(); @@ -353,12 +393,16 @@ async fn handle_leaf_activation( ); for pending in pending_availability { - send_block_header( - virtual_overseer, - pending.descriptor.relay_parent, - pending.relay_parent_number, - ) - .await; + if !used_relay_parents.contains(&pending.descriptor.relay_parent) { + send_block_header( + virtual_overseer, + pending.descriptor.relay_parent, + pending.relay_parent_number, + ) + .await; + + used_relay_parents.insert(pending.descriptor.relay_parent); + } } } @@ -513,6 +557,26 @@ async fn get_pvd( assert_eq!(resp, expected_pvd); } +macro_rules! make_and_back_candidate { + ($test_state:ident, $virtual_overseer:ident, $leaf:ident, $parent:expr, $index:expr) => {{ + let (mut candidate, pvd) = make_candidate( + $leaf.hash, + $leaf.number, + 1.into(), + $parent.commitments.head_data.clone(), + HeadData(vec![$index]), + $test_state.validation_code_hash, + ); + // Set a field to make this candidate unique. + candidate.descriptor.para_head = Hash::from_low_u64_le($index); + let candidate_hash = candidate.hash(); + introduce_seconded_candidate(&mut $virtual_overseer, candidate.clone(), pvd).await; + back_candidate(&mut $virtual_overseer, &candidate, candidate_hash).await; + + (candidate, candidate_hash) + }}; +} + #[test] fn should_do_no_work_if_async_backing_disabled_for_leaf() { async fn activate_leaf_async_backing_disabled(virtual_overseer: &mut VirtualOverseer) { @@ -542,7 +606,6 @@ fn should_do_no_work_if_async_backing_disabled_for_leaf() { }); assert!(view.active_leaves.is_empty()); - assert!(view.candidate_storage.is_empty()); } // Send some candidates and make sure all are found: @@ -718,10 +781,6 @@ fn introduce_candidates_basic() { }); assert_eq!(view.active_leaves.len(), 3); - assert_eq!(view.candidate_storage.len(), 2); - // Two parents and two candidates per para. - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (2, 2)); } #[test] @@ -766,28 +825,36 @@ fn introduce_candidate_multiple_times() { 1.into(), Ancestors::default(), 5, - response_a, + response_a.clone(), ) .await; - // Introduce the same candidate multiple times. It'll return true but it won't be added. - // We'll check below that the candidate count remains 1. + // Introduce the same candidate multiple times. It'll return true but it will only be added + // once. for _ in 0..5 { introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) .await; } + // Check candidate tree membership. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + Ancestors::default(), + 5, + response_a, + ) + .await; + virtual_overseer }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (1, 1)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } #[test] -fn fragment_chain_length_is_bounded() { +fn fragment_chain_best_chain_length_is_bounded() { let test_state = TestState::default(); let view = test_harness(|mut virtual_overseer| async move { // Leaf A @@ -835,12 +902,11 @@ fn fragment_chain_length_is_bounded() { ); // Introduce candidates A and B. Since max depth is 1, only these two will be allowed. - introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) - .await; - introduce_seconded_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b.clone()) - .await; + introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await; + introduce_seconded_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await; - // Back candidates. Otherwise, we cannot check membership with GetBackableCandidates. + // Back candidates. Otherwise, we cannot check membership with GetBackableCandidates and + // they won't be part of the best chain. back_candidate(&mut virtual_overseer, &candidate_a, candidate_a.hash()).await; back_candidate(&mut virtual_overseer, &candidate_b, candidate_b.hash()).await; @@ -855,103 +921,25 @@ fn fragment_chain_length_is_bounded() { ) .await; - // Introducing C will fail. - introduce_seconded_candidate_failed(&mut virtual_overseer, candidate_c, pvd_c.clone()) - .await; - - virtual_overseer - }); - - assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); -} - -#[test] -fn unconnected_candidate_count_is_bounded() { - let test_state = TestState::default(); - let view = test_harness(|mut virtual_overseer| async move { - // Leaf A - let leaf_a = TestLeaf { - number: 100, - hash: Hash::from_low_u64_be(130), - para_data: vec![ - (1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))), - (2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))), - ], - }; - // Activate leaves. - activate_leaf_with_params( - &mut virtual_overseer, - &leaf_a, - &test_state, - AsyncBackingParams { max_candidate_depth: 1, allowed_ancestry_len: 3 }, - ) - .await; - - // Candidates A, B and C are all potential candidates but don't form a chain. - let (candidate_a, pvd_a) = make_candidate( - leaf_a.hash, - leaf_a.number, - 1.into(), - HeadData(vec![1]), - HeadData(vec![2]), - test_state.validation_code_hash, - ); - let (candidate_b, pvd_b) = make_candidate( - leaf_a.hash, - leaf_a.number, - 1.into(), - HeadData(vec![3]), - HeadData(vec![4]), - test_state.validation_code_hash, - ); - let (candidate_c, pvd_c) = make_candidate( - leaf_a.hash, - leaf_a.number, - 1.into(), - HeadData(vec![4]), - HeadData(vec![5]), - test_state.validation_code_hash, - ); - - // Introduce candidates A and B. Although max depth is 1 (which should allow for two - // candidates), only 1 is allowed, because the last candidate must be a connected candidate. - introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) - .await; - introduce_seconded_candidate_failed( - &mut virtual_overseer, - candidate_b.clone(), - pvd_b.clone(), - ) - .await; - - // Back candidates. Otherwise, we cannot check membership with GetBackableCandidates. - back_candidate(&mut virtual_overseer, &candidate_a, candidate_a.hash()).await; + // Introducing C will not fail. It will be kept as unconnected storage. + introduce_seconded_candidate(&mut virtual_overseer, candidate_c.clone(), pvd_c).await; + // When being backed, candidate C will be dropped. + back_candidate(&mut virtual_overseer, &candidate_c, candidate_c.hash()).await; - // Check candidate tree membership. Should be empty. get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), Ancestors::default(), 5, - vec![], + vec![(candidate_a.hash(), leaf_a.hash), (candidate_b.hash(), leaf_a.hash)], ) .await; - // Introducing C will also fail. - introduce_seconded_candidate_failed(&mut virtual_overseer, candidate_c, pvd_c.clone()) - .await; - virtual_overseer }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (1, 1)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } // Send some candidates, check if the candidate won't be found once its relay parent leaves the @@ -1178,7 +1166,6 @@ fn introduce_candidate_parent_leaving_view() { }); assert_eq!(view.active_leaves.len(), 0); - assert_eq!(view.candidate_storage.len(), 0); } // Introduce a candidate to multiple forks, see how the membership is returned. @@ -1249,13 +1236,12 @@ fn introduce_candidate_on_multiple_forks() { }); assert_eq!(view.active_leaves.len(), 2); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (1, 1)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } #[test] fn unconnected_candidates_become_connected() { + // This doesn't test all the complicated cases with many unconnected candidates, as it's more + // extensively tested in the `fragment_chain::tests` module. let test_state = TestState::default(); let view = test_harness(|mut virtual_overseer| async move { // Leaf A @@ -1351,9 +1337,6 @@ fn unconnected_candidates_become_connected() { }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (4, 4)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } // Backs some candidates and tests `GetBackableCandidates` when requesting a single candidate. @@ -1435,6 +1418,10 @@ fn check_backable_query_single_candidate() { back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await; back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await; + // Back an unknown candidate. It doesn't return anything but it's ignored. Will not have any + // effect on the backable candidates. + back_candidate(&mut virtual_overseer, &candidate_b, CandidateHash(Hash::random())).await; + // Should not get any backable candidates for the other para. get_backable_candidates( &mut virtual_overseer, @@ -1490,35 +1477,11 @@ fn check_backable_query_single_candidate() { }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - // Two parents and two candidates on para 1. - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } // Backs some candidates and tests `GetBackableCandidates` when requesting a multiple candidates. #[test] fn check_backable_query_multiple_candidates() { - macro_rules! make_and_back_candidate { - ($test_state:ident, $virtual_overseer:ident, $leaf:ident, $parent:expr, $index:expr) => {{ - let (mut candidate, pvd) = make_candidate( - $leaf.hash, - $leaf.number, - 1.into(), - $parent.commitments.head_data.clone(), - HeadData(vec![$index]), - $test_state.validation_code_hash, - ); - // Set a field to make this candidate unique. - candidate.descriptor.para_head = Hash::from_low_u64_le($index); - let candidate_hash = candidate.hash(); - introduce_seconded_candidate(&mut $virtual_overseer, candidate.clone(), pvd).await; - back_candidate(&mut $virtual_overseer, &candidate, candidate_hash).await; - - (candidate, candidate_hash) - }}; - } - let test_state = TestState::default(); let view = test_harness(|mut virtual_overseer| async move { // Leaf A @@ -1786,10 +1749,6 @@ fn check_backable_query_multiple_candidates() { }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - // 4 candidates on para 1. - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (4, 4)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } // Test hypothetical membership query. @@ -1885,11 +1844,13 @@ fn check_hypothetical_membership_query() { introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) .await; - // Get membership of candidates after adding A. C is not a potential candidate because we - // may only add one more candidate, which must be a connected candidate. - for (candidate, pvd) in - [(candidate_a.clone(), pvd_a.clone()), (candidate_b.clone(), pvd_b.clone())] - { + // Get membership of candidates after adding A. They all are still unconnected candidates + // (not part of the best backable chain). + for (candidate, pvd) in [ + (candidate_a.clone(), pvd_a.clone()), + (candidate_b.clone(), pvd_b.clone()), + (candidate_c.clone(), pvd_c.clone()), + ] { get_hypothetical_membership( &mut virtual_overseer, candidate.hash(), @@ -1900,14 +1861,24 @@ fn check_hypothetical_membership_query() { .await; } - get_hypothetical_membership( - &mut virtual_overseer, - candidate_c.hash(), - candidate_c.clone(), - pvd_c.clone(), - vec![], - ) - .await; + // Back A. Now A is part of the best chain the rest can be added as unconnected. + + back_candidate(&mut virtual_overseer, &candidate_a, candidate_a.hash()).await; + + for (candidate, pvd) in [ + (candidate_a.clone(), pvd_a.clone()), + (candidate_b.clone(), pvd_b.clone()), + (candidate_c.clone(), pvd_c.clone()), + ] { + get_hypothetical_membership( + &mut virtual_overseer, + candidate.hash(), + candidate, + pvd, + vec![leaf_a.hash, leaf_b.hash], + ) + .await; + } // Candidate D has invalid relay parent. let (candidate_d, pvd_d) = make_candidate( @@ -1920,14 +1891,17 @@ fn check_hypothetical_membership_query() { ); introduce_seconded_candidate_failed(&mut virtual_overseer, candidate_d, pvd_d).await; - // Add candidate B. + // Add candidate B and back it. introduce_seconded_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b.clone()) .await; + back_candidate(&mut virtual_overseer, &candidate_b, candidate_b.hash()).await; // Get membership of candidates after adding B. - for (candidate, pvd) in - [(candidate_a.clone(), pvd_a.clone()), (candidate_b.clone(), pvd_b.clone())] - { + for (candidate, pvd) in [ + (candidate_a.clone(), pvd_a.clone()), + (candidate_b.clone(), pvd_b.clone()), + (candidate_c.clone(), pvd_c.clone()), + ] { get_hypothetical_membership( &mut virtual_overseer, candidate.hash(), @@ -1938,24 +1912,10 @@ fn check_hypothetical_membership_query() { .await; } - get_hypothetical_membership( - &mut virtual_overseer, - candidate_c.hash(), - candidate_c.clone(), - pvd_c.clone(), - vec![], - ) - .await; - - // Add candidate C. It will fail because we have enough candidates for the configured depth. - introduce_seconded_candidate_failed(&mut virtual_overseer, candidate_c, pvd_c).await; - virtual_overseer }); assert_eq!(view.active_leaves.len(), 2); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2)); } #[test] @@ -2005,6 +1965,16 @@ fn check_pvd_query() { test_state.validation_code_hash, ); + // Candidate E. + let (candidate_e, pvd_e) = make_candidate( + leaf_a.hash, + leaf_a.number, + 1.into(), + HeadData(vec![5]), + HeadData(vec![6]), + test_state.validation_code_hash, + ); + // Get pvd of candidate A before adding it. get_pvd( &mut virtual_overseer, @@ -2067,20 +2037,20 @@ fn check_pvd_query() { introduce_seconded_candidate(&mut virtual_overseer, candidate_c, pvd_c.clone()).await; // Get pvd of candidate C after adding it. - get_pvd( - &mut virtual_overseer, - 1.into(), - leaf_a.hash, - HeadData(vec![2]), - Some(pvd_c.clone()), - ) - .await; + get_pvd(&mut virtual_overseer, 1.into(), leaf_a.hash, HeadData(vec![2]), Some(pvd_c)).await; + + // Get pvd of candidate E before adding it. It won't be found, as we don't have its parent. + get_pvd(&mut virtual_overseer, 1.into(), leaf_a.hash, HeadData(vec![5]), None).await; + + // Add candidate E and check again. Should succeed this time. + introduce_seconded_candidate(&mut virtual_overseer, candidate_e, pvd_e.clone()).await; + + get_pvd(&mut virtual_overseer, 1.into(), leaf_a.hash, HeadData(vec![5]), Some(pvd_e)).await; virtual_overseer }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); } // Test simultaneously activating and deactivating leaves, and simultaneously deactivating @@ -2150,6 +2120,7 @@ fn correctly_updates_leaves(#[case] runtime_api_version: u32) { &leaf_c, &test_state, ASYNC_BACKING_PARAMETERS, + get_parent_hash, ) .await; @@ -2171,13 +2142,6 @@ fn correctly_updates_leaves(#[case] runtime_api_version: u32) { virtual_overseer .send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update))) .await; - handle_leaf_activation( - &mut virtual_overseer, - &leaf_a, - &test_state, - ASYNC_BACKING_PARAMETERS, - ) - .await; // Remove the leaf again. Send some unnecessary hashes. let update = ActiveLeavesUpdate { @@ -2192,7 +2156,322 @@ fn correctly_updates_leaves(#[case] runtime_api_version: u32) { }); assert_eq!(view.active_leaves.len(), 0); - assert_eq!(view.candidate_storage.len(), 0); +} + +#[test] +fn handle_active_leaves_update_gets_candidates_from_parent() { + let para_id = ParaId::from(1); + let mut test_state = TestState::default(); + test_state.claim_queue = test_state + .claim_queue + .into_iter() + .filter(|(_, paras)| matches!(paras.front(), Some(para) if para == ¶_id)) + .collect(); + assert_eq!(test_state.claim_queue.len(), 1); + let view = test_harness(|mut virtual_overseer| async move { + // Leaf A + let leaf_a = TestLeaf { + number: 100, + hash: Hash::from_low_u64_be(130), + para_data: vec![(para_id, PerParaData::new(97, HeadData(vec![1, 2, 3])))], + }; + // Activate leaf A. + activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await; + + // Candidates A, B, C and D all form a chain + let (candidate_a, pvd_a) = make_candidate( + leaf_a.hash, + leaf_a.number, + para_id, + HeadData(vec![1, 2, 3]), + HeadData(vec![1]), + test_state.validation_code_hash, + ); + let candidate_hash_a = candidate_a.hash(); + introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await; + back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await; + + let (candidate_b, candidate_hash_b) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_a, 2); + let (candidate_c, candidate_hash_c) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_b, 3); + let (candidate_d, candidate_hash_d) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_c, 4); + + let mut all_candidates_resp = vec![ + (candidate_hash_a, leaf_a.hash), + (candidate_hash_b, leaf_a.hash), + (candidate_hash_c, leaf_a.hash), + (candidate_hash_d, leaf_a.hash), + ]; + + // Check candidate tree membership. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + para_id, + Ancestors::default(), + 5, + all_candidates_resp.clone(), + ) + .await; + + // Activate leaf B, which makes candidates A and B pending availability. + // Leaf B + let leaf_b = TestLeaf { + number: 101, + hash: Hash::from_low_u64_be(129), + para_data: vec![( + para_id, + PerParaData::new_with_pending( + 98, + HeadData(vec![1, 2, 3]), + vec![ + CandidatePendingAvailability { + candidate_hash: candidate_a.hash(), + descriptor: candidate_a.descriptor.clone(), + commitments: candidate_a.commitments.clone(), + relay_parent_number: leaf_a.number, + max_pov_size: MAX_POV_SIZE, + }, + CandidatePendingAvailability { + candidate_hash: candidate_b.hash(), + descriptor: candidate_b.descriptor.clone(), + commitments: candidate_b.commitments.clone(), + relay_parent_number: leaf_a.number, + max_pov_size: MAX_POV_SIZE, + }, + ], + ), + )], + }; + // Activate leaf B. + activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + Ancestors::default(), + 5, + vec![], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + [candidate_a.hash(), candidate_b.hash()].into_iter().collect(), + 5, + vec![(candidate_c.hash(), leaf_a.hash), (candidate_d.hash(), leaf_a.hash)], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + Ancestors::default(), + 5, + vec![], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + para_id, + Ancestors::default(), + 5, + all_candidates_resp.clone(), + ) + .await; + + // Now deactivate leaf A. + deactivate_leaf(&mut virtual_overseer, leaf_a.hash).await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + Ancestors::default(), + 5, + vec![], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + [candidate_a.hash(), candidate_b.hash()].into_iter().collect(), + 5, + vec![(candidate_c.hash(), leaf_a.hash), (candidate_d.hash(), leaf_a.hash)], + ) + .await; + + // Now add leaf C, which will be a sibling (fork) of leaf B. It should also inherit the + // candidates of leaf A (their common parent). + let leaf_c = TestLeaf { + number: 101, + hash: Hash::from_low_u64_be(12), + para_data: vec![( + para_id, + PerParaData::new_with_pending(98, HeadData(vec![1, 2, 3]), vec![]), + )], + }; + + activate_leaf_with_parent_hash_fn(&mut virtual_overseer, &leaf_c, &test_state, |hash| { + if hash == leaf_c.hash { + leaf_a.hash + } else { + get_parent_hash(hash) + } + }) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + [candidate_a.hash(), candidate_b.hash()].into_iter().collect(), + 5, + vec![(candidate_c.hash(), leaf_a.hash), (candidate_d.hash(), leaf_a.hash)], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_c, + para_id, + Ancestors::new(), + 5, + all_candidates_resp.clone(), + ) + .await; + + // Deactivate C and add another candidate that will be present on the deactivated parent A. + // When activating C again it should also get the new candidate. Deactivated leaves are + // still updated with new candidates. + deactivate_leaf(&mut virtual_overseer, leaf_c.hash).await; + + let (candidate_e, _) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_d, 5); + activate_leaf_with_parent_hash_fn(&mut virtual_overseer, &leaf_c, &test_state, |hash| { + if hash == leaf_c.hash { + leaf_a.hash + } else { + get_parent_hash(hash) + } + }) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + [candidate_a.hash(), candidate_b.hash()].into_iter().collect(), + 5, + vec![ + (candidate_c.hash(), leaf_a.hash), + (candidate_d.hash(), leaf_a.hash), + (candidate_e.hash(), leaf_a.hash), + ], + ) + .await; + + all_candidates_resp.push((candidate_e.hash(), leaf_a.hash)); + get_backable_candidates( + &mut virtual_overseer, + &leaf_c, + para_id, + Ancestors::new(), + 5, + all_candidates_resp, + ) + .await; + + // Querying the backable candidates for deactivated leaf won't work. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + para_id, + Ancestors::new(), + 5, + vec![], + ) + .await; + + virtual_overseer + }); + + assert_eq!(view.active_leaves.len(), 2); + assert_eq!(view.per_relay_parent.len(), 3); +} + +#[test] +fn handle_active_leaves_update_bounded_implicit_view() { + let para_id = ParaId::from(1); + let mut test_state = TestState::default(); + test_state.claim_queue = test_state + .claim_queue + .into_iter() + .filter(|(_, paras)| matches!(paras.front(), Some(para) if para == ¶_id)) + .collect(); + assert_eq!(test_state.claim_queue.len(), 1); + + let mut leaves = vec![TestLeaf { + number: 100, + hash: Hash::from_low_u64_be(130), + para_data: vec![( + para_id, + PerParaData::new(100 - ALLOWED_ANCESTRY_LEN, HeadData(vec![1, 2, 3])), + )], + }]; + + for index in 1..10 { + let prev_leaf = &leaves[index - 1]; + leaves.push(TestLeaf { + number: prev_leaf.number - 1, + hash: get_parent_hash(prev_leaf.hash), + para_data: vec![( + para_id, + PerParaData::new( + prev_leaf.number - 1 - ALLOWED_ANCESTRY_LEN, + HeadData(vec![1, 2, 3]), + ), + )], + }); + } + leaves.reverse(); + + let view = test_harness(|mut virtual_overseer| async { + // Activate first 10 leaves. + for leaf in &leaves[0..10] { + activate_leaf(&mut virtual_overseer, leaf, &test_state).await; + } + + // Now deactivate first 9 leaves. + for leaf in &leaves[0..9] { + deactivate_leaf(&mut virtual_overseer, leaf.hash).await; + } + + virtual_overseer + }); + + // Only latest leaf is active. + assert_eq!(view.active_leaves.len(), 1); + // We keep allowed_ancestry_len implicit leaves. The latest leaf is also present here. + assert_eq!( + view.per_relay_parent.len() as u32, + ASYNC_BACKING_PARAMETERS.allowed_ancestry_len + 1 + ); + + assert_eq!(view.active_leaves, [leaves[9].hash].into_iter().collect()); + assert_eq!( + view.per_relay_parent.into_keys().collect::>(), + leaves[6..].into_iter().map(|l| l.hash).collect::>() + ); } #[test] @@ -2251,7 +2530,8 @@ fn persists_pending_availability_candidate() { ); let candidate_hash_b = candidate_b.hash(); - introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await; + introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) + .await; back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await; let candidate_a_pending_av = CandidatePendingAvailability { @@ -2275,6 +2555,15 @@ fn persists_pending_availability_candidate() { }; activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await; + get_hypothetical_membership( + &mut virtual_overseer, + candidate_hash_a, + candidate_a, + pvd_a, + vec![leaf_a.hash, leaf_b.hash], + ) + .await; + introduce_seconded_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await; back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await; diff --git a/polkadot/node/core/provisioner/src/lib.rs b/polkadot/node/core/provisioner/src/lib.rs index 3f622a60a059..ffc5859b7756 100644 --- a/polkadot/node/core/provisioner/src/lib.rs +++ b/polkadot/node/core/provisioner/src/lib.rs @@ -273,7 +273,7 @@ async fn handle_communication( let span = state.span.child("provisionable-data"); let _timer = metrics.time_provisionable_data(); - gum::trace!(target: LOG_TARGET, ?relay_parent, "Received provisionable data."); + gum::trace!(target: LOG_TARGET, ?relay_parent, "Received provisionable data: {:?}", &data); note_provisionable_data(state, &span, data); } @@ -794,9 +794,11 @@ async fn select_candidates( relay_parent: Hash, sender: &mut impl overseer::ProvisionerSenderTrait, ) -> Result, Error> { - gum::trace!(target: LOG_TARGET, + gum::trace!( + target: LOG_TARGET, leaf_hash=?relay_parent, - "before GetBackedCandidates"); + "before GetBackedCandidates" + ); let selected_candidates = match prospective_parachains_mode { ProspectiveParachainsMode::Enabled { .. } => diff --git a/polkadot/node/network/statement-distribution/src/v2/mod.rs b/polkadot/node/network/statement-distribution/src/v2/mod.rs index 47d350849b20..109c29f520c5 100644 --- a/polkadot/node/network/statement-distribution/src/v2/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/mod.rs @@ -2237,7 +2237,9 @@ async fn fragment_chain_update_inner( // 2. find out which are in the frontier gum::debug!( target: LOG_TARGET, - "Calling getHypotheticalMembership from statement distribution" + active_leaf_hash = ?active_leaf_hash, + "Calling getHypotheticalMembership from statement distribution for candidates: {:?}", + &hypotheticals.iter().map(|hypo| hypo.candidate_hash()).collect::>() ); let candidate_memberships = { let (tx, rx) = oneshot::channel(); diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index 4d27ac9b70e3..d067ca468011 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -43,13 +43,13 @@ use polkadot_node_primitives::{ }; use polkadot_primitives::{ async_backing, slashing, ApprovalVotingParams, AuthorityDiscoveryId, BackedCandidate, - BlockNumber, CandidateEvent, CandidateHash, CandidateIndex, CandidateReceipt, CollatorId, - CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, GroupIndex, - GroupRotationInfo, Hash, HeadData, Header as BlockHeader, Id as ParaId, InboundDownwardMessage, - InboundHrmpMessage, MultiDisputeStatementSet, NodeFeatures, OccupiedCoreAssumption, - PersistedValidationData, PvfCheckStatement, PvfExecKind, SessionIndex, SessionInfo, - SignedAvailabilityBitfield, SignedAvailabilityBitfields, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, CandidateIndex, + CandidateReceipt, CollatorId, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, + ExecutorParams, GroupIndex, GroupRotationInfo, Hash, HeadData, Header as BlockHeader, + Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, MultiDisputeStatementSet, + NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, PvfExecKind, + SessionIndex, SessionInfo, SignedAvailabilityBitfield, SignedAvailabilityBitfields, + ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use polkadot_statement_table::v2::Misbehavior; use std::{ @@ -1126,6 +1126,32 @@ impl HypotheticalCandidate { HypotheticalCandidate::Incomplete { .. } => None, } } + + /// Get the candidate commitments, if the candidate is complete. + pub fn commitments(&self) -> Option<&CandidateCommitments> { + match *self { + HypotheticalCandidate::Complete { ref receipt, .. } => Some(&receipt.commitments), + HypotheticalCandidate::Incomplete { .. } => None, + } + } + + /// Get the persisted validation data, if the candidate is complete. + pub fn persisted_validation_data(&self) -> Option<&PersistedValidationData> { + match *self { + HypotheticalCandidate::Complete { ref persisted_validation_data, .. } => + Some(persisted_validation_data), + HypotheticalCandidate::Incomplete { .. } => None, + } + } + + /// Get the validation code hash, if the candidate is complete. + pub fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + match *self { + HypotheticalCandidate::Complete { ref receipt, .. } => + Some(&receipt.descriptor.validation_code_hash), + HypotheticalCandidate::Incomplete { .. } => None, + } + } } /// Request specifying which candidates are either already included diff --git a/polkadot/node/subsystem-util/src/backing_implicit_view.rs b/polkadot/node/subsystem-util/src/backing_implicit_view.rs index 23a758d25715..a805ef8165e5 100644 --- a/polkadot/node/subsystem-util/src/backing_implicit_view.rs +++ b/polkadot/node/subsystem-util/src/backing_implicit_view.rs @@ -25,6 +25,7 @@ use polkadot_primitives::{BlockNumber, Hash, Id as ParaId}; use std::collections::HashMap; use crate::{ + inclusion_emulator::RelayChainBlockInfo, request_session_index_for_child, runtime::{self, prospective_parachains_mode, recv_runtime, ProspectiveParachainsMode}, }; @@ -121,6 +122,26 @@ struct BlockInfo { parent_hash: Hash, } +/// Information about a relay-chain block, to be used when calling this module from prospective +/// parachains. +#[derive(Debug, Clone, PartialEq)] +pub struct BlockInfoProspectiveParachains { + /// The hash of the relay-chain block. + pub hash: Hash, + /// The hash of the parent relay-chain block. + pub parent_hash: Hash, + /// The number of the relay-chain block. + pub number: BlockNumber, + /// The storage-root of the relay-chain block. + pub storage_root: Hash, +} + +impl From for RelayChainBlockInfo { + fn from(value: BlockInfoProspectiveParachains) -> Self { + Self { hash: value.hash, number: value.number, storage_root: value.storage_root } + } +} + impl View { /// Get an iterator over active leaves in the view. pub fn leaves(&self) -> impl Iterator { @@ -178,6 +199,61 @@ impl View { } } + /// Activate a leaf in the view. To be used by the prospective parachains subsystem. + /// + /// This will not request any additional data, as prospective parachains already provides all + /// the required info. + /// NOTE: using `activate_leaf` instead of this function will result in a + /// deadlock, as it calls prospective-parachains under the hood. + /// + /// No-op for known leaves. + pub fn activate_leaf_from_prospective_parachains( + &mut self, + leaf: BlockInfoProspectiveParachains, + ancestors: &[BlockInfoProspectiveParachains], + ) { + if self.leaves.contains_key(&leaf.hash) { + return + } + + // Retain at least `MINIMUM_RETAIN_LENGTH` blocks in storage. + // This helps to avoid Chain API calls when activating leaves in the + // same chain. + let retain_minimum = std::cmp::min( + ancestors.last().map(|a| a.number).unwrap_or(0), + leaf.number.saturating_sub(MINIMUM_RETAIN_LENGTH), + ); + + self.leaves.insert(leaf.hash, ActiveLeafPruningInfo { retain_minimum }); + let mut allowed_relay_parents = AllowedRelayParents { + allowed_relay_parents_contiguous: Vec::with_capacity(ancestors.len()), + // In this case, initialise this to an empty map, as prospective parachains already has + // this data and it won't query the implicit view for it. + minimum_relay_parents: HashMap::new(), + }; + + for ancestor in ancestors { + self.block_info_storage.insert( + ancestor.hash, + BlockInfo { + block_number: ancestor.number, + maybe_allowed_relay_parents: None, + parent_hash: ancestor.parent_hash, + }, + ); + allowed_relay_parents.allowed_relay_parents_contiguous.push(ancestor.hash); + } + + self.block_info_storage.insert( + leaf.hash, + BlockInfo { + block_number: leaf.number, + maybe_allowed_relay_parents: Some(allowed_relay_parents), + parent_hash: leaf.parent_hash, + }, + ); + } + /// Deactivate a leaf in the view. This prunes any outdated implicit ancestors as well. /// /// Returns hashes of blocks pruned from storage. diff --git a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs index 2272f048089c..0c3b40743495 100644 --- a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs +++ b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs @@ -39,8 +39,8 @@ /// /// # Usage /// -/// It's expected that the users of this module will be building up chains of -/// [`Fragment`]s and consistently pruning and adding to the chains. +/// It's expected that the users of this module will be building up chains or trees of +/// [`Fragment`]s and consistently pruning and adding to them. /// /// ## Operating Constraints /// @@ -56,55 +56,19 @@ /// /// ## Fragment Chains /// -/// For simplicity and practicality, we expect that collators of the same parachain are -/// cooperating and don't create parachain forks or cycles on the same relay chain active leaf. -/// Therefore, higher-level code should maintain one fragment chain for each active leaf (not a -/// fragment tree). If parachains do create forks, their performance in regards to async -/// backing and elastic scaling will suffer, because different validators will have different -/// predictions of the future. +/// For the sake of this module, we don't care how higher-level code is managing parachain +/// fragments, whether or not they're kept as a chain or tree. In reality, +/// prospective-parachains is maintaining for every active leaf, a chain of the "best" backable +/// candidates and a storage of potential candidates which may be added to this chain in the +/// future. /// /// As the relay-chain grows, some predictions come true and others come false. -/// And new predictions get made. These three changes correspond distinctly to the -/// 3 primary operations on fragment chains. +/// And new predictions get made. Higher-level code is responsible for adding and pruning the +/// fragments chains. /// /// Avoiding fragment-chain blowup is beyond the scope of this module. Higher-level must ensure /// proper spam protection. /// -/// ### Pruning Fragment Chains -/// -/// When the relay-chain advances, we want to compare the new constraints of that relay-parent -/// to the root of the fragment chain we have. There are 3 cases: -/// -/// 1. The root fragment is still valid under the new constraints. In this case, we do nothing. -/// This is the "prediction still uncertain" case. (Corresponds to some candidates still -/// being pending availability). -/// -/// 2. The root fragment (potentially along with a number of descendants) is invalid under the -/// new constraints because it has been included by the relay-chain. In this case, we can -/// discard the included chain and split & re-root the chain under its descendants and -/// compare to the new constraints again. This is the "prediction came true" case. -/// -/// 3. The root fragment becomes invalid under the new constraints for any reason (if for -/// example the parachain produced a fork and the block producer picked a different -/// candidate to back). In this case we can discard the entire fragment chain. This is the -/// "prediction came false" case. -/// -/// This is all a bit of a simplification because it assumes that the relay-chain advances -/// without forks and is finalized instantly. In practice, the set of fragment-chains needs to -/// be observable from the perspective of a few different possible forks of the relay-chain and -/// not pruned too eagerly. -/// -/// Note that the fragments themselves don't need to change and the only thing we care about -/// is whether the predictions they represent are still valid. -/// -/// ### Extending Fragment Chains -/// -/// As predictions fade into the past, new ones should be stacked on top. -/// -/// Every new relay-chain block is an opportunity to make a new prediction about the future. -/// Higher-level logic should decide whether to build upon an existing chain or whether -/// to create a new fragment-chain. -/// /// ### Code Upgrades /// /// Code upgrades are the main place where this emulation fails. The on-chain PVF upgrade @@ -116,9 +80,11 @@ /// /// That means a few blocks of execution time lost, which is not a big deal for code upgrades /// in practice at most once every few weeks. +use polkadot_node_subsystem::messages::HypotheticalCandidate; use polkadot_primitives::{ - async_backing::Constraints as PrimitiveConstraints, BlockNumber, CandidateCommitments, Hash, - HeadData, Id as ParaId, PersistedValidationData, UpgradeRestriction, ValidationCodeHash, + async_backing::Constraints as PrimitiveConstraints, BlockNumber, CandidateCommitments, + CandidateHash, Hash, HeadData, Id as ParaId, PersistedValidationData, UpgradeRestriction, + ValidationCodeHash, }; use std::{collections::HashMap, sync::Arc}; @@ -702,6 +668,11 @@ impl Fragment { &self.candidate } + /// Get a cheap ref-counted copy of the underlying prospective candidate. + pub fn candidate_clone(&self) -> Arc { + self.candidate.clone() + } + /// Modifications to constraints based on the outputs of the candidate. pub fn constraint_modifications(&self) -> &ConstraintModifications { &self.modifications @@ -791,6 +762,55 @@ fn validate_against_constraints( .map_err(FragmentValidityError::OutputsInvalid) } +/// Trait for a hypothetical or concrete candidate, as needed when assessing the validity of a +/// potential candidate. +pub trait HypotheticalOrConcreteCandidate { + /// Return a reference to the candidate commitments, if present. + fn commitments(&self) -> Option<&CandidateCommitments>; + /// Return a reference to the persisted validation data, if present. + fn persisted_validation_data(&self) -> Option<&PersistedValidationData>; + /// Return a reference to the validation code hash, if present. + fn validation_code_hash(&self) -> Option<&ValidationCodeHash>; + /// Return the parent head hash. + fn parent_head_data_hash(&self) -> Hash; + /// Return the output head hash, if present. + fn output_head_data_hash(&self) -> Option; + /// Return the relay parent hash. + fn relay_parent(&self) -> Hash; + /// Return the candidate hash. + fn candidate_hash(&self) -> CandidateHash; +} + +impl HypotheticalOrConcreteCandidate for HypotheticalCandidate { + fn commitments(&self) -> Option<&CandidateCommitments> { + self.commitments() + } + + fn persisted_validation_data(&self) -> Option<&PersistedValidationData> { + self.persisted_validation_data() + } + + fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + self.validation_code_hash() + } + + fn parent_head_data_hash(&self) -> Hash { + self.parent_head_data_hash() + } + + fn output_head_data_hash(&self) -> Option { + self.output_head_data_hash() + } + + fn relay_parent(&self) -> Hash { + self.relay_parent() + } + + fn candidate_hash(&self) -> CandidateHash { + self.candidate_hash() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/prdoc/pr_4937.prdoc b/prdoc/pr_4937.prdoc new file mode 100644 index 000000000000..37b7bc3dda59 --- /dev/null +++ b/prdoc/pr_4937.prdoc @@ -0,0 +1,21 @@ +title: "prospective-parachains rework: take II" + +doc: + - audience: Node Dev + description: | + Add back support for backing parachain forks. Once a candidate reaches the backing quorum, + validators use a shared way of picking the winning fork to back on-chain. This was done in + order to increase the likelihood that all backers will vote on the winning fork. + The functionality of backing unconnected candidates introduced by the previous rework is preserved. + +crates: + - name: polkadot-node-core-prospective-parachains + bump: minor + - name: polkadot-node-subsystem-types + bump: minor + - name: polkadot-node-subsystem-util + bump: minor + - name: polkadot-node-core-provisioner + bump: none + - name: polkadot-statement-distribution + bump: none From ebcbca3ff606b22b5eb81bcbfaa9309752d64dde Mon Sep 17 00:00:00 2001 From: jserrat <35823283+Jpserrat@users.noreply.github.com> Date: Mon, 12 Aug 2024 05:40:04 -0300 Subject: [PATCH 088/382] xcm-executor: allow deposit of multiple assets if at least one of them satisfies ED (#4460) Closes #4242 XCM programs that deposit assets to some new (empty) account will now succeed if at least one of the deposited assets satisfies ED. Before this change, the requirement was that the _first_ asset had to satisfy ED, but assets order can be changed during reanchoring so it is not reliable. With this PR, ordering doesn't matter, any one(s) of them can satisfy ED for the whole deposit to work. Kusama address: FkB6QEo8VnV3oifugNj5NeVG3Mvq1zFbrUu4P5YwRoe5mQN --------- Co-authored-by: Adrian Catangiu Co-authored-by: Francisco Aguirre Co-authored-by: command-bot <> --- .../emulated/common/src/lib.rs | 1 + .../tests/assets/asset-hub-rococo/src/lib.rs | 3 +- .../src/tests/reserve_transfer.rs | 20 +- .../tests/assets/asset-hub-westend/src/lib.rs | 3 +- .../src/tests/reserve_transfer.rs | 20 +- .../bridge-hub-rococo/src/tests/snowbridge.rs | 4 +- .../tests/people/people-rococo/src/lib.rs | 1 - .../people-rococo/src/tests/teleport.rs | 21 +- .../tests/people/people-westend/src/lib.rs | 1 - .../people-westend/src/tests/teleport.rs | 21 +- .../xcm/pallet_xcm_benchmarks_fungible.rs | 51 ++- .../xcm/pallet_xcm_benchmarks_fungible.rs | 76 ++--- .../xcm/pallet_xcm_benchmarks_fungible.rs | 42 +-- .../xcm/pallet_xcm_benchmarks_fungible.rs | 74 ++-- .../xcm/pallet_xcm_benchmarks_fungible.rs | 36 +- .../xcm/pallet_xcm_benchmarks_fungible.rs | 36 +- .../people-rococo/src/weights/xcm/mod.rs | 16 +- .../xcm/pallet_xcm_benchmarks_fungible.rs | 203 ++++++----- .../xcm/pallet_xcm_benchmarks_generic.rs | 318 ++++++++---------- .../people-westend/src/weights/xcm/mod.rs | 21 +- .../xcm/pallet_xcm_benchmarks_fungible.rs | 203 ++++++----- .../xcm/pallet_xcm_benchmarks_generic.rs | 318 ++++++++---------- .../xcm/pallet_xcm_benchmarks_fungible.rs | 62 ++-- .../xcm/pallet_xcm_benchmarks_fungible.rs | 70 ++-- polkadot/xcm/xcm-executor/src/lib.rs | 55 ++- prdoc/pr_4460.prdoc | 24 ++ 26 files changed, 869 insertions(+), 831 deletions(-) create mode 100644 prdoc/pr_4460.prdoc diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index 7077fbbb0a9a..b3dec175e112 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -132,6 +132,7 @@ pub mod accounts { pub const EVE_STASH: &str = "Eve//stash"; pub const FERDIE_STASH: &str = "Ferdie//stash"; pub const FERDIE_BEEFY: &str = "Ferdie//stash"; + pub const DUMMY_EMPTY: &str = "JohnDoe"; pub fn init_balances() -> Vec { vec![ diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs index 6309c0584107..1a30fac6ba91 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs @@ -35,7 +35,8 @@ mod imports { // Cumulus pub use asset_test_utils::xcm_helpers; pub use emulated_integration_tests_common::{ - test_parachain_is_trusted_teleporter, + accounts::DUMMY_EMPTY, + get_account_id_from_seed, test_parachain_is_trusted_teleporter, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, TestContext, TestExt, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs index 313fa953dd05..329dbcac4b42 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs @@ -941,6 +941,9 @@ fn reserve_transfer_assets_from_system_para_to_para() { /// Reserve Transfers of a random asset and native asset from Parachain to System Para should /// work +/// Receiver is empty account to show deposit works as long as transfer includes enough DOT for ED. +/// Once we have https://github.com/paritytech/polkadot-sdk/issues/5298, +/// we should do equivalent test with USDT instead of DOT. #[test] fn reserve_transfer_assets_from_para_to_system_para() { // Init values for Parachain @@ -965,24 +968,23 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Fund Parachain's sender account with some foreign assets PenpalA::mint_foreign_asset( penpal_asset_owner_signer.clone(), - asset_location_on_penpal, + asset_location_on_penpal.clone(), sender.clone(), asset_amount_to_send * 2, ); // Fund Parachain's sender account with some system assets PenpalA::mint_foreign_asset( penpal_asset_owner_signer, - system_asset_location_on_penpal, + system_asset_location_on_penpal.clone(), sender.clone(), fee_amount_to_send * 2, ); + // Beneficiary is a new (empty) account + let receiver = get_account_id_from_seed::(DUMMY_EMPTY); // Init values for System Parachain - let receiver = AssetHubRococoReceiver::get(); let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - let system_para_native_asset_location = RelayLocation::get(); - let system_para_foreign_asset_location = PenpalLocalReservableFromAssetHub::get(); let ah_asset_owner = AssetHubRococoAssetOwner::get(); let ah_asset_owner_signer = ::RuntimeOrigin::signed(ah_asset_owner); @@ -1017,11 +1019,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query initial balances let sender_system_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location.clone(), &sender) + >::balance(system_asset_location_on_penpal.clone(), &sender) }); let sender_foreign_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location.clone(), &sender) + >::balance(asset_location_on_penpal.clone(), &sender) }); let receiver_balance_before = test.receiver.balance; let receiver_assets_before = AssetHubRococo::execute_with(|| { @@ -1038,11 +1040,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query final balances let sender_system_assets_after = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location.clone(), &sender) + >::balance(system_asset_location_on_penpal, &sender) }); let sender_foreign_assets_after = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location, &sender) + >::balance(asset_location_on_penpal, &sender) }); let receiver_balance_after = test.receiver.balance; let receiver_assets_after = AssetHubRococo::execute_with(|| { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs index 060c3fb39254..437268a19e59 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs @@ -35,7 +35,8 @@ mod imports { // Cumulus pub use asset_test_utils::xcm_helpers; pub use emulated_integration_tests_common::{ - test_parachain_is_trusted_teleporter, + accounts::DUMMY_EMPTY, + get_account_id_from_seed, test_parachain_is_trusted_teleporter, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, TestContext, TestExt, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs index 82ef74fdab10..729de65382f8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs @@ -942,6 +942,9 @@ fn reserve_transfer_assets_from_system_para_to_para() { /// Reserve Transfers of a random asset and native asset from Parachain to System Para should /// work +/// Receiver is empty account to show deposit works as long as transfer includes enough DOT for ED. +/// Once we have https://github.com/paritytech/polkadot-sdk/issues/5298, +/// we should do equivalent test with USDT instead of DOT. #[test] fn reserve_transfer_assets_from_para_to_system_para() { // Init values for Parachain @@ -966,25 +969,24 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Fund Parachain's sender account with some foreign assets PenpalA::mint_foreign_asset( penpal_asset_owner_signer.clone(), - asset_location_on_penpal, + asset_location_on_penpal.clone(), sender.clone(), asset_amount_to_send * 2, ); // Fund Parachain's sender account with some system assets PenpalA::mint_foreign_asset( penpal_asset_owner_signer, - system_asset_location_on_penpal, + system_asset_location_on_penpal.clone(), sender.clone(), fee_amount_to_send * 2, ); + // Beneficiary is a new (empty) account + let receiver = get_account_id_from_seed::(DUMMY_EMPTY); // Init values for System Parachain - let receiver = AssetHubWestendReceiver::get(); let penpal_location_as_seen_by_ahr = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - let system_para_native_asset_location = RelayLocation::get(); - let system_para_foreign_asset_location = PenpalLocalReservableFromAssetHub::get(); let ah_asset_owner = AssetHubWestendAssetOwner::get(); let ah_asset_owner_signer = ::RuntimeOrigin::signed(ah_asset_owner); @@ -1019,11 +1021,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query initial balances let sender_system_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location.clone(), &sender) + >::balance(system_asset_location_on_penpal.clone(), &sender) }); let sender_foreign_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location.clone(), &sender) + >::balance(asset_location_on_penpal.clone(), &sender) }); let receiver_balance_before = test.receiver.balance; let receiver_assets_before = AssetHubWestend::execute_with(|| { @@ -1040,11 +1042,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query final balances let sender_system_assets_after = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location, &sender) + >::balance(system_asset_location_on_penpal, &sender) }); let sender_foreign_assets_after = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location, &sender) + >::balance(asset_location_on_penpal, &sender) }); let receiver_balance_after = test.receiver.balance; let receiver_assets_after = AssetHubWestend::execute_with(|| { diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 40a1968ec557..3e8d3357caa8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -669,8 +669,8 @@ fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_insufficie #[test] fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_sufficient_fee_but_do_not_satisfy_ed( ) { - // On AH the xcm fee is 33_873_024 and the ED is 3_300_000 - send_token_from_ethereum_to_asset_hub_with_fee([1; 32], 36_000_000); + // On AH the xcm fee is 26_789_690 and the ED is 3_300_000 + send_token_from_ethereum_to_asset_hub_with_fee([1; 32], 30_000_000); AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs index 3c0533f775e2..b725c24fbea3 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs @@ -20,7 +20,6 @@ mod imports { // Substrate pub use frame_support::{ assert_ok, - pallet_prelude::Weight, sp_runtime::{AccountId32, DispatchResult}, traits::fungibles::Inspect, }; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs index 4410d1bd40dc..c28b305b2c94 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs @@ -17,7 +17,7 @@ use crate::imports::*; fn relay_origin_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; - Rococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(627_959_000, 7_200))); + Rococo::assert_xcm_pallet_attempted_complete(None); assert_expected_events!( Rococo, @@ -39,11 +39,7 @@ fn relay_origin_assertions(t: RelayToSystemParaTest) { fn relay_dest_assertions(t: SystemParaToRelayTest) { type RuntimeEvent = ::RuntimeEvent; - Rococo::assert_ump_queue_processed( - true, - Some(PeopleRococo::para_id()), - Some(Weight::from_parts(304_266_000, 7_186)), - ); + Rococo::assert_ump_queue_processed(true, Some(PeopleRococo::para_id()), None); assert_expected_events!( Rococo, @@ -62,20 +58,13 @@ fn relay_dest_assertions(t: SystemParaToRelayTest) { } fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) { - Rococo::assert_ump_queue_processed( - false, - Some(PeopleRococo::para_id()), - Some(Weight::from_parts(157_718_000, 3_593)), - ); + Rococo::assert_ump_queue_processed(false, Some(PeopleRococo::para_id()), None); } fn para_origin_assertions(t: SystemParaToRelayTest) { type RuntimeEvent = ::RuntimeEvent; - PeopleRococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts( - 600_000_000, - 7_000, - ))); + PeopleRococo::assert_xcm_pallet_attempted_complete(None); PeopleRococo::assert_parachain_system_ump_sent(); @@ -94,7 +83,7 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { fn para_dest_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; - PeopleRococo::assert_dmp_queue_complete(Some(Weight::from_parts(162_456_000, 0))); + PeopleRococo::assert_dmp_queue_complete(None); assert_expected_events!( PeopleRococo, diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs index 689409fe5040..386a1b91c85b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs @@ -19,7 +19,6 @@ mod imports { // Substrate pub use frame_support::{ assert_ok, - pallet_prelude::Weight, sp_runtime::{AccountId32, DispatchResult}, traits::fungibles::Inspect, }; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs index 6fd3cdeb61fb..294583399011 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs @@ -17,7 +17,7 @@ use crate::imports::*; fn relay_origin_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; - Westend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(627_959_000, 7_200))); + Westend::assert_xcm_pallet_attempted_complete(None); assert_expected_events!( Westend, @@ -39,11 +39,7 @@ fn relay_origin_assertions(t: RelayToSystemParaTest) { fn relay_dest_assertions(t: SystemParaToRelayTest) { type RuntimeEvent = ::RuntimeEvent; - Westend::assert_ump_queue_processed( - true, - Some(PeopleWestend::para_id()), - Some(Weight::from_parts(304_266_000, 7_186)), - ); + Westend::assert_ump_queue_processed(true, Some(PeopleWestend::para_id()), None); assert_expected_events!( Westend, @@ -62,20 +58,13 @@ fn relay_dest_assertions(t: SystemParaToRelayTest) { } fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) { - Westend::assert_ump_queue_processed( - false, - Some(PeopleWestend::para_id()), - Some(Weight::from_parts(157_718_000, 3_593)), - ); + Westend::assert_ump_queue_processed(false, Some(PeopleWestend::para_id()), None); } fn para_origin_assertions(t: SystemParaToRelayTest) { type RuntimeEvent = ::RuntimeEvent; - PeopleWestend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts( - 351_425_000, - 3_593, - ))); + PeopleWestend::assert_xcm_pallet_attempted_complete(None); PeopleWestend::assert_parachain_system_ump_sent(); @@ -94,7 +83,7 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { fn para_dest_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; - PeopleWestend::assert_dmp_queue_complete(Some(Weight::from_parts(162_456_000, 0))); + PeopleWestend::assert_dmp_queue_complete(None); assert_expected_events!( PeopleWestend, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 03d3785dccbd..3d6ae6ddd1d2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yprdrvc7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 21_643_000 picoseconds. - Weight::from_parts(22_410_000, 3593) + // Minimum execution time: 34_180_000 picoseconds. + Weight::from_parts(34_745_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 43_758_000 picoseconds. - Weight::from_parts(44_654_000, 6196) + // Minimum execution time: 42_638_000 picoseconds. + Weight::from_parts(43_454_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,20 +90,17 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `8799` - // Minimum execution time: 87_978_000 picoseconds. - Weight::from_parts(88_517_000, 8799) + // Minimum execution time: 102_916_000 picoseconds. + Weight::from_parts(105_699_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } - // Storage: `ParachainInfo::ParachainId` (r:1 w:0) - // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) pub fn reserve_asset_deposited() -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `1489` - // Minimum execution time: 6_883_000 picoseconds. - Weight::from_parts(6_979_000, 1489) - .saturating_add(T::DbWeight::get().reads(1)) + // Estimated: `0` + // Minimum execution time: 1_805_000 picoseconds. + Weight::from_parts(1_901_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -125,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 198_882_000 picoseconds. - Weight::from_parts(199_930_000, 6196) + // Minimum execution time: 108_018_000 picoseconds. + Weight::from_parts(110_310_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -134,8 +131,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_343_000 picoseconds. - Weight::from_parts(3_487_000, 0) + // Minimum execution time: 3_507_000 picoseconds. + Weight::from_parts(3_724_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -143,13 +140,11 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 19_399_000 picoseconds. - Weight::from_parts(19_659_000, 3593) + // Minimum execution time: 26_269_000 picoseconds. + Weight::from_parts(26_706_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: `System::Account` (r:2 w:2) - // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -160,6 +155,8 @@ impl WeightInfo { // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) @@ -168,8 +165,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `6196` - // Minimum execution time: 59_017_000 picoseconds. - Weight::from_parts(60_543_000, 6196) + // Minimum execution time: 84_759_000 picoseconds. + Weight::from_parts(86_157_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -193,8 +190,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 45_409_000 picoseconds. - Weight::from_parts(47_041_000, 3610) + // Minimum execution time: 50_876_000 picoseconds. + Weight::from_parts(51_512_000, 3610) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index fe8d18613925..f7891aedc496 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -1,24 +1,25 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-vmdtonbz-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -53,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 20_295_000 picoseconds. - Weight::from_parts(21_142_000, 3593) + // Minimum execution time: 32_612_000 picoseconds. + Weight::from_parts(33_359_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -64,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 42_356_000 picoseconds. - Weight::from_parts(43_552_000, 6196) + // Minimum execution time: 41_144_000 picoseconds. + Weight::from_parts(41_788_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -89,20 +90,17 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `8799` - // Minimum execution time: 85_553_000 picoseconds. - Weight::from_parts(87_177_000, 8799) + // Minimum execution time: 101_340_000 picoseconds. + Weight::from_parts(103_686_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } - // Storage: `ParachainInfo::ParachainId` (r:1 w:0) - // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) pub fn reserve_asset_deposited() -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `1489` - // Minimum execution time: 6_166_000 picoseconds. - Weight::from_parts(6_352_000, 1489) - .saturating_add(T::DbWeight::get().reads(1)) + // Estimated: `0` + // Minimum execution time: 1_682_000 picoseconds. + Weight::from_parts(1_734_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -124,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 184_462_000 picoseconds. - Weight::from_parts(189_593_000, 6196) + // Minimum execution time: 107_335_000 picoseconds. + Weight::from_parts(109_665_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -133,8 +131,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_018_000 picoseconds. - Weight::from_parts(3_098_000, 0) + // Minimum execution time: 3_345_000 picoseconds. + Weight::from_parts(3_548_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -142,13 +140,11 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 18_583_000 picoseconds. - Weight::from_parts(19_057_000, 3593) + // Minimum execution time: 25_560_000 picoseconds. + Weight::from_parts(26_779_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: `System::Account` (r:2 w:2) - // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -159,6 +155,8 @@ impl WeightInfo { // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) @@ -167,8 +165,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `6196` - // Minimum execution time: 56_666_000 picoseconds. - Weight::from_parts(58_152_000, 6196) + // Minimum execution time: 84_453_000 picoseconds. + Weight::from_parts(86_755_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -192,8 +190,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 44_197_000 picoseconds. - Weight::from_parts(45_573_000, 3610) + // Minimum execution time: 50_463_000 picoseconds. + Weight::from_parts(51_587_000, 3610) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 057dc4313510..f2cee0e3e807 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yprdrvc7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 19_610_000 picoseconds. - Weight::from_parts(19_980_000, 3593) + // Minimum execution time: 30_988_000 picoseconds. + Weight::from_parts(31_496_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 44_411_000 picoseconds. - Weight::from_parts(45_110_000, 6196) + // Minimum execution time: 42_805_000 picoseconds. + Weight::from_parts(44_207_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,8 +90,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `223` // Estimated: `8799` - // Minimum execution time: 89_739_000 picoseconds. - Weight::from_parts(91_256_000, 8799) + // Minimum execution time: 103_376_000 picoseconds. + Weight::from_parts(104_770_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -124,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 60_045_000 picoseconds. - Weight::from_parts(60_710_000, 6196) + // Minimum execution time: 71_234_000 picoseconds. + Weight::from_parts(72_990_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -133,8 +133,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_257_000 picoseconds. - Weight::from_parts(3_392_000, 0) + // Minimum execution time: 2_636_000 picoseconds. + Weight::from_parts(2_777_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -142,13 +142,11 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 19_423_000 picoseconds. - Weight::from_parts(19_823_000, 3593) + // Minimum execution time: 23_839_000 picoseconds. + Weight::from_parts(24_568_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: `System::Account` (r:2 w:2) - // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -159,6 +157,8 @@ impl WeightInfo { // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) @@ -167,8 +167,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `122` // Estimated: `6196` - // Minimum execution time: 60_484_000 picoseconds. - Weight::from_parts(61_634_000, 6196) + // Minimum execution time: 78_345_000 picoseconds. + Weight::from_parts(80_558_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -192,8 +192,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3593` - // Minimum execution time: 44_863_000 picoseconds. - Weight::from_parts(45_549_000, 3593) + // Minimum execution time: 46_614_000 picoseconds. + Weight::from_parts(47_354_000, 3593) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 4310b2456475..5bd1d1680aa1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-vmdtonbz-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-westend-dev"), DB CACHE: 1024 // Executed Command: // target/production/polkadot-parachain @@ -33,10 +33,10 @@ // --heap-pages=4096 // --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::fungible -// --chain=bridge-hub-rococo-dev +// --chain=bridge-hub-westend-dev // --header=./cumulus/file_header.txt // --template=./cumulus/templates/xcm-bench-template.hbs -// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/ +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 19_037_000 picoseconds. - Weight::from_parts(19_602_000, 3593) + // Minimum execution time: 30_218_000 picoseconds. + Weight::from_parts(30_783_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,15 +65,13 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 43_115_000 picoseconds. - Weight::from_parts(43_897_000, 6196) + // Minimum execution time: 42_631_000 picoseconds. + Weight::from_parts(43_127_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: `System::Account` (r:3 w:3) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - // Storage: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) - // Proof: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -90,11 +88,11 @@ impl WeightInfo { // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `294` + // Measured: `260` // Estimated: `8799` - // Minimum execution time: 90_267_000 picoseconds. - Weight::from_parts(91_460_000, 8799) - .saturating_add(T::DbWeight::get().reads(11)) + // Minimum execution time: 100_978_000 picoseconds. + Weight::from_parts(102_819_000, 8799) + .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } // Storage: `Benchmark::Override` (r:0 w:0) @@ -106,8 +104,6 @@ impl WeightInfo { // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. Weight::from_parts(18_446_744_073_709_551_000, 0) } - // Storage: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) - // Proof: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -126,19 +122,19 @@ impl WeightInfo { // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn initiate_reserve_withdraw() -> Weight { // Proof Size summary in bytes: - // Measured: `242` + // Measured: `208` // Estimated: `6196` - // Minimum execution time: 60_477_000 picoseconds. - Weight::from_parts(61_314_000, 6196) - .saturating_add(T::DbWeight::get().reads(10)) + // Minimum execution time: 71_533_000 picoseconds. + Weight::from_parts(72_922_000, 6196) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } pub fn receive_teleported_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_996_000 picoseconds. - Weight::from_parts(3_107_000, 0) + // Minimum execution time: 2_863_000 picoseconds. + Weight::from_parts(2_997_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -146,15 +142,11 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 18_907_000 picoseconds. - Weight::from_parts(19_475_000, 3593) + // Minimum execution time: 23_763_000 picoseconds. + Weight::from_parts(24_438_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: `System::Account` (r:2 w:2) - // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - // Storage: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) - // Proof: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -165,21 +157,21 @@ impl WeightInfo { // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `193` + // Measured: `159` // Estimated: `6196` - // Minimum execution time: 59_143_000 picoseconds. - Weight::from_parts(60_316_000, 6196) - .saturating_add(T::DbWeight::get().reads(10)) + // Minimum execution time: 78_182_000 picoseconds. + Weight::from_parts(79_575_000, 6196) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } - // Storage: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) - // Proof: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -198,11 +190,11 @@ impl WeightInfo { // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn initiate_teleport() -> Weight { // Proof Size summary in bytes: - // Measured: `141` - // Estimated: `3606` - // Minimum execution time: 44_459_000 picoseconds. - Weight::from_parts(45_365_000, 3606) - .saturating_add(T::DbWeight::get().reads(9)) + // Measured: `107` + // Estimated: `3593` + // Minimum execution time: 46_767_000 picoseconds. + Weight::from_parts(47_823_000, 3593) + .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 73a719805307..c8dbdadf7b15 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-05-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("coretime-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 26_642_000 picoseconds. - Weight::from_parts(27_583_000, 3593) + // Minimum execution time: 29_812_000 picoseconds. + Weight::from_parts(30_526_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 35_124_000 picoseconds. - Weight::from_parts(36_510_000, 6196) + // Minimum execution time: 39_430_000 picoseconds. + Weight::from_parts(39_968_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -88,8 +88,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `207` // Estimated: `6196` - // Minimum execution time: 55_950_000 picoseconds. - Weight::from_parts(57_207_000, 6196) + // Minimum execution time: 65_555_000 picoseconds. + Weight::from_parts(67_161_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -118,8 +118,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 23_747_000 picoseconds. - Weight::from_parts(24_424_000, 3571) + // Minimum execution time: 30_491_000 picoseconds. + Weight::from_parts(31_991_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -127,8 +127,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_853_000 picoseconds. - Weight::from_parts(1_998_000, 0) + // Minimum execution time: 2_568_000 picoseconds. + Weight::from_parts(2_703_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -136,8 +136,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 19_164_000 picoseconds. - Weight::from_parts(19_643_000, 3593) + // Minimum execution time: 22_159_000 picoseconds. + Weight::from_parts(22_517_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3593` - // Minimum execution time: 48_708_000 picoseconds. - Weight::from_parts(49_610_000, 3593) + // Minimum execution time: 57_126_000 picoseconds. + Weight::from_parts(58_830_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -180,8 +180,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 20_586_000 picoseconds. - Weight::from_parts(21_147_000, 3571) + // Minimum execution time: 26_589_000 picoseconds. + Weight::from_parts(27_285_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index ddfc599fa579..935636651eb9 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-05-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("coretime-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 26_842_000 picoseconds. - Weight::from_parts(27_606_000, 3593) + // Minimum execution time: 29_866_000 picoseconds. + Weight::from_parts(30_363_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 35_076_000 picoseconds. - Weight::from_parts(36_109_000, 6196) + // Minimum execution time: 39_434_000 picoseconds. + Weight::from_parts(40_274_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -88,8 +88,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `207` // Estimated: `6196` - // Minimum execution time: 56_951_000 picoseconds. - Weight::from_parts(58_286_000, 6196) + // Minimum execution time: 66_303_000 picoseconds. + Weight::from_parts(68_294_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -118,8 +118,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 23_796_000 picoseconds. - Weight::from_parts(24_692_000, 3571) + // Minimum execution time: 30_523_000 picoseconds. + Weight::from_parts(31_289_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -127,8 +127,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_990_000 picoseconds. - Weight::from_parts(2_142_000, 0) + // Minimum execution time: 2_517_000 picoseconds. + Weight::from_parts(2_634_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -136,8 +136,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 19_572_000 picoseconds. - Weight::from_parts(20_017_000, 3593) + // Minimum execution time: 22_151_000 picoseconds. + Weight::from_parts(22_907_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3593` - // Minimum execution time: 49_336_000 picoseconds. - Weight::from_parts(50_507_000, 3593) + // Minimum execution time: 57_763_000 picoseconds. + Weight::from_parts(58_941_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -180,8 +180,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 21_230_000 picoseconds. - Weight::from_parts(21_870_000, 3571) + // Minimum execution time: 26_322_000 picoseconds. + Weight::from_parts(27_197_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs index 11c1bad9aa17..58007173ae1d 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -60,10 +60,8 @@ impl XcmWeightInfo for PeopleRococoXcmWeight { fn withdraw_asset(assets: &Assets) -> Weight { assets.weigh_assets(XcmFungibleWeight::::withdraw_asset()) } - // Currently there is no trusted reserve - fn reserve_asset_deposited(_assets: &Assets) -> Weight { - // TODO: hardcoded - fix https://github.com/paritytech/cumulus/issues/1974 - Weight::from_parts(1_000_000_000_u64, 0) + fn reserve_asset_deposited(assets: &Assets) -> Weight { + assets.weigh_assets(XcmFungibleWeight::::reserve_asset_deposited()) } fn receive_teleported_asset(assets: &Assets) -> Weight { assets.weigh_assets(XcmFungibleWeight::::receive_teleported_asset()) @@ -114,12 +112,8 @@ impl XcmWeightInfo for PeopleRococoXcmWeight { fn report_error(_query_response_info: &QueryResponseInfo) -> Weight { XcmGeneric::::report_error() } - fn deposit_asset(assets: &AssetFilter, _dest: &Location) -> Weight { - // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_parts(1_000_000_000_u64, 0); - let weight = assets.weigh_assets(XcmFungibleWeight::::deposit_asset()); - hardcoded_weight.min(weight) + assets.weigh_assets(XcmFungibleWeight::::deposit_asset()) } fn deposit_reserve_asset(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::deposit_reserve_asset()) @@ -132,7 +126,7 @@ impl XcmWeightInfo for PeopleRococoXcmWeight { _reserve: &Location, _xcm: &Xcm<()>, ) -> Weight { - assets.weigh_assets(XcmGeneric::::initiate_reserve_withdraw()) + assets.weigh_assets(XcmFungibleWeight::::initiate_reserve_withdraw()) } fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 2364798596d5..4dd44e66dd5e 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -1,41 +1,42 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-kusama-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("people-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=people-kusama-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm_benchmarks::fungible -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-kusama/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_benchmarks::fungible +// --chain=people-rococo-dev +// --header=./cumulus/file_header.txt +// --template=./cumulus/templates/xcm-bench-template.hbs +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,110 +48,140 @@ use core::marker::PhantomData; /// Weights for `pallet_xcm_benchmarks::fungible`. pub struct WeightInfo(PhantomData); impl WeightInfo { - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn withdraw_asset() -> Weight { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 23_309_000 picoseconds. - Weight::from_parts(23_777_000, 3593) + // Minimum execution time: 30_428_000 picoseconds. + Weight::from_parts(31_184_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: System Account (r:2 w:2) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn transfer_asset() -> Weight { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 48_808_000 picoseconds. - Weight::from_parts(49_427_000, 6196) + // Minimum execution time: 41_912_000 picoseconds. + Weight::from_parts(43_346_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: System Account (r:2 w:2) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: // Measured: `223` // Estimated: `6196` - // Minimum execution time: 71_204_000 picoseconds. - Weight::from_parts(72_121_000, 6196) + // Minimum execution time: 67_706_000 picoseconds. + Weight::from_parts(69_671_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } + // Storage: `Benchmark::Override` (r:0 w:0) + // Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + pub fn reserve_asset_deposited() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + } + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_reserve_withdraw() -> Weight { + // Proof Size summary in bytes: + // Measured: `70` + // Estimated: `3535` + // Minimum execution time: 29_790_000 picoseconds. + Weight::from_parts(30_655_000, 3535) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } pub fn receive_teleported_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_559_000 picoseconds. - Weight::from_parts(3_616_000, 0) + // Minimum execution time: 2_438_000 picoseconds. + Weight::from_parts(2_597_000, 0) } - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn deposit_asset() -> Weight { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 25_042_000 picoseconds. - Weight::from_parts(25_630_000, 3593) + // Minimum execution time: 24_040_000 picoseconds. + Weight::from_parts(24_538_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: // Measured: `122` // Estimated: `3593` - // Minimum execution time: 49_030_000 picoseconds. - Weight::from_parts(49_828_000, 3593) + // Minimum execution time: 58_275_000 picoseconds. + Weight::from_parts(59_899_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn initiate_teleport() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 27_142_000 picoseconds. - Weight::from_parts(27_416_000, 3535) + // Minimum execution time: 25_638_000 picoseconds. + Weight::from_parts(26_514_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index a50c8860c48f..729a32117041 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -1,41 +1,42 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-kusama-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("people-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=people-kusama-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm_benchmarks::generic -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_benchmarks::generic +// --chain=people-rococo-dev +// --header=./cumulus/file_header.txt +// --template=./cumulus/templates/xcm-bench-template.hbs +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,24 +48,24 @@ use core::marker::PhantomData; /// Weights for `pallet_xcm_benchmarks::generic`. pub struct WeightInfo(PhantomData); impl WeightInfo { - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_holding() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 30_210_000 picoseconds. - Weight::from_parts(30_864_000, 3535) + // Minimum execution time: 29_430_000 picoseconds. + Weight::from_parts(30_111_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -72,97 +73,97 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_808_000 picoseconds. - Weight::from_parts(2_848_000, 0) + // Minimum execution time: 607_000 picoseconds. + Weight::from_parts(672_000, 0) } - // Storage: PolkadotXcm Queries (r:1 w:0) - // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::Queries` (r:1 w:0) + // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn query_response() -> Weight { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 10_353_000 picoseconds. - Weight::from_parts(10_569_000, 3497) + // Minimum execution time: 7_445_000 picoseconds. + Weight::from_parts(7_623_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_074_000 picoseconds. - Weight::from_parts(12_280_000, 0) + // Minimum execution time: 6_749_000 picoseconds. + Weight::from_parts(7_073_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_080_000 picoseconds. - Weight::from_parts(3_161_000, 0) + // Minimum execution time: 1_275_000 picoseconds. + Weight::from_parts(1_409_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_649_000 picoseconds. - Weight::from_parts(2_732_000, 0) + // Minimum execution time: 670_000 picoseconds. + Weight::from_parts(709_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_652_000 picoseconds. - Weight::from_parts(2_749_000, 0) + // Minimum execution time: 635_000 picoseconds. + Weight::from_parts(723_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_642_000 picoseconds. - Weight::from_parts(2_704_000, 0) + // Minimum execution time: 650_000 picoseconds. + Weight::from_parts(699_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_438_000 picoseconds. - Weight::from_parts(3_508_000, 0) + // Minimum execution time: 678_000 picoseconds. + Weight::from_parts(728_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_626_000 picoseconds. - Weight::from_parts(2_701_000, 0) + // Minimum execution time: 657_000 picoseconds. + Weight::from_parts(703_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_error() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 24_737_000 picoseconds. - Weight::from_parts(25_106_000, 3535) + // Minimum execution time: 25_795_000 picoseconds. + Weight::from_parts(26_415_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: PolkadotXcm AssetTraps (r:1 w:1) - // Proof Skipped: PolkadotXcm AssetTraps (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + // Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn claim_asset() -> Weight { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 14_712_000 picoseconds. - Weight::from_parts(14_976_000, 3555) + // Minimum execution time: 10_792_000 picoseconds. + Weight::from_parts(11_061_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -170,114 +171,93 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_689_000 picoseconds. - Weight::from_parts(2_739_000, 0) + // Minimum execution time: 624_000 picoseconds. + Weight::from_parts(682_000, 0) } - // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) + // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn subscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 26_478_000 picoseconds. - Weight::from_parts(26_695_000, 3503) + // Minimum execution time: 23_906_000 picoseconds. + Weight::from_parts(24_740_000, 3503) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) - // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::VersionNotifyTargets` (r:0 w:1) + // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn unsubscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_811_000 picoseconds. - Weight::from_parts(5_062_000, 0) + // Minimum execution time: 2_621_000 picoseconds. + Weight::from_parts(2_788_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) - pub fn initiate_reserve_withdraw() -> Weight { - // Proof Size summary in bytes: - // Measured: `70` - // Estimated: `3535` - // Minimum execution time: 26_945_000 picoseconds. - Weight::from_parts(28_093_000, 3535) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) - } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_144_000 picoseconds. - Weight::from_parts(4_217_000, 0) + // Minimum execution time: 954_000 picoseconds. + Weight::from_parts(1_046_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_726_000 picoseconds. - Weight::from_parts(2_802_000, 0) + // Minimum execution time: 742_000 picoseconds. + Weight::from_parts(790_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_719_000 picoseconds. - Weight::from_parts(2_790_000, 0) + // Minimum execution time: 664_000 picoseconds. + Weight::from_parts(722_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_660_000 picoseconds. - Weight::from_parts(2_742_000, 0) + // Minimum execution time: 619_000 picoseconds. + Weight::from_parts(672_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_874_000 picoseconds. - Weight::from_parts(2_940_000, 0) + // Minimum execution time: 798_000 picoseconds. + Weight::from_parts(851_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn query_pallet() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 27_235_000 picoseconds. - Weight::from_parts(27_811_000, 3535) + // Minimum execution time: 29_580_000 picoseconds. + Weight::from_parts(31_100_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -285,27 +265,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_807_000 picoseconds. - Weight::from_parts(4_918_000, 0) + // Minimum execution time: 3_150_000 picoseconds. + Weight::from_parts(3_326_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 24_698_000 picoseconds. - Weight::from_parts(25_077_000, 3535) + // Minimum execution time: 26_152_000 picoseconds. + Weight::from_parts(26_635_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -313,35 +293,35 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_613_000 picoseconds. - Weight::from_parts(2_703_000, 0) + // Minimum execution time: 693_000 picoseconds. + Weight::from_parts(724_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_602_000 picoseconds. - Weight::from_parts(2_661_000, 0) + // Minimum execution time: 632_000 picoseconds. + Weight::from_parts(678_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_557_000 picoseconds. - Weight::from_parts(2_655_000, 0) + // Minimum execution time: 646_000 picoseconds. + Weight::from_parts(694_000, 0) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_724_000 picoseconds. - Weight::from_parts(2_760_000, 0) + // Minimum execution time: 622_000 picoseconds. + Weight::from_parts(656_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_764_000 picoseconds. - Weight::from_parts(2_872_000, 0) + // Minimum execution time: 639_000 picoseconds. + Weight::from_parts(679_000, 0) } } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs index b1fc7ad8ed83..b44e8d4b61b8 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -60,10 +60,8 @@ impl XcmWeightInfo for PeopleWestendXcmWeight { fn withdraw_asset(assets: &Assets) -> Weight { assets.weigh_assets(XcmFungibleWeight::::withdraw_asset()) } - // Currently there is no trusted reserve - fn reserve_asset_deposited(_assets: &Assets) -> Weight { - // TODO: hardcoded - fix https://github.com/paritytech/cumulus/issues/1974 - Weight::from_parts(1_000_000_000_u64, 0) + fn reserve_asset_deposited(assets: &Assets) -> Weight { + assets.weigh_assets(XcmFungibleWeight::::reserve_asset_deposited()) } fn receive_teleported_asset(assets: &Assets) -> Weight { assets.weigh_assets(XcmFungibleWeight::::receive_teleported_asset()) @@ -114,12 +112,8 @@ impl XcmWeightInfo for PeopleWestendXcmWeight { fn report_error(_query_response_info: &QueryResponseInfo) -> Weight { XcmGeneric::::report_error() } - fn deposit_asset(assets: &AssetFilter, _dest: &Location) -> Weight { - // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_parts(1_000_000_000_u64, 0); - let weight = assets.weigh_assets(XcmFungibleWeight::::deposit_asset()); - hardcoded_weight.min(weight) + assets.weigh_assets(XcmFungibleWeight::::deposit_asset()) } fn deposit_reserve_asset(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::deposit_reserve_asset()) @@ -132,13 +126,10 @@ impl XcmWeightInfo for PeopleWestendXcmWeight { _reserve: &Location, _xcm: &Xcm<()>, ) -> Weight { - assets.weigh_assets(XcmGeneric::::initiate_reserve_withdraw()) + assets.weigh_assets(XcmFungibleWeight::::initiate_reserve_withdraw()) } fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { - // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_parts(200_000_000_u64, 0); - let weight = assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()); - hardcoded_weight.min(weight) + assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 92d08a246180..8f6bfde986bb 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -1,41 +1,42 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-polkadot-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("people-westend-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=people-polkadot-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm_benchmarks::fungible -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-polkadot/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_benchmarks::fungible +// --chain=people-westend-dev +// --header=./cumulus/file_header.txt +// --template=./cumulus/templates/xcm-bench-template.hbs +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,110 +48,140 @@ use core::marker::PhantomData; /// Weights for `pallet_xcm_benchmarks::fungible`. pub struct WeightInfo(PhantomData); impl WeightInfo { - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn withdraw_asset() -> Weight { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 23_363_000 picoseconds. - Weight::from_parts(23_663_000, 3593) + // Minimum execution time: 30_040_000 picoseconds. + Weight::from_parts(30_758_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: System Account (r:2 w:2) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn transfer_asset() -> Weight { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 49_093_000 picoseconds. - Weight::from_parts(49_719_000, 6196) + // Minimum execution time: 42_135_000 picoseconds. + Weight::from_parts(42_970_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: System Account (r:2 w:2) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: // Measured: `223` // Estimated: `6196` - // Minimum execution time: 74_134_000 picoseconds. - Weight::from_parts(74_719_000, 6196) + // Minimum execution time: 67_385_000 picoseconds. + Weight::from_parts(69_776_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } + // Storage: `Benchmark::Override` (r:0 w:0) + // Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + pub fn reserve_asset_deposited() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + } + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_reserve_withdraw() -> Weight { + // Proof Size summary in bytes: + // Measured: `70` + // Estimated: `3535` + // Minimum execution time: 29_804_000 picoseconds. + Weight::from_parts(30_662_000, 3535) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } pub fn receive_teleported_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_726_000 picoseconds. - Weight::from_parts(3_881_000, 0) + // Minimum execution time: 2_358_000 picoseconds. + Weight::from_parts(2_497_000, 0) } - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn deposit_asset() -> Weight { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 25_903_000 picoseconds. - Weight::from_parts(26_150_000, 3593) + // Minimum execution time: 23_732_000 picoseconds. + Weight::from_parts(24_098_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: // Measured: `122` // Estimated: `3593` - // Minimum execution time: 51_084_000 picoseconds. - Weight::from_parts(51_859_000, 3593) + // Minimum execution time: 58_449_000 picoseconds. + Weight::from_parts(60_235_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn initiate_teleport() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 28_038_000 picoseconds. - Weight::from_parts(28_438_000, 3535) + // Minimum execution time: 25_708_000 picoseconds. + Weight::from_parts(26_495_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 861f03819959..1377d31f2db7 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -1,41 +1,42 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-polkadot-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("people-westend-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=people-polkadot-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm_benchmarks::generic -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-polkadot/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_benchmarks::generic +// --chain=people-westend-dev +// --header=./cumulus/file_header.txt +// --template=./cumulus/templates/xcm-bench-template.hbs +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,24 +48,24 @@ use core::marker::PhantomData; /// Weights for `pallet_xcm_benchmarks::generic`. pub struct WeightInfo(PhantomData); impl WeightInfo { - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_holding() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 30_819_000 picoseconds. - Weight::from_parts(31_157_000, 3535) + // Minimum execution time: 29_537_000 picoseconds. + Weight::from_parts(30_513_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -72,97 +73,97 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_869_000 picoseconds. - Weight::from_parts(2_920_000, 0) + // Minimum execution time: 683_000 picoseconds. + Weight::from_parts(738_000, 0) } - // Storage: PolkadotXcm Queries (r:1 w:0) - // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::Queries` (r:1 w:0) + // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn query_response() -> Weight { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 10_268_000 picoseconds. - Weight::from_parts(10_496_000, 3497) + // Minimum execution time: 7_498_000 picoseconds. + Weight::from_parts(7_904_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_990_000 picoseconds. - Weight::from_parts(12_206_000, 0) + // Minimum execution time: 7_029_000 picoseconds. + Weight::from_parts(7_325_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_170_000 picoseconds. - Weight::from_parts(3_308_000, 0) + // Minimum execution time: 1_343_000 picoseconds. + Weight::from_parts(1_410_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_650_000 picoseconds. - Weight::from_parts(2_783_000, 0) + // Minimum execution time: 696_000 picoseconds. + Weight::from_parts(734_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_681_000 picoseconds. - Weight::from_parts(2_829_000, 0) + // Minimum execution time: 690_000 picoseconds. + Weight::from_parts(740_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_622_000 picoseconds. - Weight::from_parts(2_688_000, 0) + // Minimum execution time: 667_000 picoseconds. + Weight::from_parts(697_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_385_000 picoseconds. - Weight::from_parts(3_538_000, 0) + // Minimum execution time: 692_000 picoseconds. + Weight::from_parts(743_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_630_000 picoseconds. - Weight::from_parts(2_720_000, 0) + // Minimum execution time: 670_000 picoseconds. + Weight::from_parts(712_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_error() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 24_446_000 picoseconds. - Weight::from_parts(24_854_000, 3535) + // Minimum execution time: 26_405_000 picoseconds. + Weight::from_parts(26_877_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: PolkadotXcm AssetTraps (r:1 w:1) - // Proof Skipped: PolkadotXcm AssetTraps (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + // Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn claim_asset() -> Weight { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 14_713_000 picoseconds. - Weight::from_parts(15_010_000, 3555) + // Minimum execution time: 10_953_000 picoseconds. + Weight::from_parts(11_345_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -170,114 +171,93 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_702_000 picoseconds. - Weight::from_parts(2_744_000, 0) + // Minimum execution time: 644_000 picoseconds. + Weight::from_parts(693_000, 0) } - // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) + // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn subscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 25_955_000 picoseconds. - Weight::from_parts(26_632_000, 3503) + // Minimum execution time: 24_157_000 picoseconds. + Weight::from_parts(24_980_000, 3503) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) - // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::VersionNotifyTargets` (r:0 w:1) + // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn unsubscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_965_000 picoseconds. - Weight::from_parts(5_168_000, 0) + // Minimum execution time: 2_767_000 picoseconds. + Weight::from_parts(2_844_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) - pub fn initiate_reserve_withdraw() -> Weight { - // Proof Size summary in bytes: - // Measured: `70` - // Estimated: `3535` - // Minimum execution time: 27_707_000 picoseconds. - Weight::from_parts(28_081_000, 3535) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) - } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_215_000 picoseconds. - Weight::from_parts(4_362_000, 0) + // Minimum execution time: 1_079_000 picoseconds. + Weight::from_parts(1_141_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_843_000 picoseconds. - Weight::from_parts(2_957_000, 0) + // Minimum execution time: 776_000 picoseconds. + Weight::from_parts(829_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_751_000 picoseconds. - Weight::from_parts(2_809_000, 0) + // Minimum execution time: 696_000 picoseconds. + Weight::from_parts(740_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_674_000 picoseconds. - Weight::from_parts(2_737_000, 0) + // Minimum execution time: 655_000 picoseconds. + Weight::from_parts(684_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_891_000 picoseconds. - Weight::from_parts(2_952_000, 0) + // Minimum execution time: 825_000 picoseconds. + Weight::from_parts(853_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn query_pallet() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 28_600_000 picoseconds. - Weight::from_parts(29_001_000, 3535) + // Minimum execution time: 30_222_000 picoseconds. + Weight::from_parts(31_110_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -285,27 +265,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_748_000 picoseconds. - Weight::from_parts(4_813_000, 0) + // Minimum execution time: 3_108_000 picoseconds. + Weight::from_parts(3_325_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 25_483_000 picoseconds. - Weight::from_parts(25_737_000, 3535) + // Minimum execution time: 26_548_000 picoseconds. + Weight::from_parts(26_911_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -313,35 +293,35 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_755_000 picoseconds. - Weight::from_parts(2_817_000, 0) + // Minimum execution time: 684_000 picoseconds. + Weight::from_parts(726_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_700_000 picoseconds. - Weight::from_parts(2_773_000, 0) + // Minimum execution time: 649_000 picoseconds. + Weight::from_parts(700_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_670_000 picoseconds. - Weight::from_parts(2_711_000, 0) + // Minimum execution time: 650_000 picoseconds. + Weight::from_parts(686_000, 0) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_710_000 picoseconds. - Weight::from_parts(2_762_000, 0) + // Minimum execution time: 652_000 picoseconds. + Weight::from_parts(703_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_839_000 picoseconds. - Weight::from_parts(2_931_000, 0) + // Minimum execution time: 673_000 picoseconds. + Weight::from_parts(742_000, 0) } } diff --git a/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 60c40429b1ac..7d743b209124 100644 --- a/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-09-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-nbnwcyh-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -55,8 +55,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 23_189_000 picoseconds. - Weight::from_parts(23_896_000, 3593) + // Minimum execution time: 30_672_000 picoseconds. + Weight::from_parts(31_677_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -66,8 +66,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 50_299_000 picoseconds. - Weight::from_parts(50_962_000, 6196) + // Minimum execution time: 41_132_000 picoseconds. + Weight::from_parts(41_654_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -83,10 +83,10 @@ impl WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `243` + // Measured: `281` // Estimated: `6196` - // Minimum execution time: 71_748_000 picoseconds. - Weight::from_parts(74_072_000, 6196) + // Minimum execution time: 97_174_000 picoseconds. + Weight::from_parts(99_537_000, 6196) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -105,16 +105,18 @@ impl WeightInfo { /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn initiate_reserve_withdraw() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 27_806_000 picoseconds. - Weight::from_parts(28_594_000, 3607) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 67_105_000 picoseconds. + Weight::from_parts(68_659_000, 3746) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -122,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 21_199_000 picoseconds. - Weight::from_parts(21_857_000, 3593) + // Minimum execution time: 30_780_000 picoseconds. + Weight::from_parts(31_496_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -133,27 +135,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 23_578_000 picoseconds. - Weight::from_parts(24_060_000, 3593) + // Minimum execution time: 23_411_000 picoseconds. + Weight::from_parts(23_891_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 48_522_000 picoseconds. - Weight::from_parts(49_640_000, 3607) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 61_541_000 picoseconds. + Weight::from_parts(63_677_000, 3645) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -169,10 +171,10 @@ impl WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn initiate_teleport() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 50_429_000 picoseconds. - Weight::from_parts(51_295_000, 3607) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 48_574_000 picoseconds. + Weight::from_parts(49_469_000, 3645) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 9939f16aa29f..e0c61c8e2bf2 100644 --- a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-nbnwcyh-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 // Executed Command: @@ -55,8 +55,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 24_815_000 picoseconds. - Weight::from_parts(25_098_000, 3593) + // Minimum execution time: 31_780_000 picoseconds. + Weight::from_parts(32_602_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -66,12 +66,12 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 51_268_000 picoseconds. - Weight::from_parts(51_857_000, 6196) + // Minimum execution time: 41_818_000 picoseconds. + Weight::from_parts(42_902_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `System::Account` (r:2 w:2) + /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -83,12 +83,12 @@ impl WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `210` - // Estimated: `6196` - // Minimum execution time: 74_113_000 picoseconds. - Weight::from_parts(74_721_000, 6196) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `351` + // Estimated: `8799` + // Minimum execution time: 101_949_000 picoseconds. + Weight::from_parts(104_190_000, 8799) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `Benchmark::Override` (r:0 w:0) /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -105,16 +105,18 @@ impl WeightInfo { /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn initiate_reserve_withdraw() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3574` - // Minimum execution time: 28_919_000 picoseconds. - Weight::from_parts(29_703_000, 3574) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `351` + // Estimated: `6196` + // Minimum execution time: 70_123_000 picoseconds. + Weight::from_parts(72_564_000, 6196) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -122,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 21_685_000 picoseconds. - Weight::from_parts(22_528_000, 3593) + // Minimum execution time: 31_868_000 picoseconds. + Weight::from_parts(32_388_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -133,27 +135,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 25_192_000 picoseconds. - Weight::from_parts(25_445_000, 3593) + // Minimum execution time: 24_532_000 picoseconds. + Weight::from_parts(25_166_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3593` - // Minimum execution time: 49_349_000 picoseconds. - Weight::from_parts(50_476_000, 3593) + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 63_378_000 picoseconds. + Weight::from_parts(65_002_000, 3612) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -169,10 +171,10 @@ impl WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn initiate_teleport() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3593` - // Minimum execution time: 51_386_000 picoseconds. - Weight::from_parts(52_141_000, 3593) + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 49_174_000 picoseconds. + Weight::from_parts(50_356_000, 3612) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 1daf5ae750cf..5101a97dc842 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -858,14 +858,7 @@ impl XcmExecutor { let old_holding = self.holding.clone(); let result = Config::TransactionalProcessor::process(|| { let deposited = self.holding.saturating_take(assets); - for asset in deposited.into_assets_iter() { - Config::AssetTransactor::deposit_asset( - &asset, - &beneficiary, - Some(&self.context), - )?; - } - Ok(()) + self.deposit_assets_with_retry(&deposited, &beneficiary) }); if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { self.holding = old_holding; @@ -890,9 +883,7 @@ impl XcmExecutor { // now take assets to deposit (excluding transport_fee) let deposited = self.holding.saturating_take(assets); - for asset in deposited.assets_iter() { - Config::AssetTransactor::deposit_asset(&asset, &dest, Some(&self.context))?; - } + self.deposit_assets_with_retry(&deposited, &dest)?; // Note that we pass `None` as `maybe_failed_bin` and drop any assets which // cannot be reanchored because we have already called `deposit_asset` on all // assets. @@ -1282,4 +1273,46 @@ impl XcmExecutor { }), } } + + /// Deposit `to_deposit` assets to `beneficiary`, without giving up on the first (transient) + /// error, and retrying once just in case one of the subsequently deposited assets satisfy some + /// requirement. + /// + /// Most common transient error is: `beneficiary` account does not yet exist and the first + /// asset(s) in the (sorted) list does not satisfy ED, but a subsequent one in the list does. + /// + /// This function can write into storage and also return an error at the same time, it should + /// always be called within a transactional context. + fn deposit_assets_with_retry( + &mut self, + to_deposit: &AssetsInHolding, + beneficiary: &Location, + ) -> Result<(), XcmError> { + let mut failed_deposits = Vec::with_capacity(to_deposit.len()); + + let mut deposit_result = Ok(()); + for asset in to_deposit.assets_iter() { + deposit_result = + Config::AssetTransactor::deposit_asset(&asset, &beneficiary, Some(&self.context)); + // if deposit failed for asset, mark it for retry after depositing the others. + if deposit_result.is_err() { + failed_deposits.push(asset); + } + } + if failed_deposits.len() == to_deposit.len() { + tracing::debug!( + target: "xcm::execute", + ?deposit_result, + "Deposit for each asset failed, returning the last error as there is no point in retrying any of them", + ); + return deposit_result; + } + tracing::trace!(target: "xcm::execute", ?failed_deposits, "Deposits to retry"); + + // retry previously failed deposits, this time short-circuiting on any error. + for asset in failed_deposits { + Config::AssetTransactor::deposit_asset(&asset, &beneficiary, Some(&self.context))?; + } + Ok(()) + } } diff --git a/prdoc/pr_4460.prdoc b/prdoc/pr_4460.prdoc new file mode 100644 index 000000000000..81636c3313fc --- /dev/null +++ b/prdoc/pr_4460.prdoc @@ -0,0 +1,24 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "xcm-executor: allow deposit of multiple assets if at least one of them satisfies ED" + +doc: + - audience: Runtime Dev + description: | + XCM programs that deposit assets to some new (empty) account will now succeed if at least + one of the deposited assets satisfies ED. Before this change, the requirement was that the + _first_ asset had to satisfy ED, but assets order can be changed during reanchoring so it + is not reliable. Now, ordering doesn't matter, any one(s) of them can satisfy ED for the + whole deposit to work. + - audience: Runtime User + description: | + XCM programs that deposit assets to some new (empty) account will now succeed if at least + one of the deposited assets satisfies ED. Before this change, the requirement was that the + _first_ asset had to satisfy ED, but assets order can be changed during reanchoring so it + is not reliable. Now, ordering doesn't matter, any one(s) of them can satisfy ED for the + whole deposit to work. + +crates: + - name: staging-xcm-executor + bump: patch From 1f49358db0033e57a790eac6daccc45beba81863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=B3nal=20Murray?= Date: Mon, 12 Aug 2024 11:44:35 +0100 Subject: [PATCH 089/382] Fix favicon link to fix CI (#5319) The polkadot.network website was recently refreshed and the `favicon-32x32.png` was removed. It was linked in some docs and so the docs have been updated to point to a working favicon on the new website. Previously the lychee link checker was failing on all PRs. --- docs/sdk/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdk/src/lib.rs b/docs/sdk/src/lib.rs index bc0970c01f1f..6dc87858530c 100644 --- a/docs/sdk/src/lib.rs +++ b/docs/sdk/src/lib.rs @@ -25,7 +25,7 @@ #![doc = simple_mermaid::mermaid!("../../mermaid/IA.mmd")] #![warn(rustdoc::broken_intra_doc_links)] #![warn(rustdoc::private_intra_doc_links)] -#![doc(html_favicon_url = "https://polkadot.network/favicon-32x32.png")] +#![doc(html_favicon_url = "https://polkadot.com/favicon.ico")] #![doc( html_logo_url = "https://europe1.discourse-cdn.com/standard21/uploads/polkadot2/original/1X/eb57081e2bb7c39e5fcb1a98b443e423fa4448ae.svg" )] From fc906d5d0fb7796ef54ba670101cf37b0aad6794 Mon Sep 17 00:00:00 2001 From: Alin Dima Date: Mon, 12 Aug 2024 16:56:00 +0300 Subject: [PATCH 090/382] fix av-distribution Jaeger spans mem leak (#5321) Fixes https://github.com/paritytech/polkadot-sdk/issues/5258 --- .../network/availability-distribution/src/lib.rs | 12 ++++++------ .../availability-distribution/src/requester/mod.rs | 8 +++++--- .../availability-distribution/src/requester/tests.rs | 4 ++-- prdoc/pr_5321.prdoc | 11 +++++++++++ 4 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 prdoc/pr_5321.prdoc diff --git a/polkadot/node/network/availability-distribution/src/lib.rs b/polkadot/node/network/availability-distribution/src/lib.rs index ec2c01f99b01..d3185e0af809 100644 --- a/polkadot/node/network/availability-distribution/src/lib.rs +++ b/polkadot/node/network/availability-distribution/src/lib.rs @@ -25,7 +25,7 @@ use polkadot_node_subsystem::{ jaeger, messages::AvailabilityDistributionMessage, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, }; -use polkadot_primitives::Hash; +use polkadot_primitives::{BlockNumber, Hash}; use std::collections::HashMap; /// Error and [`Result`] type for this subsystem. @@ -104,7 +104,7 @@ impl AvailabilityDistributionSubsystem { /// Start processing work as passed on from the Overseer. async fn run(self, mut ctx: Context) -> std::result::Result<(), FatalError> { let Self { mut runtime, recvs, metrics, req_protocol_names } = self; - let mut spans: HashMap = HashMap::new(); + let mut spans: HashMap = HashMap::new(); let IncomingRequestReceivers { pov_req_receiver, @@ -162,7 +162,7 @@ impl AvailabilityDistributionSubsystem { }; let span = jaeger::PerLeafSpan::new(cloned_leaf.span, "availability-distribution"); - spans.insert(cloned_leaf.hash, span); + spans.insert(cloned_leaf.hash, (cloned_leaf.number, span)); log_error( requester .get_mut() @@ -172,8 +172,8 @@ impl AvailabilityDistributionSubsystem { &mut warn_freq, )?; }, - FromOrchestra::Signal(OverseerSignal::BlockFinalized(hash, _)) => { - spans.remove(&hash); + FromOrchestra::Signal(OverseerSignal::BlockFinalized(_hash, finalized_number)) => { + spans.retain(|_hash, (block_number, _span)| *block_number > finalized_number); }, FromOrchestra::Signal(OverseerSignal::Conclude) => return Ok(()), FromOrchestra::Communication { @@ -189,7 +189,7 @@ impl AvailabilityDistributionSubsystem { } => { let span = spans .get(&relay_parent) - .map(|span| span.child("fetch-pov")) + .map(|(_, span)| span.child("fetch-pov")) .unwrap_or_else(|| jaeger::Span::new(&relay_parent, "fetch-pov")) .with_trace_id(candidate_hash) .with_candidate(candidate_hash) diff --git a/polkadot/node/network/availability-distribution/src/requester/mod.rs b/polkadot/node/network/availability-distribution/src/requester/mod.rs index efbdceb43bdd..0175161af70d 100644 --- a/polkadot/node/network/availability-distribution/src/requester/mod.rs +++ b/polkadot/node/network/availability-distribution/src/requester/mod.rs @@ -39,7 +39,9 @@ use polkadot_node_subsystem_util::{ availability_chunks::availability_chunk_index, runtime::{get_occupied_cores, RuntimeInfo}, }; -use polkadot_primitives::{CandidateHash, CoreIndex, Hash, OccupiedCore, SessionIndex}; +use polkadot_primitives::{ + BlockNumber, CandidateHash, CoreIndex, Hash, OccupiedCore, SessionIndex, +}; use super::{FatalError, Metrics, Result, LOG_TARGET}; @@ -112,14 +114,14 @@ impl Requester { ctx: &mut Context, runtime: &mut RuntimeInfo, update: ActiveLeavesUpdate, - spans: &HashMap, + spans: &HashMap, ) -> Result<()> { gum::trace!(target: LOG_TARGET, ?update, "Update fetching heads"); let ActiveLeavesUpdate { activated, deactivated } = update; if let Some(leaf) = activated { let span = spans .get(&leaf.hash) - .map(|span| span.child("update-fetching-heads")) + .map(|(_, span)| span.child("update-fetching-heads")) .unwrap_or_else(|| jaeger::Span::new(&leaf.hash, "update-fetching-heads")) .with_string_tag("leaf", format!("{:?}", leaf.hash)) .with_stage(jaeger::Stage::AvailabilityDistribution); diff --git a/polkadot/node/network/availability-distribution/src/requester/tests.rs b/polkadot/node/network/availability-distribution/src/requester/tests.rs index 09567a8f87d3..decb3156004e 100644 --- a/polkadot/node/network/availability-distribution/src/requester/tests.rs +++ b/polkadot/node/network/availability-distribution/src/requester/tests.rs @@ -208,7 +208,7 @@ fn check_ancestry_lookup_in_same_session() { test_harness(test_state.clone(), |mut ctx| async move { let chain = &test_state.relay_chain; - let spans: HashMap = HashMap::new(); + let spans: HashMap = HashMap::new(); let block_number = 1; let update = ActiveLeavesUpdate { activated: Some(new_leaf(chain[block_number], block_number as u32)), @@ -281,7 +281,7 @@ fn check_ancestry_lookup_in_different_sessions() { test_harness(test_state.clone(), |mut ctx| async move { let chain = &test_state.relay_chain; - let spans: HashMap = HashMap::new(); + let spans: HashMap = HashMap::new(); let block_number = 3; let update = ActiveLeavesUpdate { activated: Some(new_leaf(chain[block_number], block_number as u32)), diff --git a/prdoc/pr_5321.prdoc b/prdoc/pr_5321.prdoc new file mode 100644 index 000000000000..97f75d28dd52 --- /dev/null +++ b/prdoc/pr_5321.prdoc @@ -0,0 +1,11 @@ +title: fix availability-distribution Jaeger spans memory leak + +doc: + - audience: Node Dev + description: | + Fixes a memory leak which caused the Jaeger span storage in availability-distribution to never be pruned and therefore increasing indefinitely. + This was caused by improper handling of finalized heads. More info in https://github.com/paritytech/polkadot-sdk/issues/5258 + +crates: + - name: polkadot-availability-distribution + bump: patch From b52cfc2605362b53a1a570c3c1b41d15481d0990 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:58:33 +0200 Subject: [PATCH 091/382] chain-spec: minor clarification on the genesis config patch (#5324) Added minor clarification on the genesis config patch ([link](https://substrate.stackexchange.com/questions/11813/in-the-genesis-config-what-does-the-patch-key-do/11825#11825)) --------- Co-authored-by: command-bot <> --- substrate/client/chain-spec/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/substrate/client/chain-spec/src/lib.rs b/substrate/client/chain-spec/src/lib.rs index c43f9e89b8a9..5451428d3481 100644 --- a/substrate/client/chain-spec/src/lib.rs +++ b/substrate/client/chain-spec/src/lib.rs @@ -172,6 +172,12 @@ //! //! //! +//! The main purpose of the `RuntimeGenesisConfig` patch is to: +//! - minimize the maintenance effort when RuntimeGenesisConfig is changed in the future (e.g. new +//! pallets added to the runtime or pallet's genesis config changed), +//! - increase the readability - it only contains the relevant fields, +//! - allow to apply numerous changes in distinct domains (e.g. for zombienet). +//! //! For production or long-lasting blockchains, using the `raw` format in the chain specification is //! recommended. Only the `raw` format guarantees that storage root hash will remain unchanged when //! the `RuntimeGenesisConfig` format changes due to software upgrade. From bcc96733f7940edea9f3a782aea78a92c8bb61cf Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 12 Aug 2024 22:01:02 +0300 Subject: [PATCH 092/382] Remove unnecessary mut (#5318) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivial leftover from https://github.com/paritytech/polkadot-sdk/pull/4844 Co-authored-by: Adrian Catangiu Co-authored-by: Bastian Köcher --- substrate/client/consensus/common/src/import_queue.rs | 4 ++-- .../consensus/common/src/import_queue/basic_queue.rs | 7 +++---- substrate/client/network/test/src/block_import.rs | 6 +++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/substrate/client/consensus/common/src/import_queue.rs b/substrate/client/consensus/common/src/import_queue.rs index 35fc8ad4a402..1baa67398a49 100644 --- a/substrate/client/consensus/common/src/import_queue.rs +++ b/substrate/client/consensus/common/src/import_queue.rs @@ -225,7 +225,7 @@ pub async fn import_single_block>( import_handle: &mut impl BlockImport, block_origin: BlockOrigin, block: IncomingBlock, - verifier: &mut V, + verifier: &V, ) -> BlockImportResult { match verify_single_block_metered(import_handle, block_origin, block, verifier, None).await? { SingleBlockVerificationOutcome::Imported(import_status) => Ok(import_status), @@ -295,7 +295,7 @@ pub(crate) async fn verify_single_block_metered>( import_handle: &impl BlockImport, block_origin: BlockOrigin, block: IncomingBlock, - verifier: &mut V, + verifier: &V, metrics: Option<&Metrics>, ) -> Result, BlockImportError> { let peer = block.origin; diff --git a/substrate/client/consensus/common/src/import_queue/basic_queue.rs b/substrate/client/consensus/common/src/import_queue/basic_queue.rs index 05f2b2527961..6a307acb2517 100644 --- a/substrate/client/consensus/common/src/import_queue/basic_queue.rs +++ b/substrate/client/consensus/common/src/import_queue/basic_queue.rs @@ -222,7 +222,7 @@ mod worker_messages { /// Returns when `block_import` ended. async fn block_import_process( mut block_import: BoxBlockImport, - mut verifier: impl Verifier, + verifier: impl Verifier, mut result_sender: BufferedLinkSender, mut block_import_receiver: TracingUnboundedReceiver>, metrics: Option, @@ -241,8 +241,7 @@ async fn block_import_process( }; let res = - import_many_blocks(&mut block_import, origin, blocks, &mut verifier, metrics.clone()) - .await; + import_many_blocks(&mut block_import, origin, blocks, &verifier, metrics.clone()).await; result_sender.blocks_processed(res.imported, res.block_count, res.results); } @@ -388,7 +387,7 @@ async fn import_many_blocks>( import_handle: &mut BoxBlockImport, blocks_origin: BlockOrigin, blocks: Vec>, - verifier: &mut V, + verifier: &V, metrics: Option, ) -> ImportManyBlocksResult { let count = blocks.len(); diff --git a/substrate/client/network/test/src/block_import.rs b/substrate/client/network/test/src/block_import.rs index 690a579e0272..dad5a27e9535 100644 --- a/substrate/client/network/test/src/block_import.rs +++ b/substrate/client/network/test/src/block_import.rs @@ -78,7 +78,7 @@ fn import_single_good_block_works() { &mut substrate_test_runtime_client::new(), BlockOrigin::File, block, - &mut PassThroughVerifier::new(true), + &PassThroughVerifier::new(true), )) { Ok(BlockImportStatus::ImportedUnknown(ref num, ref aux, ref org)) if *num == number && *aux == expected_aux && *org == Some(peer_id.into()) => {}, @@ -93,7 +93,7 @@ fn import_single_good_known_block_is_ignored() { &mut client, BlockOrigin::File, block, - &mut PassThroughVerifier::new(true), + &PassThroughVerifier::new(true), )) { Ok(BlockImportStatus::ImportedKnown(ref n, _)) if *n == number => {}, _ => panic!(), @@ -108,7 +108,7 @@ fn import_single_good_block_without_header_fails() { &mut substrate_test_runtime_client::new(), BlockOrigin::File, block, - &mut PassThroughVerifier::new(true), + &PassThroughVerifier::new(true), )) { Err(BlockImportError::IncompleteHeader(ref org)) if *org == Some(peer_id.into()) => {}, _ => panic!(), From aca25a009f7492d3c5ef07d62363f6812688355b Mon Sep 17 00:00:00 2001 From: Javier Bullrich Date: Mon, 12 Aug 2024 20:49:44 +0100 Subject: [PATCH 093/382] ci: Paused `cmd-action` commenter (#5287) Paused the action which comments on every command starting with `bot ` until we can fix all the commands which are not working. --- .github/workflows/command-inform.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/command-inform.yml b/.github/workflows/command-inform.yml index 2825f4a60460..afdcf4c1b7b9 100644 --- a/.github/workflows/command-inform.yml +++ b/.github/workflows/command-inform.yml @@ -7,7 +7,8 @@ on: jobs: comment: runs-on: ubuntu-latest - if: github.event.issue.pull_request && startsWith(github.event.comment.body, 'bot ') + # Temporary disable the bot until the new command bot works properly + if: github.event.issue.pull_request && startsWith(github.event.comment.body, 'bot ') && false steps: - name: Inform that the new command exist uses: actions/github-script@v7 From 8e8dc618d1a73e7ef169b54551edc6904beb9ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Mon, 12 Aug 2024 21:19:39 +0200 Subject: [PATCH 094/382] `polkadot-node-core-pvf-common`: Fix test compilation error (#5310) This crate only uses `tempfile` on linux but includes it unconditionally in its `Cargo.toml`. It also sets `#![deny(unused_crate_dependencies)]`. This leads to an hard error to anything that is not Linux. This PR fixes this error. I am wondering why CI didn't catch that. Shouldn't the test at least be compiled (but not run) on macOS? --- polkadot/node/core/pvf/common/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/polkadot/node/core/pvf/common/Cargo.toml b/polkadot/node/core/pvf/common/Cargo.toml index 18b3f959c955..bf663b4cfcea 100644 --- a/polkadot/node/core/pvf/common/Cargo.toml +++ b/polkadot/node/core/pvf/common/Cargo.toml @@ -42,6 +42,8 @@ seccompiler = "0.4.0" [dev-dependencies] assert_matches = { workspace = true } + +[target.'cfg(target_os = "linux")'.dev-dependencies] tempfile = { workspace = true } [features] From 79e9aa5811dc108a0d423926b2440ac127635fbc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 21:22:05 +0200 Subject: [PATCH 095/382] Bump the known_good_semver group across 1 directory with 3 updates (#5315) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the known_good_semver group with 2 updates in the / directory: [serde](https://github.com/serde-rs/serde) and [serde_json](https://github.com/serde-rs/json). Updates `serde` from 1.0.204 to 1.0.206

Release notes

Sourced from serde's releases.

v1.0.206

  • Improve support for flatten attribute inside of enums (#2567, thanks @​Mingun)

v1.0.205

  • Use serialize_entry instead of serialize_key + serialize_value when serialize flattened newtype enum variants (#2785, thanks @​Mingun)
  • Avoid triggering a collection_is_never_read lint in the deserialization of enums containing flattened fields (#2791)
Commits
  • 85c73ef Release 1.0.206
  • 5ba1796 Resolve doc_markdown pedantic lint on regression test function
  • e52b7b3 Touch up PR 2567
  • 84c7419 Merge pull request #2794 from dtolnay/neverread
  • 536221b Temporarily ignore collection_is_never_read on FlattenSkipDeserializing
  • fc55ac7 Merge pull request #2567 from Mingun/fix-2565
  • 2afe5b4 Add regression test for issue #2792
  • b4ec259 Correctly process flatten fields in enum variants
  • c3ac7b6 Add regression test for issue #1904
  • 24614e4 Add regression test for issue #2565
  • Additional commits viewable in compare view

Updates `serde_derive` from 1.0.204 to 1.0.206
Release notes

Sourced from serde_derive's releases.

v1.0.206

  • Improve support for flatten attribute inside of enums (#2567, thanks @​Mingun)

v1.0.205

  • Use serialize_entry instead of serialize_key + serialize_value when serialize flattened newtype enum variants (#2785, thanks @​Mingun)
  • Avoid triggering a collection_is_never_read lint in the deserialization of enums containing flattened fields (#2791)
Commits
  • 85c73ef Release 1.0.206
  • 5ba1796 Resolve doc_markdown pedantic lint on regression test function
  • e52b7b3 Touch up PR 2567
  • 84c7419 Merge pull request #2794 from dtolnay/neverread
  • 536221b Temporarily ignore collection_is_never_read on FlattenSkipDeserializing
  • fc55ac7 Merge pull request #2567 from Mingun/fix-2565
  • 2afe5b4 Add regression test for issue #2792
  • b4ec259 Correctly process flatten fields in enum variants
  • c3ac7b6 Add regression test for issue #1904
  • 24614e4 Add regression test for issue #2565
  • Additional commits viewable in compare view

Updates `serde_json` from 1.0.121 to 1.0.124
Release notes

Sourced from serde_json's releases.

v1.0.124

v1.0.123

v1.0.122

  • Support using json! in no-std crates (#1166)
Commits
  • cf771a0 Release 1.0.124
  • 8b314a7 Merge pull request #1173 from iex-rs/fix-big-endian
  • 8eba786 Fix skip_to_escape on BE architectures
  • 2cab07e Release 1.0.123
  • 346189a Fix needless_borrow clippy lint in new control character test
  • 859ead8 Merge pull request #1161 from iex-rs/vectorized-string-parsing
  • e43da5e Immediately bail-out on empty strings
  • 8389d8a Don't run the slow algorithm from the beginning
  • 1f0dcf7 Allow clippy::items_after_statements
  • a95d6df Big endian support
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bastian Köcher --- Cargo.lock | 12 ++++++------ Cargo.toml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ebacc9ec5a8..87ff24663ffc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18838,9 +18838,9 @@ checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.206" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284" dependencies = [ "serde_derive", ] @@ -18865,9 +18865,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.206" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" dependencies = [ "proc-macro2 1.0.82", "quote 1.0.36", @@ -18896,9 +18896,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.121" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" +checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" dependencies = [ "indexmap 2.2.3", "itoa", diff --git a/Cargo.toml b/Cargo.toml index 7ae7c3bd1811..2333dd15fce8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1172,10 +1172,10 @@ secp256k1 = { version = "0.28.0", default-features = false } secrecy = { version = "0.8.0", default-features = false } seedling-runtime = { path = "cumulus/parachains/runtimes/starters/seedling" } separator = { version = "0.4.1" } -serde = { version = "1.0.204", default-features = false } +serde = { version = "1.0.206", default-features = false } serde-big-array = { version = "0.3.2" } serde_derive = { version = "1.0.117" } -serde_json = { version = "1.0.121", default-features = false } +serde_json = { version = "1.0.124", default-features = false } serde_yaml = { version = "0.9" } serial_test = { version = "2.0.0" } sha1 = { version = "0.10.6" } From bc22f086bb54fd2665e5376382014a3636ad5808 Mon Sep 17 00:00:00 2001 From: Elias Rad <146735585+nnsW3@users.noreply.github.com> Date: Mon, 12 Aug 2024 23:24:48 +0300 Subject: [PATCH 096/382] Fix spelling issues (#5206) Hello I found several spelling errors. Br, Elias. --- docs/contributor/CONTRIBUTING.md | 4 ++-- docs/contributor/DOCUMENTATION_GUIDELINES.md | 4 ++-- docs/contributor/PULL_REQUEST_TEMPLATE.md | 2 +- substrate/docs/SECURITY.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/contributor/CONTRIBUTING.md b/docs/contributor/CONTRIBUTING.md index d8f956b82d2d..2e2d7a7fb4f7 100644 --- a/docs/contributor/CONTRIBUTING.md +++ b/docs/contributor/CONTRIBUTING.md @@ -42,13 +42,13 @@ The set of labels and their description can be found [here](https://paritytech.g 3. If you’re still working on your PR, please submit as “Draft”. Once a PR is ready for review change the status to “Open”, so that the maintainers get to review your PR. Generally PRs should sit for 48 hours in order to garner feedback. It may be merged before if all relevant parties had a look at it. -4. With respect to auditing, please see [AUDIT.md](../AUDIT.md). In general, merging to master can happen independent of +4. With respect to auditing, please see [AUDIT.md](../AUDIT.md). In general, merging to master can happen independently of audit. 5. PRs will be able to be merged once all reviewers' comments are addressed and CI is successful. **Noting breaking changes:** When breaking APIs, the PR description should mention what was changed alongside some examples on how to change the code to make it work/compile. It should also mention potential storage migrations and if -they require some special setup aside adding it to the list of migrations in the runtime. +they require some special setup aside from adding it to the list of migrations in the runtime. ## Reviewing pull requests diff --git a/docs/contributor/DOCUMENTATION_GUIDELINES.md b/docs/contributor/DOCUMENTATION_GUIDELINES.md index cc1082347d20..5ac99fff1cdb 100644 --- a/docs/contributor/DOCUMENTATION_GUIDELINES.md +++ b/docs/contributor/DOCUMENTATION_GUIDELINES.md @@ -136,7 +136,7 @@ the `macro@my_macro_name` syntax in your link. Read more about how to correctly The above five guidelines must always be reasonably respected in the documentation. -The following are a set of notes that may not necessarily hold in all circumstances: +The following is a set of notes that may not necessarily hold in all circumstances: --- @@ -205,7 +205,7 @@ properly do this. ## Pallet Crates -The guidelines so far have been general in nature, and are applicable to crates that are pallets and crates that're not +The guidelines so far have been general in nature, and are applicable to crates that are pallets and crates that are not pallets. The following is relevant to how to document parts of a crate that is a pallet. See diff --git a/docs/contributor/PULL_REQUEST_TEMPLATE.md b/docs/contributor/PULL_REQUEST_TEMPLATE.md index 083b30b4a356..9612ab6d8d3e 100644 --- a/docs/contributor/PULL_REQUEST_TEMPLATE.md +++ b/docs/contributor/PULL_REQUEST_TEMPLATE.md @@ -20,7 +20,7 @@ reviewed by reviewers, if the PR does NOT have the `R0-Silent` label. In case of ## Review Notes -*In depth notes about the **implenentation** details of your PR. This should be the main guide for reviewers to +*In depth notes about the **implementation** details of your PR. This should be the main guide for reviewers to understand your approach and effectively review it. If too long, use [`
`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)*. diff --git a/substrate/docs/SECURITY.md b/substrate/docs/SECURITY.md index 0d2064863d85..f52da0327132 100644 --- a/substrate/docs/SECURITY.md +++ b/substrate/docs/SECURITY.md @@ -8,7 +8,7 @@ required to address security issues. ## Reporting a Vulnerability Security vulnerabilities in Parity software should be reported by email to security@parity.io. If you think your report -might be eligible for the Parity Bug Bounty Program, your email should be send to bugbounty@parity.io. +might be eligible for the Parity Bug Bounty Program, your email should be sent to bugbounty@parity.io. Your report should include the following: From 819a5818284a96a5a5bd65ce67e69bab860d4534 Mon Sep 17 00:00:00 2001 From: eskimor Date: Mon, 12 Aug 2024 22:34:22 +0200 Subject: [PATCH 097/382] Bump authoring duration for async backing to 2s. (#5195) Should be safe on all production network. I noticed that Paseo needs to be updated, it is lacking behind in a couple of things. Execution environment parameters should be updated to those of Polkadot: ``` [ { MaxMemoryPages: 8,192 } { PvfExecTimeout: [ Backing 2,500 ] } { PvfExecTimeout: [ Approval 15,000 ] } ] ] ``` --------- Co-authored-by: eskimor --- cumulus/polkadot-parachain/src/service.rs | 2 +- docs/sdk/src/guides/async_backing_guide.rs | 4 ++-- prdoc/pr_5195.prdoc | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 prdoc/pr_5195.prdoc diff --git a/cumulus/polkadot-parachain/src/service.rs b/cumulus/polkadot-parachain/src/service.rs index 80698a2d7115..3b9ae6bd4457 100644 --- a/cumulus/polkadot-parachain/src/service.rs +++ b/cumulus/polkadot-parachain/src/service.rs @@ -832,7 +832,7 @@ where relay_chain_slot_duration, proposer: Proposer::new(proposer_factory), collator_service, - authoring_duration: Duration::from_millis(1500), + authoring_duration: Duration::from_millis(2000), reinitialize: false, }, }; diff --git a/docs/sdk/src/guides/async_backing_guide.rs b/docs/sdk/src/guides/async_backing_guide.rs index a9fda2c3aa8a..25ef3a12cbf0 100644 --- a/docs/sdk/src/guides/async_backing_guide.rs +++ b/docs/sdk/src/guides/async_backing_guide.rs @@ -174,7 +174,7 @@ //! - In the `para_client` field, pass in a cloned para client rather than the original //! - Add a `para_backend` parameter after `para_client`, passing in our para backend //! - Provide a `code_hash_provider` closure like that shown below -//! - Increase `authoring_duration` from 500 milliseconds to 1500 +//! - Increase `authoring_duration` from 500 milliseconds to 2000 //! ```ignore //! let params = AuraParams { //! .. @@ -185,7 +185,7 @@ //! client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) //! }, //! .. -//! authoring_duration: Duration::from_millis(1500), +//! authoring_duration: Duration::from_millis(2000), //! .. //! }; //! ``` diff --git a/prdoc/pr_5195.prdoc b/prdoc/pr_5195.prdoc new file mode 100644 index 000000000000..cfd435fa289d --- /dev/null +++ b/prdoc/pr_5195.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Bump Aura authoring duration to 2s. + +doc: + - audience: Node Dev + description: | + This PR bumps the Aura authoring duration in the asynchronous backing + guide and the polkadot-parachain service file to 2s in order to make + better use of the provided coretime. + +crates: + - name: polkadot-parachain-bin + bump: patch From c5f6b700bd1f3fd6a7fa40405987782bdecec636 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 10:28:31 +0200 Subject: [PATCH 098/382] Bump libp2p-identity from 0.2.8 to 0.2.9 (#5232) Bumps [libp2p-identity](https://github.com/libp2p/rust-libp2p) from 0.2.8 to 0.2.9.
Release notes

Sourced from libp2p-identity's releases.

libp2p-v0.53.2

See individual changelogs for details.

libp2p-v0.53.1

See individual changelogs for details.

libp2p-v0.53.0

The most ergonomic version of rust-libp2p yet!

We've been busy again, with over 250 PRs being merged into master since v0.52.0 (excluding dependency updates).

Backwards-compatible features

Numerous improvements landed as patch releases since the v0.52.0 release, for example a new, type-safe SwarmBuilder that also encompasses the most common transport protocols:

let mut swarm =
libp2p::SwarmBuilder::with_new_identity()
    .with_tokio()
    .with_tcp(
        tcp::Config::default().port_reuse(true).nodelay(true),
        noise::Config::new,
        yamux::Config::default,
    )?
    .with_quic()
    .with_dns()?
    .with_relay_client(noise::Config::new, yamux::Config::default)?
    .with_behaviour(|keypair, relay_client| Behaviour {
        relay_client,
        ping: ping::Behaviour::default(),
        dcutr: dcutr::Behaviour::new(keypair.public().to_peer_id()),
    })?
    .build();

The new builder makes heavy use of the type-system to guide you towards a correct composition of all transports. For example, it is important to compose the DNS transport as a wrapper around all other transports but before the relay transport. Luckily, you no longer need to worry about these details as the builder takes care of that for you! Have a look yourself if you dare here but be warned, the internals are a bit wild :)

Some more features that we were able to ship in v0.52.X patch-releases include:

We always try to ship as many features as possible in a backwards-compatible way to get them to you faster. Often times, these come with deprecations to give you a heads-up about what will change in a future version. We advise updating to each intermediate version rather than skipping directly to the most recent one, to avoid missing any crucial deprecation warnings. We highly recommend you stay up-to-date with the latest version to make upgrades as smooth as possible.

Some improvments we unfortunately cannot ship in a way that Rust considers a non-breaking change but with every release, we attempt to smoothen the way for future upgrades.

#[non_exhaustive] on key enums

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=libp2p-identity&package-manager=cargo&previous-version=0.2.8&new-version=0.2.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87ff24663ffc..33ebf14be0e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6778,9 +6778,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hkdf" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac 0.12.1", ] @@ -7910,9 +7910,9 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999ec70441b2fb35355076726a6bc466c932e9bdc66f6a11c6c0aa17c7ab9be0" +checksum = "55cca1eb2bc1fd29f099f3daaab7effd01e1a54b7c577d0ed082521034d912e8" dependencies = [ "bs58 0.5.1", "ed25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index 2333dd15fce8..74b12f96603a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -819,7 +819,7 @@ lazy_static = { version = "1.4.0" } libc = { version = "0.2.155" } libfuzzer-sys = { version = "0.4" } libp2p = { version = "0.52.4" } -libp2p-identity = { version = "0.2.3" } +libp2p-identity = { version = "0.2.9" } libsecp256k1 = { version = "0.7.0", default-features = false } linked-hash-map = { version = "0.5.4" } linked_hash_set = { version = "0.1.4" } From ae1b84df5c5276f235ddd1447b8b1afd22f4c377 Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:59:09 +0100 Subject: [PATCH 099/382] Create subsystem-benchmarks.yml (#5325) Closes https://github.com/paritytech/ci_cd/issues/1014 Adds subsystem-benchmarking in GHA (only works with temp label) --- .github/workflows/subsystem-benchmarks.yml | 82 ++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 .github/workflows/subsystem-benchmarks.yml diff --git a/.github/workflows/subsystem-benchmarks.yml b/.github/workflows/subsystem-benchmarks.yml new file mode 100644 index 000000000000..7c19b420a6ac --- /dev/null +++ b/.github/workflows/subsystem-benchmarks.yml @@ -0,0 +1,82 @@ +on: + push: + branches: + - master + pull_request: + types: [ opened, synchronize, reopened, closed, labeled ] + merge_group: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: write + +jobs: + set-image: + # TODO: remove once migration is complete or this workflow is fully stable + if: contains(github.event.label.name, 'GHA-migration') + # GitHub Actions allows using 'env' in a container context. + # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 + # This workaround sets the container image for each job using 'set-image' job output. + runs-on: ubuntu-latest + outputs: + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT + + build: + needs: [ set-image ] + runs-on: arc-runners-polkadot-sdk-benchmark + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + BENCH_DIR: ./charts/bench/${{ matrix.features.bench }} + BENCH_FILE_NAME: ${{ matrix.features.bench }} + strategy: + fail-fast: false + matrix: + features: [ + { name: "polkadot-availability-recovery", bench: "availability-recovery-regression-bench" }, + { name: "polkadot-availability-distribution", bench: "availability-distribution-regression-bench" }, + { name: "polkadot-node-core-approval-voting", bench: "approval-voting-regression-bench" }, + { name: "polkadot-statement-distribution", bench: "statement-distribution-regression-bench" } + ] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check Rust + run: | + rustup show + rustup +nightly show + + - name: Run Benchmarks + continue-on-error: true + id: run-benchmarks + run: | + cargo bench -p ${{ matrix.features.name }} --bench ${{ matrix.features.bench }} --features subsystem-benchmarks || echo "Benchmarks failed" + ls -lsa ./charts + mkdir -p $BENCH_DIR || echo "Directory exists" + cp charts/${BENCH_FILE_NAME}.json $BENCH_DIR + ls -lsa $BENCH_DIR + # Fixes "detected dubious ownership" error in the ci + git config --global --add safe.directory '*' + + - name: Publish result to GH Pages + if: ${{ steps.run-benchmarks.outcome == 'success' }} + uses: benchmark-action/github-action-benchmark@v1 + with: + tool: "customSmallerIsBetter" + name: ${{ env.BENCH_FILE_NAME }} + output-file-path: ${{ env.BENCH_DIR }}/${{ env.BENCH_FILE_NAME }}.json + benchmark-data-dir-path: ${{ env.BENCH_DIR }} + github-token: ${{ secrets.GITHUB_TOKEN }} + comment-on-alert: ${{ github.event_name == 'pull_request' }} # will comment on PRs if regression is detected + auto-push: false # TODO: enable when gitlab part is removed ${{ github.ref == 'refs/heads/master' }} + From 0d7bd6badce2d53bb332fc48d4a32e828267cf7e Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 13 Aug 2024 13:11:12 +0200 Subject: [PATCH 100/382] Small nits found accidentally along the way (#5341) --- Cargo.lock | 8 -------- cumulus/primitives/aura/Cargo.toml | 10 ---------- cumulus/primitives/parachain-inherent/Cargo.toml | 4 ---- cumulus/primitives/utility/Cargo.toml | 5 ----- polkadot/xcm/xcm-executor/src/lib.rs | 2 +- 5 files changed, 1 insertion(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33ebf14be0e8..60be41563299 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4256,12 +4256,8 @@ dependencies = [ name = "cumulus-primitives-aura" version = "0.7.0" dependencies = [ - "parity-scale-codec", - "polkadot-core-primitives", - "polkadot-primitives", "sp-api", "sp-consensus-aura", - "sp-runtime", ] [[package]] @@ -4289,8 +4285,6 @@ dependencies = [ "scale-info", "sp-core", "sp-inherents", - "sp-runtime", - "sp-state-machine", "sp-trie", ] @@ -4343,8 +4337,6 @@ dependencies = [ "pallet-asset-conversion", "parity-scale-codec", "polkadot-runtime-common", - "polkadot-runtime-parachains", - "sp-io", "sp-runtime", "staging-xcm", "staging-xcm-builder", diff --git a/cumulus/primitives/aura/Cargo.toml b/cumulus/primitives/aura/Cargo.toml index 062b9ce736e7..185b2d40833f 100644 --- a/cumulus/primitives/aura/Cargo.toml +++ b/cumulus/primitives/aura/Cargo.toml @@ -10,24 +10,14 @@ description = "Core primitives for Aura in Cumulus" workspace = true [dependencies] -codec = { features = ["derive"], workspace = true } # Substrate sp-api = { workspace = true } sp-consensus-aura = { workspace = true } -sp-runtime = { workspace = true } - -# Polkadot -polkadot-core-primitives = { workspace = true } -polkadot-primitives = { workspace = true } [features] default = ["std"] std = [ - "codec/std", - "polkadot-core-primitives/std", - "polkadot-primitives/std", "sp-api/std", "sp-consensus-aura/std", - "sp-runtime/std", ] diff --git a/cumulus/primitives/parachain-inherent/Cargo.toml b/cumulus/primitives/parachain-inherent/Cargo.toml index 172af4b9ec63..a4271d3fd9cc 100644 --- a/cumulus/primitives/parachain-inherent/Cargo.toml +++ b/cumulus/primitives/parachain-inherent/Cargo.toml @@ -17,8 +17,6 @@ scale-info = { features = ["derive"], workspace = true } # Substrate sp-core = { workspace = true } sp-inherents = { workspace = true } -sp-runtime = { optional = true, workspace = true } -sp-state-machine = { optional = true, workspace = true } sp-trie = { workspace = true } # Cumulus @@ -33,7 +31,5 @@ std = [ "scale-info/std", "sp-core/std", "sp-inherents/std", - "sp-runtime?/std", - "sp-state-machine?/std", "sp-trie/std", ] diff --git a/cumulus/primitives/utility/Cargo.toml b/cumulus/primitives/utility/Cargo.toml index 82d18c8c0aac..2ca8b82001d5 100644 --- a/cumulus/primitives/utility/Cargo.toml +++ b/cumulus/primitives/utility/Cargo.toml @@ -15,13 +15,11 @@ log = { workspace = true } # Substrate frame-support = { workspace = true } -sp-io = { workspace = true } sp-runtime = { workspace = true } pallet-asset-conversion = { workspace = true } # Polkadot polkadot-runtime-common = { workspace = true } -polkadot-runtime-parachains = { workspace = true } xcm = { workspace = true } xcm-executor = { workspace = true } xcm-builder = { workspace = true } @@ -38,8 +36,6 @@ std = [ "log/std", "pallet-asset-conversion/std", "polkadot-runtime-common/std", - "polkadot-runtime-parachains/std", - "sp-io/std", "sp-runtime/std", "xcm-builder/std", "xcm-executor/std", @@ -51,7 +47,6 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", - "polkadot-runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 5101a97dc842..74561e931e7e 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -809,7 +809,7 @@ impl XcmExecutor { }; let actual_weight = maybe_actual_weight.unwrap_or(weight); let surplus = weight.saturating_sub(actual_weight); - // We assume that the `Config::Weigher` will counts the `require_weight_at_most` + // We assume that the `Config::Weigher` will count the `require_weight_at_most` // for the estimate of how much weight this instruction will take. Now that we know // that it's less, we credit it. // From b78d7955e280309dc7cbd6b0b40819e942dfc597 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 13 Aug 2024 15:25:09 +0200 Subject: [PATCH 101/382] [Bot] Add prdoc generation (#5331) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a bot to automatically generate prdocs that have all the crates populated. --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Bastian Köcher --- .github/commands-readme.md | 11 +++ .github/scripts/generate-prdoc.py | 112 ++++++++++++++++++++++++++++ .github/workflows/command-prdoc.yml | 78 +++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 .github/scripts/generate-prdoc.py create mode 100644 .github/workflows/command-prdoc.yml diff --git a/.github/commands-readme.md b/.github/commands-readme.md index 793524e056f8..ce4e0fd0d789 100644 --- a/.github/commands-readme.md +++ b/.github/commands-readme.md @@ -10,6 +10,7 @@ The current available command actions are: - [Command FMT](https://github.com/paritytech/polkadot-sdk/actions/workflows/command-fmt.yml) - [Command Update UI](https://github.com/paritytech/polkadot-sdk/actions/workflows/command-update-ui.yml) +- [Command Prdoc](https://github.com/paritytech/polkadot-sdk/actions/workflows/command-prdoc.yml) - [Command Sync](https://github.com/paritytech/polkadot-sdk/actions/workflows/command-sync.yml) - [Command Bench](https://github.com/paritytech/polkadot-sdk/actions/workflows/command-bench.yml) - [Command Bench All](https://github.com/paritytech/polkadot-sdk/actions/workflows/command-bench-all.yml) @@ -235,6 +236,16 @@ You can use the following [`gh cli`](https://cli.github.com/) inside the repo: gh workflow run command-bench-overheard.yml -f pr=1000 -f benchmark=substrate -f runtime=rococo -f target_dir=substrate ``` +### PrDoc + +Generate a PrDoc with the crates populated by all modified crates. + +Options: +- `pr`: The PR number to generate the PrDoc for. +- `audience`: The audience of whom the changes may concern. +- `bump`: A default bump level for all crates. The PrDoc will likely need to be edited to reflect the actual changes after generation. +- `overwrite`: Whether to overwrite any existing PrDoc. + ### Sync Run sync and commit back results to PR. diff --git a/.github/scripts/generate-prdoc.py b/.github/scripts/generate-prdoc.py new file mode 100644 index 000000000000..b7b2e6f970fa --- /dev/null +++ b/.github/scripts/generate-prdoc.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +""" +Generate the PrDoc for a Pull Request with a specific number, audience and bump level. + +It downloads and parses the patch from the GitHub API to opulate the prdoc with all modified crates. +This will delete any prdoc that already exists for the PR if `--force` is passed. + +Usage: + python generate-prdoc.py --pr 1234 --audience "TODO" --bump "TODO" +""" + +import argparse +import os +import re +import sys +import subprocess +import toml +import yaml +import requests + +from github import Github +import whatthepatch +from cargo_workspace import Workspace + +# Download the patch and pass the info into `create_prdoc`. +def from_pr_number(n, audience, bump, force): + print(f"Fetching PR '{n}' from GitHub") + g = Github() + + repo = g.get_repo("paritytech/polkadot-sdk") + pr = repo.get_pull(n) + + patch_url = pr.patch_url + patch = requests.get(patch_url).text + + create_prdoc(n, audience, pr.title, pr.body, patch, bump, force) + +def create_prdoc(pr, audience, title, description, patch, bump, force): + path = f"prdoc/pr_{pr}.prdoc" + + if os.path.exists(path): + if force == True: + print(f"Overwriting existing PrDoc for PR {pr}") + else: + print(f"PrDoc already exists for PR {pr}. Use --force to overwrite.") + sys.exit(1) + else: + print(f"No preexisting PrDoc for PR {pr}") + + prdoc = { "doc": [{}], "crates": [] } + + prdoc["title"] = title + prdoc["doc"][0]["audience"] = audience + prdoc["doc"][0]["description"] = description + + workspace = Workspace.from_path(".") + + modified_paths = [] + for diff in whatthepatch.parse_patch(patch): + modified_paths.append(diff.header.new_path) + + modified_crates = {} + for p in modified_paths: + # Go up until we find a Cargo.toml + p = os.path.join(workspace.path, p) + while not os.path.exists(os.path.join(p, "Cargo.toml")): + p = os.path.dirname(p) + + with open(os.path.join(p, "Cargo.toml")) as f: + manifest = toml.load(f) + + if not "package" in manifest: + print(f"File was not in any crate: {p}") + continue + + crate_name = manifest["package"]["name"] + if workspace.crate_by_name(crate_name).publish: + modified_crates[crate_name] = True + else: + print(f"Skipping unpublished crate: {crate_name}") + + print(f"Modified crates: {modified_crates.keys()}") + + for crate_name in modified_crates.keys(): + entry = { "name": crate_name } + + if bump == 'silent' or bump == 'ignore' or bump == 'no change': + entry["validate"] = False + else: + entry["bump"] = bump + + print(f"Adding crate {entry}") + prdoc["crates"].append(entry) + + # write the parsed PR documentation back to the file + with open(path, "w") as f: + yaml.dump(prdoc, f) + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--pr", type=int, required=True) + parser.add_argument("--audience", type=str, default="TODO") + parser.add_argument("--bump", type=str, default="TODO") + parser.add_argument("--force", type=str) + return parser.parse_args() + +if __name__ == "__main__": + args = parse_args() + force = True if args.force.lower() == "true" else False + print(f"Args: {args}, force: {force}") + from_pr_number(args.pr, args.audience, args.bump, force) diff --git a/.github/workflows/command-prdoc.yml b/.github/workflows/command-prdoc.yml new file mode 100644 index 000000000000..da8f14089cb8 --- /dev/null +++ b/.github/workflows/command-prdoc.yml @@ -0,0 +1,78 @@ +name: Command PrDoc + +on: + workflow_dispatch: + inputs: + pr: + type: number + description: Number of the Pull Request + required: true + bump: + type: choice + description: Default bump level for all crates + default: "TODO" + required: true + options: + - "TODO" + - "no change" + - "patch" + - "minor" + - "major" + audience: + type: choice + description: Audience of the PrDoc + default: "TODO" + required: true + options: + - "TODO" + - "Runtime Dev" + - "Runtime User" + - "Node Dev" + - "Node User" + overwrite: + type: choice + description: Overwrite existing PrDoc + default: "true" + required: true + options: + - "true" + - "false" + +concurrency: + group: command-prdoc + cancel-in-progress: true + +jobs: + cmd-prdoc: + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: write + pull-requests: write + steps: + - name: Download repo + uses: actions/checkout@v4 + - name: Install gh cli + id: gh + uses: ./.github/actions/set-up-gh + with: + pr-number: ${{ inputs.pr }} + GH_TOKEN: ${{ github.token }} + - name: Generate PrDoc + run: | + python3 -m pip install -q cargo-workspace PyGithub whatthepatch pyyaml toml + + python3 .github/scripts/generate-prdoc.py --pr "${{ inputs.pr }}" --bump "${{ inputs.bump }}" --audience "${{ inputs.audience }}" --force "${{ inputs.overwrite }}" + + - name: Report failure + if: ${{ failure() }} + run: gh pr comment ${{ inputs.pr }} --body "

Command failed ❌

Run by @${{ github.actor }} for ${{ github.workflow }} failed. See logs here." + env: + RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_TOKEN: ${{ github.token }} + - name: Push Commit + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: Add PrDoc (auto generated) + branch: ${{ steps.gh.outputs.branch }} + file_pattern: 'prdoc/*.prdoc' From 055eb5377da43eaced23647ed4348a816bfeb8f4 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Tue, 13 Aug 2024 21:57:23 +0200 Subject: [PATCH 102/382] StorageWeightReclaim: set to node pov size if higher (#5281) This PR adds an additional defensive check to the reclaim SE. Since it can happen that we miss some storage accesses on other SEs pre-dispatch, we should double check that the bookkeeping of the runtime stays ahead of the node-side pov-size. If we discover a mismatch and the node-side pov-size is indeed higher, we should set the runtime bookkeeping to the node-side value. In cases such as #5229, we would stop including extrinsics and not run `on_idle` at least. cc @gui1117 --------- Co-authored-by: command-bot <> --- .../storage-weight-reclaim/src/lib.rs | 96 ++++++++++++++++++- prdoc/pr_5281.prdoc | 17 ++++ substrate/frame/system/src/lib.rs | 2 +- 3 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 prdoc/pr_5281.prdoc diff --git a/cumulus/primitives/storage-weight-reclaim/src/lib.rs b/cumulus/primitives/storage-weight-reclaim/src/lib.rs index 5984fa77a2c5..a557e881e26b 100644 --- a/cumulus/primitives/storage-weight-reclaim/src/lib.rs +++ b/cumulus/primitives/storage-weight-reclaim/src/lib.rs @@ -174,6 +174,9 @@ where let storage_size_diff = benchmarked_weight.abs_diff(consumed_weight as u64); + let extrinsic_len = frame_system::AllExtrinsicsLen::::get().unwrap_or(0); + let node_side_pov_size = post_dispatch_proof_size.saturating_add(extrinsic_len.into()); + // This value will be reclaimed by [`frame_system::CheckWeight`], so we need to calculate // that in. frame_system::BlockWeight::::mutate(|current| { @@ -190,6 +193,19 @@ where ); current.reduce(Weight::from_parts(0, storage_size_diff), info.class) } + + // If we encounter a situation where the node-side proof size is already higher than + // what we have in the runtime bookkeeping, we add the difference to the `BlockWeight`. + // This prevents that the proof size grows faster than the runtime proof size. + let block_weight_proof_size = current.total().proof_size(); + let missing_from_node = node_side_pov_size.saturating_sub(block_weight_proof_size); + if missing_from_node > 0 { + log::warn!( + target: LOG_TARGET, + "Node-side PoV size higher than runtime proof size weight. node-side: {node_side_pov_size} extrinsic_len: {extrinsic_len} runtime: {block_weight_proof_size}, missing: {missing_from_node}. Setting to node-side proof size." + ); + current.accrue(Weight::from_parts(0, missing_from_node), info.class); + } }); Ok(()) } @@ -332,6 +348,82 @@ mod tests { }) } + #[test] + fn sets_to_node_storage_proof_if_higher() { + // The storage proof reported by the proof recorder is higher than what is stored on + // the runtime side. + { + let mut test_ext = setup_test_externalities(&[1000, 1005]); + + test_ext.execute_with(|| { + // Stored in BlockWeight is 5 + set_current_storage_weight(5); + + // Benchmarked storage weight: 10 + let info = DispatchInfo { weight: Weight::from_parts(0, 10), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(1000)); + + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + // We expect that the storage weight was set to the node-side proof size (1005) + + // extrinsics length (150) + assert_eq!(get_storage_weight().total().proof_size(), 1155); + }) + } + + // In this second scenario the proof size on the node side is only lower + // after reclaim happened. + { + let mut test_ext = setup_test_externalities(&[175, 180]); + test_ext.execute_with(|| { + set_current_storage_weight(85); + + // Benchmarked storage weight: 100 + let info = + DispatchInfo { weight: Weight::from_parts(0, 100), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + // After this pre_dispatch, the BlockWeight proof size will be + // 85 (initial) + 100 (benched) + 150 (tx length) = 335 + assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(175)); + + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + + // First we will reclaim 95, which leaves us with 240 BlockWeight. This is lower + // than 180 (proof size hf) + 150 (length), so we expect it to be set to 330. + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + // We expect that the storage weight was set to the node-side proof weight + assert_eq!(get_storage_weight().total().proof_size(), 330); + }) + } + } + #[test] fn does_nothing_without_extension() { let mut test_ext = new_test_ext(); @@ -545,7 +637,7 @@ mod tests { #[test] fn test_nothing_relcaimed() { - let mut test_ext = setup_test_externalities(&[100, 200]); + let mut test_ext = setup_test_externalities(&[0, 100]); test_ext.execute_with(|| { set_current_storage_weight(0); @@ -568,7 +660,7 @@ mod tests { .pre_dispatch(&ALICE, CALL, &info, LEN) .unwrap(); // Should return `setup_test_externalities` proof recorder value: 100. - assert_eq!(pre, Some(100)); + assert_eq!(pre, Some(0)); // The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo` // we always need to call `post_dispatch` to verify that they interoperate correctly. diff --git a/prdoc/pr_5281.prdoc b/prdoc/pr_5281.prdoc new file mode 100644 index 000000000000..60feab412aff --- /dev/null +++ b/prdoc/pr_5281.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: PoV-Reclaim - Set `BlockWeight` to node-side PoV size if mismatch is detected + +doc: + - audience: Runtime Dev + description: | + After this change, the `StorageWeightReclaim` `SignedExtension` will check the node-side PoV size after every + extrinsic. If we detect a case where the returned proof size is higher than the `BlockWeight` value of the + runtime, we set `BlockWeight` to the size returned from the node. + +crates: + - name: cumulus-primitives-storage-weight-reclaim + bump: patch + - name: frame-system + bump: minor diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index 0c6ff2cb8ddb..abacfa7b62cc 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -917,7 +917,7 @@ pub mod pallet { /// Total length (in bytes) for all extrinsics put together, for the current block. #[pallet::storage] - pub(super) type AllExtrinsicsLen = StorageValue<_, u32>; + pub type AllExtrinsicsLen = StorageValue<_, u32>; /// Map of block numbers to block hashes. #[pallet::storage] From 42eb4ec0ad9ab32385bbaefa572c79acc5fbf27d Mon Sep 17 00:00:00 2001 From: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Tue, 13 Aug 2024 23:26:50 +0200 Subject: [PATCH 103/382] [Pools] Ensure members can always exit the pool gracefully (#4998) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves https://github.com/paritytech-secops/srlabs_findings/issues/412 ## Changes - Clear any dangling delegation when member is removed. - Agents need to be killed explicitly when pools are destroyed. - Member withdraw amount is max of their locked funds and the value of their points. --------- Co-authored-by: Gonçalo Pestana Co-authored-by: command-bot <> --- prdoc/pr_4998.prdoc | 20 ++ .../frame/delegated-staking/src/impls.rs | 22 +- substrate/frame/delegated-staking/src/lib.rs | 56 ++- .../frame/delegated-staking/src/tests.rs | 4 +- .../frame/delegated-staking/src/types.rs | 19 +- .../frame/nomination-pools/src/adapter.rs | 51 ++- substrate/frame/nomination-pools/src/lib.rs | 47 ++- substrate/frame/nomination-pools/src/tests.rs | 36 +- .../test-delegate-stake/src/lib.rs | 324 +++++++++++++++++- .../test-delegate-stake/src/mock.rs | 16 +- .../test-transfer-stake/src/lib.rs | 14 +- substrate/primitives/staking/src/lib.rs | 23 +- 12 files changed, 500 insertions(+), 132 deletions(-) create mode 100644 prdoc/pr_4998.prdoc diff --git a/prdoc/pr_4998.prdoc b/prdoc/pr_4998.prdoc new file mode 100644 index 000000000000..41e3886405cc --- /dev/null +++ b/prdoc/pr_4998.prdoc @@ -0,0 +1,20 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Ensure members can always exit the pool gracefully + +doc: + - audience: Runtime Dev + description: | + Ensures when a member wants to withdraw all funds but the pool is not able to provide all their funds, the member + can receive as much as possible and exit pool. Also handles cases where some extra funds held in member's account + is released when they are removed. + +crates: + - name: pallet-delegated-staking + bump: patch + - name: pallet-nomination-pools + bump: major + - name: sp-staking + bump: major + diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index f8df9dfe7b46..8c05b0bcfc22 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -32,28 +32,34 @@ impl DelegationInterface for Pallet { .ok() } + fn agent_transferable_balance(agent: Agent) -> Option { + AgentLedgerOuter::::get(&agent.get()) + .map(|a| a.ledger.unclaimed_withdrawals) + .ok() + } + fn delegator_balance(delegator: Delegator) -> Option { Delegation::::get(&delegator.get()).map(|d| d.amount) } /// Delegate funds to an `Agent`. - fn delegate( - who: Delegator, + fn register_agent( agent: Agent, reward_account: &Self::AccountId, - amount: Self::Balance, ) -> DispatchResult { Pallet::::register_agent( RawOrigin::Signed(agent.clone().get()).into(), reward_account.clone(), - )?; + ) + } - // Delegate the funds from who to the `Agent` account. - Pallet::::delegate_to_agent(RawOrigin::Signed(who.get()).into(), agent.get(), amount) + /// Remove `Agent` registration. + fn remove_agent(agent: Agent) -> DispatchResult { + Pallet::::remove_agent(RawOrigin::Signed(agent.clone().get()).into()) } /// Add more delegation to the `Agent` account. - fn delegate_extra( + fn delegate( who: Delegator, agent: Agent, amount: Self::Balance, @@ -118,7 +124,7 @@ impl DelegationMigrator for Pallet { /// Only used for testing. #[cfg(feature = "runtime-benchmarks")] - fn drop_agent(agent: Agent) { + fn migrate_to_direct_staker(agent: Agent) { >::remove(agent.clone().get()); >::iter() .filter(|(_, delegation)| delegation.agent == agent.clone().get()) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 8203f7513305..1882989cfa7d 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -304,6 +304,27 @@ pub mod pallet { Ok(()) } + /// Remove an account from being an `Agent`. + /// + /// This can only be called if the agent has no delegated funds, no pending slashes and no + /// unclaimed withdrawals. + pub fn remove_agent(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + let ledger = AgentLedger::::get(&who).ok_or(Error::::NotAgent)?; + + ensure!( + ledger.total_delegated == Zero::zero() && + ledger.pending_slash == Zero::zero() && + ledger.unclaimed_withdrawals == Zero::zero(), + Error::::NotAllowed + ); + + // remove provider reference + let _ = frame_system::Pallet::::dec_providers(&who)?; + >::remove(who); + Ok(()) + } + /// Migrate from a `Nominator` account to `Agent` account. /// /// The origin needs to @@ -599,44 +620,19 @@ impl Pallet { ensure!(delegation.agent == agent, Error::::NotAgent); ensure!(delegation.amount >= amount, Error::::NotEnoughFunds); - // if we do not already have enough funds to be claimed, try withdraw some more. - // keep track if we killed the staker in the process. - let stash_killed = if agent_ledger.ledger.unclaimed_withdrawals < amount { + // if we do not already have enough funds to be claimed, try to withdraw some more. + if agent_ledger.ledger.unclaimed_withdrawals < amount { // withdraw account. - let killed = T::CoreStaking::withdraw_unbonded(agent.clone(), num_slashing_spans) + let _ = T::CoreStaking::withdraw_unbonded(agent.clone(), num_slashing_spans) .map_err(|_| Error::::WithdrawFailed)?; // reload agent from storage since withdrawal might have changed the state. agent_ledger = agent_ledger.reload()?; - Some(killed) - } else { - None - }; + } // if we still do not have enough funds to release, abort. ensure!(agent_ledger.ledger.unclaimed_withdrawals >= amount, Error::::NotEnoughFunds); + agent_ledger.remove_unclaimed_withdraw(amount)?.update(); - // Claim withdraw from agent. Kill agent if no delegation left. - // TODO: Ideally if there is a register, there should be an unregister that should - // clean up the agent. Can be improved in future. - if agent_ledger.remove_unclaimed_withdraw(amount)?.update_or_kill()? { - match stash_killed { - Some(killed) => { - // this implies we did a `CoreStaking::withdraw` before release. Ensure - // we killed the staker as well. - ensure!(killed, Error::::BadState); - }, - None => { - // We did not do a `CoreStaking::withdraw` before release. Ensure staker is - // already killed in `CoreStaking`. - ensure!(T::CoreStaking::status(&agent).is_err(), Error::::BadState); - }, - } - - // Remove provider reference for `who`. - let _ = frame_system::Pallet::::dec_providers(&agent).defensive(); - } - - // book keep delegation delegation.amount = delegation .amount .checked_sub(&amount) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index ade0872dd390..bc8bc2dccc3c 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -943,7 +943,7 @@ mod pool_integration { vec![ PoolsEvent::Withdrawn { member: 302, pool_id, balance: 100, points: 100 }, PoolsEvent::Withdrawn { member: 303, pool_id, balance: 200, points: 200 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 303 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 303, released_balance: 0 }, ] ); @@ -1055,7 +1055,7 @@ mod pool_integration { balance: creator_stake, points: creator_stake, }, - PoolsEvent::MemberRemoved { pool_id, member: creator }, + PoolsEvent::MemberRemoved { pool_id, member: creator, released_balance: 0 }, PoolsEvent::Destroyed { pool_id }, ] ); diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 24b457356544..aff282774c3c 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -251,25 +251,10 @@ impl AgentLedgerOuter { self.ledger.update(&key) } - /// Save self and remove if no delegation left. - /// - /// Returns: - /// - true if agent killed. - /// - error if the delegate is in an unexpected state. - pub(crate) fn update_or_kill(self) -> Result { + /// Update agent ledger. + pub(crate) fn update(self) { let key = self.key; - // see if delegate can be killed - if self.ledger.total_delegated == Zero::zero() { - ensure!( - self.ledger.unclaimed_withdrawals == Zero::zero() && - self.ledger.pending_slash == Zero::zero(), - Error::::BadState - ); - >::remove(key); - return Ok(true) - } self.ledger.update(&key); - Ok(false) } /// Reloads self from storage. diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 4d571855e4fe..272b3b60612b 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -106,9 +106,11 @@ pub trait StakeStrategy { /// Balance that can be transferred from pool account to member. /// - /// This is part of the pool balance that is not actively staked. That is, tokens that are - /// in unbonding period or unbonded. - fn transferable_balance(pool_account: Pool) -> Self::Balance; + /// This is part of the pool balance that can be withdrawn. + fn transferable_balance( + pool_account: Pool, + member_account: Member, + ) -> Self::Balance; /// Total balance of the pool including amount that is actively staked. fn total_balance(pool_account: Pool) -> Option; @@ -181,6 +183,9 @@ pub trait StakeStrategy { num_slashing_spans: u32, ) -> DispatchResult; + /// Dissolve the pool account. + fn dissolve(pool_account: Pool) -> DispatchResult; + /// Check if there is any pending slash for the pool. fn pending_slash(pool_account: Pool) -> Self::Balance; @@ -253,12 +258,15 @@ impl, AccountId = T: StakeStrategyType::Transfer } - fn transferable_balance(pool_account: Pool) -> BalanceOf { + fn transferable_balance( + pool_account: Pool, + _: Member, + ) -> BalanceOf { T::Currency::balance(&pool_account.0).saturating_sub(Self::active_stake(pool_account)) } fn total_balance(pool_account: Pool) -> Option> { - Some(T::Currency::total_balance(&pool_account.0)) + Some(T::Currency::total_balance(&pool_account.get())) } fn member_delegation_balance( @@ -300,6 +308,17 @@ impl, AccountId = T: Ok(()) } + fn dissolve(pool_account: Pool) -> DispatchResult { + defensive_assert!( + T::Currency::total_balance(&pool_account.clone().get()).is_zero(), + "dissolving pool should not have any balance" + ); + + // Defensively force set balance to zero. + T::Currency::set_balance(&pool_account.get(), Zero::zero()); + Ok(()) + } + fn pending_slash(_: Pool) -> Self::Balance { // for transfer stake strategy, slashing is greedy and never deferred. Zero::zero() @@ -360,11 +379,14 @@ impl< StakeStrategyType::Delegate } - fn transferable_balance(pool_account: Pool) -> BalanceOf { - Delegation::agent_balance(pool_account.clone().into()) + fn transferable_balance( + pool_account: Pool, + member_account: Member, + ) -> BalanceOf { + Delegation::agent_transferable_balance(pool_account.clone().into()) // pool should always be an agent. .defensive_unwrap_or_default() - .saturating_sub(Self::active_stake(pool_account)) + .min(Delegation::delegator_balance(member_account.into()).unwrap_or_default()) } fn total_balance(pool_account: Pool) -> Option> { @@ -384,12 +406,13 @@ impl< ) -> DispatchResult { match bond_type { BondType::Create => { - // first delegation - Delegation::delegate(who.into(), pool_account.into(), reward_account, amount) + // first delegation. Register agent first. + Delegation::register_agent(pool_account.clone().into(), reward_account)?; + Delegation::delegate(who.into(), pool_account.into(), amount) }, BondType::Extra => { // additional delegation - Delegation::delegate_extra(who.into(), pool_account.into(), amount) + Delegation::delegate(who.into(), pool_account.into(), amount) }, } } @@ -403,6 +426,10 @@ impl< Delegation::withdraw_delegation(who.into(), pool_account.into(), amount, num_slashing_spans) } + fn dissolve(pool_account: Pool) -> DispatchResult { + Delegation::remove_agent(pool_account.into()) + } + fn pending_slash(pool_account: Pool) -> Self::Balance { Delegation::pending_slash(pool_account.into()).defensive_unwrap_or_default() } @@ -433,6 +460,6 @@ impl< #[cfg(feature = "runtime-benchmarks")] fn remove_as_agent(pool: Pool) { - Delegation::drop_agent(pool.into()) + Delegation::migrate_to_direct_staker(pool.into()) } } diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 70ad06e2a4da..9108060ccb2a 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1831,7 +1831,9 @@ pub mod pallet { /// A member has been removed from a pool. /// /// The removal can be voluntary (withdrawn all unbonded funds) or involuntary (kicked). - MemberRemoved { pool_id: PoolId, member: T::AccountId }, + /// Any funds that are still delegated (i.e. dangling delegation) are released and are + /// represented by `released_balance`. + MemberRemoved { pool_id: PoolId, member: T::AccountId, released_balance: BalanceOf }, /// The roles of a pool have been updated to the given new roles. Note that the depositor /// can never change. RolesUpdated { @@ -2342,9 +2344,10 @@ pub mod pallet { // don't exist. This check is also defensive in cases where the unbond pool does not // update its balance (e.g. a bug in the slashing hook.) We gracefully proceed in // order to ensure members can leave the pool and it can be destroyed. - .min(T::StakeAdapter::transferable_balance(Pool::from( - bonded_pool.bonded_account(), - ))); + .min(T::StakeAdapter::transferable_balance( + Pool::from(bonded_pool.bonded_account()), + Member::from(member_account.clone()), + )); // this can fail if the pool uses `DelegateStake` strategy and the member delegation // is not claimed yet. See `Call::migrate_delegation()`. @@ -2368,9 +2371,27 @@ pub mod pallet { // member being reaped. PoolMembers::::remove(&member_account); + + // Ensure any dangling delegation is withdrawn. + let dangling_withdrawal = match T::StakeAdapter::member_delegation_balance( + Member::from(member_account.clone()), + ) { + Some(dangling_delegation) => { + T::StakeAdapter::member_withdraw( + Member::from(member_account.clone()), + Pool::from(bonded_pool.bonded_account()), + dangling_delegation, + num_slashing_spans, + )?; + dangling_delegation + }, + None => Zero::zero(), + }; + Self::deposit_event(Event::::MemberRemoved { pool_id: member.pool_id, member: member_account.clone(), + released_balance: dangling_withdrawal, }); if member_account == bonded_pool.roles.depositor { @@ -3078,16 +3099,11 @@ impl Pallet { T::Currency::total_balance(&reward_account) == Zero::zero(), "could not transfer all amount to depositor while dissolving pool" ); - defensive_assert!( - T::StakeAdapter::total_balance(Pool::from(bonded_pool.bonded_account())) - .unwrap_or_default() == - Zero::zero(), - "dissolving pool should not have any balance" - ); // NOTE: Defensively force set balance to zero. T::Currency::set_balance(&reward_account, Zero::zero()); - // NOTE: With `DelegateStake` strategy, this won't do anything. - T::Currency::set_balance(&bonded_pool.bonded_account(), Zero::zero()); + + // dissolve pool account. + let _ = T::StakeAdapter::dissolve(Pool::from(bonded_account)).defensive(); Self::deposit_event(Event::::Destroyed { pool_id: bonded_pool.id }); // Remove bonded pool metadata. @@ -3525,13 +3541,8 @@ impl Pallet { // this is their balance in the pool let expected_balance = pool_member.total_balance(); - defensive_assert!( - actual_balance >= expected_balance, - "actual balance should always be greater or equal to the expected" - ); - // return the amount to be slashed. - Ok(actual_balance.defensive_saturating_sub(expected_balance)) + Ok(actual_balance.saturating_sub(expected_balance)) } /// Apply freeze on reward account to restrict it from going below ED. diff --git a/substrate/frame/nomination-pools/src/tests.rs b/substrate/frame/nomination-pools/src/tests.rs index 28063c2ecaec..06261699a5b2 100644 --- a/substrate/frame/nomination-pools/src/tests.rs +++ b/substrate/frame/nomination-pools/src/tests.rs @@ -2364,10 +2364,10 @@ mod claim_payout { Event::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, Event::Unbonded { member: 20, pool_id: 1, balance: 20, points: 20, era: 3 }, Event::Withdrawn { member: 20, pool_id: 1, balance: 20, points: 20 }, - Event::MemberRemoved { pool_id: 1, member: 20 }, + Event::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 }, Event::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10, era: 6 }, Event::Withdrawn { member: 10, pool_id: 1, balance: 10, points: 10 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, Event::Destroyed { pool_id: 1 } ] ); @@ -2939,9 +2939,9 @@ mod unbond { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 40, pool_id: 1, points: 6, balance: 6 }, - Event::MemberRemoved { pool_id: 1, member: 40 }, + Event::MemberRemoved { pool_id: 1, member: 40, released_balance: 0 }, Event::Withdrawn { member: 550, pool_id: 1, points: 92, balance: 92 }, - Event::MemberRemoved { pool_id: 1, member: 550 }, + Event::MemberRemoved { pool_id: 1, member: 550, released_balance: 0 }, Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, Event::Unbonded { member: 10, pool_id: 1, points: 2, balance: 2, era: 6 } ] @@ -3688,7 +3688,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 550, pool_id: 1, balance: 275, points: 550 }, - Event::MemberRemoved { pool_id: 1, member: 550 } + Event::MemberRemoved { pool_id: 1, member: 550, released_balance: 0 } ] ); assert_eq!( @@ -3709,7 +3709,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 40, pool_id: 1, balance: 20, points: 40 }, - Event::MemberRemoved { pool_id: 1, member: 40 } + Event::MemberRemoved { pool_id: 1, member: 40, released_balance: 0 } ] ); assert_eq!( @@ -3731,7 +3731,7 @@ mod withdraw_unbonded { vec![ Event::Unbonded { member: 10, pool_id: 1, balance: 5, points: 5, era: 9 }, Event::Withdrawn { member: 10, pool_id: 1, balance: 5, points: 5 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, Event::Destroyed { pool_id: 1 } ] ); @@ -3806,7 +3806,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 40, pool_id: 1, balance: 20, points: 20 }, - Event::MemberRemoved { pool_id: 1, member: 40 } + Event::MemberRemoved { pool_id: 1, member: 40, released_balance: 0 } ] ); @@ -3827,7 +3827,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 550, pool_id: 1, balance: 275, points: 275 }, - Event::MemberRemoved { pool_id: 1, member: 550 } + Event::MemberRemoved { pool_id: 1, member: 550, released_balance: 0 } ] ); assert!(SubPoolsStorage::::get(1).unwrap().with_era.is_empty()); @@ -3862,7 +3862,7 @@ mod withdraw_unbonded { vec![ Event::Unbonded { member: 10, pool_id: 1, points: 5, balance: 5, era: 6 }, Event::Withdrawn { member: 10, pool_id: 1, points: 5, balance: 5 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, Event::Destroyed { pool_id: 1 } ] ); @@ -4018,9 +4018,9 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 100, pool_id: 1, points: 100, balance: 100 }, - Event::MemberRemoved { pool_id: 1, member: 100 }, + Event::MemberRemoved { pool_id: 1, member: 100, released_balance: 0 }, Event::Withdrawn { member: 200, pool_id: 1, points: 200, balance: 200 }, - Event::MemberRemoved { pool_id: 1, member: 200 } + Event::MemberRemoved { pool_id: 1, member: 200, released_balance: 0 } ] ); }); @@ -4070,7 +4070,7 @@ mod withdraw_unbonded { Event::Bonded { member: 100, pool_id: 1, bonded: 100, joined: true }, Event::Unbonded { member: 100, pool_id: 1, points: 100, balance: 100, era: 3 }, Event::Withdrawn { member: 100, pool_id: 1, points: 100, balance: 100 }, - Event::MemberRemoved { pool_id: 1, member: 100 } + Event::MemberRemoved { pool_id: 1, member: 100, released_balance: 0 } ] ); }); @@ -4310,7 +4310,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 100, pool_id: 1, points: 25, balance: 25 }, - Event::MemberRemoved { pool_id: 1, member: 100 } + Event::MemberRemoved { pool_id: 1, member: 100, released_balance: 0 } ] ); }) @@ -4548,7 +4548,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 10, pool_id: 1, points: 13, balance: 13 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, Event::Destroyed { pool_id: 1 }, ] ); @@ -4588,7 +4588,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 20, pool_id: 1, balance: 20, points: 20 }, - Event::MemberRemoved { pool_id: 1, member: 20 } + Event::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 } ] ); @@ -4626,7 +4626,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 10, pool_id: 1, points: 10, balance: 10 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, Event::Destroyed { pool_id: 1 }, ] ); @@ -4672,7 +4672,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 10, pool_id: 1, points: 10, balance: 10 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, Event::Destroyed { pool_id: 1 }, ] ); diff --git a/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs b/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs index 51f6470f90d0..a50a6b73f5fc 100644 --- a/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs +++ b/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs @@ -152,9 +152,9 @@ fn pool_lifecycle_e2e() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 20, pool_id: 1, points: 10, balance: 10 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 20 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 }, PoolsEvent::Withdrawn { member: 21, pool_id: 1, points: 10, balance: 10 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 21 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 }, ] ); @@ -193,7 +193,7 @@ fn pool_lifecycle_e2e() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, PoolsEvent::Destroyed { pool_id: 1 } ] ); @@ -476,10 +476,10 @@ fn pool_slash_e2e() { vec![ // 20 had unbonded 10 safely, and 10 got slashed by half. PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 10 + 5, points: 20 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 20 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 }, // 21 unbonded all of it after the slash PoolsEvent::Withdrawn { member: 21, pool_id: 1, balance: 5 + 5, points: 15 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 21 } + PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 } ] ); assert_eq!( @@ -525,7 +525,7 @@ fn pool_slash_e2e() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 10, pool_id: 1, balance: 10 + 15, points: 30 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, PoolsEvent::Destroyed { pool_id: 1 } ] ); @@ -1160,7 +1160,7 @@ fn pool_migration_e2e() { vec![ PoolsEvent::Withdrawn { member: 21, pool_id: 1, balance: 10, points: 10 }, // 21 was fully unbonding and removed from pool. - PoolsEvent::MemberRemoved { member: 21, pool_id: 1 }, + PoolsEvent::MemberRemoved { member: 21, pool_id: 1, released_balance: 0 }, PoolsEvent::Withdrawn { member: 22, pool_id: 1, balance: 5, points: 5 }, ] ); @@ -1183,3 +1183,313 @@ fn pool_migration_e2e() { ); }) } + +#[test] +fn pool_no_dangling_delegation() { + new_test_ext().execute_with(|| { + ExistentialDeposit::set(1); + assert_eq!(Balances::minimum_balance(), 1); + assert_eq!(Staking::current_era(), None); + // pool creator + let alice = 10; + let bob = 20; + let charlie = 21; + + // create the pool, we know this has id 1. + assert_ok!(Pools::create(RuntimeOrigin::signed(alice), 40, alice, alice, alice)); + assert_eq!(LastPoolId::::get(), 1); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Created { depositor: alice, pool_id: 1 }, + PoolsEvent::Bonded { member: alice, pool_id: 1, bonded: 40, joined: true }, + ] + ); + assert_eq!( + delegated_staking_events_since_last_call(), + vec![DelegatedStakingEvent::Delegated { + agent: POOL1_BONDED, + delegator: alice, + amount: 40 + },] + ); + + assert_eq!( + Payee::::get(POOL1_BONDED), + Some(RewardDestination::Account(POOL1_REWARD)) + ); + + // have two members join + assert_ok!(Pools::join(RuntimeOrigin::signed(bob), 20, 1)); + assert_ok!(Pools::join(RuntimeOrigin::signed(charlie), 20, 1)); + + assert_eq!( + staking_events_since_last_call(), + vec![ + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 20 }, + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 20 } + ] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Bonded { member: bob, pool_id: 1, bonded: 20, joined: true }, + PoolsEvent::Bonded { member: charlie, pool_id: 1, bonded: 20, joined: true }, + ] + ); + assert_eq!( + delegated_staking_events_since_last_call(), + vec![ + DelegatedStakingEvent::Delegated { + agent: POOL1_BONDED, + delegator: bob, + amount: 20 + }, + DelegatedStakingEvent::Delegated { + agent: POOL1_BONDED, + delegator: charlie, + amount: 20 + }, + ] + ); + + // now let's progress a bit. + CurrentEra::::set(Some(1)); + + // bob is completely unbonding + assert_ok!(Pools::unbond(RuntimeOrigin::signed(bob), 20, 20)); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 20 },] + ); + assert_eq!( + pool_events_since_last_call(), + vec![PoolsEvent::Unbonded { member: bob, pool_id: 1, balance: 20, points: 20, era: 4 }] + ); + + // this era will get slashed + CurrentEra::::set(Some(2)); + + assert_ok!(Pools::unbond(RuntimeOrigin::signed(alice), 10, 10)); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(charlie), 21, 10)); + + assert_eq!( + staking_events_since_last_call(), + vec![ + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, + ] + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Unbonded { member: alice, pool_id: 1, balance: 10, points: 10, era: 5 }, + PoolsEvent::Unbonded { + member: charlie, + pool_id: 1, + balance: 10, + points: 10, + era: 5 + }, + ] + ); + + // At this point, bob's 20 that is unlocking is safe from slash, 10 (alice) + 10 (charlie) + // are also unlocking but vulnerable to slash, and another 40 are active and vulnerable to + // slash. Let's slash half of them. + pallet_staking::slashing::do_slash::( + &POOL1_BONDED, + 30, + &mut Default::default(), + &mut Default::default(), + 2, // slash era 2, affects chunks at era 5 onwards. + ); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 30 }] + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + // unbonding pool of 20 for era 5 has been slashed to 10 + PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 5, balance: 10 }, + // active stake of 40 has been slashed to half + PoolsEvent::PoolSlashed { pool_id: 1, balance: 20 } /* no slash to era 4 + * unbonding pool */ + ] + ); + + // alice's initial stake of 40 is reduced to half + assert_eq!(PoolMembers::::get(alice).unwrap().total_balance(), 20); + // bob unbonded in era 1 and is safe from slash + assert_eq!(PoolMembers::::get(bob).unwrap().total_balance(), 20); + // charlie's initial stake of 20 is slashed to half + assert_eq!(PoolMembers::::get(charlie).unwrap().total_balance(), 10); + + // apply pending slash to alice. + assert_eq!(Pools::api_member_pending_slash(alice), 20); + assert_ok!(Pools::apply_slash(RuntimeOrigin::signed(10), alice)); + // apply pending slash to charlie. + assert_eq!(Pools::api_member_pending_slash(charlie), 10); + assert_ok!(Pools::apply_slash(RuntimeOrigin::signed(10), charlie)); + // no pending slash for bob + assert_eq!(Pools::api_member_pending_slash(bob), 0); + + assert_eq!( + delegated_staking_events_since_last_call(), + vec![ + DelegatedStakingEvent::Slashed { + agent: POOL1_BONDED, + delegator: alice, + amount: 20 + }, + DelegatedStakingEvent::Slashed { + agent: POOL1_BONDED, + delegator: charlie, + amount: 10 + }, + ] + ); + + // go forward to an era after PostUnbondingPoolsWindow = 10 ends for era 5. + CurrentEra::::set(Some(15)); + // At this point subpools will all be merged in no-era causing Bob to lose some value while + // Alice and Charlie will gain some value. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(charlie), charlie, 10)); + + // Now alice and charlie has less balance locked than their contribution. + assert_eq!(Balances::total_balance_on_hold(&alice), 20); + assert_eq!(PoolMembers::::get(alice).unwrap().total_balance(), 22); + assert_eq!(Balances::total_balance_on_hold(&charlie), 10); + assert_eq!(PoolMembers::::get(charlie).unwrap().total_balance(), 12); + + // and bob has more balance locked than his contribution. + assert_eq!(Balances::total_balance_on_hold(&bob), 20); + assert_eq!(PoolMembers::::get(bob).unwrap().total_balance(), 15); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 5 }] + ); + assert_eq!( + pool_events_since_last_call(), + vec![PoolsEvent::Unbonded { + member: charlie, + pool_id: 1, + balance: 5, + points: 5, + era: 18 + }] + ); + + // When bob withdraws all, he gets all his locked funds back. + let bob_pre_withdraw_balance = Balances::free_balance(&bob); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(bob), bob, 0)); + assert_eq!(Balances::free_balance(&bob), bob_pre_withdraw_balance + 20); + assert_eq!(Balances::total_balance_on_hold(&bob), 0); + assert!(!PoolMembers::::contains_key(bob)); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 30 },] + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Withdrawn { pool_id: 1, member: bob, balance: 15, points: 20 }, + // dangling delegation of 5 is released + PoolsEvent::MemberRemoved { pool_id: 1, member: bob, released_balance: 5 }, + ] + ); + assert_eq!( + delegated_staking_events_since_last_call(), + vec![ + DelegatedStakingEvent::Released { agent: POOL1_BONDED, delegator: bob, amount: 15 }, + // the second release is the dangling delegation when member is removed. + DelegatedStakingEvent::Released { agent: POOL1_BONDED, delegator: bob, amount: 5 }, + ] + ); + + // Charlie can withdraw as much as he has locked. + CurrentEra::::set(Some(18)); + let charlie_pre_withdraw_balance = Balances::free_balance(&charlie); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(charlie), charlie, 0)); + // Charlie's total balance was 12, but we don't have enough funds to unlock. We try the best + // effort and unlock 10. + assert_eq!(Balances::free_balance(&charlie), charlie_pre_withdraw_balance + 10); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 5 },] + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Withdrawn { pool_id: 1, member: charlie, balance: 10, points: 15 }, + PoolsEvent::MemberRemoved { member: charlie, pool_id: 1, released_balance: 0 } + ] + ); + assert_eq!( + delegated_staking_events_since_last_call(), + vec![DelegatedStakingEvent::Released { + agent: POOL1_BONDED, + delegator: charlie, + amount: 10 + },] + ); + + // Set pools to destroying so alice can withdraw + assert_ok!(Pools::set_state(RuntimeOrigin::signed(alice), 1, PoolState::Destroying)); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(alice), alice, 30)); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 15 }] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, + PoolsEvent::Unbonded { + member: alice, + pool_id: 1, + points: 15, + balance: 15, + era: 21 + } + ] + ); + + CurrentEra::::set(Some(21)); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(alice), alice, 0)); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 15 }] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Withdrawn { member: alice, pool_id: 1, balance: 20, points: 25 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: alice, released_balance: 0 }, + PoolsEvent::Destroyed { pool_id: 1 } + ] + ); + + // holds for all members are released. + assert_eq!(Balances::total_balance_on_hold(&alice), 0); + assert_eq!(Balances::total_balance_on_hold(&bob), 0); + assert_eq!(Balances::total_balance_on_hold(&charlie), 0); + }); +} diff --git a/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs b/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs index ed47932a323b..d1bc4ef8ff28 100644 --- a/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs +++ b/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs @@ -154,11 +154,14 @@ impl pallet_nomination_pools::adapter::StakeStrategy for MockAdapter { } DelegateStake::strategy_type() } - fn transferable_balance(pool_account: Pool) -> Self::Balance { + fn transferable_balance( + pool_account: Pool, + member_account: Member, + ) -> Self::Balance { if LegacyAdapter::get() { - return TransferStake::transferable_balance(pool_account) + return TransferStake::transferable_balance(pool_account, member_account) } - DelegateStake::transferable_balance(pool_account) + DelegateStake::transferable_balance(pool_account, member_account) } fn total_balance(pool_account: Pool) -> Option { @@ -200,6 +203,13 @@ impl pallet_nomination_pools::adapter::StakeStrategy for MockAdapter { DelegateStake::member_withdraw(who, pool_account, amount, num_slashing_spans) } + fn dissolve(pool_account: Pool) -> DispatchResult { + if LegacyAdapter::get() { + return TransferStake::dissolve(pool_account) + } + DelegateStake::dissolve(pool_account) + } + fn pending_slash(pool_account: Pool) -> Self::Balance { if LegacyAdapter::get() { return TransferStake::pending_slash(pool_account) diff --git a/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs b/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs index aa9135025900..28e978bba0e5 100644 --- a/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs +++ b/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs @@ -145,9 +145,9 @@ fn pool_lifecycle_e2e() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 20, pool_id: 1, points: 10, balance: 10 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 20 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 }, PoolsEvent::Withdrawn { member: 21, pool_id: 1, points: 10, balance: 10 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 21 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 }, ] ); @@ -186,7 +186,7 @@ fn pool_lifecycle_e2e() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, PoolsEvent::Destroyed { pool_id: 1 } ] ); @@ -275,7 +275,7 @@ fn destroy_pool_with_erroneous_consumer() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, PoolsEvent::Destroyed { pool_id: 1 } ] ); @@ -558,10 +558,10 @@ fn pool_slash_e2e() { vec![ // 20 had unbonded 10 safely, and 10 got slashed by half. PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 10 + 5, points: 20 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 20 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 }, // 21 unbonded all of it after the slash PoolsEvent::Withdrawn { member: 21, pool_id: 1, balance: 5 + 5, points: 15 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 21 } + PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 } ] ); assert_eq!( @@ -607,7 +607,7 @@ fn pool_slash_e2e() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 10, pool_id: 1, balance: 10 + 15, points: 30 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, PoolsEvent::Destroyed { pool_id: 1 } ] ); diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 03c1af50240b..5e94524816a0 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -525,23 +525,26 @@ pub trait DelegationInterface { /// This takes into account any pending slashes to `Agent` against the delegated balance. fn agent_balance(agent: Agent) -> Option; + /// Returns the total amount of funds that is unbonded and can be withdrawn from the `Agent` + /// account. `None` if not an `Agent`. + fn agent_transferable_balance(agent: Agent) -> Option; + /// Returns the total amount of funds delegated. `None` if not a `Delegator`. fn delegator_balance(delegator: Delegator) -> Option; - /// Delegate funds to `Agent`. - /// - /// Only used for the initial delegation. Use [`Self::delegate_extra`] to add more delegation. - fn delegate( - delegator: Delegator, + /// Register `Agent` such that it can accept delegation. + fn register_agent( agent: Agent, reward_account: &Self::AccountId, - amount: Self::Balance, ) -> DispatchResult; - /// Add more delegation to the `Agent`. + /// Removes `Agent` registration. /// - /// If this is the first delegation, use [`Self::delegate`] instead. - fn delegate_extra( + /// This should only be allowed if the agent has no staked funds. + fn remove_agent(agent: Agent) -> DispatchResult; + + /// Add delegation to the `Agent`. + fn delegate( delegator: Delegator, agent: Agent, amount: Self::Balance, @@ -616,7 +619,7 @@ pub trait DelegationMigrator { /// /// Also removed from [`StakingUnchecked`] as a Virtual Staker. Useful for testing. #[cfg(feature = "runtime-benchmarks")] - fn drop_agent(agent: Agent); + fn migrate_to_direct_staker(agent: Agent); } sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); From 0cd577ba1c4995500eb3ed10330d93402177a53b Mon Sep 17 00:00:00 2001 From: Jeeyong Um Date: Wed, 14 Aug 2024 06:54:51 +0900 Subject: [PATCH 104/382] Minor clean up (#5284) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR performs minor code cleanup to reduce verbosity. Since the compiler has already optimized out indirect calls in the existing code, these changes improve readability but do not affect performance. --------- Co-authored-by: Bastian Köcher --- prdoc/pr_5284.prdoc | 7 ++++++ substrate/primitives/runtime/src/lib.rs | 31 +++++++++---------------- 2 files changed, 18 insertions(+), 20 deletions(-) create mode 100644 prdoc/pr_5284.prdoc diff --git a/prdoc/pr_5284.prdoc b/prdoc/pr_5284.prdoc new file mode 100644 index 000000000000..a3244a82c860 --- /dev/null +++ b/prdoc/pr_5284.prdoc @@ -0,0 +1,7 @@ +title: Minor clean up +author: conr2d +topic: runtime + +crates: + - name: sp-runtime + bump: none diff --git a/substrate/primitives/runtime/src/lib.rs b/substrate/primitives/runtime/src/lib.rs index d313d23395a0..fd10dee2a7c5 100644 --- a/substrate/primitives/runtime/src/lib.rs +++ b/substrate/primitives/runtime/src/lib.rs @@ -438,10 +438,10 @@ impl TryFrom for ecdsa::Public { #[cfg(feature = "std")] impl std::fmt::Display for MultiSigner { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - match *self { - Self::Ed25519(ref who) => write!(fmt, "ed25519: {}", who), - Self::Sr25519(ref who) => write!(fmt, "sr25519: {}", who), - Self::Ecdsa(ref who) => write!(fmt, "ecdsa: {}", who), + match self { + Self::Ed25519(who) => write!(fmt, "ed25519: {}", who), + Self::Sr25519(who) => write!(fmt, "sr25519: {}", who), + Self::Ecdsa(who) => write!(fmt, "ecdsa: {}", who), } } } @@ -449,23 +449,14 @@ impl std::fmt::Display for MultiSigner { impl Verify for MultiSignature { type Signer = MultiSigner; fn verify>(&self, mut msg: L, signer: &AccountId32) -> bool { - match (self, signer) { - (Self::Ed25519(ref sig), who) => match ed25519::Public::from_slice(who.as_ref()) { - Ok(signer) => sig.verify(msg, &signer), - Err(()) => false, - }, - (Self::Sr25519(ref sig), who) => match sr25519::Public::from_slice(who.as_ref()) { - Ok(signer) => sig.verify(msg, &signer), - Err(()) => false, - }, - (Self::Ecdsa(ref sig), who) => { + let who: [u8; 32] = *signer.as_ref(); + match self { + Self::Ed25519(sig) => sig.verify(msg, &who.into()), + Self::Sr25519(sig) => sig.verify(msg, &who.into()), + Self::Ecdsa(sig) => { let m = sp_io::hashing::blake2_256(msg.get()); - match sp_io::crypto::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) { - Ok(pubkey) => - &sp_io::hashing::blake2_256(pubkey.as_ref()) == - >::as_ref(who), - _ => false, - } + sp_io::crypto::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) + .map_or(false, |pubkey| sp_io::hashing::blake2_256(&pubkey) == who) }, } } From be74fe921add60f79e2d11548bfa5a7d17e063ff Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 14 Aug 2024 10:06:16 +0200 Subject: [PATCH 105/382] Migrate foreign assets v3::Location to v4::Location (#4129) In the move from XCMv3 to XCMv4, the `AssetId` for `ForeignAssets` in `asset-hub-rococo` and `asset-hub-westend` was left as `v3::Location` to be later migrated to `v4::Location`. This is that migration PR. Because the encoding of `v3::Location` and `v4::Location` is the same, we don't need to do any data migration, the keys will still be decodable. The [original idea by Jan](https://github.com/paritytech/polkadot/pull/7236) was to make the v4 changes in v3 since the ABI (the encoding/decoding) didn't change. Corroborated the ABI is the same iterating over all storage, the code is on [another branch](https://github.com/paritytech/polkadot-sdk/blob/cisco-assert-v3-v4-encodings-equal/cumulus/parachains/runtimes/assets/migrations/src/foreign_assets_to_v4/mod.rs). We will need a data migration when we want to update from `v4::Location` to `v5::Location` because of [the accepted RFC changing the NetworkId enum](https://github.com/polkadot-fellows/RFCs/pull/108). I'll configure MBMs (Multi-Block Migrations) then and make the actual migration. Fixes https://github.com/paritytech/polkadot-sdk/issues/4128 --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> --- Cargo.lock | 924 ++++++++++-------- .../assets/asset-hub-rococo/src/lib.rs | 2 +- .../assets/asset-hub-westend/src/lib.rs | 2 +- .../emulated/common/src/lib.rs | 10 +- .../assets/asset-hub-rococo/src/tests/send.rs | 11 +- .../assets/asset-hub-rococo/src/tests/swap.rs | 41 +- .../asset-hub-rococo/src/tests/teleport.rs | 6 +- .../tests/assets/asset-hub-westend/src/lib.rs | 5 +- .../asset-hub-westend/src/tests/send.rs | 11 +- .../asset-hub-westend/src/tests/swap.rs | 38 +- .../asset-hub-westend/src/tests/teleport.rs | 6 +- .../bridges/bridge-hub-rococo/src/lib.rs | 3 +- .../src/tests/asset_transfers.rs | 58 +- .../bridge-hub-rococo/src/tests/mod.rs | 18 +- .../bridge-hub-rococo/src/tests/send_xcm.rs | 2 +- .../bridges/bridge-hub-westend/src/lib.rs | 3 +- .../src/tests/asset_transfers.rs | 50 +- .../bridge-hub-westend/src/tests/mod.rs | 22 +- .../assets/asset-hub-rococo/src/lib.rs | 113 +-- .../assets/asset-hub-rococo/src/tests/mod.rs | 72 ++ .../assets/asset-hub-rococo/src/xcm_config.rs | 15 +- .../assets/asset-hub-rococo/tests/tests.rs | 162 ++- .../assets/asset-hub-westend/src/lib.rs | 47 +- .../asset-hub-westend/src/xcm_config.rs | 15 +- .../assets/asset-hub-westend/tests/tests.rs | 118 ++- .../assets/test-utils/src/test_cases.rs | 48 +- .../test-utils/src/test_cases_over_bridge.rs | 17 +- prdoc/pr_4129.prdoc | 17 + substrate/frame/assets/Cargo.toml | 1 - 29 files changed, 951 insertions(+), 886 deletions(-) create mode 100644 cumulus/parachains/runtimes/assets/asset-hub-rococo/src/tests/mod.rs create mode 100644 prdoc/pr_4129.prdoc diff --git a/Cargo.lock b/Cargo.lock index 60be41563299..2593452fb869 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,6 +42,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "aead" version = "0.5.2" @@ -52,6 +61,18 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "opaque-debug 0.3.0", +] + [[package]] name = "aes" version = "0.8.3" @@ -63,17 +84,31 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "aes-gcm" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc3be92e19a7ef47457b8e6f90707e12b6ac5d20c6f3866584fa3be0787d839f" +dependencies = [ + "aead 0.4.3", + "aes 0.7.5", + "cipher 0.3.0", + "ctr 0.7.0", + "ghash 0.4.4", + "subtle 2.5.0", +] + [[package]] name = "aes-gcm" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ - "aead", - "aes", + "aead 0.5.2", + "aes 0.8.3", "cipher 0.4.4", - "ctr", - "ghash", + "ctr 0.9.2", + "ghash 0.5.0", "subtle 2.5.0", ] @@ -157,9 +192,9 @@ dependencies = [ "dunce", "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", "syn-solidity", "tiny-keccak", ] @@ -228,9 +263,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" @@ -284,9 +319,9 @@ dependencies = [ "include_dir", "itertools 0.10.5", "proc-macro-error", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -518,7 +553,7 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -620,7 +655,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -661,9 +696,9 @@ dependencies = [ [[package]] name = "array-bytes" -version = "6.2.2" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f840fb7195bcfc5e17ea40c26e5ce6d5b9ce5d584466e17703209657e459ae0" +checksum = "5d5dde061bd34119e902bbb2d9b90c5692635cf59fb91d582c2b68043f1b8293" [[package]] name = "arrayref" @@ -724,7 +759,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", "synstructure 0.12.6", @@ -736,9 +771,9 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", "synstructure 0.13.1", ] @@ -748,7 +783,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -759,16 +794,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] name = "assert_cmd" -version = "2.0.14" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" +checksum = "bc65048dd435533bb1baf2ed9956b9a278fbfdcf90301b39ee117f06c0199d37" dependencies = [ "anstyle", "bstr", @@ -1179,14 +1214,14 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" dependencies = [ - "async-lock 3.4.0", + "async-lock 3.3.0", "cfg-if", "concurrent-queue", "futures-io", "futures-lite 2.3.0", "parking", "polling 3.4.0", - "rustix 0.38.21", + "rustix 0.38.25", "slab", "tracing", "windows-sys 0.52.0", @@ -1203,13 +1238,13 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.4.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ - "event-listener 5.2.0", + "event-listener 4.0.3", "event-listener-strategy", - "pin-project-lite", + "pin-project-lite 0.2.12", ] [[package]] @@ -1263,7 +1298,7 @@ dependencies = [ "log", "memchr", "once_cell", - "pin-project-lite", + "pin-project-lite 0.2.12", "pin-utils", "slab", "wasm-bindgen-futures", @@ -1277,7 +1312,7 @@ checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", - "pin-project-lite", + "pin-project-lite 0.2.12", ] [[package]] @@ -1286,9 +1321,9 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -1303,9 +1338,9 @@ version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -1318,7 +1353,7 @@ dependencies = [ "futures-sink", "futures-util", "memchr", - "pin-project-lite", + "pin-project-lite 0.2.12", ] [[package]] @@ -1362,7 +1397,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -1508,12 +1543,12 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease 0.2.12", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "regex", "rustc-hash", "shlex", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -2590,6 +2625,18 @@ dependencies = [ "keystream", ] +[[package]] +name = "chacha20" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "zeroize", +] + [[package]] name = "chacha20" version = "0.9.1" @@ -2603,14 +2650,14 @@ dependencies = [ [[package]] name = "chacha20poly1305" -version = "0.10.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" dependencies = [ - "aead", - "chacha20", - "cipher 0.4.4", - "poly1305", + "aead 0.4.3", + "chacha20 0.8.2", + "cipher 0.3.0", + "poly1305 0.7.2", "zeroize", ] @@ -2715,6 +2762,15 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "cipher" version = "0.4.4" @@ -2723,7 +2779,6 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", - "zeroize", ] [[package]] @@ -2790,9 +2845,9 @@ dependencies = [ [[package]] name = "clap-num" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488557e97528174edaa2ee268b23a809e0c598213a4bbcb4f34575a46fda147e" +checksum = "0e063d263364859dc54fb064cedb7c122740cd4733644b14b176c097f51e8ab7" dependencies = [ "num-traits", ] @@ -2827,7 +2882,7 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -2839,9 +2894,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -3026,7 +3081,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d51beaa537d73d2d1ff34ee70bc095f170420ab2ec5d687ecd3ec2b0d092514b" dependencies = [ "nom", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -3039,20 +3094,19 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" -version = "2.0.4" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ - "is-terminal", "lazy_static", "windows-sys 0.48.0", ] [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "memchr", @@ -3611,9 +3665,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2b432c56615136f8dba245fed7ec3d5518c500a31108661067e61e72fe7e6bc" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ "crc-catalog", ] @@ -3763,6 +3817,15 @@ dependencies = [ "subtle 2.5.0", ] +[[package]] +name = "ctr" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a232f92a03f37dd7d7dd2adc67166c77e9cd88de5b019b9a9eecfaeaf7bfd481" +dependencies = [ + "cipher 0.3.0", +] + [[package]] name = "ctr" version = "0.9.2" @@ -4149,9 +4212,9 @@ name = "cumulus-pallet-parachain-system-proc-macro" version = "0.6.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -4648,9 +4711,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.72+curl-8.6.0" +version = "0.4.73+curl-8.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" +checksum = "450ab250ecf17227c39afb9a2dd9261dc0035cb80f2612472fc0c4aac2dcb84d" dependencies = [ "cc", "libc", @@ -4684,9 +4747,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -4723,10 +4786,10 @@ dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "scratch", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -4741,9 +4804,9 @@ version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50c49547d73ba8dcfd4ad7325d64c6d5391ff4224d498fc39a6f3f49825a530d" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -4834,12 +4897,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" [[package]] name = "derivative" @@ -4847,7 +4907,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -4858,9 +4918,9 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -4869,9 +4929,9 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -4881,7 +4941,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "rustc_version 0.4.0", "syn 1.0.109", @@ -4977,9 +5037,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -5037,10 +5097,10 @@ dependencies = [ "common-path", "derive-syn-parse", "once_cell", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "regex", - "syn 2.0.61", + "syn 2.0.58", "termcolor", "toml 0.8.8", "walkdir", @@ -5086,7 +5146,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -5232,7 +5292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -5244,9 +5304,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -5264,9 +5324,9 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -5275,9 +5335,9 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -5353,11 +5413,12 @@ dependencies = [ [[package]] name = "erased-serde" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" dependencies = [ "serde", + "typeid", ] [[package]] @@ -5440,23 +5501,23 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.2.0" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" dependencies = [ "concurrent-queue", "parking", - "pin-project-lite", + "pin-project-lite 0.2.12", ] [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener 5.2.0", - "pin-project-lite", + "event-listener 4.0.3", + "pin-project-lite 0.2.12", ] [[package]] @@ -5478,9 +5539,9 @@ dependencies = [ "file-guard", "fs-err", "prettyplease 0.2.12", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -5550,9 +5611,9 @@ dependencies = [ "expander", "indexmap 2.2.3", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -5753,9 +5814,9 @@ dependencies = [ [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -5882,11 +5943,11 @@ dependencies = [ "frame-support", "parity-scale-codec", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "scale-info", "sp-arithmetic", - "syn 2.0.61", + "syn 2.0.58", "trybuild", ] @@ -6077,7 +6138,7 @@ dependencies = [ "parity-scale-codec", "pretty_assertions", "proc-macro-warning 1.0.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "regex", "scale-info", @@ -6087,7 +6148,7 @@ dependencies = [ "sp-metadata-ir", "sp-runtime", "static_assertions", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -6096,18 +6157,18 @@ version = "10.0.0" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] name = "frame-support-procedural-tools-derive" version = "11.0.0" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -6250,7 +6311,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29f9df8a11882c4e3335eb2d18a0137c505d9ca927470b0cac9c6f0ae07d28f7" dependencies = [ - "rustix 0.38.21", + "rustix 0.38.25", "windows-sys 0.48.0", ] @@ -6336,7 +6397,7 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite", + "pin-project-lite 0.2.12", "waker-fn", ] @@ -6347,7 +6408,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ "futures-core", - "pin-project-lite", + "pin-project-lite 0.2.12", ] [[package]] @@ -6356,9 +6417,9 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -6402,7 +6463,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite", + "pin-project-lite 0.2.12", "pin-utils", "slab", ] @@ -6480,6 +6541,16 @@ dependencies = [ "rand_core", ] +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug 0.3.0", + "polyval 0.5.3", +] + [[package]] name = "ghash" version = "0.5.0" @@ -6487,7 +6558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" dependencies = [ "opaque-debug 0.3.0", - "polyval", + "polyval 0.6.1", ] [[package]] @@ -6605,9 +6676,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.26" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -6758,9 +6829,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" [[package]] name = "hex-literal" @@ -6860,7 +6931,7 @@ checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http 0.2.9", - "pin-project-lite", + "pin-project-lite 0.2.12", ] [[package]] @@ -6883,7 +6954,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.0", - "pin-project-lite", + "pin-project-lite 0.2.12", ] [[package]] @@ -6906,21 +6977,21 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.29" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2 0.3.26", + "h2 0.3.24", "http 0.2.9", "http-body 0.4.5", "httparse", "httpdate", "itoa", - "pin-project-lite", + "pin-project-lite 0.2.12", "socket2 0.5.7", "tokio", "tower-service", @@ -6943,7 +7014,7 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project-lite", + "pin-project-lite 0.2.12", "smallvec", "tokio", "want", @@ -6957,7 +7028,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.9", - "hyper 0.14.29", + "hyper 0.14.30", "log", "rustls 0.21.7", "rustls-native-certs 0.6.3", @@ -6995,7 +7066,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "hyper 1.3.1", - "pin-project-lite", + "pin-project-lite 0.2.12", "socket2 0.5.7", "tokio", "tower", @@ -7047,16 +7118,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "if-addrs" version = "0.10.2" @@ -7097,7 +7158,7 @@ dependencies = [ "bytes", "futures", "http 0.2.9", - "hyper 0.14.29", + "hyper 0.14.30", "log", "rand", "tokio", @@ -7149,7 +7210,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -7169,7 +7230,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", ] @@ -7294,7 +7355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.2", - "rustix 0.38.21", + "rustix 0.38.25", "windows-sys 0.48.0", ] @@ -7513,9 +7574,9 @@ checksum = "7895f186d5921065d96e16bd795e5ca89ac8356ec423fafc6e3d7cf8ec11aee4" dependencies = [ "heck 0.5.0", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -7764,9 +7825,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libnghttp2-sys" -version = "0.1.9+1.58.0" +version = "0.1.10+1.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b57e858af2798e167e709b9d969325b6d8e9d50232fcbc494d7d54f976854a64" +checksum = "959c25552127d2e1fa72f0e52548ec04fc386e827ba71a7bd01db46a447dc135" dependencies = [ "cc", "libc", @@ -8101,9 +8162,9 @@ checksum = "c4d5ec2a3df00c7836d7696c136274c9c59705bac69133253696a6c932cd1d74" dependencies = [ "heck 0.4.1", "proc-macro-warning 0.4.2", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -8174,9 +8235,9 @@ dependencies = [ [[package]] name = "libp2p-websocket" -version = "0.42.2" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "004ee9c4a4631435169aee6aad2f62e3984dc031c43b6d29731e8e82a016c538" +checksum = "3facf0691bab65f571bc97c6c65ffa836248ca631d631b7691ac91deb7fceb5f" dependencies = [ "either", "futures", @@ -8185,10 +8246,9 @@ dependencies = [ "libp2p-identity", "log", "parking_lot 0.12.3", - "pin-project-lite", + "quicksink", "rw-stream-sink", - "soketto 0.8.0", - "thiserror", + "soketto 0.7.1", "url", "webpki-roots 0.25.2", ] @@ -8328,9 +8388,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "lioness" @@ -8391,7 +8451,7 @@ dependencies = [ "rand", "rcgen", "ring 0.16.20", - "rustls 0.20.9", + "rustls 0.20.8", "serde", "sha2 0.10.8", "simple-dns", @@ -8517,7 +8577,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -8529,9 +8589,9 @@ dependencies = [ "const-random", "derive-syn-parse", "macro_magic_core_macros", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -8540,9 +8600,9 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -8553,7 +8613,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -8604,9 +8664,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memfd" @@ -8915,7 +8975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -8927,9 +8987,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" dependencies = [ "cfg-if", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -9039,7 +9099,7 @@ checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro-error", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", "synstructure 0.12.6", @@ -9087,7 +9147,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -9176,9 +9236,9 @@ dependencies = [ [[package]] name = "network-interface" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae72fd9dbd7f55dda80c00d66acc3b2130436fcba9ea89118fc508eaae48dfb0" +checksum = "a4a43439bf756eed340bdf8feba761e2d50c7d47175d87545cd5cbe4a137c4d1" dependencies = [ "cc", "libc", @@ -9467,21 +9527,15 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-derive" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -9646,9 +9700,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -9659,9 +9713,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.2.3+3.2.1" +version = "300.3.1+3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" +checksum = "7259953d42a81bf137fbbd73bd30a8e1914d6dce43c2b90ed575783a22608b91" dependencies = [ "cc", ] @@ -9713,7 +9767,7 @@ dependencies = [ "itertools 0.11.0", "petgraph", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -9874,7 +9928,6 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 14.0.0", ] [[package]] @@ -10417,9 +10470,9 @@ dependencies = [ name = "pallet-contracts-proc-macro" version = "18.0.0" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -10585,7 +10638,7 @@ dependencies = [ "sp-npos-elections", "sp-runtime", "sp-tracing 16.0.0", - "strum 0.26.2", + "strum 0.26.3", ] [[package]] @@ -11651,10 +11704,10 @@ name = "pallet-staking-reward-curve" version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "sp-runtime", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -12296,7 +12349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -12325,7 +12378,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "syn 1.0.109", "synstructure 0.12.6", ] @@ -12710,9 +12763,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" @@ -12742,9 +12795,9 @@ checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -12783,16 +12836,22 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" + +[[package]] +name = "pin-project-lite" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" [[package]] name = "pin-utils" @@ -13694,7 +13753,7 @@ dependencies = [ "sc-network", "sc-network-types", "sp-runtime", - "strum 0.26.2", + "strum 0.26.3", "thiserror", "tracing-gum", ] @@ -14894,7 +14953,7 @@ dependencies = [ "sp-runtime", "sp-timestamp", "sp-tracing 16.0.0", - "strum 0.26.2", + "strum 0.26.3", "substrate-prometheus-endpoint", "tokio", "tracing-gum", @@ -15130,9 +15189,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ "polkavm-common", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -15142,7 +15201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -15178,7 +15237,7 @@ dependencies = [ "concurrent-queue", "libc", "log", - "pin-project-lite", + "pin-project-lite 0.2.12", "windows-sys 0.48.0", ] @@ -15190,12 +15249,23 @@ checksum = "30054e72317ab98eddd8561db0f6524df3367636884b7b21b703e4b280a84a14" dependencies = [ "cfg-if", "concurrent-queue", - "pin-project-lite", - "rustix 0.38.21", + "pin-project-lite 0.2.12", + "rustix 0.38.25", "tracing", "windows-sys 0.52.0", ] +[[package]] +name = "poly1305" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +dependencies = [ + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.4.0", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -15204,7 +15274,19 @@ checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.5.1", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.4.0", ] [[package]] @@ -15216,7 +15298,7 @@ dependencies = [ "cfg-if", "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.5.1", ] [[package]] @@ -15234,12 +15316,6 @@ dependencies = [ "rand", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "pprof" version = "0.12.1" @@ -15324,7 +15400,7 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "syn 1.0.109", ] @@ -15334,8 +15410,8 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ - "proc-macro2 1.0.82", - "syn 2.0.61", + "proc-macro2 1.0.75", + "syn 2.0.58", ] [[package]] @@ -15395,7 +15471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", "version_check", @@ -15407,7 +15483,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "version_check", ] @@ -15424,9 +15500,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -15435,9 +15511,9 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b698b0b09d40e9b7c1a47b132d66a8b54bcd20583d9b6d06e4535e383b4405c" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -15451,9 +15527,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" dependencies = [ "unicode-ident", ] @@ -15470,7 +15546,7 @@ dependencies = [ "hex", "lazy_static", "procfs-core", - "rustix 0.38.21", + "rustix 0.38.25", ] [[package]] @@ -15516,9 +15592,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -15597,9 +15673,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" +checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ "bytes", "heck 0.5.0", @@ -15610,9 +15686,9 @@ dependencies = [ "petgraph", "prettyplease 0.2.12", "prost 0.12.6", - "prost-types 0.12.4", + "prost-types 0.12.6", "regex", - "syn 2.0.61", + "syn 2.0.58", "tempfile", ] @@ -15624,7 +15700,7 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -15637,9 +15713,9 @@ checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.11.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -15653,9 +15729,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" dependencies = [ "prost 0.12.6", ] @@ -15765,6 +15841,17 @@ dependencies = [ "rand", ] +[[package]] +name = "quicksink" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77de3c815e5a160b1539c6592796801df2043ae35e123b46d73380cfa57af858" +dependencies = [ + "futures-core", + "futures-sink", + "pin-project-lite 0.1.12", +] + [[package]] name = "quinn" version = "0.9.4" @@ -15772,11 +15859,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e8b432585672228923edbbf64b8b12c14e1112f62e88737655b4a083dbcd78e" dependencies = [ "bytes", - "pin-project-lite", - "quinn-proto 0.9.6", + "pin-project-lite 0.2.12", + "quinn-proto 0.9.5", "quinn-udp 0.3.2", "rustc-hash", - "rustls 0.20.9", + "rustls 0.20.8", "thiserror", "tokio", "tracing", @@ -15791,7 +15878,7 @@ checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" dependencies = [ "bytes", "futures-io", - "pin-project-lite", + "pin-project-lite 0.2.12", "quinn-proto 0.10.6", "quinn-udp 0.4.1", "rustc-hash", @@ -15803,15 +15890,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.9.6" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863" +checksum = "c956be1b23f4261676aed05a0046e204e8a6836e50203902683a718af0797989" dependencies = [ "bytes", "rand", "ring 0.16.20", "rustc-hash", - "rustls 0.20.9", + "rustls 0.20.8", "slab", "thiserror", "tinyvec", @@ -15843,7 +15930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4" dependencies = [ "libc", - "quinn-proto 0.9.6", + "quinn-proto 0.9.5", "socket2 0.4.9", "tracing", "windows-sys 0.42.0", @@ -15877,7 +15964,7 @@ version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", ] [[package]] @@ -16076,9 +16163,9 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -16254,10 +16341,10 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.3.26", + "h2 0.3.24", "http 0.2.9", "http-body 0.4.5", - "hyper 0.14.29", + "hyper 0.14.30", "hyper-rustls 0.24.2", "ipnet", "js-sys", @@ -16265,7 +16352,7 @@ dependencies = [ "mime", "once_cell", "percent-encoding", - "pin-project-lite", + "pin-project-lite 0.2.12", "rustls 0.21.7", "rustls-pemfile 1.0.3", "serde", @@ -16626,12 +16713,12 @@ checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" dependencies = [ "cfg-if", "glob", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "regex", "relative-path", "rustc_version 0.4.0", - "syn 2.0.61", + "syn 2.0.58", "unicode-ident", ] @@ -16774,22 +16861,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" dependencies = [ "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.10", + "linux-raw-sys 0.4.11", "windows-sys 0.48.0", ] [[package]] name = "rustls" -version = "0.20.9" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "ring 0.16.20", "sct", @@ -16875,9 +16962,9 @@ checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-platform-verifier" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5f0d26fa1ce3c790f9590868f0109289a044acb954525f933e2aa3b871c157d" +checksum = "3e3beb939bcd33c269f4bf946cc829fcd336370267c4a927ac0399c84a3151a1" dependencies = [ "core-foundation", "core-foundation-sys", @@ -17018,7 +17105,7 @@ dependencies = [ "multihash 0.19.1", "parity-scale-codec", "prost 0.12.6", - "prost-build 0.12.4", + "prost-build 0.12.6", "quickcheck", "rand", "sc-client-api", @@ -17114,9 +17201,9 @@ name = "sc-chain-spec-derive" version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -17764,7 +17851,7 @@ dependencies = [ "partial_sort", "pin-project", "prost 0.12.6", - "prost-build 0.12.4", + "prost-build 0.12.6", "rand", "sc-block-builder", "sc-client-api", @@ -17809,7 +17896,7 @@ dependencies = [ "futures", "libp2p-identity", "parity-scale-codec", - "prost-build 0.12.4", + "prost-build 0.12.6", "sc-consensus", "sc-network-types", "sp-consensus", @@ -17851,7 +17938,7 @@ dependencies = [ "log", "parity-scale-codec", "prost 0.12.6", - "prost-build 0.12.4", + "prost-build 0.12.6", "sc-client-api", "sc-network", "sc-network-types", @@ -17895,7 +17982,7 @@ dependencies = [ "mockall 0.11.4", "parity-scale-codec", "prost 0.12.6", - "prost-build 0.12.4", + "prost-build 0.12.6", "quickcheck", "sc-block-builder", "sc-client-api", @@ -17997,7 +18084,7 @@ dependencies = [ "fnv", "futures", "futures-timer", - "hyper 0.14.29", + "hyper 0.14.30", "hyper-rustls 0.24.2", "lazy_static", "log", @@ -18409,9 +18496,9 @@ name = "sc-tracing-proc-macro" version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -18523,7 +18610,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -18561,7 +18648,7 @@ version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "serde_derive_internals", "syn 1.0.109", @@ -18600,7 +18687,7 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de18f6d8ba0aad7045f5feae07ec29899c1112584a38509a84ad7b04451eaa0" dependencies = [ - "aead", + "aead 0.5.2", "arrayref", "arrayvec 0.7.4", "curve25519-dalek", @@ -18682,18 +18769,18 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.28.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" dependencies = [ "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.9.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +checksum = "09e67c467c38fd24bd5499dc9a18183b31575c12ee549197e3e20d57aa4fe3b7" dependencies = [ "cc", ] @@ -18861,9 +18948,9 @@ version = "1.0.206" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -18872,7 +18959,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -18922,9 +19009,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.34+deprecated" +version = "0.9.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" dependencies = [ "indexmap 2.2.3", "itoa", @@ -18963,9 +19050,9 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -19006,9 +19093,9 @@ dependencies = [ [[package]] name = "sha1-asm" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba6947745e7f86be3b8af00b7355857085dbdf8901393c89514510eb61f4e21" +checksum = "286acebaf8b67c1130aedffad26f594eff0c1292389158135327d2e23aed582b" dependencies = [ "cc", ] @@ -19256,7 +19343,7 @@ dependencies = [ "bip39", "blake2-rfc", "bs58 0.5.1", - "chacha20", + "chacha20 0.9.1", "crossbeam-queue", "derive_more", "ed25519-zebra", @@ -19278,7 +19365,7 @@ dependencies = [ "num-traits", "pbkdf2", "pin-project", - "poly1305", + "poly1305 0.8.0", "rand", "rand_chacha", "ruzstd", @@ -19341,16 +19428,16 @@ checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" [[package]] name = "snow" -version = "0.9.6" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "850948bee068e713b8ab860fe1adc4d109676ab4c3b621fd8147f06b261f2f85" +checksum = "0c9d1425eb528a21de2755c75af4c9b5d57f50a0d4c3b7f1828a4cd03f8ba155" dependencies = [ - "aes-gcm", + "aes-gcm 0.9.2", "blake2 0.10.6", "chacha20poly1305", "curve25519-dalek", "rand_core", - "ring 0.17.7", + "ring 0.16.20", "rustc_version 0.4.0", "sha2 0.10.8", "subtle 2.5.0", @@ -19839,9 +19926,9 @@ dependencies = [ "blake2 0.10.6", "expander", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -20037,7 +20124,7 @@ dependencies = [ "sp-keystore", "sp-mmr-primitives", "sp-runtime", - "strum 0.26.2", + "strum 0.26.3", "w3f-bls", ] @@ -20168,7 +20255,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.4.1" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +source = "git+https://github.com/paritytech/polkadot-sdk#838a534da874cf6071fba1df07643c6c5b033ae0" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -20225,7 +20312,7 @@ version = "0.1.0" dependencies = [ "quote 1.0.36", "sp-crypto-hashing", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -20241,18 +20328,18 @@ name = "sp-debug-derive" version = "8.0.0" source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] name = "sp-debug-derive" version = "14.0.0" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -20330,7 +20417,7 @@ version = "31.0.0" dependencies = [ "sp-core", "sp-runtime", - "strum 0.26.2", + "strum 0.26.3", ] [[package]] @@ -20518,13 +20605,13 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +source = "git+https://github.com/paritytech/polkadot-sdk#838a534da874cf6071fba1df07643c6c5b033ae0" dependencies = [ "Inflector", "proc-macro-crate 1.3.1", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -20534,9 +20621,9 @@ dependencies = [ "Inflector", "expander", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -20629,7 +20716,7 @@ dependencies = [ name = "sp-statement-store" version = "10.0.0" dependencies = [ - "aes-gcm", + "aes-gcm 0.10.3", "curve25519-dalek", "ed25519-dalek", "hkdf", @@ -20795,10 +20882,10 @@ name = "sp-version-proc-macro" version = "13.0.0" dependencies = [ "parity-scale-codec", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "sp-version", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -20880,7 +20967,7 @@ checksum = "5e6915280e2d0db8911e5032a5c275571af6bdded2916abd691a659be25d3439" dependencies = [ "Inflector", "num-format", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "serde", "serde_json", @@ -20905,7 +20992,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f07d54c4d01a1713eb363b55ba51595da15f6f1211435b71466460da022aa140" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -21098,7 +21185,7 @@ checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" dependencies = [ "cfg_aliases", "memchr", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -21171,7 +21258,7 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -21193,11 +21280,11 @@ checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros 0.26.2", + "strum_macros 0.26.4", ] [[package]] @@ -21207,7 +21294,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "rustversion", "syn 1.0.109", @@ -21220,23 +21307,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "rustversion", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.82", + "heck 0.5.0", + "proc-macro2 1.0.75", "quote 1.0.36", "rustversion", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -21374,7 +21461,7 @@ dependencies = [ "sp-runtime", "sp-trie", "structopt", - "strum 0.26.2", + "strum 0.26.3", "thiserror", ] @@ -21550,7 +21637,7 @@ dependencies = [ "sp-maybe-compressed-blob", "sp-tracing 16.0.0", "sp-version", - "strum 0.26.2", + "strum 0.26.3", "tempfile", "toml 0.8.8", "walkdir", @@ -21683,18 +21770,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.61" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "unicode-ident", ] @@ -21706,9 +21793,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b837ef12ab88835251726eb12237655e61ec8dc8a280085d1961cdc3dfd047" dependencies = [ "paste", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -21717,7 +21804,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", "unicode-xid 0.2.4", @@ -21729,16 +21816,16 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] name = "sysinfo" -version = "0.30.5" +version = "0.30.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2" +checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae" dependencies = [ "cfg-if", "core-foundation-sys", @@ -21802,7 +21889,7 @@ dependencies = [ "cfg-if", "fastrand 2.1.0", "redox_syscall 0.4.1", - "rustix 0.38.21", + "rustix 0.38.25", "windows-sys 0.48.0", ] @@ -21821,7 +21908,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "rustix 0.38.21", + "rustix 0.38.25", "windows-sys 0.48.0", ] @@ -21848,9 +21935,9 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -22012,7 +22099,7 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "syn 1.0.109", ] @@ -22023,9 +22110,9 @@ version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -22099,16 +22186,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "0bb39ee79a6d8de55f48f2293a830e040392f1c5f16e336bdd1788cd0aadce07" dependencies = [ "deranged", "itoa", "libc", - "num-conv", "num_threads", - "powerfmt", "serde", "time-core", "time-macros", @@ -22116,17 +22201,16 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "733d258752e9303d392b94b75230d07b0b9c489350c69b851fc6c065fde3e8f9" dependencies = [ - "num-conv", "time-core", ] @@ -22166,9 +22250,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -22176,7 +22260,7 @@ dependencies = [ "mio", "num_cpus", "parking_lot 0.12.3", - "pin-project-lite", + "pin-project-lite 0.2.12", "signal-hook-registry", "socket2 0.5.7", "tokio-macros", @@ -22185,13 +22269,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -22233,7 +22317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", - "pin-project-lite", + "pin-project-lite 0.2.12", "tokio", "tokio-util", ] @@ -22276,7 +22360,7 @@ dependencies = [ "futures-core", "futures-io", "futures-sink", - "pin-project-lite", + "pin-project-lite 0.2.12", "tokio", ] @@ -22343,7 +22427,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project", - "pin-project-lite", + "pin-project-lite 0.2.12", "tokio", "tower-layer", "tower-service", @@ -22361,7 +22445,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "http-body-util", - "pin-project-lite", + "pin-project-lite 0.2.12", "tower-layer", "tower-service", ] @@ -22385,7 +22469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", - "pin-project-lite", + "pin-project-lite 0.2.12", "tracing-attributes", "tracing-core", ] @@ -22396,9 +22480,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -22438,9 +22522,9 @@ dependencies = [ "assert_matches", "expander", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -22536,9 +22620,9 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65ed83be775d85ebb0e272914fff6462c39b3ddd6dc67b5c1c41271aad280c69" +checksum = "0c992b4f40c234a074d48a757efeabb1a6be88af84c0c23f7ca158950cb0ae7f" dependencies = [ "hash-db", "log", @@ -22703,6 +22787,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "typeid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" + [[package]] name = "typenum" version = "1.16.0" @@ -22778,6 +22868,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "universal-hash" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" +dependencies = [ + "generic-array 0.14.7", + "subtle 2.5.0", +] + [[package]] name = "universal-hash" version = "0.5.1" @@ -22790,9 +22890,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.11" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" [[package]] name = "unsigned-varint" @@ -22830,12 +22930,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 0.4.0", "percent-encoding", ] @@ -22865,9 +22965,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec26a25bd6fca441cdd0f769fd7f891bae119f996de31f86a5eddccef54c1d" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" dependencies = [ "value-bag-serde1", "value-bag-sval2", @@ -22875,9 +22975,9 @@ dependencies = [ [[package]] name = "value-bag-serde1" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ead5b693d906686203f19a49e88c477fb8c15798b68cf72f60b4b5521b4ad891" +checksum = "ccacf50c5cb077a9abb723c5bcb5e0754c1a433f1e1de89edc328e2760b6328b" dependencies = [ "erased-serde", "serde", @@ -22886,9 +22986,9 @@ dependencies = [ [[package]] name = "value-bag-sval2" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9d0f4a816370c3a0d7d82d603b62198af17675b12fe5e91de6b47ceb505882" +checksum = "1785bae486022dfb9703915d42287dcb284c1ee37bd1080eeba78cc04721285b" dependencies = [ "sval", "sval_buffer", @@ -22989,9 +23089,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "serde", @@ -23001,16 +23101,16 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", "wasm-bindgen-shared", ] @@ -23028,9 +23128,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote 1.0.36", "wasm-bindgen-macro-support", @@ -23038,22 +23138,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-bindgen-test" @@ -23075,7 +23175,7 @@ version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", ] @@ -24141,10 +24241,10 @@ name = "xcm-procedural" version = "7.0.0" dependencies = [ "Inflector", - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", "staging-xcm", - "syn 2.0.61", + "syn 2.0.58", "trybuild", ] @@ -24307,9 +24407,9 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] @@ -24327,9 +24427,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.75", "quote 1.0.36", - "syn 2.0.61", + "syn 2.0.58", ] [[package]] diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs index 80d2376c6811..1f98d3ba964d 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs @@ -59,5 +59,5 @@ impl_accounts_helpers_for_parachain!(AssetHubRococo); impl_assert_events_helpers_for_parachain!(AssetHubRococo); impl_assets_helpers_for_system_parachain!(AssetHubRococo, Rococo); impl_assets_helpers_for_parachain!(AssetHubRococo); -impl_foreign_assets_helpers_for_parachain!(AssetHubRococo, xcm::v3::Location); +impl_foreign_assets_helpers_for_parachain!(AssetHubRococo, xcm::v4::Location); impl_xcm_helpers_for_parachain!(AssetHubRococo); diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs index 608690218d2f..6066adec52c3 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs @@ -59,5 +59,5 @@ impl_accounts_helpers_for_parachain!(AssetHubWestend); impl_assert_events_helpers_for_parachain!(AssetHubWestend); impl_assets_helpers_for_system_parachain!(AssetHubWestend, Westend); impl_assets_helpers_for_parachain!(AssetHubWestend); -impl_foreign_assets_helpers_for_parachain!(AssetHubWestend, xcm::v3::Location); +impl_foreign_assets_helpers_for_parachain!(AssetHubWestend, xcm::v4::Location); impl_xcm_helpers_for_parachain!(AssetHubWestend); diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index b3dec175e112..30e66ced1fb0 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -63,11 +63,11 @@ pub const PENPAL_ID: u32 = 2000; pub const ASSETS_PALLET_ID: u8 = 50; parameter_types! { - pub PenpalTeleportableAssetLocation: xcm::v3::Location - = xcm::v3::Location::new(1, [ - xcm::v3::Junction::Parachain(PENPAL_ID), - xcm::v3::Junction::PalletInstance(ASSETS_PALLET_ID), - xcm::v3::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), + pub PenpalTeleportableAssetLocation: xcm::v4::Location + = xcm::v4::Location::new(1, [ + xcm::v4::Junction::Parachain(PENPAL_ID), + xcm::v4::Junction::PalletInstance(ASSETS_PALLET_ID), + xcm::v4::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), ] ); pub PenpalSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_ID).into_account_truncating(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs index 364fbd0d439f..ab407611c736 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs @@ -36,21 +36,18 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { let para_sovereign_account = AssetHubRococo::sovereign_account_id_of( AssetHubRococo::sibling_location_of(PenpalA::para_id()), ); - let asset_location_on_penpal = v3::Location::new( + let asset_location_on_penpal = Location::new( 0, - [ - v3::Junction::PalletInstance(ASSETS_PALLET_ID), - v3::Junction::GeneralIndex(ASSET_ID.into()), - ], + [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(ASSET_ID.into())], ); let foreign_asset_at_asset_hub = - v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + Location::new(1, [Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); // Encoded `create_asset` call to be executed in AssetHub let call = AssetHubRococo::create_foreign_asset_call( - foreign_asset_at_asset_hub, + foreign_asset_at_asset_hub.clone(), ASSET_MIN_BALANCE, para_sovereign_account.clone(), ); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs index 16e0512da960..ac0c90ba198d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs @@ -17,13 +17,10 @@ use crate::imports::*; #[test] fn swap_locally_on_chain_using_local_assets() { - let asset_native = Box::new(v3::Location::try_from(RelayLocation::get()).unwrap()); - let asset_one = Box::new(v3::Location::new( + let asset_native = Box::new(Location::try_from(RelayLocation::get()).unwrap()); + let asset_one = Box::new(Location::new( 0, - [ - v3::Junction::PalletInstance(ASSETS_PALLET_ID), - v3::Junction::GeneralIndex(ASSET_ID.into()), - ], + [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(ASSET_ID.into())], )); AssetHubRococo::execute_with(|| { @@ -112,11 +109,11 @@ fn swap_locally_on_chain_using_local_assets() { #[test] fn swap_locally_on_chain_using_foreign_assets() { - let asset_native = Box::new(v3::Location::try_from(RelayLocation::get()).unwrap()); + let asset_native = Box::new(Location::try_from(RelayLocation::get()).unwrap()); let asset_location_on_penpal = - v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).unwrap(); + Location::try_from(PenpalLocalTeleportableToAssetHub::get()).unwrap(); let foreign_asset_at_asset_hub_rococo = - v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + Location::new(1, [Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); @@ -141,7 +138,7 @@ fn swap_locally_on_chain_using_foreign_assets() { // 1. Mint foreign asset (in reality this should be a teleport or some such) assert_ok!(::ForeignAssets::mint( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone().into()), - foreign_asset_at_asset_hub_rococo, + foreign_asset_at_asset_hub_rococo.clone(), sov_penpal_on_ahr.clone().into(), ASSET_HUB_ROCOCO_ED * 3_000_000_000_000, )); @@ -157,7 +154,7 @@ fn swap_locally_on_chain_using_foreign_assets() { assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), asset_native.clone(), - Box::new(foreign_asset_at_asset_hub_rococo), + Box::new(foreign_asset_at_asset_hub_rococo.clone()), )); assert_expected_events!( @@ -171,7 +168,7 @@ fn swap_locally_on_chain_using_foreign_assets() { assert_ok!(::AssetConversion::add_liquidity( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()), asset_native.clone(), - Box::new(foreign_asset_at_asset_hub_rococo), + Box::new(foreign_asset_at_asset_hub_rococo.clone()), 1_000_000_000_000, 2_000_000_000_000, 0, @@ -189,7 +186,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ); // 4. Swap! - let path = vec![asset_native.clone(), Box::new(foreign_asset_at_asset_hub_rococo)]; + let path = vec![asset_native.clone(), Box::new(foreign_asset_at_asset_hub_rococo.clone())]; assert_ok!( ::AssetConversion::swap_exact_tokens_for_tokens( @@ -216,7 +213,7 @@ fn swap_locally_on_chain_using_foreign_assets() { assert_ok!(::AssetConversion::remove_liquidity( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()), asset_native.clone(), - Box::new(foreign_asset_at_asset_hub_rococo), + Box::new(foreign_asset_at_asset_hub_rococo.clone()), 1414213562273 - ASSET_HUB_ROCOCO_ED * 2, // all but the 2 EDs can't be retrieved. 0, 0, @@ -252,8 +249,8 @@ fn cannot_create_pool_from_pool_assets() { assert_matches::assert_matches!( ::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), - Box::new(v3::Location::try_from(asset_native).unwrap()), - Box::new(v3::Location::try_from(asset_one).unwrap()), + Box::new(Location::try_from(asset_native).unwrap()), + Box::new(Location::try_from(asset_one).unwrap()), ), Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("Unknown")) ); @@ -262,12 +259,12 @@ fn cannot_create_pool_from_pool_assets() { #[test] fn pay_xcm_fee_with_some_asset_swapped_for_native() { - let asset_native = v3::Location::try_from(RelayLocation::get()).unwrap(); - let asset_one = xcm::v3::Location { + let asset_native = Location::try_from(RelayLocation::get()).unwrap(); + let asset_one = Location { parents: 0, interior: [ - xcm::v3::Junction::PalletInstance(ASSETS_PALLET_ID), - xcm::v3::Junction::GeneralIndex(ASSET_ID.into()), + Junction::PalletInstance(ASSETS_PALLET_ID), + Junction::GeneralIndex(ASSET_ID.into()), ] .into(), }; @@ -296,8 +293,8 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), - Box::new(asset_native), - Box::new(asset_one), + Box::new(asset_native.clone()), + Box::new(asset_one.clone()), )); assert_expected_events!( diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs index f74378d7631a..5f9131b8c123 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs @@ -141,7 +141,6 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { ); let (expected_foreign_asset_id, expected_foreign_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); - let expected_foreign_asset_id_v3: v3::Location = expected_foreign_asset_id.try_into().unwrap(); AssetHubRococo::assert_xcmp_queue_success(None); @@ -159,7 +158,7 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { who: *who == t.receiver.account_id, }, RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { - asset_id: *asset_id == expected_foreign_asset_id_v3, + asset_id: *asset_id == expected_foreign_asset_id, owner: *owner == t.receiver.account_id, amount: *amount == expected_foreign_asset_amount, }, @@ -173,7 +172,6 @@ fn ah_to_penpal_foreign_assets_sender_assertions(t: SystemParaToParaTest) { AssetHubRococo::assert_xcm_pallet_attempted_complete(None); let (expected_foreign_asset_id, expected_foreign_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); - let expected_foreign_asset_id_v3: v3::Location = expected_foreign_asset_id.try_into().unwrap(); assert_expected_events!( AssetHubRococo, vec![ @@ -189,7 +187,7 @@ fn ah_to_penpal_foreign_assets_sender_assertions(t: SystemParaToParaTest) { }, // foreign asset is burned locally as part of teleportation RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { - asset_id: *asset_id == expected_foreign_asset_id_v3, + asset_id: *asset_id == expected_foreign_asset_id, owner: *owner == t.sender.account_id, balance: *balance == expected_foreign_asset_amount, }, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs index 437268a19e59..fe484771931f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs @@ -26,10 +26,7 @@ mod imports { }; // Polkadot - pub use xcm::{ - prelude::{AccountId32 as AccountId32Junction, *}, - v3, - }; + pub use xcm::prelude::{AccountId32 as AccountId32Junction, *}; pub use xcm_executor::traits::TransferType; // Cumulus diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs index eb0e985cc0ce..ac006653ca67 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs @@ -36,21 +36,18 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { let para_sovereign_account = AssetHubWestend::sovereign_account_id_of( AssetHubWestend::sibling_location_of(PenpalA::para_id()), ); - let asset_location_on_penpal = v3::Location::new( + let asset_location_on_penpal = Location::new( 0, - [ - v3::Junction::PalletInstance(ASSETS_PALLET_ID), - v3::Junction::GeneralIndex(ASSET_ID.into()), - ], + [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(ASSET_ID.into())], ); let foreign_asset_at_asset_hub = - v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + Location::new(1, [Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); // Encoded `create_asset` call to be executed in AssetHub let call = AssetHubWestend::create_foreign_asset_call( - foreign_asset_at_asset_hub, + foreign_asset_at_asset_hub.clone(), ASSET_MIN_BALANCE, para_sovereign_account.clone(), ); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs index cf429378cf6d..1a2821452155 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs @@ -18,12 +18,12 @@ use crate::imports::*; #[test] fn swap_locally_on_chain_using_local_assets() { let asset_native = - Box::new(v3::Location::try_from(RelayLocation::get()).expect("conversion works")); - let asset_one = Box::new(v3::Location { + Box::new(Location::try_from(RelayLocation::get()).expect("conversion works")); + let asset_one = Box::new(Location { parents: 0, interior: [ - v3::Junction::PalletInstance(ASSETS_PALLET_ID), - v3::Junction::GeneralIndex(ASSET_ID.into()), + Junction::PalletInstance(ASSETS_PALLET_ID), + Junction::GeneralIndex(ASSET_ID.into()), ] .into(), }); @@ -112,11 +112,11 @@ fn swap_locally_on_chain_using_local_assets() { #[test] fn swap_locally_on_chain_using_foreign_assets() { - let asset_native = Box::new(v3::Location::try_from(RelayLocation::get()).unwrap()); + let asset_native = Box::new(Location::try_from(RelayLocation::get()).unwrap()); let asset_location_on_penpal = - v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion_works"); + Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion_works"); let foreign_asset_at_asset_hub_westend = - v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + Location::new(1, [Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); @@ -141,7 +141,7 @@ fn swap_locally_on_chain_using_foreign_assets() { // 1. Mint foreign asset (in reality this should be a teleport or some such) assert_ok!(::ForeignAssets::mint( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone().into()), - foreign_asset_at_asset_hub_westend, + foreign_asset_at_asset_hub_westend.clone(), sov_penpal_on_ahr.clone().into(), ASSET_HUB_WESTEND_ED * 3_000_000_000_000, )); @@ -157,7 +157,7 @@ fn swap_locally_on_chain_using_foreign_assets() { assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), asset_native.clone(), - Box::new(foreign_asset_at_asset_hub_westend), + Box::new(foreign_asset_at_asset_hub_westend.clone()), )); assert_expected_events!( @@ -171,7 +171,7 @@ fn swap_locally_on_chain_using_foreign_assets() { assert_ok!(::AssetConversion::add_liquidity( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()), asset_native.clone(), - Box::new(foreign_asset_at_asset_hub_westend), + Box::new(foreign_asset_at_asset_hub_westend.clone()), 1_000_000_000_000_000, 2_000_000_000_000_000, 0, @@ -189,7 +189,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ); // 4. Swap! - let path = vec![asset_native.clone(), Box::new(foreign_asset_at_asset_hub_westend)]; + let path = vec![asset_native.clone(), Box::new(foreign_asset_at_asset_hub_westend.clone())]; assert_ok!( ::AssetConversion::swap_exact_tokens_for_tokens( @@ -252,8 +252,8 @@ fn cannot_create_pool_from_pool_assets() { assert_matches::assert_matches!( ::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - Box::new(v3::Location::try_from(asset_native).expect("conversion works")), - Box::new(v3::Location::try_from(asset_one).expect("conversion works")), + Box::new(Location::try_from(asset_native).expect("conversion works")), + Box::new(Location::try_from(asset_one).expect("conversion works")), ), Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("Unknown")) ); @@ -262,12 +262,12 @@ fn cannot_create_pool_from_pool_assets() { #[test] fn pay_xcm_fee_with_some_asset_swapped_for_native() { - let asset_native = v3::Location::try_from(RelayLocation::get()).expect("conversion works"); - let asset_one = xcm::v3::Location { + let asset_native = Location::try_from(RelayLocation::get()).expect("conversion works"); + let asset_one = Location { parents: 0, interior: [ - xcm::v3::Junction::PalletInstance(ASSETS_PALLET_ID), - xcm::v3::Junction::GeneralIndex(ASSET_ID.into()), + Junction::PalletInstance(ASSETS_PALLET_ID), + Junction::GeneralIndex(ASSET_ID.into()), ] .into(), }; @@ -296,8 +296,8 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - Box::new(asset_native), - Box::new(asset_one), + Box::new(asset_native.clone()), + Box::new(asset_one.clone()), )); assert_expected_events!( diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs index a524b87b2daf..02a0bc0207bb 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs @@ -141,7 +141,6 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { ); let (expected_foreign_asset_id, expected_foreign_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); - let expected_foreign_asset_id_v3: v3::Location = expected_foreign_asset_id.try_into().unwrap(); AssetHubWestend::assert_xcmp_queue_success(None); @@ -159,7 +158,7 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { who: *who == t.receiver.account_id, }, RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { - asset_id: *asset_id == expected_foreign_asset_id_v3, + asset_id: *asset_id == expected_foreign_asset_id, owner: *owner == t.receiver.account_id, amount: *amount == expected_foreign_asset_amount, }, @@ -173,7 +172,6 @@ fn ah_to_penpal_foreign_assets_sender_assertions(t: SystemParaToParaTest) { AssetHubWestend::assert_xcm_pallet_attempted_complete(None); let (expected_foreign_asset_id, expected_foreign_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); - let expected_foreign_asset_id_v3: v3::Location = expected_foreign_asset_id.try_into().unwrap(); assert_expected_events!( AssetHubWestend, vec![ @@ -189,7 +187,7 @@ fn ah_to_penpal_foreign_assets_sender_assertions(t: SystemParaToParaTest) { }, // foreign asset is burned locally as part of teleportation RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { - asset_id: *asset_id == expected_foreign_asset_id_v3, + asset_id: *asset_id == expected_foreign_asset_id, owner: *owner == t.sender.account_id, balance: *balance == expected_foreign_asset_amount, }, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs index 3ee509389c67..0aefe5b6352c 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs @@ -23,7 +23,8 @@ mod imports { pub use xcm::{ latest::ParentThen, prelude::{AccountId32 as AccountId32Junction, *}, - v3::{self, NetworkId::Westend as WestendId}, + v4, + v4::NetworkId::Westend as WestendId, }; pub use xcm_executor::traits::TransferType; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs index 6053936487b2..8a674f89c9ef 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs @@ -36,10 +36,10 @@ fn send_assets_over_bridge(send_fn: F) { fn set_up_rocs_for_penpal_rococo_through_ahr_to_ahw( sender: &AccountId, amount: u128, -) -> (Location, v3::Location) { +) -> (Location, v4::Location) { let roc_at_rococo_parachains = roc_at_ah_rococo(); - let roc_at_asset_hub_westend = bridged_roc_at_ah_westend().try_into().unwrap(); - create_foreign_on_ah_westend(roc_at_asset_hub_westend, true); + let roc_at_asset_hub_westend = bridged_roc_at_ah_westend(); + create_foreign_on_ah_westend(roc_at_asset_hub_westend.clone(), true); let penpal_location = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location); @@ -121,11 +121,11 @@ fn send_roc_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { let amount = ASSET_HUB_ROCOCO_ED * 1_000_000; let sender = AssetHubRococoSender::get(); let receiver = AssetHubWestendReceiver::get(); - let roc_at_asset_hub_rococo: v3::Location = roc_at_ah_rococo().try_into().unwrap(); - let bridged_roc_at_asset_hub_westend = bridged_roc_at_ah_westend().try_into().unwrap(); + let roc_at_asset_hub_rococo = roc_at_ah_rococo(); + let bridged_roc_at_asset_hub_westend = bridged_roc_at_ah_westend(); - create_foreign_on_ah_westend(bridged_roc_at_asset_hub_westend, true); - set_up_pool_with_wnd_on_ah_westend(bridged_roc_at_asset_hub_westend); + create_foreign_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), true); + set_up_pool_with_wnd_on_ah_westend(bridged_roc_at_asset_hub_westend.clone()); //////////////////////////////////////////////////////////// // Let's first send over just some ROCs as a simple example @@ -138,12 +138,13 @@ fn send_roc_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { ::account_data_of(sov_ahw_on_ahr.clone()).free; let sender_rocs_before = ::account_data_of(sender.clone()).free; let receiver_rocs_before = - foreign_balance_on_ah_westend(bridged_roc_at_asset_hub_westend, &receiver); + foreign_balance_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), &receiver); // send ROCs, use them for fees send_assets_over_bridge(|| { let destination = asset_hub_westend_location(); - let assets: Assets = (Location::try_from(roc_at_asset_hub_rococo).unwrap(), amount).into(); + let assets: Assets = + (Location::try_from(roc_at_asset_hub_rococo.clone()).unwrap(), amount).into(); let fee_idx = 0; assert_ok!(send_assets_from_asset_hub_rococo(destination, assets, fee_idx)); }); @@ -185,9 +186,9 @@ fn send_roc_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { ///////////////////////////////////////////////////////////// let usdt_at_asset_hub_rococo = usdt_at_ah_rococo(); - let bridged_usdt_at_asset_hub_westend = bridged_usdt_at_ah_westend().try_into().unwrap(); + let bridged_usdt_at_asset_hub_westend = bridged_usdt_at_ah_westend(); // wETH has same relative location on both Rococo and Westend AssetHubs - let bridged_weth_at_ah = weth_at_asset_hubs().try_into().unwrap(); + let bridged_weth_at_ah = weth_at_asset_hubs(); // mint USDT in sender's account (USDT already created in genesis) AssetHubRococo::mint_asset( @@ -197,19 +198,23 @@ fn send_roc_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { amount * 2, ); // create wETH at src and dest and prefund sender's account - create_foreign_on_ah_rococo(bridged_weth_at_ah, true, vec![(sender.clone(), amount * 2)]); - create_foreign_on_ah_westend(bridged_weth_at_ah, true); - create_foreign_on_ah_westend(bridged_usdt_at_asset_hub_westend, true); - set_up_pool_with_wnd_on_ah_westend(bridged_usdt_at_asset_hub_westend); + create_foreign_on_ah_rococo( + bridged_weth_at_ah.clone(), + true, + vec![(sender.clone(), amount * 2)], + ); + create_foreign_on_ah_westend(bridged_weth_at_ah.clone(), true); + create_foreign_on_ah_westend(bridged_usdt_at_asset_hub_westend.clone(), true); + set_up_pool_with_wnd_on_ah_westend(bridged_usdt_at_asset_hub_westend.clone()); let receiver_usdts_before = - foreign_balance_on_ah_westend(bridged_usdt_at_asset_hub_westend, &receiver); - let receiver_weth_before = foreign_balance_on_ah_westend(bridged_weth_at_ah, &receiver); + foreign_balance_on_ah_westend(bridged_usdt_at_asset_hub_westend.clone(), &receiver); + let receiver_weth_before = foreign_balance_on_ah_westend(bridged_weth_at_ah.clone(), &receiver); // send USDTs and wETHs let assets: Assets = vec![ (usdt_at_asset_hub_rococo.clone(), amount).into(), - (Location::try_from(bridged_weth_at_ah).unwrap(), amount).into(), + (Location::try_from(bridged_weth_at_ah.clone()).unwrap(), amount).into(), ] .into(); // use USDT for fees @@ -258,9 +263,8 @@ fn send_back_wnds_from_asset_hub_rococo_to_asset_hub_westend() { let sender = AssetHubRococoSender::get(); let receiver = AssetHubWestendReceiver::get(); let wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo(); - let wnd_at_asset_hub_rococo_v3 = wnd_at_asset_hub_rococo.clone().try_into().unwrap(); let prefund_accounts = vec![(sender.clone(), prefund_amount)]; - create_foreign_on_ah_rococo(wnd_at_asset_hub_rococo_v3, true, prefund_accounts); + create_foreign_on_ah_rococo(wnd_at_asset_hub_rococo.clone(), true, prefund_accounts); // fund the AHR's SA on AHW with the WND tokens held in reserve let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( @@ -273,14 +277,14 @@ fn send_back_wnds_from_asset_hub_rococo_to_asset_hub_westend() { ::account_data_of(sov_ahr_on_ahw.clone()).free; assert_eq!(wnds_in_reserve_on_ahw_before, prefund_amount); - let sender_wnds_before = foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo_v3, &sender); + let sender_wnds_before = foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo.clone(), &sender); assert_eq!(sender_wnds_before, prefund_amount); let receiver_wnds_before = ::account_data_of(receiver.clone()).free; // send back WNDs, use them for fees send_assets_over_bridge(|| { let destination = asset_hub_westend_location(); - let assets: Assets = (wnd_at_asset_hub_rococo, amount_to_send).into(); + let assets: Assets = (wnd_at_asset_hub_rococo.clone(), amount_to_send).into(); let fee_idx = 0; assert_ok!(send_assets_from_asset_hub_rococo(destination, assets, fee_idx)); }); @@ -309,7 +313,7 @@ fn send_back_wnds_from_asset_hub_rococo_to_asset_hub_westend() { ); }); - let sender_wnds_after = foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo_v3, &sender); + let sender_wnds_after = foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo, &sender); let receiver_wnds_after = ::account_data_of(receiver).free; let wnds_in_reserve_on_ahw_after = ::account_data_of(sov_ahr_on_ahw).free; @@ -341,7 +345,8 @@ fn send_rocs_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_westend() type ForeignAssets = ::ForeignAssets; >::balance(roc_at_rococo_parachains.clone(), &sender) }); - let receiver_rocs_before = foreign_balance_on_ah_westend(roc_at_asset_hub_westend, &receiver); + let receiver_rocs_before = + foreign_balance_on_ah_westend(roc_at_asset_hub_westend.clone(), &receiver); // Send ROCs over bridge { @@ -372,7 +377,7 @@ fn send_rocs_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_westend() vec![ // issue ROCs on AHW RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { - asset_id: *asset_id == roc_at_rococo_parachains.clone().try_into().unwrap(), + asset_id: *asset_id == roc_at_rococo_parachains.clone(), owner: owner == &receiver, }, // message processed successfully @@ -403,7 +408,6 @@ fn send_rocs_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_westend() #[test] fn send_back_wnds_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_westend() { let wnd_at_rococo_parachains = bridged_wnd_at_ah_rococo(); - let wnd_at_rococo_parachains_v3 = wnd_at_rococo_parachains.clone().try_into().unwrap(); let amount = ASSET_HUB_ROCOCO_ED * 10_000_000; let sender = PenpalASender::get(); let receiver = AssetHubWestendReceiver::get(); @@ -416,7 +420,7 @@ fn send_back_wnds_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_weste let penpal_location = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location); let prefund_accounts = vec![(sov_penpal_on_ahr, amount * 2)]; - create_foreign_on_ah_rococo(wnd_at_rococo_parachains_v3, true, prefund_accounts); + create_foreign_on_ah_rococo(wnd_at_rococo_parachains.clone(), true, prefund_accounts); let asset_owner: AccountId = AssetHubRococo::account_id_of(ALICE); PenpalA::force_create_foreign_asset( wnd_at_rococo_parachains.clone(), diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs index ceccf98a0240..6ce8ecef0df3 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs @@ -69,7 +69,7 @@ pub(crate) fn weth_at_asset_hubs() -> Location { } pub(crate) fn create_foreign_on_ah_rococo( - id: v3::Location, + id: v4::Location, sufficient: bool, prefund_accounts: Vec<(AccountId, u128)>, ) { @@ -78,18 +78,18 @@ pub(crate) fn create_foreign_on_ah_rococo( AssetHubRococo::force_create_foreign_asset(id, owner, sufficient, min, prefund_accounts); } -pub(crate) fn create_foreign_on_ah_westend(id: v3::Location, sufficient: bool) { +pub(crate) fn create_foreign_on_ah_westend(id: v4::Location, sufficient: bool) { let owner = AssetHubWestend::account_id_of(ALICE); AssetHubWestend::force_create_foreign_asset(id, owner, sufficient, ASSET_MIN_BALANCE, vec![]); } -pub(crate) fn foreign_balance_on_ah_rococo(id: v3::Location, who: &AccountId) -> u128 { +pub(crate) fn foreign_balance_on_ah_rococo(id: v4::Location, who: &AccountId) -> u128 { AssetHubRococo::execute_with(|| { type Assets = ::ForeignAssets; >::balance(id, who) }) } -pub(crate) fn foreign_balance_on_ah_westend(id: v3::Location, who: &AccountId) -> u128 { +pub(crate) fn foreign_balance_on_ah_westend(id: v4::Location, who: &AccountId) -> u128 { AssetHubWestend::execute_with(|| { type Assets = ::ForeignAssets; >::balance(id, who) @@ -97,8 +97,8 @@ pub(crate) fn foreign_balance_on_ah_westend(id: v3::Location, who: &AccountId) - } // set up pool -pub(crate) fn set_up_pool_with_wnd_on_ah_westend(foreign_asset: v3::Location) { - let wnd: v3::Location = v3::Parent.into(); +pub(crate) fn set_up_pool_with_wnd_on_ah_westend(foreign_asset: v4::Location) { + let wnd: v4::Location = v4::Parent.into(); AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; let owner = AssetHubWestendSender::get(); @@ -106,14 +106,14 @@ pub(crate) fn set_up_pool_with_wnd_on_ah_westend(foreign_asset: v3::Location) { assert_ok!(::ForeignAssets::mint( signed_owner.clone(), - foreign_asset.into(), + foreign_asset.clone().into(), owner.clone().into(), 3_000_000_000_000, )); assert_ok!(::AssetConversion::create_pool( signed_owner.clone(), - Box::new(wnd), - Box::new(foreign_asset), + Box::new(wnd.clone()), + Box::new(foreign_asset.clone()), )); assert_expected_events!( AssetHubWestend, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs index 652447fa5601..3f2038b4bdd1 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs @@ -29,7 +29,7 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable let xcm = VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit, check_origin }, ExportMessage { - network: WestendId.into(), + network: WestendId, destination: [Parachain(AssetHubWestend::para_id().into())].into(), xcm: remote_xcm, }, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 782b83bac475..3262cc17ba56 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -23,8 +23,7 @@ mod imports { pub use xcm::{ latest::ParentThen, prelude::{AccountId32 as AccountId32Junction, *}, - v3, - v4::NetworkId::Rococo as RococoId, + v4::{self, NetworkId::Rococo as RococoId}, }; pub use xcm_executor::traits::TransferType; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs index 0c0b04cd45a9..e2c496802e2c 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs @@ -35,10 +35,10 @@ fn send_assets_over_bridge(send_fn: F) { fn set_up_wnds_for_penpal_westend_through_ahw_to_ahr( sender: &AccountId, amount: u128, -) -> (Location, v3::Location) { +) -> (Location, v4::Location) { let wnd_at_westend_parachains = wnd_at_ah_westend(); - let wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo().try_into().unwrap(); - create_foreign_on_ah_rococo(wnd_at_asset_hub_rococo, true); + let wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo(); + create_foreign_on_ah_rococo(wnd_at_asset_hub_rococo.clone(), true); let penpal_location = AssetHubWestend::sibling_location_of(PenpalB::para_id()); let sov_penpal_on_ahw = AssetHubWestend::sovereign_account_id_of(penpal_location); @@ -116,10 +116,10 @@ fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() { let sender = AssetHubWestendSender::get(); let receiver = AssetHubRococoReceiver::get(); let wnd_at_asset_hub_westend = wnd_at_ah_westend(); - let bridged_wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo().try_into().unwrap(); - create_foreign_on_ah_rococo(bridged_wnd_at_asset_hub_rococo, true); + let bridged_wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo(); + create_foreign_on_ah_rococo(bridged_wnd_at_asset_hub_rococo.clone(), true); - set_up_pool_with_roc_on_ah_rococo(bridged_wnd_at_asset_hub_rococo, true); + set_up_pool_with_roc_on_ah_rococo(bridged_wnd_at_asset_hub_rococo.clone(), true); let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( Rococo, @@ -129,7 +129,7 @@ fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() { ::account_data_of(sov_ahr_on_ahw.clone()).free; let sender_wnds_before = ::account_data_of(sender.clone()).free; let receiver_wnds_before = - foreign_balance_on_ah_rococo(bridged_wnd_at_asset_hub_rococo, &receiver); + foreign_balance_on_ah_rococo(bridged_wnd_at_asset_hub_rococo.clone(), &receiver); // send WNDs, use them for fees send_assets_over_bridge(|| { @@ -187,10 +187,8 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { let sender = AssetHubWestendSender::get(); let receiver = AssetHubRococoReceiver::get(); let bridged_roc_at_asset_hub_westend = bridged_roc_at_ah_westend(); - let bridged_roc_at_asset_hub_westend_v3 = - bridged_roc_at_asset_hub_westend.clone().try_into().unwrap(); let prefund_accounts = vec![(sender.clone(), prefund_amount)]; - create_foreign_on_ah_westend(bridged_roc_at_asset_hub_westend_v3, true, prefund_accounts); + create_foreign_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), true, prefund_accounts); //////////////////////////////////////////////////////////// // Let's first send back just some ROCs as a simple example @@ -208,14 +206,14 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { assert_eq!(rocs_in_reserve_on_ahr_before, prefund_amount); let sender_rocs_before = - foreign_balance_on_ah_westend(bridged_roc_at_asset_hub_westend_v3, &sender); + foreign_balance_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), &sender); assert_eq!(sender_rocs_before, prefund_amount); let receiver_rocs_before = ::account_data_of(receiver.clone()).free; // send back ROCs, use them for fees send_assets_over_bridge(|| { let destination = asset_hub_rococo_location(); - let assets: Assets = (bridged_roc_at_asset_hub_westend, amount_to_send).into(); + let assets: Assets = (bridged_roc_at_asset_hub_westend.clone(), amount_to_send).into(); let fee_idx = 0; assert_ok!(send_assets_from_asset_hub_westend(destination, assets, fee_idx)); }); @@ -245,7 +243,7 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { }); let sender_rocs_after = - foreign_balance_on_ah_westend(bridged_roc_at_asset_hub_westend_v3, &sender); + foreign_balance_on_ah_westend(bridged_roc_at_asset_hub_westend, &sender); let receiver_rocs_after = ::account_data_of(receiver.clone()).free; let rocs_in_reserve_on_ahr_after = ::account_data_of(sov_ahw_on_ahr.clone()).free; @@ -262,14 +260,14 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { ////////////////////////////////////////////////////////////////// // wETH has same relative location on both Rococo and Westend AssetHubs - let bridged_weth_at_ah = weth_at_asset_hubs().try_into().unwrap(); - let bridged_usdt_at_asset_hub_westend = bridged_usdt_at_ah_westend().try_into().unwrap(); + let bridged_weth_at_ah = weth_at_asset_hubs(); + let bridged_usdt_at_asset_hub_westend = bridged_usdt_at_ah_westend(); // set up destination chain AH Rococo: // create a ROC/USDT pool to be able to pay fees with USDT (USDT created in genesis) - set_up_pool_with_roc_on_ah_rococo(usdt_at_ah_rococo().try_into().unwrap(), false); + set_up_pool_with_roc_on_ah_rococo(usdt_at_ah_rococo(), false); // create wETH on Rococo (IRL it's already created by Snowbridge) - create_foreign_on_ah_rococo(bridged_weth_at_ah, true); + create_foreign_on_ah_rococo(bridged_weth_at_ah.clone(), true); // prefund AHW's sovereign account on AHR to be able to withdraw USDT and wETH from reserves let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( Westend, @@ -283,7 +281,7 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { ); AssetHubRococo::mint_foreign_asset( ::RuntimeOrigin::signed(AssetHubRococo::account_id_of(ALICE)), - bridged_weth_at_ah, + bridged_weth_at_ah.clone(), sov_ahw_on_ahr, amount_to_send * 2, ); @@ -291,21 +289,21 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { // set up source chain AH Westend: // create wETH and USDT foreign assets on Westend and prefund sender's account let prefund_accounts = vec![(sender.clone(), amount_to_send * 2)]; - create_foreign_on_ah_westend(bridged_weth_at_ah, true, prefund_accounts.clone()); - create_foreign_on_ah_westend(bridged_usdt_at_asset_hub_westend, true, prefund_accounts); + create_foreign_on_ah_westend(bridged_weth_at_ah.clone(), true, prefund_accounts.clone()); + create_foreign_on_ah_westend(bridged_usdt_at_asset_hub_westend.clone(), true, prefund_accounts); // check balances before let receiver_usdts_before = AssetHubRococo::execute_with(|| { type Assets = ::Assets; >::balance(USDT_ID, &receiver) }); - let receiver_weth_before = foreign_balance_on_ah_rococo(bridged_weth_at_ah, &receiver); + let receiver_weth_before = foreign_balance_on_ah_rococo(bridged_weth_at_ah.clone(), &receiver); let usdt_id: AssetId = Location::try_from(bridged_usdt_at_asset_hub_westend).unwrap().into(); // send USDTs and wETHs let assets: Assets = vec![ (usdt_id.clone(), amount_to_send).into(), - (Location::try_from(bridged_weth_at_ah).unwrap(), amount_to_send).into(), + (Location::try_from(bridged_weth_at_ah.clone()).unwrap(), amount_to_send).into(), ] .into(); // use USDT for fees @@ -367,7 +365,8 @@ fn send_wnds_from_penpal_westend_through_asset_hub_westend_to_asset_hub_rococo() type ForeignAssets = ::ForeignAssets; >::balance(wnd_at_westend_parachains.clone(), &sender) }); - let receiver_wnds_before = foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo, &receiver); + let receiver_wnds_before = + foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo.clone(), &receiver); // Send WNDs over bridge { @@ -398,7 +397,7 @@ fn send_wnds_from_penpal_westend_through_asset_hub_westend_to_asset_hub_rococo() vec![ // issue WNDs on AHR RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { - asset_id: *asset_id == wnd_at_westend_parachains.clone().try_into().unwrap(), + asset_id: *asset_id == wnd_at_westend_parachains.clone(), owner: owner == &receiver, }, // message processed successfully @@ -429,7 +428,6 @@ fn send_wnds_from_penpal_westend_through_asset_hub_westend_to_asset_hub_rococo() #[test] fn send_back_rocs_from_penpal_westend_through_asset_hub_westend_to_asset_hub_rococo() { let roc_at_westend_parachains = bridged_roc_at_ah_westend(); - let roc_at_westend_parachains_v3 = roc_at_westend_parachains.clone().try_into().unwrap(); let amount = ASSET_HUB_WESTEND_ED * 10_000_000; let sender = PenpalBSender::get(); let receiver = AssetHubRococoReceiver::get(); @@ -442,7 +440,7 @@ fn send_back_rocs_from_penpal_westend_through_asset_hub_westend_to_asset_hub_roc let penpal_location = AssetHubWestend::sibling_location_of(PenpalB::para_id()); let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(penpal_location); let prefund_accounts = vec![(sov_penpal_on_ahr, amount * 2)]; - create_foreign_on_ah_westend(roc_at_westend_parachains_v3, true, prefund_accounts); + create_foreign_on_ah_westend(roc_at_westend_parachains.clone(), true, prefund_accounts); let asset_owner: AccountId = AssetHubWestend::account_id_of(ALICE); PenpalB::force_create_foreign_asset( roc_at_westend_parachains.clone(), diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs index 87ae9aedd6f1..bf894a3baf58 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs @@ -69,13 +69,13 @@ pub(crate) fn weth_at_asset_hubs() -> Location { ) } -pub(crate) fn create_foreign_on_ah_rococo(id: v3::Location, sufficient: bool) { +pub(crate) fn create_foreign_on_ah_rococo(id: v4::Location, sufficient: bool) { let owner = AssetHubRococo::account_id_of(ALICE); AssetHubRococo::force_create_foreign_asset(id, owner, sufficient, ASSET_MIN_BALANCE, vec![]); } pub(crate) fn create_foreign_on_ah_westend( - id: v3::Location, + id: v4::Location, sufficient: bool, prefund_accounts: Vec<(AccountId, u128)>, ) { @@ -84,13 +84,13 @@ pub(crate) fn create_foreign_on_ah_westend( AssetHubWestend::force_create_foreign_asset(id, owner, sufficient, min, prefund_accounts); } -pub(crate) fn foreign_balance_on_ah_rococo(id: v3::Location, who: &AccountId) -> u128 { +pub(crate) fn foreign_balance_on_ah_rococo(id: v4::Location, who: &AccountId) -> u128 { AssetHubRococo::execute_with(|| { type Assets = ::ForeignAssets; >::balance(id, who) }) } -pub(crate) fn foreign_balance_on_ah_westend(id: v3::Location, who: &AccountId) -> u128 { +pub(crate) fn foreign_balance_on_ah_westend(id: v4::Location, who: &AccountId) -> u128 { AssetHubWestend::execute_with(|| { type Assets = ::ForeignAssets; >::balance(id, who) @@ -98,8 +98,8 @@ pub(crate) fn foreign_balance_on_ah_westend(id: v3::Location, who: &AccountId) - } // set up pool -pub(crate) fn set_up_pool_with_roc_on_ah_rococo(asset: v3::Location, is_foreign: bool) { - let roc: v3::Location = v3::Parent.into(); +pub(crate) fn set_up_pool_with_roc_on_ah_rococo(asset: v4::Location, is_foreign: bool) { + let roc: v4::Location = v4::Parent.into(); AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; let owner = AssetHubRococoSender::get(); @@ -108,13 +108,13 @@ pub(crate) fn set_up_pool_with_roc_on_ah_rococo(asset: v3::Location, is_foreign: if is_foreign { assert_ok!(::ForeignAssets::mint( signed_owner.clone(), - asset.into(), + asset.clone().into(), owner.clone().into(), 3_000_000_000_000, )); } else { - let asset_id = match asset.interior.split_last() { - (_, Some(v3::Junction::GeneralIndex(id))) => id as u32, + let asset_id = match asset.interior.last() { + Some(v4::Junction::GeneralIndex(id)) => *id as u32, _ => unreachable!(), }; assert_ok!(::Assets::mint( @@ -126,8 +126,8 @@ pub(crate) fn set_up_pool_with_roc_on_ah_rococo(asset: v3::Location, is_foreign: } assert_ok!(::AssetConversion::create_pool( signed_owner.clone(), - Box::new(roc), - Box::new(asset), + Box::new(roc.clone()), + Box::new(asset.clone()), )); assert_expected_events!( AssetHubRococo, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 7414adacc448..4c7356707ab6 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -83,10 +83,13 @@ use sp_runtime::{Perbill, RuntimeDebug}; use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use xcm_config::{ ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, GovernanceLocation, - PoolAssetsConvertedConcreteId, TokenLocation, TokenLocationV3, - TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocationV3, + PoolAssetsConvertedConcreteId, TokenLocation, TrustBackedAssetsConvertedConcreteId, + TrustBackedAssetsPalletLocation, }; +#[cfg(test)] +mod tests; + #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -324,11 +327,11 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< Assets, ForeignAssets, LocalFromLeft< - AssetIdForTrustBackedAssetsConvert, + AssetIdForTrustBackedAssetsConvert, AssetIdForTrustBackedAssets, - xcm::v3::Location, + xcm::v4::Location, >, - xcm::v3::Location, + xcm::v4::Location, AccountId, >; @@ -336,25 +339,25 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< pub type NativeAndAssets = fungible::UnionOf< Balances, LocalAndForeignAssets, - TargetFromLeft, - xcm::v3::Location, + TargetFromLeft, + xcm::v4::Location, AccountId, >; pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< AssetConversionPalletId, - (xcm::v3::Location, xcm::v3::Location), + (xcm::v4::Location, xcm::v4::Location), >; impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; - type AssetKind = xcm::v3::Location; + type AssetKind = xcm::v4::Location; type Assets = NativeAndAssets; type PoolId = (Self::AssetKind, Self::AssetKind); type PoolLocator = pallet_asset_conversion::WithFirstAsset< - TokenLocationV3, + TokenLocation, AccountId, Self::AssetKind, PoolIdToAccountId, @@ -362,7 +365,7 @@ impl pallet_asset_conversion::Config for Runtime { type PoolAssetId = u32; type PoolAssets = PoolAssets; type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam - type PoolSetupFeeAsset = TokenLocationV3; + type PoolSetupFeeAsset = TokenLocation; type PoolSetupFeeTarget = ResolveAssetTo; type LiquidityWithdrawalFee = LiquidityWithdrawalFee; type LPFee = ConstU32<3>; @@ -372,10 +375,10 @@ impl pallet_asset_conversion::Config for Runtime { type WeightInfo = weights::pallet_asset_conversion::WeightInfo; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = assets_common::benchmarks::AssetPairFactory< - TokenLocationV3, + TokenLocation, parachain_info::Pallet, xcm_config::TrustBackedAssetsPalletIndex, - xcm::v3::Location, + xcm::v4::Location, >; } @@ -409,17 +412,17 @@ pub type ForeignAssetsInstance = pallet_assets::Instance2; impl pallet_assets::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; - type AssetId = xcm::v3::Location; - type AssetIdParameter = xcm::v3::Location; + type AssetId = xcm::v4::Location; + type AssetIdParameter = xcm::v4::Location; type Currency = Balances; type CreateOrigin = ForeignCreators< ( - FromSiblingParachain, xcm::v3::Location>, - FromNetwork, + FromSiblingParachain, xcm::v4::Location>, + FromNetwork, ), ForeignCreatorsSovereignAccountOf, AccountId, - xcm::v3::Location, + xcm::v4::Location, >; type ForceOrigin = AssetsForceOrigin; type AssetDeposit = ForeignAssetsAssetDeposit; @@ -804,9 +807,9 @@ parameter_types! { impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type AssetId = xcm::v3::Location; + type AssetId = xcm::v4::Location; type OnChargeAssetTransaction = SwapAssetAdapter< - TokenLocationV3, + TokenLocation, NativeAndAssets, AssetConversion, ResolveAssetTo, @@ -1019,7 +1022,6 @@ pub type SignedExtra = ( pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. -#[allow(deprecated)] pub type Migrations = ( InitStorageVersions, // unreleased @@ -1234,16 +1236,16 @@ impl_runtime_apis! { impl pallet_asset_conversion::AssetConversionApi< Block, Balance, - xcm::v3::Location, + xcm::v4::Location, > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: xcm::v3::Location, asset2: xcm::v3::Location, amount: Balance, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: xcm::v4::Location, asset2: xcm::v4::Location, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: xcm::v3::Location, asset2: xcm::v3::Location, amount: Balance, include_fee: bool) -> Option { + fn quote_price_tokens_for_exact_tokens(asset1: xcm::v4::Location, asset2: xcm::v4::Location, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } - fn get_reserves(asset1: xcm::v3::Location, asset2: xcm::v3::Location) -> Option<(Balance, Balance)> { + fn get_reserves(asset1: xcm::v4::Location, asset2: xcm::v4::Location) -> Option<(Balance, Balance)> { AssetConversion::get_reserves(asset1, asset2).ok() } } @@ -1781,64 +1783,3 @@ cumulus_pallet_parachain_system::register_validate_block! { Runtime = Runtime, BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, } - -#[cfg(test)] -mod tests { - use super::*; - use crate::{CENTS, MILLICENTS}; - use sp_runtime::traits::Zero; - use sp_weights::WeightToFee; - use testnet_parachains_constants::rococo::fee; - - /// We can fit at least 1000 transfers in a block. - #[test] - fn sane_block_weight() { - use pallet_balances::WeightInfo; - let block = RuntimeBlockWeights::get().max_block; - let base = RuntimeBlockWeights::get().get(DispatchClass::Normal).base_extrinsic; - let transfer = - base + weights::pallet_balances::WeightInfo::::transfer_allow_death(); - - let fit = block.checked_div_per_component(&transfer).unwrap_or_default(); - assert!(fit >= 1000, "{} should be at least 1000", fit); - } - - /// The fee for one transfer is at most 1 CENT. - #[test] - fn sane_transfer_fee() { - use pallet_balances::WeightInfo; - let base = RuntimeBlockWeights::get().get(DispatchClass::Normal).base_extrinsic; - let transfer = - base + weights::pallet_balances::WeightInfo::::transfer_allow_death(); - - let fee: Balance = fee::WeightToFee::weight_to_fee(&transfer); - assert!(fee <= CENTS, "{} MILLICENTS should be at most 1000", fee / MILLICENTS); - } - - /// Weight is being charged for both dimensions. - #[test] - fn weight_charged_for_both_components() { - let fee: Balance = fee::WeightToFee::weight_to_fee(&Weight::from_parts(10_000, 0)); - assert!(!fee.is_zero(), "Charges for ref time"); - - let fee: Balance = fee::WeightToFee::weight_to_fee(&Weight::from_parts(0, 10_000)); - assert_eq!(fee, CENTS, "10kb maps to CENT"); - } - - /// Filling up a block by proof size is at most 30 times more expensive than ref time. - /// - /// This is just a sanity check. - #[test] - fn full_block_fee_ratio() { - let block = RuntimeBlockWeights::get().max_block; - let time_fee: Balance = - fee::WeightToFee::weight_to_fee(&Weight::from_parts(block.ref_time(), 0)); - let proof_fee: Balance = - fee::WeightToFee::weight_to_fee(&Weight::from_parts(0, block.proof_size())); - - let proof_o_time = proof_fee.checked_div(time_fee).unwrap_or_default(); - assert!(proof_o_time <= 30, "{} should be at most 30", proof_o_time); - let time_o_proof = time_fee.checked_div(proof_fee).unwrap_or_default(); - assert!(time_o_proof <= 30, "{} should be at most 30", time_o_proof); - } -} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/tests/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/tests/mod.rs new file mode 100644 index 000000000000..12c0bc4e1688 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/tests/mod.rs @@ -0,0 +1,72 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Tests for the Rococo runtime. + +use super::*; +use crate::{CENTS, MILLICENTS}; +use sp_runtime::traits::Zero; +use sp_weights::WeightToFee; +use testnet_parachains_constants::rococo::fee; + +/// We can fit at least 1000 transfers in a block. +#[test] +fn sane_block_weight() { + use pallet_balances::WeightInfo; + let block = RuntimeBlockWeights::get().max_block; + let base = RuntimeBlockWeights::get().get(DispatchClass::Normal).base_extrinsic; + let transfer = base + weights::pallet_balances::WeightInfo::::transfer_allow_death(); + + let fit = block.checked_div_per_component(&transfer).unwrap_or_default(); + assert!(fit >= 1000, "{} should be at least 1000", fit); +} + +/// The fee for one transfer is at most 1 CENT. +#[test] +fn sane_transfer_fee() { + use pallet_balances::WeightInfo; + let base = RuntimeBlockWeights::get().get(DispatchClass::Normal).base_extrinsic; + let transfer = base + weights::pallet_balances::WeightInfo::::transfer_allow_death(); + + let fee: Balance = fee::WeightToFee::weight_to_fee(&transfer); + assert!(fee <= CENTS, "{} MILLICENTS should be at most 1000", fee / MILLICENTS); +} + +/// Weight is being charged for both dimensions. +#[test] +fn weight_charged_for_both_components() { + let fee: Balance = fee::WeightToFee::weight_to_fee(&Weight::from_parts(10_000, 0)); + assert!(!fee.is_zero(), "Charges for ref time"); + + let fee: Balance = fee::WeightToFee::weight_to_fee(&Weight::from_parts(0, 10_000)); + assert_eq!(fee, CENTS, "10kb maps to CENT"); +} + +/// Filling up a block by proof size is at most 30 times more expensive than ref time. +/// +/// This is just a sanity check. +#[test] +fn full_block_fee_ratio() { + let block = RuntimeBlockWeights::get().max_block; + let time_fee: Balance = + fee::WeightToFee::weight_to_fee(&Weight::from_parts(block.ref_time(), 0)); + let proof_fee: Balance = + fee::WeightToFee::weight_to_fee(&Weight::from_parts(0, block.proof_size())); + + let proof_o_time = proof_fee.checked_div(time_fee).unwrap_or_default(); + assert!(proof_o_time <= 30, "{} should be at most 30", proof_o_time); + let time_o_proof = time_fee.checked_div(proof_fee).unwrap_or_default(); + assert!(time_o_proof <= 30, "{} should be at most 30", time_o_proof); +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index a11dca4f6d7c..2d1914e059bf 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -65,7 +65,6 @@ use xcm_executor::XcmExecutor; parameter_types! { pub const TokenLocation: Location = Location::parent(); - pub const TokenLocationV3: xcm::v3::Location = xcm::v3::Location::parent(); pub const RelayNetwork: NetworkId = NetworkId::Rococo; pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = @@ -74,8 +73,6 @@ parameter_types! { pub TrustBackedAssetsPalletLocation: Location = PalletInstance(TrustBackedAssetsPalletIndex::get()).into(); pub TrustBackedAssetsPalletIndex: u8 = ::index() as u8; - pub TrustBackedAssetsPalletLocationV3: xcm::v3::Location = - xcm::v3::Junction::PalletInstance(::index() as u8).into(); pub ForeignAssetsPalletLocation: Location = PalletInstance(::index() as u8).into(); pub PoolAssetsPalletLocation: Location = @@ -177,7 +174,7 @@ pub type ForeignAssetsConvertedConcreteId = assets_common::ForeignAssetsConverte StartsWithExplicitGlobalConsensus, ), Balance, - xcm::v3::Location, + xcm::v4::Location, >; /// Means for transacting foreign assets from different global consensus. @@ -361,7 +358,7 @@ impl xcm_executor::Config for XcmConfig { ResolveTo, >, cumulus_primitives_utility::SwapFirstAssetTrader< - TokenLocationV3, + TokenLocation, crate::AssetConversion, WeightToFee, crate::NativeAndAssets, @@ -369,7 +366,7 @@ impl xcm_executor::Config for XcmConfig { TrustBackedAssetsAsLocation< TrustBackedAssetsPalletLocation, Balance, - xcm::v3::Location, + xcm::v4::Location, >, ForeignAssetsConvertedConcreteId, ), @@ -502,9 +499,9 @@ pub type ForeignCreatorsSovereignAccountOf = ( /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] -impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { - fn create_asset_id_parameter(id: u32) -> xcm::v3::Location { - xcm::v3::Location::new(1, [xcm::v3::Junction::Parachain(id)]) +impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { + fn create_asset_id_parameter(id: u32) -> xcm::v4::Location { + xcm::v4::Location::new(1, [xcm::v4::Junction::Parachain(id)]) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs index ee1461b7f9c8..83f4f9ec3dc5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -85,7 +85,7 @@ fn slot_durations() -> SlotDurations { fn setup_pool_for_paying_fees_with_foreign_assets( (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): ( AccountId, - xcm::v3::Location, + Location, Balance, ), ) { @@ -93,7 +93,7 @@ fn setup_pool_for_paying_fees_with_foreign_assets( // setup a pool to pay fees with `foreign_asset_id_location` tokens let pool_owner: AccountId = [14u8; 32].into(); - let native_asset = xcm::v3::Location::parent(); + let native_asset = Location::parent(); let pool_liquidity: Balance = existential_deposit.max(foreign_asset_id_minimum_balance).mul(100_000); @@ -105,15 +105,15 @@ fn setup_pool_for_paying_fees_with_foreign_assets( assert_ok!(ForeignAssets::mint( RuntimeOrigin::signed(foreign_asset_owner), - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), pool_owner.clone().into(), (foreign_asset_id_minimum_balance + pool_liquidity).mul(2).into(), )); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(pool_owner.clone()), - Box::new(native_asset.into()), - Box::new(foreign_asset_id_location.into()) + Box::new(native_asset.clone().into()), + Box::new(foreign_asset_id_location.clone().into()) )); assert_ok!(AssetConversion::add_liquidity( @@ -217,24 +217,14 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), - Box::new( - xcm::v3::Location::try_from(native_location.clone()).expect("conversion works") - ), - Box::new( - xcm::v3::Location::try_from(asset_1_location.clone()) - .expect("conversion works") - ) + Box::new(Location::try_from(native_location.clone()).expect("conversion works")), + Box::new(Location::try_from(asset_1_location.clone()).expect("conversion works")) )); assert_ok!(AssetConversion::add_liquidity( RuntimeHelper::origin_of(bob.clone()), - Box::new( - xcm::v3::Location::try_from(native_location.clone()).expect("conversion works") - ), - Box::new( - xcm::v3::Location::try_from(asset_1_location.clone()) - .expect("conversion works") - ), + Box::new(Location::try_from(native_location.clone()).expect("conversion works")), + Box::new(Location::try_from(asset_1_location.clone()).expect("conversion works")), pool_liquidity, pool_liquidity, 1, @@ -270,8 +260,8 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { let refund_weight = Weight::from_parts(1_000_000_000, 0); let refund = WeightToFee::weight_to_fee(&refund_weight); let (reserve1, reserve2) = AssetConversion::get_reserves( - xcm::v3::Location::try_from(native_location).expect("conversion works"), - xcm::v3::Location::try_from(asset_1_location.clone()).expect("conversion works"), + Location::try_from(native_location).expect("conversion works"), + Location::try_from(asset_1_location.clone()).expect("conversion works"), ) .unwrap(); let asset_refund = @@ -309,14 +299,10 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { let bob: AccountId = SOME_ASSET_ADMIN.into(); let staking_pot = CollatorSelection::account_id(); let native_location = - xcm::v3::Location::try_from(TokenLocation::get()).expect("conversion works"); - let foreign_location = xcm::v3::Location { + Location::try_from(TokenLocation::get()).expect("conversion works"); + let foreign_location = Location { parents: 1, - interior: ( - xcm::v3::Junction::Parachain(1234), - xcm::v3::Junction::GeneralIndex(12345), - ) - .into(), + interior: (Junction::Parachain(1234), Junction::GeneralIndex(12345)).into(), }; // bob's initial balance for native and `asset1` assets. let initial_balance = 200 * UNITS; @@ -325,26 +311,26 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { // init asset, balances and pool. assert_ok!(>::create( - foreign_location, + foreign_location.clone(), bob.clone(), true, 10 )); - assert_ok!(ForeignAssets::mint_into(foreign_location, &bob, initial_balance)); + assert_ok!(ForeignAssets::mint_into(foreign_location.clone(), &bob, initial_balance)); assert_ok!(Balances::mint_into(&bob, initial_balance)); assert_ok!(Balances::mint_into(&staking_pot, initial_balance)); assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), - Box::new(native_location), - Box::new(foreign_location) + Box::new(native_location.clone()), + Box::new(foreign_location.clone()) )); assert_ok!(AssetConversion::add_liquidity( RuntimeHelper::origin_of(bob.clone()), - Box::new(native_location), - Box::new(foreign_location), + Box::new(native_location.clone()), + Box::new(foreign_location.clone()), pool_liquidity, pool_liquidity, 1, @@ -353,11 +339,9 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { )); // keep initial total issuance to assert later. - let asset_total_issuance = ForeignAssets::total_issuance(foreign_location); + let asset_total_issuance = ForeignAssets::total_issuance(foreign_location.clone()); let native_total_issuance = Balances::total_issuance(); - let foreign_location_latest: Location = foreign_location.try_into().unwrap(); - // prepare input to buy weight. let weight = Weight::from_parts(4_000_000_000, 0); let fee = WeightToFee::weight_to_fee(&weight); @@ -365,7 +349,7 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { AssetConversion::get_amount_in(&fee, &pool_liquidity, &pool_liquidity).unwrap(); let extra_amount = 100; let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - let payment: Asset = (foreign_location_latest.clone(), asset_fee + extra_amount).into(); + let payment: Asset = (foreign_location.clone(), asset_fee + extra_amount).into(); // init trader and buy weight. let mut trader = ::Trader::new(); @@ -373,13 +357,11 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { trader.buy_weight(weight, payment.into(), &ctx).expect("Expected Ok"); // assert. - let unused_amount = unused_asset - .fungible - .get(&foreign_location_latest.clone().into()) - .map_or(0, |a| *a); + let unused_amount = + unused_asset.fungible.get(&foreign_location.clone().into()).map_or(0, |a| *a); assert_eq!(unused_amount, extra_amount); assert_eq!( - ForeignAssets::total_issuance(foreign_location), + ForeignAssets::total_issuance(foreign_location.clone()), asset_total_issuance + asset_fee ); @@ -387,13 +369,13 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { let refund_weight = Weight::from_parts(1_000_000_000, 0); let refund = WeightToFee::weight_to_fee(&refund_weight); let (reserve1, reserve2) = - AssetConversion::get_reserves(native_location, foreign_location).unwrap(); + AssetConversion::get_reserves(native_location, foreign_location.clone()).unwrap(); let asset_refund = AssetConversion::get_amount_out(&refund, &reserve1, &reserve2).unwrap(); // refund. let actual_refund = trader.refund_weight(refund_weight, &ctx).unwrap(); - assert_eq!(actual_refund, (foreign_location_latest, asset_refund).into()); + assert_eq!(actual_refund, (foreign_location.clone(), asset_refund).into()); // assert. assert_eq!(Balances::balance(&staking_pot), initial_balance); @@ -500,17 +482,13 @@ fn test_foreign_asset_xcm_take_first_trader() { .execute_with(|| { // We need root origin to create a sufficient asset let minimum_asset_balance = 3333333_u128; - let foreign_location = xcm::v3::Location { + let foreign_location = Location { parents: 1, - interior: ( - xcm::v3::Junction::Parachain(1234), - xcm::v3::Junction::GeneralIndex(12345), - ) - .into(), + interior: (Junction::Parachain(1234), Junction::GeneralIndex(12345)).into(), }; assert_ok!(ForeignAssets::force_create( RuntimeHelper::root_origin(), - foreign_location.into(), + foreign_location.clone().into(), AccountId::from(ALICE).into(), true, minimum_asset_balance @@ -519,13 +497,11 @@ fn test_foreign_asset_xcm_take_first_trader() { // We first mint enough asset for the account to exist for assets assert_ok!(ForeignAssets::mint( RuntimeHelper::origin_of(AccountId::from(ALICE)), - foreign_location.into(), + foreign_location.clone().into(), AccountId::from(ALICE).into(), minimum_asset_balance )); - let asset_location_v4: Location = foreign_location.try_into().unwrap(); - // Set Alice as block author, who will receive fees RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); @@ -535,7 +511,7 @@ fn test_foreign_asset_xcm_take_first_trader() { // Lets calculate amount needed let asset_amount_needed = ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles( - foreign_location, + foreign_location.clone(), bought ) .expect("failed to compute"); @@ -543,7 +519,7 @@ fn test_foreign_asset_xcm_take_first_trader() { // Lets pay with: asset_amount_needed + asset_amount_extra let asset_amount_extra = 100_u128; let asset: Asset = - (asset_location_v4.clone(), asset_amount_needed + asset_amount_extra).into(); + (foreign_location.clone(), asset_amount_needed + asset_amount_extra).into(); let mut trader = ::Trader::new(); let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; @@ -551,16 +527,15 @@ fn test_foreign_asset_xcm_take_first_trader() { // Lets buy_weight and make sure buy_weight does not return an error let unused_assets = trader.buy_weight(bought, asset.into(), &ctx).expect("Expected Ok"); // Check whether a correct amount of unused assets is returned - assert_ok!( - unused_assets.ensure_contains(&(asset_location_v4, asset_amount_extra).into()) - ); + assert_ok!(unused_assets + .ensure_contains(&(foreign_location.clone(), asset_amount_extra).into())); // Drop trader drop(trader); // Make sure author(Alice) has received the amount assert_eq!( - ForeignAssets::balance(foreign_location, AccountId::from(ALICE)), + ForeignAssets::balance(foreign_location.clone(), AccountId::from(ALICE)), minimum_asset_balance + asset_amount_needed ); @@ -841,15 +816,13 @@ fn test_assets_balances_api_works() { .build() .execute_with(|| { let local_asset_id = 1; - let foreign_asset_id_location = xcm::v3::Location::new( - 1, - [xcm::v3::Junction::Parachain(1234), xcm::v3::Junction::GeneralIndex(12345)], - ); + let foreign_asset_id_location = + Location::new(1, [Junction::Parachain(1234), Junction::GeneralIndex(12345)]); // check before assert_eq!(Assets::balance(local_asset_id, AccountId::from(ALICE)), 0); assert_eq!( - ForeignAssets::balance(foreign_asset_id_location, AccountId::from(ALICE)), + ForeignAssets::balance(foreign_asset_id_location.clone(), AccountId::from(ALICE)), 0 ); assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 0); @@ -886,7 +859,7 @@ fn test_assets_balances_api_works() { let foreign_asset_minimum_asset_balance = 3333333_u128; assert_ok!(ForeignAssets::force_create( RuntimeHelper::root_origin(), - foreign_asset_id_location, + foreign_asset_id_location.clone(), AccountId::from(SOME_ASSET_ADMIN).into(), false, foreign_asset_minimum_asset_balance @@ -895,7 +868,7 @@ fn test_assets_balances_api_works() { // We first mint enough asset for the account to exist for assets assert_ok!(ForeignAssets::mint( RuntimeHelper::origin_of(AccountId::from(SOME_ASSET_ADMIN)), - foreign_asset_id_location, + foreign_asset_id_location.clone(), AccountId::from(ALICE).into(), 6 * foreign_asset_minimum_asset_balance )); @@ -906,7 +879,7 @@ fn test_assets_balances_api_works() { minimum_asset_balance ); assert_eq!( - ForeignAssets::balance(foreign_asset_id_location, AccountId::from(ALICE)), + ForeignAssets::balance(foreign_asset_id_location.clone(), AccountId::from(ALICE)), 6 * minimum_asset_balance ); assert_eq!(Balances::free_balance(AccountId::from(ALICE)), some_currency); @@ -932,10 +905,8 @@ fn test_assets_balances_api_works() { .into()))); // check foreign asset assert!(result.inner().iter().any(|asset| asset.eq(&( - WithLatestLocationConverter::::convert_back( - &foreign_asset_id_location - ) - .unwrap(), + WithLatestLocationConverter::::convert_back(&foreign_asset_id_location) + .unwrap(), 6 * foreign_asset_minimum_asset_balance ) .into()))); @@ -1025,14 +996,11 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_ Runtime, XcmConfig, ForeignAssetsInstance, - xcm::v3::Location, + Location, JustTry, collator_session_keys(), ExistentialDeposit::get(), - xcm::v3::Location::new( - 1, - [xcm::v3::Junction::Parachain(1313), xcm::v3::Junction::GeneralIndex(12345)] - ), + Location::new(1, [Junction::Parachain(1313), Junction::GeneralIndex(12345)]), Box::new(|| { assert!(Assets::asset_ids().collect::>().is_empty()); }), @@ -1047,8 +1015,8 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p WeightToFee, ForeignCreatorsSovereignAccountOf, ForeignAssetsInstance, - xcm::v3::Location, - WithLatestLocationConverter, + Location, + WithLatestLocationConverter, collator_session_keys(), ExistentialDeposit::get(), AssetDeposit::get(), @@ -1138,16 +1106,17 @@ mod asset_hub_rococo_tests { let block_author_account = AccountId::from(BLOCK_AUTHOR_ACCOUNT); let staking_pot = StakingPot::get(); - let foreign_asset_id_location = xcm::v3::Location::new( - 2, - [xcm::v3::Junction::GlobalConsensus(xcm::v3::NetworkId::Westend)], - ); + let foreign_asset_id_location = + Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); let foreign_asset_id_minimum_balance = 1_000_000_000; // sovereign account as foreign asset owner (can be whoever for this scenario) let foreign_asset_owner = LocationToAccountId::convert_location(&Location::parent()).unwrap(); - let foreign_asset_create_params = - (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance); + let foreign_asset_create_params = ( + foreign_asset_owner, + foreign_asset_id_location.clone(), + foreign_asset_id_minimum_balance, + ); asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_works::< Runtime, @@ -1181,7 +1150,7 @@ mod asset_hub_rococo_tests { // check now foreign asset for staking pot assert_eq!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &staking_pot ), 0 @@ -1195,7 +1164,7 @@ mod asset_hub_rococo_tests { // staking pot receives no foreign assets assert_eq!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &staking_pot ), 0 @@ -1211,16 +1180,17 @@ mod asset_hub_rococo_tests { let block_author_account = AccountId::from(BLOCK_AUTHOR_ACCOUNT); let staking_pot = StakingPot::get(); - let foreign_asset_id_location = xcm::v3::Location::new( - 2, - [xcm::v3::Junction::GlobalConsensus(xcm::v3::NetworkId::Westend)], - ); + let foreign_asset_id_location = + Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); let foreign_asset_id_minimum_balance = 1_000_000_000; // sovereign account as foreign asset owner (can be whoever for this scenario) let foreign_asset_owner = LocationToAccountId::convert_location(&Location::parent()).unwrap(); - let foreign_asset_create_params = - (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance); + let foreign_asset_create_params = ( + foreign_asset_owner, + foreign_asset_id_location.clone(), + foreign_asset_id_minimum_balance, + ); asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_works::< Runtime, @@ -1245,7 +1215,7 @@ mod asset_hub_rococo_tests { // check block author before assert_eq!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &block_author_account ), 0 @@ -1255,7 +1225,7 @@ mod asset_hub_rococo_tests { // `TakeFirstAssetTrader` puts fees to the block author assert!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &block_author_account ) > 0 ); diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 201698ecb7f9..ebbc000d1413 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -80,8 +80,7 @@ use testnet_parachains_constants::westend::{ use xcm_config::{ ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, PoolAssetsConvertedConcreteId, TrustBackedAssetsConvertedConcreteId, - TrustBackedAssetsPalletLocationV3, WestendLocation, WestendLocationV3, - XcmOriginToTransactDispatchOrigin, + TrustBackedAssetsPalletLocation, WestendLocation, XcmOriginToTransactDispatchOrigin, }; #[cfg(any(feature = "std", test))] @@ -326,11 +325,11 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< Assets, ForeignAssets, LocalFromLeft< - AssetIdForTrustBackedAssetsConvert, + AssetIdForTrustBackedAssetsConvert, AssetIdForTrustBackedAssets, - xcm::v3::Location, + xcm::v4::Location, >, - xcm::v3::Location, + xcm::v4::Location, AccountId, >; @@ -338,25 +337,25 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< pub type NativeAndAssets = fungible::UnionOf< Balances, LocalAndForeignAssets, - TargetFromLeft, - xcm::v3::Location, + TargetFromLeft, + xcm::v4::Location, AccountId, >; pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< AssetConversionPalletId, - (xcm::v3::Location, xcm::v3::Location), + (xcm::v4::Location, xcm::v4::Location), >; impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; - type AssetKind = xcm::v3::Location; + type AssetKind = xcm::v4::Location; type Assets = NativeAndAssets; type PoolId = (Self::AssetKind, Self::AssetKind); type PoolLocator = pallet_asset_conversion::WithFirstAsset< - WestendLocationV3, + WestendLocation, AccountId, Self::AssetKind, PoolIdToAccountId, @@ -364,7 +363,7 @@ impl pallet_asset_conversion::Config for Runtime { type PoolAssetId = u32; type PoolAssets = PoolAssets; type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam - type PoolSetupFeeAsset = WestendLocationV3; + type PoolSetupFeeAsset = WestendLocation; type PoolSetupFeeTarget = ResolveAssetTo; type LiquidityWithdrawalFee = LiquidityWithdrawalFee; type LPFee = ConstU32<3>; @@ -374,10 +373,10 @@ impl pallet_asset_conversion::Config for Runtime { type WeightInfo = weights::pallet_asset_conversion::WeightInfo; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = assets_common::benchmarks::AssetPairFactory< - WestendLocationV3, + WestendLocation, parachain_info::Pallet, xcm_config::TrustBackedAssetsPalletIndex, - xcm::v3::Location, + xcm::v4::Location, >; } @@ -411,17 +410,17 @@ pub type ForeignAssetsInstance = pallet_assets::Instance2; impl pallet_assets::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; - type AssetId = xcm::v3::Location; - type AssetIdParameter = xcm::v3::Location; + type AssetId = xcm::v4::Location; + type AssetIdParameter = xcm::v4::Location; type Currency = Balances; type CreateOrigin = ForeignCreators< ( - FromSiblingParachain, xcm::v3::Location>, - FromNetwork, + FromSiblingParachain, xcm::v4::Location>, + FromNetwork, ), ForeignCreatorsSovereignAccountOf, AccountId, - xcm::v3::Location, + xcm::v4::Location, >; type ForceOrigin = AssetsForceOrigin; type AssetDeposit = ForeignAssetsAssetDeposit; @@ -801,9 +800,9 @@ parameter_types! { impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type AssetId = xcm::v3::Location; + type AssetId = xcm::v4::Location; type OnChargeAssetTransaction = SwapAssetAdapter< - WestendLocationV3, + WestendLocation, NativeAndAssets, AssetConversion, ResolveAssetTo, @@ -1331,18 +1330,18 @@ impl_runtime_apis! { impl pallet_asset_conversion::AssetConversionApi< Block, Balance, - xcm::v3::Location, + xcm::v4::Location, > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: xcm::v3::Location, asset2: xcm::v3::Location, amount: Balance, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: xcm::v4::Location, asset2: xcm::v4::Location, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: xcm::v3::Location, asset2: xcm::v3::Location, amount: Balance, include_fee: bool) -> Option { + fn quote_price_tokens_for_exact_tokens(asset1: xcm::v4::Location, asset2: xcm::v4::Location, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } - fn get_reserves(asset1: xcm::v3::Location, asset2: xcm::v3::Location) -> Option<(Balance, Balance)> { + fn get_reserves(asset1: xcm::v4::Location, asset2: xcm::v4::Location) -> Option<(Balance, Balance)> { AssetConversion::get_reserves(asset1, asset2).ok() } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index bf45e146e334..d61381d3f50b 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -62,7 +62,6 @@ use xcm_executor::XcmExecutor; parameter_types! { pub const WestendLocation: Location = Location::parent(); - pub const WestendLocationV3: xcm::v3::Location = xcm::v3::Location::parent(); pub const RelayNetwork: Option = Some(NetworkId::Westend); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = @@ -71,8 +70,6 @@ parameter_types! { pub TrustBackedAssetsPalletLocation: Location = PalletInstance(TrustBackedAssetsPalletIndex::get()).into(); pub TrustBackedAssetsPalletIndex: u8 = ::index() as u8; - pub TrustBackedAssetsPalletLocationV3: xcm::v3::Location = - xcm::v3::Junction::PalletInstance(::index() as u8).into(); pub ForeignAssetsPalletLocation: Location = PalletInstance(::index() as u8).into(); pub PoolAssetsPalletLocation: Location = @@ -174,7 +171,7 @@ pub type ForeignAssetsConvertedConcreteId = assets_common::ForeignAssetsConverte StartsWithExplicitGlobalConsensus, ), Balance, - xcm::v3::Location, + xcm::v4::Location, >; /// Means for transacting foreign assets from different global consensus. @@ -384,7 +381,7 @@ impl xcm_executor::Config for XcmConfig { ResolveTo, >, cumulus_primitives_utility::SwapFirstAssetTrader< - WestendLocationV3, + WestendLocation, crate::AssetConversion, WeightToFee, crate::NativeAndAssets, @@ -392,7 +389,7 @@ impl xcm_executor::Config for XcmConfig { TrustBackedAssetsAsLocation< TrustBackedAssetsPalletLocation, Balance, - xcm::v3::Location, + xcm::v4::Location, >, ForeignAssetsConvertedConcreteId, ), @@ -526,9 +523,9 @@ pub type ForeignCreatorsSovereignAccountOf = ( /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] -impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { - fn create_asset_id_parameter(id: u32) -> xcm::v3::Location { - xcm::v3::Location::new(1, [xcm::v3::Junction::Parachain(id)]) +impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { + fn create_asset_id_parameter(id: u32) -> xcm::v4::Location { + xcm::v4::Location::new(1, [xcm::v4::Junction::Parachain(id)]) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 48e6c11d268c..1c334d6f84f8 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -86,7 +86,7 @@ fn slot_durations() -> SlotDurations { fn setup_pool_for_paying_fees_with_foreign_assets( (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): ( AccountId, - xcm::v3::Location, + xcm::v4::Location, Balance, ), ) { @@ -94,7 +94,7 @@ fn setup_pool_for_paying_fees_with_foreign_assets( // setup a pool to pay fees with `foreign_asset_id_location` tokens let pool_owner: AccountId = [14u8; 32].into(); - let native_asset = xcm::v3::Location::parent(); + let native_asset = xcm::v4::Location::parent(); let pool_liquidity: Balance = existential_deposit.max(foreign_asset_id_minimum_balance).mul(100_000); @@ -106,15 +106,15 @@ fn setup_pool_for_paying_fees_with_foreign_assets( assert_ok!(ForeignAssets::mint( RuntimeOrigin::signed(foreign_asset_owner), - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), pool_owner.clone().into(), (foreign_asset_id_minimum_balance + pool_liquidity).mul(2).into(), )); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(pool_owner.clone()), - Box::new(native_asset.into()), - Box::new(foreign_asset_id_location.into()) + Box::new(native_asset.clone().into()), + Box::new(foreign_asset_id_location.clone().into()) )); assert_ok!(AssetConversion::add_liquidity( @@ -219,10 +219,10 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), Box::new( - xcm::v3::Location::try_from(native_location.clone()).expect("conversion works") + xcm::v4::Location::try_from(native_location.clone()).expect("conversion works") ), Box::new( - xcm::v3::Location::try_from(asset_1_location.clone()) + xcm::v4::Location::try_from(asset_1_location.clone()) .expect("conversion works") ) )); @@ -230,10 +230,10 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { assert_ok!(AssetConversion::add_liquidity( RuntimeHelper::origin_of(bob.clone()), Box::new( - xcm::v3::Location::try_from(native_location.clone()).expect("conversion works") + xcm::v4::Location::try_from(native_location.clone()).expect("conversion works") ), Box::new( - xcm::v3::Location::try_from(asset_1_location.clone()) + xcm::v4::Location::try_from(asset_1_location.clone()) .expect("conversion works") ), pool_liquidity, @@ -271,8 +271,8 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { let refund_weight = Weight::from_parts(1_000_000_000, 0); let refund = WeightToFee::weight_to_fee(&refund_weight); let (reserve1, reserve2) = AssetConversion::get_reserves( - xcm::v3::Location::try_from(native_location).expect("conversion works"), - xcm::v3::Location::try_from(asset_1_location.clone()).expect("conversion works"), + xcm::v4::Location::try_from(native_location).expect("conversion works"), + xcm::v4::Location::try_from(asset_1_location.clone()).expect("conversion works"), ) .unwrap(); let asset_refund = @@ -310,12 +310,12 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { let bob: AccountId = SOME_ASSET_ADMIN.into(); let staking_pot = CollatorSelection::account_id(); let native_location = - xcm::v3::Location::try_from(WestendLocation::get()).expect("conversion works"); - let foreign_location = xcm::v3::Location { + xcm::v4::Location::try_from(WestendLocation::get()).expect("conversion works"); + let foreign_location = xcm::v4::Location { parents: 1, interior: ( - xcm::v3::Junction::Parachain(1234), - xcm::v3::Junction::GeneralIndex(12345), + xcm::v4::Junction::Parachain(1234), + xcm::v4::Junction::GeneralIndex(12345), ) .into(), }; @@ -326,26 +326,26 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { // init asset, balances and pool. assert_ok!(>::create( - foreign_location, + foreign_location.clone(), bob.clone(), true, 10 )); - assert_ok!(ForeignAssets::mint_into(foreign_location, &bob, initial_balance)); + assert_ok!(ForeignAssets::mint_into(foreign_location.clone(), &bob, initial_balance)); assert_ok!(Balances::mint_into(&bob, initial_balance)); assert_ok!(Balances::mint_into(&staking_pot, initial_balance)); assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), - Box::new(native_location), - Box::new(foreign_location) + Box::new(native_location.clone()), + Box::new(foreign_location.clone()) )); assert_ok!(AssetConversion::add_liquidity( RuntimeHelper::origin_of(bob.clone()), - Box::new(native_location), - Box::new(foreign_location), + Box::new(native_location.clone()), + Box::new(foreign_location.clone()), pool_liquidity, pool_liquidity, 1, @@ -354,11 +354,9 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { )); // keep initial total issuance to assert later. - let asset_total_issuance = ForeignAssets::total_issuance(foreign_location); + let asset_total_issuance = ForeignAssets::total_issuance(foreign_location.clone()); let native_total_issuance = Balances::total_issuance(); - let foreign_location_latest: Location = foreign_location.try_into().unwrap(); - // prepare input to buy weight. let weight = Weight::from_parts(4_000_000_000, 0); let fee = WeightToFee::weight_to_fee(&weight); @@ -366,7 +364,7 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { AssetConversion::get_amount_in(&fee, &pool_liquidity, &pool_liquidity).unwrap(); let extra_amount = 100; let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - let payment: Asset = (foreign_location_latest.clone(), asset_fee + extra_amount).into(); + let payment: Asset = (foreign_location.clone(), asset_fee + extra_amount).into(); // init trader and buy weight. let mut trader = ::Trader::new(); @@ -374,13 +372,11 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { trader.buy_weight(weight, payment.into(), &ctx).expect("Expected Ok"); // assert. - let unused_amount = unused_asset - .fungible - .get(&foreign_location_latest.clone().into()) - .map_or(0, |a| *a); + let unused_amount = + unused_asset.fungible.get(&foreign_location.clone().into()).map_or(0, |a| *a); assert_eq!(unused_amount, extra_amount); assert_eq!( - ForeignAssets::total_issuance(foreign_location), + ForeignAssets::total_issuance(foreign_location.clone()), asset_total_issuance + asset_fee ); @@ -388,13 +384,13 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { let refund_weight = Weight::from_parts(1_000_000_000, 0); let refund = WeightToFee::weight_to_fee(&refund_weight); let (reserve1, reserve2) = - AssetConversion::get_reserves(native_location, foreign_location).unwrap(); + AssetConversion::get_reserves(native_location, foreign_location.clone()).unwrap(); let asset_refund = AssetConversion::get_amount_out(&refund, &reserve1, &reserve2).unwrap(); // refund. let actual_refund = trader.refund_weight(refund_weight, &ctx).unwrap(); - assert_eq!(actual_refund, (foreign_location_latest, asset_refund).into()); + assert_eq!(actual_refund, (foreign_location.clone(), asset_refund).into()); // assert. assert_eq!(Balances::balance(&staking_pot), initial_balance); @@ -501,17 +497,17 @@ fn test_foreign_asset_xcm_take_first_trader() { .execute_with(|| { // We need root origin to create a sufficient asset let minimum_asset_balance = 3333333_u128; - let foreign_location = xcm::v3::Location { + let foreign_location = xcm::v4::Location { parents: 1, interior: ( - xcm::v3::Junction::Parachain(1234), - xcm::v3::Junction::GeneralIndex(12345), + xcm::v4::Junction::Parachain(1234), + xcm::v4::Junction::GeneralIndex(12345), ) .into(), }; assert_ok!(ForeignAssets::force_create( RuntimeHelper::root_origin(), - foreign_location.into(), + foreign_location.clone().into(), AccountId::from(ALICE).into(), true, minimum_asset_balance @@ -520,12 +516,12 @@ fn test_foreign_asset_xcm_take_first_trader() { // We first mint enough asset for the account to exist for assets assert_ok!(ForeignAssets::mint( RuntimeHelper::origin_of(AccountId::from(ALICE)), - foreign_location.into(), + foreign_location.clone().into(), AccountId::from(ALICE).into(), minimum_asset_balance )); - let asset_location_v4: Location = foreign_location.try_into().unwrap(); + let asset_location_v4: Location = foreign_location.clone().try_into().unwrap(); // Set Alice as block author, who will receive fees RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); @@ -534,7 +530,7 @@ fn test_foreign_asset_xcm_take_first_trader() { let bought = Weight::from_parts(4_000_000_000u64, 0); // Lets calculate amount needed - let asset_amount_needed = ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles(foreign_location, bought) + let asset_amount_needed = ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles(foreign_location.clone(), bought) .expect("failed to compute"); // Lets pay with: asset_amount_needed + asset_amount_extra @@ -557,7 +553,7 @@ fn test_foreign_asset_xcm_take_first_trader() { // Make sure author(Alice) has received the amount assert_eq!( - ForeignAssets::balance(foreign_location, AccountId::from(ALICE)), + ForeignAssets::balance(foreign_location.clone(), AccountId::from(ALICE)), minimum_asset_balance + asset_amount_needed ); @@ -837,11 +833,11 @@ fn test_assets_balances_api_works() { .build() .execute_with(|| { let local_asset_id = 1; - let foreign_asset_id_location = xcm::v3::Location { + let foreign_asset_id_location = xcm::v4::Location { parents: 1, interior: [ - xcm::v3::Junction::Parachain(1234), - xcm::v3::Junction::GeneralIndex(12345), + xcm::v4::Junction::Parachain(1234), + xcm::v4::Junction::GeneralIndex(12345), ] .into(), }; @@ -849,7 +845,7 @@ fn test_assets_balances_api_works() { // check before assert_eq!(Assets::balance(local_asset_id, AccountId::from(ALICE)), 0); assert_eq!( - ForeignAssets::balance(foreign_asset_id_location, AccountId::from(ALICE)), + ForeignAssets::balance(foreign_asset_id_location.clone(), AccountId::from(ALICE)), 0 ); assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 0); @@ -886,7 +882,7 @@ fn test_assets_balances_api_works() { let foreign_asset_minimum_asset_balance = 3333333_u128; assert_ok!(ForeignAssets::force_create( RuntimeHelper::root_origin(), - foreign_asset_id_location, + foreign_asset_id_location.clone(), AccountId::from(SOME_ASSET_ADMIN).into(), false, foreign_asset_minimum_asset_balance @@ -895,7 +891,7 @@ fn test_assets_balances_api_works() { // We first mint enough asset for the account to exist for assets assert_ok!(ForeignAssets::mint( RuntimeHelper::origin_of(AccountId::from(SOME_ASSET_ADMIN)), - foreign_asset_id_location, + foreign_asset_id_location.clone(), AccountId::from(ALICE).into(), 6 * foreign_asset_minimum_asset_balance )); @@ -906,7 +902,7 @@ fn test_assets_balances_api_works() { minimum_asset_balance ); assert_eq!( - ForeignAssets::balance(foreign_asset_id_location, AccountId::from(ALICE)), + ForeignAssets::balance(foreign_asset_id_location.clone(), AccountId::from(ALICE)), 6 * minimum_asset_balance ); assert_eq!(Balances::free_balance(AccountId::from(ALICE)), some_currency); @@ -932,7 +928,7 @@ fn test_assets_balances_api_works() { .into()))); // check foreign asset assert!(result.inner().iter().any(|asset| asset.eq(&( - WithLatestLocationConverter::::convert_back( + WithLatestLocationConverter::::convert_back( &foreign_asset_id_location ) .unwrap(), @@ -1025,13 +1021,13 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_ Runtime, XcmConfig, ForeignAssetsInstance, - xcm::v3::Location, + xcm::v4::Location, JustTry, collator_session_keys(), ExistentialDeposit::get(), - xcm::v3::Location { + xcm::v4::Location { parents: 1, - interior: [xcm::v3::Junction::Parachain(1313), xcm::v3::Junction::GeneralIndex(12345)] + interior: [xcm::v4::Junction::Parachain(1313), xcm::v4::Junction::GeneralIndex(12345)] .into() }, Box::new(|| { @@ -1048,8 +1044,8 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p WeightToFee, ForeignCreatorsSovereignAccountOf, ForeignAssetsInstance, - xcm::v3::Location, - WithLatestLocationConverter, + xcm::v4::Location, + WithLatestLocationConverter, collator_session_keys(), ExistentialDeposit::get(), AssetDeposit::get(), @@ -1127,12 +1123,12 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_pool_s let staking_pot = StakingPot::get(); let foreign_asset_id_location = - xcm::v3::Location::new(2, [xcm::v3::Junction::GlobalConsensus(xcm::v3::NetworkId::Rococo)]); + xcm::v4::Location::new(2, [xcm::v4::Junction::GlobalConsensus(xcm::v4::NetworkId::Rococo)]); let foreign_asset_id_minimum_balance = 1_000_000_000; // sovereign account as foreign asset owner (can be whoever for this scenario) let foreign_asset_owner = LocationToAccountId::convert_location(&Location::parent()).unwrap(); let foreign_asset_create_params = - (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance); + (foreign_asset_owner, foreign_asset_id_location.clone(), foreign_asset_id_minimum_balance); asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_works::< Runtime, @@ -1166,7 +1162,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_pool_s // check now foreign asset for staking pot assert_eq!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &staking_pot ), 0 @@ -1180,7 +1176,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_pool_s // staking pot receives no foreign assets assert_eq!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &staking_pot ), 0 @@ -1196,12 +1192,12 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic let staking_pot = StakingPot::get(); let foreign_asset_id_location = - xcm::v3::Location::new(2, [xcm::v3::Junction::GlobalConsensus(xcm::v3::NetworkId::Rococo)]); + xcm::v4::Location::new(2, [xcm::v4::Junction::GlobalConsensus(xcm::v4::NetworkId::Rococo)]); let foreign_asset_id_minimum_balance = 1_000_000_000; // sovereign account as foreign asset owner (can be whoever for this scenario) let foreign_asset_owner = LocationToAccountId::convert_location(&Location::parent()).unwrap(); let foreign_asset_create_params = - (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance); + (foreign_asset_owner, foreign_asset_id_location.clone(), foreign_asset_id_minimum_balance); asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_works::< Runtime, @@ -1226,7 +1222,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic // check block author before assert_eq!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &block_author_account ), 0 @@ -1236,7 +1232,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic // `TakeFirstAssetTrader` puts fees to the block author assert!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &block_author_account ) > 0 ); diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs index 884b71369e79..67b585ecfe86 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -367,9 +367,9 @@ pub fn teleports_for_foreign_assets_works< ::Balance: From + Into, SovereignAccountOf: ConvertLocation>, >::AssetId: - From + Into, + From + Into, >::AssetIdParameter: - From + Into, + From + Into, >::Balance: From + Into, ::AccountId: @@ -381,11 +381,11 @@ pub fn teleports_for_foreign_assets_works< { // foreign parachain with the same consensus currency as asset let foreign_para_id = 2222; - let foreign_asset_id_location = xcm::v3::Location { + let foreign_asset_id_location = xcm::v4::Location { parents: 1, interior: [ - xcm::v3::Junction::Parachain(foreign_para_id), - xcm::v3::Junction::GeneralIndex(1234567), + xcm::v4::Junction::Parachain(foreign_para_id), + xcm::v4::Junction::GeneralIndex(1234567), ] .into(), }; @@ -438,14 +438,14 @@ pub fn teleports_for_foreign_assets_works< ); assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &target_account ), 0.into() ); assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &CheckingAccount::get() ), 0.into() @@ -454,14 +454,14 @@ pub fn teleports_for_foreign_assets_works< assert_total::< pallet_assets::Pallet, AccountIdOf, - >(foreign_asset_id_location, 0, 0); + >(foreign_asset_id_location.clone(), 0, 0); // create foreign asset (0 total issuance) let asset_minimum_asset_balance = 3333333_u128; assert_ok!( >::force_create( RuntimeHelper::::root_origin(), - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), asset_owner.into(), false, asset_minimum_asset_balance.into() @@ -470,12 +470,9 @@ pub fn teleports_for_foreign_assets_works< assert_total::< pallet_assets::Pallet, AccountIdOf, - >(foreign_asset_id_location, 0, 0); + >(foreign_asset_id_location.clone(), 0, 0); assert!(teleported_foreign_asset_amount > asset_minimum_asset_balance); - let foreign_asset_id_location_latest: Location = - foreign_asset_id_location.try_into().unwrap(); - // 1. process received teleported assets from sibling parachain (foreign_para_id) let xcm = Xcm(vec![ // BuyExecution with relaychain native token @@ -489,12 +486,12 @@ pub fn teleports_for_foreign_assets_works< }, // Process teleported asset ReceiveTeleportedAsset(Assets::from(vec![Asset { - id: AssetId(foreign_asset_id_location_latest.clone()), + id: AssetId(foreign_asset_id_location.clone()), fun: Fungible(teleported_foreign_asset_amount), }])), DepositAsset { assets: Wild(AllOf { - id: AssetId(foreign_asset_id_location_latest.clone()), + id: AssetId(foreign_asset_id_location.clone()), fun: WildFungibility::Fungible, }), beneficiary: Location { @@ -526,7 +523,7 @@ pub fn teleports_for_foreign_assets_works< ); assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &target_account ), teleported_foreign_asset_amount.into() @@ -538,7 +535,7 @@ pub fn teleports_for_foreign_assets_works< ); assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &CheckingAccount::get() ), 0.into() @@ -548,7 +545,7 @@ pub fn teleports_for_foreign_assets_works< pallet_assets::Pallet, AccountIdOf, >( - foreign_asset_id_location, + foreign_asset_id_location.clone(), teleported_foreign_asset_amount, teleported_foreign_asset_amount, ); @@ -566,7 +563,7 @@ pub fn teleports_for_foreign_assets_works< let target_account_balance_before_teleport = >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &target_account, ); let asset_to_teleport_away = asset_minimum_asset_balance * 3; @@ -580,7 +577,7 @@ pub fn teleports_for_foreign_assets_works< // Make sure the target account has enough native asset to pay for delivery fees let delivery_fees = xcm_helpers::teleport_assets_delivery_fees::( - (foreign_asset_id_location_latest.clone(), asset_to_teleport_away).into(), + (foreign_asset_id_location.clone(), asset_to_teleport_away).into(), 0, Unlimited, dest_beneficiary.clone(), @@ -596,7 +593,7 @@ pub fn teleports_for_foreign_assets_works< RuntimeHelper::::origin_of(target_account.clone()), dest, dest_beneficiary, - (foreign_asset_id_location_latest.clone(), asset_to_teleport_away), + (foreign_asset_id_location.clone(), asset_to_teleport_away), Some((runtime_para_id, foreign_para_id)), included_head, &alice, @@ -606,14 +603,14 @@ pub fn teleports_for_foreign_assets_works< // check balances assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &target_account ), (target_account_balance_before_teleport - asset_to_teleport_away.into()) ); assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &CheckingAccount::get() ), 0.into() @@ -623,7 +620,7 @@ pub fn teleports_for_foreign_assets_works< pallet_assets::Pallet, AccountIdOf, >( - foreign_asset_id_location, + foreign_asset_id_location.clone(), teleported_foreign_asset_amount - asset_to_teleport_away, teleported_foreign_asset_amount - asset_to_teleport_away, ); @@ -1559,9 +1556,6 @@ pub fn reserve_transfer_native_asset_to_non_teleport_para_works< ) .unwrap(); - let v4_xcm: Xcm<()> = xcm_sent.clone().try_into().unwrap(); - dbg!(&v4_xcm); - let delivery_fees = get_fungible_delivery_fees::< ::XcmSender, >(dest.clone(), Xcm::try_from(xcm_sent.clone()).unwrap()); diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs index 0b2364dbb8bd..e0b3f70c7546 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs @@ -331,7 +331,7 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< block_author_account: AccountIdOf, (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): ( AccountIdOf, - xcm::v3::Location, + xcm::v4::Location, u128, ), foreign_asset_id_amount_to_transfer: u128, @@ -357,9 +357,9 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< BalanceOf: From + Into, XcmConfig: xcm_executor::Config, >::AssetId: - From + Into, + From + Into, >::AssetIdParameter: - From + Into, + From + Into, >::Balance: From + Into + From, ::AccountId: Into<<::RuntimeOrigin as OriginTrait>::AccountId> @@ -390,7 +390,7 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< assert_ok!( >::force_create( RuntimeHelper::::root_origin(), - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), foreign_asset_owner.into(), true, // is_sufficient=true foreign_asset_id_minimum_balance.into() @@ -409,7 +409,7 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< // ForeignAssets balances before assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &target_account ), 0.into() @@ -418,11 +418,8 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< // additional check before additional_checks_before(); - let foreign_asset_id_location_latest: Location = - foreign_asset_id_location.try_into().unwrap(); - let expected_assets = Assets::from(vec![Asset { - id: AssetId(foreign_asset_id_location_latest.clone()), + id: AssetId(foreign_asset_id_location.clone()), fun: Fungible(foreign_asset_id_amount_to_transfer), }]); let expected_beneficiary = Location::new( @@ -439,7 +436,7 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< ClearOrigin, BuyExecution { fees: Asset { - id: AssetId(foreign_asset_id_location_latest.clone()), + id: AssetId(foreign_asset_id_location.clone()), fun: Fungible(foreign_asset_id_amount_to_transfer), }, weight_limit: Unlimited, diff --git a/prdoc/pr_4129.prdoc b/prdoc/pr_4129.prdoc new file mode 100644 index 000000000000..dfcc9b9ef030 --- /dev/null +++ b/prdoc/pr_4129.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Update ForeignAssets from xcm::v3::Location to xcm::v4::Location + +doc: + - audience: + - Runtime Dev + - Runtime User + description: | + As a stepping stone for XCMv5, the foreign asset ids have been updated from v3::Location to v4::Location. + +crates: + - name: asset-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor diff --git a/substrate/frame/assets/Cargo.toml b/substrate/frame/assets/Cargo.toml index fcb151298f09..e20b576d0836 100644 --- a/substrate/frame/assets/Cargo.toml +++ b/substrate/frame/assets/Cargo.toml @@ -30,7 +30,6 @@ frame-benchmarking = { optional = true, workspace = true } sp-core = { workspace = true } [dev-dependencies] -sp-std = { workspace = true, default-features = true } sp-io = { workspace = true, default-features = true } pallet-balances = { workspace = true, default-features = true } From d944ac2f25d267b443631f891aa68a834dc24af0 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Wed, 14 Aug 2024 10:46:03 +0200 Subject: [PATCH 106/382] Stop running the wishlist workflow on forks (#5297) Addresses https://github.com/paritytech/polkadot-sdk/pull/5085#issuecomment-2277231072 --- .github/workflows/misc-update-wishlist-leaderboard.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/misc-update-wishlist-leaderboard.yml b/.github/workflows/misc-update-wishlist-leaderboard.yml index 68625e5433ca..326168717674 100644 --- a/.github/workflows/misc-update-wishlist-leaderboard.yml +++ b/.github/workflows/misc-update-wishlist-leaderboard.yml @@ -11,6 +11,7 @@ permissions: jobs: update-wishlist-leaderboard: + if: github.repository == 'paritytech/polkadot-sdk' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From e4f8a6de49b37af3c58d9414ba81f7002c911551 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Wed, 14 Aug 2024 13:58:07 +0300 Subject: [PATCH 107/382] [tests] dedup test code, add more tests, improve naming and docs (#5338) This is mostly tests cleanup: - uses helper macro for generating teleport tests, - adds missing treasury tests, - improves naming and docs for transfer tests. - [x] does not need a PRDOC --------- Co-authored-by: command-bot <> --- Cargo.lock | 2 + .../people/people-rococo/src/genesis.rs | 7 +- .../people/people-westend/src/genesis.rs | 7 +- .../emulated/common/src/macros.rs | 5 +- .../emulated/common/src/xcm_helpers.rs | 7 +- .../tests/assets/asset-hub-rococo/src/lib.rs | 2 +- .../src/tests/reserve_transfer.rs | 37 +- .../assets/asset-hub-rococo/src/tests/send.rs | 30 +- .../asset-hub-rococo/src/tests/teleport.rs | 186 +----- .../tests/assets/asset-hub-westend/src/lib.rs | 2 +- .../src/tests/reserve_transfer.rs | 37 +- .../asset-hub-westend/src/tests/send.rs | 30 +- .../asset-hub-westend/src/tests/teleport.rs | 186 +----- .../bridges/bridge-hub-rococo/src/lib.rs | 12 +- .../bridge-hub-rococo/src/tests/snowbridge.rs | 1 - .../bridge-hub-rococo/src/tests/teleport.rs | 20 + .../bridges/bridge-hub-westend/src/lib.rs | 10 +- .../src/tests/asset_transfers.rs | 2 +- .../bridge-hub-westend/src/tests/teleport.rs | 20 + .../collectives-westend/Cargo.toml | 1 + .../src/tests/fellowship.rs | 72 +++ .../src/tests/fellowship_salary.rs | 66 +++ .../collectives-westend/src/tests/mod.rs | 2 + .../tests/people/people-rococo/src/lib.rs | 21 +- .../people/people-rococo/src/tests/mod.rs | 1 - .../people-rococo/src/tests/reap_identity.rs | 545 ----------------- .../people-rococo/src/tests/teleport.rs | 162 +----- .../tests/people/people-westend/Cargo.toml | 1 + .../tests/people/people-westend/src/lib.rs | 20 +- .../people/people-westend/src/tests/mod.rs | 1 - .../people-westend/src/tests/reap_identity.rs | 547 ------------------ .../people-westend/src/tests/teleport.rs | 162 +----- .../collectives-westend/src/xcm_config.rs | 10 + 33 files changed, 376 insertions(+), 1838 deletions(-) create mode 100644 cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship.rs create mode 100644 cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship_salary.rs delete mode 100644 cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/reap_identity.rs delete mode 100644 cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/reap_identity.rs diff --git a/Cargo.lock b/Cargo.lock index 2593452fb869..67cfbb5968d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2964,6 +2964,7 @@ dependencies = [ "pallet-message-queue", "pallet-treasury", "pallet-utility", + "pallet-whitelist", "pallet-xcm", "parachains-common", "parity-scale-codec", @@ -12686,6 +12687,7 @@ dependencies = [ "pallet-balances", "pallet-identity", "pallet-message-queue", + "pallet-xcm", "parachains-common", "parity-scale-codec", "polkadot-runtime-common", diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/src/genesis.rs index 43d182facdd5..36a701d24c27 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/src/genesis.rs @@ -18,7 +18,9 @@ use sp_core::storage::Storage; // Cumulus use cumulus_primitives_core::ParaId; -use emulated_integration_tests_common::{build_genesis_storage, collators, SAFE_XCM_VERSION}; +use emulated_integration_tests_common::{ + accounts, build_genesis_storage, collators, SAFE_XCM_VERSION, +}; use parachains_common::Balance; pub const PARA_ID: u32 = 1004; @@ -27,6 +29,9 @@ pub const ED: Balance = testnet_parachains_constants::rococo::currency::EXISTENT pub fn genesis() -> Storage { let genesis_config = people_rococo_runtime::RuntimeGenesisConfig { system: people_rococo_runtime::SystemConfig::default(), + balances: people_rococo_runtime::BalancesConfig { + balances: accounts::init_balances().iter().cloned().map(|k| (k, ED * 4096)).collect(), + }, parachain_info: people_rococo_runtime::ParachainInfoConfig { parachain_id: ParaId::from(PARA_ID), ..Default::default() diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/src/genesis.rs index 0b99f19bc130..942ec1b31d2b 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/src/genesis.rs @@ -18,7 +18,9 @@ use sp_core::storage::Storage; // Cumulus use cumulus_primitives_core::ParaId; -use emulated_integration_tests_common::{build_genesis_storage, collators, SAFE_XCM_VERSION}; +use emulated_integration_tests_common::{ + accounts, build_genesis_storage, collators, SAFE_XCM_VERSION, +}; use parachains_common::Balance; pub const PARA_ID: u32 = 1004; @@ -27,6 +29,9 @@ pub const ED: Balance = testnet_parachains_constants::westend::currency::EXISTEN pub fn genesis() -> Storage { let genesis_config = people_westend_runtime::RuntimeGenesisConfig { system: people_westend_runtime::SystemConfig::default(), + balances: people_westend_runtime::BalancesConfig { + balances: accounts::init_balances().iter().cloned().map(|k| (k, ED * 4096)).collect(), + }, parachain_info: people_westend_runtime::ParachainInfoConfig { parachain_id: ParaId::from(PARA_ID), ..Default::default() diff --git a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs index b11adacbde5c..578bca84ce5a 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs @@ -228,7 +228,7 @@ macro_rules! test_parachain_is_trusted_teleporter_for_relay { $crate::macros::paste::paste! { // init Origin variables let sender = [<$sender_para Sender>]::get(); - let mut para_sender_balance_before = + let para_sender_balance_before = <$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free; let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone()); let assets: Assets = (Parent, $amount).into(); @@ -302,9 +302,6 @@ macro_rules! test_parachain_is_trusted_teleporter_for_relay { assert_eq!(para_sender_balance_before - $amount - delivery_fees, para_sender_balance_after); assert!(relay_receiver_balance_after > relay_receiver_balance_before); - - // Update sender balance - para_sender_balance_before = <$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free; } }; } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs b/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs index 76179c6d82c6..7a289a3f1ac6 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs @@ -23,16 +23,15 @@ use xcm::{prelude::*, DoubleEncoded}; pub fn xcm_transact_paid_execution( call: DoubleEncoded<()>, origin_kind: OriginKind, - native_asset: Asset, + fees: Asset, beneficiary: AccountId, ) -> VersionedXcm<()> { let weight_limit = WeightLimit::Unlimited; let require_weight_at_most = Weight::from_parts(1000000000, 200000); - let native_assets: Assets = native_asset.clone().into(); VersionedXcm::from(Xcm(vec![ - WithdrawAsset(native_assets), - BuyExecution { fees: native_asset, weight_limit }, + WithdrawAsset(fees.clone().into()), + BuyExecution { fees, weight_limit }, Transact { require_weight_at_most, origin_kind, call }, RefundSurplus, DepositAsset { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs index 1a30fac6ba91..87a090bf1ae6 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs @@ -37,6 +37,7 @@ mod imports { pub use emulated_integration_tests_common::{ accounts::DUMMY_EMPTY, get_account_id_from_seed, test_parachain_is_trusted_teleporter, + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, TestContext, TestExt, @@ -91,7 +92,6 @@ mod imports { pub const ASSET_ID: u32 = 3; pub const ASSET_MIN_BALANCE: u128 = 1000; - pub type RelayToSystemParaTest = Test; pub type RelayToParaTest = Test; pub type ParaToRelayTest = Test; pub type SystemParaToRelayTest = Test; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs index 329dbcac4b42..70dde03d75a2 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs @@ -493,9 +493,9 @@ fn para_to_para_through_relay_limited_reserve_transfer_assets( ) } -/// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work +/// Reserve Transfers of native asset from Relay Chain to the Asset Hub shouldn't work #[test] -fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { +fn reserve_transfer_native_asset_from_relay_to_asset_hub_fails() { // Init values for Relay Chain let signed_origin = ::RuntimeOrigin::signed(RococoSender::get().into()); let destination = Rococo::child_location_of(AssetHubRococo::para_id()); @@ -526,10 +526,10 @@ fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { }); } -/// Reserve Transfers of native asset from System Parachain to Relay Chain shouldn't work +/// Reserve Transfers of native asset from Asset Hub to Relay Chain shouldn't work #[test] -fn reserve_transfer_native_asset_from_system_para_to_relay_fails() { - // Init values for System Parachain +fn reserve_transfer_native_asset_from_asset_hub_to_relay_fails() { + // Init values for Asset Hub let signed_origin = ::RuntimeOrigin::signed(AssetHubRococoSender::get().into()); let destination = AssetHubRococo::parent_location(); @@ -691,10 +691,10 @@ fn reserve_transfer_native_asset_from_para_to_relay() { // ========================================================================= // ======= Reserve Transfers - Native Asset - AssetHub<>Parachain ========== // ========================================================================= -/// Reserve Transfers of native asset from System Parachain to Parachain should work +/// Reserve Transfers of native asset from Asset Hub to Parachain should work #[test] -fn reserve_transfer_native_asset_from_system_para_to_para() { - // Init values for System Parachain +fn reserve_transfer_native_asset_from_asset_hub_to_para() { + // Init values for Asset Hub let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sender = AssetHubRococoSender::get(); let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000; @@ -749,9 +749,9 @@ fn reserve_transfer_native_asset_from_system_para_to_para() { assert!(receiver_assets_after < receiver_assets_before + amount_to_send); } -/// Reserve Transfers of native asset from Parachain to System Parachain should work +/// Reserve Transfers of native asset from Parachain to Asset Hub should work #[test] -fn reserve_transfer_native_asset_from_para_to_system_para() { +fn reserve_transfer_native_asset_from_para_to_asset_hub() { // Init values for Parachain let destination = PenpalA::sibling_location_of(AssetHubRococo::para_id()); let sender = PenpalASender::get(); @@ -768,12 +768,12 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { amount_to_send * 2, ); - // Init values for System Parachain + // Init values for Asset Hub let receiver = AssetHubRococoReceiver::get(); let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - // fund Parachain's SA on System Parachain with the native tokens held in reserve + // fund Parachain's SA on Asset Hub with the native tokens held in reserve AssetHubRococo::fund_accounts(vec![(sov_penpal_on_ahr.into(), amount_to_send * 2)]); // Init Test @@ -824,11 +824,11 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { // ================================================================================== // ======= Reserve Transfers - Native + Non-system Asset - AssetHub<>Parachain ====== // ================================================================================== -/// Reserve Transfers of a local asset and native asset from System Parachain to Parachain should +/// Reserve Transfers of a local asset and native asset from Asset Hub to Parachain should /// work #[test] -fn reserve_transfer_assets_from_system_para_to_para() { - // Init values for System Parachain +fn reserve_transfer_multiple_assets_from_asset_hub_to_para() { + // Init values for Asset Hub let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(destination.clone()); let sender = AssetHubRococoSender::get(); @@ -939,13 +939,12 @@ fn reserve_transfer_assets_from_system_para_to_para() { ); } -/// Reserve Transfers of a random asset and native asset from Parachain to System Para should -/// work +/// Reserve Transfers of a random asset and native asset from Parachain to Asset Hub should work /// Receiver is empty account to show deposit works as long as transfer includes enough DOT for ED. /// Once we have https://github.com/paritytech/polkadot-sdk/issues/5298, /// we should do equivalent test with USDT instead of DOT. #[test] -fn reserve_transfer_assets_from_para_to_system_para() { +fn reserve_transfer_multiple_assets_from_para_to_asset_hub() { // Init values for Parachain let destination = PenpalA::sibling_location_of(AssetHubRococo::para_id()); let sender = PenpalASender::get(); @@ -982,7 +981,7 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Beneficiary is a new (empty) account let receiver = get_account_id_from_seed::(DUMMY_EMPTY); - // Init values for System Parachain + // Init values for Asset Hub let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr); let ah_asset_owner = AssetHubRococoAssetOwner::get(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs index ab407611c736..29eaa9694643 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs @@ -18,7 +18,7 @@ use crate::imports::*; /// Relay Chain should be able to execute `Transact` instructions in System Parachain /// when `OriginKind::Superuser`. #[test] -fn send_transact_as_superuser_from_relay_to_system_para_works() { +fn send_transact_as_superuser_from_relay_to_asset_hub_works() { AssetHubRococo::force_create_asset_from_relay_as_root( ASSET_ID, ASSET_MIN_BALANCE, @@ -29,10 +29,10 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() { } /// We tests two things here: -/// - Parachain should be able to send XCM paying its fee with system asset in the System Parachain -/// - Parachain should be able to create a new Foreign Asset in the System Parachain +/// - Parachain should be able to send XCM paying its fee at Asset Hub using system asset +/// - Parachain should be able to create a new Foreign Asset at Asset Hub #[test] -fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { +fn send_xcm_from_para_to_asset_hub_paying_fee_with_system_asset() { let para_sovereign_account = AssetHubRococo::sovereign_account_id_of( AssetHubRococo::sibling_location_of(PenpalA::para_id()), ); @@ -83,12 +83,7 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - - AssetHubRococo::assert_xcmp_queue_success(Some(Weight::from_parts( - 15_594_564_000, - 562_893, - ))); - + AssetHubRococo::assert_xcmp_queue_success(None); assert_expected_events!( AssetHubRococo, vec![ @@ -112,15 +107,15 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { } /// We tests two things here: -/// - Parachain should be able to send XCM paying its fee with system assets in the System Parachain -/// - Parachain should be able to create a new Asset in the System Parachain +/// - Parachain should be able to send XCM paying its fee at Asset Hub using sufficient asset +/// - Parachain should be able to create a new Asset at Asset Hub #[test] -fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { +fn send_xcm_from_para_to_asset_hub_paying_fee_with_sufficient_asset() { let para_sovereign_account = AssetHubRococo::sovereign_account_id_of( AssetHubRococo::sibling_location_of(PenpalA::para_id()), ); - // Force create and mint assets for Parachain's sovereign account + // Force create and mint sufficient assets for Parachain's sovereign account AssetHubRococo::force_create_and_mint_asset( ASSET_ID, ASSET_MIN_BALANCE, @@ -167,12 +162,7 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - - AssetHubRococo::assert_xcmp_queue_success(Some(Weight::from_parts( - 15_594_564_000, - 562_893, - ))); - + AssetHubRococo::assert_xcmp_queue_success(None); assert_expected_events!( AssetHubRococo, vec![ diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs index 5f9131b8c123..c8da801a14bf 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs @@ -15,53 +15,6 @@ use crate::imports::*; -fn relay_origin_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - Rococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(631_531_000, 7_186))); - - assert_expected_events!( - Rococo, - vec![ - // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, - }, - // Amount to teleport is deposited in Relay's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - ] - ); -} - -fn relay_dest_assertions(t: SystemParaToRelayTest) { - type RuntimeEvent = ::RuntimeEvent; - - Rococo::assert_ump_queue_processed( - true, - Some(AssetHubRococo::para_id()), - Some(Weight::from_parts(307_225_000, 7_186)), - ); - - assert_expected_events!( - Rococo, - vec![ - // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) { Rococo::assert_ump_queue_processed( false, @@ -92,22 +45,6 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { ); } -fn para_dest_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - AssetHubRococo::assert_dmp_queue_complete(Some(Weight::from_parts(157_718_000, 3593))); - - assert_expected_events!( - AssetHubRococo, - vec![ - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; let system_para_native_asset_location = RelayLocation::get(); @@ -230,17 +167,6 @@ fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) { ); } -fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult { - ::XcmPallet::limited_teleport_assets( - t.signed_origin, - bx!(t.args.dest.into()), - bx!(t.args.beneficiary.into()), - bx!(t.args.assets.into()), - t.args.fee_asset_item, - t.args.weight_limit, - ) -} - fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult { ::PolkadotXcm::limited_teleport_assets( t.signed_origin, @@ -274,90 +200,41 @@ fn system_para_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResul ) } -/// Limited Teleport of native asset from Relay Chain to the System Parachain should work #[test] -fn limited_teleport_native_assets_from_relay_to_system_para_works() { - // Init values for Relay Chain - let amount_to_send: Balance = ROCOCO_ED * 1000; - let dest = Rococo::child_location_of(AssetHubRococo::para_id()); - let beneficiary_id = AssetHubRococoReceiver::get(); - let test_args = TestContext { - sender: RococoSender::get(), - receiver: AssetHubRococoReceiver::get(), - args: TestArgs::new_relay(dest, beneficiary_id, amount_to_send), - }; - - let mut test = RelayToSystemParaTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(relay_origin_assertions); - test.set_assertion::(para_dest_assertions); - test.set_dispatchable::(relay_limited_teleport_assets); - test.assert(); - - let delivery_fees = Rococo::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; +fn teleport_to_other_system_parachains_works() { + let amount = ASSET_HUB_ROCOCO_ED * 100; + let native_asset: Assets = (Parent, amount).into(); - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); + test_parachain_is_trusted_teleporter!( + AssetHubRococo, // Origin + AssetHubRococoXcmConfig, // XCM Configuration + vec![BridgeHubRococo], // Destinations + (native_asset, amount) + ); } -/// Limited Teleport of native asset from System Parachain to Relay Chain -/// should work when there is enough balance in Relay Chain's `CheckAccount` #[test] -fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { - // Dependency - Relay Chain's `CheckAccount` should have enough balance - limited_teleport_native_assets_from_relay_to_system_para_works(); - - // Init values for Relay Chain - let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 1000; - let destination = AssetHubRococo::parent_location(); - let beneficiary_id = RococoReceiver::get(); - let assets = (Parent, amount_to_send).into(); - - let test_args = TestContext { - sender: AssetHubRococoSender::get(), - receiver: RococoReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), - }; - - let mut test = SystemParaToRelayTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(para_origin_assertions); - test.set_assertion::(relay_dest_assertions); - test.set_dispatchable::(system_para_limited_teleport_assets); - test.assert(); +fn teleport_from_and_to_relay() { + let amount = ROCOCO_ED * 100; + let native_asset: Assets = (Here, amount).into(); - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = AssetHubRococo::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); + test_relay_is_trusted_teleporter!( + Rococo, + RococoXcmConfig, + vec![AssetHubRococo], + (native_asset, amount) + ); - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); + test_parachain_is_trusted_teleporter_for_relay!( + AssetHubRococo, + AssetHubRococoXcmConfig, + Rococo, + amount + ); } /// Limited Teleport of native asset from System Parachain to Relay Chain -/// should't work when there is not enough balance in Relay Chain's `CheckAccount` +/// shouldn't work when there is not enough balance in Relay Chain's `CheckAccount` #[test] fn limited_teleport_native_assets_from_system_para_to_relay_fails() { // Init values for Relay Chain @@ -397,19 +274,6 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { assert_eq!(receiver_balance_after, receiver_balance_before); } -#[test] -fn teleport_to_other_system_parachains_works() { - let amount = ASSET_HUB_ROCOCO_ED * 100; - let native_asset: Assets = (Parent, amount).into(); - - test_parachain_is_trusted_teleporter!( - AssetHubRococo, // Origin - AssetHubRococoXcmConfig, // XCM Configuration - vec![BridgeHubRococo], // Destinations - (native_asset, amount) - ); -} - /// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets while paying /// fees using (reserve transferred) native asset. pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt( diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs index fe484771931f..a887ee6a532a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs @@ -34,6 +34,7 @@ mod imports { pub use emulated_integration_tests_common::{ accounts::DUMMY_EMPTY, get_account_id_from_seed, test_parachain_is_trusted_teleporter, + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, TestContext, TestExt, @@ -88,7 +89,6 @@ mod imports { pub const ASSET_ID: u32 = 3; pub const ASSET_MIN_BALANCE: u128 = 1000; - pub type RelayToSystemParaTest = Test; pub type RelayToParaTest = Test; pub type ParaToRelayTest = Test; pub type SystemParaToRelayTest = Test; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs index 729de65382f8..59f63d380590 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs @@ -493,9 +493,9 @@ fn para_to_para_through_relay_limited_reserve_transfer_assets( ) } -/// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work +/// Reserve Transfers of native asset from Relay Chain to the Asset Hub shouldn't work #[test] -fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { +fn reserve_transfer_native_asset_from_relay_to_asset_hub_fails() { // Init values for Relay Chain let signed_origin = ::RuntimeOrigin::signed(WestendSender::get().into()); let destination = Westend::child_location_of(AssetHubWestend::para_id()); @@ -526,10 +526,10 @@ fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { }); } -/// Reserve Transfers of native asset from System Parachain to Relay Chain shouldn't work +/// Reserve Transfers of native asset from Asset Hub to Relay Chain shouldn't work #[test] -fn reserve_transfer_native_asset_from_system_para_to_relay_fails() { - // Init values for System Parachain +fn reserve_transfer_native_asset_from_asset_hub_to_relay_fails() { + // Init values for Asset Hub let signed_origin = ::RuntimeOrigin::signed(AssetHubWestendSender::get().into()); let destination = AssetHubWestend::parent_location(); @@ -691,10 +691,10 @@ fn reserve_transfer_native_asset_from_para_to_relay() { // ========================================================================= // ======= Reserve Transfers - Native Asset - AssetHub<>Parachain ========== // ========================================================================= -/// Reserve Transfers of native asset from System Parachain to Parachain should work +/// Reserve Transfers of native asset from Asset Hub to Parachain should work #[test] -fn reserve_transfer_native_asset_from_system_para_to_para() { - // Init values for System Parachain +fn reserve_transfer_native_asset_from_asset_hub_to_para() { + // Init values for Asset Hub let destination = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let sender = AssetHubWestendSender::get(); let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 2000; @@ -749,9 +749,9 @@ fn reserve_transfer_native_asset_from_system_para_to_para() { assert!(receiver_assets_after < receiver_assets_before + amount_to_send); } -/// Reserve Transfers of native asset from Parachain to System Parachain should work +/// Reserve Transfers of native asset from Parachain to Asset Hub should work #[test] -fn reserve_transfer_native_asset_from_para_to_system_para() { +fn reserve_transfer_native_asset_from_para_to_asset_hub() { // Init values for Parachain let destination = PenpalA::sibling_location_of(AssetHubWestend::para_id()); let sender = PenpalASender::get(); @@ -768,13 +768,13 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { amount_to_send * 2, ); - // Init values for System Parachain + // Init values for Asset Hub let receiver = AssetHubWestendReceiver::get(); let penpal_location_as_seen_by_ahr = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - // fund Parachain's SA on System Parachain with the native tokens held in reserve + // fund Parachain's SA on Asset Hub with the native tokens held in reserve AssetHubWestend::fund_accounts(vec![(sov_penpal_on_ahr.into(), amount_to_send * 2)]); // Init Test @@ -825,11 +825,11 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { // ========================================================================= // ======= Reserve Transfers - Non-system Asset - AssetHub<>Parachain ====== // ========================================================================= -/// Reserve Transfers of a local asset and native asset from System Parachain to Parachain should +/// Reserve Transfers of a local asset and native asset from Asset Hub to Parachain should /// work #[test] -fn reserve_transfer_assets_from_system_para_to_para() { - // Init values for System Parachain +fn reserve_transfer_multiple_assets_from_asset_hub_to_para() { + // Init values for Asset Hub let destination = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(destination.clone()); let sender = AssetHubWestendSender::get(); @@ -940,13 +940,12 @@ fn reserve_transfer_assets_from_system_para_to_para() { ); } -/// Reserve Transfers of a random asset and native asset from Parachain to System Para should -/// work +/// Reserve Transfers of a random asset and native asset from Parachain to Asset Hub should work /// Receiver is empty account to show deposit works as long as transfer includes enough DOT for ED. /// Once we have https://github.com/paritytech/polkadot-sdk/issues/5298, /// we should do equivalent test with USDT instead of DOT. #[test] -fn reserve_transfer_assets_from_para_to_system_para() { +fn reserve_transfer_multiple_assets_from_para_to_asset_hub() { // Init values for Parachain let destination = PenpalA::sibling_location_of(AssetHubWestend::para_id()); let sender = PenpalASender::get(); @@ -983,7 +982,7 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Beneficiary is a new (empty) account let receiver = get_account_id_from_seed::(DUMMY_EMPTY); - // Init values for System Parachain + // Init values for Asset Hub let penpal_location_as_seen_by_ahr = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(penpal_location_as_seen_by_ahr); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs index ac006653ca67..761c7c12255c 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs @@ -18,7 +18,7 @@ use crate::imports::*; /// Relay Chain should be able to execute `Transact` instructions in System Parachain /// when `OriginKind::Superuser`. #[test] -fn send_transact_as_superuser_from_relay_to_system_para_works() { +fn send_transact_as_superuser_from_relay_to_asset_hub_works() { AssetHubWestend::force_create_asset_from_relay_as_root( ASSET_ID, ASSET_MIN_BALANCE, @@ -29,10 +29,10 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() { } /// We tests two things here: -/// - Parachain should be able to send XCM paying its fee with system asset in the System Parachain -/// - Parachain should be able to create a new Foreign Asset in the System Parachain +/// - Parachain should be able to send XCM paying its fee at Asset Hub using system asset +/// - Parachain should be able to create a new Foreign Asset at Asset Hub #[test] -fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { +fn send_xcm_from_para_to_asset_hub_paying_fee_with_system_asset() { let para_sovereign_account = AssetHubWestend::sovereign_account_id_of( AssetHubWestend::sibling_location_of(PenpalA::para_id()), ); @@ -83,12 +83,7 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - - AssetHubWestend::assert_xcmp_queue_success(Some(Weight::from_parts( - 15_594_564_000, - 562_893, - ))); - + AssetHubWestend::assert_xcmp_queue_success(None); assert_expected_events!( AssetHubWestend, vec![ @@ -112,15 +107,15 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { } /// We tests two things here: -/// - Parachain should be able to send XCM paying its fee with system assets in the System Parachain -/// - Parachain should be able to create a new Asset in the System Parachain +/// - Parachain should be able to send XCM paying its fee at Asset Hub using sufficient asset +/// - Parachain should be able to create a new Asset at Asset Hub #[test] -fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { +fn send_xcm_from_para_to_asset_hub_paying_fee_with_sufficient_asset() { let para_sovereign_account = AssetHubWestend::sovereign_account_id_of( AssetHubWestend::sibling_location_of(PenpalA::para_id()), ); - // Force create and mint assets for Parachain's sovereign account + // Force create and mint sufficient assets for Parachain's sovereign account AssetHubWestend::force_create_and_mint_asset( ASSET_ID, ASSET_MIN_BALANCE, @@ -167,12 +162,7 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - - AssetHubWestend::assert_xcmp_queue_success(Some(Weight::from_parts( - 15_594_564_000, - 562_893, - ))); - + AssetHubWestend::assert_xcmp_queue_success(None); assert_expected_events!( AssetHubWestend, vec![ diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs index 02a0bc0207bb..15d39858acca 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs @@ -15,53 +15,6 @@ use crate::imports::*; -fn relay_origin_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - Westend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(631_531_000, 7_186))); - - assert_expected_events!( - Westend, - vec![ - // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, - }, - // Amount to teleport is deposited in Relay's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - ] - ); -} - -fn relay_dest_assertions(t: SystemParaToRelayTest) { - type RuntimeEvent = ::RuntimeEvent; - - Westend::assert_ump_queue_processed( - true, - Some(AssetHubWestend::para_id()), - Some(Weight::from_parts(307_225_000, 7_186)), - ); - - assert_expected_events!( - Westend, - vec![ - // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) { Westend::assert_ump_queue_processed( false, @@ -92,22 +45,6 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { ); } -fn para_dest_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - AssetHubWestend::assert_dmp_queue_complete(Some(Weight::from_parts(157_718_000, 3593))); - - assert_expected_events!( - AssetHubWestend, - vec![ - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; let system_para_native_asset_location = RelayLocation::get(); @@ -230,17 +167,6 @@ fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) { ); } -fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult { - ::XcmPallet::limited_teleport_assets( - t.signed_origin, - bx!(t.args.dest.into()), - bx!(t.args.beneficiary.into()), - bx!(t.args.assets.into()), - t.args.fee_asset_item, - t.args.weight_limit, - ) -} - fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult { ::PolkadotXcm::limited_teleport_assets( t.signed_origin, @@ -274,90 +200,41 @@ fn system_para_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResul ) } -/// Limited Teleport of native asset from Relay Chain to the System Parachain should work #[test] -fn limited_teleport_native_assets_from_relay_to_system_para_works() { - // Init values for Relay Chain - let amount_to_send: Balance = WESTEND_ED * 1000; - let dest = Westend::child_location_of(AssetHubWestend::para_id()); - let beneficiary_id = AssetHubWestendReceiver::get(); - let test_args = TestContext { - sender: WestendSender::get(), - receiver: AssetHubWestendReceiver::get(), - args: TestArgs::new_relay(dest, beneficiary_id, amount_to_send), - }; - - let mut test = RelayToSystemParaTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(relay_origin_assertions); - test.set_assertion::(para_dest_assertions); - test.set_dispatchable::(relay_limited_teleport_assets); - test.assert(); - - let delivery_fees = Westend::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; +fn teleport_to_other_system_parachains_works() { + let amount = ASSET_HUB_WESTEND_ED * 100; + let native_asset: Assets = (Parent, amount).into(); - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); + test_parachain_is_trusted_teleporter!( + AssetHubWestend, // Origin + AssetHubWestendXcmConfig, // XCM Configuration + vec![BridgeHubWestend], // Destinations + (native_asset, amount) + ); } -/// Limited Teleport of native asset from System Parachain to Relay Chain -/// should work when there is enough balance in Relay Chain's `CheckAccount` #[test] -fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { - // Dependency - Relay Chain's `CheckAccount` should have enough balance - limited_teleport_native_assets_from_relay_to_system_para_works(); - - // Init values for Relay Chain - let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000; - let destination = AssetHubWestend::parent_location(); - let beneficiary_id = WestendReceiver::get(); - let assets = (Parent, amount_to_send).into(); - - let test_args = TestContext { - sender: AssetHubWestendSender::get(), - receiver: WestendReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), - }; - - let mut test = SystemParaToRelayTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(para_origin_assertions); - test.set_assertion::(relay_dest_assertions); - test.set_dispatchable::(system_para_limited_teleport_assets); - test.assert(); +fn teleport_from_and_to_relay() { + let amount = WESTEND_ED * 100; + let native_asset: Assets = (Here, amount).into(); - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = AssetHubWestend::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); + test_relay_is_trusted_teleporter!( + Westend, + WestendXcmConfig, + vec![AssetHubWestend], + (native_asset, amount) + ); - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); + test_parachain_is_trusted_teleporter_for_relay!( + AssetHubWestend, + AssetHubWestendXcmConfig, + Westend, + amount + ); } /// Limited Teleport of native asset from System Parachain to Relay Chain -/// should't work when there is not enough balance in Relay Chain's `CheckAccount` +/// shouldn't work when there is not enough balance in Relay Chain's `CheckAccount` #[test] fn limited_teleport_native_assets_from_system_para_to_relay_fails() { // Init values for Relay Chain @@ -397,19 +274,6 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { assert_eq!(receiver_balance_after, receiver_balance_before); } -#[test] -fn teleport_to_other_system_parachains_works() { - let amount = ASSET_HUB_WESTEND_ED * 100; - let native_asset: Assets = (Parent, amount).into(); - - test_parachain_is_trusted_teleporter!( - AssetHubWestend, // Origin - AssetHubWestendXcmConfig, // XCM Configuration - vec![BridgeHubWestend], // Destinations - (native_asset, amount) - ); -} - /// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets while paying /// fees using (reserve transferred) native asset. pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt( diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs index 0aefe5b6352c..ac08e48ded68 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs @@ -32,7 +32,8 @@ mod imports { pub use emulated_integration_tests_common::{ accounts::ALICE, impls::Inspect, - test_parachain_is_trusted_teleporter, + test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay, + test_relay_is_trusted_teleporter, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, TestExt, }, @@ -60,15 +61,20 @@ mod imports { }, PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner, }, - rococo_emulated_chain::{genesis::ED as ROCOCO_ED, RococoRelayPallet as RococoPallet}, + rococo_emulated_chain::{ + genesis::ED as ROCOCO_ED, rococo_runtime::xcm_config::XcmConfig as RococoXcmConfig, + RococoRelayPallet as RococoPallet, + }, AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend, AssetHubWestendParaReceiver as AssetHubWestendReceiver, AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo, + BridgeHubRococoParaReceiver as BridgeHubRococoReceiver, BridgeHubRococoParaSender as BridgeHubRococoSender, BridgeHubWestendPara as BridgeHubWestend, PenpalAPara as PenpalA, PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender, - RococoRelay as Rococo, + RococoRelay as Rococo, RococoRelayReceiver as RococoReceiver, + RococoRelaySender as RococoSender, }; pub const ASSET_MIN_BALANCE: u128 = 1000; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 3e8d3357caa8..84328fb7c6d2 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -560,7 +560,6 @@ fn send_token_from_ethereum_to_asset_hub_with_fee(account_id: [u8; 32], fee: u12 2, [EthereumNetwork::get().into(), AccountKey20 { network: None, key: WETH }], ); - // (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }) // Fund asset hub sovereign on bridge hub let asset_hub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new( 1, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/teleport.rs index 1fb03748d926..8cdd9613dc52 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/teleport.rs @@ -27,3 +27,23 @@ fn teleport_to_other_system_parachains_works() { (native_asset, amount) ); } + +#[test] +fn teleport_from_and_to_relay() { + let amount = ROCOCO_ED * 100; + let native_asset: Assets = (Here, amount).into(); + + test_relay_is_trusted_teleporter!( + Rococo, + RococoXcmConfig, + vec![BridgeHubRococo], + (native_asset, amount) + ); + + test_parachain_is_trusted_teleporter_for_relay!( + BridgeHubRococo, + BridgeHubRococoXcmConfig, + Rococo, + amount + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 3262cc17ba56..5e0462d14882 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -31,7 +31,8 @@ mod imports { pub use emulated_integration_tests_common::{ accounts::ALICE, impls::Inspect, - test_parachain_is_trusted_teleporter, + test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay, + test_relay_is_trusted_teleporter, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, TestExt, }, @@ -54,14 +55,19 @@ mod imports { penpal_runtime::xcm_config::UniversalLocation as PenpalUniversalLocation, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, }, - westend_emulated_chain::WestendRelayPallet as WestendPallet, + westend_emulated_chain::{ + genesis::ED as WESTEND_ED, westend_runtime::xcm_config::XcmConfig as WestendXcmConfig, + WestendRelayPallet as WestendPallet, + }, AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend, AssetHubWestendParaReceiver as AssetHubWestendReceiver, AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo, BridgeHubWestendPara as BridgeHubWestend, + BridgeHubWestendParaReceiver as BridgeHubWestendReceiver, BridgeHubWestendParaSender as BridgeHubWestendSender, PenpalBPara as PenpalB, PenpalBParaSender as PenpalBSender, WestendRelay as Westend, + WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, }; pub const ASSET_MIN_BALANCE: u128 = 1000; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs index e2c496802e2c..fc8b772a9c7e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs @@ -452,7 +452,7 @@ fn send_back_rocs_from_penpal_westend_through_asset_hub_westend_to_asset_hub_roc // fund the AHW's SA on AHR with the ROC tokens held in reserve let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( - NetworkId::Westend, + Westend, AssetHubWestend::para_id(), ); AssetHubRococo::fund_accounts(vec![(sov_ahw_on_ahr.clone(), amount * 2)]); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/teleport.rs index 64378a844f52..a5add3b82957 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/teleport.rs @@ -27,3 +27,23 @@ fn teleport_to_other_system_parachains_works() { (native_asset, amount) ); } + +#[test] +fn teleport_from_and_to_relay() { + let amount = WESTEND_ED * 100; + let native_asset: Assets = (Here, amount).into(); + + test_relay_is_trusted_teleporter!( + Westend, + WestendXcmConfig, + vec![BridgeHubWestend], + (native_asset, amount) + ); + + test_parachain_is_trusted_teleporter_for_relay!( + BridgeHubWestend, + BridgeHubWestendXcmConfig, + Westend, + amount + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/Cargo.toml index 3012e2b19f53..c4d281b75a77 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/Cargo.toml @@ -23,6 +23,7 @@ pallet-assets = { workspace = true } pallet-treasury = { workspace = true } pallet-message-queue = { workspace = true } pallet-utility = { workspace = true } +pallet-whitelist = { workspace = true } # Polkadot polkadot-runtime-common = { workspace = true, default-features = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship.rs b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship.rs new file mode 100644 index 000000000000..f97599bda7f0 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship.rs @@ -0,0 +1,72 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; +use codec::Encode; +use collectives_fellowship::pallet_fellowship_origins::Origin::Fellows as FellowsOrigin; +use frame_support::{assert_ok, sp_runtime::traits::Dispatchable}; + +#[test] +fn fellows_whitelist_call() { + CollectivesWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeCall = ::RuntimeCall; + type RuntimeOrigin = ::RuntimeOrigin; + type Runtime = ::Runtime; + type WestendCall = ::RuntimeCall; + type WestendRuntime = ::Runtime; + + let call_hash = [1u8; 32].into(); + + let whitelist_call = RuntimeCall::PolkadotXcm(pallet_xcm::Call::::send { + dest: bx!(VersionedLocation::from(Location::parent())), + message: bx!(VersionedXcm::from(Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: Weight::from_parts(5_000_000_000, 500_000), + call: WestendCall::Whitelist( + pallet_whitelist::Call::::whitelist_call { call_hash } + ) + .encode() + .into(), + } + ]))), + }); + + let fellows_origin: RuntimeOrigin = FellowsOrigin.into(); + + assert_ok!(whitelist_call.dispatch(fellows_origin)); + + assert_expected_events!( + CollectivesWestend, + vec![ + RuntimeEvent::PolkadotXcm(pallet_xcm::Event::Sent { .. }) => {}, + ] + ); + }); + + Westend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + Westend, + vec![ + RuntimeEvent::Whitelist(pallet_whitelist::Event::CallWhitelisted { .. }) => {}, + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success: true, .. }) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship_salary.rs b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship_salary.rs new file mode 100644 index 000000000000..840d2da49463 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship_salary.rs @@ -0,0 +1,66 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; +use collectives_fellowship::FellowshipSalaryPaymaster; +use frame_support::{ + assert_ok, + traits::{fungibles::Mutate, tokens::Pay}, +}; +use xcm_executor::traits::ConvertLocation; + +const FELLOWSHIP_SALARY_PALLET_ID: u8 = 64; + +#[test] +fn pay_salary() { + let asset_id: u32 = 1984; + let fellowship_salary = ( + Parent, + Parachain(CollectivesWestend::para_id().into()), + PalletInstance(FELLOWSHIP_SALARY_PALLET_ID), + ); + let pay_from = + AssetHubLocationToAccountId::convert_location(&fellowship_salary.into()).unwrap(); + let pay_to = Westend::account_id_of(ALICE); + let pay_amount = 9_000_000_000; + + AssetHubWestend::execute_with(|| { + type AssetHubAssets = ::Assets; + assert_ok!(>::mint_into(asset_id, &pay_from, pay_amount * 2)); + }); + + CollectivesWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(FellowshipSalaryPaymaster::pay(&pay_to, (), pay_amount)); + assert_expected_events!( + CollectivesWestend, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::Assets(pallet_assets::Event::Transferred { .. }) => {}, + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success: true ,.. }) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/mod.rs index 40e98a8b6869..ef4e4885183d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/mod.rs @@ -13,5 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod fellowship; +mod fellowship_salary; mod fellowship_treasury; mod teleport; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs index b725c24fbea3..06b0b6ba6005 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs @@ -15,14 +15,8 @@ #[cfg(test)] mod imports { - pub use codec::Encode; - // Substrate - pub use frame_support::{ - assert_ok, - sp_runtime::{AccountId32, DispatchResult}, - traits::fungibles::Inspect, - }; + pub use frame_support::{assert_ok, sp_runtime::DispatchResult, traits::fungibles::Inspect}; // Polkadot pub use xcm::prelude::*; @@ -36,20 +30,14 @@ mod imports { pub use parachains_common::Balance; pub use rococo_system_emulated_network::{ people_rococo_emulated_chain::{ - genesis::ED as PEOPLE_ROCOCO_ED, people_rococo_runtime::{ - people, xcm_config::XcmConfig as PeopleRococoXcmConfig, - ExistentialDeposit as PeopleRococoExistentialDeposit, Runtime as PeopleRuntime, + xcm_config::XcmConfig as PeopleRococoXcmConfig, + ExistentialDeposit as PeopleRococoExistentialDeposit, }, PeopleRococoParaPallet as PeopleRococoPallet, }, rococo_emulated_chain::{ - genesis::ED as ROCOCO_ED, - rococo_runtime::{ - xcm_config::XcmConfig as RococoXcmConfig, BasicDeposit, ByteDeposit, - MaxAdditionalFields, MaxSubAccounts, Runtime as RococoRuntime, - RuntimeOrigin as RococoOrigin, SubAccountDeposit, - }, + genesis::ED as ROCOCO_ED, rococo_runtime::xcm_config::XcmConfig as RococoXcmConfig, RococoRelayPallet as RococoPallet, }, PeopleRococoPara as PeopleRococo, PeopleRococoParaReceiver as PeopleRococoReceiver, @@ -57,7 +45,6 @@ mod imports { RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender, }; - pub type RelayToSystemParaTest = Test; pub type SystemParaToRelayTest = Test; } diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/mod.rs index 3f18621224ac..08749b295dc2 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/mod.rs @@ -14,5 +14,4 @@ // limitations under the License. mod claim_assets; -mod reap_identity; mod teleport; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/reap_identity.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/reap_identity.rs deleted file mode 100644 index 10f0c61ed63c..000000000000 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/reap_identity.rs +++ /dev/null @@ -1,545 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # OnReapIdentity Tests -//! -//! This file contains the test cases for migrating Identity data away from the Rococo Relay -//! chain and to the PeopleRococo parachain. This migration is part of the broader Minimal Relay -//! effort: -//! https://github.com/polkadot-fellows/RFCs/blob/main/text/0032-minimal-relay.md -//! -//! ## Overview -//! -//! The tests validate the robustness and correctness of the `OnReapIdentityHandler` -//! ensuring that it behaves as expected in various scenarios. Key aspects tested include: -//! -//! - **Deposit Handling**: Confirming that deposits are correctly migrated from the Relay Chain to -//! the People parachain in various scenarios (different `IdentityInfo` fields and different -//! numbers of sub-accounts). -//! -//! ### Test Scenarios -//! -//! The tests are categorized into several scenarios, each resulting in different deposits required -//! on the destination parachain. The tests ensure: -//! -//! - Reserved deposits on the Relay Chain are fully released; -//! - The freed deposit from the Relay Chain is sufficient for the parachain deposit; and -//! - The account will exist on the parachain. - -use crate::imports::*; -use frame_support::BoundedVec; -use pallet_balances::Event as BalancesEvent; -use pallet_identity::{legacy::IdentityInfo, Data, Event as IdentityEvent, IdentityOf, SubsOf}; -use people::{ - BasicDeposit as BasicDepositParachain, ByteDeposit as ByteDepositParachain, - IdentityInfo as IdentityInfoParachain, SubAccountDeposit as SubAccountDepositParachain, -}; -use rococo_runtime_constants::currency::*; -use rococo_system_emulated_network::{ - rococo_emulated_chain::RococoRelayPallet, RococoRelay, RococoRelaySender, -}; - -type Balance = u128; -type RococoIdentity = ::Identity; -type RococoBalances = ::Balances; -type RococoIdentityMigrator = ::IdentityMigrator; -type PeopleRococoIdentity = ::Identity; -type PeopleRococoBalances = ::Balances; - -#[derive(Clone, Debug)] -struct Identity { - relay: IdentityInfo, - para: IdentityInfoParachain, - subs: Subs, -} - -impl Identity { - fn new( - full: bool, - additional: Option>, - subs: Subs, - ) -> Self { - let pgp_fingerprint = [ - 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, - 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, - ]; - let make_data = |data: &[u8], full: bool| -> Data { - if full { - Data::Raw(data.to_vec().try_into().unwrap()) - } else { - Data::None - } - }; - let (github, discord) = additional - .as_ref() - .and_then(|vec| vec.first()) - .map(|(g, d)| (g.clone(), d.clone())) - .unwrap_or((Data::None, Data::None)); - Self { - relay: IdentityInfo { - display: make_data(b"xcm-test", full), - legal: make_data(b"The Xcm Test, Esq.", full), - web: make_data(b"https://visitme/", full), - riot: make_data(b"xcm-riot", full), - email: make_data(b"xcm-test@gmail.com", full), - pgp_fingerprint: Some(pgp_fingerprint), - image: make_data(b"xcm-test.png", full), - twitter: make_data(b"@xcm-test", full), - additional: additional.unwrap_or_default(), - }, - para: IdentityInfoParachain { - display: make_data(b"xcm-test", full), - legal: make_data(b"The Xcm Test, Esq.", full), - web: make_data(b"https://visitme/", full), - matrix: make_data(b"xcm-matrix@server", full), - email: make_data(b"xcm-test@gmail.com", full), - pgp_fingerprint: Some(pgp_fingerprint), - image: make_data(b"xcm-test.png", full), - twitter: make_data(b"@xcm-test", full), - github, - discord, - }, - subs, - } - } -} - -#[derive(Clone, Debug)] -enum Subs { - Zero, - Many(u32), -} - -enum IdentityOn<'a> { - Relay(&'a IdentityInfo), - Para(&'a IdentityInfoParachain), -} - -impl IdentityOn<'_> { - fn calculate_deposit(self) -> Balance { - match self { - IdentityOn::Relay(id) => { - let base_deposit = BasicDeposit::get(); - let byte_deposit = - ByteDeposit::get() * TryInto::::try_into(id.encoded_size()).unwrap(); - base_deposit + byte_deposit - }, - IdentityOn::Para(id) => { - let base_deposit = BasicDepositParachain::get(); - let byte_deposit = ByteDepositParachain::get() * - TryInto::::try_into(id.encoded_size()).unwrap(); - base_deposit + byte_deposit - }, - } - } -} - -/// Generate an `AccountId32` from a `u32`. -/// This creates a 32-byte array, initially filled with `255`, and then repeatedly fills it -/// with the 4-byte little-endian representation of the `u32` value, until the array is full. -/// -/// **Example**: -/// -/// `account_from_u32(5)` will return an `AccountId32` with the bytes -/// `[0, 5, 0, 0, 0, 0, 0, 0, 0, 5 ... ]` -fn account_from_u32(id: u32) -> AccountId32 { - let mut buffer = [255u8; 32]; - let id_bytes = id.to_le_bytes(); - let id_size = id_bytes.len(); - for chunk in buffer.chunks_mut(id_size) { - chunk.clone_from_slice(&id_bytes); - } - AccountId32::new(buffer) -} - -// Set up the Relay Chain with an identity. -fn set_id_relay(id: &Identity) -> Balance { - let mut total_deposit: Balance = 0; - - // Set identity and Subs on Relay Chain - RococoRelay::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - assert_ok!(RococoIdentity::set_identity( - RococoOrigin::signed(RococoRelaySender::get()), - Box::new(id.relay.clone()) - )); - - if let Subs::Many(n) = id.subs { - let subs: Vec<_> = (0..n) - .map(|i| (account_from_u32(i), Data::Raw(b"name".to_vec().try_into().unwrap()))) - .collect(); - - assert_ok!(RococoIdentity::set_subs( - RococoOrigin::signed(RococoRelaySender::get()), - subs, - )); - } - - let reserved_balance = RococoBalances::reserved_balance(RococoRelaySender::get()); - let id_deposit = IdentityOn::Relay(&id.relay).calculate_deposit(); - - let total_deposit = match id.subs { - Subs::Zero => { - total_deposit = id_deposit; // No subs - assert_expected_events!( - RococoRelay, - vec![ - RuntimeEvent::Identity(IdentityEvent::IdentitySet { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == RococoRelaySender::get(), - amount: *amount == id_deposit, - }, - ] - ); - total_deposit - }, - Subs::Many(n) => { - let sub_account_deposit = n as Balance * SubAccountDeposit::get(); - total_deposit = - sub_account_deposit + IdentityOn::Relay(&id.relay).calculate_deposit(); - assert_expected_events!( - RococoRelay, - vec![ - RuntimeEvent::Identity(IdentityEvent::IdentitySet { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == RococoRelaySender::get(), - amount: *amount == id_deposit, - }, - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == RococoRelaySender::get(), - amount: *amount == sub_account_deposit, - }, - ] - ); - total_deposit - }, - }; - - assert_eq!(reserved_balance, total_deposit); - }); - total_deposit -} - -// Set up the parachain with an identity and (maybe) sub accounts, but with zero deposits. -fn assert_set_id_parachain(id: &Identity) { - // Set identity and Subs on Parachain with zero deposit - PeopleRococo::execute_with(|| { - let free_bal = PeopleRococoBalances::free_balance(PeopleRococoSender::get()); - let reserved_balance = PeopleRococoBalances::reserved_balance(PeopleRococoSender::get()); - - // total balance at Genesis should be zero - assert_eq!(reserved_balance + free_bal, 0); - - assert_ok!(PeopleRococoIdentity::set_identity_no_deposit( - &PeopleRococoSender::get(), - id.para.clone(), - )); - - match id.subs { - Subs::Zero => {}, - Subs::Many(n) => { - let subs: Vec<_> = (0..n) - .map(|ii| { - (account_from_u32(ii), Data::Raw(b"name".to_vec().try_into().unwrap())) - }) - .collect(); - assert_ok!(PeopleRococoIdentity::set_subs_no_deposit( - &PeopleRococoSender::get(), - subs, - )); - }, - } - - // No amount should be reserved as deposit amounts are set to 0. - let reserved_balance = PeopleRococoBalances::reserved_balance(PeopleRococoSender::get()); - assert_eq!(reserved_balance, 0); - assert!(IdentityOf::::get(PeopleRococoSender::get()).is_some()); - - let (_, sub_accounts) = SubsOf::::get(PeopleRococoSender::get()); - - match id.subs { - Subs::Zero => assert_eq!(sub_accounts.len(), 0), - Subs::Many(n) => assert_eq!(sub_accounts.len(), n as usize), - } - }); -} - -// Reap the identity on the Relay Chain and assert that the correct things happen there. -fn assert_reap_id_relay(total_deposit: Balance, id: &Identity) { - RococoRelay::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - let free_bal_before_reap = RococoBalances::free_balance(RococoRelaySender::get()); - let reserved_balance = RococoBalances::reserved_balance(RococoRelaySender::get()); - - assert_eq!(reserved_balance, total_deposit); - - assert_ok!(RococoIdentityMigrator::reap_identity( - RococoOrigin::signed(RococoRelaySender::get()), - RococoRelaySender::get() - )); - - let remote_deposit = match id.subs { - Subs::Zero => calculate_remote_deposit(id.relay.encoded_size() as u32, 0), - Subs::Many(n) => calculate_remote_deposit(id.relay.encoded_size() as u32, n), - }; - - assert_expected_events!( - RococoRelay, - vec![ - // `reap_identity` sums the identity and subs deposits and unreserves them in one - // call. Therefore, we only expect one `Unreserved` event. - RuntimeEvent::Balances(BalancesEvent::Unreserved { who, amount }) => { - who: *who == RococoRelaySender::get(), - amount: *amount == total_deposit, - }, - RuntimeEvent::IdentityMigrator( - polkadot_runtime_common::identity_migrator::Event::IdentityReaped { - who, - }) => { - who: *who == PeopleRococoSender::get(), - }, - ] - ); - // Identity should be gone. - assert!(IdentityOf::::get(RococoRelaySender::get()).is_none()); - - // Subs should be gone. - let (_, sub_accounts) = SubsOf::::get(RococoRelaySender::get()); - assert_eq!(sub_accounts.len(), 0); - - let reserved_balance = RococoBalances::reserved_balance(RococoRelaySender::get()); - assert_eq!(reserved_balance, 0); - - // Free balance should be greater (i.e. the teleport should work even if 100% of an - // account's balance is reserved for Identity). - let free_bal_after_reap = RococoBalances::free_balance(RococoRelaySender::get()); - assert!(free_bal_after_reap > free_bal_before_reap); - - // Implicit: total_deposit > remote_deposit. As in, accounts should always have enough - // reserved for the parachain deposit. - assert_eq!(free_bal_after_reap, free_bal_before_reap + total_deposit - remote_deposit); - }); -} - -// Reaping the identity on the Relay Chain will have sent an XCM program to the parachain. Ensure -// that everything happens as expected. -fn assert_reap_parachain(id: &Identity) { - PeopleRococo::execute_with(|| { - let reserved_balance = PeopleRococoBalances::reserved_balance(PeopleRococoSender::get()); - let id_deposit = IdentityOn::Para(&id.para).calculate_deposit(); - let total_deposit = match id.subs { - Subs::Zero => id_deposit, - Subs::Many(n) => id_deposit + n as Balance * SubAccountDepositParachain::get(), - }; - assert_reap_events(id_deposit, id); - assert_eq!(reserved_balance, total_deposit); - - // Should have at least one ED after in free balance after the reap. - assert!(PeopleRococoBalances::free_balance(PeopleRococoSender::get()) >= PEOPLE_ROCOCO_ED); - }); -} - -// Assert the events that should happen on the parachain upon reaping an identity on the Relay -// Chain. -fn assert_reap_events(id_deposit: Balance, id: &Identity) { - type RuntimeEvent = ::RuntimeEvent; - match id.subs { - Subs::Zero => { - assert_expected_events!( - PeopleRococo, - vec![ - // Deposit and Endowed from teleport - RuntimeEvent::Balances(BalancesEvent::Minted { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Endowed { .. }) => {}, - // Amount reserved for identity info - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == PeopleRococoSender::get(), - amount: *amount == id_deposit, - }, - // Confirmation from Migrator with individual identity and subs deposits - RuntimeEvent::IdentityMigrator( - polkadot_runtime_common::identity_migrator::Event::DepositUpdated { - who, identity, subs - }) => { - who: *who == PeopleRococoSender::get(), - identity: *identity == id_deposit, - subs: *subs == 0, - }, - RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { .. }) => {}, - ] - ); - }, - Subs::Many(n) => { - let subs_deposit = n as Balance * SubAccountDepositParachain::get(); - assert_expected_events!( - PeopleRococo, - vec![ - // Deposit and Endowed from teleport - RuntimeEvent::Balances(BalancesEvent::Minted { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Endowed { .. }) => {}, - // Amount reserved for identity info - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == PeopleRococoSender::get(), - amount: *amount == id_deposit, - }, - // Amount reserved for subs - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == PeopleRococoSender::get(), - amount: *amount == subs_deposit, - }, - // Confirmation from Migrator with individual identity and subs deposits - RuntimeEvent::IdentityMigrator( - polkadot_runtime_common::identity_migrator::Event::DepositUpdated { - who, identity, subs - }) => { - who: *who == PeopleRococoSender::get(), - identity: *identity == id_deposit, - subs: *subs == subs_deposit, - }, - RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { .. }) => {}, - ] - ); - }, - }; -} - -/// Duplicate of the impl of `ToParachainIdentityReaper` in the Rococo runtime. -fn calculate_remote_deposit(bytes: u32, subs: u32) -> Balance { - // Note: These `deposit` functions and `EXISTENTIAL_DEPOSIT` correspond to the Relay Chain's. - // Pulled in: use rococo_runtime_constants::currency::*; - let para_basic_deposit = deposit(1, 17) / 100; - let para_byte_deposit = deposit(0, 1) / 100; - let para_sub_account_deposit = deposit(1, 53) / 100; - let para_existential_deposit = EXISTENTIAL_DEPOSIT / 10; - - // pallet deposits - let id_deposit = - para_basic_deposit.saturating_add(para_byte_deposit.saturating_mul(bytes as Balance)); - let subs_deposit = para_sub_account_deposit.saturating_mul(subs as Balance); - - id_deposit - .saturating_add(subs_deposit) - .saturating_add(para_existential_deposit.saturating_mul(2)) -} - -// Represent some `additional` data that would not be migrated to the parachain. The encoded size, -// and thus the byte deposit, should decrease. -fn nonsensical_additional() -> BoundedVec<(Data, Data), MaxAdditionalFields> { - BoundedVec::try_from(vec![( - Data::Raw(b"fOo".to_vec().try_into().unwrap()), - Data::Raw(b"baR".to_vec().try_into().unwrap()), - )]) - .unwrap() -} - -// Represent some `additional` data that will be migrated to the parachain as first-class fields. -fn meaningful_additional() -> BoundedVec<(Data, Data), MaxAdditionalFields> { - BoundedVec::try_from(vec![ - ( - Data::Raw(b"github".to_vec().try_into().unwrap()), - Data::Raw(b"niels-username".to_vec().try_into().unwrap()), - ), - ( - Data::Raw(b"discord".to_vec().try_into().unwrap()), - Data::Raw(b"bohr-username".to_vec().try_into().unwrap()), - ), - ]) - .unwrap() -} - -// Execute a single test case. -fn assert_relay_para_flow(id: &Identity) { - let total_deposit = set_id_relay(id); - assert_set_id_parachain(id); - assert_reap_id_relay(total_deposit, id); - assert_reap_parachain(id); -} - -// Tests with empty `IdentityInfo`. - -#[test] -fn on_reap_identity_works_for_minimal_identity_with_zero_subs() { - assert_relay_para_flow(&Identity::new(false, None, Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_minimal_identity() { - assert_relay_para_flow(&Identity::new(false, None, Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_minimal_identity_with_max_subs() { - assert_relay_para_flow(&Identity::new(false, None, Subs::Many(MaxSubAccounts::get()))); -} - -// Tests with full `IdentityInfo`. - -#[test] -fn on_reap_identity_works_for_full_identity_no_additional_zero_subs() { - assert_relay_para_flow(&Identity::new(true, None, Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_full_identity_no_additional() { - assert_relay_para_flow(&Identity::new(true, None, Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_full_identity_no_additional_max_subs() { - assert_relay_para_flow(&Identity::new(true, None, Subs::Many(MaxSubAccounts::get()))); -} - -// Tests with full `IdentityInfo` and `additional` fields that will _not_ be migrated. - -#[test] -fn on_reap_identity_works_for_full_identity_nonsense_additional_zero_subs() { - assert_relay_para_flow(&Identity::new(true, Some(nonsensical_additional()), Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_full_identity_nonsense_additional() { - assert_relay_para_flow(&Identity::new(true, Some(nonsensical_additional()), Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_full_identity_nonsense_additional_max_subs() { - assert_relay_para_flow(&Identity::new( - true, - Some(nonsensical_additional()), - Subs::Many(MaxSubAccounts::get()), - )); -} - -// Tests with full `IdentityInfo` and `additional` fields that will be migrated. - -#[test] -fn on_reap_identity_works_for_full_identity_meaningful_additional_zero_subs() { - assert_relay_para_flow(&Identity::new(true, Some(meaningful_additional()), Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_full_identity_meaningful_additional() { - assert_relay_para_flow(&Identity::new(true, Some(meaningful_additional()), Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_full_identity_meaningful_additional_max_subs() { - assert_relay_para_flow(&Identity::new( - true, - Some(meaningful_additional()), - Subs::Many(MaxSubAccounts::get()), - )); -} diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs index c28b305b2c94..44e6b3934f0e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs @@ -14,46 +14,27 @@ // limitations under the License. use crate::imports::*; +use emulated_integration_tests_common::{ + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, +}; -fn relay_origin_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - Rococo::assert_xcm_pallet_attempted_complete(None); +#[test] +fn teleport_from_and_to_relay() { + let amount = ROCOCO_ED * 100; + let native_asset: Assets = (Here, amount).into(); - assert_expected_events!( + test_relay_is_trusted_teleporter!( Rococo, - vec![ - // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, - }, - // Amount to teleport is deposited in Relay's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - ] + RococoXcmConfig, + vec![PeopleRococo], + (native_asset, amount) ); -} -fn relay_dest_assertions(t: SystemParaToRelayTest) { - type RuntimeEvent = ::RuntimeEvent; - - Rococo::assert_ump_queue_processed(true, Some(PeopleRococo::para_id()), None); - - assert_expected_events!( + test_parachain_is_trusted_teleporter_for_relay!( + PeopleRococo, + PeopleRococoXcmConfig, Rococo, - vec![ - // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] + amount ); } @@ -80,33 +61,6 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { ); } -fn para_dest_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - PeopleRococo::assert_dmp_queue_complete(None); - - assert_expected_events!( - PeopleRococo, - vec![ - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - -fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult { - ::XcmPallet::limited_teleport_assets( - t.signed_origin, - bx!(t.args.dest.into()), - bx!(t.args.beneficiary.into()), - bx!(t.args.assets.into()), - t.args.fee_asset_item, - t.args.weight_limit, - ) -} - fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult { ::PolkadotXcm::limited_teleport_assets( t.signed_origin, @@ -118,92 +72,8 @@ fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResu ) } -/// Limited Teleport of native asset from Relay Chain to the System Parachain should work -#[test] -fn limited_teleport_native_assets_from_relay_to_system_para_works() { - // Init values for Relay Chain - let amount_to_send: Balance = ROCOCO_ED * 1000; - let dest = Rococo::child_location_of(PeopleRococo::para_id()); - let beneficiary_id = PeopleRococoReceiver::get(); - let test_args = TestContext { - sender: RococoSender::get(), - receiver: PeopleRococoReceiver::get(), - args: TestArgs::new_relay(dest, beneficiary_id, amount_to_send), - }; - - let mut test = RelayToSystemParaTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(relay_origin_assertions); - test.set_assertion::(para_dest_assertions); - test.set_dispatchable::(relay_limited_teleport_assets); - test.assert(); - - let delivery_fees = Rococo::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); -} - -/// Limited Teleport of native asset from System Parachain to Relay Chain -/// should work when there is enough balance in Relay Chain's `CheckAccount` -#[test] -fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { - // Dependency - Relay Chain's `CheckAccount` should have enough balance - limited_teleport_native_assets_from_relay_to_system_para_works(); - - let amount_to_send: Balance = PEOPLE_ROCOCO_ED * 1000; - let destination = PeopleRococo::parent_location(); - let beneficiary_id = RococoReceiver::get(); - let assets = (Parent, amount_to_send).into(); - - // Fund a sender - PeopleRococo::fund_accounts(vec![(PeopleRococoSender::get(), ROCOCO_ED * 2_000u128)]); - - let test_args = TestContext { - sender: PeopleRococoSender::get(), - receiver: RococoReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), - }; - - let mut test = SystemParaToRelayTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(para_origin_assertions); - test.set_assertion::(relay_dest_assertions); - test.set_dispatchable::(system_para_limited_teleport_assets); - test.assert(); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = PeopleRococo::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); -} - /// Limited Teleport of native asset from System Parachain to Relay Chain -/// should't work when there is not enough balance in Relay Chain's `CheckAccount` +/// shouldn't work when there is not enough balance in Relay Chain's `CheckAccount` #[test] fn limited_teleport_native_assets_from_system_para_to_relay_fails() { // Init values for Relay Chain diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/Cargo.toml index f7e1cce85a2c..aa6eebc5458f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/Cargo.toml @@ -15,6 +15,7 @@ frame-support = { workspace = true } pallet-balances = { workspace = true } pallet-message-queue = { workspace = true } pallet-identity = { workspace = true } +pallet-xcm = { workspace = true } sp-runtime = { workspace = true } # Polkadot diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs index 386a1b91c85b..418cfea07ddc 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs @@ -15,13 +15,8 @@ #[cfg(test)] mod imports { - pub use codec::Encode; // Substrate - pub use frame_support::{ - assert_ok, - sp_runtime::{AccountId32, DispatchResult}, - traits::fungibles::Inspect, - }; + pub use frame_support::{assert_ok, sp_runtime::DispatchResult, traits::fungibles::Inspect}; // Polkadot pub use xcm::prelude::*; @@ -36,20 +31,14 @@ mod imports { pub use westend_system_emulated_network::{ self, people_westend_emulated_chain::{ - genesis::ED as PEOPLE_WESTEND_ED, people_westend_runtime::{ - people, xcm_config::XcmConfig as PeopleWestendXcmConfig, - ExistentialDeposit as PeopleWestendExistentialDeposit, Runtime as PeopleRuntime, + xcm_config::XcmConfig as PeopleWestendXcmConfig, + ExistentialDeposit as PeopleWestendExistentialDeposit, }, PeopleWestendParaPallet as PeopleWestendPallet, }, westend_emulated_chain::{ - genesis::ED as WESTEND_ED, - westend_runtime::{ - xcm_config::XcmConfig as WestendXcmConfig, BasicDeposit, ByteDeposit, - MaxAdditionalFields, MaxSubAccounts, Runtime as WestendRuntime, - RuntimeOrigin as WestendOrigin, SubAccountDeposit, - }, + genesis::ED as WESTEND_ED, westend_runtime::xcm_config::XcmConfig as WestendXcmConfig, WestendRelayPallet as WestendPallet, }, PeopleWestendPara as PeopleWestend, PeopleWestendParaReceiver as PeopleWestendReceiver, @@ -57,7 +46,6 @@ mod imports { WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, }; - pub type RelayToSystemParaTest = Test; pub type SystemParaToRelayTest = Test; } diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/mod.rs index 3f18621224ac..08749b295dc2 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/mod.rs @@ -14,5 +14,4 @@ // limitations under the License. mod claim_assets; -mod reap_identity; mod teleport; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/reap_identity.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/reap_identity.rs deleted file mode 100644 index cfbf4d7d9580..000000000000 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/reap_identity.rs +++ /dev/null @@ -1,547 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # OnReapIdentity Tests -//! -//! This file contains the test cases for migrating Identity data away from the Westend Relay -//! chain and to the PeopleWestend parachain. This migration is part of the broader Minimal Relay -//! effort: -//! https://github.com/polkadot-fellows/RFCs/blob/main/text/0032-minimal-relay.md -//! -//! ## Overview -//! -//! The tests validate the robustness and correctness of the `OnReapIdentityHandler` -//! ensuring that it behaves as expected in various scenarios. Key aspects tested include: -//! -//! - **Deposit Handling**: Confirming that deposits are correctly migrated from the Relay Chain to -//! the People parachain in various scenarios (different `IdentityInfo` fields and different -//! numbers of sub-accounts). -//! -//! ### Test Scenarios -//! -//! The tests are categorized into several scenarios, each resulting in different deposits required -//! on the destination parachain. The tests ensure: -//! -//! - Reserved deposits on the Relay Chain are fully released; -//! - The freed deposit from the Relay Chain is sufficient for the parachain deposit; and -//! - The account will exist on the parachain. - -use crate::imports::*; -use frame_support::BoundedVec; -use pallet_balances::Event as BalancesEvent; -use pallet_identity::{legacy::IdentityInfo, Data, Event as IdentityEvent, IdentityOf, SubsOf}; -use people::{ - BasicDeposit as BasicDepositParachain, ByteDeposit as ByteDepositParachain, - IdentityInfo as IdentityInfoParachain, SubAccountDeposit as SubAccountDepositParachain, -}; -use westend_runtime_constants::currency::*; -use westend_system_emulated_network::{ - westend_emulated_chain::WestendRelayPallet, WestendRelay, WestendRelaySender, -}; - -type Balance = u128; -type WestendIdentity = ::Identity; -type WestendBalances = ::Balances; -type WestendIdentityMigrator = ::IdentityMigrator; -type PeopleWestendIdentity = ::Identity; -type PeopleWestendBalances = ::Balances; - -#[derive(Clone, Debug)] -struct Identity { - relay: IdentityInfo, - para: IdentityInfoParachain, - subs: Subs, -} - -impl Identity { - fn new( - full: bool, - additional: Option>, - subs: Subs, - ) -> Self { - let pgp_fingerprint = [ - 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, - 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, - ]; - let make_data = |data: &[u8], full: bool| -> Data { - if full { - Data::Raw(data.to_vec().try_into().unwrap()) - } else { - Data::None - } - }; - let (github, discord) = additional - .as_ref() - .and_then(|vec| vec.first()) - .map(|(g, d)| (g.clone(), d.clone())) - .unwrap_or((Data::None, Data::None)); - Self { - relay: IdentityInfo { - display: make_data(b"xcm-test", full), - legal: make_data(b"The Xcm Test, Esq.", full), - web: make_data(b"https://visitme/", full), - riot: make_data(b"xcm-riot", full), - email: make_data(b"xcm-test@gmail.com", full), - pgp_fingerprint: Some(pgp_fingerprint), - image: make_data(b"xcm-test.png", full), - twitter: make_data(b"@xcm-test", full), - additional: additional.unwrap_or_default(), - }, - para: IdentityInfoParachain { - display: make_data(b"xcm-test", full), - legal: make_data(b"The Xcm Test, Esq.", full), - web: make_data(b"https://visitme/", full), - matrix: make_data(b"xcm-matrix@server", full), - email: make_data(b"xcm-test@gmail.com", full), - pgp_fingerprint: Some(pgp_fingerprint), - image: make_data(b"xcm-test.png", full), - twitter: make_data(b"@xcm-test", full), - github, - discord, - }, - subs, - } - } -} - -#[derive(Clone, Debug)] -enum Subs { - Zero, - Many(u32), -} - -enum IdentityOn<'a> { - Relay(&'a IdentityInfo), - Para(&'a IdentityInfoParachain), -} - -impl IdentityOn<'_> { - fn calculate_deposit(self) -> Balance { - match self { - IdentityOn::Relay(id) => { - let base_deposit = BasicDeposit::get(); - let byte_deposit = - ByteDeposit::get() * TryInto::::try_into(id.encoded_size()).unwrap(); - base_deposit + byte_deposit - }, - IdentityOn::Para(id) => { - let base_deposit = BasicDepositParachain::get(); - let byte_deposit = ByteDepositParachain::get() * - TryInto::::try_into(id.encoded_size()).unwrap(); - base_deposit + byte_deposit - }, - } - } -} - -/// Generate an `AccountId32` from a `u32`. -/// This creates a 32-byte array, initially filled with `255`, and then repeatedly fills it -/// with the 4-byte little-endian representation of the `u32` value, until the array is full. -/// -/// **Example**: -/// -/// `account_from_u32(5)` will return an `AccountId32` with the bytes -/// `[0, 5, 0, 0, 0, 0, 0, 0, 0, 5 ... ]` -fn account_from_u32(id: u32) -> AccountId32 { - let mut buffer = [255u8; 32]; - let id_bytes = id.to_le_bytes(); - let id_size = id_bytes.len(); - for chunk in buffer.chunks_mut(id_size) { - chunk.clone_from_slice(&id_bytes); - } - AccountId32::new(buffer) -} - -// Set up the Relay Chain with an identity. -fn set_id_relay(id: &Identity) -> Balance { - let mut total_deposit: Balance = 0; - - // Set identity and Subs on Relay Chain - WestendRelay::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - assert_ok!(WestendIdentity::set_identity( - WestendOrigin::signed(WestendRelaySender::get()), - Box::new(id.relay.clone()) - )); - - if let Subs::Many(n) = id.subs { - let subs: Vec<_> = (0..n) - .map(|i| (account_from_u32(i), Data::Raw(b"name".to_vec().try_into().unwrap()))) - .collect(); - - assert_ok!(WestendIdentity::set_subs( - WestendOrigin::signed(WestendRelaySender::get()), - subs, - )); - } - - let reserved_balance = WestendBalances::reserved_balance(WestendRelaySender::get()); - let id_deposit = IdentityOn::Relay(&id.relay).calculate_deposit(); - - let total_deposit = match id.subs { - Subs::Zero => { - total_deposit = id_deposit; // No subs - assert_expected_events!( - WestendRelay, - vec![ - RuntimeEvent::Identity(IdentityEvent::IdentitySet { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == WestendRelaySender::get(), - amount: *amount == id_deposit, - }, - ] - ); - total_deposit - }, - Subs::Many(n) => { - let sub_account_deposit = n as Balance * SubAccountDeposit::get(); - total_deposit = - sub_account_deposit + IdentityOn::Relay(&id.relay).calculate_deposit(); - assert_expected_events!( - WestendRelay, - vec![ - RuntimeEvent::Identity(IdentityEvent::IdentitySet { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == WestendRelaySender::get(), - amount: *amount == id_deposit, - }, - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == WestendRelaySender::get(), - amount: *amount == sub_account_deposit, - }, - ] - ); - total_deposit - }, - }; - - assert_eq!(reserved_balance, total_deposit); - }); - total_deposit -} - -// Set up the parachain with an identity and (maybe) sub accounts, but with zero deposits. -fn assert_set_id_parachain(id: &Identity) { - // Set identity and Subs on Parachain with zero deposit - PeopleWestend::execute_with(|| { - let free_bal = PeopleWestendBalances::free_balance(PeopleWestendSender::get()); - let reserved_balance = PeopleWestendBalances::reserved_balance(PeopleWestendSender::get()); - - // total balance at Genesis should be zero - assert_eq!(reserved_balance + free_bal, 0); - - assert_ok!(PeopleWestendIdentity::set_identity_no_deposit( - &PeopleWestendSender::get(), - id.para.clone(), - )); - - match id.subs { - Subs::Zero => {}, - Subs::Many(n) => { - let subs: Vec<_> = (0..n) - .map(|ii| { - (account_from_u32(ii), Data::Raw(b"name".to_vec().try_into().unwrap())) - }) - .collect(); - assert_ok!(PeopleWestendIdentity::set_subs_no_deposit( - &PeopleWestendSender::get(), - subs, - )); - }, - } - - // No amount should be reserved as deposit amounts are set to 0. - let reserved_balance = PeopleWestendBalances::reserved_balance(PeopleWestendSender::get()); - assert_eq!(reserved_balance, 0); - assert!(IdentityOf::::get(PeopleWestendSender::get()).is_some()); - - let (_, sub_accounts) = SubsOf::::get(PeopleWestendSender::get()); - - match id.subs { - Subs::Zero => assert_eq!(sub_accounts.len(), 0), - Subs::Many(n) => assert_eq!(sub_accounts.len(), n as usize), - } - }); -} - -// Reap the identity on the Relay Chain and assert that the correct things happen there. -fn assert_reap_id_relay(total_deposit: Balance, id: &Identity) { - WestendRelay::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - let free_bal_before_reap = WestendBalances::free_balance(WestendRelaySender::get()); - let reserved_balance = WestendBalances::reserved_balance(WestendRelaySender::get()); - - assert_eq!(reserved_balance, total_deposit); - - assert_ok!(WestendIdentityMigrator::reap_identity( - WestendOrigin::signed(WestendRelaySender::get()), - WestendRelaySender::get() - )); - - let remote_deposit = match id.subs { - Subs::Zero => calculate_remote_deposit(id.relay.encoded_size() as u32, 0), - Subs::Many(n) => calculate_remote_deposit(id.relay.encoded_size() as u32, n), - }; - - assert_expected_events!( - WestendRelay, - vec![ - // `reap_identity` sums the identity and subs deposits and unreserves them in one - // call. Therefore, we only expect one `Unreserved` event. - RuntimeEvent::Balances(BalancesEvent::Unreserved { who, amount }) => { - who: *who == WestendRelaySender::get(), - amount: *amount == total_deposit, - }, - RuntimeEvent::IdentityMigrator( - polkadot_runtime_common::identity_migrator::Event::IdentityReaped { - who, - }) => { - who: *who == PeopleWestendSender::get(), - }, - ] - ); - // Identity should be gone. - assert!(IdentityOf::::get(WestendRelaySender::get()).is_none()); - - // Subs should be gone. - let (_, sub_accounts) = SubsOf::::get(WestendRelaySender::get()); - assert_eq!(sub_accounts.len(), 0); - - let reserved_balance = WestendBalances::reserved_balance(WestendRelaySender::get()); - assert_eq!(reserved_balance, 0); - - // Free balance should be greater (i.e. the teleport should work even if 100% of an - // account's balance is reserved for Identity). - let free_bal_after_reap = WestendBalances::free_balance(WestendRelaySender::get()); - assert!(free_bal_after_reap > free_bal_before_reap); - - // Implicit: total_deposit > remote_deposit. As in, accounts should always have enough - // reserved for the parachain deposit. - assert_eq!(free_bal_after_reap, free_bal_before_reap + total_deposit - remote_deposit); - }); -} - -// Reaping the identity on the Relay Chain will have sent an XCM program to the parachain. Ensure -// that everything happens as expected. -fn assert_reap_parachain(id: &Identity) { - PeopleWestend::execute_with(|| { - let reserved_balance = PeopleWestendBalances::reserved_balance(PeopleWestendSender::get()); - let id_deposit = IdentityOn::Para(&id.para).calculate_deposit(); - let total_deposit = match id.subs { - Subs::Zero => id_deposit, - Subs::Many(n) => id_deposit + n as Balance * SubAccountDepositParachain::get(), - }; - assert_reap_events(id_deposit, id); - assert_eq!(reserved_balance, total_deposit); - - // Should have at least one ED after in free balance after the reap. - assert!( - PeopleWestendBalances::free_balance(PeopleWestendSender::get()) >= PEOPLE_WESTEND_ED - ); - }); -} - -// Assert the events that should happen on the parachain upon reaping an identity on the Relay -// Chain. -fn assert_reap_events(id_deposit: Balance, id: &Identity) { - type RuntimeEvent = ::RuntimeEvent; - match id.subs { - Subs::Zero => { - assert_expected_events!( - PeopleWestend, - vec![ - // Deposit and Endowed from teleport - RuntimeEvent::Balances(BalancesEvent::Minted { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Endowed { .. }) => {}, - // Amount reserved for identity info - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == PeopleWestendSender::get(), - amount: *amount == id_deposit, - }, - // Confirmation from Migrator with individual identity and subs deposits - RuntimeEvent::IdentityMigrator( - polkadot_runtime_common::identity_migrator::Event::DepositUpdated { - who, identity, subs - }) => { - who: *who == PeopleWestendSender::get(), - identity: *identity == id_deposit, - subs: *subs == 0, - }, - RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { .. }) => {}, - ] - ); - }, - Subs::Many(n) => { - let subs_deposit = n as Balance * SubAccountDepositParachain::get(); - assert_expected_events!( - PeopleWestend, - vec![ - // Deposit and Endowed from teleport - RuntimeEvent::Balances(BalancesEvent::Minted { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Endowed { .. }) => {}, - // Amount reserved for identity info - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == PeopleWestendSender::get(), - amount: *amount == id_deposit, - }, - // Amount reserved for subs - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == PeopleWestendSender::get(), - amount: *amount == subs_deposit, - }, - // Confirmation from Migrator with individual identity and subs deposits - RuntimeEvent::IdentityMigrator( - polkadot_runtime_common::identity_migrator::Event::DepositUpdated { - who, identity, subs - }) => { - who: *who == PeopleWestendSender::get(), - identity: *identity == id_deposit, - subs: *subs == subs_deposit, - }, - RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { .. }) => {}, - ] - ); - }, - }; -} - -/// Duplicate of the impl of `ToParachainIdentityReaper` in the Westend runtime. -fn calculate_remote_deposit(bytes: u32, subs: u32) -> Balance { - // Note: These `deposit` functions and `EXISTENTIAL_DEPOSIT` correspond to the Relay Chain's. - // Pulled in: use westend_runtime_constants::currency::*; - let para_basic_deposit = deposit(1, 17) / 100; - let para_byte_deposit = deposit(0, 1) / 100; - let para_sub_account_deposit = deposit(1, 53) / 100; - let para_existential_deposit = EXISTENTIAL_DEPOSIT / 10; - - // pallet deposits - let id_deposit = - para_basic_deposit.saturating_add(para_byte_deposit.saturating_mul(bytes as Balance)); - let subs_deposit = para_sub_account_deposit.saturating_mul(subs as Balance); - - id_deposit - .saturating_add(subs_deposit) - .saturating_add(para_existential_deposit.saturating_mul(2)) -} - -// Represent some `additional` data that would not be migrated to the parachain. The encoded size, -// and thus the byte deposit, should decrease. -fn nonsensical_additional() -> BoundedVec<(Data, Data), MaxAdditionalFields> { - BoundedVec::try_from(vec![( - Data::Raw(b"fOo".to_vec().try_into().unwrap()), - Data::Raw(b"baR".to_vec().try_into().unwrap()), - )]) - .unwrap() -} - -// Represent some `additional` data that will be migrated to the parachain as first-class fields. -fn meaningful_additional() -> BoundedVec<(Data, Data), MaxAdditionalFields> { - BoundedVec::try_from(vec![ - ( - Data::Raw(b"github".to_vec().try_into().unwrap()), - Data::Raw(b"niels-username".to_vec().try_into().unwrap()), - ), - ( - Data::Raw(b"discord".to_vec().try_into().unwrap()), - Data::Raw(b"bohr-username".to_vec().try_into().unwrap()), - ), - ]) - .unwrap() -} - -// Execute a single test case. -fn assert_relay_para_flow(id: &Identity) { - let total_deposit = set_id_relay(id); - assert_set_id_parachain(id); - assert_reap_id_relay(total_deposit, id); - assert_reap_parachain(id); -} - -// Tests with empty `IdentityInfo`. - -#[test] -fn on_reap_identity_works_for_minimal_identity_with_zero_subs() { - assert_relay_para_flow(&Identity::new(false, None, Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_minimal_identity() { - assert_relay_para_flow(&Identity::new(false, None, Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_minimal_identity_with_max_subs() { - assert_relay_para_flow(&Identity::new(false, None, Subs::Many(MaxSubAccounts::get()))); -} - -// Tests with full `IdentityInfo`. - -#[test] -fn on_reap_identity_works_for_full_identity_no_additional_zero_subs() { - assert_relay_para_flow(&Identity::new(true, None, Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_full_identity_no_additional() { - assert_relay_para_flow(&Identity::new(true, None, Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_full_identity_no_additional_max_subs() { - assert_relay_para_flow(&Identity::new(true, None, Subs::Many(MaxSubAccounts::get()))); -} - -// Tests with full `IdentityInfo` and `additional` fields that will _not_ be migrated. - -#[test] -fn on_reap_identity_works_for_full_identity_nonsense_additional_zero_subs() { - assert_relay_para_flow(&Identity::new(true, Some(nonsensical_additional()), Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_full_identity_nonsense_additional() { - assert_relay_para_flow(&Identity::new(true, Some(nonsensical_additional()), Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_full_identity_nonsense_additional_max_subs() { - assert_relay_para_flow(&Identity::new( - true, - Some(nonsensical_additional()), - Subs::Many(MaxSubAccounts::get()), - )); -} - -// Tests with full `IdentityInfo` and `additional` fields that will be migrated. - -#[test] -fn on_reap_identity_works_for_full_identity_meaningful_additional_zero_subs() { - assert_relay_para_flow(&Identity::new(true, Some(meaningful_additional()), Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_full_identity_meaningful_additional() { - assert_relay_para_flow(&Identity::new(true, Some(meaningful_additional()), Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_full_identity_meaningful_additional_max_subs() { - assert_relay_para_flow(&Identity::new( - true, - Some(meaningful_additional()), - Subs::Many(MaxSubAccounts::get()), - )); -} diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs index 294583399011..83888031723f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs @@ -14,46 +14,27 @@ // limitations under the License. use crate::imports::*; +use emulated_integration_tests_common::{ + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, +}; -fn relay_origin_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - Westend::assert_xcm_pallet_attempted_complete(None); +#[test] +fn teleport_from_and_to_relay() { + let amount = WESTEND_ED * 100; + let native_asset: Assets = (Here, amount).into(); - assert_expected_events!( + test_relay_is_trusted_teleporter!( Westend, - vec![ - // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, - }, - // Amount to teleport is deposited in Relay's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - ] + WestendXcmConfig, + vec![PeopleWestend], + (native_asset, amount) ); -} -fn relay_dest_assertions(t: SystemParaToRelayTest) { - type RuntimeEvent = ::RuntimeEvent; - - Westend::assert_ump_queue_processed(true, Some(PeopleWestend::para_id()), None); - - assert_expected_events!( + test_parachain_is_trusted_teleporter_for_relay!( + PeopleWestend, + PeopleWestendXcmConfig, Westend, - vec![ - // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] + amount ); } @@ -80,33 +61,6 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { ); } -fn para_dest_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - PeopleWestend::assert_dmp_queue_complete(None); - - assert_expected_events!( - PeopleWestend, - vec![ - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - -fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult { - ::XcmPallet::limited_teleport_assets( - t.signed_origin, - bx!(t.args.dest.into()), - bx!(t.args.beneficiary.into()), - bx!(t.args.assets.into()), - t.args.fee_asset_item, - t.args.weight_limit, - ) -} - fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult { ::PolkadotXcm::limited_teleport_assets( t.signed_origin, @@ -118,92 +72,8 @@ fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResu ) } -/// Limited Teleport of native asset from Relay Chain to the System Parachain should work -#[test] -fn limited_teleport_native_assets_from_relay_to_system_para_works() { - // Init values for Relay Chain - let amount_to_send: Balance = WESTEND_ED * 1000; - let dest = Westend::child_location_of(PeopleWestend::para_id()); - let beneficiary_id = PeopleWestendReceiver::get(); - let test_args = TestContext { - sender: WestendSender::get(), - receiver: PeopleWestendReceiver::get(), - args: TestArgs::new_relay(dest, beneficiary_id, amount_to_send), - }; - - let mut test = RelayToSystemParaTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(relay_origin_assertions); - test.set_assertion::(para_dest_assertions); - test.set_dispatchable::(relay_limited_teleport_assets); - test.assert(); - - let delivery_fees = Westend::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); -} - -/// Limited Teleport of native asset from System Parachain to Relay Chain -/// should work when there is enough balance in Relay Chain's `CheckAccount` -#[test] -fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { - // Dependency - Relay Chain's `CheckAccount` should have enough balance - limited_teleport_native_assets_from_relay_to_system_para_works(); - - let amount_to_send: Balance = PEOPLE_WESTEND_ED * 1000; - let destination = PeopleWestend::parent_location(); - let beneficiary_id = WestendReceiver::get(); - let assets = (Parent, amount_to_send).into(); - - // Fund a sender - PeopleWestend::fund_accounts(vec![(PeopleWestendSender::get(), WESTEND_ED * 2_000u128)]); - - let test_args = TestContext { - sender: PeopleWestendSender::get(), - receiver: WestendReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), - }; - - let mut test = SystemParaToRelayTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(para_origin_assertions); - test.set_assertion::(relay_dest_assertions); - test.set_dispatchable::(system_para_limited_teleport_assets); - test.assert(); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = PeopleWestend::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); -} - /// Limited Teleport of native asset from System Parachain to Relay Chain -/// should't work when there is not enough balance in Relay Chain's `CheckAccount` +/// shouldn't work when there is not enough balance in Relay Chain's `CheckAccount` #[test] fn limited_teleport_native_assets_from_system_para_to_relay_fails() { // Init values for Relay Chain diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs index ae4fe9e84337..08b1d192b0be 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs @@ -48,6 +48,7 @@ use xcm_builder::{ use xcm_executor::XcmExecutor; parameter_types! { + pub const RootLocation: Location = Location::here(); pub const WndLocation: Location = Location::parent(); pub const RelayNetwork: Option = Some(NetworkId::Westend); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); @@ -139,6 +140,13 @@ impl Contains for ParentOrParentsPlurality { } } +pub struct LocalPlurality; +impl Contains for LocalPlurality { + fn contains(loc: &Location) -> bool { + matches!(loc.unpack(), (0, [Plurality { .. }])) + } +} + pub type Barrier = TrailingSetTopicAsId< DenyThenTry< DenyReserveTransferToRelayChain, @@ -173,6 +181,8 @@ pub type Barrier = TrailingSetTopicAsId< pub type WaivedLocations = ( RelayOrOtherSystemParachains, Equals, + Equals, + LocalPlurality, ); /// Cases where a remote origin is accepted as trusted Teleporter for a given asset: From 00946b10ab18331f959f5cbced7c433b6132b1cb Mon Sep 17 00:00:00 2001 From: Muharem Date: Wed, 14 Aug 2024 15:35:34 +0200 Subject: [PATCH 108/382] Make ticket non-optional and add ensure_successful method to Consideration trait (#5359) Make ticket non-optional and add ensure_successful method to Consideration trait. Reverts the optional return ticket type for the new function introduced in [polkadot-sdk/4596](https://github.com/paritytech/polkadot-sdk/pull/4596) and adds a helper `ensure_successful` function for the runtime benchmarks. Since the existing FRAME pallet represents zero cost with a zero balance rather than `None` in an option, maintaining the ticket type as a non-optional balance is beneficial for backward compatibility and helps avoid unnecessary migrations. --- prdoc/pr_5359.prdoc | 21 ++++ .../balances/src/tests/fungible_tests.rs | 41 ++++--- substrate/frame/preimage/src/benchmarking.rs | 2 +- substrate/frame/preimage/src/lib.rs | 17 ++- substrate/frame/support/src/traits/storage.rs | 26 ++-- .../support/src/traits/tokens/fungible/mod.rs | 112 ++++++++---------- 6 files changed, 115 insertions(+), 104 deletions(-) create mode 100644 prdoc/pr_5359.prdoc diff --git a/prdoc/pr_5359.prdoc b/prdoc/pr_5359.prdoc new file mode 100644 index 000000000000..bf059129a436 --- /dev/null +++ b/prdoc/pr_5359.prdoc @@ -0,0 +1,21 @@ +title: Make ticket non-optional and add ensure_successful method to Consideration trait + +doc: + - audience: Runtime Dev + description: | + Make ticket non-optional and add ensure_successful method to Consideration trait. + + Reverts the optional return ticket type for the new function introduced in + [polkadot-sdk/4596](https://github.com/paritytech/polkadot-sdk/pull/4596) and adds a helper + `ensure_successful` function for the runtime benchmarks. + Since the existing FRAME pallet represents zero cost with a zero balance type rather than + `None` in an option, maintaining the ticket type as a non-optional balance is beneficial + for backward compatibility and helps avoid unnecessary migrations. + +crates: + - name: frame-support + bump: major + - name: pallet-preimage + bump: major + - name: pallet-balances + bump: patch diff --git a/substrate/frame/balances/src/tests/fungible_tests.rs b/substrate/frame/balances/src/tests/fungible_tests.rs index 1a09303a6590..e1c1be9b1335 100644 --- a/substrate/frame/balances/src/tests/fungible_tests.rs +++ b/substrate/frame/balances/src/tests/fungible_tests.rs @@ -518,24 +518,25 @@ fn freeze_consideration_works() { let who = 4; // freeze amount taken somewhere outside of our (Consideration) scope. let extend_freeze = 15; + let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10); - let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap().unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 4); assert_ok!(Balances::increase_frozen(&TestId::Foo, &who, extend_freeze)); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 4 + extend_freeze); - let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap().unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 8 + extend_freeze); - assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), None); + assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), zero_ticket); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0 + extend_freeze); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10 + extend_freeze); let _ = ticket.drop(&who).unwrap(); @@ -560,24 +561,26 @@ fn hold_consideration_works() { let who = 4; // hold amount taken somewhere outside of our (Consideration) scope. let extend_hold = 15; + + let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); - let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap().unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 4); assert_ok!(Balances::hold(&TestId::Foo, &who, extend_hold)); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 4 + extend_hold); - let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap().unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 8 + extend_hold); - assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), None); + assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), zero_ticket); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0 + extend_hold); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10 + extend_hold); let _ = ticket.drop(&who).unwrap(); @@ -600,21 +603,22 @@ fn lone_freeze_consideration_works() { >; let who = 4; + let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10); assert_ok!(Balances::increase_frozen(&TestId::Foo, &who, 5)); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 15); - let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap().unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 4); - assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), None); + assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), zero_ticket); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10); let _ = ticket.drop(&who).unwrap(); @@ -637,21 +641,22 @@ fn lone_hold_consideration_works() { >; let who = 4; + let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); assert_ok!(Balances::hold(&TestId::Foo, &who, 5)); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 15); - let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap().unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 4); - assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), None); + assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), zero_ticket); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); let _ = ticket.drop(&who).unwrap(); diff --git a/substrate/frame/preimage/src/benchmarking.rs b/substrate/frame/preimage/src/benchmarking.rs index 2d3bec16b818..3d0c5b900579 100644 --- a/substrate/frame/preimage/src/benchmarking.rs +++ b/substrate/frame/preimage/src/benchmarking.rs @@ -116,7 +116,7 @@ benchmarks! { T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, hash ) verify { - let ticket = TicketOf::::new(¬er, Footprint { count: 1, size: MAX_SIZE as u64 }).unwrap().unwrap(); + let ticket = TicketOf::::new(¬er, Footprint { count: 1, size: MAX_SIZE as u64 }).unwrap(); let s = RequestStatus::Requested { maybe_ticket: Some((noter, ticket)), count: 1, maybe_len: Some(MAX_SIZE) }; assert_eq!(RequestStatusFor::::get(&hash), Some(s)); } diff --git a/substrate/frame/preimage/src/lib.rs b/substrate/frame/preimage/src/lib.rs index 30056fc6d9a4..658e7fec5348 100644 --- a/substrate/frame/preimage/src/lib.rs +++ b/substrate/frame/preimage/src/lib.rs @@ -124,8 +124,6 @@ pub mod pallet { type ManagerOrigin: EnsureOrigin; /// A means of providing some cost while data is stored on-chain. - /// - /// Should never return a `None`, implying no cost for a non-empty preimage. type Consideration: Consideration; } @@ -162,8 +160,6 @@ pub mod pallet { TooMany, /// Too few hashes were requested to be upgraded (i.e. zero). TooFew, - /// No ticket with a cost was returned by [`Config::Consideration`] to store the preimage. - NoCost, } /// A reason for this pallet placing a hold on funds. @@ -274,10 +270,10 @@ impl Pallet { // unreserve deposit T::Currency::unreserve(&who, amount); // take consideration - let Ok(Some(ticket)) = + let Ok(ticket) = T::Consideration::new(&who, Footprint::from_parts(1, len as usize)) + .defensive_proof("Unexpected inability to take deposit after unreserved") else { - defensive!("None ticket or inability to take deposit after unreserved"); return true }; RequestStatus::Unrequested { ticket: (who, ticket), len } @@ -288,10 +284,12 @@ impl Pallet { T::Currency::unreserve(&who, deposit); // take consideration if let Some(len) = maybe_len { - let Ok(Some(ticket)) = + let Ok(ticket) = T::Consideration::new(&who, Footprint::from_parts(1, len as usize)) + .defensive_proof( + "Unexpected inability to take deposit after unreserved", + ) else { - defensive!("None ticket or inability to take deposit after unreserved"); return true }; Some((who, ticket)) @@ -351,8 +349,7 @@ impl Pallet { RequestStatus::Requested { maybe_ticket: None, count: 1, maybe_len: Some(len) }, (None, Some(depositor)) => { let ticket = - T::Consideration::new(depositor, Footprint::from_parts(1, len as usize))? - .ok_or(Error::::NoCost)?; + T::Consideration::new(depositor, Footprint::from_parts(1, len as usize))?; RequestStatus::Unrequested { ticket: (depositor.clone(), ticket), len } }, }; diff --git a/substrate/frame/support/src/traits/storage.rs b/substrate/frame/support/src/traits/storage.rs index a954af14d259..eb63ea59f6cb 100644 --- a/substrate/frame/support/src/traits/storage.rs +++ b/substrate/frame/support/src/traits/storage.rs @@ -201,7 +201,7 @@ where } /// Some sort of cost taken from account temporarily in order to offset the cost to the chain of -/// holding some data `Footprint` (e.g. [`Footprint`]) in state. +/// holding some data [`Footprint`] in state. /// /// The cost may be increased, reduced or dropped entirely as the footprint changes. /// @@ -217,16 +217,14 @@ pub trait Consideration: Member + FullCodec + TypeInfo + MaxEncodedLen { /// Create a ticket for the `new` footprint attributable to `who`. This ticket *must* ultimately - /// be consumed through `update` or `drop` once the footprint changes or is removed. `None` - /// implies no cost for a given footprint. - fn new(who: &AccountId, new: Footprint) -> Result, DispatchError>; + /// be consumed through `update` or `drop` once the footprint changes or is removed. + fn new(who: &AccountId, new: Footprint) -> Result; /// Optionally consume an old ticket and alter the footprint, enforcing the new cost to `who` - /// and returning the new ticket (or an error if there was an issue). `None` implies no cost for - /// a given footprint. + /// and returning the new ticket (or an error if there was an issue). /// /// For creating tickets and dropping them, you can use the simpler `new` and `drop` instead. - fn update(self, who: &AccountId, new: Footprint) -> Result, DispatchError>; + fn update(self, who: &AccountId, new: Footprint) -> Result; /// Consume a ticket for some `old` footprint attributable to `who` which should now been freed. fn drop(self, who: &AccountId) -> Result<(), DispatchError>; @@ -239,18 +237,24 @@ pub trait Consideration: fn burn(self, _: &AccountId) { let _ = self; } + /// Ensure that creating a ticket for a given account and footprint will be successful if done + /// immediately after this call. + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(who: &AccountId, new: Footprint); } impl Consideration for () { - fn new(_: &A, _: F) -> Result, DispatchError> { - Ok(Some(())) + fn new(_: &A, _: F) -> Result { + Ok(()) } - fn update(self, _: &A, _: F) -> Result, DispatchError> { - Ok(Some(())) + fn update(self, _: &A, _: F) -> Result<(), DispatchError> { + Ok(()) } fn drop(self, _: &A) -> Result<(), DispatchError> { Ok(()) } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(_: &A, _: F) {} } macro_rules! impl_incrementable { diff --git a/substrate/frame/support/src/traits/tokens/fungible/mod.rs b/substrate/frame/support/src/traits/tokens/fungible/mod.rs index f40e494b930d..9b7c86971bb8 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/mod.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/mod.rs @@ -164,6 +164,8 @@ use codec::{Decode, Encode, MaxEncodedLen}; use core::marker::PhantomData; use frame_support_procedural::{CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; use scale_info::TypeInfo; +#[cfg(feature = "runtime-benchmarks")] +use sp_runtime::Saturating; use super::{ Fortitude::{Force, Polite}, @@ -209,38 +211,35 @@ pub struct FreezeConsideration(F::Balance, PhantomData ( where F: MutateFreeze; impl< - A: 'static, - F: 'static + MutateFreeze, + A: 'static + Eq, + #[cfg(not(feature = "runtime-benchmarks"))] F: 'static + MutateFreeze, + #[cfg(feature = "runtime-benchmarks")] F: 'static + MutateFreeze + Mutate, R: 'static + Get, D: 'static + Convert, Fp: 'static, > Consideration for FreezeConsideration { - fn new(who: &A, footprint: Fp) -> Result, DispatchError> { + fn new(who: &A, footprint: Fp) -> Result { let new = D::convert(footprint); - if new.is_zero() { - Ok(None) - } else { - F::increase_frozen(&R::get(), who, new)?; - Ok(Some(Self(new, PhantomData))) - } + F::increase_frozen(&R::get(), who, new)?; + Ok(Self(new, PhantomData)) } - fn update(self, who: &A, footprint: Fp) -> Result, DispatchError> { + fn update(self, who: &A, footprint: Fp) -> Result { let new = D::convert(footprint); if self.0 > new { F::decrease_frozen(&R::get(), who, self.0 - new)?; } else if new > self.0 { F::increase_frozen(&R::get(), who, new - self.0)?; } - if new.is_zero() { - Ok(None) - } else { - Ok(Some(Self(new, PhantomData))) - } + Ok(Self(new, PhantomData)) } fn drop(self, who: &A) -> Result<(), DispatchError> { F::decrease_frozen(&R::get(), who, self.0).map(|_| ()) } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(who: &A, fp: Fp) { + let _ = F::mint_into(who, F::minimum_balance().saturating_add(D::convert(fp))); + } } /// Consideration method using a `fungible` balance frozen as the cost exacted for the footprint. @@ -263,34 +262,27 @@ pub struct HoldConsideration( where F: MutateHold; impl< - A: 'static, - F: 'static + MutateHold, + A: 'static + Eq, + #[cfg(not(feature = "runtime-benchmarks"))] F: 'static + MutateHold, + #[cfg(feature = "runtime-benchmarks")] F: 'static + MutateHold + Mutate, R: 'static + Get, D: 'static + Convert, Fp: 'static, > Consideration for HoldConsideration { - fn new(who: &A, footprint: Fp) -> Result, DispatchError> { + fn new(who: &A, footprint: Fp) -> Result { let new = D::convert(footprint); - if new.is_zero() { - Ok(None) - } else { - F::hold(&R::get(), who, new)?; - Ok(Some(Self(new, PhantomData))) - } + F::hold(&R::get(), who, new)?; + Ok(Self(new, PhantomData)) } - fn update(self, who: &A, footprint: Fp) -> Result, DispatchError> { + fn update(self, who: &A, footprint: Fp) -> Result { let new = D::convert(footprint); if self.0 > new { F::release(&R::get(), who, self.0 - new, BestEffort)?; } else if new > self.0 { F::hold(&R::get(), who, new - self.0)?; } - if new.is_zero() { - Ok(None) - } else { - Ok(Some(Self(new, PhantomData))) - } + Ok(Self(new, PhantomData)) } fn drop(self, who: &A) -> Result<(), DispatchError> { F::release(&R::get(), who, self.0, BestEffort).map(|_| ()) @@ -298,6 +290,10 @@ impl< fn burn(self, who: &A) { let _ = F::burn_held(&R::get(), who, self.0, BestEffort, Force); } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(who: &A, fp: Fp) { + let _ = F::mint_into(who, F::minimum_balance().saturating_add(D::convert(fp))); + } } /// Basic consideration method using a `fungible` balance frozen as the cost exacted for the @@ -321,34 +317,28 @@ impl< #[codec(mel_bound())] pub struct LoneFreezeConsideration(PhantomData (A, Fx, Rx, D, Fp)>); impl< - A: 'static, - Fx: 'static + MutateFreeze, + A: 'static + Eq, + #[cfg(not(feature = "runtime-benchmarks"))] Fx: 'static + MutateFreeze, + #[cfg(feature = "runtime-benchmarks")] Fx: 'static + MutateFreeze + Mutate, Rx: 'static + Get, D: 'static + Convert, Fp: 'static, > Consideration for LoneFreezeConsideration { - fn new(who: &A, footprint: Fp) -> Result, DispatchError> { + fn new(who: &A, footprint: Fp) -> Result { ensure!(Fx::balance_frozen(&Rx::get(), who).is_zero(), DispatchError::Unavailable); - let new = D::convert(footprint); - if new.is_zero() { - Ok(None) - } else { - Fx::set_frozen(&Rx::get(), who, new, Polite).map(|_| Some(Self(PhantomData))) - } + Fx::set_frozen(&Rx::get(), who, D::convert(footprint), Polite).map(|_| Self(PhantomData)) } - fn update(self, who: &A, footprint: Fp) -> Result, DispatchError> { - let new = D::convert(footprint); - let _ = Fx::set_frozen(&Rx::get(), who, new, Polite)?; - if new.is_zero() { - Ok(None) - } else { - Ok(Some(Self(PhantomData))) - } + fn update(self, who: &A, footprint: Fp) -> Result { + Fx::set_frozen(&Rx::get(), who, D::convert(footprint), Polite).map(|_| Self(PhantomData)) } fn drop(self, who: &A) -> Result<(), DispatchError> { Fx::thaw(&Rx::get(), who).map(|_| ()) } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(who: &A, fp: Fp) { + let _ = Fx::mint_into(who, Fx::minimum_balance().saturating_add(D::convert(fp))); + } } /// Basic consideration method using a `fungible` balance placed on hold as the cost exacted for the @@ -372,30 +362,20 @@ impl< #[codec(mel_bound())] pub struct LoneHoldConsideration(PhantomData (A, Fx, Rx, D, Fp)>); impl< - A: 'static, - F: 'static + MutateHold, + A: 'static + Eq, + #[cfg(not(feature = "runtime-benchmarks"))] F: 'static + MutateHold, + #[cfg(feature = "runtime-benchmarks")] F: 'static + MutateHold + Mutate, R: 'static + Get, D: 'static + Convert, Fp: 'static, > Consideration for LoneHoldConsideration { - fn new(who: &A, footprint: Fp) -> Result, DispatchError> { + fn new(who: &A, footprint: Fp) -> Result { ensure!(F::balance_on_hold(&R::get(), who).is_zero(), DispatchError::Unavailable); - let new = D::convert(footprint); - if new.is_zero() { - Ok(None) - } else { - F::set_on_hold(&R::get(), who, new).map(|_| Some(Self(PhantomData))) - } + F::set_on_hold(&R::get(), who, D::convert(footprint)).map(|_| Self(PhantomData)) } - fn update(self, who: &A, footprint: Fp) -> Result, DispatchError> { - let new = D::convert(footprint); - let _ = F::set_on_hold(&R::get(), who, new)?; - if new.is_zero() { - Ok(None) - } else { - Ok(Some(Self(PhantomData))) - } + fn update(self, who: &A, footprint: Fp) -> Result { + F::set_on_hold(&R::get(), who, D::convert(footprint)).map(|_| Self(PhantomData)) } fn drop(self, who: &A) -> Result<(), DispatchError> { F::release_all(&R::get(), who, BestEffort).map(|_| ()) @@ -403,4 +383,8 @@ impl< fn burn(self, who: &A) { let _ = F::burn_all_held(&R::get(), who, BestEffort, Force); } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(who: &A, fp: Fp) { + let _ = F::mint_into(who, F::minimum_balance().saturating_add(D::convert(fp))); + } } From 05a8ba662f0afdd4a4e6e2f4e61e4ca2458d666c Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:55:29 +0300 Subject: [PATCH 109/382] Fix OurViewChange small race (#5356) Always queue OurViewChange event before we send view changes to our peers, because otherwise we risk the peers sending us a message that can be processed by our subsystems before OurViewChange. Normally, this is not really a problem because the latency of the ViewChange we send to our peers is way higher that our subsystem processing OurViewChange, however on testnets like versi where CPU is sometimes overcommitted this race gets triggered occasionally, so let's fix it by sending the messages in the right order. --------- Signed-off-by: Alexandru Gheorghe --- polkadot/node/network/bridge/src/rx/mod.rs | 30 +++++++++++----------- prdoc/pr_5356.prdoc | 18 +++++++++++++ 2 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 prdoc/pr_5356.prdoc diff --git a/polkadot/node/network/bridge/src/rx/mod.rs b/polkadot/node/network/bridge/src/rx/mod.rs index 56965ce6ba40..7745c42f78a1 100644 --- a/polkadot/node/network/bridge/src/rx/mod.rs +++ b/polkadot/node/network/bridge/src/rx/mod.rs @@ -962,6 +962,21 @@ fn update_our_view( ) }; + let our_view = OurView::new( + live_heads.iter().take(MAX_VIEW_HEADS).cloned().map(|a| (a.hash, a.span)), + finalized_number, + ); + + dispatch_validation_event_to_all_unbounded( + NetworkBridgeEvent::OurViewChange(our_view.clone()), + ctx.sender(), + ); + + dispatch_collation_event_to_all_unbounded( + NetworkBridgeEvent::OurViewChange(our_view), + ctx.sender(), + ); + let v1_validation_peers = filter_by_peer_version(&validation_peers, ValidationVersion::V1.into()); let v1_collation_peers = filter_by_peer_version(&collation_peers, CollationVersion::V1.into()); @@ -1007,21 +1022,6 @@ fn update_our_view( metrics, notification_sinks, ); - - let our_view = OurView::new( - live_heads.iter().take(MAX_VIEW_HEADS).cloned().map(|a| (a.hash, a.span)), - finalized_number, - ); - - dispatch_validation_event_to_all_unbounded( - NetworkBridgeEvent::OurViewChange(our_view.clone()), - ctx.sender(), - ); - - dispatch_collation_event_to_all_unbounded( - NetworkBridgeEvent::OurViewChange(our_view), - ctx.sender(), - ); } // Handle messages on a specific v1 peer-set. The peer is expected to be connected on that diff --git a/prdoc/pr_5356.prdoc b/prdoc/pr_5356.prdoc new file mode 100644 index 000000000000..a306be335440 --- /dev/null +++ b/prdoc/pr_5356.prdoc @@ -0,0 +1,18 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix OurViewChange small race + +doc: + - audience: Node Dev + description: | + Always queue OurViewChange event before we send view changes to our peers, because otherwise we risk + the peers sending us a message that can be processed by our subsystems before OurViewChange. + Normally, this is not really a problem because the latency of the ViewChange we send to our peers + is way higher than that of our subsystem processing OurViewChange, however on testnets like versi + where CPUs are sometimes overcommitted this race gets triggered occasionally, so let's fix it by + sending the messages in the right order. + +crates: + - name: polkadot-network-bridge + bump: minor From 81d8f0c0fa0be50dcd2c0291840a1b5b7ace8b98 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 14 Aug 2024 21:28:18 +0300 Subject: [PATCH 110/382] Beefy: add benchmarks for `report_fork_voting()` (#5188) Related to #4523 This PR adds benchmarks for `report_fork_voting()`. **Important: Even though the benchmarks are now available, we still use `Weight::MAX`. That's because I realized while working on this PR that there's still one missing piece. We should also check that the ancestry proof is optimal. I plan to do this in a future PR, hopefully the last one related to #4523.** --------- Co-authored-by: Branislav Kontur Co-authored-by: command-bot <> --- Cargo.lock | 925 ++++++++---------- polkadot/runtime/rococo/src/lib.rs | 82 +- polkadot/runtime/rococo/src/weights/mod.rs | 1 + .../rococo/src/weights/pallet_beefy_mmr.rs | 89 ++ polkadot/runtime/westend/src/lib.rs | 18 +- polkadot/runtime/westend/src/weights/mod.rs | 1 + .../westend/src/weights/pallet_beefy_mmr.rs | 89 ++ prdoc/pr_5188.prdoc | 32 + substrate/bin/node/runtime/src/lib.rs | 2 + substrate/frame/beefy-mmr/Cargo.toml | 3 + substrate/frame/beefy-mmr/src/benchmarking.rs | 129 +++ substrate/frame/beefy-mmr/src/lib.rs | 43 +- substrate/frame/beefy-mmr/src/mock.rs | 9 +- substrate/frame/beefy-mmr/src/tests.rs | 21 +- substrate/frame/beefy-mmr/src/weights.rs | 134 +++ substrate/frame/beefy/src/default_weights.rs | 5 - substrate/frame/beefy/src/lib.rs | 88 +- substrate/frame/beefy/src/mock.rs | 15 +- substrate/frame/beefy/src/tests.rs | 10 +- .../frame/merkle-mountain-range/src/lib.rs | 13 + .../merkle-mountain-range/src/mmr/mmr.rs | 46 +- .../merkle-mountain-range/src/mmr/storage.rs | 29 +- .../primitives/consensus/beefy/Cargo.toml | 2 + .../primitives/consensus/beefy/src/lib.rs | 10 + .../merkle-mountain-range/src/utils.rs | 11 +- 25 files changed, 1177 insertions(+), 630 deletions(-) create mode 100644 polkadot/runtime/rococo/src/weights/pallet_beefy_mmr.rs create mode 100644 polkadot/runtime/westend/src/weights/pallet_beefy_mmr.rs create mode 100644 prdoc/pr_5188.prdoc create mode 100644 substrate/frame/beefy-mmr/src/benchmarking.rs create mode 100644 substrate/frame/beefy-mmr/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index 67cfbb5968d5..9e3afe21c447 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,15 +42,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" -[[package]] -name = "aead" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" -dependencies = [ - "generic-array 0.14.7", -] - [[package]] name = "aead" version = "0.5.2" @@ -61,18 +52,6 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher 0.3.0", - "cpufeatures", - "opaque-debug 0.3.0", -] - [[package]] name = "aes" version = "0.8.3" @@ -84,31 +63,17 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "aes-gcm" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc3be92e19a7ef47457b8e6f90707e12b6ac5d20c6f3866584fa3be0787d839f" -dependencies = [ - "aead 0.4.3", - "aes 0.7.5", - "cipher 0.3.0", - "ctr 0.7.0", - "ghash 0.4.4", - "subtle 2.5.0", -] - [[package]] name = "aes-gcm" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ - "aead 0.5.2", - "aes 0.8.3", + "aead", + "aes", "cipher 0.4.4", - "ctr 0.9.2", - "ghash 0.5.0", + "ctr", + "ghash", "subtle 2.5.0", ] @@ -192,9 +157,9 @@ dependencies = [ "dunce", "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", "syn-solidity", "tiny-keccak", ] @@ -263,9 +228,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -319,9 +284,9 @@ dependencies = [ "include_dir", "itertools 0.10.5", "proc-macro-error", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -553,7 +518,7 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -655,7 +620,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -696,9 +661,9 @@ dependencies = [ [[package]] name = "array-bytes" -version = "6.2.3" +version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5dde061bd34119e902bbb2d9b90c5692635cf59fb91d582c2b68043f1b8293" +checksum = "6f840fb7195bcfc5e17ea40c26e5ce6d5b9ce5d584466e17703209657e459ae0" [[package]] name = "arrayref" @@ -759,7 +724,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", "synstructure 0.12.6", @@ -771,9 +736,9 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", "synstructure 0.13.1", ] @@ -783,7 +748,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -794,16 +759,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] name = "assert_cmd" -version = "2.0.15" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc65048dd435533bb1baf2ed9956b9a278fbfdcf90301b39ee117f06c0199d37" +checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" dependencies = [ "anstyle", "bstr", @@ -1214,14 +1179,14 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" dependencies = [ - "async-lock 3.3.0", + "async-lock 3.4.0", "cfg-if", "concurrent-queue", "futures-io", "futures-lite 2.3.0", "parking", "polling 3.4.0", - "rustix 0.38.25", + "rustix 0.38.21", "slab", "tracing", "windows-sys 0.52.0", @@ -1238,13 +1203,13 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 4.0.3", + "event-listener 5.2.0", "event-listener-strategy", - "pin-project-lite 0.2.12", + "pin-project-lite", ] [[package]] @@ -1298,7 +1263,7 @@ dependencies = [ "log", "memchr", "once_cell", - "pin-project-lite 0.2.12", + "pin-project-lite", "pin-utils", "slab", "wasm-bindgen-futures", @@ -1312,7 +1277,7 @@ checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", - "pin-project-lite 0.2.12", + "pin-project-lite", ] [[package]] @@ -1321,9 +1286,9 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -1338,9 +1303,9 @@ version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -1353,7 +1318,7 @@ dependencies = [ "futures-sink", "futures-util", "memchr", - "pin-project-lite 0.2.12", + "pin-project-lite", ] [[package]] @@ -1397,7 +1362,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -1543,12 +1508,12 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease 0.2.12", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "regex", "rustc-hash", "shlex", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -2625,18 +2590,6 @@ dependencies = [ "keystream", ] -[[package]] -name = "chacha20" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" -dependencies = [ - "cfg-if", - "cipher 0.3.0", - "cpufeatures", - "zeroize", -] - [[package]] name = "chacha20" version = "0.9.1" @@ -2650,14 +2603,14 @@ dependencies = [ [[package]] name = "chacha20poly1305" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ - "aead 0.4.3", - "chacha20 0.8.2", - "cipher 0.3.0", - "poly1305 0.7.2", + "aead", + "chacha20", + "cipher 0.4.4", + "poly1305", "zeroize", ] @@ -2762,15 +2715,6 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array 0.14.7", -] - [[package]] name = "cipher" version = "0.4.4" @@ -2779,6 +2723,7 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", + "zeroize", ] [[package]] @@ -2845,9 +2790,9 @@ dependencies = [ [[package]] name = "clap-num" -version = "1.1.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e063d263364859dc54fb064cedb7c122740cd4733644b14b176c097f51e8ab7" +checksum = "488557e97528174edaa2ee268b23a809e0c598213a4bbcb4f34575a46fda147e" dependencies = [ "num-traits", ] @@ -2882,7 +2827,7 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -2894,9 +2839,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -3082,7 +3027,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d51beaa537d73d2d1ff34ee70bc095f170420ab2ec5d687ecd3ec2b0d092514b" dependencies = [ "nom", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -3095,19 +3040,20 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" -version = "2.1.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ + "is-terminal", "lazy_static", "windows-sys 0.48.0", ] [[package]] name = "combine" -version = "4.6.7" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" dependencies = [ "bytes", "memchr", @@ -3666,9 +3612,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.2.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "c2b432c56615136f8dba245fed7ec3d5518c500a31108661067e61e72fe7e6bc" dependencies = [ "crc-catalog", ] @@ -3818,15 +3764,6 @@ dependencies = [ "subtle 2.5.0", ] -[[package]] -name = "ctr" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a232f92a03f37dd7d7dd2adc67166c77e9cd88de5b019b9a9eecfaeaf7bfd481" -dependencies = [ - "cipher 0.3.0", -] - [[package]] name = "ctr" version = "0.9.2" @@ -4213,9 +4150,9 @@ name = "cumulus-pallet-parachain-system-proc-macro" version = "0.6.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -4712,9 +4649,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.73+curl-8.8.0" +version = "0.4.72+curl-8.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "450ab250ecf17227c39afb9a2dd9261dc0035cb80f2612472fc0c4aac2dcb84d" +checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" dependencies = [ "cc", "libc", @@ -4748,9 +4685,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -4787,10 +4724,10 @@ dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "scratch", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -4805,9 +4742,9 @@ version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50c49547d73ba8dcfd4ad7325d64c6d5391ff4224d498fc39a6f3f49825a530d" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -4898,9 +4835,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] [[package]] name = "derivative" @@ -4908,7 +4848,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -4919,9 +4859,9 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -4930,9 +4870,9 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -4942,7 +4882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "rustc_version 0.4.0", "syn 1.0.109", @@ -5038,9 +4978,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -5098,10 +5038,10 @@ dependencies = [ "common-path", "derive-syn-parse", "once_cell", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "regex", - "syn 2.0.58", + "syn 2.0.61", "termcolor", "toml 0.8.8", "walkdir", @@ -5147,7 +5087,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -5293,7 +5233,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -5305,9 +5245,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -5325,9 +5265,9 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -5336,9 +5276,9 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -5414,12 +5354,11 @@ dependencies = [ [[package]] name = "erased-serde" -version = "0.4.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3" dependencies = [ "serde", - "typeid", ] [[package]] @@ -5502,23 +5441,23 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "4.0.3" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" dependencies = [ "concurrent-queue", "parking", - "pin-project-lite 0.2.12", + "pin-project-lite", ] [[package]] name = "event-listener-strategy" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 4.0.3", - "pin-project-lite 0.2.12", + "event-listener 5.2.0", + "pin-project-lite", ] [[package]] @@ -5540,9 +5479,9 @@ dependencies = [ "file-guard", "fs-err", "prettyplease 0.2.12", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -5612,9 +5551,9 @@ dependencies = [ "expander", "indexmap 2.2.3", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -5815,9 +5754,9 @@ dependencies = [ [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -5944,11 +5883,11 @@ dependencies = [ "frame-support", "parity-scale-codec", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "scale-info", "sp-arithmetic", - "syn 2.0.58", + "syn 2.0.61", "trybuild", ] @@ -6139,7 +6078,7 @@ dependencies = [ "parity-scale-codec", "pretty_assertions", "proc-macro-warning 1.0.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "regex", "scale-info", @@ -6149,7 +6088,7 @@ dependencies = [ "sp-metadata-ir", "sp-runtime", "static_assertions", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -6158,18 +6097,18 @@ version = "10.0.0" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] name = "frame-support-procedural-tools-derive" version = "11.0.0" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -6312,7 +6251,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29f9df8a11882c4e3335eb2d18a0137c505d9ca927470b0cac9c6f0ae07d28f7" dependencies = [ - "rustix 0.38.25", + "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -6398,7 +6337,7 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite 0.2.12", + "pin-project-lite", "waker-fn", ] @@ -6409,7 +6348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ "futures-core", - "pin-project-lite 0.2.12", + "pin-project-lite", ] [[package]] @@ -6418,9 +6357,9 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -6464,7 +6403,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.12", + "pin-project-lite", "pin-utils", "slab", ] @@ -6542,16 +6481,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "ghash" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" -dependencies = [ - "opaque-debug 0.3.0", - "polyval 0.5.3", -] - [[package]] name = "ghash" version = "0.5.0" @@ -6559,7 +6488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" dependencies = [ "opaque-debug 0.3.0", - "polyval 0.6.1", + "polyval", ] [[package]] @@ -6677,9 +6606,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -6830,9 +6759,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" +checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" [[package]] name = "hex-literal" @@ -6932,7 +6861,7 @@ checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http 0.2.9", - "pin-project-lite 0.2.12", + "pin-project-lite", ] [[package]] @@ -6955,7 +6884,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.0", - "pin-project-lite 0.2.12", + "pin-project-lite", ] [[package]] @@ -6978,21 +6907,21 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2 0.3.24", + "h2 0.3.26", "http 0.2.9", "http-body 0.4.5", "httparse", "httpdate", "itoa", - "pin-project-lite 0.2.12", + "pin-project-lite", "socket2 0.5.7", "tokio", "tower-service", @@ -7015,7 +6944,7 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project-lite 0.2.12", + "pin-project-lite", "smallvec", "tokio", "want", @@ -7029,7 +6958,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.9", - "hyper 0.14.30", + "hyper 0.14.29", "log", "rustls 0.21.7", "rustls-native-certs 0.6.3", @@ -7067,7 +6996,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "hyper 1.3.1", - "pin-project-lite 0.2.12", + "pin-project-lite", "socket2 0.5.7", "tokio", "tower", @@ -7119,6 +7048,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "if-addrs" version = "0.10.2" @@ -7159,7 +7098,7 @@ dependencies = [ "bytes", "futures", "http 0.2.9", - "hyper 0.14.30", + "hyper 0.14.29", "log", "rand", "tokio", @@ -7211,7 +7150,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -7231,7 +7170,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", ] @@ -7356,7 +7295,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.2", - "rustix 0.38.25", + "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -7575,9 +7514,9 @@ checksum = "7895f186d5921065d96e16bd795e5ca89ac8356ec423fafc6e3d7cf8ec11aee4" dependencies = [ "heck 0.5.0", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -7826,9 +7765,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libnghttp2-sys" -version = "0.1.10+1.61.0" +version = "0.1.9+1.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "959c25552127d2e1fa72f0e52548ec04fc386e827ba71a7bd01db46a447dc135" +checksum = "b57e858af2798e167e709b9d969325b6d8e9d50232fcbc494d7d54f976854a64" dependencies = [ "cc", "libc", @@ -8163,9 +8102,9 @@ checksum = "c4d5ec2a3df00c7836d7696c136274c9c59705bac69133253696a6c932cd1d74" dependencies = [ "heck 0.4.1", "proc-macro-warning 0.4.2", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -8236,9 +8175,9 @@ dependencies = [ [[package]] name = "libp2p-websocket" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3facf0691bab65f571bc97c6c65ffa836248ca631d631b7691ac91deb7fceb5f" +checksum = "004ee9c4a4631435169aee6aad2f62e3984dc031c43b6d29731e8e82a016c538" dependencies = [ "either", "futures", @@ -8247,9 +8186,10 @@ dependencies = [ "libp2p-identity", "log", "parking_lot 0.12.3", - "quicksink", + "pin-project-lite", "rw-stream-sink", - "soketto 0.7.1", + "soketto 0.8.0", + "thiserror", "url", "webpki-roots 0.25.2", ] @@ -8389,9 +8329,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lioness" @@ -8452,7 +8392,7 @@ dependencies = [ "rand", "rcgen", "ring 0.16.20", - "rustls 0.20.8", + "rustls 0.20.9", "serde", "sha2 0.10.8", "simple-dns", @@ -8578,7 +8518,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -8590,9 +8530,9 @@ dependencies = [ "const-random", "derive-syn-parse", "macro_magic_core_macros", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -8601,9 +8541,9 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -8614,7 +8554,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -8665,9 +8605,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memfd" @@ -8976,7 +8916,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -8988,9 +8928,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" dependencies = [ "cfg-if", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -9100,7 +9040,7 @@ checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro-error", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", "synstructure 0.12.6", @@ -9148,7 +9088,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -9237,9 +9177,9 @@ dependencies = [ [[package]] name = "network-interface" -version = "1.1.4" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a43439bf756eed340bdf8feba761e2d50c7d47175d87545cd5cbe4a137c4d1" +checksum = "ae72fd9dbd7f55dda80c00d66acc3b2130436fcba9ea89118fc508eaae48dfb0" dependencies = [ "cc", "libc", @@ -9528,15 +9468,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -9701,9 +9647,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -9714,9 +9660,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.3.1+3.3.1" +version = "300.2.3+3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7259953d42a81bf137fbbd73bd30a8e1914d6dce43c2b90ed575783a22608b91" +checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" dependencies = [ "cc", ] @@ -9768,7 +9714,7 @@ dependencies = [ "itertools 0.11.0", "petgraph", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -10135,6 +10081,7 @@ version = "28.0.0" dependencies = [ "array-bytes", "binary-merkle-tree", + "frame-benchmarking", "frame-support", "frame-system", "log", @@ -10471,9 +10418,9 @@ dependencies = [ name = "pallet-contracts-proc-macro" version = "18.0.0" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -10639,7 +10586,7 @@ dependencies = [ "sp-npos-elections", "sp-runtime", "sp-tracing 16.0.0", - "strum 0.26.3", + "strum 0.26.2", ] [[package]] @@ -11705,10 +11652,10 @@ name = "pallet-staking-reward-curve" version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "sp-runtime", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -12350,7 +12297,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -12379,7 +12326,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "syn 1.0.109", "synstructure 0.12.6", ] @@ -12765,9 +12712,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" @@ -12797,9 +12744,9 @@ checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -12838,22 +12785,16 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] name = "pin-project-lite" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" - -[[package]] -name = "pin-project-lite" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -13755,7 +13696,7 @@ dependencies = [ "sc-network", "sc-network-types", "sp-runtime", - "strum 0.26.3", + "strum 0.26.2", "thiserror", "tracing-gum", ] @@ -14955,7 +14896,7 @@ dependencies = [ "sp-runtime", "sp-timestamp", "sp-tracing 16.0.0", - "strum 0.26.3", + "strum 0.26.2", "substrate-prometheus-endpoint", "tokio", "tracing-gum", @@ -15191,9 +15132,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ "polkavm-common", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -15203,7 +15144,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -15239,7 +15180,7 @@ dependencies = [ "concurrent-queue", "libc", "log", - "pin-project-lite 0.2.12", + "pin-project-lite", "windows-sys 0.48.0", ] @@ -15251,23 +15192,12 @@ checksum = "30054e72317ab98eddd8561db0f6524df3367636884b7b21b703e4b280a84a14" dependencies = [ "cfg-if", "concurrent-queue", - "pin-project-lite 0.2.12", - "rustix 0.38.25", + "pin-project-lite", + "rustix 0.38.21", "tracing", "windows-sys 0.52.0", ] -[[package]] -name = "poly1305" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" -dependencies = [ - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash 0.4.0", -] - [[package]] name = "poly1305" version = "0.8.0" @@ -15276,19 +15206,7 @@ checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", "opaque-debug 0.3.0", - "universal-hash 0.5.1", -] - -[[package]] -name = "polyval" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash 0.4.0", + "universal-hash", ] [[package]] @@ -15300,7 +15218,7 @@ dependencies = [ "cfg-if", "cpufeatures", "opaque-debug 0.3.0", - "universal-hash 0.5.1", + "universal-hash", ] [[package]] @@ -15318,6 +15236,12 @@ dependencies = [ "rand", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "pprof" version = "0.12.1" @@ -15402,7 +15326,7 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "syn 1.0.109", ] @@ -15412,8 +15336,8 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ - "proc-macro2 1.0.75", - "syn 2.0.58", + "proc-macro2 1.0.82", + "syn 2.0.61", ] [[package]] @@ -15473,7 +15397,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", "version_check", @@ -15485,7 +15409,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "version_check", ] @@ -15502,9 +15426,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -15513,9 +15437,9 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b698b0b09d40e9b7c1a47b132d66a8b54bcd20583d9b6d06e4535e383b4405c" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -15529,9 +15453,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.75" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -15548,7 +15472,7 @@ dependencies = [ "hex", "lazy_static", "procfs-core", - "rustix 0.38.25", + "rustix 0.38.21", ] [[package]] @@ -15594,9 +15518,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -15675,9 +15599,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.12.6" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" +checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" dependencies = [ "bytes", "heck 0.5.0", @@ -15688,9 +15612,9 @@ dependencies = [ "petgraph", "prettyplease 0.2.12", "prost 0.12.6", - "prost-types 0.12.6", + "prost-types 0.12.4", "regex", - "syn 2.0.58", + "syn 2.0.61", "tempfile", ] @@ -15702,7 +15626,7 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -15715,9 +15639,9 @@ checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.11.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -15731,9 +15655,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.6" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" dependencies = [ "prost 0.12.6", ] @@ -15843,17 +15767,6 @@ dependencies = [ "rand", ] -[[package]] -name = "quicksink" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77de3c815e5a160b1539c6592796801df2043ae35e123b46d73380cfa57af858" -dependencies = [ - "futures-core", - "futures-sink", - "pin-project-lite 0.1.12", -] - [[package]] name = "quinn" version = "0.9.4" @@ -15861,11 +15774,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e8b432585672228923edbbf64b8b12c14e1112f62e88737655b4a083dbcd78e" dependencies = [ "bytes", - "pin-project-lite 0.2.12", - "quinn-proto 0.9.5", + "pin-project-lite", + "quinn-proto 0.9.6", "quinn-udp 0.3.2", "rustc-hash", - "rustls 0.20.8", + "rustls 0.20.9", "thiserror", "tokio", "tracing", @@ -15880,7 +15793,7 @@ checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" dependencies = [ "bytes", "futures-io", - "pin-project-lite 0.2.12", + "pin-project-lite", "quinn-proto 0.10.6", "quinn-udp 0.4.1", "rustc-hash", @@ -15892,15 +15805,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c956be1b23f4261676aed05a0046e204e8a6836e50203902683a718af0797989" +checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863" dependencies = [ "bytes", "rand", "ring 0.16.20", "rustc-hash", - "rustls 0.20.8", + "rustls 0.20.9", "slab", "thiserror", "tinyvec", @@ -15932,7 +15845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4" dependencies = [ "libc", - "quinn-proto 0.9.5", + "quinn-proto 0.9.6", "socket2 0.4.9", "tracing", "windows-sys 0.42.0", @@ -15966,7 +15879,7 @@ version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", ] [[package]] @@ -16165,9 +16078,9 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -16343,10 +16256,10 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.3.24", + "h2 0.3.26", "http 0.2.9", "http-body 0.4.5", - "hyper 0.14.30", + "hyper 0.14.29", "hyper-rustls 0.24.2", "ipnet", "js-sys", @@ -16354,7 +16267,7 @@ dependencies = [ "mime", "once_cell", "percent-encoding", - "pin-project-lite 0.2.12", + "pin-project-lite", "rustls 0.21.7", "rustls-pemfile 1.0.3", "serde", @@ -16715,12 +16628,12 @@ checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" dependencies = [ "cfg-if", "glob", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "regex", "relative-path", "rustc_version 0.4.0", - "syn 2.0.58", + "syn 2.0.61", "unicode-ident", ] @@ -16863,22 +16776,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.25" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.11", + "linux-raw-sys 0.4.10", "windows-sys 0.48.0", ] [[package]] name = "rustls" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "ring 0.16.20", "sct", @@ -16964,9 +16877,9 @@ checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-platform-verifier" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e3beb939bcd33c269f4bf946cc829fcd336370267c4a927ac0399c84a3151a1" +checksum = "b5f0d26fa1ce3c790f9590868f0109289a044acb954525f933e2aa3b871c157d" dependencies = [ "core-foundation", "core-foundation-sys", @@ -17107,7 +17020,7 @@ dependencies = [ "multihash 0.19.1", "parity-scale-codec", "prost 0.12.6", - "prost-build 0.12.6", + "prost-build 0.12.4", "quickcheck", "rand", "sc-client-api", @@ -17203,9 +17116,9 @@ name = "sc-chain-spec-derive" version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -17853,7 +17766,7 @@ dependencies = [ "partial_sort", "pin-project", "prost 0.12.6", - "prost-build 0.12.6", + "prost-build 0.12.4", "rand", "sc-block-builder", "sc-client-api", @@ -17898,7 +17811,7 @@ dependencies = [ "futures", "libp2p-identity", "parity-scale-codec", - "prost-build 0.12.6", + "prost-build 0.12.4", "sc-consensus", "sc-network-types", "sp-consensus", @@ -17940,7 +17853,7 @@ dependencies = [ "log", "parity-scale-codec", "prost 0.12.6", - "prost-build 0.12.6", + "prost-build 0.12.4", "sc-client-api", "sc-network", "sc-network-types", @@ -17984,7 +17897,7 @@ dependencies = [ "mockall 0.11.4", "parity-scale-codec", "prost 0.12.6", - "prost-build 0.12.6", + "prost-build 0.12.4", "quickcheck", "sc-block-builder", "sc-client-api", @@ -18086,7 +17999,7 @@ dependencies = [ "fnv", "futures", "futures-timer", - "hyper 0.14.30", + "hyper 0.14.29", "hyper-rustls 0.24.2", "lazy_static", "log", @@ -18498,9 +18411,9 @@ name = "sc-tracing-proc-macro" version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -18612,7 +18525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -18650,7 +18563,7 @@ version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "serde_derive_internals", "syn 1.0.109", @@ -18689,7 +18602,7 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de18f6d8ba0aad7045f5feae07ec29899c1112584a38509a84ad7b04451eaa0" dependencies = [ - "aead 0.5.2", + "aead", "arrayref", "arrayvec 0.7.4", "curve25519-dalek", @@ -18771,18 +18684,18 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.28.0" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" dependencies = [ "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e67c467c38fd24bd5499dc9a18183b31575c12ee549197e3e20d57aa4fe3b7" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" dependencies = [ "cc", ] @@ -18950,9 +18863,9 @@ version = "1.0.206" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -18961,7 +18874,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -19011,9 +18924,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.32" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap 2.2.3", "itoa", @@ -19052,9 +18965,9 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -19095,9 +19008,9 @@ dependencies = [ [[package]] name = "sha1-asm" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "286acebaf8b67c1130aedffad26f594eff0c1292389158135327d2e23aed582b" +checksum = "2ba6947745e7f86be3b8af00b7355857085dbdf8901393c89514510eb61f4e21" dependencies = [ "cc", ] @@ -19345,7 +19258,7 @@ dependencies = [ "bip39", "blake2-rfc", "bs58 0.5.1", - "chacha20 0.9.1", + "chacha20", "crossbeam-queue", "derive_more", "ed25519-zebra", @@ -19367,7 +19280,7 @@ dependencies = [ "num-traits", "pbkdf2", "pin-project", - "poly1305 0.8.0", + "poly1305", "rand", "rand_chacha", "ruzstd", @@ -19430,16 +19343,16 @@ checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" [[package]] name = "snow" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9d1425eb528a21de2755c75af4c9b5d57f50a0d4c3b7f1828a4cd03f8ba155" +checksum = "850948bee068e713b8ab860fe1adc4d109676ab4c3b621fd8147f06b261f2f85" dependencies = [ - "aes-gcm 0.9.2", + "aes-gcm", "blake2 0.10.6", "chacha20poly1305", "curve25519-dalek", "rand_core", - "ring 0.16.20", + "ring 0.17.7", "rustc_version 0.4.0", "sha2 0.10.8", "subtle 2.5.0", @@ -19928,9 +19841,9 @@ dependencies = [ "blake2 0.10.6", "expander", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -20126,7 +20039,8 @@ dependencies = [ "sp-keystore", "sp-mmr-primitives", "sp-runtime", - "strum 0.26.3", + "sp-weights", + "strum 0.26.2", "w3f-bls", ] @@ -20257,7 +20171,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.4.1" -source = "git+https://github.com/paritytech/polkadot-sdk#838a534da874cf6071fba1df07643c6c5b033ae0" +source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -20314,7 +20228,7 @@ version = "0.1.0" dependencies = [ "quote 1.0.36", "sp-crypto-hashing", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -20330,18 +20244,18 @@ name = "sp-debug-derive" version = "8.0.0" source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] name = "sp-debug-derive" version = "14.0.0" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -20419,7 +20333,7 @@ version = "31.0.0" dependencies = [ "sp-core", "sp-runtime", - "strum 0.26.3", + "strum 0.26.2", ] [[package]] @@ -20607,13 +20521,13 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#838a534da874cf6071fba1df07643c6c5b033ae0" +source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ "Inflector", "proc-macro-crate 1.3.1", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -20623,9 +20537,9 @@ dependencies = [ "Inflector", "expander", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -20718,7 +20632,7 @@ dependencies = [ name = "sp-statement-store" version = "10.0.0" dependencies = [ - "aes-gcm 0.10.3", + "aes-gcm", "curve25519-dalek", "ed25519-dalek", "hkdf", @@ -20884,10 +20798,10 @@ name = "sp-version-proc-macro" version = "13.0.0" dependencies = [ "parity-scale-codec", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "sp-version", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -20969,7 +20883,7 @@ checksum = "5e6915280e2d0db8911e5032a5c275571af6bdded2916abd691a659be25d3439" dependencies = [ "Inflector", "num-format", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "serde", "serde_json", @@ -20994,7 +20908,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f07d54c4d01a1713eb363b55ba51595da15f6f1211435b71466460da022aa140" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -21187,7 +21101,7 @@ checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" dependencies = [ "cfg_aliases", "memchr", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -21260,7 +21174,7 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -21282,11 +21196,11 @@ checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" [[package]] name = "strum" -version = "0.26.3" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" dependencies = [ - "strum_macros 0.26.4", + "strum_macros 0.26.2", ] [[package]] @@ -21296,7 +21210,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "rustversion", "syn 1.0.109", @@ -21309,23 +21223,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "rustversion", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] name = "strum_macros" -version = "0.26.4" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ - "heck 0.5.0", - "proc-macro2 1.0.75", + "heck 0.4.1", + "proc-macro2 1.0.82", "quote 1.0.36", "rustversion", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -21463,7 +21377,7 @@ dependencies = [ "sp-runtime", "sp-trie", "structopt", - "strum 0.26.3", + "strum 0.26.2", "thiserror", ] @@ -21639,7 +21553,7 @@ dependencies = [ "sp-maybe-compressed-blob", "sp-tracing 16.0.0", "sp-version", - "strum 0.26.3", + "strum 0.26.2", "tempfile", "toml 0.8.8", "walkdir", @@ -21772,18 +21686,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.58" +version = "2.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "unicode-ident", ] @@ -21795,9 +21709,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b837ef12ab88835251726eb12237655e61ec8dc8a280085d1961cdc3dfd047" dependencies = [ "paste", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -21806,7 +21720,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", "unicode-xid 0.2.4", @@ -21818,16 +21732,16 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] name = "sysinfo" -version = "0.30.12" +version = "0.30.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae" +checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2" dependencies = [ "cfg-if", "core-foundation-sys", @@ -21891,7 +21805,7 @@ dependencies = [ "cfg-if", "fastrand 2.1.0", "redox_syscall 0.4.1", - "rustix 0.38.25", + "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -21910,7 +21824,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "rustix 0.38.25", + "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -21937,9 +21851,9 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -22101,7 +22015,7 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "syn 1.0.109", ] @@ -22112,9 +22026,9 @@ version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -22188,14 +22102,16 @@ dependencies = [ [[package]] name = "time" -version = "0.3.27" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb39ee79a6d8de55f48f2293a830e040392f1c5f16e336bdd1788cd0aadce07" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", + "powerfmt", "serde", "time-core", "time-macros", @@ -22203,16 +22119,17 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.13" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733d258752e9303d392b94b75230d07b0b9c489350c69b851fc6c065fde3e8f9" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -22252,9 +22169,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -22262,7 +22179,7 @@ dependencies = [ "mio", "num_cpus", "parking_lot 0.12.3", - "pin-project-lite 0.2.12", + "pin-project-lite", "signal-hook-registry", "socket2 0.5.7", "tokio-macros", @@ -22271,13 +22188,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -22319,7 +22236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", - "pin-project-lite 0.2.12", + "pin-project-lite", "tokio", "tokio-util", ] @@ -22362,7 +22279,7 @@ dependencies = [ "futures-core", "futures-io", "futures-sink", - "pin-project-lite 0.2.12", + "pin-project-lite", "tokio", ] @@ -22429,7 +22346,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project", - "pin-project-lite 0.2.12", + "pin-project-lite", "tokio", "tower-layer", "tower-service", @@ -22447,7 +22364,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "http-body-util", - "pin-project-lite 0.2.12", + "pin-project-lite", "tower-layer", "tower-service", ] @@ -22471,7 +22388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", - "pin-project-lite 0.2.12", + "pin-project-lite", "tracing-attributes", "tracing-core", ] @@ -22482,9 +22399,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -22524,9 +22441,9 @@ dependencies = [ "assert_matches", "expander", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -22622,9 +22539,9 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.29.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c992b4f40c234a074d48a757efeabb1a6be88af84c0c23f7ca158950cb0ae7f" +checksum = "65ed83be775d85ebb0e272914fff6462c39b3ddd6dc67b5c1c41271aad280c69" dependencies = [ "hash-db", "log", @@ -22789,12 +22706,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "typeid" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" - [[package]] name = "typenum" version = "1.16.0" @@ -22870,16 +22781,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" -[[package]] -name = "universal-hash" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" -dependencies = [ - "generic-array 0.14.7", - "subtle 2.5.0", -] - [[package]] name = "universal-hash" version = "0.5.1" @@ -22892,9 +22793,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "unsigned-varint" @@ -22932,12 +22833,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna 0.4.0", + "idna 0.5.0", "percent-encoding", ] @@ -22967,9 +22868,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.9.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" +checksum = "8fec26a25bd6fca441cdd0f769fd7f891bae119f996de31f86a5eddccef54c1d" dependencies = [ "value-bag-serde1", "value-bag-sval2", @@ -22977,9 +22878,9 @@ dependencies = [ [[package]] name = "value-bag-serde1" -version = "1.9.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccacf50c5cb077a9abb723c5bcb5e0754c1a433f1e1de89edc328e2760b6328b" +checksum = "ead5b693d906686203f19a49e88c477fb8c15798b68cf72f60b4b5521b4ad891" dependencies = [ "erased-serde", "serde", @@ -22988,9 +22889,9 @@ dependencies = [ [[package]] name = "value-bag-sval2" -version = "1.9.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1785bae486022dfb9703915d42287dcb284c1ee37bd1080eeba78cc04721285b" +checksum = "3b9d0f4a816370c3a0d7d82d603b62198af17675b12fe5e91de6b47ceb505882" dependencies = [ "sval", "sval_buffer", @@ -23091,9 +22992,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "serde", @@ -23103,16 +23004,16 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", "wasm-bindgen-shared", ] @@ -23130,9 +23031,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote 1.0.36", "wasm-bindgen-macro-support", @@ -23140,22 +23041,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-bindgen-test" @@ -23177,7 +23078,7 @@ version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", ] @@ -24243,10 +24144,10 @@ name = "xcm-procedural" version = "7.0.0" dependencies = [ "Inflector", - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", "staging-xcm", - "syn 2.0.58", + "syn 2.0.61", "trybuild", ] @@ -24409,9 +24310,9 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] @@ -24429,9 +24330,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.82", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.61", ] [[package]] diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 2f23b889916b..30b915d32deb 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -1335,6 +1335,7 @@ impl pallet_beefy_mmr::Config for Runtime { type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; type LeafExtra = H256; type BeefyDataProvider = ParaHeadsRootProvider; + type WeightInfo = weights::pallet_beefy_mmr::WeightInfo; } impl paras_sudo_wrapper::Config for Runtime {} @@ -1629,47 +1630,45 @@ pub mod migrations { /// Unreleased migrations. Add new ones here: pub type Unreleased = ( - pallet_society::migrations::MigrateToV2, - parachains_configuration::migration::v7::MigrateToV7, - assigned_slots::migration::v1::MigrateToV1, - parachains_scheduler::migration::MigrateV1ToV2, - parachains_configuration::migration::v8::MigrateToV8, - parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV1, - pallet_referenda::migration::v1::MigrateV0ToV1, - pallet_referenda::migration::v1::MigrateV0ToV1, - - // Unlock & unreserve Gov1 funds - - pallet_elections_phragmen::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, - pallet_democracy::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, - pallet_tips::migrations::unreserve_deposits::UnreserveDeposits, - - // Delete all Gov v1 pallet storage key/values. - - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - - pallet_grandpa::migrations::MigrateV4ToV5, - parachains_configuration::migration::v10::MigrateToV10, - - // Migrate Identity pallet for Usernames - pallet_identity::migration::versioned::V0ToV1, - parachains_configuration::migration::v11::MigrateToV11, - // This needs to come after the `parachains_configuration` above as we are reading the configuration. - coretime::migration::MigrateToCoretime, - parachains_configuration::migration::v12::MigrateToV12, - parachains_on_demand::migration::MigrateV0ToV1, - - // permanent - pallet_xcm::migration::MigrateToLatestXcmVersion, - - parachains_inclusion::migration::MigrateToV1, - ); + pallet_society::migrations::MigrateToV2, + parachains_configuration::migration::v7::MigrateToV7, + assigned_slots::migration::v1::MigrateToV1, + parachains_scheduler::migration::MigrateV1ToV2, + parachains_configuration::migration::v8::MigrateToV8, + parachains_configuration::migration::v9::MigrateToV9, + paras_registrar::migration::MigrateToV1, + pallet_referenda::migration::v1::MigrateV0ToV1, + pallet_referenda::migration::v1::MigrateV0ToV1, + + // Unlock & unreserve Gov1 funds + + pallet_elections_phragmen::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, + pallet_democracy::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, + pallet_tips::migrations::unreserve_deposits::UnreserveDeposits, + + // Delete all Gov v1 pallet storage key/values. + + frame_support::migrations::RemovePallet::DbWeight>, + frame_support::migrations::RemovePallet::DbWeight>, + frame_support::migrations::RemovePallet::DbWeight>, + frame_support::migrations::RemovePallet::DbWeight>, + frame_support::migrations::RemovePallet::DbWeight>, + frame_support::migrations::RemovePallet::DbWeight>, + pallet_grandpa::migrations::MigrateV4ToV5, + parachains_configuration::migration::v10::MigrateToV10, + + // Migrate Identity pallet for Usernames + pallet_identity::migration::versioned::V0ToV1, + parachains_configuration::migration::v11::MigrateToV11, + // This needs to come after the `parachains_configuration` above as we are reading the configuration. + coretime::migration::MigrateToCoretime, + parachains_configuration::migration::v12::MigrateToV12, + parachains_on_demand::migration::MigrateV0ToV1, + + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, + parachains_inclusion::migration::MigrateToV1, + ); } /// Executive: handles dispatch to the various modules. @@ -1735,6 +1734,7 @@ mod benches { // Substrate [pallet_balances, Balances] [pallet_balances, NisCounterpartBalances] + [pallet_beefy_mmr, MmrLeaf] [frame_benchmarking::baseline, Baseline::] [pallet_bounties, Bounties] [pallet_child_bounties, ChildBounties] diff --git a/polkadot/runtime/rococo/src/weights/mod.rs b/polkadot/runtime/rococo/src/weights/mod.rs index 0512a393a6c4..cd3f4689f562 100644 --- a/polkadot/runtime/rococo/src/weights/mod.rs +++ b/polkadot/runtime/rococo/src/weights/mod.rs @@ -19,6 +19,7 @@ pub mod frame_system; pub mod pallet_asset_rate; pub mod pallet_balances_balances; pub mod pallet_balances_nis_counterpart_balances; +pub mod pallet_beefy_mmr; pub mod pallet_bounties; pub mod pallet_child_bounties; pub mod pallet_conviction_voting; diff --git a/polkadot/runtime/rococo/src/weights/pallet_beefy_mmr.rs b/polkadot/runtime/rococo/src/weights/pallet_beefy_mmr.rs new file mode 100644 index 000000000000..317c9149ec6c --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_beefy_mmr.rs @@ -0,0 +1,89 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Autogenerated weights for `pallet_beefy_mmr` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_beefy_mmr +// --chain=rococo-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_beefy_mmr`. +pub struct WeightInfo(PhantomData); +impl pallet_beefy_mmr::WeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn extract_validation_context() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 7_116_000 picoseconds. + Weight::from_parts(7_343_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Mmr::Nodes` (r:1 w:0) + /// Proof: `Mmr::Nodes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + fn read_peak() -> Weight { + // Proof Size summary in bytes: + // Measured: `234` + // Estimated: `3505` + // Minimum execution time: 5_652_000 picoseconds. + Weight::from_parts(5_963_000, 0) + .saturating_add(Weight::from_parts(0, 3505)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Mmr::RootHash` (r:1 w:0) + /// Proof: `Mmr::RootHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `Mmr::NumberOfLeaves` (r:1 w:0) + /// Proof: `Mmr::NumberOfLeaves` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 512]`. + fn n_items_proof_is_non_canonical(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `226` + // Estimated: `1517` + // Minimum execution time: 11_953_000 picoseconds. + Weight::from_parts(15_978_891, 0) + .saturating_add(Weight::from_parts(0, 1517)) + // Standard Error: 1_780 + .saturating_add(Weight::from_parts(1_480_582, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2)) + } +} diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 838ba17e5613..aa446b03368d 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -461,6 +461,7 @@ impl pallet_beefy_mmr::Config for Runtime { type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; type LeafExtra = H256; type BeefyDataProvider = ParaHeadsRootProvider; + type WeightInfo = weights::pallet_beefy_mmr::WeightInfo; } parameter_types! { @@ -601,20 +602,20 @@ impl pallet_election_provider_multi_phase::MinerConfig for Runtime { type MaxWeight = OffchainSolutionWeightLimit; type Solution = NposCompactSolution16; type MaxVotesPerVoter = < - ::DataProvider - as - frame_election_provider_support::ElectionDataProvider - >::MaxVotesPerVoter; + ::DataProvider + as + frame_election_provider_support::ElectionDataProvider + >::MaxVotesPerVoter; type MaxWinners = MaxActiveValidators; // The unsigned submissions have to respect the weight of the submit_unsigned call, thus their // weight estimate function is wired to this call's weight. fn solution_weight(v: u32, t: u32, a: u32, d: u32) -> Weight { < - ::WeightInfo - as - pallet_election_provider_multi_phase::WeightInfo - >::submit_unsigned(v, t, a, d) + ::WeightInfo + as + pallet_election_provider_multi_phase::WeightInfo + >::submit_unsigned(v, t, a, d) } } @@ -1847,6 +1848,7 @@ mod benches { // Substrate [pallet_bags_list, VoterList] [pallet_balances, Balances] + [pallet_beefy_mmr, BeefyMmrLeaf] [pallet_conviction_voting, ConvictionVoting] [pallet_election_provider_multi_phase, ElectionProviderMultiPhase] [frame_election_provider_support, ElectionProviderBench::] diff --git a/polkadot/runtime/westend/src/weights/mod.rs b/polkadot/runtime/westend/src/weights/mod.rs index 2248e421e639..cb6e2c85e363 100644 --- a/polkadot/runtime/westend/src/weights/mod.rs +++ b/polkadot/runtime/westend/src/weights/mod.rs @@ -20,6 +20,7 @@ pub mod frame_system; pub mod pallet_asset_rate; pub mod pallet_bags_list; pub mod pallet_balances; +pub mod pallet_beefy_mmr; pub mod pallet_conviction_voting; pub mod pallet_election_provider_multi_phase; pub mod pallet_fast_unstake; diff --git a/polkadot/runtime/westend/src/weights/pallet_beefy_mmr.rs b/polkadot/runtime/westend/src/weights/pallet_beefy_mmr.rs new file mode 100644 index 000000000000..5be207e3fcff --- /dev/null +++ b/polkadot/runtime/westend/src/weights/pallet_beefy_mmr.rs @@ -0,0 +1,89 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Autogenerated weights for `pallet_beefy_mmr` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_beefy_mmr +// --chain=westend-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/westend/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_beefy_mmr`. +pub struct WeightInfo(PhantomData); +impl pallet_beefy_mmr::WeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn extract_validation_context() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 7_850_000 picoseconds. + Weight::from_parts(8_169_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Mmr::Nodes` (r:1 w:0) + /// Proof: `Mmr::Nodes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + fn read_peak() -> Weight { + // Proof Size summary in bytes: + // Measured: `201` + // Estimated: `3505` + // Minimum execution time: 6_852_000 picoseconds. + Weight::from_parts(7_448_000, 0) + .saturating_add(Weight::from_parts(0, 3505)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Mmr::RootHash` (r:1 w:0) + /// Proof: `Mmr::RootHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `Mmr::NumberOfLeaves` (r:1 w:0) + /// Proof: `Mmr::NumberOfLeaves` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 512]`. + fn n_items_proof_is_non_canonical(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `193` + // Estimated: `1517` + // Minimum execution time: 12_860_000 picoseconds. + Weight::from_parts(17_158_162, 0) + .saturating_add(Weight::from_parts(0, 1517)) + // Standard Error: 1_732 + .saturating_add(Weight::from_parts(1_489_410, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2)) + } +} diff --git a/prdoc/pr_5188.prdoc b/prdoc/pr_5188.prdoc new file mode 100644 index 000000000000..b2ab9ff6653b --- /dev/null +++ b/prdoc/pr_5188.prdoc @@ -0,0 +1,32 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Added benchmarks for BEEFY fork voting + +doc: + - audience: + - Runtime Dev + - Runtime User + description: | + This PR adds benchmarks for `report_fork_voting` and `report_future_voting` extrinsics to `pallet-beefy`. + `report_future_voting` can be called now. `report_fork_voting` can't be called yet. Even though we have added + the formula for computing its weight, we still use `Weight::MAX`. We will set the proper weight in a future PR. + In order to do this we need to also check that the ancestry proof is optimal. + The PR adds a `WeightInfo` associated trait to the `pallet_beefy_mmr::Config` and defines benchmarks for + `pallet_beefy_mmr`. + +crates: + - name: pallet-mmr + bump: minor + - name: sp-mmr-primitives + bump: minor + - name: sp-consensus-beefy + bump: minor + - name: rococo-runtime + bump: minor + - name: pallet-beefy + bump: major + - name: pallet-beefy-mmr + bump: major + - name: westend-runtime + bump: minor diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index a94838cf20c0..ff3f7e26baf2 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1612,6 +1612,7 @@ impl pallet_beefy_mmr::Config for Runtime { type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; type LeafExtra = Vec; type BeefyDataProvider = (); + type WeightInfo = (); } parameter_types! { @@ -2585,6 +2586,7 @@ mod benches { [pallet_babe, Babe] [pallet_bags_list, VoterList] [pallet_balances, Balances] + [pallet_beefy_mmr, MmrLeaf] [pallet_bounties, Bounties] [pallet_broker, Broker] [pallet_child_bounties, ChildBounties] diff --git a/substrate/frame/beefy-mmr/Cargo.toml b/substrate/frame/beefy-mmr/Cargo.toml index 672a7a68bc7e..d67ac20ee922 100644 --- a/substrate/frame/beefy-mmr/Cargo.toml +++ b/substrate/frame/beefy-mmr/Cargo.toml @@ -18,6 +18,7 @@ log = { workspace = true } scale-info = { features = ["derive"], workspace = true } serde = { optional = true, workspace = true, default-features = true } binary-merkle-tree = { workspace = true } +frame-benchmarking = { optional = true, workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } pallet-beefy = { workspace = true } @@ -40,6 +41,7 @@ std = [ "array-bytes", "binary-merkle-tree/std", "codec/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", "log/std", @@ -65,6 +67,7 @@ try-runtime = [ "sp-runtime/try-runtime", ] runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-mmr/runtime-benchmarks", diff --git a/substrate/frame/beefy-mmr/src/benchmarking.rs b/substrate/frame/beefy-mmr/src/benchmarking.rs new file mode 100644 index 000000000000..135f95eabb99 --- /dev/null +++ b/substrate/frame/beefy-mmr/src/benchmarking.rs @@ -0,0 +1,129 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Beefy pallet benchmarking. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use crate::Pallet as BeefyMmr; +use codec::Encode; +use frame_benchmarking::v2::*; +use frame_support::traits::Hooks; +use frame_system::{Config as SystemConfig, Pallet as System}; +use pallet_mmr::{Nodes, Pallet as Mmr}; +use sp_consensus_beefy::Payload; +use sp_runtime::traits::One; + +pub trait Config: + pallet_mmr::Config + crate::Config +{ +} + +impl Config for T where + T: pallet_mmr::Config + crate::Config +{ +} + +fn init_block(block_num: u32) { + let block_num = block_num.into(); + System::::initialize(&block_num, &::Hash::default(), &Default::default()); + Mmr::::on_initialize(block_num); +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn extract_validation_context() { + if !cfg!(feature = "test") { + pallet_mmr::UseLocalStorage::::set(true); + } + + init_block::(1); + let header = System::::finalize(); + frame_system::BlockHash::::insert(BlockNumberFor::::one(), header.hash()); + + let validation_context; + #[block] + { + validation_context = + as AncestryHelper>>::extract_validation_context(header); + } + + assert!(validation_context.is_some()); + } + + #[benchmark] + fn read_peak() { + if !cfg!(feature = "test") { + pallet_mmr::UseLocalStorage::::set(true); + } + + init_block::(1); + + let peak; + #[block] + { + peak = Nodes::::get(0) + } + + assert!(peak.is_some()); + } + + /// Generate ancestry proofs with `n` nodes and benchmark the verification logic. + /// These proofs are inflated, containing all the leafs, so we won't read any peak during + /// the verification. We need to account for the peaks separately. + #[benchmark] + fn n_items_proof_is_non_canonical(n: Linear<2, 512>) { + if !cfg!(feature = "test") { + pallet_mmr::UseLocalStorage::::set(true); + } + + for block_num in 1..=n { + init_block::(block_num); + } + let proof = Mmr::::generate_mock_ancestry_proof().unwrap(); + assert_eq!(proof.items.len(), n as usize); + + let is_non_canonical; + #[block] + { + is_non_canonical = as AncestryHelper>>::is_non_canonical( + &Commitment { + payload: Payload::from_single_entry( + known_payloads::MMR_ROOT_ID, + MerkleRootOf::::default().encode(), + ), + block_number: n.into(), + validator_set_id: 0, + }, + proof, + Mmr::::mmr_root(), + ); + }; + + assert_eq!(is_non_canonical, true); + } + + impl_benchmark_test_suite!( + Pallet, + crate::mock::new_test_ext(Default::default()), + crate::mock::Test + ); +} diff --git a/substrate/frame/beefy-mmr/src/lib.rs b/substrate/frame/beefy-mmr/src/lib.rs index 195bbfbf2f29..73119c3faa9b 100644 --- a/substrate/frame/beefy-mmr/src/lib.rs +++ b/substrate/frame/beefy-mmr/src/lib.rs @@ -35,27 +35,34 @@ extern crate alloc; -use sp_runtime::traits::{Convert, Header, Member}; +use sp_runtime::{ + generic::OpaqueDigestItemId, + traits::{Convert, Header, Member}, + SaturatedConversion, +}; use alloc::vec::Vec; use codec::Decode; -use pallet_mmr::{primitives::AncestryProof, LeafDataProvider, ParentNumberAndHash}; +use pallet_mmr::{primitives::AncestryProof, LeafDataProvider, NodesUtils, ParentNumberAndHash}; use sp_consensus_beefy::{ known_payloads, mmr::{BeefyAuthoritySet, BeefyDataProvider, BeefyNextAuthoritySet, MmrLeaf, MmrLeafVersion}, - AncestryHelper, Commitment, ConsensusLog, ValidatorSet as BeefyValidatorSet, + AncestryHelper, AncestryHelperWeightInfo, Commitment, ConsensusLog, + ValidatorSet as BeefyValidatorSet, }; -use frame_support::{crypto::ecdsa::ECDSAExt, traits::Get}; +use frame_support::{crypto::ecdsa::ECDSAExt, pallet_prelude::Weight, traits::Get}; use frame_system::pallet_prelude::{BlockNumberFor, HeaderFor}; pub use pallet::*; -use sp_runtime::generic::OpaqueDigestItemId; +pub use weights::WeightInfo; +mod benchmarking; #[cfg(test)] mod mock; #[cfg(test)] mod tests; +mod weights; /// A BEEFY consensus digest item with MMR root hash. pub struct DepositBeefyDigest(core::marker::PhantomData); @@ -126,6 +133,8 @@ pub mod pallet { /// Retrieve arbitrary data that should be added to the mmr leaf type BeefyDataProvider: BeefyDataProvider; + + type WeightInfo: WeightInfo; } /// Details of current BEEFY authority set. @@ -263,6 +272,30 @@ where } } +impl AncestryHelperWeightInfo> for Pallet +where + T: pallet_mmr::Config, +{ + fn extract_validation_context() -> Weight { + ::WeightInfo::extract_validation_context() + } + + fn is_non_canonical(proof: &>>::Proof) -> Weight { + let mmr_utils = NodesUtils::new(proof.leaf_count); + let num_peaks = mmr_utils.number_of_peaks(); + + // The approximated cost of verifying an ancestry proof with `n` nodes. + // We add the previous peaks to the total number of nodes, + // since they have to be processed as well. + ::WeightInfo::n_items_proof_is_non_canonical( + proof.items.len().saturating_add(proof.prev_peaks.len()).saturated_into(), + ) + // `n_items_proof_is_non_canonical()` uses inflated proofs that contain all the leafs, + // where no peak needs to be read. So we need to also add the cost of reading the peaks. + .saturating_add(::WeightInfo::read_peak().saturating_mul(num_peaks)) + } +} + impl Pallet { /// Return the currently active BEEFY authority set proof. pub fn authority_set_proof() -> BeefyAuthoritySet> { diff --git a/substrate/frame/beefy-mmr/src/mock.rs b/substrate/frame/beefy-mmr/src/mock.rs index 1102f9677aaa..6756c618d706 100644 --- a/substrate/frame/beefy-mmr/src/mock.rs +++ b/substrate/frame/beefy-mmr/src/mock.rs @@ -37,6 +37,7 @@ use crate as pallet_beefy_mmr; pub use sp_consensus_beefy::{ ecdsa_crypto::AuthorityId as BeefyId, mmr::BeefyDataProvider, ConsensusLog, BEEFY_ENGINE_ID, }; +use sp_core::offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}; impl_opaque_keys! { pub struct MockSessionKeys { @@ -122,6 +123,7 @@ impl pallet_beefy_mmr::Config for Test { type LeafExtra = Vec; type BeefyDataProvider = DummyDataProvider; + type WeightInfo = (); } pub struct DummyDataProvider; @@ -191,5 +193,10 @@ pub fn new_test_ext_raw_authorities(authorities: Vec<(u64, BeefyId)>) -> TestExt .assimilate_storage(&mut t) .unwrap(); - t.into() + let mut ext: TestExternalities = t.into(); + let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db()); + ext.register_extension(OffchainDbExt::new(offchain.clone())); + ext.register_extension(OffchainWorkerExt::new(offchain)); + + ext } diff --git a/substrate/frame/beefy-mmr/src/tests.rs b/substrate/frame/beefy-mmr/src/tests.rs index f99835a1dc0a..b126a01012b4 100644 --- a/substrate/frame/beefy-mmr/src/tests.rs +++ b/substrate/frame/beefy-mmr/src/tests.rs @@ -24,10 +24,7 @@ use sp_consensus_beefy::{ AncestryHelper, Commitment, Payload, ValidatorSet, }; -use sp_core::{ - offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, - H256, -}; +use sp_core::H256; use sp_io::TestExternalities; use sp_runtime::{traits::Keccak256, DigestItem}; @@ -40,8 +37,6 @@ fn init_block(block: u64, maybe_parent_hash: Option) { System::initialize(&block, &parent_hash, &Default::default()); Session::on_initialize(block); Mmr::on_initialize(block); - Beefy::on_initialize(block); - BeefyMmr::on_initialize(block); } pub fn beefy_log(log: ConsensusLog) -> DigestItem { @@ -211,11 +206,6 @@ fn should_update_authorities() { fn extract_validation_context_should_work_correctly() { let mut ext = new_test_ext(vec![1, 2]); - // Register offchain ext. - let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db()); - ext.register_extension(OffchainDbExt::new(offchain.clone())); - ext.register_extension(OffchainWorkerExt::new(offchain)); - ext.execute_with(|| { init_block(1, None); let h1 = System::finalize(); @@ -262,13 +252,8 @@ fn is_non_canonical_should_work_correctly() { }); ext.persist_offchain_overlay(); - // Register offchain ext. - let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db()); - ext.register_extension(OffchainDbExt::new(offchain.clone())); - ext.register_extension(OffchainWorkerExt::new(offchain)); - ext.execute_with(|| { - let valid_proof = Mmr::generate_ancestry_proof(250, None).unwrap(); + let valid_proof = BeefyMmr::generate_proof(250, None).unwrap(); let mut invalid_proof = valid_proof.clone(); invalid_proof.items.push((300, Default::default())); @@ -343,7 +328,7 @@ fn is_non_canonical_should_work_correctly() { // - should return false, if the commitment is targeting the canonical chain // - should return true if the commitment is NOT targeting the canonical chain for prev_block_number in 1usize..=500 { - let proof = Mmr::generate_ancestry_proof(prev_block_number as u64, None).unwrap(); + let proof = BeefyMmr::generate_proof(prev_block_number as u64, None).unwrap(); assert_eq!( BeefyMmr::is_non_canonical( diff --git a/substrate/frame/beefy-mmr/src/weights.rs b/substrate/frame/beefy-mmr/src/weights.rs new file mode 100644 index 000000000000..c292f25400cc --- /dev/null +++ b/substrate/frame/beefy-mmr/src/weights.rs @@ -0,0 +1,134 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `pallet_beefy_mmr` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// target/production/substrate-node +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_beefy_mmr +// --chain=dev +// --header=./substrate/HEADER-APACHE2 +// --output=./substrate/frame/beefy-mmr/src/weights.rs +// --template=./substrate/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_beefy_mmr`. +pub trait WeightInfo { + fn extract_validation_context() -> Weight; + fn read_peak() -> Weight; + fn n_items_proof_is_non_canonical(n: u32, ) -> Weight; +} + +/// Weights for `pallet_beefy_mmr` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn extract_validation_context() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 7_461_000 picoseconds. + Weight::from_parts(7_669_000, 3509) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Mmr::Nodes` (r:1 w:0) + /// Proof: `Mmr::Nodes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + fn read_peak() -> Weight { + // Proof Size summary in bytes: + // Measured: `333` + // Estimated: `3505` + // Minimum execution time: 6_137_000 picoseconds. + Weight::from_parts(6_423_000, 3505) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Mmr::RootHash` (r:1 w:0) + /// Proof: `Mmr::RootHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `Mmr::NumberOfLeaves` (r:1 w:0) + /// Proof: `Mmr::NumberOfLeaves` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 512]`. + fn n_items_proof_is_non_canonical(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `325` + // Estimated: `1517` + // Minimum execution time: 10_687_000 picoseconds. + Weight::from_parts(14_851_626, 1517) + // Standard Error: 1_455 + .saturating_add(Weight::from_parts(961_703, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn extract_validation_context() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 7_461_000 picoseconds. + Weight::from_parts(7_669_000, 3509) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Mmr::Nodes` (r:1 w:0) + /// Proof: `Mmr::Nodes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + fn read_peak() -> Weight { + // Proof Size summary in bytes: + // Measured: `333` + // Estimated: `3505` + // Minimum execution time: 6_137_000 picoseconds. + Weight::from_parts(6_423_000, 3505) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Mmr::RootHash` (r:1 w:0) + /// Proof: `Mmr::RootHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `Mmr::NumberOfLeaves` (r:1 w:0) + /// Proof: `Mmr::NumberOfLeaves` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 512]`. + fn n_items_proof_is_non_canonical(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `325` + // Estimated: `1517` + // Minimum execution time: 10_687_000 picoseconds. + Weight::from_parts(14_851_626, 1517) + // Standard Error: 1_455 + .saturating_add(Weight::from_parts(961_703, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } +} diff --git a/substrate/frame/beefy/src/default_weights.rs b/substrate/frame/beefy/src/default_weights.rs index 70dd3bb02bf1..6b83015459d6 100644 --- a/substrate/frame/beefy/src/default_weights.rs +++ b/substrate/frame/beefy/src/default_weights.rs @@ -57,11 +57,6 @@ impl crate::WeightInfo for () { .saturating_add(DbWeight::get().reads(2)) } - // TODO: Calculate - fn report_fork_voting(_validator_count: u32, _max_nominators_per_validator: u32) -> Weight { - Weight::MAX - } - fn set_new_genesis() -> Weight { DbWeight::get().writes(1) } diff --git a/substrate/frame/beefy/src/lib.rs b/substrate/frame/beefy/src/lib.rs index 353ba876c7ed..cf690a9df339 100644 --- a/substrate/frame/beefy/src/lib.rs +++ b/substrate/frame/beefy/src/lib.rs @@ -19,21 +19,33 @@ extern crate alloc; +mod default_weights; +mod equivocation; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + use alloc::{boxed::Box, vec::Vec}; use codec::{Encode, MaxEncodedLen}; +use log; use frame_support::{ dispatch::{DispatchResultWithPostInfo, Pays}, pallet_prelude::*, traits::{Get, OneSessionHandler}, - weights::Weight, + weights::{constants::RocksDbWeight as DbWeight, Weight}, BoundedSlice, BoundedVec, Parameter, }; use frame_system::{ ensure_none, ensure_signed, pallet_prelude::{BlockNumberFor, HeaderFor, OriginFor}, }; -use log; +use sp_consensus_beefy::{ + AncestryHelper, AncestryHelperWeightInfo, AuthorityIndex, BeefyAuthorityId, ConsensusLog, + DoubleVotingProof, ForkVotingProof, FutureBlockVotingProof, OnNewValidatorSet, ValidatorSet, + BEEFY_ENGINE_ID, GENESIS_AUTHORITY_SET_ID, +}; use sp_runtime::{ generic::DigestItem, traits::{IsMember, Member, One}, @@ -42,24 +54,10 @@ use sp_runtime::{ use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::{offence::OffenceReportSystem, SessionIndex}; -use sp_consensus_beefy::{ - AncestryHelper, AuthorityIndex, BeefyAuthorityId, ConsensusLog, DoubleVotingProof, - ForkVotingProof, FutureBlockVotingProof, OnNewValidatorSet, ValidatorSet, BEEFY_ENGINE_ID, - GENESIS_AUTHORITY_SET_ID, -}; - -mod default_weights; -mod equivocation; -#[cfg(test)] -mod mock; -#[cfg(test)] -mod tests; - +use crate::equivocation::EquivocationEvidenceFor; pub use crate::equivocation::{EquivocationOffence, EquivocationReportSystem, TimeSlot}; pub use pallet::*; -use crate::equivocation::EquivocationEvidenceFor; - const LOG_TARGET: &str = "runtime::beefy"; #[frame_support::pallet] @@ -102,7 +100,8 @@ pub mod pallet { type OnNewValidatorSet: OnNewValidatorSet<::BeefyId>; /// Hook for checking commitment canonicity. - type AncestryHelper: AncestryHelper>; + type AncestryHelper: AncestryHelper> + + AncestryHelperWeightInfo>; /// Weights for this pallet. type WeightInfo: WeightInfo; @@ -295,9 +294,10 @@ pub mod pallet { /// and validate the given key ownership proof against the extracted offender. /// If both are valid, the offence will be reported. #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::report_fork_voting( + #[pallet::weight(T::WeightInfo::report_fork_voting::( key_owner_proof.validator_count(), T::MaxNominators::get(), + &equivocation_proof.ancestry_proof ))] pub fn report_fork_voting( origin: OriginFor, @@ -329,9 +329,10 @@ pub mod pallet { /// if the block author is defined it will be defined as the equivocation /// reporter. #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::report_fork_voting( + #[pallet::weight(T::WeightInfo::report_fork_voting::( key_owner_proof.validator_count(), T::MaxNominators::get(), + &equivocation_proof.ancestry_proof ))] pub fn report_fork_voting_unsigned( origin: OriginFor, @@ -358,7 +359,7 @@ pub mod pallet { /// and validate the given key ownership proof against the extracted offender. /// If both are valid, the offence will be reported. #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::report_fork_voting( + #[pallet::weight(T::WeightInfo::report_future_block_voting( key_owner_proof.validator_count(), T::MaxNominators::get(), ))] @@ -389,7 +390,7 @@ pub mod pallet { /// if the block author is defined it will be defined as the equivocation /// reporter. #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::report_fork_voting( + #[pallet::weight(T::WeightInfo::report_future_block_voting( key_owner_proof.validator_count(), T::MaxNominators::get(), ))] @@ -740,15 +741,52 @@ pub trait WeightInfo { validator_count: u32, max_nominators_per_validator: u32, ) -> Weight; + + fn set_new_genesis() -> Weight; +} + +pub(crate) trait WeightInfoExt: WeightInfo { fn report_double_voting(validator_count: u32, max_nominators_per_validator: u32) -> Weight { Self::report_voting_equivocation(2, validator_count, max_nominators_per_validator) } - fn report_fork_voting(validator_count: u32, max_nominators_per_validator: u32) -> Weight; + + fn report_fork_voting( + validator_count: u32, + max_nominators_per_validator: u32, + ancestry_proof: &>>::Proof, + ) -> Weight { + let _weight = >>::extract_validation_context() + .saturating_add( + >>::is_non_canonical( + ancestry_proof, + ), + ) + .saturating_add(Self::report_voting_equivocation( + 1, + validator_count, + max_nominators_per_validator, + )); + + // TODO: https://github.com/paritytech/polkadot-sdk/issues/4523 - return `_weight` here. + // We return `Weight::MAX` currently in order to disallow this extrinsic for the moment. + // We need to check that the proof is optimal. + Weight::MAX + } + fn report_future_block_voting( validator_count: u32, max_nominators_per_validator: u32, ) -> Weight { - Self::report_voting_equivocation(1, validator_count, max_nominators_per_validator) + // checking if the report is for a future block + DbWeight::get() + .reads(1) + // check and report the equivocated vote + .saturating_add(Self::report_voting_equivocation( + 1, + validator_count, + max_nominators_per_validator, + )) } - fn set_new_genesis() -> Weight; } + +impl WeightInfoExt for T where T: WeightInfo {} diff --git a/substrate/frame/beefy/src/mock.rs b/substrate/frame/beefy/src/mock.rs index b423fa0bda89..5c79d8f7d7d7 100644 --- a/substrate/frame/beefy/src/mock.rs +++ b/substrate/frame/beefy/src/mock.rs @@ -21,12 +21,13 @@ use std::vec; use frame_election_provider_support::{ bounds::{ElectionBounds, ElectionBoundsBuilder}, - onchain, SequentialPhragmen, + onchain, SequentialPhragmen, Weight, }; use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{ConstU32, ConstU64, KeyOwnerProofSystem, OnFinalize, OnInitialize}, }; +use frame_system::pallet_prelude::HeaderFor; use pallet_session::historical as pallet_session_historical; use sp_core::{crypto::KeyTypeId, ConstU128}; use sp_runtime::{ @@ -43,7 +44,7 @@ use sp_state_machine::BasicExternalities; use crate as pallet_beefy; pub use sp_consensus_beefy::{ecdsa_crypto::AuthorityId as BeefyId, ConsensusLog, BEEFY_ENGINE_ID}; -use sp_consensus_beefy::{AncestryHelper, Commitment}; +use sp_consensus_beefy::{AncestryHelper, AncestryHelperWeightInfo, Commitment}; impl_opaque_keys! { pub struct MockSessionKeys { @@ -131,6 +132,16 @@ impl AncestryHelper
for MockAncestryHelper { } } +impl AncestryHelperWeightInfo
for MockAncestryHelper { + fn extract_validation_context() -> Weight { + unimplemented!() + } + + fn is_non_canonical(_proof: &>>::Proof) -> Weight { + unimplemented!() + } +} + impl pallet_beefy::Config for Test { type BeefyId = BeefyId; type MaxAuthorities = ConstU32<100>; diff --git a/substrate/frame/beefy/src/tests.rs b/substrate/frame/beefy/src/tests.rs index a63b3532b698..d75237205cac 100644 --- a/substrate/frame/beefy/src/tests.rs +++ b/substrate/frame/beefy/src/tests.rs @@ -35,7 +35,7 @@ use sp_consensus_beefy::{ use sp_runtime::DigestItem; use sp_session::MembershipProof; -use crate::{self as beefy, mock::*, Call, Config, Error, WeightInfo}; +use crate::{self as beefy, mock::*, Call, Config, Error, WeightInfoExt}; fn init_block(block: u64) { System::set_block_number(block); @@ -765,7 +765,9 @@ fn report_double_voting_has_valid_weight() { // the weight depends on the size of the validator set, // but there's a lower bound of 100 validators. assert!((1..=100) - .map(|validators| ::WeightInfo::report_double_voting(validators, 1000)) + .map(|validators| <::WeightInfo as WeightInfoExt>::report_double_voting( + validators, 1000 + )) .collect::>() .windows(2) .all(|w| w[0] == w[1])); @@ -773,7 +775,9 @@ fn report_double_voting_has_valid_weight() { // after 100 validators the weight should keep increasing // with every extra validator. assert!((100..=1000) - .map(|validators| ::WeightInfo::report_double_voting(validators, 1000)) + .map(|validators| <::WeightInfo as WeightInfoExt>::report_double_voting( + validators, 1000 + )) .collect::>() .windows(2) .all(|w| w[0].ref_time() < w[1].ref_time())); diff --git a/substrate/frame/merkle-mountain-range/src/lib.rs b/substrate/frame/merkle-mountain-range/src/lib.rs index 0ab44711bcf5..7dfe95c83361 100644 --- a/substrate/frame/merkle-mountain-range/src/lib.rs +++ b/substrate/frame/merkle-mountain-range/src/lib.rs @@ -240,6 +240,11 @@ pub mod pallet { pub type Nodes, I: 'static = ()> = StorageMap<_, Identity, NodeIndex, HashOf, OptionQuery>; + /// Helper flag used in the runtime benchmarks for the initial setup. + #[cfg(feature = "runtime-benchmarks")] + #[pallet::storage] + pub type UseLocalStorage = StorageValue<_, bool, ValueQuery>; + #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { fn on_initialize(_n: BlockNumberFor) -> Weight { @@ -439,6 +444,14 @@ impl, I: 'static> Pallet { mmr.generate_ancestry_proof(prev_leaf_count) } + #[cfg(feature = "runtime-benchmarks")] + pub fn generate_mock_ancestry_proof() -> Result>, Error> + { + let leaf_count = Self::block_num_to_leaf_count(>::block_number())?; + let mmr: ModuleMmr = mmr::Mmr::new(leaf_count); + mmr.generate_mock_ancestry_proof() + } + pub fn verify_ancestry_proof( root: HashOf, ancestry_proof: primitives::AncestryProof>, diff --git a/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs b/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs index 2b46357c5072..f9a4580b9bb3 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -156,7 +156,7 @@ where } /// Return the internal size of the MMR (number of nodes). - #[cfg(test)] + #[cfg(any(test, feature = "runtime-benchmarks"))] pub fn size(&self) -> NodeIndex { self.mmr.mmr_size() } @@ -252,4 +252,48 @@ where .collect(), }) } + + /// Generate an inflated ancestry proof for the latest leaf in the MMR. + /// + /// The generated proof contains all the leafs in the MMR, so this way we can generate a proof + /// with exactly `leaf_count` items. + #[cfg(feature = "runtime-benchmarks")] + pub fn generate_mock_ancestry_proof( + &self, + ) -> Result>, Error> { + use crate::ModuleMmr; + use alloc::vec; + use sp_mmr_primitives::mmr_lib::helper; + + let mmr: ModuleMmr = Mmr::new(self.leaves); + let store = >::default(); + + let mut prev_peaks = vec![]; + for peak_pos in helper::get_peaks(mmr.size()) { + let peak = store + .get_elem(peak_pos) + .map_err(|_| Error::GenerateProof)? + .ok_or(Error::GenerateProof)? + .hash(); + prev_peaks.push(peak); + } + + let mut proof_items = vec![]; + for leaf_idx in 0..self.leaves { + let leaf_pos = NodesUtils::leaf_index_to_leaf_node_index(leaf_idx); + let leaf = store + .get_elem(leaf_pos) + .map_err(|_| Error::GenerateProof)? + .ok_or(Error::GenerateProof)? + .hash(); + proof_items.push((leaf_pos, leaf)); + } + + Ok(sp_mmr_primitives::AncestryProof { + prev_peaks, + prev_leaf_count: self.leaves, + leaf_count: self.leaves, + items: proof_items, + }) + } } diff --git a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs index a39089801484..02852388b417 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs @@ -22,7 +22,6 @@ use codec::Encode; use core::iter::Peekable; use log::{debug, trace}; use sp_core::offchain::StorageKind; -use sp_io::offchain_index; use sp_mmr_primitives::{mmr_lib, mmr_lib::helper, utils::NodesUtils}; use crate::{ @@ -47,6 +46,26 @@ pub struct RuntimeStorage; /// DOES NOT support adding new items to the MMR. pub struct OffchainStorage; +impl OffchainStorage { + fn get(key: &[u8]) -> Option> { + sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) + } + + #[cfg(not(feature = "runtime-benchmarks"))] + fn set, I: 'static>(key: &[u8], value: &[u8]) { + sp_io::offchain_index::set(key, value); + } + + #[cfg(feature = "runtime-benchmarks")] + fn set, I: 'static>(key: &[u8], value: &[u8]) { + if crate::pallet::UseLocalStorage::::get() { + sp_io::offchain::local_storage_set(StorageKind::PERSISTENT, key, value); + } else { + sp_io::offchain_index::set(key, value); + } + } +} + /// A storage layer for MMR. /// /// There are two different implementations depending on the use case. @@ -78,7 +97,7 @@ where pos, ancestor_leaf_idx, key ); // Try to retrieve the element from Off-chain DB. - if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + if let Some(elem) = OffchainStorage::get(&key) { return Ok(codec::Decode::decode(&mut &*elem).ok()) } @@ -93,8 +112,7 @@ where pos, ancestor_leaf_idx, ancestor_parent_hash, temp_key ); // Retrieve the element from Off-chain DB. - Ok(sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &temp_key) - .and_then(|v| codec::Decode::decode(&mut &*v).ok())) + Ok(OffchainStorage::get(&temp_key).and_then(|v| codec::Decode::decode(&mut &*v).ok())) } } @@ -203,8 +221,7 @@ where target: "runtime::mmr::offchain", "offchain db set: pos {} parent_hash {:?} key {:?}", pos, parent_hash, temp_key ); - // Indexing API is used to store the full node content. - offchain_index::set(&temp_key, &encoded_node); + OffchainStorage::set::(&temp_key, &encoded_node); } } diff --git a/substrate/primitives/consensus/beefy/Cargo.toml b/substrate/primitives/consensus/beefy/Cargo.toml index f31aa5756ba2..57ddab9a70ce 100644 --- a/substrate/primitives/consensus/beefy/Cargo.toml +++ b/substrate/primitives/consensus/beefy/Cargo.toml @@ -26,6 +26,7 @@ sp-io = { workspace = true } sp-mmr-primitives = { workspace = true } sp-runtime = { workspace = true } sp-keystore = { workspace = true } +sp-weights = { workspace = true } strum = { features = ["derive"], workspace = true } lazy_static = { optional = true, workspace = true } @@ -48,6 +49,7 @@ std = [ "sp-keystore/std", "sp-mmr-primitives/std", "sp-runtime/std", + "sp-weights/std", "strum/std", ] diff --git a/substrate/primitives/consensus/beefy/src/lib.rs b/substrate/primitives/consensus/beefy/src/lib.rs index 6ec4a727e276..e977fb0ea25f 100644 --- a/substrate/primitives/consensus/beefy/src/lib.rs +++ b/substrate/primitives/consensus/beefy/src/lib.rs @@ -56,6 +56,7 @@ use sp_runtime::{ traits::{Hash, Header as HeaderT, Keccak256, NumberFor}, OpaqueValue, }; +use sp_weights::Weight; /// Key type for BEEFY module. pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_types::BEEFY; @@ -460,6 +461,15 @@ pub trait AncestryHelper { ) -> bool; } +/// Weight information for the logic in `AncestryHelper`. +pub trait AncestryHelperWeightInfo: AncestryHelper
{ + /// Weight info for the `AncestryHelper::extract_validation_context()` method. + fn extract_validation_context() -> Weight; + + /// Weight info for the `AncestryHelper::is_non_canonical()` method. + fn is_non_canonical(proof: &>::Proof) -> Weight; +} + /// An opaque type used to represent the key ownership proof at the runtime API /// boundary. The inner value is an encoded representation of the actual key /// ownership proof which will be parameterized when defining the runtime. At diff --git a/substrate/primitives/merkle-mountain-range/src/utils.rs b/substrate/primitives/merkle-mountain-range/src/utils.rs index 72674e24a272..2460af4b800f 100644 --- a/substrate/primitives/merkle-mountain-range/src/utils.rs +++ b/substrate/primitives/merkle-mountain-range/src/utils.rs @@ -91,7 +91,7 @@ impl NodesUtils { Self::leaf_node_index_to_leaf_index(rightmost_leaf_pos) } - // Translate a _leaf_ `NodeIndex` to its `LeafIndex`. + /// Translate a _leaf_ `NodeIndex` to its `LeafIndex`. fn leaf_node_index_to_leaf_index(pos: NodeIndex) -> LeafIndex { if pos == 0 { return 0 @@ -100,8 +100,13 @@ impl NodesUtils { (pos + peaks.len() as u64) >> 1 } - // Starting from any node position get position of rightmost leaf; this is the leaf - // responsible for the addition of node `pos`. + /// Translate a `LeafIndex` to its _leaf_ `NodeIndex`. + pub fn leaf_index_to_leaf_node_index(leaf_index: NodeIndex) -> LeafIndex { + helper::leaf_index_to_pos(leaf_index) + } + + /// Starting from any node position get position of rightmost leaf; this is the leaf + /// responsible for the addition of node `pos`. fn rightmost_leaf_node_index_from_pos(pos: NodeIndex) -> NodeIndex { pos - (helper::pos_height_in_tree(pos) as u64) } From d8c2944741b1f1b43fa37951fb311d363c753e4e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 14 Aug 2024 21:22:08 +0200 Subject: [PATCH 111/382] [CI] Fix prdoc command (#5358) Changes: - Run the prdoc command in a docker container since otherwise the set-up-gh script wont work. - Take try-runtime snapshot at night to avoid spamming the node with snapshot jobs at day. --------- Signed-off-by: Oliver Tale-Yazdi --- .github/workflows/check-runtime-migration.yml | 3 +++ .github/workflows/command-prdoc.yml | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/check-runtime-migration.yml b/.github/workflows/check-runtime-migration.yml index 2b963b2230fb..9b7a6fafcd15 100644 --- a/.github/workflows/check-runtime-migration.yml +++ b/.github/workflows/check-runtime-migration.yml @@ -6,6 +6,9 @@ on: - master pull_request: types: [opened, synchronize, reopened, ready_for_review] + # Take a snapshot at 5am when most SDK devs are not working. + schedule: + - cron: '0 5 * * *' merge_group: workflow_dispatch: diff --git a/.github/workflows/command-prdoc.yml b/.github/workflows/command-prdoc.yml index da8f14089cb8..3a08b9a5fb28 100644 --- a/.github/workflows/command-prdoc.yml +++ b/.github/workflows/command-prdoc.yml @@ -43,9 +43,21 @@ concurrency: cancel-in-progress: true jobs: + set-image: + runs-on: ubuntu-latest + outputs: + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT cmd-prdoc: + needs: [set-image] runs-on: ubuntu-latest timeout-minutes: 20 + container: + image: ${{ needs.set-image.outputs.IMAGE }} permissions: contents: write pull-requests: write From 5a9396f41e3cb7d94da3a9305512783229bc278b Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Wed, 14 Aug 2024 22:44:59 +0300 Subject: [PATCH 112/382] Unify `no_genesis` check (#5360) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The same exact `matches!()` was duplicated in `Configuration::no_genesis()` method and inline in full node parts creation. Since this is the same exact logic and reason, it makes sense to de-duplicate them. --------- Co-authored-by: Bastian Köcher --- prdoc/pr_5360.prdoc | 3 +++ substrate/client/service/src/builder.rs | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 prdoc/pr_5360.prdoc diff --git a/prdoc/pr_5360.prdoc b/prdoc/pr_5360.prdoc new file mode 100644 index 000000000000..4b07f30bfd09 --- /dev/null +++ b/prdoc/pr_5360.prdoc @@ -0,0 +1,3 @@ +crates: + - name: sc-service + bump: none diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index d7fb7918481c..90bfd9ec27f7 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -248,10 +248,7 @@ where offchain_worker_enabled: config.offchain_worker.enabled, offchain_indexing_api: config.offchain_worker.indexing_enabled, wasm_runtime_overrides: config.wasm_runtime_overrides.clone(), - no_genesis: matches!( - config.network.sync_mode, - SyncMode::LightState { .. } | SyncMode::Warp { .. } - ), + no_genesis: config.no_genesis(), wasm_runtime_substitutes, enable_import_proof_recording, }, From feacf2f3f9f39c1dbf0ecbee983c4488f948fd61 Mon Sep 17 00:00:00 2001 From: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Wed, 14 Aug 2024 21:48:13 +0200 Subject: [PATCH 113/382] [Pools] Fix issues with member migration to `DelegateStake` (#4822) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Context Pool members using the old `TransferStake` strategy were able to transfer all their funds to the pool. With `DelegateStake` changes, we want to ensure similar behaviour by allowing members to delegate all their stake to the pool. ## Changes - Ensure all the balance including ED of an account can be delegated (and used in the pool) by adding a provider for delegators. - Gates calls that mutates the pool or pool member if they are in unmigrated state. Closes https://github.com/paritytech-secops/srlabs_findings/issues/409. - Adds remote test to migrate all pools and members to `DelegateStake` which can be used with `Kusama` and `Polkadot` runtime state. closes https://github.com/paritytech/polkadot-sdk/issues/4629. - Add new runtime apis to read pool and member balance. ## Addressing possible migration errors Pool members migrating can run into two types of errors: - Already Staking: If the pool member is already staking, we cannot migrate them to `DelegateStake` since this may mean they are able to use the same staked funds in the pool. Users would need to withdraw all their funds from staking, in order to migrate their pool funds. - Pool contribution below ED: For these cases transfer from pool account to member account would fail. The affected users can top up their accounts and redo migration. Another error that was earlier possible was when member's free balance is below ED. This PR adds a provider to delegator allowing all user balance including ED can be contributed towards the pool. This helps `1095` accounts in Polkadot and `41` accounts in Kusama to migrate now which would have earlier failed. ## Results from RemoteExternalities Tests. ### Kusama `Migration stats: success: 3017, direct_stakers: 361, unexpected_errors: 0` ### Polkadot `Migration stats: success: 42859, direct_stakers: 643, unexpected_errors: 0` ## TODO - [x] Add runtime api for member total balance. - [x] New [issue](https://github.com/paritytech/polkadot-sdk/issues/5009) to reap pool members with contribution below ED. - [x] Add provider for delegators so whole balance including ED can be held while contributing to pools. - [x] Gate all pool extrinsics if pool/member is in non-migrated state. --------- Co-authored-by: Gonçalo Pestana --- polkadot/runtime/westend/src/lib.rs | 49 +--- polkadot/runtime/westend/src/tests.rs | 137 +++++++++++ prdoc/pr_4822.prdoc | 25 ++ substrate/bin/node/runtime/src/lib.rs | 8 + .../frame/delegated-staking/src/impls.rs | 2 +- substrate/frame/delegated-staking/src/lib.rs | 112 ++++----- .../frame/delegated-staking/src/tests.rs | 186 +++++++++++++-- .../frame/delegated-staking/src/types.rs | 38 ++- .../nomination-pools/runtime-api/src/lib.rs | 6 + substrate/frame/nomination-pools/src/lib.rs | 112 ++++++++- .../test-delegate-stake/src/lib.rs | 221 ++++++++++++++++-- 11 files changed, 733 insertions(+), 163 deletions(-) create mode 100644 prdoc/pr_4822.prdoc diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index aa446b03368d..a9fbbb4f33da 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -99,7 +99,7 @@ use sp_runtime::{ IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, + ApplyExtrinsicResult, FixedU128, KeyTypeId, Percent, Permill, }; use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] @@ -2476,6 +2476,14 @@ sp_api::impl_runtime_apis! { fn member_needs_delegate_migration(member: AccountId) -> bool { NominationPools::api_member_needs_delegate_migration(member) } + + fn member_total_balance(member: AccountId) -> Balance { + NominationPools::api_member_total_balance(member) + } + + fn pool_balance(pool_id: pallet_nomination_pools::PoolId) -> Balance { + NominationPools::api_pool_balance(pool_id) + } } impl pallet_staking_runtime_api::StakingApi for Runtime { @@ -2776,45 +2784,6 @@ sp_api::impl_runtime_apis! { } } -#[cfg(all(test, feature = "try-runtime"))] -mod remote_tests { - use super::*; - use frame_try_runtime::{runtime_decl_for_try_runtime::TryRuntime, UpgradeCheckSelect}; - use remote_externalities::{ - Builder, Mode, OfflineConfig, OnlineConfig, SnapshotConfig, Transport, - }; - use std::env::var; - - #[tokio::test] - async fn run_migrations() { - if var("RUN_MIGRATION_TESTS").is_err() { - return; - } - - sp_tracing::try_init_simple(); - let transport: Transport = - var("WS").unwrap_or("wss://westend-rpc.polkadot.io:443".to_string()).into(); - let maybe_state_snapshot: Option = var("SNAP").map(|s| s.into()).ok(); - let mut ext = Builder::::default() - .mode(if let Some(state_snapshot) = maybe_state_snapshot { - Mode::OfflineOrElseOnline( - OfflineConfig { state_snapshot: state_snapshot.clone() }, - OnlineConfig { - transport, - state_snapshot: Some(state_snapshot), - ..Default::default() - }, - ) - } else { - Mode::Online(OnlineConfig { transport, ..Default::default() }) - }) - .build() - .await - .unwrap(); - ext.execute_with(|| Runtime::on_runtime_upgrade(UpgradeCheckSelect::PreAndPost)); - } -} - mod clean_state_migration { use super::Runtime; #[cfg(feature = "try-runtime")] diff --git a/polkadot/runtime/westend/src/tests.rs b/polkadot/runtime/westend/src/tests.rs index 4d5e2e946bce..dc8103ab52c4 100644 --- a/polkadot/runtime/westend/src/tests.rs +++ b/polkadot/runtime/westend/src/tests.rs @@ -99,3 +99,140 @@ fn check_treasury_pallet_id() { westend_runtime_constants::TREASURY_PALLET_ID ); } + +#[cfg(all(test, feature = "try-runtime"))] +mod remote_tests { + use super::*; + use frame_try_runtime::{runtime_decl_for_try_runtime::TryRuntime, UpgradeCheckSelect}; + use remote_externalities::{ + Builder, Mode, OfflineConfig, OnlineConfig, SnapshotConfig, Transport, + }; + use std::env::var; + + #[tokio::test] + async fn run_migrations() { + if var("RUN_MIGRATION_TESTS").is_err() { + return; + } + + sp_tracing::try_init_simple(); + let transport: Transport = + var("WS").unwrap_or("wss://westend-rpc.polkadot.io:443".to_string()).into(); + let maybe_state_snapshot: Option = var("SNAP").map(|s| s.into()).ok(); + let mut ext = Builder::::default() + .mode(if let Some(state_snapshot) = maybe_state_snapshot { + Mode::OfflineOrElseOnline( + OfflineConfig { state_snapshot: state_snapshot.clone() }, + OnlineConfig { + transport, + state_snapshot: Some(state_snapshot), + ..Default::default() + }, + ) + } else { + Mode::Online(OnlineConfig { transport, ..Default::default() }) + }) + .build() + .await + .unwrap(); + ext.execute_with(|| Runtime::on_runtime_upgrade(UpgradeCheckSelect::PreAndPost)); + } + + #[tokio::test] + async fn delegate_stake_migration() { + // Intended to be run only manually. + if var("RUN_MIGRATION_TESTS").is_err() { + return; + } + use frame_support::assert_ok; + sp_tracing::try_init_simple(); + + let transport: Transport = var("WS").unwrap_or("ws://127.0.0.1:9900".to_string()).into(); + let maybe_state_snapshot: Option = var("SNAP").map(|s| s.into()).ok(); + let mut ext = Builder::::default() + .mode(if let Some(state_snapshot) = maybe_state_snapshot { + Mode::OfflineOrElseOnline( + OfflineConfig { state_snapshot: state_snapshot.clone() }, + OnlineConfig { + transport, + state_snapshot: Some(state_snapshot), + pallets: vec![ + "staking".into(), + "system".into(), + "balances".into(), + "nomination-pools".into(), + "delegated-staking".into(), + ], + ..Default::default() + }, + ) + } else { + Mode::Online(OnlineConfig { transport, ..Default::default() }) + }) + .build() + .await + .unwrap(); + ext.execute_with(|| { + // create an account with some balance + let alice = AccountId::from([1u8; 32]); + use frame_support::traits::Currency; + let _ = Balances::deposit_creating(&alice, 100_000 * UNITS); + + // iterate over all pools + pallet_nomination_pools::BondedPools::::iter_keys().for_each(|k| { + if pallet_nomination_pools::Pallet::::api_pool_needs_delegate_migration(k) + { + assert_ok!( + pallet_nomination_pools::Pallet::::migrate_pool_to_delegate_stake( + RuntimeOrigin::signed(alice.clone()).into(), + k, + ) + ); + } + }); + + // member migration stats + let mut success = 0; + let mut direct_stakers = 0; + let mut unexpected_errors = 0; + + // iterate over all pool members + pallet_nomination_pools::PoolMembers::::iter_keys().for_each(|k| { + if pallet_nomination_pools::Pallet::::api_member_needs_delegate_migration( + k.clone(), + ) { + // reasons migrations can fail: + let is_direct_staker = pallet_staking::Bonded::::contains_key(&k); + + let migration = pallet_nomination_pools::Pallet::::migrate_delegation( + RuntimeOrigin::signed(alice.clone()).into(), + sp_runtime::MultiAddress::Id(k.clone()), + ); + + if is_direct_staker { + // if the member is a direct staker, the migration should fail until pool + // member unstakes all funds from pallet-staking. + direct_stakers += 1; + assert_eq!( + migration.unwrap_err(), + pallet_delegated_staking::Error::::AlreadyStaking.into() + ); + } else if migration.is_err() { + unexpected_errors += 1; + log::error!(target: "remote_test", "Unexpected error {:?} while migrating {:?}", migration.unwrap_err(), k); + } else { + success += 1; + } + } + }); + + log::info!( + target: "remote_test", + "Migration stats: success: {}, direct_stakers: {}, unexpected_errors: {}", + success, + direct_stakers, + unexpected_errors + ); + }); + } +} diff --git a/prdoc/pr_4822.prdoc b/prdoc/pr_4822.prdoc new file mode 100644 index 000000000000..44f3e41d8d5a --- /dev/null +++ b/prdoc/pr_4822.prdoc @@ -0,0 +1,25 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Ensure as many as possible pool members can migrate to `DelegateStake` + +doc: + - audience: Runtime Dev + description: | + 1. Allows pool members to use their total balance while joining pool with `DelegateStake`. + 2. Gates call mutating pool or member in unmigrated state. + 3. Runtime apis for reading pool and member balance. + +crates: + - name: westend-runtime + bump: minor + - name: kitchensink-runtime + bump: patch + - name: pallet-delegated-staking + bump: patch + - name: pallet-nomination-pools + bump: minor + - name: sp-staking + bump: patch + - name: pallet-nomination-pools-runtime-api + bump: minor diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index ff3f7e26baf2..ea9a66862d64 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -2784,6 +2784,14 @@ impl_runtime_apis! { fn member_needs_delegate_migration(member: AccountId) -> bool { NominationPools::api_member_needs_delegate_migration(member) } + + fn member_total_balance(member: AccountId) -> Balance { + NominationPools::api_member_total_balance(member) + } + + fn pool_balance(pool_id: pallet_nomination_pools::PoolId) -> Balance { + NominationPools::api_pool_balance(pool_id) + } } impl pallet_staking_runtime_api::StakingApi for Runtime { diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 8c05b0bcfc22..4e6812dee249 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -19,7 +19,7 @@ //! Implementations of public traits, namely [`DelegationInterface`] and [`OnStakingUpdate`]. use super::*; -use sp_staking::{Agent, DelegationInterface, DelegationMigrator, Delegator, OnStakingUpdate}; +use sp_staking::{DelegationInterface, DelegationMigrator, OnStakingUpdate}; impl DelegationInterface for Pallet { type Balance = BalanceOf; diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 1882989cfa7d..08ce747ec0c0 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -148,7 +148,7 @@ use frame_support::{ }, Balanced, Inspect as FunInspect, Mutate as FunMutate, }, - tokens::{fungible::Credit, Fortitude, Precision, Preservation}, + tokens::{fungible::Credit, Fortitude, Precision, Preservation, Restriction}, Defensive, DefensiveOption, Imbalance, OnUnbalanced, }, }; @@ -319,9 +319,7 @@ pub mod pallet { Error::::NotAllowed ); - // remove provider reference - let _ = frame_system::Pallet::::dec_providers(&who)?; - >::remove(who); + AgentLedger::::remove(&who); Ok(()) } @@ -392,9 +390,6 @@ pub mod pallet { ) -> DispatchResult { let agent = ensure_signed(origin)?; - // Ensure they have minimum delegation. - ensure!(amount >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); - // Ensure delegator is sane. ensure!(!Self::is_agent(&delegator), Error::::NotAllowed); ensure!(!Self::is_delegator(&delegator), Error::::NotAllowed); @@ -493,13 +488,8 @@ impl Pallet { /// Registers a new agent in the system. fn do_register_agent(who: &T::AccountId, reward_account: &T::AccountId) { + // TODO: Consider taking a deposit for being an agent. AgentLedger::::new(reward_account).update(who); - - // Agent does not hold balance of its own but this pallet will provide for this to exist. - // This is expected to be a keyless account and not created by any user directly so safe. - // TODO: Someday if we allow anyone to be an agent, we should take a deposit for - // being a delegator. - frame_system::Pallet::::inc_providers(who); } /// Migrate existing staker account `who` to an `Agent` account. @@ -510,9 +500,6 @@ impl Pallet { // transferred to actual delegator. let proxy_delegator = Self::generate_proxy_delegator(Agent::from(who.clone())); - // Keep proxy delegator alive until all funds are migrated. - frame_system::Pallet::::inc_providers(&proxy_delegator.clone().get()); - // Get current stake let stake = T::CoreStaking::stake(who)?; @@ -534,7 +521,6 @@ impl Pallet { T::CoreStaking::set_payee(who, reward_account)?; // delegate all transferred funds back to agent. Self::do_delegate(proxy_delegator, Agent::from(who.clone()), amount_to_transfer)?; - // if the transferred/delegated amount was greater than the stake, mark the extra as // unclaimed withdrawal. let unclaimed_withdraws = amount_to_transfer @@ -578,21 +564,23 @@ impl Pallet { let delegator = delegator.get(); let mut ledger = AgentLedger::::get(&agent).ok_or(Error::::NotAgent)?; + + if let Some(mut existing_delegation) = Delegation::::get(&delegator) { + ensure!(existing_delegation.agent == agent, Error::::InvalidDelegation); + // update amount and return the updated delegation. + existing_delegation.amount = existing_delegation + .amount + .checked_add(&amount) + .ok_or(ArithmeticError::Overflow)?; + existing_delegation + } else { + Delegation::::new(&agent, amount) + } + .update(&delegator); + // try to hold the funds. T::Currency::hold(&HoldReason::StakingDelegation.into(), &delegator, amount)?; - let new_delegation_amount = - if let Some(existing_delegation) = Delegation::::get(&delegator) { - ensure!(existing_delegation.agent == agent, Error::::InvalidDelegation); - existing_delegation - .amount - .checked_add(&amount) - .ok_or(ArithmeticError::Overflow)? - } else { - amount - }; - - Delegation::::new(&agent, new_delegation_amount).update_or_kill(&delegator); ledger.total_delegated = ledger.total_delegated.checked_add(&amount).ok_or(ArithmeticError::Overflow)?; ledger.update(&agent); @@ -638,9 +626,6 @@ impl Pallet { .checked_sub(&amount) .defensive_ok_or(ArithmeticError::Overflow)?; - // remove delegator if nothing delegated anymore - delegation.update_or_kill(&delegator); - let released = T::Currency::release( &HoldReason::StakingDelegation.into(), &delegator, @@ -650,6 +635,9 @@ impl Pallet { defensive_assert!(released == amount, "hold should have been released fully"); + // update delegation. + delegation.update(&delegator); + Self::deposit_event(Event::::Released { agent, delegator, amount }); Ok(()) @@ -668,49 +656,34 @@ impl Pallet { let mut source_delegation = Delegators::::get(&source_delegator).defensive_ok_or(Error::::BadState)?; - // some checks that must have already been checked before. + // ensure source has enough funds to migrate. ensure!(source_delegation.amount >= amount, Error::::NotEnoughFunds); debug_assert!( !Self::is_delegator(&destination_delegator) && !Self::is_agent(&destination_delegator) ); let agent = source_delegation.agent.clone(); - // update delegations - Delegation::::new(&agent, amount).update_or_kill(&destination_delegator); + // create a new delegation for destination delegator. + Delegation::::new(&agent, amount).update(&destination_delegator); source_delegation.amount = source_delegation .amount .checked_sub(&amount) .defensive_ok_or(Error::::BadState)?; - source_delegation.update_or_kill(&source_delegator); - - // release funds from source - let released = T::Currency::release( + // transfer the held amount in `source_delegator` to `destination_delegator`. + let _ = T::Currency::transfer_on_hold( &HoldReason::StakingDelegation.into(), - &source_delegator, - amount, - Precision::BestEffort, - )?; - - defensive_assert!(released == amount, "hold should have been released fully"); - - // transfer the released amount to `destination_delegator`. - let post_balance = T::Currency::transfer( &source_delegator, &destination_delegator, amount, - Preservation::Expendable, - ) - .map_err(|_| Error::::BadState)?; - - // if balance is zero, clear provider for source (proxy) delegator. - if post_balance == Zero::zero() { - let _ = frame_system::Pallet::::dec_providers(&source_delegator).defensive(); - } + Precision::Exact, + Restriction::OnHold, + Fortitude::Polite, + )?; - // hold the funds again in the new delegator account. - T::Currency::hold(&HoldReason::StakingDelegation.into(), &destination_delegator, amount)?; + // update source delegation. + source_delegation.update(&source_delegator); Self::deposit_event(Event::::MigratedDelegation { agent, @@ -752,7 +725,7 @@ impl Pallet { agent_ledger.remove_slash(actual_slash).save(); delegation.amount = delegation.amount.checked_sub(&actual_slash).ok_or(ArithmeticError::Overflow)?; - delegation.update_or_kill(&delegator); + delegation.update(&delegator); if let Some(reporter) = maybe_reporter { let reward_payout: BalanceOf = T::SlashRewardFraction::get() * actual_slash; @@ -801,18 +774,21 @@ impl Pallet { ledgers: BTreeMap>, ) -> Result<(), sp_runtime::TryRuntimeError> { for (agent, ledger) in ledgers { - ensure!( - matches!( - T::CoreStaking::status(&agent).expect("agent should be bonded"), - sp_staking::StakerStatus::Nominator(_) | sp_staking::StakerStatus::Idle - ), - "agent should be bonded and not validator" - ); + let staked_value = ledger.stakeable_balance(); + + if !staked_value.is_zero() { + ensure!( + matches!( + T::CoreStaking::status(&agent).expect("agent should be bonded"), + sp_staking::StakerStatus::Nominator(_) | sp_staking::StakerStatus::Idle + ), + "agent should be bonded and not validator" + ); + } ensure!( ledger.stakeable_balance() >= - T::CoreStaking::total_stake(&agent) - .expect("agent should exist as a nominator"), + T::CoreStaking::total_stake(&agent).unwrap_or_default(), "Cannot stake more than balance" ); } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index bc8bc2dccc3c..2c965e18b1b3 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -334,6 +334,36 @@ fn apply_pending_slash() { }); } +#[test] +fn allow_full_amount_to_be_delegated() { + ExtBuilder::default().build_and_execute(|| { + let agent: AccountId = 200; + let reward_acc: AccountId = 201; + let delegator: AccountId = 300; + + // set intention to accept delegation. + fund(&agent, 1000); + assert_ok!(DelegatedStaking::register_agent(RawOrigin::Signed(agent).into(), reward_acc)); + + // delegate to this account + fund(&delegator, 1000); + assert_ok!(DelegatedStaking::delegate_to_agent( + RawOrigin::Signed(delegator).into(), + agent, + 1000 + )); + + // verify + assert!(DelegatedStaking::is_agent(&agent)); + assert_eq!(DelegatedStaking::stakeable_balance(Agent::from(agent)), 1000); + assert_eq!( + Balances::balance_on_hold(&HoldReason::StakingDelegation.into(), &delegator), + 1000 + ); + assert_eq!(DelegatedStaking::held_balance_of(Delegator::from(delegator)), 1000); + }); +} + /// Integration tests with pallet-staking. mod staking_integration { use super::*; @@ -625,32 +655,42 @@ mod staking_integration { #[test] fn migration_works() { ExtBuilder::default().build_and_execute(|| { + // initial era + start_era(1); + // add a nominator let staked_amount = 4000; let agent_amount = 5000; - fund(&200, agent_amount); + let agent = 200; + fund(&agent, agent_amount); assert_ok!(Staking::bond( - RuntimeOrigin::signed(200), + RuntimeOrigin::signed(agent), staked_amount, RewardDestination::Account(201) )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(200), vec![GENESIS_VALIDATOR],)); - let init_stake = Staking::stake(&200).unwrap(); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(agent), vec![GENESIS_VALIDATOR],)); + let init_stake = Staking::stake(&agent).unwrap(); // scenario: 200 is a pool account, and the stake comes from its 4 delegators (300..304) // in equal parts. lets try to migrate this nominator into delegate based stake. // all balance currently is in 200 - assert_eq!(Balances::free_balance(200), agent_amount); + assert_eq!(Balances::free_balance(agent), agent_amount); // to migrate, nominator needs to set an account as a proxy delegator where staked funds // will be moved and delegated back to this old nominator account. This should be funded // with at least ED. let proxy_delegator = - DelegatedStaking::generate_proxy_delegator(Agent::from(200)).get(); + DelegatedStaking::generate_proxy_delegator(Agent::from(agent)).get(); - assert_ok!(DelegatedStaking::migrate_to_agent(RawOrigin::Signed(200).into(), 201)); + assert_ok!(DelegatedStaking::migrate_to_agent(RawOrigin::Signed(agent).into(), 201)); + // after migration, funds are moved to proxy delegator, still a provider exists. + assert_eq!(System::providers(&agent), 1); + assert_eq!(Balances::free_balance(agent), 0); + // proxy delegator has one provider as well with no free balance. + assert_eq!(System::providers(&proxy_delegator), 1); + assert_eq!(Balances::free_balance(proxy_delegator), 0); // verify all went well let mut expected_proxy_delegated_amount = agent_amount; @@ -659,12 +699,12 @@ mod staking_integration { expected_proxy_delegated_amount ); // stake amount is transferred from delegate to proxy delegator account. - assert_eq!(Balances::free_balance(200), 0); - assert_eq!(Staking::stake(&200).unwrap(), init_stake); - assert_eq!(get_agent_ledger(&200).ledger.effective_balance(), agent_amount); - assert_eq!(get_agent_ledger(&200).available_to_bond(), 0); + assert_eq!(Balances::free_balance(agent), 0); + assert_eq!(Staking::stake(&agent).unwrap(), init_stake); + assert_eq!(get_agent_ledger(&agent).ledger.effective_balance(), agent_amount); + assert_eq!(get_agent_ledger(&agent).available_to_bond(), 0); assert_eq!( - get_agent_ledger(&200).ledger.unclaimed_withdrawals, + get_agent_ledger(&agent).ledger.unclaimed_withdrawals, agent_amount - staked_amount ); @@ -672,14 +712,15 @@ mod staking_integration { let delegator_share = agent_amount / 4; for delegator in 300..304 { assert_eq!(Balances::free_balance(delegator), 0); - // fund them with ED - fund(&delegator, ExistentialDeposit::get()); - // migrate 1/4th amount into each delegator + assert_eq!(System::providers(&delegator), 0); + + // No pre-balance needed to migrate delegator. assert_ok!(DelegatedStaking::migrate_delegation( - RawOrigin::Signed(200).into(), + RawOrigin::Signed(agent).into(), delegator, delegator_share )); + assert_eq!(System::providers(&delegator), 1); assert_eq!( Balances::balance_on_hold(&HoldReason::StakingDelegation.into(), &delegator), delegator_share @@ -694,20 +735,123 @@ mod staking_integration { ); // delegate stake is unchanged. - assert_eq!(Staking::stake(&200).unwrap(), init_stake); - assert_eq!(get_agent_ledger(&200).ledger.effective_balance(), agent_amount); - assert_eq!(get_agent_ledger(&200).available_to_bond(), 0); + assert_eq!(Staking::stake(&agent).unwrap(), init_stake); + assert_eq!(get_agent_ledger(&agent).ledger.effective_balance(), agent_amount); + assert_eq!(get_agent_ledger(&agent).available_to_bond(), 0); assert_eq!( - get_agent_ledger(&200).ledger.unclaimed_withdrawals, + get_agent_ledger(&agent).ledger.unclaimed_withdrawals, agent_amount - staked_amount ); } // cannot use migrate delegator anymore assert_noop!( - DelegatedStaking::migrate_delegation(RawOrigin::Signed(200).into(), 305, 1), + DelegatedStaking::migrate_delegation(RawOrigin::Signed(agent).into(), 305, 1), Error::::NotEnoughFunds ); + + // no provider left on proxy delegator since all funds are migrated + assert_eq!(System::providers(&proxy_delegator), 0); + + // withdraw all delegations from delegators + assert_ok!(Staking::chill(RuntimeOrigin::signed(agent))); + assert_ok!(Staking::unbond(RawOrigin::Signed(agent).into(), staked_amount)); + start_era(4); + assert_ok!(Staking::withdraw_unbonded(RawOrigin::Signed(agent).into(), 0)); + for delegator in 300..304 { + assert_ok!(DelegatedStaking::release_delegation( + RawOrigin::Signed(agent).into(), + delegator, + delegator_share, + 0 + )); + // delegator is cleaned up from storage. + assert!(!Delegators::::contains_key(delegator)); + // has free balance now + assert_eq!(Balances::free_balance(delegator), delegator_share); + // and only one provider as delegator_share > ED + assert_eq!(System::providers(&delegator), 1); + } + + // Agent can be removed now. + assert_ok!(DelegatedStaking::remove_agent(RawOrigin::Signed(agent).into())); + // agent is correctly removed. + assert!(!Agents::::contains_key(agent)); + // and no provider left. + assert_eq!(System::providers(&agent), 0); + }); + } + + #[test] + fn accounts_are_cleaned_up() { + ExtBuilder::default().build_and_execute(|| { + let agent: AccountId = 200; + let reward_acc: AccountId = 201; + let delegator: AccountId = 300; + + // set intention to accept delegation. + fund(&agent, 1000); + + // Agent is provided since it has balance > ED. + assert_eq!(System::providers(&agent), 1); + assert_ok!(DelegatedStaking::register_agent( + RawOrigin::Signed(agent).into(), + reward_acc + )); + // becoming an agent adds another provider. + assert_eq!(System::providers(&agent), 2); + + // delegate to this account + fund(&delegator, 1000); + // account has one provider since its funded. + assert_eq!(System::providers(&delegator), 1); + assert_ok!(DelegatedStaking::delegate_to_agent( + RawOrigin::Signed(delegator).into(), + agent, + 500 + )); + // delegator has an extra provider now. + assert_eq!(System::providers(&delegator), 2); + // all 1000 tokens including ED can be held. + assert_ok!(DelegatedStaking::delegate_to_agent( + RawOrigin::Signed(delegator).into(), + agent, + 500 + )); + // free balance dropping below ED will reduce a provider, but it still has one provider + // left. + assert_eq!(System::providers(&delegator), 1); + + // withdraw all delegation + assert_ok!(Staking::unbond(RawOrigin::Signed(agent).into(), 1000)); + start_era(4); + assert_ok!(Staking::withdraw_unbonded(RawOrigin::Signed(agent).into(), 0)); + + // Since delegations are still left, agents cannot be removed yet from storage. + assert_noop!( + DelegatedStaking::remove_agent(RawOrigin::Signed(agent).into()), + Error::::NotAllowed + ); + + assert_ok!(DelegatedStaking::release_delegation( + RawOrigin::Signed(agent).into(), + delegator, + 1000, + 0 + )); + + // now agents can be removed. + assert_ok!(DelegatedStaking::remove_agent(RawOrigin::Signed(agent).into())); + + // agent and delegator provider is decremented. + assert_eq!(System::providers(&delegator), 1); + assert_eq!(System::providers(&agent), 1); + + // if we transfer all funds, providers are removed. + assert_ok!(Balances::transfer_all(RawOrigin::Signed(delegator).into(), 1337, false)); + assert_ok!(Balances::transfer_all(RawOrigin::Signed(agent).into(), 1337, false)); + assert_eq!(System::providers(&delegator), 0); + assert_eq!(System::providers(&agent), 0); }); } } diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index aff282774c3c..a78aa3f55906 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -64,15 +64,25 @@ impl Delegation { ) } - /// Save self to storage. If the delegation amount is zero, remove the delegation. - pub(crate) fn update_or_kill(self, key: &T::AccountId) { - // Clean up if no delegation left. - if self.amount == Zero::zero() { - >::remove(key); - return + /// Save self to storage. + /// + /// If the delegation amount is zero, remove the delegation. Also adds and removes provider + /// reference as needed. + pub(crate) fn update(self, key: &T::AccountId) { + if >::contains_key(key) { + // Clean up if no delegation left. + if self.amount == Zero::zero() { + >::remove(key); + // Remove provider if no delegation left. + let _ = frame_system::Pallet::::dec_providers(key).defensive(); + return + } + } else { + // this is a new delegation. Provide for this account. + frame_system::Pallet::::inc_providers(key); } - >::insert(key, self) + >::insert(key, self); } } @@ -118,10 +128,24 @@ impl AgentLedger { } /// Save self to storage with the given key. + /// + /// Increments provider count if this is a new agent. pub(crate) fn update(self, key: &T::AccountId) { + if !>::contains_key(key) { + // This is a new agent. Provide for this account. + frame_system::Pallet::::inc_providers(key); + } >::insert(key, self) } + /// Remove self from storage. + pub(crate) fn remove(key: &T::AccountId) { + debug_assert!(>::contains_key(key), "Agent should exist in storage"); + >::remove(key); + // Remove provider reference. + let _ = frame_system::Pallet::::dec_providers(key).defensive(); + } + /// Effective total balance of the `Agent`. /// /// This takes into account any slashes reported to `Agent` but unapplied. diff --git a/substrate/frame/nomination-pools/runtime-api/src/lib.rs b/substrate/frame/nomination-pools/runtime-api/src/lib.rs index 67627e0acb13..d81ad1dd4954 100644 --- a/substrate/frame/nomination-pools/runtime-api/src/lib.rs +++ b/substrate/frame/nomination-pools/runtime-api/src/lib.rs @@ -63,5 +63,11 @@ sp_api::decl_runtime_apis! { /// [`migrate_delegation`](pallet_nomination_pools::Call::migrate_delegation) /// to migrate the funds of the pool member. fn member_needs_delegate_migration(member: AccountId) -> bool; + + /// Returns the total contribution of a pool member including any balance that is unbonding. + fn member_total_balance(who: AccountId) -> Balance; + + /// Total balance contributed to the pool. + fn pool_balance(pool_id: PoolId) -> Balance; } } diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 9108060ccb2a..44e3463dc9f2 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -2010,6 +2010,8 @@ pub mod pallet { pool_id: PoolId, ) -> DispatchResult { let who = ensure_signed(origin)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); ensure!(amount >= MinJoinBond::::get(), Error::::MinimumBondNotMet); // If a member already exists that means they already belong to a pool @@ -2072,6 +2074,13 @@ pub mod pallet { )] pub fn bond_extra(origin: OriginFor, extra: BondExtra>) -> DispatchResult { let who = ensure_signed(origin)?; + + // ensure who is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(who.clone()), + Error::::NotMigrated + ); + Self::do_bond_extra(who.clone(), who, extra) } @@ -2087,6 +2096,12 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::claim_payout())] pub fn claim_payout(origin: OriginFor) -> DispatchResult { let signer = ensure_signed(origin)?; + // ensure signer is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(signer.clone()), + Error::::NotMigrated + ); + Self::do_claim_payout(signer.clone(), signer) } @@ -2130,6 +2145,12 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let member_account = T::Lookup::lookup(member_account)?; + // ensure member is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(member_account.clone()), + Error::::NotMigrated + ); + let (mut member, mut bonded_pool, mut reward_pool) = Self::get_member_with_pools(&member_account)?; @@ -2213,6 +2234,9 @@ pub mod pallet { num_slashing_spans: u32, ) -> DispatchResult { let _ = ensure_signed(origin)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); + let pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; // For now we only allow a pool to withdraw unbonded if its not destroying. If the pool @@ -2259,6 +2283,12 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let caller = ensure_signed(origin)?; let member_account = T::Lookup::lookup(member_account)?; + // ensure member is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(member_account.clone()), + Error::::NotMigrated + ); + let mut member = PoolMembers::::get(&member_account).ok_or(Error::::PoolMemberNotFound)?; let current_era = T::StakeAdapter::current_era(); @@ -2493,6 +2523,8 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); ensure!(bonded_pool.can_nominate(&who), Error::::NotNominator); let depositor_points = PoolMembers::::get(&bonded_pool.roles.depositor) @@ -2527,6 +2559,8 @@ pub mod pallet { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.state != PoolState::Destroying, Error::::CanNotChangeState); + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); if bonded_pool.can_toggle_state(&who) { bonded_pool.set_state(state); @@ -2562,6 +2596,8 @@ pub mod pallet { .can_set_metadata(&who), Error::::DoesNotHavePermission ); + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); Metadata::::mutate(pool_id, |pool_meta| *pool_meta = metadata); @@ -2638,6 +2674,9 @@ pub mod pallet { }, }; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); + match new_root { ConfigOp::Noop => (), ConfigOp::Remove => bonded_pool.roles.root = None, @@ -2684,8 +2723,9 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::chill())] pub fn chill(origin: OriginFor, pool_id: PoolId) -> DispatchResult { let who = ensure_signed(origin)?; - let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); let depositor_points = PoolMembers::::get(&bonded_pool.roles.depositor) .ok_or(Error::::PoolMemberNotFound)? @@ -2720,7 +2760,14 @@ pub mod pallet { extra: BondExtra>, ) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_bond_extra(who, T::Lookup::lookup(member)?, extra) + let member_account = T::Lookup::lookup(member)?; + // ensure member is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(member_account.clone()), + Error::::NotMigrated + ); + + Self::do_bond_extra(who, member_account, extra) } /// Allows a pool member to set a claim permission to allow or disallow permissionless @@ -2737,9 +2784,14 @@ pub mod pallet { permission: ClaimPermission, ) -> DispatchResult { let who = ensure_signed(origin)?; - ensure!(PoolMembers::::contains_key(&who), Error::::PoolMemberNotFound); + // ensure member is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(who.clone()), + Error::::NotMigrated + ); + ClaimPermissions::::mutate(who, |source| { *source = permission; }); @@ -2755,6 +2807,12 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::claim_payout())] pub fn claim_payout_other(origin: OriginFor, other: T::AccountId) -> DispatchResult { let signer = ensure_signed(origin)?; + // ensure member is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(other.clone()), + Error::::NotMigrated + ); + Self::do_claim_payout(signer, other) } @@ -2773,6 +2831,9 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); + ensure!(bonded_pool.can_manage_commission(&who), Error::::DoesNotHavePermission); let mut reward_pool = RewardPools::::get(pool_id) @@ -2809,6 +2870,9 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); + ensure!(bonded_pool.can_manage_commission(&who), Error::::DoesNotHavePermission); bonded_pool.commission.try_update_max(pool_id, max_commission)?; @@ -2831,6 +2895,8 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); ensure!(bonded_pool.can_manage_commission(&who), Error::::DoesNotHavePermission); bonded_pool.commission.try_update_change_rate(change_rate)?; @@ -2852,6 +2918,9 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::claim_commission())] pub fn claim_commission(origin: OriginFor, pool_id: PoolId) -> DispatchResult { let who = ensure_signed(origin)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); + Self::do_claim_commission(who, pool_id) } @@ -2866,6 +2935,9 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::adjust_pool_deposit())] pub fn adjust_pool_deposit(origin: OriginFor, pool_id: PoolId) -> DispatchResult { let who = ensure_signed(origin)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); + Self::do_adjust_pool_deposit(who, pool_id) } @@ -2882,6 +2954,8 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); ensure!(bonded_pool.can_manage_commission(&who), Error::::DoesNotHavePermission); bonded_pool.commission.claim_permission = permission.clone(); @@ -2956,9 +3030,12 @@ pub mod pallet { ); let pool_contribution = member.total_balance(); - ensure!(pool_contribution >= MinJoinBond::::get(), Error::::MinimumBondNotMet); - // the member must have some contribution to be migrated. - ensure!(pool_contribution > Zero::zero(), Error::::AlreadyMigrated); + // ensure the pool contribution is greater than the existential deposit otherwise we + // cannot transfer funds to member account. + ensure!( + pool_contribution >= T::Currency::minimum_balance(), + Error::::MinimumBondNotMet + ); let delegation = T::StakeAdapter::member_delegation_balance(Member::from(member_account.clone())); @@ -3459,6 +3536,7 @@ impl Pallet { fn do_adjust_pool_deposit(who: T::AccountId, pool: PoolId) -> DispatchResult { let bonded_pool = BondedPool::::get(pool).ok_or(Error::::PoolNotFound)?; + let reward_acc = &bonded_pool.reward_account(); let pre_frozen_balance = T::Currency::balance_frozen(&FreezeReason::PoolMinBalance.into(), reward_acc); @@ -3880,7 +3958,13 @@ impl Pallet { return false } + // if pool does not exist, return false. + if !BondedPools::::contains_key(pool_id) { + return false + } + let pool_account = Self::generate_bonded_account(pool_id); + // true if pool is still not migrated to `DelegateStake`. T::StakeAdapter::pool_strategy(Pool::from(pool_account)) != adapter::StakeStrategyType::Delegate @@ -3914,6 +3998,22 @@ impl Pallet { }) .unwrap_or_default() } + + /// Contribution of the member in the pool. + /// + /// Includes balance that is unbonded from staking but not claimed yet from the pool, therefore + /// this balance can be higher than the staked funds. + pub fn api_member_total_balance(who: T::AccountId) -> BalanceOf { + PoolMembers::::get(who.clone()) + .map(|m| m.total_balance()) + .unwrap_or_default() + } + + /// Total balance contributed to the pool. + pub fn api_pool_balance(pool_id: PoolId) -> BalanceOf { + T::StakeAdapter::total_balance(Pool::from(Self::generate_bonded_account(pool_id))) + .unwrap_or_default() + } } impl sp_staking::OnStakingUpdate> for Pallet { diff --git a/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs b/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs index a50a6b73f5fc..7fee2a0bdb23 100644 --- a/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs +++ b/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs @@ -25,16 +25,16 @@ use frame_support::{ }; use mock::*; use pallet_nomination_pools::{ - BondExtra, BondedPools, Error as PoolsError, Event as PoolsEvent, LastPoolId, PoolMember, - PoolMembers, PoolState, + BondExtra, BondedPools, CommissionChangeRate, ConfigOp, Error as PoolsError, + Event as PoolsEvent, LastPoolId, PoolMember, PoolMembers, PoolState, }; use pallet_staking::{ CurrentEra, Error as StakingError, Event as StakingEvent, Payee, RewardDestination, }; -use pallet_delegated_staking::{Error as DelegatedStakingError, Event as DelegatedStakingEvent}; +use pallet_delegated_staking::Event as DelegatedStakingEvent; -use sp_runtime::{bounded_btree_map, traits::Zero}; +use sp_runtime::{bounded_btree_map, traits::Zero, Perbill}; use sp_staking::Agent; #[test] @@ -999,7 +999,6 @@ fn pool_migration_e2e() { LegacyAdapter::set(false); // cannot migrate the member delegation unless pool is migrated first. - assert!(!Pools::api_member_needs_delegate_migration(20)); assert_noop!( Pools::migrate_delegation(RuntimeOrigin::signed(10), 20), PoolsError::::NotMigrated @@ -1031,13 +1030,17 @@ fn pool_migration_e2e() { // move to era 5 when 20 can withdraw unbonded funds. CurrentEra::::set(Some(5)); - // Unbond works even without claiming delegation. Lets unbond 22. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(22), 22, 5)); + + // Cannot unbond without claiming delegation. Lets unbond 22. + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(22), 22, 5), + PoolsError::::NotMigrated + ); // withdraw fails for 20 before claiming delegation assert_noop!( Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 10), - DelegatedStakingError::::NotDelegator + PoolsError::::NotMigrated ); let pre_claim_balance_20 = Balances::total_balance(&20); @@ -1060,17 +1063,11 @@ fn pool_migration_e2e() { assert_eq!( staking_events_since_last_call(), - vec![ - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 5 }, - StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 5 } - ] + vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 5 }] ); assert_eq!( pool_events_since_last_call(), - vec![ - PoolsEvent::Unbonded { member: 22, pool_id: 1, balance: 5, points: 5, era: 8 }, - PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 5, points: 5 }, - ] + vec![PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 5, points: 5 },] ); assert_eq!( delegated_staking_events_since_last_call(), @@ -1113,8 +1110,9 @@ fn pool_migration_e2e() { assert_eq!(Balances::total_balance(&21), pre_migrate_balance_21 + 10); // MIGRATE 22 - let pre_migrate_balance_22 = Balances::total_balance(&22); assert_eq!(Balances::total_balance_on_hold(&22), 0); + // make balance of 22 as 0. + let _ = Balances::make_free_balance_be(&22, 0); // migrate delegation for 22. assert!(Pools::api_member_needs_delegate_migration(22)); @@ -1128,17 +1126,20 @@ fn pool_migration_e2e() { ); // tokens moved to 22's account and held there. - assert_eq!(Balances::total_balance(&22), pre_migrate_balance_22 + 10); + assert_eq!(Balances::total_balance(&22), 10); assert_eq!(Balances::total_balance_on_hold(&22), 10); - // withdraw fails since 22 only unbonds at era 8. + // unbond 22 should work now + assert_ok!(Pools::unbond(RuntimeOrigin::signed(22), 22, 5)); + + // withdraw fails since 22 only unbonds after era 9. assert_noop!( Pools::withdraw_unbonded(RuntimeOrigin::signed(22), 22, 5), PoolsError::::CannotWithdrawAny ); // go to era when 22 can unbond - CurrentEra::::set(Some(10)); + CurrentEra::::set(Some(9)); // withdraw works now assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(22), 22, 10)); @@ -1151,6 +1152,7 @@ fn pool_migration_e2e() { staking_events_since_last_call(), vec![ StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 10 }, + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 5 }, StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 5 } ] ); @@ -1161,6 +1163,7 @@ fn pool_migration_e2e() { PoolsEvent::Withdrawn { member: 21, pool_id: 1, balance: 10, points: 10 }, // 21 was fully unbonding and removed from pool. PoolsEvent::MemberRemoved { member: 21, pool_id: 1, released_balance: 0 }, + PoolsEvent::Unbonded { member: 22, pool_id: 1, balance: 5, points: 5, era: 9 }, PoolsEvent::Withdrawn { member: 22, pool_id: 1, balance: 5, points: 5 }, ] ); @@ -1184,6 +1187,183 @@ fn pool_migration_e2e() { }) } +#[test] +fn disable_pool_operations_on_non_migrated() { + new_test_ext().execute_with(|| { + LegacyAdapter::set(true); + assert_eq!(Balances::minimum_balance(), 5); + assert_eq!(Staking::current_era(), None); + + // create the pool with TransferStake strategy. + assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10)); + assert_eq!(LastPoolId::::get(), 1); + + // have the pool nominate. + assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3])); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Created { depositor: 10, pool_id: 1 }, + PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true }, + ] + ); + + let pre_20 = Balances::free_balance(20); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); + + // verify members balance is moved to pool. + assert_eq!(Balances::free_balance(20), pre_20 - 10); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },] + ); + assert_eq!( + pool_events_since_last_call(), + vec![PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true },] + ); + + // we reset the adapter to `DelegateStake`. + LegacyAdapter::set(false); + + // pool is pending migration. + assert!(Pools::api_pool_needs_delegate_migration(1)); + + // ensure pool mutation is not allowed until pool is migrated. + assert_noop!( + Pools::join(RuntimeOrigin::signed(21), 10, 1), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::pool_withdraw_unbonded(RuntimeOrigin::signed(10), 1, 0), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Blocked), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::set_metadata(RuntimeOrigin::signed(10), 1, vec![1, 1]), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::update_roles( + RuntimeOrigin::signed(10), + 1, + ConfigOp::Set(5), + ConfigOp::Set(6), + ConfigOp::Set(7) + ), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::chill(RuntimeOrigin::signed(10), 1), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::set_commission(RuntimeOrigin::signed(10), 1, None), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::set_commission_max(RuntimeOrigin::signed(10), 1, Zero::zero()), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::set_commission_change_rate( + RuntimeOrigin::signed(10), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 } + ), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::claim_commission(RuntimeOrigin::signed(10), 1), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::adjust_pool_deposit(RuntimeOrigin::signed(10), 1), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::set_commission_claim_permission(RuntimeOrigin::signed(10), 1, None), + PoolsError::::NotMigrated + ); + + // migrate the pool. + assert_ok!(Pools::migrate_pool_to_delegate_stake(RuntimeOrigin::signed(10), 1)); + assert_eq!( + delegated_staking_events_since_last_call(), + vec![DelegatedStakingEvent::Delegated { + agent: POOL1_BONDED, + delegator: DelegatedStaking::generate_proxy_delegator(Agent::from(POOL1_BONDED)) + .get(), + amount: 50 + 10 + },] + ); + + // member is pending migration. + assert!(Pools::api_member_needs_delegate_migration(20)); + + // ensure member mutation is not allowed until member's delegation is migrated. + assert_noop!( + Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::FreeBalance(5)), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::bond_extra_other(RuntimeOrigin::signed(10), 20, BondExtra::Rewards), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::claim_payout(RuntimeOrigin::signed(20)), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(20), 20, 5), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0), + PoolsError::::NotMigrated + ); + + // migrate 20 + assert_ok!(Pools::migrate_delegation(RuntimeOrigin::signed(10), 20)); + // now `bond_extra` for 20 works. + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::FreeBalance(5))); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 5 },] + ); + + assert_eq!( + pool_events_since_last_call(), + vec![PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 5, joined: false },] + ); + + assert_eq!( + delegated_staking_events_since_last_call(), + vec![ + DelegatedStakingEvent::MigratedDelegation { + agent: POOL1_BONDED, + delegator: 20, + amount: 10 + }, + DelegatedStakingEvent::Delegated { agent: POOL1_BONDED, delegator: 20, amount: 5 }, + ] + ); + }) +} + #[test] fn pool_no_dangling_delegation() { new_test_ext().execute_with(|| { @@ -1214,6 +1394,7 @@ fn pool_no_dangling_delegation() { delegated_staking_events_since_last_call(), vec![DelegatedStakingEvent::Delegated { agent: POOL1_BONDED, + delegator: alice, amount: 40 },] From 53f42749ca1a84a9b391388eadbc3a98004708ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 14 Aug 2024 22:09:08 +0200 Subject: [PATCH 114/382] Upgrade accidentally downgraded deps (#5365) --- Cargo.lock | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e3afe21c447..cc8b64457a79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22992,11 +22992,12 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "serde", "serde_json", "wasm-bindgen-macro", @@ -23004,9 +23005,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", @@ -23031,9 +23032,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote 1.0.36", "wasm-bindgen-macro-support", @@ -23041,9 +23042,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2 1.0.82", "quote 1.0.36", @@ -23054,9 +23055,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-bindgen-test" From ebf4f8d2d590f41817d5d38b2d9b5812a46f2342 Mon Sep 17 00:00:00 2001 From: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Thu, 15 Aug 2024 02:10:31 +0200 Subject: [PATCH 115/382] [Pools] fix derivation of pool account (#4999) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes https://github.com/paritytech-secops/srlabs_findings/issues/408. This fixes how ProxyDelegator accounts are derived but may cause issues in Westend since it would use the old derivative accounts. Does not affect Polkadot/Kusama as this pallet is not deployed to them yet. --------- Co-authored-by: Gonçalo Pestana --- Cargo.lock | 1 + polkadot/runtime/westend/src/lib.rs | 15 +-- prdoc/pr_4999.prdoc | 18 +++ substrate/frame/delegated-staking/Cargo.toml | 3 + substrate/frame/delegated-staking/src/lib.rs | 20 +++- .../frame/delegated-staking/src/migration.rs | 107 ++++++++++++++++++ 6 files changed, 152 insertions(+), 12 deletions(-) create mode 100644 prdoc/pr_4999.prdoc create mode 100644 substrate/frame/delegated-staking/src/migration.rs diff --git a/Cargo.lock b/Cargo.lock index cc8b64457a79..76973e3397b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10489,6 +10489,7 @@ dependencies = [ "frame-election-provider-support", "frame-support", "frame-system", + "log", "pallet-balances", "pallet-nomination-pools", "pallet-staking", diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index a9fbbb4f33da..5f8d5d424938 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1760,11 +1760,8 @@ pub type SignedExtra = ( ); parameter_types! { - // This is the max pools that will be migrated in the runtime upgrade. Westend has more pools - // than this, but we want to emulate some non migrated pools. In prod runtimes, if weight is not - // a concern, it is recommended to set to (existing pools + 10) to also account for any new - // pools getting created before the migration is actually executed. - pub const MaxPoolsToMigrate: u32 = 250; + /// Bounding number of agent pot accounts to be migrated in a single block. + pub const MaxAgentsToMigrate: u32 = 300; } /// All migrations that will run on the next runtime upgrade. @@ -1798,13 +1795,11 @@ pub mod migrations { /// Unreleased migrations. Add new ones here: pub type Unreleased = ( - // Migrate NominationPools to `DelegateStake` adapter. This is unversioned upgrade and - // should not be applied yet in Kusama/Polkadot. - pallet_nomination_pools::migration::unversioned::DelegationStakeMigration< + // This is only needed for Westend. + pallet_delegated_staking::migration::unversioned::ProxyDelegatorMigration< Runtime, - MaxPoolsToMigrate, + MaxAgentsToMigrate, >, - pallet_staking::migrations::v15::MigrateV14ToV15, ); } diff --git a/prdoc/pr_4999.prdoc b/prdoc/pr_4999.prdoc new file mode 100644 index 000000000000..d396fcdbe8b3 --- /dev/null +++ b/prdoc/pr_4999.prdoc @@ -0,0 +1,18 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fixes entropy for derivation of proxy delegator account. + +doc: + - audience: Runtime Dev + description: | + This fixes how ProxyDelegator accounts are derived but may cause issues in Westend since it would use the old + derivative accounts. Does not affect Polkadot/Kusama as this pallet is not deployed to them yet. + +crates: + - name: westend-runtime + bump: patch + - name: pallet-delegated-staking + bump: patch + - name: pallet-nomination-pools + bump: patch \ No newline at end of file diff --git a/substrate/frame/delegated-staking/Cargo.toml b/substrate/frame/delegated-staking/Cargo.toml index 1bd17c59f1e7..8d5ccd342b6b 100644 --- a/substrate/frame/delegated-staking/Cargo.toml +++ b/substrate/frame/delegated-staking/Cargo.toml @@ -18,6 +18,8 @@ frame-system = { workspace = true } scale-info = { features = ["derive"], workspace = true } sp-runtime = { workspace = true } sp-staking = { workspace = true } +sp-io = { workspace = true } +log = { workspace = true } [dev-dependencies] sp-core = { workspace = true, default-features = true } @@ -38,6 +40,7 @@ std = [ "frame-election-provider-support/std", "frame-support/std", "frame-system/std", + "log/std", "pallet-balances/std", "pallet-nomination-pools/std", "pallet-staking/std", diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 08ce747ec0c0..7b8d14b0a611 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -126,6 +126,7 @@ #![deny(rustdoc::broken_intra_doc_links)] mod impls; +pub mod migration; #[cfg(test)] mod mock; #[cfg(test)] @@ -152,12 +153,25 @@ use frame_support::{ Defensive, DefensiveOption, Imbalance, OnUnbalanced, }, }; +use sp_io::hashing::blake2_256; use sp_runtime::{ - traits::{AccountIdConversion, CheckedAdd, CheckedSub, Zero}, + traits::{CheckedAdd, CheckedSub, TrailingZeroInput, Zero}, ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, }; use sp_staking::{Agent, Delegator, EraIndex, StakingInterface, StakingUnchecked}; +/// The log target of this pallet. +pub const LOG_TARGET: &str = "runtime::delegated-staking"; +// syntactic sugar for logging. +#[macro_export] +macro_rules! log { + ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { + log::$level!( + target: $crate::LOG_TARGET, + concat!("[{:?}] 🏊‍♂️ ", $patter), >::block_number() $(, $values)* + ) + }; +} pub type BalanceOf = <::Currency as FunInspect<::AccountId>>::Balance; @@ -463,7 +477,9 @@ impl Pallet { /// Derive a (keyless) pot account from the given agent account and account type. fn sub_account(account_type: AccountType, acc: T::AccountId) -> T::AccountId { - T::PalletId::get().into_sub_account_truncating((account_type, acc.clone())) + let entropy = (T::PalletId::get(), acc, account_type).using_encoded(blake2_256); + Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) + .expect("infinite length input; no invalid inputs for type; qed") } /// Held balance of a delegator. diff --git a/substrate/frame/delegated-staking/src/migration.rs b/substrate/frame/delegated-staking/src/migration.rs new file mode 100644 index 000000000000..8bc7312c4eab --- /dev/null +++ b/substrate/frame/delegated-staking/src/migration.rs @@ -0,0 +1,107 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use frame_support::traits::OnRuntimeUpgrade; + +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + +pub mod unversioned { + use super::*; + #[cfg(feature = "try-runtime")] + use alloc::vec::Vec; + use sp_runtime::traits::AccountIdConversion; + + /// Migrates `ProxyDelegator` accounts with better entropy than the old logic which didn't take + /// into account all the bytes of the agent account ID. + pub struct ProxyDelegatorMigration(PhantomData<(T, MaxAgents)>); + + impl> OnRuntimeUpgrade for ProxyDelegatorMigration { + fn on_runtime_upgrade() -> Weight { + let mut weight = Weight::zero(); + let old_proxy_delegator = |agent: T::AccountId| { + T::PalletId::get() + .into_sub_account_truncating((AccountType::ProxyDelegator, agent.clone())) + }; + + Agents::::iter_keys().take(MaxAgents::get() as usize).for_each(|agent| { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 0)); + let old_proxy = old_proxy_delegator(agent.clone()); + + // if delegation does not exist, it does not need to be migrated. + if let Some(delegation) = Delegation::::get(&old_proxy) { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 0)); + + let new_proxy = + Pallet::::generate_proxy_delegator(Agent::from(agent.clone())); + + // accrue read writes for `do_migrate_delegation` + weight.saturating_accrue(T::DbWeight::get().reads_writes(8, 8)); + let _ = Pallet::::do_migrate_delegation( + Delegator::from(old_proxy.clone()), + new_proxy.clone(), + delegation.amount, + ) + .map_err(|e| { + log!( + error, + "Failed to migrate old proxy delegator {:?} to new proxy {:?} for agent {:?} with error: {:?}", + old_proxy, + new_proxy, + agent, + e, + ); + }); + }; + }); + + log!(info, "Finished migrating old proxy delegator accounts to new ones"); + weight + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_data: Vec) -> Result<(), TryRuntimeError> { + let mut unmigrated_count = 0; + let old_proxy_delegator = |agent: T::AccountId| { + T::PalletId::get() + .into_sub_account_truncating((AccountType::ProxyDelegator, agent.clone())) + }; + + Agents::::iter_keys().take(MaxAgents::get() as usize).for_each(|agent| { + let old_proxy: T::AccountId = old_proxy_delegator(agent.clone()); + let held_balance = Pallet::::held_balance_of(Delegator::from(old_proxy.clone())); + let delegation = Delegation::::get(&old_proxy); + if delegation.is_some() || !held_balance.is_zero() { + log!( + error, + "Old proxy delegator {:?} for agent {:?} is not migrated.", + old_proxy, + agent, + ); + unmigrated_count += 1; + } + }); + + if unmigrated_count > 0 { + Err(TryRuntimeError::Other("Some old proxy delegator accounts are not migrated.")) + } else { + Ok(()) + } + } + } +} From 78c3daabd97367f70c1ebc0d7fe55abef4d76952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=B3nal=20Murray?= Date: Thu, 15 Aug 2024 10:02:56 +0100 Subject: [PATCH 116/382] [Coretime] Always include UnpaidExecution, not just when revenue is > 0 (#5369) The NotifyRevenue XCM from relay to coretime chain fails to pass the barrier when revenue is 0. https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/runtime/parachains/src/coretime/mod.rs#L401 pushes notifyrevenue onto an [empty vec](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/runtime/parachains/src/coretime/mod.rs#L361) when `revenue == 0`, so it never explicitly requests unpaid execution, because that happens only in [the block where revenue is `> 0`](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/runtime/parachains/src/coretime/mod.rs#L387). This will need to be backported to 1.14 when merged. --- polkadot/runtime/parachains/src/coretime/mod.rs | 9 ++++----- prdoc/pr_5369.prdoc | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 prdoc/pr_5369.prdoc diff --git a/polkadot/runtime/parachains/src/coretime/mod.rs b/polkadot/runtime/parachains/src/coretime/mod.rs index fbd8935f1990..9b9bdb86878f 100644 --- a/polkadot/runtime/parachains/src/coretime/mod.rs +++ b/polkadot/runtime/parachains/src/coretime/mod.rs @@ -358,7 +358,10 @@ fn mk_coretime_call(call: crate::coretime::CoretimeCalls) -> Instruct fn do_notify_revenue(when: BlockNumber, raw_revenue: Balance) -> Result<(), XcmError> { let dest = Junction::Parachain(T::BrokerId::get()).into_location(); - let mut message = Vec::new(); + let mut message = vec![Instruction::UnpaidExecution { + weight_limit: WeightLimit::Unlimited, + check_origin: None, + }]; let asset = Asset { id: AssetId(Location::here()), fun: Fungible(raw_revenue) }; let dummy_xcm_context = XcmContext { origin: None, message_id: [0; 32], topic: None }; @@ -384,10 +387,6 @@ fn do_notify_revenue(when: BlockNumber, raw_revenue: Balance) -> Resu message.extend( [ - Instruction::UnpaidExecution { - weight_limit: WeightLimit::Unlimited, - check_origin: None, - }, ReceiveTeleportedAsset(assets_reanchored), DepositAsset { assets: Wild(AllCounted(1)), diff --git a/prdoc/pr_5369.prdoc b/prdoc/pr_5369.prdoc new file mode 100644 index 000000000000..1baa5e1cbe7d --- /dev/null +++ b/prdoc/pr_5369.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix failing XCM from relay to Coretime Chain when revenue is zero + +doc: + - audience: Runtime Dev + description: | + The coretime assigner now always includes UnpaidExecution when calling `notify_revenue` via a + `Transact`, not just when revenue is nonzero. This fixes an issue where the XCM would fail to + process on the receiving side. + +crates: + - name: polkadot-runtime-parachains + bump: patch From e91f1463884946fa1c11b1994fc6bb121de57091 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 11:21:18 +0200 Subject: [PATCH 117/382] Bump trie-db from 0.29.0 to 0.29.1 (#5231) Bumps [trie-db](https://github.com/paritytech/trie) from 0.29.0 to 0.29.1.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=trie-db&package-manager=cargo&previous-version=0.29.0&new-version=0.29.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76973e3397b3..2bad3947edfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22540,9 +22540,9 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65ed83be775d85ebb0e272914fff6462c39b3ddd6dc67b5c1c41271aad280c69" +checksum = "0c992b4f40c234a074d48a757efeabb1a6be88af84c0c23f7ca158950cb0ae7f" dependencies = [ "hash-db", "log", diff --git a/Cargo.toml b/Cargo.toml index 74b12f96603a..edb425e619c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1319,7 +1319,7 @@ tracing-log = { version = "0.2.0" } tracing-subscriber = { version = "0.3.18" } tracking-allocator = { path = "polkadot/node/tracking-allocator", default-features = false, package = "staging-tracking-allocator" } trie-bench = { version = "0.39.0" } -trie-db = { version = "0.29.0", default-features = false } +trie-db = { version = "0.29.1", default-features = false } trie-root = { version = "0.18.0", default-features = false } trie-standardmap = { version = "0.16.0" } trybuild = { version = "1.0.89" } From 048f4b8e95d4fe75c9562acc8dfa3cfc0fd33d31 Mon Sep 17 00:00:00 2001 From: Iker <34474035+IkerAlus@users.noreply.github.com> Date: Thu, 15 Aug 2024 11:04:31 +0100 Subject: [PATCH 118/382] Update Identity pallet README.md (#5183) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update Identity pallet README.md according to the up-to-date docs, particularly to explain the _username_ concept of the pallet. --------- Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Bastian Köcher --- substrate/frame/identity/README.md | 43 +++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/substrate/frame/identity/README.md b/substrate/frame/identity/README.md index 0203656eff46..94b2ae0231d7 100644 --- a/substrate/frame/identity/README.md +++ b/substrate/frame/identity/README.md @@ -1,9 +1,11 @@ -# Identity Module +# pallet-identity -- [`identity::Config`](https://docs.rs/pallet-identity/latest/pallet_identity/trait.Config.html) -- [`Call`](https://docs.rs/pallet-identity/latest/pallet_identity/enum.Call.html) +## Identity Pallet -## Overview +- [`Config`] +- [`Call`] + +### Overview A federated naming system, allowing for multiple registrars to be added from a specified origin. Registrars can set a fee to provide identity-verification service. Anyone can put forth a @@ -23,32 +25,53 @@ by definition, these have equivalent ownership and each has an individual name. The number of registrars should be limited, and the deposit made sufficiently large, to ensure no state-bloat attack is viable. -## Interface +#### Usernames + +The pallet provides functionality for username authorities to issue usernames. When an account +receives a username, they get a default instance of `IdentityInfo`. Usernames also serve as a +reverse lookup from username to account. + +Username authorities are given an allocation by governance to prevent state bloat. Usernames +impose no cost or deposit on the user. -### Dispatchable Functions +Users can have multiple usernames that map to the same `AccountId`, however one `AccountId` can +only map to a single username, known as the *primary*. -#### For general users +### Interface + +#### Dispatchable Functions + +##### For General Users - `set_identity` - Set the associated identity of an account; a small deposit is reserved if not already taken. - `clear_identity` - Remove an account's associated identity; the deposit is returned. - `request_judgement` - Request a judgement from a registrar, paying a fee. - `cancel_request` - Cancel the previous request for a judgement. +- `accept_username` - Accept a username issued by a username authority. +- `remove_expired_approval` - Remove a username that was issued but never accepted. +- `set_primary_username` - Set a given username as an account's primary. +- `remove_dangling_username` - Remove a username that maps to an account without an identity. -#### For general users with sub-identities +##### For General Users with Sub-Identities - `set_subs` - Set the sub-accounts of an identity. - `add_sub` - Add a sub-identity to an identity. - `remove_sub` - Remove a sub-identity of an identity. - `rename_sub` - Rename a sub-identity of an identity. - `quit_sub` - Remove a sub-identity of an identity (called by the sub-identity). -#### For registrars +##### For Registrars - `set_fee` - Set the fee required to be paid for a judgement to be given by the registrar. - `set_fields` - Set the fields that a registrar cares about in their judgements. - `provide_judgement` - Provide a judgement to an identity. -#### For super-users +##### For Username Authorities +- `set_username_for` - Set a username for a given account. The account must approve it. + +##### For Superusers - `add_registrar` - Add a new registrar to the system. - `kill_identity` - Forcibly remove the associated identity; the deposit is lost. +- `add_username_authority` - Add an account with the ability to issue usernames. +- `remove_username_authority` - Remove an account with the ability to issue usernames. [`Call`]: ./enum.Call.html [`Config`]: ./trait.Config.html From 63bf73d5c5a0f89f7eeedfc268fe4be2c14123de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 15 Aug 2024 12:45:19 +0200 Subject: [PATCH 119/382] Aura: Ensure we are building on each relay chain fork (#5352) We only want to build one block per slot for Aura on parachains. However, we still need to build on each relay chain fork, which is using the same slot. Closes: https://github.com/paritytech/polkadot-sdk/issues/5349 --------- Co-authored-by: Davide Galassi Co-authored-by: Sebastian Kunert --- cumulus/client/consensus/aura/src/collators/basic.rs | 8 ++++++-- prdoc/pr_5352.prdoc | 10 ++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_5352.prdoc diff --git a/cumulus/client/consensus/aura/src/collators/basic.rs b/cumulus/client/consensus/aura/src/collators/basic.rs index 4efd50a04ec6..0f9583cd0eb0 100644 --- a/cumulus/client/consensus/aura/src/collators/basic.rs +++ b/cumulus/client/consensus/aura/src/collators/basic.rs @@ -138,6 +138,7 @@ where }; let mut last_processed_slot = 0; + let mut last_relay_chain_block = Default::default(); while let Some(request) = collation_requests.next().await { macro_rules! reject_with_error { @@ -215,11 +216,13 @@ where // // Most parachains currently run with 12 seconds slots and thus, they would try to // produce multiple blocks per slot which very likely would fail on chain. Thus, we have - // this "hack" to only produce on block per slot. + // this "hack" to only produce one block per slot per relay chain fork. // // With https://github.com/paritytech/polkadot-sdk/issues/3168 this implementation will be // obsolete and also the underlying issue will be fixed. - if last_processed_slot >= *claim.slot() { + if last_processed_slot >= *claim.slot() && + last_relay_chain_block < *relay_parent_header.number() + { continue } @@ -261,6 +264,7 @@ where } last_processed_slot = *claim.slot(); + last_relay_chain_block = *relay_parent_header.number(); } } } diff --git a/prdoc/pr_5352.prdoc b/prdoc/pr_5352.prdoc new file mode 100644 index 000000000000..4b055d81cb51 --- /dev/null +++ b/prdoc/pr_5352.prdoc @@ -0,0 +1,10 @@ +title: "Aura: Ensure parachains are building on all relay chain forks" + +doc: + - audience: Node Dev + description: | + Ensure that parachains using the `basic` collator are building on all relay chain forks. + +crates: + - name: cumulus-client-consensus-aura + bump: patch From a6dffe390c24011fcf3fabab83c46d82f9410400 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Thu, 15 Aug 2024 13:48:17 +0200 Subject: [PATCH 120/382] Correct some typos in crates' descriptions (#5262) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- cumulus/client/pov-recovery/Cargo.toml | 2 +- cumulus/pallets/aura-ext/src/lib.rs | 2 +- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 2 +- prdoc/pr_5262.prdoc | 25 +++++++++++++++++++ substrate/frame/try-runtime/Cargo.toml | 2 +- substrate/frame/whitelist/Cargo.toml | 2 +- umbrella/src/lib.rs | 6 ++--- 7 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 prdoc/pr_5262.prdoc diff --git a/cumulus/client/pov-recovery/Cargo.toml b/cumulus/client/pov-recovery/Cargo.toml index a95b24bc2933..3127dd26fcaa 100644 --- a/cumulus/client/pov-recovery/Cargo.toml +++ b/cumulus/client/pov-recovery/Cargo.toml @@ -2,7 +2,7 @@ name = "cumulus-client-pov-recovery" version = "0.7.0" authors.workspace = true -description = "Cumulus-specific networking protocol" +description = "Parachain PoV recovery" edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/cumulus/pallets/aura-ext/src/lib.rs b/cumulus/pallets/aura-ext/src/lib.rs index 4c9e61458a87..dc854eb82018 100644 --- a/cumulus/pallets/aura-ext/src/lib.rs +++ b/cumulus/pallets/aura-ext/src/lib.rs @@ -16,7 +16,7 @@ //! Cumulus extension pallet for AuRa //! -//! This pallets extends the Substrate AuRa pallet to make it compatible with parachains. It +//! This pallet extends the Substrate AuRa pallet to make it compatible with parachains. It //! provides the [`Pallet`], the [`Config`] and the [`GenesisConfig`]. //! //! It is also required that the parachain runtime uses the provided [`BlockExecutor`] to properly diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index f3e03aa74300..f85861bdaa54 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -3,7 +3,7 @@ name = "bridge-hub-rococo-runtime" version = "0.5.0" authors.workspace = true edition.workspace = true -description = "Rococo's BridgeHub parachain runtime" +description = "Rococo's BridgeHub parachain runtime" license = "Apache-2.0" [lints] diff --git a/prdoc/pr_5262.prdoc b/prdoc/pr_5262.prdoc new file mode 100644 index 000000000000..828f0ffeb1bc --- /dev/null +++ b/prdoc/pr_5262.prdoc @@ -0,0 +1,25 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Correct some typos in crates' descriptions + +doc: + - audience: Runtime Dev + description: | + Corrected typos and copy-paste errors in crates' descriptions. + +crates: + - name: cumulus-client-pov-recovery + bump: patch + - name: cumulus-pallet-aura-ext + bump: patch + - name: bridge-hub-rococo-runtime + bump: patch + - name: frame-try-runtime + bump: patch + - name: pallet-whitelist + bump: patch + - name: polkadot-sdk + bump: patch + - name: polkadot-runtime-parachains + bump: none diff --git a/substrate/frame/try-runtime/Cargo.toml b/substrate/frame/try-runtime/Cargo.toml index 2bd791f52385..7f7d1f2b50e0 100644 --- a/substrate/frame/try-runtime/Cargo.toml +++ b/substrate/frame/try-runtime/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "Apache-2.0" homepage.workspace = true repository.workspace = true -description = "FRAME pallet for democracy" +description = "Supporting types for try-runtime, testing and dry-running commands." [lints] workspace = true diff --git a/substrate/frame/whitelist/Cargo.toml b/substrate/frame/whitelist/Cargo.toml index e910fc323714..a347174ed2eb 100644 --- a/substrate/frame/whitelist/Cargo.toml +++ b/substrate/frame/whitelist/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "Apache-2.0" homepage.workspace = true repository.workspace = true -description = "FRAME pallet for whitelisting call, and dispatch from specific origin" +description = "FRAME pallet for whitelisting calls, and dispatching from a specific origin" [lints] workspace = true diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 07f1cbad1262..7bdfcd0b03ae 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -109,7 +109,7 @@ pub use cumulus_client_network; #[cfg(feature = "cumulus-client-parachain-inherent")] pub use cumulus_client_parachain_inherent; -/// Cumulus-specific networking protocol. +/// Parachain PoV recovery. #[cfg(feature = "cumulus-client-pov-recovery")] pub use cumulus_client_pov_recovery; @@ -272,7 +272,7 @@ pub use frame_system_benchmarking; #[cfg(feature = "frame-system-rpc-runtime-api")] pub use frame_system_rpc_runtime_api; -/// FRAME pallet for democracy. +/// Supporting types for try-runtime, testing and dry-running commands. #[cfg(feature = "frame-try-runtime")] pub use frame_try_runtime; @@ -689,7 +689,7 @@ pub use pallet_utility; #[cfg(feature = "pallet-vesting")] pub use pallet_vesting; -/// FRAME pallet for whitelisting call, and dispatch from specific origin. +/// FRAME pallet for whitelisting calls, and dispatching from a specific origin. #[cfg(feature = "pallet-whitelist")] pub use pallet_whitelist; From 069f8a6a374108affa4e240685c7504a1fbd1397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFs?= <49660929+SailorSnoW@users.noreply.github.com> Date: Thu, 15 Aug 2024 14:33:32 +0200 Subject: [PATCH 121/382] fix visibility for `pallet_nfts` types used as call arguments (#3634) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix #3631 Types which are impacted and fixed here are `ItemTip`, `PriceWithDirection`, `PreSignedMint`, `PreSignedAttributes`. Co-authored-by: Bastian Köcher --- substrate/frame/nfts/src/types.rs | 36 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/substrate/frame/nfts/src/types.rs b/substrate/frame/nfts/src/types.rs index 1687a03520af..60d7c639c88c 100644 --- a/substrate/frame/nfts/src/types.rs +++ b/substrate/frame/nfts/src/types.rs @@ -193,13 +193,13 @@ pub struct ItemMetadata> { #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct ItemTip { /// The collection of the item. - pub(super) collection: CollectionId, + pub collection: CollectionId, /// An item of which the tip is sent for. - pub(super) item: ItemId, + pub item: ItemId, /// A sender of the tip. - pub(super) receiver: AccountId, + pub receiver: AccountId, /// An amount the sender is willing to tip. - pub(super) amount: Amount, + pub amount: Amount, } /// Information about the pending swap. @@ -246,9 +246,9 @@ pub enum PriceDirection { #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct PriceWithDirection { /// An amount. - pub(super) amount: Amount, + pub amount: Amount, /// A direction (send or receive). - pub(super) direction: PriceDirection, + pub direction: PriceDirection, } /// Support for up to 64 user-enabled features on a collection. @@ -518,31 +518,31 @@ impl_codec_bitflags!(CollectionRoles, u8, CollectionRole); #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct PreSignedMint { /// A collection of the item to be minted. - pub(super) collection: CollectionId, + pub collection: CollectionId, /// Item's ID. - pub(super) item: ItemId, + pub item: ItemId, /// Additional item's key-value attributes. - pub(super) attributes: Vec<(Vec, Vec)>, + pub attributes: Vec<(Vec, Vec)>, /// Additional item's metadata. - pub(super) metadata: Vec, + pub metadata: Vec, /// Restrict the claim to a particular account. - pub(super) only_account: Option, + pub only_account: Option, /// A deadline for the signature. - pub(super) deadline: Deadline, + pub deadline: Deadline, /// An optional price the claimer would need to pay for the mint. - pub(super) mint_price: Option, + pub mint_price: Option, } #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct PreSignedAttributes { /// Collection's ID. - pub(super) collection: CollectionId, + pub collection: CollectionId, /// Item's ID. - pub(super) item: ItemId, + pub item: ItemId, /// Key-value attributes. - pub(super) attributes: Vec<(Vec, Vec)>, + pub attributes: Vec<(Vec, Vec)>, /// Attributes' namespace. - pub(super) namespace: AttributeNamespace, + pub namespace: AttributeNamespace, /// A deadline for the signature. - pub(super) deadline: Deadline, + pub deadline: Deadline, } From b5029eb4fd6c7ffd8164b2fe12b71bad0c59c9f2 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Thu, 15 Aug 2024 16:55:49 +0200 Subject: [PATCH 122/382] Update links in the documentation (#5175) - Where applicable, use a regular [`reference`] instead of `../../../reference/index.html`. - Typos. - Update a link to `polkadot-evm` which has moved out of the monorepo. - ~~The link specification for `chain_spec_builder` is invalid~~ (actually it was valid) - it works fine without it. Part of https://github.com/paritytech/eng-automation/issues/10 --- Cargo.lock | 1 + docs/sdk/Cargo.toml | 1 + docs/sdk/src/guides/your_first_pallet/mod.rs | 18 +++++++++--------- .../src/reference_docs/chain_spec_genesis.rs | 1 - .../runtime_vs_smart_contract.rs | 6 +++--- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2bad3947edfb..df01a143efb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14565,6 +14565,7 @@ dependencies = [ "pallet-balances", "pallet-broker", "pallet-collective", + "pallet-contracts", "pallet-default-config-example", "pallet-democracy", "pallet-example-offchain-worker", diff --git a/docs/sdk/Cargo.toml b/docs/sdk/Cargo.toml index 2f85171bb93d..424758c32b34 100644 --- a/docs/sdk/Cargo.toml +++ b/docs/sdk/Cargo.toml @@ -22,6 +22,7 @@ frame = { features = [ "runtime", ], workspace = true, default-features = true } pallet-examples = { workspace = true } +pallet-contracts = { workspace = true } pallet-default-config-example = { workspace = true, default-features = true } pallet-example-offchain-worker = { workspace = true, default-features = true } diff --git a/docs/sdk/src/guides/your_first_pallet/mod.rs b/docs/sdk/src/guides/your_first_pallet/mod.rs index 3c74469e1768..006d0be7ded3 100644 --- a/docs/sdk/src/guides/your_first_pallet/mod.rs +++ b/docs/sdk/src/guides/your_first_pallet/mod.rs @@ -21,7 +21,7 @@ //! this guide, namely the crate/package names, based on which template you use. //! //! > Be aware that you can read the entire source code backing this tutorial by clicking on the -//! > [`source`](./mod.rs.html) button at the top right of the page. +//! > `source` button at the top right of the page. //! //! You should have studied the following modules as a prelude to this guide: //! @@ -45,7 +45,7 @@ //! Consider the following as a "shell pallet". We continue building the rest of this pallet based //! on this template. //! -//! [`pallet::config`] and [`pallet::pallet`](frame_support::pallet) are both mandatory parts of any +//! [`pallet::config`] and [`pallet::pallet`] are both mandatory parts of any //! pallet. Refer to the documentation of each to get an overview of what they do. #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", shell_pallet)] //! @@ -319,13 +319,13 @@ //! - Learn more about the individual pallet items/macros, such as event and errors and call, in //! [`frame::pallet_macros`]. //! -//! [`pallet::storage`]: ../../../frame_support/pallet_macros/attr.config.html -//! [`pallet::call`]: ../../../frame_support/pallet_macros/attr.call.html -//! [`pallet::event`]: ../../../frame_support/pallet_macros/attr.event.html -//! [`pallet::error`]: ../../../frame_support/pallet_macros/attr.error.html -//! [`pallet::pallet`]: ../../../frame_support/pallet_macros/attr.pallet.html -//! [`pallet::config`]: ../../../frame_support/pallet_macros/attr.config.html -//! [`pallet::generate_deposit`]: ../../../frame_support/pallet_macros/attr.generate_deposit.html +//! [`pallet::storage`]: frame_support::pallet_macros::storage +//! [`pallet::call`]: frame_support::pallet_macros::call +//! [`pallet::event`]: frame_support::pallet_macros::event +//! [`pallet::error`]: frame_support::pallet_macros::error +//! [`pallet::pallet`]: frame_support::pallet +//! [`pallet::config`]: frame_support::pallet_macros::config +//! [`pallet::generate_deposit`]: frame_support::pallet_macros::generate_deposit #[docify::export] #[frame::pallet(dev_mode)] diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index 557795cb410c..39e5993d020f 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -178,7 +178,6 @@ //! [`pallet::genesis_build`]: frame_support::pallet_macros::genesis_build //! [`pallet::genesis_config`]: frame_support::pallet_macros::genesis_config //! [`BuildGenesisConfig`]: frame_support::traits::BuildGenesisConfig -//! [`chain_spec_builder`]: ../../../staging_chain_spec_builder/index.html //! [`serde`]: https://serde.rs/field-attrs.html //! [`get_storage_for_patch`]: sc_chain_spec::GenesisConfigBuilderRuntimeCaller::get_storage_for_patch //! [`GenesisBuilder::get_preset`]: sp_genesis_builder::GenesisBuilder::get_preset diff --git a/docs/sdk/src/reference_docs/runtime_vs_smart_contract.rs b/docs/sdk/src/reference_docs/runtime_vs_smart_contract.rs index 4a0ba3ca48f5..c91b66b944c6 100644 --- a/docs/sdk/src/reference_docs/runtime_vs_smart_contract.rs +++ b/docs/sdk/src/reference_docs/runtime_vs_smart_contract.rs @@ -20,8 +20,8 @@ //! #### Smart Contracts in Substrate //! Smart Contracts are autonomous, programmable constructs deployed on the blockchain. //! In [FRAME](frame), Smart Contracts infrastructure is implemented by the -//! [`pallet_contracts`](../../../pallet_contracts/index.html) for WASM-based contracts or the -//! [`pallet_evm`](../../../pallet_evm/index.html) for EVM-compatible contracts. These pallets +//! [`pallet_contracts`] for WASM-based contracts or the +//! [`pallet_evm`](https://github.com/polkadot-evm/frontier/tree/master/frame/evm) for EVM-compatible contracts. These pallets //! enable Smart Contract developers to build applications and systems on top of a Substrate-based //! blockchain. //! @@ -108,7 +108,7 @@ //! - **Deployment and Iteration**: Smart Contracts, by nature, are designed for more //! straightforward deployment and iteration. Developers can quickly deploy contracts. //! - **Contract Code Updates**: Once deployed, although typically immutable, Smart Contracts can be -//! upgraded, but lack of migration logic. The [pallet_contracts](../../../pallet_contracts/index.html) +//! upgraded, but lack of migration logic. The [`pallet_contracts`] //! allows for contracts to be upgraded by exposing the `set_code` dispatchable. More details on this //! can be found in [Ink! documentation on upgradeable contracts](https://use.ink/basics/upgradeable-contracts). //! - **Isolated Impact**: Upgrades or changes to a smart contract generally impact only that From 41a679c466d2da1c5760405198c09bb2d320a106 Mon Sep 17 00:00:00 2001 From: Yuri Volkov <0@mcornholio.ru> Date: Thu, 15 Aug 2024 17:30:29 +0200 Subject: [PATCH 123/382] Moving cargo check for runtimes to GHA (#5340) Fixes https://github.com/paritytech/ci_cd/issues/1015 --- .../actions/cargo-check-runtimes/action.yml | 22 +++ .../workflows/check-cargo-check-runtimes.yml | 136 ++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 .github/actions/cargo-check-runtimes/action.yml create mode 100644 .github/workflows/check-cargo-check-runtimes.yml diff --git a/.github/actions/cargo-check-runtimes/action.yml b/.github/actions/cargo-check-runtimes/action.yml new file mode 100644 index 000000000000..869f17661e4a --- /dev/null +++ b/.github/actions/cargo-check-runtimes/action.yml @@ -0,0 +1,22 @@ +name: 'cargo check runtimes' +description: 'Runs `cargo check` for every directory in provided root.' +inputs: + root: + description: "Root directory. Expected to contain several cargo packages inside." + required: true +runs: + using: "composite" + steps: + - name: Check + shell: bash + run: | + mkdir -p ~/.forklift + cp .forklift/config.toml ~/.forklift/config.toml + cd ${{ inputs.root }} + for directory in $(echo */); do + echo "_____Running cargo check for ${directory} ______"; + cd ${directory}; + pwd; + SKIP_WASM_BUILD=1 forklift cargo check --locked; + cd ..; + done diff --git a/.github/workflows/check-cargo-check-runtimes.yml b/.github/workflows/check-cargo-check-runtimes.yml new file mode 100644 index 000000000000..ebcf6c5fc9bd --- /dev/null +++ b/.github/workflows/check-cargo-check-runtimes.yml @@ -0,0 +1,136 @@ +name: Check Cargo Check Runtimes + +on: + pull_request: + types: [ opened, synchronize, reopened, ready_for_review, labeled ] + + +# Jobs in this workflow depend on each other, only for limiting peak amount of spawned workers + +jobs: + # GitHub Actions allows using 'env' in a container context. + # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 + # This workaround sets the container image for each job using 'set-image' job output. + set-image: + if: contains(github.event.label.name, 'GHA-migration') || contains(github.event.pull_request.labels.*.name, 'GHA-migration') + runs-on: ubuntu-latest + timeout-minutes: 20 + outputs: + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT + check-runtime-assets: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image] + timeout-minutes: 20 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/assets + + check-runtime-collectives: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [check-runtime-assets, set-image] + timeout-minutes: 20 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/collectives + + check-runtime-coretime: + runs-on: arc-runners-polkadot-sdk-beefy + container: + image: ${{ needs.set-image.outputs.IMAGE }} + needs: [check-runtime-assets, set-image] + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/coretime + + check-runtime-bridge-hubs: + runs-on: arc-runners-polkadot-sdk-beefy + container: + image: ${{ needs.set-image.outputs.IMAGE }} + needs: [set-image] + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/bridge-hubs + + check-runtime-contracts: + runs-on: arc-runners-polkadot-sdk-beefy + container: + image: ${{ needs.set-image.outputs.IMAGE }} + needs: [check-runtime-collectives, set-image] + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/contracts + + check-runtime-starters: + runs-on: arc-runners-polkadot-sdk-beefy + container: + image: ${{ needs.set-image.outputs.IMAGE }} + needs: [check-runtime-assets, set-image] + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/starters + + check-runtime-testing: + runs-on: arc-runners-polkadot-sdk-beefy + container: + image: ${{ needs.set-image.outputs.IMAGE }} + needs: [check-runtime-starters, set-image] + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/testing + + confirm-required-jobs-passed: + runs-on: ubuntu-latest + name: All check-runtime-* tests passed + # If any new job gets added, be sure to add it to this array + needs: + - check-runtime-assets + - check-runtime-collectives + - check-runtime-coretime + - check-runtime-bridge-hubs + - check-runtime-contracts + - check-runtime-starters + - check-runtime-testing + steps: + - run: echo '### Good job! All the tests passed 🚀' >> $GITHUB_STEP_SUMMARY From 90c91b1ec0a90f563283b8c26144d0839528a711 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Thu, 15 Aug 2024 18:28:31 +0100 Subject: [PATCH 124/382] allow for `u8` to be used as hold/freeze reason (#5348) ..without needing to provide your own `newtype` around it. This will allow `type Reason = u8` to be used as `FreezeReason` and `HoldReason`, which I think is a nice simplification if one doens't want to deal with the complications. At the same time, it is a bit of an anti-pattern. Putting it out there to check people's vibes. --- prdoc/pr_5348.prdoc | 13 +++++++++++++ substrate/frame/support/src/traits/misc.rs | 4 ++++ 2 files changed, 17 insertions(+) create mode 100644 prdoc/pr_5348.prdoc diff --git a/prdoc/pr_5348.prdoc b/prdoc/pr_5348.prdoc new file mode 100644 index 000000000000..c2282c4c74c4 --- /dev/null +++ b/prdoc/pr_5348.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: allow for u8 to be used as hold/freeze reason + +doc: + - audience: Runtime Dev + description: | + Allows for `u8` type to be configured as `HoldReason` and `FreezeReason` + +crates: + - name: frame-support + bump: patch diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs index 7c8c22d1ae5a..492475d6f63c 100644 --- a/substrate/frame/support/src/traits/misc.rs +++ b/substrate/frame/support/src/traits/misc.rs @@ -48,6 +48,10 @@ impl VariantCount for () { const VARIANT_COUNT: u32 = 0; } +impl VariantCount for u8 { + const VARIANT_COUNT: u32 = 256; +} + /// Adapter for `Get` to access `VARIANT_COUNT` from `trait pub trait VariantCount {`. pub struct VariantCountOf(core::marker::PhantomData); impl Get for VariantCountOf { From 843c4db78f0ca260376c5a3abf78c95782ebdc64 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Thu, 15 Aug 2024 19:40:08 +0200 Subject: [PATCH 125/382] Update Readme of the `polkadot` crate (#5326) - Typos. - Those telemetry links like https://telemetry.polkadot.io/#list/Kusama didn't seem to properly point to a proper list (anymore?) - updated them. - Also looks like it was trying to use rust-style linking instead of markdown linking, changed that. - Relative links do not work on crates.io - updated to absolute, similarly as some already existing links, such as contribution guidelines. --- polkadot/README.md | 25 +++++++++++-------------- prdoc/pr_5326.prdoc | 13 +++++++++++++ 2 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 prdoc/pr_5326.prdoc diff --git a/polkadot/README.md b/polkadot/README.md index 47af79a3aa92..fa14995e9af3 100644 --- a/polkadot/README.md +++ b/polkadot/README.md @@ -103,9 +103,8 @@ Connect to the global Polkadot Mainnet network by running: ../target/release/polkadot --chain=polkadot ``` -You can see your node on [telemetry] (set a custom name with `--name "my custom name"`). - -[telemetry](https://telemetry.polkadot.io/#list/Polkadot): https://telemetry.polkadot.io/#list/Polkadot +You can see your node on [Polkadot telemetry](https://telemetry.polkadot.io/#list/0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3) +(set a custom name with `--name "my custom name"`). ### Connect to the "Kusama" Canary Network @@ -115,9 +114,8 @@ Connect to the global Kusama canary network by running: ../target/release/polkadot --chain=kusama ``` -You can see your node on [telemetry] (set a custom name with `--name "my custom name"`). - -[telemetry](https://telemetry.polkadot.io/#list/Kusama): https://telemetry.polkadot.io/#list/Kusama +You can see your node on [Kusama telemetry](https://telemetry.polkadot.io/#list/0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe) +(set a custom name with `--name "my custom name"`). ### Connect to the Westend Testnet @@ -127,9 +125,8 @@ Connect to the global Westend testnet by running: ../target/release/polkadot --chain=westend ``` -You can see your node on [telemetry] (set a custom name with `--name "my custom name"`). - -[telemetry](https://telemetry.polkadot.io/#list/Westend): https://telemetry.polkadot.io/#list/Westend +You can see your node on [Westend telemetry](https://telemetry.polkadot.io/#list/0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e) +(set a custom name with `--name "my custom name"`). ### Obtaining DOTs @@ -147,7 +144,7 @@ Then, grab the Polkadot source code: ```bash git clone https://github.com/paritytech/polkadot-sdk.git -cd polkadot +cd polkadot-sdk ``` Then build the code. You will need to build in release mode (`--release`) to start a network. Only @@ -185,7 +182,7 @@ You can run a simple single-node development "network" on your machine by runnin cargo run --bin polkadot --release -- --dev ``` -You can muck around by heading to and choose "Local Node" from the +You can muck around by heading to and choosing "Local Node" from the Settings menu. ### Local Two-node Testnet @@ -214,11 +211,11 @@ that we currently maintain. ### Using Docker -[Using Docker](../docs/contributor/docker.md) +[Using Docker](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/docker.md) ### Shell Completion -[Shell Completion](doc/shell-completion.md) +[Shell Completion](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/doc/shell-completion.md) ## Contributing @@ -232,4 +229,4 @@ that we currently maintain. ## License -Polkadot is [GPL 3.0 licensed](LICENSE). +Polkadot is [GPL 3.0 licensed](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/LICENSE). diff --git a/prdoc/pr_5326.prdoc b/prdoc/pr_5326.prdoc new file mode 100644 index 000000000000..0301b8c17a30 --- /dev/null +++ b/prdoc/pr_5326.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Update Readme of the `polkadot` crate + +doc: + - audience: Node Operator + description: | + Updated Readme of the `polkadot` crate. + +crates: + - name: polkadot + bump: patch From 80c3c1fd712ce9f1dd0c539e384dbeab699485ed Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 16 Aug 2024 08:33:31 +0200 Subject: [PATCH 126/382] Fix zombienet bridges test (#5373) After https://github.com/paritytech/polkadot-sdk/pull/4129, a zombienet bridge test was broken. This is because XCMv4 locations have an array in the `interior` field, which also has to appear in PJS. --------- Co-authored-by: Serban Iorga --- .../environments/rococo-westend/bridges_rococo_westend.sh | 4 ++-- .../testing/framework/js-helpers/wrapped-assets-balance.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh index ef4a5597902f..54633449134b 100755 --- a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh +++ b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh @@ -270,7 +270,7 @@ case "$1" in "//Alice" \ 1000 \ "ws://127.0.0.1:9910" \ - "$(jq --null-input '{ "parents": 2, "interior": { "X1": { "GlobalConsensus": "Westend" } } }')" \ + "$(jq --null-input '{ "parents": 2, "interior": { "X1": [{ "GlobalConsensus": "Westend" }] } }')" \ "$GLOBAL_CONSENSUS_WESTEND_SOVEREIGN_ACCOUNT" \ 10000000000 \ true @@ -329,7 +329,7 @@ case "$1" in "//Alice" \ 1000 \ "ws://127.0.0.1:9010" \ - "$(jq --null-input '{ "parents": 2, "interior": { "X1": { "GlobalConsensus": "Rococo" } } }')" \ + "$(jq --null-input '{ "parents": 2, "interior": { "X1": [{ "GlobalConsensus": "Rococo" }] } }')" \ "$GLOBAL_CONSENSUS_ROCOCO_SOVEREIGN_ACCOUNT" \ 10000000000 \ true diff --git a/bridges/testing/framework/js-helpers/wrapped-assets-balance.js b/bridges/testing/framework/js-helpers/wrapped-assets-balance.js index 27287118547f..7b343ed97a88 100644 --- a/bridges/testing/framework/js-helpers/wrapped-assets-balance.js +++ b/bridges/testing/framework/js-helpers/wrapped-assets-balance.js @@ -8,7 +8,7 @@ async function run(nodeName, networkInfo, args) { const bridgedNetworkName = args[2]; while (true) { const foreignAssetAccount = await api.query.foreignAssets.account( - { parents: 2, interior: { X1: { GlobalConsensus: bridgedNetworkName } } }, + { parents: 2, interior: { X1: [{ GlobalConsensus: bridgedNetworkName }] } }, accountAddress ); if (foreignAssetAccount.isSome) { From dbd194aad4b20ff03ed74e64cf06cb59d4396788 Mon Sep 17 00:00:00 2001 From: Tsvetomir Dimitrov Date: Fri, 16 Aug 2024 10:05:40 +0300 Subject: [PATCH 127/382] More logs in `is_potential_spam` from `dispute-coordinator` (#5252) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add more logs in `is_potential_spam` revealing why a statement was marked as a spam. --------- Co-authored-by: Bastian Köcher --- .../core/dispute-coordinator/src/initialized.rs | 13 +++++++++++++ polkadot/node/core/dispute-coordinator/src/lib.rs | 12 ++++++++++++ prdoc/pr_5252.prdoc | 11 +++++++++++ 3 files changed, 36 insertions(+) create mode 100644 prdoc/pr_5252.prdoc diff --git a/polkadot/node/core/dispute-coordinator/src/initialized.rs b/polkadot/node/core/dispute-coordinator/src/initialized.rs index 5f86da87f21c..5096fe5e6891 100644 --- a/polkadot/node/core/dispute-coordinator/src/initialized.rs +++ b/polkadot/node/core/dispute-coordinator/src/initialized.rs @@ -1351,6 +1351,12 @@ impl Initialized { } } for validator_index in new_state.votes().invalid.keys() { + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + ?validator_index, + "Disabled offchain for voting invalid against a valid candidate", + ); self.offchain_disabled_validators .insert_against_valid(session, *validator_index); } @@ -1375,6 +1381,13 @@ impl Initialized { } for (validator_index, (kind, _sig)) in new_state.votes().valid.raw() { let is_backer = kind.is_backing(); + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + ?validator_index, + ?is_backer, + "Disabled offchain for voting valid for an invalid candidate", + ); self.offchain_disabled_validators.insert_for_invalid( session, *validator_index, diff --git a/polkadot/node/core/dispute-coordinator/src/lib.rs b/polkadot/node/core/dispute-coordinator/src/lib.rs index daa384b36ffb..34d9ddf3a97c 100644 --- a/polkadot/node/core/dispute-coordinator/src/lib.rs +++ b/polkadot/node/core/dispute-coordinator/src/lib.rs @@ -478,6 +478,18 @@ pub fn is_potential_spam( let all_invalid_votes_disabled = vote_state.invalid_votes_all_disabled(is_disabled); let ignore_disabled = !is_confirmed && all_invalid_votes_disabled; + gum::trace!( + target: LOG_TARGET, + ?candidate_hash, + ?is_disputed, + ?is_included, + ?is_backed, + ?is_confirmed, + ?all_invalid_votes_disabled, + ?ignore_disabled, + "Checking for potential spam" + ); + (is_disputed && !is_included && !is_backed && !is_confirmed) || ignore_disabled } diff --git a/prdoc/pr_5252.prdoc b/prdoc/pr_5252.prdoc new file mode 100644 index 000000000000..fd4454ac3b9d --- /dev/null +++ b/prdoc/pr_5252.prdoc @@ -0,0 +1,11 @@ +title: Additional logging in `dispute-coordinator` subsystem + +doc: + - audience: Node Dev + description: | + Additional logging in `dispute-coordinator` subsystem tracing the list of offchain disabled + validators and the reason why an import statement is considered spam. + +crates: + - name: polkadot-node-core-dispute-coordinator + bump: patch \ No newline at end of file From 4780e3d07ff23a49e8f0a508138f83eb6e0d36c6 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Fri, 16 Aug 2024 10:38:25 +0300 Subject: [PATCH 128/382] approval-distribution: Fix handling of conclude (#5375) After https://github.com/paritytech/polkadot-sdk/commit/0636ffdc3dfea52e90102403527ff99d2f2d6e7c approval-distribution did not terminate anymore if Conclude signal was received. This should have been caught by the subsystem tests, but it wasn't because the subsystem is also exiting on error when the channels are dropped so the test overseer was dropped which made the susbystem exit and masked the problem. This pr fixes both the test and the subsystem. Signed-off-by: Alexandru Gheorghe --- .../node/network/approval-distribution/src/lib.rs | 11 ++++++++--- .../node/network/approval-distribution/src/tests.rs | 12 +++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/polkadot/node/network/approval-distribution/src/lib.rs b/polkadot/node/network/approval-distribution/src/lib.rs index 3462aaef1f69..134586253ce1 100644 --- a/polkadot/node/network/approval-distribution/src/lib.rs +++ b/polkadot/node/network/approval-distribution/src/lib.rs @@ -2508,7 +2508,9 @@ impl ApprovalDistribution { }; - self.handle_from_orchestra(message, &mut approval_voting_sender, &mut network_sender, state, rng).await; + if self.handle_from_orchestra(message, &mut approval_voting_sender, &mut network_sender, state, rng).await { + return; + } }, } @@ -2516,6 +2518,8 @@ impl ApprovalDistribution { } /// Handles a from orchestra message received by approval distribution subystem. + /// + /// Returns `true` if the subsystem should be stopped. pub async fn handle_from_orchestra< N: overseer::SubsystemSender, A: overseer::SubsystemSender, @@ -2526,7 +2530,7 @@ impl ApprovalDistribution { network_sender: &mut N, state: &mut State, rng: &mut (impl CryptoRng + Rng), - ) { + ) -> bool { match message { FromOrchestra::Communication { msg } => Self::handle_incoming( @@ -2555,8 +2559,9 @@ impl ApprovalDistribution { gum::trace!(target: LOG_TARGET, number = %number, "finalized signal"); state.handle_block_finalized(network_sender, &self.metrics, number).await; }, - FromOrchestra::Signal(OverseerSignal::Conclude) => return, + FromOrchestra::Signal(OverseerSignal::Conclude) => return true, } + false } async fn handle_incoming< diff --git a/polkadot/node/network/approval-distribution/src/tests.rs b/polkadot/node/network/approval-distribution/src/tests.rs index 3ea722c51a92..1ca571721ea9 100644 --- a/polkadot/node/network/approval-distribution/src/tests.rs +++ b/polkadot/node/network/approval-distribution/src/tests.rs @@ -59,9 +59,13 @@ fn test_harness>( let subsystem = ApprovalDistribution::new(Default::default()); { let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(12345); - - let subsystem = - subsystem.run_inner(context, &mut state, REPUTATION_CHANGE_TEST_INTERVAL, &mut rng); + let (tx, rx) = oneshot::channel(); + let subsystem = async { + subsystem + .run_inner(context, &mut state, REPUTATION_CHANGE_TEST_INTERVAL, &mut rng) + .await; + tx.send(()).expect("Fail to notify subystem is done"); + }; let test_fut = test_fn(virtual_overseer); @@ -76,6 +80,8 @@ fn test_harness>( .timeout(TIMEOUT) .await .expect("Conclude send timeout"); + let _ = + rx.timeout(Duration::from_secs(2)).await.expect("Subsystem did not conclude"); }, subsystem, )); From 74267881e765a01b1c7b3114c21b80dbe7686940 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Fri, 16 Aug 2024 12:29:04 +0200 Subject: [PATCH 129/382] Remove redundant minimal template workspace (#5330) This removes the workspace of the minimal template, which (I think) is redundant. The other two templates do not have such a workspace. The synchronized template created [it's own workspace](https://github.com/paritytech/polkadot-sdk-minimal-template/blob/master/Cargo.toml) anyway, and the new readme replaced the old docs contained in `lib.rs`. Closes https://github.com/paritytech/polkadot-sdk-minimal-template/issues/11 Silent because the crate was private. --- Cargo.lock | 13 ------- Cargo.toml | 1 - templates/minimal/Cargo.toml | 22 ----------- templates/minimal/src/lib.rs | 75 ------------------------------------ 4 files changed, 111 deletions(-) delete mode 100644 templates/minimal/Cargo.toml delete mode 100644 templates/minimal/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index df01a143efb6..4148b660cd3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8729,19 +8729,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "minimal-template" -version = "0.0.0" -dependencies = [ - "docify", - "minimal-template-node", - "minimal-template-runtime", - "pallet-minimal-template", - "polkadot-sdk-docs", - "polkadot-sdk-frame", - "simple-mermaid 0.1.1", -] - [[package]] name = "minimal-template-node" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index edb425e619c1..ebe9dc0dab3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -522,7 +522,6 @@ members = [ "substrate/utils/prometheus", "substrate/utils/substrate-bip39", "substrate/utils/wasm-builder", - "templates/minimal", "templates/minimal/node", "templates/minimal/pallets/template", "templates/minimal/runtime", diff --git a/templates/minimal/Cargo.toml b/templates/minimal/Cargo.toml deleted file mode 100644 index ba96e139bcf1..000000000000 --- a/templates/minimal/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "minimal-template" -description = "A minimal template built with Substrate, part of Polkadot Sdk." -version = "0.0.0" -license = "Unlicense" -authors.workspace = true -homepage.workspace = true -repository.workspace = true -edition.workspace = true -publish = false - -[dependencies] -minimal-template-node = { workspace = true } -minimal-template-runtime = { workspace = true } -pallet-minimal-template = { workspace = true, default-features = true } -polkadot-sdk-docs = { workspace = true } - -frame = { workspace = true, default-features = true } - -# How we build docs in rust-docs -simple-mermaid = "0.1.1" -docify = { workspace = true } diff --git a/templates/minimal/src/lib.rs b/templates/minimal/src/lib.rs deleted file mode 100644 index 68825d190bb2..000000000000 --- a/templates/minimal/src/lib.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! # Minimal Template -//! -//! This is a minimal template for creating a blockchain using the Polkadot SDK. -//! -//! ## Components -//! -//! The template consists of the following components: -//! -//! ### Node -//! -//! A minimal blockchain [`node`](`minimal_template_node`) that is capable of running a -//! runtime. It uses a simple chain specification, provides an option to choose Manual or -//! InstantSeal for consensus and exposes a few commands to interact with the node. -//! -//! ### Runtime -//! -//! A minimal [`runtime`](`minimal_template_runtime`) (or a state transition function) that -//! is capable of being run on the node. It is built using the [`FRAME`](`frame`) framework -//! that enables the composition of the core logic via separate modules called "pallets". -//! FRAME defines a complete DSL for building such pallets and the runtime itself. -//! -//! #### Transaction Fees -//! -//! The runtime charges a transaction fee for every transaction that is executed. The fee is -//! calculated based on the weight of the transaction (accouting for the execution time) and -//! length of the call data. Please refer to -//! [`benchmarking docs`](`polkadot_sdk_docs::reference_docs::frame_benchmarking_weight`) for -//! more information on how the weight is calculated. -//! -//! This template sets the fee as independent of the weight of the extrinsic and fixed for any -//! length of the call data for demo purposes. -//! -//! ### Pallet -//! -//! A minimal [`pallet`](`pallet_minimal_template`) that is built using FRAME. It is a unit of -//! encapsulated logic that has a clearly defined responsibility and can be linked to other pallets. -//! -//! ## Getting Started -//! -//! To get started with the template, follow the steps below: -//! -//! ### Build the Node -//! -//! Build the node using the following command: -//! -//! ```bash -//! cargo build -p minimal-template-node --release -//! ``` -//! -//! ### Run the Node -//! -//! Run the node using the following command: -//! -//! ```bash -//! ./target/release/minimal-template-node --dev -//! ``` -//! -//! ### CLI Options -//! -//! The node exposes a few options that can be used to interact with the node. To see the list of -//! available options, run the following command: -//! -//! ```bash -//! ./target/release/minimal-template-node --help -//! ``` -//! -//! #### Consensus Algorithm -//! -//! In order to run the node with a specific consensus algorithm, use the `--consensus` flag. For -//! example, to run the node with ManualSeal consensus with a block time of 5000ms, use the -//! following command: -//! -//! ```bash -//! ./target/release/minimal-template-node --dev --consensus manual-seal-5000 -//! ``` From fd522b818693965b6ec9e4cfdb4182c114d6105c Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Fri, 16 Aug 2024 23:09:06 +0900 Subject: [PATCH 130/382] Fix doc: start_destroy doesn't need asset to be frozen (#5204) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix https://github.com/paritytech/polkadot-sdk/issues/5184 `owner` can set himself as a `freezer` and freeze the asset so requirement is not really needed. And requirement is not implemented. --------- Co-authored-by: Bastian Köcher Co-authored-by: Oliver Tale-Yazdi --- prdoc/pr_5204.prdoc | 13 +++++++++++++ substrate/frame/assets/src/lib.rs | 2 -- substrate/frame/support/src/traits/tokens/misc.rs | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 prdoc/pr_5204.prdoc diff --git a/prdoc/pr_5204.prdoc b/prdoc/pr_5204.prdoc new file mode 100644 index 000000000000..38a73b6b00ef --- /dev/null +++ b/prdoc/pr_5204.prdoc @@ -0,0 +1,13 @@ +title: "Pallet assets: fix doc: start_destroy never required asset to be frozen" + +doc: + - audience: Runtime Dev + description: | + In pallet assets calling `start_destroy` doesn't require the asset to be frozen. Doc is fixed. + + +crates: + - name: pallet-assets + bump: patch + - name: frame-support + bump: patch diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index 6f8ad0c29393..e909932bfc82 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -801,8 +801,6 @@ pub mod pallet { /// /// - `id`: The identifier of the asset to be destroyed. This must identify an existing /// asset. - /// - /// The asset class must be frozen before calling `start_destroy`. #[pallet::call_index(2)] pub fn start_destroy(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { let maybe_check_owner = match T::ForceOrigin::try_origin(origin) { diff --git a/substrate/frame/support/src/traits/tokens/misc.rs b/substrate/frame/support/src/traits/tokens/misc.rs index 9fa1df862097..52d3e8c014b3 100644 --- a/substrate/frame/support/src/traits/tokens/misc.rs +++ b/substrate/frame/support/src/traits/tokens/misc.rs @@ -98,7 +98,7 @@ pub enum WithdrawConsequence { /// There has been an overflow in the system. This is indicative of a corrupt state and /// likely unrecoverable. Overflow, - /// Not enough of the funds in the account are unavailable for withdrawal. + /// Not enough of the funds in the account are available for withdrawal. Frozen, /// Account balance would reduce to zero, potentially destroying it. The parameter is the /// amount of balance which is destroyed. From feac7a521092c599d47df3e49084e6bff732c7db Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sun, 18 Aug 2024 08:23:46 +0300 Subject: [PATCH 131/382] Replace unnecessary `&mut self` with `&self` in `BlockImport::import_block()` (#5339) There was no need for it to be `&mut self` since block import can happen concurrently for different blocks and in many cases it was `&mut Arc` anyway :man_shrugging: Similar in nature to https://github.com/paritytech/polkadot-sdk/pull/4844 --- cumulus/client/consensus/common/src/lib.rs | 2 +- .../common/src/parachain_consensus.rs | 7 +-- cumulus/client/consensus/common/src/tests.rs | 4 +- cumulus/client/network/src/tests.rs | 2 +- .../src/lib.rs | 6 +- .../src/validate_block/tests.rs | 2 +- .../service/benches/validate_block_glutton.rs | 2 +- cumulus/test/service/src/bench_utils.rs | 2 +- polkadot/node/test/client/src/lib.rs | 4 +- .../xcm-executor/integration-tests/src/lib.rs | 10 ++-- prdoc/pr_5339.prdoc | 45 ++++++++++++++ .../bin/node/cli/benches/block_production.rs | 2 +- substrate/bin/node/cli/tests/basic.rs | 2 +- .../basic-authorship/src/basic_authorship.rs | 2 +- substrate/client/consensus/babe/src/lib.rs | 4 +- substrate/client/consensus/babe/src/tests.rs | 2 +- .../client/consensus/beefy/src/import.rs | 2 +- substrate/client/consensus/beefy/src/tests.rs | 2 +- .../consensus/common/src/block_import.rs | 15 +---- .../common/src/import_queue/basic_queue.rs | 2 +- .../consensus/grandpa/src/finality_proof.rs | 2 +- .../client/consensus/grandpa/src/import.rs | 18 +++--- .../client/consensus/grandpa/src/tests.rs | 8 +-- .../consensus/grandpa/src/voting_rule.rs | 4 +- .../consensus/grandpa/src/warp_proof.rs | 2 +- substrate/client/consensus/pow/src/lib.rs | 2 +- substrate/client/consensus/pow/src/worker.rs | 2 +- .../merkle-mountain-range/src/test_utils.rs | 2 +- substrate/client/network/src/bitswap/mod.rs | 2 +- substrate/client/network/sync/src/strategy.rs | 2 +- .../sync/src/strategy/chain_sync/test.rs | 58 +++++++++---------- .../client/network/test/src/block_import.rs | 2 +- substrate/client/network/test/src/lib.rs | 4 +- substrate/client/offchain/src/lib.rs | 2 +- .../client/rpc-spec-v2/src/archive/tests.rs | 16 ++--- .../src/chain_head/subscription/inner.rs | 2 +- .../rpc-spec-v2/src/chain_head/tests.rs | 52 ++++++++--------- substrate/client/rpc/src/chain/tests.rs | 8 +-- substrate/client/rpc/src/dev/tests.rs | 4 +- substrate/client/rpc/src/state/tests.rs | 8 +-- substrate/client/service/src/client/client.rs | 16 ++--- .../client/service/test/src/client/mod.rs | 40 ++++++------- .../client/transaction-pool/tests/pool.rs | 2 +- substrate/test-utils/client/src/client_ext.rs | 31 +++++----- .../runtime/client/src/trait_tests.rs | 6 +- substrate/test-utils/runtime/src/lib.rs | 2 +- 46 files changed, 223 insertions(+), 193 deletions(-) create mode 100644 prdoc/pr_5339.prdoc diff --git a/cumulus/client/consensus/common/src/lib.rs b/cumulus/client/consensus/common/src/lib.rs index e12750dcc553..6766c2409c38 100644 --- a/cumulus/client/consensus/common/src/lib.rs +++ b/cumulus/client/consensus/common/src/lib.rs @@ -185,7 +185,7 @@ where } async fn import_block( - &mut self, + &self, mut params: sc_consensus::BlockImportParams, ) -> Result { // Blocks are stored within the backend by using POST hash. diff --git a/cumulus/client/consensus/common/src/parachain_consensus.rs b/cumulus/client/consensus/common/src/parachain_consensus.rs index 944917673b11..861354ed63c3 100644 --- a/cumulus/client/consensus/common/src/parachain_consensus.rs +++ b/cumulus/client/consensus/common/src/parachain_consensus.rs @@ -433,11 +433,8 @@ async fn handle_new_best_parachain_head( } } -async fn import_block_as_new_best( - hash: Block::Hash, - header: Block::Header, - mut parachain: &P, -) where +async fn import_block_as_new_best(hash: Block::Hash, header: Block::Header, parachain: &P) +where Block: BlockT, P: UsageProvider + Send + Sync + BlockBackend, for<'a> &'a P: BlockImport, diff --git a/cumulus/client/consensus/common/src/tests.rs b/cumulus/client/consensus/common/src/tests.rs index 284fa39ed1e7..06f90330d474 100644 --- a/cumulus/client/consensus/common/src/tests.rs +++ b/cumulus/client/consensus/common/src/tests.rs @@ -321,7 +321,7 @@ fn build_block( } async fn import_block>( - importer: &mut I, + importer: &I, block: Block, origin: BlockOrigin, import_as_best: bool, @@ -568,7 +568,7 @@ fn follow_finalized_does_not_stop_on_unknown_block() { fn follow_new_best_sets_best_after_it_is_imported() { sp_tracing::try_init_simple(); - let mut client = Arc::new(TestClientBuilder::default().build()); + let client = Arc::new(TestClientBuilder::default().build()); let block = build_and_import_block(client.clone(), false); diff --git a/cumulus/client/network/src/tests.rs b/cumulus/client/network/src/tests.rs index 18d121c41d16..cde73c4c5180 100644 --- a/cumulus/client/network/src/tests.rs +++ b/cumulus/client/network/src/tests.rs @@ -612,7 +612,7 @@ fn relay_parent_not_imported_when_block_announce_is_processed() { block_on(async move { let (mut validator, api) = make_validator_and_api(); - let mut client = api.relay_client.clone(); + let client = api.relay_client.clone(); let block = client.init_polkadot_block_builder().build().expect("Build new block").block; let (signal, header) = make_gossip_message_and_header(api, block.hash(), 0).await; diff --git a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs index 38ba84748c1e..c796dc5f7c38 100644 --- a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs +++ b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs @@ -423,7 +423,7 @@ mod tests { #[test] fn returns_directly_for_available_block() { - let (mut client, block, relay_chain_interface) = build_client_backend_and_block(); + let (client, block, relay_chain_interface) = build_client_backend_and_block(); let hash = block.hash(); block_on(client.import(BlockOrigin::Own, block)).expect("Imports the block"); @@ -439,7 +439,7 @@ mod tests { #[test] fn resolve_after_block_import_notification_was_received() { - let (mut client, block, relay_chain_interface) = build_client_backend_and_block(); + let (client, block, relay_chain_interface) = build_client_backend_and_block(); let hash = block.hash(); block_on(async move { @@ -468,7 +468,7 @@ mod tests { #[test] fn do_not_resolve_after_different_block_import_notification_was_received() { - let (mut client, block, relay_chain_interface) = build_client_backend_and_block(); + let (client, block, relay_chain_interface) = build_client_backend_and_block(); let hash = block.hash(); let ext = construct_transfer_extrinsic( diff --git a/cumulus/pallets/parachain-system/src/validate_block/tests.rs b/cumulus/pallets/parachain-system/src/validate_block/tests.rs index 1527492f5784..a44d17507810 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/tests.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/tests.rs @@ -312,7 +312,7 @@ fn validation_params_and_memory_optimized_validation_params_encode_and_decode() fn validate_block_works_with_child_tries() { sp_tracing::try_init_simple(); - let (mut client, parent_head) = create_test_client(); + let (client, parent_head) = create_test_client(); let TestBlockData { block, .. } = build_block_with_witness( &client, vec![generate_extrinsic(&client, Charlie, TestPalletCall::read_and_write_child_tries {})], diff --git a/cumulus/test/service/benches/validate_block_glutton.rs b/cumulus/test/service/benches/validate_block_glutton.rs index 6ec338c7f142..6fe26519a3eb 100644 --- a/cumulus/test/service/benches/validate_block_glutton.rs +++ b/cumulus/test/service/benches/validate_block_glutton.rs @@ -43,7 +43,7 @@ use sp_runtime::traits::Header as HeaderT; use cumulus_test_service::bench_utils as utils; async fn import_block( - mut client: &cumulus_test_client::Client, + client: &cumulus_test_client::Client, built: cumulus_test_runtime::Block, import_existing: bool, ) { diff --git a/cumulus/test/service/src/bench_utils.rs b/cumulus/test/service/src/bench_utils.rs index 4ace894b392a..67ffbdd1d212 100644 --- a/cumulus/test/service/src/bench_utils.rs +++ b/cumulus/test/service/src/bench_utils.rs @@ -111,7 +111,7 @@ pub fn extrinsic_set_validation_data( } /// Import block into the given client and make sure the import was successful -pub async fn import_block(mut client: &TestClient, block: &NodeBlock, import_existing: bool) { +pub async fn import_block(client: &TestClient, block: &NodeBlock, import_existing: bool) { let mut params = BlockImportParams::new(BlockOrigin::File, block.header.clone()); params.body = Some(block.extrinsics.clone()); params.state_action = StateAction::Execute; diff --git a/polkadot/node/test/client/src/lib.rs b/polkadot/node/test/client/src/lib.rs index 6b205c09f2f3..498994d9a0a8 100644 --- a/polkadot/node/test/client/src/lib.rs +++ b/polkadot/node/test/client/src/lib.rs @@ -102,7 +102,7 @@ mod tests { #[test] fn ensure_test_client_can_build_and_import_block() { - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let block_builder = client.init_polkadot_block_builder(); let block = block_builder.build().expect("Finalizes the block").block; @@ -113,7 +113,7 @@ mod tests { #[test] fn ensure_test_client_can_push_extrinsic() { - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let transfer = construct_transfer_extrinsic( &client, diff --git a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs index 279d7118f8cf..7683c6025392 100644 --- a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs +++ b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs @@ -34,7 +34,7 @@ use xcm_executor::traits::WeightBounds; #[test] fn basic_buy_fees_message_executes() { sp_tracing::try_init_simple(); - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let msg = Xcm(vec![ WithdrawAsset((Parent, 100).into()), @@ -75,7 +75,7 @@ fn basic_buy_fees_message_executes() { #[test] fn transact_recursion_limit_works() { sp_tracing::try_init_simple(); - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let base_xcm = |call: polkadot_test_runtime::RuntimeCall| { Xcm(vec![ @@ -174,7 +174,7 @@ fn query_response_fires() { use polkadot_test_runtime::RuntimeEvent::TestNotifier; sp_tracing::try_init_simple(); - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let mut block_builder = client.init_polkadot_block_builder(); @@ -256,7 +256,7 @@ fn query_response_elicits_handler() { use polkadot_test_runtime::RuntimeEvent::TestNotifier; sp_tracing::try_init_simple(); - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let mut block_builder = client.init_polkadot_block_builder(); @@ -332,7 +332,7 @@ fn query_response_elicits_handler() { #[test] fn deposit_reserve_asset_works_for_any_xcm_sender() { sp_tracing::try_init_simple(); - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); // Init values for the simulated origin Parachain let amount_to_send: u128 = 1_000_000_000_000; diff --git a/prdoc/pr_5339.prdoc b/prdoc/pr_5339.prdoc new file mode 100644 index 000000000000..850ba903e126 --- /dev/null +++ b/prdoc/pr_5339.prdoc @@ -0,0 +1,45 @@ +title: Replace unnecessary `&mut self` with `&self` in `BlockImport::import_block()` + +doc: + - audience: Node Dev + description: | + Simplifies block import API to match intended design where independent blocks can technically be imported + concurrently and in practice was called through `Arc` anyway + +crates: + - name: cumulus-client-consensus-common + bump: major + - name: cumulus-client-network + bump: none + - name: cumulus-relay-chain-inprocess-interface + bump: none + - name: cumulus-pallet-parachain-system + bump: none + - name: sc-basic-authorship + bump: patch + - name: sc-consensus-babe + bump: major + - name: sc-consensus-beefy + bump: major + - name: sc-consensus + bump: major + - name: sc-consensus-grandpa + bump: major + - name: sc-consensus-pow + bump: major + - name: mmr-gadget + bump: none + - name: sc-network + bump: none + - name: sc-network-sync + bump: none + - name: sc-offchain + bump: none + - name: sc-rpc-spec-v2 + bump: none + - name: sc-rpc + bump: none + - name: sc-service + bump: major + - name: sc-transaction-pool + bump: none diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs index 8239637b3a9f..9ac9d8b4f67d 100644 --- a/substrate/bin/node/cli/benches/block_production.rs +++ b/substrate/bin/node/cli/benches/block_production.rs @@ -124,7 +124,7 @@ fn extrinsic_set_time(now: u64) -> OpaqueExtrinsic { .into() } -fn import_block(mut client: &FullClient, built: BuiltBlock) { +fn import_block(client: &FullClient, built: BuiltBlock) { let mut params = BlockImportParams::new(BlockOrigin::File, built.block.header); params.state_action = StateAction::ApplyChanges(sc_consensus::StorageChanges::Changes(built.storage_changes)); diff --git a/substrate/bin/node/cli/tests/basic.rs b/substrate/bin/node/cli/tests/basic.rs index 0a2e3fd25047..037ddbb1e47b 100644 --- a/substrate/bin/node/cli/tests/basic.rs +++ b/substrate/bin/node/cli/tests/basic.rs @@ -850,7 +850,7 @@ fn should_import_block_with_test_client() { sp_consensus::BlockOrigin, ClientBlockImportExt, TestClientBuilder, TestClientBuilderExt, }; - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let block1 = changes_trie_block(); let block_data = block1.0; let block = node_primitives::Block::decode(&mut &block_data[..]).unwrap(); diff --git a/substrate/client/basic-authorship/src/basic_authorship.rs b/substrate/client/basic-authorship/src/basic_authorship.rs index 74805488792a..527a3d12d9e7 100644 --- a/substrate/client/basic-authorship/src/basic_authorship.rs +++ b/substrate/client/basic-authorship/src/basic_authorship.rs @@ -852,7 +852,7 @@ mod tests { block }; - let import_and_maintain = |mut client: Arc, block: TestBlock| { + let import_and_maintain = |client: Arc, block: TestBlock| { let hash = block.hash(); block_on(client.import(BlockOrigin::Own, block)).unwrap(); block_on(txpool.maintain(chain_event( diff --git a/substrate/client/consensus/babe/src/lib.rs b/substrate/client/consensus/babe/src/lib.rs index 0c1eb8875864..9770b16871e1 100644 --- a/substrate/client/consensus/babe/src/lib.rs +++ b/substrate/client/consensus/babe/src/lib.rs @@ -1342,7 +1342,7 @@ where // This function makes multiple transactions to the DB. If one of them fails we may // end up in an inconsistent state and have to resync. async fn import_state( - &mut self, + &self, mut block: BlockImportParams, ) -> Result { let hash = block.post_hash(); @@ -1405,7 +1405,7 @@ where type Error = ConsensusError; async fn import_block( - &mut self, + &self, mut block: BlockImportParams, ) -> Result { let hash = block.post_hash(); diff --git a/substrate/client/consensus/babe/src/tests.rs b/substrate/client/consensus/babe/src/tests.rs index 6f805188b9a4..5c2e0eae959c 100644 --- a/substrate/client/consensus/babe/src/tests.rs +++ b/substrate/client/consensus/babe/src/tests.rs @@ -150,7 +150,7 @@ where type Error = BI::Error; async fn import_block( - &mut self, + &self, block: BlockImportParams, ) -> Result { Ok(self.0.import_block(block).await.expect("importing block failed")) diff --git a/substrate/client/consensus/beefy/src/import.rs b/substrate/client/consensus/beefy/src/import.rs index 848026852933..17a4a5866636 100644 --- a/substrate/client/consensus/beefy/src/import.rs +++ b/substrate/client/consensus/beefy/src/import.rs @@ -132,7 +132,7 @@ where type Error = ConsensusError; async fn import_block( - &mut self, + &self, mut block: BlockImportParams, ) -> Result { let hash = block.post_hash(); diff --git a/substrate/client/consensus/beefy/src/tests.rs b/substrate/client/consensus/beefy/src/tests.rs index 4b1dd447320b..afa6191d8bfe 100644 --- a/substrate/client/consensus/beefy/src/tests.rs +++ b/substrate/client/consensus/beefy/src/tests.rs @@ -790,7 +790,7 @@ async fn beefy_importing_justifications() { let client = net.peer(0).client().clone(); let full_client = client.as_client(); - let (mut block_import, _, peer_data) = net.make_block_import(client.clone()); + let (block_import, _, peer_data) = net.make_block_import(client.clone()); let PeerData { beefy_voter_links, .. } = peer_data; let justif_stream = beefy_voter_links.lock().take().unwrap().from_block_import_justif_stream; let mut justif_recv = justif_stream.subscribe(100_000); diff --git a/substrate/client/consensus/common/src/block_import.rs b/substrate/client/consensus/common/src/block_import.rs index c5adbb5a5fca..4d7b89f37d86 100644 --- a/substrate/client/consensus/common/src/block_import.rs +++ b/substrate/client/consensus/common/src/block_import.rs @@ -310,10 +310,7 @@ pub trait BlockImport { async fn check_block(&self, block: BlockCheckParams) -> Result; /// Import a block. - async fn import_block( - &mut self, - block: BlockImportParams, - ) -> Result; + async fn import_block(&self, block: BlockImportParams) -> Result; } #[async_trait::async_trait] @@ -326,10 +323,7 @@ impl BlockImport for crate::import_queue::BoxBlockImport { } /// Import a block. - async fn import_block( - &mut self, - block: BlockImportParams, - ) -> Result { + async fn import_block(&self, block: BlockImportParams) -> Result { (**self).import_block(block).await } } @@ -346,10 +340,7 @@ where (&**self).check_block(block).await } - async fn import_block( - &mut self, - block: BlockImportParams, - ) -> Result { + async fn import_block(&self, block: BlockImportParams) -> Result { (&**self).import_block(block).await } } diff --git a/substrate/client/consensus/common/src/import_queue/basic_queue.rs b/substrate/client/consensus/common/src/import_queue/basic_queue.rs index 6a307acb2517..7b371145e2e7 100644 --- a/substrate/client/consensus/common/src/import_queue/basic_queue.rs +++ b/substrate/client/consensus/common/src/import_queue/basic_queue.rs @@ -525,7 +525,7 @@ mod tests { } async fn import_block( - &mut self, + &self, _block: BlockImportParams, ) -> Result { Ok(ImportResult::imported(true)) diff --git a/substrate/client/consensus/grandpa/src/finality_proof.rs b/substrate/client/consensus/grandpa/src/finality_proof.rs index af965f2e4ae6..8a47d121e869 100644 --- a/substrate/client/consensus/grandpa/src/finality_proof.rs +++ b/substrate/client/consensus/grandpa/src/finality_proof.rs @@ -319,7 +319,7 @@ mod tests { ) -> (Arc, Arc, Vec) { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let mut blocks = Vec::new(); for _ in 0..number_of_blocks { diff --git a/substrate/client/consensus/grandpa/src/import.rs b/substrate/client/consensus/grandpa/src/import.rs index 8b7b02f180ec..5cec5204c739 100644 --- a/substrate/client/consensus/grandpa/src/import.rs +++ b/substrate/client/consensus/grandpa/src/import.rs @@ -20,6 +20,7 @@ use std::{collections::HashMap, marker::PhantomData, sync::Arc}; use codec::Decode; use log::debug; +use parking_lot::Mutex; use sc_client_api::{backend::Backend, utils::is_descendent_of}; use sc_consensus::{ @@ -62,7 +63,8 @@ pub struct GrandpaBlockImport { select_chain: SC, authority_set: SharedAuthoritySet>, send_voter_commands: TracingUnboundedSender>>, - authority_set_hard_forks: HashMap>>, + authority_set_hard_forks: + Mutex>>>, justification_sender: GrandpaJustificationSender, telemetry: Option, _phantom: PhantomData, @@ -78,7 +80,7 @@ impl Clone select_chain: self.select_chain.clone(), authority_set: self.authority_set.clone(), send_voter_commands: self.send_voter_commands.clone(), - authority_set_hard_forks: self.authority_set_hard_forks.clone(), + authority_set_hard_forks: Mutex::new(self.authority_set_hard_forks.lock().clone()), justification_sender: self.justification_sender.clone(), telemetry: self.telemetry.clone(), _phantom: PhantomData, @@ -242,7 +244,7 @@ where hash: Block::Hash, ) -> Option>> { // check for forced authority set hard forks - if let Some(change) = self.authority_set_hard_forks.get(&hash) { + if let Some(change) = self.authority_set_hard_forks.lock().get(&hash) { return Some(change.clone()) } @@ -461,7 +463,7 @@ where /// Import whole new state and reset authority set. async fn import_state( - &mut self, + &self, mut block: BlockImportParams, ) -> Result { let hash = block.post_hash(); @@ -474,7 +476,7 @@ where // We've just imported a new state. We trust the sync module has verified // finality proofs and that the state is correct and final. // So we can read the authority list and set id from the state. - self.authority_set_hard_forks.clear(); + self.authority_set_hard_forks.lock().clear(); let authorities = self .inner .runtime_api() @@ -523,7 +525,7 @@ where type Error = ConsensusError; async fn import_block( - &mut self, + &self, mut block: BlockImportParams, ) -> Result { let hash = block.post_hash(); @@ -750,7 +752,7 @@ impl GrandpaBlockImport, justification: Justification, diff --git a/substrate/client/consensus/grandpa/src/tests.rs b/substrate/client/consensus/grandpa/src/tests.rs index 2aa1b5f6ee1b..9cca28a39599 100644 --- a/substrate/client/consensus/grandpa/src/tests.rs +++ b/substrate/client/consensus/grandpa/src/tests.rs @@ -906,7 +906,7 @@ async fn allows_reimporting_change_blocks() { let mut net = GrandpaTestNet::new(api.clone(), 3, 0); let client = net.peer(0).client().clone(); - let (mut block_import, ..) = net.make_block_import(client.clone()); + let (block_import, ..) = net.make_block_import(client.clone()); let full_client = client.as_client(); let mut builder = BlockBuilderBuilder::new(&*full_client) @@ -954,7 +954,7 @@ async fn test_bad_justification() { let mut net = GrandpaTestNet::new(api.clone(), 3, 0); let client = net.peer(0).client().clone(); - let (mut block_import, ..) = net.make_block_import(client.clone()); + let (block_import, ..) = net.make_block_import(client.clone()); let full_client = client.as_client(); let mut builder = BlockBuilderBuilder::new(&*full_client) @@ -2083,7 +2083,7 @@ async fn imports_justification_for_regular_blocks_on_import() { let mut net = GrandpaTestNet::new(api.clone(), 1, 0); let client = net.peer(0).client().clone(); - let (mut block_import, ..) = net.make_block_import(client.clone()); + let (block_import, ..) = net.make_block_import(client.clone()); let full_client = client.as_client(); // create a new block (without importing it) @@ -2122,7 +2122,7 @@ async fn imports_justification_for_regular_blocks_on_import() { GrandpaJustification::from_commit(&full_client, round, commit).unwrap() }; - let mut generate_and_import_block_with_justification = |parent| { + let generate_and_import_block_with_justification = |parent| { // we import the block with justification attached let block = generate_block(parent); let block_hash = block.hash(); diff --git a/substrate/client/consensus/grandpa/src/voting_rule.rs b/substrate/client/consensus/grandpa/src/voting_rule.rs index c37596d20f68..c1d3cd2fbd6a 100644 --- a/substrate/client/consensus/grandpa/src/voting_rule.rs +++ b/substrate/client/consensus/grandpa/src/voting_rule.rs @@ -367,7 +367,7 @@ mod tests { // where each subtracts 50 blocks from the current target let rule = VotingRulesBuilder::new().add(Subtract(50)).add(Subtract(50)).build(); - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let mut hashes = Vec::with_capacity(200); for _ in 0..200 { @@ -416,7 +416,7 @@ mod tests { fn before_best_by_has_cutoff_at_base() { let rule = BeforeBestBlockBy(2); - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let n = 5; let mut hashes = Vec::with_capacity(n); diff --git a/substrate/client/consensus/grandpa/src/warp_proof.rs b/substrate/client/consensus/grandpa/src/warp_proof.rs index c836ab09fd5d..a79581b1e9f1 100644 --- a/substrate/client/consensus/grandpa/src/warp_proof.rs +++ b/substrate/client/consensus/grandpa/src/warp_proof.rs @@ -338,7 +338,7 @@ mod tests { let mut rng = rand::rngs::StdRng::from_seed([0; 32]); let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let available_authorities = Ed25519Keyring::iter().collect::>(); let genesis_authorities = vec![(Ed25519Keyring::Alice.public().into(), 1)]; diff --git a/substrate/client/consensus/pow/src/lib.rs b/substrate/client/consensus/pow/src/lib.rs index 50e9533abb36..cd7da128549f 100644 --- a/substrate/client/consensus/pow/src/lib.rs +++ b/substrate/client/consensus/pow/src/lib.rs @@ -317,7 +317,7 @@ where } async fn import_block( - &mut self, + &self, mut block: BlockImportParams, ) -> Result { let best_header = self diff --git a/substrate/client/consensus/pow/src/worker.rs b/substrate/client/consensus/pow/src/worker.rs index 9e9c4fc137d8..73400136483a 100644 --- a/substrate/client/consensus/pow/src/worker.rs +++ b/substrate/client/consensus/pow/src/worker.rs @@ -192,7 +192,7 @@ where import_block.insert_intermediate(INTERMEDIATE_KEY, intermediate); let header = import_block.post_header(); - let mut block_import = self.block_import.lock(); + let block_import = self.block_import.lock(); match block_import.import_block(import_block).await { Ok(res) => { diff --git a/substrate/client/merkle-mountain-range/src/test_utils.rs b/substrate/client/merkle-mountain-range/src/test_utils.rs index fcf9fa25b593..3b0506ef55d3 100644 --- a/substrate/client/merkle-mountain-range/src/test_utils.rs +++ b/substrate/client/merkle-mountain-range/src/test_utils.rs @@ -122,7 +122,7 @@ impl MockClient { name: &[u8], maybe_leaf_idx: Option, ) -> MmrBlock { - let mut client = self.client.lock(); + let client = self.client.lock(); let hash = client.expect_block_hash_from_id(&at).unwrap(); let mut block_builder = BlockBuilderBuilder::new(&*client) diff --git a/substrate/client/network/src/bitswap/mod.rs b/substrate/client/network/src/bitswap/mod.rs index 22f1973adcb2..1e20572eeeb1 100644 --- a/substrate/client/network/src/bitswap/mod.rs +++ b/substrate/client/network/src/bitswap/mod.rs @@ -468,7 +468,7 @@ mod tests { #[tokio::test] async fn transaction_found() { - let mut client = TestClientBuilder::with_tx_storage(u32::MAX).build(); + let client = TestClientBuilder::with_tx_storage(u32::MAX).build(); let mut block_builder = BlockBuilderBuilder::new(&client) .on_parent_block(client.chain_info().genesis_hash) .with_parent_block_number(0) diff --git a/substrate/client/network/sync/src/strategy.rs b/substrate/client/network/sync/src/strategy.rs index 72f84d1c286e..58befe94e84a 100644 --- a/substrate/client/network/sync/src/strategy.rs +++ b/substrate/client/network/sync/src/strategy.rs @@ -617,7 +617,7 @@ mod test { #[test] fn set_target_block_finished_warp_sync() { // Populate database with finalized state. - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let block = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().best_hash) .with_parent_block_number(client.chain_info().best_number) diff --git a/substrate/client/network/sync/src/strategy/chain_sync/test.rs b/substrate/client/network/sync/src/strategy/chain_sync/test.rs index cd955113542b..39d0c8f8d4d6 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync/test.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync/test.rs @@ -91,7 +91,7 @@ fn processes_empty_response_on_justification_request_for_unknown_block() { #[test] fn restart_doesnt_affect_peers_downloading_finality_data() { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); // we request max 8 blocks to always initiate block requests to both peers for the test to be // deterministic @@ -103,7 +103,7 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { let peer_id2 = PeerId::random(); let peer_id3 = PeerId::random(); - let mut new_blocks = |n| { + let new_blocks = |n| { for _ in 0..n { let block = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().best_hash) @@ -242,12 +242,12 @@ fn get_block_request( } /// Build and import a new best block. -fn build_block(client: &mut Arc, at: Option, fork: bool) -> Block { +fn build_block(client: &TestClient, at: Option, fork: bool) -> Block { let at = at.unwrap_or_else(|| client.info().best_hash); - let mut block_builder = BlockBuilderBuilder::new(&**client) + let mut block_builder = BlockBuilderBuilder::new(client) .on_parent_block(at) - .fetch_parent_block_number(&**client) + .fetch_parent_block_number(client) .unwrap() .build() .unwrap(); @@ -282,13 +282,13 @@ fn do_ancestor_search_when_common_block_to_best_queued_gap_is_to_big() { sp_tracing::try_init_simple(); let blocks = { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = TestClientBuilder::new().build(); (0..MAX_DOWNLOAD_AHEAD * 2) - .map(|_| build_block(&mut client, None, false)) + .map(|_| build_block(&client, None, false)) .collect::>() }; - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let info = client.info(); let mut sync = @@ -415,13 +415,13 @@ fn do_ancestor_search_when_common_block_to_best_queued_gap_is_to_big() { fn can_sync_huge_fork() { sp_tracing::try_init_simple(); - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let blocks = (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 4) - .map(|_| build_block(&mut client, None, false)) + .map(|_| build_block(&client, None, false)) .collect::>(); let fork_blocks = { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = TestClientBuilder::new().build(); let fork_blocks = blocks[..MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2] .into_iter() .inspect(|b| block_on(client.import(BlockOrigin::Own, (*b).clone())).unwrap()) @@ -431,8 +431,7 @@ fn can_sync_huge_fork() { fork_blocks .into_iter() .chain( - (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 2 + 1) - .map(|_| build_block(&mut client, None, true)), + (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 2 + 1).map(|_| build_block(&client, None, true)), ) .collect::>() }; @@ -550,13 +549,13 @@ fn can_sync_huge_fork() { fn syncs_fork_without_duplicate_requests() { sp_tracing::try_init_simple(); - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let blocks = (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 4) - .map(|_| build_block(&mut client, None, false)) + .map(|_| build_block(&client, None, false)) .collect::>(); let fork_blocks = { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = TestClientBuilder::new().build(); let fork_blocks = blocks[..MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2] .into_iter() .inspect(|b| block_on(client.import(BlockOrigin::Own, (*b).clone())).unwrap()) @@ -566,8 +565,7 @@ fn syncs_fork_without_duplicate_requests() { fork_blocks .into_iter() .chain( - (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 2 + 1) - .map(|_| build_block(&mut client, None, true)), + (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 2 + 1).map(|_| build_block(&client, None, true)), ) .collect::>() }; @@ -708,8 +706,8 @@ fn syncs_fork_without_duplicate_requests() { #[test] fn removes_target_fork_on_disconnect() { sp_tracing::try_init_simple(); - let mut client = Arc::new(TestClientBuilder::new().build()); - let blocks = (0..3).map(|_| build_block(&mut client, None, false)).collect::>(); + let client = Arc::new(TestClientBuilder::new().build()); + let blocks = (0..3).map(|_| build_block(&client, None, false)).collect::>(); let mut sync = ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None, std::iter::empty()) @@ -733,8 +731,8 @@ fn removes_target_fork_on_disconnect() { #[test] fn can_import_response_with_missing_blocks() { sp_tracing::try_init_simple(); - let mut client2 = Arc::new(TestClientBuilder::new().build()); - let blocks = (0..4).map(|_| build_block(&mut client2, None, false)).collect::>(); + let client2 = TestClientBuilder::new().build(); + let blocks = (0..4).map(|_| build_block(&client2, None, false)).collect::>(); let empty_client = Arc::new(TestClientBuilder::new().build()); @@ -770,14 +768,14 @@ fn ancestor_search_repeat() { #[test] fn sync_restart_removes_block_but_not_justification_requests() { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let mut sync = ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None, std::iter::empty()) .unwrap(); let peers = vec![PeerId::random(), PeerId::random()]; - let mut new_blocks = |n| { + let new_blocks = |n| { for _ in 0..n { let block = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().best_hash) @@ -880,11 +878,11 @@ fn sync_restart_removes_block_but_not_justification_requests() { fn request_across_forks() { sp_tracing::try_init_simple(); - let mut client = Arc::new(TestClientBuilder::new().build()); - let blocks = (0..100).map(|_| build_block(&mut client, None, false)).collect::>(); + let client = Arc::new(TestClientBuilder::new().build()); + let blocks = (0..100).map(|_| build_block(&client, None, false)).collect::>(); let fork_a_blocks = { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = TestClientBuilder::new().build(); let mut fork_blocks = blocks[..] .into_iter() .inspect(|b| { @@ -894,13 +892,13 @@ fn request_across_forks() { .cloned() .collect::>(); for _ in 0..10 { - fork_blocks.push(build_block(&mut client, None, false)); + fork_blocks.push(build_block(&client, None, false)); } fork_blocks }; let fork_b_blocks = { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = TestClientBuilder::new().build(); let mut fork_blocks = blocks[..] .into_iter() .inspect(|b| { @@ -910,7 +908,7 @@ fn request_across_forks() { .cloned() .collect::>(); for _ in 0..10 { - fork_blocks.push(build_block(&mut client, None, true)); + fork_blocks.push(build_block(&client, None, true)); } fork_blocks }; diff --git a/substrate/client/network/test/src/block_import.rs b/substrate/client/network/test/src/block_import.rs index dad5a27e9535..8c120d9de662 100644 --- a/substrate/client/network/test/src/block_import.rs +++ b/substrate/client/network/test/src/block_import.rs @@ -32,7 +32,7 @@ use substrate_test_runtime_client::{ }; fn prepare_good_block() -> (TestClient, Hash, u64, PeerId, IncomingBlock) { - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); let block = BlockBuilderBuilder::new(&client) .on_parent_block(client.chain_info().best_hash) .with_parent_block_number(client.chain_info().best_number) diff --git a/substrate/client/network/test/src/lib.rs b/substrate/client/network/test/src/lib.rs index 6ac20842a33f..9285306948a6 100644 --- a/substrate/client/network/test/src/lib.rs +++ b/substrate/client/network/test/src/lib.rs @@ -217,7 +217,7 @@ impl BlockImport for PeersClient { } async fn import_block( - &mut self, + &self, block: BlockImportParams, ) -> Result { self.client.import_block(block).await @@ -607,7 +607,7 @@ where } async fn import_block( - &mut self, + &self, block: BlockImportParams, ) -> Result { self.inner.import_block(block).await diff --git a/substrate/client/offchain/src/lib.rs b/substrate/client/offchain/src/lib.rs index 48d3b8f1393a..7cee64e6ce7e 100644 --- a/substrate/client/offchain/src/lib.rs +++ b/substrate/client/offchain/src/lib.rs @@ -481,7 +481,7 @@ mod tests { let (client, backend) = substrate_test_runtime_client::TestClientBuilder::new() .enable_offchain_indexing_api() .build_with_backend(); - let mut client = Arc::new(client); + let client = Arc::new(client); let offchain_db = backend.offchain_storage().unwrap(); let key = &b"hello"[..]; diff --git a/substrate/client/rpc-spec-v2/src/archive/tests.rs b/substrate/client/rpc-spec-v2/src/archive/tests.rs index de71ed82a128..078016f5b3e2 100644 --- a/substrate/client/rpc-spec-v2/src/archive/tests.rs +++ b/substrate/client/rpc-spec-v2/src/archive/tests.rs @@ -96,7 +96,7 @@ async fn archive_genesis() { #[tokio::test] async fn archive_body() { - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); // Invalid block hash. let invalid_hash = hex_string(&INVALID_HASH); @@ -130,7 +130,7 @@ async fn archive_body() { #[tokio::test] async fn archive_header() { - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); // Invalid block hash. let invalid_hash = hex_string(&INVALID_HASH); @@ -176,7 +176,7 @@ async fn archive_finalized_height() { #[tokio::test] async fn archive_hash_by_height() { - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); // Genesis height. let hashes: Vec = api.call("archive_unstable_hashByHeight", [0]).await.unwrap(); @@ -282,7 +282,7 @@ async fn archive_hash_by_height() { #[tokio::test] async fn archive_call() { - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); let invalid_hash = hex_string(&INVALID_HASH); // Invalid parameter (non-hex). @@ -341,7 +341,7 @@ async fn archive_call() { #[tokio::test] async fn archive_storage_hashes_values() { - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); let block = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().genesis_hash) @@ -431,7 +431,7 @@ async fn archive_storage_hashes_values() { #[tokio::test] async fn archive_storage_closest_merkle_value() { - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); /// The core of this test. /// @@ -592,7 +592,7 @@ async fn archive_storage_closest_merkle_value() { #[tokio::test] async fn archive_storage_paginate_iterations() { // 1 iteration allowed before pagination kicks in. - let (mut client, api) = setup_api(1, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(1, MAX_QUERIED_LIMIT); // Import a new block with storage changes. let mut builder = BlockBuilderBuilder::new(&*client) @@ -787,7 +787,7 @@ async fn archive_storage_paginate_iterations() { #[tokio::test] async fn archive_storage_discarded_items() { // One query at a time - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, 1); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, 1); // Import a new block with storage changes. let mut builder = BlockBuilderBuilder::new(&*client) diff --git a/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs b/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs index d4d616f54dc8..14325b4fbb98 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs @@ -890,7 +890,7 @@ mod tests { } fn produce_blocks( - mut client: Arc>>, + client: Arc>>, num_blocks: usize, ) -> Vec<::Hash> { let mut blocks = Vec::with_capacity(num_blocks); diff --git a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs index b195e05b6649..38f091471f87 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -137,7 +137,7 @@ async fn setup_api() -> ( CHILD_VALUE.to_vec(), ); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -186,7 +186,7 @@ async fn setup_api() -> ( } async fn import_block( - mut client: Arc>, + client: Arc>, parent_hash: ::Hash, parent_number: u64, ) -> Block { @@ -203,7 +203,7 @@ async fn import_block( } async fn import_best_block_with_tx( - mut client: Arc>, + client: Arc>, parent_hash: ::Hash, parent_number: u64, tx: Transfer, @@ -245,7 +245,7 @@ macro_rules! check_new_and_best_block_events { async fn follow_subscription_produces_blocks() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -316,7 +316,7 @@ async fn follow_subscription_produces_blocks() { async fn follow_with_runtime() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -469,7 +469,7 @@ async fn get_header() { #[tokio::test] async fn get_body() { - let (mut client, api, mut block_sub, sub_id, block) = setup_api().await; + let (client, api, mut block_sub, sub_id, block) = setup_api().await; let block_hash = format!("{:?}", block.header.hash()); let invalid_hash = hex_string(&INVALID_HASH); @@ -626,7 +626,7 @@ async fn call_runtime() { async fn call_runtime_without_flag() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -691,7 +691,7 @@ async fn call_runtime_without_flag() { #[tokio::test] async fn get_storage_hash() { - let (mut client, api, mut block_sub, sub_id, block) = setup_api().await; + let (client, api, mut block_sub, sub_id, block) = setup_api().await; let block_hash = format!("{:?}", block.header.hash()); let invalid_hash = hex_string(&INVALID_HASH); let key = hex_string(&KEY); @@ -835,7 +835,7 @@ async fn get_storage_hash() { #[tokio::test] async fn get_storage_multi_query_iter() { - let (mut client, api, mut block_sub, sub_id, block) = setup_api().await; + let (client, api, mut block_sub, sub_id, block) = setup_api().await; let key = hex_string(&KEY); // Import a new block with storage changes. @@ -959,7 +959,7 @@ async fn get_storage_multi_query_iter() { #[tokio::test] async fn get_storage_value() { - let (mut client, api, mut block_sub, sub_id, block) = setup_api().await; + let (client, api, mut block_sub, sub_id, block) = setup_api().await; let block_hash = format!("{:?}", block.hash()); let invalid_hash = hex_string(&INVALID_HASH); let key = hex_string(&KEY); @@ -1287,7 +1287,7 @@ async fn unique_operation_ids() { async fn separate_operation_ids_for_subscriptions() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -1375,7 +1375,7 @@ async fn separate_operation_ids_for_subscriptions() { async fn follow_generates_initial_blocks() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -1533,7 +1533,7 @@ async fn follow_generates_initial_blocks() { async fn follow_exceeding_pinned_blocks() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -1612,7 +1612,7 @@ async fn follow_exceeding_pinned_blocks() { async fn follow_with_unpin() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -1720,7 +1720,7 @@ async fn follow_with_unpin() { async fn unpin_duplicate_hashes() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -1825,7 +1825,7 @@ async fn unpin_duplicate_hashes() { async fn follow_with_multiple_unpin_hashes() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -1972,7 +1972,7 @@ async fn follow_with_multiple_unpin_hashes() { async fn follow_prune_best_block() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -2160,7 +2160,7 @@ async fn follow_prune_best_block() { async fn follow_forks_pruned_block() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -2322,7 +2322,7 @@ async fn follow_forks_pruned_block() { async fn follow_report_multiple_pruned_block() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -2559,7 +2559,7 @@ async fn pin_block_references() { ) .unwrap(); - let mut client = Arc::new( + let client = Arc::new( new_in_mem::<_, Block, _, RuntimeApi>( backend.clone(), executor, @@ -2705,7 +2705,7 @@ async fn pin_block_references() { async fn follow_finalized_before_new_block() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let client_mock = Arc::new(ChainHeadMockClient::new(client.clone())); @@ -2823,7 +2823,7 @@ async fn ensure_operation_limits_works() { CHILD_VALUE.to_vec(), ); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); // Configure the chainHead with maximum 1 ongoing operations. let api = ChainHead::new( @@ -2930,7 +2930,7 @@ async fn check_continue_operation() { CHILD_VALUE.to_vec(), ); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); // Configure the chainHead with maximum 1 item before asking for pagination. let api = ChainHead::new( @@ -3115,7 +3115,7 @@ async fn stop_storage_operation() { CHILD_VALUE.to_vec(), ); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); // Configure the chainHead with maximum 1 item before asking for pagination. let api = ChainHead::new( @@ -3221,7 +3221,7 @@ async fn stop_storage_operation() { #[tokio::test] async fn storage_closest_merkle_value() { - let (mut client, api, mut sub, sub_id, block) = setup_api().await; + let (client, api, mut sub, sub_id, block) = setup_api().await; /// The core of this test. /// @@ -3414,7 +3414,7 @@ async fn storage_closest_merkle_value() { async fn chain_head_stop_all_subscriptions() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); // Configure the chainHead to stop all subscriptions on lagging distance of 5 blocks. let api = ChainHead::new( diff --git a/substrate/client/rpc/src/chain/tests.rs b/substrate/client/rpc/src/chain/tests.rs index afb81f709f7d..9d78aa88091d 100644 --- a/substrate/client/rpc/src/chain/tests.rs +++ b/substrate/client/rpc/src/chain/tests.rs @@ -72,7 +72,7 @@ async fn should_return_header() { #[tokio::test] async fn should_return_a_block() { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let api = new_full(client.clone(), test_executor()).into_rpc(); let block = BlockBuilderBuilder::new(&*client) @@ -137,7 +137,7 @@ async fn should_return_a_block() { #[tokio::test] async fn should_return_block_hash() { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let api = new_full(client.clone(), test_executor()).into_rpc(); let res: ListOrValue> = @@ -204,7 +204,7 @@ async fn should_return_block_hash() { #[tokio::test] async fn should_return_finalized_hash() { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let api = new_full(client.clone(), test_executor()).into_rpc(); let res: H256 = api.call("chain_getFinalizedHead", EmptyParams::new()).await.unwrap(); @@ -248,7 +248,7 @@ async fn should_notify_about_finalized_block() { } async fn test_head_subscription(method: &str) { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let mut sub = { let api = new_full(client.clone(), test_executor()).into_rpc(); diff --git a/substrate/client/rpc/src/dev/tests.rs b/substrate/client/rpc/src/dev/tests.rs index e8f9ba4990d2..8f09d48e8a5d 100644 --- a/substrate/client/rpc/src/dev/tests.rs +++ b/substrate/client/rpc/src/dev/tests.rs @@ -24,7 +24,7 @@ use substrate_test_runtime_client::{prelude::*, runtime::Block}; #[tokio::test] async fn block_stats_work() { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let api = >::new(client.clone(), DenyUnsafe::No).into_rpc(); let block = BlockBuilderBuilder::new(&*client) @@ -76,7 +76,7 @@ async fn block_stats_work() { #[tokio::test] async fn deny_unsafe_works() { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let api = >::new(client.clone(), DenyUnsafe::Yes).into_rpc(); let block = BlockBuilderBuilder::new(&*client) diff --git a/substrate/client/rpc/src/state/tests.rs b/substrate/client/rpc/src/state/tests.rs index dd866e671c50..86df46f63a0f 100644 --- a/substrate/client/rpc/src/state/tests.rs +++ b/substrate/client/rpc/src/state/tests.rs @@ -216,7 +216,7 @@ async fn should_notify_about_storage_changes() { init_logger(); let mut sub = { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); let api_rpc = api.into_rpc(); @@ -256,7 +256,7 @@ async fn should_send_initial_storage_changes_and_notifications() { init_logger(); let mut sub = { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); let alice_balance_key = [ @@ -304,10 +304,10 @@ async fn should_send_initial_storage_changes_and_notifications() { #[tokio::test] async fn should_query_storage() { - async fn run_tests(mut client: Arc) { + async fn run_tests(client: Arc) { let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); - let mut add_block = |index| { + let add_block = |index| { let mut builder = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().best_hash) .with_parent_block_number(client.chain_info().best_number) diff --git a/substrate/client/service/src/client/client.rs b/substrate/client/service/src/client/client.rs index a2c9212f7b9c..22defd7c5514 100644 --- a/substrate/client/service/src/client/client.rs +++ b/substrate/client/service/src/client/client.rs @@ -1754,7 +1754,7 @@ where /// If you are not sure that there are no BlockImport objects provided by the consensus /// algorithm, don't use this function. async fn import_block( - &mut self, + &self, mut import_block: BlockImportParams, ) -> Result { let span = tracing::span!(tracing::Level::DEBUG, "import_block"); @@ -1854,19 +1854,19 @@ where { type Error = ConsensusError; - async fn import_block( - &mut self, - import_block: BlockImportParams, - ) -> Result { - (&*self).import_block(import_block).await - } - async fn check_block( &self, block: BlockCheckParams, ) -> Result { (&self).check_block(block).await } + + async fn import_block( + &self, + import_block: BlockImportParams, + ) -> Result { + (&self).import_block(import_block).await + } } impl Finalizer for Client diff --git a/substrate/client/service/test/src/client/mod.rs b/substrate/client/service/test/src/client/mod.rs index bd48fae63444..13e63962fe8f 100644 --- a/substrate/client/service/test/src/client/mod.rs +++ b/substrate/client/service/test/src/client/mod.rs @@ -238,7 +238,7 @@ fn client_initializes_from_genesis_ok() { #[test] fn block_builder_works_with_no_transactions() { - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); let block = BlockBuilderBuilder::new(&client) .on_parent_block(client.chain_info().genesis_hash) @@ -256,7 +256,7 @@ fn block_builder_works_with_no_transactions() { #[test] fn block_builder_works_with_transactions() { - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); let mut builder = BlockBuilderBuilder::new(&client) .on_parent_block(client.chain_info().genesis_hash) @@ -316,7 +316,7 @@ fn block_builder_works_with_transactions() { #[test] fn block_builder_does_not_include_invalid() { - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); let mut builder = BlockBuilderBuilder::new(&client) .on_parent_block(client.chain_info().genesis_hash) .with_parent_block_number(0) @@ -390,7 +390,7 @@ fn best_containing_with_genesis_block() { fn uncles_with_only_ancestors() { // block tree: // G -> A1 -> A2 - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); // G -> A1 let a1 = BlockBuilderBuilder::new(&client) @@ -424,7 +424,7 @@ fn uncles_with_multiple_forks() { // A1 -> B2 -> B3 -> B4 // B2 -> C3 // A1 -> D2 - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); // G -> A1 let a1 = BlockBuilderBuilder::new(&client) @@ -584,7 +584,7 @@ fn finality_target_on_longest_chain_with_single_chain_3_blocks() { // block tree: // G -> A1 -> A2 - let (mut client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); + let (client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 let a1 = BlockBuilderBuilder::new(&client) @@ -625,7 +625,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() { // A1 -> B2 -> B3 -> B4 // B2 -> C3 // A1 -> D2 - let (mut client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); + let (client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 let a1 = BlockBuilderBuilder::new(&client) @@ -877,7 +877,7 @@ fn finality_target_on_longest_chain_with_max_depth_higher_than_best() { // block tree: // G -> A1 -> A2 - let (mut client, chain_select) = TestClientBuilder::new().build_with_longest_chain(); + let (client, chain_select) = TestClientBuilder::new().build_with_longest_chain(); let chain = client.chain_info(); // G -> A1 @@ -914,7 +914,7 @@ fn finality_target_with_best_not_on_longest_chain() { // -> B2 -> (B3) -> B4 // ^best - let (mut client, chain_select) = TestClientBuilder::new().build_with_longest_chain(); + let (client, chain_select) = TestClientBuilder::new().build_with_longest_chain(); let chain = client.chain_info(); // G -> A1 @@ -1045,7 +1045,7 @@ fn finality_target_with_best_not_on_longest_chain() { fn import_with_justification() { // block tree: // G -> A1 -> A2 -> A3 - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); let mut finality_notifications = client.finality_notification_stream(); @@ -1099,7 +1099,7 @@ fn import_with_justification() { #[test] fn importing_diverged_finalized_block_should_trigger_reorg() { - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); // G -> A1 -> A2 // \ @@ -1160,7 +1160,7 @@ fn importing_diverged_finalized_block_should_trigger_reorg() { #[test] fn finalizing_diverged_block_should_trigger_reorg() { - let (mut client, select_chain) = TestClientBuilder::new().build_with_longest_chain(); + let (client, select_chain) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 -> A2 // \ @@ -1257,7 +1257,7 @@ fn finalizing_diverged_block_should_trigger_reorg() { #[test] fn finality_notifications_content() { sp_tracing::try_init_simple(); - let (mut client, _select_chain) = TestClientBuilder::new().build_with_longest_chain(); + let (client, _select_chain) = TestClientBuilder::new().build_with_longest_chain(); // -> D3 -> D4 // G -> A1 -> A2 -> A3 @@ -1410,7 +1410,7 @@ fn get_hash_by_block_number_doesnt_panic() { #[test] fn state_reverted_on_reorg() { sp_tracing::try_init_simple(); - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); let current_balance = |client: &substrate_test_runtime_client::TestClient| { client @@ -1492,7 +1492,7 @@ fn doesnt_import_blocks_that_revert_finality() { .unwrap(), ); - let mut client = TestClientBuilder::with_backend(backend).build(); + let client = TestClientBuilder::with_backend(backend).build(); let mut finality_notifications = client.finality_notification_stream(); @@ -1619,7 +1619,7 @@ fn respects_block_rules() { known_bad: &mut HashSet, fork_rules: &mut Vec<(u64, H256)>, ) { - let mut client = if record_only { + let client = if record_only { TestClientBuilder::new().build() } else { TestClientBuilder::new() @@ -1771,7 +1771,7 @@ fn returns_status_for_pruned_blocks() { .unwrap(), ); - let mut client = TestClientBuilder::with_backend(backend).build(); + let client = TestClientBuilder::with_backend(backend).build(); let a1 = BlockBuilderBuilder::new(&client) .on_parent_block(client.chain_info().genesis_hash) @@ -2160,7 +2160,7 @@ fn cleans_up_closed_notification_sinks_on_block_import() { /// Test that ensures that we always send an import notification for re-orgs. #[test] fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifications() { - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let mut notification_stream = futures::executor::block_on_stream(client.import_notification_stream()); @@ -2232,7 +2232,7 @@ fn use_dalek_ext_works() { sp_core::ed25519::Signature::default() } - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); client.execution_extensions().set_extensions_factory( sc_client_api::execution_extensions::ExtensionBeforeBlock::::new( @@ -2263,7 +2263,7 @@ fn use_dalek_ext_works() { #[test] fn finalize_after_best_block_updates_best() { - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); // G -> A1 let a1 = BlockBuilderBuilder::new(&client) diff --git a/substrate/client/transaction-pool/tests/pool.rs b/substrate/client/transaction-pool/tests/pool.rs index 49bd2203c12b..6d70b6ce67ec 100644 --- a/substrate/client/transaction-pool/tests/pool.rs +++ b/substrate/client/transaction-pool/tests/pool.rs @@ -980,7 +980,7 @@ fn ready_set_should_eventually_resolve_when_block_update_arrives() { #[test] fn import_notification_to_pool_maintain_works() { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let best_hash = client.info().best_hash; let finalized_hash = client.info().finalized_hash; diff --git a/substrate/test-utils/client/src/client_ext.rs b/substrate/test-utils/client/src/client_ext.rs index 9dc4739eb795..a4f91f2ec836 100644 --- a/substrate/test-utils/client/src/client_ext.rs +++ b/substrate/test-utils/client/src/client_ext.rs @@ -42,25 +42,22 @@ pub trait ClientExt: Sized { #[async_trait::async_trait] pub trait ClientBlockImportExt: Sized { /// Import block to the chain. No finality. - async fn import(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>; + async fn import(&self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>; /// Import a block and make it our best block if possible. - async fn import_as_best( - &mut self, - origin: BlockOrigin, - block: Block, - ) -> Result<(), ConsensusError>; + async fn import_as_best(&self, origin: BlockOrigin, block: Block) + -> Result<(), ConsensusError>; /// Import a block and finalize it. async fn import_as_final( - &mut self, + &self, origin: BlockOrigin, block: Block, ) -> Result<(), ConsensusError>; /// Import block with justification(s), finalizes block. async fn import_justified( - &mut self, + &self, origin: BlockOrigin, block: Block, justifications: Justifications, @@ -94,7 +91,7 @@ where for<'r> &'r T: BlockImport, T: Send + Sync, { - async fn import(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { + async fn import(&self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); let mut import = BlockImportParams::new(origin, header); import.body = Some(extrinsics); @@ -104,7 +101,7 @@ where } async fn import_as_best( - &mut self, + &self, origin: BlockOrigin, block: Block, ) -> Result<(), ConsensusError> { @@ -117,7 +114,7 @@ where } async fn import_as_final( - &mut self, + &self, origin: BlockOrigin, block: Block, ) -> Result<(), ConsensusError> { @@ -131,7 +128,7 @@ where } async fn import_justified( - &mut self, + &self, origin: BlockOrigin, block: Block, justifications: Justifications, @@ -151,11 +148,11 @@ where impl ClientBlockImportExt for Client where Self: BlockImport, - RA: Send, + RA: Send + Sync, B: Send + Sync, E: Send + Sync, { - async fn import(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { + async fn import(&self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); let mut import = BlockImportParams::new(origin, header); import.body = Some(extrinsics); @@ -165,7 +162,7 @@ where } async fn import_as_best( - &mut self, + &self, origin: BlockOrigin, block: Block, ) -> Result<(), ConsensusError> { @@ -178,7 +175,7 @@ where } async fn import_as_final( - &mut self, + &self, origin: BlockOrigin, block: Block, ) -> Result<(), ConsensusError> { @@ -192,7 +189,7 @@ where } async fn import_justified( - &mut self, + &self, origin: BlockOrigin, block: Block, justifications: Justifications, diff --git a/substrate/test-utils/runtime/client/src/trait_tests.rs b/substrate/test-utils/runtime/client/src/trait_tests.rs index 6f6bb5c36ee4..c3a5f173d14e 100644 --- a/substrate/test-utils/runtime/client/src/trait_tests.rs +++ b/substrate/test-utils/runtime/client/src/trait_tests.rs @@ -46,7 +46,7 @@ where // B2 -> C3 // A1 -> D2 - let mut client = TestClientBuilder::with_backend(backend.clone()).build(); + let client = TestClientBuilder::with_backend(backend.clone()).build(); let blockchain = backend.blockchain(); let genesis_hash = client.chain_info().genesis_hash; @@ -221,7 +221,7 @@ where // B2 -> C3 // A1 -> D2 - let mut client = TestClientBuilder::with_backend(backend.clone()).build(); + let client = TestClientBuilder::with_backend(backend.clone()).build(); let blockchain = backend.blockchain(); let genesis_hash = client.chain_info().genesis_hash; @@ -390,7 +390,7 @@ where // A1 -> B2 -> B3 -> B4 // B2 -> C3 // A1 -> D2 - let mut client = TestClientBuilder::with_backend(backend.clone()).build(); + let client = TestClientBuilder::with_backend(backend.clone()).build(); let blockchain = backend.blockchain(); let genesis_hash = client.chain_info().genesis_hash; diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index d1a3eaa2daa9..514f3bcba204 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -1062,7 +1062,7 @@ mod tests { // This tests that the on-chain `HEAP_PAGES` parameter is respected. // Create a client devoting only 8 pages of wasm memory. This gives us ~512k of heap memory. - let mut client = TestClientBuilder::new().set_heap_pages(8).build(); + let client = TestClientBuilder::new().set_heap_pages(8).build(); let best_hash = client.chain_info().best_hash; // Try to allocate 1024k of memory on heap. This is going to fail since it is twice larger From 3fe22d17c4904c5ae016034b6ec2c335464b4165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Sun, 18 Aug 2024 21:41:47 +0200 Subject: [PATCH 132/382] binary-merkle-tree: Do not spam test output (#5376) The CI isn't happy with the amount of output: https://gitlab.parity.io/parity/mirrors/polkadot-sdk/-/jobs/7035621/raw --------- Co-authored-by: Shawn Tabrizi --- prdoc/pr_5376.prdoc | 3 +++ substrate/utils/binary-merkle-tree/src/lib.rs | 9 --------- 2 files changed, 3 insertions(+), 9 deletions(-) create mode 100644 prdoc/pr_5376.prdoc diff --git a/prdoc/pr_5376.prdoc b/prdoc/pr_5376.prdoc new file mode 100644 index 000000000000..c9874ef7d865 --- /dev/null +++ b/prdoc/pr_5376.prdoc @@ -0,0 +1,3 @@ +crates: + - name: binary-merkle-tree + bump: none diff --git a/substrate/utils/binary-merkle-tree/src/lib.rs b/substrate/utils/binary-merkle-tree/src/lib.rs index 030e9f69b9b6..f2d338cf028e 100644 --- a/substrate/utils/binary-merkle-tree/src/lib.rs +++ b/substrate/utils/binary-merkle-tree/src/lib.rs @@ -356,7 +356,6 @@ mod tests { #[test] fn should_generate_empty_root() { // given - sp_tracing::init_for_tests(); let data: Vec<[u8; 1]> = Default::default(); // when @@ -372,7 +371,6 @@ mod tests { #[test] fn should_generate_single_root() { // given - sp_tracing::init_for_tests(); let data = vec![array_bytes::hex2array_unchecked::<_, 20>( "E04CC55ebEE1cBCE552f250e85c57B70B2E2625b", )]; @@ -390,7 +388,6 @@ mod tests { #[test] fn should_generate_root_pow_2() { // given - sp_tracing::init_for_tests(); let data = vec![ array_bytes::hex2array_unchecked::<_, 20>("E04CC55ebEE1cBCE552f250e85c57B70B2E2625b"), array_bytes::hex2array_unchecked::<_, 20>("25451A4de12dcCc2D166922fA938E900fCc4ED24"), @@ -408,7 +405,6 @@ mod tests { #[test] fn should_generate_root_complex() { - sp_tracing::init_for_tests(); let test = |root, data| { assert_eq!(array_bytes::bytes2hex("", &merkle_root::(data)), root); }; @@ -437,7 +433,6 @@ mod tests { #[test] fn should_generate_and_verify_proof_simple() { // given - sp_tracing::init_for_tests(); let data = vec!["a", "b", "c"]; // when @@ -501,7 +496,6 @@ mod tests { #[test] fn should_generate_and_verify_proof_complex() { // given - sp_tracing::init_for_tests(); let data = vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; for l in 0..data.len() { @@ -521,7 +515,6 @@ mod tests { #[test] fn should_generate_and_verify_proof_large() { // given - sp_tracing::init_for_tests(); let mut data = vec![]; for i in 1..16 { for c in 'a'..'z' { @@ -548,7 +541,6 @@ mod tests { #[test] fn should_generate_and_verify_proof_large_tree() { // given - sp_tracing::init_for_tests(); let mut data = vec![]; for i in 0..6000 { data.push(format!("{}", i)); @@ -571,7 +563,6 @@ mod tests { #[test] #[should_panic] fn should_panic_on_invalid_leaf_index() { - sp_tracing::init_for_tests(); merkle_proof::(vec!["a"], 5); } From a4b51593dbe9b9390fa64f5040a9230b5d33b898 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Mon, 19 Aug 2024 15:05:54 +0200 Subject: [PATCH 133/382] Repair the docs TOC (#5388) ## Before ## After --- docs/sdk/assets/header.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/sdk/assets/header.html b/docs/sdk/assets/header.html index f55c31b53216..c24c10940759 100644 --- a/docs/sdk/assets/header.html +++ b/docs/sdk/assets/header.html @@ -14,12 +14,13 @@ headers.forEach(header => { let link = document.createElement("a"); link.href = "#" + header.id; - link.textContent = header.textContent; + const headerTextContent = header.textContent.replace("§", "") + link.textContent = headerTextContent; link.className = header.tagName.toLowerCase(); toc.appendChild(link); - if (header.id == "modules" && header.textContent == "Modules") { + if (header.id == "modules" && headerTextContent == "Modules") { modules.forEach(module => { let link = document.createElement("a"); link.href = module.href; From 946afaabd8244f1256f3aecff75e23c02937bd38 Mon Sep 17 00:00:00 2001 From: Egor_P Date: Mon, 19 Aug 2024 17:47:50 +0200 Subject: [PATCH 134/382] Fix publishing of the`chain-spec-builder` image (#5387) This PR fixes the issue with the publishing flow of the `chain-speck-builder` image Closes: https://github.com/paritytech/release-engineering/issues/219 --- .../workflows/release-50_publish-docker.yml | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index f09ecf1c7998..a749c86faa0c 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -75,12 +75,13 @@ env: # EVENT_ACTION: ${{ github.event.action }} EVENT_NAME: ${{ github.event_name }} IMAGE_TYPE: ${{ inputs.image_type }} - VERSION: ${{ inputs.version }} jobs: validate-inputs: runs-on: ubuntu-latest outputs: + version: ${{ steps.validate_inputs.outputs.VERSION }} + release_id: ${{ steps.validate_inputs.outputs.RELEASE_ID }} stable_tag: ${{ steps.validate_inputs.outputs.stable_tag }} steps: @@ -93,10 +94,12 @@ jobs: . ./.github/scripts/common/lib.sh VERSION=$(filter_version_from_input "${{ inputs.version }}") - echo "VERSION=${VERSION}" >> $GITHUB_ENV + echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT RELEASE_ID=$(check_release_id "${{ inputs.release_id }}") - echo "RELEASE_ID=${RELEASE_ID}" >> $GITHUB_ENV + echo "RELEASE_ID=${RELEASE_ID}" >> $GITHUB_OUTPUT + + echo "Release ID: $RELEASE_ID" STABLE_TAG=$(validate_stable_tag ${{ inputs.stable_tag }}) echo "stable_tag=${STABLE_TAG}" >> $GITHUB_OUTPUT @@ -104,6 +107,7 @@ jobs: fetch-artifacts: # this job will be triggered for the polkadot-parachain rc and release or polkadot rc image build if: ${{ inputs.binary == 'polkadot-parachain' || inputs.binary == 'chain-spec-builder' || inputs.image_type == 'rc' }} runs-on: ubuntu-latest + needs: [validate-inputs] steps: - name: Checkout sources @@ -129,6 +133,7 @@ jobs: run: | . ./.github/scripts/common/lib.sh + VERSION="${{ needs.validate-inputs.outputs.VERSION }}" fetch_release_artifacts_from_s3 - name: Fetch chain-spec-builder rc artifacts or release artifacts based on release id @@ -137,6 +142,7 @@ jobs: run: | . ./.github/scripts/common/lib.sh + RELEASE_ID="${{ needs.validate-inputs.outputs.RELEASE_ID }}" fetch_release_artifacts - name: Upload artifacts @@ -181,8 +187,7 @@ jobs: run: | . ./.github/scripts/common/lib.sh - RELEASE_ID=$(check_release_id "${{ inputs.release_id }}") - release=release-$RELEASE_ID && \ + release="release-${{ needs.validate-inputs.outputs.RELEASE_ID }}" && \ echo "release=${release}" >> $GITHUB_OUTPUT commit=$(git rev-parse --short HEAD) && \ @@ -198,9 +203,14 @@ jobs: id: fetch_release_refs run: | chmod a+rx $BINARY - [[ $BINARY != 'chain-spec-builder' ]] && VERSION=$(./$BINARY --version | awk '{ print $2 }' ) - release=$( echo $VERSION | cut -f1 -d- ) + if [[ $BINARY != 'chain-spec-builder' ]]; then + VERSION=$(./$BINARY --version | awk '{ print $2 }' ) + release=$( echo $VERSION | cut -f1 -d- ) + else + release=$(echo ${{ needs.validate-inputs.outputs.VERSION }} | sed 's/^v//') + fi + echo "tag=latest" >> $GITHUB_OUTPUT echo "release=${release}" >> $GITHUB_OUTPUT echo "stable=${{ needs.validate-inputs.outputs.stable_tag }}" >> $GITHUB_OUTPUT From 37c20272c9fb93a37e828f539da20729d0f22820 Mon Sep 17 00:00:00 2001 From: Tsvetomir Dimitrov Date: Tue, 20 Aug 2024 14:01:13 +0300 Subject: [PATCH 135/382] Fix leases with gaps and time slice calculation in `MigrateToCoretime` (#5380) Backport fixes from https://github.com/polkadot-fellows/runtimes/pull/426 --- .../parachains/src/coretime/migration.rs | 70 ++++++++++++------- polkadot/runtime/rococo/src/lib.rs | 9 ++- polkadot/runtime/westend/src/lib.rs | 20 +----- prdoc/pr_5380.prdoc | 15 ++++ 4 files changed, 69 insertions(+), 45 deletions(-) create mode 100644 prdoc/pr_5380.prdoc diff --git a/polkadot/runtime/parachains/src/coretime/migration.rs b/polkadot/runtime/parachains/src/coretime/migration.rs index 4e7508867559..d4be135aad65 100644 --- a/polkadot/runtime/parachains/src/coretime/migration.rs +++ b/polkadot/runtime/parachains/src/coretime/migration.rs @@ -24,7 +24,6 @@ mod v_coretime { use crate::{ assigner_coretime, configuration, coretime::{mk_coretime_call, Config, PartsOf57600, WeightInfo}, - paras, }; use alloc::{vec, vec::Vec}; #[cfg(feature = "try-runtime")] @@ -51,18 +50,24 @@ mod v_coretime { pub trait GetLegacyLease { /// If parachain is a lease holding parachain, return the block at which the lease expires. fn get_parachain_lease_in_blocks(para: ParaId) -> Option; + // All parachains holding a lease, no matter if there are gaps in the slots or not. + fn get_all_parachains_with_leases() -> Vec; } /// Migrate a chain to use coretime. /// /// This assumes that the `Coretime` and the `AssignerCoretime` pallets are added at the same /// time to a runtime. - pub struct MigrateToCoretime( + pub struct MigrateToCoretime( core::marker::PhantomData<(T, SendXcm, LegacyLease)>, ); - impl>> - MigrateToCoretime + impl< + T: Config, + SendXcm: xcm::v4::SendXcm, + LegacyLease: GetLegacyLease>, + const TIMESLICE_PERIOD: u32, + > MigrateToCoretime { fn already_migrated() -> bool { // We are using the assigner coretime because the coretime pallet doesn't has any @@ -94,7 +99,8 @@ mod v_coretime { T: Config + crate::dmp::Config, SendXcm: xcm::v4::SendXcm, LegacyLease: GetLegacyLease>, - > OnRuntimeUpgrade for MigrateToCoretime + const TIMESLICE_PERIOD: u32, + > OnRuntimeUpgrade for MigrateToCoretime { fn on_runtime_upgrade() -> Weight { if Self::already_migrated() { @@ -102,7 +108,7 @@ mod v_coretime { } log::info!("Migrating existing parachains to coretime."); - migrate_to_coretime::() + migrate_to_coretime::() } #[cfg(feature = "try-runtime")] @@ -111,7 +117,7 @@ mod v_coretime { return Ok(Vec::new()) } - let legacy_paras = paras::Parachains::::get(); + let legacy_paras = LegacyLease::get_all_parachains_with_leases(); let config = configuration::ActiveConfig::::get(); let total_core_count = config.scheduler_params.num_cores + legacy_paras.len() as u32; @@ -154,8 +160,9 @@ mod v_coretime { T: Config, SendXcm: xcm::v4::SendXcm, LegacyLease: GetLegacyLease>, + const TIMESLICE_PERIOD: u32, >() -> Weight { - let legacy_paras = paras::Parachains::::get(); + let legacy_paras = LegacyLease::get_all_parachains_with_leases(); let legacy_count = legacy_paras.len() as u32; let now = frame_system::Pallet::::block_number(); for (core, para_id) in legacy_paras.into_iter().enumerate() { @@ -175,7 +182,6 @@ mod v_coretime { } let config = configuration::ActiveConfig::::get(); - // num_cores was on_demand_cores until now: for on_demand in 0..config.scheduler_params.num_cores { let core = CoreIndex(legacy_count.saturating_add(on_demand as _)); let r = assigner_coretime::Pallet::::assign_core( @@ -193,7 +199,9 @@ mod v_coretime { c.scheduler_params.num_cores = total_cores; }); - if let Err(err) = migrate_send_assignments_to_coretime_chain::() { + if let Err(err) = + migrate_send_assignments_to_coretime_chain::( + ) { log::error!("Sending legacy chain data to coretime chain failed: {:?}", err); } @@ -210,8 +218,9 @@ mod v_coretime { T: Config, SendXcm: xcm::v4::SendXcm, LegacyLease: GetLegacyLease>, + const TIMESLICE_PERIOD: u32, >() -> result::Result<(), SendError> { - let legacy_paras = paras::Parachains::::get(); + let legacy_paras = LegacyLease::get_all_parachains_with_leases(); let legacy_paras_count = legacy_paras.len(); let (system_chains, lease_holding): (Vec<_>, Vec<_>) = legacy_paras.into_iter().partition(IsSystem::is_system); @@ -224,7 +233,7 @@ mod v_coretime { mk_coretime_call::(crate::coretime::CoretimeCalls::Reserve(schedule)) }); - let leases = lease_holding.into_iter().filter_map(|p| { + let mut leases = lease_holding.into_iter().filter_map(|p| { log::trace!(target: "coretime-migration", "Preparing sending of lease holding para {:?}", p); let Some(valid_until) = LegacyLease::get_parachain_lease_in_blocks(p) else { log::error!("Lease holding chain with no lease information?!"); @@ -237,10 +246,7 @@ mod v_coretime { return None }, }; - // We assume the coretime chain set this parameter to the recommended value in RFC-1: - const TIME_SLICE_PERIOD: u32 = 80; - let round_up = if valid_until % TIME_SLICE_PERIOD > 0 { 1 } else { 0 }; - let time_slice = valid_until / TIME_SLICE_PERIOD + TIME_SLICE_PERIOD * round_up; + let time_slice = (valid_until + TIMESLICE_PERIOD - 1) / TIMESLICE_PERIOD; log::trace!(target: "coretime-migration", "Sending of lease holding para {:?}, valid_until: {:?}, time_slice: {:?}", p, valid_until, time_slice); Some(mk_coretime_call::(crate::coretime::CoretimeCalls::SetLease(p.into(), time_slice))) }); @@ -269,16 +275,30 @@ mod v_coretime { }); let reservation_content = message_content.clone().chain(reservations).collect(); - let pool_content = message_content.clone().chain(pool).collect(); - let leases_content = message_content.clone().chain(leases).collect(); + let leases_content_1 = message_content + .clone() + .chain(leases.by_ref().take(legacy_paras_count / 2)) // split in two messages to avoid overweighted XCM + .collect(); + let leases_content_2 = message_content.clone().chain(leases).collect(); let set_core_count_content = message_content.clone().chain(set_core_count).collect(); - - let messages = vec![ - Xcm(reservation_content), - Xcm(pool_content), - Xcm(leases_content), - Xcm(set_core_count_content), - ]; + // If `pool_content` is empty don't send a blank XCM message + let messages = if core_count as usize > legacy_paras_count { + let pool_content = message_content.clone().chain(pool).collect(); + vec![ + Xcm(reservation_content), + Xcm(pool_content), + Xcm(leases_content_1), + Xcm(leases_content_2), + Xcm(set_core_count_content), + ] + } else { + vec![ + Xcm(reservation_content), + Xcm(leases_content_1), + Xcm(leases_content_2), + Xcm(set_core_count_content), + ] + }; for message in messages { send_xcm::( diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 30b915d32deb..31713755b9b2 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -1580,6 +1580,13 @@ pub mod migrations { as Leaser>::lease_period_index(now)?; Some(index.saturating_add(lease.len() as u32).saturating_mul(LeasePeriod::get())) } + + fn get_all_parachains_with_leases() -> Vec { + slots::Leases::::iter() + .filter(|(_, lease)| !lease.is_empty()) + .map(|(para, _)| para) + .collect::>() + } } parameter_types! { @@ -1661,7 +1668,7 @@ pub mod migrations { pallet_identity::migration::versioned::V0ToV1, parachains_configuration::migration::v11::MigrateToV11, // This needs to come after the `parachains_configuration` above as we are reading the configuration. - coretime::migration::MigrateToCoretime, + coretime::migration::MigrateToCoretime, parachains_configuration::migration::v12::MigrateToV12, parachains_on_demand::migration::MigrateV0ToV1, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 5f8d5d424938..519c7dcde54e 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -65,7 +65,7 @@ use polkadot_runtime_common::{ VersionedLocatableAsset, VersionedLocationConverter, }, paras_registrar, paras_sudo_wrapper, prod_or_fast, slots, - traits::{Leaser, OnSwap}, + traits::OnSwap, BalanceToU256, BlockHashCount, BlockLength, CurrencyToVote, SlowAdjustingFeeUpdate, U256ToBalance, }; @@ -1775,24 +1775,6 @@ pub type Migrations = migrations::Unreleased; pub mod migrations { use super::*; - pub struct GetLegacyLeaseImpl; - impl coretime::migration::GetLegacyLease for GetLegacyLeaseImpl { - fn get_parachain_lease_in_blocks(para: ParaId) -> Option { - let now = frame_system::Pallet::::block_number(); - let lease = slots::Leases::::get(para); - if lease.is_empty() { - return None; - } - // Lease not yet started, ignore: - if lease.iter().any(Option::is_none) { - return None; - } - let (index, _) = - as Leaser>::lease_period_index(now)?; - Some(index.saturating_add(lease.len() as u32).saturating_mul(LeasePeriod::get())) - } - } - /// Unreleased migrations. Add new ones here: pub type Unreleased = ( // This is only needed for Westend. diff --git a/prdoc/pr_5380.prdoc b/prdoc/pr_5380.prdoc new file mode 100644 index 000000000000..75063e335343 --- /dev/null +++ b/prdoc/pr_5380.prdoc @@ -0,0 +1,15 @@ +title: Fix leases with gaps and time slice calculation in MigrateToCoretime + +doc: + - audience: Runtime Dev + description: | + Agile Coretime storage migration wasn't transferring correctly leases with gaps and was + miscalculating time lease period. This patch provides fixes for both issues. + +crates: + - name: rococo-runtime + bump: patch + - name: westend-runtime + bump: major + - name: polkadot-runtime-parachains + bump: patch \ No newline at end of file From f239abac93b8354fec1441e39727b0706b489415 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Tue, 20 Aug 2024 14:19:30 +0300 Subject: [PATCH 136/382] approval-distribution: Fix preallocation of ApprovalEntries (#5411) We preallocated the approvals field in the ApprovalEntry by up to a factor of two in the worse conditions, since we can't have more than 6 approvals and candidates.len() will return 20 if you have just the 20th bit set. This adds to a lot of wasted memory because we have an ApprovalEntry for each assignment we received This was discovered while running rust jemalloc-profiling with the steps from here: https://www.magiroux.com/rust-jemalloc-profiling/ Just with this optimisation approvals subsystem-benchmark memory usage on the worst case scenario is reduced from 6.1GiB to 2.4 GiB, even cpu usage of approval-distribution decreases by 4-5%. --------- Signed-off-by: Alexandru Gheorghe --- polkadot/node/network/approval-distribution/src/lib.rs | 2 +- prdoc/pr_5411.prdoc | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 prdoc/pr_5411.prdoc diff --git a/polkadot/node/network/approval-distribution/src/lib.rs b/polkadot/node/network/approval-distribution/src/lib.rs index 134586253ce1..a1bdc47e9fb2 100644 --- a/polkadot/node/network/approval-distribution/src/lib.rs +++ b/polkadot/node/network/approval-distribution/src/lib.rs @@ -161,7 +161,7 @@ impl ApprovalEntry { Self { validator_index: assignment.validator, assignment, - approvals: HashMap::with_capacity(candidates.len()), + approvals: HashMap::new(), assignment_claimed_candidates: candidates, routing_info, } diff --git a/prdoc/pr_5411.prdoc b/prdoc/pr_5411.prdoc new file mode 100644 index 000000000000..c24001d77bda --- /dev/null +++ b/prdoc/pr_5411.prdoc @@ -0,0 +1,3 @@ +crates: + - name: polkadot-approval-distribution + bump: none From 73e2316adad1582a2113301e4f2938d68ca10974 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Tue, 20 Aug 2024 16:02:19 +0300 Subject: [PATCH 137/382] Fix mmr zombienet test (#5417) Fixes https://github.com/paritytech/polkadot-sdk/issues/4309 If a new block is generated between these 2 lines: ``` const proof = await apis[nodeName].rpc.mmr.generateProof([1, 9, 20]); const root = await apis[nodeName].rpc.mmr.root() ``` we will try to verify a proof for the previous block with the mmr root at the current block. Which will fail. So we generate the proof and get the mmr root at block 21 for consistency. --- .../functional/0003-mmr-generate-and-verify-proof.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/polkadot/zombienet_tests/functional/0003-mmr-generate-and-verify-proof.js b/polkadot/zombienet_tests/functional/0003-mmr-generate-and-verify-proof.js index 6583173e40c3..20d0c2a988b1 100644 --- a/polkadot/zombienet_tests/functional/0003-mmr-generate-and-verify-proof.js +++ b/polkadot/zombienet_tests/functional/0003-mmr-generate-and-verify-proof.js @@ -3,9 +3,9 @@ const common = require('./0003-common.js'); async function run(nodeName, networkInfo, nodeNames) { const apis = await common.getApis(networkInfo, nodeNames); - const proof = await apis[nodeName].rpc.mmr.generateProof([1, 9, 20]); - - const root = await apis[nodeName].rpc.mmr.root() + let at = await apis[nodeName].rpc.chain.getBlockHash(21); + const root = await apis[nodeName].rpc.mmr.root(at); + const proof = await apis[nodeName].rpc.mmr.generateProof([1, 9, 20], 21, at); const proofVerifications = await Promise.all( Object.values(apis).map(async (api) => { From 717bbb24c40717e70d2e3b648bcd372559c71bd2 Mon Sep 17 00:00:00 2001 From: Javier Bullrich Date: Tue, 20 Aug 2024 16:16:57 +0100 Subject: [PATCH 138/382] Migrated `docs` scripts to GitHub actions (#5345) Migrated the following scripts to GHA - test-doc - test-rustdoc - build-rustdoc - build-implementers-guide - publish-rustdoc (only runs when `master` is modified) Resolves paritytech/ci_cd#1016 --- Some questions I have: - Should I remove the equivalent scripts from the `gitlab-ci` files? --------- Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> --- .github/workflows/docs.yml | 141 +++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000000..523c2b19ba89 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,141 @@ +name: Docs + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened, ready_for_review, labeled] + merge_group: + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} + +jobs: + set-image: + # GitHub Actions allows using 'env' in a container context. + # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 + # This workaround sets the container image for each job using 'set-image' job output. + # TODO: remove once migration is complete or this workflow is fully stable + if: contains(github.event.label.name, 'GHA-migration') || contains(github.event.pull_request.labels.*.name, 'GHA-migration') + runs-on: ubuntu-latest + outputs: + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT + test-rustdoc: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image] + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - uses: actions/checkout@v4 + - run: forklift cargo doc --workspace --all-features --no-deps + env: + SKIP_WASM_BUILD: 1 + test-doc: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image] + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - uses: actions/checkout@v4 + - run: forklift cargo test --doc --workspace + env: + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + + build-rustdoc: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image, test-rustdoc] + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - uses: actions/checkout@v4 + - run: forklift cargo doc --all-features --workspace --no-deps + env: + SKIP_WASM_BUILD: 1 + RUSTDOCFLAGS: "-Dwarnings --default-theme=ayu --html-in-header ./docs/sdk/assets/header.html --extend-css ./docs/sdk/assets/theme.css --html-after-content ./docs/sdk/assets/after-content.html" + - run: rm -f ./target/doc/.lock + - run: mv ./target/doc ./crate-docs + - name: Inject Simple Analytics script + run: | + script_content="" + docs_dir="./crate-docs" + + inject_simple_analytics() { + find "$1" -name '*.html' | xargs -I {} -P "$(nproc)" bash -c 'file="{}"; echo "Adding Simple Analytics script to $file"; sed -i "s||'"$2"'|" "$file";' + } + + inject_simple_analytics "$docs_dir" "$script_content" + - run: echo "" > ./crate-docs/index.html + - uses: actions/upload-artifact@v4 + with: + name: ${{ github.sha }}-doc + path: ./crate-docs/ + retention-days: 1 + if-no-files-found: error + + build-implementers-guide: + runs-on: ubuntu-latest + container: + image: paritytech/mdbook-utils:e14aae4a-20221123 + options: --user root + steps: + - uses: actions/checkout@v4 + - run: mdbook build ./polkadot/roadmap/implementers-guide + - run: mkdir -p artifacts + - run: mv polkadot/roadmap/implementers-guide/book artifacts/ + - uses: actions/upload-artifact@v4 + with: + name: ${{ github.sha }}-guide + path: ./artifacts/ + retention-days: 1 + if-no-files-found: error + + publish-rustdoc: + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + environment: subsystem-benchmarks + needs: [build-rustdoc, build-implementers-guide] + steps: + - uses: actions/checkout@v4 + with: + ref: gh-pages + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ secrets.POLKADOTSDK_GHPAGES_APP_ID }} + private-key: ${{ secrets.POLKADOTSDK_GHPAGES_APP_KEY }} + - name: Ensure destination dir does not exist + run: | + rm -rf book/ + rm -rf ${REF_NAME} + env: + REF_NAME: ${{ github.head_ref || github.ref_name }} + - name: Download rustdocs + uses: actions/download-artifact@v4 + with: + name: ${{ github.sha }}-doc + path: ${{ github.head_ref || github.ref_name }} + - name: Download guide + uses: actions/download-artifact@v4 + with: + name: ${{ github.sha }}-guide + path: /tmp + - run: mkdir -p book + - name: Move book files + run: mv /tmp/book/html/* book/ + - name: Push to GH-Pages branch + uses: github-actions-x/commit@v2.9 + with: + github-token: ${{ steps.app-token.outputs.token }} + push-branch: "gh-pages" + commit-message: "___Updated docs for ${{ github.head_ref || github.ref_name }}___" + force-add: "true" + files: ${{ github.head_ref || github.ref_name }}/ book/ + name: devops-parity + email: devops-team@parity.io From 5ca3d2ebdfdaffce7a6678a26bbda8e9672b6bf6 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 21 Aug 2024 18:28:12 +0300 Subject: [PATCH 139/382] polkadot-parachain: compile separate lib and bin (#5288) Related to https://github.com/paritytech/polkadot-sdk/issues/5210 --- Cargo.lock | 59 +- Cargo.toml | 3 + cumulus/polkadot-parachain/Cargo.toml | 97 +- .../polkadot-parachain-lib/Cargo.toml | 117 +++ .../polkadot-parachain-lib/src/cli.rs | 391 ++++++++ .../polkadot-parachain-lib/src/command.rs | 293 ++++++ .../src/common/aura.rs | 6 +- .../src/common/chain_spec.rs | 77 ++ .../src/common/mod.rs | 4 +- .../src/common/runtime.rs | 61 ++ .../asset_hub_polkadot_aura.rs | 5 +- .../src/fake_runtime_api/aura.rs | 5 +- .../src/fake_runtime_api/mod.rs | 0 .../polkadot-parachain-lib/src/lib.rs | 51 ++ .../{ => polkadot-parachain-lib}/src/rpc.rs | 0 .../src/service.rs | 0 .../src}/tests/benchmark_storage_works.rs | 0 .../src}/tests/common.rs | 0 .../src}/tests/polkadot_argument_parsing.rs | 0 .../src}/tests/polkadot_mdns_issue.rs | 0 .../src}/tests/purge_chain_works.rs | 0 .../tests/running_the_node_and_interrupt.rs | 0 .../src/chain_spec/asset_hubs.rs | 6 +- .../src/chain_spec/bridge_hubs.rs | 9 +- .../src/chain_spec/collectives.rs | 6 +- .../src/chain_spec/contracts.rs | 136 ++- .../src/chain_spec/coretime.rs | 10 +- .../src/chain_spec/glutton.rs | 3 +- .../polkadot-parachain/src/chain_spec/mod.rs | 356 +++++++- .../src/chain_spec/penpal.rs | 6 +- .../src/chain_spec/people.rs | 10 +- .../src/chain_spec/rococo_parachain.rs | 3 +- .../src/chain_spec/seedling.rs | 3 +- .../src/chain_spec/shell.rs | 2 +- cumulus/polkadot-parachain/src/cli.rs | 155 ---- cumulus/polkadot-parachain/src/command.rs | 856 ------------------ cumulus/polkadot-parachain/src/main.rs | 48 +- .../src/guides/enable_elastic_scaling_mvp.rs | 4 +- prdoc/pr_5288.prdoc | 17 + umbrella/Cargo.toml | 9 +- umbrella/src/lib.rs | 4 + 41 files changed, 1528 insertions(+), 1284 deletions(-) create mode 100644 cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml create mode 100644 cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs create mode 100644 cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib}/src/common/aura.rs (95%) create mode 100644 cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/chain_spec.rs rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib}/src/common/mod.rs (97%) create mode 100644 cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib}/src/fake_runtime_api/asset_hub_polkadot_aura.rs (98%) rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib}/src/fake_runtime_api/aura.rs (97%) rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib}/src/fake_runtime_api/mod.rs (100%) create mode 100644 cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib}/src/rpc.rs (100%) rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib}/src/service.rs (100%) rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib/src}/tests/benchmark_storage_works.rs (100%) rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib/src}/tests/common.rs (100%) rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib/src}/tests/polkadot_argument_parsing.rs (100%) rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib/src}/tests/polkadot_mdns_issue.rs (100%) rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib/src}/tests/purge_chain_works.rs (100%) rename cumulus/polkadot-parachain/{ => polkadot-parachain-lib/src}/tests/running_the_node_and_interrupt.rs (100%) delete mode 100644 cumulus/polkadot-parachain/src/cli.rs delete mode 100644 cumulus/polkadot-parachain/src/command.rs create mode 100644 prdoc/pr_5288.prdoc diff --git a/Cargo.lock b/Cargo.lock index 4148b660cd3c..a67f9fdee31b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13844,19 +13844,48 @@ dependencies = [ name = "polkadot-parachain-bin" version = "4.0.0" dependencies = [ - "assert_cmd", "asset-hub-rococo-runtime", "asset-hub-westend-runtime", - "async-trait", "bridge-hub-rococo-runtime", "bridge-hub-westend-runtime", - "clap 4.5.11", "collectives-westend-runtime", "color-eyre", - "color-print", "contracts-rococo-runtime", "coretime-rococo-runtime", "coretime-westend-runtime", + "cumulus-primitives-core", + "glutton-westend-runtime", + "hex-literal", + "log", + "parachains-common", + "penpal-runtime", + "people-rococo-runtime", + "people-westend-runtime", + "polkadot-parachain-lib", + "polkadot-service", + "rococo-parachain-runtime", + "sc-chain-spec", + "sc-cli", + "sc-service", + "seedling-runtime", + "serde", + "serde_json", + "shell-runtime", + "sp-core", + "sp-runtime", + "staging-xcm", + "substrate-build-script-utils", + "testnet-parachains-constants", +] + +[[package]] +name = "polkadot-parachain-lib" +version = "0.1.0" +dependencies = [ + "assert_cmd", + "async-trait", + "clap 4.5.11", + "color-print", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", @@ -13875,8 +13904,6 @@ dependencies = [ "frame-system-rpc-runtime-api", "frame-try-runtime", "futures", - "glutton-westend-runtime", - "hex-literal", "jsonrpsee", "log", "nix 0.28.0", @@ -13885,13 +13912,8 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "parachains-common", "parity-scale-codec", - "penpal-runtime", - "people-rococo-runtime", - "people-westend-runtime", "polkadot-cli", "polkadot-primitives", - "polkadot-service", - "rococo-parachain-runtime", "sc-basic-authorship", "sc-chain-spec", "sc-cli", @@ -13899,42 +13921,30 @@ dependencies = [ "sc-consensus", "sc-executor", "sc-network", - "sc-network-sync", "sc-rpc", "sc-service", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", - "sc-transaction-pool-api", - "seedling-runtime", "serde", "serde_json", - "shell-runtime", "sp-api", "sp-block-builder", - "sp-blockchain", "sp-consensus-aura", "sp-core", "sp-genesis-builder", "sp-inherents", - "sp-io", "sp-keystore", - "sp-offchain", "sp-runtime", "sp-session", - "sp-std 14.0.0", "sp-timestamp", - "sp-tracing 16.0.0", "sp-transaction-pool", "sp-version", - "staging-xcm", - "substrate-build-script-utils", + "sp-weights", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-state-trie-migration-rpc", - "tempfile", - "testnet-parachains-constants", "tokio", "wait-timeout", ] @@ -14366,6 +14376,7 @@ dependencies = [ "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", + "polkadot-parachain-lib", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-rpc", diff --git a/Cargo.toml b/Cargo.toml index ebe9dc0dab3e..397163b3cce1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -136,6 +136,7 @@ members = [ "cumulus/parachains/runtimes/testing/penpal", "cumulus/parachains/runtimes/testing/rococo-parachain", "cumulus/polkadot-parachain", + "cumulus/polkadot-parachain/polkadot-parachain-lib", "cumulus/primitives/aura", "cumulus/primitives/core", "cumulus/primitives/parachain-inherent", @@ -535,6 +536,7 @@ members = [ ] default-members = [ + "cumulus/polkadot-parachain", "polkadot", "substrate/bin/node/cli", ] @@ -1041,6 +1043,7 @@ polkadot-node-subsystem-test-helpers = { path = "polkadot/node/subsystem-test-he polkadot-node-subsystem-types = { path = "polkadot/node/subsystem-types", default-features = false } polkadot-node-subsystem-util = { path = "polkadot/node/subsystem-util", default-features = false } polkadot-overseer = { path = "polkadot/node/overseer", default-features = false } +polkadot-parachain-lib = { path = "cumulus/polkadot-parachain/polkadot-parachain-lib", default-features = false } polkadot-parachain-primitives = { path = "polkadot/parachain", default-features = false } polkadot-primitives = { path = "polkadot/primitives", default-features = false } polkadot-primitives-test-helpers = { path = "polkadot/primitives/test-helpers" } diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index 5d6548447463..383e0f158bf4 100644 --- a/cumulus/polkadot-parachain/Cargo.toml +++ b/cumulus/polkadot-parachain/Cargo.toml @@ -2,9 +2,9 @@ name = "polkadot-parachain-bin" version = "4.0.0" authors.workspace = true -build = "build.rs" edition.workspace = true -description = "Runs a polkadot parachain node which could be a collator." +build = "build.rs" +description = "Runs a polkadot parachain node" license = "Apache-2.0" [lints] @@ -15,19 +15,14 @@ name = "polkadot-parachain" path = "src/main.rs" [dependencies] -async-trait = { workspace = true } -clap = { features = ["derive"], workspace = true } -codec = { workspace = true, default-features = true } color-eyre = { workspace = true } -color-print = { workspace = true } -futures = { workspace = true } hex-literal = { workspace = true, default-features = true } log = { workspace = true, default-features = true } serde = { features = ["derive"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } -docify = { workspace = true } # Local +polkadot-parachain-lib = { features = ["rococo-native", "westend-native"], workspace = true } rococo-parachain-runtime = { workspace = true } shell-runtime = { workspace = true } glutton-westend-runtime = { workspace = true } @@ -41,7 +36,6 @@ coretime-rococo-runtime = { workspace = true } coretime-westend-runtime = { workspace = true } bridge-hub-westend-runtime = { workspace = true, default-features = true } penpal-runtime = { workspace = true } -jsonrpsee = { features = ["server"], workspace = true } people-rococo-runtime = { workspace = true } people-westend-runtime = { workspace = true } parachains-common = { workspace = true, default-features = true } @@ -51,83 +45,32 @@ testnet-parachains-constants = { features = [ ], workspace = true } # Substrate -frame-benchmarking = { workspace = true, default-features = true } -frame-benchmarking-cli = { workspace = true, default-features = true } sp-runtime = { workspace = true } -sp-io = { workspace = true, default-features = true } sp-core = { workspace = true, default-features = true } -sp-session = { workspace = true, default-features = true } -frame-try-runtime = { optional = true, workspace = true, default-features = true } -sc-consensus = { workspace = true, default-features = true } -sp-tracing = { workspace = true, default-features = true } -frame-support = { workspace = true, default-features = true } sc-cli = { workspace = true, default-features = true } -sc-client-api = { workspace = true, default-features = true } -sc-executor = { workspace = true, default-features = true } sc-service = { workspace = true, default-features = true } -sc-telemetry = { workspace = true, default-features = true } -sc-transaction-pool = { workspace = true, default-features = true } -sp-transaction-pool = { workspace = true, default-features = true } -sc-network = { workspace = true, default-features = true } -sc-network-sync = { workspace = true, default-features = true } -sc-basic-authorship = { workspace = true, default-features = true } -sp-timestamp = { workspace = true, default-features = true } -sp-blockchain = { workspace = true, default-features = true } -sp-genesis-builder = { workspace = true } -sp-block-builder = { workspace = true, default-features = true } -sp-keystore = { workspace = true, default-features = true } sc-chain-spec = { workspace = true, default-features = true } -sc-rpc = { workspace = true, default-features = true } -sp-version = { workspace = true, default-features = true } -sc-tracing = { workspace = true, default-features = true } -sp-offchain = { workspace = true, default-features = true } -frame-system-rpc-runtime-api = { workspace = true, default-features = true } -pallet-transaction-payment = { workspace = true, default-features = true } -pallet-transaction-payment-rpc-runtime-api = { workspace = true, default-features = true } -sp-std = { workspace = true, default-features = true } -sp-inherents = { workspace = true, default-features = true } -sp-api = { workspace = true, default-features = true } -sp-consensus-aura = { workspace = true, default-features = true } -sc-sysinfo = { workspace = true, default-features = true } -prometheus-endpoint = { workspace = true, default-features = true } -sc-transaction-pool-api = { workspace = true, default-features = true } -substrate-frame-rpc-system = { workspace = true, default-features = true } -pallet-transaction-payment-rpc = { workspace = true, default-features = true } -substrate-state-trie-migration-rpc = { workspace = true, default-features = true } # Polkadot -# Use rococo-native as this is currently the default "local" relay chain -polkadot-cli = { features = ["rococo-native", "westend-native"], workspace = true, default-features = true } -polkadot-primitives = { workspace = true, default-features = true } polkadot-service = { workspace = true, default-features = true } xcm = { workspace = true, default-features = true } # Cumulus -cumulus-client-cli = { workspace = true, default-features = true } -cumulus-client-collator = { workspace = true, default-features = true } -cumulus-client-consensus-aura = { workspace = true, default-features = true } -cumulus-client-consensus-relay-chain = { workspace = true, default-features = true } -cumulus-client-consensus-common = { workspace = true, default-features = true } -cumulus-client-consensus-proposer = { workspace = true, default-features = true } -cumulus-client-parachain-inherent = { workspace = true, default-features = true } -cumulus-client-service = { workspace = true, default-features = true } -cumulus-primitives-aura = { workspace = true, default-features = true } cumulus-primitives-core = { workspace = true, default-features = true } -cumulus-relay-chain-interface = { workspace = true, default-features = true } [build-dependencies] substrate-build-script-utils = { workspace = true, default-features = true } -[dev-dependencies] -assert_cmd = { workspace = true } -nix = { features = ["signal"], workspace = true } -tempfile = { workspace = true } -tokio = { version = "1.32.0", features = ["macros", "parking_lot", "time"] } -wait-timeout = { workspace = true } - [features] default = [] runtime-benchmarks = [ + "cumulus-primitives-core/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-parachain-lib/runtime-benchmarks", + "polkadot-service/runtime-benchmarks", + "sc-service/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "asset-hub-rococo-runtime/runtime-benchmarks", "asset-hub-westend-runtime/runtime-benchmarks", "bridge-hub-rococo-runtime/runtime-benchmarks", @@ -136,23 +79,17 @@ runtime-benchmarks = [ "contracts-rococo-runtime/runtime-benchmarks", "coretime-rococo-runtime/runtime-benchmarks", "coretime-westend-runtime/runtime-benchmarks", - "cumulus-primitives-core/runtime-benchmarks", - "frame-benchmarking-cli/runtime-benchmarks", - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", "glutton-westend-runtime/runtime-benchmarks", - "parachains-common/runtime-benchmarks", "penpal-runtime/runtime-benchmarks", "people-rococo-runtime/runtime-benchmarks", "people-westend-runtime/runtime-benchmarks", - "polkadot-cli/runtime-benchmarks", - "polkadot-primitives/runtime-benchmarks", - "polkadot-service/runtime-benchmarks", "rococo-parachain-runtime/runtime-benchmarks", - "sc-service/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", ] try-runtime = [ + "polkadot-parachain-lib/try-runtime", + "polkadot-service/try-runtime", + "sp-runtime/try-runtime", + "asset-hub-rococo-runtime/try-runtime", "asset-hub-westend-runtime/try-runtime", "bridge-hub-rococo-runtime/try-runtime", @@ -161,17 +98,11 @@ try-runtime = [ "contracts-rococo-runtime/try-runtime", "coretime-rococo-runtime/try-runtime", "coretime-westend-runtime/try-runtime", - "frame-support/try-runtime", - "frame-try-runtime/try-runtime", "glutton-westend-runtime/try-runtime", - "pallet-transaction-payment/try-runtime", "penpal-runtime/try-runtime", "people-rococo-runtime/try-runtime", "people-westend-runtime/try-runtime", - "polkadot-cli/try-runtime", - "polkadot-service/try-runtime", "shell-runtime/try-runtime", - "sp-runtime/try-runtime", ] fast-runtime = [ "bridge-hub-rococo-runtime/fast-runtime", diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml b/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml new file mode 100644 index 000000000000..09bde034cf26 --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml @@ -0,0 +1,117 @@ +[package] +name = "polkadot-parachain-lib" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +description = "Helper library that can be used to build a parachain node" +license = "Apache-2.0" + +[lints] +workspace = true + +[lib] +path = "src/lib.rs" + +[dependencies] +async-trait = { workspace = true } +clap = { features = ["derive"], workspace = true } +codec = { workspace = true, default-features = true } +color-print = { workspace = true } +futures = { workspace = true } +log = { workspace = true, default-features = true } +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } +docify = { workspace = true } + +# Local +jsonrpsee = { features = ["server"], workspace = true } +parachains-common = { workspace = true, default-features = true } + +# Substrate +frame-benchmarking = { optional = true, workspace = true, default-features = true } +frame-benchmarking-cli = { workspace = true, default-features = true } +sp-runtime = { workspace = true } +sp-core = { workspace = true, default-features = true } +sp-session = { workspace = true, default-features = true } +frame-try-runtime = { optional = true, workspace = true, default-features = true } +sc-consensus = { workspace = true, default-features = true } +frame-support = { optional = true, workspace = true, default-features = true } +sc-cli = { workspace = true, default-features = true } +sc-client-api = { workspace = true, default-features = true } +sc-executor = { workspace = true, default-features = true } +sc-service = { workspace = true, default-features = true } +sc-telemetry = { workspace = true, default-features = true } +sc-transaction-pool = { workspace = true, default-features = true } +sp-transaction-pool = { workspace = true, default-features = true } +sc-network = { workspace = true, default-features = true } +sc-basic-authorship = { workspace = true, default-features = true } +sp-timestamp = { workspace = true, default-features = true } +sp-genesis-builder = { workspace = true } +sp-block-builder = { workspace = true, default-features = true } +sp-keystore = { workspace = true, default-features = true } +sc-chain-spec = { workspace = true, default-features = true } +sc-rpc = { workspace = true, default-features = true } +sp-version = { workspace = true, default-features = true } +sp-weights = { workspace = true, default-features = true } +sc-tracing = { workspace = true, default-features = true } +frame-system-rpc-runtime-api = { workspace = true, default-features = true } +pallet-transaction-payment = { workspace = true, default-features = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true, default-features = true } +sp-inherents = { workspace = true, default-features = true } +sp-api = { workspace = true, default-features = true } +sp-consensus-aura = { workspace = true, default-features = true } +sc-sysinfo = { workspace = true, default-features = true } +prometheus-endpoint = { workspace = true, default-features = true } +substrate-frame-rpc-system = { workspace = true, default-features = true } +pallet-transaction-payment-rpc = { workspace = true, default-features = true } +substrate-state-trie-migration-rpc = { workspace = true, default-features = true } + +# Polkadot +polkadot-cli = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, default-features = true } + +# Cumulus +cumulus-client-cli = { workspace = true, default-features = true } +cumulus-client-collator = { workspace = true, default-features = true } +cumulus-client-consensus-aura = { workspace = true, default-features = true } +cumulus-client-consensus-relay-chain = { workspace = true, default-features = true } +cumulus-client-consensus-common = { workspace = true, default-features = true } +cumulus-client-consensus-proposer = { workspace = true, default-features = true } +cumulus-client-parachain-inherent = { workspace = true, default-features = true } +cumulus-client-service = { workspace = true, default-features = true } +cumulus-primitives-aura = { workspace = true, default-features = true } +cumulus-primitives-core = { workspace = true, default-features = true } +cumulus-relay-chain-interface = { workspace = true, default-features = true } + +[dev-dependencies] +assert_cmd = { workspace = true } +nix = { features = ["signal"], workspace = true } +tokio = { version = "1.32.0", features = ["macros", "parking_lot", "time"] } +wait-timeout = { workspace = true } + +[features] +default = [] +rococo-native = [ + "polkadot-cli/rococo-native", +] +westend-native = [ + "polkadot-cli/westend-native", +] +runtime-benchmarks = [ + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking-cli/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-cli/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "sc-service/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-transaction-payment/try-runtime", + "polkadot-cli/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs new file mode 100644 index 000000000000..2aa2b10fbb67 --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs @@ -0,0 +1,391 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +use crate::{ + chain_spec::DiskChainSpecLoader, + common::{ + chain_spec::{Extensions, LoadSpec}, + NodeExtraArgs, + }, +}; +use clap::{Command, CommandFactory, FromArgMatches}; +use sc_chain_spec::ChainSpec; +use sc_cli::{ + CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams, + SharedParams, SubstrateCli, +}; +use sc_service::{config::PrometheusConfig, BasePath}; +use std::{fmt::Debug, marker::PhantomData, net::SocketAddr, path::PathBuf}; + +/// Trait that can be used to customize some of the customer-facing info related to the node binary +/// that is being built using this library. +/// +/// The related info is shown to the customer as part of logs or help messages. +/// It does not impact functionality. +pub trait CliConfig { + fn impl_version() -> String; + + fn description(executable_name: String) -> String { + format!( + "The command-line arguments provided first will be passed to the parachain node, \n\ + and the arguments provided after -- will be passed to the relay chain node. \n\ + \n\ + Example: \n\ + \n\ + {} [parachain-args] -- [relay-chain-args]", + executable_name + ) + } + + fn author() -> String; + + fn support_url() -> String; + + fn copyright_start_year() -> u16; +} + +/// Sub-commands supported by the collator. +#[derive(Debug, clap::Subcommand)] +pub enum Subcommand { + /// Key management CLI utilities + #[command(subcommand)] + Key(sc_cli::KeySubcommand), + + /// Build a chain specification. + BuildSpec(sc_cli::BuildSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// Remove the whole chain. + PurgeChain(cumulus_client_cli::PurgeChainCmd), + + /// Export the genesis state of the parachain. + #[command(alias = "export-genesis-state")] + ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand), + + /// Export the genesis wasm of the parachain. + ExportGenesisWasm(cumulus_client_cli::ExportGenesisWasmCommand), + + /// Sub-commands concerned with benchmarking. + /// The pallet benchmarking moved to the `pallet` sub-command. + #[command(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), +} + +#[derive(clap::Parser)] +#[command( + propagate_version = true, + args_conflicts_with_subcommands = true, + subcommand_negates_reqs = true +)] +pub struct Cli { + #[arg(skip)] + pub(crate) chain_spec_loader: Option>, + + #[command(subcommand)] + pub subcommand: Option, + + #[command(flatten)] + pub run: cumulus_client_cli::RunCmd, + + /// EXPERIMENTAL: Use slot-based collator which can handle elastic scaling. + /// + /// Use with care, this flag is unstable and subject to change. + #[arg(long)] + pub experimental_use_slot_based: bool, + + /// Disable automatic hardware benchmarks. + /// + /// By default these benchmarks are automatically ran at startup and measure + /// the CPU speed, the memory bandwidth and the disk speed. + /// + /// The results are then printed out in the logs, and also sent as part of + /// telemetry, if telemetry is enabled. + #[arg(long)] + pub no_hardware_benchmarks: bool, + + /// Export all `PoVs` build by this collator to the given folder. + /// + /// This is useful for debugging issues that are occurring while validating these `PoVs` on the + /// relay chain. + #[arg(long)] + pub export_pov_to_path: Option, + + /// Relay chain arguments + #[arg(raw = true)] + pub relay_chain_args: Vec, + + #[arg(skip)] + pub(crate) _phantom: PhantomData, +} + +impl Cli { + pub(crate) fn node_extra_args(&self) -> NodeExtraArgs { + NodeExtraArgs { + use_slot_based_consensus: self.experimental_use_slot_based, + export_pov: self.export_pov_to_path.clone(), + } + } +} + +impl SubstrateCli for Cli { + fn impl_name() -> String { + Self::executable_name() + } + + fn impl_version() -> String { + Config::impl_version() + } + + fn description() -> String { + Config::description(Self::executable_name()) + } + + fn author() -> String { + Config::author() + } + + fn support_url() -> String { + Config::support_url() + } + + fn copyright_start_year() -> i32 { + Config::copyright_start_year() as i32 + } + + fn load_spec(&self, id: &str) -> Result, String> { + match &self.chain_spec_loader { + Some(chain_spec_loader) => chain_spec_loader.load_spec(id), + None => DiskChainSpecLoader.load_spec(id), + } + } +} + +#[derive(Debug)] +pub struct RelayChainCli { + /// The actual relay chain cli object. + pub base: polkadot_cli::RunCmd, + + /// Optional chain id that should be passed to the relay chain. + pub chain_id: Option, + + /// The base path that should be used by the relay chain. + pub base_path: Option, + + _phantom: PhantomData, +} + +impl RelayChainCli { + fn polkadot_cmd() -> Command { + let help_template = color_print::cformat!( + "The arguments that are passed to the relay chain node. \n\ + \n\ + RELAY_CHAIN_ARGS: \n\ + {{options}}", + ); + + polkadot_cli::RunCmd::command() + .no_binary_name(true) + .help_template(help_template) + } + + /// Parse the relay chain CLI parameters using the parachain `Configuration`. + pub fn new<'a>( + para_config: &sc_service::Configuration, + relay_chain_args: impl Iterator, + ) -> Self { + let polkadot_cmd = Self::polkadot_cmd(); + let matches = polkadot_cmd.get_matches_from(relay_chain_args); + let base = FromArgMatches::from_arg_matches(&matches).unwrap_or_else(|e| e.exit()); + + let extension = Extensions::try_get(&*para_config.chain_spec); + let chain_id = extension.map(|e| e.relay_chain.clone()); + + let base_path = para_config.base_path.path().join("polkadot"); + Self { base, chain_id, base_path: Some(base_path), _phantom: Default::default() } + } +} + +impl SubstrateCli for RelayChainCli { + fn impl_name() -> String { + Cli::::impl_name() + } + + fn impl_version() -> String { + Cli::::impl_version() + } + + fn description() -> String { + Cli::::description() + } + + fn author() -> String { + Cli::::author() + } + + fn support_url() -> String { + Cli::::support_url() + } + + fn copyright_start_year() -> i32 { + Cli::::copyright_start_year() + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + polkadot_cli::Cli::from_iter([Self::executable_name()].iter()).load_spec(id) + } +} + +impl DefaultConfigurationValues for RelayChainCli { + fn p2p_listen_port() -> u16 { + 30334 + } + + fn rpc_listen_port() -> u16 { + 9945 + } + + fn prometheus_listen_port() -> u16 { + 9616 + } +} + +impl CliConfiguration for RelayChainCli { + fn shared_params(&self) -> &SharedParams { + self.base.base.shared_params() + } + + fn import_params(&self) -> Option<&ImportParams> { + self.base.base.import_params() + } + + fn network_params(&self) -> Option<&NetworkParams> { + self.base.base.network_params() + } + + fn keystore_params(&self) -> Option<&KeystoreParams> { + self.base.base.keystore_params() + } + + fn base_path(&self) -> sc_cli::Result> { + Ok(self + .shared_params() + .base_path()? + .or_else(|| self.base_path.clone().map(Into::into))) + } + + fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result> { + self.base.base.rpc_addr(default_listen_port) + } + + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box, + ) -> sc_cli::Result> { + self.base.base.prometheus_config(default_listen_port, chain_spec) + } + + fn init( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + _config: &sc_service::Configuration, + ) -> sc_cli::Result<()> + where + F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + { + unreachable!("PolkadotCli is never initialized; qed"); + } + + fn chain_id(&self, is_dev: bool) -> sc_cli::Result { + let chain_id = self.base.base.chain_id(is_dev)?; + + Ok(if chain_id.is_empty() { self.chain_id.clone().unwrap_or_default() } else { chain_id }) + } + + fn role(&self, is_dev: bool) -> sc_cli::Result { + self.base.base.role(is_dev) + } + + fn transaction_pool( + &self, + is_dev: bool, + ) -> sc_cli::Result { + self.base.base.transaction_pool(is_dev) + } + + fn trie_cache_maximum_size(&self) -> sc_cli::Result> { + self.base.base.trie_cache_maximum_size() + } + + fn rpc_methods(&self) -> sc_cli::Result { + self.base.base.rpc_methods() + } + + fn rpc_max_connections(&self) -> sc_cli::Result { + self.base.base.rpc_max_connections() + } + + fn rpc_cors(&self, is_dev: bool) -> sc_cli::Result>> { + self.base.base.rpc_cors(is_dev) + } + + fn default_heap_pages(&self) -> sc_cli::Result> { + self.base.base.default_heap_pages() + } + + fn force_authoring(&self) -> sc_cli::Result { + self.base.base.force_authoring() + } + + fn disable_grandpa(&self) -> sc_cli::Result { + self.base.base.disable_grandpa() + } + + fn max_runtime_instances(&self) -> sc_cli::Result> { + self.base.base.max_runtime_instances() + } + + fn announce_block(&self) -> sc_cli::Result { + self.base.base.announce_block() + } + + fn telemetry_endpoints( + &self, + chain_spec: &Box, + ) -> sc_cli::Result> { + self.base.base.telemetry_endpoints(chain_spec) + } + + fn node_name(&self) -> sc_cli::Result { + self.base.base.node_name() + } +} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs new file mode 100644 index 000000000000..7f915b729e0a --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs @@ -0,0 +1,293 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +#[cfg(feature = "runtime-benchmarks")] +use crate::service::Block; +use crate::{ + cli::{Cli, RelayChainCli, Subcommand}, + common::{ + chain_spec::{Extensions, LoadSpec}, + runtime::{ + AuraConsensusId, Consensus, Runtime, RuntimeResolver as RuntimeResolverT, + RuntimeResolver, + }, + NodeExtraArgs, + }, + fake_runtime_api::{ + asset_hub_polkadot_aura::RuntimeApi as AssetHubPolkadotRuntimeApi, + aura::RuntimeApi as AuraRuntimeApi, + }, + service::{new_aura_node_spec, DynNodeSpec, ShellNode}, +}; +#[cfg(feature = "runtime-benchmarks")] +use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; +use cumulus_primitives_core::ParaId; +use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; +use log::info; +use parachains_common::{AssetHubPolkadotAuraId, AuraId}; +use sc_cli::{Result, SubstrateCli}; +use sp_runtime::traits::AccountIdConversion; +#[cfg(feature = "runtime-benchmarks")] +use sp_runtime::traits::HashingFor; + +/// Structure that can be used in order to provide customizers for different functionalities of the +/// node binary that is being built using this library. +pub struct RunConfig { + pub chain_spec_loader: Box, + pub runtime_resolver: Box, +} + +fn new_node_spec( + config: &sc_service::Configuration, + runtime_resolver: &Box, + extra_args: &NodeExtraArgs, +) -> std::result::Result, sc_cli::Error> { + let runtime = runtime_resolver.runtime(config.chain_spec.as_ref())?; + + Ok(match runtime { + Runtime::Shell => Box::new(ShellNode), + Runtime::Omni(consensus) => match consensus { + Consensus::Aura(AuraConsensusId::Sr25519) => + new_aura_node_spec::(extra_args), + Consensus::Aura(AuraConsensusId::Ed25519) => + new_aura_node_spec::(extra_args), + }, + }) +} + +/// Parse command line arguments into service configuration. +pub fn run(cmd_config: RunConfig) -> Result<()> { + let mut cli = Cli::::from_args(); + cli.chain_spec_loader = Some(cmd_config.chain_spec_loader); + + match &cli.subcommand { + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + }, + Some(Subcommand::CheckBlock(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let node = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + node.prepare_check_block_cmd(config, cmd) + }) + }, + Some(Subcommand::ExportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let node = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + node.prepare_export_blocks_cmd(config, cmd) + }) + }, + Some(Subcommand::ExportState(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let node = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + node.prepare_export_state_cmd(config, cmd) + }) + }, + Some(Subcommand::ImportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let node = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + node.prepare_import_blocks_cmd(config, cmd) + }) + }, + Some(Subcommand::Revert(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let node = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + node.prepare_revert_cmd(config, cmd) + }) + }, + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + let polkadot_cli = + RelayChainCli::::new(runner.config(), cli.relay_chain_args.iter()); + + runner.sync_run(|config| { + let polkadot_config = SubstrateCli::create_configuration( + &polkadot_cli, + &polkadot_cli, + config.tokio_handle.clone(), + ) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + cmd.run(config, polkadot_config) + }) + }, + Some(Subcommand::ExportGenesisHead(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| { + let node = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + node.run_export_genesis_head_cmd(config, cmd) + }) + }, + Some(Subcommand::ExportGenesisWasm(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|_config| { + let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?; + cmd.run(&*spec) + }) + }, + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + + // Switch on the concrete benchmark sub-command- + match cmd { + #[cfg(feature = "runtime-benchmarks")] + BenchmarkCmd::Pallet(cmd) => runner.sync_run(|config| { + cmd.run_with_spec::, ReclaimHostFunctions>(Some( + config.chain_spec, + )) + }), + BenchmarkCmd::Block(cmd) => runner.sync_run(|config| { + let node = new_node_spec( + &config, + &cmd_config.runtime_resolver, + &cli.node_extra_args(), + )?; + node.run_benchmark_block_cmd(config, cmd) + }), + #[cfg(feature = "runtime-benchmarks")] + BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| { + let node = new_node_spec( + &config, + &cmd_config.runtime_resolver, + &cli.node_extra_args(), + )?; + node.run_benchmark_storage_cmd(config, cmd) + }), + BenchmarkCmd::Machine(cmd) => + runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())), + #[allow(unreachable_patterns)] + _ => Err("Benchmarking sub-command unsupported or compilation feature missing. \ + Make sure to compile with --features=runtime-benchmarks \ + to enable all supported benchmarks." + .into()), + } + }, + Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?), + None => { + let runner = cli.create_runner(&cli.run.normalize())?; + let polkadot_cli = + RelayChainCli::::new(runner.config(), cli.relay_chain_args.iter()); + let collator_options = cli.run.collator_options(); + + runner.run_node_until_exit(|config| async move { + // If Statemint (Statemine, Westmint, Rockmine) DB exists and we're using the + // asset-hub chain spec, then rename the base path to the new chain ID. In the case + // that both file paths exist, the node will exit, as the user must decide (by + // deleting one path) the information that they want to use as their DB. + let old_name = match config.chain_spec.id() { + "asset-hub-polkadot" => Some("statemint"), + "asset-hub-kusama" => Some("statemine"), + "asset-hub-westend" => Some("westmint"), + "asset-hub-rococo" => Some("rockmine"), + _ => None, + }; + + if let Some(old_name) = old_name { + let new_path = config.base_path.config_dir(config.chain_spec.id()); + let old_path = config.base_path.config_dir(old_name); + + if old_path.exists() && new_path.exists() { + return Err(format!( + "Found legacy {} path {} and new Asset Hub path {}. \ + Delete one path such that only one exists.", + old_name, + old_path.display(), + new_path.display() + ) + .into()); + } + + if old_path.exists() { + std::fs::rename(old_path.clone(), new_path.clone())?; + info!( + "{} was renamed to Asset Hub. The filepath with associated data on disk \ + has been renamed from {} to {}.", + old_name, + old_path.display(), + new_path.display() + ); + } + } + + let hwbench = (!cli.no_hardware_benchmarks) + .then_some(config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(database_path); + sc_sysinfo::gather_hwbench(Some(database_path)) + })) + .flatten(); + + let para_id = Extensions::try_get(&*config.chain_spec) + .map(|e| e.para_id) + .ok_or("Could not find parachain extension in chain-spec.")?; + + let id = ParaId::from(para_id); + + let parachain_account = + AccountIdConversion::::into_account_truncating( + &id, + ); + + let tokio_handle = config.tokio_handle.clone(); + let polkadot_config = + SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + info!("🪪 Parachain id: {:?}", id); + info!("🧾 Parachain Account: {}", parachain_account); + info!("✍️ Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); + + start_node( + config, + &cmd_config.runtime_resolver, + polkadot_config, + collator_options, + id, + cli.node_extra_args(), + hwbench, + ) + .await + }) + }, + } +} + +#[sc_tracing::logging::prefix_logs_with("Parachain")] +async fn start_node( + config: sc_service::Configuration, + runtime_resolver: &Box, + polkadot_config: sc_service::Configuration, + collator_options: cumulus_client_cli::CollatorOptions, + id: ParaId, + extra_args: NodeExtraArgs, + hwbench: Option, +) -> Result { + let node_spec = new_node_spec(&config, runtime_resolver, &extra_args)?; + node_spec + .start_node(config, polkadot_config, collator_options, id, hwbench, extra_args) + .await + .map_err(Into::into) +} diff --git a/cumulus/polkadot-parachain/src/common/aura.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/aura.rs similarity index 95% rename from cumulus/polkadot-parachain/src/common/aura.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/aura.rs index 9f72d847926f..9e8837de7f87 100644 --- a/cumulus/polkadot-parachain/src/common/aura.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/aura.rs @@ -18,9 +18,11 @@ use codec::Codec; use cumulus_primitives_aura::AuraUnincludedSegmentApi; -use cumulus_primitives_core::BlockT; use sp_consensus_aura::AuraApi; -use sp_runtime::app_crypto::{AppCrypto, AppPair, AppSignature, Pair}; +use sp_runtime::{ + app_crypto::{AppCrypto, AppPair, AppSignature, Pair}, + traits::Block as BlockT, +}; /// Convenience trait for defining the basic bounds of an `AuraId`. pub trait AuraIdT: AppCrypto + Codec + Send { diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/chain_spec.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/chain_spec.rs new file mode 100644 index 000000000000..974d6ef2b611 --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/chain_spec.rs @@ -0,0 +1,77 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Chain spec primitives. + +pub use sc_chain_spec::ChainSpec; +use sc_chain_spec::ChainSpecExtension; +use serde::{Deserialize, Serialize}; +use std::fmt::Debug; + +/// Helper trait used for loading/building a chain spec starting from the chain ID. +pub trait LoadSpec { + /// Load/Build a chain spec starting from the chain ID. + fn load_spec(&self, id: &str) -> Result, String>; +} + +/// Default implementation for `LoadSpec` that just reads a chain spec from the disk. +pub struct DiskChainSpecLoader; + +impl LoadSpec for DiskChainSpecLoader { + fn load_spec(&self, path: &str) -> Result, String> { + Ok(Box::new(GenericChainSpec::from_json_file(path.into())?)) + } +} + +/// Generic extensions for Parachain ChainSpecs. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecExtension)] +pub struct Extensions { + /// The relay chain of the Parachain. + #[serde(alias = "relayChain", alias = "RelayChain")] + pub relay_chain: String, + /// The id of the Parachain. + #[serde(alias = "paraId", alias = "ParaId")] + pub para_id: u32, +} + +impl Extensions { + /// Try to get the extension from the given `ChainSpec`. + pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { + sc_chain_spec::get_extension(chain_spec.extensions()) + } +} + +/// Generic chain spec for all polkadot-parachain runtimes +pub type GenericChainSpec = sc_service::GenericChainSpec; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_decode_extension_camel_and_snake_case() { + let camel_case = r#"{"relayChain":"relay","paraId":1}"#; + let snake_case = r#"{"relay_chain":"relay","para_id":1}"#; + let pascal_case = r#"{"RelayChain":"relay","ParaId":1}"#; + + let camel_case_extension: Extensions = serde_json::from_str(camel_case).unwrap(); + let snake_case_extension: Extensions = serde_json::from_str(snake_case).unwrap(); + let pascal_case_extension: Extensions = serde_json::from_str(pascal_case).unwrap(); + + assert_eq!(camel_case_extension, snake_case_extension); + assert_eq!(snake_case_extension, pascal_case_extension); + } +} diff --git a/cumulus/polkadot-parachain/src/common/mod.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs similarity index 97% rename from cumulus/polkadot-parachain/src/common/mod.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs index d7718931b872..89bc7511dac3 100644 --- a/cumulus/polkadot-parachain/src/common/mod.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs @@ -18,7 +18,9 @@ #![warn(missing_docs)] -pub mod aura; +pub(crate) mod aura; +pub mod chain_spec; +pub mod runtime; use cumulus_primitives_core::CollectCollationInfo; use sp_api::{ApiExt, CallApiAt, ConstructRuntimeApi, Metadata}; diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs new file mode 100644 index 000000000000..c64eda12d5ef --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs @@ -0,0 +1,61 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Runtime parameters. + +use sc_chain_spec::ChainSpec; + +/// The Aura ID used by the Aura consensus +#[derive(PartialEq)] +pub enum AuraConsensusId { + /// Ed25519 + Ed25519, + /// Sr25519 + Sr25519, +} + +/// The choice of consensus for the parachain omni-node. +#[derive(PartialEq)] +pub enum Consensus { + /// Aura consensus. + Aura(AuraConsensusId), +} + +/// Helper enum listing the supported Runtime types +#[derive(PartialEq)] +pub enum Runtime { + /// None of the system-chain runtimes, rather the node will act agnostic to the runtime ie. be + /// an omni-node, and simply run a node with the given consensus algorithm. + Omni(Consensus), + /// Shell + Shell, +} + +/// Helper trait used for extracting the Runtime variant from the chain spec ID. +pub trait RuntimeResolver { + /// Extract the Runtime variant from the chain spec ID. + fn runtime(&self, chain_spec: &dyn ChainSpec) -> sc_cli::Result; +} + +/// Default implementation for `RuntimeResolver` that just returns +/// `Runtime::Omni(Consensus::Aura(AuraConsensusId::Sr25519))`. +pub struct DefaultRuntimeResolver; + +impl RuntimeResolver for DefaultRuntimeResolver { + fn runtime(&self, _chain_spec: &dyn ChainSpec) -> sc_cli::Result { + Ok(Runtime::Omni(Consensus::Aura(AuraConsensusId::Sr25519))) + } +} diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/asset_hub_polkadot_aura.rs similarity index 98% rename from cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/asset_hub_polkadot_aura.rs index 7d54e9b4be04..f2b8b4d562b9 100644 --- a/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/asset_hub_polkadot_aura.rs @@ -17,15 +17,14 @@ //! These are used to provide a type that implements these runtime APIs without requiring to import //! the native runtimes. -use frame_support::weights::Weight; -use parachains_common::{AccountId, AssetHubPolkadotAuraId, Balance, Nonce}; -use polkadot_primitives::Block; +use parachains_common::{AccountId, AssetHubPolkadotAuraId, Balance, Block, Nonce}; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ traits::Block as BlockT, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; +use sp_weights::Weight; pub struct Runtime; diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/aura.rs similarity index 97% rename from cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/aura.rs index ca5fc8bdf119..eb6d3fafa3d6 100644 --- a/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/aura.rs @@ -17,15 +17,14 @@ //! These are used to provide a type that implements these runtime APIs without requiring to import //! the native runtimes. -use frame_support::weights::Weight; -use parachains_common::{AccountId, AuraId, Balance, Nonce}; -use polkadot_primitives::Block; +use parachains_common::{AccountId, AuraId, Balance, Block, Nonce}; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ traits::Block as BlockT, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; +use sp_weights::Weight; pub struct Runtime; diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/mod.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/mod.rs similarity index 100% rename from cumulus/polkadot-parachain/src/fake_runtime_api/mod.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/mod.rs diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs new file mode 100644 index 000000000000..fc164a9d8907 --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs @@ -0,0 +1,51 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Helper library that can be used to run a parachain node. +//! +//! ## Overview +//! +//! This library can be used to run a parachain node while also customizing the chain specs +//! that are supported by default by the `--chain-spec` argument of the node's `CLI` +//! and the parameters of the runtime that is associated with each of these chain specs. +//! +//! ## API +//! +//! The library exposes the possibility to provide a [`RunConfig`]. Through this structure +//! 2 optional configurations can be provided: +//! - a chain spec loader (an implementation of [`chain_spec::LoadSpec`]): this can be used for +//! providing the chain specs that are supported by default by the `--chain-spec` argument of the +//! node's `CLI` and the actual chain config associated with each one. +//! - a runtime resolver (an implementation of [`runtime::RuntimeResolver`]): this can be used for +//! providing the parameters of the runtime that is associated with each of the chain specs +//! +//! Apart from this, a [`CliConfig`] can also be provided, that can be used to customize some +//! user-facing binary author, support url, etc. +//! +//! ## Examples +//! +//! For an example, see the `polkadot-parachain-bin` crate. + +mod cli; +mod command; +mod common; +mod fake_runtime_api; +mod rpc; +mod service; + +pub use cli::CliConfig; +pub use command::{run, RunConfig}; +pub use common::{chain_spec, runtime}; diff --git a/cumulus/polkadot-parachain/src/rpc.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/rpc.rs similarity index 100% rename from cumulus/polkadot-parachain/src/rpc.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/rpc.rs diff --git a/cumulus/polkadot-parachain/src/service.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs similarity index 100% rename from cumulus/polkadot-parachain/src/service.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs diff --git a/cumulus/polkadot-parachain/tests/benchmark_storage_works.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/benchmark_storage_works.rs similarity index 100% rename from cumulus/polkadot-parachain/tests/benchmark_storage_works.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/benchmark_storage_works.rs diff --git a/cumulus/polkadot-parachain/tests/common.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/common.rs similarity index 100% rename from cumulus/polkadot-parachain/tests/common.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/common.rs diff --git a/cumulus/polkadot-parachain/tests/polkadot_argument_parsing.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_argument_parsing.rs similarity index 100% rename from cumulus/polkadot-parachain/tests/polkadot_argument_parsing.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_argument_parsing.rs diff --git a/cumulus/polkadot-parachain/tests/polkadot_mdns_issue.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_mdns_issue.rs similarity index 100% rename from cumulus/polkadot-parachain/tests/polkadot_mdns_issue.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_mdns_issue.rs diff --git a/cumulus/polkadot-parachain/tests/purge_chain_works.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/purge_chain_works.rs similarity index 100% rename from cumulus/polkadot-parachain/tests/purge_chain_works.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/purge_chain_works.rs diff --git a/cumulus/polkadot-parachain/tests/running_the_node_and_interrupt.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/running_the_node_and_interrupt.rs similarity index 100% rename from cumulus/polkadot-parachain/tests/running_the_node_and_interrupt.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/running_the_node_and_interrupt.rs diff --git a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs index af5bccdc416f..f6bf6375a353 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs @@ -14,13 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, - SAFE_XCM_VERSION, -}; +use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; use cumulus_primitives_core::ParaId; use hex_literal::hex; use parachains_common::{AccountId, AuraId, Balance as AssetHubBalance}; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::{crypto::UncheckedInto, sr25519}; diff --git a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs index 3b7376d045b8..754bd851b40a 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, GenericChainSpec}; +use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed}; use cumulus_primitives_core::ParaId; use parachains_common::Balance as BridgeHubBalance; +use polkadot_parachain_lib::chain_spec::GenericChainSpec; use sc_chain_spec::ChainSpec; use sp_core::sr25519; use std::str::FromStr; @@ -129,8 +130,9 @@ fn ensure_id(id: &str) -> Result<&str, String> { /// Sub-module for Rococo setup pub mod rococo { use super::{get_account_id_from_seed, get_collator_keys_from_seed, sr25519, ParaId}; - use crate::chain_spec::{Extensions, GenericChainSpec, SAFE_XCM_VERSION}; + use crate::chain_spec::SAFE_XCM_VERSION; use parachains_common::{AccountId, AuraId}; + use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use super::BridgeHubBalance; @@ -254,8 +256,9 @@ pub mod kusama { /// Sub-module for Westend setup. pub mod westend { use super::{get_account_id_from_seed, get_collator_keys_from_seed, sr25519, ParaId}; - use crate::chain_spec::{Extensions, GenericChainSpec, SAFE_XCM_VERSION}; + use crate::chain_spec::SAFE_XCM_VERSION; use parachains_common::{AccountId, AuraId}; + use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use super::BridgeHubBalance; diff --git a/cumulus/polkadot-parachain/src/chain_spec/collectives.rs b/cumulus/polkadot-parachain/src/chain_spec/collectives.rs index c0a9f195d89b..865a2a917086 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/collectives.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/collectives.rs @@ -14,12 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, - SAFE_XCM_VERSION, -}; +use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; use cumulus_primitives_core::ParaId; use parachains_common::{AccountId, AuraId, Balance as CollectivesBalance}; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/contracts.rs b/cumulus/polkadot-parachain/src/chain_spec/contracts.rs index 4e89b81d1be4..eb10a43ffbea 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/contracts.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/contracts.rs @@ -14,13 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, - SAFE_XCM_VERSION, -}; +use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; use cumulus_primitives_core::ParaId; use hex_literal::hex; use parachains_common::{AccountId, AuraId}; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::{crypto::UncheckedInto, sr25519}; @@ -132,71 +130,71 @@ pub fn contracts_rococo_config() -> GenericChainSpec { properties.insert("tokenDecimals".into(), 12.into()); GenericChainSpec::builder( - contracts_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), - Extensions { relay_chain: "rococo".into(), para_id: CONTRACTS_PARACHAIN_ID } - ) - .with_name("Contracts on Rococo") - .with_id("contracts-rococo") - .with_chain_type(ChainType::Live) - .with_genesis_config_patch(contracts_rococo_genesis( - vec![ - // 5GKFbTTgrVS4Vz1UWWHPqMZQNFWZtqo7H2KpCDyYhEL3aS26 - ( - hex!["bc09354c12c054c8f6b3da208485eacec4ac648bad348895273b37bab5a0937c"] - .into(), - hex!["bc09354c12c054c8f6b3da208485eacec4ac648bad348895273b37bab5a0937c"] - .unchecked_into(), - ), - // 5EPRJHm2GpABVWcwnAujcrhnrjFZyDGd5TwKFzkBoGgdRyv2 - ( - hex!["66be63b7bcbfb91040e5248e2d1ceb822cf219c57848c5924ffa3a1f8e67ba72"] - .into(), - hex!["66be63b7bcbfb91040e5248e2d1ceb822cf219c57848c5924ffa3a1f8e67ba72"] - .unchecked_into(), - ), - // 5GH62vrJrVZxLREcHzm2PR5uTLAT5RQMJitoztCGyaP4o3uM - ( - hex!["ba62886472a0a9f66b5e39f1469ce1c5b3d8cad6be39078daf16f111e89d1e44"] - .into(), - hex!["ba62886472a0a9f66b5e39f1469ce1c5b3d8cad6be39078daf16f111e89d1e44"] - .unchecked_into(), - ), - // 5FHfoJDLdjRYX5KXLRqMDYBbWrwHLMtti21uK4QByUoUAbJF - ( - hex!["8e97f65cda001976311df9bed39e8d0c956089093e94a75ef76fe9347a0eda7b"] - .into(), - hex!["8e97f65cda001976311df9bed39e8d0c956089093e94a75ef76fe9347a0eda7b"] - .unchecked_into(), - ), - ], - // Warning: The configuration for a production chain should not contain - // any endowed accounts here, otherwise it'll be minting extra native tokens - // from the relay chain on the parachain. - vec![ - // NOTE: Remove endowed accounts if deployed on other relay chains. - // Endowed accounts - hex!["baa78c7154c7f82d6d377177e20bcab65d327eca0086513f9964f5a0f6bdad56"].into(), - // AccountId of an account which `ink-waterfall` uses for automated testing - hex!["0e47e2344d523c3cc5c34394b0d58b9a4200e813a038e6c5a6163cc07d70b069"].into(), - ], - CONTRACTS_PARACHAIN_ID.into(), - )) - .with_boot_nodes(vec![ - "/dns/contracts-collator-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWKg3Rpxcr9oJ8n6khoxpGKWztCZydtUZk2cojHqnfLrpj" - .parse() - .expect("MultiaddrWithPeerId"), - "/dns/contracts-collator-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWPEXYrz8tHU3nDtPoPw4V7ou5dzMEWSTuUj7vaWiYVAVh" - .parse() - .expect("MultiaddrWithPeerId"), - "/dns/contracts-collator-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWEVU8AFNary4nP4qEnEcwJaRuy59Wefekzdu9pKbnVEhk" - .parse() - .expect("MultiaddrWithPeerId"), - "/dns/contracts-collator-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWP6pV3ZmcXzGDjv8ZMgA6nZxfAKDxSz4VNiLx6vVCQgJX" - .parse() - .expect("MultiaddrWithPeerId"), - ]) - .with_properties(properties) - .build() + contracts_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), + Extensions { relay_chain: "rococo".into(), para_id: CONTRACTS_PARACHAIN_ID }, + ) + .with_name("Contracts on Rococo") + .with_id("contracts-rococo") + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(contracts_rococo_genesis( + vec![ + // 5GKFbTTgrVS4Vz1UWWHPqMZQNFWZtqo7H2KpCDyYhEL3aS26 + ( + hex!["bc09354c12c054c8f6b3da208485eacec4ac648bad348895273b37bab5a0937c"] + .into(), + hex!["bc09354c12c054c8f6b3da208485eacec4ac648bad348895273b37bab5a0937c"] + .unchecked_into(), + ), + // 5EPRJHm2GpABVWcwnAujcrhnrjFZyDGd5TwKFzkBoGgdRyv2 + ( + hex!["66be63b7bcbfb91040e5248e2d1ceb822cf219c57848c5924ffa3a1f8e67ba72"] + .into(), + hex!["66be63b7bcbfb91040e5248e2d1ceb822cf219c57848c5924ffa3a1f8e67ba72"] + .unchecked_into(), + ), + // 5GH62vrJrVZxLREcHzm2PR5uTLAT5RQMJitoztCGyaP4o3uM + ( + hex!["ba62886472a0a9f66b5e39f1469ce1c5b3d8cad6be39078daf16f111e89d1e44"] + .into(), + hex!["ba62886472a0a9f66b5e39f1469ce1c5b3d8cad6be39078daf16f111e89d1e44"] + .unchecked_into(), + ), + // 5FHfoJDLdjRYX5KXLRqMDYBbWrwHLMtti21uK4QByUoUAbJF + ( + hex!["8e97f65cda001976311df9bed39e8d0c956089093e94a75ef76fe9347a0eda7b"] + .into(), + hex!["8e97f65cda001976311df9bed39e8d0c956089093e94a75ef76fe9347a0eda7b"] + .unchecked_into(), + ), + ], + // Warning: The configuration for a production chain should not contain + // any endowed accounts here, otherwise it'll be minting extra native tokens + // from the relay chain on the parachain. + vec![ + // NOTE: Remove endowed accounts if deployed on other relay chains. + // Endowed accounts + hex!["baa78c7154c7f82d6d377177e20bcab65d327eca0086513f9964f5a0f6bdad56"].into(), + // AccountId of an account which `ink-waterfall` uses for automated testing + hex!["0e47e2344d523c3cc5c34394b0d58b9a4200e813a038e6c5a6163cc07d70b069"].into(), + ], + CONTRACTS_PARACHAIN_ID.into(), + )) + .with_boot_nodes(vec![ + "/dns/contracts-collator-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWKg3Rpxcr9oJ8n6khoxpGKWztCZydtUZk2cojHqnfLrpj" + .parse() + .expect("MultiaddrWithPeerId"), + "/dns/contracts-collator-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWPEXYrz8tHU3nDtPoPw4V7ou5dzMEWSTuUj7vaWiYVAVh" + .parse() + .expect("MultiaddrWithPeerId"), + "/dns/contracts-collator-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWEVU8AFNary4nP4qEnEcwJaRuy59Wefekzdu9pKbnVEhk" + .parse() + .expect("MultiaddrWithPeerId"), + "/dns/contracts-collator-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWP6pV3ZmcXzGDjv8ZMgA6nZxfAKDxSz4VNiLx6vVCQgJX" + .parse() + .expect("MultiaddrWithPeerId"), + ]) + .with_properties(properties) + .build() } fn contracts_rococo_genesis( diff --git a/cumulus/polkadot-parachain/src/chain_spec/coretime.rs b/cumulus/polkadot-parachain/src/chain_spec/coretime.rs index fe60b09fd8b2..742f41f39e9d 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/coretime.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/coretime.rs @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::GenericChainSpec; use cumulus_primitives_core::ParaId; +use polkadot_parachain_lib::chain_spec::GenericChainSpec; use sc_chain_spec::{ChainSpec, ChainType}; use std::{borrow::Cow, str::FromStr}; @@ -144,11 +144,12 @@ pub fn chain_type_name(chain_type: &ChainType) -> Cow { /// Sub-module for Rococo setup. pub mod rococo { - use super::{chain_type_name, CoretimeRuntimeType, GenericChainSpec, ParaId}; + use super::{chain_type_name, CoretimeRuntimeType, ParaId}; use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, SAFE_XCM_VERSION, + get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, AuraId, Balance}; + use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use sp_core::sr25519; @@ -243,9 +244,10 @@ pub mod rococo { pub mod westend { use super::{chain_type_name, CoretimeRuntimeType, GenericChainSpec, ParaId}; use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, SAFE_XCM_VERSION, + get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, AuraId, Balance}; + use polkadot_parachain_lib::chain_spec::Extensions; use sp_core::sr25519; pub(crate) const CORETIME_WESTEND: &str = "coretime-westend"; diff --git a/cumulus/polkadot-parachain/src/chain_spec/glutton.rs b/cumulus/polkadot-parachain/src/chain_spec/glutton.rs index 77a4123b13ee..136411b93e8b 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/glutton.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/glutton.rs @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{get_account_id_from_seed, Extensions, GenericChainSpec}; +use crate::chain_spec::get_account_id_from_seed; use cumulus_primitives_core::ParaId; use parachains_common::AuraId; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/mod.rs b/cumulus/polkadot-parachain/src/chain_spec/mod.rs index 19047b073b05..de9c6a889ed0 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/mod.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/mod.rs @@ -14,9 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . +use cumulus_primitives_core::ParaId; use parachains_common::{AccountId, Signature}; -use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; -use serde::{Deserialize, Serialize}; +use polkadot_parachain_lib::{ + chain_spec::{GenericChainSpec, LoadSpec}, + runtime::{AuraConsensusId, Consensus, Runtime, RuntimeResolver as RuntimeResolverT}, +}; +use sc_chain_spec::ChainSpec; use sp_core::{Pair, Public}; use sp_runtime::traits::{IdentifyAccount, Verify}; @@ -35,27 +39,6 @@ pub mod shell; /// The default XCM version to set in genesis config. const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; -/// Generic extensions for Parachain ChainSpecs. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] -pub struct Extensions { - /// The relay chain of the Parachain. - #[serde(alias = "relayChain", alias = "RelayChain")] - pub relay_chain: String, - /// The id of the Parachain. - #[serde(alias = "paraId", alias = "ParaId")] - pub para_id: u32, -} - -impl Extensions { - /// Try to get the extension from the given `ChainSpec`. - pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { - sc_chain_spec::get_extension(chain_spec.extensions()) - } -} - -/// Generic chain spec for all polkadot-parachain runtimes -pub type GenericChainSpec = sc_service::GenericChainSpec; - /// Helper function to generate a crypto pair from seed pub fn get_from_seed(seed: &str) -> ::Public { TPublic::Pair::from_string(&format!("//{}", seed), None) @@ -80,21 +63,330 @@ pub fn get_collator_keys_from_seed(seed: &str) -> (seed) } +/// Extracts the normalized chain id and parachain id from the input chain id. +/// (H/T to Phala for the idea) +/// E.g. "penpal-kusama-2004" yields ("penpal-kusama", Some(2004)) +fn extract_parachain_id<'a>( + id: &'a str, + para_prefixes: &[&str], +) -> (&'a str, &'a str, Option) { + for para_prefix in para_prefixes { + if let Some(suffix) = id.strip_prefix(para_prefix) { + let para_id: u32 = suffix.parse().expect("Invalid parachain-id suffix"); + return (&id[..para_prefix.len() - 1], id, Some(para_id.into())); + } + } + + (id, id, None) +} + +#[derive(Debug)] +pub(crate) struct ChainSpecLoader; + +impl LoadSpec for ChainSpecLoader { + fn load_spec(&self, id: &str) -> Result, String> { + Ok(match id { + // - Default-like + "staging" => Box::new(rococo_parachain::staging_rococo_parachain_local_config()), + "tick" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/tick.json")[..], + )?), + "trick" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/trick.json")[..], + )?), + "track" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/track.json")[..], + )?), + + // -- Starters + "shell" => Box::new(shell::get_shell_chain_spec()), + "seedling" => Box::new(seedling::get_seedling_chain_spec()), + + // -- Asset Hub Polkadot + "asset-hub-polkadot" | "statemint" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/asset-hub-polkadot.json")[..], + )?), + + // -- Asset Hub Kusama + "asset-hub-kusama" | "statemine" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/asset-hub-kusama.json")[..], + )?), + + // -- Asset Hub Rococo + "asset-hub-rococo-dev" => Box::new(asset_hubs::asset_hub_rococo_development_config()), + "asset-hub-rococo-local" => Box::new(asset_hubs::asset_hub_rococo_local_config()), + // the chain spec as used for generating the upgrade genesis values + "asset-hub-rococo-genesis" => Box::new(asset_hubs::asset_hub_rococo_genesis_config()), + "asset-hub-rococo" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/asset-hub-rococo.json")[..], + )?), + + // -- Asset Hub Westend + "asset-hub-westend-dev" | "westmint-dev" => + Box::new(asset_hubs::asset_hub_westend_development_config()), + "asset-hub-westend-local" | "westmint-local" => + Box::new(asset_hubs::asset_hub_westend_local_config()), + // the chain spec as used for generating the upgrade genesis values + "asset-hub-westend-genesis" | "westmint-genesis" => + Box::new(asset_hubs::asset_hub_westend_config()), + // the shell-based chain spec as used for syncing + "asset-hub-westend" | "westmint" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/asset-hub-westend.json")[..], + )?), + + // -- Polkadot Collectives + "collectives-polkadot" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/collectives-polkadot.json")[..], + )?), + + // -- Westend Collectives + "collectives-westend-dev" => + Box::new(collectives::collectives_westend_development_config()), + "collectives-westend-local" => + Box::new(collectives::collectives_westend_local_config()), + "collectives-westend" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/collectives-westend.json")[..], + )?), + + // -- Contracts on Rococo + "contracts-rococo-dev" => Box::new(contracts::contracts_rococo_development_config()), + "contracts-rococo-local" => Box::new(contracts::contracts_rococo_local_config()), + "contracts-rococo-genesis" => Box::new(contracts::contracts_rococo_config()), + "contracts-rococo" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/contracts-rococo.json")[..], + )?), + + // -- BridgeHub + bridge_like_id + if bridge_like_id.starts_with(bridge_hubs::BridgeHubRuntimeType::ID_PREFIX) => + bridge_like_id + .parse::() + .expect("invalid value") + .load_config()?, + + // -- Coretime + coretime_like_id + if coretime_like_id.starts_with(coretime::CoretimeRuntimeType::ID_PREFIX) => + coretime_like_id + .parse::() + .expect("invalid value") + .load_config()?, + + // -- Penpal + id if id.starts_with("penpal-rococo") => { + let (_, _, para_id) = extract_parachain_id(&id, &["penpal-rococo-"]); + Box::new(penpal::get_penpal_chain_spec( + para_id.expect("Must specify parachain id"), + "rococo-local", + )) + }, + id if id.starts_with("penpal-westend") => { + let (_, _, para_id) = extract_parachain_id(&id, &["penpal-westend-"]); + Box::new(penpal::get_penpal_chain_spec( + para_id.expect("Must specify parachain id"), + "westend-local", + )) + }, + + // -- Glutton Westend + id if id.starts_with("glutton-westend-dev") => { + let (_, _, para_id) = extract_parachain_id(&id, &["glutton-westend-dev-"]); + Box::new(glutton::glutton_westend_development_config( + para_id.expect("Must specify parachain id"), + )) + }, + id if id.starts_with("glutton-westend-local") => { + let (_, _, para_id) = extract_parachain_id(&id, &["glutton-westend-local-"]); + Box::new(glutton::glutton_westend_local_config( + para_id.expect("Must specify parachain id"), + )) + }, + // the chain spec as used for generating the upgrade genesis values + id if id.starts_with("glutton-westend-genesis") => { + let (_, _, para_id) = extract_parachain_id(&id, &["glutton-westend-genesis-"]); + Box::new(glutton::glutton_westend_config( + para_id.expect("Must specify parachain id"), + )) + }, + + // -- People + people_like_id if people_like_id.starts_with(people::PeopleRuntimeType::ID_PREFIX) => + people_like_id + .parse::() + .expect("invalid value") + .load_config()?, + + // -- Fallback (generic chainspec) + "" => { + log::warn!("No ChainSpec.id specified, so using default one, based on rococo-parachain runtime"); + Box::new(rococo_parachain::rococo_parachain_local_config()) + }, + + // -- Loading a specific spec from disk + path => Box::new(GenericChainSpec::from_json_file(path.into())?), + }) + } +} + +/// Helper enum that is used for better distinction of different parachain/runtime configuration +/// (it is based/calculated on ChainSpec's ID attribute) +#[derive(Debug, PartialEq)] +enum LegacyRuntime { + Omni, + Shell, + Seedling, + AssetHubPolkadot, + AssetHub, + Penpal, + ContractsRococo, + Collectives, + Glutton, + BridgeHub(bridge_hubs::BridgeHubRuntimeType), + Coretime(coretime::CoretimeRuntimeType), + People(people::PeopleRuntimeType), +} + +impl LegacyRuntime { + fn from_id(id: &str) -> LegacyRuntime { + let id = id.replace('_', "-"); + + if id.starts_with("shell") { + LegacyRuntime::Shell + } else if id.starts_with("seedling") { + LegacyRuntime::Seedling + } else if id.starts_with("asset-hub-polkadot") | id.starts_with("statemint") { + LegacyRuntime::AssetHubPolkadot + } else if id.starts_with("asset-hub-kusama") | + id.starts_with("statemine") | + id.starts_with("asset-hub-rococo") | + id.starts_with("rockmine") | + id.starts_with("asset-hub-westend") | + id.starts_with("westmint") + { + LegacyRuntime::AssetHub + } else if id.starts_with("penpal") { + LegacyRuntime::Penpal + } else if id.starts_with("contracts-rococo") { + LegacyRuntime::ContractsRococo + } else if id.starts_with("collectives-polkadot") || id.starts_with("collectives-westend") { + LegacyRuntime::Collectives + } else if id.starts_with(bridge_hubs::BridgeHubRuntimeType::ID_PREFIX) { + LegacyRuntime::BridgeHub( + id.parse::().expect("Invalid value"), + ) + } else if id.starts_with(coretime::CoretimeRuntimeType::ID_PREFIX) { + LegacyRuntime::Coretime( + id.parse::().expect("Invalid value"), + ) + } else if id.starts_with("glutton") { + LegacyRuntime::Glutton + } else if id.starts_with(people::PeopleRuntimeType::ID_PREFIX) { + LegacyRuntime::People(id.parse::().expect("Invalid value")) + } else { + log::warn!( + "No specific runtime was recognized for ChainSpec's id: '{}', \ + so Runtime::Omni(Consensus::Aura) will be used", + id + ); + LegacyRuntime::Omni + } + } +} + +#[derive(Debug)] +pub(crate) struct RuntimeResolver; + +impl RuntimeResolverT for RuntimeResolver { + fn runtime(&self, chain_spec: &dyn ChainSpec) -> sc_cli::Result { + let legacy_runtime = LegacyRuntime::from_id(chain_spec.id()); + Ok(match legacy_runtime { + LegacyRuntime::AssetHubPolkadot => + Runtime::Omni(Consensus::Aura(AuraConsensusId::Ed25519)), + LegacyRuntime::AssetHub | + LegacyRuntime::BridgeHub(_) | + LegacyRuntime::Collectives | + LegacyRuntime::Coretime(_) | + LegacyRuntime::People(_) | + LegacyRuntime::ContractsRococo | + LegacyRuntime::Glutton | + LegacyRuntime::Penpal | + LegacyRuntime::Omni => Runtime::Omni(Consensus::Aura(AuraConsensusId::Sr25519)), + LegacyRuntime::Shell | LegacyRuntime::Seedling => Runtime::Shell, + }) + } +} + #[cfg(test)] mod tests { use super::*; + use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup, ChainType, Extension}; + use serde::{Deserialize, Serialize}; + use sp_core::sr25519; + + #[derive( + Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension, Default, + )] + #[serde(deny_unknown_fields)] + pub struct Extensions1 { + pub attribute1: String, + pub attribute2: u32, + } + + #[derive( + Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension, Default, + )] + #[serde(deny_unknown_fields)] + pub struct Extensions2 { + pub attribute_x: String, + pub attribute_y: String, + pub attribute_z: u32, + } + + pub type DummyChainSpec = sc_service::GenericChainSpec; + + pub fn create_default_with_extensions( + id: &str, + extension: E, + ) -> DummyChainSpec { + DummyChainSpec::builder( + rococo_parachain_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + extension, + ) + .with_name("Dummy local testnet") + .with_id(id) + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(crate::chain_spec::rococo_parachain::testnet_genesis( + get_account_id_from_seed::("Alice"), + vec![ + get_from_seed::("Alice"), + get_from_seed::("Bob"), + ], + vec![get_account_id_from_seed::("Alice")], + 1000.into(), + )) + .build() + } #[test] - fn can_decode_extension_camel_and_snake_case() { - let camel_case = r#"{"relayChain":"relay","paraId":1}"#; - let snake_case = r#"{"relay_chain":"relay","para_id":1}"#; - let pascal_case = r#"{"RelayChain":"relay","ParaId":1}"#; + fn test_legacy_runtime_for_different_chain_specs() { + let chain_spec = create_default_with_extensions("shell-1", Extensions1::default()); + assert_eq!(LegacyRuntime::Shell, LegacyRuntime::from_id(chain_spec.id())); + + let chain_spec = create_default_with_extensions("shell-2", Extensions2::default()); + assert_eq!(LegacyRuntime::Shell, LegacyRuntime::from_id(chain_spec.id())); + + let chain_spec = create_default_with_extensions("seedling", Extensions2::default()); + assert_eq!(LegacyRuntime::Seedling, LegacyRuntime::from_id(chain_spec.id())); + + let chain_spec = + create_default_with_extensions("penpal-rococo-1000", Extensions2::default()); + assert_eq!(LegacyRuntime::Penpal, LegacyRuntime::from_id(chain_spec.id())); - let camel_case_extension: Extensions = serde_json::from_str(camel_case).unwrap(); - let snake_case_extension: Extensions = serde_json::from_str(snake_case).unwrap(); - let pascal_case_extension: Extensions = serde_json::from_str(pascal_case).unwrap(); + let chain_spec = crate::chain_spec::contracts::contracts_rococo_local_config(); + assert_eq!(LegacyRuntime::ContractsRococo, LegacyRuntime::from_id(chain_spec.id())); - assert_eq!(camel_case_extension, snake_case_extension); - assert_eq!(snake_case_extension, pascal_case_extension); + let chain_spec = crate::chain_spec::rococo_parachain::rococo_parachain_local_config(); + assert_eq!(LegacyRuntime::Omni, LegacyRuntime::from_id(chain_spec.id())); } } diff --git a/cumulus/polkadot-parachain/src/chain_spec/penpal.rs b/cumulus/polkadot-parachain/src/chain_spec/penpal.rs index cb1cb632d638..5645bf06b67b 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/penpal.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/penpal.rs @@ -14,12 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, - SAFE_XCM_VERSION, -}; +use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; use cumulus_primitives_core::ParaId; use parachains_common::{AccountId, AuraId}; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/people.rs b/cumulus/polkadot-parachain/src/chain_spec/people.rs index 6100a15018cb..3c1150d95422 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/people.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/people.rs @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::GenericChainSpec; use cumulus_primitives_core::ParaId; use parachains_common::Balance as PeopleBalance; +use polkadot_parachain_lib::chain_spec::GenericChainSpec; use sc_chain_spec::ChainSpec; use std::str::FromStr; @@ -121,10 +121,10 @@ fn ensure_id(id: &str) -> Result<&str, String> { pub mod rococo { use super::{ParaId, PeopleBalance}; use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, - SAFE_XCM_VERSION, + get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, AuraId}; + use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use sp_core::sr25519; @@ -231,10 +231,10 @@ pub mod rococo { pub mod westend { use super::{ParaId, PeopleBalance}; use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, - SAFE_XCM_VERSION, + get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, AuraId}; + use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs b/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs index 0434e5f7be8f..9f4a162e67f8 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs @@ -16,10 +16,11 @@ //! ChainSpecs dedicated to Rococo parachain setups (for testing and example purposes) -use crate::chain_spec::{get_from_seed, Extensions, GenericChainSpec, SAFE_XCM_VERSION}; +use crate::chain_spec::{get_from_seed, SAFE_XCM_VERSION}; use cumulus_primitives_core::ParaId; use hex_literal::hex; use parachains_common::AccountId; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use polkadot_service::chain_spec::get_account_id_from_seed; use rococo_parachain_runtime::AuraId; use sc_chain_spec::ChainType; diff --git a/cumulus/polkadot-parachain/src/chain_spec/seedling.rs b/cumulus/polkadot-parachain/src/chain_spec/seedling.rs index 32d516220545..a104b58db5d2 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/seedling.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/seedling.rs @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{get_account_id_from_seed, Extensions, GenericChainSpec}; +use crate::chain_spec::get_account_id_from_seed; use cumulus_primitives_core::ParaId; use parachains_common::{AccountId, AuraId}; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/shell.rs b/cumulus/polkadot-parachain/src/chain_spec/shell.rs index e0a9875fb96f..0a7816ab3193 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/shell.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/shell.rs @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{Extensions, GenericChainSpec}; use cumulus_primitives_core::ParaId; use parachains_common::AuraId; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use super::get_collator_keys_from_seed; diff --git a/cumulus/polkadot-parachain/src/cli.rs b/cumulus/polkadot-parachain/src/cli.rs deleted file mode 100644 index a5fe33dffc96..000000000000 --- a/cumulus/polkadot-parachain/src/cli.rs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -use crate::common::NodeExtraArgs; -use clap::{Command, CommandFactory, FromArgMatches}; -use sc_cli::SubstrateCli; -use std::path::PathBuf; - -/// Sub-commands supported by the collator. -#[derive(Debug, clap::Subcommand)] -pub enum Subcommand { - /// Key management CLI utilities - #[command(subcommand)] - Key(sc_cli::KeySubcommand), - - /// Build a chain specification. - BuildSpec(sc_cli::BuildSpecCmd), - - /// Validate blocks. - CheckBlock(sc_cli::CheckBlockCmd), - - /// Export blocks. - ExportBlocks(sc_cli::ExportBlocksCmd), - - /// Export the state of a given block into a chain spec. - ExportState(sc_cli::ExportStateCmd), - - /// Import blocks. - ImportBlocks(sc_cli::ImportBlocksCmd), - - /// Revert the chain to a previous state. - Revert(sc_cli::RevertCmd), - - /// Remove the whole chain. - PurgeChain(cumulus_client_cli::PurgeChainCmd), - - /// Export the genesis state of the parachain. - #[command(alias = "export-genesis-state")] - ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand), - - /// Export the genesis wasm of the parachain. - ExportGenesisWasm(cumulus_client_cli::ExportGenesisWasmCommand), - - /// Sub-commands concerned with benchmarking. - /// The pallet benchmarking moved to the `pallet` sub-command. - #[command(subcommand)] - Benchmark(frame_benchmarking_cli::BenchmarkCmd), -} - -#[derive(Debug, clap::Parser)] -#[command( - propagate_version = true, - args_conflicts_with_subcommands = true, - subcommand_negates_reqs = true, - after_help = crate::examples(Self::executable_name()) -)] -pub struct Cli { - #[command(subcommand)] - pub subcommand: Option, - - #[command(flatten)] - pub run: cumulus_client_cli::RunCmd, - - /// EXPERIMENTAL: Use slot-based collator which can handle elastic scaling. - /// - /// Use with care, this flag is unstable and subject to change. - #[arg(long)] - pub experimental_use_slot_based: bool, - - /// Disable automatic hardware benchmarks. - /// - /// By default these benchmarks are automatically ran at startup and measure - /// the CPU speed, the memory bandwidth and the disk speed. - /// - /// The results are then printed out in the logs, and also sent as part of - /// telemetry, if telemetry is enabled. - #[arg(long)] - pub no_hardware_benchmarks: bool, - - /// Export all `PoVs` build by this collator to the given folder. - /// - /// This is useful for debugging issues that are occurring while validating these `PoVs` on the - /// relay chain. - #[arg(long)] - pub export_pov_to_path: Option, - - /// Relay chain arguments - #[arg(raw = true)] - pub relay_chain_args: Vec, -} - -impl Cli { - pub(crate) fn node_extra_args(&self) -> NodeExtraArgs { - NodeExtraArgs { - use_slot_based_consensus: self.experimental_use_slot_based, - export_pov: self.export_pov_to_path.clone(), - } - } -} - -#[derive(Debug)] -pub struct RelayChainCli { - /// The actual relay chain cli object. - pub base: polkadot_cli::RunCmd, - - /// Optional chain id that should be passed to the relay chain. - pub chain_id: Option, - - /// The base path that should be used by the relay chain. - pub base_path: Option, -} - -impl RelayChainCli { - fn polkadot_cmd() -> Command { - let help_template = color_print::cformat!( - "The arguments that are passed to the relay chain node. \n\ - \n\ - RELAY_CHAIN_ARGS: \n\ - {{options}}", - ); - - polkadot_cli::RunCmd::command() - .no_binary_name(true) - .help_template(help_template) - } - - /// Parse the relay chain CLI parameters using the parachain `Configuration`. - pub fn new<'a>( - para_config: &sc_service::Configuration, - relay_chain_args: impl Iterator, - ) -> Self { - let polkadot_cmd = Self::polkadot_cmd(); - let matches = polkadot_cmd.get_matches_from(relay_chain_args); - let base = FromArgMatches::from_arg_matches(&matches).unwrap_or_else(|e| e.exit()); - - let extension = crate::chain_spec::Extensions::try_get(&*para_config.chain_spec); - let chain_id = extension.map(|e| e.relay_chain.clone()); - - let base_path = para_config.base_path.path().join("polkadot"); - Self { base, chain_id, base_path: Some(base_path) } - } -} diff --git a/cumulus/polkadot-parachain/src/command.rs b/cumulus/polkadot-parachain/src/command.rs deleted file mode 100644 index 3df90c889e8e..000000000000 --- a/cumulus/polkadot-parachain/src/command.rs +++ /dev/null @@ -1,856 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -#[cfg(feature = "runtime-benchmarks")] -use crate::service::Block; -use crate::{ - chain_spec::{self, GenericChainSpec}, - cli::{Cli, RelayChainCli, Subcommand}, - common::NodeExtraArgs, - fake_runtime_api::{ - asset_hub_polkadot_aura::RuntimeApi as AssetHubPolkadotRuntimeApi, - aura::RuntimeApi as AuraRuntimeApi, - }, - service::{new_aura_node_spec, DynNodeSpec, ShellNode}, -}; -#[cfg(feature = "runtime-benchmarks")] -use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; -use cumulus_primitives_core::ParaId; -use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; -use log::info; -use parachains_common::{AssetHubPolkadotAuraId, AuraId}; -use sc_cli::{ - ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, - NetworkParams, Result, SharedParams, SubstrateCli, -}; -use sc_service::config::{BasePath, PrometheusConfig}; -use sp_runtime::traits::AccountIdConversion; -#[cfg(feature = "runtime-benchmarks")] -use sp_runtime::traits::HashingFor; -use std::{net::SocketAddr, path::PathBuf}; - -/// The choice of consensus for the parachain omni-node. -#[derive(PartialEq, Eq, Debug, Default)] -pub enum Consensus { - /// Aura consensus. - #[default] - Aura, - /// Use the relay chain consensus. - // TODO: atm this is just a demonstration, not really reach-able. We can add it to the CLI, - // env, or the chain spec. Or, just don't, and when we properly refactor this mess we will - // re-introduce it. - #[allow(unused)] - Relay, -} - -/// Helper enum that is used for better distinction of different parachain/runtime configuration -/// (it is based/calculated on ChainSpec's ID attribute) -#[derive(Debug, PartialEq)] -enum Runtime { - /// None of the system-chain runtimes, rather the node will act agnostic to the runtime ie. be - /// an omni-node, and simply run a node with the given consensus algorithm. - Omni(Consensus), - Shell, - Seedling, - AssetHubPolkadot, - AssetHub, - Penpal(ParaId), - ContractsRococo, - Collectives, - Glutton, - BridgeHub(chain_spec::bridge_hubs::BridgeHubRuntimeType), - Coretime(chain_spec::coretime::CoretimeRuntimeType), - People(chain_spec::people::PeopleRuntimeType), -} - -trait RuntimeResolver { - fn runtime(&self) -> Result; -} - -impl RuntimeResolver for dyn ChainSpec { - fn runtime(&self) -> Result { - Ok(runtime(self.id())) - } -} - -/// Implementation, that can resolve [`Runtime`] from any json configuration file -impl RuntimeResolver for PathBuf { - fn runtime(&self) -> Result { - #[derive(Debug, serde::Deserialize)] - struct EmptyChainSpecWithId { - id: String, - } - - let file = std::fs::File::open(self)?; - let reader = std::io::BufReader::new(file); - let chain_spec: EmptyChainSpecWithId = - serde_json::from_reader(reader).map_err(|e| sc_cli::Error::Application(Box::new(e)))?; - - Ok(runtime(&chain_spec.id)) - } -} - -fn runtime(id: &str) -> Runtime { - let id = id.replace('_', "-"); - let (_, id, para_id) = extract_parachain_id(&id); - - if id.starts_with("shell") { - Runtime::Shell - } else if id.starts_with("seedling") { - Runtime::Seedling - } else if id.starts_with("asset-hub-polkadot") | id.starts_with("statemint") { - Runtime::AssetHubPolkadot - } else if id.starts_with("asset-hub-kusama") | - id.starts_with("statemine") | - id.starts_with("asset-hub-rococo") | - id.starts_with("rockmine") | - id.starts_with("asset-hub-westend") | - id.starts_with("westmint") - { - Runtime::AssetHub - } else if id.starts_with("penpal") { - Runtime::Penpal(para_id.unwrap_or(ParaId::new(0))) - } else if id.starts_with("contracts-rococo") { - Runtime::ContractsRococo - } else if id.starts_with("collectives-polkadot") || id.starts_with("collectives-westend") { - Runtime::Collectives - } else if id.starts_with(chain_spec::bridge_hubs::BridgeHubRuntimeType::ID_PREFIX) { - Runtime::BridgeHub( - id.parse::() - .expect("Invalid value"), - ) - } else if id.starts_with(chain_spec::coretime::CoretimeRuntimeType::ID_PREFIX) { - Runtime::Coretime( - id.parse::().expect("Invalid value"), - ) - } else if id.starts_with("glutton") { - Runtime::Glutton - } else if id.starts_with(chain_spec::people::PeopleRuntimeType::ID_PREFIX) { - Runtime::People(id.parse::().expect("Invalid value")) - } else { - log::warn!( - "No specific runtime was recognized for ChainSpec's id: '{}', \ - so Runtime::Omni(Consensus::Aura) will be used", - id - ); - Runtime::Omni(Consensus::Aura) - } -} - -fn load_spec(id: &str) -> std::result::Result, String> { - let (id, _, para_id) = extract_parachain_id(id); - Ok(match id { - // - Default-like - "staging" => - Box::new(chain_spec::rococo_parachain::staging_rococo_parachain_local_config()), - "tick" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/tick.json")[..], - )?), - "trick" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/trick.json")[..], - )?), - "track" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/track.json")[..], - )?), - - // -- Starters - "shell" => Box::new(chain_spec::shell::get_shell_chain_spec()), - "seedling" => Box::new(chain_spec::seedling::get_seedling_chain_spec()), - - // -- Asset Hub Polkadot - "asset-hub-polkadot" | "statemint" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/asset-hub-polkadot.json")[..], - )?), - - // -- Asset Hub Kusama - "asset-hub-kusama" | "statemine" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/asset-hub-kusama.json")[..], - )?), - - // -- Asset Hub Rococo - "asset-hub-rococo-dev" => - Box::new(chain_spec::asset_hubs::asset_hub_rococo_development_config()), - "asset-hub-rococo-local" => - Box::new(chain_spec::asset_hubs::asset_hub_rococo_local_config()), - // the chain spec as used for generating the upgrade genesis values - "asset-hub-rococo-genesis" => - Box::new(chain_spec::asset_hubs::asset_hub_rococo_genesis_config()), - "asset-hub-rococo" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/asset-hub-rococo.json")[..], - )?), - - // -- Asset Hub Westend - "asset-hub-westend-dev" | "westmint-dev" => - Box::new(chain_spec::asset_hubs::asset_hub_westend_development_config()), - "asset-hub-westend-local" | "westmint-local" => - Box::new(chain_spec::asset_hubs::asset_hub_westend_local_config()), - // the chain spec as used for generating the upgrade genesis values - "asset-hub-westend-genesis" | "westmint-genesis" => - Box::new(chain_spec::asset_hubs::asset_hub_westend_config()), - // the shell-based chain spec as used for syncing - "asset-hub-westend" | "westmint" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/asset-hub-westend.json")[..], - )?), - - // -- Polkadot Collectives - "collectives-polkadot" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/collectives-polkadot.json")[..], - )?), - - // -- Westend Collectives - "collectives-westend-dev" => - Box::new(chain_spec::collectives::collectives_westend_development_config()), - "collectives-westend-local" => - Box::new(chain_spec::collectives::collectives_westend_local_config()), - "collectives-westend" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/collectives-westend.json")[..], - )?), - - // -- Contracts on Rococo - "contracts-rococo-dev" => - Box::new(chain_spec::contracts::contracts_rococo_development_config()), - "contracts-rococo-local" => - Box::new(chain_spec::contracts::contracts_rococo_local_config()), - "contracts-rococo-genesis" => Box::new(chain_spec::contracts::contracts_rococo_config()), - "contracts-rococo" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/contracts-rococo.json")[..], - )?), - - // -- BridgeHub - bridge_like_id - if bridge_like_id - .starts_with(chain_spec::bridge_hubs::BridgeHubRuntimeType::ID_PREFIX) => - bridge_like_id - .parse::() - .expect("invalid value") - .load_config()?, - - // -- Coretime - coretime_like_id - if coretime_like_id - .starts_with(chain_spec::coretime::CoretimeRuntimeType::ID_PREFIX) => - coretime_like_id - .parse::() - .expect("invalid value") - .load_config()?, - - // -- Penpal - "penpal-rococo" => Box::new(chain_spec::penpal::get_penpal_chain_spec( - para_id.expect("Must specify parachain id"), - "rococo-local", - )), - "penpal-westend" => Box::new(chain_spec::penpal::get_penpal_chain_spec( - para_id.expect("Must specify parachain id"), - "westend-local", - )), - - // -- Glutton Westend - "glutton-westend-dev" => Box::new(chain_spec::glutton::glutton_westend_development_config( - para_id.expect("Must specify parachain id"), - )), - "glutton-westend-local" => Box::new(chain_spec::glutton::glutton_westend_local_config( - para_id.expect("Must specify parachain id"), - )), - // the chain spec as used for generating the upgrade genesis values - "glutton-westend-genesis" => Box::new(chain_spec::glutton::glutton_westend_config( - para_id.expect("Must specify parachain id"), - )), - - // -- People - people_like_id - if people_like_id.starts_with(chain_spec::people::PeopleRuntimeType::ID_PREFIX) => - people_like_id - .parse::() - .expect("invalid value") - .load_config()?, - - // -- Fallback (generic chainspec) - "" => { - log::warn!("No ChainSpec.id specified, so using default one, based on rococo-parachain runtime"); - Box::new(chain_spec::rococo_parachain::rococo_parachain_local_config()) - }, - - // -- Loading a specific spec from disk - path => Box::new(GenericChainSpec::from_json_file(path.into())?), - }) -} - -/// Extracts the normalized chain id and parachain id from the input chain id. -/// (H/T to Phala for the idea) -/// E.g. "penpal-kusama-2004" yields ("penpal-kusama", Some(2004)) -fn extract_parachain_id(id: &str) -> (&str, &str, Option) { - let para_prefixes = [ - // Penpal - "penpal-rococo-", - "penpal-westend-", - "penpal-kusama-", - "penpal-polkadot-", - // Glutton Kusama - "glutton-kusama-dev-", - "glutton-kusama-local-", - "glutton-kusama-genesis-", - // Glutton Westend - "glutton-westend-dev-", - "glutton-westend-local-", - "glutton-westend-genesis-", - ]; - - for para_prefix in para_prefixes { - if let Some(suffix) = id.strip_prefix(para_prefix) { - let para_id: u32 = suffix.parse().expect("Invalid parachain-id suffix"); - return (&id[..para_prefix.len() - 1], id, Some(para_id.into())) - } - } - - (id, id, None) -} - -impl SubstrateCli for Cli { - fn impl_name() -> String { - Self::executable_name() - } - - fn impl_version() -> String { - env!("SUBSTRATE_CLI_IMPL_VERSION").into() - } - - fn description() -> String { - format!( - "The command-line arguments provided first will be passed to the parachain node, \n\ - and the arguments provided after -- will be passed to the relay chain node. \n\ - \n\ - Example: \n\ - \n\ - {} [parachain-args] -- [relay-chain-args]", - Self::executable_name() - ) - } - - fn author() -> String { - env!("CARGO_PKG_AUTHORS").into() - } - - fn support_url() -> String { - "https://github.com/paritytech/polkadot-sdk/issues/new".into() - } - - fn copyright_start_year() -> i32 { - 2017 - } - - fn load_spec(&self, id: &str) -> std::result::Result, String> { - load_spec(id) - } -} - -impl SubstrateCli for RelayChainCli { - fn impl_name() -> String { - Cli::impl_name() - } - - fn impl_version() -> String { - Cli::impl_version() - } - - fn description() -> String { - Cli::description() - } - - fn author() -> String { - Cli::author() - } - - fn support_url() -> String { - Cli::support_url() - } - - fn copyright_start_year() -> i32 { - Cli::copyright_start_year() - } - - fn load_spec(&self, id: &str) -> std::result::Result, String> { - polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id) - } -} - -fn new_node_spec( - config: &sc_service::Configuration, - extra_args: &NodeExtraArgs, -) -> std::result::Result, sc_cli::Error> { - Ok(match config.chain_spec.runtime()? { - Runtime::AssetHubPolkadot => - new_aura_node_spec::(extra_args), - Runtime::AssetHub | - Runtime::BridgeHub(_) | - Runtime::Collectives | - Runtime::Coretime(_) | - Runtime::People(_) | - Runtime::ContractsRococo | - Runtime::Glutton | - Runtime::Penpal(_) => new_aura_node_spec::(extra_args), - Runtime::Shell | Runtime::Seedling => Box::new(ShellNode), - Runtime::Omni(consensus) => match consensus { - Consensus::Aura => new_aura_node_spec::(extra_args), - Consensus::Relay => Box::new(ShellNode), - }, - }) -} - -/// Parse command line arguments into service configuration. -pub fn run() -> Result<()> { - let cli = Cli::from_args(); - - match &cli.subcommand { - Some(Subcommand::BuildSpec(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) - }, - Some(Subcommand::CheckBlock(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.prepare_check_block_cmd(config, cmd) - }) - }, - Some(Subcommand::ExportBlocks(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.prepare_export_blocks_cmd(config, cmd) - }) - }, - Some(Subcommand::ExportState(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.prepare_export_state_cmd(config, cmd) - }) - }, - Some(Subcommand::ImportBlocks(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.prepare_import_blocks_cmd(config, cmd) - }) - }, - Some(Subcommand::Revert(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.prepare_revert_cmd(config, cmd) - }) - }, - Some(Subcommand::PurgeChain(cmd)) => { - let runner = cli.create_runner(cmd)?; - let polkadot_cli = RelayChainCli::new(runner.config(), cli.relay_chain_args.iter()); - - runner.sync_run(|config| { - let polkadot_config = SubstrateCli::create_configuration( - &polkadot_cli, - &polkadot_cli, - config.tokio_handle.clone(), - ) - .map_err(|err| format!("Relay chain argument error: {}", err))?; - - cmd.run(config, polkadot_config) - }) - }, - Some(Subcommand::ExportGenesisHead(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.sync_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.run_export_genesis_head_cmd(config, cmd) - }) - }, - Some(Subcommand::ExportGenesisWasm(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.sync_run(|_config| { - let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?; - cmd.run(&*spec) - }) - }, - Some(Subcommand::Benchmark(cmd)) => { - let runner = cli.create_runner(cmd)?; - - // Switch on the concrete benchmark sub-command- - match cmd { - #[cfg(feature = "runtime-benchmarks")] - BenchmarkCmd::Pallet(cmd) => runner.sync_run(|config| { - cmd.run_with_spec::, ReclaimHostFunctions>(Some( - config.chain_spec, - )) - }), - BenchmarkCmd::Block(cmd) => runner.sync_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.run_benchmark_block_cmd(config, cmd) - }), - #[cfg(feature = "runtime-benchmarks")] - BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.run_benchmark_storage_cmd(config, cmd) - }), - BenchmarkCmd::Machine(cmd) => - runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())), - #[allow(unreachable_patterns)] - _ => Err("Benchmarking sub-command unsupported or compilation feature missing. \ - Make sure to compile with --features=runtime-benchmarks \ - to enable all supported benchmarks." - .into()), - } - }, - Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?), - None => { - let runner = cli.create_runner(&cli.run.normalize())?; - let polkadot_cli = RelayChainCli::new(runner.config(), cli.relay_chain_args.iter()); - let collator_options = cli.run.collator_options(); - - runner.run_node_until_exit(|config| async move { - // If Statemint (Statemine, Westmint, Rockmine) DB exists and we're using the - // asset-hub chain spec, then rename the base path to the new chain ID. In the case - // that both file paths exist, the node will exit, as the user must decide (by - // deleting one path) the information that they want to use as their DB. - let old_name = match config.chain_spec.id() { - "asset-hub-polkadot" => Some("statemint"), - "asset-hub-kusama" => Some("statemine"), - "asset-hub-westend" => Some("westmint"), - "asset-hub-rococo" => Some("rockmine"), - _ => None, - }; - - if let Some(old_name) = old_name { - let new_path = config.base_path.config_dir(config.chain_spec.id()); - let old_path = config.base_path.config_dir(old_name); - - if old_path.exists() && new_path.exists() { - return Err(format!( - "Found legacy {} path {} and new Asset Hub path {}. \ - Delete one path such that only one exists.", - old_name, - old_path.display(), - new_path.display() - ) - .into()) - } - - if old_path.exists() { - std::fs::rename(old_path.clone(), new_path.clone())?; - info!( - "{} was renamed to Asset Hub. The filepath with associated data on disk \ - has been renamed from {} to {}.", - old_name, - old_path.display(), - new_path.display() - ); - } - } - - let hwbench = (!cli.no_hardware_benchmarks) - .then_some(config.database.path().map(|database_path| { - let _ = std::fs::create_dir_all(database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) - })) - .flatten(); - - let para_id = chain_spec::Extensions::try_get(&*config.chain_spec) - .map(|e| e.para_id) - .ok_or("Could not find parachain extension in chain-spec.")?; - - let id = ParaId::from(para_id); - - let parachain_account = - AccountIdConversion::::into_account_truncating( - &id, - ); - - let tokio_handle = config.tokio_handle.clone(); - let polkadot_config = - SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) - .map_err(|err| format!("Relay chain argument error: {}", err))?; - - info!("🪪 Parachain id: {:?}", id); - info!("🧾 Parachain Account: {}", parachain_account); - info!("✍️ Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); - - start_node( - config, - polkadot_config, - collator_options, - id, - cli.node_extra_args(), - hwbench, - ) - .await - }) - }, - } -} - -#[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_node( - config: sc_service::Configuration, - polkadot_config: sc_service::Configuration, - collator_options: cumulus_client_cli::CollatorOptions, - id: ParaId, - extra_args: NodeExtraArgs, - hwbench: Option, -) -> Result { - let node_spec = new_node_spec(&config, &extra_args)?; - node_spec - .start_node(config, polkadot_config, collator_options, id, hwbench, extra_args) - .await - .map_err(Into::into) -} - -impl DefaultConfigurationValues for RelayChainCli { - fn p2p_listen_port() -> u16 { - 30334 - } - - fn rpc_listen_port() -> u16 { - 9945 - } - - fn prometheus_listen_port() -> u16 { - 9616 - } -} - -impl CliConfiguration for RelayChainCli { - fn shared_params(&self) -> &SharedParams { - self.base.base.shared_params() - } - - fn import_params(&self) -> Option<&ImportParams> { - self.base.base.import_params() - } - - fn network_params(&self) -> Option<&NetworkParams> { - self.base.base.network_params() - } - - fn keystore_params(&self) -> Option<&KeystoreParams> { - self.base.base.keystore_params() - } - - fn base_path(&self) -> Result> { - Ok(self - .shared_params() - .base_path()? - .or_else(|| self.base_path.clone().map(Into::into))) - } - - fn rpc_addr(&self, default_listen_port: u16) -> Result> { - self.base.base.rpc_addr(default_listen_port) - } - - fn prometheus_config( - &self, - default_listen_port: u16, - chain_spec: &Box, - ) -> Result> { - self.base.base.prometheus_config(default_listen_port, chain_spec) - } - - fn init( - &self, - _support_url: &String, - _impl_version: &String, - _logger_hook: F, - _config: &sc_service::Configuration, - ) -> Result<()> - where - F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), - { - unreachable!("PolkadotCli is never initialized; qed"); - } - - fn chain_id(&self, is_dev: bool) -> Result { - let chain_id = self.base.base.chain_id(is_dev)?; - - Ok(if chain_id.is_empty() { self.chain_id.clone().unwrap_or_default() } else { chain_id }) - } - - fn role(&self, is_dev: bool) -> Result { - self.base.base.role(is_dev) - } - - fn transaction_pool(&self, is_dev: bool) -> Result { - self.base.base.transaction_pool(is_dev) - } - - fn trie_cache_maximum_size(&self) -> Result> { - self.base.base.trie_cache_maximum_size() - } - - fn rpc_methods(&self) -> Result { - self.base.base.rpc_methods() - } - - fn rpc_max_connections(&self) -> Result { - self.base.base.rpc_max_connections() - } - - fn rpc_cors(&self, is_dev: bool) -> Result>> { - self.base.base.rpc_cors(is_dev) - } - - fn default_heap_pages(&self) -> Result> { - self.base.base.default_heap_pages() - } - - fn force_authoring(&self) -> Result { - self.base.base.force_authoring() - } - - fn disable_grandpa(&self) -> Result { - self.base.base.disable_grandpa() - } - - fn max_runtime_instances(&self) -> Result> { - self.base.base.max_runtime_instances() - } - - fn announce_block(&self) -> Result { - self.base.base.announce_block() - } - - fn telemetry_endpoints( - &self, - chain_spec: &Box, - ) -> Result> { - self.base.base.telemetry_endpoints(chain_spec) - } - - fn node_name(&self) -> Result { - self.base.base.node_name() - } -} - -#[cfg(test)] -mod tests { - use crate::{ - chain_spec::{get_account_id_from_seed, get_from_seed}, - command::{Consensus, Runtime, RuntimeResolver}, - }; - use sc_chain_spec::{ChainSpec, ChainSpecExtension, ChainSpecGroup, ChainType, Extension}; - use serde::{Deserialize, Serialize}; - use sp_core::sr25519; - use std::path::PathBuf; - use tempfile::TempDir; - - #[derive( - Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension, Default, - )] - #[serde(deny_unknown_fields)] - pub struct Extensions1 { - pub attribute1: String, - pub attribute2: u32, - } - - #[derive( - Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension, Default, - )] - #[serde(deny_unknown_fields)] - pub struct Extensions2 { - pub attribute_x: String, - pub attribute_y: String, - pub attribute_z: u32, - } - - fn store_configuration(dir: &TempDir, spec: &dyn ChainSpec) -> PathBuf { - let raw_output = true; - let json = sc_service::chain_ops::build_spec(spec, raw_output) - .expect("Failed to build json string"); - let mut cfg_file_path = dir.path().to_path_buf(); - cfg_file_path.push(spec.id()); - cfg_file_path.set_extension("json"); - std::fs::write(&cfg_file_path, json).expect("Failed to write to json file"); - cfg_file_path - } - - pub type DummyChainSpec = sc_service::GenericChainSpec; - - pub fn create_default_with_extensions( - id: &str, - extension: E, - ) -> DummyChainSpec { - DummyChainSpec::builder( - rococo_parachain_runtime::WASM_BINARY - .expect("WASM binary was not built, please build it!"), - extension, - ) - .with_name("Dummy local testnet") - .with_id(id) - .with_chain_type(ChainType::Local) - .with_genesis_config_patch(crate::chain_spec::rococo_parachain::testnet_genesis( - get_account_id_from_seed::("Alice"), - vec![ - get_from_seed::("Alice"), - get_from_seed::("Bob"), - ], - vec![get_account_id_from_seed::("Alice")], - 1000.into(), - )) - .build() - } - - #[test] - fn test_resolve_runtime_for_different_configuration_files() { - let temp_dir = tempfile::tempdir().expect("Failed to access tempdir"); - - let path = store_configuration( - &temp_dir, - &create_default_with_extensions("shell-1", Extensions1::default()), - ); - assert_eq!(Runtime::Shell, path.runtime().unwrap()); - - let path = store_configuration( - &temp_dir, - &create_default_with_extensions("shell-2", Extensions2::default()), - ); - assert_eq!(Runtime::Shell, path.runtime().unwrap()); - - let path = store_configuration( - &temp_dir, - &create_default_with_extensions("seedling", Extensions2::default()), - ); - assert_eq!(Runtime::Seedling, path.runtime().unwrap()); - - let path = store_configuration( - &temp_dir, - &create_default_with_extensions("penpal-rococo-1000", Extensions2::default()), - ); - assert_eq!(Runtime::Penpal(1000.into()), path.runtime().unwrap()); - - let path = store_configuration( - &temp_dir, - &create_default_with_extensions("penpal-polkadot-2000", Extensions2::default()), - ); - assert_eq!(Runtime::Penpal(2000.into()), path.runtime().unwrap()); - - let path = store_configuration( - &temp_dir, - &crate::chain_spec::contracts::contracts_rococo_local_config(), - ); - assert_eq!(Runtime::ContractsRococo, path.runtime().unwrap()); - - let path = store_configuration( - &temp_dir, - &crate::chain_spec::rococo_parachain::rococo_parachain_local_config(), - ); - assert_eq!(Runtime::Omni(Consensus::Aura), path.runtime().unwrap()); - } -} diff --git a/cumulus/polkadot-parachain/src/main.rs b/cumulus/polkadot-parachain/src/main.rs index 84e41fc347d9..f2dce552c51a 100644 --- a/cumulus/polkadot-parachain/src/main.rs +++ b/cumulus/polkadot-parachain/src/main.rs @@ -19,38 +19,36 @@ #![warn(missing_docs)] #![warn(unused_extern_crates)] -pub(crate) fn examples(executable_name: String) -> String { - color_print::cformat!( - r#"Examples: +mod chain_spec; - {0} --chain para.json --sync warp -- --chain relay.json --sync warp - Launch a warp-syncing full node of a given para's chain-spec, and a given relay's chain-spec. +use polkadot_parachain_lib::{run, CliConfig as CliConfigT, RunConfig}; - The above approach is the most flexible, and the most forward-compatible way to spawn an omni-node. +struct CliConfig; - You can find the chain-spec of some networks in: - https://paritytech.github.io/chainspecs +impl CliConfigT for CliConfig { + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } - {0} --chain asset-hub-polkadot --sync warp -- --chain polkadot --sync warp - Launch a warp-syncing full node of the Asset Hub parachain on the Polkadot Relay Chain. + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } - {0} --chain asset-hub-kusama --sync warp --relay-chain-rpc-url ws://rpc.example.com -- --chain kusama - Launch a warp-syncing full node of the Asset Hub parachain on the Kusama Relay Chain. - Uses ws://rpc.example.com as remote relay chain node. - "#, - executable_name, - ) -} + fn support_url() -> String { + "https://github.com/paritytech/polkadot-sdk/issues/new".into() + } -mod chain_spec; -mod cli; -mod command; -mod common; -mod fake_runtime_api; -mod rpc; -mod service; + fn copyright_start_year() -> u16 { + 2017 + } +} fn main() -> color_eyre::eyre::Result<()> { color_eyre::install()?; - Ok(command::run()?) + + let config = RunConfig { + chain_spec_loader: Box::new(chain_spec::ChainSpecLoader), + runtime_resolver: Box::new(chain_spec::RuntimeResolver), + }; + Ok(run::(config)?) } diff --git a/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs b/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs index 939043b53529..38ef18b88e0d 100644 --- a/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs +++ b/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs @@ -85,7 +85,7 @@ //! This phase consists of plugging in the new slot-based collator. //! //! 1. In `node/src/service.rs` import the slot based collator instead of the lookahead collator. -#![doc = docify::embed!("../../cumulus/polkadot-parachain/src/service.rs", slot_based_colator_import)] +#![doc = docify::embed!("../../cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs", slot_based_colator_import)] //! //! 2. In `start_consensus()` //! - Remove the `overseer_handle` param (also remove the @@ -94,7 +94,7 @@ //! `slot_drift` field with a value of `Duration::from_secs(1)`. //! - Replace the single future returned by `aura::run` with the two futures returned by it and //! spawn them as separate tasks: -#![doc = docify::embed!("../../cumulus/polkadot-parachain/src/service.rs", launch_slot_based_collator)] +#![doc = docify::embed!("../../cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs", launch_slot_based_collator)] //! //! 3. In `start_parachain_node()` remove the `overseer_handle` param passed to `start_consensus`. //! diff --git a/prdoc/pr_5288.prdoc b/prdoc/pr_5288.prdoc new file mode 100644 index 000000000000..8241e75876f1 --- /dev/null +++ b/prdoc/pr_5288.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Added `polkadot-parachain-lib` helper library that can be used to build a parachain node + +doc: + - audience: Node Dev + description: | + This PR adds the `polkadot-parachain-lib` helper library that can be used to build a parachain node. + +crates: + - name: polkadot-parachain-bin + bump: patch + - name: polkadot-parachain-lib + bump: patch + - name: polkadot-sdk + bump: patch diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 65ff9a81e474..2bfed05cd178 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -330,6 +330,7 @@ runtime-benchmarks = [ "parachains-common?/runtime-benchmarks", "polkadot-cli?/runtime-benchmarks", "polkadot-node-metrics?/runtime-benchmarks", + "polkadot-parachain-lib?/runtime-benchmarks", "polkadot-parachain-primitives?/runtime-benchmarks", "polkadot-primitives?/runtime-benchmarks", "polkadot-runtime-common?/runtime-benchmarks", @@ -459,6 +460,7 @@ try-runtime = [ "pallet-xcm-bridge-hub?/try-runtime", "pallet-xcm?/try-runtime", "polkadot-cli?/try-runtime", + "polkadot-parachain-lib?/try-runtime", "polkadot-runtime-common?/try-runtime", "polkadot-runtime-parachains?/try-runtime", "polkadot-sdk-frame?/try-runtime", @@ -755,7 +757,7 @@ runtime = [ "xcm-procedural", "xcm-runtime-apis", ] -node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] +node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-parachain-lib", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] tuples-96 = [ "frame-support-procedural?/tuples-96", "frame-support?/tuples-96", @@ -2212,6 +2214,11 @@ path = "../polkadot/node/overseer" default-features = false optional = true +[dependencies.polkadot-parachain-lib] +path = "../cumulus/polkadot-parachain/polkadot-parachain-lib" +default-features = false +optional = true + [dependencies.polkadot-rpc] path = "../polkadot/rpc" default-features = false diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 7bdfcd0b03ae..5820fa803547 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -882,6 +882,10 @@ pub use polkadot_node_subsystem_util; #[cfg(feature = "polkadot-overseer")] pub use polkadot_overseer; +/// Helper library that can be used to build a parachain node. +#[cfg(feature = "polkadot-parachain-lib")] +pub use polkadot_parachain_lib; + /// Types and utilities for creating and working with parachains. #[cfg(feature = "polkadot-parachain-primitives")] pub use polkadot_parachain_primitives; From 646e4f7ed3eae1de98d34da2a5c785180c2ea1a9 Mon Sep 17 00:00:00 2001 From: Diego Date: Wed, 21 Aug 2024 12:41:57 -0300 Subject: [PATCH 140/382] docs: Fix beefy primitives link in beefy README (#5428) Just fixing the beefy primitives link to the right one after moving these primitives to `consensus` --- substrate/client/consensus/beefy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/client/consensus/beefy/README.md b/substrate/client/consensus/beefy/README.md index a7956cfcd42e..cb9a9267f77e 100644 --- a/substrate/client/consensus/beefy/README.md +++ b/substrate/client/consensus/beefy/README.md @@ -159,7 +159,7 @@ ambiguity despite using block number instead of a hash. A collection of **votes* a Commitment and a collection of signatures is going to be called **Signed Commitment**. A valid (see later for the rules) Signed Commitment is also called a **BEEFY Justification** or **BEEFY Finality Proof**. For more details on the actual data structures please see -[BEEFY primitives definitions](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/primitives/beefy/src). +[BEEFY primitives definitions](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/primitives/consensus/beefy/src). A **round** is an attempt by BEEFY validators to produce a BEEFY Justification. **Round number** is simply defined as a block number the validators are voting for, or to be more precise, the From 8b17f0f4c697793e0080836f175596a5ac2a0b3a Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Wed, 21 Aug 2024 18:50:50 +0300 Subject: [PATCH 141/382] peerstore: Clarify peer report warnings (#5407) This PR aims to make the logging from the peer store a bit more clear. In the past, we aggressively produced warning logs from the peer store component, even in cases where the reputation change was not malicious. This has led to an extensive number of logs, as well to node operator confusion. In this PR, we produce a warning message if: - The peer crosses the banned threshold for the first time. This is the actual reason of a ban - The peer misbehaves again while being banned. This may happen during a batch peer report cc @paritytech/networking Part of: https://github.com/paritytech/polkadot-sdk/issues/5379. --------- Signed-off-by: Alexandru Vasile Co-authored-by: Dmitry Markin --- prdoc/pr_5407.prdoc | 17 ++++ .../client/network/src/litep2p/peerstore.rs | 79 ++++++++++++------- substrate/client/network/src/peer_store.rs | 41 ++++++++-- 3 files changed, 105 insertions(+), 32 deletions(-) create mode 100644 prdoc/pr_5407.prdoc diff --git a/prdoc/pr_5407.prdoc b/prdoc/pr_5407.prdoc new file mode 100644 index 000000000000..f7e6b86f9d1e --- /dev/null +++ b/prdoc/pr_5407.prdoc @@ -0,0 +1,17 @@ +title: Prepare PVFs if node is a validator in the next session + +doc: + - audience: [Node Operator, Node Dev] + description: | + This PR aims to remove the noise caused by the peer store's reputation system. + A warning was emitted each time a reputation was reported for a banned peer, + regardless of the reputation being positive. This has led in the past to + situations where it was hard to identify the actual reason of the ban and + caused noise for node operators. + + The `Banned, disconnecting.` warning is logged only when the peer is banned. + Other misbehaves are logged as `Misbehaved during the ban threshold`. + +crates: + - name: sc-network + bump: patch diff --git a/substrate/client/network/src/litep2p/peerstore.rs b/substrate/client/network/src/litep2p/peerstore.rs index 55e912c31f18..dd8d92bcee64 100644 --- a/substrate/client/network/src/litep2p/peerstore.rs +++ b/substrate/client/network/src/litep2p/peerstore.rs @@ -192,38 +192,63 @@ impl PeerStoreProvider for PeerstoreHandle { } /// Adjust peer reputation. - fn report_peer(&self, peer: PeerId, reputation_change: ReputationChange) { + fn report_peer(&self, peer_id: PeerId, change: ReputationChange) { let mut lock = self.0.lock(); + let peer_info = lock.peers.entry(peer_id).or_default(); + let was_banned = peer_info.is_banned(); + peer_info.add_reputation(change.value); + let peer_reputation = peer_info.reputation; + + log::trace!( + target: LOG_TARGET, + "Report {}: {:+} to {}. Reason: {}.", + peer_id, + change.value, + peer_reputation, + change.reason, + ); - log::trace!(target: LOG_TARGET, "report peer {reputation_change:?}"); - - match lock.peers.get_mut(&peer) { - Some(info) => { - info.add_reputation(reputation_change.value); - }, - None => { - lock.peers.insert( - peer, - PeerInfo { - reputation: reputation_change.value, - last_updated: Instant::now(), - role: None, - }, + if !peer_info.is_banned() { + if was_banned { + log::info!( + target: LOG_TARGET, + "Peer {} is now unbanned: {:+} to {}. Reason: {}.", + peer_id, + change.value, + peer_reputation, + change.reason, ); - }, + } + return; } - if lock - .peers - .get(&peer) - .expect("peer exist since it was just modified; qed") - .is_banned() - { - log::warn!(target: LOG_TARGET, "{peer:?} banned, disconnecting, reason: {}", reputation_change.reason); - - for sender in &lock.protocols { - sender.disconnect_peer(peer); - } + // Peer is currently banned, disconnect it from all protocols. + lock.protocols.iter().for_each(|handle| handle.disconnect_peer(peer_id.into())); + + // The peer is banned for the first time. + if !was_banned { + log::warn!( + target: LOG_TARGET, + "Report {}: {:+} to {}. Reason: {}. Banned, disconnecting.", + peer_id, + change.value, + peer_reputation, + change.reason, + ); + return; + } + + // The peer was already banned and it got another negative report. + // This may happen during a batch report. + if change.value < 0 { + log::debug!( + target: LOG_TARGET, + "Report {}: {:+} to {}. Reason: {}. Misbehaved during the ban threshold.", + peer_id, + change.value, + peer_reputation, + change.reason, + ); } } diff --git a/substrate/client/network/src/peer_store.rs b/substrate/client/network/src/peer_store.rs index 63e98a2fb4bf..0e57791542e1 100644 --- a/substrate/client/network/src/peer_store.rs +++ b/substrate/client/network/src/peer_store.rs @@ -260,11 +260,37 @@ impl PeerStoreInner { fn report_peer(&mut self, peer_id: PeerId, change: ReputationChange) { let peer_info = self.peers.entry(peer_id).or_default(); + let was_banned = peer_info.is_banned(); peer_info.add_reputation(change.value); - if peer_info.is_banned() { - self.protocols.iter().for_each(|handle| handle.disconnect_peer(peer_id.into())); + log::trace!( + target: LOG_TARGET, + "Report {}: {:+} to {}. Reason: {}.", + peer_id, + change.value, + peer_info.reputation, + change.reason, + ); + + if !peer_info.is_banned() { + if was_banned { + log::info!( + target: LOG_TARGET, + "Peer {} is now unbanned: {:+} to {}. Reason: {}.", + peer_id, + change.value, + peer_info.reputation, + change.reason, + ); + } + return; + } + + // Peer is currently banned, disconnect it from all protocols. + self.protocols.iter().for_each(|handle| handle.disconnect_peer(peer_id.into())); + // The peer is banned for the first time. + if !was_banned { log::warn!( target: LOG_TARGET, "Report {}: {:+} to {}. Reason: {}. Banned, disconnecting.", @@ -273,10 +299,15 @@ impl PeerStoreInner { peer_info.reputation, change.reason, ); - } else { - log::trace!( + return; + } + + // The peer was already banned and it got another negative report. + // This may happen during a batch report. + if change.value < 0 { + log::debug!( target: LOG_TARGET, - "Report {}: {:+} to {}. Reason: {}.", + "Report {}: {:+} to {}. Reason: {}. Misbehaved during the ban threshold.", peer_id, change.value, peer_info.reputation, From dce789ddd28ea179e42a00f2817b3e5d713bf6f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=B3nal=20Murray?= Date: Thu, 22 Aug 2024 08:37:12 +0200 Subject: [PATCH 142/382] Add the Polkadot Coretime chain-spec (#5436) Add the Polkadot Coretime chain-spec to the directory with the other system chain-specs. This is the chain-spec used at genesis and for which the genesis head data was generated. It is also included in the assets for fellowship [release v1.3.0](https://github.com/polkadot-fellows/runtimes/releases/tag/v1.3.0) --- .../chain-specs/coretime-polkadot.json | 92 +++++++++++++++++++ .../chain-specs/coretime-polkadot.json | 1 + .../src/chain_spec/coretime.rs | 5 +- prdoc/pr_5436.prdoc | 20 ++++ 4 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 cumulus/parachains/chain-specs/coretime-polkadot.json create mode 120000 cumulus/polkadot-parachain/chain-specs/coretime-polkadot.json create mode 100644 prdoc/pr_5436.prdoc diff --git a/cumulus/parachains/chain-specs/coretime-polkadot.json b/cumulus/parachains/chain-specs/coretime-polkadot.json new file mode 100644 index 000000000000..73e104b38290 --- /dev/null +++ b/cumulus/parachains/chain-specs/coretime-polkadot.json @@ -0,0 +1,92 @@ +{ + "name": "Polkadot Coretime", + "id": "coretime-polkadot", + "chainType": "Live", + "bootNodes": [ + "/dns/polkadot-coretime-connect-a-0.polkadot.io/tcp/30334/p2p/12D3KooWKjnixAHbKMsPTJwGx8SrBeGEJLHA8KmKcEDYMp3YmWgR", + "/dns/polkadot-coretime-connect-a-1.polkadot.io/tcp/30334/p2p/12D3KooWQ7B7p4DFv1jWqaKfhrZBcMmi5g8bWFnmskguLaGEmT6n", + "/dns/polkadot-coretime-connect-a-0.polkadot.io/tcp/443/wss/p2p/12D3KooWKjnixAHbKMsPTJwGx8SrBeGEJLHA8KmKcEDYMp3YmWgR", + "/dns/polkadot-coretime-connect-a-1.polkadot.io/tcp/443/wss/p2p/12D3KooWQ7B7p4DFv1jWqaKfhrZBcMmi5g8bWFnmskguLaGEmT6n", + "/dns4/coretime-polkadot.boot.stake.plus/tcp/30332/wss/p2p/12D3KooWFJ2yBTKFKYwgKUjfY3F7XfaxHV8hY6fbJu5oMkpP7wZ9", + "/dns4/coretime-polkadot.boot.stake.plus/tcp/31332/wss/p2p/12D3KooWCy5pToLafcQzPHn5kadxAftmF6Eh8ZJGPXhSeXSUDfjv" + ], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "ss58Format": 0, + "tokenDecimals": 10, + "tokenSymbol": "DOT" + }, + "relay_chain": "polkadot", + "para_id": 1005, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xed030000", + "0x0d715f2646c8f85767b5d2764bb278264e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x15464cac3378d46f113cd5b7a4d71c84476f594316a7dfe49c1f352d95abdaf1": "0x00000000", + "0x15464cac3378d46f113cd5b7a4d71c844e7b9012096b41c4eb3aaf947f6ea429": "0x0200", + "0x15464cac3378d46f113cd5b7a4d71c845579297f4dfb9609e7e4c2ebab9ce40a": "0x1c00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127049bec59fb5fe6adea4578250578e89dd7e51ad88c7c92493d6f451c6680925c20d8c795eef2620fba2bde74dbc36461c07998ebf600ed265b746c1e05c706064c0aa0240b2d7485675e52cdb283a87973652f6acb42c830a5a5faa80f7a707e6610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38689e1a66fa33b75f66415021aacc4fa23f49306a3c21407748b8b2d39b4abf6380b6f570f356fef7b891afa2e1c30fca89bc7a2cddd545fd8a173106fce3a11f", + "0x15464cac3378d46f113cd5b7a4d71c84579f5a43435b04a98d64da0cefe18505": "0x00a0acb9030000000000000000000000", + "0x1809d78346727a0ef58c0fa03bafa3234e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x000000008200e17579c4", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9381ca18820b278a00faeab03d52440be00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da944098499b5de4f5677804569aeadeb5e4c0aa0240b2d7485675e52cdb283a87973652f6acb42c830a5a5faa80f7a707e": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94c4f030742ff8899655335ad1e54ca6a6610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94d2dbb242ff048066ba7c14ef24678cf20d8c795eef2620fba2bde74dbc36461c07998ebf600ed265b746c1e05c70606": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da956f8d5eba063b801102d640867bbb26f689e1a66fa33b75f66415021aacc4fa23f49306a3c21407748b8b2d39b4abf63": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da96684268ab336f4623df9eab07c482056049bec59fb5fe6adea4578250578e89dd7e51ad88c7c92493d6f451c6680925c": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9784e05d1b3afe91a143e23fe5983f63080b6f570f356fef7b891afa2e1c30fca89bc7a2cddd545fd8a173106fce3a11f": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da994eb9f87cb79eefab94a8a6ffcb94bfe6d6f646c70792f62726f6b650000000000000000000000000000000000000000": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0xe2373d0044636f726574696d652d706f6c6b61646f74", + "0x3a63": "0x", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd0058b40805be8e467714531068689474e824c10a8cc53774726667883ec05d802d901643e234ed8170f89bac708c7155821d498f97282e09dca01e2231f2d04dc75ed1ff328c8cb9dcb66ec66356fc03a1ff37d21a2184104208d95b4a1924164e147013536e28ea9568e6424433bf39eaf5749ad799e3bcdebcc62baa8b99b5bb1045972edcdc7cca0dbd79253a5d88e8f499df68e0ac0d450d80e0c5cccccce93399579a75cf70040f84703a9d4e9c877dc8db66a56206344470c4ebf6d36be937afe0675e636cb07b17ba3e84ddf3bc4fb941bd77b35289620b6264b15d8868f36695b98e5d08fcd0750cc3b06bb3b292e88214cc28954adb4bb362b39aaeebbaae67b312f1043020c8b26d9bf66dd66bd68e9b758823c8308533689aa6bdc6bce6953bf6aa8136b36623406307660c4066d66b8a2aca0004ed42441ac9346bb340081ff881f681ddacdc0408706084a669da6bf6ee35e632afdc6d5eb52360e9436091598b385201129cc82e44943de63b940e5e68fb50e920087eca0d0a7e00b366a08b177c8004ee4244dcb3c7cc0ace5a647be942f143db4ba552e9a4598950e20c56a2c8b24fb9a1d92b51cc858862ce3d9bb534eb005e8784e2b70b19f950fcf66dfb941b74fb37eb65842d5ce0058efb941bcabd12811722021f73eeb574d22bf8ef35e6da2b37eb362bf74a348593a1f1536ed0f821a17823171a8a3772a01f993576d1842a4d88898979252a5d88a874f031b3c6f9b3c3ac54be50420e5e0082e02b51bc10517ce9e0ac46e6008accaa811970600c4628954aaf443ac49766059a3b746a3f805933249c80841234c64fb9a1f195687521221dbe7a7ca5df5e578f79d5e1e06b9c5500b300b3ca26a6808213b412d10be970a2d5f4a1537b3a1df03325403425a33a9c68ca0cd5e1536ea80eb3874eedb9979edd74efa8db78ce737cc77b5ec3879cc873780000000000e490430e38e0800311229f52a2446eb8e186214386d860c3a794a80daf3caf397676765e6978d5c183078fd71faf3b7af4e8f11ae435e5c3c7a7d8501faf425e7b7878785e55af4068a0e1536c280daf3e2a954a88102141827c4a8906f9f1e3534af4c76bcfab4e8e1c395e81bceee8d0f1293654c76b0daf3c76ecf8141bbae3d5861a6af89412ade1d5c7eb75743ec586eabcf648a53ec586a65e6b000204484f4f4f2af529259a7add79b51186e12b8fd71b737e8a0d9daf3d5e7370e0f8141b8ae3c78f4fb1a13f68a081061e1e1e1f3e7cf4e8f12925da83070f1e3b3b3b3a3a9f52a23aaf375e4f373737af39af332814eaf5af34383838af395e6bd8b0f12936d4c6ab8ed7f0c68d1baf3b5e674ecea7d8d09cd7d48e1d3b74e8d09123c7a794688eff534af4afe1ab773a9d5ee7ebcdcccca7d8d099571caf281a9a4fb1a134af3a38707c4a89e278b5f17a79dea7d850ef15a7468d4fb1a1355e71cc39c330ac51e3534ab4c6ebcd6b272323f38a7ab531993ec5869a5e715e6b30ec536c28f67f8a0d7d4e4ece8d1b376cd8b08183f329258a8342a16e6e6e3cef534ad47bb5798d0141f0b5e6b54422915eafd74fd33ec5866aafa7d7aceb3ec58676af33af323636363e685e4d35359f62436b82d4a0a1a1999999399d3ea5444fd7f529257abdcaa4b6989818cdd4037aa552097c259dbeefabf1662592c251ec44533e8a7d4a8962af9d0eb7dbb6490d27cb3ec58666efd4be62433ab5279212e975a229247a7d8a0dbde6d090941a1a2f45a3f143451a158a6730a2f1453534ce1c3ab5423a2869559c555e49a756eefbe361b3a0d7acf172a28e3489534807aae488fec4fd80ba6584034006197ce101767c8c8f315a41638c310e75cb03dc219ce009fa56a55a5525d8aabece91952b91369fb6f67ebf47888e2e185594d8efd71d63e4d8dcddc789ccd7284f1412be3cc7b99ddaca34320fa15bda7133771cf6d1d7de2e11ad5595697c10301a8953ebdae350526f890c31182a94512931a52e7a1c46fa49c13c2f548a475a22436c448574a0fcfe7e3c4823fe8e804694ef19517e77e956a79eee00cf5f91218c8c8c8ca0fcae3dd92afe7e5bd55aa7e27f9a43af53f1a534c07aa2af1a8def4ead929d5aa6f138ade2d7b44a86527228514ba37d8df64421e9cbf7e52c62ca73bf23fc9eb5cf8fcd4c846ea9d6aa7a138302b0cfaffb2e863580fdf8fd7e3cf49bc4a9756a7fbac53c80eea23b861457e809a75b1ea07c4fabaa36a455157cabae076995a4d76b5ab5472230835ec76955d38b568fd61e7a7ddba757d5a3b5865e075bd5bf7e6a55fcf5eb5eabf8d757e79a4df6882e8e163357e9f8ab537bc2a51d7b0c243bc5f2ff7890cfe6d675ef598b22e5733fb5ca7b7c4dab344fc3b059b173c734edf160abb0c7775ac8b4c3c2edd476ea3b7f469aca4e7db37ee7ae854bbfb0e35e8e24be162ef5c2a55898aaa1fc5d9e781c90f2acd9afc77791b90b9772a1a6037f0b8b2eca7ca2fd6ebbf6f633829d6798859569f6f82d66e2578fc677f2db68f23aa5d3a978eddba9fd3ba549baffe99f98854b37dc4ef56558990a71983061c2d0daa27b2cac4591ee796a9deaf307b4b3bfdf35c14ef57fda3bb56a697ca438ad3a8df7b144228ddf56f1ab0e8d5f9dd8f1b5836e752ab89daa1aed578deeb77db677fafb6e7780f1ddee80ddfe00f7a84a15ba471ca8426b89f66da07bb4012eb43f201a407e7f5b15df97adcaded75ab5b4dff50794cd05e36f7cc8c204cbb394bf02b4687cc75e0c976621d38ebff82e86452c04243b257f04a4f1fcedb0347e3fa0ed54f5285fb3b6e8a5c52cac9256f9a248b1ef0774fda7bb6e3f23d8ac9262d79e85f5347b0c6bad350611a50e41b36fa7b2ff442d1462d1f82cac3854be630f0b97c670e97e3c481affd33f7d854291c6cb703bc5df0f284ed929fe4faf4ecf26205d1cad8ce311dd2a00ac3894afd32a3e1f877f7eaa557bb48123caf7c1382ce56ff4d9ded9f337fa6cef548deeab46f91b7db6772a48f715a4fcd5e1d9a4872e8e167393ae01209fe9b690fd7edd2a00ac1eed7badda31a4b842fb60abf628044dd0fea955fb7e4dabf87d1cc681697f757a36395d1c2de60074cb03dc232392a055a37bb0553b861457e85e6bd516e10c69d0bdd72aa6fb6d9f5dd513ddafcece26a78ba3c5ac43b73ec00a527eaa55d5a37c7e4dab6a8af2f93e5ac5940fb66a8f36b085f24fad6acadff6e155c5a17caf557bfeeaf06cd24317478b3901dda6c0fd662632b0108e500617f381180fc424110302d317d3144c523069c1e405d3174c603099610a83490ca6334c6598ac600263c2820906a618805e00c1805d00b9006e01d4029805100be015c032402b805500a9007e01a7004601f40242017c02e8049009e0124025805d4030c02e4022804500bd008f401a802f00a500a3003950c2822904252494c62889514a4249097104b189c8445c222a21c120bf20bd20af20cb9056c854f0edc0fb80678477a5234357061212242c4860484f208d411283940512164a2328355162a2a444294b498a52074a564a1c286da064440a433a43898a120cb2116062c40f805f605cba33b82cdc141c0c382a382da62b60570057909d202b41f480d701ef08cf8a57858b02e704ce0bc90892d117e63bc367862f8daf0c1f1adf19df183e33be2f7c5df8b4f095f155e1a3c2f7e59bc227858f8c8f095f123e247c637c627c617c5c3e30be2ebe217c41f8b4f88e3e2cbe1f7c3e284df11df15df1e5e0bbc167056605d007de06b21490c440fa02c90b243390d22095814406121aa5284a2e2895a04482af0ae94ae9090f09508bd881d295120a4a554a272819513241c909ae0a8c0bac0adc0c38293a2e7038e06e509904611a830f0438b2831103140009d000563a20a0071e0ea0eae9b620c4062031213075c1c405d3164c593055c14405d3134c64988c60fac2e485a90b93104c5b987a60fa816907a61b987060b2c26403530d4c4f98a83081c0944412300c60124024805c4023804060238a1802b3b01168a574855219252b94aa50a242490a2528949e506a428909a52e19a00286211d7d3af870f075c00be39dc13383978657068f0c1e1ade19de183c317861f0ccf0c0e07dc1f38207c6eb82c7056f0b9e16bc2c7858f0aee09501c513418706f883ce0c100b308b6e091d153a31b8343833381b7035e068c0c9808b017704b701d315a61c987860aac2e403930e38a32dcc7686cd0c5b1adb184c5a9882602282a908262eb6306c666c60d8beb0796103b371c17464c2c2b4840d0b26304c4d30753129c1940413124c6398c43085613ac27685ad8c8d0adb976d0a5b14362f1b14b62c6c676c4dd898b029614bc286846d09db185b97ed099b13363236317046701ce0aa806880677056402c26266c5ad8d0d8ac6072c256854d0aa52fa52980660087008e014c03dc007785f30087049704f7010e045c08382c9c08b62d6c64e0942879e19600c5e098e046c091a0b404ae049c13601940326c626c616c5c4a51d8bed8c0d88cb079b11561eb6223c2c6c536844d08db165b10b62e6c653071310d6103c2a6c576b465b161b1fd60f30138c6c6836d071b0e361d6c576c39d86c104fb055b16dd968b0cd60abc1a6657bc116c5e682ad051b0b3628b6156c2ad864b0c560a36283c136c5966593627b6243c19682cd041b093627b612704d804c804b804a6c37d846b035b131b12db129b161d944b0856003c196c4f6810d89edcad681ed88cdca56058462e3c0b681cd88cd480ba39d413383968656068d0c1a1ada19da1834316861d0ccd0c0a07d41f3820646ebc276028d0bda164018949c5022c3e4014d0b5a16342c68656856d0aaa051c1f401b00a78c47684cd0a6d0a9a1440316851d0bc6850d09ea0394123436b82c6046d095a174d095a1234246863686268616847d0b86846d0c0d0bed0bcd08aa075a11141e3421b8226042d08da161a10342db42cb4230d0bed079a0fb41e683cd076a0e940bb42cb818603ed069a159a0db41a6855685b341a6833d0b46832d062a051a1c1409b42cba249a1bd408b427381d6028d051a14da0a34156829d09ed050a09d403381e68456028d04da08b4263426b4253425342c9a08b4106820d092d03ea079404342bba275403b42b3a255d138a06d40334233cac26467c8cc90a591952123438646764636864c0c591832333230645fc8bc9081c9ba907121db42a6852c0b1916b22b64656456c8aa905121fb924d2193421685cc4b0685ec0999133232b226644cc896104b9075c99490252143423646164676848c4b66840c8cec8bcc8bac0859171911322e32216441c8b6c88090699165911d6558643fc87c90f520db41a683ec8a2c07190eb21b64566455645b321a6433c8b46432c8a8c8609065c95e904591b52063014845e60476042c0ccc081818d81798175811b02e30226c1e8826882158828d114110932062880afc4001100c139040027a6021e8982e7765d83a06112da94092284b82824892a4e308c493a0244b8a9600912409b7fc44080b2429fa21688809104f98244962c3c3e38141f2be58a2200c2c799204e575b14541444143519214055dc0c623c206152d292222624214f424c8c88dc7c53e81c22449d15090942750a0f8f0039402e4f086f0431228167872c48913280228fa21c889132802e8e1096131f0c31033de16fb4488a124440c0191844914fa448804103134001a0f084b1414648124440c054961e2240888d0d36231f0c31037bc2c7678582c51511011d1922451920439a9c092a228413ffcb004033f3880c7fb418fe703274e96fce0c4c91201f42088a20b3c0972c2830d0ab240122845498a82b80093a0293b9e0e96c809932545414024090ab2c00e0f070bc5024f98242162284808212296f470ef061bf4c313284f922c49c2240a0dfae1099403143d1102ca0f3f501b9e154040f92108881caf064b44445093244444440421790285091151900242af8a9dc2240a1d0a6a3204a5013c841e0d36a86809104c9c2c9192244888202110e003104117b8c0121f7a783358a2263f0405f1430c96a8e849d05050104f98dca3e209130ba0bc29f60726444f980c150d119484082751a22c4932140484132642ac9afc00c5c90ac7cbb244371e0a7649121caf048b81244b8a9600b1a428c800266f041960e264099322264f98cc784a6c931fa038a94092a022217e081a2228c6c3b2435004e07921d82750a208910408274c84a84092274b7e48c2240a2d7a1284810b0429008820208470f2c313264f16b0644808bac303c1120531296232042588222196c87849ec13284c8a960c0901449222284e8892308942970c09416776b82c64508c91c509e8b6ba3d3232a2dc8a361826024c041c8533194d269349472772e7f04aac9725b618cbd5b48e3b0cc330896dcbddc532d9d92663b2acf3e425fb9ad998b16ba6b3968cad5c6c33de666e8cbb71601b316c3199652d7baf2b36ef4a6ed6b869b028773dde6ed9516a51ca28b9939c3126637794d815802d46665994b21bcb64dc96b11bebed4d4a2fcb9a59669eecade5caedbd5a7a5df3769671b3646c25d69c71b327bd6cf3322fdb0df3b2de4dee26b965b7e4665e996198a6692c97a564c9383100c830acbd2ddb4cca2e4ace64b66d9994524acebef96158672da5a669dd1d19a3699949afcbe416935dd7c59c75632cb7cdba97e52ec771dc0d77d31c731c37c735c735f372dccd35d7dd1c87711c87450cc3b0b8ad35866198d61ac69dd5d4608be55053d3ac6158776b58478e8d615ad43a4c6b0de3d8bba4dda8f576b6bcddbc196fafc65d7fe0f72d7f4c62de355d5fd738607d35efb5d7455a6c1bc3b0edd2f676b6d862bdbd18d68c3586ed2ecf485ec6969b799b33ce564ac66878b398e5ee6e5ead35c91863dddd758c75cb53e3586ed981b26327b9376652c6ccbddb2dbb9b4863cdccdb3277866a790373044b33bbccccdd729bbb872cb36c1f8c5b4a660c5be665d912c3b65b8569cd9259322f7393b26e6ec9429825f7aeec65ac7136eb1966ce5a0bb2dd9d79ddbd4d6ad2c7ddbb4c026d905a2f5fabf52e6fb3a6691a336bdcdcecedeed7bccd1d57f62eb8bbcc58770d40b85bdbd9bc2137694d2b9b9b7bbbbb9731eed6b8a75b62dbccdc65bdcddbadc9dde5eeeeeee6edd696f43573dfd8b0bb4b18c61896358661d933eeb0ffc062e4c5b088e25d6c8b281a78987b31295993925993f2da24fbc8616e6e0c6b8c2396c55eacaf5d13194c69449a6d2286a06020080318b8402e00c549920b3c6172810b40716253fa698820209e3049f284c992207eb08005a04c1b9c3861d2a4e7c68e1e291ea91a3e6aecd8e191ea2901e18449103f24297222c4120b0401b133b4a488684789c8e6e6a6c90f4b2ae0c40994211e3b96040dddd8043549523444d0931b4117b091212a2262324410ce55f4c312a2274b868a921431c10006a0a46a6c0d4c2c103414659e86960439f901555a204f82a2445982a3a368c950d093a00be4a009ca0013a22416800265284a92a00b2c1982524464430d434436160811166032142425c90d408408a90188201c1c104144444143513000e58724434152a0fc103494640911103f042501d24425a486283f0411258152148588a11f7c726c0d4550a00c2501226828088821284954426a08623254c46388a020704e1628122249d1132643516c16080698143d116228c90f444c303083a86ca884d450435093254e2a7099213500493204942945492ef0844910444b82b8b141414316c091f224286a0b24c913284c888682a424c140922128454b7e80e2c4c99224454304dd90f22428a63c347a5bad8e6b492a545aad8b4a0b53b254a850a14285a92c152a58cc946cabd5a2125b5953914a5a54b845a5d52d956c8b0a152aad16a6645b54a82c95562b462a9b9216b75a2d564285a950a1c2545aad64a9b0921653e196a664a950a142a57545254b854a2b69f12a592a545a2dd9ca94b4980a634a5adce2d6a5a4c5db924a5adc6a25db6ab5a2926db1121d6217de5dc600d1920ddf100320f6eb1800e5e3aba4d965ab9666ff96c8d22cbbb744b2af4ea60525b8501cba474a58417b5a2d2fe52f2a345e5fa89c42314c18d9c4c97cb26060a971f2b1fd785830b0506c66e9a8d07a85d79305030bbd5e3340b10b7b02802a47f49a598aa0a2cd1d82c6f3bb9661fc7e10c8520495fdb2dabe598c6c80e0054d403194030a1a7061541465bfd02c463f94a08591140054391aa266805e1bd82fab6d1aed97153f3e8b11cd13aae0c0158c8c563b8ba0a24d23a992b346a1f1a6283c418a335431325aed6700197a3546458785f6f73b68dd0d97062d91a11e9a45beb55578fcca79cd2a47f0acae5f915925d1aa13eddfc43001562774df43eb89d6a5420f55ae9093e8543f0a583909daaf4b05da67c98371270c4fe4600539d8e2c5c868753dbe82b4573134923108810b36d0021facaa1cc1b38abf22b392558ee815f62b0158c55fe19500ac583dc49d7ee442fbd7595a7cc07e4dd64fdce96393a5c587eb71b2aed953f7e828be2e971543078890650b4ca0220a61c49000dc556b045ef840052ac8a24b0dc0b0da957ceda1fdfd7cb891d12a26d1aa1db42fc3186edca91a5dc642170715d9e2965aa294e162a6fe755ecc945d57765d97c4e9d4f5fe98ee11161afc34d04693d7d725901087a1f2d8c4e9d4256594dd46d86b4be4fabebb2247ed117bc41eb3c76b587cc41e63ec3019c82b5c0c3b6347c9336d8a9231c07e858c4dd9a9d5ae90a90cf7a882343e4a1ae34f1187c66b5e04de83ee11087e4097d4d100f23113bf084799e97e401d3776330f1df6aee30f887b77b94be4fbf5ee0b5bb476d9d724fbbe0b6b768e2649f708cb13542ecfd51bf6bd30fb1632ed6fa8dd0bb56f46f8fb9fd666dd6bef36acddf75df4b4b0bff336d1dea47b9f0bbbb8bd3380f7ae3bef0a11b5e8f73aa50c6608438996cb144d78807eb3765d77795ad8db6587795ad8dfcf08126fd621e8f66e67fdf515e1cefd276eb8943fa06e36e18a70dfc97dbf231add6613edfdaa21e96bfc6de152196eab6379846ebf3a759708f6fdd56161904e5d58a4a03d9dbabe5fc5a1d7e37e41781e4efb70bf9ec312f9f268bfeea35560a7b4edd7b9707b47bb4efb6828af7d4ebda3fd3a962ab406a1d7514b84e9d56d214ea7b447ba47585cc0613093bcf678ba472200436bf8d38eca1e6953543685380ccdbe4758a0a0eb859722d053c8dbc4e9949685d7f2ecf79358c83466a155a35d341267effa88ef3886952976f6a9c2bd8a96f8edfbaedbc23deaeeda8bde16ae17a3eb2b80fc7888349e0b77d5ddb54de309d01201d578e8593007f8a1dae387a44b347bc717a952c1c25e7f3bfc78f1dbe1876aefcfc8132a448566dfef885773bae8f54780d60aa8f62e7af22bd282820a29a1dabb6612a952818262eba3cb3ceccb348fd3b67057dde671e1aeb67057fd1de963b372f34652a5427779ea1e31018662e742700b6f64646464b43a221f67fc8486a8b201238ce810366bec8f07f9489bc45fdf6f87a5d914a28616d5506ceef2d4788e315c7a854df8324421a944d0fe522addc2fa3a43b3c200af5efd41595575283f3b020116aea1062075e128892f1b6e0bd1c12e00dc2325a2a06c44e3bbd2bedff9b0b7a103c0266c04bdde74bf08630565caef0f091b51ec1b4a914740ba8fdf2552e55147382aa75089ee5709b467caf786a15d7b1b3600e433d5b2e74fe84220bf7571c3a05302761d0de0fe24d341009460b798c9eb94ecd4e93bf4c0456263c8cb6bda469fed9d0ad27a8abd5b1ee0f59a9f56550d54d22aec396077c01314bb045a152916b70bc586506cbda0d8e57f3a0465500cc3300cfbea60b3c982692d7447b2cb77320c33f9eca7a5b6fda765c8543bf6edb5b5615a97855298ca500ad3ec1dcb307bd7fde5914d320364efe4857064b75df4649885cf5a9b5552edd8b330b69c553b3ffb4fcc8458b4b3ce5ad3a6d655764a7b7f45987a97a1d4e67e40deb3b06697025227d2c8085ab50b65691811864ad1a87619d6eff157d884fbf5ae935393f2425a950d184187a46074bb948c12b5e83665bc56d49474d214e23428f785454db97ba150a4dc441de1237a5d7e40d76328ff13b95fa194a6f15d37af8fad08d3eb5c28b3599966dfc2ca5b58316fdbb66f53fb4fc4b0482b518bf2e5bd59449ecf854b67cd6613398558b446cae72320edff741796f61451f27cf90e51f2fdec9d0cf91d7f3bfc64e7ae3dfe8af4b3cb23fd2c13e2304345de7e47a21c5142b56bfb26f2d93b2dac92cac76d0e092d1823235a1443b5c758e4518d87285c3824a403ede5912fc228f7da547e0b87b817614892d0ed3fad854239743b170ae9407979b65088c3d0ed5a5899ca6deefa90fd15b92ecf1f10366b8b6297a1a685fc2c5cbabfc2daf23fbd1fd050f4d86b4257bade5de152fe7e15d92d565de948f7ee1d7bdcd73be77d3bc785a74eedb7100cf7fcae0ba530e5500ad3aeebde4912cf9f6e827d035bd53dfb9ebf12d36e3fa0c54c60a7b8f750efdca2cc6165da04fbf625c2340bbf3c7bb0553d74df8560a756a31912ecdb9425eed7e5bdf3f700ed9c374fdba9c5ceefbafdb6595b74bb1772e8859503dab85925e5de9dc3d895a9f74a80161674d787372bf7c8854bb970296fdb429266ffe99feeb0b05ee76b218aa710d36ce26c6164447bf0d02cc4e9d41e0b2bd3ab95f175854ecad0016013f91db4d225527be8d5ba8e70f4baae57392b152a5f3703ec14e835b368df50fb1532e08712b208b3dacbb0b2a86ce264bfd0ecd82b157a7d1787dd1f75bd908215b48869369fec172a14c384a1d8cc5204950503cb4a9b46fbcaa2d7acf275a3f13f51863b68c79e16eef783c0828165b53b28156e5ddd61a45a15822ebd8415d1678b3085078cc4d88285175a582d1653d8820bbe700215472bf00ba0d7297e46f78b1294419982668015a4fc2a2f30360086b749d7add78a37dbddd29552ca90e97ef111e89aa0fb0dbf743c067f04408100927c61f99f96539b3af1025d11a69a00c04e953aa6e07e04685129678b7900b5a606275e468fdec8973162dcedb7c7e0bebf02b468ed37d92944857e40a0141dbfe3b008534d88e9326d31e340e20bdd2324b6a0b544e57bd0058303576849b62abe6390cfa0fc0ad0a22dcaef3006055dfc80aef6b655d86352707dcf20ff0a43698429d62a5411a6d8b1a951fe15325d1d399b8067e804c0df37e93387312960fee92dd26d11708f3e7005bde81e79a00b5d53b7d4a35d8941ecfd9ef1f2c2faf13fbdad4215e9c747ec7cd9aa7abd49bc3c865d6b556373418ffef44fb7981907dd52b02b6dabf6080933e85eb6aa1fbff75a250bd09297ef4a3f607c5f880a95424ce5dc4e710598b8c2bfde6d00c03dba72855e6f19d1aeb43a958de8f68d3e33ef6bd1e7f43e187d64deaf893e31ef3ffa98de4f451ff0fd9ee8537a7f48f421bddfed67e4faf718ce6be17599d04acdadc8dce6a5d0ca8e5be9f1d249a115d4adcc845670260bc7716e65e63a3f8556e2ad9c7ee331a1951e9365e33d6e25e63c0e86566a704c968ee3e078cd5938af99ac9b9db3fe1cb7021e09d641d51c7556ea289db350a7e1a6d04a8f1ebf11dec664ed4c560daff1798f9cf738abe63d262bdcb9cd64dd787896cd7bdc4a9cacf81d13891e1b3e1e274b67b2748e03c77be84c568fe388c771968de398ac9b9be3384be73ddfb1b3f31b938563b2789ee361f81b67e1f88dc9da11722067ed1cc864f9d031593a9e9343c3754c968fe700392be740268b861e61781aceea1144c77bfe23c744a2e7e6e63c39729c75e33926eb664890e823c654dca9e9a18303651386378e0327b573a363e306c7a62627e2c49a540f1d14ea46b8f31e386e727470d8f4b07163c3c6070e2a95f358b373836373a34688eac60e0e540cdf23654388901e38efb92aea5c9523448810214370e8e4f4dce73e3754aab35257a96a50a9bfe7435043767c7cceb2711f213e373670543aaa3864c859361f32e4c68e1021393df7b9a9b90f0e214284a8ceb25109d1c9410d49c50ff90d67d9f80d3e417cceba711f06e28343a5bad979cf85842772567822ac1a7216ea43264b85f31f67fd370001c2731f67d99ca7e73e349ce73c7ef4f80df7394be73e1c44c864d960c3834c96902067d97890c9b221c8814c960d40ee73a387c764d5c0e3ac9af398ac1d3bcee32c9bf75cc5c39323070f1ee7e1392b751e9eb350579dc859f144b8e786b3707e03d7f01f379cc859ff8fb370dcc786c9e2992c9efbf0c133593df7e1e3369c65e33e72dcc7593ae739101e3cfe83061a0ee43c7e9cc75938cee3ac1bdf719fb376eec34254931524c8854c968f90b3722e64b2820879cf6405f98f1f579d15bee7ac1e2732594386bc86c9f231593eae8367b278f0b88fc9e2b90e1f677d3ac0c764f1f0711e67d5f09cd9c3ee60bf01488ed330593c268bc769f86fcc1d720079cf59396e63feec0ef69cb36e9e3359436cdcc7861bdf31593676fc860dbf7156fc8dc9b291739ec9baf1dbf8cfe2796a2a893388ddc1ae9a2c21a90799ac9ec9eab90df1354c16909e68454e96161fe2e7e57bceda99353d93553341f0a7ffa0a1e63c93e5a3741e353c5e73568f99e231593af3a5cb7ce72c1c73889dc90a6794d48ec9c2711da1e938c239595ab2c78767a1a6b63bd89a3ef34f560ee9376c5841dde62c9c396477b0a326eb66f6ec0e7616e93137bdb3097795856c446bc25d6161d5be8f1f12fef5fe8ab4f62ba29dfb4fdb84355e13d23c0bbd6361f74bfb4ffce99fae3184fd3a4dc86aadb460ffc2ca46947bd785312800bd77dfdeb1e7856c44f733c086a1de6c929dbbb066137584cf9dcf4da196114521e167cfe6d2fd16c63001b2115d9acd841b77faa7508b3b7d99108c3bfd98b026eef44de1e34e1f0c5371a75f0a7be24e9f140e893bfdd5c9661336a28b830a4f2beb1e1974d9e9a56fdce1711e7ff4b1e2dd0a127c1bdfbe951adfb8d3e33d48b782047fe38e8ffb784df4b1529aac997ba5cbdccab7d1c74a8dc9024faab9951be7bed7a28f153959d29bac182f274b3ef3cef2262b4e24f8ac789c936e4566b24ea7cf4c96cc4b353e73d6cc649dc29fce9a79f7d269be07a38f956db2b6779f3b4d56ceb35fe79dfd7456ff345918763959dbe559a773b772bd66b262620e4e16a74d96f61adc5935ce4d96e79dc5dde62629df4d16867d9b2c6fb250df2ebdcbb3bacbc9c24c6761d7f17996fc9cac9898cb4c96ccb3ec3893158373994ce6f3acec73b270340fe72c2d9c48a44c9365ba8ccc6d4c323239ffb61d07b56ddca6d59039cd903c99b5b6cd789193c14a35b0aee38954ea5823939edc32190eeb664e33b14689846d9ae671329321757246eb4ea4ac636fa51a988c87c99cbca8b343725daac13d459a9b8e8ee9c4799a0e1d67f575e8d06666729e43c6e7e876ec386bfb8e1d35b2acf49cebe8b6ebc072e438abc673e8e4882519f99cef3891bee3a9b3669ed2a1e32cee3aa68ea7ced2301d1daec6739e0393798ed6d1d1d97156f61d93a5a343f29e9a1375567794f69cebc039ea33a9eb38eb741d3972e0f89cac1cffbc8eb3b6ff2c9913de64ead373ae8342fd3838b739ea479d453a2a5ee73b6795bec33d374f9d557a8ac33ff59db366feb3e275e4b8b9c9f98d8edf9c55e3377c16f79b4f1b9b1f67b2509385fa6f83739bb3badbf059d8e7739c259f8371e89c955d67b270e83c67b2701c07e739cef29e7396f69dc9dab1e3e164d94c96cd4da6df4c564dcd6d26ebe626d36dce92b9cd64d5d8bce6ac6efeec0e3f7cea1d78d464d54c56cd51efbae79cb53d67b2c03fe72c6e46d91d3ef8d459db1c6277f8a9c9da3191601ddce7646ddb63262be6dc3c7796361dc04dd6c6fd66b2b677dabbb36acc1e76877f7396ccdc6177f839264b47e638262b67b2723ec3c9ba8de74cd662a10a1a5e8449011896b8b2aa71840e3461043274e104568eb0b252e33f716767e6de981b77f636e6e3cefe34b5dde1e3a06afc66b26c665e33592d1ed8200b5cba484105183c58d568a160034c50428b25a0d16505ab1a67d598acd20469a61677f63373e38e8f5953744f9a3567793388dde19f264b6622c12b2bde6326ab74d344a26765857470b23cefa5c96abd208b19b2bce00c33b6e08515699b00c6062e500205676881c68a741669b2b2d9f3cd9ab8b3f7e6c69d1eb30ea17b3987f454b23bfc6eb2b889446a65457e9bac1c72d0032ed0c00c31c0c460a54d24beb2929385186e8005138e2043165656fad959d96461f3bbc3675d138c3b7b3937eef098b587eee34ced0e7fdf8a5476340d569618a37100f7a8034dd03da2d7bb52d7edd7b3897c9fc32acfef7e5a55bdd7530fadaadeac2c1b04c10b5fa0a1a19939cd6b8de73064c8a7d8d0210ff2aac3bdef07d02ad4c9fbd0c9c8f73eb4aad2ccaa813154610863989999b9f9cc2bcd0310804f29d1000409f2293634c86df8eafb1d5a753a9d4e9f72839e0ef4bd035a556766cd400bb25842166e6e6e6e7070705e7178fd61830d9f62436d38fdbe00adaaa7596fcc6a63d69c594f02f85e02adaa3fbfb98761181156ca10860c4354e1892a57686868664e632367d621a46001982666666666724eb36641188110ba70f33a7300bcd21ce7e6e6e6f586571e20403ec586027925f24a430d357c8a0dade1b5c6cd67cda2f881152e6059e8000a60b8c800431346b09133ab148314c6e8c2e775e638bcd21ce5e3f32925eaf33ae4d5474fcfa7d8d09ed71a910b3026b0417c811668dca077c0022e78c15e7042972b425e677ec32bcd55af35f80b26364002e6001547605a505c91052b4558f12589287ebccedc86579a0779ad914411b898800755b8c005ac25664002301938ba821745e0799d3990579ad3f05a6304618ec0821b4c70b1042b368608c1079698012ab3c11423e0418fd799a75e69eee3b5c60d36050caa3006ce8d592f1378390113376ccc2aaf504513d2b0819ab5c168418b2b765e67aee395e63c5e6ba06e666d1f9031022b3738b3320ba208c30a38376655c1400847b86163d622b4a8a00543c0f13a739dd71a3650b3b27260540230a06e66d50019a8106608352e4454e30667560d182561042e682e4444f31a3fe1dc983503590c21892dd4a85183e6355e4f0f5f677ee395e6f3b5468c8d5989c8028d16844143434303a266cd725005259880ba1011aa7433eb45052dc420e3e642443747fdb4e1cc2ac50803092a50a807b36e7c0e6d12af181380fc0e65b819cee36beb23dfaa1ed0277bbcb63e1848e54bb489d4b48d535eceae7b4ddcd9cf78a74f8614533281a0a9c42499efe4cdd474345c8dad46b3e96e3cd40eb331d485236dc41b43e89e3be71eb30004691f7b5f0b6bc299934c0c0de7794df7bcd7f7d5805180fc282429b6e4c5f8f3c2e5d947c918ed77248f4179d207a44df990ee65e903925749d27e077acc02f0b49abc8e718194f1b805209f590032ed9f32d239d4d8ebb88d448aa0ec1f5ca02520ae54d00db50908e07edfa23b3b2edc503498782419cbb55efa6dda02d61ed31630f338346d01f79d966197b69178a28ed42c4c983060a0cd8567ad00955b96678f81d1f1bbcaef1863eceed27ed1554923a9a7d7a94d498a558c44bbc676483c24a993215462a49ea82318ed37ed591b40b999c1d0550659431de1788e4b6cb4eb4e91fa3ffd9a668e523ecaa945d49148e399c6591d407bc62f1d073a1f6c418b9aee97a6ab4d2b33874da4449a8588f88deee783343292743f1fd8c868157dd8a35564cadd7c44b7cd1de5f515d9281460f7bbddb849b102945fb5731f907702570056f97d4072c81335453d6ed2768a9f002b48bfafc84655003e01568dc178f003aae914cfdaaadb85f6390560c7531c7e02dc7726d9bc34a4daa225dab326a13c23674be8b8b9e71d671619a453fbfdca7d3ddd479f3502172d608841088ee86203ab960cb2084307300823085dc458ed77afed5767990af2dbaaf5a972c490d5f72bdacac6156df5a556df90d5b76df4d91a4a3acf2c365876007440944b250eb42fbf114c7c05b1a73a25df31a805c9a153f2da1cf2ab06a1527e87e873a3321690afa05ef70c9c5736ca795d2e355eb74bcdeb7ab1f9cdbb9c7057376ae074d1475ed69a6cf6f84851799d3fa01f930f3f30a3534b771840a7e45113fcf560ca4cf2594e68fa8d10ec42a6a4504b02765525be5ff31af7a9c2010eb3d272995eaf0054b9326465fa11da07802b35ab7e35e57025b5ea5719ba27ba974bc4333d8677e46fdc50f96e3f900b901fbd9c30fe46d8ef8f3b49889b282e5b6a0a57bc23df7dddf7eb81748f14fec41df92e092aeff110407e9523be5ab6a1925848dd2ea83c7f4548d49b3fcb23cf4238faaeeb9dd0f56e1a59aadd0ac84771b38718fb442d1be18ffc15f650a5cacd6b5ec5e635defd2c111235bd43f05bd843a7e4390860a4df6ddc01df6d4cd4751bdf950348475d138500d36d9c01a6db984568f1a10fbe8878d31900dec66dcc22fae077c5540b13c05f0230540fa6276057a6332092910526c2acc0778d0a777513eeca26dc55cd529cb0872a564aa7f94fcffc446b84bb628a13968e0a697e13cedc263cbd2694798d30e608d8551599c7fca7ab980e1e043b05863bc41d7913172a1fd385cacb78a1f2a732a8fc4c284f235fa209773513eeea14ee4a26dc554cb82b53b82b30dc55693fa01e7ee2ce07c09a4ec95ff3d429296f780b904f0abf7023030164f9d59132adad413174bdd3d78670911a5293e242d767879f9ff5e9e9599f7f7dc0b8c33e1bb92c17364207762af521945f7be82e1794b726fa3c157d7a7627faf04a5e5876500bb3d0db141f0bb54df16e6a8f7a0e523985a2d0a6422cda5452497b66d14e9d8a07a3cfceed9d187dbeeddf88c7c0aeb417464ed017e10c69d0d6a1fdcb8b5d941d5f6a916793f86b97085f765758f9174f2fc6eedd66e5cb63b3f2bbff44166251ec5d58af677c226c3691e7cbee0ab3103402b7576fbbbe23d7b759afebfc26f1dafb03e250865ea7fae0e9c28e615dfc8e5cc7666d127f3589c7c2ebfcc94be95de1528fbff8fd64a8e9343685aee346ad1a7b9f017b9e45f0f73d8bd0e2037f5f441ffb2e86edeefbf143e23e48a7624ff4c1e6b6f67c1f96274b8b9c3e60efc9e26367f5f7ad558c31f6742ad61c687c87ed35b360e1ae7aa2cf7b273ec615f64ec518638ce7af872a7dceb2dd8aad65e956a77650809c179de2a253bc2700b32fe6f109c05eed0392df2ecf66f24c2f995139ab112aaf581e1180d93bcd63f00848fbae2bb26b03c3261f7e00a6534b6b878503137d382f5e3a2c4a683c83cbdb6b8df60a5eafdabebf84d829f61cda0493df66ad99d7dc63215746c879f980f627c6b0e8a2fb9f6613801d16caefb0b46a27c7a5537cce0bca6727c0ca7165744de5120959d27ddd735f3ac595d1297e0da0a4cc95d12aa6bc4b642f2bd60bf7852ba3550b24e7822b2f8b2e8e0bea88fcbe45a7e818a31d739e0eeda2f43af46877791d82b4c332afc3e5423bcdeb10b59d0242370bda6d9cd7610ced3aaf438d769ed76110da7d5c90b492d761d30ef43a3cd1cee4755843bb18afc31edac9781d5626cc5021b43b1d7199f13a441da992ded09d4e4e5cd0781dee16b4ab41337392f9bc6e71482b04dac52d9400b1631d1663f35ea428b70cba6fd16e9500fbfb152151210eb357a01b05da655236c56615a22dbd2deaaeaf0889c6d9b4c31d830a2b68c75e1a3486cd9b65596324d4918b625f5a99627388c6b963c0600b7a6df4421de92f94cf437768d77c7901134b00d6ab0cca472161322edac933817d479ad605640c4517e535d498b761f568ad4e964b0c64408bbacb341280f53adab8f008c0ec4318edba21d40b8c7a43177512832668516fd12d8d76a4a312155aa045ed05e5578e565e02ac35949700b377266fc39ddc0498bd45bb186fc3218e3a592e743b25e36d78ba1375648f1d9bb54b17cadf39246975b25c9c2c177a5d415bb43b791b321360f66e4f323126b044fabc991939139bb7a6a644b15993d0788e76dae66d181e81769db7610884769fb7a18976246fc38b76256fc39076a0b7e1d0458bae6b0bcaef4c197649199b7939a61de76db859d0ae43e980f29986b4e32524ed62f3c68d98e7edbbd3aa04d803ca8c05acd751dc8c745db11db76919767d32b6c74b2261cf2836ab111a3730941f86340beb75c5185378d962147d960a36c8a205540053061818adf8dd95e8d3802f64687125064720d183159f03c39d714d214febb5652907a655fb6ef3580460bfe33c066b803e7079b64f28005da0457ba6fd21f1e8a28e906846775623928b9d95e9a6e4d265309de2caa541292643ce8c4ef13930d187f3e2a5537c2f94bb48010d318a3006316cf1c0727922066788810355cc2007f2ccbfa0883e205f532cbdb0d0959f9148e5e3fb9d0c9b76ec5d9beaf777e4a2f1f19abb3c1d69ccc2451f0e4ceff059a3fc7160f895034399268c2e625839c216621066c53736c0c10978f0840778c0c48a7f6d893e4a9c188113a2f04215c69062c5bfae883e53ac408225b850410758d2587108409e4d3a28b6ced1d25ddfae55f2ab5119ca4e5dcfc2eb1d83bbda70a9167d50323e3b03fada2c427b3f9b456829227bfcae2a48f7deaf0f117db65fddb3702fead41525fa74930352d2a90bb5dabc7767c0766e16c17d7b378bd0e203f7ed4574f7beab2bcaa641af779bc733cbf79ff68a3a7545893e4a7ae7faf5558dae36259dbaaed7f542af83d76baeef75edeae6c61d6ef6f0d3fd6da7ae3308c0783d7bbc15eda81ee244618f6fad260a9b18d65a5d7d45320eb20374df1f10aa87f8a53e4432b2d084d1aabf1f0a01d9e37dc81e274b8b3c2b3ebb9c3e68efc9ea6bf228ecda779585286cf6b03dfbae3a06a374ead2422dee5cfc0170884e5dc76651a7ae5fe72a2067ad6e0d092f71c7b8c89857974ef59300ab768d41fb3fd18757fd1da28f0682758fc03d0273c6425fb909aabdee19f4958d288adfa21753ee0048f7678944da97672c911b34228047a0bd4b18da5f5768a72a3741fb5a7806ed676c4477883b7db0067c4d8ad6d30a2e18daaf295ac1d5864baf56cd533db45775480276f5d3fc0150eeaba45e645f5d74bb037c0fa8d3a9f6b946755e63e4c737897faa5539dedb6bc9e635877b25ddbcdee85e3fd4ab0defd5c379c5a1b176365e51a457eec6eb4de975cb79b5a1f1da12913fbdd680afd95f6b985eb1f09526e6f5a2f375e6facceb89cabc461caf3234be9708f61aaf269ad79825923d1e5c22358fb3e6b88e7739421da18e07c0d3a5f5f48892a8e3fd94fc8074cc05e373844be3db07eca7663de5a9133218cf488055d2530d4e3f6abbc3338255a4517c6b1a08d61a1abf712e781c337cce0d1b38a81b9b9a1a3433273041d0a20994c17899988bc69bc08cc697481c8dff3a1aef751f8de748346ae12e4f7c16f2f2c463612f4ffc15cae58997a1b63c71365865a56587f0451834076b3fe8fabb3ed8ab56419f8d3bd7e3f9d8ac5acdac3571474e1fedb3abde14cf9a03ed07a17db93ebb3b2a6c0a4a3c610b347ebfb84759e856a75e5ca4c9072c9ce8d452f0b47edfbfb8883efc2cd8b35cbf8ea20f36af2d9d42c9ef77c521763e03aeef2c6216a1c587d5b2c9ebd47574f97a79417b77e4b150c69925fb4a86d7169dea5fe1aeaea3e8736de99d7e36b777b6d07a1dd1feaeae6bcbb2c9072caa746ae969bdb6d0e5020a33a071061728018b12562d23295e70851a58e183207c59b5ac88c00a3a18c2162ebed062f5630632988110c2a001129c58f51510255f8496f822f8d88bd0e2c3f57d11d8f928f922f6d78bb8be47c98942001fbb0f7cec71fab0bfde2b5ef1d743152b7cec56f6d7b903b42fbb2c111c687f485fd78e402e68efce461f6c6edc915798face47804bb9de0ad8ef83f7010ba34e2d4d75aa1fa74e15b0cf1c00b9a00327c01082097cf00530abbeb8d0c54125b662635858406bd52f22de4a9c1dd061d5ef49a539e7480916340b31dd5fe978656d55271c8d97ef8f87ebd7cc825d7e469666b9f21536a9702b2df1f143b251396b0f749767a3cdb42b03f5ac2dda1b497501b4b4bc44965bb1bb894e1a808da8fc78002936a38c51ce1899896ebf2d54de83824a1ce4371218d1edda470086ca7f24c042e5b577a520745f73a05bfaf7fd698f739d5679dfa75ab57ddf33a4555b28af2b5e19c7854b334dbbb6b4a4fda75148ba63f25d47fa7e06e83a8ed43d7b77795f7a79b348f6eb17f72c647a61bf6665da79def6f120efcd26d8ab3c518b6ed73e1e885a947b93edf2ddacdbb97961e16e2d8a85dfbdb0854272fdfbf5efdcac2dca9542ed5c93eff27a29ac52a54a95d28fd805c2bc3a6217488d749b75f5138558b414ee4a8b40315080dfe387447e7bfc8e7cd7266a7b77ecfd1df9266a9b48bc67f79e5dbef3ba269e01daf3985eb36ed7b8a3ae2fd5be85a86b16c1ae5d9b4bb96f94bbb62659dd34edf1d714e230d7230ff2daacd9b773ccf40a9952e116d75d13ddf200f967a2cbe93266022100aea49f47921253fa987a4b647777dfef8f07c97b0f6848eb2cfbae04eea0b27779b0cb7398bd1fbf22fc3db744b259cf5f11d64214926cf239ca57b77d0e65a71aeb637beee3a19bdcf2748685bb3cdd46d0bd26d8a91d54ce5367d1c5c75009c897348055a30d264106ed7bad0a999ee8eaf450b74248132441b147cd738012127ca12c7b70801355a22423a65a85c27ebd87eb574a4910adaa35cb45015ac57b54822de84539cac546eb92f1855eb4ae167cae559534824d2fc61eb1eff28b30ba4c755a25c46186f64518c5ce1f9224b48ff5977beefae059e3377e408c7d05387f45623c671aa0d85945144145a342b16fd75601fa4db459b122d823927e9c457018df71732389676c22e19a5665c78400e5d90c74c790e20a78da23121c512cbc28d8aac57ebdebaf006630c319ce40f70bb33ef658c861e87e46eba38d8ca0fc8e60b6a34e88697bbfde2111d36d36d926e01e9dc0095a9b5ee7708897e77a914685360cedbe5f11ef1cd7f164cad40b2bf6eb5bb8ef160779ef0453d0f8ed1c56bebcdeafebf6f8fe9020f1bebd931e7f46b2ee5be85d7e48b433cf5d1efe4f7b61edc9cbc3dfde85b529ef372945dcc2b8a5d0ed94fe16f45202bd8cfa039202360de81eb9c00aed15d03d82a20a3445f7080a1250be14306a41f7e88931286f41f7080558a80fba4727b8822e9126c2d1e51ff2a525b266a0f1bb3e18efc4ef972fb19f2c41589ea59fa43f5980b03f96ca597b562a544ad30b0c31e56f270eec3c6f897811660b2a54a25234ca8fc71887a54b6bd38ad1f82e7ad777a44b030bad46d2c07204a355082584f085172aa48432181550212a245a8ba4f0849a15d05a34c50a3a29d05af401295cb4122de1063d76c08556a22bb8503e7f473423a323f13bb26128fffa8e9c0ac93093b3327f48b0f7ac539041bf22f2d77f62d716cd7e8528249712a83c1636a9442d2a9f5d68c3840913d6566764d04b0a2b6898859712688739943f23973273cda24672da6fec57d8a427ea88c4260a897c76ecd7f92b229fcda26e8fb274a1454daf59fb52985ec7c2905e6148bba8eb2f186db45b000949a3b85ee4151719d897651cb80c32e8bed967577b8e71904674df655efc8a2c1999a6691ad69a16f94a91f8dd06bc507e7696efae70d2e68ec326d9190bef59c8f7c2f819c99064e76c8500f7480930947b7606807b94850bca51ef5d173dfe8c68dfce85757b932cacdcb37759883a228d8ce8774923ddef59d8f4fb4f67deb5cb537a3f0b492f8535935f91eca4f9fe8a6407ff13c11085243be9d949334421e168768e66f3667924f6ecfd0e8c3ebb92cfc253a7e44b27854b97a77acf3e1e38eafd6689c85f1f0fa8e591f7e60d8964848db298fed360f4314dd93bf2b234ab462bd8243bf79a7d0bd0c282e338ee3fdd498fbf22fc2c7e47b2734d48cb235f5a1e6944e5f2c822501a1941bf775bf7cdd27779b6d2495b5823ed4eea485bf72f1c2295bcff74c93ba9fbbc6f9d37777d74738b4bb7709f55c626d8a9f849fb5f674974100025dd2ebcd003088017dd2eb0e0b28b030cbd48e38c8e7b06fdc5599efe7ecb9375a8752afb7e211b592ac461b8436f79b23678cd04c8b7e1ebe127ee28a105f17a044dd4e8f084ecd091daf1543c767af8f0783ea0e5f14103f7784380f4d4e06dab96006ff07a891e01d84d803878bd440e1e5fd71580dd8d2b375e54c6b031a65778ed27c3489b12f1647843d3ed54878327c3cadb4c805adc690078f4030a80b700d84e074ff6122baf97e077dd36bd62bce2526fa3cf156d65ba00bc5d8e3691595d1922d33d31a1cc4d61ccafa456a6772bcf145ea9599962c22b5f99a200bc6d55bda83c472a4d8fa609c61d090454a4c876465a09902f3b231f50b7aaa28e30462fda1929b51031babbbb5952be3c27a1db567169c17ed797ec90bf5f4f2f457a7a227f8b60ef0e0143e2cebe93f13ad13ae40332a100ac1a2d827d46ba52115e1e3ef750c58a152deef0b5b8b3d967245c1e7eb83cdae61929e245a97d4652d54477fb8c34a9b668f61901c112028c64549efb1250028a81023cad1be5d74dbef3daf33ccfe3d548f233323b6e99e348d78d60d7e4b8a87531e684ae6390b9232e605bb6a0b11730745b3524c461e8f5a2ea615f2e362e269b9fb8d3e7ec0bc57e855c2836b177517007c0ca5eb25ca959d59cbf3000aebc0a9bc3959e554d006adec79c58223ad03e77617d86883b6d85cdc2aab2f79530b4c71006c861800c76ffe9293a05c451fca7b1f307744269bf796b85d2260a02a7d3dc8799df4c9616ed33d7a60fa7d39c66b248442081163354118591d1eaf4d6cae635be5dab11da68356cb69985e63472b217d8642e9a006aa7993045fbac0570573c051a60418a131819ad6ae24ebfa6565aa47094bb0d7f0620bdca3bf98e42827dfb36bfb40343a69c05b072144d640e1e0be0b90ae076eda04c184b60e908768511b40b77f9e6de9dc7e85477612edc9956e6c2bdb668696639459fd3642f4c31ee421b3b33c51807f012fc7ea56f21c63f402b3ab585bf74aa04ca3016c0989b2e2ff34eb62783cd98b0142eedb88c96973926fb32217fb902286f643f24580ec250d97d0b3924dfabdce59199594ea7ff347f699fd399b29732b07bf9f60e63ef1432175d006cf2165c0698430fb6a043ac0056ee01ed730f5ab54dfec247ad9233cbe9cce5c45cb8742ac634d90ae642fb5c05b0b62a6f3180e41fe0b789f10ff03f9d7959e802dd5465300c1581fc05da5ffee104dc20a47105ad816e10d2a842bbebc364507ad3aa51f02038ab138f96bebd2b854c05b0f4d27ffafa906c185aba0c398a5a44b753b262dfce5f11ec204fd1a97ee8c4a3a5c932f8029242294ccba852e588af6afc889e15cd678e7d48be893a72ec0a23ba4d2d601f909c58d8b4cb3cf6d2a92980dacc72ba17b297900bb90c4de3273ad59741969a3396e85333c14dc35e23dcb5a14a8a9d26dc0552318a7d26dc1582fd0ab34d61cfc26e53d887883ea5ddc1de39b406c5b6fe115f71192da4f70cf6c204b0b29721d9672ffc85cb686c0e5d14c3a6932b68515320ae24fa9ca6d73bfc3e51ee891989548a46af7759286f062ac312fdc2892050195eb46b8f0bb48bcd72e345e2282c4f3f93f40af7480b2a50196e943f20f6b23c7d9602c8edf39e2e189898a69142b717181adfedb903208ee46289a868d4a18c5dd7f43aa5752adedb6d8b02185324452f2e62183f01d62b8aab4ba7e239f6025690c61d68768602786d893e9ad6f5da7271e9543c3f01ac5714dcd4c22149ebc585c667a193219e13c02b0a7a45d1aa292e192c918b0c709b9a36a5eccbc3302ffb0a804d6d79f81718ba385a1a6333e856a7f6118d59560aab68d1ce812846469da6bde2d414ddda1339566d671fdef2f58972795d5829e42f5b488fa46b738b9785f21d06d87506f4fda7b332b4d02aee114c77d2ac6c4629beebbc2e5cfa5d1e180ea1ed79dbaa4b7a5fb8d48bb4fbbc2a5ab0ebddf60171374b3cbb92cc5b2b3093097b86947ac0cbbcf498d71e1ac19005ded458b47e22a61453d2669652696eefc4f09129e4426687b8137f455bd1296e02c8c597c25d31f37eddd43e231ed596479b594aef48dbaacde3baed9afb8e64b452a117cd4259a54aefc467a11666291d0c3910a503bd2c0c3fa0befc8c645d85c637d14e740acb10cc04b0b17413ed44abd603c35d31b89d9f006b8a6eef3a9494c3002b1ff5fbcbe6823d872edadb7492e5ca57e065408b9a9700563e3ae22d5ad54d742a3e9eb9681f70b613bd1323cd6663e954a4dc05acbda50a169476f98bc6b715adbaa255d763bfb5025f41baf5cc627a6b25f36ddbdeb34418689b0b0ea1a6180f864a007775d3650ebe6a7cd4a9e8058defc0705706a4d9cc02fea7b7e8546f017b4bdc2213ee0a0c3f305a92b0031e6031325ac5842c2d3e80374d168be0082118adc0f9b813bfb1b140117d7480b1c413a8d84017aa28c22abea7883ead2b27d8828c315c21892844b08a6f2cd1a7f3c011d030820eccf81284557c3b117d648ea0c51738c8c1152f3a58450c0450ac76a5a55fdf583a159f04b036960fe87a636955636915ea08a38ec8eb1795b47f5a9b1d5e94bf04801cc801c35fbfaf74953e475930609cb4ea9a710a1b28687c355176627db867c5de5da1931088209000ca782f07b1d0782fdb7f9ac13e7be9a69798b197382558458bf623bed2c279692c8d25eec4cf2e0bc3ec95bdd098cd6db2974ec52c0bf6ec622f5b687c6f69d5953d62b13bab5a6519c2a6965670952507bbb3b467cdaebdcb422ea3535f3a159756f6824486f6b90cec03d2e6827d7969d597f6e19e75086d2cd1878fe24efcf6aa55b0b1d0f8dd4c3e609144a7962ebd0cdd2cca2004ca47d16757dbac9a362b98cd5a53c407dde5c92af6ce527a5f33cb95af4a0fa27d4a8f07a255f251be14cadee93ef21a0707f1f25774b25d286bb428d223dd1176e004dd2eb14bd7ef4adc71bb6c978e728a2962949794f2c2aeebbab0cc0927300cc3b0acc46280555e41a356068dd189e84473d15c9c71064fc1537c402c0658645bee282f2cd3da48ff3243b75ce8f5eeddf655127db6f904d81f10a63d110618bf22910e5db60aa37b04b09e5e45cbc3053c296995f6eb40b42af67b5a257ffdd72f2d642b3a753d867c45c83de8d4f520edc3577ae73a0e8e8e8e0f1f4182d07a7ae55073a04aa24f91195ca553d79f002b1f15d9281f75ea7a0a4099a53a75e974ea323202785d7c446b7096c8d2ebd94d4c0a4027db856acf320986d43ad558a67d05c83e02b46896cdfe46f90bf0fad2bec75c343094baebd18bd3fb77a20158a54a46795d525e5ce3a4e0a6454c8b3bad6920586be4ac97a7f5751daba157cc302c4a958c1dd6a6dcb1f907df6bcda355d2965dd0ebb2555e17c31a68b73b207f0468d1386b53d411d4a95371468ec6e9c4a318ed22f746499273f9075fd2ad0bddea7801d6ddc20ccae72f652d11a6fca201ec50805635dde81e7400e380d11dc309a860b4aaeb85aed12a0dbae7228055a3bcdf42f7dbb9206fa13fad310e4bf91ce3b037ea8263947c052b41c9b7e8e932de3fee9d967da34fcc4ddf56954efab68ae633608dd79834e1766a669ec2ed94cc8c09b753a659190adaaf0937ee80b33296d2dc4e91e6372be8cdaa75b3d670b382dbd46636b179cdca5762649a965abad5a998118df724d8718859d4a9c84980ad9ae8c3abf8ee925f30345ee8519688a4f1d7144be4061abf68b0118c048def2e4f8692f607e4a453f1d704a2531109d35873e22a98518bb185f2bd4ef1a3e814cb80a7e8147f871ea24f4ddce16b20585373e626aef3bb2be4c7104bbcbc9cfca535d1a787b8c30719aca9a172156b987987b8c357cfd3043be5f116f9a55b9dca47b177bfa0fdbad13daf0fd0eef449b46390d90aee014fbea20b5b41fb0e50d2a54b972e50548e227b6527683f9301ed33145814b4cf2146af39056d255dba74e972510774613ec25a4aa1db1d20d32d94f842f7880763d022d03d02c215f4d42a795d0646347e0c1a675153216a688d626464448b6a68896e35d157120ea6fd01528feebe93024aa9a191db88a47156ee382bf6f80b9b4491325d244d99c65f7d9dafd984a9a44d98cacb4ed12cdb3cc516e5ac63986e01add575d40110c865194b0407caef592224cabc5950fe0d2c753ac53e66aa873f24fa706736aa7b86f7ba657caf0ba63b17db99e3f4b42fac72754fa77848904ef554894d0470cf85a81eba8ee3b8b31570bfef3f1dbfb08d283f66c7b66b0ad8d5f59fe6217187b570e3c6c7f819df8b3bdc1377f8d42906afad253b2343b73aa8888a138580aee38ecd22b073d8b91791bdfbae503d6447256057d9510cc09030e5f30714cf3d0695e7eece19f97ad0b4b823b9bf1eaaa0e25157ccbe9d0118f60d7b11bb02a2533204e533b95a65ab3909a253f2555b3058ab5b3ea149d9b2a38c12884f424698f68f70b4af0fa827d8298961b119d3bcd075dbdcac814eb6684a7e77e557bec96a9dc2a4bc24b6cda579b7638c314a23ae48238d2964eca8826ed332ec92dd732f2d24c76e13f06eac751631795d526ea8231cd1104646464654cb628c1293cbb3f20732fea033eff107209f774b8392aa745c4515ddbd85eebdd07d16b1d03dd647747f498682ee631555d41a74bc03adbba1a0fb0c0cdd6347747fc9b80390df3be8eeee65461dc962d0e303b0d6d0aec1b90eaa87ed4b391966e9ae459f6c563962c86a7b37af682beedac66959b2c7b04641c5b3f66845447622b4db405552c6fee3513b7bd01e670276a5cd8d3b54aed6d5b20835357fce05768c60496a598c327694524629a5945276dd1d27eb40ca6b0aba456e74a1fb9fde6020a594584dcd9f7345b024a59645197be680b178bea49432a3712e632159aba2e3362dc32e191be452bf7bb98e9445a125c718399a2064292e2fcb962766d98c52caaba3c4f68a987ca472d629f4c2a494bb9ed4564a0c0bddd6031ec41863dcdd0eb14b6eea749219c2dddb4397ad30d22d4785384c9187836e23006e172b7082f2104ae005c5423b078f2ee3a0bd32f39bbffc83999959fb3565a7b450d3348c997972b70cab138ff2f7044ea8bc2655da2bd6d81bc3e666929b434d36cfaa512c9b55a37b4d8ef237da447372a2ccfdead108832eee8d60dd492f46da5d9ef42eec2b125b88c3502c749ad4c115af00f9db8fd1687958723442b743478eeeeec631c3e7dcb08183bab1a9a941e313dfadc59deeee934c8c092c913eafe3362dc32e199b773745e29629a3eb9a9a3fe7aa31bea5e6c3bba3659137b597b1670eb8d431e66031dafdcebae85676062e1c5cacc5c8e242f706710bddb7e418e3c61d743600f9d5bbae5953f4baa6739c13d637819cf5ba9ca4e44f1928a59432c6958f534a19e5253962e15eefa43c662485b194f29252ca9304a594524a29a5c41a40352c98c1b8f3836988717968dddd8d31c618d90795e7e5e971f1c076b218638cd2a36207b77d40fb8d3f56441db4c618235701f273ac0eadbbbb1e0e5a638c11cbaef30cab31418c314649f25eb34ff580f1d7ab94f132be898c570c698d31c692c736883146d0e318802c00a5c98b33c714734326c6c649e603da2fcea9eeeeced418639451d49035357577f7c68606b5df18638ca8babb8bc3653d2a34191b5eea3d436a62726e989e03dee3e5893528cd50c707489a5e4d0d628c31e2f0a200efe9c41863ccb1bb3ab61d5ed4525e94d7b3dd2ab56e671fbf977f932e72470febe1edb7fb9865f73c228eced5c393d2c9102aa5f4e1c57bcce36d517c8a5404290d3cfd83068eb1eb8931fef062ecf941038f8f1e3c76523b74e4d0c131c3e7dcb0d183f10d45e8e41594af9541f9597482f2b1e682f2af33285ff214941ff9cd5580ddbb5beae6f746da6734e838065ac7e035dc5dea591d40af6c01bb58c115f0d43ebb6aa395f66ebf78455b69ef2cdaeb1e7bb7a1b61b66435079bdc6631b632c6a2a677619aa9232760c130687f8babb3bc474c3845965f1454db364df8f013aacb20886ee63af7777777757f24a5e9eddaec678b5184ba525b26524d212d1b4ef5b22d9e6794b04e33ada44ae8ee39688f4b66d89c44fa34da44959b644b862d8d61863cc30adeeee6e7577376e948f31c618bbdddddddddddd5dcf051dc7cb7731c6b7a99b99c832b83c7bee2e5dc75a800d0583c1b83414608e188b16ac05b8ef36d334d214ba8d5b687c17312ae74ffb905080f23b823e8325730bba8e3bc5f1f3427e02e41418b12661186317097504a3fca63c6b03688ba0eb4a0cca2143b03d3a4ea738ceeb3e20f69a243bef034be0694f25197e42e63473024f383520cd0724754ee009a706ac7902d47a7b0a8d67d0a371caeee606f50181db25053538fb01d960158035752367088de727c0585394b753e15764a39f73e6dcb081839af3c6a6a6c6a49939cd3965668c69ce39e70f92cc966eb9a3bc4ad811987d4698728ba7d4345273e9f8d7f9ea5587417a85317f25999999f97bc45fe83a0f4863ade307f40179401a26b1c87b43aa2dca24e8765552f6ac3586ab9232760c1306876eda67da6488e9860943fbfbda6f1d68cf4ea384a24d831b9b58236bd060dc4c76d2643692d79c3c1a1263eac0d247921fd8938a3bab79606792dca6653206f4309a8b47eaa6e6c46954e2b099cbc33e6c17cb058d33905cfcf40c9a511cada4e51fd5a3aba570503736353835b01c40a7628c1d37e3b0e21f5a403e2f4f63f2e2d131fe3419390c39b5dc02884ef1b5598498944de5ac4234ce9d2849544a4fc666179ca44c494f7a5a4aa7c747a7f698c73d390c69d6bc2836b627658aea746acfdf2ecfbc649451c618638c35151be3378d6f1a675d003d5623f368366fbf5d2743e87e67b61327d3d5c4783aa9ef271cd307926400722906356f92d7a9cf84b3c3102a869c6a3ae5c100e48335344a1c9a8edb7098ca79d2366db31a5814bb93ba3639c5234a6b0dc7d3a1fbd614345e869c05e44b27db257a9daa279602e44b8d5f00f2f735684a54e6641ef30dcdd356155ff7b2534d7669368f37aef338f3b08b548acddbc9784ae815a44fa8e4e956c863a19c441d63caccd08c268004731540403824180e48e371a249de0714801198a2484c9c0aa3b1200b72180521630c21c01003084286c018e10600791dc89d2509890a5cff6e97debd4071d54cd3e3cd9430493078d579552cc9a67f2718ef8071f81e77092537b35aab78c60d0a77b0014e52538ce35525e533b00c8754df5f03e9852e377e773bd9905ac41f28848402f14cf84335bf94e4608212b263c09eff2047502b2b894193fbbb47c596116c8ddb7f67b45398a242407eccd60b973c5dc294648e9ca080671e1adf5b0760b0b13f336e06e681819ad10c41ea831a9ecc3394a504310ee2b105185987f52b8879f98b82450385073df563387cc1ade3ef79d3e3532b8a74b616af185ced04b1628b4d7104d2db5dc4e7ba7d36ab9d9064072ee4fa1538ba191e27f0ab6f3778f48c477d532424444ea7579f4a73e562d5e24bf6d1509d1ced29dd4a1b4a688a01a4fb3b349b96e8bf674584ee93e4fc08f395e26b84816e917db2749fabdc13f35fa44ed13537a6ef564fee6063e921afd15de1bf32a1f17c3a1e30790d366425aeffc2f8e091929531ad42679c9471df5a00a8b97631cb46fa2382986dd4e9f3ef758e11f9fde940e32f60c68c632950dfb9281a3cd2cd288aeece0d5cecf0685357bbbf2d8d9269caf5ad057a455d7c3b86e329d10f26125a570a7fad0c0c4a9d776dd545ff1d83f51ecf9d51c0505970ef7b583a4b43c3177971f501669c4f5fae0b01a4e95b2131394f0731e725d371578c1e09ddc51958037e0ceb40e93ed11c43d98471092ea4eb0fd16acd502d3d08d42814c908c3a2f40491e7946d4a7b70075caa1e9c73a5e115404165edb883a45a22a6114d2a14af8a6edaae2096d57f7874d0ed88db8614d8735036e1a0eaa0a9f3fd334f643883f08ec353121005c92cfbc42d24774a4d281cd79a220f10b557932cb53f55d782b6e1a04d6cd34cd944a4d54ca1741500cc09ab09ae27e4aabda847571b5afd2e79b6ef3be082e06e4aab99da1f6698c4cfbfb3b074cf5dd1e556364259c157df24098747af98dc69bce17e32e01f562c5c5bcf477404756ac7b95c26da6f1ef3c008bdf5dd80c36eb1709018272ac06fc390b6f2466f29578d3c85ae0686922d3e68b448429b45a40a3004ccd7f54d1baed2fa5e035c99602dd73e67b9ce84b69b483dcd3b017444585390b9294e54904a6c99000b519f376ec1d4685684c265c8c5211effa9c6ec34b048a2ba10de4cd581c5cb7c7b49b822414f5d41f7a3f89ea5985bedfb9de4d76d8dd3a94c2b5be04e0981c0d6b1a71809d7650f6719127eac39b8b708da6dc63a26da5221a8a4a10955cb2d73734bd192485989a2c3d22b665a49461c3298dab3662c4fdb73cb4fbcdd80cf81404324bbd1793d826d5804783bb65ef891426c6b24a90990ab249c213dc48418b02f85bb130a748f3787031a970bc0c7a36b02a544c3485d834d6faf5461d1029aeff1365f1e193c1b6c4813f741a2f0dcd182c01e3c9d19da6b1e314cf37dc75a9629e813360d40570101772aef5954302f9358048d850c0e2952420e0742506c41a15ec1e5aa6a8d26588a739ca71dacd851b00c3f397dc086057716a27e461cc91bc4c30b5788251fa0858324b9f820f1f717ff8b6a6666fdb00dc2e0acd1926ee8f8fcc425985b52dd5a93682640885aebbf93293e07bb07f453f7a927b7fb5fdd60b4d09aa24f030ce91fa3c3831be59c71638ee70f6e7869931b0f21b9de597380e6bdfacfa84e13b39485a00842a1c7370f5cd064bfef54278dd993fee22dceee9298697f9059b4930ca3f4b650b9421596c5361dc5985c5c12ccb34070fd426bb04f929254988e0022830a2300e9a8c4c46074b46f5f43e7a27203e7d5b781c37230b749bf97b04c7d6e4c736575a64ac80dcf7a921161b5d2cb42c798e7c8d23d86bc8273016b48947a1d225d4508aa8ab84668fd07bc8353be35bd184f9ea1dc3918eb5947a67a52fc4ec789da931d42efbb1e3b54dc54f946e8d33e608989529533413045510261d2e890dbc1e22f89e01d04bf54229ce8f37f7e0b51c1fc114d13b510c351c80cda5a43c16a1304f24b1684702e60bf9186d884504131d0131741f2dad11bce06adffbd5c57fc3e26845a7c9bdd66a5a7ed48b31e8b57cd6b96427572d45fded05e44c866ce5575ddcc212936e8a675a94ecbf3572c8c6d53bfde23bbad71a5a6e781e541646b0551921b8be3fd6f1f7a2d03e8ef15afdc51cd310d6d514e9dff144e7cd4a6f80e9fe21fb99f5b92603feae1b5c802f615fe2fa8a30eab191138740ac02488e90f295ceb82eecf5b1b6bfcd15b1224a344f12c4cae34c493ff4241175d173c7864363789dc09da143544a61e9d595adf92451f99d2df886024dc03172dd9853a7ace9644ceeeba82f42a62508327975680f179a50bc168e8400d779a8792ea15f48c4fa52587f9914c1c64d3385f23dd5dfe60d19c838508321412f4cc0ab67c9fe297ba7e311eb7a081782e5ff77cb99c4c8ab403479ca136ac42276ac27de0fbd23f392e5e3f24185fb6cda3f05212cdb0a65246f108583051bf405f215c7d39426596eca0c6ab7d29572d08fe1a01f9af23e9536895ddff46e6b7baf73fe370aa317d8ff9058d9c1c4d1706bb782ca35c9e1c142745d85e368c4b50ef753c11955e6282012e0e10225308e4bdb846f2196906a6a2bce412ae3b8c85f219ccf14ce077aa9d1dab51cfddb17e44cc39861ee61c0edd45f9d4927b0e2edd13a22f44e005c59166096e34dc0f32d1af67db0c0c35711f5b88ba8a4c4b1bca259c4ec6a7facfe396d76d3d6383eccd9f06a3b8de6c1cfbf60bc7b301af1ee8fedcd9c01471155522f3389583c62768eaf8ff2cf8e08bf1bf886d7578f81afd1e2868a71bac6903b52b984f7aafc46db05407692973488d2a09e667a0174ac312def493cfffdd446fa11a5e3ce98a3a90302273e2128051ce1345ab2e3e42bb975c59eaf8dd4164090a24320a59d05d4b46ff7aef0a88c2e5b87c417fff48d2176f18abc4c08f32f091bdbd5c50e1a801bc4807bf9092457992acbc833f215fa744d116b69164e67d5688993cb7919ac12df8954887c31c1853d3ebbc418ace4108fc16e81d7490cd3078f3d567079134251bbc32c912107bc4b08fd15256bc04ca90ac92b147c15a84fb22f2588ff4319b695e5f233a331bfa63db43144619f4a8be87139de9fb94d747aebde1c7e1a96cca21275abda4756934c36e1601071c8e41b235b1080aa4c38e85eecffb930d4459be65ebfe760f6a77a8beaded5eff74e30b09d025cd97d07533acb25b6909f4518fca43341a32f0efb05e2cdefe178a75a2d01e4e27a2e64b3d7f9aa44adc4dd2f31c1c14b7744d2e3525cc2582ba4b44bdb496b64338486aab537b99749298f467423e79d5c04042528f01ac8ea7eaa502a2c0ee83d32742fd812788afa5ae6eb6753286beb8befafe328b349c107147e4b21d6d6950aad82775611a1a6d99f9b0c09fa235f9b0cccf833d1a706734d0934d964513c12b85f882aeb661e72a879fa6929fad2bdf908657e618e1fb900f9ce0d9c2585c0589ad1aa88d83387f94a0c753327b73c8196bd9fc6940e2773ed439ea4776c3e7c4f4602fe7e7eb7bac0a1203b204448e78e579d063061c1b27e02b7a61ff4b8853942d4f8936f1cdefbafcaeaa177b5d90f8adf1c3a3962c4a0c686b298a873c09c398658621e889baa6066f8b27a80a3a9ae6a0707aa34f2fb82b34fdd5717d34fb8925d1af1944c7cf90d2b7db53c66457b055473bf86a9b44e7bd7a1908aeb7b06fbd85fdad577acea0ca2eadf0673279f956bcc8dabc4ad67eb5c72acca3628444ae88174e9fb0343711a83995b7745aaea61b26d6b8f13b934a19180c12e8dcf25502ca1dac0c89335a59b4a19c4089ff329f88b9a46864f33d7b1056f21c8bb055b3bcea13214324d91c8ed854f33c3fd00561e91a7be3f5f61402957a425460892211dc74ea7114518b81bbd25e887b292b175c6f0d4532c24c005a1e0d6306805f90ad5dd263dc39bb64ad100d97d1eae8c09ef41dcf883c81c1033449cd8df3937d9c7d5ab21f67e4fd5885140b67add9d5972f0ba7b5f654ccaaacc2351293bd2ea044260615e3c75a03444e1bc024f9ae29d25a19564919cd91821474e52c79bb0eea29540b44ea9b127998b23b3ee024378173494b9744137831077539b9d2788af1401f74f1f01e3bd0a0127aba22f0b0827615970bdd49440d290e33dac44e5aac5e11727b7a4990e96e20d47822b41ba68daddca82e629a3bac20bdf518353330f846b2627e3251e8b1d00f0246d43f5750c63efe8b4614108285c249d37c5701d7e00f3d393880f4bffcd854e25dc8a9a0f380f018f240e286e1a196e0b301898182a94df40c3b3ccd9d26621d561ff1440af57a0c6f3e1c86ffb883d70f67e8ea164ba4dadb0dd56634738cb89c8f9f1fdaacb34cab97a14ce9407aab422e1510c3907d3d527d3811c23162a25f108ab0b3ce6f101bf6678a6569dd7b5859e1634641a5351990cd641496bdd85dd201aa8c05a6e3e52f4f70edd892141bedd9df0e45d9b02c491094d1862a878c818af03080973d347c389b321399f1bfdaca36c9899aed2c7844e45806f9d486d0be34f41156a0886c3d192229b66a7d0db8381108286ea91bca4038438e6e967c3e3a71fa0c1668a1f108ee9f233c0c8ff2227ff535de05f20d8a8a73aed649df16fce3bacda616e4ef9e850f8c798763bb5ce555fa50af7fd902dfa6dcd8c8671a5159bd1e40a2dba6b19fe351cfa7461f388b28c688a22898b453796b0409630035ff9c35b4685b10de730744e4ead1e79ff4630813b22a1c96039cdbf0d203589ed3829f2ff407ccb8997bf0b9efdc7406dd962267589467e6190613367d7355a604d01032bb0373ef0b75ceb67fccea1e51057b5980614bcb90b9442bd13eb1f22a0bc3efa60441c46de3a95463bdd12096cc413c027bc9dbc313fda3e5d4c16dda9e3ecec86f6534c9d5d152de6beee95193c5e7678290c4f7f72c5d8154186b693f0148a34a851054dd284d48e173c7b8c91106f1c76529b8a337b849826cfdf31598518a546af68fcd9809c1b99269616d10f12e8106a3f1c8c184be42e8cc82d4ea674e33294ac1f23e82418606f4faa4420949b839e0b1f11351bac82564cbf9fb24c5dfe9b0e5e7d53aeb2a06c154baf852e958b2545b1913ad590c3d9942b7de86acff8f652ff56ab86f59f525a707e97b41351447c54a7150aaddb958d3344ac2be97626aeb29d4f0c0c1d704601356c5d918eccc78208371cd93f0de7d51db060403efca47bc1f0b2fefa3f940e3886108ceb46c284028d8179a221cdfa7ce465256511a47dcf796ffd35e39a4c1eca114c405f455342f8b9ffd40b326e94d1cfa1d7e8896c23426134c9f79d478f9ecc241eeca4a886af66f7bdab898b8bf4bbcb66fdcc07a69425ebb60fbf3c3ab734f3b090464787ac1f607a4e0f6fd2d2c230e0b13d849ce04ddb6db8b4a06544271f8f42d5329e5033ec8c785a7f8199e23568bc0fb0d28d7f686a6cadfca326e68354eda74d9bb8843d0cc0c92f1d4023c0feb08dc56a74384c8ad6ec846397509c9ef7f3dc990d2a9e279bad4401e191fbf2c3c69f8f17c190164ece18cbca632f465997065a7643beeae1fc29b9b3404ee889beaf231800bc2d3f34ab89ae752912699e765582a53b03b4f697d2a79b9604ab3f011647a485c50c079bd1c943b3e9f7b0797e802f9334251accfb8fd9e0d703b6f4541b56b6e608c38b59c05dec585b990aa8f33ab3d49697fca8d908355fc4e5fdd6ef9911a079619a3882fd0bf1ca7e31bacf7023f945b3cd12794216ebe7f53fc95ff36627dcf7f4f4eabf5e389c6eda8165754d93f4aa1f1892dc2cf2baa3b0dccc7b3c5901b04f0100ab4dc3fa4ccc93c16324b9f9b8532e738a8cdae0a7c8b64e654be959de5715fcaea775ab894d1320a335de884eadd2c38ba0d0683dee422a4d9629996d2ededdee1e614232e9db0f5566a69548226140ade76a12c958f7f764467cf8b14a2711c31fd2fd9088e6a81adc96fe00844a5de21644cfcd2c317460509ccad2795712f473795a563c5d123c7e71173f075f37df0b75c4670fe46ccd6a8936c37141b2591ca0f136f651fba444ee333c496f094d1aebd8c05e3b73b93b57076d13737dfaad19365cbf9c7a0fccbf9ec74d2d53cee7ce0510643b2400b395c6ff0d9c96437321811b7b825bf25d926cc471fa225a802a08a969c19562dde88db321c92ee568df49ddae9b1e396aa9e6765f06a29504e5e9ba2fb0361bc245e9ba35d25c64f606bcb4237a19593bb63d034bdd68b23734f8c5332584917243ff3271fd4a69ce55c01232600d380059d24bb415fde00b0eafa7b5d0c9a8904e1d4ffc60d4e6ae393b514caf1b1a40c75a184455b113b078a40857f82f217b47828dcdf8273fe32abc397f4632e34af0eabd1a1d24af449c6955b605446f7b22fb19075c31b24ae52096fa2d623719aa65270504790e30086ef2baacc0fbbd5e4e57ad9ab156ad03b66503eb906ada4ea718f42139aa34c00f2afc5472f81f9317dc9cc919651750d751515971314f517f060eb84d67b1045f55fa1dd6fb91abd35b0016b0b76fc7014616fb4a1cf897a3fb4f67c5cb5f171ec6ca55226d0439c8f245fcf0ebe8d449e38f57a02de6d853d3fffbf22d969a4eb6af9a76552ea72d5a0cb47b4a35cbbf5a510ac0653098bf08b99729ad75200d59004ed6d287d12e1e3f2110734df6bb88e2e2ffaddb666a06a0e2bdd74e2c0de796562ecd3b96d89c5aa7a36b00732845c05019b3037285bbafa39a7dc25961b3734058896bbd9c21aff474ba076be2a01cc0981dfa224c5ca86feb11f90929a841ff12fa2de895c5cb4420143af693225053e62540bbb0a9fd3a2bdc29ee511461329e3dad933afac0a29dafe91f3b2dedfedd3abfad124cd36521992ebce8bcaed0f1a5e0cf2103998f92af73b2bd20496646f91de18a03d78873e090e005dae45db326d51099d2c6b6ad4ac78f6d5a391c00fd34320a3908461ed9b86a1cc0cf8dec400ea5f4f65b4bf3f91032c345782ca3b432bfd5e9c3708c4008148acd7de1281f98fd65880556e54a404ca1883795182999f6dff0a1d621e727d39514255a9edf5a6a06be0e1e6288d0496db677943db035b8e0cd4ef4c22da808c6f978903250338670e735874c79373ced9c661f25ed11b58a3863821ebadf7c8c0618856d2d26f95aacc81f37a7e4e9335f0d65891f9f989bd3f1e3daddf58017b35a5a0a544f295c1c6ed25e337a492608370d28ad6c089df94f306c1cd129c0d8813f9c8717214667edf73e8aec0a715afe279f492e64322bae3c066e6eb21ba19541d33d382a82934064096fff511889bfe969cebfeb9f4a897c32b4867533a18c76b253a38850aec251432576ff6dde0ee9549ae9041b23b5c2d00b00dceaa151e8410e46a45004b403cb4f526038032589e7846ee5b50b42a4897354bf9d968588ea8ac63f5bab12096588dbe9e1765fca76d534ee1f59f4a575db62bdd28405e9a744126a1ed28647e176628aadc770abaa6255f1f3f60ae1d659ab31a65d86d76dde97e17e9e67a0b16a0d92652c4d02407a82b626f7c9f50e73b1c5fba9565b6f72ce0e6d24a5d1628f59958e07ef48d90cd81dc1dd967501151a97e4ac25d69f5049a5669cf3f5f355b6f63a559e350321bfd0cfe33b4b529e76c40a1208d678be3b0b6b99a5f72f5c9c6e9664a8b52032e0376be136647b2785a0b7ee8009fe4509d910582d95c806b9a229961747d3498fdce57dcb7a18b9916797091384810208571a9b17049a4050adea81cc324f10853c68770be025bf91441c32a1fbc6572a19817bb722102818cf02ef2b88062ee46e8191c7f66a0715e08d5a582c3f234c992334233e298672534e7baa1364db69e817c5c15c76787aa21c0b3ca5e18ae14041e8ce9e2f2c9b8dd4bba858a96b175f55b6707663ab5fb771d05819f20340a13131c2c466b7b2981d04338d6dd4b66d562bf46da1778c42a70a2ad01627a300408db584fb35e14af3eb1c91d4326ee291964e5ecaba0d98591f30f15f93266a3a5bf6d5bfdf7f4637d0f79f3f093f86220dd11bbc9750868dbb2bde695902303aa69f8af827af1ebddda42c105505272d109fb1247d0f830534dafa429f534a27b760dab4aac5f42863bd23575ae302409ffdbe9f70acf57602f7316b66261f3d877ece80e13c999d61c5187ac216f46e73a5322a0be706125abea7fe2e087868eda3f804d9591c1f03afc6c311fd507dbb47246c35edd49fd72b80f0cfff383c8d01a53906721afbb5cc4c2afdec2182301ed818aa3520bbadbb344b3325712340875319cda665ecfcab8a310fbc1f25dfd5aa2cbad8e2fba01b2cbb661ef7e02d71f24a5b059bc2b37c81a6d8cccb712d3081ee64284cf56454514d8cf03e4b162796b9d9d6559960911fbc18ebb02e0be393f6e32d05eb60c23d4e2028cdc32b4af08da922578fbfad6c8717d00de6a7a963b1c777e7725e81b6bebf9fba6646885bbe143ec15a6ffbcc516d03fdeb5f40b07996b5adac4b781a3a03b987931e21a529953526211870a7019378dd4fc0f42ab9db2d0fd270ab72db3dd4455193677625f2eb7b6bd123267ad2233f5c39adfbad0d88538fdc94bbc660ffd184d3d3ec08efee158bfa53e4238992301c5bd5224471f7ce28e51430885986d112b6ad4f91a1265d042dab6de76444b7d26515e55f2d8a5a2abe24cb6a810450ddec558957de7f89ebe5553883662a8425fc180f8f922f7bd5992a7574c2755982749626ca7e13a3dd26120ead3bcb2f8df17d707d29408a022e4a8b89cd3d53cb7ab7786c5968b59f12b252c7fe2fcf120cbaa2aa9fc441cd2007e1e18459e664b202ec9fb863b701a10ca1b03ee0779c19903d323e15c02cd52fd12ac28c1e62f197992265e0c4f6d7645d35c055a39b70b9a43e6d6acf85799d584d99c02a6fb43c9055c4d81a59a106373562dd435af8200a09d7a4884ae64a0a98903d95cc7aa7a077eabf82f45ec0ea40a477ecabde0aa66c8a68c3fc71df5ea54a8cffbeadb6ebc1b7e3a30fdc21788066e121c25bc2cb77daf43a5312cdd2ce0e8bd7f80dba97e83df0ceed5309a4e8f4a8b6a506f3b979a8fd0db78a8c77e4baa769c0f58d6c44f550cb6501f3678aea9cc97d115110c3a0d358ceb48330f1716d47e6db1ddd7dc7cd9575dacfd709729b7b58f65ff8820da9958d4570a155a3566ddec186b3c05a477f3a831d7a3b46b8be13ee6eecbb6e662edcbbbbcd4ad6c6cff18172bfb358b71f9a8a324700491f63d760f7ac064a2b1e10807d038eecf488007d2f5a8b0de6bb6b549632ba7f464d16c886d6bf37f87f0f9d20029e560c45a831588dc767d141291648cd40045ecae6b22e98a4966891531fc862e4c2062bcffe26a8ef4cc18bb069563da40e02026e3d960140ab0386e2342d86f27835ad399bf34b54e02012fc46f570a5ab2c5db6a8a53b7e07ecb230b71237227d065340d778c6795130644ca5be124a93c5d2d51f0db09729ba67a497e08fbb7a56aac1998f27ffc37f22495d24bec96b985851038f9f33a167c0a5d16f68cba3c2c2272a7f8fd15510048cb683f1ed910abc379dafa50c8662486ab405de058039c68463d55af338ea1bace733d8271648435751528ddd05e6f40f8cd532568e1842cbc32491b71daf796bc896bb2d2b8212fd8d4366afa2d4e800a2744c02b9e8158b1091c154924ca96935d02e301649793f0fc532fcbe99fcae1ac38424c215902e1767f04a714620e669847cd8e785f03b9dbbd31be501cc03c4c3ccc01b6700702424fa70c982c6c2d8990251c62381312c50543166b49b4c909d02831ba8aa52979d16974dcc00cd29438622fd146efbcaae2e6031cec15a09739f3cfbb1d9f7a4c5291a821e9894455da09ee884d7783e2055c210a70091c8136ca895ef2c856f148c0c6e08b1a7b63b1a5917626d246781e32482cc6da833d06726f0200036c870470a0c0a857e49675edcc808d4eef7162a93d7ce2c47136c84c731841c844ce42d1909d2ed6e1c89049fb1ecf3ee80d62874e05e1bfc4e856b428ebc36d11eab21cee5c79464390537d10141545c82e888dbd127cc903ffccab16f1f615af0b21ac56541b8b7397f26159b842977a52cb1a7d07043b0c71794ba02f6f249644a395c058f8a67c6ed19378506878daefdf486dd78f1dc1efa550eed31b046d8a7e260f9599d46589af0ba12b33efa1ec1f0ac17eaff0255fc3b1d5b702aecedacd9f1dc809289572b73079098d84376a4f4e6a39cc9a56c546350329827dcfcfdf232988d7aafc35e3ab0df7e591d87ed8b02706e69968f8a6b41a0b2091d8f8a3db1f517ff35d45765fbc46804e9297092f47f36069947d59e8ff556ef0980dbd1cfe361a834932e444b32d0e0a4e5349e9816f85bba527ccb400a6b2f03bea379095392d38994656810e5b318f0be587e2c9a17f46b21437d2ba839829bf6c666ca10cdd8fdbfac9fe4673a3ecfb21fa22d7e4cc0fa371e8b3a2eb05d527d1636907f3f6a5aa51746cdfb8680f4fcbd2e854fa77e7a21a0ef5a11b02383f318471caca8539285b1f877f6bed1c5b6ef7ea682cd9308c14265e4b53a82bd4c19fd1eefca0612832e14b0b466177e7a9824dffd676a15d799b317ebf9f8d593b80200d28ddc828a30c30aa7a7165287d67a9de311b79f06a456a84c663da78fb3e3690b0e15a34461480422bc8ccacfbc00633007e7321151c1195acbd7956af0b0cb7a68aa80ecce7550d7087c0899ab5ee507d08edb35d4c5cc7d3dd1f55b601544de78018277743123ee1dd440c2076d8982b6ca07cd47ffd23fd15440b889b8547cfdf3017a26ea61d7b7ec583e00679cc578ef2ba426632bd229eba307b43a5d825b08f0cb444c893141087024088a154ca2b73dbc1444d6f962e879629e340d0746e15723fbe5f1932360bc080dad440687f81630d54a88417b8652442a05c685b6d16f3f3d9394ad86824f4ead8bc2e8d2dd9e45f247cdcd303fb3281d483cfacdaca01fa6aa7f177669a75930c74f068c385ef30d4f1b8d60ed28ba6cbd8c588015b43e208d585a5e65f19a96100cc6abe263e1eb31879e7a049cbc43942a07d2a312c8ca8d414be886c807934f31e32f184348477a0e0950cd171b9a186d50b4ac399d3ec37eaf2b123d4585f8782b0a379aa105d88a171c911bcaaca77555c149fb2dbf8d5c2d233dfb2b8270626662fa28b14a9092f3259c7609ebf55cf89d8114a487df7bdee816416e8d551017bae1f57da28a08ca677b056b291ad96913b7c8387849d9a0015c4875e93d4851eb904b038b60f563e44e96f1483acde7f4098d085288b4d3493b499edda59f3017ac930d3055133353cce8a7aa8a7c6132b4d60fcf18e39b5817be7960a6a52c4568650909a21639ffa248e0c27efcb1af87e28b82994af3bc8f91a05623cbb9ebc2e4dfe89af3200ebce2d2da5fd78aa0f0e4bbead85d80ad340c52931b317007fc3430381a4913608e15d8ef24f71ce0b28fb2b9139f3979fac26c2d82fad785f62e505a5c6c626565ec0868763c7c20942c16295121ebbb7f7702b0378fe5a5e6f1f8c045fec95b20a848845b5e4a05221660e587bc73a445037d9885d23c670b1c88fb28c563bf90225fe57247350c111d66527d47bae6d999b804d2c02f7b870879124e8cf3604d42d50e0a60a87026edb566a28c074382167ef8784fa69287e30ae96eafc0edb8c2ebd63503cccb1e6e29c12b56c220cf4ed762b3263721bb164b803b2964232a2f52b2f21b0aed6270b571bcb99f1b910d27c7b26496039fd94c8f444e7908a1594d63331857a4f5e729499bc1789e97c909c23caa98070941bcd52470ac2fa9f78b224311b70f4b7fa9ceb2d56749612ac5d472749dd8dc04537e019a5423e8952c85f35c784beaeab7a0dd9090b08ab5be435e49efeaa7bca8147f209131145a211eb214b01158681efa6aa4a169baf569d9430a3f408cc6b8f37e8249b3b2b51012f7fe9a073426ebab5c92a88536b9b3bb03209d5567b9ff4fbb8a76f2798e8cfc8f74f4463995a51dda75717f8db63f0a996f18046ff660436870ed779519802ee5011fdfac3feca749cc913aab4cb1ada6cefed2b7e6777ca7a886d865565dccfc41a4808b3f9f1a8d5de4b3e7bfb4736085ce9dd187da1b4eafd10adb225c14f43cb476b19d2d3339a073279e48c33fc5bc8cb8faa15b4072628d10e45e8c68fddbed540e024bbd6e5cc1b03f89e5072190ae3434145fb80c4aec8fdd5e87de21bb90d8fb8bec35d86610b3e6504194c7e4a05c3a4ff195966e9c0588d562e2dfe3c1101dad6e00e5c18f2a136a663cb32a2d715021f7c72218028c3dd797595dc6461e43a857ff4340c2981ebc0e47d0dbba03de46822d26f14fc4adebf4ee1be92c33ddcc3adb80adca29fc7251913b4a7f50825f6d1856c7e50d897d0df0b5ae7a0b6b2d014904203d23d058fc03f012afecc95cdc9013b326aa17ee0e897301685a50e7ae751408a948b8d875a237cc711cb89c23482949a8173cc882c628a81e19727c6f9b1d2e9bc77782b3cf1c91e1115dc3d4603cc3ee6ee5f4b5088dae1a09d7f7390e5a9e8d1e71d5ccec47d2f5005b311da91245f2883b446373eda118150c3579765d7c3124c4d49449f022f487c592c517685550c63028a00e92dedab1e98129b67a9656ca4180cf1e0a42a86fc051aecac1c19c6c763732064832ae1ac0df25946b3b1494a083597534855e953c546c41f0e4e7d74da677bcc87ae715479611fa089311bd7d3f9f719bdf7189aa9360be91d9189bf1eedfd15ae6f2de4eb596cd5a71ee7008a10087f35ff5133eaed32c29135e75e2ca2cd773843bf69bb12670afe36e903ae76b117f6ae04aa97aa3e5810c9a7fe0fa55f519cc18e8a30f9e2aeae9995b13d55891f91abff5e68c2dbcc9162b4a1143c214af587a88ec983f75b001ef8f5c3d3d3f38202329c713174fbc5b90f3a99ef6707b87f8c6d540167e905c176939309b916fb0d584c1d99fd06b5098f83434fd1d0f2a1876c7651debd3bee5b77c12f5888f2ff481947b80ba0ef3bcc8be5e02f67b8b552901a5a13de4900488d58dae6b483e6318d51e5c4e55b545fc3b20886d105e12397fcffe65c27d3e0b685b080e2ffa5743aba5c56c2e2d88c3a6be0804766867eafc3724a38b65cf3e1f6cae395c7a9a7f7f3c383e90eb8bbc18e1ae45aab100987b053090ac03e35b658e3933ff94aa7165462c46520faf5fdea372cdd8a111e31662a3bf42121d27dcd38caf086f4b79a7b008175fd51a939ff4e77642c562e07a6515120bfa83fc44a43ca98ef2468d949421df21fb25342fed2cea45b1e5b992272ec506cdce8b4d43c30f2d09b5330350bcd7988fce8d1246d607411ee5b00cf58f2304a363739a9fdc832d6f837d42ef9a632594cc0d47a6bfca2bc4c98a9a9f1ed814b7d637cdc287433ba8f04afffa3e59af3db9cb8219219da3cfcb02efc2b3aeb8f19167cb6fda90d60d9de1b270c7796404f23715ef164185e4a89da3f2d86a580732008e2aed741d7b52231c088597678ace3f6511ef6238ba48e6010076ad8da2fdbe20d130f903fcda5e1c9484934cd11009f89686615ec24520b8242196ac6044e516c7f59164f2b6d3823fae5db8609d3a3fc92b8c5b8d8d4462f3998f1ed621436564567a2a2f9f065ffadd7b8304df993051cb631bedcaff116cd418eb2e322bebea100abb72a86248986cb3321c22356518dc4fd9cd01e7031c18a09c167d5dfd1ad530302760d15a05378d2805f36412ca0746cafd4f9458c706dd67cdd980c819ea8f1193d3577fa71692493e71ba6172e60066d7c5d283637a3d1289c63c8406969156179a1801d29e103f582d5bc64a2778b952080d00a81a536117e8286115e81a19b0d0a14160e7bda929522c879aa39479de1564555c93d3f8958c5a59566f88c46980e42d1233f5d962fa2210f7c62092ecf70f175d8bc94021e97a59a85fd34717fe4ebd6d6cd2a634e62e0583ba4e7d237c07ee257750f63a9f525e3e60defcc9f745da059506b04fe42569b349b239921f9903cfdd45f17154a4b58fb00045cb3483c69070291184b245ede9a1b026a5a5191cef620398921241343375badf112c87ad5ae0994f474353cbdbf6c9f1b3c71edee95b42d905b62c79a46369b2347f6b3bb19c3c435368a1d7dc01d4aa7243fd027558dde8068e9695ec1de7621ea7d57982622db2b5c5dfabe49aae72cc633f72bd19e8c867cfc5cb96d08021ce0dcdb19c11054801bf296893d80372e7e829072462def57682179a217ee9bf10c8efee0ec141c393faf6178816a798af1f2841af3d60533af31ba7c19a278f16d40c28ceb60e9b206c33df44fddf50dda114f804559728ab0debbaab2e6a6a75b30dee2a9f455b1242add26edf310caf8cd11a16cd1d4d98db1e7594ca5827351dfdd65d101b40b36995c6ddc3f3c0d9e5020c8c3f184847115b79af821f200cd7fd142a83dcc0478b8674891239e003480091a16e3f7cb006d35503626dacfc691e64a15f368601affab3d7544ac8f88d491c89589b3ee30d5ece8004605f439e83ad8e0100cb9452477229c60a512de919071f6e2fbf268133239b50367a6b0efa8701812ca6aa25df542edf8c1587cd40c6c3947f43e59b52b85ce4a850376a12dc32d747bdc8f60f1fee9a1a2239cd3ad5dba247a1ff010b0ed56a970e438e8308d29ade283512ba0dbaa516f0dc26b494087a8f323dfdb27ea3ff09991250a8cc6c89fb383746dc3891a4f8f6bab964ce593cb96c9b47b971c867b14c2b669dadb71ad9c6f095ba4a42950446e14afac00dc45dbc2ef199ed8e44f8b0df72986b4974138bbd4a52c21a00d9e5d1f6a613e6606526342f92484003cc056c0fcaa746d47b7cd0220d6afb0e24cca68fe076528b064cc0465e108cfe18c29e96293e883042275be39108666c6f2e4b209eeccbd8053e095c8fcfdeab518043ea3a10eb87422f024cd08da258dbbd7ba5c474e0df5994e9805c2c351a65d2095d8ff0abb416e9e007fef9318e924129bba580793d4ff04596e061d2d7eeeaf7e78dbf312e2a777366b25726a9696eea9ea65d67abb5568537b100ab542e90a247d3c30d7bdb56ed7ac84d9f2983698965034bae3627b902030d4dfe51368e98f53ae57adea90df63cbbc727efe9d27783cfac332f9f2a4e5f3f71ea88606bb2564f0b5b03735f366998c0fb65b13bd638822d27ef54cc909215fdc40e23e6f2084eeb0bd0aae8145889ca2f4bfaa07ddb5000a304f1915fbca6b1c27a2e6089a48621a1ccf5f21a09db7f30e625da52b88a1b5e694bd17ed4a75ea2c083a41904bda2982cb812d678394227e14f6ecca142022e5e1d70e0890071a304ab860db43dd28b77c983e58020ffb2238955055ee30d9809f8bd058249a232f4e3c3d7da44b098d70431e4171f5419d9aecbbc3b69a2a9104840729233d939e71c2164d0a2fdcdbc29b79336a97aba41308c3f5380741b01423b5b266e93d37e9566052ae15b08369e630106a0826c7e4135f1bf25901136553bcacd23ab5522305163c3e042ee534e5c2fd95902419820db5593476cdb2c7b530d891e3f0cca2a6941b930b47f0715e795c50be7f019da5d185b153336276dd05bbd0b4bf391585d9eac5b04d54129810d8962d16517ad862688b990a6a7c0f54eb15bbb0f02be44bea0bb5dd634706e7a9e54c229d33126592c0fe8f4820fb9a02d3cd81cf3badcbe91e5c9029e31415b5d5a8c3cc8c188150f28aebf27f43b49f42c2cee0c0c243e19bf207641c34ec1b16162b4b2793f39e2f6f2aaf14c38818905e914716955fc95064930bab293eb24d2962e7871953a2a015e6482da92fe54f71eb07136fe617f9ce44a378840ca50ba45bb5462449e8a682f1e57da15775cacf36f73218eba3884551971ea1ff9c2af707d388f4195431ec6c5203ca08ecaef0a5098034d925dcf47467d42cbb821465adda7be0cb76d9f83e0c3a87326a53cf91b30fdaebde11f9647356c444d0baccd495c315f2be44e77aed1848fb98b0804c791fd8ed17968cc41f423317ca34ab858b304ef6f2cc9a8306e59e185708d05a8e822225be8d4ba83191cee382fd935a00079e2cee3a648c8033bff83308143256baf32c0fd17b60e60300487b00dc18dee3e70a0c7cc8f959c441a4a8a2418ed88edacdad9bdab6c033546b8371dabbb0a696e83e5b18a9406803b90246271561cce043d74be022505e3f350637ca4f4230046b425d916081bcb563bcd05f84a85904876b288adea78902cac325c75fc900a10c5c9d13068f0c4249c68c23758ab4ba563c4d06f39959490ddf2ff939b99ac30d586c4b5391a62f8e1420bcb16bff8843d748df8877230bfc4216ac257e0e1600d7538e564c0b66042d0fa6dc05a0543ef5d396e70fe6fd5de19afb4ae641009bbb8295188662d277c951fde8087b6ec027c2f952a5167c3546e2528e103ba4ec94697620200dfc8941ac8456ee136f4c60d993ab7c294735f8de09c09858bd901f3f45015c7226ce8b4c7b05b8fdb7545bfe106c0bbdbb63afd5b46da4b9b6328fe4fa72a9a04144c2f1962cde85a323f7225dcbfc5a29d269db35767f20062a735bb6377287799ab062f08c7954bde7ff2ec6cf96bd52fed6a5ac984619f3c50f01c38ae45dad4499fb68e1031b8aa7f4b82f27456753e415f1165f2aaf54bbc349d6720a57bdcaf46f83d8904bfdd4a24466c6f0196b1a255cc67200ffd1893b54421d3194aaae12fcba8fd5fa8ce5b84f86a9c9c2b7d56f9f07b3356324db95dca42ddebbfd96c72a7f8813f7b158f859c65948557056b89f19d48ab762d278145f8c5a0e68ee996a224811fab5bc815def380d9b4a7106ec3306e71bc616cfc5b8f6fdc3e9361448eb0b300a652890b94fc1fc81b9d80514ef91548e65a9ba0c252067b711175971cef44ea42598b479822522e768ca7ba3ede02f32cb02dff888aaca28283394508ef94645e1f7bf6c76980d9c1c454f758afd145f2e7c945a4771565ceaefe70274170ffdd5f5f38a2ddb1f341b1d34fb0403dfc9528ed5d53edafe15f0984c7fcc25a007606b44d6017a295bf83d3d0ce219ff430cc944ba901fe1cdfbc6226e001f7a7ff5aa25b15dd6ec7aabe96d46a606943c714458d80c4f14e32b5dd69c0a4490b114b578ae3cb63b90e61de9a642fbd71f3f9ab2fff44fcaeb78d3aa3b36e2cc2d5081b6eeeb98c4f549797b369648b2b021b3447cf89b63235ef28f80d352b4366b3f54825ba15deaac858f572647546e58436e8c64cb4ac2066b7594f8d6ae700825a19fb30c0d5f2ab8278a0de516f6c65f21a38365de12fdeb2c742bc4e67857203e4b0ae745fd4c66f80da9b5c03608c35db04048d620debfe2a583c6d5e6a946b0875636701a8c0c0ee8e7abab2d6ba78c0b6d8c835f8a7c3893249c272c85e87847e3653d587f2225de4339fd21144f8ddd45a3a8890c476a81b13b72f395ca5b24e9689e52fc53c6250b00170c6dd5b00b35b82a201f89628782093d9d6455a268381365e04a765816f6358d4d372a342059f6b901e86de072a2ea91fd127a35123499cac026f15e4612dc7f7114d5859b4142876c3b8107e8be9f79e19c46ac2c8cbb21c01b2e95ca85fd852986a1827d2f6160d7b6ea4534e6d84c79b52a6c4c69ebad19ce1365c607d045f490a8a29cb0e94d02eaa70bff4340bca9860d389db9dc05fdcbc972eda3d6031c5f6bf0993e8222548b114e05ca76517abf424b9e09110af726bca6bf83696726ae854351f783aabb657949ff7e93c0676a272bdb496da1c70f4efd8c97e12607ef6b1d423489f11e8fdd6e92f56bdfd347bb40f7b121ff2dfbd7f1fbea4e1a0a2e6ea82ca4c4959bafbe251399146f818cc7e08008a2a9b38dfe95f586fb28837876bb5573cb21875d433f261d02494789b18849d5b331551dbb57e4a3cff5818fb7ba7cab44e1a4f42347e6d699b0535057140e1c565e0abf385ad263d61b49e3fd8613fa24c424d27d8a06c925401673b45c3258ddb207da6e954a93bf485c0e6f4b63145609cd54b88c223ccfc1785861f2929a3cda462fc067e0eacad4f95647b9311bdba2d0d8d73747907679c274a8e6c3573bc94ee61639fdb874de2599abf0af3055705b13863cc633e7b21e7d6340518269d0a51c9ebc7477e9cc2aa303b152fce1617c2c5509d4ac023d2fdaa05a29fa415abb244140a87247d46f82fcb563fd44a3247ac5ec893dff908a2c630be725f03c99d24d19a9239c91700309abb3d982f1edaca6b683b140aeab7110f0d5b668f6da6224755fdb84db348fc47e5a75e0e360ed4c94fe4effd1fbf05b3083cd927bb932a80e8eb7c153f07c8a5753a8011725a93a4f514a854bf555a2090e6bb008aae0b4755df20dbead8b6046be886dbef05f38e95b8b5e3f9220f2acc1bcefb515b34e8bdd0c8e48fc9835a323ede4a59099701be98fef4c44f2a8c51f57a30db6cae0e2f878c51e089e1404aa5e9c13de110982354352de33b4c153a095680083773c1186fa2c0f642a71cf56b8fc6394fe93a9a8939a865d3d309c2ac0210dc42987c6a6c7a5eaab03029a281f956b0ced7738f383a04a05780101804a2e8ac9336d9cc8c020098cb823af74aa3f7deb2f401084a0c50922633d629e900c321071e19b79a9801d4680c6aaaec5c4563a2c6e235b9e3b942367385e19517564be11e408bea8948c0ae1158b5543587fa4d090ebadeafdd1b1e99aa50b99d0dc5f6e9ed9ab3247abb8e81b33a3238e8a97f053558314ba8887359a81856313948ef471e5d0ff3a2e7a5dd30f489213d95c263513fcb174e08ca5c0eeaaa18d7c90c992a183e76044f51d988bd7a9c7bd48321f4a8439a3164412752078e740f63d02ecd38f290db995978ccda1031835758a1c9d9713dcb5dffa8a8ff9c2bada3a7480b76570f50d7016a1fb335b482f82e1d0ab09cdfb84f009c77bcebfc8757d5498e3d6e8e41b84e2f1c0d666cecfb6b43a8a245156b52f0c3c0e0b023febab92c5f669ce5b4e9d8abaf182a0e58514876700b403d14ad2eca456e73185e536a8b2ad49f150a0be24da251c4813ee4f518f49c00bb35882c58ab64cf15d3a99d478430db02dc5cbb6496f7697f5f628415f1d8cb4c43a8b9788e4085254b28b82aee5090a5708fba060734c899f336e96fc382318f2b4a44f1828ec03fd31b7471103c87d13ebf2f5f9c6cfcd6aaa92bd83c6c1d17180a62850907207d482c5bf883310cbc039b173e45bed0742931896ba90585f685cc3dda8604477f0c615f893b1a51ab801400e42e0012291b9c5475d1d05f2028a5171c3b916f87cd98758531e8c3152be6d99bc15469e8ffa79826363017ecbd2fed0c3954fdddcd1614c692e2226503487abd706da76d85043d7ae62d87fb1b22285c90c9d2658aa23aabd62c2a19632b21aafab8f87de9dd78d030401d61d0e0717c017e4c9034b0c88249b44019ba521fc38498eae10325da06ab0bf2b0a54a58244ab0a0f5accf8bdd7cb7252cf51039e02b9673d45c8ea9cb09f68951aa534806996acd0330cd1b7780b95a05bc99758bcdf13ba94560f25f7f880e2dc76e882efec2b93c1dff16b59340799214e0b1f34b58eb06e2166a07a26175d7bad7ce8bc633a922656a400cb1ae556ec13ba5c90c01f7a3995825233db3e117c7b5556c76095e46a620d36ebb63f303cddc46ce454236a6b81b96854804c0994f27e4a0ef9566e8409f9993c7db2737f4cb589a70d8b1c2f2f26870e65cae6169765ed9bf607c7257c005ba671a00d328411960bf1955ba1c2d1dd776650ddfe9aba72f77438f279fd7c116a6ce17dd68273306ad3325c0b88c3ceda3a07763d9c5d133ddd9a6e48628b02c4488e978d15914ea1704bcab88d6b523db15c4a182ea78bc09715cd9893980c0e813ac5f820aa307ad0d6e51138c88f092c5a958aa04964a421593b2c3621190c817b11ceb088f65d02cdb1fe07881b826719154c46379751d171a34d9fe741b56b83b59a9fa58e0f26207ba68cce6ac54e8f4da84a0cb914baf6eeb1edf0cfe0ee601b1fa077f7c626943d7e501e20a6dfd6ab3bd8ab89142819096efea790d23b141991ba7bf1c067701c2cfa0ba20c75a32d01af89ca4509b781970b359b7731a91aba2374a10bb012336217d5418752785130768d028d07426f0518b7e65801fcc7da955eeaf3329050e4271ac27425d1530e9f281afb2ab6555531de9cd6a8a342a2a802205115db1812ed31d75a35ee8b112a7a184de69f4a1ab779ce2d558a8886fe8dbf08480e3a1b60cd5b8a8b17127c75d6272d9f5a141d7a52b241fb64c0917bb02f7c0d81acb607b7c054ae32ed77b1498fed3ec2d565a142e0e12dc1338de0a38cc8ec10d9e29fb1617a315029b55e271dc8d9406e5c8719e51b54d45cb2f66db06ae5196ad46676b71abec620d40bad2ecb0883e3f08bd1e5fc43a17a522c699784582ce9f33d674659880183b0cb7fee56930e62c49173c87407f1856d9ad94420521857a8445d8e41f0931f81dcc30b934a8848d1afc47b387ffb9ac8d2201d7d88ae0d7b4e0e709426ea685603d10851ff95cf562824dc380a5cbc558e5e466f0450a510de1631db533a127a0b12c2e031e2f2f52c6202ea120f971c768333294f10fdc11b0ac8350971d2b40576aec716c216bc91530beb5eab61a58c98821cd8395e4b6fe5bcefce826c85e0da278297ef566c2b2007009f72d96264ef664b482ce73bf05ac1351e643b7c334a74bac0f40df02b18230f993b4684216ef8e8952b25ce90ad6080e612a8bec8c9a9462e305d67309a84013f2a1bb80e38471c0a7f9005dbf31181ad8836dd71cfea8e504be8ab9b75c49f1752fc6a2178e8c8e9770dbc2342e796be5644c662076af425358dad93432f217ac2b1a76847e36e5b26c0f084ca8347799bd8c1f69db1ff6bf8377c49e2b98513ff11f2bd04d8e00557355cd6045e933eea799dcb804aebebaedf7f0d5502e4dd7259ba170585cfdd82cf571c70e0cbab187c41f699e00094188f47ef75b79d6af8aadbe10fb9b1a544e6058d83cfc3fb50c863093d1ac62850919afac705805786e2636ae51d0b14629a3fa5acf80a1321b539753f1bf8beaf84f2111dc50596a8b719939465c18b01a76abdf9d2926695d73cf45ec7c577d3dc58d3e8de453161bb1ae6c10108ba28d443869e2a2e61017ded30ff00a455015658e37d66f4601f826b388559e016d362e2e9ad927b269491972552349bcc217805e2e30e6d1fef6b84eedc18dce1560220c48c5cbfab8f4da917e15403b3e9dff94cebd6a4051cc5283f1f37fd8d8987ce4c71b5113f2e8081be470b4567cdacd6072207964646fa0b35722d259765c89f37005b8f7e9e7af447eeb796b5e59a7c8f4a63f9136ded3d266c8063625104bebd1d6742caeaa550f3a3b77b2d8a4ce24080046e5f2b2e93d35deb194b370b6ed14141da38bd3f1cdb2569ceb063dea5d9958b5f20a44e78c6848ac1a2c34c77766218a44ca410f0ae31381c322563d63602065f43c63df1721ba2b6e4da113b0b54ee5af8ea61ab251154af68b6967d444000cbe20c919f1ad9afedb3e9e1fdd0793f0ae11be578a57c73d279ec3aad1f109395d55534edb07f1be7129c8613ddfb02d974816da6f8e1b66f92e5e08f9b483d415010b31c84525b27ee00beba4f12bb633bbeffc21f089bdb9210a44e3399227ffb34583182997657026f6612f89c85aacb27fafd92b4335bd66afed6a31b038743cc912b932c6487f7d9e038116cbfdb259d4a0d2437b748a7e1e606c65ceb872c3a5584767d17db45e91fe9b07260ba230c2c40619b22a92f91995231eb019941424cd53399d96839451e134b9773f3d8bcdcc3f06e83458a5a0f01f270d42b4d00fa0e69b30b5bb52666e660d70543f6388a79a9a4606d10d3467615385574aa8b07b9b0486c54ebf3593792d49f1408b12c652c2e981db68d9aec07d8f710027f0a53cafb419c96453f5a2188cb7307ff68af6e56fe9c2038c94168c816ce4af15eda7380122486d060895119f3a21de520a4472091c12c5c54f59ad15472161a85800c6ff639b1cc098cf082e980d2631f30ddf77536524a720d3766ed3f4318ee086d0ef42cd66a32fc7c99b4c667e915f110f7bbff05ae9f000c3d0a18b93b738381f85f9d56b0ae49d76fc97e6381b38d6a15661b816d5935237d262d688b4528b5b204a89a4eab826ecac202187e266b7bf0b0d71398855996f8c3ba3d2ce2d21cd99af2c6f6d12c668394d5cf826b31f7a622ed617280b2539c4c35f3fd9c0ca11cd2c083cdee47a5aae141d19bc1be5ff98e17a34bef1c4efbe6d3c2205f0844ae86c9c02bf493a3c78036b90d7463f65685986e1070daccee9b853fc876937b68cbd055ac62d8fd70f7a64a36cb130733f4c9e54beb4519a00f7eed35c30072fdb5c1b28087f608ecdcf6c275c4ad09179cd8a2906e23551ca027165930c00c0950121038432dcc04d4231517aa8095d487bc740ba4d97af534a1a93ebc3fd8a30237c18768375c4e10980dd1454292a190e612772f7a7a632a66a139bba921f67aaf75b62a364a3eea4297a24ebcebbd8b65726b994234db8c74313ef4db43c0ec3b8b46d3b7e76a355475751f51b2603567fb6e9cd16537b85b99b88c643d53569be8ecf5b46271d288f8edf231efa4bd790e0be0168063e399d1014f297621ab7aeb14d679ddc92bc0dded2bcec286fd3d5aa9d3636ee0b25c849d02f2a63623a27e2e7467397a3f05bd01a55107a4185c4cca004c228c0f905e7e2f939c98192f7ebe1343b3e9d9f2dd6933c26a505ba78bd2e41d389d22abe7c99e0c3abf901563a1fa32de144d0b5491d584bf71c094a8321a6212c28232e9d4e89b06282c0b64fffc4459d996ce35f38236ec449dee19e26c94ef52c6b4e4482919ea694162a9c1c2e850e048fbf201b4a4df764890cc6e815309bcee496c1daec89dbd62f082c8a202dbc7fd0a7046a7554725161c7b78a40036d4be46f40eea149c52dd5b440845d653664e5a5c04b5459c5a2a9111641fb87f2a3140ec58c0e7e1e20460cc4eb4e2d9efe603bcf41e7a07ceebb9d809e59532d450afecc514da3fd6f1cdf16919cacd52491808c8b79439090e3eb8eb03e68848ff58929689862ea5b84be053b5b8f8c462550c18020d1c7250d681f7e703d93dbe5589fce6e26f38873fece57d3c05286220d22718d4f28abcd69b797d11976944dd44c1d95d19424ca7190183e14b7148882418c50d7eda529de5a3ff33a3c26ecf8708c29fe785fd4b622627489f02833ecc42e4e0101cf84781f6306d5c4f51f45e9cd529f357ff7a4617510b1fea8a85088ae019b8dbdc3c50f88454674e013afaebf0fd3c489758255e32646256cb13e6b0156e83293c62dd01ee76a280b9604e4eaddec52b0448c42eed8c54a010d4a1c44b267674780567cb31bcf636e2860d294a0bdea7b0cba7c5d29f88f77c5b9e94ea136ba4d94ba254d609eb659e78feb071e5d3ead02c4febe85c34e01368c8832857e2faff087e599064be6a1110209bbffbb7f2daaf76a0ef6e5315787291f8667eb5d58376e7c062bca17b9836e334fe70c171d25908ecebf2af2da6fc332cbcc5f1c452814c917ab162a0c2ba9f6168048000a10ef7ef31573c3a8b3da00ab78f4dbf592e72f306ee39b79e51ceacb61873dae2b115592792f41a4b6e7e4c365f1d26b9eb54b187270528eaf07ae41a5dbfb287c78f62ad8a0d21d06b53f9c18e35cdc5e13a316536b0a95a5d61c9ebe97ed6f18982468195c762a5894a468bfa5051fef1c5d0c404f448ee2dec17366164ec16fafbec94ed4828345d8c69ca0e14d3bdcd74112dc3c9620dfb38f726bbd7e9e3298176d8949f14bee1816028e230059a5156ba884cc2385d8d4addd56515551dd95bcea47c517d0b57ddea723321c4649a5f8961071a19b4899958726d25617e4f9c8414f3538a26f0aea0761400e451e02b2a786c6ebffac84bd0e0e67fe2f1c9af3232248bbb4c0dc4cadfea33ecbaee15287e1170d90858b4bed8c3a49154929d7186b56f6a38e409382cd031873fc928945461f7395266f64f628ef3bed0a4f6791cfcadc4716de80ed10faf860d32738153a6304e810b149206be3f4a52359571a417d4290067f6b3db79faa3d60be8a92bc36c532c586131dc4743fa9495e5642e63367f847646e53999db3b931fb1bb73e6f4253884cb9a1d3fa23b51d91ce26e66e68faf0e3f71da129cca65cf880f114ebc248771b833c207c42741611be4cf690e6e76fe98ea444b730a873f3b7e5cc4703efb78d4369c7716e368b8cd7b7447dacc44ceab4281cc75794943b68024fdffa39da848fa3243f08eec301429e1af92d812d1f9bfc9a230ead069b08747085a165f23e74de61cc6d8b2c358d49cab6a6d948642e9f7209df3e8afefa6090134bdded7d123b3a9aba227645a8e5d23f76680e34ba17e1aa86b20788df1b4f20db233cd51e0f43b532b71efbbedba4fa7026682cad493e670ba9e0219c062edfa1317a383333d3d66afceb42505a7b187c2c1aebad3da07bd469c5da3c2e91d8222c7ce138a09e3f7ab16efeb27e314b3256cfba1129ff6303ab7b0066ebe740d65d11fa18c8a0a9b9d07b0293341bd1dea74928b0f7dc5b12d063af5d69a0907d8ed3b11e280fe5242ced362cd2ae16fe53d919f18be3fc54565a874247551cdadceeaa7128eead45125a41a4c6bdd57cee39f8655b5079ed7548ef57d7184f8b24d45c458c1e32e62c8192e12ff92663f75c1090cb9ce0672009755ebe68a1bb7442e069f880bcfe8dc49efedb7d093a42ee5baffb82be8b179955969ed16a866acf6027d4d5fee26ffb420408bd1764ceeddbef71c8f2703df5dc7254136d9a1210a7b88a2b25c3f6f9f808f6f52fb07e6711ed33a1a41279a3999fa829acce9daa87d504eb9368daca2b0c665068116a3fc00f9ed0e60eb976c3f13c563adffa63a11dd9711f47fb4ec457c90a5473de3321cc5fdcda40fd0cfb81c47e51e66e2e3a93356c6c939b799f871c20995e7244e63267f9074424b3908c73c131f449cf0520ec671ce840f904fa81407719e33e903c91352c2c1395049cc621f8dd2ffa362637b86d8459d4e667166199973354c9353fd7dcedea608347db2649fabfc20ac99b06d3e10edf2ea49878522e96c3592ce13fd60085a41dbe5d02a2acfa9323111e1679f3aa24359c0889f90acac385ce18b5691b14834c87890ae038e79d36949a9c60d2cd3190136bdb2d0306d6af2d4f0171104e9188b0c7513e01e7e8b9363f32999fe51da560b0977884de2bd6493f129fd427ee9a1f232dda09d9d0a0dc47d8d2eab141b35952dac590cae870899ea172b57bc3785fe33de8cb7bb9d0a680e3d587d573743a41e9f9724188511ecbe12c480bdd68575f2904646f6b107e7eab03e6422e85ef6a7c103299ff2b9e7f792bcbc712936c20ce78578d872d062b0d86e4bf45aec7fe77de0546e7897de3870f1a4861b0df07b037dc2c07be1010d402577bb96c686d90e6ddfd6c3c7f62e11c9f5755216e45892b2548ef523bc2bfea9cfd8e170ad97ba560285e93816af74abf64bfd5a6d0fc7e2898c9bfcadfd8336ea70c4ea84581d17bb8f4bf818dfdc5110e5cc3e695bbd8c46b9d11892a24d3e2f3a46e154dd76f96711c199109bf5932e60c96e020c9a915b8ed332a69ea681def7082c41fb3d071ade4387af4d3f39dad7f8c3835a5efcc3cd4b811428ec0c810a9475666014f43bf381cd495337f392d8d7159d416c76d5ce4cbcba4a6784889274de935d4e70d0a399265050b5990329a8d90c050627696dec1274013c1ccdf4d9f680df8cfdee0ce4cdc43e38e92514bacbb8e17818f96b72d29c01cccf3ae901cc0fd63292232824cdc4cb03777fac225d1ab8fb599919c07cca42497cbc60d466ab96f6900798bf69cd935b4e7ea415491e1d313e7aca5af249d24dab3add2bb4933afdd1fca65f32c522f0352d97e4ee28f153390445dd081a7549ffbc319cfef3e3b3e0664aa376d12e9fbcdad28ae07ac1fe88c05f75073474ab1f3f6740dcca787b08c83a5ac8760894b141d26d05d05276f593f508102bf56fc0faeb3a08f311a40418871eb4264ea27c6dc2bf4b847a62db541df9b876c61d32b69c3a325f011960d291b74c3e93e186ed8f471090e76ba63c4f81bf63ffe7b4a168b135bae257bceaeb7f71a724c4da88a4bf01a46edf5e1feeb2f4e8a70eb4ef7873c1cfa81b774730c67af9acf1f661b6d1af16394404261cfc5c1be1f8ba4417f73f7536725c7f8e5aa1fbed70ecf91eed7c375617f8cfec6e97eca18341a01287c25fa49455dd4a3f7ce0165ec7bf9751c34bd1d8928491bba706f2ab2079759cd8f1d12b70dec16398306c2bb37491ec5377a1c379faa817badf0f61cfebd54ef6b1bae1e986be78c91e2a18052a7528fc472a4965bc76e507e9d14dfd7919053895ed214b4864fcd463b29af86cb5aef61aa44b55cc01fe844ddfa7ddc2bfd188fe1fb814b77a6616b1debc7934effbe5b42c2f25b7e12f321f28fe776b965a401752d93fb264ab81e272c9b83f6f36ea70d383bd857be8601071d887b63229c5380abba249128cbb31cc8e2766d182b080d2e464eccfa52055331b5cae30dcb8e95b14542658a316387790e50868f4eb69dc3290901ee1608067ff58587eb92e35eb471a8ab87c81e0a15e2a631264262afaaca72e3242b0079958f12a01f0a7280eb8af5cf201102ceb909a520b3ad8f92e933d82560859dcfa2a036069c3e41d7e3c99a29947705d82e9b9cb00056f07907738d1ac259f1f9d2aac1f67729694247ffca6987644eb11ca583d8a1371a5038921056fdbf25b2c42071d2f0983d3c3d987e4e233144815076b8c6af0ded6fc2a513289a47cd1d554aa7758c7fff089fa6f56da14b7b2e358288eb6c28d98bc4ab27f6d634c8f271c8a098a34542b2e3e59bcd365851604b55805a5b09ed2d7636ba3507c039967aba755eb7fcc301d152313044fd1a891da04ffa0445219b8e1ae525c473aa86961c497aace11bfacdf8299df314e157443c0d1a8507cde63f381161ec7bfb0b002ffc241af156658ee264cd79fafbd5581996f70ffa47ddc1854dbfbfa050f78e8d78841a96270a32ff8365a6ab302291d7e869dd3b1d6c7601c56184a85976cdfc18a1fd702b1a38723521b4ff079c34a1823d2163084d5b41ce5e9ec17a92fe5a89d8476d605f48cc491d4da87009f1420c347ff414346cb5a190ec3c54d12781002506e07aeb90f9cf56f86d1fd4c4c5c9c911081e445f72f0fdfc04e0a9411e99f855491a1bed6b092be56953703ae45432918cb9515cdef481b24f31f2982a2bf7ca4702d51a1dca1cc5831dea9dab25bf797f7e0d80e413d228528b42432ab35d53c35cf97d4fa3462847e7463e5c3f22ecd7a5f0fcea9f3f22c62baa1099aa9e5ca13d180c6dc071770580492026d9e4911ca77fbdee16d2f27996cd4106b570818c27aaad8a8d307bac59b26f9b157ad7d38a4c0d28b0aa3a237904a149516fec5507f3d623dbd677df2da9d6e6fe1e71a46800084b18681db72ab1a352ad64d44ba4973dc0dfacb80d84c30acd43ba216c85c9834fdbe5a2d57207de396ee817e5d4b93a4dd281412059efea767109c4b16e674ad94ae16380ca6b1129d4e41f2b6415d3d11ffdd1845f6c50231e09ed3177aaa518ac4a4de7be3797dd68e7a496dbe62e535a9704b565629c21754cfb2f16f02f5a870e5d20768843027a40f00d6b008bc9f8c513f586d14c4541154309aa8f60c17535831c8a80a5acae4b5a4e22bdfe43ae4b51ee1341ad34aa51433fd2c95183449fa6b7e84ef2b81ce3d9e91b46e53441c76024f04818b52630c8976bd200109b11a559ead04e67a82810a7692d5a072cb0602fbb3266abdec54fcb392e295d4463d77a10a954ce81c7fb08481ee19f91432ad091215171cea58d45e49bbcbd786bdd23147338de47ae81de9c0400e14b00d99608235c2669de26ab6a31bc9c40688042b654b8590eb4e2844d2d464d83d8e2ea635b20e792b98270b6e1e43fd144e6674aa49405693d3a439165469c820e04449a08d7bb0076af2e2f6d4be797767040fa60b0bb071084176cbb5bb3b9bb58ed8251108c4c3d57d623a04901d66306d92fa12253f76197ef1aa4b147edb04d574f94189cd3be768e5e8098300d5ed05f59050fa6ef4def15504e82e03af3aa0c87ac7030bdf2b5a1f3c76c3c1b8294b7efb2f0427cd6f074bf780c0a720d6ca5363742697b134ab74888dc92751950570d5a439ddc2b4219a3b20dc08bca8040eae04660f591058c86ad201680df19b76216b9ea56ee87707924b8f027d0593c20e11acf49cb6d9ea0ac2cb7d9529e5997ee8ece122437a07e3fd2f07666460177e884ed49d311b8e2f359f92e57b78d4ef600ce946885a238410b2e5de019c0e870e5610e30d4728f8eb2c1138d599790afe8ebf0cc33d9633dd59a11762685a2880adb5e60f42b8ebb3cfae7fc21405a3b27e48b07608f86ae6f1bbdd876b7ff69e49bbae6d0ad7ead50fd7be0a36b41fadb59dc964c47ddba180d5219050bfbef0e012dac5c2eaccbaeaab15edaa67b1ca59b5a2ab52bda2cd6a8512ca4ca33e22fa95cf29fdca3674d7990dc3b2a4c79d9994fa502fb1301b9c724ddad067bd0fac003158020b86e0451318909d1ad8e2082a982801133f90d5fb007957aa6843aa351d3cdecee702b5a89da11629a8525b6441291d6fb26eed77e6c4843499a51a32647066b6f0983c1ca638de92165e64726ac43c27019e165ee705c6bc6358435a99342f657df66a5bfc0bbfc5c7b3b3c5165e023c2dbcdb169e0df7d4e89ab01556d42694f5d6da1c09f0b4f07a7424c0d3c2cbe1a301b15569506a858ded28a59f940f099f3703fc404a69682ba5d4e2ec927579c05a6bedf7c988aa98511c439b8d0925393201664596e3a76495561f13482b90ba3197982e9531945299948cb14e21ce8034236be6eb6a28edaced9db5d6da9ad1d6caaeebba8eb5844ac3a65b346e5af42d10a90ecc3bbd778bb4ab02f5594d189c5066fa05aeb7a15fd61442d2af3693bc9f9ef964459f3cbaee40427dd6ab50f2c0423c4e1e40fdf25e2fd47515775d9f3a61502007504988c04909a31b184a48d0e204961b00a1b32861c030a5123f92abd3e3e897501dfbf4b42faf0e3ded61ee494ffbd5bda1a73d7959f4b41fef0c3dedc55bd2d35e75477adaa76e483b7a7e3af8fb0d6ba61c3dafebcca4181e313d9a8006658417554c6082121c91a3312d71399a81ac9ed687655b18d4dbd51dd4b65faf3aef03c3d49822c20dd3abaec0f42a25da432f8ee44cb922893a22849e0dd353240f89e84a87bd5a1943129544902942993d253c43338324aaa1a9a9a999912153531353d6d4d4c0d4acc89a9a9a14c22088224c598580443e6c2db5d6ce307dd9819e6de6a954bac9d18bb64aa0b46d574270c2acb11c82ad9440993b1872c80d96212323a6942143860c1936a92f9eb0f7dd48664f3fb3c81112f5aa2351cd22b2b48cf4eb8bfa35bc91cc2c126fcd29a22783545259eae0bd15f955ea45f5a914684e016fbf5aad562569f6c72b5e15f68bbab72d1a2f9a53fae3ab7d51bd85fa4c27d6e5dd3e7c18bebe28fcc29a8f596bd6e5b539c5f35eab3965e8de55e5bd293e50040758a0369b3dc9c8a88cca64a428656454567f040d7401811510d0801740905199f7213b5e2a9385ef4fc95264c3db7baf66ca9c5271d6e5bdbefbd69df55e9baccbf33c1b1a75832a686a8feb6b12cab064d1b046c0fb6eb37755e8bcaeeb3aaf7a1dae754ba300a66bd8d1d7a7593c1fdabb9d0c14659fe2aeab8c924da46dd728cc4a4f4f01b88978b88974b327df9fe8f00e8585bb29b85f9b48cf707d359f7c8712e24a7bbc7b990aeed7182cfeec10dd5da5ddcab3b5eb6a79c3dab0bf8de7ee3a7d9eddf6c673d7d9ce763685c3e02e07772bdcd5c01d893b1cdc8db8bbc19d88bb16ee54b8a381bb14ee6c704703e2ae06771fee66e0cec31d0deeba19dc59dcc9c0dd9bc6c07731b893c13416be7b496336efcc9cc2cf8b29dcd3e6e0eeef5777de76295c8abb3bce6db26e5c1b77a6cf535c0964bdf2e8ab550dea971968c6de0a617b1b5257675a20201d14ccfb9c1a38372d1a36ac9a19345de386e005ae65f7191932dd63ba97dd61baafba936377b17baaebba7fd7ebec8d9a4a1abbc644a80c3020830c8c3210a98e780aa3b8872df4595fa4cf7a18b6e07ab15cd2a778975893873eeb99743548e5dddaa7becb64890fca8f87ebb02a25fdca43a8ee4775582121063168064125e5d11eebe1c6a1d9024f714b48c5a0d9d11e6b22e91357524887bd579248873d5389e5610913d16432626bb688b4357f18b1923e5fa833dce6185230703d9286c1d08285010175398404802083183c610a21e840567f5ad80c4fb0000a1ba4008ba324c8ea4b0b33c2e24b174948d8028c309a20ab4248620b0a3860042582a0029977a3de37627b6b662642df0bd6076bb688d4f0e0bb874c843cdcf5d95f39d3e71865bf91f86e1e2dc32ba9418d04ee7b483416c3fd01542a06a54cf83e646129b36479f62a13ec30ec937edf052f7eb64fc9c2ca97df656e24c3a17ccc8b602e03f3cf2c2a1ff31823d9eae4fbb3f5d9ffaef8befacace884ace5062959914f3ef7e56d82cecabc57cb6afd667ffabe1fe37eb7fb4be07d5f7a2fae53dcccb77640ce3551747d5079ec988c5212aee4473fc77b30ffeae5221c1df77fc7dfc6ec6814792e8eb5aaca990240a4d21a02394d50865f6a070df83ea57172fea2349a26f8c7ac10beedb7e852a337d0abf9bd8aa0ae04aa0a4015c96272272e8960d48004761d96e099bb5e050536adf9dde1f80b00fe17b5652dbb09409bebbdec39bcf8ca3bbbda079a919d2686c76e402118b0e4a51801b08365b45684f1f670aa328404116b87e2933ed02858829d6565bb70491ea3001f2b8a0f4523a6681339311e3e8b3be4d9d0694e5ed0d6ffad566c76ad347cb0cdbd0c276bc571ed7456fbc293b2750fa24a297386159d705eaed2bb8f3aab4055b1e0f1c47fa8177f5bdbb6eed422b73ed825de74f93962e33b78ac4e08a5b550bedb1f7b08df23c25896a54973eedcfe7eeeeddaf5e8e5d87c19d837b85bb066e12370eee1bdc22ee166e156e1ab86d703fa4b1f0ac67d6cc33a8f35c83fbc36de379066e0ff7334d2cf59ec16d713fcbc0fda631d55b0677bfd298f87e4963376f33bb5cb77ab929dcf5dae0910165a65a84ac709024b4607bb17649914494e0eeb54b54bf3c2f2049e4b92ec55f9b15eabc950b12396c9fc20d1b3a2898f73935706e5a346c58bd846bd9afe9cfe8d3f467fa32fa32fd9812a6bfea93fdb12ff655fd543f6c15f00057250b5b625df66158962cd67f9e3eb92ad15c55a2312a6343e202d81ed11e18b6ef7c40c081dcb85e80c6bcdb2fb1301cf5160485552ca8cb1e0868c70e97eb5e33dbd8c4e05c9532cd1969190eb32a59977ded7254562f35aacf3ea2ccd4a90bb6b7a17fd02ee3b93d921a344489fbf63af09ce799ea00db9dfbece0e401b697ed3cd31eb0c2288708719b493bcff407423b6608046ccf435e6a02a622609e6b23dbb954268ee3cea5329e9ba52f961ed0028bed010a60d18e5160191e58006ef575e017aeb7d566cdc2822c2080290bb2f8028b5f16a62d1d95c586db84d2b866a13df6325f603135f61d711a9879005691d51cc7beaada0ddb2c46714557ab7a05edb12771bd94051d38028babd1bbd7b927cfc5d12efb9d4bfba7be1750fe6c1895d9535389c63e6c5f9bfaf59de776cbce735dc2f6621923634719dd38e38d34df388384a1c6ab4a469414ef9cc72ea131fb03d019a54ba046c05689e7ee5c98b15ff1079a6755ead715371aa3d8be1fd6201a46adbd5b2f407bec6148211fee7e0c60eb050c04d8560e60fb2689aa12edb1af5fcabe12595950da53ec99f59f79de6ad7b314fac608224da1a44e5884f98e858adf15d2e125b4c706018f7dce601b688f3df568ad765504fdad2c28e92bb66f5cad1132fab006dda8617a4b9d70ed867c9852332300534c04a6098f7ddab6ef3739a40aa9d8a3dd2bcfa421eda1519f505292c86640a4de773310fef289471c78f4c1e3f3f1f8ccaa2f19f41c4f2d6cf55ce2f13126cb5ad6d932e88b879e3603e1f0e2591676daf37478bc1dbfbaa399bf57d2a4b4274c7da348c2308e63ea29d13cfb0caf0afbedfd7a2613f205192b52f004ee1053d2476caf0421eb372cd67fc362dddc36ebe5d88077f6e9d3f4ca238acb9267ba2645ad7d8103100849b4eb28384cbba8a47c48f8ac08c4ee5b8f6bd22dac33cb76d5ae3028fbd5309d3ce25c0ab1b8cd53a0cc3c703d8f7ee510a8eb49afce6d50d6ebf4cbbe56ca01280ca56f0053bbc2c4ee9ec0043a1b8cadd2aee255796955544b204a15652b9f74301e8c0dc66661de9576590f4b47a0ccaa285594ca4bc35456802a9b04ca279d0ddb08944fba24f1350b134d30c4d6ae3295f01d0e8fedc78689b72d5edaae0ec33749e441a0ac0f431ae699172ceecc24f1f58ea185a9a2b22aca68045930b1c45481a513f42088131e0001135734598089cf48acb6b26229c3571c9a7900ddcd0b8f1b02650dc6262a554c039c55362c707d77a3b1e36a05ce23ae3012c0f659c4f5291a040301aac3beaa5e53304a7d3616181b8c0d7bb47ed13213af86c37bb66ea45eed7a2117573385a33df62a241ace612d06da03dc79355cff3dbcb9e2f0956c2aca1cde63e2d5700763058c0dc686ed431c8efd4addbe5ba2b1637b189b85d5198c52bf1e5066af866d6e2cb000088cb8428b0a64f69e92850900a90a1cc840055834a1019951084cf0846d8c1a0f24d00399bd87646136d460065acc96d0850460c8ec45dba367c7af1bc16f4c913080a40b26a832536017e561c1f66247e9099a49e2bd2b350b0b4593b6ad614b8a3395973e6daba2b085b9852d3304314b9123fbfe38254b910803031ee62ab368f5d19c4257174fb6283e4bd1cced6b7dbfa8bebb88e6a339653c8df972c604df5d98977775f24ec95294a565a43bcc8b3ad5556651f99539458975812f5f5f4dd19c327498c39853b214c1dc9a538a58177818935a90a6c45bd3c7bac04f214fad2b8c29632ec347c6a94ce654067ec89297cac44b65dda5b212bc8c3b254b1119734a95c1bc7ccc2dc2c965a4284d18b3ceac0b042f8354c4942c00a032af338bf04e65b9ce30b8ba54461e1539fa8e449579b89ba5a8deac0b7c7dd79953b2147536eb02df99b829598aeaad39a5be9a536acdbac05b935a1b065f6b5b07d6531978f1529968d4f57784caaad64c3dbc79f50cf3ac7a1e9f4971d5adba2e87ff2ec294e5614a13af54abd555ab8e1cc999f156ac12c57ba24af54f65d23eed2b1eb296c989bb8be1cd1fb69de7f5d891449d996dd799ddd852802325adb5de459024f22caea719a47dd68e76a1b5244c8cccd85058178d39b5309919b68676971629ad8bbe59b735f60d4e8d32e7a26a6ca4b2a43d776c0798230a3ad67ab7b73f1b635fa9d67a3752a0d4e2c6e239d27ed5aecfce6c29c27ea2dbd9e1e1b13c2c4bc3da789eb5b136d6c65a1a3468dcd01c14c75a8a4371280ea55ecd31d27ed52e878e71c70e156650d6772ad441374912b4a75fe42661000e6839dc96853789114916407bfa3f6e9223684fdfc74d72447b72b85536c395e126d17c64471ec34da201c98e1c86178edc853b4488532772872871ea2ddc215866124a57c8140751e07e123e324a77648a0307e07e1227e84f0f790e45370928b3e10e3193cc684f43cdf88ce7108827209c80d80284121057007103220b209610b0829af711c054c37a9fb2cefa0d2924880944d396d206c9a559ea9f3e0d9706675d7dc33fcf1407b332761c92283c0e39a413fa8a1ce2c34482fb719fcc68cf11d671a3b9a24f25dad31fc0cdc7e743260f6e388721b762167cc4783cfb20ed40a23d56d0d868b0b85c392339430f5d24857458e8e18dfd3cf3d4f00c9f4297a6467bfab14b83850669e8d25c81aa4194eab0f1b6416760633c7271308f8efc082a33190e34d0709c10d05700aaa04bfd0a800d0767614772210e2ee338e1be906bc489f6f46fb8469aacab4f748d7cd1673fc835c2d4679f856bc4d6675f768d085d234a7df6f13512802a6c006c30ef1fc97559a6cc1cb6122e6fc395e14986a02b03973efb355c19a0acabff21d3c82d06e591fb644677b40a4597865b22ddfcb825adcfbe8f5b56e933bce511905b1a417b90acab6fc32d6dfdd3ff71938aeea3c81475c65a1e471205dd186b11cafcdbce18bba98b35dc23606ab8683d5b7956a91aee71d6d50fbae7c235b69863ec2187ece0d403708fb208fd082701b828741fbb35b098638c5d1ad892434e9cfa91fb5bffc4ee0daee136396a5707994d66302855ffba52db856f1c9a39003865261d390db7a45957bfe89635ebeac76e0bffb6e33b9e5976c7ed88b9e339a4c1a34ad9f0b06a7842906646c6e775258f6d5b6156941cc59b558fdfbc9cd1588f2e70ae18e8d9f302f75340af37c3fef53029ed0132f3ebdf2fc5af4bf1cfcd218f9bcb15ee930ee752859bbfe3661a25dcf7b999e602b8afe3669a2c703fc7cd63cfcd3448b88fe3e690e7e6efdcdc60b86ea69902f7cf9b696cb89fc2cdac1b373fd338917bcb939acfdc61e43206b97ce60ee349c5b99ce17eebe692c6cdb4085fe413678a8359a645e002f76b6ea645d801eecfb89916212af7964c5fb003dc975103a8dc5b626ea6602c21b3722f6175736fc199b6e029d3221801f7c79b2fdedc60e04c8730c37dd56fb8bff0d6cc4485c3d534bb1fbe1bee4c262a1cb62655026964a40b904427f64d8f86ebeb15a57dedceab4cbc5a17af5651008423af767bd1c64851d6dbd9aceb6e4450504dc600e227dcdd6d6bd812596b3b72c8d7b8c6755de791433cef238774d5863f901c02d384439228ac1f92732bc47688379bc56429bb3e2d3924a4b5edaabb6776b736419baa0977d7755d05ea13a85dfd168898cd660bc09d29d4afead1e82b7cd7bdbb99f50a016129eb330f1e7df6d71e5388a3f3e9deb652ec3cda2b3b000c9aa928dad3ef177458e0ec31611e4042b59a5e67b688d8d71fbc1af6e9b36918d89a1d0e1c0dbb695787edeaab9abc16d4a61ba30dd650a3701bcff5415a4001059dd7b7ce6f3cd7aa739dfb503fd442bf6af80c836b0eae2b5c6be04ae28a83eb0dae2a5c69e09ac2d50683b8d6e0fae13a03d74a836bb5b869cc7b8dc15506d7fabdd6bcb670de20f54341fa2d741d6aa1a260ea98337db60d9d0694d907b74fbf9a360109f52ba3f0aaf3219df75bd0316d7994a59a679f8d8289a39b4a9f16fa050443d6c17d1f6b7a3ae67d4e0d9c9b160d1b56cd0c9a19197526035ccb7a9998fa12a67e45d68fa2aa3e157eb7deaeb7f506031c1aa5505330f8da76f7bd3fcfebbcafab5fa5e207862955155df8aa522128bec3e276739dfe4019fb811f188617044122c07fdf876cd7755d7829df4b2cda91f6789ff53c0f14c391d6b69dd71f387637db775eae6377c5d4d8795df789a1edc2d07eefbe303563c390be53594b555445559476a22802219555c49d19365269474ae98a01624351d66f55a96be535218a63df5cadbdd6daeefe486db54874777fdfe779dff77ddff77ddfbbaed556c05a6b6bada9063db07d64157cbdd43b65f534960c5fda65d5abb57ea8adfd58ced4004496c23e74240e3d93517bd152db85ed23a3e1e9054f69a78cc6925dda65f4b4ebdaf443ed4521b603da0130dd1522d5a132b5b430f02d03fbe0819716ca520faf099e16ca5466f8d18064e155f7288950169e464478f07b102c6cc919402c040cf61e0488ade983f75d0af8128b6d5a994ee57d1dd881d6be9a35d82f5f443b7e20587e4f74fd2521ec6e4c569f7d5a49cddc2a42adabded09e3ad4f48ef68892fe0362aeddb46d63ebadaa9542a43a95c495c439c431880102da43711e5f3188a1053d29ee256c01010d68b14414616c9135494bb24a11bfafbec5dbd730da31f5ef956661e13ff249aa93a6ea539dcac2d4c9d7108627c31741d62af8bb18a6561fea549d599893767dff5629277d660a85bfeffb48f32bc2b2aeb2cfcf2c224bf722ecc5b759442ecd96f7b0a83f9a2dcf2ca27aaa6fcbfb28debe687c5f7ccb88bdf8a2fe784a7657bcd4d61294b4cefafc6e5ddf5397655ddf1265a5f5f91dfc5e8fca5a6b600f78c08b5e9de9aaedae1d4dd5b041fb759e57c31c83eb58d29efa9b7e75b857e0440e4880edbdafad949d0aa7c0990fa7f0978bc09f59d21e0f9b39c441d42f334d9479a6cfcaa23d75e6bf19290ce807610e0ec38359ed6a1ecc84426ee1faef5906ae17bfd71884808b41085b5017c5f51e0ff63c2898d29e6f48f5fecdccccb0700c608c4175501c031846a027c5df3dd06c3dc4e067a6684f3d0bc030428969ad35a40d2bbfe08f0ce8967216a999a270550a9f7065834fd87637db2d2dd5b0fd538ff5b3b5a71c88c3f6d66b9cad59248b0485ed1b673ba8c6355333d9b03d94ad36a8a828dcb75d6db62a55257b1a5fa54e37dc9920499b30a5f5e1d308824fa4fdf23a100471984aa5cc20400c662ab8fb70384369fcddeb2e0758300b724bb89528fb9624d220a83abc5b7b7ba9ea68d3bb0d82aaa36f1b4665f56dbb695fefb5ec7bb7defa6c22b0f7beb83e9b48886de3b211f6ec830031d10dfb24a20fab970331a4dd430aa31ea53456434cad8b7618b258dd1907d3eaddd01e6a5ffdaeb3b69b422c71b70cdc2d7b1df885fbb6ac59694f15c1154b960b30b07d8be008dbd0de11b7bd32b8ed9dc1ddf6aa707725711b75ed1c9020085e6e576c60890815caae0c7359b27a85eb3f0afb280deaaa8e7adb6789295972a0e959373406dac83d98fe607a1c0abb4c752b75794fa5b01723bb850d59977d183ee76003f7872c8ec8f19715d0d5da7dfae47567c4676e5833333737ac9919d6cdcc0d1277804875c453b36524f5f045296a52eb328b545352579946b26a66281da6afe00d4a96859dd6557f9f3087d8070765f8d473883b5b61aa2e2566ca347483f4d9f7c02841f39f892315d2a6afed0944ef54fcea9db1307b7a5aef258d89d80bc2de5916664befd6f7feb36479370b13cd9498baea35f445b308fbbeca2c224b11e153a7b2ef5da73e334925ebda5e56d94c94dd2bb65fcb887dbf28f5f0d6a7c7c525a2fa50b7175e2af36e16e6d9549ecd7be8d9faf4ee79ff59b252b7f5fdb63eb3f5bdc83ef5a27e78fb81adcf34629f3a0ef62913877e6896a0a4b87bf570b6c325885427c4f43d5e128bded8d7068b762cb5a0803746018697f6d89453181ca071e076c52fc6b1c9b1af8c0656e558ef19034361564994f5de3d71d7cafc5b984ae8b4df930865a9d7c8a06cc109b03dad3fb0f5818f6840b2d4930865e093f09185179f4b2dde092aac0619e1ecb5e0046a1775d19fb006fcc1150bdd155699af1ece63f2b4dcbbd19e450d6a338956ca52b7f7b4484aa59ebd0f845ba89e55387bb7ec85800b4f04d54c523d7b23503d7b5d5c5b6355f8aad4908abb7a9dd3f3bc9497c262555d1b596a4c65f12e7a75f4da8e5ecbf3ccf6a0bc7858d8b08705539419184ae985a3971a3dd55859d08107e0ee33f8bb288ede38d6dbddbb1f49576d7adfb51755a355616b0f647fe2fc13dba76e55b7e393c48fa668ad6dd58b3792a96eab9aa239657cea53c4ab6e242349a09fe33dadcb5e041fdeaa9e7a91ea29734a96fa29e055af6651ea53c2370d7bd94b5d5ac5cbeaf5b488d65a1fa09f26b6efc15261c7d29e83f7f5bcd7d4350d9448ed29ec00d9e6009e995338d39c35abac33b30d26bf8b5f509aa4caa918b32b55b314132729264e96e49c2cc9657102f4a438830f48dd24274cc49c0adb3b01534b98c0e05ab1f479185f2ce4a4a06713a338d2e0199998264c26edeafa45118d51d812ebeaaf60ca1819193358427389926e0553c6c8c89801471a1206520814277dd67be1b377247db60d4a6ce8b3bb0f43bf2e9429ca4c9db8e0be122f88855c959085dc1175516ccdec3dbce85d254828ced4e9aff07d252a1b9034ac5a4fa684c1f526d12e539d89937e555a82dc91ea3924edead76b43957249c354ef3b51dd254b649a5b9531323248186646901c32624a7bbc7795f6cabed589a1f79cf27ca8cb3bad6d3b2f87a118aa56990a062f8ee4908a433343c1a049698f67e6108b2269bf5a62bd25d633739d5933d3a8ce6c000c9080eb3b1ae28e1cbf4be411d9ee29af5cb2a45ff69dcbe57ea8302aebe7ae26f1042ccb3409b8cd1696c8d193e26ab6b00417aa83e2dce16ad2f2c3b4a7da58200c4cc95bc182022d4e4a68d182232038601283892c4f78e124a334f0c2011a0f38e0041e3071838b2b52f0012d54200610aa103e63ad65cdccdc84e293b84cf5d0de1b96cd125e75b37c17bfa7bea7be535a7e0743229fd9991d498150563cc3bae9c817a80c3020030c746177468853745d67bbeb68d8afca6285e19f533b4bab5dd77da813e069e1515514ddd9759dd7751d092c8df6d0d72eca7a6aad145dd7750ec09ed94d33f0baaeb36996ade96678e1fb34df0c28239449c5a84a11665c91e46a84114b5518ca84a18c309c09439a309c11863561c8b209431a614863654fc1af69745d67bbaeebaeebbaaeeb7a5079211443d35285d0964f2cfb9ce91768f669f80c84ed0e6c5de5f3c5f63614c6b2aebe1547f22b98dbb3682ce6e0676a5409f6d92c1b96660e532cd690edd453a9f7c07608fe7e5c5eec68ed558cc9a23dd6a634b30160ee0c3d594b56b79c6142deb1741259ea029a10aaa1ddd05a4caddbbdf7de7baf699a28bca6a6c60903915499b8b843e5265954ce663393044027f2a430d6286e2c675d312a3756afdcd817ed22b1143306460c876f6a1354fa35e5fda05a10521096205b90154204dd829682a8c0fda029625c1a769445caade4a45c1e32339f9a194c336e3838383838383838356ad4c8b9cc4c86265393416ac240bc66e5c640592962ba34ec284bf86ac58c81ea970a87a932cf7c62250bab4dd4082c595865220781261b1b1b1b1b1b1b1b1a3468b45ea758da19ebeca270176844a074e8c03366cc983163c68c9a9a1ad68970968889a889c8c9c2aa959e226e832412c2c8a8885392c8e8432da6d038629e8e6897d528c2c784d1b098a77e09f12d5508f3a7690265d0ac5f421cf5ab887e19bd5a318fb23c893db937acd18925c44daa4e8c5fc430c534c53859580cad84922143860c193264c8989999a1f9e8347e313635ac3e91e3064924850a157167ac57ee88b3a3ead58a39e2fa15ce804b36c02bc625314a7c2acbb22ccbb28c898991390882e015e04d4711cafc23ee18c34b63384c85fe34c5b434ec284b9d3d8923ea17590cd72f291f7dc66ae55628eec413558a1b34b3aea02a3748881bf4e40661e9b3bf430ad181ab116610ed06650669e0126003401a5805ac81574024100b6853ddacc20d516129efb0671fd41705cebe1b8d2180244992244972b55ac1fc27063db3b06a45015bfa358453bfa2fc3cfd84f193a308e049a14b97281ca3152be629c50c9ad9a0cc3587fb35d72f29ef572e2dde48c1559bac28d5a62b4a504041e1b464c992254b962c597280031c20bf5a51b272b3b2640597428e5a1b7069d5e1c3c58da58ac46ca92a310b79e0e14b0dbed8e0cb0dbe80f105075f72f045075f9e8c64603403231a7c71327a81110c8c6260b4c5c8c9c8e9cb93161b38023c575c3d8aa23d7d1d52480d5c8f941ac0d480db0e3becb0c30e3becb08312254a04f08ac40c091a123524900670a327a3262327a39c11172328a32e4651465efaf5645667b42f42c07da320ecb8424789094c4a464ca10e3a8c318131283046156358314605c6b0c01849632845814094084491c018b62853447940142aa26089628b621b43290b146e499eeb2d0138530a94518d4ad40d37dc70c30d37dc70030e38e090c313c09480a6043825e009c9a32845418a628b728b924594a5285c44c145d11285294ad3181dc0fd2858a880c54482aa2145a98536d8204610c410430c2188c1458c21884104318a200654f641fe41068218b9bc83cc83dc831c46cee59c1850565c5b009eabcd09ed402c417182823b72e4c89123478e1c292a2aa2e14e6a4e909cd89c28d5f00c959f722e47652f3ecc7c38f281e643151f6a3e2089d104dccf4b5861acb35bab48312b8dcacd4e4a415734ac4a6161358a3f8718862b87810a7af6b3c5a312487d2a41b3b07ac58ce5ac0ba95fb50aa656820afaaa55ac80e6a01aee29cc9c12342bf74707d4f543832a56405f3f31a0b50aa67efd34f5a9839f1cfce0e0078c9f1bfcd8e0a7063f4e3f3488e52c2c86fb99c18f0c7eb6fcc480c6ea02deff81018d5523deff79c14f138d3d792c96c36108e59f1fe0fecf53ff10a584311c000010c311f184c513079e3af094c593079eb478fa00ee1b2d592559946840c9069e6ef8c9557201daa30403465c2519a03dfdba80abe40ada6394041465083389082947c01da2c452ae803b040b4b31936a14a6929b757ddfcab4c2c4c53a525172b3f2b4d4c415219e536b462ec4eda189f6f46b90426ae01eb22dd3628821861862882186186490418619de83530f4f3de47a8082c240bcc8952c5998122525372538255a943029f9424953bf84785fc9967e3d795f899385d52aef2b79b2b0bac45309705f891722d018d6d97d48222954a8d4d9294944e5435d831c92c242987546bb4f5e197085784de2427945e246f9ad4cbc46e0d6da2b046e13af58ee103faa513c89507604f8e5cb1759961f5e91b84f82a032c315898bd45adb49b589ef90dba1890811224488102142c405175c78e13928e570cb61298b022ebec62ac50d3a6a5743f16a2589a099851d552b6696297df693b86c0729248543938821c7018703130e4d38cc6ec805091224489020418210111109390d4b34e06860a2a1e967ac579cb85843059c5e651b705275e2e20dce0abdca08c02a7349ae576e53dce88e7e458aaa484f3f7efcf8f1e3c78f1f408000197abd72b3b0fac49285552770f50a130e29a486949bc2a28f1f546e3d7ab5722bd2ab14b73ae0958a5b97787dc0ad555ea7b83f5bdaf5e488ff34595895e2ca0d62401437a81e1171282cacfa218867a1f27ac4adb35331a1b0b01329541e6485c68a783fa8091a337a3f88091a03e2fda02a3406e5fda025682ccafb414ad0d810ef0735208816c480a0248290a0b11fde0f3aa2b120de0f3a82c69cbc1fb4001aabb3f7838ca03129ef07cd688c4a8de239480adc0faaf58f1a05d2ad565e1d708f866ed501172b02a6c07d053c00f787a002f7a360c17d2810c07d202280fb4612c0fd226c1dc41d8ae2873b24051177c8012a1c12953be404ede94bb9434fccee10144eeed0952280a8566e7493684050a298568e00d515708728b1ea43dc2158586526552be610927525d5274e0b65158a2b7728bc1fae51509ced931d51bfc8ea1397e21e65a90b30910451bfe028af469836d4a0149fa9530d1c2447846461b589f7896c16566bef1329595865e27da21b8d29e07da22cfa35c4fb444bfd8af23e1117fd82f2fee1999aa003b84f84050dfae1fd204e348680f783e0688c88370ce6e3739ec7e1f331d0330b03019d52580e3110100c76f20c837b38790e710f27ef51c3413cf6219b855528de1faa59587de2fd21240bab4ebc3fa46461d5cafba2d068a55ab9f833aae043f4a1e3c3f3814267bb068e0f1f3e6e7cb468f8a83e7cf8f0417f7c564d1ec5ad50bc5eb9d589d7d94daa4fdce82655284c2a4f984538614ea9351309d72be052da93804b698f945bad5c222ea53d08b84dd498b0e1fa216e7d142859e07a212eedf309175596b8d4ba54415c2737775be4ee05b81e889bbda6cce4df7ff032a0237b17c0b589e9c3bf33b914fb7029ce37ff004bac28e1be016e7e017a50c286fb3cdccc22c0cde700be34e1be006ebe921d9098e1be0e37b3f289640ca47c1c6e6002536605e0e6b306319e709f869b5f7404aa86fb00b89965e4e6b3087d52ca9721061f38e13e0c41b3cc7281c80d946ce084fb436e66e5130591c112eeb370334b869b90707fe8e603b9b9c3e247bd72cbacdc5d2088aa2087fbb19bdfe3e60e0ba09bbb14c06e0e5f3f11c0c570413c4b2c1f1d3e4cb89fa3e7da583c3b376eb8ef62e17c53bba9ddd43a9c83747e9a32cbbc3534209c8366efb0c091c914877350d84d6153a7587a874597029ca90970e18c8cd189d55da0f302677029eca620c7205c0df7f71475aa55d030aa0de80a813070d55a227495f6f46ffa54e5fa6ca83ebbaeeb4c151753d5a54f1dcfe89e063737e2027077f742c80edc999f3995aaa83efb5995537981c1abcaa93a54e5745cb8ebbfcf0b570dbbb443e87ca8e6687d707d52713e8d173b7035ef5466307fd6652634d6e19044aa9377a054b88675a7f455e5a9ea2b11ca7af0962a958a62d567544e2a959999a82e8a4d83b2f3ae3233751ab203871f6ad00b77907690682c543d75d52d29c4078726a53daa5a31b883e4bd8addad587c25894493962a1ca699a2962a7c7fa04ed807a7213bb02a751fac32739c68cf0ccaacca71aaf566eae48355e6907d2a47e584bbd26e47553f22f4a04ae81a76940528755a4ba05b2af5dd860054451a3254943dfe1de84d120199b4ecd11de8861be86132f96ed87b77a0fb01ddefa91e406636c2404fa55e8950a684a40e1ee8fea0c2611f5207dfe3faf0bd9a30d85f2f265c73f6d639ab44c3b3c5c1903cdb25d891678bb4c3b3b535815ec1517da69ead135e70ed8267cd25c8b39dbd889eed11ec86674b8b0979b635169edb49f6dc60083df7137eee30869e3b876bead907f73890e78a6b97c37358f45cc26c7866c59e8f839e756a78eac78f949969e08eb2fcf0f1ce59188e92753581be704240cf8eea17ce52db9985e1e4b09de1d8198e13b6e106400cc03d3a42b1088e4428c52e9ebe602840af98922fb0309059ca4e4922164ec921176bf9e39424f2f14314c5cebb01ea479475751728a62fb6dc00258aff61e6521c3fa3d5ae20c510a154e1702e67a238440808514ca23a18e4801095cb819e939353f891422250660a04260330512030cdcc868e7284b4a101dc7cfca8012a080896b1500314911964e6308809c4cc2536334b66e6b360e69306361898b286a6c04906272d4c34054e544b59c6621769edb6ac275ca30952139b9126331c388e703992bb01ea862e3744f52be62368c88160190b411ec66048119a0ba13911cd8398f904820bba4170b4078899c3109bb92c656666b158304333761b0282c33dcfb15b4c8ccc584b19c268c1d3eb3324910c4f7d768f1e30180c067b9d92443d4c1972af2b43187d368d506924675d4f4e5b9a94ae30928591a599999d9d1d9e671c27199eac2b07d5e5862eed1212c220463372eb97501896258bf5e713af7cb0ccb30f967906c2ab1f5b181be2f1fc63c33f3ed440801c08e87508a58f53920848d1cd0224876f1c899a21814212852487e4090918489c90dcb258e242860c1a1a9a146e80ba21ea062fb321166440ee0d5137749119f2a3c9ac5f3ede3f62407011214441828a08210a1254023181f041c2f70789f6f487dc211d16b29539e4e084739e69e08a8a8edc23b923384a7f9f1e49141228245c90e4906841c284e40b244d7d764cccbdd7cce1de00655d51d685c30c071a0e5570a85924cc8289042783b2f52647fd1aa2f5eb47bf621885531a540354d5d147a186a51a7201b0e9e8e8e8e8e804c026232323232353fef80c4934f419724887854ec9213c359f9a8d97a82d4e603ce1e0e0d46842b3ae26b526484db0f469238500d1c403a6190f98686848a21f426636c24207224229f4a1166b6aaaedc74da221cac98c67665d4040019103220c209e96b8c06961eab36db8b4cb8a8e130292a4f19206ae55c36d42e3396a58d1fb3cb3a27b43172a64de0015815286a77efd785f8630fa35f4be0cb97e09bd0f4453bf5878dfc98cc67c04790e839e4b20cf2c2c839f8f6564cf27960962e670f52033972cbc3a1033b38e57c766feea32339f4d446f22164c23b78e41996d44f9b83b51443672b5f24a89d4a78d28dcb791b391c35d0e7d0649143e163b101010508f1e40403d7a00f5b8f9c4401fb24077e894248a7da857e410211388a63fd9786a72d484860b430a69fa5353934c69b3d9624821b55a4d063924cc9fe1fe6c85f757f8ec8eb10b80075d23afe126f98f9b7494c39308653a1c0992fbd0e1384a163680db004a30005c006c31251ca60670b3b01aa0acab04f4858015d001e0fa8500a63e3b66e630a434b0d5000a88dc0e1708a8a2db8401b48915eaeaeb50241504e4599e3c8152fe381416f67128218ebd89151a9be1fd264dd0580cef376182c66478bf49151a83e1fd264bd0d80bef375182c65a78bf4903688cc8fb4d6834e6c2fb4d18d024091a2bf27e1324682ce8fd26473406e4fd2647d0988ff79b2c80c67ebcdfc4081a8bbddf644663a10fdaafe02e7aa6439002f79bd4fa47d1fb22006e12a43efb466e921cae97e41e25b1d11d9d2487dbb9d000dc27a204eeb7b004eebf5005f7616002f7656802f763b082fb33d4700ed702b94990a03dfd483a725431e3b5e4794d2d799c92443b176b46ef123dbc4244f09ab8c6eda3d9acab346d85deb9d2b5aee13e0f0e1a2ea4466a2bc8b6b5add5a23182f7f2708f34ab59570e13ede9a26ce1542fb4cf1659a3a25a6f55b15994b4cf9b1cb385a54f1776992d5a074ada678d2bc373ee0c77dd18ce7361f8ce75e138ee0be771897c85db82cf1df25f23d7718b3cbc3486168cdc1ca62237674986db3a8ae1b660b8ad176ecb85db22725b482ddc16969c1cdf905b69cff71c92a875f15e8e5b0383b7066e99f61e491482172707bc37a78171dc241d6ed626ebba645265b22ea6fae4e454a16e2ec52d1b1cf6f01a15975497acab511851508d2cfc8128ccd0b919c14b836de0cca8cda8f52b3ff71c0a423b2ebe0a57f615aecd79dc9fe3b83fbe735fe7a1f19cdbe3352ed079b84b4e807b808357060d1bda1202b8f906b85556805b6d49f55614c8ea23c9ede1e11572ef12bd7583fce6b270035c2a0b7d8407b857bacdba647846f409f500a251bb8284a54fd08b92f699e3c6aee306fdd7dee70a7dc7f57115eed057b8df795c1ac7715fdfb93fe7b93feeba36cfb940af716107af0ceeb9493afcdea41d747868f3edd08335a3d5906c3fd2fd95d7faa409ddcf826ece52ece6dc6464c850f5306fcc96e999614e2637b3f951fbb9ad2bb785d427965b9d7b6fc404c56e5a1e88877c0805c56e5a1e78c1eb834d338511e7ca9ee3fa18bacfc54241b19b9627c3433e84ccace33c7f16ab2cb16792423c5ce2f80e49c4f31d7248875d17e9ce98e4f670f0c2ac405be226fc770aa405b63c5ae5b4df96a0852e1ad721896cae430ee95c23cec5cf7185aee3dafffab8cf1dfa00ae012e80bbc30b7075f8aec2b579cffd59e186e7717f1cc705face859de7f6b8ebbecec33dc00970977c855f61921b121541b19b96f79c534e0dace345c74b139a40633427ca87d06f41e66f31f3b71bf3b796e999bf81e66fcfe11eb7750454a555b3e14f789392d4b468d635b3b9ada3032c31c00e3a78b8963b8e83240a8f831cd2e11e12df9bd4430f4dfd27504adaf269cd789e516b7db715ded6d16dd15a3ddc160fb785d4b2b5945a05b82d1ca4100ff798ad19ae1c71cf4deae1f726e9f01df7b5fb2bf733dad3c3c31ecc27b9b450a6837924eba2a1790f531725edd3137f7398684f5fc7cd59929121432523232323233383c595735bb3a32ab52b4839728037856bdc7a349bd52ab44aab3ac85a036f8873a7f89044f08ce005718dcb83929ccb6409ce0ed209d48757b814eff0d041051c666b0604d442d29075d9605d7d15c8227daa40be603110202a883a749044f6e0f5c1e28e11e7dae7b83daee3c6fefb739f1bf47b697cc715ba0ad7c757b8aff3b8b0bbee8fe7dcef35aecd935c2a03af877b6e5292240f7fbc90a8f8077ea32d01f4ace3e6f96c0969cd6ccc703feb78f1426334c74b929b03450552acc73ff0db123420f3c66c999e199aa0d99abd6eebe807a4716d42f073591e632d73ce8324c2b9d81ac1db619f9b13457b7a85833158bd0f36addeffcdc1d19e7e8e9b033ac1a8aa284b18d009067482019d604027189898e750a975c5755bb3db3ac2715bb4dbaac2e3b66a544736ea016dce6d1df15861e47151f80ad7bc0a3785efb8e793dc1eee736f5cc7b5f11c57e7e0b5b8e7262979ce4d4a721c376987f3707f23c0fdd200ee7102b867b2aebe779368a14cc94325af91e43c3a7c6787dfdc2142ec32937478eb0e1162f36053bf6ccc3a06f465e3087a824e16f69c750085d9b0426b166683665db67ed940eab30afab241053d5bc779fe2c5659824e79464dc9cd81ba3929dc1c2f3939dad3b771446a967adfc6cdd1b93948391b34f17d1b54f46d208d3a749ce7b12aa77a4e957b0e37f93ca3d69af190426ae09b39b71194e0edbd95f680e72189bc8b3868ed55f85b03d3873bdc92e8db09bf43b26e16d60ac3b2fc2185e8c02d9a0892fc90e0ed17782b0e6139b755e3b68070ccd68c8bb23eb77af4e080f1c04c58cd0c1ad88c0c180c7c924b65e147c3cc46d8e6d6c6a4ac9b5b4aada5d68d68d972b6c849410b29e702395ee42ca145cb9922670239078b9c20b4d3b7a55fd6a95f2018a1d981adc2bd4077508224c01bde9a3fa87018e4c2e5fb3e10043bf0a02deaa24b1710b45ebe2eb80b089a455d74b19db52d1aeed6ac5d6d6d51175c72ad19ee2f6cdbb0618a021518a31cba22b5004af76c369b416956231db44c38c2224da9c63014492c3e9e3f25a1e76f02b8fb37058585b7fc5d017afeaaf478fe8e3c2f5e17dcdde3426121937521d1988e77f772f4e5f3aebb5da230fcee164761b277af677bfb79b617e0f16c27b0c2b39d0277b74e509888f4caf6c8f5dc5e76a8d0bdc3a03011675d340a1b7a775ba3311cefde4ff4d5f3cefe86c678602b25ebea8739debda3af9d1fef4cf08d04dec6b282127cee2bb8af34961ff7280b18de2cac6dd6c56461bd645dedc3cc3042b7b02c7b98998573c8046432c1cc7ce21c32e58ec27d11c9c23c9a75e12cccbb5957bfcc9cc3fa3133abe461e67205a450053387e50e33972e33b3441c6ecfc907b8bf52b2b05009d3a850d64526d59c75e57a46d6a83ea370dbd4279b549daccbc963e11e1cde9df1859706ec57a641a241a2315acb5518ae9648a2551844aba7fa843be686fb326a5839b8e639a7e69955f35cd6b0582c968d28bbb5a9ca2a599956297ca9ec16ce3ca9a0ec4e436bdb2b93939393a3d27159b41a271b376cd8387f162b97394c81e646a3645d3448406ebd5525255a9363215dbc78f1e2c5a73a5c2a45d6a525954a1cc91b83c350258a23b9026fd20f0ad2eea9936868686868509099c95836b0a4c112de128be348ae602eccd28d766f599665696aa13d5cda6547925cc1942b164c169ec465407e9e5218ebbc8e3be3d3b65e6a5bdbf289f3dcb99828eb62d9626e314a31b31a39357298939393a3fa6aa97fbabb3436eba241a241faff9f06a926478394b2c26623575cf4d95f9144aba53e574be5128e89a1f5d997c4716370314b31b525fa45e959c362b1582c9bd517ab25eba241a397c2b02c59ac8bbd64c3adcbae0cbe3234ebea30bc89c9dd9cd29b531f53c7cead58ea6a662905e393d3cd0a88673ef3a6c9cfac195c684f4cae81dc3a23222a8409533f6ead595768c2909526f6dc195c9f1df3989898189967cf2926675d5e64c828c39bc2a9cb839225392831a413a8f08678e722b1c129bc204edd2043e085a105a622b2c887792e0dad68580c9776751b2871e498dc8981b27b3e817c66c8172ccef1ce5472dc4a12b92ed28c5502658e09af9769909c669c684f9375f57bee8c128d7cdf6b9246d25699b56a98ffb82c2bdad5dfb92c5b9fbd5aea90ae96faaca2cc2c2eb8e85766850087606669264763a9f262299662298aa5d89931b4d5520a0c59acf0763b77b534f3645dbde2a25d7def168665c912bd5b09f0f8ec39c574b1c58436dca7e9316798caee200e9086240a6754099479a565c80d5ee1fab43c77c505ede987360b03f2bee7a47a3f54ead76aa96160544ea54d0aadeb8a92f659574b161693cb313952480e8e8142c12deb8ab2bbcd0a6fb549faf1d40f3386cb8e19933b41599f6372ac9a1934ac19192c56784cadcbcc46a13d6ff624f01b3668903c07c8d470df5c2dc56c4183b45a5a2d1de7989c67450609f769ac56aba5182f707f464cee0b9c6372bd041931391a9c637231b9150d15ad05e7985c1b01e7985c26718ec9f516d57de2e1bc5ac25dbf52af8dd46778a5911a4b5771caecc093301861d06412922f545b9312eebc77f7de993f7c373c03f5011e6ab3452488fa058737c2dd3f32ecb3bb91471285af537066c2152cf6181e5712a498863150b86372edea0f045366ee52ffc02f2687bb1b03a53569d370b370427d24091c0d47c32161c1d570b50be06ab81a161c1216dc15dc152c17c082abe168381a8e8643c2d570557057b0e090705570341c0d7784ab82a371e1d2a54b879b75e9a2d4a50b6ee605378b8ac2cdba28e1665db870e1e2a50b54146ee6058a0b971c1417123c3555c10526c1d353152c521e4a8a822660c122eda1a42868c215381b89455d38e50e3c35c266f7ee238bba70b24051174efd720ae3141775f104dea22ec2482ac1d32d5d80812939043469775d77afc420d959a0ac77ea93e2a22e9ab068c919c0a22e9a7051174f4eb8a88b2db8a80b30ca0e8337c26651176178b7a88ba73e8bba68ea9711a6b8c9a22e9a684c51175ff4498bbad8d227c5e0972f5f9e803bb3a88ba63e29b61e0944aa639b56a0562b1a169a63bbcc1eab286bab4563b43b23d0b4f8d601b556c144c5843a418049ea3b1326262d2b134a836a32a14e4ceef75dc544a552a90e9ec9f5e163727d60c284c97df0e194c69a3c754a63097818541bf0dc529ebb3a7b0ea93c8f579ecb289e59569e2fc5f369c4b30f4e2de099079010cf14f77af24cb53c204545ea629d5da3a01992ad4f2b94ac4bfc404b0e01a382519f3c266a4ddc0a2bacb84759ea16f00bcba4848dc98aad895c134d3df4d0430f3df4d0430f052840010cf0aa844d0925256e4a902b98324646c60ccd8c152ed6b06c5812880004cc2308607935e256245e17706b12ffd9d2aea32c9501b49fa609fc3409f1d713b352b941345bb32e24296e90cdba8294fabce2b6c4c5036e108e8a1bc464c31b4175f4a1b070900c2964060ecd201c9499dac009071c9e70c8e10015c4f3895bc02d04f1ccc22d04f15ce21682780e710ba741bc858b2d1935465c05bc4a71a13811e234d040030d371ab2e8d71032a0a1a808638c31c65826935d88cbc28b2015b115518aa28a5badbc5240895bc4eb046e7da2885ac52b14b752e045dcaac4eb13b74ee01398c0451b9beda294ab7ab5dda32c4c5a35558a542952bb6135cd8acc8a1c99346acc0035439719a07850d267dfc60660cc909b4169865a50505050505050909090908fbb907301ca852817c078aa41a3c9852617b6e4b4a80ab4b8a0c505261b518650c0c51b3c2e9a4f5c8a296eec890b54975cd02c965be1414704fd303ba255a95d41b259585de2fd18180b8831c59a624efdaa461415c95571ab62a90a1c05a028e0f4f3f3f3f3f3f3f3f37abd60af11c05958b572a3464e8e1386167c989872e4e059fa61faa1f180d2a143870e1d3a74e8f0f1f1d9711f269f261f2715ceb3c483e361e2f982a789670b8f130f183c4f3c399e15e03e4f0a68d00f29e4904452a85039392485c38b2e217c7865c2c407263e08f1a1ae52ae934f318519d3f2f34449214e9ac8051912c2e512e2da786e3b503b4e3b3b3b3b3b3b3b3b3c3c3c387ee37663e906aee7f72a59d8bddd2ceed2e5e2e2ae96cb749b6e0770ff6271dec5e42293ab73b1a24051b0f51657332300874c2e939b24d1cf963e3b0c99fc3431b93f4e52bc5eb93f4dd6e5647f9efaa7a530635ada25c406366c5c2734836637b55bbb486ffad28d1b376edcb871e3460a29a470fea676837463b3b06ac565c42f4934e5b7a6c21141ca879a0a15a6761dc59afaa7eb957b9445882771594c4bff0861c6704eaf5523926897d5ae2061b159a1748302f7839ca0413f3ca6a55d1d9b80943aab52984701a740a854aad68e9341ec989a111140000000b315003030140e87c4a2e170a4e9ca0714800f85ac5e6044980793248f619062c6184308000000801100c268da000213c7708f8a8415793ec03a432131e00d107f000ded1ada506550f6ce59756a7aa02a638511f40086048d988b6f924a3e00ac0553ca5fe0b845356fd845ce22592c3440b650b2cb2d0e8c790e326edfe3c2fc03737c5053855dfc521ef1e51fc2c6f8106d1cf2e9fdef4876d23f64de721dd3cb0a2573ac7d996fd408dbb20393d3d343f86ba4f6691a43383048f2c34cefbc066a47279f1cb2a96862ee2d88cb948065551d43b44f1150ba173e5364448052915502faa5fad7c93ccd215e52a9a2bd7cb844940f162b6f64502451a8e40c5ba71b5eb5b5d59a80074dd1d20f86c9a7931b8b835b0e6f5f14de9f91f3832d6fcddbb5ea607a8439213649f498057d74c396113b7dd9f68bcda020c5616fa5944b0a301d51120cf5faade7721bd97833a8b9b913df30ba7bc3f125b071700fd8c953da80641475e23156565a880d39335669da00c2e4f448904008b6ec6a207158f0ce8c0a21439052a4530c024d952f06c11f843b2ce6e14e12d5d7ca88f894e213595120d379576406a0f1ac56bb52480cfb23ee23235afb7c0c30b696ee5dc8e694079668308621f4243ea505267afcd591388820eacd5b02facbbb2e2d44e0052f35d3043292bcd03345098c99388a9bf9b93bc690a3920a36890117f0cc60f8e497efc6dea7e3fda8c9bf9b1e50590ea42614fc6977a6c18368bcdf45a301dba18d06f201740785570bd660b83a5b238bef353488b133ee03ca92a432ffa559ddd39cf176091b514218d99688d8d780abc57246b281439aaa127480f33da584c42c4e7583c1491c93ec4d0e496328b709e6c498873531038b199ca67fa71833b5f55927821809f75dbcbb92e1b515f03aaafd4d0d1c4e8f9b93383571fc017cfe656f4d5db4d2b11abab7e69407ae7cacdbe0a6da2ee66675d4591d2c375b4dcdacf47d0e02d7d3ad6aa9c65fded05537cdcd5cf9c1c30faeb1723e44f7dd635fcf73b466dd0276f353c447756326a6db082b0c3930ebca9d484f4c7cec6a0c8d05926ab8144aa77215b0d4c8c4b2805e83b6f2da361918133ad2159ad0453a8e48acda8291c1bdad4955e8d6f266d367d23943eb7756e707223370a013b1d3e8471c12547424dcad5cfbe7abad8b990f117ae444c8a18d154552d5755a43205402463ac7565b51275c34db3fe24d26a3e58ad584d038378182afe6d534138bfbc17a29308d21d592cdfda4fcca9eb083daa0ede6e0577064ba140c8f478c74871f25386c991d8c127c0797ce658613e8ed7cfa4f4350edae109d4373eb041900f3088a36aa0f04eef31a58d8f7c41b0b7cadc61298ea5672c0499c80943a894f484b8f5208ebc6786d4cbfd79fd05f258c500ff6e5b90a87d255c8908165510b6a941486a3e80eced037a33ac50fe2fa1435390782111aa20c34ca3baf1c7d71569b98426545257ef1beb1556012811e70e8ca01539e4d6b863e4230104259f697e868e861684cf15d5bb6507a5e838643df941f84d4acb39a70c694e8c77813dfe4ed7e12653b872eb27138b08e6cc64818d335554f35e9ad7a8de315d83c65bc567605054cbc93b695107b725478ea56108b375953408d542a472ff98a4c64535411a67ced6f10d7b87abd6b1478a90610519296760157179c548d10504bb644b635b1b20e4821d104540259700fc1f40586a6721e80b8627b74ab3e32f0da32a364eb80a33d051fda483c65977d96f1d6c696a324d89166999e84fa84153b18836261305c444ea4390a9002a4a0af3e4dfa9744a302493e697dba25e963152d8497d3c6cde7c2f22971b760704c4cef3a834d27301e2ee04c7ec16ce0090442d309520e304574eed3f066052afc0dffbab33a600c420234b92ed15f880dea565e1502fe2f6404d32b117b4d45f3cd90c1c32e2f1dba724d0c4c2db0e8e849aaeddb7498c573d29c727c5ae32c4be1607fc37b505f46ed4694040361b0814e23dd8706cf1b5b814929455cd2d75a395682117a0a964ea2c79ee0d5d4f8d3fdae9f18d6a50b0e7f46aa30b8cc65fcf31a0262f429b9783a4abcedf153770a8a2886b0eb16474a158b14e76746b39083f2104e10e0a3c1e093b6d96bc5803a3bf38525664acde0d800a84d0bf2574878dc3d875d2c7ed44877bffb49848991b817085fd6b7487129f23abc1a4b017c4bd902cd3cd23eb4a2a09e3b50eab2be291a30f8804511d55dbf03b9aad0b788e4688596c3c6812a3b9036a0d9cf8518819d63854e9430d9c9dc8390c58e949a18efbc448889599d635311f48178f9b29c12bc7ff7807bf116f39b912553307265f6d6b14539f91b667aa36b1734412048d9a52371ed3863d4261e09a804da9126024615e0e9b5e73ba1adbcb55d7df7d1d1beec751fe4ba9227545c3f4cf3fc64e26affb0ca7155c33ad27498716a80469b9db770dd81a7be3b9e8f6ed83e80d562c00e3eee80b31ca45b8639e826f304af2907751de5fa5835ee983fe9c25dea6331663acea19ca446bedcca137eed78d497b8a279410afb5cf887bab02e923a9449ddfd2e58235ed869fb51a1a5166e2f84eefd9a1653c1cad4a2d2d46859bd4132e5ebf4cd35b28917c0062ffdfdf12753c7da9298739b6e5bf178c835ea404de2ff9823a30e03e53f100e7fd25fa649b46a5b3e71ea8f16a00f917d6b0e50949dbef0eb8380ec2e85f5205518e187323bb64186d8c33a041ed95d4a08cfce655a99f39052ddca63973aef4ed35616901e0afd8f0a68e899ffb51f930ccd300af899436210fda5160b014bbcd036b1dc912040bdf8c9ba7d93227b46980aff91654a6eae613791f18b1bd4e9794d4e78501991d77f700003a9c81441dac04d20bacb09d7bbd06f7411a240b064fc5e321c0b3e4ed0703464cd01be0b51c1cb933941ea1c8d4d7aca7140cbbd7c8d9f1e501af69e7a33aa8e4c285b1cee99e842346c94451931401304fcd4dca0dd966a88e1891e4343845e1f095df7b0455024b31022affd3006c80d1a2862252353f40a450857ca14a8872cf1beec177ee99341c8e5a51fbabb1f0cae6abf126acee9651500d7b108321fe3cad141cf47895cfb86c71874631b4f0ec2db3f8969f0ffec6f1f40504aff5b56cad5fabbcd6678be6ac786f6f847de4ffe1b6cf60e16eea16b4be80475721fc8fffd542c033df0594f226e901f09ec97501df5b16fe3f9728e16ff0cd07e71fd2b767f643d74d5104f95a5fd194fde03282022dd5bcf1cfbe0902d0e6f1459d77cf15fe3647698e6b1c76dca6c902bf8ccbca14e39f593117c42c058d1b160b72517e3b551a0a8f71a7863d2ff5a8c61f88afa37272079ae742c51dffff40d0434ede3351bb98d677bf168a0acad58cdf88ed72b08071320f34217617a4807777dc557aa3c2d2030d8ac5205a2a4834daa65704207b2e826167268102eb29b591f22601ee3ea499b5903d0aebc7ec48d5625d2a311c64924d81701a3ff40717c482862630e825b17d719780a533bbe2b4106a43f209593d1bd639a4ee24ba96fe1a17df9389054d2a25b64e1378009c3cef12fec5697ae1e9db9989e658f6764a6dc7c22c43acb0af2141a69cd5085aa774dc32330bc3658788045d21ff20c74a833abaef162e091540cf0b20518837130c80b91e4ebf9e2c49957d83a1dd21b9c5c743b923ff7c15aa7501a12eef94c5ab3fed62d08a5dcf603f0cc660e5bec0c791e7f3d1cfd267d2f7d8dc0f400ed1e1e223d45558f7253ba19271b94b4a1cbe9bf1a85ddbb9093d3a878f47378b76843a229c8d75c316949dadb35e247975c081fc353c8b14d8adffa7436f91f131735189ed1bd08e8e18b2be53f446dcdc1f1b910c79e04130ff47b6888d4a28d83b1f3a66985988d2d38082f859a06718d893749d487b040eed9099ff07fb2b0639a4e679ffa18f49417c95e1644a0cb1f17b5f37d596925fa928370c4745462a954201b02d111b0f63b943da5ea3e51a7c24fa0a85a8748ec4cb8c33f3c211d08ff320a64cd37ba6dbd108e786a7d054332de00e446daf1f8553ab9785e4836761eadcbaed735caf75784edc343dc0f76a76016435133d5a8311931d4ab02f213d8fba78928876aff48717371d6d2789f89ed2082e9f919801c45ebe88fa399571ecb9e010f0d50fece70d4dabf43f75d75d8e7cb8d6a6086f2a11fb59380b87f68e6b283aa9b6a2db18059e2dda22c9674f4751eedc96eec38031239d2f34a639b30230692f01010cafdfa6c05c4f051253a0214042fc1bcb97509c6851975dbe45cfe7efaf2b9a2a243fd5f5dab7c38c52472d134e9ecf57d60c3e434b0d42dde0f2d090df6eb050ccc007b4c115e89e87b5bb2c0e40d7a087d443f01f806a2a3762dfb540fd2f2a05a31f42743f5bd511b1edf00c66daf672c1a03c4cb1f577dd609fe510d5bd3be201a2905df1435006df727bdd53cc76e65bb13d2f642c3131256a2542329324866069841ac0b058f9c728f93bd22d42eeced609be4ea7faf1f2c723ba9cfd37116998e257b3e960fd53a849543c488dac893c118644d8e0629459e2b518c24686ae8809eee99f10b3c9c4d44ed41513641048911cfec658e4de752a97beba20781c923e90172ac1d2169c26ca730b74de59e09d47c6936f485a30c2c1b225b2ee066cb703c5f762abc08311a4e441349bd5c6d09fbf6e4fd022819a353ae3e1c91c7d3c201994fd08ee03c9d70ea3e39ad8cb4c289e4e50fecf7965ceec05a4ce40313832706caf490e363d14c86abd120732675c2f7667c7ae07084ae1a988443a8f524161a1a43ea7f3de052d500993dee678ea469b12cb7773a8016b8f7a97cac81839d29fb227de10ebf930464c717505010bd08208965984d58c513fa34dc5a6c54b9c16365e565523602ba2e4b792b8ae36497e1ba8f2ead8f7da9c76d464b4148f4f6096d318e306cf05a367c1e83b3d9c9a329a99d32e30c37194a2534f14858610046a609ff200208349259bbd519c0e748786a36d3cd3f301fbfbf0a03070f56d91e0d0e27bd8e7ae31affc649b234c37533b79490849a5718ceaf8d422eb5afeffde0481b6767bc0a90e201a15fae479d3172fdbfcf73a172b3f7d75f4aff00d321540269de7619bcaf44050510b1707456f9328d95315f54e8026ec440b81903212a6e12905e04e4438cdb0d16adb051d1cb51219dfb693043eb2dcb8032979ca90075729b58c71a51969bf77d2fcda1919cee87c955ef20d6dddf2b8aee588e50550e40540c82fa018638d0ea484529cffb7d866f51e9c11dd2f5cc3a1439f17c9f89ce77d4b9313c01ca60fc0a22c09b10ea885d676af006c08e1ad641f8bb4e3a0f791e2a8133c2160b9afb3d4530b18bed49741fd84a2f8d8d41c018173da8f7924b9e4f7e14038caa9c86886d520451fce0b2fba06d5b279e0f2d9bbe1170f76da2e2f81c34e95f060dbc8b61813b76c9e34311bea0b0f0d02bcb1fa66d0e0e563dad8cf7c2466541efe74640e12d67f83c64093e6d888a48a0ae0e1fb0b7e64c4c03891389669d5053b14167a4e2c5fdb8ae61263ac5a64c5e71a3594051fc031925a40757d8be8f29ab3cab486874a1a59bb84c788b102818c64e0c5609a4b4f980557166f1b9890780567991e106fa315dd7921f12c3672228c6ac487587825067858951c7251c383ac67533e6ae8616e647384f8f98e0f62497c306c774f0162ff9e64d2571cd80aeb82ba90d3eca7348eebac4753fd78a69bfaa0a5c3cf20eae81539cd4567de18f08ddab183167dbb939da44e2039c1785c645efb702a35e43c5eeab0f69a2ff61b1b943213b19d091a818850404db0fdedde625c54330b28889b7aabe3699aab5d0d7d0561198883756e7437b6dad3110e6fc4d6099b88b0a8684569fb38ab308c708e0392c36abad2a26c982feb0386b48b55aa97e64e5242c39c15b276d5f1993158348a0efb1304d0b9489ae55e9727787662cfd942cce33ed8650ef04b000321be2f47641895cb083cc44a2fcdb3d713c8bc5c12898ca0ef6e5734e8978362d4fe0b11cfc6c8d51496ae92c37f5484c92a41c63cbfef92026f645ffa474553746b775c793245fc88f9fe0651aabc02d7499ae42401ea47b6efa018e926c8e1d17fe40e629257495d16653013d9578baead9a78f16a523d84f096156c9e57e7e06b08ee294dad929c3b72b17db081d3be2063b722bd817e2b83c4e55fb16fe10f1563d95815c8e88c2b0e76543241cd81b58467594364bcb1c04f82790ad3e56df26e245a7737d6150b16e84f5c673cc9ee4325e3ae3c48efd033b1acf94d7ac1d2a1c9616350a386cd8cedca007663cdd389a94e8759ebe991701e8ae110bde27a231ed56fa3483791aa04d244992ee99a782ddcb5073d711e90443d9e61d4816327eeb0e9bae672d0fce680baaf457e708d3eb26371ce91937d569b937c55a7f6c3d54a96f38ff731e875d3b9f261e676b4aae7d4d0477bd84169d8b3b052fb37e8d3c0a04c8c70f6a2341587573b75134ed5658e686e211d965766497cb65dd09ddee7bccb80379e2df88281af0f03f6cbd4a2ee85c1599e82ee95ff9cff85de929686edcda5ccede3d90ab1edbb6d9e430e2549ae9c33516ec5cddcac69ca0c8927f9cd127c76e434ceb8d3c22131190d52a9b2accb10088570aebfe73a6e6488d35452ca0c83c0751d08eb86a173e01c5d93cf50bd64d451ca3dc8851d53cd2d3bce63ccbe6771ccceb47efc379dd645e5f78336a0f79c35759dbd4fc9a5e722880099d8e9326f2641d52e05ddc691ab2c9aeb7cb05ae37978ad084f6cc89e3bb854003e81045f142eff8cee4395279d7b4889a13b2e462fd8a8e1d01711034adeb3e38000e4bbf3f3ed858a9b7a5df53798c33d23e5724498c2be08dd6ad469c9d33e64fb20cbd69ec5fc9a720ad8498ce3040321b53c333c6b67b938bd5274fd1ce16d6665c801ead46467daca640a99abc48970178a288adbc93862e684a3962cc867af0bdbd72aa3664ff83118d828c5e364c7b254281d8dbf77fcedfc640b5054aee2a55cd0f331d74bcc353eb7a8a9fd77ca115920f53b0d59866a57faa439213d5c1d9e89370f58d777630277363b35589561dfbc6bbab3543228847a88a625a1daa71f3969f1dc75adc3979d5479f728e0627bd87557d9a8cdc657fb21107a42d1ffa5ef8f157605d3ed1e9431467b2b86d8a02cf8deafb0c1da9b47e7c5c807079be84440dc28a689df4cd87d28eaed853cfd7c8c8626a78acd727def0ee23de021fa928a686918578472a3d18ef97dbde8e1310f514bb637b68b611b74f2a159075d565c57aaa1e35e1c8f31e56ba60d28d9604b949314126251e892d19d168609947fd00b5063461fd5779ddb4bb3ff84646d340eff874c4960b86e88af3b9a1e3c332a739c77c1c440ce01a60dd5241b19caa3048b6e755550309b5ecb27c62e64d0821e4ca3d27c6f9e4d8151653b82b0ddbd2b42d3ea205ce5d34092362cde6aecdfc64704014cd2413693110c8c78286b773de30a5159d0c3bf9f9189185b253210b45a9fac641ebd1ccfa68bfae63d84698f794704ce74ad13daa8dd15b19011aacc65e0fb098c297eec84d49a300734d8583a526136124f86643aad9b6f511ccc1d7f4aa64c1f749c73db26daa63da34d61308b6581601494aa85d7d2d751bda5e12cd210c58f20e654794ed735d82dcb80ede6efabcd2080c3ed0ad42a4aa5c01b41fc31616c0f8921ca19098800a7c1654ed38a913679c42526c26bfa9ec55de13f51f6f45af55d83fbf2057eeed76933ab06f0a4d6813aaa1ff7d3400f9b1330a2212533f1116fe3c717105a902a75200680d8d15608d626d3d64055ca3acd08853563ba04555db8d9b48407868fecdf61342a4b06ccc02189dfded841f43c2e9152e8ef5662eaa3055835aafeedda78c96c3777e803018f34e2c4040301855c231c4b27a918ab09229136360960c70b40c446f62e97589bb209ba5d35f069ea4643a2147d66545e21d3a8bae44c01806b93601a983e534e286292b58191727daf8231bccbfb1c2862f1a82b6af5a3f6910b9902e8986bd1b5799e0bc0cd331a436d50836aa45fc2cf01b3891d9f3ad472757a7508e17587bd5788d78e36374fabdd9222cf171522394809f3f4adaed9a981441c8a1f497a61382d8ec1682361cb83cfdf8c68d6db48baaa967aa2dccd91d7f178e9585b178be319c37b11440b05d81042f0c1b2a38160d3504889831f0014f35af84d204bebf09c515db097a20356ee42dce5b28a0c79f57d83580731f0c1d210eb6f6ad167ae3ba15f654c797a28794ddd0afb1ba2641bda43b6d7c00403657c4892807d521170bae5b742f065fb4331d29c718c258794809aa45b061a122cb919ad9c83344bf8deb6651dd3aa12cfccbb7d5a5cd1ab12c4331fdfc11e15c333c27c0dc9111e035d668a52dbc3731bf54fc25f046590df512ac4f12d44bbad3069f0eb2b1224e043950097209d450dc598219c031c2b2a180603a538be650cbc2132cac8c3c1002374bed032c33948e6ecc75b218eae9e10faa73aec21c0243478804acfda685b6113d0a7aabf5adec42ca6e48f770bdc9704fd29c1a2b6a908d953bb1e484229153a442e2c4106c0cc618d6060506eb9422029f322b8f656460f682695c380a3f60d3a774ee5a5cd584550d3a14268dda70d5cc454646cd0d3edb4cbde289149b5295a67c417589906d3210b801b4cfcada0cebbb1bf4117b2ec5c23cac33954d4925f688fdc657763d856b66909f65296e9068e9b2a542ecb2914ccc0057a4bb408072fd7bcc0772b9a7fa8eb34277ab9da2da8be032e138fca4adb90253412418debc97ad005649c67c771e9e016bf97c5b21bf372dfb9e7fea7a2eb851b62902a0980f35365b98c122814a66bc97eeb7a76120bbca976165bf80f8fd09f1ce060e7e0041e6f365e3f1708b1c0e36069e46a0061bc48f6ceb23242881bad8405c56d3fef16c30854d543b510db2f9a81aae7196fd9b5cac3eca65abec28b91282a7088289266cf1107a1b5bd81c23f17ab8db434808ccc5b67f823dd499795a5185c33ce9629300e87d9a622675d6416d22405ff8e422e170aa87bebc55284d8ae98863da071cc332c4b2a582623955e1902abfacf1b273a4e13c48a3ab51f1da05c1bec7d7e77f61a232494e6da4334229d49713cb2e0a49f5c2b4a38467d3504762e017b1c81ad8181b246319a9f2eabad6f46ec9db4491b6b18278fe4b8e621b60d852c1b19cab2c067d595ca55181961ce675a88c8473d1e0348312c778187f65e7ceece6945e528473ae985b06d1c4b160775389e7f715c41bb1c5269038204e406bd8eaacee2aec96b3783eb9afc24bd16986112bbf3ed8a939114fd59738cf6d5657551e689d6235aa1e4b5cbd04b2f53297f105fe39a96f8031a47f36a7b40d634eadacc5738ce396b481c8aded28b258b9cff168b54743ac6eb6661653801dc1397b23dca8eec331658c9ac13f0699a891308a5d3d13f5c419f7812822b3cd8e2c9a27d543c278322f9f3bbba5e793d0165aec518beee8ccbdfdb5e827f33d25d02759f710469a79f8b172e213e856dcea32c6b0e8d87d8c08889d2803a9892e75e9daa39ee88140d6f20086fac814ce821c541b44004d78ccf7c2c304cf776d36de93d3d583f1ee9b79137bda1e3d87bc8503101df0b705e68ecb46564f774e568c81450cbcd9153767643002af322ab433550b2f0e6b16533243fc0f702533808ea61481473f50bb7470e43b1300b49c6a61ba8670fbf8cc673d815289a1178e040313aed02f1828b283c1835160772a80683915437515e1fae154e01f57e8933a8348e5219048060047724d30f6866401216605b8e5591621cbf817dd4c639ad98c43b762b82daea0195a0b0bd41f8ffcfdc28d10b3822c4cbe61dd50b82d52fa821ba12f9c300c1c9202e07334ef6981b80eeb3aa83c2135e7f1e9df7a27819ba696254c8c339ffce3d5f3382a0e8a0fc866de87279fdb343706667672f4c76a2ef3ac5ff81d77ebd91533f342334738dccc62fab8b5afe6cc2e3633a463336f0f77d85459e6f8d7641605fdf631372f66ebbd5e8d1febc97b31239d68ccc00e9f1433e514c62c9647d60a6af18f3a9d4ab5986d673cbe37351262a2ddf4f8326b870e7a1fea384b114cd47e8fec54e0c9729eec384263e7ef9daf0a1a1da519f9e52084f37703bc45d5bbf252431a005615875380ec1ff87dd1e1ea159ec8621ae0baba41243e98c5dcf13d17bda765e3a19560efa3133e6f9f4c93b55688fd869fe8d1c080e0fc871d5fcf8022df02bbd21cf5e6a7b13875554babd0663a331ca188796e669a72b10fce92d6dc482205a24d67e8fb1c3bba4add41767862145928b23c9e8f4b5fe23725dd47891b4351e988a42b45f32bb5fd15fdeb1b7d6a63f8ec088a6cfc5cb1a08f37170d9ccd55e7d4e7c623040412516ca9904f032f4e6715582c3e707fc5223681f5ebe4fabb0472d1dae9cacea07622ae03d0f76405665d26cac5e95657cc4b5aa24fa868823b1cbd421a71513bf59b4b50a4f55004b841b947828372f9348e9021f33912b0bbb06643aa832b8491cf515c8c0a51929e5624b0620db5c62f010842c53448d7d4241b858344bb0aa0accfc0b1c23f65070ebdb6037802fdc2da45d995de0d46214a8e3274f489861a12528634970bc165dd1e2e18c83fa9d113992364c9c2986f63cea92c99b7110895181cbe076113552aea4951df126bd19d6b3f4eca1d9d1d81f2279f8ecc084f5a2cd788ba3e28cce1c935b2cb12610873f972b044803d3c657bdc0caa69734cc63283f10e46240f8899e1b47d979d2cf756e92a87aa0ecd96abb05cebc42460bf2f1d39c87a38ccd17b5ff395449e34b0663547c64d39adb14166948703a5e2dae25c6c6124b09e7943cb91cb50f5acb56e356d195a04170dce8eedb12a4b83a8d66ecee731f3a8e27dec77d1825760feeac75aaa535a7d79249332a3c04691e37fdd5b2b4846238340ae38a8fd5f10feedeb53e91266b97e3463735280711808341b0fca14b88a8dac03f86eb8e678e1daa7d0977e45ac8462e899690f18aec0d4318380a88c8d25b284eed4c44959f410f66ea2db3aefb178e79254447acf0556683a7c401bae564d4b158af36425a43d3c3a10f3e40b13e172bfeb35ee041fe73014e92d311f57e4673794f97ef79308a051040066cb74c2633bc2c057088cfe98b700b0576c6d4648e22ef33d7c201cf09028a269d4a547b0a7f636f4421b33f24abfcbbdf23417385ad1d893f2af6fc8f5e2de67f979f2c0560d78e8ad78644e0485febc25e9f80bf80ae82f7148dac5bfc4197b1e9a609fcddaeccb03004c460eb11cf851b7058d12911e96d21421a97c8ddcaeac84bdcfc638620da86e4f68d9f81e221270dca39522f46ab68dd5c3498a87941023f5c4225b125dac7a4212c4273e410402f0394520010876a50a04e1960d820915ca0082e5ee60006a869b40cdedada15ec3c6143331f828a5de9c988b831b21f22385328f925af94f0f56c723aeb99a5bc81bda215d070127e33a739c40a8f37d3960ab012f8306a55f930387a8a4306310d0771098cc3fcaa3040e880ee23b834c068e7b09101792b2701933ca71828f3d42aa1ead2235fa9121d4a26459344d0ee55e9745161f8350afc9919993cb08f7e5eb3bb8f7ef9e8fa844c0ebe70012c4ee28ece5c3fb143cbe2e870b9cc0d8d3d8935d962c3eca9cdb21e7b982ed5fb3fdcb0bbc219f0063d7e102cd17be1315036faf0dcdfce8e2e2efe2d9f3133bed9f4f0ffe0cd256f14c2560af3bb12d7010c6ccf37ba0b5a11289212e17b701b03b15d69f5cc23018d1bac3d0b6785ca17eecad39c9f5faa82da42ebc91e63c0d8995c1d092e499788edbe0d6738dd01979968ac89ccd1ae3a292c6a5a9d3db7da15aeec76834c545bfa83667b69e8d792f296d14c9b9814412614258837fce136f8ef50fee357e17dce30d15f94c7096d7aeba782ac50c3d6764b35d42d36926f29556eab6c81c539d559d00b1d8e831f638f1412b8cb55bbf369a0c0f113e522fdce00617c32ddcc5f86390e8ae9a6c77b6b5b724ffb31a210a71abf2c96ca67ce580fdc9e1064da5cab12799f37571bbe664ec4c1f85089d83abe9da53e163f4a2fa0fc1eef6500e5e2529ea19b9c693dcacbfe520cc556a692814c9aaa1b3aaeadedd391396deee2c7871d137e6f6f0ed4bc69784d77e9df8259584cd8035eb04189bda26eae7d104d1ac203af097e8f94f38b076d992e40bb5ee7982748d9a15c746290933ea84b3bbebe02b30f0d7b640cada4aeaf905c4addc44ecd2ec06f6fc4e2b69bfd47208c02ab2d5c809812bd136cfc2bab9a0a788e02d424160eb21c620d9bb145b12e5368f076033d9a7ff485b4303ab424c6f6f5379d32b74ea27229c96f1c6f3aeb5438e2da436cb4a27139c395460c36599874307b237c3bd79d282f92a764118fa2b4f6b1098ce409fc241f42a5087b0c44404938dff51268a289c3cd3c054afcd2adfe98eccf43c3ea091a417a0441b2cbc04e56620d32d08d097ce40238265c328c0b1b082457d5ee4a52bf3580191f898d6e1f79fdffcf3fc601133bb6b472b22ca25c3eddd0fba1dc7ea987611e474e3cba20bbc5c2f6a651181283ed275f169104ee38bb7be44f00c987c5a580f4fd53cacab616432975a03f2b7d3cc68431e38baa001617781b6694a96112a5942575aef3912a569c8395bcd78bd3a025b78b83e357f33d9bb33f469d48580a7dfbd1f25bc9fa7d1253e68dfc3024edf03fd47b026f5ebde43d7ff8679a4a3434eb2663c70390991ddaf451a34a972f448375b0260690ec190390a76aed0702c3a509bc2f253a189ead35027140ab0f62b064ddcb82cdc0848b0a4b74073a5da26519844399b149ff94a4b98272eae3fba0fafe94863b33c0e9def807d5bbfe420e5a1bd847753632690374237ee1f4feeae03a73de26382054142590cecb469fea3982a8fdd3c615d56a311973ca88d502dbc2ba3bf7a82146c7a0ef96612bbbc071721ac8aeb56356493e4d2404a95958055affa0eb94ca16e0cbade353dd27494042ec79d91fbe6e58104bab1dcd5e5bf579db8d95b510857b17f7646bc2aa08de50806e58f9829af5260a909e2ca0f5a572da2b6bbdb1017f21d1390bfd879dce0784264297e8528b14e780f827ba6ab16c7a38ba7cd76be22cf29831ffba55181a7cbf2b05e8e895ec074f9efb77ffcbbeda5b9c3a19d26417ea9af0caa65473753fac59146be4f31f1639a45d08e5672b2b825a0bb2693cf34a8d5d672c50197a6a8dd97bf2f7b07a7cac0b1b00a85ede3c4049a2c9a18a85b05aae8ab35bb3c7c955d382cb8965e9fe080180fd0d7de0e8fb73512129c1e27c0b2f1c6737cf2fc27ffa145e3f80deae6db378d6bd365197c9960a8d0438b34fcf788ba3ee8ec3d2ebcd827898bb15eac0d8f7a9f49b7ce616acbe8c53ae160a6c63189a81bb757dc1a8a0e38f1cb9b2ab526b374fad5cf5acf52f0ed557dec972c7d93adbf91950b92c3e4c4c9f2dd1c30f7ada10bb992fea3824adc12743a20c2b9f301b16f5f2f5760ef44daeb050180ee7a9d837673bd541148b65efe3ea4f5c61303a691f54e5a60bd20967264ab17f93ba2ac74243d201485f0582ff9ec30ae5e4a2703b37a91b00758f248a703d5db001c9a15512fe5d840a92b7b1c6013083c3ccba91b7d84f51bbe224e62d4e953f45e83ed7ba8c3f6847bfc84fbc87012b8df872bd7279a9751a5486b34cda37d79f8db3d19d7172031cadbfa232584bd31cfc4d7d15528e95710be96bff3d06594a8d47c531a216061d0e6eebf4bdb0bd32ff949fdb021ce6da35160cbab1c9f399b44481851ef1d3934ac3b45339e3dd1a6cae4d3f4b9c0eab8fef167d9ea7c5a24fd44ea00a44433eae1cdad78dd9baaa3b45b21fe33414bfa191af7b0795ea2a12013c2afac0b8345a5a1e0a56f85aae11221f20eb0a9d92415fe24d572a3da1e0ba2a573eadf9ae57af3e937cb3f7b2ec9fdc11edea67b26849b171c904c4f535bb1be78d0dee3109bceecaceb9ea26963d44fd57923c8a88cc949de7a0834f8f60cf60de618dd561cfa2b50944d3845f7a938ad8c94e64db0fc6d7e1611c3309b2f5caf76512f6fd1fa43dc895285da7a0cc297088715032d2525885c09943834d69966a2f01a032a58051e672591a28e5f41e1489bfee140eaec9e060ddd1720d291318cfe3e4c243c553ca8a944cf7b80ed482b779d09349d566b27fc59c64ab39e7c66f24abc52bbf2125e9ef68116ac2626a9333cdc5a6c8199c640023059274e615b3e84e0bffe263edb559f8e4a2181dc713f45c1172e54ef94d261bde092abf3c5c51c23d7a06ec741903bffc23357dac4c10c47764ca5a9e044d6439d34644439cb9fdfaf2a33dce1b5f82282cf371b33240cce7a9ac3ad192e6bca4e21f3916b8b2d58b9c735823fc20815c2b97087de95b86e80db38c3913b6207f7e737b43f4ee063f0e91812b974b11bd3ae969fb222f248c1c8be0fe1cf4d9525b4816aaae140f9236caac109d2913cbb19fe49dfe78677a627543e7e3fbacdab7a9cdb6cd3c240f0c8b4449085c437a57e3697810293e2ed0dec10edbb8495bda1779085e8b764cd53197d2f19250eec143df901602c5070b17b8d5e74878de4dfc55f7876ba56c19c771b58f4ce6e5d2a23aaca9c3b3b4840577257c3637f2fd4ce8ef68804a96e67d7d942fb7a355d8ef593dd991c6c9f9c5f7ab42fd3387dd4fd6f6ff10f31e1aea54f5bbc8f31f8fc08960b8bf9b7ef6b2b07f34e97a90305e4c18be670d4b10076073301988cf21abb88466192f780c86adb8f870f0681c9220262df509df7ca0ee8de72da41b1c3771b6aec620ec083014149759dd7e6055af9a126795854f70f124729dc3716413442887599d0e1160603132b2eaa2e83deef0fa55914412c06f4868d4f6a1e7df2468174367b1ea584dcad13d6856b610d4aaf638558faf09c5cb32377ac0db56405c769639adad8855301509d1648ade5473b00566d4e5e56d94d5aff86eae666422bf7ea31cefe69ee4665a09792929ff2640dd4d0bd4020b1ff2879cd1228774915bb483434646653f70b81994fd77b24d2b642eaa8912b1e5e2d0879ac46da8a8f9fbf49b1906811e90cc6bf931ab9df8b1043491c6c3809a6857a6bbc59f9ba51ae0076f9e226c578491320acaa5a68416056331572c8b2cf3fc26b66da5832674a6162956afd767234a3d26be513011276b907c80a007beb60b533a63e22a87dcdf037490752889bf61e72d67d3546d0781c64834610cb0e0f00c5329f37d7ec50f3da7571bfd01451509940498b1738870427ce05bed00d3d356ea324b6f086d8f0506237d5a3ea44018382007c809328568040c498f8b8f1e8f8e5faff1b4faa7a6707a224c331332df87e09d63927c3db798e9765e74d6835e00bbc1b94160822e84dfd9fe874d04fec48f6c8447288e41d18cf0681fbb768fdf42f77a591036fb97dc990fb48e95117d11f65abba31c2ed9479e17141b571b3090a5bacb63bbda038ab77ee40e8074d8fad6aff99c6df566114520fc853083e265bfa7cfd9d17f14cb147763e07a15f867696934a7512e7acd0d84b22b583afd4ec72b9556bd576c18ad713bbabd862a20022a11b7e4067cf42d544890b6546154b6d7f601920789a0c116e2f817565e6b7006a9553b14e10e63f7d8a28d33d85f9abeaa3bfcc1a53c27c377e6a9cd47985724bdf9860fa0c19191522343c54087076a6ee7edd05143f4efc19c6d941dec516d3bac64080e83edde49f49087c07d7a2a18b5304557f0679275b66df6194abaa6df1ee889864cd98453f5c632c22a81bdb87cd41c543bc3c8d2284e0c716e9b286b086840e04b60ed491fdce60694b2c1e7aaa56b3098d013bed5eb1ff5a2bb49518431a903bf6bab86f5b525595c50c7ec9cab5cfc25a763bd4b0a74c855aff846fa42ed2343879512cd2b956424bcc79a8d4b3491d2e177e45ad6f9af62c98aa3833e809fdc3e36e94ed33572720c2bf3c7375680dc28ba4b194f1065d016d99b3488929ea6d4848ba27fdb4fb2be9ae879075daa41b94d28d1494a2b229a786292d94f3d86c95a238c8dda88cc5619655811639f0f87c69619fbb548ee87bbe7355e6023c54dfb3efe74e2fafce52a05a9f43da6ea512b2d4313740163e0e50774d8c866432c40bd39c55ad907e3f95722ee4c818bcb009fd6fa9025cef5bdc3a0727812428710f5f68b3c0a94e26c23d52428c2b03c19cf2ed0542ba50b6dbf1dd7b06fdf9348cf7d891bcdbd10205d4ef1a6a8acb017ccbf6e49f38e8723a9582fe62702489160a54cd32fa36dccb0ef1af1fe9e1b56ce198bf7b1235e234bbe0875afe8932f3b9c4c25f4fa5e4ebfd0b2bda4fe441d8e3e85f4b0f873ce9203685a56830fde82d5c24a8e843d05ab8436b2c6b09757b00e0e8ea713b5c98467c6f62de396fb0af78c344f54a4569eeacae5fbc0cd230aa131dd5d89d399643c009a89e7b05b35bccf809a34e3503ce1efb87dbb554d7aed64658bd0111507ba1c2933f2a9526405bcb301b1033e22d5a3127cdc98d9db6196257c751c850b2482439b77749f34e6a76061fddf4683c0b6cf41146df00bb2eea4976d0d3ad9081d3354bfb01e8b7b256c90e636b3321dee024e95a0907ab8536565a500884a15e95b98adf0ea0e0f70f825e33adcd29b27deb62073539e0ba3759e16eedd1755cff0eb2b5ac6ce78090062b65ccc7c9f0521008de5e91e3e8a844969dab1cc0f3c62d39e0f666c10e1d23b4c27f1e34fe3a589f969c1c02e7db83d785a6eeb4817ff1efe7f75e445f22d6fb9220c3bbee804496f0f8cd80f4086bc5100c9249a6adc888d424828abdfc86687ada82fbce8596b7221160ee8e6e65b9326887667ed36691a343b6413e353f7abccdc876b469b5af3343e54531736f8ffb7fc41ec65bad5c727932681399ca53a71c164b241fb0989e4271bb385644491e262fb4c89a1a6704a3855c01d5fcabfb53c37f26491887754801f8c5c92db221e5bed89c82ac0a9559ec729359e9699c8d8159405643162eadd15e23baea6490a1af61ac5f3a2e9e879d214ae15e22090679a28bc696ad9cb2eeb17c57b6b6bb18e01e94a21813357918b10c75220f16271c00a280b9b8ae69d6ed63053abe38564489c8005e35ebe450ee1773028d63d70d6ceee2e114e0434b6a48513d0f6ab81fc7474c04c5d709ec75a8f825940206c09eb4efb0e92abcb7c551b4302db6eb8830b10088b298b9136c42ff7d348acdc887f7ffb97468354f28897e958d7e356e6e84f48790a8ab848ed4bf95ccb8b3290e7510e0a56e20485587f18548c5e1b52d8ecfeb5bc2d3789e09f5ff4a9cbd743eab07d7fce805258d028acd1c8952a270be0625ff1eed7f2ecc2cf1e70155479dbdf0c2622d973352be399f0f99e9f38b9f05470dba4c05f58f1e15c5b879b0c37140655543a408bf3afbcf1bd3659bfc0b2cdcdde4818102ce2128ab2df86804b032076d4230448d3940d7a387f86e4a76adc1dc100b2d8c4140f4e85fbffba80b553756914ee4d144fe0adb01d2d3016b84e73625274a874721c6d28c123586121720ba2c1f5d0da543276c21a9616822ae94c6ca8f615b07498fdf43a4f0cf1253a990e9f307df0f5c889a71987c2915abdac7e8a8200303ae49a4471b3c1cc4bcd5d6c67ddf2f616ea1b8aecf3bfd3b60d592becd4207646016474b82490967355e80acd29e516673815ac900eb81dd4f4957472e862824ec0aba03d38f8fdc140c53f7e0307328bc35460d2863b4182cec87b8e7b24d09be60e68b6dbd9d29921cc53a0213179717cb659e5bf7a2cf17a9d5209826623ab868dcf828425f1ba1113dfec87ad6936ef76de2ae33004367ca0e121b8d66209c4df06907ce9768da1e56aa5ce702d2672d04d098fe0f5503f150dfdd0c120f39f4f27b4bcbd877478653965079f4c26fb4d8e02f756ce4cff2d6fc120cf0386d85c51f5964ce3a5b316552afd2ff28f2a775d68dec2e7f8aa479bfa11ff19cde15a0ba48ce19237f9d77f5e4056030cb4d12942d24777a8ef2bb9233da60347743d9e8adc78b6ab5bf371e452bd106e0d6d0ae86805436381d40099a4188e562243d99d787214212df971fca14910a1c28a8f01ecdd248c488da94d338432223104a30fbd74be107d82c8640043395319404f4f01394f88aa02123afcb243073291a2cb7a054c299ae514083f7480d4a7bc7f7b3ef61e22f7a5002b50c4e6ed499112e9426a502a33fd41588526c8c909bbecf524c3866fb990bff22034b22ed262a660a836206ab6fe0f4f10538f1958527c73c4a19c97e03a514e889b20f03cd03ab41108fc56c55f715c68202097e763686d6f1cf6515d7aeeaca682dbc6616e8099620105943f00427c271cdc8d5638f9c691d8fbcc6f452dbf8b9c0bbc4579579039a05630b2f02ede646e92026f08054523063e9af8115f0121994f73aed290f19beffe2a5839bc0858a804fb2debcd3217fa1c1d3b08e163871bd18bc3a6f456e37a288fbec4a70a85d55d299d808d1f90050511f617e513f004a039e054e2bb526c524cb1569ea768f3a60bcf181b14320385060dba61b54af07ecc710ae9fd0430b09f4d0c36c793844891b10b23e2a5e97e0afb54dc08d54cf6138ba536730729fa2f75b118dbbc3a812d407b26206e7591414eb076a838f9a1ad398c066ad846edf04e3e28d990f3dfe08bc112ba4ff2e65e5433a1c103014d040e099a714294a42867c814242e329e0f8093c05efd82c976f0cc1a3b737314286e17a58eac1834232e8e01c27f3003b6c1a79a2e58e54ffde00c93c2be732bce0b2b35600e07a0511eda9074dda748adf99cec7ac39e6ff0e954f584643eb2df0212d9b27acce666ffc2756a7d8413d988bfe701c58b49ac1233d71514eed4532bc2b469000c6f0aa77285e46770358e9d16cf90510ddbf2e6b07ea76e56737c8535bcfa82166b810fbac58cc0a41dfd2031c3d1eb2eb6c89b91a22bd6577a8f305e57fb83ce146345c031722596b7fb26afef4175f282df57df0bace42e24157706679b397d69dda4d26084ebd425716aa5dbdff78784f4bece97d7a478047eaa1d4609693b97a3564f395fda7ca546bc71b3fb3c4f3edb314d579316fe7fb1aaec614bacbf8c7c1d5120c25e316bc7eb73cf71de0f1fb522ff98f45e225600652c2ad36b74069be59583cabe22d384c780a5569b50f568c9783a35cf77c290d580441d5b49730249376d0e3c32ccbf2660945876cb0efdf502c52b289eab1fd246a6cb270604b29191836b775b410917aba6b51d21a1d730ee8452903951daa7e4622f4376d6992400663375ada3dbdd06833f19a76518ec9ae93906054f6c3092e575d501c910a9a9b58dbae1418b90bb2ae78b1b6de75f7e8dd3fe05987d223e5891a8603034be0d0d4f09a9db9248899b519f62f3eb9595e8588a4d3f727220247d5fe302b8624223f7e6055880c82d6a94341fdd9d5085288a0136ce4428403f8d3edbc61c462849d0121126bcd10ba33c3ca48b0f97d7ffc839db69e279d8cf4a926a9e5501294d4d13998286b60f431a99c8b33e3b3ce5004a4fcea39895b99957b1a4a1e8c94900871a1c9864d41379a6e41c0f71101a8a935eab29085b996b254fba32cc74dde01be4f1081c07a03fa150259697fe4c5582bac5f3cff015ef4de3352c0abe2b36a07a41c41ff8cccd1301b2090251708b1f870d36d4e88717c2f540ee4bce9978659487a854c88d6e40110120168589dd6000e038c97999bfd051fef803ad4245aada59a0224ad5842d1fedcc24889d6badcefec6dbc165f5946f2e43d361a7854856f2c47cceb7c24daff51d197674388900f212e20e7d821c7ced98ecdea9bf1931d8b67eb26fbacdf7634c5cf7a10af1f66f973e3dd4cd85e69dd5c8a80056850b7d5b5ac562d308cc59bd493fdbc1f3e2806270bf99dac0ab201d4a6be1641eb1abf347650db9cf4ae3c7122b94372dc07edee329b60e00e7c9c8f1d886bc2965bc40a394cc657998e8ad8ebee8843eff0a1295ee79cc276348e6a45b5da7150804354b6e3deb3e42680201cdf2073b6e363a19aa22a9caa34fe1893e8adc9da7ecb0ff9f8a97cef4752612d842d8b7844a0cdbbd2c4acc8663bf6a84087407236a801b5c37e3c7ac59bbdac74edd4f015306b929a4ba298bd6ac450e62c14e71fa3770cbaa4ef290bf44743a472a8be6a492aa78188f16671c13743239df15575568128e327d24b553870eec4f3d17924fd25f4782bd16a737a7fc8fe9156bb83be73474545c5bab38fd6252f02e23f2e2e4dbac025ff9633e7fecb7a37ed29a5947d75bc7ef736c78ff288b170f2f375df49fe6edabb3d99d280e14b332275d5a90eb028073ed8c10f72f083ede02a96937a00aee361700a4bdc7ad6d38c8b2c902a0e4d147d6619235c7fd0811c7850071c94b64f6e90770ea4bd37f73ffd2806c9279d62123967f48f6c9c9848715535ef19cf1f605b88e0f726fef34ee7dda1c63dedb329424366605751d91d93bb1ee3a2124022bf080858ad2ca30c1ee66907b6b87aacb993e51a53e87c9ac076db60e7a921c72ee520496f10c26808b6c20411972aaccbdc2545a9313f4c1cb7b77676ef17d7d36a360130c45616264ebc873cd95740678e7d3c8d153b40409c79d5b566f67ff5720bda3968db4b1d43c945e79fb4ca01b2b55e8c16f214b15ac1ff46746fb39b6af82d7710610452558e0eff10fb7eea518bbae0d7bd00844f1ea0bb0213971eaffd745bef6b30e73c50a63c35877db4bb98e7cfc03b8ad3134a912975d5d7f3b59d88bd835f48b9ca484529023a6228d32b91ac60c5ff94f5f3523b2a204b1f9d0d173111e59ed6e319fc3068b8d9bb61446b48f43da280c40be3da1ffb3320ae24450401a23ff7c1d6ba9e5635cb8880183ce47530b49b97d1ae04f835c15b422e4f749e65613ef588ddc39471e6de57b4c1b4573f7b9afd6e355ee6d12f4eecb0857dd76e2a63c03a8c4bb217ccd3ac290d1572f3c44f59f7099b1bd0b07502cfed2f79228775e66c9eee114cb21ad9a09739aa44f6825e46782c773bbb477f35fbfcbc2220e2fc1f00bd66772df5a721402f799893d8bac1fb91e6cf34b46d1ac1a9d949e104c06f54b522f576a04cef8b461573543218c36ec6145b17f49a48cbf83ddebb0954182464b1e9c18049d35235d8c0a48cdb7097278a7b1192e2b9e1ac94cbea66d228d306a2fe2a93130ea5070c1ea26e01afd602c831e143e9c2a8929325c616f903ca948f4b5e6f8a4c1779a49097fd4726dc1ebf8eb98bc59bf6bfdac705cf1be1ec05060a9b090e923b423792feb7311383d2efa9cf7bbffcf51a0b7508030c03058587e3e76bf03d68ac8351e0af9bc595e9e0f6e66bb0aece507eb4d78a17ec0a059d0fc58cc731f4b950af0686900929682a866ae0badb35c9cc655facc4f03ea4c95355374c9b1d7c374d9738112e80126bf563bf469767ba2ce49cbd0bf258b860a715c3d4aafa0c6d04524752a318cf019c24650a4499c77df3aa2b9f9cc91a5e9b9146006c9ae218bf0047656365cab0311fd82f8a456491aecb890b5ca66adce2a91636b96f175f43c0015b23ecdbe41a3d3780859b672404ffc40325706e1b321efc0dd87b06d4073a86121fd50d4234251906a1a0d004e91aab6675edc183edd0ff65c36f0f4eb5b2bb5f49813850dfa0968e745a5600ee5793bf478167767d3c9c61d06838802bc1ee938dc1bebb8846fad81e6dbc3ec20fa8bac07c941e1edbc5de235934184238b4ea732d63559a90e054cc556b4300533555a50b014ed5d054e7537d07d75d1fc51d1fc1839fe80a9dc48d0ea8b9c23dbc60cec15a848613e760737f0aada74d11185f14b8f32ed5b50c0335f942bb25ffc57883ead49ebe58fa2e301d8d0337b82d0835d22ae3d8dcbb9c60ee01dbd2c9e596c04a0cdff4fd7f291601ba4fa0b2e73e0d8b2c247c20079d39e06ca36a80fec6432d46117968540878a706aa8880dc564e43748ff6aeb46232016b51b334aa1cded1ab4d0fe5f0563efe94a6020c4ccbb532b328cc345a4d58413f95821342b0d8f1fb0fdf7db365d526f9c1a443c3e2d482d3ad611e462896085541ac3cc570fa1ea654868044a9046d3ab3d97fd20422b487c2bc84fcfe9a9ea1aa69f2ad5ca4be79f4d4697a324cdf5d84a920d3bde41ea20e5ad514b98b026beaa24c2f776a04440f96fa247f1848e8b076d92f97198c1c1089aeca974d4cfdd101d5cecc85edb71931ac0fe3d5d70738a226949574e3229366c6c1a350bf6d1c570395f18130d038f249c6b8cb7f40973d7c98977499cfe76d1305b0895d604f622fa78256b2cf388dc49529cbab87fe80cc6a5251cb3057bc60e20ff2d4cb84dc8e0b11c284353eb898e590f61f0a94200e988707988e87a60a94410366ff295927495069598cfc366e2fcc89a6d8abde8f619c0b1abb447417b5c8e1f921d961a120e76ea011132c9ffbdff13329895ff644a897278ce893cc26a178f8696b6cbe398190e7510c4a07bb0c5e81663e39da4f380feab5d6905cb25e147b4ec375dddaf3db4df8a29c41a45c27ce229ca3b51c9d92a0994bb29ada66a6a58be82acf51f125bcd9ca3342adf8c051759a22edc9d03dfaaaa8edf91cc75f874820cbc670c729273f7da75c3096c41401551ada194b742d651db3cace0d42c4b385af23d41fc41d82762234a008635b448f3c8efcdba84ddf5c2254fdb8ac28b92d6474e04b3994207e878ba27efb102eb25aa27222a6a2cdde81d3e28da89a34682d6857ea2b009e898eaee1688493af5eaa7b9fe56ead952a2007a4ffc74747b303578cdb4b2cdc08fc04ebdd7ee15d762e3fb32d7c947a4eb265365d22152c323bd323b3033d3e33d6b4c5b1f101dc33c7754040049fa9993eb4431bbad26ad6c13f97a0c878490a6b87a735e2bf1d9882615e822947574ad70864849b09d716ba227b50d41065f4ade46927bea4935e025d39f4c3c787543aea879beab4e5fce126a498c2075d80f92d2a2f91be83740c22c12f6dac0ce55e2cc8478ade7ba0f2412a2f51fda30be38a59235324a18ea1415e827d4f3554444c1e74fbd12ae27e99d8cfd0dfa2dcfd13baaee8c3ea259d70c17a63df1e0307e090f63048389cfb27184c0b2c1bf40a1becd14c042eff317574f4e75637bbf0b5f5124321a7d089d1b466dd5914d0e82a5c97d3caf1bb7a2d27b47b094468f3bb87f625499fef8d5a9f6294f8e3dc81d7c4fe046df7bd9f2e063bd4ab830320881f07b89ef8fb2c723f0f1d3ebcb7cad8977003ba87a90c2866618c7d49de2a138edb155f22b279d93870b5a2fc28e4c4122ba93a84d80ae2ed92a33a446825b640a93692dfd3cb9df9659e3383d1d33c42f4472bbe0996bf11dc4b8e6624d01605650443514136fcc1a547e1b60093411a633604a39edda8f5700ce14a58ad9581b7c52912c6c9fa91891fe2da87c7b796ab9b07fe18c589b5c52e4ed1cd2e102864e587de12b7f50fed844ad08a79c65962901ec27b50e6ef44da75b68f5677965da71cfc1a2ff9b8da31cdb4f4214eb85523fe1eba7946ef0d9c39e80f5ad898e7fd3f4df1c9b82d8f4e5cfe20ce3a729a9072822c569fd080051793d8915b549fc41c32208396b01841f305ea4fe2612e48b8073c4a1b469066fca563c5a68e1df2d2c472d5704418f7da76f318dc6dd5e2a900ac1f2eb9af007a800a9cd47e6855e351277663d121930e23c21b3426c5698f406fa52840ef5abc156003a040baa1a1e2d11a29e1bc4cc53ad63a69054c836655f7600c9a47bc9853debe9517d166a99c5cf74ef7a089f20bdebe6198e911063e4c530ecaf5f8d67159641b80eb3f3d87a0cd76fb41062e6b9f278ac6bf08fa6154895f4285ad32b53fab6d2d3267423a89ca8fe7fb3ddd2c1fe4868d315c279c64f3adc5a5be85b4f5a1f4bb9eb50aff375114ab8fb3d3221ecf3a24e7b87a3069c56d3acc14c28f17e3141846c25870ea5e6d19702039ebf5dc109b8ebe82826757c4c555acc924b889c410c6416009d2ce8095258758b07d01b711d2d5aa483971f250a8bea41f66b91d4d0da933213b84841d1107a477b23f0d89e18f11b57c5c9af8548a6a00100fc91b3587856bc9575ab61556ce0bb3800645929e8a6ac3e42033aee1657d4c090085a2570b8677f083049dad8a70c1f56d6bce86c1093d1a56a259d2ba68ed5eac3367c51f82a5bcf6d59ac4060d21fd92d3f91d90e5ebe4515c6ae39499195fae132c304a1252d7a2b0dee641d6d19498d8cd1ea451fc3760b79579fbbd9e3db674ef55fceee754ec2ef4e95d0633b9fda697052eef838f3e3fac02924efc0a44cb7b0b5c86f5f41072025821b922c50f359d4584e0c09027d15a213736cdfc17226f41e978c5512aca98e413795031c061862e0f91e89cdaa14871ed814cc259cf4092993df78f9888f811dbe1e1e9451d92fc48ebc28a426d18bf2b0223f7fb684bf5ea1263fc8e62d96ddbed8aa2740a860e53fbf00ae2fc83114c76ee4f0a9fd4882c6f83330abce71a64b27426f03bc18444d4a12b293a7dc900c47e3b16e279681408a9dc521b6beb3e302c1d2515a2368d4e9392a3b69132ee32d978b7bed1130dfe56287c3e3d47000ecff48573a7116b8ae96b3f7c6f264d5c0f33678e7010603b8e16f45d1ea3fc3630515722a679e5ff4e22900b9d7856361c83063945381d89a84a9341400a0d7a251cd60ce52589d69623c81ac12cbc8c2ff668f9751f630fcdbcaeccb14f426f68c8df4927526f63fcbd3c134a512384c9a371339dc628a4fcc2260023e7af178c31389ab46b9ab2ed3586f6b5d80654a0c77624dc698317d1c74925c710d2f691a8f8d13fe8e1b1da4aa383dd8c12998e11c2cd42dd2d6400aae8a42f4b7040fea50bf263cfa373acf90ffe8e663ef9a033a7b7606012816ccd18199e2b138fac84f4034c0cab2f06fe0a239c50e4e855bd0c654c8819247fc8e786d0a5040e5b4c53e82db768dafcb69d67789343c19e28547df8fb4e4fe244ef242c8e69da471a02643b3ccbdd60596c8c87357fc80c08cb068a83d55f417a3f6077da12670551de7b03f69920d58c76586ac358cd38aa0388ae1915d993562cf535eb1ad19954d9a2d2a8c3c8b0a22656aba08d455b4e4582be908ecfd175f3cbdd17f5e71ac9dfd2c3af4b539f047ad6798e77afb17cb2941018e175f8cf834fc9be6a869afa180779f5c0c2e13bbb397c56fa7d959b8156767f146e065b27bcf37d91a1bb81639420b9c5b1a73e553e5c4ca9a91bf87b259bd905a54faf64e5e263caabd2bb8bbad6adaa366915f5b808f2df818dd15c730d2c40381a56f5d67e37089f9db5a8b8c4da6868555f3ce3f50f993a2e9f3092ed41ef2133ca597bc6910ca863d1289f2ba1bc468dcb26b3a976dc3e0027de8e6d7652145dcb2d3abacb5850b59708c47ae2650935f309813b32e4536ceb115927d73171950bf352bdc4ad30ab6dd49be686aebedcad6721cb2c3366e54f5e3d5ac47d9ca69db48489e3792f5b9929bc460037773c1c297b865ff21ada962468b81bb530faa6b70f55b351e13f4d8f1a0f9808b11d5d5acb65607dfbd8f13e055a1fc90549c81e859829ee59f25bd19f229b28d3565596e2ea000ac0e0ec37053a72a0eaba19c59d9c38675aac05d11bbe37b5321d3d7420c7a62a13317c6ff4d05a55b93d0d8d41ae496f0f65a930c83b719f8cd90384413ec451c6f24e34a6bcd242222a630e8a4a19beb556d4080dd5fd1129536e8b612c3d6b42f64ae5004aa5cc3fe88d1e8283ea698f469443b38d123584d9e82154cb66b41d0aaae37dbd43a01dcee4f8757d10a7469487dccecdd893e45e62ec26d58cb0e1e7a335b768b681da34135961e9a97388e35d6ac30d1cd89d36a03013992bb328e590056e65dc57660aa25fbb59f7db0f808324128164402108f1882a88fd670a3e40b0e692b6c57907cd5502a3e4fcadb630e1618748bc18993bf50315178ac25249c3b832509b4467322a51f2482b91252d3fa3a844e093b21285ff1de71697a9c359a621f1c408d852c34e476c74b3b5fa5404e3f1d380d08ead9d9fa4e38c44e108c51fb31f39bea1196806a7fc685191a8832cb25da77749d2842739674397ded5723ad4922374fc92e7d139ff377ef3a6996731e3513bfad7ad77a15ccf8228c8a1f3587d80013881ff94e4a1f2ea339f397ff709c6c807c2e6a5284c31b0123ad346449a999c7cbc6128d10c211548912b8c823841952be7d358a6502f6b4eb9176ba18e3305305a9d134dc85371860c227ce5e429df02de72d08dac3f412f7e4e17ba086e733b0299202fe80ff232e645c78774f10139d6ebcc7122560bc746f4e24d9145292fa2db3ee039f86a26e4101a4dbe2527f49a82be52d972cfaf16077f45ba4212d54d9ff9f15f3c187158683c43a66241eca3eca1eb7cef0d73774bd42ec058b2d4e08352fbb2620d3e7e97bc35201c70ee5b499c350a6da974e5fcf0663fb7d079817eb9a750b573bdc014a746c9c2416ebb4ff4e36d2fc532c0e8d6a42d4e51ca64b00eab98816d536ed5c7ccaa206c475544fbd146c76bd1186e0fc113b0caf2110eb15a2666c19a017f65a39c7c0464cf0da2957eeb5e721998a963f573046245e892c222a52279abd1258cd6fcc07717e3b5910282831f54b68bf3d4dac20f0b5b2482148a1d94bec2cb0409d53987698c29e34899425f2f1635ab74911dbebcc9c420c3937302b5bdd82d97a7e07835e7f24f30af1201cea95205c9d442b051e3314a982c29f7ffcfce1aeaac1942301a58e383cec37b33943ad990b6a2613146e483bb2a35d1a63447cde15f969843aa3bd9692b2101f5770cd28ca15d81072e434bf770013a804e6ff5990d4d426da8525fe384ba8204983fd2e6f1b902d5f317ce518fd3f459c3e139c5aeffc485e800797257839d1c80a24347de67d605155f26a1c6c5b7ba222a2e35571e2d6fe995594c02149093a77f0333f227d707784c38042c3bcec7d10624954b59f43a4075ac5bda7a170cc38bd80760078bcd8c54195be49dfb670fb7e6b0dec7fd11c47915adec57253c26180a0a67c046122bc2ff9bc903db460021833a9c548da4d3d2cf3d679b8c7fe75ac658469b1dada8776d389267b20c39c3d0d01194ab7982036b2b46949a4a0b7671b7912dc7e58ed218264714bb47fe296fb741b56620c017a902fbe8492736f171a88881d3ebe1c453578207a497de7d02d859b964ea33188e0323198861483a790c87e89a7b0ccdc9bda6177cddeb5dd509dd8a1c74d141d4036a86485b624774240b479834e46c84aab05c0446471762c00562c30940ef49a18812460864fad93342ce58682ddd1566a3a2730b989d291d484a6c4e55dcc45be0051ba9abb879de23674bdfafd14b63617704aad1fc31d96c3599a2e36804c05e59e094ad5b10dbfc78b6b50267c00a4cba65010869bd5613d9f222fc28abc34610c6e9f62c8235c0f0a2ce24050c32044ae8a4e13f6b6a1a600e4a4d6c4ab10c8d13903740569f91be2e22db7bd549baf4b8245fcb4b0b9f1dd8463d4080168b6b371603a52a4924661479f54232869be05d4846902a504d12b6015a78899452a16b460df13d9d05e2481daa96243200e60c928e64e314ad083830b2f9483f71a7bf8f998bdb7689f44d06ab3e9bed549c8c7f7089c7bebb0a7112b345f231a0a8be42812659b41507d1c48ebf8a136ec7f9da3270a31c8b16e776f31cb9931727ee86f6ae19dbc441e390a8b8af49dcf276d21d670c1c6b45e24e0eb33600b71c51c529a4059c9c0315b8dfec490bae07b92920a5085a8810533c41ada00d745e2f2c9d8d088bd092093490092cd872d2166279d241b7676d7bd3ba15e6d62a59d3a17eee0ead4701fe83679c100bb308c9276cc9d12c9652fb78fe6010982c2248dcb1848af5e5a2ec88f68b1102ae4dd4ad95e48f95f93010fb61462aa40590a6276e02b832429e0c839d4206596ebfc90dc48303462cea247c6ec00c1382082dc552fbe0e5e610491d4e5eb558ea2c08955de1aa1e2ee78ac332beb5305b2e037f31083adbbc0e54d0fbd623d093aec2b63021cac281552cc1300d0ec43af2dba72a62e3a4eca689ea03b232467d2dbf82f683bf621953393ef5a00853e98e77ad24eede8aa8879dc0851e8ee4634a5e5e1b14deed14c90d610a6b990c58805009808c2fce172e72df087ac604e16d3d695449cc8cf81be21b3a2cf8e1a695df8c09004dab10e33369852ff8ddd7ecaf3b749e23ce1564bffa74069c6fc20cf34c00de9f4ec28df4cee886d5380d432c4b039d04353e6355b6fa27b3fa69fdcf83f1dfe15a1fd2fc95021598e0109f802b1bd6909f0a17eec20d71e1c7c534c660987763e27126474600fa058b9e125a85f106c58cfc587127e21ec2386e475d5c27211a6b5cf38546b792ac7ee7f21ae7e33f2bdf44983d1bd12a4e9dfc550f96f15a6ba5ab50a53a5407cda39aa5cfe1c5b89e67bc876dbab0300ab34f68e10244b5d900225771266b0bfd2388edd50967abd044522b0399ab7a486018161dadc227b8ea37d8217a26332f2bef1c041ef233279e458f599d169be85d4e1ffc7d566cfe954154fce091e80a54c8b2f8ae37c9d8eeb79f8abbfd428bc4a0ebec9fe50a2c9108fa49fb01230ada07cefce9b4eddc31cab0a8b4d5da12131d6edcc37a1de372163e41696e5f2c8a23c4bbf1fc526b6583944bd3308c7f49c400a69c2c634cee0e1de85762969cb5fdaf2a2b456e927e93475ef2cade8db1bc113980be3517b04cae4788fc7384493ed6725d353c3080f251e9cd848b7478ed266187ad670b26b8684ea74e63ab4dd02711a01182d6b081f770941199c941700fd0819d2dd43b785fcf0f90c810b67e02bc9c071c352e48462064522bac4d0f6b0703b24a27acac1aaf54c08f8d9addedad6cd0e93b99cc3ddc5fd9e0012b007febefe64d96580a13c7362336fdf40007f7b3031addaf49dfcc7c8705ffe0f6f6294300cb21238e49e9c9296afe62ca909d690d710794b77c7076880c45d49ab8e6fb06b34291b1db601ac696d29a82085d51a4465e35f5df966610f54d819528e4b95821c31b8d82d9e6fd70ba8ae633962bce011376566f8ce4244e020ca35d95fe382591092b29e1e2e0f62cb9b515b51e79f63fb092918a45ffd7d07020a42d8f4137865949ddbd0dd1e85b30e2f813bab14899f066dc18aa6454f3c67f0f2b3a13b882a054bad416b3a2e4f30b292580fad01fae98fd7c8f15ba960e8f243f45a271efc9902c1431a36e7cba1b282587f969de83cd6fe18a82be7b540010f63e10d3cd0b17e765df6b880ee7a9acc4b081013a4ce228b4c41b31ccc71aefee580bd92d8dbc9c15ce69be616c1264a62da039c50444ba54546393e97bb39edeab42f2bd60151108841c335e41fbf7bde5c98d39152565cb0374b2442a442bd3527896cc3dc0cbee6761d988132830d95154c3605cd5a53a3505dea47719b1f3e383c29f45daa8789d289b6d0f690d4cf80c4752c7c847a75dc6fb29493ec5991b99f2f64dd340ccac84b5131b2284b94f4ee881baf9269ca14ed79f22a60220dd646d82e1647de9664431d41a4571f0ca0df4572b92de3e471bd901ea742f2b2c455f3ba64232b6ff6224163e12133b59326b09cd68536833ea2f8c5bce176272d24610e5673466e5fa41d2b55a528a6943100d2c4f33c962bd96cfac3a2b782fe689e6d40db6d982909af88de25dcf107290b3b8480d6daae31ec8805c3878dbb9d87a4268b2cf0df1abd33b100c758c476fca1136ec378f51a6aadf99f7139c55ba3677856b5148e1ab233e47b0ada2e1f2b434f23c88af2e0a29cedf9e8fdc1183f9210234f0965ef2d0801436f7d3be2d7ff4d4922842ea838ac8225e6d635bea50d0bc600eb4ea1e37be7fb8a9264c5813916b89f6d863ce70b27a0b03f7f663961655ac033fcaf76ef235b26d6e7394dd703d7f98aba354b37f3cb232473432b829aec3d18b02e8f8317bfcb4f310bea396dec5245ed5b3d5c9f9fc2ac18546421983635dd3d4032f8c63f5d51d02205e4909ed460da32477de22310df606aa9e73184194d41144dc2e4054036fbc086eb0ea109a84b1f5f33b49dc18e8d2384a5daeded52dff169668bd13ba687f8a8b83e1bb9df4ced0d3683c1fc39ae177573bbca6c8577c09eead6cc38209f969c76621b0009b476b49bc2ff60d215c99f9d728fbc8c732826a8b2b0c75fa297f9f29091f1b95aa3074dae4c05f06423c163212fba90aebf45fb16ebe4333a88191a7a778d2bb2de3d0a7d3867127d590ad6efbbf747b2a3493d606fd1c4daf62366120671ce8d8c894988bea3317473a9209aa11716eb4c7c88dd71464de8cbd2698d0e5c54f0949073d9b1376e5fd1f503a4343464aa898324122a08bcc90cb2d425f7dd890f95d293c94dcda68548358c9418655a6a9a0c7ff102e416c10e38dab7946c30628523de9cf950d1f408e196bdb4ee51a3ee1a7798354ce2ab5818a461a94531c83ade93de24efda7de46b56b0e0a1965a2a902d75d63c6251d8c38d663a43424d0ac02372ef7299739aac862bba8f8e785021c976459ba0753c58014a379623ca81239c27eb02da0c62663aaf0b67b74f4e2a201d2a03fd7a001d894464f925b45f41114336483008616cd83ec045e4e3118cdbd57a29b4aa81c1e7fcd1a388a6a574b47b671f1dfe8ad7f5c0113d71af65e098aecf79135847c967f4329705d8e4fc2313b74b757754153c8d2d7d8a6d3e6d130dbaa52e43ef97c67b74724d72809622708cc7edbf2613eacd68d750cfc19095aaea8dca1b2cfe9edb8c38464f42f44f289180157aaea9bb7ec104f9593600e2c77685ee4fe1ed499d275b73509372bac089b7d000284e0323caf0394a36e6910bc938c64ad627b8895c3897a84f24101ceb80485d3d23f4ead73485cb564544ab6d3f603bb51a28d45782175aea10f96658504f169bf647e2aa5b10671b6a1f3792f698aa891f9e6294beff19503585b494c90e465f616d718b2b5210823be48c2eda80dd5d429e3484fe588d3f7420812f20f5913b848ae92de1d347ccf17778c6e2e05d9e62d55467461ae5bcb20d0e6b6d920f40468b20058680f833c6dbb5576fab8929aa4009d482504702f7cfc9dd8277e19ee5c78abdb7e7cfe6c7160a97672b4aeaa66ab115a11a212854b090e9c07f6b4ac24685d80ca8fbc56e508dad760780b9a5c0d8fb4464068ea5d6083b6490b8aa4a06e0431a63ff424fcc96b16448223b5b34c157ea45fd4bcffa46b8738c016a168b64cf76769a80502441a04ead42bc65620da516d0ec99c4a72a94057bb98ed4504e6d6e4b29698774dcd5085f9e9ba868d9b8219274ec8d706402196923cc0db4a21e8ae4e593f15e96736adb51ec5fa1f53f8185b51ae7fafec7eb535db2bbbbbb794324919040cd20bca0c3b93d4cbcf43b2bbe766bb7bee3159b43b4bde01ec21729cd2afde2be498fcd062891c23f9f3ba4c9fd31ddd36bdfee55c7e8a019eb7edb66db46a30e72f8ff0d36b44757bbad45495a9d7234a2c99399e0edf57d2a45cc86f522c806fd2293c618fdea44b18755d288ae038961fa0364210f1c6816289aeb8d72187da401f85dab03d4dd9f4233ff026cd62c37611c40f8880ae2c69630b551c17e6b8bbfbdc7dde9138dcd968eb0455f2287d2d818df32cebcb4f37fdacdfa6c64a41907ed7e5a8521ae59986308129d5a69f43dffb8074dc86afcddbc31168a98ad2526f8adab4b46985bd19c4b60fb3ac0fd8947fd789e348691536b34d4a9bb901a029ff2b684d0b1717dba9b0fd674d6cff2f27276fea2beb8d482009a4b475cadf087f95756ef222bbc50e76ceeae9ec53db596badb59db56fc94bebfbb6a38de26bc53232285a564ff7ded30feb992db36360be0d88a7479106b9f5fe17979d3f10eb96cdb2330862bdb2730862ad82da9904623dee2c8258a7ec5c02b13e6d94dd79a310787a4331ed6c02b13ed91905c4da64e7138875698b3b8f20d6a49d5120d6a39d5540acc39d5740acc19d5940acf3ce2d20d6dfce2ee00bf820d6dcce3448106bbc338d0c03626d778e01b1f69d6540acebce3552e508ff0930b5c128d8f63e2ddd56eb3ec2153b5e82df145937f80daa21cb1a80ad7405849774f5005afa0fc0b282b029ffc003e84a0460ed21963ef47cdfbd873cdbc6ff7d1f846595c0a6dc4704745591a0a57f75209af2f7749d82e2f897c0b2eacba67c7c556148d055f551d33d554815c2f6af31bfe15f5f9655a740533e0525057158d8eea2484a60dbaf9c075dcb729aba261db0307bedd7d4fd1be6e8f676826dbf96f77198e37eba4e4159400e34e2f74a6dee6ffb233b4a04b156ea234bdec0830d559d7167f01e14692b3ffdfce91b34cb386ecee3348cf5c8ffbc396dbaa128621b9efe3698c9955a6bcdb171036d6d204776d65481531b8cef687b6ffb0a6cb8136e56f8f44d10dca56585a48d4a6d845e7db43d4766c753187de511f136e53aaf088b07f73a5adf1c55a597951c000f04a0adedfdaff527ae0d722f97c31f93e6bdd6d61554af40c81466d3dea6b29d6da532221a04dbf93a2cc8870aabb49db1dd34181495e6e7cedb1daa343f7f5e3b73f8f5e3e7cbce76eeb69712762674633b7b9c9ddd98cf76ed95fbdeeef6767d6e8cb62e1537e4997d615f56093ffd0a5a1d0356a1f0e14dcac5c69121a8270f8268dfb16be3cef346db63fb1bbe27a04c617cf0d26d36fe1f381a7c70bb7d10d261d20720eabebe0afffef6a0e8abd16fdb8fbe027f7b165f917efbf795f8dbc3f8aaf41b690e516eec568838e4f1c15a6badf75eb883dfd33f78198e3ee370a7eb4ca2c8328edc97b817b92f91b81749dcd33d7a30dc199154e4461cc79130ff2421ee8f3e3f89fb11c9699325c4fb7487ef853b2149c550d79d233f0d7b50ed4bd66c6f9636545efa6fa40a9b77a77b2eece17df801085f7de40d77bc21ca8d5772748fbf7b4c76a0bcbb0b7ba0e7eec81bec9b3919bfaeb4acd9345bd6b87db0294f6d20aa1043d034b14cbec6a6bf836599903e788aec7cf0556a535fdd6cfa79e3b61147374c9a97f6292153cb0ab23de7e1027c828020670b4184ddf8cb5d840ef79ba620a867f4706f441010e46c22ecb6dfbd6bd32c62e3984514b1b7e74c3757b01d5ffdc3c528b861bffe5075388f04db3f573cc2e4087c5f7b5dae6badbd3b87bbee13d1ed6d05180c3202dff7377ce975413679e9f85f4834bb108789257cd3c6b65fe32c20ac2d4b5ad4d4a6bc27e991a69eb3b7cd87206e4070efbf01cb0a49f069d1d3a27f9316a9dc0e1e2267db1cd3be0e3ad6c276ef86efbd9bbdd4e6e26dc3f88bc024f5920aecb2d608f5c0cf92fae461b4e9b5c6ffba7bfb4203e5a45d565e452cb1ca99b79f4359326f7f071ee88ae5ed0f80b26abc7d1f288bc6da1a056bed11bd9dfc8fa617539e886ee32fa19e886e779f4d1ae673054d1c17863b1c698f58a21c88377b05a8e6b79a98542a26f5b946f704b13f6ade2dccc26adc729b8bae3ba56d0cc8da186d3db4d6bab8d4eea46d0fb73ff6877ba1a19dde941e455f5a96fea43b5a96de458bb42cfd8a36d1b2f42a9a8596a54fd14f6d4a202d4b232d4b25f1f5163f77e089a6fbbe94c4d2df70c7e433fe6af078bdf0965349f8f86c1c58da1eebb22eae0bc1134d3ea76786644433244227112f97d46adbbfdbba4e0685e55bde9f45bb9216d269b4ed4f53e2f2f1ec8cd3312e9fd74f8f0bbb41366567241a595c7930dc71f95cc594a7e18e0ae9459e863ee446bc94c9b67f0a8aaa1c7122a2edbf32822e339f79122f8b8ac2d0c445db99456293d85a114d494c0c51dc4f3f3b2b84d32f35920b698b3a4d4f1ac8e6379bcf36ba27880f99f9920bcd944c3831dcd94e5a4a2cdb44a5a686f41790fbd894c380a44e420ff2723c69a099df4e487701d51a10141414c8e9b12aa934da88723a711a654b41e1813882b587e87d6e01397db2596e4c68889744bc7419d1cca6849c43455a74dd34ba120155994dc96a91adb33af396162b8e5fdad918e2f0f6f50142ed21923ebb809c36d932faca866ceab44292d0623865c8a3421cdeb6b598cae87365a12c2b2b9c2e15191aea5456c2cfb585b6b0b08844ce738505fc5c5da84b4b0b25c14ebf8bbe31215ebe600e772852b0b4e4cff585beb8b824e1f46897fad2fc0e8915dd1365c9125f99d78c6bc6a5f9ca3eeafdaf12daeaea1014598584ba961ee2f7b93efd1bdb42bc748e1be2a5d3d01f6e147d63fa0ab943eee9e515aa42e848d6586c4bd9dcc36d675460709fb49dd9a2156d935825d76553342fdd2ef1d253485bf39878d215480df274edf1f3c34b18acdb28ba26517d7c64689c68a0d0f8e7f4b6519aaa688be44403f994b6086624ed8c005efadb010c01e1834d6d00084cd883dd2fdbcb0613ee70313131bc57be43915788ba9076e6250f44f326d9fe3789afccbb64096d09f155cafbdf98af54def1fba7681e54c81d704c154f2f13eea0fcb69db693157d830b99d38d1d77d216898cdd270d947a170d14f3a5beb1197d8564f43df51d62d1b748ea57625e45e65133a5cecc9bf408ba3d9240328ff508ba9d4202cd7c4ee99e20b7fc991087b75148fa03b17ea92bc5e17ea644d1de8c8c8c8ccc8d5996edee8dd9d94c88e366dba220313335bca4a9a99a93b648485b44da991462aabcb979b94939a1dc984e6e6e4c279a964cc4b7cf9548dabc43db7f1482f98b5da2acadb59612652e88d0c4b64b10826be3b0e912847cb1e27ee7d1b594080c7ba048e8f9dace9ffae238cf5a969d79ca3fb7dfe9635d341c014f888159224f39108e33e2a5d70a926e44009ae579d02bbfa3f1e3d0dd634047e0fd88834252d0d2dfc4496cfbf808ba2a0579e9949581b78f4fcaa2c0dbc742945581b78f63946581675de0ed63186561e0ede31e740580b78f7fe80ae7ede3176549e0ed631fda52bdca9bd82580376f19770cf38661de2f506f5e2a704159a518ab04069af237efd0b67faf289941876cfbd70aba2ad564b4f56fff9eb47543c35b1317a7c3db776a0302562d6fbfa3ab97b70fd295cbdb17e96af5f64d7445f3f647baaa79fb2c7455e3ed3f5dd978fb307475e32db9bd136dda8f60216e6fba11237e440236df8331a0f51bd1e944e057a927a2db2c7fa38627a2db2b3e0bf215277329e88aeb829631cbe2882c1828ab6b82165956f7b2a99aaf3a58c90cbaea7cd0d25548f3a67c1e48f3c71dd21c7190a5188a2cc53a13578843b5b9cfd9410bb2680138ce18fc4017dd807fd10ef89406e2693403be8656c0d7e805bc0dfdc3d7a013f018027fa31f8003800722013e48dc6c083cc0010d0082010b50c00f7ae527017aa50748830a0c69e03609688f0179904d053918ba10109b1cee70346c5caed10101dae9cdf62dfad272fb17ddd1727b170dd2727b1a6da2e5f6357aa4e5f635340b2db7b7a19fda6c222db7d71308c31c7aa33e8320cd2691c21d95cf610d10dc46a91d861c6902b4728f461d0d37be029a034f01bd81cf80ca05b4106f016d3ea767684fb23748341594107ae547afc082624238b8bde2baa245e3f0a87f6d4305b406ecd7a055de865e7d8d667d0d9dc3d3e89c4fe91b9c96b9a112801797108797da6874b690f00940114bd14a112eea8a4a37725839ac545c368a7c891d61c56527f0030b431c5908628c4f1764327d90a90b327d90e94d719bbaa0531044eaa5a611afd72b8336a834da79dacfefd5b3ab673db94adb6cf6babc7d2791f33c4f87c160f5d268b44aabb4adc23ec76eda2676966d62f73e5fe14bbbb44bbbb44b73992ddd6555e630efdd619bc3be1fee9126089a4c1c69b2b06ca409035353e332cbc2463ce57f8408c96c498d2cd1a0fee66ac00ffeb5dd57a80f15dbf7b1aeb572351c576e14f7426c5ffffbed5128928a1fca7ee4103686d2dbf699abda44752a8fd2a68d6dd487dac4b11dd250596a4b75a92ff5eb4a7d15f2355a58ade1cd8e0500fd4f9add0b698a2fef429aa3cbb7902dcfc2f23478f66559a3229bb2305f8d6a5efaa0ab9119b4749234bb0d009d6bd0990b34e53f3283ae3218b4f49aaff2cc4b30d0568df7cf5fa0adf2fdb317682bf5fe39096dcdbc7f1683b664de3f7781b668bc7f0e83b660de3f17d156ccfb672ed016f9fea322cb22b9b6405b37ef9fb5405baaf7cf48680be7fd3318b445f3fe390bb455f3fef90bdab279ff3ca32d1a46007812007ef499e63900e0fd33c9e579ae4aa9cdf8309a529b94a7a129b539bd8ca6d406e56734a536a64f694a6d4ebed494da987c0dd171b40d627f60a7b04134681b05b5f1b7d1d607d4c6bf465b1a6d81509b1a9a529b540d5fd6f03332357c0cf930e4d32049a01a481b0380587a6b05e69edb35681bd3411cd1a73ba2ef885e3aa2ef5d3aa2968e48b5cd8e28cf56c8512cbf9967dbdf06d11657b70a0e4e8534c3dfbe6a13f5db0e8ed2748fdab4545820d82aac101b042b043b048b046b84ac248f816e1b64834642b6bfa78386d87e7b1bdb97f45239e98c97eee88395ecc0d069972952b192f7fcc2d517933ef302bedafaad97dae07ac9d71572716c3b7efc66774921f077a3d884ff5f4cf2d02d8a3f22c658638d6b9090fb10d5a0da8f688eded799e3c578db38aeeb3ceffb4e11932c60371abdc4d5f1506759f807fed184b515149960a288893b54dbfe9c65a288898d0967a28809cc0413454c30c14411134c1431c18467f1b66ddb7dd38bb8bb61bc5dce762eda4034f18f8a81f8ca1bddeff67520267e1c843506e2a5e31ff687da600c0407e11fdbada67b332bb0edefe038c857407c453ddcb9e3ae7b7cda1aa2addfb116306436e34e07b1f7da6bafddb1f6de4e665d7ee408122449b6295657e24bb629569f0d3111a4d26f57520435894dbbe7eeee74ed7125e1a58f4fa789f8002d0525f04e5f2088d9101b80001044dbefe05d57dd5e9d37d0eadc815667ef05398320882f18d6307395477d8d2a257da3edf17691f062b06b90d71f3ff880cae0884dffd33b7839393e39c360d3bf1d681ac26443a57210046560c4b677fbd15a0b96a5d7cf7177254510863bf647302cf6e22dbb60670b6efa7a640df214a534c423b3fe984234815ee9bd5bd723831ae35cbc225318d3be7420d6af16d3b0d48468769bc879ea16431d45ec213cdb06faf3600eb422cfedc4cf9ea562dbf7421edfe2e78fe77bf16998e37bf133066b48529bfa81da94698578b8fd7de9b3f891dfd710c7e74ff3d0fdfd47ae842f04f8df87e07f0f7ed5fb5c0df07d767777776badb5f6de7b2fc6186fdbc6711ff9e9120f91fb0e0443f0bb0d72218fc98726e10d458023fc5cc1a721cf0e9e718883dbdc36f991e6b68906413da344f2705b2429b5b14fa90d89346d6cdbd18b2be20b41fad18ba4094c5cc941fad193c899b547b6fdfa65ef736d88524a696ddbafb5d65addda7777778b7facb5f682235d24742fc67833afcfb6f6b7901c8d74c90991fb6e441249f1bb2d823fd2a4ef3689f4a3b7610e1249b4ed8fc8a06ddf7e4fc390c491498f34b7a06dbfd366695aa26dcd14c8b67d1d88f6b5290d141fd6a710b284dd60b22836ee861ccfb13e3526ec26dbbcd836a28d0cdb1914c77f1bb2ac6d665344beda685e7a41571b19b4f41fdac86097b0dd9ed46641591c8f87511e5b6c7fcbf1b0e125c763fb5b15d7c319b1fd39d736b32cbb9d61b799b5b7b6d1b00537da46e3b6ed8c8d0c9d87af1d61ee565bed49b7d7134e2e5694298ce9c1ac0f6d197196786444e249ac6ac9b64e9ebbe299978ec453fe1b8f9c24545b4fb9e495e0242e5a1e5efacbcb179e55ebb3cdeda72b7a8268dfdc7eae8faa7b133fdbcfcff6f3e3c1ae70d248a02b6667af130bd1bc7eb7ee5b0949a513143daab0b8c8681a3135ca1a36372a1cb0a506d0e5300ff2980bf9493bcff33ccf582c168bc55c36e572b95c2e279a15d16a2c2b2aa831168bc562292e97cbe582b1c183567397fbf8ebd2ea908c88363bcff33ccfd7ebf57abdec2c66531e8bc562310ff2980b9d3e74a2d168341aca799ee7697abd5eafd789492c168bc562dbbf54e3c612671daab24a34abaff33ccff3f57abd5e2f3ba3d5dce53efef29f92789ee779925eafd7eb35b2b3d92cbc59d5a0beba1f58508d05719ee7799ee7ebf57abdea90ac12cd8a421a099ee7799ef9f57abd5eafedffd5c05222085a446bd57569e7eb3ccfb3fe54580daab12af479e7799ee72ae7a5ea72c4731cfa4bb34194e5ff388a625724d2c67789e37797c65dda7669f8d2eea5d95bb1100340836df28488613665966e52190649b3eb3ed214c58e34c78d34ff2f96951c963901cb66402db5d44beaa5135a32d8f4b30da0cb7c5573d5d1e3a5dec72ef7c5ed86dae34eb9a78db24920d897a329fc5b98a3dbd5b4f3066efac464e70edc7469670fdcb448da1904bbd1ce20b8e910dc79046e3aef4c0237fded5c2a819beeb89d4fc04d6f3b9bc04de39d51c04ddf9d4fe0a6edce29e008aea88cb6e74cb0519bfa27f0b4d31b9853e1aa7db91dcb264da6954dff055baff73725380cb3d55a2b696e8f7509824d10cdd1963035beeabaef4d0bc4b43f4cdb237cd3f218bd6993d89664df9778cd57de5bf20e6163bbfb9ab288d8bd0903e3a5dbd9f6db8834c590dc1eb433df3edbd9f61f59dba6ea7aa40db24060ca9a8dc336d6834f21a406d8c6b8d7a6348a18d54d091e4751a44fbfbaeaa5623b69f284782fb62fb6fa08fb50cea48a637b7bfbc30f7b14e2d83608ea19fee2a81b7dee407cb789a9f0d1670f8fcc22f6e877701eef473ffabc81360c010ff85bb87200d39fbc034c24b5a91307086d7ff079b6ed3df879b43d52e87d0d717cf6340fddde7bfeee6badb5d6eaa1014e5bfceceeeeeed65a6beded7eeebd1863bc6d1bc779640907e1e70ef4740907e1875da94486df853c265f3229955e043870e45024439d2ff71b496f9b8620c9b3ed1149c1e70640b7f7dfd79087277f6729a534574fe8fa6cff5a6bad152cb242eeee60f75364adc5971bba17d730decc2d08041d0885e8fdf75ec85337d803b7c3cf9923b9ef36c7fd4a8e6e87dfedd0439ef0c1b7618e9024f2b798cba4f9f994983437fc97bc2790ed2170ace4261fc48e7005126252f84efa451630f8ce22f93ccb4ff60d7d449f17df90657d339b22f2d547f3d20bbafac818dace71dfccb2be9a4dd17c957d3e32e82a7f5fce5fcdb272f6f155ce48d055ce5fce39bf2ccb0039673a859c939373ce39035156a9faef9c0839f8bc49c8e71032853149315c5bf45eb7d87dabda37eebea04baedc7befce1b58434cdebbcd2fe8657b324f8268dfc430af74bb23b7c17b7f537c41540a8a6196d591e357851df110520c13890d0d0dd1968b1dc23f385a6cfa2aae8e8e62f9a6f7c5f6377162d492deac7acc85328531bfeaf6e28debbc2f83e18824964c4e4c28a79411a5b2c2d2e202ce60ae1b635951418d292e2f2d302c9b9d504c2726a5976781d95749248dc2d72bf5b5d14230fb7f9a54797127edf3fc3b9206ea556a255fdbdf2bac7bd18019f76c5399179bedd3f64c2f96b24d9e1029a536fef56f4c06bc31da72d9f4bdcf85e23324bb462f3d5b6d2262622e70ccb39862776f622e70707b23dfe2188e65970543f56b43a5ba2127c7ad85f9af294bebabead407e38831c6d7d6d306714fdfe3be1f2ef63ccfabdc47963c50da36dc593940feef1790c9ef17409fd3749bd8400401ad754f8b76113a458439f09366edf1fed33ddf9bf01041406b9b089b2dce0d716cda1cf7b6daf41598b7868d3fd36ac351478ec0397cefb5f6f30573b88b73cee220d639cef1901255640b42cadc753bd8aed68dbc9b0d794a40448e347376f7dde78ca39e91bb2e578ef39087f36ddb66c7b3598c6fc883efbd38e4b9d8da2de4b19bfb0e793890d3a52944efebee429ebab9cf1ec8e56f6416114b76f7260e916d1fc73ece199e4452507af4808d208cf6799d18724789a7ec9f76685810cdf0f50a79384bbffd8cca2b2c608cd60fa349edb24b7d5d2e9d19bd43b329fba0de91d1385c3665df45e3f0b129fb2b218e9b9dc99dda1544330c4a026ddb0f81d80f83866c1b0ad9f6738a8bf5930b8ac9e5c4a5bad4b1e3e886535846f56334d58101faa7a13d4df6d81d69b26cfd5ab4a97bff455b219bbaefa2ed6953b79294da5c903a012c62c53970a4eefa9576d185b1e97b45b475fac1b24dc7a78f7bf84ae5e96398af509e3efee12bd4d3c7419625f334e5e96321be0a9ffed397a72e4f1f3b416d1899f8894daf193c367dac045ddd9a97fe3e30cc829f32b06b0c9f1dc61794855df8455be0d3bf33dae2aaa0e5112ad0158e795904235cd01516c24ba0f8a4b80b8a655e127d4fbda7ddd3ceaebc39b329e9e97bb5417cf2f5d37f5fb53c7d18da628979fa31d48615bfc9f1cab144d7893f380cf1296568c8572befbfa388b368f871e2acddddb8cefb32d8435cf970045a2db3470de4f25803bdbccbabbca8707070707070707046154cc2092a12e345ab6424958c0a954c4505854ae6844a86e3021c170e12383e384de0bc707ce0c0704824d0ea98dd43fe0a4d12680d24497305bd41e353ab71d16a449b86b6fda4a3ad209a9d68358dbda59c7018e253ca36235a528692318abe3842057165542a853b9bd57ab768a07f96ff9567f9f0e78951caa9e55554c484080d39abd8fe608dda0089a20cd29c39a51c31ba2922922919234918332a18e1e29a34e7994b6df2b3c5508e2e72c872602107519118b4f4ef3a511c7f3ec7cf1674b5438a215fed08ea82ae7654414b99af76c4bcc40265ed40024d59d68e1c45beda31cb21065ded00431cdf1c5f8b9fea3e322bd069975d809a65a99c38ad184973149ba06f68d46687143b827654b123b603099bcc883ac1c87623be1abdff0e226759dd7202ad664919c77067fb28c67143e1848ccc68f5490728ab5174209a57a50b627dab4d64748f8c112a9d99a7214324e385cc6c668999d7cc13333f334033b099d84c15e29d1152b1e0544493045a83ff8ca65902c5f1a7a169aea037fc536636e54fa3a7e101b08e0cce2945c60b19229999cc4c664692992921339399cd2c31f39a7962e6670668063653c54c6c4668fbaf8c543064666969b1bab4354d93a16ba41464105905192391400a9143e4165624659a0c4dd3b5195db3a429ea1a290519445641c648249042e416e410296b7141197f0777015b7c5a906879b564d172b66cd132d432851659cbaca5eb4a2d5dd789e238bed9e98b9f6e2d48b4f8b4bc2c4ba58450cb8b6b79b5bc5e6c7989fbd59245cb162d432d536891b580d1326b29dafef71c921921aa53f068434057c0acf85151f12069aaa21837728534554e90f038f22462ad2290fe9770e7f42f21cfdd296f35893c7d4f90940f0111e2463d4a175ef14b2a2d868a885b9caf192a3e0aece3a2d5c64812c6a35056f449d310f1526684680a2924cde96188a6b85f40ef828b215a3d1a613efa1883462f46a2118c713682612c1a6b63d78928d74b091d541d4d2d8b2be80d91212f8bc86c8ac8cb235ece8a5c52c61d453b92380be65d361f236c0c1a9f30068de71834c6462f46a2118c71e62b150b4630d4c622cbba19633423c958f3d54d188f5a19c17817c23d3a394fc838a19d98715233c1c0e432bd4c4bd0d2df964c3faa1fd615dbbffb7fb3db352e3ad382ded468365564533448686634443446686434456886a8cd16db69aea0344468e9bf0292346718e2c969ce88a43d39555490487c729ec8ede43c21e3847662c649cd040393cbf4322d61fa197575d456832a2be8cde8ab102a581004f44a1cf12111cddf32246975fe71e2659bd8e51a0dc9888ecc5e3ad78278bda4e15b10473030e1cef6be0571f4b98a98d4275afbbc8e1e0d8db618c9482e20b94848907c484d905e2418c947ca29245d52d0891067e9f73f89f9aaf444dcbeb8f2f56b4907a21d6d311a1ac9b639929146b29034c76d8e64a791ec0819c90524170909920fa909d28be4830423056dff95fa91e4a8875863c29ded41ad3a5fd4c665467fcd20b2555750258480a280c2091f57511247f4b0e0a6b67f9b34e748d34481de9c4332234453d83bb82311633ccf2bfa9ef87e3e1f1fec9b82a6be20fb09f957045afa97deecec78face11e96ff878e681e1157d4f7c3f9f8f0ff64df1055956016a42becaf15304bacaf1448934bb8d34459134c71369fee92de9157da7ca0a7a23446d869c5e12d145d8316ae3a23642a850b12068fb9f4873268534df8915b236235a528692318abe3842055a8a4bd03449a03727914fbb6cabedc61ac8e56534cda9698a782923b22977791ae2b8d92924cd5091387a93e6cc8138facf458ba34dbd8c0c68b5cfcc8056574a694b95593692ae7ab4a96a35a5214273724134ef4cc8e3ed15d265a3348c97f551f47b595f45b3e811456fea8f722c91e3e5ab122deb5b6db3b30d68758d8c8cd842b349ba521bab6b6c224e10a1e54022874fe9046dfbe7f0c98144caead4d6010a1d6a3c2cc1c36b46072874a86d7f1e5e3c2c51e2b4a2b0c1a5c3133afc9c40dbbfdc26cd6975b9655a51e8f0a3c313335803c1fc85f995fa2b37a850fd509b960fda82b9ca099af23f67b615302a7ea2d8ee82c247f552f9d4aae0b1554ad024b6ea08eada3765d09b31a8925d4463057ae30f436a9a212ba321a239a269663458d8fe31ddca0d2a7e563edbbf05f355016adef24157053083a26820f2470d4492053083ae68b2a0a5af68ce4b0a314b4be6502a28dfe875a0c6fbeece1b3f1415d488cb2abb7ecb0a0e5daab6bbf3c60f4505953282da77e78d9f09d47587a48b59b8dc32c6cce85db71fe95741e9710422c9df4871db3763d07eb639d2bc689a21174d230b71b8ecd34ace1644d5a61f13f2b86c4f0be2e84f6fd29c289a7a393eea6574a85f579da2fd7788418ad466072feb7f30bb9a36cc1c6a03b41a66e71ba0d5e40ab4fa1f6b204dfa0d1668f50ba963e84da75d364cc8e3ed91cbce0100ad5ed9b9055aadb2b1069279528b365565c8cea6b20da0d5a88d3590feabc9cea6eae71b40ab5376c601b4dab4730e5803f5d078233a1d98bf1ac867d481f95bf3d58e9997ab1d60b82c2b07cda6aaa0ab1c5ed0d27d9583081781ae7614010bf96a8790977809945580330a50c3325fe5c05dd0558eae13c571c42e73863aa148156a1511d5a94269951014ad8aa980a8463a04db839f494db3049af2d79ae60934e50fa35d40cf38e38c33f008a5724249799a27d096c9fbd33881b64ede9fa609b4657a7f9a22b4557a7f9a2d684b7c7f1a26d0967d7f1a2d68eb92de9f6609b4357a7f1a25d0167e7f9a24d016eafd6988d096cafbd364415b28ef4f83056d9dde9fe60ada4a797f9a93b6c637a2dfa491c2ce41bf7fa6a1b14e8ba65eb2c0ec8ab3327363c6d0d09774ad68daf54ba6d3ae7f92b22b0d6d75c694138a0a0a8f6868d719534e283474d531917f42be89a6d4a624924f1afdd52310f7e8ad1ec1b8472410499a2dbbbedc3051bb86346034d5a1a1371d52aba2a037fe9b26425449b1fd7780a1fab1fd5550d0d22deb660c9aba29438b1a0bc423b4a059255edb7f98d0c44dd1cdeca6d33739bc489283c8573761d072471190ec10f2d5cd17b43c83a80035cbbae182a69eb82992e3c757375ad0d27db649733aa168fbc370e40d0d894873ee6053f55f5cb20ea0d5de0ea0d55ddea0d5dc06534e38d4100200c4c99f6753d3711b0dae91ba56c72b1d5f5c5a58565254a36fd32e5c2c5e87a090298c39726dff0caa7c5c6683b32cafd9947fd789e238fe97a6d74c1b35cb0a8b78cadf88eb880f925792eda192ede192dc0364fb5ffdd5be259ec2643864625aadb3d4de11a7b1124f61daa56d27472e1c455e3226a94f6bb6edebe3cf58dbdf34c61b75bb69682fde34c41bb769c875dea6a1f76d1a7e79d330839b8660b869188e360d47a44d4312b5a96269d3b0446daac9c9a6e18969d3d084b2698872da343ca56c1aa68c9b86236ad310a5b269a8b2b269b8c2b269c8d2b269d8e2b269e8a25f360d5fa8cd6f4ca48891b124b24d5fe23457e2b54ae2b30bdaf6633b69d5ed8d61eea475a7f7cd326883f0a48d4e92089a9cb493132d055459a1b4a3221d95d07799ff70960ac816a21aa222a22ab2e93bac2a7156104b66dee349cc9c87f7a0f9ca23e2acfa7a157919791d79217925d9f4eb597938ab9e67ed71d61f6705720aa17f0167d52e89a8645cd2b3e9d797afb8a71f84afb63ff246c622222562deb069cdc38c2c2e33c96c38eb93a9542a95ca457a53dce3db1bcba45108b6648fe524729ee7b9f2e2f17abd5e2a1b2a2624168bc546574f4a1031954aa552a9b29d792168738ad79d4e22e7799e282f1eafd7eb65fa4e624262b158cca4be5eafd7eb7cd95910e1837466e92cd3199d79f6d585a0cd261d573a899ce7798a2f1eafd7eb45b248ecccceecccce6cc542ace7799ee7ebf57abd5eafce4fce66af1b715b781239cff3045f3c5e01f9892ac5f7d5700a9e17fa6cffb0abaf0b5ab1fd412e7761fbd78dcb4f6caf67fd1f13acc24cb01a6482d5980956854cb0ed7f42ab5fd4519a486b23cd87b6733a3b129d9ce318f29c36fe8a85c87dc542ec567691ed9e19bc1f7458a865f0be229d6786ed9f39ef4def07db9f7a1d16eaab9e2895fde2aed8c2a0455749ee4d948bbb62fb7f1bb98561fb7b75565f2ad7c77623dcdd7c7046e86cd7c742a4e37e81e5c2865f505950ae2b044ca25cd4e606623d6d94acc6cb6bfbe7122c75a7734b3f28e7ce162c754f908aab58ff597e4413131df115f7be4d78b8f7fe6aefbbee617c8549f33bb26f1528153ea27dbf38e2f3d99708cb506636e5f7e945a13915fbbe8985ca4b9add8f3e8f4a3a10312a8a2f506696e512e422e452045a3acaacd4798a94ce402e45a0abecc345c8571936056dc9fc80b66682d0560c10da829182b6481f44415b9ab6de25c8b25c4e9bea41e641fe917de41dd056f8fe190ada5aa12d1697a032c3ca374fdbccb01c4a32f536341f53f333350f43d6fcdfd7f792403540357f431f6a2e974ba7a7c607090111e356799677395d4e6e646df4e6f21b8e83fa52d35ac3f8353d34a411af3343e233c890f88c18129b0186c44ba88d3f0d12978124b1199ac46590d82747a70669c4ebbc90780cd4e692980cd4c65d482c06122ba136fe2c1ad3f018980c1c869a2a516a675097330a54ad8a36bb6c4cb3ac0ca329ffaecb305a860971ffb79961a71ffa04f3d24d1b3bb9b6ffbd28356a83b2e4f4e304bbe429487cd3e5dc9e61204992cdc4d29b1986828d345fd98a623ad9503c6a1da3b479da26a699b68969a2154c124acd1437d21c95e022941a890b2731127cc44b222bde516a8f464f51cebd83a7b63fe8abad92660a64765fee4d4c03b94b6618b50143345135da124fddef7af6fd5112fb7ec863df077becfbd9fed8f73f0b64dff7ee9b28af99ddee3ed4a0cedafcdd992a25db9fcbb0edf8e525e32744b7c705a40e7fe6f0670cae6a6c5ab36ddbe6f7dabb32b2badc7e43ecac750f42a6f62beb5453fe66b97700c9ca12356d795c97f3c699ddf63a8cefc5f8de0deb917d233a9dfc9feef1c9d1c96f44a7f37d0ee8790e7ed63d46e4e880a268dfff5459ab2a7abda960a8f34a25da94eb6fe70a7ab6f9e29db7cce5ce5ad22445ba45fa559a47f77dd35bd63d41b6cf7faaacada65c6553ee3da8e9a7c1cf59539dbc5516ad2973db5d4d7d6f844a277ff8b6aa426f7fe1bdd7691e6da0deaa3d92bb7eee367df3e7694d6dbeb4b3ee6c4adc5ec893f74676d446abe849dad903bb90479baa5d79b2999337ae20edb8d2f67d4408416f119d8b47c0acedb9bf1ac4376a88c365f76c4f75f2a6a98ecdfb6e8ff5b7ef2529f6c141dd03f7a64793c99d0b8fb3da667fba2788c7fda63b505eb1391a768e92e3bcdf7ee3381ae2a81d28b710dee3e7bedb6a980367d7b0fb0dd4a297389bdbdeb71d2cd7914394bbbee791d8d3f4be078a23cbf6e9ee9a17932603f625535bdcb680a5bc11af130485faec293928540af728dd811cd46f9f9242521195d2030b450e91939292327e8a367376ca73dc8ab85279b386c6dbbc88896cca7191d3a02b2c86c667504c0631e80a93414b2ff215a67979063e036de9f7c766c04b68ebdf1f9b415b2fef8fcb405b2c65d0560b6db9bc3f26036dadfcf6b2a9316031d0560a9381c380c7c034da9a798d683efccde5396cafede5d2c2f2f2a4feec35b40db5f14f691af4cd4a39a353ba460db5019fb4796df3ff42baac7c8b66d12318f70a09644392c40f00ca46aba84d81a81add1304a451f9deecb08ce2b86c5cd2166afc8c1a49934bf9cd04400d27dabcb162e980c3d6b319216eb32ccdb2ccd96c1129cf5548896dff6bafb601749a29d18958782432483fb8511446467ca72fb63fbdb18f2cd18e348989586c7f4a2263fb53d28fed4f4751d8fe7464c4f6a7e110b63ff5ab6b7a50d2562df24027b6cc84cf8c0fc84989ed5f676f821e6912039dd8fe353361fbd7cf8ced5f3f20dbbf7a56d8fed55e0dc303d13489a19cb4557f065b87048ea37150746e5f9d3783edef1b6912eb90b0fd7de33828b6bf6f50d8fecef263a46d2ee0b090ab8531b6bff5f3cdcd05dbdf72a4490c0bd9fef66a61fbdbcbc4f6b7f86a930f13cc2ec17a6d33415169a5d976dda3d8fef7da7d35b803b193d156ad020bb6ff898bb6b0498cb6eefb9f70b1fd71695685ed8fb1252b12db1f5f6edf07e206fbdb25375b9ad156fdfb9e69529ad5cd55d22456df348979234ba6c4bc7a02c3099cfeb224a284a3a78f79f8caf4be4a31b92733199471493dc5b225367d4a71824e4fb9805d65108d210b63e80b9c73db372f155838423665532a9a97b68ca15ac7d8a08e47bde97ebcec9ea06507b31d1668aa2bf2b2bb29cbff710c5df526ab36ddf287717e1aee8827f5b5892c3c526fc299972118b4b4a8b0680434828d62a32a46afe340969190891167fd9b14a926325fe5951a1e99ac797df948b521240a67be52158560d0954a0c14191ed9f6c322cb52d14640948533c50866593841a398af760c8daaa0ab1d5b78615158c485456058c442864523a190e8081546bf429a145109897ca0f8512289236437e573e0e84403c5fcc5d9f433fe2f69fc22887bc29ed0076f9b479d3064c790b3db623b623b84789c137eecf8b123e80353a0eea1470f3f3dbc7a48a2071f2f7be8e9c1b5a426a3832080eabc7c4cea61b4cfa89322754a4719f446898e31ccd033a903703a32608826c5e2ec8185916d5fe7d419d291893843bcb4f9d30168c3c74bfba5d61fa3ff61348dcf33ba6706e68d50e9c47cf93814c1418643173810e5d0440eaf1c6039f8283b580e413a429e59c2e81d31d01ac84071c6a037ec67bd53f3f2151e00ebcc903b348a637184702882830c872ee86ac7161f0e45b67d1c726882b25464c8e165592a5a0e305fe104e5e083ae70a4f8381207220e07a2131c8836d21471207ae510b47584aa0daa229d219e50f7697185155444b1d5d689c3496d70847c85bf256b11b54efb1904c1302cdd68c20dd98d2e6e34e106d1aa89d56b055bf95805b586dc90dde8e206d1aa89d56b055bf9b8417483e806d10da21b4437885641ad21ad213135999e6d3fa626e30ac1301c8d52b8cd64e24cd944bf40fb6824f40bf44b3f410f6922edc54bcf6294a47c78e34c6f5ffb6824f44b3f410f6922ed857ee9977ee9977ee9979ec5288951f2e114ce9405d16c99c56041af1f58cb926dbf85d652d30150f9a47027dfcf269fbf27c8e51e54418d5ef7e113156d49cd4b25346aa3630cca0432b67d15186af4ba0f8f63d09b7c72b3209a2a3325ced26faa24d9f655662a4524904412431ebc4d9fab98f27d26a6cf26142f50141a0a19285ed816a5664f43a0a953cc9e4e3f65817af3669b1d0cd6b59c86548eb010e10cf194fd141d0213f78e23b443f355cadbdf51b25343a1f94a558442065da9c440a99d86708a9da72ce86ac71628d2bc1949b3f448f3516a2d244aed34a472a4daa033442586af7484d16221558eac90e68d0a699628d27cafd31972228da8128276c6d82183da28d9a99de89d25aeac71f878998497af9f9489dca1511b9ba281f46f1a887cff42346b5e35af1b5851d00f2c099898f2a570c7f4251328b33caa0d3a43eacda6a2e14c8113b46368c716a3d77d98c311c2996254d13c92d6d13e1aa671341c214749f9d4f8501b4b95104b691c41309bfac1c1c371bc4e348e9eab71b87696ec98f48ece906ac3ce18646c580cd97644eed0be104d1c213d6463c857db5befcd94d0b69f0ab2ac97b79f8a5996cb5b1f4fd97fbd7e7eb6b53058916d79e004e148f1753c368e508a06291f154d4506e6c093bc99b40e245eda4fd13a8abcb46fa27524711d336a43b463a8ded8d7f105bd394205670218dbfef7f2a136f671845234f8d9f695a029151949f84ac711b4946dfb98346fb689c3e108813842273842546cfb1947288a6d7fc311ea76683b4aa88dfd1d43d5861d31d01bbb4306d2f46dbf149acc5066d4a667771f82bb4d93d9b66f323381a1d641d23a745841635b4714540714f4c72e7ac217431d8c0b212d62411f53c3e1f24902870c68b0ed9b610bb67d13149f8926dd029664776fd22d846cfb3dc06c0f413dc47c650296429e229734533e45641a88064c048a79947067fbeec7573b86bc7c82ae766c414bfb36e563b140592a325022cb52d16caac85738415e7662d0158e14e64d69fe68f2a66876b46dffa47514511bfba1abdaa02aaa373bb648b2ed5b1d61d0f2865a078ad6f1436deca77c2c4b07153445061aa4a259960e28684a0a26f4d8f6ede304f94a8713b4b4b36ddf449a3754d8f641d22cb9305f8b6d3f93e678c5b66f429a22658215dbbea77c76126a5edbfe977aa7a671b81c470fb5b1af2aaa36e07801bdb16f9500836ddf54156d4b2275c0a8cd4c34718446da8610b5b11fa3e919a594a8148b16f0aa9e656a4600800000009316002028140c87c4a2c13850e3d03e14800d729856564e2608e45992a5200c330c21650c00800100191818481c00b0700f9057d44896031ce9978412c352a1522c12efc191843f78751a7c290e5e74d8f9d8f1522462c98e12598657979cf36e2faf349fbf6338cfd1d16325ac3500e8a62316b1d14f0353c94400592dc6da33978eed09b8f3a176265145749e9de3d981e8723b7d2ae5944e2173336ec3d090aba66b10ff59304166c553a3469c9437798edc8794150c3ca75e20e05f39b078e179d5366b395d0658ac743adaee4b417691f553118584ddd98b6a2f887d2981677a8a49ddc60fdfcc2b2ffddc2a95caf5c0c833ad595c1f2134499a2f7e2f405a20bdf9a4c4e8843f0aa6c7094f6c63981437171a99f1c882f07b1c07c8154653a5a099730fb55907fd5e3fe17ab9d417fa99f0b31c6b6cf9939ef13d28a2a0130348532773f1c236c2286102709c200568f3c35214586c7d97d02649e7b53500d82e0a5c9ba5788d4de9248da4ee27c993c4485ccc0be2289ec2a2f9b6bdebdd1f8f1fea8b23dc0e77abf579038d64af88b3d94b01d60d43ea42d8bed594500c40dc1a717534e2e143e4f4b14613c0f6a9d1ee31acccb60ca107ea536a4930a85658ddb194b0a83b4558a8826c6c43c81f2d38b5914251323469dee9967b06ffab12b4f4aa11947cd12f16b12e1864ebc336f5911e803cc3d6e5ee53d0c7f533061adaa91c82dd0e1dbcc2d294acf04303e3fc78f2504baf152ebc4d677ed63e74317d95c59116b084d0d2afa5be6eb0ae1da2625de0061a6e65d86a1602e4386dcde25d4bd7aad4c3ca01bf6dcf56d886d5b17b8370c96a37321a99b45c19057626976bb257548743b2bb92eababcb375a6572173014b13078e7873014a2777c6bab476c7781a28ed75c26689884c503366a1fbc3f512be41e219a8cf5a2e7d1d2f7f9dafeb31c1b9c4f8fd59a45e7a6c0b622e3c84f1cfc15ea139c149fcc65bc9b75ffa29426c4e710257b1b9d5595dbeb7f2942018de3c48447a6c72e3618cd0ffc48d58177cbba42ec0582a6eeddbafd961d7198f759abf4d8f5e94184df24a3e742bf82fea424687565dda8d0b15a866726323f9a4d71d838f3ca8f9234eed3a1e411bca1a617416a6a6408457b7fde17476623d9c8da4d3110eed5d7aca4f5f6630e0fb2f8070b877e4e72f9edef8be28942b79914d8fe4a763e1a411fd71d0b5324f32554ed4b504e5f8f8269be8fc7bd9b207396a23cb9020a13c69a6848883ea9c13fe095082e0db10ac39621bedac746a23504902f48e3caee619f5968a4a896c162223e05065571591c3b92e4def6b630ed558fd5bdbfe9baaf0f42e3c19f95b0745ac9a0706ab71d501f503380ce40a757f6ae1e0a6e69152e6f772245565ed67cacc830ea06941d915f49eaa85330f7562b6dc37ed31c05f1d9a26af009aa674c6e2141b151b7dfb5c3b1bdfb566d1b9c72cd51a0805b239e80e8cae6ba8fbff4e035dc81595e8450fd17e63402a6e3455029aba93eba70a76bdd64b3ac188751594a7623fe99a83d3bf792a0f2ea95a40f02ab5ac1ce34a2c02ae9d126dfaa9219ce55417dff5c250b90ea09ebe51ac0d0c2c73aa22f526bf28edff42c8a9c64f262544117813e866519f27cac39603c71f46f9abab9a305bac047c71a52dcaaa2b16021b6a5aba4d7851864594b8358a35d9409f718f9e7f3a215bd7a459a11ceda9db20a567f923fc1b3c11f6abf3f78073c7a34611a20f813768d67a8d934b1366b01a27d3f880ea727b6455986fb6500c3f725e87f8ce3fa8ef5de85713a6dd0a36d830d0f24462004c20966b14ea41d0249b4329c7f6c605fc385441ab7a3bb7a7dc24e4db8d4d756a6a343829d526e8f16b0e585578b1654bff0f94b282fb33ca5c9d66ad1d35b5377162c06b5e5ff55256a0f51d579aa5d6a1756cebf7b213f16d6cd033fd6d9026ab25454b17e0b43363efc55c8b5a31d795e1eb04c6535623d9e0b193522dc8e3bd22af8042374721e0d825decaabb98dc131f98aa393805a1a56bd17216e9418967b0c2d92dc485ac8f0c86f6c19422d28657490f261632693b1d643c08c52cedeb65ce60ae7decec40eefdfc25fa1215fda01e820c52671a26e069b9291536542780acd18e361bec2fb59d6933b3c8f42b3067e9750342a9477ef2b75a430e5585e828acb4c25bdab430f8543d2a831709e7ad783d6f4e2916f7549c47a2d551d7e1cfd5dc1639a79120f1eb88012d704e7c9637520bcf09fafebf1dff5fb3862ff55756eba19eb8d0c18258db0b014ae520a06136ceca5f8952419bb988b6e62547c63fa9fc02bd84a61a7efb278d235def88375ed02b8e79822da0a1117a7b8859aaab431b346a33127fb947361050fb98e01e4f445b5b7651f1c724f5b0ccd6af25fb0645a8eb62410d72b356c05670c5bb8e983cb5ca4f71f0912be2622b3b0826ad046e397440f038ae08c60401a702399a861ae9cefdbd9e908794defe3f92f26743a1f36ed784b4eb57f19ac711f6815265435fed2246de3e2a151c5104b0db1e44716e749360507b809da40c1729f1c93c6d5440ef3316fa1e17e62ff4e14e370897507509246b141b965d093f7c9c57dc64c1418cd6a4cc38c9dfd0fe3e39c3909e26ea16ba158a4143aa6eafa15b74b60d9d79189992478eef29283ecc5630056bff4b50a99956eb215bcb64c8a01e3116743bbc6f0950a5117157ad5275d80354bfc45125eb85c38b0e912fc26f30553e441e12a1d999c09426e8796e4361914070fb7c57b7e972a1e2cb916396597bafdb2b5a38f170b58d7d808c7dfcf7896cf9d6cc2dff1298f17c6e8426abe4ae557f94861a923aa48cc975d52340f196410d21dda014240c85ad5b6700364c5c14bc42aa829dcf9acce46a22f1a8dab96008accaa270a2318bda6d0d4d787bc539abec7bc246842c86e5685e1d1cd4969521b53f5b233ad7815c5e1ee6b76416240ccc92702b4b8762fee2531988cdbe099739c5c12db2eb6413e9dd9e9be7c7f51308b970351d77e68f1e26fb878539fe3bf52f1f2af78cfeaaa5e3b57bd13a22843ef41054c2b0ca487e9d5cbf44297d046d7166fbc148a4e9d354cc8795ae83c9d0467e0926802114ba981aafe5ed982855d2e98400f0582c52d195e7cfd4f5fed8d575fad9f9072a1f37f0b9425a001e76d6fcb2734c487bdde42d7bdd67ad93b5216c35356a028d1085907b61eaaeca37adb5b72c13a41074d63ab8bf9b64b0c38130551e706b9e16c6b062458cfcfcef372b9836f5ffb07b221b289a05913f46d8143393becaa2562068144c1ed057a32f582ce4bf7e8508a44b56a329c8f4a709304763cfcf8e6e1986f800e5f4fdfc38837552f20fd52931043539d95f6108dd9c9ddcf621468a1132d929a8cdd8f7f9b09782f2dc09ce2f4df96f55fb5dc4e40a0195045332027cd807e9a01118df5d40cec898f9ec19574deb4e8e5f9af7232fa2f9cf5dfa6d03858bcc607d35472fd6816c5743c883a6aca8814143fe55ebfe7e81a37a30ece8f6411f9343d99570fa0e01832d028745d38cf1d368d74747dfecba36c61e4859cab72c43f78ac82ff4060e5379f2eb08a1f029b3e5af4dbb8d3f9b6484935230ff0c77d5539c08c839a78d39c237a83a866137b1badd7dde590d796c5bf05da25d08af8072fab60f4a13c521d43d0887ba0a2dcc8c4c19374e7e19987fd3a79d5c08184aee8b2e1fda3430ea8fc28d42f61b6a92db8b5b189c2a45107145a05c307b42a35aeebb6607746ac574100d2b7d29fad7b674098be4397672d6556ee7554b08ba6cca7758cd71b599dc5c79af17e645805bfa8e81cadb9286e39fac934f4f9236b2b6d09e9f617f232d2128dc8470a0004bf87441ed76c4bbb363da97adb0f863b61a523599a5d6be934fd7794fb297de80408122e12fa8ff3005a94e0205fe74a53f486c94c5dcbedc639da62296e52234e07cc0251edb17fc90045ad9672082ba5386c7dd1abdb121d07bd97cb43c8cf9e8df2364c1089a9fd2bb8c9f4745f8b651ba00105f1ecf5eb67d014d16bb0dc8dc70b040b8b753caae84755af65f022ffa73ea27eff3aba930ca75d8c12a298cdd93edb2d67d2626ead45714f3bf847b779cc2c6c978bfe50e7b5f172dd3e6e6decb660e6aea30c5a7132b553e71db357b1863552a8118e2174595b2e1e1af56f5e011a12ab013f58d0bf856b02260a21dc98879f6183c7b16b3ee1dcac90f8b1a23a5dd4bcf2576227562c2b912012f1a575afbd3a9d2956372bbbc3c18fa0cfbcec7b6b1593ea53fab3cfe539696ab7676ea3efa48e89ca95e567af7b37be17bb9ba0f7d76b4bead4c0250a3167c8977a1a52f6c345382eae8a19646f29c44078bd5f6ccc0a8e5162d8ba5e9c29459b833ffc4ca1668af02da031d5f716b8dbe4e9372aade826f4a30eb58a52f0c90012d04f977ca29cd3cde7151ec77e56c52ed6062e98f9d59fdd6785534162b8cacbe31c29beecfc3c881ab3ae2aa069d9ebe6a97103608371b03a4daa172b9fa3206cd1592dc07a6264362c787b8017ddc77135000111c260e908893e1b3dafcced95582d46444981d4f0c8cde99fe4393a0ae9c7ff15d39db4c66302ca96a19f02b2fd1ad6e7d28100b4c393623c170c51faf841139d920a4e06cdc5e66aa94e88ee23c28625a565e5630635974d3effd5cbc4f57a2e2c3d36a020a4b9089e661f18ce9064de015fdcd55414b98d5849494a3b4e5d05b79c79a15c9c846486bc21f9f96fdd813947df0a49f6c7cb11f3cbd0afe2943f7ee7724eba17cd12870fa2506cd97bed10ec227ef14900ef7227dda4360b3ad8c5aa1f2e59d3aadb7c709ca907167c48a8dd95bb35078d159f37809fab386357b48408f212c1089df076a3608f4a70debe4eb8bbb2aa7344f5d8495609964fbb52613671acb1aa8df0b6b9a0ab9f84205b1c1d87a9a60355a4fe65f7932ce4e69a6c61053746e9e61e9c4b772511bdb6bfbb6b4434eadbf9cf4b8bc6ca781a1d8bbb5aca091ac6ad17e5d294c3b745208fac95a78e253a4721d7cf59800f9f64ced5690272ed1f2e4cb5e1cdce39e49af844dad146b675e8942ff70e1b1141a8928df6928067e3ab2ab43b8032a48f86e7359fe3f1a645a02b84fead0fc3ea0f6fb2be2af9043bf46888471258e55a0d2ed6f3bb165b05dceaccabd63ffe97a364248db40600688469a04bcf00ece18783d1da9cecc7e0ad00fa56827b498ef95162ae6e3438ae1e6fb6191fad9f9b26ff5d555a950a1402e8dbd7e63c7288b6a8432d8fd3cfca14d05f48a45fd1ac5de8c9e68991095f108196a2f060a7b868da01a3f7cbd7a3b37f5fd9852e7ca80b7de8c2af7aa0bfbcd57f77e18a423308d21b740e96ab4d7c7d05c3874870c885cbccb95d7b9cd87b4c3d85459436b6c0291e5f499d64f7ca48040d5a32f6946119857848280a718806aa62a067afea5dd1850d0e41e89dbf6fe85231000945e6964326008e8041fe3f1e15f966624538f41b585584c1f5524787dc2bee879163c5ca9ac01fc9e52289efaf00e9d406a06e400e073350870e27bc7095b6890323d150e2300a852d1c87194e24b8dae0857d4dab3dc5b43093892d1ed5cb445f485de206b1c2fc574a276e4830b28f04045f900083815296fa29e5603c9808e8964640110992c2f71d71c24a4ea0d50a37005bd3acc9ee09d379e9f5830d1423851a641980c3b8aaae8a6fb95a1279c85de65aeb82b3d93227298695dbcf4dcf3ff65f512b7286679b86b998438a1ec363c4301cae8430a738b3b458ee1aebca11f04a9733188e2e30de253243bd7956864e740a4048d4d6c3ffd5afffafed587678bdc7997082215f5dcc36ae4c03adfc8cf9c77defe808b4d7633cda82535a5d4e90f54a9dec90aac4736e7dd0d1cf43a4c2a827449b9a63b4e499d2487bf0f5985f63a05de02d7c6cf64759d63e3f816687a2e36542f372aadf5758054e3f851c517b88fc87e9b6d0f50eae7dccb1cfaef329509b240604e0545eb4d0c6648ad35325b34e4bb21c8af35ffb89c92b0fe77c755f2b2ba32a17655326c8f6d30a2527a7f66c3a06132890ee4ab27334e61d2750d9e356bf915b17d9d36a8a4c42f49778a3224d25b4b764bc99a5654d6720a8f78b177b90ccfa446a706f133e8678f50145690ce9237eba09d05905165207306fca7efe80bf3e96a97597eb19b49a0c0fcec08ac9667b5dcc92c9d675194a26ca5186267c5eb286d01d123487e0638bf717143012e630441873bac40d595ac31cf5c31ffab364a1d50d7403d845ba5b26ee3a967750b73aa7b67878143bbdf50a001a28fa2f4c84d9a791dbc8563f8f545e6883f77b3237d48dcc01944066118eb890424b673ec9cbc6cf33541875560041c729df86760bb54351e50ee9e66e2fe1b98642e0ac6db64785f989564e9ea619237ffe68ac6bad3c02906b4233c311fc4614c541c0e6ebd9b2799eaa528e91a370921d94b7f2bb5e223006301fd139ca7cf9b04d8c4a06701ecbcd8d0958714fe671efb857bcdead4dd1c376d79ace35aee8e6e8ee7a267b239fa08f07e0ad6a46e91f0438e2c0d911dddcb288b3f3212fb37e6c4e4ba8bd1209011fbdd5085a020eabd806439c59e7da51eb2926a6cce997c6f258b25d7c463585ca22d405d85ce7318f39cc3f1c2048efa0946380db3c2f3d7b806da4f35d4dba4640522c278c36c667d65cf29d3d5d37d8b2d2fa7dd1a2f9b4c1bb630a4651959d09cae977d178271998686c51af23adef2d5467a3aa95f90998a562b8bf07e31039b781a60fcbb445c02ef2b05a73c9d08aa5f3c18b784194e50bd7c5eb6dedfec6ec94b6e713e467bb6b2e787a8d1472aa16a39b55336e8914d4b4667d8a4d90365a95d1b7ce4c300df4d2678af487952f8459cd3a87f22c4487761672bffc63a0c2c6786c65d274fd35a6bdf837c3dbb42eea4e2f01e05421c9593ce24f9e8df7e6250a4cd0d80b1f05dd0eec5bf1b06191bd1bff259092242df3b8acf325920196396ed12bca1c55c16a76187b2e4fad53c98aeadb7956f6dbd07eee15d1c57f50a4b8f75036d30fc02fed6ab811336560ca5898e4c50357a4acb6c020b1ec94f7492392bbc8ac8160e5ec1f86aceba6951a5d9bbbd9d64f9e2b15dcc57bb414542813ff63be71dc152ba5aa762309404f3a86adb9176658c295d6773e139515f9db535950d387a24e94cf20c9c4e08828a5a06f763190121e4a8818051180872739693bacc4baba1c124c4929a454c4354dfd94138434315819f60a61e1619af8f87ab47428247f10190ef4f8f5f81b9b77f6e6af73de55e11e1024afbaf5dbfd1501408696aeeb70565745582f1918b4a679f6375d934f0644374b29eedcd43282ff7c02cc088477458e59837bb9764f2f2a5d6d32741efb75399053fe1fd98f570027c6b75c6f7fd28e6bfddff9415e38af975aa70c800e3ba25a390700c34440082b3fae3587f48b1931062ad685cdf0a2a1e2aababf222c1ba658099c96a400bf983a6b998d60afa40cc586ce2b448a1bafc8d424210b7453dc06d05bfa0a77e360641da34be00cfa7ce29f5a3c6f447693213f0abc31790669d4dc5653cbb6075ddfbacc1b96e0edc0e8a8a484b8d7a60b75476017b5f959371f117a3adcf91a3538f5c4e214a54164a36f7632865d371a9df3a1c14edb09cb955bd8d9b8c130d29e7e1fbe72ce8669a795d3fcc757a072ec836424aae1d7c0374e4eefb8c154d7872b032b1ee5b8c5f92c3494f26dfaa36e67394d26c0ea03cbf9a3bf46dc510e6297bd638765898740e168dfe63fad34f8a62f0ede1d8975da2c8206dc26888dbb84a4c10b407abc4eed3469e6fad67760e13d25cec9e3b353890fe3c3640c51bd94f99dc65561230acffeee4ccb49abc18ee64e1d6411cf288327c72edb32452358ea14345df34daaa868e544b4d199f659563de511779367da8563d5fe4aaa7c0edbd48117c257153f6134742b1dc08a9d4a5fd8ac20dbc3906949d8169fa1b1852b9073157f1608543190376b3e01b9402a7023eab8e8e7c825be230b789b9bddcf4f56869352e77ad74c2e77fdbf5c3ee4ea3cc1013d26a376491c6d136f4782108b8815a9bb0432f3a380f853a98d3136934f08a0f66d225807c681e24a7f744036e57337db5c028e67cf42c95178eb19a016cfa40580fed0d5da4985209a038320f288038215ecb8034e439418207600fdbf1bf457c0dee4f6ca48a64dcb6f414d31f0a2c0ed68ebcbf812f2fccf608fddbd3d825969aa2c64fde0757d61f20addd79e3c972a7f513c3f18e03bf08f6231dfd2de9de7b7b4b56458bb8d6ccc4523ea1a81c62a7e045ff354d69e63bce9035301c1111a31df36abbfcab9ab2cab6a91e711b3927746a8d70187c851e5d916d7dc9e6ad344163729c12a2868cc3f38ac20b87e5c078f232b738f5503215d5597a7b77b106d7e3f2c02b7ee1a1c97f4002fa343f72e1d2b2a88dcf4cd167c04cce54344d1aac9cb6e48a0bd7ff8ca04771fa2fc002ce5f6c35b45cf304f19b9ccb4192340f64aa92d4343413dfb7df4fceb35bb69d073eb4d8eb71fcea627410336dec6d68011f18b8dd4ae1ba1fde0234761a2e24e2165a21678022278f6ea76db968efaabaed3ab58cb05a22f92e85ace73a886142d775189748c85d506f60cec333067c1a71a1db9d152b20ec279d61a8846784ca757208a6fae2132dab1803f28490efadfcfe9d1dbfd4326220aebbb920a91e3bc44bc78db2d485050c9cfd1f4326d90d23ee2ebcf5c441ea07c6436b2de33843779c2cb130213afe69d7439648487b2d1639ae03b87e1054e71fea3bc57858ce47797e07b43515d0fb2e1a29cd8bb8f54387d92e747701a81bc0cd6c670a97d96c9eb60f69894f08960560dea6b4a2ce0e4c0cb0527f7402aeb81a5f4c0e483427fc954b3668fb0bcd6e1284309adb2afb909ce732b2a497dfd28674720373e7bbe0268e8e441e5ec6bfaf1142e886b35bfeb9378b1412955e34af698376cb5ad8a74c6dce5aa05950d50e13883b686a99aca8b4c03f4d6eb1c47feb106eaa68bd881a4ae1f82f76015e443d2ecc31b79437240418817d705b3a93f936aba31d9cf6ad6e7ac0ae139e2686928bdc70a3edca2a7bbf1a14934fb430ce6328ab72aaa9c06611e3a402ad50a9bfca1095e39ec3d9253e5ee7a87f876887f5fd79cbb809fad051cbb0055f74e1a8464a4e4c9865cffc605cf995067c9a3e7f786256ea0990c8030787b20b3aa146c4193403ee0c878a36be433bc22e0efd44a0d3eaed767a70bc7644cead2de26e0d76480c4251f4e96492123b0fe2db0457b0fb305edf44c4fafe939515ea23a79a983896e00a97f98dc7ed1eed5d21c5fef55adbb624ad71155acdf1028d532010133491383d208f1d444efd38749aa22d9831e22bd67a9025e5dd902004e951cb6f685401661e97abe4a93c3dc16769f2a2dae668c321f6b2ad7c2041adf286317fadc16ca7890a7abef979b1ecd04b4c54c5df62737a4934f3990d7704b3177d29849e9bb21500168a7043d52df4ef18d83547e83fa0c10200b9920458dde5613c648b80dfa73d2d13e06d0ee54835eaa171fdd343e8ac410eff713176ebbe362939cffc4333488b1f21876de887fac64f7672b0bfe88a7d85dd8eaf929cc3b085242bebd121b76a84eb526ebc4980c01c9f5de311cf4723075f1a8cbd61aea14f45350d78f89b7bbfb036fe20fca4b704363286f35663de3d399443f16172db211974c4544cb9959de83c16efb9b8fdd4c351670e2512f95cfb098805e342dcd5632e4a02175810d4c9758921d8e3bfae16dc1d2defe32183970751ce99219c24e2c64b6e142e0b3ad00708290cda652b8fd819241d5dcfc95b64b373ed0b13eacf4275f545d59c8e8d42e5b7707df115ac31dd302999884280fc707805aa6ecb99296ec0e6a82fb6101d7f60617b66ef63ea6466d73500d3029a14af1abdad675a59bfd9c941295c2541b84c61729802dcd6c82ff71fb47660c6574532045830e05ca04b977217fc57f4102de4f9b592434dbc339077f0efe398213a92443d5fb425324973f01c2a39001406eef65cb37674e369b6de23a0bd2770c6d842b1a5d7ac652f4514813c0cbbb2f67c8d809400ca43a32fb867b683b7f23a754c792c345d34528b6c8aa63e17cc53bf1638faad8f02a843b43b1467334d2d88e0ed277288b2cd0256951e00fbf6293e7a1bb83d2fb9d7a5236bf820ddea769452dc5da7ef936c07c3cb7f03c8c82322f545ef321c74e47995e0a1121ae9cd6836c46815a21c1cc50ccda94d6d0d01fc1b68a06dbc4830c9362810112abfb5654a9f4138af4ede0f2a7ef07acd33a74ac4459231b0c703408a5348836375866858c58b51dba327481132a39a4adab40067d20f46de0a70b58395578f023901303e77c9b52c74e6ec41229889736632d8eb89fa79fb9495e46c4b81b12acca4f1a12668bc48923ae0825b7573dc48f91e2c29241c1088026b93d0697fbd4564db47233b56d74ae2c31b714b68898a0e4d387278be776bc46d825082e9da9efe9391e00a6ce64093311ffd111084f6c92b5e170380d5589e0ea0244383e35f2964146d107845bdca4f4165062672fceb6358e3b28d23f68d56a00db5b0d9571c0af9e194aa39db054b3bfd1ee87837a746ffc30b81f0001dc48c36fa39da242d509dc5c791ec478d6ffdfb41f863ee99cfb6cbe928b037fe6903697851b8817fc6bfe184da9b469298c5402fe29ff4d351cd5d1fc44a2feaa4216f061e146c4916005383d71ccc1cd0fc7a95411a495f2982011ec268d361feb1c592fdfb85d09ca0a43ef500ece9f3e87242162afb363e715163c99ca8ad9542185dee11fdb48489635ece3c6e00807349f1d938549f8ee3ab01402e676aa5ddd20cc3784d956a591099635287058077c660ac639afad78c40f58c364e54e49b560212d56cdd4b0d59486f9326dd981319574f51b4054ffd9350d8cfcf42a0b0d525a33d27325a886f56c441c6dd7ee423ce293f05ea10c4c85890d8471e192b2f138884700573d8ea21b3cc000329707c9713eff37177ec03465e6e195895d5ab3744171a4914cdbfd960ce8f88bb6e8d1edcba96eaa5f19b177fb10581629e435ccd5cbaaa328a645aa8b0e20e6bda55a60f99afc98cf114a95f703405a1e57e433a87e905cdd4aa5aa5c6c2aa33a2b30489bdda0f7a98082642fd783c52a908f838ee2efd1445ff4272a20be990dc93852c1e9795ed93d6dfd3b0fe3ffaf7bf1ba0aebd9a29f3979fc342cd03b07a8451ee4b248f89afa32a8fd1514b44321595f7be8f0f2fed38e29f56f37be48fc6966805c431989a858640d4ff0c6ad8f92627d642f3a4fff3fb41d3623b6a932ee1b37d36c4ec85809410435ef12adebe92f6db9009c1a22ad03998935f93215338b1c08d3770538bafaca928eec13b8af124260f623b89502f274cca8e82775d6599d9f57461b64fbe77137597dad48d971f3c66ca889b8516be230cd7f798e947fb13517fc8391c7813e1b4b9764681d68a88e672f10c64cc5cbd2625f3585fd81d8080420e7f73c9aae26471b743a990a1b4cac2a5269f392e43e337fdd0c0ef1d2643b7823d9fb17022d1fe08710f2d236abb1c3253e70041b010a44dd5dc5f0d1f4855d5af5d7bb598908423eef949ed1dee1ddd7349a34733828f58ad0885daf019b1bb3a71bd56bef6877ea5de59e0c52836c05a911b8dca7c12acd16a6f1da742c962a949b3b72b1a8fee40695e74603ca0631468991e55d2bb7c18695fdabf7baf789af9b975baed6e380481335ac5b9eadec7fb6cd5db30fe5ba506e6c511c101e32bb9f6a44343a1254a2faa10b7516fe14fd2e1ea63e01ce49cbc2fab2f6aa0fcd76cc53cd7552e63202a746aa0f360f3c3bf319f09ca091031cb341d4e5cf891e605f0ea549a3f1a453a8c22032a4d1fd17f7e022c42df751fc5d9b3668d3b3811bc7f52c43a6de01934ba982ea629e603a706af35feb7a633a9212ce479bfd92f86d192eff285700d9ed07d2d096260385fb3a3cdb84d26e60b1a74895348271d3c6256b86a918db89a35e4e46289addd92b469f5ca6a4524bc8c22bc4973fd257217cec9112db1ce4f937969296fd32480e77a46cc151966147aa6f0cc96a67955df7a6f5d6a92b300c51ad005251a7d8cd7fabd472c1fb08cedd8598926a5abfe130111b6ecc535ddc3fcb7d26a0c9ba5e3af35c7f297191241d5272c8096e19549ae353fc93e8d04977fa327f2346665c299041c3e76ea72b518f8820d7eea0e9881bae5c53f5e2dc3a2956e4a9b67f8671b057702816d2c19d016c5fd0ce53a88c404fc6a826a185bd029fbde67a03c0d7a1a3aa8d116aa69c9a20a93ca50050fd2f0c2e71e3bb417ad3be6094a67a6cfc9a815cfc33f6ca3ed6cfc58e0c74937cd538ed61b1a69711d3b609dffa79edc225a675bd087275545e91a7a8ae98c73e51e1ed8e8ee70e91dce8c34d471fc036f8e5552069924117d956cd781e55a6ed5e9858afacad678cc64f26bbfd5c80700f1eaa2c047b1d7dbf5fc3f79e647fe43da44dfd6dbc77137b0533aa68091430867e74a922cdff80b55b0359f1dd53db3965bbea82782aff92a888f1e8a0114ea16cc8776b38855ed2643e416da48d9f305db061b2a88589185dc80d8f5e5526ed177e2ca706870e7eb865e9f0ca8f24b172967494421c6a682e2b8d2786e0ca7a5f3ab70686cc2503a3a210708873079d9db8bd4f4581f8066fb9a0fe4332e51e828b88eb3ba12994b9861c7f8a7e5da71fcd44f14d65bdf143cffaaeaffdbc068c080d332044506c5fd957262e9da29c1a57aa19797854c5dc27bef8564d24e49d24396627da80a892418944611edbe40c54b3184d44ba393804520602bd32e5525e82c142057fa2bc85617fb46afd0c700d119fb54b99ca0dfdc531e434a63784dd8153bef6a124b8db24ce6fe5897cad7bcfd7e8fc0bc3f8fef99970685d8310557b7c58146afc8696090ea9350ea44e0ef7640e6cbd330653bad501a0fde77d55771d0c69ee782f2e10857c5b07176c890da09f3a3842cf4e5d0b7b722c1c42e1830e5c0ce14f9465cb8107f3c1069203969ed996423b263e861ba9ef562934a951b867efd8f19b32087df328bd18aadf1a76fe7bc61f201a290484303300ea4f0471308fd2c48071c1f9b97ebe3a46cd5e0350ab57040283dd6310cc08f4b77734f86dc060f507d1343ad62ac02a5e110cd8398abd675c1fde7aba7bbeabbb936bb6f782632c03a292f68e2d5da742316c676993dc9078681b1a14b361d007367d93ec3c48e310d52da2c1bd40c16d81193ed950a3fc66b784ec653cf8b2e1fe32cd02937883f99fb29d7251aa03aa5b557b9a21e31093386e170369614a9cb35bae3445cdba06407f47e1011ba2785200b8e190d42d1ff417ddf2e3c3dc83b0770f3bf0fd701bf02952e1590c5dc7a40071e11e8ab98ae8544239970c1bce981f59b28fcf08db5d3a54ecc5f713e52e59251494a722d79210a7dcd0f4cc8b4f97a908caa2d2c6f854d9392c0daa8049f8506cdfdedafd016110fa6553d682b29b35ee10f7906b6eb28975039edf05e5a3378ea7a89814202edc23edc677f3a858ab81687fd113d032472ba27ef14f7d6496eacb547618d0980e25b6941b730aed94d25f3726d49d7f70c5e446826b0eb38497929e7abe74b55b4a8bc432efa03fa5a851ee726ee8b8a04268de8ec845590db06ef5ea698619424cc5f05d6ca48320f1989dbcd2342ad61a0228508fa3cc801fa3385900b8703cc56d4914022f038fc4a80b502d801af03abed5863b439c913bfa614ae20f63beff3c650ab5361816b00e3e55824a166887226a329b00aa73f2cce9a3ad71c6e36f621026548a74e227f047719ffbb00f004a0e479c33aad7eca383f5d730262fad0cbde31937f136e5308d0a3e08ba7ec28170189c788c52e7d86630ab7d0cc661baf8111d125b50137ccb87ce173803e975af20565e74f8233485aa7a24a6e042b76fd2d03a41e0543bf431fcfb90228d1e787b8c93d24e6e4f2d8920bcca375cd0979fddbfbce179c405cf5041e98292560dc27500d53f9a5e89dcebe7049cd6ed4aa40a673885e5e73a97a13a818c69309a1fc4f5f06f5456f83fc1f8ddc49b22f9807d88e6f94203f7b3953e11f37916228b9227426f70f82f5cf87e027b0ea484044f9b4f52d7d99e1cee09950ab327c1d78c696dcc242f55ffcbe4ec26ccb2b9226a8a7f9b2fcbc59eb6786cf82c1e3247635c1006ccdf2d22ca76dd0b9be864ac3d2f358160d051865f3fae08adbc0804d5c587422ed186b8f570d58bd8dcc7715dfe028d26693d046e7e7c3bb71813c1c7d1b062a30da5adc16aa80a0ba24640d1c5e6c21afd55b108bff57d5c5762f4bed7f3e9e5f0c682e368c34c0084977a5810e982b2e7d1cc628ae3bb8ed01df4ed058a4a871c702977b39fe7a8fda4dd3829c48b1f1a7e60b23bfb4f8b333eaf104641f1c029e5e90ae4d26b7650a056ed119900b25da30e8e5ad35fe9c81ee3d2878aa13dbcb6b89b8e37a4afc5825d93a66cda4b1205e2a41b84454a349b88cf73b181cc56afbacd228caee73bfedb6c91a13ff463c36991e0dce2a5701b9e2635dbfb9d468a5a1307d4c3b355a181afa0e2c2bf3e17987cab465ce43856ba986ec38bc7641cf6a8b3def287e6cacfa79b36a6ec734e5b08a23b34d257594c4a44d12040fd86dfbb1fa2e133742ec0935e8449840b601181a38299c57ae854ea0eb1c44bc28c3dd6c20470df1344a74d214c5dbaf9a73620aee559b432697292a85c2f40e3b410f8aaeec95174b9308d48facf99c3c9f7764f990e9a0837ce0d5c22ca5f294b2c7f218e029e1280ad8d1a179b94e41f8deb0f6a7001e47ad63041288fe57301cda224850e5fb293dcbf380b084d26afd56075b66e6e16accfc0f3aea5654b7e37704a4c79862d6908b6c6e9b90ac8c6a505945566c2e2712cf6bca1803cff60c835645969e983f621bedd6dd3b5eafa0622a788d75fe618ac8727b6823389f528b234aae47b8092c878656986b6d27449e45fef94168a7f5004685adc501ab93fa2087923b3a40c6088d6c473a5db5000aa9f95216fb20cfb0a68d02ec8c6b0c15cf6a272d8fadc9a9b0f8ca05016a1960de49a3474b2715b3fb9cc3fe49549648a55b76242e0e490e255a2ab08a33d5e8f033d2b6880c4c87495a76098671b8c2a5f49583f56b8e1f026110aa8d1c8c5304593e1529f9ed07dddbc9111da564f95122591a1b67518943d65e93e8a60347cd4b8a76a1402033d61b8f896f19092f0418114385e79dfa2d928b090c8e2005f0e8432f9884d9167c9bddd2a73ef8eba970d6c06cbd2a62c581f76e176b45bd0b250cbc296855b16b48f251d04f066d62a837a83446b72abc476ca8e238989e503f65c588b1102e05f9f09176d69fe99c8d536daf37c1f7f0a3781341d434f021b0f15b044fea16d78fba390b1428391aa790df1dcf953fbacd8f1d4e26ba2bb48d7ed9535627358821ad4e022cc0bfb471da833de4aab9aa71667030c07cc0ebe6baa772da1d37426345a212e7c9b0225afdf5fd068947c9501801a6994f72959e9fdf92b76156c1208b642fdae85ec805e18cba60c0c04ca955fbe1b9e1b8dad52a45186d69224b53c0b973adc52ad754e946a1e7b497f2eb03fe4835302a75e07916f788e26cc22c3e9d28de72ec2a4319c32577a649e25fff5e42be5fdda340cac38f163ed7bd3d2623955fee749f4d3bf6a52dc25496041e2a223a6651e2fd070b0d38b8c76c06481a1141f711e8c83cbfd364b2c1ec54660dc01bafe53ec152c34c626e6b77c1c87bcf1e60006894f4674301a24032144150b7c44461f432388f4ce04d68c26e6378109ad42287a19494ecbb5da961dc0b5bf81629938d5532510dc21038d1286d34b470c8d9187f306ad2a847d5da46c236441b64eb46f657ba06b221b2e23f228a5bb92fabe92decec70f74a85b1e42f0e623c96b171928f94953a743ea4478ee44b76fa50a0954a3ac3715e0e14d4511a159051bce217d0d38efa9c0678c8c10e7f19856f9d53a4f865edca6e6f63e89a7934dd4594f9c2b5f132be03ca3cca32ef43572c0ebf34dbfa18896be4356f75306a1b4b03dfb3a82f4e79a230c9c1c4cbe2ee1f4565e3d36e969e0eb5898b4fce7edc2fa1a59f8999b408b20781d41aacb8f293ddc105ce90f613e422eafdc6e31cbec7802527815675cf3907eccf59f183c88b68fd7ce1c70ba3c022d7827a17038bacb9ea51a275bb4e2bd9a55435c7953fecf73b5fb05fd2726014abdb229edc1318ef959f23dfc971490982b6f6c8722784e50884e7282b978e78c668682176313d8c869a3479328742436440ae28652ba218a15964595b6ba8c98f7504159ab0f4202cb244f210958a59610fdcc55a89bb750053e8a66264199bfd62add334ec037c7b5217011b4160908288a4de07c3937925bd9df4d9664b2c0d28e05ae70fa0f44ca7870bb579bf29f5682c5643c9759aebabbec5d5db569f0cc9287a2347085b977f12cf861e5b1e084dd5c419e597478e16a20d114b9ee6a93bd875d236155a93aa809aae1f4ff78d0a50a3a4012ef6a7d9c79fd80c21334debcf41dcc7459466646a5c6068f2a636cca75974c3d7da2361c8f41b2b7bca2e99b0e8261597915c1bea422b949a3d1ab4f28b57bc9e6721f69e3cb5c396a0443e280adb0757131c904ccc302f02ed03f4a37cf85b371691b8dd8c6faf89cb574fa88e0d1590a9e9ef0d7e0dc096a0049af2bf01f227d653509c2f3e344edd1af6e96fd3ea5052858d5afb8ffba3dc24455e2e84784d3e6d62f00ac39a689a6a1aee4ff05986a8ba37ea0a2cec2f59bde44f62533609da840628b5107a8e496c67a7d6a7e5d37a7b2c15ff2a96821df4f8a0d3316bb7f99562c463a05a0af0acb3138c7b2905bd9d37b54d552082f86255a9a19200322b2cf816d2b1f1e82ea20ed6ef9c3c4fafd41c4f147d28d07acf5f72d07ab1d42ccea59c9207519b545db425463a78cf091c154c8113ea217c78bd9dd4fc1f6195945ea7655b3d18d10c5b7164c8ebfeb155a989c3794b97e87dab89d66645b18a218bc72410bab0855bb72eed69a1661a2d8451d1e9df77cd3d5c1ffa77bd4213a946e4b22f971e0b0bc33183776b8b095a044b27e94cc21f58c6fc221fbf03af3eb6ffb5fdd8effc0954a697cfe453432afa36cb757f202acea396044380df2f32f6de006fd850a984fa5a4eaa33b029945ea705df58e5a09b29e8376e2df4303a5745dc3f1ba2ef36a7b11331110f2cc6a8e6f39912f0ae33bbcc2aa6d82e1ca2f144bd1487e2dde4e7ddd780d031bfde62b0025111852de6a1bccccfd4adc2081923dba1329dc656adb59ae6cead04d2865d0d021635cb66d07f1f3034ce1d0992d422bb7d876d5de490eca70150fae41132366648f18cc0c3f52585db42398383722fb9f7268d5bb6a73cf0fe9815ede89231713b1d820c1af64e2fc47898306c952f187d89adf24c68da8d0f86cdb9e79e32f15df09fb96fe83c13c3d1ec021c2d4a41d6dc4789d04c75e322ede5ea6953209e47fb42295f4a214766df210ccc4c4fa6207d52fd42a9b1dc3fc6b2336a3d396673654fff8dc58a31b03b82feb8e0ec08e4fcf182d7b313fd83ddd6f8ca44e1146c6d4f623c214f78c9ebe47dd91f89243c13b92635fbf6adabc9581812cf8aefa973b846a4a5bca311fc2ba55ce7b07be6b59d1454c94ed0b8424a5bb72f6a40a6ef0f82fd5abc3154bb595b1cb7bf760bf668e6a3eff47c57bc2811f55540bfc720715e506a8a30307b310a241d8d7b7fc9b7d49112ca0f4129a6d3de215230f393bd37058645ac4347d9c0cd6ee7b304ba387458e1e7d30693c2e438f6d4c062ad49ac6e0a2db67c6b3328f5f24ed7d883160e43552bafb5f194d6a0f3dc70f9f18602785a96577add00dab1726ac5ecbe0d95eb10d2df09bf20287598a5636201dc10396f86772fcbf6e6450f1021963e2c8af1e7fd30ecb45d713dd8609a7087a1826383aee873589a578b805362a8c54baff678a93aab8ab2fe8a6b934d20c53646e5d46125283bc3421a5787346697e424ea63b906f6f47f3d252dab229ad87087ffdd1f79a5d01ac13e4e0a3a6aa495304c79dab45bc02e0b90e15911f51203da88c749c02f181c28403ca648f9411983f683d27a1e5c60c7eaf9e0835341aa62be51181a140ed780aa3e8f840491a772ce844bf476ef9d0984e1ee1d21d047a6eef366844dfe38ce04df215c251b7cf0dad767c44786de61bc132730337c8cb6102a180efc71c67b20366c2ba134def3d3612bdd76e3ad8965ee7b80e92c3d9eb090c573dcec407fce09303850b972158be1187590e216f0c1fbfbddc240da52100b916cd237c2bc00635f43aa5e20a22b34adcda2077837341c5238d468ed865cec4b33326c6e01544fdd8f2013dbd2aa5cece3349bd6ec14a46371b1f7e2779ced7f10379a1e022817faa9929729e43a5073e29509c89cbe46618fbe200cb35603a9d5d0ee80027058e03e7197d875103bf5cd5b5dcfcef2bd155403b4942972f95b80904c01d77e09a986cacdaf81787f80c17caf14cdfc4baddebf60982d2f1c5b92eeacede2fe78c722c2545e551aceafcc6bc0859ee52e4c74d76907ed592e044708472946fd8e6e46e9a52e10d769cb6a5613d07677caa7c41147126061e177150a3e9a97d7f88855a00f250db9b2f81f01d99f38e3910cd3b3c6d5d0f53c3e99110162722f011cf89b967c94e83f00f4da98217398bfb83df3bd8a112b9c1b058f70fa89cfaafb49963307ab616ca5402b23f2c737e020fed5d53554b31efcfb9cfe2dd501427b33b0ad0e74dc0e32bc20717daac3802d33dccb20e88fa0b0a013de0e40071c1f1b8272ac3180b35d3aeaa20a43f9830f63f19ffc9c227e55d27e25eb155421efa028c51045084b43ff09eb20a72d5ab27d470d1cdc607df2aa9883cf5f677847140fd75ea2ff46a301afd20f4c6f920ae63529d67eb7a1a6c766b708cd73fb84981e3c23d289adad8414e17e3b19d81df26a7711f9ee8f71ef99da2925102bce6d8522ca8bf0e7fb29de45514984f1d092edd72a3f96738e37fbe54a347482082d012652d45f5a381dcbf5289f3699530ee3d7c4ae24a927642fb6c749e48c059259f7c4f724bfd4d4abf84642dc3b5fe608ef80dae39dfd0038a0b76152d6e35522051e3dbb537d449d30b76b86e669544471cf321fa1b01ef79138c5d8192358e1bd6f8ea54bcc3d372813ffcaa50599e56a27c67de57e3cd278368badcc953f2e7587db98985a47b0dbd72a72e9eb70d469e9748cec43321485b8f7545b05031f69020ddc7dfae8e07ff3d30d698d376a8b4c1f65f2c52145aeb20d24306f630cf4e355a70e81a4d198b81e8e0ff66a13df252188058d2117d87d12bb76c751c6b7685c1ec37c00a6484587fa57f73891e078cb936e30f163f18a975d079a1acdd9d57efae1d537132d233e9d4ccc003554ee191997e28ebc759608e3dde844a008e384b8ed7f7e5cf768192f476a58158c05f827ce295ee6f1e70a5d6fc6214f1d7ff730c2133422de09743d673edcebf6091f83e7f8717cb8ace7b07cf71768f6411bb37ec73f5d9e9c10a1aa82cbcf8c69fea267c9b7387155399c06827713a8c73d5fc24f21fe71c4fedafa3017f30e3f019774f4984bd6ed08a7f057b23da5822e7e1acc411436c68f6966b8ebe8020fc7b3d77b61dc6cc2ffdd01217e8085a23a294f79b5d80e055da57fb1c37f13dbdd7d272612e0bb872056e3fc5f7f29c873752907ad55be0bd41a7668fd79f3511e02fd77ff4d96daa02fc88848c72b3dc9f8b40eccd2ebef600cb3ddeb87eadac9bf100d962f147670058b3807244034f0569c4212800f5cf6aa454f64b9a384d1f29707a768d346d80a2d8eef3423b48086c271078760bc6d8b18e061a07d04b71b5f9c21a25fe244ca2c0b055a41b983858c6fffb07d5cb6cc2f41a85af6222b46142078234de699c66b369ee2bb1099a1d780d0a87b918e4cc722722e4dd392f7b59a0bf7ffc3f4ee793ce83f01d2a6a0eb90b51831a0d40f1a83dc2a3afe190c3b1c57a3360e65c036930a840254f02f6cb1e23879c6fad50eb673d34eff652c06a657546f5d65d5556deee7f74667e6bc887da8fd4a83cba2444a8d2723c006e1f12e7ab124310fb599c72cc8f15cdcc11a1b0613f1c76ca430678decd6cc2034f7ec3ef37020bd2f3054455b5ed0e949ef849aef678f3be95c4d739c25c8c03c67b5fc58b055b0faa0ec990cc6f9e201e396de115dd4da6f420683c5ed7359ed48bc36440a2c7d975650db39538808c291ceb37417cbd6b1a0fac70103d51117ab1eaf235f78d5901b254211ca6bc6544173277cb8a384c1c1125d4789e370a6385f8141cc4cc0d52bbe36e513ca2ddf82d3e6116b44ff5d2616d7815e2eb09402943773d2600be9c256631a713bc9eba122154085f28e8a6fc9e3bb2cdfe5eaaf5fa25344cc5fccaa7e1e633f17eaf63989287502200658a457794b6561e6dfe04a6afbda6618c738c69c36720a1e3efbaba061a26c2fd185995a7fce1bd570cca93e115b553af9475668c9931559ac640d061e06e29b8a8550d8edc0e71ca5c6e7a2a8a35dafe8262202a623ccf8eb0d75260234419b4180f2300d86fea0e20b235e1896499ed880b84bef8ff97813c25c1308071d8595b0f6aeeb1b7962100fe600ccf9e8cd793d63b03027a66a4c6115a9bdbad39ae483996a672dc3ba7fc6eae8e6bc8ff58b0a0c2e7bf8444e35d947d05932d1654c20b68e7e386b4a9363cc4eb39baf734dc4ddcb03b540fa7d5bdee0d9d216ae2b788d442044b507ccda37b4d9f77cdea0fd7ec6475bcfde6ca6a46720aebe616be58d86b5a691d03eb0a249c9b465aad29bd67a262bd49cc17cf9a130fdbae126cd49a98b35b932d15feb8cb475425a6c853f2e69a7905b8a05dd780f024664d31b81c90f80f2f990b1f67e99cda405f855bf78d08cc63f6b802e68c34ce713ded97a353dc3dca00d6dd4b207de4633712f721e6276e68e68fa9e4f0916a0ea507af1625ad94d2b491e4a42da2190d6b1c1a5ac0c2a13ee665d931d51f74babd0b3cac65c0b1ed5a758ae45548d90c0b85dbf4461723b020238e172ac7102fbfb7275aea4a2e439b2a3caf9fdb18bd621ffd06e8dfe9ef6d93192d6d692f8023b000e55f929eeba05976ab206c00780170f247d29e8a6c3a08e16005a428e26fb5752231dc1048a7bfd04303eaf4edbdb47a2005c0e9b41acb041d240e6f16cfc7fb79cd0807097b6b237c599cd307f12e751e690770851643093dacb4c9db4c2053d5d400c308bf9ecd5c92f79342b437edef5124f67b94f5fbc974a427a0ef2d6042ae79b992643c0525650f6574ad97e968f62f8775230d0de97a6481427a5b4e6cd68faed2a1ae65727ebd98665ea6329f32c536f333ca277a83ca417021432f67d362f8baf3e6b029fa8fe27e6e22ee39733b842179ed4c0beb9513058923321772570ecb49f9b56294b9c924d8ccad3c1449abf932d60de8b5d6e5191b7e9fc40e55e16862d3cc93c86cd9ffa57ad71910648f72cd8ba7dc2007061fa2e346382f4f7db7a9a97ecb3c6cdbaf8b49765d1e545d124daba2fb6b829857f340d09a1eeb0fa79e74e7c4e8c4433858f118d664c2334a5da03cf0e3e17e180364300c8ea07cfe3fa712947f4bdc88a72eddd3ca7c97221b4ab9784e914dd19ac441acb6fd273547d4b205e72dc2e96b73cb1295b7f2682175f0675a19ef985252b8f62d100c71b3a92d0b9556ec57081da5f2a81be58897d3413a2cbe318e1b66fc7a134b785f60b1c24352936c1fdcb7dd60fbd23eed2ba015b24bbe9b7b83940945759fb566ee0346fde466691f2c5996ad77ef32e4953ccfcd9132a83b441e856c8056668be110cc34a52d03a977ec24738871d4e3536a1e18af1495cc88c2b6a7ecd5a73f1f20f366f5335b0a76c7648e25a2c253e199d942bfe7d35ab66d5289ecf054a36f5f1d6a808ccbb1f1994220949a4301a3294115459974204317030b485d7523366b3908f46af016ab8fecf0a7c0b2088318ef4bcdfc478b549e33b7044a9232f424f56001e7f54bd1ead6bf28fa7501da8e86c21069f81fa7b9bda01fdc20c6b63cdd7d636af406cf4314a2a9880b6ed8cef48419de52fc1ecb9be9e8086d94066185c592ea1d5b9063184bbd9749abd2fc7db8523e4d803deaa318dcb6e11b278ff7645a594e76a3c1a62b305c9a7d91edf4a38e62d0749c1f6f8e912d0666315c95c4007871148a9b8e8bfd635198441148fee499d4bea664b409df834579a95b0896b7fd38980479d9e1270e76e1515379aa2681feae1ce1460c45e485638de41ca4ec42df4f80a89ecf8856b24fbd62a074b258cca83d845278837973e304452820f047fa25814744ad86907e7b90b19bc6819a358956a2085c8bff5bc6b3f66d9e8aaf1e41f197c8d6f6b75ad6a0549af42b0890f14f44f52b88e04126238b7b5731d61c9884813825e5a32a0545708b80399054f6a1d71c0d85e19573af4cb8c04bb7d0482854a84051f13e634d24c3ea660121c8e74145df474f0b5bea9fd2ab74b8d97dc13e64c8de2fccd538dd3cf05d58c9cc8f828949591c40b34732f2cb530e5b6b9658244671136e81594af65e8dea2da6f2ae317cd60922f59515c67716a40b4bda2f9317b3284fb55dbb97c8552ead21992343ed2d608702e4d0edb2370f9b13cc6464bc085aa36a5d968853c61dc45b3ad66af7d29459d004565fde92e34385a92083566f9ca3ba159fc61c5c1bf160b6ccf002c9225bc987017c49672d15e89631ffa3641d8de1213e73df49dbc18ff95e792638e7654a937ad0d7a1c2787a2e34c9a4c55749255003271989689f5fefe2d471b2a96765d4ceaecfd81dd9da185df8bf11cb04442109b823e354f01c711e49f4a813fb99789eb2c64f47168909bed3be24ddb8fb3ad3239fc192be26954432a7d3ac8cc506611aa8a0f48a1f3a5c452cda9de64c5f3e1f3eadcffa5fafb873f1b341e531e0254e2fe0e19c0aa7480ac0e0881392e4270cfbfb362f558e4af7d9e3d42cce0d0c6c0b0e7d2a64898653d577bb0c3ab8c0640abba6af034c5e8aef22fbe2955cb142deef21f39687429302fe2e2ffe40f45efbf7ce3dbf47c6dd004049c22b6f861b04ab2160956529b3fb0bfff00662fb0b9e980b8dd4a5cbbdab97b5b4a473ae3a1c18afbaeb60ed5a7af73d0551154d917754d55c4636fad7b4151bec486f35431827d23eb8a1ec570a2bf0247feb68935a6442de00839ac2b76187dd454123b713a88e8efa28db0133f51c15378e034bf78209a3b795c3bdc820ef2a67a38cc4aa1a6e6ecee2635c2b7c95bc2782036320a827e78c3389605563dfb33f0be2869bf27b89bd72a9b4af2b52f3d727e407f3cad926f993e8589068fedaaad6eca771c39aaf04039e712ca2fc7684250aefd611afa67b7ce27db1d47504f7f8f141382a2cf5a0ee27ec865b41040410a347ebe61684164b49802aef890c5037da3c17354dc26ddd538e17330b07689af88b873067d9f1fc49a59033878be7f276ac20f579066fd262a44cdff66481c35c0d94d179c5fa9562f37cc2b5135d4f18c79932155ce9d409a2a5aca54fcda5e9d4a5cac1ed52167bbed4cfff980dd47c3b623c244404c4a8115c9e33c15afe5b8cb19c3fbd04778f5b432ae657f486be8ab3df5b59bb3e34324a452359ee66ffb612cdeef5054c3bae08d62f42256aa01eee941474f2d2ba6a65669e4c293f8022baefe8375e114671dea68b2c6c5339a2e0d1213538577e3f7abeb31ce7b13f5ae7336da46bcf89d3b733ba41955ad55ac68fcda227efb90f360dfe5c0e7e8d92e1b0839a10492db9848e06925953ce88092a9af8d8e7c7f56b7a9cb1224f79abaae58028c9b76216e98f7e4c830bc0d005f87fb001cc0844f189188076ec9eca351abd349c15ee5ae54eb7f5e2449984332a88089e467899a30ebd864acf914b9922fdb5191bf993999430460f4fc254a6930fca1263579727725745ca9028aab5c8c33692e7eb42f4404ea6ab83661d8204cf1d0ad68ae1de70e4a2d9cdd50fea953ce2babc0f61350fa3563170debad7e7607d6b72a1ef485a60408fbb50a9733f2f21e9c7d941bbe9eecb5c12a611e395698d5e05abfcf0ba8f63051f9ad21d9325dab33f91033cc21938031dadd49c5eec55a57149194ee3a04a226db9669087ce09cc0550d82369495b68c5970524e3c859894186f6db7157be2a3f8b3fa17aa861f64314afc92b64374ca4fd7e84dfa22b7c5378041f43a0dc0b9a7c3d939bb9405f10986334d33315f2de6f825e8e06256d9f5c48250386b2649b0d12ab8069d22fa8ed0dbc1d645a53173aa88fedfc3330b87634d7ba2d460960130ad411817254006d635d628dfb6bec88f49e384cbb6551b4437a693554cb71a1d6a308eb9c53fce7d316525e459a3745d3b49ed0000a8d11445ab9a7613c446f0a969b98d97576f8af1f13df910e06627d5b1725b37d3352124f052672a6fb03cfed3eb3fec529d81f64cec2d8c5c70800c6c97e2da8cb5e8bab2e047cc14544cabd0d971dadb52096a6a95e3a9d3d408327b152c81b102d5ae06f68414766078dbf6d2b903980f4c442d05950d251296d3dc2333cf600db218c2de9d8cae9519f748a414f77c7c02d19d15ee692154b20d885aa068f4ae4428657fc14dc957e55c29337768d8bb0f0f3f44dc2c76209aa6a6c0e7cf31c96bb99dbe8faa3470d3bf903f7e80a39eb7c119daae65ea116c42f8c0af59048b597db298a7b9f274737683b5e1ad751c7ae5c57cb2ce598a7ecb617feb640232abced902ace9699ab82960afd950b6f7e0c576f767a2d3c4900b8f4d2343df437c89028ae6b6476daf20138879bcfa4b5a872772da802359c71bf77d3ae10423d2273d787845b2603637147829a95149ceccc4028b4307c454c743f816be9f6aba3a8fe2a6fa5bfc02cd488b9c3ff481822036e3878acab5f92b5789fb9b6cf37d5b6c57a00ae40db11a59591b259c142b76f1ea60d0304e5a5925296a9903355b9d74e5168f7df38b0fda4dc4203ca97b315bd9e1dbf7285093537d8f31c60991efd775f128aef6acb18560f484106e8af0c733bd180416a40a99f942a35cc27163b3730680f22eeadb3e53a9950dfd4d42eea1c4e7fec0dec1d0d61c6bbb25606e1446f81bae583788b7bc8a4177133a808d5334ac4a4bc3c2a962d33f9f1eca61f1329e1025269472ba649d6eec5b539b9956128a04504622061fab93f99016f419df8917a919f617974de9d16986d4e42bb43f55dc0b639b544eddda318f13b1f37473b58c78bc3fb17de996d65edfc163d3bd7ba054070f0b15646960784df1233ae53fc16caf216215f429b4acd91939dbbc916f73546387b98148157105a918922f35199c02c24351c34b101d2590ddfcd317e9f77a415bab88491068cddba2634b0fc3e2bcc2ee9915ed8897db895ed2a570056e22cc857f477bd77e1cc03d89c720b1abc8d27ab8b33947f8c9bff00c3428e0765b1b23eac44d22490f38d48eed1ac69021b500cec21ad825c408175d2afb0e80e7e527d32b190e8653c8037910875419933489e3804f28d1f0a19717231ed4595084a87952df1e10e19fffc291abe91fd063d287531cbdcc6ce23182d37ad90a2a4ee5360d02b8b91e07f8c17008e3e0b732d1d2839d496732a76f2d1b6fcd2e03c38e38b287135748b867c3b261365320e6ccfdf171e96f581d63163c9b1670c341169340d1b5e9069a4ce3f9ba1410b6a43c943444e1d769b663e1b402ffecc9dbc908a3492d6bde70420fea1cd888a528b7c905c58e53478a9f98bab4bb5eca60293c097735fb26b92d0e33130f7424860dd0ce3f36a9d9f4658f244433aec9a63e33c4be8bf2fe130593607b8bb43417287923fcbbc4b6407f555294de2dff0b721807837b86190b7e1433981b50758b9e5efab661577f49efc83fb794e193810460b8c132ded83a7076b42e18fb3e533be30b8719b836e59d87a08d029fdae3064b9f529ec5d3de43823b0e3608cfc4f7786260045615b816c8ccac2c6819cc897e6781969d6053c50e8a5afcb21e81e384c68c31daac593660df0114ac21d2715ee5cb099d09c4312f5b3f2fa670653baae30cbb9d4ebe9f5edaebc52c0fe04833f5e9f0774d9a86b5e0f7a991e9fd6c3fd5dbf4ee4e26da06830e4a69ec16c9671549d383fc276d727be798a2aa16564dbc5a0947d042735e3dfe1ef8d525c71cc3999ee5f2652297b6fb0d85360552bd820826564085cafff05aadd4e485bacd8d5174855b7cd43bd5afed79c46b894a9027f5003e88165aa52cfe76f383e89347b53b3cb1ed76cd488a87cd6846d70f50ce74619e8089d5137e0680a81322ec087015126847d8833ec32fef086b5c33f21a8bb2df23833bd8285c8a8be5444e689d06d627ee4ef90b0c00b4d25fd10823298eb776ce1e8f23583b92c01c38fb9e8ef0c6a2824964a82e63d0a1b0432464162b31eba01f4738c85a5cda707f0ce226c4cc63524b1e7c9ae2089a8825a9bc6bf95a731255f0379f1bf166c052b3de3c90c1c19e2ec0c7d04693fe701e3a5e80faef8cec8f39ef72d92d8ac915003023089e4c11fd735f0f54454bec6233f301d6742912eebf59032dbaa48f3a9c6339115ee71ea4f68411d43207bbc717f9f69cfdea29ee7b52d56ec0714d557db34c9f9310477ee47514b2448c259f49d037285affa9ce0c600ca1bed642b6f091d5685a3b3f6f23216b4298273ea4e0bac3e232ecf766c08433722568533417666e08964050679540e5cd17d96581601aacdc13a7530b957c68d221b88c0599e0570b48df17cd655976b3b217b6d6d1a65ac404756c2125b9e33a49a7a3bb6f5d462ecd4d083b4ad5e537ea6aed5393cfcab47e81e3739da5832e8cad3f7c4e87d81410b7fe95b3c344a1c140540711ab44bfa973188f7f47301fe71596da35d188b2e104d7d3acd640a659398dd24f6495b29a00d80478fef31dea480ce6e258f58fbcb8bfbff99791610858b8bb2c9c772f708fc07a156a0d8ab6c28c5b9a09c7001fc01ea1193c187fda790b7cdc8eb926c1a907288b4c28a769cd2cbcbf770dbe0ea8870e94f2a2b9bec22b238a3d545cee4d789fed3d11fb1a09e401321da47cc1be425144fd0e52c201aa886593be3c3d99a5a06fd1f095a445d3957fd746cc9a2a064200aa55f9704f63bfabaf123f331283c4a08b3e63260b3c329f0cf121bf9a6c63d0f7301ee09acc28c9e38fbd1e8a9384c9b223d5a34d09b1ce633b10b4d9b8717f41e81f2d5ce1090b4b30b140366626eff9fedd461a52a3d04734b87574935c5c31b77c174e01af6a89c5f27d945b14af900f2351883c7db467b2561d70d000b8ef916e2c8491041820238745d77756061afb25462cf1e45f7bdfef50ceb9dd87a7d0a1686afdf98f06e1aa1eb60af2797c025cd6639cbe3fd0090c7e7a8e24d204f6ef568e262ee347ae9bfac045be0762c0dafae56157e32301bbc29ef83bc55471c1141ab36b2ad377ff4d4f0965ee550cdf80bcd67c5b284443a58576a8e4a5276a10aaf8823aa5a65cb005b3ec3c155d108eb3a88d4dfd45fd08b73e9ffa57c2da2bb0ede74f32080d3f5a3ff7baac013d6a6e78f01e107edceb6f370411f319c5a8a9a875439cd67f574bfa111686afab577488b89d24028cb29cb85259250c9fb486fd9f917a4c66ccc742c97211535473863081da01d8737c51b110285330885583ccba23abf29856109dcadb1661d35febc7b9e42301a3d800aae0287021ecd61d3abcd81c66b7da835f28ac18d313debb3b3836512d020e384688ef02f1cd878c30fbb6087111911c0685490ab4bbed9e1bea4aabd365402626124f42fe4396c3f11f3823c4d0c7efad01aca8a59662a35c96d3e2e142a6d448c2cee133560411efd328722f0d8afa7f86cc16c8060445f5e04b1ce0d307d7508aadc09359e7f203401fc43371bbbb146c0d8fdca6391424b99d16a7b196a50dc951f5998bdee0b52d652d1e33cfc84233a5ff8b00d0d71c2f2505f74ad712d6261c84c1974c9e3adeefe890b2cc2302dc071c9b64e42d8970e52027df6abfea1077c0142669f61e333da1dcc1b01b37f3bf6eac064d0751121070f2cf8ebc0e3a23a53e0dcf41cbb6454d4fd886c23f2a4231a0836c6304087fd111c333b209471726b113c0af61722f5aba3ccaae8f6de304627c8db6f507e3e140ac4d4e4e411f00b4bf761de1eeae49d733c4efd0d32a7571586cd2b84312978afe7bac17d5e137518c34ae43dd7dc8c5e4743353f9b0eb46018f316ef4f9d62d0cece2487aed7cb0bf37afcb8579ae10aebe26b1a2979191e27107ef8180d5a01cd18766d7dc2c54a3315af54e2453a760b4c884fd30ff429225faf0cee60b416d73463f382902eaa41344490f2aa8709411aa59f80adf9fe7aa954ea5ea16840ecb774de62ff25cdea1608a067b6f3fa3fa00367e5fb492f43d1e9a4a4d19e517ea31c6be5affed70efacc88a0c58ee9a998d8e6360a2892c1c2e44fa5347f1ba298f77ddc033867c5b34ec6c7deb2513d44c7362a05d4daf9c2ad222486bb8c8170e81f60073f224308d4680a3b0b34a0d4e8363fadbf0908773c83016c49c6c460fc026ed8a305e27154d1b63e490eb609fee5d7f83c0029206d4e4969c262589f0d1daa3f8b118f443bd67e3120c57ddab151c9720bf229ab68084a8f87f44915bee3e888cbc4c644e48df516cacabf9ff13ff78b17b0b58878f8480b2cc2cf47448b8403aca165de31a4dd8b2d8e2b7511ee966d91128db07a473fad904d04cd195808f5923be5814aea3d28f297eaabc4ec7ff90752085f16f8de2a9acee6ad6b8bc22434f5462c8587a00d0aa32229dc913ee8cc53be46663521f470eaf1d099b7cba702b1dc693ef4c2b9d797346303ee080956465a6459a63923bd1e42a7bc7b9f99bfe4c37acebd7d8979b108e170a99ef8c635d3421e7680062a285dedd9503c796ed60ba2c541371c2933a433aaeb99094530346c6f8a75a6894a0a15ae3169b630a1989c3fb36250b4f4a6ac3956832e834d7085b1c97cde9bd2e6f5031f76d28103b66dbf3491db875eb45f54a805d7510fec39edbe3583578fd8bccc2a5aaf0aee025b170875f44f500fa6813cc00c0bbb94b2b144d64741e052b68d8286fe046a0d775792917849d18901d9f6cd56b160f1625a607aefbc48a72ec02811da19cf9d033611a36540f2da2cf8364090368b76ff2d30ddbd440cde292038522775374529716501cc5e03aad0ba8f5a30181aa5033e72e52aa9613b6b14906a3e1430d0d7ea3653763155a1913c320899390e1b9de0f87683bf6d49a9eb69bc7b03ffdcd7d943cd89526121fbfd6c000493f451de8fb782cdac95d78d104236215bcabdf79652ca24532209780806090418805754ea45068c2a4626861d020cc07b54ea45068c2a4666e6fe70152e10203ee3bee03137000e736170d215808d0e0c0178c16b4a003004e0851f3aa9171930aa18999999cbc355b82eb8e0336e0f8fb9380e73739c747dac5ac8711a35ea8b0c18558ccc0c8dbdb602f8c8c1e9c1a3851718558ccc0ccd8c997be32a5c16ba2297efe0d37a167cc6cde1315787c35c9693ee0ebf61e34faa1a1cb5a406e770e0e0322c1d39bcc2dcb8b1498bc3ada1eeb8cc2c1d39ee0d0e948983691b23edccb434d4cea031e70abe028d0f156edcf0aa823626da196969a69d412d8d1ad6da701b2a74dce57a55ba289a19ac1456b8d455b8d46d7435ae11ec6ffc70e1a957522d01608666068d1a7e3bae74399fb7fb17ce53272338944f37a1bcca6c3ba68e3914ca79db41d538e7d3495e555ccc46c3cf73a40bdf244d76bb9acb5c569dba1aadce8096468d685dda3b6d472d006a623c06a663be89996ebdaab61ddb31a7c96c34fcbca675f564b9bb727b57ae5d1baf155a68e331db8e4dc7dc8a66a3e1e7575e7be5d3e1dbace6dd3ad9ab193433326f2fc6c1a86ccd75a5705d2a5c978deb5a5d97cd75ddb8aecbae70915230325e66581bd7616ee7a70b0037dd1ad76e0a5e03f52a5c203838aa06e7ee121c3a8e363750b7812ea9a1e36863af7b4dc15335ae0280cb74d76b0a97b90600ddbddc2baf4fb3a73f3bacdadc5801c74d0e1d2c1ab6c6a96a34622e8e5e513fdd9ad255991cda15dae288f646da1cd3eaa096b563c6676674d467783cf55ac3b4028e9b1c3a583b58505dc661ed8db539acd5612dcbda1dd6b2606d0bd65e3b6f896f288eceaa488fba96c435ab94a9e36460e4d31cd0ea68cb8a7687b42c4cdb02b53c7ac038cca92606c7538759bd6ccf5dcb551dd0b2daee889605695b989607b53d70508ee21b543781b36f2f86ba34aa6b04fb935716b43bdab2106d0bd2f2e8412d4ecec94f265a53b03b586881478f6971727c58b7990d3b2f5657db95bdb2bd32dd92a7ee4b6704fb929fe8977c3aca6b8e6d07d531577216361a7ebe84ba50c68558afa86b977453bda2cedd7a83bb7ca511ec4b1d66f26602f77138d0e6b4f511ad0bd2ea4cfb83da172e1b808d06939b501d75207da26ef279e57bcd81d6870bd1eafc7821001b0d30cc497212e96ab76fecbc30c776591ac15eeb302e38ee15cd718343c70adac32d06ec72482765d1d00c40a20448e99c7352d971624c25b3650f4a6707d98652ea93d279512ae7a4ce1e9c92ceebbae6452915d2ad8b5239af8b5217484c5e98cb38bb0f1ec1a633efa0524a29e34587c81e330312eb20dbcc79514cd239a713232da45bd349ec08c645a5e442e5f47975906f3019639a72ca2540e92345244b493995206794ab9b493020df480d8a6bca8b4e39e5947276cc37d3a5a4d74525dcb20497578c314a87984df160b1e4a5846b0b8763b592578c3146e9f09297a4704b4bc2217460898c12ca083be806d68141310809092750120844e316a00fc4a20bca3b132127658c502ef132c286c2081e0541246ef08fad90b32b02bf9d79b61e484b225496a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a39723464646464646464646464646464646464646464646464646464646464646464646464646cc373126e98e0109151e21a19720d70e63ec08a1066a76d189699115c918993483a901cd220d09090a27616429b1ab09a1216997412cd01319e3d32e4e7a5d43c8322ee60b8473c58a32ca0e070f79e56a0afd2ae660d80ac209b197d207105310e7437a4180ca359f0ca3961fa5152f3da49840694185e084035313a494505eac78e921c5044a0b2a041fbd4f38f8e86d6ac247ef194c3e725b7041946840fa826402bea26e3131e1b6e08228d180f405c90472d25061c7b042f507950898022d26d9a8b06358a1fa834ac4a43336f89871c9548065018fb2aea2d920e392a900cb825e323f5c5028118630d805fbe182428970613125f838a788435c90092dc19c22062c5385117d40b38451b720435c6f314079611e7632829c7c9411c43731e8a38c9761a432a2722dc12338387dced9c94e0a514a29a594524a29a5b493b36bf5112ad77ca9169318150f1a928f9157d13fd982aa5b1a826da2c7dcd20f8c53d2c010acf3e2d14b1e601d191ebdd401d64979f49210ac83f2e8250eb08ec9a39782609d93472f01c13a9c472f21619d92472ffdc03a9b47d75a31e78658f28175481ebdb401d6b11ebd748475aa472f69a0d44389072fb55847f388c5147f32a297801a0655a7721f585e8ccfb830fe32e3291a9741e328138d97609cbb46b887f1d335a27a181977528447f558e66d6f8874c6d58aba47f41819ff6944b0852f151de62ea1e96664749a0ff826be745a177c13539dd603be898eea342ef8269e3a6d077c134d9dc603be895ca76dc137d14b9d96856fa20ca9d3b2e09be83caadf3a4d0bbe89b6d3b0e09be8b5d3b0f04d74ed6a447c133dbb9a157c131dbbda157c13fdba9a0ef826d28fb99a166c135d75b52ed826d28eb52e98cb125d238a3970f220143b8d88238dd788624ea9a511955a1a91469dd2156d1869875ae2db4badd9a1a27fa5164cb564b4160395d1a645531110338bc6c3044223223941e3a154452382126554aa1273505d5248be4b559ee8db65a2fbcbab42c209dfa82ea52a28235510f40f651491f00de4785d14179411c74e65248340d39adfcdab9ec15326d049806df99637fef6018c045bd1b79f80d83e70cab215d1f8539698038324da34cc290b4cab08a635ddfd8369cd23b30551c0419f475abd9add4432b304104cfe83419205a645bb1921bee9d96af14d6348f8e622c2805a12e6084cab0b0efa07d37a2c02f50a3a4d2b06b6cc1c4ee1e4a9b50591b1c1290df4d56e18dda057ba6557e6bc49672e7e50c43a953d02b18ec6de52c0a3980409d6b9d86f502d01e3050c086444f102c6cb0a525a48a85a226349afcbf0af19195f61bce86040f0adf927238a6fc7b41730fa6505584a8b6fbfa4949fcbee6ba2a12a44598ab6b44ead53ebd46ab54e9bf47adaa00f6d198a3f6606e705078252142430482ba8fe9996f8e69734ac8cd78c2ae7c5b74be8311b7ac881e0db5b96a2803f49609056d041cda4851a4a0f75156804bb74ab91345027815e0512c12cb0086e4101a1805040282014100ae8db4f45a7a253d1a9e8545474123a099d844e42272178e16b34afc586c4cea016210655487cbbc9a79a974a5dfc1c4a1bfcc1996c989da9fbfa4bde515e73eeaed68ebb1f7c3afdd35cf3d9e55072ead52b855f9a5a3733596d00cdb7ee13a2f9e69a93b6feed6bff76913c5e5b4ff783af914cdde5248dfaa5512791b42d88e6395092671aed4e235dd817a5393f0cfcb66d5eb94e5b8add73014abb2aff5d2e3721f1eb06851a0afca16d5f0ffdc7d3f13ff81548cbcd8652a77594bffcea7ecd69f77594bf9ca77f736df3ea54f3129643b99c9457a7b7b45df9a5bb39dc6c98bff9ac57b74d0712b99b79eaa2dc5eebf1f2c328bde49bd67dd84b4ddb7ae37c2391485d7212d72412a9b4917aeb66daadcf98237df3e9f69e8ce03e1e8911b14f72eb1f7cf29867a82b89d8dfdcfadc82903c8752f7c1df3c73b9d9609fe42c59da7c4a52f76198c76d07abf37bbbf4fbc3c05727e08fd9f1c82ff9c7233f1ef9249f2e7332019743f5cc37ff88d8277576db9cb719aa67fe11b15f3b36e9cb40f5b80999f25b48fd4cc26ddba2fce6f049e368be39146a1cfecddb09f803fee6bded6c1d731de5e9c5b85ee8f35e5ac72653cfea7307aba46e46067e9207e127753325cf81e4d44b24a74e2af9e53fbfddcffea6dd8fe7352fdd4ffee69b66bb0f3ef9e9f5febc7689d8cfba7602eae8952c758c0aa6d3340e8f7c1e097be05acad8d95eb58c3e70ec9fac810d8015423138592f7fec81fb64addde5f25e18e63e5f7fb228984be9bc5507d2f0665e33bf5cdeec92de42bd92ed04d421fdf27ab3cca5acddf7f3d5e5ed2859cd5cbbf0e1a651cb1be6d9c51c72996399038917b828f5ec76945e658e61f4caa4e6d3e5e5d764e60c3bce39bb8f876621e7fce8e595ded9e530ddc6d7b6f1d3c6db780968cefa8f89e0d4bf4929f5e9c11cd3baac3be2c38d9739129487fdfc256fea3fd433eb7caceb93dde7c3b35b029da1f0d1db04dbdbbf52eabbbb1ee8aa68937acc3f9587ddd6111f6ebc7624280f1ef1a13dcc91c0e0b56717090c1ed6e364487b8f0264883611f351a56384ed10280341e51f1c5f01eaf2d1fb610a08eef36fbef20197b75fb519007eec689070d0a57fa9ef1ac6d87db3e32c7cf8379a8b2b3d64799ae72b3dcce021a9b25e5ed0b499e46fd55be8233d9d3a0cfbd86a9b18c4e3a7cfe8a0c621e1903e26f15628417f9290d4ea832053823053610a00dc420f1f1d6a699bd82dd611bd8873adfbfc33875ba2b45bb4c0a2220c72699b18dfe45f4de79cfc5bfdd0fcbb3f9d35ff00f0d39d75aa4faf5a75534dadd5a45552fc6af2e8dda4a5cde83e9275dd9245f0f041392045117ef0d565b69dda3127bb344e7fa99f341f392dd33ccb3453f7f96bb04b0c4d845d6058da85a40b8b1a476ad9723f225cf422235d46422d9d23bd28738a48721911841c18e1a7a34acedbce06e4b38f8b8a4cce8a392f1e310ceb300cd531db90fcd4b1344d730dc3660781ea6af5d33f968f929b2e748df5d1df08fd0fc8671d0ffdcb51db0ee4220d0b1660f1574af5b105bc629c0204c105bce2375df74e8710abb5d61a35c0f1f36f4b4e3e2f8f29fba3f3f0f102eba7cb8026930b64b0f385941753c7e0fdb80283a19677ea2ae6401a8915a0b0ae058ff828735a490799bacf016ff266c239e41f2cd611dd624e72ed56ec3693568205f52adae995d3a4632c7c74d36d92ef9064643a688345c02276be2041dd80306ccefaf1ccd75cfabc1f0f7dcd4b58d62dd8cad285c8a8ca96a1a2d610922a405ffd63a221afb22262d53a4bda0e3d7f5d3bc4bfbcf2ccbf5cbb1d563299ae6081e90499d203bee1cf4cefc3210198352f8f17da267a077510048240acd332c9118d233f2a3154b4c44fc7e477ab71ea47ef239dc3a36d264edd910209dbf0cf1d298698f5598dc3bf6d1f87d886834cb901dbf07ff3a7d3ed211004627d8cde42f5e83bf847d59958cec92396a457d16b6c31d0a7628328cf537e20f41f8bd538d5375804dfc48baa4b4e8e3a29e91ed1bb7610a8087844db44afb107ae5b9107eeeba0a2235a41ac239530e91cc8a5ce0b8d7a1584e458f7b11ed3ba387f3ad639279f9a5bafda855d209716a7f9078d1e1af52abae640fa74bb882439acb6893e6111d37b0c8e05e4861afd051a1a070be2e183003274f008edf24f875cc9e7854062446f31b8af5951e038dac08fd325d638a6ea9365ba2723b8ea999bbcdaeabed1f0f3a6daf108838b9d44db762676a56b9f8f25272e8d63fd93457c746cdbb13478e45f4e639b4100638c31c65f4631878718fb53078b2822494325d5bb8f67ceee23c27acc65d67dfd986b5eabcfcb5578c8c78b7f2c267ab9cc2b968faf27ff309433e7a4fc53cd10c216419fff4cddca5c2ff579a5633bf8fce5d9dd01fe855d7e19d65f2eb3cdf0c2022c58f05707b3402e9600832c471002079698e28537318c6ba152e33851d906c7591afb0aa1c46c4be9bd7a7965d52b685947c6ee2120f615c2e8f0258ca4ee224a8634df399ebd9dc07d9662dc389b63d6fac7e168fea5240dc9bf9a974e9d6403dd5e895ddab6232f7be1d7abcae89ddddc6688b12204267ecad4653b07fb1b998c3db79e5354ae25db3845a6f46c079fd65f9d8d0eeb63da67d3528cc6d94d8f3fb367875c703f189c5a2a574671daaac4b25a75e8aad28b437753caa1d79c9c077b95435f358e8c43c7d138341c3aab718aec00c7f819ce837d0d8737fcfb316538f4863f647008a1738dc3915033ac43b3028e0a0e9d008d63c36f70e837c41c1b1cba0bfce386c3fb5db7922ebf8ee7ee1204b8bd4b0ee0381ca0e2808357bb914a9ce984c20187cb1e020e20c44db7008eba3378e9d2e0a46b009fb746a5a10042bcd670b16843dd0034cc50002135aba741b2b6642d67adc9da93b5286b53d6be585be3dbbba4069fb9964ba9686eb49b58478d1935dc1abc965e644c9fbc636e9dace1b267badd85fb56dce5727c2dcdc9cd699af334276aced49c2f73ca98f3e671c87099756c167b4e31aff015b333d773156e90203ee30ec0632e011ce6ee38e9c670e37702438d451b08a4dee0004d869904689eb9d6294002d16609175dae808b1e55c0c52e2ae1ba0b449bd6a1ebe129403ce109cf278ae139894a38e83f5e3c0107bbd49094a0ffd88887986012c68c10f9f1bf9f97ddb495c62578e00452c800e88a4b3cc188c3e6104964a10507b004800956087999ff78ad0be87002ce723b3140783f152701495d5e22aabf9c24c50842f07a32f78155ae5081e764fa8f57b59bddee23a27ae9b4c26d0793ceba5a373344d3b2dbcd0cc9fcc7fbf180448dbacf0ef0bd4bbbba0bf4aa9d27096e4a971d06754007d2746688e6d56bd70395a02205483c4d1ae1840a02fd008b91975d6984131e6f41e263bdca6e069278da6526f24194214fbb3340f06a3fa106423009450461a89398e244cbd3ee0c27dd830efc30c4720212402db0a009a29921996bae753db3c996202feb7ebc9a611746273633d290cdab4efa0ffe92daf5f878401a32018229429e7667aef02a0661f06188c8cb6eddc2ab9099219ca4ef478716535a9965d8eae28996c74a58c203431efb0c26bc1948789986a580632d5b54918507a6a20a0f7461f94082632d8f821c28c109c6f0b2bb6308af4eab5d5f0571ace53f7fed72d126a280632dcf5afee332259e0f0a0a3ca678f2010f0e418824bcace38c052022a06a6734264984bc520b86803c521843439e766b0fbcda36bb7c8321292dafe7f281e7936a81a779767b30120ca1a569ee73bda00a263c8da360e209208fb1d0c08b20af8a2c4300f23e90440e843cedb29e7858940082249e766980f0aab51be041908785c7cec11f6d621dfeed8798a343059421dee2b107b1efe719193ed869376e7105172a435e76b71f74a88032e46597bd4ab2daede9221100793e9b12bcecf6f015cf8771e0653e0650508f4f05c368c8cbdc87adb0e2022feb9ccc40ff79d3911900ad9a64f1b22eeb813cc060c8ebf1d1a1c5949697b90f8f2552bcac8b39c0c2064924f1b26b8fbc8ab2daed812d2d429e4f8be171f7f85217852f5c49dfcf11826062c8cb6ebde255ae9039362c955ebeec3e23403537e8978d6d1a420821841072dde43ebb2a06cb6a1ae7e59452ca181b87a95849f29ffdee4e418da48172a485480d16a7179c0209469b609a5b4ad20b1ab5a84daf8ae3482507562a9c7309c63107ebb8b6918e750e7bd2b9ce919ebca20323bc7c59f3d2088effe3727828765c2a7b9eb2040aff711716d3950e7429310fa03c31020f47b4c04a142c2879c2500e3c40840e94683f70369eaf74c08aa7cf573a30851256b8b0f26c8507555878b6c203113c44d238995f0e85986d8dfe9ad0ff46e70d2302073338ef1129bb6fb65cc0cdc0a219a8e5fba97f1c46909e5e5d100914a257975f0e9d7059763fecb75cded338f22f872d78c4a743bf5707ee14c4358c974772975f4e38cc3fecafeb72052ca071a45fee00f803fee512681cf89713691ce897ebd0385c3f5be141112f3d3ad619cd2358eadf0b569ebd8fb13cfbf441808945055cdef48c4505ade7e67e42d103237e3a8d370e53a9f2819f5ed3393bbce9383a477a354871a509308cf0c00a80f0669115536400042b5c1891c09bddd704c7ffc91cd8b20d6da0c7245ccc33152a5dbe3e43b10325fe935c0c206c082184b06574f9b1e381df0ea180cfb24810fe1d32f09f910fa27a8e29d5c48e4ddcabd87535bd628790ca14430f39092112956b3ed87ab9721f2cd6e279b7fb70799d0018dd87cb8bfee3c16da71570830c408624d105749080039ccc08612d0f9db53cec6cafa4d7485271f1ce1c80bd1f8fca8ebb87d7b0d51db5e81863e49b76d8d04e80632a236812c685f2f9ca962e49949cf80870a467af2fc856208f50a50b7daa57dcc95e4d8c6fda6528c06dff59eb19eb70076a70e3c56a1ad6a042e72120261102290121dcf85abe698f37029ce5c1083e025ce52b4044ff6130f266035bd370cb15a0249fa2e921ba3c5319c111ffe9d0b8eb0ad096672a26f8e23fd5374de71c7172516f57d5740ee74b38afd9bd1c6e41b84bd336ed9773d709850047bb1921d42fff5458e69cfd68375a3fcdbaaf3ff3e8dafd7c5efb2ce0f39993dc5667d629f9d47620b976ddde1c489d11f8d44b4f9330799dd7b618b28ec2d67f9ac7195c78adcbfc82f2585b4869763ff859841bf38d74cd3aa91ba2ca6309e0f8ca10591ef352dcaa6757f37a85e87ce654f38ffa052496ee0c9695ea6b9fe69ad3aaddcd99757220a2f35947019dcf3ceb8a64ce83fd75518a39954eedfda6cbeab5de4ff32bebf8ab675a777599f36704fe0781341673b8568bb3a4c6c4a85cf3c9a28fcc17eb21f310248a36d1210f713cf4409a2846a7dde743761f6be52a0d1241a28f1e8b624efbf0d140acd54a28da4455033df7dd87fd0bdc8fd7310beb4087ad183aa7c709ec882011acc23a977f97b7be5bb007b63c9eeb673b6c7d5f3fbb1c76badb81c7f87623d777f7fdfcf002c79eec3eae0224e6c096179d50c5c72718808829d10044dc80a7f8e81facf21088e7221c7182953142d00422aa78cec59ce8ac9813dd47cc892e8bae2b2ae4986d448912258a95af505cd14473eceb970d9f8bef2d48f79857e8c5d7d6f2350ae1abdcf21f7dc9031445e08275308f1dfd19e61b919f39768b1e8a2ba27c3bad57474d6a9a363588c990be1f258f79bc0cc5153ef8ebd68f3aacb5cb817af51c48d5619016d2a44d062006a05ebb2f3addc1d8ca18583e63e666a6576e34fc3c410d40312011485f17ca8e3b180453276a8d1be08682e2111d4a37c04dcf5c7a6b9aa66914ca4b9f515e761f112bab3c47b809b93e63f6392bb3cbe696cedd7781e748e58990962fbe969899f9da76a8531bb097fdb3c3f552f6ddb2e5e5366f109e891dfff4befc58e344a17b42e13f29a5ec01c63df88f9939f2bd4564eb77e8e1618c6f8f4738e8fc4de975ca2dc8ecb018bad5ddfa7879e447e6c1c397dec5db925dba0d9c83e41660a1858b8c0578028bfc7e828857fcdd6153f6e512c6be441190c032d2864c7f32b8a1a07e18c3f698622ef5315c5d8bd1dddd31768cb1bbfb78e44561e390315a2927892f28add05891b2a2ecb4eec9fbf3952ea2f88192673d5f29e2f239707ca58b2749defec935ffe22bc7c0f1152fbafceaf98a17440f04245f9f1425ca57128651c77cdb48ce9d8322751fed3e48da6ca8af6d5b900837d7f835e72876dbd9e8953bdae5f692fce4cdc2b6eea3b506338bccb4d0e2944516335860512314b158b9b5b78d583887b03ba2c9039a40e0852b7e04e00a175cf08123000108400052881021422014042000010860e21ca6e218c00006304088a8ca109408d8084460dbe16d8894a08fc036448a12d8e174518c90e889e0e8a0478e0e5a68810556d709b0acd0b1c38a9b1b1c37a016c622db06ba63b1d290e621c4c1a2c00d229b15886cd850a1c6a8088b4515a8a90200295471afd3e82410098615b17410a241c58c1a54cccc70980ca66a22040546698e7507d51598982b2f2fa95303c19603ba632bcbd722365ae1be0e92122425480a932a2f07fbf6691b228549e33079d2380d7d8814a15ef1cb1ba7e060674df076935ea1b6ce0127e54937440a935ef1f3d7d21029418dd35282864811ea1c6e269dd34252944869d22bfe4f7292d29e43a43c91222445899420d236444a9094235a488a925ef10369bbcdd05f37f938444a50afba49e304350e3f165931c75da5e2b88e31c621062ca9cc10e208d2afe2a49cd3078fca7fd129312c36cc5ed861e768d8ee4c49ea96828b55b8cfc6218e672a0a8eb53a82e38e0af7798c824bb1431d70dcf160e180c371032e46c1d5d880e35e69a50cbb68694a929445e6a414c3b24cd3583b75c56caddcb83885db61ad616c2c65bbfb46ce6e24e6380e6adc4e7fe50cbb348d6a53464d6b482aed008a308e78f204097311145cfb10077b7277c71dd62bfe796c0738c6abe2131cfdcf9f53bdea6a7ad54560d111685cd52ba803ca1c7dc3ec3862645f61dea036578c31461f2cb0a165588aa379975292a053b170c4183f6b4dab1a966952332f988c8c9814a762669918991966a661d5a9060d93d7b0ccccec920606a5eeae481d848112550accac4266035b598e31c6688359d8bae192053e525c8c296ec8076be537ad23478c91a563db616716694608eba3d38f3af4cac7085250c1c6ca06b6c210329d50a917ee980b518cb1a36c39bdc6ec19cc16dfb0cf8e1d430d09e5086a77773733737bfcfe1a90c4a74b0d20921825d2a0327777b74bd92d39bee11ab014394687ddb3fb62d73b087ae87c3f2e6a672ec2a132e340ff66777777430dd438e3a4133690033e7f86b0578dcdc933a32841ae55b073660cfbeee85254e8842b358448b8540a25619c73ce39e79c334266292177df9c5e634aaaa4521a1a6337a57f734e0cc2863ae6e418539152ae578c33a59cd74ea7734e9f9d9c702a71f947bd2d335f2804865d981093af4983a0f4422450879441401dd22914623a14822ff6eb46e98452cae22476c1abfb28a558f761fedc4be6d8865d5e8ec9d9ab4e32a55d179fa851ca6e4e0947d944cd2886b14bd62ada7013424d34b1921b76456d62f4ba289c32b625cd6ddb615226a594918b31b294524e4c66180d4cc565d681348fe8449c414d4a09ce6a755535bb6d16f260e1586117e552a6694af5ea04c523aa697aa50407bd09dc01073dd52b76d88483b6ab914529a594d1ed84cc1c779a6d326b131c8c24be5693418a632aa5949226062f286161f14914432f28e194c4264f7844264cb16124c94e4a29a5942c9b95300e061c744a8249657c6927d5f28d4b70d0a3904a4a09046b6fea4f281377e24a24df64a4381b23032b849cb596c9a8321929eca2e156e3aab763575e8f9d0b38e830bec0a62083859ab270c33a1d7e467629a5e4287164ecfe218bc0e7e9e731c6782979fa999923c6239f27339491e5d2afeb7a7a5decf333bba60d500650077bbcf009d4217d4e8632803ae494377339b32c5ef162396d3b980e4fe5f4cbbf397dc60b8bee327a7401073d3ae1a07f92339da4ec20aaa652adbdbc64ed5266322e988eaa148de1b12f999959ca0fdb7658029f8a1d93a12c709287ea70bf1411959489334dc3d02109382dc3bec2492facb2dd48258e4d0d4b2569a4b3b6e1873a239f7e2d113dc637ecfdddf30dc3508bc4d2d77fbbff81d0abf079dbb98193542cd54fbfca49532f325fabdd2ce9f495b37de55753ea67667c8dafa89389e34aa46d3b35641910c217200b769433369c1de5a472f2880d2f9e240921f6ed34dc5f5fe935bf1d9b89e17a6eef5675d645723c77b00855e1bb2bc2336720a6a25f8467fe0cfcec7fbe25f4f84c20138e7b155b10bbc884abd33217b17fedb05bb57df6734e21fdad3dfd9ed2b90b8347afbc618c2ce070c49f8f33b8ecfa1ad55c5ab02c99152d2ac10d41c8c2b1aa5604dc4c11caf593e7e9778e89493e7747745004dca9c87c224c003d4f7faa8322e0b86dceaec7c7ebf23cfd35cfd70ed3354dd2943192480d39cb2c33f7e921845cf3cc973f4faf1ba20d3b843ce08a6fd87f7ee600ecd1e918a06e92add60c84b7bfe368f3ab97afb06ddf6ffefd2a2749f20d7b7661d0f674edb56d88d4b621b06f7c6df6edaff56ef02bc9f6fd4ab6ef077f4648161ef9ddcd086987ff753723e483dd67bf2d11fbdcab867d25ecee3eecf9e76b246255935e9f7c2c63ffd267e4a16c1b48804088c00359804199b8228a31a200946508472eb822055e8f4f1df29cb4fbc8304ae039813d8cf0d85985c67a159bd48e4c388630888e537a11166da237f650764744f5b29d025cf439c038458bc0074010c2f3c18c5002af7801172ebc1e9f79a50b124a7aa4e0024ce2c8104bf07c280f4478ed83e33584abca31a787ad84a18222e93d3d3ed17dba28074d78d27d1a093008a267c7105e8fcfb5014f764eda7dae0d78b17302dd868ab114c09427803ced967c60711123280843b46080032a47786104196c814850b10328618811c5d015af5e5ba638c982d9ecb2154640d1ca584f3c1f54965e428b1fb278bc032954dc8ee2030f803cede6f8c2abf504475250e469b7460aaf5a9b5da8a4075b84362e4bb668916c76d90645404290a7dd992ebc5ab2d9ed265cbe08f2b45bb303af725570c14190c966b7472546932c5ee0f9c02ca86030c0f07c805ed6f53014da1649e0d2f2b45ba2e2555415217c3194b2d965296a3025c8d32e13618a575f64b0c42002086249120223e098228b4210c4f4a18b2164a1500441d0e20438a65c5486a0040cac4a12a02421a3c204133d68465088a855b0a0e0488d175e155766108597f94f8d179ee63f1ef6fdbcec6184077b788a2bbe10f27c260aa0c03dec61042b732d0a195c1103cf471e1de165533891822b3c9f5902202fe39eb8a8c42c1785450c19a219003000000173154020281410894583e160928579a40f14800d7992485e52349347034990a3380ee218460820c01060802188d0d08c5401964c002d4c17d2bb544f46cfd03da53d32f584ec598a1eebde56f8ed270749132f6648a8d29035bd16c66bec1147e0c93273e147107524f6d3d0b0c9b15c6881a4e304db1c302b24f009461ea8c1c9f493b0e3239a34fefbda471c7ce1e1815323a75b582867485f2491f4edebfec75a5857579b4ed0890cbdeb78d1736ab9b119a7df402ad7ebde8ff00bf68850ac95620d33ff05b41c9a59a16c158b3631448dbe25540419e63a3f13562885e5ff9420846dd191a43ce6a762bbe984ce0ae51f849fcebceb20db59c8424a89bd8258bb89a56f76b888327dee8722cfadced625e9407c2801a5ea01f654d44daf3f96e18b93a0aa6595d4485705d32349433b30e896171863142baeb4b70badf10f5522602c005c4ffd920c24d0e3684bf10747ee9e520cd6bd432a156b88599c0cf9f5079ccc33f26d027ec4004a421e7b4088db5fb44e5add768cf3fba0487a297ca1a2ba16b66c97ee82163a651ffe0d88323d79fee10a5456a184b4c0b8610bf862c84c35d5712002b743cf08e57da4a0deda209a23ccc625b1cf7575bcf06cad98bb3f83c27f2e5f0136f4a63ff21b10e57af346478a04f114e78b9c2218d12226a5d6fa0badb1b14bec5c4f611cb32e6c992aa4e5f4fcd13ab9dc9db4bbe51ae7f74195f5d093871702324b5d3e240613bb488eda377d18a970e18c401f5d2f070b10e45e96db99f1fe7da00dd24b85146071f7439aa02ec9a53dbc4c7fd9602c8634e6039403e4df7c3895722146a3882c435e99944796cd368e647e28d0eb0bff4a96fdbd5e681d7e745ca47bb3dce68cfb77a0caf4e4f5c33e0147bae5ab2660b2ccaabff9790265506504a217cbe31a270aa54f062c0e704785dfb5b78047450cdb62b6cb1c8267a7e1ac63c79208db38b52947a7b0453d931d3b4412fb5f10d1c735a72ffd4dc685fc363da953a447eed332c2c25a7094d1f2603a54eba6454f0270eb32df83d5adf239a8b8417a405d536da93e1f1b74b3c8c23f1f3e88ff938a64f563728987e27e8a9eeb40821854aaaef73007577e99818555fab8810f96e18539b45440ae5e83f9b848d1383c80fd873fd6ea9a3a1b5a6bf7ca02a9650bb8441afbb63ee912f83afadc3b73378d5e1042d046fd9904d707f1e2086c3a382c3d7c254074513f7c851b585c2c6e8cc415cc906c737bfcebe6d99e778e436bfa966df6660378225087b452bac477c0a2795d94654962113016d032e9553fd54a99e915a3ebd3cffcd14435b212c668194565ee579f92248583d018731dfd0f4654543e8dfeb4676afea6830087c4218e657323037401d604689d347e6ba742e226a961a2977cfb2829b155e48e26d36c2797bf9923711479b7854f0e04fc9a0c28898e4ece4b837fd42ee98c23346dc21062e5f3d20b785f0140a85afac2222fc801d4a7cdebae5031c2c8e8c42c2be342699e68c39e287cc1516d475c361a0061368010fc957a2a92fb43a7f562463fbeff7ed104aee38b1a8dc21e3f224b52b162eb46c0bc309632a01b30c76b58b0192cbac8e661d8307b2245e6ba6c362b81369ae670c9400c1db60a4982e6f999e5157305b217299b9b31c98ecc2bffa0529fc9f3e9527bdfe5ad71f244180bfd184cb36adee11e55c174cd8ee8bbed3631585288e0647996f6a8d3f36e5e72facd144e0ca6258e0d63eea40667ad316afaf0e772211bbe9eea361e7bbb3dd726c8be3a9326efb7d2ba8db6eae433703c2c30418534633b9a9c0eb185048dcab8b6c24a781683140180ec2bd7721d6974b2e798a2c48921b593843cd7c52fbbd0516eebabc27c98fd13666cbfaeada7b3dc4ead0fbf52d1a79bb4ed2c0fe9a71326210817369b5192dcfb9480d9b382df7a687ebff1b2cb84e56a88c7e859d47131a09f96ee043ce2061135078ccf93a55e2488b1c2b5fbd380e52ce73e9760069373d5d8c3d2ae43b6405a27d5bdb83a948cbfa43bb084a16f74b14375c54e63afc1e7f1a8914e8438f09c27ff0c9fdb9c74559904474ce32e94755e9365b98009511e9bc5692b5254965c1d17a4575988d38f05d10ae0d5fc345202d6bd6b894eb6c1e113034e74e9e0e886408c6a1443046d982b971a2812c0de06a6b62f332d3b89d1f315a96a59b0892f208015b7985e86c70f7764a538d18780f797ff4103742ab7b29c5bce93e219fdcbf7f7cfe4b9823b68c672fca403bafa4f220baaf52f09d759687fa7584cbd2bcd4a046919f4ccb2a8fa2f8f46f5162eb3283a296920f1bf5ca6d42f87c861dd743d23449b94e2ef2214d938e9196eff1191aed0f2f44c50fd116b22b60e00606237afe3386d00a6d20b948752608cdb5df80a29b1dc992165e3581552ea87f2965dd5c49f2fef03a1b2ba179d6c869ea51e0d92221c7f560a755c92b1e18ece3bab7d747c882b69a90a5a1a4d81d1fa8eb0174f03abbabd2a60fe342871c242152e4e3a1d399440f15b3d86a2361f4c58c122ee3924f1adeb01734e64ae6248e74d3c2871d82df39eac707020b99213616b38c990fc7ba0bff7af97edbabf4ca5582e378b0844fe2788fd3b6691e924b3fe3cd4af4600d4a4debd4d604e34136fabedfc122d3c2333de2585379699b5d40bd3aec08830ab46a07c34044f2e01012589524115651d04323073cd5fa06cb9428a51906c606a8c8d720d8f623e00d7c09498da21fb3795534b14139f6b832ff249608f681a096bf08d6775177a04172f7d13dfe9cc4fa799a91a12209497daabd63c2b1a627bf1050ceec0a4d78722ffd3894b03c20f140b50670766f944ae50a7dc90f6078db8375180fd119d0f4e0c6598a5e87d8591ede265592ab5ed233bfd8815028b60f673d0164a717646876a1d412e6819bda2d5b2e897f6e1b748068ddba65075d1faddd1fc2ef3590fb9299fc36efaf7ed03610647fef0c5be0532c8dc7988d53db5bba3f3b54cc72a70abfe0876f37386aa518d109ecb51a7a725ac3b36daeea8f925011a38b7ece60af38c41d177f35568a914590d6887e7e6d1d2181c518065f43ae44cac314405fedc771201c3cd2aa1f75ce8ff01000673ee7c0daa65915b62471e1e8700db4abd51120cae99f03ac4450a87f999b6ff61f68feddc6694b11adc80beec14e5e78e610986020e0d28b0bd6f8466d23bd3d93d7d01fd1a042e11fa82428784f7277515c12fbffa22ff773df968f6f13646dbaef3a7513fdbcf245048a5ec1af3c1f51fbf5b4d5a7d40b1e7b060d3c046723e9f67eb7be4686030d9c0eeb5addf65d626ad26044a25b738fbaa90913248ca308b7605918ea159a35bd1b6a451744f51da7c0cdb1cb754677b00026beba99ca72d1e2a9036317c4f321b48f061a997c8e9b413309c7430487cd8fd7858c6115596bb1e34e1bdad7dbf7364f5e5e7e4d58b67e014404abf7ebe211a5f1890fe0f5dc08295e1e4018e974a92b30c11ec0076db9d26a240c74806682cfc8aab35657ee4971c3b2c87fda34839792a1a3eb55656c781afee68434376fcc38dafea3af2d3ddbe1b455f04b77dc609b7a9ec4c67989306f47557fcf5410e02c2be31e26529fa9092ae3fa88718b19121cd695f9dafb3d7146179379439c6bdb659d8791c6020522810f12c5416384ac658f561a06e8d6f998662f17f4b28c9a9c8344ce0b2444e263abf41589ef1609258289cdb27cc1a743a85510e8be01b90643c9e9c68b71f0a02b694145784a05e89457b1cb4b1a60177db6d136dded40a5274bd9897bedfbe22dc736b16a586df010b2663ca5ef84199b5c99d5e732b753a3e5faadd8abfc1798a496e6694e9404f7a571d83f6a5e6245e6e75784ae9e2253aa3ea6f008d669aa9a1618443fe13cc9a7fc663f652a03817978b52064ce46316711002c88cfd0202006b10f3f6b4f71921a1edfeca1aeab81bc98c8aeeb9cf67bcada1e310f4616fa2df4add63e8d85a8268fe68049ed585ac5b6af39a3204e3f4a108e8ece61eeb5b5b2d98df46a34e823bb97b39f601a850362b61cb602fc80a9ceb2ab919665316de2550ef67eabdd4b374f043c2fa6a4ae16a3891f7ba1545070ba276c20282a75cb7b048cde52c30466d84a847f013b778cb8f11d59eafceb25e1d8dcf9cdf5be5b7eb29ec38836b3b7ad45c6940d408347fd02e44f2f37ca806596519d774fde23d73081855d344192dc0d4b69705ed3c33869720b0d9172d1eead465413019d8ab9e3490139124bbf91e2922fdac82923244d6fe470c3057c1f96b8bc847c0fee1f67a11d875e16c50a635602c721416d184b0310defa5292d2417d3f035e886d2a5d9c748dc862a0afda5e46e12ef0aed29ab12c2abc52b1fa537df578b86b52aaf999285ae6b1117d0919738ebec81cfdbdb3461f4c47688fe921d5a4627a5b760636003b17274b531fa42cb71aeb1ea5630c42b8932b8510974dc28eac633512c21bfab5314e30f1b70d1e8cbbbe079302add1eed65a4b2030e62bf0dec24ed191bdafc68778dbe80267e002ec1a42546ad4e8806674e312e4f9fd4d170d1975f5615db12c76d47cef6c7639741b7f476765ba84494070504bd3b437be21bf516cffa52a2addb2a24cae1b7fadf70f3d74acb4d95478e671bba57593583927003ca6013175a1e30a25f128296666fb7215906298986ea60a794d904ad964834be3b7503eb21bea27e3df4ed1ab29d120e1ba490a51b32fc83edd4d55d543b303a93cd3077748b0ad0ff7ce0f923ba64103dfbe621a5189cf0a24189519f622c62bfc859af545b002e488dc6f75e4512c635a3a9f736ff15e889468840d1212c2417583769ffb856962d73163802baa681fe441d7ed205a205e1c962277a886c2aac7ee489637a6e24b77b28ac30186d42e3d5795564cb194e14300fbba7f889874ecda9531a7bad2c4e49a350250f73ba58c965b7ecc65a0078bae20056343ab3166b4519e5e0cda6ffeaeb5e25782c2baea4dedd44a1db6f4d65910d39b250d1f3e594ba040da47c9270bf1b6b0d891b411cceb48b2a4640bd5e341b83e3877ffac1875dc2f3bc6333e0d56e64f320ade7591bc2d45e4335f128a1a8040bd674f41e7e62523a2720bc24d615e3e8485d496287885f8a2004c792cd12a19915bc55a2f62d5927c51144520ed83d105c9fb2f5196a6dd50bf7f44646dad2d0bf7d8fdedee0491e6546a6e4edb41da5aea049898fc27d649a099638ded242d7e8c5c639e706cbee3428c6127176fa2a99fb8c1644afbba8169502252c40940eb29a433f8b1c23f3652b45e9e622edf0368d1fa6ae9da44550c6a2eb8e4269300a5dd92762588a54a4e6ebc620b1d535cbf304a61a2608b33fa767a5e4ee3cb064594d6b3f2d44614403a140b61f22c05c60222fb0cd1e33bac13749767a5be6d5e381985400d2b6fd1eae2727b827c669929edfba0179c9a02c87c8adf80a3c3ed6d594f03109b329a4ac8b210904bd75ec077a8d2cbcc61931490a57f33d9616d3a65f41157596858c15cd58f40ff607d142b2a84c8441ada57049b62b289459c56203292ceda2741f0810f308d5f350e0c12ae52f70e9202d5c12643b1e92b14d21366fdba8767bea171f5bbec296f060a2faa6113628abe1ee07bf5666dc7df46d2eb4244e48d0faeb810577dff6e67a5736ffe1635aeb2225821e2c162fa97f6ec27a3556337e8461b55ce431b33cb143fe1a2dc6201fe1b723ec3d22e78cf5fa244a3b4e6eb3d1a3c5c17e9e73f7d05466e3d4edfa68ed48e9f66275fbdfab429afd3d15c58e4a7d3bc9d82ef305e9b227132dc94a639b71ae8c6c32c8b57897a0c706e2a50bc6242d1d9d8191c0b0b9288ef92e8c83c3841c76da99e14951c1baf4a9523a197de202d20646977651a710c7b902deb19d49fa1134fe157ca1ac4b3b04cc5202a6879c091ef987f5919df7e480af466d04866647e66c8ae99ac765f775c88f2816407ff4c8387f4600016f43ea3f1be53bc2b349977460c862fa0b6d0550f077364a60cb9f3c810f76f163362ecd0ec6887f09f1d14faa86065383760ad95f8099860431592ad4d34913bf4ea27e02584cb9b57972cc82aecd45aa4ca2bc1f40cc4b62263dc888d9299a985fb6fe42a9b45d582a48d2ca48d13e4396f0efec37968245b90348f1cc4c80b32a574d66bf21be6a8b3ef829f98198f1a6465415886fa05fb56188305b199757d7bed7f009707346b6c35053cac9bfcc677bf9968aa06fc94f64877444532e0efd443c2357c610de2990490b3438c556251cf83e2d651eb368fd61b63827ccb67d05f3f4894ccbbe1665ac5fec0e179476809583cc43aadef18a7bbb630e7c23a985222e77600ad54af83790aca42750f80c1cff067502db0496875796800fb4203ef3359a03948dd931e42550846b05b1a7b8a6c9e1aadebde5b45bb5c92a06187f692d739a065f61bd71793946ad32bd7174c98202ea7876404e369d7fac85d881c62c2375cdc2c637f484ac4ea8b341137e05ed01e8880c339354fdc82f7e87e2fcb80587dee3e79854460f1568674d6cee225ff598b76d93d6872ad7c65a6f955b44f24a2475cdc495b85916fcb1c9a0e5d683af49beab7f5c278bb5459750c1a0857e83157272fb16282f65c1f45547f811e1beb61d0c5e86eacf29b2de59b3eb44eb70e9790f65f29c2a1be890cfa026cf595b9e73449f9219ea680f92c2f3c5b42209df68741c27b0a67266ee9220240037ae45d3436b5324f4c414295fab598ac5ba35ef98c8fcfea3e40bf591ef5679556b330a3e500178a0f1559dcca9dca8a023bf251681b42a5306b676996bad07bd7a840f2979e6de5efa5cab0ad8ebabf47a35c69c6f931ea83430e23426b9b81efee074b443aaca3a279ff6ce0f8ea40e6d91053487f7501448a18ff287021f3877e5c220b8b13df0e1d3e56928648f22184af163c4d111aeb7897d429fe921a1806bd930c35645a262a2bc773d94c6dfa45464dd9aaf2f1ab686f2c4b5c5afb1fa25aa85dcc8659d327a140d2ca1babb3d744e45c3cb463cceaf48965c7771552fa42eed09905ec854a933c96c4e464406018cff2291f3e6d68246f9330b166dd8bdbc0c366b9efcd698dcb73b5fcef754c115443cb223d06291a0fa8d44205d234050325c31cd94095c018392541d0cbac3c8b83e91d7b964a0f09b59485f260bec1b85c6384a923af4b8d2749c404d9e35416b7df8cd5883e91de5cb8442e734b58d5a177ed8dbf71cff179b3bb109f6a17ebe0734c98d144db15ff4a5407260ca26afe95d7a63c13be099b2630ead8a928745b425b71894f25407eee51dbbbeff2b6da80f643515f72c6264548fe8c528bc4c0deacc5a2bffcd51a1ed00c4b6d2d44651fd6bdac8cf77ed747cc3c82f36331ef733cb09f87ad22c15691b0a063d7a4cc86f4351191263ec16c5fbe122baaed47940efb45a7674ddc00c8f9100c37822158daab77af24edac03c7b44947b3516c4d4750455834b16568b1c096893fe68e24cbd249851925887481044cbd4c418cfa2497721880236defb8791ce759102bdaa3eace4002948ae842f8cc4efb06e6023a2c1540e1e94d49240d34bf2817642209472f848a2720525a30dfdb745e8933279ada141e22176ac7230da9bde360312baca62ab15d92ecb07fce14eb7cc243a139253e6415e86c2d5fd4a7fc22d5a6f41ce12250f6ffa43509b5d3a04771f16244f856eadf26d3464f64a7b3587212050b99511c7419d2c873d04b4a9feda1e4264d2b8350a4ead215bf91fcf8f0b85b07d0e1a34958b38e242f9e381fe999e584520f6b3522fbc273778fc3ddf994c4be8b845c9a5fd39905323f06edb7b307ad0171c22974ac79c3eab1eab227fe30bd00d3105656fcdda865f150142f90540907d65813cf08612bff9a1e93aaf8984a275d648afdd9254f2f2bc9e7d7d4a9402019118f28c32191ba9f21b44ce3b3c12729d069adf88eb4886123a71f3aa478e911c02172e8d30a2fa601c2ed02cfc19c911a58a2dd78393fc71253663d93d0c1a1c995c7882ace05735407baa5a8f4a12f547586c826b95eb274988462a791701f5b020bc0c0d2248d034f7c6ddd823eec4a026895295c1024178f2987b9dcd1cbfe91cd8924fee462c3349a0a772b805356dce30b02de9cce804833ce603a720f59b823195200142600274115b0549e05f74443f2b125422471669b42b6eb064a4d6c4f167a32623789fc3e5951680197475eb43be6788c075f6b1ca4ab43ce62b7414f247839ca093baa889b00c4614682ff24db5052f3d5ee6f194f9467edc32966a5351f0e10ffaab230af2ccf047d17ab34b6dfb81e3f3acb224ae7ceb3b63df4bd586a0043fb20d9c11b9d6ca084acb877a65784aae71357022695c65302be5da4bfbb81b21d81596ed79a57be74b5e4831490a20b6da2cf38dc0e2d13832f59f5a49ccfc1fac643877644029976deca69e6dd27b63591373d25f159c47ab27ce1958c8274dc696ced1f68953a9a9b0aeae217c70bd1731f2ceb41e2d825b858618d90e2e89bfaf4330c578fef003544c87f9ef8dc009b245a2e4030110dadf5c079f7e992def0402556061a58c128d96e042d0e87c993da560103645d26daa75e1a99cc6769f47c0d3a7fe48e2181d9c70aa9086adc8e7b8b67a1c48aa31a80db1f064838d93217691156b82297f12dd743bf378d99b4c8b8fa4f83d41f94c2119375f8f7f456b4c1abbc9ec0db9d889f63e3d9ac708e2a514e132c5e482a275614516d7a290413fedc74ba8e14ee36b67ebad38b18ca940216c8684a593ddd229f5c8d39b041a4296fe40993f9052865a66bfd3c6e26f54e97864985fcb3ffe0fbfc9a5a3a3513f4051f705d512c5e95ae551fd3cd4d283049529a2d5454e835c053ed4b5e226a3de655c20e2ea90c2f446133ce3e9ba19b3b9774da9b484170fda7397fc791a8d9e799afd09094698bbe9c090c0e516dc2c1010f00d8befd155ecbd774417ae1979fb9b1c228c39c531674a10c8a727bfa33558e135ae3f7c730749a620b5bfec5b9682f59b4cc4f5627f4b17c7424daeedde98f688260508af49d19bb97e8dafd46333f758623c5bbff4be121a7bbf0c50bd7266730a59c458878789e8380edc00e357e79bed31418c2004f0cdc5fadb41d745a86e89f6a680e026e090f94588693bc8af2783f2179ae44ecde3a3348222875c1b0162659cb484ae6745b9f0c9983a5bc1f03bada53e9d6eb5cb3e8210887eb33a846a1ecdc1cda3104b7b937b23fcd9697e33fc7f4f44c32709aeda400ee3dcc1a0b7c1ab393e62839b3fcc32d91d6c039e73c6d38dcf10991b4a55193f1fa69bf5cc1c255cb166408710f04ffcf228011453c12a9730142be085f01fed751bf7ba9959078b55e1904b4eb5a82302d4853ea416ed13729a6aafc81002b07eb8ff2ab224087641b7435ad16998b4868132a4e527282da505214b7f18f5f94ffc44d92c54e55681ace276364c47e07bb7896106953f4859c8f3f7a06afc7a77611addd280452aacc8ddb6cae647c8965f017edde527545562f5a2a14a8906960b5123798c466d973dde8e9ccef313ea0e29e1f8d90ed90017b3f41635026664739f7aa111e85f71f57b0893fd5b53ff06e0b02940c2ea24468130ec96536ba00124bcb03f793c81142618d8714a0d45381d48351806eee66fa7f5439906e4a22b4b949342ce33c021ea0bf4f50e24320c0f332665aed552a5d231cf8dfe58d3cd2f0b8bfdcb1d6e68239fc7a38c1e18e69b64942454cee124f79721e0f239dce4b6e974bc0f18f312fb0fe1cb6231b438e93459f5d37afdc63ef7d799283a8a8d0afe090432fc4f56e1ff77e93a483b83bb9908a2a02e800e7ca2ae92bed28fe041987cb5b628ff8016c38321b09559bcd11bcd8c3cfb01c419d8e2a4ff012fed5e3346fec627255737105fcebc31be40cdc0c6d87a5c2e119da95a414d19e55d087c46d38950f1071f91bba5ec2f301ab603f699b3f7e5994a62b55feccf20e47b3de9830dfdc5963a398c835cbdacd29ff9056f1cbea8afd908087dc83861b173e40232ae54f4e91b8e917139ae6f238f869ff47d510cd288879035ad8b6d071f0a0084083ce6c277e61b7ec043a2be4d02c9d64a69af64e362c57e9cf99735fdef82ab13f73993eb51ee274f4aeaa71add604019fa5854086a0b8559b0f7f571c0bd4bfd7d17e83215655f064f992d297ba4f33b5180fdc4ea0a4f3f22f7f889c60acaf5aeb5a59307c4004f9c3b1cc210bf619377b0838aa32aff619dceb16834919c01ed4f12ba10eaa11ef5ccd4c4fcf4dd5b5ed2f14ee4f48821471535bf0b100750aac2f6fa1a923cbb1d2072ba3874c2ca6dc2b9ac13a8d3c8354c41ffdd2eb658584c3bddb39b391aa31d25e2dde24172326d86ab2500fff8701bce0eb719b18f55100eacdde3dabd6be6a29e9e881aa1021c268a7dc7179a8066f84db34d4fb3a68c1f1915871d7cce3fe2be9e5529111fb0bc411c45e309f9c985ad807b4ad9de7bfd86b0660be7561ed8e92fa6f7fa136945430573481f792ec0c497703eae671815ca523a72f0ed6c0202dfbeb8db4b1cb24410389208a879eb0b097404b93938ecd8e2eb045d4963340bdf2192a902f4b77ebad98476b9bf9da5788827b4c2873f9bbcc1e36292fbb2c32ed9ad01f8eef32f1b9ddae623d0ab61e0d296cde5f01261ee7804cac0a35c179d7a0a323e4226834a1b0887376c77f0efd78e32b6a9084f34c7e527df1d62f432158c248ec6e6252005bff2c3b824ae9ec24a78f42eddab878b4651c50ca6eed5ceb9ea3e622e2c224f16c3dac79ec7567a60f63559c4291402f9dd54eaed68cd6d5112a2c0d717d6c11d0f4969d85645a2d2d9b4ee219bec7184124a394ab5d3615a80a278c7d2b84d198e8ec1a991fc9a90a9bf73970f8c9aa4d33a9d7cd04bb1216785a22c75cd592ab6c851ecae743fb24774b73a3b69ab84a3b91e42674171961f7721cc1b7b231259f1ac1664eb45ca1901fac0c505c0e12ce1049c3f0d8a0a8616384366b2ff2aa84432398440fff17a6013379386eb9395c4ecb75cee9b2e27620d1827bf2f573cf46ab98884417e65ad382fb659dc03dfed6f7962c597c8ddadb0e23b246566473b619cf6968b70ee82373cccbd7298333b8e109ddda9457f23ce46d5a6c5a6130d042e13de467326e75b5b81bdd5426c0ba73c43f73ca38a6dd5c3e6b528d5ee01fccda08b0a6e75e86fafc20f20d9c0f01efea767e15af7ba1f38e0eb84f087b1a886d58170d6221927bc00315ef95324ec574e754d626df44674d3304f4411823ba4af9c1bdec24577e6c6a7c603a7ee24e368d7550d26d38bfb90cc5230e056d3a21718aad7283b0015f16733e813526c55370f3603f633664d9254056c199992c83ab1d7f29c620c7a64e632c4ab9846c9b83f990800f66f1ba04e13165b7b944aa8877182f9ecf934536da5ca61bd818a8397cea7758811d6dd4ab274c2c2210a3b08d64a32873c082ecda3ae82c6ba147321a2e8056b21eccfc7511bcca8e4ef1b8603a5b0a1653f4c47c4a0ee999aa537d417195e414e0a4018aca321c141efc7cf4c3f85985338b48de3a759efa52c1a7fe38ed850ca3b791e39a8af32c9ea2e5111f267894186620f2023e63ff08468b46aa17c713fe07f5ef367ad744187f702c01142ff59c3209186807ce3c7e21abc489b0f8f3ed335089f7c2f0f72a9974aa93b591326507aad61c0c61969713cef0a5082200059365dbc9f020cf6327ff0240cb31c14f530dddc82d65abc5df875505f0eef8dd17b024d9297e1ebf3b9310a8b23c86344a11241dfec1da8076372c2442f3714cf198a427eb11352704415cad99f02f352c060a26b08b44b99fffe8def18385f3cedf3299545182f7f42d11e4ade339e76ad8350e61b6874de35ce81f2654f5e671a64561d4a9e7c296ec9d438531b99f4838d4656ec4393d49f0a3b31a24513882c7ad9def71b03dc438b1cb0ad5f9aa3123591212c55500cc85e78a0003c95d573e001c9662b9b8d18681575e3c736405f3eb788938a19c4c6cef569560a26be1fa2e4279011ee25963441a43ae0d0b7b8924ab4457fe45a743c6ae7d49fc06fdf5135ac0a6d9dd5797efc4bcd788c6b3a7d292a55413ce34cad7a990f594370b20591fd10f1e87cbc5d808b1db14c5f4fa5b035f6d5194a94687c3b21d9302b34483020f7453f201404e5135ce28a1fd82cf29d24b40a72a5a782bad814901532def034bacd7f97948a6243bb1727ea829790b8c751da2f7c80db76d105a8fd5f5057467ee33b27179078d6531848a211128eb01f462926668c2463524e6be7c1aec5cd05c5391c68b69de9a36e59f96839b1a3937512d6e16ba0b1d6c73a3050ad99ee8cabacd852aae937970aae624c387d4aa78f781d9b36288015d3bca9c0448f69f9af586fd66de013e3df2bf45253e3ea6fb08d10156e178f12278641c7c285cbb6286d4c10b20705fd6f5a3f11a5b94a3b5b403873aca381add236a16a94b9ff0d8b5b446ad226ff0c540394b6b69e64f9a359b2cff13353c2563b13c252097461e7bacf35be6b26a7c7a454a4b316e04e628d8803f82b21421310339bc8bbbbeafd1d625cee2cbf7b38f88dc6ee1109a197554a05076b173437aa8cf8ee4ab473699996e07236ffd9ca604a490c82a244e751f1010aaeab2a0cb1aa2c7879981147fa846097a7c3d16b9258cc070013912600d58fc8b07882ccd59cf5ed32919bcc240470c34d8b6938452a34dfc193ee81c44fdbb3e7ca1a101c18ebc12852b7b9741ae440d46abe99cea4f1d15f09a0ab19dbe54110fe481ccc00764c0ec40675c0fdfc057dba8c2db509d55ce518e136c42b7fd50f7236ee7be7587827143e084b61c2800bcdf04a08ca86758c0dc5a898f7bc80868e07c405322e77c764094781624a505b90a6aa91e897543c100ced8791b6bb13a4c21c10a0f96be94ebc34d220da14e47237a7f44c02bd69a5b63850fb8bed09589e22d5471594a0b3d7248a5558fb9b3233af52d78ca3d81b61e56e982c1f2f403c76db8d152ecc2e02647bf5a51a7afe25b93a181b94010892a0e2382a9d75e4e2e80383b0047b97677bf5368388b9ac2303a66924d7c1e9bb7b0ccbc819acda77a01e622fdc9cf508e6c098347ca10186d18b8daca162c88d1a63b0be094f54ae4e3d87f7dd90e456a0829e18182c2bc3490c32c5a92f90a1526914541bac6c964db9a90c68024625f4e2fcf086a11457d3e4b49a88dac0545ef1144ab71f512bc232a326f9f2f766856138051c1e87c32078e871dafc7a0f4b65bd1eb204e0aa8b5851a0502ccf30715782f1c734b65ceedf3d46afcc0f73d58263cf85f76a7ee5b083d8325e1aa249f4e8e19f0e2180af9c61dac15ebcd1f5026e47134f93af4d607b83383ffc43ea81051fb8a5f08635e68c438975c625ace557a7c37d3285799990b81615cab4f16be81824594ae3c7ab22e631cf236d6f584182a43e26491e0ffcd96ca75b3924e2c225067d2080d861191bdc7ca901a3992d3d33d74dd2729dcac28c3f4ba5a173878fb484d540b100e0eb1abef0430df852ef68f9b3f5fa86b6eaf4c240ea0cbaf2cfd6a52842a3ae63f36cec1e7bc4306ffe4ea1c5675b19b134678518292bd6a4fb4180cb7cee77e6ce3d3ba55af65c69fc4e9e7904021324296d974a08d7416393139c479b6aa64bc0d012ffed5db95ab3d60327efff65320ddcc7ecad01c81c08db84ecc7244f3f279c1880cfd32813fe2dffce52195cec08774fcfd183b18f6d347c105ff7c80f97350cd894e2766a4af07d0238ea867ae4dc00d7ea9edaaf311a1b4f54bd0d36417e4f9a9cb85f8316eb300283184c1f9d8c7f6a1f2765f612b6f6d5d2d85311819882b289d42b2f513b7501f9b72e96d008019ac12943d71f3b49aac980256c0fa897b5af8e3f3440117024540a36c0b2233a8c7a838645ec62976d88518c15a51e0e54cc38d5a0bc8197ceb72004beb220a83c294372f39948fb0a20e18dd888dd5bf5b84c315a6523ff0c12cab299bb3cef83106dd67332b45c912a6780d0c0ea1351e018098a2a987a744d4c6370d40f42459d86765476c07fc1897d727e1a4d1b986f59584c6c10eea240f5ab763d5b79c4e12301e16c61c76bc9e5a01a177752c2d55dcfdfe5f737858a86b0c75bbe0aa2014d91ee3424201a7db2006116b7ab4cb3f1c0b5e81154864c0222738850a7604794393f69769dc7d30df07c9babc6608faccda4c618c9ac472ad7c00594fbd8e3e72b6685ca234d6c91db6da31cc44a891c089c1506ad8d0a815b488635259859b452f5c70f4789007d9020dcad22f58e3c61e4783f38da1b0bb5bbd52e55e020837c2ac7ed0cfc48e1d6e71ce7b8bc639345da1b166816015cb19a7ac3e76e9bc2902d6085e96295a838418de933590492f636248b4bf1a02386def01a096048b8f19e3ed1485554d2655277e49c5ea061b2eb4051ebd9b6f0670e3309a3a2b9218c5bdaa75befa4ab9bc498beafbf98a41704fe96e62cc7bbd8b0156ce0acc81173e36a3e561ca8e76328592cb81c111ed69dcd57e830462b6de37ddd4c71c7004086f684f448bc8df0feaa0ea83d90fb5dcc3b417c7471c3523aec0de7d654a5c01e8911834a385c53a2c0efe0511afb6e81848003ef9bd78a9bc30223afbbb699fbdc49555ab2902220f51a6e59e65306d2c3b36ff39294e9ff52bde937bbc0c995ee3dd4993fdaaf3ed1f34546a03f370afafc195d57b0660244366563e0820209d5caa5835f417d670a85a17e02d9dbf60c148690a4392291d5145bc2a4b13cf1a7a36581790546a078a534c7b02a0ebf614c2934d66156e05d1144fb46fc5ac35ce57ff1bd021088992e694099ac827b8dc716bdd4c479b06ac088d68f752ca55507e15891a5256b4bea6795c423582983cfebc51449401e0c8512cf9f380c1572f672249cf828ccd5b4a7248011d46ab568d2027a0383fbc9b702b6bb87065bc817f04234d712f024ece91630619dfe54c42bb8a3f7c5cefdc21932bf5f98d18e90f7befab52dead4429313a00b81132af7003aa9d17ce61706e05ee69ad46be6982ff8a35f182089b735e5fd8180d5086b267840c7139a046ee0f497bf83bdc858046a0c1df8c3bcf41144d7a7431006b8a2dd7c36d8d173b51f0906284d42192a7d37f7d22ff8c51dce68af591929c3fbc30645da3838e90df05117c8aa75183ee1c3a52df42327515a78c8ea02055f85b7ebeed907dce57cd6611012d7bcbfe0e37e3bf925ddc4e53d0b8464e7117ae9492b8ba298ce0b1868003c6e1fa277358f7f9822c2c8cb60393ec12911f12fe68e8f530b6fda679c4247e8f189993c6d792441b0133e487e0790f7ee841ecb1bda59b228051d25213881b12bd1438c8e5a76811f18a18d03f09aa728141381ca74bd495e57ba934ea193b200c4bb5d7c775de9cba52acb773b0a2607688d76a09e75d9f1e476584ee95e55a8e3c4eef6eb6861cc6213427eed06c6c20d3e263f58097340b3f8ccb4b57b788e9bd127b2f96aeda1af8c6c67c19c717647ffe22ac56877fc5d7f20ea7cc7f9f5eb143905eb43914f2661331041bdc23b13b81daed78b574f9d6f4fd0bf927c26c01ae0f6ed0baf7e34936b0df09d3c8f4056a2f2ab86ae4ce52afe1b5ef80e09ad3572d3cb10f52cfd49aebe90bae3c2113deba714140a785d65be9d1be7c1989003e59e5cefbd026920788cb580109ea3a2ea7116936c51a117d83fdc0e702dcdd5df0ecdb586dcb7ef70c3a4520a394e2d6713f7837cd891aece79ea97a710ac58097a96484f09bebf63540cf6f6a7cd437422cc8b4e4b5beeed0aad26121395760119378c3148f14946ca35d34ba0aa6fe0363e9f27cec582782a3414ada834e32265e9aa77ae8ffa02f09c2af31ac62f0243f882227f490e70257e97ff8f118e31a87785f81c5b8bae4efaa9dbe1c8bf29f9e6975aeaa35089b1d94571536aa1cec3d7969aa13093e7a322586ee8b8265757923b74eac9cec780c1c55044275d39e3e87eede93f490af88d2b179496dc48bf012eb2e0c991b9db81c1d7d20a06385aee70c698d36f3dafe2ef556284200aca68aa0aaa005b9e27687279c8738d16aa19ad3d392624b2011de059eb71b7d013d259f118bdb15de610041f04a68db9fbd76fc5671341ded16a618915dc871fc07af1000d3039fecd4cc048a4aa41268a60887e9d184b708481bf646ba9751429429246e408039092a1496c45b09c91a52268d15cace08143041f61f0331dd363d04368255bc2b8ee635a95ac98db9896566c8564ccb712275dcc92b1468158e79f06dcb08a471f903528a3ecf5240873f68671b2f3985e3043762cd36e88bb02b031c611f679aa85ad05aa12624737035b25ff15912dff998a07c48d8687def1af0c11f00c98601ec60c4009e448d187a71eb62f5588dbf09f2c4ecbd92da0a959d7ed515ef3124f8f7e790e4e1bba592d90cbb6fd5e08759f05fa9aa503df1cb9b67e469b57f65f4c18432ec1e55981cb3583dec10072313d070256ef24b361ee0bb5b8be5c48e20050cb3d34a23314c87fa81885887a18e059ea920efd7abc836232436f9ad5088cb5b5c28d681a1679a6758613f260ad62ebfb008b61b9dc032d270ef47dad26dea8b2bcab0edbbcfa3e03539c55ee20bfffb3621ec1eb3099158199466c50b438187a693051365ee296e3077bdc231e52c08683bb57830b0142b72d9dc6a3a45a5af434f54a1a21f69160a7a9829320eceb1bb285d8a7e922ae08e60b6873a710ffc0e23b4a94ced12f71914cec21812981c6b80959cc60ed404644de18e2e0644fe8f573fbacf219efb09c368d691d68f9178933b009e30906ab7bc1da163c2911c1466e0417bf3fde4c4f76acb8822c4bf8d33ecf4fa6afcd16aa97996f641a15b926ada03bb56984a23083be752ea9f419f1affbd7060e3c24ce9e1c62349294444be1aff9039ec62088b29922eb73c45002bc6a1c752d2ba79040e002445236b9d84fc76eb6520599485f2e2ffc8f3f5deb28a47740ec1c207295ad800f476a5245037909b4c840c9782e526738971f7b5c1b0bda442d020ff12be86f537680f404e1bf505efcf8bbd13bad0b621b15069dfe7dfcf03fed1783bf40a1004672b1d1f65c6c4a8da698ac3baeca7840290f0350b426a3353ee232a36e3f6e46dd0adf8d1c22afec8a7254b3655ce1df1d95cd06a6ba34ea11a756913c2cf34be4a8fd8b4e5fd6722ec21a72bb0f98f16c181780d0b0d243e274254e47ea413872d23f57e1a3207a8750c6e36949439e2b8b614ae9ef9190d4be0c78968bd1b0ed1a9dc3e849ef1b208474b558468deaa3d2a24369d0c508bcb56286767d0809b7aee6dce61ea1941ee2c41840c1b5f1101198a06379a2cf4645fdd984f476519b3c4c28ce132bedf5c9155511aa324d436ef96d50a1d8094bec50bca2f39afaa722d21f89e1f473f52606e80fa8acd06140a2e88d125234013a73d83c4535919c62f561f803e7a0a560c36d0c7fc4a884ac0cea5c764c7764787615c7af15a55e461fa3a8a2e8c1a2ddb5004ff64ff18736f22eaba8015d484257927096df8d3fb0bd70b4d818f30fc92fc422bcf3dfff24668d2a089a64d054a29c5dcc795e9025688430912e79be78aae18839d70c1f3983dea6285140fa25f06b4cd6282e6bcdf1194c38ea78ecf5732ee61765cc43da3b04d08fe38c0c931e14c13fa741f9d758969c4996761c656422b4c54f108b634552bae8810f029d1ea841ea98ab268dde1a94ede48eab9a5a45b213b5c8c920060dee675361262ea1d742728d83dfba6728344f0e785adaab31c14c6b68ea55319c539cae0e5d007c4909d2c87d406481a4dfed2b103d576783ef782b0f24121792cc107654e42bed61252f13541b4ea4ef44c406911ae718eb537ce75c8fd8eeb54ec51a34e04feb1299dd12f47042d53dab55b4fae89f974ebd903baacd78d1b3918f7f6fde934d8c64fd9f418f378d11d63a6054a0d725eba7ce86ee6b01c82ab63eb9743a4e1de2d25fe035741f4e124849f13851c069b2aa3309006f85bf751065d43fe8a41a20bfda2fa4d55f4885d6913a7217fe84c310563150092b09b59c06153d7987fa404e89de7c043c062670eb0539c600a2d4a186e76d16a1639945c49620c4c4215b1f0984ab9afbc9b0b49ad25b80dc4f1d036106022e823e023db7724455791c6ecb89dc8c823e70dd36ba2d3f58125e346cab54b414c1dfa4e06f0e24dc3e5b406ced312619ff341547e820d4c4044c0d223e0c22b43189f7c72e8a9be93cb80c2e42b9c5fac33504d68e8fd100dc59968302512ea2535a1363c1e65df9c36bda2895afbd97f372bae26776942976312f14183c1a6747704fcbc800a93e282a311c9a7452f8db3d9e0ce88c046b8ad50553d29e4ef8633b711ac4f69f24fb7b98ee37e822a9d1119c33914b61f1fb1842b755b77d31b3465051e803976cbdd2e5435cd51822fb53f89e8b6d7289fcf9f721a663d4b78146b0557039238f0013057aac82d20902c9f1b27508853754074cd2e0b0a63528bdddb9012d29aabf1c9f68e32abe2ee5c7a89163ab5eb7050a23824739d0de2472f8538bab89c0c68502fdae470e629342393569383afb864f3efa8ca22a821dcb487d146936e61e80b7fb2e1869b9cd54ab7add25e12d64e5220f65cfef49d27e2e44f8910593f32fe943afeca48854e57afc6f4b6f37da8c5d984b099eed4cf2d2bbb0728b739c60c7c00bccdd240f3265cfb8485fb9615ed611fab2ba074833e2ed5b5c1784b5037cf061326deb9f2b4ed444597142f5d645de5819c15a8c9dcc9e6d6ab0290c115191c7e39bef68287aaaf127712b1b8273171c6353a1bb700a0e219810fd6941b72384a73ef71fe7ff1f1e914efb032e2fdb395a45115cc6f170227d5782557e09eaeee88b1b8d6b07a5b1f97ab6d630fafe4adcd3fb5c7630b93a48efb099396d818574ffbdd01a6866bc92778af5fca4a1fd2950ef14a2878efea245be10881fb2a0cb516ce438e1f011adc4e97476791d8f0617ed9c6d68f669fec1a842d67ac0fed0ed21c1ac305350f175b12711ce0b7c402c74e00c721a8b5f71271a468d0169132c92542952209977169d12ac7ffa7a56e1d1558ca4268ac9b4d5e5d7d893496ef6a750106a1c67576660b739f06d127833013e6660e739d0e908e0180740fcca8aeb192eb4ecf4a806bd60b25a2c96cd29d7ecc5830613231f79330f2f1f7dae456ca4140d1ae9c9367782df88fdb91b1cb6488bb49493e80b11b6a463d2788d0bd480bdbdbca3681623b094039f0f35bfdb581c3a07e4642d5aec683f5393f6ed35b57eece2d61506cb668c6664ac3b8bae08d14b1d8be2eea82f75d81845394820bbc4d8bf9453ee29184496711c5521048d5f6cfe462bc280debdcc5174c5115cca0e0cbb79b0bc81c984fcc7e24e2b953b90abf089a40d235e3362c66054ba9c054f192f0de1f8584d9a2fabb1f9e4729329be341d9b7925c3296e82ca6b487f2ced3f9de336f99ddd6ab3bc29dfe6ea7d7e4493d955b1b19bf2a8e423f56cb98d2bf297fdcbf391c32acc1cac3c6c9625e12336d9e143d74b111ac6f204ae6ac42c83f0a2688536bb13a996956262d4185aceb833593b97bec41578542895ec61e6e0c8968239c303bfcfba73e960ebf72313dbbd914c50f8672faf85454e632e31d1cc574ed0b8d90b0bc0f78b1d2730b768a5e067783914815755398a9cdcaa13ebc3117aba8cbbb0d8783f8e28845d23cfb172c9c7be6348823083e75c542c0208cc5e636015b622f063de724b9d8373bc1604382c9406c4148e60790e512f1c62800e20a34a28970ad1beb11b03bfda1f9363c0fdc18ba78852d5daa0170ae06a15d8edb3335779a980e5e21960e4d0b4edac9e4f1d33dbde03cf501a6c40959eaa2c7bcec186c5ed5cd2db384acbe618bcb03613995faeb72f865d909409efc4c0d1b73505a1e53555e9ef47794c36c559a61406a81ef99af83a9c66bd8a6057a5c8509a240a13d0a2a7ce50732cc9d1920a832b8d3bd5cf18714fbce9788f8d6e00ad181a6689b2d6868693a754fd0faf1a3b1f111cd0703cd33521b79024dad0088d192cdf5bb2190f9776f5e9d94f7f1ada478d7c23227e68e81f5a90cdf7251a05ea249e78e920fbe6cf008fedcdaba3fd375146891c67b9cb64643700570c0a2e539f7a16bcb8800c00ca5422ef96842961af18bc11f0b0c5659c862e72a9ae7af42306ac3a90d5a2a342f9ccec97d591c6fe1546c841f783bc2fba9b91655db93228a968677231a29774105d112226b02c325a33a593913424e8858880f3eae8bb135ae41d192dfa374a57133d98f7d147d2def30ec75b6e461e1c2b186eb824c11e7677cd49059b910c9cd855ec4656d7253b8bba10424b387e540796534223a2d10bacb72f0dea9d40f450a35fb4e80702f14786fa26077dc47b6ef0bae293f167b45a7387b96511ca25319a4542c93323bbcc958fa7d2887071df3b33cccddb79b2bb7ada50bd0e2e5ee0451bcd353c6e2b9b7358808b5e593f091519427a4b7c3979ca507e73ed7159482fb383b4da3de3e1ac45cfe4c1b31a23d2255731d9045ff23ae24a3150c9a939c60172fd0ccabd5acadfa9e6de53cee2159bef9dbe8b41d4aef1464e6ebd52348963578b13ccddac1a1ae48c6612a525756fb026edb89837ee70f4906179779507aee5e7d518c44e7cfb80c8f8e23047217efc146791244eb1836c2b0f51f4720c0e795d20f09c26906792f1af1a286581aba0fddcbadd4121a43e3d0e7e58890dd07f22faeca7775d444cef39de006781b9e350a1934a50c4f5b8ce30983136a725cc7dfe29cdc5fa8e12ec339aa538c0bcd4793a17b4d41cc0e96f437eee91c16700af1de0070fcfe680a470183e10db2d93dfb884e99694bd755cd9d67c750dd7e8ee7a6c1116afaa7d16f23627508f7db7d6212817af8874585c288ebec51bdd2cbad48280892ba1b29777516a6e0151986807a4ea2a2102e1f2062a1ae274d662408cf05290bd6942401111fba0e33ba507d0e8ed5bfff625ca8d87cefc0e6f94f9a9335cf7b0d364924479d22b27629d62cb0f2c1ad06c2f0e3a711d3b0d483fcbafc1514bc550847f721a3298ff5e777673587a9e392a024e5e2220ecf450a0084e98f9149a5803a880ea0484c290de5104a9e622246d9e7b40501f6d566cf627b64d11f6ebd938656a2dc49827af4caec7e7bab76d76c6f847f0d9807b44cd3147de0c50afa7ac7e433ac502318082ceea4f3e6530688ae4b4013d6687ecd5be93db137236c01e38ad3610cd35b401f38a9b2cb340bfeb33f41fb0886b0371dc0d84166d200e767189eac4c418daee11a40fae4807b86d20ffc30c8f23d3adad181eab4f6970cbfe6d807f7f5f447f20297f111712128870030941c4ff9c36ab411754a0cc775726d96635426a27633542ca955531ab81f484e884ffffcd5cb31ad1a5f3a0addeb6398756a35864e911768074adb11ab72c720f19a4921fa55a8dfc11790bb193d8f14fbd20f538abc1b7421ad30d263301a0573caf17029ae7f1cf9b154d207b070319ae06555a70bbd4851b97b48f8e5cf7ce24c488ba212e58a8afc036370d4d28e715a8b4eb2bc87907757cd30ec073f81550804796537440700dae8d1b131958a12b30a231cd1463be02fe93d1450016f0e75d84fe774feef608163cf7e0b0a04f2413f285ccd96dd719e461b3acda6e3c0a61c1e4e6bac9ffb460415c6a67d6ce8561013fafac0e906441029ad7111044e34ebb650584e3c502fae615105d0ec30274a6cd5178484046c202a27b801f493a0bb624823779432c884f7016e038a35830c7e66620130b8c1cdd851967b44d9a04ffe55f065fb82c7c697e1cf02fb10d945802e0fc68229e6e3982864622e8ed165b6dd10be6da7fe2d0d03619c158c2cb89689d2c032781abc9bda42db13fda0896cc52411145801ec7df8bcf3c6c6092b0a4882bb48b18b1bd17111d6ed4c750fa7101b4e224bca0c4a94f868a38a34ebe5ce3f892669650e1e861c07de4ecb0d53d21581185c54e30e29e8f1d3e8085254ef1ff0b1977a948e07c8515712af92d18770d7e1ddeb4917fdb1eda6d2759e2362c6264cd293928aa7b9bae468b98d7a63bef08211608b32cd7c2e947b32e600b02b898f3a2ed6031af7d9610e487f01642ca4c4a1ceb63b4a402fd372a90774851a8e211824348300111ffc321507df42ef144a91f3a049e104c889030c122fc142250953ee1c1add95894421111c10454321160cb3ff927675233545b84a8f04e0ab7224096175c1120445fdff6d7db75aebd189930423869bf242b8c6045e2e472d6c85c54095ac608da3b2c3227b546f8c335429b05ae0b68539231e11a61e86aa07c009b60014a0bb7e8a3f225cdd5460825976d2b2cb060e5b427687d9c6396ac7f813dd8fea02702162b3b1b9ebffa9e90d7c15e796e88f047c9e06021c02dcf3a024a77fcd74bef13fd97bdcb96779fbd32748b97df7bb04c08faed5fbdf6866bebda33dcd5977ddbbee1aeeef7c765751f6fdf1f5cd5ff5e3819e9a9cef7c6757559f7ef05b7eacd6eed1d6ea1f471e52ceba421e2e6091d823c69b3d2b0054512e777a97fc726a5778d4daa7f8949a257dcf55c68a4f45db1a4f5eed8a4f4b6a51e57bd4b0c12fd7b4c12bd0fa5ef4223adff8e25a51766f75cf52fb149f4ea7cff85464aef189deb1e8feb35cd1dd24d7db2d130e33a91bb4379a398d632e7a740541123d794361d7e86ae0868e4f07482d487760cac1af27623b842d2895a5f981837c9ab8d8f27693a514249e5640dd383be25bb708baa54c948cd0267ef1e2dca71aa3d21cfbfc6da8ef421b3a086274bc28c507b73f2e3365e9654804b6a734f062e69edb435ad9f7347f53ca5044f869cc3f8510504e22c0174c50e0ecd726d35700f194b53ee8bba8cd2332a0f23c8d49e11972243efd8f2814e94932b838fa28b7c7c81829157b21f610c1dda97fe42eac37c4a2c788fa9e05a194af7a82e30c56cdc0392aecfbbea8f80171921bbdf310edc72f04fadbc13ab604ee1cbefa74916453404894e91284f771a5e66b23d3e11ba4ddd81034d84babd0377d80c113c52aeb8b47e070e36c420383d47bbb9e1d924055ed982cb421c16f0cd552fbac69a2583c61e2f26eb7c2b40d6591d2c10bb18e028b4504d63594434c61bce1b5c7974b1ada6e92d81508b36590d3b54c194546c7068930d0e4168d8af74014a19c4b096371110cef604c295435d46d90a0b73f61cfc913c611c4342f31c4f559dfbab9e542f247565b744ba86d4215e6ce7c73b1f038b4ac3b2950ef59516c926a7ce2ff2fedf48b5df674f59cddbd1c4b1a1c301b24086a256b1f62d28cc2701ba33eb3c434f57a338d2bcb38aa15a160ef0ce337136beaeae8af0a5de3bc7ce8b7a3a62f5f1fdc517a6ff8df07522acd8d98261ee341e291691f009905dec6c65d4cb1586962197062ba6f2c368ecee2e948bd50f2e0dec5a4b7a7c755e5c081f17f2953e7c648d689f5b23a5a31e9da4c6f56091252b037917606567bfda98734b920a31bdf23003d1bdc101221ad2426437533efbf81ac9ff23250ab4c93672566ed0f45e8da7ebc054a8e5e75cc981a85020d3363a7cf41b7b19167551ddb2608730c7333e36706460ba20c08e12d204c9870e35ed25fbdc1ac995f49204602b6e0d94190e208c842c9fe0ea7695ba234213c834b36af9352a263de5d1da50b4f70542fa52c133c052038732df3a0a3ab3f0195ea866ecbf98de028103a1e4bb4b998c0c7cf635d158d897ef49446179f1274281d7d7e0919ec156d78b4c5400d14ba2707ac4be14cd2210ff93a85132bc44de20c51b037303012c3ab72d0bf31a9b9174351fa76ad4fa9946be06590ace9ec99ec894ca9ecf96cd9a900ede908d6fe2972cf513137a97e350d17aad149eb0d80cc74c5b458a4ced47147d3a58f822e27e61c92c01a009d4ade87df27d7f857840f9cd7be5f5ae5908b222832509b7bd274c3027a9d432caf37f9bfcdbb3dc796769788df5a2af8c024125e9a4295645a582a4cf8adfec5fb6d184506fe5f6488607a340cc1fa27e8e1e967780bc51086722b96744d0adefda67229683f66857351183102cd64ade5a00d2519e633472893704cf62e2aa7929e67e89104861535b955df7bd02ab36bf2dff82746698b8f9e544d26880ff977b40d8fe13805dfd14b8a5efc1e6cd6ec3cb81640707dd875b3b4677d0511009b9e6180e4f0be06f9d17c145de575c208659e2d4a7e209102ad0a1e310166183d359849e6d31a4ab0e941de234535d0a6b6af4538c27a65e67d331afe47f51a2f9b670b833cd44edc28998cd83816a2cd55da17f7727bd3d94aef4ea7ce47efb218af6b6a30173f6f8e510436bf233f4c2c0c31b82a8b9e14b5792f34a0d1d62500de3b83cc98b5ae5f176ef682728c9de86e04ee0d19d94564fbdefa0e882b89feb8319e81ba5a06685fe98ad62e02cc54410276ec54fc1216aeef673f3c9ef4735147865b1a83d1a437d2e230f0fb5c535e08ea63a68580f14a0b735a6c0c3af0f806488181d681fde70eb1dc1c539f960eadea185a4a2818e54183e8eb5ba2463a9b98a97c843d4123a3eeb84506fbcbba9c1ed2f799eb7853206e1dd20e4f0a3a227f335e586ad041f36833cc69927a48b5f5638b1969ce526619a7d08a65537895e4e4771a68085e5d93a1d0cdb8daba195de774ddce051e1ce080c08066a2f52424f5308e2f217b7976a736ed88312d35431310c48c8245118c16ef3a818967dcfd3e377be15ad8aee6d0ad274c53a4ad832a380b0458e1eed3f779cf048c8a770a581b6b8148a6b7d6139556cbf286b03d2038f2988b3bb7772abd3a9779dc7ee11906db206f07815931db144ca54b23328764a97a17c4293cc2fc5a5d3a2b47b112a51827440d211c1410977e700ace3eb05342e295bf3fc4bc4770722d9df7a58fffc72fa4326a03291ea9d7da193b2da904feb3eaf81eb1d7fd979476f5fb67a27fd90b0a8b0c4c9342d4f78636d65fc126e2899052d2fce18836fa916ba1063aa0d9c9846683550531df246ee770a130639815eb00ded61c56660a47699095bea142ed02ef05c0148e8c3b0b1a5298c1547c73496d4e9da9a2fd9cdc5775c7dd9b340251902167ae130e3f9b85e90de87f1948e46e58791b06a9dcd77f90c75d643c50be9d4229bc75dd7d7677a3af49d1e6771a5efbd827f178b498c8c8a4ebd2fc2c97972d39529b33c5e5b656ac22e945a820c5041908b8b7e5310cf07b7a8e53fe9f7e77f9e4c0e56b5a663bb96756c4d21e04026c9797faec926b9e2134709f21f0bcf0e049ab42c04253fb7a0242960a83059607c1902e96bf1461a626197e24154e872c7caef3d9065f36934b56c37d0a67ad5e188226116d1a6082d4daab8175648e48d9ba2f19a422dc92e071adaf484a6836bb959756a0600ad338d8ddd9a8b4646836eebe2d54228a7cb0b1357c02bd26992a8049b5b2e6909daf1c024fb1d949e7478b09cc351711f5bf15379dad5553e8ea5a3d6d6fa0a40c7eb94e8500c7db8acedb49223991cc2d85489ef525e5f5ad9f6997b807a8bac4cbe7c42ce309455ee4ec72089c54529250dd51daa289d064b0249901e995029cda1d55735e90d2062a486f3a132ec8eb2e3c1c33edae1eec10e7fbc5db002f436c0c86860397b3a8025af6cb80f68d87d9ca9e32103cd27c6256d10de47021511a89912e063ec94a07c85913dc5cb2d3325cd3fb03a441f4f396b3f001968014c6304ba7bbbb15c30523a4e07971f6bcf7449858f4f9fd945a7649ab6a8e28996548b34adc20dfb7d99b665daf52bce2a9e3e82e75ecbaafe8608848160188639624e58baf007ec0dc35abef443ca39c4ca5ef8879db5c13ea271d326bbdb9632a52403120b120bfa0a3a7061441535891a0e8ab26869020534e03603beaebbdce54fb6d77d792ae6fb3798a764646278dcdda3b0560b70a246f1cf2e00042d5a10e9e0832c9880e107320882145a868e68e2384a3d25b6a4ae237928943a1a85b59a390aa838be149ed5b67a0e6eac8dc90facafb6f249e177e1567e291c4d97065b4e9e6ef22ce0b647ea463f3f5a6a8132bf11c984ee72b95a748515a8b7d8b167cecde6ac0b47d3ccb772db72fb3daa830e093968d51bcb07cdf5f71650f22e6cdfb1575702f48e3db3e9512f7fbd1ca250efe8aead747f72fbb7224fcd305200054c064a860002cae4c10db0c0d2c5115818e1846a02810b172421420b2094a840d5bfd13c75c4c78621b83c61c4506d89aa59a867b767a98d7439dc4a2eec4857e5ddcaaddcca6d4bcc46d462cf62663deb192cb65aad562baf4846330d38de46a54fc69bd9b6ca26bfc2da38ccea08e9813cc9fb2f2c62e42b62044af747acc90aa9cefcfd54b50d7bff794f0a8dc0fcf7de17296284f4a5870204e63ff079073bfeb5e10742fad27f361cc2bc375bc627167d9f245655e3672f5812abba99f139daa7c50d841768b159896555f70df319cd8f2e24e34797ddfe1f1d76fb478f75323898a766b7bf61b12d74253f3eec2c67d222d1cfef87693381beda48df81df7320dfa99adfc8bb01a76aaa3cf72781cda54fece5f63be8a4f381b27d17fa24b150dce76747d57d12ab22a5b45013c1a7fea6d249dc7e8fcad70d138a016dde8224fc0c38c2cb80a69701d2781ab0e6e9c74f412730ef307f03fe5855e9678033b06ac6d7007d92eca84aff6355303f3f18ea7398a36db61268b385be64e6b440bf814ffddec66d603fc78425b90e770b7d45c58e2dfb74d0f979374a93df845b33c0a77e1feea9e110d6e9ffe119e0140b21171fa7c17d66161762260f3f0b3c847bb8cbd4e9672ef8d45f63daccd1b284a5b56a063bb64c8b85c19c6ece34d5374a8890ed2b03a60977be891e60f6f06f3077e3de1814027387703b6a455aacfc11416287f0ed533f09e6a91f0957c312ea531ad5b0c9e3df9168b0be1a0939cb5d2df314ccf7f7cc53df8f3e9b15be2f3d0b30ef3d83de957925b6afe29679aa6116e6ab96a78ee0e0a505839f2376d0a189aadf5d9ea24012188c8105084045f4a0eaf755bfb34a3f619e85ee614223df931e26340205c8f7a43702f35d0906e68fa83e20dd7fcfff911e262cf23de98bc0cc6f0433a77fa78b78073beedc064b3e397a74c73984db4ffafe114c1eb79604763b75f33cefd351fffb6cf8e705d2bd67034ed5680253e533c29b9e0470aa3c07b982363f48a46faa46d0f1c3678623a48ca7f919e05479b4c63c09a0fd11c0993781324f0394f13520cdcf0059e5316f493f3c528ccc0c8d0c1949067884e4b1653ffc44bd3616fa559fb529abe974dd0372b8bb77a00f8782431463dceb84f4f3ab8025a1705f0228ece0f8361c64fdf96db63f72b707b4c8f1a81d972a91592342c4a7488b341ad398066b94afba9a9a4f07fd9aa75162902f4fe61cc6df3d0683c56c7018885b4d5a2489d8d8d8e0d0ef9ef4be83e330512c98a8ca4f7c601e7c75539d04f3ddb3f03d293442faefbbd0081420a4d0e647d78536f4bd9faaef8d74a10d0d6d56207de959f0befb5268040a10efbb37527ad24f15cc4f55c35afc4067b9aebf3c3655a5ce577eea8ef5941659093beff4259e030ffe83afb8c540ecf295af7ce54d9c89af7cc5414c2b3af58a694ca375ab5dfd0a031086610068df389540545a398dfef4e9a87ffa76f4cd71af231ad33c1c1d8b1246d5afd8bebfa798554c143773c7966d2d638e5a61475f51cecacc299d8892456c6d50a76677111371c9435c65a77befc22926946a77e96cd1a7dc865177eaee519ce5ab9d92adbff3e9f0dff976f4e537a1c4e0fc1a46b3f3169d6460308d0261a2ea7f340f12e3b6140f01a50d64fe1c2df3146b99a7fe2f6e7f974622a36561475fd11c3673a678ac45a6d1b8745b689e6a5927a3b5ccad34aaca1d67adc8531d11af79cd6bb5135c28bc6e50c7b5ee044009661c19b506a070b57ef24eab724cb1616438c5f1a9dfddbd04cbf916762281858603092ce5546d5fbd3a1ad2a26a8401222e76c0418b2d3248c2012982c4c8c10f5f96d0b285134f4ed0c317308aaa50f50d48908011430b7a50c31692138b52c55f1f48110314a9b6afe1f453d0171e8d9a415f68b7ff01a193211ff0940b6567996581848bc9112f3852c41716b47270032690f099dffff0d408828801104d5a3ef85084aabf054f4d1fbc584535c0e20822d0a0faa28b1548f1b2030b682852f5f3f0d4036a90d5a01586161f74a1ca6247ae7ea07ea0c5feb9d3a8c9b56d537f8b4c7a346fde189d7bc475ef26ecdcb80ec8e1240778ab3d4a3d4a49f3bab7664efba9590b3b768c04837f21a993d2c884286e7b69b16b958e400b802df0007fb8121e53a2512be4e89a8333169bb198cc7946bb56a93b75178acd1c39e60326d31a3576ec6e69d4e82d6f4d1eff99c3759ef83f6e86bd898d1e906316cda10854b9b1183373d76a1ceb5aaaf4a658c762a590963dd46da59ba8b7fc7c90773c89c8a9fed25dcf2bd51f0580e2c219a73c76a20af0853b5e58a7bf72d556faf5e90997861c7acb0776ec580eef58a3b804f97a152b8d82dd0e126ad4d0aca3301777eb443628e6eede40f7879c395d0a3bd657baec59cd53ad051760ec9815536c21ef224fd25cd41c8b1d672c168bc5c6aec24e833ae6296f79ab63ec2d6fcd580ea122d2532df489bf36ce58118bb69a2547e95404fa87a080422c16f398fb0bd4e2b1fd61fe392965d041a6cc51f875997e3a789b7374a457dbf6397ab4f58ae9db6095b0cee4b68175f869f7aa45eefb15e4a7608fb89a808118f16a543f69d4fc799b691a05c48817d37e9fef6362e1cd1b63c76def943aa54c67f59a4e15e3d0b04eff28803b5f44d91679b689bc53024771f09603059257e2bf214e9e0ee6e79b502f4cb1ff345137a69862ff081345bf8711525f888c4e3151ec03c7791ce7711c7b3d41983b5a2d2c5f8eb356668655d37e208d5db123a5345ae4291297220ca10aad798a8b582d4fcd180c15299c58c93cd531929c959ef22886a408a2796aca6a51ad0979aa270741c464354f7511a6acc86de62919190b884855ff565617b9b13c355553557f3c3555ddba2395d162b0976b6bd5d56dbfc2561086222409837f21bd824e4a4f0269232d6f29a4b30db8facaed3a07bb0da45b5aa4cd405afa30513d8329b6abc51950227a658adf0aadfa422a035be5a51db9a8d6ed69a51bad5cc49140b0bb5dabd56af11f4001260bb4d0627ff419ecba4abfda35739a69b02395753f934701b7df7b605d68b19bbf98a7feae8b89196184a22c2c95b56cd62e57bbdad5420927ccc02f4da055a508f0d828ddba12e98bc0914f0757e728068811b5d6a5e18fca3c9f7a6954e5d13131313225885fa314f3f5f0cbfde8fdfa28440106d7e1610e191c3870d06c5b48f3d5efee8c5238ee5ce69bc32b380631ddfaa3f79c866bd16ff5b0041898e6d351c39d16878cf0724b60abe6d0112b4a65ed02678b9ea2e1b147033ba0d69399d36c99cba7838b2a48a398798a4a41b5b83d521edcfe52702cec3869320edd8ae59a0eb7c28eece57691a736e0a7fea2a2a2a2a2222aa332aeb45a6b32fd8bb421cfdd256ca084cb8cf3a069f2cc2becaac569b956ae957269b1df9b51d9cc01afb0b3c60031c20b0f6cbbee44a208b33b3f1dfcb915b6c6003962d2182047b0dae583bf5c6e578b738ab5b988632ebab4d6a8d2f75319957d09e45beaeecda7027fb9f5a9cc8ab8854e4a43324f753795dd9eb9d38924e41ddb75fbab3873f8bb92d42ae4c0633ba78dc79a14cb81b717abce60e7e770e62d4dac8d3b47e0b087c7d6320d1eb72c8739bc8d654f7027b2e38c19693af31c02d4c9b1befc290a4f83ad652522b126cfa472fb69cc569697fc693369f18aebb3d949d80b7fe15a8b3fa4d7fcd45f4362e674122d851394ecba2a3119e18e1de3f7b0b2eaeac2805a744742ce1c865d58a39c2449925c808ccae814b40b59a3bc466934897b19f22f5eb8d6ae56d23e5ef394cfdc67359fd5ea129f79aade507bb85507067c29ac664ead250f794a8b3277ac0bf852f01850e2d87cae74f9794a8b1cab3f320c566968280d4777907b1a9f8e2e9c96740a83d05cee4f9f0f1372a892a5e33876841d19e63f3ed65d89a3e0f6bb9299d32479e3a493a49335e75aad56ab592b1d3b8101b456256aac2a51a7b835d6a82a9ba20b2a13d689523dc85b2b4fddfc4d68476b6134b762e6380fd8b910d2e2768e59125be38e7e4b6cfdefa7837fd6e21095d5c08e5446678d9a43237068e335d6e9bfb936649e9a4147447142288a214c49a2cae992e4862b548a98c10f2a1a5160c1850a9c5062053950f5b790a742209bc10f0f52ccb00a43d5eff4c753b5a84f3e8daaa5d7ea964a69dd809a3f1d140576dcc82b04c9eddfc899536bade596e9e486a8bca925d758b057d7bc56abd1308d40824d8d1b1c1b0f92ba9a229b6e58179389ee05260f077901136511183880c14501aa9032e50b952e5852bcde41f1abc60eaac68b85e0176aa7c424ffd4e09e5963fab4a802bf874cf20f9317984417f0aa023fa51ba0ec1ccc343d0ca704b04d8e7fd8875f7664a012ac0841040e53f4e005142a1e53bc64e1e14a14162851f5f3ab9f61409dba409f7a0b3f31718f0bacf30101704a043c4b531317810b538b0bc3776425fc1b68d1c32017a031d89177fab6d0a8b1c6ed4af2e79e2d649275fe8ae003ce24f770acd3cf2d1701a738061cd95d068263879bc480c9e51e6ec1d4611b5ea00405486c939695b0125e758aa3f06b0c3beef08b5ffce2174f61252356c22f56a2e436cbc4209cbd6e269c211ff22a7c08c7d46257983b5575eb1811eed9421ad6692a9cc2a9348dc3298f017b6bd6440c64564c1d76e28a564d318834958e390b79b1f48e2ce495a845a11833d172017db16393edd3a871d62e3f91c943bf9f0433d5a4f70f92dbbdaaa159b01a6ad239ced2b834eed875b63cc5f3c47d03debc1c955e866e65568a43b75f3ccd940321fa4bf497c8852498b5578c3b361247622c269b9534dce34d5827069cf22c7c6a6f4273db6eb56e5bad11d811b564f12662b8e0f653a605f5a97ba8ab3095db4874ac0896e35e2303bdbcd86ac3efafd58f8e8d7d02eb308fd2220b9113050ec94c71132c44b29090a7b8b09198a7fe2ec60aa18e89e288c8a84664fe68d4c8ddfe12fac45f65c670702788b3a8b669076fde18bd970d2ac93da49096ac63034e552a6825ab0c95e497773d631d2a38b565e95907733b0a762b0bf7340f53a77de041f7aa97cc9c55a9a76f5b1ae5dff173efe57012e8a57048bdee7917ebcfbd8b6119ac77d463c6f56dd5321ff34564626442283426b481e2fd54ad7ae5a99e4d18181af372feee4777eda83270f41e69f361f7361f06838b28f52aab64a575fba9a44702c245597eea27a56a8b7fa4312a4506841457ecd61a3889797f4aa9bbcbf5631ba7acf64ac64ba7399117d576bc66ceeb02b1f1ac511ecc9db457947a4f7fdcb9b43f1f130be97665d72befd5fc76f84bc1bd90672e4c30ec8abeb84c9e5ecd1c2b66aa97c4c4f4aa67254a7bd533bcc452f2f6f276818ca85750e394ddd9bb8e597e721eac3f03b55afef57ac84f5caea25a497b8d95dc984c611849fa94349e6509a424fd6954291cf996be396e2bdd7b81bf94b2a5e7e72f05f6c143eae5f7de2382a5e46dd229cbb8b658e629af34f00a2462b95dae605bd528aeccdeb4cced8bb975f12e76f45ef784115aaae8028320ec801404272a30228b1334b1454963ef48db6a5b012db987147ac93ab5e5a71570aac6a0f392d672e64411833abbdc53af4c9d7e29f8d45fb16881e33576a7650a2ec89455944e2a7ac061095509280cb650c598b6bbd52a47ba463fae9d4a56a04e0075eeee72eca25a492b8b6a25adb62a10cf1a45da7cf34ddc64fcbd67effdbdf77ec8fb1db643af6d346c3eecbebd9a86896230a6b8f9348a8b9a969b11a42d8a97cd06df0a550604a54f2e0d826e2b4aab8cd2288df22810b38769acd35488dbef402d56579fbaf23075faeb8bfedcf661e63017b741afb6664ecb5a24ddb1b654984497b45a79d7bdfbcc8ccc8f3f6e901dff7478adc9c33cccd4b6b52ccc10ac1128406a5ee68dcc7ccc1ba97919d7628d40e9de48cccfbc1199aff9981ea4af59ff23d7bbb036fc2cc4fcccb320f3359fa367cccf3c90989ff92e0422f335b346792452cd4c7793debbb094c32e87eff0ac451ee200f4cc1987152db948001d0e2b266d259db2c661a2b88b29f633adc592b3e01e1e629db6c254f0a9bf1548af9923f364b33ad62db257ae5e992e0e3b93f881c98589794d1e20e694729dc48094655e6bb129d74088d14f6ead51fdc30c824f24ab9a082fa9971e929c9fba07fb7d3fd3d01b6d3462798a722d8ee44c2fb723d7cf0e3fc3fc5c7fa28d464b1bec46db886aabc6fc34043bd2b235729692de0b47d19f740925dcb851a3060d4d2994ed9420def8712bc7addc581e868579eec76d15137ac15e31a1176c66442b398e9d96efa99ae7f75ea0e243933ba9f8a0a4007752e9a107777b798aca3c554337babdb66da6fa4be1f34183ec781cb8fd08c1c7a7f4fe363eb60bf3afd5e6d362fb167f71b90edbbcc47c84464ac411932e2c0c0b312fc3c2ccd7849b9223a48779f157ab553806d9b9a4e7b6950aa487796fa63402eb1d417064fc4b0307b25b7afa5e09a511386fa9c4eef5aa5d5d58195708f55e06c9a67bbe5e29ac804c38fd643f26ac00cbb4a85f2194f3203b1766caed4dc9f6b3f9d81aa67e71b1632569397390cc1435996262262d61eed82f9b15643ee681c8840d24473b18767c0a6bb1575b6fe527d3b2efc2e5efc28d6848bd1b6d2b298d16d15a779b4c9d9a2de44a64cec49bc4d01890ae5caf99d3ce2b1d362bcc7ccc03997920312f13ae7a153323f3397aa699b4a8ea557f9fc04a7eb5e5ea193b75261b15f332323134aa6ce698487070d628666a9bf6876e836d47c05c0edd357346947e29747f6ff4a3af3f2d569f162ba902db28d568796a7bd516e947983bd6d6ed265b0463a25a8629f6375176d07527f8d48d7224e60ce68edbcba58801a7cac329c5b497721603bacb4f31a199139b3c4d7814b7494f6d4b72b8f52705b73fdc5633876cf1c76d556ff8a930734b93344993e4835fe0eef93db481c0ec429996fd4bfa71487d11949e4bdf7d3a4a21fda13e41b02325470a33b9ba3bbad0759299b79feab3adb6d8b1ca6a1452eee4a1325a13572e45a2ca6475c89b9d3a0bddb167ff26d32ad579c9585ce6a76d54af1acb445519a65836aa922dca8455e6ce84759ce89b40ab62de5d5b39423222b7dacc19c5c0a7fe8de6a7fe1ead5c704726586d45e05f4ec14542366aab324cd426736d386c5e6e3f6aeb32c52fc2349c80891aa84a507b60a28262e3831ee103a440356eb51192394735dca6a3d5fdc2f21d7b46573e2d92b5c54af887653c74fb698bae286b2b47e4f5229ba37be629ba649efa690e94071afd6166bafdd155096c550360b02f75adbc0bcb77a4af1e93e892c22101f83e306f1122dbeb75c7edb55d99a77e2ddb17adad8b6b2bd9bd7beab4a3744515600202bed27b269476ee9ff0a540bff4a477bd9e00c18e5b79b5d89897f9efb672248541762efdee05faa5277d3d28e8fd8feb3b362bc4bccc5720e665c20ad0f8e934c8ce2d3d83e3df2eacb33ae485a51173c2a7c3fb133e9b1566bee62b40f333de031fd0a207e6e02dece8ae08b4d83caaaccaea10cdcfbce72e8f32a3a6c69fcc00a7ca5d9eaab22adb8efc10c30a685102ddcde8e3f73a674d9e7953d925c4cbe3195309459cca2e16825f3837b016598889e229a6c83861259d9c3da99d57f313ab3bef819b2aaef50ae645534c9483d139cc9cd5cca9028cd6814fbd648a5335eb2106038f41a7b4484754d6a7a6334aadd82d2d1675b93299561e33e93dff793beeeb43a0d6f0057e2eb4193dd771a311e74ef29c06d12c2ce96d40c063f4def3982008818deb8523ff28b4a93fafb7fda8ce298e9e04d2298e7e7e29d00b4391bb8dc05a7a8ad22a8d0b69152ad4221696ca827860bb5abd57ac966a0dc7203494c6f8290c8acb3c522a6ebf409fa853940ace62cb7c85340af7d01675324ffd439dd4d299ab98392de3b85797aeabd568a5d4fd85c2e520985789f990100eb38c432a73998ef1cd7324b73ae6a94a4a60f4679cbff9980cb1ae0b2b11b562852a49b2ccc1f0229635ca672d7a51a3e8cbc198282a8467b1c243595021268a1631c5a6b01629cd4f453071fb29a5c9682c65e6b0139c44fd528075123be38e0cebe1d9d407c01f51e18036f56ffe88ca8b21d9f8ffedbf1fd54aafb063dbd4bff1137c9c6721fc1ba191fff07142235080fc876fc4c6833f559c8d07bf85fff0e7a742f7f581e0fccd6fe00a387f131681320a81dc3c00c222ff375fc4c6e37ccf1a65e3a3fee847e104f907fc8152df66f4ff475436a35008101bff611122acdb3f81a9629235f2cf4f55f823b36c8450ba9f2af0c759b3014ed5873d0bc116f2ef870d1b21e77125fe7a78ef5ea3334b492ff473380e385537e05479a3120e3855a34d0ffafcf4abca521e5821dbed24387513d6959ffaebeac3951437e1ca09da5a991c9c749efc6bac687e4aa4ccca58e47a432dd6588dc96aac0ba9ab45a06a85e5d92b89bdb9b47916cef8c53370f6783700a88f73f37e135a50049c08ecad610d3ba8e4d519c679d36b736de4f675ece6717eac65adae565eabb153e7c799b11b654ad0b3d7bdf7f336cdad455e586b2b58ef200a7a1e8a4e514b5bbb09cb1414699161a698e627b8fdfef5b0e9e1d5f7429b1ede7b21d38ed098e179dc937e8aa477b0870b77b4c0e59c0b9ddc903e273d2becc8340ad6dac467b5864e3a162c45948d62d815d6e3ee980c1a2c8d5b4300c46071fee6e7f7c307e76fc87933c949b2700010fafce050e1d8f8243baa7ff0c33009a7c261197c30c4155ec460498e06003855213855eca0e992b499a365ce99801fc2edb7518570fbbd15669686fee3d36215b6daf4a8ded73f728ba69716bd467da8b0a3cf68a33c7472f3ab9b15499d7ad40150bab56c9173b721099fda6f1ae5edc33afd9e7703b6ca6fb8cbcc61229820861ad7d8c6fbfe4a422dab7b4fff411a4dd81b15f8cb75af5df51b0e14801af7f09320658a1c5810c60d4b9450cd20d0a03cc1162e78420a9718ebd05c6f001587baa272a9d75ab43cd41a35696ebf7b6914cd3d1284106630830eb6b001891778c0894f0d6ce042fb420695d24aa98dcfd1d40637caff06c7cef4d4cd4da5d5f2066c953b39b3dc5a7e8d642fdb8f7ee4daaffc87bbb174997eed420ad6d5f49efa93be03e97b2029a42dfa7be0c8ddd136fa3a023ba8c5396fcf369f5ad62bb574d249275d89fb38b9fd6c4a1a454b67127f5d7512dbddb1ae5693a79358983bd65527b13577acdc7b36250bd62b5ad08095a8fa00ac59664eff0860dd82766b805cdc7e9b19707e865af424cc6b02e79d31af0c386f0c0c7c4b4fc29742df5258cb994373606b7ee6bf1dfc328ff34d8055378fe363e6f9397c81c6cffc84c2969eeec08e14e66a14a36078bee7274d9e6d44ea9e5fc6e4f19edfc64c1e4a734b6f430a5402a96bc8b65aa509c57535aa14be10f34d734d3f7bc68f35ae89e6c71bd7148e4182dc98e79f9f8e19e158e306c7c683210068c2f146006240be161c3905fb341f03f20d47bef64b30c8f8afdb9e47cf3de9bbf7be3ed3076ab169c0f1c88d293d6d79aa965d0a4b8eb63c454e61470a83358a4628847f0877850cb1b744a7704f4d588958a73fcb16b4aaa57251bba85bc0fa8399e3c5cc01c2f6536b2079af4c1d2c33a7dfc69bbe155c7513d6223f95525858518bbca36f15ec6a24668c0609d45505573179bc5114055ac20934012f1bd0160daa311b70aa52a141b455a9502aa702d41f7d36cb1b1c1b3f6e4aa8cc671f525adb08679fe8e3840ceb53ff686f6853b887c6f7b311930704334ff37cc4f7f4c74ec6d71f3989d27b3f7a776498fdee470e8279d28fbc44cc730c0381cc8f1856b7700f8d900e71a338a4b216296d74a790ed8e54c7e54fa66549773a8103977bab169307860d0bfd8fa8be0c1f785fcad25fb7983c30d0b88fd152d2fcccf3c0008223b726a4558415c9cc0129156025c15ac3ccf9a93458b026298175061960fdc02ac306d6510f6e3f97c5a55858b9fdde1577a4322b6ebfaf9c9c79e95648d98c0f30e3ae5765950a2fac2495d15a5b750915ccaa37541f62b8fd5e7b01aecc2da65f31ab97cda7a763e15a56e7dbb508dc0cb73714dcfe4d05379cd2432153c9828d602aaeaabef3270167d9f4a8ef1ff291cb341cbd05dec46b949c3474b2632dd441af1629cd4f4da3b45495512af84465ee5db628f5f97d98a9ba9ac115e2f66f31706a725d32cbad87bbe9b0ddb09960d622d79a49bd325337dfcc7cf270e4aefdc274fdc7dae58ecd04c9a53f5dbc202ff7d01aa64e3b11e80992a0a5fe76d46a9f0e0ec7564133993957a650d5c27f1386375c594c5a6c19ece8b3d565d8cc49aed56a5cfbb7fea486414e360a430e8395608052cd29b1a5341da57777f69751b28ff3374e7214d65fc68fb5645a25b25ce28436f471febfff96de3eea7b1b7ff34070fec30d8445a000b1f138611128a3139c6781bd18c2095265a4042189f3386109c03782f336be081420ff36c22238ff210e3ff517f99f4e9fb06c831d9d044328f56dfccd8f385660239c7eba9161d9a9fac7f971cea0b09546f4f6a90eacff56bd7413c1fca86e91b1bd8c9f1fe9fbfeade5299c3731b9f447c9dd5cb71da4d43d2f1ce99338fbe92085d37237b8ef6f7ca4cf8294e651489cffd1570ebb31022674233e1b1e3e183e0886e104ff71a896c9801fee0773013f22106c7c3fa54d146c8495e627b0d66eff28ac443afcb91c58fe2df4578b4e9d083782fa14d66927dd7fbc06279d74d249274937eed552e85b4bd6e9a7542ac93a5cf30398e021a054974c1e117037708f08ec9d3c9cbaf9ae4d50b5b4f1c12ff8f3cfede77dd6a7de5838b3d2faacad704f169c3aa2ea77b251ec3f9ee5dad9822e1c39184ceb50265d0d71fbd955c58e5cab798a55ff383efac53afd30f8183f6c3087a30db8c7439b1eef638c71bd6dc03a952806fbfdf001ffe649ac7af31c69b79f0b8661596928c0f9ffd149362bdc3cceb770f338610b38ffa1e8a7272cffc8b54e62bb6583cdd19e8c6f472d3b081b4800061f638c8ec23a6de3792a6de674c581e30007186b79ba23d7c03bd612bc23d76ac9b5cab5155a6523aca5139375eca7a3964d30ac6af9e2a77659d5b269378f8373c343d65715a22a41c67c48aeb97316870c0594a8901d2b135646a5a18ec01ce7bdc536d8daa4511d98b25bb9873b35bdea7df53a3065d7a64778e4ceeb797daf9c16dcc34e58e6f712c7d54b431a6b5361f400891b0c01c44594a9c513404004e1d3822148d38a67f5c6ff8cad741293a76a31735215cb3cf59b4c3377ac65ccf39a75a8d8b1962dd6e9c701e9f4d34867b79b708dbb70add5a891ca2e50a3464a5bdce3334fe17c3fd7706e9c003db15aeb149706ddae2bafd515cf9424d67b4a5ff566a55b59c28e5c8760022bbc1c1186155602e0050cba4051840d5438d17531180db2d40b19d6a20fd4a521cfdc0ee4680a5225acc725b64fc1baa5c5144a9a841d2b127664587d55d2034da2b8887b9de0c3c483939964ce4562fce4a246995a1463a27cf068b1299b1a85c266ba5dab7d4c54e7c30bd6f9c042a1514d048bfd0dc414fbbfb05bfe1e8e9c00eed828b066d5bdba779366353979daf3a80c2cb7f2d63ccd26cf0cbaa9ef1b083193140bc4f41f88bae5419de26af3d46ff2d2cec5b77055ec90156e8a99e2b82b2c96db5ddcdebad6c87de586b8ef7eaccf0db538e338d28fb5cab659a36ad89adef296c95bf3e43d37e38664f5b9124cfdaa2cec9f0e680993b63ae3865a6c598b5dabb3eece257ee083cbdd19f4440beee8ad92cac06e3f36f9eaa2eece6fc78ed9e228eca199777dd5a8fade9a3929b7d2a719b4b3e32d6f81de1ac15bdef2d9cb8e9cac66925999b7ec899dacc3cfd5809c5fa4d36eb5d87cc72627eb70d8555a6c97e22da0cb62b1582c160e1c5e9d88e6639998b8a7fe0ce2aacc8666b34f562ed7ea5edd6138d84be6a7799b1b6d3cde6b0afe83cbb05eddab4f2aab9193bdb62f8529e3649c8c93f1a0fc5bd1e53a6bb1d65a7fee1622d8cc91426b60b95aebc759eb9e75fa8978ca7fe080b8ed605f6e75d9a29516592c9692468df6ae1ac54c1ae5cfaf9ef16b674adb2a43d78aa760fefb7e0b038a5dcb8354515ee5e5a0290d6b584fe99403759094d9e33c4c1d1fba8857b3ac531a4099b911b6891db91a579b3cec40d69f3eb17c9fb8b8daedf7d60cecc8bd3859a378a851b35a6560e91d1bc6bd3cc545c159c1bdb82bdc8bd3c2bd38d9943ef5077155ec105bb9f5d5a2b7bce52d7f523f206f7d2d7e79caad35997ed65e2f06ba266ec28423e2b5eec99d311a00f9935ac3eee5758f2ed879a8a9d4b6405a6c8ff39ee2a0f4dea8e64eea3896775dc71161e647b5516df27419d5463cf3343f554638567d9a91634d60aa6a7e7e361cfea00981cc8450c61b849b18a275f96b424ee81b85513f00b367c465eaf4a76a7eb4343f6306d865f902fff7f547f1d62f8878f9b92946d8ef39588efec079fd63df98796c423cb8a78764787199c630dc1410a6653c0c076b18193b72b4f7bde71f7365d9571ca8512514780cdfef334cd408c5b90e3fba0a6eff1cdd878b55c7f23e1a263ae9e771e5e4b95296de915a3a52878d3f9099a7f9cfc6435358040a109aa7f1d3431bfea9a211161915e10411a48a26e4b889215aa17847fba358bee06f930233efe8517a0ebfc5cea607e9f977909e3be6df312f87a27dfb2450c810eefad7ef18643e4666c4b162c2919f70b11a9013f2381c4c9d2386b032c2160a633fb891773fec234eb6b96f31341c16ae6c122cc23ff45622b3d691dc147f0e166b94871c37678dc83cf57b186b710a07e362eddb178123d73b653b9f169dae2c17e360306e4aa3f83d057674e7124aece0d63b99a8f2ba485cddab83959d0caea4a10bacd3917eea92d084a424e1625c1353a79f08ee09f17a32dc05d3982b5d0aacde9183d54a84933253ddd16e9e1e62682d38d59974b037906e028e0b1b0422dc7eb6954735ca6a714d280eecd8a517320a8a53ad1ae1000720b4546393f5c6e471f132d8dc5c5b65973fc85e35abeb56bb56d79a3cdedd5ddbb34ac4465f4635ee8a6b611ad801752f3fd1991d5c432eda2233aa8dbed0b087b68e155d99391cf7c5d4e1b400e37e3bcaed8e5c49a7d8b16b8db8cc1ce677a1451eac237add60dc13ddeddd6a1c71b9b01197c9e3f5fb7f346aebdf2a0a4155623175a33f58e0c13d341c71611758e054063230e2e282968682e6091e54dc1fdce5e4e9c9339798298ee5d3a54c0762f63b9299e2ca2b338725a5d7c069b15f4c5377b9fddc433422ec570ec60979aa86e360a32ef3d4486e3ff7034512515ab7017726214416d7e3aa7b75e7ee38aad51a2584bb475e3ad55d8ea35a39849a772d35e5ccd33c0b353f83059a9f098b400132f335a6195f33139a9aab710e71e0d8d911c56e1ac44d69918b15c1c15a6c1f36ae874e6686ba45f2a9df047316c78d2a0dc27a0b57bc3173f81d0c225e9b5b419b4b36518b1c76d35a1cc2f68f4f47765dd7310f5dd775a4b22c2abb64b1843a5937ebca8e263383837130512439d80c7ba5b4634d651be252d355f4900b232edcc3e3c7e8e9bbb81c664eeb723f4c6e061c6be6f4833097e338224a841dbb56c79e9a51f36307e3588d12e22f640877b92b5387c332739a748a23ea53731ccdcf7823335ff346687ec68f4d96940299f1346fa4e667dec88ca7f9b1c99a9f7916663c4d1128406a7ec64f3fb140f333c222a322c400225535210e3ff90d5cc454530585eb7bcce10bdb57126476b0418e4cb9db8f41ba77f005fefaf5bd51adb4920661f982db27d3b29dabc5ee805aecc2ae25c58e68d782d9b1c93e010d13b591353437fa74d41f75d51dc61fe6a98e8779eaff010821295debc795307bf5278daad2dddd24e9b2e21c9befcd80ad128348d79fa95d7696cec2a9a6c234f68299498ef27ab27225feb3d5bad5ead3b42e6ff7fa21eb5a1dd962f1a99fc5b1581cacdb5a7a033b76ad5a8b05d4a89173356a9ce5ab51e3a8d63d79b6c1f6531b58ff912b278f94c953278f95c9b335317b3a927588600e8bd9d32d51a5535d943ef573300ed6b5b82e68e6741d0f60f722b9a703a2eba123b71f4735ce07d60e13c5bd60a23e168c634161f9c7bdc74d8e1d66c497936124c6448d3151dda8e64c4635afe1768d3b5a9fec5a1fe8515a7c3d9939ab16aded5a5dab6b3da1405dabe5b2a1c9b311f93799aced2da8b96cd9424474799cb5b632346bb3366bdfb703e68e6a33877f1cd5eef7a960e37e644996db6f29f03d72ebef20f5207d0dbf6b68d3a3fbfa3bbaafe18d9933fa23777eb5fe8e79eb281c838897d6e7fec8fd9ec7b85fe864e6416e0a07f35497ce71cdc1bc8495bad5dd1c176b949069085b0a5b00d2228d623bd2535cc9a7fe2d1c392e2c39a44158ae649140ae8b12cb446d3151dbf7577260d0d241de2acb53ad85f585ab0bab19a82c3be6ba5526bdd64c2c2e5021ecd8250c660f0fd621819a49f792d9d343acd35fbfbf63e014c7b3f1708d211cb82c7c326d5c3d9c29a6a5d80b17b8878e6a7e622f3835fa12722937b0db6add6a652632847b465c46354f712c1f7de114e70276b65e6f232e2e608db88cb8702e60161861dc7e1a841dddb17b887b7eb00e9785539d844f5cc9837b7c86d95c7a680077ec295486ee3373aefce09efe62ea24692d4a202cfdb123619c121cac7e3f57c44471dfcf1d31511d27e360958375cf4d67871975a4a71ac93cf5d7c02db9dd1dd964c9ae1681b8357338ce5a93a9cbb2dc3e1f5df6969926ead2534dce905d324906d1ef5a5dcb53b3bb6b75d7eaaed55dab13da8975ad98ac6b753c49db9198662834a8af28324a49db887b86796c0a1907ad56b49242bc487625b8c6550672f3aac83d3290f555859021e0ce2700a7a8ccab2cd6a93a74ed41b5b8e3335762a2ea14b32eb981759854569f5aed7d94ca629446bd88c128516354c864501f688b97d8ca2fafc6625e3e858c65b2283499937e6a752e036ee5adbae2c15932219adcee814f9ec3145f3258f9aac43034b8a746c6f7573026cfc765f26ca330260f47eabc4adf76a904ea98f15ecd4cea8692eee57a2dc67e89866725b0fa9063e9ed9320b3832d3dcd8fd696defe58c9d2bf60bffe34ca86cd71d696f3d46fc351e663de9301eb4f254bd6a75131e1b4f69b246d38fe2d855cd2806ca5079c021b73e7124914dded4e269cd0a236794a4f7f6e304ffd3825906f1562a668cb5544099cb7be3c4583e6a969d06b46f8e58e348888273b4ce983268f10194b4c1e211fa53279846c9489c92364f434df4f85268f10ee677c3f6d62f20821bde9fba913934748f7237c3f7d62f208f19e84efa755268f90fa36df4f73307984d0aff1fd9e17a73ba8940dd357450e324523000000000143140020280c0806440262a1589514bd3714000d7f9452745a1d89d324c7611043c620648c01020000220020826d0390a878fca82b7b6b0cd8f02ab0653d9000185a0f217b24e6a86306f688a612542e5ac2d836451b51f6a3584cc2e64fb4990b5507bde2072deeceb6099005b122b7bde3a59412300b6871694e16fd4a422f5721b8dd28e9c3567d5c2a1e7dfd713565ef600dd725a36e68cc0c578e5f701043c4d3ceef51a29945895f339502d82733a30401f3f4e4ca7e18aa89828017ccf79904640afa61f6424d1372ffb837275dae343b67a52571afe65e46f7cae47192c748a4370d195b8808f7619fcf84a265be2f9d9ec2a515e439f351b17adbc2bb9dd2367c79c134a060845c3cd32a0da405e788852c42f17db58f37bdc01604d8d808e3b6a45506bfa75c95cb6ca54bcaa7425485ce94da77117db2badf9ec2a20b47e4b640b2d73438926d28a3a312e6b1d05a7773d482c5512ce5b4e4c8c2198609d32f730d68225dfa20ee1129088aa0bd8881f70378322835fee7d900853d0e6fe384403035c5855ae62a0900687e0e542a266503cd5fc0a30cec4228043a65f3f5642a9bf9100785ab39e075e7e536b283a3748f08f6901b09f001c8e63d0d4424e909e0f93fc508a27502131bd4cbb9c2286920c336326e1d38bebd8e75e602b2db77129918f34dc9cfc45f9dcefc2ce6209534488f8c2875197681c926be762feaa94a18440de4e226c0d93e15656f1a423850363c0654dfd7cc5ed4e33ac56922c60435ed604995a073f4b9d9bae53a6e674e45a46c9d377b7c75ba5126cd803372dea4b642d9a399e834edbdd178ba75cfffa2282321df0eae93a033303f33325b614e772bc516651a7623686680b9d903c8bd6af2c8ecc52371c392bb5e8b69f82bda1a637ff79ca66070f7b7793115f6a16d39d68356f5d15daa1d0f5e5c1bd799e80277139f0cb562f12e3244bd451e7a7eef6a2d08c74409538bc17fdbf3bb2527cbe628b2348e2408f1b5934e0fac395cadbbfb10059a39a2d05e8104bbcb025a6d4039b74d65790cf1a5f46b87dbbfa0bae0c2bc4d64983a4c7888395eeb030654feeb59096144742f931c96b27c047f8f5587f20c8db745413d0b00c6d7d96e1e55854a6575dc2bab894cb39c918ae2af681a65a443e2e1db9f6e9d8da172a368df9bf17d70e1b518e5a5bc2c09d02e06ff0a5fd5820bf0ab819e1c192bc4903049d2e4a1b108499f12e417c8d74affe762dc4ad7b933219ce9cb47e44760f99a0f31056452762df96bc60f4f6890731b1e5305464a5838b14a6c06857f08ef6e6093c413fee7e9c1fb879a82704c9c18bfedfce8e12948018d274872ca24173e3e1f6876d4e23fc4a7398d88b79f1c74c806316254e5d4c16c2f7b456017215bc409930d4504135b46340ad25b7902ec49c7ebf378f7e00c313f9130c95b317af111a70d2212ac7f4dac906056bd1fb9fbccf275cf28dac4e565867dee4138a901a51e74d6515abc687d03dfc15849e6418f2703f02e5981ca7203165902204875128a0bb99d4aa726e99fea16b1fed807e091175fc514db51d48767fdab67ac46b5a5bbfdafdb054e95bfd7b6e20bb9cf7fc41d87e46c9b62075ddf8cec9e6b7f8b9dfce6c1c4703311c9af072a25057ac1090a0aa8540a0ec50d059c49826eab36da703699c99c468e576eb6aabb2b04432e1b9c8019a9fa1ee5a83fffaa48d9f766151f00d1c24f176f4efd737609a8fecd2e0113e4b35885169f60919345c296c8175cc69ae25d13a83c950fa8f9eb811d2c0c501b523446bba3fa6865b30b3bdb34ca78d09a5676a5bffc3fad7ae0a8cdcf70a86126308f45026c4e7e41117a05f90be00f65301af2fb6cb79a01df335580101b9d260c8c705d5623593203fe3e631e757c7b4b27d65b368e22965d7bbde0d7d2cf6aa232bf8166065b1c69f3718c75b8288bb1bb7c7d3792fe73e131c812af09051fd8ccfe65eaf3b51dde8d2b1fca063f929c1bc35c71069787173200954a22a862cbc59bfcf0145b9a4fdef7de92c1350d679c686e26e18a8130a7ea70533df4be7ea5c05aec1d48b0351860de6411802a63c8a7daa60e794b985159df7927911d4e0a804bdb9d0a09efd9feeda8c89f56ae69b933e2de9c584c01b17aa11deea54ec7fe392a984129b6bd53742b21a18603d3e1ab53b06f361ea8b18b5475e248b4b5b73576f6f31245c724c4dcc44e6bc97ff9bc1eaedb4a55d2f575c83ab880c2fadd2b81e177a6dd9950748d33c15b4cd7a61d93041c28a9daae76a526461cfe6b38014850f2cd4076855a2699a533f95ab4d45ad823366fc5e307b1c61b4f39e67c867a8407839740132439a207e22fcda7f1ef7fe39370e6e4811159bf5f449d4c091d5553456546bb1eb71299bf68b3d461a3b7d795ff105b57acd372f58e63b655e12bce3fc9aba76e4f640e4e54efedfe82a14172582000f84d960090653d8e870f210bbaa504e26a4da45a3325a96704a0838062d67163c43d457721f50c69049e9b9e57aa4bbe85fc05b2dc7b179a8336bdba187b690e6bd773bc699b277a889b446ca30885aecb1e7d77959241cf5ffbebc0410f18f7dcb2615f6476b35c135c8329bc14fd4346da46ec1db7863c34b36bc2b40a231f55e11938719a8b8958d8437b5382832fb82049df40c97a9711bf8af6f3bb13503ad9d212a32d015fb2770034e6512b3a7ba694bd69fcc3541a9ea97ab8f1f7af1ec37bc4277a556463a44af49e066ad990a6281d33a85cf9ca0109356b676bd2d6f27c21803478fbc225904f31588a7c6b3d832f43ef85e5c79500adaa3c990f891a5ba57cb09c22414ce3d0dd3de07b1fc59a4cf15d7aa0b317a64f6fd5251081eed9de73472c029cae4171d46c7d89427f6d68153608de66f469871e962d2dc04d0a5ea0126d96519752a30338b9ac5e2af8b1cfb4a0b1faa0c00db8533bd1511cd4600b36d2882ef7ce8f8b92df5e867c5c82184f68325d8aa205d8471ae70eb7b431c76a46ebda7f366037976bf6d35c41025ab9a1a5d89e36c08a1264b4bff6e43dcf0642665eac7d4fda535218e81fca5b98b93e42264ff9477d9af9f7083f42812eb0fa4cbb83d48053d838f79d68cead5959468365367658fc41fca39610b9e3521c10030a5be0f5e5ae113eb9f4215a7b209a7383a643dc29c256537cf15ae85b20b27f778410c5fe8140d815fb37b036b870da19d4949733fa62c7b189e90bbfe4d2433ec1185350ab07973bc73766c1d0cbc200c94222d0299837b1455649188d2b17ad5628fdebdaec7f44e9f2e19d55e8a73ae34a22ff54929966c23f47efb802c197defb6e12470d2156673c4d1b2eab3c346c91af4668c041a97aa76bbd626b339031fad532f90b5b75287193085cf35eb67acde05b701e4e86e6649dfeee71953cebb7c3b480530c15778c428f66b83266977e1e2f8834d44f039d1eccb23fc9f5e7795948ade354666568d690f5bdbea25a7972a41f6d907c47782a1b643a75bde5e938b02f42b85f8a81d0e9992f6ab87cd3258bd756a87e83842b04183b67cf18aa72f3391bf882ce6a2bfd707d46ae49c1a779cd8c97e67142f118f64d52b8a5cf78085f21debf536f1842d5757f474adedb1a0ec378e885dd731faeb4ca386d37f7658a83935fb9bb0d60bc4d93da660f4ab64d23270e918ec6ff0a8153d419ded3d975ed6e15cb158d8b87eef75965e715509b00e64f2762b8ee0309562c3426a589a1b8010279804b35b47941a364692f7629463da04b78bdf1f772751f0b83ca0e528d9be58680de708a495b3df08bc3c6cf4218ce32d3b0c940da8a13c81c9c91200b682c0b1624a3daf0a876cf4ea1f143f30ac107924cb9fb8133485e5a41501be6a464d53d293c6bb9109a2e05a9b444772f7b2e3dc03eaea2dce065cdcf410301e2fba9f92affeca3399fd30e8d97fff8ecdc862217083ed772cba7cdeef538a37158a2dc5068c8ca3f2d6540bcba35d728792fbd4d437870d9f773ab5db4ec62c945f5671e398621c65ffad2c649ccacf75b6878d4c1da30c63142da10df78f3d4aed4959dc0ffb72d875b6494a556a1a4a3af84653fb1d7e38271aaf7cc25edc912f0083cc1b96deab4ccfaa4d67a1aeb9f7d59a381b76dad88606059b372cdccf4083000e54babeb7cc3604a62532d1e007f78d7d0a9c1fc59637721f40dadc45f781c4f8cfd54be5a5b123624bd1ec92af5fd4bda28c4413a076b74a581163fb5e5efaea9a048339e78b4bba46d29e5d9e5285c7f00fbf4a7ff3bbeea16da2a56b8e3e4ec9a831e3f5845fae7865718cabaa575aaad9b956289e93e0949deb541425c2a4a5ba6eb9036c2a5cee0e58b91038152d0bfd846e3d158a81b12e087ddd1ebc2211ef0ae5709d8e3d74d03008346e7e65675411dedfce9e3edff6d791a12c5fb3b86058f0dd5417f14a0c535f898394d947a1ac739705c2cee8c8ffd370c6539751d47a735be9da772ff9daba0924a961215ea8df42e11e26c9f6e69bf63dfa5cd5b5eda25b2affaaee33dfe4120e3aa324d92b9c0967039e15f93775c1243faf62dfdb596589aec4da503f217a48702b8cb8a1de908f05d25201619693f9361feef7b11565bff83dbbd634849181e740d2366ac83944dea097915ec8b5bf6993a9ff102d8efb73bb1b7e63e803737bb5ef7f25d72cfd2a4b0c063d4e21c153b3e2fd22f7d0d9c397479ffd2436dd6b4ab6e87e66e51a7b16076dcf4b436747dcdd04279f6a9c7c30e6b350fffecead0ea55a9c6afe5cd0b32212d1c258416c9108dfc34ef7222a44189720a805850acebf245bff62c0f1a4ce166c6a80e6653796bf9eccd094d0e90520e47c8391a95278c1f49529e522031d72d1a4e51ef4efb0ae8fa36481cfe348af34861a3bd804bbf0f843ab8306fb5bfce12ee69d490692bfab13150f5d05d4f646847b562d9ca70cf8b213794cbe249f0fcc6b06530a4497a8196acfdf1aab0267268c4906746a8253cea74091f24b954b40ff1b318858a23b07e9e1cad5cd3046b25fb7c3ebda20f0069d06b431d22c2c8eb993b766768f3951dc9c64e4885f36236b801ba354eabac19568472826139bfb9d71745d7a9c915f3db6afefcb4b6c5834e336600afef810b91ab9384e1d1535d4a14c61395e5dc9f13912f61136858c50d2b50c07956a3f3a33fecce55c3639fc89ecd78c36acfec62a7cb6301895af10511c8a4ba169c92c5cbc9ca9f698d760855bd22a2b81557d566aa34b34c142bfb93040706afdf1b180312b047f85a620a31af11e3db1a3b0efd381802015fc53c23043d8839fa8bcaa7d8ccec727a405d7de5c9d6875c1b02672d39ce1155ef8948ffaf585a031e03d5604e9918d28d47fc3fd1b54c3beb44bc81e135a40f431eb1853dda46db02e8b915403856cf52d24393cd48b8503a684ad7e45b4b67882d0930bd835a2afff4b1072a9c245e9c3d6bdc71f8d05aa73a96bbd6b827e3267e834c220f1d6f9f8b7041f6ea0d11992d17fa3e4e9d0703853bd8781746e112b40ac5798a13e59fb169bef51adc60c9fa9f97daa2ffebf1c2eeabf8800412235351755c8850fc4182b8483b25a9b6575acb0c5f845bda36fff71422ff45559de789efa2af3350de3b5f4004faa40041b76454d032f5928fc899902516f9c2ab548f0c4cc50a32bd039d14f6378298af363507a54a779d22a512407232980686e3f1aa44c4dd252e9351a1336f682664990fdcf2489a2253f9245763af3c8adfa7acb4fa39e20fc17f134a2dd97b5bb7c7b32137b14dc6f1b705495a0aa33d2c85458fc633df4ed34284e09b4254962e4dd0c3cb25509ced0e848f4e718b69771ceec90652826ff72312543c7dd0e77a689d71cc331ef216aebc7d79c485474cfd577e8e51af657fd864489ecc7491212d7a797a27d63c21da34cff4bceb3ddf6255ad1cad9e554d57ba1363aeaddf74b6444c1d5d048eb13bc717cc28ef859673627a32a5c3699a92c167f73f4752cf25fba8e0b865bddb6127f1b6a3a9794ad3e507195009fe5d140a0f7e5f8b22786f63eb15c520da9d7f902e330c431ff9abae01f2f6edea6519d120898dab828c17f186c4a9a41c67f575f40bd4071834d1d3f8f76e4168bcc441cbcca1d719e8dbc5ac3c401db04854af0fc297beb4a103d001f89c181ab31970a66314fb281663c8d9d4052f47c0e52c40cd8003c42c715435fb054bc01696a4fd256cfcaa4c26b827f83c1f4245fb98a93fc962ce6d3aedbaa937913e4dd090bb3a1c56177faf3ad563b7b6f01289a45a1b4625b65997cd4139e4d7a00a8e2916c238c51e745aa25810fb05f381e1f9255c036d397df317551b519c03206eb0d39c0b1f9af095005c061c040b81efa3c31e133e0c766524ef7dfd048c7b717144d0f68a08ad3db595cc0ce55a7a4d9ac0931739022af3e95fedc21bbbae438d64e33b2ec7ce0f1aac2d0ef0ed2e3b30c1167a18dd46d35220271e92718858e76dd9e5211267f757302e41ed3f6963badfce790f4a623a728639a322114fa7206c44ba7e2f0360c06a0d60069af0a1cbe18461e1dd7ded7086a108d1e0120d12365c2ba0ed3fa84b03d87368edaa9cf78ab0d1d09c2081215d97804320c603cd414d88e94f60284373c18681be1242f2f9ec05509275d531547ec66a3cce91a51ff6f7509b4fa9c3a38cbdf6033e732d26afbd6384cc751576fe248cdb7060be8f015bbe668461a9a80f1f1f9fc34a120641fd57c4e791a7ba8b4c1cf45ab2bb29a6e8c71cbcdeaa0b37471ab744d295f98b6df455e95b5ea03b8229611e2f58c4ebccb4b3b03422a81a65cc054b63638356fb5a4ebd3a9132b2179c51bce5fe56250f6907342993a4451634f1b1f0c932d64048900be9d5ff3d9e622da90f363ba0d9ac78ae0c6cfb86c171c0bc4b18e08d766e998b8f9f32a0b319ac1b0d9099bb8056044b1d9f460f7b1d5600034784eae6ec59d72b1af07f55b72ade2d32c008502f9c1171c09d2940869819bed4d9d89b959425d18294ce36a589d1e4a3312d79a40d4cee04df1826ca365af3d37b6272237ed527823086c2c2219a65a7d9da649d048bf5998524305ef330251598aa9085e001905846f550e750ac8f3f01cb7b7536a807ed32a5d69fce7292e53c589d2aa9ceb6c3ab43176aae88707b7bbab095d3b125ccd0502e982d282cc149db147ad269aebb39cea587e9e1ee2b40e609993720d18e7be95c0edb421c247ae1257c3cd2f7586f08a504dd5fdc35e30d4b16a2758d7c9775ddc88907d51ef7db71175bcffbe39298c1ed5fe96049d602c426d6b281785a7ac9a0f4173ccbff82c43a43ece680667e34ca3ad6423a7bc0f553414fa6ba756a7d3e629975d8c62db2787497b97a753be0bf62c86741d2fe4d22613d59adcd9e78ef76aba175eee65e1447dcffb4688acc3c03effd1669c3295a9318cb0082477576b92dc0fcd895948a572dd6f15dff14a371074eeba2f1587ce61e26ae91df652647af6178758c0b335d59ac35aaf5ee4bd0b524a69048b8d71c3f24f568faa3bd82b2572ac078d32bf1d34acc058494fe58e05355ab3af1ae391f9980d812c11c63076c976ab746f1c0056266ebab88f7061218675b51dabbbf99dfa501a226ecb4a0e966071000187af12a21121ed9eddf61ebfd4230c9383be091a83e80d6df6b832cfc1e226c26a0f9204a929556b0846fe46edd0be4e43be1ff32b5a50a728e65b40e4af5111006b1e10b75cd34827efce8ec41866c1d36e74799d97e2bf14821b09135badb3637ce12057054fdee72871697b08d95cd53908df20f3c78884c08f1ccc6f9c8dce241658ace725d3c4c109b51f6b983acd43a71d076ed6da9e8cdd24054ab93a494231e20f72360cbfe5d0be03e2847022a072691962a01e3092bb0ceba9e6f92e0f7f839c59ba20c96f0fa1ce940ab3a2f09e128cb8be34c5b454374ea80e82c868cc2e0fbd3e7595abc34e14e0badb1d813b4764172bf15578b34bc47ff802e82f792c89c3c9ac43405286fc1b18bce704c8360957c63741c2ad887ea248a9c780bd91d962bfd54e0e55dc8b7e576edc30dd185eb50a159786aea939ff92ddbd7774dc0339e600393ecf934d55b7b543faf15c8d0c72c4963b5b0b1d1ffb0d8362726b279786b9b12947b38261b5a2630d7b691303bc4cc39bc329ddfcee7117e2895d7fba44c8e7885d4ca77f3d207d331494961860dab9495f3682d64715ca56fdf97e94cb0bfb4540df4b65dc8ac2119823d4bfaaf24aa68bdbfe0f34dc96b562600d17f8cac4064edf63d72cf7e0418a4e2c8f24b70d13a2c8b06ce47ec7c32f0a23e8c3d4685ab8e37bc7b364a4da81009561254ce6039621897dbe47dfa1245826409077e9d3698edabe7bb64f92504523dc5ab4e589584301e489e441d1344493a361f19f99edd2460c6613db744505041a2a6eed3153c5fee36f2fd6f69ff3f21408cffe537cda8f7a987ad6e83f7f2074c30a7223e1109dfe0d861abf7ce2da0ecd3b99c5a114c76d56af92ba063cda422a13cbce5e0bb7fef3f271bfa54c090dec3bb64fd8336fc81633500b2ba5b9003490a799af3b943d202bff6390b02dd9c78817287abc5c200bec5aa45b8c69b244210848ff8e44ce004313dca826b9982e066f8ec62743da0b4070a02183812ec1d913b09b5bcb26f39845781013008c28e6bd19890a61d9a59e00c11be3be0f904fbc40f4a47f7abd981cb7f7b06f954822820b906882066b6d422bc6fd82b2bf4fcbd583952a1b2184f277e29a0f7b3db617f6f4e21544b66f5ab5bdf52586469af5d6754debeca56e9ebc203755e0a66f16d5b72fa076d3052340c7f81dadf775d0aded17946bb1f351ab9faf4bdba7b2d2a578e469289514367342b47b4ca9cad87c1102da38ed143fb76216f687c5943fb8433342fc6d0ebe60bad14f0ac2dd429f9b8956274ce722bbace8d5def39d6bd2ea6a435e70114deaa767631ce043d07c5b3a6d7b18b216a4b27b4890b7880605ea1b0ae0274e2c49102ddddf8b0b17dfddbc7b94d01f2c750b6d182bc0cf7af885530cc8359d75c14d5f5a26f928f38c33d267e92f971993465a9e1295b4e8041f2de8502915c39c0ab0505aae7f29c1ace0c33fc7589f332b8af63739388b265effeb9fc9e923444f30fa7b61c3d6db19a52cc1c8e91adf89139df744007a87ffb7080668f5d68c644053db8da53e7290c5df6c55dab35b191d3d65095591a0c120acea90a59242155bfd25040ddf3573084a7dfd7ae0781e3184b4d86ff7e9e74405bcb49196464536d5a6105b14f66a9177523d8cca12aaf729f2a9ebae1a1a27698928a0d3228157edf5cd74256ad7080c272c44136c95c0fb47bed9bd30161aad04062e0690c89612f78d8d754a08b708f172d3da05ab4cfe8e6c2a991a636b140917dab58ce36b677576638012d22bfe196ac71fda4a00934665d18fc29a6effd13993147e5257dc242f0611d3d30517b91470a02a08293ffdb14676387aff2dfe3c224b7f2dc31717ff4ca26defc5bd428d2d8d66179b0d5304e6077c8c42900c56c1605e987f2e981613a960cc43c4652ecd74781d4e18e6aca36b8927f778ecae2fd94473551518b1f68d268e3aead2d7a9c2dda702050083db912d7428180bf5960b9d65972bcfaba093cde11354319722b8fc636bdddd79e145d08c264c44014c6339317387144187cef46d64d7dd75742cfc280e166b329dec2b5fbe1f3092ac5057bcf7654c08f6464521407ffdfe26d8f3384aaecfbd66056c5928bd9ff5139a576294905b34cb2eff4b4dfc64b5b2d4642c14e437cdfc7a32fcda52426474a9f1302a5638dedb153d5b92e2146c950410ed9775c2b7851fb5c4c374d9bc10c9c96e61c6be07cee0b0cbac194ad3f8cae03715a8c0a3ed11032c63839c02993611f05c252283e1e765f8e89b6f5a2d28ba4d26bd8f196662df4887868831be7a89d1491bccc6d9c5d0d8f94468f088a3da6c4dfb69d726bf3018ad56aa9aa02dae1839508764bcb415c061b70438436718dc5a1f732e3277e045b0cb7b6655560103f2b0056b948d64ccedbb896800c42c424dae98fa7764f412164ed351344d96acfeaac628053b31a6d17594d5689fb3c398caada6e036dc4f6d36ec8c38f6f3ba0d8369a11bb1f3924ab574b83b07d1446c6d9ef309c0b16e542066db6e38eb288465a169cf89a9735de5e26f2fe62fa78214104e2e3bd2d904aaac37e2aa1483bff0d31df7a59af6e5b89679df65d795abb0ee2ee4ad9559cbb921067f1ffced02e4ac629f056d301b2bd6ce3076ec3d9ff237077b7d595dbcbe13eb46515001358ccf1ee34368372f4301a86c2546b9adb1c2342349a725b76444502a19d3acc5873d75613ad0cde5ab4ebb04dd751e6d9e122e2852ee991d87fba03dcd0e80d6b16a31608e3d4ac9106b50617a8dd14fa65f2702443dd33f470c1987c5f87f53f771ec156455173bf7ada7620b114e79ed49e76d7faf2132199ed1275d960323470bbbfcbdc47f25510880fd7b2aebbf1fdbbe4a5660413f31c3c1b4066fec843d2e91819802822b0b5860b91782402ced7edf57ce52208efccde9c8cf058611995df19c65c39eb07e9527a731103073c959f43919ee4d8ecd4c46acee30e16b4c461e21f3a25775519e315fc487b40da4c2ae8064476aa4c003991dafa41021790882a3d0b4537c8c8c4bdacae02e9feb5183af373984b7dd69efc43731fd83853651eac27b22124b7619848980a094bd133e87f160f79becc0c635c7d6156c3b66833eeda31c65a8f5e851a5c7f829991ddf7865fbc882af0d62e026a6f0b3a16068d52fe6510529759370a4869e358f31431c5b7686d5656d9c1480d8e7e1f658334d39c0bc3c8a07da9a848a21abcdfc63360866e7600b09eb7d265237503aa751b449c0e236216568c692c51a2426a0826c5ba3a7bcae47b50a167cbf9e862ce9f97e9187d1e110ee7897975bd89d2cedee6b599e023e6a79c9632d5a8a8104aceda4da19012f59c1c46a10f2d1faaa1a2c641f551f50e619a389e264342d0d13694b37398e8a0b1fc0c0b8520c8c236422f353309f6d9eaa062f56443116a75f535a4c9fd3d392bbcb5382d611da0d439ff91121ee195b3dc15348eb424c5f66e7a7b5a9ed11341ab4268302ee99ab7bec930cff605137266c28e76c88f1f0aaaa3ac54e3d5b43bcf83f8bea5cc9573ecf1b08a671cc3c107ca3032b9828f0f1346511648f0baf1d09e3c705fce045aff0ba367d9f76a13ad651cb1a969c13e51c0b3606e4b62c9273df2c3b8427c21a58740d53224467d85c4964786b0ee221ed37f56263c5ae38c04e5a53ccab2c4acb9a7977af2e15c5e669ad857f49ef6710accfbc26b190bf0f54ceb349a040d46cb83e557cf30dceb99a2d5cc08a13139457288d0a1d911e1d80c7ec0e49f574e3e20549511d838e83b60cb8caaf7ca2e2507e4f7552de9c8eebee6bf0e3e2c5e562c11ac07f3fbb8e0b18798abb9f49dcd528c1620b2558182f049c0e823e12f772009388132c7d652d553c1cf880468f8586cbabead96a06011dab146a62b9ff5b4544ea8c2d4f20304be6a383e8255c3d55bedd8c563651d4ecaa4c5f7e1bbf4af8c067c46d4146e9f2b56dd19e8256895d443e6b231b1613f3c4158ca81e49edd327c248b18162ee177768914abc912dc10bfa06b6a5074aa1c83e31d0a4f5c950b5495dced31c2f97d5bf1c2f3f00725cdb596198d3f1375b0e3b00cd080038263db9c0b2435d356e0434d93feb56eaf5e0b1ea374f1e22817ca5a3cb2b7bf0480009b87510565794b41394101e1683d910d1b47f901f9607b1f2779ac7582a50bfaa21bf60e8838d08f6923fc2ae15ec2eb8f9d6518bd08e40996298736b238b0fd7cbbe92c8af04350f56f0e8716c34e91f0d3ee19b109e821e746447875c40a4d2fb2ec890e00c487a5a47c35b3c9481919c2993d38616590f13288e890248204024ae86564387bda820dbc7d0be2e1d5df29f05623b052a7d1e229584852b6e6b7fac420f027c5edf6774e28e157b2b743fde1a69a3795c3d023dac37d25923a6d18e061031ab30133158b0d3d2cdc7c6e7152cb52f450c1eef67608e39809ac4e39f7e991492c59226daf5d9a0636a09843c3b4f4fd9e31f65106679dab1f947196a0b75c693cc54e7fd504a27c993980e941291727e64556f6702f2c64bedb8b51e5574a6d518f455da24d6adbda0eaf65d6710eb710037c40e69931f7787b8ed257daaadaa0da279a7edcbf232969c20a85733a28a19e73ca2f89309f514c6a26464249bc173be3051094632766593c5eba89126ddcf88842f5003e9837b84116720910eb72c63fc0cc8d008d4eed34d37d8da8aa900eaa7b09bf9dfc19012880bdbe1d1951e8fb66aba81bcdc2e462c10355125af42ffafffcc5440a4f40b289894803886fdf49eea25b77f95631d67da93096ef0ae72e0987e3ed04941c4971b2bf2e470774ad8e18df4ff8262bdc662efc64a8dbc79747f8eb3ce0d4f985c957a341b776033540f90e902c1716c0ce84b3bdfe658c57695989a341c99d538953b93a03bdd2c2ae22e042cdf11800d4c4b11d1ac81b4e1090693e9fcd0dc44e83b39f90350d2549647d5714788b6f728c764773c3f0041214d01f335038f5219131b303d5642d38028e1ad9930daee4929c954da95f9a318ea87943c38b751c4c02d5e487a79bd07f19ca9586240bbc616d103bd64fbef9585da778f96a74a245d96b20780e7018bdd81fcdc8e4855d83d3f7b334c7ba88c52bd3db28b4e53e56e2615de440d7c34e8dfca153438371cb9e3512abb1def04e9644aeebc90d1b903590e8839f18e66fb2063c9ff7e7b8874421c9dcd22d12c2fd44e89ebf9bd2ff885c645f46156167ab1a6b9b1df0e3471706711e9e06d62bb091957f61d50f59fef35033f5befa09f74c87686e8b2210314cb74b06623d32fe34f935378e8bc4cb0d061263cef69e6e1b0adcb1ccf67d20ba4619a342dd1ae8e2f8426cd4ad23f53d27f4e128a7f6ea96074e82a823a54761ef9761079538cffdbb4598ef0ae380e1cad0a344ce05d44ef7063b1e33c54d912b3007c72431157fec3bf6684a9776d182c6c6279cdcc3f194704812309c60a6652e39acec1890ea7e5360b84fe13143dd1b1d8c0d46a37145b77b021ee6f4034fa10dcfcda937a418e4e40c6efc77a4fb41e02101f13aa3751f0f5a0bfaec91e193f85ee6ab5b04dd6964a8453cc9ee16c375947c0a8bc3ebc2f51c7e61d050af1973f748abcd5d9459b9f3835a0047265c245d232061b8ec7ccf35f3399c6b49b44b77f64b29f8ea26ddcdb70f615f0a457b7f8414997ba37ecd045976b811ce56fa647f1b1ace40f53187b4272c00c35b36fec0e180a4079d4a7e41ffa6ac190178936c8527e3a6142ec242ce829ed2fec8ce0e9acb958dabdd75ba9424b9b86e90e6363489d9f80776546ebbfef7d059a9c3c87acbe82c81ce9c9ed46a7e9142ce1f69edc5b0a2752602d5e9520463f8e0b434a41098c1e5ac463fafe52d3840e7ebede48a91d2a3bcdae71df9328619006d662c8b7b707f12da520cdc3b559d3c85ac400cb4b15fe9e346a9307dd160b2a47827d6f210fd12bdcca2d4aee821bc1a38b0189be7afb3ebcdd1717a895f28b9faa9260826e60d2afe876e7cdbddf067f589c27ff9ed34d17f7f23a351e9f8a6bef273657fd22988955d301a37e555a2df38c831c2bad254153437c4ce0517d447227ae44e3d1c74a87756acf6249d156ebbf3c2bd3dce821e1659b21cbe82018aa138ccbb8a86c9e333fe423408743d434eb58186669a6e1504175b51eb830eb7fef7dbc145aaff16e02065d3ffd86aa083d17f616420345d23535fb03e9fe3b120a6e85f090de1274812cd303d5cd6c6abd5bf062d64db45d922115063884a68f807391d793eb3f05261e557358d0581b0c0edd4b3fd66e727eb87c43f246e75243e184e899bc042a818a0fcffe04f9e49ec6ca015d56ed5a4ee69f1f39e4d1cb87bf93fd9984f0d251812f923aa4fd8149ca01b6ffc60019f526bf1bee1c5e85e289e006b4bb039296a17e6df892f5ed44c9a6df13bb43165670b2f9f7c382fc56561e7de759e9865d3a1f7e7b401d6abd953778b85092bd01a679405efc1efb3eccfcc44c846651dfa1aaf9855c7d2cdff1bc22565f06d420c41b2ed0c45c6ba0b7a5d668867860f2a5c2cf06600b23a6bfa720ab2217ddc68602a903511cb5906754a7d7628ba1a32bacc74da9ebda78547f12f8690f1da9c66a4663cc7c44df6a5c0a88e6ea8ab9d9cf2400713f6be0626c4d2836d059399183386c6c48142fbda4bd152f044a21f8fec017cb618c8f93c7ab937937841722cd63399cd2983dd2946fc5fe9ec197253b1f37504359429e18d228b5db0f96d52cef9d7ce2df17c690f9eadcfbc7e45d35a2dcee7e9761f8ce3a290b9474d36efa772bf530c73fc95aa8c17e3d0b352421c922ed224a2a6920e75d542c533ac2d6c1ee671a6e0dd0577130ea5e3e7dbbbc1db32f10146b8ee08d96e26fb6ef91c551da6ec965c55ffc24f66aada2a6cfc3d62988258bdf5e89cc1204149eb1f098214aa6c2e9a44361c59efed0eb7ae6845297464e48bc43483d454325b71a864fac21a21badd9eba5c02eaa479690c1056cc9c2bfabe7ee04d56339828b2c91ff8f10e891f38cc40dd5aa8d9ccb53bdd5afe5f4201742802da600f8d39c95ce0f7ce8ce4ff2f02e4145a99432797168357671e013314a890b82de60b3d112b9d1fa9c39b6da7bf2e701b15b256aca9f41493e7ef4145e7f894a460a1d612487fb6a03fd3c248d8280e335577509db089926dc349c6812953aa8ee4da17ba88782e02e4cc9a2f963a276d43c33e21ab9aa02f95032dc4fbe3f1469d1854f3ee15de5f1d2ebcb2f9c234a20cd809767b7200d02906f5cde78ff5215b18e18029a86cabf50a990dce76832be5fc539a46050b8eb428be66bf99756f215dbfca95f8a5de9717a7689819d8c0644fc47dda24c29e9fd565498d438cd9e5a7d857164563dc406739221acb77a2d9f07e7455d0a1b798db70df7f585688c742f6e97c9ec5d09d23fd9d722e726fbe7a2761ae7edfecdfd783f7399ae647ab98c1094bcff16d93809e1c91dd600d005739c8633c7bafbca469b13061156ad0771a09a3ff3ddc45205026532b10515351e0dbae52c83def031bdfce1a140b3da25e844e827fbfa48b1dfe3f4da5f2bc827e924227a9383341ddf3b7eda5b876a627465242602e760b4c20eac41727a920d5462b98e2a961cc318cad5aa51981a686b19eb7d5364cbd35e8194401a385c0266b5c0a216c32a6d5f902a73cfc3154306b71447e59cdef66115d2eb29aa5c85d6793dedeea401ed75660c501251b2364da40bd6e648b475f7a8100f745d056c1684879b965ec8e18c96bf7a0b2277dda53a9053c7401255d9a01dacf69a494a2adbcf775083680dda40d279ef3aafa34600f90758634d88366173532e1b7198e77b95ebaa2efb2f536805fca5615c69d05b0e558844b266ab8cb6d8c57f1904096655a4b33a8b6018063725bcb5e236b6b0588203b18a517c0c97a80c36550776d9faf1074cc838dce8485d1f656a062a362f23ed65eb25c0d31e1087d1781173e8d0b6c8888fc660b780ff106d6a498ea11e0c7ba2cd307dcaab2b44d0c60935bc4c50062379d815449eaf4488131c87264378ebb3e47646a89ee96b2ea2bbe592b6db74759205a76c2c68348a0707b83a6000d069876471ec003a5befb6b30c17db3349e2679c772c71cbe2f55f625bc8ef6dae852add7a792574a42018466626cebb33cfb3bb6caaef26fb48f0df9d0dcadca602b7c2994677c2d60609f2cd734d0f9bb9648324d09d8e25cd400f7de323862ddca15e6f7c45e983fbc3ab4e53aa1663de5400ca1f2cf25e8bbad01ea96f1adbdbd63444f1b121de7a8affa3a9b9b4311dc71378c798e463c61d12096b6a37fb7555be1d1946c48ce5bbe6dfc5612382b7bfad2ecf40d79966197764ebeae836800f8801609ec1ad12274a83eb67b4ffa982c81fcf23f8e4bb90d02ac2285367436912cdc219540093013dfe13b7cc7ef46587baf50e93f027b4e9d25e4374d523f14df64fdb53b3a09d66b957220924dc35c608d05f62a99cf89e700a22c24c76dcdd1ce5c3654a0aace27bcd4603e8846ef65a5338a68e10bda87de0c43fa9d30c436fd29c0bfc0e3fe6227b3b45034fdab38abee8775a5a1eedcb4fcedbff80b00d5f0a08280b498c9795bea3c068bf685ab857839f202da50cc97d3f641a51b06906b7da088682169d5f1812ad4b9ffcf2d2739d4d3a3b55a9d010859da4565ce9a5bd2386862263b6621d08905b460911965d09ddaf909385c87aa9b9bd34ad692352c6ca89edc4a13f43cfca9c520cb3a27b440aef04ea5241bf843c0c1ae952baf6037ac9a506d52788bff134b2ea2d6789929286a861d2c77c61344a3b84dbabc71d5dec8878710de8b1ac2af687110a013d03e26511cd4391390f6ab7f63e5f878884642c7bcfb2cc4f26d471044b238dd7f2cac980ac2b4c8b4176f6cc5c7ff910dc677ffd7459213e57a3c72f8ef2e9301eb8e37aed3af115cd16279bdfc9ae09ff8cf00091b456513cadc76e496adaa8577b167b5ab0d4f82d1b16a61c0108e90572afb1a95034391d96f96d3385e4626163ab63399de73703f26d9acae8ed54bd676a1c0279c4c57032134895db348405b9066a77d283ff0c2989e2db11b93102d38df39037de937c321cab407a72392137b3e04f9eef67f75fad531537bdfbdf5287dcb439ef5d917b16e5c94eb1b2e4196308d7aead8ad47dab231ae22240e11467cb47c6ea8df4be4f5fbfc70909178af74ea13ac6afccbd9e9481a1f8469a922b3c293beb39eb35ca7789f3c555b9cad872478a44945632fdabe18bb4149552143dba7a99fe4848b86ad6e1b0e891aeef6a9789f5fdee0f969027b80d0961555ee1955e2759e27e60dec71d4c48e9ed093a54a403caa529eabea543d59fef32b912687e7ecc88a78f22cec82cd85aaa99089b97adf350ad9baa5cf68577c4e5c68a36ebc40a0e5aef4b84dbf74138e3b376313046a06108f00622c1ba23f60099e0dac26150358ecfe19f7488a83e87ade027170bffcdaa24e72106c237c460e5f5d4127851a78696e36a660b79e879238a0ae416d6072297813b6e9f000370bf7a0cd35851028e8dbcc56f7da9ae80ee962387fc9bd18ecb7e3c883a55ecd7301c84bcd78799e3f4bcb0375f11be5b95c0b156159a1114302c46b1bffb00e45c0c6539c0e99cdf3cc3a5923b9a0df5db6e33fb912c6b20a0430d2ae7a53e8de4d037f8934c7385121eca6a93cf7bd23cc23b1f3465088029641faa6149560047ae64ba13971a796ea8ffd4002d1430ad4c1eec2f3cc9163f90877983fc0903bc221fe83d8cc38689f4eae760d3478576b7bcfb547f1ced853761e39f11bd11e9add0381a3bd6a3ea33053aa546d5f97ac0e7b2c32f478757555e6e624c0f70fce68e2dad77535bcae23fc3f762a90cf5700e56d1d635a89ad05de026f8c207b03c981fc63fda06e152085350e022cb8c42e868ca493204e020b8c724aef8ceb38bbcc96660cde181cf61861c5eaa2c143b0bcf7245f5568caad4d36a33674f19f06134d178f60cd3f09e5ac4bfb9056f88da9d5abc106552899c2763488fc1adb63ec8eb90359951d6c7912ed269b8113a1642e44904c01914eab8dc14cc04570822a84f29121418a26f5ab1306c9690706a8a75314f66ce9493022e770c11d21bf41b0f8e8b3902a2f71d8fc6438f9517af41316db982015c7d1e219a507d1e5011c17929b490239883d5c6e514a3ef1e58ed23a7fa2bdc3914568669307cfb47c3012e8019e16a6b4820bf8bd8b56a8eb8b500914540eb6054f68e2b52d1eaf1e71b7158d3c62810ad72b1183d202f8ec82e587ed981f9c2386d33a3a665bbac49f8962da749df751965add5bb28e25d5915fad5ff22402cd07d73bc5ee108bdbe05236721baa8337b7617cd4858f616490fa0db16b8c6ca1c9b9ebecb186b07b067478d521779e2d2f26491eaf9a33d9eb9e4f888ae3189c2cd3c11744b36ff91e613fe1ceff5dd10b421b8ccdf4bb079811ad26715bbaa0b7693194c6ca611c4c464ba3f58220ac9eaa8f8f2802a20cbcb415884b212d685dbdfba7d1745bc8263681752bf1319e4866b1dc6d9f59a3191eb89114dcbb81c7c7e789d6945dacb136e58d6cbc32eec9ce59dad6e78f2d3d4a160d165eae2bb3257c9d7646c0cc1cd30c50f2e1c4d41e20129e5dd93cfc3c536006e86ede04eaa6db2e24cb5bfc24634c8cd9b9dbd3a06f9d8ff9f08b2f428bd89b7b0d8080582de713103d110246c86e84ffa922b27ed9ead21b95087d45caea68f257fa982f10fe63c693772277ec09478803b9efe77a64865b448cd26e9427a5d4457b4537792ff2c6e07254fa4ae0b661db7f073b0097f4af43183c5964a1dfd7a76d26418484acab77da1377f56a53dd8c96a7ee82166070030721dc9eb7000ff47cdfda023c88051e196082c6314706a344314e59fa9ed5cf3661ba9f689c2ceb3738962b4d529e8d5e20ba148f97126055bdf6ce3f717db1d3d789f33588cabb349057833af6f91ccd85ae25a8f6c63430524a4c385a78124ec721d2474fe0097f1eb42821f3c9037b11115f3ca07c9f1f860e13e4ba5f0aac9a21cc0c6661580728eb44b2bc0357906ed5e3a1ad564831cf436be1283c8007182bf8b418abe743767a34fb782194be5de3ea4de736ad990f4fa5991e50efe181c480d08d7de955807d9025889888fd869e57f150bd9b5142ce518cdc410bb6c62acec3c35baf7472de195ca475be0873784326dc63fbbe7b8398230f9e107d189449b4aadefd3577010125ecc21b6ad78702ef84c2662d452c476fadccd6fb44dd399fa81ac844578376063aa366ed9497f000954c0deebe0641197709192a042ff698b243153af854b17a22fd500a82bb201488917e41b71bca16c9682af7607acce1a75a72a68c97caf40becd42fff2611a2778a95a3200c5fa39478fcf364d17334dca63d5ae760d81dcfa0f5c999f0e2484be1558b2703a32c629a386009e251a95af4cdc3a6eb02d35c2dd03668a85e8a5797025220c9bcf3c031efc2d7e4ee9ca03da7c1a5a6b3079243686c612245595f5ead531c3c3516aa8d6061c709c30aadbde023432de4ec2b76b313ba934b394781142e2bb915aeda9fc1c0dc1c8f3223446fb89817e60c0c267776e7301f4a0a0fa51e03ee391d50c54e2e43cd0efa82e17a960b19944d61b376c706fbe4b50b92358eee99cb6a82b0641567930810aa4842ef7a7e438f5e3ba294778f67198f42419fbe8c26151372f25ab00f85b670f080f5919d3b503f21484ca3363dc4bbcbee8b5ec7b3a9850c805f280f011e8f29dc1e68f4b002c24b3b3eb0bb38a3c8bff890465d240a4c001a015cd0412ac28fcb642f4784763056e4c8f5ba5bc8bda34e2c7fd9fa58cff62202d37e0ffcd71c5123227f13e27b6a03d7a74bb0ea96024fe042cdb8aeea33d7f540170bf44c7f50be10b01718da5a8a2b1389bfdea0d26bc51c08d8179488af16e1b36147340181581b63e9a50a267cca4a8b8fb8d0358fea1ba5bfc721deb51223c48c0312bb8b3f836eb7411d660296104d20b827a501a741c48a67ad8d50862f0e62958fa3757d2e79131b98f92e4ce123868a442e8d8c76f6ee0e6f127fa584d2e99630248b102257429e38272e463601a30602f62576405b97e9d8d9d99201435bba6a237b6baca52f1d26637100a5eb847db4f55b0c0767ebcdd727c0b1c9a2f5808a51dc92ce6b053a829f27482ce6c9f2ce969c8cb748a632628565d75ec6c15e816c5e8e27065ef3687342dd52bc8e9bd283ecbc738f7ffe96731b5b57d0c59fd364ef6dfcef89e85013821556d47cd0600f4ade602378d0dfdaa687a0ab3690f1884ef788c6d6f00cec61e5666674010dabb756f007fcf1f7abbc072766a17aad87e9b327da9db33deb40472c4ee41b8e89e557ba7e068b0e378e78a0154051adce22946220b927b9442be6e7ae93a398dce63c71c4f9543913bc53d2ed38d76d61da9c05fd7ad9c1bd828092afd68a0ddc1ce60994af18e543d3a1dfa8534a0ff54cb00fa9bccab58702e580173be834564e99f45d64b6690d15d76b6c184e3772415f21a5de32d970ae88de984d2107931688985348ca2000d51187ec6dac60407142f8db6a1b521a67b157602a16243acc20f1203099c88b0e2232580df70f7a1629fc385b353d76268c600ed326981e5b05c29e21084a9109f72c3c4120aa6821f7e395adf27c2fd829b62c50348fed84ddb62c757c275fd9a8f488957a5bdd47975f32f6626a5033b26fb74e8090faf216080ac18ed5e40b2c6c8a60d05c0d4b94693bc9c2c424014b274585dbde043bf2d2191ee640b7e206648213245dfdfa12b80f716cf8a78d148ccd4fe486677c7c58cd9b7ecd12b4987b614596d6e5a901685b8a5462db9983bf1108d536cffcb7212475e981459d38b61cd1734fed42b45e5e924392e3c8b6071b172271851e393421ab2916db94de067455c0382be39f02764d9e333ff8ef5074e0729dd2735f2ebb34e68aaf8b5d1d20319d08a4cef63f2ac51681d4f4a64902742a20c5c87b367ebe2ee6426a4b4ccc88d0d2ac8e701540b89c8ed97d5fefb4d97e0c5e93f69773a7c0152701146fcf7e64680496aa0210c24d58c2d1895f244a78b58239576e18f8036356af0cd2c36d153845d2f59a5f34e588b44502126fc437870f7436f72b15d2a199d6be336fef548bc528ca7d9a659094f3e4f3f8a2201b966b2af12e28cb4519fb733cb566b5b563d1a543b8c322ca0752567bc925272b9c829aa8f9bd206a5d15a6c3e18c807a38a2740812e06563fd7d2ebb5fefbf5a8f358fa2456301f93daeb52bdd3d59342cce5da9c1294173d23a1bf8cda80d96cd7f73d40e06e23b6f6dc97db28237635203747a31817910e940ea93e3d522d88052b14bb60e91ecd1921ad362e04165fbe4f716f49e46a7727456b45a8772f9724b728ceafc7d90c8daa49fe3ec9fc938eefcec9b8df7627eaebbbb95feb248f94a18cb6c8f1cef3744488b3fc1e15182f21ee0825bcf5f2ea3d15b180d75375811cde4f894f84b877f48ec83d87d8c884c8352f788e5ccf0003d96b3f07be5327687a9189efc5105c42f8912510c4c7d73feecc57ba7640bd9336b6294eff149c829fb44635c1e74e31d691032708c5c7d91b8be287022002ee86485dcc148c5eeb69f5fee6225f1a4c706df8719f02961031db4eea799ed96787a849a8fdc54320ae86c733d01f41179ccc15388cb3f08c8a407a0bae3a9919206b4b14a64644dfd2dd3dbec8a7fa0c3a0a62b02f6bdc9e5e5e9af68ee6451688846a15961d83de24d55c5a489f3e06c849fe88bf3991ea592bf491aa39584f4317ccffc5edfa415a2b3fd74d9e9a31ab220bf5528ca402519086a72098ec37c701f15b59e869d4f75fda7d89da761e853795d07aa513631bfdf7c44b021a4f1e78f0211fea64470d64e5e1e778249789bac9fb3fa951e19f3cc70a16571cd8092adbff17e562b5447e1767cfd22bede0ff8d3cd60c477559a3a7e0015408540591b268b6f36b9e3f3c76a51cb5be143f4b577829e229b05dac5ad71a2d2051ea8301fdbb4be2a9ee30bcfb7d0004949851e3a8afecb5d5cc01c70b8700c80ff183bb425fa523cae5c3a1aec8ceda0cdac4973f37d158da8435547f6d6d7bac2be30d6b479d1d4dbf807227865a3e45f02f9caeb051c42604addb6b40f877cc795f6b5b4ea2c3f49e9246d2b08d1c76c4dad7f4197a8a9a94d68aa01fb21aba901e52fdeea723fc8ee43b51b2c2358c5a7d9250044b578503f7bc1dcb0086946eb68a673159a39d6ea5654d74ed032932cd26156225cebba0966cc385bb594ac4271ba9003df73811ee2fde1af1725b2b542cffdcc31e3b4abff7fccd10698661c8c204147de04eacd3c3bcc8f3b8de5f612b98c92f0225a97e82d68bc964a54fc134285266c6daba38c2785ba92a0535a1231a6087bc69018872181f6ac92def6cc127e829aaefd99ea7d6ff175cf396184ce7f1d25c2793cbec4dbb55854924101b2ba2553ede9e6fd83ca134f96eaddda123971f559431bce4d2c794107a0ed675cde3768609e8eb72722bb6f81d4ad247d9fcf8a0474eb194951838a97522e25d92def61ef945d3247f3850699a216b5433c5d8920ff28cd6ebd40490629323d0b34bcf75a7acda8de9af8bd85559424fe8d476afe7757853708fb0786ff1c22c262773a599cf9e6ce352e58af51e32f35d1e111c05297e400467028b90736d3dc6f747bdf6359f02db71ae562ca4e7392c336b1e15382805989591d42cd7bbbcc35e286ee684fffa37b8a693747c39334ea3c0df5618f500012e90c5b82ddb732b03188d6a66befbaf788228bcc9bc6ea0380dd984c67314f7bff63420458cd7b3da463195ce6c2dec2e251fb9b796d0e89674ea20e0e63049c607961a65d0edb440aa68004fb804d0b70597b0e1c961793ed0c2cec865635aae534e2c197325cec05b8e71866ec4feb3b0ef4d7c36414997b80dc0db4e45f5ca29c940384a697bf534b235e7f8a4ecf14728d578c185eb294e7687ed667c16b0760439be350d9ee4f5ff9d0856a7e9bc478c1d3129426233c584566c14e38f0d8815018bbaf6d053dd45cbb779d4a73e570189c2dcdab92e69f76bcbea86dc6e92907c6f88001394f09045361bd5e509f5cefe021da32f0137ad42b6aa199bbf9742f47e261a5fa7020e29e470b12058f4331c7bf687e283827fb2a6e4ac115fbd5a95676692db9e4ae8e250c2ab9db1920f413e05c096b299bec7e81fafbff0fa6ab8b56209531545b66746d2792affb87cd42dc80c8a72ba90713af9d80c24b92d47552c71c6fa8262a2f44c0c02e0aad080231a514c6ace5f25e317dfb83aaeb7b654d799993a86d59be5701c945b5ddc78fbe45f6d1d0b57a9bcc4496fedfd38a63e8574f47afa6852f3ef66f7dcefb83c931aca5eac927ba65152089344f2408a1d1be006312031e16af83c90fcd35ab67a6fb8fc0ed1845138865d9941c531f9d7c4d56ec91a5d5760569ac168ccc8fecb292cf46c4a734fe828a1289cc40c5d2600ddac79bdaee83103836f149a5a1e44ec3a061dac42492fee1b661c1289f2afd4eddebb95ce57a4245f1175140489ca8fdab6380facf700d57654859b34f75cff573f37e03fad33685b5686bea4387e883b9df46950878dfc6e53e4254266a05606b792f1f7ea04aca021f2e9cf227da7555f698488a2157c23105ba9b2a25a6819db67a29b51f4c653e49d48f32d71ae29da6f6e4923e12688492c5480531035edd9629aa8051a5ba4c84264c6264add081b8a242a3e09e5183bf65094aa3228dfdffb1166611b574253ace9eae3a73cc66e79a2526d4c6eef833a67da48d7afeccc27e592976048cfe17f5eb3c01cdd89648b6be45094c3c262829128e7833cb2a03a544dee8f18f2538fde0ce20200304e9d0be6c4dff13a94e009f6dadf2fcc0aeb93a68b50e7468dfe5133cdfa9c5f9c279a1e4c2b580228e93858439e5157190d9726577757e9c8c1f7bd0952b91a49dec34deae6e783a407a32ac707406a2fd19b6ecddc3ca53948939e82bbaae1a3e12936a564724afd132a5064df4371d6e72badb85cd75c509740e2f224720f0491b70191c6f06f4830ced4475579a2f0a59fdcd9b6a5608cf1c5828ae89ef447b0791afd1b16368865f3c13b036a87d817691394044a572f7e20a807866be9070e7df82353906d621dcfb177601e639f98c8eee22fd3460e666c2079a68c46a3acc7fec0a6196d7ed972b98c01a8b7c3b9069ec5c0284a0ed17b8633a2268835e9344ef675149693596e0cd3c5fb041d92b7f595cacb4574b2195b33df05a6c897cb577d2ec08f91e527990489cae0e8438897dd98f4c0517556cdd8c16b09e8649e1d323b6d7b1b3761c65919ecded12d1fff38d4093b42ba7f78f2fc9a3e56620c7fb447636930d3cb6ca314c5ad386204d4d13b1d94e6689d610fbc37b6c1822943aa9e93f31e9821bb101487f2e4b29c45b893626b6fd8b5b00aee6819202eece26cfde95623740c815551ca8473032c4fc02995324831adaed0038ebec1ca6f0233ffac6e4e5650b19e984a497e8d7fa73225f1bf5510734f7856197494c57254c4a73ae77adb962878a68b4f8ce94db361c914ee1a72ee2a5beb2cb1d55b2f676db2902682d850198bab57c812763e3dbe9e8bf5f7ded2792531acbc845a113ff45046a985752a832d659a452763ef914c821fd70562443c5f78a0531ef7c415750bd7b7f356a23d7f16fa5e4f4aa614bd4436ca227c506e7965f3c1fb1137d3be60fa9aa8e3efcaecdbb0fcaaa8f8e0da50dd3709abb1c0f739bc4ce03859d248c4a98fe36ebd7a6b93ec28ddad644963ea25ce4e7742fec619c02a63c3f63176d312a3f72cb7e75d49e51fbe9dcc137e51e0c3d822c258f9912d9eac2b35eb58c992ca2a31f54ecbcac308dc16a4d2199299b207fbe729f00ac7a0cd936c03d945ef477ee67f26a69ac4d1227a6235cb50f74247a982d5921d7e3478a2e4f6413bf0ea2a0833caecba4ca14122cc214e8216bceb122ef388375f0176e7dc4316c69fb1ff65f2250884aafdfe8f85132a5dda05c5493b4ac785601faa977cc60c8e5e327207a42827ad033c39862ec3489644533715a2684d97e65636219c30b9c4ba264bb2346d611f903061ec1e2a2ef9067ab46f005883df3cece51e972da772fb56795b6cbec263f8b24987ecd3997a258fe9939e03185aa07e1a15a3e739d591b023818c87485aaf146c6bb885ff138c318df0ec3a720ec5651680723893f65c8b82d5c82e2df2dc1763b1e1376c31cbbadd7d488f0877119ea8312e316af55826476de1c8e80fc08a0d780e27e3b4b0a254408539551290e0edd066cecc880db1706871c7624392f26d4b675f483a5023432117185cc1080e379814a46196260b9d95b51ec96844ced708546a814b1322f7fc2d759409feb404674a891ae81ed5c94acba868d4e6af0a4e2e032c5b149d5b87e558e530889907e401477ffe6ba97110a9df5ecebd70413a86da7fa2dad3c87c270886f6112df50eb01faaa91074fb20a819f76f8705294130b5925cda5e31bc13b0ce12d16c0ebc1d2f92654bfc043e0748a5e141058decc53a2cd0bde618d98b861a84ff0b439e89e4c8e049ff658b2d143689021abd5f9942c404f4d8544c528bdd759c5d6e5b0ac3b9b00298a70e053a031f672086c07368f9244d343e11e640461c712c5e98819110c7ab0915381839c10e690a589a1b5974d94e873a32c43fcac82440d2bffe64b284db365655891cc21fc3ed71883495bed0e484211131acb66a6951024cba75a6baa8e706ed59bdabe9fd439166ad80608a164f57e6e76a2204561276105901e7f7822665922deb420577388ba1b0907378dfb601e58abaa3f3f5351e32a21821c8ef6d89215eabf025885b53799b0c9797b64ea1bf632986dd247c0157ca0149ad4a8c1cf54f1aef738f3ccd8666fcb77e2715af1650d2a8dbfa821f0bda1c699ccab11df848bc2725ffdad4afe904b563e24340b92d4b14c429cec963b11dcdc8ea5e12d8b0da4a514d1870255c1644a4e7c632e6cbe80ce067abc15e0d0f1c827f60bd95acbab6304a5e8f5218546292d490827da8d4417ca734e75d519268e4df092226893b8fdb3e948cd27cd449202fc750e89f5f52ec0f68ba23371866e12f163f4962591065e12fd148f792f333d5272b7a987625a2b32f722a813880a1c7d7bb374578b531130db5b49891276907774cac400cb2b3989984f71ca0d523762656b6ab832a2e827a930ba761506a1d9900701114c52154e1c1dbc6aed5848f721b8658e106c76b8fa83e5928411370480f60be97b4b780355c08c1808f0bd4db5479fd5db91229b2c197471c68e71357f3e30bb33cfb2c1a0de2bb0ae8991e376db13a88df042406e315d4a22020b27c4b0a17b8589a091060f9b066f1e74b6bbbcce0b3bfd50399e8a38d95694c9c93a56d753e861a7a78c61dcfcbc0c9ef731bb8a0bba35bc5fd662828e5b5a979b6e57f5ce5dcdb550099fcd0f03535d1860f08f722413068bbf22dc07c2bc7e0a877a97c551bd26962a858a12bb7ce31a89bbb109624e32e65894736da21ffcc2791be1cd0ee7909a9440c2e2a94f983c3bc9194155a32bcb476d40816b3d0d801013602abe83521940ddb16a59d5237c43ceb81cb22e0e87db10f71b78e048375d091cf623a7a34f0d2d07d0f8e792953ad78b31560aa3c9ce53d15adb07b232d92dc7920f8179280640596a9e0d33970b039d2df6e8a6f10a5f971523cc9da3c8c909e1da330717f63baf38fa2127e8fd457bbb17bcbfe50a1f91489f866dc1b9d5ffb4307a6168201206c822989142a8cb4d2230ac75ec76f94937106926bfed6fa327ffc70689e07b97b3cb38a02d721c113030c392e2661a4e9dfa83496e9f3eccf741dc5dfa6e34bedf8e5170bbb802e4211c0f32c9ff270554512c906154019d5e56d286ac70cbdcd0e8990bf5ab6630bd1aeb666efaac1d29d1e555213d682bcbe7526e485c593249153b2b16843413a0e386b7bc4961b87b04019c6cf76443535b202a93b0e1d403118c7432f7efe260f451f4f6bafb6e6d6cf7598dc7678f453f68573de9405e996ee053a19336249712557a0bad6abd46682a666bea0bbb897f13f87827e07e91c0fb8914f2d82a1dc82a6fc948d62372b13fb0e5d9192b213095f3b1e09d97a647d903a7ef7548fc899898c69b6e29d4e8251b4b95a0454681d15c5d09a572099e48bdb8ebed7bc0f7b7df5ac185d1b61e04a1468eac78b9ee8f7c1a0b74ebf0d3a879251d9acec16ab4e247c69478953668acaf2ddbb9d8bb761417254d2f79a4670c4b0d1c9d1309978bcabbc438011322ea9b4e857e8661e0a13314abad57bff52f9bb58a1584a80f46e45cafb4aa300d3001f96e3e92b716e82e7c71590cbed53b2fd0600d19984d76e9e5101d2acf540336efe4047d2befe8c3e5d08caab6aef025604d72107a53329430214d303b2501703a18dce17a910bacf750e6a6a6a24f34cd62b128edb148ff212463d2f8fe0e77556f048008bddd0b2e5281b492603ce5e78c4f092d807d4b01c092acdef9718ac9d39175f4ca80e822090902ef95ad8c981c24f6cfd12154dcb88b41a0489c263a13eaf9d0a906fa1d1da5166584c9bd5008b036d9715e31473afa86a7b8edcc1ce7d6d19cfbab258596d988170dbec28f5c599d3bff54c473c36f950b2b2477d6b9e6d5d7432816526e9d53ae7f7c9522c2fbf018aac446602e1ff2f8940e32f361cba746ea84f599d96d8d3ae35e1aa244c1e596454c78c326bcf7a2d7d83872a3f7709ecb8daee1499dd64d2e2c1c56b379a28fb844ee53e6250211d20a3a853a1ab41a6e09da5761e1d92269d6ba8411c7634dccad9f040c4184079ac22a6e44a78e59661569a36535289a70f56e1caf1260ad81418815dd06dcf43e5071a93cbed4599b6f9b0ab1cb20004e3baae89091cd41e46141e3f7b8eec7796d3fb752505c2bdf9bfde0537e36eb24b13224bd3b0ffa0944146bec20f5588ff8894c702bff5f1ec34be1c4fc04750e1c17ff9d81268084db67682f720e3e04bc8cb869c2201923520f71dd44b8acbce21ef4b899054a7df40b6823623c0b2a0bd5ea84c6d0ccd6fde2dcdb32e1550c97c83ccecd7b622df3fbbad02b57e36398663b62f90a71abf06cbe3f336993b61c2015fa77cc806b0577dc27ba8f7f5dfc7208b8dec49f7fcfcc3d6206007ba6c6b53190b5f5d6b3dc93011d07f39f97248d2d49f3c20f480a8e70ebaead861aadfd451c5624ec66ba0c47c58d3204dd6d0c8315169121517e85b09505da58202d37c0b747312598424f060ed24bd82d3f8555355b7789aa0387b33a498b058c362146cff3544866c4150f21daeba8b6444c6497b30576f5e2c4741ec4fb1177e496dec37c87ac5c089f649f810322e7c766aceac5d5171b07e6d83369f84f23ab60c5d9af90fc1a8c44928678f3781a6f3c1819f410c14a1ec15d8f796bd8437f6f5f1c305773a5fa50fd060ca28647dd4ce08fbc1c187449513e07e5110be92fe2ca48d5b29ec3d165c1d2b55c0a1d90fe83091a6f136dfa32f56d6b16839458ec1a1dc4e11bb81d86c72464ed5f7bd55eb17610cd82216018c5c0bdae7944cf3b9b7f39a4ef12a95374e549d1e3997976443e80cdcd125416d72002fdca307871246569febf7912c7832666b62c08c67249b1c00280f55fec403fa01b3261ce9b1cb1f1c5bbb36885209704823af89e1a2993ad36439edfeebe2ae2e113ecac4094f1ac55f75e5018e1035343262e9c1ae99764e8c2b7922befd283904bf542a334294dfeae0316decdd445381a99f759127c69cc101c15bec5bf636a2be2db723fc86ca6960834f85e41ac962855b81a03c9424d45a66e184f6c0e04bc81b3440a8d2e438cd34de595ec657a46cb01fe07be7621df82bb2bc002f94bdc2a73a5056ac1d9c82eb7cf3e7aebed0906e2edb881fa833ea504033cc8918db0800e99fd672bbfa2a647c0ae90206b172cda3cfda27aa9a49e15166d3febc7903e9614a753ce184cbca426673af847fc87b4e591223ee5bcc74d0cc3eb92a44290e8498291e8aff2c181d25f926b9bdd575200ef0478ec94176801948ecd0aee48e4438e2182717537c0d935974fcbcbc7090a29d30a44e777f035fead86720964f6012e74f54bb7cd3832dbab7ae34376926e4c7e943303b778236c0666efc4b36983134b45af3a01419af44c0aa33b690e5727a36f1a65400464466e6234178455de875b8ba44993005b5ab4bec8c0b83c630dff3871467bfd0355c6b68fea8b38c5a6a89684e345c902bdab00f8544d9ebba7e8e9c8eaa0dc78f06a0dfe9fed50188d1f368adbc2cec3601270a79430e1d50a20fee725a707bab01160272e70e47b4eb3fb24f0e6a8d74a722a3c778b0a53331dd97d7a3b718bb476bc2fcc130a1399a30d3e815d794f8da62c59d49f07eebe3c1b9ae0acf50738cee1bd3744fb5352e23e11317b3beca2f49e4f11d65f2a1a0fcc909217a8436956f0b7fdb48a021dda6919109d3083bfc4ed83a53412040808d962423ff830d50d0302023d5134b05e0763f69f3b8f1ef34013deb00ef44c7e6901ddaf377609dedce29c8ba130eae21875ac1cd035cfc6105a6e292dca098a2810274c2a4e62b01ded3783020c3e2b210ebeec1271f0a6f0167aae11b899622dde23de668d784e7bd73e461fe0dc27d66fe74454cfe0487078ec621d8a3c669b204a68717858afaf084070fb3f8a91910eb119a1e34fd94067626a0e04b9d029b709b19ba554a685a3401afed8599af124c7fcd07a4de50ad6491395c7ebb3d88fe25eaf1743de166e989fc48cb40116ea16a2224a0f3bc781d92d1e50b1257c17c51f8e0e94c48e55882b725448b5c065a0ac3a406ad65aaf21841c9849319e6947be96ba533bb48e0cf3243a1e3dfea8f59ca829205e068e8eebb0d3ab291ea8e3cf4866c6e0fc7d3a1490f538e22b94a71f3e84fc8d680d5c3f131546e9b04e03ebd4b12775b987d936fae51103e25dc643ccf1e2d51eb21c04136e2ea50a041ea0e4aed7bb037e438bcdee0d01b488c1a771470ea1d585206f9fc897a50321600650397dc863e04760bc2b81873a0a483f9db4f8af538539c744805428326eef1345543d7aa1f87e8c9eec0e2e88861a79824e5fddbbb735adffa323b84a77d4d89fc7e3fc6ddc44395507742984127a205c612bf64230f715a640daeb38c5e2c10b83e0b67ba48063d91aeea29e3a0b3e54cd3480bce32488c9c9e96f7388ef64fca07b10fbcc64bac263cb5b243b7ad1df2344d644e3b8834a90ed7b3afc0470a9e89ceeebf41fd943e07e6f84f5a728cef09fd81cc0dce8b87ad8ff4d563187675a890e4470a23f6f575a5646ff93fb4e126507900dea2b5551b0896c9537356a7fecb18b87180c6f7aebc40140f6e81e3fafbb29c0581abb38132aff792f791c7f0ae6a5835cdae36afa8bb6927f523dedf25c3be409c9c29ab64b6cd4677f60e12260f327c657488caca5c05adf5c0f34bc546b17917e81b4df5dd4efc600e764bdf481ab180ccb9be8672b83e58409e1edf1f97620e4129970928f621974f6fb4a22e9314fd566a627d1751f825e096bc4d0fc7d346d4338f285271850efd4501f94600ae28dff496649a1e2c881b6822d6df7bb92eeb94eff88d69f5a29da6907b1708dbeef2a756b5b24ded74e57051f24b616f5cca667e5e13edd0f81703bd9b6957282db27d474a39db3885f151e742f1800172ff8bff78c9d94aed3b4664e71d18e720e7cb16d638b8cd4ee2d7d8d21b01eee3ea5da3b098af304e8b4155932e9fb05f34f0273b3de7b050065f1eb2bed938b67d651416cf145c6e027641815f5f83e22f92aabae23bfd8b7f9eb5ef4eb75d518f4e5660425cf2b20a3d8922dcdb351b04145bf5074e2d9652545d22d1eb9f5f310ac40b6f6f8cf9e8b44034f0eea3378c6ae97db72d6769690f848914878c01041fd9de4ee4f9e44b40e643dc0667ae557c1f8ee5226f4645b50b946e1d3a764f5f3649ebf6144bfa185756f0d95814518d2b45f598b5d3b0716733181cb11121816b08c5ca457dbcabc22c73136fb5af4a59a8b1016c9ed7500d296b3ebc194f3eb09a2aaaa6acfc1de88de0d6b21e130072c10714b4dba78b0d47a157f73ba36d57c1eb124a4528c9b736da9ee6d2bf26f6e18763f55c46d51ce7879cbeaaae1ee1555bb911aee03530e4840f2428b9902df6460a48451bb27c044aee30453693473ce887165b3332aa6b081f5f2a6ea583590e54a0709d6bf51e03dfdbe112046877ba61b6ee2f3e64e6a4833da6de84924d78d6fae1dde28e094ea9146c5661a2e12872481225c9be7f0821f3bbbffcbb4e5da7e691c41aeee38b34cbb5a53c6869abe246b9a8c7eead149dfaf89eac588a265b7d942cfcdc596d132870c87831c61aca5a54424add061689c4101b31667956a890967590ebc845c7e4dd485b0d747792ecc655c43e7ef4c6d7c1df9557947614b9409fa4fdbc6f6e90f8c532ca44248ce096602d9b9178ca1fbe107cd99c64afb2b722bc92737779f4b611a974c34ef4b09859ed173ad6b9ef54528b3fbff208fa50785491b2f4c8f00d468b08e12051b22ccebedcfe8a589f491dd567580e2ed55c16c43b485e04297d1ca2a134a0b4b2d6f5903e53efcad9176487c174d000515800cc52e1ba21daf582413d46621682a1124d97ec422e57f9ab5b56b6a2989a748d28ec1e20cc3c1d2e0b90acf41622c8c056b00071d3d41f11bc46b1e195a2b6cdb3d03d65def58ece01adfba01900807326d10aa97cbe0d389e8ed7cb0e096d8780f38f8731adde9a3aa4094aed13786f5d3e680b0b1f6378af7b5343343dd40c433d7a17be6550544d489019bdf2b3deaeebe5deb03fbab73679f249f59dd86a5b549ed9d571002bd73ed5d595b5e2736adb8a9f0201d2f7811b5d2a50ec716009d23a2b6b2bfdfd469c440c18a03e4c4973dcf5cd9363e335d910cf6e6f9aec9c6619559d57efafb8a83b10c303b8f19007775d3d677574d1695bf6ba5a6075bd401abd596dca75a2698c6d51d31bd16178f992c67a3ce6ef77b22e3ffff37f8c136282dca20f831d0aef6ee7d11f428c52ef150e4133fa85087bc007a7dfbcf7f074465ab3ae09b8674ae2febc394a383c71abe861e9508c7a63a213a3e1380a836cab8d0a55ab39286c420a3179e3306bb51c36549434e64dc3d982220cd90d4fa54245cacfed3c4c2c2c68f540570a7f9ddd8574f8b1ab6cb008a9311aac7ecc180935d05ec75909499128fe275b6930686307ece252b968f525f8551cb4d137977934f883a48904771d8f31f440ab553c93012eac86534bb0014f34b2d2c025b51282d2bcae4ec314084b510fbec3b221cc1e3c2e86f202ac3af2fb7569fc4096b70151d1d14600f8337ba3332581f033e801b47a7a4f531202e2f6b08d2875b85f2209f6bcebcf7017993c48aa49585907e24509aef45f4307f97585093601e4e0f8a066ea025561383b592c7e10d628b002c2b12f7f8f659df63ce1d2386ea1fcef6cffbde3d229af70e72efa39ec36f670107c95c7cb2582301ebdc28d5c1d58b40fbf848f469a10531375ef8c10d563ce68812b02910d8e30869712c7aac47e578c5c3337c73e85be024ee29e10cd557ad399156b1d297d9c80076c2fbcb01f1019d975bbec8f2068c6038888547d67e2174636883252349c0a3bee33e14cae0fad9a90874ea2a75a457daf63415200611664b2cc87b7c48e95a6502e1c25a2b8d89403f3630f6eeed8063e5ab1e894f793e1eb8fd1b8d2650275d1c711595c894f76adc1d7ac6a45b6117be04465943112a0085e48abe919bf1df062b3bdd4968c3ebc0da4815cda8f731175b725ff56ce500b266643dcdc2b6a5bde2fce5882b1000e2d738b0e093ad7377d4bf6712a658abc831b32c99de653843abfafd313650656a66b76daff865d4f2f381a1178067e2040ac1ec39d119b7ae95310860f581d2d5db3fd03bd99569b298fe893213cc19cff571afa89fb9eb8d7c5378850de2499c0dd74cf591bfc79a822fbff243d9e713ec865abf1f9548f80f9119707d52746af87ed51d9862d1516fc3116127166693bd421f931baf6ead4399766963a775a83bcfce31de5bf769eb5d6f0860620d4d30b662362f4c1c2ab54510ed5b5136026413f0a5a737cddbfc1bf41c40505a79db25de36fb12b2fd584bca3e5b03eb7319415b1c3399a5afbe23472e287cfd3756feda624e0e601274648076ca1c392dd381a957ffc39c9a2363b91f0a228ac9393db86e86bc3878c37c855dfe6462c607346f96959b40ba2820a8c98fe1af51bda538b75ab3811e23797bb79d4ce0c630347376aa189c7023024861bb02fa20167995a47e7185d998f63fb0d453d15c75a11cde803287ab242cf557256f9907e752dec64029cee1bfefd4b0231b65bf9c1dea53ca53feee8582f6f1eef0eafa672af8f828f4c6e113f7b11397372a9ccea9f2562ac110f2ee31a09874fb49313b7c5aa45131ccdac02752a8f6fb2fe0f42c97aba40afd68b45f7c6ef0a29901278d8e781b24e9b4696bffe7f29ca205188118d42e5896a53a16bb0f5f868d370fe4981cdcf17af489bd98efbe4d8f2d072d4be0537af061604d03d49892a17f17f0436c86767138f5ac8c7f65b2a982ad5ceba0b88326cc9e22f27dc4794f75c9fc1dc40c81a6fc841b5a5759955952e7c6979c3aa291cb4fe258cb68cee98e754e868456aacd4790ac0db399367b7cdcf50d05b1b58d99393b458fee04ab02b6e2bae6196ef41ef7ca50fab9257d5d2226e8f862acc2ee41c437d28a10d792d7dd51d323dbfba1a9746f52af09b9679e7794093943bc10b5867fa178009c63d9c76228f9d39d1f33c13a3221c867553f3de94ea1e257039f7d9f79294180b01a0ca119b53488dd97e8db07c28b51245e36f8529af15985dda5cd4a1b6a45af243b2262f4ecdb4fac8084c75afdc9f12e5dacf3de7181f553eef3cd6e962406cd09e8586f784c38c87222b0deb0074c54b86c712f504b7ff1c264d93dbb00463b0e600b7e488d66e34356d284c9a0a5de2028650ac21e97b9b89cfaca51d4f74d1b5ed8ae2dd2d3ab99748a16612a6d5c5e39e741a9cc922f5519616f6541e9c968fe60a7dedeb03f820e0ff05a72d74e0b96042d2c8fcacdd70648189ce320924ece1f8a9a8217c34e7dd6ee695a091e043bc13dba19e2f9dac85b11af1f069cea1daedc8efa19c27c95ef20ca132f50498b52ef4c851502bdb8b453e90802bbfc77b215b02c21ba88d8fa7700c50241fcaa7ea48e50ca31b7b1ed56325d1db59a5fb44918d803d3c5f9fe607ba51bb6a3c4420977c0884bbf830aec47f205fa0cb3732795148fbe2e2712227c97f50693932c5c27cc839a5aa027d711b3060281847ce578cfa87ecd64af82f32be58bfc50ef0c904d74b08d239dab314327a3978e041bf1515e82bcb83c0192174ba57e46f4ad19e99f12b418f8f547cf54e24a09aa0b1f9466b2a908c374e7ae5dcbb4e63f174e6a9bb9fe5e7032a108a0d2a3d3b85126cd58eb00a7b6be18bb8b2453391e06e39f7b0c632ca946f8a4842f4fc18ff3dc2f2c7309d588e56c743524e57246d87d42ac2f7dbcc05f0ea326597c7eab1e48c55e384e4b469393a2d90ec1c9d23a9868811251ec449dc55ed1422dfe8798903ec55a42898cb2c2a8b2548e82c2b9faa8999976680d5a802203d83375bfb482d319818a4c6ebdaa74dbeee2df1eec60fa9a6469ad8355fea0d1e7b2e7d06aef9cdb8365aa076e4500cc82d64efdbe40fd53143605eec4448e0f571058c86ad20100f9b3ca3ea3eac441f6277b7aa44737603283ccf433e37c5d599f6773c3079970e630e2dd22ab019e476cffc54a95bda793d273424534a9440f003532ffd80b13f2cbd737b326dda4a9b54d08d95bee1d6b0f5e0f780e615b1b863d817cd266cca152d2a96aa164249e929692383d31372757e50e9d2856cc35eaba9bbe56b14511927e1b914aae972e60ca151fe9962cad9c2894bd438443c032770af6e050d6eb5173c5dd0d5bfcba3b563c7aaf5e07cb63d83dbfba6b94c26fd908cb93933b4d841c044e10658b1804596104453788328611901085d31008babf5a84e908390cc34fc231cc9d4369402d68820533b0704a810b9a34b1200b1f30218919b82006a00d0d3e87d270a8c116431da8011754d8745451451526784107a6c0248733e0cf943895c3d228f5b0a50b29801254b4822650010a4b58c20c424005143570e205cd3994160127c80009464f10d2e24aad93e0842e9c3c6551a50456d4fa17fbb6bf29a80c429a27d883ca209c97b693e08f1e29aa90bfb76e212b4b2e96689059b96f847beab26f55794f79b5755a8c215c7daa5ec11f35a78e03478b49b2ea54f583e614b62d8e47f228411d80a44052128527b690e28322d4525775c7d457af208fd45bee435346306fb94454d968e83518acd3168f4059f51d2d1fbd6cedabdc71b53ef66c757bfb29775c6edf72e7754b0f5e9d898430d52365a568edeaf42fd7e54e8963ffba2d71ec5db74a1c6badb5bd7a833f6c5ee19c16e98f6a063e954a5910c77ea86eafc22a6c0155cea157e5d478e4bb558a296b8d745a461f438bf4334bf86a917ec49129fd70d803d1ef4e2f63318e16a9d79fdd8d67700e8c1e470eb5a11b08bb5048ca3c93bf3ab5c4276dbeb067df94dd2b7aa5146563a37a9f961782ad2b1bcf8930a76731f46c9d56c5d05c2c21e9085948820e9ed4e66194865305174ba07882c20a65d4e6ec1ff4a0361c876366898dbafb34b6360c61bf2889332f2fcd525229294ad2e65128164bda2c6d4ed3276d4698a6dcffe69c734e1e9bb9f4c2a4cd1c5c5ac93b7a72df46769169c8520736b3bf1d72a79fd3f2f14696af8794f5af6868b98c09696939506db55aad3ed2c8b57a8d5ca75e84ab6b8990ec11954d21716eb68adcb724983274ebabaf7092d6816a9648eec8f02db7257eb216b6b490ab9829ad65f5d68dd2c2db03a6960e73454c45930706c906499c2390884062b5be9770bac2173b793afc99e3e5c3c2f4c9338370032f727f2ed940e2f4398ebb85dc841203a30218a4b92433b96e2907304893a762182222a2c903535404c354b35a5171b34173452767832c930edb9943b44c26ed051f12a7ff8254fab12371a03383c4e943b9cd963c0851a9540a95e718fdaab790fb5ceaeecc3293d26e06157aa0041545b0e1a0d6e701098c600327829024a444addfb7febc44e0bc5083d2e18248838d29c21b51763324b3beba349d1855d775ddb9fb340f42d427949086bd57ab573dcffb3ceff3bc96951652ebe816d6ebc89dfeee7c51e9b418e108f3c4a85313da43f1eea30d8241a233d36098e68f1c4138c228794f3d85ad8761926098aef8e00514014aa98a1620f6300f544ee11084a30dbad9a7a6ace01f4eb55789a0a8c0d447445353535313c6797ad3fbbcc9d4b37a054beb8b56532b8c9e757779086bd9749aa8c53954369bc4e3b09981bde3b88eeb3a8eeb38aed5a2a19ab76710d27f34543d463886f6033344ea5b4fd1581c350954504585128e62b629299a3cd60332477f28db25451dd4e2903c31114d84ab5729c2ce33c806d528c2791c3deb0c53032967305fdec07c10f373a86772e79b386c27f5496873db72250a59594a580339f3c2974b87478b5d304c5726c7711c27aae6b431c60dd504234cb9a09a2157a108479dd10615116518264a8351a24a4c3048120841158b1725b9e315218f158b7c453632737d221c6194ecc7b964933dac4292381d0482708429b281a9d2a20d9d2d962da26c17f5cd096aca0a8e79a0320a734772a78bacc0acfa964e93ce6cd269d2d111a94e8e8e7dd7d9ee66a353391b1d1d1d1d1d1d175a72877e34cca5251a74c2dc5994ed3a510c80d2cca124e660c9769ded3a4b25db9b25529a3c304c3a3438b5260cd3e49964d0608d309b88085b4ead0dc3160ce6fa43516cc192ca6eb7db9cb7287355745bdde493959ed52a3deb190d2f067557e151278f2d108f39796cd94671bca5638843b4798b5cad5cadad560b061d30e8504d3042950d923bab5c3f10b6f48c9321f75bd69f991225cf4ce9285d94411e4e84a30dca73de25b164b953cf05c027d75402a8e1c753f10081c05e2d1aadeaea4ab1c1cb9cfd689bb81244e11f3673aa55dfa638729f73a9e8c6bd8b5a3ce2b89ef373be8fac0d6d9094f5bba2be75f2a7c3329c2b0e4f261c2d5a691146a96d61ad61965a34214afeb2e4c20a0ff268955ce4ac4b502471fa56b99a2ba743c39475188669e6e8cf261a266d8a412985619a3c210c5318b6885a54b46c72a7c108bf3cb66c6084311e5e9e4765b065933bab892d07c2712eb59cb4405093a85c8d41b5aa94564a6dd09cf574d24aa73d6adaca0a60b55addaecd5ec82d1bad42434fc2b1656b6a6da07275cea65c1bc9d5bab246b90829c7f552f70ed5eb7251b2dc6aa1d1eaac6b875aee50585b611b3404abadeeba4c6c6d75bb0149c3aa09b983c43ccf943b1cade546ac4fd759600f17f2c446aeb3eeeab2ce24a7b6faeb75ad07a40cd66683a86ce85a222abb81008adcaf4d842d5fbd655b0acad38adcc12171ba90339c25ee76ae4c84948656a86d1316092532477f355a0fe4b61fc87dbb012983d8446c128eb36987ce12a1114ab3422deb3711daa096d1a6951621b443422d062505f56c0a0df54c56235c9e67814080f2c444542dd25f99c2948ff45b4e5ad66fd986c2d106ad2e115466836cd06bad2d5b5b69f98aae7ec2509c477012a7656b7940e2b4a69039e46c39d15589d09669c7e94c9e0e8b9ca8234613a5b5528ee366586bb55d67bbce16d9a1a029863879e61232476ba085dc6f2122034cc25bcb36976eb75bceedd637fbb1a37476d5b2ad6e2ddb949e8d7329f7ac3b8ad233ae673487ca6690b559227bab2f1eaa6a8be44e92dcc1194649e996a768a26930482debc32039c930452ea092821598404aeed19620f73f1e2a3a7910d65b4869c7711dc7b55a2d1eaac983b01611f55015a19e9a28050325e4389825982a6084b1f999633cbadcb995c71d372be1289f6094e48ef7e9e3fb7e7cdf00becf86ef03f27d377c1f01be0f87ef83498299d2621f46a967a8f761aac81d982b5ac8198c0f6060946098a6088d54a6296098729f8824b3e9161aa979ec1b13a57551cbfa3058644fba325adda76f94b6c2304853e44e1289b3023933428a7d0e061391043d0c949e590c43d4b30e27e981ca44b428148eed7d7e35d81f8f47d783f3517fd0cc0de0b341e58103f8bc331f354d88895893c25d488a3d937b3f30474d9362df8729ea3a2170e60925e4fef950f9f051a56f72a79b51907a0a2afa8adc6f2992876ba52410043f15087e5781201e813e90876cf167ca21ecec22141287c01e496ec448e771823d3280271812677522ac0d43d8932d1cfb3679649e7defefbbea3b0bfcf98e907a7f3a812179a4cc91c2466693dcb172c73b0a85c7b0a50952b66c12a75b4e5a442d282d796c8540e6b04136c806d9265656c806515adf56371bd42d5b2ba945a56795b66c4ab7a5a34c572b9ce4881a4c93aaa70ce6a429140a758a6938f11c9a93abde94355993b591060e6d15bf8ae7508ba107240e513b31c5db13b96b5148aba5ab22330b99528fe33cc97d3eedd3def496660f72bfd669148ef3ca6d125199076c5584e3c44265761fe7136809c2eea6d24a1e41c484127ea1a4f37de0cfbc5648164cf60ac9c2caa744850a95fc8142b250aa18357b207766cf2a0a754f65d356ab573dceab3d44e007da3b27869e7557125b67a9c58b7426752d95c1d0e911714410f388499b9e53ce164d79f24c25c81c5308120a390b16891384a21095c789050ba54d9b0d4b4b9e362a55aacc398564c19445952caab428b3902caa5461a2d30abd42b260aa5484748962215930a166e02441c8dd86a94599ab5099debc902ca8f46c52b152a569d3d6b256ea9664c7948515a52ca86441258ba469cb426962215950913b730e654a049bcd668332b1a0b04cdb1c32cea13c87e6afaab66c9e62a71a117cd266f45e9ecbe5f25c2ed7abd65a5fb76b495cafdcc7b9bca0aacbe582f90baaba5c2e17afebbcae0b29ed87a62c73badcaec1dc594319bdee7afdc575174ee2ba0b5ea9264542bafe4396d9f4b95ac9a576a7b490c2bae7b85aa967d6f3ace7b98c5c7fb9dcd80b2779dd05d303d55a5661d7a5dea5509de7e170e6a0216de6602e519948653998b417aaca75bbd66395bb8e48e2f40bf389ccd19f4da4e041a7aa5d57bbaee7d4a16bb27b7bb57ab5765dd7553abbead5ae1bd2f11167978f61def1fa088b79bd5e1f696479dce1593f5025c11ea9a698f79703d55c37561fde9959b95b402229eb83ab5527e455af1097cbe5250b2255cb7ff8c096b1835a0a233373e14ae5bab2f6fa9aaaacfd3e4e1573b9bc5abd5a29a54a61f78faac2aeabc89daa85a4757761b404511bcd1fa8ac6b7406a148444522715acf7a5eadb513296d918a1dacd6ea59afce5e3e18555d1d955dbc560bfc59fd43ddcedf8beae5be56f769973b6b3017e6ae2be4f217b083b9495cf641a618be257cad8294a1ef7259ad7f9f8c6a82e0e7edac0bcc4b07f3020373180ccc5f6e4c08ec2f87bdc0ccea3f14aaaba8d7a35cff813fb53beaaa9a41f0678e221197fa17a3eaa8777b676edd7166619c57c89dea25a0a7f250959bb3d6baf2260fad972eaf7731920657cfd06bae8f3459ba3ed278904e6866994def195d0ab19c39eacb953557e7ae76ddacb9ee90adb95c770651d990c46932c5e640ee8fafcc34ef5c22f6e272a0dae7725f37f612c45f3726e4e5afbfe01c2a6b172ca96ce21cbae6ba0b07218290ce8a2518c5249a3c1d947b62e91498603609fad6449ed775dd41f0a75bfd5057e0ed67387f90324b876b28d4dda44c8bc38961547892329905f014246532e7489e8ed64a2d3a72775733475340cafa6454e10722224a54bdda433d1b5799a79b514a8389394db9678ef98fa6464551f97565ed06e9a19f6bcd3d806ed9dacf0bdebbab87f9823fdc5daeeb5429b4e7012a2571adde577ae6bd065d4c88ebe5ae03d55e37e68293b8fc75a0da0abc2f772edd97c3e0b98463dd6796bbdc981098bb1c064f261cebfe82a71395c9621dcea16b2e77c1b389ca642db3d02cc2a69f9408c25166f9a10859f58f86cae51999b779eb5f57d6e8c4446a2bf552cbba6930c5ae5684632bf9e0049906e128445131ea51d875f65ee759ef0757964ea43458cbe6cfc995c7a4c309e4e95154bca3310460471d826403d37823c728b5a21c32a7e0be6591fb7585e507deae798165ca1a9db5a3375a458777cc1cddd51d58764c9e4965937663eec87dae8394481c39db03b2974c59575507c91d2ad48492308cc11e43c7e42c007248389d9ea6d3132b8600ec6831784c61149516abf44c7228a51671b4d897d9a2a8a0a8f4ccc3a8282d2af52c858d6894862222421d115927a22c7d7084254f6c000a4c388941ad8f1aa2341c49c4800a50ac18e94004b5be75b24f9e5038338ddfb8046f9899c69bc8ff0c637884b20b2b64c938b2ecc20a54b2159e32be8c15a2d830730d9a472180f2cb696c646ee3e26bcf6e2e0241dfba0daeb74eafeba336d473b22f173bbd2d2cc4ccb1cbdc3a07440c17f92189e4d679cc3c7699391c66f04824738fe1d6633c344ecfc8f437637451852fba980296faef9be08f99732e1c5aa742ccec7ae5ca62bdfe635deeae3b73fec1ba34f6238665d3cfdc31f61be7402032779d037dcc2c73fafafff338900056450930b30ceb2edc9222646122af5f8238c89c622064eec240dc9cdea76397c88b8589bcb2cd2b1e89bc2868038d1f37b8b668831f738a471b58b628beb6287237bcdcf0729abfdcf1884cc33dd3d07c82378cacd71e72eb16bc81669a09de307267d1fc82472332cd636ec11b3ad39cdec09d855b788cf9119a692e411e343ed61a7804ca34b8c66328f8a3738dd35b43e6158f5fccad9f61cceb6d4911c61c99991ea9995ee50985311b667ef9cc2fe7f0cd278efda071ee34648090394ee140e358ca1cf3d038fe276908651755b092f165eec2235076bd75c7576e9d75472299c55dd962e5689f99c1a378d6631c1e69deba0f6ddd1b675d7bf18e445ef9e53477dd21667e79833f3abffc7839c6a3cc5f5a9385836c71e421b74cdde5d32ff88e327ff9f772c7ce339f047bcce071e6998baf9775d76dfde5e56bf087ebadb3eed3dc59989bb932bfdccedc1d69ee5dbc40c8fc3ef617f087abbe5abcc136f9e6b1c7301032a77173471a4ce4956530fe8dfb50d50db1986f3c75479d7c23dff86a06e59f63191279e52166a6719f067fb8641a47dd946d4978ef38f33d775344f7f5c6bf1b37e50489708cd1a53c4e2b313a8526298d295b4ed9a8784ac291da3adf57be1728d7cf6ebcf38d1b974da3b696f56fdca0d306003cb18284cce1980d315b652966b1e4216a06c28641e3ca7ce3d6ec2d099d2809477b435275454751a6a83c9967ce7d2c95046de04ecfba407035b35aaf13e4d162e191fb1033b35e73d685d5ef93493296149b31164ce16249b1582cf6852aeef5aacbbdaa4ec11fce87b68093053387eaaaab52799e3773bbc7543095cb55b5085a9bb211718516286551842e94f08305e0a08a183c41f941d313bca0f08e610e227353d56521645d4e257a1e8b41cd4693a8127716a698b3f636ab10c67ed0980ddc6b8cbec628e65133cd36a416a748214c113969b19691b24df007cdc21999853aea5c10b9bec382bdd11bbdd12b84b245d6a90f655d7bea04457b6e927904273c0189f623a410cc5841538d86a5586be98dd2a4454ad95250687037e5a4452faed0228b104451c60a327002253499a04916a22bc440d23a29e502c973612077b0dc71495d497591d20247e66ce4cc8811d95b70e13acebdca19ceb95b39bb71ee2a39b371eea19cbd70ee2f39cb31cb39f7193983e1dc2f670098e138774a6ff4964a3a231c53492a4a4464072db2487d9bd4b7377bd4b3d763f8b8a3bee5a3ca8c10f69a1fc99dbfabd0201a837e72a2329efb915a414f909b46a11405e1686f35d748919985dcfad75eea40425746f0440972f0021520219b30032b5b3c61cb4151184aabef2e17b645f62625ec4bce7597a5b776425d59c1e076ea2714d31076883053dc65d8181e61a853a872425d4131c1cc601485254461415d4131a198929292929266e8ba30355e83c60f0d9f9e5c8dcf1a35eed3388e6440419779b81dd9027f66df0cbb8c165b86fb74d8f216f823c3531df813c363921c7b3fd97cd4816194d5c48831e71e368d76ee30b933033bf72a775ae7cec99d9b73ef260feb1c1e89ecc8f534eeba63cdaea72ea33a511ba5a56c3558e6e5e53678a4916f483073f4c56b8bec914dd1a091c7940caec16348833bcf603b45e2dcac109efb289bba8c8b7aaa42081b51aedb727a47bb82239d391aeff7130d2c43da51ee1df7efa562a132ea8ac469a79630357a566b65c2d294a556540b27037e0a47d411aa0cd94e768a3d6aa776429232a924c92a51ba2959629d05fea09c5e3766c38d73e73010dc6f5ce6186ecc061b362e33caa9e56a51aef5da72b95c2d897a42958172ea4631552963d725262c4dd2fb3fca81acbf62b855b270911eacdb40338b56b0e2c85c1629934e36e8290c9232998128199232994539abc791ebed76b24e948662b24ef606f38cafcca83ca2922073130a0b8a09c5c47a0b8779a43698fa7bf1a51287bbcbad36ee3346c7e199256b3f75192827541967f44ca867238a08f5940a4231a586a0a0986844444a9c943c2d095a32d4ea30ea274bc2596e71461300b8d72173e12db8806d60c8c9f1828d1bd74a911ee75629d2e3b8528a99e5c295b9852b330579d8c8787c06f60a55acccc24592e4faa644ddc4c27154e0905ae0f404269c904216a0122831050b2170c22d0b4aecadc5b647dd14c25b2aa9be351e915d77dddafa6658679ed97569ae4d5994d225484b9296282d59ea30a6b7c4ba2a25bc2521b04ecf6539033382903fd66c6d61c6195b985106ebde58adda6a3dd5baccf5b4750b0201caadfb740bc7e0242765eb190bdba29413da3b95d4342c5b763383a89f4097b6b002155ae7ac8a037bdcde6ae714d2283c1a0ae1489f2077b89025d372bdb38ceb564c89b8d605292ba39c6c4d44738b2d6909211ced8d8b6206454a68a9a4e6411eeb7c128ea92498eb6338535383a9a49264a6c1a39dc1a38de131c4b6a49e51e959e55a5aceb550a49eb584b745b115c1feba5fe7148e16a972282ca82b36149314c211c58485ab078100f1e86ca9489ce670ac7e662e7338f663ac99bb2a739888aaca162e78120220e4e008482451a20227243d81c45311a4112d95744b25b9c0b87c823f30df71650f5cf001a55a509633c8920b1b95fcc91ef7377fe714d6f827bb3242b945118e32534d5d411dc99d8a5357240ea65a489c3eeda29a504c3d9b791f75a56735efa3b0f48ce67dd417a8265418ad566b167b7dcded19ebf534b7b65831d3cc8d5dd41554ebe2d0ba2c3ccecca243284685d162b330aa09d39204d1640280fb9704b3280bf5451442d7c77662722aa3674f29a614d3f5384a398ec5a23423da7d282dc6baccf82d4a69abf5164e59613db59485a59c9325175920ca32e72e10b5c5975720648b2f3f42e5375531d6639fb923509e79833f33add3f7689d1e2873774c9239d6a5615da04c8367939923f6458edd87ce5cf1b11b73a45fe8b9cbdc1128cb5c823f312e57cbe5aae3119955736576d59ce36a28e5326b488b1c77d69599885cb91aa05c59b888976beed3b5e63e4d5d67d55c823faed75c0a25b5d4625fbc292b3258b6e8aa719f4e55e91910d4499516957a068411ed29a5165beed0b075a2b21b1f69d8f808b3b1a911dad0a041c3023af9af208f9bdf9b3beae49bdb1ba5b948d9cdfbd689d268bf790b8ff6e6376e6ee392d26ecebac996dd9cbb2f897373738aadd3cd2b76ca7d1f7a735d7ee38e34f2686fb71a3795347277f9efe823c34ec3e512e4d1b9834238a6926077b158ae8e8c907597bdc1dc972dae8045366d218426b293ae4442c98507ace49d2cb9f0802d73d1812fb8e8c015d6b5a8e2c9498b2a90b4a8e2284f284d93b576b2929798042d25f1648f7ac6ba4e663de6ae7f1fc803e62fef9c42ee3e94bba3cd23119d6c019dec7aebf49f4a35c11e307f197532f7981e2d3ceae4d6650ebb52e2c43ec6f0a27d74d1f948b3f3d146cdc790c6f3d172af786cdde53cb7c51d1b99bbcee54ebb1c4ee63e1343e61ea3c11c1e5b231de915b9656ecc1db9bbceba40c0fce516fc71c132e430119d0c8343abc8064e64eef6e8e5302c14f8e322a2935fb2045fb03d6ab12dabe6ee09291ba5a19ca8ac9f72e25e57e61d3af636a6926c510d3e4f51c943c0b2a48a325230042894dc8041962ba290820a9aac00e54469d4669d8c68d4465d98b630c535a49d8553495c8b639d7685a45016ebd4f6819065596f0181006516fdc1c2409cf8984908975a98f5d65d44973a615d7a534aa95136e5ce09e1686f497936a66c19e594671e5323aa8cdc4fd9b819b09411e5949d7ac601093a9232896493a44c661b9020252993200e34736f5007da756838a683ac05a1b27e0fa1062350620c32aca8bde05493355a2a89d274b00c1884da9a106a614494a516464334b2d4a2680699da3c20773823da1bb4c1cb94562911cd087562338522f72908648eee2c31b5b5d88d113e4b2eb2f09447d9447bc8da98108698da28b52d21fcbefa486d39858550823f6a0dc216fc438bf3e08da1c579d53d3206ca769368c6c0511ac6ec1642b905168a88e4165880e2044aa647948a11a664e899041fdc58210c7267ced0e18842d19a290a8542a1f068b1ea1d0ec34c61b04c3fc510c6ddc2dc2e1f41978f2df985ee38ff2e7f4a4be170e1f23e94d6c2e57d04a14328ede6f23dfc10041194a6bafc0c9a4394667359e3f29388d2f0e5e76d1e4da49994e3f27369324d2c3397b14b99cb984bd8e57be8e5127519d2e092239437684225cb1b0c65c9634beecba62c6fb08417b9e52f5c82b3c890f0ae0f417df51fa3169804872489993927e732b01c31afff70393ec1202f231af531580cf715576f9099960afcfdc5b701c361c046dc1bffa9363ec120378c6e6a18a135bfaafa96cde935326fa488911dc2b37ac86cf4ddc89024e25f1e7e4812d9618e9acfc9b9f716ae117a2166365264486c079ad7bc061b219ee6b286f321b11d6aaee33ab0113518fe1304e7130c0283d10b305c97e7dc9877170e54fb28cd6bfe4d7007f134a74364afb90487bc0c81790d8ed1a70e548bd1b30e54f36e101cff725c9abf6ecdacbd6256395fe5b8b11d5eb0bf70ece56a158d161aff725c1b38b6438edbb80d6cc49f03c77668e978eb756fe0d80eafe7780e6cc4fd0bc77650fdf597eae6ce9a047f8cc0dbb8397e7365edc67ddddca7bf1c17e79dcbf7c29db5972c95f314ce8dede0f217fe023642bc0b8eedc03242c759aa178c3e30c80b475dc74d220586e73cc911afe7380c39f88817962dc3d851f27c8ef0e191d1eb2f5c9c1b52d93c8eebc2d5915d1f54362fde9b3bc4c6fdc1e6ce1a77da26d1bdf3771ecd799a3b7367d39d59a86c5ee64e272a9b8fb9f389cae661b7616e0f51d9fccb6d176c6f8c73e189a5b61ebe8567508c3eb64378d6c3b3b04865f3e10cd282e712ea2b3c99828038081511544671900fc77640fdf32d0079dd42824fda4818adc33da5394ca57195dc484a5848597fb45972b691969ac9a99fe88d224d4a9928968a54b1d4244a93b226bba9b334d252bd55a26aab4335e8960a08aa7891852d787045ad6fd0410a9060d2c1192ca042addf48fd4e429238434042f298086b136d0e08df0c4f7d53c57d5fea16e62e3297f90e8b396cf5f05b5dfb960bce5bfb983142fb0abfbaaedb3bf6902df896fbb4b561181b00d078840b8500ca2c8c84465ee11e72cbc39175aee582afb7e0b1c8f3103299f5964f1087f02bbc3a78c767f02008e2980b0f0028b3f0ea2d5f5dfbd71d59977675f0746557e36a858f70d4c6dcbee58e2b7c8493391873f92306d71665f0eb2d1e3f5c5b84b5bc7e8237b4e0716698bb5c8237c0dc05e68e4764979683ffc0ebf28f53d9ebb23ac8b9dcb67c7510c6e5f59ccb6dcb57211eeb8fd0ecf2b1b698c2235076c1a9d7141e67b6a98316bc7d3d789405adedc2587de6d55bb0ea0dfe48bde5f32da9960b44eadecafbbce7dd3105ce7bde53e11d7bc82108e2914606dff2178d9a1a1a9a9999584c462626060683817979717179bd5cae568bc50ac39696d50a0455aaef4ba550a8173cda0cfe75976b738b97c1cb7ce4720b8c0c1ebb0c1ef6982b250e0c8f5d6eb94bcb250c1e6b6eb9cb5f6ecde05fdcec75b0f5f02d7f5d29c5163c7ef2e1e5de5afd3b6bb5fa046d985fddc33e0d4e700040ab151e675e85a72d6fb90f5dddce2d77fcde815fddce52f5f159056220529f1f3f7c84cb29ec5d8679fcd8439645bc2c64210b4df948fde19263e62b88d23967534a29a54d7b40ff165b9c728af3f38a36628b3de210e99c611fd6b7397d863c4c5e5a2524d47fb529f87a91d256af2d3772398e16ec62c11be35637c6e15802c0afbe83cb5bbec3ea21360274b9ac7dc491ebbfd59552667fb8acaeacd557eb72656dd543fd0ff541ea875410c7522e9f4c333fd007e186e40e4f76793de52a101fe7820ca1b49ad77112cda10f5623f3d5d75bae1fc55c5fc55c2df6d95183b4583f44488b4c45a20d77090659d9205476136d68eeea35e1656ecbcc33b1bbfc7599cf5c59dbb1176292535b7d28acd1faf253f20c01cb131b0931096bf4f3237db5ac3ea434a1a19c1a3d93b0b67abd3707bfc005eadf54f5d2789a1b7bcd753905ed67ac4b0edced815e1d98ecccf7991bc442113f6cc374c92485906b105411d47bc8cc3d89701a89a57090984b10212198bf2ef472997f54b57aae2de47a593fb5086dec331fe2729adb203577b946f4c1c7ae11b1bbbc869a4b333399088158eca33e76a5a0ee728da0d766e9f2d785b8fc45ef728b85bc2e8393f4ea2e17fcebba5ce6be72a05f5d08fdea282ca40f9ee66f66eeeaf05aad3cd55d5eebacfc8ab9aac3aeeb30acbbdcf0afdbd2b23a58875059fdaa257c0a4265556cb1bed649df7701d97e31a2f4f556d2ecad614b06a26f6d18d67b94a323919c9c1cb61944725c4ce08313e450e923adc8e6b0510d2de6743938b098638219cec3e6a5ad5ce883562ea83a85f52a1e7a4631106cc471785e944ebd03267b57792a11e627b9d393e97de48e9729b541a61ea553086308423f84de87dec7ab45eba38627844099b3a74a3895d9fa29515986f2ca679b29b5f8128706c99deddb7b39996b91be38a6a22ff60303656993ab6cacb2565ee7152621f4d1337920f212d6a2947ff691cf91d791bfbcc844494003a3b63d4d5b4dcda09aeafeb52a022dce1f5a363f41201168713e48a627326deec6b01329edcb69b941805775ef9893eb3fef12f90109a1d7230297b2fad49532084ae3f15c2408ca438bf395f372fad80239996219c6bc381fcec36ca8e6787084cb94d6fbe3414141b377a0827d0606fb6760b0993f6c5145c74b6e06d5e009272b64e4d8295b486125698ca42397294fe98412f6ac4a9e1ac6964ea8692e4859f3f05044f3e4aea16555c6161e26cf9436485a0e29ebcb2a994a22e41e35f4287e9c6a0b7994e4a961e6689b6b983c3e63e77a9fc953c3e481953c0af678652b71e618b9b94fb0c73c2277a6b4eff71001eb7928cf4b79dee7792acf033d6fe5792d9ef71944c1ee53c9bb192777e63d3c4ea1dc606e30a7af4ae234aa2945f4491b6ee61f9da73752dd287c6d31ec9b80f9fe0e611fc446ccf73c957dc3da85f5ca9a94b5ced1b93871ec7b4cc8fc95087528e5c92db20689dea3b38970b49943cab3462f52da3d8581c55c59a321739d54f454466760e32bd3b748c52954bbeb5a4657bbae4e1dbee1ef5d9de6ba4ee3b62e5413b3dfa1affa0ef3e07f65adc1fa8faae412be35bfb7e5134cfd1f0e52d3f2ee34ae1133074f738d902264e6e08d885df56f82a81445d1a756796e11cafa09fe1829528fc23d64f62e2f83d4d4b45c825d912042b0c33c663fa4affa9079f02d37564f53dff2f02d1712bee5f52dabd3e02433f3e0574f12b33887beea42a6eae1816adfcc1ec8c582842b5953c92cd3b1fe729f7691b9acc7dc97c3aecb61eeebaaeb3a785b3908855ffd5b852dd78bdee5854559414e65540aadaeba50cb41792aa332fa15a54c8494c200a1daa5834fda8c9ccc5355eb955446ab12a10ffaee22ed3a9edc1dd61462e93ad94926617f0cc3de407fca1d8ffbb81a5fc229eb3e76d9cbb5675689fcd179670abcac592ce47b128f82b12029d525689fa4febbc5425257e12460eac6bcc36adf8d79b86bd0bba112f9a32aefa694c85f55799ccabb367f9db7f26e2b913f7bbd7f134c80ea1f8ea112003e86c242bea7701229a8276949ad9ee26c9819751d642d0c12dedece764db9bb0df25d0759fb70fd97c246d0a7bc1c5e487d8885d04b2ae37287c2def5ba6f75bbb7dceee09554a9e54a5548651d97c44da9ff2e843e75ef9f673f6e8aac7148dd3bd52d771f77e36ed2a6c340701e50ed2f049f544048df920ba226f268739d7d467af464413845cea42f2411954fcf395fcf49f9399ba850d0a20aa18c93a516454d3ab2dce28c23647beb19ec7d7bf47d1fbd369f91e5952cb970e2084aaaa94918d6ec9c761a4e42fbce816a639875ce731e9c2416839ace816a232cf78d9d9ba4e73b68e01128d398e00f77c35322747911a810a2d2548333bca8618b1a5ca185119278e203f572339fb94f7baa098a7804cae22bf85359ac732c168b357365e62a9de1b85c6bcdb5ced43bc3e25817fc11dfd1c89c6bb1f0f8ec7a5bd769feb96e4ac9f53195e47aeb52e231ccad11965bad73e04f2b5567ae8fadd3e014959452cd2ddc617c4464d7290d75e2a271e11128674a5b2cd6b94bcfbad43673701df8437137512788a04ea8134a5b54e6d6e5ceba319999149594528be29394787a3aa94d16c58b7a728228cbb2e4c20363e44f7c0c8f3b72cc4663b1c75cc6fe12cbb1b3625e64625e645a2c168ed940cf7a0f7a56abc5a269d1d8dc756f6e582ccaa2a134766b6c286373b323c35e03fb0f0bdb32765c8fcd1128bbeacc515f670ed6632d3c1ec92d7a16115576a59c84ad4fd69c42187beb632a299522ea59ac2fab9d3c33f6d6bfd84d11a59cc462f7e91ccbe391fc7a47d558dffa0479d41411558ee114511821edeed416b64e349ce70dee30c377dea0109df3e0242e3aa8e9bc4121b4efe0243d5dd89c6ab41f568f9873d896a1e32d11d2cbd02ecf75eecec77369dfb93a6ff08697bb1eb3a102d1fa0c5db17a3aa57563d54691a410a6945254e83b8fa924eaa4c51d68b8ce25b8c30ca7e1247389075770aad12e41213bd7c1495a78426de71214c2f324530b255060aaf1dca725eba9a430266530b551272cae5622483995f4199a8a2909c231651bad53eed3f09d21339c47e7ee5c8209a09d0727a13b60e256a3ed5c9d53f087f56ff69474d5fa3873bf75592ceec6c77ac79a55b42b6b3a343ec3a53bb8c2832a41351aae640a0aaa7d13fc31a29d46c30da9ac3fc33d95a5925255326cb44ed6a96715a7a8d8327ac6a56cb694cd072e60c19522145c210756c0420928ca30d241154330420c87c7578ebdc34238525bbd79778530f6d86bc44e736332a7f9cc2e372673978bffb8eb1a0a3f1bee86da6c6efe4dd0e57af95dd7868d5495a59e397149f06706fcf924eb3d5a2ceeb6eecb25c8bdb85e5da8325a6c314525e6c228a7165be61af921e2d85dfe43731f1abb374e7365b6b9a3455d4131b548a7dca763ee48e485727aa2345415a69ec9fc1db5d8236a8a12e188aa82b282aa825ae21ec617614cc9598e724c965d64e18e6a11bd75a678668a8972fd77d6e958333efd18738a5b9771cdfce5e3cd6962e2bd94171f8f568c6199d3bbc4bbfe72658baef1e5f4a924eaba8da41a49aed859b799c147b83c43735b528437f848e7998f9fc83acd1bec01447dcd331808171e678e7de60827d30a418831cdc7187ca4b3eb3478ec30b81cc340b83ef3c38581a8e7f20ceb051fe92c730ae2c0e5eaa2d4899983dea75bb73bbdaad77dda4915ea2465abb4b6aaabd63ace5c5dae225475672e41dbe469bd5227ada36a8c21c5e3112ed38f1d468bc32d7aebbb89da2abd7504aab53ae9f0386766ae3ab5cdcca83ef30afecc6019d22a59f54f82387cd975c7995ddc972ba64e74ca466934aaf3d6ce0eae8147ab832595d5b80d95d99c06ffac7bf37b73161e6d328ae9f5f42d17be0f4d25d978833d6297f9bd33a3985ab8e298146f03d7c036f8ab2d639d7591f518d65f2e733d90c7cd59bf97b931548b9731a87b9f56fde27757bc3717bf9b7a26b3ead489c923b3ea923ac9bfc7249cf9c73f77e7b465eefe33b49fa1f8abae7a0cb51173991aaff1f2fb1b9bdfb0de3a8b759b31bee858d7757a471e32655116a52c1c9b790b8f311c6267b5cee131f390b9c7defda039378343eb2c1c62779d452b8ecde01f31173ef7cabaf434f8e5c5b2aeeb93f562635f5e6c6c6cf24b0d9b6c737c9b1a3117efcd8d1873994bf08698cb3c464646c6938991b9784f4694b90d196ce362db38bd36623e51145fff7d4c58c73562aec2b11f3ff7c7c45c85c3277843ccaf523d2676d71d6d78c8adca7d869ba1b98c0c9e99c1232cd79997179acbd0ccbc72a5a1b179eb33333777e1b76e779b3b12b1c9acdf3c7687f8d136fc07eb2a3cc69cf5160e9d1be4c1c2e3e4aeebf5ba3ad73bde3c86d6591cb669612062fe8f3798884d8ec1aae3fbd02f863bcaa58cdfdd91878cdf027f446bf3fff5b3ee6893594f11f5ac561a4939eafa98247378641dffc34984b5454f33335ad77d1aa5d433171e93645a730ba3a8fcd6538c52625d31af7e747ecd58f6cc2be3cb5665fcdab414beec19fdc15886d3969328ada2a8d4918714d32bb594590120a8a94dc2515a610222ad3025f9c11f237eb047fd44dabf1b4e40f924ba3b9a825ad633d46243c13bb05059233993d7769cd132f40e272a6b2478c7d3ec3d1fe011c2e68408caade80869c718fd1d58bee8d98e3076641112645a8194652e51824444c10d054528384201929426294b3a3a3a3a3a3a3a3a33cc30c3cea71548e2c530dc033426da151a13101f3a96966080012789b644b3421b9a39e9d0a143870e1d3a740000000088e13a96742ce95892e13849384a384b3856709870aee060c1f902a709270b0e0e721fc706325ce34b684d453ead2d421ba3082d4b5e729fbeb63c99b834a7a68dd12211c639a238519a8673d4331e49d4e0b304078e25356c38379c221c4d389670707070707070705e78e1851c176fe24dbce5bc86ad0611a5d5a851a3468d1a356ad4a022f76b4cd1420df007c93f1790fc43726ffecd1bf286122477a435e1b001fed0c268b16959a692fbf44743d5813c9bd0c2a065699910cda965688944084896881f6dbe117f531334b4c4d6a851032be123994c2693c964b2165a68c185d704d504d504b980c46d6c401dea7cf24fa6949c80d3e43e2db18400ab3890a714570a9287e036f9a2c67c8284d1d06bb7a2a32848539294a8c8fd9e2990dc1d585abcd2a21c9ac20aed803d355982e689e68c9e599a9b8d8d8d8d8d8d8d8d0d1b366e1cf6642dec090b0ea1db007fec6d803d6afe709eb2bd431cc9a71293e8eeb8d2b21d5fb4b8a309cc7309dec1b48369a41d61a710949edd7a8e7aa2f420f54cd1404f4fcf0972bfc78a1d67344d48ca10489e86b831e4b89c604eb024d8edffffffde8bef727239b99c8e7ca4353511c593a18b041317875824e61945cbc00a222ba05871b3e2e8088bdc01529ac9c9c933333333333333343434359f45479f0b4c647b6d6e9c0b262044408272e4c831313131313131323232b1f7606a7211fc994b34195330b480c42a795b3b95f8f454f2dd504da22b93c893896b34a38802d3c26882694d53843db69ecd257a369bf40c09115632033ec19e48300b1218b4b2b4c66839b59e8ee40ed0ebf57abd5eaf978b8bcbcbc12ce01398c55e06fecc25489060a3219ffe44a13c85aee511da26274b2c13d8048601dec021168bc562b158ac56abe5ba6dca0fc3278423ed03009470dcc124694d3ddb71a569130a1692a25ae2a290c873c9ed81d262cf8dcaec133d362a7be23842b887c8225915582c248f1048728708239ea4080d6480d2261413ec91ca7389b7a5473d3b62662791a7b45aad56abd56ad5d2d212ca40d28ec023c489e89911597a9684e6442b83f62481fec49763490b44443956c80590071da24374e88524c67eca19278a111089a049044b44881021428408112215a840056e9a5c54b6c02708b280000629f96b214a2735a9d25190a8ac7fd4e2909213e46f66022a6212134a493625fbc30f4fa290024a08443082dc07bad1c90404126732796232816232219a4c3c2071261327244e33f9c064e26432219a4c8826935b951ba5097c9c4a4c24dd6892c536d4012708400002108000042000810844200212f86442c9874f26374a9b44b69e1de1a467f648ee1011a5674620f52cc9949e1591d4330d50e95906942693a5c9e40456d0ac8872e3682a196232641ff00033c030c308661cc18c30cc4082196298910433b21431842288504411cc682a22084508a1082f8af8a288a6229accc832c50692033e4ea43c95d8042131c91da0a4052c60010b58c00216b000063080010df85432d4c38bc852049622be28a2a908a722ca28e2a98833920425114a3294c466461472bf0832748ea0d8104d8189a80826cb030f6458418609c838011951c89042060ac8b8820ca422252852451123328e8a4c51840445a8285254e4a8c811194845356e0af8386f432c61244f451015117480031ce0000738c0010e3064c810047c08a604bc0852915b91a2224745928a5029a254a44a91a522568a3015c142060c72bf880b6630b2b7c7498b3d472df644693189ca7a94a80c4a135148d1636bb1a5c03d4854b64411220943bf27a96748a8346d467104a5cd27412e8378413368c3326d4b3b9eb0f46ca220a96d5ec8d944010c6ef9c9b6949bf60429a3250105309033da11a48882a49ed19a5a7c02cd09b426d0c6a03181b6049a1268596849d8f144693b98284d0c1a126861d08e4033020d0c5a93dc994bbe63c753a695419b42eed39c3e6b7730ed709b77f868c520430c28881185dc372246196248418c29e4fe0ca242ee033d511f9a20717c7082c4f1e1096238e55e727d50820f4bf081093e8c61349413f4e41830828d86a612f643dc21c23c042c5b6c349f601f9cba6e35959ce80ed9da24fa3755473cb93e381189f134950849994e2e123c6d0620629330f40ff2a8c94004d114c4520e39e490430e39e490830e3ae8508013213a7221f7e189d27cc8e2c3183e384d2068024213189a40133d2be2fd09d87aa681f727e0a46719787f02449436a1787f02374a9b4f240189dc9f8012c92344cf92cfc09fd9040992b9e412fc41729fbe123c83241859669325486e076e924fdb2de24557488a0632f0b56a527127d12709ee7cf2398511179a2010e2f37685a4143922281ff10e5c231df84ce288fb70e4830d0810204080000102e4861b6eb80c01ee802cd6ce50943f1d54f010a480271e8690a0e1a4a3ba03821c20e4802107d8286d1201f9f0e1c3870f1f3e7cfcf8f1e303b8029e3e00a86693968114bb065c48595f4182dc2aa0413505d1dda189cafa76c90e58a8ac9f3cc13d4e5a48ca5c7226af49994b184d22bc438f8dd284a44c26f0913e6e8283984aae002c0af842014d0ac8a200270594d13323bc8842ee2b800c05102920e8e7e7e7e7e7e7e787070f1e9f4a6e8fcf25b74f20c5be42b60aa40cfd1db0e0f030c4e1a3bde2a60229eb5fcc33825bd18d8a2c7207a856abd56ab55a4d000210c0547251b986eb70f405408afcd5886430906247815b0652d66f81009e5c5153cd24ee270027f9e3a1b237039f4cdcc981cf28ee14c127d19d4f7c2ab91af85ce2ce26fe0128a0f86c728da47ec4e6b9814f2897c96b52ec27933b8443a081cf115ca119c5e7937f93e8f640217b46c0816b3404ab0931794d0a133f02cb48869ec4a54c259f1bb84d3e95e023b03c97984ba692f78c40ee08f17e8f087a42d00345ee1879bf470ab93399bcdf1385dc19e2fd9e2772672af17e0f91dc9942eff740217790bcdff384dce90181dc9943eff738913b3389f77b3e2077e612eff73821776693f77b3cd0632b9a441f7baac8fd9e5bd330897e6bb3896ba4d68490154a9820f7873841ee4f265172df8894dc3f820628a0c115b92f045227716b2518bab52a98b8352389d347726970945bc9ad4d21716a2468726b54dc5a914a887bc4351acaa94d298e816b34a4539b52dcc86482a5f81442f2a9041224d8684a816b4792062b90346841eed7b09032f4bb55ea5d9e44b77624050d9072a770ed16c595c24392e70cf2a8c93c9e244ebfc751cf8e78bf47941e4894369fbcdf2389d226d1fb3d94e40e11eff7a8d23323deefb1d4b324eff7b0d2b3222ec447c9450d72bfc70c248f10eff3c8c28349eecc202448902041820409922186f868f38e21be0303ef79ed76815b7b818fe1054e83db1243495cdc279062ffb3413589aeac09117d2aa1e435a43c93f8479542db4ce2f638f594d1b2b1c78681c33c75fb48e2f4a792db57c89e44b75d41b46131301a725a56a0ea7b21e5420b28ebc93af186ab346ca03d65cecac887aecc53c9957932b932cf105c599b4934c953e81acd279f485ca349f4b904cad112251f694c28784a81a3c041742ec1415349076520a63c7fc4952d5a274f88888c485284063200c5134b97080c0c81e448917717c2081e8378f72257e620aecc17b816004196dcaf00912888721f883b7eac58d89a6013b8a3e8c36462cb7d09dcf1118000134ab9ff803bc21c7047b10733b0e47e03ee78062c40c950eef370479802ee289271fb90034c810966803b8a528c2cd7218731886038dcb15b90fb04b823ec06206064c97d1bc0201a8f858f3b561bf0f8f90196dcafe1f2e028f70570c78a456d8ee096fbf91aa13e778405e08ed50a1a641741b9df73c763c173c76a033b838e1548b94fdbc194fbb33b8a32e0248d3000e8d0b194fb30e4d4b0c15ec0116fb98f6384b520ab09ca7db1e786d694fb37ee08b301833de9e4b1e7ba5c4eb9c71edb68eb9399597494fbb13bbe62516d9047c905d3d832c87d185750eebf802ff069ac56e4d1daa62779a447f4a8c796fbaa3bc2f228bd78a2c16d0ea140e905343fd0330633e09e70441c141c110785c4e9cbcbcfa7458ec3767ade9116f35c80a449171a9b1c4488dc110b9083949426b3c9e34c86c9e13893bdd0aa6c8b1ef8638466be5a5badf53cef67e2a091b4cfc81d6ea6c730cb6ea6e5ce120f9ab9b678f2d07950f20c60e6983c0498397a66dfcdd9b536c8f1983c03b8c116b969f55412675e0e60f280920093a7678ef99e2d79de6066c9e45d52ef56ee74f66ec11f0f485a256607644391fb5d734c70032d4d99eb543170617058284d14bd8f5c166e8c6c3d2fe57d9d67adb529d50ce9bbae9bdc39ee8b16b9a72c1746e6ee7160c81cdcbf8e4b3535f52c8ca609f166113c95bda8a7ae903b84bb01640eeeddf77136fbfcd873ff217980f8c80117997b0d72c79e0b5f216755b6a7056285fb0c926708db23797ce60e397e798c21733c99bb0e776f730e36138430352477b0c81def1cd7629338dcbb291c534d9449e27041ae9021930701320777ce8bcc7d9434c81c27f6ac3bf745cbb8731de6b0b4c89dc3d2d2d4f5ca0aa6ec7d86ddfd31811176b9f322941c1524169206977befa2744872e7bb2271baa4ec5dca1917852e4af65cf0e70329f3ee7d43c81ee794bd30b2778b472e0bd9fbfc985af4de0921ec3eca2cdd92fc028a4de866ef1d949ec95bd382b467a9ccbb87f2d1a2f75427d4b490764dc81da3a1732b088d5c6eb9a57024a293b9b77c94597e5adee00ddc2718a14b965fe1d1475e5d75479dacfa376f337fdff72e08e128b3d0fc3acd2f8ee807b9f3bad77ae8ea96248e4dfe70ea49e26009475965823faa96bbae91d9baac6b648677ac58e4d525a293270f3d03efbd879eb5dc7b047a36df75e00ff809651473cb5b7509fefb52aa4f25672aa5e3d96a8faa525a69abf7092594b7b332bb2bf9eb76207378ff3aee6362ead995a611699977efde2c027857ff2e91209e90bd5740e6f0deadf00f2d7a07af10baa52c462f4b94f6823d9b83f46cdebb01248f77040c913b2a4f02217b172277be7b36e760333055abe6a7faf25743cf52f7fe43f20c616f903c9cd304c241c19338c8de7d4c90072aa7fea56e4e0c39dc157104214c3dc99d25b983ba473d9985297b475d9a45e2783f5c222d7a47dd20248e5744e6f0ee3d0a50c81e472471bc7bdfd134977bd7e999bd7797db5969996771b7d4a2f7f9b15b0a9956ed7241ea26557684d525aa83ccc1907a621493c4e99ed59ed5874153369dbf6559e36b91b95195e2ce62a6ca7dabca55d2e80394689c95a294751714141474abd92cff4d5a9bfb3c6c4e25285a9cb1d4822721226ae68bd0f790797e5e1e3de7a511779f162716674bc2f99bcd0d9b396d6ce6b499b36d6ed8d0db48b1a178a4d406cfb40883d10331f16d60184c75e3dec6471b366ca0d9c6c6c6868d8d046df068f31564a0c0a0ffac97e9187393e9c7eb4919bd9cb5674f3bc159bb0e06b3b6bb475aa4e72e112dd20ea2454ac4112c84a328f69b9e11d22c83a0346b432122a5ff3a8ee33814376d94d6436a42c9b4071e86b4482916e87df49f7bb0921604953bf5948a31641a84b56108cb1e364a4ddb0f75de521da841962936283fbc8411ce3c2f3fc11b669e5eaa7ea8d48b14c7a920764ad588080200007314002028100c0884a201894cd12349fb14800c86aa5a70549aa8510ea71452c620628088000000080824690800a0172abc01d4f13fcd2e40017622956a06762131a3ace2fe18e493294cd23a047d84a6a9c788fd62a3165423bdedef2182ec1b29ee5aacdc15d93ddb12f9b860b489c1884c32e1ede0a140dc42e9a326d317653c555c0a1c16c1a99a5e611d433515e9efe5a184e92c8653e72ba2be26aa0ada6f998430933f39483e53d569823c4ce01a80a0c63bc9cef0ab43dc3e29e359111a3727a2bb47d5e55940af2aaa0535dc35c51daa2d19bd2df5759c2f661ca84b99cf4a77c25f1d72fb6999e76ad8cbb7b14cc0fab6e6e6de06c5bbedce05a870d25593fa14765bc2c2a1e614b4996dabfb9e1a9324af8cb739aaf09329787d99640010cf2c89cdf5ffa2128548023c43c39d40a658cb01dcfa8facf0ce6c9c3c6bbe35f0afff705c71944b72a69f19c1aeb98d4c5c327a1fdae26ae96805c9918321216d14f06d065f064414c0c5d12807e4f0b9732b4350e7cc4d57ac3c62c5a67ecac68510fe3324e6b8c1dfef134203094ad0f56811cbafd9d41e97443fba82bfacf537f05761db250459f0f2850869fa8632d09283e8dd066089c1a6c78516688bcacda1fa76beeb1e4610d9b1880a9d1365ad90ccd82fcf402177f3195c53d24392a8aab07dec33467da78da6eade77483194b523c7d445210b8b3b3725e2350ece590ae6ddb4e627a90676fa2dd4e2a7ac0a0a9be0e20200d3a10d58e13f00e68af70ccb525bac604a004d79f4060ddcb15faef2864648d056db064249d11a61a24b70dd482f90706b9411c28d87534cfecc7da3a7636d032a06bfd9fba2c1a58d6814761ed9678e51620c9fe9a8cc66f7ba14da35c591c3f26c05568841d1bde3c71fecff0bc8f675c6d85f3bcd4d0191c6f4b417e54827d10387a8e07f3baedbe0beab832ee6aa1e240257a9c3cddd9b7098e3d56a2f7fa7556ed200c1b31b06404308192e43cee30aa71a971cce4d57b77becc60ae4f829b259841ce4b9b3547e0878ea7478a59d11af1467249f89d2eec0ad40c72298b5b6a464171b04bf98cf382a41dc6832ff4e09fe4cdb267c6ea66b0dcbbad575290f7b9e010732b48403b93554e885297470131e3e425f0874354e781c777242a89ad3badb618f0bc1dd16f5fbb87c5b4851022276c7d179b451eb1aa1fe08e0e62e93331a93b37dacc3b2a1b8fc6f1c70000e509302774323e1a60abe2a1c2e225d18826c244dc344fe5f0deedd58a8e8f0bc9593a558aea28233c2afa68c5b2682bf2c7188fee75d256bda7005b1ab8b373fb040335aa83b24e120eb671d5382cab9e250700992ebeb4413cb176bcc2ec88d7e0108d64a72cf0deb04db00e63d7f00e3c27074982c04c47e1b6ef482a308ccafe0931936984371181e331ec2c69a68cb900e99d261d60719209d9a53a4f8e728e7c42f5a0b9997e278944f1de1b639e96fca785eac267762fdbdd3066e8e03fb029ad0302790c15068c1dbaa608dcd0373b5babb430202c3500e9fa040f92815453cc43a3226b40e53b0af0d17e186080ff2ba281086e7ca5f0d913a27575ed14a15434e5fefb8d24ecfbabd38715ff44322bd1fb89a7c3f3f02be6522fcddeb0483afc27da7308d8d7312c0ac114dac295f3704a5df5d051f238f692bf2a1d6df6c245bf2c82da520b363b0911796313523511a0e74820d9987194febe56ad54ce611decbaaba2f01c3a181c7146d19e9e7b85edf5849e42a7104ca3164df57e3eaf8159cdf795ebda33afa090e05e49b66e4bfa96273e7422d83608e2bbd45367e1325f52b7503b2aef9c08d2ad5819202dd20a3d47a3d6538a813274f71b2c3d0b45ac1c5bd62619941d2773fd94ccf6935dd575e5ba714dd564e7e5f2fea1098f131f0e97f023ca5e7a3615b6920e38233b21718b8c357dd4479f7f99fe1539e690aacff1b4a2949f4c26664d04a320a864ff35241bad13cff9d0abe272e8a15830b38449eb890b1316d7401a1ef61d5324d42cb2e27585cf73d95c09b82a6e23e0e48e911beab307d749e88bd84b5a79f9a8cd3f9ae176ec4241d77d59ea1e6eae97b284ab2c2b48980c28052b70d4a30bb1b00dd2a3a2830c29dc4c65dc4e6dd450ef5d5c3194860d7530d58e38b1e89516006cca24c20719edce4efebdfdf482d530b37296b9629cfe99d936717c9d917c004cd4c92e8a63f89c3e4c05d1d729d9313ba4a945a9ef64917a84b12eea46b189ef960913abecb6d643addb6092051b29512dcc9bdcd5457105a351463bd234e424c0d8263c4f473b101b5d20749942e8569fc008580526f2b54b068f78f5f9c27b21f22587d974b46690f59152f9873d8841703de22271b3770aea08407c0a46cc937586e8679eb0a51f47fec7916fa2e3c4a24301d725147ac72ee8c5059ea93577fd208ea3acd08b976ee5c9a72351f4ddc5b485868a8797f8bf486647d4f7839d44e3f241d492a8cf8510a018a9d56696ad0ea324c61bf0b3a407cdd1a9688542d72537ce8a700085f6c20324eb6373a8e4589679cbdeabf6a84fe059b1c2627dc8a10f6dc39e19529bf7d5a06eeb2a956dd05232451839f8d346c94a15d187491d4e707c51557b324bf9acbba175b7b4a56c503bc34e4a31d215e837dab95da2a7ae65adc1c1b62d24a53eade0dff595c02e4cb98f751a1cf96d48c57f19c78349d407e8939ae0b0d69c49864630222b36477eddd69c75333c5be9b9595c9af90dde6b88e1dc56d8c84c7d656e53015dd3bdb39c5dd36a79f896dad9f3b2ca042cdcdd8a66ce8dc7216cbaab659ebeb075f14686bcc468d310910f0bbb93e285587898e397cb4afc8f2675d687531c459b61650a42d89d72efc74b3778a6104351e7b93e4de049631aa0b868657ac6b3711b29a6790c0c58636910d6cc5e840d87adda11e561ab8ed54b3070de3689bb24542158a1d108a4509a549d7a67637b9acddc769469d105e4d0d17d2828e63eb9b7e34f5f10ff11523d7303bedad1ae998369660f490a6447b783c11520ca4e375d140469e510fcee0d5cefbbc2d91d76c0f7447bb0fa8536fd88880503129f69100a63ac00c74c9c279d85c9f72c0c79be4da98139e8c67520db8b28c64efd6960d45e9ffd4872b2a560795c230ed161cac6dbfddec327080a31ab196d9494fc5846d0c0724fcfa6122f500ae4c85618bb87ce3ed5ab2396a33b1302a1db91f4f48c12955102fc0d5851be6e2b986929074df8635f613eb783444b946c38b2001c381bf12127d6eae60beacfe6e9cb1113c4120d301fe541b8451b04601cf959d5fdfe7848b643e9985cf540784e8269f1b197cbe8b38145603cc6d07579e6e568097db20d509e62834b4ceb6db6130f90884aa291afe00efb68019037769cb4c42c72f70165c0158a69e735282a06172ee8d726109ecb7ae00feeef472e11644d39ae46944677402d9240f7c3a9fbe24db1ddaae570b30453f03ba3ba7a3c1cd820667ad903b3f25489ae6b978a8b61d880090876758ea78f40af476370d69063a30f434c299ec52ebc5b94c5103eb17db2db0c28f6396377d6f86a7e13359d1c2c7e499d218ffd5b0791bcde0de1f78d928667b27d426af8db939bb6acae8efdbdfa4662438772e78401e64cc695a5a8bf83dd6595ab71acf78f3d813b8229d4717cefb9e840199fce25d20d5c1f1b69490fa2a6e2f06df73a0319ab1acd4d98a5a0e7bc89c63f5c0172818178def69655e2ccc0055b62620ca85a29f65674848050c225579484a6692b38049433dba550709e2b22b73c03481f401828f61402a5e7b81a2c7fac0cabbd90424e966e819967c4fe4a578d76f02e15d19ea93c4243a7f3c04520f905d0a87dea3649f4d0dd2e142091e369ee14c26a0e07806954e9ad9577f28df70efff9eabf2b3c740d84fcefcd590693e67194f534128d49d2314c62ab8a91eaeaea496bee82f689013807e020c42b189a3a168bfaf0312f8b988185f2de44dcdbeb48413ed60cd086ace5170d9b86f3f63c24987cfaabb848730d81ab060791050b99021be5786107961ea8eadded695c1ace08d7583ea115a76cb1cce364182cb2b5b6404947ece9fe2918940ee3bbd6f1e6f6c5d008da3fee3e4547bf3824dffade5b02c1c3ded508f736468e15d761c9eb547337344f3f05e5fa59aecef68b66f2054ed6a8491b5923780b48c8b584ef106d4fca3c3f79a27a53a1958f176150a3cd39da3419e0889875d068746d90b99c781348b9d89590d2ec3b6434d3256e2aa253c6a5332de52adef232ea4772d03e9b61edfbefdb2ea2e120e5b9a996184dac804ecdeaf1b14fab0c051d256063a814b0ef6bce415c1745950a1e6c0eb2f0dd89fe3d802d202006f2cd4fdccde4078b81de5ddca403bc46101bcd151d05606da956b804748f86ce3568645607361a8592e8cb63268d4158a01809f182e5c0a9b2d8297f887e7caa05786fd64afca95c14510125cf065348dc818cc36eced93b4055e31a420439da1d9183257061716fc32249e2b838eb06041a8768e9ce3d1eeb8ab835f898bdd222f420c5d3031e5c1c4e0be2b43345ca4185c822d1ec1e97f9c4ef1dc518c5dc2c95fad91cf728ccf65120bab666a2f8e719f461ee91ab66a006d0edbc4aa350519b928fb4f2685fc04b80b7c54a09cc2bb8116d66b020e7948c90921de3791ec0d1445e39499e9d24cda43a44aa7f5f004671217870328710c943377a2a0cdc58edc99708716e43b1969155915562216869b4fc6d873af77adea67bd3e6edacfe895617b594098289a51a47cab5f834d3e8277febb15d8fdc69e5ec863c8d56c975abc2cbb0c6dec7be57f4258bb832d46a8dba0e156f783cf8d90bdb61b3aed868ff65d57d211d97026660bb3a952851c30748ae8d50c74828288f72d40987501418d60a9f255ad543bc016f5e8bae928505bed6cc270d2c5f7f41c807e459ff34feaa564bf37f763d7c3d16de8d57f3411949db5938264b6d9817ca14f8062379d91308e805c819e169fe221a9e8b0f217b968086eb1e7fa2d3db4b387c8731cedcc4da6a87ca3e2fc47435d36f5e947676a27fa81814429b6cbca607e27cfb25c4e223de91265f1444f61bb32341376cb6d8a5cc4f21c98550deaa90c00b9ca03f6a8c3216151db727138e483c4f7cb03ba46cec58014f72ce65d5cf3f466e36b188e2dd8186ee277e725e92299031282797034c5156a24ddf024409e552b7ac4be3bdc89ac74a2712a6e975201a24d566b496541c22c131e59849c0a9ce1b2efab7804ea7b043c4c44c03487c99967d20f115e7a6a7ad24e370550ec080b6e8caecd0b9141abaa6d43bf1ed5615ed7dbd09598a2046afaec8f32bacb07c13c712f098188f494409a7f0631e668e5c13d3f00c468e984c6d8933e4b4b66439305c10dee2a72163f30fa5465f8e45fc3bd046872bda18cd6675880a0049e0f037d33f506f45fdf1dd087a16fdc27e801bd5d83a4b031b6e5d1b07056345bf0911595f5bfe1d40232e8886468437ba2dae53163d71cfb9016c516f430fbd699d6424e9bf5e3a5c316ebaa649d3a9b36facbc9c1488bcec16b5bc6b4d1f63c1e575e57a9114a74c545a7c1557497d6e42b5ff38e32b589a00a3072244fa936f427c8bf094bcfc0045de42e641bed037050ca47ca06a5be66e8850d1dbee9c74b8126003b51b80c3511376d347ad81c676393f931243428a20fbee56a35183e09dce065a30033e4793d5cc62cdc815dcbb1d1c19c5326d4a558f7a90f616c84d807ff219873fcacece490d75fa7b5a6930f58a3d55ac182eb1fb219a40d4cb970f1481a07dbccbbfa66a8b04db937d925741f6e3e558fab641d1913e8771658b94ff64182cbc915e883cfef6c1aa792e2267520a0f73750d13af19dcee2df0dc2435d7d23f5002618af9115ce873f936c4d5b60d9605a2b4664ca42cbfa567e5c8d9c9123217a1c709d2fa4b9e5a6e85e1cb37831000837a6eb5a1db30c3412c9a78f4ffac5a216b5717d237cd0dcade4d0d88ac709dde8b9a61433748e1c65b587a3192a89e9c280e9230ab9a0fed944cbd7ea2f638de910a71f83bcda30dbfd8fcb24d0fd15ce8f8cb46d4e36e2ef00040495655c5fbb7b19c7ae1900e689b44e4c7afe1b3852460f0212f368632298977acb52f07509d8ceca6a03bcb18759dc61c4811b0b631bf305654de88c1d56263f5510362885e395949e5743187dc2673033cbca76d74698134f949781024c7c7305f3b8851c29f5fc7073e088a5eb2a07fe9cccd2ab3216bdee609f6a0a91d0a5b7686ae9a33a7edb6f831f10b0d3da199a053243ccd850b6cb5d127dc412bf35f92062041a6209826db917d0ae4dc66b0afe9662f13672fe000fe8fe60e43b633b9f60253fe744b18ddd89678627e93e05fdb0b9f0a0d9902a56f7e4a64662f6a32b5301cb5ab7c664476cf1ed23e4760d267f23f5c4812ae3e7e2dc61bbaa8e89db817159386182e9f70a2c6efe0de3f43137100118576f0069f3228e0974166769380d33d04881006549ec80ffd57b630aaf865e132b785f913f982197f6dc7504c654385f99e1e31c28575d410b5f3c45869eb06d1bee51548e11fed3bba15285cb458535be8094d18a4bcacb410c61698c09335a8e9ad8469b372be957ef2bb2a6163b3a023d92f1d526fc981b0efabc043ac4c339a4f2da0d59e484de9cb3d3bfe2c871e32393e989759afc109137fccc4abe9238235b5eadbf42d64e8ed8490f2cb20bb51244dda3c000f1eee95883c531ffebddd1ff40cac70cce18ac158c220700b1c164b21380c36e222983487acda18529720e79b4d5950bc3d644978409b6099b2f204e9d39bf7e401af50ba3477d4d84fbb949d6f134ac16508f71dc0a59db8338ac791ab88c1ce8a4ef1b3723129664e48fc2e7478d99ff4e0f96228e27a7d112d6e921250adaae2a2618d953a4b5dcafa32a5204b8d9deb64b12e88ff3e0683c62b0a5a08dc6f42f37ee10a885e5fd9a14cee51734e0eb69151ebeb84d818d9e4b92913c98c0e9f89eb5a933db0c5b53e9e49aead822022724e270db5e3e2b186e5c5de4323748655fcaa1c5981ff2d9ad987d4a49b245204971683ed3ab044d73e5658b9373eb5c9c08a7ba74329ec1741a6ba503bacfd6a5cf5aa6bd1569f34a7c89b5c7f2a3ed42fb725a016d34aa05817eb8d23947b508b548bd2d12ff5acb31bc1c5f992e5e6ef2dc546ecd8ac465225dd698fb3881704b10899f6c8d44f9dfe2a010c943867f295294d8c0727c4f689c73674a363abd6e48ba55aa2864d7737096027c21a9c8a6aa1ab161645841289dfd05bf751dccbbb58c27d224c92deb7a5c2f6b97ad9e80fb1a3e04c09471ced019e73f4b4d7190f6d8d3d5b74663f9363d12988594e23a394245a70b6357e3e5389908527d98ce5a3d86809b9022b894686e33e7cd71acea200916459879bc36cf58ae9446ebad11926a1e3a7a0282790cdc091e18a99a3295a0d1668e902d5d68806430eca0ad785ec0259da87f26ce13a702b9c01bc1de7dfc596682fcfed3428de6802eaccf49b75434a3dd0361b9f2d4607fe3f6c969c5fe9fc4160f93f6e2f57a838c7d8b64dfa341350d4f6ce6f52cbea6f8e8eb058b2b4b9a02620dd98226f5fa20fff8f9422193d6d01121687bd114a76aba3a9a8fa630adb1ee80805944742f1564ced865bc52c30234bcbb551db25399101a65499ca8c795ababf17e71c97a925a4d4a5a981ea8704a9780175cb0c1c2da186d08cb11790f5d6ddddcbd1ad95b10b48fa4e2329b180aafb6480f2b74f847f63779b6b2cee22c2ea239d57de0a828a36820ebe19c81077342bb126b218873ffdb96ed8e750fe14053104828003f77e3a6c3dfe011e0ff60ef2ea1acbd7c8eb6e15d257d9e1ab36e71a47c6065f2d20f4cbf74517034b0fdff376d96110fa6b78810e891ce575204e09722801a991682374e3142a2cd67ec2c71a1b38f119cdd67a4cbbd7df5107513530540c983eb63aad9a116987493fe12fca1434b357b32c8dd1e4a9b850f50526eb59b046e99b01b379ec09d92fb66bfd03846b21dac616b3761d6b44c1f0b294772280b6486b1529a269d8e009648d6aae2dbed1727209e9df12147c59a9e90a009ca68977ef3acd227512926e09e62227a1009a6621c8ca8056e41a02d369e5a55f4f64fe063926981f643c9fb91d28fbc15687dffeabc24faf03c3f9bf944f328691e1b9b10cb8cd0ea81e4df4ada67fc85281216c6d100e97c50df501778d1bb5c8450e81b26992ff360be4d21a7c140a06f161d8ba62c4d984c6d836f58ab413f5e59f7a4024e3b8fba6b03b41b95e53c261b8bec47e4002d410a59d28391a03550251bc4484f780a0d7656273d08da63d879a7955f3e10fdeca53f7838dc7acca9cfbcfa4cd7c59cfaccaacb7c7dcca9cdb45aa6d7c64ced4caa657a7dccd4ce546da6d7624add4cd5667e3de6d4ce5c6de6eb4aac5ca97bcc19abbc0701f50770c9eb16d54c09dc124fc30d4377fcf0994e89c00c2e9f0781c50c40e0a84840b5d1a5bf89ba79a70bd43098a8b4e3edf5d79bfdb10ac1641f74e1129ddb060926ea7037e7c0e72ea648fb8dde44cd3b48dc163524eae1c15f3b7487d1c1f1e4ecf27b0121d47faf58346d5d100449c5a2916b4f3a0f3f3b54a6409e01dac771d6317c2c67fc7b2e0d503cf3b022dc301f007edd884ed1381ca27692c221ad91d82123b4639a80c0c58a3f660c1e5f702e80ffbffc5828c270869da36f07499bb44b2fe1e18f10dcbdd3d72542eef34c8b8e8350ca69d1457a0cc814f1ff78d68954d5db542dc05d2d1fd6c9fd3700d52e600e16ad14dba30fe6ea97057c1c9094b976bab5d0b0f2af495a9e6c10ee2496d5ed63e63b728b41a1bae66078b350189ec8fdc5a7882a362f9e7c558e953f0f945f45834d78f1455c0f43424c44415899cfd60f3c8be5d92e54a3d05f381e25c861df5f9144c51d32216a99bb745febf68dea64cae60a41af005839d6a24c1978fc9a4c57c4d99d20f3726a429c6b325665401b21edb2edce3c4c55948633ee2f0ed5e119d8637f1374a1873db9ab10ec11a85b375064bf343b51beed2d4d66eb89851e2175951b451442c22508d5568c5292205fbc88e5e2df48e524b92f5fe58e65b4bf78446e46ddf2771c5e5d703f40d2cb63cb2c44850d4d80ecf25e502d49afc88c6a39cb8ac4ee7e6450bd4a49a62153f9836008bc1d1d928f059f0301099532c6932d44f34a095d5324770b2f01663491e28ce4b2f060b22ce67e95ed5328fa466288986cfd66af63d794a309c2c7aa63cf6b3e52cefd365d1fabeb04959ea25c13b6dff81f6d3eb5265200c78329e29ec827b4d1cd619f971853842db49ce03d090d4b2d9498fc9d610aa3694c884ffa614c3fa521be7e02e76ff8a271727bc4ad859237adb178b8678175e3ec0530ed133e1a17656aace16926475497a01139eba9e0c5ff22adc17a5cc83ea16863543090ebd13496aff468d5c5b1724115232c9cb0b206b702960b6a55a675207e5a1e8da15c20761d8cfb1a811282a767d59106b791d41a8d9c153f1ad036ea3eff15e83b45df3264c51897094684586a7d1d54cb3952650b4c929c23143a6e07fcc69f9d151796410d234694c115b991c87a55cf133073126f643272edf40c2af8b92491233247a6aed10529a85a83ff24a8ba853506b379d522b961889b3d4b535343b55873d63531a57d3521035151f80cd067ccac060539313635aa6bbc90d2a923cf172f303917f17d4bc06582aa986d4a29de90bfb17f9c665bce27c5125188768f53b3e5129bbe28f538322feb4a115b32b806f9648432a9679c13b4205e9de7d65185d6dcecff43cac7e1a8b009077dff6ff9ff291f20f2cadea32698350ae08163721ebb15586cf6f1f2fbf8a2726a9026cd916fa54ff4a990d8d1dfe965376749b3cf22ec1a796b22a00cc7bc39ae69e4e66274cfdfbb0111b02e0d676decbcc229ed0bc0e10fcf48f26e9b515a0442d010b207e823703b08e935055c2614cc2bc231f909865e8f34ac7899ddf2d1408528949f23f537deb1e0a29dbad8a1b65b3b0ebf1291555601b1b91905d2e80ab0e8700ca729c05886f409fba420d2d152b2e6207fb88ecc179fdc676e27c273f29db44988b50938d00683bfb2e886a18b12cc52c50b8daece4dbbcb1935e5377823fd3cc6c84a95a2a592d9bdd0285f8eef558e541cd4287b86c630e00c4bf3759cad3b6bcff112500aa5126180fa81227983a2e9fb42f6b1b2ebdb24c10d87e191b2f71b0d2ff65ae5ca4a59068f1c744c2283ab760c3275bc3bf06a14111143630a8eb1322c361f390d0da5f918c5572a6736e194de65a28f9c56c8990f1ca144fc02b012e913c01a4384499d220cd5ffb4897f470eb83f4b7563c648648a43cb9d3635eef446a08685831d79473aa87dc0c001ba0a0d9a6d60d733665cf8d536f8352833b835bb9d3f4fea62a3fe07b10dbe1125ebd61e239e3d2512ce9275f402dd320275945404f9064793426768f951b15594535444e70370c09f1ae2ee4b5673b568adf511ba604112afe175f323ffc823408d2881948314ea2186aa2652cba0f32c6abaf122a7e536485a5a4c01eea4040615023372163bc3fbc3fc97d48d4c5a8419c3a154b8d0a88bca55cd27292771e77b093b87295d0898f22ec549bf15cec8bd94fc1a9576e9753dad5e74800a2be3374cfc2cad20ba4e38e24acfafebc3a03158f83c6759d4618682e5ab9d132ab646d6420913f81f7ab1e603c80ad85f6c895470f3c5bdbb86d984f43099cb320d196eaa94568351a9c29102ac3b0545452bad63f264f107c7b40e318c576218daf26a385d00903eb345ddb33d9579590c6970120eb3f399c374e4c1532cab1cef33172d86d9cf52c1f1a748134fe52481e22d783e5a241610c20d349e9928a8f96742e627c2d3dc4232e7b7af44686f18e344a0388021167244303bb4bdf39a2551495c89507ff2d96248776431207db618293e8a3fed18b562636a399eb6e9a532fc40546077199a34a31c58c73499978830211d08ac806177198d23caa2817ec55252b17a06bb2744244ad067568f629a0920352315b5e1dc03c3734c756f89022ff44e8be08888454b7d57af2f4c6aebf8c0541415a5d44a38450701a7b2cf24d248a115476ce20a959a7ca14643397f8872a42827e6c9ecbee194e81f10c1d2a47a533f1b1854497d3ffd21eb68fb3ca57fc153186b2a4d11024a130f03ce853cd9179f30d77ec4fef007abcda8c7d0dbe1a8af5f595d17d4ef0106fe387cb04647ab57dc70a051118a14a82d2aa2b08f51f5483b32ea471d176e8035fc9a4f634c123f641ae4ef855a24994764a80ba4c18525b82940b6a3f2ee18e768c9cf0d4808d91ec82a59ac7bca0e6a47173de6090e5773ffaf95eae10781ec9e75da6186cca88bc9202b25a3ff0b498b2abe216f728f257ac98a3bc0350f9621d9ac3c952df9e4dc6cc24d8c09efb680df13613bbeb5e983ced30ebc2d64bcde023152e3456740a5d7f20f05bb960f393e453f502e20434ab10aa863023308151afe3ce8409ca050c81fadd09fed6ef7fad05bddd5b79cffdfcd603e695f427cc0a409ffffd2c27d461b5d1913ba5e4fae705072c9c997fc27f8243fbf92592ab1c8da694d9175d3995a24e96b7fe19f484870901938b296c15dc02ecf62ce71e48ba70874fccb9612c55694eb4127af3a04d4ef9fef9620c0e99122a2311a5ec00a29621a69a658b32f05fa4e972ae09b3de07b37ec29022746a70a2d921fb597537c74e41282265eee3ddb0edb62d644d352905823cea61f8f4d7498eeac9abe3e8c3832e9020d3fd0eb1075fc30200fe0e0e4ccdcf9a90d8452ae238ec529741df8f9f54ada6ab144a63c280acf2f26bfc019a53e9bd59f6059d378b09b14820895d2f504f09dff4cc50bba6d2692d902b03c426b4800a842629284ca9e5a55950fa690854e3e4445cc83f3f44e11c7baeff3f2bd627f6890f181b2afe229f5aa020b60c15785d57025c1934accac1592f81d4c7b5e34cae8541eb79690e3033e6231b97e076b4cc00422aea885e078f249f02e0306a61d2271e5c99a0f0d881408daa99ebe850e1fc97093a5d946adf5c8df8dd1b8cb6bc1716a6abe8cfd6ca6ddf89e92f6ea2572659a76149761a0c11d84c031a589dc338797bc5b31ee9a6bf88fe21dc59e09bf7eb450595fbf2d3ed61a72d90206395512c2ff29531e77e1bc98bbcfc08016c802cc301f5ffa2e85c08b1dcd96babfd368a5adedfcc400e78a28885e8b1423182121646040317c9e8cc414142df0a52738e8925a1044a775181fa9319d69ccc114b86bbcf2db920f491b9fa1c3af3a46e7091c4278bdfe41df74c6993c53e9da75e6e1904dd80888ea2979a2a7b84dcc2d35a47d1b0ad5b0357046b844910cfb0092b716fc2668ed4ab755cc2d2166bfbebd2d5abb1d7fe15ba109bc1278e852861821ea4a9d03cbb76da9461c248fb895c27323eed8a5af21e5b382c7b694adf2ab08b72d790d7ad77e8eccb0be08fa44add49bb38c7d5e16cfcd87df0484828d4a9704b0b86d632c7d401b0eeb569d1dd9e655d8e264cc31f30b5a3c4a228171c37c4ce127f9a24125f40595d6be88cddab0beff2db040af2f4077949c15a22092324278b64eba6a3cc47b7521eda09181e67270ca84f6fe0097f6311f973994933672334311f3a4f0549b42c94229b54e8cb3dad1685b2bd65cbc7ed7e427dc0c180fd74b8c841cbcfb3e3c1027275b1af548ab1bdf144d6e7896b8476b422af398990ecd740531deab3a9876be7082d6bb28f6861b516872a11af2d279155a32ac33f40f2a0bda0f4c32483dd7b81d0c54f577cd33abb57e11147a127f053f5682d2d64345d59f1bd1bdea41b46d16ac81b16740ba7de8d44db4b0494fb127d6a805eb494b106e7cc53580c50eba1a316229054b164051e1c54b072dd79822ffde378ee2c131ce8a18dd7eb69dadc6fbcb97e7ac5fa1a8a28bd85ee6e4a14072722f5eb43682ee921206442fd8e64bbe82631f8dd0b885afb00760cb715c58263361fa6ff9e6bb83c0cdfba0dde5a8a4d6d264860dc6bb02a20e4ecb60d112e621e97a34326ecfa93800ab3d76888e37f88e1dcab501caa579099eb559b8e4dfe9688bafa925b279059d2e203ee800d99edc0e5615ee823c3e79fd9a02c4ca81d2c917c6917e9dafc3a9851df7a55517f4e56d6a0bf44b6cd2df7c47f0e2fea2ffebf364140c9e11602de0ee977aac6854d54ecc3befcb153fbd516f1b523054af589982e50fe756f0f4303d7e0cd625516a443f764886c7800c465b251f7f80c25806bfb4d34ecffffd796a6185bc97439142ae82560647adccba20eb0781f4d835d0b37e99920b12f2b92d71d3b932987da4bdcded28cb26cc699f980c26fb3725b0c0574b5360df8ffdb728a89922a33327a88d7103b2ebd2a89761748c92e102de5658bad92bc5843613e9621227ced53cdac9cb4e92b50b28ff775e47ae1aeed736d05fa2a51e68ddacc5fe80675458d51d94e6964f3a4e10f662844fa86ced2139eed0555f123451b526f54612ab638f6a639979b53b4c2f01f23a412b87624c060f2dd6de92970d0f534c9432bbb2b03d4e4eaf7b78e8170940b498f236592088c6e7075f749f8693cb97cd0eec6742c8867015b00016ba3138fe379dcb9701c4401aef263659329bb93a35fddeb0a392e94ba35cdbe6e94e8a776ad4f50eb57cd87991580b3882fbfa6d21048b59fa68f607ed30647aac5788fc9127479746af424f551e4204c00fe6f433189117cfc070a8374c4e6f285b2de2cf24835f8f04f736adde2dc221344b8b093030b875b78e331e8d570d08e86cbd81463869499975887feeb539a01a85781c72bc9884581773197f815e2a2b37f2105cdf9efc514468d122f138cfac711590a62c331a276bcb71faa78d02622a7eb07d0a7ab92215a868b4d53298fcc50ed4cc6ae63b52481dfbdbe842c8698ef7c0610fd271c1edda77bf08e64356aa5b4ec57f88160a63bedc28801e3e5478ae44a3f25f80bb024ecfca2ed818369959c260768961a771567b71e0e2a867a3fbc0e50e242233a84bc7affffd0b3cc6f097056adc4b3d2af80e63b817301b366e558079932014bb0005a2474f74d638e527899927af8acd61eaab3c1f9450dc6e256c486d3e579822ae141c50149ed900b2b5139a12810d143f5cbaeef7700a93e12dba8683e041fb46b842d233f147520c905466d4035dd30cd277211703ed868f88d4ceda3486561416177bf6a26aa1152e62702ea6fb2e0e79705e4577aa7b3b84dd578ed44abee4885db3f5e9844051c42e38173ebc38187416ebb85edf32dae51aab866b41395b0cf650a2af7388eeed6005286e09e9fbac06e904ee9d7d114c423bdb54d936f542cdbc0f5ac7143cc03dbaa17161757a5370e24d90f19668da4347314ef96c363ecd62b1d560d67788f71a88e3fb78a059200ee5c0547b5eff8d54f107c478b6f102d1fcc3b479e1b8305d6e195c8ef0387f6ac64e81187827db41695747f26b0117a8f7d4b8023b1d5a374e0f48d0c91382b286da5f90af229b4da73e97842211c50d799ce7173b7495c489d1697a661113050544cea5a025652a0c585a267ce05bca6e385f1c7fac001811dda724f5eec3bf78f915f40f2c328bc7013ecd8ba144f4752008b623b86bca5cc9f64afba926b62845aaffc4a0215fa36563fcbc84531854463ba709c5ac529f61cc1a517cf6a92153f8b1f3bb279c272e320b9f7801afd1a2aee4e1a6f54e1ce54fe91252bcaa902089af0aaa281bb2051e55f4f9b1d87d081fe1e3de720bb62e39a51ccccd044db061e41e07ee483a38f51a3c548a7fa8b7df3f325988b44ff10a03ce8a8033f3c2de476c30017659a96d175fd02a7d845c406e361150ec317db30c98fa014d972cf9a40d61010af30be0c44276d0e04ed95c118a6dc5dc3bb9cbfb2bac063153f1d087fd43940f06d44a1ba106e258cf397172b5807e60d8b47a2bcd4e5ff3afc46733096bce4357a0e09cf553bc63eef65d1e0b626409d05cc0b0b4a1c858045320e5d830650a4a89926fa1041a55a52c6c1b66b3d67fa88c1214667afeb01075b82d74effd4fdde4b35c7af6bb41c83db4a7a082e52cade69838b7bc81ca5320e675810710df43e33f0f8b31d377575fb8839299250f61cdcd624fcaad8d5ca4151f5625127af2102f0875a206c4e0e411199ae1f3bc486107eda2fd37aa3fa39c963d6b6283d7c4fa8a6f48f791ec8458ce4a7a0c38f08f3c5b84f7023075ea9ca281df1126f811446b287303248a7ad8e24ce790623f7eaa7350d89a1dc95f6d7722959e2edca3b8d78c3a89f6ae184ec9e0939c01876342f6da5ebcaa0c07fa5fe704fb9247672e903caa34e4ec0eb2d6a9ae759da0c37626e2da48ec512a4ecf03b1876004179130fcfec3e05081d8b1f95f5c2538fb24bae175de500fb1178f90a1e58b8bce89de3a594a36a33f31a85fa1aaf0ff269a276e14d4153a4d2ca37e4517af5c77b2a3acc61db5a2c81a2b4af2f8b7025a13252d7592968bcd9b1c45970ecaff4953819ac65a05d5172732c105fa7b8099cb17c4c5294e331450fc4d98f02eab0337661f61201b6e0b265893bc698abee47a3e023a9866836fcef7be6c63a613f9c7e6c5d039140fecb43479c8a8118a6d237e9553a1cfa451a6b2b2bcb31c58f21bd23642f5f00951d157dec0ae06e63b9fbc6a831fdfe5ac18e41821cc4a4e69060ccb258a5e2ca601dc00b7aca1750fa989547a00401db9f1eba2db3ebae94c400429a0787ff83775ea8f5571a59ac001fadf2d5d93e046f1b0b4237f84c19410d84505107cdc630d288a9ab78ce9fd41866b0ab613fd02aaaac682ad145ca670fd3aa81916a1376132187727846180395ba36eae3a1f1857adaa4fbe5f7821f4616f66c21937c3fe460d29e9028f30eee197bc36c71a02cb473258f6bd8519f0a7663c8bb13b96cc8d6eb0b2611e53a4e2f0b1bc077e10b4c6b50b4b2e60e019df07c48de9dfdf048f974bd594da7d4d98df93c110d40c2f883d312496793b2aa8bfbec8142f9f9f95b1eba196b42b11df8088c830fe1affacd072061b9b17c7b0cad907390dcbe128254b351910aa464a47e832cacefc42a96a349928306edebe407542af7ceccfdbd7b5efd82538d55d1632cf2fc97c9ca2a78acc1bc018d14abe0d495f661c2d244e6e1a00d9fb6284342c0a92f7fe08377160d9a1c34fc2d96b1afd4c9371672412f7bf3636e7a2f89d12e30267169d4d761e980160ba4a617b405f69ae3db087914e9aea2ae272c31db90fddda8b43b2544a022d21c0caa4d2a1a85af6f186788bbac6138353d67472c1504037895062708df2941a359068351c6f520e952c1254379199c513c8503d58cda700cb308ec7a13d3796f045096bee2384b102c15e0bee2a540cbc8f3491a1705465c2293166d543d397a7490ace4e5cc89db74bc7d118515fb076a58eaa14f9355fd2ae6316f6a880e201c6d9bd12c72ff9b8a6055b23bfc6f387eb94b3abf0396050015503f345533c4e7c0294f61a8e205b505b0efbd0129ab687729c8e0bce27fbd14a81655286435194ec834f8d966a391789bdf793e99d9efd58b5ec1922a177bb274856743a38be759e63ffc2949ef80ef21548794669f5a4f32a9ae37079b5ecf204c7790d04169044568b94538f03ce187ada18c8f0a99b4dfd3ea6ff630c0a181cd099ae7d5543ad5cd08b3697fcff6275b1dd3c55f1105b0c204fc3d1417313e6f1079329c181435779fb7f4c7f73f75dca9607da546771cb45adbf703d868c6a8de616c7797921cf615355d38ddf8c1da0671b4d95bf60ed0cf04c1d9d39e1c73ed44cc03e21f444e5cb6a4fa50fdc2c01feff344bc084e2eb1356a98ab2229012eb036465db43a0ecdb50e645653c74e4e478bf2c93707e663814703fbbe691172ea6453d8e754684fb11a507645c0bcc587e656c1bd1421f8aaea5458fbe1407cc22fd141be4a01fa45d970c8d4f07c0e34c075de22a63dfcdc52575592f0b049df1358e765f9d61f88e64959d844a55a66d66d8fca872ca9d7abd64902a1a44a3d483b976dd77b358cd38c16f474765cccee4f67fa00440faab374d1ba1f594419f3d3838d9060ec73d71005d191419a4ba53cf9a4526b0a5061021da2101d92c62221375ae5a18fa1ae6a773018ae4ecbee351c3075cab429c2e00b80064ab6f2676ddd4d5264394c8aceea0f8b218d7a47a8c8ea4944165db6c352cd65dedfde67fb912797fb8d2b95dad0a12cd7db9f4c1e70178e20ea64b264a5740a7ecbd0ae2fd261e5ff8fa9b9dc326432ed3e105baa968c82e2acca1c32dd9cbcfec42e26f9483cef87768cacccf3391b2793e68f62351162beebad1712f401eda2ea1e907c95f524db5c0e1cc1e7d4281e06002b3934fa9a6e5cf02e8b4cd041a4d43a1b540b90c110d6b94261da36395a7994770300cb19bea17b269605f634f8ec80600b5f8668d907bbc8ce13e45a90f054bf682df4bc4bb9404914fc44629a65d1c5bedd5c26eeff983c215566e0e14d9a0ae719305fd23d39d8121652411d5d5b238cc0a888a42002f13fad2784640b2403d8935f7b3fc76886a33e5f62e0af51b7bf6f96e3e7aad1c406c46d463716c749a83d1d886de96b54d6a9b95f7bfbf2d6bce886454a04b26ef840e1e55085b076b1e1a739eb2d390d44887f7cf728dd2c48d71af8300eb235249ff32be24b8893ff43bc6554d6189d19764a0c35c2a1ed8aa9c75b9a105ec37208e0f2963001564590f1a89519a866c27b7ddaf3c263b9910b9b3afe73730c1be4938b9d680b4d93085edcee645461dccaa39a6488ef1fc134233171d5e51edcea667b2d8a340d84e08712711d4abaf0e2b0db5440c48fc1fe46add2fb5f35f056181b7e7e5bc214e5aaa378bbcaae17441e977eaf7cb217d868ad9b367a007931e489c480a4f34a3e1b7c2f4c722698a078c7605b1c5116b02e55056239f211833e76fd5647f12f2d281bb20883689d3310ac1cfc497b9b796037925b596dd798207b6053366ce2fbf54cb86660920f925d9faa5ec1869ff382bfddefc58e88bcbb8f883c03d43cf99d8fce2d17e8a9a39e1d98a29948b39ca6e82a297fdebf162557027a29043ced837efdfb09ba0ddf98a23a99ac5c444d8ec51a3f1de9e79cbc5f79bc9322313148ff450550dd60e1df9e90dd757acd1ca2ad1eee82f9a33b6228b0502d6502ae681ce576accd4d7811abeff5907543c4aaf64e684ac435e6a49842d75b9f2ca2e61c423bff10e84edbfbc45dab80385c74263aad09c84b5a68de26a9db7d1b9643009b53495748488b78ea1a5c629b56f4446fb8b56d19f746a14165ee82b4a9bc0db43e1b9b9e1d2e4dc11d490e85120b4142911e3e5e8f1a097026d8dfda0ab997b865ad86c14121b602e4f756230d0a4a0c4684faede77e40d13781c08ce7fa1f12557f45c422e93a549e95a63b09245d586ebd18f182d59a830461465727b5b399e97b1caeb8278eb388a6202f5a3e2f54a294c19ae61a4c9c99b81240a491904c7d96199b6bd69d3d96b9dd3e1c4d914316b6395d3224d66c474c279bf352d21ea694ccc40872d2a2340702db0a2dc4d2e88f563a17fd519edef01a257d90ecc2f4204098e37e022b311e440c374c851fab37424962a2a64b2dab009d5cfc2d6e5d3b39a6166aa6b8cf29cdfa5373453e6bca584453856302facf016e7ff3dc145c228c325451e3b145ec29cdf3e904e9d92782c9885ea25ec22522f112dcbccfa077c6fc829f93066582b93d3018f1515cac04c92b72011797ed4d1ce5c579c67eb48636eebd6d05610f2e5fd6bbb7800ec778ce69a43c01f63f703dc992ffca69652d1098cc2895caf32209fbaca20fdbe6b1c7a983fc711ace6397516461cefa9f196328414a27257fc26f0e9bbdf03453da006f3bb6944ff622d9f52d11bb85b262e5110652500da2d60a045f0e5518f6a573e3ab09db66399dcedd1990a2b411d2d9426f52c4e5e2020599a580796aee691c00edb96f7d16805e7f28e8a83680616234d06583c0854d55c6996ad48c80935802f4e29200bef3cbea57f775cb17d8854d936d4b9681b4323d501e55953b62c1426c44004ced9ec3e3a512575b23070d8aa0a29aa0e44151c46e94e5c9046ec325d20228693949b30e4f18311ba8a186ba085692f09d546ff225044d1710e0202a84eb3309e13688113575fef0d09e52619e02fffd4d4abf9d2c10c9dae08eaffc71d7c1e6e71dec3a2ca546b05144aa2c852769089522192bb4795a03e338285b742a14d07957d227f7029fb2d77f9f2aec98d1eaf8177b251889581203eaf24292af9909d289dfc7290dccb5093da209c34d5407ffa4b882fde154c35e72bdb2099e62f69ee2b6001ce576193f518654ec3729ae5c06d64e96fe2ac1760ff1fd7fe6e9eb106352dcc595628484d1c1930aeeba71394eed2eea7148f05afca7a801493271750ce971ce4503695e9dcb52b10a949059c266977b71f2bdd8add7f1a216f9a7d97780e2abc34f21c222e1b85c73b9cbbca18ef6f36e99394100f50a68bdeb7878e5a712ef46b7aab966af1e66c851fa77171c80380d5474c6e307587539ff594d6fbf16786831888b21502318efeb1807a0977f361f6f6854a523c7290367b7178783e1509b02d8bfb935b1149dfc89f9e1f50d02ec4330899264d764af7ba2cd08551f7cfbbcd33a5e4d9569f47ba65d5cd791e498ef84ee551d6157e4d8db9a129ffbe2b72b3427f5f0e87a9c385922ae149987b4153ca5d05c91e607fea7b0b0c8d2f5c83ee11fea6027996fa3b02fd2e05ddbd428815b6763d9cc14543fe4570e4b6a09fa3950a3fe4a94fe9ea7468384bb3552097a3b388f7afd117c4492511c0f083ad571596c5455c12d1d688b49af8c779828c96f15c3524a9317f5fdd3b9ca888dadd7e430ec0cb12f38f43bcf6b90da8266a41451d4c1d7eba255b0809656e21badfd11f890b556843dd828b6addf7bc1e6806c200fc41f07b2a494414531ce94420027c0449e1078bcbfe853e95986ec17e4266f066bb6a13d5733f68a4a1dc74e41d3503896081e4718532b6d027e9ca6255f1b9deeeaba93ed17ed59c1f46f9175db79246380149113bdc3ec3821cd1defaa916f2bd9dc967e878e732deb1bd62a752e01fed0102910ca62073284a088dae6e2eaa10fc9290fa8fa07d24e24cccf01884b53fc3bf5df72f85e4d5ae0fddc2ba3712a28e3e2f9eede0d69db00cdf745e7465c934f3b5cdf67222285d138021fc56e993072064294aad0f67f246467fa4b65e058144f515ca2c1cc65993d2d9533205b1f86911c4534a5762ea82b4a9efed98b6d9da162493560f1d13c2fd957740c4e337d485159217009d3ae2d5891f453519dc018b059905b1bb8e7fc66824d8713358498238a8bc6c9384f60b63c20865d9c2c8f7aa51cef667ed871840e61551c043c046e6018900e89cc82128a4817eb81c51e0dffd99f58a6f1d4321847b10e03360afd0ede1f992b028a5806dcd017ce7613e34485d6db84155219abe1e33e6f64b95ab8b99b3e00285481002046de4d6393fc90803e98d8fc6165576f4a4255de06ba20cb5cacd3eb643ee92b0adfb74f670034c4090d4f1cb4fc4d4d471449da4606b0a3e0189e6396c0d19c5243b03c4077562d27ed7d1c735f45dbf68cc758e42beae75b8f0580ee864b1cc496db930dc27d39af47bf06b6325829e7e58d5f51af620a775004f84372afee20f3afef8b6734665dd7386457e6ce4590b3c1e2308fd3743a88f1d7538827f9fc60e1d442c28ab60ccfa8c9cd3b784d7ef8d414ece50c49d1940c0f8372df7f3dabcba6a0f382d80f149945022b20d36247f563bb4416c0876a1f0f46c246bcef86b1fa62714ff216e4d81b5a906da1721027090bd8d7c52db7facab43a06fd5095a93ccafb9aca7faee48430fcfd59159c7dc0e10d3cab797b8a003c3a4025137958aeaa417c713aa1724aa4f3f7e0a7c8d8a0bac86c2285797b894aa48e940acd2a207a2ce5352a669a839db1783546f4f6dac1a271691594cd2ea0a606dd55037775bae3d82b59e6d10f0608823d8233feb8e1ad28872ac1d97d89b0a4e20c96cfc5fcf74d191315947faee0907ac439d970bbb25aa6411d3aca4ba3b0ac89313914d6aa317d4561fcdc49714339b41481194de487006b9ac02b2c16f522700325564c8a8f6c75373e9c46786b6564a698c64572dff7ff2f2f12cdbc3d4c1f223407221bf0193b86b17f5b6a13a61f927c465485464bdc2b4879c4a76ec94cf32b91ee441b9737ae04799cae69c071e34337a4b396ac18545389d1bb6069abfc5980f4b0c8317270dc1a68438723d9107fc5d6297b22d87ce6369f48462931dcab5d54da92054fd8f2689920c8df18088e78b8ce51d60de25ce7a9acc1439275599474cbc9001352316a334d7569b970678136cd3602d5318bad84ac78df31ef667185e485f65d74eb6af35947af175c71000166facb1746de0843efa670cdef78e6fcaeac07ff427471f26ee78cd92f07b277abb4f993070ca14b0aea0751dd6951c9f6d0068cdca347162383f51a2506b750c06602f2d5457cde338a8e03971e16877cff4affc370f22f228f64a410faf13d804b206060410221f1633dce439008b1667123959341a8af102e30b3f8e7aa2fd12d7d2961cd61a38958d4d96f5a306f427b5e7f64405bc5e12598367e23ec81f8ede0c72e1057e884c56a7c5a33ae2599f2d799807d6a9b66891012abd508a4fcadda557b8c49bef835fac04ab8e0a62197eb79f03d636607cc73964147ef316ec6bd64f8cf84a71f06262e30fd72a875cb800d68755d1f8a0b90686894150e3993b869a031909af44cbcd71cc43f3996fe980da6d5a13f5fb0f6b7f6a4b02e36368f9b0ad91ff54de641cdab2be4755a20f9bd943acf45be118fab0814c693464170f06e9c3fae2e6d4aa19cece0ea4b0873db5e3097c7aaa7b9687ee28c4c0d162112cbddf6101c73509e069d22960a9a510d9f201e7da004c7b6a2f45e5aa3002d72d8e1e6a83562ce7d5dd418f202826036058107abf8b60429b923224f5365655b86cae460dfc36619e51bce0e80f4892dd70a1d84919867ebaf749c27c6bab64010e537cd62d5ed17ee3e2d16bd40b8dc3868c124af2d8ad2ef1845a72d5d91255b356dcbea3908f4c4b6091e1176b43baab49d23fea8ea2e6386d18d773d55c9641d521749223b0e834f213510231cb040cbb74b3e62ba7930929cce0dadb3fecd87dd2c812eb22ffde1f47d657263021d0442e5fca2e620fa81bd06312eae90f903d2334aff436c846908022d583ebd39de49a50110a8a0722aae31a8dd8238cd3ae497d8649fa4239dee13746a6d2ee9d1dec10d5f72e8b82ad3a82b70fd615a42ba4b6fcf19e8c8c604cfda1f925c6ea009265f64d940f6f818efc8f39ba44f59a76718aa5e205e1dcbd256cdf0d2deeb522a2e29b4249fd90c775ac3911ab88b2cc44f80f668c836d674eff8ee3903ce96b99a2c32c448b2d86421fa8490b0b1430bb824ca01ca640b362f199658232b5173cfd051250b89659bb153126f4e1743d31b9113219970b1f8e2dfacda2f0207336c589f1789565e8386928f73f6b1720a9a59853405a4cea5a2d3b6bb78510cefdeecb05b4804bea1eca6b846b9f0913298546c4fecb20668d768214d1fabc9714301d72dbbd44c218a33adfe81cd9ba9ce5db24b3a95005dc24f2f70df47b9be7223058d3769b11256304c6a186051f064c608f131131320ac1dfa7506c83ed2e9e38cc5e7081646944145b56516b37e2b53acf86881615afd21c9cb3e6a040304fa2302f154f79c0a666c42a8dbb4715e1c3567a60d6a0f614e7880623203c710ad8b490b869834d331cc86ea308e90b8f26c30801d3c5e819c083a9b32d79b08ea7ce945c2048eb4ce44e3c148a87b05e971b691f2790d4bfe78413a162937aa0d941a72dc990e4f877ebf6fab5a98f083ffd551834ba8e1dc31f7cfd020356d11e14b960007e05a6ee00720ddcadc881c356a69dc92a0b9f08956e6dbe8e6e765c68b80f70bbd90200f7611a3ad3cff8a5d38cbbdede2716d784d873923ae65aa881803b62c88dbf4683a7a7a193b3877abfab5e1cfc9ee12f60d9528d39915f1b696014202e5578d0327043823276e011d5e1439fe5c8502134ae2eef8336f4bb1d3e34d5d34d1aa64975502fafce4002cb928bef1eaadaafe9cce1ab948557aa7e1cc20f41fb8cbbf39e0ce920aa0726a7d4362ca62509a075e8033b59c40a08a36dad9e7acf8ccac30b00f9a5e0b46ec81729340646577bf8a6853a1c733ab5de4c66d4c40d4a5216dca72cf2b32b45d54a98ab738e86b01022c6892684ea81865ba4fe93912a33c447188c232f6f40e4dd80f0d2a83c111bd813f410e1ab7308a0967ce1d2cf46cf67c2db437d22ece3b0a31e1c678dd48469d7b74b258a44d285d21eb46a7aa774e428724e21dd7842ce027a9b4595dd8f0d14a73243d32468e0dc4c1509cbe6515ad30f19bfb2ea419e7f4ae3f12d15e7f7709b521b8e7bcfc0a13ec4c54c4052e3e9a7055bb9d117409551e800207a408a8a20e146488935a4b883cf5a486826f4abc97f0335c307a77d001177f70cb4591b5683de3c1330b69db45d3d0ccc7b91fcf725a916b8e1e0dfec0a99582ba2d039925c4e288b141fe847a50e8cb1a780f2299ec2cd8b1ed12767b5f9f337d85869f23cef6950a98b5af85708d1cca1fe5e35b74979aaa7ef00d5f46148631e52c7068a5ae8f206b5527aaeb91c5246c657bb95afc741e185acba79620101f8db1df9b7e37f03d7e1216bd721d5d63878b2d6620e4cf8f126526f7408fc869e74239c5ce79fd9e680733e85391bb7830800d44ba9c8c6ab7837a11e7f4d7e1ff51cadfd04386f56c8655d7f1a2e0b0468ccbeb5887f7ba3c8ca7ac3d8bc0fbe652a7a3e42a03be353eb2e9e7ac6059a47270eaa3e49af4b9a42ffa615078ed8a650ce561863c37e68a44139f486ecb1dbe0ab41f957554f6e05d2536b6392108906f415a961b0770c6c6f2029a621a8d984d822b45296a6f58ba2a83f77bb287852b2973d7eca888e8efdaf5946b747090d2ae6db6f597e82adb6c4b17411d9b48a43ff52e50c2bc04a4bfd883a0b868c938cfd644cdf16c4a20775261831e84664ce8dde4a9f2ffc0d70d306c82d4e43fe278e32977c61fbfba1f285788997c968e900fc1159d877591a07b2c216445736b4ffaee8c3622d2d36a6bdc675b7a41168a2aeed9ce7af6f24c48947f2592988b5509ab85343f002a10fb3d379e941bfaf9507c62b6a34e152253d38f94934e1486dd9a16e3ec15b4c9fa21a821ac044ff95ccf15f9178011669d5568ef1fc8745a21748c2ef6fe96122e374a4043af06191d41f9ea2763d4c3a2c92580e7ae4ea72886409b15e2f8e4f36619164821028b4b2cc30eaf2e130bf8129b6ac2f3e2cd24ad44a32b891519ab08ae4d5dcc9a537438931c299153a57dc4023114c637ea7cb73c07d73f2169316ca43de793336bbcbc88122f2d31048f091dafaff17be0d15abf108871a2128908589c3e9ce2a03aa1d272869012f057f5842591d537ddb4bd4e64a3ca3a83cdadcb2b9ad19419a46e328415be0cc7884cf5d32f4cdb3538238c18e5339e140843e3d293d18f26ea8e2dfa9d87c182f441a0e6320fc739174e686765381d7e870addbc3350d5b31bd80df889850ceabd1f0c75dcdf91ec0e302cb9dc0a9f82c5d6b3698ae9d5a9a2eb41f9d7926ef610bf8ab1e8bd182ae8dbbdfd327f03ceed2c0dba8e2332a6d2cda7bfedb5604169e74f6a863741c55d93f04b4213294db0d0d9acc515d2e1c941cee3c4e5760234a18f4b78568140bcec3673c436dee2e6dd9b32f4618e434d66c4af930f8427fb8f3c04d977dfd39390b0b6bafab149cb7da79f4bed4515f2cdbd6cec321ca3da91a3ccb81c6b87e044f82c2e9eaa7d53c11355a851ca082fff00e7139aee82b86937c62f3472eded8f58235a368301d7c18e66a04a690782928e913ac663af97753267cae21fd183279c6ed7cd93e115d976c445c13888a8cc2a71c2ad58949fd69c2f31c6d9b3d3d987b15cc20bc10d17c8a735e400608eda9eb2600d96dc06f741a86dd8edd8a0b5619e61845309e2249089105a68787279936260e6fd0d9e6ff06c76cfcfcf99f02e9614e9c7885f776e48de83345f68fca9234adcf506082ca3a155ba9355fdba3de697b0309bcd50b09fbf58698ef6cfa61a8704669e26819dbe1f760d50c828a0afec7529a2c2c40972cb436d64bf0b5b4ff5a43a9c1c001d739e0b065a79eb3b049d795c11db078381a044b3294229c04880b0db2dcffa01b2c46033c780564f319f7b417bcdc6bba2008a5337d64b747e9d078f0fee1c74cf3c6629b5fe2be1400f141b97610406f4c330e70152e5c7c312f193f3747a2caace282461a20040f72bb34730e06bd54a2d6c3df8149e77d4b3e2dd03b2120d5a1306e80d46d2faf1cd1ac8dffa33f57b9d78dd6a5e4bee917804acbe84962f0801c95598491596a5829913b2a58dd3b94122f99dacb0e69da549793c38ac8f7d3b45c63cbe113db9f94cecf54022cbfa047e36ab09ae759799bdf7472b1f86c7c4d5bed54ff7dd90994d52cedd6014ac48be1847ac94780f3b2b968023c96f5de29d0d75426c898f364bb491462d72fde3ce3daa22fb2b18d00624af40f48def4b9d870ec022d0327a3cf64f74ec07b65a1ccf097cb77bb751cd454642fe5cd60bc04d0732f31a169d53881417bee789c21c38bfd6b16e70ea2e719c13036068c40cd1e82acb3add98cdaa697126df892fc55e2b168fcc6146b3c55ee42699ca28e7f7530fa6c7e24c391c71d095170d01439b7d18de9d1a56991739ba849617f13465e38fed734555ba0fe6805f587776011ce91544079ec722b1ed388086a68262a63fd23c5fe4358bd167558ba1297a7203d64bcd4bf46b21187fb3b2c2a5d9d1dbeb4be45106cb7bab74a2c92cbbe494a6c435d981c3eee11827a654ee2a758edf1f926a378cff7bb0352245ca5d2c0c0b588e53cbf23a9df4e27a83575333589dfb2a83339a0d2fc00ae74a37de796e885a3d556750f242cadcec86208b7205d3651fab98072eeafb603b31d667f66cb5bc986993d2a723c4873e19e023eb416b9baf2e2cfb04290583f05c899dc3a1b63542000227046f957be02b41eebcdbbc02b70e1da967560a23b2e5838a29200061f6c9a953ee4cbb6397fb2524a1c5084b100ae10d686204aa635a09c9c61d1fa25cdd8d133e68ac4502a8ae2c42cac90896ae081ecc2880246f25ef772cc9ebc2c5093094bf4596ec4b76b3f2b5cd40974acee7fe6dd38b9054c7e659b95678bf7a2c10adf852bfc965326063ffddc0c785d2ea0984d311f4c039cc6667325183963dcdf361f4df91e04bc30ff77946617de2701811155e8a9beace0dd9ad9d6213a60efe764342704a84c1c02f3a668612ce7cc91a8308f5858297aea7358c4a6ebdab366f274a56c155642a275eaea41b77af0039ceb9361d7b9372d35431a414aa2b76b09003ef55453d0e342a2de43292b973337a424619ec41d294ee5ba8b47edd66e7b7f7e3dba290d5e21658f869fdb03ec70fabb6ef676084cb2114ab106e721b065c0fd36a71b0a6c242b1835c7a13d26175e94f447544ed889068bd1039ab8a60eb2e7eb796dc695866e56ed9347d1e22530522439dad5e0516564d8c118934347742a0e5a5122d20a31ff227869a6656b526dbe2da227882e0a4c2da458345c89285b05001f6dc98fc33b539aa8b3b3b020d7df02496058040ae79053f1a11cba673b3406d049d96d033e1d8de7d2ae7ade23780095937d89136c91b681103eccc84b244de07de0bb839d4f0faad0f1f66b41db780c47da089d93dab7f97d00100460257e0f2e19a9580871fa718bf648f626a45ae93ea133d715893ea6e711b139e8eb148c0f65c349b96e58c54108573c8e172b010660ec2b67eddddf74d2823d33b708c4b3e39c7ec048e06c055d341dd07514ce3d10b0c342cd49c8c6345c6f78085ce188abcbeaa887ab3dee6875c456eea29f74db4fcc9452d0be2d3b7122ce6b9f46d41279b25a1a16654c7d6ff34c219633c3aa0105444216f4e3712e1d717b1b46105200247d5818f9687f12507e91982f5d33505712427f40863ee93e5fbcc4eac9238545af6cfa2533c8e2684dc1ce93af4a517e7b207f54fa963b127b35880f05c043cc56825ef3d0ca6fb248169150906288bceb452588950678dd8ec806fb0590442de4781a0a30a04bb377541ded2760008cf1e5159a1fc68a3446318b21a9162742e0d84c302e8b37b5357591776e28052139c0a7c96ba96c1812178000399dfbf5a670a32ea3159e087a01c7e5c8782929fd22ac2307410d145b02044ba6efd48b7ad0b0711790aa06bc4f859511d7f45cfade753b3544f6d940f0d5f02803eefd32243d23744c42d8a68a631a16ac098d1885daa2d782a10b00594394df5db78d198f08baede4412492f0347a0c205cede26808a582ecb85679aa02b6c24b454bffd5ba5ec5751b8dda1b6ea6e81ac6e80e0a76ae81557750917e7d62d5973aeca0483f253da19f4375d09500090ef37836e61b9abc4e8f4f97c419308f26864f1eb03e1aafad4ac73e66d7ee4912ce1923e9842edf2367531fe5c1af8f60860658a7e199c37949b17e1ae974b9eb1740bb9cf0fe2db6fac31ddd5e01777efe40e5b37d97888fa55140beb5c6268869773240f8bafe12cabbf5fa75af5ac9857dfdbd57f2235aa2aeb0246ab1cb0eaa6b2b7896c7aea26da7289b2841d315272d670b9836c676bcda4e09254dd2e76433d33df6447f8194ce06e27559825eafd63799728e4b4bda951653046073f69fd09f83a1b573486673b97336ed3967372b49b81c5a9cf2d4f823bae06621d54c91098e5472dd4e9a2d4b47e60d8ca8875bdc303c76571c1600b65071bb72ac177b964b9c1dec0b701cac3e26c4335d5ffc3318815338d4c96379bef8130d380e0a6c1f6a111cd684fadad2dcbdadb53ea5ba9c55f748fb579714083d0a849d205ee18ddd17eefc33a70720cca538852e70a6a1b203d17c2ad810733174907d52f5c420f76da02913e6a565645b46a325c73d0af7a3678e86bbb75a19bf03e8949e6d4d9934a38246123ed8f5ffb1f750c85a6d7d4e561c44acddc57f08b30406cd4f86efc37cc6dbffe3294959377b36f50da22f3ce52c4134a060f9d3d05fa56d2958874096c76c81373342cf14ee3060c326e77b6eff19be2cc1f0518bad58b8151097a98c10fd5b5bc0b42283c9b0a9022e859aa2e53682a10643e4598a724fa4678af2b55fbe8b915fe2f6268ed7b2ebc2ee3362fb6a40f2c1238d18ae6574d787adaa9a4b1485eb537c61348669aa6ac6f7b329ad108fc6d1c8b04c6d39288d5804e934469a24520bd3c6de52e43c41482d9384c0e2e94c95e19941064e90766dba2ae87be8ca017b72d2b38441dbeae1c9a18b8dba66a884b868176d425821875105a3a9dd71bc14f2fc87dcf096672a6a64039f8b76dd38fe4e5953874527fa980ee9ac28b81e411d715a477256ea55ddfa97111c528a017fea5e6c06b52a9112c75b8158a1b03c73af3700a7cb0a3f3179a1a23ba071216a86d2d36a8afa34a2b5457c70895bd802c89283ce92a9dcdd8963090b9f42026f05e40c88bb88647d7696a500470f32b0944b121ee88aff03c5c898f4be9f97534d239959b063128116bb66bb2673b380e20bbb91c100ad97497196dd44789671fe23e0c1bba99ba9539998a7f201a8b237aa6ceae9d97417357f780fca8114ee4bf251557c5cfc4181d7f5a1b5ea279a6ce336186e42fa30105b3948e29fd2da0c4638231c1876960c3adbc727e8fc7f54979934cff6dc594165aa0df31a6192128b9382a7af8c655dbc0af6836ec47d939dae2fb039c3c87f080a1ded040477025d7707c135e3250a8184e68bf7a051fa3e4b2bc88eff829a75373c707e828ed0e2078306983ed2ef315e7ffbaf81a2f8c59f53b8a930af9f6a42a74b61ce714ace5b612924ae7a8b616cebc1eaca31a8f41fc6de09d6a281b468b5165f433a20069c4d1eaaa5d784388438cd89f334d04b28ca574b0558a97c5d588c01567a3c632edec5f93aafb82de3b8049841256225f07e6c0e9aaaeb2c616c47b445faa5ed0a7a2d29e1432ca2330f5ec255c3fba3c49dcaa2b8d01d29351e823ea57b5d4e3b3a4826b73a9d4f1c514358463473511bfe62715dcf11abc63c082aeb8c14a029cfd277e4bd2d1f7b236e7797b6e09ed90c32e1dba4abaa9a7528f0c6c8ecdd9c7b708de5b27b45c07e2bd1a34144a45670013545a753b2e756b7bd6a667572d7b523ec240ba7808b046f208548206346e66fb8b9642dab6609b86cee2e7b4b59c914d7365f33b32af5335e1dae92f753bde20cc24910b0bcac2d533c288f8bc36d955f6091eb946d83e11ff78d8e90eb66f9f7b29573731393ba9241eea5a9dff0bd6e48f1dccaaf80e59b04ef5f0f6fb27b30757e9cfdec4a95d3c10e11b59db7acf7e3094745be8a78c77f0ab93861794c67c417468422e87a15f19e9f97786f8b30c7c3fb4bdd5e42fd28f8c19209bcef036470f4fb2c4cc12e08baa01e734ca8f85647e8f8016976490ea3d1346e17ad6c52efa107db61ececafafe405292f754c665a3bf37ecd814b0a217c370483c27176e94bb779eb37b9af2779d30dbfe887b978c5af4751373ff45b2b36c4d09bf68fe636009d7702a015e4e526d4f8ae61d995c96efdd8779d2207f69d94da6bc260543fc6e8f3419c182b194376f67527d8ef0c3b63f19426de5ccb211b24395aa4cb34399ef8a1cd423642fc4609fe7dc5afb9164d852227bdfecdac74dfbbe5a8e0c7717864b3e757723faa41431871f153388ed24169fb74e8f0ce59acb9338e7a40e019226890b2659e520427746cddc1f7007a7e57b0aa01ba337e509cc311f49838c66d8c1c7b7c476e022e95a1ce809cf8178e16b795a5a4386ebf241138f3b6cb7d4827f3a00765d3ef084b56968ba9436e13ee332c7a968f6ccae6e356ce996d64558c7d56b99968cbfb0988034e5b489c92d88e28cecc01b4356d7a82678c850805d193e0882fab10fc1e84c280d05f44897fa02682f4512b3983b8beca1e3f3c055c7ab20dd979658eb9c6f3e2a1dc68b84bf4fd850ec14fe3d73484f39dbf597e8c0d470a41b116051c4d1fe1eac90ded9008506e29f07bcad3e0dae6c8b5b6829f772bf6a371ecc1cb117b5cb61d2a55798863932423e498a3d728db62cfd1ed206fb374c7d31d3518962ea25af12d7fb167af19e20bda5b3bd67fa4022e63e0d19fc7041222f24e652536397f4d9de907140bc11993e644171a8eab0fb5367f113028e510282e8b57294e678baad8ccd008b99236ec0ef3cc825867580d14bf2518e9130b7d6775893374ddb80ec62dffc5562816e88eea5e676386787984c4f9f5fe0805268604c146d37f3f26d7d5996576049674cdef9834035686097af7a30342b4579b285b30e6220c2d1587f118e324b968894a2a7132806f3c4426188f69713201bc04be15b7bd1ce5489a7e61e57d09432202c4ba6f501f9f9d200bfdc4e184a29dfd22e02cf32c1d74afee51ad9d025ba03dc309ea79c5f9ce8e539612b0dc60959818a569e51561641a72b0f9f556e110b1d3991f7a449348c2a05b44f8abbfef924ce4b13af2481399822d53fe5b64ada66776b99f9ac0bd115052368d4bd9420bd54f99e31da2960125dddb89b2fb8b7b0643465acf4146bcd8966c6ac3b845805810a0909efd60eaa3136ed13db21cc65b5c73b28f3a8b4aeed2cac9c1716c0adeb829916e33bd1a6a3f0f8c166a096e089c03e3ae500ee78033d3322c81509f34c6e965c942b3624bbdd84054164f167b20d2c6213af614a4df81ce592f3da19a6207a385d9404600f8fc4c741c75dc79e4986f2973e28cca0b565646d4e8c58696cfae0979de8bc48d7d58a5bcc2e8ed5a7cc9c1d9eb15a504e4bc8c91ad8b775ace8f01aa62103609d93234775a5922acbc3c81c6a75377add716b64f0a06cc8ac038ffe610f4b36fd3f97968f9ca549f900c5e0f81e3660f150b957d96ea57c310a311b3eb48922e0df645dedc62d137ae071b9e89247848cda708a703cbef9ba67bf084641c285a8d3e90e2b7ab7daad80a737cde47be661370e2f107fe1fc6e814188053ed4e36039cbf89fe2f914374e4dfa6aa35d41ce80752549b35a31276274ce84ec75de1b446745ac87f843375f9e333604053960947fc1fb770c500a8e747c5f57f11c6153a41eccd6e641b907e6c95b01537ed8ebde49ed1d130c1150d32bef9b6a254d1f06b8957edc48ac400159e707024477db3c10606a68ad778e22435e6fcf6c7c946a3e0169b8b50b4f936c30d95ac796e2a095cbd1a18c8904ae47aa8c6177010fe1210a78f530f9b4c03bf1fab12c1c6ff3840a7798b532708951d9693f50e1e90846a92c5735c1e9e2606eaaadfdea3d29b267d1b4bc725b333ee7a14d4b572e7db26817603ee636f145b7fd5dfc6c940c1c447a597ddd8aabd55569538854a4bc5dca3f6a090fb88b563f62c6c7c13f4a5e124b03228926a12a28b46b5bbaf24072f57977c4c1093dbb99ca1e293ae69c812f8bb67368ad344dc79803f452372d6ae3791d1b9ee776f84787f86c27ec3185fff1c140ca1f443856aa5312058a53133b55f7ec5732904e17efbfdd2f4a10eb2ecf7d9696efcc5a1068443058a1c24579942cfcc34f347291cbd35ca5738d7260dfd97c65713c93f4f0382eb5257e6365cb1fa0b2d43e7fc1674206d34b0f4fd2fc289993ebb50d58b648a0373fc7a8504a2b352da5c9f39b79926de117e5a60d373ea43d5c15e367e441f9529d99634c45a5954b5f671915799703b54579937d46a5c65f702725b65e1c0d1b4ca32196468975546773c7b722e173bef2f72e7984988887cf93e9443a2a3df6262d8c144b8353040cadc6291eb9be548aa3bf5feee6ba5c1f7e998b3cc17eaca44d67e0b9aa9ca2805997ff68954d18859fb40a6a9b8f2d893557f33e78ad1838f961189364bcc3ed7eaa8638affb8e01d5a6e88e305112a2c1b2655641669063b2a154bf7e369165651a2ff6ca961a71ea0a8c47c083234881441bc6aab9091e275f0e4769596df00141ea528d151286752b4c79d7e398514f471ac9c18f8e70236863b8cb0763d6e06475d73fe8cf88d34bc231762669c086c8aca2f3582a2674580e675025e813e903e7f7669d3ba162e03b553a830131bd3d038b78dd85705d242296127225ca4b62fb064b79a1823ca33d4f9b2bbca26d4eca8212fc5fdb64d13138d3706e6649e3d3644f5bc9c7dfd73ccc41dbb5ed5bbdfcba040655aaa3c0fbc6e451cbb7a1183663e908dc1becf24f3d79833fbd462ff472ee30e1b9842eba3483767724b2ec8edfd6c4f98e85d306f62a80c0331c37a958886df6642a40adfcadcc57cb581920f9fe2e0386afb5ae098937be7eab015620e4102c2ac57cd0a0229e282b2b4c2fd91ca69fd481a65f9716cdac2bc49acc991d926035fc37d4b8c19ec22c09fdc03b6f8a659cddad85f9cd38e868e3f90b2b425aae47052693c748d68a39fb6f1fc94b9fd581cd445dd184565c79b6f7dd42cbad730934ab12401b0d50d9b80ea135e9e048feb8b123aeb0c431349996ddf834e029418f4112d0fe59a02d98a1346b723a8d9c11e7871a08cf2229a757a516bbbba683d71fd65ed3468551d76f89ea5db89017ce0872f1120cd52f3ab9848a3ac7cd8977a3b4186f3055871233d1b9a78005e9c1f5a77be38cd02000a3001d84685327ddc0b1574e40a90eb8997f73dc089c856ef6d90377c0c4c80111b19fc030f3f2df2df6fff354f263f6aaf777a7bc287a069ead70f49a9a8b59823e738e1d820fac7170f5592720d16c18ee512d73a4b715dcb3824fe9a373eb1a94ccda0c7a79178968b7726d2e137f34e88a0b3580c2cacd6f1de785853aada185a0fbb66f1bd0bbdcf8c10b00fd935d17f88bced1634f49b17038615c7a8c641d51029c6b14540ab29d39ee53a10a46bd3301b15c3e457b374340e29320ce2a1fd05faa600e89ca8c741fe22f311b14a8a09a8f993e29ece93606490c4102c70afb692023940eaf1b65a6eb482f14e9de4b8c1a69ee605d4edfc6d4c9e7f9b59bf0b77bf93737fcc3bd9d93c21756055637a2991c7bc3924c374a02c7ce76c85a4616595c98bfb07c3d35416d04304582cb51dc582c6f3a474d005e6c53d441f58f1f3c4c0ff2a26ad9fbdaeb5ba06441936a22d988f0e1da5fef7875210171ed694b723b1321c9dee3ad8a5c1ee7c92c472833581f37208770908d832888f9b7959a52d206845d7db98bded6c5dd99e77256a23864d281451fc08e1cf67d29c488f8df22546266819b302ed6f85235108e5566ed0109cc857386b84a06cd8ee1d4219cdf2468b726a94f2229c50f1e306f3e6a1d435b49b5b5e33b170510f76e7a6b8ad1473896219cb3e839f460b04260485785f8255a5bf7ef2925b8bea556cc697ec7a5b441c2c2b20edad81ab066caeee4e27082d195f86602e29a38c2fd37696b7b46427629585c39c8d1f022309553c53da1175e74fa70cd1aefa43a0d7aa3f1019efba1c1b4b987a5854748c27949906fd207f3bfb84717c2fdc7997309b020fbf474ce3ec217e4f423f108e1622e3642128847b34224e38e0b13c9db228d534485feb30b1293fa5f4e88b70ff49ab2b5cf09f52eee6cb64b189ee5a62f4ecab5bfa83ade568f0f706ff063fd792c34b93bd9763431533d254bd29d73e96115593b2e0c69326d3aaee9bdbc6f6b9aea80463cfc4cef5fc4380f827b677567bc467c393f6be429203ab7d5cbc71c0dba47bce9544d73863bbc2679db6a2a04bc55f78135cb18b0dd395c3be9c2ec6e2c92deb9515fb60bdfc49b35b6cee825320bf6b3dd4ed02259d50ef80e78769d21f9be1cd1cd25ff3fe406d060cc207f3c080b1ee6618fb1a8635ca82d81c05feea0eeeb60c6fbc59d89243d86eff9f4bb50edba255cc222e790a9cbf0c352cb08220eeabec5764cb0afffabf8167634fe07fb94edbc26aad22eb5fcb071fdd43ebcefe8740a8819340926f68f6544b45c67a8868919905f607572605416c518666c186b2d03de005f82c46100e822ca1f33aba354f8f416d598f75584fc06418ee47c76b48511d677dd97937882c189cb19ca6b8288ee974de45ed6b15e061c4944e343500598e8140f405378a3427db31ac9d2231070d637a31aca032edf0fbe3a81f65494034525309aeec67b5fe939bf11b14ec4481241307170b19bc4519162e0eb4a36a6863008b919dc1bfe5d1f73d306cd5b396e2ffac426e42b8a9603f9953d7caf8c5b83ff997737a0bf25e827ddb6568eda24f20ff70c6cc942495a8f97e39fb6f36d08549a4a11d0d48b2d3d04092f85fbee730fc9f69e95c28966d5a4b6d2360c0ecc8ad86b95bd9cddcf5a52d1a907d170dede06fe68a06fb105ea0b0de47dcc5c61187248150126ec39d70828db59a85e71155b9898aa74224424a0c91d28b0bbe94f00af51972fc028c9f076fde35237ae197422767abbbd7d84ae046ed7dd3a65884da11d3a44399ff1470379d1532585fe8f052d46301580389f5b95fb620d14de4cb09917d51515a6d228a8545c405f9a81eb134bea68e3d8c4b8fa20d27d23e669dbc1444421e51fb580f7f5707a00ddb52b5cf266658d7f9495ec20f983700bbbf31428f84f2bb188cf01694c7b042e7fc35a1280bf3f8ae8c362f15443424ac47934165117f9fcde1500338176a9af84cb2b9195bdd904ea45e3c7b469c839e769f0503dbd4b1d418e6e7e29632750bd2afad462cc9a39fcf6d98c41b3cefeaa29e10750e54bcae0a8ec8a4a185eda385baaefca00b9bc31117931e701e8891eba07bed2a9f47ab228891694f99d304f52ccf9062b5009d0b406c1e27ae550424afb58924c45fa432cc727ab249063a24cef6ddf7466f3c1dbf3b2db9490e9cd1e6e2109cf49a9140b8dda049a29cb93fc57c18b38e4a6ac7b8009801d8d864248454996ebac1543011c5947ca810de93794d7a38f080411cebecdfdaf8b8da98690cebd9c569c4ebbdf43a8e292496c5bf2fd2a424c23e6d263037612b648f727e74ae97b3667845d812d89bec8c754d29627a0f8390f8377f626c6e89a8e522e118ab82ff9d2cb6d5818d0fec1ce08be92a783b8a15f67d47d2573097372f1630eb2b2cc3840209c00e28f1c114e796bd886ed0c96ec50ae642361c6c0c54dc157d40ef8dd1a51c65c59fecf615ddc5bd4c1122b338b11644cc9631f95eb717043cda3203f72271ddc6529508e9bcfe37e5b6222adcd12e33e7425f835eba38b24c8126f8a71b9e4f1e8215b056e79d42202567332fdb05d2219837ad0734b63e944509842566380227156060dd8b3c05a9b5099460b752e2bd39e62f8902549638676463ee45b7982b0aa818ca37bc80faeb72c186bb88e1eb97405aab7bf74b53a6dad071fb53d1d46ad6aff1eea33943511b60bafb42090437d7ae4adcdcff1180e0207b075ba20d0efbaa48179dfd0a2e54c61efdfb056be1f707aaf9f61245f530a83b4bf6104333528ce228d1386492f1ddfe77c848ba1b320c9475d7793c506a5664153f0ca56bd719bad5a5b0d2ee0fb3fb48c02ad5acafa5b076dc763adc24977d8bc1212fd24fe99d4249a202518724a9c10a3370dc01099eec9f9bc0f2b957e1a424bd278ceab7cfbbeeb6054e105d3680105c4b6cd71bfdaada3228de27906d9116475d23d8c3f8079ee70f1c5991d1fa1136fb28a06e143d1058d763d41a3caa11c94e1edad3bbc253ee8a35ba0ce111bbbd08aff460431913371133a086e69b519a874ed0330b8eb95f7c38bccf5442c21f035c39c3e7c0fbeeaf2e42c0c408939b42b303657a2df34d6815cf9aa682de62b20907d65b680c9e1b7d5cb39ec6fc13153f69fc5c3d94e794386a133c390195634657abbb8ac478e53897c7643403b39cc593d5af7a5a8a0bfe4663598c975c044c2cfc490bfa3a69db0ce3a75f4dcbb2a4432f42fa7082e450d9c9a559a813d17f8a5f214a5ff30c1ee00d1070ce5f711e800507828c8ccc7616c548d4e8f91085100065286e8a87a727cdf87c17da324167a8bb86862539a9ebe0b95308f433a4851aee5c8307b2dc226c477e2bb44466a6184327973e60f83bf03f55297fdae04745a1f6e41250bbd0fbef2b858b8b6146eeffc09440f83d7b454a2711546d68da299399a054dc0c94725c15b0d8772941b32f19d7a2e83ea2aa668eb024b4bebb449ae3864417a14aff26e3f4f16627ec871806506ac05a83eaba83a6f3e7b1fd301b479ed626bed14d83c784181f2e519df54fc07dcfd262fd10a94a86126f82ab87b88f9894eadcb70199d52268cfccbaa180a0886b554e90cb04f41a254f6005f06912dab09b0f62696e0ff4ca9f6a3a170e0e78294ded342e7026e95d1eaeedb577ad7c511a0ac20de98c0576c8c3086487676b124729a7377ffdf19f42e2e5027168034d86188ade3c46aab9c4edf88842a665fc48ca4d20f96d68ef4347b2f546e430e17d31eb294de6e09508ebeab98674d011fe3b06ba936d8e8b93b6b130bf2f6db262852f3e2530bf06a86a2745c2793315cfb847bafc313e58ee670409fc25cba442a7a3224ef743b50ba13f4533dbf85c32addc2487e8099444a633edefa8032968e3ffd66737a513bb87daba23fa7a4a22a0bb98b9bbed01220edb47dab0cb4672c48e1d9766c3fbfb6814f744f111ee36b5ef6ed7c800317abf00fff77d6b879caeeb555ea23c086cfde0737e705249a83f9dc1afe8aaaf5655696478e3b5f9999476e17f66e843392075734ffd80b1ee5f827bfdfe544ada957979a1a02feadaaef7d3b626dd9856dc7243c3708755db6dc149724181d86eecc9ea0a371b6b3eaf36176df44eacf09445a42c90b36e9dfdd252b85c5152e4d9b5461930b089b677a9daf25bed594d2bbc7fd0672b3bdccbab5922621c8b26976f03c35b3218cb851c0730d905ccf9393b8b2391f58000adc2cf5d9e48edfd59d6baf2b99471ae35c0e7cb0cbbb6673fe9c36df068ba068c10ded0e5c26a654e9954261447f4bdf23b0a6e069d68d7c7d6fda4efe3abc1c20976b1aa0dc8571992861e995dd7f6ebcd2dfea47cd0a605cccfddd7c2f9d60c0290f0d79a9d4d64ea4b51dfc8278509b12a1469338a1ef44e306888518683db00ad658e51f52da0a8d5e72372d009007d02e11a438d87cda708cbf4e9801b494c13ad03621e41992e9dba127550a925841e6bba5ddf2bbac33b50a1c28f376ba023ed0d83942a3b5ae3281526f5d833de3e9445bcb8273ae9f611d7a5f26062b8b25ed223ea189dc641fc85b26920187a79d4b014d014679de8b52169386adee73178118bc53c6a36b223a3d248f76c2b9259bf651a7b5fcf04b7a356ff112588d9441005be7ac4fd0d55209a554d3a45e244ec355db1292c6f9e824c945c7ba28054bc2d450edf45e294b1e98c3e874f1a251803331c620408c0fb4d7199f750d0a981d2aff5c7641454a98de03e26a61a4b6feb5f6b09ed46a491bdb7dc32cf0daa0d9b0d110cfa8ef2ae872e7625418d7e5d64e39382c2631fe560bf727a25e3d8af23bd4a3976490566491ccc045f34f60b8b663d012f2afac4846e71b1eed2e2baa2bbac903f60c830b02096c1621938851d1b32c18d6bb8d871463601ddba6cf481298c74e11168277130c9838b61973c609a77b14b25cc3a8253d879803df109a2ed0353f10cb0876eb7fb11a2cb2730755dfec0140673600ecc9959cc01eddbbde50f11a577917469d31cf6ae447af02da239ec3f4a363e9a813e9c8bdd6431191663d21cf66beb9162bba739eca53aef1469a53aba3e18a6a439ec3cd4bda1261ef6b7c334a3633930851dc328cdc13e477c610e643d89c97710c96de2dd518539900583600a3bc6dd0943218d0e833019b6c3526c47ef08c542309ac3a88542cd61a40a839ac30eb1a01d0cbad8611004030af58a2f46fa833c54da8805ed7771b1ff3cb9d867954e9ac3ce23ecefe72f6ef61d8d697c630e687fb4b5cb93d9a572b4f04f4c53f98eea6b65156523128998c543e942860c9f65b950f41ccdbd43560ba7de2bf65459c52a85a9122612c23db12112e6e3c7fc4eb065638d406be4daa2096334c414d051c0f7d0508ad88535463bfabaefe9b20ef8ee7adb8af5f52057d2d8dd1abcb01b7b753d3e5ed236b1728bc808db303906c33a8daf47562c22a31cb839c839a0efdc05f9e75527c4867817b3d75544de1821c461d204083f40b022c5c550ce837f3fbd9cdff5dea15ce66e47b5ed327331b473f95374550c500c9c6e7cb34c8d6d61edda76f2fd2861ffbef85e12cc9a4ca77777f4d3c2e4967e3a324c34df363af76a64bb77e7cb6a3c418861574ad1bdaeeb8a44f26258bc5906efe86153d3b48c7455ecbaaa4874d50bbbaaccae1a4723edaa704eedaaad5d97a6c1fa2e56f96ee7ccf255df9ddb9691ae5fdfa2088bb023243d96794254d81026734cc7e677e4c8434ae68eddef41ee8611c2f79a7d70eea3399a536da3639085b2a12fd91b94975e3a02a67afb3bbc90edcfc6c43168d3fa5a30021de8702fd681e2400a34c869956a7c01115a910eb453ddfee1044516ef257b4219513a3db5466e5a252b061d695574676f44de9db05ba27ec80befb4710ef4a675d3828fbf6e10c8a2e7d6bc56e39dadbd8dc77c6fa46b1545b24d10e1e3f1cabbd9771c00f2eeb4cc4f07ed40072ec3dac16b7f55c8fbf65145fd1875a81faf93f7fde68e7e73e3f6e3d9cd761c00f26e6499f8a3c2ecbce550e5601a1b1f98c5004ef58b8acaed5497df703dca818afeba9b5bddcfe6684e64b1e61ead8cc114b414de6c9d7da004cf313647d822462963844760f25e6cd8cdfc38f640a18a20f004c136f169e253a240153647d8627637d76e22d1f9b6ec5e14450c6366f8306ed6e2b945d8b16b45300dbcf136479c9570e3e5b35f4701c9e4336b80e6a2361a599b678f43b4fb0f6d2ea28cd0be7db35064915db3aa19c84269160aed9b9d81a9f8ee261671591b3820001e05440a4101c1ee75ec5eb6023df747762f8b027209410111223fb206b878641689e6a24d0dd23ec4c7ada688375c5abc82be0d08bcb43b9a8b326218a5a7d3cfafdae1231611dd404d3486a14d51c524b30c26e5e1766d3cb21f61d36c8ec0e46647606273842d24943216d1a9286b17afc4588a9aa5c5bac4336ddcfe0d70022010810458fcdc8004ad3e637c9513e67c4866e0dc5e1420d7035375dd01318c7bd89beb3d1e984ca766691812ac67405947b288b06a388ce33d1318a09939d22359441b82b7b0f6a176432350f8f71db0fbe5c8a16de265dbc85fd608e58e95fbb5e411fa7ac561b8fdd7ef069fa5fec45ef3d607980c95dcec939fc020ca18e39cf33be09472bebe233e731732ae2aba222bf87a0f0c74b6883720bf1717a8051e0e388896677fd9fb826532252b21cc6557dcec4a40568b7d62e054f60c0da62b6dc770b8d9d96e8162979d05a6c100ed2bb00ed2b3c30ef02a632e1303e2300d3d55a1313dfbca339767da91205ade9938a6b99efd31bd9adbb6d96bb612a6d91a8b6b4ed19cd89cd99ca339e79cda9cdb9c4a5ae7ce3c9edbc3c5ced57e7d4e30d06bd39ab8e84ff6b95293a0e63578e71fdf9827cd4bbe314f30359fcd959a04f5969af356cb5d5a2cc354ce5b288bc1d44c916152596139c9d054b2fbac70a947ba1582b7b2986fa9dd22550c60d7f4474b7f5650963f2be87b72b33979eefce15436190cb40c275ec3390eb37fa6a0f39d05663da019e7b5239846c69dd81728194e6a381cc6606a3b28c5cd4830098944ba8d5ea19c7415d3ac9cc4031f89740cb25e4efa64010385cf0a6afaa3cf0afac0b8b2ecaf0caca379727086bb3dfbcb48cfae73367a85fdba8a69b45fc720cbe52d9cfd04b35f7fbc6a9ee6b25f77a9fd2408a6b2d601afda49f6e9a25d95746784af7687181a357878b53bc4d0a8c1c3abdd2186460d1ea08662fb8765506eb17afdf44fff98fa477599601aecd99518d2ab6a32994c2693c96432994c2693c9a4612c635bdb016a3151a321b51a97c683e8ba4a2f6da4d2cc156d241ec8ea1fa8d33005b6c3525eea0b8229c89823d926de108cd0cd5450e851b2af549d9bfdf49430d1abbf9c5e3d93c90ad35f17f4dd79d359361e26cbb475308cd2d3e9e7b83bbfb2f19896e98c0c03ed38a80777fb470e34a7a96856efc0141898d53c30959d32d3bb2c7b6bb8d98b434a7695da3f2c93fd65abc92eb53dd45cf6de81d99b87f4ec279b919e623b00acd8ee84623bcad036f1b29b6cf60b87d2a79cf409a33d2baca0720cdac12b4ccf0e793a7b66b2b0876e765907da71179dbd83200beac094135ec1244c055ec12bba7f9866e5d97ba85728cf60b2abd8aec5b82a5903ddec2c75421d78054c92dd95b3b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0bc6f467a4622912ee948f48ae5a4ab6c3c4896e9e361a9af0ef435e9d5bb59f69cdceccf09acc3017c237ba6859bbd7b50b8d95f4fafb467bf9e6dcf4ae77a85f2d26df4caf4d2554ca3f2d231c882f9cb1996bc602efb09b2fae78f570fa8b9eca5c35426602afb7c383005d4ab259e17bc1ac25cf6ec0c9fdc2c5bb14ab04c664d37a164375571b39b361e25cb74e62a911dc3283d9dde9d93613b8e643b55f62498a6f4ec48f42ae5d983f48a3efbb42912899e6235da5269875229a654a2512ad52895782895b85229552a695bbd646c8f33d74b2ffd59e99d8a643bee3f9d608672a92d95616a6e08b8ae598c457b60a02b97883a674642b15c73f3b4ce97a6ed86ccdcebda8680eb5873fd022a2f3a6f5a0fd40f784b87b7646d1781cda2343b123db342a012da3bd4503339a653f9b9d487fa347c570ed3bccf69390b39dcc99c29640ea43ea621a6799797efb2215cf96cced21a5c70eddde1ced64c71b8f25d690d571ed62e50390fef7cb55e226f0c58636ef50163cf8744e65c992d6119d9b0676b0f09b5824e8e90a55f40bb914e8ef6de501a6ab33fe0d50eafa6f59c2a502727e66a365b023fd2611aedf2a41ea69997d9453cd02af5310db18cfcf584469f7b64549b7859f569e289463a57feaa4366aecbfc6c09d3c45cf9a884696254b2376130dbf4bea04513f77bcd024d458306a4a1a2a19aa951e33c7cd6a83339543478787f86871aaa191ad67415d77c15a7a085f4c8c3c9a23b5dc87aef7ff19454e1728d270a6372ccd5ddcc17605666313b87fbe6a31cbbb939be57d939b331be5772bec3386302b2342b39053f378c535028b20cfca302b50216112396840da2f3901cc087019a837f57c0c71f72e4b0612326a67bb9ddd3b9d0be29d0200df4f37eaebec800cf34621a85184c62724c3705ed4e5aafe04dbd3a1d029399254f99253a63a278558c53b65f0426abe0dcc6eeb3a25718e96e57bb33ee8c1c5f471845a52bbafc5e71972fbda0dde8c97df274bc0e7bd89934e7d3736513f9446ad1a9972b6e8b640fd3e4905674aaa592e674648f64227d7af54e1742d8320d318d5018bd82222c9b224dc36e27650faa88ec9935f25ad86f5af34a5dbfa40e6499844c4226a19b846e4b0fdc30033274b1c50c3cd0ea1b1b70e1670b3f22e0820e3768f5658fec913e5632b9baa01d76e59730cd2fcba8e0cc3acc1ef0f21f8c10c6d7af6d87517a7a8f4094ff53070ceb558dca73f2369220a05a006e8c0c0115fd3a7c271d3b85acedd84fbd9ac7fed2abecd8dfab9111f2db7933229ef41d9d84f4514db23d23cd2886ee3caddb4b95744d7b6f36f49b35728229ecf4bdd9944eb246284c612fbd3705700b8329ecf208ed6c5cecd87740ad669f75c8cbb9a3918d5e09199dbfb8d847168b319815a2824b902d903b12043be871c28997f32e41e084134e38f1a478394e38e1846c811355c8f5cc76447e89fe722e9133c10417f8c8cdde93eec0cdce1d0bb5e01d8741337af7709a7821c8f983c18f78e01d760aa2404d3c29b27367b43b8de0ddcbcdb89ba9ba192bdefd7033196455649f8f0b4e9a0acc61d893d3919c9e9726b1fa4673be0ed428ce039ebf1c9e3f29ea7c22a8a33f1e2753dbc1c57c9ecf5501900a7854a0b3c4124b2cb1c4124b2cd1759d903f1da0d56ab5fa7441bdb8a0be2b68f027457d3bbfc18e3d92c5030192d7e0cf0a243530c175663083f35633856501b18044e08115665d6ce0634505a249cba002e1a48a0a4417cd01b10353bdd384137f5258204e00440a9cb040e88041023b1978c75d201978f70b2403ef4e174806ded10b2403efb00b240307f239638ab36c3c9e0744501f0a52f097e4cf03f59d400617bbc10d3e5f0a6a430105b59b90e4043748dd77e41db12d048689c1c56a9267031b7cbe0ed4d19f0dea912c860000062e3b3bb702d5812ae4e454212706e6652789201c051ca0172105e028e048027a12c0134410410411441041045180021460c711807300214a85ec0a6080808076c009b1b93705ed4edc0be4b288d62471342e6af07a9a6ca19ac3ec1db30fa1c59aab4df4803d542eb30cdff47b3bf0bae9a0bc1a86061adfc51ba9c46c474516cad6e2658bc82bc718630c0cc80b61169849956346a59a99c9a152cdcce450a972ccc8a89a79a1cdd5a0f10b637ac5d95061b0bb9f4a9523767f1107c34e277e681043c4610205185c8e812326f0bd77f84a4039e6c97e3f6088cd8862f6ec973d9d288dcfac89ebe934832f876fe642958ae3de89592716f6c7175bd48f26e295ffe1dec0364c29e593b2bbe57b0fcb5aa2c5fa3d36a8a18b3ce2bcde3ef88a5042f07d8c29486811793170b107667f76e52648b8bc1151bacfbeab90dcb711b1dd674b44dec5c0538243125dac26bbf1cdd1359272ca1963d4228490d439a16d70923a23b7c30049833d1f9344247e97b61eefd20d4523c5ade65d6a7adcdd295a69ab81576e358fe383a4cec88dfc68f933768ce24696c832b76d24c3e4431823acb10cf41d46d82a98f351e6e65e35110f7fc4db08688dc2130345d6016197208c11807177c4cf88e0e7c6ff1099c98d3d14c02dbe02b875449eff3605704b7eca6a240b9beb6c8d64814a003cdf0868735e0b9ef950187124040568c1e7bc561f0acca6ac40644f30159fb243e141f05acdc154bcac292b68f13beef24f3f2989ce3194f9ba7c3a626e9f8ef77e0c67a3597354affacec5d0ebd929777722228bef4686616f07dce962e77743dad8efbec7f760ec4262580c73d7af2356daf942e2125dd00a8987f2f518f0d42fa27794f5c7f8c95c7ca923024776c432f1ddbc4e5cd1dfe846cc7618b8a23f918d3c2e6882b71530ddf873a18d392e2e29a3f7ae463ffb56f671efe4f21ecd25e525e52b31d340291fb3cc932f5271ddf8139451475f57cebcd8aa29069150a2849f18e6c7c43c143ca6d731e63f7be2fac27c5dd7fb0bd7d3c5aee99a7a25df9799852a42645b6e767774cc02e58f60764b6498e86219961136cbf0bb251ecbf0976020fd7306d115e531ac66fc3280b10cb7fa82dc52f0392c527aea4e45cff5b5c649021b446998af619457bff48ae1d3313551363affd42b0d1b037def3015be2159ba25b61f7de3a7885f934c2a422efc7bdc80dc91a5cc49d3102d420c6a452e3ccb27e29b2f96208a618869002d684286212627f0ca71fb7347a77caeb5f103f42ada24d775ea7805fade69d70cf1933e2968915ea18048211d4d375ef2300d33e1e74ce0c4cf2ce99e126ee6aa13471f2326a443374884b4a7527cd8e1c6634773518b53a0d9b10b919fd73e3792889b64bb2143ae7c33c134444e6796d92ccb8a8ae6c4860152064a4f886ce9c4b2a262929182424bddd77e5521fdf9f9d955da77dd98601a22268c08d310919135c13444524617601a2228b308d310a19a134c43a4b461806988904819601a225be909a62122a21a8855fbc53222145a226d22a969d86714a3071260a0f14af4eab24586b4fc009d8a8f2d318b52a0f1dd7847808c5108b6f8b383b991483f5edd42f3c43366698053f133333870dc98bab1b6b458a1d86299e8033417fb03345e4616ba70547a1343d61835ad393e659ad78d58c3461725bb9d05ae4441b9946918c2c72cc3475dd48f4edea872a31d62ba8d854823d27817630decb1c6df678dd7f1c51a31368d2fd0fed35182506cc1931a7c5fbe1dd06b11bbde65ba710618eceef716d237bdf4aa61777777777777777777777777777777770dd385b0bb4df02f4d35acbbbbbbbbbbbbbbbbb9e8e23edaddddddddddddddddbd7a3a629ec8061aab017c2a02a6207dd88b6800b37a70aa4711bc006625d16dbfe0c2859fb9104278be1766e17fb0405647955e296d92cc46edcb41d6e964a2cdc1f84335f884bfee9ae30721ba9496966cbc8ee91ee42e1df21d7cc734f77ea0813ae23b1b5cc3f7ccbb01a5e59ab35163e27db95dcc7d97c80fb763381bbd7a79b9dc1cb3389862166c8b2ae2b204b8a9cb069363b09bd6757e0c532f0ba716ca08285036bf695de756ca8ec88ace2d2678d10631705ab2321752e404e1b478ab79635c226f0c94cd1520b2f10bcaad2c503f9a6beede74185c70e107a725ab8c01139ee8e0b43806767712ce862a3b9fe6e9189dffc3d331cf0fe2e98825193f7da63c56e61bda3f53ea6b952a0fb5fbc1a27723d548e91c70e1b5daf110c337faa36af906df608e31e66a5c787be38d9648142fa7d27374abd4373b3708c2129aa461095ae8f090146470069e2414210744d83abe20139d6f7a3a2ee698c41ce41bdaed3670e3398a1e8db26e2d26c7d07ecafb885958b30e9ac2636c01a48631aeb053c5095a4f06631012e2020911e8a086d63b6317bdb627450fd36817fe29e1e960f2744452939fe7440e310d33e1aa1caefceec8aa12a61aa140e53b166a423b167a3a34db2531591cd34d368a2ce255b2e8c137074f47d41e2d0ac89c96081c630ccda2e66fae76f8b783b7d32b9245fd90a42494680e8ba7231efec13ff172564442b156e0dd80c72aa9625553a9a6f6e1c5255021dae7e1df13f8d703ff7ca69022b07bd70b1a5b487cfcfc8cefced6b4e98510df38eddba239f8b784895e751da7e1c2c38e836e935e25810a21843e2d62c4b4c7ed31da6ebb66511115cf57d3668c6f8b25ef9e1617cef939e7531e6b05b42b643ee5747ba47c3ed6f9ada2b0c76b15783a4476db6cf7aee8f05abd00562fa01d7302003eb66fc7aa0fbedae314e8f3b18f09e9510a54f4d2d46aa71d8596366d764bc42f41ba5601d31d62ba5996c46463a7e2a1e50a3794f97aba21da8d16c53e2648a01550a257d476a5d3e65ee92b2c371d4e533dc114bc4a45454be10a8b3dd140c2031f9c56472f8c67b9e92b15156d1147582c2afea6a58229f895af5854b45c7330a5f297051593e5d68d2c2801a7d57197618a631442480983987d4cae5dc0c73c761f98ede2a74561e7bb1df5277af6ee61d9b74c647a0f6431d1290c92ac0f6b80e660ec0215fd4786d9eea1e2e745f351cd47fcb4a4cda73978927d4cae7dabafa739f87a2ebef074e11f13484f579b52a01df6d145df017f7837f8bc1191599a77838fd920de0d56357773b166ddb4aefa4a4063e57bfda0f449c9cae9a5bc77c417dd4bbdb3e13e96c37d2bf7b555e2dd783941394850710464f9f079e243f53ef37eaec1c0b8082c02612f810f070707a708bc023f07182e90c31040108238d0a0756384371c19e2a10309c8d0a4c5673ef68592fb381b8f39bd98b417a4b93fd1a9771c2c87eadcf7fabd480c03ed1ecf7def8fa757d126f1b0e8d593af8b68eea9bee3dd070c42c42206a312490c11fd2a02872811997c914444e2ea9eb82f48124a0ce9221003f00661bd73a7b884183b72cdddb703daf0ddc663b878624cd56d2845d1bb8fe6de0dd0a4bbbb9108725d419080eff8c911cd3dd10303edf807894ebd23900802e5102dc2db34f746b77f8a6896013af558aaa7fa3bf74edf4f710c137e051f3f8528319194121361115e5e6641fb8d49d90d91d1748770b4083aa46491942ce38bf1cfc658e57b5950791d377ee6ca337331e014b35a3c2be014dff81cbc123d5ec5abeb3aee8e1655662e6297984d92c92739095b20c3ed7e6509286fac8a59309392145c84e1bec73a8499bcc682bc36967669bbebba906bd3444caeec6ebe62b41dc4fa9217cd6cfcc5e0313b5bd41cf70abb22b458733d6a3902daa524f493d27718ca3b2a63e5dd8ce83daf524d95616abb8cca30457a4a45a9346326b108ac8703472592edce71aa992baa3b7098c9cd1657f470664e57aa5ec33d1de48ade3894993093b7b5688cf78304167af1c132f09a871b8feb3e9048c20679e42ee46cc838ecf1d1ab264810e01bf0454e37022c73806605b1699612a44bb248b00cccc226e5320e05ca4d8722e5324ef2c134a43bdfd663bb26e32987c274944321e3292725c074941b21e329dfd12837dd08949b64dc8894cb80da4924d2f6579a275521af46fb8102b209e1fb467fa4a98d6a562356b51986294eda98b884be772f42a4c640101f64ad7d8831421efada0a2f31177e7610396fcdf369d724f37346157f80f31da6bda3db1fb3556dc7bb7a08f4bd0a218ddb0fb8f2f0442f166f170120d715d9b34c5b1bcd4199a357fd43aff8d921dcedb745fd88b715e0aeb42a1bcd7a39c13ed6920b5c5c817f943c298224c134f2f0129e02ccf28186ab6219781f9085a3270b171280c214fc8e963d81f73cf07490de8d23419e8ec6797260039b8199196b8ef93a373e46a689c76e0ccbc4184552d20be50f9480832bba10230071c4376e44e9b28e87cb7fcca23015b577239effea0ccbc4cfd163a20f444f070dbef14e004ec5c71b9fdca0428cdf1e1cc25dd165bb23eef51424f471e6760ccb64bfea104e74a5e531ca2aea1d7b8c3f54d4b3331c10e8296a41df19e8d2c2860afa98d831c80a027481aad629880951ec4910c83af111d7e191e855e7e38a3a161ad5ee94e89eb8a27880e6e0e1219c690ee6886aa036ae8e5300ba58133c589ebbf6e0bb4d2ebd7b8ba15db4f198a34ce3faee63ae2a9d740ab302cb60ef76d021a9c037b0730fdd6119ec14b301a52fdd13f01a589601073368c1e99a823b1087930c8239ef6224601d38431783ef2e135cec310df4c516d0777fa7487b97ba0c74b15766492abc2d840315592359d8887efdb2464446504310705a22fb73c183d3e2d607e01863b4b2e87717696cc254aed7125594cd6504052ffaca2fbb43a5a26c548ca0e0576c7c02535804bad855ac0f98c27ed38249287d867d5e97485467aa0aa6b0c31b50fa4b8b6b49af64c020292ef6531928182ea6858bfdaa825e2cae906160520c7ae4a2e26287676066d8068673e0f8818b3da2816201d96d7a9688369b1e2cb0e8c1a26749677d9b1e2dbadaf42ce9b3931e2597b71e4b50d470af67d9b16be391615b266340af9d5e75d76d7a9634b7bab0b0e9b9a2e70a2d9a8545b3a44ea7ae1e9e4cbb215d8280922af07250ae7d8a3409026a237760eaba44d9c4bbdcc51a914860ea7afc8dd481a923f4e65e8fdac6c3a64749737cb3cea04dcf921e2d9aebb982516c7a9034c7994d0f4fcf15bdeaceac4dcf15d407450d4b7ad5ef680f4c5da73e616437bdd40359453a95fd12e366b3d49689eee58860e5ae56bbec510cddcc0e21897aa76b9e7bdd54bb784b2527f73aad1d87715d46ed1e16f77a4a6d22de0ee5e75eef6bc5ede42ab9d77be7e77a33e925455cadae7d60ebc0ce691ceebaaeeeb92ebed7e85d73d1f1170c06bdba1706185f740173ee7372af7397ddf7ae93d07117d7a589b0513cb5dd491ec5ba9c643bcca532a75035d727241285106d37de7aa0d41aedddf7b6c3ad88b7f5e041e9a3bf8b72d227d4a44eafaa78f7aaa1735bd49a8b8441dc7a943efa8c8d7289722949d7eda0b8a4c3adc77b0da3bc441a0a09a5d69969f3746ad3720c16c13075d2943a31c61c086b7a90ecd0dddee5e648e7ed07a9f9bdd742dab61ff2d2cb4bad90d24b177d96eae893daee5dfaf9aca02dc43442b6f7edf9eb5da54e95553cfeb9a4f708ac404947b9db516a8771f6eed6617daf1ba0bbfbfdb8f1e8faea407b4e92d469eebab482cef749258a9222c3a4d2ed032ceee8a5c70d01a3973e6be69defad86c69d76feba935e44b1db424c337fbd837ab5fd7a83d1abfef5d9a26f3544de18b7e78ae6b73f1add2d5f40b74fa93d241777d7d43be346fa8b026d309abb4e3abc5aee04dd4b8865aede6ab24bb21dd4dcf5ad7672e75e8f20a0dd08c9bdde7590cfbd4eeaebd205f452678689e64423f953cb9c53c42fdb78f4d6e3dd868f3552c781bbfdbadbaca3779d1fd579f5d53d34cb44736dbb212fe74edbddbca5326f3c687328d74d4afff064ced2a3d7c663e6078af1c9063779abc92e37a76da58d074b106036381b39542c839e4c9a899e4c9acac6c3f412a386f1cac6e34cbb0cdcd61ea53356c07782a6b61aeca1cc32f4a2de3b5398ad26bb33cdf15f0ce80c007ce3c1b1b9d309f5e3b6bd0013cdf16574805b8c7d74de6ce67c561f4321063a485ea6684d8b5200b7284c410eecb4b8c346358aa7c515702a0a07ad594b4960b2a435e5a3c209661852697169b346d15f84200e405130932fb4a6add067093dad1e9c2869cd4c8b34e0b4e6489bf5746e02185ef49ca6362ba77a411ace30b343a545ab59b0832b28f1518b30017d99a2f5a47085244b5a24309461496b6adaac1c44879ed6dcb459712cd1694d9236eb0068d0d39a295ae51e325c21d49a549b5565069cd63469f5a5810c530cb5668a362b8e181c01a8356568b346c154f802c8a4cd1a850e17e8b4b0f6e17daa68b3aa5e20d49a2bdaac4054e1a7355d5630862afcb4e6499bf5b121c94f6bbe68a3aad2a2356b5731052f785ad3459bd5072a845a13a5cd7a230a40adf9a2cd2a80317e5a13469b7500423fad79aeafbb5618e5257a58369ada467a258a42536498aaca0acb89d65a5b5c502f3000b88a247fd31ad59bd69cbc5dd767157d54b1c73a379bd131f8d1e766332fb24682c014fcfcdc14c0ad23600a5e1ea1fdb8035ed7a390ebdb8fcb62194ca190ba0d5c69e3af1bdf5ba2caadd8a239889ee8c915889ee84915889ee8c92adecd15e145c1642d46c7005805590760d6914469aa6e438d88249e8e8c6fb0584de6186e77254cdb58c73bf26ef4314c4aecc8d371802158a6ef35142fb30cb3cc3bf2ac409f7cefc8cba18269de7b65b8ef6fe3f19ee8958ca2d6b26d3bc026c4cdb3950911115ef5e5bbc8761c868a0d1564a9c0ccc498580663997e0685f95c604c335f525e52767747861d16ff7844eaa2b8d2c62688bc316e1f6399d7457128a45dde77e5fb8e8e92673ed3cba9dfe310ed77268d8641df9909d8b0c7e48e1c5fa41414be336995c281f6b13303282fe109fd6b095038726769524bb57e53b801e95fef0dc8063a791fcd654e4e59230b79ef1bf9da0673f4eccafe904016f3b353d837fb3bd2abee157cf647850e9781d7c071b819f623595c2323443ff28137462b0bf91060ad9417b95b92ba9b9b84a26a78b8f2dd922d862cd448fb8aac4ac027050d2201a691cf504650284a19ca881452c6cffeac903bd927f030335036dc4c0e373b0659291665437aca53ac9194937ed3ba6ccaa45589dab1d08d955f6bb646eb13ddc389dcf0881ba4b9ec578680728b5bcfee03b2549ebd089b23a62ac7b66dbf361edb9319a679379b611abed95517addde26d762bd6d17b9ba715b6644d22e3574d82f2514d429f5273b056ca2508e868c6ed8895f3660485a94bb336a4af58232af6084519599bd24dd6488a3d92f20f944a324e51b4a75494d32ae3a3dabd1c9b94a364541ea1a36bf22b21f2fcc5cdce1b112c862bed4c73598ee654cd659f5104a3674fa25906d8d15cf61e4d055931d9bb999b1db2a80dc24196123088fc4096dd00bd92cf5eaaa44747e72cc6645688cc6e6493a698f04dc4dbccef469aabc92e12cdb53d8265fa35f0884ef531ac4ffc64360b09d6ee112cd3011f9065c3864ab3c123e25058f9f28574609626a01d3d5dfba6c911d08eded3cae167cb554fc7f616de6cf7b68d8879356debe1f2eca65ea9a87cce39bba89d4ea7ed64bb9b7bfacb0bf6ed84b2425eaec19c3fe31ba38880b15d5fed28db758ebb75989df172eda777459a37209aede1ddd8fe828279cb5910a0fdd0ce2ddf98679ce55d8edb7297baf5389de769863d9d46334ecf4ed8c6a3e50506a6f27d61969acab976f4c236f2c4b6596679dc7ec0db728de5a7ab9e0eeda722cd1b8feedd162b84e346848aedfa6ed7ce7296d3e73b986799ed725c96bf08e16f3fbd83b12de78d88eddcbd53ed5e6c8bede2adf1be54e6c36c56bbeadd60f96bcb51957e07747161e9e1e9606dfbcb8bb6bde52c6729d2577339cc69e5e6602eb71eda85b8d897c78d08ed6afc9696c38d47cba9f2e52aafbb69e0f6f074b0fcf497f34f7f697882db0f19ef561f493ba1bc6815c6ee802c55e52d3563ada5a519a6a5e53b608b6963396f3f444177c59ef8bec9525fcfad97a310062bee44d16409600e7fda88e8e1dd60cdaade8d1ede0db64520cb08133dbc014c23f6d4f20bdc5c6d3bf7b05522f0aec0ab75515c212f3c34dbdd300968a772c272bea79697daf22c83394b55f90bbc30ef8dc81be34a2ae8eb4b96d359de52bb9bdbd2f21dbd6ddb7bd497f3c6a77a73b916e9fb627b78375858be03b654186baa2a2a3ee8bdb0fbe1c24f39025a448efbc59df371f72e6bd5ee802c5ae5e63496cd12819725e34df574a8bccb713bad8bb72bd23d3c1d2a56bb9bcab7cadfaedabe55ee6e2ecb7734334b75b195aa5c45e973f44a8507b54cf90b1596dabdcbc2c2b6877743f3d12b9595cbb58b800ad92eefe19150a257bcfd64bb1c36490ce99566b543dbc3bba155977355396f3c5cbea349a7ca9fbcd5b4fce5a7cab68b77426de5e5a70af3ad47cbcb57eabba7530f9895baf2775fb8c27ca5c2fcf41ddd72aea7afa89ceabc8bca7b783af8aa1ca67645ce39ee8eaed8e514f45d0c01912b46297693e92a33ae65873900ee82bacb76ed2ae77ad2b4a3c040c2a6571c85373cb9f0470061b9f6cec855e10dc8e9fc1d8d7ab9663b14112ddfceb7c56edaf91ab97c95ef802ab56b394a3dbd9c5b88d0be11c172956f2df6878a3d6d5dcb278b56bbed2adb4f2f6d954565ab2c2c36e50554b3fc928a15b272162b0475ed45fa6ae7da6db648ff273b0f372018cac6e66e4fafd90e007cfab7218061ce1b02b2c33ce38c6aa76f2a1afff42da31affa4623b8a69676dc68cd319d7ea0cdbbdaba99c6d3769e53f4acf7c191a6bef21434e4151402e7fb32b7f16f503fb76ec9bed46574588ca4d6c52b9e9a6daa95496d359586eb69f3696afbc371e44faf2b19b4c57e9c162bbbe2c477daba7da3d714f2f87816969b15d91bedd0520cbb3bb9c7bd5f2edf496cacd9d564ea6a31c5589d43cbe2e554891be99c5c0bbfc6ee572fbd140b09b6eb29dcaf995e56d44b065f9565b7eaa5dbca7b7d81df0f49473edcb725ef96663732b2c5688cab1afd4ae485f156bfae46ba237a5769d913befb2f1a025316e9177319b2346fc78b523f14efbfc9cf5881cca36cdc16ddb0ecf9b3540bc23ec3647f46af2e75cede0431f7c3320a2a08b59ad399be306e8d56593d4c30a5542373bf0001734c869021a526084160c82083929b8021590c08235b4e099807f82090fd0cb337a001e60ec0453103e5aec4291d6f0825af77b178450ca5ff2ddc85d30de2e123172e3bd2e84dd0b4401792820ef624fe0a106d7a928e56d3417dfebcbf3f04e4a29cfa97ac53d96ae0480f68ef4f26ec6f66e83790773e5bc1275e5e84a972bb32b5bae94a22b5964bcbb7678b77265cc3b952bcf42e39d8c2bdf4c83bde51dcae95dca95f28f69b2bb5cda8e3bfce4aa8abbe42417afea15db9b6ad453173303658c91eb95edb84795a35771269e46cc0eb61e0033605e502e2d2716f915b9858abc49867c8a94295579526596d9aafca866f5b18c3c26aa51f66442981ca3d18631c7ac3e3a155f10bddefd808373af187fb8913227fa0fa25537e4621a73a2bf8a6554839688918bcdc719e315b51f146827c1d363b852c538851debd57626f2c48df192ea636ef4583b0ec83bbaf3dd09e7b1fa361ef36a95c45d0552ee7bcf2c6676639957bb266e7f543bd27d5ad65d60c3a4763b465b2a45d61a5e41c5a7bf9c4effcbe9f4f2d3b1d076371772efbd39e1c603c34c54ab2fa7d3e8da68746d341acdd16864639aebb08f84608fd847d819fbc31eadea6296cba12ac2264279682bc0ddf883dc66b01821965d5cd0978d5ea9f4ae25a319e3d699151e73f22c2116452df77459e2ca55b9a22ae29494f0ef8a31c618638c5154b915638c31c6185d5c48d15e2c232fd2c0857f3ec83fb9810bff627c22d1bb8420eb309907ca3df734f39e0ebb0c218410420821841042082184104208218410f6dc1aef301aef68ccbb13fcad4123c676a70b217c48948004da03f9dc407920cb0689c78304f978b6b0dd1161d0165da144f2441e1ed1df15cd758fbe419a3bb2f5b01121618b961ddf9120bdeadbfc5e3784314a795d22edf5d008315213260cc424484bd1655f132641a0cd114800bd2630f7381b27c80ac2a4b9ec0fc2f79bfb2cea4711cd6d356da3a26afac2ab9abbeea82e81ad441006821d3bf57e65f5d4af0941208beb14935ea130a7b2dd9019965b50a8eaa7a4b9c7c35b8fec4669bba7e4f14c413b4d5b3ddb836d60aa985e4578be3f3f586e32c34cacd6dc0f4501841046080f451611c2f864074b780115bcf0d3ea77fecf0504ee4a1c3c9c43dbe5e828ca269bcf7ed37a289b6b9baf897859188f2ebacde8a26701ad0de99735729d7423a28f7ed3ea9e126d5e7b48200b65537af6cc1ad95efa4deb1dab463ad59de976efee72df1768179fe0a13c7459b87dbeff76fad8f300edb8e789c8f7d98e6e0828f26e5bd1dc6b0ee52810ca2434daf787d3dc53c20776114a7e746e912c4a5a5402481fdd0892e8a3438129b9ef464021fac8b2ed1ecf7da33f284a273d5e84c91bd0924591ec4382324274ec9885e2bae8dcd22cca66f4762cca08eca38f2c14d9b173aba3475c4664f6f970b7e7e7be2b898df96cf292ef7565307bef6197ccc1cbb97ea1264140dfb90f217765edba2779b8bc216074af1e2caeab478b1e2c2ebcac4d0f165740785d363d5af4f0f45c81dd1e9eac8bf9c9f55d52fc7c0da3bca22d42bcfe5ea1307439096b00c271390a4f904213f1469d4f4c541f73089b58b944a2c0b959c7df5cae87424c75a3fc42e6c4439154c525f0920f0706f95c28dbe7caf7f37e6e3c8cb291b44f238112768fe7ca167a43f1b0e65d8cb11f79d8037b6e3c7661d707491b0f29e3e788d4e77ad51c7bac60562200ab1df632d2654fcdbdc4f44ac6d3cb6328cbc45f36c9e8f1f1a3aa44860c1a7befb55d72b9e7f2a58e1aa80c283c61cdb50a6608f1e376ce01ef7c6a6811e3a56528065494e45df6f834d7120453fd4e36e9b0db12d4220459232c6b091afd401b98806e5c41bb918f895b2620084d40d00424b71a005c53500d6837f2b9ba8be26e3ca26cd16916f9d43bfa91a39fab84c404f42670b36a02fa999f5b7d7c030a78477f223b5b74b47082db4a80464d9aeb192dd0c2ed16aae47666996544f58e7c463ecd35d6a3262d413376463eb7dfb504596619e9d3abeedd26bd122579b29fe5ae300533b4a749415e22ac059d8d7a5a40c5183a5d161e3e301fb6f1b894ec74d845828476980665c89021a385948d01e89a01432345c54451627640d5e02dfc40898cb31a244ed0931f9f1e16e80ccd61cea8f6c9d38155f16e603bcd612fe861810e16d41c76861b1cf79f4e98900e0a0a4c4d79b27db1056d67d884483c20f19096908a20d2483d295d74ca935ed978367646f38ba76303da827aa512dacec02b551b4e67b1db17177e13822cd5108907ccc22104120f64e150425ad2ab219e908ac0ab21b8b022b422242424d462372152cf06d406a1d306045942cc805356b48039a01bdc354e62df403d477a5041854e94126b3244931f91ec19a2670826d815a7a01d4b410b2d96346b46a6d5c002080f102440ac00b2d31c902a80e800a102480e109c1d5e6eeb911d6abcd69811f31a40a680c2910ba40323b8f01db6820bbf64464c0dd10eb606e78b1fc89af104b26080302658d633ea993a5383a15167dc5698d7facf987a24a6dad3e802c80b19940489921f885082b0209670b5d68080072005e3e800fb003f918c626c0d4e174f874a08a857433cf1825743704102d1c5850722288824412809e2072220825080080222080322e802220888a00b77825872838400a30cccc1b741486a810a4e48424a67088fe6e7a7a7e70708b25e82200bf5f333a7a6511fbae003900f5ef8d0051f8256495648564a563f582df101c8072f7c085a255921592959fdc087201f827c08f221c887201f82564b6ae0d4a0e2c2d7c0a991d32b794ddb36938a8a8ae913a592a0ead4145412d49d9a85fa5381aa173b35c8349297a8ead414d49d9a85fa5381aa1775a7eed49dba5377ea4e0d1a499348250cb483015ac2a3446707c98c23171e666806ce3644a27112090506856525c346523404040890234070580608143e70e15994ac64d8880a28479aabc11912a35933c2b8f02e412e42245249c57456517103ab0ca9cc01aee0ac206165c98acf0a1458563acc65e567c7561c4c4cb509151c3d48680d4ecd915e5d2a43bd5209b5ca1c70204b35b4820466e110c2ca12c8c2a164c5a757433c598102af86e002cb0aceca0a0ecb0aceca0ace0a8e8b5dc159f951b541252474e3643b8ec576cf308a9353223d59226b4d07f806bca8d67c80658e442a74606aa7392b9a43c25343c5d6e0b00c7c4d32e3574d028037186867636787470912c87ae1812c94ce2ca528dd78a89c6e3de2257dcad06ceca884de8d4b3584430838940cf164082e64d8488a70f4a886700861082e0cddb89d6a08530d8d54435235a485a1211c3dac634387657e7a6ad49a254a608a07a6aea841526b766aaaa8d1a95151093d1d351da8f980500d0e1866f84016cccf4fcf7d590259289d1d243c4e2eace2e918e2c9bbb1f302ac8a8ba327c582948e6a483507918632af56a9428421c4901062c818e2897c72e14b55082429387a522c50cd4184a307470f0a8e1e1c3d178e9e215cf8c6d14395b00c7c03d12e252828284886cf926a9dd033841e6831b4069f32f420d102cfc9e6a0a458219430b9f0409440204b80f4f44a9a68546ec13013703ef0c6a8c1b12104593540b0262768e8c22f9840c575a2e3e449b366bcc756fb840b3a630c2969ee0cbcc2f103e6a0f6ee0607778484186219f821b820c67dd22b21e2c01c7c4f4a4a07b2844802a7c6587287204b881e70ea074fc00287925e098103e6a0d08d325c789494ce4ce9d0944e479fe08494ce853b9709383b171e8646adc9812925387a7a5806de56f6c1d103593087c7e1e3d38445e58a2b784e5aad396f595bb35283f305da6d3b3b9b15cd9a71f8d932512f00f884da56675c1a35e2e0b46276a8353aa85ab35393a38409385a286182cdd30cd82c3356546698b293addcaa303f0c0066cc807941019d5a0247b8b27065e1c2c28585eb0a5d7481842f9e5c5740c2174f7e7e7e809e38e902095f0039f9f969e2e4a7c4bdca3039554fc7b4dd9b6fce673b4d8bd7678c337e36d44ab2b1ecd9e8da9c733ed2dbb64c9b28dfd1246df449faf62ec7cd4ab623954828b61bbda3a49748b6dbb49989de1f6598c87697bd8f3e89853120618e2f725d63d0114390189edc1e2e47410c575c163ebb9ef5f97ad6f04229fa4504de4cce8edeaca1eda0edb27791bed7bbaeebf044e07d59966598edb26777d12edbbdd1b957d316e93bb2d975ec326917166410e306e07214c840869efb4a70390a6350726709638a5df57494ec157f8833d176db63f617e355bb99be321375f1a6bccb71b177396eeca1877783f452974adb4ba548af7a3adee9f6226d89c06bfa8e46b94efae93b1947b13625de922dd2d764bb0bdc74326c91beaf7494a7546a4b3d3c1da3c7936a0fefc666f98e6c8759d3afdac3bb616aee590de55d4980c2f0749a7421723c868944efc9f72eabeadd88d7c6e3c518af1cefba2e8a7d2d7691be700c538e80f2639a7b4f9a8032558d60c51833cdcb9ee50851f0b409b5de9e9529a01d0637be22a042b26bcf4473bb5637d145e7ed3ddeccee80d29a4e2fa6d34baf9aa3444194f204f4fabb2f22a00fc273364671f477bb1ede0dcd86818a1f1e8623173ec6460fefc653bd1b714484bc323e1dd276379db43dbc1bf117b8b9d7bb8e9b135d98ce94e798d833f0d7cfd1d3a3527432c94b1dca977d79e5310933593b793944bbd06ab14709eff5a5dc78bcb328478cb17b50710f39725c2f06210a62a8a2d4bdcbb1cb71bb7e91be0cc5e52bee002e47c10ba13b7946ade3eb3746a278224271f9be7b5521448cbcda710ff3bb678b749783c7b5f1b8aeeb5ac56aca0be86547b68b57f6e86e84407b65b6485f78ec3dbedfc3d321b242deafbf5fb68777435aad39bed176efd0f6f06eb43535c79f2ffacc61c915c0e528cc21e7ced29c57e477910891ac7922db1979e276fc227dbbbbb9fda0d0dd8c3d3e8a627c89b1e5298ade424497dfd19748243a8ba8d6dc93afc93039a64311ca32d1b985783418fc609386248e18378211c3b897d0bd0e74afcfa17b9d87e6fb02ed3786c9c715fcb9d77964971565440b14f22de756ac28234e8f3f7d8122c1a259d407e5a753d74f4c5ca48e0dfae45e672d507ee415f73a5df3d23491b6f5a07c5eeaf48ad2d3ebd9242eb20a1d978b624b3eb6c4a4391a50fae83cf58118d65148adaca239b9d33e5cef4e28f5e1e0dd0035dfb5185ff08e6596414d5b93ddeba4cf937ac53c48b609d1ed86f0cfe5e6506c87aac9ee767adfd01d3273b9b90ea58b7b1de547ce80f63b1427f7fa567ba8b926b8b9cd76508c7b1d0addeb1d0cba3c543efe49414bf77a162d14f12e97168a2ca09047fd97fe4016ea2ebf609ee4e5b4890f64c1d89213978acafef29b16aaa2328b4a80bccb6de45d9e65d626fec51a413dde88cbf4c9de5127973995b54df2f21d9d55daa4b98bfa4056c9c94bc949c949739744725df2489deb720755afbbc49d7b5dca80720f8fec13f59bd685b279797171d22cac85aa8d04a62e97dacd24cbb2b9a3371ed91f9d7f4f9efc40acf64f3b69ee5251d9786417d579acf6cacadf90bc8b7d43281b97a38eb246e25d6ce3c0d4d54d7ac54d985cefda4cdaa7b98be5d71f4fafe0e995d1cd92a10359251e98bade16d38ef2aca2bcd194955b2c757458b3c7caad124fca2f2ce53c9945f969ee3aacdcb5e9b96276b13bab1da8242cf5c5b19e750ccd8c400000009314002030100e074442b15834a02b9bea0314000e98ac4a645a9a87490cc31042ca188300000000000000820030000030f5ba3b6df8e2a98bbaa483c84a878344d4b604ffc14019da9479c7a96fc80fc692e36a5f9ca7b0045075062ccc4d8598e78068ec1d7394dfb2aed10356561fb0587c09cfaedd38f052e3d2e184aea53e1eebf7ec97b837f5ef25f77b45a1110d4d31927952191c89d0811658e1a3bd962c010e0c9fd3951df86a8258cf69750b521a0e9898dd2329eee3a1bbbfaaac7dc064b15c93c2b082c7ce29c0e927c30cc9bcfba7a856e8b136d7949c7018b220e2e0e8c7ea63d7a17ec53a9555a123b3e39e6b62fb0e53b8186b9f59157566ff1dce0a2387490a9b139880632b89b1f3e9bde1c54df9c466c8164567bec965828b1bd728e6b8ccaafc998bf0f592f2155c4566b1ebe79180c12d0834943894e6701f3705b71603592d1b188bd132b4080fe777ddb3149e901feb4cb2c53a4e5506eab5fe9f55d14d990842b1b8b9fd63baeafcaa71f390bc2de396b30eaed052f1be6598e31d1418303f8ecb1c2c78427ae8a3d95eed9fb728a0a2b5876a32e9fa5119400b1f501cec3a5275b4eea20ba9c5091447cc29b3682af356255c2dcaa095329427dab4d59e202723e869521ed670c0cda76a90fe59da341d79249f7836f8fd2f4db7e924237fe1c774911735491ca16261c1446bdb29e4ec1afae4b96b5f50c5649f29bc752b51c3466c4c82c78101c70ab92b7b394dcad4fff0428da5e40297227a4829f6432119d4e5b9b8fc8a04df19bee011441ace7e6e85f3cdbb7fc63a371d090eaec6709b04de9d3a6b320aa27df9f3fd949682de2069a96114f901b706c739213b9334bca03c2c85871aa136d203805004ecf46fc34144a81036ff0df49866b0a126ef2b169f09a2f2175d896e636ddaacb1cf1a0dd6ae6bad3a61ad4a9a2a6ef2312d7dae4bbbcf00c459c0b651198e1d6251c5a9fb01b716c95fae1bc1694909d12f3c18cdc1aa7017533acd4b8afb8ec49182427ae18e7d1a067a00e2f46040f1b5fd1fa6d296f2b44f092093f5159bc2f46476d617c9e8fccab072daa5455b9c55d9a8159155d3c3ec2f3360519af234f3545a58a84c1e0ceb0c8aca87dc9494c6b4a596f66e409930910269b8c9dfab7cd34ecd5ea60fe991842601bdc8c41e78a8450442da692d13eeaef5774bca3be4368930031a3b91e6ab4f37dc345377f432def9736211f8e1c9a1cd233e5ee8d9d820fc53f2cf1c63b2b038e610b6226c49053ad42b200472f2bf7b5c3e3b7ca2a188e5edaeb2e93aaec504df8ee601bb242fe9598369ce2b2b48745acbe2f62c867a629b886e5b627bb7f5cd91a552fd8afc7785b0024aa305e75be86464515b2b9a2f88ca114eae87648486f775a4624d91fbe8ce2b2d9f42f0a12b7c30905ae5ab2c459260d101c98539bf7b7748e51c7a739e496afab937b38d00beb6255b2faa8dd81af65ee19c38faee8213daf16552d29b3fea20526f070930695bab5327adbf54b09c7128e797d6fe520a6081a9e3304f2aeb8d49a348df7717c8a3b5d805c2f6a004f410918e0af4fd338f58002074100b2e818aa8704299e591a49fc245af5474c3c7e924626f1dfdd456bf4dab2cef2e1a003aa6945d29e80eda6e89fc44b16d08aaa63864e0fe1ead746ff2fc5cf3c84bcae60cb50fb52ed7bbcad9a0db9491a43387ccb60228e27e6cc05abbab2110180d07b6867cf44a6b6d66e3615f9f727e2b5a2599f2f87647d43dbcf05ccce06670621c90b79f7dda98f1bab7e1aa75b94e2a1468819eff1c231691d845ab10872486ade8e1c99e0ef44ea7e221c2a9309fb8f910a9753546dd1e41d7045cb7d7b57bc44ee493000b504ff8070cd63c5e7cba851a96835177438f52b07234102d16f1fc464a42ccd4c9d3104caa5fe98a433180ed97384845c72cf62cdfc716055f479a0b8701feb94b23828dbe54a45f8da02f29dfdf2e527765a00df4e59a84c0ba9c4c33abe55cb93032efa2af9d4e3b6ac7507f45c3312e82ca3b1dc2f23536e19d2fa13a9a86cc2127ad4fbcc3d17d484b934880b896800d6a8bbba405f75b58da4b4c2626728f625e00b9699daace5f0dce48e4f09bf50c61fc19d9d4d2032c820e0a699d9bf0beca1d8dbbdf63af31efd3dbab6e6ac0932b2f6658b0952cced7c02fe59c705650652faa6ab2f3e12fa3ffa6f5ff1e1e16f5d07c4ef3f6b3c22816519c3ba88f82e1be7461e14f13c2e7dc5bd6696d19a30509855c3e99751a77a41550d80583fe0aa5aae687840e8e3ef98b77ba78963bbc1d65dee4548380fc4fafb34201a5351097bbbf6b69982b53c054bf9f404681c9d296066095f9c577a6b2759674a446cc80ab4b1b1bc76566b413fbe37f8eeffe516ee7edc57479ba631470d788d8b7f9c01e2bc6e0fd971f4d65f71c4489f52dfcb7f13130177af26d6111f0624baf955ef51d6c15838adff79478d25dcc055127d23270a63fb912635e48d4848ab2435b4a072e9289dada1918657ac5d83ec430161ee6aaf00aa2c4e908c475b2d121470c251cdd8f6a96b35ba805a0f1a0d91133f61fda395e40b11eea86185708f90b3cfa029f099695407e4ef8cb97c4008a7190f5877d1c31fe412e78c09f46b2e1c0b492edfccebcf2d7213c35becb7109b56e43a367a3eecdca4046d77d5a60ad396365616a4e1a81ec7ef16efd769e193728bff38c372b29df07c45629ec3d37812d4e9141b461a56ce000d6bcb3bb2a3a214ce1846d66e10fd307c51f7fafd6131ca12ee79fd2dfd37db27522be7f626e04d5e90f1ce3776eb7bb4d95d43b5d078c35762400b68d9d6c5da5d106ab81dbc30f5cfef43df4e9828defc52984bba11367581c2224efbedc761a0d41ec3dfb08a9abf78a18b0bbaea8519768d47311dcab1dde8bc5222d917d4ac0039bb203c4140bffb82c80f2645b275947867f37e9bcacc0ee45871ec2978b03838d64fbb1911c9395ef6b66fa137f8d8a9292cca2aff128c79d5f171e1a889a9a44e6cb4a1596c7b893345c48ab401b960bae40085861c843504cdf95a779790660f73d90406a01f74991002688770a82d0cf426efa04fa63ec55dc6788cefa26c8e4fa678cdcc9816e4c8deb222725d004cdb824cd647f31e04336a73163796752db03f89be21b9b1aefba2fff174a14b2592f863cbfdc8084abe671282e67786701e3de91b605102023c83200893d5803670c4a299a15320c30034791ce8dcf79d4289a35050916f21a60c36ac917c5676ae78301630706aa591ad8e4ca3547612aa1dbf1c5f2ec9b0468340b930d852eb0ed137d1326243d94383fa228a3ef5d06fe582c68b4aa441b6dad283abc603c6fcc6b586f877dec7106ac876da41220be5174291a34da7c05cc7f82d10a25d1cc0d4b69d5904bc8efab6fdcc55a8134f7287d079bb050fc802998c9d32d3b75268d549cc047ca5a7121ddd0180e66510210fce145809147502b8d2f09946c81e3fdd0adac4658acc52abff36d6c14b4081ab78778cf8c60b8065f7c05c4b37d562ceee8a450d9d08a61ae6ddc33394eb7877a1eeddcde35adc2bb8afaf60fc13b7680ac0cf235a3f1e8e9e13c771dd698e03a76ddfbcb7d1f7ec5e883f0a2c3c97b3f9bbd8c5bb67704de7a22f6200ace0b9f072a4291f2074a891896fa74bdf5746fdd2d6871bbacd4a741e8bd1c2ff5c5501b2aca568b72ad716776a4d73a328f8d25c02f85aeac99da3fc62db58673833f92a48f5dbadb9fa1e1c846380b65b895688a00d850b9cd4315e65f6dcef35d9ca7d739694956b5f92099307b61769aa9807bf369c4f843a84f1045d933110e29be671c6d0b95b42e44d9ebe07f6b86316737b1ca64a973a3aebf292a7cd219d978f8f4c702e51f64e447941ec9c81730214f701052f22d42d252bfb7ab739bf14cdfc91a11b5d0c7b9d109677df358f6fe9b6c1dc2c67723b01c7248e4ea63cae43359ca85df4ca0fd794a82707f6b0a8261f9157c71a860e4c491601839b7ae97214bc08a73e30272685ed92723e17a9dc61ab33901489a5d044875736b7d2cc253782fa3838b5f4750eec39f636ebf4374b512932016dc2de2c2a0436616a608b22656510e7c7758a6b77f0da539e4700916b74b5c7bfbb9a3ef7b7013743f7bbefd04152754b1b970e05e17d92ac1d413c1dbdb8f037886020a923e2e90c5fd45ab4db8113887b6670d7441ad25ff994b1f051115f773d6a8098c43d392e665226caa357bad37ad72686b7dda939a613b3cd5d58977615b830126cf65001839447043f43e169dfb3b4c2780246be902b3495e413df65be42c1aa48514a67bd57199a94fc48964ce6a78439938367f8ebc5fa52a1cbb5c312a112f49668f20be5fb30f5958390ff335715eaf17d23d25d9397f912cd823e353aab11b747b0cf4eafee3579ba76b27ee226076c6b3026a946c04280f74dbdad5f3051bc8cbb0e3979dcc7406f41a7acf352f364377d513d9a9a3b2167262cd423fea873bb048f5fe7393c446f00955efdfd51b9a2821d644c9814d015adb0411a1f5c0a5d118f86769608820232a608a141f0f2c868588b361c7e7202cffa4216ada60e0afa64b0dd99ade0104a896ca85482cc2781d4c057b5008ff62e0cf3af91b7051478528c7213ff494c07b034139a047d01f2a5f3790a74d3e941f90bde0f83ee36e2c00f97e18ffdbe193700605e1d6f50a84187541e822519bb22b55f87375a33e0171405d6d6602dc1d6813fed2513aa1ebe769f183f13eae63190f71fae8f0f30350cc973324360c0de61f0f121bf71f0128aac6a9fd470d7fde4d2bdc41193a8dd370edf097141bef8bf15184d9387dc815306c61ab8e03ab421fe2ee3f42120dbcd5d04134585b43ce439036cf360b2b7aae89e928e282f2c101e4f13755bcd68086312941dbd8246b0206b10c46cc6a3eb01194d22286424c695a35063e116db2b35a8b5be20035c41c0edcd4f20edd4594b35e8f5e7436ac8526f60e9f2ffe924057acc91858ce7aa37016f6268db87469898297b0c1d0ef7add98b239117728a710bb5a701732a926af519dc9883a1e1efb64f81206254b53e8cb1831ae04415b6425c27b7b32bb2829ee91638135895cb94911d4ef4bfed3af7983ee5672a3a045071c24c7e9f55819ce59ac155b976447617051bd95492d16580357b4b783b0ba35dda963e43548360abb009e9d1a08a2cf1043c6b74e95032978cda0f927955f9e0f2f87d910189757045644463f7f04e4f0f838a6a595254cbc36daef2c10bd37ffc9ad35cdd5628b93e0e32ac5f87aaed00b8e10ac7b5c3f2841769e6c2cfaab9c394925744d91f3d0fc0f7c19ced00b866fd291e18e505e04b87c5a005ae74c6a6abe3a6f5d6b827d3cf8b5d06698780f2e413110c3ebabc9e7cf07029896d9afddc214fb37394343764ef2f933d5ebf6aee6caa8ebe87901775303852370f1e6ef200096eb24e87b2c514222555e8d13770eb2ccb591c810e7d265058b510ae64aace07bd07fe658571cfedabd4540588ff859b755685ed5be4627d0af67994fa93117d4bb295a27a6db9beaf54b71e37ccdf57995c7f538196c79c24a07819510456a20cf0a2a524f7296ef82b68fa297fe458fd6c2da785307b352d5262bb44d4bfd3cefc164d5fc21da60245a63cbe962a38aab89e2932606496251c4014192597ea14ab238b3636d61a95d83bf5b421173271fd35ffa7404e263be157f895350a138c3696c584209cc5322f0b8ec6753b3f5e8cd7d9fef07b2724226e8c79995d650559659899056a4547d01f6f52946e64316c3863a2e90bd04a410042f58a2823879015a7401c12f284f94d4026ef94d21af7201fa97224188db6ae8e18a56ff3cb321962d957345e2f585341119496aa1fbb152857833135a0a1f13c71d294e1af4b7f11a5c2d5a23a1d4bf83feccf87fd267b1a056ffc638d20db9ed7d056a064fb5fa8fead59fbc5c3ce196cc5e77adfe0458a67cf84bec6191e6fd79cc82bfa3d2704a32a87199926659a5028b8dce382b4b225f507a9e638d274493af01dd6794f5ef0f3cf8ef8603752d2a91503eb53df141e89bfc1bf33fbd0f569025c3023ee3acb153fd06458ab7ed4c29f7223955857c2ed78c6b15ecaf939a6efb37708d670590669205fbf3d3908f362e71fa0c248159199bd8374435be84464e56a1d97febc04f1f596b20f36be1176d383a5599dd314216154add8705c4fe3ff10b207cf5a0b111cdfe79230bb5e6e293e232abffe4efd32b356a494f15e2ac3f939adadf6ebc5e38abff6bb6179439d866c69907940fd1c1ca5dc69a3b1069c89d59ac3d4ee009b86263117bc195a38d1b301610bcdef957fd5bfe8c35b55e89f1445a5b14e1258e0bff29203015deb6b9e4baee266160dc125b6a98c43cf2139bd15016efd32fa5a47284e0fb05705ca1dffdfb72ea79a3132981d49b3c0ef3e500a32f4b1009f6376a90f257caaa6882fd4586332d8e7649929c98d8b1abe4fceb579bc702cb8b0379632d9e048774dc19ec53e1e4620b3b652fc55b2af84c93db385a82a7d852ec1f17e3befbb045dc03a8f8d78b08f6ef19f77be5ceebbf14ca9b78da123656cb332792e3beaf5924fa9760ffd139a5b736f7d287d67d436f3e80f57e953e5c67cb2c60ee59dea394ebb4d33648719396a67b5165004c8e50a59dc747e39f040dc814f194e4788e081cb571e9c68ce209831511c595961915101de5bdf98719a5e027a03745167ce40ce2916ce5e0b96efdd82d6ab07b003721d48281cc67383c905d61709530a032c11819dd058938c5165cb54c190ebeedbd884444401c05edbd2c15e083578c29356113d570329d3cb89a6fb04a36dc69306901e733526e1935b1dfe186133d2226fb31296a564c8dfc850327e431da7acd936a7f014b0449974477df494d45adcb5577d9ccef7f24d602749df63ca2815913cb240edfa1b8f95562b4a926153d99d7ffd66d0f505915f3197bf0d537d50bcdcf93c09293a6a2d5c2fe8c1720d9e43755ed490c1efe92ca25910bc49a0ddb1b77317b3df2270a6761187db82014f40167044226e2909b07ce3ca6c75cddb9bc98c28304ef2da53b9fd490db0a533db2bfc9838762b3b53f57f6b4a77a96ebae7cc909c6833922e1a7bb21118f8bc4001ac959f49ddf4447f27e2c2c2f656c437363866b881db4d9369f4eea83a7c71a26343c45fc900c084d0d47fd751191e6bc994823e37173235b137394deac96955b482e773ac485183704ba2f54a9f7581377640908fd760c3f9d723a944f0c0632e0dbdd552b5c1c06d04e028e43707179406183b87a53eb6c85b392808dd3e4fa816e45066cb92bb6fc5b8860cb00909a92a5af94cf2a6833a61fa2503b1706bd866bb983583baea2a73219409dd3b12263da9f21fa13addb8b0595179fcaac21c8e1183ba79644c62c99c6dc3512bc80608c8dd15f98be0cacd963dec3546677813d98a0810a3a7a1ff3bc3dd821fb55ac7dd1983af8ab9542af825835fd7797958c270be77ffaeb9df57bfae93335ae56592cc8b8f9f4a2b69d2558e2b441896105ff7220897469ef3916ea26d0a181727f82dc3edfec8d4888ccb8d96a09571c944ef86af1184bacc39d5d6fa59c2aa57dc63bd975d9792ac221ec8ce3cb13a6c7ef99136c649e900c1d09588f255ad83c61314554a9c4cb3e508d93d0ad0b846900fda09cf6b3e8875637a022a24cb040f1d81326638a5902ff4ba85b35bf8ce9b06e6ca142ca016d3ed45735764ddd611a083c63fcc151b9c78b162f80b857b18a459e90a15d464b3392708a48490539a10368e1552be5846cff8f86645bc2fbc1e4c9d178ee21d8f1cc416d71d700480c0aecfbca8edd4ebcb4f6d6203e635a12e728c9c554dcae800205f4bc0fd12c347f8d3644744f805f210a309c76d0466bed3b1ac49be20ee193026d1fe7d5dbf01cd8bfadd04e456b05f4c143e43a2256fe2b178bf636d71a6977b22b3bd9aa0eaf35f470683b4f30598f06ef5c47c43b94937cc8ba741a558087d95aa9eab4ee43c126924740fc580baf5b0aea427b641e671460347627cf118e8a181db8829bb2f989659d5e6a82a56a4d65da108eee2dd84fe675409e3515043e3156e0f2895c7b7424b75d2591b9d522e097d37025402426cfc1ddc506c1373299ac0b71419d1594407fa4421137dd673ded746ce8b595685097b081c056cc79dd396fe09005f09aaa7787bd0454f8d095ef76e0e85ac8f4d05edafb8906f5d532880340f48f0e83c647a66996ba441219453c9d94d937651d7e532818650a53d6328a3db8cf089acf8c2b4f9cd1defecab70f8270abbe51b9581d9aad44b4b9db702cca87e8cd13beb9edd236884c31bdb3081f4ca9a054f8edc691a6f45b200868004fee2df524baf6110d6642137eefbd236fdbcfc9b56c33cab6ee288012785e2c217a24873aa760d072321a492a441c3cf6f5442a45e3cbf7c368e3392f42a5aa0acc220e2ad840449a7def04ff17cec9da22a9d68ec44976417ba48842765e6ea40c752355ff48520d7051083fff7e669342dc7f898e14feff881cb226b230fe004a6a7d3250ed8e6268923d9799169dca728174dc460b18ee5814717a63997eebc2201efecfa72d5be0279961808065b265f7e42c47b462e03c0cc585bc7bc4685e5dc3f90175ea6589ccc08d25df019b9ec7a17809b9b726601e6e55c20c94fc3865cd0cf2c212d9de2ad7e1d116beb0cc4d137d99fe81534b791b2fa43a146b928312e55a0b3490fba777091c76216ab84e5a302c28c7017e8785fe37674cbd6ae78fb9b72d289ec395ff4a2407d21dd4b1403a205cc56bd8d376ca8621ba32e7fd894971758f23d1cf92d8036f2bf6e48097999780b93f1d829dfce64b9948682933c4faee1d298edd4cc6b0304095fd092b240840af44637b5b45ce8a3c2a3c0022a96776cb2ca32b31a127e290f16e8dfee851d1cfada247e678b6f7aa98ea90bed1b2bcc201ee4c9fd7799c1f4b7f5abb1ade5207b6fa64892a3e9cd9e602ac5f1c63f1c70fc28e62e5f2bdb958e0f84bf54b12c312d96eedc3c49a7dd76b7e47e2434ff83bf8442baf8d2aee947b27555038165d28b99e269ee7abf70759296b55f400e9f8824330c05ebaddcc97f507a412d05eaeed624037ca39e51792f28ebf3f0338acd78895b4df6d3c922e1dc6b28a64255e83f4d66ce4aa535214007f3a76937d1405f6ceaedbabbabef2d49fe6aa235e991860ee626385addbcdc476e50dbc78dd66d2b48c144a84f63ab8e3163c141c42acc4184264c4610284f60e780017b405a5cc8c11c513160422e776529ed4aec343ee329e1849c5b2c70378316c6cc2d956d0b754b56a87f926077d852979b03045a5d832c7a205626a6c78e947e1f832aaa8a85819980220683f9a02382474abdb551d2194faf483a3990ae2b5d3b4ed2ea255e3bc8f2765795eb63beeff7651d2a33757ba47083fc487cd6caccb95467275be2d63261afd8a7b75e4af06a506ffb673dd59819812871b541970e74dfbbdc2a17261a271e7dd7240e4384c2a606345b541d6a42a206ffc0457faf2f9fb3eca0d7e20077fd749c91ec77ce3e5a4ba90a6f9c8854361eadfb8fb7f9a803d1a28d2f7261ae293ee29a3f2d99fe27dafda36c0f12086a8828d2056b8a8d00aa9884f1d62d7d51f030fe56112ecde7b7dd19188b1154f7b0a73bf342ada7b4cbc15030698b58d0118b08d5c125131f59becadffd70c1882c931591180cf01581f19fead1ec023c70a61e9f1b14d5e679b850de07613f5f27ee578b9ba718895723e6fbbec3ef5d60c17ce7b00578ba04e0dcdfb37d3aa40b9770407b3d608b667032434ef4497001d8a364cfa31ea7385abbb50ee6eccaa5ae32065050186d65d5bead9d0df6ef86ae7aa90f9a6b86f731adeae494270bb0283c04c6b0872d79035142aaa598073b2eb280bef86cc7da71d57054796e69ffa701d7cdb1256a6c4cdb4af14f18c994830101552cfee777287057b8767b750146ae830b2a02119c3d1c967cd2a74f6ef9844cafc772fc3d91e245665aab80a9924cccdec14af8a40b252704e2813146638dfee09aacb2944a59747f7e71096684d6581765f2fee7cb163f616bb35b1bf048da57cadb8b88154d4376094373f6c407a65a9f06730fa2462e1725e804fffda0393176cd1864c460eeca3a286042749080c0d8da3246362f3284f8e6e7611399d630d9db1d390924e8c082e11a0bbe7608634a0488995475d57ccddf7f872058465e2162f3fd55a112701a44de6eb5330c67f3197134f06b87cc44d4457de77304c8fa4d4388aa0150701fb7116c21fe3e19fb7d27dfacccbae2b8abb92eb4ee4c72406ff445307c1a9dea7f4af0042969882718737650b587eb5a7e7a0a00de8926813cf261e0d463b0d6b7cc9d570c8ae91506613abb17c5905e5c4897807acb284e892758430997e7e55884b54b23ebd0a80802bd02ab12a3918534031fd038331574541068c4ed8c0a42009409b5165bdc232799a3655f59c4b27708a5c54133339879ad79265ce403506d5a31faa7b3cb5b3051ac054ec0ba9bc5d49b4749f55a676f9bd7579b9bbd4b36ab856b5ad1d2efa0dc14bd331f2445df38504681ce06455de16d3e1309bbe0e2b5dea84af94e88e662364d333110697f937b46567474ee575eeb33ece55e254689ca022cc6815cb2efb63be568b8040e87c098d5ca825e1964050b2eefb654eb58f9b0f9c3d9619a03db55eb1b7aefc84190664ae8ca3c75db0b8bc23bf00b1fa6eb071e35e06aa5b636a83e726a1a275388940761f9511d600f349e048b14dca051c14db6599c16222052d0cc93188e7377be5ce4f4674ea8eac026540eb0fe59c83bb15d84f03d8f1abcfc6a4904885fee8a104270d56e11b0983806db86b87ef49fcd1effff3cbf93b0df79c2d16f868caec0bf86a842346d33d31f62bf7b65c6a00bd07ece0b099d3c05df6bdf4fb8d69bdf0590c6f775fbe95c0a1f26ad82e3d6833099cc1f3c96816902a6a8efd9993a621e156389628506241305997daf4d221625612321419b26e41076878604dd9578c3eadf3a574420ca26f87077ff005d62a777c5865849d66a48a7bf731a3908410ae1cd5f4a625efce39b75890d9cdfcc843f6acb6e78b3cad2a03dfc83ad0e390679f7c059ddf52bcf06cb875d998a02b2acb381898879ece1ff7a626f18aef8d6d91931020e25a0fa1e0b218a24325a233135a342479a94026981e901aa0e7927170b9f9b1b1e14cc4e283234f2ed43f7124e1aaabfa95a4a7c78482c5a7695c114d773a26644557e61acbc8ab5e3749c984f7881f8448299cf0f563ae77784ea26387ba7313299fc2ddf46ef7607c49aec1d1f14d66d8c3da8af210747feb0d43bf4d13b84aa3d67b35e02a4f66e2331f8dc33904a6a614425715900860bedc901414c559287337063c0230b877e56e5bc13daca84121b7b5892d4676447f80b0a05da5c8c5dec7eff53e228ed3b8a0869f142f6e1ca64f8e2461ad00cc92947918723d3977fb94a73f9b4e7be8e775180bbe111d7e047337e4e290215ebeb0ded5f500fd21b2beca59d349a2ad7fb66210f1c5f6c36fa8d69250ccc15aaacb0bc507e178323e7ecad6fbfc4424602af4681130c32901f81290a3ee605821769ba61a719ab5f20e9d307f52726203eb0508019904df3c92c7401bb2af9520897582d186f69e71c8903765f0601eafbfbb04c25b919f295b7c45c0a699c0002a4fd4419f0fb8a8c109a1b6314b8b06b88685e86cdd0a1a3de81c08f0520fe2476698fdba8307bb1c3c6606ca4a1ef4f8c68ff4346803c9796664875463de711640ce408b925ac61198cdce67fc7b59eb7073162318972487f91dfe6aae5167ce1cf399dad41661eb2f19187b60986717e077e25b30200cf643160c0001071539b6c31489bc4aee4aa2248ced1a7eb62070e9569d7a547508900fb00f56a537cd4c10a76fc1ae1b7dfa005ac32c01b8a0d3096e42c4a512beb67b6ee803f2fa330a38b204c6206395b4cc2d07698762274724e60ff68a3e9a6819a6eef1e3826e8f2ce5063fee5404ecb01107ae2dabdad623a8cf2a8d4a1b692805c45213a87dde9a344a09209b33cddb2d21da009de71669454ba98dc9a3b40398d908f9e81e1460e8d0837b2c2c42593e252398017079e755c854690475e314cab116dcd239a15231744f2914e3dd1ae017c12816f552ff8936d89c69414ac6b08d1c010da94f8998ebe706b36cbb9a3b00c0dbdfafb287fe61d8456df971f8d0194268552fcdb34b2316913c572ea2e491bf78615dc003ad19f3d7eb7bef86bf040fa95b5959bce05d06abfd7be672e2923317ec799bd62314c5cdcbf4706aecfc9bdb10b6671b685ec8480899666e18bc37e89f5e7781b8e89bfde9a9c086dd1acee00df351c18776db6af97ae38cbcfdf2794f0243d75e5071b0a25db96dddb0c845bb42c031fae3d3806a5d9ab778eb846c251f5941fcfa3cf1b0a631282dd8c05b310be628b475d55ec7680187efcade648f7f458a6aff8739944dbfe4047ea597fa43f2747d6a9a6ac66d306a97a4fb063dc9e061b4a4006604112cc6806f6a10a12d1c997110e14a44f1ed649cdbb8d2f7bacd57ed48adb563bd95b37d6243746bcc6515ae7a7123a621bcedeff90e552b759ea194f9dffac95bf3e6bfed276f4dfbfc62abe6d364f81a85fd396920f15429bda3e9b8a1ee59e1987d2755fd09e2c665602f09ddbb00c5bb5af7eaaae923da39c2019fbad023d47d6b721de28d1fb8c4d26e4c086000b0b11260da6684890e5406d09dfef6b32301c52d05b9482c75b2a254c35206b83623a96fd3f2af9add27a6b3661c9a812641275056cf61ab22002692e86dc2f353f24443f784f2ed302289f23ad0f16af168a13773137aa48a22a66ec72ab4e10b792aaadd7bd07abc0a1f2d7ee3fa6f9c79e6b6be23c9e76f2bded23899759c5de11e15e80dc48127756cae95675058f2e12db4a06d5abf0976e5a33c320e884b9cf85abcb9f6712f3d696e70e5286152dfcaf4faf93e3a304f03d1d2fb58411e304deb5718a31be334166130891ecaa4a7458821ffd529e64af3b40244fa1139b1b2e1c1ce0399799b847794281f1fec28dd8a94e47a5d3826eafbfa2e74d5ba3e2764a45d8f37ea3c922788326a5fb5774050b1f4ef0b6f3461d98d5b93adea7c861a4e67c251a2ff5b705b0f54aeacd0262995c3545078a310e63f8a1d1b4aae9ee7888a7edebe6738fbfc230c89f277d96d11c73db10706450f1fd0a963459d633c266028767b6b5ce7a638e64e9448eef02edcfcd217963e682a5830a6400a4745bb184f94b6d727f3e0be8d7fecc1610c37e03ba01d33174e325f2c586cce47cfd963ba4475dc028af424475b6cfd160aba1d31cd1ab2bb1b6fa6f0dda6d93700f897810b30e24ab431fbc0eee07482f99054af8298541fb80527507f1bd31415937df880c421a048e1a36ef60a90176f607b507e4e909fb5ce6976f739a647fa27d36285bab9c07f1b03b2ef59f0c5f787a001ac42371303b7952cb90bf67b8e95b57f92fba09735357b0aab82207036f8c8ea713000555ed538b93511fc933ab9fd11675114f6240b8817fc14cb68104c1aeac68e150052d00417e829e9de9f8e16a30c0d181610d4039810dd0343b8d0458967c43d607288dff07df96562c7878342d3daede240c66d52a4485fbe161a8379712158f94379e10e5a2421bbc4f711070d9c87275a8eb2c67106b28f4f7bd93014d036e8bd8581c71c3548e29cb307f4558706df3d77c019040df6c825b712a037c1a738c50757bd468a744e24ccf692d02580a00f2908880f4bd30abd1de3f24f045c630ec57311f5cb8d4855bc63d9870a1d4ed30deccb0075b283c1bf1e0976437179c43c8f086fbcd43ef01bf609c889010dc4f2638aac05f644a4854bb4dab95dfd1cc2d756c77ededa8d7f7664cadff9598ef44af0ca0d0d10a9c3c2bddb0f6b75044d0282f655e88c0af694e71594cf086a76bc56e1777826fa9f10d60df3ad63d8d63afc87dfadfd40e697b27d0e82aeb035dccfbdbd15a2ac449c105ae9c66db296084a94ef2ce92a95215a07fe8a97aeb930191336f16e8a32e1ded619649b45f43abe6d9b475db36305df8b8f36357ed3ac72a443aa050dc8546e01469c9c0534ae4885b6ad831ddf17ab54e12a0c4db22569b5083e1286121724745c305ee01f53247b5a267a597b9daca10d6cf9ca633d56de5cc5abab1f13b6db42214ca2047d2d9fd5dce55de13182eb919875206346e96ba0d65fd85f4dbd3990a1943565fbfa1a7f3f6353645164dbb6133810c65d4dfe195d38bae926349c7eba673bc7870d0d4a3b4955481839273858214a3787776c850dd3386f004b7396439014f46c8d19cf3a02a78b845537f978d245841bd4c435ec3e89b7e4f6d5a486e4c01a3e607ecc9c0876ddad5254a2c3cdcdb84c299ab8442761c24c49ea7735115555f273d1f1310134986f410ef75b3ba175ba801051cbf645df873f7d4a15a03f9d6d521e74bdb3385516bac84192060f37b7aabeaff1c9701f14df9486eec1e0341424636b48501d49b4e41e6b96dbdf3b6947092b6262135e624ad1feca9e7a47fc0152e5f15f0fd801cc98fc1422f2a455cb8f49185bc4ba3d78dc0e5dbab8022fcafbf78624f881c3fb8443faf93f6d3b6f781f6c48ff0453727788a4bbb76e13f3a64490c2bbdd8f321dcea1162801687e1102f49f1b4747fb6bd7d55b7c98409815708fd4a88b74db043751b372f2e2072def05b460ce01871087c7a1e4c4f71b61d27969d840e18c200db902c13c7c850a0e4f2749e6666d2742e51da305e51b40fb5821924b729f3e3d1db71a15e7f741422267539e2c8b891440132c7ee5ac96ba10c8b69cc82c6a2e4b2f8f0b10e4eaa6f6d397398db612efe4b09393b1ba6e2c47c7b7a929d90f0f13abe57617bb5b152b140ccf2a13992e15e0508fc9d3466ac4f382223b26fbbfb91f7fc871b0b1ebbb3b33a94739fba7819a551398dca9817a18be4ff03a9ff91920dca561c0d9d50919cac6cce5f779ca5545df6c0738d010263036443c21624be0b16e221bee116d9757230083492008d0df65b07267cfcf71dba1a90b51d3637396c5de8172a2164419f2ee17fb339650dda68d59728f0d647acefac045b243d4c9ca05f483c3e3edbc3ed9812ca50f96c69756b496274942a46a247137732d8ef7eece7819f3bc3645ddf17ac762ed837d53073fdaeafe11e31e52ca7a2e15a5d5547f8990b985240a2dc9778e3028dd1f21d07922083d7c6c399b8362656c37a7f0bda7bdc3357c76805ea4acd5cda818803b287a54a698d4cbb7976d319afe29c2c41e7c72054b5aa654ebb5f7b5b6594156be563da4fc1b633ddc552c5009738058faaedd8ef3bdcac01fc57d67b54f46c5d7f66a7b93f6285a6f2c90a18fcd5fda61da62eb53126b92a39daa68dc528e67934bbfbd8a2436ceafcabea5d4ef0baa411a60c518b2b69e449fc454d1014fc138b787778a88d5c81b5f48dcac86162705cd71b7c44a924f4bd5ce531a1975c8980e69ff4b22b58aaa081b56ec7f2a2993da944238447b368068406ff14362a708040c98aaf4970abc52dca344b94f50554a7f74ffebb2a9f1dc47703e83aea7ef108ce4e9f3c1e4ca35534b511f9caad196b5c614d59545afc25d4e7002b91ed186739e8c62f1c786f2d95d67e64cd34e9c1845d933c524fa1d3af7e2c117c6d7ececa694a3922c6245334248166a7687062ad5404157e8640221f6175877754d9582dc8b52c839e885a21b3541b5988e7868b5267b36c927fdfc19861ce86c42002afba26328626b2a3f9703bba49f318d1de2bddcd41feab52946a9a588aad482946e791c2d606207321cafd10b09dd5996eeaf76fe4437726c0ed8faaced6eaafd5b4e395100089e5037f630ffc4550397bd818d9204f80e10e3c25f1eecf02a1d2881cdf329d85f14204655f7685873943b148a51905d275db92718b21f4e8be4100290bbc04f5a029285c77da1a0db7edcded0ff9a4d644f580e9f064cde273647b6adc52bf8ba8ac2ca477131e4366d96961e9b165eb07b7b4c1c6f0b0a0a2febd8364f019dc1f79fb35022cedf4422849a9d061e272389b2456d0574509c29dcfcbd4096eaf32ed3615be1c17fd9f343a783368525c3bf2bdb1fb5e0d0394f97decb62fa8fc0f23a72d5b9762f7f01ec30bf4e65133f22b47659c8957d29fd35612f3ef57837237b1392cf7cbff85c97a58681547cbe3a84f0430058b444f6a12c1242919e4cf6d95106877307457e2018da838e6100092f3b2d5fce9f47db390ddeb854cbadce683e533da9fb3b074a0439f0093cf259fa38d0ec9259b0fa88631330a296a3bb4f6a5e967ee779bf66c7c07c83026978779f2be6198019dd41af814b4ae9539a837d9ead3d1a3a24a077f7d908a4c0509a6c6ba08445de45911413b0d5661617ee4c64cdf147bb8cb53c7ff11ee190799f956f1e164c67900b753b715160ec0343f73ef9bccfc3bc8fa37a3eb61102d104f4ffa38139af3f8e3c2100522144ccf2d3713389a1405b18b419e7514b2101caa227a97006831f1ee6d6a8c692d419e3d19e08ccb6dd3b6571a145d6fafa093b2111500f89f87558c1368e9d25c79a08f1e55973346b8a3655007500442988f6e7e8bc899fcde07519345b99a257e219ef770a635e9e8f351891aceb3a778a224b2e8331f829bcc2e7fbde973e09b38c8432b67b038093344457b47345d85fd7c6d3ef0b7fc5404ef978fd01e564e233cb7fd7ca77c26bc8bbcf891a5c037293912c41dd6c84441c129e6978113bc5162b2a8204618c89e7a7d9687dc89ba2061edb7a683bc28bd01da61db755e5a67d7722daa7e343937e50f7e0e81c28a95a1307b4ff23ff0c38d7b828c31558479d7fdb4ee01939e898f831bdc747ccf4063ec91aa3f8026fe066495e8b1ac64cbb844e1ff2ef34994cc904884bffa1539829ffc79079c20a06d815490c0556dc9d97180b35a41676284ebf083b6458bd172ef33fbcd707d1309a5d358478564a4f643a776764d957639f3bbe0d50a40319ef27c328114b0509184106dfa7a5513b808e8373c3b5d544c26e982c695a433d9057ae15effc35c44a95477511cfc62d1048a771075a7c8557284a10bf5156574aa1707c9e93c89980925e05538d8c85a61be465a68c2ccd35ad35d76f95640ddfc085bf93eba49864c8ed2db27e370805bd639724772e3a68be38e381774b5ba3441e750f65cfd01b695b1453da099ac9869cf7dec4ce204d7105fde3183c4d243e61391ecc253ca32256bdabf107889c07a6c2da98bf8bf8102d5a8b879d7fef597b6a47869985cbe2e250128a9b705f9748131e9d82c8995164aaa1d472b5043d3316dd2dc3bd2d89c47884be47f1d30eb62e9d0d391f9b353b8b5219be8fea1b45c33751f41f97a48d51b13a365b9c85950343434246967ed76e1655419e0529064df9c13d0a15d279e9cf24ebe590cb48721f624ccc013d58d198ef7916504638cca2eda7d2d022415e04900fb6201b6d8f7e742867f2a5f3bbac2a1a738fb7aae76edac25a6b41a0cfa1d16ab61307aa6ade1bf241904074156ee81b64498d1caa51804db3d3b6fb9b84fd0a78991326e5f4771e1d67026c8b10948511f5233640ee29fd64124ddd36fae85c257f13bc518a7a0b203b85758cafb0b30d2cf8c7d96e8989b8f4594208af8351a09826d31a946e385f44043ba0d7d97f8e2322f191ecbc664887ac673f821cbdcbcd666342bdcb25c279cced0881fd7d7bdc1cc270708307c7174cdca91451829a622c825f6c83b7886d8dd17cdd738642cb6be28a6f63aebdb600e178af47da289a64778c089d6f9c9377e1dd1ed2bc517d9ce71fd831f6ae79a7b3c809b4a12452103a73bf506c1fed362abe339121fb395b1e4d01491c6c3572fcf672f9682da72394bb51a935946362f98bf810b3e9f27ed99368e193029e258f637583a9fe29578729663a080bc45bc2687fe036a98a853656b36e29d6d8f5c00a5ae1cf764b83d41475074b9042233e7c7bcee071e3f4bd65ae913d6cc217e261f79d205c94be4f3306ee41361b9cdf8fa1ffbfa6ca6a54a0a7113a00678a8e3f25d16d4259b7419467d70cc5d19cfdbf0b45266896503ad1b759473b57a787751586c7ee4d9545391f393455165355d1c882ab22f71dcbbf81e2b7beba40f119baede05e754cdccf195a8804bb2924e0db243a90543d6a8f2a1a702cea3c7d65d695b6e6c466e9abb5c26fbb4efbc1feb211803e77d948d079d87cd283948c291efd07f568458317c59676155f152a7a8f4ba29187e586110f757010f3599425fc77cd285eb67d37c965328d4b11d0248fd6290eaeef13a3a27beacd5ea581fe856552ce42ae828d9eb06d8eea7ff613ff6a2ff3a152ed51fd1fb6645837cbddb5c9f42c22f1b3e3d45d4171daa47eb5b57c32fb11c983863941989137873ca24cad49001ca126ad98bb7b559faedbd993b6b051e0e8e32f1f64ef6171877a5c640a06d8dbaa3b28e3238faaee20ea027935b4c070ab4e61c0e28e885c8cf286a06a58c5aeb9e519cb210b299fd1148c2a0ba74a7faebf553fb5441d174049001211f2556518c2e53c17100ec48795bbe91a1135adef350f1ca92c804c3687960f02c8619e4e0214987db195db11bf2153d540575ca24a1cea81e76b03c5446b92a2a2f2bba5a80c19a428dc8a24bfb3fb6c06a3b875cc38fd15a2ce8817d39949e691e4bbffc4da9f38dfa6ad2bc5e2314f55108c3556ad48e65ec5d71ae6d4f1535c964505c49f498adb9d3c42e6550b46dbc6797d45a0db215c3c23375124bbd71ecc629cf43b88d99d51cdadf839ed6f148d605915946bac8865d54727044465f20c80db7504bb863f62b43ac6a4d6d10e1757a7eaefed344d703e4b7cd26cfbaea5775832005c945e3d64791e3741200283b82c6bdeb5b29db3c9c1f1cf4f54cd84c56e54649f91390bb1c33e3a7f5baac1513ab76ec0053a3bc443045158dad58e599a658f10df9cb5bc1c7c84f9ca4157313548f96c1b3a5029069707b230824be5a8ae48e32f7ebb40142197a79007cec3d0aae12d1a144daab5c3d98b1c195828ceaf92fc6a17a9a5495f1f32e394937242f30d1200df92a0cc548a188d740a9e47bff40c75d485e4bb87466829bc926330c8814bdac03d89832a55d2f084cbeb795f315d449fd12883308a181c9617493426d5fd1a35f881f0dc4b1b397967683998e15d265302fac74aad7bc9979acc54306551860640d8bbb70a8c694af44e7c957a1b495a512c8e0beb43fd5b080bfb00e0b57679f141cb2028082572158b72d17735138a4ea8ad370147b744d497b41626c711f5d1033d59dd31ba60a04d75d456a6294f7752a5091840c89701d7b1507e8c7bc2e6323b54a4dca78f5d5b68703a6edfd8280891adbc051066257a94428558c9b797be30a54bd3d98c5409b8d63d31a8469979d010bcffcbbe53068137ad5a3f702a6a93c7cb88fe6b7885cff06e40c89ef68b8c78b578c78b007a57c1bfd64dbd81a150b38565f13a95a5cd18cc028a886056db67c7dafd9f217b353b9538ab05043b695b38a4ca394e1685a92dc7f2916334fd62643df0e3c004b0cc06582d9f692e19421185f1fda4974b7c2c638ee03f15d718e4056b58f430ff7cb6ff8bf85de8480974a58213847d5c743c48b4203abab9575b8a9ac7f36619541016c448569c2c04a47abc236623d0e2cc81b93dc36194aeb41eceb4aaa61040265514ab1e89896aa9432066bcdc63f02a11cda6cb6bcdcd9bd9524e2a39df2641554357f899a3a4cd4ff963783aa473e9a620f568fddf0497ea3cb9552d9e29ffe2b06ee2cb6b1bedc5ddc70e6d54364a12d4273f142fa52db382843d2fd2d0fe24c9047784362c3be05cd4ccf5ad1b3668d1d5ed8665457c5b988f6c5c64f95d035045aa0451ae0c50331970a2b3882f65b3da2a9371ae55922d4904cf4d1f2d72a379ee965092fea0adc222b9f507e741791beacca308aa0826fb1f23d05fc36eb03c361319b0d27cea4e01769eaa8dd2311a54694ef41a97c96c9ec3d77eb89fc3b8a999cd60f9512e4f5b0c8083011cc01708fc8508ce1b50c18a0134bca6941678caf82683d14c186df428a01a521aad1622d3434e4c20610e3d63545a18d164d01e5d1a9cd61599c266e5138ba0e059cbfde8356c67263ebd5f5e48862e1619b0e4cfbeb0a1aae8b92fc540e8025dae8f2802ef80c0d81446d03bccaaed9e241e856416d58671886fd2f6b944eae285c79d8322b295b2f20607c6c450d6b23f60b82e5148e8c5acdc8003eaaf2d5bc676fe2d6bbcb90eb9d912e451e48c96879f14a97707d9c8e228f265680e0409672546b39cfc93b0c83b04e54bdae074e8c1bee042ad444e03f664a2608d42ecf307a6eeb527cbe5cdec010fc1bf92521d1fade7a0b625dbd2a86d1ddf9d0b3e2fcb3d3a9712f37bf37a922e8fd9343aa61b75ff71910061fcbec76dfbdf70e4410193a099c0d4c9175412a0841d3c658311ea4cfe970d769a7721ab9bfb05d315023a548aaad1124e4182d8c63be87722fd723c9432e8ae89804c6b25dd0dcd5763599ce547edc7c490a1ac34eb59062bf4ff2326b0eaa9582512515ff38b2269d0be1676727d912492f3e95474cc6384b248adaa6e55348e3e12fac713ea83c3bf64661c602e498bcf658d2962bdbde3798a4a0216fa1031a83828525d1694e535197227642d9b5af917542457e670455559b3888cb2bf718781bfa42f01155bbee5d5f52aad8c0d5d1977b4c13d62a0f9842b6afa064a92e8e49738413e18a5a88019ddc5c3cf6dc434fd0866304b709ce27c2e8b98427e070cb2c815d9b520bc9e20e8c446dc855ad70edd008f95eaf70ee49f57eb0845c87355b64a58d4442f7fdfa8d8b1a9430050abd9778d92d4cce0913390a3e076ccbfddad8e60891cda8ce318374b9f7f85106d7c84ed0d8ef36594ea637353ac946c2ec0c87c31ec498e0a9985bf37283f582b15b9239114771ce40754bf6947d1af8ca804063dfed420a62008a48d997d841233cf89f4d42b9a059730bdaf11f6091519964e9b870d2b013b2ab6c536a43290369e10a086376b9aa22db5f2dd33e6d0fe879e8878723e2d9531b6581eca6fc079fae82f6bef00a5e794a1f8b7f6c61b80c937d791fc2ea1d77851459c5680c310bcb2d8059b33d1ffce70e87f4978757ea74e0bf85b3519ec7373da899a13ce808df483adcbb3c26bf2416b817f82e739cee3ad764de48e6cacc3d02fff461a8c345a2f73f4e523b18e3d33c85e59a6fd4f57e70d95a82b0135244a1395481812ad7576345610974221c1bb19f22872142c7eefe2b53564e0ae9354dc73e226dc2b091a89ba69556e800f15b5a1c16fb7314871033f823c84fb1cd3495be7722b8122692457e108ee00f565f4204ab66b7e68a6376b871cb6c9076b28ff835674b2524ec82f62b879d67d25125bea78e4b7864112518f898ee6a1d633df1348d3e785d5323def642a4df570b026f167ccf51838bc1ee27f198c2e2804c3b4513451934ada98e15a64d09aa499fd9e35edb42d5d9a4ed96d6062843e938aecdca7fbb07a2c3e99ea7d5f17ed3dbc0cb01bafb316591150a4eda37cc66859bf824d115bace090b23e85397410da615dd50a9237017dcc1b1c0c6aae2ea4a8209a8736d6b22ed2636c53544dccb48303a98618583a1508cf12ac658dfdf685275f709f8153dd555e541502d01d54f4f32ecb016da2e255e1bcbc6b1bf611cf89ac3e394b87f3d322d1aa8ccf080980383181c8b1a09f0532892f611a60fee3d09860446d7920bddb5c777120256bec1ea259f6385dca78811932fde8434a4543022308d117517ce198cf37804172c02230440c2b83e49ed9fbf38c10540123b821ea2415019913b0937dd960cad281c55207a7729a728e72c21b098ed4b148b6c0613de980b4cc2f3cdfc69cc8d1c7c66b0cd9d9a378f7c75bfb2aded6600cc3ed46d20fb95431ec294fc3a6bd2b09299d38aed603c2343e1f53f56f3dffa691beaed29eee1e330004f5c447e81fdfff9a07fff9b9576112a62841ba2163c89362ed880fb743e0b9737c1042533a7f0be103a81a24cfb9f067538549c2ac36e18fbbacec005c3fa4e5897d0ab0c6e1b237168dc3dcdeb80abc8670922591ce51e186f8a7ac99e29b2aa31669a9cb635bdb3b3dbd5126564897fd8ad128b45f7dd9dc842524238e06f0e0715cf0761277109ad57f47d270892cce4b768e7d3436c89700558377563737eadf1f8e80929538c486bb594571bb14bd8b48145e53ceced539888f60c6581fc11a4c81aa2d803f1059132dc7a03ce532a95d21a745cc8462742b55e70a702b9c531f4faf44c6d8c6dcd450160cdb40d43bbc9ae16c4bc647efc8368212bf61f801a668c106b8d22515210862078389be778ddb495ad096f21d131c17b741745d9d47e843eb0801c7c5258eeee5f02cb9c67a33d16fb6ff9e47946a4f213e48f3b151a6a06aff635db2e440ad412964c9dda5e0c3aae43444d0f5287681635cf5117b982a9e02cdd0fe933a1a5eaed1995be00a45010e232ec4682654214a80eba7b3cd72abd06a7e6daef1229c06dd98c1d2f66dd4d27868030642fee88d0c1d4c5330412418051fcdc9fab8275f13d242bdf5011917d2b31043a1546153ea4ea86ba016cd86df92c330e8306df4077890ab2c77c5f5b175ba807524f5c9cbc3126e3db28f5a7ed16b701e8308723390f701b5f19dbe1cf0ac8772c4277b265ad52b2309270460043a116bced18afca6901c3e81cd2d365bc1d78ba93e98e7ecb9d91e3a63e888403cb92aad09bb5d9eb0dd98d548b75e17cc7c544a8b6a82a232aeb90fde5bad26c9220e9377fa7355f98ddfe263e04c98a0afebf254db8957fa782fe57643af70db4c67e0b2a77bbf98773c5c51ea60d1c440ee1ad48701c8cd653e2b96c48e795118e4a3962dc49036f1260a708f8cf47f21d4cfcd4e034de9d4acb030e63d812026aa322be3eea19ef2395289743658a331e6db43f1385268c2201be4cf24b11ef99d36849816b5c2c12d0fc88c1fafb6270361f0f0bafd20b4dc98c31b60242e5e5348dc35a47b3f5816cc03011d62596246896c03e077ae9480f5f73da17ae38010c0246555ea28f03c4190f4a0639a0f1a308d924cb7ebf206e5bc018c5832b2c31f864c4c2151e1ad466a2600b1b7953a69b9206506150d1b9c9115656b7f4e60ca9008accff7285910a868885859d2e1c5a0a8138c588f655d971ace9320d7d06e0d65343cb0f38b3796cb1865b3c45858d007e5c8c8e3201a886e5e3a486029848f6c76feb4a95f11e83a7e57f3113ebccb24c8cac19419fd05fea690c74af854e514f04af9c9100b6001bd205686eddaffabad580678fa25961979c0f114493cdeb024cd605f7ee8ef33312c5a74367e7c2cb3946c61c1e8c60eb756a168670bcb56d0b23b0f2b04ca025310f3f40bbb976b5c8a914be7676276d6a5da52a47abab6e60d54a5e7f5cd27195a400977f6c3802edd1bc054ef95a610f5c4a0f9be87eb8e77902d5ecd78f147b1b2705c25dc4dcaebfc0560b202630273e3124ebd022d69ea93f5c40d0ed961f4f9e15f58e323a556f084842b0c83d4a12d012c4db46ae19bb6a5111dfde6934c5d297226e27f069a8943ea36807e419f5f98ba1b2206e4c573c550ada94e46ee01672d1f51aba54ef063402181b6496c9726eea185166ae6b68cf45842f9f9bb6b63b35ecd4f95194705fa262f67983d2dd89b479e0956b3203ea4b683095a7cafd36f67362677134aeed368253182ab81478f4a707619e2ace733a8cb806de5c7752af0972ed845756d4b84e721913d79526ab3ba46224778f2ce68e5988fade68fe04a47939144ac24a519112655c7f2ef36eecbe779a0406aab8343c0030da9313a6c10b250f52b6a7811c59d983d952071da91cea79668b8c2f9fd0b613105e56e40e59364eca50228c74c32e079e076003d6fef076c1b4a2a0b9ce842ca6401ad6d59a7206255f1ea464c86443ae40fa057a79a2387c583255cdfc4146ab7e1b748315d138bfb3c28a2adea7d745c6dd32a77af3676d4e5d548320013a0fbbc3114024643d7c7cc1c99a100ccafba444b68f70dad97e0afc497a4d32891952ac75934819e95ae10b73c25d94b7ed62e43c01b2dee10ab89949f79d5d7184b647baeeaac55e9274ab486e6073e69611559228bed4fac95ace2086914fee0d1710881b315077832b860294b1abe5772bd359ab428053c4b13f37b4d0cc97acbe36a21f9aa4d8e0d0db4b3f5266ff522f82c851320e73cb2e32231db9c5ab9654b2822168a915f8bd270ca2e5cf40b4bca0f275ab3691370ace183fc93b9f7e953b18cbb6bbbc4b49526eafed4ad9e02c577c7f38b4e891ac1334227694ef1c593789c69809c60f9601a557c7d4a95016046e88415968a9ae55ae4631ddf4fbf6299dc3389e908979025d2bae391e2463bc7196c5e7ac7943a298d4ed2f6831f8b9df7124d8865cbdb437d2829d2804acac7195a8f2a3f47274618281ceaac05db3e564a58ea017c6a03fc9cfe09683f0a6476ad8ce5a4b4a4cb03637e34ed228496ab64812bc4a3ad31fc6851678d16a5d733ff400a42125365d4bb0df72dd84bff5a5b01492cd0da908c1391f30ffdfa43ca7150e5898c781ce706d0443974bd93364f76ab1612162a69064629b9c2dd0a03344596a60f4f2ea2a8881d1ef236cf8dbccda6ee5a2b4bc58ac1eadb0800d3191a33a948a7fb3314534642744b36430e74309d1b087b8c3843b560636a871c3db89d33383a5a27f424377a813733a9dfa8349be04715e1f652a616464c15917bf05421f3aae52585f21974208d740deb646b70d896897c70f9ddddf6ecc2544e6533dc35f1e8e3d8df3fa43f1b6d124574725c18cedc30623a793c6ee577edd9925314ce9028f117ce00c3001d132d0db34c7e3c72880dcc04cc63ece0765c3636fab004705bc49c6bb0565caa3aed98c3504bcff2f4a0c9c4e6c87f8c91e680f14ad361975d05ab4576c47904a9e95a2bed6cb624d4cada0e918011b6a0ab34c0f6567838866e4e2eabde13487ddb116c155f984c52a7c3ecd1508eab163c5bd8e1915843565a24dcc740589118bef14a98b607060bf4aa250f932869606508170767708dfbac0a4d174d744e802b7ed398799848bb8734f32ebcf97be86e0e253bad285955e689a08c68af2abbf34f1023100e4185b5268d2b801327827e331fd5a3c0deb2cc02977fd8b1851b13ca1546b0c3f1eba4c8b00a0ac5f9571d91fe3ba437c1763bb3b3052c297cfa6061766798981442883a48a4d6bf5da4ef30e80e67c215f83768c4355b513492400b5c4b185e7da9e4760969420465ee7b295c113f091291115208022f620cd1e43d367962e41ef2451b5ded2783865c25a728354850f23c1ea99dbbdbf61eb8f273a449df914cbb54e687206e2dd9cd24b28090b78034a91053565d1e7d1cb0f6694955befe3fae0b5ec07a5a120ddbfc28fa6aee9374b0fdcf7805cd2a00643564efa95b1b49eea0f19f3186f9abe56a0b3fb46c52205a3ef57e597f0d1b2aae01a0c0a2103c82b82f8e6ea214490d1fc293cecad755c67be317981234429c0b98d50aec805a3a9fc960a9b21ab2b464a75f71ec184f80b11970980e704ce604d532b3159e69c9e936c361fb0bd2bcff04276957887ad087c28f0fb9b22372b9db56b3065d9adacb13a7b51576988a2f29d081e19340f0555c9dee90f9234ca69db000054608215430085e03743dd5a7ae3e62c39227db6aa58accc97925b2c780c86a17d0cb9a69871bb9c9c45924104f027b4be7f77f4eb39c6c5a4d7fa4caae87b24307a71e97a01c473d81c2926e4d1d684785417c7f3e926fb2cf54a7d00c94665cfa8838de7c29913874dbd58c38c9bd3671a62b41c88e58ce202faaa67131890eb3f664ab4edbb4deeb8ad3d350b668b2259670d6752acaad840729af439ed239086902ccdf0ba24e4bf138d36f2d8f80513133d30eeeef4d53c935384536ac9cfee3b62ba7757620f9e1541a67e9a6dfc6da27953c4cf5ab349206e28a0f09eb57d4112cb2af9410e0c403b393be343b39c99e33e73ddfa63f15a675cad477f93b932287fdf58bd467af8dd405d89ece4810b36e91f3fe5612c94f222c4f1f696df0db9c86b0ded449770da743ae89bb3762de3aa5be168d21b9251241a32605129b4dc6723ab298f649c5a176683c8302afe181f1e670b6bc6b7e9b3ebb38cf790f095d991070a31e3313cf9546ec6a41aa1e92d85b62ea70ca9c133ec4ea006028a44bc60477df2f35271f2fe2ea442283799e277beae0c02e22488edb5828b4ca122348b4a4c1e24919185cd58385598f4bdee258b3ed061a85298339d34d485b0dd1f4bd16757f46e9b138bdf004053e7cc30dd5a3d8134f111a914edb60b7491bdc7fee1d5fc1c7a7b5aa7bf6b7b877f7c2337e53c0eb70056235b440725704cf7ebce7ca395fc98a9c7bf4a01b6e91d0af61041556fa1c41c10b411f296cd0749b03252f75a22d86f387ab2b88e8a237315aa0136577f005e5bf899f120ca80fff490ffd80068f79afe91d79b86d5008349e833eb799d967de5005198fd8679a9236f46bfebb8a76f8e10251a8faa27aa0c097a3dc9cd37aa7148871be27a69fcecffce87d5f7ebf03827c216bc98373dff7394c7e2657f29e915a46c0eeaf9aa33a48f1175025fbcf48b28b6a86fb496987ea63486d126d7e66f1698b9a9f6d381c9abfe6d27d76d4d1322dff5001c04e79c75229efcf1c9e70156325722656e02604b0676d3ed299d490d8e93844ed72fef151d2a76a296b2564dee72ad17c7e7df1de6d4e73c1cd02037418392867ba4f0b202e4a0908a46b89c110c14b8062c384249a90c0726609b456c5a2fe52c7d957a083590ed6db7b039484bec89586ce63e25b49b3b45117bdcb42fa02113924636006030c1b3b3957720158fd17bb80777fbde34a0c183ee48557e6d006f6924ecc6cdd34a1ef41cbdcb0c63a4d491d8362121ea235562b33981af16872e24f5fde35b56772142c4b7bd7016fdded3f686e7e63778baea511f8d14ad63b8944d362224c12b01f2df4a736da4321ab0a5ca4e8c031580fdb9b6bc0bd3147317c8082cecdba0de0942798326153811cd20902c92d104bcefeda4de735d49ed878d4f2cf8309cfe02bec035589d49807588640466a49031cd1a0442434903720d3d651c4cdcbabd6fdf7b922791042c32b33f3e2a221ca5216a605a1db5568a43cc9573b82f74cebb5a40da6111b54256c16792c5aeeae0a7083b71f510dc419db64cb98a8a8812a9a7af6cd0d139ca790913ede337f216323a82cf5143ef552c552299db2460ac985bfae99c5b1e20c3a0d4bf006511c84c5ab28bbe9a63f4992f00638c5a9c04246298f0144170c1fcc476fa24f2f9f6066fc6026c42715981149c882832b09bf3a13b7e83306219c8ecb8c93ba051a98d18729c7c8b1d920543cfc5f15c7ca8a23254fc5aac99ce87b51308e3027accc9a4654127475ac2e6604819e55ca9b88606712d846eb5d245414ffdcb2bff80e4bd217d8151b0e1d6bf9fc53501590b3371a3e2b24368ceb127e6ebbc1d40186e034691035b987e158e42f7a78de75c07b3a845d27164540b2ac5214d9004efca366299dff5d076bbd1082b6f33b0046c6100023b571c3e9cbd7c02b1320ad6049296848a0714783707cd160e9186611923d94e163ddddff72d5d4865d5b2a9da198540e2c3926d0575f6580255519aa42a86b7fda7b8d40d40a211407a2795a0871dca6aebee0fc8293085a568a96c8e548fd2adfdf97e38d97bee5abb106c045e3eef76b6ddb224c775805f4860293165f6292ef816215e1ce4f450da24d6e670062959a134d6abe61b80d6f909f0bb54c2cdef5085e3069cd81769bc2de143ca6c0b4f89f514565e2b235fec0db2ecd19e960fa10322545c08f150cd823894b4b255bd0a3226e98b654b3cc1f0e8b480e8e83568474edae0885fb9e1dc4e0ae00caff2256196d2a270211b3d5d98a75d6bc0b057705f56087148fbd2eebae8bd246d5de795db6499bbe86a52cc30a09d2d038e44ae29173ad1dc84146dd40e572c603d67a9da937b538ef20c8d05b18439c8cbdd98040c9a5fde76229c44892e1e6759414c610139418b9eb2e0e08487d3d5dd9363abc9546a00cb42b1155d103027895fce8b1cce6b7d9369f81af0c3299fec8aa782eb1b7e4c0c446d491c26f6c8b882f22057bef09f295fc516cbad8b86ce38e54714609818552e3910837d9ce3b3e0c04178d07170724f8d024eac36d7d490a40fd58b01916e05d7457aaaa41346d79e67d3658e4f81f8682bf2e0f90b58182d9ec6b747dc9b698a2065919732f874660378a53cc06b8cd928244fcbc27695434bce805d22c22869b875f733818463551ea752eee479bf81c56c661c528e2ba6db6a6951c3440ac5f7f7582b89521da0633b00217089edd33443219e8fe432a5116507910b7db919cb5b50a92a5b34d2c67d860fe4b377825d05359b80a1f4a456c71a125a6b099a58ef2ce46ab033e1671e7a3d7fac9bf771f25156608274bf9b7b2b0918f7a880dd4fb050c1b0f63d78dd1a3ab6b452ff01339c2d3b801ceccdd462a5be6897b237e01fe732d58fe6f6acc445da3414c2c373caee3b67c98ef40ee4a26388c8ba40cf741100ba0db510d0dc5e5f94a2c5906f8b98004150ceeb54237ffa06861585347f625bcaa54f2757bb53a69156073f6dd6df06b2f92006194187d0c2e6cb619d042cdbc5631e75ddb3f9d42a6932f25b421bd93a82457c7e3fd56d54d372b7d39c892e2a9a7b085e174a0e8f3c830d641f8ec1bdeab33cacde8b2cd39fc9a466fad09db509640e0cb90302c4c10df3b01de755f1eef5dccb7f59f617e86a77b77993e340af0f1235f3d17b67f40254d794f340b4acfb7a6fde2d19a85de7206274c83cf8469760814fb32184da9e3a187bc5a811e17463a7445217ec931ab416a567748e3cb912ae749ef19bae390f7ee3d41c5a30b3abb82beedc67fbf61a6d730de1c0e29aefc927ed11703fc987548d9cdc186ff54c636e862e8b535e2944e5005b1744394092118b1ddd94286f01d29c559335806059f1fa676a08d7eddc04092287068f06e725b6540c87141d2cf8992aec0f2b2ac593c9adbd35cb30795d8a76b1b94bee7205488423179f91629bf9f7e93c7de21fe1502db7769c73bf893c3fe8a6ad6895cd1fc8f115bb332c0b8e3cc4e99041154fbead3b540837341b034992c61a05699732ad7edd8ae23f34c0d4219d4780e42b1c5dfaf6fa7d74635837ff6b4d6bca9158385080522bbab8083ef1c73ae5caf263c2ddfcd115af4647e0a510c84d4237874b6e3f9367354500e0fba8e728ba81a4c826450be874e0dddb50406779750dee05908191e9a1fea8e162c0f622a31b7907ea3a9937290f61a2adabc44deae8d19c45da70872e11a440aaaeea844abfac86d6a0f23a021cac48cb541438169b46b25ff9671768960fcd19fdbcca62182b0628fef5a9ea44d47323880d76d98f1cd08088ec93926786fe531ab98653bc0bac189aaf4b15105540b3444acd1e1b6d764964f386bcb394d5ff9e87969213f7f2cb17bb9d779626015777c6f7c58cbd6fb9f7c5638c4b878a9d01db903dc58d8cea97a4ca0bf94c51db6f352bb3ea34a607abc293380a11397e8b2b06011d5a20a6d970161c5a8b2cdc2f8e011184e39441ab7c90b904f13895b0dfb674d3edd1f320277a2875cb89b6f3b13f2b1b9e70e32d22fef970e3045b1553192db76db2016b4e45ae49a92505acfddb9c31a8759c616c5a5a6ecf6e2e48fc47f143de77ccc72ccd885377899acea9cd4348bdeb231c699e9a03e97b8aa001360bff0a8760fa6e1f1a6b0c96e623eca652b8e164fa62ff188375682750492a89ff332ba483f2f4cb1a48df2b2da140414bb9b078ac33d4231d9385ea2697bc258d0da70561353df285e376b15d09a400fb93393ee207da7fdc7493be7e82e85ab7c8cf5bbe1570f7fb2273f4a79b1ada10c3ea29b3ab04e05d4351ec8e3b2cf9c0a313dda1cdc456c8673fa8f0d50d0e5c789cc143afd327a1c4fbbeefd533ea1cea5ab5f3c0bcda8343a8ffba01b95ffda22c7bbf8551c325eee72ee52b670b2ae6a4e7e859dc957dc823791101d5bc1dc53ebe53161047b79101643c8517a04576585d6020ed98036b0172d197e9ef7ff06c36a859b9f42e6c55f100551adc7c51183b8e57fc2402ba2246aa3f5ce26d19bc63573e356ffec87b930ae2db838f80f924546b9bfce8f26eab5679b990a0bc4a24849aa2157d775ea85115e9f7e30332042c8960bb01561dbc70bb1148e48dfc7a7fe62996ab1e04fb52208e5a68df246b8b4ec46ef4fad44147985748c2af173350dafaafda53be692d3200c24cee724c0f8a8528381ba875a9c465fe8698f279c43d4939b1922bbe9aa6d33d7ca32e66befb4804d5a218e2d8efd7df0890b29103350f5efa5d64545a88fe6d0de7d9cd5faef97dac4b44dd32ea86df17270eca9df2c96a9c5a96c123c1538656ac9ea9ad4be3801aaa7bb09bc56979c11745f23181cef969a9a6fbc0c5017d40b2629e7b61bb104d4c330d501f764892b3a632f27ca97e979754d5415dc63adbc9a548ddb387dc27698c2005e9e1c0652d5cf897d9e8733459b1f10e3f39adda8a8c037c84bc25157cedb51d6fd13df18fbb0692c3f840ce705178f0717de81f020f36f2673230b8888e901afb884179dc21b57f0caa787378265d21358c6be7ac59917e4f1a9bc3b4be9962df93da2c1222471a5e40598a52d2975d79084469a8719498ba46bb267098b14a450c549b2888048cd57910e5ed99e8e31b56c49b1a4af2cdb47d775ce46814002610e4bee9d123885d8229e7be05db4eed468d9037bf6b0dcaf563a120cbefc8d4f5721949637d3019d81e16aeb3fcff10fbf10d6987629c6da88e830fcb7277f6c584a2fd95da0c51e345004ddd11f9c8ded8601ff493f5d128062507af620831b594feb4c470f845214fde086527adcd7580b1e88be64edf8febc05f4beb5092649646bc2c91bf5e1d952a5821d7839247e59719088c5a74cf05d57f5273a095a7ceccd7886e198eecd22f6eb9570d204d5a35492ceaa872704f5b05625ea64546730dc2d6da8850072d45bcea24cc3cfab7b11028abc4b1b0e42f912578b02c174401fe46d1537af883456f6d4faee3d14660dadc20223f0a642344946727fc3adac8cd9209577d1fb2555cce10adc6d9ebd0fab2c7cf9e4138da1029a9c1b621ccb6c9aa6550b3d4ec757f892abe1bd6c501d4022bbd12e02a586c08ceb3047639c21184b4de572e357bfa7f06af1d8aaf28c744a4cc1a13cc510190480f4a67d1a4a86682435ac727e2fd811dc23cbefcc2795efdbad39bdc364d1b46bd3fd39d4707fa03e369741b89d20ba02074bb7c0c6b814b3481eb092c41e017ed6dffe84ef992b8021303e488580269456b43b2ba8d1d89c62746a023ac6505f9978b261c59ea9fcb04e02f4015ba5c5b857161a8df12fe60504a4df013571224c167243859dfd2fe3f584f4fdc3b5f0a1f206e3241e55bca1099024e017cb9510b3aab82c63b8c3fd68d1094e6b0be70c3dd2e24c9683a320d83d3cbd2898166d86937cbf2f69e1523243546040fb5ca7387304067eb06e2145d4de0d617364f075fa1891ddb9d007315a9ccad130bf69ecdbdb90e1e6494fbe57e0e3c206f0c099f7d0fbbcb1d6c6b93ee83d1fe90df14891b741f7de86bb0f364f02e7b1d9544c27d6dd6d023bbe6fcad6287b220027f430736fef04e21a84f39d26874fb52ce8b22cac9a0c3abddba97d061df631a859e752d637729faa1c89dcd0e43bbc0893bdc3895039e988c33f83a291d478d3ec0e1f3e354177e50d03ffc81d2d51a1d0142a9352e9e486c27d1abb7f555458c5d51fef0a5d3ebd4569091207b1d6818b192c6a608d6583ae8295c2f8000531e67a25916bd6faf76d1f209d79692b39d5c201a695588e0535db7b9a810ece1d255797a500abce5c8287548dfc65c736e7d8ce20c492a9deb16b2731eca97f0188d18447c062b33a7f8537fab28226499d7f46b4540ac227ae99da88db9289cba1e5b684cfb2e2e2ed7e4308b5171e11039f647ecf275574a214c1a64cd8bb28d59c760a439ce56e91a740c04771ca60918f267114c6570bb18bd8ac580e5ed793e9caee14994912dd2e5d63e19190ae32e606854a651dadfb10bc50d4d31c0b3b0f6ce1b96151ec28226e973278bf964a076eb08ef1ff740aa5a168a47538a3d01078e2d681e3e5830496f57ed1b9dceede5c4479add33d8b2840eeb25883ad2bbb2867eec7b0c87ff8095b9b63bfddc08e32c38c2b67702c9f850a0e4121c40b03ba32513e9309fa0f98051cdc11bfc15454a1cc9c0b8fb1d2c9d4cd603fac00f0f0a40becab74c1032b4204e003a32623136278cf4dca60f17fd4fcaf41c9d4e67f3eb495d7a67421c76c08e55cb1901c285f36b9c0750b32eb5a818102066b8d307e72944c2c550a3cd65f8c7ceb5f173e3ae4d62cc27386cfc55da93832402b880c248c19efa30050cfe496e9018afbc96f4c2036ca1e7e1482b9b84e03c2d70511582424251d6a27ce5724b86a231a27dedf3246e0e48808819d3ad18e51e1d33435a98b0a1adaab2f25ca1bc2cc770aee7e2fa03e65ee62e3086d1d0aea39dfff2dd54d17a6039b74d2519877abd59a66f4d9f6698fa2c8adece5e94984ab346f9adf6646f319493f6ddd2eeaf4658f23d1161844d9bc372928dcb22cc804e58ed6d6113dcdc25677859b711b3ee808cb1f7514aec3dcaa6a12014e0f9f558cbf00f316cdd6d4fa3683794e2e86ee30e071876775b72bc36916e3a1face6a5d474f77db9f2b2ce1f800d2198a4cf69120ce2470a6144566b773274db49b97dcb01a97849a6c37638eb5013d5cf488a0cdf02cc80199ff98261b00cbd4fab6d203241b9afbd55f75b5a5d22a946bd84411c7ac604aea039bb0dacdde39a5015c27d82c6af3f2331d3efd9c75af95e17c5351631851e4823c88685ec13e17cf1bb8ebd68588c3b52b615ab617cf77586d4b833f1af915062bb783802e17e4d47ea391518e07a8931e39d7c48261a3648aecf3e40f6b75522848c8e343371d602a3e6368f8471ff490e00aacd31476102ff4eee093b2048dc9c198fde673fe1f75d559a887435ee643609e2737c23aa3848733cb3463596a3aad037864685281751b1b8ded8fdd5ed04684c42211981d8844b2ed2364c24cb0a64e3d8e1bc4624fbc8ef85a43ac8edec9ef126cc79b8b699aceea036e1cc0a58037aa9c1fef4b13a4b0614f633996a5a911182cb65f85699937dbb8080729f97d72c0fccf595a83e64ed5225af763d1a48b202952de239f0a30cb374e0c9a7411248552a26814c3b575efa17740584551d909d1ef37016cb2eeef606ecf17b6f0d68f9e6dbd55e16c3ce448cefd813a5519ee42717af5549a8a8358599d6d4f745a3377e587bf3049f431641eaa4820ddfaec34e2a2da641fd7f62403dffdcb085089eae40e3b96ee5e891b57841b0dd095c4d93deb2b1b07690c301889dc0c9f50752155d6218dafc3e1085907fda5587258ece263db6c62956808b3eaf806294a7eb8d276ae30e2f0b564815eb9176dfff0b9542c790579588d7cb5cc565e36437eb15758559a5823eff07130164fce1f5a150e18339ec98da2afff37232c86074b16b81f5425c02930c48b6ad9cab70b195466021d8bb00425620b0213c9e15c9db156de42f07b54b13247b964bc08ea18f8ae2ca79847ac7ffe44ad23d6ceee09e3259fb54fbe3f9318a773f15bc119da5648bec6bb0cd0c63cf1035402f641e11adafb24a1ead184f56005a5e13fab468a474e8e1b85a9a4130c865cbe57cadd4ab86985ee5a79017bb86985ee5ac91d2be4c6f95eee56c24d2b74d74ae2a60e7a4cf8a5338193ead63f700e804763749648292a3008c4309905770147030e6a6302e6c3161eaf00506beb1fafa48ace74d31d6ae9ae96417fdef0e097cad048bdc5c00d019d4a860ccb76be8a4e9e31415bce13773ee5c3dc17308b21ad441958ddf2e244e2e82444d987a6fdaa911f84f376c9a29c3d07bbb21bb8978d9c163635db533db162b39822868af928868304303cff0d386c5bb277b806702b6d626a3138c800b9cce46c19dd2358c02908c870720e2dc1344593ed70a1a9375c74e5c4b0a5d6c694694f7142b30aa8be25335c88099b33adda090443c4b45a54a931f032c67098500db89281841b0be2dac74e10e7f211d4a982805a5dcaac8c1f7c00d2380c5681f248f260e5ba82b36a3a161ec481a1c8f71725c0fe11b2c1b774026fa7f0384361bf22c031e55b16d28de490c3383332ffc2335cef86818c8ae4a041b4f4fd933d83be616b3db949e6391cd3c4bbd0332f8e7cea848d938ea9f78d062335008cb11b954d1410576a6c3068864e0c9edc488a186412bae10e739772f73e56367af12f42af77848270746a732068b0ca6f4cc665931d37b02411c8dc75f69115c19267eee7cf1a167b8c89577bcd79a0d8bfa1022c5d9aff1c8d2d3b7cd01a752281a0afaa0492541d521642714019093ea0a3cc16ea3a36b090f768c362ac23ce7131169caf1f5494ce8ce1fb3eebc34674c814b3daf16f200b8b7e1c3e8118c6ba7c2e79147ee7bd301b309d0d45a75f30026a286571241e8170820c1afdcd925c6c71a74994e49e1f26c5734887e17b51675c375266c5c9f9d590619b1e3e4d805100666bad654d8a1e0c8ac5b574750a6f35d017d854a3b1b652ff38bb9cabc2a4ebedf542ec32741e3c8af52c2a8b51d24636d4cca1d20d26648d775ae5d2c80122e80a6614ebb12c4d496001f49d187c282fbf2aa2bb9cb1b99751f674b4003388c7b069e7b7d4c6c1484b2582504abd857c14c296da3848ac147db22e8a479fee75f5df9526b7451021c0f56d8329c69ef7c20291f94325d08f0c738a2be4bf806703a75e0e3c655e81e2fe7f1c4df3e73e85b0abab1deb740105716ee106b0bc76dd3cabb51c998372a39b497474bec19bbac4c8dbe2241d7ef6e7644439901e6bb58186323332f691bc2e5f210e7e1dd18cb20d8fe6993f89f8ca575e4f7fdabef6dadfb818fc8fef82408a175a84d06f3e84a0931e1e8970a117da35318bb92238948cafef68c24d8931509144f7832543e4b52e29a13822128a33b1129471d29009d1bc947090bc1b1107addebc2a987d092c5d7e54caf821c083a1cf0e30e1935bff302882c8560c511476eafb143218d155fee461780396c64cc1420d1cb273da17887feb9be9ccbbe10105ed8792bebdbc71661bf123d4de9148039224d8fad87a6ef3126ab64b367ccd59e1da1074a99e70534ab75097a5c8097d4c4e249fc500980fc2190c363c1c6a8baf99d21356f4eb5851ea5b2d70bd0f14ed50eb6c8a07b8aa65b4ab2666f0c40f8749a061f1707ca15365ecc097d30191a2bf2bde3b1fe5b42187c36efc98f4c63d42531d63ba084e32b420b9975eb0de51e3044c7c9cbaf5ab86edae5085015f23fcdf905c3554440f5fc7951943969402e6bfc9016a646e81f8a77db5c993c703979cb1b6fe0d8f7a9a87ef9e242dcb6bcdb60f7a30760b60100c176482f0356de2937c6a238e23b4b51f02a514c8f4e18f5d3b7621b796e1c729a00b7cf9d64e10590d370aa9939f26fbe6217c15482d84581de6314100fd269534cb01bc54269ebda266fbdbd1383987a64d2f645b13b65277f47811db094bf805eff803da9461f2647e9de12467493d203909bd6ce2104af3427989fb1543bd4e62d232debe39c67b7d942b03ae0c59d0e897cd08e7345085317d8acaa80a5ba4485022f26201574abcc0ad08b0e1026a583cdff10437ba012293db9073c4b795525db2976f44e7c436450569d11f96d33f1df0085873319e4323f6d1f8befc1d8308ba2cbd08d0171fc070c417f2bdb727a02aab1e9a166d267e459d0195ef674f945e8d5416919fabadf2ac9c615819122e4e715171c1245945b7366ee41ab4f7534fa6dd86783e846e16de11261681f68dd89562abc69263c2dd60a558a38e2efea6c89de9c92100436fd6284d9a2998a453e1a4edf6f49b1a4a5eacd29b107d86913c106db2c349e846d67734cb2204e9c5c63634db44494a16e5dfbf35cfbfc618a8b4c5397f17c5aabe044df39961eeb65a5aa8e02c0331bad996e669e355dd02650d62c043fdf45572ea5fc37324bf218f7d129050b2fb2568598dee86946abfea65e7c2756e11e4ca5aee69e21805828209840c3f852accd5e810c1fed6b60ff02ea40efd964b95c570950c67c38eeac80d1b21fed5f1ba17e55922885088caab14e2373f55ce040a02718f78488a1a35e38351171ea8121c6abba85527659d651f089dc5fba15866e36ac60bf22c5e07f6a2598ee19bdc442da211bea341698df30fc006c9e95a0367e31bdeace8824fdb1a4f4ef9b6a690c85edada67370a0886dee580cefd6024b222144b97284a3557d2c08c391363df27eceb072ace176bbc961dc47ad65bdf798de72673a82d66f6a169cf3b2de60f006eeaea8b08997231c51721e7a1c6a724eed2bcc84bfb36f3d6e70a30f08e5730a45d466379a3cdbdda699f4056543c0d254471464f47e54537a460b9080015a4148041c3acce2b1d4eec812da5f93b3d6501071fd5b11a431682f468bacbbcc82b3c068a8a349afdf156018b9a5d74cca54aa82c6828b1a3b31349f481c46610bd9f0bda1492ac5761edc2b2fda13f081b2a304283b757456e045d9f2174f799aef7b5f9492ad0ba688f8c0ed0898d506b16515f88e4ce75951e6140a6209240d86390bbea6291ec5b925291e0a3988e65dbc183ef4b0adb9dd5f731f70eaeb248b1a4c51374361b064000d8036b850691cb75b5711bf42cc648b8ada427d30d33880f4a4e86f8e77053296bec3591080b2c6a0351ec0658ef01004d740b1e055dee7f9ac7bcca3b0e46c6171323d6e8f0c4d13c25cd30a7260c973006ddeb58012ad61c395deebb405d3ae760fcca7ff0d9ed1bd36ccf88bc8bfcb5fee79fdc2556d1e5cfc7308f9808c5cc9289ff5f808cddbe769ce099ea696f471b776005bf3bc40d6608513fa2fca7be8f9ad1fa2ca57d23f8d75f3924af24b1e810b09748b05b90adedd91b156247610ac0791c697877fe1413aeea201a85f88df054aa098548851d96dd0be752224852407e88a63080ca8585c38bfda80ecef1068fdd0c85f05a4ee9b74bc098a2af580c73d34fa65dce720b7b7eb5408c9eb9afb04e61ce32625bfebfb65e70ed279d05f5c1385733425502925cc6b5353792c8556438f1ca60aae655c5857466d9a9823759bd664f8fef02b7d2b27e513262d2c62e4fae0abf27bbe46f10011e9bca1aaaf7265ec8780a83f040993d832f015136d4d52b1a4609b6f44e126fff22f66dc177981ae019829f18b1476463606f60e48d8f6e15d45566f8cae420fad98ab2c01229b5d51f832e6fee8c34464987576b465b41b37416d7a8b5b3e207c6d911fba94518e8109d32d95d281f929f191dc4a9c6560571c89d3c853f0306f6dbd61f0c649b821e9d30b85a1d54e8208ca848d0be42ba55a2293e56b7487f0f40e4c6f3a2cb128d076c3a20c9e04006902e16df621c18187da0d9b312641af9e72ee2e3e880924dbfadd368a4c1ab6a4f01b03efbc5c2059ca4478bd845bd61e035699c00e7462e1ce5a7ed50f9121dbe9501025f96ded0c3fc6bfd90a1e26f8893b724f3d1dc94f261f52ece24aabfd702c738b4ade51b0488478eeead00ee442127f62240de269f6b976b831e71bde0746a8adeda9bb5f7b0e963ab072e4ee9097cc4c49fa10eef85f872da7a1487cb32f8d0b2af9c2ba46acd99be9f03f4df3aa9d2d597cb9d559e726b665c9576ccbdf12cb788cb806694ecec8a26b8556a8cac06467d00d5c432d51bde2c6d78bd84c23e91bf9cdd03aa91c3ee1fc58a4a3cbc56054e2c449d0c55305c15a5399981a229dd53793ab8721757c7eeb17b2a9bee5d6de52a6e8edd6397591e889ec1a5bbae822dd819c038e57c1958827689b5b710ed22054226dc80d1c906c0601ff60899454b9a6dad7513111112b203a50c9b0c4c0c138b2862f434ced80ad1a9c9e1a045b0d6a47704f35c5d85122a749478ae584488ec6e6557db40936b222c7ff1b1c8bbdbc9ef222c0447a4f42b7774d16569acc7ca47788a1ea2b3e029446f1c8743741d0e4aafc331ba0e47d774e9077da4f6ea2f6cbc15cf62e5182cbda3b642938328d9d1a18aecb430079b4c80ecb4b0b324832e8a63edeb7165f8782d10a853f22b62df7b57cb52898db8865264156cffc4888d6f1ad7da2624479792d2cf39088844ae0d19d295bb954b93409268743d0b878cb019f8e0a327432a628d48834de01c1c099ee52b17358e33b0353b251986e14ca3c311a65a4031a8186c3c8c1617c59161a0ee6aec2a2c154288d48aa374e53e2cda08f8b2b80f87d63cdb35ad556a13638ca76e51975601064bbfd19aaec9f56a395317de6a9d1853157e46328fd6627614870bbbb9244d7429de071051ae8f5a86af5ad04b71b48629292a1736535d0b80e28d913760b3bde72b50c031c6c8f6d9ad5f8f7f176567c4629fc7deef18b2353b21cab6fe5d20228a43b5f882bd22ecd6cb73df5aafb05c340019de723a9bf3c28a31c6537fb9468a6297a2225a6119b5c870f65d9326c99417ecfb176cf3e811638c9f80c3e28d7e7db016cb65f5fd215befea7b65ab2f7875add646095b3e493d5d45961f1b6c3f073878746b994717ecd6356da44bd0a64945baf4baa66bba8a26c1c7ae4962fb220a87d8482ed8510e6aa335f8adc1972dd82d6ac1c62adaa4c521ae06a55186e2e0582d57a48eb40c915f1138e9119905bb511c8a3645222bebc23c19c514a0c5164d8f5013d5a288ab297d79c91bad89d516f6bd8fcc215d7ab8886913f3b070259a02a715c195684d6b5042d34a4e129d8793db82a7298040bc0421c4101a0923c9f00f0bda9852bdb516385a746f515353535ffd46b7a8544551af6a156d72cd83e24d664886a2c75414a5349e7e2042a4881118e5a495aa7e70b9220f327ce441bc8957443969a52acb72b95e116b41486ba089058b7268b9be48164c323932ae0635afb871f38883e1d15a1c4074ec9b157191e12b51d7502d2e9c87e5d070b050d3261d90041f7dc4115d5844bf68520dcec497b8a2284ed71431c20e9258aca53594c915ec366f722cb48143201157e351b556b55ab55eb562b57a6acd6ab5b5be1b23f71281410b54c8cf00f9e1eb88fc80b85e9197ebd9bd04110fc51b233f77b582b844ad491cd82134120b00dcad1934aefec2b2bc1bd88e017836a7d3a90438dcbca922432b3c242c2c773844f8219171d145788ad103445ff90afec048f41519d835de1e4e7eafe53e246d04fce83a485ac9c9f05dd346c0af5c774749bc538e6d5af968c72957c1bef785445a835fb95052c1f6bb8676cdaf75cd13ec466b5c924f76b78cb85e115bba9c33b1dc3d2538130b5eb150c822fcac90e12dd3f5bab0a0ec4e716ab55624fa29ceebc2da9a23af0b2bb58826d12dd511dd3ed2da14accb1bbd14ec26ca4d7120091e5e46c16eaf0a89632386266bc9746b3205b82414585e235a48ea5880cb41d3c4e16ed5b8e413ecccceb518f9fd3569edd4568dcb86661d71bd22560ba7b10cf01bed6c5cae4b29d9dd3ae2924e88cfd1c49102740d34cd1b68bda0f761711f126ea0e925e191e061159064f89e90e1b779843a68b766d2471a8b36b11cbe6bda069a56a4ce14a48e732588738be5c6b67dd114a40e4e1fd6a821b3b0540d2f20cb45f196eb073b4219a7a4f3521c1c1bf1ab545b1e6ca66f59ef3d4ab5e812565ffd7defb11fc5c9a15b6033afe17bdf77df91ee1729c5740b9ad31a9cc154a7358829ce7b1137c581d8dd60be70148f7ab1b5914dcb5abc2d1ad3b8cc8512baa44b11d32a74ba347a98d6509bd6b42f280e8d0b23cb503132b4a70c1bd321998a0c2f9b6037fa8d3e99778af3228df401e9525bb0f176156913c56912a498071936136ce8ab02a74b6e0936d674c9a699349303500cc9a39a80104211ada15574c9c6000740c08d78651c09a01712981291d67489b21070398aa3655984ead28db6ac8c233e1bd4bb43f48baf4f2ea9049bddad0fe663e522dc0f109d05770dbc418dabd1376dea9b216dea217d829650deba86d6c4f8c172c0dc49b0f4db3b824422c1461f3c5c0dea69d124687a479a042fc64d1846be20e285cd920c214e4b9ba04bc245ae9870a53e81d35ca9ab70193643e19658d88de2f4cdbc621e7135eae12d1c09406226078d0e4c891d17c5141043580e2a3f8bf34e0ea6ce5cae4b43e4ea56f0b469127cc32619be5e2a534abd2356acc418ad10ddca71c57923c2b466dec0cf6039b41c9d680dda6002adc16e1aaf15a2575a13338c3d24c3d31a4b014c6a1ccc11baa64b558692499736fa6622bd680dfe6dc1c677dea44e86d4174e49d6f1825ac14471e8149c0f57a3ca9d240b19de83cdb43482ed77de1cbe5a63d2a522d88de2501c570325439b3ed8c631c6dede913ebdcb1188cc280795e35d7677665ae120adc1770492e045a223f5baee575bb8c7d2fdb6b82b2611bedee9ad1c39b09919a3744364eb5d3619d21a52a687828d96690d34890e4598077c4d4e72246b99622b44a76ef34892e1a108d31c4882a74a32bc8e5cffbab05bd7740da53514e7886d4aab3baa2e473c33c5e912a4a72e4571ea374a45548bd660d734091e8a6aba86067b2782158d1ece41658ad35a66f9ca2693fb14079a641c4e4e866759b99aa56308a3961d3fb05208b67fc0e25de22d055cf20abb754d697ba7fcba26c3cb20d8172c89cd476b103b2186d04800984103260031021800018a30a2945993cce7a400356c1c618017d001107023e3a828dd396082e55eb6d8857db7ae36b998ae2b14dac946b0bb75cd93199a7182263573e614799e5e7c030a99f08b6763877e2b22673472f6926557ce1edbf46cf00644268221b125e67cf6f8c59cd8cc9c339efcba2cb75d3a397cfce26212c1882787d208c466288e4c5aa357ce0f78ab55c66bc19be821bc5d43c1541daa0846dc79603c22e8afd626b6ad39275ac1708ca6fdd6b381a6d61ac9b311897e4d8b89b9b2ec9b966bcdb22c9bd7675eec6a24319af68b44d6b64552abb522d1af693131d9ad8c04bfcb49807fdfd88889dfe53d8e1d24f5c018e260580349efc13746233b5a88d144286bc4686076695cce3e311d2e6738e52ad9ad09fbb998d15c3b2e0c330c4f0e26c339a88cf58eb5d65a4cd43bbd83c568da6fe79c380e8137b07af5edd537adfa1683596badb5f6169665d9a9eca0ec317b28fb4976c92cace85b1c82b798184dfb43781389a0ce077f74e6bb88181adacbe7e7373bf1064426c7225bcd710895b14b6599855df9168740cfcadd5c8efbe213453c7d7428c1a3878f0934610127803c11448a29de89fe824cc078365c867127df687b5deea1d0c7e4f277e7725d93b2bbef6497788b529a65f44d816fc0795d7758769ddccf3c85a733cbf0c95d9bee096e26bc892d61e50c53fa2c3b0e85cdd0ecd26f4228a538577c19d4a5cf4fde489e0df7f9e7f3c6e9d2c93fbd332fe3607f6497d8ec509768865345bfb0990c9b992e04e7c4f006fcdc93ebf6b5c3ca145bf736156df34e7d6a2d7b46f149646dadaf1813d9b52ed7e1e89d2e510f6b40e3a890d48717e5f4dabbdc8723bb17fe4224b76b7583dde08da5b7bd53c19bd6688621132a3b8c62a853abb53756b8c02b906c229b65f0ba60e81bd4d9e952186d72f9df77b9f7780b1233c59009d469ada96c2f95fb04d6f30deab89c1ce4721b477577621c1757a67a278ad838fa3145c81c7183c621d12537aff77974c966effb98401398c665fa205d1ad1bb9cbddde3c9c99d6d9c7ca3af5a33ea5dd9057dde8f925afff9bc667babde1ff5f69e7f6ee35428680e4141b33d3663fff96c9d3d9f3b3f9ed7d02136333f0fdd7a4f15921fb603f46ac197af93ead7754aade35cd645274629bc317b765da634a334cb32680631f2c3ac89cd549fc76e46e960ad83268ce211132eff7bb8fc780a21d04f81618a7bb8fc3f9ec2050bc17172066bef5ac368aa83b1af0e06d21bd0669fafd9af5b9febf17842f7787e03ce19aad565cfe703ca3ecf3eefe7d9bcfd3bf2b9f3d9751974df9cd5cedb389b909cd5db8acd647834efae7b72b7ecd5b5e8adb2ec5595ddcd5e034272969dbc72f973879d602868fed8bf25ae678de41191ddbe23ad65ffd8eb72f6eb36131e119914996658761ba7b5ecf46e422ef6ec422868c6e01d865df14ecbe5aa566c664623325a4717aefea0c16e356b90044db0b7182b543f3a3692609e3a09e8eb3f2004f90fcc53ff007d7dcf53ef314fe11ef4153b486a1c924b6cfce99dd7c5861d61606d419c9555388855eba3b4e78452c6182584b33b5757c2c1be6cf58e5a2b76b3fe066c263c22dcfb6e2f0b19636b0d7760c1e8221e31d17f7fc0c4ce3eec20e92736b6bfbd9a131bfb5ee4d96032440d0ef867638310a821c317f16cbce3d8e97e5758c24e15e5d4c23ed9c347b45db21e1f125d8fd8a3e771f2f0f00828166fefe40ecb31439846935ae56a27d7b921c7a3382d43d0ddaefc36203259d61c2b95944eb9ca5f91216d123d3eaebc21364a6cde13ddb7a449f12af73181a4f894f8d1b1551434ae55ee54ee95725d935e489bf5049a521e7fe9e227643394d0536e6c12fcd80ce56e0fc9f670367b855e4a11c9456b31254a93ca33ad7c8bd6e257ee4bd2a498b3c46ecf467b3943daa46293f2887295d0534244e2b3b9c99149132b74fb2dd1f1e27d114697240e5df864140ba0df1a43cb59ae5a65b0a14794c73f1d57033efeef75f6b25b0f8b6207fd66af634779e890ca7d35a94f9df43ff610e5c6d06b255958ea6ef5e4da1c498ed9dda8bc3d259ebb5d8f9f401316e8923cc4340ec8135d92a24bf0bab1b5ead6ad0ea672d7b3b6ab94d4b5226ab556a484469bda29a17d5262e5f694c0d71b5fec183dd29af06af929b1b3b1a56d64f421bb981d8410426865fa418109995c828dad550458cecae47fc857c6ae27d36cfddd2c5bf5a2920ff62df226c3cb5fc62cfbf2cf04362671350890e5452d7fe9d275f998cd6d91c99c232ea955597b440e34bdc867789349b23c96932f2cd39a14b5b426af2c2f2f2b29bd85a63ebd089ae0e90f4df1f45a9762a68fe9120d2a1f2d0a4ff9f9e90185136e1143ea542c58520cfb1460f5adbe4c600d60a2e1636b180d8c30be1863742d9e8d03199ec67111c1885f442632323231da7b0f8c9d182f4a0f92952733c208238c30e2247e2a2c2346631dbb6559007481dcb546d55aebc927da67f7880cc3883b599e56265fec60b74626d82beec4989344d7ae64eb93ad53ceb22c0b6f40642218118c4784bc73a2fa3ceb3bbddb8c0c2d301e112f82f17997142c9745b6de05afe305dde643f4b552ef4c4abc595575f24ade52d6a5b4eeb9d52d6aade52e84625df0cae265e5f599d709c85df417fd0df8b92ed7fb3eafb54ee3c3c9d62936d34c386ccbea19cb83cd34ae4bb01896d1666a59d6e575bdba1ad42feb533e05624b58d992b73c28957b8f0f8b2c253623fbf0a42b1c2a6374aec6bbeeaf23d8f923a143892ebd78449e169aa0897a3422c628e34cb5e2902bf59c96e22d096482ebd7816aa5a4b44bb3479378b4c6e35276f77b33ef0e068aa08906bc6ad5e09d9607db976dd34b8cf1d13f4cfe61f1925af07aa9262a9a6813d04461ad7a36227e2b7844c06b3a3a2fc61763b4175e0ad30a9e0d9223021e3a2bc0b736c6cb11c3d8d812cb59d9ebc3f63617218eab71a37879b333103fd1c46eb5b60e183859cedcae6429a59476962d8d5b28b4b5c88e30f68b2e52cfd59a7c96e35629ea53b3a41c495607d305bb392d5c956cb0f1cf069a8a40424797e0e595e8d2bc3c8f2e6197efd1257af96c4ca07b9e9daaacbb3ec0c9af6397073481b08f26c94b89df49fb58782b42cbd2ed644a7ab06b52c55b8ba418091dadc9cfab446bf2468c4088a4357927df11235a1c81cf083492e5dd8bc43f1e0ed5b1e6ddcbf62c6fcd33587a778bde79b2d4ab59c4be316cb57048f6c1de801a109917f9b4b08ce4d733260beb80a4f80a53b80beb5ab6abdd2d34557ddf8c83894a28a2841b7af516998b248491671c4cb4c91676bd1b917030f16dc14b5e29a6b8ef741f154d8a46bcb7086cbc14ef54458ef14ee888f1d5ba3c98129802e60dc8c476fcfdf6312cccafe2adb31b438ece0b0e0b39c61b0e2652f79aeee13e59884493e229de42f9ca8d1bbbd15afc3b438c5a6b4f87abf1a28d6c4b0a96b3b2d311b68a1bd8cf4d9effdc40d3e7f39f23d0643fff41024df1f3283aae861079fe93f349f2e9c2b70fce4592e72f928b73a310c48eaba14dfae0b49e911b58eb949f092c3d3de56a5ca7ee5bcbcd10b9e5069adea6422488ac4224450792a68e8e0d529028a9ee01f2f6137b40fc07cf500249f3f6ced0b9815d837d3a364e3336c7c1cc4b2fec6689e469ed8d25628db81ad8e76d0e5c8deb9608923c2d1152154d9a43304f663f27a0d0bb81850740f664f673020aa1c8d82f53bc552067479a74a907904d36a749f349c2a0becd10c3d613500825c552b1acbe2f67cb7e4e40219414954b001f8a3aa128104585280a85a252284a85a24414e56a749e9fb164860e34358e194a6634a171ca2bb0cf045608262757e325cf77130bbf0991034575ea188d1de26af4e72d0c9c0d9b53854d972a19e4796843adcdaa49bd6acbaa6b13d9a49262e4f99ea719823982ea26cff69cbaed82b391a26373ba447dde6e6193b81a594a1abef017cf33518b88ca6cd880144680e79b8d8cf264293a29697899f238ec0d79fe82439eaff0269be4f9cd3ad5d7d0b309c9d48dd8c9f346ecd4b8713580c8f3463431428cd2a982d082f0821083d0036106a185f003e1c78b2e550f808d5393e6679da1a44f5dba3396ccd09981dd6ca8c16e3394cc50f26c7408e0fb798b09518a42690aa52a948a285da19485d211a5d0c6096c9c6c5061a3c6d5a8365290e70d6c9eb766d8c1d9b039332616f09067ece4b9cd0823cf5b28559552552a55255a6119b598608612687a9f9ff14597a83c1f63e36a90f2fc0c9d195ee4b9cd6002a10b8487f04288210402c217088380b72eec69c1d257ea3145f4b718b993a746a3b53943c97ccc7c8c125783fa8c45c8a73cbfcd5092e73b6b1ddbdfacd37da2b5f96ee2f290e7e71eb090a7dbd1e1863cdd2b6c867a34b215f64e3898791dd064f1cc79afe6749f8e3a9c13009a3c93e7797ec8906ad9b2aceb59588e97e7dffb7b3526cab81a2fc7c428f1609bde80313158123147d4dfe18cc1d908a5a49794d2fa96a2dcf256d7797937a7458e9fae663902f70a4daf94be37deea67534ad56aad8edc9791690242082185b549efdf9db4c69d1ed8ccf84429554aec1e11321e9a2a96398cd88a7a7a23707d381878196892c0ab588a37647829e3d290e19f4c8652ba243cde1367b0fd28e28d12bb9d27ba140f372746fd8d9637486b3c2a4e02934c22967bb981f0c67a4f36a171d6879d4d5fadbb9b5e2a8a983797c425e911a353c2e4ae99d058e92dea7a5dc0537547fa1487c8f0044dd2083472ca9094e31111964920ace0957051ddeae15458be7ef050f4e6c81594482412a991488bd02115ad2a55cd8a7afaf166d95e276d53299b24491776dad43baf68ad3538b3c810463c6f4eb6ad0cff03dae44897b678e48a78135f92c785922b8e401c9c8638d3e6bdd82f763f3c87d84c22adc1d82ff62c02a74d55554d2613f88b89176065024d9db393eba96fd11a912e3d13e5c26971c4114da3bb6b1ea64968cebca93e7ab4c6830b1627becf254d829f4b54442b2ca316193f3c240ee6c98869ce1decf6dc8d6e127c3fa9237f805d6b508621bdd0690d4a2638120fb6c2dbcb62496bb0566b45a2c318690db4a13536b04816d0c6c6e68a184633914c62a494d6446a7324b68cac456efa6da35eff7af8b86f08b5a9a1554dcb29d82c6884367deb4ac9ce93dde733419c285d0dda6f029b68f909627463f7888038d024759e7d905e47b1f5e07b13982d9bcc37a9d83d22ea6d5a83c1a53178d40719be627a830c8fd1b9844986efc96432a9d38b2e6d947a9d5e4c1dfacedb5c926175fbe60aec967449274992d89f492016ad69fdfaf57b930734adfc48bfe31fd9e29ad2ba5e110b6d2692790476e8c6b25e17d6bab4daa679d045ec2336f3798c168b26c167a1856c3dd8cd25a153a6c16e2e098f7e2f1ec1723c4da955a55651ad2bb5b2d43aaab5a55619b5ca79723062b40691504be97d481e16408ef4b469126ce7a6cd11d964da64c74e3cbfb0990b05a2c1524fd22a9747e5ba3c297874612b58ee320ab66225846521ac0996612b44203cc24ef0c4355b853694524adf9a6c72411938130bf6017100ade837581eb55a5b0240865c402e8ad80c21e26a50abd6ab56ac564fad59adb6d64fadf43aa9b5699c365da2bab02ef74c5b9665b5d5dd4d598fe7e3f19c783c208f27e4f1a0783c291e8f8ac743bd6247313613773cd529bd55dcec5d11c52a5af495d8a3abb0c95de392f4e8526c8ad5eaa935abd5d6faa9f5a45650ada15a31ac8af42d1b4b2bac3c36e58d22e6cf55636a1e4d825c64ebf89d78f8a0ffcc54af2ff7a9166d06bb72d1378a6362b14697e63409e26c5d73961ada550645fd40512e14758aba1485290a088a7aa128fcb2c874e58a9aa21e23164f511815efb05ad146d231a62c717da336455a832e97c16675c542523ccb5db9587d750b93e20644a66f740541758755faea7604a36f3cbdf18b4744bfe2c82482f188e85329e24edc7958c466a445712014345e4adfb752f45241e372bdbca38aa358c0f58de268af794b0436079a463d333c6542bfd871352c3c6262e5a2f71861168c45d8fa21cb5cb2ec5976b30c67191059f6926541645975b5086aad225c9368984b42066bd39af64e514a8720c10b0033e445635e3001b89aa23c17b6ae85cd54585a612fbc691a8ed8dd348a034db4a64990da8e0d3b7afad9ddae1c67ae78b79d72474c04f197f708023b487ac13d62c472c8e922ec27b17530f00e0694e38d9125e830e4b2b2324f574e57565080c86c200c44267b3e51ee728982636b1ebc8d64b4b48ca688e5a2b3884422118be823d1487459226c7d645d5eeb638d46a3c35b3b87ec2f679fb8fff068c7f5f9ebf33996dd619f63d731ec1f1048468785d292527fb050e64538e507199607a5056f3f0ee14d83fd794f0eba1b78394be2da911dc3dbe7d9dd7afb406c06aa80de98dcb1f320bcc9544fb93f3ea79eddce0e933bb655541a884cfe60ec2ebf01edc9dd746497476c660444266fe0613b50f2e57287cdfc0fbbfedb44161425437dce6f9f2eb75ce460af4b82b175a664327585e4342976774edbe4b48d8bc8c5a6e04d2452c12e2e2e2e6f6cc6053bfb743a070cbb75ce4e18395b24e142c9121d2f987cd12578952c1722ae7d74c90929a005ba34278eadbd390fb1a98722a1fba8a096754707dd95fb682d4208218412db11732814bac3243613fa3b621d5af8405c823e55be3d24ced960f9f69cb0019b5924e8d39a204b5ef877a491e0cc9d77a499007142d7056f2fbb7cded1e896351a591fbdb1991176b647d70abd106461f9e896e7826ebd6be5ca7b2e089b81f27206b605baf4f826ba5402cdcfbb7c5eeb139b71b9f5ac14adc5d3e706209ee251d15a8c31d65ab3e7d63929a5d97329a574ce993d77d65aa594d97325455131c6ecb9b1aa2a0861f65c6859567767cfedeb7a3856e5d785cda87ce5beec5139044da03579d01f46d3848389a2b78f2ec90bf194bb36bd1ce5ae4d2917e2c6eb5c935e2ec43eba4451198a6e7430f2405c7c1d11f22c1688e6089dd2cb1c5506ddbacb75f9aa32cadd6a4e79cbdd44390547117395e3a598a24bf451d1a510de82c86859967c83a5fe4e5560d6e773908c4b9796ff70eb07e82a077dde02c12452aef2bb3591650a4882a41c85ae82b7118dca27de40a0bbdc4406894022900824028940229008240a7d45e5220be4297ae820516805247a48c482432b786b22abbc3172e81975297f35e1237e02d17991e3bc50f419fabcc396503988e68d91530e7aada310cef1c6c829188a3746ad8fd29e134a19639410ce6e19fa4aca574237b6167aca65282547e8290fe1ea60404fc1d5c1cc3746be0141575e74b7d069cb535a2e0f25c6809765ca1dc60095bb819e824f6a60413888c42ef18f96a7e01fd7651009c2416476f9757a8825415dc6d65cf03d9778fb2106810f5b00ca02503e42b91b08f2687e24ef0874a832ba9c071da28cee198154f0e679109847df229431c232ee91717965e043bc058159c23feb3994f71cfe444229e51b50fe32ca27fe017a78b4839aa0797f80fe5982fa07f40ff5cfe7201578b726f25c09b5c0d18a0f166badcfab8282f2872541fdf3c11be8281be828b750eed6d072180d7ccae75579cff91b10cedba7dc1fa0d7d0516e672bf410fe013af56de2203283f0e7d466795dfe6cdf9af06014756b567f18f5ea7692d662e7e8d4c75a6dec1cbcc1d3254c2cf59c36d8cd29c979ae5748f6014d6d53a585a2a9c54c236dc2070dd506db597469735cb44d97e41022458cf0a06fba74c591c6a24797e69c129e7ee80c84c1925fd85a1da56fce9612c6182194dd33935fd89773bdbb90f7fa043b96669e788fbc73f221c1dbf3380ca3719f72be712e6c46ce997877abfae89cdbc2794e29b5b516d78febb17aaec76f419ae2ac71e82ba62879790affb81cc5cd04bcbd7c5152cabb9f468a72905e188dbdb3b0259c8cd45a47ab88e92929dd69bdea754977379b614f21e31679830fd29483a7f27483b1e2ad3375d938191ec24b77eaf11b902bc6ccc9e8dccb943cc568dc2b0c43f7f3774fde1774cfb55c8c91fa45c58847f5f49002b9f275d089e7736393307cd1ffa0f075882d71658a370d58524ae91cb6820489b0e21ff2f1cad4293d05e49291de087fa3afed6591e51f162d5f5f96977f11839e0ee1e075d5b72c0cc51be3a26a7d94f69c50ca18a3847076e7685d09870829485914760e0656950272658ab38097f5c127c8cbab41271b919d16d470639fbd5ff2977cafaaaaaaf07b184683f2f8e8de83d481b8a05728c777fe3fcb7591315a518915a55486aa5a250de11ff5f4d6c178364297f51510afb6cef440e0adabad888d86ebaaaa70a632128f420fdf68239f3be811e276886319e9270ba83edf65a6f7a4061605c368e4df75397f7212de6234bc694360234204f1f24e9dc268e47f58787b95acaaea5975acbaadee0122f391ef93a2a4917a670e5d97eb7b7f36c39e424626324816236f557dd4466d2f57fce3456c098ab7ce28f85bdcc9d4231854e22dee64262897d589146143a3469e5a9a3801c4e50382121fb6847c2854f58ad1a0fc37201017747cf3c9dd62eeafc70a00bcca09688a81a4fec55b75c19bfd818988056fc7a28e89c9a0c77cbec55c643f3219c39f3b6c01d63d1f3cc21e7af51bfd8ca8e02d46d38e82b78b4eb0910c6f167b5e3d744f6a6081c8647bf2ead74117a3b1ff718233fba7a486469f5e9ab4ece4fef4c7f3ead6f3ecba2c3933d6816cdd825711d9b22cabb22ccbaa7e58bf5ebdca9675e8a90e805baf88d37889eee5f7504726d37609ab5ea1a96ea1fc6a06c0b56d6977f7c43e5d58d86da3f589585bdcc999a70f6dc46862ac129ba9685cfe5c623a5cfee0132336e6ed93ede127ca4f4e82d041283f790f14ece0c97b84b083a0d32a3df5d4629f9ae387e6f899397e648e9f98e307e6f8c9f1f3f9cbf19b10ebc1f2e3a9d9e2adef3eef75ccf31bb081c8e40b47301e113787ccf4d5a56e8e97e92bb6f193411085642d5fcc0f5e124adb971036ec977998f221295e14469c940e241e123a5a8b7f5cb4169130491d1d9c2552496bf13ab0dbb9b0de83b0bb9b476b11421a4f217c144b2f5a8b4cda88a8f303fae7832875fa51c80c44091523a143eec1c653bcb92da6bcd1a4789c89e580f9861e6ca4f9c887e58d6db2f16f89857227fbe2883bb131a6d0633df966ed379114d0f479ac304ead216b4122d1c96f352d3ff7aa3b76cbeed94dbb6b37d55d755f957abdacab713dd66aad83a6ea56f68917485e21144c8994cbba2eca63e10a43528f8aa51596e20d5e3247aca692bc9b6a8524497c1b6a685f114f06e6bf4408618dd530c69b5d98d35eafc76bbdabe5b9d4b35bbdaff5eb52ebb24cb6e596455dcf73a93f8cc6b29ebd6e5dd775e7bb5aaf81ad75de97eda5e2f3121c876579f97a167b633b0490adf997b17aecd25b27466c7dac91baa147c4fce7a63801ccdbabe20430f155b119dc165fd8d3510b59ed18639d4aa691d3e9a4022ca421f74fa414ac108391d3e954c591dcffd41e489bd3e9c48297be5d833c2121f7b38b0a5728128d9c4ea72143f43d9516ea104fa7d3c9055be4e43e66e14ca10ab8733a9d5090f52f2a070559d0399d4e33e85b9404b94fa7930cd890fb558c821a9aec9c4ea714001187dca7680f744ea7d30a5efaf553a4939c4ea7197cfab44242e39c4ea7169c20f7e7091688ace1c8e974a2c1cb0b725fd62a3c23a7d3890541f420f7230686dcbf562084dc879415b6a08698d3e96403041372bf2184f377be4b59ee2a88a1090e8e389da690fb4f64fbd714e527ef601f16aaa0418678abb9ada0052fe4ce8da39e4e3cc87d27ca8fe5c6725656638c31c618638c31c6189feb86f175cb7791987130391ea9df61c8bd851e223f19a3c537d86037a7c40774f0222545c0e8a34bf0d0076c7984392bfc8d7e36b032ba7a75297ee2709faf07d15461bd7c703998ab4b11c2f86e1430f70860a4b2654d8f943e5a83d9ad87635ff68824de9adfb1bfa65f30d18948deaf379cd92111680399c028b3774ee3548f328ad16487ba9cf05d91b3bf9b26f5f3e1d89779b2fbe892e759760f36935df1be7cbd7ba8e8a34bf1797ebb1c36a3b3411b9c9d67a48e248ed5024da4c1becf96b8deceb54b127b1e3558efbd771f1378f7f5c10ae9a784e59c732e4608f160bd22f685a096372bdf2bb25db5d42fd0a4bdb818e304cbbd14d1648e08b5d030fd5908ab95c93026c36b9e6c8c3d5a832ec7478fd6641f1abe4a29e527f06efa6075ec60b991d3fa5ba4e12e1ae507490fb602341a5eb02c88631d8c73779ae59c2be2edc06e3177c52207690f58a1fcbab04fd4a6ea4d2f0487bc9da7ef31e3a7a89f22ea135b472776d6a785e1abfb449f80c450b84292ab31b3d599a7b649a21a3af18b0d7160b997adeb7bdda78dad67f33e591e3f6d9a144fc5e82cb746a1fb50f9ca7d881c347d40083d589ef20f8cf0c8ae5cb2e091c5a3106662e52aefb18255dee3a2a760f9bc682dfe2d794ae410a7e4d93c9b5a53eea9d54211ca370d2bf9a0c1827ea3458f28f61bcc190e25d355c2e44d184d5f1732d9d1d1319930799969e494e96cb65eb7b3765f929cfc2b27a7cac2adae5025a9b8505231698aab1c0773ca744e5b9686dc97290036235f008ca6f3b56288d36b85f64a3ba5c91c9871f16c482439e322f7b5fb863872a49a4b9af46c54577044545ee43ec55928409533026bd5a874d6a8e12ccf3c0c40021e7c48cc5c159a84f895dd85b8efc661563c239808722e92b147e4c6352fbc320723c0084221d7b5e634f90880cdd0cb91e40a18f25625c92de3e10980edb8168e4bde6a8b2a4995d344b02f01d8b3295d9eebb2b559cf6eef76657b59d352646c8a8ce1cdbe52314560ad6ff274a20efa0fea27bf40d853b07bf2024ba150b74199b01dd44f6ec9603ba2f440fefcd2b0cb603b64fe5c064b02464161589ca200d88e983fb7ba06b6a34f99c2d483d40c7a83207f9e83c15e039b39d94067aaba306cc2663ef686b2e7be9cdd1f3297e7faa94b316649c5cbb50f50ef621f4088d79fc464216eb5458d4b22f1dc97adc668ac29a85308b82e02b0779a46a0a9ca11699b8f4c5d9ede17a759b766ae8c250173f52a07b6c8557ce109646cc77325b1b442085c6dd1a43e12550e12d8ab9999cf39ae1d254c72bfd2c9b1aecaa97220a95f104af2263b1a910e2d94b311771e1116bc164d9c000de64349a503db71738eef56a7494d9e5eeb3479a905d72147da99efce5bdcc97d29b003db7173ac0a883d1480f148924412d8cca3d975094e91a93bcda278f22089a74412980e25ae67e795c06624126bca2c9690482c89434f3db649884b1c99c55b8344229108712515f274844c6b7d194bbeb6f624129985c4a1164500460046e25202bbd15a5f4e234dea9afb42dc4c0903322e5aeb6cc9257520a9ad4a94b325adf50c818ddf322e322e321d6892279ce9f0b8444b6033f1f3667d7a395d6671258e44424353024ba2665c644ab225f8cd1b581200000310c090633cc545b426ca312f008be9c880160600012e115a02b3b67ad3750972e039923c0f6c0795e3df9d3cb8938969e46202b31a5051b5011efae7e80c3613692ee76ac064301eec82b1602aaac2d0092361606060602284e94700cf3ccc765efa91c1c67acfedee8e2c498e2f7be66ff46b18e5dc6a86f856071391004404003000210c22cb8270d391a38dcf351010426821c9950e217d0f52b7074283a61b90d40e705a9778b4cb65dcdfbaa1ff70efeb05f970a2df2af587bf17f164628b8cbf17a3122cf61bd0a4411e5d9269cde52d27c12fe393fdd380f70347ea43e14834ef074eeb43e1b4f7f2088fde5d2e0447cb1d3d3c62a2e5f73d5a7eff7ef11442782e2d1782e315b11f21d895832e7a4d796f166cc5bde85af1c353feb8783f80dea1f7ca3be2f2933f1e74c9723550de0e9ab427a48026197f990d983f77ae24e363a56b7622e587c35b44b98e4611d7e5bac7285ededccefb416e1c6acaccf76dcc229a46cb68b9536b6dce335878286fb48b8ea5311af4c300217d90ce1042bc0999e20bf807f37cf4b96f71c74108a1f6ea2a290f9ddcded55f6fb9a3cb72452a17884c45c15b05e1cd9ee00c6fc79b76b92da6c21b101948c10846040387fe80e63deb5b5d29cd5ba5336f79ab53cabc559a0873de2a0decdc18cd870818cdcbaf725990a1d673d198f9c1dbcb9f4d47b6288aa2286a9e2e01fa880995a35c022ad84112ca2520e426634fb90bddd2c40a430c70427290b1cb0ec110ec2741212146327688851ea2f026c426633a328637ebeec683aec8c1c014948ca258364f2cde2cde4422bcfd56c55b4c0cca8ea52b199e343562ed8077d98809f9f81e1247f75cb77530eed6c1bc5b59f7eb13c8cc8dc6c9b7175008e5eeee0e05876a05dddcdc38d1a4be6501ed17899e705e94c1be1b68b280cdf40948ea530b54efe6dd74f5fe2bd2a677c5bb7902f6c42e0bdbab56f63efcaa0abff79bcbd335c2bff770140282fc5eebf681fc5e73bc3fa09862071430d7e76aadf5159a4e5ef1861dd253b7f4273553fa10aecfb2bb365992c5b86277f166fde4534f6a60311c24665a7d02b932ad6ecd533f2a4c83507fa75ef10f8a25c4a1d6b29e5a58d125698c0992c4662836b11cd445a9caba282c074d3f07234653610aa3d1b64f96d80cfd4665f98a55b7aceba281580eadaa301aebbaaa276f619785f1a0820424b03cd785e5700e46ce5454f5a9ac675996656599bd28ebaab0998a890ba3a1b9ba757261183dc11ae0791e8fc7e3715d3dd5319afaea16cb61e58aadcf75d54a5a9e0bc3662613194643f39c01610d709064319acffb7c3e9f8febeaf96034d5e73f580e2b57d83ab9322a4f8be5b03e957e3cf53455904c61f87ec555900d42f8475fc503abdeb21e3653df7bdd0daf0b466c47ad568c71a4c309c9d4b70fc47a3446436114891a90dd23016b292a62d62dd9524a299fab1eeb37da3a0c85ac5b315bf259ee529a93ec9e93e0739bdd73dbe323319acfa4d88ef7264663690d654a319aac5ef53620bbe714d6c32353693cd4552fcd75eb15b6e3a26e5118cd55311a944cb1252eec1c8c85d184f275d58b613a5cb6f016f3c6c817f6b981a54e6f6118cdcb966561fd30072b0ff663385b028a3746bef06847152453789bef8f765441f2fc289e06fe5dc828e21df00f045148d516105ebefb647aca99aaeb59c1aa3af5783c1e8fc7635d48d09c9cbac3769cd0807ea2237f7e829bb8913f78b377f6c667f7c1cb0b6f29579427a5df2ce548f3a13be7a94b27762a197e5a7661f5adae94ce29658c103e0987e7de8d2e473c3c1e230b81af5ff0d3e91444ea5cf6ba359eb658599f565cb77a34a93fef045a6b3358eb5511326dba74b409a3b7eaede1a349bdc4feba2f4d6a1a5dda6c2c620c1b4d32b3e545b05a55c5db11ad751152a7760c83e568f7732d6dbaf013167ec91b0d2760ad22d126ca22263a16be42936beb756137518e22893941fdfd0694378af830d7daa494e6c06e2e8949e48386ca407abbae8adbc833831d4d9c6fb4135d3ac94e74e9e344976c7e02c6fa6aad48742d5278ab9352e2a2a85f4e74c9e344973027ba746527ba6439d1a5ea72a24b548ef43353bc71e019a97ef05e2720ce1df4bbd9628c317e4689a38389f2e1e8ab3125846c557e432cc4169240d620565a632cc97813f700f360b7500b3da52d4fb1a845f6154fe44a55e46accdad2f43224aad493f8d2c1c81bbae2b574895ed45a0b3dc5a19bafb02d96a42e630f75a9be9ddc5eb6e41dedd85ecef4be21768bf951ac514a9f1f95724280c66a39f89e7b6e7ab61f595e3e8af8d21ac5a3ce146f17c8128fe885648a6f1cbd0c33c5a31d549eca18d8ade61703bb499bfc6ec576f718e1bd76ce5defbde75cbff78ee5ac4cebd2bc28d12d246914a594524a29a594524ae9cc24561f9d20c8ef3e78e1e383d67b0f76377cf0f5e8bdff708ece70761582e5acb6bad49a9c73d259e79c734e0a53404579621f36aeb404ff6a9d73ce790a278d8dcd348477d812f12e470961e3dc4d489e3142f837f372746e7a03103c656907254d8ad7b1793b00b5cd3382df92302cdc5e9179a5682d9e88b4893636231f53c01495cff776301bd4720a1f71ce39e79c73ce39e79c3327c7478352547027c9995970dad468b44988cf8b5482f87c8340f3314f3c9a1762cd3bda31679c31ce2c04e82f3788eb2069c29e36b089a8cc0731ff9285391f757294d3c52841ee9a346a48f4d2251863658ea071d97ab4eef2e7fa7c0be517b297e8be226fc8b5ac7b5de5868e85bc54ae8f4e095d2f32a8092f16ddb244a23b4c45243a4c49393c516915ab52a9501595c7363d239014afa2a2729882b797559648c12a2a2a532545d430c3d17afce2baac5f96e86e2f8ba048051e9a5e5e30de625e82b80bde2ec42fbc89f0533010f53fe0ad36e90721b06b120d02bb26fd05bb26c938513976556e754318a39473525aeb108ce6e594bb2eb9aa7a548e91e009488aef1b488a541daa0846dc8960649f37b3694d664efc86a41c623432a7fc37a50118702452bcc468604ea1c180d3e23b074e8b9f29b7688a130e263e05b71107136fc95a6bc66ecdee61f20b4b29cd6fce99bb4a29219623858a31e658410833766196f3b2ba3b63b7f3c472a43c65629fb76a0f692d3e45e5588e26e26062688897208430e16f07e29b96e51017888beff4d1258aca21bcc9ccdb7449c58302a27813651025036f5a06ad7c5e97778e7cf4a0b4dcc151b3c8778ecba3cb3d2e4771798bcb472e6771f98aca1b22935d7288ee925d973f55f674cee8b2dc2d266f9d03b2d93ebfdbc7a5e4f2f847a44b7f14e10d884c9c837df9bacb751d6847d731ec1fcbb372196f6981f04e1df0d7b49818d055f0a6236f2a072293413f407719ee08814020102802197e8b405601815e708f263208040a6195837c64080281361d197ed391554e96a05c97f0ced021dea0065e4e9132e5a0a7dc1db73411561a987237e09241df3e3401b19fa864ad15b5492c99a211000000005314402030140c874462c17848a809abec0114800d92b0527a4e1849410e634621460c2020020000000000002005de42e67a7c0b47e83e8158904ef708917d934feed679bfedf6735903b3963c8117774d2631510b17168b564d4df4ad60b0b58e2a03284438e274a9bfd0f733f845803db5686a613e254269a3a1bfa67152c11563356ef59f41e91be9d318ff1feeeb85ad5e9d9211ae789ac34c2893c8747e99a9852220e598b8574c05c15c6021d1c9171dd0b60595ac9b4b552945b80fb8a279d448193483355b66ce0d6200bbb55c058a93f95766e9533d58ead7347bfed320422e46628c3a8fd13575563ca010a003933db1b39b25088a1cb19a28873b62b05b9e226f138fbb9d9c74fe8e957b044ade90c0c86c7c4c9c8b167933bc93fc866dcf88efaa78acff585f57338121cf3bdc5fb21e74b302fec3e5b9abdde3be15f84f43b29d5a16ae2a0afc34327797ab6294d23614c9b02ee4a50c5968416bc54816730848e8adfe6931a8f188d9f8c4278efbc16009041c55510c49bed62e78b279d04109292f36c9a213ebec4e60dfecce0e2c02349250a592cc0812125ae1a52479de0583ebb393d4a9c87736f21ac4e97556fc4fe5653316c3dcf940ae18d09df8afb1e886fce07640d6dcfcc2f091b61f9b91a6b6b1453d742ff66658a76d2de5126a2ddd5353aa3620d29a436720ada54643a2f74c004654e2f253006eb38a64630dcaceea76dd4eeb52e58b529940662ad89fc5daf760c9f583b98959895a40fa1114a379ef86fe6e9071c814d8cbdde5767356e013476fa1c0034beb038f4c1564cfd4d045d8d060e6e72a248ca3199bbec08ace839daf49f7a8ae6986967eb79741a12bda939847ff48f56118125937bc259055a53369186aec40bad408dc0997295f8c7a3e41cafc39f126d9be972dfbc34eab7b3d8f43d80985c790bcd9edbd91e570ba1f0aff8fea6ec29774a97e8ae41c984e8d243746f8245506c5da9acf051be1fbc7956986ebeb0d8d41c87f205e2105935d7769e6266372edbc4370f54e3cc7e81c7ed580ae899092609470015ce255be911e533c5763c8daef6ec6a3420638390e6218e1fd3e9b7331032a452b7705f1048711f6c49498a4722c190d8a62523280d4daa16689cdc2350dc5a04cf244662161bbac08dcb09715ed871876517fdab7caf3fb56d08f525f51c7aa02571f6d7ef3e029462a943087e7773ac90f90ef29b1dd5aa70855abcd9770e2cc9b930a0d07c0c2c393387b8614ffde35f1507ec91c55ed32ae03ce4dfc783938283839adeaa661184cd6c7b9eb78a6117d943cca5742c6d321e1f69846ef1dd1701f88745080e913276803e4a642a24baf0125a9fcba394aa50d45e6c06702648e220f487e128da1513585401bc37266d488c378a78cc72722c4c92b78fc28b692299adb691961a4ed906d93157f4f43706043dd2be006b0974236659ddaab5503c6275be5fabd3057aea501ab22d08ddc4ed74dc63fe11208bcb9484a8eba181337c68cd664b0b44b224bd76012c7dc9deb79f8d71b4fb5cb5f2ae42b4a44d4f3a0d71c30a34432900480ce1f133c43e7651686121adfdc7eaed28d5147aef4064fda5e642c2b6b16ffb5aa02ee3fa7dfe780f7dde291bce5e0f8fd780b295b3c3fac5592b39b248167ee43a753881be63051b2b310827e53c7d85bec2bf5682bc7709c569be88887a67aff341d38e5144644bfc4c7f183f31f1eea9e93d3b894cfa91801feb9937d7165536b80632e2501abb5c79a4109b8de61d2b180bb4e06c370e501b5bd7b3dc596e4305abf2288ce0c07d13d0484f90b88655f3c4d6d233b79833489cf51c33e4e2ddd20486b47c5783ae1a04a2bc46dfca0c6c219d0101d4ca87f69b17f172d98617a0cf49c0f50e24ec312c0965efbdc79df3ae1ee40187b0a8b166bc5c6142a2d61f0f98ce3742e682aa6624e2f245d321a267dc7b8de8273a591cff540179884d17a8e44992aeaf8c8d72310b30b4c798b0fe147f7edebe20fb3f38ca76c7deefedd14907d8f75ba2678afc58148cf676002c053a735f9c53b1c845cc800dc180c0fb056cf1fa951732278ce93e8badb8701c74a5e1f8a16529238755fee92825bee560a1cbf2f4b7a9d3177f610aee4a31f507f7a841470b3c8fde62b9a7470ad5126fd313cf94c45a0a4bdca0416e19a2038ecf2f23c3ec23a95fde39951b57d6caf8222dae2829da7c7f282cbda26d297979d337eb7ffc2953dcbe82e2da0f4c800e622a46341f7129db2835cb5f128a4301cd0b0bec181b52993b1da4b2bb33629f80c763ff0a1e03f1182c6a7f901bd391ce2b02c64ed7fca01722983b30c25dfe31730c7638660916e8ac685f5309fcf042c2d3c3d4602310ba11cb12d14692cbc94d7713e0bb8b35eca655a5ef86e7571fb091d765bff45efac65eebb3833884b537ea3731de63c140977f0a61fe6cbdf1552aa5a73b6522e14dfa83ee5b8b2d21969b22051dd8441a2628b5d5cc4b6e4985aeb5541f3b8a5b8fa05e8db331d1499525278a22b30d25db2a7ea638f20cae80f1b290096b2b25177b330ccaed4e36f19ea5714ad3981dab83f5cf30ea6d9946a1d4d26c3a887a5c23878e61533099900e71c3c4f1bfffb5081e2c1b0738bdeb0ed5f826400cc551124f3990e3ed0884253b47188b8092654030819404db297edaca2d552d6e05d11b28f6dbfa79a28b270b6a76d235e821a416bb4b6b05d58b636e4f968be2425e1a38ce9caba88302899aac182a74a7862ff26909c3b71b8b3938c30edd315f9b79464e40eb80983776866c8a0336cbed440e2b34962be1c71e1269f508c7acbd259921920a40562b912f9da576ceef3b7ed4c1461b467be873795dd235d9c21af08def2f8c3b39fd77a1b65897e4140b28ee7f7dee53408bb46d5cb06aa030ac12c76e774d950c38754d7cfd4b26c4d69991890d555bb1c790148d6fa83f3aa429d1a1d03c34cad7fcd5f75add71da57853d10b4697492ccf5c6b6fa860e878ee96d3f1f00534cfe1002786fd8816c4386424da8fd651a70b352d2ec311116bfd4c2178215399e5a2e174e6965bee34d3c8b4859d26aa193b4a061509a2a02778bb6b852ae913c365abe8030ecf90689f6ea2bc7cf8874d1b5ca74b854b03b7e38896179aa5867e2da20c8b54a494d4490b5d908575ccce4257be3d2dd5b284c54225d653af56a882fa2d9cd04447f1c3deda2da54956942a6dc3faea7af25b035ce8d32057921b4704e5f87f9ab39f781ff25f7fe52b43825c7d0052913222413efe426c0d578126fc21161da388b8246d3394d2eeb502f6d1937c6eb4bbf8249e9e9631b7bef1bca825266dc46ef81eeede901cbd4dfabc20e0bba8aea274c248dd7a1f53840142d55c76ae1d0ccf9737e33c8406bfadf29d00da691740ddee948fb820d8251fce2b33f3469cf55945e3f757f535b351a6ea7fa8ca6184857d71ba73bfa96474b10ae3b2254d77e879b66cafee0dd075dd9d1e459bd47847109f2f83d5a51cc9dbc87ed1ea48502924ab5c7c41121f08ebb7ee25f769ea6b07b014f747510b823812cdea44715dd2a17491e13028527796dce608359bd4865892455ca1de24509bf532a26716ea97aa591ebae4996a4574a1aaa4c126fecdf0bb7b0b6db71527becdece14990e6fc6687c7e2f5b1ed24e459c60f0dcacc0fcafd13084d0eb32cc5e7939c60ad6cc5b9719c002cf43814f78ea60b2ca111d4f866f9b2fa5aab69841b78564ec5c55aa84587e95cb5b83068526171ed820e1b93b99a6357706d37c0e5c40f07f9d90ca4402a4ee887ccda4eaaf9c59987877c52e1638a5c9fff460afa8fb32ddfef5602340eff60ed0b7a3e96d2c0c39e6cd2f056e896baa8b5b33df92cef9c3da2d8725a73df6cda7e77ce26ec291a1b4acce49d23536bf1327db30fdd48b7e3dea71e8b7961aa6dc0ab2f034c356d0c44d972baeec159a00314afc258de84df03710c3701ffc4c5a709ecf9e35f7a6dc9f920392671133e4989992b49c8d7c0f4e4105be79f59cf9eccdb9713d625515d41181c8a65899610c19fecfd945264ca00576f7b0a7acef64fb0a1e8a9592c7e47d6fb85fe851d3b829fe0462461674ad22e8247cac982cbbf882c1cc071741a1e46e51d26c76191b5f5c18f217f2b8e110f161cae9e286ec25a0b031e0f8aaa5f19d75096ea4e7da518397ad5e483b3c32a3192baf161953092c479ffed577e42c7933924d562865251dc6a236cf2abdb75b8bb05653b4ee5aea2aa5d2fb79b28ff5b9ff634f33f7006de41655fbcd9a9e82c600056dc4f54e9e7cb65cec5bd88cab713dfb26dd13a569a28d187ce27cd3f656e01dd3681b16447978ee923ee5e981b53e7d2cd53b478954e0050814970101848435ff1a0600c6da20b634b3e72b89fcb233464382331b936a9baba463ffd4551da914423a8dbd138426b46bfda17dbdfc01c94973a1cc52da48bb335b8d706a6563a449d911c4ae1b51475f35ee6a00b4c1285ec7331158ae4698fd261937156a5ec587d49dbbc971bd0e756125a2241a7278c2a5066ac40429f751ef30e9da2421988af8254ed07d68c50ec421e62560cbcd9b03c6b3de41d715789d584a51c907f24211629b9fc88acc83b4c809c267444527f684483dd591503556485b8a4bcfe720ceb2e5d67cc628bc6c75cb13e22e968cee092f94ab464a9d57633327586f22e5c0ea38cd22d5cf421bc7e2e92b3fd0b566fe45d938998d4d5e8f0f5beddffb6c141b291eb74c82b4e381a1e18f70f1c9de130d3248d6aeacf05f02d595097b6880ace9c97209fb007e2ec93c0234a8791c24682e59c96aa49c46b0185544d9019fd0f5cb5654cf1bd529cec69c873c6a4823221928717ed37372a0378fd4fb2b71ed958e735577670940f9246e58588e8ca9460cba81d403c6204373c3449e399571a60e863eeb455b780dae6e94a6093e3bdf807470cafa175cbca2366d14b7c7328389bd8d2226b14aa3b0b4b95411da9dd535275436c2c1bad927995a1f269f5df5fc79c8ff717d4b2a8c1fe2dc8615a9e1b268332283f4d029cd9214211f7aefda3ee90e678d069d16b0c51f6f07e349a3ea9199bb7bbae153970086539fd9a0fb1ef3803fe21f940a1c2aff3b61d2a95d66ffd9a5bc20e252dfe917d0dddaa1de57ec86952bd713483f58e295f28cd6dc2df73b1676a08e34e19800b7b2334a067b0bf47e5520320104fe8454b203489656e920b83765697a49a5c6a9b76121c6d83275e2b8270a7871ab3507806a8f8de0761643ffd4ff40c46a8af2c0d51f85196d0ccd99a05a3615b9a5f0e4fe2e4286e9422f771ddb16161f60e51e29181cd524f6bbdcf1456092674a63c477dc0e2777e91fb0d16c1d5d0fd7e88c5c6b2fd7e2606838f0da176c3fb51eb5a7f21bf8e0bbf864b04bf23422530e9d6fde10e4abe03b67afe23def6153cfc84502444b07adf7f531a6c1198d94d2b5513996bceabac3ef6f1fa91b565ea6c70d2882d451d72034e9803f22045485cdddd65c34b0cabe550c1b40b8cc7195facb7611ae7721d68c9111fe719574ee7fe46b09496e1bdd819706757fb9633cfedb7971f02104613a0154f833ad0f97542f3da229ea28074260dab314ecd86c3dbf8f9d3e4ebe5c09a30a143de94a2fbb8834f32295507a2ceb6071e1b0cd3c5b6f260594dc1ad557b4914aad6a3b76bdf57b5307b5f911d444774b2b19a0c1781c9622ea50ba72e4a638944278238e213257197ccccfa11f63088657ecff097ace820c3a5703118e33cdf3516ee2c4d3034d8ae63eb8386b456076b22faa69ea8d687d602228d5539e9096299d180024beeb0c112bb35d3d3c37235fcf917dd49613c2f8d80a346a2236433d39448feea93c21cb630b931395ee43feecdc770d60924e56cbf65f484ae5e499c8f1b1902ace53ce997d02bf00c3195e58e88764de578deba36e20a4878587f4cadc9683187171e7de60ff7d76988716d57661c50f942a46be9439eb2867d134ca331c2d876a16a80bedc1d6c9d608bda9ed1974207d3cb10a12dab5a54726d8bf27f9dfc5a83e27f46095cd600da738c3d455316ba6d9b78558ec1f1f85ec9efdd0fd0ea8a2eda05d52bf1298bd5d40c290dd16ead41bd29c818057a242e7817b41940bf6ffc08e2a015b670916a7ad91110b1fc8307357dec6a435def3f40dbe068a2f2153ce856e9d124ced224eb73c76de1fb672a4fa4975b19c19386c2572836be38155ca6bab6ac663293d48d804a351c51a1916c1009159ab0f3dd7c4ab6b37665887a2ba404065441d83364b7ea5ee80459e1dce4f63ddd546457a2a88131cd753a4a06a1033f9bd8eeee8b5e137141a9bbaafac28e493a8ea94f7566eb69ec8ab60c530ee06ebaa2a0c81439613894401a7b693ab1b433fd86639641c40122f925f6eb97d441191bd8759c3c1c5c0df51d26c9b34e9bc009929d5c0df3cf40f4fac0758124a319c34a77baf49fc88b196ff370a5f41e3bfc2b7cdd1e3863c2da8bed7804fb1a15e8583794ef0274ee4973e23d3381422db588afc1dfb6443aa156ea4824ea64d84fe4f4211bcebe6564754933ab18c5b7638941ebe2b9ad61696432f3721aa9b86c78a652702cb6de8fd6b451d6df670ae1328370756b8dae16e7e529625519ffd6c35ef12b5dd22c2bc7b90fd8223c7ecfb47019409064a77f911a70de1d965fa26f8a5aac21fd1effc2ba0597f8df9211f9c2a0a02b03e8700870b1862d1a81a0480d958cad5709296df4c1591b9f6a3468a1eafeba6b7c54ca8952158f4435e7eedb87a7aa15c4dafd60da575fe8b125441d441bacaafbe4a0fbb6aafb6ca8c0a16c2f64e4709fd748adbdc4f17305a29b6eb09e9dc534a4e6129b13d96be7882e760500b17914d7f9d55c776590666121e4d63b13516438453a34b66fba6fe81ecceb17210eef50cbcd78da0b1960b385e007456b8c068adcbcac266eb03eb6534795d38cb34b818c731498005a18c28265416c24943da9d727dba17f3bf50e69262e3a33a1196bd450d563cfe9de26f0c4bf96afec2ef81012b7a007017656e99e0bcb90992abe5a92f0141a4a38f4815ad94b25254ab8a9b52f7d9b29d093e43d6ec0122cba88e6ddf24f2d359f44391ebde6273c6d1093aa777c64601a8a43f7bb611ea02a5a2972993ba6c66fb2660b44cb6842d4b35ac87b880fd9dfd9dfc4d101f735dd9a43664d9b5f97433cc302e48cec7d0a7cd70dfc4c5d7808b587913e706fcb0f81f38fa957a61a6294720975a731f9c95ad3a718d403299e2e946432abe2a3513c259988a132193fa2428ba87126581d5b5307b363ba2888f91cc9c67940e9ee13a819d6ec344a0b5ae49e661bb9a02201018616ff4cb1a7211c758555f117de1a9766d8299866106b364aa27836047d0e4f294c7b9671c30e422f0f9cc0e76140b91d444a82566ca278b289a66160f0f8492445d8a0e4ce43ff1db30abc17dbbf239fd556f02192ca3d0d7fd66cf7a4690e68b550f10efae46826507b27e1a4b8eb88dc61737aa6784f00acb2df3fd951c94f7fac2d788ff8083446f15b404f4cdc78a483d641a217ea9e0623809a6b8bfe4cf863cb1200a356b14039e9556fd299d983af0583581a5654162423ecc266617d541f1768190ed118019105707dfeed67a55f6fee88b35c7484308a1c4f62eb4d1fcb95751bdf8772fdfec54227451b03d53bc4b69af55e00b047dfdaee4af45bdc1396f6d85326bf3d4cc4ca451d5c0809ec7199cac9ce6f228d0592898a2cc06110c705b551aeaaf027c6bf7e02ce940389e4a32093037dad065919c671c22987ca77d419dd500868a7de57a59c2fbe9273aaf69d28a7f5ef508a679cdfe4e2464f3607ee851f50cf04da6612dfcd8f6d9bba2f64e8a8f8b4c9283bb9d6adc16ce6195d6efea6ddfd6168368069524894cda656241891fe136cbf9f11d8adef2f1332408377337abc0746038ed66bfe24e6b27a35cac9d136adeb8cd0b51d9a356efbf6a9caf9b6967f393fe6b4a9435026dbb5feb4618ace665c087efe240ddeff12cea9d0b04f20fe616b507fe27cfbc7c08b6ce4ac3cd5b8bc884f986e4e0b2386107f5124f98c4548203f1d050a3df3b93caf594249a7d1feb36001387ed3edc8219ea17e658eb317f6953bffbf79756485f1164e474f52d95337e6f8f267c36dd9f38718bdea12d2a9ac782a7ba2f0c620ba53a3516bea6de5d897079b0bc4c1e1e1070f3dfaab42589f6e807c87879fbb915ecc66e76489c0b06c188aad37569e460a300343d1ebbee8cd3fcff5fb893c64121b90c8eb70ed2c7dbee57bcd65a6395068ede769af20b908a2797f38ca0f13ebbeab3a82fda0b8f4657115bfa4abc3197ec0c1019e860685f139ab8cdc5ef6072c7c3b232a4f8778f815ad26fe573a455dad8e89c4220f0c8dbd2ed89d38e9d87565c60b793631761253b3bfc6ae95140723b0b7930a6313faa19322121c615c49bad6240fab0611bade61279f011bb3709576097713fa62a027c360980017da5d1d251d8b428138af49c27f217a03878601bbf32cee682a8e682ddf502c05e2d4502a0d4bf3691637328988250e73a960a86c74470d582dc5bb9e6f9525c84683723e2a446e1590b862fe7cd648b4cd6707b39ddd234cad5392baa9350912310bbfe88e918b95b81defb4830ed22d84b54ff8ec842f8ff8fe045ff7f874866f97f8f45d0396891b10af02c813e29911c1a87897074ce05042eb070011cefacaf2bdb24003d6bf9343e39cda99062c4a44d3ad871aa61f2784de80154aed69b8cd662e7922fb5a23bb90d7efe8786d64b15963fe0e3382f6ab77ef8f0f3df9cbd60bd8fd46ea45f376a878067626cb0b5855ad7aae46fa418fc89251efa3318998114bca682047d8983fcb7daecc2d67c8974aacfe0236e7eac838c43da257cfb9552174fbd68014bce41606c717a74a42fe56dbe276d528604457244fb862e652cbb0c531695fc062a611fe89d155929d03c9029c8abc99d4e402f606380175d38870f5d58549b575d814aae3292e3c978fbba8b074fb6dd216ac7205485d738e1cc8dc43ac367e8cd95a8f2467921a0b65a950e4806011b619b9d6f9b659ea8e649fd6740ed85da69997636ba878bc12099c2c352e93457997436616574bc3d88e990663025a112078a4aa91a540c0912c964046ae5f9b941ed7930f547443966643b755599ee79772c3d9dea6307e0fb7c843e0b614756f6ad6ea06d297724fb991ffa8ed708c62fb1251afa37cc4c565312e3702d288b984f62548ab5ace2fce0ff939b43ddfd0050232b7fde6c041c622add22128b11d70822907618c333c42d56a896c0c842dd48901991bb3f2086cd6856733b6d8a716856d39b4e5a1cfd8de0121968247f4e4dca6bdeceb13eb76be676ecb52ccf24b5fdb36d187122e100bf157b4ac236668261f0673407090d97274390bef40c77d0384ece80728cf277ead0d05690fb552d11ec8f5590b3fbbc21f8e60467fdcd11b84665838b22c472468875747786504eb23b668640b47ba7ec7e5280abf78384be3c2ae1fac52f9e752fe136bf20b6e3514be7c457b1034033677260aaf7f2684e03f2d65cc59e73e5d164c9523ab87979ed6a22d471cce468fe002ff507a7c024c479fca6c2deea0ca315030b8c6cafb1e284e06452a0ee79071aca7479ce3eac739a3d51af798264e6f02f2ff27961c12786af326da10710a08bed1334fb2a67a065cb450a320dcb5f5d7ff36f92557b2ea56f43f697c2de9b56d92251f0fb5bec502ade268fe9968a50055c7217509720e2fc54c34894ddb06833bce0994bbfb9e96b70e7875e1e299a7694b69611a8c63758b1dfa53aaab25ac6ea5d2bbc2e3c3bbaeee92a4e89eb39a71baeed0611038b9335975b9f21ac8ce1c719ce230f7e44f18f1225a1b29d28f0a24e8562f469ac519cbc003862a728c46fcf34316bc88b00eaa5b0859d60e510b72de0ebaac2e44f7933f11dde963843911a011bbf14f9d2b875d7b64f8edc311b7c7896c23afab85c6941674f52c980a20f12ff869fd976a8e2cfe5a4cd2a0493c20f1d6d4133cc58c05bdb938dff394203e530a153720574d3006847413af662e7f98075b49a1f3b79b33babc73fac7bd7279c3c41cfe2cda9c9f114731dcd164bfac121b5619f64231982f4c82af2b5a97ef5f6cbe644a9242d6e5ced730ad1d156c3fd0fed4a42c6862be86e7d2d598b832620dd76b7f3d4e7431efd3c20f6847213aa951e881c205865533186ed5cb0e4490db996876b622fd149f8f6cdc58e2558b3c77ef5973be5a112dd860de4c4be599933fe205090f0993fc3a2e842dacc66e15a78dd1132fec65c874102ff95bf1dbb9b119027923d0fb1de851878267ae6f4ef867129ba8a0f5c884f1da63202736b3950470510a9a4d8c01a041ab70a693d353600c82ef0af9c0b812e6b67635c6461c80139d6be0beb2b926a8aafec129ca9ef6efcfd61937ef9eb926a178e1649d250d41f9b174b2a9c921d030d3beac02681d68e6cacd797d2c0bdda28de12b973f37fe1466b621a6e1e2c4bddeaa2571bf0fd5b0460ee4f23e56debf645e9525a434f0eba4f15a6210ddd1f3f8ba8e29ac300877d3cd31134a57291332ef5ab9a4b7907a03c726d9a75c6ccb8ddd5fc5923123f36b48bf80d2562e5a4a94f34bbd8e6c431d82de5e82bb652db6febc641b8df83c9662a4a898298dc8ae71bb4d47f0cca78adce95fa4a0f32de5be40f8ab15c0a6dc4939dfdd6a011b8dc761c338dbaf38b16c4fbace37c55b7202eed932fac6f70a911899e3310f3bbeed832ccd58bfa9407393d01373045a16ff0a443c31cedfa8486e22704cef0870c97cc59d6a5ee26497e490cd4ef51edd53b1c9d1925b76f571c7179c3a7e6c0bea65b7ba48b7ac9d54c95cae7a57524d89e22df352a6c72519acda9b74b4b4dea30baa842d1ef22503d148906632fb043a9c3a62b3231d42ebf651b15b7fb3c1586f9923bd7a4174ae1e314489cea754b93d29f9a630369af412a674d0fc4ceb41ecfa7e32257c66b43bb03c6be27d257eb77fb55cd8b184854a7dc034dc4f5ca307f024d7fedbdf9b809c3578f389e809b3a36c710fe33c941cf9a73af18bdcec44348bed9d8e09d5d711bd81d769575c0cb9ba57a8b1649c2c84d332f21d5b329f04a88c09808625ea3cb7fd42a81d3fa1b575c1adbdc953b0c1d73d1112fb46669e60ee2ca1411c10e0ab2ccb120a978ad2e32568f013f98b99eff46586666ac72ac34c25ed26f7377bcfbdbe70cfa384cd94545a91858b750808acd54f66223269a6e7f5e4019c08e23e5b198f8a9a6e6f8ea86767e2d197c2ea2404b19c1a4bb2f7a5553ffe5d8005be8fdf7a563c93df2c5c2fa459e01c7f147b31f356a754f7a76d6c3dc579efc6c12876a6a27619ff411a85cc606621b9e5beaea1fcc5a326a2fff340f21dd0328d6f351dd068480855b5a74435abb9e170a018a766d58437863708cd3252d15db81a0a05cd185d39ad2ba8f4944cbf5bfba9233fbf210f502e0099db72e25c1e30ba6665c804acbb05f80fd957eb1c2dddaac2ea608431a6c954426028e9f95c5fdce2183516114595b40cc91d7beff665340d6867286067284de1cc17e74bf450f296ce836870762777533479e092e34d74587acb70f5c343b0c52af71c8f18f1e23cca3d8a43db53137904517dc9a9b906abb657b30cad8a156bab71edf1c80aadfeb93e120485e112ace2b0cffdfc1a1fda89a5e278f07fc9fc629f6e28acbcbcf9ce11edd437a96a040babd28565f58d0b4a4910dc0a5fa4d957df409ffdb49ae7028723a041c24519e3449f07d1878e6e37fab2eae3a1b71f1510a76e4fd55c74aba300691aece74eb5bd946cb60b79419bc7644da2bbdc0de4b1d13ff668dc3b62df2e7bf05163da9dc354019f395c180d26ec2828cab14d2fda07d671e0a9776b581b920bb2b74e76dabb6e684ca1480758571def4eb2b073d308d4d2cb6309d4f2caac5c32e2782a8009ce5f84d8b5e6b6bed2a43e979091488a17de411fef8c3c10e26bd32628a63428ad65aa1d77a810cf7a616112ee5bd8f7862648211177df321c5c9c97f23007e79a78ee09e93256c82758839d9c12f5b8ad837bf3255a04371b1cd2ac81b9f6270d4752ff0b9a43529d1a9a01179376b34c3b5ab143c64d853e1dab076950b1b6d077d10efb35272e48136193454a789d168e9b32b9ff6244e4c328e4a0d137aa13166caaf0219274664d202d39327ac7e559c1f1beafb9d9c14c51282515eae02f04fd3f4a5d73f8f1818f360ce84a1d7f7b273c817e0ae4119d5ebfdb91f43aa9b989517331ec2f9c6629714e1472fc1e7532df0110ab47c016924e29a792f47bf825491ab065c59c8459fbc6ca6c25a595fde7a7bd951d460fc23832e99dfe6b6182b81a584a2ea9f181015b3a0b5395e1a258b7a971ff31b94a529e2111156644192ab4dd25f912989dc9058a3d823debd4cf2a9a1ae427cdb34385379b81c08b2399f097ad6700a2d0ef86409673155df71bce3ff3df9f4425550a475b4cc469ca26189e7d7c34d11c470f1d5329cfb5aae7126b957b4c5362a253827252a81326074a44c75364ef3c1ca74a9e63d62b1cbd17ff2e41c7c6fe5bd7d260875832e340310b701806139b3f40af130eec79619816c916050f963eb15bd6ffe64ce019e54867ac4bdda23390bb580e4c16e669c7b2767f62466dcda23350c539961e47b1c38a77f28ca0186c83be148f0f6900916c1755b87081863c3a3b1790c08d41ca4fece18553bd59897c03a0d34db9251821fb6fee5bee2a0ee3d72979fa5a609d6f761a10b28e303d5760b275a13eec109dee3d1645df1fe88df484a8523e1666a4d10c7facb8d263e464968b233be5314139e09821d43ffa5d073ffc9fbc029a7ffd6f1e30fe0a9c42c273ba4c4e58c58a2f1b8e35c2e24ffb7580a02a3d8ae88d03779d2558bfb6fc0a3fcdeb8ae392d6f2867746eb38179f56b522e23115837d829a6ef133207b41a5840a1d66890c816c726bd520dce61dd5bc21620919050efaa1da03bb026e1aa78c91e76f123b1e7b93cff9c3d3cf395d42d147d07e81d67b9c67b251e0b35e84365f184cb5bcbcd308edbb566caf0ca46ad02070bafdd9e4ce507e7bd707a7935ccd91a066adc9277d4c8637f81886e6ca83d49f00e06d7144102f1bcdb81277d538fd12b6b49ef31156a5f46c0696658b5fcb56ca7e7652a4a1e86780d51d50118e869265bc939a2b78c953c87b2e9768ca2a34a0e67e089df86daac80b1ae05e5c3c692e812810262d5f2613c7a9b74756229b24e2f62c1aebd28ad63e2cc74ec997de76f9da8c4c59879a017d296c7bd395a9b528bcda66375d3fc0f781217fcd04b22ad90b7b2393b8bcf830999898c4524912e8c0081184d2331beef277ead69b88bb8e2927dcc372fc1891728ca3fc94acf1e3e9aa5b8e68540a17777703828d51ea11cdedc6866142225450a2de6316fa1b49ea0b303270466ae83bba491028b53c596c9f8e2d06baed8afc3e953f32274e5e68fba01c007b8a4fbffdd404d6a5995319d3e74c2ff98fe9853f107be177d53242c6df2ddd6c4df5eed255c019a0e16d6162d64cbc96048b59ed071295c5e25d033538025329081ec2e3c05e949f9325d8fcfa5550b09259996422b1a53f01e23e4a7c2d515104245459b759fcf28112222f1c0ba6d472d54208f22d2ea8f2e8b051b631fc44b8817a3b26031d1866cae2a04cb919cc235e0dbc5c26808a8462b583bce366d007ffdf8520495f16e9c42620e4b30e108aa4a8de7aebc2e84a84b9f1c89dc19002acb0708b1d4736b2b71421eeb72f8310a21a0d679b4b07fb385a4f8d1a7338f3d1d4e8dc0a0e0b8ef14e463ccb26dbd70542f78d1a059378717211ecfbf341eaaa8e421185c38912aac6df5c175ceea5c34b1421220d9426da0c094383bab999d25db2190593954b65043672c1bce83edce4e2ce90994b2610ddc101e17c6d8ac1ab00b68e578829c4c0f29cd66f393d004b1ddb1c04a9500370186cf16cbd738582701b87e54ea655ce8d93a8199709e6e28d804dc549a25525dbb2f557e6ee173c58deb73b3be9680fb440ab3a27e8e9b8b94b203494072b6b11de5475cf3299e516869804eb2d71f3082bcc790fbfdf3261b778a3e89830b4f8e7bef452f0fbdff303cba291fb929617c83f4bd895e45a637a1973e6777932b3f5184f42938d26635c57383ada8e95da42d90953a2aeb272d51c75bc01560498414a36e69a9dac3e6166d0758fe1a0167ed44a0a5d8862c4823351105d0ed16750fd79af9ea120b9aff60a187bc679d7976f196cb9c31c94795c8a104d72ab5644d43429ab6eae573a300e6864614dade20b1e0a0cb3ef7c62ae7480ed77936de8b8835b760531c5e10f93469cf350ff99d5df7d8224e17e46c533ebcc50008a19d5e2788f9350da92769f9df3972e86fe2513bf691e9f3c93a34c4ce133124b0816c74b3ed4d467a6cf6282f992e096c85f69415fd56ebc87c4afddff87f8a2b5ced6930eecf98332f559ec1a488609271f22bad2492eff66c76914c976900f6a98741a76c506ece28a8ad9d604cb69e5b9d6db3c215887e5642fa379da3a4b22a8aef3d0e024c61109bb8e26203352d597ca95aeae56b8c32a423185234f31ea78811b1c0293dfe4193278fc1816523a0ecc2ab1967cc7aedf4e4932ed73089f49ee008145679c94549f6014b98630ef3384a8acaf5ddf8b5524c4ff0b7868263078d74d6661224cc1cbe2fd934b274180be48cc3502e5a19c48fb478aa2bcc71d93e6a71d3cead8f62a988d24a4512a2283d46ec0d5821bc6851f4a76218055d64e9b709f96fa099d2ee6a542ef7b3635ee8664101b2db9e7646af72c20a4e950ed39590f835be838ef07e204409041a48b46f093d0f375681dc9199856d2cca49096ebfed51f01012ce1ab4c8c81d40a0269ec607016ffa7735b7faa0bd3d7c1e13af5383f81485e8ab60f1b77bf23534930ddd110183d31121f0ae35aecd7c8aa74decf3ac5c37942c4d1fdd3edfd44e34b6393b6eea151637641405543a0e3277fbe73a69230db7cf58c9e2ae8b7b260bc7982d851eec9e85bbc70b662ca51a5d3266461e64d341523868564d1ce540a6c562d4f68923b51a9f883c7fbc5a5db5bec869fedacbac904a6b5b6d00d86488c396c2d9a0d671afb240edbd018e2cf9c927022a2a128fb558fb1799025a0a63aa7364b3acd5703d830b466c1a9f38565216f7c08735e5140f4eb8aae59d2f8dc82815845889ee5b2426e3d6589caeba356539727ed93de37ce627b40b4fcdaf0fc3b443029a4d1d7ee515422473530cebf3aa8f13f1ce135d189b072654112b32cd847165c9823e6cb2d3f2d1b7a3d4daea4c8dc9f25c1368e6a5eea67940f1cf19c8d3d8c10b5460be153cb36934dcb3381f71cbef4db7e150c58fbfd82283a7bdd41cc2b01708b3aae2004fe105017edeed6608f44c158c1bc8d74f506108c1e8a3875600506516558173d76ecdb1a49a758c0dec6737ece212670252f1a9fa7af918a2cd13a56bedfd7e1fd09797000d7b18a05e809a4483c84d1b12d7948b289831c1cca199682cd31c2ab146623fb7260a3d0010fbe3b6a16cea51f6f944045cd623f88a2d31e5f456574ccdc10b1f93d3c06ed4be9bc5fdbace48057f4d52d2c5e29e2b4456a18555988c1835d4536cdadadcfe2b3759caa98ba3f21a5ea3c360a9f9ef5d14cde1b941405ed1c9a4744058bec22a54290ed8f4a0597a8ba1ee33c6d298b5ec0dbc97d6010d8b7abb8ef9fc5c840974924347dcf122146c6de92b89bd8972633098088f44ba09f8fa3766ddb3aad651850bda3bb43849580a3abcb1b3ad08231e3ddb0a326d26735027e9cee7e7a494805a3f0d910a51d6d4c0c4ac4abbbce700035c9108ececd7d50d5e68029e4d341452fbcc6fd5b508157d495a85995eeae4a9d855530f49fb240fd2a5c6a2d20ab0f392fa5045aa0a3ada59e102295456c2133a1e965a6f42ec1b6b6113187beb8141560685e32a4a14721521aa0bdbc41388f472133752a0fc3eed64efa45d0f826ced5051811222d8aba2cbe2ece1310803eec9c0113e8082e2b6745770f625f250bd32cf78f0dbb2e361792786cde3387975e2834c9ffe6858464c73e4863f720a4df662c64e942898b4b7d2dc5bd0c725bc1872d2909541ef068b5be72bfd31d1686db9e72579fc51d9c11136e433aefcfa8c171d68379473cb11273ef2c9a54ed91cdc9e18670e0674c65200a8fd23f1701d5255318bd35b986e5eb341e8f054688b0af3b995682acdec5c2d6e1aad440013f0522f0a536f35176cd4ce4380f8727f1d4e53c2ada5bcbcf19584221cdcac776fd37203f8ebf27e889fe4d00202c67790f0837f1b04e9e25dbfd759f3ac0fd6dd53f876e31bf98507168f2d80463bd8dcaf112468ee3e940def6914353654a508cf5c2ed726e13ad43a5c0ea758529bdc485f688179fb3ac3c7843d3a390e7f634f7387f70c66911356a1a139ef466bd3ad5ea0d671fa79c44929b67981b10fa8221c2fd6f3ddcf60bc150812816db89f216c0f90ddd19f514634e4363d99e367409077dc9113414eadeb25727a5ce54e15eaef1ab7dd7acceb5931f38c8880d13fc167ede486121f1f72c6f8a800906dc62b0f90687fb50de121b5ade2c565e7e7641062273ee8deba999d68ada7c6fca580419660fb8ff5351f8ca3663f44cc5c4012dbea565ce8bef3c01598eeba0ff80342a9d5f767bf827cb55de2f131ecd74d39a6ef8680125aa97d2aa554140bddfd03c4dacdccc32a7f5dd38c98b29b2200c79e59b9b2705ab56023f853a0c47a856c53311bb9606688e81edbadd0ab96db91201634d6335f796d4b3b794c9f9e7b48522e27dac02cb2b62f8119dd42410b8d075e879f9309927d9ac4d18e273047ae82175586b48607af41aadeab3391b6f41ddca2b05073a622c324be4d1755a8de68849dbec322e1643d5cf71092a974d312c18ffaefea150d31a12f23921bde12325998999cc8703a58147fb33d4dc58921277ddfc509a359feb770a431aa3406629a22cbf10bdc2ff049f06a505a73c884306cb0c793147e2be386060235c001b4cdd3e30355170802857643494be1ef061f2193fa9db8b02f58e414003aa724d5f37f469ad8f8e04ba63c6008e0605cd464309a00e920052ed9d6f496dc40223d30551a99350d29a43f3571bbdb817b73374fe78f3b7a4ced8bc2413ee42d5497125f4e5803e71c59e582a31628c11c2bdd0673364e2fa6f7d566a997a217a4dceaba9c8bbe3e6aa2ad6d5633534c0b0a404ebfe808a4a95bf861c121088483cd95fd08026bb8490b94463da05e517123f3bc094620e465ce25d60e6c7d2f1a537c5f8a618ea08aa687ba1ce7ba47334505019135922b5e264ce6d6a3fbdd91d6913bcdf4a18d3ab06d6e1f55e9379e2fa0c41c384b8a847fdec5a94ce0c58fca4a9c136c891ee1a642371c096ddfd882420fa0284fcc7b6b220f7c60ad6f4f5dee1b13c765a491419709f22aa0f7ab1a4421402979b4d82f4f6624c2c41f102434cce044220e368ac95a7712e1aadab0c2f170a61e6aca860fa144fc0a72e61f3f90ba703b5697e3d84f90d5801207b691427571427fa9928a7f211a889de3b5242f7146a5495f1ff4fe4f178eae75523299a466fac6b54f0715da99466d8fee950b5e19ffe819fe25131f2059f6d459ad9f4dfe3f478f7fe2c05a9504cdeb212de9793b45cb3d28dcb1e95269666029c86763ca5cc6f51ab9e35fb805f2a56d8f849662c2d027419a57cd79708d4c4a918934165f0bd07d450c39a24c95436640d589aa4f2c604036f4418ca104fb8b5b4d5a0d43217c6adf106e0ce8261dd3d663e3efde98abc93808f9fde7a8bbb1c257649cfa27bee6ae45b386ad45d44fdd5757ae29b600d2d42ddf3d269b747565fc7aadb5b7341e73d8a847af88f783849de7b5f0e846d15dd7a1b2d434abfd43c2a7b9d876f68a95238386d3eee077c4ff774094f0f2b6fed3613461615afe853a5cabe3f5432b0e07b9aac361e3ddae7c9a702e4d62cefe2a8418678aa2a2a86b0fa0adb8925b16b0686385538163cad22beef50dc562a2423a08a91f3805e12eb30ae42eb7900be85500aba0abd8834765e60778cf7d4750aa914d9094c647b8fb29790568a397233a45e77d7525b93b3053069ea29225e5caef88ef36506184646b4485bdcd2650167b5687d7e6600ec2154f5f3ef7c91e7a8f5352167fbef48e17c23947a884111ac9b9441553762341af6626632248c756370898e867020ab6b011cacb55ba6f4e06db9f015709dd42783eb748767dacd708f4ed0d8eec8473266b73ef2a38ecca2b43f922debd0f388237081009ac945cc4e1c2ec5dc5ce9f2679d4e5a4afdd0cb640fcf537b867db36dce1c1a7e40c0697a7da05c3fd2147c269a517e1efeb3b62216c2581875aafae7ec57652c251b5846507e1aff69c3ddcff58040cd62a09014d9ed0b9dc9831a2905f34b12fd19f1b4d890cf48672b21628f3537e0a4da8f9aff532c3a6dc76030dcb902070c361e0e17855028a5263c7a3edc749404213c888325e60e0199445649552416a74013c70beb6cf1c8d060bf84efbd0321fbdca1702980df7496923b49f8bb21866ee8a93462380ab11239c2e6ce5b0b46c2f0855fb5a8502f6f62b6b160814bc9ff8f749a933ce69d84ef1ea4411d3bee3ce26093853844dde374b260cc86bc6e5c7f2f5818a696c5bd167b012cfec26e7be76c425136eb67d59b28431537f04dc4e81da5dbb42618b8549ed06ec5479d49c03fdc0427657e3f7f6c54d1da5ee40bbaf7a463301ad8f8094d7c9a1bcb385b46f86ebc28455533dd75786a455fd6f09028bdb21df3fc102d2132b204a2b4211295ca3d7500870ccf05981ccbc9aaf31dd62bbb8f4e788b4aaf9c45794da568dc5a5d9779fbced6b05fd8756c6149a1267907b08346e138dc70c9320f96cf9c69a57e849f05ec6e92529f3a93daf7072ec95c9c961c493c07d7cf258650f7ed8750b44da3bb2893f79cd638755e1284ab25c17de4de55d75f868358793f6183016001d0b7a997926d2f3056a97fb003ec44977a1fe9cbeff3a58c2a1f409316a2e6f8f31ba9319454832608282d29aa16e5b95f920e93193f9a11fac5cdfd85bf2cde58e35c43dfb635bd14fa806906d31f784b0dcc78bd3c5845b859c079ee8fd2b26f0cd335af6b862f1da90443c2f688f7390bd1153fffdc9b1ac93eede0b93fd0130fcff725c124c7a1fa58a5eba9623d2794afed51ab85f1be7dacb608c75eb29467d929faafb118604c78579384878fb9b26b0b5c7d262cc1cb378406067c06fd96d7006b8fa41f3fe52ba8a2911d160ed0ecc42b11e4d72cbdd101798c266b6087f6765430f31286a46fd645efecf98443b23e963c78ca09b79ca6e8528cc80d1aaa4c424820fd3cb5270ff4a64bed59dfa2c005ce7f66d9a232c2b8881e4d2d62e50f5b9f6303412117cd8da98ac763d8ad5db96c357c3b501a611f1e19b0ce30faeef86f7bd80005c43fb82a65b522ce576fc38e534dcb1dd9f6211c300e982e015bdd8863c233125b51b950614532318694df49ef8c8ed85d73370b30497f9e4f5b4e9a7a89224e213c2e31f8eef047332ec6a60ead87fb4222c9312ee3b93a4d064d45b77376f770d26aa5f2145ddfad953a4cd8e185c287028824c79a9dbeff8f4b82c4e24ce52edb0c9395dab8a42fd385d7f78fd8a167c20cc1f856045147658707e7c0699b9452726770ba076a1b203b64056abf83fcfc572713bf2c190af23b661959ed2cc2ab6b8fcc9229e5a99b354a5f72053f1f98025983a3ed8a2d526f8c0afc3008291845b557964f0e0d358c7517471581cb0e7b2882691274ea38e19bd0a8f34d1d719b3719ed86ec3b6d04a285aec0ebd420813adca866b3e7514534d363c2238a95a3b90228e10a20453a8ff6efa0ad9f2a7d6b93b0ac0a351f751ac51d8eb6b5c5febb85d7cfe3ae0051ea69712e3c6523408cf88e4479a2a73e91e1f4ccacb0053c75217e4b69d3e8f164203880c5a1942caa600925465fdc0d764c04011b32283176c45044c5edafa74a0ab0f3c794459244dd7ddabc9fcae38b59870875b66c3bc2afcf6e6332380d35ab4c2048f83565b06c78dd4172cdfa0e592066f2c7a46f3f2d98e14ebf6d4361602b56d11d3c9190f8a213d03b357b9d5afa1811975a424041b85b6bd2d216de8659570408b9739d1968d8613b0095afa13b99061ee2a753f6e9c39a407accb29a6a37dc0af5210ff719da7d0b63af69c68669beb2f1c989c37cc3c9e3e274136504e2ccb10deab7f4151a349856aa32353906cb40a062b50a70596f6a0e1df05e554eb6d4ea5d4f2d7b029ac945364abde6d7899bb5bf6bb43e24abb643c8a6b57846dbf76571026518c7268863893e905513db713fb11e7e732ac4116aea193189924b6dd401d8e6e318f795415d6ae78828eff0dee937861d54cb747315c917eea8a623fea14d969dc8cb600826c350495eb945b346555cb71b671ac25b90f0dd840276c6ee3fe793883799414eeff36bbef04128b1af07582209f996ef6006f03fbcec32b15ed8f81a97ef5ef75d8d8528bae00414ffd8cfda3f632d63ec4ea76ccb4afdb4164d12301affb1b7b369f3adda11d7f951408210434885c4659f20fc50690dadf619424cb1274f5abc6b1f374c6b54e0d06492645aab59704b6ab97535bd282a320622ebbc3c1df2d9c185ed9e0814450a53bddb1101751b5e50e0620d6da3db3578fe63e35623df745f5c392cdf2cc51a75f26841632d8d3d663e48b0054fdd282503b2154728fa7ab29a2dd6072ca981a9bcd57643f64e89f78a61c08b1b798f18dce966ceb302b03ae05f00aa95256b68bfd8b6995e5758b85335c68a9dc4be945979c4c53a5c8ec86547b10a3bf632e2583b0fff165c139e2e84a028a2b4b6df6bd70ba7503c861cc616cacc784220008e51810691f4e869d77f083681a0165021b53c009743eef8266817caa4db06c070714030304073304176575b15f9ea6b3a093322217b35b4af28e8fd4082f884b94eb53273e17904db33a51f5def794023218962128ee226c0c4bc731caeb6218f7f65e032c755190641d0da6cce375aade6e663ca176a6ef20fd762d66b09468af0fa36a22127446f1922e21388d4367d861ea2be2e5055f37a34638571630202e1f2652a40f1b54cc9943a1a18fb2971cfbbe5a72013e916044bcb751abf4e9da71d31b3265a3506564c1bbbad44e856db1762543cb583c7096cd76b90d067888f143e1aa629f22b48044ea24ca9946b22b83d187e925f12d051a9b74a414e2ac39a4367227d500ef60ef064c0846b3a34e754c67b122000bcb91fabb9add6a8797e6873f9c2d724046c835a28daa8b49223e24639b392d6848599cc5d33fa08072059a3de75de31f3630d93c43060230a54aa295a5a1a0336e260155641d2b166505532bf9bc49ccee8c79686b73f25d38de8bbaaaceec5756eb846d5d3748b1ca2fafe434dcd4d145a304b432046f670fed853c8e8eab40232024da5ac034fa6f132c6d01be23313fba047fe55816ec46c63dce11caa24640e3b31044100de51c8c3021126f5bf2f3d0344fafb0039c6cf397a167a83d131feaaa8f17a2a1aa828f82e8161ee41da5e50ba7b90919679993eaa82279d91b0632ec36677a2a1ae206dd4fc8998477fecd4f01f28f33efabfc58a86201ea0586100f83855b7cadf43100ced27c34e43278d767476cf25d9be8d0a66b3fa472e7c205a99063b2e4effddcd2088748409259bcb385d27873605afc19e9dfcce3759eed132ff35f1b248518b69dc4f804bcf8c8bfd24ddd764900097c54f19893881e9938015193df37ef7eee35fd18b02db8355e9d6f051186168aa64e32deabebc22826a812027352ae04e81044f9acac51f9b98a7b111dfd3f6a6c4dcf93184918f03807de73c37a604f932f9a673253a3c6f109e6fe7949d5941bc9312f1cd03787aaf1ef8899ef7410a0169ea3c04e2d943428cf218f06e2fc3debbfe9d0979ed36460497296241246526b8afb3d9caf2a3636bf5330722a60c3574fbed903899f35e14b326747beb728e2f8eca4d397d418600e0e9b90cac4b187f78c66092603f2f29bf05846103da163b601b03afdf1888bf5c97e59749dbe14aaf20f58338688192171f951a8c0738f29f0f4557ec6c74650981673596e4fec38f982014ee08a9e1fdc75331e9b239f51600ea240cff82f8fde5ce48b83dae65be1fef39a96cbd14fed44f70ee5d169186bbb7254632463f14bb5d76609c27207a690d63b5880d6835718978a49ea7350fe3e3c80763e29701b6d5051b4d89da108ed45bf86549fe53623d2cf829f689d351c7e40beb911ecf06af09ffd1e17364c89fa05bfdc019797a64616c8df271f1d0a2bac8467af1b2cca06ebb911e46adccff2f56e795f53529050edb600859d7c10e3a34f6053f21725df61ca4845a4a971289e0cc599a6900ba311a0ff30cb202d0a355dfc87a6c3729d0c6aed9b07fac0a4209d9c0d7a94102e5a5b0f3c3d5f06a81877284aec030fbbf0560a9e11ecb74a4d95eebc899842b8da6ebaae7cc5d94e7f5e32f18a93044268682f03142de3b918430082a4bc2305d13ca387dcfd34f1a0eb74bc4cd380cd7db70c90e462c7add605e8b39643bbe288b65e0326afc82eafdda2fffe46339bcaab2bc4fc8b80971ac25809a600a72325fa9f2a61d4654a86150f6cb1475cc2275bf43ffa81b887f27f38022faff32bdc9298613e5e78fd3f25d128555ccbdfca7b6ad61047a66f6c93d94fdaeabb9df3453ebb35d8f0a7a99f3dcba970d4b96d683ceb7340c1be3fe7e4e150049cbad654883d9d4d22550462a35216e9b0d28dc000934b9f1ccd8eeb2e67e01759e4a5dd3f12be6d316aa8d7278d49fa19a984b9003f0b42bbe4a6471c630a461f55db964feed1908c89575510a95b7c8578a005812145c561c741e5d52f4dce4b5ac939911c35e755adf6fbc811c3c26ec8816c3aa7edfabb70c5282f74aaa5b4cbb2313ac8c9a9ba2eff000f1db7139af0d3bcd19495b405f656e9ab6a2eace62657fb132f767d8b33547ed41ac390011e9e5956c99017f10a7f3ff7932edb520ad95f5bbbc50285bb4fb740fb6cbe0487d7cd2a7f3b8925ffd56a1259845fe9db620257276f0c10ad9d29f49f7e67daa58909d0a09fc529affacd1c0e70f03c9b503a87c09b92d9c476dc05b322cb8738ad1e6e610e03094db78fba5395616591f511fae7e2d6844d771fca020d131242af708d7594e9e67c19bd5944629d2eb1363ee1ee87d4a3187e7d38d58d73a5ad11950162ddedeecbd5dc79f69e9084319e5a6231d81fdca09bc91a74a71a72252cea4c2e776f7f87cfb6deda43fb7b5671ee79f0e070750c16e7b405a61acc939047ee4794cfe3a0b2f3f9c607507610402cad8fd59b88a83a83d9ab6ee1889b15d09b240ceda5b04a700c955fc21406251d1fd01f8c804bb47b44b6eddd84028b8e0bfb746f20b55573fa7f0ec340a975e4ad378485fe727fe63126c11f6d756927b18d752a8852071b5f8bd7989e627262092e2c6e2935ed5eee77222fa8a5e64c5003103cafd6af12ec523173c08838a9984117d72830534db85d9fd5f2951e058910bc366e8cf9b08d4c6132cad7a004d5606f1da5042fbf6bbd791ed97f56ef81e9ed1403e99e6fc7f30761055a23aa6d341c2b37d7c52da7898f217f27267fbfcd3ea04d557663cf6dac724def660cb4a2fe79fbf1277959fb4f3cecd6ed3d76829e9ecdd23b54a7da82f755ee2e7f4f7677c3b02145cda0a835f7b98016b17700b98b5ac5a084066f115faebe6fc4b812ad10b707f2bd920cbc7595084264a28212978cc6f2e7b93640efd9cc6bf1bd971a7c238697fd1a1e062fdd08f970429e5853c41babb86afea130c363ca1d9802d96c057164b0da40559cabbf23e2e4dcb9258160bcb00d979ae6b7b2eb79e86bb255a607621c2e305b7c4d3a255013f49f278e5258765f31c38ca247bf52f3c0b25a4fa9a2e1034e8cf9ac4ec6604022c14e793f653bb83a0404242e41dd3d3b6475b07358f0512033bbc5dca0ac4df486d16630b2981c02f2526684d9de16696ff0f04a68d66b1c64418a028ca8a2d815034e7e53da6bd6a4d13afcd1f7e4f08b84e26fc1f488f2b3f3d9773225dd5317ddb0392b9b82fd8f890ad55de39f587bc594170cd3dc355c2a3d2629d8ef17ad481b88081da25358a9ee0427f34f2d1734561efb48131da8870613963c67aaf078e25b48dc8e6b43a72e21ab63e8b071d78d5fa90edc16b2967c943f74c256fb8e6acfa15350a48e14f2dc7598e03a781530ea993277fadf2552266b00eca91784e393cf33309e0d398709882a4b9126efc01f21cb14a597744822e0f762ed0838b2164114069ea4e08e680a430306dc8fc1c426f6a9f7838b3e6bd74284305c212f9094c665c1afee2baf481f4e94b42ebfd9be36d8f320b41bc4b7c81e69cefaeba9776d7c51b028ae87602caf4cf6d417cc085ccdc5c29c12563177242a4d1dae8cb2e24db05d3ad49279ab607ca5728410fbf258ae487d621d3940fc012cd3b9948f339e9e9193c8a34bbe7fd85d5a82bccfdccf53840b715618bca9abd097277f208802ad7819ec2553e2be938b9c0df9534c37d270815b85681e292b42faed5f1c0017efedcf47a0c59d12bc6d2ea6491e6a69dcb9c31f3d3e567f77cebc1b57761c60549727b20d8051b23d1ee48e9bea600dc4af9e84fd6d769c64232ea7e20274b9d120f5bf7f0b14d506eeb5aff2c3f4a46a53b2d065ceee746531c4db8e235690c9c65f814da6d54245af8a96d8be62153dfa11e77a750e0733d54e9a69835fb17b806a1cb08a9bdd8b779747eb58adbba087963c0b6a114f7b59525106e07d751eea4de2345cce8de38c26977d6318e93460c6c262d2784fe18e1847bc80d7d8a9f8c5b2e370c09da6aa8fd2c2233e0dd67963eb6770406e9effd71dcd2a5c5c18398754c58d67252f0b9f8418217138215d17d6ec23af174b9dac9facea4b06151996574b5f3253eb198a52f859b29d9570ca2fa12bf7e725c615059f88a3cd45d09d713afcf7fb273e6fe6af92c7c26e8f2a9d9f6847ac2dfd3bb8a012a2da59681b38463ca7e3f0e6a408b1f13a73425456530124e0fe038bbe62f668a341d6d826377ad499868c76a5a2f8bcfdd5d5e4cd9bec684a6d3036b02626d02a5c6aa70af4c4d76425e9eb5e3c01bcfb81342b7e2df02170b3f8251e8fe218005950158fc4604102319a6de43a617b0608100831dbf2c64f5d9cf2d67518023caba4b90f9662067295d295b8806d176d1b2ce7d52d9da8448c3a9aff70f6974e9b7650b0459355ab24da85cc2862127a0800da875f19733c71021f67868379d5ca572d2fc77511c569f1d0405b6430eaf23d849b9a9caa0514a7a5228e7dd174ba7b65fb6bdb53db132c513302b08f87cc39908b6909480e4dd3c6f6a5c72928d8552493319d2568d8fe239c41f49cd1ce0258651659cdcacef856af2f5df24a07cc754a0381e5ece39fcde5a447bf21024cc0462cc63e5c72ae74486bfe61bd4c40d0b1d2c34d3c21810a444416154c19dc2181dc1eb336734b9dbd770d4011e0652b2d2a112930e5e6614e7d33a9ca1bb983649cdad8fc0993fd09b3d7ad9feed3e3d63ef4eba63c76e4c01b18b9820451a323dbeeb4a17e34fffbb8dce58742ffdb160379a4ab0c7506d0162c9e9275d0c102e31989250ece21f9cdffa35b956f5178351fd5524b6516df8b55f07c7c391356c32080471f74d8110ab0604de7c5b00c44c813f785ffcf8302da0ccebb7053ccc37f0c9fdc59f0ed302940200ae732196d4a6ea10ac425b062d73ad6e7945cc23499ab7e11f16ead0709f8eb1ed0e5db1ae1b9268dbb1ecf22dec8025445fbe0f1f0330802148395d89e21981fe6c988893e39cc739fe72f917eb7106c720b496f2727a09db37d889600789f8a8a5517b8d1897cee077646e70248ca357403c7f4ef99bcee9865e1ceb0f695f3ac8e83ab87825dfc8d2b938e2848e3b910f9e8f65611dc01b082f11b071f5713c0e037ff184bad53ab4d388015e703807558dcb3f9347fd0fd6743c0b2f79e1ab8270ac20e37ed73ebcd016ba2ef48d03c997e48c94250af83df609044188d295f06c4e57a315cfba7f2f7183d1d7a93f7569eda0c6bb0cfc715dd382f5786e91798e26be80caa4ad4d5302994726b61d7bd8e3bf88e3d955f9a75f41a97eb83201d25e9a0927054b227d5061b37391db9a13c4bcf9a84c7549ed7508f33a2ab35a527b15c2b875aad38d58d7ea8a811acea1d597d625ef69937ed74d539f4931d005cb888b977e6e1825e96fedc1ffec92a85ddb6456685c0938895581b19efb9b8ac1ee7a0a06baf018325b80573fc110396529b99158edf88084996f8e3577d93d65bcaf84d27adf36519d2579f9ba03a7ae1ed7afed07054f8b9e37926d821f37be8f9c9467df8a008160b44906b50a690855a4898bd1ca0217c5e1262be50b6689450c755b286210cb7c11e3e4c08b1800cc77017793711e71d2644992284e76bf34fe023353e75801473c602078fb03809c5292129c8a5718c68bf8f84699cd7a161f6b106dcd0272a3c5d201229f7ef55afab50dd96dabadf6d3586980b7413a4346455c8f5d1eec02476c4bea1630c1401714e9bb1c64eadba48490d18676c82c8a335e6de072d0ff627b1032529479993613e4e6790cb3f178cf333638f9d6f2d3899345fed1906ab4ce1f8cac252dba0f14ebf7742c6238539c59712ff6e417232ea6c709aa0208f1cee2f1453a12f3ae05927c3d2e144620137c3c2a4e0a695514cd20dd4c8f188ff9e74743ae04e0df788b681b6a1aa9a182762a4ce04a89ab9bd81f782c1c36ee907238545a4eb906dd55e932e666de8e3c7e976244cd4b1bcefc4860f903aca783a6411b54e1df5907db08c17390afb020eef68a2c4561c16df4c87b02f5f4cba6036d4328f8c327e0d7abddcfb1205a48365632ee46f863d783214fba796bc4681061166b96904d23307596073fe01ea65a4fae12c4943685b89eec22099f048cef136737176182367c301279d2b1842062f9e21521a33048c1d76b177ccdbf8047d17421aa13d2194db20dfb0ce3a53dc6a2b9015c299a2f8da96b66c3bef9a2fc4dd25f174899ec1142d950c318a08e3502c3b2b6dc0df62ad1c423eb6de0928d95c84b13be13ad521fa8c6218d3e8871636cb5ab4d555af4858ce3648c044919555165edba69a7417288117b35c5b3d09289afc05b6e734a03d4147b5bd04c2422e629f27e033b38145c9de5131fc5f8d31286e78144194ba0542d3b06040e23a15d5708523783e7485a59b5295e809c0ec2cff9952a1a3407778d39306e28ffca029acc7b7b8cbb620e4b04ca3a9476c81370798347559697e69c171d3c9746e0e2ec88c2307977f856f83141127ee6fe387730c7d3807dcbb2ae4f32c29e81d08f0fda3860a00f2a566a6d2bf2ce6f587f1141aafe8377805853b0972463a9de19fe11269b0ac2754aa58055d4db0f4b215dffb1d7f7a4efa2569367a0b62218221c8c1e86c9322b69c426ca6bf481e1774510a7dddfa04dba791557fab8535da9bba33f1bbc370e613a57b5f87c63bc71045c7d471a94a3928e04b3b0b2c396b63fd0cc52faa9d7cf0d167d265d6a4e1955a0e68ae7b555aaae683eb6e3c89d555340cad34026d2e4b1f749e269fffa7f4338e41f15f626f2de3c298ef71bc0482a9fc40c2c45f6062114f48025d9f8bd48d2bc9904a95e6103cc06f8190210fd7fd0fd1f533c497aa93c0d118c72ec7f2e75fae257a4e53265f1a67fa80f97a920cb120a99f3c775d754c428063baf18ca55da5048eb00526253fda5b8ae69df24ffd399def19a398c842f043cfa48a180f58c8acb20619ec132d65a6275e1a0a32f36f866c0f74b81ba5bd70085e04fc8b16c1768ec5c391fc3f0cd1d67308d7d1d3470805696b9330a09d4be8d2e9c3c7309b93b0cbaf9a2b22631ec0aa19661eb98cd646fc10d3d0a932db1ee672290cece5f01357d1bc53eb22247ac6893e0ee28d49b22f5dd94af7711bffff505525cb8d093fda6bfaaef8edd2a4684d19968e6df1776b902fe1df1913404981eb94faa8d4dec1a0176a67b047a5e0f287381467d9180f138851bc027637b81082004e177b9cf87c42a7ca0ec01eb258a0249c0092a108b583e944b76f765604df80cb838ecf67f509dcfc608f3e05903e5cb57fa0406a0944b4241e4a4425bd912543715a2aaf587c07d5ac2e0d86a939340d0ce2dba802eeecbfa16159c24c83508c2b7de6b5e014f57a006a7273117acacbaf26396ad066d43e88c130671799f923104e8988c52c4574ecce38b9f15a5cfd727042b332a01546e26d62e651b41d8775741f52be3b87ed00816f325f121caabca393e174b0704da54bfa10f69971ab181cd34da7b54129cfe0f7a46c86cb04e5f37dd137c029eeab327ff376db14ace0a8c1b02c0b8e2232ff60f3dcc9649d59240ba4f7c0115f632dc87bdb8d61f59138228846fc758600c4765942414528b40771ec3b5590285153c0f325007544f499859291ee18d0b5996aaa984cb1adf2fda27d43342705b145a5e220bca47fecf987d1842375e98e42e6aa29b7040f369074e0f918689d30411ddd220be5689569088afa96f9238643b8ffb1f2a2b821d65344d063c1cc5498f7e996dfca0319df2e9b0a6c4aa21a4f275d951ac40799c8bfb647f448fada102bbd8bb1bfec4e3f1685224bbfd5eeb0309ef08a27bc91dcdc1c3a91929e8e2bcf238e9567311f0db3f34cc67be997fd639f5c4526b5665f80799a798509b37afcc3fbc1f99b9f921d446fc34840ec4e97256ddc5ea78cb339c3dc75eb05075d4112cb9860712186ecc5625a0a7d117eb6ed33396dd8d9e6df087b77bddcaef0971f9a07613e7deb4b54c0d24e7deef9c49fe9160920ab56211f61e251ca4f28f22ab3ef93d5ba829552ffde6b24fc3bba8e24273d636332663929397946e54fcb4eca41d511f708a25c1d7a67f9fa12efbb458c566f5d49176d38e4eb15e41241d5dee7e645de98ae19d001fecd6ad1e93490675c12452dabd3dc06e15939ac641bd4454e9c9dde4be38e4cc3efb29ddb670b9f20737636726ab67bf5ebc3452fd27d12dfc98eeeb2ddd8a3a51aacc8e1423f8314e521468ad006da256f430356ae6eadfedeb6c0c3f0e0b4dd6d1091ba1a38399b88474bb7de9d272bc42772f66fb7cfd0779bef789f1874756bde06253880a29c5afb0b359b40ff6c02ce7e8110ccb4dfa3cf44ac662ee26334d2a0781eee729b69db5c733434882eec9815d2c8ca252a103a36bb6caa1ceed78618bc6b45fca091067a80e068c628b2d4f615d420f12960d82902884aec9fa735e06176f1325b76a67496c04d2f47b2cc5502f14b689282aa0cf406cac309823ba17ccd9ff09ee223e143494d32b7c464a83ae8444fbfb92b31da20f0d246f593e81f831cad3a723743a51e17e9744cd442873a80d5335b4f7bbf3e0fe9f5423205b3938c19d0a08a343d255c239c01df59fba7f32a7f8596ac9664657d84fa2bd2744a19d2301dab6d8079bf5d9faec1e956aacf698af226fd1c2f018f2df22be10031cbd97bfa276a4977bf88f6295622b777cce64718c7ccc2e243680093e1a1fc23abfe412c7d9b0dfacae1c167fceda077ac5f2add68c3aaccca1c16761f719647ea730840bdf6aaef9ff1a3b611587ee1ae655fa3191f9194a415799670d69be3fbaa4ac0879145f94fb46a91bc7ee0e121b2bdf35e9fe04fa86a6ef8d7065bc327104a76d9d87187b8d4c985b6e2ea6a0b05ea1fde74376b13bae7dee2c9eb15d6f3ac5ea482ece2f6fa4385475720116b75ef2ebd60ac572b1554645bdcaa6ea3a11a97d1fca8011063b1136109d27aa3a3893c9701957f3bc85dcafcd694098ff2dd2218adbde2e11c8eb8ef850b6d98870de480739f9e60a59c1bbd53f8eb2f13d28436fd5b05fad05a0569ce6c80a03d9a332d626fd21095d24ce3900b89a8c318c9962e876291c0cf3ebf24d642fd3032fb0f28ae434d3512cae046569a90382aab6d78986bf26331d76db2e312ca2177558378fbb72dd6bd9e68cb9bcd30a46a738893f390ad8869500a6785d3d97f10ab478b0ae1521d058265f4110dc66e053e0f6365b8a161c9ba998f8c6c3e0e022795da79cc9a07182a3168b5c2c2242e5e3cee41bf2bcf1eeee2c43b53e1b415d29429158c9fc1d6e6321807208ada164fa28277494af7464234c6d75f079ce1c1add846f57904ccda5d782d971b4e47380771c2231ef8bfe81287c911ed60ef784476318e045260708f177ce1f8f5d114a4d060ee6841c5804cff0a0a412981468146aa49e28081bfc8c86f093a7fa2aa356fc843bcb97416412e10f787ab3715230d5991f0c592a81d9343a18f1697812a7be56daf9a5fe7e04cee01567b69c1d6ad8197cdd19069eb9f959c223c9fc948ad1fe073ac2ec96d5b3108b28d556e1effe812073d572a47121ada15159369f817bdaaa2f2a6be0ff866f28b1d61a162a65a1f32e2f32429470e72d2c213e477ec579e1bb1928ad148375983b9f934e0db359eb9642cb864f211442b4e24546d9e67496c2e32580a3af4503f10d421e841a4c00f1dda552dde00f8ef50486bb5094475b73df2efee6645cf8d88127121cf5669b17820fff65a6c132f52ed246d4b5554a72a3531dfe22a7b5894f3df402a3a6d018e5a08411b435467a5e8c065362e44cd985da587c9cc7404cd823e079cd6389a5f1458e6219c86a91f658e152da06d0ce2361b40c1c181d69d4e9db01531a0957210315ce4e157a746f24afb298b232835f7d36108278672a5741b305dd05201c86d4881ebaec1e98fca97d844f69109d004f97c03571c38b545a6587a1efc4095e80244ff9ea299759a33153f0a24b985d93dcc829d21e625cd4ad213e0023b3b52b9e374212d30c738374addba2d3b17a8e41d42107550cf9638e48709a4e9e70545c63342ec6e336646cfe7cb13512db7675a5aeb4b8b63d15724564926f20be06df3fc57594918d9dd2870b15a42cbd76aa03928e4a53d630f0ea2308023172c21c79aa684a2e163f224839fa05b210183c201856964ea6705c40331e27c6e33b647f06929b6de5c8eceb38b624310b471120189fb4ac8e6564846c2a0fd48a907c65b4538973027c529a8d19efe2f043ce7181e9b978a63af97ca5c0f42fab868876a862b29f4a14efcd29a2f0250a329ec3dd00c63d4916d3591895e816f18e56d861bb7536758c5564630873ade367421508950d01c79c971989c0f3ffdfe2f7b11963c867372c123541506fe2da525ce873af7ab624f65837815b56a798eac94c9568ad9509867a2253354a63b58b05c55849158bcc953d09a22ccebf9c7c74d43f672a03181ea862ba41dad460184654ac2ddcb7467e52696d50467bc5498769e2dbe7206861212a7148ed6910bf44ee06f615e4893a129d6925ecb021f7b5eee403322e802136447faf4875c609ea90fbede9b723f30d9b0cd4f51dd40b67105d67556cbfeb38bd859c01d8d9f3921d0bb8661bf2570cd1e283dd77012eca00e8c106701fe16a7ad047d2bdf8e4256807138ea34ad5d34531c3cd938a804300303f00dfbe4312bb52021e066d9f4329970a7f7099614974da2327e464d4157ff185c34ad349958e0b28e6c784581ca7cccfa9139e8c754aad53ce789510aa6f35cc582852aff4fcd3e4b512a7bfb5e93ff1c267524a3750fdfbbbd52c83d604348cada700ccd83b3fa4c3bf2eddb0e54739aa5bea2fceac4dd4c58243719e5d694484928a9d030ffb9bccf3a4f011730f8e26428ab806b83b8a67e6591521541410f2d6430aa904b5d1825a98e3ff69f14121a80519cd857ad762b0df0ae9d39f7702cedf416ea034fc2a352ad0661a3c85e1f73c9470c611f56b9441a899c57086ed9bfa295bb207865be084d96a24f6c383b9badfd627050d21f20062c752d8545d4aae67da3a6a68ff8bc25209fc38ce00f6b7401f221c0e827d219ea85e35c09189131a36aef4dd0f4f3af59a8fe1914dfbdc3a26c7c9fe7d3dd8ca8164cb612ce9ca9f37f0071388626b75e0437c1394d1816d5580d381b62912d49ac9552b38efb2ae325f67c5a38fd960046cbaf7e786d54c2e781b7410c35872fd801df31230b0ca736650d0a9a078d3e9241f799c5a0668a39f7246845602924491bfe621254fb6089f663caf99c1fc71e2c5da29a6d1fe0137610c83ddabcaae45789f4e38ade0f0768a8d079da0bc41907069883771d94e83f1e6233255a53557208c5b4b99de88c551baff55fc273937fb10dda1f96e0c5c1b99411bc9226d1e73eacb494a2b1845886c2c6ce67b8a82abe5b8fd0c803e5d6bea79086fd00b10a2a38b140d386e78c2949b7e9b517478b012f9497bb2e45e75b2025eb26432c5045f9a7227fcdf887377589f81024d6747c2d459222684273db920e0be935a1d6b1562c1c5813a3ab0d079f639b514659b0eecfd731c5c3513d1cfbbd57aa6a482acb191cad1868e90f5384b5137ce1515f9618a66baed6973dbf9587caaf4bd9981e29382ec5d5634e904608207102a6bd26af02706c2f02f8ce4e945af3b3bc5c8a972a940501372caad1b6563092c16b0086ff0f38a5d24a1795c3930aa2c8d81626c8b22629d0453e2c37f8eb612f88d3a2269f14bff3df627629032e5925b4b274d42442af0360b93d3486203152599ec11788b84f973976c139f2b361ae6ef7068e86f9484debdcf5b369a02e138b7bc2eacf408824594ecc3599e1b13e5e1f6632c5438ab74e881e499cf1ad60e7d9df14ace3b49d501b8fd6fc23329ce26bd388b85a0964088dbbf5180826bfd96c0bdcaedfe26abce19dc3e60892e274659a5680980014b39675ee715b40dc45722a3b2673be53b3a21141b291e55548e7696b5a8ea458f3d42276c03a766889b4c6924615cab5d81662d4b137ad4adb5951144aee51356b3ac9aa855ae72c01b88aa038d10128ca41a42842d05d0a8e57d9a00f05f7cc6335a374f0d30aa2387cf49fc01d60f35f786b8224b7938c00e37d14196a2c7446e343afa915ed1143381371b797ae134a4c74c636f52639ab3edb45be6620a2a9427ee4ca057be7650c9ab04131fe2f2da3668466a6e10bc4c43805d77bdde0d4c2397dc46e2264380c01b097f98e0defe5a74ad9cfdb3f4ce67a4c11e4ecf47a2f631d486891a59e741301a41a97ba929e2813a34c6614f85ec803022e2bbf10e3306f8ce551ababe5505ee65387c19aebf2e737d92bbd2d9426430badb7048a6329c099e846e68dbacc2ddc9be615798ca09191b02265182ebc2f2999fd42ce803a1fc7e6fb70641281785d70e4ef88d34ccdda5c29dab15784084bf92d2053b90cb001c351cf8969db219ba42528653623381b81a644924a62f13f0d8441008330e6d8da43679a3f1ec4b6085c334c5742831236fc9a0982cb2c11d08b1d391d82b8bb58cc664a877d15f49902e92d0f8eb216c9e94975049ef4aee336439a48c2eca7f67f8485bb06126ed37c48c92be3b4642fe17df94d244bc0d2bba9d6737f581d85094586d9ed192ab4ab412ec444c348e272fef8f75274370bf680d40bb2aa1a363db5e951469cfb71efd4ef13ee1588642c40dc1102846eb770edb4243b373188fe47af3214f004df5c072a970e5d47066e85996f0b58a3bc82772a28014359ed98cdc0a690734c1512e07c3931cf78c85a8dc42ff6557ef5fe66441beb6291ed6419fd73a4afa2c1c77cc890e5e3c68f3c32155beb4673f17902b05ef0df3f004eb90f7c96dc16bddbb442cf4fa8eea99a07545b95e63b7593e53fc22e7a1ebd80ee49823d57befa41c6031a6b868813976fbee217f3c0993c3e03b7679394166e43dbff4c393441de053bc0148dfeeeb78a33f1e97c77004b7fdcdec2942ec298c51b9e6e692d9142cb94b37013a2f1be2b180dff6c387bcc9ff37e172952531a4e4f91330a6434d662a2982a8900e70495ed68c74b8b85d5f93c666f8bd00174b956373904b147c0685feeaa5e0f56b28abdc23595e08149b0970277551dcd0949980a4de2d79ea201b18ca0dcb811ed735210b138eeb4bbacbfbed85741cad739d6e452796dd475f2cb9393de8bda6af1b7d59d671a3500ba14f91c7760be817cf8f993df970fd19c3fa16e975207102c4a422e4c985243698992c72666ab7e7c8673e640c612b4a5c6e9206d757afa0aa9b409466ff83d21dadf7dcddeb6cd4ac283f617523482225be93a8da2177f9070cb49e267dec08e910eec10989dbf53caaa3f508c42a13b52ed9c43cec9b3d43cc753b12686e8697e976592e2e2699bc2a672939be779092b4f74d192097c80bdf7436785338499dae45fc34e37b6891c0ac49ded3c87f61bf27d5bc393d523cf15be4c247a296662a3d38c80a53184db1dcc47e17c5f59860d2e0379804c9b372961cea8777c867a951a5df0f04a983215e2e3ed089a2417269fe93791c4fe905a4ef6e285dec3862cacc8553e05cfe9e7ade840958f5e8c9ee0e7989c6a4f8888914fecab827f14e4162031bccd5f052ece1ae80ad1d9060b1d772ad775b9f285da946ce8f4fdad74a41774e45df5d241df906ccfbf387e43bf0df54d6bee69e04e734dd65737ceab34e35ddcaa90b2cdb3f494605ed3f52ce2df15cbc9940ee88a9e587adba1c255a19cee56f5cda04958a17e8520405ecd44008b2db946ac4a56b0840f008855694c6bc0393c5c6f406bd63e726ba62f5e8bc937b28fb13fbc7c949d858dde754cb25734d40609dc8ad953f9d331443b9982fa2a6e6569941ecc044bca10f3ffca38c510536f2d423bfef295757c20131704afd09fde4ad17f472b6791167d56a9b2e7e9311466871daf9747d377f7614da42cca6c34be2aa3210b9de4426d21ec5015ab86f394aee4c30ec3110f05ed02fc61913a9decf67f773969b2ef8cdaa745604c2b4f46494e0ce7c0c1549604d8fd87243f758b969baa2fcfcea2c61522924bede926a1a7157d4fa8c2ab5c92d8232810adea9c5827e4a48e0d3d45c96a0a31b822439b68baba8b7aff27282f1f8aaf98ea5c2572e4977b6de4b22c0400e091f3ac825723e246913f7c0bfc38455fca663ae013afd3a89a1ebc6d2fed703d7be8348fb433c0fd4ad492cc15e7ed63a1c9247ad7982615b5abc294ce2e3fa89dfe5d21a60bc23b00c04560b200b22e6721000e6029313ddc893a82ff4a56b3d63128dbbd1f70ed97fe8aeb15e049793568624f899fb5fc3f19b90e43e3a63d77dbac4e2a679db9e4e62f676096c60fc74c4a0ba95ee587043ff58921b1513b48739d5a084b41c3fa511ba3315472ead26d4a598db5ec254c1c56f891041ed686be6db19505e1642123e1c543aa1ccb0a3072336279722373566ae8b102dd9b3b946a3510fa62f3b73c187c491cceb19119595d2aa0abf2ae8364cb102d19fa65580cdd84038edcb12127b6d1d0e373e8e25825da46c4c9cc4072a338b05ba0651823c6202dde7cc2a7f4f631bf716d2b08ee576188ee5a7cf0d3579308a8df7f95227cf430eec510ccfa7edc53ed73e39a360dd06f64905cf120b666927b2dfcef46f58fec98cd3bc31857e61d54862c4a356b95613a8fcd1f5f44dfc8d4118f966a52a67e88c9f64b09019949fa81b5ada2a8868f8a9daa804a9a5474a90014ea6a826cb1642dc2a8f14964b37aa463124950a482afe7a6471ceb9a58594f9929e241c99474bd4ccc70aee7392a842ed51f7f37323b255a4dad7d08cabe84c75a9e61514e33a3a538df92457c1276814fd93400d8f9995ab76841bcec0620d2dbad3cba94f127bbe2216397f8558eb06a3c419f316697df7e950942212fa730204294be3102d310a974f8abb0c1cd947d0809885af40c23d391bb5e282b8968f6e36195480ab9ecb3c07a2ec7f3a3d2f940c4f9d0dfa2092ecc3cdc57dada4a6c298ee1fbd7e3245c17a69c8a2302e921723b126d4f70f221cf2d8c1a86151cb05cc01583abeb3ed2cc0b395308ed182edeb497a1053a4cdac7014d94f21b0e770347319778abfc63fb753db5bb8b389a7e293bfe740bcbc7569afda83cc7b79bc64168166fa35cb467d613b6db5247704c82dfaf954f01180fbf4bc0bcdc05f26546b71834f53d07ad9f172e4013908b41651d9a6b5c4d3cd5e1bcec0c0e3bd20a6cf9e63eebde18038881a0370b8edf519c63e1ab21e5624b0b8e556572f1194ec20ee5e1c11b8d6e04cd0813d0bdb517bccd3b134519cd79f208959d7ec5309a5b5014c0afa8a0e5aa44814d49028823fa133822a543ada274bb86fba1083b5be33a3ad39f119fd749bb94b4e45420a09e0e5c6d035480362d08008ec6f34ea2bea8eb8292a1ee822e0ffb6350350a16b723d7cdb556ab642e54e2b1690570a0c8585c46a4a70a43696290cab22bbec8fd8cdf49ce880a0a93375eb4566918e8c6253baeda5e1e211c0c1eb69161374b3ee9fc6b5fe965bd405e1a5f863b889ba34bbd495d226236d54347495bb27c44c8e6a6fea6d06ff3088e6bb29a574f8124dd21645e3e1d7ed9c01daa439131edaf66328c4dacfb69cf63851983f4a53db211d53c19d46fa1797968b1c6d6cf6c376ef07b8d1f6211434f030a8943eaaa15dfb6ff6ba1493b116a5e8d07f1f38e28a74157c876a6a0c43da0d121fab647253791209eea5717f9666e4019b7a0c9cdfa31e36a057d05d32401b4d81ecaba6fbca116f97e71c084da716a0ce811abf06581828590842713ad76cf55c37a49441371844e26e888e83064cde96f51b097b49eccd678b96b03ad65106cc9240c3cd69039f71a883b2516cbe56c10a89d8e30d7090b22b5fa0a8fb1387e36ed8dbedbce91044dc99630745a77544a3ffd8600cd51da1d27c25587c31d23acca9fc66a998048d60b41fef82b0b040a9bd7101f9b81019d0d90632178e0f872ec26dcc71604c58c12ec6859b68e2009a9d380fb76950b7f1d03921531eaa99992867b2ead6044de1b57417fcd86703a7cfc807df2f307219a17b41bdee733dd8971eef9bdeed9bdeee433deffef8d50745a4ce28d1d21fd2a17951020392b7d529303ca7bb73333cbe3849e48b46ad7baef89b8e0dff6def2df97fc4bbb43d1827e529739e21022e6d43e2479bdbeae6a6d8af073775b76a429779e2f1b69b39e4cc6dbf5bfb89b6453f587917b289b0135b5f94d91821bff8877bec595dd049487a124a5aba082d4ab895760ad6d97ef16e2987d919997b087623a617a6d91fe8991531e691bf0a35c18edf81704c28ee854b4966bc74eb63f84b53ea9e12df6d27f04f3bbbc76eb313916329ab355f2f90e496bab81d8a132b65bde140d94eedee85ca48e6a87a54f75315b5d654455184dd61891b8de6acea09aa0e48f2ed2d154159f8f5c8402beb1990a992f2236d6c777c9bba8d37db1474af9f98f1562a2510680a7cffdfc608daf4214dff9dd80f49081f7b15390a0469d566991a2c1723194ad6a022e675d61483cdcb99318728ff915f3372e5df0e1581eec0a990539cccbc15bf4c90e8b99a1727e27a3887858bc03d7336ee892fef132459d4fceb78d6e03199589ad9a388892d6276958838259f66abb6924b0b3797db0b7ef8d1dead285021e4cc3a3c47b7bee3918657d441ba091a7b82fe3d5c4990e2fab0aa274738a63861cac5f546262ac3f4fe03d4415ccfe9714bb2ca6f178a4219a6a5476ebf8c7e1326a67636ad100dc40bf924e4ce721a7b89646d7dd268714eff1d584c58dda4f9b098b48f9e3ada04e741c126ea87dcf753a6cb12b7734514a78458dcbf2b56c0c52f501ffac4aa543fa838dbc22ede64d77b2c4d134369cbed8c0eefeeab98ea0abbc8948bf988066a4efc7c7d1c12352b2df90d1979cdf4bf554ab0d46e994e0bf6e0f29d246c0c95b6687ad146865422f4126a7b70581b8b1df6c269c48f47d4253ebdb6ff916d02603f2bb2c084a9ef7e41c538da4d29461a2b500815f918a09100ddc664cea1fe533c196bea9e25927079cd543af1bffae005b26f789974d94cc200da0bb3465c27f7a9122fdef61b876d7af874f2f43117493ba3bceb85a11d64e29edbfc0c989e5415469081c79ab827a621832775abea3d5b67c98adca450d543a75e1fc51cecc8ad5b2abc5ac646448e456fc8195111e66a08aa202c6b0eb535990817762406862652c412a10b27d23aa1b823c4f6893b1bdb374177a0f87df363fbae7b8aed23964263fb575cb3bf1b376a23da2a2a60ad2947aa6d1ff0ca6e5ed690eb633c886c635ba83e33d75511d9679c23a3430f747c39716d1cff8a6283af9ed12e7f887ee89f4c74664c866bfcd00f4a6dae80d36f3af76cf22f9436dc848cbe149346ba55046f910248226f58b91a4f15739255d14463e38890f8ae2a85010d21a5f4fa3d7b77c272be327fd397ab6930e33437e31f987518ac1abeaf839818c83d6da707d1daa9316bc19e60f8236f7f06ec6146ab6c25e463f7620ce2e1b33b8c4c0ea22131082ea542174a6916fd844892084171a62521d606616e8940240c9d46c1808a4fcad98167c4159afbbe805a3b7e73cbbd14e31d133f37ece9d2527e1627789208bf7b8dd265e1947d7722b7287279b247b3e49ef0f056b8e1d156afefb4898e46660bc2b47714b0ac311c561d703020225ba67e091add6f67c585cb739ef84330ed8ff341465ceb213154f4bbb74c2fdd108b5a61f0f14b28d8b7f7ab19d63abe9b1983687ffddf88d65fdad112f0da81e2a0805ae8c329d874e86c22a6e8a2e6bbcf68fa9bd7a1c3c2935a3ceab2fb91f5fd5c0396da3c69a79d5d828bc20344672bd155d491553b72c79befe5c60551ccce079a3244132092c5d77277b25527a8841da24ac98ebeec5fa2b9e1d4f10dbf1f158c2217e40eedfc8ee7d1e5a14b5a42f4ddb20f7f33a61109fc84815ad4922afe260b6d9d595f6ff77a04127598c871cc40327b2e1956c0547df1ded18050ec30850ee6f802aedb1555945c7db185524d8e9a5fd9f468b293455e822fcf32b012574121ced80aa350854625fe20d526c3624a3c5eef22905828d39a4d7bd3d7fa9afdaeee7d24c2e4bbe36308baf53be0a749a6b78dfc82c2d45c8b3140219b614e572ab09d6e2e6bfa14f567eece948827e3b1a779948900708a9e7a585432843e4dcfcffbc619bb29d7f717cf8669c7043b00afbf07f7d617ef38bc87c1cef7d658916c8d1ae0939cf73911690273a97c0dfc9526fa1a577fe39380fefbf4a239ca10ea76599e7265aa831a640372eda7258b0e1b446a17566e07a564cf8344270410731f12b7be22fa5782bbd8faa34070de05eeb53f80523b6f489caa93852b5726e10b7cff6fd057440219925450377faf6d84b6c416ad5c31401b3503ea6603d76a0ba754a38b85f437b690932a24f31140cabdfad6231a0399b5a6a2011a6f6c50d71a768a967671a734aee22391c3667433fe781bbdbb9ebad9e0dc07e7b6360894cb248801bb8ae0786f2ec1b8e7774346ad3b8461c7f241a051e9142dd40afc557c104174f6d65f82473f29a4e98cfc94819a072ff594ca3328c2e45d895a30ad3fbd936a0d6ab03e1471303f4e3080b2298769b5c838302f378a0c5f6560f0ec4010348a4c2a54df566b1761d17565c3ebf55d49945351a6ec343eb0481f1f79ce1b096d8b3e98f6bc8f8a3e34a2b442f37ed140b204f431a6c4fc455149114a326114df56e90b9f30d2f773905ce5789236a43b95f2235fa985cff9474898a04c493fa5da3adcee05d5b2ae8c55afa5996311557ce452a777d7c5543b619819788f7f1aa9e2fce0513f03b3e5f7dc47c11caab7cbca159c631c654bac08e965e9166611cc504714fcbbc38b894f09326b0619de044158dfc9fa33c776ecf2ee8ba5824518ea2398e13a1cbbf2b45e4bdb9fc604bce375127d5993567d9613c5c29a6ea9f9d0e50616cb39c165304c4e6e949a75abd2a09d113c33a427ae485ea972d65cf96e9359c17a9abf627b202828c835fbfc824809061788962b8f07ed810a7c7f4e7aa09075032bbbd7b46748a9dd901623d6685795c6a18bba1e9f96fd2fdb97dd6491db340cfdd525d4c9accea51ad61882eb5f3470bbad39efceb76e18ddbc872591265816e3f380bcf0e3998d9ff9fa7f74404c39db821de9ff986f0ad6863909c73bfdc8f829ad1ec4016bd430d09044f93a680e23fdd230e7da599993ea5ee6830ead6c47bf05edc83ba83cc163117a60b5845d85daeed0f1a2b4ae808e46a311baa5416b83d0dc884df29e5865f6b3afff83df2462ca75f5e58bbb642216239ea6ed352992931fcd65250479bcbbe002f793c26035515e4ba6b6ba00d1fce6be96872d71c5f279e40b451649498e30477bd7dfdce3a6d51b1ee65a25e40b5f2b86581c5db8b8cb4d15dce6f42acd0026d727674fb59020547a83666f8b75a082263e7f1f2e94899b6d7100691cf91d63ac29748b45d27533c5d4b5f1b0dbdeaab8765d35388be7de60751ec52b520ed0c1debaeef2e582aa8e1646e6bcf9d2ad8595d27654ba2f8c9a4c3b87e6f933f6721d29f3c20bf690eb95060b298cef9d5be1eda6496f1bca136b796332486e60e2dde906a2f1be1d352c2664779b2617bf0110219b2407c031bab83f2bad52982725a493bd97bda408345d153039b9a4df2f723408994274e1f1235a129725acf5db9d30b9a4e80071ff623d79cd3c9288425ee8e11ce59fcd1a09c5c00d218d799cdae38872d56082f40bc55c44031645509648561e3587d3a7ce2a58875b5065660dbf1d431260f8b3b4c2f9fa032fe8db5e41dee8c355f9d859fa10ce3223af5cf50ea4606b4f14196c3d5a8a805324541d301e96c420d724785635549706b77543b593654a69ca0a0b36d14fba7f0fdaeb1e5181e0cf58f56df4255024796920a5ae3b569f09884f5abf87c117af0c2996d92601a41f19518a3e12f9307125960015b9e9f4f40a6a588f5fe16f48220c54dd28fa08f4eec97dca7289d2a933ca327c8200cbd915ca30eb5baf34247f976c263a009ffc3ca1e72ad3671d614b8f72aa03b39e650643f00b768394611b0b22cbc61d50d46a23c1cfa0bbcbbecc21b24c43299013c045c446efe312e8f7d284b5be020d3b1a6b74ed427f6a669f72c8c9d01c3c0949d56c7c04a34f8743a4e06ed8c9f58918ab8ca458df5ad47db51ac6743bb5d425114d8f07dd070d165c453aef434bfc6f2a744d44df73c0603bf1fea082141017593f08ed978c8ce6529ccef9c5888b2a8945a7b326f6e523865daba62c8194d117dc58e19fde0d8da17930006e6b64f7d374321ab32603c8803a2db8b2757e9cae074e1317628e63175ffff298f846fb563cc7f6bf1c6e627a8086463ffa8ec63237915605f9bdf470c99b7aaa19eca078677839c5c7848f9e4797db86df89684b9d1a590d6cf0f128e5ca3a02f7112604dcd20d4b98855300c0c5c5cd77db9f8bffb7b89e99d2eff434de3cc10522b4a459fe185a639883fca069e34acb47a1de02c809c52c5d024859b053508fdd18e13037457b26fe080acd6f725bfee83c71a6d4cd19450f3062187ad18c72ba8c4f8665a2c6a2a0af94cb6c9ae1cb44a0847abae3ad8c8d461ba8245e9b46353881fdd825b2945391991aa7766208ada188051d967f4db909e235ff675392bbc90188a252e65ecaa23ce2dceee8fa74b3984fc2dfa26e880dfe5901d62d983d9f3fedffc88773ed999ea0e4a06da674aa6a790efff09e6682cc351a4ab3785f0a65fc06052a649476528d3e198f325feb3eefa7db1a48ed779eb0858ae95279c29b0ea494e7d0312e22b431d98b1f99d4172b112f9edf5af9936fa7886eda4657d21c5735888308f0e371a86c7dbd97103a79b20340d840780d3a44b288a549be4ac30072117a46f0f4aa42a18070a1d18ccb89813f6e09c97649b4458fb0540bc6de0b57703496a9c5b1c88570b87c9cbd5d6a454dcd050fc28d1940c7cce34255b54118eafca875220466309bcdbad2fbe2f2bf5165f5b812b8a3cefb87293d764870990fc470c3aeb352aaa27ff00115bc884a41f28cefc401d99e42e76ed22b98695eac06498f0ff38efc442db5278e52e5fc5bd828bab30da5c8242a79417ab3d6be9adea788ebebfbc67909dcd70d393d16c04d97553012e43b5ba942f33317098636ac255fe7751de53d1fa0d140cab2a2eee93b0d8ad6be771a483ca6dfa284ee434fb39949958d7e207e9a111752d2c57f80bd6ed54ef5c257a9cf3c95d32ddc6fa5c6a4c38b3d9ce70c2db38b7e0a286725a3a3bda4996111ec54fdb738437fd192a1f0fe848a3d19e95c23289f6b0fd1cc22cf5092331979f08b3cf4cc6d8cb66d296d69c697f65bce4495705b79781ab081a889ae4d8eab6eb3edb414cf8d6dfe7b385684f68fed7b08a1c272724a150e695d866ab5ff09eefd4515c0356956990bf00702f7ea0c57a8469e4dd687b21787d9d542bdf8ded9ca1d53ff9692ee50ff65094bcecc98d88bd24e63400e60f0591b5b979b1dfdc8a4973e12db8888f5bcb1ba49434c3cbf0a8c09553fd8716de5264f91db6110bef9830632c7679e55a02005ad692eb463cee93bd79e32ec8819b99387105d23f822dc4f1cd77781cf4e3566c28ee33b74b22d93c389e255dff94765f8d633d4013d9584a4597affb173d38ffb7e9c9920e3107a7878a8b96d719ae7a102304229332f8b1dcc5fc3d5a4225994aa54ed7ab657138cbeadda4c13067666d429d04fc46985ccfa3b256813d20ed1541d02481972217dc592fd1c3ef6deb1753afda3d5553e09348f93d0f92d53b2c879845b6ef9a0d458cdabec0bdcd24af62d3012b7fbb508c46a23fecadedfa72c86615559f8986955193002d8a2c12fc95113d8fbe879e3329b1e5275903b9eb99acdc733c95efdb825dab3bf8c0f011ba91eb67ee22b938ba44e8b4e6844c79ff0047600497cab91879cf0ba0241451f1a5a25f94e0112e5bfe99448e14c5e97f9cf1bba6844e568b14244f855e112c8f599d002ba4ba458adf4972bdcfa2e3619985d8cae04fa7d1265108b39fd30a6148924561b97b669d3112e002246965e060721fd1839b0c6a327adb2b5eb823413d698dac7fb6bcadc0e379204d2c8a1a8e303d53565b6a60b546d82ec615a931611b65236f8dc80c4ee11eb79b475e7ae02945204fa9283b6542020554d073820c327868610af10bcb17c04b0391a4dc24bf32c619571948e2569e58fe0243f0335ff7862c58d0878835ecad2476edbbaac25139befd5e8f87cbb330c74a8a814d48424d97befbdf796524a2965ea0a5c0bf10bd9c7ed4f9ad4a341df303069b07fdcfebca6e2376d2d2861dffb3567b65ccf67987bebae73378fdc6cb1e0c518638c31c618638c31ae434bc05ea7df919b768ee0127e1ddfbbe16d734c80f963b38d5467e147e79b2b34f7e59f5db1f55c6cfa422ae951f64aa4991dfaa7475d177a472959c9f00b1df49ceb5c7cec99ebb899098ea5e761883d3aa29a669d4a569a7130c94a4b94446d4e7d655a2571305ff9083327d3f211c2d03731f4effb21c8fec61e98a6833a010a4ed8d8a9e7853eb32b98161686996661a531eb58d7acbe72cf0ae62b0b6192d429a5630907dace766973beb4b40dfaa77f9ba3e9ca34ebea11c27cd949d274f2b6f3355fe18b7af7815ca651da1d6ddd74979ee36d8ef4ae35e684517d521a73ca94259bce9813a64ce14dd43fd29b8e73b6804e43c7433308c398486948cad125b9c39e4d41c7e1f9e6df633f9528dfb6504725421330d8d86b18a339ac24246c7518fee8e327ab0b83978a4197aa6d6956110358e9a7aa2f95a9e2659860bd9aaa9015c6aa2e8b43b5062ee84e895b6bbd74da4ae9156dad980a1fa74734094daf5b6badb5d65a70bc36c8a5150cf682652083f5edf3ba6f4f1f9a51b02a56d9130fdd21ebaab04b645d38b27f97c8c2708f7501dd210b1b11a99c6a7487ae35a23f2febe824cc96ea5faec18848ca4f8ea281a61be527948ac5727d519c736f8dfe2afe9c8b1ff79c831fa26aa81c99447e30f3a2f088ecaaa3e4a2735f72d2973e36891b4d4c3bb473a2836369621a6ff4382bc9893eb78f0d3aa924f9cabf0c821ffaf7813b44e776949ce49c1e4d4ce34d6924710e72cff9e8b4e2a51517555e2aad784d154b57b5b2e2a59187cadde2f8e3ee9550c54f28e75c857363398eb392efb7920f02018e25ddeede72ff7204bb057eab257a486a3907bae8a1b75c454b248da58a168a73906b8d28ae827b1450ab50e1a19f702177e25ca681c9e93639388e9c8e263728071de5e0587a5bf3d0ce32a19ca59d857216cbb58f0d967cd691441a4b6e8760e82c5f63c3f2c7dd2c5fb96832390d75a09cc51a4beda60ab2bed45ed62dfa67775893834ff2f04bbd43278965e8266e7a1eda4b4a6eba01475dc951a934f2d08ef2528f2c3f798d3d79bdfad2c73ef9288ca95011ed1f76a3bca6de946c947f3987b1948f888c8aa0bca6a67e345432aa953807fac9bfd38f8ccc40a37c3454c9ea2819ea6b6c4d055fef920fb2b1ccc2b0cf68686464541b4f9caceb343722525f737b51c8409718a8c449a038099306a44008011e9621ef2b8160d785fe7dfe758f67ba0b7b6040fcc8b1c67625a79dd7d48f2b8db7ebc61f57878d3bdce10e9d83e38f11c8dd77e3d007f6e18ce81c030c545f9c17e411a9af0c03fd39177a682a017784748272f08c326126739ddf0d749fb9532acdd5e0e020f16233443b8e092c12baf3a80caf881f7a93bed0c511184644541bf4214574fe43cfd825fd3cb3171b96faa95855d003595ec4e00a64f9fa5487dde60e7ea647eddb0a6f3018e2e9c225a62fd91ede681c82cfc31b9c029afef42aa0e94de30a68fad2de3eb0af9de4b522d8d7b73125bcb1fbfac7029a9e845556807a9334b0d275dbca0a099a7eb4c76e92780c2749626161096fae0b34bdf885e2c974e115e18daf086f340bd0f42199c99c3732674ab6004d0feebce3f2bc9820f675927b0f335bb0c73ecf3b302477b95c2e40d37f9bc5f7a14a1661ec2be101725bc762db586c1b65416966d1029adedbf649b305bbf7a5e9829dbe6aba60e77e355bb08fa7cda40225f0f31694e6aec5d622bcb9a6ef362997f0d01df8a18bec22e7cd45785377ced4850b083e6992d8bf2f9924f6f0554c12bbf82793c4dd4f17c6303ba60bf6edbb4962ef3e9c247692065ac2ed032d38042de1cd7d014dbf5911f010dec080a6590237729cbdd8b08c74ba30c92506343dde9987c5e9f3905e6271f0799e67a66d48dff4f3e020a16d587cf662439a86f09e345db0932c2fe14d2569e025bcd1d4f477c3509a615e80a6b79bf440f4ce8e738cc638312caba40ccfea0be36f34943dfc78867130e6fc1b47fb6ec0fb2fbce1587620bf375b7cb811c8fe7cf36f8b09635dc6b3ec26379d5fef4e77cb74e395096f5090e9e9e61e88de74f3d203d1bb730fcf497ae3ac2fc20bf4026dbdbc40bbaa30809a2ed8618437757bfebd26459525b305fb8b3096fd45f895f0d89744ba61c8817dbbce03fbe23c43405fc028814101717394181d0db98f0648800a1b0d555775315319259fce81a6227e705b77decb84b1f8259a2e350545d9c267866bcac03dd4a5ba8f75556f4484ba0011a1d448755507b922bb7ac518771be71cc63e6e678c7f03fae63649ea9b0b75d2db39324d92a626ee696d7a3d212216fcf2ce1d37cc71618eba6ddea67bb58fcf85a2923f3ff772f55ef0bd5a258d0ccd967c3bcf0c44d05084b3e2e35d5d6edd96a4235f7bad7b767bd6bf3047ad1b744a49d014ba98776c7bf30b72e2870fe60c825f681d343d100e480ff6e921e51cfd5ecdc86c01ead230f38eb706bc432ebbfeb96968802f16863364e438a423e1a88d0b459f1e89c55fd74d3efd06007ea82fedd385183dcf8a3b49eaa4e726499de5c1e9a28e9f35497a9a2e4a9d8eb79246887f27d9e123c26eb336678b6f18c3ceb3c23ff24d50235b31de1debba3a57e7d26e4fcf9cdc04a79ea4f5fca7a99aa475d2b326693ddb0c3edd194f7273eaf81bcb10ececd64b5bf60d34457171a73dc73f5af4ef5a22452ae909edea1e10f9d326bb2026b2bc07e40555b2057a56d223e1ec527768bfeeef6766810eb29b0c0fc803f280807ec8a9ca3bd3c53361a45727472cb32c5ee6246cd2cb4c845dc7695d322a4446319e2bc0d65615e812fbf880584665d05d7109280cfb500b63f18a9db0abe3255018cb3822e2af3b229247447216b3cfa0016a0bc319d2f9694f95ab742c7f65454537c7ba66959c9d63b74ec39bedeb72edb6bb7ddd66f7f6f56e9aebb534a8a535eaa550d0ddbdf7fac67db3ce0ff44f06f49e6705b782f48fe5a9cd8ab7424141b636344462f9694312bdd00341d0ab3c70e919ddeee6786b6def414b64893e2ad20103f1ecd2dbc2ae40958ca2b5b9512caa53a86229821b24fcc6b912c6b63cd39df59cb1c5750c73fcfd6e7e01593ef4a1d1807e7ef06579d006dbfaa25fe63eab6373ac2ba76767e7b713dc9cf1e6799d5328e8cf3f1990e53dfc3ca47fe4d7229e4a44646756663d74ee56920bdd726e1d0c2d67bfce3d6feb36ec395f7c3796ef368ee5390ea5a394e4a1e821c9bdf0066fd1bf6fbea8a2b3fc6633eb2b0c49a317c6c4f156321c27cb678bef38c39bbb2fcb111cddbe63ad0d415367f93961bc16f85541977987e5b18dc78d35c8baaa1d79587c863c15a80e5997101355087b405e14f50b6798c3a240832018d2802c7f1d4409c350a43b1b8582f6fc9301b2bcb5a17f8fe0c659bf9837662434f59aba79d7e56d148b5a612c8727a2288e66bd8e12e6b07be3c189a64d9aeb180c780412431cbfef28169d40c7ecc85a51cc6eafb55103ce4943da78636beba6966e704e1abd7b54da35cb63da79dea1150aa0fa53e996afdda33dbd54cdbc0491f80a6e611e06f280ac4aa5b585d9d1ed1ade90f658b91bd2bedbcd3bb33ca79aaecffdc34037bfefe36d37f998a481f086d47dc521e0c21bdc3ddde44f1b96cf4b747967a7ab74debd5d6b034e3a69ae2637c741c26d8674d4a7b782b6637dbcdcf6f4190bcb3bd6557df0943122f281df394ca5c1384cc8537bb88d9f87f45c755876786a4cce7504fbe96cc11e2303c684d865b62aabb3a2cdcae43d22f2855dfdc5c5d130c017217618787fcf8193dc46baf9d89bdfe8dd51183042ecafbf1bc884524bed9d01424552452abfaa1d4192c4446682e3f26291f2471851447164544685bc8474361bff47e8c305b8fdb6539e012a4f010854b28edf23c44025abbf0fdefa1be71e008ef2391c7f0c979f9b885f1da9ae4a84cbac6a2f646a78668490090120830387ccf8d65f3c0d87f1369c88ffe1afaf71d86f5f79ec336fe32c7fe2347fc3657c0f3ee36bf8cbc75ce5b3a73ccbb7c75be6797af470ae47ac87a23ab812ae53936292f5bd7429c9191b57b52574a932b4ab972a45548a4c9ad932d2c3a60e45bbc695701d296a3ffad81a51466e1c6dfca8f1abdaea08de4e58dbea88cb52ade230c2d8e89f09b8fddd33df1a9a2dd5df372c8c80465e7de55b3daf6fe5046917cc3c12c9c5852483f416a4973021e24d885497c9e5e557b515ccaf66fcaae8c5af924c98cc66557e7504c60b70c6932ee379709a6f71fc317ce65d38ec5bf8eb593811af0187f12e7ff12b7c7bbb659ec787ab3c4f0f1fe29d03ef45bcf75857f50dbcd378669c8d0fe77c784a0f8f19c26188f07f20dc7e197986701f798870940da8be54862a16e64b050d93dc822c4c65665d30612a534c5755a959575529aa6455a9c324ab0b49baab54415b505952b44b0f12dad53f1fdfe2e15b2ddf32d20a42c3507d556ff1add96ca9cee25bb299ca14350b9b53a50ea0575729025d5c48d25da5d27aaacc9ad8a1972545fb47036f0285e8a5ffaab63ab232b674585688788ec510afd25ac7c256b5554d6755d359d57476f5d78aa8c793afd09d07980bb83ddd780606c4ab2329bf228a318eabdaaad6e262a6858b5cc1b2a2923233aa6652a8929993696666f3ffb9fde76efd44f91396dbccb2265f9ae498ec98f4b4d83033d2da41ab0826329322ad179861451405095260832d044511446445b4aaad6a50fcfcace82065a53d90428675645506958cfd0dae079b1c1a356cd4ec9e174ce8c68719326862784536f330b3635db30903805fb7399326c6afd7a1f992f1eb63be5ef8f51f1396c36fc8c406c3c5af7313860316f3ebaaf97ae1388ec3f13843388f3d8c23c1d9dc702ea7a7871c9b9cdd53d363a3a7460f8d9e220bc333ebaa76c658b65432c652a569c652c7188b388e87b1d4e5f7ed949808d0ae9b4f9585b4a892453486ed9489b1f96ea28c26abaff234741a9ac944267a4ab167eba25d3d0bb55aa5aad4accf871b7bd3d98e26eba14d5a2df7fa54506aa79295450b162c5a7849b6f0d25bb4f052b7f09273712a3a11cdd3108ea7b41eebea99295a6ac857f48a5ed12b7a45af9068ddf5f1bc0f0c3beffbc050eca93c7567b55aad5624900f2e97eb64e403c1501c7924992ed2412c2c28140a855a31646414fb0986a138d2af3fc1de44848384b6b9e138bcee549fba5377fec7ba80acabb6b478cceb224da481c694714c494949511185c422de890d53a67aec4f307faacd96ea377e4e98f224c53e0dd54e43b52449f48a5ed12b7a45afe8d3108d761aea6ce74293c96838b24a6a920eaa648d79d28c24d3422b60b1bc9e9c9c9c9ca08835f18828ceac2ba792d5394e6b95ca7776cbcb5a23095957f59b27055957e53827d17cc2c6eed5b93c4d42e78ea2ce3ec9ac10f6ec14b960abb075b55aad56263321e1cbe3e0501aa5511aa5515a46d341b2ce76313a93f9d30cd3529e725a0ad3525eea9253e924013ac9c7ba7e2a29a30df5c040ed878f11e8c2b9dcf898c3f0dcf01c4f6aa2de7831926834929c39852f2f3aec8661613c0d633cf85792aa916830cf1373ee79706c3ea79dd9a2634d44255369e2d92ebd9e64258ee3532e9faa55529489b34aca52495249eaab4cd56157af9eaac34976a24d1a8b72ed28140a85f292dcda57a24c078932dba56aa9daae5e620239181700fca9c7ba708c224e75d598d65a6b6a6493fcc59772fef372f321d1483e9594194f325a17d3d164349a905d3d15e6606d71564998e7c1e12f2fca2a59fdc6cf381b1cbef9783e75f3d9d571c82c8c44a3c968329f58890aa5820a9a7a0ae49e27161b494db88c241a4dc51237bff1d3a6fbb97908632d5f62257675175f7a8bd3d06908d7a02409edea2b4419890495884a8a28ab7478120dbb60576f7d491a9a484e64a4144c232b12adac74d82589866497245a2db24b128d48c969a82464573791686575c32e492589568988a2ec9e764922d1ee2e45990f8da6da82d8d40174e19b2ab45a5be9ce20d8284df69fbb4e2b829aaf575aede975da3a3ac0e9a456a45eb1b5155f8ce98b7398439e1782f085a11dec6067470925401f7feccb5dbfd6b7ed5e93cf7978cef9e739c771e58f7d1d8f18e8017ab45ee1da7967c6396d1ae6acb3524a079f9a4f6d7f541445277c4a3e4e94944a3e4e9494f80c4be306acb5254d501927a75fc9cb999dbaa27f2f3b7572e2746ac2099fdaa9c48992e7c692a2504d388172ea8342d5504e94949438e1dc567a8aa3b840168afa537a62979c36317a497d7699f2ce4b282f7989bd3496d46753b77a2cb5934625d7f665976676e9e4a5120fffeeb2a486a19b48a4702c5f769912474ff4bb49238f91934ad358be6c8a1a7f543cfea85be34d9d3a41658c48de8d3c44c79bd49568d1a913d4a5c479e92d9747ae45afb11f90bb79888e7d863844c7fa474eb5c9691823b9fe51a964f29aaa6725450f1f7cb092a28beef1a8bca4f249eac8ce55cf5557e7e2971a3b494fcfbbbf4ef8ec9a8aaa2316e80fdcdb87b770c7ac241ef3f5ce62ecdedfeac23ec31d3e36f6fce3fab8ba02012cf9ac511a55d57a1cc771deab3409eec7e23c3439126e53f2f03e4b754f43422ffd10ce3fce491b69e436bce15c8fa32fc54087612c04b990bbe1461bce3ac5d849c5e2c6eb8531effbfce67d6b3ea2fb39496e9c7387aeb848087d8252aa027ac45812c70dd85d3af90c4b4e72f16ef1fb44f1a928522abaf8b40c4263b4f4df9f4e6e9dce4e27a727aff48953e94bba4b4ec3d857a29f53306ba761cce4f9a98f9ca7c4b5d7d4918f35959cf466e729f151899b9e7375f433deb949589d2a83313b9d511973d3910aa9a49091d36fa4634985e0d05ea2249283632d7b9470a0f5d803dcda4739787c7eb71e799874c94b6379f1ce4e55406590c691f3f81c6f3d1acb6f3452155017cfe9db9c73a679e692c719127a8db5eefddcdd8641aa0221da54c319d8c094021de5252a2f2139c7691578f29ce4dc7bfe7df9637fda479f0f4136e7240e04d9e2d881d6e6469390f3907371e4464e1ccb1f24277d3af41190d6f676909c1b45936ff421c8f64613ef73ef73911b71ceb90ebffc7c822e824efa924472edd6f3481ec88d3c4017471e2ae7bc07b847ad52a54ea89292d2c94ba792f64f9f4aa512c9f55722b93e954aa4afc471a6d2683299b4d65aeb6c0312c93fd2e8cb1f2a3cbc2afe25fa486bd0475ff60037f56d1b4b2fa4a10f4142f14b3efacf8a9f8b9f8ffc13bda435e9eb9e24e7a17de424ed2307c5b107f8a307b881d0541559801d888f6e9b3ab7e91c6dfbfa388360da81ebd3edad4cd07a816f64f124adcf10ec4a8e66b5b4896a71919e4e9dd620fbb4989bd6656b9d7d7797e1867868ce3aabc3e874dd1c3ad0d4ab4a6dc33757e839b4bfd19e5ea98cecab71d639b4adc576889a215bebf855db5b4a029a1e73685b9c73a02de72525c1aeeedc1741b3c51baea10ecf0b41f00247c41c9a62747daa94decf4d311870904e099baea81d3b9f13e8e9259e6dea375b6cafbd76f472365a87e424320ef20d68a6dd32cef8daed35b05f2b1502ad4fe8594fa71315eeacb556fbaaae02ce418ecb15cba6fe71c2ef19545f32946dadb5d6da0ba46da5db6dc23755b60be2cce2f49c1bbeb60d1a63d5377a5b57de58f6938ab56d75abd9b1e711048283a0af4ea53caec5d76e790329ada91cced9e71d7a802bd0d5379f143b75ae6e1b0778f4e841fd0e51a1bab3ad4a16b050db74d816284710dac6a87d7dd2a16e230522b797947372d25cb745d0dd0105f1d965f53cb1b39c9b3426379c873850fb3a47a3089a2d163b5763b3df61979c898eec9c73230fecd9e7def288471ecfe71eeb0ead43fb82e0b723413f3f5b8ef93e6f510ebbbacc25d2f409ea437d42eb2fbe9cc7395d398f34e36ce30beb30c21c77d77e72a61b110cebafef2deda1edbab3f3b20ee3f0cfcf48465d544f735233d0bf592745c786ee4847d421996959579b2d3f23d94f097675d84c18b33ef3f1a47c7b1e957b29a72aef547e6922122ae3fe5097ea594462840feb82035187bf23ea8e24b93f5446c7069c4e8a7c71601fc4e0b61c23c6fc44ec2d2f192606857a0c40e00d433132209d42a8b68539ad30a110e307a6e69a30fc03bb35ca000408ed5a53d53b9a609cd2806dc7d972bdab75b5293aa2cd86e9aa38ba5a4d13a092d585e88ecc168f05d355bb36784f4c8184da90211fa872a067e09d4065872e0eb52534f6bc7d3239335957eb6a5d8d5a7cefad8d5a706b2321d485fef7e207007830ba6d1fcf6e92d7f38793bcbe3d6992d76f1be66a5faf25d32584ca98d365f3cd85ec3a3ae015de3807bb51f3333313c630ed8e203b0c7431143378c64c38f339861d34f5123fbe350bc33f62918c108788a492554ce2b091ccbe98442c129188486416767faaab7aa34c5ba711c64424f6c5aa52c9ea4df8d068740d5a44221689495657c5b5aec2da4b13be5c192c19a14af576c66946ddf879508e725da711de703b6328806ad09a06bd46c8ad41dbf03ace06f61bf3428fb41882fad35ec7e92d6bacb5d6fa5a5da98c889de616cf3955da832cd0d9260533b881ceb6cd16f9402bf485382c111e7ee52e6fe22f9f1de6b1bb9e735ba4be2a1a264949305f76e84685d95a2322b337e7c8a461e1d67f54580cb7eea3be36d2cb2985156edd457d8dafd68affd599718b5271ebaafa4a71eb7bca20dd8e29ff526f8bf040a72c91d116a9e4e30c6199799c133771156be528f72c1a98d8f57581306dcdc22eedea58d825dd552aad69b55d75dee5857b977f15c4e172fc30fee57f719377f1d5f3e0b4fd5d22c0bcfa077b9c217609989becce0ecc744ad9f2eb2d8dc7f6d81efb630aad8ff539b14a1c585333845de3286f93b03a3a791bb24126f02453aef225934c79caab9864aa9bae149d648a34c9d4c924535e5325e1cd6f947f1f8ac5a5502947853115fea5c220c8202727a5128120643653a918f5581c2b7b1546d020cf91111359579d5969fd2a49caa3bcf52dbee255f8f82d5ce555ee9f729477e1acb7beb13ccfcb57fe22f95b345bb85b875dfdd221857facab7acbb872e1e22b123b562222ac635d2a1ee7549248f537c9635989d792bb44241f38697d5bb168a7139611dda2c745982ed5671eef5857a531d193c41176c7ba44e212a61ea0b440aa51bc355146ea6bf3282a0964838276755799b94810504e5a2c234343f548ad7d0e523dba87b115fea176308b7cc57889ea4eb529256632d7be454720b2f138367cc6440735fce47966c699dbd4b0e15f0d1596ca0a6b5301d2a091031a2a3366243163858583cdbac1ce282ab0b1e658577dcdf88a90a7ceea4ea585b5c7063364910b0911914b080eca904103192d1ea4a1f119d0b8c03f28e0c63d4d4297fc7b30061be5e73f5e796c64687c4c0407fdcc8c584885c452ecea980dd3a5f2bcfcd24824da36bd9ed220eb9aa12c964ae54e922f2f3ce8188126a1b57f2be0c6f23c33afa0d12daaaeeae35f24b270afc03f97c805cf5e62660c0c8cca0ba20002ca76b32f30a17f5466cac4c4ace440f1f30313f362be90910963db46b794ad24774a9e09b273cb78e3b694bf482a5954491c2ae315428c0c8c09e3c58b6f05b8a53c8ee26745c6b944330bc92bc82bc8b2241967d2704fc8bc78cd17fe8101238c61287014d88ef8e773812cd8f9f10f50145054b28949a3e55c6bb82f511166c65b34d47530421cdd4e19f14c932ba04cbc684f0aa80c6021cda13934099d2582309b8981128315e37381fe73c77894ccf3729a4bf4030d02788498791a2668421d76b3c82c324d18db64c8a049c480dcbbfc041b035d24d5553d65bc4438c9aefe2ab3905dbd4e0c056bd2e01b355886769f11c6b2aba8b8cf30a6e235f5830171b6a3fc0cd63833c39d7076b3fe17c98c4c03ff6ce4cfbde2795ecef23c305f799e1947790c452531112c04c3c797733fc30363c4402bd6887f7cc441462a0c46914ad619a72d98cd696d7db739d86bebb23cafffc13f9708ffd00871b8ec5b34e42524c9172f4af0a2868c8c6312c8d88889717c91c4f8cae416b1c64b74772b852a39995e2a37f9b955b057d655292f8ca1300e6ea2085cc347b010eb87b5cb4b4481e02a492d112b648b709ba5331d9b14cc40446778e3bc5b7489be4bf4e5519097fa80167c53a55c91edba4d4ab7b14651c901fcbb0fef7ef32d07c0739ee3513cc663af447a70a0d3810d14c1812196982f1daf01d09cf498f121c70d0062b076ca2034c59115b77e7f260dccedcb2d0909ceb66ec47cc170eb474c18116e4b7a704060544989c5f40033e345109128a2c86325d243c6715a9cc75ede7b70713f91e13fb88de2037816ffc16d5c857a7675924e7d6d4095c4c17c6d5898e48e856d41659830ce05d355b999751da92f2e09d214f3c52931c9eac358ba9039c692f49bb17400907662907682483bdc1224249f500f8e97bdf9f759f07f001483df067e18fc403a852e5d27e52fe02a8f017f790b78cc57c05d9e020ef313f0192f016ff170d8974ec487c0617c06fcc5478046074f0f1e222c40819a142e9b47998110ac6a17f8d5110bfc8aa802bf4242815f154d400211a0b365f350dc32115f7b78ea4edda96158694cd06464c0ed79c879b3200e088c2a29317f9a647698674d32fb8cf7d992f524b36a9239b36e7eb3fc0b350eccd261ecc43f910508e6916b87e1a65d88c82d8adc8b8617f11ef00d3c10ef400fb121fe032b50b00bd1c8d21811c556498cf000901dbb4161fc20fcc5d730a2075ff91f8af81faee359cef2d669fec4657c8e01f1ed5970cc5b28580c626834eac9d19e472269adb5d6a5201322261a3b566343ec61553bb1accf09bdfd0ad12754539fd027643f21fb09d96f55db3e05f6520266c68bd40ad531ae0a632ffe513dc3671883f14f057edb8f3be569125aa432e3458511524d5ec6552da7e6d424c2aaa3c3623d9296cbac6ba846d42aaa24cc8c172dda6c7982668d941552222213d07d8524c461bfb52a0a71b8f6cbb8223a829e953c01537e01aef20cf0975780c77c02dce511e0307f009ff106f0992780affc031cf60e70221e040ee31be02fbe000a40c003be4573c0b776500d78132110bc4991140316b0225a215915ad92c80af026384ec804783a5bb27b1843f1eff1eb51f6f62828631904b7c21877e2ac8d7ffe0a06c80f20e30f1f431efa635d3f5428a892b44687546c3c521f1e4a53f140d5ce9b0a0bdc1eb572026e5fb2594a8a4a0ff22015f0c5f7e0307edb38ec6ff8caffe0332f848e1780b3fc0ea7f900b88c0fc267d070976779cc89ab7c8da7bc0d1fe23de01c78237c7bd3092b881d352936f01f70204f6d3af0948756263ef0b487ee30b133691581f0188ef239fc7df0ff0138909f364018f19ee302d81180208478d9349b5d3b4284a46895446682b3aabdf898c3789b1b0efb1f7ce5adcf3c10aee37738cbf7e0341f84cbf8139ff1341ce66bb88b0d7f7996abfcf694aff1ed4b7ee2b8879a14de42c15c6d9bac6a2ba255916c16e2c0db715c16c4b792b48a8880f856ed876fedcc161dcf5b28f8b48d254542952c92da352c1b398989cc04e769e3b88234da979d924282e30720881d3ef34200f1c38d771c00217604010449baab545a6f9c625dbc45188bf116618ebb5ffc9b2dc091bbd4a4f88c83a2a0e0f984a6f9169216918e123a52d454662a339599ca0c06115c8b1796af84b1ec2b610e16e0f842f88bafe130dec689f80ff8eb8170d8c77ce583f099dfe1453c10dfc0fff00e7c0fd7f127cef22ca7f9ec32debaccf7e0dc0360c6db7098af7197e73ce6b7bf3c0d1fe28d700ebc077c7b71d3707f96eb14d5e1871befd883546642282d4711a24891318c61d2ccd6501040b8ce0fa3ebdc185de747d7f1118fdb68f3011a9be857487e5554c924b11ef0c66dab16caafd8305d8eb07e4574f22b2446fcaac8036f2203f226b31f6f92d3e34d7456ac96e12d618cf39630c7dd32e1f6961350665036abc2cb92a295142b36ac8ec8f815110d8b8e21de44663233c931d1e9c09becb4c88cab1a48f3ad243ade71be559b2d756c1159579531b68a80709b65745925552ad7992d9507da13be956fd15a3ba79212ef743a9d4e27545b0b1b2e4e504e4e50505050ba1a2c2c8d56abd56ab54a7452b5129dbcc48e4a85e8b3f231f1619166618b27fde4a050a9542a9552d178d1c7ba54a8f03ccff3bcd5fe524ecd976635939ac99c30139f2e3cb89e2651c9ea3a2a747215c016365c984ca7d3e9742a6151e3a9cc88158da739ab90ceb6ff5cfce9a792556b4df2120b91583c29c96ca9ceed947088f92900811e18a8fdf0e17a06dcf8810042b058a2c2708654e776b8ecadf0c63a972d4a18db4eac6d85377674419233adf0e54587dd2d2dad3046fae7aa9d1ace345ad870018620188661283a0d576e42c5049f536dd264afdec81b79236fe48d3c3b9266a24f56edf875d9c8343ccff3bc8f858bf1e4d3a2e4c7d1b278f189d9529ddbb1b01a5e1d0b95a311f7c05535657baa42b2cf4363241d21d572126a2c73736418c32b58565452c6f716ca09cb64a5429542959c4c25921e95e080542b43af5604ae1ecaa0ea24b17599a683ee04f5cc4cdb34e80941a3c94db9596b7118dbb6bcd9bc6dd6da2025c7711cceb876ad8c2a80367c53a5b47706fbe25b6f252da594528adb706b507dd97a6d0e0edda9e40a726691d784131506187ab69d8d081b2d43cedde8cf467f72cfddb937efdc9b77eee5f1f0cdf8be982f2f560db21e0f2b210c7bfa159a34d7679e913f9e8f07e4616192d5bf206f080bf3e5a161dbc2981038a07b5e41d0bd200bf36a5e51253d34cc9757879a174451305d2e9830d5bd9a57545f54e6d561bea80926599b54a6a432a034a830aabf58d857033aeb5cd07d4a749f6c7633ccc92e95814e5d3061a057a72800f346c1cfdb5e94b02b45d954466554866f7e54845d6bbde3286b1957bb29dc22a27c87cd966108ffec8a31ae1e900b66a2fa720c873b6e5cc8337313c4406dce59e573afb5f7de7befbd4fcc28ecad17df99207cd4c2bc3a149cb5f77e37dfbb85bd6d26583c7e325ade72eeb6e59c73bed7d6ba6d9df781a1c86ddbb68921f8791d37da9e25e8d232b1e16bb1bd9636b79c849f9f273cc1094ec839df9b2f38da4637bba71d09b719b9e771dc10cf473f24fb0d1d671741bfe14d2ec540679fdbf32f8c79de0d6ff27867c57e3b2e77b9bb77cbf8861527613ab7e74867cbbdf65eaa8137bc014fd2daeab5cee9b40e413443eec64729b5b43ab6d4a9754ac780297da1e34b0c4f9acdadf5eb7534dc8b0dd27c8abf49ab98e7bef525867fd21ac45f87f3966d187bb130d5d7e9823d7fa09e2dd85516469a2eec1473fee56d04b7d2f994d2a75b0d2c7697baa913a3d3db1f8d55f5e3b073396fde03bdd51766593bf6b03863217c53a5c42a55ceb8b6a8635ae9a5597ce76cc10e45adf5ab5bb8486aad352b61669f99933067ad60c84e6424e426ecdd18e3cbc3e26b71deac1d92dd3ab67eb175ebd8ded891aee13ab56ef1d437e89b2ab44da7ff50b93a22f9f419359c109c564d275fe80b89e43535ebe89242b1a914a02829f9e9c4cd1f8d72aa53275fad1c643977e29e8bde72947794cd39a7f4f1e98f4c9c23a88cceb20f1db8fb7c1b4b931da0739bd3b134f9c61b706e1f9b7ae72a4a377034f9c69b3cce4a724e1d775e123d8bbe7db96d8e1d95b3e89be7b18403bd8d3f2eca270fd1f3c8e39dfb71f736feb85bc4efa5d3fb89a75a2828d4536349f7091027ee7d275f8e60dfd0f7bccd71e751df3ce5756399f24e94e58d2c4fb19c7bd6977aec54a71ca71cbb0936713af200e968727372eaa70df2007db5e3e42b5fad560eba2e7d6c9ac7cec7e7e8f83516ff8fbb3d107a53f7e734d471f295cff0e6f3d55882fe957587cdf43beadd865d857fcf0374130d731e79807e723cfeb81b1c5726ab9f5b45c95525f7c2580a7553b24ffe6d5b182bf193d7d492a7464c5fd26da247eaabb4dbe49c976a272fa9116ae444b44d7a9b3ec8a644164683689091faaad557e9c838931c512a79475492937d1d48a18882ca8ea04b2a851d545f4484a400723ca3eb9193d9914b7124dc66f4cdc191e2501997f243361f7f08c6d843f7c6f97d29069b6f1dde301e4f1bb6f8888cf1c867181b8d26379d73de8d2c20775f4a44a331d0477065b779e71ff7476070ee0c52a2fa2a3a815ba638f565c4068fd820f8754f659b9733498e232a59c5d0a9839ff8dd701efad78537174f12f4ae0b75d82238100c39ce7f705bf43a0211f42f8c851c47c3b10cd2028e338c7de3dd4ef788faba7753597d755e9de2749ddb178e8658a5865ec935b8a864f5ee535925695039939433c93c839679f526464357e8065da07b84fb83ea42710333e7e94bebf0b7f90fbdb5771e5a0a4349e16792aa6df342934e513901eda3702e11f374dddc02ed9fb667da5e6973f77a23689f94730a58da2a56f12679398e6329a1a6eb3aca745d1e8a360b9197bad0c31ec69b8771f6c08fca0011335bae73dec5f8f77d2468ffdb20c7a532b0add3166e9217ac607873b72dcd96eb52a262ba5cffde64b65c1605b6e53c1187e18dc719917318de5c16a0fd0e55eae7a68b074e97ebe18b221631de448cb3187e09b59aae3b4ed77516fb6e6ebabc385dae83afb75178e35d97eba3f0a68e5c78ba05b43c2807c3ad7b1ed5387bb1418d75ba6e89c443a742a150a77dafab50a51211e18dcbe701c9d914dedc6ddfe213cc4784cd6079529ebb9a274b79a8f22d3522d9d5672f36aab1435197eaf70ba9921476162f6f2391854c56e24c7a2bc9240fec427184f2296424e4d26eed5e9a8884bae04ba9177e5713bb24b85e8c71b771c8ed1c76bfd5beed7a3f9a265a0f95717faebd10187d57d42109bf23c25b077a39ef922d894d47e72eb143a78bb5de0c47e681244062822e09974b8ff7b5db74b958771deec2d829bb577abbbd0c96be6e3a8bf8b139cab161ba48315bf2d795906ecfa5f5e06c043dc3c9a07782ae0805327c37e9e73eade8ae7f2c54dbed3e505b2e447998249ab29872cb85a83f4d9775ef55266959d36549e7eeedec95c1cdc9c1567baad46889accbd66cedcaac2d2aba93dc406dff34c9cdb9574d72f3f05993dc20303b0f981e6f212859a7a207113ba223de1572677796afe6ec8fd6fedd2ec9d7157535d54f9b5c048dfaaed611d1b810db6b7dbadc7c7fda1041774788ee1036d2d5d5ba5a579ba1f00faec284a954e3fd29311876f5126b61d7715ad7cf9787dc9ffbc3c4fd1182a6dd9f1b050abb346a61282776f56b040a438d22129988e42e41441204110976170b2e639445bd0d02021a12a2b62b8fd89b53b2cbdd86b79cbb2de74cebde32c66e59ac8054e3152e0a050de69c6b2af63ccfd3da3f17307c9e949852f987b23d3bd6b5637d6895a4855e53bff0d35a77313d4ead7b26e71c776b1cfee1fcc3dfdadf5b282a59edcf143fb4fb6cb0ab776ed8d5bb5a258f10e10c04cf83781d5ec93d1a1c37ec54a35ec4977e18c39fab450b88fad0ce64d625b33a36c7bf857ad08605fe69318b162d5f6d68a88888a8e537965d803c68ce73f0537d05faf911b2aea04a06814e690ba8c1cfebb88db3d9516e760151df6d2f20eab9ebf75e8eb2bedfdbbed64ef2ba4b7873f7c8bf971953c5cf6b2afecf51bfd9d8d1f8b9a03edb7cd87616e5d8a7fdfac49dc24e183bce30764f4018d05641a31edb84cf837271acdd87471e944e7d83deb66dc326d020185e16681286e2b6e530f46f0644bdb5f92c75911b63e19879685b974751a70b575677fec140d4579bef37300259772bac3bf28395b65dcde2188681e98a8bd942c2bbdabb37bcb5f0b0a13010feb94736c73f2a5595412da7db320f2e5b56cd147544b3d6d56adb771b63ec3141942ad76d37effc78765ded72fefb7ad9116deed66e7c778ef78755bfef8dbece4d1bd46328aaab7a877faa903ac37e77e7f2658b519c2a85834439e09f4b13917cdf77fe72efd3a297979087123db7edf3a81cd3211838680a93ad082a4c0c08135a8fd98002a821a8942b590ba3d62944230000004000b315000018100a860463d1701ec6c1207314001379a05e624e9989c32c865190630821630808000000080060208900e8b725a57873c4c4886bba342115882659c47e2b26a328d7d90b29f989ea108343f931cc501291af150cc0c84cab2d261f704554a2178a54fd7e6450140962b6de94822266484574c29de4b54480c6cbf0e73d33a791577467d362d920daf114c7ef3fa78da125a60ca46e1a4c57e43d4fda75c79fe1cfea9bea52d8c117e5d142c77aae7d026088bc4109c4a7ed98906156a9e2639f277f26c46cf7d1a19f2055c6c085b24567c944c082ce016ebfa44b8c5253343219db62ae0163f266c872a9c604e976438f2217dec424588e854591cfd1f44739a119ac64f431a2155477a0531272dbc545df989dbe411363f714506a0d7c82fa8a20379c560ba40488dd75d4be622095224eb3cdfedda39b5b3ed5d6afdaf4d7fddbfe48df74384a8575391e45dfbe2a03dcad81633d56268749e45097a624608238a38acbedd018c75af687007e98da47bf520528d1ab77859c7106b2cd60433657f7dffdfe8dc60dbd87bce63ef0e673ff0dc8eeeb9951dbefadd96a0aad0c1bb77da05a3f824cb56f9bc90f1889c032a94460e42a1d1a982ac5908b9f47c1e4c756db4fce4f3836a4ce87dcbff9d1b14d7892f5a74a9eb918caae06394fd349eed36faa10a8ed24d39aa393bf494442e59a977518c51f388cb9a24b651f97c1640a350d010aa9a6492387ea82b1323fa9f7c35dc078e626e446391cc683c9d7af195c572d1f9e5af696c4f1f1551e867e7a21fe65eb252601462fecee1b50e642949626c2a1d5660a1db01ac264a973449d82f05a3e6c8ce03abd5af8e526cefedd9ad7fdb8d9df673d472b25c69ebf29fed3ba3781cf7d8c40df072f250cb7fd60eddfdf332817ca15bc7729ac0a0f112466852da5ceda9f42a206a6a6920b34d84a603558e94ab777b1ed4561f24e3eee2ced7b344c5e6dcf67ac09c415dc4bbf9ff7d643ca392ca1eea1bfb4d73c09f0b69d8d8e31c327715806fe6d4761d9de8e88184486ac1d8288190eeb5f808655ec1b9a8c8891823601ee9dcba51a7bac525351ccfacded5d3ba044bc884dd63a07064a9042e08ffbe120db37571263f2ba370346779846648b6d146e4d5f1d0e57ea691012bdee728d341c93bd3181610096439fd0b37eff99f8fd375170f9f38bc93e50a43a8ed9f6a18e7d0c807d659140b5a92350ae4d4ac623d1bf455c21c00ff8d402bccb8b3398ed73a01d1d394995e20b3ef5309a26ab35dfc8a8cd4cb702374b517c2fb039bffbe8a2a4f405074c8d750ac7a3e2fa796b9fe9a68ac7625dd503be4aab37ad50e322a2b73cd7cbd58c1401c6fdcf5f7a6c5f726ca919be670036d1f66dec59a9434f8b1155b2705713cfad4a0cef9a2cb6c6472808604a160fcd94367acfe2d6f55e8f6b834e08af15f53a11d690065099573266538a59ac73c7d7807f5b0af3404a06794c9fafbf7aa088ca749e11a04b8d6853c8643afe54f32eebb0869440a026cc26de8f856009fd50b50236557be33031b59d8e49d18b3d580d3fc5116aa1c61ee32746c62adddafeaa23f177f82e0bb99c35149a977257481801974a83f98b3a1a036c9101144404c11a51b8e0ce0bfb80e043bae5eec5df57235aa3bc768cced4c9f3d7fe124f498f0d7b3985aed42208c6ba66827d35cc08f081c5beb8584d76ca8e8cb4264787798bfc3b53873da0183c624d4047d1594158c39a39f3996457f98c7b5a566b1faca1853d3e1f7ff66d69502687f50b9eb7869f3802e0058d7d1fe258dc2973192f9cca9e7ff3653917a3e6ca36daa96a29a12572ba24d5ab9cf1c61209d41cb3300d4ca81c64c2d4c2b401803adbd45ecec2672043c7f536291db4182cbc3fdb2067c08ce179541914213038ebdb484225d8b568e0a6cf5d48fc628ced01e93680a94fb664d1228af2a11a8b5271362fe7dbe5496bc76b143c014aa9098210866ab7923f594b7b5ed1f8b0871887940ffd9bb523d11425867ca610f0ddc1cbc04ed5ab713e941c16df325c07ec8833d5fec2e2b2a3c052946015551b5e728b39a8582b7c58ce4827b28ec0e74773f628a0882b441fba320cce17cb0863bed03f599c5fc628da6e19ea023347d4095d0991f8c39d7542b907a5af95047d33bb64cffb1394c361512ab3c7afcdbdb0045db74c34cf6c6d6c9f2d6eee6db8ef9e5927838dd33944bec08f473bbff8c15cf6ebc6779867907cf09c1952b5690c2cfa39e0bf6fd64b5b4084cd55096bf8e202d5e86ec0364cd451821906ac6b6e3bb2970e36f51a35109d57207d5ac26d30def55e51569d6366ed4cbcd12501af1c4d2e88d029114f8b3fa3ee0ec8486b549440a417a0679bbb4e9864f1a1164bf41a55759ed051c7f2d8c1d18f65531232b2e960b4d29ca12cee7431980fd898cd85ccc8dbb76e68000494c50395c416176f32e3db3eb8f964fb2f9ba26bfeb4c6de0beb7f82c025a0e855275bf00eea2f88b6f5d9b4c5ac4eac83513411c403841a78c79fb4837be2ea447e7b5f50245690bb9eda8d672392d46f387b0551fc1c3782283e073748aa6c51da5f6acb29bf04670e3010dba9a69dd27a2924db0d6836731f71fd9f9e420e8aa9cb29362ef3bd4b93d34bb9e6b2e344a28b58347c39c02ea846162f134f1ce3583c13e18797231e1f6d4eb5b09d198fcd322a503e1afc06ae0689309953b978f564c49c1d3929441e4a447900d7f929adde0912b189a1e14094d30337ec2344b1ad6762046d325733a7b8ec3aa217581c739c39b423e591b557780e0668ceeea29af392fea211cd354765227a14b5d87954e6437ad41caf3e5e7d90bc9ba8f9c5d7057df8887aa4fe5d3432166aab4da54745b21e0583e885c7d4a3f0077b54168349503422d261e8e02a10f366148d0ab447495ece9939c0e11861f01160e112d2915fa84044e57d7491290136bb6a3b82757377a05fdecc97ab468566cba26495dab83ced143d6a2940621a4904ee5489e6f78d4776722cb5a35be41e0c64297113b1910e07c8f2843ade8ac5a25fc6ad1a1d848a9bac8ba6bb33b9f9f1d1ce17904a6bc76161825f0f20b1d4ac513184db52922bf48f44310a6e48c53c0173cd908322a9ee28ce5406b361a42b1b9527059f918dc75594ce192217fae4a59a97ea806801b89dc34bb5f92391703d842c15e8a08bbc9747ffec9b50ab698fec8edee091f00191dc07ecd1a793a0de93f7ddac2a7a252166373cef456b62158e8550f6f046eae243a9820329652f5438c09b7f28b4960de964c0ff56129499a47d38c72d566ed860612f6ed2523907e9af4b78b30a9a401ef33692e2c11ec3cc553fe2ab5ed92bbe4755c63ca0db33acecffea3b814321896b466336ca7c007cfe32362ac2cb6c5f7c39f16d00d25184847cdee79d6c606935ce239609c36c6916bbaf4c1b5e33cafd8a026db801672f92a9541b252a4a771689b03ed7b1d1a2616c888aaa45134e844c9e68ce828472610ea55a24a504430ed4bf45aba44379bb1c668a747a6ab49ce847e12a2671337032e876ae0a659d42731121b19a04a7f54875a025e18d9ccb38e93f8581e3210096176969b945f40128e4917a5c7a116ea1e9e30f4fcd181a3b5f7404d252e595ec3c8c7d5549ef024900236f369dbfb04e1fdbb0a60184c1a8be70f96f073c7bbf3af643cb859ccffae897c3f1192297a7d52bc4c85b69a91a0b48eb00ec5656f1c4fd7eeb08f8264feb4538ce220640dcbe68b620d318b12875ba58ef6772bac05b7d22a5e4f600f605725ede62df00db6494a923bf0c923191fc9a89aa8e4c0a88522f4fe44bdf35512e4fd1a7261a1f447a29d8f3dd72f5f59f68c075489543601aad20a1c7b851797b368895784e2b7c532e0c4db7d4a8242359419dac3577d804aa5f698fa18c7c7b1fc7553a197a6fa838b689f426123d1b011fe8702093248cb2d498a44acf98648d8dfc432693c8280f5a6a0e2970ed7d330984b69e0ac621f1e9a821d2ec12b502d47befa8076be5e2a7664cb03f65fda0aaabe94c20fa72b349f0fd01d619de03044a9947b64d820b8bfbae482a48827763a0c3973646c44d5e7d207662247424f5272753a807eadd47ca81ecf3b0b7d24790a36218f4a46009fe2135cf592105f84d32ba1d9f8d27a94f3c7024aac1e91a8d450389f0085cf5fe49dcbcd66ae74113dcdf6515108b206714394e586ccc594e7ac6ae9d65e6c54aee6acf332204e99983f21265fa87b042b33675c0c04b4e776cfb56d7f2cec04b6d577eb764a8608e3f5510b9654d625c71c9440c222675c1080afd09f97a542bbca80f7b4a9f9b9cf1c86fa0410ed44b7d6f6fd9fb2ed35a9f25c3541a82f9396f7170e62d52ca5baa04fd40d558374e2296520093bb6654d396c394b778d623578ee7d5cd7fa85388fc1e862abdce7e2a5cd8043e0070b464521473f6d67ebc50420ae91acfd217642be1c63cd212251d1072995538f4bc0e033ee305b2875bba2ab0307576a7db44a4fce5b2720ed0292dd9e836e0a8ed172cc026328467c13374534d4d066710b8c912f9484b964e3d6dba0512843c8b6f827dc7de454e34426f169596ccf7325a2880a6828377758d8a1b89aa27b8ca85960cc94ee20bd837e11c0b7c6fa4367866d42572e1dc17b5b7c600c8237607df58f2f12664c10ec8b2b3aaa077d2222685bcdcf2654a61fe7a91a8fdc4cdd640040122a8a435f612c00583188e2002f7a5d5417c9a72bafbc49aa413b627a036c35f9e2508c69aae2b0c44c834d5a826a6e9f9500523142095a95bb4a1254da8bfdf85d308243b4cc88f80b78b84964ce5fbb40f4f9348c6ea75f484940d455ab2ad123288a4cb9b88dfda689bf8d23778454ba672c8084dcbf8a42de34e2645327b4cc06499d79241cd13d7012f5d2e73b06a475515ecaeb93839558dfe08f6679c32c1cf8e5b6e282f01935fb72e1b961e5b329794e51777bd6c05a1449d45cf265a618525d5347e87936c0bd897c6c325140363eecb8251895f249881e2a935e3b36c66fdc8c6ec203c3ea2969c7c04a183905edf01b0cc81d82860361065e9c2e9c20bf293b013f498843de9319651a4721973e0032ac7c10c4483648786190820e6c86f9df930b67f0850e8d85586ac407444b02fa1c035964133ff9655ffdbc85b33ccb1bb5178501a8153a559ab24b4bbc15ae59e010c072e36cf62f226b370055335678cb8ce320da2715a0e9e349cb200853547e94edc267b89a6d7df86ec20eee350c6590246f8e5854901b901dc46161eafe93498c5b3f88f11a784da105f7a1990b823fd467a3e1e79f9fadd7586c9e4ef3cd18eee4827daca13d16a0db82db72d3c1463240556b0a1ad29a36531ee790e05ce7ef67c969468705777437f67794b021584418d46b1862f45ab676df15507049d2489bef261608504431294702c6b0244b6a5219df4d5aad45b1ac2e00dbc5bd743818152c88cc0f0a251ec1ecc12dd76a28ab9acce590848fcb3763079b3682a104ced29da98f098eab39629894613217a2b83bce2b3d67d3f082f00c7304a7b7f661c404bb8146021495531e0e403373ba22204d0a4bcc837dd544884b7562f1441b25693a4c0483ff0c8dd26e6cac74e0ecd26f58207b99062c8b2492f0210dacd5f976be0c429baa08a9dcb4448f4e9e74becf57eda7e45654515b4851717b6fc847753c1d65d87294c05127bc4fd4dee5a3a5ca5901bc72c057cea6f50b109f5c5edd1dfb8995b418cf8b62f56940154249ee52e928565ed18e5805549f1284945c73c05fce2959daa823cf09b7cb6281d8025e89e9f63184a280561163e58e19a226876d54e8786218e364c5399f543d9de66e30ec6096c51844b64bd25ec2173c20f1c6148546193bd5c4b612e56832978a92b112d0d2698474c810d75066bbc22466f4e1b02c64d046d4018baf3c4b97e13a1b063e1b52fc6974e04601140dbf68084c304e70bdc5c34487761d0fe60c53dd164c97abdcb42978c1c4b118ffe0d87d52538d9b3660eb1b20d3ff439a207f42c6d6944017fa42513ed0aed2fcb2833b27ac36ff105acf38afb61bd2ee55bc86f6bd01a3db7bde50cfc95b1a7c5d869f65293431ea5a4ee400c374414547caa085a1a73ea1ed58fd425c29addc1b2d0851eab7eeff217d04418cc7f8b5f04bb5a0aa8bb83604e2918e2a20b78b103f50fd23838d084894d2872c812898a61553e3bbfcc305fe32db49d5b326525c231ce687e8fbd9352aee08c5869b63a7b64028cd71aea8c692a2a866224e3466f30863a51485322853c31c89a7f8f65360fd9a38510da9139f1c3a2c3aef2c1ab292a5d11a65e48755eb6061e049f04f2dd630fdb7d658936a576f1fce6502dc1ccb0a072b4077139cc3ebd8e99ec8b951587c65aa109c46bc8b62db878913308eeeac5f8899a886e5a37c904fe2e75110e88ec9b4cb88a2c429a86c8f5587c7d5a7f10ae92b1ed45c7a6f0ac85f492a44ee508593647b8c634cd2a01fbf314b330ab9315be92a858cf041e1db8938802836f9022290361404407a77b08096ae73837712515b36dbb756e2bb8b5f5fc45460e0f0cf0dd647d998ebf5e5535c19984fd76b32d2479eaebc102c471e98bc894b4e9260615a0481941fabecc6ab72601a943fd60855b995218c3c63622ad309c387f08e8e7aea9cf3861337242c534ee50a631fa675b8e7d97f6e21e8b31a3c51728234ca7ea715c2a2dd15199b4909f6503be11d8fef899740f6942c3383465c620c2f4e463cb256104957fd80837da23185e60216a492aef66fcb237659f006b1f5bc16c85d75e9ff4b457a0b7d8052f94b6d0d7bdd4894d67f03898b9116ee67060de33a253ef247ec41c18dec12cafd763146e961f35f070418e4e958068743cfe32e1a65b3ea73a40ff5f50e05cf71240067cf81c8e532d7f7f7c93e21297ce1171cf4a83d30415dbfcfab7709c42da6fa995c2cca900425a2534a1df31408129149785e581f54addecc34c7478bc5cdeadb8b18e15670a308ab05550fafdaa00efc4ec097b5052b1bf8109f6da3bc0f051b41991a06e372ce758aff33aba382fac0eac225aba9f51a43534652766c26f18f9c9fc0971395b884eff561d9300055a637a6226cebac2a1384398970c44c792a7e7bb789b935955237041ac79b1ff6206d1be026cd70088fed4d57edc9de316d72c21011e49d6685fc68c475848587fc15960f83c0a85d2c971db7d841d87e18778522eb54e223310e40e2fd957700e8451d33a72928a188899790ee1127f6351661cf44eed6c4d6c381aba1907ee9a351b3e1595dbacceac4e2a05a976c7a85658bf7bd514c668a66850d7c94eb2b612acf5fba4763192172daed2ed52c0c6127507f40ca9b4c6c49836c04fbc48c11b14f9ed75276d3d8f76d5c404cab925f55720a6781cba17f7ef6414169151b37fe0c35960b42d101daad27c2e0db599de84e08cc6cfc505f9ccc5144a460f53e476302447cd4e585ee6646af6eb5ac6895b8cf1baab6719a12f5bc10890dff2f54ccdc2e3c97016f50a80c84ccdf8b13efc20d5ec835fbdfbc1cedfc28df6b2dacc9a47975270d555a36396e01ec6a0302c03686b26f19f9120b4a9e80bdcbddd2724227e2d54cf2d4258bc84f21e38e9148a4139d6e3ee9cd83e961c225767b410524d9565d53529354c33ed56eeb8460450158c0f1493839d6714136d190130021f03ba3aa198a352bfb23563afbd085291162fdab1a11e08df1746c1642fb0d46a196af98e3a738cf87cd6f4131638daad1a7c0313698301c1d56405710655cd1dd7c03ce908d70e92cdf3c180f36606df3180131c1ec5dd24da022f232931be2c54b8f89a786efe31effcba28620c5350e84dca343240469ca0b1191c8466921c54cec04e249bd5c204f27b8490c10867116fe5d2c2429c32daac66db2c3fc1a53cc767d32994a0f2b9e65311ea9b3aecd36b6b72f2c5e59e28614e3c5cbdb0dc235d83baf7733d400d9dbfd147f7aecbc4fe24cba699e12bbb85be18826ee672d203b20c34eb80cc10ba129f50f9ca6a1d47ee2bc1e408981599f7074da29b3dcbe111e15801bf741a724524f7a39b4d7a455fef1d3c9244f9d0cd2e45b78dc35ff430ba99d5cb034c63de22d0bae3caf8675d582516850674338f332b172c6e4291335ebf0ae256b3e1de6c6ab724fcecb7c5760e0704ceabcb30f8481b4753de0b7ba1ed5eb74e3bb6bb44eeaec0ae1579b1a371684093b05e7691862005eac25486aadf6cf4e8738a3d9faa978bc884eb37832f6d49ff1d3c008713d9d32e966cb9d1fd823d4fb754fc8b2c4c20ed0bd2efc6e10ac0bc94c4f019064839477667d664ae68cb70adbffbbc37a541559bcd5ce7f4b68f9cb142ed368930586377b4e70ee345766751978533968deb94851e5be81ff3264340e6396f9fbf6e5bc75540936abdf914dc0ea020f5b3f0d657159bdd3926df657539d03489f02d512b0f5c8f60d7998d0cb745bd7f096d5ecca93714e347851f98eb1d7c76b683da190b6fc69b485a7d794523b08dcece36d812656d6a67f21d0bf917628430a815e9000008f9c78539cc3055f80d4960b34e446e7c5037d9d4b2d930b891dc3d7bee0610299eb6cb2452233fc2963cef233547b5b4a28514bd7091d6153526efb118eb4ee0a9f4daeb9c1f1bc6858582fb92ccc8118b1567044e48d0083b1cc75e8d24cbcf687aa3afd6cbda9d6e177c4a374353a455ef5638030469de2d1ff332674aa6cfe2e5dee6b575b13ed6eb5f4c054789be7385c604dfade01ce6e040e18b5fc4488fcadb362cbefc89ad37aeb24a13b9a850f5e3b802406ebb43e0c4ccf0369584e047ab38bedc9b2c1355c1f1990251f1dc9ed662cdca6bdaad361a2f48169e008affcf8a982c1e88550bae36a51f6b8464884a61090ee78b21c5495d9621380dd25415e114693149eca6c1e5d1d79123cfa365935f6211ec4966545757c9d150fbae64c0b85887119c783856df1445ab01b149db8a3f4c3b2be75bd0708dd7c6acdc3a14449e5549e40569db5280bb35ca1de35aa2388f47205a2c03c021e95249e8fb61bdcdfc1a87412ac1642dba4b2c86ea7cfd622cb84ed302980d9ee061a925a6c54bfa047ca4929b7654f12c665e4d4a4db7852ef7322b1c4b253c0d5cc6af8a0d5a1833d0540851f35dae762a5f6e610c155e4756ee4f59c6589075b905265cab56f94ede6fffba691eeca41fc8c792e1c35a94a7a8101bbae9341c66740b8cf8a6a84fd7031ae16840c8df5d3ea64b746a55a2ef9ac9761bb2e3fa855ae0428ccc27c66a40e20b9bb5781aac7e67d9b75ce4f4a2d9f95680f9b421c563f05a3cf39eb6a1e3bb26dfd30a94eb7636820b4ea1d4873a36d370de375fe65991f4afb517ef1967d37b1aa92523f92de1e9a1fae00e810a691eb55cac8d9e2fd33f0e85db53fe4fe3348f92c0831505d3e8c82092bb9f9cba5ab0498759449be6f1a77dbbf8f163c524f36455677fd7bb04643732ebc0dd934129c50320feb4ef53ee026deb15ecbde252c89c78c479264a1f07e97ad16989bb14cc3b8b0faf7df44890a04a79e1870f4ee111787787e51c5000b10b12dd6ff12d5438072a17b66d771812bdbaeb7b7c4011f24dd2992451f42c4cdd4bb20bc3f72d08ac528068b7a1a0eac9743cbef6519223064a5deaf2789acaac87d3887db22112c3fef9d2008d14285e01ab192934551998dc1ff6be36322a93bbf53c7cda54c01b60eff77194a921bc3d0682a6d215938243469f0508a3ba0f455c7f79f58756ca948702cb9ca5b7f2dc5814e679c3fe020599e7872fa20e58db2950811a6ea29e8d27a3dfcbe0c595a69dd9f39251b3d4d94b8393b4c51d138474579af28000a84fa935851da890023b389a9091a94b8da7c13e5867f8cc69ed781aeea6339f76c88feab8c7d35e82bff19d20110a3d30c6d3e6ae50369b734e399ee6d5da1f41c4f0e5b02f49a3616df18108da3f71722c5d1ab874d9c388f5c564d8865fe03a354f93020fd62908728dd4a5ff338f7fd316a489de0b447eb6d1be547a9a82284373113febdd7f4a94fb31678e58359b1b2daa86bca7ad899da22266cf3dfd52ffdf5869930962b7d7947a8c86d211346778d8c06743b5952bfdf9e403080995ef69be8b1c7d0437f9cfc3dab4936732a9453eadd2b84455e0d45db953355a867d8fc247c52d747ad13ce7fb219fb63dfc2bf104e530047c47e1f8eec9b6e3c3a68360d67146889f96c5dd5bcaf94460bb40425681d581cafbfe5ee87cf7828917e1c758870f632df3be9b8d5479e2b1c7f20b392286c0eabc0419363bfae6647718d2ffac0c4ffbd336b8bcd79ee8bd8c8692a2d98f11a048613e97c54e1ade3a5a26b04a574bf3da0e5c335c1c6a60e3c62b0e49ce88de4f6c995c1903ce1f648d691cb0798fba49ebe16a95f0854b7f940366a2764699b84d9e878d65f10d67282fad98152559a36956ad7802f0e093a3d454d90dfe0108b41d54bc8183c66a14e3d7d486d71155c1272f2c8781d2a5525292156f6093809794ab44fa49cc087ad0c8d4fb5ad857a1daf09d6f8a0ae35a7a6e6fd28307e84ffbdbb0d6dfd205b85efa5b60804d152ed09ad54aa7576fa7c89beef36e16094d1a5324c4632c34a7fd49413084501bebd73d829abe3270b0c85c03327261314abbca0e23b6f1ba96a37db5e80575731405f9473ff0d86da4be005dd7e7283600b4c8dd48b76c62672e0663a3d8ac7e26867b150d36f086c9438d3e3e6cf9b2aeb997616a5eb013e4856c7577800395d9821f13abd075c0624491b1b07a9da32e8c4ec73680eea6a3861cb2a2c31e013ec6ff245764eac7bd588c2c2115614d8c7c344454dff32b44342202ccfd1d8a9e9cc5c99aadc2edda91d36027165c16697e1918139eed1e92dec63b29c5fa9bff0785b5a0736b057e98b75e5050103a54a50bff03bb74cf79c6ec81a37189be82f4431f3f11021862fdc8543f1b6066bf54564915bcc2c811e8a43de8a82e9def5937c28892c66d46c990470f31eb86391ac40fe7e83b706095c83ed1257899a66e6d1908991725a32b6db123b559d719b7cc60dcc01d39b6c8755f57c63178e1fadacef0e22843d4bf1eb2364fdd9e7150d04bd5ed4b8e4f3a7d8515400f778d0d638c4f0c5e88668afb23b49bd723531f031d3cc3564665b70ca005a20dbab6c7bf179a6b18db95b7ca377705c3054417b085ccad37cdf29174aa3bd64f246db42554654051a3fdf6fa62b9105b284226d15862e4f2a273b96a40556f372599badcf1a9fb35a876eda84f1d9edb1818999dda03fe1863cd0796dd78db445e584dad7287971ef865bcfd8526bd36e8b4ac93174c8842e0cdebd8a77fd5947607d6c305f3a58019e81aace9326a58c5fea9eff5d35e89faf239f7dd334bbf5e29119e321017bac64c7c8cbefd26bb2a45e13d8f6fb8345408dc266a62b4225588bd2fc0e9e68faae163caf00fe2b8e478108bc262f4d4567300d8a3c35063af53af1826b7c187790dd5856fa702de9d40c0e073b0286ab10fa2bc7b7c5f826bae9a66403dfc67bda185d591c340a8a2ff5536a6d4aab9db075bf316013a73ffb824f9f2574f1094df292162059a7b16b8308e929ce0b307509a008eba572b106159e5c5edb61449cf4d88838e2a13b561fb46ac8d3145d92e0584cf1f02a26091c5de12cfaf989a96944e2c748b902727d80de3959c24c75e9b35258abc139ca45d3c574d7bb8db6df9b6290936d94a6f85fc15d39fb49977c4ba5edeb40b0d0dcd1f549cdee1488f516b05c8c33686f43d32e8571df83257450237cc532e5806d31b0423b5c440f0cb25a30099d96b59f02253825278619d4e0a5243f238af7347d9f936d3044e6aa1a02076b11d8f636198ab95aa09a68cc950e6b9bd511f2645b3de8e887bc32d8550b31e42e38c18ed52ea1c54ffc663fee564d668fffd9fe206f609e061488e20c3467a6fbd012f3385c6a2cd77d5c271e97d2b6b8aa7e85551d1b3aeb9de2e864b07c62e5362ba0bb4d6aea729aaeb3a897537005ab489ee2922ca6dddfc59b3b4fbb49b17542bdd64d74a6303fa99fe2f6c99163038ee1d94ee36c769ea45de5d759ef1f096c9fa8a4f745617bfa821260437121be264d26a65594dab5fe3c27a5f4d4644655f2ed89e8dc7ae2788ab9c13523587172451cfe2fae9ab1d040be18a251c043f7428e38286c2fa0ba2f36e54bf7b17aff9af64acd75e219cea46d1b59e5f8c3b70870873e69ccb70ed0699874da707139d8bedd1dbd700c04f85734483acb5c1f94110af9824698663bd1a880ea5f49b67949c91d80bd74c20f483c5f99b9d4a6d1a08337a6b833c9ca87b8909e4ea03a286e2d0fa53da0f1228ae98b2818e585f3e8570dcf67939ad57a94d04f5000e1a44cf3013633924df6e908f3b0c1166384c411eee50362c2e94fd691a8a9518be9afeea32b4d9d67fe1e33fd859d5937e82769cceae5613ca4f74d9f7763fa271e199b8fa736b68a687a41286fa54d0904f6157844f6666879271a1ad536eb6a8009f8c177b3252682c2e2b4ce9269f8ca7ae41573b2fc0448e6bd4520e144c48eec75ac28595835d24a768ffb1fcdbc491502df0cefe1091ca460ab40d5e891d3406685f8e39d1eebdf109cc88501015e9eff8066f6737a4c27f26c700273ba8610fcb6940db0169367b0f714656dadfef632172ea8c94b3ad7dab1ceb1364cc4e108ea8efdb5da343deffba442324386195cac755d82a7b1af71f8e9b6de1469da42aa5f161cacddba08c4bc5f675b5a48b1f01896875fc538d73f61d0cbc718942e462b4b04e2837e8b857db74db0846cf491ed16aac27a8c832ff5d060daaeaf1b60e2c4f4c887c66781634e9f840eadf2fe07b7248a39008fd13ebbb4f7ebbc4eb6771e75e51912fe31718807ce14bf0e1d5fc32d0b44afc1195fb003e3cae43cba5eb7082826ec708be1231952547bd9713e7fa4a141a360fd28164097d9475be19830f148439eea23cff0fb87af390884896d021e8bca12774859b612c34ef7715f0c8a0ab7295190587129842081c202c6dfbd707a15c2c622293123431d1e82b1899c487c70a23b4f5b82b51e29889d23cd9ca3cee6c57c32a39d0716844720dccbd5c709198bedae4e26659430087bc1a84e9a8d24d98a7b199644a145137f5a08913bb9632e637a29b450983ddf675f428566664664d8587f7cdf454fa17003694584430cae9d11fb8763b09a5353ec15420debd4fbdd865bbf32a9a8dfc5484514ad46d012904024a474baf5b953b46d98d716ad3251908d2af136bb73d8b7ce69398ad4cd46e6ec03d1c1f468e2f53e95233d4408b0446861690a27a5916f81033f110ec86e46129f836d22406a25d38841a685c445f0355f3b414b35fd24b968e059b25db076c3b42298989268cc48e3c434cd96f83253630d5eee09c5d1ff02f673b5339d00b6a1b1f775e5cd2862394f9d77d98c946933409db976d07e35374a4f84cfd9611c3ddf26c0c87b1cac136b2fc42982b67918868e3162289bfcec0b9103fc5a0b3d5e21bd017e4020b077a03541831e478c1a491788e54395d665086fdf99cd2980f07e66813161ff19b2db84fb3dd5434d4954ba6fd6a01d48497317bb1e5997a2f39b128a22dddae46fbb10087a996eb7a84d60c9deafa8539c3a5b889d661882125c8a7bbc8036adff147677e095577b963018f53d1e334d8ed2391d83684a0730f460efba2b37d9bf7a2e46b643e26eb9779a3a5929bccbc5903627cfc9a8d45436e3438871b90a2bbe205df96f65a5ef820852c3ef31de9cdabb7ed12b0a81ff2f2812e41aea340811d082e7ba086f87ee74e9cd34cb905b720b267486b6a1d33fe9300a9c566d2b1ab409e05ad014c7e64af3fbde8d614fbadded916fad91c93f649816fac91d0d8b0488c7d381986a570b476b17588fdd12f9c8ae50180416db19b6cd7407520c209e40e5209df4c6bcc51503366ab61592030fd77deaf3db847c580283e82c8e6fb6e287768361a8fe6c570a5a8d2ac0157dc827c6873cdbddc484c5823665781351070cb23520bd1f35b720244d2368ea0ef966d48d15eed7bcb301c4d2288056c348b11d52b8d7e2746d497409f4a10b18c1c68904ce23855746802d373e581e3999af80e5941f51ef3edff2ba1dbd24d632df0c4882e66d63793328ff672a70c1075eb617ccd4b4302d8c770d7d14fc3b242d3888f0369947689aa0bdd5ed7656c6211bc041c99935c8c2d0248bd6b7e9e55ae1a906011158d1101899c81f4073c2314311458905a68edd364d5a95dd44ecbf2a730a28dd309011b6e538a890690a0dce6af86cff445f4f25be4bde20e415f224961ddc705abd4c9751114bf2eb1734ec09a5573bccd15d175f90deb48b0118f2a4042b8ab382ad75cf3bc050ad7257d1b1a72b61d2909f77f56cf7e5d2101d7769ddf8ba559f4973110a4ba7b188a0cbc7d47ff2c9b16883de25105a846817ed5d104a6ba58fc4dfa615cacc12b174756cbc81dd1cf3a930064b300ea30ccebc75651d42194585336beea28b3b8a007405f75d6d8afb1d781187a939f75ee619e09ceeffe33b922be4657777000bf345fb40fd64ab033385b39dbe574cc1e2b11b411d587ad2cbed5b2ee20927957f4d0c5b72912612dfd7252bb18c6d9aec00f6e4b8058a1e7bdb6e78dbec1b0b56ef14bbd54b707614a57547564de5c487319a5c7d022df40bcc745c83ffbbef3b4d74f420d337cc135b6528027fdaa7f5396217acaf98d6521f26002c3d82ce0125fdd83b0fcaa7ca887be105d4220bddb9c6feef6fe95ddf47a6e621ca6c7f6e6dbdddebf52377de7f69cf9ee8b37e04ec555578d8105f74bdc2de3250326efd08cb0d3eb5bd939dd9c09f6bc56c9dba10f8206c1e838762b60bbefe14425a2d8d7963193155f7bbab862fb1b66c7d576d58ec311b9ed5c64fbd106a009f7c2c0e00dd4a4262ad068f070ece40154abdf79b6c33760dddf29de892e78f0c0cf89122fe5f8e5f330e6cc3f767572d88b818f1b37e1b09873cdcce7775703274f4532d465b731a3b050f891f89708126ebeed853b3f8e1245c317b13f3c44de3442a204de861b2743074d4bfce272007ea4006654f7a80ff7663fe545f4d049946ca862997808f6ec7a79baf325a9d454fe076553c7747a7311d7f57a1efe817bb556c664eeb1806a6db3b3fc0b77901e58e9ca4b32c4d697e3f8b1997221363a16f6b73c3635fa32676268444a407074d770cbf3bf245fbd18a8a97eb3b2e0f8b487563be6dadb482f0e42ed18159e87d26b6069185bb86f0e41f43fd14006d082c712d62fa30446afe5081718fd9423b2ac1a7d09264a07c4e6ae3f555e8920043f65fb9f747d0860a7169704efac6caaa73166e3bb3395c2ed2660aec8e5ffcae4911a3e2485f467f5c2f5abd8e14a0cd6edf84a9ed85c83a56a8e0e6acce832d08e487103417a8da326c6c894ed7cf6a7c7e8828228b9873d324e7105b29d6fcb58c6b6f55a2d5b0b5fd4610bd09dc4afc628ae487905b6f1399afd1312f77c7db5a7fe0d1f587b9350438618b89a570bc48081f23aa6479cebe1eb98c92a7bb7c26696076ead07f0bcb3ec3ec1faaa2e3ea1708c9a9515ababf102d8b05b6113aed60059116e3b6ab5ae33d044626c4e625ac0aa7fd9c96ff0a1dc50fb525965fa287538ff8e489f89eea0717fb28f09ac01070a03981bd56b341d2d696548da4c74506eef259d6161f0177a1543d9515b269e82258aee81cf88691445d439daef8a32b045d65ae6e9ac9bff92ff98b2dabf2b60e6c00d52fc2e1e44ce67099df26e3a88f1ed2a4de692187882a563083f3ad34506d2ffe403a4dac660b4c4fd0cf1e43bb69a7e013111bb0f935c209dbf2381c5b06628de24d84d294d02ddba96a6c3d523175dd7d1ff2ee9ca2cfd2c4db7e6e9ef6aa2517705f618fd26d19387ef95c45ecc0acbd9d890095627b14f6f5ea0fb893d7616eab7d7a72f47ce95567bf0a845d2406827be22943e35cdcd6c7efb8de9465ca59f529de24e4ebf82206d8f620c0b9ad814013067ba7ae1ba29b2c961905f868a8f880639308d675abed719af4310f1ea5912e32596fe99bdd21ed45a89aa4dd51484a9640d68e2d029c5d3d024d18ede06b404df46e986561511cc1cc033bccfcfff42be851c7a55fc9f2644afa63133d8b356ecf29cfafa3b7031f44a808568ddc4d2cbff8bdc33f161a0b7eab7262ca347da60db4c4d3f4fa59cdecacc81f74a37922f030e04d125bbb515088f6ece051eec915168dbc5d658caf86f73163024b35da1233fb742a8d1e99c883590f796a1e3db5dedc2f3538c2056d314db742547d7241531ec5ed2bc422c922420b69b49c68c0ca025cb022a202a1d07d74e7defe41ed07b3df12dfa27bbe50d6a64d343fd6c6151d8359fe451803bc909e0090fce265d56f91f1d8e56818d6bfa5f8b4039cdb017a338829c73a3585bb9f331204f65d9b256a852c22df95f045309faa0f9fd90ebb358f6f2a3a4442dd85c78b582c609ae23dd2509cf71b88741fce19350c1f24c76b5af81130e68bef271aa2d98063c154c4c77c6c76aec8c00e1d4e97373141c3ad17ec675624ed971510a17cecc2f9213b345170c3beb5e43d31e00019ff7cbbf068b2207dbdcaf0e39174429df8c61922592f543750b888cdbd5758228ccc5606b7829ac93a4272aff0f923db3e6a7463a6a8273a97a3e21c2512939816ed0fc7276845f00d2bcf55fd657f510ea240adf1353372a1b83e8602c5d2f12d021e7a68ce12e80e01b65d50841e4bf2473157c58f441db197a915927f79767aa95e4e7e8ae2c9aeee7a6dafc3d962fee5b05eac90e4905fea36a245f47525c4472ac46ab57b7f1aec75e43fd1903c964eee33aa069c23b51c326455b966d5c3104bcae5b13e6632d685b87551171e1400db7ab12fbc6d231999d6bd6ac5a67a20005699479fe744db024c4603d0f6183a1dfcad0968df0d407d3a0df2544761c2006709cd3123fe21a449380fe972a116862cfd72aa8e58ab9c811a95536a99a748200dbfdb584741d0029415130e69cbcc6d099b8c47b86898256d7532543d774c61a7dcac6eeb037458832b4eb248a30e66aeeab0038a4766fae4aab583148f2f94d3a91af9be44548e8564b80822d2260224fe9a6776690be08930763c9ee738b469fa57a3ec5e929fa3a84c4fac245f232827539aacb1ea5d04d1059d466612242dc583947019917a52c0d45eb0f0abcf393ca05e893db24d7a6558392ea4e89139c2761c88093f7c9c443f4501b49755a029ee00c396322ef0485fe66466856052ef3d822a42e4e728d5cc71a73194317d3072785c604d0b8fcfdae148dc4beff03b1c9c1a44198fb73506cab82a6df2d96cde72cb3dbcfe18b29b71c5c474d7e89b20cb8003ec7e92921a8236d8a70fd84a7f46a095858221718ae5452e435907e6108c093baf4608161e931eaa4e127aaffbd63943b8fbf7906dd5fee9fffa85f46fea3157fe226c4eeadf41592e961f73917b401cd7920fcaada7eb3bf1bfbc6b26617dcfad13fb563a690046577a2398b1d0d728ac8a71644c84942d244189cee18f92c002317ee207599f5a1d6a0aefba0dfe6b971e1b3600f7bd2c217df0c48c1dc8fdaea1609e7bc410b86870aaa3785c19f39989047a8ef673cdf84a2b6724cae03b38c95d222a8a1b1d1f9ed80a117b34abfe8c9a6947ef676a1f5b4a568a76c17835614c7b88b16e5234b6cc3a46e7a7ce4e63799315e460605865ea298bf977fa04967e424236876d510815da30225a26f5560c0588a5eeed92b0d49b8bc42c75c4a9f8eec989a1664f7f192e96438b97f978d8a11e4f690f2e30ea55d265a72c15e45af09b22f74690b27bc9ee5492eaad526cd4e510439bef149c30ced2d2fe75e3352a7c45c59970f8a6ab007445442cc43784282181a684a4412838b06e885fe656861c209aa3c7890fcacc94f66cebb1a6181bf5b64935e86eaa5e6c7edb0f6ff73f9cad063989ee7ddce338b5013a5a8c9c8e4d864c464c4448c67a00c3d3eb393a4fd8763ddf277e6b4782a98ea858ae6a9847d474c04e00aca8f7456f642730f0bf3165cf46a8e7e0d3ac768ac1031c888542e5ef352ff10169fedfa18d0c7d2d53ff157ac6bfb440527c6df4d022f96336319daec5cc7c94d75a41b4365e9494cb296a199076953bee0c6335618e290a901f403a36297315ae91d74d961ba70a30ccb1918ebfc41e0c65cdbaecf2c6d57d5ba5ad845ecf32ccbd00b02abcf3f3044aa98f74b5830b34bb8be944dd079188365126a3651201c5515e3890ea9dead38a570afbfe9ac38da1128b33655a1071ae7d7985a4af83e0fba8128431719d7074fff89ed4b83ba8f8ca93ac9e30bd7a4282121bd5c753941505c3fb83a2c77588d64bf38c27d2ee98f40d8e01f899f9e19b8c357938b08e11b1916407045aa378381c6da4cf0465e20969de150c2384c174d2bec1270fea7d7706e0c260ee13a2a8896ae1e27c13b9e8c1ffe4786ba4f053374296401e5ae7f35adc7df0547c41131d76503dea41d5507ea9e9ac68dc4734d064c6ead31c37cfc35ddacc483619f113581a2b542a68fd5b290ec4aa30639f0ddcc860844e8de0c8faf62025fee14335723d186313216c4777828336763577557b2fa564752dbc7f77f79f5d81781734f26f403bc448e5873455a4e8730552e9a1a1058ca966d28c7bc54efc54d948edaf6b8e116c6de8f028a5a075a3b13e5b0ad30c919fa638454ce180f422742ab95c13381c3ec7209258079e6c7aec7e5807326189448d9c3d04b9622769c9ae27e9fb8c9d94f7bda6f3531cb9a750ef503d2c4c2a4b4d2566a015d90610c0367e8a98d2ceb32025ba6e71c175cbf0e3d107e19aae64c3adc94ed193052bb9c7e4157e9d040ca43ea7b2fe01ad9f578a5a5f6fea56e496efc62055fbafe0308375199a5b179f37e50b1010cad25fa175d5b692b3643da0f59a96a9c9e33d9688f4ed142f903146211a973fa8414daab85c5066d4175d3285f3dd7937d303eef8cdf89305cf78372c244a42ef3e5a706c9740856a02c1ea8423239fe1a74c8c5c7b30a24b85636f4807c0afe4cb7ce6ee2d29408edaa2a8a5560a6bd0c3de706f6643a6cd3b07310a61c2a1c4902b33e39454749955266e32e6c130e8af9f5f427d0a090a08fdb049b122e2e09fadba0179634b01b44d0cad0e4d1b1f9585d753c98ff90d1271671bed9f7ca5705cd4db19262194ac7d561d2211679e77b19aa5b761ddd853a170f7245df758ced5ac521eca28dc049474e84e33c7da8f4d497c2118fa283aa9e3dc37a4a951b8e1df183442724179fb7836c0ac2b43cd08f1baca656cf4cb6ea46901d79780a9961f0c41819f43b6a5e141c8a6c129380146461535681e1a5326570b978a832f4d2627ad4e44369f26dccf44541053f73688a3df24ad27109132849e2e81056349184b675688080a41d3c26b869db3d188c5f2109d03dd2b89915f8b336ec84fdbfa3d63e32f4af8223b18cab73d8adfdc4ac2558b37f906b3a596344197adb3b286b53828312b76451e93bd1d43b952db7e479917d4c06e9bdcdb16e54fad3ad3e198aee8b2bb2202fb19cab8dffbbb311b372217ca8d9adc9be387e5b878d6bbae955865263487b718f5535a6e0eb9d1cb2c5ec851f6e197a10ea167b53da6719b77f7bc103c678a90e06a89e6166d88bcf84b5a07130c87729ecd778dd51c0daa8a0d1f204a2228d11435c7fec4f20c8090a546c76db556b1bebe2c003052a8452f95713331228d0e1890271b1fb01cb50f4280ac4b64781cef59e78427ab4776544ff5952226d2f09f4b6f8b70824e9a8432da1d39c22a4c25e344cafba270194149873335a42a02746b0af4689217db81a854246736ad49886a1a35752aee4085e69a115b9f9760ca51563503cf7068e71c207427cde2af7c8ddfa535426070e30cb9a0ffd4037d3108dac5ccebd069f927a4e2fd02643d94409f34c76daa7af332466ae0f4b680c4b70c42ab3f7f10ecc2e099b87b2f27a610d7433413407cd5847d4e958c9b3566ad3615be86b0751c643eb68c0a25808555b0fea2c6514a1e04c467ac074dca950b1fa9f1131131f24ed052645b6472811984a9c92961ce7293294413e52a30c3565416b97cf4243248a2d114d0b2d0ed4f43b2e1455000505bdeb356b9da9d8840e81ffa1fd6977bffc0b1e0118793909a99261093abd52630a0e61c8c781e653dde6d40b88f139dc18c4f89cbe31a97f229057a9180e780293eb99a73328d66db6681941fb3183027989814fa4a061df4851831b82530638de087eba505150945b6bd5d3927186925d765870ee5adef965c7e28db57f04839dffe11646ca1dd8878cc794de60b2146b2d9db029d79a83db2bb1112567f5969a704456e249c2864bf20c9e883fad74f6139bd8cfb84f4dfc0cbd0af46295945fb91801226a46255295a0a0ca40f3f21fd5de1d7d079730fdc899ea207b95bd4ad30609c9a2235fe490d4d1b685ff03163a9862e4f2e9ab051c65562fc64e983c1986374baa03b374cce8c7b020b0d19dcab6f684f026f851688be58982c84758ab088e4d9305fa3f50242419f158034471b1014f7caa3afa704ce800b2dc184029fd6698179a65f30cfb8393099463f8e43084d75626dd687b49f30c7638c06101a2566f8817d2fb5eb42a1fdd4323bd5d21ba38eb19f922468ff6b2ba1b4e3b5d48316f5c93766a9316727983f7199e7ce1c863d5a7afd9538c195163b7268d2ac755d7f18ac9c9e2eb0fd9f9dec40bdead6429067c21dae6fde1a2a4f325ed34e985d66195e800059b9afee5198999f24906d9c378799f54c00c936145cb62be233a0b1f9e14399f8a2aef88ea16ea0f158df130ddaef4f647941b934563da9df4134aa7fd47d8eea382f22e797eae5e6ae6a3018d9ee7929e0e33cf105e4156df1b015d08a8424a95cdadda4cfbb611a1fda532bc6d1def195a690f4f17955f9f419959103260d8d182b360c0b31e2de1cffccfe3cb04dc7191c4387d1cf19ec4a4005820a409bc8f08a7956c8e808cdb077f43b5145117163137597548e4b8f450f6979bcb6d886359fc82b7e85735bade61f140fa9881e52aef01c62d152d81ff684aa0af4a5afe761d9a6d25c2daad94f73cb1dde1322a1366d281d0416c367b794128c67d55b1e112ca0fe34812bdafd71843a6db8dac521b9cb102fc13c6543b45dff51f5f154f12fd0355096f08441227bec1efacc594e045f236a797e0884f7f22634ddcdf32d4830d4f8a92c9af40a4a0cdf53829aa2df97ec9cea608a71f38b179682dd6fb034d3e0f3e3f2538022b401d25be6f8771ecddf27da1d97b0bd39dc145f4e8b6f3f48e0014c2813d3831f8f2c1bf5428c5cea5690124cba76f42962020d6b8232f91d2bab7b3a79265a133e6f72627af51356acf8a6eb36dbb8492359a018320abbad77888336825285fab6ecde8d47f7f9e5267a7abd46449dfbdbfb68fb3e84e0b7f50b30f810051c285892dd01c2daff3ba1d833ad699d9c71c6b2f0527342cbed907db123386af704ab4b58321c96283d4ee863edf27807ff2e19188a948bcda3b1ec36ddc89f029ce29d7002200d316cae6face29606d8c4b38dd9c7a219346e413b812c165d4a550dde392b0f75a3d1771e7001d269d551834a766995a1c1dbf9c46b66433f39aa945b46328672184e671e66952539ca4d64f5c9e6367ab45771de3042da2f13cd7e5c5083b0b3851e6d013b3b3bc4aad8f56101b2ef2bf9c682aeb1d510159fb5b70fee7d16041084c06b3f19d38fae778d03ce44f7c570af02073c7e42a0b56885c8e1c1d723024da34f00741abf81013c5d3600abc73d689fb05f04b0864b6cb977e8f529e34ebd16cd96cabb520138730e866c68ba189e92448cbc0ae4f7b4cdf148e0fa44f068d7a51e64556bbcb13235a824a703bd4e1ecce69d7ceacb4e79c4787437029904720f7c632ea5484ec9e292cfa290b3f3e632eea541c4a51f4d70014f42be59dc4bcd600d3f1246a807f45c9ad9e995cbe233958f97b7cf6f4e9f50a3a063988de46d8e5d197f9aee740134e887c7195b0825c9a28c448d73f9e56b4ce686b029f6fc6f803fc0c59058bc2285b92461ce4e0a3422ee0ffc502b959b55e143c06fbea485fe1d2f5dc1e743e15da43e5b185d8031a92f9ce855482177489f2d12b47b1e85beabafd471b396071efd35cbeff43651a0b0cdee92b8051559294f5b027be4b82c94c76360ca1ce86a0889d21801f843462bc64741b68315229f6356d7faa8f2d29b556ddc1ea58659053b3389fa0afb3a2958ef3545951c82d52459254d845ed9cf1e7be40f97d3243315c22db4faf4fdd06d921112db27dfa6929b39acc1039683b61b44dd3b9b47d8b8ace9db96d0b6c52da3682dcc8b9684ab379d7a72345720b3fd263958c2a359f2dea13671cc4b70cf15fd46a7c1519f3b1d5a95fcbf344f58013714a4dc4c4757f028f6ad682c0c014cb7c9d284e5c62accd284c6a928ebdcece96a6001a31d4bce64b65c8ec21c97f7daea31260816b4462a69862339817042447944ac3f620e571c8ea86029af014cf2cdac425499e956134404280c488c35be96c679b695e54d0f4c1465f2a4350019dc73140a15a1b90e1f12821abb6bc159b6b49ef4c2f23332a139b0de2fc576669c781fb092abdfa931fbf4560a55413112f7a3548227a4e9b8c7011412d65dd973e6b30354b0328e8c59b3b7fe82c03c63ae402611ac4f668d2504b39a2897fcc4e0e3faff88965e98fba0f9118df872043925e31776fa583fb98376a50010e162944b735b88fe315cb8b11eb9189a04e986d9cfb10259aaf44c63daf26dbdfb4493df05815ea8a56b4af1338419baea99f93b67e1e1aae0a1a0766add720427d5676bbbcf4407447192da69f5a3147c16473efec972b2088fee0e4b0b4d85b871fff9a1aa71e3cab3a28227c614ca83f2c100fb2b23cc30360682daedd4731a37308404f7193770ff74f17ce41b67c562c82c5aeae08905ef1c7b7f434d0681f4bfc76e294e2068d10a521cc9f41fdaf9081f988298a3aa1224bf8dfc3dadef8604bbcf9138b0117181e4e00b1c6e562edd022a71f469084b363f86a95635f983b2c50f26fd353fc5ff8e24d7c10dcb98eee34a57749f2aabf5312f53c0b3cb52624b3f20184d1f432b4d4c531d7e6c49b9522cba946e0e2ded49d6387860bde881f1398949109b6d45a537ad29ee89e3ea447aaec006bf205a20da4de43a3e7ecf89bfdf94fd517c34769d8c970bb25f10193a1d6afebb9f22e0a4234f4dd0ea8966c6f89d3420d056e8bac0336cb2e413ba7d82964bc2ed7caab420a714ac56619bb8e1f3f591725cd1097b36fd8e05e65d5e9b444f7929dfc2e04d89a01358f085fb9a7adb1865a31dd1efea325ac8542a5029953dd84082c1bfaaf2ace63b39d7278004616e12a77f71e864094d29b795432584168f7cd96ae9197a84cc24aa1022b2183bee4a85874db0d61e6f081d5b8644686b6576852a96e327f2e084198729c727e455875db82e0ff7ba5e3622a6b5711d47b1210c7acbb02a79690020be4e542d51e8f16e8bc1abd60f5c3d04da8be31461855bf90d0578f8c4d3886bd2e73ae1bac59dd3b5345f01bea0a46c3e708842b09c494aba889991dda6db268a264210021336c7c59a8f2fc132b0ddf03f778c3224b897f389543c7caec880d2b4a099f86186293909ec4a1b11967ff11797ef0bb9dda79183c84865c94b1165292fc298fbaf642b4501812bc27e5f39e2c47505ccb854da8114cbcca3cdd3069bda82e04808379ab702bdd89f5a7347fd233ea3a0bd6745b4a781c9df2ed7c51761378569ff90e573074db2545f28c34897e8532ce1b4b5ea1209984b61b01c5984333a48fcd71ad18465cbf65ed1cdbe8d67cc7e6d0cf1306adc9eddb37792ae0c6a8b96fbd46cd369c743504c0e2bf57819ba26fb0ab885c00216dde0f93c7fee63d90f0974d4e58082c0603cdba6f7460cf5f8f9b1d8b7e96bcfad6562d07819543a0c52d9bdbf4bbcc6aed776528b7769cd30862213de4c9a3cc64d46b3edfd0d5a5bb69ce2e792dd6dcb7eecd2c4bbf2844dd62dc63a4b512f78d422979ae6cf058091d65b6214932805a7c61c16fc01d8348d015795ef5983522a1608f2c35ecc427a37194106c1911134ad53faadccc477e3b3cbf11bcb0c30ca3f31e0efbd795500a8f69ac646825753436ff0aa467f00c20d7f6b076b435013eabbe13471445766b091b47668c23050d829f643276d005b01d48ae36c0e132f67f9b9b920b7c6c5ea1fad5fb905eda7841062f069f06800e854ef45476b8fcd9ec63fdf1dc261f209fcdb3c0b5b1e0d5b19073d02697830fa4707f18c9aa373892da30cd09596442ce247f229d13d7fb542328f3fab934927a3a7127b03a9887f2d4511d1cd575e2683f01e0ebbb40cdcfdbb0a41eb5e4d59fdf49f8affb473b5e4fc81100a52c3cffc7ac3ac7454ca100997711918a745e50cd25b3af3138268b63a438dd5c7f9bbccbff0e7db841da56dc5bad09f91fcd331d3edcdf918fefb56fecfca2d31ff8eed0118b17aa1f23abb6b4b2de948959417b7a3aa78fc56835ca026aa0f74fa8e213c77c0c465d8ac6915af6f545fb5e2b6c10c33bd7904d1b487e9cea2edc152e94016b9283a6ab87cdcf0edfb131f9fbf96199d9231d6b47d447d6d68e2e603265a832ff97bd2688e74b92748ad4298595b1095751004c5ded890e1674b4e892013a44b6a3078e866fd0599132e848f28a067e62194aaa17369468503aa4f46755c535a0cdd09d45d29d022393f5481c9045b0668c1571d0026fbb570211bc61c40d0602a6123ab158237bd37ad0bd87b278e44a58d14e9687433cdfd3c9d317112f43ec896cb9decae07430cde177a18fc8508efbe529d264f1f21293a74b590cff576749f869450062b399b0f5b24cfaa151bc7e9b852f61a95422104b71fc6fde2f0657a51189c8d76703f77991da3d554918275bc3148ac7941d09fb2873a830e07ea86e5d642c9e54c329e1e914084ef106a71c46d04ef80b4289c201d0aaff03f6411be980775e7d892b33f84a18a37ec7cb29a59e9ed80bc01717a6b211f9de4f522a001457dd1f67164abcbc25b9cfb9b9f1798010cd3426a48bb5f0e3309769a51c06ad97f5d77a3c429a1ee3388e91b802c1488690e443a94c5ea0a8af448efbad871c0fd0e2367f53f2db134c627d4157d9a72e1e182d7c075d1823d0f470721d998e4d11a5575c5c366f0db4770b782c4574673377699a4f5bc079b2c993d247dce33e2d717fdf32787edb6aedff11130500f7e98b716c7721b98502913cbdbb23d7e86407b9abb1a1eb9d3f518fa977041400d22563972b56ff8c9337607ca3c090c40238674af00090327ab5c1a1e6d3ce5885ff9c354b4023f0e6622c5854b942115d4b1f65f353390c0530106c47d1313526e88a35b684263afcaed6bbb031295e45ac64671d7386ed2db71cdefa688d54c3637300cde12196454ccb232cd456410b131b627d06cf1d308e56d1c69a8547356b1f995947d8fc50590b359b9f4f9a50033f8bad6795ce0bb99409a63f422d6d8efe6cc45ed33829ef77a721d8756119207202ad3b3db0423ad0d4eb4f36123e97c1d231a11fa18f162935a6f05a0e5a2212b9886a15b91c0beb037d9eee5dc14652b6363dce27004a1a659a8617d3c5c124a4c2c7685b95aa14f7a525350e6b3a46e35e72503375ab200a2aec75fc01b562c6bcab1ed7e2f049e484169412d620a339f521142ae507e45e3ca57880a0f385f7be814ec4275275b9afbd145ae26ef43a52cbf985b992b5f09ed75e688072669aac02d3cb37ed03bca726cf33455e58797133febb84dcdeaf00b78e40b30b74a28089882ff27aa2e205f18ac9dd42bb008ea393a6f00f6297d6ce576378a0f13f5e87faa690cd454cb5d65b847268e62372a89d91d246b312d43e480ad6ffd924d888f6ec65c98fb8e32bb5bb130575af2005989be1e2073e76be402eb1844a1b137f205527017d1e6912084ecc31e905fac41fc77a3151d37032768be068107ad43dd07a2b89260291a37081a94149c63af5db1e9c2e9aa2e7a914580f46f445e0b0f106e0dd1a7204200ad63ebce75f1cd734ee936def6204f15195a12109689387b378263e99669e44852c535eb089c543b9140af21c0ab534053006d5957e7ab35487cb5986464730db784a8f64eb51698c92ff4b0a371c36a5fc60bb2f741c0e1ce9af0eabc0e3166218d1df7d90182b2cfeab5e5d5ae45cc2a702e9d9ee3c3298ea90b499cba0d34fccda7b0d4fc86076ecbb72a657d41a28d82ee8615f8e4f261fdaf3c3cb3576fd7e4e5b8651aacfd83199a307008c51f3881233e705187d9a5ffb280c65e734ab2011b7c02c5a064e266c0f2ec4c98920505a15022e39092109aa85d9b0f6f57cef2d476b5589e5929a2da97d50272560f811390b65543689dd180c999f5bb439b37e489d801459dfbcb81bd2365a0116a330e05b3849fa0d23d9e2ae063584d46b27a419cce71c7d3645be51aa26a24039485904e45229e0c86d2ce60f3b315d5af5601ce6018ca19c949d00f30f6479174756433aa539673945b265f9d2c3d0bdd98e071f4389af1c4a80374e9a1670a5a58b6a13e861551e342d686ca38c2df8c626676b82c555a77bb196186e7dcb1e0c727133066e848c161db31ce1f4a48a77e01a0dec76c3130b7a90b1d71ed44bbe8239bf45f51aaa535d5618fc150f4191c631bf5baa1fbd4219a7156e78239a15842580dc004629712a735da0682a13b29eef6a2e8e3500ebc4711da3709e3d743a66e392de68435951eaec1cd571d9ca847b71b2d9f17103fa0cf8b5055816f3d8816ba9a69352538a5048ed2f1ad642a9c9900d0b98524d368a25a7b4b308e45578edb8cd4848299b083551affbb31c5cb057a5e7fcd5cc4790abb108d4ba5dc93dad5fcd8417a36c5630a5d1b878cc41842ef3b35322f41eca2136bb2ef6e016bf27654248d4d1fbae46bb4873787d945b589d452f0b8d66c74d0497f9ce525a93ed04ff21da8d358adc98fffed6125a6337d6cc2f524089a7ef8d3543675d67329ba13a1b6152eab3190ab2414df97951e7500cf23720cae206e31c3ad24a9bc4398d56fec5c5fd059f5590634117b8c5dbbb25744e214cb2041f0b67ce30521ab6c9327ba97553c6969263cdd2ea99e56cccfc8b6354b9711d80fc9e59330ffa3742bde12ff55bbc3d5c131e832cf53f272bdc422c631736b9e316004e61776522877b9e8d756609918cef4538ae94255b844479222bfaa19ace955cbc1e64df352d3fdc85e0808e18c29fcf56f1de562d119a49ff6a92ffc61055d77136e8df79e33776afd884ae67b57f3ab31c053bb1ba93a619310de2a5664377f314b41f2a56e706ff2458a06ea22b4dfd96ff82c5da4c3d5d2dddde580f10ee2b4d69941f25ccfc3a2e6f2505e211b2e6bed988285a89d17ccf28745a0fd5a68a432d43d57ba37e28c984657096688d1085ace44b06ede653a253a29a6fc6911753f9df99da49e0942e1d0b0e4e5f3d2884fe8080f171e78feeb686c45040399c67e315017b8e209ec4eee2e579b88c296dc0ea335f02377e130c642639b72acca6bf7cc2b9d3661a0a33c91a950cc7badbdd928027a2bcb0e1b6d327c056870e379ade58834993a988cc30a45ee255fd289ed0ba9f2a3f2f0202ba26a6c3b434811e3cae9e814885edaddc52a102b721170c8cb9e4054b6f9567c920522b2c8f9290a702a3f2cfbc70e4ffb07cdb2dd7c52ea03e9d005bbf7ed6953856b83760f7810414b04cbc5a170d42a334b9fc5560f778b99aa57ef91f161d86a3719c72257de41d6e98ce255bbd73c963d357decd136aa78a184f104210cb46b2713a2b9c26d10be13e1d222afac5f9e20712228536713e7b43afc0fc9c61d4b57826a01d57503e89aaf867874d911512a5c6cf4eb83b0027871250d3ae82322212b71d152ccf6f481796afeb14bf88855904041001f0bc0c08252b1086d43fd4da006fe8207cab1d0e5e64ac4263756183fe13ad057415deb74a8a4fed7015c46146efd6c18d34dac448e51a1ffd205b64b60c2a382a14319189b8ea46e5db70673bb3dceedbf824b10df6ba45c638b458e04d5e9f267deafaf1f5cbc6618500cd54f92772a94a36bba72a2b0eb0e7662888c474af85d0b14fb4bc86ca9938f95ed21056692bd71de826b9d9742b1c8501973b1532789925ecb741fa2d17172c299794afc1ef74256b06ad3f34a6cfde00d9cd9a706541e04ee24af23d23e83ac7d01e9dcd8ad8a56d16ec4886e3a44264088d8fe50c362a48670333f6f2865dabab9d7f1b2d009a7251a75cf5cb14d88e4d12830f4371e5b632aa906207d0364c6066249ea73a07d0dbc6b307742442e9184a4cbb8f92e0bc09a9e8be8da72a18b1b899d47419050838cdbb9abbb21e91e6bdc8a49052ffcebd0e1016daf654af1d361dac6ded722ba6b4fef46df56a20be81b0300e5fa5371de066e2634bfe57c17e523466bbdb1772a0cc6ce3c516bfdd2a6a62de987f4b65f0dae1a6f7ff2d0268e9585ba4d08cde1d032b294005775f56f605c918b0bdf3937426d4e05a0d85253d7c433771faae5e2a19bd5f7dfc9878caf82cd017e2931508b4acdc94b637b4bfb0147202b4a0c35d9fa9c72c3e750be33df5a28fab12f8f8c0589e40fdd0d7230055a4358bd9e969609e35bd083dfb09c195a9a8bf11e2fd3e153bd4f4c0cce37184034dd85b7b99429367a5c1fc5d66314784655b37e04acb3e0b94cc92928f7a0e5f1fe090952bcc29023e5688c5a7825a6e4889f493346d65a0ed2eee81d42cfb90e664de8e76937291a3c0e3f1ef0d0a51e89b9763d4f9dc6841393ccbd17d2b7e50ead6af5c105889e7d32a4f083bab9e0d87db93bb0e1b92c9eb5409a7707286b409ea1238faed64d12076d095c04d383c2c8e881875aef624cbafe9f018d3426f28c674f48d5009aa3c1b1853ad5a06ad90ce0be880758d030646fa03816c59709485ce4e051e29bf9624cb87634c0da9b565b097c58accefa5bb5fc1738769b942abedd215deb504f526f6bb05e2ae30579b82b72106d800e83faa5bd74cf33760802a7c165f88e639baa9b1cae745006e252af32ff8563b4f12e6a7fe783783e9fdb813b8ea9c39e3a2979ea8881a33e9c63166a335541eb950c3b60709faa1528491d75922de15d2126d4d31102de51b087cba33bf0133d740bb600ac2643c3c5e92230d6d40bbe7d25479cfb2cc01b4eb0edd125bec276a6154385a5c78f2605a99b28656e6f2898c4a31d112c106097a20255406b35fe1ded428caf7083c08d4c1cb67937dec5a05dee428a17e85d6d92c22c24402c11e2f04c1493fbe773ebec9afac63eb773306d5222c02b2abe2e65cf37587c8592dfc5adb18629a647b867b5345ca0e20f6c69bba11136a3538003c46caa9c2dadf6e1a94f7484672a1cc9150b19a376a8fa7cfbf2121d4960448757b063b50d9e0dd3b1ca8d3d6c0badfd961cd0930b489128a96dd86917a8307a77eed3957b50c3b31bd40a583e3aed35894390defbdc60bdc211a8b0e6f175ad1f7faa86fbe9f9e40d297790c983311bd1d22554d214babd0d7e1212cb3cd86d8227f65feba1779dca2069c55fa2b8e6b180b8db1bef9222bf38868c5f236c527c54e2e8ac6f3ecafd044065fe0e00ded8873cb877373a5114fc426a96ab7230aa16982ecceb85b0efd92b33aa050c3ebd5eb85dab2cea3610dd40e5252b51dd98e480908cb89e41769fbe92f284e50e6924447dc3a920882d16aef24a42df494c94fb3801d9224807c53f25931030a2075c8b7371634accd8ae3007e71c2bdfffdfc9ad668f0a075c1fe98ef433e398c882b4ce9baa71adb0f69446d276ee2d3aa0b723729ace90a3a679197e9c60bb076ce2024ce2b3b73e73da686e3874f1b32bf7bd9864a18b1b28d20587c6802017e4536dd2db7c9d03fc5166a5c565f69de8876c53fdecce609c581a04cc68bca5ee004927971d714043f5b0ecfa0ecf77cc3b8550b9bbc46a3986b2a006109c89e09d3c63fa43bda32694e99e96efb68c98a50c75cedc43b4182d906ad5b06efb6ee592539d41e42847af2c949794f5017cdb457683d9b0a040355ff2676d12fe9a2a5bffc4282cfb9e8fc3128c41a6ec500b044dd660784e52cd33f813015f926d060909914ac747843b99f55ad6b37fed7d382f5c29d0cf8496cadef73dc0684353d1f84bfcff93e9375c2ca361d94124acce655b0c862bd50d95df140b0cd9118307db750984f6c2063ee288936d691871d31afe14160028e7f1b70ed002497ab07edc24211f2c6d9db10fb6cd63ac9bdd37ef32c0fe822dbd49589f456addcb3b296257e0d3018eaea025829bf6e7e7d3aec45a181354568a176ca1210374e5c316a536b508becd2d893a50684f79b0efa4211b9478360829e579de0cbb340c8a960602c91dc1064f5571f8c83b3c7e2c9464d6ca54b033a900c388a56b8f4faf85a0582d285d26bd318ca0b68e0699aa5b9199a1c4b74a113aee04f457a8e64258ad44b9e1b2de98c1197959ee4f195f9290fa47f19d3ee4920d48165974ba5cbdf62b9115624a29a2a44f694253425851f4cb8b40e6c56c5bbe7af369554a1cb8a71ef9d728856f0d1e1c290a36915fbfa5977652575e23162a405fd33e0e7a83ee104f9f26b924ecdc3c8ab969c78f3c400bcf2756298fbbfea68147aabffd0d8584cc0274f4a86e8fbde282d42ef1059bd5024ba4ca8a4c29f6ba35a923562eb5af97766f8dc5ee8c8ade5efdde4d7716bd6d66141a84db11c05903e250cf61e738bb32225e5c3f536d61b95c4afc004389614d599a768c9fcc51a837922453d382e14948422635bcdd02069b692e72b3108ccd1b1de6b8f7ebe7549eac512e58489e07c868eb70481eca1e4690e85fb1f98759126cdbbabf4cb3e425da7d4b203af5103366973c96ff3d794b856c784f2e83882923964f1a47d2c6820b8cdbaaa80830f0cf119a89474a54b24c114f44bc55c95f50f8b39c7eccbf42ab389b9ff0797c57a7913f816642e0409f4466e8cba85ce55e58d87278f5b1b6f2e329a007e5ebedea4745b165b84dbc0b50b03e203e7b6972cc5b6eec41154c5955c7cc46ddac755a1b7f8ca01eb4a2a658a97b7dced0c8bfb3bed58f9bab8cd5b0ca56d1df925932796a9ec05f0393948584c557b6bdadb4cf943b60c2f0b36a8d703691b7f9a1b570a141c3024ccda2c3c4aa5a9ce8b5831abfdd00acf8cc1277cdf58a8ac2f4977c2ce55a457d9af0819f475189b966e2103d8f1193d5f84d23e483bde96b21ae1a282765f6db023d805ffa272f4e0e111c07f29d538a16e5653abb242bb7c6efa0af422ad19f733e35ae4dc03375125e23de3ac3289378a02e40dca7adc5331f33b00753b2540843e288814535d375b4d33a4f9b44a48f28583bd22438e2115281acc2a79324517e4fc5a55d14cc99ec2999103de79456365bbfa7abe9ecb0f33107e4da79a4fcb3ad261f4816bf7c12b8bd3652d683b76fc1624a0ec69a6afd38d123ad29d8d67fab7eb88ac9636fe9eb597f05cfe7f2fdcbc669733b6f29fa2ad4d0c41a7875a969e309f54112b503280d9ff6592750d485cec730c65a895ff8525a279eec00c4e2fcc6ecb257eef949e786e7a44675c47f28bf451b2bee62bfc40739f1e2eb489d1ddd27b78b7635127d3d692f2a778145ff24b6c57222088f90ebca43bd16bdd9dc8ff2eceecf12368b34bb16760ad7eb296d9dff77608d563d9321ea73bb23b306519d560bf81518613c2a5dc3fb1b1141d62b24b62d3c4f21b0e185a9466a13b02ca0aa0d4bd3c49f7c33e303cba97d3376c930c6f03d857991d0244d8c7804993c2b31b1720892b1a8596f0b6125a8b440e2a58952b0a052506dc84ed3f98639684f558269a5c18eff5684ccb46c6a03bc25d16b569494d21e105f1eb00046ad8bc8d1b818decf434226785e8bad73c5ba29b8fe218b6d937ef01f961b4475cbeef820f03838b1eaf92cbcafadac8428b1beb6463d8d7303c05488402f348fb928c9fab3a48c1a0b80d6600f67d99980cf8d2c7d6ce3351d5accba18dd129d9ec4a445842cdf3e60b2c849160869b6c90521070f849902d3880e43ca36ad7214d965d66eb8f6d56c07ced3b1c3bc30db45bca042e87f5b8a92e963b1e6137668aad9eafa48d01350d0b1b0a80a6d41be398074ef751bd3629020b6439a46bf4308ad6c8a30d96e3fa3686221cf1c723713b984eafa1911e9ac8ee959202af9926377de9a9ef869204633065a928825a843ab2fef85d9c26ad1651de7f58c4f851b8877ecab9cd310dbabf13f51181d6c8243e25a444020145b967494f79130118c67798e9cd9c13e9445404a6b770777a849e7773caa0ddb31e22c0cc94640a2a5a903ed3e79f20689ee2bf68ab12bac820c65f212951c58b46dabbe1e86e28c84149f356dcb87f619e8768aa997a7a6997f840dbbe689e9bb68ca40a7182ea2007ab14d27a95472b2ff12aeb6bebb029d486daab3588b59e92f13dec8980db16019a91abf17689a71fccf75bdea05045b38d47836a92a65e90be08f6e867b37fc709e54f8f45e4d1fcfce1a3fd9450e59c5ec4a3c1b9dd3ad2abe76e4fba8d8e5c1fd8b6029c7e4fe87db386c888df39e8f355ffd1f6b273e70061d5476f5e8ddabb6937dc3b2f26cd3cb98093b40765577b327b0e442b3b3bf1d0d72bf8e975b518d47148c22336780f9396ba23e905cf52e6416a8a809292ed4eac59095e409eaa2c0da3a1667e83ddd023f13454759cc8c1f0959f57dda3c6e4b0c09e94200a97b2916b87227c430c5894e3d44c623a1187ae569aa7102a91de8c37bececd1ca6b3c01cf2d9a8a3004538ec413d549c9112fcf71c39b17ad4b20bbe6fd8900bd522524481261d286858b75b4f05333726182d797f8a2182c24bbab99680a8765de01e02f252d5b1866db68d88b6069b5bb426033bdb222e3b66f8dfd03e3d9ff884a1c2abdf7b4e95eb1a0dc07e73b5110791cb85df33554a20798eebc137468a95368545b836b9b8d07e317533323d1f6e1c93e8eede4399e62388c01b7098e477752320c3f340e18b397fe1ff068d6aa451e102a000030fd2ef82d70624b457bcff8e34ec3a732c3b0004f46bf8c5285e40dff0d837999d41e2cb6357c87a4042d13653b0a8a6012845edde3c64f8f355f1c1a482fb45f17023b76f9f6abdbe557270bc8d031af0c29856205c915163c9784ce6f0e02153251f018207e691c2290dacc444971ddbe8813aaad28065c86b42a9902c76e783cb8373a4f228ca124f3502da097056be94004f778748dc125a698d5bbab0b684ec5d42e4de5b4a2965bc092b0ae008ae7d8e9b2ea18728b157dee77d78532986b0600f31b6f6a9a914434e60da548a2121c0187fd98ab00dd9f3bc5ca2750c7b554ba69289b5ed6f18422c984013b8a91462a8c2994f4d08c3125db1cf23540a30f0600f31aa36fdb7a9146280a24469e84d859b4a21861fec15bd0ec581b78caed083a2c8a3b318567a2a5e2ad4a3b4a9146228b2e3093050adb5d65a3192c863f7bdf506091102b6a74a57ba8e0a7942f626e4099586ba275c0fc4785fcfdaaeabed9205afaedb07ee6eecf56c30d6dbe15aeb77fb6ee8e6be92b4f5a76907961c227f743cf0be0edfaf0aaf8aae0a5c9690baa27badfd015e6c6ff5d93777b8bbb7075554513a15b1ef6bd9d1157c1c161d06f67dc5f75d1190dac48f521bd9bdf8c3daf67bb1699bb48d354b33323694a6c39d57c2b582b782d7fb41c9764f785fce7afca8a75d4c19393ad8cccc0439c12da94ea2477eddbda6f3ef14b472c59ac1feaa8dbf3769ee2467922bf9fd0dc5c97e9f4371b1dfcba0b95b0e851e6dcfab53183d12adfab5aba68bc8918bc831a2490ed670e4a3472eb8aa960d234af94db199a6cd3c989272bb2a438abd9f37336df7cebc19cbaeca60bf9056ae58333dc25ead70c455ab4ad33d92b2f1bd177726141e41419d07de760001795f68de9febe37ddf13590c555affd32740821c2151f1c3080a6b5a916493bcebb1f4a522169395610673bd5ae48949091a461fa020a11e7808fbb837d3454291806128eaccfe2a59415a247d4d017385332a9090ec9050b442ac10511475560755337d912e1577c84df2e95a458a308a4646f9ee557991a0a825a1c49ddb9e9557c811b2a62c4da4f2ab725045ff3491b2f68767a22d7df19e525defa6543f80a5a4d846ec084b1399fcaa4aeb539358b548254284a5ba3d8815e11f5058d3063942ba37278c804421d375a3dc90acacc4e4e4c59ac1465c2d5275579e118a009351592c968a0208101914464631198dd266337ab3aecd342e72c29a94e695987557de0f68b42124271a13b1f809d8880b1bad701b70114ae3fd09b2ac3fbe6badb82f92114c07ebe96f79e2a25aad5ffd487e71af581a3b3133b226fe518b6ad1d7fa3c23b0f445b2a6903be4a56f91928835edb5a2b45be54df28a70916cacafca50d23d5679af4a131216c98ac427eb1aa9e94b5f2acc2bc476916810f9706f366e0d3187bb69af345b49092e4a81e94bc588ab45cebc1e386171add76dbe7d4be7dce41293b08eac9b883aab7b5957a58fd036b37266c6735d2a4c52589ac8fc4c13d97ea271d1bd95686c746f407014e661b6bf341169e298dc551295b44a229383e6c94f4e74a52ff72766c9c4c4a4c4e4e2a27b72b58b694d96eaaebc1fb4a8eabf7a31906d7f1243101fe9e087d874914c9d4f697d4041c9d0d1179aa44086298eac697774ec381af6adbbdfbcb8b4c8bc7670f64d8d6545469ba1a4dc62546ec660602de669ee944547d987ca07b4614cef600c73d0947230941d3485a8b08e37e6a0a832c069fa0037358da3e802fca607f0144d80c7e81f0e03c4595cfe1283ad565a56ac95914f2b40db2222ebc3b268b52795ab32a26259adb4ac465402a02d6db997d166287a95a2491935ef63f4e9ca1da9797510ac832b170cfbb95828ab15aa75db303516166b74a91ca6c6d26aa556645e42e89e37409ffff44910445c57c6f5f25432ede2af7b182d93a273bfe91747d13337551e83c9c05aa793910f94c9348e28140a8542a558cce478bcca1a502c081d83808ed9d0b12174cc87b803a2618cd1662829b798980983b9ad6dcd404243794dd449898139f8d0d429bf699872a7ccb4fdf1f1b13030303030a73be6a4a81353d211e63698181871064c696d900d4a4949494939dd285f893a28251d535ea6a0a4a494b68790905dc29a4376e8af85332dbbca5d5e5a6080d01524c864b2953843263bddb48f883ab4928eb293c868b28b336465b5e2624d4a7a6c8735a18869ba6b3065ac8735cd3206748471a679384df77053ef7014adc36f1a87a7e81c1ea3671ca645f3bca601709618ccb67235c0a32d5db9792593c86cb35c02db299a4801bf95265588354b602f077ce680b21a11d51ff7669e8460ad341dfb412149b1a9c82e0425ac798b76e1a263fc45e7782251b5558c282a369dde31bd63537362dc4a94a8f9d142d78aee83178dc004b4f3ed984f105634cd5974cd6bfabe45dbdc25c661b4f7182df3148dfb4db3388a7e7153bf4ed3339fa93c068399316031a01738163d4c204848b9dd566a6860b1eecb89e1c2a6860616ebbe9b0a0a0a4b28d33a5fd12e62bc45e7d8d4d0c0629d4e4e0c17363534e5e9f9a271ec5ed7dbe9761d53fe22eaa0fc45e4c1db3c485f42140ce6454023a0a66d31cd9f24c7b1151483d1dd8e26f37ebcb8d492942811dae9b659beae22ea78571179f0065d4299cef98ab6398b66f19a76f1161de30cd0437c013a883f402be0d7afbbe8fb17ed5d45cbc0e8fc98dc51748b9b31585189094a808494a8f9d962fe244721ada0594e0c588b9958508cc7eb7a2a2e8f8951390873981a0b8c499ba1a4dc62ba96cefb5c5e6254bc24c0a8dce5a5056606949a324090d0813bd780d0951f47990579062a954aa552ad7a00f9fcec08e221c462e565a6eb8fcf0e201e620d0a7ace00124931b319da4c0e2288a4e898c334ce3bbd9ff5cd13a01d00930005c4dc8bf9f71a73afa428e28c987f37e674df7e13756e251d636e13738b6921ce88295b10dd24a085e883355bc06881c3893af733a14cdfafe81667d134af69166fd135bf96b98bb681d1b8c7e8dc4dfde234ed7da61283c150527800639165f2ebc7bc3535aa969879184c6d05f51da1bea3d4cfec23c623e6f3a36340d6dc11a36341944585a6633f3534a1fe8b36b98b3e79024acea26d5fd1242ab49419ca2d66c2f48949898d24688b3f5b41319fd84fadacd5a3a478b420f4781389b2026e36dd1236a2694df3d3d6052efc74ddf9c517fb8901c188f9fc18ebcf988f4aca0b94172f5e24ebc56ae4c50bd87d7937044da592148ea6a815c780623e319f284a861060ab900b4823211e3dac9035edfde80ef124f260ecd9dc9a7b713777676e8b4b732fcb6fcbefbdf9ddbf2caecc8db930f776d7bc2ef7e55e18bf29bf2abff794ca567e6fbf2e7e3dcff35ce89f26ae675343d362268763211303f3e262766017b6cd43271f1b56898844558810eb53935e2e50473f8aaec904a20ca9a48cad144b88a5726264e47ee48e78e1a74760a8782e5aae56abd572b95c2ed205caebb56f80688a536f1d59d16aaf8a45934074e5c89ad65662b3ddfc49d28455c4f2716f9625f4c253c2f56aa69aa966aa996aa6daca68b512eb0f16abe5224bdfc76ab55ce48bc805496669191959ad56abd5aabcd9293054c8a14b0e6d568f5aadd672b9c8170c0543c573f1a35028144aa3b8a02e927cc16227171707e7c57772729696500c435114455176b37301361627e24493cdf8841a55a20eabf01371e840d319c42e750f7d4c16a609ba410aa26ad963ac62a786f1beded3ee94866e7a73a1bd75210dad26655b18f16db82fcd48ef359972e828dae799ad3da519c18b3befcb259377baa8746a1889e0335093b8d3a26166eb7662a269b69b33174979d26ab55a2532d84d5dc45d359869be60264cb7fc65ce4860b2da1d1a1a599554b891999155ac55451d72361293914c74ac0b93ddbf62b776ef7dadb05819fb3ef2a04bbc018fa0201779b2c10e2020f285f2717f4ab4c98ca484c95eb19a8d5686972b37d128d73ae5249a7c4db73ed32a8f95d87e0293b55e654069b9d030969cc064af58ad16bb8da64d6624256d16ab99b2daade12422456a654dffa28ec9c1560c68d26730d98ca484c95eb1da4bd30d9bc9622d4b5fee6d252f7da42c66ce4860b257ac863b993981cd5eb1db8d261493cd68544a6ea3699392e40426abcd541b8b9594243398ec15ab698d8b743e262526f2f2623b41144080c0c0940441616454bf5abf5a96e60c267bc56a5f59bf5abffa95242630596d16f39ef8f1c3ab5fad5ffd804451ab4ed4a222d98c04267bc56a65d901c166b132f0c8cd72b11c6c260439ac695bcc5ac45ad466af184c5603e976236b242435121308c6445eb3c76edf0199c5728d84a44652fb2cf6c9be1ac989a6dba4c4362329f561b257ac469ba996a64b394a49162e6431ef07b357ec3059ccfb810bf6d294ac77dd2a62100829482100fac741d73e43d3669fc941048e226ec85ad8f4e4028003cf0c1933361c0b990804a149ca17979a981e185acb596ab7a1995d5612001c7866c898993908def03a009fcdcc68b390e5e0491f04c394ccc1d3aad40f38891ee22dda017fd1429c4537e02e9a01afd10b38ed3c4e43e011d017b88cb6f1185da345cf7320ea7957e280d71a7042c0c60586788003846800031650a3956bb958b0ac0f223b6487ec8f96688b5cb4da13cc8561852d16b9162ba4cd82d01a93e85ceaf045af5c6ade42ba5aaf9defe54261b150e46db3d870add6f862b1e15cae140badf39c8e4184a6c073dcd048bc085d817ffa848508498618a24e2e71cc0728486828085af3f018fda202316ed33a3ca777380e6dc473d0b216dae634da7bcdcd59f65bb40b0abc469b91942edaac02ae183d362e710744c3c822295f5c6a62de5263a991fce55ad461a989a9952d9ae52e3ae6a5ae56fcf8785f4ccd4951a7c624a62626a6b43b808058585858584ef7cb57a2ce4b494796db585e58b43883a5b43cb468836c8fb2d461a9f53fad6fe4683d3d339a9222adb5fe77f5e926f988a84352d251bf4493e88b3374598b946225528315d9b9256d064bb2b5943022a53ec0493402dea20df0174de32cba0077d104788d1ec0735a00b769051ca71320a381788cfee10678ad000a4800101610328448912498153e3fe8d88f090b01989cb0289999d0ba13170a50a0ac40eecdde44a26a64548f82f4dcaa409af5a0a4dc30c76263f195972445ab0409d291942bba53414a18a36d2ea36fb64de7e4a001d042fb701abdc36b740c16ed95fac56fe8235e84aec00e3c10a191380574c5c9a162ab4042571f7588567bd281a7a671f875004e014d7174a8807ed001c0830f3b0829c2d1b5a1ad6b42a61b2360bc70a113c3e6db5e1062b4ce65344ece715a87dbf47dae44c7780bcdc369b4cd6bb48bbbe817678141a2f7cbdacc8636bb51040e227298363db499112612526e37161d72706a9f0f3df0b0830e3938b5ef8682d2126a0d001e7a784efbb0830e3938b503c0871e78d841871c9c9ad0516e1d590e23eabc1c46e4c1bbe5208509513596a4360811c3775ad0f4e46c381632116891bd845afbb059e81d8ee3e139ddc371680a9c086dc473e89ee7a06dbcd3e87b981a1867d199f61bba022f421f71da6b38b59394b3d93b9cda91a0dd64182f74728c3ceb6c2a7004058ce851a1b98c0cccc19892205daef33e9a16253e31202272741ba68409f19210b6c8d17a7a6634252c38162c8a90a0d2d58a868264a01e30884092902145ac88f9fce0589439929cced5f44cd942881a48376cd09ac6e1343d63a679dee91916a21e231e011923649e65eec93cdfc41932f7644c99bb9091992153cad0882e2e374433333ed0883268767664dac5658c380b7d739cdeb7699be734ce4bb4ec2d74ce69748cd7e89c447b2f7507a3897a7a9e3fabd1662c2f3ce8a9d166308c78e182dac8ba1392db883afae02a5763b1b19089008b106bc286c08a24c1aca03018d8109b1a7dd242abbcd33311d012b88c361fa34d6034d1044ea2897a7ea3081c44e8d326f03c81973d7f7121baf11a4b491481cf5626b0a29282723382e224c444c386dc6c654da8e486628b80cee60d4d8e2e7a5cb07881833143a2614360446e740913b2535060dc340c18ae160bc66a04060c5a278b820322680114a2082205288c2060eb0c86e0f3c5f8e4e8181fceddf7da5c17f7c585716fee65f18bfbbd327e67fcd65c9adbe2cedc9b6f77576eee5e9edf975f98df1bf3bbf37b2fc3a293cbf93df97ddfa7a37f9ab8df735c62e0ec1b182f5cd8d4d0b498c9ad745927270735f27c3a31725028140a35c6ec98725a605ec61466e978dc11ef4827268bc562b1a01932b28cbca2d5deaca34badcbb22c4962764a2d2c703794db0d050505052565860c9515928e0b89d2641bca86b2a16c281bca56b442f229ca392c56cb459658ad968b7cf948d221c9d7ebf57abd4e64cc78e18129296199a6b9a222fb8c49414c50725bc262b1582c13242a4e4029275c24f982c558e8d5d0089118d749a95229954aa5525dc60c1595bff0c0e852ebb22c4b92989d528b4e8e4b8c9b6d47d95036940d6543d950454599e545a780523ee3914e2c2693c964b259cc8ea893d38242a15028146d4690a25a51ada85654c3952b9f22162c453e3e453e86923aa0d7ebf57ac18076e8f8e41c2017209d56abd56ab96276409d9c169f22968f2586ceb94b8c18df89e972f7bd963f5a4ccc8d89892123b372f05713e9fcd3798c9c171e98942a9552a954aa91989dd57558725a6e46b1502c140bc542b150456390fca24b9473169e4f27460e0a8542a1c6981d534e0b4cb9f2297ad165e9a28974def2183aa5924e914f91cf8f1c12dbd54439ff72ca94132c65aac8c47b0c4d71be154f2505e5669e9894d8484afd1a6d268bc15ea4abc55a8da85223ea2486a0e9e4e37dd7fbbe2b851d34c8b6020082116a158c8400527aeb7804fb630c4d504a6ba5011d830b587082312431861f3ac40a4a69ad55ec2146d83ef3be43bc40299235994c60e882222a50137872a2d2aa12526db5292ad86b6f14f610e369df27dcac847cf1c562f80a83116810c2d0836f05e0bd37e3e09e86c2d0829b3795220c270803083e37ecd5d5a522dff7dd6e0a5c2a954a224fc651afb875064c800109f76a30148196f2d7b5e1de1ec623f0f64a1cf50a4c35a52bf4a506012f813e4731edee69208ce3bdd33f8c38f43e36c3b46f8903b589bc532a6c978dcb9396f7576c4fd37bd35080f7e61a6d268bc15ea4abc55a8da85223ea2486a0a9943fafc3d7528ceba6de152aad56d81780f5d5522c527ab10b82a0369562480e70873bb2893dc41827654febac1e5c7805b586a5a775d6532b3db856cad3eed6b1d03efd7496563eb9ca9e560e4f6fa55a3f9531d7c79317e8fab8b91b125a4d9f9ecbf3f4313fc5ba4a893187dd397572bc8a3eeb56f928f2b03ca358f459f7893ca5ce329e52270ffbb5c8737a4fea174fbf42e371eaf4c34616127d5a3bbdb41048cbe5d274b7724b679ed35bcffa1cf7b5194022955a828577263f1ef3a835a78f25b5e6897bec934cb1ca9edc3a39c94abd3cb1d0269fc3eefc70a78e53e353e59903a7c87cd65327537eda213b75746f7825459afccc283fc79dbafd24772af54c3e678d774be77c178b2c4fe7b1a2ff137d7215151c64cdd11edddc05923da53c33b63f4c50ca73bc95f637f602dddc15bab71f3777839395d82ccbcfec8f92c7ec8f8dcdcf6628b7194ad973baf97c9be570ba399b95b1d94f3efe0add9c15727d989ca43c33b69527597b599e630935daf9e3612425b79d942a25c61cdd2629f978d8b3c843f29e92f29fd843b8496e85941cbfe42625fe35b5664979daae0f4c529e25ec54973efe5a798ecf1f4f2b7d6c7c182c7b7b6601bc825a33e69c7339d68e37edb93c2dd21e5f2b7b4e87759bf651e8deb0ec997cce0f292ef57cda78bae149ba7e5a2a5ae269876cf1a3156f912cae75f17805411647b7285e7cb638d645f11e16da634db2263ea9ab152c6d7dec8f10eacc42a3161a4fea2aea90076f4c06dda9f3a4ae639fbef347d8a9c893cfca3a364be7e836acc42ba82de36feea8c593fa25fee8def071ce20ccf5ba8f8e8e9ac8add3c193abf5931c4f3b59c775d2758fa7d7a3d3f1e9d12997c49dd4c7d7f1b4727a4a53951263ebf9a7b125eab4cad3f3572bd66a75fa8d1d4734dd797ceaf4d4b33e3da52b506dc9e3edcd4ffd549e48ec531eb33ee99b8be3dba9546a3c81cfe1477d2f6a943e47fc93e6a1fb8a9a6e8aeb985f7f1ce75ce2f167a44c40c18ffd953f36dedee9c62915260be0e9f7758735893dd42450a89f4e4f9d15a8d67cdfc33c3e8b620fe0ce654d221f3ce8654ff651d4f1ac871aad89cf30d4a6f0eeb038fbc406412f0ca3a93c756491e25212a5b22651554a8cf6d95aaf6ebc6b12b5a52651aa3b50d5c302582bad2c88a20421a0369b06f09edae86d6c49846528ce477a1a0590044fec950ca4d6c5b3b53bbf73f87b6aa2b73cb38924f7ed1e6a4a71d0e17537dda58108c68bb3a33dd9c4edcacac4788ee38aac2b18eb1419e9ab17ff400a9283f615f2aa7783ae10fbcff37078712572efdd3a4424c9de7717a6ed2f2edaf519297c02a4b43c476c4d5ac40c185a4b7f6b1daab5de5aebbdc7bf7546fd2d4f7b94c20139f257eb74d30914c5b75037fda451170f7ee1290730e77124c9df344f972dc6c69b4ea75bc6e994b1f34f193276fe39becb1acc279ed3417dcee43c8e24797e83a2181bcb9de7943ec7d391a3462e1efc4589278d1af98d35e9f37454a952623c953766462e963d2347953dad8bd73766ce537963668f94d49aae832db12596e70b04411308fe6402499224c1140e40d0757a5923a9cb9ec1c75ee05dac11970d3e05134f96656b259a76143f0b0feaf3c6ccfede1dc5036ad42f78101477c29f333333326662165505486f2feebc2fd76b51a8eb4369ec692ac8d6db34dc92f12626366b9a2626b6c434cd6b9a27a74949e61517f6399a368b3b499553365e99f5190313190a2091dd1da4dd07c64cc3508354535ac2399732e8cee4a675572d8cb8f3b430562d8c2a9dfcaa8531567addb11d6f974a1db6ea34643c41249d108c5e114a034469cadddd9bc2761e15bbbb17e47a426e90ede91974a5d3c20e2b7c6077340a6ec0a1deba9f1e1a64744732e84a57838c2788e40979439406f47a80af3d469a4390d2d0dd5dc6bba34e08d2cd7950d85bf7eef484ba2136e709d9c88c3f556cb2ae0a3437837a423707c48eddfdf480d8a8cad33392f17ea88ce5e915edeea4403c1fbb3b9532e80adeb9a471824820987575c198511d124a77539ccaeee804912892c59dde1da4c2e2f2bb8342284d1a8ef2c95a4acd9d0fdac8366b76bf39977b9b21621d85558237ac211869d4e82ec3e2ccdddd87ee9dde5d1090255020821f23c400e7a442764769ec5cee1847beea7e7da0346068ca39e79c73ce39e79c730e95183398f5b781a45d7a4b8f9ca557cffaac403fb43e526680c6bd756fdd8a3fb0be2a33e0c3bd75675dc6bd75ddd1ee9ebd97567ba43beafe5d90c8c98e4c01a4a1cfceb7016d984c600e8c3ffc9954a7ff7caea0b77c5349a94971a635332e859ad9bcf8a6db77ba9a375793ecad0d79d70f50333fdf2b68aeeeb0b7fc635379a276c7c3748ceff1a9d883e93df87507d975bbeb8eb0356fd0a3ddc35479dc745d67739a1a4ce5aa90eb52a2b9cfe7def24776f74c694eef4ab1ac3c6ad014469b6b91909bab46e599c1d207a414559eb57dd23589aee4e7309b4aa5ce84347a94a685655d35b2b7dc1925994a1d08964aaa69cd112fa135326a4b7ebe8dd294ce13d34f929d7f3daeba9f63e74357f24d3ab466beadc2e8f9f0b2052dd2d8bdd475ef2ea3d69498406dbda503a134e0f36b7e85c2d3a7b9bd300cc3300cc3300cc3300cc3cfe481a178428d2915188a27d49852614a73373daea187347af6a7b3403bdffe801da529dd549e76c7cedff33f9fef27144551144fa28812c5511453a2a812c511510cbfd0641275f0b74d26d305b2f358c71ef960ced5e8e6ea1ae82d3fdb2576be585aa19d5f607992a952bee733d61e42d6cc2f8d1d2e73d1ee7edea04720a82b0f5d839c30da0dfeac3bc0a37e6381b20ddaa69f372ad0c6ba02d9fa938511e78bd867470d027fc41dd3b2613c3bfcf954213bbf7421dfcfe763cd7cbbcfcfc70713d9fbd0cebabe61b44bd89c8a0dcab7f935a95a61ed8fc5a180ec8e9a04ea9535bd877ab4a67794e7bd24dae4fcba23c8e278d42b764ea2b736d05c0e1a94c644434feb019deea702116132d5a40a647336ec2dbfeb0e1e775b18c163ac6fcc6cd33d2c8cf8e2477dee94f6e9e14119bbe433de5833dfc69af9ddc110ac3dbe2ffffbb1b96a85bde5e79a64cd7cda98f935ac696e312361cdfc3ac5682a89b0663e58dea04576ae493b67d5f3ad8fc585cfaf912f4436bdfbad6f18b1a6fba47fb03955be0c1d2cae7bcef93994267c7e362c2e040f3b3e1237f77a297fa6fb703a15756ad82885b8ef42309ee3fe81861ac6d3ebf163dd75ecae3ceb969535ee0dc766e0f3aa581cea670971df33be863571771bdd4e8d6795428cda461263b7c79f1469f78c658d6ed7b839f2f8e3253b2ace88cdb83a762c075cf6a04a22f2d8c767d35d83558ea5c548b6c6e9a4489e9035b12896d587e70d5953a835b2528d42d8b8d1a55cad91956ad4f4285d7fd2f42c5d57bbf319eb291137a7c3045374c0055f68810f6860061c7c246e4e4713474948a20a6340c20d7480835fe3e674d8a0045a08fa410bd620861f38180c2df0c11454d8118334640107d3bd3b7df7486ebcbbb2061ac6ca44597db03e2eb122cb55eadd71d2c84491b32e811bb90a973a0ed39b2377ee1ecfd242d81851a5563edc1c11f7865577fb707314078f650d728584c2271ee3999fffd2b11f5e6fe2fe751d38301df3813c5966a0e290d781236a21ac8d1b44dc1bfe4b2371ab8fad3f40948a3c787b658d5aa4ea82fdf0f95d3aada5dbd7ca2cb3eb3d9b5ddfd96c884ba5d24f5c3aaeb5565c05f05a4b2ded0eab87c15e894fbc7157666b86f8ee93628a2bc26edbd11a6b77b594d274a7620e30b63dbe56f85230d6dbe7fc3de39df1f7ddb3debdafac9e572d2ecf715b4a6ff4e3f78dd5f155fb868ee66c63b3bd9f2ffb74db27bd4f2f54089a7eaef6891c8f7f6ff7771c356a3fe793acaf5d6d524050c8c0e8a6a13b38cb259b9ed293ddfd94f94e572c9acbf7d6d5d453971a8d6ebb1cd5aecbe88198ae288dd5638871a52df8545ce54ee664a5c39d0effe9fc4aa37bce47473e37d7d9ae3c59dbebd9f5205ed1150f674aa9b91a435b6b59c36e4f1701045da159d8dd13405bba5bd2b066f7a127406a3b6990adb2f3c5e133dbcbb0d5e69bd3810341f44685e88dee217aa33ee88d6e1ac40bd8e62aa634f794c66e50676b625c3511f80b7ee10d415d71ec58ea485977b58c241a6d68cb7034bd1badd9ed9b7b63b1a893bfb238fbae94c36e7bc515575c51cab73cc2eeb1eb3e767fd196ae7bf72195da3acf7639c0b55a5c99182fb63bacb5c67049b7ddd50460c6d6a497c76e5acae80ab5c7cf785b6cedb5a5a663cd363f97f93bc1587574f512810553e09460c58e735c6d8adfd9ef2068437ca5a86009e5045413c69e0fbcc5954ca8aeead2814c34beb22671a2713a51dc4981bb32081585cce24abf4735c174f0844f38ee1557b480a5c90474a271c2678edb5d0c3db03c4d654d824e51aa0128a334f434f0c17c734464bb6952f7655db7f7ac4f0c6cefd5d361e38635eb0e1ebbbec72e41bd8d1b2653fd4cfdcd3a847770a9594b9b3dd5a08c2f683c7f73eb8d197bd29ee60ed05a4fa93d4875dd5135dd9e876958b3d61d53b0a3ed466dc3de6041694a2b2599457344ac284d486d7d873f138c96889bab463ec49f7205bb4ed411c25a1b44c6b316d9730328bb814021dd5b7d16ab913e83d0dbf48a9433bd251d40d4b066ad1ed25782f1a453ecfa9b6b2b15bb5a8cadbdb797c0ccae61cd5abf5f6fd68ff5a4f541d912106e784da129dffb9c7faf093485605832995e3279faacde3fea7927dce10e77387c872303dbfbd7c3dd27f84f8fd61c7dc6fa33eca1beea1ef1f542b59667cf7dae07cf1ed34bd55693f72a9aeee99ef0a0e962e95ed4f75567d29a750564dbb6ab5dd7fdc6e66ef4de956777d09be1f5741f778ff72a763df7a00f486d37d779a715c7d394666ada9c3d8fd2223da042044b00123a0111ce6d04465940c10e1b44f1069cca86345801093f8a6ce1099cfa8302149461073930c20f0eb6f50a90dace928fc591ae535414eabdf814bbbb2badd0965c59ef9e90c5423e2ec92a4f932c853e2cd4956d786267a44a58038f8dda540949427b8a7d9d308221fbbecb21a929353f86755cbd8a3a2367007e0328ee529375fc1aace31ae9daac5316bdb16e7a153daf8a5ef79dab3e3b38fb787c300c5dfa03f24141ab81a7901479eca61d1c432ceef4c8689f619afe8cce1a747e467b76fc636b2b139547151ac51dbb674e108137d6dd7963d775b43ca2ee71677c063fcc04104b636bb2ce3a10e32a86a36ed68fc0fb3b0ed4fe4a6bc51eeebff288ba59e539be7bcff87c95c8d3e33d47ddae77a56883b18bc2e268c7ef82d85ceda14b631c34d3f6da82acd0a5eb0e5d816238bc2beb0f1446d4ebf1c7ea9628c23f2ccec8e250f77162269cd8f8f4c44f78176d309e540a1b8f883c3de373943d382847dd7847abda8eb6bad3ec244d5b20360a7bf3b1c59f16a9f6a89fae3c804eb212a8dbb3b2fe1c8ddd59c74cd8dc4f13243e16b238d7f1710f8bfb8e8f87f01216f73a3ef66171b163208b6b1de34d6aa2da6b46fbfcba221b8f9b24c1ef098b73f17cd3ee9d787bd6559eb4329ce5ba8f202ffb8c36fe8ca238a282c86705108f21267e4011640a211657c4e26a9a248f95e086a2ed1db64377b23ccb0d7bec5c79b191d4713da6edbea28eaca4b4e5846d60c3bc1c3ad8a957ba608fc534b5620edf0f6be2f27bc28a43e3f939b1b10b96437758791e51774b4b810421234a80912ac10d43bbfb27ce18bfb3bed7d08aaf1274691eba5d2735dd60dd618ab63b91a7b6bd12476d7b77e9baed4975da58e441a1b677b2ee4fd79d1279c68da312d91e8e8ab4bd9307c14a53f6aaaea7bf93fafc449e7ac5f6bcb30e8a3bdebb8f88395cefc5fe441e18db2b71c0002dbdd5e293ab34c2ee23bceddd862ed66fecf784c5d5edc4f7e37bc2e2e8c6ab12f7b078284c929dab338ca5ee1dd35c8d7bc3f75c464a2c840a329e14098b3ddcdf7777c208a2e8543fa26ed56fae8a4681c5fab3f1432c449a74115f3e12128d1f80c8cdd81c1d9460458f20a740c82adb16a02b1509ba525f8ad99a16406a3b311219b0391df9369749d807079d5868e35f249b2f919cc791247fd3443d61c4816e517cbd5ed7b1b33e6ba886c6f154cce2535ac7ce3a47b747f137d73eebab733e9dc29fc2dfd8cdf21310920bfdd83689f11e04536115e93e6fe8dc8fe338ae76ec3355753efdeafcd4e9e67b7ffabde1d57587bdb9df0ec37cd2a10ef851d4b9bfc575d72dbe3efc9df5ea293df251d37d1a5dad5b5ccbd5c2819e556798c5c1c6fcd36fad2dcfb13ceb4b9f75bfaec22b107954fe3a0ea25bcc74e763a151d4c93ae24190fc8afe474dfe448fb7473797726b7d641c9b9907712a9fc562b14e27af78a74e6fb5708fd64faca263a824ebdce78ad4a91911001000040013170000200c088643224994e4609cabfa1480127286765046980984498ec32892a31842881842083000c68810cc9058011f9904d6919f720418a5292dacea0199710f21a4081ef30a0c3e8261555c1e8b71a67795b067b8b4c5d5aefe72986b450b3c99043b880287552cfd948a599ec4d078a2f5a530baf231ac8ea8d5ecbdc50af1df4445ab6c135c64947e7482497c4776614f739bd78922a700e8b83027dc998cd5f5634eb7e51d8566fe6413c8416ae6032a0b2875c29dd158df9dfbb3ab4602d68fc35bf65cfc41746962980c71c82f33f23efab5d7a89b4877bb0c57875ebd218e4b6dc4b96da2b952c57c4821e3e57055d40c2cfc61afbf0213228767241eaec0a1a31a76c53904e6d12dc60a2e163ddf7c0db127d000cb8534fd44f9816fd910f0bb9c86a16c93dca20e38f10a20bfc443ea6bb48d6241fc06fac856f0357c0fee98300b4b54e4bbc134ee288645c4b1945166be77657f206fd07ecae25a8d94b6db64cae8c719ef8bdbf42f31a7c939c5577cd0e5a9ba28adfa4d4d5de47e3812f9ec04ac5bc63a4d77d3e3e4ea6cfa011ab2a662345b2ea4557ded87ecc761587765edd6035d744e87c12b0ebb75d7d12840c51002ef8a6d03a029668cb66ca10d1d070da1f5764f4416c468716d37da8750fe30619d30df36ea9700f2ef596f072bfad74f9624647f490cf397f25a2d05a90df4bd002b066acec214d3ce30a6d7783ed84a5d48fd2d4a7aff8a5ad8e7db9b22df9cdf54170f6b6811e88969d32b313752c03ac25cf9e68e487569fdea01f7587042fa93c470ffab129f0eac6fddb69b62afe77900fea0ee15ec3410f9fb9afc55598cee67decff885bfc0926a92c2751fa720c8fc6f252293f9c440014f1d2e70f0d667e513308a125498a6ad99d6181b61a4c266331362160d5d8a6b1497c3d5445c59693f2717e825d5c09cd065b9792fbf6adf5b5c0ded870bca73a3acdc0b72a2b41f704a0e3eda9c4628a9a62a8a496e0f03ccb13636658784b5fa4059b8d12ec2e6d2d69b92a7852b4bc4e1154f1ef952082c67220deb9a88146e189fe2933380be3b964f982ea01785b4060214dd9a08b42e4ca6163b40f4e883dcdb6c5e9812c0c689bcf18644b2e9a70c0a58ea64abbc5c6a2706af552e6b6a68066468222258d7432c1f2e5a3c2a2f09380b22b69f9328328108c604853bdc6f28ce990277aa387084e6e98dcc8857692e0e8e58f36c97c24cd44bfe8978463373caeec22ce0ab3283c497912c4bcaae819aa5ab426a2b9bc9f20b36a6fc31a2936847553daaaa349f6a1495af89d86fb5b6a04674681b1f4f22ea51a4af63a41231b2c6b306210e8226965a3521cc86e29d35064371feaea6e9195c678ec8c20be1a60cd9e4fa64ad3a19b6de7158be511a6e1a0c43f7ec9723249ed4ab89326249b6a210ab9c229dc79bd0c5e236bbeff50c9173720e14bb733022b0a87976daf2cc35341ab5c802ff572767cd5844bf0e6986d4f3e8098428fddf704abd9bd50270c0b2a4dbe3192d9a60aa324dfb23190bac19b778226b8d4f79e8ef12debf38891802ffb06604e6219d5b2862e8a87239a2014261ad1581651c52270373b30617f18be1d3ce1d227f007686e52c31a3c4679bd75cfb1110c620dae648f43c0b9869623891a48558b5f01153ce8b120900899862ae4ca7a9ef890381a240d4de7c533f8ceb87c485f58783b9964b940351c7d55c5a394b604a2282daee8feef0f10421ac186b7501fe69eab2dd4732dc23718141795697af0e64e02c5003381f0ddf15d0a1f3b36e986ce7757652dd3a010c0824234cf2faeed7db1dd8c57bb2288e1f7f7898f7326a123dbc037e4690765b8c6cdc60a02d809658d62ccb20a47a41d6c1b4de6db348f1dd9f517ab0d6adbf7e21182b5ccb023ed191f8990057e6952525e9592ebb862e08d59e6a62b29c3f4335b95b2df1545ea8759ec4734b3777a2d35c2eeeac6e78197be7b0d8e912706246252781598b739b62d7c94d8b4a13b2e360758580a71e144e25ce83694525c3fdf24303246556e099d5465fc1f2a031208c795a1f6daff37421ab03408cfae474c803c1098e37be0e61e3ec0c5f05b61eee9410aa42067ebfb97696a03a58771704074b5f8cd139f8a381898c6b62d3bee0041495ccf9cb15448fab7040a4ac7c0192519f5186cf9ff5a6535bdfb5284fcce2cb450c8118c5903739d9934d3cb93845f6160b2408ef6641221d1ae2c24b84e88ae9ec6b281ec7857f0095c6f857392b1b001b9ad12a1df271279c1055847c46e34d7c0d7ca94a1541eaa6c48168281daa1516f817618498ec610b5d4804b50f7df451913075c3e7db95611c7ceaecc75611c5f94923f33f22a365d49c3c78c910c568cb4e566863210e1e03f9ba27be53a56525dd202618dfbc0406e766adf57b3819cb586c9c51ede0649c9c44b70e05f4f1918fa5d50e3ad41519e1577b535b4a4a2df1e638400fb727f53a8ddf51240eb6f62f18be8974082b31a4635fbcbd8eb3dac8e5c9a0b14cba448f4d11aecbab51cbaa62d878132cc583f1457ef4fbaa5340c964bb4f31e68b54b82d3e499717a9d1384e42070c194f6f1b368d829323d00714de5eabf2460d23db8e2850699ac181f0f52dc0e6f962143e50fafec42fd5dfe9b4dabd9e4c084225575c9bf3c9c4e061f69af44c03b78cca4f87678d2270c6bb413de2c942f39c286937082f6189d8871b71d4bba0869a69414b5f04aa63d1e059a4dc545593406109c9d9fb3b54be533d10bc81da692864b5e142ea5a4181f1cd45d3f8aec50b06140862f97998c172244a466a0907a96a05169c0e7042126cd37146be1a0bdcfc092c14a6f3531a3f2e487f3615d0bdb0d9f8ce7d2e8e02e197697fabbe0fab85b259d146c6ac44c1398b07675fd809b53a1151aa8548723bb89adde52c560d1a2616019376155877adb2546c801b9966b318dec5c3c723197907f5fe0008c101643e15f05520824bd403feda5d6efda7dcb2eec6345d21e40f968c697a2b9cd02041ffe882ed6e8788f3c87c66e563870bf13b676d3285a328a6499c7ba45093136a906ad78d79084713b493bc240d464960e888696658ea72d5ff7907c4d8304d5d31b798f5d37cb3551d161c9f62a4b6ddd181eabb87fb66d67a51afb05e63a5c95c62559a3ea44a136ba29f5f570271f6859568833a4f74e3aa3851ea7a1925b472eff182d5c58759492f3476bda7fa7136a7eb605bda528d15e77c608ed8a88216d49ff017db9619fa9eba5d6b9910d1daa2f136a59cff481f9fd9695695938097391dde55102aaa0c19293b93ca9382632fea22236688a226732336b8a8f7d3d0c850f740bc92487fac45a81ecfe90ec1920eef429ae5ee43ac21cd96cd0186a0d8040c71867869e08babfe3a007a0114b6da88db0dbf6a2095e94cd50393bc8f901b01f3f909f01c8d508ceab8396450fdd8a083b762a952ecc4043b49239772d1026dcdc6b0d6170585831dd5661f7c613c299c0b7f048e895070d5faccca7e8612466b63e2754fbe8ab2ad9628554835e5337727045560f299761cd4f9ff137c667445c0676ead42b4da0e61b84f5d46074f83db105bc1a791241c77a668e75c37a433caed0bc2fa642f1ab542055030cfccd55990c5fb3add7d59d66ee414a24f2071a87eb56b91d70ec4466baff7c8dc01812daafab1ada1f64213c39135670668455c769feef1a02c4cda5122c922210a1592314b4742ec2f44312879f8cae9dc9c143b57e9527fa15dcec4f41dff29350b6ae9abca810de2d94b6459ac158e18dffffe7bbe867d57399a483e86a20e51ee07d14f509ed16c4bf8095b4ebfcc16000b3927cda2a9f8e0c4fe9534b3593c09b027acb3bbad1b0c9efdf9f96f48aac585ae0775a7ace40bf4a1db3e79f21bfc9fe13998ea74005796b13690bba9b02c08cb2c6ada6ba1373b04392fa18a97380c39b802d3c98d5b456436bd4ffa4b8046a9c7e9a501bd2527f11f600cfb90d76ee02a805a297b1939cf0d85c2032c6b2cf4592c05518c56ea926a057af372678d7ef8b41cc57393f6cda378656541ca66a6447dedd8447c0f190c63794887dc56b3300d882acd1c9d742b1db00edd4ce450e14c047d327b39b1120490ee69e8e49dbc453438f911677ca1e94fe14a524a0e4fb033ec5cb1c1d6f0f54c489f7b61a7a8134e30c2bdd7809fff93087c08a0db4eeafda9e26adf545706cb0f852c687de206f8a3bdf43b3adb175ef666fe0026d5b4924b75080af9f5e14d37382207fc8ecaa17dfa6625a70f58c4f35a8300737ba7b7202b3bdaacb6e4b22303e6aee1134eaf7a757420df01a0325b89c784c7cbf0ac056728e1cee015ad808712e72572df2df07b9569dea31b86ae4345da758e9507f520832a536aab7d4083db534b14eba0c4631408e63720cf78633af7be966eefd18bd4f6d80a28f3d20742f5662652952bcb08a79ee32f60b5cf5e754ceb4266e596cd4e814c01a744f9ce964dff7d744eb52ea14ac3aada5668cb545587b4d832481203bc15a814aeba447ae8c711de62113b0eefa35c71fcb55883599b194c1d62c280633802e77a46c76d9a50fe68510f5d4c7f982ee9902c14e93f88fbfc1b8a59a206a152d462fb9b69c57c10a4fe4effc2b81dc1ea89e0b1c6feb1aa1776d1eb3a8ba77a4dc1abf412a500b9995c3003329449222821a99c119c32985ea46247fcab7a0401a9221893bc78cfdc1291c5039c3ed9a9aeaaaf2c8eb90222516c604b4dcbf6c34564177572ad839284bacfac637666ee23ff06bd0b0b148b548732bfdda9ed194200981d727128a0718f54a49f1d7aac94d9f0f22058a1b4e1f57349ef48ce1c59a4489b59e31e16ea823731e4edb8314d63dddd81edb8e4261d4859301d1d0edc4b8955e9530b4272ea7d5f4e5bf805fc47e53dab4db9672ebb79f1517b7c7666142494e002aba4b4d32f7b89e9f9d20545c7aee0bbf72a774855761760cf9532ee0fe4091101d8164763409ce906eca65101d2f4ade4ae4b074dd09c1b9e8ccf969652835b1fc6a09e229e010941a4f4f6d778d0c0c1bade16f92d2f3b4803e7426bcb4d87513bfb83b3be72f2429026df50f95d9c28760a1fceca1302deec809bba45f0b1d8b4ece463f9b5f20810f2b26110ce9a3a32aa59d14775a1261e83f4d3076e34f07868d692778a5f733aaeb3f95f5fd613328655343b83c24b3727750295363154144335383566f97cc569e3acf6b0199c360d9d43a212de86d57c1003e55f469c30b58c12d22110de9966da159f0a5192191a1e965cf84cae044829f537621d4bf3fea03331536ba9ce595b50c4876b089c36a49a771cc4e7626ca5e824149123aa6c8f660d477d15870cf91c945b4fc8873be270a9c05641a3b750cdd24cf47c16588b23fa53b5c64b629af9eb38a205fb9b38fdf43ae29fd15f0ac2f9d5f349c5e63c0e4f5fcf1d8b055e5ba5ffceeb895cbf1c2182ed808bcd06495472b7b9f711460e7998e1987b2a365827b146b351bc70f9aba63d8ec765e12738ac66379ca1d6977d5f3a44f31e5848a0327ed2690e119c96b3e0a44e985d3f7fc4a25df0f3622243a4dc5562a8e678d42fd5b40bdeab67e506219bf4a91a54900dab29f2cc1398cbf2ea5ed46b9ba1ae7702e890ac9fc230b61f2de2df379e80d4a7842ab10b9d96759df8b529f3e594b31435ba2ae04115418f3b7e6fe9725864962700eab577f00216b8e5208a88f27dd8cbcea14a41bb02771a7af0e4d0ba7245c1e624859ae330115b3a94f6b24aa3ce039ad52c018ed765d20fcc74bdecbbb7c639bdd11b6701993a9afd59c2f3dd742cbf659a4a3414d58720b7d3bdc9d5c1451f06c6d162a89d52ecf67d7194a3b24516f1f01553396be10eef0f5bf348b9cf98b7522d22f2971856f29aa6d738cbc54151519f16b8d4ec47397229761c32ad779ae020724430508b0b8eb2c12706d45b0a6e474ae795a4b0769248242f0de00eb0944155d8dea29df194ea03e7efd2a387cddb2136dd368ade74e8e611ada2d6a91cacf943d2ab03856973f3a88b15c5b6e7165078e957c0abe961797ace07b2ab59d2346962f50bc1b85f48452cff73a1ae70f4605ee619369d2d2c7bb9f893bde9bf1fbe7884bad2aa03b1558fda81470dd8aaaf5a3c02b8470a879a5846d040a1270d723b58d36785e1c78a620d621aa50ac71d573c77950ff70f68400f85d6623009f199804c018b554429904fa1873ac2cf649827098a95fd9aea551ace6c1a02c4e418658e443368ed5f045c3618315ffc79f1e9488307b59e969a9ac3e8a562218536147ecec19e38d86a1dab8c7ebc65dfa810d540abf4c10d5d5cd93da941de0d836ee899c80cc7d03627dde393743145a7ab635b6e064ec9be4d2c774d11ec4440b437954cbff86ec0054a3c4e7728f9d543dfd65e56e10ef015e491dc1171cdaa0a02344d8c12f58d49a0dbf8513a81026d1061a56008ed0f64ea72907fc890915bff4c7c6db46bd5a8a442f1e432900b264a73d858e7423167e4a6b31ee2b192829607201b8ecba30ee1d4050aa68ea79fcf518b22bf4a710341cf489070a380cd6281de37e8f46ac4d880e12ef65cbe1777ca2dee2944a64464b43c0be34b1f39cebf1ef429eb930f251eb9434319307d81a3a7c340e5d5a962a845cd9f5b652e49367f323db0d6934cb6b081b0e677f027ccb868291864885fc90279833ab750aba84058cb74b603a3caf55952c8640576ea3652ed60984df2da0116dcc730b502d026ee439b8ff1114994a7381e1e6d3ef0b4cc0a420758573869fd7791669b8b289392bad2c39cd236296e5b9252a6594f621c62fef8fd09971e02a15066fbed976d1578e6ba89814d0c6195e0c4cc944cba4d60527e1ce1c439b39d4b06399a97e1732db9ed62769cbe92e11ac9641cda74991652331e44edbd392a860836786d4fa6baa1478ac00380cf91f8af24bba1b7bfa11de250ab38dcfef98efaf02d95ca6a539612296d01cbec011a83591ef17a45e707cda15e2778bb5ac615222249107f102e83442497137c1b887ef2a3cc0b8f8f2ddb15c390a626948e39d0a57c021322ec03214853ccad3441e208e8682907144aa0540997518379f0c0310ef23921149812b585aee70947585fa4d1cba77d761e57106ce6d2d7574e5fdeb3b3710aa6752f6929a7d6386ae79f9ed2cd2260a365000ef8fb0edd0f7d54ea341023707231538350e3a121d6c1b88f7728c0d5d7f5dc1aaa89160bd7d4e59a415d45c736a2ee6091a09e2e31cc988fc5caba444d1ba04deeabb0367bd47593bf2c80e10509b94df31b1963bde3e6a19c811260e80037fcf22cccdc8c5a2b064efeb4b18ee4ad099fd277c8b405ae22de8bf4a8ba6d2062cb1fcbb3689b43f236783a64d641907ce99281f47a918ecbb3180372a0d385edd8a5c4c2a20a3e1a15c75d7a8a69d57b3f649511b72b19e8993c772042e9b20c04f6373f4512569780e2bca5b79c732a2726c9e82e63f7e04d01dcee22bf8252ab12df8582e94cb94556d5addadfc984fef57da751ece9e2e79a06172cc879892dbf4c7ebd1166fd5955154ee655ca604fb30e2cd8a8c6fe126944c3fb6ddc19f92e853fa30c05769915f953811c53db1fc130708dea31212c3fec3b2301ec60167ac867a4629ace9c9be2214ee66de0d5b419583be6d12d649b068119d1c43477a8ed3df2577a7f31ec9994a61c668c33172c11ee084f46b2b075ae03e52b9baf0ce2e2d742758118a6049bbb5de8ee0ce9a95ef967b6ac97ba2b4ef2d24b8b41cf69827c308330e3da9a5a264a2240e9c8dc4146a19b39cf4e0b49ea9534634236f46e0f2e6f16508bfdc40322726f6c9b14c12d0a9d7352ec6861c9f62cdb1fbbdd2dc66c04bae18406b35c9f63f64f0b3dcb305a886016f94ede4f25120de9cff65a8902a2deb126ee057ee1aeca05165ec3f18c731f4388897ca9248c481124d3708a20280f5b38d012813357e0883b7456bb0c70cb572887d24cce3443005fde1da451fb05c3c0068637baad14442ee14cbb7504b80b190741348a775049031133ac7f1a342e9a82ff3ed136847b6f4609016fe900dc18140a46b5c4ea0d1d776ec1df0724b2ac083b0422792d9520cf9d843227de168a05b9c5705e0d0b7cb340225739bda03d6d4cae5225183f03a0942af1fc0e0577a5d104a808332e1b01a4ae6d2196f09739506c302f735e023128d132ebd92970bcc2d65fdb762bb75338623cd183083b488a3f490d7847c63ac9a0cfc46b11168b4997e249a81654e638f3fb5a8abb2fbd34a264326a5cadf5f353d425d302f48ae5fb34c91c16c6a8c549d85d77414348042a5e824affbfe93db707ad35b21c137e0461add58b618f3e236f76108be427f1b88bff0418375a9cf04183a20c036d48df04b8e6371c55a893af76aeaed2807fefb430ba51e3af9855b7fbc6e4afa48707b0f35e9f44a3dea145806abacd25fda783d8ff9c2d67caab507048021cd83f128cc8fd1b52575bad13cd00714b486203b3ef484af0fa96f1e8f7cb38f5f2ba351896685d4e9520552438950fff15aeecf76c05dee3117c1a9e2890c9b78f3ad3915bb13107d2c45555f680437c0a964c8b4c4e3708ecd72085e07ec1c4c467379919f94e44f8b98110516f32e6ad70411592de9ec3b901e3c68ada09de8a551969cb05e8e34d9eb21f618e6e916c89a4eb6445798a7c5aa8a7d71cafaf465c78cc6fab18bdae71df7fd00da1f38837995c8853defeec18af6fa5be6d05e4bd28914def9e026da77ac1e0a5e78dce5cabb4371df91e233d906b52baaf866d1142ec04a20100a1fe8cbd99882c74e59a3a011063bce0d2c17da742e71baef6513e31a2837665967afd1cd0c3432a2d8ecae1b6a27b20375a048079b35f8fff9fd4e0ae5ad055672491f088e9ed52ac004792591d1d66eca1f1914378b3fd7b34409f0143fdaf9e38ef95b895441c26d8f5d9fc763ab84dd6cfd25095bda6c3f13b8de54666797b60d32c6460425f2a3483ab6d56ab2b9a81271ab002462be3b611fb6c3719fc6d3150a200d03c7bd563b2878d266f6b82748f30832c1fda3b7a334c3c2a8026f17fd377279823c7ec7503d8b09cd337e15db435a2590800ae41ab821fe63d40a64e1a32cb4a32bc4366adb00a15f0846f2123616a831360a37753841ba01ba975c9d813e19f266470416edae77f51aec27ea17fca524ccd7ff0634ade693deeba11e3295361709af7046e8bc921f85000c30695a5fed1e5380fcac19244116ccd488973571ca42f4b781f00e3d1154fb1181b6f65b0143a4719df95bb19a594a5f0d6d2c5cd0321a79ffa1b1439290687360bb88621b006eeb2e03e1112c9c7c9af5abc7f10ba066562f1cc51fd1466fa41cdcf1196b3681667396233772a73ddb05d484ca0d87095ffc4fe13cdf1d4212794f35a84f99566c694ac779c0fa5e5e50b6e00bc96b50364f4c0880d886a507b422cd69f1de99f0f9e6963f1946bccead7cf51f9b0bfd601349ca66777998e85596fecd797600a5a4e8561b0ae32c2d055c4bc5f9ad8fad2c15e4b9629c7c95e7d7580b808378ec68e47ffc7a9481ab4b51092b7848a9646dc2ca0f3ff593a9665e99c6f53f984466c32c2706e0bae963105468bdbb28812ff1ceb69e20d57845296482ce8ae4684c442b924230cfdd5acc32acc96d87d0aeb41970e5c832bf70b46187a5a2e92ac450f23ea90a60986ac89b3b28f285cbf386580716a45a9ea33a7a91824df8647036f55a303023e9e0b1acc4061b73a968fcfa74092d53b03ebdf0d40542a5995a9ec0ca604f3aef5a6e11ef166211d32fd5bf4e51a6d5d7e49474f51e89ca598793c2caf3c25c60f94f1632c044762aebc7d4417e1f1bb3f173fba06a3a9632041312a662bee7ee6fb42a50e06657047e69f83e17da145180dde225dccdbaf64c3086eda25326fac28b6fd98e1c64a7af7063700f7f5b2d980f65756cc0f84740ab357fb0c96c47b75bbc7de0ba1227fa86be000a9449b10c1ec7bc3bd37b2d4e49cf81ed2295066956dea49814b57d95430e24d9013ae349ac43dae505f70efc0a1f388669bcfbaed6617037367a58dfc7c9c756357be866db2bdd7b99227a48d866b83694dbaa9dd0caa471790c4da21e483117e1536bbc774613348f492c601be70886618d961c280315db220bb5eca86dc8e717e07953b6f0704a9e3bab9ab7e84ff030a8475808403b42531d78d6768078ac66b68a679ac6e08c81b6331ee4158940800a513e83ab8ceaf83e452a47a53b3638dbf0e7a233e80a55aada9872c686ab654b4034a942131a766776fba80008970c11d91fa923b6cb64797aa01d47c5796cf9764bd9935a0ed9d8f64dcd8842203d2c7b2640dc40b4e8e89f48d9cde7a5a8d35605c5ce1d533c90a5a67380e04d2de8ed4340c0bb528bf1ea8ed047fe9edfdd8a17fb9b15421a9d1e125a274b03cea67a4f89f67e4da51908dfcdd2874f82b77adc289cd5f06776e9363e13fbd55a6c86fec0897e1f1cf40c8705b7b6c0dc0b58e173f2d11ae073225f9ed69c3ef747ab746315041b147183defb5166b428c6e61ff267db2f70bc5a97c78556da89da2709ed002e9906b6f59deeffd1587d1334a523078c30039bd21a5e204b293bb968bf4f63624495729a2e6c781382f6f17ad7c71885104650ea4df3ca05d7269d24ed9b3d3d427d3cec949df6a5e62f8510626554c607f2acacc4cbc95c12573a08ea899d93c5ecb61a5b793332ff880ab4c12178532d2776478750b34cdeb68765288fbe9fa1d6ebb65beb8bd7bfbcab4d115c1c7ac53d66c5f08dd82be460fbe4e7a1b9d5e0ea317f7b121e2b7c50d0e55087165bacb7b40ecc7ce9c39b8334d8fc1b0a48b73fcb2a494ca990bc5eba90ca988cc21e4aa396b5253b9a59109adb1c821b26eb182abfbf8f984989c1c1a20ac3be7e02aae837fc4dc3d4c7c99cec06d8961fa37d1bc0960fa3715115128be7b4df9fda00c3b1a386cf7ad17622182e5a93d6eea03e04e4edf73bd8a1a89b37dec65f725e799e306292848a3f70fe231525f583ac80767e89384e6a3ad642c3beb0e2087cc62a5227bee0436bc81d34ede2bf057c923d33a92ad36707db8ef90ed0dec47cdb4e9b842eed15ca8f587df86615fcf49d96ae83bb7b5ac6810c055cc850b7aa66427bd876986392dd86e611d3f6e44a5dcc979236977f999b0c10f4358cf4e6edfd7bea0100039b108960715c73ac783db1b6991fb9fe1a5907a3b24571b1bd71d9f7bdc0c7628e1fbae4161963de7a74a5d1a63f0d00f02a8a5eb333f495e676a777c4c88f4e6729455d846f1b210420bf4c9850426f668d9212b77c3d7d9fed72433ddd630594e3ae2263e1d5a2b445b3ce0eeb6d609ac199a8b79e0da919d5b39934d03e75f44b738f5d2fd43c521ff0eba1cef879655ac5b1cbe7a420bedaa325bdaaf1902c360c28b99f603937a99365bdf830471162e9a147465a6d659e48fa03976c4858ae41e41011554b072c9a4cadc13ff3655eba7c7b0791717352ca450f9e85c23c5c1df66d736ff9b949734c0bccd035f77a8739ac16a07f339caea527d73d387ff2980231f7937e1d94c1a75d8037e3b578353f99bcf5af766645ddb36704213b54d51b52d7e5717f1b12b03b12a14becfe0702b20b80cb182bbf92a016641ea4a3ac7057a801a8a60669294c4a2fce56b8f8f48ac48eca0a201858d84af1fb58c2a5df01c0ef12e208c60e97c7bfbbc3f693b6784bbf0ff3036c2ecb53a510d817518f5293393da49f6202ab0f5a129c2db88c74baa1251591b8cee3e576527fe90a7a84e7fb06e9e357a0b1ca826a4fc32302216a6f847b97c9fa18671b46ac26aca8d8ed60529e85e385a462add37ece8be009443977ae7a028d87959284f285e360712b39b5485f7092e31b3ff13a0a4370a269ddf268b55daec6f73472f4523b87f5a22928adc39632c42b4461cb681597912ba77208cda606914cc69e0bb3fb476d6dc36ad50eb01fabc1a0b6114cacee653e400ed310ce2810373274caeb94d55a3cfcd738ce7d4da928595c6feb22e39f7234e9ebde920dcda3fbc4817fed71c14fae6e00685c5f58b07201faa6177b592a582a9137758a1b0aa4eea6842a3fa0dc94ae853d4f355c7695d97965f56ebca3aef956728fe0c73deccb7f3de0c03e7d9185fe71e5c98c340bac9108b283f49860657e626a36e61425d12e0e29af7f2d2fdf1bf015eb8d257e3f07c61ea56f632d901a08ffd2f052504e63d932b4c6c7746afef771ffd6cab95408db7e8f737c8ab90d57eca5e5c458e2929ca1271dcd6a579293f5495a8a6674b0633e70584330670da471ea99e8504a3fe00b679c9cc73e7939771f0fd805d0b1ddf906ce1f36c1b728e6fa239d484bfca86143dec718768fe48a8151c3197c44d448c8bbda3767849941c2d89723f5d1635474ba274ac4b4262cacb2fe388be5c18c0aaada2f16653e3eadbb0bf51ec20e846f16d537f427998f6bc8568e12e1c28d1d2566bcab999f6aae6e44fd1988e8b633586291e7115670a9a0cecad7a799f2206edb5c2db0875e9b02ea0c483ed5a2de15e07146d437816e3f56d40781c2154309d5cf5444d94cbff0d809692bf5b60fd3cb587b0f73b9ec51e45ac9b7ed42350998ff78e96a3bf21c71ab1a07375bfe31cd357db101ab55d0cb6a948a26606b7805444d54111f73bfc826a76e9d7dba4afd8afb48d20ab356582ca71a617e100591bace84ea52b9e90b02ce8a9c2da62411bdf047ba9a546d072d71533455c1f1f9553e64856e7c17e3423a8056b234654764baed26a484a9809819f6393fbc5e50f50531bb478ee01f5f94e6560746ee0a371b7076c24ac5b8a07c9dabb5393f50a534566aad223c2c09b05c878f3a447f4bc8cc9bdd9fb8f909f9e177bceb5778b664e6480865808a076e8f6fb354e5c5b3af473f7aa73da63238ac3e7f65f13cadd26926ef076c0123c9757427a42dccca7d7f3715b32c2a821efbda6a02b3352940b66058d0457d671b9f9242041cd5f267d59b76b18fe30f6973dad5b3be267db53c24e323c2a681948ba2e630e16591d20e4a7ef1a207591df5590b9d7364b9a2108964a7d4fc86e1c2aa5a658adc1e0a5ccd7594a8997a36884793757d6452ca302ba23b25c3f0a37a5e379ac33a67a90928c9c82fff695b3876d4a4dfec49bb3ea7514fee314599f2cb79fd9f70e9e4810424292a2109a716c103ffb6b3534b4ec8d0e5fb981729102ad54bf56b0a89dac85dfe7b22d7d850b1b997430139e30ba12625c749a30d1ca96ad924fda0bdc8344ba0bc201d59f507d8dd02b610aa07a809c3a1d960661f2d06d7c79c8772508d5825ac675c018d58f709709571847e6339e0b8a835ca018cd4d5351063a6584b21f56d0e1070a05b89884e2da167012c8e81801c02cad731752d20077213f04440b2c04e52e24a06186edb522c8432e64a20896d28fe4ea0d385c241e367a511f35c5fce0d4fa8285be9a13075e04b07f539df1d97ebeb9034c7ad227c747cbeb70e81ac845efc850f072b893571f523c3a8aef333ab81932a3358ad633744fcb2a17c13b10304d7a17d956c69dd6bc7f3eb0709562e2fcf6ac490f53c71eea5586e18286c95d95f10d4e1edfa1ed42c967bc0a601b3a633415ea41f8e55cf7a89859dd6bcf026cfb56af62cfbbf3ed80ccbcbf704eba0e66e0db7b5207ae47e5d092a81d5906cac2d4929813d1c0082664cbf427e4ed6c4de002c9b46d07b6ba067b01d4fb43415255e3cd05fa095f9b90b920ae4d9b6cf0b7661ea1bd1aeb9221d992309b136a72b88a3768b616e5bfdd7de1dc0a9d359838eb2ca60b9eb197178c9878ee93862b46ed0926d76ca46ac183d9a60b663e748e39b07b18c3037010e7e468118c5cb545adef750dfb7018f28b30f0650737b5a36ad33bde0517c0559bd718c775ae8240e1e505de4d599f1d68a4b16aa604fec6a213672d455d1c749617585f3b275e6151a451c4ba05f669c946aaed09191d2a6334eab87a90f4d9d8da0c5a86d2b80a566220950eda262ed7067ae7b49009a8c89f576b2f10857419c8c51728f3be17485fe717aca93446020b9dda356762f64d9d12b9be0345fe5e90c918e4670cb82702c07f56fc56167c9e3d83155fa06601fb0ce50bdce74707062845aca71cd53bc2bd1a1958be20a4d48bb8c99710acc2ab85e577820416213c8d32f815c3abf923b6ce5c48a971d755f9a05f907c464f7815c0407fc1bdf6c8ad3f2bda88ce5cfde7eb4cf19d64478b565875ef2e73b5d0fd0b9441083740ddcb013008b48fa91ebb3ec567b1b22ec8601d6010eac40ebaae8ca5da8081c61627567137c1fbc0e7896a72989efe4b7dde46ef2bbcf3f4a8acde232eeb063fd88ec89114a4919ef5986ccde786725721202df58f6236d4150c7cb863fa6468aa2c00c2e1391e0c88f8eea1e3e17530fd8203f357aa8323cecf54c1f0dde365c71fa977ddad090319dcc536652e0b2bc8bc3cf6ad9f721c296c292746e2df17260cf4bb473576f89c734ec583887f38c87a9733cda84b8a5c9b6bdb247f4a5adb3e8641b87aa5ba58aea252f00e5b4ab39c7a0c834d6208d3f2b6846a03cfe8461c08387570360c7680214c8bf712aa0d9e45b6c4508c3b3db69c4ef2d4c31b8aa8e90e833f8ba8686f2646515c33a3461a9579b086ef306855fac15041cf811830f26282bf3054a3a79873b6ee9c90e2ad0d2baada3cc392e73df71a22e51d7a1e88c1d7b57e82004a383f30c311f2cc904fe1af0dab0ad456c4e0fab74b2175850a16b47e2b523fd2a7b072065fad39e18091c87e2c84174a0cb21a965d6123ec175162a055dffe024a0dbfe50e3106d416014e3198d064d5301273c38a017471d1b4fb8a22dca7648b90bc5c0deb19e5f49f1dde1be21783a552c5180c5e7d03bb8c41b76b4b41637008345cc0852376cbc83377b805e9693d06faaeff2fcf90445067f93f6bc4a25f4774373e9b40558f01623d2ab69797c0405d8e40be91aaf7de00476c45ec1f03234bbdc9f8d4f20467159293703b0d46a842fdc414d3313682d69fb020644031f75bf77434b2a1c0daf2f10770460615334439d0fa1f19d48c7fd5597fb6a74fa22abcf881c5703cd6d906cba6c3e6dad2fff9d46293082656ec2b5e9201290c1d7887be0f28c9e08600970cf46a097b0d45a68b555c5666d04a9d4333d8e3f916991ed77f2b64ffe1835633c867c5ad814c2d3acd00aa6935866d1370369a3a01fe3cb1a386939666669d849f3c7a85202e4a2390308f90bfe30c0db8d98a14aea7780d281bc019646b10326aab7b82ccddd6f3f1c0bfc019dc0897fea9a54310bc4ca5658f7e43a8207e86a50cbe98abd385dcafaffffbc09c9d8a07a094704b0d1d94e9c1c5d04a192ce1a07c208b2a99686690a10356f9997cd1061cb27193c6bbf3d04afe99ca06525f30d552edb3b10c1c54ea1c8774ecbc700e257c193832e319fcff687688e602f61403ecb385c2edad26340345644fd29bf9730b57dab0b49216e849b55a2a493f612143618f412129daa280734e11449f10d0b6debc9c44507c6f68c4ecc666684957635f69a6f1838c2791a44a5a53006d27364393b8807dd1f803b0b187d9a305e60c08370080c11aeab0138ba027e70e0d46ee2d857e3d03df06c68307c508578778872460ce73a26f80797e9c24a087649cee9c3eab937c067aa5babf26135a130d9bcc11ca8e3648fca2bec243e4ac07a98bbf15d3bc6252257ba143448a1ea26fdc1da2180b881bac0190737f673507b05d0e6263acf65a0f5c70b7cecfa031b11241dfad57f86750d449118237bbdcc45112ad1340fd6c6600810603111e9e668cda4a5b7fb4c837edebe0de90cfef21fa3b1f10d57aa334574f228128bdf0eadf725424dd743db65ace2bdb1707926e08713d62ac009ddb438af2a0ddfb21a22efaca58536616c5f1219b84aeb4b9046bb4dcf97ae8153e5aa627e811434c19123efb365006c148efddd3bbe048071053b8b7488b037fa18443ff7ed3a13a8242e501794b587f59606cac2df10d3ffc8776166646ad7b2a201c6a2d9c25a20252238355aefca56faae146b1749c354ec7a0ad06458f3cb4474daa856a0ab0844f0a49133af7919da3efea370bc401be9fa16455fe520e0e9f7d957f90fa124110ad1a4a21c7cff050a03f060a8f8661349b098b64028e97d05290a5f3d99657de25d4e6a4d5939bdf4ad1dc14b4dd7f312f000cf8f7e4049fc47e63643ee3254afda6f3ff4507781573c4e6edc720ca539b22cdaed6f46aa0ef0b2990c1322adb7be9e9203a64b0880f5a40f2fba0154206840e4c6e405f6fae38c0ee0c1fd44157ecd652a27733f02ddd7c1d74450950b53fbac380a6f824233240a3e4646e11f141e92a15870919c8ca900c40534ae5b9307b4cc2c28b9141c0e6a6ac3c0cd97645b92dae25f09efdd20cea049294f3d12b845bb94c879b1afd9ef124ad27872d9f16d667390fa10a0495bbc31f40d21f180d4bc22421e840077cb3a0e504602144360c701e7fe029459055a6c45afe40754dff120481fc749ce7d0bc42847f30ce5e6f4d06ae62f05cd544406160db5cebc639bc0ac7333d962b3a7c28f03a57f6e4ed5660e5c9529301c1ef4f2b709363eb9d409feb6450364fb841c291f50a84115cf28d5a47d5887f6ab1e55b1842207b0602eea14f58c267906c37c67aa02f37e766d04f43c435a3f900eece8c58876cc47d6ad5f2ad3e81a4f2a7fabf9e008a06c2cdb7006e95e36901f6dd5f79e88cab20638bcf5f4493f2c0b2f435bdcefdcbde921c07d5ddaca85a6490df3ff4cc780ac9df9b48d4c772641990183276646760ccd877a206ad4481e420205e82801eb4858a39c9f3f6d477c6084bb341d1b648a0c148ac02065f7dcd44c83250c24eb555e8bf885519dc2b860cf4e0c83616ffce01b562f015d1a746ad2a4f5859061634f96484eb5b0f45a4eae98069ec6e135eae47f83b84f6498581bfea917f59dfe932e893876af5a591e0a0e063eed8f8c195568f7b4a98610f8e181bc2802cf7e57b4438e06f961afa5fba7ab47c884c0a33e012159055fc15209c64550c3d4478f11e2dd93ccd54ac7fdf2aa584e299e140964c0710969da706cf512a88b7cc189fb92cec2d9be6050920dc1cd0beff6658b32df0a2ab1e86c209c7f43ddc5d4e9303ea49d8a8f4ca0521bb5225b5907aa7b2ef15ba9adb80e0660634c9074527967c0101567c9c871119bc13888f936f2d0ac2116b9e5fe21402453380f5b10272ba0193b5ff7aaa863c788253c58c54093ab4ad1cb362ede86a01338707cec64a90953e3230df648d94df6f089f44b7008f01e9ed9d1b2eefe37b2a13dc0cb36bb182cef0974faf589e586d0b39ac905517de5f3daf71fa871a607efcc73b72ff2d5f8bac0c84014d73794a5b475c662f557f539f9908b23a1738a173c3c652979501a57c05831a3d65a51aeeb358a29d7b90c25d1428dc91dd1cbb109a32ed6de18836f81c06775064365f5a06b175d8fdb317f8c17dd89ed4a700886d3f59c4a2979b623f1193758f8681f5d5f8437e6861cebea22ef657fb4798dccc05669091133340bd6375062f03d258f259110cdf13777cf661b40c12a9c23998159e2468bfc3caeb6a232f83ff1022342841c851273240cc0084eb27a3021a791e70ed6a4a593a4299eeb24fa4f3c6b85202f47283825a9519047636c5577208f2aa1e0846f0ae000060b01e47623383491d7c9ac6ad412efd4fb7d186ead944bb0bd7dde35638f0c34e47cdc232ccc06db36efed21b076aeeb259fc325a2b4c984164842430a627d6ec8308ff0cee4791425c0181065f83ac4d25a312f0418a2cf96b3abdc631531ec89a7a20d6e21489b59b89c20d5e8eac43e60eeb61dc9d08a8bdd7b92aa9421fa7e603d08032f1c655990e31bb6bf65644ced5774dd86216b218a8640b70db4e02a1f83a8ad753702d7caa11e5eb29b886b2d711a0c1e7db04a03a460ea08150c6b535515a784029b7cf491404b112125f4f92151c2f197ece326464045b0ebe7102346878ea1d112ec17959230eea2019e476637beace82069f9fdb6c4c98bca2941de0d6b4eb58818206d229248944581850622004a39e0e5a41035b6310077d7f2712507fe2b2f802547280b6bc9aba9f82063abc25e2222801e4580de96060ad44300c4b1d1d5d7d1d5b04f42d476ea8b24d4e48969090b87cbb59d0207d814bcda1e5e1969abe101ad08a0936b18b7808f20e88d3caf217d854498977bbd41448b18a1f3f81345aaafe85aa9af66339b093d2a786fd671f62895c43039aa374f3d39f59ed1f65a7536969d2a48c0c1d13c307bd6d684035012103101d513fcc260c1cc86d018a826ca1da288a32f7a8b1470a93d98c47df93bae4e51e29c9369e7f3c8022d0dc433aba5762cddda3c34c3a8b0aa242960020772f57eb9d7d6ef98c0e71919368f053f9c581935b9f44c7867fe058f4f4f5e8ebebb022004ea54a086e207c508f8eae4e3b023996c3b52a2a2a4cfc070f40ef4dc948c2f621d1a0cb8e7719836f2a4b308b602342896ed06559fad5ba68405bce26d7b643058b8b79a22b8823e42eb51ac3211d3305fa1c433ace314c4f47e7565f34e07117788f55957b52c117cca74825110bb8e7303685441b80150c363e1092e326b35e17c6c524e486949918f3e57b7cf018627016c4981d28efcd9fc36c0a947a0ccccce4f4b8c89279cdd80d9eba069721430b94f04a75473e25254dfa0cc08e630e8e0644e194769c964d5ad3a2e2aa1c0d36ecb7ead5066f6b1628d76ddc1c0d36c1a634bc17a808630fae36561ab21368b74a3391f21fafcdde242630eda6794d08a93d19e3a7ad3d65cb74b68d0d8c1918813d17515a78bedb8428d3a019509531e7c41bd6ee1309268d276e670a41708c4d888f8893fb8d30c803954e396da5194228ce7a3583494801b5028ad8b5a0d122074899cc29fc791f9e7b803c37b319d50cf00a1d2b056f358972b834140d0b60d566b0ee33100a3616f04985fab5194c29836e06904db36cc24017db76d62c7d055b475291a65727aba1ad6e06d64673cda2d0f5ec01f5723696ba19c47cda373475fdd60cff990491cf4b17b81936f737216b4edd7cc02e88b09e510604513dab0ca8c41b7206f4df984a2297874c53cfb871cab16cb55a0be8d2dd7e9cd48e25c1a3c17136b2661a47a766e6ce18bb70e1f365f4a293da68a04ecfe0eed23b883e31f2cebd260eba022abd150854a4f73e0bba5f9965c4908a97def596398e9799fb276ef6103f97604cf8b0840821cbec1887eb135f20e8a3146c301741ff0bd721d5e896fcd305aad3a92d19988d9206c13b1483e372162bd8ba01352a57153c5528a38f927c8002e362a7e0f8fefe09063fda5c626874e1b4f28850ed7740c1b8042a9044b77355cf8c8875f42aff39e9ef1a77f462136dfc46aca3159d2a4bc6c504a83fbab9ea8afd2dbbd03e2f51f389360e25a9d2c3e82bd0388eb0157685ee89c626552f1b19e98ad53855a405695ed1403af9a08902ae4e9773ff257c1a964e9d7edecd4a2a801d79581afc192d1458738dda861f032b4d9c9ef848dc3814793ecf3867fec23f0176ad16dce55998487ebd891c3767017ab1e5d016df56fa659c5eb16ab8fb54aaa04522faa53aa8a62adfe58627b224deb508aad226265d5ab199a57ad5c32dd8fd802ebcf2ab05257ddbda381d25fb0c9ef05738857df4f3fe8fa0f2140f3a8bf75a03ff4127e7219d09e3e22e78cd43da5a20f37e79d53ce43bf55b5dfc8a3fcc43aeda6c44238b46794f3408a890fbb04437bd479d2a121d0878dda5192e7e18eb889ddab886f41c529be6b5c1a55492501c6a0fd3e2a214e7e83d8474dcd690aee46c0de9ed93ac0bb66bb977c994de1e2efde11cba3b49995679bae8c2f523f72cbeece59ac96f27a799f8ece69ec1cb9e5cb3f9ede4348b87fddc3378599b02db2973265807effd221a8fcf7170dfde93cfdd8e71faa1b716f06dbc0137225dd3c80f8715b04fa2cce2b980c3a5c386a65e5eedc5392d16dfd4d702f93f23bf05f8b0f6e3a66d672df28e1b06a5b120208511476b460c37451b16fb12e6cebdfafdda4054d7715490ea3f9eeed21bd8b0f426fd589e5b23ed28e6e405a3e6bc410d340e362cc3fe3b799754e8c7d62f8d7619ae71089adac2badf5c7f114cf8be6350e4e67f015ab90a832ba551e3ac9485262fc7218d5053f4953af72a6eb197c6a02455f9e66082a4a8e3a020a5d717d73fb13a9118a991867e45541147137a76d1fc722f48c452bea1112567e8588e0674e017fd6596eb12e4f6663ba3583fa029531acd7fca446d1f1c9ade2c40790277d66094bbb8e30ca306f1c00a61679c2d4c52f4bd15747d582080995c75ff355e94a3ad9d45684efd22da5decfabf7dccb452bd29aeb36066baadd720d94da640e4924d0864bb586e8cd359eec8771ce764d957c9155975a0a6047fc6727714f90fb8146b042890055840def4e5a0af729dc4d4a20ef7a3a6745c5552c836372c14de373f6c2d11947cd615378d8992bcd3ca8cc1c96ab055fb6ef48de883c9af4a561fc117d109d10353a51d155b03c34e47613248a96d10d585275e37567187770aadb75ae69d159616bf5c21d8e93685cf3559c6b26144ae5e88400696e18efcd3643edcd080ce480c2c37974b06e32bea3ae1e4298a5f2b9d8b4a61da7cd56f7a60aa36c53e697cb46fe09e0b85403db0a87fea22cb4b4f1ae409f5cc54af02214b98a9e33597b694abd5a571835dca177c0a2f13ea297c5177b3bd025cb2bd32f88f3df02aaa30a9a5603e9d5e04ab930b5754f3d803176b6d98987d5460c537287e7c0dd15798ef781e5acbca1a6f77c64978b2fe572963fa2a27bc45a91b89214b5991c7a7083fe96a299e5f593af689744c5eb3149ca5ccdf3aaa46ec205dc6a18ba91681942fa04286b621947b7a6ac5861ec83d4817dfa2e7c15d8cee6c0ea04b8e3884922e7643cd1a3e5168957a5ed838bfd36f1c38dfcb75b6522dd049e962ddeb664160b3e75409a30da7a32f8281928ea702cf62066ae8a678bf61d026cce808db32fb309f3a54d90f884d71d17252f4ef69f5dd8da18dde6455c3f1937b337a88ea1547f84d38361defe14f0b94998c0800d8a78cd5ec7c26a5e03a2e49dc8cd4c42abbfd37cd97c4db7e9884e295e8e554921b7151b214a1a4d2ea5b445be9d9c096c254c6d5af96c0841d40408ac20492849f777274ef0f8d9a4f55c651a11864a928ad7cc450222dff625711cfcab8486d17669c605850b29122ef832b43f99aad6c0d60428ec885f7331f1c75150b6f95fe05aecafc57f4f8f199e41788ed821203663229e8a24931fbeca879ad7b4fe6814f5a35920abeec8f74388534c391e8c5f45d8e19bd4f7944455383d3acc104f94893c92b33e61378b07a84caaf29947ba029ef7f27b5d0f87881c050668e24711dc89fa3558f8ecbbfcc093c0f71f3f80f4a8e821d8dcf0e1eee8c6da0ec3fa04945e4f3605a14de67fadc234a54d8d0ebad37170308e1cb3180a0183976908492fff42f154af6ca8d323293a23b8098ddf91e779f3254213132fb3ff66b121a0ffd4bb9f690a099371026d10bc6135b3fcddf006b684fc48f46c971155a0b42561121cddbfbf454b8f3bb09e580a223ddbe431464dd3044d63f20ab7f3a9291c10418c83dcb652c143e614434104bdd4a3ac8961d3c29a12fe8096920a7b48879e7645acb898c53dfb0a280d95533cda30d486823e3bc3d9292a3d1df5f30c23b64e16f1cd565a492da5fb176861032c02229f936b01cc79292576a4352ec9e3dc267463a2d7db62a26f1a60f183928b30d14db876b19fd9820d874a94c5776f170c59be62d56da0bb93dda4eca039b14474c9d820dc9966ab0c1d1c1036645d4be8dbd7d7f6013369898e646275375ee16b2925f06f311b15d1d002283b3d71c4dc0043fa22154380be3322ab2aa01c45deb72d41b6c7106b4ca3dda592b15ad3e71820110816dc9ddb75e462df9f5c698d5b121dccdda98e5c75c5cfb45bba5ee1eac18ceb951bcd0ccf13230c1d0721e7e893598f2eb590a5b8bfd568be383c0bec39ac8025fd28f0dcd4132e13047958f0c84b1381a1afd4ff0e60620eeeaf2ebb9c10b9fbbf82959f0f5d03e96934082686164e8e8d7bc2297620c16624470bc780731583f255f482efffbdc466b5001e8cd3b84c29980cbcaf33cc11f2a791c2363507ec2f78360cb003b048a4a16abb640a368c1d0be87f106490416748f80322c67d1be5f4f282a7f7ddda3920b48653fa799f8249564ed29510756a9f8158c69201c60977a34cfac10d79b3e9f187b331ef20a5810864483af7cfacb34f5a786965e4b01a9e3d2a408c9339071c89a4a0e147b62bf6f0003110a5b657eba90dcb3130367218645f81db6cbd7e41980fe53c9f792d26dcf08346903f337eec10b814afe4a580f187d8af327b83100206f5e468a806e5e471f2b49aa0ab14ff54fceac09c9ae7f72076a2943815a903affc2e1c154255b0767080d5b3f536edaf0ecce65fb910990d6e2bf3503427e29176b6816a492a0a5294955b2b773605ff3ef9097476e1881eab90f5d043904910161d63635a6218079a6bb1c456a3f4877765b2637f6c2085b379fbb7e2acb1c9b3fd0095d4f78e26e5ac1dd4979283061542ce26388031f54a8917cae4683bba7ccac18bf0710fd02c8b72bec861878064ef18cb60934f1261c85c32945081315717356c9b1e5ea101c74e03063b11ac0dd578c3fb58fb40af25437a08ab25709ceb93c2827289116ae0c5af0e0f44085db37879ec2f306340a18d0e8682b915c252f3ae68a465e255fbeb18c530576eb8acee815ce1af3a1c2abf874fed46e9d51d90f602425f24fd5c06bcc4b8a3db7dc1be7b4bc756379777bf822e53fae7782520b3a6f5f5f99e6a3edfb465b6e86826b63ed8f1cb216b8cdd0e909b273176a9367fa759a43debd3d10ad2ca3deb7e6b9a315bdc110c92c84913baee47856e4907259d271e9fa86301e01648718671aef63df04e2057e9debc1e97d56255f484457f283b77aced5fd3e20fb4d0251a8a99e5f519cc1b61ca0471dcb4d9c136331407c6be09008fedc90dd8e0f9391e311ed37ad25c3f9fddf52758465a1848d0f2241790514a18ad3f9aa75a582007b32d3467b8eba60fbe6dfbe3c765fb03e7579294426d02e862575eb76c8d9783744cb4ec2c4c28d7c7b16cb1b15f9bd1ae6452b5308e2f9694f8481b9db635fc904b5f05edacc9efe1bc72e16209cd16516b1dbda88867de5725a82d99aaa7d5c76ccaadf162813df80db771fe682b592948356983551615b3307e09250a0a29863961c0fc0b959b21ecb3495f6fcb139589afe8c08735b774b8bf031f79a8e697589b0230c713964a0a4520a98478d999aabe372c2dc724ea2347f77d7cbb4f0d9ff75189ae10c019d0063645de371d672d43a55ce0495de17a87f0427b79b1c66e37045d292cf858b830e4f662037cbdbf68cdc13032fe26adbca9747dfc0c84d748fdeb00fe383fa739a271637b6438224a2245d3a0ca1d3f3c727d8b1069b1cca533274341797ed101d20fd41210a989f701022429755453f8778c137cd328d0283da3cc4d1ea0bfb872c591fc6b8a1d48c75f0e51e49f9263431c96f53641c5c65c97403881456880e5a3cd573423f853fa80a5eafd98a73569cef1cc9e744494bf17f04791ece487e3d4634509571a797ce178a86958ae5979fc9c87a0ff98537b02e163f27b82c5050165133d3a63b52e7b17f620638a8120edfa93051222cb998816a378ce58c4dd04257b5afaca10233237932425d21d9e4e8bb4f311cf47842e8aef6b9d146470b7aeddcfc34ab36b12547e78c4dd083707fc3e3d4e584c54cdbb6e483a25ce2721cb884536ac71bca1d6dbc9e36f50a4b1a6ebad7165af8bd392586083f92495c22c719fcc40a00e67367a0d9e77a294884b76451cd00ff515a6de76553498c0308afd4956502159e919df0962c70fd057844d9a3f00c5b8d7a6ad4a8d5cf33e181a28958266d6a9c5141890a317b4b46ba9f34c5603b9551cea3ccc1b959be375df7a70cc4c770a1bea4d486cfaa5d508d7e4baeaae92f992858f5560e2f326ff060ac09daa56a6f3ecaa65fefdb057b866625d100fe3efb39e114bfdac7126b0be9ac0132f20036e551053eac3e0e74655a07fe3e2bf56f8b43e0d751996c932943b89ffa5c4fd966a7f8463b1ea0fd8a5303c53749d245305162c1c0ad68ddd8b8046c782c701c15ac2bd6126bd3c68e05f2aa558eb858f272e7742b7558ec9c90187477aac49ba691c33ae445010d7ed32c131052508090ee4d20a3bcffd317a491d45d8f151c0f004c56ad309d556413814c94dc298b1abd7b068c30f30f00cd62ccd41edafcb17b6c6de4e6dd95ac0d17a41eef86a84d5d701e58377ea2ae33df494e6b3cbbb7910d2ff0ad86c1e56aeb776e2026f6f9617594631262d89185ad25e080be740fbfebd93722841df34a768b5be2b443117b25bf6a404d4cbf8490c0a2eae778453eca0f3ba4979a1f09d9afbf4803c8531dd094561e30466cb0c90cec83ec86c41235f831bf51e1b861c16f94bb6af227df4a8989f3953cf7affc499cccfe1da55b236e1f4c0699bf0a1522af33c509f94aa63d2f66112dd97e265522f9951cb1959f85f67f25472e2246180f8567e5fea5fd96e3f6af64a76085f8a811e6b1e23394f208533318a551de352755f2eabe49a14e776f52e42250b941cc4a189af0010d7925f642a057e67708f5452728f61a93278ed95204ebd5111460215a8de8e8d1e983253bc8d20dc531cbd9677c77c119ccf21154c2d060be8f20092cbb8ca1292c99abc61524aecce6d30d7729f1ac72aeead4a46694c9a215de175d36f55903fc0b7de6aed53921b1642f1a57085cb3359bb2dc55eab5cab803f5c54ab7d62a21bb0452259345617ca3e22e061eed76436835a176320c4447b906c1545d77c4c9dfdee9ad53058d7e14ccc4353630c2b1bcd32e8d275b2f461a9bf1a9af45990bdcb538fdb05c4f6737aff3299b05367a2083f4b064e44505f9429f8ffbaf08c9098cf663c99b61e569bfba3d19150fcf622865de94da3e96fcf4fa9bc1d30f9eefda02c5f648a793abece4d6c91affc5105face3d8fc95252a361032fbb164611ba8e545b28ba6056ebd0d17c278081dfa8498fa1ed26b9694c9826b3609597bd082c8d4baa68d348f358bac6ba427bb1285c7baa7db3907857a3b6fdb2b4d044e68c4263f96acfebae7ea38feea349289c3238ff124f1442ba442164cc89bbd82820f6ff8556443563f4a1fee4243263eedac1c18f864c9f71e6e0f651976433ea8270fd3d3a24304d8b6fcfac8722c7100773f61be2cd90da32bdc032017e8ec32a9b35d60e55a9b073b2293241caf5c79bcaa1ae441e6b078414c6f7b488cd5c8c3ca58f62eb1285e65253c527901c21c12b8c3a6afa7a3abcb02f8cdb63a4b8e33344242f3bedcf7649bcb533a8d5ad81c7abb56bf5d98ad7fffd4ed6eb3015b93711121c21ad7396e08a8c941f66100698c7f2f781a61790396320be2a378858deec0640cba2bf282417d702dbff9afe8f462c63eb0d5a579820d546489a3eb62b28f3f25ed869c835d271a336639ab180b8d60c974bc6793f0431c16e0cae24ec060d35258b222f3b3cae7d909c4a3f4d3678703d7fc67df5f0c72bf9a39755772ca7b1fdc373bc2583a63c97d23c9850d37b75d2bb7973fe75f9d419d4ab07c3979c761c590181a6903c60d403a34548cdeca5f9b796f4ab487568f387c39c52cd9fbe776693584149b14c72f62860d13f907d2ed0c8833c8e82a5b3fc774b096253b7a3f7bd5f2008c7974bbc656fb3386f21faf8a52967cb7cc39780a5dec88017b3c4475b19b4f3449311966a50a6a87df4e6ca4b452f984a999fe25200bccfefec9de78b053846c1d6075b0a2c95312b225b2a1b3939f211d6652292e1f1afc66c962f3938fc130229f3e359f9834d739c216cc03f2cd98a60f71acca16c92d8ebe13088da336c524a3f642cf991fe6b17ef6ab693b549625d721b8790754df7ea388bd892f4baeb6f7e963f5f36ddbfbe074f6f908e35afac5055896505565b6e4fa532a4d65a3125ba8833f633c74bada91c238efe89c3cc86c0f05e03a669b05ad1a86851fc5c8db9e8af9853fd4a3508cdd31ab5c4e44b55d0752da699968c168d8bd2f8f107c3935fd462592bb86e41838535d50ab550d56623ab9badae781fda51e215582d593ac4d129854ebcd59133179818a0cde3814b1d1cb0d1193b5793425d02feb2eb899b7b49d0d13063c108ad753e03acad7c2478d29b90ee5eb28c245c4e442910c82891e25f186621733195aa51be0bd0e36bac09e28a318f355d1ccfcc671ce180198ddfb54d7414e66ad7a0cf4878488c954534e015f8a69499aa045b60f1b49eeb08ec926ca07b448fad03ee70a3578e1eee75bdb89e6abc8cd71661ab39c6300e997295610058d58519ec4e44808dad8da0683e0d34e7a9b8a4b2693a5f7bb6754ca671c21abb5f2a1a46e0cc018a80a0d0b39606c1d0c314457475f57878e3101be5b0c7c129323cbdb77a909e33176af1d91be09665e4395b095aacc24264b20991b2cefa83eab433d50436fccbe29d8370ea78f834dc7986c5d957bcb4293536284526354f71bf933d12a6197d9796e5e374c20037f3a33c174efeba5fcf0e2990622752beabd68f1bd3f4ce98488ea5625ae0c0caf4e13314ada0f064e4c4cce93906db6739f31831b9030f134b6041d010ae36bba86f66e6863d5e3f7ca042feb41f04523cdfef75c9948c3d35cf40c4838c67c2ecad408c48c7872b66e093151e9a2c9fb9d83cd3348474fe70c20c7001bc5e40360be4239aa453a140464300c542be5a44d5e5e87ffaf7346cb0db4c8490a9db59a549f9251931cd237d320522a46cedb78fdeb5b8970208e649743721dad0641633e64989e8e9d2bd08ac94568714c14e6dc390addfbb3d6407459fbf4df7f6d225a27ca90c19d2969c188965a1a28c4be727a2a45425a4252ea2500c2edb0176489044e4404010f521048a3743aae5f281834060b8213c20c4ad5cc4c339eda36104e2c160634460d030ac315a4440030ca288f9911890060240200064630378c087000219e71ab516da4fdfd50a6c96feb4d4bdade5b4a29a50c290e4c0f5b0f8ff66821071c01c099a58109a242db121312919dd112124986e041078c4094a5a39ad1f873442b600c84d700bed8bda17d76cf81f3f1d953d0b15180489d59731ae2b5b47701bc7eca7e5754519080b99a31d44592f10939e07c81b193800df4f7c892322c6ac050cba135017ab80dab6aa4c8321a03b43476c9311605b40d517e0531fc3504973152ba8cb9f970cbfaec395e707c761d2f769f7d044f871e4f1a3ebba9b5080468229715af5d6b2e64af1d855de69c73f6f1e5b3f0f33dc0dd5286f664c04ec889545c0c2c13037832fe0a0084e94de8298718dc0ebad566b7e5f8da45acfddd5b0a8d4d72d370b2c09d69d1cad28225ab8dc9673780871f75362178364c8d8dc98cadcce713589e6401f2d90bb04b123e5bace8006277940245d6598707920d6f8cd2956506290d879a4b329c73d1b6afa55829b689255a2e892b5e234097f0b59528afa1bcbe22f59aadf8da07302cd1b62f850da6a4b8176553ea9abe760d90afb53ee17593d46bed66deb170733a9c90f952a338a56a5da295641b1b75345a8d95f22b88cf596b433e3ff9eca6cf674fd53efb30abcadbe72c2b5e9ffd041db1325dbb7a92fc157bed228e5232f42426221d2381f9152799f02b8602533ca60063f774e4ace7c78513343832183bd45b877d9e242f9ada93f66b6ce8c9f5b295f0eb9d8b819d0f5696a3221e41ea7adb9e0c22c444d6238dce4a5d2f1c365e19bd92fe2a5da627fd3ab599bfde2710c2a72a73562d624079a2ae176a4912247249415cb40551d73b558ae10fd7d73b246644ba757bd25d407af2572bd78174bbdab95bbc965c71764a280c423bc9a5c2eb4901fc1b8fb92129614f6490d4d51a9f4c7287c28a11b7383058d4d51e9fe412f27a5f006c80c346591150d45b0a35c48b313a256926ece0b60fdec144f9c32502fb55e8f52b87cf2c8a3ee75cc522c9e716419f5b947d76137288307ea2123056c33bbb88d1e0fefcd3411049ad1697d15ff97ec58eeff55c00684f2f4dff3cc5c019d5ec0148c9d652edaf0f597d59fa87dcf31c7d85774aaf2119bf5d9360fabed47627a7582c9a30168bea852b163ffb08684f8ec23b184a08cbeb10af1d00688f3602954378a65f26bda6370e71c30abd1283eb0cbe89ab90b861051d74eda9cf3371c30a24be475c35f8d0497c5996c75eaf67ecdddc2fa75edfac1a6ed6ed183060c08001ca098356ca90c1877e66c89041230d87434d34bcb99f4103e50ff7d1b4185cdbda5f34b1e3df17d2788d9f343434c69a9a9aa99a9bfb3450f943f0e6fef9fbfde6fe040281487f696888fede0766a0fce1deffe5221038f770f9c3fdfd701b1008c46bf9c345cb1fd05eaf979dcef3cc4af9c3bddfdb7bf388ab9e2bcb122e7fb839ebb6bffac307d71f887a2d7fb8f7ab20295ddf4e627073c5bc838d452092e83a89a4443acd5d2f8ba5530974bd24da6850e4f1aad7f754deb1aa528904d7b686468a19ffba072cf66bd735891a56609cbd4975791da96bfae48720342988a2e09218c15064518942466e66b063c9dc0fae281e49f7e6384df91969a97137068c0d1436ac0bc1e8807110b3210a22dca08aa7cf679f854e7cf61052206236f77248fbec3afb0612a248b941462cc6922612b09ed2822dd09a5c742d3db903b2f7a200c20df229fbec1a32891e484698208122c30891baeaf47a9f2ff147a0d2f2d91990e6ab1c4d4a7e186139425273ce190060c99c1cc09afd3a62ec577b6524dd27ecc92d24144b8a7eac3ce93a3c349e7415528079d21f0074f5a463b06242bd359b2a9f2561121956eccae4cc98540d909ecbcf3903e0f3d2671f33058a8e8a91a8216af612ec5c83f6649fd6afc210bf72c0e9fa15840805bcdc934fba99263d5965e2ca93eea4e7c478b2ea8495273dc893c5489934322d8ca2ea70bea4c64848fdc0a2a2c1c20c101397f4d24791a6c4d488cf9e6a62d6934fbae98454552289c3670752997d914fbaa9f50f4ad567474d10d6e507d25091a2290f25508b8522c94b5c91129416b524a6a5d85f2f35d1f728ec9e5d5514ea9188a176248b4ba392e46477205a79a5248a44861cdf7e92b66ea84efb0ecb089fdd0c6223bd8361b5ed82e328ec0b0aa13030374e6e2701ace26408d159b1827677ec22521cf1b3937abfb024040565a9850f92ff209292205257a648891f5748cedc5ddc459fbbb93e7c76ad77b14609902b4631209f36754d7771884574f0b1f67bd2cdbc9342941861542dbab6ce54521fb5111fcb1c29463b93948f0f682295c5a484929914ea3a5c40131421428cac92185135407a084af080bc83d1f287eba46311223ce93c584408222c565594279d015844f9a4a779c79e05fd680197038809322a793cf2a1a5cc902c415b2a495c5bb06dbf89f878a594da5fb1f1fa3616d5a3495454fabb9df47d2a3b2b8fc267bfe663233ed614bf84cbc1ba4b9393ed2ddc5be20b22ceec1767bdc9d2e4f9b23d7da8b5a74fcb68e4f3dd7baf0f85416ab58c3ee7989c73467f18d6f6defb07ccf056abc916d0579665094c3dbc3da6984a7d3c1e8f97d2f49ad0f8cef33c69825a4d8e047d288aa2c19aa0921a1f100804d608bd7eca843e1a1a1a1aa18d8dafa6a6a6c606835613270c3e1b1b1b1b0c199a20693519d26a527ca1efecf1d01f061b434a86febc3e9fcf4754215fff01c91ba0ab807deec3bfdf6ad52ebe2c60113e167c3e9fcfe763018b08b27055041d5f25f87c20d047b4f1d4c6cf0cdebb414bdfeff7fb112dce61e1fe8f851a07faefe72bcf7fbbe7d985fe739b1b8b3ffc52ffa1ae1de8343734fef37b13a491f688eb0bd997896b0d4a5cadda75fd9af882deeadbc4a0af3c62d0e73b892fe8b9deaf892fe04f892b89ec230efdcfcd9bd27f3758e8e84dcf24aebef7f9bc7763a35d7863e3bd9b53e82ae4f3fa4c0cbe741dda3840fb5c7883c16d6e84448bc177d3f3d3bc597d6fbaf046e30fa6f7f28f97b1f36ef09b37d9cb167c2ce01f6812418cfe6c5d81179ef49dbdd41fb4db17e08c394aeb96b8628c7149e69d6fce58043e8158e7f6edbb26a4ae77dcbc8fc3c1ade38b9a90ae3a3cd62b45f1b187182174b11ee77513d235158ab0378f775cc5fe00095814f2eab36026e0ee86f75d1e24dadeb2bdeb9beb5a6bbcf6eb0b2a60d224319226b73e22f78d36d244fae89e707de700496b8cc91b9f793711a933e23799899ad854e632fff5e1757bddc42c4e4b2c87c2d4675e274bd3124dbbb630e2affb9c26694d9c834dbcb35eb35fd3e723cdbeea5f2f79af0fff7a89c47dc92c266f137b373dabf6eb0bb92c795a97e50f5e6b6b7d3b79637d93ae6f5692f44d92d9ed8d0a7906e5e76c5f677b4d222649d7e525ab7076039445e9f5229ed5f769dee940e4803f60c797c3e3324949e99a833d87e7258df43eefbebe29d06f9a0a3d67f0cc4104dfe02080b0799eb387900322844e0e881028eabd9bf585efdd1e71838d6be2baa1023fcf1b6c882bfcb2fd17fe7a6fed390d06bf36da730550ef79de60435c01750c376bcf4f111c15c133314803e867795111b4a3c4208d54135bf0b9452b208267e20b447094f882103c7b0b3e4d6cc1f7221077087ef242e0907a26ae1b4000211d6af0a186e1d087bae7e5d0b3d630f4d4cba167dd23aee9f0e60c4362064f33649b334d53df5e937349433bcfaff3fc5eed429ed0e6afff56dde1665f0c7e6fd6167cbd9b1b68a10b89ab8d6bd737bd0a68e27aefea7b1cf49e6b0c2bd838cf37b1055fa64981be9b1c10264f27079f7b90896bcf4f3fcf1b10a803b2d639405d05ac3fa3c45b816de2e7b90af841e86011bd33c7f702d69ad8b3dbb421e06092af18686fbf621c7fedf0d84d78fb1503e953e0b9b973ddfadca19b373cbf373f56d3b5f79cc723f66e822fd2d2edf3bceb8cd26737af6ad1154ccfa55f9457a22b94fe82d28725895640afa06950da13b4bf307c6b4b18e2125d21fffa832f4b37cbd25f309fe7432c22e7e7dde019e42fbdbcb9b6843b344bb7c620a6b7c6a0a8c72ec4223694c419dcd74e83fb4d77153efe8a01f41503e87da9f3eede41a6f7f28ad6cd5f75da675f51bb7601fb21fbbadf6ea2056c09d9b35a4f6e92f25ebb0b5ac3e98c4dcf79bbce6ac532fcaaf36257c9b5aff7e94bb740dec435c39b6e4bd0f8492736dde0d737974929ef5c22a55bccee020eb41cb910c37284a80e8ab4719e37fdf0c6b776e8deac3a7ffd2c8fcc374223b36e8f3e6f8b258cd43a897cf1cfb9374480f8824ddcb0ddbafd4bdcb0890dd0bfed63c77fde54dfdc1b1c31e9081e9c2317e2b393778eff85cd57d6b3d19fc07dfa998d30babe00bcfe431d60535edf782dbbc84658c4eeb9b63c6ddcd6d000796e6fd6f433ee815632eff65e0f04df660578eee222a067f21c03311bd5a440dddb3e014bbc20107b9bb881a7826fa8e15ebfdaf67a2078d63dbb896950b8ed0a02516bab35107dc47d5df79c3cf27937f6cd7b7b19f7201359257bb1ce48d7dedece466e9db866a3cfce73dbdb3debab4934cd34e3ebd9decd23f27226ba27ec9ee79beb196e4ec77083fabec9f6f19ab0864843c4bfbdf7fefdb6f5cdcbf65e228ff4dc731ef105bd08906e7fbe7f9eddddfafac27e9eef74f4c6c7431d60fd053ca29b68057c6f9db8beb0ed76df496e53074fea09dcf5054c92ab6f933716b36111f6fed66b1aee300daf65a3f55e7bb3d199e370f8b2dca587e04b279dc8e330f485a0059c10b480a385237cbdfa9ec7e39dde028fb71dd0ebb5a00516be475c4bb727eab9e4f934cf01a7a7cf23ae2dfc7693685b106618c23cc25ef1e01c9b2dde381863bc7d38e76abc7142e09cab71cece5bf3f0dc347d763b267926f18ef8ebd7a78345f0fc7a082cc2f4dede9b98a383c3d3c4a69368f10753e3f4dc49310e0892244b2f9df4aca342ce79d2e8b42048ad5691da2bc94a324e3ec0bb9085d4e6072c7dda2e25f1a50474f20e4fceda4760110cc03b5635c9fd2b0f1eddfbf306d349bc49f46111a8eb16a044d2c9f48674e0cd697383fae31cadd19ea661b6166081a8019c23769d9e700e19724d445ac48082040ef5a09f6810456d6aa03da5d07112da2324daf4c2017d9a3aa681f6a4a88720463f5a5f4a60c1a192805134da52b32154a745d54099855ebaefcb975ebe84fbb28402038bd0132466c658a9a51c16166757453bb3196a30bdc1479b1942c732d01e21fa66f4288abe1179d431128aa250c7764408450917463730d3aafd00a20506d891a32f31ee17b0ac232bb441e646d1d35816225daeba824818f901a466c38914313954218cfba585028c82c2b22c85716155e4d650d02519a96bfa5c8288912440d5b021e52503c718f5c5970f061dbb407b8236a589332e63949cbc080a6ef101c367c2cf179159d095245b6a59a68efa99a2692a749b52485ccd22ed05c6075d071d13a13dc1f40643f0a4cc14186a46b52655032c6011a9971e82b2ed4ba8a12fcb1c617e0499531222e38a5a7a098468192c1db7b8556e5d3e4d1db3407bd2122aef60280c3a07b3ce39eb9c669d89607dd6809572ced926173f67c839e79c7316fb9c8539e79c73cf05319f77de207482347d0e660cc49c7f3ebbce39e7bc862118e4f041b7313ee81a8b30c0078343bc3e4804c7071daf407b82410c2eb818d5a09b160fa13dc1b5602a4ca1a07cea3a4d85ecf854c89c90afa54162eaa6c52ad09ef40a7bfd0d72f736368e83d01e1b201dac0a72e48542c729d01e611afcd4f10fda939669f955a2953f8eaa7e60b9a802c2e54bc728ee9694924f5a42a1c247393ceaf6c8a3ae5137514fd1a7258f3a3ee1659a840b09365102edc950249a90d92981b31194a5e5a237d5d20425cfd47650f18104664a0d063592d47111ed497db07814f57b87f6a0656abf9891894b6a01e6890e9718cd52566ed518729685c812ac2f6970c946fd9eb9c72a1e62be745df6c8fad2af1d56ba27d3799ee7799eeec2da9f67799ee7793a0fd29fe8a980f33cdffe3c4fb7e779a6574e743c8a8047fdd6a13d289a1251bf66d01e74158695b9120bfbd26f9912b5722aaa65e997eec672f05f41800d995d8159a9f2611776e22a2c6cae2c97bc15299249c7a03d67aa1343ca4deec4969158873a1b664e514b76692716362c3c12bef88c95619161a3284637a5c99095202628c4c85d7d69f9b2f41b87f69470178ec8e7f3f99c830fec7d3e9fcf87d14aab39a61bc696f50003c753cc7b9f7b6f73933b6f7263995dfdf6fb64dc7be7eddb79bb0b647eef2bbf5ea0316b4d5b45c680e4beecba2437223e256e2cc9b1f7de20b687e0e1fa6deebdb7a67ebbde7b63b133ec3c71fe24f3f9f3248bf2a7a73efe3ccbcabce5980880f7e007964a109fa2fac18b5b0d0051e82519f605400138209c88e178e23526843566e58b56ba62e351df1efce0c0fd22c525000f80f7e0830b2000c41fdc1d619a8aaa0f7e402c7ef00f44ac3424ae58ecaf6720ae58e9d75dc4405c4b241be24ac209892bd91624aee45a4a5c493494b89262dbb7f63ed73e2c3123ef7322e46578baa13d19ed2ca14ab352e94bacaf372c302cacb0a42ea72e16b96c417b360c86be407919b3a548886669c6da9c8caf3914eaaaf3eec356aa7851b24328b9407182abeac8c40b1c549c0f1b9dd68cf36ceb693943ccb2f75a9393bdf7ce58d09e1cc686f69424e86012c70696032ee803483c4c5c6dcdb811b46fb946c52a876f0e0b53d65c9a18aa124f66ecc88ac67e2ca9a79efbeb5b29ef582cc2967eaf60792515c0cadabd627b1d5bba9164061d152c754d7f0a102ca7ea84cc0c2a5dd892bad47d96e5f655b1e18003e2c9d3932767c65e9459f3edbdf7de7b0381fbbdcb725f2a68cf1e009715017e0d411496e5b3a7313e1bc5aa7c66323a29f3d9ef94edbbe5ed8165e323ae784d0ada93af94a0d63b8345ae1835c58c9b141340124d25aa54599ac2e54d0c8d72a15c301f3c692894d1d455d59831aa0b8e22133246a07640b9e5d5c96a518628b121b19426171595457785f54c4f3c9a287dc15146c55728dae9f8ecebed4b331247b8a08c76c72ea520b7bc509c6a58301443a4d2e4b8b404a9f796a13df9eb4802d9cef944c093a2182dc8dc2892a1aee9a701496263256575e449d21519d85996be313561175c9ad7d66f4fbf743c19962bc819fdf8a20b72820b6f6cd19084c22d2f3e415eccb688a9e70dbe2a4b276200e07afbed7abbd9d5159696650a4f27d817367201f992ca0b07453f4e6b4636bb30be332f65494972cc9d75491ba266cfd97b6f8da20b6f8519c1b54f9fddc5fce176702273ec42336ee5d83a4fbdb5af435593de799ea7d6228e9ad442d372f11b84dfdba8f5f4db78cc3ad1b39f23d851db10b0d3fa5230b3628352515775f5252ecc4959a2c415af615d9fdf2bbb3ffff46be53bf70f27beec2bab8c59517e5395c6bb7bbdddebf5cc9e6dfb5e8fe8c4f77abd5e0fad87c601f71c844e082c627fcf79aa7c4f43afd7ebb98dfbdef77abded83855f72f812e7cb1565be2c4b164e5fb6287ed9a2ec4bbfc61cb1528c9c2a7ee9b709da538a956520428c08fa49cb0aa2a2224a48284be84ad61092152c9894687f8b9ffd2e417b7289cf9c88f815e7c7ea490a18bf53b8fd065af2dbaf929c5cb67df69b0409dad3339e3d041cd11c99427b7a3f2ebe34b2f72def8f2fbfbdf72e82a24a4c6aef11b68815d9813445c8f196b7ec05b02b8b0f0e323f37acecad4f983e765851be7b26a0ca4a4af4f5880c417bceb21c21172da2a5880bb12632e8644001b30309c7893067eaaae3986ccc0b9570073b4c9cad28ebe213f582a4aa2436184c414ad4bcf7ded90785809e8920d867c709407cfbf8d454e4f1d41686249f3cd16d7001068ad0d1d8c387effce133bb2a6e9d9550ca2a216782eb84d209b6271d11529ea0bc2832c60c94696e2077bf3708bc9a9851adb919d1a2eebd85d2ddc2feca218de6d711764b766c678c7733b0eea87cce2a4e7df6ebe3425d28139a9860e202ecf84446cc878f298323545a47a624b5e062efed714708128105d784c7de7befbd77e9c401cbcb1700bcf8f286f596950342a7c643d4d47cb1a8f13535353535615f7356545992506951f1d9ef0e97b2a666bf3a4c26b4a7a6b46a11c30bd3993913683987050172ee24b815614938b29ace1c260088f8188e720055685d34c884608135659d34d8820223b3426c87db121516a40d4a9728ef6023190b2159bc8c54b9ab53d7e12dafedd9af0f922564040b8a9d4f1b929a3da7e7f706dad353f2a58d6dcf6c292b5a6254d1c385baeafce96575647218fb026416860564a8d7ab21a250e32659798141020d0148113375f723cc93295d3da00477c0c32d2987d2d0538e7dced061611a987c763de587955b9d8cb23cc92c15b3f06831a4c68631178f1c5ac89c0919a3a28c5df57a489a878b91b02e3f31d26dc53eb87fb83983dca165792f0a47f783f1b7eb6dc585058c74dff206f1edbdb7110e115e6c1740cdc89261e4eac7d4960fb35e88133a7cac5d192b43e2c26acb890e4579e726a7b23c8952b1a52a26e068d1e2dc408a6832115fe11c7c2ccb93c5cef2fbb79b4b3ebffdae1896b7bc59e7cf0e4d34357708edc9a75fa1ab84f565e957c5de245a44b9b80224ee8d4ad7de7bef7dfccde3c7ef5d866c0aac8c8715321f757b09cab22c6bf61984f66ca1141c1b4fa0dc78caba6154aa0194d36386c267c790131456aed60f18553784bac8a08c6151963745960d16bfaf7ecaf48ae01483b465398605a5e4f76fbf284a5082a0f1105884cdd3d0d08ca510317c9aa7f90262a3a8aaace82c8a4ad3f5a3c8cbc40a2f59d4edb34fb204fc8a03c202530f36d66d00bf5a395be7b35ea3cbe405c3abcbe72caf315f5c6029b2c0a0f438b29515e656022b4b6c4c9613aea5ac27749ffdfaa03d39ef1326d01e9a1bb458c1c2461526533ab07894fd5008e92afb42c17502acd7736b51b0d706f942b37bb758425dadb546d343174a77d03d8d234c678d35d65a9fda5d70d320f46be759d2da39b8e8117cadb5d6a5f124f33dd73d2761f64ea2b1e73fd0a531974b3cbe745d96a5fb60581acbf2587a0f74b9b3f776fbe3f756e2f27b6fe7816f02fcf61de82d2634f3f02b881f9154a606970d251b739f3de706d867d7b901e6b3eb00edc93bab75312ae60007688f2ea17870b9d381844897194577dff26aabb476575a4928aa0cd51883afce1bd800edf7f617b43e6dd6318dadc5d69522a1bb6f69656503ff10231504cb4b0d1c5a9fbd1a944e839bb4e437fcf619e4ec03235e72398e439c2c51579dafb26261f2e4e04227356469ebc80519f8dc7e15d2704a693879ed3118d26416658c6f4f5882deb4a8ab4ece39e74cf5b9e8f3fecc002a488ebe1cf530baa2ee4d832626761261d0c256171959aff5950cb9d73396bcf617e80300d5d882d6a28b102b75d5190e5f6badb5d6cea3b5d656b4c814cf843075495235d11a4fe28ad72c8f8ea7d6b4ab35f6dc05684f8f68d32bfc8c03e2fefc1a6284bdf2c2fad592a97db619cdb62e84bcdea1cfeb35fa6cf365b2eb8d52621454750583c567e751e1011638a2f1d9592841d7676f810b659f3d0746568a6b404644313efb45523ab2738ae104eac95e218df9ec2d306e1e2852ad7bc415afb140ebb7ebed2bd0d68c19b11c654cd0526c5100101c280f92c05cf4006b91c5ca265a155cbd587aad3d052ad8bc638d9f802730cdf6d951508a01810e02f840b7550f7c20100824a28d3d41e3f0e381c79599baab3ab02bbb1d9ffd043a3b26c0a9d94dc0d3d37a88778f38e76aef1e09ca38d0f510b1765546eab58fa047c2865b965d508b21a017f7a6e0549541a8cd7061c3c7952d3ad49c7b447c249a5b04e6159d8910dc5ff6dfefe71c7e56fee73f1f621109f85f98a8b238135355be98a8bf174c8a96987d91b11aa36e9f3df4d76d1408d09eac3fe0019e520ce70cf439bb7945b7e5751922af9d85e193090d742fcc3a906a29516959b5813971c47844931a081dc65081f9da1aca32b35a732c5e6b37e7b0bc760ea418378db9d9c0083a93cc93d7ae81b42c411720518e64c415b5dd9b11f0d98791cf193e0375414539d2a872e5c4cd694ee17e98214942542caad9cd0ca4638c0778ed18b8d7e9870d1ac61710236701f30a0bcb588bb22a2d6ee48c020f181988a458754d35d538ab80982d0150808bd3f148d1118f2d72391fe08986dc9b0a2f4a9830b94245cd190de3098851fa20812047d008cce540c0096b0504fc2a942380dc92d7fe8061b9021b5a132542a0888018797c34a50bc0f262c78f14a62f636ace18cd682ccbec80b823af1be03dde1213962d635f734d5a7fce79e7f35397cf992d2a890e197647626acebd340f7f2571051273aefd1157f324ae69af6717f14eba8186618412f0dacd34ccf1b50fe3965e3b039cc41197a6a4256971328888f88183e6cfa0deaccad4b061660160a83e737d76331d7a8eaa43428ac848982073476b2acad2005730e8449958a2a4ea84d931c33346dc9f48637f622171f95346d59f9e8370dec6edcaf3365546575438a9717764a7da35e76d5ce76dbed0aabc8d4dd5dbb8dbe0800c1b50131a4e56f8a8369e81d01e1b20100804f1c0280f040281c023180f3c32f240cf2974705e48f52bcf0bdd0065664cd1c5d9e052445d735ee836ef34002b491693284f8688a9abce0b85421f366179a1d0f30fda234cd314e753b73e699ac200f2a96714684f6ac207d98c1e5144711e9ed4780f1e8cefc1cdbc430163902e544277268eaa07cf3e5ee3ab01e1ad8bafa971d3a8c9d7d4b8777d8de713393c2ca314cfe3f1789e4da03dbc123f9f734e761d1c82a7ea3386bc24e39c4d6461ce350ac81998b34dce39e71a315ece39e79cfd08e9793ccf45b3a6a6a6a6c6f19dc3407b1b1bc7678040c7765a3060bc50e8b80eed119ee7799ee7799ea7633368cf6914f5e99332a60fbc549049e27292ebc20564856e1263cb179718389a909ca09b437bf28b295f964cbe2c4bb5749dbca3d5d279dabea4299794b854c3ea6ad75ca0fbb22c9194b48c866aca5b6631b4373373868a9492dd132c29e6608451c1258612b5bcdb12c40a0a2d25547266746491d3b2455dad11e8da0492f11fae3f5d9f63b4d65a6badc5e49c17689988d68b2872714b4f243a0e83f610b3cc8cde344dd3344dd37433ab8c4d074cceb1f55904b8371e2a282dfee6c6f117b4e786685333edc4a3288aa270e8a3ce53f728aa0445d112288aa28e0211f518c204e3ac58877a097d8da1c26a82437de75761da8d11b0bc9c5469487f9ea71b10840d71b6028beaec89510e2ab4b0cf8ebbb892b18aecea4a131d175b189d840734b69cc879e92197c6650bda83fe7877c76d688f53b5d9780d1a7ca8c15d83e7e49d5483060d6e6ab06e580bdaa373ce393bce8285e7f8f31d5c7770cc666a85f9a1f087487e987f38242a168b27d4e1d0436011bc1fba99776cc264c0f50873e68b511dca41d90a12630637f49c963b58271fb25f557dce576a585852593f59699f5d87276beeb3aba0b575e5882f91ab174cec848c59d280567bb4625c5b501c797d657ced2a84516129aaeb09dc8d326eaa766c65ad0ada3324da942728e945702d82e335b44704a24d4d1740771ca3d62992fcfe6d237b1781404abf6b7691bdcfbd6bf6c60a5af11bb8a57ea37b0f714b31d776c2f8a44bdd51646ca0d81205c9c716a38e2a3da07ebedc414982822b06d30c2c2a6a0acefc5425751923c46bd3c15d25e948a1c30cd0d4a36554569ecfaee316638a93af715de3580ada5343b4a989c5c03d910144c5cbd3d69faea3fe84b216c6065c17195c4dd4d3b19a63a5587a20d07114537338da20e4b50fef5e3b86828290285b6598c418e2a6aee9cf0a2218ba544d998a41c4ac875a3a4e731c553e04d721383e437b4220dad45482f1bfdf90fffd7ebfdf12dbff6a7e427ebffdfbfd7eee82d8ff687e6e2fd1ffdcfeaed2ff7a604ebee29e4851c9507f562ccc0f144729259cb6d435fd5f9834a7222674b490faa95d090d297d69d9d1f673fbfbb9ce1299d75ae350321be61a1fb494b296527c765c566232b467df08f3a5872f1d8fa13de513b4e77763c773e0e0d809dac3814b0ead2ce17b3dcfb947503435bb565ead1b472954af77f67a6ef66e1871617c22a34b89a31e50b17bc2c5448e226359580ced39350084ed49cb0f921a449e5ebb0ef23a8a2bca158f2d4890c854ed384ccb8692862f1d83e59d168c908099d12209c90a1588c867d7403ecf600b0226899b0a246a76fce55a35ea7e6fc75eb86bcb4af145f1a13edf99cf7ac9b9bdcfb5cf4d3147dee7e9d0c4fbc650791f1924eff391d17adfdc8df7cd8d799f632eb4c72737e53364c890c133784edeb94ac562d1849a218387c0227a9f214386140744881127fc6abde0c27c760dc6f839cc8acf616a84018315e60e2bee0c4b2ec867c75b684f16c379cdf3daeabc76382cafb5e6d2d2f6da4dedf8a885f66420da9467cc6fd8e0380bedd940b4a929a4ea83c11f1f74cf3bb79893777482ae13028bc81f749eb30f8ef8e053d06dd0ae7d5007836ee61d176cbdd0123325636a4d0d5699fd5c8991438894baa61f2402a465664716d21529c19b2c3e655234d1920125e85842c25e6bc75842a8321c28bacf8eaf865616aeaa4873939de666e5791abf680f06efadf73506bf294904e1661741205a28675d7624154d49c54500c99d99991a46984869d1f8e831e54c8d31585fcc98a19100d21204eba8895c9622d5062b2832642cd86888991bf260c14c0e3353594c7654c62522c718473232a07e9401399dec18bab9b1b23052cc2c3571810a2e4acad78ab5165f7a4d4f2e3531528507959c99b728262e987848c5387bf12208da6fc70a27452c88b46cb9b2c447d152955695294aca70f8741045b066a49323c245884e25dd95b4f0fa3132f4d5e5a7ecc88f49026e626727e08aae4425c9e4ceda31f8a96f6e8a7f77f1075bc65e90516204e544253715d99427c91c1d9e947145488a21b6a9202ae98e916ce09993738fb88170ce750cc4edf396da8be37fc475f8e4d29fc455c70641714edc641481bda93de26a3ee9040494e5f523c8c9492d897755be4c7c7dbd6139513771f52761b059799114c595a46ae29af3245519d193a4e323d67cb460f06e0daeadc1b6a01c017e0dae41b1c741a55f83377e0ddaf835685c83c7c7412f0c9cb8b2e2c8c2c6d31635005dd49e00296631a3a4ae41a0a0189f322c2340dc6aa86b10e929b075876386152c2ce6d435a8e4634c927163aa079bba0683fc54650991b1ba22455d83d848437387425d69d61ed390ea4a4313805f69eaa068c41ed3a0d5f89526e9571a1abfd218698e8f69bc1e3b4d31ef582a5943b8003121c5879521a38d2d2b5864b14853579ab0ba2cb7a6292a49a0d49506894998a1145dba3c91a91b5c7819a9a47d659d51571a2835868b30476070f1e8a1ae3462697a97ae3d4ed30fbfa64aa9523ae3d754c6af29d2af699a7a3df6b49877480409ab0a2a33da2232a662402d288ad31521aa19ea9a021d31b23d412be195425d531ca422b4245b25669851d774042b369ccc8e45c850a86b3a77a445ecadace9c40c754d4b23107807647b0c04ba01e51e0331119008a816e357e0d1af4018bf028d2b3085ba02bd1e3bb098776ac8b0d4cdb88b232aa11af199fa814221c66c4d5d81408f79a4609105a30cd60b7505ea58f3cae2c2c89a5012750566d1a93989f9c41d7da82b102a0519653c5a240122c384ba02a7cab59fddafdd0da9bfb69f1cf1d71f2efe70f167f4ebefc5af3f17bffe8cebeff83fafc7fe2be61ddb36244a95324534843c5187b47865918244b5445d7f408f5920e1c264c6149f28eafab37a62c6eb8c92949016eafafba931c5a2ac89cb8c13eafa837a0714a9ab7342a598444985b22760a8bbc76e57940d45e5fc57b46e0d25fb8baa15fd8a12fd8a3aa1c6153d3e0915ea8a7a3d76b4987748489d4892e348652eed47c56c59724479b9c214a6ae285013a6ba2c4ecac0505734061328426f5f594c6ea82baaf4654ff8ba3bd5d5e7f3f9e63afcea13f389f95afcea63f1ab6fc5af3ee3ea3b3ef6793d765f31efd82aa4b72d4d62ac48518dd0aec6e4f84124860b75f501593429c71d57762bb87c51571f1209ce8a14c7a2bed8aea8ab4fa9a9471d1d11ba2364a6d4d51707c535b7228bad098cbafa96d81396dce99c6b8fcfacaee72927c2afa7f1349e43bf9e42bf9e2a7e3dbda4fc7a9e5e8ffd2ce61d12299c7cead8ace8dcda54b8aab7212fd098a690d4f5042a809d1818234d704057ea7a226928d2230b59b1051323ea7a1ea0eb8693bab5221219753db99a2489fb528644c90b753da77ed8137edcf57a3db8b9e1af3da8deeda11139f5b2fcdaebd5b027d4b8f351575e8daf3c1e4f2ec3af3c25deca432372e21957def131cfebaeb027dc1577a5ba9aa66962f8d5243289cca05f4da05fcd14bf9ac6d53c3e36bd7e40462d4d8917236535d4b41d603c8cfc1032c6495d4daa1f546d679e947022a5ae269257d2162a5ecab07e9ad4d55422808b2d2546aaa459995357336c0e8891112c5cea6afaa0b4bba351d7b22c4b9b5fcbba5b2c4b14bf963ebf965e73bf96c7c7a5d76312498c2419d3c5a26acecb0c8e78d98001747687455d4b1e4ec8cc7a4451c1c385ba964b4f90242de5882a81435dcb335225887abced809ba2aee5928f152341c7aa2a6eea5a4ec1c09ee0e5aea7ae2449c2cd3d5e491748128dc88934aee4f131e925027b82943b17eade6e5beefc75df75a39df8753bed17dbebcfb8c13111c38a4277a4ae1b0984955a8f1a545490a6d4752b4dc09e60bfeed4deb8ea35dda6e51eafda025a0ccdc4af9aa844f1576d5cf5f1b1f67a5bc6a98696b8a01a5098a8d8cb8dad34b9b5b51a403c2d5e5e7051011627455d35520b50ca188b594c1091a1ae5ae9b109a92e348c62772ed45543bd0898ccd88d09d93a7254573d95809c803b3554b56b869bfb5cf72ae40f62c67cf76b2672cac6351f1f67afc7b60e8b2e488657d6113b1968814a030394238b8cbae612700977412ac658eeb10ed639f32b267242f22b3e3efe157b9da0c8172ca7144c27c0d4a4224977735fb4661851570c848010ec09d7d76bf738843bbc5eb7c7ea7ae51edfbab7fbf512d5fd7acdfc7a8d5f56d7eb317e4b0b5303cec5d814752856d4648b09ae2668ea7a814804155f9818538106d6425d2fd2d316a4b223596ef0b8a2aeb7ed0ec810193f9864e850d70b5522eeaa055894ad2c32541f5804a0f684eb38cd21582b168b3fea0ea9627f01ef68157b0f2c0af67199c7748f5d052c420249656c53c21cc9a106a4621d5d5db850a9c052a453b10ff38e0d138475e5e9c8ab4d858aa3ac6015db9a2c29aaab5bbbb4baad2b28c4f860a1622c53494a647101d7a5ae3c8f4b235a0acbb22ccb6059fa75e2f2655a96655996c6d258966959ba0b4abe2ccbb2e402d2f125992fdd71b0081b2f3d07efa4c56211482d5d2704cfd097c0b29c91c4ca440d2d2b2fb504935372e5eb8595a2b214e37aa1bfd0f114da231462e8bdd0b111b44788d9c4d0f860113328dcf9a063292db71685f6a469508c889c8f4751c7436c1c0b31d19e720d156670ba3352d445ca570b754d7f4c0fa517cc8a51b09809c3fa5183dc8100224c4df861f513fe407bca38b0013cadd9dd318d7912671666c5508549d529b9342626688455f1d1234c5c141ea5c5b236b70594e3c8c2a8ae3adf41eecb2ab12f4b4f5b8072e643c793b2133df494d4bec54e617c1c9b4a2a35aca524b2629838ab2e3643b2292f724aaea08dedb85887962ba6d40423287a838f281a0ce6d038c0109d2dddf4fe90f500e2d6456e489b088eaaecba5a8cbd7d51579d0f03c64acd4c614a0d2e6e6a604f193801a423c8270d8c11a57d03eeca06ca831a67eacab4a2905d39010c8a901339e2c4c6dcf4c2bd4d952470d4c91dd390966401a281beb29fe05219d26710de1ac9dcda7434ef924e40c83937ee3dd73da2999708b15e733e3bceb9bac42b75016637334e4e9a7708b5d6aed422038e3b8ecd5bae1b6dbfba0e59848b4f512e96649469a2140404595d52e8c8e2a38825051c2b9848813b4a4ddda9504c601c4d9983c3216484d17972bef7faf0ba8e452f987bcf7b75ef9abc33bd341df274e0bdcd33558ca29cd19048c468196a6bc2c484baddee9024a9a214a66bbffd760ebeaddade7beb13e4d67b8fb509710e11cb6fe7709a449c1cd7b10bf2c148c59ae8c69a8258d1b1e6c488902e2bb425294842f0e8e878ae83aab1d2800363ea2b889818a30c7547e951c50b4eeb0b99249c1cf739e923ea6c2359d3efb81434dbcd10abebecdca28e359ea64e08ae1b767fbe358e49f955e77f40489b75a4317e47153f341ade1e55743df0ed31880c9a0909f8ebf6ba797d78e521a9889920268666fc9859ed38d285e2ccc86d2b4bda0cf2f8648c919cdc18aa32b94888471148d3b1079dd469e623b664caca0bd80d3507dc92ad20435e4cb123bcc5ba6af1c0b7585745231dee75f3fa3085bdf70ebefd6221f4f769cd4955912b5e6a7032520ffed7bab6737fc7d06a480d69f1b343edc00a2622213a1338ded4ebc3eb36d5b9561dd63c230d2d6cc811c90c99a434203da6bc886371438431296069912a1324c4ecc6097040d125b111c762089a182469367658f13012f3801c4d9a7cb05dd962a688ddd26e8a70f31a34d0fcbec9226fcef92b4ad967fc1987463abcc3c934acd17ccb852445861c18a3131c2d846821c1b268674bbab6c84d291143e1f4258ec895aa277130c8516685e48c9b58d85418b2c7991ad29799af1543547d226cc8451329634fb490495203cad0d8590f3035f0f28686648b50518a29b8e194931748b8dc38fa42802e62702ca5384b222201aa23b3155057aa24791b61234dad686736038c27f99e102912977a975f5e6fa991565ce882ae4a5cf9005399598ee3118ba421cee160d3b6a89959dba1246ccecb56143634a015715b3d7e08a9d9edf06bcbca56d2af4223e42def754bda5c2fb47a465d49bcb45829d1a30ca78b95295645b064714aad70904257a602ae8a96745965a5ad5400d78b1e43c8ab1f8a05f83aecbdf787b75d67ac1ebfed3a53c6496f12aed7e5d866e632265cee767831032a2e21291107054caacdcde6edd7718ceb78e6dbd9755e6fce64faa42689668f0536d14a0165f4761384b75f4261cf7bfb3534e4c1ba7dc10c2166755bc084e142024910181132556e536c4610a990832535c6e9969cac93e34c1122654ad6e24610b2b6c328264808c8ceaf3c6b8e0e0c21098031049b6fbf60f030ed170c1e7f731089c2fce6a97b6f00bc3db6b8f1346f8f44559e36d97625f4322116dc41921334a5a4af1c407840615b8c438ea8487a1a9e287e3678a1583ad988d688c32383978f91cd190c6e41af135450ad35bcf53a91e5cb3a02d4f23a28c5ebf4f53128c66ba2aa75076ed061dd484a521b53b5f16a0d495d9c992c672469665923e19862ca031399191734a284ccb0e1414c092b4e51b0a09881ad7c47a0301a6d80b7c714511ec3db23d08fed058b568f9e56144d0a7565bc70719a82b204624a13912c51277c8e6078fba565f4beb75f5a4044d84a986af20526881d9b91ed338c1f2668725350a684210b406abda40cbddf2b7fbdd4eafe3a079cbf8e55a4d8cd7bff9ada7d588845d8afd33d1e8f38d43ef49c6cb59b9ae81cbc6d4ee75ce23a7c6b9421b404b5e30d0eec5af9ecc2680dc7db982e202ada636e49c9b75f5b3dd21e22c8dedd4fc8721f5914fdded942d180b6b79b677eefbdabc49d5d6792984d4da88e9a74587d15a93b871a518d3931684437674ff33a11e433bc3daea02a832c49deacd94907214803e3179044f3eebdddeaad497293fb622ce536f11a901600693805cac37673afad709145e5c7437044cb8dbb1e71744dd49d663402b503051c1b1395ac295c7ec71f2aa89b1126485b4d34c2d4eda6240b151c51566460a97b935beb7c5c816616e0edf167e97f6fb3a6cc59d9ba312c48a8f8f09acb32c59a114a6261451455c5090ccc4de62caf39ac4d7b245223e5e8a48f4f25103228e0ac9c7073a242d546202b6c277cb88e49d6a7b5d6f94e6bad79c6985a6b0baa855ba7a950cbec7f6fbfb48ebfa63da8d26e32c63653f8bcf5e2b39797eca65038ecbdfdda0a3a96195353d1257fb1b1653f80c2c44519f97163cd7284622ceeca8ab2f32d970aaf22648ed24548eea649850c1d4c36b824a9ba84da992f2b424149cc549e35256db6c2d35aef736fd3b7a985d05ef75e731d5f93aad6da4d7d8bc2b75c2acedef76baaa59c9684749bbe1dc29640661ddfc4d5a7374eb39989660b32d2d57ccce17d2052d357bc4dbd4dd2247def4d1239e01c13e7acbebfbe0acb4dc4a9bef1996e7f354d2287215b4ae2efcecbc4a3933f58cff1e165ba9804be796c97be847e1da9a2b59bba24fa5273fb6a6edeb0e8b5363363a754b583b0a6d65b6b2d1c0edf5295a1fb55a879d7ccc61fd69e89da6abfc4a65ebaa3d528df355d6836f5dcf49e49ba70c793cec1896b0b384f7aa691dabf5ea664da93592549b72649240e71cefd9b0af75bae2118bfa6be874e5dfa3ac26ed242d15ae391d160c78e696b6f4ca68c7952b3d60e225aa4bc6c6dbda924d197ee1bdd64ba769328bc699f8f449f9d03d710dbe71a35a3808850919b950f2e3eaa07a44401997d854d51a166e2da024fcec537c29b21fe701dc35bae21b05f8524ce6560ea884059c17436042d431d588b1e39aabcaa40704144c6628797a1b57b0676a445cae94c0e266f291671cda4d2b49414e0034e1c4932e52ea88a18755bc1282a22819252dcf0f60be9c5936fbfa4c6a44eec1bef1b8d4ded9668122dceb122b4cef8f3dd1d02300deebf80ffdaeb0eb8f8e605fc17ceba6dc1ebe7edf55baf14401897f9de6b7a695af4caf9bd54784064e708aeb232f26e37b73cc35b2ea0259fbee502fab1a54eac6f99326d72af6bf69d735ec9bd3d7f586fd6bc77495c872449a664ce66fe606a93687591ad9e68e645400fc55bb7004fdeb146bb00bd7d588415a1f7defbf7ab808fbcb0986fbf8ce67ed529e6e11bf1f68879188045e0b77ac8a989429c93829d198bb7c42c87a9cdc6b21ebc2526a9490734f19ce41153fce1762893b6b74912b191d4a6b8cc8f0e65867c76d3ad107fb8ee7bcb3534a30c8e5fd3bf253665d8369e5bd310db66c2b9138ff318e1d0724cf735d3cde39ae96b4337dd74311ed3b470451e1e1e1e1e1e1e539bda5cbbd7bcd7bce6bda6bee635af79afa94d6d5ed3534f754eeae64d531da78b4dcd93335cbbd8d4e131af79f335f5f0e2f46233d57eaf69e6e438a5a6a92f36b5ce1dea1c3779cca136af79af9993ded4d4663ad4437d4d9da34da7cb939adaf59aa9ef1dea6b4ccd54eb983cd74dd3d43cc3344d53d31ceaa11eeaa176f7abc3a3c3a387d7bcc31cad738766aa7d62f7def49ada8a6953f3985adf6b9a9684d4fce2c3ede2c5f256cbdba1fd8a821a4335e46c8bbaf2bce53167e3448b201a56d455e76d5892b9371e4d82f850a7545942442a8ecb1575b5c7b25f2d1ad9afd698f3f60dc593b9a42b512b549b82ca14a92c4851c2a8d6daf4edd85beb25b06e470605585a17b6353b246e8bdc54912b46d65a9e8b1552b82801b3e5cd16d5c802232a06da9b15e22257a54b89981475bd725436a82879c1c44444bd6c2fc0e034d6d7ab16039e6f8107edc9af57cc89bd4a62d66d8d259d7493f4148bd8e0a40b874c2989a394f3a44e083aa55f7990d475c49357470ebe5a31b21b5f7eb5683a56edad857b522ec7d2af76ced6f95c20117cbd444173bfde9bf4a4af97c9a983af172aea98f6eb9d3a52d7abe4495fefd5f1c92b46d696f5eb452b52d7abf6e4653341ca41e1f1eb9d1352d75bf724f6c14044aa5f31d293469cf424931394d18d5ff1140c75c54aaec48ed07ec56832d415ab3d4942e1b627492c5783c9af786e85bae2ba27b3cf9319e849d2b311d3d4af1929273d99999c707ccd5051665cfc9aa752254fe6ab7c0480af598cec45dcaf194d67cd6a996d275d73db93192ecba59124d50d146e4c55b9617fba8fe0e769e3e7d9a4c69fbd33c9799270deb5d3b6fd5c78f1a7efbc60e799c354020aad145b57c26c80a967da12108f33b22244d435b5470d14017b6164266d0cd902985ecc9d298991b1cbc9c971dbf439ae7372b09a98f91ce3d8e778b681f6e4106dea241038fc75c403ed15700ce81668dda6dc1ee81a78e4ea815847b890c840d2e581aec373e281ae4292ae07fa036e3cd02da064ee81cec292ae077a09981e88c5a4ee819e6ba03dc0243e724367ce502c11a96bfa24b8c5224820e1eb49f85961a468c78b283dc8a824784e427b4820dad46dced8d894606333e26d6cdc12bd8d6b1b37a5b6bc8da75261dea608dcdb18c97a1b1b23716f33c5f5369e69a03d362360490dfd08238c3082e71968cf08449b7a9681f69c500f00d700f08c84f60080685313eb85d27bf0e0f51eee7bf0e0391e80ee610577268ee058789dd1a91ec2e882d4ccdaeaa8b05cd43d10e83906d5d28df3f47c84f69c3078caa83d0e8e6723b407876853b34cd6f3303cafeb79ee38bcd3792816b429b626a11e402acf8a992070766b5ac2ce00d04dfd799e7454fe2c93f4a7e71740201008f4eca28847287c218717ba700d31e28542277ba16ba19ba992f1853e547a21d652da0b855838945e98e3e78539a8bcd03311da233ccef834e5fa344d53a1a7a9f36c7d7aa65b690a4c539b347533ef6808d222ea0c8809ab899a325df9d27a1165034b25f2642c320624ab4b8a840e09da0d2c4f8ad4f586d4dbd8786e918147209068ed68cbdd97981c52d435e78165deae6029a9a212a4ae3a37763c1008fcf2caf140a06716680fd07863e88542cf2bd01ee16d32a309135b5c142a2b9a748c6427aed69fee2c84a0980342922446a49e9e87b210da930600e76b407c4d88af195133a2c692cd40c161c6d2d7f8d0bbbec67366d47d8deb2cf91ae7a161f735aec203747c0d565295aff1ac02eda931c2f25a67bd76c7d109c1d3f53ad5585a7bd0627abb60f65a83bed258235d25f23a8579adad34d636fa54b361b64099500245cb5913950ad7da8f24b136b724ea9abebef216c6280a52122532fa465397991730e6c27409cd3911612d255142312309521063939d9538a6187068c727aa14a6205fecb89808f2a413709e1cd640fc19e2cf11476f7fba3edd8c51e44f4f03a688a874aa19a93339c81465350400000073160030200c0a88c582d13c11c4527a061480106ab2525e4a1a4803e220c861148498318618030c30060c01ccd04c1b00f62a0284654235f05124d754493236ec89ba8a96e8e682896d2801b18a9ea34dc862248c5ff0af1f59fe5e00dbe67e07ea77fb276e08d54b83b6fa14b5ddc27b7368e8fc04850d6ab21e6016d90b62d6036d14018292ecdcd440b1c0a68c043eca77a68f1b503ee4b0f33ed80b6e1323c22bbe262565f660ebb707fd884f3e50ea0a49ae1bb395212da9badb1a66ab862b8311b1256f1bc4b0007a722d1d7ee2d2d39cd615d57848da806668055cca51cc07b0ae6bee79c3db45e5871d03f82c2fa627ef9d03f35372b319e37a9f889c9d0e3716b1cf7d17262a635c32686cb134d7f22248e891ae2f3810b7c661c219cbc0b7c64c8e863ef8c52b8520aaf2937de4cca05519088e3ae13215e01c6f5ceaed42b0f528a267aa11f25c6f86615e30ac7ec288a25f3d4ddd367623693fd738b9232b7e0db58597b36c0520935c5f5463f5a6999b940acee05420a9547688c181d3141b114b91244ce424ddae74506ef32c0ef992e26ae2375bddb347563ae90dbbee5a8062f944a56f328f104598e0beffe52dc456323614c23fbd2acb7b9f0e85bb8692e8eaa6506b0accfa2b0693e768d355f5ffc1231fc3f085f5abb103215ed40995b6885cb8d6f9aa63c3d724c054cb1eddcce17078d4b8fe28a89b39a9eb374c91b4a134e46e2ddf37c8ed0a53006a9a1818dc0379ca77d82cee8379502a02bb8c18db2c91720af52a606fcb0289ad538da5fd370800aa81a81cb7bd253a05862b93a4b4643915963f343efc5b5dbcdb525f8aba432e0fd074604ac5037f7255a99301d6ce11401eab7a684977e800396d3bd90e3b1182b60159d922e9679fc6669e688db35423fc2d09cee3df90afaaadf291a97774679ff0e4ce4152dd2ce489cf39c7cbf00f1e491b200592de2a7063d3204168b7a4892956a05a2521c8141b24c95f016b6dd25d08cd3768ae8fbb3a48840e99499e543e55d765777af118926071a1240d80c48746e04875980dc4f49215e886be5419c4f2ceedc53cbdd21ee001cfdf4cf98a638c28cf551a3a6f0130d5ceb524fa36ce60dd9fee2ddab9cf677c1a588724a2cf486e70b55bfbcb5ace9850cd3891b03a9273aeb58039c2c630a72c62503157afc3f7df96b0e8412e0bf484e812e975e15ef75ded7437f31c85ff2c1017a0275fb37d6f284b35be7198e9ae64b8a0bccdd6b64e6210cfd894d64df5672d24d6e95e7b90b6fe682dbd160bf08f61b04bccc30464f159bc516a9fca7f08770282c183eac91d3d7190ff86d80883ca9545f3d6982cf161b32a9b89243378d52afec98275b5602363aa966c18bd549d951628d1ba654aac11c6fe827a604c0c5e9ee3e623392e661b83ddc902d7361903720c82fa15a42e51bd8dc56091ec23982d3b18dc6ac83d4e2d2b8983dd7695387d5f8dad4334d2a1109230d1d7d0810c54a6e023eb559172f135c5d5c04e417d56df7c976776f53459ebad2a49469afd73cfab35e2b42955654927b4d3bd03d42ef92125e2d6eb51e054ba53ad26b88d0f8cf5d22ed191d39c5e644dd64a301d0394815898a5976ebaf6b16094f2cc4517b99e637d9a1620e57dc3bb5ab047274a30368c7ac7213cafa46eb7c3b477afb5c93f22f35e8ea8853558b87e8a5551882dbb60a55551bab8f974ffbc0c62c02e33a7cd72a705e185bd5d1e054bf487343609bed59e81b1596c40106677b5adac540fdbc89c2a249e7e4f7915e324157be08ade196b3d110df32a8f980727e4a39bc959215c3293e4a9b1d163f265f938301f855da3d1c28519c4704929dc0cfb4d590f7b769e977ca2e898bbab0fa66d600b3d92e5e797b08a38106a08e10746ad6b0074f056336b49ab368515e1727feb2110d9b88550864ec90ce1bd3fd4121a317b08fbde17b69a954ccc0f41b08c00d8c5c0e7563262e5c368ecf4e2c64a0f136957f01413308e62726cc70b323a8cb9b1bfae79a73ac0ce53e9354d783c969f605df9a5f6e02b6a60e084a1fea3a65bca20566b44b0837c96250e9bfc4cc0e63b1f847c117873aca8d31f8b3c03f195200af892cf9b38b0deb6baed49fdf36873293e424d35eabd69feead8cbc2472ca64b15498bf458b91205b73cf972302678415791f6408145330e43258bd158085ba630715d432554adf1e5256c0dd91e3c678b0b6831481141421831ce8efb6240092ccb9b61358430cf8ec61a14e3cb95181526c38b63985d65f83a25ba683b5763602a12d7010b3f9d13aeeb334df3c0f4b4ebe2f2a9532a453abd23417814f5a2e88cba936caa1e0585ef5375c023f2a66b3be608cdf7559204d9e34e3e3b11da5c5069217eea1c526b8fc440fbbf497b80c0a4f39713e5262c44ce072549dd74046b94cd0f3dc1c63ce7c78c25937a080f16e02f5e776ca7b6ef0007ceb951ee2086b7467239bc446a7982b614e1857818b8c4620f57e980e4814bd1d7eb920e118b8c54e8900bc772179e077050574a7b4d9ba37dc94680023000b2c98eb7d84214768291a0887882861b71bc172e80c47eff6a48f5d10b040a6cb15a47b5f8e1223473839a2b28568c400f64fcc6d7ed3ad3268bcf1ed54f36e3e0bb4615cc5d9065af6233d6a7f49acfb5bf9467cfb10ef75b98ea0c6cc2e6b79ce8ef89405746e4a80188a176d491be364aba52ca203fabbad8ee87f5ec8f802081309718e3596b09055dc690df9a0afb6a9b47403c79bc1013aa166adec8d9eb005345a5d66c7855b7cc5f1cf44bbe37ec55cd82c355dcc1f7590d7e13c96d306cb193ed4ecae10ecb71f0220e9e2553adaf2bd5839f4abe5ba9f59ca937f1344f159b862cfdf13b9815f0673dc515e5c9fabf199a51b0de1b17376e41541b0544174200319e211741e6b4194533f306b6362a773ec1e247f06c8c3576c4ac60334a0f9666c7401d26538113865ba88f3a32568b1e501a373ceb012a4995a19513ef5b2993ac2df42c226d8fcaf4f86820ffb66332f20ab74d8a4df29429f632e54436bc03dac03e04351a25ec10bef852c61585212043ae8dd4a0b0599bf49c01e1257155867c77028082514a997ad447209aff2768d902aeaccbc2dad42e20ede408f0c909e398f9af7f899d4e2b3c8a32c168054ad52f3c4706ee1a80fd629ced72b59c8d5dda06f1396766e6151943502a6ef41b2def18069330898f47c73fe4be0862e2c829cbbae25d395131cca27e02857aba980ac89610dc04c447ea6c6021dfb9cae346f9478ee5e86f9b23294fc4d65cb4fee775b33ae3ffdfa9270e3063d06f3d3dcdc0d584f2d7c34382099a1a35a5eb8c52bab7e213f8077687908713fa57f6b04215f4f38304da0f115deead0e4242583b28961f12cffcd9761bbf4c21319cc10077638b54973f3b1fcb6e222c682666b555dbcce48f7fb6c11ed11ce7d38e0e880f49d90b6d23d4d4ba218aba41a8d38a9edab016f5eaa5f3b7f99a0500c004e0e33656ce34c201225b3ced76f83fb95dadcfc142d15d183d3072e34cfa939a2817b32e985442c31ea4bac3814fd80541b45219e042d699aa699375188fe40714f3b7f5a6cf882b87e25ce466640891528a0b59d02096735448a0d08224405072a8706a944dceb79af02dea5e70f516acdec03648fb9a2a35314d5c303509e1dc3d76aab0a10509bc310ad2a89244217b571b063ab30c30c36cec58fd5d85af392153def463f79c760f5c6c34bdefae4a7132bb858aaaf6c072418797c862d21e0fcc8318fb44459d13c3f5f3003338ec26915465c4f059d7f34ec358525a535e1a77a9d829c379e30547b903ff65b3a9b5db793e00fe8615ef54190d1711b333e91dc0704f360fc0cc6269aa9a454ab667e5de6ecc2b03b6e0a4697a799fa8aaf169e52854d9e202bc57ea31fa48323ca84ad8f0d46c77d61b8c51e51b418526f03291561c90f652a33f5662694e19d5dc6634144e04a31ef2dc1e91da5bc8258ac0347e487b7b8812085d88ef34c0495dbbc5b4bc8895327e4ffcb83bb41477ce47b2a8f50cea98ee387f1936ee723b8a5c6d1b5eddaa1284b4386e1a2411e7a19859917b7c448bd32608592d281d7032f34f79a52bec6252a73bb5a7892f896b4de111399e102c2aeceeea5695cfadf70ced82a6768b42ae913eaf38b147b2a5cd1a0afb34c68fd8c272d322447ff05ab3e3cfde05603b25760dda616ff9d40c3ab0e91ab76107a4315f9333331c1887cc4b72ad0f99cd9d88b228354f46ffaf2a3f3043439d5cb20ad7a1a1dc3ead159e7691b4b87cf1d2062aa23ae1c6b00137d6dc1c8b8d7fd9bd5ee4318a5bc323f29e9d3a6da54c740f695afae957852192825b0a2d2c5b0cf10b2410d74fc144e181005932e23d191a8488c0c16ae68df8667c8047ebffef3472a40343364c4173aca467424ad11153d0f8da3012668029a0cbf6576842a4b8cd483581dbc34ae42a35aecdd0c80aafce7ae784e8ed7abb1f36241cdc707f49e58180594bd885fc84564561afa727b96f8434628b2a359f1aa837e6a604f7d0bb207255182a0ecc487f9bb5a5a6a893c3c0e029020c8dc2dc0780a3db025f6df4a96a12e3ba95a9ff1b75cb9e17638af62da335968ac557fb90a18674e06b5e61577d24b88f3ba3acafd568533937d03b847627929b08d555c4e026cfcbb5238e8f1ea54dccbdcba3538615bf3849158b848bfc9f1126646b12c3abc028c972e0c2615fb7eaf039a42f02a1d6860a19213b40a106f30a54702bcee2a9876365a4d555d6084cce7f0313bef8da21d5501839f4ee29edc11f2a4d1097fee8d042196f19c661a34d73b912d50028bcca8371651b176f3d971153638e36ba83d3a84f82666056cf8276fb233c92ba60234e95bc95bac18093d14607ddf632b521c3fe05d6ed67147517d612d3be6968dae5f6a1f7b9a1b707464fbf7c84d66d9da6e546077cd9cc4c8b45613fba0ef17e1cb52e4518cdd15884aa0facb81308a47886576ad5039de31b711aa6eb0813dc18cab0c6d061bbd3ce8492749778a6e9d2e869e715aa87f4935a6778f20b6f2d671a09f52c165664768374de16873882d22f839971a2ccb0d42a447837d7d2a8a903101d85b3c346055d73b1fefdd2097acbd94efbeead2cb1c4fadd0da336c2337003ddcbf57ce7879d84948fe3f352fb61998e14f32e880cdca9ed8689196384498d4ee4315c68f7d704c04458fa4ff7138739db74ba068e9da7b69a7594aad1a0d0c87a089669f987e15df7f5078a954078526b4e6059d1019c015709d7a8d945ed6e5c83a793d8cc67688ce1d4a96ee8f703f3ca833ae4cf4dce51bf2e664387699ef0a0f17409cd38a0e2abad90f91a6f82cb4ee951b1b0550db54c1f21f815233b9bb492107b750bd83d7537d349eb1ff49a845043c30f9a7dffeaa6669c8d7600573373b228607b0062242a7eb220fc7ac9326f5ac031c19352f3909d41b361814db19c3a8821919416355833cb5146d2ab8b72bdc00952bafd38ca81e888fc7c4ba326685db28e50d8395e703ce03c327c0c4719e85874352123945c4207780c5b4fc1adb312e420021da907df59c66eae083a3247390154fdf28459c4baaad0a2d351dbc3b8c06f0fa39a1fca58e89948bbaa04145415e882d7bae3421d010b680335d4baadde2c00e9185959b1931a8a1b626ca8c01772bc1bf2c42d59675223436be8f09c435db2435a1a6d6937ac1bd057a0f809da00a96b1aab69937604dafbf9de688cbe7824ea264a33656c4d0294b9f853949899aa8a1fbef8e10a558ca5afa070cc06339806f5fc54268613c1c9ef77b2686ce880ca57d4b4cd8e31f4f633b7d3873862e84fe97285ea70bb697b69986032719efc9cbf97d2bc23ace9264432811a7b611d5701ec4e064e1e87a25806bfd624c4f5ec9f30909d2a12d0f8e9aef15e82c5ff9b4b71babf4236d179d9e2666859d062124939ba10cf490495016eb2b64904828a355e11362002b72eab410799e6a34517815e528dd215f7a5411b6a288a24c692154e2f1cae9fac0bcfccc374ce79d3937809e03569bab05ebe04577eb505dc6914c546116184c247f799194df59b8b84f74691dc981f2162ee0fd2487316b730cfa1645cb65ebac481e0d432b2d230122ca532b47f048a3908883322a63edf83f989c0c1633722df693805d73b8b35c61f848c156bd8eb7041040bf5a924c5cb5344ed2e0e8ac6a438bef13289d79bd6b9e1c84330f19547e18f075f612faa25d1a1c849c604c04fa307869406ecfc193e0a7628d8b0bbe837e2bec0af4e542864d973bc936b6de3f4ccae293a4a305afc3e7390e18a0a414bbf5e087f459c0576a2658faed638a4d2c32797fe9b32d8c64f049748bc11e6cddf4ebea6cf1a00caaca83772b7b195e3156c398637a9e527dfccb97686c8de0734f633a10eda245664609dfa9bc42b046f9c743ae3f045b3baa2222ee39ade60f5e8c396d23ee4e3fe094fa2e126647d2d0a43bcd4e078df356ea0a1d1afd144386fc7df931735ee3c0635c88b463fab086fbddec733ca0377f9ba51651eed534bb7e37795fc0913077890020fa4a8eb37fb2d7879c8118931209f1780068ec15108a08b222003c319c939c3d110dcf7e7bbdf5f3c365d7fd3033ba4a5a3f972be21904c2b64cff829f5b070ab2421b9b12093af3a0cf0d6e0b1c062d199129dbbf32e6acc1d229f0852722b011cc1332a58806676d19907de02d8518fb468f0bece2aa75009ec9bac4d417aa26a173c4105aa2d01787e12cf6c7caf1b008d022c579849f30133d72a4f21d7a48361de8f585c75f2ee6058322f92777dcf4a099f4e0047b1e70b964cd883bbb6f81e3816a00c5e8e15c01d8c2d1932abe88ceb1ed83a974709fd9595d610e1f5df5bb88403d9a55b1647c739d57cc6f566318cb467cf63853f1ed20b2a48b27e2910286caeafa8a0b6086027aea52c46b63d41cab5c3ed7b1430f2bc90b5f1a2b92b8b6b4bb4f995660f3be2fd90cd8e4c624db51c0d9339ac7ac25d996f3911074359b2ce8ba8293ef025311f23b40a6e1165ceb8daed56cb5829aa19574c791c759cc1ade308111fdabcf5a6fd141875694eab79f3ac6993b7445ca4420410597696c127335195e893f34a98ef7bb33363b2ee802daa894c2d386e617a11a9e7277a92caad352702ca7b741328e4cf2510e0f6396190003c0f32fac9ec8a55d1192379ca7d0913edfb34a3cc645a6e2227c4cfcfef4c1d7bb9c947aada568a0ad74186b75b1ba3a4e472daba8a40551421faeda8c57792c34a63c3dcb7d435db0b3e2bb0b4c903598d3750282c12db10335cc711a644ffd2f8ade3c79b7243311253bbb10f42a85f018f0e30555abc068e480e0dbffd897bbbddc503f23852cff8966a55e1f76da65eae15331d81aee4dea0c8619f65130b1d661fd880bba4cb45813c85eeb019699693561a721d69ca67547197b714ecbfd152942ed86cb23560409ee13efa1d50a0c746d3725a3458ead8a61a120e605d666814fd3dfcb0d20e026b9e2e9b2f3316dc04a44bacf678367a89d18919ebad0288552e4483d104d6d832a5e55aaca6e0757b0471057cac14b0f62c9e5a0c6924f261456d9c463ab4b37940621563f34a05969fe207406ba29526d668709ca85662a18eaf9a183d83dcaa209bd020b666ad800f4e1d683d133ac8b6e979abe8eca9dd00f534a429a57f27ea18882d9bc557c6e586d703c4a637fe07f09784f59335bf99a89d9e9c65222f56a190c97ce9f5354751b08697345e0263de5b1faf38461216e101fb7ca1670f670b5df833c4dbb5aae5b22bd98ee30d4b5d693538bd91452d8e913b688d5caec78e4fa55a72191cba7b3cf74844cca3a1540fc742b863b57631d1f9defae03c291cb5433532ad5435b4b2e1a003da226f30c24adacf0677d0a4dec0ca2e5b65cf06b66a9cfd1c520f75f9ce9cbc6596b5f9205dcc4f11d1143d6ca53bb984987d2719da9eb49f18ddfbc25e2a94caf993e54ff9e1d2ffc396087de671931ab770229892da9f70ae0bd9337afb4ddf248b08037ad40c067f5f0bf90a672cae21f65f08f3e77a7d8a45c709d7efdc9abe4ceff9b6b0ae9aa99d743d1707ca305257ceb14853a19b2f534a6384f4a0bcd914522c2fbf3f3338722177370d776862f939538d8f21ee9d4b90bd8bd0c88e837a15c70c083240035eb9694c374514a895a136df02e3e5772acc3600894178bfc81e11098cc3e10c92f78353c68136ddeada03581c16ff88a509e55c98ca52a9e0e33d091a754a2aa99ca12f0ab620b15a56d4bdbaddfd8ef24551cdfdaf486e57209da34482d3b0fd6fe6dc349c3f843a1c76a3062defb21b9cee47105c4a6255d963e129bfef0a377e249dfd3bb465c34d23763ae400ee1529d7472d88fc967a954a806361402ab1844a75a407c495db536c283de0cc3860d7470114e197ff0f9211c883a780703fab8d202af80bd908d67bfcb90233b232babef44b68507bade7d0ffba219fbfe54404345c5083a5ef5d4f57ad4c1e0f74af7bcb2717b9f4f9ee4606bce3efcd20ca6e2225e5771d1f332913338dbade00d10b4f7a1188d7f69d85f330e4bcd2c8bbfe990e4a257eb6dd076670fc3181731e35b08c53c6e7ac3e7a0f1c52a54c634a7c980966ca334f28474dde7283a98d69e4a4ca4c26806a4b3990d2c339f181147b07443820c6d02a3196e7186126e29f764814d9ddfda438ea62ead184865a83091b237b961bf01060e8f1941ad84bedbc0cb3ec5d72896ded40d5060c2f88abcbf38bbfbb1eb41087f5216e07b57edc6f335350a3c924625192b7c0cd6133b26cf30dda06a5ae9dbbd98f5963e4a2e315cba72104f986189bc70fb7193ef7bfb45d0de59b63978bc1c441a4ac31ddd4f96e44d1cfe03b8bcd96c9ce0ccd703e40c36773b2fd0259150eb83d7253c39094de2c181d6a55de5941203f2c3d11fa7975a24406c513ba1c051d4f33a4cdb3eadb4d229a299f850b2fa227e918f81f4cfe50272b7fdf9a1d9a81fb2ccde3e4a98f64906f2d18964795391346c87c3825bf14c1270e6d12ad36eeac73807c3b24e766809905ad114b14e3f291e0e56aeafb4d7ecfe393d60084722374c3c2a1847d2ae1fd0b52aa68c9e038336b662d7b867678d4c704f801ae98cc1c0dba253974045eade9d836d2f5d246705cb5b566757be9502ad2bd87094868fa65864ebe61b693180c1f126c5e70e9c213d0a0c7137522221a6e7a2cf7163ec6c3c42d2af595a9cd15415ae93cde70f9f4b949c535e5f0cc8ede03fce697e1ab49066bd84ddc38b4ea196d8e1f74f9eb1b4b08888ae3d52160d012a473c5bae84e52e335cc239d5ba45629ac70b16bf851170a8284ed20730426eb6bac1a86262b73616097c57e05bcb43e21850c0df74ffc17427c138d466baaed77f9b1e8693c7fb7ed9b4216ebcce70987266cc5e9b841b74de5a00ea6b2c185c916de5eadc343640583509a6293d78ecbdf774e5b352ccb4fc6dae6c02354c43918f1d700016812ad9c5b4ba54318b821cc76468552b236fb9e3be0eb4bb8b69feecfb4cd22215425e5690b069db680e12247729d085ea513cd83b1a256e353a2e2fd5d8ffcf1cf0e7a761f9240c8e1a46061077545c3453aca4477ca4bcfecdb3de93343e6a7d69721f69f47a5a0e98c4c04df235eb205bffed5cad9ec22ac320a71bae1e01d3b89562e22410c4d90ffc54e586622513249948614156ee71cede842c80af6ac46d943d084a632b720f1ba543c9c680562d68f436afe4547522387985935ba03a32ce33c31d9bddc8118be29fdbe8477953032891ff53f77e5c1ddc2f5ceeec55cd539320b7056bbf9fae7c0f79582cfd141badbc92f47d80c850609888016aedd0c4b0e4b62a1b9d11834091c99b48507105cea1bfcede9cfc1c6ebbcdd85b2183b2f8dbbaf0b35aec58d8d0d84762707241b7ba8bdc04ff58079fabdef8df21201566ecd82ed353c8f5105411bbefb7c015ce1ab1b32a8a7eed8f4eb42ec35178dae40a16c8c2ec30e3f42b747dd1a377b0c925583f0c5bd172e86204c793bc314d22a60cc20b89bd9156f41eb97dd43c00d21281fb807a0ef3dc7f16638eaa198cfb2dcf6edb7f3d467d6cfc09728e4b8978bf76dea17b53c0eff1c841446600a4656b935b16b1bf61f76984c83be1fec31e1a0b02824f4bfa32093d3050a650d28d168d41358698af38f72422d85494bf0385b4478cdc86460c6299089281a89be2d1172e7ccc6c40b6662a648f7bd04e98b181f31a2614fe0bf6df0e0f51333fd1b553c30115567bc5bd7906a196d5b92e456dbb186fc91271ca9c52c2347db21c4ab46b9436c0bb6e427944abaf58f876397c325c38fee0523acbe466bef3442e6795fe9fae6606b49bd094d32a032c74c11fc30c111fa4cb84b5a437215d3d30a6af49f97a95b6edf09238d8fed52dcb533748aef42a7a9fbaab1b50b3ba8e12b6f4474ac851525e202d07fd8d8ddd54344e616025eefe2d3e5e62dd9d1738a08666e4f1728ad8d65e5b3d39e470d992b01dcd8300062e66ed74e01aa4bc7848930e4d56cee5dd6b0d0bafea0b787b15062857fc70aa1f43aa251f60b7fb6e2b5d3ce4642fa1a017a633606421987201bbd889f61f911562239a8f6f7a6f65edf954a2b7ec08db6122a48b0b7725688830be17e0a92e4da36de1206d36f7b43c9bb4a80960dd98524897c182d21a61850092010910c1215962e29a38a0744b23242722e24972646e7a6b768210b52a3da10ad70247a7f565eab5ce4a4e96c8150a2f2a35ad3b0f1d8d42aa194c804b72b422921d6c59e97aafaff67edd04075a09e298ad00922e1b4787e657c1f801a2d0a50a2b5e5a8230f6ec33b45c07327181afe917a75b5d7d8d6814e887ec03f4b3334a2cc9d0bcc5a677310e96a6cea4b12dbfa84adac9adc222fe51f762a4163af0b94fed0dbd8f26283f6ff733e8e402c52687679da8a2effdeaecbcaca2d7276dc1e914ede3105cb3709408cd3786b11b3044c8d18229354e28e297cbe649661664274295f01db6e8a7f55e7701ead1cbdccf3bf6376ebece7464aa4cd0b44e17ab8ae5bb1be790725b29c3471ad816d461c1c37c1bde38833eb106239b739591c931f28fb986d07ef757f377f7cac81a2576b3ae8230c4a6f06ae97e3100da9b13246c98ae0f979d428ca603311bc3cf18695df73d6e74d61bf08e2438b314d474df7cdebd606d24dd31fe811dc2883cf9e8d5a4910c10840c97d9debfbab0b08d18ce31ad41a8b75f42af4ce20644dc774204088c14227447f22c1bf109aa02f8cdd777fe3a0683455f02df859368e36311c2b76bca91c54ee2e7c19990f0e5d85aed81c155946c1bf644c3fe10dbf00744f082e35f1fb6963fde9c416d1d3e4c67e52e75f1622bd9231cb4ec8710b07b70c765a67e3795aca28211ac30f74f635204f5726ed70518eb945ac39bdb22ac22957d309f0fff805eb6c5a02e9c1cb97034563458eb9882c6b51cff38b5c179fa4926a1ffe1a2eb4216c2db8736fe1ba44334bd87fe5ea76d2caf27b7623d3bf8ab9910bd09bd1d03d7a8780868f45a924f050160cc03f53fa0358b950d1f80602a3dc5686e79c7e526e1f52b89947ef2aa74fd2b919a94fcb783e0b8b5b24992e4d197fdcc1941183d47f93b37dcf762e00152d1f1ccbc3e81c93cef4fa758a86552cd2093226bdb341506020ec42a23e4ffd18df28aea802e5380dfe697af1d7ce43697f91287a0c2d2a06f2048f549c90e1c94f284797084421ef2f9716da848085d0eafae3dce80a5088aaa708df5ccc10117f3beb5884603034a1cabca82613185d348288769994667845697425b8113401aa6afdabe00a827e175efd4e603bac8afee81bbd1b7d42756dc6646f332f58599155413b997d01b3e3263044f70187cc0f10f3b40a55cb9836392796bfca817a6bd1a3f2859ee7abac4896fc58cf188e50496c4fa305325ecde34b8629fb5fc689ed34e8b5327018f53bd12dcb2e43bcf3ee4222da54f2d6931d7c4340e98dfb6e21683caceaa5e4c1be5c6ca3d72ee39aba066e2193e24ef28443204a3738308573795231bfc4ab2c9d3ffc9515d6b78a3ef1f14497d7f557c070f66255ffc1aa6fbec75fbc30b87924f361c973b05ef57fde420e9250d2089a10515ccc1ef1e45c4f11d377c2d143797307cb8364998b75bf2a3e5228d10b72368bb9bba993ef043a653ca4e8ff16b33f7b08958e36617e4f6bc2eb41d3e82de369ff9eea17ae04cb7569cdec20c286078ac286b2627d967a4060c6869c7aa1bc12dbcd540a938cd1727ca4fd67d7dbcaa6f49a64ef3980595e4ae81addbb05d19ab283f325104e26d4adea1a174d01bf14a5d9fc726ce133ccb744313f5b25580f0bd06bf5ef25e6ff158a2d35a0c0edb2220eaf349db8c06900669704fbe29b988f4752d7a331427f775316b5d9c06e93a8ca35b26b5fe1b09aecae783517a1ed24ea472415412fc012b5d481f2e6dfe814dd426788a55d54a709723dd0002690a9ea42a123f4862547e9de1ce91eb38ba16dc19cac97227c4ed1d0acb0324037324408934f3ea33a6b760c7e29d0faa43e47b6b234616915745109520e2932309e5b844c281422b6e8302787a5ca160d13aca244da473a9ea0e2d65b49f11b02c206eeb1b610ea5d85a864be6a9e8280330b9eba1182d31097ca2684fa8bd83ff8600f1727877650480b7196949dff049260d8c1f66116a44bcb1615bd62a639c50b98bb6bed5fd2d3350af487fa44985f4ddcd2dfde6eb48700822b6f1c82b41832448912f6ac22be4e6f9b4d6600e5f12ee93050994463d040528792bb62c19ca0f641e8ebc6cb84b0d38ca7d5c314df7cc228dd8ad79c2535bc5b21ad7412f4b6d06a3803a5275a2a2597f1ed5011d1b2bf7adf2943e647a83183d5a5d5426ecb5dad2b61a6867128714a38d9e8dfda92466219c3c74ba78f8101d1b9a5b265e9095904cdf88e878e01e021f4408ee675490633301100231a12977249f5830ac09d7c651cba34211226a164783b59b63d6d34dbfe476916e4625a38a21304f67e1410a76ef0accd7429546597cde1d7944b76ef38a78e754a33e7b0c2509c68c7fb6072cf5516e8dd520a1cc37a6918add3c53e53481357dcad1ae3ff4f8a09ccd4554fb9aea424884b9b1bebb9d069bcb49362ef368cc340f8674ef3a0dd0671eb1e0b2030235c4b685fd307dfd710c0c496b7669b5a1f5b6fe7264637a0b1c1779bb85f92e7281737b766a6462547b823eda23028dd29979aae066c2e874a56e55217353e14bfe628ec0094483161faef6a7bbd81f7544ee47cac647969bc1b6f710f1508ac87443882e7ba2444cec7db249ecf1cb24301f1b45c3c4ba4d9f482b340e2d3a29c1a1839f5e9fae6b72386d81d4814f58c363e9291028084f41332364123619a1f054aaf0ec3f8d14c1dc8a907a4ef891403597be096624e0045a960bc1606720b0c8516345fd4596f9d3708090e1a01dce36643d5378f393ddcac6268a052c05ba4e3e29aa4a80144afecdd0f8bf988c34c73282e9ee077a0f20f8b50a9823ed7cc0ff46171256e410a87f0b088c869d9c12123a646308446041bca7e8b5c3f6625ec3b58005af690594c195ad46bb1aef5904ff12b3d80610fd23f84b29f75351f4bb27ac74077227026fe88ba7efd10445188a0f52d005450a56c09525a680355ed0adc47432a0fdf18324025fa660e63a8f535142be8d414b027d4ec8076f1453e8626d012add11aa632dbc6f04466742e433037d41267b476c4ade82c2cef33f0bfac07e080686e8920665932a660732f26919d0cd02d01cd05de1d8bc96e5259206540537836adbf15bd0b6ff1ce373b173ee7456536618d0793fed66d807ab4af21a23950bfe5cc73ff5914ba52983ac133eb286b09f18230ce470a569480a0ee1805d7ee46dae62698de5311c0491f6452cdcfd6cff89bf223c02914f0b8f1c6a8297b246a24b5c4e314913cc40a3dfc17813974ca038912d37448b9424cae1999fd7204558f9b144b960fbdec683264d38791c9c046ad4a7a7ed68f1e7f8ce9619f67b1b7c8770b07c0a692a2a59bda80a0c06f665a66a2534e5bbf544d7718863a9d726218feb55b196723191389e6ea19ccd5b063189873b9e90cfe1c9a33ad88a16d783c552756b49423f773ed5794b1139d038863c31cebe42a121353a8482a889f612b357cdcfd7e13124020a1834c50546369b6e7aebd98df2eab97e7286479934e5ef3319a926e0d50a7dc1e155c400c59a2a6716476046d8ee34d4a0d81900531ffcf3514927c68ff91956de35a91fdb12d1976a70d429b512fcc997030f715ca6dbd0bb4c50f859149590dc25ddb98a2eeed1a6234a28d8e7dfa9c2d354b9bc9291840a508741066d5f301fcfc3866fb8914bd81fd6a0e24f9f3704d4b8b9a68d3c34967c6d4c3bc75e1d552268bd0164225172cf33448fa5818d9b7d4918b37a71b6544ece326c82345e8d311029381392641c65291f53c5684c501477213fd9a4712e0f38180cd5aa7cc1a94b52ef28ad9cac31deec5b82f3b3d4d6a00953a99d88e10d11228ef1991d8df0b5020a0ee2f4d6d309fdc420c04943204ba595910cdb58c3cdfed85f958e9e2d0388814f6025268cd7a0edb1dd2bc2282aa0e0d3fb7aa035422768eab1f04abeb00b863c2423ca539e1ad783824d894faa744b7729e8eeac34d0f387c807dd951a0c41e785669b42b78d2fdb5f4f2521a326e25654472485dea4b805752cbc59f17e67e2ff3325aa0d7e4db6e2c130d18eea94cc2fe18f9e34045eeb7857c760cc6c4e5061855d281dd348db51f6e0fedef1630a1539088f99ce909a5ce8f823e882aee0c9b31ab7555af5f5d4917f1fa70cadbafce3ae01656c755332e28a7889f1b3a4617f1a1991ce2caaa6a9e802c3f0e97ae8ed401d60cf4f3bec681a51f5974b05c04c6700bd7d1a2e23f513df311b963d09d72b55d1501ae84180be1715700a22f34d026f1eabcb281f64209bfc7254d2c66144c89d842657a94a29727094dc265c07c7fc8a1967ff53260bcf0fb26fbcd816fc8ccf8385d8086d814a70022d1e671adf1b456b5915bbb0342fabad883c9360d77e27ca0a0baae072784a753a06f6d0dd5a6ccdd227e99257b4bbc638c63a4f611d0068d07a2727216a79a90b457bed6422eaca4ea98f77ba9f9f3e215fa8ae0f026f68f34be5f1b5311c2e61f4a17936840963d14b0d4aa3a6ee4cc8b8699ffc66d3a9ec85bf216c7f5018ddee11affeb2dffeedfdac52845f5776e55022318e772fe24900868ebe29895f71670500917d713908175565320bb653a7b0c282af680f1822182580cddf16aba89a49ea62232b9149c3992d9eb6735e0fcde59f365746e11ac2f9bdd63659042e8ce220d20a06e6ca0f0792e06034aadfa62a7202a9bb500b6ea1ab49a30639733edd7e72a1107e101816efad8339b1191b0b0cb43d20585737942850163ddb08a3364ca62de4dcaa4120dc413141bc0b0a759d287101471b7ab52492da97bec8aa083f04f18fa0bd2a0d43d2a4232c72ade8062664e3c402b3b76aec5c0c90d21b6acd1a2401f2157839232d1c5fe3baf7f5cbc94517816f3171766eca5c888bc5fde82210c0697967b0fe226937319675e3d7290741f6727c4cec8dcf11942991870085c93895959fffae11ae108a6796c753277d4987dfd50f027d7073eb4f91cad30d2a0aeb09ec3b30adc4ac715f5474b919287fa3db252fc4b79129147dc90caef2b0da8385cd331454a0d945d00121c97623a8d42107ce7d2fd4986429c61fff462f84fca849672fd8d4ef319426acf5603d9dd6aa0d60a951d045556607d4d656fe9849a249f4fb08c53bce970ca4aeb0f1063a408b3eec7fbc75a55042a5fea39a1060c769c85b0c26cd09ac3fe86b7e14e1b175f83fac7adb60a4c9219c4f0e6e5f9286c3c01a073f1931fab9b94022aecb9fba3746ba8a32f5f54516a33762772e6cffcc72e3e49e2044a7c08be3b26bf7ce9160b50d42898cbe97f3345af3f79e92842d89b1199cc835dba6bc9c48ea95f8edb753cb5064cce03e9bf2103bdaddd61f812a94f6cdcca68111973446db1e6b4fd9f75307c30bc80e5d430191d110b106d17b59d1e0e9003f073d86dd44dd75bbb32b18aa96457d820543f6c6c9b40a222a60ca8cb95f93167d48adc08815e71c914213a326a60ac98eb835468661cd7823398f4786c64628c77bc4015ebfa32a9ecba77bcee775f3635de529ca5198de0214033441f8d519162283fa910b67638dceba515a30d00b752798f43d9f9458f3d50a321d2992071f1860fc6c7abc48a0574941156d6324c664a75f2b503811b0a46e8a5526edc8e2d118e1b987d88fc1b448b2f5de4fa84c3bb40cdee8a9e163633f3d102f55584c4932adbe67c8900fccdd327169e39c5c47961c2e41d3381b1676a478651b8baa6f003bcf2a02343ebaa318849000866a5b566a9e82dc497cd238a8046aaecb29698b74697ad31e54360f65ac36a682c806ad97ca6b105f4a39de94740cfa48ff0673720d9241c5baa334ac12887806ef5067a036b5176631754c993282bb18db6ef7088bb40a2a761dfa10ed2815241708e3a76a6989d7c10f1bd2d8d26aa706af8ea3522778a3b57a02034c407f17733e88eb9107c53c6ae346f3c768cb77b022eb0d59a4a2ba55c2fb028bed46e2bdb82ddf112b37786b63b2eb96a5bfa2653864cfad50be6599c5041813c557b7c09407d35dc6a3853a2ecfc3223762409807a64535534c1b114f44cf9736869472181c1cad82f6ba458eca9323cfcb09827f34da8080e227dc440073b870b25a3d45ff50672434311733d3d295e5444b64f3dc1780fe21a2fd1ab5f1da9ee835499d107b562fe13cf88ec64e39456e365d643a9fda47e75bd0462a6bb5146f84e1d6d449c8ebfc51e4cb05d7ecfbb9f2add61dd24b9753bb5d2196f534595da1443ac25aa4ca69c1a59a733766856e082b84a0e9989809f767ed0d008608090e7a21d8a82c0fcf62c4c2d2cf92ec2554a3af5c826acd941f5f5a856dfdd2715e540d23d5b205e82a0265a14f1c2f34e23a41fcb9fde0fae981e32f5c1308efb8c0862ea7cfa405dabcc1695c43453520d2bbec0055176b00d3b800b0d9525682b270a3a8eb17d8d81726b3d9592c6aa224d0229343b1096c42fe27fbb9dde544d80b2c5dda682bd2bb1f4e757d101b542207f20eb79ff244b9be1f9a3c6e482dd8f51e357d3e2169ba040e7c187d816c1284117585930ea0141ea8ce47c9be11da82cab60fea16f897a8c064f02893fe5dfd8b85a7ba47cdf0b458e608d515109b547936a5627a11216f070ed52d929c24a55ded2fe67152962931ab1bc43b840ac7b20561248c8123f3891615605c8ec30e4063720ab8b511bdc318a404b83eb5ada1219217adb0344f4aefc352d7ebf6b65f815286b051dda8702ba52d04e45549de289ead646772a3d09d65422b91080a7411c87a6808373c3900d72262d4d93215126a0ba2ee5134f6d91b7197e3ad1f5008414a9dedb67bacd4e81659495ccb1417436bb099a07c7b8bce2493469082353d1d64939183bfc04141a08c4dc719fe6043c8f29e6ad9a6814560c0a9da638b9222d4cfebe55fe5df9b65da15819ef5f4b6ac2593e8c6bd1cd9f98982506aa99470e5ddd2779c5bc9e5c2218797db33b8b148016a172c9af7b35afe47e5888ba8140a74243c2b6a038b24c36b3bfb7de3fe66209aed2240dbccbf5d75b67e5ddd1dc427b9b21c78808c4c9209639960075a7b9cd0a2a31b3baa5ee4c0074df164c598fc0cb71ec975a11ae79446d4508efa90e4104a366b84dac388c8a4220099014e6460f949cb0f678439fc86d744d0a0d0f12402637186bc57675241ac4d62cf4c51c32b6d8c868f0b67eb99111099a041f40b823f5049e86656f5a6236c8d2ef1478854cb147de9a06973bf0d945943c5827c1bb5c2021a5dcd35857a1390875b10b52968bf4dc78bc66c90ae3d275f1244f94dfdb1f86b3c8d6552bb2d8deb0a098110e8b99e0c18d9f8f2242784cb2ed8006f8d06ff39b76e4455c4f674b006de7f1e76c5c514100f01bad8ef985034f8b08c049fbf329c49d19a394f8582ecebb520bb57ef908a472f4394f979c1df104a21f387a90d7a6da3d97f703206d35d04fa61d99be772985b6f5e3bf78db10067d28f6da7221f7c22914d7a4c3e938e070448e7e58f340125d6be8218871e04fd941ea3d451caa8fe18f2b0e35317a7959883cd1dad66534304f4e626ff85528c729cea3331de039b0a914449a434d4e3c9590ab44e9a102c6436be52393208f6afb70028e2b63bd302887b4e7feaf70e1f082329f3f3675ef771c7a94fe3bffee58b0ae68cb5a40deaef15fd3ce47ac55a8af489bf6001398e6bbefa6dd8a10b2e6582c216f2becd4c468b2ebf03807cfc0d1f856c485cd931d26598af29405edc3f94afc9ed66edf9639821e291d3470a231b8ce83c7a0360bc807b3e76dcbdc38d50e9e6ec8deeb9100afe26390712b43bbbbf0cf725d4e9bd905e4037e30fbc0b9e670c34973f3d459e6b4644c8d4ef32559113ed692f26630919d0557aa18a923236b8553978631b88dd52528d60e0dfb186d3ed9e298ebc07cef95f60bccf011e0b8017bc4269145ab2332e747f9631fea0e8bc1a517906d96a3ab826b5c00427cce0b95f4e06742e01dcf8b9db8e45d8a86ec5597e3eb7b0e0b54d5b30d38e91f55fec853bd999ff08179410c254cf9a187b7422fe843e84271465c8ffad2d7a295fc96cd69096a68f5c8dae5c6bc4b4a6b8e726969f736f297c424641c74058f553b23eb2962ff312d4cd14909f379d04ad7d9b007331f0df916d0e0955083166e041670fd14ffaa2c5d81e7ce84347078a9a47613c408dc1caac6dfbc3eef4fcfb149c0af58d5cccec90c0636c9764993c0b0c73dc16fbaa79b670ab19ad283dba1d75afb66333e7d417be20ed33a4748301f1cf8b83e09235a1426c4243c458682e3303ecf6939ea2e36048308512f107d27f2a0460921c953f8bc21f8e3a84cd36a2e03aaa7cec1bb14350325958e2a301ab7a293ce4b23714b716ac16ba603389a79573ab2d62554353dd5989e154d33b5197f2bfcd032d73733651eabf78dcc50d79bdaf5e6898596204b1e92d72b99faa6c53053c418d882a64c5f432e39cebdaf644a614e9d8c904c6c88f942220a8de523cc12509853b0345503a7d331a60c026bc96a7ab7567cc2934661166d6aa1c2bc892a28ba56a70500556818b87506a1b64a103b73ffab28d89ac46308468612dd421762b38954586056a25e291ed9c596c942bb81a1ecda61e56570c8305696817c545d135314c691be30ff89af07cb8f2531870b20bb142441538aee11d83f4f0321c00d2964ab9eabc65bfbf93a40435b80175eaa6c2e93a65bd07a726b3a6862eb8a82538c50d71ba24a1a3db4902b11db51978ef8599a369e970ce9ec89c946dbde88766fc414ee8724330c1b88ffc82eeb1a5ae2bc2ab5269d6332ead57d647c49ee17777a7a24078d449e23ba4012ba9d049958d71e5ce14eefca544eb2525d1c5e81b53851783d898fb968e7b78b08f7e41dd7ecd74c547e7e80ee9e9c2d0b6b971cea07dac18814437e4c11f93b6f8a5d05224a19f8087c49bcd444d768f9e87bc27678dd9f243ad1f5cf83ff864c4808762a06f98e6e3eacc3d53baa08ee30c88069347ec7242f68bd41a696fc418062bca407bd61fafe4819626f750b15a1d89155b4cc77e3460f82e55e366ea656dc275a3469285ef3074a611c7b170aa34a5a64b6af7608f79a37fcc4f7a058fff8bfb43d49c0cae476840cbf994cfe9715763a431a8606f000d4f792c0e05e763838ae976ff94420de2a10ad390712610114f3cf25e5c4fc88e3eef27c7716e5b145af1de384916b20129c0cf9640f35e082ee89f56921669debd00fb960e027f9cab1616521798ac13ede777476c4a4e1073d5f02195efbab20f7e8aaafd6dc22b91ad232a1097606eac9d97bf0327be0969bf51e52585a64bc2fd815dda6b1cadffd2fed086353a3a9a59b0f4cbc9e0c53f9e4c10bc0b1394b3f1a8448dc1558931d84fa38994bddebc6a034490a9179cb8c75bf2033de45330457bf64a8f1c209161502f3050d7ef411ed83351b37e791dc08a56223a00dbce2a814b63ee75fb196d2a8848df8115e626c4916def2e37be0f1fe3bb599bbed904102e1027236271402e53615f4e99f0cdb7c2a8de989fd932d1f7d64dcb457d3c9e75073b807d9889a5ee7c8f2e89f03e52f5a4638f69c3aa6a18b5cb442fe52bebcccc3c96d879a4f783fff7695d321cb4f43a3276b79e084fa880cfd81ff818a03c7ce8256fbd5ca8df4c80e401a4afeb0a66173b9a49925107b83c0925312cd99d1c5360116d5ad48556a0aab39abbb6d90eede4f4e3004e88cd0801ab2cba68164e01e667e7d024a2045e8f33c3b73e79d4612d667f5cf00f6e5cef2b41e68283ae3dc8aedbbab56def447b0c02d8c7da2f0dc3246ded85327f4f57746ecfacb9978d088b15a47c836479c86c9813fd8a6bd74bf9997d5efcda5c32b3a87a2664b9c6d6fa3300e66f2ae472f54440c1b265345ea806c91074f6da72e52016cce6539f4ee8ada988a1152a8c9806ce95dad119aa2331d185a0d16f7ba94998593c82391333d94238568a1be54d5d5babef9fecae8688acc16e83758c0403dd10641af6427fc09a42e38829e1ee9e264da2e27ea949c7dba13f77c93c070914e53a11ddd759935ae402cc6c62ef3241fe127d35d0ef12a7bcb37242c4a1a31937de8d7c8f895527a2d61e083fca2b9e5ca506960599a6a9a133e250ca884e6debcb4bac0f32c179a3ef27a3bd95da1100eada73b42a631991b1586d560b69dd05dc62ea26abcf3cb367ac01660bfdaeee5b14c490bfd21b87553056b44307de136ce1f445999125107f6ce8aa87e581f9a78009ad697e996d0d0c1f864af9a37d73041aec926729e0c02227448f4593d9b663843f18208d1729dc44096ba2c8af4081b16400988cb52aadf6e683d191bd301c0ba0bb73fa2faf9349bd29f0349eb93203c787acc56328894bc4efe44ea1a3a0ccbb0508d1f83674cad61a49af8e3fabecb12d416b65f0c9075ee93bca82db388dcd3379bb6929b1ae9c0d4348ba49c52c653c955f4f116e2079f12156df0f6939221f008b619913141f45b56b579eedf516f34afa5301b0de9a825c8c1be6b44bf32c580f302d74f908b459431d70806babacba233b45ac90324bae34343de4e8a8a6cd0c2e5bc6ec13c8a8c19a32b171f088e25b20479119010d3c2db7d122418aefc76e8cb0f3af012cc0cff2d841c5385b6321a2b0047e93eb37dff071163e0a248e23c511ce1b6618822571329bb54e48a6c9ca6db66e0c21cea710a24474e26c78b90da2ec2ab9cf6d8b3e171c1b764f007dede138ddfe41f0a1ab94f088de610a38110ce86ecfe1408f791f8eb9cc25081a5ff494e418ad81393dea97123e9d01e1c62922d2e44a297bd9d5ecbe575709cbfad622354b9dfe653ac6037622f6c35f123f0a40f3b9eb03082ec9e3a317fdffab46504ff46dc81e13e242ae2f194c673c2af7b44f7e4a7787973740530468d536833af265bf9873da65e5b9770b8be86d7e4e13fcb433d783e3cbbc8ba28ba50ef7e5639061f1eb76bf055d0951a157f970de123a17b72d389b33b4d4608c67fc0b217b32e526a7bccf1fd11d9605859a9dff431c85d3ffac3afa8c786a0fa0fd89e64cbb6c448d61988b82989392d63e60462d74e74b5d5d63f122bf877a7d0ef6a37e4e79e8058fc87cd423a2bff5ae33434026da653fb10c527818ad3ce7c2b8a855c78dd5b71105fbe8fd647b5b93a602ad0d4ac0d8fecd04fd8a04eb75aac75cc3e4ffe7603f9060a85386b8cf2a880976ebef84f7e18bc09449c7717e587f55101d78f5e4195ed1308579ceea42db60cdeb8156fd61589ce589e69d361f34f551b54d2c4710fa588ff76b71c0c70b963a9880790b3a1b97076208312373682991b5b1f956509d5ee8a74093b31329bbc2e854888bebc8a42b5b3f5def1251b7f8102db6377c350427aa1b83151592b2bc45cea8bc3a434f1996317ca6dee86c11c0282f1eb1156d9e972b5856487d00532d777869d099001c5ef8a171ff2ac447599d18145578ea0103e20a187169d244048df967ee13c3b94dc5e6ef7175262fb00ac33c68afcfb45d388eddb4fe167c85351f3dda85ec7ffe8a41ff707cbe50fda1f544d2ce0164b08549fdf1f21bbde199579086cfb4295498664e5ec9ace2d33b46ca977d597d0f168e2c25661a58bb889c9d7cd7a2485f87014f2f45c00ea560225d2f6234ce58782845145c10741eea1adb25da0574ad1a602aec7be5813b50a14bd35b832fe47f621820bb8cf3bc85200e1c36a39ec6998a2a4b2b00f5ec0131d6be454a52550ac2546852d3f624b23dcebfab92148190c6f825ac45aa2ce7b8149a59a7e6ef4733355a5c930bd41f26437380f3d27e377885d747b460902604e2b13380231bc8bb1990a3459c98fcec34943cd49ac44fb61e61bc2465259d5406c76a4beca62c12a855f33b04573c4f61827a82c911360cb91b55028aa11027ea2451bf153f35adc2cdec017587291a6feeeacc5d1215f3f691f4e959766431a5de891924a1641ad8fadb6d41dde1178bc07997004052a5d8234d4f910ceac7afc38249eca4676400553f9932b737666363e1eb289d5790d563432ab76298b800edfb6816c1fbaeaf700027aae0f81ca665852d1708a614f4495428ad7f3a54bf98ae45fa30ade673de5fd845817a95fab4878f2f962886b22094a5a6911cc1397cd8134d548c2897ff12bd624317376bad8fd7eb5585d45af02276b0050bb236a103eb96815a5a9acc08dce80150d93e17b1be72c65dc6a026428162390d712c4ec25d86a43cf228dcc808b21c2744b7a8653e143c86cad7008f84cbe2c938a59f4a22b3c10efaa1813d99e54598629a8cc65fc36cce693ab4e1cf232ec6926e4e5a57edc71c46c1cb83aa0da3c51c485f2bdb36e18eb51954eaae3b98cd336d03c02fabf080eae6530a6c8fdf4b48d3eced987a2992edd06edc5588dfc9276de7c37854b9228b5230ddc43b289d5d63f1a69712edf8747b417e0270471cb9f0a5a1952ed637f857b1ad39ec525a5ec25a620845929c7cc370413b5945238c3665e58358812b3d0f26d436da2083d3b8d55b687c131220ce43bd3434b089f78161cd2d684921f5e4f78429517e2a5033442d158c7711ad75513f3463588ee382dd4cb1f700194eb9315d356161753960a22547d22ed78b213dae4e6267c256b53082464ac721bc9c8035b03c12531c810c9dcb825eb0aef0136d16abea49c31329612aba5f4bf0298453386fcbc77b7778f50792cc1a91a886b200c9bafe829652015e40267f6e65ce6ea87230c9aaa6e6c275d6335c04c473647b67871139a16db997b9e79a8032061b2bcfc6323d0fd18696af83a65e7f2d3819111cef071764e60ad946c5d8c7c6d6ffd605e793db8005e7f53767b37a22301fce9c41a5f47858e4eb888174f1a8540a64a05560f5d057d9c87174e607f623bee451c334ba88826f50fa9448d626b7208e1411df5ef0b48d2c6c2900d2f63209b847681a173a51e6920c5abfab9957a20d1ce7ed61ef0f7abc3d5c730d22443e32ac30a4c3a6a87b6c5d26f74feee8048f9d77bde7bf77bde7d07fc0ee24bac5d366351da57d9fedf25eafd1e111c13431ec677293c66b5549b9834876adf441d484febc85eca5d04bc91f20dcc9891880c5a4e14ffd2225251ac02fedfeaa45efd1248a441885da2c4ac69eb73bc104b44895ba017e68c41551e05cc9aae30d71417b32d0189286396f4ba3962a44a89125952ade7c78d22026babd481ca774a43321ece3829b77568e193a00ad2c507f29a952f23d035718e526441bf9a0375245d3e54828cc5cd2bdb8777f4a3516f11f20dd1c8168842f7ebdf87b628c2b17102503f21724f6e7f5e99623a29985d1c6f16063e1f72c215b2c5e1dfe90ed5709fe72179d3ac836809e773b81d96a025300e063796443aa1988c1574187d83add737261d4976bdb73ba470b46b224524074aa7b220f6fef160e1c999881f4a2938ea86d8d0ff7e3e5087b70ee68a34298e6029059e9c0d28d2962da4172210a94c58f88ebd1ad9dde4ab3daf58356437dd8b7924b60310f314bef811c31a6616d32217531e9b7c8aeace26a3461469cbffe44c47a950ca1e604f92593b87c83882da5bcf389e92f21481f572d4242d019e41df2d161f93d4661a2686bb61d9cbef7c26bb2a3b57952bfea704b2ca0bd45f7ef15fc6786c4b23863e9f5b1dda5d9462818d422a9e33dd885c0006a7bdd1e37202c0949bda9c973daadbc1b8ca69dc09cf523b1f761c797c72a098525bb620d7f4fc4a82d5c12e8c9e5f56cbd2513bc37071fdf11d0da38c7222236a3fa2d5b0b5719e5545b1a30e348e985ea7d7a41fafe69704f1654d8da4872e3e55eba09359bb1bed43af174d5339ce8a5c389dfe6a7b92d27820ac616d808957b4be8da06f3a8101ae41a5bec5290c251d0f60307d030e35474b16b6976951b8717989a1ef712d9dba81770bf53db236236838b5747586a9741adde54c46c99ff4c6434fefc7ac5ec8b07c9b25496806f5bc4c8ad3e46c34aa4ccaf476b032d0c0cced98c87c66bb8eab11e47cc609faa2d02558cfb7e7b18b016e865e11afd3fd1a9b0d69ddfe7963b35b412add3c2e38d8fa1c3598a581ce2ecbe26754513a7eae9cc43dcfaaa109d6c3f588114e623d91083f9265e9c1a9516a65deb6910d6b1c8bb0bb2951ce3ee685f07e892228674cc3fd7acaf504079f98f656d4bd8846eca7b9ed8d33828889c0d1313886089f7865ce0aa069a736b7b4dac7904a83902245259169e8eb8e3a75f42e126c732fc1b19f1c0f07d16e160858b0701421339d73902b12ad44a9d9335ce348c84a5b3faa1fb1eac478100c102394a8f6f3cc54267f1b63d98de44dfcb221264bcccdbeecba3b4ea74c4269d0fdaa14f95a21443ea9c2e7800ed9ada093f4297847dd6fdaa8f3bf4e33d00324ba5768ff107ee03614aa1fa9b16c4015f337dc4e6d600edc75a39c1b5db6439bcd031bf2768a6ff2c0f1d14ef2702ec022ec12f11f9e75628a28be9d46fbe43cd0f0b82fd347e12ab6fbc50f6608069bcd3276ee1452fabe0eb4b4b4467da5c0e266526f4747327edd125f946ecc286da4602a84386e23dc921ed00303e6ffff7144c0505b02222f9507dd9598dc411e8f93dacf84c70b03b561be0ac403df84a8abfdd662cbaa210b5059125f6f2148c2e8b48159e0fff1f15fa504747f1c5a8d303208ac6590180da845d1816521ad7d8a2a7143e08b5993703741157b0e9f3a46bdf267b3b80cec8a950d064ba0867f1248ce7dac2ab75735707b90b507529b2d802f60facd837964f3e8a5de57de616e89af573b6605a73dd614be517e4f616c014a2540cdd216c2a2dea22dea7d34e9a6eeefaf52406268acb4813829bbb9dbda2a16b5d3a5c136b6debe7e9932503fa7c66be20187662cfdb692db5d3e789869dcb9f9cf34b76fe3f9b2a8a1ad95bf7ea494eb741a7b932d61d1c4c8bd7956df516f41c0572256cc824c30e29e63160e431f980abd524cc5e885a8ad3b1ed0f411ee60150d04a7ce256a9b17fd1ea7adc43e77d560e7b96767131d48fa74ad719a67c386765384b67bb8bdfcd876abaeaac10d76ff367a0aa0d3c493ac247fdb0b7d0d228537b191c0fce9b333c31184bcc084f9ff68bd3c6a68fe54fcd85c5c220b15afb694907ee6a258e236a4c95b4459e65ca3b64826c866eb7291915d9b7d9a4a617850ec730130fe261f5758b7c837ba2c0e1801d0cd7ae940c89b61a124e7ea5b090c63b308da18edf75612d98d8b1846735997c3c75efe408405e6dda4604084f70d272190dce490c886b00f93ec2361e89c536bf6d2f4ba9f29dbbf59b328cfab7a03b5728ac4007b571aa5d087b9d6be807196776089b5e79c901718fd7623b1893da64d487f83d85085d84906b94837ada33ddee3f6f6b3eeeb62de048f49111d870ccf70f2e87f278f2a89b68037da5b2b519f41740badb208ae35c7c4e98b0080c613d7b85a1a1437bb4694ddcaab09cae0e9c51811f233d2867d8527e804171e58904c1b452b355e19258ff6a92e27143fbe218d60680e9bf212db3412b42835e009b05eccd11e1d22ff496bdbb8734adeb84f17c04765447426708a3243cc7a45e30c64de82c460b64645e12327f8948e6b3144146badba15c6fbdd0de71583e69040ad694a190c4d92133027499c4b13583a18df7002db99abe43c8209881b0c78dff0ee4e922228aa254566c0e8e0267011bde8b4d296b0726d4b3e4cccf584738a32e57773adc63dd164c48948f1c6dd945463e9f9e41e21d636c54d902c37bccd177e9af7bd7376c9d950766167deb23415b7ba0d020534cfb16aab6451bfb75de00622702e64fc7ab4b5e4533e894850317da135b63f9c2474bbe4c810da9041cf32519461a13a23011c808cd439eecf8805b7973948234538620734171c8628407436cea14b8e803b7fb10c26e197591447979c25169042cb22712c1915411ab9a39d8502348861e4570760d188959096d89a11916bbae41460b8ec43314e6ae786bb781fc409d248fb761b5667e273aa14a72d896065e9c3869193ec3d656900650deca408e53c8754d00661762e8807f21123e81438f56aee1b02796a0d4cce753503080e53947b793b9985426c6e4380c49957fbac13b122e2a255cf52c0db8971b804cdfcfe7a436c7740ce51e6880f6679b030392498d038ecc2d86e997fe3edd6b5a3e3d80e5e320ec3df508e687ba5c74d2a42174cb9cd4282f86865bd30535ed7c1d9b723a1e4af251dedeb92e39a22df8a85a07e3b2ff4f305c7fe92e0da7c8e62e7bda0ed0262a15868c1c0e0fdabe31db6379818515a62ede8c2880a8d8d519558a49bb28b71836975557c8cef10b4efac4ed0d8e6b4a3dc433a6c73df4068a6073e57e624664c4993b708531a8a42820a7f77a5cc65783f9009121e2c2f7e319a240774920f0cb4f8b9d05ef3b104c3799808763615cf5932c17010e78bf6f6d938fccdbf2409f2a38010b0a7b4d74c495764c34e8a403b37df9e0dd5f198f92ba82cd496e351c1a23f66874a06ccfa0181e53351e446d76978579c70b9e5b5332e6ccddb07495b30b5e3ba3de2f055b714e5e1a931d2d00b7579946bddbaccd02b4b7475cd3df1cfc8ae955ec9ca0335d109371ecfb9fb72d01b708f3c4c205ab6d94480bf619c6d709c88b440a60ee8753878a6604ce2013fcf405d74b67633d454d9a380eb611f47b56bd7ef898b0b28404b82a5e11d2e2c056734e03a9ae39d27db0de2bb9bed6b3386a1ee526cdd3053aa31a6946591ef64f38014feb3a55f4144ec84e602472707d03c04685869e0ea380633015e873c120cf49743d278eaa21032e9f0ca1120cdd732143c491f01fcde60e196af3e1e06a08eed160cf0b51cf3b90d8efc46e423546bd900edc4411b6ee99f1bb2bb4e74fbb249196eb962df7079c6d48f94e085f179382557ad53afaee7797d14408b44b5f1aa173b39b695722bfaa1ac0f819b460e60f49fd54088e5b583a270d34a9ff078058569c532ada47ed84a522cada4b9609dca1df50a0c65450517919027ead70b0b0d934d91cef0f0429a3643fc40d0182aa74e395da5c6857af3e62aa722f037c6bf91b46b18b740f6417a5896484787d52e3cc454d5d72936c01948360620e9569816b264bee0930b57effc1170e788d1900b3426880680f4a7db7ad742a84d4517cafddf0479d439d820b38118d999cec1a2302bb5749191a5c6afd839b819a385650652da33cbf1323f411a2299cc42eafac05883912fccc29331232d8ef6770bc29e986238b6be73da6098bb51c671e0bd5c056030c01cb2fbfe0b2f550593ee6e507811e3c6a166e3cc2d536967d365e4df6d549d5e6470a3307bb13a0a856517d78adbff92da3a49622b94ae569ead59f77e62aba269e34d7832633c66421a1abb6d50b3564a23fb3fb4e2835ec3054ddd85d4019417ab35d8b04089182bf8402235434bc49ecb0b472b5dbb20e6a5d129c33c4bc586084314bd40e9b318b48ee3fb968a987a14856398615a9fce1682942f3de55f3f6c068e7a894a12e6c287f881442639f962f1fc9d00f19727dd5428cc4a9d8f37b0a6a542ca5ff75866e6add0468971cb453dd1060fb9f6a0120af68d5951c2e390979601b95c8940d4ee968b6f544e2820abb181d406bfa1e7cefdc1fe8a7565b24420e84d78de2b7366d0136c9f8fa544fa6d1ddf965283b8b137357306112e698ef5adbb26104e6d3e02483e7c680fa7a3f2ec015f4560fd38141e5c0c182bfbd6dcda1f0ed3f9db59d3bcadbef02b16a5a623a7d0efa7560b3a9a2a42530d5f8ee199f6a2d4727f742d8e49a81f0a6f49e28e159ca7ddf4298315e60293a4589071d3b823e6b294c3a1e653798e164c5957a10c9d9d0806a7857cafa357a4ea5cf43bf2586bacb82c63915893d3b721737371eb205342272c40279a71f94b86c21423c1bab6b3ad2c356e5ab48cb566b57c663b32ff38a3712e842ad615923fec59cacbe4c10a294334b36c2172c84bd6b3f239bee1278c9e9ba1cb49295098ee984434e6c18a7b48456bd091ea9737541be401ed39c9475fc0fe140df03c58c134c9bf2628318978b0d2d8b8c7877ff42751c714797cedfa7d99b629a280cd6eee15f6aa962151af646a4b714016152894c8d40f26f158f4f0c498a1601d5ec5bb2383ef95bb014665135b8bcd7ed782c8c9a405155c5ea1b660549ac97cd085d3358f3592d225cb9571f8d33d170856d547b39d85012fc9558098cf7dfc7eed09b01a17d9f849c7e3a735953120b8d765559175d22287b6c2b1ca362c81bc4e328045b6d94ec420590191f85cd68fcaa776ea2274329613d7ef9cb2d53b3bd272eae80507d8eb273798f5c4857f21ee125b47ae0ad9b108976379dc9495fb5b697462cefae6d8fa54e1ac1f5334b1df23c2b98d4dae515346dc94c27952e646dd95946ce599101288066311474244d1d4bcee85cf45adc3d78497507b0a2d3ddd92d176ed48f9e8d520068793f8ac76bddd44f71922d368490c50a07bdbd2f7ad102dee49d016d3a0a232fa1c705429c55c642a54adab7405c080b609bf7a83c80aabccccde1e7712170ac1e11f264414fd53dfd20bd2b108cf9b019f0a6a6d1fc453da021cb6b6f7808a4b8a8a10cdfc3e04b62c84c103823dae65e2d1d4c687eeef107f049cfaa5104a0c441ef779ff60dc6b18b00fb3d39dc2f705ab1405bd3e7023b5f9eb5d0a9c0f9e1891bc4b9b29f1465c037ceba949c57dc6c9c386727d7c2983e8dda57c5e4528f1860ad0b87cbaa17f43d0c8f5a567f688e220eb50c15b51232873664024ce85ba4ff3d1037973a03be4c4e79a2453922d97637bb6e95667b214452a1948d5beb38d18a5ef09c0bbe99405974028c955baa91217767d7aa858e12d091d59bf0c24cf9506381a49ec90eeb7b5494e8cdaf0f368b1271144447bee5243b9af58f82dd8ed369e55dbb879c187170dfdb8866f346e51560f025ef02060dfe22562948b55f7a5906e6fd69eab56c7451de580701b5f458348fcf85c0dc993daa092415b965e2497a1793dc3275a954d89dc8edc8ae4cead8d5f88a88cc33bee3f6ee7f24b07c71fdabe8606af678cb5bdb9b06477cbfdf7cd2a82ef02b26bc80de25a213cc28d66cf298d5e66fb5d0969c20419742956f040d02f634fbf2bb4c7e2c34c91e3a0b7bfe4c27948d0c4249a943c2ec9010bb25b923c737d49d1de553be7e36f4bd7b6a55db3121ccbee143e5c76cd2e7274421be68dacdeec88e8f609cbfb18c6c0e40622f299403e03400c5d683ef5c7525324b4b51f08c37b7eea1957f5de3dd1ad1f2acb21be82b6e613d2d347dd184cbc710d6df5e540cc8edfd75daa59a5488fc449928f7c1db4bf1dd3d94a2a67846c06bec6242287226134a81391926a100eaeaf23450f244849215f243a269b504d39c9f0a83e1c43730357b2a1e41b69e59f21590606f74382e62c3d79cabfef678caea223b4085b7c1b29e22c08d962f68b1ec062354c39737208bd5b7fc089e2b38f050a6d6420710214871f794f7ef709629da7f26689c09daf128bd1330d69f4fe4f637d90a4ada6366dd13b97cf7693a02556e7637015f1596e90b551c07ba7677526dbfb66ccb8d30caa1d37f072dd9968d20010ee167cd352a46e0500e9a5849676116e7f2c3c0cfba71e97007645f71857b1058bd70ccbcf3c85a5173f0f90d6f9bcd4582007f1fbebf431ca8c675bb4cb91384eea6bca1b9c9e34301ba0ee374b915304d8ebc77149ab620468bb9d4ba8516a1a59b947f5d228902cb0424a40da0c9f0f6e473f241ee6e0d7320aa7cdd578a04490d06937ed22a84bde4d9d88940fc60c940a8463ed39f63684db5787f469ea9942c366065e47ddcb858cadb433d4875f29aa03fc2702da9ad9585e60caa4a40e8a6203a45dc72979d6367bcfcffb068def823779d7a1d5a8fa7645c3341c46e89f5d6629bca097e02f12b005bf7dec2dcc47611c7fe3300f6bbeeadbc231ec12dc071f73d30a2828e2a3ecf90db1cbd8dade52ca94e4de7b07c205530577054e943e4c63ac797a3c4a3c3e9e20ae00eade6bc1ad56abc1287dd319038302e62f78abeaf619de030cb144f0ddb83722b5b75aed56a20743a2aa8407b11da62fa5253526867a0cadac7c44f168c264adaf879bf052dbaaba497491f8a2d137aa6f5b96476caab75aede613648545316c0290c8ca97da56d59df39df896705aefbdf73c9fb4a0424feec93d3800be17585f35e5de04c4941b2601b51b0378f44a9818bb6a00b371e4182c3f9a7a080fcb192828c5f75e34938fb333c16e2f5691cbb7020e29ebdd2ff7de2b73da42fa9befbd5831ab6aad02197f63ff1fe849d3b5bd6b2bb2ab7745eedece8c63bd97d6b3b7f302fdbe32e79c8182a78d5c7da327ae40a350db796bb057d9ce321e1632a8988a6364b123a388920f327783a12033866430f59a8c32b744efb699d75bef7ea995a88ae26923673236752b7cd6bb5f76d37aef257105c5ea46944fe64a69d215c24bc9459250e6568881316be5aef1826e310a303c3930d3850956355ae29c52e19f1e3a63e5f2707937c16b1585efb4912bd65f4ac981b9c5240ede537f2945839d6975f7facb262d7ebabbdb9c367c3b7700c0d4ea89e109e289a25567acf7eefd6fa2cbc497d626be5bcf9cbe86d6bb6b56a68d94497165dac855037c818c099bd346ae56aa57a2bea6afd5572deb8d58bd6a89071d5353c62f104b2b641c048928428e3fa282622b29b4d1d900b9304d0b732242cf60fac8129795fc1df9c1559644b660c01911a23b1122fb5b347ee72ac7ddf192300c52e4068e1c494511248fac184cc20e4b60e4769278794b222cfcad5b6b7f44460c953f53e72b3062094e0724761cab3ef014ec2951e38bd293c064833088b188089783b47584c9496744f04f11c21c3536c70647208a07c60f26462100e576488ae02082e4a4c75a6badb5d85a6badd6339d1240ee631d5913c7b1945608b03523888aa5163b608440cb458f264c528884a7bfdf3205182b288af507c23142231ce7799e3a5a1ef8df10ab3581ea54d91f189a683ef0b638d490b13034f4f48458e1240a8867e6558cd55a6bd5752661257f58362f8c3b9dcbeac57e44425061d223c9280a94260e5732de634224f5fb30a57384cca281ea22bb98b185bb7b56149ab391a13ba97f583d6eec9795234daf6c257f5852b124fb2f7906c9e493aa93750c9db3b203b45a6b6d01763aa03a44429d291f0288a519564aa4764d38200308e24372a2d483e3010474a28a2851f53889323f64709301c54f930c3b4b3a289d6a560c7b4d423a1900814207488f08b003c9046a968d3c60e48d118496272b15c8e102d762c6083d4460c900421792118f0f74587bd8e05275437477187c34e574f0c5b851410f9f974e948f9f0f8e2c0975807211ae58daa3d3df1121add39f112a278415b6923fa38c34aac35a063948d19201e4c747d68484838987202ec7c6024c0e6bc961e70738c5338542a6247144b4f9ed009d8e64caad3ed534d8a65996e5564b9cdb6ba3866463001e906469a7a8899026562692ec78a1f3254207e46a0aa678e48cedfbffbf308968b5f6626bdd6d562bf4caadd6270104411ebba61ed2548c9e22276a6c15f1b2f12163addbbb001248018975ac9de43065c087a9a3f5dcb8c4e195044e95809200d3ac9192b2525502c000d78a9a165b1a381891f117345e4a48423c719d5d96143a05b9f55093dafcc8ac942f5b2c7564f7b9b15dc927a95ec6a159c935611a636c690af0f0e44f0bcc9a05410fc67fd96aaa9e17a41568554a46d89a4aa3d16a410f393ccc5835a065d35594e50d0e90d80fab9fbea1470b51c0e09542438d0e863c153ca91141538477745385e4e79989c699852d3e3855762186359657f249ea88a5bd7c0d4e4a4a48f04089a2528ed10f125c325932322c0141da8043e6dd2681b0407cd67325ab3c30ae1a5893080d05b070f4bca64e7465b800122c2ce30e4a040f05e0aa86c43822c161c3e4802694c4840a8608277c312888394f6c92d79a6686167987114c0716256418d921e545140b28112ad21585c593103c21b1849b4046ec05017b67f45e20265677a621d3461e3101a9acee05a6ab0c259cb28e757eecee04c446423903024d1b49657575018875e6137f2cc77e78ad907a1b57124aa9cb784e30044353d34c2141a6d5f6cee952d4f1631fc2c141a78d446333d9fdfdddffde7befbdf7e6fb3f9441df21622695ccde6e4c621f2ecab47a81a878f1ffbdf70eb5a5e9dadeb5dd2f00c9659a9d3da23f6161d162d2746def9a0b9c8dd1ad94b3fde278da48a85e117d8fc94236f7de0b2f86a7f9a49573efbdf7bd56c10d8c71cb14ad5b045994a8ac28aefc98c958e4b450c7807c2a3022800171c978a9c51756e4a04ad400c0a835010fc74382cf8671386117a5724145f0a8456937bbc0822b077621c806ca8746ab5aab17520c7bb6265c10c2e912aae76ae502af67aa1456d2a9d8441dd2373f5fc78fc3f0ee8255906923d7172e2171c228ecd8be3128ab1e6ae34415ecd662fb6315f649b33058859574fa40446afe08bee32d26cdf9fa756a61617c15d114dd14e514f914f18a866272ce99e7d9dde70462824ca465da48a22e4cf4a55fd0a82f6e76aeb130c1218bc8d1520445901421b038b0d891f10e4cba74281141878dcfe79f5356b08b2b17582eb4541c8cefcdfe182b5973ce39e75c4b62f224a02751799295275d79125692d62da9cbb4912bbe522750e9e89effe2f31ca2e240540f64f540578fef3dff6615aca20366c9e34a02876e56df391f9788dc6ffefc39fffb5a4e9567957957998795795a99d795795f375f91cc54322a71dd2cedd6f6e77bafbe6bf6f94cac39e79c8fb44c1bd9e5168ebe686e722cc4f0b9c59458e2398a801c3e8c5545787a0491f1c0e767042122a207e6d0695765893966beb10088b4fe184cbd689a91fcffff82d2095d27ca430b6bc625a18a4943cb93cb1566719af1f815a0aee5accada1764d151af32dbaad6aa0ddc4a6ce6ffe68df32dbfac514346abed75d78caf6333efeeee3727e1d876c2a9748891615245198c7418137a54ab503b580ddecdc2a0d4070773e8e95e43e7de275c06d6e080e5b51a7514c5e4e3b5fa7ed9ee6e02c101e2e801cd101e193716510089800374f5f7efc3d4567b6f08eec47585354456ab368e7f96e512f404fa4e1b697f450c67f96516d6aa30f83031b7db3ea7409837a090cbfabbfb35cd2158ca265b90cae9f712d844d4166c284d8580019748b8fffdfbeeee4e811138834c97d3a65d4c8486167409e625cb3288691218a67144a18e542e8d5e2ea6df34c452137c86ee32c5ac8ca06fc1721e7d448ba9d59ec5c9e06281c9a64b6661461085d6eaf4e388650376d2479a98829574bac219a14b61e5455b5a7c357869050d336bb8d4ade4138d56a95b3185d9e58161bd1b68d67bef7d79c15a5557d2f7ab9aa5a60d0683fe9e71bea1d66a37a8118aebb5d93afed98abbbba6f9326d24cd4d8e0f6f28030d471926df9b92486a7937034c7ca33430c5deb31308295a480bd20a68851c9581b8de6df6ac3708a2834b03867438f2d068501abe4c618958f1fbcaa0249675f38bd134c6faff3062ea28855db680c221c2a240e0a838262f760f3eff41e75fa1b8feffffbfd6b2edb20afb9f8cbaf5ff7fe3f727f3fe8d3dd0fcffdb9c36d2ffcf9bb51f99e0ffbf9641073babcf47f370fdf7fdaede184f94c207e3fa511fa4b8d46835f3bf8ceaa0a4d27eef0fc45d7300cf70d35490688cd1ebb9192b2a88450e00c860836e89041970c86dd3f7f3fa0e411974e85920b332831aaed346ae5897c1960c6c7ca78dc46b0a1b272739988fe0deaac71ecd698ea2a62f666dabaaba597a7e2368d60b8bcddcfc308dd70c64cd7bcde89a73ce398be899365209fb74501d6e62ad68daae08634c1a9bb99f55ec2f5cc92f5b55778fb187abe7e88343f7d2cab5c9f8e2c701284dd7f6aee976a68dd405615d4feb94d47467657c7b1c9990b713091a34576531fe1ec20ab0cf0870f52c7482034c078df486d595cb408454d342c649232682512a8c49ba2b19bff7de33662def94f4ef5b7f77ff6fb1dddd3d68dac82126eaa26adc0c40c255c3b77cb1b53d6e28781e2cd346f268619e2ecdf3a5de8df3c5ee4e22709aaeed5dfb31fe70fd1c77b89d4d0d85cd8a130a1e167c71beff3c4058b134c7d7807b0d36af21f71a74af617773f4e28bafd962757c6245ccaffb678c854d4ef7ff73864c1b79c404a4b2ba5a73b0b28eacffbf7f9f051002f8c6ee14503035c19c69956b259d74984e2e8c595898544aeb9e5b49df166ff532570197cc87fb1964f44bee30b53b25a5618c491f0f256bae58fd9e8072f18d6b7b9b2a76392478034a60ac63b4d62a5655ad6aade297004d3dc804dd69232388cd389af1d150c226b12fa7660f62179ff1b4911804863feaa933123b1df30f2741ff97e51109092d5aadd29d36d205a7471b58a88ea898663bcd6e7494ad68158795e3b8721c582db1198f9a9d39446823222eda6a4c62281f54106a0845842a4219a1b858defd8511a84cd14cddace07bddcca611d24c8649dce4336d6453106b0d046484c71881d804480ba02941be36d0d0e4c18f07c2430824f71afd4c88cdc899e1a372f13fce6ed4c10586e4e3483c471a92a926cf4888adf2c0eb399064e2890811d68d0c0d9cc8ea021a414706d0e74fe8a0e9639792aa5aab347ca78d8ce1143ba3afba92a1421365c5ce91192e4210da7102466faa460f143f374b556363b17f99cb22ae18fa3bbefbe3eb63429ee5a52bd34662612ddda52e7d6d9ada2ae3e616f3bb484a63123f0d9f884f4514c59415205a021a5068b0521212fc4171f143211443c808e23c8b9258ae5699ba98be68d0e8acf25afe86ceb4913776186b1ae86601bbd7cf7c16e5680f5ee52056504b5f3549cbed6fe786ad7b63d6d2cd6c16dd605b63b8d620fad34061626eb7db866261de8dafffbf698e70de6be308c49d58af08bc8187e0570b5d861a2b111b1842d13dd5a8c8b0227ffe7c9e5b2ca6d598c44226d346025721950e22d651fc23b6d4ea1ae3d4422f3e85dc61cdddf8c6dc881fa43273b7def7611d3b9ee47c54159778c850a087c4071bc62127bb7f2718c5bcfe947e861fee50672681173d1f0ed2d30c29af9ae10b224529ca92985deb000fd61863ad25b0a1aa403611a84c709153a1762890c0a238e1e94b02179e84272f6f814174794a026359e19b55f1d05961816b7922b6e8144596272a980a906fe5e987ca846cf4d39084985051e5a96705d6a2277bea4096f28ab1ede65386f4d1ffff17c51746d811cd43bd34490caf30a24262d7c5300f65af4287a7dc8fd748b56b50d0099655c59007e33ea46bcf07e42035ae445c9a4256ad56aba9b78fc1d09cca9800d31863cdf365da481a7ca3d50b24d0cf402ef6056f55dd3bbd1da51ddf4e7067e8d8020c25a203f584a91a63ada3d1dde872743e3a9ea5e18430335ff056d5ddc3eb19ea39ea61ea01d6d4f3029a7962b9bc5acba1f21c2bcfb9a2912bad4693a5c8a9601c180e28f76eecf438364f52680010420745a6d59e35a22fe48a4f5f44d98510c62158c5f4665bfa30ec062d7371256d34b1597d9fd6b74483a238dbe8fa1206e9c0dd7da66aadddda7802bcbe316cd36e898de2ce32635338a208452b6858891da02b633378a603c672c05ed417accf73a6ce6cce15215ba5ae0f66c53080529c9003c230f03262f0c438c207136362f6aeed18d29847c0ffd8b7f305f97a3e259fcf17f40d53742beb51c78efd3afe7cbbd21b658ce28a3a0671419b602ea8cb19fb52ffa0e569bab6774d864f4650c6500611e3cf8f73682f2a16198bb88a8e186ffc3ef427987517ea843b6190b02754fad8cc257db6b761626eb7985f9069237b4a177ebe447278ccdd82b0342dda917f3bbe52e2cd62fcc35abd7144318a16bd68b9241eaee4af87551edafb42153bfc5acd3f2b9ad5dddddd1d5b77ad8b5438f993f2adbeeff541e49df7de1b15e3dd7d9cb88f59588a7a52abd59f6ff7eeeeeeeeee37b2f81dc8681a49c9b491bee090288354bcddeb2bc270a3a2316de44d8e0f6f4886ea088dc918e70ce3986dd0b8bbbb3bdeeeeeee1e4483ffbafe2df1eebd31882e348d196f69cb45d69657550a32c8ae08a885505211a3bd19617ca7603667a5f4d1ec849e5a1da23b6da410290467722f4dfe0d39e6ff7baf5236edbd38977350ecc6dc6e3e184e3c67e110217bafe9a68bb414d22523e4ab0021ee3dfa05dda0c422b0fb6387c8931fbf2fdf00b660400161ca87cd1386b3b6effd05d9c5648670dd7befbdb73601108a24bd417948fa3d1c04f31fb8cb235bae9f3a6580049d850d532688ceca5c118faecc8f630d6190a9846f465d52972b158c49e00739ab6f34cb6e4d54da045e6959fc2f000100318995561268846bf5d35472c7ffd748eb888ad6910655c36b27e484140e1947613482464e93ab0f885a45f78b1f9661bea6de2c03b570ab6fd3ddddddf7968fbbbb04371fcbb047d3dbe3ffff5ca9a336e87de2c19c69d577da4877778789b9dd68264ec6d8fd46d125b60025f7f7ffc461982d1719b885f4e238f9312e815d5ce993e1692357100142c1380cf4c2717f5723100163ac7fc462412a532a30264399524baa842bd642863639c27ed2cc354c7f487830e6c2a133f06592cb55372daed5263bece5722cc262614990d58ad1c68a052741472a16ba7409a4d27aeba8559f13d1ec6634668cfee3a42fc2d1b491389b9c6e97fe4d782f1a424d6a3928fd7bfeff4aa33806511c83afdbce8f1f7f512f2fb9dc8d6f0dfb3bc9e656d5ed81b7def758cc4809dbf766202e68bd270ca20eeccf47860aeec62f5b55651568d703ce59731a0bcb07fe02acd9610ab4decd0df026c47a2b90b977015384725c228a5744990b0523eb075290203a7edeb7cb939bc3d29b2f8a81609e92b80411c5f9c56a2dd147b66385c5b54b01a36a27632bf0f1e304c70a1d280e646c8dc43dd65aeb80124a7e29ec12e742121f62d65a5b334251b73953b5567d74a78dc4db5acb63b4c5d346ce60d284d93d13f210576b2bf4e87a76d6da99aa756cfb69adb518cb58e89bf153b0589b065031c6f72ddc182d0cf3f0588dcdf3c888afcd682c8dfd66b0dcfbf4a3b8fa464fe37bce7e617ec7dc68d8bfb82c7f45599dcd5ce93cfbdfbfbf63f1d94baa04df30c69e5bd0b406eaee982b65a94ae2c339a0b52ce369236f5650e38c5e17ebef9757b186a78d5431c214bb12d6dc7db7eccac7e18f31d88acda719c5bcac8be44e1bb9629ca40959fc47f3ae25c9316da40f6fe868260953be9d1bfe8d448cb7efbb7d3fce4472d6df2f8f62ed30d1fa5e5363fd2dc2aebfc5e4ea461386174a647e030f3747214d780099a464fe1663c4c7b491bca1192347f6ffe98a08e7552acc1873b7dfeaef97dfb4df16dc1ee65661262be834555a09335f9ad4add3272a0e2d7887dece2997c4207c9936926686c88d6a8da83f0468da482aab992157db8a65e603ae1b6f6cd233427a3132eec5cf04e96df3698a97167d5e0cea9729190e9dfdd1226de69c35cd4d6a47dc9c33f6379f8c762c97eac8ca34abbea862161c7dcdc4a03175111ce4108441b14edccdb4c9dd6e5cb9bf2a38a3b7f426148099aaf5a7276e259d703eab6f5476e232c6472c422cdbc52e27db9970f18dff1cba315b9940d7cfd4d6ef064ac5063386c60e287c4e1f76dc499cefff0f0c6631894b60993652ab6b2df145b39ab8894d49cd4b810231a7926a9574024105336d3026e2749a20a8d46bdf29a9062802431900000300184892344ce35a720014800727f068a4cc6c1c0e8c4391301446411403310c06311002000c053180e33c895575436464ab58f4dcb2c9dc7e6658f54a090f56dba56edba1b6e001b971b7ed01e6f89af44d80e1d1735491b11ac0667742ca1b3e11ab83348297a1451fb9faeeebb9fa9fe8ba6c9e9202ab39dc53d522816eeabc75562b38f6fea6aa17b48dc4137536c0fdd1b5374d00900daad7ee2afdeb066095f83e22f61d9e875f4563df19bb69171b0238dd2a8592954343c9d77f9cef50ba45c0d2ca44e8c4cd034861a4b711a5011751c73b165b1e4e13c7ab080a40507d9642b357701db6948bdd7688aa5aa26f89337022fe2264b946a01c5e5d3792c1cbd4992f7b7e03ee7b650016705a78080f03a516c4bc1a17472092e7caaa3b4680f3a0e94656503a67eb5c2abc1004108d378bf95e874881474e5e58f36747977191514f34011c2a40bb9c32db3888aa5e1a22164278c1021741b8b8a83e9812553cde21a5eb67ac64e9df79ea55a0462432e94aee2f40299f9da19418cb2f133ee2174b642185172c78bdce31c524d48b52599967c31e832e5de10c248b040b06d303e2a22c249b8eeddbce3644f11382315f4d78d3dcbd3fe3f2113f56f1f013ff620d7762b1cb6a9ec4e88cd2b6c7562a1f0cd2c0d7fab16edb7d3038d73b2932a9669e59f0f2e97e2ddaaa69721fb21529d1fb8d08e4d86d702016a0190c2912a198208008203a6db30953b4371f4178b4b9a896f3128a317147b7a7dbcd89cc896208686d2b70b5a8da1135d41564f2a82f1d6e522070dbac2acb04f45fbc9ebb94574d1e4f0ab2bfdcfc3cc907afc905b6ef1d144690a2ac10db85da1f3d806e4dbbc53a5990f9893644e76f7d95ca22932530619849e6942dd78ebd95205ff46f47d64b221f89ea505cfc79a10f506ed203966250ee3bb0235e504e931a8e5284e64185f491711436b73be01caf58353f7de7765fea97dee0ea775dc83b01f003618e4a1b97261008185f23375e26a7667559554e67b28d7596b0f2c1a79f50ec8bdaafe06194065d618cacc3ae0ff8610cca00b8fa0830ba1bafe92953b3b0021cb657bc989b00f2f3d4715c29f627d6ac00f2c55c629fe702e66c01ab64165072284f27b9c68114dd35d1552e41ebf08b3efa002946910489627f8cc7a32d25fd3f59ac73344ff4ea51a122a859808f1179db68a9483d8e4a4e0a3e774e20e5f401872d151f0ff27e7cea172dfc1be3866527e85ae134a1aa8b2d2d61d7aa5f42a92bc585ae07af278566d650542eafb9523075405f15bac49634746b1f44a460db5e5f296fb561040a12ee9a713b057a0fa889417a95ce9a43b0307e1122390a1df94e96c7681b4bfd2614ebcb6aff490ff9e59bf91891d76d69b94bd389aec96ac822f64537bcd86ee8d1d7522965ef708cf43185011317ea6edf8695c8a02e5724fea1cf04206412050e6544f583007ac11b0bc4a5248801c779929a951059e423e924e0cd011a38c6d3f3e0cbb4db2d95976a3a7284ef699159701420f6119e7d2ed93fe541df1d20fe788916e07d88ac14915cf2b51f4e2c5dfe53dc16d460092ccb242a72e975aa64ccb3deb1531e1746862c96a2d965f313b5643deb06cab9da6b99080ef1341f5c38a6dfe9130f2698b82cec01e9162d38e932ae23d141ef66bef442d80336aa485d721f08ccdb0dc2813089c5c4757aa0f7f43d3f1149568c369bb011904b4e918990fae1ef15b59a908bc4a8575cd291e4e7a1b846f6e867706322ee16b78d5bbfb6c9b1eb1cbd15501b2767d243de8901ff4cdd7a24658c8613df3fd35c426622c1342615718a5426b77a18fe765254258d9cc152e12b1ff74d3d0c1f202f9a4aaafcdd2b92575eaeec3593d8019e32c9d336a9ab3c2be80407a384ff33a9610d8c17e4f648b1fc538bd8c1670c433902154383c8657e76f454d913f503b06b0fc118e441d444167590b4f23a4238028878051012b9c5d024a25ee86b17da2693461c7456dc2e13a5287d5aeccb08d313d5495170f122624b2f05ef5320766b04d4274836addc885c8557b4be2c5c10e8c4448a1b547910233a528e29507595e389fa29e2274609dc5d31a0685650886d822239c919086aef06159d7514618a91c8212b9fd484a2961d574f8795f01ed2f5736e15f6c261d6cd2ecbec37aaa3db23ce80842286c1730775551eaca098d7a33fb461ecd19fd28d5e8103535314ac8f226441828b2a72f10417e0f8090746b3a780ea783becc4c11cfc22bda57270bd69f8caacc6d165f601d0c610df4d58685dc44c99b21a64ad888c05f745caf5abe38f9c31bc8a37eb0a002f55f32171bb1e8087adc152c47d67c45136886f7c83006d26097208410393c03c2eaa8a07b866494086f3b868a17a01e115c8c21f3b0c29e1f669b659c9579c38e09c4de25069c537878c0be927d3beeda7e7d032d4eb44a71f203b99b3321a1f8cbaa00ea103fc238cb256caa98454d701f2bd9937c5e56ba620fd553350d361bdbc6d5ab98ece381f66ad2ff6b259d5d387013607b81eb91a71cd5d81e27a95e1df9d7633cca6c6fabc1683160543fce077a87f52d2e6be902d395f3789ad0c018a0b2bc4b265ea9133b09c7638668ee4ef2c010cbd73bea08df79543737adbafde1dedc57e5dee69d60fbe6897da5ee6e707589553418f1c342406ff0b764e427a2e90235f8f5d71f2402958b6bf3b853471681f36f8a4860d9358e8fb54fe54e8759e0333620fb16d1ad477ed5a4e65a791c91043665489c32f4edbe28cd46e5ed2d28539319ecd3c87b22f0b6a941fe61a601083a9a1c503d0c2d14152285b2ba8a076fc08ba262ffb1168410aa8a632e1eda7732d0920ff97016d8d8fa5b3055e2d2da8dd20ed940097b829b0828a12ccce95720f78f96226a06f1d7fad303da19d415ee27ca0b3a432c6e05731b058bf7a8d753a8eb7fa5e733423872542da22d7cd7a0d3daa6d7ac655c8831014cb3012e038917f7729de4edb35f895ac38e1a74584514a4865b4ce02093f590be33f120b2c41e86be358ef83f6042f5c63b39136b0c5356106a31ab2224bcf693db0168c21a8c82548b4ac4f2175e89ebc5a4b9a152742e74faaea7672ae72c5da76eab5c87f346c70097f0977a3e9239ac0e26ceb1731bc23bee1d216fccf78d0d0ad441c3e981d382cd7dba60f42a7de8709a07510e515e54e5fff2b7ff804a128bfc0d5a3b36c1fd09f89285bed6465bdaf587f4bd58c1f1f34c3fc4ccda62b477950e2ca248fce3a16f09b316e37e13a6a5f6f9b062c5c7122bbd3deb4c1d7bc0ea21e0be163eaf625bbe5dd09ba6fdafa2b2d4cbfdb12407bf03107245b718a2b8bbb1f7e958f1c386aa9643deec4c1f6d83aa3dfc9c2277ba33f45f6f7d7670062b039b1d0556418d6e02884463cdbd3144b81547504517a2aefa4d62f7b0742464ec0902db1456fc8abc9026bb4663c28434ee7355bc8179fff38631b16105b961ef84f80cba9b21d364f521d41943dac90782aa0e26998dfa6e765051261b7fc6c9409760e704e691399b153a51175ec0a1d4c9446a9326cdc791d959f7c98427b3ff94840cd0b58903a8702ff8adcc8565bf6a96afc73b18ed70cd1ca2a921428c983558e2ca0ed3d8270a1885ac875e25fef748dd64b11639727b188a7ddc837396357a4268efe6d3cb797c3dbb309f50d91348c7711a5ca4e5e3a98bb8f03797a2328a26f8dbe633c04e9afb0eeb08cadb6133d434ca4896189d0eae3252d4adf1aa991c17d6d08a0972d0f848365428f86222901a7274f83fa63115e8cd00589583ce1cfc67297a3e071f66468f80c780957be3d09bbbd3b7c1ee433122aad094d7d776a257878f832bac02ea5dfba676e5c933f734b412380aa22480dbc2b0320a5ede15ef00da3dcdb36ca07582f8a71a464c729d8bf5950a97fc8e5282c5016a51e994494ca132a89adfd467ea04a7faa864f1e5e3a3259ffb9d7bdd33afb115f2881ca908c6e1e93afbba0f987433525ed0b31d3f63f770e16ff9e0a91aa348c4cc29fdc2fdea6d22561b7478426a7b2fc059e8f335d741351853be11d13761e39f50a1ddcc464585f62a1edf4fa1b02b3a26468e96bf345f56de9e8e6a93adfb717149ba61d9ccbc97270759c1448e000fddea56cd9caee467591affae074b98810cca5e37b5d67b58193f9a1a3ad84ffc4aa87f50134f57b42814116dd95fcef119126fda89c2a6a0412e1436054429caa521e630996a4486ab7a8c930c9a37cf3e277321252e5497791cbe7a991240564120a7693eedb9e2529b06042fe6de4250788cb4abe28d221adf5dfac086b88e882e48a0a4540ffd2d02628b2335d49f4162c890ab22bb127de32104168adfad02e9e0c47ea9793bdb2ef81b028c6b2362f51c15de586964344f70fd9b442491d2aded7c20c378d4fae3f4910e5660a57d6e0626681cf11792e5f3fc764078e6577c1afb89bc14607c66f9821cefc20d956580e0bfca92c94a0e872a49775bde3c86637a7830dec3da82ad457248169b0fa7ac3522e379f0139f5f9ba36615872dbbbffd583d0c062d429f1e4addc080041cc7b345399a0b440268e87f8cc2457d437684075b66e6515f8700b65c345494c3139dbf7f8c53cbd3db5556847a494b700e76794a183f3bac9e2b8c44bb9f6b742a6146e6de7d9933876387136d349ccff872914751af1615320a80ab2e8a7df0c52218bcec1bccbcd3edd06390c61e2ac65b894b16be71c6665ce19d18550c25bf6d48b4c6fc52c3a77ca1c0b849ede0bc1891134d0b2ccca058a237d9a3a9954fce6bfc45ecdb27446d77c2c663182fc938a2468b8140bfd24cb9a8b8e58e48b9f1915f29abe9395c900aed61bf18e6835ded2e295764634fc7fc272c2bd3d24df693d1d0be7ee78bb123f7096cc6e569b9b4fab3f4bb64cdd3feeb6a0b6c5306df06fe5249409b50725c5f4d252a1acdf742b6b7e98be8f2b3db7788e0963bb31f9907ef34e624d9adab0df9fa074e9ad2ea61984a71b46bd0547b961364221e4521084b978a667df5e8d9cf42ab72a35773a1d181a9ec3c1b1d268f77f71cb333d7bc0f7673375ea60b7f420579131b700cadec2779673edb4d67432acc730519c08b285aa05555728fddf074be3ed73c202def8d8f821f81490f29f57063694cc87455490d67019507b29de4ebf17bec2e3ae2cd49c4ab8bb78e624451757b1344b7cdd6e231da9a26663e1a88eb6849b4318bf46eacd49472f4d60ce1f985fe48f2ff60a665df495e02c348e8cd16ebe0a1f980e8056b9e3e378f8db39602581db1e08a393fa7380154e66a946f0950486e17429ca069700bc17a707e19a4c6a2d0f8ef3e1f17814bbed5a6dccaedab6299836688459ddc016eb53cefa442c8ef8628b5c08e105095e04e1f2e2b18f8c17c2e1639f8d1da7dd7b7ff5b583f16d502ff32b2c23da884dee4d998f38e6b76fe0e18864f5ddc55ddfd14a32fe714920febf268004bd49cd11b10e6e1151a1f792de9670dd9168ce20dc342f98a9b6519d78025fdd3043a7b6558d93614c476cb6ab760f4b254211c4a266a266d255fd49111756130b05ab0a62f27c917a0fe2ede0b527f9a075732c333476d0cd999dae2c4a1a5b761fa1cebcff06b803255ea845599acabb012cb765237a98b591082ca5ac27d8069195850e056fa49042daeefed376f3b334a6d8b663391a17dfa3dec228af96839f6361f903594f0cdd57767f5a1c9547d006623afda3f34b0ffc6e90de996db53702958d8af0eea5c3944b29e9e74cc28a12dfbfc40bf3b9b893bd3c59c81ab40f7ea427613487b58d8be0989cf9fa2620c75abd1d51c7d3088271d84e8632a0abb7249e7ad613848b585c53f7c1d925b39ba5ee500ff8debca1b67223417e89cdc1b5b92dd42c0024e4f8f24268cb8e8b419f2c5d7cc118fdd78aefe816c1b7b4e8ede44b35310bf2e6f795e3f7ad533711707197134d26183401482370a6468a2083a9107ead2b6fa987af6bf049b1689fab4de6e20562acf85a9f3b740fdaef7095358c11cc1779e8519ee9f3fd2e354f57b3b395a0c3a70f03f0f3dc10389255498c5f4dfe482be5db4604d5f2a0ae1d694e1358fc065aff8317c6048039ce44fb6719a8551521f6c91af668cec693da25fee4e78e0eda928ee0c41fd6c2a1caf71966208b3baef54e3ff89a0927feca51988371b9a5934a7c008e2dd738e9a9f434d591e5230facbe87275752a01fae54a83816aa6258e0d722731d84c1e9f220b027074c2f98e3f9749e1930c6fd7aec7d0de5cc834203d395df7ee6c637c4186dc618ec87f68312a98bc1cee2bff6b550449f66fe27f02874f5f4ccc0ac9e15b930068d791526d54307f6de43f8c330d098625d974102524736909bd6ad31e4d39cabdc5443959d9955d2012fc44016e08726406227da5029c6c76713a083d48a6a3176f24dbcd8f8e80ba7470c695115cbe0882d150be01a659b8d2f752c321d24973d91f8b389576e20b4c9648d8397ae1726e899263568bc04dab9c6b13393b9b751bc44aa2ddb72b1cea60800449ec4b360374f49c56d74faa1cebce0b3a730ae33f968dc3d3800ee478fd977a056250dd20a85cce795ebb9a95738accffb1b6866ee1bf1cdf1439ae05bb9fd4dea43b025da024f4d8127c8bd1b3e2dece7a48b97302c730dcf49e6861154daa860a4d82f90d2207b48890c5140a61a6fd2ccaec0a9753dc3227ca1518343f49b699fa970629490e2e4ecf07d670ddc702d5fbb0c7057255509076e38c0552efe56b2bd40f2f4b54f2225939f9bda3139dc0f2e38b58366125186eadc09c2c7be1509806207aefc6d46f266bc8123ce42b95b5125427c9f784ad6766cc815b3b9a3e96bd846a7ad77289627c11982d265154a87d74b118654f06a3847f2022528f043f6e89583de7654a94719b838dc3bc671e933cf37868346d1904d2ad7147370d824e7fa6532f80f460210c7f9ce1165788d915d84c38552f72593acb56c5e91d342cfdee6600540fe8a94c04bc70aeb19ee688660bc6279921d1c56618791a3720aecc7f42495d6e6516cd0e932a4c6377e85ede34d40e912e9809fb4837bb69939e3de1597c5b7821670e424b3c4c2027160369c49dd2e0f665d0138a6949418ba3c30426f492a5c7f21ecf7806196167de887fa961faca441a6ff0cf45a237665844bb4e090797417a46ed4f8cd4b50ad1fc7da14ff4d996150638f8cf3ee76c069770c0031465c0a7f798494676e209367670d47d2832b9ed5490c32d8253693d301e9648a59fb2859212460edefd062508b3aaf1d8342bb6708d128c5f49509a86802f42cfe3581f58bd1a510f775bceb29d1dbfc44dbc64105e9bf5ea43e41ba2962bf1e7f3f490f43d743eb5d651214425a3006619263bb1e47bfcfc25bf9a9dae2a1827cd712f50bd3a5e4bff00141a954422a72ebfd77f4f5e63d73098ad899ca0fee88145ffa16ea2fc15bb85231d205aecd41974b5b9e71ab579e2b85850a3906ce5da7951df71f5ce0f4600fd260a22c6e8233344804c900055f7a93e4e229a6a7d6c687f6b9af574746a6df2c2b5b7c774baaae711917529b70381542da3bde5edde4a2a6c9c8b7e922b74c0860a7d214c5cb3532498a8b00740192982bf2b1d5a52043c94c3f93dbf5d482bbb2dceed6164d963f7208d833123ac7be34a621a6efc19bb9dc4eadae91d8452d0b952fbd28a742adee8504930585fd1e589ee25a8577c9d0b3fe0411f6cfe6fb208ad6798bf90a5d905014224809a621e7d748bbd4ebac2b922e749925541901d8fc28af44fc5fef6f217464c1514159a82442fc66240f94a28ab87f1c15fa0fd65caf18bee86487b5bc598a97758083c3139a3d8200da6ca70ffe954e4362afd751bba642adef02de784d0591a9e9942ead2e49e8afcced52973d408f158d71994e3f953d46034110f7ff9f5c494516e9d5ef1b38bfaeeab024fe04e2e349bf125a592316a2d901f8db76e2319b7ec6a36c95310da980a7948b656611ee0252a159360226f02b556162e9454750de246ad2e107a367a4c2b3046105dd5600c89c55eea39d6f784118028768f46c31838da171e2dea8bf004a88b055cc001170d5d3bda1705f3aff4c8f0cf5d93121584c8e60ba7e8a8df75e08ca81be1f98dabca9012ec6055310836c149d02c52941a06d9b34ebe576866101a10441e7a1934b58d81f700e4d3fdc1d3d025b86298efd9fe1a16394a98828a089f2b6e1d96757f5808d04add095498bf8307ace5696e2f543877aeca7b0ccffa2794bd03632a133f592382f9405043cc0302797c15138dd7062325242c6c93e617fbf9ad8313bc586ce90101da8b4c4ea022fb60e90909d50101d6b121ec5ea620ce621682076d1ff88b0d241856e93a2a4294a1154732914411ad7fef4d35d682b2952f8a58d7dca21714334a966b317b50410f2f78438c402e7508a6d544c0d0f34f9aba8e364fca2d7f0c587c7b256a7e037db641324ea5d0255731e25cebe504b36dac2288caae239c17d638188f55332e6afc817b5342ce03f753d6eb4112c3dbbe1819105339d3fbfd349d876090beaded1c7097e1ef29bca4b3ff343d81c3bd0e2f1bf4ac43fccb6da9edfeaa3d71f3556ff972430a698ace493c9dfa51b72f31aa7029f80df353809341f3e081677f627c3c1ea885221fcfd488259b50cc14c5a89710cd83ac8bc5e0cff5a38343e2bfc20b66cbb536a95c8f4c758d860df1d3243baa5773d1606bd18633855e5759d05164cc8bf2d8165c03d5bebd63c513cc57e5d77946704f0a95aa114d280a3b9e31625ee6594e29ca11692fa442c8ef8628b5c08e105091e112fba926b6a4471f1a913a79db7297e8383efe367b905dcf0304f257e158130cb0d8fa045124f6e464d996180191d221e3d0e160120b7d7de0a818bbffa0a4967a8ab8ca76b437289345d1f5e5a02472898e35a813d98fb8dc7404a71627aa5d1b70f3f64e6bbf465fa7e49c1af5ac7d7b22b5bc2bb4d40bebf088b580fe962427e16a25223967a9c068f1653eb062fe3c4bd50667e4927054eb29eb8c282000ec1caa0894b5d7ebebe92b1c686bf11696e15e88b931ca0565e8d0c5ef0903d462731128ee366261840716171e5e66e032603bda4fdec79355aaf99a36fae561859bf19d39a16b540ba787c2d30d50978ce4c416e4a2343d6db2886a0d9d4c9e7dcd9cae85c3568335d3e137754a177330211d606e87a8bf7783f77f504a6fd5209cf0706034db0352d3d5196a638242d8302fb9184ef019b724a5570760261f61ca4c6a8ff3a25b963981fae70b38bcb469c686b799e90828ebdc6db31a3d16082403b90c2acb0055085fee74ba0feb1a39351e0a01be85e9dac7d3ec7ca975680c8b1899b74ebd549527c71280951141d32644bed336652f656b15a756c4827bdb1421cf9cc48357154b73075c78f41693990f3db59a89507554b1e2a88df95905e513a9e16d4918afec970960a63f570e62e034282d06e1559de22464d93a2a425e211091a24412eae71f6c7552ef42c0691c44c40d1adaa10db444576823311d4de0d283aeb2fa20541113b477973aea080138201055c5fd38740646c0e720049e5585eeb891ae6da37a238df8495826925a95d1a691f0b94cac009e395b449d90963c6de04d589058acf7c57403c4f0b215b0ff08bf195c44317f82dee28d361091aefcaba77b87b2056e47e2efe1fee80f8ce3f44b60c347808b2f502e2733bb0b37371f3325513f0ba5a6b7eae56c498950d5dad68fd8b2cd5bf6218a3d31c4507055ca7626fee04869a156c6428d6a96303047ad8fa765c0470381b23e626dacd4ebff60d2dd490cfb3a04dc8c4ad0f87fd84e54e02aa93ff691153697815e2b42e3fd1e1b38bd0fbacb757b6320b610f0ed790d5dc166c9dec47b4001c2f9da0fe8c9be189ebc45f08cdd5b598a845783dacc82048e9a5335cbd3e02943d875e857d8915ea2ffd3eccb20c39b9833bd6fc6f019419878486cfc31c6be926b9ddc362f5987871e441dffd204ed405026a8f972003f3712ec86c5d55cae152646639d4fe669723ffb10219ac9ba7794e64c4c38cdcb9355519b6655bef7a2906a5680b84048b2676382a84b7f9958ae4965ce9ccb7db2f3d2350f742ae9032873f1db72cf54b293b66499e3a663324b6520b4efaffd0d5047723c4e891bf5aeb5b7347cbfdd4c7d9bc23752daafeb3931a7cb7076a929d157bab6030ae425ee5e22b80fc214d5045d21de871a544253bbab28425bca2677f5027eff8f2bf9c647f281da0f7552972741c42683edc011e452c5dd4efebea9a36955d65069980f6f29f3f2255302125c88ea4cf26f477facac7337c3981518313a1ac9fc4f0d469909a1d5121d2940f9ce1b84eee1199919810759d988ac3fc163c3bdaee24c402d3cf122b3b7cc5175bb89a35973228de45f39fa7ec4b8249f616babfe09f25e7cd6e6495657de9f858fd1b1bbcbea9a06edfe8414682a965d4f9cefd7f54ce34a1bb1c0f2f61e7ee648325572d1b8e0a8a73f695d200498db42d57477edb1b9cfc5cb6c0d4b275150b003352a578f695af34374811d1101c020f1e9cc5154d2f3f6806b5b96e978853eb3ef86daa87823345f0e3fc20e138d74aeb1f493b7418c2a4fa6ec017a5f98f49771481a7d3ca6b1b47a8b2a9b83cfc526756093fbc2aa3e08ea07083f1d5115565c52a83a058d95fa72d10b906065aff8312a0857e257db9e82217f647eaedee4eec0c8ea230d4639f98b97d194e34bb695904131069fafebf9c849d5f5b6df27dd2d391837f394e9ad42fba069c33560f95061a7ce8f73fbaa67f7da1d1f75b994642337fdb3170b82c97f0d86cdc21b65315715e0c0e5baea029cf681d00f4f0b515f5fa9e84f7904210c2c3a83b20322ccd9f79ff91c61c0e8057e978753e8df5f7923feaa2e67c9eec3f511c1c4513987c68ec41ef8758261c9fc940e3d19d4258fdd9cfc95e07d1c60ce470d365433e831b30aa4702ae3d4d706647ed3c26dae174acd557920ed9c341ea328283a4e402e150b7a6abd55143de939a45911b66389654881664880ee3b461686b016e1e452a86ba630d5b00c061d824c9213dc7d0db832fe8cde0731740bfcd68b60d75bf872e3183cbc6118a79e9798c892b2ce65c6f9c86f2bf744b810803fae062dd4881e44d2e96237f2642e2bd98d41b0ffeeadbe94bddf935736abe8cc36ea7d653b4cb97b454316f173b27fd74c960ed06dbd5d57e3e987746615995b41ab26af409699e5a6d39bc1976a6dc91dd19651a19114e837b3104c90a8005e47aaf85a0cb63489eba3817a81b696f9e6cf67d65c1111158d7098ac416bbc9ac4e824086ca6e32a688eb6dcbe7682de5824e83b55b0be811209b0a1fd01b1368ff0849a010a44bc97f39f67d12deca4f6ac90d5d410449a55ca80859fb36478987da0fef629673151cce37f123b508565021f2c486758a762dd28b1f1eb39bcdcde14c5176711ed9bb737b227cec55a3a3cc5cb27c9dd2618929888390a47460a9a6e5a8c3c6ad271d0af85fbcc99700dc4d91b0fee78d2666aa972be288f2a3f6305d7815002f697b02f1baca9bf5242c57924c9124d650f9de894f5ead082cc4dac171f6aafa9f6ee8a7d102e36a0d426662207c9010294a023a88425bf46af0bfd98811b1f54337ce7a2b312d4e38f91a8950ccaedb049d9992007671db849da02c39f046a913ab93675ab82a531eaa018fa38b238f96ec6cf6fb3762f5f8db47c3e589b02308653d77e69bc728130861d512a672003d39a44b7287b51388a4ecd27a9500a7ef9f1c086d4382a7ccc9fa1aae9d082e98f062845890e8e2095f2411d64eb17aee226189154d99ffac32371ef8235fa853e6c51d287ab43f68a82a4db65b9932f5acbbc4c33bfed2a78fe3f84af7bbb22b5f1f3d948a900554d31a0942938cf17ca4111d65a88889130c81048be9eb71289443514371fe90177e07ce1e1388f353447418fc3be0af12e6c064eb79ae12110a453f5e5cf4be577d511a18fc6eddf45a700a1786fc3ee817ea5a980edeb766c59f29a68c6b271e984be466124b42d4eb0fbfe235c1826132e122eed115611484ab96c551752c4a0f036169068d18f080d05e8dd14cd025743c83d659e567d415c399593b534c635672ebc4f33bbd9bbd00facc53572295a0b330283081f85fe32c3de4c79bd3b2cd016426136128631064e6d077ae0d377a73d75ff255bb001ca821ebdb8c2bb00ecb19d7033c7ff6b452d3dcace8427e402b38d07d9fd6397ad2d2f2c9e775ac7b8974d1efbf9df95a15a74baf574050e0aaec26de0bb8ee040fd431ce9d2af1733915d4558a62f415d01213db0819f0e9b81b0ced361824a4ccddea411f0d692c3a0cf043ae08950625b90f9d9c50b65ec6f131a182dcf51c5b2d462806966386fee1f20eea83e3379ca968ce2919c49aafd1b95ea2b6d6f70aeaaf4cd18dda2f9295fe10065802de78effdaa2301b95f2d116f5ded22e3a5cfd0e01d5659d2d8be1a001ad4d2500004350008303f612a8e68cb1a0332cfcb0de41f824fb4268a844ee38bd25ae212b533542f7e0685e229c7a80f922a4c2591a5c76a85731f0ce88554e41103fcbf3ff8273d1e3399b2cd951f3bd24611f51f4d20ba86be13811469fde8718937eb5bf079428251c7a7848ef938c229dc764f416c89c37244ba8471de5b079e0f4527a3d4e169ea777e74c0837c1e35910b5bb24fb719d8b5f3417c1ee847b01ac2d386b9e8129921b14ed0bb9af87dd23a24c744d108ecd50645ce8891059a4b62d12cc3502fa0bc17a89d02d1aef815a104c93c2efd0f92fd34fc34a16c6129482d97e83077b1e481fa16515a684ded66fcf9a22d8935a337682b87a528ed19f3eb353fedac3b1ae73b65423ab04f315eed0e7c8f7ef198ffe3ba0e42256c81b057f14e6bfbb79aedb283804b358731384104be8f7fdb14af3648bbc52a782f2906291f53bd723fe9b6d9cdd4723db472fe36575479ff46f7f970bcf6edc7b14740700193de418016ea67927009207c0a12c7c18f7266a7b26a5eff000cde2c4154d21ce31372b7c97b16318449dfe92cfcf6fa29056c43034bd4325f1af9bb3b0cac94e50c9bd7ea3ee1315c3b813465f01754e7d769f4d24928dc1c5376be76e9c15455816115ea4fba0922947032a9e3cb4c17b7ffc3020851f5033a8bf65ec6384f19a04513c54f9cb4006d1ca01427d162dd8a0550b2228b83fd5ad5f12087fe5ac4282da3841c03ea4de45bb3efdceaf83804d84ae5af39d422d68fbc341e5c33b8da6daf926829928c47e9427266af59faa1b7259709813ee4ea34392e60cb48c93f5facde9f7be65630d03a5c6335bfdca7ba7ca52869376e633fb9b29e03f5798eaafe52e36823976e9173bc743796eed4ff6953bf2617480128cce5989ceb44f8b45110c364a822024c47ad64aa954b93cfe405632189d766b30555a0ece712805d65419dc33dd7bdc592197fc16c612421811a0571647aaf3ea94560d8b8eba4865be060ef0a0857543f098f796a08dcdf6ef750d0ca8c821ad935d09fe33437bc20cace549d2861fc2c57b53eb62e968836868334eff52ac14c50e218567175ca95255a57099c1dc33e679b64060d1371d38dad8c6c35e36a043d62e31cf667b4851806c7ec4cd3d02fcb66e2b9cab3943b11fc8729831cc0b72ed14bf9fd671d3b3c0139bb3e4c746c3bbe00661457c16ca5836bee53afa32069b3cdb2e634bd8e89dd1cd4c7d660b842274dabbf7acb1b164e33ec2ccea27a19bf768666e4740639a5199b0996f3528f846ebb070d6b09dafa425a2b3012e10149e5e40bf4b9f6cdbdcd368dd0e504e5689c4b65b66211888f5c4e557798f11c84074ec4d700d4f106ef7f5d593dcc92a86bc74af718b0510b09705be515015065e63b1c3db799005cc4ead26ddd5480427b8e159f9f99535e623b540e8f4132462aa7691a03a0b60c3222e35757bcfa6c0cf41a2809dbde6f0d96c95e077267113ddc1978f6f9213f242e468594bcd7632f27296dcb2b88a81cf0224125f0c0e6ec04ea4249ad598a475761f1dd9a4fcbfb0fa4a811addcf61258d5d901ea9eca256119ac0aeeec01d4bc985514a5e56e5e3a78f6d2b15f9dc33f6d89e04beae6dda5cbb00212b3c36ae7a0d8f52014ef058d05d4ae69126b7f548b5a34585ab12a4912f3c4b86819857701b360a4aa7cf6c1614d6d918ba4c01b098a81940bff339dc9973a37edbfce5370d1f54adacb94ef17fa60424da93c32a923794204c9e081061ffd3c9b3474b97badc54468ff78e316a6368693a4b4b813f48bcd195a2b359ac7cac34a1d19403ce57631b70bde237427555d893cafdda21d44d35b9c6365af69b7611a2972f05c5abd2677f3b09dac3b972ae865f0431a76881a527664ab8513ec5db187242327028e06916b03360fdd0e82b11ab4033290f0be70766b397ddf029e40da99814cccd246305ea97b682f8fa158841f070b234e42d58de069e0353637972622b694600d419d735aa55684303d428d3d34e2b5ef0042b1b9c0ce2fca6f7e575cbbf24c0e86da8b895689e0b37be15f0fd4f217563b0ba056233f7a928573171d0e3c0897b5e0ca17068a2a252fdc97947b40829748745b04d741f724ac17b19ec9ba3402e42079ddab21e182bc471fe7831cfb4907f0b5069d7a8fa3b563cc1333766b8ac01cdfb880053aa64a1642945c9a7ee41b3cfbd2ea0b7d4aa2c13a82fa035203c36ca52158ed566486aefea154dddd8ae4192e7dcf007113800e680b44e3eb64b7eb56e7085b74fc5230cfe7c74f5959b7ee9de66dea8d2e24032e2b3803b1aea2e6d0545964751e2e34740a5430773b893eae669953447415e06eb0dcfff468e90e13e04a9ec53a777b65846966b8ef52fe4d73182f36f31c77da07beed8c941678a0e8a6eaebdd693a685739a89769da04222506f6a0aa7cd1338d42f59cdfec0e8d128f52d4b9ff563391a813296351500a01b5813828318f5cc48ca0642ff04b2369d5802534488dd4d81a3a701349695a3f9328a78d8375cd4eefdeafc7434f44b2226899c9b3f85cd110e830f829a56551648b52629e8edeae1819bed1828f9b5004c1ff6aa5e025591c91a2fada8db6f906a259c96e7c27afa56bf62c372193b734c2805ee980e2d8c5f730935cd18bb8c408191529f345ae0ce5fb0f15d278db054e533ee824d61f3342bf4a6c9060b0e031ea40e0fb5a6ec65e7d7e43ac15354912c89047a45bcdbd701b97e5848881db2da72c6c39fa65d53160f3c85a7ccbe202734c66cbca34ab2f150543acc022c4c3cece3c6a4b39e74c342924e0d0d8945039fe80df2cf235c697eae67789760c9b8c2cfc1e8e2546464dca28e9f901688653d091ad91d64d56836fe556621a2b6751b036be6bb2b21936a58ac5958b51344f26704e054f27349291e28d8feaa0355a6ad7c959bbf520f4793b118b98b11630b7c8e803fad4a8f7f849889cf4137363c02db06a5344600f7d48782d607d43cbcdc7777d01f5146499b9b00514d8af60278b34d1f45d2f601b855bfa7b66db37fd4dd3faa813044db4d6a3ac6533b2d729f6882cb33332e80598b58095a12a2879f23c8166552c3aa2354c13a2e91a360d61b8432f6ae400bbe80b9b78d1567b15751b5ad424af1e0bb851b36438fe3a19e4cc9a48c063d8941b4c4267a04824fbe88b84fee4e82ab900e58282b295a29285153f7ca4ff31c02b1006cc95116f0912c09b42bf4c7df72cfc0c1fbf4b801c99aa8aabd1e10f56f89e89f27f5fc4288822b2da0c62348e5c98198bc19a4272477ac723a7b89299e5b7299fe080b76f886162e0289fc9eb96010206eb861f9e58b31053734cb58f1f86b8318454e56b97635968eadc0616bafc0c95f5ebfaa56f593fcfcb2e8b945a417339a6378f4b137fc0a65e15e8dd4f53d881d863fb651123e45660380dec6cb17566d0cedee9f461b3ee8c30c6b37529d3af1da9e690c1bba680b254b566e4dacd16956cdc92905896f35b9190e2c98d6dbbc9160822f1985cd4ab3fd3f5422d5069544b56bb200dc0352139ab11e2cfa8d4744e70cd0cf0cb6f15173e34031b51289d3de6fded679dd98ae02c3bf9f10080d88f30e9b58bec596677e9d27a1c9124c1dc85192066bbde645b18ac2bda8ea2b43f27a609d601a865307bb0e3b7b0b9865b285d241e84531fa52dc6e5139feabdbd736830ecaaf1af3de2bb13aaa1ac08f5601545113f7c2111ff01452fc7fafee03eb062b0fbb9eecad07c2ec593dbf712e7c9d6e0c1efc38ed99b9debfbf79ab2dc2f58405def6488b9e46e1e39ee6000df0fba88318b2b2e06194b3857d57baebf4bdf0e73da116056db3e9725c04fbd734c99b8ac5aaae401e29d553644de7a8887f969139582cd8c800c424b5cb9da17016aa92557be1753dc2f3966dff52a6d90d9fd4f2c135fcfc9bb2494288dd9884507913d119dfcb78fdafe00d94c45cd9fe19a04ab3c2c8682babad92800f4b0a4a31efa70a4ee010dda3d4c41560bf934dbc1d43db8f82f8d23506d8e9eaa1ace2a6922949478674591cf40032bb63ca474a8fe3dcba9f99d2cbabe770483c97ed6cf60959aa95e20a39d305935eac2cd3b51e1c29052b09930db8da592dc805b6ebcdd45127c2a60253e830d54953ccef815e637083036a807d91610951c3d78e31735d2772579ad6a8d2369e12cd63e7cac10c951785ac8c60444c17aebd796c920bc4d8bd9069b9215e1d0096612b4eb3039eef1861939b60ae915c22fc43d73d22f2a4ad01cccfc01ba7985554e2092d49b0336af8c18de0a55049106030632411495ac824315c1b32c7b810d18ef01e68000005455bbaaaaaaaaaaaaaaaab61ab28b4bbd782ef679b98eb2f192d43c252f42a5255ba694524a52ca4c053b053b05ef47eabdffdebbcc523bb24ac964bd325fb04cd9976953722c7bffe0fbc7587ce167d6f3eb59f66c7bd23df79e804f191b3036dced6ed7e59c0d4a136744db9115c9c96aca9c51351a37f73b870f8f2fdce469469b484da7a65573daac36d38a4f3e40deafaf7224f4fcae5a04b2e530fb20991d0a16946acf8af167be6eee7d938dab3d08bc7664ed2b0131d874d2404b70789ebdf7ee6e77bb2ef79b7318d7bf300a5d3bb2ee79c0174506acf24b526294cba9be7befdcefedeeaebe39df5f96a5766495cafa2a6129db5badd837df7cf7de7ddf7ef3edeeae38aecde1f7855dda5ce85cf65c005d6478cdf776a3f3bdf7de7b62dc1e9b1e126f0f10dc53f4bbb2078bc8b7555393a8f5f7ab978a70f202b8bd17fb06b7efce2418f7dc3d214ddc5dee6e77bbbdf3de7be7bbf7cee4b67fa7d98eac4e1a284fb46407497311b9bb77fd65ef05f04546098c0afc7befbef3adb515d39bdb6c4756278d5bb202fd2b5e8a4b87b2b8f480d3bfaff7f0eb617f22655a19e18227de60b835b5818b57b5064b84450b2fdc4403a71be9d02deec707442db0a006ad720285f0877684e334e70525798be619184dba546e7a33c72303a3c9d66b343170412461f5505630309af415cd8d15d317a4418d3b0dcf0546930d37d882b65d8adb806129a95e2295c582b70a5d01abc6a382b18d11bc15520a52f4a9130a5ccca22758b2f2a830956602a6f7b743d71da31bdb2b814d07089ae452f9bf128c870446933d1d1cc1d2890f10ac4111ac0d866089c7784ce104c1075c3b1a0f2cd5707c3c353b2067c501a34927ca3dc368361037a881a50eaf87f6ab4f31cd400a3314d237249a1e3bb226412b181de603f6f89a555e50511f36902c974b123828068c269da3215d000dcf027e6fc70b5f5580772705f42eb33289eb789f90260063430e5a0257c8c59865458e1776728a874aea5402ed7683a6f02a7111df0d26bc08184dbafeaa4280458b07052937be70498abe07184dfcce5e2a5e315e360e805b966ab5014a4b1cc52a2889b133c068d2dfb6b8050c7ffbbfb2ac59e654c09bb1ec8a2eccbbe806b70e9a00295b93b8e1932c9724256c5504184db6b186697700a3c9e6354c3ce85289e7487ce12803a8a515c02d8d004a351c432c9e13b87806d0a6eca90943ef2a7578dd23fcbe7055dc888a3ee51f7a97cfc32795ca273c570046933e0c730ac0cfe3bf4bad00e09254febffb4f1a4dba3b87a76185e7547d222d5a681b3d96bef726391b2cc168d2934be5ff4ab1e9379a6cb73a188236dcaba772cab0fbc7306d22bff40b5b0d5e68321bdc3863e93a462ad684caaece98bc094563282e4b092e2ea9fc7f979c5959309af4adaab10c8f842b625e7d09d314de49e5ff5fc9953682d1a48759b952aea80ddff049964b9230bc3cb26ece41b2788e502f0c85f7b18164b9dc60530fa349f771d33cb29aec97ddf049964b12b3be57b74539a45a51af35b8e1d38c7056451431e7ecea4bae90887e529f344fe77455200df772e7bab276646d2ba9a383c79a9e73eec2a57d49513e9c6a766956d525a9fc25599bc7f00d9f64b924f91acc23abef5b726f729cfc7bce4be86836e73c862fd79cd81c33fffad77b1fb2d681448a66a01a2f183531546d588822516c90b0faa1b80b7b3b1eb010928597b37b20566f1425612f6658a1280d1e3c82948096e019a1eee38857e1b7681b1fab278a23c203d1ebf278e1a0638a8f8e12a1c18f22e2ebe401e4394a92783a708c1b254048afccd05862828623b7587a2a6e91c33f563fd38d199d88543552a38a884314d5e28ba16b5425ee751f8d1bba71f4a65b35f292134e9ec596bb4b50443b945117414f188fa03dc521241fe41463fc19cc23ab0711529e20ae3978034d3e5c54384d2abe6a5858a4c488c23838c5001ce154565d72fa97aa538ca430342eb0271899c1ba1d3941270318355d88bdd5d13872fd4d099dddecbb6ea97d64550e28c4daa0d0979f2042ee740545f1f2d3a3a3f7f0bf31fb61387f3dc57c9d4a3d3de5dda0e4d531b9988d4ed445961c0a31c44bc8e3077a332c75423c6f2a4cee812c30ea168764d4b5d5f782c1a9b2f7f235e5a12a8183d4369315a8d432558cd72d3e1a2e1817158e89ac4a6204558fabedc912e71623e765020514524861fa33c52a12e4034113273402016f76d5ba2a0f0ed18f895677cd631473ae779b79df7bab2da763056b288b2aec459a4f0062708c0247a07869dbda8cd56a8be9f8b0f181aa089d68c63c9d4d1eb6087fc8da5eb4f42bdf6a2f527c7d56d1c5a1c74351184c891f21251d644db93449094960e5dde6368bad2ae62e90ba7022c3ebfa504d496a12449991b31382df78a5b21d2d3591dd4397fb13ad1eeaad57d7e7813872136d7ebd779352aa63de17d01a9ad409a11a47364230670baf352366e7dbac45fa162bee794447961382510f587e21a01c823ce855a95437625f577930c4a4928d57ea573426c62bf50b8a57233e492e3be1320fb5eb1110735704d92a7fab3c81e1ecbbe6e17beae98db44dc87131c4c28f886821d5827fcfd49545f8f51e67afba275514b1d183f3e3df4f87bf86451fdf183104e93f0ccfb068f6b9b9198af0eb7d4cde0717fc7bbf5e01e93f0cbbe005ed7580b02c77488125fc10e93f0cb32876d216278cba08bfded7a813fb8372f387e14c836ece05ff9e6f2de568952c9fc107c8bfe75baf7c457a91ceb7e654f438df1ace83894f92cbcfd75bdd6a7dbdecbdf75575b3e0184feeb2f44ab4c900a985d5789209ab3e51ec9d2459e6ef3fe255cb4b70b39dcd5a5bc5fac33bb1d6f22ac2d0f5216e87a7ed4be70553abb5148105a640c1d0eaa2110fe355ab0bd0480f7795fe3eb7c0f9506da3ab5b629ebc55719e3b530ad429ece0d4a081ea59c3a90688182b552a1410bab05b9146c35524c4d79deb62fd9149a7b8e4ab35d5492dd9c41777c42bd569aa527c616492ddae6e87dcd764770fa73c2f869879bbdb7be79b01ab55916da7c268c045a353134c5e513967726f856dcc93e551080f71fe3effb62b8433843366bfee435d27d15ac8f6f77d75c191670c8f5f745f9cbab7f6ee191af14caea8a92164576749318fd7abb3954225be979efc46a9a8bf7712a92054b43ec62bf5a6afed5dd79da5e2ad6fbc008007258332f82ca2d16a0015152867088e4a5475a3a4b5065eb5a6e5bcf68db15535a99d9c2bf52ae3556a47147bc4abd40e1a9f68340d3d5ea9373af146bc526f6892f4f74971961a334aa9301e8ff0d30ba9232f7dd7bff6b66db219ea6ca4e03ce79ccf56788a3398385f99e9c53b5eafccec028b39efd66c9b7394161a60574a60dc7b5fc1ec80b09844988511323b09514f33bc9c24a477cccedb12bcd65ae7228678ed4a298c3be2f5ca6c2ba374e8efdf8e387f5fe4f0e0d59b9b9991daa00acfd9ab419e7818af57652e58b685a212d5c8832a30a2b6f8a0bcaaab5fdc0729b9243f36a9ab41c0b416c620352efa9890136fc4eb95d9d3095bf11baf57656f7a65596d8078bd227c117bc4eb95194d6ed939cd3647f0974a25c9ff6e911341b61f02ad7344d71d76a1f56dd7ddee765bbe416c1c93aff2ff1d98a2535970d7142b1d1f221f7edfe570bb7e73ad5f7878245be85af65a005b641c87dcefbdef8a1d2f3c03345334b334233593f5d177ef9dadf8265fe5ff4a1512edc80a5454a2b2e4a15fbfb9d65d1d1cc7171e41d78eac7b8025236474f8cdab881b5f59586d47d6341ff7320bf1e045198459527f72424c2eb7123435d67626c386d6ced9f968590df18dc55cb5731a809262cc10b17011c5c22ac4015ff3cdb085f8216fbedf42a35c170bb3fd4265fc3e219ac262b0750011821eb22533bb9f102af80b1da7c1b22b7c8dcc1b169c53e05f04988cd6a62a2b5c74d82c9d2c9decb6c651f13a879f2a2ebdb0a73f1924789045c8728555c89276d3420423075d6badf5d118af5a285afe5ef4b4817e39806b5bba15354d2c48516a562d5c9c8b2053a289222da926ee086b88d7ae9696d8c72b965c5c7c72d4807cbbedadd65a6bad8f5a7fe4d75aebef7b35ce5fc359450b967dab41ea3fbd07d6f4549d08fcfafac9125cf183a6a00576dedb9a141b5935259c52d07b18af5dcfadcdb5f60076d4108049725bbdb5d6ad9f7a388a3fe2750ab393f613f6d282cf8ac5d9ba71204a73799971b696b487d7e73610dee30ab37b7fb18075a80f4cd6cf7dd082a107629113765d0990d7aa0b588839ecac70f12a72923f1038c4f967110bb44a0ef1048adef69fac594378db78fd722311134f7e16c927f9af1dd0222743b8408155c7415041e9d08cd755b9fe16031ce2753d81436b34c1c1faf31a67a98f572f1a5121bbbab6b5388d371a52ec028b9e6522c8f6983e606e36857c472c320b210749cbb29759e038bb3babb5d65aebdff9e69bb3bd916dbcad7073e4a27335343b23f2367ea63231045fa251786fadb5d65a6bbdb58f57ad379a2962fe9c732ec2ffdec575fd16b4fffc9f681cf022ffdc6f20d7a851a346ccf2b66dd93666599dd90adc855cfdeedb95ebb65d73c137fce56db170a25bb961a615fef89c50c19a9601b2cabc989bbdbfdf9b6ffdb22227ca657e9b917dd73a3c18f8f71f864700aa20fd48117ebd87d685f0ef99ba12e93f0cdf6c65e0df4b15458af0ebfd879f24ff7e8c1882f41f86655eb85870733314e1d7fb97b811febd5faf80f41f865bb032f0ef3b408af0ebbdca5f8811fe7dd3af30869fd7a0df91df8f05268e0037fbae770031206d133e0c934843f2effd48117ebd0ff114c2bf67ea4aa4ff300c020844aa2852845fef9f82cd24ff7e3afc48ff6198e64d847f3f460c29c2aff71eb22cf0efdddc0c48ff611866c408ffdeaf5728c2aff7ce401610fe7d0708d27f185e711be1df3f3d8845f8f53e45c984127e88fc309cd90e5b306c62cef17aef5719807506e0dfaf84805c48c0a90c61c43599ff090a6b2ec1b7236c8fd7adc19aa1b498c8516c17b00338c601eb56a7564e48511e96867506e1fd25f4c0e80195159ea543b733c564f6c6dc222b10203a11a337349745c4d4226bdc1a4ba0657fe213e5c96a75b2a69dac7d42f109eb8cae26b2482b72c23663f69fb0d098fdec7f59f67bf6c3ec5fb2df02ec7701fb83c07e9686fd6c94fdac15cba6c512688bd072c85030bb457e1886d596deb8900d4a66a4a746b4b499065105a96f59502a9d2dbede7b9fad9f66f4358d0a4513f39a231079641df6a8e698fc86049451918c9fbea7ba6ed5b647aedf687dfe841754347e6a4680b5313b5f8d3e943a9da09728b3f3203717e1d77b9de4653fb9457e18d61a7ae73baf6db367c50f7f627060516d53400098f180d37c5a42eb295ce1a47b020c177bdc0c6538eb155920198b2221dbc3aeb127d6d6fc995906b3e896a0115d528914990e28404a80722fcc669827cbcd573869adf5bb21c3030b5ed065571f65bab74887a074fe5e3899da46adca274caa89c4d7009a121f43b0039c1cd3181c0f6d8cd7294c0c2f43d6bf1ff3a1090cc123c22e068fac4d12acf25006f7fc19ebdf8b8d114104544a1cd21a0994a4a638206a07a39523bea826b81f0f8433622100926311535557f6550511011a0f2e025e18a4c8a80b4a164c90f0038f18e15961040cc88894638c8f171e7a1d732f1b07d98697515a8c222af961915bc2eb741c4eb35df409f052a914819291ca0fc3ca18bd3d61a9546add0276b6d8b66d2bb26e58b62d090a1bdab60db117b7edffed276945da0d7a67ac8942478ca629d5224a636608699ac6f5d65cd3415ffe7eed6359f6c337d07d23433c12facdb0897a0500151909cd1db9713137d78a8722353423a42e1b1a1ba709bea4bf0f11e264c93af3f79eccab2a079fef968e9d395f5f27cad647f87d0787f97de1231c44446cfdbaeb36b921f1dad532d652f5b7f4f9ecab4300f9e52300db75c5083b005cb13b8fa45e51d03a7365ce878623d586c8bb342ce1f8e1f7fdc59f7c95ffbf673b21c70fbfef1f6648becaffdfb3198ee1f8e1f7fd671d3690c957f9ff6b1648bc70fcf0fb9ac91d2db8ae59d80709ae99b408bbae19d3c608ae6b56e2ade0ba9d67b1390df2ee0868adf5f799a3158f5c1f4faf87898d8e9de23b35e41003e16e88352b18f58a6a1ec42e8e2113885b268464f4d5208eee72331a906da652d46e981a0979f31d7605bbee9df1873d69bdf599db5f2adf64a8a10c77dc46f894b9cc08f422804c54204e0d3ebcbbb4bd31c156233f0cc35d73de761773ce798cb6340e27b6152f1a96aa2fb8e7d90d0c9a16e0955bcb1672716068fbf001eb0ea9461d9353f588f840e41775a1d306c5c3b35dcf7bfec8acb54e12a2c56148e9ebeac00394633480aab3a60b94f0886bf3b49a387f64ce7f308d28001c82faa4a8a25aa015c58c8089c05bb17445458ae919e93f0c871c6dcce0688bf0ebbd20d988e30ac2a8e78ab30957c4cc3e414e2a9f200d2f66e69b0bf29ce1dc73ce3939f6422b933e3e492e8b6e7af3404f0d43ca5754a8b483cd99b38d995fd0d662a6058e10081accbc37cd39e7b5b9d8ca4ce7be7bce5defac838645f6e59c738e8461add439e9d99e9c53d764c43d3e492e7d0ca2a7ee2177ff0a8dd9e5fce51d22701639c27f781e47e25f5b9148fc2bf3d43f127f53eef2f91663bd3e7614b8cf482ad9d45de13340b22923a22be4cb5ca22f0fe65b88344be19e7fd2f9a7580a9bc3f8701a31a190a77a824cae30a60430b524a40f34bc3a001276d0bc5c7023b29b73eb7a4b910279990dd16c4d916d20eb98a103f1ed0166b238ae46d0df83c4eb74e685c592bb6c4e912416a37c86c4a810eed0e71e1563745fdff70ba83b8f268b0cbc40e9d91a11531b539e726ac498d86a983a430dbd5ef95f1318870d8f0e0d1263342ca632a84baaae60528919cb7b33ed7ee47e4d59c31da5c01a3bad20ba18bd2940b335a94b8b8c5de2e5224d2288fbc3effb5d3af6de5d6b6a530ad75aebdcf6c54c7bc4ecd3a170ca060af563eab50f2750c8ccd050cbbc9ae32a3f9360716fada950155a6f6d8c572a93964760cbc762e6599efa67a8c4b32c08b0ee6e2de38a01957192e187e7b5d65a7bcd75313e79f51b07749262a4c030cb116b5d8845c19145d95cb890e520033ba6e3593fb7be3e39efbaa146a500b31e34cf9c64678e78edc291115bfd0ae043f6de9b1a731363d48263c970428daa523e3c4dd07507e676331433b5c2f52dad189dd57006ee31a19c35282c8e70a85abd0bbcc85105a17af2866ab0047627a716f424924bebb1113c0a03ac6a5908608698f40d87691089657726bec410375eb3be6088f26f17f08190a7bf7701e01a89a666918aaabc385387a858a89c29f23ec84e002d0525e965e10da5a9504a61a2a6472585fdabbfe33018a2116342a96fa54f0a58e7330d6a72f1c5ae30363b59722d602aa25a3b12ac44dfe43c3c8bb26a84b0acb4a7c9db5c7cbdf775801c787af99b8a61820a87f9636248056187aabcadcd5a3abc9637a864ac9df90a0d4c6e50003319000003301c077224489228846d0014800a23e85484a460482218c8827140180ac318068128868128866120904228ca83ce491eb753ffbdedaf0dfdbeb44f68ef77a4fd227bdf959a4f680b9f97f40becfd9e645f6ceff3a57da2addf93f40bd8fbbe3449481bdafaa56efbeeb67f65d0ef49fa425b9faf52c91084b47139569b08dba7ffbf06a3fa1ed75e9c445b5e550f4c821c062fe0209fa7beb99fcf23da83eb379eb5486ef667b010bf3268d4cc1082c65701362824b8c06e1632a3f16f8339ccf9b28f1b9a058f16943626a07da7ae0ac127be325b2f15a12f974f4c9df6a2e63be6b2089642d8697f9864364abf00a3d6ffe191d9d822aec92b9a6ff2c4c2758bf72d780d585a9b81837c6c47c77d87924391c131031be19a6e4a0e5c09a0a6b72ba3f831ad12f38b666b41f406cb88f5ba85d2690c58d54133ce8e4a88aaac56f4e7b6860e7f27744b08999a03c7981fb7ef0e36aa27b80df298f0504d2c50240d63521f00a3e343bff3ea2bdffb148f1919926240ee94bda7a07d6f3c825235845a220a20ab1cd88807982006597d887a881ec7497dee857bd01d608512448cf97d64e8194c7030785b7717f4f4c84c63b4aff136cfa68d26b698ca3850b1cfe65808dde5342799c7e1db7d4ef5a0d046fb627dec34c657d941fd3b8631ae4b6ed9c1af5c24156a38fd81484ddc9cb9e758c57abd1c7cc2e9360761f9c036014091fcc7fbdcd3118b96609ebe84843b6d82ae8c94dd1ab58e67c9f6449de5c26103e0adef6a482a5298ca6186eaf79345e1ef93cea811a147fee101c0b6e855a743a32151b19bca504eeeb3972ec33c68a546ea10f8ea2e8eb0933eef99f8257cb1cb42e80732f0672f79e1d74b141c6c6378e3175f1068c48404adaca11c297eac92812bcce42dbaff48e64a255b7b57ee45e84e8b9dfe86c0ea5d0e86dfb0b6d9a007182cca333990065146950453d171c26a0156d8163d73c2c484f59c8287a1098fae6a8f8e8762e953aff87e4baa0331d640555b9824897b2f9a7b93ff5b79805aba3b60f7734dfb5c75cd7d8b7facbc83e0d758a9b5b1b02da4ce98db4e543ec2710fa931669bb12e376ccaf103bc3fb0e97c386e6168fa7ee5e24593c6256162b1842787a4735c0954c7e0de3b44969f55d4c5382bfcbd9d9768feff952ebeab17b8eefd984625cb38f17615ba74ff4c9db215d2acc3d1685a688577d6773451ed34911eb6494e47d7a9055529c57a1b8da68d56d864ddc072800cd7bb5168c3741640808f417a0cd3f16415f6eaf834f71a3dacd63869e5610143d2dd72a4c866082ad7b5542262c0814856cb05249e5824f5806c9abcbe8e67ded5dbcd07464b81c079637d642e1a17823ed2c1dfd7e8705d5f6ac79a20da087616ddfbe9ca9af774a7f6e937d2d48624ee73602f8b0d582101cbc286154207a5bb5b564bf2c12863421c9f5cb25ceeb6ac176521b9a58274860d8a4d28fcd3ac433f2d58a2928c4c85fa45817ccf3e1af3addb1b5c83e16b7655e75c73cfaa8da1864f19efc5e500bf96b8829d5d5e809b2dc838ca126ab8fb671ec1633e2a071642afe1b348bb373ac33098d48a060d01b3c5955fda375b04314e01d7d3dd2b00df1ba4390e96c1c8ba2d63a5fc9846c001f43f6021e3b9446c7f18c9d1e4eeb0f2f7aa1a11ba446e63b617f786ded0030161d06b8269d337103aee063443efd6737ccbbfd3b803e6171598c5b2d7ca73a0a26aa998b9cd354e58560bf2786941e9b9aeb5d2dc051ea87800aa22f05c4937b590f6a621a11cd2680ea1e9bb1aa13ff748cdf5421ebbb20bf0de35f8c213937232d902197d7ee4ad4e4bc5ddcc3cd6638d389f345b48ff447118f08de7bd9786cb394ae148c7a7f0d830c874d013d568b7ee69332998ef7d814722a2afeb46bf3ec331e074b29fd4eeb75b57ce0d52633b1ee3e2b2b0c44687a0143697f1563290e4d4dffb9a807223f7bea6c4e6328fc31bd4d56ce52a527c024ef45e280c4fe698668ac89a6a7099c5e4ec8f4665b06df2325f7b7a3b0a81c48a2b249e86ea9768689f1a64ebef247873a2f1b9393db96a7e0b4b251ae39b16e56bd42733db6a23605594313a7fb3cce386ca087acab7b4b59d01a34b2811261f6da1bf1b95468c9dc864d029ce3c50d545948e455a1b601a4d89057d9be18659659d1ded2086ba31b9c9fab60d48869ba57d34b9876a97fef29c7eea38a1a7aaf6ca69ec53d67d388fe927a327d95750a7241fea86f6c2b1ba3389b8c58ee1ad4d7fb979ad0149bba27781ad85811ca02228d29545f6edb4162bdc80a34c5b9f022d64d95567a9f7cecb058b68d208a015465ac179c854a90642b805640a27281516eeda1b523b6ce7e70201b4d407a868ec9446a877b564e3f2cd04577fd5f5125fb46c0210a5ab66d0130f15659e11adc8f4779c53f720e224899c1e49b8959b3a0271c21b11b95b4a7cad7c5e44bdbca002bcf8021a9ae14f493f529341a54fd74f9910690f851fbb33c245d3a9d6d9d21a42aec3ddc8d7f43b7d3a0991efb78200e99665e776ebf66c28f4fa3af7f1c6db7494db95a1286e62887e6b919d216fdab6bda82e653be3daf15c55ca2ad1753149b4e361357736873cb5f533d5942a934046b91dcc7c7666a1c0ad5edb17994cdbc7b624ccbc0d477f9eaf4be5ba32191c166888c626a243bdc1f190226874eefa25291b63ebe517274d8fe904f3b36ed07accf89b320ce7b0b2027e36515fcce3beb9057eb177f1a76f4e657794ace520e1b507c142b6bf100c7684cef073115548b474d03006e6affb6e61691c33f5c2c5377867a29530cfb07fd811b136af9d2ebaafa5be3e850f4c89615fcb830a7399bae26e0ba7bc004380b80f235623a343f8f0c221147ee8d4cf9f890ebabbe1459b09e28c1b57e5787ad98b08c4a681f88b765de6d334cdaef9ce6d4aa91a1db28a572ec84a1c71976592fefa03499fc24fa44bc7fb87f44d85fee08a49884f98b1f08d8363abc24141301d0a035e4f7918895cc5fbb5e942a92c85f9b7c9bb9432988c56b1454742d5569336653426b7b04c5a06134eff477c1a27662d3fb5c73cefcadc1f8a8d059978313b62280f5dbddbbf2f312852af895989a8b222d32f151cc9d6010ea24c43a1f960d4e46b1ee792e4af7cb183893988c6dee337e1bb8083e5131c304439ea7c2fe6772680be117fe28859244c8753bedae481f40a76be71241885f12b022154540f232559bcc0d811714635b0983b468269fc296f570b871b6ed5c1edaa7f9bd560e871918f612cdc9c866680b735f5c31ee6732821567fa233c44b172964ac9ae25742e1ab90d08276c4949b29a0e2db6d2c3f88a2684c2fd42407cf883cef91f2e0de921c5c1a46f33ae88260c389c4c4bdc0e33b2bf97150ca1b574548681a721d6af4a3dbc07cad61102b3a408432113d1b4eb864d2aa511062cc575ab462af4e1d89317441acd8fa02be790944a134a533c5808a20deef82d65d4ab75415cbeba541460b22135b1ce2e1485e952aca5f48b76122f28806f2222f1ec80a551d4777386d9940edc39547ad5c4141ac253bd85307069955334510cd8acbcc4770443da1d32f0b9afdd75a636cb4a8c373f82e171d7cefa3a37bc335d05c9a004408eaeca347f72a198c86bf7e1099beac64223a4efb68a2b0c17aa192f10f917cae095d553ee6c42d6ac11dda156c79afeba4232a822dd6dd61c17b7639ae946565278fa4c1be428705b7544711d4cc19e6ac76c8204f1ee696915d398f08576bcdaec257a81f8b75bc0bbd2c574721f7fcd8b70cf29a0941c1897f92921bce4d3b4e72451fd5ac1a84e2ad77cc494504419480df0a3fbc99f06ac9fc4a2f2fb1f947b5941f8b62a40c911ba435b79ae7391b962a13d1130c3f1718bf1efadc7edeb45d24f6d9b24c26ec176b07ae9c29ea2a959a16d6f4841b04f2f2f4ac71bf0e40c2ae307b13e20387b24ddc53a7eafa478b1556c0c0520abe8819f66c066484e688aab1be9baedc05476e83a7cec9dd2e1ff1aa32a6f327b5404838b1fc1fecbdd7a49481a184d45bcbea1fc01f19bb6fa329b8e8701ee63eac22a3bf53f7b9a7bc867fd528349350d0f540ccdf2277a99593250b199c352e7edf52677a6ab0085060e7fb8e9234e8af938cec00c2b684cff955e2ecfe049f180b64560ecb97fb15f4709607399371eaf61f7d149e519361e4237525472ae71cc510937ece9f0fb4ee1b2eb9f33e403a4ea3a940db48da307e4e8e6b189ffcc50add28689ea62f3697966d82402eed47bca26e71bf88779f6f28ba3f43b6e8ade2d7dcc3d0d2311c3b2b487e6f2748c1ee16cfdae4c80f3f28d34fdc09aee4bc277cfc7c8d3b59313a8959e8886a589c78f2706ebc77b77ed5c27be9d0b0150300c9a26372d6c401bbdd701048989af139844be5531fca9da2ed72021897395b9b7a45bbd1ee52af3e5f44d5947b115dd31b7dc5f858c726eb52a07f980e76b14290399cd3d12f87d22a14381db3e1f2eaa51b8a2d2f50b12958a84823658c88308bce71e09483756857b48b00d9e791ae491d7bac632236064e348b1bec516b0b5b4f5759293bd3704609c575793947b97f405d765601150e458084b152a12cdccc4af995a8069d5260830de1b56566d35803fee06df4d06d4617f2a6c244e8e82aa68bd0ad11ec4eccd55b18773c7be802d5b3302b7408396d70931be2fb59e3e84e8090c4554432ceca1e7744f40fcab06a1a796b495ec8f51602bda5bc19d5cdaebe9c8e40ef774b1f40f58fabc9fe639b33a149444125193b94916409e4c9c11b4c0d736bcf2e140a2a7df657fdea490888b934403cdf965cba074856fe85815eaf097f7d4a334f4d1a1ccfeccf4551c6c8d48dd15f28880dd754db78fe5a3f61a7792378a382079b3babfaa1cf70a3127e52c67645ddf201365568d6f120c864d91dd67b9c72895f3eef119fe5a4570cc5f107b8c878c17983ede3801f42331b87115a5f05da021ae5b1c964746e944579a9e81b3f8339f537a5f4d16846f652e8ce41469a76ec0e70a7a2cbd38230c924aafd8c827774ae778d8e44e358efebd9b2097b173dfd3c71cfb88e78652d1d8e1fe96607082d703e3de65ce31f3ed1fcb287d1891942aebde1597a3e5217e89dbab8ebb9eb5f6e81ef0ad1770a34ba57aa072d628b202cf267f9513f77101d3edfa69bab2aa42a3175cd012a8c76bb85eeb42269a139b8910bf3ff8a3ca4744435a4da8224d79b148ed343978389e21619964643d2bea4f361be9e927a81dff0882fd8878aee1eeb6cde8a75c9f04a3fc86a6dac16667741fd86fb091050f56c4cdce6139514fa724b89c9af3ea62d9f00bd0ffc2be3d598a5c1108eab8a021a78140faa5665acf1a9c600546864e0687b000f3b03b93a5ded06f2ea936079b5427085e1e7836171e806392ff0cf0eea089e17f89ed415f7ea3ef44c9c81ab35c5c35e8984c33c4d6f2dcdef1fb8641699e5360fa96379f17da6b0b62a3563c2135677becb814cbcd1e3a262c3d1a12d78c8dac277832b8509e724027e8bcbc9125d9ba35aaa74a677507749961300719943420b907f90f3920c68a8cec04b028513eb1fa24c3eaa0d7f6d3f8e89127e85aa1b3f103b61895a8672a336242b52e11c445be3a0abc4317e59509676a65d400e9724c06c8a0a6737283c63b036f7d5b121acab27f9890abf2c5b0ee58a1c0f8c548bd6c6af227a081dd1b5bf40c14e542fef1b35a20d87a2c97e47e8213f74224235c50bce70a8c5126aad823e35f82bff4050e3505ff48e447f9a6ffd8f0ae7820a9f01138a11e6169f819646e863aeb9dee7fc4869436635747e0382fcc9209486cf89445d13d99a09e20efcad4c9edf419483c4b8f904c3cd8cb0980a2c8c721de5c5b60380837fa274d8b4b08576645fe8af30b8088f6ae55b93eda5f25b1f03859571799832311e8fcc56591d66e0dcd6886f4b21b4b7b9755bef935f0fca76fccd5e74bcd1aaa41ae1db9c5645dbaf5abbdd5d8daf0e1ad676858175ba8c8452b2c38806581fa6fe3e34f58d0b92d3471db206b2a60f8568f565b865ab5fb548a7105ca8e02f8722b387602f9bf541f859bf40d78113097a788f2448a5c120b40b8bebf34cae1fc879a00a32e2b2f7c0394e554274f5fcfa48eaaf3f8279f00f1fcf9c75114512c2895018ce7d1d219efbd2bd0eb81c248989b79c2aa90e71fd5ebb4fe1edbe81ac20dd2845077e48826aad3a44bbbdc98fc29dfc03ba7595a323eb420d7648020a6cab116dc8b5fce8bfe54f5f963bc816d6fe8753aefd40c3a2cea34ceb1fd1d0d65f6656d40373d5cebafee5b46e4daa0bd528c6faccaa15bcc7168538e7e1550c1cd3e7f1e4bd3eaf768e03e90e5e064b3a062921609905f4233da0378375787fdd58325926e84d0491b986205e7ad0431884be98d7411318597f8f2448a5c120b477869e677a0dbdfcce8336e992fe1c41a0425215e2dadd44cff8277af3330f4de290f43f2201b5b42ea25dfb458fe730fac11d076ad048d7bd078e030609a10b66464f021a7d08d6c13fcc7106569b28b9d5fa506c03d263b120fdd8f7811030bf76dd85e3480b2142076656cf0db4fa10cd03b6d78fb5b358a1de4590d29608e2f45f3d8406d6b7342b430171acea696ec81abd55100dabd73333ca5eef5f9724d8bfa83d504b65206bf70ae2723aae663ad1960a49953de1eb65a20f5d5fbedeca51d4c53a461bacb24baba932a59d657f49f8f688d3b7dfee0575f98ab010da5dd34d807b195a54dae3b67787a73f654cb31785215992f71c6f63e733f450294e4333074bed599b9ea9bdd5e2855326ce4c4fe375432a11c82cfbf0c5ce20d6a2ddbf36bdb4ffc5e9658da7e4883e8b4753c5e3187cec231223eaf8787dcdb15e882262e5b195afae6539d508def12fdbc7574af91046db1c1d98731dda307e39c739be99a728df1b41366a3cd3fbf93e42babcf1c7a1e3263e7f8f45c1936135375bde79d057cf73f7741fd43c3d33320bb84ee91567651d47b5e89159e52b42067ab8dc992c3bface234113376fe49179d8c09cacd15a1ebf619a3b5fa8073f41d41ba511f56838d4f45233223d28ad875ed2c4cf0380249db7035fcd2307589817d5571ed6b1794aee0fb06ac4a4f4a6a0c5b5091422a1a3a0a2f95bae7a1fa44b10a8956bf3151811912ca0d8e049a458f957bce4135175cc0befd3d753f64c2dd2a424c6d0ebea817222a8627c1766afa5e9524851a53b0bfba83731c40abf07c1a727079393999e1a820ec8a8879b100086c66cd569f9f01b462e7908da42538fc448c6fd9ff615c86a1e720b92e06fd9d0a8023f506ce01c886204102cb38b6e4195ca51dfa940270cea3ef62a36b5985a20d950456baf319cc0d5b1b8fc5da2acbd662ffae3010ae51a786251cd4f1906b7b10058865922ebcd06f020132308d6415673fa479a2066f2571b5fc875dc37af76d1d36c9e02158646743e2046e07b0a45cdd950dedd98c3570915f75e01afac99637b3ac5f9bf3a3a378aed14a2bbf5e1a554f70c8c32b2ba4585c1261a3ca1885ed63484b8e8581ea863b2441598d72340d65e0d6e717aaf8eaa30b211aca4438c28fe186715655d7097f2394ba2b055a3387ac9093ef5e882c3e50b2bf60eacf7ed86ef67d9aa9a61ec183d55f01ae2a26c29b515c65a8f2805788f2c7c4c4b52db0f445b5d99e55187a73cb966523347aea3b40b4edb9a2db911042d4a3d99489b270dac4ac19090d457ccc4ad5a7236158b49d3332f354ad8bdbf62602e7a276b8edf529b5d6eb01f10e88dc6cda052ae37129085aab6408d9f02133799557fc4478a19fec23c2ea3e24150a793bfe717feb06ff2af9b5493c1042ef90ece614821490184fd74a6bd238a8d5d46c75d012da7e3088b2471663d96ae65563281f581c6d2d04d67e09df80e8740b115c66ee111aedbfc168e1da1779d62abf1c85a66695c8453a047284e96868456b0df340f00b5faa97ef56f3d2c2ae9cd3e9bb752bfe96e412f32d97e976132f657f82e86143ad4d3944720199ecd9b8dde2c4833c2fd97c230d3fba2ccde13242963ff6e3e95bfba52f7df1bb8e30ba7ba1199d177cd49febd8b6bf1462ff1e3b1b179c70a3bc26662531ce33879a82ba1544f640bd50e3e0f72a2828cff5974d75be7f2d818953d97c07ada9d44cac80763f873e9adbeb9c9b28029e7c90caf005324a3b975d64f67da6849c655f172b1784bef80e5ae1c44650323a56bfa32237f1ca58db6b14f6f40291116434601d81013f2ccb738e90d277f2e82bc3138f0d82c3b3b4fbb4d80f1f1bac663f042905b92c13013c65fcfb9fe04e2e7a606a15775c88b543950af428e13e3c9392356ae9caa97bf77fc15122773de0ef66f492e5ca15c50ed8c3d06cc6055018bc2193362d6fbdd595e5e77768a4e012f63d9797561687931130695ad4ad75215fef4c6076921641624591a6d742ad328da281ee49baef3f046962328a40c7d86c7fe65af2c49f9a9561906de66bb24247442a87d774c9147c77efe11ea9bf3d5b2512a4923026e766d438ec317d29b9d0c45222440214dcfe9c20225aaad50801aa8701877fd9072bb440b5f28f0e06473b27b6eb6a073a7dd6e00e881c22585a01c95908cae051428c44c4cc91dfac6696de9982d4c56757b4c419995953098e55f6034d585895c0c86bc583303629b66c6f4430c64d100629beac6bcd658de0b56fa372d47d26b5d7ffd829e3b0d44ce68f70daef4ba4160580ec5062894522e7a3b19cbce29cd702db65e906bb287072321348f088291253444e0a4b25f5c011bcf4a1b194d24b324033d94fe0836b24a944786b0f1c09eaec21af4268a1c33fb11188c420e92dd0eb491d81f52e3dbf620c959c79a2df0f6a074cc376392733610fe21c798b05e448967542f228b510027b9dcaf074de244dd19874065c44a494ceb0da3925bb30fe7981d8151b7fbca099b8e9898d8c4461947241395911e267a205811b8009f94816920b40127e9fc9804ebd3538512fd1b118d968b681f9cc3c333b32aa29063823702b35a906b591c66016f21c242828333e06bc3c3e858cab00cebbac1114a3a4132245e5a6e42504f77c101f0928990854c105672062ae70befe0dbd77b505c4613245a0d7e2e6971d0ddff62ed3cf293e3197e48c8fe2ccbb3ecc9bf1d906d92012825cedf5cb52c9bedab099a4a2eb9bb4926e9e6c619008ffb242de727f8d46c3198593ebdd1c03a69d94bc0907843d97fd6b5421f00b36cc3e51eb540d8df4288b9cd2ac991e3486fe61560fbe148d28f59ff1e71dc1d37fee069e8a453ca42e7bf0f5a55fd4c95887e0f3102c898c8357a3781f81aa143aeacfb1ec2386e6a036647c0594193ac326018951f8861361886f2d27044f22762898b8fd46a799190155e097bfd0cac81a288ffff94d949761033b30995068ee705849d5256f9bf0c00fe3c131e376a66863a64e383cf782c4330cc3af4bedfe0c9ec5f14d0eba1f66d64243840534befbac00b805f7fff1b3edd52e26c99b506949697c2827a02acd55eefe33be88e2e4485ea0b12bf84a0ae2e7c8983aecdc443cfecf62f4c0a4ac3dcf22b52a0525a28ee1bfd06033a14b2fd830a5f8858ab5961f16e0eb1c75b5b55d28fd9082ea2ed2a3311c3aa7ad51d042595c14bf006343f909c1dc9f92735ff92eb44090014d184c554473f33b17aacaa482c6152d80bf451586ab0fcf075223f505f796930dea8319d19e46fafee4c85d4964e5f7dbbc440b015243ab93d3f0e6274842446d2ab0095a4680a8e0a7881166b3f6e53c3183d34e94935c4d31254cb0c12cb022d5a44f9e7c881eed92184ff858a0e170c52dc9f877dcf8bf0c667ed87ea82413920d2993cd02a88b7520950e1d5d193328ab3b2fe6c560e579a3c3933338de880a566a290e43c0eb7352e071ee5df447a38f05fc1e025059916696b2c0ce5cc8287669776ce486cd74d5e333cc1692e7072a35a4a7ef8d92db80eec6c59b28e55a3ff9bb029f235789a58eab6e078764ad06d0a254e863f761120a7684c3e74a1d84796e0c0d68ab36f32dcc1580424a07a4156ab686c95eee2a02bd3d4c50c307cd2e489992661b4666329f6035b03241f93a942b7ecc626bf6e9623d56b76ae99061c80cf006c051e637c2b0736891a51d2ec279ec5e5b92b440deab189a17084c841744961994cb8855777e8364a622b1b5ffac8b555ddccb7643ca0961bc7709554b258522d2d346f826e61e8980af9f489f920c8e106b50918e04d51de0aaac0b0e99189b6d336336f408c4d99a9e96d3621c1981e21b54d0ba01a6b7ba14c6606cc20b4c59c1d9817c8d98a23145fc9583483f9e714b755858c91c86746973ca7f811c95d11e1569a18ac14dcfc5f07119bb2a939918fbbca92404ee98e65946ac593d30748cefaece480b7cf3c1331c1ac87d691d66a6d65946d6e1290ae8c0a604d26597bd45734b905d333ad58935b0626d3924493e18c6c7ae947510da9d2897d268ba6b607cfdbd1346464ef24b39de32b32b1cb3f76c69ed25fa1e2751f105e355be5a6a2a8193e0eca9e9f9af24b186f5d0e1a1424d32198f8b19caa986e800ac59d7e6200deaed146a22ce2ea171079a24e543c793678eba5fdab5fb15c7f65417c60ef05b59d1c38fea9719624980ea3d321438caa18f3572c70a9753142ace4eb4a665faa8c3fd9d7f516727954f1577094000e5040a10c008afe1ebe147a5172c7ed129aa1334e6caa3dd822c6c3a2b22949552c05b28bf72b6133e88a5d1d4b140aecd8ac652dc24b2b5beb757a69596b556ae58c02ce5a6586afbbd044d3858a9516af303b0f34185e83a5abefbfe5f46f19b27f2fc7c763e4e4c5779e9356ee5c01ca033d490c8ac4cd1a2a74728d57d26e0ac30c07bbd8dcd45f4510243361cd7c0074731eab40a6971eb62df6fac81806d1ccb20a44b4493d790bcd9a3096f204614156813e86eee0f14e6e7752de3c96ee7676aefe1dacd535cf26923f451c63ab6b54bc9149689a3b4650d7dedb95f30b87799336754ba243f3ed590eead02597618b8a1ce5649e3849f1f28726936ea9ddbc0f3dadea95698a0b161f9ebda069a69614569ca7840eb4bca0ab54d1cf8f2a3ff461e8f5f3b9d87237623d8317ea8e0136b45cc03125f405265180caaf2d51568688b7500d72bd85382986205b0110ea61d5a4060464c188988ee57871c8e1e340a27e1776baf2f38a304b42c7fad071663bb91b1b77339400f7c247d938b8c58b94a600ac9b0258b1f49b9c83e71831df93f9b8dc03172e4635ab3aef8269f73f370a2f9dbd41c8af81f48befcdb0b955e6f8024505fa076480692280ea2bc93822247d72d8db1062580e4571320fe083197d1c7f716fff070638e83489b36456be9f6f5069c243a680fb282386032b6e25e756a9377a5d1bc903e497bec8e5befad52ffaad3c959213b490bc3f63ad2e4f2d1368b7905ab40dbfe52906c19e28328103e6e81e3775ab4b56ce80a197a4a2905ff98c518577865262d42a2dd6323bce2c1df615dee0064c0d4eb9dc156fa64ea80e179374cb8e0281883076952bf63239d64638d90800ffb491ec95c2fb06f209d69664bbf10f0ab4bf04d4a6ce887bf19092ea06565b18b48e814ff13470957dd9617c50cf02997438596c52a15579c0916557e9257074cfeac8882ff41616b5b6e3d3ee810ab1903eaf53bbb3c059a7303a55e5e3dc2b6f75f8b5d572af63eed9dfee976cfce3a1586eff5b7cf6a58eddc210802d72899dac984a101789247eaa77df4483b473c0db707991771023a64df2a7d8cbed454a2d7eb0acfdac84d6696d0564b313984c69b4d569789a0fd7a74a34158440e6db33806150afe5e28171aec000e8c2d3feafa08ced04fdb96b29222cfde67833c85f6f4340a407fb4a12d4ccc8117b59622a21ae123571b43a3897533edd8f53ea3bd483177c2d4f216b076fa6d2a02197a52a53165ec1ad169858dd710261958e72a8b5a0116ac5237e88f8fc957ed2da1cdbf4d408de6938e84046fb79433b6c38eece5d20996fa9753a0a571d21aec22f2de2b5058c2b77585cac9110169f12175259681bb90755e2486fc343f839798e170b1f6e8405d87ee254b476e47a24701b3d1a2829823e646fd383122dbdace71b26776bca7040e6f3d3bea45dd427d004a0729cc98b1623c66d1e5841bc52411d7ac205ed29a2ff0d21b1e2fe3148a4d5f437c0348bb9f7497fb6e86f54cf8da789919d46a8e3fad4ac54f3c954c0097abff02f222404c2bc7dce5949a8dd44f287cc371606536a2c78b5215201c00bac18f0cff5adf8835287f39c6b8fd69937f18df13adb43d0d004d9f22bcd526df1b9680a9cf2e4dc10d1cad07e6b383d95becec2dc790a36f2281ad47a5df64827e67e96b1131628a2c9d74ec1f6abc7e5ee569eacbf49359f2e9697ee86fb4fd096403e84c9122f497196237e32e22399d3bbbc03887d47e7408cd2dec39bb1e48e5fb50aa0069ab11e1b52c1607192ca48ebcc06d9f1dc33f6c093389d5f2424fb9464061ae7351e6058dd9b7e3ad3512a94c25f4b8a61494817fc510e9be5d5942551cc56e5a38070ff14fa4ac27dece612dd1c293e0e447cffdaf18d47f0696434204f990e8144b653513b110be33c633bd676062d10d9512865313bb71d6f2c424698707f1ca2aea956b177774829fe909e8237d1c12c97889e48559f98295133a3c647f96a050dcca9100fb32e920efa603c310e7013c72810e51c9b8c34c865039226afa8ad6e10a2d518ea2e64dbdee70f1697345cc233cd68887f47ba1cc46547d408878a2eabd01a64b7419096d25cac652d33226446ed5daff738d8a0b63919d257310619afe81cbe148c9fdc4991b0c41c26691b800633f251ce8a6767efc3eaac9c8f115fc17cb83f9a99e2cc836cd864386cec4423252e5976f70762677d38b936f8a0840110f3b49eaa625f26ad725df6ffbd58bb2d67fdcbdb88fa0e2592fadc4964295cc82aad0654de8a38d41654ac51f6c6752842a77328aab54c926f14a9247d7f61ea0d99084888253f570912436321714664bf874c2a764200c0c9fb4303815bd2044a9512e604b8137420274e1a7d6501afeba0fe58d8048b1a1ea6c258124226278e457a60d266ef13a14bd0cfa142cf481612c78a3d9197b0c394efc9c4845b5c892e01072a73eaabb3c3c99895dd23af27d07104e2b54e5e6d48fb51d3f35580b0aa8bf12ffd3a639ff75989512048d747dcade05efaf24c7ca9b43c6e8e9d59cd0fa2db57a8f92d730a37ceca0ec253db1125475fa38930f9a2de718ec2e0f1a9b6db7fb0d307ee92215ed9b1fe7aee2e8a1af43e629e02a9bcd4cab427c66e432a0f10d599e9404b0f770fad11691b280a77e616621002ac8250f93e6cd80bf0e818e8b098aa3edbc5b1845360566082d52f12267c30e654f6edbd84a4a7fb83e939a3d6b7b5c0b23bb51535ced0a44f5314b17d926a5d2e750553050b6bf4481ff0781117ccc6036b508dedc5f52bbb0aa43d87b33e8648a9c83bd87bf4c51d59ee4b9a891da84629dcc6d9a42c43ac2ff01f55da9df990126a6a115a3f520d875f03abdf653958327ee111f2a657c02a292ff02b75be40b3d1c4660aba3a4f93aa626b207b079db769de72e783c148e2aa81d340de82839e53cc852bdd4485d9055449038f2e2a824cfa48d742b4e605206ddf82f6b74cd2f3029bc30112f21da2e89c36951a8daedad9d0dd0b0b64784ad0666c166a207b2f1ced6af598ac93ffc4c9aa54967468bdea157ceb069cdb546e714148c1d7b3d002f446cdbd5bb38ad833d3a196fe4e6c2b5366f7d0e3d81f1d409cf9b9fafae0ba86d670ea85c6a08afaf6b3190e22a3e0db590abc2dff02d84224f2704dc22e81ce900affef86ec842d101ab9e348ca434d0dab5d5567e71f11d21fa8e7160544bd7f9e32d53774eb6880fb5716247e1baec052909e487749d3e639c2f9c7311640ef6d2eaab4e3aadedcfdc4c0d3581fc1ecb5c2bf7934ba57e58c866c2ed31bc8727252b6371ac2f0024f5e69a25b4fac3ec9c3a4b6bacde6b6a77575ef4fcacb133a57856aefca5ed4a447039a593b59855871635f1e6d6e57acf6d8cdd12375ee4710cc0c6a1968a3633c449110125957d0dbee88054aba19b61cc727d36b3753a94fb63b978f16231843629c6d3d8daff2a723968cc2580745348ac45d9d1927e9692ac4c5d8e8efc86a7c56a5cc122a67a7b3ac5f8ef485a228d4cc46736dc9770ad7f68ce8dc3957394fab94b6cf294dacb0773ab34ec2b4befb91085b5268b6a89fe991d8dc975c5076c104a7ceec1f3e541e43690f868f28be19a4eb249ff2159c90ead4f3a7c3689a9f0c430388a63638009735007f3321b76a308ce378667794d6552e4f1464a7c2b3fbd8f0155a5c49f04aded81b407c57da002f9d74a9849cfc0fa7542c77a3c0b90cf694e5159965806a27d7f0d486f7f4cff7ae420b8558405dbd494a219cf077dadf337c559ec828fc47d02663be54c18368e04333b63c4383b01441492ce7c8081d6439b2138381a74bbae4f40c6353487af8879da9d15e02b053ba9193c33c31067790850362521f26fef936b4a1c25e6278b44db390761befec2769775b809dc54d598fdb59de117d02586f0423b0f3d45093d0b69052dbdd143bca4897a4c6d4af65936f95097532d9b1513fbda5a1fbc24719ea75c6bc4181d395dac5258b0851f47cf5eb93aa9a54183965488dc0c6a9d45bd8246f37a582fa252945a579542141875121891b7239b8c5e1c7f667d9605e6d6f007a86bd33d22dd818aa9d5e7579b9e7cf734dfd62d5e5af12f4096f8a384824f3f608ea1e7c0a2bc302cc488ae1b35e183994dfcd4b62147bebe5d7404fa0566d62084750a85da3a6a8f222155dfa09531457487054706e8b565194ab84d1b8aa87298a2bd26726442a510322bcd2ca0a013b98732faae656dea021639629a92e2e6824c8c09851a8900331c21cd70f82990785bb2c9ec219573298f510b895c15b05f4b681c2261b9ad94e90e37fd1a737e18ad22a92971e6cc93a1084cf2155914fcbdec0ec8a5a5986f5a70cc3dcf4ec4d621fb9c4f6cd71bea04798f0f3fc6b17bafdbecc4e1ec3b7c24618420f3409017ac7e02d505010b1ea1256a9f27c23cc1713d4859d2c95a6a18228b60ffa1875915125245422078f1a3188810300000e59882c3bd9410c201818001b80d92e6e4811fdc7d9843e33c3ffdd04edd62ada96494ad6ee0203c702b7024ebfad9bf29f31294a245b2bd2f04b894d2fbfde905ac652daa8b6f43287a0137f7e4b3f2950e90a2174d8f3c81917ab2a119723e858950e79a196a868482dfc624efed563c2e99270ca184ba7343a1b7df39e926c246dfb6061125427c630ac855d6b10a43e0d913b22a5ba3422a2ba31ad142829dabca624a5bfa3338d0e2eb8792d255c30962fd81c94d9b2170643779c5f6a92b389697eb7caae6609e677ab4c66068200473c705803658e0f202cff3b03391eee1c34043b5ee2e6c001870a6e0876f0ae6007b7ce413b1a64601184602c147f0065e22c000d37e010aca0770e38168a4930e104165c88a10c66de6a3039592333f09edf2c113ca190e382a00240d983293ece2f35ba9cf5fce13177fe787f7350847c3ceafe7b73e8d64f66eb2b8bf1d5c5e27bda43d24b91e7836773c7b483a2d4414360e8d171e75e70cfe4ac5b2d6d54761a370407f486e316f3e2a9d2f61461e35ba373f1d6224d4d2c8d8a3675b6645654f624d3c1ce8c05b15c8348c46284d559e3c05a7c8d797175557135558da8d12da7d6892c2013584025577e5677555aaab5293229afa82b28a7a7232721163d4d4da697a59f13a51f90695e921c52db91d90a30a32c29545112918aa11f15372960847c5250f82ca37bd0b9a75b101a50d8cf968f548f120f91336827474726a76705a767e4a66b03a30957a376e24a539da192618a91c240993b2f342e3c2d26784ab4f4b1d8ad64555ca46051ba4eac4c5894ac2881828467a446c4e9b2749a0043fe8020787f5b3fb6beb2185f5d2cbea73d24bd14793e7836774cbb1d94ba9d10187a74dcb917dc3339eb564b1b959dc60dc101bde1b8c5bc5069d31111b275d7ea5c645baca995a581a1699d49992d9549c9a2589eb1e69529969312962362ed82d57d65bdd6bacab8c0aa5a54a9ad252d6956148be7aa69c5acc249a1c21199ea4ad54565a16ed69eca3c00eb408b03a90d9634906610c580e782a605cc0a6e5228b811f1e84e5027413682b5641904600f683920d5c01203d205a20af07434136022609372808d08b24ba26e4496634d2e3300ac00391dde9f6531241a8f71ac63c6ba3ea4908dab81b9fa915b53f62bca90d254ec2f6e6fa2e210d9935d71dfbd677f861ef24e3e9c85d11026b910fe3ffa0b6620dfde50d9900df8851b7e0bd13949964812638f74df5c0d7e0d33f74dd7b421800f4f9990d10a924f9af4e5a5d9d6e4a2f744d90b8466ce850d659cf0dce9109d60c148e1c7c6cb8cebd2f6dc7ccbd8bbee9fa4ee7fa762efbdf7dd7bcfe6bf8b8a74327d13e54decbdf7be39e7ccde3b726f627f1b104164872c6f1e570200d028bc00ae88ecbd77993b7f24812a5d1e64d38248f6a66d692f6c373d59ac3aa8971f0c49634c3532cf2bd7e59ae7471d9979be04e10003456af982bae29928a145a56e2b0356e50216b79125cc6b6f19de9befbdbd366c3c86985f286bd2fc61dc435a6bad753145a8438933e0d396e1bdb5b5a5cbb066e932ac5b56302b4a4aa36273539bc1f935d524d386d4e501c9a39aaad9896996a1109ba9d79489f6a457e69c5130cb25d00af065a3c1cab37bb2911a5595107d027a2b31dbd306b58a0a4b51bf69d71494910c56e3860e0958e99fc3793cbf54375afc2844314415084cb9c9deec4160c35b5f3509c51dd50c0a7a23475db967d44ca3e0648576c714949e5331aa8b1d2d0b7aba5b46512a58bd7e8213d4e1288344d414c618cb528b90013b3f4e45324a2f725d9b94c55438024861c15935ff60a46e4a6ef647bd69c144e131c270eb4f75fe30bf54384f4fa54b76b4283b7e756d2a1358e159e55c3c99a02a0102388513a7b4b7e536a7cc8be3d22eb1285a9210a02346f7a56442c7454df96e2e756e2a7778336d313ef2f313b718f8bf8dff5c84f1eac4053f1a8d47213026acf9d79b7e75e02f897f7eb374f8923280bb73a0a1cc871d76f9bd9fc7b0f0680caeccffc6dfc2f98d5a1b993f62b98ef7cf182aeef76603b9615c11a268fdb19bf919941e018136c662c7d2051663a67175c6dd8959228bda5a04eeeb71101133190bd117a8a4164f4a8dc482450cbb1c961378c31a4e69d65a6b3ebf54b8a1302e96ad9d5815e10c58327935cd12698cf28fa27156512dac4f9ff2355709f8fffffff93e0ee70f6412b8f42fdf7befbdf7de7befbdf7de7bac2b28086fb8f31dafe8105776b9a2ca646be1c44e864a49ec79c49f0bd3759c3cba675347cd18147c45e673ac3bb583d5c705f91c2107cefcf0acb2b621511188d9ddb4700a2223d272f978d438644d0d1ae7089b8ab4aa2923a5bef253790103098331b6f50dd989c671c9c595d796e692a7d44ccca31f67cbb2476e2aa845c0f2f1a86db0791a5d33412972d7b213680e51a612dbe89dd19bde5090788c61b8d9a627b8a2e3f6a14c7547949ac78a9975c327d226d70cea1a2c9886062a35356279ce209b1c962d7326542fa86748ec7416ce1fc8e486091048024bf30f7c639b8bf30bf576dd6a9d26e48abfb03e63fe88f7dd735838e40102e331163a7de03ee7337c4067ae99e1403cc7841b301ee3786ffe9bcf990c1940c61db20c131139ffcded161780b71d147861ce7531ec888fdf3b172b4c60c72f7c03f3deb94504f0f48929b3fe8213845944476436a5ec2a28f698712b2fa24d1e0ed03449b0520ba4aa62cec7aaf4d6d138abdec4b478cab8d00da42ce4cf95319240267b72f3773fe7a210476c187e61350fbf375dca90efa4cb70aff07ffefb6fc861bcbaf4be9359a3dc79f2bff7de7b5733a0f7de7bb7120b78ac210146f918c7724f4211da3f8d0164ce8d0570dbe87b874076fdf06ecb38f62cb9bd877f6bcb3202886361d710845108effd5a280f00cc3909b54645e3cb1fd606113776f285e1de7b9fd970437c861aee75fa35356916ad967948d51e69989cb4a8618c8f317357bd6b55e20f2fbcff2e80d8adffbb9f67df8a1e36fcc223bdf7fe7b67e1adecaca9b89d0ddbe890c52216b5cc63638633543d4f27b36bdab5e7616db037644d673d7a6cefa44787e63cc82f482687c8d75cf3cd390f0639e71d5afe90f550cbf0731ee49c73be7be79c73ce797fce39f7e07232e9616d46c0a5b5a65a5f5463acacf4500856bf13b71c9e99124acde7567d0a698f56b8bdd65a6b9da662e6d5280f589ea2e50a59080c1907266a5f6a735d203b25a75a575491ac9e3fcc7bdf2a22f04c61261cf3e182c25449d1f69428aa9c5c9914286a84a42fd32daa90c896b213395d5567965821506201415843028cf2318e85227e77c88f620b6584981ad9614a3bab96297b090acd1bcdd8092668fc61c32fbcff2e80b8ef1db6d18158dc7ec3d751e9e3c55c2392b9a5d55349083080c4ae7835d41328707ba207547d6952a167ad623bcadae1e316871db2ccf573bb03c0338617141765446c84951e6bcb3364684aaa5b0dab31c5fe9c7775101780114ebe27ad6a9559a4ec4d13542cd2de98eada3a8860ef90e57d8c6361af22f52e17f4496e2150f3ad549cc1b74943e3566808ff6f6fe0dfffff7aeb036fde019cf9d7235cf816a5ef98ba1dd1c6adb5d6da18f0d623323234ef6a0f945fb8c330ea5877a07d41d48fbcbec6ef7cc75b66f1ce90c88350defdc4ffffe6da1e210ca415dad41ee61b1c5b5ab44984164f31402d6a6270863f549ce7eb32cdc459c550bed3848b8a1855654bf9bf5f6b9b1f0f87237887b525199f845f18de5dfbffcfb7eef2631c6f2df3adb7c80f829c23f901d40bfe6bcd77168df8bfafd9b8ff7fad0ec11dc32fbcfdffffffffffffff7f64872c0f7de6ef56940f3574cdb65d2824cdedb6dbb2f71563e9c7e2911d1222b1ffffcfe5a407566786e797ba953400d2f5ab4370c39df3e725573912522792902f39229cbb144a569a8ab854dcad5cb53af2aac6a47b3696a5c4c4f645cc48f911f7cc5a08ab0648850e4dc5ee2f7fcd336b38a459579d4311a2ee028bd4a8548bf86846004a77531a0c2010876120c791467c2d1400091cd24468a848242858c024108a0281502800060241a1202008832014072118c3642886911300c93dbe43781e1e053d0e85d1b25e4320e9ce459f94650dd4f841267ac7151c6b7e9785dd455fd8e572c62d03260f3a97a8789ee8a29a4e8d6276dde80b2a83090644eb711aa69a553c32dfbacee705e4529bf99af360a2910fe420f4bb0cc8b5293a685efefaded1e37131d566f69a9d815e8ee8c15247138d46a641527afe90143b636b80e2abfc0eaad649e3697514d306061991c133216489488b6cf3e306527bb1ec4eae59c0b7dedc2064bf5c4a0689fc4402553ae4c3c2265767ad8520f99f5e107096d49effd8d8e70d129ee3e6979636326d126ee3e68643a630e12e77dd7d6bd300579f426e4528364407c8b00a406cc5f3ff2c422acd45771d006bde5d1ad7464cb291ae7ae99bb0efd1a7ce03ed654a44e1cf45e0a066b036da4b7c8f5bc49f7f63608217ccd05506a02b5a27872d27c7bfc96cbabd1e23f30ba41384cb781d0c69a91b8e82db6407848ba0875270822ce3967aa0a445a7c8e3c4b5ceb0861ee2e26d358b1204b514b1b7a3da8e35b52b0b374fc91319f5340f5d42e0123362bd577afeae9ce5070a7ad3603a9b391b4cf9fd0de4cd457819c794fde36d55681b63e0cef12eac85b4c7185bed8d37e5ea22a88f70baf186ee721941f4d686123a86a51e7df0044bd2ca377a5283a9656a89ba784b424c8028fbdbeb54bc9b0c234b132a3a8b8ce3423a69cf3a7610c2dbdb9200772f99787bbe9ee4e858f3d00e05cf1a73bba3d3f94f9787f89f912348253abcdf185f7b209835aa76b69a47c937372abc8ba4d90acae2e5993d024af5e3420cc814ec0ae0d77a326b0ad10d88cce3e00ab917ac96d7385d06ef2312145a4674ef80e75e6aee37229d1cf02fa9470940f036ea8c490ff82ef0b703acc05baac221e4e28c4087bb363aae7a6ac0db71828350d0c0fa70488e5bfdce00de82b292843491a1df2d12a71a13f99014420f2ab36e199d90b59be39a352de680be9b52a83af0c0eae73353943e4b967fe40aa2ec71fd335250ae9fdc4b9323be71427db7811267aa0b1ffa657bad4e1849b845700d198fb2120479607f82941020c4fbf753a2be5b35be60363665f8b743db239725556a57ba6baf8daeb7a877534b1535126da195733dd1b3bac08251f36358cf11efb6929931049eab5b786ba38fd316d0627a3d766208bcfbfb6fb1fc6328e30765a7c74412026dd8ddf48aaf7fc0508282e4a576d1dd7069fca472c6c190f37cff85bc549e8382dc4d3af9471523ab207f6e7b514d3e51f41f868e197d5564bdd00424020945670eb94d215f03d49eb54067abc53330f5c768d49c8265ec9be4b60ba84d53202ea1a501fa6ea03ac0a057b5a8373b7e7efa58ef02aa0d99ed802867c8b643ab8470638ccbd9d3cfb076a99fa18cda75c5aa0d9266718e60efe9459d2e00527911175c14deac969108f5c8dfe1f3833cc3c413efd4571c1a890af80ab7a8e3e3eb6dadc731f1af18b9e97bfb6966877d71bf20d5ce6e5f2e209e1a2aebe179f3ef24778e086d1ab85d0d0f86123d02b3f4a6ea3308b7e1402c4a59523f33c878f29da77926a7b0ce60e7c63ecf4238039977b7a44cc9c7416ec219d8d6844e01ce8fa01ec250be3fcffe8a70e033dc28ef3642c6083e0da7a64948f6d513c1e516fa2cdc13638c0b582e11e0a52c02c2f4db8841a5390ee073678521097d0035a9660dc7a86762768bc3610fe03669e7ceda2fdf97de0223f5f80491634132c92e4b0c5424c301d8d812ace758c11b58d1dda9fc89a4856e60b10001e1989628b9c5499feeeccb2c1668ff5cc4077ccec82abf18cd21f14de662c291fd147f17acfa0453d1b878fe01ed8b65483ec5cb03e8953cdfc6744fb81e027e87189ab38cb1311f11c29adc206b40d55f8292b7618dfae2d82f2f412bf717b1196502f345066ca1cc7ed3643a477e20ff8437c629bb1ae82963d221855de47da5209ba27d07707af73d1b94991d39a088913e43522b8d91d5ab476d68ca8ff355d74e8ba399ba5ff9bade98f5e00194918bfda47740ce97cba785dc42c0513d540df89131740d2c231f969091c386b9c51f1b8cb6299f23e02dbf2037942e983e224d2aaa2ccf29dc546603a9fe39336f834fa384d291ac287830b36a0a4641c372151cb9f5f02db3aa03db0cec5d1fddffb93f93ffed193ce810170884461be134d44092791e0382fa2bc105f27816aabadf600266e77f7134006dc53d428a9835f291f0fce706d8571eeb7c5b8c8fe1378d47de1a17d59fffdec08565468c0870930da09ce0b73356dbfd679eb288f0b98aa823c123f44d5ce51716e28195b4e625b08a81491310ff82f99a6ea60753e1cf87733bba9b4162221f35d0e071f5cc5f8c4a258a332c53f8d8e7cbb8723e3cd309b09949e309878c10360a0e4631bc913b3c875338f5ae3fc1447baddd736f442226b82802f953911f7ff9751588dbe71eac5a8e333c02b9697e917cc76bec42ba7050a76a592637d615d537886c046c594c77f94cf313acb2a0a25f25b6e850f04a21de8b35285b2b1bc6f3b6ec61607b957a5d09970c9e3f57892720793a85bbfce84b036449e80044218deb007a828f460807a5ac29963cec9f300047bba1caca4c4fb854aef34de4e0e60cda9c1dde63f52b891bdc72cd08f4d21d234e8f84cbc3d7249784f8f5e8ffd0a2e9ca986d81da00473e5d528a2f6070cd502a91769a9423294d6ff2fdbc6d8e20a8dd997acb299dc7787d3016b4b0288dbae585d5a2c5c6987dc54a51813cefa188dae14134ff9426306d4782cb6c3ae9b6235a031d2f9ee377948184fac01a0f6c56cd6a738779d0ca675834c16075995d5c1367431527a931a226b8a5ab86deb844bb74b19974a34085c370dc5437e43b9c0f257b7048e0ba2b78a78b81be953befe49ce245f5b0d473d993c2707202af3b763b5e3334f050ed9c4f2f59f7cac726c4083110c2d89d08a83ecf35b69dc228e94e019656e5295baa4848e43df2e2fe889a602b91f2d2885c339e72b910f0bf1801550b7091260ea7579b19898fc09fd179ee493a4e202d35fe9ae4a1e6f11d2b5a9a8d53b026a0d654256fbec6d8c0784a7f4074ec853aad788711c2d0f64cb85d1d67487016fcdea605051462b88d33798e07eb4f87ef82ca30ee810954fe16e4fde9be2e1867b90deb9516e4f07befb0c0dabbfbd0ce453340c7dc2c18cff534680f98efe73fb8bb8fd3dce88076b5de6ec0dcd86e80fd899e0d1a5e0527b79110dace64b924b3654f52e361194b142fbc4f24f958316cc3aac13feb68a24a03f4a90a0dc01ad60f1ed7037b90ec87146480ce4db7e3e25f18cc35285eb2c1f8ac8e6182e552bd19cd4bde64806220ee188ae4f9d1380d023f0f83cccd179394c61281bd04339eccb343d712bef4e65b48504b92885748b38739145dadf50886f6da5ccf6b8818b48fc685025d49df31b027ad9a7b1e8b601620bb782a01eca1f2aa63ffc49cc2dc00c53f6222a00a711836271448c12afcd0cc331edab35c74c46e5a58658aebd231e8c573800eb83705feb3e3fd1877f0297f42cac96c650abe7f257d805db3267229705c4bb3bd45d43e6f1a5c222c4fa402bb3cd7e26c59271df129f903c1150cad9b419cf1a4e011e1e9982748f709411cf3dfb221b24f3e2131fbb84873d27fd9338e8a04479787332117891d45f5f0dc12a72c5e417d235af4f6a52671ba06a81dd145d725295804f919c06fcb515500b396c9b8313813dce945ec1b2d130153775dce6a3b4a88321e3d6101f12a4d1d54521b9454fe7e2e02c08f159bcdc77a20c67a8284b3c8c201ce4702d991d9957c4bcaec0237fdef0b97777d112bdb2e186bc1437b17d2e5b1101183641f1287f2aa517416aa08f8bc596fc3b4f61b93409c5da7e9a184b98290d0b6c247a5e2edb6ca4e402ce5353b46df270601d1110edfaa32e6a26e60725193550ae36a2404245abdfe3ccde5c69049a0307e272cc5a085fbdbc48642d8eec7826c8e29a39ed3d255f1f667d3aede7fa14d3d2665cd2d7fcf12bdf9c1c654baa59b72d584d2e72d29208ed43c92ed8aaa9b82fbc2d009e2de9a944c779022084abf17c084266b6f552933804088c82944ff93c3a8954352e4a3718ea17c642bdf6e76cc0e243469fef1de01a4bf6da0c8fcfa5c9a92aa52df882fa856c1dec1e5ebcb1878e92724923c01ff23514cecd41e5ad592cd344ab569e6cf65356e2a9dc7eee35485a0940ff688672202bf79cb3abca67ce1a56e2681920b8b414f0872adc25b967dd606140873447485860e52291c04ea7fe851e28997d2b6889c9a20d608fea304e2e265c3db36794d1eaad8384aa1a770c3401c205c0019ab9e14edb6cb3f2171f6fff0e8d080f2cd82b95a66606b982caa386dde09e37f8910c58cfc159cf795ac55de7106bb3445569c71201bb9ee85840569879dc87a67e04b17e809f8b467f811d9f624ade8aa929eaf27a5cf714d983d0a6c192008c32dbcb3a1217c1740842de3ef9622adcf1e24af8865713681a4277d6d24b37bddb217674dceb1a76579962511168a5a6a4d32308b2e5b8fe55991c7f3ece9344d98c484fef01b5052dafd7e67d9fdd8c08c02fcbe9320fc3128b517e7cc00ba52fd96c995ed3ecb9e82d225ae4311bdca6cbb93f7de932cbcf654dbf6c06473d6b26cc0be0b90696ad013eaf8250b707389ec7fb9222b13a0d8a15e0ee65c6ba9f2b463d0931832c2eaed36820c98841a402cb74258119bab38b690c4646567ecc34834850e0c369106d20ec16e6db0fa040ed44f9b34d555a42431983f2cee6c9a8254b9601902774dc37fa5a622f5dbdf0e57278a9487c884ee63fc8b8d292a92125b81f8c092ea46395f3ee56e7c94201bc8d426a4aca2a4c90a5cdd39748a627dbb8a453008c75583224fa51b7f2b0ca638611076ce0633646636b6d7cd058075331b64c2d675b7e2c298ff754c0417361b89f0e84ef83d7c3225a362f9afee0e2aa697853d53f89876b842fa6021446430e8d79046fcffd8321c4aa41a920db85e31104ef8143848f640d282838bb6f6f3e2268c18d7ec9e298c5ed54bd9d1735cb3b81e446775aa9ccaaf2bf1e680fc77b74429c224f23d6e4c22c325a52a4dd9591f44c7a77944df6e91f61dbdb9a2217c6e6d6ab1dc6cec96e809b1c5100a230e2a31f56939403a377ceaf3551c75ac6e515a79ba15f11fffe737691299e8ba6f76b97fc3d390b862aa4651a4c0592ec5a5a99515c400714b6c9169b8798c4cc5730859da55e91a99506d9d830a3d3101b999c329500f818aa41cf25c253d1f1d0e33b7cec3a9245eec106bc99a14ef7c72eeb1a441a0dbda2be47b768ec28e4fc8aff03ca7157235cd1181121542aba49bd4a7e25b7f1ab510e8284c91ace23164972889a1267021214c43e0251b8d3d9120486fb37d7e59e1cfacc88de7cb5d60ebbd9696bf80093c39755c6dce39cdfb201de0fa1b22999593bedfc4b4351a2f6d9ccc053f9b001325ee25b261225f1714aff534d10519758d905d009807a22301bb39f5b2a95a3e94474799705cce5b5463e4b2343762f6477993c847b3c5f196d1dd1b3aae09826d2cace847afe8cbb11f03ce78609ce10a1905f28db48cd539cd2139da932d03fde565ecbc4921c8e9fddfa6deedd94a20afcd437b82633f54cc802d43c4eff00728a202c99193913492e9e3cbb193d43a2407a56ae5cc6f362f0c578ffc6ef696012c307344edafdffee8516dce99a14973d26fff7d9808d7f35ea21ba13bbf3dcb0009b6589527f0b305feea82976d5d958b319ee00a8f00b7ae422480ed677c5e7ab5139fdb6ce83db1290a802b523b0f9f4f14d15bb592f56ed5339e8ca71f8bce2ba08ba125a11ee88c34855c7746b54acbcb27458d744ed0f49d42df8ee3e2855d6bd33e449cced0d19adf254edc7cf058526206f161de777881197be47e997d211412945750a6bbdf08fea6ebe5a65177d1bd77994790853e97686843147a29982583e66915c9072ee62077442271d71d8d098cffd7297c47ae8690cdd55cacfa99b4596a866469aa213cde0ba255ee23cfb1eccf617923549dc980d7461c206d1907a517d7a14d02bb711861df5e535ddd1de652de1681c0eb34009673e67916a84bc23ae9efbaf996c5b4ee45b167a9e1d00c38b90d48a1816fa683e327f7c280ac301c2ee38cc1cb7c876438c6aa98d17a27e20cc30f5d2e0e156c45d62ffcbc61b983736c20481b535cad94f5f101d34cba8beac52f8f9136755d501dfde6214580ae647d8e2aa48d1579e80b08fbb92951f8cf8c8f4713f040ddfa277435c4cc0fd8bd17f390dd8af0b600452b8100e01376c38b14dc6de3eb78462bf991150c4126488d86def9150e57c030346496256026bcac99630641582f5d0c13560d201ae545d7116c4e576e0e76105ffa47b823d8a01bdc399eda18215c50fda673ead7a0bd378225f1c24c397d08451ec39c81bbfcdf4b9c14565217c29b988b71a9c225f66dac7e81c18308a0e9d6fa38d14cfa27563eb8810781f1b8d28f0ad3530991ca247d5bc9bca3cbc7dfc7f2418ed6d7c716c00b6beab6bbf8bc635d1fb8bfbea335f671ae45995ece2e6119c82edd8059bf6dc3531787b50b5dcb49d6e5e3c367b3e035c5658b92c443ba842d34f84b1760bb07974eea03ab53cb323fb864c7775d55cd4f547080ed70923b1762d7235d3efe84e23f5b735e6a7a3e5824554888f90327313a03eabd904bcd80365f52a92845e6d4f8fcd654ee477377dc4891483a91c86e11b5ac141b2cd40643775bf4b3311fd7950206fcc16e83d2da2ec2a5bb5abb14f6812d8a03f7b06f5cad0e33b8bbb13d511dac360f6ebd5dcb57ea8d24c14bab71fc82d69f9a7922b52e58183fa95914c4dea9f92b31130c4728c6a0a3579cd819e9c7e4522661ea737de03bc410f8c86f24300d9536139b81842886494103d9efd611c31bdd2f6dc0c74b80306842b221b820c0cbf7f7ed27dcd755bf64ff6d1fe2febcfa5af7c7041f03f929ec9bd437db67a83fa47eaabed27d6bfb69e813d037d9e7db6ff2f811879f559feefe69fa14fb0cf459eaf7d3b7a81fb12f55bfdefd65fa24848f60f96deabbf50dea8bd49f565febfeb23e8efd14f44dee9bed33dc1f29f8a9c857ba6f4c3f8d7d02fa26f579fa4dee47dc4faa4f55ffb43e8b7d02fb2c8fdfeff906f703eacbea57bbbf6c9f843e02fd36fb6efb06f705ee4fbbaf35f9c3888fa19f83becb7d337d86fa63f6b3ee6bd5b7b69fc63e83be4b7d3ee73729fc88fa59f7a9eedfd667a1cf609fa57ebf7d83fb81fb72f7abdd5fe67c16c047b0df67df6ddfa0bec0fd69f795ee8fe9a3d8cf61dfa4be993ec3e28f34fce4d40786fad0f4a7dcd7babf7cdf9e7ec6fda6fb45f6fdf4e3dc4fd5577a7c63cecf439f80be497db6fd96fb11f793eed3d5bf6d9f629fc03ecbfd7e93567834e27c02b3ff521faebeccfbf4f7cfed23ea2bba5f45ff6d1fe6bed47dbdfac7f471381f83e3fbd46fdb67d417513faf7e5d7d6bfb28f629ecf7d467db67513f667fee3e5de75b367c1ec2e4883c9b270849a206df4037b159f2254b4973e28075d3808eecef50f5d9d35f6d7fe3fa44eb67db4f729f8afa22ee5fb63f1ef341447c40fad1d47f63df03fa0deaf7d76f6f5f65fa9def27bd9f6d1f89fa2aea17b97f5df345b67c88f483ea4752df15fa0fe8efb8dfdfd88afdfec4ea010a8865505a304cd24a0733f40c74ddd593650fccd63ab3b337cb2f2000e204346424ba93262b02c6f1739395b5dfc7517284332657c030101c1d6e5785fb4f4dbe731454f443b58df5e7988c71a9987bf3e83cfa9290bde5de7b6f29654a326505a204ea04596a3f3844929f0734738e43afcadbd65dd66f77b23ae6602bfd1bc046a82662118a89abdcdf357e2d3e107a0a44989005649a2488540113fa40e8d6382f905731e54abddb4bb783087db80d6180c11dba42374a183e61679717d3d97dfcbceb24f7b32afbd84843bae43cf03bfdabb2ef68fa9d46ee40df5d16105567d0c6ab5c3275f73b929d636fd317c85f9234bcef1d1cbefea7e77d70b8e5da4931bb78b44ef219356a405dcf4ba0ef9ed33e6e5b94366fcf83ed1b49abd6c79d8ff27bde3fed230e5faf1f1c5aeb83718c9a8d6a256944dc837770682d6e59e57ebd2cf7d9a8efdfd6bd79f3595ed82e54dc4a1df319fe1d8b3cb7e90c62f6aa2c85cdfeb7cdda304750e69ed6eee58d45b972b262aaeaa5a9a9a72a3252576882be06f81424a79c73ce9bbbab8347ceb6cf3f3fd9679fa42f6f8ea09cd9ef0e9a9f43fa3982b27f9c77e8ce8d7b1e3ca48030f4429887d00f611e431d0e4db04272fa0086911cba11e63766211d3cb2d721f3d08d30a10cd329a4690c18342b603993e4003564deb0206508141a58ff1075aa28b1a932051b1608c0a58632376031f3c60dac7fde0c38183dc731679ac86de74f2307a3905855f9628314501410858f1e439e3451c10f9d1bc90b1c2256aa18e9a124842522cd9a224ebad059d225b161c7f20c0624b9ca37ccb4992607630eb7cfbd24697c604a152a1b9444fd5801d6df4c9246047a706287ab237494b6c0fa3bd64a31073bce1d10bc30c9970db7304b7c880244098cac100636a74462630e3b26a7721480091429c8db091318cf178dd7ce2c21fdcb24f224b51ccd106192b4ede43866054d0c8862bafb5f6dfa63ffd6ffd2344df33c4df3bc269e17f4b296db26d7e379e0ff7c59cb45d1d6da4c11f39b5d8e61cc04f186db6846a7e5ac4091a205c70b1c2929a5947213001ccde8c43500521c008ca68680b20394f5f87977b77f6fb9a57c7233ddb3dbb6d19919318b1431413345ba64568e668a50c9e18ba026122a6a054a8c80c18a9438b07e33059226394cfd908187beead6b6984daa1e35da0e41685e8e627628c349d59fa097b5dc3675b02c92a270701204082a37725c3e518131424e1534fc60f2d1769b2e4d4a29a978a9451c2126c80d505f6e900293545091041a226400628aeca25959e1041a904cbd81c9353b0c31c5143d748822881c585448a0208a2e3d40612487144e40e3e6489928b22ccbb28c0b97258430c35402364fcc4cf951c6ca11262e4cb1022cb287219afcd76b6e1a6db9dd36b9254be9aa000d2660a3841a27264e60320e1d2c6ace006992260a29a5dcbe68f14018345637045124cacc07ac6c802ac114a5364aa2a266d7347b415423a85a1635b65ab35a9570c1d62a71a82facb0e40b9126599cc0b42544307981489a372ac852022e48f8485a63822c53609a10578458e2480c8a0a4e93c733edcedce236b32c9b940e90a5b4fed72a628ca66974f6b0e69c934e3aa3a6f7515153c045d334adde609134c920c40714aa24e1a049851eb4bc3901489a2cab5e8f9c74ce1ae49aa6cdafbec0e253020d74e048191264cd189816c48618ca34e1c5479536b5d65a6badb52ee18305a5934efa53eb1525c4744e8c8071c3062acd932e300d4e0b6b9c50938316261db45ac57c9a18add62834adca27a9a6695aa5d26a05ffe7cb5a4ed3b48c62c0c7171fad879681e2450a94116aa8ec00a18304941927374328a1820ae650252186ea882f564a507a12841346e4103135b5a06d4ec9b22c6365599665d4860c89cb9b17ced0a044136a58806552ce9c6044082a58d2a040ce962a754b0a4fbc308133c40b9d28b54492113f0831a3a507184d9e9122843cc19929d6922398f4a8820a26638e80654f58a84365882545a4c8b22ccbb2d7192772f8643629a5526e9a1694c1a1370438ace1020a413e755b39d83ebf6ddf069f3b0c45c8e738e8569286947218fd383a0c2965955b9209a554ee977229cb259f1b7a9d707b20faae4b3b296a2f71381f68bbf49bfb6da3ef52339523941aa97c418e506a9424c90ffb2aa94e2f7eee12e873ccedc89fe776de3027638ec6a59c8c3f7577c73e04836a6430a806d0cfab7194ea90997ea42f796ecc9a4dfaa0d458e5f8dde0c11deb62b071ea87952f73c7a28c3256fad7720a1bc6a910e4eb652d6e34afadc62b08e4b60de8b91b6e61c772c4a9f81d510c74a38351cafa523e91e4441da8279ae408f544111a394239112477ac577c47a70e9d39759bf7683e5125aa7528a82f98ef434495a8d63ae377a47d47f33bfbea6fdfd1f4ef3a84ed3b3c1ddc41f38ab9c2ca67acf8ee9fdf91afe076e882c41050386122888e092846baac0152c6cd6665bf94c2ae884764f9fa3a70e5151cb7f9e69b7c16ef3894af75dc5df1fe1d693173dd8ae7567c7f3756fc8fa464261899dd17902526a23bcfbd87f41d87449bf63e40dfbd7feb4607c356b8833b9fee67d6308e1dad07fbd0a759fe107ded391cddd3ace1a1be806be11d9e43a4cc752191d7f73b591fa9bcab0793e3b8797d3e7e9cdc88f2cb70870f75061bfe1844c4452aa25be3f4f199eff3f43bf2f1b9533e77b9a15a5f7bbfb4bb317bf746d23bbfe1a437809efe6703e8e977afdd2ebbb3bec61c87808872d7643bbb5ecf04d3126d49e0f559f61c4f7d1bfabcf73bbfc3fdbc9bab074e941368baeeb91bf2c8dcf69303a4ec2e458094353c820064c7f2257743ed5b7ccdbc7937a09f38d431f30816f4e7817cc7ae610e9032c5dc77db77d72ff74037d49ef5f48b80cf774f3f0d3afbbcbca13f873fe7dce73bfc7dfc3cf0790ee3f0f90ee3d0ee0ecf126f98f5f2ebc0f5f2fddba0074f072dfed64b1c7ad3c19d29819f9faf3f373421d7f9fed56faddbbb94797bffa9dfaa2fe76fad2a792ae6f916cfcbcb83e3c4616db57e5a302de89a1c7fe3161bb8dedff58ec32e7338b8cf3a787df659c8b9d721f96fde7d7dff8e74789e9f859ebbdf0949c81b100d4520bbf7f99f8ff135bf756bf13b3eef9f8dce3edfdd987f2e8e1d9e7df008fd74669fe190e300c9aff6db959a8b9fd773f7d3f174b072effa30b3e0daece2b660fdce77d4fa5caefc7add9facf71b6eeffaceafffd70db7fcfa79438fe5399fb638e5c5ac93eb294b17fe2c16efb28a2aaa98922c7ebb3ab4ec58c7eb22e5f81db176785ec122f3fa5bd18fa38533499a5853278d970cc449228a1b27351861c4d005258cf8425776d66ee72b20420879c2440569061de6a0b1a20414393d602c928833e48d8fa6285e6c52163368944e000411b0f9724aced7aa36df3656d5fc67d579a5b7f9eb54dea62f98bfc9779657c579bdcac1f952babcac4d6e4f6f1894a95c72b0adae7ace73df412cd0876dd559248d8841fdd066296377acc3924916f1be23df01a1cb9c4e7210880f84dec8852ed7dfe119fb61e9d3c7e0d21fba34eca770dbe6d7ba5da775e6959ce3569d372cb7772aec550e4aa0dfb60f81f086e9c6b3f56cad972efbf3316e8fc105faa11b230ca10b04f4da77ccc5836d0f0e6db883ee609aa5cf6befdccf2c310e1f1ceea0b93ecd0e8471d4ff36a81807f7345b7e6497508cda72c3961ab62cd902c39c73729d95ec5f8e515b826459963535c1a265dbb6a62258ae5827515890b03c2c2ab4bceffbbe966d4a8ac2e2c37af2ba763c79af3cb1b3b3c393640e538d1fabf65059fe459afab3f6c4865bf62f43ae88e1a1082184b002f3236756e0429c16e220b982f98be04f02ccdf48d2d8acad91b7fc2198c31a317ec43c620b10e6f737e6d13f3b2afe430859db41b3d386c1fd0591770d0d881cf45f80cfa81de7e1bfdb87444446e1e631868394a886cfd026de417367da78049a331ce4a0672c3865da6e6c1863fa077def0ec3df3fafaf64ee98be9893e2707e36e76ff8fdca9fe18e2cbb2f40079d66d9a30e506849d3e6c89724531c9314b2f0104515228cc0fae30603a57f8452f6dfa08e8865ef9cd8f9928a871ca1a8b8647f05e40845e5e3e3f705c318924edc9f07190b33e4961fe860c4fed6ffe51ffd379f53b2cc764d2c0f2121301e26b8688049916f42cf10b0d6f38874ac9e96603c4cc820aaca13acf5266429882201ac85a5b4e8406aed7d903b77ef3efb2e647d76c30ff2f7ddcd11c4ba2fa2b0dd7b479d3ff78e3570f0bf7f01d6eaff7dd79f0dfff94632bbdefbf4be591e8680bcb2b52d757158cab1d13d29c7f69304264b297f56d6c86ff8dfdd3084a38a3b1686c00e9a3570b0e36dd141e69fbd06bc7e6dcf3be6e06f5e15a125428e0ec838d6e849be671b7fcc5a0771b1840d5dd18d0dff45772c3e7d70817e230d58deed30e7d49ac7e4e1cd837fa40f3a38710c07713806a96cfc90088c11e46fe42e25f63a5b4903e904513ef810640c1418a08091f0020c47201142030e4eb0feb839129bf477d01514a9cbb5f4850737534a51e918981d62b8f2c4253a91692a3fd09e8fb1e8c5ef7c8c4503f8d6c7584480a202bccfc758047efdf9401f63d1018a62bc8cef9efb188b2e087a0fba000132621cc00030c0021060002f04a086e7fbfb8458e4ead13e62112b167d3f62110ab1e895422c5a118b5a7c7fab108be2f7f70ab1c882ef6f20b188c5b78bef6f16621100bebf5b884501f8fe16128bfe3e76a187f4fc0e0e9bc84e91d6ffe0b08de4fe791f1cf6119faf38ec1772ff04c261c3909be2b09124e9a6ee391c760cb93fe2b065c8ed386c25b937aed3289000aacf4f6ba7c76219dfcaf20300001751860537461937cb6f71b72ccbcfa40cf9ac2bdf73dd186558b5795222462b39ee4b3c5cb50ad19d8874cc899494b8a4c5134024f46da5946d95fbbf338879e889be7fe2041009e124d7814d9cdc763e06778b30e6c4de3f7cd8fefe8ab425cc10273a4b6c9418d11f99d858774cc66e89f550f0440ace0c4941d65880029b14c440f5a64e0f37666a00b2848a26ac86e8ef9458346037837575f69b8793cfb574e7a494dacb8d7292d229bb00503090a08c0c52e0a024ad5c6989e3a66acb126590705343ac4a0990334e57983001153fe8505501ebaff646f7daf67c95dfd1fc4e8995dff3f3caa5a51b1a71b763f3473255a00f5f9c2ce264fd43b9e41f22e59d8a430bf4598648b1bc23cbb6fb8ec9ee7770b859e5aefb1facc476ee61f219ed33e6ccb288cfe8f9f69e9e9e9e9e8f30baf7963ddf15cb705ed1c1dc8a3bb26cbfab92c1daeeb76077ac63355a3f3eedee719e77a3fefccdca1287a59483ddc34920240df9f4c445667972ab7eb76a61b7b2b773cf0d659adcefd59fdf545de533e0d8dbb9d5752f1994bbb08655f49895dc36fb8343ebb381396cab200f1b38d2c4264e1040be046103aa870f609a9c7c597ae2129dc80ca579e2248e1460a04002e204993628cc6026494953f901a44b6afa41ab364f4a80518eb70a0f80aa21093a4f7078220858af19c3c313535461642a4c24d231275252be8fd5b2b6c5fa6acb1620d4fffec33cbeaf8f04f36e0b10bc673d0bf360bdf748b02827892338d449f2420a563008a8206502272790382209587f65ddcf637dfeb15ad6b6585f6d59e95875decd20aaca53ee0c62aedfde7fcffabcec6b56e577b47d5bed6dd6bcca41a7ea577ab052cfbb115635ed8aa0f3118f93ddc9dbc7316474c8f1a3802ff910c1c8350624b9caf5b37f073b8c91bbefbe3a9aee63ce32ec4ae360ff77e4e2b5e1171a8652bfffc938199f72327ee5535e45c6ca1b83e6c8d9cb5bc129cda71c46bf86c31cae0cbe00c2fa87ffdd182b7f66cfe1ca3eb374bad47903d8701162453e65fbaa9af7c56c7ac7c98a51464842f60f73b8b2d328156be1aa66bfd9a419d36c524f21c9f13b0a0a12522787b249c81c19450c5304c30210b9898ce23422ec5629e7a434cb6ecc974e4ea37dd26cb34952322cb338d88fc2c324174f40bf36c486b2c98af462ccbeba74f2194d7c463635db3571f2196167b6dbb8387da3acacfbda79d995997adf0d99b39f73d6ef86e7653c4e36fbe8a0772346308261df68efd84d1ca30c4ff2a7281aa224492b3ec3712b49269f11f1cbc130168cedbed84e075b3b281bbfeec91a3995289d172bad388d4d765dac9228a35fc6102fe827eb5f35011038532ca12a67382ca1aa95655394d1dfe560c38ec9a6eefb59681390b06e1a492a95c312aa42c71443517a1887457018f159945095e787ce941dcc529d1c764c36e506b3344736f98c2cfb0874745688945be1083946191eaf72c7c5864831b6d8289f5c606a8445d84bfb71f00f8bb0f8f625ff05d838042ddff3a20c7f0d88976f481c8da82e5e64eb606c3b9164f79438678c33e6f0f28c1a55966919159ba96643d554316e485acf26aafc5ea3237cda0226c767e53886492a1f2d4d71bf5becc8f43486a986216a74679240b4e0833365d5bdfee86d1eb12c6f38a3dc91394611c921772c471cdb479e788a117295639490aa8a00970f91ca9e5d4cf627f21971f3aa7f1140f292b718237ed490aabce598e9c7efcfc696a9fc4c3469f45e514b68b2ff01723ce343294fa711df77de83b2f3b55eedbbfece9d0ec3aff63b574adc808df5be6291f711a9be1769c4a1d7ce5e28021372fcee3fef632cb2dfba31cab038ac51a3114747df52ee697d49a38628e5da8102b287c3f85a485fcbc0c6a8a5ab2c99fbda5f8743241a6fd839d2cf06addfb75adc53eeed57efe30645393ead1fd2d7b6cf067dae05f7f46bad3874efbdb07e0d69ad971531088a72fc8843107fc7966394a1bd76351a5b77b26ed8fd7c59bdaf61fc4ac4461cb3e7ddb0e2d7b08bf83187afec613742bfe4d778832e783d7bfc0d830e76ce6e0d079d4a251bdfb1c718290ec2313ec7683bb7dcdb12666e61e6c7cfb48fff92c6c67d7c0963b3afcf11cc3176378112250c0bbe8491dd1af46a00c548c2e87fba8084d1df1dc56748dc4dad34a744e827a13f840e50912ea9a021b00081956076d02c29cdf18689eff48391cb2f399cd1cc0f31640e58a9430eb71c89f0724c2205f20a1c5c8596225cea68517a2f6bb945c4314a0b14394605901ba824cdf594414c32e70c01000080004315000028140a0844028158300b72204cfd14000b74a63c62582691c622811408311005210c40310c03200c020c32c0200529a2dd00b5141f2dea4b26862c5d72b1f1b48ea30f815acf1a6ccc567002db0443d31b35b685ab8adaaa94aa5229c3345961a58f65e94a5c02019403f48b5dc32c5b9697e134ce1117515cb370790bd7d971edff108d19282fd573b4bcb25072d94e02bb8c12398223c5d2f5e75de0cad0bdc5ffca020c57d0731ab802d0661ff69a906fd9aa7156a1a5b7b50a7cc0f8aeb22c163a7c129ea9f824c5c58d6f71311e94b2df3f288c0817ad79e1b1f110f655554fee15dc38dd0f9c547ee2df8fe57a3bed1bec0b207875f17a84a1a44e7de4c136a917780c742960366526be435e9b100cfcc955448322301cf457ac71d5dc88667d861085095e2ffc5c29fe97b8aa1ac7c25660898e5255c8b6de97c843f2e51356accc18feaa980a7b05109eb6717d58c6c90c54b9d10996eb69b7b88dc1907d04d21d7b8e77278316ff942151e709196074241179825bb2e8f834a3c0897d7efc158489af3fa7620851e780173e2bab7c73e480275b44188064272a5f422e0e8976551d842e521a18a3ccb196fe77ef723b446d13b14e76214f626b7de9570004206332ffeb1f82ecfc8a55425b2414cef45b4e2efa92670944430d1578bed4d9ef0b165b5c450d4b06d2ec0dc0081223f40d32ff8ea51f7e12bf703858df286cdd93ee61a86bd5d90511b85e7abaefd2f299790bf1a11fca4c252688df64d01f6686846873836cb49f8970e28d9b072169975b16ca6d2d5e8a1ed0bc15e766bbd53c70502e0414b58f35dc3a79c164bc796faa1e59201ae76c705fed0e57b91399c44ae98f6b8acbd3804d238c8ce37510b2c4529901bb856587e2ebd1e9b6583c2527bde6e1380adaed2aff5e84c2a216bff93d9e54478d1b3783fa02f77cc919796b88e64ba354b3439a07b7934eef88f27b7f9aff9dd8b549b153ceba393f1489ee70b1db3fd844aaa19d802847b7c0710882ee9b5790509b269e3152c556718bc15ac9f64845ad0ef15618dd1c49445051798ce585444fbcd9da9925085800b5b2404f91a15578f93c59868d5b1d6b12aa3cdba228f7c4bc73134db156408a5bf96c84729a0aa9e938e2e4f823a9cea07539d4459cde4c09d5af96edadcbcb65664bf8d7456c307359802e97c7096c9616440a59fdf2b157667ae7b614276ea2d5d09e1b8811ef51f9117279693a112e8443b1dfe8d66495486edc03212b81a184ab15fe94ec353b0ca2511173263eb294efa15504a8420a775edbf3b10652289486b6c421bbb607b822fd3524412b07a315addd101502bd7661fb125066e13942054dfc707d4a51014f816cb9c904e7bc3ca1f5269b57b16f07418d6c669943abc9a05a41594dd9e66f2c3a7dc45277cd2c5a8b6b79506c81e1a35fe1079433ad1eeae6ed1d8b2719e84c73601485c77e1285a5e5becba775cd95459e900ee1204bb02417c4d5ca7a2d0b7d3810a77d64793c74e25abdd6f12914e291d8b29376ac95064bcd97a8b2b37bc42453ca123501640bbf6ccba996816ef8edf4ba7770c1f14a55575888161b3a3a944ddde55a036b56190ee3cfde9ad6c6166905e100ba00b4cc047fcba653f03d9e75b787f577a499b98e09bfd7d9db41b3bb71c390a000da38ffe054e608c8be7485c8ccbeeb51134422a1796ede01f8ddbb9791345324d3433c4f815d88cfbfac1c771a75d1b166f1eb0cf2600c9267eeb7c86b095709b22536cd78991ff5fc5257af7770283be99f414c0616e946ef1a64e3ac89580228760e1000c73513018c8c05f146030bc5ec1760ed15fc6d81d9a9928cf28e5a4c1ab7e0c805618ca81641074b8d9dc5d5910753b43a1bea6cedf75109edfa2900dd133c2bed05e7aeac38921f9d899152a61be443c2f34810d0942f7ed873d89642d0e9407ab8ec54d41fbd47e69f2ea92e462b23132af2813309f739f36e77006b35b0604d63e0a9393df05f1a15dc0857eeb591c6b969fbc3f901b9558698110cfc92a3d877373045104d665a488c85943233b4209a4162cdbfcfa4c721c4a00166d8e9026e65f00d7f4796fd69f998dd232fd5ad1b1cd7b27c6806d164e065ae3d37898f07e25364d6043f21b5f0a6043b3886b2a1d8141e30254393dae94cd6299daf5c6c4ccb97f3d1e242649fbaba33827c226652704d6dbf6a105880966432c003844b3299c29b5afe401694759d1d838b9785b927bd1029005bea8bb22f646a2451666c060a7399cdac1f9620bb33d4089f2cc127e28c7654cc66e6b0b968071bd527d751c4f7a1f4c69098a48d44eb86c50a9cf1a2c6ae2e3fce1ab8b8c65c275d40c0ef41729eae4167c6a7465038d834c6051c0e24b4977a4081f9a1cf362e86ef91149510870799347e76d3ffd0d5674c2ae7b3480da255c70b33e78671dc80549ef478c399f4fb0a0618040351e0087c7639dcb6b9760023147f088e70fe27e86d7d9b403777143912659774303a96a4cd0671d87fda3a126321d6b0c73f16f24b3a978c5508d2d6b6ee7620ad693082c8340752f16df52ebedb37ac26cdfde4c5306fcc039f6cafd6bf066dfb61543f8b1504d92f2ba980a260385a4091aed1e753c1e9b905dde98c99855e69b6b06285a9ad3f90ba3f3cd289b2eafc68bd8164fb7a7f9a8b0c16f8567590362b69b62eb02d5f4b28aaff7598de06bf2ea97f1d103b62fae6cce800ea2fa5b793ced560655482c325c5bc3388b2ce029321c4260b7246ec4bb34b58a77ed15ea64974fbd25e877bd33fda223eb4f7c368beb4152e26cfc895bd37a72f58191a8fda21aba68388a6f88d7c1b4c29f8a285a47feb8a51c8b584c68d587cfa47dbcdc50071312413964d0872b28bbedda55d652d9bd1e88b0e63a217de65403dd20297a2bad5ed81fa1495c6d58477465655307279d169c2731e7ac6b610778d1bb96246347bb9b9791403c41758b145d84c7b352baea8df7d9e346013472662392820fc32051f69849f217ed021c016f23f41dde4894e5bbe92a2fd4232e5a356d21f77a1a8c087f54fd4dd50c631184b5e0a0649acc568379ed8a3496876c367ed73cfba5f5670d0a68bce2d10faaec1dc2248cf7219992dd1185ded886526fdd020edb41727e3ed986bd27b1a4d25da6e6b6c341e6e924731e1bbeb5893fa6c12e6ed385a5bef31990119e11ceb2fcbf0f033a43ad5ef4e55818909c82c106a64709e0d778c2335a9d0e7e6b4e30202ea25f18353c45d919ff78a5bebf3be6e162eb3ac30a9fbda84320dd8b4b3d86384ce5877028d4dc8a3bd8cc6865df69413fbbd532f4e915678fab18545a0fe9b3ed26194a4fff7b507d7e1789735a3ac318d78326bf567d74d2fcab0930186ff61f16a840dc6657f290f85dcbb58c62359c92210456c1bd0d556a24c7c0632301e638c938264f778c03803d3648ab4c0e30b4cb9d832631bf3b38402926d9468f20f0cbd5a1aa345bed8db6e8ad0ad53161df7011c8d054781816f915360a69e44437ef6bed573c2b6979d5de2fdd8c54765aa36b84e76d9895882a7cf5d0957f0bf646499a0aefb32fb2853676cf4a9e5b36b74b89c323122e410b5160a4c296a711b1f7a77d11e29433d5e32ebb106bc29d16421903a63fed159a288d6905ec0b1477d0a9bb008475da2291a11f96bbfa81a6a5d548d012729423160eb9283c8ea06061af04e999d78b1456200051ebea41c46009db795341a250b1f76144870f65f252ec662f768bc085a23eba6178a7dde2d66eb764648e6647e84897b39b132b9171b98789ce6dd5c9386ceb33dd1918774f9392cfeeaeb3947d3fd8a084a2817f67f4df65a981d3fce81d4d1d650332e4bf7530d842a8589d2236f824d4445cc9c35f0445314e29b95a3ae52666f54209e9151fcd100a606c0fa8b24230708e7a6c645ac77a9abfaf564d2334b4183308c884a5d87aec913acfbf84a213bd2f144f04aa3e2797b1a57c55c4df9722e2a1d69463d1333abee8008654b2881155aa0090d1a25a9a9359684010a31253b421d22d430055e3b6f5d414db28f06557eb9bc5b48d7a8c0adae2249dd48aa0da4c33c74c4ccf579d1793399ec9035ec800ebcb7e6e9a6509d539ef4a47a7d54a9d1d0f217ff39a0ddacd767e8377da2272caa779a9ec76cf3e50329d82753efd9f81c65e39304a322cd7b64743e93064a6db0089d1550244117a731de52b4b23512360d9d53b1ca9658039ae7b9fe067b9a1c4c573d4cdf526614c16d44f0360907bbf4db38b129601d66f4c30932b5752b1b0d6cb78f1258cdca1551d14855da84eb587c88447ef9b496f03026dc9e5adae49e22322a0d9425d60dd11cad1a34c9f9828dcd6459a23a2064b159fc221cab589683f98aaec20288e5c7dcf38c480238ea7d11480e8f62d39000c41d60749598e7b96a71d6216cbb7e009ba61c62672162b3f4fec17a88631af3c4a283d7a0a54b71ae353c46c0ccba22838c4028d54b9e434c00a6ea631a6a273d36389d9d3335bea9c139dc931ecabab373e8e8d845b2b2cd14caa4801c72ddfcb985c4d82273f3254c7930bb80e81d6885465a2c0cdba13e06f2e99f7957655ea681843eb13353d3d39abbe2867a844ca9a76e563d50593dfc80d81fcc844f46996168e4e4e02d159342d9db6018aa83e8d36a79630c7fa647db8b335ef544be6cedc17095eeb9fc9431273c1136698c4ed77e131ac564be15ced703dcdb9b258311cc093091154425f323c778609b7dae9b849b4ac911e225bf06002b2991be18b7404640c544bf7981f9641c67df839c6c720153b7ab528ce2fab911238d63397819aca31aeb9f5fb2fe9f0654d23e6c335d00b39e34a49dd87b8ece8a1d6003fb0f04455bd09fc80f98e97484a0440e9f12c968afa9ba7eec1c174e9b69d01d0e4e0962e75ec6f1ac53aad914fa7fd2a70de8b5989fb0c01e4fc3e813c1dbe8f6e052c652921f15f2dc20a5c7e4187e112fe2ac414aea3b4024091ff4a56bcbdec22afff3544aa3c0c52ccca22ea1c0d422786a1b5022c51377eaac9b4f9366a1d111c401b6e71e9d451b9531a599c89362ee48ccf29e6967148e641f39e1c04a061036d51f88e60bd410bf228ed58ba7c55ad63d7e8a282d04aaea60a503cfca2b18cf5c8b28284355c34a8d267f0f64a76811123de8882a87dffef07d301380a9a43836e2d335bc04a8feaaa41bf72ed75610223f46fe3be19894fc85167209d55d67534ecac0c205a67233b2d3665e684be0458dcbe9a6ff1a17089a405f47b5dc3990436e5c86ebd00f6820206482e7f9015bdda91002e825cd42e4e7183108ec0bc950028adcc8b2a8c9bd1cbbe1bd466129d59a3ac619e27347508c371479118a01be50cb0b9c4fcb077e5f94b7ddf95c4c13300a6fc68d1dbc06a6c29a4c808cc094c941680f42b08b5ac2f2f37252659b8cf76c39a561c5007fbf5f8ea6096135224dddacda08587ac73c7487a3cc1d90bf3168c9962003930ef7b4584296ed6774405c1100bf6d0a1480cc738ff7e967cd3934701f0f11c45f09d81892d71c82ea12875639d7ecbc02286328b4192ee1a6bd8913b1f0ef86aa17ce1968731f9bace248ba8ff81b6dbddbb8afd7f4a4d0f261fbc1c146683f64f0888d8fe0c0f124ff87aa2c9f63fe17f242d7f6b97084e99bef56c6a10fdca72bfec0e5851580b8e7a493107f4d0319ce468d9ae590d958eca211408498dcdb646c5bfa884e03cfc8476b229328aef6abce82a213ae5f421633106094a6fe7c246ce095c307eea0311f8792e0ebcf90590846844ba0dc9711c3d3810f4591a898b7e53d05a266fa3fefedafb24ceeb2cb1d27efa513104e877511b984260102375eb348f2ae399e266f6cf421f751e779e89fe761120c7c2dc226ee079fca01d93ad62782a4d1b898d8711acf6e6a0da7bb9144f7b731d8fcf0aa40ed7e55348962c6245dd51e85fb222a631c37355c0a14f7ee77a16dfdf9ead05c5a64b1e1515cf4a3ed465234b9d4d675eaae904001f4ed4c387ce88353d429cd7976a37ea8a6e3dc0907b5e3d1a1ba3336d13c30812cd41a5a677a66e1ad722f7585ce8434566bc9140a5e785b6d116e140b2e686c3c2d4ad028d817c1802e59659556370e01a6f62773dbac67dbbec24d46513b5b8e3020e94952d78b404b196277445ab367ed49305a2b837d3cc8c1b912c8a4651f8760cdd1fe00b7e4e418be8a4dd94d848bf58a1ab6080349001ab2fd2c023803a9083668eee92949ac7f770d5ab0a1c6522b3b38f3e8899fd31712bb0e90853496a584502c0465c0ddb898d1c5055cc95c2af76f52c7c974dc135749251420d89acd89df5a26d8a8a590951ffafab0a9e16147ee72e0cc20ed49b649be2248113cbe27616f914549c035d549f4b21f249f76223ee6c7402bccbe2922d9fd785c28c736906d62e50f1f4d499325ba3a82761422f7ab002f5519cd8d93dc184f959d9d49c3244a90709a31700e1e95c7a5ba99fd51c76e6138dc14b37d697243b2f02e758ae9f22a0bedea596d7850f6773cce34c7ba109863c4b1c873b20d40f7da64999b89dbdf73d29bafb11acb590c48cc573ad49db4480eac1202df2acc03f92e61cb8c3eeeed5371097b548f126f6b384cf6c277e9ba46138883d9b18741de5084b811e1b79248cf0ff73c30b12dc22cfac511cc3bab24434ad42a2aa30c408081a7204380a12d904b83caf2966482f12618a94dea3b847198acb9877429596bcb33a449066139cb5d1d5ba8761f859dc943d6a72907f4890501fcbb398d9c03208eef9d90c06a1f64a271feebd41177184de371c8b35a860f8d2d24a90a75d96a84a017ccad5a6a23649d9ab3c3a324371918e03b808eff47306ff5623554c86d5db1cfd22fb6d0d6bfe4334ac9c4018d9c1c09dc42f89493bb8eddac69f8101753f98240179155783a78f6c8c2171f07c634f2d576e89e3b46591cfb6303fd0d8fd4a5e13446fb806ce78d98a564c992cb07bd1bf7034dac2793a5aac67375d7f73aab53ffe45ec997c21eca60ebf1b2cba31950cc761e0f55153a4a8cf039adb956db303301647ed849921902a8cf1b58e25575bb01d28b13af462fccbcccee3b0e511173b44583c2771e8b4cf390732cea8895ba77ed671f2ce6a0c7e80ef9728de0440caf1cddd0e5b3f3b40f07081599143f1eb59ac063709db8243704477b0ce25086c4fc45d08d67ecc042fb697feae34614efc211ce4a69a4d1257a4ccda7255c186dea5126db517d6b43ca74c6b7e0021c408f984fa48c24ba6d0521249dcae7a354457a8625edf03328a85abf489a1748e8588e72e819a5f78051691caefe8723b9a0d068d1fe2c6b4346e471b530b3d02a3966df881d0ad6dba266dd7d069aa47e475d14a8054a9926b7cd1cb4082e9026c94a70466824473c260bae799832b9aa289609c50339859658f023899a806bafa9b51c8d0e29dfcb3f857e76ba1f578a4dd2c4f19e870c54ecbf8abb5e4469f8d09b2d09349ed3b68324d7c1bd97c8f227b7ab4516ec5be0ba4d07cc76cdae16ea74b63deb91f5ca6c857e9fe2dfe2f6010f1843055ad10610388e8b6e4334fcd1aa6942755094fe9fdcaec0c2bfa56c4516a054415095037ea0adc8802e85c493e33325382654fadd817bd1e7e814988c129729ff619e7bd8bf1f146941aa2216af7972b665408f6160957ea8adbfe439f5eff78566f7794e7fdfa5b45aae103ec447f6f1c1973f8abe1adf06b54f84e000aeb7f61f86afde584882ec5513e388c78897a63278b3a9003702126e5c560d15299d923534028da668b9331c190d7fd148b857901faba289af701ed7152f3b9a03961ef778b080a1502261495aca6121ce7a54c5c232166cb6bbceb448bdf4ccf66d0be4690a825b424a76638934153b8fe00fb3200a42d41fdac1100b4b469e3b663f9ba81380111963dc2a78b3c4301dd4bef4f7d3faf9d4e7e5fbbf4f552eea9f6fb042e7f085b34f29fe09bb7f4b6e7c57e64495381bd87968b71c34f219d4274ea9c621f2fcb31a3ffb23b86a7333551404080b8505e40327562959c56e366bbac2275139b32d34619b7456e009374841845299a6400ab222d8b79a0328d4e09e5f5be19088e4846b7d3473bbd4230ddb70055c46d3c13dbc9aecca9eb8da1140b34a2eeedeee86ef916551446063d49f243bc5cb3cc8a1118bee0d0c9ae11a2ec0eabe470ca6901ffddb8e6d2ecdb7234d86040bcdb0e3485e4036b574a164ce8d0194cae96e72ce5a982ee2b24a5d7866d3d876463759cad58125d2bdd40588a91cc0599d7df391c84617bc7897037902f49af1f2aa28c36a09adba57b40d8ab3eadfc4adbd4974a78af19f3f0cfc50cec978982b48fc90b4f06cfc8471ba6f5186c3461f2f73912d9f208324707dd26c3add3e0c5dba40f2033b082af8ccdf321f2ec8321b8935b5100c08607182cfd2c62c49e6bb004a66b930739ffdaf4a523a1f529537b1033df340d2c9589151bb04755fd06d48616336654bbf28c7264d391a41c20f01bb6fb59a3782cd0d7882e456570042d32fd0391b64e1a0b608cf22f409d03d9ffbbfb664f5a1628de684da0449c0fb2aecd4f3f1e01c9c5ea0cff00fe27dbb73ca46709b536803d51e18c4531ed878b1e0b3d36bc650620ce54d37375093145158a52cd797e6c991fcaf57c954ea4d830b21ff735fcf0cff1206a8bc23d22fb8069c06c172b3dc047c76a761ecfaa644f899de24760f227f37390c3742292b06109b12e35d6a6fb8beb5ef402929187617e8843f08cfc8bf02c1258d0cd3b59f8a4626ae699d01732b527bb1b1defdc52ee0b14bea08b099d34bfb0dfd4aecc086068ad638668143a19bd4b08e6317f0c09c638175cb6cd59911b4176c64494c20b57ac8585e74299c0d417f36dbaf0cfc5ca2e428f59a49b074933c6fc9d4e2aeac3865ea286221d954e996fa165bca0030f01d4ae9949232977bc7bb9295a145049402474bc6fd9c880cc661efe8b96db6e58e77b94b62f81f7a8fcd0031007264a63b930f8c08c4a51f221d4a195f59df36d0e2bb5bd70ea52485ca419790d6c028ad6671f24c18a52813e94d04a9422200e8275c0264960ebbc5f06e55facac12dcaeb34ee339a06fbfb9dc20c59f2a69261ddbe1052e8efaf11f762fcfd4292b67992adb1f164f4a50f1666288bbd1940e112dc88a83980cadf5d9a1358a0bc6503436f52ac2442d5ce85c54fb09eb349d1e96ce3494c4532e1bbb51b25d2fc7e95a23a09d0fca1321dca5ed1a4ac6dc769d2f18af40ac9cbaf375c166534db56b6e92b90d7c720d552f3656e705cb1956427aacac5a386d73f8a7334e32175eff4284c35915ac3117fcd82b47f70825d0c04ab44ea091499ec94efef532130b718264652baece4c45890b15aef8b971fdecdee38f18dd79637f4dd11f6b9269a2a61f83d03944a2e3aa152d8156d8b8f685776b6bdc60d18c0a91e204f80d4958ddfe389952062150bd8c662ff2d15a5820b5881806ee8cc7c5df5f0cfe8054c10f562f4d83f2c7092afc696d3241a9000657dd49b7bdab05035a8208cdf411360530990f21cf5f4b74808d5ab623848783f3c392768e2e0b18ba45b8c46c7ed424e52e51458f4c77ab1c64f647e3911906d5a285e75731818183ecd2c8c84de408ca656d1a3eadc2d568e0b443325b8e0ce53d3d8f1ffe701f1d836b15cc41d98088fb0141c103d2aed0289db6632e8848b9ce1371af96e06ccc242d35bc28018430aac6d9a95f61892f00d143fbb1606014762a5a521e11a40ba8a98784c42c55c4cc4b5c344b269198cf58539c24dfb75c13837697cab2070478c5474887001b5b996383d76170d2fb340774c3f803195508b7d2625212dff8f2d47c35c122919b51e778291a9dc1237ae7a39be185dda57fbcc7f10ae56db1786c063c023f9431f879e778f1a4a91875d73d1a462b3cf74ec44d3290e074d93a19a60c10410fa572b48830a9e0aeabc711fea19db8283628560944424414e5f7d354d3fb19213b350c3005e6f6cb835c9ac6c014d85b355580f252170c077994a22a15fb587fa75fd550bbf810b1ecb875b6d3dab6305b42ab95832e396ecdc5604db305a5603cdc978d19fbaac1a66ab1b9485fd1e5b38900b2d7986dd74024cf44c831739e3ad5f74b8ffa8d80695920487a3430cec2918f3778b05f07c876d8056133b3ee0dd70922435968353fb2bf74860416056c8d08c563c227b23119324b3efe732fbb61cc06ad99c5f6aa5ee4cb9e42a6d1994f12127701be244b7593112f15e84a2a74d2b61e362b87b920d3700f2806123d2f5dd4a2d6bb5d81375b8a19ff47b84297caf77218aaf6173cc75f808799b8f113106f5b94968f229fea74088d9138c8231466e7add9e94a2488d5f66ae7df18b79697af911090a263370288218cdfa00404bc17d498a1246b83172a161790075e9eb2f29a7e2592fca2cadef1157a909bd20cf53d85dce7714084f8b967317a8f60393205d9548c01dddbe93a0a67d8aa79f6af8dd265b86f19f97da1e51d36ac7638d27841200be394e91722ae411afc74302af774365dea321f7cc58c0aaf00b70fe7c66f6af3db2394c733501c7d949c9b07f6b8f4ccef6d1c46815c45ce394dc072ba881db880e898bb16415e89d14763c9090576458697c658ef809c690b333ed1ef9cfaa52460b7cfa02c9e7e4e6ac30f016f86a3bbc6d7d58637dda04a83c1c8e494be13d2c2d3d2be3dac563a621c1a504013ac2e02548486caa5cd9c9627d1f1a87e4a840744bed5e45a40d85c296e2a9de972e5e388a3156304ef8743204ccf1e038a5359bbe42e903ccffdc1e7adf5fa3c7c406b48a159e94018745279a2e38374eb990aa411baa17081888666c0d1317969791a307d088f83af3b3ee51cc0a4dd678cf5fdef27f2091eb8db84c3ec23356bfa0984f3a808133cc223f55fe83dcad6ab57c5b3327ae457e1e31eaf0c75be6d226fa84d3c98b8b16a5ab565d44953bc88c5629e01c5e2a3852928e198e2fa28553ec6cc8dcf7eb1e67984938fd0926d3a7fa0f34e23aa03c32b3c13b6d8acb1da65224c0849a844c04305c6265d0bfab7b06dcc594a2cacc4331d393314c61589d260375e9684d203b1b9e52d5223f2610f4501e8c231aebaccf01f957debcf2168f3b75461e4270b3208e948df3333020f970163836ed83110ed1ccc77ec168b914dcb7710bba2be00c3708f33423ea171a0025fb147684db4f6b9aac4d8cf9c9b37ff5e2b62e8672aee098c3b5684a4c205de0882fabf299665a17d306bd3c32e6e387d5e9651faacc671c60d161487d7f11a6c00e942d63c99b50f37de4a1f1e375cab42b92466b002e094ca12c12c29585cb8a18a656dc50d954c1d8c08007f92c0e40514df5175b155ec4dab33f365bd2bb51ef239f5bfbc0f52dd283959dfa82fa7e137f9288c112a08dd94e09493a58a071b742ba498579b2f7b9a85ce30887c66e68644e80633bea583ff7768a388bcca66bae59b2571f11f18188a408476ddb4a84f13c594e6f6394ace46caf3a593a81d946a5dd0b536fc7da6c34020bb664808830ff43d43c5aacd6a93aa3f505185e355270fb78642e2c4f086b18709bc9c38e39fec8934aa6d007f68c4c45492b8dc88c4edfaaa057d885fb9f37d217410cc2bd413f32828e5c65508cd9bea2d93d70d82cdb0b8a2f3152652dc368465dc8c6a18cef66ecfe06b53d483c908ba3ee0975eeda0a504755a5327bc0cddebd423cb8ae2a35f2897280d8a383231e1a4279f4bc8a884dbaa9230836f83e3fc74d0b76538490a241daa2583303f4766b40161add0d45405cdedd8d9bf0f035f16dd6dafb33f11c670df9779966cdc5b7b4119e15c5a3ef6d06fccae0153f3f8d60b4daf6b36bca6d22cc72dd24bf9862bc6c1d2289c5fecf1f3d33ea21a8fecca67400bf72865fdd18855158edae46044d12ced4a27f46fe80da805c18f060c7785d86c5dd9ceb76bc5ce9c74a0b5a40d72da3ae90fbd63b706e4eb729154000718b49d775df3f7b0118206d81061d56cb080e02a1316dc9368db909092b0f13dc59791781d9718192ec5e6166d0266eb31175133f9e49df046f829cd149944583295f4492455217bbde8422860b9fe3b373df97bf003bd931a32e786b0ff97e5e0096d1aa19b8268d4124245f50b1eb2d4a7cf8c571b66acf7dc571b0829f36a78b6da2e89cbe020227a3388071805b8b26340a26cb96b5c51120b54a5816dfa01a303f8eb1734573ae4e7525ec0a607ee674a5ab162668d8355ed6029c7d3830280fc3c988a11d13deb5eabd183164b2abf9cb1048f9e6e45bf823871e785e2d064ec9aca8a342f8b4c25f9d5da8a0902f65d557fd8d0c332160105301f894fd76721518c8819c7c5608291f9f53deab02e5f3ff8660549dbb9d982eaaf1e1ef8cf2ca37dcc3f35cb844ccebe37c637feaade36c45641a89533761bb2b4feb2eeb403dd84396ef190b30599550eef8c44336e8d67b7ec42a1dc4bfec1413c725a6f47933c23f4b99817a1f210fbe18b18ddb8997f90f173972b60bccb49a29bf9868bcab55e8cbb946a27cc7760c89b30a76086bb95eef3db50963cc16ce8ef4c21ec507decb3bbe1c6ec1d55561af8302383ad875cd79d469d7befbeda24c031712cc1f743165a5270abdfd1565357d2d5d02a9f8df96690173190fbef66db72e3bb22140255f557c9587e6d8b096b843e84c08a95edf13422ae0a6d8893b6cccbd352ddabbbe2084b2660f34921ae2cf0d8e5c5d1d334495afaae764c8370092ec233ea0d276a80bb1ceef915dc50d23f3aa2407c8d176ab5863b8a7cb7daf115e95196952d41a19c9013c972411f7cb6de5740d1705cd2fce82a74dd7792ac0d2b11645e67401fd99a39735a847012d04b1eefbe821e59c187cedaac56e0ed225eb2da559dd89519593ee763ca1e6e7457af54ed978e306fe35dc90127653ea921d7be86b31ce674f484d777f372e504692c916c32293fae7bc78abf1abd9b52841950a76eaec7f88618bb9487e848355298172babb8b2d46e4a7c49db7feffc0ffd44a29451e6db124432e2e315dfd051107a0710cded87cbe7df3bc1dd379bb6bdce30bc0a02548f95ca5a320a9e40a62168c1cac1bdfec337ed977a37bc00aa76609fd365de9e0915e19414c035e1e834036cfb75fd33d7a010f5a428af7453a0c48314710b1c1e862df8cdc4316989a25f8bcbba4434f8ab3822805fc720c01dd86257e676648476fa95c11c4bb008d25ecbcb5a4c39d145b05914f79473278d3330bb16e8a6ae76ee0556e29a265cd33fc1982f57c28a67ed45c099f0839c10e1ddabfd2b17db4f225ad62540d58acdcbd5adbf5583d629116d65c272ec14dfa26ad9579f752d96a91d503204290377513c4ee4d5649506d41328edfcdce7885a54f4736c459b9e11ccce7818c46eb3293c63ed57842b2c5b75b1b6b92adc65c166be558b72438539febc9dc7b8a1fed53b60c19928a5c08e0568f637630161afcc789a42266a4bfd328ca01734f29b5684925a0029c81a2be5f06b9889a64b4da2734ea47d4744097c39b8ad0e8fe25a51c10aac6d1e8968e07a8c3782cff64e852134924fff571ee2c34b81b08707ed43520199546866dcaebad30edd80b389d017dde306fe9263f4dbc432100b122165949438a3a43c9dda056cee124f616add386098c66e38f1d0611a1c958f898887f8177fa15db3fafd0ee987199b0867d1ccb2beb8b9a8aa44f72a4ede4bb2875e94786ab202cdc046305394fbfc1880ea539880bffac7654c6fdb4f5b46d1f5aac6bfb02d5fe0a90fe64f03faae03af8d3f7431a51cbc86a9188ddae6b5e626b21dc469c98064d286ba14c9dcd8cc62dbab9c21fadec41114a025eb7f8b77f6724e2a58b06b79fb960c7ddb4bf9bcef1e8843b8d69597564b98ae16ad5531810e4bd164c48b56d78ae4f6c8420676254148f7fe62bb96471cf1474891fcc70ae5f943d8a184da8f3df9a19db84e2745df12284e2090717fbcf85468ff250071cf2841e0cb5a8d281037a59c4ccda1d463a87d2c01301ac5fa0118fc348f2e64d0dc795177ca6cb0d23a7ca2037e36b7f0439df55fd08ea1ce65714a99f89b7e61a8e39692c360c2aae4e6433d8f543739bd4b7459b76e2552bc9773912e8b97da04e8a802d9179193944dbe109efd4d5851674553608c43f42c4c69ef46f04bb3620a34bf26ed988fe137abb6f74a547ca974e67c367f2b17a02629e1e836ba7e304023e37649e898c913071da975e3af0eaabb15a9f79a2848f20f1afead8391398cd0cbb8d447cb32079ba37c498f2478d9c1bc0dc5fb7868c0b0802967103da317354671c102348eb0614554d960acff501684945c9e41adf6d772748bb62c4d3e77e56b2a5ca69cc0078a3015887f7e73ebf05b3df2351a40dd90c1eb82eaf359460b0401c7755f87624de0ec316af001c892cd124df742e163090e7057f4fddde019fdfdc7c4518b49446d66e6e2458fa325a3f2896ff40fd844bed76f91d52798d4415d84f0cf3c030db2055f88711b27cbc3c08f7a4e59bf2697985f2b303610aaa1e185dcdd4bf3cdae78c872edc59cfd4f5f44530a2c9f5f228b151fc865717da45d8a0e55154bf0e3eb06d70a7fda6194c12a5d15bebda32293f5452ae9ccf2fb0f810535b1ea9d5e8f7b24c7cdd7ac994c95231f54fb23c50ed592388e58125a67654ceb71970bc26c468bf00a0cce4405d66c57af7b1e0a610ef560bbcfa7cf280769bd3a6a3f4c311b1938fd7e6ea0644af79aec46077231685ee032533dd7443137a47f301bcc3058e5585c61057205c914d3ac9b0611b5fb6f3e4694d4632024212c09152eec2cff10c394c5c8506e0d55475135a83070e2c06821514200065e46fc5d981f0ce10e6f551e0ac28a3929b81f6924165de8c5a3bd41a216a8d10b2f7967b0742112f1047102e4ff1ee623ac245bf5ede306f1393975c22ea4b0e5e22c0abbad225627eb91281caf5da2d482f1152ca8c24b9be9c62a024f328c930ef66c995aa3b613010cdcb056a9de676f84a975bb3cb9d42bc3b7db0a5a1b9318d01bc1ffb5ddfba5348bbfaf2c351b7a0616bc462a0d65bde3e4f00ef59bf57e6df96cb431665d6b7ef66055bd994c1fa008624f113da20e63ab15a295759235ff6455fcadab546ac11ebd65a0b84e230033f68a7ff416640032d38cc1ab6dd94558ad5725627735c174dac552c80ac8f53767542100c8901196418901073bd2e01b4b229a38fa3b12e3f0450d1d32f16ee634016e59d12162dce21b93fb9008e9dac93f58bc3534827a45fda9475db6b87b4383df1eaf4089080341413efba3a334fc45cef5628343fca3485c6fb1649077b0590e63437a47f5dc65a37a4384440eb3b3c089abff011344ef31df0114572689ce647e8f01608c0d777787d071bb9773897c41dc92f4c77b8d688ca2cb30e58521a5d447b48afc3776221c544e4d0b80e5887064e808cad7e19a3f938939b062e422f63ad8f606e1dae8cd1d8c9363d9b90311a34b02d02eba93532d2228ad10c4a40c76c3502dba88db458af00ebc76e9de6396ac518c81ecdadd255dff2720e541e75329d9debaa3c1113afce9c95810c3a887de0452c04b48b2e6245ea6d91ac2959316d5439c5b4512fbf1b33fba05d7421a50c7bf97118a7b386815aad2334ece088046088411384b045ac6fad7dc1b43ed3467dcfc01c4c1a5828dad5afe2bbd1b9de64c574f5ad60b9306f606dba52a1a85fb69fe81795c95cf7e4859878e519b25082a158cf286cca4ef2d8b3af8b1aa98b2a13c0d1dafcc005f062eb2eccf38179033400b55a6fe18a248774e47085100338ca222fd75fbee86b3d9ac2148e18d0ba78f921a07511b71175e9688d0cd5a66b4c6c4d591be9775318c9c28811a32a72df1ae9d7c422f70d1114986000c72e3a1aa7ac8df0e8cd93b9720949a8014f4c7e434c998cab4016626042055cd022074a30a0750d09bc4c8c2bb58ee3388ed3ba895bb34646306e45ba5d846493ec11cd47fbd1863422ad4833d28e34242d493b4261723bb269413c3e455a4fdb22a421a15950ee9665d4bf9f98504ade512bbcb8d7735ee972f7367053c1149080448675511be5fe16542aedb3b45a0b9e089b4d6ef39c53bb262a13d35752fa6ec0cb6f5e75b512c8402bc14fee3af9dd68f5a5db3bce362ab921bdbdcc35a4b88b8c64130daccf086affe91758a20df901ed71589f598247109c59bbcfb6a846bf6acd7648bfb87ecd9927c77dd6da227d45b9f4a58bf2da2f14140e757a1180a3edd74ad60860ca9ad4bb289b4c9369327349d36ad49899794b74c59ce251fbac3123fe31d775c5dbbaf6bba1bd7472ee9adc966ed3d67e39505d74d14536b9324f93eea745534ae9d5c4c4528d72745215056909a5263f7977723b2231004d3e763fb94d4a4071e2f11cd6f02d0e0ff6ac5379cf54de55af3f1c25957b7a0ac7e92aefcf3ee563769153ef1937bb864fb1d6c94af0b8b29c15e26e8fc25928ac1085591e219ecea713c223938158a6c223283319ad14b91fbebefbe988f4eba45f73668e03c113eec49ed09379223be9d96c36a3585683c3e35317c71de590ca45a95c5411085a9e16db161581452a5bd4451303b58458a11e0e5b2042b9673d9bcda8ea8e3d4433e1e852ae2dbad688354ab92537ddaea70b72a303d262bf039257b83f41a0d6e96dbdda5b174891da599f5c6fcdf3ab250e0fd1f00f9fa43632a45fb48828a8c0041da4c18a27ac701e30a1441349f880800420c775518bad696d64ade59a446b236dd4a0ac083c78e590769257ed2899bb8eeb39e7e4e991f9d42223d4eb25e12cc9fc4eb37514066aa1bcdbf4973a89c715c0c3dd3efdb2b854c1bfcee45a648f3abd3e2d240ca0fdd8325917d11a356c51178907f1b85261a969da3528fa557adf0ae5121e21704ea8c56e212deb97f746599496bd97be1c2854888375b4cc610b851156c80af58b04db21168a7ea53050eb7766f0cad39df9bb337bb29ffe0c9a3eddf57163661c3203c9c00a57bcc0067098819008b880075b1c99410a28a84167ca8dc5115558c1c80c892d6881060b04a1054832981da10d5cf438f5e4791857c662ae8c752622e0cc13b1c986341015c5ba09d69e59ebd35124b073cf8cb50e448901c45aef9931d747eb23d3d989e914c911ef7aeb32e774cc8575746a20440b9e9878f9853a5a38ac6f5dc6441c56acc375f132e6bab6e85a23eaeab7ee2401d862cf108045d6c767825c57ba921e810b481900591ea10b3c946a9a566b1db9b17467b63eab15087691cf94b5d842e657a4699a01340c048222f5ad5b6b42aab847eb3385f42bb4b9e219a4c5f619d2ba52baea0d99eb47efc823f310e2ba673e143665d4fa589fd56aca7c2c9122ca712048ed10eb43613deb99f5e959bf8bfab5e190ca34143983148c925a90c40522ac60820b1aa440073038c1b54536092422c5134150b8c1139870c54e1129465064094b241121c1490d808480032daee0794217b0d053062bb4e831d28521ae20528321505841c408028e202951451bce1044e68026dca008478650e1c80c35ea6d68d5ced04dafa8439281b146b93faed822e9ea97fc9047fb2bc0366ab18d98011cfbc5ad44b29ea1d84e109688c31b9c48ea811096380242145318431247104211fac5ad3e42530cc5baa8b1c83866965286be3565ad2993d9238b648bac5137cbd2e503243b2f3d7d205bc9a45c75d40b616db45b6baf0f2dd63aeee46edc551dda6f098f3c7249876aafe118b6da2ab65823051d051a369b7a43342edd182d42408c71d3d18e4749a6d7512fc4110474d40b41613b4d1cc10a21e0c00a25184921566f53ff43c7518ec3150452a464035aaddbbaaec3a309ffe0438b75c559ebc30f518040902e99f31b76205d325b1b5315607d67f952f10e99d465a1d16dabb7f5da476beb6dfd0edb739cb65f1ac7713d358fd29ab977e6f0b8d98de28437ec7128b55c31976dbac6e172455d92db4872a238d2a2cc3b79e6d35fdaf93ac190b969c4853648a1096c58c36c89981ca283236b0881133b08c21790051648e8d18112238062a69bb260d25c39dab3a9adb427adbdd9be53b3b4ca1a49393b658df6b6dfde6976da4b69ded1a62592c204435a68c145902c878820451ea711f27c8c2c8ff0862cb46bbe481d123653832b015959ce48980e1ac393b02a5df35287c41b96454690e41841884cead1a2113f5e008e0fe80204b9b9f72d75db0c8571dc2c81320329d2f54c8ffbc3648de857e3f11dfe6144bf62235a9cefa103b071d276ee86fba603907b8f1f466c5f01766af7bea3f7ee33a8c7fd614415c0399b4d6c1f67d066a2a50e9a4fe4b95d7e1ac6a9d9d6e9e34d9f2023d0740538d66c02f0086f9822a7005941891c3861055708926690a2062e00021741d2b082d87c0f237828ecc40a232924110532e8a00b151f00e1054418b2041154109bef61c49077c5145308cad547beca7eb27c1522eb902cb5a22c6faa015865143691384e5669f02387aa1086206fd8421256e8c102b4804513a690042de4a006f29a263f9328acbedbc9a9d7de38c77ec33aa08ea4ae9d9d1cee1ad629d2d7e972744c2f7d27ec6ff5da83b0dff011dbed357c44919c0d4bea92b73f425257bd8c4d2dc20e1b8708e0ae3d08ee1af7204ad7aee1234cf263c0c673431df5610264ac3e0cc28928f020092d82a616a0e9dd7f7e78a68c9399a800ca5878748526b658c3cf9cfdc81889125970a10c473cdd9d43d425b5004be75ec2617f2726a96b16c5a6d1bc82fb8c7197b1234cda7118c4f650c7f604c8d88683d08eb0600c308001110f4fecd425afd3d3b1ee1fd8aee1b05fbae9e3740128632916803246a120c10f9ef0842c78786232d6e1233eb0611e1c36c619b2a12ef930d49143b11883109e0cc4610850979cd13e639a8e21130e73340e571fea92f2291f50c6c220666cfb8cf5a02e294bb8caa84b7e9449dbc7f6910f1b374fffc8d32d8035a8ceaa5035aa4796c7f65899f5b13f36c8ceac90d6a3c93421cde7a7220d59a3a14a247df258651acf9124247bd4b59415ccb363f6cadc18696e71a7162d4a6da85df2520eb5285f65597ed486b27cfda1b00df4e0064e0cf9a9410d881093af41f255265f7de44d5300b5226ea7768a083f796ed71ee6681c75cded86391bf89d98fc48c21bf4f46110dc76ae7484f6d2b9f14777bfef0e8a431c55eee3dce2223aefa0974b5081d155657ae5125410a4872acb976e188456ba7619e36e18c411dcb7cba13047e71b16a254838639dbb9735847a42eea9ada3ced91f9080d1d21254d990f5292cf114a4461f247a87f286cce9e5c8f8c6679aeec8f6d0d06f2a62a40f9697790c8c11744d08524d880441162f22285a582ba18c111194c503484983cb8eabeca92526c9cda9a94334440199331f909018e358410c39ca9b3a22ef98939ea9245d4b50570ac91a594524a29a594524a29a594524a294f7d7ea494524a79d30c9c2600c71a59fef2353aa98ff451235959a50205844ea0040d78d006232b6881133438c113c2b04419a2a8020a82e429a7f7511a218216b9a5a813fb9879fa40519435b0ca3fbe332701bca1dc53da00793623e4de6614072d6404da4e089db2a90a705ee66dd6aff9392906f26ed3d30340ee6f4e4898ed91ae7e3d0f8c82a2b03121f711720b21f7654abefae824b2c69efede45b12490b0b62a7809e5eeba65ef86f3bba1f6ef3bb1ce5e1c32afa44c38768f2cd7eec9e9ee619deedacdf1fe619d1fa86bded3842efd16c0719b6ddb4cda8ef05098fc099a51580ff10871434414268b8a38a3a059c9a85f12ea97867a8eb8a30d89c264ac88e8c747d693bbc4933bc908d82098628fadc477a333eda1b0eefd964d1bd3543465f45b051246a5205dfd991918305c5cc23c52a2308fdda3ca63f7e49636e6c1f6ea77633b9202588fba15e0587f260e11d0b10e531a0538d69f2a45bf68307f66206bec5743007b90fb72e620f7a6699ba6514a5f850f0c044df60885a1febd4f7de84f2e62bedf5d90b05a5790fb601a78c660c223c7e1115ce171953deb1dc96dbd3cb19559564b279d74d2ea699ebdd70304045d695a6bc7ed666e332d486ead277797794c53f6898e45647b44ca74ac933740e90417609153b274420b92f277e316c1651f5b1771e09977de53aa03599190325ed515102177e5c9d407262119db89f5b7a18d682ba29b91ac99390752a63f2f0d9a32fa1deaced8140188246b6ce4a641421476fa3693359c51c69bd0362b01d85966f0a78f346876a3030888c3799d93609d0dfc4e0cbce13c8951384ff29dd85138718880d3c1e79c0e7e1ec43a4526ce014f02629dfe090fe5ee368f863932cd1df54e57c6c02b63fddde8d866d4153345016c1e2923d3f7fe7d1e493ac5a622b016b5d8591c7d9bcd5b8d54a31693fa35ce6d8bedc8c645bfecebcf764461f5679b6db36da69d087b5ab168b18fe48d8bdc3c990392fb28a4a4dcaf3f14b6813568057e75542557bbbb7b9b5157a3ae8c4d1280350b5c8f5aec0904ac582040c64e979f8eee36ad324d00769a37af29534a6f0bcb0a2b544959a19c9894a8522420eaf4799da9c46d9a94913254ca48192933815030143e75f5e9da1975f53714846910a5415d94c7cf6d1604dc84b6274c5000e93b296aefe636dba0c8dd6d4fb4d8b6c7ce68c90a5c657af93e9a3628b63db3b9206b26b64b1c019c9f69089c1f3d8d01d847d4d5ef3eaec43cda1eb18fb8b18f2c8d75d8065197e909e0b8cd7a72b7cf2c77d7d9576693134abc6da7d4304840ab949c04e54afef3630cfb69717b2f98e91674f265b4324bb74b29a594cbd5540528b3890828a96bec5998d3ddbb87754eef3a88bae68f8e501eda4383e88c1651237a4491681285d12818b1420a7a84c26451e5e931aa4714262b524da242b21a446192caa80f85c998e539524434447ff2fce97e573601a402ecd7d3772626400fefc45a8b3e6aa43c6f620238f691cb09212534d18534f0a08a1e3c3bc8810da4e88115d8705a343b15610811520cb048818f4b14c6008621f044e102354c898532d8400838e0020a90b680035218a688821186f004f9c10cd8881f5700516eaf84370465d62996f9069c8d32293f71729b4f4e5ec72ac4c78653c6c3c3938b9879fa00c1c2ab2e80dd44bf5cf038b3cb65b7aacb53ef5c6e37713ba84579ca03badce5e6051be0e4ded0fe5ee6d50d2dbed1e99079c5e766a5b2742b57e63a5bef2afdb1827b802f2dcabf45d93221d46bd55e39eb26ef566eeb3417489195d6bb940ac0d6655e396ddda4b5d26ae1182dcae7b4286fc41559c4c23446fcb032b05970ce17c0cf00f41dbefc76983c97fbbbbcb4de6ab5d8fa3873eb2bf62777476579bdc5618e3ecbca9d99a57582c7fa22663e39cbde93d7953bf30bebce9f137c3929d3f2d52d499996d35ba78c96dfab4d192db7a12ea30773471e5e0ff96e4cbf4c5dca2c535ccd3ff7e5f8bbe9a56e3a2d9dbe7413ee9f2cc0b17db27c577bfeb4b4f45b3ef6cc7bc98206594852a208464bccb0084a83142818720530c87e07863a68681e06719ad3b834f7c404161481831344787862344ed3519a4b83d2d0a81888264673e5670e9657af8e2be7e1e51396133cd67c7296d33bce8ce3581b90e91099de874e8cbcf26e56d44f7cd01b2b972c1db5b22ebf1b14eb2d2d1ced732db7667a79783fd98102a990c90a20eaac1797a2de75a85b4f6fb39ca55b70b8721b8afa917978f98485c2a1acb7ace010c7f4ce2f725ab4b5da6e91b9b6dc9c175766890690e784e29c16e5af0e8d0b70944542e8b87c7788d3afaf8d439cc63c78c8f53b99070fb9bf93eb9d3e93488bf22c770a6971c8c99d3f1690421b9c4086239820073129ee40082160a2044928c117a68cc8884924937868e1712753faee20b4565abb57ac2dd55aab6d7b57deea5a70fa54b6b83ac563496ea13b280e739cbc4f6f9ce091070f99f5febdbd7775c79ddc6261396179b73db9e307b25db932b3dc95b3dc95dafdda5808dbbd626df7ca6dba576ed395e576e5f2bb61398b6c11cb16652b2553269b128f2b348024ddbd44773a27273e683e19672eb9eade7ebfbed653563dad5dd7fd57bde9714705d37bef55aa7b55bff75ed5c989102740345f752c77a5e5c2bc6badb5d2d9336dc8d6c719248fbf7d7d79fdd80a7263e693cb86d1f8c8720af395fa5a5b8ecf3acd0f9a5b6ebfbab245d62ac504c8827fd0cc5ab929264018fca333ebe3311e2516fde1d8415f330bef606135cb0b5ef9d8c23f3ab37c058f128bb9c4b4d18277b09c75cb8277d015fed1f9b29c7e402c31653466a927aa77254f45c5e45d7734ee0eef58efcb8247afde1d2ecd695c9ab75cd60e3b9ce6d258edc07ac92cefa8b7c3a57196bbf296cb3acda5f1f9e15839cc6d39cdd5c1a37165bb4630af74e8b7eecc2cb76fb460d66d5a87eedaa3b934ce729b6e5d9959b7e51dcb1dbdcc721ab7b68be52d57b6c8f2f9dd58394b4bf5d6edcc724daeb272471355906cd31b13269714e5135c850fcfa27fdfbffcc5e518e6d8767dbb0fd342f2fd84942cb7efcbf2933bfec827f2cb514f70c8bac5630844cbdbb2304e0b962d36b6f4e3c953eaca29ebd4be597ef26e09591887054b7c7293d6595a6f966669dda659708a09d0e2a334ded13a0bde01f33e0f8b7978b9556172a47060706df1057ff5c6a30b962de2d7163155ddabae8aaa54f6f544f5beaaaf54ef133cae547d15fe57fdf77d5356b75abdf657af57e9da2aef6c4ff1ca270e7152a74f9de2d194593b580f6dc87a18de9175128667d1bc05b39ce6ca76b1e0b1b2b0dc95d3f0293fb136bcfc70ec433cb24ea44d5b8b4f2fcbeba51f0e3dcbdbae9cae9ce01daca76ed32b7764e1b06556b9602eb92926afdfcd0ac51b79c894e6b03ff909de4169a7ded744529955293c899080770e913fdd49d4a2fc7767152d4a359840ee43a610c90394535c21cb7228cb29a85094bb1e928b2c3f9b48966f22fd6afce31d24df83c274f4907c13c9f7ac85e869a69fb7672dca57286cd34b69a64034b9650fd9a345397fe46790fc94c94f9f2ea2e9a9831e74610ebd610ec5218711303f30776637a023a279b29de5facefe54fb4361ddeb6d1085950ee41dc874fb43611e36c942799394efb04e118973ba9bb08ef7ee3f4d5c29e5c4405f4c5e4b44c6ec4f7dfd64252b6bb19aee114572be9f7e8477925b59ae07eb57f5b29eabe7e1f5331466e2c055ae62677fc09d1c0f87f3a577ef9e537af71cd3b99bce9debb87bc4e9dfbb7b44919cd3bf1f4172effd8508f04e72067c3f79277912df4f31eeca5847324500d6af28ecd546aee7a49424b59ea412e0cce17c7719f3de3d08ef1d3ea2480eeadf8f00ef3d88ef08d4bf03499d3b10d5497e64ea929c6c10b6443e8180f2a3fdd1c1a1c05b7fea4eb7bb477d273613ced373ba9f3e7f3a1d8575243d618ebaea753a2ca98c9d2475d52880a09499c72133112b2933bb9fbeebddee74e50d73be63aebb87bb74c71a595ebb1c9ea12e693b3448b9aaf11a9c3d3dc547d05bcc05a11d61af49393f72f61ea11d518f802f01da6d9863af7134cc99b65e3a9b1e75f265dc8a6c67727991c2549fe14ede3fe1b81f2159cfc673e4075994b5e6c9dcbb1944e9c9e4ae6ec955f55337d4be13a31c883a79d7ae38435d54ccdc9c578032a61ac5cc1d69913bc9b58f1448e6ded1abd126c140b4b9e82314a6038f9c8e16b9c7e8e56e6742dc6d1077d5b5a12eeef60660987324cc49bd9fc256465ddc39fb9339ee56688888c26612924da23029f3f909d2664214268bb4218dc887c2b86f3f1b11f72d88fb36b347b4a29f19e36eb91f141e37a1cc5dd24d96b90f8f5b4fe63c3c6e3c99d38e64cec495f0688f32b799fa5ba223e2c7cb688bb49c6d15125b22fa63a5c854467ffa34a84f65d46762131840c91d9d3b0aa2861092ba40f905d16298535a618ebadaa683f882d061ba4dcb8d7b69be9b537b09eb94ce618ebab4f797c37dc33adb39acdd515c713a66a8cb9af08abaec915a487634d60873b8c9b3132bdd0dd3db3475511795d11915a246f4a8f2d49e2aab3ef5a7ceaa90edb1322b647dec0f1285c9213a44617396bb8f24e5a64554c8a528a02998325af60570de7617872224b8a2dc9f72da30d121508920b9933b4039940417a5201ad4ba0ca2ae964dd73cc781e09c41cdc9caf3f94cb500d29bbcf084e6c9146b432d6a4215077306e0cca3bd8150e5b999da1047575383a2456d4813b24fb94d4bc9d1151eb93ca9bddad0d584b4a17e1119d2a0d086b421a1fa9305386a429a9026a441d1af216e0b4bd995c7f991dee436fda2750ebc3849e5e1a997b9f5998275bcc032e7c8a1580b379135b9f4e541607d0bc91a2021703d045903d403c65aefd32359b30559837a9f7241d69c640dcafb15095973f27e0581ac297900983812ebd72059b37abfe2a0e640d684ef571dc81a95bf78bf0a51582daa43acd71048da05599be8d7ec897ed1a7aa0aeec74a82918e21b77ca469c8bf41c761dad0949832c820611afed86ac82e1f1b0eb9df58c81a0d0bb2260b6f2dc89a526f41bebc20c3d030f8058b4a4c1b5b0ca68c1e8d879ec75c1fdb08b9df44900270e19e55ac75173c82b8270b8d1aa809dd20f796445dbd151551182794c2a3c9c526c7a309d8824790058fdc0aae452cbcba33734214c6c93899102713cadf6d77956b7a784b4fb9dcc6ad5657b92d65569f974a71757b3729ae4e6f27c5d5391985a1a46bf55e7df591f3c97ddc44fca8b97ef58ea6bc9432eac8299888cf3cc71d1c1860b661dc7141a678d4912926e2594a994945088090ed77b3da56279d4b924cf2b8252591e024144c4e6872439883a2c58a81c21c54175d74d145ccf59113ca3aae1f0182d65d380cc2758478172e322f63e2fbd3d17a373d978e172600c1cba1e09e38156421b9391c7881c810285090fb5b0ca68d66c194318f8414271ca4d806398f4c36489979eece354c35cc2da4cc14af519edf7e28ac9390ae5902ae57f0b3094da43c474d28f79b0909e3215d330252c67c0be48bbe0d798b1ae4ee429e97d12190c6a444421e2790274040caccd74b81fe0bdcba1de38a5e9a7582d93e99d5af9e5fcd1d13b34a56ac1e279d74628f36019ab2d06ebf83ce24f2bc77849e7328731ec541814899f91938d67920f915733ef95d473f2a6fdfc91b437ce918b7e97ec5b0337753ca494fd266b0bfea4baedf50748f34122f621d71868ccbbc8561bc85752a088af86028a6f3e21f28b27d27b6e3ba0bebb8665a8f790cd6c9e12d1ce6c4c478ccbb4a048e764651842bcc605868011c5ba6c51c026a7308f84d277091c536aaa2034267b28695eb7c028aae059d4c5621571574411d13a03de570f79305f0f280a6244abba08ec81070ecdc455d50581ba1d8a445ad4466008ab9dd00766791fbb036d2330be0683a92fb62d67207a45b1a4f6e1e59238ff48b0bd905c97d5bd4e5fa6abb90b02993aee6447082dcfd516b41ee9bb6306941835b5826419641083e4f98c205454043d27c225701cad9dd148b3108e99979c8a22018a0d806e138cdd2bc0424ccb2ccd2e33e20567ef138f3bd78c31cb1675500398a89df89b96e98e37aeb2dac03d38398eb3bb14ea56be97ec051c200ac9f5580e3948dbd0670ec64ed236b66bf64679f7e75ee5c5feb859836349ed82891729d4d00ed77538ba60d7acf72c91df5abde9e36c21c14093e284502cff6dd543cda571caf74a33280f640588c012f737847e9f43b2aee39dad1b301e89989b5de2f7b580bd776d5b70df3bca30a97667e4d1b3257af8e9e1db9a959db0599dca6c157dc0549eef6a66943d505b54884526eda18b9735386bd7d373f1c56bb1d77327d697257754d2507ef585f82532ffda65453a9aee4763d3fdd05a53a1319c0393f03cc9c0251524f79ca55d454823a8ae4ddbd1df4da9fe396ceddf147e65eaadcbc46774c1c12015e3b8e9c32a60c8d7ba76938f3aa8317c7bc4a95b5ca7d95eab4ae9b1f8eea158ff57466eed40b391c12513a771c8e9ebb38f3dc4b17c7c415d71671b873dca457dd91fb37e9375bd25ec22926400dff44f10e934fbca33bf534ccc3cb26d8e2f14b75281412cf432929b94d97dc59a2dd6e252547f956a27178442949555309361dc5747a4d55858272fb303491413b063f7188937a7dea158fa6acdaa17a8a967a4aea2929d76e29f59494ab60ad975ab782af77e4e171b78889f3e5483de5297854953e1c33cb0f876aaf77864494bc1ec7e45cbd098ec9eb4b2e0e13fb8a2d4efd7cc9c5513f67677bc77995b71612a1dd1e879b32c05b1c0e07771cfb7aede298a06a9c988797553885ca1c5e30abdcf1f9fbea8e3f504e5e2af5eebbdd3873ea3d6da4febd4bdd2e48d733bd009a3e76409eda611fe3c9f2cb418fd0450ed2af14c6809777a00ea4ebe9d777d4bf37c9bf1c5d26319de4df45a12a0e7168173e68174959bb0fca4d1b1ae6618bf27801eee305e6c70b943e5e40f50b801f2f50f2f102261f2f70f2f102de2f80724a4faec9e47625d703efa7baa7d24571776aa8d38b7a3d0a756bba18f03209e6519253160aa02cca3047717171ca4ff0386b6adae8a229a36b505e5f32c99aae06f57a4fd6985effc99ad4eb4fb286e4f52859f3bd1e9435a7d793c81a95573cbebc7bb9262eb400613e9a8e988ec04c1ba929a3a35ebd275b14c91a1f264779df4641d6f828b9f7beb542d6f800dfbd6fa5206b7ca88cc81a1fa54f41d6f8984fbd6fa9206b7c70f60a59e343bb91acf1617f7adf5641d6f8a057c12452a6be7fbadf4549997a927b9232f5a9fb49997ad3f5a44c3dea7652a6bebba65b9232f528979332f5e1c9ca869edcf8de7ee5b242959b821b2480ed311599cc008e9608f5d21159437315929824299ee4ce38cd0fcd33d3e051029906889aed65fcc81a9acf93dcd3bf59a6c12d1d057086ac5f210e98693e827986907e853968c61e9906efa0f321cf696ebaead29c84e403baa18949baa9e0bb09d25c7e37244fd1a6f2c3a16f1cf6d41938eca149d1bc1ebc639d914472431e9a191f6b0d7bfa455373f21195ebd882f2f194ebc892ebc72f572fd72ed7988f61ae26181f55581f4b39c6c7945ccfc91a92df2dd71125d7f124579beb689253c71f4bfe91e63aaa727dcb1ad5eb4892eb98cab2a604e6158f34b7ef686ed8130269916709f059422037b53437b60b793aa438e6c88c1bb3c50878fa35c218f5f46b3e0412068131c2f9fc6733327290890123c625be68c1bcb8e09b0303691e2590610a6f615961852a292b94139312558a8400d886dab4cc3d5c19f3e1ca18ca12e5f953c8539dc8634c52cda670862c4f9c1fee8c2c378ecb638ac9f450d7fc8dcb638ad12d80a14ff813068544615198141e61f1b07a5832960feb8715c43ab2c2b312b4d2b3220b8d5833161185499e70160acd9067c52709e9c8885594c3a13cef03d0cdb59bcbef467d4c528b33868b3cdf75407212b9a3df8d8f19f987fdccf4cb61b3adbfb97fa25df3b316e7ac46490d59fb344b8e4ab2a09f5dd3296d3d492903e4c363b668713e2689c2608c7cc0303149d9872b633dc018853c797e8431caf3a12cf40979c29e972cd61a28d234b05a0e02ed9ab5014920d2103cacc522b84a92424784c793e96f18c48f998500a94be2e8bef2284f50cac839e7e4290a9a090d51983ca2477a36df45443f3eb29ea47a449bd0c9977a679ed286c793651639b4326fb7693b4361df7e907e452fb99ea1e4e58b7763e60efc614ea2493489ae7c0f892b08a4ca24118e8489d455e516729551d82eafcc2285fdd0b8d60f082af3bc31b3fc0f14d6d225af7294caf2a6228c83337da74dd927ed61878d7dcc2c4fc4ccf247bf2406e2e87b4827b4a027ff90466ed0938de8c953396c02472e574a29a53d654fd9264527650c29233f8300cee9026983be739f881f3503f18441cc58fd8c55dce3c5eac1fecc210e2af75119a8031573b3f6e55aaea0932f4d982ac7cd39e9e424858da07c7771fc5ce7140efbd4bbf6b4ff9aeec538a8bd2997c21c25dfbec96cbaf6dda0aaad003a5ad4fe838e0248a2ce6bb1ce0f0727041cb9cdc424e59bc9894fa882b2627def58ae7d78b99bdc3064dd0985d013b32984e61f6a47b3ca95f98730654bf966a2d2039fdce4f50838652caf9fb3f9c41482a25f21d1a953b9db596e4ab842b97346f30f5d46a1b96339c126dc6d37ed651567cd517ac96bf4ab8443fb9dfc19e08638aa73577d27975e2638cc61b2934d2e73ddea14c286ae2b1c66eea69713fa98954e7aa2793bcd1b04fa15e26c57b90a1e59df7088a3b29377727d03fad58016ab8ded2d4aa0228bdcf23077d5b342ec607dc3428439b6870f714eae2b0ff114f2ed4ed99763c359790b1ec3afcc198095759b576cbef1b1727bd6955264b9fd32806366d657f00d0b5729c382c7f00568b166160bef08718803011dbdbddbeecc33471461fde29930887b7c8c757662178bddf6ddf8983477dc94b3397bb22aa759058f1fc82bcf610d1e6c7ce897caeb0a0487a0ae7ad4bb09b2d4cfd97c620ab558af7227142dd60f513924994b3c6fc333089e3d6f0029eb256f408bf536ab9c75c392571e1e3025054f21df5e8f80e3aa478b2517a7f2f0645635c153c8b75b116c97c7edc106b074d3b5d103793b0b4b29aa5c7e3966ae5246058f2a0f71015afc70547088f3ed2a58470e0a6008b478835861cee2808b24846cca2dddde99b99c1f286c9ede492214bc5a0358fa945d8cafcb31d6a94c0c21e6827fb18e00c2304b8a6155a944e506d0f4438b95d657ee3bc96af5bf688cc23e1c354f998e025cfb28895c8270f99c5fcecb5db08ea4a20ab19739d790841804d1d4a59964454425a2e67125b7a8d63e5b1e707b69fecca0299b3ee311d9fe5e7c59e94e9f21c7ac16ebeb16a0493bd630885f3e2f5f2ebf2040ea9ac7975f8ecb5fb04e5884980be672ee31d6914da0e28a59ec62495d50582bc3e19d6ced73a2b05e4e1452e492cc528a9c28a098d6d6296b57b5962b699b3665510cd9a1957e5185204d64fa91ab583b58b79ab5ab7d395148118514511469395114d500dca2206a51e66d1c40a6af16ccdbb77501eccfd38fcf61bd8e07a610c4084f6c5ec6fa861513916391288af5736a0ed2d0044f6c5ea7037198c5fa3246b75aebdb45a3188a82a856abd9967500b266ced3be9645275fc69a942b48a9890b6098436fdb766bf63c36a6618b79ec2dee1975d56f011c7be8a891286c26f5c8284cce846a0ff11045c18815521851982ca2471449a8be0e2155a2fa5a447d68d25027d5f7915a6bad3eb9ca72e5c9f53589071dd854d612adc792ccbdeb343c7259eb39dbdab7384d9dbd4913afd65c95b266f6ccddb79732dcff6aac55af45cebefe459c5debfc6a5e72dd4dcd0c355e62d4a85ea62300e933fd3833d6984d4105146c6b9676c0a12ebdd3b71180df4fef9a7a37050370e491c792ecddbbbda75b4d5a004779b479dc4f06a05dbc217f78a62b044c550087a0408bf5a62c80a77b2d7a1fe5519532d6f33ca09e7b17883e468bddb661ed875195b793481b079832eab7cbba71f7a1c51f8c80dc71703e66ee7e8069030152468d42aef7e413727db7bd7e37362d568e03a5bddb0f3e4c508816735aac3f5c21da551bb0bdca1aee1b1ebf77f636e00a2185919ce3d0cc9de6e8cce12a65b69f3e79b037de735a887e7dddb6e1d297e3bb35c4f16eef61af08dad51afd1a2d0ff655dc708d162be67c68577d6d9795db5eb70d8738da3150927968116cb1be468dba5ad52142b1bef25bb558bf2d81b48223520861084f7c095a300ac110ac108445ac1e02435018275d36a70e118ad1f795b16e7acd800e831598488af5e4f9dea069da535ce9158f3f4aff2a0e21fa254b25cad2216f1f4be4952d7247ddd328b1d8ea0cc299b03079f65575edaaae6cd73cfd80182516128b799bae7db0ed1b40f955172495f23fec2bb598c3725e1e5e6e8ebeeb5a69bf60ab303abe64fa0e085724c504b85d6c917e9b38468bf422877b6609dd103c5a9c736e9eb59ce5386bade5b68f5c0feace9eaf00b89341262761f3a7f471cb76b4d246e9d64e20f3676a585aad5ffafbbbf3b77ddcf6e158cce259ada890860129a24dfbd309eb5813b7e1585cf2d50ff4ee9432f69d894a29a3633d90351c9bb7226476bed3e9d7c87dfb8e92b51f0e87e7ab467b6a9a264b272965ec75faa5d3e2d43eb1f6dd50694b320d1754f185182579f2882dce4b2ef2d469188f21a610f336f33fcccfd4987d2be8ee5ecd2f474ba618a8be3ed1c9178ec2bacb4b2790808b3c4e1ffbe9438221d97e0eb144ae4e919cd2e924e9e91acef96ec23adcf7d2bdee26edaad351f77bdf221aeeded8f4f66e69fef49cf953e9f31ace41fdb3475d759d22397d15d62992737ac9fe84af532447f5c63aaa7be02a25925cc6ac8ca9ae8ccd2b63a891cbdb1db9d4d529a28304ebccd8e9881f9e6201584f4f38e573844e9104c818c547d0cbd8288d80cc03a94f62c668919cf9c63a454204f4bbe7b4d6d337619d21a8cbbef1eca12efb898f20f5532cb07fb1dd4e6d3b85a22ca7b8c24896535c2145c6594e71c54f66c941460ef7f44aad6d5993d695b9f5ceceaf65adb5d59a98bcd5bafc6aebd277ad3bc1d66dabd5aaad9649ab656262d26add5e931ceee9f26b59af7eda4f6f5f21545e97d0454fd621cb25c0a1080a7b28ea8364e90255efba136f7e2578dc91b95b7d37aad7daddb665a62a575d7595dba954aa57954af5aa52a9a8542a2a55a57abd2aaaefa6e426df8deaa28b0b28bf9893937f46f10866fa1692f663fd09ae2df4aa1f4521d36da11663663d7a2694fa6edaf51f2e8aa416a50f85359176c94dd641249de727d343f3a75f32979f4a98534c1b2d9b32e4e5a70d24ac8774c9a35ec0b88c71f94944d6c82c8b00fe60161941b08df8717ba8011c6592113d7e80f446e7ae42cac8779e21108dcf6c3d209acf1f1a580775c9d9cc395cbee3216b482e2fe3f20de8d7cce587e817ca5ba75a1248314eadbc53f1fad6cb9830af87f94e9671eb591a8738f7e597e4bb393961b9e3cebb596b3d8e53bd2cba93e9fdd19975f23eb90d65b92af7be88293cd6bcf25f7cc79d8c4ff2dda8dea1dc8e7567bc6bddfe6d87a94ee912d587a3749356abf57e0a8acef6575656ea0baed9d6df8bbfd795172060deb72f9f415656f01d7fd0bab2e3e5f62befa6b772f919e0e5c33f3abffc621cc27ce52c03e0d4f7eb57baefedefac151cf68d77a4eccaca8757c2708564e5e00a0a8a1718982bf34bf76fd7be239869eb18612df6b9e3647de52bb8c795b3ecc53bba7e40c0e0b1f37dfd3fbedc5ad6570e735b61f08efebdfdf88261decddbcb0fe7aee0ae5f6ee73e0caeac83f79f3775ee86ef6eca6d68a924448b3366cc58f98c8a979836589d4a0a06a0c53b4e7edfc730fd3ae53879cf78bde38f19385c793deb2c7a983fe5ca16611ea658477d38c679fa80f072adfdffdf77f3d6f20a07faf4cb71027372faf5bd4d7b2bb7d57a03fa757aabf525a68d995b47b969b156eeb89357dedfcd8ac5618ed3ed719c6eed090ceb277727f79d4b4c19acdb50d66d7de59a9ce85b5a5a5a6caf8264ba626161d158eaaa572b9b666961b92b7c65beba314e2f8cb7c85c7e37302ee3cefc74bfb35c9ae56780ef33f8c5fb3b99e57419a8ae754d2eca9d3e58d4cb2c3f21244e0e9781735acce18638dce9533c966b33c9e577c3740550e616a045f918f78716e5615c1f48ee3c0f2923af83e38a563a78084161327694e56f670d0d3177661f0ae032dde5ca1c7365de868052a0dce5f2ddc604985d5d07826006efb8ca282fa1bc7e3738b4d9d545d9f58eb33ec0ecba4d775de7fa93afa60e8ff6215d58d984425d4ebf1c1d98420ea6f0935d2df7eee8a34776dd74975b44cd2ebcc3f4968bd78764bd6bb97482b2889ac71e79c7bd318236cd523dda2d7764c13f6a6ed635b5cf1a40fa16f9299be821fdd3a25cf9fc6e5a40acbcf57b477cfc150c4c0beb8585f17db9cc302f2b5fc13b5a5ff9ea8e2d5ffddeb1e5f7355b8adf77bc63af76d4b364bfbcec35cb592ab52da717c686aeeed88279fcc8ad1d3e7432cc65c6d707cd3038cc9129e6412d0c30be210e7e1fbff161600eb37a3759b2722db6dcda22beb7a1303030abdf154e677c7fcff5ebe4f7a4a5efd83aeb2b3f39c123cbb86a1ddfd64fae6cf1debbba238f1f79e5f7acdb554a5fefc7293340eb2ba72b97f9be3f20ba1c528cd3faca5b5fe571e5addfdbc23b564edf5fb17807bde98dbb09da2f8a77b0dc5ebef2f89159c69959aed5965bcb7244a61e20424a99b1945b64a6b87f5c0c30b34bf8096217165dbd65729897c3b8bcf84cd9143287f4e8d1a3478fd28f8a726f787fd4f0de971ff547cd7f7d398acbcb57c7263030b8b57a6bd56ab55aabd6710bb75c5a3875dc37d5618c5fef791a0b52527088e3727a971497a7b83c2525c5e52929600faed2d71f35a7bce4f29414b0033bd0e547cd2c9c7294db50ef82b93b949750cefa6e70e8e2729b4699e28a2cf2c9513860de82bbb1b6546aa790ef66e5606be5f47b69799d36566fb9366ddcb79c9335f82d2fc91a995b421d341e86415cd2c034a771f9eee4f626ef685c1a4b83e60403d17c0a6991a667351763eca1c0fa537f562e3f2e1ad8b978a87bfa015fc053104ced977c6981a720c89911b81ee4fe8b5977f1bbe9a130ba5a8120ed4131d2ae7e17f3dd4031aa263cfeb09d76518c508c681f518a7217b5d8f5e7c323e7618afbdca59519a508c5088a51bf64092ee9b4574c81226cdef1558b93f541c0d485abd4f202e38197e2a90637445dfda1d34d1169e04d1149ead23078ad64925ac904d3b27164f4794a9de4a32cfaa941256da2fef47cd1a016c7139db5b6b554d3eab6594e2b8141f39e7ea44c104e33c6931178904f33903690329ac87d13d77525cf337d5f773a953c14ea03c1d2898404bc40c02b01c68028f198c94b6850bf28518bfdd5eaab75a6485227229346bfe49e7e7a871b4cd7f81d8d27206c4f750908d3d53fddd30de6a461f1788a224fd786c7d30e729f12f5ebf4d32722b48adc4f059540958aa4a4a494323129a9330afb88a88bd293d43dcd48565fcb4a4df4a4642a414131f14eaafbd51ff0a87bfaa14414769aad4e3f292625aa939386614e3f3465ba292a66ea09334585e9ea5416a62bf5859f1491514ec1c8d9a712f6b8210a4b3537f4a1d50b674a116929a21f868478f974b4be1baf55fb457bbaa44b3b559c22724911bd103d6e08009926e5314544448f3add74204a70b1d2950063aacb8b2e59c35dd6c068b2a454c44c58259a321a48a5a2a5567597ee3d0149992029034489c74e369034faa719c81dfa239a3228ec84034a94fb24c483dc2727686f95e49e82205d5fd30d8fa7a08abb02a96eba272348191e4817907459950a9f82205d7d4a346d9c7e682065fa23a522f7ed294cc68266f92494eb0522b9e902a56e495e4afd7443a0c3c31323b9eaf680b1d4354ecbd179c3a79fd415814e726bd0999913ea9e88b4ab0fde53137dca9d12e0fc78fac96d7374aedbe907bc1c90a42ed4f68149d84a7bc3a7d8e9e7f4236ba490ac914b38a13eaf439950256e4369165529aa278d538a48ca8c29a21491155648ca28eef9fd1ab7d76faf78ec6fe73aa900505291c450e6be5191c42c6bdf4024c09679657a49bf647da9a45f1a1eb79b7088a3bde4b548719448728c10a445b963bb769c4da660006e7887f6fa1940c3f2f257560ad4a162f4cb8a2dcacf17d8a0dc592f060c259b27006d9025f2d91fcd6ed9e211472e1949998992c2b33f282294142d4e8be5872abaa82a50446400ed479414794a812aa230d30cf4509f369403577934cdb6a3a4e81711aa8a7e494db35f8e96edf6dbd476c81635bcb298d505283f72b21b7209f9840402507e34cd72e759a5b3253d516c9a4999d92131cbf3f2a6990d02426c51a2923620485c3d9cca43394ed6549ce6c9f4146b44cc3c6ab93f359c5b44cb7af24173c5b643c2ca1a79834ebe0041aeae5679c6e8975cb56b72d9d485fd4cbf28ac797e560f71409e4c0fc628e141d6507c63668bc116419e4c714a0520789e0ccafe4a4a498728e10d5918a184376091fb3f9a8bdc425dc8a32793bc4fb09b808459226d9b1843ee4bef9cacf1b277af5f40e0bdf777e3d99f267ae6fd7be75ddbc4776d90f7d11221729bb67d9a7bb4d85e6e1bfadd9a89d826bc56c21bb6f0b80fc7b3433a20d4e57783ba4ddb34787be8b4ab8d90493d7eb4d8ef41619e97bb3d237e7030e88e8301d1649d36a866676dad1bd716edb9ec4919fd8a479d5cebabbe4b3065cfcc9c69a80c539b91a1b6587ad799ba528993693748aeb5564cbb4963d0cd3929a53caccf54420fd2607d8c3af9022c39cad6274f61e463359e3cabe90de0cabe748d87f5c9f6dab52f7dacb684c2789833674ecd8779151b1e6ec0011c25914d5b9ffc0385b1da3559ac59b3fd1ce55692ed8dd1e27c8b33c90407706469afcf356e3409934b204212590c618311ba7041cc7e2561262b3082177a60840a245011b397d29e93d6be01f4660f1ed6276bd8db30e70670669987e05167b2e6d9bef6edadb53e99a5695ef5b21d82c23afc039d1608da0fb8218678021c250ff9877e8d609635e307b28907d379b0e95769bbf5f67299fea803788b130d60d5789ed0996cbadbe9fe52cfeb458afd7122916727c9983c79361093939457c95b3e56d60a487825b898cc593eaef264e570204554503ecee439d680f9c88394224f3cfea6de72e73bd6ede8eaa6de55afd3bc960b3e2575d645bddb3c94fb5de59efe72490e73e7f14de570c5779e3777543c4aa35cf2586eea2b24ef78006307d9f38ae4eef35aee4a133130c7c560c4c0208a6c913cc4bcbf49f92279f7a7b4d1bd4f254ac262f494711de46ecb0187835290c90302bd1b7425decc6d1de67ec7f7f41caeeb291775930bde062a1748fccb057a81e2f2c2c4eb3ab0069f478395a77249ce72c1af5cd467105ea09818ae9877285eb7ba40316e22832e25062a300887b03091bb162fbcf305304217e46ec5077bdded5c58c06a793902f6c797918734ca249f6f0ec61392bb963773c5c35cd4f105ff7b7acbfdae82fc72815cac0bf4e2e1056addc5b57a71bacced5e78e1059f82ac7281623c06c6b56131deab141144a5a688022c8bf158eefc8aea2780217381142982cb21068ab912ab5891d4bb17af0b2f90ebab981817aeed82f118efe77081142179087ae4f013b10499b5d202a3e42f82e4f002c138cb058a598981f1935b848c47029911cc78bfa9f7c810018d8732df72497eafea2932874030ceba40312ee3d676c1787f35cfb50062deef6478dd6db9403167b940317e182f8171d60a8c77335ec7ba4030de42720c14e31d8df70bbee5a2ce724f5fb9df73b830de5dd605723dbc40e2552e50cc53627c7581504e6e115c36c140310e5ee6f67031f19d0e20d8018997d7dd22b89c7a2701e07d89acbaf3925cefc2b831e01551ae2bde6bf3e2b6ae0eeaaa1eee3e7bb2c68226d88027978e0169942dde5171f55e9a2585198ee3914bf2d4e8bd169bb22806b714807f992dbecc378f926cede7d56a7784a4a38215f70ae43aecb5d85c6eb00637046e3374829d098fdde5aa9c9235be78d2351f234f2fb70c71283d8f926cf14eaeb57253d45c2f5f7d9774bdbc64f955b07bc2a0eb26d894528a80aeb55a6a2da5b47259b61343d98e33d9ced0d9436514c669efdb9f19a10b5217140dc451d7764f5df6f68d06501bf391ace9db2b66b21d67b26d5c429232f6363d5aa16c1fa35f618e7e8fdc629b6653246456d66383f46bbbc55ca6f29d0562b4b2920a87c70e24657bfbedda202d5a4d03477994e76165dbec16bc78f1e245abe572b55aa278d73bd1e572892f5a202b77cde22c29f17abd6bb7bb1dcafd05dd5c85a44c5f6e24db0eeef42377ef1ed7628a09f07bbdf67d7e1a96e0779bfe3010caa3303b4371f73e5ab044ebb87b2fdde35a1c3913ab7a95daa1afe918682f21d3b7ce923532c5040ccc847969dd550f91c187d382a9ce64cda5f6d62295ae662b963c71a98847cacc12b00a200f9593d6999431fd00818777c5d699bcdbef7b9fe34aa757eda6d75bfbeea78ba31b226b78e42ee777672e8d3fb2566f70df8ec30d91ebb71ef66adacc5b9ba60c5b39eee22869dfce5d1cda372d6f58563cf2e0e179b3bea0e44404f29393698327085fe8e44b96278efb70d9f2ae4b691880f529dd29971f8efc424679d735f5705cc98617788127e4310138c21801dd1cc688c26ef0ac321ca2433046fd82c1a25f249c1ce2ecd015f63549d6a08c7596fb152535a69c8795659477a6348033a33c05a527f8a1909c9898befaa9e4279492300cc3300cc330a4a13949f70d8198f1a6298c33a38de84052aeacd4eb0c20484ef30e67601c122c5ba4c1d5a6984e637a637b159aa631d1e01413a04ae31da6d3e01d25eff3b0b291621e5666728b4b9e8252826b8b261ffe2a088214fc280df8d5f143a92727ff48beeff52b5ddb940692874f91a09c1e8584a4a2a09090d0a090ec641a7a9b3abd230f2bcb611133d7d730c423c96966b0d0d09cde19b26c67d0bcf6e4d4a9c927a768f00e92a3dcca32090e9fc2724754504e39eb8e1d48ca299f0a96200f2bcb45cc8c8282638eb47019c7b5e1841ec8961744c0a706589802154e9860074c007bd3b440aa9fb9562fe469119235353efbece5a8b31f036c53210a40e0094312522637334295924b0a9b395d366c868cbae64bdea9dcd5c39bf2d495917be6a85df33366cc58dd98941bb3451f95bbe9cdb83347e19dc9e2ce20c9e49b5b63304627f33364283366f8ccc03384182151d80d0c4c52bf6a86216b312629260b982318a37ea53e0f8345bfba130a54b954c19dc233334877268b2c629062b4b879e3bc73bcfb6545d1504cd214ce1093446137dfa668c151ac0717c41cb15c80e3cc1110013e73349345bf90648dfd2ea58d99eff2bbc15aad62c2540acfb8fd8c4ff0a6c63b0c4999f91930463058c01c4999f919d9b421a50c92cfce30421486836f403007c711004b0a239455475a4cd9629c59f514a414a414a47088ac9979de9404aa5437f7e1f6f07a47db93c790a7e6112c814172c7123843569d2869efe1de685d7f37dbb6c5bdc1330e2c880152ec668a400612becb08923528414e87d143614042d0c33c0c190c21fd32f93c0c9f7e9534894a51ca69eeea2597e42697e627b71ee5cac8b487d5353915e2d3c3ad31942b93618ce6558aa64a519e33405a9c33411acff4a81ce579131ac031266906c88cfe9c213b31b9333e33438e282cc7e75590288c009f5739a29244613d7c5ececc66642d4e1529628ec81a92cf4a4329cd69ea55a45021ea571593d21e4e739c12e094d2192133ddc3f56126296b01ce64d1e29c39aaddfd4e96ffe12865209ccfc8280c07c31851d7bcb961ce8d155310014fece63bb11ede03d6915c5011f3e13bb11959bf4af9e5314932e66524a53ee3b397232973048e3046a291949991753342fa45f2f9199f182d4eb1c52a80238c9191949934a647cacc6301ece1de87bae673dc13e05e465d3f2dda8f29482d33297306709c391a6766333f315c8c31f21893240307793e75c7ea849d71c7ea842909ac24ef09ceb038c76101153c38c74981ea34389f2547293229333f9a5e72faccf4d3474a55a653d377d0abb0aae423a5947e9f2a31210c568c1519161e5a02700198c0dc01601b977c725a9229cdb18faa4cdfb206e59424d354a67475ea3ac5e377f0dd07821f6832e10ba9f00c9f19424ef74f7c66a3f439a3b16c330001048007991830625ce28b5b29be5b2e9958a14aca952b94935b29ca887b498faca12823138d94aefe6e54349724e409792897556f999d47d6e8f0799abbc3eb2c3c2b45fda221feab15081ec1668e60e0276b68fefc5d1d5a9c6600c71920343427007c9c91e5c9835cf371864c2649caccd3b832e20f829c8c1f2933df65e8f668dc92a3eee9f4b2e49923efd3f3bcf1fdf23e8a4bc8353e7e1693f41d59412ec132436400c719202440fa35a6720f781a50cad471278338cc414f731a3cf6c8f42499e21d5675fa999b2e3dcd25a832a96854180887e2dcdc1e9ea234332c897dfd0c4b42ed67dc1413a04da5ec662d090d2539b994bc98c09cb4505eacc414d70c16959810062b86aa6545c674ff81d872243272b849c9c48011e3125fb4605e5cf07d0b4b132bac5025658572625272fa40936a068732caf33577a44800a0c61d4b6d0476c7521b79dd9122b1838c1c666cdc912bdd6813dcde6d9aec131c72fd4d47bd99230a0bfb23b85a1de5f930c8c750481e43599e415a4c026b7f376eba2d8dd7dd04eb8c6e7e3937ee83fc1880e33de030a78769831400c9623d7c2766d33edc30c787dffc06ebc8309820e6c37762f6279fefa6ea2ba416278aacc5a93aca477b1a4c72639254aaefb027c124b727f9eaa3b524b740907c873dcd6d3ae6c864c9a145c6672e0dac83cb0e2faf14170cac55e305005462c49a30068c1596cc2d1e4d5ef2cee4c61cb9315bc424d1d0949ce6a389498a394283c7e751856790e094a4142dee0c215d004753519e1f67c8724c520a529e8f49ead738431673a45fe38c2c668b182efac57d860c5bf087cfcfc86a005003f6da41071a33327290890123c695437cd182797109ae2ca8348c53f6b920768aa1a111040000e313003030140e88c462d17844a42a22e47b14000d95ba506c5618c8410e53c618630c0100000000020000401b0002f3fa33c23d49de8902398b7862d01a817206309ff6344aa98c8b98342372dfdd6fac262749009cd397b460af7347f7cab8cf4eefe5a340af7a79e783db28c6682d71ca4985f462312efec52ceb214b6d29efb5fbea9f8acb311e0be88d68b7281b141d74bee3ad7f87af9d8e1f70d28b748e161c761427d13d2769c1cd8d26844dacced4ada68fe3232c07ff39fd99a78aa3d47ab878d7ae93831a2e7a3717f75ec6e85d9a5d83c72e14fd6d22a62031f879805c20b2a22cd832c8ea22969301a6d84536f6201abd5d641200ba32b28aec082ac4ae3c6cca8ed0f8a1cca95874a22d4d4066db1a4d09c1635f90ee2c0e2c8d4073b00b6547cd42ec653a447f84665e0a4c27cd85392b428b91dbf06ddaa3cf21c205981fc1c494e56cb4ec873abf029bc88311aed13461e0c1460cec57763a3631e921097175b0ede5954da1411ecdc800445efe8130a0ecbda8402eb67192acc8a9aabde7b2d0c31e85b8c046db5e2f4034d515fef2dd58fe40e4c73405c9c232b0927ced98b48d6b61804a42002d5fbc4d414574f4316cae9d317c66ec3bc33c9e4c8b03901507458043cb39ff650c5b1fb52c29da78d55db441f8c919234c25d726e5071806ddf03a7313096955340c34bb43b881f22f84b1ead139542474aedb66123e4bff6801731228542a712835357dd38f7b8544f77d288f33af6b46a3d75f3277e5cadff086a5b77323be61f7f26fc237d8bdff4df8069b5ebda0e08f4c8e0600627237faff285d5e453ed443c7af5062ad8614d56996c140a2eeac8dc21b5bce6fa3e8deb185a1ab79a2b6de8f83f4d8bdfe4df846bbd7bf01df64f7f26fe29b6cf662e5009f62c10da3f77323b869dc0bad08e22c0eb9d1a017831827afa2ab1c4d08853e89654a4e3635194afef4661004f701417d66de38e04bc3b52d5476dae2e2405f66d9192c77ca5d071e002dce3d6a0eea06c928931e5637b0136d9b807163adaed66bf1dedd02ac625a239a3af9d006073ed32c3ff464c0d7481f9a7c7636de27fc00df60f7fa6fe21b6c7ad5b4c0cec5e31b766f9f8b02b37b430a6a0fe50fb2345b99489312f05fcf799ba2dfa33ae21778e0d38deefab65d30c039a4bc5b690c65519f99f20246ce536230e330923ef08df67bf54cf8d81c84b9375313a254233c7216b79beda4069f8508dc33cf7c1eb2f6c51a13bd2205d532c3959f38ac4a50e10fb07d1d0bfb8cc59b63444feb491f6f6ee85423071c822795eca7d5996b6ba587557994a29fab6caaa678ae640afcf91d5749b3b68485f8578b12e0e48e1239c396eb20d569aab5b4d49c890ce7879ecfb39f67bffa7b5b67c2666dedb162884f3381ffa61bdebcac4bc46ce0495a982fe7c2d4d9f7682d48a222344d71c8af5b06f013a612cc1efea8e56539d5cf3b710ab393369d87a76b53dd4dbe425147c06225511d351db85b68dbfd3669d9f6504e9b8af3ac1f0bb0e539fbc41f04488aafe54ca757db122c2addab0b1bc848ebe3a23ab55abc66ce44b3e45cae5ee80d044f902d24ab7cae0148a58f769bcde9a1f10fcf2c09d33cec7f03b408bdb4ef17cc99c199987bba6ae6911f71b29715a3c8cd78ebed8f550f7f64d9520c7409a4596fa8eb2a8691fcaa6084c66883f693217205f49f84aa3cba65163b117a5270e89c68a272edf758e91405877f94fea783a8823f027688c42860bdb83190bce9b1231c064802f45954ae8c110b096ced822cd892c2c346c7eb1593f941d3315822c4c0190fc7d3d3c0c9c2a491cb02a31bb6668888970b9bfa9c8ae81724096625aa3117c3890bb27c8a521b02d81614f3fa650972331d8cb3d6c4f5d6a64c621ed923698658f625efcfb09ce3fa5f3b95a3ca73df66ad6aef2a93fbe35e1186b2834a00f3cffaf45b30a4aaec778b2185c49b57ac9bd51d3bf64f4afe2b82a8c480e357b1b2b5750987ee5e61850e42f01346a8a0493ee3a63952525bc578b13012f16bf645048acbe2a0853b68b4879b8dd064eef4962d573a9ffd15c8c2bcb96ef60ec6d3edb4cf3aa445f823bd14dee4d55d92be76f3e89a82062709dbd96ad23165dd91791bb9dd57aee3abc75f8b0b46212964215c7ce4a3ab49327eefda85fa6cfbced0fd7afb02978f7c447508cf8c66727f86a433032d9011a82ff77da0ea94f024fd6c23415b4449bb00b9770f8c3190b2642015731d62033dd9f6efa3e561b7b9d5b3bd01c4063c7a542296dd0607156d207543ac34b671f8086b7bc421069da12fcd30f11fa8dff7168859dd8864d314bf40ec1039ba65b5def18868608e2f9465b1809f7638a4fbb66d57ffdd4a1e21808eff2819aaa6f3cabecfdad715ed82ccdc214df43facba0bff11375b59e57479109abf175c652b08a124fbcf21d15a1a66a43e881deb5a440785aec78e3f04e1fcae30fe621ce634f3ad91d9857aa69dab8c171445e8d2a291ab0a3f648f1978e2dc3f97201d3e38e05a6d1b95a2c208c0ff1b9b759c03135d7ceb675047c8630bd0bdf4fb89b0ca2be7d897a05730cc3a57640280850dc9da65df38965e576a19ce193107d8e4b9e4c2db58d1b9b69fcb16af75bc861c6ba6d293689eded062d6d280ed59acb6384dd1091e28dbe57775ba2f5625f44e010a21589b4d80cfc8ee46324e689723af1189bb986c6a4d3ebd5224f647ed8b015f4fba731c3c72dc558465327a88cd20d2d97aab94a52e3ae0289be4d5cc84570a5668058b886da612665f56a2b72bc7e486257916f9ae84ead31c7a7a18e3bbb4c054227078b7f9b17090121f02f274cf88cfb5ce2ae6c1a337559779e8ad99849d0221beedb508fc7118d844d93d7ded6039af9c8b464813baf6234c3bac0417f3fb674107d14324417c81c88991eb4d954d2f9b57fd0c7a43da340e48b3f051e4d0ffc54180a21dcc6423398d2650590062b20d09ae640ee4de02ecd92ad269b64552ed2fe9747827be22134c1e0fc0668caf092e23ca15b71dd4bd8098777d0e7f1af67c98c861a30347b37287492ba0ea79acbe9021fbc5eb5c2dcea7949bf567eb0d90e2de8e79491e6f5411d36cc916bafb29883f0a113f48214ccc2e0290b8fcc32bec5cdba286fae20cdd5910ede05994dfec96d6d1b5a788903e940bbb223448adef7140675d732ed25f9345964956886371630b56b2495fafc39a63f53f196e3e8cde4e001a05dfdcfc1dbf65a3cd0e992ffc6e961dd433b37320b52d4771086afca0f1d839dc939dd35dcf34b55d6cb50bec704befc0dbec70b074c2941d2e48ffa6aee355bbf16aae6309c304ee681d7cb8f2530903b1b04c00274c41e5688185a27388e81eaecda9516f2e25a32ae5a9b0c8e62a720db3b56c4ebdd7b52fecb6ca57e2aadfa6d9920ed4f2bea4ae0aec82836b623feaf5e4426587b975abafd7e27440b0672f25f0d262a297fcb29cda6910bf004c22c93e6919b3f398147c184312b49a9c412271fe837a1fe1799ef2c53af9bc4456a606690908a5a44f48a83921b7884f642755c487a48ed241543e31614f5d137be8bf3e2a15fe92b252acff320d513877bacda2d2b7897a033a603af5136f053ad781ac588e7dc095662abd6de6157b7b66db3c4b61caa322b512eaf6cc3223050ca51529b8692895c7ebb294f09816879fb51ba4343bd6060b883a32ebdb8f46096cd3e908a8060984feb3efa9fdab52afc6109ec1a832f670172f1317192e337abe4039181782ca8b95971960d32fde41700115b2f345ac0fb2aa53ba04e649ed353590a2d8a44e164a19c9e4c5c69da351e215214ff46dcd0532c80d11dfb20144998af29c2e09a9b8cc448c9f44045b0c806685fb151ecaecf2280b9b04bb5ddcfec03d7fab32b53b0170183e6f48ed082bbd98352b74d608d9fe4a2a998df5ff73e318cb2d8dbb8032e864a8340403dce5f87297c22baed218aff581b5711559e2f351b88c866439038a9f0e1b422de24f67c5a635179b8ddcca59d004d3abe4db5dd04598fcc5973a637e8ef61059ff262b25978c4733373fe6261d5384afbf599a1d75fd866aac9339c22964ee3387c8c09130b69b9d36e3f31c76e92785ee1db286f509944db8b7e41ee83f7320193fefe73930ce74f26286d2176a48362b7ef52d3f1a1a807abba836ed4350547777f191af399d9e5de23306899e7f97c6c9c145ae38780f9f9c7f50b74c4c3f9f5a6b0c5c9d049d68214a85410f74a60e990a2680acb0f90f1fd33452b6393a2e941824a94b2d42df2ab67f77da0c852a87e2896956cda621930c4e3ff37962f9e857d48d70549e63981f7bd5f4846f54dfbf98a90662da6b36651fb556aa54a02ecc440e9486339ce66539e3601623ad09985033006189afff1552c2257307537d34870640e75c4f95546f01dbcdaf034e80e282e301b0b0e365913b171294804db7d06224459dd8df6a708bef23c29058075d0639375c828cd4c14a960493dfed9b4bd3df0347c88c54c6a8cb01ac423d082e57497da0d76c159d81babae755fb1a454bb25007c2aa19d92401696552f713dc7ac56e83347fd2c36092704e94071eeb69e3a6f1d6bf1e05ee976a5e5784b0e65a34e1705478fa643f9c5630fa911b1a9a487c876d23030f6ba48242fa05decf4356059e51f76069b0dbae520f7c2f2ba66aa3ed9543c8eb272f1fcd4f0b7d43c6e964732e9f01f496daff3055321f844ef4eecc69eccfc03aa0b3e4ff98ce361480439f6cdb57b4509d55835d1a9ecd4b878cab22899963520409ec81be9cb6120a92c4a30d5ca0eb575d737805a73e7774b689d42ea2b4363c7a06a5792461771a45323ac2fb15a25e40c92c259341341d56f94a686cfe48454b9fac1ae5319a804c7a146b52b5630e2874c3c610a29c0252663c0a862248e9266012f08f320a80a0688b094d896ceb8aa60338831a1060c853f2b7aa4dd307d52485fabef9333e72b659d51263e65f479b1c497dcc050f17e5916905f9e28011e6c6308ee6019c1eb0b469a53f4f395aab47ed0a3f52205f51616e0506273f4de888124f9c20d48e6b6e88276ada21807a062592bf0aa9d4b5663861c4c586069b8208dbc6f3346995ade1c812219e5aa8c684f23f2b2df93f8714a236726b4e5d2abd1e3984775ef80aa57b77a441d53bc30ef54d7caae31b1b525ac5111823dbb42a616db6f870aed9d56eaf5fcb9f61dfa5b1c4a8b29919f4b981d82838a854d2c501db035719ddbee9e76896ffda5e8fef170fbc90c4246a288a386392d07335b028cde57efdcf1d91537eb34617f457b2121bd99b068877cdc32ecb9c32421a5055e45290d0dfa7e3d922efa082014f56080ad728eb672a4661657efc570238a5bcdc451ac6a4970103e724109c15c07c63b58506f3c1975b43805fcd93305f7023fba5e2fce7a2e89f4d3be58b3d780b5488902c3cf4fdea059f4cd1deaf1e1bb27f95f7950b40b0c14d73687a9544794cce50b583455b94e29f760910a03aac62ed7e1e602794fb6d8380d4aaa7f17878bbb27765d820444f2666307a8d0d36a61e0346e36cb29275c786a898e372018d94997c71899d07edc34dcedf8249bad002b50d094165947d8d4a347966ae13073d141a61d7620c790b55a48dbc5cbc6446e9f06f12017608af4c70710d6460d5b5230614d77a1c4705a4dd6b32fb0cf51e22445012203c92ef8d8e01ae64bb8795031473574908b05b74c526b86f268a8d4b647b26531d248612d088906163c44a68e55387359cedb3a11b412ff2a859a5a2d4bece5d0b671f95d08a192040d46dcee3bd397f8cb56fa40f57ac4258380291aa5401deebcf7623e993835048003b00ea9182bf5a58bde046f4cd12689ab052f4258c0e14d46e742ace1dcd34ca6ecc8bab13a2c8423c11f104feb298428001bdc19aead174d64a04001930cabdf7b5d878e49c6b2088488fbc311fcca12b43a749c34900a8c4d4ab799bd3f322a823d84f47f8be0bc4f221047d2224635db5a36eb40e42f8fc41e8bc16c4d768cf743319b6728a645b564cbcaf5128cdb44dfdf9e5876879b95fd45554b7d815d9e6e8c151da9f005fd2c729830aa038623e879da3e97c7be79e8d70d814c53ab747a2d2f41665f0e61b100a8de368fa2c213b72783d41ec200e93f592dd83b5e98b4c05fc3dd417513c2075825cc855146306eef5ebd3250d7ae7f33ec0c1a99f61757624cbc97a767d1afbd80cc900037833622ed397ac389473de61c0d1b3c9c10849b78403444e4d0f620c9b4751260d81a22399efde20b77782c9756ea0fc32c50311607fb8cb8a5ac95523f03d7518591a70c7246d268550e7c54bf640bc2988cbffd88c202afb09b64c77b2bea3ae67bc6087fd332ebe1493eeff43efcf1d3c568e176857bdcece8be8bd58702d84befa7afeb9ea7ac1d24b257153512a72ab26055b782b2a46eb500e177be2965d5c8d3afbf3c01aae3cd99e15b65e40b180ef73c51a30bf5ce6422d78eb815b50cef99c97e3d0fc0b0b958389a8d1fd4f881938d46e6480a1981ee55a8f6f1cc35acf4f503674442d871cde0307c449e5da8240a4a70543924b26cd77946b0043dae112551eb6ed160aadc6a9ba5e8bc66fd100b6ae9b3c331e07c648ea5d0d44ce79624c7a2e4b983f5bff0c0287783b1439439843d47be4f041741dc40a70d351ab02c670451fe6cd0b29d2d78071c6360e65818899115fd0a1b5ffb11e4e27ff9ace9f564ba07e40a1c70fc62d30f5a107ecb6cbe417fb084c3eee97b12f2857a06dfab7d3eea0585055dcfdcca23120128b8954c1bc5ed5ae5e6e0d7c950091603892e4d8d9f7e3b6da4300537db6b9ac8fa07e6a43d4c92c9c451a1e0e9e8cb0b1f30d3d58e1306b666289bea1b79585d74f6cbfd0760f21f1194dbe9017d3b7dd7f682be41144a2811073d79d060ddc4ed8728eb643840f221832bfd49b3286d72e2491a77b3346b65d8e0e781a79ef97feef2ab4834e0ae1942ee0ee579aaf156cc326b0220f56a30212226aad856904a34136137e70c150dc5569c063da5f6e7e740b08a72f8fb43b2a89d04e866e58d4c4f82846683d5632929cacd8e78c4606da1fb262e3b14d8a070662d6fd32b0b3805ca6c812223d9c4beb1480d43d9cc03c0b227d11647f822127ce06c1c2c0b9c44a82117969091d4d618470e255e0ba09b49f012813732c8b7f847b5e9e240ab778afeffb2fecb99a7e9f985b3ba7cae6d60d9ff8fa44f0b42b05a5fd800adfb2a0b9f91f09b6bf5d568085d9ed5a1c0153412c72688d48178d34d23671eb6a593c30bc8c9fd92bffa0c7fa801525a7fc5122c17cf28bdf75740bd042aa02bd5f330bfa6337f945d1ef29ab535861370677e3c84da7bcfebb4d030c94a87f22246b74d31e4536ac11c61b6edab86d7b548718b51fb19a7569ee1f308adbeb40ae1b52d0514879c46609480318900002c4db8230dd370d26b779b72dc865bb2067d388de1f8f0635ddbd6e14d570d9666db6ecaddb512ec70235b3bbc7371ac025b7a614a0927306b2335aead7517513987e51b929a3e750dabb747c447378c8728a0c427e808942a30f532e8c74116e9ca21ca623a82c91391f214698b288fdf43c467c4017c65c58edec3f35c5e0eff0392f064f5904b4b42083f937ebc098fd137164d78bd2429061793c60cdecae64311f7a002584128b4f6d8c11964540a7a502accfc66e0960e4c9d37a38b6b0df1648d2c10ab51895107fb3b529831e07fbca22e03d8e2763f3795aea244257d0e203ea3221162c015d7315153a9a5a3e8957aaec3be139015c89095d05e8dc0e26eff577f999e585865e221c692eeb9e7665e1b8fc3a607866e1634cd11040524aa4181de30a3c1336b0693e38db14ec5e323678ff3cbc7dcf14639f9690ef5fa718430a60b99f1a5225b97b4a3386a0d4cae4c3be83feb8c3ecfc55a2802ad7e78b825f26e59966c412b38b8aa869dd0c6cdcfaf09d6dc6bdb3ad5f15c90f8569d98dd1893bdb72f54bafdd1895f872b63ddd0d31e010e91763d043e0c8e6c2492ec6d69c108db0761968619752a5eac7a215b9882bf118ec169773516614b32c36e735b42222c5837fb035e4b0a05c769c2e39f698af9581a47202d78aca31539327274fba0f1671acd8b334118d20b7d44da872727c0c92543cb9dc09e9da29f09a66fa0e906b3be71499d7c10a34c1ab1b899c37eec1cf9b48ec80d84dd33d17a3c6ebf4dbd0c58e7044c5077305f1584df8e84e665673130046fabd20836d0bcca6f60805103dae644fce41a1735081cea1c6adbc8d6d7a8433619aed4b690813fbf267c045bf1e31bb6b0723502e5c829c8ba5091934955305acf80dfb614d28132de8af4ac9ea4da526587c9d74fbc9c7016da72a9a7821a12c0b7f6a8a8f7a7849996113a971e7aa40448c91600c9a6a403d220627d79df1dc5b27c4db26cfa4b95e0ab36c3bb95980fcdc3a98f8365812fe8ac97fc63315f31346dce9b223e4ae05a77453d61a57913ec23ec6d373efc3046363da199b57240fd8a808e4a12fcd9a4d681ea2aed4d1cf02a5df2c4531b10756f220e035c8e493ddf3e9cbbbe427a85d72c232cb6f16e92cc1f689954bd578fadd595dedb89c09a224c7455bb9e1a0598fff098bd2de97e08f43542e3471dd18e9408041e62bd31ee8f82db17c2e74e7677a573f87e15380e50b8767287922928114cb1f48e801b4860c827f3fc4867f1e8512196c362bb0479e91995beb97cd2018b49e9111978174282e8bb75cc11b7ea3a30129be1afc4b3b87e5858072c8bde1edd348f3d709f77f92900b53c7b4a403e657f827ac3c38c4b49a5b800140fe9a77009594587d7226f1efc74b60b382bd7e1195cf38e98f05b10355b6dec1040519e9ea1bee60aae9ee9780f5a17ad5b0cd21bdb51f606012b40ea8e4e7f801b0698ac332c18536060ad6883e7b93f29b2853c45ac8edd36c0ebc788bf620514b3f23e0127fabd6c9cc612892a56793020ed27a199e7e9ecf460c8f653d7eb853ff1090cc421d06195d02003cd6a90eb3c012bb83b716daf0510a5f1c1488aed4f34717d36742503b4d43c73ccc1005ccb6465ca9bf92071c9e20703e3ca1c80ece26c61da9b39b34906c0399f8b224c1ca59cf45130b345ac372a9ae67c23a0b514bf76a8cb6836ad42d1ff023d2951c5e6d9887e813b40354c13e6e4fbf15a10cf7ef2dad3786155392dafcb62a37fc40784c0501f19f6be0ad0fe2755b285e59bcb278a47809f1908954d90da583d20a1d90fffeb5acbe02a2fe33964f4a58fccb07d2260438d107a478f3255b1f43417e52c4f6336110708a094902111b82c35b29d8f2f7857058063343faa85517d650cc02b03aafd52cf5c992484921cf6d99b0d555e84d00bd26d56cb1cd5c52f6a637a968994c1deabb502ab991609797d8cabc350269ee6d772eefb25f9ac864605e62a797d410dd2aea08ff4fc195a89dfcd08ace06b9b2c6a2c1ff358c6e0b252aef923e4ad5af7c367f186dd70b8ca46a0152bd684065b9b40b5b522a1298555cd670bd36ef2100068540e6d292c8e6a0612586dd6b133cc3319374e47179400a2a7077baacc17d92dda731a45845349ba41d1bec1cc82230e2b51e8c47a3f7310aaa39fed6d35b584059702c7854465f93009b1286afae6a9d72cf81e9d704e4f067e5c13512e0ab8c6d9cf5f57edc39804dbd771f1ec86fd19700f559fe1da6dfe5ef733d9c18adc791cd300892bfde68378f6d1aa171cc5e78cdcf8617ec6cd071357bc9f5d2975141d43b79c0558c719d1cb24ffe2a503d2fe691039afccb1936e400ebc4fe2fb8c3276f34edb818c6a940188db178349a81cf811e4f65cf1a5a4ea2b19cb83f382e8d66b4298721521e8c4379647078ec9b93e7a0fc258f32963c4e475e828a0661e24fb94450444fbcf21c50b700392c4f3f01b3e0a71c60bb70b985892bdf5a8cda6b7889d10527511848f91739604d1bf08a968d928bc5376020a81d905124249e57050f9e2e5076adb84ea9bea8313e2089120eddfcaffb5494309451b14a9ba66c58dca494c69ade387fc31fd9a2d12ff69f840b5d3aed439fe37b22f26ae4d8bca9e0c699d8195e2c814b75cd33cac8ace0ae504d951077297827c708935b20f7af595b04301d0635e9930f8c3240d7f0dcf9f9b8bd803e5612013feadd3f363679472e2bc2b9a538821275e522192e5a9c0ecaf6b15dfaf20b93ce8ea4e19678bfa448191aa6bb9cffdfe7582f9908305629a0cf9ee6c788186acaed31b164414ad957e7b8e733d6240d7c3df00fbd5f2b2467bf5997ad2281fb69af60057dfd22c8fdd6817a2d0d775d855da8ec374bfb947e97552c21aaea486e8192c7b5906587787b4acc856f618314504a95fd66699fd2df24f52b5455912ce96072d12bec45055fa3c4973aba2871abd39bbeaeba8ceb5f5e252f2b4dd36a64775054d0a9b468e7abc94459964c918fa20ccc9c07e7a3e6e81501f89ce2d9cc5fabeaafb95a94fdbf4d448f7c4713ddc4f14956d7708d6f96be994306ea66a233c5315d327ce7f746db159b800551e8ea147e0a7c18e495c7e153cd8051b89857f2c96094ea9162135c4b92b25fb89aaca8bf8ba5f8b026d65100f0dee2e86ec3644a0d222e945b8ae5e3ee537806eff8d91d84972abc307c372051cc82a830872b94683a34a75fa2040f3ce7f4bb26daaaec80094791887a4c9f5c770b44530793a3cbfef0e1bbbc9ff7c3abeb786abc6f1ca7cfd477995bde7b098daa9cc8d30cf9c96920125a7d191d9a0ca3b21d5b60141dfd2849811c2642c5c0a9de9b3b22f98f322975dd241170aa8ee4a044bab0f059f32cdaf0ce1bde2a44ba599388be8f97bb83c7e7fa365c6eeb3e490e26f416fe84e95f215d5c2fe9444c8d5f41e21478265446e2e74702fede041b313089f43b590b085f9e2bf45347a9ad29b124d9938689692bf50a61ec83b6665885e36e242b28eb423e1530bb3c624cae0c53733e8aae78c913214d8fe0071077eef2500b753b6052443fc7dde4f9b5e4f25f904f7addaa8f7b05de354eeae06733319b7945c07a2b62311dca78e21fa8fc0aae7bb9c9185f3c8e06fd0e95e114fe33b3b89f37261e911a05182460e0eaea0fdc32a0cc71e9f910c8055a135e370162a33335ac54a4fbea622c18bf56b7e544e5b897f6758f27783200027489f2086b7b3b10ef09007c19ba2307de6383b85cf2753e559350a433aeb10ad2042b315749ba6c1abbd9a2db768b64cfd8e63fea3a4fd622bf1f625f63c5c0a0892df8dbfcdeab96649bcdd89951b4b4b9e61be8d6ed244b0298cde3c09366df126bb18cf12ec4b2972d1aff0cedb863e7af84b253cd765c8c62ee580a306a6687f357c2544e33a895bdcde3b9abefda68b6ca96f9ee4b998f791aa2502cf090a184256dad9e133fc409c87563aae4e5b0beb742857d1b8ab5f5517012cbff3b05a7c0b096e2203d4d7822ae1c24b40a7ac2af1de722d2bfbef82160dcf2c0769607efb1862614e52cfd0f539fe5cf797ebee7b85435f42a4e9e8a8d48c16f92f095ba52295de663243cd16077e25b8466e6953402def181da5aa6e2f596466d614447e2d18e0dbc218bd11f2e52889a431963a776adac567b018ccf31ca5262e62641449a7c8dbe91582fa511478cb7c2fd7b1add41466c3ef9f3b18e2d04cd6cfd3f4d3bd4b4b33d4b415e20262461d0607d0bac5032350019c8386b315b2bce4dd6ccde7fd1afbeb0649551fab8260dce49d616088cac38f24ecc01f0068c93879000368e96e849439ed90b08b4db5816e4e11c910286eaaba7d8ec4cc7afabe1010e011112b5f72adb9647a77b973fb259383cbe0cdcf2e38fc993a5a9a9318efc9657d5cafcb6e2e86030e284cceed7f3a95d4c89d71c3056300edc031a8cc038fe5470dbac422e24ba2646dcc105dfdae4a5459bffe8662fc1e1dbcb2f4a8e67f91c66aad2bfc3f8ca7f8e862453370e3725405daaea8cc36d8c2368ec2241a38604526491f581527a0f4520db08552a737025b27e82773443e238d58ba4188e6cf331c4189df824b3ed0d0b56d13cd18ca56f978c339f8fee0ba94a7b19ba43b3eecb73c489bb5228941521c70e98a142211c49b4550ced710803445c99d064905c15f059a14c65c0cce4c8b65028b21e09ba216f5df81b6c4bc0eb8213f35e1c0c64bbfe4b72acced61d4776b8cd8ee6430eb1d93ccaa27c59bbe7f80db2dc7d33f12ba6dc0cff464be9a40f00f49f7378ad046de40de95522e08ed69fd073a01688d473856cd737029a64628b055aaf043c8568b6e2f9d18f1c35ec67624c8ef04658e1cbc933198cb3a59003609c35e9677fc6cfd9d08ee80f3236dd476845776c491fb6ff38181148baa5aa70158e8c9f4b8ece3873cb0727f923e9396072c7f6d58f0adab00c77265aaabb46a8b1071586716a5c0494c5e576ac3646f0f4eb2d22f98f4118aec41112dacf8348118348f604111ecd058804692ee8fcaeac7f9ca8fac8e4dea591558ecf415f22374a6230496e9d9a749e14412d603da5df88f74a68e120ea91517d807c79bc70193431b6ae9ea308aab01ce1901c38ab6e0797f157c541cbdb83090ae3b35eacae8ee8d0c059e2876b9815ff8211650696adbdb020036737175c64a4074ba019aa65e8eaae8df9db3214c0fe14547ea89e96dfd96ab0e008ee0a9d16b64265bbcd5cdd6b69f715d7a90844ebc1629bc5f8c3ae50c157a73e62f0f9a5b853efff02eaabc1dfc3c2bba07f4e5069b15cf9ec03c52d74f71bdf6e33e891d1ee316bb25e9b86f558ee02a2ef255e682d6882ea74ee2138a5e74216f38dcb084657f94ccdb3dafe32635bbe5566a2bf5cb0cca34f2bf988f3f44a4320435a3d53e2d37bf1ccac93a5f989e2db0df9b6be000cfee60f41c46d779993082ee69cd165e4de3018df75dd39c519f7b7c8de68e45ebc666da1c41471897a5313885896fc80319a28074c42b3c55c237302acf336f3f5f0888bb49fa4c8c7953b03e92991caf0264d58408230292f584504164e7dac9316eae0c699197489178e5a4f70bf2ffbb782decb7bb190daaf66387f0c09eac2f53b5c0f8a7061362b66c8dc96cc25ad782e6a66c8c3bb704d373431b3f23a6bb4cc8d68c9b431972eabc4bf38787755800f258b3157c93c61c065903e5405bb48bc6be79692d231f802bc13fbf5918d6e57343859066ee4df6603e46ff616c7ccc56e5808095980bcfcfbf0c8ef1ec1693801a1674b881efca32dd5b2e8e76eb05fd9dc3071775a1b04fae103ec092256fef39820c1fbe935e76e9c27be0e94c60a293e45dc1252665c47f7bf2cc3c357d4ba0096bbc4cca003d36fa3f60b9b9d389cf54647b8a065735785c071896e3ac87cae6ef5061daf753e595f7f1d1fa8e21b1993d640e2ed37c560e52f1fd29320ea17fbe4273cfd7e048e9ef9709ecc3a84892c743a5f33157dfe7e6742712e831e9ae8a988c13b91a5c6d234f7dc23a7820e4e18ae7f370c71f3737bd429eebdd0a3eafe8dc512602f7cd4e7cfefe610634b23c681153cc8bfec1661b7d98d69b871268d610aae993e18c8bd2ec1b6fbb049308c3c3fa9ff7a527164faf45b10d1aaf7a8c9b232ac3ce625ca36c609bcb0f6d2f7fa8577d9d878aea4cd7431a33cb2c905cd5bd697c5e613550c83c003950516679552c1f90ebe039d4d488d12c0fa142cf189456237e2d874cf16cc80143bbdf903b47e1609d2b53b97f51c52fd65a950f6b64485912a862f1fe787cb6d9675b85c080387109c89e0570e47b079ba52a260f802587d62df3c75bf4d41b25e643c3ac876f58431f7174803a9c6dd2610f48c2cb03f8112cf18a5d9d27cb4f0709fb14f0f449008015d25cef5d3c3718e691a5f3c3c63d3df1369416c3b90bf08f044760db188f45a70dc672cd6df399813e76a0215a6bbbc44899c513cb744b1e6719c4e61023af2e4b2a38b83e0a891b51b036e0a24b3fa28deaecfc4d286099c5073bfd92f504ea89bfdf36beb0e3ef7e5121de09e42a702f7a5a7df2a355cedf5e038492868a02bf0d9402db1a491d869296412e7147d2036250927e381d128f407ba134a25388c7f9ca1d9c81c65e13ef86a123634bc32ae3e8dc8919ea6ad30e58c7d2b6390256644a5ed9027b9c9b8847cb664390dbb3dee3350e57032a00ce1a8d6b19fe053877d800bcd6d77c01d8ae22ee6b11fcedc51a7083c50535a654e531a5983a6b4d5cf94b6f0d672529557c0c36da4b0587f8664da8698d206237c2e2d6be7696ec60cb4e2e0151ab379469a953b2fadccbe561a370dc8b178f03ee3d91ffbee75a6ab8d58852f25284468f1e6f0a23abeea0480469917b40751cf4ec04f5ae526de73d380a0033c592f545da7eca6c668a549f842a272f7634b97cfa7a8c8684a7c7ade9494eeefda0d82b518e8c5310ddeb9a494285a475750a90d3797057aa2d3ffa95d5543524e746958e4b097c4f5b1b9fc467e55b378e22e0d53b0f76722c5a473994fe03acdc984af68bb4e109a3b743267c27a492b48465ab345bf397bbfdf9ba6b60fe3fdb3005dcdfd04a5528721c750b5ae0cd8483f12218c5c142cd66987c352064815e8946e62dfe51314652e98bd4f319f3cfd144fed1770cd9596069d3f58a25bd95ebd64d792ce2e6c543012860132f02292d21e983e1715906dc8fe2001fe9d0a8640e14e489c1944bdde7e1796886916999c7ac765e06d3e8633d5a5da7202a280a15e08e91971280980afa58ae676eee2ad5db6043cd8a3bf775ce4c54dcb35045f775ce88ffe800ea647a9619f749219a99632f5160bc4c8a16145be542501000f4c60934aef4374c291fd659bd5b342b94a701f717ade05b38bcaffa8f10a95dc824f697113f88b14e08569be2778f21c41569e00c3025ee3f87c988360744501ecd7e878306d10b53ec36163b8504b5fdfd12b5e96f99dd209a8730458d51ba0e38047c2c80727f977b4a5f7c415eb0c0fef1cdebe7a51de35bea932441f4f8ab2c97c19980e51e3ff63566451ca1f23a0f4a3c07636896a94444528c1ace28418d8c6704ed112a3115cf69f00dd8f6f461ba47856fdb4b472d7d573bc3dfaffc92afdcdd17339d832ac73adb1b7ae8a6405fe3d15dec4d2fe19aabe699011d935bf6a05503211e651df27048cc6c30a2b7bb7198cb030514dca44a8568a0be2cf88996332960ba0d247b695ac41aeb8eab321f434b5b4118af132aa3dd341e34cd703270c6ec1bdc1df76291c89f343706e04542e258a0ee473406d532b6898c71a0ba9407c7d251cce3a4006c05bf97a7e7f4f55b4ebe1a24e701e7fa60b940567e769c15953f6a7c05758c42071bd33bad043a2681d3462ead6b68aed7a2297f8dddec05046e4d95518c55ccc6253cb3352a53dc5ae3ccc803101aa34fc87b1344d18c1a0a383f04ab7434a0755ef4cf541438b28aedc31d2b14f6364e5e9ecbf42ad712f686c359fb3427f01065acadbe1fd83a7b20c3e90e42906af0c53de5c1153f95036a6a857a1d97d2fab1fcc1d8f82d1acf2e705c4deac3a6146e3bcdfcd0b1ae2a4606a265b6ec9abc4ec648d996b022bde2721d4d98af4332321e6a2a3d86d0df3dfe64327842959f683af8375b6c263ba594daf26a345ae8e1cf81db081bbc8648f76ffdd096cf06425ceabc4f9e465cb785b6be326c24c633c75bc71da0a93e24c876b9753dff1c6f61d7a3b77aa7107ad90c10eb41375e6878439a3a9b568b028a92b120565fea7eb107f6dfeaa0f856673bf818c4d78b39c1acc05797e8c03c4548b59021d0092ac7af07122bba42f9533f7f9fcb952ed74040540b48499ae8350a53c34cda621100d6062966a44b99d86b76ac607459d99a668ab359f596cbe6570cc03376c9bb1d638d3be9a5f396c16cc1f516eacb60e3216d641b4c2aebcec446c329878f0257a48b791afa128b2bf0e54fcc1c1bfca209e620966ba03bed4cd259bc5e7563cbf252d1d9243869d46bdba9b7719ed1a0fb7e6c9017f56a6221871e69c9e393582a2d1b57518c6e3e3eab32f32a576ca18882e91880880a4402f48d7222e14fe5c1f3fb87fd48bf76cb860e195a1e53b1d594afc96343a4b895620829ffc0e58d6607af887c15cad1047f132de4097d93e8482f30d179533393c2e43fc8b40aaccc30e482c3deb971ebf7e2c0531c3f98c5cd3ced5940ab880b0278e143b97367e2be5b912c9601b155ed495eb4d34a0bfe858b99ff7064fde287ca65f24a1eb4124dc15fce08038c569c167cea2335bece0d08b06fc539acd7004cfa06d7a90c697ebdc98e2d1ad04139a1f18f484274ab41f9caa06741af6d8e2358bd406df87858148c31dd3cbae2183aeabeb34749a7671efc2bc72c0230de70060da1cdbd31e28029c5075ccb58a531720a6820777681858d39cf9221928467cef12d9e708d1bee108d103a6aa98c9892e222f1bdf25cd75eff22098a87e0264b01ac1439c9726eb6b0d140bc315008d7c4350caed1f65210149061d5bfdc70dfd1fff00ea576c1da34a80b2bec26106852ff41e64e328d76abb3ce7ad6dd30b6aaca0caa8e0fc9971239a0daba5d9042effc05f1c05c4f343a95f9d282a9bf4ce2067ccc9ce33f863b29b44b67c0b63b3064400a7f12b23107c9cfb3b4c87c44c6152c659cb3351799dbf9ad95520f0c8f02f952d52fb0a1680763022b6801a31124d553a989e62056174a8abb433d8c46410c069b051cf3ae48528458f041418b01ce7f073e134fe1c8180f93934a688ed56988d8d6502e2e9a72ce4ac5c963195e3ca72f86b408a6fa7bdc3a2409e59fad5f011af4ec358d2151168619609736941072275ad3a82f95c9efff100fda748af7fde2366e8cbbba521cb6af4b72f81136af2f1475e741e1829face541be33dd8855822c9ddcc33291321957577d7077fe94394f7a3a82fc1e6d6b58ade04b00099517c4a1490e9416d60222d605b7e269199b8d6c1a0d602916324b9ac359ba973f4e2500b3075ec90a10666eb38a03638d15e798e27a52c62c7e6a9d807661137796b1e10276bcb4a45f8a8791282e5b35315cb67b5900fcf865f4852ade382a13c3b284daa321794936f63ca2f9e3c0a8812052b2148f0de6cae44f787efaec9424561414d4ee0fb0afa27eedcb24035c75ee78acc5dc809e321d89a6cd56603e83b5e86ba26ca38666a1b4ac22a88224480f4f6c306fd68678cfe4751a10e31e3e0b34b631a0496c64f4111c7e8136709590f65f6716ac9d541f47fc4e9b6bd7158f256756d875002b20ec31bccc2d1784218c21d6fd9e8b365655f0fe08f6193883c8b2bb4a9e2dd18c807e2231896078612dc8d35b543dc42865ab688ebc5577c20c8d044d5747d72d2e6e003a98a20fdc90394fdc9711eec050ee9c63f08be1e65c55f69f7fcd4fcf564d1eb67b402f4709f1882bfa3233c8ae6fc363e5013e7619da72cd5c00d325f302c063730aa13d0ed3456489997464ef33b95aeff54f6317b5fd7d370308100234ccf10c7d189decc3766d8e1293d242600d1059638ca05daa5dd37ac8d4c5c701795673cdc140845ba581e15d6ce1c51e897ce179dcf18d1a7fc7ba7377de1505b43cca9c4eee2aa97ce47a4fdcab09934efdca3ca8ffd1b9d99e247e55b879d527d5a85526bb12b6a80e7da5bf27a3b5789dd7e0ae0e388139e3bbd8d1cc8764a36f9cff07235804fccdbd1e6062ee618c4ea3e64e9659917cdfa778a2a251e09a68766560732167ad01338d539b80339afa40e2518f29e4ea1fea45eb5331c0ab8a2aba7810566818839757d749cd40e232ed6f3a12ae6f5591dd8d0ce15e3478e3acfaffc04e27eacbb36d2986ec8ab7a14d0331a847192e17b68bee463d6057c5a3fc4cb2a5e0046121ca782fb5c95ea7844c342052e72385f976b3ac331b3284bab1ab745abf61683d253c8dc71bbe0f208f04e20ec1cbf7eaffac5693861dec3f1b21699e7ae28d027388a009db81e6683df161e73f183b26d494200314e18837349d102f494604a564f4adade0809e0f01f11810e036f3f7652e17f3e1fe9794a6ea0cfa05bebf73f6f1f94fe4559295ca8b2454eeac1f201ae01d7ad263151c00a6be6b28b741459ff5144f7af09e9621fd375c277496842b1c5295d13fe61f0145bbef8623a6b5f6d67b38aace0d22c27679698d1614785beb3029cce84e82161fb3b4d5ce50aef6fe60773e0a9c80f53e704047e336181c74a5095150999953fff50db0ab4d929234eb559df585650721b7bbae4e74fb9ddd933dbb88b215ba67749d3ac5af127c7a2ef3a7fc35ffc437bb99941860cf5abe02f987098102f2b0e6efe4b7ebd2ece123663f01190785ec3857494ec1a02b1f835603282c2782818f627100f5fa7d07d6146bfae4f3bc7bb626aebbc9cc5e514e96715520ea051fa0c79f4fe2e647f6e33e33d003ec39db9772d011f46634d3b677165a2d93465b0737a97ae4a6cc40cf8d4aab6def2994c7558fb9d21e3b8358ff54c89f520bbfa68b9d2f9f6f544d15baab5ec747079f4262c7a0bc205b5aebbd84acd7dadd0bbb99e16a53a0bfe51064bf368d13be6944225fdaf83c2203ab2614b89ca92ab530db6122e840db436aebdd34ec9d29bd90b3e6f668892c5163266172ee544790a283144c0ab1936f4342a6bc6da3e48ebd4694da39b28d26748d1a51d93a2dfb17eaa24e0a6aa23682d3db7c5f339592e9ac16e3a9011a5f10987dc4b96cb40ab32f0de58c9479e150fcb8e7f966777d6bec09ab79b228cdacaaf55aec363d84e11545a9749aa96028f7fb904bd11c9c74683a7921173d788309cd3baa00cf22f2108ea35fe3f0a50a6895ca93b019d9ea73cd568f1d428cc77e2101ce0aacf515e79e691aa1f1d4f712eabbeb09a009622d5394b0d0847e0b95c87b8d479ba246cf9fb8757d6a5a3882ecf31759aff777480f8ec531923af2a3bff18f8cc3cb2ebbd29f352b54a64d2e1b6381881a877c1a37db0388baf122cb35c3551b809749e08587c3ccdb10311f6015b4439e24ae56a08e8c71d222daaeadfdcc6cae79efa01e86ee8588b3d7878a61fd52f22d082a62667baecc4057abd9ecaf0e76e974370684d598bd2b97b6e68e26844baf220aabfba67569c46fdce78456cf0d8900a82f2f9238f6dc38afdf1cb667c80be9a33aec385098b636e861faba027f18ca535e175c50b7e5a68462a0b9570efa6dae9f06575fdfbc7fbfbd28d2c5f31d7503d631261270daf52a33cad9011959a0e0cd56e16d69ae88195aa4a64e9c0b79dff20130fc08bef36aef49a6628748580c773540d454b862b0d7d45fd485b83abb224e4735ed409a8d5f5b3aa0bb847dde052a5f9d57432b3db0bec64912f30e899ba60000d08067e76e10b028e13cf7c5c1bd9537148c63b6e1678778cbeadac3c57881f2fccc1a6ee7428328bf0fd37eeb6e63516c08ec7685cf4a2156eabc778658df578b67d96bf9ee202630904243aaf259f49c8fcb1ab79ecd8c67db72603bd23c7cce292c532cfdf3f1d9ae3a3ee0f6218bbaeff56f6a74a6a568f93b4439d92284e109d51d22067e3f0294542f415bcd54a1401114c6f78440b646a4d0b7d1c9e0f0937ce7d12acddd1b085810e4b1a7d11f956e7e74722dcda6f22b3abc72426e43194f811373df21001ae929c15aa322404cb703354589888249358bdca6a59ccc602fb389949121722d194b523f0f1c285b4f78b17c2805e07e6e879d4e0d62bc8126ce5399e61de4dd76bbdeac93bc3e0111885cf42bc602be30bfe679103e5382878e0d6992f1bfb7d3db580f224a95569487690f82cdc026ee3901a527c067242e3891f08f5af8b3445e9ab8acbac4798d001ce2d6b10f6550f7c61e56b045602b6ba74889190a2c5e2059561db2f6cbff5c9a0a0fa156ff6a5cbbb7264784f2ae0bcd215e512983cd95a8899adf96ac99afc47f890b2f1bc752c91ea1b9e48498bef6c99fdf7d047925127b32389e20c9b0803c6a5a09dc93ba452129689a08b5e77180e1b1d79faee2dad8ff5bc1ead63cf7f06ba2579c977d9798af8d001d2bd6d9cb8e1c0641b07a8bc2855f41adf03017653379f53f925c086e50469bb46e269ffe5b6837aec0ff757b258a5187a136472d8a7562a11da1b72ff7b367de68657bcd25009dde4d24954ee7c6ce7be482963c7f20ed3b31df0dcb65c77e8646c4cccc494620d44472408570f8c8a890036651265800f4cdf6d2ba215392fe7731a92165a429611875201d3aaaf2b66eb84d4d9c3c4a3d3d055180a936bb4b5a4638739030cdba6788f9d17462b31e574ae43089ba3360986cd2c56ad28df63d6df372059dd7b8f2ef574fef6171f4a5ec3eee65e0050c90faa10e7743a96728d862e7e914402d7c9978a909d96280469d237905faee6f9913b5420ac70d6238c7234e1f9962adb18dfcc4a09a6ebf59ce25961f67a6e76d4ddf201229fdcc4f8006034c5ec06c70f82b0448bef3ee26cdca7be6260f119fb56883e82ed2a085e8e0b35bb385a66c9e8953053d8f5715573da2a397ae900f451cd86e96912bcf49e857a83725b24ade751c86883fbaff40b840575407cd661d0398088dc37153681f46e8f480206269fd43b9cda09fc37213c6feb56fcf792852bac721a1220a45102a7965abf7670482d7dc18fd60380a84ba5f0320b7f0f90cb942e057d037594ccbc09cc227d6afdaa541a9a762170cc3208c109a62c60f467cd071c2aad73e4c0fae91ed74adff11d3582ce896c21fe31e555551554ac061b53a30c2dd29f1ea9e2140da45bf231994a5e5417c60e6a4dc972ff0034707f31723817f24af9646579ccd01e7db945d6e2be3beca78d5fccb15e2cca9c3c7201c43992262c32c2a2d8de16441de23cd14816842af30e253ee969f6b168afc0502870567c3d854f97f1fe051b2ac53c47d01ce37083e12a22b4baaf710c9a2b69bc96c011061fd982abcd062c93ef8febdf001f68c361783dd6b9df7b81766f1f1e1ade053353c6725c8d77ff9eb03b2799c58b94683ebc3c2ee831d61df6066ef661e2b5aa7e340228bbb26f36f7a1b6fb116a448fbf716c84e2a9bc974a46cb9976165cde47f2546376f776220cde4903187717a3cac8f2d91965384737a70212bb57302fa1b61f0a82fddd6f50dc94721cfcc322baf8b88ef9bf669e0f2f98a0dfe8422515c83d6243b1da3576bf5e8e3784e60bee79d52637c03e3e412754ff33110dae56a214cf443600e5915f5887f184abc8550cdbd4a487bbe3b4ad9c5d8eb9fa33fb8d0a56c18d78670d0a8d840dc48126aef9e2faeedfe97fbfa34d7099df28b670fa87d33334e5539e12f175c0b643fae864465f5677520dd14193522e526892af81b9ae87f309ef531f46db9ee201d9e1d25f884df4c42ca4c5c19e2fc64f1d159c3fbe7c628fac0de56a361cd2d91fbec5a54cab4f6097648eb4489f7ee559d268130867ae28f74f746d23afd2cc5a84217bedf5c46514e70949270c94718b0439a5b60052ec0a4c3bb9fd10fa1e98a22bc41ad51d4e8a940bcd875df5914fb61b01a66f010a2929c7744b47cb37f3e59202bda8c49f4bdc3ecb131e9ab6d4b0278698d0db9e502128e6aa36e5ead587e6cf179c07044dfea80cdb1fe71acefa477d30208edf3dbbc8d8402a39d11c4d5aaebccd661a3e32789fa13f253b77b8579f1312ef675178ea950012e5c07de27401c87c7a9438dac7d02592c3d04a14b13cf0626dbca7bbf62a8ff7705c3861459978381b3db4a0bf8e92bb60022eeeadf8233673123db73b7a571d452b856f7455edace5afb5ee50e0b2fa8893e13a1e5b8e9cbc3e270d82ebb0f4bb79efe3fc6010190abb6b248ad9157fa90bcf3758f901ae6e169107904abf82ee0cd800d8166eb6e1037ab8fc9b4a4b697e33b158ca2f28ba6a1d0c7160d860feb34c04e07b9122f51ff13c183a32ef8d02c1a31562f163254911f96c465e925a280001d7c1e3b0156555754d3d4202ee3cb656a4c97c7e72918d72014aa428c6963edb28bc98305f7fffe9894c616c4c00d3627644d71e1af493eb8b3d910640c7bf7005bb7d5650742c76675ea3791865b4027b79dcf57ab87bd6252770e90dc7446f8b85b7612a359689c690ffabd1684aa27126d27efb158e8c4bdeafd216cce008974d9bd2609e7b364900636987aa3f1a3670853f32b3fb3de9f8a19d40efae4cacc439207771118e267ed1be1b2f329bf3f8b4a901638a7f57267475a0392c5ea6a3aadbcc697446d956eb15eb1da62c63d3c6260cac3faae972de21e50d6344138328908a803122fc8a110f9cd832ec061a8c0e7189ab34503762506121b65ca43763d96a56c472277cdd969018ffaf0931f502836bb770f74f61e692a2adcb3acfa7dbec2f01ab453d2d2f8030c930ee25200437d40a0d9c3a0216aba876750af0103d1d0ca5ea1f73925de858e45e7cf458ce7178d49b5f3ae71b0592d94242534d502edd0c8e38eb702a9330dd2f57acad81d37798776e3cbab33e8a67d75c7e868615d40b1680a80d14329e721c3859d6e28c91221349fb04a05bbbe6177ef5163357df23d3c59cd38980f1df76dc1bbaa1889cbfeca4659e8902ce9efa703106d878d756e9f7f94c96a1da7ab1fcacb51492b4d9936a3457a19bdd8f7a3dd1ef689a71d102c6beef36120585b0421f0db6e594394e13d3701515d481f0bfad89e0f521e735306c43ae35d8fb526ceaa2b3aeacd7ce7426e8c29e5e91765442329bf062d5422cef1f5d623fa84ae28e1b3a3de24e93b28b2760558ca2af7c35237570dd51386a53340543016cb42716cf9c5c293cfe9cc973df520314bb6877fb2ca3a9edf5a209fe299f0daefc1425b179d2ae8c3fd42754a6b8fa346cab8be1de7fe11286becd74fdce8d0050b07e067d517d2497b87fd9766e8d7f02045315a79de134638c84899e13b8ba06059f8d42fc0f2c73d27fda092838d1bad5857a2b801f8efdc2b56da7f978bf84e19fea5ad54646098224ece994651dc3d2472e9edf0d46db04fb6029ca536374f8ec6b5589913a71d91c704ef8cfe707f2a29a3f8eebc1a14ab83a52da51812d85b0167a12273b4ee964360029f66a6f840e8649ae042cb5f42f332ffb06f3ed64870a4329c50902356830c2acea0795ed0e5eb367d9b18c7154135ae42cd912672be65aa43162c133826c9ffe15f0a0e693014e6755a8ef26c0b996e6a40bb917958d16f618af96335906435e292ae5c6dca5b32a32f5a7b4786c952e14f5579b782c5b44cd2a67a8ebcb308ac7b414d446b25838ddb0ed27f9a2eba1fb4472d23e5c211a357b8fc439c052d90f12302e0830d808736eb437519897b51fc6cbabfa347227271c31876d456863936f8d2c63af902f57a7bfd28765eca65f2da9bcf57fdcc2198de97af8480c2ed977234798f9b800bfa8705643a843515210f437208a92a49c54753a867f3587d5f92b29a2318f1c0649fdd520f01fe92f72b91981360a04df6431356ea4130e938db96d634efeed064a914173b796e7e1a76a33d13b20c3d5869e9b104d7e8d2c79fd201585b6035494140a817add3e8d734651520ce1678b82023ad6e5afd4175cc68c2e0acc3e3029add4c82662b5c19e460fa51d6f29b5b914864fb4a019b2e9c0275b0ed6753b57c823d2142515840ff74eedf95529670c17d676eb1728ced59ed7cadfe7a69b4431c618a609446d433fcbe06b8be1fab14225e53ace89b10b1542b3d4166dad6ca3cd639f2726b91355b7b632eacdb505983b189419cc16e663eeae95c7a14655b15776063e8275b23fbd459e5beea9b30c9046af96a4d04050eb860173fd7afa8e789b119f8277c7255fc26bdea77ac74f2a4cfbf1b0ea749122bbb54ed4024487bf42c6da9f8b3c3cbef057f29372bc093d15f997d3eb0b248abd40d7bc78daa840be882ed0c539c7b696295208237293e82087bfea4d29268c93736d932e142844b7b75166cb4b5219530dc3ae91813e984c5c8445d013091625d1a2085eb88bd7d17816363edba8b57d2ec2fc0790f03545912a7450ebebb2a2c496ebd2508483fd43b4bdb9d972b1c5c44873c1e14d85d48aa48bd887c1c8672496279c9651b58b154c505baed3fa9e74a55076d95edf8de963de961041fda92297be8febd3e03cd354dbab5ffc11df7625939a46744d15f32d1cdf36798437d6b429f488f2acae236c41efc41f32ebaa06ca956acc325d0a46b32aa05c71c113ecde9b2a2d1ecc1d28b42c0555a929b9cf4e4cd5856720025a18caa38503d7cc841b548553259fd068b6559306ca4571f022611b0e78c257b896666ad03203704832d186b05a6298b776eb78c2a3088e70ef5ab6abfff7f5c166610ea05c3113ed9b4ef26dbaa6322f50728a5239a5d69f57be9864124f485060365963bc07a8dd073c8125a0563d92d0b76cfc1c85814e4317281346dd15d3268f430c992dc8daac3b716588e39e014fb8aefa69ed218a27b47ad7248b19acac708f6de131c9e3efafda6549cd39d27d559cedceac4079dfe97a4cd7543593824adceb828d168439f070fec92ccef2812987d9a7467e9abff47da820f94ef615c2895150ea47a6c0a8305261718444e6ce0a7362ab7aca4cf044b22255ae173c0c971e90ec0d59d8bdc87b54811421c49f2f38857ece1a2932b88b944088e7dbd3ba35b78885cfd73b6d8da70c0fb306d9e43912a65cea8a055d180bebd27c75d446ed516205ef0db7c27d2ba70651a823220f8f7344c4c96cd35f08615cfd293776325d9aa2cfb682a5bef172aa68a070d2b74ad98b0b55e02238237d6c4e1cb14b642bcae76286b7b12b981e2ea1f61441f42b1f9469fc462e5356c07b0bb799aef8063dca85417614427dc9e3d824a08a70082003389367772d1260d5a8100041aa054902e303c7cd3781af4a5a368bcfd663defbfae86fca64e8db1703a83cdf5b52d76df998be1dce8d307e99284f58c2cf337b4c1f105f71d0c8409c3e2e6ad6d6b11c46af369f80f60b115256eaf440e7420e537d037e29fd1e2f2b4776cd108564836841967efacccac3be57e59709d2886ff4e17d1b65ba3507786e98701ef5baf9d2b8be5e54eb6e99995407adc84f0d63d043d548e807fa3a273147feb7b9973119e84027f4ed1ee871298ecc2b4f3a7b473da594e5c4acb504eb0a1f039979f76cc38af74dbcfbe13dfb343c2d39b2ac6a22134c398a4d802150d0a2a814b4a8c2c54c5488f2765dc89c13f24a5826d5f435d98ab5f229fa2d575af18ba1e7f19bd711cae5aae670d1abe673eda11bf8778d5f00eeeb1bd6cc8cb8fbdd449f438b2e4653c3b00eb943dcb4371623fce20f8921bf2ffb7daeb128770fbb4f0b805d6e041ae8b734efd3243c8be6c7daad0eae365ae5eee749c5bf26c89891c5f43b2f8042c578e88bc58debd3207011283b3d2c684e5f31a796638140a7b85bc08eb697e28afcebb9b58b4459e3bd2bd8b61a64621a81e702a3311cbd1824d6fb32c7ea9b7e0f9c218794dc424b128467b36fde0e775de47f2b9825c4073aa4c198260645234d09c4e5a4abe097c1e07029f07a7cfa9a4e14ce93f71d002de732c9b52be709976a0a5d221c46d812e55a369cd903fbb46d039c38affe2936622f59ff5b9f44049b891289effc604f8aea6015f0105f43cd8ea01a66e8cd31fc38f661773483be7f1970d1a8c54e4e9c3722581327d9874d850aea44d20e5ae14efb38c0c44300db83445f68e428907cbc3a3ac97e1eea91cb4a635e5229f53b1c55d70289f79f9aeef48eb76bf9a9a9ce261aa4f07eee39146dd02b3cc2c5b153cb710e5836aead9f7744e0390056e4f6253d9c0012256bb07c0a6951fb32f66a092cd5600f6f80f9c5366b1faa360a37ff46416d8ecaef92de9ee66003dbc27c10f523cbaf681eff381a906f48102dd73556d6edc1cd4b92a3f3abe3eab0111cc9b9352316016490201b2e941705f2a224d2103242984ebd6c8ed2257aaa38625fac7a563f3ffeffe9c9f5af4406d122fd3582dd383c5ccd116b5fe6d182087c1f3457994c6533bd2e81a7af65d63f0b83458016186eab1156f7c7b09d3c524062332db0b7a627cff82246253d50ea8a7ba34f536ba12a2808b00da39a9ebcedb3f10ea88f94b5d4d5fb72e38ad32f4856f66f96e8609872c742bea16bb93a9c6f70b635f1b4d113602ab283e24655e49b587ddf1b821236d57d470e31a995c4fdb7a37ef5ef35914a8103244873e5f9c67d05b0757fb1287d8e4f5db41dcae6a2ad18841e581d3afeaae34d315fc789f4c47b0da54b2a2d44fef3d0c676875be2ad77bd5b5bb6e54472b37c1cc491fac8a8f4c7ec11c9160dc4ecad28848eb897ec09642557ec579a5f5881ebd9277144547572ac28fff42dce6a4731d3090b775852c20952b5e6ae1fff9c2a0d44e3e39e1e3418e7bb72d21965449696e0a17458409cbd1e319bca5cf04de2058947dc31e501124f0ca2938c21417395782d6ecaf21a2b08e6e1ffede8310d349c0215d505057ca8141fcf1901c228b81fe888603bed76425dfca60c70b1f76375666d051c246a0eca8af268aa127c7e71c597a354cf7cfd999e2723dbc2e84050244117c9da8e5951887438dfaec7112910d61af4eea760d3433ab093e05ba6e96b0faf4e7a7fbb4eb46517b9ef2cd4afbc81c7e3a6d911012301e892e9eb8f8a73fa039e55b531ce02302d9d0cb55adc55ac33a0e414e931ce6340e49dbc75b2265c82621604add9212fd22b2733b2dff65aabc12d6a9101f3c09ca29fec0effc20c07a4b102144b11b9deececa18f3b456741d48a515ea114e622d91a4f9eaa66468b99d8123658ce43686d7d46e4b284fb5cf694139c541f697603f46c5133bf094032e38248e2d373ca65131c3def099af6f6ebac40daaccba6faa2b5dfef2fbd364d8775907e67c770d032556ca5dccbe150684c32d85daca8ee3637106bd6696e5a2d51384f882aef5ed1ce05bde0fc7683fe82f5960727f5beb6258c63c6e0d121a9fb81166262db51adcb2ce74171d7ca31d3310d329359d6507b6c7619d523741aa43656b3af8d7d9008347a4a3f84096678900c69da7ccd448b5bc2a43b4c731a3c2e5813f07547dd82eb2b8b9fdcb8dada7139f2118100a5c83af913a4d8061217759b7032424c5927ad1bb8d3147a70df53715cf1bce2c45879ab23d7dd46c5b74689382ae8c0dedfaa6e78ef6efcf2fb525d74db68b27e80b4c2e525abdd20bb114d6685698f8132a39c88383f50f8f63400aa628fc4eb772f9a12f8d8f62b797f82104aaf3ab72ceb2137c6db4f246ecb3ca708d658eb2924c94e0f21c5199b96055726490a154d5327cf41e68bf93fe323f1aefabd10b2da65b151c47291e7d90567e98ae56a8167a3da1c324d88804b371e69476d10e12a2200acc466136f50dc5571d9494620ccb97bec825019c7d3986ffeb9d2b09aac71b25fa62ec488f1cd38af3895a1d920773e15cd4e068f537829f5a67d8e18500c0b30a38bb288101080a395ab8393c94abd0786431da424b48c499a38194e7e8a595984d40b85e872a9e3df3d42ab698db1d86995efbb1a017ba7bd517d27d8d8b36723d840553d09100259ed057655bc95d39684b98117fc2242b9a8b6b4632f6cb1f6a24eb1a8309fe6b4ec206ac69ad176d868ad994bb90ce59d43098ea2e9ac4f806a2105ae1573e67f76ea38f1b384c75fa5a62d94ec3044facc7bb0282669a2db23bf4aa672880c5ddd41b561d146ac4006990e4fe3e2ca38112a011d7178ed8102d08574196c61b3b4c2f61890f59b4e1f40046b93c022e5b4ea4003687f179b087a40795e96a242aeb55976b633d65aa16e9a7c750e46f1787bdf046629a060fbf3b9ffbcae82c916bea37ba8597f2fe12f11ab88b876c2353ff612321710589171c1546bf818cc80f4f548f27453249b66a162882f0624bb9a3cf7f4f5c8d6d0ab5202fee31c3442b5063c4da6398b2636f0c4a9cb3252af985982be0827fdc3572de9b34f571f70f903b52193f8156c7e949ae05e0018ef5493b6f3548d1695bdd794b73e18c1f3ab8f883a1ed9fdbd513e19ad22b01e09554b3e7ec2df0422b0e936af83f8b0e19708ab97bdfcb8f5d9ac74b60b2a40bd12fc08b46d94d2d42d2f2b9e3fe12c4e8717aafcda376e02b373415d9e5369dab2c90e15e0891fcd0f9c8787f2769f177fcdba16c3ac944276e87a3322b208dac73eddacd681d20b2d8134961fc4474403064eabb7c3f43323439053cf974905570ddb0ea170fc5f332096049c6f76db4cb336e1c0cb569d5b1d14101f6b89d66b28a23cd3048341bc0e6bc997fc4ca25d424ceb6211533b27ce4f6caa0c21d9e4c2df91056711f0e06bedac37d46add1f301c7e21a0363c2cf3fdb82454ec8b7924be17ef6dbdcd0dd5e4cf4befc44ee610c9c2cca5f954c63bb6de14b19241c742d5df6717949b678e27c9c8daa0b34702996ef5320849651e2ba5388ca9c1021f718d19fa90e6b4893496056fb30b8e0bea7672747775865bde6c7b454587d6988b664fd6cee6b9eeac9029b7ade54dab1f40e5c81944ba3b1df3345df0cee261cc943660264ad7b4ac62d05517ad7e0158d4d8a13a6df26b11bba0a1ccb68aed0d64ceb532f18be1759544f524de1df653071ce195d465225c5b434a03374af52e6b1c9412c7f28045fc3877be1d4e3c4f81f0b942df3592e27c54a918f14b4ba68eb55f77609a7a649e65fd10c9a2a0b396504cd45483e47edb44c4910b1e140234d640a84e42e9b9002a3685828b716911e31f06f22a9d5ddc98802cf1ce52ced05fa7317e17966b0f3e0fffee5ba76926451f929ad4f218f5f35a36880c5d055f8463804366d6e73f996ba1ea96e2632ae5d7769d9364145f9b58e3ace4d8cde42d63afc2d19bfa65e64950c8ba4b0cced51d371f794f48c79b04b5d1a292e34252dda0577e816d20c67d480b7c891781d0325dd9a0085ec5b1f80d748c3bc76905f5fb1b7b735a6b5181a174c92c56d095324e5888a028b914e03db6248ca7b127d2a1a98ba3f160bd91512568267d59ad42028c7995ab35dc76839ab8a8ca79ded5719d5c567db608450e340bae0a8643dec48c0e401eac9fc83d0889b70afcd0925c56bd41cfd07b19a3420799ed8947d5084431d8d80bee89e8d230d54decfa467bb35a0154f18ec8703fab9324598230745fa6e0eebbfeec6064f9ac17a97de2a376e4242b66d2fdc1171e2970ff470c0e74cd4dd8b0ebbaa351fc929f8dc8d253985ab60ae5a5d5babcdee9cd936798f0501a60a60bf8f4ddaf87694a2682df796b1b5c6d476173942212df66e4a427e4564d997dc723588c151c03783fa74c7b2e6ebfca43fadf1a882bab3d8aa56048d96e79747478ba976ea5f7408402024916a837094c2757b072529f655c584a8833cd27521851056484a24f02b60a8d88c0f9e626628b47d9c5331834d266b534a34b7b69421748c4188bfb3e101354714d9135e5f1f6a35cfa422330fc2494d646be1a36dfff46ad04da678473f1ed7a8973c438128e5bb980b4f446ea75869a2d059f75d2c42126b52f18589880b3a03986b2346db3a8c724dd31843efe9b4d46fc8f2c25c9261be09aaf070f02edc602c4190c4e6bfa9be41f2395fd8dff79950b5b6a6df04b2a9f836c28790485355d5cb2909d3590184d8ae14635530106ff675538a908d1efb6a202f9acf561b8135da817c4ef12b7ac11fc43237b435541c121b565592872330b8f1c52e17c7271194e45cd1e3583227145f13c0f3c7d1054b39624fabcd7ca34b6c48a8e6c00e708188aa998d593f9b2155683ca15e09e4cd95d011cae9b8695aea18b9c31ecae3bce9c822185167a1fcb365e61580b99f2e763e5eedc95c961c1bfa7522571a66d6c4235bb5e47dd96bca7e70c855273475f8b50ed3ad3734087d864ed0c65fa3ded23ba239a8090ec000c5298d0803b09195ca705365e91cd31dc3a642a24f02675596d4ca8792210a3f54228b34f35d34222d2a43ee424094c8d371c12011c80f99e0092ff50806c841f655b1e281828a37a2b1a6d2cb18a629cc030be884c062b1a895d1530dd5010ad68a068d07fb9d5d9ed340756db002c873efb79ffde552283408b862166ad68a46c9fead36b9c012e3738de4f817781be6459cd21f29b748c2b1aaf7c0dd3547fd298899d8fcaa735bb42310d576d2b229365fc60f8288cce54e8e7eb2e948ba5884c44ba774cc42ba6099c107f069a4469bcb8f128d0a2a6a449385c234bb146e59243847c5bb3eeef61e3388d4e1f52e2ed8bc844fbf74b83f50fc01120e44ee08c35cb705aa7f58e4a38d931f9688a96cc6225a0f18c1f03e6d7e263ead801f915da8d74f8bb439a96611a78e4bce76934f83e80fe6da351e2443991447c873a01be8d8b8933f9590722e19ba789ed58a8323e1648723473d78840620f9b714853b226c9aeba4e8e3cdee3119345e184ee74438314cff42a3fce8b203a5acd51557df78952541e52147004b65a8f919e1d234370512fbd79704f389cec83da67ad3d75f3e010b559486e274beb45e6f9aa94c2b205d456580dcc5d34967adde6aa96a8e5e04f7deff4f75230d89624ae69a212c19781a352fa029e8d32601ef71f13ea239e1a57a0146d364fa9d2749c02840d5c00258a44b1af720fed71adf8881647524824c383e327516349a6e55f5be817ebef200492c68578bbb746a9612e5e668ea98ef3850bf56e1489c04fc5fe328df4d55d49e9152811b26df676d0db57e8aed17711533f916b91837077551f2d56a840f2c01c669d721ad06d0607b0f2dcaa6a580958b34f4328146ef4b5b5d7ef055740f3878a9359a530e6d6eeb34c1ccce4a5250b825eee4bbf8c9487fa17cb2ccf5e8aea749f93b870a1859ec12479db13d2291d85b6c1272d757f5ff1a42f4b89415a874e6a3719c99fa7804615b8b4155d2045e0afacad2921f8df7ea670c6ee0eea9874b6a9a59bd4612448486a0a82d33f92cfc9e9d0053b841460201d803e62e93d5b6c5fda4f1e62a1eca226bc5f890082fb1110d42a87b60b1a63a13fc6727414a2054594ba992106d543c510fbf14053850b4a0bbcae16319eb2a74c0ca2da4271ef31fc65ff30205882848d32918ce2cc6005e34aff451964374a10f538d973b8c72d3e5d8f033f86e5c2537e094b2d0c23ed7832628b60e0ecbade701933096656384274a39b4a73506826c4610a1343963aa3d3c2f61a949a3058b8cce1a6cc726b6ff13a549a08115fbe0d5907d31ad4919083d62bd5f89ddf739b4a9081e42a21f69f22cb76ff429f33860c79fd8da25729d50627cdda082c4804855ed65a9d4370d1c7b16aed251024959b99c5d2fe89639f91be0f757f11ec6abcf160814e4b879d82236ce8eff8e9ab956c8322885db90d2e7052fb829236e224238f209c38f7c6f68a81392ca02f0f3bb701a8ce33287ba746c53bba894754c4bfcb3915f5d0e7b0c69f2413dc7a10928c6569d8ad8455cd0c4505facf563e012e43bd136d02174c8d76cf0fa18dbe80ce9b6f166119034fd56759c6913dcf651b08fc9cca48b7cd07220458158cffaf7da14e0d11439f950d0018edcd11e935aebbdf4370db514a00b67bff35a2dc58b790019df582a7a023a8a9477d5c8fae9feee89ed107ad58c5fbf4b5ae262b21ea83c31279cf30739f3e56e065380d741ad924937dfaad481e2a5d8d4835bcafe4d6da752ae34a947b66e0cce5474a9ba03ef828f57455d1ffaacfb212943431d4ee5aaf6725ab3cc3f4a79f04e84ce1a2c232d7c6cd8a6856b4151cc214a2bf543b6bfa2a4d23e0e5055eb1d062eb2a13423f907a7df31d7b293bd0d0d50a1352259c0742508e684a620b9b4d8cb17f95d992a04b7d7871351dc1e721a87cb0a1ccd9baf3b77af0ca214011b494624d8706c3686035d2492bf984a91b4c66d211d38ffe9a0f076cb77866941c3525986c98b6ba1442da66a9029a16989988100c69af8c80c5cca570952f24c7f871d57b93ad79c4891b075661b893e640659d1789657dbe19c6144ae00053788e50f00796063dcd9163bea555a68a8072a12031a3d315a9756d3c53b460d7387386afbee72ae91e4fa338a6a01f4af0b7d0ee070ee29f5891f97086fec1507b628a3194fd2aa4e7a4b7ada2e6c4087033a2bd0ba0676b3c38de973e7549fca0dc590081cc0cab24c5edc7470ea7b5807f780a0da4e3483137e03a8b4fcc23a59c9485c23e165857a971d9008c71050fadf1a492baecc38eff85ba7122243962917679ea80242c1d06bc788afc2e4c04bcf0e6bd8b9095efb26a7546d3ad9a59c89cb9e905a185d579d0b50a2c9d42b722cc748e00312bd64947562917a16aea5984aa3109fc766bbe8e1bfd1f3af43f36f40c0d6340590b501bfd235d25aab11dea681f39147eb7ce11260254672a2b50b631a4b1cfb1906d14bb6e5eb586cb05c26240c787984a8505f85565ec1ab243319f24f40292f5ef4192d91486317aec1d9a983becea53824c29c95fba286c292628871d77ad6709a6717f09ed5b8c12e626e346179c9e32ca87001b84f40d20e1f3798a6878392d481ddea9607293c8019c79b0fffd1af388e008b1a2d80f811c6ab590f865f7cf52245fb6c9d8ef8bc0b8ac8e127feea7edaf5e6acaea375c1580dafbe6fd9ca38f0e2f39d2c9c7e053ae3247848c626731ef6740f49201fc0a7ef028035bc9c732609cd83b1e3cfcb857695d6b56af5578bb42f7a745c95c3f7e3fd5d3fb7247e312757d83bacd54b3140ada22d084927089cccde4f1899a2bff291977a410c49f9b17189b1a7c8a872d90df3f400116b6f8016572d15952fdd1d3712965e6310f839b0b7939845b41fe00d95c010896b980083cd347efa94558531c44814f2e27381b8219222c79ef4f9b7cbf007886fdbd64cc11a5951cebbe17bed8dda8a5dd6f19d2c52c7411c584bd2f1804a7c115acebbd3d5709ebafca0582da784d280ce9b10908ce7c72adef1bd241d158dc6cc203207bef4d8c58b6cb9b890bd144abc2d554afc72d290a64ef2cb97c9ff8de3c661e3afa4a5585c8a3415e34edc32672ac011b1606d6d14dd60f4cd9cc6fa9923669d155e603f18dcb4d05325f503658291bc4359beb4a1aa4c9c1ff9ef58176ab79648e541cf5c292d418a88b4b358782b876881117117180242fc7183e49511f634922e2e6e49f838000b93e9e8f4b28cd468fe54985ea790535d512095cc33c543e60c186d486dd5059a04ded6ee1fa9b289e343d38eb8d6e2ceb1c34dc5a8a8aba83f9d03bf8ad827b400a988640d783df9751e7756fa4c0a8e17286d1f154bcd9cc46e88a51f76800e101cbb99e0e03cd4c77cbc7a243410c96bef125ae6485447eb74a6aeba89ef0ce8efa5e1567de448b6aefa2e108135dc222433ae01c7e6fb2ab86bc06891ca15f9b9fee8f8499099b44659e092f3138300501440f2a0d77f0fdd9b3ee7eba2ac8d3c84cd9451b260b740bac5b4eae7ec4cd1c2b7fadb3f4c15cd5a6d3e893c9290fd87a755e052068ff3d415875095eb12b77054db0ddc40ed58c397ac1a4f0cb820c404682c53f384b38b8a336614dd496b4cbd704adabc7f099865d3c5109001d54a013426b1673e964558cdd7fd04558321805381e0909f2fcfffc0d97f2a226f1c6f0df2946b3b7ad4ba206895da5468f7ff435ca3b697505bdab7ae06fbbb5c017e298c27ef4db93e54c00bc2b08562ab83b037b6b50e1c194f6f73488a0326275fb15d11cbf1a110a7100bb452d855cc4fe67a8694b84be0c9062ac135951ff8ef58255a44adc3d653afcd54bff7ccf3dcab64c3e1fe3c6dbac9be1c840952a8fe20ff37ce610efcb1bbac2c7cb7e5e12a022db313f77694d04a05aaa1a61e4a25cc2c665ccd25ea6d58bbd9429d5b7ee59f5481860433f919903db8868a845c36d86d09d9dcab381bdb4313cb113b3c7f7d7e3e32cb2da9b1f2cf5c7c86a8fe366d236584061965d10019d1b399fe34f87442e6565a074de4b568b548380f0a2b60a742f575fa2e531c2b50fa016963721bcd94ab78461cf82ccac9458c8192ebd5c099c3e0b8c3458c2bd2697d1c36ab4007f72e41348cf28c37ef75beb6b8cec458576cfa97659cd70fa5e9622abcede94f2ef43a8e7764875bc96229a65012321da13eb697a0903cfc24855861b08813e9f584cd8eead421c6c8b02fb5c977af42c0fe2b670dd337b6a4d419ab4938e8991f8c3224d0bc5002569a21bd91e4847376a485598a1c3619892aebb2a72d2c1ad3424a1f8054a47363f875f91f54c560ffa590d53a724fbb2eefa6bd5aaa5ffae88be22e41206f7657a2d9734b2321eb460a254af2767b0f00ce12c230a9735b95ea59d1f979ddb42977dcb3651335618176ac05627438d95c592eb645a0c82d1e92add1b5bdbfa987a0b09990ca7445019e62489f512b81750f22c9a38b98802752c9b1d880b7f708d47f6ec56eac0ba28991cbc572d2229570734cfa22a22332622712711ea1bef08366d1758f6c37b137688b4d341c520723612e2a5b5b02e8b98aa9d5517d0f8ce4452926c27a150e3c668a778c4a2b1de44252fcf21101e5200d0529283a8ff90170f7252327713b54a435565d989130d97c5f54e1a4a4a6fadda169378222d1b47f3f264a2955c2995045a9458c9549fb4645252cf288443e548fb463c07f5aefde48b597e82769e1115f1791c420628fd64b42cb46d9b79d868a91b3308aba92d24d557d528297445a2945896bcbce460bca4292c84d61d40fd90ebb53e8a5e1998e3998795a13435ee62e7303c6662bcfc7fc2892ba75cf639dfaeca32e37b9c0b25891f1476245bf6a031fee5d0011f54879f0176d6909de53b427cd9f87d1eff716289582a331f676221378b3fa4144939d12bc9ae900ec5ef5bd26cbdb02c1b0eeadddc937a85a33eb8f30001eb17494311eeadb25ec4d9c5c379c431171afca31a599ed4d11dd5406307502e797f4a5a341a3676544e19bff431d934b2166cc66ac159cb89597bbf2d56b869ddce6b319e1070295f337dbf67ba3aeff32caa838257a8e57c2fe402a194cc79a550c519a84179a052a0620cf9783be6187525264b78f60cc0e49b09493f6983dfff389ef5a247b3e47a66e181f50468bdceb2ae4f4087cb69f9a373656ee2a693bb23225ef5081e76e6155898a7e730c01ccc0598ebc33fab7d177326597533b9d8129861e70d374bd3b154f3c5e24403c120584365fcdda78cface4e48da878cade966c2a0c04b029b17061eee6cbda58004ea4ebca11bca1627dddd365a637842a1a07d61d9de3aa58c3f532e5afb795c310a334effaa928a5464e9055efaace9a9da6a565b9e005476fc98088fe35cc483b98ec1e18bbaa869332b69024c7254b8432b9cce855177600d505657403993774481a76b4abd11370157b5a2e6ea0db21bd1ae224d48e6cb8720f0c87748504e453ad8c7aa32cb5b8ec909f79d92a649e9d96dd0b8ec87575a99595d48cf295b1a9fb5068e783a92a79edc65de04a2c5290a861f4a297462ddd2da00a1ce39193117ae944e58ae3f9393a3f5d86e0c87afae5d8229a8f64cf8d4cc08c9334864e1838ed8f9e8c4b51032d2d3f5898ef7abf14324d7bab0e6dbe7dbd58ad4496680fd84735e1312ac26d3c457db5a34e6ec8aef2e6ee00576b2be9cdea834701fa9710de9dfda010d8dd48a8466063a1e507e329bfc957dca87023a63510628e30807dd885dff51a8329555d5dfd9d4ea5e032fa438658f09b3e29d3e18317ed7b390e98549d46873710856136248cc9a98ac93e107f20e6f08e41c0fc9953411b0ee8e3e852c4a1259e0c2d3db759eacac5113895249013b76bcf7e63e420e8905fc723eb56eff80ed4351498ed3c69844e2497561c6d594aae27af2df3e82523accbb0bacfe752fd1bed4459554a005218059d4a42e0015482c63e18fcd47c354f3149aec79663c8418b0f5ebfef816da0a387dd65b649809c7c4456385787c4f6d613114e8ac70ecbe34d83c4484acea8fc03c81737200cf7cc6f57e10f6d59396d21b2d4ab76cd3c1e6d1a99bd32b14253ebdfc9e99229b0bd333134ab89e46ae782f3059c1ce477f9d3a7ad6f3bb6ab982fd4cda8794050b28e3a816de953bd8c7764d5b5585e1a651e183026704418e92c2dcb5655ef77ec32b2efc7157bc16ff8c9b3981e6737f191b5870285cee7964d08ff574e6cea583558670cabbc823242b32f6019307532e391399c88ab71c611ade729a5e5732cd04ad954c63b7b5a57c4ac35157cd5b7fa2de27446bb080d9176bee26ed3bc93219f1eb90e8893934d64abef03941432b7893dab141808d1640dc422a72a6a908073f4cc7b76e746c295d4b40c7221a131114389364077e609ae5a0e64af6cb7d274dddabe80f950fd049e457a0cd9d60812e15a6d3bd54cdb83bcf4a2349ed5fef79c39e518a89f4950f98f93311963a2b31818d0a0b72d4f5eabbfff1aa90de600b45ba10a802a52b42fd1c4bdeb9e3b1b5fa8ad8f0c42b8814254b87c401fea9e32fb4fc3bf5d1411f8d28b7c2dd7c1b9282f342c89e628e89a75ad891b5849421fd3fd6bca3b257587211084c01696b01bbc5f4c9075001f6015115de3335a1735cc2e276032f13962748ef7893ae2ab354477dcae372aff732eab780971dd393aa7e401279d88e9d64ad83293f88d5418577a9a557d3171b9764af7187bf866e793400100a11a264a4680ad729858c2371192ca024847e163188b6f8ca45025c98a0a91c29980043090272a802d4052d39e55302662d9139d0026a8ce82acedcc18b2c15749fccd7f873e807f493a2cdd5eb34f00f75058f5e471ead0f049aa19923c298f3a5977fe8c64f70d7e712e79385c5b218da40a444c135383910a45637f224bb5321c23bea37a8ad6dc1232a45ba5c7605dc27e77b8a0cf4d11d31441f82e91713032805e20705e0b83d73da4d6c76a94d81adc2f604c50f2d0643e0fa8d83607b11b1a2c560e9aa744390b2f9bb8d3496f36b6a552715e837acc1532a8b33f1c7d3da371a9619f52202b1fa712c253b44d58970e21ee9afc12a7a94a1058cd53188f572a41699b1a53fa71006f8fd8708c25b2a9cc0828e4da42e90a3184170979ef256185d33fbdb97b3b8f45887b7c742d45bdc628f74e6b6c8119c0b8eed214b21a8c8fe269215beaf38de2bcea231bbc023ad27e2cd7d402379349833ba5c8726ce0c2a8ace7899d91a08236a8ea1152359bfc7bb7e58eecb75d0dad3b99986ca65285667e64f71f096d1a7a13d6bd928e5bc91b41a22ec84b5808099937d5e6cf27e19310ca1000509e284fd3001cb3a7d2c92bd49552f484b43af924a5f641182834c4e1447630b578765e4bad39bd56e58052864691bde73f629678fd1108887793109a9322e5288cea8cd4f36fc5185012c99602f3f091b84e9080cff936870bbf1b108edc45e002a86940a45f0286ded7c21db5fd16f4d2b515bc1024223d4255c7e8ea1e73da54adafe50bbcb422f9dbc4acf8c029741bde9f23b99451197835067c350401897704b503f19a5644a6ab021292280b67374244812c2acacaf10e06cfb9dbfff1ead237f82bcee22601ddc423f25e98e3d4fbcec207188970d55a50bcd4b5e9d6b165ac8e4827a41cf7d7d0f358bfefc49d3891470e28929f8c544b7c10a50b94a7875ae184d13299b55c1a3693197ec6c29028935896df61aeeea3d8a72c7ba63dc12b8d8cb0b6e0c7de116a94e9000d4daf92fd3b6e66c61490c77962203184b682d4494ff6e01db0e93a35837f974eccce89370fb4d11fd4dabc2ba477b14fba55db29bde1908e294b16ffd2b1e6156efe17288129c39258ad9f6172f820f0da87b494f68a091ce9f5cd4f61c1fd62883ff8129dc71b2463d0663773e6c6da3256503d664c9ee8749784c9a603c0add8d42370aee25a4b13b9b9c2611a51fc0a8423fb598cddce8a78959a87cb02947e6c53eeac6713bd685dcd0306ce5dd3c6951a7eb17a72bafc339ebaf211f4beb1e7d5dd1d05290c4b4dbd335c1c686925c1f94f8a64045ec31aada368f4eb208ebc9f8a169c7a4c02c4141d4c9ec27791a86facd4417925f3ac1d2268d3b7a76d9af5ed51ecf2bc70bde83dafbd22f447ee81116bbb4283a527922c6e8823213a06b5c1ebde53cb9a3b513b8c186da276892e3933433dab14e48fd3f1699c06d250d18822f3842c73307d555700ec6d8402ebc353f76c2316b54d52f06e4740344a5992460431f1dab0009b0aa0a01def8bae274df68199ec5a816f89a6cb1fbf3cb12aeda6b0e8e39d5a6a02fba5e22477e0b274d2364522619df09fc692eb89b1d0f11fe8105909280b264de7f195ffdcdd50ee1fe97d04b40f1e5bd5981819708760cdc392bc13c20d0996e783fe8c5cafaa6fe2d9339715bbfcd81c7707c0b26fa58b4c506a3d3cad753721f4cb4a47279599c1494aab4b7bd4cf3060bb2201c83b19b01b3ef237a83fac0a2ef1fe1ea84d324d160112b7125186fa224cf0c2ca245c1c2d571aa0059f303690aaf9a93f619e2ca3dc72f3362f90f0cfddbd5ada271cd3e044f8400f2aae3deb549067a4a85d37574a809b4bce4ded001072ccda44c9fa3efa7af0bacc09f99fabeb34f1a5602ea300ef38d5eceeb297abfb400e2378130c95eb5c611d031621776758d10eae66b0ecde05e71b20f5cd352bb5d1405c5f745b23099cbc70f273f795ff69ae5cb0fdb33c8fd5be93925f5320341301df37e3e561adf2f4c8f77ab125a4ea6578359dbcad822efac696495300bc7e19ee588f4e2a90dc44e791e568f4cf22576f9e4f518f2e1f3318dd0fe3edda7391db4ba41d5ca71cdead4bac2613b445d19c580631472327de3c0b659cc85108af3af480dbc9eb4a6c5341b6ace91a8ab42289616c713095d156e3c9878f089a343567799572db219b3a8ccb8ee0cb81aae1a19dc50ef772c084e43f9e4e7eb2a4e0fac8da0f2288a55357b654c6c9456e95a458bb5c46686678263f64cbd3788282cb3dc74d579352a9fdf237efce447444f3b9603d67e355841f23defe0c73221a76a4998ecd751ef5538d81b729331d7111e9688f51c9145fb3a6ec93e3f50ce8283fd5ac4d4c02e0cc1420ea2cf673accf7760e3c2150a241976b23ea60b2008b5be36abc99ccc0cb7cb2b7a229de02dc8fc06128017f101b8976ea05f606da9429328d9cc5d8a59dee9a5de6564eba827d834889cdb2763739c69647ba2f676e30770d96651f98a63dbbd6793b88468f24e0822b76184b957cb23366edb051b94590ef0b003b6606865b0e6f8597050fc78ea3a3264a1a8dadb0fde65e639318dd469b48dd2444f38d8d963f8bd7439353a880c20dd92a4117e5e29f82cedaeb7629061fa68180fcc3354e3de45e7b290fd90c594008c42fcc3909ad7ec9fbdd8a778bcb7b496bdd491756b0ea77ba82210b239e85c1ed7a4d56f9bc1579bf29d7faea714e1e28c3185b8ade721298dcb01a2b18aef3b55a2fc7da5de5e093cef20ff09d14fd954f0230586d6820dfdd3b2fddb54beea29d513b2046417f2b76039d0f8482179167581ae20864a81cd131c23a58867c847570c78990557160bfe68fd98ded8affad200a32b6f398a36dfa9f8d8609c81da9c6404a089aa8a8acdd67a6619d3071aa563f6247ad90018639eafed37818622004a94887d95b823054c2e894100c0fd5fe6eb3633b158792c7bc6462fd0461f2ea6e3cf69888d912770c084fbc8d3f2ea3424d26e3683111bf087871b561412398f265534192a1365d9da12b4b4d1a92e851dbe0e9a8d01c73723bd6f821c105e7c5e6e9a36f971bc25415a4e16a0b34eac7000e046d833591cd91c449a1843ab15f18ecf67a10d17749cfc08ed34629a907910c2b9f04688b242a7a1c96ad371817608567725aefec3ecd2490781ddaf7ad79351f39383030e27d7edb1dd0e65dc17d3cb2a1f5ca144374e11adc97afe2e01bda44c9abf1c52be66d08d8aae19f3ce698005132b2855d6a42729817f4030482d02e53a8bf8d4fcf026d1f66756b6e705fb9a7a38400a546cc1dc319072e318aab648359ffeefd33e0682d6da4cad4aba00c538a0f4b80bc59fe9cdcafc339ce8871303cde4c8ce82f274254e1ad9f65a729c5097261e46ca9ac36e7a8fe63db72b00ec71bbe953bffb07fa2c1742e29a069301194ea136e0bc071543cf79b388aca41197d48213bb65c66cdbf0997320533b18e871b419d0223cf43d9742263abc3e6b3e288bf80252740ea36ce2b5212402f85375ab14ec0bf23948194a0c64a34a4a23d7919cb36f06df0bef0d99d7f8cc53677518beb356c5d5b12080982b8ec0e2e20bc8369a2ad4ba5e80e03c64db1fb1fb6bc39a686988b6e3e3fd5510f49f803d0dfa81759e1d2e1dac6599168b1db7655fcaac7d17de8da95e0a7aa177f4989909db99280eafae448eae1c794a4cf9fa9844bdf6805ab53b550424c213db2f9c95e8d941eec28c16af0381f1d7ac251e95d9fb034de3676446db424069358010c82a52915a2d45d9a1b32ea24fe2432eb731f36d73ff3c3399a6962e30d88effc3649dcbb13c37a7514ad8d50bb0c753c344d7e0181810366c66979629a1cd1e0120b9bc84f43adf8f3dbc22ba7ad67ff3463111e1998d85effbeeb2008f1530a56839150b6b50d3caa6da8ce31d4327b9febfba590b913362137b54a99169f045a62a05b0512809c7a2a977dec07fb44e90a66db5ab5fc7d675152fae613d1ea10bee4907408dbe51a12354d70b823c0513d227c23b303a275b0beaa98606f244a6fd49484d630ab827c720e63478887ea4f0cdf5e02ef458de19af860b9ac7db54867c74ba2682003d230e0423d39803205162cbada603b81e6f17f0044e2740009813f46bfe709ba58a84a2eb5ebd12950d0428c771f6d5ef90a6440c11895187d831579f37c16406ce6ca025d159055ca5e7a478b405e4c1c2bbd6f3c30c229a2dd47553e7d6d330083479b0aae1821f3fd2d4700e881d090c4a2b9854e3160c08d374778490548c40f09d67fd29c5908f634748a84292b17924b37b00d1843ef6a402c428e22019b4c9cc143db73057efcb95bce9c330ad7024d10c66289614021ee50d20828fe6fa6f32c6c067b0d07a334ba035e621ecdab552a886135e79f292db77f58418b6b8b7a61826cc7eb3bf491fbe82e228ef05b9ae9ac5a8360f70a6447b21d630345bb46b3222f0247da41f5975f02e67ae620ddedef524cf684fa8f8c6b8ce8f8c66f807106c144c1123bd85802ace33f3ab53d3fdb3898b49844b46432586af8bdfe086b2c68388000e1f6fcc77f1fd677f9a7cb3eeeeff8bede7b3731417fbf1189ca61d7360ed043571b8b20aa18c822c3f8093d5d011bf8585926468477213cb30f6bb5bff32a00f3c9d251e546c8fbb0c2921b993cf1560409b33ecc5467cc5aad9d73890cd92e614c048a12d6f303b9b326c730da7d69071b6b2f2b11350f6980d470b6c29e6846d1a4c922dd6cc989117c3de8c8a8ccc99aff9003566dce8f331060b3636263eee5de233971e23e085ce794a5855a7d66702b1f3aeb400043bce439613bb5a84c820db3db9a0695a0019a5a0ef1a5a1c377dd288a4dbbccc47597ee22e124b977c7942561fec08012306549702cfe01efead7c19159370603793c5705fd88f893b2976537fd5ab3a743852f8b6e2441127ca86c747f30373a8d3cc07e849a1d802269257a65c052e3bb49546aa2cc760d4da38b84d34516714d73ff733c254c54239b9301770fb0b568289d8253b91fdba38e697b63056a49a5a58846dd5b35ffe8e5991688f78153a743433c352ffc3f69b83dbc67ddb686dc895725868e06b19b92c3b2ae25b4a740e038abd4f14f766a0b92a011725a602a9d0a883b9380e141a142691717c49f88d347544d22f2ad27827889ea56ea9e02da3e1bdcc6588244241811ea06d0aba452613426a1be8d5aba7dbc4dcd3a574d61c3a5e9691fb39eda8e9719b64f9a83549299d6f8297822911917f75d2a46407899c95bcfeed34929156330dda68201cac647e97200860c96fc0d43af11fcc56e0e6727e3e47a320ec4239b83b391225103a4eb96678cf7d2d47061e9495a3530be2264182d8b8ad1926015ed0e9be75e0d34d2c8135b9337a8ad02e725ebdc275b17a385150ca1fe50a8d0853effa4985ab4812b7823cb779c8ec04c98f60729c3005be7df8df6a83003c4c7037ac1028a5a409a15a703ca890ae10fe171e06269dbd14cb3ace3c3dfcc121963f4b9ee1cf16e9c231c2e0d70208e0ac5956b2837401de56bfa8484d02e6141637ce4b7247f1b108ecc2aa4b712c6abdfa0a72624c9de5bee2da59432252903ce088a089d08f6835f4c863d4f5ce307bf181c1d06bdc42edd410dc47535a08fa401320afd75b1ca0d337309712d5eba405e4cddb726b85e9ad12846896d64d07c101e79a9cb386e5450ef407d4b0e85803c4d324fc762b8067bfa7c671e083f1082200c416892458d0048640863402803429a3d845886e152bed87ef00c11fa4a7ccce7ed0c085520a401610d086580b083b004a10d086d641efcb275b134320c9ff2ca0c2c2d34d49043c70dd30c2c2d34d49043c78e956c074b0b0d35e4d0b1c3b4e1a723b7d050430e1d3b4c27961c2999861a72e8d8613ab98c46d7663394b735d870e928878e1da6938b0d336418a621878e1da6938b0d2fd9b583dc5fe4ad348e6c02cb54773452e53da1705115c32cd124bfe734cd799ad3654e1be67c9993c79c37ccf9ae5243a6cd901d67a6938b0d2f3c6e00c08e95ec0f043eb727171b5e78dc008000680568c91e485ce2a57e9761b82e36bc78c6236637c80c00330b000e5a0190c8b3d287611c392ef1d3eb1cd90d1bf985c70d0008000e30f9e4dae67f52341f570c8f1b0010001c6072cc8e0cc39d29c3b00c35f2859f8429493ac8021fc3343af80a40866195ec5832d1dd12974c75d7524877ad8222492fb97f4386e11939a63301e410b31e3ee40fa9837316267b19c35c9634ddb55ca2bbfe98a44d3205242a691d231a9a0d80732e94630620eb5b8139c330c6de733fdfc145d4204754ad727752aee6f33189b517b6913fa34f639834000792af37ea64c955c7c192b2882f207e72b919b17287698e78cac822962f85e4a7b85e9c45ec197259b63332487136828feadc2a0f848ad6c37f82fce31f2c1b8deeadf55bdcc67847fe250d90eefae549e84d243631c1a350067d3ca19b3b79cb9f21ec24d624ce24b61263125fa0b75b2631853a7809c20efe5b6528695ce69199edcd2496124789bd4ff08d8b4b0c650884534fd52244a6240d8fb804b2b4833ef2258d9f802c6987403a4710feb4a83e95bde76712062f354049636384723d9206bf1709644a1ace860c1f255b50438692c646868f83133d03f1137ca58782df0a7c0902d8651a3f717002ef345ac03bc5f7859c512177e80a0d78cf0a2f0b5946ef091fdd3a472e16792bf0b77c1ba660887d2aa893568ed7c4ee0beb716c7b04c01d75fef5062e43aa0d64c93affbefacf2871211da5b449fef5ef9d1555cf6d7c08b5215f6f2f6c43b69d2a725892c61922fe3344eaf6ef5d3ad909f4a398cdb79d5dfb623a2643d80249c7f782fdfb1c0379c07f9f830e7df27ad365d4b68d5abaf7fc0423a7811f8b40ee515e44ed47ca87656370e1db21b60606177e665f60a1a02929d9d6ecc37a2f2626f973c53b45d5eb7c0d70ea478bdce136287e8b62533de144cdac0bacaa0596052b68c2aaa093d914581458211fd6a7085a8f6db327b02628c128b34c5812bcc8ec08ac086c4a46cab691e67bef3d49258eaaee606c1384f00650d218bccd06b9b71de43eb971d55b91383ef1ba297277322aaa34967397eb2a2976c17b628a074503418a184557b9c9006a520802c4e101ac37c83ec4fbf14081f056fa35563dd1585205599c782b3a803fc8fd3798c2a34d741a4d63398dc6d26e3426e47e89cd2dc76d7229309e112a9e0e971bfad471401042f4a9da74e16e511f16ef7a9e2228abad94df8684930fa1f893218410daf8e9e4e6e4c626bb14efa4b1dcb59611ae105edd7a903b3148459b13e47ea75c5445d583bde58e714814ddf5471bef1869ece0a8f2d2741a2f55b7ead5a7922123a3b64d08efe434f189777216cd6684510fae14b96790ab6b1aed60ade525ed486b384ec3a1349cac69aceee2d358eeee1a4b635dd06fdfc13349c8e862e6fe8c125dcce7957089f1fcb6f2936fac8cda9010553087138f86fe0df45b7ce295faa59841ecc1db11a778518ad7350d95434520e48e5cc7599d1c63bca22a46558c30decb156a13f6a213f29db6a8822cfd9fbc552b5ea95a5157fe616d197a32863f38dddd5cf14afd5b9edc87a5c4cd9d6c6336f1078b9a777a2f8b07797cae782cfd1f2344277c6cf9e23e2c4c7a43f9e676e12d7faee7e8b970670c6faa0bbef68b3b3f31cde2fb156faa983b31dbece21703b1bfb683418e6b3c5f71cde7eff6b957763aa7a6932471998ed64b2c08c3988ec9401eb37554d009080328034803b8fd20aaeeb515cb1cb3f5ac83ef57ec1984fba6a6595e6275cb446b9cc6e96ca3459973ab9d1d6f2a138d8bf9934d1cb3ede01703b51ed2058fa53f5e78ab79dd0c5e5783579a22f76f2a934de591f12a41b82eb1a56629a74da5a4bbd1286e518fe4fe0f12cdc78d0c391c0df18dbca9bc835fcc855d723612a164173eb51151af56a29317173655161545e60bd28577e0806463434a91915d58471429437a480a8ccf2e9c038bcf9146921b6789860286aa553062d4009298ede01922bd04cda67a2b57aecdc4eb54a75e133184088921e3c234f48c8c96e99f32beae1ec765f42b12d516f5a63b2c2a0bb7b21c8a3556a6852b72e788a118962c812230e4fe58c38996842046ee22a33e5ae466990801814a4395c71766a9ab2ef5af3e80752167c2193e49f868f15656dddda9844bd28f7174ceb802712d0264b9e2add4d55f91fbb096a8b14dd7c490b45a33f08571d4950766c41e62884383833f5874a9ffb345eee7668decc23768d4b8b08d2d341f17bca951ca18aed93eabdbf694fbb02c08cf108146baeb3c9c1c5c7568e887fd606f7585affcfa6560c970e1aec3badbd9c82e2c43092bddd28decc23572c5359ecfb09aeda507047a0afab9b98ddbe0bd60df321b864252a01aa8f2920a168136ddc169a363034776611a8eddb8372eacd2f97e1c9a0f96fcb9e9c0c5513fac7ce11939e2cf0dabaebc54571f2c3eaccfcd112b46e0ba7a2bfd2b755557ef65864d95ebcf15d4905d98cb19aeb93f7165757739c745d48c3dc445dc8c71db90eee67f6ea2e8d3e7faebd3ca587477f36175c781db2694d3649a6aa44d91be9729c40b22c906c5b60db966d07c5c19e32840736768c92e8c7356733fe2cfe7c8a7d51f24f721aea1b931f7e9e53e3749504046e6c3f2d227098be6e346feb03670e1a33e2d27a2d186391c306e4616800f0b860c1951e5348f718f69ac8fd89481738dc45e2c8b31c69a6d0f267991dd0f94f0de7befbdf7dedba627e42a9e2a8e538a1477341a8d46a3d1e8351429292929cee5a003e411ff3dea457e1801e0e925e1464b0e148ffc7ec63bb5605785d8559bc21e6e8b9f7af09691ffcde47a68f85e24eca107afefa607c9ddf4f06e7ae8e1c6888d72d296f1dfa31eeba4f274e4f7b74fff537e747fc8d2df0d79d0fbffffffabf8444a4a4a4a6cc863caf76e70e1bfe7a558a1186c4a7e0f210ff9a216970d9e5ecbc4b73f3ed60259ba05f26869c9dd5eeaafa39617d3d272e35cc4c8ef6d6b5262e099b0e8d17cc4c8b9565a4ebff7de7b178c148410c2941b19ac55bbbbb156eeef884932d6c6dddddae47e7fa192a131a2a8b29b6993fba5cc6e72bf1c65154aa6b41c2d47cbc98d4f724b94654a5aecdaa4284b9992725b8154f6a9a02aace25a2c7bc7c0335d2f8e8e3c9256d25dc8d0c5a6885cb1e40dce0d4e2b494e2a954aa552b2e5a5ae3868d96ab55a545423371f81919391a93636ed312727272747ce542a95a2b2255bb2255b18ace232216568644a0a912a3904a7952467c95ca5a44d2a954aa56411b992ac1b89b35aad56ab552a954aa53212c49b88d34a92b344a552a954aa99b3f2d26ab55aad248d4c4921522587a8542a956ab55aad56da1c816a884d2cd245560e55cd9c88d34a92b3e4e6e6e6e6e646a552a954aa99337366cecc99393327f76b9b0872224d14426f6e542a954a1587449b5824ae228bdea8542a958ac22a2e8ade5471515586a65695756f4655c693d09b9caefb1f8d5237a9ea4baecc88e6535148a553be6a436fe8a43792dec44eac32abf79bec389e24f7773854bdf7e250a311e5503fab4c9751d52677d5a6da44183f79ca4ba897b2c1858ff2547bca539eaa191c8007ba1a34f2fbee7aa96dea0c2e3ffcef63b409467e6d4a6913a94d2f3af666af4da3fcde7ab2d7a693fcde823293fc5ecc6b53e8b50924fa9c784617a5f36223a564af4dd96b936d1326237b6dba2a7e6d9a2ad96b53acd132bcacd4755b7d2bf0e51129a5fcff22ee52c618638cf1f929082f09e1922595724e5b03897a58e44ea194b443949109f6a9a0445192fe65425a72c28b44de1694b92d99993ec1d4844b3021c4e7fba3ef3455538a89f2a87b22bd7b8789862921c025e0127605855cd694e299ee13deb469d3a52c3f99e47809a74d2299be4fe8540f33284f7aa8fd98197b1414110d69161199140185408faa5dbda34b942080a01807674621e11e74782c7fd3ae268e91690a7e750e7567919e9c49ca9f89288451370b2622c8fda0a7f5e878c75f4aecb11d425972472ca131babb3b144d7427a4bbceb0ecb5e90467bab3b828b884ff6942d13ee79036a13c8a91bb3391fd5ffb12d9412d863ecd229d7754975d244ae2752299ee9af4ee3639e239644281c58d8f9aaaac7af14ad418d2cf221eba1fcf5f0ef5371322a2f1131422d3269190249e49a40213110da62778b405425c489b68aa3b153c1325c1ebfa9b9e60fbfe57ea27fdc99be4f889c45dd63561e960b5b49032eda9aa77f4532176e5fd2db0dfb9bec9cfef7a5b3709bdd74bb5d2549b6eeb8a24d2d316bc13e9fbe90948a39a91a57413714e3309d3d4981728910617769236757ee55debbda38f5d7d969743752d59d2949056b752ad1bbabbab5aef1d8dfebb0eb3abdcb7453b1cc8e6b6de4adf56445d2b5018502850370bb91fbb9990dc54b85fc8ad9a50cc215146497320ede024191da6bb2eaa3b6cdd564ba43a691960b04fa56b9b6600d24ee47a9b56f26d181da4fc1b6a9394524a1d7258fd80c9ba833d6020cea1bbfe5a43f7a68c6aeec554fec2359dc7baf8a14b40a2b8f2650e9c105df6774e025d26ede02f5fca9f3fa5bbb6c30d2e2e12477e38ee3730307efaa14bfd489c03b2a31f46206d02f2561a88892574ead8387538204070ea0504c87b79391807ec0cec0c5634cfe3c36aa6bbb75a6245d3dd73598547f718dd638c1e3d7af4e8d13dc6e8d11d93e25fb966191a9b16179e7211f4aa839003b50055d0053d3cea6921b9676570e1b7cbd407dba39cf4ba36e71ec80a086d3c9c941b8269ff7e23fce41c10a147654b7203d130301f88eda2b5c41afd466ce81bcc5bbd97976a62cbde5b392fd8e97426addcb66db466f0ca90d334edbd3c7bbd9798bdf7b2e5ec65efbd74f6b6ecc5ceb6514a630988b8f521f68efe177d8093ab15080d91202e39025d4622663e9e99af0491202e9908a7a1c6488c8a1994fdadf40c3ee4aa7cffe7dd1de516b9223a3b91223abb73b5560f42ca0a2bcca4463797e575e5ee703ef60fa70eb13d11b533c46b43643fc47dcfb32e338b5fd62888e66392c0a18b9c19254631a057e2c62401519f1ae2ce10892dda67ef726db613eaec853fa2930fe8e1853d4b26aea901af6bd9840fdd41eeadc0c79e924999bec85946c4e7210bf45c2affceecc22821139a5d7874e1930b8b72e7c226d966a150d640275ab685e824b35a74b15b27ebc89b3f9dfcc92ebc6926d9b52656860c119663fcfb55f385690ecd1ced8becc2925471a83bd1475c73f29dde21129d7016656465dcce94cb5d4aef803332047daeeb5e49efc3f885238dcb75b1c611fc0e08c3b8fdac83e55f3526a1d07f424ff331266f2d4a884cff4108bdcea7414ba52903d788be836be72578634c422d34a31893bf38c81191a3f918113722bd401985b2960e3de3f7b7746b92e649a731668f3887ee7015197e34c2b2c17bc96fe5b570706ee05bc95204ef845f09be0d31a0c1050f053906b2c411c013074bb0210bfc171940e9446e1b29c8a1fabc76683479822cdfca12211a08169382a66891362539c4e347d278e993e17b4a32fc2e2cf9a16fe812fc891f12a982579f787206d9b18eeea07da84e0e02bb98f87529872e513983775279250fbc2740efd1807aad1d40b0785b94a28e0c71cb14251b55cc9d9d4305c932931d6880949187a4812c9f0cc41bf04e3204af244310b9e8c911c70cdf7d057e03dee93d20b2038ff4320ae63944a69c66fff3afd2a65b2f10c8d27466011ead07cc8c5cd453829381846002d22c4954c5453d25d006e1a29e122d08cca1b729360a97e6a3f3f6231b059b2d8ccea378f2b3a28a2c3cd9c610fd80b966c6bfd03eaafbc9cf0a2ea8c8d9ca90da0f98454fd23eaaabe56745152b2d5b1952ca39a79cf1d169323bf959c10591ec166719f6b733a6123ca397a159c18512b2d3995f11b0e083ec6f55b20cdbd53df9d7ace0e208346f34bf22546106d9dfe2eb6fb94ce29a35cd007646bd66c81974063663c66b9f013b1bc4e90e1e01c2453d25288843448cd86399bd628c32464fee6fcdc71b0397e7dbeb27fd796112bd50a49c988465965e9b0c59369f523aa7dc222a92327bc57944c6b00fe219cbb4e62b8867fa92c69fd7c33c2f4a9f4e1a0466fa42f3f1c690e75b1b99c432fa737f4f65eb4ef38c3a9fccc02fe68351d5d3b9580666fa12459a413ccf29e9c4418ec82898493b681fe3c3ce8c0f33c4695377b613bb4f4dd334ee7591c4953f91883f73847114f6a8ec51f651d7cf979884713eb04cca381f58f62759ce07964917e70343c53c831c91e3db2bc3b0fdc17d56fce026a3c290af77cdc77b429ba4e42106f99851325146860499086d87934c399405f2c5bdbf25300b418411322d831b3ea104e95b4c3c63272602c4a5941527fe305d9ee602bf837f4749dec54f9dec2efc11fc575b4f38e62d53dd6fad38080c07e142c06aeb67ed32d7bd3e8689c8b73fc007e2e27ea84fb51e75d60804d4de910c7f904040f78eda0e3874d1451745c40c44e74c25a24b3a2595d2e5fb4bb7d97148eac032c53774d7b37b1e65edbaae75bc95761da7fa59eefc54391dae438737ec4d870e1d1958b160ba1cdaa4838c2e3c208187f7f27aa6238eaed3d1d2d2ee9fa384f24db9e1808393a0654eab813cfaf7d74b155afcb28c7e63469fb3cc6297ddee5b812c80b01f3582953e8acb8f4ed95cc8d0fb4b88508e316e71630b765d302b988b8369f1138ce41a83f1e8a11892f1265a01617b9452ce4adf561c3385137aa6b8673b008896104124814070e456b9195b33a96dd76621131b4c552c7aa07620bf875040ad879b0b7b8f6198cbc5c5e5caa0eaad400cc35c86a08d8e96166bad12d9e5857f34c2d17559ccb41efe3194bd135441151c026d322ca2eaeefa88211451a8a0ea0333db06552ad58af6ed40cc8580cf5a7ffa575a27ad4398f06a0091901b4ae157371007b2fc42962a56a86a135c7517dbf472d7fab006ef546baa7370f55edecc95ce862bb86a81abd52a360542cbcd34e6ff6bcc631f2077bbdc3d4806b8be7e022e2e01ef5382081091243fc533f4af97fac3c40df296642262fed1005f8c8f62f6b7d3dbf4f3ff3b76b7d462e8ffbf97273b5ec1c2ee86a317f2177d0bc1fe6ec2c66e7f39f647ee4382dbff1c891c1d421cc8af81ebd3c0f7299ed493a2f201c9401acd38a941e3868dd20b94951928a54e393f22839bd4af11ba4fa3c6437ae7bca8682359f288e59da5c381a3c3f19605cf10712858de5dc5e2b14918661cd86d6a7c880ff195e645baf32224aeddabf5e019f2f426a21021f1621a3f71e4142e1e4bff073b159e155e161e175e185e176fe50aaffbc2ebfae52fb1f746a6ee14d6cd0613a954bd3aa52568682eec763ee332989df11e2c03eb06a785a4c8ca488b61492fb1353e28df1ffb517077217bc1750e4949087b8ec897c8d0683e2c0e3c98860c4d4a481338aae2909c259ab73c78c6d6e9683d48275c96225048743232e99090b45a4aba4b72afd6e3f356947930cea4d1c90d1bd5e486a648de4f1be995e8b30f2702c2307e84b937c1f84ff07f08cb78118ef11e2c233b7e19ff8df196c38e047b1257823d077b177c7499f481560c9170973f1e1c23cf96f796b72d7886485361e3431f1b2113100a8cd08b14130f7ec1c2c2758a86a6554270885232940cc23536dee5c992d1c883478f34f260118a079b641076242f6cde0b29834ddceb2dce3c1804228d4e6ed828c96a724353f402c33cc34aeb05d7b5d034713b6fb9cc833fb984e30dcb4b2bc781921c9b1ba2620a6fa5683eaebcc588e1c11e2c583062686fe1bf9721e31a59ad62c8c8f0ffabd1c9241c6d5238d29c9cb7643cf7388c956cf9068e393852317224dd258945baebb8fa4c1abbf05efca91071b0c8e3196f06c779b0f6019146b108c481094713f256db78cbc68796bd35850c9907671984bd853d49774a4eb07721ca78c9c510e27c890eaec42fdb0cc2300a38c483af2c534d081102858a0655aa9a3f182e016968e688a0404e0d38d39dcc9c5974d2554f5280ee74e864c08183f052bf0c1a10ddb576f09cc279abeb6c641e79925984c4792b2ac9fda82886dc62782f2f87700e271c0ced968017f425adc7c71d3443e6c1314b5c44bdc10554f260cfb206c70d1c6d702ce271e59115b13811cd1ae26a6cbcc5816788441b9a8f2b7fb84773037bebd1d06fc3fdc683bd5db9f46e4d2de11fd9f024de6ae9baeb5afa060d10074b3641d25b33dc28e5e872e4381199e4088172e4f070de6ad5ec0f873ea5d46b340f5f802d91482412bd1793fc502d5de9f108e5fae1d9436eeb3cea3121df473d24dc8d1b695c67b3116f050772d2ec7a9e29feb3e209c0a4889bfd5127f9659c115dea3742ce46749703ff0b3c93733774078f440e1e792b7d045445177e2f3e51f27b08f37060de4a5f9946034c642fcc5b8156945d1e145c6a81f1929f5e4cbd38d4cdae0c8f6aae97b42d3bc1211e1497b77aacb72323bcd45fef68f46f890c19058df0537ea5fe2b64b4a0690217b005363b0783030b63f395556c83792bfd0f86a040dab60df2a0c06c5d1fba6b2319959d7ce801c829e36666ec2701231393bd121f83fde450d75b8981f8d00391f918fe01e6e8ef863c1e863f4449b91f8efcf05e9e0f6d9ad3afca45096453be07591af278510b9429a9bf197d353af118020b33e4c1e34196f9a8cf9b1caae60861106a81f2fbfade8d9c2e0f863accfb897b397ae9306fd582c28d7fa274197dd792a1430923d7d2d2325d5a5aa2063c2f6ae0de8e8fba5ef2db0d39b0a564afd62a4496a1c7eb1f9df279a9df7b6f76d893b8d06293d67a796eac9452babd56b1a6512d34fcf23643247edee3a93f5f4c05816aee7bf0c7f3dc93fd5dab7ffebe0f9e0779be723e7cea7fb8770f77c49523afa07690c4a88e52b7b123f40d85003803a9c32c6bd308d35e7741e8ff7879082dcaacbe536cb3d0d1d252e14ca537b8b8c05a9520c92c56001d58263f2cb62ba9f9a82f2bf6246e0e3333da0f3ab307af0bc25ab1b4360af7467e5978008a4c3df808223e78fe7a38cf5fac7b901400fafb3e7cdef379c9f900facb1d41443e88a38097fa636b43f870ffc31d010477801713c40e57961caa87fba89783c5bc18cf6bd1432baa5108c8efad2cd19c85c2ad9c0dc2cd1a1c82129ca7f9c0accda0f6a3bae603c332d468e0127006ce6c19c5e84cad31766aad1d2dfb2cfb9096dd4caa320acec019382363066a5eb179d15a61adb5d6ae8e3b1c5d773f546284504208e5d78bca182116310dc362adb5d6fa53eb51ef3d62dfb15dc521a24fb7469c4092b3959a01a076801f35571c2a9d4bf92895fab25e1d595ffe85491a36eb7545fadbd3a757adbf552a257dfbe875c94abffecc1c5e99a45fa9a4549398a45d52522a29a5d4297d2969ac0fe3b94db45e99f61a362ffbf26df62169dd072dc717362fd881a310d8245cf8f15f955fe555abc568ad579b62f5f93ffe27a61896fe98fcf1f294500e410a9afc32a490625285d01d3e8c5a8c2fabb46994b5204364af695f31ccf46b6c48b1cd22be7e9537064a1fa552ab9455cacb625356595dda235c98df10ae6022fbc799327a63c8f49f5d5d9906e5f411bada8f78693f502a8d326e998661a616e75608dd553694057284691e2477439a4af7840b1fa542391b840b5feb31842b68de8ffaf243b0be6b3e6caeefd846dce1e8aeb3732af067a862da0f94f8306b3ba0e4e85f3f26716b8cb56ed75f169bb5cad75ebeacf4c9d8d45dd3b40f615a0f0d57ca8d5699d24fe2d6874f3e2449af1162947d144649169bb456fc3294557e108f52457ea8561adf3578c285d53b757fd97b5492aa9cd13d734adf73af723ef7cc7d9421a594f6a0f4dd22e1a2a0e35c4f86ff46d0fdbbdb669f5d55b6631f1f73eb3eed11463e3acb4741c13ed426ac0736391412196ace592d6e7e2fb54710d8858fde4be5466fe5fdf3814d4685e07c4288d2f7e8938fbe509b64f76763b0fd4fa5633be68e8a95cee8d7855dd70cbd47c50c51f11f29b6f4231e465d8f42cd7f1789ce7e59468f31b6c9717cebf1c5f85e0d5b47cf240c2cfca7826ab14eb8a8ab0224b973670fa10366b0b093d2ce360fe0bd17ee48b3788d99c4a6466b9b5ac7c5beb3d84014d9daac6258cca19c1c84e6223e26f4fd4fecfbdb0b7388bec5309c99ec80041521e822a390a042462657c06d647234828a43b064b27d22b01b5f2293516f25932d27822c90c5881732a5d8e4ad8cbaf3b7e20fd37708173e4a9461f812e19e64ff67692b1791896c0dbd9721e02a1731737c7f93f7320494c94170c83c5c196d005026c7aa0d00092a5232b9024800c146c666f6adb56f33fb94abf6a3c52cc53ec39e24b1275dd8932066edb4187e51db016b53287b11996c6d64b2e582f812991c390cb2f857c8e26fe4c223efcefa80ebf365a74c89e651939aa64d4da39aa6554cbb344dd3ac0f1b8a01358b192ab2cc62460cd97e7c1b32bdce3ad4a64e8f07a06a5f72ce19dfd6ee40190a667bb5c6837cfba1eb6b9b300e75a1b0cfb8886b04e2db8f0016639b300cc328fad8d74aabb55f312acaf998ac4fbaea93607dd2ac36732e674772f6b1c2875a161a882267dc5f6f310a7b0c15bf720fc6613ad61851d8f84fc5c60743f7aa7c0ef1c1fcbeceaf34d22350587d8a49587cd245821de99c53ce4961866913565daa7d9975740784fb9e3e4a4765c2edf71133cc12eecbef7a18e4d132966636629b84fbde2ae1f64398f782657fbbe1e8ba2a5c32bbb8707199928c0bdb49198daab07961db8216b1277133bb85d47c4c54fca924bfb7e257f35139d20e3ffff2420e7596ffbea3f130bf7e08662c62107b58638e3714122fc8f561671b8a01b972efad643c4cf96eec13de32b927bcb1072cd21d258202dd5d14c481ac36b5da8482ac8c459bfc8adc0f71daf4aa98020e4492dbdfdd29a52e33fa244bbae89322dd5c9404ec6dcc5c7bdd410c05b7370ce5499d94ef5769d38b1bda1443c6c5df5f8036fdf7f7d0260dc68cefeffc440308574972292004107e82343e4384b37e922587b2407e6f05c62936c24f504877f79021fe5a589861288f829d66708857e88f65eb9be8e78be6765d5524125d46b8b28d2874db50b66dc35a43d93694387fa3db86b26d1bc6c1d0aed836ba6d9063f9bd3efdfaf519353360124903cfc056efa0f12a7886e66368701049975eef404125b907115050624641c1301419c3478c9e37dcb26298352284cb610482f87569af6199bde2755dd73f28648b617b73df10a448e567c514c49061c6e2c719c433a531d2395ffba8cdcc5e536298866134f747fad6b3d89da8f5a0f3a164b54844603eaab3d4b488c5f93462d28c947e8c33caf954e239a3cb27259dd883429e19bc5e1fa6c1b4cc84200105339c792b3786a873900ef571ffc2d0e45b8a356b2da531daf9f6e935298539da2863949e8d38021747b25c8956220c351e227784bd4456eb119fbeec49e9c08bff693d60fcd37ad82cacccb8f931b0bf0c0c5bddc1227e822c088b4c25b89d59d899c5098d3f409905c11be36d28cbb0cd02d6cc7898f6d82c9ef6a366f1b42030c3368be7c1b0187db20c6398f6a3f386190104ca8a7022c230530ce3a9c6c1b8c224e6170428a8e0cecbbaa28bd2e96429b8a6c693303c52838339acfe705009c4b102259b81610c0d0c5bf86910a73b229c6427d5c3419c218c326c516a7e469c68c2f37ca55da8242df864450a158900000010007315400020100c0706e3f1681ce879deec0314801175984c6a501dcbd324895114c49042ce104200400006044064a64d00ea2bc09a2724a73057b2ea2914d79a31dd40a7d5ace371bf5cc0ec7e143766d196a571e9a6f9fa120431361026765f0f5feb6d95d49b0fce626d2520fccce42126174a7c165f3d34167a32b3e6ef9447cf161a708fd8c2e84b411232f43f7ed9399ea94c4750de7d68baa0f55411f6a26a6f895e7d75eec9ff56fbeedcd0d6d086ce1f1c90f0fb264a129e59b717a09b919700ce2f411b6c309464116230af4f1b0b693b3077d307949c015373a7287e7be08642a716e44f7720b153ff0b0070318a8ea71071b9120784033ddeb00785ae65801ed27a374e76ee29b2fd0d626ca8d76e8b983667c810cf509d480c8e4104866bd2cb9b9ca0440a79062d80a5ad9b27c826a57213b889f24b4e837362527b489f5c9e05717262d1e144017c7e3e6542b512e8104eaf74620e6338067a29a1ec5acdf8e91cc0a444020df92f473c42127e15b04cb3f20bb731823a1f4d5f393f5475d53f352f9f0513ce28f2351593bad2a12b14d93cc5acc5906119f7f393b08d7a09b734865cd45ddcf09400fde55c173ba6a757a0a213555f82450c7e68e43c86f0245801a9c464937153cbb64928ba99a61272343d64337ec093d2641fa96b763d87af706036d2a84df26752fc2fec50da043569683a2dabaeb1d2f365cef8836ecbd3ff00c774cb14a8fc0966dd733cca3f832894a188534023a18f35bd739506213af8cbaacca0af9de9a691f5caa70b67baa7b84c14bc62a9fc375a54075c51cdb46538ed3cb503b0522054893d08aaa5945dcdad4021e024dd092338d7eda8a48554b44da76890bcb0d16b51cf5b113f6037e746104e6e1fb4049d3d2780e4405f817c637855b1f0a97082bcc4049181484c9620554c58580722e54047a88c19d62a1174e9a92c319f1131670c0e0956e1b0422159587c7d5b401a071ce8e94915313f28630d2d9940ba30a566a2fa0a9124d72c84ed68f06f6df51b2b1092d276c0916fe906743dea82153f62e532e8bf96893d02ba2c6a1ffb84e1bf4424310221451e15e967df3a01e4866e677ababbaa00d0535be33e4500b3d540a8097e1bd8c12895abffe9f44fa37ffb0c6fbdbede5daa8e67ce908e21ce515a6a18ca9f5aad020e3b4504cc6d42f4cc3d48b2359c578e38b7f050979431b29f1df293f0f5ab1a88e621931e2b76e902c744fdfb1c8147ff151921638b982db3ac9857194dcdafba5e40191383462cf7465d97eae26483aac2f34476de17a5d8d6848c104941be9ad0d822c1992a07d452b348044a9e0e225de2f1fb249161c114b13bd20dd86b0d8431ffd5a258a8d724402a49d502d8b0bba725181060c2263cbc8b1c672d957e7d1810806e5e902b190464429c0aba642517e45f21b28eb46e4daeffecc7563159600f908a948af5bef66b4545df539c2da998fd802a5683017dfec0effa19efd64829b1900520397596dd40048d14a7c8bdeded8368e6953cbac6f05d4080bf69f23761b4a8eb1b2c5ca08a3c5bbc02e92a9074f2407d4605c34ddd4d0f1eb2b9e90176e1dac66760423435192e7236aa3e7217a8c4582600ac2a317ed69a5d3d3c75ef984404594d03baf4acc1815ff951a7bd3ff5f6d922ed16041042411f969805e8173fcf43f758a671a238c7d0de23f216b25e9ffc1605bc9bfc058c72ff608c08cc1c8b37cb42230ffbab7ea49d3b6d884225cde5569436f52b172a263f6c307d5ca990ac1900e3318250fd1e7ae86cd186f17e252fc1b9d322e1abfc759998e5272f535456bbc842a60afa49c83987bfa283804767777856609b88cfea3d5a0fd2e9584b827123fcbf06db004a7834475991b46571fe3edd621f3644e0e4cc1aa1b8b321a0f35db2a80bb962233a653e55e7210b61403911401e968b55c67cfdb8443e0c5de655b6388ee57e0a332ea3f798017663ec1a0a95be3ac83e7255de14de951eb9983762656bcaa0c2c609e51cc99805caf00fea0d41d98b59253bcb372009ad43a00ad252c08e7d3aaf27c0ccbee1421cb004776b1da766153e728447be7bf40fe2ccd10f22ed900c9400595405edd027167c368edd8ff59bb41249da82c12aabb75424f470ad57a5d6a230e3a708fb66c4559b3d2837e4fe06103d5d416f7a50397cb8cd3050bc310ccd1e5685caa77748a832f61435c96f5b32f19312e9ff3373cc9438f69c338babb2b0af74064afde96a844e59cfd17c2ce70765153e7a430e845179cd3d0bdedf6c5931dcca33165a725063efea276b7c835507c8837bcf5253a1b3a15fe51a3923f28764c5ce369e046d4b0f0b252c30c545884691c03e6b128915b279129884290fe188d7a883339633c9b552e9f35cbae467bbe555c2dde11821effe2d8ce07c897a94d8e34308ab65d031eb1b007d027b00a20c0124c8bafd9d67cfd01b7a0783bcd131764e71f6ad142e2a3b883f2d2034f92487a4e559bccab584700a93482b0e7dc1532c42dc1d05f8b31946a0d5ce26a9529e793b85bda9f32ee1094b4998da848b8ecb1b2495a0c2de4c07800196df7fce5199ae8b1d97aae2e377c4be27ee6244ef2fa70699824f8062754234b6a0799fd74f97006deab6bf909c7033077c84da13827b72af8e01e6373263d59e8731369bee19a9776005722f7a466f0a9d876b52559c6095e8ad01bef83a7fee1ead8ba77de7afd556d1d4c6418180c5c8c3d9a2349f23d7506336bf6990aa7cb2bfcccd03a436f7f754d75e0b318d960c13288d6d2661aa65b1614d445482d1b169d6a0dcfccf235f7f4fc218b8d04bbe241498305c546307c2b8aee1eed7363fe3bd9596b5c962c13ce143e61bfa78b96366d310c8b23b10428ca00e04c7be4ba2f45fe10434a574c35034177ad2b3a1a13ea7ffb62c2bfbf800ee3720749eb9d061bf659d4ee327dfcfeb99562e620cf593d513d4d3d68729073b805c81d2cc060442e43f054a4711413726689246ec030444921f4a4989258b205dcaa175ce7000db0fec815e2243a8d4257043a05e411953fe4d2da6ef58c61a410740d122ef0a692e60351efb22c29d14743a59419dfa5abeb3b1c53db3c2a7c0becbe87446207fc84a85b120cc408bae3f3f2719eb6f425b426dcd831ac678353f10494ca0e269e3c1a1a206e691fcd5348089106be0d164470e3af79366d802812a5e75618dd5b0e18a0a373167f6ffb4944a0a0164bb5a7b6c3585ee2ea85195025fdaa020cda535c7a484f2da6d2e6e99928672779967bf8aa4bf10b903db478066e8a5565f2bbb1cf3723b519e168fbee39caa2fa279e45f0288b49eea433defcf2390a8c4a2ea3ac2349eb0d416bade85651a7a1e5d67a5b004ad32acfe35a877f6edf0f4a92fe0e351e2198cceed4734ebc03e7bba12897a48615f529f2a4caea2e14614883ee384238b19d515d160d4741c3a0c9d2892d079e7d805305c32570a58780ffa09631e22136fb49b6433bb6a5294808f71dd0c142ba4ab43a4d0ce7909053e033e937ba7216873d9a406ffebc8d10b56df6e58cd6bf5af88d0f1f6c7ac010a21f0b657057500d4d7a2ea1f302303ba6f4e697e736e4676a16fab7c100497a4258d09b740a66c3812b2a75fb6a20c591ed07910f9143256fd0ca6d9c40dceb069816384fb3a8d0e453c0f92be1a1f8839c4e35f49b6001675143a6eae3ed73a042994b3d0cf838a563fadf7c854123e034bc413fa62afbf887f2717e88068755ab420993772286eee0a4b0c81661f43cf37e08c10038959382ce789e33cf5aca78fe9c959dc9377a48916da3e19645f908e8910208f690c0295bd1d72f98c5d398d6b5d5862286b2aa916d063a5ba2c71a24443933532393f3b612970b874f7a6d0c15036b847326a39b22ab7a656fd65dccc815bb31e5880dc7801559569235a2e5565ea38e5145da210c5f2f910a415e4a55811281724b1c0e750d34ac84cf735964409e63ed9763a4c006bf0ba64c68ebf457080417ce2947e0cbde87b0e23c8164b9194d8e6a0a22fda0f37a4195a731670436f2b04fa0bb81058c83c4e0ca9b9a286e51a330773d88d5549c1431c4928619a22b31c62159ba5db37381704be38c4212a0f89076355bb13581015485b87bd2133ade04eff1ed088e9384726d605590e37600fa0a1496994a0a7a4c1baa48a5a02e63f8e0c5707b69c634bfb9682d8478a337da41a6575d04651341edee26bd1c8d1fb5736fd1d13a31297923072adc068d481728d1c9c899bba6dc84d0d3f86f67caff338ed1f5ee0986ef97e370a21f2336f34d977af59a95102e53a8c974f1f4931e9f3a8ccdc5df783f0e153ffd57145d992fd9e27ca5d21103e94f881792d5382efa51989c987f33d9822cde619ab4eecdc7ce9683997e50d8c4a8505883a72ac049d7b3b986671abca977a0c54f8a4e5be0166c6f8650bfe00c1e89d50012c9bf80e8b5c7954f678b16b93fb0a8dbf37b870023f04386f4d9f223219075c17cac7780a2f71394941d1b71d30ef960a4fd3d02ffb9ae4f734867687a33218d3d55eae2eca41a16e7511cbfe4eb8e778d941dccf733b9fbd004b5f7b5afa17dba00ef239cf96141ca4185f8fe78be766b8c4d76c5e88c65fd2052e70a96cefb0de6fe8b4a0bf221500cd671132e11a29839c9bc31646937ae4a4249480d67801246fbbd1b8157f09171915a0f79164830125c441c7fc105064e1b38a6330a7231b5750d0088ef28e34faf45028c4c8a202a1370b64da72d50efa3251769a770a4722a36a4a82450e46e8e0c74cdd6740c882585b72307f060d98839884f66d8041d4935feeb21469ce08edc6715b4ae80b64f7d25d21d02a70a8dadcc3e58ca00e858e95ff4f8800f9f140f94f0bdc82b744c7103fc717c26afa27865e8f3f3f4e77f0d5b4c22577e94c5115c7f26d6bb32a0d89c9537058b29a327d8dae9b909d2bcd59437de9140dd068845ba88669242e9c8cc4b59cbbc75ed1c47652866eb73533eb940e03fc4691d3872c77d5e372f714944bb55cb40ec7f05184bbffbcc0452923af796fe121501f734cb7af799ac2a6db852d141392dcf2bcb0491902a04ddf975e6341d64f12623d2bc9c8918988795a68a5820885b543949a03807b8833231f6957b1db71b3b4293f573905309ca1a21976a433751467149641b543b2781f590aff0b680044c1dbe5e859e9bda52cc0e8cfe44de44da3976693c188c0500e10426baa73b8152b9ac6e162d3332fc3f424d2d98b783b115a557a52773a89946729a85f13d1798b764f27b6f11621815765c26175f47af0161e36b47bc6d0618910683bd13b393d49bef6df1d9aadb1dee8413b7fde8d9a4967f10ebff9f7f21969d49e97a0d516575c0fe4974c26d0c1cd31d09cfbf83bd25507f9032d10b838dad8b20e20d5151fce190677577a165b3c87a72c76a2ed83e1c49f87856c1c1e6e3609d9a3d270b4ee5c306fd7ec19c6355b6c9733768794136978a4445ef49ac01cc74d0ec753b3e5fccfecfe40c37fad0a9ca0c75bd003dd26ec63e8908c8a84dd44455d7326c6d223d0e5ded6f0ec7bc8b7e55534781e04b68c9768e76a234479edfe2420268143737b11ac1123ebb827f929a2974d44a24d36d743138bc7e62d55b6b58729bb31d5f29306868ebd6570e5d090304abe5bd1662ebdd15d4406ebee50cbc1535be820eae5d0e3c649058f5a7219f7d683ce0ace10f5cb700ab2ef36feab6a4f20616c031c330d9c7d3b7224e2edf4af48178a11ae1b2b22eae1d071f1a1d2514bd2be6594bec54aa5d2ae46c50a514f4767889d2bf6a2aedb76e0425b2e9ce5099adb4746b66ea897879956df5e2ae3a307418f89ffa72e735be18d207b77f04649debd1eda00d1cdf1b5f425eeddc7fdd88c9608cc4901f0ccd8c4d6917d33f8a709f62a1880dc683756f0a73a5603664322f4761a951ab4c6d978e696db47b457c4461b9b6c112429e052e0280024a6d7cf4f044ec75db9fb800296a339850c0c0b0437864743de3db8837a20c258b0afd0ff0b85dc8594e939b0511eeaba68730d318e31bd787e06b74f41d3962720eb39a33c990c1d1d383e449ce974084801f1216ccc708ac774828fef0655a44f6c907fa8aac247deea933f0f739cf2c9ab6311c06ae66109c60557be0cd7a6533c4100671016485d56f142173d12ac4905757e5cf62b3cd5e5121bcc11a121ce9db19380e58f112ac040b70366a49244bfbdb030181a313ed2e44f029506ac0f2e0422c351c34c5e426d97ad544f422432e4e0c2061b8745233e330f4cf94ea586b23184b35a83b0f5e8fd48206f6f96b8d55479cedacc5849c589bd792e42cbd582c5a24b60dbd7df0b3188536b938300236aead2d226c32e90e722969f099c2f77018c42a6bb7398bb3245c2ad0aeec1f4fe8f2d619622de0fcf9f536e5167d856c11d43c43dbf2378ee8d269cc69fac3c4245c12acde89b5b73e327766cb731358a876e5c994acb6e0614ba8cce1f782ff4e7294303ab25b2424369c30b45837f00b240fda1c2f2193d456d7bdf515c726390496e4f064513235ffd44f257a6b1029e5faa5340c26b2125a5450a430f7fc6efb0e487bd4729d26474e7a0b92a58834b0f14002644fcd70e9e7af32c10407105f4c1beb3f418f57460fcfa61116f7cc58fd100841806b1e2773be4eae72257f432a26130cd7386e8f01d4b5871a00289b917211b41017cf9826f50039dcdb4a2f57b0e4961bbab9e0feb59039cd04248ffea62c4826a71f550e7949025ac03101f5ef22448dae8f35ab5aa0f4141025a34c176b78b653f20544edc40f7f3286871c61ea7f91c39ab89c81ee55f52c196c2f6c3b40fc5572a805c804a43b5c32d364dac08847b0856b6db211ca0108c14ba7a8b63175ded54276427d8df74a63c8aca9b6ec0e0f3732cd3806945476c6e68596accbff95e8c6c4ab4cd6fa3b9bcb55729de3d69277956cf31b63838ae439e28dea52b1053a529942872c0105e3a8ce8d087d062af86eab4a423c79216b6cf0bdb72b56a8267c89c36a540afd4a46cc9218a74c9aa780bec5841925ef2f4ad09701a3d13e111c9ba8dda7a09969d1f82e014751a434a8e65be55a08330071cccacd6e61dc3d9698c14000f19a992af9242b2e6b70d471661461b3d731496bbfcba90a7227ff6dba5952867e73a44dd21d346b42ff38ec6b1d45ee3d86fa147fc531b83cd08ae2dfff344235aec83c2b6ebdd6b2aaa8575ca248370dbc37810411dbf3875d8c12e75db435bf9dffc379afe60d3d4915bea9e707bd010921c76fcfd828cfc1c3b5efa8cb4543536988d60fc5ca48f7cd37326c80ec6ab251d0ad56ce1710a776a01fd3105716deaf8f98541ca1ccaeace1d9888323e01b8d6d10ab88902a2c7acc3d19ec4289f726758fa1d6b517c0ab8f8a48383e50260691833bbfa0d169cfbbe037b5429c8e7cb1dab279cffb4e222c18c4290958ac2ef1753b53e77b02cecd1378ba0bbaf58a76cb325bbd80841147347f0f28f0fb231a8529a88703d40624b0444383f284e08d339cfd6ba616d2111f2d8320b1c4d8da6576225a53b21440cab0c4da7a0d39bf3dc16b2174a395bb8c661e45ce4f93d2bbd48413f9f28b7ad496e1b0d984d33754624e66dd8af73eb5d84a00291bc409d3956c1943e052d746a71b0180c07ca32267ddd237f1eef85adda354d233dd773b63223e0d499d1294691d8badc04edd7420045cb86a6f06b07a6b07ed97049a88a63a0a9571e1037f14a1799e38008380df2f8bf7788b8fdb4f0abacd253ae0aeea8dc85be6208f20e82b427382241974f8814e6b66d2a331c8a9a199d0212381b2737a1aaa03d5c4a4f85301d57d1aef2722e651f5fbc59b1e2858c44e6436c4391185f87f3391a8001a27143a4fb1c2f35c1490abb6c577814c4c7a78837ec684eb1399e146f24af525e8b3cc415abefa34c1b7a0bf7987dbfcd88d64f89a22e96b7e71e66bd770905b3b22f04884ad400126e2186b8a3991b3df80a7512c9b5634bf6ac6eb5e2d160c64f6ed2106b25c57cb32fa06fdbfa687711abca6a3ccd682d3c258dc9c9aee0153bbe2794077dd893ba17c43bfac77ea3f1b842e2ccdcbfcc131f47e84de39adfabdea34795649578ba1f8be584ece6663d63273f76e779e28aa76ea4ee4b504a24c4cde2201092864a20b188f9f8ae8bce20d3a594c48c2a7710cee9b6a40bc6f3a1550c6f25159f9d39a64abbaf813e3dfdec362331ff0e27560524d7544b5b4de2a940c92b2da7383427d30d8e341b8752d165d6d236a8530b20b8865a981ab0f8c8b4ce804e3b469f35ee18c7acb7cddcad3f22f2c96cb7e3e8549db2fc12009f45f4c8f5a6509a34d23b7ce7a65388dfa1540ecab7630e2d42da3f4ea7b4064b5a2ef153e8e595f6e329701a2c00f6f32a8127c9e74370234f88482f94e08a5ac92abfe1efcbf06591fcf653232805aab1c3fdd26009721b415aee39bfb8880efda21162cd198c5b4652bff7c0992aa82b5fdef8e254ce33c59d51322fc8f73148db3e2bec7bbc84ed7b80796c6195d9c27f884419b4a95b13bd01567578802dc684624c635bd95d4a70a835948c8bdf39a15d448a181227c38b600bd102b00dfeed61f28d6100733cc2bda1982cb3eebc77377a529c5254d767805521ba862874ca23cc4edebd23d7d3862a3a3c0c36c104646abb1148f54df9405dd13fd56ba18cd5d3a43ffceae389a6a2eca751811811ac5c6332d370ae27ff0ece70eb2c1ac3a9061d0b32719d9983e557aaa866d5434d003722d881d2f8b63137ecf3472c91013d76191244c0c511c13247410414fbf7c28224e4079af5f94b8ecb4e5bfb3a360fb177662a336637eeaa2912b55974819f73c7b7ecb58269208488a135162f3623edbf16281a41bfd7dc0dd3d884e85afdc0d75c0f4bd765ab8e170fb43c845577dd78d6bd8372ff883c10f9cdeca74ebb10474aa93d0b491c6451204909760645c47d111585b02084d7862520e1c5b78e61d1368778edf62738921762e4b4f2ad4db8be68ef83ca6909b20aefdd7032bab6be81c09f733a7829af5c838bbcec3233ba9ddfb48f59bf2ca088223859849f4d0e01a74aa49c960acc51729cdfecc964ce983ae6669f61a2f9e5d698ac07ffffeb94f9d5a7fecae30fe06c84958c53a108833d499d0655421cc1fc2522f7aa745998d7a5b0e0c46003c3e5b2b3d0c3ea8f6fe921517179c0b2ec824e9e95d7d66761bdc78756a780c13a37228f35939cff21128f6a3ace8b3cca6a6d79741e2ed62c3d420fda44e59846846457c9e54146545e0420eca05652e15531de0fcbfbd7087aebf859dbf5960efea72e5bb62a4afc07cc6aa87f69bafe41a020982866e749fd593d5d98df897c7b931c5df66fc16da977f4996a0a569ab4653056ac5c007314231422726e0967c7650ace98b1f664cbb2b28b45badaa2a7b77255c25b83bcaf3a0fad35ffaa6bb2962e6c83591f992a46c760c5a23bfcd2ef92f86f691513fb231666fe1afb8cda952931d41273b9b12f79d0f47119507ac635391868654edd6f15985e92780e2e68983966d192d3122c118757c4fd67ac95769145fbf51cab9dd77dd17686361268c1b17443564e4d22f382958251d8c4a81f50a8aa7b78036a22a5f669a8659adebef6c5f1927c04b1acfc729a8bd1352dfecef8b0f3f2d5985eb5c0a9529b53612fbff9b15611b9c697f46f8b01860ebd8c0c9133e24835484a581e91bead10772e3c646cb41d21c98d2b40ac1d0a04c3d877c3943aaf1b92788b049cbd6c60719529571cd22e6fa73a82f3e5197b76a064c2a92626afe4dc1ef818c159481d440df61275784c1818ac2716f5ddc244ee8a149eaf506d2a2f609ab4bf5c757fd7b15d8dd4c78dd7190ea99afaa2fd03d881c26d563bdceafd18e42dcd8f6d72f48f71b0d5a31a803c5b196a5abc0b5b9841ad7ff8aa180b246dbb6ea8ff36a60de97952a8233e8606534e5a491c0e4ff2a279420224ed834481e9c932979ffe7ab039f1419f11bd344e18d2f3a541352044f02c2d8088270ca85ec11cd7d40353bcccc909b558388cd45d9bcc19e6e92c5c1bc644538fab7d069b81f8ef26b9cfb359037b3e6328a02193cd781ac0a34be482146abf9350b212be6c1ee58b390eb4d94e6d0c30cd9816991af56334e3c005b41c3ba6328bcc5c37c89c6fbdde24c8db10107c462bdc027eef30261444fdc515774b115f782d20ebe53677f177e1c69822a4de93651261565b1433318e5800bad80e0d7a562e299a86c1b5f7a4d856939d6c887ea15668744d7c1a2c7c7a4532b5cfb55290574eddfaaca3672d1890f9fffbe50c58deae91f0186e540ff7d9115517db1db748d5f2c2953f8381881a99a21e1be98f241b2ec627f7db7f2de8711c706025f0c614d15b4149934d490bf8387a7df76c71b25fe7cf8772bc5a28d71e63c75e6742b950579fb6087a26f7c087280f57bcf72df35eeb360b92b79d5abc75b6a16feb0197c5e491772c9a7781024e2bf7706a3992b3845222a6bada2ce90d2eda36e0d585e3b266a910e2f916cb3abaa59ad4ae45c22f24cdca6975dc01abcb9aa0af9ec35e8f09e2b002a1c591815a06caf8e68e6e4c851a82f0be4ec947477e44c3b439d6a6b218b81086eb68d6f0330790d83e388d170fbeeb78030a2695c57824e81e2a0f436062317c2f301de70b97b61a312f4e5955d70420b15404440c2096e0049e699cb222e061a5416af8944c88bde5442dc0044e6c61de02e8c73d17775847fe8404bc2f37f735d63456273e7915affc02762513b6942f51169f766f316d818760bda415ba2f403ef51f80f46eb98bcea5b212e9c3c7f48d76c9053538671932ee5a1947dc5637d96bdde250b79eb1761f5117143181fef099991e8e5f3552ea19759bc533a8cd6c4cf074121facf46eb1f17ea7844afef889ac5cba762d7c14fb4189c699723e419ea82094ffac114dceafa86511fc80f6a5d6a035a51d152879f0b3469d9b737111fe91f6fd84ba4b3c7d7ed6dcf17234c0f96337d7d5032b8495d2bf27674734ba3a93d41369eac71ad608ad82fb0cb897b69c4b2dd9c9435f91a1cc02c022620bf995462506409208cc111f8bf0cf520af345eeda1db5103de44bd5d1e50cccc5845525ae77ca20bcbf79eb8283bf6220f9b9e5f49bd7a6d40cee214487888934f77b0c2c24cd1f59c9c32c0188e6c300b49eaaf89a1aa5c8621b5defa1540631f7ded0e20d43e24d36f4d6af14dca2930ac2d4e66e1cf44c1f6e4b330d0a17ad6589f0ed0deb7edeec71e4f833fb3e57fd6f426ace06a530ee1119ddc5517189adfb746be8fac4f20aec5f263f946d323570354d7938ba5e6b1d72cd2bfd466e9ab87c29bc48f516b68b3a6ac19b50d727d6c1184c73a62e2343e4f8e097933d390929610106a402d270d1ff88a88fe3b4b898575de21d8ed570490caf9a7689f665a412268b821f622fd01a4e1568cf4947b58f79dcb815ddccec6eafecc7055a4f7a28aac73de4a0b620fd832304d9ce9e86f7ac060793997abc82384c48bbee3cbb2dfa27cfd902ade7a5fa26ff0f22fd8a43e613e08fcb5a31f096e601bc6028d29b981a43ef2580594b8709e95e481c78bcc2df9ac80cfce7d64d13df91ccc78f5863b78edb8dd0af6bd8ae0667eac549d8acfbc27d5b03fcd93f49d1c3d9e0e060a8f8994997d081b036163223c950d4f52a26c1422d0250a25458ec1848b3ec4c01822f09f44c07f86e2dc58c009eb5ad06198dca0046a484028daccdab570f08054e4c507b913b7215aa7e74b515857159718de11fbddb0f8357b261187bd3daa27f2d4e664a0ed2949f17588226f122a64dc8d40b77eccb78381994a0789263b54ffbc1f5154feeaf00931c75e6c3ea208416b0be6b5ab2ba4f4d14dabe43291d8ff91e3a8d67cdd9ac085ef40316b779c710fd5d9392d2b11ba19a274c9dada2c4b53c08b5841f3702c6f80ddba4f6f669cfb297466895835b24b894ca76eb688e418bd63c94972a4fe077d5c3d195cd8488d3b9dbb160d19c4e880e65dc697a93eb7c61ce18c2ac881162d2de44156ab0f28b4549fbc7c456fb8ef0cf306e9dae46fa123f7feb2655ef47678c16217633552ffbb793f2815d64c66d04ed9bb4a2c7bf39940fc61aac116b4893e45d7d2b920cd36e3c1666224efb59974d9667b066d81f85ee8ea4c9015602fef53d5ff18c218e041600925b00ade20cf6b3e65220efae25b167d1212cf8bebf48a635d99d2850123980d6efc0ec19dce9ea7e1bec871a22db9cf312369e0ae7f25e02ad1b595a496a39cb4235871e6364bf621be17118432e28a1df401f9707d0cb06997dbcb2423af351e2412f793084a5f9964d867b8d8db7b85146d64d7c56a8cdbbce31594338f01e10702e6b1dd8ab8036124785a102139958d97ac4a07545b84ff0a264a85e5a6039d29ae24b9529e119e1408be059e041d49a2a0a5bb1c39bbd0aaff35ac10159f53c4c0d0a773b8741be9c90875d608f76e91b6005f8120afba88b22161140b67cf0b5bf713e628ad26d4ce7bbd78aff080847da2ad69a5c83d05ae233453b854eecd0a34b11611c45ac2f2cc73a713226bc7d416d574f64501d797fe777e558a3c1e4c0898a40a7d79e97edc7e041f3f16638d72ff9aaa37caa035ccbd4ff7b914d94526165f3620e46bc2bba8ce90b9cd32e8e950912fe519dd51ba88e505a45770d273594bae5ddcca6fc8f8a9973b09644454a578f08ea4ca7d34d1be81a518c56d82784f570720b90d647b0c211418a09a8de5aa0e0b45cd695c0e055b8acf396c943e4ff264a4fdbe1ee6eabfd9e7d05a9fc6c26c875897331ee7710bfa81c1e7297a126c6ab1da829e067c624f048e0d9857344ebb15a6c70d32a4201bd16858d9d121c90053761921f34c3556c0af16def823fb64b585598100075d20a0ace0ac2ccccbf9043dbc6b4a1c4ed9747dc6ef00d2d18f9d3f8f36a136be2b3891b96d4f058823b8809ab71fafeb0131b437f34235ad72d400ae0256c777b1e1e80e0d78ff2b2c4fea168f99caa9ca8cb5c439ccea0350df044cac987f52ba54a15d8b7c2de0f3cac601e6f9dfa9e89e7324b0355dc5df4a558220ad13b120cdd6987c677b82a1b3b65624a229b4e1a68902daee5ac5dc7c7df8ecc90766593a356dee3685c9175f183a6ae9beb09faa35edf98ada803d6fba3a02c5111060ee6e8d2d1c36951cb84aafed1900db4f3bfa6cd10d0b9d66596b11f149c1071f03ba30f1cdc130a87d22f2da457cc67a8b089f2daca1ec6fa64cd121bb8535eb4417a7583af943ad473315a433e14ddb7c4390708c0e1069a6fbd403eea24ceebde3830e9136454a7180e836cff0ffd7d34af318ac120e3d9fa463dfd3e61f92416765ca15cf83d74a46e285bd62cd146d1272cb508a576ff6f94a0c31d0e5b545dfa89a0c01de8494dc207324f966bdaf76a7cca7623423c330918ff178e38cd9ac8670cb60c75cd72bdfc59d24eb9e0246f3ba9ec06876fe798512d08f936b978e38699c76aab03399e4351632fb6070b955e8b0ece16c08d177540261cbd3baa3dc6d79215a878b1a922ab13dbcf7478a91245d72abd301edd28126890d7e5a85df6b58d1d3962742b7146422963e9422df299c57a79a4d36dd487a44f3b80681f7de40db5af85a723ebbf4c71264092ad8a582646535c3472ebe03e45bc7395c8bd6ad9785ed8295356a56b07240a9ecb02d1936704214a177719c5a9ab47bc866731056a63499186cef1289d3837668a2a6123580fccf905bc9866baf4204ce2a8efc3e8cc8806af650a749379f7d7e15b2010e724c6db3cf93101b5ad9a030532cd492c6663d54fe11b61c327241ab715a517d1ffcce79c70e523e5d971eb49fb2af0a974fad686f05a5183c52a3c84373a0f726922b75c816a1bc220e4e6ccf43610fea26b489098bf592381b976ff2d47323e5e45e58ea5b1d0ef308b51768e18e1f730207036284501e1c9c88cefa38c11da72186cf7f8079e9bbeae597b94ef07a0ab9a6c5427ab3a276723a1e92710c88d40da0c7f3a2c32f62c78580ee7a80b3b9c10470528a630b631d92655fabed365506ff3b07f41b16d37f4bd7630dd99b177b452b60143d18a59c4f5ec8deacd341d8b60b22f02a768dfc70dc11436d849617f9770cf2b8be98d385f5bbf759d28ac6409252010c712719c7f84a10fa815f6cf053a8e7689962ee9e14dcc4098082aa4b5a28642cc213beb42a0c512f215989f802ee52fdc73d01c9afaa89f54ea76566f7483f4a26f790e55af63474a90cd7342ff561ed16e80083b7ffb4495bd116ca0c830ac630ee299127ed788e9c19be87b59ed5822906fef4a0090e28e012ad52881d0f08432a4153ecd1092c82901dde51860aec6aa03700ca4c18c748983a1897b25f67dc546529a46306a62fc90dcbbd6fe64c88ba3d417102b4d029057a963acf14307d8999046ddb2e2e95cf2f970dbd3b261317c26aaa9dee045b27ea034f5e4451f58251cc0b647bcb84a6c6c86336ec19323afa4c4939b1a1eeba6210edc683c70474097334facd5d5b9dba185baacb1ac24ba4e4010a73d2035b7f0981dab672e0de472bffd4fbcbca16f608ddb11e5f48937eff590d932d7fb9269c1d74aa116aff5e5eb1b4467f7a201d7f4f8a306f220f09b715c4c2411d364f663e1bdfc891cbb450f072bcd0fa898d1c5e11c5a8318bb51c6ef5c0b4a8e12327a9099b25108c984e5b09abbe0cd40c7abf637976d03222b1d3d9de1ab956a745575c90c026ac693c9a153918ea31cbfe79967d7bab38f1adfc0470de14b4661a41c8ebd9fd8945089a2548f29b00cecc3a476c78a8ba9bda09a4798a06c716aba8b788e3f83dc2345c7c2c545db9a999b682fa3f6efe9ddad6cc10be38d9174c19428d0c82a8fd56c4542b14fc22b57867ff2017bc2395087c0cc96df72619d04a2282c6832dd42f190439f8d49447962c5086a4dd3d9eacf92bdac30ea4c84f2ef261330e0385ab0adc5c49b7198eb2111317ac98d17fadfb6bfedff932c89dfeec204ee97c6992c092660c5d6db147eaea46692121411c6b61b9ab90d5d2de9f09a718c8ebe9c3ecfe869241fb151846afb66abe7ad00c47e39cc6ca11fcc69cf48cacd46342e92662125c11c211e8f846b52ce3a47084c239072164bea4f7ccf72085b013335e6622a129b327c748953078b7ac89b5f124608277ff636e15a6eb0cddaa426fa5f2611786368bca9eb4633b97dbe7919963111cd799008275e8e2071bd97807f02c90213355c7da8cc022af8d01f2d211b8279ca757905c0de7fabf3fd563c9e613f7bb437b6e696059baf026b7c7fe0ca2acaa3806b0fe44bb47f3f833e79e8dbc24ecbffd0ed1d74d1cf7a524deef821a04a7ece22b9264bee0fd491b719c07d4f2ba032904008c8de329739e5d9b13643a1ec6cbd801f8c59013dabfea805942c1aba2101f315c9f162606199adf2763021f428214ef626ce9041e3abb997fb9f0d5fa988db6a64b03533561b5ff6c57f2d347c6548cba645eee57106d5e709a941572866dbeac0c03fa902a03efd4b7fc70b2367d6fe060250ac204931ac7bad42c2a9138d31e9f340be1d2b3f485af4494d4eea701453927404ebdfda7969c923e8c764390cea4e91d907ac04d05ccaac9ce871565971679698db61edf40c402cf7d1acced60e33f2c232cd407ee49883c24dee3b0246531eb51fa9bbaef54c89ff5a558b6c91d9e5ff39f8b0a7e5960d4b22f169b99d04dcd597b2785367965d5e2660bcd9cef55bf42c017f27efa569cf921aedc5bc5562ba7be19cad4ef07a2af2263eb367ae99959939735be189d39f0607a802759bee7a85dd7610c9a8acc012db106a1633e91c3c9ad9325fe631333fcdfcaf5485830abc00b2386997c00abb1e534d518965bd3d999db939f36332574c6c85a374277477a7ab6387968d427ccab5107c665fe64fcc5c9d388a9ae040fa7b3041398280a04c3892fda40c32bf9e49764fb4305c64ebc02184bdac5efd60e7ead1021538edabca3c32a4c96ec816c9c1246bdb20a71a52ac7f02e3aaef1455315a658dd422d5e8458f8b05ff9bb414fe6846bb104ab1fa09c43772c44b472a7903249b42623c24ceb313f76224bbc6675caf8d7cfe1524e9005925ceab1395f26dcbdaf9066ea2f6d4782346a28db5a29cbb3504fedc3baab1a15f0a930d6ad4817354f956fa900a45b43051d192cf81258b45819a4c496cd8d14fcbb721195881bb1ce51c1a1a603151fde6183ebcf48a01c69ceaf9a1c805777c0e8bc6f56d92d50cf2c501589962882f0baab0c353237d0bc1f2b035503db6348632ea7ac0ed374e2ccba9a09463ca054af99242e6204e2c3b68be7861b05af1b21ed38cb43ede50b310243757bc9aebda22b1f5e21ccca2ee233cc5258a34a800d120312f895b88bbf51b56a774cd1283c3a3f587c88bb6fcada2e3b7543a72d19cfa164d9118eac83b85baa821d986298aba359e0a425c91d589ffe25b86f00d38cc7a95d8b29ab8c39f9bb0faaf2c485ef5be4f6a99c7769b23dd2a38c0c17e76caf453bd927189ea2375926d5f4ee462f4a8bf5287764344e12d4c4ebc705107acb044cdff72127f3667aca5a67e9570f94e1788a844f00e978c73ad074bc3f661a5ace51242a31bb8db5f11791b4c8ad0ae59152b33822eacb4bb2eddc7648aa661122b69f106fd7e5ce6b1810c1f969cf07d40df3cd6cd55c55ae8802e90030954a27db3ee8f9ff91f9d78eecde0fab6c4ffce60d9c02ef49801171c33b5c458226c68d1a1294078199705b9f69910109438472332ddb6cf94d9e4400c9c192999126851998855d27096e94490127aa7f2975c04a4d7401fda031b58e0c967c4ce249833260591e4f1395e2d5ffb4c95a43e74403ff0c072e085b8efa3d7baf16da8ea902101e54bedbbdd76c1bba09407f8af26f718fa60647f690e4be1b16ac1e2118ea2e74dde52e21f1ba454aba01d0dd2c3d325f630d4dcb08417ba576474422093919bcdc49bf5f1d76c7e1bedf4d9326bd93027c18b0e51ea9e587198bf1cfaa01fc84025b03e023b34e6b1b9a932acd2ffee875a938d6f79dffb743e37ef7b8d7d24bf36b767329c3e891a6884e04de1a0117a2318d40cd21e1816c25356e8a68861e3570af7d5427669e92e2d41e1ec8ddae42c97ef726d2968f1dae3e78c674d63be2f7741a043335d7e516d6e30d68c843b62d324907ef8c5aff9f0da0035f4402fd0812e9039ee57e2135df5f3b7391f891d78f84d22c912b19219da8c1a02c03f404c8e3f17b07e3b55cc1f4298c7fb12b784d7823e5611c64dfaccc2cc19081eea011e290313825e6337d89287e80826f089ca028cc08415d9525448fc95636d1c062452b2e14819565570863074108c2c14145874ec31954b86286ed154533f1ca77fae93a7e528355b0ca01beec80ab2838c1949acec0456b156e1ea1d9c436423f1e0a0d8d829a435f505dffba74b3540e8ed908a3aad97f7caed14c99d352e5f3134c794494c935488a3852179d71e868191f35aff5d24f2b5b7b6a9ea9214cf544e6cd81b8433b5fa9565b20bb75f7dc3aaf7578aa2195eec22a94c589fd55526edc271070cb89e06b9a26250e872940d110ade8c1123658a422e14716edf6874e1b97a71f60486cdebd727af7fc865124d93c9da58407128a8c2199f614a09b1491daed76182e3e42cb299345b0540a7531df83ec8b35281b207adcd161b72504b56f2facc3aa5ccda024b29aa4745edb71559221e266e848de792fe9c69bf0abc364d0e6d3bc18e3c1ed228f121ae9527d1675224b136586bc3e047d9e8dc430c230c3aa507ab8377890bc8a5b3fe32b9a317269a5f5b96b601c4c8c688e3718ca71f6851e80097995832a7a47cc0c7825214e34a36df596de4b1876d150f6430560d9e7b327480c2deb2949e3b87e904e17adfba30f8ad3b47956ac6d246a8365748395c25d69a9793b416167abc152e9d7ff81ccc807bc4e79a6e361de4d47297d0634b50e90d2eaa12f800a33e172ae20774fda2bfa353a0d472f8a7304b3327afee79dc9ff8e333abf018b89454b5e0e0fd5dc94ece803bf3d61fa00d56e8c0e5a8b1d673166438403042d20cb80532694efbdb225cf524ceb1806506e04ee25d6c647724e5790a930165de090ca8f21b545bf9a2639704d7684f1042026c3492528af2834c2c2d389d667607f6572d57ead66f4b0f91823f61999b1570a55a5edc2cdf1c71a79e769ba3c6681ea99fa677150e04fd00cd4f0b148ab6931793becda24917be0d08ad945048bd2972f7179a49f9f3299110d87154d08b77d15020ce714910dd89ce42dbfc7f98e99c1ad3ec25888f47eaa14b8392e3b6f4aa4d4c08bbbe93bf275c9a55d200563308017ae3bbc427c12f62b1746f259140a1f2d7743a219ddec371c08e7c6e94b834fe650a04595b1f3803ab5f0529ed62b03b3f6b91572ce0ba69b18dcade0f7eb53ddb88ef78da2952eb25d3f2624a4e498db7ca0ec0a03b366cb55077d2e587d9ae5f93aac72814b63c0f1ffeec5688763f295390fc3f63f7c46f0610105c45cf401a72f6a351c41b7097c5f3dd561af61d21e427ee430b77844e6f9b51ec57d90176c2ba2c2c059078b6789aa2e9d6406a8ffc9e2b14a7b0b3778f73d5b68da0051cd2f02399538a6bdb7e763122658b6d9f98706a48402682016b722379fd6ce449c54018da5b80fab8b66cec2e79928e5fe88034ebcba24d077cd536f0843fcc3dd899e2a2f5007e879a30bf2a33a1e133c6f6f3bc9521ed36c5a49e6ee64134fea61149524622aadb2ca11c6c2435b2d7f208bf4020c0513a15d11e78d99d098edd522ebe34e08ddc9632afbfa570b2e1b36b8aeebc1a8f0f0be972c3e684c43d7689ad42e35160f2f4ec90f3653c80bdcba0d25c8e676b8fc0960da4757af8364405cb667fb7e50bffce233ec2ce42b313c288cd2cc29ed1a76bd3935fc782048e6e0bb427c54e3e75e63c305422f47c1ca61d5f01c85802e623257cae11d80268ea7b1c138478e58024e0533ae818e4da4709033f56cd1b33c5b1e28ef6cfca250ad25d26953dff80138e658495f414cc32d804ed365ae5926246f24572f508da25ef8eb77de5c3637e6f13fae9715fefe437b66c710b66c7e6d6e3406f2ce23e80b7806abe0ef87b85813774f41aa2761617590ecbfde8c7eaec1c2e197507e60fc966d2181d4035f6528aeb9699309761409cdc7d390d73a5c60170cabf5e7f010af78e3d7976dedac5c49e6c47e41aee78e6aa719285970e31c720f161cc2478ed2c47f51aef4cfda5aaea04110595406733e89701a69d9e8ed66d439a6428541c15746ef7e261c1e1c9b3ea55480541e52a402660eeff8341802bef6c3db37c7a8743050f819367b7809784b83df4c83c93c6c9822708f74144b2c71433a728f97a20c270c1e2e200e0934a6e9c67f3df11328178c64ef790fd06865e6df7ceb3581a15af6e31fb51292db34d68b28d71ef404b9f82a169b1c2de60be9f3e2631563b99316ec369da60c1ffa54e1912ac937f8064489979276a42b233c52235d0fe9b3b11f6bcb171d5118874d19f7728bbf877566d83a65a4112e288fe3a33595546df9fa5c60a049208a0a878ae39a1f1317e438d23b1e81f0e0de25078fcbb63c1ec38f14118091098c910d3f03111e28f108aa3f865141a7f6432dc784c7ec4b316f31ee239d438414ba2e088f1a3ebbef41f2ed84383eb30a083088ae5e93fa69e3fa14b22f9a4748fb74c55713e508593f8ee2d45f83d22166bc2ac16ecdac19cf959885809d65853b946b6d66175add272f9e886d674654eb0a26e628dd3f73712f77a8e456320a8b0e5b3691629e50b5c0fb21c40760c717b4297bb512fe6270a0da9ca0c0000d2191d8373a8fa9cbd7c356fb9aadce25caf47d02d106dbe08663a01431cd92586188f058cccc2f2ae151d2887695030fc576ad9fb0a382102657a9f470a171977c5f0e8565508ce5bbbee76d56303c8d8e02e763593d96ca872566455cad7b07f9d2106e2e498d1d2dfe449de36497c68d9b76a4bf4d167e6594d4f52173e4ff621ce686d12e2aaa51e6329c8b2ab76e066e63a1e51e1152c5d915008f0c6d1c0d10df077b809dc25fc0c13b5de70b15f5e8ac25f24e2c83831c48304414ea11748a817469c5465bbad8fc5b3ebe3f10fdb8124be4db7fa30d63b6efa4636e366fdc3a0e36d7f6c9cf63d115dade23f560d466ac06bc5b13da79c27ca76e61f9931bcc3b10e28b98e4d12cd4c646441bee0ddc54aad6c9f644af9ac4fe22c2c007aadd8b86649bb1c6bc90d0b3738bd8b7a9b4db9522e1669110b7728bd417ab7e689aa1c8b9f370b3738fdb57a95c9745353361ae35fb19b06de128361f81e593afdf0a8ef218ded9af46ef5e78e79e4c76b952a6d6a3b09c56033aa5acefbedf05272260f4ef6b62024d5c6c32a3658f12bb4eede631a1cccf1614c678f36aacfc7f5fdfe70dbe26a73d0cb02c4918aa710e4b650baf9d542f47ca0ef423282d30a29332f367be9454fb2daf88f97d1873f1245064b4878dc2b6c3cf40a98f63eb3c29bad508786137572e004c4e2d899a45a1e6df1e2a9dce031fcab192bbc91ac17a1a303f638a5700ccfe4072bbb2ed452b2eea7e925e0f12092abae8a0a6f082bb964062eb681c107cc0efc46d3b04081e83c97b0d7da16546c231a1f56c7fc1145c4c8a65c8d37f83163054119eae46eae129809a1ee3484ae8718ed684494427c4a272ce95c1b454e2b020a2c311e0df45c4e4d6b14f2271e479aa47e8494573f492ec289b628f57020ca346139a25bf3f80b9081e1d950d010b06e8702a0fd340e4c4f005df03e3a502748f16962ecddf825f0e845fe1e005b70aa2627228461f453afeb32a4473fa13df95ce709d6ea92f6f61f9805d28332c604d8efeb8dc00ce5b6ba2039a104cb76dc5c68ac4b8f34e686b675c668670cb4c0e37682f7799838535857d60e3cadeb445f0e65cf9d7498b6d9f63c6231b5bdf19c016cd162df749dc221f30b614625d855e8bb134469802be69fd055e8b31e291e3e20165abdc971490bf254de3f0e253d6f998d2e93e75262464931ae3f06bd57aa951cf718edddcbb6ee4c0298bd870472bea7c01117faccb6778e04f01c5ecef6b6ce0b83f520143ff95e64ddea55d8db4abd4d00df7b6fa59a0b1cf43ad5b9412da379d1cd7726645737fb076cfe21204f0731b8a19bf10bf9fd7cd90be609c1592cbc40b047bd0e341d6e12e4a6688fd7ee71357dd4746c797c34f2fc11b0286eadfcb7ce0b0a4d8e8b3e8a0b96b1cbb5a024313b498063341366066475a0a5545a1261fb9d766d037e802b0e371415c7e620e056ccd18d64e6842232051a65c4eb3d0b689daae7529a16c127d1608388a6f8d372f876b241389ff0d1adc0e2d779d7e17cda6a26a29429fb7e70c051b15efb14300d0ad1da58cdbd4b117d89848f8fc92d1da3c1cc16ee5bf0d5c12b6e7c1e373c7af4dab740ea259e17e144e4bc0e636e9fa7971e9ef9bd40a67e153c84d04e22a52cd596519fe2efbb8e46bfcc90aa67f66c8283989066b15fc4d494e16d64c525fc2247044c6d5e315e8867ab44388910fd4b7c6c38a4cb20480d80752932a30ba252260918131a352ca15c873a80b887285e95fd0f3ad73c0e35a4b2f8658564cc68366b5e70e744fca5c8eb32b8033737e708538ce8628d459faa307bf09dde924382c5618004dee9f5059d56decec096fed1691f43f19d943a24d052a1b17a7a816b5f403e6a353a71195662baf5bf120ce0bee11708c8e89e54cf86846c26e7a6d8c9489c48602bbb5d1614326a2664bd1eb57710ada4846b6828d7df5ba70259fe014501683da152c0bc6ddbf1854a7d402e81ee33d9ff4aec76c492b5953e7b7926ab2347c7479582095ddd7d4624facb0ec832628a283b42c3fd43c9e157a56b01812c81a1d140f0c5b4be99380e09d25143c768ab8a799fe89a83f0c7a1dcf8c8bf9e3fd4f5abfab5fe391f29d9eab87db767ba081095698bea40862760585da1eef0381c2dd0944fc0a5f6a0304e231aaf40e63f39de096eb56bf4d3a7b4344018d48f652c49c545f7069d7b7a263e6e89a9bcaa65950e78d51437c99431ab88fb7fc2ef501b86cdb575a9a900e868cfe7077404a1bca52a83717db642d64d83e497f57450fa170834429d86f5e39e594535c87270529d892d7ac99eb2e7809f005a2f85553345ca38d48dbf5c0412d844dfd3a922309e563227dcfdc4439177f1df6ceee4d635ab6c51245ee090f72aaca4960c222d409648a1e6c47e93e6d6b723325dd9b9f7e8178bf593f99e3b667ee471776d2555e343677883556a6652b5677e266b04e19912e0db6bebb400a985be8e5ad1fdc7ec7af7775c9370d0fcc8bc87766e1dc16b42e9d65427f32f8c329534e5f81c0d294f9b83fd68b23d7f024e445bdcb28bd676fbc4e77b98e20888fe43f2c4aa8d708fd49307c5df39d716b4c68c062d37232eb73c14743c2cae17062ba2b62c3b264469affba77e434c06b8c8ac11aed0a4f5ae2a1fb49a0ce3230ebcf73c3154e08af67d72caf2a5af1f1df4a0b9a074053d764f6c4f2f218a95711a7bb9776f3a0d516a5eb0f9d5348188d685f6fc790592c1875e20a620d387b898b57c8294a8caeca2eee7b5960c58211465165a80ee919cbc4eae91184e1b09635459a7da751853652de541656b454bc2ccc6f75b73ac954151427894f38aebbe349ae780011e371c5231eef09ff3c18fc21617e861c47acc1eaa6b5a214ed5f0a601b04d4404ac78ed2cfdfd3d16e52c2b79b50df102580b62431855ee48d40f4b5c8dca3b820d30afd2e4aa2c83a2f149cb27ab26742c2be5900a0d8921aad001ab13744c1406f4559cac983393932c1f41667baeb5b4501f81e1b674d942709c41e7b4c267d32ceb966aaa560d6d4a297810304f536f6878e730d7c4938d2c1a9d5d739bbec4e813bc1f9579e19d9e73142186ceec428c817a9c61ea7d3db23c80273ad710199f279895f68aff9852a63d2362bb99a099874d5f960e25f805e832e0c294a94d35ed9a630fb237b9a79dda7dee217555739008e8c95cc57484782ffb4e8d37e5bd3c8b6a94fbd8ab81b457f8fa030aed3ec5cb6ec48cfd5a36b247244a2cb9e01ba603842e7362db85143e12ea8793e6591b9f620f4af2da331c44f09b67c917b84f53161d98f2ded2d545a61af8f0fab063b795b2f69ac4dc410c6ce98d46f68deec78f110549114c3e500beb4d94e82644f3656abf0a2d9ca2b2085c672d88dd2b21f9ec0d13e5fd04f16d661d11d0695f34aee02b8fbea8faa17f1709485daa8aec21c2035dcbd21740c2b02986df9f50d65548e7787e0affc3552e86e58004aee3e6bb56632e95cf632bf43d45c03d17266aff3c2805057a5b56d812aa2d8edacc10c88224a743a662045ff2d22da3ab854b89baa16fb8d7dad5b12fd7e920f93fa5719c64fd526a9f07de22b37da19208499d03f24be0469ca1a604c457f8913fb5ffda6d69a5af63055d2eb1b9b5663c4ea5293e4bb5e5aac891364f2494bbcb92a8e92eebf8a431ec43a708af41170e769e0bedd0aa4fb03b78b36104093c9e181e6c5807ebc4bfca4a82c0e50f5bce503fd0e024bcc31c98c9f761c121f7838a7675116105771f7b0d0d27ec10d935f4cf92b4e5077fb8164bc226f3292328883f16b1e1b3f1ab67458a514403f7cc80e16ca2efa89b7876dc1accdf289c85d2ecd239bad146f51aaf5ebcf743d40c5fa71b689e0d42c6c84e796307003b3c37b514f65d7808b010f760b7344ec85e2042d4d8e3461943fe4ebe17aed76691f454e58cd0f97e29ea39233031f406348a81825d82c5c7dd8b2063bd2a92116c672f965dd5b5ac6e756c8fed44f812ca076c8b6afb1823ef11824261c4c3d2e5cd01a8df9d3fd8d2a6116c1e3eda6fd794dddfceb751171c01d1cb25e8a94f046a3445a56f84c33a997ced7fe775bf529b8a42ae5d28d07be9f8a89799dffef7ea25432bf70560fb424c7104a924824fe30f064a746978d590e1d7ce37eac938e8582920cb390e0629fe83fc6d85d07ffff4993eac04611dd9d3bf9d6fd593e137fb0ed63e08b5bea80d447df9ee37eab1a286a241801790c83a128780e2083cc06133dc89a71c454ab98882e2bdaed295f8c3f276ca778efd1711f6b5ffbd75b2f9e13ed218aedc864b7d997dedfb1e75e677078592b4ff48e9d9cdda7e37ca1dd0dcfea300a48ddcb6dfc2007ec9a5a91bcae495909ff94fa18a08b385853de2d83ecc96c4cca7209ad81f84811160e0021aa61c0794e8b8100196788d85a51e9e4aae680eab54a935ee9e79be027e4a5053df26d897f024b02fc82a2f34e257567c5d4381e9d4766bd381ae903a3f83c4cb29e59ef76bc9e4b3f7df1d2922b45b0c5ba6e7542bdc6cad58be4665f67a41df21bd39999e3af4ee9f4521b95d469d25e28bc8563b25640fffa49088ea5c6477beb34e367ffb6c76bf22f4d996622b8f1b837c49102d31fec4244f6d37a97f4b47ee77c0f5b86c344b31e6d324b0366bcb7f331e1ab9e57d3c04a0016480d40414230dca9285c22fa58152d45194f80e056e5f65e6133ba49fe29ba0ba4058b267b34d837b75d5418b1153956afc6b24bcb4a10b599dade39df2c1804c41cfa2c40038eac9d46fb777acdb44b6229f526bba1ba955027ad989dc76eb9df31bc4419dec7d77bdf3babc0dd12bcff1f98fc4b4ac00241fe62befe75998318488cab44ca2cc11f2518f5c1462002833e16aa0a9e8d3cad92d261bc1930c4101b29dd7bd25124000c73f30eb011fb10aba6b7481cf34390f00aeb8a930c6286d68aff60843f9f0851e0168f0f4f1fe3a7a1bb53bb0a359889a05ce2fcdc2ba651959dbd32c50f5950bb6bf4506f97a5ded5f036698e883e0cb37b81c198bbc46a24a6e195073c3a8d678900f8d36809c0a5d1fd9f531260142f56af29312c1c50841361d168c81e83d86cce47c6d09964016349135247e077041e59a6ecabbd25373de2ca8c8800d4ddd24d1a8b0d2d676a3dc4a8ccffba3048ce45d49832039e0916d87b807b98ad454ff0aa49d203165c59bd9847d10df13d1e8dd207ae3ab9a0800cf90cf8b19f56898df68a88017780ce8e82febf0c5b5a1577388304bc48c58eb0ba2367cbc138b426e385da4fa799321eaad9dee396c28f644ce1206299be8a7a7eb617b37f3f2b7e0a4120e289a66327391c8d5915d7011d080b56b08d0bc888ada5ec39afd3964e2f995bfe9760ae5305cdae50fa5a8d704cbfda8e3433e5d50ecfb8876255ecf0089033508c491cc09e6038111dbdd01e2589253f548c599faac1f51832c7f3b31525fec94b842ea180feca4960d0eac3ddde561e0be1f409139f88a20235810ccac10739c23b5217ba9425e6da6c383fc1e820ef18189c890d92a715a6307c5cdf1d4829d15854194e099bf6ea52ad4158d05341b04ed7040418cb1234d83cc3ceac91cef19af04eece9a79e9b5472633d1e0ff101a09c8dead26588c9c7e31eb8223b8c0a4a04fec1cb1b9dbcc8aaa4cbcd14f9c1cf828270372358fce1b414a4d0dccd6281bdd6e0cf1ea7e50805c087df1d810322de64d09d898ad98764da855c57e67f2f2dbb7862cb2167721a7b2f07870ea3b2f1c7bbce839f02cb419e8b7d23e39a5e753d66608a8537ed79c13bd39465b977a911fd447deb9f44a932a42c5d4c16c941779c980575165400354c457017f6f744d071a08691bdb1b502ea307131da0b4544b800b8e7bc711586443eda568d6b7e7304a1ddb853acfc410cc2237b0beb9540b29b03cdf5751d090ed6494af0724845322ce433301c3340155aa6b271372197b3198a48772f8e3af3463354e5b80c022b707f645c55239bef6fec617bc868b1a1a865a54b09c60fe64a729db49b55722b136fefc99b1f1e7e0a29b958d667a3d6f0b8b62ba890a13cad9c956393948ad255273eb9125784196de2ca742541c3fbd24c7a25024653ac2a788bd388e76b2ef5e7307f47895a5563fa87fb4f4d781ac6a696c1386d941573efea58e3dbfc6885df7261fe7a7c8f29aff918b3b003179ff303cc6d608f0ab8680ae6ef02bf55787f1a20b0420404edee2e593522dbfffb9db07d7fc061d00e1d6111c4b99ef995c0fee4472ba540ea199ee95f5b1be98e018357d0fd29130182ac206b6f4f1da02fdabe35c25c66237a606ca1db5744d42152c4a4ebacce115179c613efe8bcdc8471458747b4823943f8ca42dae1373718ea28c72e85411336b002f736446f31469b46151901d7dfb42736e0dff95cda8df40f3e653483651e3843030c645f25cd176f43af1c02161549e001b2f69ff39fa19e131b98ff7c710213aa663a14b9cd67e0636d63865466cbeb2f95adbaad84d5f2f83767e3e82213a60a521b46ad8165b785d924f79fd3999a2e4db3c5df6eec801832da539c8a22b0637a7edc5934b383a2e5a261c3f94be75214a5d4bdad5029d7bdd456f433cdae642516a99dcb82057685adc9c1f88080b37181b0d40b7cd80c452b5d1e9f93aef36c7eac5006cbccd7945667b103d2f087c0a051c0784a7324988c870444ddef5d3342eb195048d15c232a0d0a9e6f2379168755384ecffee75da45b11b30c3630a766a20c415b188c196ac8594cd75e5fb99f1e4e11a480398691cae19dcbc604d1254c0d0baea3c8c7bef3b81006560be3724729ca176cb551b0d31a7f692b3fc9306741d93d92b31fe1861f08cdd0e99ff75f26db5d4d6c86bba0f235089229e82c4c456421be6d3b2684037f6aa7cb8e625810a4e9de10aeb1a6868915316d1d599bd41aeda49dbedb1683a758cfb170ea3f86b9ef3abcd270c0611431d5af1be70fc77047e9668412b00cb0bb6a963920867cfffb2776d09585dcc0a61084dd20e101573eb3fd239646a93a75f4527f8ec2e8a4834624f82e56322f097c28c66e8ef801abaee254f3759f53a042cd4a9e9a9a4d4c4cde0fc47b3f505b4074b5ac7858b668d8d2f6e6de6a3a7de9e89d6dd30231fb5e5eba9d1c2e161d607f79f3e9942f22e9a9efa3db0b0a3cbe3f4f1943eea06f54ee246b84fd1b2487805ca4831e6e1182588769e6621fb4ecddd0a1e02284c1fe7c87318834f0f0be4f758680d085e44dc9235368b78633975a37e6b0c0584e4a7964880adc8bbe246b7d40fe903367c1f498a805ec3e4758d24759e0788533da737ffe8363299febb8da822029db96dddb16015e53a35bd3fcdf4c4307df01b14734906a5e1a9c825eba54f05cf1905c159010b9bf2d8c1096bf955912177f8e09d5c0be66415b952528c0831ad49c3d37f4eef215932b6b613211effd16b85b02890a1c3c551f884e457dbf0a85263b7a57da28aea883d9fdb675e1e3db97637cdeab319fe7791ca0db461af4602e190cf9add78b09a26ed9319866f60ce4a857f29478dbf7b6d88f0809f38441a85cdc7eda2c338b9b1d7c77a957677aa8ad54bdb48ddb83a245af3372fb48a05060c92a97c96a7ba3164963a06b9141314621c9c384323b6fe7321a53556373bd26d49469926d0508c6004b5d636c689514bd6a3096d354f3be22201ec0c2e0834e684bebc9624333fc81d61e1eaf3bf48958e264d085d4cf334b0f94723123f230ab62ddefeb44fb668ab10c0ad3b2ae4ddae71449d3ecc8f279569280f7f0466fac6c5751375bc17c45bbba2fccec161d8b98416547c366f4fda8173bc80ddc5ccc397572d25bd3652db00a21a069b3ef8ece5eea65c19ff64dcc061fdeb11708bfae981ef6befe08f206d114aca1eddff8d173ab7d999bd6e021f15d3daaed0a1633cfc0f9249350f8b13b0e2f18efe298bf32465d11b0c3a1c735896a45a384f7247e696d058cb35b5109559edf02c430ef03352564b38a57cb692a87a79517bc1bfb2779557360daa882026576d47f10cd382d5742133ae6b430f23a234a6eb1715b8cf1da9c142194af78ed8f8c67f8c2288c08dbce4d4968595fe8d6ae930d0dbe1a0fe6afd7df96b288e7ba30a5af0cf41bd7182a6ffd7de5db78567bb22131ff7972771e5ee099372403be81488534f4446859abc9bbb1439eda10f8b7a203f2b7e4e8e395884e2546165844075d72e7c5a7a5688b5a2331ec5fdd77d944eae5fb8931e6215337d13cd31e43500c72695be8b4f3e5b851051865630a6694648ec43be9338e926ead9f7077d53b793c2a93300a58aac1ae05daf8e1ef83f0babec99bfad0887e6368cc34d53b3da9020ed8c69d1cfc757704a44091c55d7863be472ee3e4d9c9d513444201d1a47fbd56d5d6d1a2def9569ac5a99c6bf34aa280ac4883a10112e61c70263339020ac548093c6324de4888bce150117d1a64aa79999b704be2c14c37eefd8a0e870f4f29f6930179047b0abe9b1d1024a21c4e5339176b45ceeede4a02043cc62595cc2100be091129c0b40239c7d72c2aceda711ff9c9e170bf8e5e7418080aba229cc1acc591b3b60e04c31023022eaf9a1f2bfcad52aa1603e3ac5a2c98bd16758d423c44180aa1fe1dbaa008776744696db375ed15bb9a9e148098f907a80aba0d003ae277ff8369c44ccc2886461d639b1f07550e54c1885e38a008d5c1cf9e69ee050cc2e477406468ed5bd57d4f1db3b7388f798eed388156e4454d1404a1cb29e3aac69c8b553fa4bd79e9e92ae95e4011b0a7280ba6bb1fa96f559700fe9bfe82282a8385d5438f4e138c0801a22c08f990f66925e17c1209dc1800369b82b68d18e75cc990c494cc5ede2ef6ba0af2b0aa5c76bd7ad5c117f60bbe452ec32e7a0bf200b47000021019442ab2f0133411b30f45ec1cf8930158f6d753d0b76f87ed16d4ecbaf625d503fcd545d07fd6bd0c36a52164ac08b223ab31c2fd38121625ff329f42f47dfacd1b1bff1c9322a7dd88b69044dfa98c4dd931d841294cc016f5f086913e34260ca5a00e3a9408d0b7708cf1cf44edcfea3b498143465c9cea1e61dd37f656a058ab9808773aa5e1395f486d589d50218f7f97ddd4383cd491b7a6983d2f222ac4d41b0bfbca41a89728813ddd4772d34835b5b0331f72a07834ba0c2c53e60f9ec88e6faf2e072bb3517d3b8e727ba592260a9341705ae37d98d986387e4465b3541d0875b5f69ff2045f15db99fc0ab6a0c0a67fa118717a2f99100310939d9fba7d2a8edf19802148f1d0ebf501c4b1dbe640b8984876d0a606c0aa9d29ba0711900a4e3cb8dfa784abf8aa97b53b9980180357768b853c55409f7c28af1f0780db26aaa2093acbe4f9b2a0fdbdc47e278e79129dc0553dc70bcb514288b6a08967bcac9886cff12daa51ab53b4ccec7665651dddd6447d76ce392e7ec9b3fa85726b47ccacb84527d05235df228932c4ae9bf97fe6ee90156f2bfa475e395242e012a44994354800be47ff97131566703b30d205a5b9385970256e918395c052c0a61ff5d01ca6892ab4aaf9d823e5aaa9ae0835678aaa649fcbf21d7caeac9a4aac1df25d7eb25cc4c5521cf08b8fe0260e500a3b1cf477aaab761bf3e405c32266bf279aef078faa9a790edfed4748840014be21e3380a1bcc3ad007538744d902b5c2883f85015e9d1999c1f99b12744b9abde3a3cb7dc951bc438d37a34b23e3e007f3ba49c30c9c700ff8f1e38ec22480d857a96ae3b0b8844f27a59fa7f717f6f42f881945e2ebdeb1659e5c1a720ed25cfc87ca382fd788b19bf1f2e6203156fbc853ec4b590b29ae7783ec383ee5d3b07e7d5a070575edccf99c29b68abe5ba600d5018fb44877a4a3a6c96baf6a159c22f19bab3c9cc4619eed8da910cd959d3191c38859bb49f69040efef7565e46859a32f9f38507824ada94bdb9ef459643fda3e751fe39e64986df349a628e1947e445c32eb1705edccf5f522a1857cbb2e01ba873843039281b232f068b3cb54cb3d6514217a3b014ff69bc1cbb8dcb62021292ad083a1ef8dd2f3cf293d9362be966ad4308bd37f744e2804e9b7e8678b72bd8456c6ec8116c5380ad85acd137cdca2cc966829b97570dd8b0b5d8eed8cfa386a5be9123dd5ab2784983466cf40ca436bbef7c7e0df455238665016485435cf825718e6056d95e6a5b2710b7cb681f96b11065c503a914fd79fa6e05881ac8ff1c2e34745894552d8a286e370b03179da8bb13f967020af68324927f86875241e73d8113c23f7038681e938affd38b33823cfd5d4af2d303e06e6c9a521ea9df864f2cb80a8513374d9d1afaa808adcbd13b9c876fa08def914d91bf184575de47375c0a0043ac5960e4063c7b11f34cbeea30653ad8ba0af319be03da2da2f17d19fa29ab9b1b08c9e1c16cc32aef63606d97f6d77b2243814e8136a7a2aadd64c4b100c30c30a0991a0e26f9b66ce21ad2f1edb7a05914b45d17d5212381d717237648ba8dd7b235a9a200596615fb6c2b2f042aa770ad8b89eaee52671665458fd9c481f51eb921329a4637a035a282ac442e919ccf430f32c4b0aed6f57bfde5b0805f1d5e0f19cf0469d5f2b61653bec2606db97d563d94ff4bd668f62e79c5b40f68bfe46618fd631fbbbb9aef5a9f90d455240b1acd050cdbb18d9d956f72beb7561f34a0ec8f140ec4675597e386d2f8835365e172673c2ef83a3c1a300eff723c99ea953cad894d53aed2e60b5d683b68c20d48c85c93d6b9bcd56c9aca0e51332b6a0eb7f4770da91e0d489e10ea7ba210628b2d9577cdf1a6dd3e3dc1c23a0164ad8df09a4511e59a3463a6f52486ec29322f11fbaa7a48ff5db43429f92c2bdee70d5eb69a80c4c2be8a17d49f16680f73f6574bfc5f78f6f88064d5053a6788bd3c3eddc274baf326649fd0dac4374f1002e6d392394c75bdfce05f9fa98aa086bf51d68950edd3a76b600108df570af5ddd9324227041dc6ff4202ab40c6004ee2e377c2b205f08a4bab576216fe87ed91ac7a78b775fea208bf0fed5948bc6ea14788e623bdf9700677914c9c2c4cff407d8787a636e2aedae9b3e032ad550d0ae05b7f38023ec54f45053e0d8fd7aeef7bb6b4fd497d86412a28825700fc2013d4b3b2d2fff581012c00050ae5e7bd55205c554973d998c4190d6c011796d3c089988d8141e0caf217f409ce3d1c8d61a49115c31aa8805cd1311200012a0500a8a068daf261e90b3ae01b6a7284dd2d6d848234618d6b5d2d7f6d9bb5dea41b351121213bc40bdc0b8c0ba87acda3ac896199dd1ec624be36d40d76ebd825c2dca0e5786af5b06f02ab81c7b6bc83b1393b7bc3bbac089e9aaa063768f3d86166833dbb7907aed7e2589fcf84e5f481aa18e1eb4ec0849589965bae69b06ea145830790efd3be7cef1076925aabdd4ef0059a800953544a015763af08ea5d0d4d57f9d430bb9e5adb1ddff12fbbf1038e563dc0090e00dead41abef1b5b0f1d12022e23c205923318e109246730c213d9101896f7e61d38da58b7bc035e17a5f45950c7bc75ad55d9cd35b26a20455396d96869d01c76354de975d149e7ece7f82c3b4a3aaf79796d43f52ba487f49dddf801d3cb4b2d1c31f458144b48f1766aad425db7ee708c8fa686fd5b7d60f82198f47af87ef889116c5f5a12041c831d923238c1b201f270663731dbd1704ab7cfa537bc7da552e9345dba3d4de76078de6a3887a09f375ba7370a93c351fc788de93681d54831ad9b77e4a1097cefe0cb5ae701cd21791204c3e3b0eca3994dbc385786e37ad90df89addd4935eb31b787bdd60178e1562c709315b38ac4b4dbb795ec2d2cd70dc72a903182783c093008510b850062648a1a786663714cf5f1b0e69f47ad997dd201d1ecedc2761f1389610f1f030cbf1f6e6f84a03eb230e7cbdf9c21247fe0338de5ccf5e4b5bf481afa374c45f8f37932e7f91a1091e5d0b82479faf6e029a4392c5107c7d6636cf361c9479e760b019b321aefdca6ec4dbdb0da583f478d23bb3b294c76d741cd2b35797dd700ee61a7d5e54ca5137a5935e3ae9a467a347f1c33920ea353d65bbb183690aa56374d24727cd64435c1767745f3060c7094d306067271362c709190e89ba893db008b0cb6eb45513d85d3b185eef0cd39949f05000b6d7999887859d83215911f068c1d75d6d0253ec5e97de2c3fb31c34d83919593ee760f2a887766187c489110c2ffdd18454576f680e89908f31d000d7ee20e03e8b9379356fec90085501cbcc04d681f4076d6653bc0212a120fd1c2c59d7766a5e0b3b244247b0bca6d7108ee275e93bc0e8fa269334bd79ca64c68b32759f4e9d328141bb89ae0db5ed7b33421805154170702eb5c618314a29a594bea226d1ce182311ce160e9e935661ede2d98dc17501431c4ec686e9f035ae06cae117e054d6152083e10c0c03d082618b93e564da86f61e0e0f9d4a1e5efae7e489469f5f13fc9a3c27bd82322d04572e6e3e692b2b7aba996a2d6ed64ea7a7dcee59adba67d543adb5566ef63d6cf7dab6bd85a00cfc812b28037f7e28d5b4d56ab58256d28889b95b8b6fdbe91242a12121a1088566667c78a6d9a986b313ce00702311e8c31d5d075f52e290cae1a554fbc92466674e36987436f0d886c361222222a22bec7041f0f9582959b5101e1e18042244a8f0b199b55b64d229b8bd269dba8930b6872710283383f2e96e97dfaa1c34182d4f27415a952713dc3e5a95ad2098a755d00ab86ffde8e9a46fad5ac5e4a9a21a5eaadf8f69706b7030fd6f91c94bf567d0e8a7a02079e39083e9c741b322e6c45ad1e7e904f7b1133947e48888ae7059dc2213249dea3fa0a782335b8453427145c931624889132a3922265c1194910023e8b221321c1147105e76a3abc88290254c705561c18f1ff00e38c6cbe33da9d8ca5be86d66b392e17060681594312c9c198e766db9585fd962cbb633331b2c46166fcd43ab526c0c0204bbc15ea3c9d4294ab7a7021e0faf1c07563c682c37b56ae5c8cfcf8c1187788b898592dd906428b53052a488194c7230c121e564e3904ef55f4f128d455ec678bc09ae6437ac8bd2615dc6b062dcdc2ad910f296d5c262190e78f9d9f34d38a5f5eaa825ad673dcb7acf072bde5e53a74659bcb50aadfe4497e1884c3a1571248a43f42ccf7188be9284c801d4251b930405619844b131089a21cb64591c8631443cc4a4b4365bb9c4ac2d4a20202989e0d30963f4c93438557c64b2c934b8a156a1bc271357c33a41c984c8c1b415447816b99c451e6791c35b240ec141cb336806b58abe3f939062c9daad6641a327a29886851225dd12b3362587b81a8ec9d01b82300ed964e08a622dcbf3e31007e3c26097742a4587a4c54223a51708928e0222fd05e92e809e1ade0c260e860d2ed58f446abdca2562ba299148b55b0d8a69884371280e495189429120e06ce1c0cea8d9a9d5a11736c6218b72383a4c8a4317ce71c805ce71a8c9731287aa7d9e76d6381487e68c4356cf6d1b1a402ae7e5869a115a4aa53c8cf313ce29257defe60fd45a6f92812bb4915aef58e31e61a43262d68b94d26a82109a5e7d9713529de9548d113e296384efc9f7e468a653cf1a76f6ae87144464b7d9a5346b8d747b2246cf46f6f690629e7453f0d98f6e8f93a59f37efb0d9632c2a6acd3b0fd67766b321a053562c8126658c52c2cb8ad91de9c3a8b26ee3fc2ecd45401a348723267ee659efc51a6396179d326258c5b0d13b128380d18720ac4741d57a9ab5c29905378459948f9bd6a99f4ed8b2e0e549dbc9c406ad61b0d0964934ce2cf85508591e76d1196d8c27ebe46a5810b2bc27a564e9da970ceafb19b42e02c2ce0a1ab000f765b0bb820e40b0cb6c2a905962da6187dd71cfa089761a42458102fe65f0ba4ef5a975266a63b4ac2725c52e3a2d4bbed8aa1881e098dd7081adbfcc653a5cc82849f034fada96768cef0ca65caeebbaacebba9ab29c4e56502124a355ada256c6f4d2a1698b81281bdddccd3931325266d0eab38bcb5d3785104288d9c057d236fa0e2843a9f6e4add2f67e349e13518c423b4d6bf7f9c09726c3142fd57f3cd90967d9e9e3811706b9a70407d3cfc78916c4d5782757031e291ac2f98d01f71fa552522967c5a494544a4a29a531d26ad2b4ebba324ae3ad59b1686516c39e8f35aa14675b7e4dc0fd28c33d1f4a63a47656cc8a36466a5d8ed2f72a26a394d467d4a72631da8ed6e4d050873434680ebf67ad8a2c500183e72ded59efa2d2b2e6b4e69c5aab64b4e6dfe0ea19b4db566d285068cedd3977238ce67a73384975b3643b15ef4c35c68777e8eb1fb59d3b7c84dd129ec203c4c38dc201b8f7783b0f3e2de4e81728046c1140114820a662a29315b0af76661070351a7c819f2fd6377d76e3614adfe5e8d6438af726b09afaeaaab52eb3512202f6f5f6a8b78f80aba9578ab7836d06011428a0c6de137c81b779070a1450535f8fc17aa3617a9aae994dad36081a2861c35196c028b3a18f593f6e42ece854bc56055a7640cf9901277104cc4408db5ddfd12a382fc4dc5e1a70e4e814fcbc178e1fa280e7ddbcef45fae2a4d3a2b604da9c77084b071d025f318965bd675d4f5ef1bdf9eab358a6c3fac4550911adca301cc2c21183afdb1268d785b7e68e563d1c9f43881d241fd5391a739756c12b304482071f1c85c0da1246291b75516bbe6ed83a60efd0d730a8ee07936b244e8070c32763d3aad09e8d798725d0a47cb1077c0f65bd1861940f46e89ceb547f61ce39a7a4d69c11c668fbbd47239ea639219451526bce394d6198a639e9a7a4d69c73f6a443a8fd3e6168b543f153050c9d0bfca142ad19e17561d7b582dfeb8b17b5668c11c608e194adc1e799863d900a385b18638c3e708cb17fde7b60a8011226046113a3365848c137316cce3963b43293a777f50ad87559d2706b8cc24eb16004c35337c2ae0a2cec00d3c36d84217c74b69d98f56aeb65d58935be367735a6d8c468137ae6352ff95aac9a50863254818625381eba2ad0d0050c1f71541c235da1208b2c22764fb0c118a04007222cb31b1187058519a8b006ec5c2a36c1049562f3b228cd4c572901299a4906a536b4c1426973350f67c73621b6da8d2bcc11a349d35c7e39e79d965a4138187a9653b638e680148b568c378ad25d3681ae29dd1d1b11fdd24f31e1ecc8a06d42ec6803f4ebcb82b8d80004d7c0f92cab5a5686cdcbb26a8e186766135d7d167da49466d8bc78f4c0d4a292526a552b6810bb2830f98957d82b7268879bfd8357b82c8a127699d9e410022b6daf46dba2283de5fe78cd25945db756a191b61c428c81e4b21b35eb0cc7c3d92b96d592104ab86af6daea4f2cbd66591cf56834aa23fb7280afc32dc332b389029320f87a25451289e4e4282b8161fc8e4edd1c42d01c96655d97854d7a5da4cb932e2f8d74e588f1ea548e6849694d3b2a597a5da5cb01e5a099b50940707640a61ca4fe196440724ae7542fca485531dec167a3e3fb5297c4c32e7ede48297c6f273e5845855bff8010c60837fae406e98ba675eab91c3999aca6f08a98ac99cdc83e2db3816d03ef8c66a38e7cefc5930b86105a27e541cb3138de8b8407cdb2b65f2084f03d61e78c3831ebcc3b5f113caf655d4aec0da453b2093c65036927de8c66c19b01fb473b313787a53c84706b2072523a1f1157072101ca6e6f3eb626fb8addec82b1cba294ce39e79c319ab0770ad3c1524a29a58c903ec3cc666a2c4eb4ec823ba55ddb122d58891d0c2f4a07bc3a0fcf63d98d3c9cd7e5c6104e49218410dac88610ea744755848d755a07840d2184536271ce39e7c4b09ce382e1b13eb659fb8e9c25e63d4103fb75c53b61431c5511367cf2a46dd077120fbb274f3094f29aac4e630863ec4b080773bdc2cd7441795daf177417941042784179bdaf9c73992eed65296d8c86e11374c64e659d88e5e183f219420853f3e6eb079be4bd03b4f55993ec14b476802fac67938d125e1a3873dc110c49751875066dac129b97654dad6a4d6e2668b2d95e9366b21147dccea92e6035537e38eb407c499c5dd48dd489d83ec78f3eba39e29ce382f0e8f095544937bbdd46f51a8d7e8dee96ddcf4db51eab17ed203a4529a431e2d8704a79032f939087ee089e91d2471ae3928818bea6c371cba79c9986514ecbba516602a372a000c393708e9180931fdc4f6659a49dcab2d1352745f8c1c106126e8665c12ed8d91e687d4a35ed8467cccd0c36992236f56824b9d91e2de5d921b1b44536bf32af326d5a6eda4e6fb1ad6c3985b3ab6c3906673789fc9c19fcb7cc53cb35177474f3c3a325339ad3b1993158deec342db35b1d6877f4785f2f9cf89b72f3ce0f9cf2b945d336832702f07cf1377472c1f391b6b8d974d451b627e460ba7ff44bbf22f8e20eb978f834e35e911c68f63faebf1f3cb6479b772172d990802ffd568eb435915764f52d0eb420411a7b543c9f2473be873b0d746a3e6e3ddcfcb020446266837247308cc34c47132e40c1138af0a8b4398cc211738e2bc230ce72215ae611d117681957734c133373c40cde6ece0c3ec9703562bcdff788198cd372edf92ef8fa0cd6aeb5e4cce0f996d782057ef761bb007c7944ae533136e7605c36075f606c47b8223cba38f132b361b94e8348b822962d5a21da3c0d346d4b5838efac30cee8adf07c7d2b3cbaf52c936fd5aa1757b938b91aa5f79fcfa3e2fd2001655e5cde7f3fa04cea00b8eea04c008866f00db49c3383536ebae9a6ab6c42546b032d53d43b099eb758607bf7b2bd48cdaf6cf32aa66d1ec5099e01d81c7c01c0e6c3bccb36af11e179f3e8d9e3883a24d92db9a885e72311dee26582dfe1a663e137d4a9d773e7768b202bb4e0439a772e6d80bead8186840e13efbd4b9f982bc00215d880fb9647cb8ec8c281961d5113d9a03ee7a69bd339e7a69b6d44a756fdfc922c49f294086da149abf27bf29ae0225312dc3ff50abbf7de732c9d6a6c09da0b1fcec70cacb6823faa7301aa10fe6817703522b5eeecdd1f107c41f029818fe8357972784250a67485e6f06468bc973e7d79a0710287c4892433c0ce75e1998087070965783c1870369e11948e8bd34f49120ffb143d25b89f9167e3257933fa3eb97d92d4c35ba110dc2fc9cbe8bbf17551177e78547f8d4a9b96d59e42bbb0edba1540b9769b404de0d5687f35dafd616b50fe23d6f8d07e025a533a697b352f2322d6a0dc13948054a3bd6b5c6d0263cf0151ff321b9256abc0d79fd0841bf784d73e706626262695faaff6b9dae7baab02d7c3bb1d2760ec10b3bf6c853dd606874021b304da03dabae66542f0704f30bd15371936f027ebac8aea5cf20bfa69a848a808c8fdb8196a62d9e1649888713310657b3d0d638cefbd0cc7133284661a6954b3cb3f57a3d4f384f8d4cdb5cbdf0d594ea7ede14724081d4d1af692461a6d37f21139e260fad9b3220a478ed01f68ae536d4fd3393a0723afdb227e990dbd4e9f106b88a45694718489094a8b6811bdfba1c1f2d78d1816a12406fe74ea18344243752e48e8b44c8552a85fe0e7dfb3a665cd5967ccc914cbf75e86e361b1e4aabf6ece699eb7a4534298116d3e3f2cf21b7ab04890209cdfd07b58b46ad2bb0b26712288da239a872ec14431189a40261ceaab712e5368efc78bf681fb56b3220b425e103968f0e688f3fb912dee1f16065a6e1fede347cbbc81f8c0c210ec47bc815a853c140980f67025f9d0dc314d6f374d84f500eb1407ce2791f6a4123bd85d01b87f8805dc5660c107eece3399cd7b56c999c133e5f27c3668f9772fefa2de1f0f26f0fb8943d4828270dfc422851593b80096a77ed635699deaa8234d9dead47b226af1f35d94c574a0652da59dd62000cd597105216cadb88212dcefef8500859d1559188924cb9252c648d3ddf2bdf8704768450c1f5bbaeef85aba27e5bc2d812663bca5e35d6b3275ea6df4c1e0d806804d31be68d9694ba0bdcb1be13c5294c4845d148a80851fb477876d09b45a1da6adca7df8badfb3a9ce999e2dee1fdec14bb7c636a369d0e01d8cd8662488b3d15badf07b6f25350a459eb0820a1284514a88825142e8a8355d6fb1a13c0dec6c88f83ecc74c4ec2f6bc0756da75ce376cf5d188698e384e2bb281df03a2d54cbca4c76a5397993bcdd87b971ecc6f103eebbee0b4d0e765f545ace4d2bbaa7e31a02be3bfe650d80d98d950d011ffb7ec2965730010ba29473ca39a373d3c9e85c7f4e17bbe5f62c7e94c637e3b9eef75ed3760f2563c78e31c66e391bc656cd487b4e89926e4a79c137b56074cecdd66edc1b7d6f3fc21bdfe5a4746aec9ee1cbe4e74b05117324478a860deb7d083c1bf412bba04c2a95aaf2653704802b1d344a04847f5b0ae6197833bcbcac4bebaf88eb5ac0ba49dca217bd2eb42895ef0a91a3534174aa8928c202312919294a29a59452d2503d525444b143128628c0c3274fde8c57802341d83dc1063ece95b0b3c2481016f5063ec9c34e1454602705a2038c01f76f3c350481431a709f810adc1f030ec47087332cc16111042f8801b71594e0aeb802922b382bf888c10e363c1cd06a045807fee19777a61003ee03a8404a6b946f728063861560b6805370501d400c0e89156e162f4c50b2020f488f48c315ae8ad508b04e9b70dfdc8124a5195426316694917002ee888db207823ce004f7e3124b60405d830684d41b7073015b65c07d59836c944140072b0be8036384304218238430c60861ec338c32964fe0fe7504b70ddcd7edc04759f0a30926d9aceee4071bbf2630253d23bdc78fae5b273ddb9c8321dd4c294ebc4b3c0c9fe11bc36390e2d1e7064f8f7586e3dd399879baa1746087237a923b82fda4c007c60e4ffabb39e2077b098aaf5f97de4c9f53035bd65d765d1e1043d296e12dec4a77644502ec320738fcae7332b2d13c5c3918f99aafbf1868d7cd3b0fc7174f9dd546cf4202840d6d6cf860a71e0cea7bced9c0060f3b2b6cd082878721f10a2748362b6cb0daaa700739cc392724c2ce0a215cc0a63b778515428e6053a79e7b33dc9f8dbeb36208931a9f8dc602c033c3c0796564c9e7d8950eda74710e06f6595a5460df6c3dba19f4cecdc8fd48fb26716cfd8079b82779c0f38d235f6c5dbec22478bc2738892526a6d7693caf43310f05607aadde227ef3c2eca631842e9d829f83e6f2ae78c10f2c8d509dcbc9450a4b0a5f6ca4de8c7e6edc2b495d923e134146b42abba21e17d040abde8ea7fac1bd1495917a3660dc8c46f264e0a659c60c1946c8c0dd1a80cf4e480684524a2865c89011e9b30c4a21a494c227c30a69ab86b3610f1ceb5115bb68ecf9624738449c114aa8845ad5f0e8b356d60150fd1a64cfee68abba1f51743e4abb2b76c539e97c718e32b043f4447675d0b65349b46c4738db76307a0607901d92254ab00ec453471b31b2831d3220898309d600d413448601e782c88e80e7c59957625e0fb4e0f83805bc73913d7a87761482c40bdd5ab712dcdd5d4516a8c041ad697003bd3791aa1ab468861f6eb079d9807977e47b5360d7e91bb4f91d6ed0ac1c595a16462df66959b5d65b9685d9f7ea76bd1edb5033d2cbaab45a35d3719d3ea294c8116f9d010ed75bd6c5a9d33e07c3f5d5ca7458ee0c4678028910ee0c4678a20ab7d73a0dc486c070bd1646afd3b76b3b87b84cf8391769b5a066139640c36e8f7a139bf376cb6ddd1edb503d1f98ecc14e84abb55a1134ab75aa2f1bf3a67d37cf9c7e9e1d507641d8cd4e8913b2cffed55fd77545967af3cbea6d8643fb8bb3b4cab970d80551ab5cbcbe6636356563f9dc68294565e50ecab478537bb386dd7c7abd393561f7b83027184a647269e0853a867271acdb19dbf06ca01c335dd675b23fdd4894c262e55e272429f7ba3dd775dd7c94f97c3de7d088c1d82f8ed5008bbd66d4d5696c1dbba86bf10e92ee602647cf5a95535b1f8c9b53a87bba2deecaada72b2af7ba58b00c07765dd747d765769d74ad44154a658f655916ec1e2e32894e70a6f691496c42517e45a2d8061a8964b8b09862cbbc3beb3198c6abd328c79edfd569202c7e92f820c1b0873d0663f514e515655e9dc6955a1385462629316edade3524e04b3fe7946309ba52a89b531fdda02bbb320cbb2e0af5f7f9d349a3d7270d8c3de7d0c0d961e8748e2c9bcc7060c7c96e7e5abd8c19979caacfc75d6f8671730c9e2c4f4e8c0201b66d7836a6e302ae775b88a934ea6fcea181ed67b5cf5eafd2619f7d3359fb6b8b1895bd9e5a55cf4203d78a005cadaad5ea6ac8fa18d3aad1eb25e06a9c5eef621b26f06c8cde8c7e7d47a2393b5e940e78ec38e0b1efbc1eb85ed95d564fd3a6bb83eba6d338bbb10d6fc674f1b7bdd02eb0998e5dcd737d88646bad774fd96c8f66e117d4415a507e41da0bfa99250783abc93e6d66cfbe5edb85619b367f6df42db6fcf01210a7dca67c3e25b3a18d535a983a356d8fe652b69922d4024ff0a55b6437b25b8bd95aab0c3c31ccfead6437deafb7a0afcfb6235c30cefb75738bd3d7b75517561ee182b15797e16831b3bb96d92e473d15e5baeca56dd97d9bc5957ea37f1bd5b4235c708e0b36754a3e223dda115e0f56d55715f6ea4cad6959ad371fe3eb5d164fdba3b504d5f75bb65a6bf66b239d6e594766f5a2d87889e8542bc9e15c49bba42de25a35231b7cad19f62cbfa191b517bbf95a3644c51729c3a15d5eab43ad8aaf1f6536b5d69b2366adbd48d7e7bc0e4bb1a44b5f16cf066629f686f0757ddadbebe2908eddde62d7eb7551f4f6d66bad37df5cafbf6eebb13b8f028566ba6549190eec758459d34db198b52919666bf39028a9d6faeaaa8cb07b308161b709ced663768355ebf64fe8d9b86e1f93cb035e96fee63784ad938e51d2c59ee3c5eae7b167233d1cc36cc4a4add6d3792fa137c33aca96bdb48deeb26def61bba7819a765f914e5957bd2e6a7fcd9bb37836ac67a4be40b18196edc5b97460b7f557bda879d2ad7a8b922e0a14d9efe620e9669d38b26e331cd771eca5afd910165fd7cd4aece0faec2f3b96d96472621058fa8addc2b079535b5e6f9f9156bd2c9e8d876bbd4e7b43b86566c3e3d98b423da0e5f713f8b7df6b827ffb0eae5bd670b5dbb639f852e937ebd8b683af4d27627a5f166f863537b9b5ac60741053785052ec878a67e4d93019e51d1e8c33b21fdd4ce539a6a094482f9e59121003159369bc9bebb1571728d45b7c4096c36bf0a48d6543bdb4c4bc88f113bc06c6e14f315c905c1bc0c5b6b5000a4aa2a45f8e1ba236d87bf42fe28b5edaa998173d9d54172f21841042d847cc601c991f35ef73a66bb6ee1c5dd8f349559f535986dd489b832fa3ed5178b375eb46bf6f1370a736144275396e084bebf4aefbefbd07f48e704318e71dde0c373784c3f5cd366d5dd7d66b9feadc3510638c313ee8fa29498224dac4089524c1ae8bbc927c48f10ec49d8b26814ef01a29b23bfc8bf91c24dc903b0ebc4818e186dc95483ad548bc2dc7082036cd881cf72cb10cb24f68b9993413a056f5fb0b249b042232572c3dbc3f854c16b44ac65dbcf8b4c313dc45120fb8a70f98e7f903779e41704f9e3904779658e07e009e2593d4b3247a79964d04f02c8b708b67e904f76d0fe4906e39a4e5909643e4901f5c9d730e4227dcc5da9677b0d3342ddbf253b9cb1c49a6002887f019c12baf085e89ed4485dbad1fd269ba7fb4ca09a8f5db54543e7ab3c08906d2aad155defd33d2b6f7c39da6f3ce9343620f14d23c0fc320d8b1c0659d86b0b31b8f24e1cd2a77a6a3dc7549023948ba3a10939ef2fa0a6ff5d44abaf51514283495ab13b1d35036142834d3cd2917653e62e7e24379d681af88c4cfdd3acebcc4ce2acd971b0886edc49b010fb7d12b85f0f46e6c479c308e7b0a4a7c1440bcf4ce70c4bb9b08b069fde2ee522e53ee4ed3a52d0a201e8198b75036dba9f9026c5abf9c523e37021c6533dddae4dd264797db667fe59c53ff6895c3a3b713cf86c3a337104cd396075a763794ad2b640924911891d384c8904482fb6a69b9ac772ba58fae72138efbbcdbb23b77150994a4074012496ff25917c5801deca6123b385e0fb46077512477d235e2f396750c92fe9668c1f2efeee61c888a28799c78eb325a276dd9ba2d3d96ee2efce580e71cf07009050a6dde5feee2941e2f8ecaddb79c135e396195dbc34c4776a37263caddcd23932905e51aca35144d739b360f2ded0ea2684fd1ee1af03e6f4eb90ebc913d4757b55b9f52ef3657533608dd530e1d74cef2e0dd95bf8bbac9fe9efddd8c618923df32b3b7646f794bcb96e546caded27219b77c32025b9a09febdbb2edd4c6f5d3b109beb17ed6695476bc35eb79c7322656f69b9599ec4801d477adcb43f683dbe5b7fb7aebb761bc32dabfc74d2276a1ee7a2f468efae3cae90eeca96556ece09cbdb72db826dd9086c8f693927bcc4b3b21b159cbd566b5f96dd6e138ba9a4536e9b4239f821819f0e133df02bbd96b689c510ed274f25b89574aa3f148f740a3a2863fd45eb3eab47069e5b964b70117ce9533a8b7a66519192563d2c844a25205e2a412036b9a45ffa755db1efe69cf0fb4aa643005bf8c116de77f0e3e954aaa5908b8b123ca30e742e2705d4a9bedd8126a455590e71ed438ccb09f7107f6036cc071f0e7397ddf0e130a76998fb706cf321e618869db62ae5d8b556cdf80fc78e9da555f4d8695ad3344dd36c0fefe1a6bfcdf4942d3f9c62b79c03dbd248a5fe9d5a1505106ff9fbaf77738e0b4e398caf58968dd6108872b78d64e928b7329b518ec5a5d116b135d4aa9556a1c826594328816894b694cbca761979e6f494cd61ab9dde83cb4f97990d0c1517ec72954c078bcf1bca805d5e9f85b1459ce27269cc663b857dc6a6750afb0f9ba953d8517463e914f6d3f361cbc731b2d573c12c21314e8cb73cc65b60b61897217fc82127d4bb00d8f2c300b8db9ce5a2d3368904c899524af074b70de0295b3e95a00402ca03b84e7b2f0670ba8a0036dba3bd6739c4f60cb13d1a0c000ce2417af2e409a641310e48bef7de7bd14598ad1bd6ff2ab7252c8c63fda59b7756b8f4b759bf5b7ef8de6dee488a439269e097bd09c4e33d79f2e4c97b4ee1f7e254994cf8ddecbe1d886d52d1f3081094994cfaa5df733201326d0faf00a008beb8f98309265d88a853b647b38a583d16112036a9645a3d5309864d2572489155d4aad7c333c5307a388cc993b2cd2073fab40aeaf4cbbbcb9d0f4874eadd7a0f005b9eab4cb1cbcd938a3c85e03e8c593483d099cd769df6b096e382b08beb3762ec5bd6b1de4af2b40a0601de9740a00c10ad02ba122843f4867ee2cfd08f8b3f5b299a4e03e71212e3986e98de627a565f3ab52f9da555a3976e5b4602c197ae281bfc7b47cc6089f2b884c438d65bc5437334b4ed31ef02d8fd695b76574762f794755de2169f72b9e51cd90de194a3d8944ed3285bca8d9d2add6ea74e953eda583a55b2fd52facd265cfaca71e99a4e0369da14a3658bd1586c460ca16ece0c865046e6fd4885abd1e22da1cccc55a7418017a7ab641894723f4e4934823bbf00cf33180818b484b5919e23cf6095bfcc8606aa6ca79b3383df4f40b84fb77fe0eef1f94182fb3332aa57e4e00b8dad009b832f406c38d8e6e0eb5d86374ed397405289249a3fe6115703c80c3279a28c0c106fd50df00258568f454302114002c1208805e941495aa5a455dafbc7eac97a200c6ad58aeb9b5d12bca2b2a5801c1292eb977769001a05006273d98d9be1aff823815c121d0bbf5fef52cbca6c5a4208df7b2f063b087b09eef711dcfd63a1129c248921dceebde9ce8c9e40fde7a455f236d22aead5bb8fa853fd8a5e51ab56fde3350900007c70e9e16e6f89018305f5c2c5e9b5785fd91c8c2905a5a4914699837130efd74637ea60de99287245f739e954afda00fd7ca2af08664aa35326cc847337bfc7635668f9bde85d427908af965d11133388242354161476294db91a3cfa381c4e3916ccddc5e5ba8b0bcb75fae6b13668d6e771e6edd48bd80689541f52c09422429cb34ece1744d6f7de23e2601adedac46d95c8dd1ff0e552a0c2926ae1569bd4221f68ef7d480f6e9d2258548b3ad53ddebb45b5492d7a25ea54ff55b7d5a2da249379354dd4aaa28a7aab378212094aab37a32ffc2eeae6ddfda26e72c49727c7e221b8b3782be6a3d55a89dee06a7469a569a7139056bd5722f812a465ac0fecb5b4aaa5552dadb05b897cec4a095a9e4d2a1194714b7cb46541ab80bcb4228d321b575af9c06dad2a760ee6513810bd19ced570efd337381beff00d1b84c39b319a440ec641194a74a4652e1f4d8964b8b8e04c8b10657aad3a557f7e80829408419958f31c76198442506888a868056562cd0e842520259e524f8988abf1301fad7db45c895c8d11381bae34821209de0c5b448b3f1128280a41d40eec3b3545f109a7a2455c6a155d8d478d2004f7ab7d0ff5fefa1ef6a41eda776a48d7aedd1e8e7435927cf87d4712107035396e0f9aa06741f6a288c03e3a76513b7ca41c3b8a88fabf9ad195e288777757839277878fd1b38f6e0f297664b7c7abc93efa4e4d763f2bcc6eab941d29174504ca538e72537850fe6a502ee5812fdb4e4ddd88281d7ba9b49ac0abc1fe6ab04bb3cce6dd11ad56b87f56ab677dec8f15b24396c81665abec470624e3c98632a2114f56345a59a09eccc7f6404ba42bd1e887909220a0cc1ecaa0dcb1d20f28837217f580d2b12b02ec25eca58ba0dedef52b911012160a2dbb7aec0f059a46c4d5b081312257834e9e9eebe08bbbbb59db6a51252239d27b5c4b64a786a66bddb08d07e57a30ec472d822f6daf832f3b353b35a5213d2520ce95784a41302c68f1d40737cf95832fb4872757a20aa402e181323b35b016a93eae86bc181ab44c79268659697506ed3d0a090d113569d58539f1517fb81a0f9b839677f0752db3b968c4d81cb4f93c57a3cc46d28b723649d0fa637f8eeca844f0a52b503dd2a9b9c28a603e50063501ec15bbabe91fa02025aec6db7ad88b8004d8ed454de08ac0f6c06eefa4d90eec4e22f0a56e523c27345b44cbd86a85fdc080603c580f4624284889d01051d1d573f55c457c7e806c112d5322dca714db766aaeebba78ac6985fb8f044f7397ce39e79c73ce39e79c73ce39e79c73ce39e79cc35627ecced53cae44455066a7e6e707b440a04cace95b9ef956cf06caca5d4ac4c1dc4083374f221e28137df209ced55ca54aabd2caa7b48a3e3eb754895e934e35512dca0fa382e6707442832b288351ea6e1ea500f72b5169d5aa9c32045cd4aa5c5a957c54a256392bbad0a45622abc426b141d89f111f28f363940227537fdce805b8474fe0fe2807b8ff3648548970ff854f25c280f25c5940f0a5673ed51f3757b8e710ee2553c8c1b405f46e05924bab150a506985fb0191f94d29825d0f4d603bb46d0c8d6ee4ddf938e79e914ef950fceef3814c60faaac40e76786eefd03dbe5b44a7dccb022661e3aec4c3f01551c122ef59f3e689732c8658124e261618ae46544293b69b0ba269a557c4bd08771b5704ca68fdd245dc2541d34e3176cafe4d6074d25ff62e11a36b276d3d7e20aa21bd487d25954ed36f2b02656011580416e954f7786a285243baf69476223560ce1d0a218431c26c3e6617fbdc64bd76b0ed8d3e6e2ecb01ef6e0601110ce19ce6c1ee4122b85d13f98b98dbc8c3021786303968daf381f32ba2c9402354bc9083f67a8ab491d7f386d82609dd745914cb271ba313dfac5dbecc9b73b02c22c774a2ea5cb27330605c16162c095a66c1ed52af251d0442085f16b0ba1ef76501374851dbe92e834b709694dedcf22c63ad3bf0453ca10761105a691ada40b99d4e47fd099d4ea89fde1d04a38ae8942441f92d8a7a5996c70d4aa1222ca550132ca5909442520ac917d12a19ef5bd229599fdf9298eb8d32055f5a2503e3f974d9e2ae656852b0c5cd6e09e966aa035958685858ee8bebe2e658ec62e52cb774ba3a10bb58f9751cfa885d5c9c1559aa2f41892149fea297585e2d2efa96648f1b8aa647529a7ad895cbed0813c6815f796df9d35a1cc6d6020f3308c078cb6bb418e662b32bc7565a6c51c488bf36bad97e31dd58797c7d2d31c6e6430a172554dceccc31158142fd2d9142c7d3feb4d2ca8bcb1627b19c65e5a8bb38dd453cfd16e34fee8a39c8a011377978c52b46aac40ece39183ee7607a8a22ebe1122d780e411fb14789c22e037630858f2bc7564e2f0a141ae600eca240a1d19b33c4cab18b737acc3165ea3ac5728aa7975cb828b538a9c5492d488f142dd2650bd24bb74897b0449287f205e9be78e9c5e5f6e2e687e5cae3cdb5547a2c3d5e4546a962a5d0503a303dbca73ff86c65e5266a5ab9e95b5e39cd564c38075f10c63b48bad9c5afd7c3687194bbd8728b4f4a4d37ddbc72fa48af43136df194c7538a4fcf267c8a1767e5a72db7b83926bc724db71a969bc32653cec1b6e4b41c5336a230084f9b6d198e2d26239199c0b407a6d46e3232d2fc45baee74cb6cb0c6acec4666373153e272297a9add441df02626219f840ec5979b41af73c2d4ea5cd94d5bb92d91ddf40acb24487714fb3597682c219617a61f901b753094fea2d673891fd94c06c6adbd0d9a38f2f598cc6ee4b19ba3bcbb54f0e9a0f1f62045156348829d14b0ab620c2bc02418479da62b8ceab2185f1cc626c57b75d9751931d3aa774aa3533331ee22f11e93738576bdf8db6668f4cb7b22bebbdc42b07bc492a0651ae3301ee3f6f881a826c661fc6677739a676e2ca736e1405d5350a73abf18ee46d192c4d5fcb45cd72f316872c4f467fa25e668ee678a213885dd8f12aaf0338511ec7ea6f00183dd8f126e80635c0d2713e3c323042ba03842169ad4f45fa6c3c2ef4228a5fc51c20c228c2c41389977cd7b118e08a75a51b5b0cf24fcf228c3cf05333f80c82ff80d3d17f8650cbf8a1f029ccaf4771aa73afdbde4aa536129282feea9532cce1480d3c9c5cd3bd8c5dfe67045bd3e7a63a7506742107e71fa7769a750375c765dd7461f371cfa1718f597ddd0bf78a51bcb91c0516379f1b85d2f2eeae61c8bafbfbb17cebd783e96b521e1cdd8dcafed1979335ebbf7ebd4e26161cb3adddeafcd7a8bbb95bbd8540e6562de87628686f2738c1b52d9e2ab89feb439fcdef567c2b96eaec75fa763fedd8479b85328682db667e4f9506182e9a36c1c7030dd06dc6ec0ee0d360e8f48bfb4079c902ec820a805cf04b88f07dc46e4f7f37ee812faf8aeeb3a0e87afef60979940e9d1aa184309b0e576f0db7420b66414304ed32618f705c1c7052349d0f2cb69624410b41c1343c3a1dac5144db6e0e451ca392dcbb22c1999e8d106e86374d072ec88fb39a1b9ec7dd552a8fd5ebbf7b0be20a5969b535a91c2abb15709a0c1eab08bd6d17dae2d3790625057eb032d37911d68b989146995abe1b0c3451c0943226fe7b6dc18557230fd871ba350a620c23a8d21bc8d3126684f8b47345313d90210dcef20b411dcaff047cb9a0e120ea6e39b48d6b47821910da2e57824531a832218926091297ef6da18ad5d3951f3fa89dc5c9c314aad0852562a27bd35e53bb3a19f52c64844299d5b84cfd1cf8dd28dd25a8f9dbed6691fcd6c281318fb9c731ec3b026f07c43d59a2f0bec69f418a5f4439da2563db6557b2dfcdca276db2ad2ca9c73cebb3b7a1d6d94081ae93b37b4165f798f56616f7115534a8ba3b478e9d9ebaa97d2538cbdd60da71e3be561e1ec1846696e6c27cd6cddc12ebbb94e8af8b15553357a18c7a46498ee6874b3a68d1e5b461bfd61d8ed19717d96d6c475635a112dc7e06c3f376a95d8c1f3b7b6566c27769abe2c0bdb1c9e578c449d9af559978785a30d5c792a4ba0963cd94b3ea3f8ba59a328ecd875facb3abd46e4ecc0d93de7047b4e279dc63702d8c20fb8703a1778786610a455cd038409b8459e4e358d36ae53f7520cd539e79c737de7ded3b2bbbb7b06e71c1867f4fade6e60337874177cb586db647adf7c9d9e4219d375f025a3b647b37f0f6206ca980e4f93438856a1a858d82f6926525ba72d672667e6c152c7251506aca5a8c40cb6e5452d999a11090080021314002020100c078462b168402c5574457c14000ea0a854704e1787598e29648831840800080000000044100409009663365d0df3fc26e86c2f4b93298a4c0b525111287476f7f0e09710190119e6dada482cdd5ca35d2432560d9fddc3a4e0906a835192122fc34a814dcade8aa881f181b68a4bc45080914c3c0be38b42ffd91429172ca9d6918d005ea107fe08c5bd1bb6dda02d67a2e31c5b5586105e6ef7d4b080b2556b8c6f3a7bee0a110fe7705bf1804736b14ca32905bb2fc4d3877af8aa227dcb8bdbc709e0dfbc70bcf0fe82c96a10994d1c682e370a7a8098096d468806508a8f984bb623fd84f90876c98814b602411f1573ec8d6281607768696d19ef53b0db6c0013f324686a0d3812b34f99d9c12610e24bb766742055d487eb521a21341eeafb1fe60d84ee60d6d8c61c2019b4b24cf117a89df91a635020faadcfddf57fcc959a82ede000eebdd5a1c58b7899a601fdd13f18368efbd32fca36c9f388c10973c3cd414f8dc8a4216daca190929f99eeaa38e3afa15ac98868df0d4878201d4d4350f0042878215cb9cfba9ba69b46c099cafed60c9daf4c6db8ab4282f58a864b17e49cd67ff5071234491dd49881aea896fd34676d0211a2912b39761ef7403f8dd06f9e687bc4258c6ebcaf6ab1d32c73d78de6f5e5565d76b4a6d9d2154cca1e3632dc5d32e2a0bc664324e9ed1ede5b0817a76caf14087c428c70a0857ac8026482f472a7a35ac777e51db6493718fa79138243addf9a75f10aa13e794c1c5584d2dd426a781bbd6807c541a62a203246b3dd486c39b78c9a0a412d76e818c2772268778db9604467fce6628cb308a524d2f640ca652987b51e90bd37d761dc2f3ca760ca81955f93b6ff3c1fa69d79f7d14054ec0869998687ef6ecbebdf709e258c1e5939a0694055f92a3e2bc698496b624097c1af6ad4929b233ddfa1c55c2f7ab7d2435c56814b0b04c26b00a4bd36cd09c59511aba5a0c8f8115134f1fc0630411fe69d60d0a40e3580f7aaa4e00f5fce1c4d3207aa9346dc522e09de1d58007200d2e840009aea1c477580c3667a16d51563a87102a7ff0bac4779d696488f87089cc9c959fd39d0c71b3253fd164505f98908620cb37a820f1d67b1830d3c6697ec33fa008a1b0301e8f26aa7ebd5795f6d09476de74fae2c6ec262fd1a6394b67180b5c694264a5f68bbb0548d3cd8dd7cb8f53ca01da149939eed14262b167aada46352a035724829de0307656985e958ab1f81b80d3d2881b7d7d497e872b442b98d10039a44320b9433c337000cf11b5e7e51d8917b48082227b6e59d241d40d1d24ee21c9e948f738eb0730bae7c5d28d3c2b1bd6bb452ff267a6f65a193f081f77cdae30e1b34ceeedc6cd9135613e2b70469700bf50286d8e538667ba4120c8589f8b24388072ea54871d8d41b2cc553c53161970e5ea462c5084d025dc4509b7a342ce86791d9df233caabba84d0be7d019274c1be9382cbf47b477cbb48112f26bd669131d5111e3845e7f8b098b7eb8c3df0897c63bcc19fa6cf84770714706267e946f319b53090a14c21c7db0a52f7b2dfdccee138b3bd66509d449464cd5096487b4a5d6c92d6d15a7f8bc1a2980e89cbe8fe8d0c450f60829fa6af7fbf914c6b2e648863f73dfe56acb2b62e1714a3e8f93c23786afb8f2a32bccb58d16d94a4a2664cd0ef2cfbfcb8b8599bd223b5c21381c1a3b34505c325a6036b9c88d2ba96c45de77a81097c21c9b16399189efbfa1b270b296a4300d977e758cd18d0b6c275ad40510dfb282f446f0cc71e4af9c5d015fecdffe40a82441f4b6470c4ab3230103719220fe7702fcd82c136b8f81da6cc00dac78da241ec49bedaa086a37dcb29b351da91970483316dd54bdf9dfe326f1b4797d2d43838e2b68b470c1c0c2005ed6b3897c91811f106baccb68a96b0579d1020e69d6dccb84c1a8198650a99ce62986e8c4abc4f2d1492aef204a0680615ed243ecbba4671178ad80a4d406803008485af70049dbd6678e31528504a1cc83ab4bf0c15f10fef0195cdc65b0796eda95f4467e613631f6dee8242f39c42936311c83e94456bd00c1cb3a40d0863517ca1c503b27af2ceb5817027e0643bd0eb3ce9ccde0026a2abcfa8883acad64ac2490e035305132899e74e8e0fd356cb973258ec6a9d20969beb71cb12bbf9a8b45e46336aa1dff05349f519cbb004abf6041071543dacdfb52a2bff221f791d2f7fac1ab4f6dc9e800eccb79e6ee870944e4770281d654f1d916bf8045bc022931c3fbea188388df68030a7a03600979dd122217479da23680a365955d2a5dcc319d5a6155e94d3a7081943e60f5657449c8858d9522d6563713ae5b53608ffafefe15cd800480d005d1729901651ba316fc648ae187353f3b1674c8d0a16cf9b3a9da150b19d3d4e132572f0248609b026657825a4723eff097dbdc25061315a6db496a69ee832d716361fb55d267e7d1388367599ca4c6094c941acf3259072ed99267bd4c453236a9e68923796cd8b500b96788e1ffb8022bdda9d4d9377b200d6e192ae20f2688fae41c3af9e60173d1e25ac918a4a9af39c00395fb5363514bc4cdee2b03346a50fde35addc046b924e447fb03a97bc685561a2ed2ceaae49a84a97bffe8b5fda8369406b85d0e1c571d144cfb9027622c0cf31180e3400686210ec013300e22514c1deff2d6164b1655d2dab6141bf45a918f8d70017c3b8b23ec00bb545b050212d509f191aaf38175a2c28cc0116f51329c1a9e088c05f38f13d6f116368dfdf0807b62df3feed77c724244c1e6c999db4b4ea321172fbbd1408a2243b6e2d8c1a858475cda2b3c406cddd460a2e54a357c2130ea821e30a5a3022623342fc831ec75928ce929c3751b06ce70f83790bde707eb7c563754afd56ed373276d68e6663dba01147e5e10cf683b28cd5569bddb4a3daa31ab65d713d927acef74a482d9ae19a59ce143c9a930b153e0c4e7dbc01444037cbedfa538e7dca60f5d8c87df4b32fbe45ef3a68daaa932fb3094740181dba5e0e4cd6ef4adb10d93914df91d1bf3e9b0e8b49e572483703300518454c8cd5dad9f806e801e08c7fcc10e8878844d245fd49c0e2550cb5b8378faf6961c7488e12bad123b82062f5d813690eb736af4b84507c2c80d5b67f9a77fe0664d2f954c1aba3e4072cf7c3ef13a6212c429763c627ff69aaae934218d970b3e6117f3495483eae37749a683299e2b4e5188c753d4bdcbd9963d6049e066e5e93ded8578fcbc7f2eaaf0f22ddc9a91bf156f28d5cc6f4eeac00475f6027399975289adc0b98aab67dcfb30ca701ea6a0ceb5058f350760cffec6bde40130993e76dd4913e78c291c8c30fc2ec58cd989623aba26bc9f244f3435e210ffbf27e1b92f8c115d9f20d3fb402d42118646663ddac2d2b9b3c0021ca81992110b849931cd21b465e3d70089cb6365be296c971f4f1164031155c83d7c2109d27905af911b8da4293e8e69bd423488c1c7596544ac083834f7e916a4006497bde534634b30d8bbd07e51f43e951f5e0519fcc2bf37aaa94b10afa1f720d954493847b4fa172e5a3f5f1865ec9eb6cdb06e97b05db15bae3598fcc32d0423e110f65addab6a4d0f395b08a99993fa190d17910c74ff9134d58c014c19132a9692d8531442f75014c9eb89a6a7a0ebd31442c45753cc07f6b6fe4a93d8f374e51a98200da6d872d90b8488f06aef8a830eb2095662ab72650309d3680bd5861772ba2d9e725b6b7fab249a0fa84eb266b06a359522560ac2ebb0628ee354222fa19f12caa6693c99b40d1a1fc05d8e2d0c77ca8c05e23f99f857dd188fbdc240dd781b2c8cd3d04507d2d7d248e33b9ac6d9babfc01d3f70b90ee80ee10242d4e630d5716fc6f07a8d50765d2f85856162b4bfcd1fc83a8a4b5a77b1e3586b6f24130707cc5ce3a7a87fdb4ba8acbeebf5dbe8deee4b923e61cf585f893ebc301270487b905ad10ea7bbbf5fd87d5b16ea15431e87a4e93c3a3b20af2037f95ce45f08e33df681def54b788c6bc15260d260785c77d5b69b3ad2807f9b99375542291605e89d474c1357e0be673f39d5570b9717f8d8a3b35306af6164831b477180066a12b0d2cb71670cd286ad90d36b9bfdfe158a15c06bd161bd6e0000e35116821c370b8b2ebb637a834a755fa17afd114d9a49cc03461a7bb7cb002505e73279fea9009762a3b8ae204e634da38c5d5dee4081503a6e0d3b49b6fddf4132d31d0c20ce5e3c1179504c978e63799a07a8906aa7a9a12c6fd6ee2a75978c3e72382ed4fecdd0819b90ae33553d57529e697185b64a6caef50c5683647750bf503b10ca03596739f70d907839ee08b9a15ad600e7262e0c504b6acbc39c88f58aa688f9f6769fc5e195c5dc5cdb87bb5448250dc60b2c62e85b455616e17c1843bf0906422cafecb07a619a94f9b5141d675302356c71eb269f4704df3a6331eb8f3f70c84800650b2fad6b4d5e60bc090894ec953ed8a218b332c5c7cb48322fb84c9a0139f58082cc20f6a48bfe131c9b00210631a6dba13920042bf9ec059729500107b57e870dd69b0d433489400225ff989809c8fd59cd9b7c86b5bf52f3b32eb09db75bb6033dad56dc4a23021163e93c7ccd9a35248297e30a1e18328b0dffe207504292bede9be61402620b19b0594b020c6b957e27d2c09955c088e680c0d098279183130f3ffdd73877875e11537af950ea9a26057ab68c5902b085fbe866a4e138fd324104657493684db582fdc1aba0ba3880c74ad068b298775b1744dccc6181fa8b79a655fa805b1816f6da7998d45cd90d9013115b379442b5f3735a226ac0aad869c2f939bcbd3170095299dfca48c0001ba7701c1ac425a2864c1e117bbaf875c67f7f219f73f11349cf13b378ae61709b0d692953df4c96662095d756377f40fcebf253d3fcae27cb1bbd6e1747b55edd1a6df258b0fc8ec253167c1e253b1405ade813c9ed1e00899636686f392fc846ec7099e06ac263ff85b470bbb6567342f97433e1cde75364f7c015d5615a784bf2a1ed07488f04142b175d0fa385b6598aa234222b937f9c34d3ebe3c4ee0161b68753cae160b826cd8c2f197bf351cce0cc4d4a6636a319c268b401faae9fcc4072616dedc5781a384e13ef0be9194f0e87547b03f442086019cac1ea0b0a8d5a54cb33190f89c2e55b52d888b63c9fdebbcb0e432a588283e5e9e608acec7d7f4163d0690a68e59984b4b754a34ff874d83538a58783bc207ca1a1604f485340becb6d74687ea42e257d527058639ca8aee0ba65f6615622bbbcfaf972e2eff43a8fa39903d0ea67b9154bf335efe794eca4018c4e3dee7bb5db42933ae25d09ac49452035f9bd54c4d353fd649be8fff9a93400f51804c575f5f25252741698ab395b188b6752af642c7a9f7e681671fc000d60f0069ebc4afc60ba29d4f3a24efd3470f57bfa86cbdc8b49e981711e2d22ecb43c8aa99fe193ea4755f942448ad454b5fa399694a37cf4f348aa7eae18db56a32f54af7fbc8874a6fb7f9e9154b1ad8ea7ca6b270c4af953096d2933d170a178897101ada94b1a756591285948bb3b8487024bdde633d6c187fd1dc406193a552d492d13f23f278d4b05aadf11221bd64209e893ab28b8e3af402ff269dd9512b4161be558332c184748e83a52268a95992f879ab022e6d333e461424166284c1a8b92fdabf481b58b4663366cdb65102f326d4beb7dd4bf38019682053a8a0f918d201b7b9289266346efc5e016a2a619004573a8d0ff5cb6064a950d07343c2d2736b54fa763f2f3ce6354f676a6dd17454a3035b38eacec878a707cbb0b14908670011018e187a53bb4d4bc026bf4890b98615759b7759224d292678af8535095815ffc12c283dd042871156d7b64577f92f02368c23d8a43d42f4b8b090bfe0cdc293342d2e115fb289c7ee2a0c30b4b8fbe2457000899acdc378a4b79da3576be4577621ea52df34bfaa204de2b665436078e9e2f915fd7cb256978aa92db6c2224c87b07422bf48828875569615e441efe04bc4de11eb896061109ce785a8ffccf68399ba75759defb9a9808f3cc8d40409076191ca2c651a5e65fa8cb79fa8a8279c26693c06107f879fc8af4b86bfc6345f6af51cc39cb1aa70b0d5b8fd49adbbc51cf7c910c46ca4cfe124e45edac74b2b8b9e63e89f2afdc1e0c191c0b7d5302a730dc980b83929088043e0b1e788ffd1d03ff5223085472f35349b4145b3c1bf17c6e804c93291d01ae264658b60f97f34eb9523927ef04dea89f84f7715575cbe9a1f265a06cfc3dad70b896f58e94abec7afda5650d300985d1c016455b5693ff14b712558062b809e0df8e138acb36a18b07617cc4038894a759a0ce8e6f5cbcb71335001c70f43153711587970a80aac9e49eab077d849851a83b183e972ac0c364813b9e7b24f638efbe3d23dd977194f765cdf42736bbfb08be74ca7ae4f1e34a9b38f48ecc4272146bc1fc6aa81c7120265ff833110005ba7ebebfe134cedcc60d2451b7e7d0271bbd2088a1d9b690c6026d1632413c850949d7281acfee900cf6e998fd8e39490706c9592cc0979af72077e3a1f4f600fde528cd1fd5ffbebe0ea140e412fd87a2677c814d470a6a1341730cc729f50738797c45dd3f8d767a303f6a3866da195254e06e4dd232ffad3b4f358e1cfdd55be45d25a58deeb07a6cce503a5cdc7dbb53370efdd2b2573a92f1965a675b695618636156f26531588c6e64e27c2acd069d4fcb93592634c2ddb191cf60c41dea703cd9b6402b2a53b91325f27e56f8ee20ea17d05982cd9056de50eea4a22d05b57ccbeb85cb4fbf4110db4753fff240165a5f358b4be85e745ecad1dbb427b4e8b844d3c418d13b44ddca06b1f199970aad9fdbf7ffdfa2d79b8c3a338ae406b1ef8e99320f7b1030a37339bb767bfb56b01e948f99261ceb42befacad6ec15513afeab79595442f3fe8ed56e7e30ccf288fa2dd9e653a3bc7617164a35f214870756b8eb2d3434a9fcb6abc8d0b85c5afd982169d1127a18d0df713ad2d99d955ec6cfee76757699b398bc25260431d8f3cb300c136e3ea43dfa594b8c6bf43641631e04399cb69a0a41bc01d18338ac4d1cdd73179b2b022c7d4d2471b558a6098490ab8653d96faa4a1e875e1f4f42beb4da2b6fe0e8c5da7f22a1c86095583642e3948641a6aa86cb24b846cad4ddbdd8135e309168b87b21e73509263265d972ed340d5886523ff8f39937a4d2ae31950f5bb4fe9667ab245195f482c01a8885f367edc8f78a8c6a08a4e62549ef01f3357812a0e762cf1a9b2f80104bd9e6e25c3ca6b0640db81eb2663be210b0b5e3718f490be03d92a1f19ebed7923aecf2c776dcc1685167e94089f9a228bb100fd94367a977be51c5f3d0062880005ecc90291343a6975f480c34717a1e2d1da9793904a04611b2f45c25bf2fbedbcc41d9c37c750d39c134121dbe7b77f6a4827489656d87722e98ddea20616104cf1f540bc19f410ea079e327acf66ec4a1accfde08337fd3a200e3d6bd29d275ac8a745d0a16e9aa5f3a1963c3051298684fd60295247f58a39feb8e714656ed05ed08428a95ddd3ea74af465864384fc4e3ac78e62c63a1b4162f177c9270efd73e17aa7a50cde1ea25f5372fca73e1f2c0dcf371363ece88c719f1382b1e70c6294e900580470f7ad82309ca6d811946e8b85d3b1d1c03cc8a8a40af0507be88085660731292c3f51dcc3dafbb1d51a97bb847dde2faca3e3c63da96123da99dbadb32a7620d9ee9fec3e46c4aa10c15a8806f56972d62cd2f91920fb2ace20ea27f51245436a370fb72f2635819611a7779d73dbf4baf5ed68869e55222bb47b744ac6c4d9e34002c2320a6a2b88852a8474c969b8112678b0edff073ca65828784295093e72e44ebeedfd2f082e9d2e4557c6ec938815ee960d725fc6352a45f53eec3c6d57f55a4c12eaf52af4e23d18e7ec5159e5a36a2acb6cf1a507e848ee69d55959641390ceaa80ceef697546f230ee535f9e1860ad93b45335dac3495ff3f9ca8934c8542876bbb5122486ce73d0da679d1ef3b68e72f4d71c3fd4ed0d1304507a684da1b1087a62884508066994b4c8fe2948c139d69487c26a466b984b4fe2574da7041b6ca79cd3a21b5c8c75253f5963fc70c0d9c395ad3f8096105387487788964a64b0663ba1c2c18b4e6972e4fe5edb27d0086bfe2d5e9b934cb1ff7b45112750bb54647ad53fdb3ecc7174f73f90200982e72a568c82c06ad80d881f6f249b9739e1bc8797af28200c4445d82052d01753f7767a2fe5373e410e6203ef14809c614f70861b05d65a98159052eb74153c825d2eb029c40c9bfc404dece1c34768e4f7c5c60ea22ca170fccfc3c8aacd0bff1344fd98bbeec3e65e2fb59dd4304cb446e6d448a9c9b77622e03b7de3375fe0cd519cc081e5277afb36e86884a2315b10b33cff648d9ec85b1fba6f4a23b0cea80ff3322ddb08d71c0b50676f80ec6eb0c923e3b30db30e439ea261ddcf824f54c5101c09af326d638154d701d00a60527efffdeb2756ad0529866141707c0833b1c791f24ee93f9c0e7242a88f920bd18a29ff1cf582c10d7639d37fd2de4563e40bbb39bfd1a4483eca52ddaea71cd45be33b6319d362c6d99daa62807da434b84d1a2847519edb899903c4a102ba31a01a980671b8059897e6e81b01c5b46252b678d8edba0d376d1bfb0fb5b6cf3c362673a2d563a26f251da757cd04f2f04664b5b2d61866390d12bdba0a5e596dd1a8974e7729c326f17008bead2ea1a1d5b4db0c8a294c36c0e2309749ede1c53b0cd9133ff0cca8a3b73a5f280706cb648064477d51b467eb7f9f467af5914680c60aadd5dd4cbeeb1db51c5831b63518e9a848f07de18758624e0c43197fd084c1c3ddaaae38aa98a3fd7c6ce60e8300b24761d2d8216c54e2580c2f0ce4044a7d171e22127f96237f695d0086ec2382972bbb616bb42577f5c41cb643ccaef74b00dd0cee38062f0b1957c92f7b2db8193a3d7b94c028e431ee827ef98a464128ea3f7d4bbcfae00de165b70ea1ebfa9a55c6840ca2da80e7e5b352735e0e40ee580d9a8cc985ef238fc217557f059154504abe78fc82c00d811035aecacc1d0ea9f92ffb9f2aeb997a19473d3105705f7bb87f6c9885aa5c1b22c0b9745c91c9f4911f50189061c7143a8c84e355eef3515f3df1e431bac05ee15e022e6d50e29c6f4ffdf425221250f9a0f7f6f963f376c40b00e13616df96872bacf5702784193eff17444d76bc8b4cc08ee7fe7e3fabc5fcad10d5f83c21d7d6bffe707b9e68ff0441f0ede3782b5d6810ad3ae349730f514e6f2cc18f10be2f9d06d2ffec9c4680c1f4c743cecc94651b510adacbd13bea816c0fca4f4c38daead362af2a363bb5932f6d4309cf71e12b3825a793baf57057b9bb771dc4e82a3509d6efce576ef71821f5ef6bf8797f7e201621ce62f9f5b49d935c24c2fc6bf09deb6198a945069aa7198e530c7f87f08496a3be6a83205e9f1107ff44fd343e44bf0b726e9c2c70f1dabc20acbdfe5f5644f575425af2b0b79cb22049db900f326ed256e6ff093a37c0bd8f8e7f7cff9d6a4ae0748d65a341c8426ffff8d019d02e762b9c4299c352bd25a3ad578c7a809c66d076fbb045cf8fae91a775268f298eff73d1d73e911954e8ec67418314eb1156c1213a5b0ae061e369d643bb19791952d74b13dcc7e4e2f2d2a0f1e636ea02151bb88490ac16982291a0fc007f084200d5b0b36b9af75c2e1b9c86877ae0c9fd0ce9614a0f3e3de343e42f935aebf0522e84533374eddd8c61ad888ac20ec26dfc4dccdc7e91cd2471a67a8f932eb1528a71c819e1eec3c588afcc0197d887b950f75bf08d16743d99e062b505d0f88d6ed2ad1fb9aee5374d4b9944a8d2cc453c04d71ac311c1b936f3c1b8c4213b9734bb5a23b8d2ec7ee7f9f752498188c3596d8a33b74ecd6b1933fb8094c8ae6323170695eea9b8490db1c080f7600828103415b8234fddcf22a85b160b9dee4b90978a314e095f799276cb7125ceee508afeda9d81a7d38c288effde3ff9e17f86d1c68f09cb88afd37eb122165d11eaa6c61d386d3b077c2f78eba48c0e3969944082b3f52991054b2f8af9c855e9e306c46658f7ac16e723e6bfe0fd7eb6954b386fc68170106ee41c49332012bb7615fde84938dcf0af4d3e98a51e01a3631a0a5076c685bf78b760379ad918c92fdf80a27a30d9eecc8091b8de62f113680df5b3a085402e51818bc7d9146c04d3e790d0a5bed42e36581be69276ef9e3c5e6b3e0b2376b031d1b6441f8c58ceb99e4a4cdd5bd387fd04a6f112cff9daad0a5a06e067f598f1cf0c9d105f64e87b3508c65ad2d2bdfaa18b28a375cc06dbb7d2e844dacd7264f738933ffcc4f8ed13613e0b957087f02a0ef1a656be427264602302917474d4f117f355d64c6a1b74570c58475acb3e98450af420474e6dc6da38607b974187945b6576a1d96712a0586b3284d4b31b338ad4ba1b9411b97d17c489e7a8161a1cde68e0b0bcc5cb94f2a79f74fdc92dc9c8e97d04699b80dd05a507114ea26954975e3a835efcf8539ea36058451959266f165f9db4754601055dbd21972ab616248e020644be60a09c71d2dc321f3c4abdc807ea86b544c3566ee639d16173c00e055495a6b2dc415c14dc5b255d8f41583c14a4e30748443782e54ba13cbcfc150e8cded42c5c82d668d180b9256e71e79db5aba5170e9d810054cec8dba55ab35cab631387257f996964d7cc356f411bad5dd37b0e8651db9133802815a18b5d9e14f23be35d8b7ad594991d8ecbe9f76fb4294bd582f340feff94b19c1d34f90b3abf92807d86aa80b32e8293340c4c69a611a196c7562de66cacae3b759725b3fcade8cfa40a88de0cc7636aa69de7a7584bc3e85c81b022668317b9d4d5055a8698caf43b3548725effb2504dd9b454b4fe80f91c5ebf4129c9949f6ea9242bdb4c303a77b22fd38ec66885f77285b8ba60bd09eb91a2571118f476228ee2a2dd9618ac0b00f96d2dd1024a0d259a8c4430c5968f193d705a7e50af490e7484e6db187c09b61bc8d8869f33ca6e9933babe4c720db90b7dadf27fec5095061c0705b5ae4b00c4b1751e8dbcdc1f01070ab7902ae818e5046c5bc8a1736210380904401bc0fba82b46f9845635ec726cdd5068f02f5449af023d95c73c57c82737758363490e042e89431bc4e6270865454582dbdd4d78968b87cedfaedf4d5d2bfcfac1026f98a976e8b447f499b51f7cd7319b30eb326c5c355c3925da23e6490c9bd642ba9bfb4b60d965edd75398d2de1c16cc5e6b7812cb615c208b1a2c440b83e128833074b7528d063a1207edb6f77e7a9312710e1c7d73dc7e5875878d940072b8c59ccdca88f04cb14bc7048250c4991d519a7f56223fca912e6bc678e9c34d60c9681adbb5ec65d469725c3842331ce1fe32e57678271f93da8c56648184e39b068d30fa0d391c0b980b92aaf00d249364493c7998381ca6936c25c2b00320422cc01e210caf9801414601a4f338bfec0a2101e78080ca64299bf3c41e9de314d1a7811136b21a87e4f56d9f0c48ab43bb391a1696dc6fc3295912fc792bb0c09b033f0dea524e1415a99171389a10dbe67d44097bb85f7533c2e068b5abf44aaf5797647982d027f8c2c5ab8f03b07843177bd5c6854b94845ba6bd3494ec7c98df1735a5ad52d1199b278eef4daffef8f0781bc562c784f5f380898b801c2fc1b5379319adaa4eb7d72ffd6ccf77ef817a27d00821f0fc96ed008c52a06f685aee090bb0f2259a9d4982ac08a0d44d11568fc8aadc642e222388be7704490f56bdf05ee305c9f606fa1cf8fb87407f6d7b69d9e8db52ba449923eed30836406f1c31b68921f88aa957c6801de9afd4fa4167b79669a816e9d80792905d2031ab6edf949227f6152fea4eb42b0d6b1babaf8854f58215e246e592fbc6318a981662bb58b8bde1fcaf2baed7f66fbc9b0a2b1bfd86d3e01d90e1fd95f211dfe4c210b335279e6007ab61b2b7309a69a5f3f744da9ad2252cf58dec697e89014dc25e48ea128700089c1470048ce087f801877bf831e3a1418c3e7d119cadf0c9f63bbe4ab789962ce55ad91f9bd5a409a8f7f2a952a64c77b3fae6bc0cc0f77530e92a433ea12a8162d668aba0044675c4c03e792867770482f8cdeab00a4d58bbb1541b1e52ecb42dc715aa5fb148115dd77c3de9056de4d66394f5f41e69fada73d3905b72a810d79081135532e3f932e9b811a643fe54d9cafb237c46b348733c19332b6ac08d9e1951f8ad916c1cef237b3491fd9947449abaa5d15d7a1c559f6f3695b276d929d107b37584887a30b84e095c4edc9086c36a591dec03fd186377bdcc6f29575d1f4bb27c0819fa20722f43d22eac0f6ea18d7b1fb60deee0e75325fffc2e0f4412d2b06ca7f6e3cd185c500eabf479707e3e5824b19d99374feaeeede0d5925123d3c08f6f1ddb4e37940b85316c9c81557e9187eff6ad4fc2fe555b2174ce9f496ac8b16c9bae94b6fc917232132d16decf7745c4e1bf6991d590dea1d3e93aca58a9f8c95b7beca69f60970d3d11857ae510d938b6808beab49ca32f4ce40dafd562b66eb3b0c6fd72adeb524a0713b2bae42ca68a219941e30bc13819e167987e1218779f7c5eedfc747922100584ec2a1d05675e1610d29c1d4b8ebc736b5140adcee869c8c98f84baa7db2042aa046df97b6349851ecd61b5abc9210b344c650aa1ee2eb3bb8bf478cff69da77c804181463df5879d3ef1272e6a3f3a003190abce938226f822ec7de7f63b633051c7aa8c99552f4625856864bc3e846638c43762ed9f84ce4be10c167995e20943ff379b15c9ce162fffd009025370e36b6c7c68568b40ccf45122370c5388393a1f0fdfded462d3e1c8cc414140b72a901794236490fba880e0c937a1c6e77f1fb632609baacc9a87b8900981ab98eca16a10e5e2f5d6d078281bbb3f5047f5bc564490421253584567720e54efeada06df030fdff822d9fc14a3d5d81ba04a3df9bd36b37e96d7798e8b2343cde3073633c9cd431078ac26e018a7ca797c75441f247ba18d497088705e6d4696276b50bad25438a3ced0f9a7a4910ab12d5be7c1b6abb9d2c39709cd5c41c542c26da50a1505ba994b8ba01ba21f1ee64e8bc41b54d4817e015d6ae5a76ce902ecffb98211cb63b3b1011a11039fbb82b2c03bfbaa51dd8725caa818bd9d4d9cb6b4d19198785c3aedaa98d7780efa8e404424e44297f549e72d96f7839832e1a4402dc90a00d6457354781edb3aaa0517bcd0e0310c53d510ddc5816c30fa9f581a3ac9ede0a1403e2e693f4a7e08d7ca899449311d87821227598276804f2736788c03631af5bb0629ac1a070e4006109199419cf329da2a40cba7a7483051719bf1237e2ecc6afc5e20e73b55a7328897209ede00cbc79027e15abadda2269b805990276959cdf453fc230606db00ee5c9451d21ba83b2671243b8d2788a9c1238e495a9f6873634618b3c36796b86f8dd052faed41c10a2d064280b76051b7fcebd896fa072b4966eb9a1e28306f027c1627c861b906f5caec3d7dd2c93f5a44836fa164ab0d1b75d026e039af332e36635e61e35c5e8edc061e5a9a93e7d13abcf3fa5eaf330e4d31695b77c8ad2e225578d282fc5e07411d09f014d81c50f521a81842040e02665190699aedcb37d29b0a21fa8707325e6100f65b56ab72cea59b59ed1539bbb00c3696c9d48639f58228ed50f94241b19f3967396810e5f6177b4ba1d5fc8f7d2599dfea3c6a8fc265521e16dda40b1db3c1a0c827ed2e21481139ed459fa3c989096824f083cacf31b83bbfb3aeebad1e7a24334a790c79066dc62cb4e1db3cc134d4dc6f46a768b28a283de61ee7b6d162211e3a64fbab8e4b600de123525320a66a0f2fc5ff100397134d5a5a02b256156721d7ab98c33ec3b9331611ed3379c37c10fc9eec428e541746088806dc9df1b03d751d5c75756b3e7f6289a76f2f3332ac57c1a8ea36090db58845635516e1daad2ea545e30800878522303dd880294379e5d3797d49a313371aa1615794e906f6b05219e3f3a07261450355e13d3f22c64af29ce12bd86ddc25b17068cf83a7956ea538a80e0e162c2e6b0593750fec4e9d22ef02f33c10e009240a5d2820fd09dd743773208592f9c475252e036bf7d0965864b0b111553270cb8d9f13e4d9a641d8d37219a3b8f3a9ce623a8a02b80a0985e26f76426d58b022ff22c32b305d59702f91f96efebe304af43a093d26202a5912c1f42d94c6d25dc7d8e856a4eef965024de64aab6267059dfe186194c442e88bbc1383d53de3211a2eaf6450278db920b91de588090356364d96b2143eab48bbf0c78348f22d164b252222a4f113e17110ebb5c58c688cf0942a8488fc31db04aa23d28a002351beb0e2bdb56c34af427e6bbd725182ef8acd52262efe0caf0951a3185495858a3b066313e33341778dc5fddde8ff4e2c3f7b4aa34f64fb1d396d45ca34f5b8f2116e6e8528e0ccdceda7cad9c04a2194f22cf0e6ed1c310a69b45a73001c0cbced440a9c84fa524bfa18f58a24562128bf1280e5348724396662268ce896d383c854df9cee982bf7598cd3efb77dba68b4bfb9a7a1f866d8907ed07c9315d82c3719699fdee81d45c4bd1710e5a7f0fdbde2380772a910f3db141d45088d83b4797deae9eda20354e21b6100af7844efb79a857333d82a21b0a1bed48a2007438cd23685d56e06a193c4a3144cb431769002f6c2b63a31f3ba7d090ebedd6d73a55f2f2c7ff66fe71ca29b83ff4cba94641334c482b5cc894f92d9e693183c214c62177ab34517f5663744d4a240df3f103387ec837fe8f1ef261094a828333e66c7a51ca546034b4d17925b14b5da856509788cc79cd8891a600a6cba624d0ea7908a481a14063cce6f11ebc17d32d40514154d8a2a29c08bf191eb92c352bae5a529220cfc7accabdbdeb27fa06477d16acaf1b91a268c69a2c0e6c5036da692f81378c97a69115dd3c4783f3183532e17d9aacb52abd428a9bb60113b194169086f79863b08d160eeeda111c2acb9f87e3d27543c381e5157aa559aeb4913d0c4339246baf78413c8a4872c39fab1c206c34aaa5e5580b02066f53929cc0238592f606ffb41a1202ab931758b66e01f6ce1aeed113cf4ef8550f6b89a43e63b20b9ac5737dc4130ad9ce151b6bfcd64e4a609321f6b957ecabe76473cbab4e55924960df114224d43c1f473ac5e44e575f7aff175ae406ba754f3c1a7ef96716c8aeabde4fd22ef6b96997c25ff55ee3761c2bd2591eceab373fc2528f45a9de811b3074eef3ed3f51846e7c31bd81e47e509565b997a4371f9531703ca7d322a48190be678553c234ca8360bccc595ad7199d1456e1ecc0bad4f2b9b490debb5ce55575d9c0632ed608f272ef4cef72f26de3b2ea19abfbdc9463df2afe939bdb8450de0b4a36bb8e8c88cd33acdca0dd1f3514298c48d5185b222623f13d04a2232194817efffa3ba0d9ea346c2b373cf8a6d8f78f2ce25f47daeec655bf783d7bece00b57d25bad6381a7112296e66bbcea485a18d1b0fd80efb4b3772796c449fb020e433cdea93d61aef3e97e85bf2134338380e6280ee8ed2a17eef154d8f8815d9265722f23064121143466a06784d18041a55836cbcb3ff2945d12c85098f557de54f751edb7c156bd882605c4f1d696920f684593071370c925492d95eb9e12be8c04aef57d33804b73f3295a12bb94e6494d4a9c6ac471db0b0d2361eebe1b00c52c73799a3cc094b731ec7c1604bf891a378f64fbba521b00f392005adec691765ea78f44cb2db10ac6ec67fa056ddb8d9c75960f06b3ea69405f61fe4a59ff0f9974621922b048d0f89a4d3b0f8a33bbbe82a9445968be6513680edf4a3eaf6c2e005fd5ed6f558c22abbfe038e5fb50edd2d0b601af926c925ba564b730c7c184319c0e30aa190e832dd74f720491d33cf6c3395fd4335208b56e4113db4e5e0575c14c9ea749ad7e19931da27f558f0ef108eed232e12fbdbe292ab0d83bde5e12c269c00acaacbade83afbf7a84704bfa9ad8d4ca51ea29e78f660de22a15d061100cdc8abaef8fe41e9c6d1c74b8b748d72200c724d3693c9d4e1d88394adc9d0a14e42c9c37d859f5efcab5c5ce8f0f5bdd733c470dce15f7bb226045c04a3d3bc9584ba3262595cdc603ba4ac3d952fade45bdd8c43c97238514cca28849da073373620d74f8a6fdbe8a22e944e08555c5421b6c789d094d41811eb84447ebd170d6b5388afefc18cdc6ba0bc54d28e17473e5a5fd583a04cb1b265f8984d50288e064ad69c0e1f05e8b7bc95949696b942e861d80b3ddc8e2b28b825a4d6fedf1d2cbdef27656a0189447ffb657fb82cc606290645aabb7d19764195cfc99b611e616c72f8b0172390e0772957c50f3939ddb9a523f630c501a435a2409e52616dd6b8fdae06d41bd433f3e3359b3240154f3a970d46040599ba8c5ba1ed4fef2347855a3eb570c8b8ee1acc047ebc179586213e04ea4c1895acee6c6402464d0d90b1dc9138608f4ce37d7bad73ad7ae94c49267e2c06bb05f596d683cb1ce91fbf976bcd1355270262ea5ca7debbbf20be235fd371035d49a2007889aede2b47e62af2ce44bb814e33f1992ea82325ff317aee521fa9c38ca3315c76f7bad4f46d5676f670dc25ee109151747191af6b4ed62a25daaae7c089d425066e4a917d73bdb03751367c57675b2df46cb3da11fbf68f311d16cbf87790c0859b1b946d8036c18b40806f5d3264f6e510b57b362bea0c12051c881dc5ce32eed73410c4afb8281d470c0be33de351cddaf3b9d8a85064c11d40fa60fdc63741b4cdec1c8cc11590e341a9e571efd4f889900baed207deceb9f50415c7bb6927dca46f450adbca0bfb5dd532288059dec1ba606efee9341b5ed0009d9a4c69fd68d33ce35f42a10a97c28485290b34444341dfbcb6c784643ebefb2748118245eeb45d6707cb467528008e83c9aa5e4db5beb8e3dcdc4ef780c3ff62b7e93dc2fd7b5dd74e98b527ceca58a99e4a45b41ea7f04346b23367d846d870b6495a4b0e92900497a598a83425d42c1c33e0e14be3aaf88312ef44e04040c59e8319464a357cab4f4c4cffd134c993bd50cbebac1de7f79325380ed86314002806afe8779ffd33f4d079a956e3c2ef5a1b2eaf6c9254b1732712d7c8f706738b3c3e8974f85d1f32f6295d7c137d5bef853f8866b8d91e457b4005e2340ef25b0b5ddd91f7745c1a5099f105746b69e44edebab5edf17151a59720be6c693e1a2e11def734798e8993602ba79c8ba221187bc27737bbf7e691ddd28192a4536a56e50f8c231e780f712f48917e3cc11b1b8e55ea8bc34292637a860b8d7810463212f58eeb6cfe443f3f3a508905403bd606898db3c3b070ab9bd040ce5a47421a5692646068c7880ed561a294c3f7b258e79054d9d49b3350b7995d90a9188853c4dfe04e81dafe66d40f5c8bc9f52dabb604ed7cbe0855d9ba0d539d9acd0bffbe195623e2bd416e2892ee875eab1f7853d133037f28661d77c883ba2611ded25fcf7e712e4bd8db651a37c87b8cd45059b12a4339c8d355dec71ef2bf742f7e4a13e658aeff3da3e1548063e13eba6b2294c44188bd6e7bcf79556f31b2b81b94083e271cf587965b8f51d3a48b6609e1da4a892d2826e4987c3c0300305f5223dcc1b5b1723669e9c82711060a8c5405ce6f7e3a89e78feeb1bc4ed18b79aaf2ca77fb9c203e9185ddd7e4c1fe127789e28704778f4c004e6e5ce514d6953ec041ce200acc7f1f17d489458566942d6b89102ee86a90365c82a1d811f84384425be5e0582da39a4ced0769ae373a4ac2f78d84b06f9f4e074eaa8bb94ad62f24008e78a0ca9962588c514082c448bcbd3d0cfd5b72c00fc47e31d1032636c41144b74d59b03db504c1aec308b66034b20d05512a4873e0478486ab32638327969b8c3d833ba50105f4ab11fb5dd2df626d6a6db40cb0b13f0aef5496918471e9ae689243a64162c394afb8832321b80ee87dee275cae845f2839b48c127d7acc0ba4787c528d0956b9bf51bf0cc445ee20fdb589b47c596380f9bfe46b1d9b09bd2f17c3f25ebb0fda6a1801a74d7dc2c70057d4bb2af7afe1e19521e9a296fd95cfd663d0cd06f5bd314d4a77d66259387ef1daac75f0aa7fcffc5c629638bb6ac1f814d113be66e88e4c18fd4005eee6f99c43a861b492799a7ff7ba6e0c800f334042e5db97b40afd1f2176291c7560a1368321961e32b01bd623a06d1af6ec2ddc45845abd6750d62e9fdb3d56390930760352eeb78d7b337781339026fe732aad8c40c9c6150b1e923e1c2afc3184cd76fe88fd1fdaa3b83d49dbe491948f8bfd99c8cbe0ff5103ed4cfc71feaa978adee5f688b9881b930da5ae55fd122efe49d6c675247ba97b9ded5ea3363cd5fd582203969decfafb5582def59bd5bbdb26bb759cbaa8d597fd95434881aedf42668c0e3d5303bfa5ad4c86c37763a40c804a85b6c06537376c2b89b0139593829fe0de6333d73829e44d136456c3e46101a4a7892deeb94e9d12fa4a11371081ae8639aa0ce73256e309c7ddc8510591bbe5c67e46b674d45c91bcefc7d6f487785a7bd1e9e5f91d34c189bd913b16c5c3367e8997082b2dc34079f09db7dca7f2e1a1e7c026eecfe1290afa5f76a6d724f7ec0dbcdddee021960ec92f0f1c28ec386a22bd40cb9f0f794f2fce3b2904bcf82a5085c0fd2dce8af68c4517dfff85e183f0a836282ac6d2226fd5d9f65e9a401c478b182d307e07361072463bf030e207e9d66ea40ab3791e94a028daa33a64c412c8a30b395d39dafd475816a36f6d678c181a2fa677682d4e2c4b7e108c119d914408b1b8182d25adecc5f58a3a95377bee2661fad490941694d97a1db37635ad5db4de2ff8cf146a541672b895ea9a55c3db24e05035300f0f1141844d095e0330e3a6daaa312d86efc6b43a73b2d79c4a082195052325f075081e4194e069fbf90c02423fb3f872256dcacc68e85293e1e2929f934d815afa56be060961c485c7805c737140e6a3b0ce7d5b7befa3d91674a237dc08879f508ef175b4691d0531a8e4b41bdaea9f7cc4233b333759ac2bf8f7b5d555e4091214e21cc92a0e48edff4271eb42683d656773f4c55eda7a4a15851865493a1ff0ec1c8b41c08615661ca31410ebcf7fdd006ac270bcbcf8540b6a0dc0755976efe0539dee42cf3618411c1925dcfa4e1bef459d41d312b20ae6acc1ca4241a2e87045e0e3f086aed35c1314267d6c1e917b1e63700f41e3257861a5e42ae479cff385b55fe777a99b12e38ad870976a5b3160e9ca908b15d0a27f4ae20517d3860b5d6693ac25bca5131375c005fdefac5fed26684181a36e26f582a56021b52bb78b02619ddc48bb337af8840496380b25655de64b20d811d04ef510128ebecb4aa6d83f6cfecb9d7fa7cc720cc41f516ea2388ecd79000e00bdccd015964b5df11c5cf7c23a07953a3e8f8673e62190c392342b0c86ee780e2ebb7fcc28d3876d9976207bf91de0f86c05315899597b98adb9fd49eb25d1df212bce5b9e6ae6ec82e948979eb039982bfa19231fa10b67ffa352eee4449eebc30da6f42aa11c883dd37768978d3956ee0d8d07e8c9df8977446d626fffc03de8a338e8af76a75807dd769c5d51328a3df6a853ef6419315cc8871534143332b84de8a660890a30323c65f1ff6d2503804d3eb7c01ee42d23e3cc13f50c0bc4f4d40e6220913c57075ab0389b743f60131a5682c592407cd915bd51bc2371352bf466b2518df26a783da2dbe3c27437b1d564482b3306b4464b868de0a3608b0bf401ac35536160743a185e08f4445317f817529696d95e131c2055cdaa7b6141b6bc77ebd2fcfdf74c2981e4c1cb22762ff7a5b6ff82472d9664e38cb011dc5ab9fe64b6da096396796f201a357311fec13f3831f8903ca21abefa8519910ab7d95bd7402811faf12f93650e849eb331e73d24098bbd26f6523f46a7a1dde41d72bf8d70dbd717ce6af0dd22b0b5d6d32b2e9c314684db62504a315cc02e2901693b44ffc62352939ae804e1721d84efa0ea4781713c82f7b17e7c095e79e8d40541ed8698d4af4271bbeb9e976294b74930d93603e1644ac158709a31fd9c57577d500c8931f8702e90016f55f03cb658a1e6165e4c6fd56f7701e487eb6db89aeabe32369119cd417d20f595f11eb519bb8787df5e17f9ac35d87f1971c7e5ba7978a3c44ab7fe6fc30a5d76c3f848906b7cebcb81628b60d61b0cd8daa7dd62e6e9d359a6d8110ff2c3f302a802c7b93448390192f948f7fb708c15e7f0d2afa6190438b2a40250e51169f9bd64c27faf2097325384192039e39977eb6d52f321273b4a0c04a2d5c0de20b295c5266c75767a76f9e16901319b9aca99fec6ebf7635e06f53f4d46d4eed6e9a2d783deb29c7bec98f8a7d9e77a08bc4fcd54c9f21fde92f3830d69a1c59a0159dc4b6a874341067f2f46b8423f26d23ca18fa209031435ffe5d64352c03f4b1acbc059da22eafd525ee5a1628f56da99a9c83a81aa22f84a865592961c9c38691fb9f112dbd9b354fbd478863010ee74c035db5de0b55800503a99f4d8ac8648f80c03196475537779c7314cf0cc806f40c61e877c6497f9f493b46ca7379ec55c99e89636a3e7265260b0b77395ee04ccdef5d8768b995d84bf492d84bf492d84bf492d848f492d848acb779a210f64dac53c83b956795eb2627c5f7b926e81b750a0aa59f687f424cf5317cd09c1c15dfc7bf6302acb7e7f9400736c78d75d0dc875c2d28b65deff7e1cf7e2e60b8716e719073e7ee6307bad9fe0235c9a58e4f45eff935a07463f98bfa15929ffa97c50c90867f9841307927e911e3941a5ef4af36b34ce06b3a429932202652cfadfef370832ca7b5d62255b25051032dc8c07523664c000a3a43cad7906a61e4dab3047018146a3fddd07f3ade063e94b1654518eb7fdf8f758435a5610cb29a4df61973077d3eb2801a19fc35042d1666565239f18c4e195d73680ff018974ba8ace4fa395d134c7f9cb747b7544773f60213b3f7e3d152c470986840a5eb6dec37e60a244d4014729984c2491abdaf4e509c0a590112d3a69bca963464f1a9efb5eb393fc237396c71f996b569f36b4e5a485a8047809d95a8d93355dcd9b43f7b4ccf6f342d1759d448c7a839566aafc9849f5c8b80abb99271511686e068713fdc213b424d4c5680bdc0089ebd3d298e4933b2d6b92ed7bf6663e3078291c2accf64700a7f3f1e3d2bdc4ab023601694158ef7eb5b63f785388b7b3f542edf819e8a50f9e0ef555c02fc0cab3d01ab4ed0e70eab052935a9b60be4040b0435595a6a9697b99b0057c2e5e0c11da07a856fbe234b2b6c11485f2395440b28a3690caa76309938c9c2eb033915d7bfeca99018705617a87986fd526d4da02157d6f54b140ed9332822206fc6832e5cfa4d0bb2548d311da595a2fb124b1e80ff13a037679d910cb3270a038205d4634f9ec8878601dd081c248a74aa2df7a33145ab280480040b653fa4f48814541487848985eed16aca7b1c61c2f708e0490b51db5f09604f444660d999fa6bf2c0845eeb017cde83eb3663bec8d40debfab26b52f5fb6ca73876b22b8dc5bdbe33bf6507c6d24d1c232150b65da69766666d5b74d38778e6db8ecf34e7b7a492512c0d4fb27af182a31d30ef65419c7108da66bd42b7ee0b000cb4e2b234e4ba9e66d1be861c72e80cd9719153652b6f5614c7186603b3d61a65b6929d94ac62b036d003824d0fed1274e4e474d8719a01859d574f4887c8f7600270564ca427d348ac5bfdbf1b937fa1a7216e6f16e1dd841db708c9c58f2d98ef77955ca41bf2eb09af6a7ae0a718730b9dfd9bc2d03937820d7b1f6ad20fe21f008e5ed9956246aca42d78383ee067cbdd8a6070284a7d59fb9b22add40f231b61fa9152dc194d6c2ba679034744b7abcce04da180a1caf806c049fa0c91dea5290e9b2ac0254bd321835f9b9c1db77302359df4d07274b9528f49527c56ec42e9d8474a7d20be53d0cae02c7b3287a159043e4554287201bdf5439ba0252ed0cdc7fe3ecc965a6d88825b452887529315cd1cbc9e246d752b655e8877d4b90e2a1f4450f8859d03852e10d8a5a34ac55bf4b365b155af279610488665df3f8486c9d79a74cefcd7dd45105cb045dcb7f5b06e3caeb06761610a548849da953c6ff47a52001234d6c04c170a1b5ff962b06713aeb68c26d0cf0c275e3d803d35e495d2b48c34d5b802cebc3859686480e824ef4398f490998843312f96ccd858078cae8e3e859acc013e3941172fe615761c4fb26b32a34dc78da5f13d2146f5a4e073e588f7732e21f0820aa05358816a82b100fb489182b3bb450e0b2390eb8c2fe9cccf8992b1fd89a5098ad5961f11f16b5298f81bac6e0bf2719dedac559f0ff9668b225b521a58f40a934c853912bff562671b9cb70d6d4fd0bf1077616662cea21760ede8168b2019ab77b1b6d123cdcb4217db9a0193b7a08e84a190c572423217976515e015eecc9fa2bc358d9e3954480aab0a750c9749c51374cd042d66a6c6360b9dd698204781a820293b80bd95306441669429c4c976333b581b7c8a00d836cf54d1299fe225c6cc4a5c349ef2d4196aac44f2a319f2cd0494b6b025596c53e28ba0de365dd7448a5efd290b7045d8007e4f865ace65edd791235f68372fe5f203988cba25671109b9d5fbcfcab853d420b80a692e176856d63ed69b596c2aa0ded7f60c60cc6fb5e2ed2f8a5ee8962f8014004640555a2710f7d2318c35f392590bea7972065e0674371ac81822a95e7d224caf90cf80712ccffcceb0b5b7206833cb452b2c118d533e5ee797cd8e1d1440c62b133e55df344c1e24daadce881d3735934f35c0a99a050aa5199c05b991cf2c5ac37511848eb804d085f4623ac256dbe3c977d5391404d61d99ad07f41c31e3fbbac1452e4da2e5904971e5bc590754af7e8797d2263749d4245c803660a9725bf201a657e184e0325e24d787af6d933ba16e917174bc1312b07df86d5acf037d3f4e918169696f958f6ca0abb49dad5c2b95a628e3fbcf99235b4b06470ed890fe8b9be5051214775580b0cee6a5757ed9169a9aa72ab51c6885355a267df8d682bec822a9cad1eca6405e313590469d4fb0efdcd710c2e2bd6e13906c7b4c60b019f9191175d6e0c41be7e509b45db0c325bcfa02b38405b8c6659df26e7ceeef533a6c20f56a0dd3035545c4a754b371072ba1124f3030dbd5e169748dd6e5e6afb846edd73c315108a3575d3b79235ccc0dc32fd4527a39278d240b653eca55e6e4a281a7bddec8fbb0a591b595b5300c097d8b73b9b8781ba602f79dd182eacded257f46f3c526c85c071ff82908d6b886b7fa33d5f5b558c8d46971753df8bd67204cf6069adee61045cd2dcb37a0518b2abfbc26e0c2e6d1c7fa7bd27d1a21c4e1bc0aa9d887d7cf5b0ad93b07da229baab0892b879b5ceacbec0f1509480a68193edf79e67410d675bd5321b0e2d50896ad342246ccf37946dabb08398dfa6ff3e0343295e02d11a57252fde13bd7de745a4bd042cbd8ecc1b9195b7ae69a1533b077ff168353289b8aafca1232d0a336791b6b5912aeff631f882bf12ada3d35847e710ca88282748a74b9bde6048381fdef95d6f44a2444c6bfd1f04bf3d8790b4982df8d21bd0d0c30939741d4ec9d65f30ef0d9260a3018b33e1076819ecadac4baed20cedc687337d1ad3538dd7bb23f9fa15d31f24f633c7ddd52787e200bb8a995395ff0f2cfa18e698b9573c79d15a0204fdc458098191827d9511f8f25cc3d708e6d0647dd56ea6b4925316999eb14f0d1e6eb8a3d6c4d307dd0b9a34b5fd80a76dad83f9ac1d9808554a7524da645f516653f6e1c366e281a3945db89e839ad3e70a2f21f6928674c54eae507dc3560296cd1782da0135f615320d5d3a411295df7f7bf95e2fe2d8f55ead3c30e0fd4b9b9e4c60e08c41eac92d11a9a08457d79bd9863b8bbdfd034139615da54908198d3e10f98260af71137aaaef18bfdefd516d92142d82310bc66c4214fd6adbc571814ab266e1b699d50f0d141d59554833b3f872e82bb43aabb62c628f38f8fdb114809eaa617b1b786952e11f172590539d65e0124000d7e7290d3c6188003c3bce0567f98038181d77cc0121639fc4a3fada0a526f0913f47880454819d288a803297120118148a6334981340291cc12d9400a5a0e2dec3ff3dd25cc2c58ec23394c665396a0bbbc215f36903618b2102cd8abd8a61d5cd4b9ee6c96dee3570602afe76d4540536d41394cb76f32e311c82cebdf2f6e95d5defacc1c48af11d3e8e8367733ca3c3c944ed5816194955422a5d039e17cb9a2e621f008041d19e977a96a2e945e98d34c06d96674b084a95a3b1697e19891145cc707a503ebb07b0604b8e53716e273e8526741b5312b62fd1f712d180b0b21328b281ec4a2b08b299ad6244f076a9d1e7a108d02b2b1948fdc0442a859f726c24da3dd460bb6c8d3277223391a0f86a6dfa4afa10630a557ebbb19cf6174c3e45c9a213d2167d067d22fd7b76f6218293235cdde9c3c1255cc1396dad77c70900e0f6dbaa90a327a8eef3503a32a2302b527b37d68fe614ed12f0d0ee681889bdcc1217169f45d8e5266371a701fc9e9f0fb0f84d0f93e5b8deb29122f265945ce3e4e801bfd51a4b1e3aa8cf19d24608f8d09ba87aab74fd59b85a275ee75ca9424c174a9ecd8b9fa3062edbca534a3c7dfb2508a7c39c0ab10cc5f2614fa5ca3805dff6b4c766db20a404a8e9405185d02011f9a501459745fec4c96151c6231f5652c352a0a337439506d0f0bace90610125484a2fcc1290997b9c147195696d75f97f23043cb6059f3b36c7b3e752ff1f1dea1a4d19e3e3d5da0f23679080d521e28fc1639958f5bec45570c1baaeedd680719eea38569428d650022c85e57c6c4b4786790e43bbbe97548d7b6c599c3a71f126b453cded3aa82134294f135a00c4a99de61fb64421df643af9051b2c334b5f28c0346473910edad57cd616c20009cb503402641757d6c3ff986d543627d28aebac39765be80f7e362112666d1467950fe39c9e63625625f89e1a041e64d14bc3e1200b301bb63195b824586479246eee5b59771cef697b6647eb7be67530d9c8aea2843d631e393c6e613b0b385fa47a2f2beaf42cc706d94c4a626ad2608727c502c613ab9605f78bb4dbdd89964d709d737c45283049ee05a32b16d3214fa4825796c10a337d82525195ee339e0d1de2441d0e487406825b1b68172eaf181a6ed5afb013dac04c94721151d6757c4b3e2695fdea8e0edd6d17111963659702429c8212bc9400195e4ab0a68184b3ee24702992dd23a8961f61330a76e74e507636386bcd7d1c64966d7eb14d87d696c0a51ed0b5171942acabea58a44c0ea3b77080c7149a935e85ded3603d3797c41c7337d924a0136525ee74e52806effdced88487d08ab833699e57f2e93d732f004bd2795837cb0aced3f83b7323f04a7911e597e3df9cfb8b5976782cd08a929d0aa5da7e904680d7a603a7ac81131082ab618346fc36899485edf1b2c56f220b1974f0f951d030c42dc7e4667f53c09adb87998b716ec11097a564c29c19213bce4dd6b52dbd13ade55c92e90cce1b82167b0cd457aa10e42b24e8c1d2f27eb8239853aab35f7dfc1afaf15eed69b8e6ecc8afacd2c269833448409e31c9675d8e853f1212a777049c0f604dfe123ea262e99f307093e2440359bd7bdb01eca668ef59a15936db434837dbfff486dc8898c64d42e9e433f5b4714d71dc3f12fd1bee4a383af7ebec36b1e1a9795dbcbacf4da51055a3a0cc56429f0702431757b53a3c7dd9f743221d3b40cc10db15b09c56bd091c0bf1dbc7892074577e677ca306e807d643311287a37add243dc55e72579f19a89068466c9cea14f4450b61a5de8c9cd534c43adba4c1f5ad461999269e7921b65b9245fdea9746e6e24eec7dfa61d0454850b5097bcacb607878fa29c90d137a788adf254a5ca8ce08e41e09efd339c36a96e6f2d29ddcc5f0af6003e27288370667b8946db1bca8e2875a88cfb52c32b6f132dc4499d42f90aa305b46f7f9844f3dfa9e938a904970076170e49f33c3aa53200dac6f5e33f1d0423e12a564e8b0361a70eaf1918b28286878209c1d460b16a03e12ada1a469b42bb28f3f9c8a98d3bcdbed60b0c92df40fa6689df77567368713bb59412dfd80355007a05607281c868119e132b15eb80c3420b4721d805aa7d055b17c19cee3191483f8b45492b0c9d84ed0da4bd332a6a651fefec40913bf7a027c69837943e9239c73ba60f2f320927651d1d7adc747b5f504031492fa6fb1b048c5f09b69254039a552b4a9200780421066a07131f744d31be84d49b61536efa39e8dc50555d22d45e374ecbc079694a25416246fb7c8847c8b472b1ca664f1e81e8064a4c4be1f9083d5175eab7080c3b6c0c47068a5b5bde56c867fe1950cfecfa0051c96ae432ec54e8493b3652575f6ee583381a2d61c99e923909ce1ab09654704d2857f6fba897082c04c73715b5cb963b111077da2426882c4382e1b938a34048ceb34ede7f186218405fa51614cbd51aca50766ca9c77a70b4c684b10bb4122a94c3dbbb2b8c2c07da889796185adcbed25d3d7aa1a562b78abaa1638396560cbdf0494a096954208a0d82d2f6ce7c0b3c8fbe03258e424ab8c7f7af882b4401118902654323b77696549feab12d5a7521d02318056c4005733f4d6fb654e80283e7f7cd5deaccd8170492cde9ae1591d24bc1df940bc84efedb1c9efe43343ed45ac7e82c7781fb1d4dd4d32fb744c2aa2400d240f77eb7c90adf48f53a8f0921ed129192dcffbc82300b6b89ecf281975b4ef728a1fe147eb2d61b88bddbe2b2dbcab5f2436282535507c512153ae7732c9819cdbde61932d6f4bdb2d999cb351beeb83ec99e3bef2c1670a478960a604011e9c805ef8890777741755a3c119e9427b1233ab1b3148caecb9e93bf4ac807739b46e6677f98ca82e986b2a8b742268cc8e3dac739c1f0e5dcc8c13b6d6c4ad1c53b9a9926a9069d9e5d4182c5206c96ca07fad86781daf7dad8a3eee02232d149dc32b6f179895748288f049917816571df3dade30295abd77fb57514859ccb051a4486853b0b498967875d9c02de950043b441f960a6c4d3b7a294afb19df2ba21745f04f68605fe63fa69705f753af0f4caf0b4c294420052887a3070b41a50df70b789ab8b1cbff182796deccbf1cd02fa758207a0ee9c8eb87f003821eff9321a34c7b69dec7412580194a2c3bcf32249bf4d6b20542934465707af7726bb12684a9ac8d703b1166aaa8c1e807be8eff872e423e164fbb5934eeb8d788fd831e1cd89bb512dcc7e983bbcac49e4353b0490f6417d4aa60f9037cfb4ec8e0c05100486fc6f5eb9ecc38e0cd9befb77761ede17a1866274fc9991823faf89895694b25387a186a5819dfffcd6932faa847d6639d215303cced0c70c0177024360492ade5cd76ddaa36a2e26be95e632a96f4d02b5d49b181b3282907c9b81b438e4d6f19d10e113943e93674741946fce781fb3ae8388ad8065e7d0f424479c10ba31c5f306e18c74187094006e9bf9bd78d33dc95a145aaa38717cfe1dc8fa70ff56316100f12fbae89a25569fbc58d5afa79a4f1b7360d27480fcee2af6a36814f4b3fc2d46fa562034993b48046776120ccff62727b7cfda92db4cc6d938131638d14ee6ba0cdd478df1bdae619aaf0c1522bc3aa8aebd9ec11000fbe4199a609eeb54de8a1f47f8cde409c67d0fb50f5bb1144bee7650835f1325614696fa9a5cb474407b50cac8376fa8303a75ea4d7be4e79b44b84a41cbb648ae013d8328ebe0365a5e8f139f2854e50b6cbccf6bbdabd85d12edc6fcf27a5cfb4082918fcfbca418a2e3b4026c52b59443b280491493f13dbab8976b90a946a4168f6a80a845f4a22f4f2f6d5c1dc678438aa9b713d086e873d9ddba1dd4c5c4624d9b32d9ae94a85c0e315b5c02667a6494267c23b6ca30ad4e45d5738fa3776f0ea20f3a6b73646ecb7f0e7fe99e6d413971dd452fac1f7201a1a0fb0663ed23bf9bdd522e50cdbb2b2a403a9d53a74f48edb74d9e5e15d6a90c6f23c423ae31181089dc50bb8aaa23c1023b6ca288457d7bad385419b8aef10788b082de539e6ee19824c0718afd402f3264d882581412daf8d8d97f202463b57ce740331955895c200b3d48c08aa4d218e7911487fb9d59a15d4582c0982fe2239e0ebd3bc847f1cdb9b0a1e79b48395d65073ed7721a23688fd95047799fc9f25cd35092a279f75a7f0aab6ab7a2ec14a080081f502ad769f09e39fd132da16c859d826b9a5a63593467ee11a8c31bc74de0c42241dd0b4920192b8ae77b4ef40e966764010d2e5b1a5517218275111b68a25bcb8ca41aa56f7556c6b1335e20b733532280af281f6b40d1ae108241622ebb1ab5c737ecc1c106651cc932497a63277419917a214b991428a7610e0d504238a925404808688218e7c573a9797084063575cb050135947c4411a24d80510ae25b50db530d611c903d95db03511d47fe5fdf08629cfc0cabb8ffe23e58f6a1d3e5e81523752dda6d72b7c5c7a512c316e7f2e9b107c12e3e8dc29f71ad296013a2bc727c6619f80b0fe56aea2f093cf402882baa329eb31f083fa58596191c51cbde4eb715258b4f6087bf31c24b1e4a1c8733ef6d18d85812fe8a994e819a0f2654eb896ec511c107a86ec45bfc27be13ed61485b3040d4eeeeeb7ce647e7366f40cf3a0f5648e3c9aa9a72cf66162dd736a5a3981686b340bfdb5b5587a3fa3b7eaa892bc6070c106eb6de638a88c92cedad0a73da0c5e2452699791c168b2eba0dd872c5625ae780c43f01a2ff856467d0be8d80cc2018c3be1d9501c6eaf0b6f48c584287c75d96cebcd0e1268c41c5eede4db9190f97ee9566258e79127bee5f31d00367fe37c17d86f701e46a79289cd9f4033b47b9a66599a523761421f38b537b9bcbe48d4fb3d789fa4cff092927b2b1aa3ac0257d10992c2622c2047362e9da7ce9068192cb3adb474e0ba33cf488ef4cdcee317aa5dfe35450c49fbad999b4a84ed93968240b1025040a2a6d0917889d48b52adb06efa01ad69fd05093b97812c7084abb40d4b8afa2c65d94c3d4bc5cc58bedd9d9e4a5e66643c918289ef40b5200fa37da9c860e39a9b97643e4c34486b620783707a165e6355b76c5a4fe047cbaa99ae6ef93d055cddc6072857bea723efc5f1de787f20286397976288eaae37eb5860ec76b34f573de1c2c956a9cc8a420c76abb149fc78e5c39aac83227b4214ebcbb12c2dfe022e3f0a526a5a4224a41e5a47b8288af6104aa0a4f58dd9de8607c34e651d544f7acfaf24f5054e63fc1b902424b2884960b2b9bd81ab5782b2324f658e306a807ad4a06f930cefe235ba5c9e1b8d1d7731c6194d1c608e0fa3e5cb87bd311421a2492498178fc52a55e644f1746c184a905f46c38d816522731a9f138a0128ae928ba76e5c2417826d7979903635cc03c0e82b6e9f58c782d6e08c4b95ac2ffe88f4eb60623cc18f44ceceb38ac4baae78a608c8ef3bfa9937f37dd51e53d3a8317c4415699316162b3c212fccf3839925d53189dfe989e4439981a3dba8fd632aa69f2f85212a8dfe68dc7394bda1a8f38104a6d344df6e686d6bc9014c32e28556f1b55ddace83f2710bc1354b39dcd39973197d662a64dce8f9b36967083b648b6a6a11dd68f253f1f59202cca7d825d68c6364699653628500966a961ef2ac372d3c350b6600d20c517e86aa6674be6f621a0f60d91daa6d056e29649a35bf7127fb159fe4919dbe7c0177f5d40672188e49840bd2e9c4921ea2c0b021c74d3faf8224708060c99a0402f871e47ab52650630abf92dd5f47d0bf0a958b29ba96d31383d5527d92a976ceaec04183ae100fde0e4ee41fcd612ba7120387f3f1a1d869d5f020fed8590eb7fd8b53dff1996539b212405062d703db6cebf85ebc1930125cd8be87bd56fce4651ba44fd474e4090d6e788bf77eeb9754486c91ee18883b1a9f16c29a1e421351ea36f3059d1793084babece78d9bf60e44c27081a9339b506eb5c7d7bb9a8c761aea4d6f2608fd41ba923f3ac53415afbcdce8cd3d7f6d740428c6ed19e914873de8df8ac6ae75849eb985f7e80ae140647685362a71360d28aa54b517ecb225df77cd0b1f4cb6b9e954dda67fbc8c3d5803caef9c176ed8208aa5570f0d7b8c008ac7a8575984ac744e1046773c382f4cfbf0cf5aa1ae94af33ee40c1ef8b8330edcc105207fda6dfa89bd596a9c03dc89da2c9307014c51dd8bcab1ded92ff32c7bd8c3b4abb76c325fb115c1eab050359925281e26ade9b4a3bf6b21a272ca70eeac4f364bf28154ffc78f7b94fe067557ba66c48c2cedb648982abb536cef536776c865e3da0056ce324503307b21fce119a76580b5f3bc8c88266b927d949f62252dfb1298994c20af0800f52ce390b6495cdd2f37ee15bc3adb0f8a3ac0813f6ef0576ce369a87c371123f98b44688524a8edfd4a9bc9c3a1c49430a0254457d8743f26d81ebd0180094911fe0993532d01e0a234cfdc1512be8441e8089f2b0dd16abcc0dc7831167b3441207d476974bdd65cfa19942e21211c7ca29abd8b2dc4ab286ae667b00d4f56aca78550938171e48365cef3a119b5f8ec9a287cd47c836da0eb6b35af910a0ffbfd2d63aff9d5bfaa86a53318586e2f7fe309ac6247d1e33e7ad435046acc59fceecbfe9cf2866937be0076d998015048b32c5d7419fe101a77f82579d26cc1fac2aaa1fb8c3d2ae3a89fdadf4a88b38f1715b2f0c62f32a16dc1bb1419e86b69939dff1fb57213e9e52befe1ff06d7d35df40b6bf78d147869079acf08997ea026aaec4105872df9463eacb03d9ab6f7b18fcca2546b4dcf9a0fc53d06e49628fb02eed5a4b8c89854b96e193c74b7f51609bfceba6e5ba9b831803fb09be9e0403dc63e2f13d1424e0b90a06db76bc721049bb448a285a7024c91a0de095184143b07f7519f64be3dd243e5a636a05332df286e60b9b69a205194e26f8ca2822d6969446592bda41afef14e8af04ef22476efb8409c57e74e75d56e8fcead8f6832b876025577742d8dfbb740f87213692c7cd6a99b40eaf4cdfabca2cf3653325a4bb55af714b35b3d68bf3d448e0ea2d19743145e308b0a45e981805c39034990c7fa26ee467dbc951dc4981f46d02d91984126d891841124b5dd0962490d5847d1c4200a6181e1b434af14b2f51d98fdab1439622dd676027c7cbeecb49249929d64a6c1c50577630cff319a847dcc35d55c610fa82831a7da06e09386f5c2c7161164672cd4a929ed6b1eb1a34f97d8a75804b46a1d517e135f569051c9f0a8ce3082c529be5dd041ec842b9a82d0c8ede48d0cd65b85500548545e08dda3f9a16343fa72bbc7bcb04d78bfc2edfce4ed5d204d0d2215f12f73db64ad3fb4cb26768d116804fdc2f7e17bdba472d028fb29b58606e6bf4c94df88d122c524129bb9668b9920377286ed9d70ee6fc08d997f6f4685bf436761b941e51cc35889541df3f78072a3e0602b8fc41368db531825da0573d143618ff25c3402dd4858c688043c91b019b63d3f8a086e64c45f68bad39d835814523eb2af888e8a685aad0149a05b7f751915bc59d55027850032418fc7cd31c2d0ad56d855c688dd4bb96a6f7591424881d5744b719b06f941d4be911d10141b58fe083846f19177acf22605075ee7eb1ca9fbb3615a1d09dc33a7dc7337da30e174a90b63e8f4cc9ba4ae5c80e2833b3d3c067f360ecefcf7d49e0fc20b9dec9925c362b5cc79e62249dbc440e3e78d1ed7aa3e8ad005ef106bb149566d925e048cba59575519cac986d6d06d363bd76ced55132859ad8d9fe9495ddca23e82d5413f661672c31ed6796193b064603b9b62dd0cec2fcd97cfecd2114e75466e39ea76923b4243fa0d2af52b4acbc2116e60c5e9a85ea751eb5c31e700249473bb518b0146a23f28915fd88818010b5efbe3971b48838da57f1f664a1173b0b0a16c564aeb34ba3f39a4d3a261206c0a947d31c595ddaf7f350cd7fec2823e2646261a0b87ba9157e1a42d5c6be6fe450a31eaab2cbeadeae8a06bb687624ab20e2f4a24950e4fc3877123154abcf02a6c6261467d6d950e67e1282c097ce163814911760702f3b80fd0432f376732eec649c7d2304f4460f6fd386201c95829e55b209e946dc39d5c38021f489c2022fbe4fa50f3cef6a0125eb7faaa5691134745afdfed270b9c071731e870c614b14b890f4ce99c2de5d7e8e6240cfb45f01720156236806f6118f03a4b7807dfd6b4e694f049f21e1d84b1029dd44090f81a5db49d05d75dd3a2b596fa51b20b4d0ca7b30c88e4871898dd36889f9510c7e2640cba248263d738f684bcab6d21b1964a9612d8170aa32f822b6685a8bda67f3d0809ef7413830b530c420820522ecc5288548c454043af28c106a600ab44e5a91cab842c8928f74b9e076a5df20ad35c965e740b5e8fd870d790556b5344db71087ceff63e12ba0c0496c699b4101af65a54abeacb9ba6c0eaae4be50137f5dec61599d460adc9b910fa9e882e7e3ffc9df74ad0e1edc353c4233dae0f421f170f0c586d93e073590a3d3c20c05609632fc120ba0d98ca1ba2b9165ef7e47af3cdec4cf29750ec145200284cbfbfc5985d69ff5ab18ace2fe65fe91a13fceba554a4fdfc645156f4fad1f180e5f80c1b365d960d0dba31dc07ad8d7d6e401a21e9fa5c9912918e375c9f30d08c0c251675b2d0d77408d422c9e302603e40f1370243fef2fc1f6959d1cc6a99100349d8fe5b0c799a3203f851ef6b2c90fb769ed3008e15ee59faa9d964e4629698c5b80a0106393641c145b1ea0f218544e243d6abd2cfbd82f0407107b48e4206452f034ae58ae2f73157016b4142202144235f1756155e848df3057080803672d945f3e05ec15f8aab9f73d647e9120dac1b0352109de7c18190a91d66c560c479f9928a2a9cffdc9712c4ac84b895b1a2797193e9179d28ea89d5fbb4fba5275c186e1a9d4185c6eaea3385548c6fa7242b4be4aa4b5421f36cf90ddd5762e7e7940626ecaa4aa008b83731a3820e959731e1bddf49a63e4b9e4148754e1b8f2a757eff4441c7dc51af76ac4a13a03d2abf1ce27bed48272c593d20128a512ca7322357f98b1cec9d963e6d2151f20048408845298c88b55c8663a6e4d376f46156d452a2827b13f677988d5cf593f00e330e65e141ed66f2516d5cd7ab63ad35a0c419b6d48debcf3451c2f90ab1202e39ce2f8f9b8ba0d49858f7c72b0bae38837cce58b99c327d99c62842421d66bd6cd942c3690e73dbae58525ca0c37819223e4739e187e03c00290fb9315da0463df0cecb50a61ef4b293910e861d814d3d889513a562a9c0e3662dfbedfcf6cf3021128fbe9e657ebd43b6e2048cd52baf761b514e58f5c9c3e6627b42709eac17901962a0596aa3f7ef8e63ef3bbc204afba00283d26fd7fd88e4e635186b11c1365dee91bfcb3ebe722e6dc8883a8064d31a423fa04348a30fac4ee4c87dd690ce88a7c744a7bdc253708fea111c1b21bd2c6e0a370136d4a69a07570d04592a33a526cd03c5d01915398479d84e47a8fb6b5ce8ac346996b3b9c8674e831e24ff61d2e13971638ce62423f716779fffc68669547d0ca8c3b12e72941cdfc4000f48d56027a11dc782fbd7e9e309a2cfcbadfb5333110f884546c61df9c26c48c48eb582778857e0730898861d76aab7247093e7a7e2f118417800b7b6e179e155e5fc462527c3790b09e1aa058b70151fd05c539e70670947746b0c286154ce127b20f82e904839b9d1e6397b67be85b414593cdf9b6ad79769e7bd453d1799c53903214fc0024293126925b3c77bb35e2199a949a842f783dbd24903d974d8c00bf02e1d1d1521cee6f0d65db2502ca7713d7af5c493d50d1c7e15fdff6c1f25c0eb0c97b3dedc8882044c13e192b306a1c0e89ae4b0075b8eaa302aa518bb7bc63ed40023403504989f7598900f601d0520d22b476faa4979f437ed42feb7da7dfb3132eb8a7080c80258fe6e2ee5ce4416225be911694f50e65da425f19be0bec7ac2bdb07239eed8d6c5f847a4687bf29c4e67ad39a9e1a30dce81b7dd8a52b3c304fd4b69a23e0950493d2ec56a28f368dec68c396e001401e8afc4851a8f9c43770e288fc76893ff2bf137f728fcc19e8f57103985f1d765482d6afdaa97c63bf89bff9a420474a416340785fca3c295b37cfe8b0dbba8476ea57a8bbd13950e36cf26449eef708d5f4cea2c5434a3c95841c80c06fa2eb155116993402e89063b461cb38ad33b034769d07330566e96f479f07313dfc45649a43775808fe4589555b7c1113fb3a52ee3ab183a3853f527fbfb25c5fe7241c0a5718ffd57b7faae23f34b68fbaa83a563c7b390bcf75526a5990d5f256740d1f240f67d869bbf4c49d3d32f6185bd78884fae8abf111d71ed58af80931e20024b631ff4c2d1bc3693e64ecd5a61040a4ce0e08b17861485de11ec9397f8987136c1b1c9a2053c770e0e89fb80e19a2805f755cea478a8f615e6d87b8bfafe4253ef1d97a98484675d7f91c486d552b043d9d18366bf4f6c1997e62fc5f16adc81442c4761b02e4b30c6b1a666bac95e9a7dbe5949ed1f7940db7c0106aa84aaf4d9aa468a66283c21d5ef27b8d7b820ff31fa9ecd8e695ef190d157ef300f33570d5d1e28b40daa01f0849000247e17740798eaf31919152bd3f803000ada6dd0143568922be58f80dbeee172849feab3df037cf5b488fff4fdbd933e8abe8da699f095994b181cb241fc7f7def26464e30fb108f1527b28724416d77c4f4810462d20d8ea0c29932587213fe11dfdb7cdaaf19da88c01d09ea0126596717a5efe957ef12a8845996e6b8a898d8307f0b8bce1f291288ece59cdf52008f3069e09165739ad038b1c3ed507eee9b6c928773e25c6a360b88afd920b8b5ea04c551affd028725f32e6a604850aa90329f2d822c5c099cdf123d5732187b418d6ce0601eaf52ebfcc684e892885ed62bd34b88011894276cd1cb207c33df81b3b8c63ae13447d76a4b74ccbbecabf12f6da73e2757f3e2600765f93c9b562c893a53030c6aae8e9d3bec1e62574827a57d60d04d44867db3852bc66ffc93f4078787c6bc0af3408240b80211018776677daae53a5f7192b4e792e82c6926b9e8b723496f0182e31d7ccf1e56045a137ea5aadfbee77f1c5e2d7b974411c6f1fc060c6d17cc39d8d296d5886cb977b9717ffdc993fe7c24f7e1c1e04b7651a545c1b4afc78b1123669154941ae24da6bc2dbd12a353244f70e9e2fa45af36d4ef97a10efad73843ecfcf40c1c892e945715d377fe7f5c1783100f8ef012cbef55f9168677efef8433130598e41c00c0c1f472091bd71a7570a7767e3b245c7e9bd1f52006642a7f058fbcf99e3c523db1be688ec3c2ebfa16e999b849d398cfd6ae250548a0fee954aae48101a04a1b823add2e5c5421f0e07c0baa97e4682621332ad1395ed72ddb8e5ba17bde88ba353678d784585811a1886a20629d49281ba72cf90a0d382db743ad24d6a06d283c169b0459b8269427c0b4366af36864640073bf2d59764e36dfd39f9b3ddb5f08561340e7a0fff5840c02b918f548204a98de6318d1f75e8fe8aec5de407e8f76b3e9282dce77e219c560e0582cba190471db4e0461f9afc5b583d5f5c44f5d31a374f344e9a506f3a3f5b2684758759860ec39643c21f9ad1e695909cba72b9b932cc0a3bf4f3c195b62745fc7fa3fb582121acf82476818306fb10e7605847c08239d9ab6d24d757c9f1c47cc42d66f57da04db576e44ba3aea60c2247511497c7a62af952109efd6d8d1b0a9d4be5b40bb1af4c05732fd57c8a59d5511a7b0f9f39a744f42f6c661d64412c71ef19935d551c3459d4d88443baa8cbc65c69afe304c8196e39dac86225bc3ce416ca9e4e6a83fd7c10e15d57ab23436cdb6de419e657805b926afcc1a576ffe2bcb1fa58b796151967a4122f5cd0e475f734b477638171f3a0fa4296f67bac4532ed78107f254a596091a9c1448d2a3e3073e20c8277aba458b0825f744008f012a72aa27fa04d2d1e42d7462399ea57804dfe9301da2e8a78199f258e8d2e4660421427b692258e722fac55f3b27f5699a9ec411d32a9b29555c5d842bcd72536fd6cbbadb08163559d0433a70cd18e14139340eb5423c7ba016ff226d02c1a4a320bbf6d034206cf77b7fb7b71240d1afe322ceaf914e083f188d86e0e2682c0b2200223a4c5a26a5f713b8d79f56eddd0574538020952011779441c4e1598e1e574444a4b29ed7dca550ce71e9944718c48f3186101f121374e0e06967bdc7717b2a86928dbff6b77bff241529ac16b544a43314669b15289097a1c7a0a30770a54b5c25adddc45450dc087d27a239646df5c0b29f71b02d4978c216d11f8001f09680e0281de9da50969bb0b3afa25e879308fc45533a33f9913b9d39ce0916214da0f3747c67ae153b2f415361559364f67fb9f0a36ab447205b251434327c46ac4fc5e53dd82094196b6546655206fc8f9581593a8dc8f355a5a5484d95c8201a84ad5d00380a76a6a8d02fefacce3516bffbeffb7ffad2f17f864ca3778ea9ecaa9986f2662438bd1f3b0c78bb1f19090404c21f3201a4d8710244bb3939c9231afc2f13c5a70fe82bc9fcf60a2c5a27df8348ab568e36c7075eca641d04712009ae9a86a5b93e6a13980ae409a21db5d5cfa9168b6d49daea6c2b00550bca8f98e9e5efca561d878ce327313928abdf94d0848b428c997f29c0f1ff20d4248e94adfdc20b7f10ba99e86f52e33230b1fae5d9152b66a98da780d28d4fed4cf6f9c070e71450d363da69f83a31323b80c1a0fa2b3b2d24f0b0f7ed85697b3d848c10d1506e7e373a378d2960543a13a0bf5bd0b90dc6b17dc819f7b8447322ee15b0a20dcef26f458b0d81f2a49f601679faac50e81f0c69a6967393a586be60c130fd8c89e1c286902e9202f438bbb71e6213efae3fc32a4395e69f0009f3744c8fe1b8786467a857a1b5f1727473a66f0ea07fdcf205b746bde724e449d1890ec4cea6fc2650cf80e043f971d72991755d717950c86340fe781a25dbce3ca0510f457abac7661fc74a21f94662c7c6809f67fbee9977ad5e97e86e41917c52cecc3e7b35e79b1d9654f45383ee5f655cb38379f8508cc14b2e9ba41b600d8960b83ec83f46890195fb2704e3a7c19b0ef1a52ad6922c7610a5cc5f54c5370291bdf30443759bcac0336a251abb99d33e391b6ed395e50f64031055e5665ff72d79dc4dbcf603718ca5e49e475f48289aa9963355d5242a00ca7abefc07baa7878f36c7a45c26ff7133e9d5b0aaa803d1452a759713883bcee064390395c5413930e316b43a3bf8b348a85e460b6d968d8120f64e48626a0ce657b828d66f7744050617b2d8f6d169ce8f64b0b54704516a0e9c830a8a301b89188a677674d4bf7d41d8ca9b8ea31fc9b8ace6822de09bb11932b5e09a90a211f2d774d32cfe586535eaf0015804219041c5977cc6ced024591015cb82fdc518999e453997d82508e94d585c3381b1ea9c768853faacaafc2f2ac44a158f60db7a7f9098f4da04736d25e98898524959dcdaf7b0a06a63134edf8d9eb3d9172c9c4d8038764c75abf3f54204291e6bf20fd6c2ef32f2265695853c02a509b100de3be7eb6effa11d17bc0efa0ece5378af5dacfe9b35bfccce6e2d66657143566be7f509ca354d5092840cffbaa5a4ca43fd50dcb8040fbfe4e9bf9ce9ef390c20e7c83f9adcd3874c364b800adfab2f29cb9c3e11c1bb8f377e6431d9f16c8f3db770e1315ccfb7a59fb9ced74dc1592e70135b6d62d43803abe4996aa6631cc49b189c53d48453c39166fad44e1aa71d12b13cddec2b28842580ca0440b3c687754c8295dbf4c805df9ac01663a1a258bad3bd2466faa15e97c6c844fd908882b4154912113613126b6947a884683178782256d162aa0547efe9d7c62ff2deca93bff13f3e640b49332e15297067c084a30056d24952c7b7c0f8b109b839acb4f9248a04685c25e0d880027eea4aab422788dcc1c1e252f4d6f91646c04913721a5b0204654a84b08cdda38cc1b575c7c5f083ce37f5803255f1af7824fda7e670fbd3673006120e99b8f0a7768c843676f78b80b7d9adb02a55ca0615f246931a8db5bc6be28cc5be9d81fb2341864157537fbd07114e3d4e775a0848867ad1050b80511173ff0e94b05171bb295d469b48f6aecb328d690e09eb58fc8128e1b8a242f76604ae1fe219ac08e84c4c1f8f8f5a37ad318834ba765b4f7d9b2c7b2d0e87a78b8233873a0a29e1056c11a930acf38e8842687009a38ca7d87b780fd62aa0d2f566591133e70f8e4742bb57ce56ad4baca9b62d990c25a766d671c7096fb18e9ce72885838aecef99ef82403e9a8cda29d5b78dd227e2a1dfaa53eb9757734dba0832b7bcb07acbd7d2836fb62ea0ef7c66510e0a92c879d8b3d1c4b70c5a167e3097298a79d62892ad816e71e952264e4168c7d02f290b12f45d556cb83d50364489e17a31749bcf974ceba0f4920080594d705d9784e1c0426dad359a660a0743f8ee9e9501c1bf0c2391c8981147bd7b19063293f51d66504624179b13c2ebd2180ec375178ce74814c005221fcb47737dc2f56b21b10107822a3c4cba5bb8993a2ee98dfb5b3b1c1b9a636bc65794532552747c9dfa498323c0e719662118d54997758c6bf1fb5a35ce20cd5c3db8d137942b89892545b8ea99c4a0538f09ad433452bc8c83ac781ff4cd943e956f26a7f51f15e88381e1b1563040047f512950559e251af7c11c689d37c8bbb2ce1008136853307543b68a1502905a08114072a0bbaf83c6eb0a6e7cbcb416a9f52d2bbbfe3936c3dc8538744858cd617c043b2b48a215bc0c13195e8207f6b197497ae6d54ee1482c648a452e0ef193917fd5d41ab6386fb946db75b0dad65a87a8edd1359d0097f3cf05116fe748bf7b1ec3079c43b0349d719a8fae5adf75dd98a1c19df3a58cad53a9f275c34e4fea902877ffcf0511e7e6389f6f92de36ffc452147344d677d2daf478ffb5776c6bb81fb4115604d64820654eb0f4810f84f03681e1ecbbfbbde1ecf0adfa89ba4a145ffe910c0632bdd681659d675c7758953fab3aa10c2c06dc70d85aa7c181726d6da199d6c70aadfa110c26ea52d43b6d109d25ebc85aa90444d5b56a0389e5f05031f2213d6b5803ecf8b8f42ac3cf4852e8e88112c86442f69253bbeb4a16f0fcbb97ceabca5c5bee7c0da9c7b07c14db46375bc19e8e9d5d1e9a51d211c341d39c600e7a55b9d5a39b7e92d604a95d31fcb3c58f6ada41fdcec7421b7eb46b9f1890592082402cf232cb8a28b47eeb28d84e0d24d3aa5ffc1f337accda14156c92923348db93cb39984a99bcbd99d048a2bb03f2736c30bdb26e00baa42c25c786341be3e6448b99ad802e2f3abba964645c2a83e9953cfb76e2e41513fc5d187c0f91ad12a02d9b812d2ae95372a166b5725eb0530c1c4920a3e0898c735c936d3f204c6f8d4ad1c0081ac4c2c06b5e4d3a774cfb46eee4f96325d55b1c085671b189f143f5bdf4a975c0783544b46729da88421efdf5219aec0a2a223c8e7666db09a2b048099b7e76660a74e325522e9b0addabb1bfac68b0bbe4e2ba4b30a32b4ef699ee46917090b8e1e6ae784ffbdd0cef9704ba018844026ac782f654b9464a1d5375b3f3f5b29d91beeedf670655169e586ab8247058b28c009ef960bee6428226af9962d0118050842d34b52bab42440f9e7a851afe3b97adf15ca65444549cdfb3e05f3cc4a44b50bfb2774e4cd6a472e87b26785e0211b3346f688a34d62a506318e34bdb6876cabf8fdc898bf514523e52c9c457d2e5408d5fad8f9e09c00515bfebeafc456fd531e9519a2cd75c2236bae5549eae6f424f25a832524b0b0d7d90ce54a59c29166f4bd2416456b95aa7274421e98cccd2a5a7c64857254e083643d2fae85be2853b4c8c0686c636bd62aa507ceb88200dd612d4b992f2bc754633343181787b6af6a5dad45f08d774bae2ee8a34406bc1d00cfa5d0d5ac20beb744b9e02953c4fd6a776135bb1e36cd4cf63080414f2837b7a11a3600f4f671f1b71142148dd93af3740e05836f98ae762ecbfcdc540f6c179e2bf36014f8eb97593153eb49e24c8b4252c9e119776ebd54a550eda59478b0fb32b0a8a564ee44d7a9018413054e0884af0c59305c554d07998456b5b2a53f4db32336d2498a52e23cabd94a1f42231d5b1136f5463286d086263f6e9e031a470b0bce01008080e60e63280ba027b2dfb3e771170c4e07f957b908ee56ead8ac35d94deedd6d6f29a54c5206ce0cee0cab0c6fc3d6c0094a50f23182d139ad35a9697123853a7eba1c9211c05328d62ae9d567d19c44524a29392939c96dcec96c13b9289390b2d6cce5573d931ee328d346354f2046e1a18ecfb6975b8c2a4b051c64810b1f6edb6fce6d91e3388ee338aec618738c31d618b31c0db667517828bfd65aeb68e4218d85fdb17a6cb04a23678855e6d0bf08f931d6208e08fbcc251566a86788dbb66d2e6daa6f6fb3ed8871025128f3b0e63089af54f86ce4f1cb7cf4d17cf45ac3247ee49c966d70c428723ecb138851e4e8f8510eebc728723efaf65aabb65a6badb2d61aa56613fd86b7d1682cca1801f34064c07c48bd50e33b92d0217df02823cbe41b0ebcccc8388ec98c0fde301f9e8426340e7023e33ec884305d38bab1d530e630898f41442a7ccd4062bc2e11764ff5d517e3f553008c29a048c3cccae317eaf86883230afdc8b398640a423e87fc519e4014fa2c03d14012318a9c9998ccf2550ee3c973b38bf9561e2f0362143933abac23c6f14cf9e0325f9cf1e163d11a8400064abc200833371c789959398ec94ccc77c3819ef1c1710230b372990f2700333ee878a266e53a0eb0721f5cc7133532ee834cd6f1448d0fbeca3a7cc8ad9587d115c5d0812200d9c21368200525f80e132409020b5800c52b0760988933312b9fd19a8cf9565ff685437c8efa59cd68acc12ea2444b70f590c66a448f59a642521fdad36053244e107d488b5ad523a4553426e91255340d8102155387af2ea38c08ebf99aa58d0c132fce1086d169831ec85d24f3a1f449348b649e938919fb36c34b9fb1564996aa0273155b52081232c9cc9143a4916f568fdc89a9688c12554124ed37e7acdd95d3e58d4e44b62122661c73d67b636608e0a877084b4494998856c58ca387ae09815685918f5109961f62e48731be5b7e88336b31627ef820e09045dee9b14f4e273df3e9e4e4145d2e7f8a9c7c4a3cd3b79fcd88dc7e5a258dc4f9f5d70f0a9e774211256c46e8d95af1250d3bd87c10656c43248cde8600e17bebc1b76f42a28c74b9b54831afd5a397dc866c445c51467a74af3c5e795a157a49f8f688a48a1e5be59195a75946af2823a768f1b42a4f6cebe9d984782e6d8845c4ba894210f6e8258ff5762412be1bc7759987d171d43cda444d7ad641a523c132b93479c9a5c9a5c96da4cbac83158b2a0b092026a5528c31462959d175b062d12759a52f077d12a954922cec79885d478392853d8c45384b96b7156948b44a4699b57f93cb277d29e3e89ed33cd307e4c4312e954aa592e765dc60770dc6942c91450de2f44d832cae5973c51a034662080e6311123a8080c358f43fc09c0f70287df08d626625565895d40aa3340f7d15da80b6d7f6da801a6ce9aa43b617a59b2bca2093ca5c63bfb93697cb18e070736d2ffa02da5c2bfc9abeb95a25299597004d9d56afb204f8ce5c2387af3e0d2ae91516d2a996aeaa43738571a690b012611c1f9cea83717c706c836d411bd0e6a25f11c544db121cc3b13967f6b3fa6c2ebab9e60ab7706b73e1d6e6c2adcd855b9b0bb762980817551f0d2b196ad0f549b011dc1ac53e80c3ca93439491d4b95a2ba59356dcc22d2ce4db298f1d333b6682308e2a524c558069f6629e9206a337e4015cab8f37e4b3da805cb4095c3ddc5cde50adde900c6a159542ac4294e10d094599e965e10ad1438f0adfeec182e013dfd13a9fd15094893e5166ba7c5c3ebee3de909491ca210b3c4136233c3eeee34a43cb05e413859d2146e8077c7c867c585aad922e1f23bdc246702b89cb1565c6ad10e3e056cc1e0bc785d3aaf0c4e3c45262a9986072f2879c22721e5b42adc70c58d6573889be7d0e49a951e9f372c6a69206690fed5c4cabfc1fa6c11953757e56e0e31372d0e93208963e5de659f404fc82c1404c07c3e4a904063bb223020e61785e720e23a5c718302b8c8039c3517b0bb0632e31c23e8ef8ea5ce2a8391f1d08425af1a59d9b3d9367a63829eff5397b5e1e60f2704da36c4a02c2f98c9e0ad899af6f9f44bd6a1fdd3e967c77a8fed9999256c5e6cedc993b3020cc4e0b35c8fa25334eaf433778fad011b2ce75b78d347bba04b6b2e6340f4f8c27c6137307a5bbc78ca3e6bc447285256c1ac82571076910c967aff8527dbcd449019e3e3b29023c637c99b1fb9849d9c71cdaa04be09939161af046061cb68fefb07ba8532dd4301f42ad0abb8aafa27db42ae6266a50253310ae53ddb1ac5f7c8ad8e0033ca4c085a228869f2ea0c00920546801113a1665e22482c3b9338db4ca05711593b0337df0edad89039839034a2ba595d21823de34eb588b3f58ba14bc30c4c40e1a52a0822fd81c206108ae9c1e2030d13e79224c4cb5b30861a2cd2ba4873073c7e7e74eecdbe78f1964f2cc9d28d3819d6f2fc4547b93616e6036f1edd4a779e28bd6b14eb56b1ac6279e993bc6024cb59e51bb8e6c0971ceb7731e508cae18bd979c010e6b4ff5a9426a8f8f2a4a23ec4b0702cf077dd7f05c7395e56dfbb1fd7547d6078a32f2df735d1bf3b79f32f4f45721bdf26233d5b121db0b557dfa96c48bcd1587d3a9edd5aa9f2843b99cd8f704f5aa1ae95411e2aae22086f5871f62c478c1527b6864ae2dccc22c5b63f45e1e902b0938f45cacb9f2843ae56a958c32d1853c264415662282edc2372c565d6d493ad5db8f75cc9ad613f2b010651ca16d21cac02c4f4994a94a74e1dbd3c2b77b4351467afd31fab44e27bed49e116ceb8932b5f6d800c3e14927be34c89232be08a35b7c885934d79d9f1f2db721df3b7faa3d211681cf4f92564d29867c7b155287c8b0f6c88f30597b4e7c86f4ca03f25c46e4f6d3dbcf94337b2ff05cdf4596091c62d6cfb76356ab428c6f54d2714e690a2dadff117f8694d11bf8aed5658dd1ba57263eca34101d5d49143dad383599d7ace30036eb00c2fb2620cedce010e61b87c4f93803d32a9be851e85be863061d8802c43408e3021c3d8471010e636bd5d2a9dec1714aff561dab00a2050a374ac0a1cb3ce30538848189315bc399760040740fed5d0b03d13940b44afee8655c59f7192d6746452f7dff8c963eb0cdda634b4983e0168b2f40cca09e8e8782efa15685168a701b523223c9b70f20fe300388b9caa2cb9a850d58289cf02c9ec6b6010e6b508749c84b638587829a85cd0b506c439b922d165f58bccc9e0e117018333d46e6981ad4aa30e6db6aa1d2d9d17a6bccac81df524ab192f3374f44b72e7d6016273c38e03327f307f804677aac8f433bd9f9ee04e7434fe844eb4e82787cfb098f1e2fd42bebf264e74d50956c23a826d886947c3be781601b812734f4ed271f7820f8f1e1490f219c131e9d6aa7279ff8d2de42385aa801cb82c5db8f762aba8c94aee454245cad32e6053904fb094318660f38cd634b38773a9fb69e45193963c74c288bb40f06157de43b666234af5ea3d5ea58cb9a26b364c90d641989996ab7b90273a6b562ba1112d6ad3642050f22aaf830c6242c066bd5945902b56a116e3a7766571fb07c288030460ccbebdb1dd4648422ac2d880d60e8093b4a88be59d3a5e3a79ff64da73e630b5593fbd5432c9c9c1e9ff99ab039142bc29181841cb466688c16c599d8433a3b3dd4ddcdf3438340c00e82c398981f762a07348fd1a90eff8161eaa797e22a5c19fd7419575a9c91a8034828be3493902da5a0ad2a68eb8df00c0511f2031c6acf93c30327beb8a4a03fad19f167c6785a3b1a8f8bc286e0b5c455fbd4f05f1aef6dda299ea9d849d9de7de7f125ca1c963e6c9fdd93d2fc2da718b4d810f1657ae6337a663eedcf9c39c39929daaa3fea8ec599990ab12f6d47c84bc6e6095cd4e5841ea02056e783db676bfa7c0be9c96a74227cc74c787afbbd503094cc280458a75e63a96759474c6c7b00984d0a1c332d22437c698f3371a6661a9b29a734e6518616519d99eaa1996a9f2cc061bbbe7f1a8491453dc021289b4e41a93b81f5ed2d2f624beea00ee0c2a3c7a7efe2f3e8d25de44d8b3718dd65ba745749baade0501dcbad816483c7027b45297da66cd2c2a69cb464279591b6e23287ee2de07450e23498e33a3e5ae55a4cc53cf10d95b2a545fe9c850d35b0a056ac9c545250624c08549c98984ad823dd8edb465a66aba6f94e4b6f3e0238bd92488efb4e3ad52ebf9b27aa731467ce7d15a8cec51a7df33cf2930fe353d421c32e077d2a5180437cbac1f8e4d4d2de18336ae61a46d4cf41258ece5929f860054172724c833d9dc095ce0ad3343c591a16124fea36d2e5749a3b5309b0f40264dacaacc04d6badb59964bd3dfa8c7685b5b656cf1aa454a35a379f10622290bc7adc3c4167cc102022c2321143b47c3b045a159d46745a6db59536a055d2e54d94a72ccb43c454fb89c608cb9d0e60777e409ea81e7dda44a7339600cf8cee8d9869ee42ed63660d7dd194628a1339135bc2cec9b43929a59466d99454764f99d1cceb6985c44c49392497c8a2cc6551ab32ef5b83872c482f6df06ae85eb27028d30a130f472db428a3b9cc5eaabcb42f535ed697282f9dae4815798394a537bdf4186538978ea30c8bcb1c665e59011bac537d53f2520ee97bbec312e961bf1a2239446e88b06cb83544584158d260fc1a22339a45e61a229785436be4db6dac55611723b245524664e1e4e85c9e797b4838ad1af21365e28c79423c1f8f9443f2f124d89384bebd86884eab66163e3e00be1a223aad21a85bd31a62a455130b1f1fe6ab2962a4c78a5b03458f9f56cda18f0fe3ab29f2a3b3726ba0d0e969d5bcc2c77ff96a8af4b04eb7060a964faba6153e3ef8d514f1810247e5d64081f3a355b30a1ffffb6a8afc204ab9352d229e564da18f9fbf9a223cad22945bd32a6ab56a52e1e3dbf0d51469419173726ba0c8d969d59cc2c767f1d514d969c54c6e4d2b36a455530a1fff7e354586f08832120a1e425a35611fbffb6a8a0881e2a6746ba0b809d2aa59c5c7277d3545822cc1b7a6b584c89256cd287c7ceeab21b22489776b5a498ae4b46a42e1e36f5f4d911c5894912d988f56cda08fbfe2ab29e243c9bd352d2545705a35a9f8f82b5f4d119c57776b5a2f22b156cd277cfcd3574324d68a32b245a4a855d3091fdff4d51029fad96e4deba7488f56cdd7c757f96a8af4101add9a961011a256cd293efee8ab21426444bb352d23ac56cd267cfc94afa608cb1565644dcb75d3aac9848f8ff2d514b9018a32b205a4d3aa09f4f14dbe9a223a43f5d6b486e24bfc9a223c5a3597f0f1f157d30a7a7a6b5a413f280f0dd2aa7ad3ab0d2624455cf510f5417b7c531e2ae154e2841f034f9256f5121c4419f346c2e8e93387b40a4f23f3a755536565afe2cc64e960fad860a5955c2a99b2bbfb28bba7b4aabf7ad688fdb1494e9e6b2dff848565e2f492792365682f09a3bd8796bc77132b651209331153ed20780df7c0ac06fbc61bf29478b10689bc226fc94bcaf0aa1061f415bedd7b79304f28beb4df25a20ccfdb2f13df6e7d48195eb63b124667eb8385dbb0c1adc7ebb5f019ddc2397a6958230d36c9e49c2927ca5cb9ad5831bd6e4b9b724c2388aa7a8ae121d6c1436d07efbc8bcdd50ed9ba74c89f8bc793f5802af54aeae4175974169f4eee5e50c9e5021d6cb0416b53b49f92abe422bdf80b8c87305e3e17305e6e70172f0ee305e6e505c6512f30300e83060c8c1ca25e5e5cdce02e5e5e5e5ebc4be3859e5050a8d55c29594b5272719bf6b3dd20aa531fdd1b9466140da55e9017e4bd00e0a10704002f08009f07646df0ef85a6506bad4d714b5368a4509a425f68cadb3f17171717eb35cb9c83e3e09cec52da4f4afb399d5c6a3f36db60c75e4d3a8955dc445697dc67aa356c702e5e6972958fbe07d4abd8f282bccfbabc3458b8e963f1e233b5e9b3c1f4598fa6de72a8bd0eaf95fcd2e18b3115e38b3135bd1639e09053a70f05fc427ac31782a174f1859a351265a2bc40c8a71388e87da1bf378447bdf4edf82c5327248cd8b7c7c243bbd35d014b29a5f47cbe97c32c0d6948c392dc1135d8a552a9e4793b6a83a5ac35586af1c92f612fa9a8788a8a4a8aef78cd53547cc79f5cc597b07c3cf27afd11272831a5e1db4d3e1e791139e1c7875dec88137c1c0972fdcbc723413bcfc5ce0a38ec62271e4e9c0d14550126a413c261e7f14709a26fef888aba25d3ebf4dae315f003078e518040e8f3c72840d09241eba5d7d8fa964e62cf5b2e0d132a67b44dd2aa149c5b38a77d36498bcffecc2d1831d2c55270ce5c549ebabc37543e73ce452a3629acc1cf733103382209f2f3d8bb5883f244760dae78dd92cb6a150a0acae95158769c34882d912dea62d8cb26d9c6222d798e695734713495943caf0d7fec9ced4c3e8cddc43dc7b7003b1e63f7eecd77711acc3c97d2fbba33c4975e125fba185157e479f9b2a880ab875d8c86f69a02965efaa1c192cf989a16f4ed285d4c0a58e523128650f45b0c4cc94ba592cbd87adc1575aa63b21cc2c08033873e71268e97c399f3ed5f0c8659adead88f251f7237df6e8df40a27c121b45e572557e965837c97604270b825e724567111bd94a184d1f030ec46ea4b382417373f529206793458fae29b721a0c32f403e6830b93e0f1d4bd8f935865cae99429a7af1032938048afae0a6cd3e590df7555601be9a715c7ce9dbecef10a0a9bf8e94361ece09f7c059f48278fb8fd4472d4c964c54d482b39449d4a3cbaa225d8c4a57318c3b0501470886128893f9474f0b19b84289969c0c7b908192bf168d5e728ec217613af1f86d130c9210a671af04b384f3fc4b09e95544d5e9fe2dd069ba9ce5e1f2691f38261c759f39a040f22640ea9ab38373b2f085b9e6c7f34987383905efdde1bd5a5f45abde6120f286059bd14571b2ca6da4ba52d6eb092774198046bb03d18e7e17b238a10dcfcf43a338a269770841aec39738947834d0aaa61835bb7af156049f46117537959202a70761bb97b02f67c36115f7ae23801875d2c7fd62589decb4a9ffcd80e7e8d7d36b0f45726be3aed559c81a19d755250af62f789f4ead0c6621d51ab5692b4ca45023a399e725697d5290ef02912df2dd0158b767e5ae71bc5e957445604122f2b8ac415e5a3cf45293bc64a32017acd431bdb841aec9a2386f92ef679b3e62aced01c6a6029cbfc2679838513c7b4f98a9d6f67a6dabf9fb98beda478d33c05aed9063b76e96117e3e464718ffed6c5c2930f274e48bf0d96d2c580a87f21ccb3f0602ccee1d0a97cf44b38bd8a33abd88ade50afe2ccc9871ecc2beaf660edc1fa8910c61bd590c3933f71ee9a76f0897b79f26810480cdf809831328ef47892c3d6d1c050027a06e6b9fc4e7cc63c29b9f7dd6b274e17b379f298399afb482531d2c4e1d14981a7cec499ab0db6c1700a107f3658cc33bd4c2f6c6bf5bc52a9542a799e2db18c264e47d429540cf76278d704fb436a59e6792409eb10bcc4308961d2a518e40ac364eac73557315ceee0d265ac2551945171e9320c5186871e64c9655c82a35725d74cb526fd01518607e4106822868832262ef3cbf39173de6779ec0fcfdb603606fbf6926bae4c39da8fa9e45a715c729d5cf3804a3e2f0dbbe379a5d2677f347882a2834b930d6672148d741b136cc8463af6f81b2cca68b1f8c284d85c21a1fd4819d665ca89391f6a1d129db75bd7f5be16c527ede64571937e15158d87af07964b737215af41cba6a16c829d8052be18304a8089e13bece0726687183bf0e03ac4d8c17b26eac0c26f8d1d5d43c5b51d7c872f461dc2143fb95f1acd55fccb2d2ecd0e2242f81428a48b8bee622a2a5d4ca58ba97431952ea6d2c554ba984a0e55fce4a1f6e0a5d13cc5539766479157f1942f8c507c4aeb6d544a6e1a2a9db229e8868a6b0e5e1a2a39949f924d43d90443f964eca4be49964b904a08a0c3d733a6214dba29a8c106db5d2e0df692bbb8344ad93484b309160353d219213fdc60dfda4852a939d55037063081d6791bd390a953020e4daf6ec9375157d44981435317fb76d3d002e6aa25a6648c972f5e6f7abdde8405a3650fb4c065e9341ee10c3cffe2db497f842543be5d237dd851ea95da506fa7a5d860acad267ac4196310b3ef0d1a414cbbca987d6f507b6f744b19190fa61fb2c874263bd7a04cd428437a8c5e8f80861d245248c3b7bbf8880426f4f4a564d58ab91c3b62d486d8b63366e95381c7524a1d92e6a0ad1ae02a5b2e8734d88a3936b88d281dc5a29e2e87b4629cb2256dabc12132b3590e69499f2c5b1f0e9f21adb2de9a755b334af2a8611c8ab9020dd2dcb970488756440031532bfaf351fe6850f27c2fd8504c892402626ad3e7c7e747126124a853ed43423e34d20161cb479bead2a59090e746a5c900b589596619a4c1a121223ad59e041c5f355b1583703f9e7e464821f38bad267ab04a435828b660831ad534cd7ad8752feaf42c832f4e5e34885b7e3ef8edf1c14f020a7e47c8f2f2e3cd8cec1e167492b7a516995d37270b4fd6cf689312f832e1c3866d47f8b05b5cebc3de21e5e0c3666d3c1f4a58f7310a960ffb310a96131fca9d12d08f51b044f06184651fa360e17c187db278b4c7956cc594f49822981838747a6157c4f8740241983066b6e6c728584c7c285b4b740efd4d4ae0fa310a16eb43c96a9610b89b27a273c06756ab0f0920e090c6e20542091f21324883fdc5d827842908a98e04c98f8f429042cfc3f8280429b0664b099c398d3548748325abde13c82e09ee8e802d121e66c021058a459ac515ddc1c389a3439c3a3c783877627838797a5045ef565cd11c3cec9f8ffdfaf8c2c3867d1cfa08c3c36e565ccd160f1ba785ebb878d83b61f384ddf36074d98aabd913ca9f8f31463924631f65515c754a94391fa5cec71857449f1157ed797ce118874c11747a04898a1ee9811e89418ff0408fec408fe8408fc0507ae4080c7a0400f4c80b3df2821ec9811ec1811e49e558922337606cc90d31221731252e31a09658508b98508ed920028f988a46be1cc60cf11cc20861914330880d39f41e3115bf864c041f2cb387083f50b327968408372b624b566244a798129518504a2c0825261453271b4678c454346292c39821a61cc20829e5100c8273e83dbc1c6a3e622a3e29f7fc88a9f837f7d8184292988a466eba1cc62cd97208431453a0122e870ea4e5500b8aa9f8231b43108aa9f8590e4f3f5b01dcec6eeb43968f5fd87d5b7fedd2800dcedc5ded0809f313e607206a86c081c44d8e0f1e21ad24adea9ffddd3cb1b5bf68d5bc34d63b87f9a977b27378b2cd8bb5af3fc208a9378d069efa8b9c20fa690677bee5bd813f2b42becdfc74693cab76de20c006a76b39a42d3feb11f2cfd0ac7a6fe017a2dd16c18dba1c168bc5c44ccc3cfb6c96cd2c17c1e2e9edb44f7ef6bd68703a4b8373fa6d010e5ba884bd8db4b369adf59e320bb56ba346e6ed01782dab355ba05fb48032767978dd9c27a7bdd23a6739812d1a3db1f89cf364021d869941e0624be8f54c02122acda6d55c30168e6b52d84c75758ec2e6aaeaccaa033b9d30a653088755671b8d70e501c2c3e298b27093dce29f7b0b778e73f9751ce72d92005d277d61059ee45c46d39132aae437872821b073a852be81736c90bbd24921c9577432cbeb9c775d57ca37bc4ceab88f73d31792fca4c5bd16e772e727e732790d209ff33c5e03c86ff1197dbff0ba974f1eaf102dce659b16f7b24d0be77284b7257bd7826b91b91c7eee2b2cdc84859bb03061b172bd23b9c6b15871ed067173b8e239e69b849233c936f80af7d9d0755cd7755cc771d2243dc5a52cc92facc04b1792bbb8b8ecdcc5e4f2964aa552e9725388c9b5fc48de75a41c3b4779a9647269f2bcb0e35cdeabe3805c9e2bc5eea4a0a07c3bdeeef8d0579dc82e9f9c64784e9e5645795900e4db2b9028136b0e00a45382cb68e5e29ad6591e9706c805f436ea91e6ad396acb10c847d0b7c15fef10b0b9aa3e42d8960087db4fd7795f1260adbb3792c5ea9678ee76f5f31cda1b91033fbd521a2109115c424cb57d820acf240f89a3499d99ca91399e1265b8acd2e96235179506322a0fc77da3a71f102ba5ed1d9ed6d19e7e4072e374566b465dcb624543435aa5be01719171a5ce659d51ae52ef461df7cd17cce5cabaee52bf55ebec8ee7c2d573f1b8f864eb061f9f8e1b695b966136f494f0ed97746950275d1bfdd5394eb31f1017d567662e6e00c2d29591aae7459918a377388607ac46b03bb57e75f474563a29ceaa7dfa6d47884988a97617db14f547cad87e240c2354237cbbf5eae2cbbed6e4886eb6d2998da6cf951598baa6ac3f93b690ee99b4ca496bb356a0ba18654af529dd7e286d1927ada49c178e2b1601217a4c9c5babb2914fe4509366db5c41d54029a59e69239fd15209b873ae45c77d406eb837b870cee59333377cf3a7d59a2f57832e972fce38311ad59f6c87c577df14925c9eb01f12197ea4e0b415d0c8ae8f7b82ead6fa4acda4a2e9f2912ab5928665b9346abe27c0d2b91a58b87c976767c7a7a747e594e2f2753336541b587c1c0e6ba658532727274565d2a0d6b92c7d602bb36dd37c9be15cf8e6e2e6b8c4ba91e6db966999a58e32698bcee51bb5742e9f36a3514d9ab4882e9aa4697973f9b29916f736b16d2e9f9db19f8f9e9835e8aae0f51d7568df4e5c3c97cb5767b80f888bdfdcad9a81b878bc40daa77dda3553391325052928d8546e8dab83c3ca13654e27939a14d991733236e7120acc39f7a273f9e6ccfde83d41e6a391cdae094c9786cda173f97a469b7e9dc0d26734ed99a44c2a32418a44aace52830d539ee6ca2573e1d2e59bb1a199725164a56c4aa994b2e7ccdb8f0d2cba132e6d3f714b425cb9f07617b9feb878b855e1c2c36d0a179ab6d3a367adf196a0ced924c0218dfd4819d50872456332e5e2540c5f902b97ecb9cae0b9ee083c97cc267ee342ab76eb6ec6792e9354c2a6d257dfe4037283cb37b7a0ce6eb3d24927901b5c73f12d3183c5ba21d39c99a2383385b33353ad737374bebdba8284a2bbde2469296f3f9292e81cd1cc670bdff91528a8c1ed3573457079fc6cae1b02af665a2d7dfd5a9cb93c925c104ccfba16978bc35e662a69d46e9d0b0ca6a3537966e591d2e5dbbaae3cb058fd5aeba85ab7d746ff8ef6966e9e5d1ad6330e1f214e11c1bc252182eda56d0911dcaa88a9768faf0dc8d2d8cc54b36fca82645e7978a810da13b34d08165f92b8a4004485206de2d1a5b123397dbb369af4cddfa673d7467fe69cd6799f11397014227b2eca23d4ed0a5fc87266fa8ed03ef3d277842665d09884d155e7772e0f2cb97c3f802dcd341a2b7df15b4619bb33398a65f5ecc83b0f4f2fb8945af18762adedf07792d90e7f269ad6e1cf34da46f82b6d1f7e8ee3bccfb9eb3adae18ff4f58b3f495c0d02096d41b75efcdd6c449a244ac25ff7dbcbdb5e9be7d10e7fdc3b9678a4cd0c638c3bfc6d2fbfede7db8a106124e1dba4882fed4788607b37b2a59a956669f4391b99b669a2a65192cdb50135d8dabb6604198504b7cd6582bfec4ff067bf0271e1f403e2e2c2858b8b8b8c0333e392b79f930e7ff5b30f888bd30f880bdfba91f6cd0fc807e4860378c6c57160665c78caf6a5e0cdf5f8a39fb71fdae16fbef71d8125a644ad7d4070f00cf701c1819971718f12a168915679b2ada84122a058e5992bcfa5e5cfbeea436dae3ef1454619d7c8696f1f569fea5329a772696c39629ab77ab27269a811daa22dbcfd1c819f3af71d817f7af71d813f1b423d80a587d4a76ea86689b24975c5a5799b47d9a4db8a7b9bd836140aeac40402a4ead257741277bee2d2b8be7235bf9fdde13427f9f685dd6f9ac66d5609ac692ef16d028ffc665309f048dedb84bc5c8e78e4df2dc08e1fdd1b23b7c1a3ef88bed152ca887d85a02c96bdb7896de33e790465b11a9cefb91c79cf51de7ee24b4bf9db77f2de0fb7a2e3be38b23f5ad56901e83a002edecd684e4375d5a76543c341c4e623dfbe1c7e2b10db7e1a3ccd2edf6a77deb79fd1f6b3fd64f9e707888b6f3fad8a2edb8f8eed0709ce458aca295252759a1a3b5adc20eb5481ce14a20c0a93305a7e1c580020b6d4e8b3cfde20d620824022682b1274106152867dc114be1d07b73d3f73a003c8e1cee05ebcb8346af6f4b33bd6ee642fea0f2f6e8c7d69c24efdfa0171f1e96277ecce4f0d02e3d6a83c3dadaa1b759d6a4f0d52bdf234d834d634a6830e97463dbd943548831df33aa4b6aa0f48352de74b83031368c0042dc6044e6f0300182c30ee0906a6777edcdb04d63eb4f687e5d9c1eeecc0c336d278a834d3c5ad0176da48dbb84ebb1ac9c35ac9a4999ca0682d7920658e5b956de6c15ab44482135c6c09fd09e9b1690f198833f2ab915c8e317581d15397f786c67d5f8d64b1728cf106414301fa85251c7fc76fdde87ae70ab81de7521b7911320d7fc339bee9b6ead6d61ad54fcfc367ce7159d765d56d8f7aeb6eee92b47c447f56efbd313bbb3548b7467539c36559963113428dd306e52937b75187124f71a5796bb9a5532d73b768f2b452ba3493c601dfd3599ff3ba1865b6516c0660c7735146ef782eebb2cf52afb37d529fd333ea3728cb08eb46c0909ee394475df6dd909ba5d1f174bb37dabd6f103bb85b837a7644bfb533baf30e2be7149e17001a80e99b4669adb5564dd3b46d661e663ffbe7fc49a38ccce71c8d628b37cb69d6794284f87224be20b9b7094c3f9446de48e5b28b23379fd1f4b7ea7466ac51cde58f5a29a5aed92051343d9bd9bde1bf002dbeb40cdb2f46adb39580258d039e52af0741404b4c80c110f42d3e4201861e3c51ce7f1fa1008392f788823edc60a109e8559228c9d4c287dde3b082723e1e096221e1e391270cf9ed0815b18f47a88031e1e3112a9af8cd84f5ed4848960e0ee0d01a610287b4f5f1dee09ad6901e3a2c1c2200388cc3f01707fdf39c188f2549e267b7c159f8f5ce49cef9e62bfcd58a1f3f7efcf8f157fce42657f1af2982e2268e3d7eb803474a5923cce3959a47a7d43394cc35a69f78749818a157534e4bab4aae064b409d8aeecd2154600c9b2bed65336b03ef797983713fc0a69cb9d25e34d20740c0f20738dc5caf0e8739ea7b2eb3bda1acd0f40ac273fb6ab06e30d3abe64d681bc226a056791988124f488fe12dafa099825a85ba51653ef97083d99d5645fbc3bb343c19e2781b53ced4348c4d39259729a7e432e584a61c3cd42a1bebd54b25cf732f873870bc75d9fa7083411106a1696da8799e87a0e7218c97a94b2c64e379b59e57dad1d01a2cb9626260604030740f4f21ce365e0a513c64ad6baeb034e5bc0087db06d37ee66af433727d3b7e6db04d681b6ad0c63c180ca664488912424a3ca0180cf67abd866041b4a3b693b7864850bfb25b4324e855298e4b560e4e7c42908f440082f32c3e1e79028f17ea546f4abedd731e44507243042d2c79194f60070a01167b180c79eea5a3f501e0e5175e90912577091c7631160b0727a7470f2238af1a222f95c45ef572b843d2d891790087da6b532265c86fc7c101a6051c6a2fe91b6c7a360bf8c43d0f37180d2f9fc05a15e3091a700d11184cc99012258494c4622f1d84c11a8cff72f17a0dc18284940c05bd5c6e0d911711a0a016af06a30aa747abbe86484e8335447adc0d608f87976b88f4904782602f3dc75b4304a7c11a22f139165e4384d5aaf8354472707a155996699a9689c069ad5ce98431d53a5a042eca86b1b8d3349693fc5e7449beeba6e1a753874fea0728a52794525a6bad95525b6b96d96a29a5543a95814798b731465a6a40839afbc989bb34b216f085f518a39432d2c8b22c8bf5a45356cbb2381a699966a5e6d6718c2ddd5e41d33c6a3ea3b32e9356669bccdbb3ea599fe00635f969da48d3368edb469ba669da6834d2685b2148ac3558718355caace4d5b6d464cbaedd1577aa66d6eed0b4cc66147feda6b66d7b2dbad9791c88889610fa171f7f96f0f9ecd239b598c40295512a7929259d734e4aa5943136edd951c66c83a91431cb222d519a65d45a5b279d4b7897886870f3303b39b18c46a35137f56e4ae99c5a6b54b3b48a61646d585b2b8ba914db5a3bd2346d34b2d666a34ccb6cad2fe8087bd9735a3beb06ec6c6b6de6d6adb5f2ebd795762d62a7f5f65a67694eebb5d66aed9cb39b6619adb467d357466d4ba9699c66b32fbea6f98c6e3541f49de63de3a4d4da2ca394ce69eba4d3470d11d88b72b2144e2d736ebb34b293134bab6474cdb99194a34ff32cd3b45a2bcd2cad96f5b38124dff2f187a5f3db277f8549051875837ab6d93cf33003e97c7acc759463abb66d8bb49479db2cf3e7a2bc231ffab9e5cc6b88bca24639ad868892211a366636dbe08a048e8b57c0d1b96e1c296d12eac677b6800e6b89922141413f4a2cf9364312ebd3fa8cf446196de4d169bdd1590568c0af289b5135c3c468f046ea61ae8d7e99bd79bc0087dda3757ab409aa0f4a2bfd018898d829230dca9e545252b8e363a6c7bc68100818559da26a9ebe93f9808b2d615f2d37a76524d2d7cf75d1a3fc4269839e8f48f88824c86b74bffefb04cfe31ff9c8efc82fc92fc94723d770467e351f917cf48262e96ab081857fd9094cefcd2cb7468d2c728b161717e1ecaac0a1fc111d5dcf9c741d7ccd4734a3aee68d50d43327e52e4451964b435e1a37c7e4906fe3d2b5b8742e6e005338e490830d35b0b060bc6265e5dea00d6aa90ebc37bc1f3997ea70b834a41f3997838c4dc23e9f6b19d32a9c471e35cfdc575c1a38a334cdb59287d8350d6b1f4ad31c7c9363cd94833ea769ae951c3ba77dd57109676e3af1d2493629693ea363763c76e9a5929b429486ba34505aa601df7aed469a5fa7f17ef45c0e1d0e39742f5eba0b001817a6bb3ae04f9bcfbd7497527fd14247a3114eb9346ed8467ece0e092323ad1a65f9d3516e10d747a38c22b59156a1463f9d24cadc1bd7491935ca34e08f7c341ad2aae9dd6ad5bc3e25c9479a8fae36d24179ea9dcb53e79c83e960c0743aecc0438c1e54258fe4239fdedde939491b8d48238d7449238f7477b837bcf79ce3a18b716948ef39d703a76ad9246fd5dd7e2ea61b7d24ce07ed9b4fba3450a3d157fd7ede17a3803347913ccb480efe752fa3483246835267767d8b907f5f80337cd22a1bec9a4fef6e94198dbce478e4a64f3ee90aa1f948f391738d6934133e423ece369a8fbcf4859a7bde5d1a928f9cbb37b4ebd7b95803472436d8f968858f489ef0794ea68bad4275f1457afcf0bebc315f6b5066d77cbe7cda7595d4daa84938871a782d8f3e3a6da5dcc4ed5d27ddac759fcecfa1ffc24e6b708749b64cc1c596d0c2be3a976530eb5633612fd99c39768e56dbc928b312008bc3a534d17aea2a2b1d6d950294043dfd4092a76e35f73cbf2a7e5554fca4b9947e72039c8f483e22b141eb5b74ceb9689173b8c29d050b9696951515475d1a2a27575139a154541c7c163fa9b0e4a0bf12836241b17cf257748c7f5a5959f9627c09577c3aea0a417d251f8143276594e15edcc88242a5789edb954b23adf57bc353f1ee95cd03e5d6e857c9f1a4e2d6636254aedfeb9d9ffce4d23865d4cd34e05fbf39367857f8c9b9fb8dfcf4ade8a2803b3f79e7b1c1eee4e0a37c458e0dde8c3a651af051a32f077d9598f8225532ea08fa2a5e84f72a7eafab7c31be842a425057c947c847f11b6e0d1437712ed5d99667adcf48999b64995f19e090027d963914e4676e02445fd4ada7a4e430c557384dd2aaf0baab5536d73dcf9cfe48cd3dcd3db7d73df77866c6c1903187f2643c45c8f7fcbe0077e1c957dce6e42a5106e599f70ab87b7dc54fa84f3ebe42a8f855f1eb1c0f9dccbeeb5c0fdd5f8f716ba0780ed9c9286303c54d36d489c9fde29fbeebf4d2a8f875d417bfde22e49ffc7e47c83fa1e47045ccae0d9b93e790afe22bf29d010e3be7333fe5951c9e7ab44a25db9c5cc5333731dd1b27955cc306c5ef0cd27079b2ccfb47169e38e799cd894bcfdc746934975eba374eb8d99d8cb392ac6b366b9786b5de4519cfad4b0d6f51defa8d2b099ba99b71c05c2098f3b822d3b4eb1a4a4899f8cc4dbe1cf229f58c47abe267948998ca1c049fb929879a093e5bc1678e33bd892f5926e550fbdee172fba8eee3c1486115f635e665e69b8fb24c037e8cc79f6599a665ad661b7b92df92d85bd7fc79615bdf1eb64cefe2caf26833c69cf4e79b4aa7326679e64a0e75aa5d8b7da861d68907f6a1b6810fb597b9795c19e0688410b89ac7b7d321ad8ad47f08d5b12e2a042e46992264ec51ed1ef7a255917a485134d3800f3e0501d15fea33a33ad3804f3d26f1d2b3cc26731a90fab4092d80a2b1b9086923bdc11b84f4ce2f766872f2e0628d204c28f9d2c72898882612e0f6e8ed474820dc4193a5d4e16200a89374dedf7a104ba7a6cb302287e61d921ee07c9639750d3c89e45c7f47685fc44403ebab2fc0882f870cedd7a1f5cc7a9865994d36bd00f50af1bdcd20d64ed85f3b9d30b69aa6f99491bafc0afee55a469ea1dab3160fb03c122037dff211890c602f035754c010ea36739606a71023b7c9324b08fec86791bd35eaa5716380437a7fe050037fd53ee72469ded2aacc2baa7d737faf2dd3bbec9685c442ca2d30c0a81a35e46b19d5aeb9fc7efaf2ed15d2a6e39328d3514a354a297549a097be51243c189a21cb4f9f487800fb39b5b9c2f4b3a99e434e7f417d3a4c8c28a3f9f41ea28cf522bccc479f2604754bdd526a394c7fb695b192ba2f9c78759de73cea9ca6c5e85c6cefb25a7ba6baa91336b0eca1d3aaea6d3bce4b9fc9b38f73932f73c99aab92ed3ec075d9cee1c9de6be98f36699a33eb35c936d32f7542ca38f13afdba451142432965ce67572ddbcc0ee266ea84e5bc65ab55248f73b4ddc8da6dbe2615602ebbd7c689777742f2534a46c95c78e2d99abe6ba3ba8dcda1dcaa372b9bb4e6d0023ba813d909297b393ce5511efdf4ecdacdd68184b53f3eac3773da83faf83087bd33d7ccb930470ea9ce739ea3bf9d5ab7d6723976669bf6ebd6c356f296d5426f6d0e73681ac63937aeb7cfe81c35bbdf53e372f51e1246667293cfc4339f2bd9a30d3153d4a3a95943cc956cdd7c0a4f35778eb06dc324efcd71b6a9ee797b0e658ba6bab76d19457213cf64cee98431ac3d0b4b26d9a4029c79e7d6c35eaf10dd6f2e733ca71e75d3171bf4727b49c2e886bbd32987392a75da39bd34ba4a227df16b7542cac8720efa9e87393a732fdbd43047fded07c7d58c99a231aaee9643f959ada496da502bf976bcc19c9032e437a53aa3cfbf9606a9df0ffc5eac60e8740d94a798921e5d9b5daba82631fd5a1bf63587994faf3b38e7260a88f8d66dc4b7d9b483ebf785feed5937fb8b7fbb2cebb2cce76acbf4f4daacf189b631bff6eadcec1c14e9e2075512fdf4ee495170820fbc8c2d1f18f2d6671420e879998f51f0207a8e76b4c6582bad95de17e02893797ba55e84f742589f517c20c85b9fd38b90f3c50b23a6c7f8123d44459b5131d3801f3d66b0c118da2e0a78baf4f8a1a4c7e933d3805f33f8f473bc882f323a97c9d89d5592a582177b7cfca1ee5e45197248c268cfdd924352868c93cea638ed02fc336d9e722872dd5130934eb54e19a104f2e333a7746a8e73a8699a111e5b97c3e6b2ae03fd271df99c99383f9dd3bac965d98a2fd3af0bb0754ed6d8a4708711ad2984a3ab5fe83dcd7248c6e9498d0a4d73a9b9746e4e23adca7e2e8d0ce286ac5a57fbad75695d663c56baa887584ab7d2a7de1b7293526e595e9aa965d67ab55e7570644ccf7e0290b9e65c9431a3e800cf57b72553a9e42d2c26b7866c7d96c3eeb3cc7d374f84d5db065f1a69addf1b75e4d56dad39cc5cf6cf98dda905e607206a86c081c44d8e0f9e1394be1d4e37d252bad82a1babb2d13c0b2f8dec808fbc414ccfb42b47dd0d9b299d327625dd2c70b1259448ce80e40c3d01ad7ea8f46c35b64d2d1d9a11240020005314000028140c878482e1783ca2c98a750f14000e8baa5478521ac84110c3103208214304040000000000329a2409ea4945a1d20af66fb753dd87c868d9582d74e995b8201d98e32089015d2822a1704985e0cad3819098fb251646c7521db3aaea959c252564232bd6fff703d9f9a9d87eae7db4c781af93e8bb04f272c570e08feb1c0b386726f611fceaa641a6f12dc245fe566910d5a508cb04b6a3410a209f208ee4cc4a894fc8aaee752a2e25054d56e6deca9029e8abc7608a993b74f69e9f4d3b832bbecc802d6abbcf4344b3eb19b06d24f48ce47a790a62fb7c1ab7123539687163538d57c68157abad89bf1b450af57574192adb4f16eaceb09a20081a6ec2719e101b35ffffb49fcab985dda1ef17a6ae996fea730cdcfea9e54e84654d04c9c4c191642a5d6b145df14dd50655358cc37531d467d9d28a064f5b110c662bddcb4c0d961e6088ec420a2a98b65afe932db1b4be73abc9d73f5e35743b536fa4d9280edbc7fac0d71dbf6950d6a2d114ebed720a066d1153673d68b240476fa3854276095f521df21f5271d47ddf1e5c04d9c0f1ff7ac582772edd67b3305eedaffac492868b2ca424524069cd6a63b88d4a6521dfead1040b67d946d0bb32f87b8b9cd9708be896c228af0c2bdc97e8da326cf7361f185a868f2c2ad362df743d77dd966c36f336b7804c43d4f183960c6b8f605bb33b842bf183e821549b0734bb888dcb4474fb6e7d99681d015468a26f01b9e73a20040256c430819f32064a6389c844a4008f4b93daa833c5ba22042152934f03557001b4eb610e7f0709a8dbbad4f43431679de29d8881334604908847bb814a93ea32f6fdbb3ad458d19d16b8de8ccfaf8f4b6d7cf02e04bd437276bd99dc61d3dbff534dce00a724b850554f4bca0f5525cd00718e807bb493d57dd6d28e19def6ed4dcfd3d639bc2131d4767bf1e9a69602aa07209d976e38e7cd09bdd06218c7a82295d2530eb56481de063e7f39c39273de5d40895ee5b4b25560e12afd1b3390a09d55217d8b4f4e2bcc55babdfdcc66666e58543fc6270e6ec32f59e7629b2978690e4331014d512486ca7bcbcd6e252b202e76bb7a74094419e58ce83b13af6204330840a485228bd20305f192110924f67c5d2d9132b4a6c484e1eb94dce20b4b270af854b9c83e32dd21e867a0dbfc89031070d96f0112de0574990a7c24c92da89960e12a5560ef45e02f1b063e532a686b41fd802dedca43dee607abb2818382d31d13e37a9a9c8346101cc86aff76dfa33b89cf6fdf4bec049de5c8932c554369188cdc42ac4decc4b506ca8134003483673096b84c40b11864a88416b016e169d66da4babc915c7f5a5dc6f60c00367729381a119c317c45df837b9a720226ab82a7c4f837a0878dceb3ed94ad4f562598bd0c67d084f0c9b0469638d647650472e8f6e9e0561bb24bbd5723ce2d714b17842fa52f839c2220c07a3cca2789867fb45cb3113bf3e27aecf0623e1ad17245a765e4dbc2cb2be98d76fd8b6a4aa9c9cd40d640529f1013133b62abd8f02a36d09a74bc03e1701f886d1f423798fcb982bda2aaffc03309720cacbf142d368b2f083d7afcca0777b91d577a1a18ce61d5175c180a1591a68535a55b844f0e1344ea5a1e9c5cdefec891bc60ddbd48a4d40820c1f98501fe3daa5122d74c42e0321a01dd214d35238b8560a9470278b52d041ec32172491e43ab01a777e814f9062d5e2f17ca7748b05622e39fbd06887bef50efaf2eb2f1a117e56f6d22931e7091bc875b4a18f5a2ba0fa381dd733e06dc19f0add09fb2f33c392527d04df4d19868305a7c260d1949a654759ccfeffb98418ea8b731c93779f0633764a0a9ccc568e6665cb923e07a25fe2429f1cdf29c66506a3223913b32e4f9ce20fda88e2dd5acb5d0b5f35117298404c31e2802a2e8a40c1ab7caca18fc825c2f9994e2baf848b12aa5b23278e3594c31a1f1991e860c132bd1c31e56d3027f9561e7d4dbac443b4f5a52ffe7e98525c20291c3e3857d0bb973240b8f530421b0149b144a847c9068a1849796be62f74483b731192d8df7f8ede7407f68c53b8398f1178528201207b3604c871d77f562269d7569356ddf5bd20866b2237f972611b0a3d00f21469684fdfa39b34986f26ecf58d2379a9dadb4d5cbd02ee6cdd129bc87148dda4c89c6d500694a95178e99d1b7f63cc1ff0858b63b5d75a765c3c185488e289a67a9c79d511d85071c82ab7fd2ba3fb968655f9fedb386d34f7e58fe250ad3e5d5b22149f1a94458b653ead46afd70b307a099817fa4e68bb15a8b7d0606b8a66d1c579d3f6514dac4eef0b5ebbdf11de9532b978e922d0bfd987b3648a08bb6e84d2c2a9af9d9a2eb8c634c1ad41f791d2da9e0500f77cb775a77ae7afe49fdb739876fcb40ad232019d73677511533c236756550d209ecc10888e215e7f8c3a88ef26d856d574fffcb515803b1b4118b354c07a066f8e208a65cf226cac827d623efabc497689424fc45a2cb771856bc22c151d944b7e5958ca157fde0c788fdab32a94b1c579e57a89928eef9e46de536511edf95415a1cda122503b39c3d42614cde5a1ce47fc85ad365c4ee4bfa47291bfcf9dd99e6ed81935faea1376b45da63b0cce998dc25aa74590616ee9496f3778f9dc96f6a3e73d24c5e690faa0031f1fe267036e2b3524c6427fc4e8f25392bde43bf4c384e03c43354fa890dd53f0c68cb782dcbb30b55bbcb8f374d3cb17da22df486d97db554725cac12a5b99aca312784484ce0d2f0210808459e874c5667ff454e45f078ffb4acd4333097c958ffa6874becbea38807184652c6af360fd0c8833c502c6c3238b776dc874897d4cd1a04321d3c489ce2199037a0f5b62606b412f457b2be33299ae47e3bc76674360c55d092e304360746468f072d4ef30d5565339d7eee4109a70fec57877099223c1559a3ddaf657445aba420fe2c79d58911ac871cc404b0a7d1d23b0d7cd5df824baf903daf4828bd9c8fae4458c2f90f405ed9ece015e673d1c999d8a8195cd40793f781510e664185f4c1a8672b816e4dcf87542d462a6b5ceb3576082a894eeac064f6124d943da603adf94976a5a173c5d3375da6df8cf4744d7d74c47e7454610b0a8c5b45e7bf6f4c2f3fa40e3fe84a95390cfaffc1092d0ec1000a50e16f9012fbac3bd979138e604cab826aa5c23abcf3cd3b887900018228d3440759b465e606e8c5872651c31e87ec611c0e477a62f9510d7aad3f88f427f2678be76ef2d08451e03b8a7cd4a99a10135d789d4bb1f1574c5e01d6f1ad10058e7ce45f877fda81fdce01971ad08350d240b7d715d4e4dd46733e2eb3f7616559c09c907a2aee9796281ee0ca81ec2992a533e1e8a7b496266682ccb8710637a0990f647a7df49864d9ab288ed63c024409f34264f9e6c4b7ac1e174cd7d85588ce16597c474edd000b13e20722fe0d339643b6134cd46f6903eac63fa8b8d672ee21786b240d2b5b8cea0d79400d0aaaec934a147fbd039c75ed766e34c2259d4cca91c96ad2becf9c0f463fa95e2fb5a7b51b719fc63cae799e86cd70cbf6016ecc271f2ead29eef6e69813c61acccceae70188ae74db2ce23a19e4138090959fe5bae9d4be671eed5d8df76effa285d1c7f642cbc2774d815c8b3ab7123605e42397aa754e846ef2444606c0c9d34161b0a8366dde5cc48a6e75327cd2336d21e00151b38b9211f7cee57302ac310b23bcac2b35c5b6040d90124211f2cea78d40608cff48c9b32726507817661a6079421cfd89efa699906395135d33f85d19659a3197211c220d2bbf87ba80f6ca683dd47b0124efa7dc0f86fd76968a1d4498c9121f5e7ebe053bfc94ee0dfd050df60ac1315372e38234bee398b4097a1b57438e9e65a67000456930762b0c539a72ecfb43454255724048e17de46b1461dbb46537e0b361c9a7d34777051c730634bc054d06508af1a054d52c3d45e560062a4c300df0022257a82b9c61c266a631740b458b2959c3c4571798e338a46cf03f5d5185fe7e1f66b20f1ab12caeed231b02c83911410ee659043e78b1804c5f7c176b0a72abb81377d443201717f7090f12131bbe568e49b663f2bb9c962cacba00990c66dad60858a4335bb663b1c54dfae66eeede0ffdbbdb9f9327e9242beef1dee9b8f7b99cb476afa6223847b502db45cb5416b377675ea907badaedc032d0790f049c05bbabd116e4e434545e119ceb212389a0794a5be6a46c3d2eefcf9e2d5148b9f98dab77f329fa645afb232c9214450838c96dce24402bbd64f5bfe267e74db38cc88e26bc0b13c97b87f79b270930001a7fe3af4854654110dd4aa3dbe00bf1315a865c5e3fab85ce94a038bd125000e7de461509c8aa6f4c4f3a3f7a5f132b4a1641dc70f936d4675ee08510399d071931008954592fad892aca1651bd6ad8db16a64e54dbc24bc882a5bb5d4bf1261cc70d3165af4164cb6421c329c0b5cbd2633b8d33a6a88efc7d4fe1e69ecbd4974387c490bcd389a17a6d302b882f07af99c95cf78972465abe3d91957c5fe9e394b8a80b4e84a23bb27adbc20eb0737ef7bb2ae05618d75260d7c130f0b5b0d4a5e8811dcc5c61b2320f75b91f802bd2ef5c66f726f0f9d97c3e902bcff993ba23f663e07c2371d7f16986b65ddc7a72f5c09e04e04fb571406265f106fc59f59ecb26459bdbdec918e57809b137b10a3920280c94b0c4da433b738410812c444e432226e93de543526ef2911e800ed7e487bfc26fe5235ac707c2f9515ce082ad5622fc118ceb8e2227d920ec4666122f10a6e89beed4c2fc88cf6d68a1437e1799ee454a5d6922bb5045a803b7d2abf6ec0142c9579e783913504f05d1936daa05e2862b9eb03e56667f3cff24185d42ee43acf614b7c18aa896a442f86428a88b1940436c87f4867746dc782d1b958ea214b4925bf35810ec561a6db061a4e8630332187d88643220310621da70c6906838e2dd94a20904c352afc42b86f9f3b0b7e2e2fb5a6f52830f0547f81bc1d2b5bc03e66d670320805ed769de4e10eca311551137b773c5508822c711f237b5a30eeed66ed70e2005568d9ee1338743af30dbce5a281142434f983d7e49e9f8832dccc85bc0feab0dfd2b255a2a340819af25a2b34e192e6be87d2dbdd616b575c5beb12244c7851a5b24178b60c7d2926bc875ae6ae081fd9f134582771db5c67d1a81542b8ac4dc9225ced4f5e7c1025dfe0b7c27e3e5e59129ad4b80ea4e9e2139b75f1d8d1fb74ef7454b1eec1f3b41e7a630ac14866838a5b3745136a8f4904661967edf017114ef0f20c4d7d7c201ee6bed9452e480447ee89ed65a09e9d771ffb432f45d0dfb6e214e14f05ce4215cf4d83bb2de71d014a9a9bd4e2a8001fd026edcb7b3eea3c118f25e0f80d83a1308a21d5f044531f1e2f383179ba4221c36eab049f71351e882d65adbcee1032d835b3cc2d23137d209bfe0e537e7e11f387dfc3d6b21511e17d1812195c85504b1547cce4c6be8393f96bf8594c3c9dcdf1ba800d25d9dccf32101a2cacc0525f54cc9e7d8bf708b49dff25dd028d64defa782ace35e82822f88b769c43d34f172d1e8e0167c5917628de25ccc46f8984e5d64aaa54539ad2dd2b9f3f225dcb2b2f5296ebaeab286e3ccf882d8d7493cb2d59991beb145ed0290cec33aefd09154b863d4b4a9fc23060da21bf63423c8890bc2658f33dcc57fa6c13fd10d7b7d8701cbf8d333664af01c8a082bd6261e7163f10bc2a46c28a04eaaaa0a89b7a126522133b65b43947a20f2c0656ac19d05c22aecdd4820a5ce44c2bcab6af77258bd952b23b0bef805d9720aa3aff14ae5b4430579a14c922417e72c6f4481327b67adeafd550abc22554963bb8f1a5758cc091c4a410cc019faa075b9640656c5e4be81e01a60bbaed50bcd4ca37cc6f964201fc997b236520fa14c6500e8ccbbdd88906f6357060ee3d7a41e57c9e1dcee77c9a4139cddebc4ff4b704d8c616147c0f37518d8ee6689451853b18c9ad46013136bdd22a7d956acf74531cc0389f60888b5649d20a8f91acdfb42ef6d11f1e03bcb9b83a0d401161834418b0d899b012f7b71a4f9585926517d6780a68a30ecc8d1026e3c5a1e1801be61cabe17b0756c140306b848788e4be6a63a5dabdab2be78b8ed8289ccdf33559c599ceff57a5d6b3d30bccf48ef92406c2a31bdc0482f6c7987e48c18f95334ceec954fe84a33961b1749dc9eb668aa34866ef5581f51a6965632e71f56766c5ee6ad87a997f9a2ce71808ac0379de373d4c2cd731ec7bc89a173d3e23cef662a5c16c062543bf7f7c24c3829f272672d758ccfedec42bb86e8de55b81de55a8e14fcd40c6c34b80dbd7eff27291a7aa468446d811e37d9bcb3326b6dcc8c1e3fd3f6f87d92f2452e1631c77f989734c3fb24649cae464d6fcd5c392538097dbb681ae1657f181c577462a57b1caf5f2261fafc83ab685dc0a622d0f589e062df819b68e2aa74d0139b15ba0b53ca5d9bfb3156dc0808169dcc759714a00e25b5412320f30070570f4c1a343185f319360b938e084964e46cd38017291ec91d40e98cde77bf9352a2670bbb228ae04841d56a4ba9cadaa61207882ce86d1c3d539b5914fe1b69f81270e22b8c5b3317dc9627b459c540019ba0cc122a287d185eb8d923c1ba31f0af33e5e2fe1fa6020cf4ff51d54b14d831bc773ae71545588bc66e923a321a2fbd674cf8cfe6737a8256ea5859e3f348e74465f2e703c678a49b0a42d6ecf11e6bf28f6835d799f6a58745ba848dcf3c266818b56019eab62490395205890d85c02917a7e06ad4a7654517d3431bc01549a48e35c2b9795a1cbdb68e5450d47a5bff086c521bcc3150b0afb09eaf617e7ebb6a73608ab6a34b0df54ff24fbf00f2ed6b4723750740ab95278d0c8fdef82302a830efe03e631111e8bf7ad244d4a46c2f1b3d5888ebd0c44ecaa4789b8ad39977719831614d3d0f9bc8fd0e9ba774e92d6cc5aeccf561eb03022dcdb88508f531a760887ea12ee63b5dddafcf1cf19e220060cdf5e964c07507ddc580dc40664177c904fb8a1544421a7c67353a27c32ac4a6430b0e81dee521432f4682a4ba653fd1c2de5bd0473e4e0a0b9dd64eea84960d5058c301d99af6a10b87590c1d38b28b1bf40665798049823596af8303b1e0f45835c6d286313b55fff2c5cafba21ff7def4984e9be45ba397f8440de61a03453346712107afcf74b9902b354e7cf68f88e2201600dce42d949d0bfae92ca205853fbd6da93bda2b3716a9fcca7c4021e8ddf0e468d8d564e3154ef42760030819ec993547e9f425cb1431e969f818808a2182d239e50a06274900c14b10ed77754102645959d0f27c062a204a3527930a6d6630664c7469208031caa10ffce54b4f8c2f76c00ae79405a0943a1666b64c5e127443a314995e92177a58e3de540014d26459ae9f6136c0569f931ce1499652d11e1600a9c78ecf17eeba05dd9cba90d73640970fd24017275d35fc13bea25db976ce859c4289bf0150c207734e34b0a3df01f1697cd05bd21a12b774cf3ce725a07344a18b4943027092242a45dfb4f353252b980e343341173bc291f154dd48f3115a00e6b7bf8210f6fa6413d9182d8b42a3fcc10a392231e011caba282bcad1e1fb03eaa2404f500721c5f453872823b721358673b9d724209efe5d12603f394d01da4b9a1090498531cbe8640a8bc4bd16f79e14e9ca4cb33e83b07d6bba9caf2d34dd9b095a551c637a0a0ad4d8295b228bf3a60e084caa8267b23710af41d143a8e538d9b1882cc793cf73794316d8075ebab7be860009a63c272cc80e2d86d0c50574741a1953cd92db504e3b284b9e0171df000aab84c96185e46c8353d05b285e249753a8fa05c7f7584e0874de1bc93785e12ecca4aee381aa9c0e213959f4422f63a486d89bc60044a2e3528b31352354f7c1166413e459363655d7a2f3dc9338b458946dd62617a4be58c44bc24c70a871cf72b3d2a364ac9ad191c7f54af56fcd9d2c7cba7669a451b016e25228862aab5207d5e32818d0fd3e1bcc06d2bda05375373939420cff52a396138a00725cc2d2658b444250ccd8942147e10ba8c09f945c4239adcd00a19b2bebc9690690b76b0786d6235e5f9b01170810e6af00e020a85564d5c5ef27218d9f96cf81363e98965e003f9b82f5f3b857667d810b563df8098a875f8059a07a4bdb527c5061395eecf5855a49820210401f8e6ed938709b461322905691e79a4850e66a05770e385a6c417d03e7be81d2b0fc2cfbd2016a73229498cb299a0efb8294111bd948ec7f31407b4a769a0fbf521e3941a0354c488664078f1e696522e1adb621ceb11b01f87cc0272795e577cd9f103659fe57ee731c493b1fec47c4adc0caf51ff85eabce5c6c59ab592c5e0d1ac4dad69585f1872c1b2795c5ca0bdbd63da03814ac12d04ea5f5b78ca0e6a623f9f73e35c8fba7413a810dbabb66c53c973e24792b0663aa78122f3e43a8cbfb1f618afff84ab49c391a04a24055129059111cd148e8a27ac2627e04daef9b67c7fc3fcc78fe80536352c7c81e76047a5020436414e9fdf8ea7a9e7131f2a434ffaa1dd14e6310fda87615ff15db99ffad76db149758e7e618184fe6f2123981fabc3efcb354e095bfd43a74437e983f0a44f6e4795b302edc8d92992b09ccc958a2fc3f71407bd26aef46a258f6582c22bc0506e9f2318374a56783746132991ae379d7121b85319537da4d4603d74d9e79ba1c95a2c55726f3489a7c4f6cfc343d7c220cc00ac1feca41409e8ccc10ceb83e4e73ce696badf4965b9702410902b8be39321958d67855ece73da0b8e55afc6dda6d60d880bf13dd165d6155b13bd557f1487fc2fe9e762d2cb1e944b6f9c2f4e9382a6b5d0f1cf928c5fa076641092c1644a775ceae49ade4895d04a50adee4202ac3e7faa5910b7ace1c78a2f76000e68ab1b43cf50108ac8451ae12ca6cd01bae2f75bd92973cc98fe0db212e2544324470c0374d45e0f7a95598e8492d317da0427ade50f18192f6aac50378bcfe2c74bca3be39f217f3740d231f176e9dba10faab8a8300a637c4afb5edf5e3ff80e01f0b03b9cf8f97ae50266a4e419520a8057f0ccce20bce06730e3b9f396e737a263192c9bce66aea6b82412638e1ecdbff46d1f35be321116fad3c62b6be94e3bd5f4f3cd923a05e95ceb3ca9fd454f6cf867b335388636e2f608a7c74fb436dde94d2429447f43d2cbe4c9f55292e2219f2efef47901b1e3329b59397eec620dd2f07cf16a6f64a4225a13e7e39aa51e8c3a039e542d906094ca9aa536bc9123f48d8ddde6dd2450038b14baef9dba2b647451497430431874bf0300cd6c2b4a99a07e328e051bb8144ea2992b835907e45a5075bb26b5537f19a7db02addb4ae4cce175a5731f9ad676dbe5951dba6f5e7b9c59add65f0d276d2dd8750f6a86adfd1fe7e0dc8276acbc8980a9c9d3bea856e3daf6734a007f1032d1d0d123a7ad039995fa12c8232e189c42b3629ec240dd0b94b3294a196b7dae07d88bbe3896dcd28168eb9437bbfdbd154d712ca7c0b306fd1171204d488191b00b429072bdb9b35080c0668d974874f991374115a6576527ea66468a05b3d7b56fab1d86be9060eac9b982a721627461705ef141fcd743881d84820d2e9ed9526b6c69b80a5d8298c0b750ad7558908fd950274092e0c2dd4b71eddad239cafc17cc1d3c0ce7a79498758763366ec5cf0bcf2c4623422141f8511dd804773dfd53cc9cd192b56ff0d8290d1bfa95ef3aeea4a42b8a407672d66a534d8a64ff1c234f29e1ac0ad623c494abdbb1083f21f801c8a6c0a60687de2c934aee505cf5fff9ef96d10e2ea2796399aff486c843cc75bfa00dcbb864f2fe1053ce8f207e79fdfabb4461c375d1a5ac0eb2c444266600e9e8e1cc25d71ea97ab01ed7bb12c52e3b23acea384a69c29499b8841e48a5dd1874409f52a16934c641d54743fd473b42ff96f1b80a9a0cd8c45321480cb7ce8d26c186dd0ad4ea326de9a3ccda1d8d2a2caba141281c29ddd498d9374e1402d812a78cec23b83bc929208ea8e789f4f52f64136cf741670768fb9a7ab3de3d7b61d7d1a8510eab95cc2afcdc63c9a15c62bb39ad5cccb34a093efc98e58a273578a81161076f6ab1654032beaf30a8bb95ac3935366ba339b0519b00297d6c4cfd6c6b73b7b8b0688a124493d194b02e1ecd04b1f11f740b828752edf7447cb047ff981b2528bbc95ce66cc6cc478ae19f3572c49752caa6eeba79cae2f6a959a8565d09ab7ba95231a715cfa81aecaf05d5934cb786fa2b27396dcfcd138faf5efaa4c323823c9eff628c78bc059e7f02fabb64f2b2bb435f4a3962dd47cc8f3c39c93fb77429431cc47505db804be774e50e90ecc220f25f057342477c1a3ee5a51b945a7bf8e22936b741858886332ed00c55a0effbc86204441d5cd2c4a95e246ebd50811cd415b6046b225af7918b55d0cd852e6811a6c037d5352e50a6add8f353c787a2f8724d05a02a70c1337daf5cd1c1eb669745f9af356d9b992033946eac46342c287f31d0ba011a2b9be4aaa55257da91444c568bd7bca1a3a5278129c6d349843847c6cd8ad3992a0d08b336ba0b0dc1fce99cd8538729c74c3602d56d31d532b977d3c094baf1f542347e739f8ee8a11218fd6a017fa872fd1d5393341c3289ca7088e9159ae95f916e6f03b2f328b8574a049bc88b8c58264c4bf86668d7d5fd79f7e22d260953dc1893a4e02febfbe5c9f1c47921b5446add7999108adc9bc439f21b54220c9dacfb6f3802d20727d785012fb3e462e2f3cb1af9d999f35d3129783aef7a0c238d3cdc00729ebd83a5670637862efd8456c7310f6dead8270981ee08b6bc1480e641fb41e39603e0987f70426aa3f8e0c04b6ea3566ca49c454e964747e301dbb2b30ee708268b2fec938fcd1006eb0cc1cadcc66745c8e5d6bb9bd899c370840814dd8fa1b92aeefe7ed4151e2afc32ec8bca81d91a0468067a142bdf2d08f6722eb44818bc82b8a49ffd357c6343352442f1f6f9d518f65f47dc04fb6aec42462a53a33ed057a82537ad509ac7610e27ac70e527fd1f09127c2ead3bbfd28cbd0789291ccec45a9865f700ee1669a6882da973ebdba414a13d42c511c895214e776d53f327b51b892a01a09dfe6b36af8d401fd7b7781f3f1d75debc968419cc478410b0a0fd996bd62959d19a6a2f229d5ed1c7aba01dc94954bf0df612b6dad87d98ba2225fdb28a950b0be7aed89b15650b544e7bdfdad728f2a31d619946ad686477ca508a5ad09b3691e297bb1d98beaf3e76b8701a338b24a8791cb63958b12c15dc5a0635c59a504574db16916b51d0b199f293cc6918512015869774529e720298ad4771bce2f33e78467e8f5fa1fb43ca28efdcaada3543e98f02e7a289c2274c956f58f3f7b512d46684a1638512dc23c96282c414b2641e44168fc364b1a0825d16f5f0ec99151a7a13d2b461d4fe3d955f51548ccec453d647d96ce290289bc65e8a6033d8d1ab07d2371ad005c5dcad82b303b07423c06a1913b1eb948b085ad707b7f4ca768e88075ee22a1e336f21c25333b145cd579fba4e168387b51c248ec505220f86f6505ccce8a4eaba6608a5ac16628c589064b2e500721a33c4223c8711f0c58cdc1d88b895adb158c4f6b73d92c2158acc4fbb413f57cacbfa9e0bb76a2e62bcc4023cb17622f2a6a610cc8086906153b286e0783a018cf1e270c2761068ae806cd9aaf0732f2c5884f09ea6c341ad63ba454097fd74d103705f6de0d3ec567a4754e2069ea8b8a809775c32c713b3298b02d4afe81830145ff59fccc08b9f73b8aa710889ae7590542a035975a67074a6ef3b8596c32e52c8617d21e4a519970e85cd34ee2ae7d05e661b1448bb264dc6bd3697648ee2ef0523302ab3ea2b32812b615c0f3c0e960024f63eccfb7f863981ac094f3bc75dd7f0a483abc7b0ea3b577e4a49154f0fde136a44f6e2c9538cb1ff12f0af79319f8fe72067d303577b35a679dc8eed27cedf6960462fed323cbe47d353a043a511cc0bdd1d1e9c514dc3c58b8b12242896cae37c10bae3fa7607035d187ae4d0472671b70adabda42995fccc5f47e1fa04029c229de351628791b61fd00ea38d29d9ae92f8f163a51881268034397ebbc381223cc3370eca9ae39876650f3cb0ad8ba14495bae58ad1f6d55ab3c00b8fe61c355644cb3664901870824f7a04661c9e6408372c40d4ec18b474bc11009bbb8d1a07a20001015a515546e00e373ea74426d508bddda1e04962509398d2bdfca8bc069ecee865fecf10a577e99b6a2cdaa18700f2326c4c9791cbcb6f0be8f9de7f2cd7c365e09c7c546bce361333f68faa6c11f6bd66c7cadafdbe2e7fa150f59267190f66a5b53ce651c9df74bc1d36bf8d54ae9ac0b28e31a84861ada4bc3d77f82bc95605eb7d6a7d393819d6532b556215e1aa7be88f405973e015372f6daf67679f92a78d0d2052c31872f0afb8c7cd7ad556a7e75e3c176a0fe0c4961f61262e8fc46d541b30ca0c89e73b6566a4dc728ecfd3f88871f3f36212d616140035b388780200c0d593cd3773bebce072ec65b156e8ef91f5d3555805cf527de8c847241950482c9365a21d6eff14db05f655dc4adc7bad55bd792ff3a37d7aa9e64c8252588a61e3bfa03db338c6f369e39e5d92ccb3399c47d823ea131cea12d5104f27a31cc77061412f592172be4c32a7f4b00b5c52f416528a08fbdc30f89a081fc35e65edd35fa37e7fb4ab0b024eb3dc69b9f8968c771f9e61d8542812a04d11d13b7ffa05641a18fb4253fe66b2af2c2c6cba86285b9e84f6ac7354c15e5be160e346a721a0faf48b50de2e19a62aa469da50ff38d8f215cb0de45b22f82c3c89ac008765a8bfbee97f78fb30148678482c5a0d9d68158b8cc8d881ce1c7d1e4dc91753e0752caf10a6733867967f9d4220b011d7c21510c9933f5a83c1d3af60d6c0f9ac2d066f63200d5e912d6adf9353c34f28d26a0ade17c6545861fad7a4868f7e92518c158271a506d7bc195cfb206902a002e324e293055cbfc4dc0c8e6fe0bfea9664bdef759857c47112bc7b7366c064519e4eaf843f6982a4fee84ef52876c01cd4619fd8d453f73555b384059f926202742c76539f5494035fee7ff502e3b058f04f56fd5fa60a4fca6da50156fa887f081033c0d1bcd033c110f3ce198660b8372aacab2e3f6a94f4c06e630a85e1435c5f759d0c217c41aedabc5fdce6c9401f8f91f7ddd905582e945343c969915bf30b59133304f3efde3d1b64213678c53b70946f947815d76e6013fe6cd0cbc738accff8934761047e9651ab5d212128d6d49ee8ec641585f1997a5600e43fa33530cf13950f0985c11640876a5d32b4ce9b4914f117ec8c8f37824a425a7283d1f50517bc5c37b2f683b5f1ca4929d6ab86f485ec167086a1e362a087ceb0d7a2a429de34811358be21c82e790806a5fb8a9426b8342249167e86877d1f106ec60ca4200f613bced48b4f40b3d495ca77d3a06f66891e081831fe83ab0327272d6a59e4762d26143605bcd6b0d5221f248201797874279f948804a82d7991b4f8ca6e8e5fd5ba6d886a29b8966c21a1c072820daf279c87b43b82ec983ce0904f5a1faed26c3245fcf1d4beeaee8777fed75252e35d901a7bc719a87b6b2d1a9f774c3587d061673a406ede4bfa6f84d5d3bc375f698b6ebb5575cb3d3dccc65dd26beca694ca1c37b6f7ba73941c87d632886100ecdcf69dc7b785ce049b084cdeab21b8dd646336ade57bbc1ee83950b4ab903f97c22ab9a90ef606ba33ad078ed78dc75983f19c85d428931c44e7208c8ec81b46f6b5766db5fb2a498300469a403aa14606dc870d5393a018ed27ed33293c876963caa03cd9a87c41862d27b6238f96cb795f3be1f985032ee2e4f4ff46a5faff81c006c6d6ee03956e7bcb03bdb21f13e80319132d5d5ba57c90210ed8b8b6ff343d3e23079ce8af02154a5a855f694f1c9348cd80ca1d4d9742c4868d6a2d4d12a40a257a99d8af2b57cecc21c91f9dfc4d49786961649b580fea6ddb10cf150c70d4ef89db50c8f14765d0f58e608f82a40134de4d66df89a13164a042c3e63bed7daf23ff322c9e273035f4f65cf758f853c646efecdd0a0267a79cacac78fe1ff415e863c3f4d9225755340f23c7227af162af33c9dc0fa16cdc10f444cff0efc00e5b62806a58dfab3b33dab982485e6eda8da39af2f8038363372c8e3fc87c52834850839714d1a563dc03bd39b2fbef173a414f0974c73e26ae13d54201d2d440ea5530e33520620e1930d28d68d7d3b845a8b2d49649d576fb1f118eefa321547855d500302106638e709c6f446f4b411adcb6661da27c29d0ece05341ebd8d0dec048dd76fda457657ebabd1f2d056d0306a10c0827ff91b2c00bf2e79a746ed7132cd5e3290d0e38f00ac1d8ae00bf073caf50e6b8792c43f39f6bc0b54770f9566cc69dc4946e1664d5c9406241888fd803f784ba846a6b4e37f5ba86b51e082edd3be94a1f9755108c2ddc909a1b0d9c5cb84cbca0d6ef106dc56c46da8e38da21fdcbb8d3762e50acf8b623f2aecfd06972f206997e4b3bf9fbcff90a3d8fc42401f80d788dee45860d17ab0b35068a99666bbd162f83f1c84423cf000e0994cedbf5db9da1bceee529ac462ce5cd6a15ad239dbc191910b71fb6ec5e793ee8806a658f107a4fe53f83ffb176439f2f9c7f925d17edb2736652eb58e145deda05b0897a6616017ed47de34870531f5949fe465569fa4bcce097720e3c2173a04d1b8c23ece51ce8b3774b9c7dbe1e0d00ce2d2eee2c725cfa6ea54c9152ae5f07033551ac161e478b64f101d2d5a8d9b5171e181712a30f95c562c2c03c444cd471b085803f0f6abe32d5cf21651865b11a65c671a835f18a6c842e98b5b8bedae140278a6a93a224c7eb8918b9cc4ab1639b981202554972a49db3598a1a4abd48c71627cf69109e8a9ae3e823ab1824fe1830e452966b6240ec7c7895f7aeb44b16f0dfa0a9b5d84d075a1eb6ffe42874743138b2a16193fd14147f08bcf27e8ab48cca444f2706f109214a741c0b885de97ca4c6c1f09ef3e4612dd1164fa7c2344e0e00830a47bf49394302d0bda03ce337f25956ef741f080d63e10eecd0726ca109847edc45cb8b1a8c54f4172a0066a4982b0a96147a3cb145814617772bb6a40893500f41c3c12457e2468eada0dfd1674a6675273e7aa8e3998874a37cac33f5c86ada9bd61c9c6fc51aaa3e80d4f48bcd673148b09984ac850d7097c56f09897764780feb5330ef91797f0e432d1899464f08dbc7cea608009b22ecb349b3a7dfe4744c34f760789f6b439533b8ac0f4558ab2b1446495b1cb413680b333a3c223dd86c878ac93a24e04b58dd5d3e99e1f8ae3f8e4a539e4ff347b5e5db241c64f57c4c4f656e71495666f9e9b9f68f899763da9490d6cdb357ee953cc2ba1570a8d2d74a75592f2335b5227b07b52122d72d680bd68219b5ea83fd97175be8db888db89e0946357add3f91009696448ab2a5c75258eb7468388502914c0e0b480039ae13a3fdd67c1a32bbedcf9097d770e098ee83a93a94a8a27fc76ec9d27a458cbbaeaa12b31ff4f859f422f1807d7d0749bb4b61bb7fa897de2f9e94bfa20a241835a27cb71a9ef33d081ce7c08d2915511859db2427e776ab3f2c50cf20a10143d05c486513bbf9098bf09be6063aaea509d4fb70a5cb7dcb4b2b7bd597399f666dd344aa5a7ba9f380c816e6e1d0c9d954d7be962056eaaeb83a0a709d367a450210fe249432e28ac38950d46a340e2e80d600744602f22830c66748ca87953f5de819fcdbf51b5c5518fd4f1ba3abb2529f10325f07af9ec16e811d3eff9f3446c5120a1ccd646ad6099b037094e76a4fa2e3e1884efd05da60edb4b4131a531e1ccb2734070540450d86227231f3165457a3bf488c2303e7dd205ca638038ae5a0383325ae5e9835dbeeef0a37ab9d1e65377f18c95ce6207ba35df14c4d09a0110d12963d2503c2ef2f6e9037f20e869fd31a5b9111063d788b2998a5d705f79dbf57468a20883b7541de979362a5dbf1b1f582c3419b592758f80d2e6b44f0ea2c86ff1266edcbba8bdee3adfc3be109f26017e9c879bb6088339b7de0e2aa0b07e67da540a28cb0faf3cef865c24fce4015ffdfe1233b9ce80b5609c1d6e45a7e1b161787f0033df3b6558198b3c2c68d3a24c8ab21f23c17d1457fa1cb7f0b8a247f581e4c7c230ec4ea73cc5097fa91f9d190f64ad8a7a454bbb7223f0812d2e8dc8b79d3c14fb4c01d66121132ac82e386e4ae1d1b081b2d6e64b95b2085f479b76ad6384bcc7892b166caefe1a7d5323b3c1d39879a53642cb871bafe1ec5b6eb6d4a67b25c17a14bf945df68fd9d084b4f80a19c21da96e9c1e8fa717d366bacc2c20d6a238d3b554ee16c71e750dff88dcb6640f56cc60c95ffdba74f971c5d52133c7d9616a918edbc95f60db85dd8994cc9efd19683f47fecc546f1814142a33a5a858aa3f774dc2bc89d46a69d0daaed281cc6b51b12849716903a23efe04fec71d9e177253c9bb0b24a7eff95e8892690391d298f7f4e77098bfc5f56e3a6a3bd94813a25bd2152e8d8be7cd8b8be6818455d6565e007618ad13c76a3cffc636a73d786e09b15547b3d23ca72751f864b7590cbb3ff568c756802cf4a2c40512a988cc322e9e083e3afdeea31a19370685c644680cdb31c2f9e4346e825a8f895bdfec021434d78510fac3bf8b09cefd5b842252043210ecd26e77700999f254617f4f045876f8d53e3d804e86bd804050ae7ff4b95d0bbda830219086c1fe6e31ef638874e0d4470047722ec9fb7fc2608cdcad8c51d6747ce2b722a11b7574db638f8a88d08dfafb9e620f036c18436d111a4a7b2bc4e242442166b3ca2a957781ea604d7f4b2f1bb6c63a18aaeaa57422ca57c678ddf6036cebf83515095950e7736a55e2b0b87b3bd53dd3dc3979663d7b95280801ca2e838bdb170cbe46fc75e5b9ea50c8bcdc39a3d72907a4c626c5d3b4def8c07c9fafc6a9d4b6d5a877f5f284a3d510815d3b31ca1b152b5f3e44bfbc8349c44c8a7f09fe361a171d1aab14ce8199324f2b7a215aeaf8f87d44ce60a22f035d01d0528ae6c2dcc29d104856cde578d6b405415f224478757e3574eb1cf532ea62bf30f11a09dc84fc6ec12df378394acc800b715f88e2402ce70f06da19cdc381d801f729a64c4fb26df03a695273b5f7c5aab8e46fb1eae317478a2060556f6e71fbfe5bda5c2ac82f848efa82dc82c2a81124fb5e5463f659fa31e14792a15505829bbb50b4b6fdecd96b7dbdefbfb256c27a6fd4c567b87ab913f18cea289ef1ad34a78ba3c9dcbe70e0c53a3c2dc06fdab3702d1291d6c0ec7b9966475d015192cc19c27dad98fc97bedf92c51847f4092f30d99908da3bb0a7babd185704eb761de954c94764ef7500a77bdfb3998b760b7a5a34f69a4408febff50ab3587af60709f97a138e17702a10325670bd02689ce5d5496cb70b15106f0e1698d5b8e8de7d1e7467cc929b21063140ef64d2d290d8948cc9f128380d26466b44c1b177aa9d0c08b39ca3dd9e3459d9d38389109e0e65a201872f6bcd8ea21462c236b88732511df6e8e1335db19bf834b44e9d6729009383cfd9c04e66c68a248f4c1325daa2beb9245e2c8384ccdb97cd7041ac1dabc915e1ae3296d1031ad744f7b625b8d9167d2efcde44894c4254fe4b50fd73d32e483dfc457b8341169aded0d2e22cbc10df76940f9f266cc355c10f7c567b045286ab082125a4168fb7de1f430124779d66038aebae7ef14b9b3e843afaea27ecf1782cc50f739a4e77f1498b55cd156a570d9195da37560cd15aa089cdee3a17f73e4d5bc9ae078958586b38f00cad6cb3d335a0b7f83ae4374882a05cefcae1edc53b3742bd079c8c0c14c6ad3e3d98602012799abebea2f292ec450d1579cc467bae105e4b9280fcf423c1d8caf1f46b0084a4aaad4fb92ac66e5a358b761f0176a8b85a623df8fbdfe7cb00dee78ca61514173dc26e6d687b1398f5f3902a00fad8653be735ea9b8f93867f7ed811173c5b999d411e41041de550e58843c6ac3ce754112d59b99cf27bbf516085903f0f4179c48731c5f2e0a9507f2e613504fa80caa017cd3cca51eab9c4ef7801171ae46114773cabb4cccefc1f1f367280c8dc83a64934c9750d7f3f80c2ff3b6ccca8084fae643efa0d26000ad2698f5dc87e53c6a13398fbde3b6b60e64111e8b6798b4e4960a9e6facf51fad7ec52e6bcca4c0a3813aa6a3480024a48be74d9a81faf240d7788940c28b6f54e520fa5e31ca0a8407ad670b3d19db676080d40719f4b7a1175abf40451a4599da0d15b7f5371312b42691114618ed6f19dff3add042a7364462548bf864066219c738787b31246dcb217d549b0b57165e993cba242e20814e8136107727a9f230f61b50325afc366d6508fa8c677c1253a4486a29a267d8569d001a8ee687bf55fbc6995a48bd9a4500c70d3fdde2d06c142b1071e5d40b7627cc66bb68474c8a147bc26dacbdcb4d4d22cfc1682e8c18e335b1ea3e90d8799138ca842484b452ee94c7e97cf24cbcf8156244e4c79db40a08486cfaeecedb4c676c804ccf99bb528e9f96985807f53b30758441e1bec012de54e85f4e1321e0074d4e61b3d4d973f8a84f04615fab04d3bf75705724a217e8dec056794131b239c2aaf9572480dac15fa8b2790abf53faebeb9ee22f73c5b77b12c12c29a9a8526fd4fea034c327ad67fc488f3164dc0fe7d649be9ec15e38facf07e5a2fadab135abca706dcd2878d11bf8434b0bfdd97b62aa9f81b91a5370d530160ff38b4e47d9ff626e6efe952f966f617aa0debfec1cb97216cffd631f2707511dbbf6b2e6f0f07bbfcdba3d8d14dac260eb7375db1a401068025a9680d42ee6f94539672e7fe8fe520218e81fa7d3a631ea829548886e7cc2f8500059c898c49de5597dfff4e4d4ebbf1f7208b8c4549ecbd840d7331d6df3526f9fb110efe55951fe6f75804f24f1245575f34b92bc7ec3fd3b8d56f7b3aac75f9067dfea44bc3cc2333a8f0586afcd251a2eb2ac5290ba8c1cd5f4fa4f21e4e46c2079759448d7412c1979b11530fa1048edba592764777adfb6ac7d5c5b33d18f888c8dabcc43983928b413cdf984c41f59f0527d31ae6593c0bbeddded3c5956386573967ecaa1c71182298e6a1f91ec208e656baf4bfc5ab53aaa582845eae7ec375c85b4ce435ff07504406087765fb7427599fe59c2a56b066e67e9aa7c6aa757e61bf316c70a4df85bd0f90950c937cd4112fa4187b44b701427de06caaa0ed6bcb03bef235d88ac4cd2e35bff73328428493f4c8a605f230a4f25d8b5ea56b3e634003ee832a2d7aa185f6d16acfbf4c77185a04a6331ddfb532de410c3fb8d769d72daca5d3de42e890d540ce0f5fce1707ace1e8cf78fa8c8291895f3764e28bb2807c39166d364ef6cc2189260a6ae99d3170df30c636113f12ba02e4eeed109fbd9ba5299f2355d16615d3fc6b6a52a4b5700a3b12a16dd8bdcd0a00177d6d69d4402e272e0f293c371ca5f303d669c3ecd240ecc8266d330137ffb99b1cbf8578e493feb187abc3f87e983c77d01c4b934e4f6b583512710510958b30c95e3a05fe927e99826d83237812be3060f3a0e19470ba49b51813259f9d8871cd09cc197640e249de07d945294c3a7eadaf85e426848ad172631fcb99e275916e0a67dbcce187f33c8057b6b61987fa9b259243f2b56d81ef3a74b6d185e75e4402d2b039d0865e86af4de52ec3a05cc44a80d308ccb5c95b99d3e39dc98eed523810a0321acf6a4e6d8592bd913a8dca85c18c7d9d09fbb3180a548dd4c7eafb7505cc5212a4a50603fa9d25c193e2cb160c5446cbdaf720f7e539ded5c02a8aae5d9e18e60402e63cdf2d11798522ecc6984d4d7a48752dd5f751d463ef2531da29d77dbce6fabe43281c604fcd253fa7e8680fbcd875a86a0240d651684bca8da9ee858ec55ca6b2ac6adfede56ee57e8149c0b7e98fabe2755ca306c0b792c56deda3f262995e07ff63238b8aba7b1629eb8608b2ffaeb6739574a8320da8883fe998d33d2260a2513bb218eda8af5353c983712dcc825ece72c3e0256a39c1ca6a8386b550cc11ca55cce7cae3bb6e0cc08c21a71d830213d23232e0838fbe455b84b331d6956f9ff652c4a13c6d6ff341100f18571a54362e1cd6301d55224753adf4a8e7d385bac2eda1fb5381eab19fa610412ccf1472f912057501008d0281c57207778f3313566897f4bb5138484425fb078113a790d1258546b2612b0f8cf1b61a31bbf2a79202def7031e98b8b94a4f2118038592c711812328cd135839a1d1240fed6ddf861343028fc2ce5383950a4ec8010052f3bcdcd617ffbd753137aa5a5b427bd1c905c490add82e9f1669803755e51c45c7ffde6c369c8f5ea0e7eeb5ada7d32ba338334c54b19e7a9c3c160de23956fe9f1e2614c7b8d0699091d467374b5defaaf493212c1f885c766af2b0ddb97f3e32f4bd8ca93edc01d2dd2f8a6039aeb7a4527412bdf22245f078d72b6fb5f09633cb5b9407c50289ef389c11ea77db2dcd3eb24d2331e263f9ca105b90d0c0279c214b995d1fcedaa7200493ff019076d8be6c10d48b955a8d47c6b7c78d351c7fd0bc495752c530f80c29f0253b0390cac08c55438af82ae5978b845f7acad83247bc493656052d2a0eb3b59bef18cef53b3bc8871aeb0d8c311c5f7d8f4e0951c16e71a42570af892362473431e9786205abe8593584f4b5c7c4a9ab381f9dfa385426a76e8652dbbc1636fc6af75aaae90b913cdd37228d88aa4eabb76c9f9a9fe9f388a8a6d95dc305daaffb4ab28926d101ce8941f590916c5a73086fdbbfb503c61b582b9bfe74798878f364c860e8d1ac4051ec5762d86e02e897469839916323d2323e144ec81e8f24c49a898e7b382a30dba2f6f922c592e90b3c7a8cc79100505943888dfe06fd6d07490e59c0c5573e166d4751259040bc85a6a1aa19a88326712c42221dda5859e509029d9cefe1ceee50b0f2cdc1fc9afaad1c6bb3445e59f5199549abcbf3103d29229fbe34a6dc52e2bcf2023441e746b38631dd70f9a4a0c0a0368f87b398e146b6e40bf6618b239be0cf07c516f309d1acd3bacd484285d8c14a80baad0f992e8d629d296c086cd8e641c82cd1158123d23e1f18f2f994b0b214838ecebafe7edbc974ff1cc48ed218df3e4df2dc7bc3c18bbc7fb86ba0860a7e27842ce12ea152b62dc31ee4dd214f4ef37addf23c96ebaefc686472b9bb795e28ca75f45870ca630a670cd768e85a5eb52ce1a4bd407ccec636c13ecd07e03e68983a601b1e69cdcb39adf879a5ad3637695d57028deef39836b492d368fa1269948ed41a9b6a148c0e80ae12582b158d42e59716a777acd277b9419e804e2f85c729adf42f5835e9971f5aacb39327f1ca2a569fb2d2ee03564235f2c272b26c1ea21b75344bd612ae879ca2edcd8d0fbbfa3c3017f7586e5f492e8226e567d8ecdfda43df5fb5b89c874bd3f7c8bdc5e6e279e1f04443f0e4a3506f12b50732bc8e0d09d561930469bf301a598957d58a8f9d80e898c260b9a0ca8c8d2a63504ba6100bbef18140cca81f769998a4ae977c939a89c89716a5a3d513d3de5d8f6214271946238192ca4db175302d3a322b7bd6caf0b20064f54a8c07a5f8d16c35ec755410c5acf8c470a7485125aeabbc7126734b47b05a9046d1fa098730e1072b52f77d4ba5b2c8a0af5439303666b0425071bf2acfd8bf5a1bb3fe500bf003d3bdceb72623c3a6775a9dc326e206495c21ee8aaca5a9e92e9850003405a46bf52b3bd6939e5f50fa0acc0940f785b81fca053461156c116e81bada3d84bb5419e77af2f2dfa92efa471c140d24a8281002b1765dd161db760486a44922e50e3ead991541d8f7724283a7692fad609457ea3afe53fad26b9327cc9d4c11bfc084af55ca072cb66e870a1be0753e946297cb95226bcf1d5e6a969e2f1a8d292ea29c92693d17d84cf440090a0b885ae169435f911faad98b12f9a4ab34cbb21d2e7a364624b16803096f7d409b7cdc42fff4c83bdedc8a3cc3afba17da26615ff1f351dfbb19b6de3994638cc3cb5fb28d59e972ac0aae61df6c9a95f79300ec16ddb306f475c32d80f8ed514ab7e074105614b6ae48c7b03deba98ad4ea529f5a2004c62a28d233f03493907264e34305125228891047ce3c8a65989e2b888412029f1e98010306fc58a91c5cb6ff4eba3efab4032ebc6854ef6909665e988bb23cebebe2cd5d7a11bebea5cf387c26f2bba48a91a0b1048441fc7ec7069d7fd2968ea9c8a1fbfc85399b8224c775c2972793e72fa4f221e002c9d77df3d8567f73bb1741e68229397dc438bb1947fb66a022bb4bb6339b12678614d4e4524cece1b5c8bf5fd1f4e926350d381a2f27ab609217be18a2773be51da95b5a9ebabca356151262afc93379c7f3fdc176d6da97c93f6ac0c2702b1793f9ce9fb3455df050b62e2f4ab2d9be75cde28c2b5da072dbfe8070674e4a5f703c36f2821f847dad03e2a4fe9d6da1b54180a628464bdf601d452c6417034540e12e27a1ea25ac5298844ed116f7d96abda4f0794dc9e19e5165e1ff00e70b8fa8ef6bc33068eff2a1577d824d52d44ce3b2ce0c23adf86ab0bd00dc35b3431702321c87d5406599fb346787db759162a059b93a2cec2831ae6d6fca14a5f13e8827772833ee7e0609580722617863dc4e98e862992de6f3a6b793fbec229cf3995357f229398774c4a5f5546e59c5bce06f9e9fbadf2c5c80ad8b12a66826c39ad79b8b651569f87fa983b61a51c542a22a5cf7dff650656f37c85132ec8b25dc68fb4c05645cd438f44dae70437b6b4d6df12f361470f177fc955d9a6f8ce932b3709e69634dec95a58053b8b94f44e412ea12aceac04cb0f498b7b1f9de341361c3c4afacc294037428ced3e970f26f0a14060e3e2961f4aaeebbaa1e2a9a07f10f7fbcb31660c9caf73b98377b6af841b37dbfcb9de37b48dd21b8406504fa333f6bcc7c88553054ff7c1308ea1e11c07d54914f399852385f2d0ade64e998e0edd6321d4165ca09c2c059d60193c97a2f26586500cbfde14735288759b8194189a8553635e1385c688963918ff3cc91a377858ada8ddee4ee55d0bd386e35675e0154dc814ce50534be7e00ca58b242a27172bdc202dfc6e90b7a5400393ad30225690b07390e50d4288feb024bf993e1642ebc5bb218a6528f3591adbeae0a764d820e0bd5d6fa6f17d06a028da53abe53e9f2d91678ec667341891bfbacb22cc785c2de1c421ae41c29682a7fd58cd7af3a8abbf1f13769197703a429f7167783ced9e880c69e4ea818d29a9d5d177453101b20431e7501ec0da6309c21b25a08bf771c17ae059d3b3d448110e0b52e81e50a9d22d097e4e698601164b89b0f744ec37dd06f95aef2dfad3cfd48a88b861ebf0d57ebc301086aaa39afa1927352e00429d0088228a5565d24a3165bbb884feae25b64d28fac2435b3ca2dcc13cf1076ffd842585e751cc24b9c3a58c82fa1e96083694d940466b1f6b52e4d6c78c2fa3f89e04d3a497fd26374097404b592ef5fbd982be7be5f92f964befb7b358d019069425fba0d48e229b99eaa0b19d4269a5f12623965c7c3ba3a38707e9979dcba3148a925866053a5ae2cba6995b88bf5dc86e1c6b7db1d9cac3792f5a92bdb9fe25e49cb97a97fdba11879c9571f004eaa131ea63c24ca9bdd9040ab38660476691a9827ba93c684ab1c79fa872fa0b1429313602e18d8432c4195283cc481d8fd26728df11bd649aa68fe0d7249cc958ff243ac85c513712ced89e4e964b606cbdc37809811811c6c6b347e7a43a37cd9f11f903072d001d8238d264216d7bb4faf661744ea5f60319bebbd0f75aa10d930e270f97f9a6344278ce2b7448a07246cc9438a39b8e8c34baa011667eced7ac83a0c4d85c99969630ab31a79170825faedb4c41a9407710ff3bb4d9c26f7d050d445bbf17935088b91339f8792a7040873357c5668f7680ff4868672f41bc9f2c8da25a918f612958df3dda06022f8a42a01b10da15e331ccc640c71319cf275828909ae8a0055c2344296fc38ddfb1487dd6b1a06db7bd80b6c00498800e353eb94efb92fec1fac384213b43b4b078f0d11956755882bb0cc1da1ee6a24300d6ca355e9eaf9a93efedebaaa506e00e9063c640c0115b5ebf0af36650c9d4e6ce1256f40129821139e099de5893efa3b6c390bb3d4c7e2008604508ef40e40c5d612b680d1e7cd979032adad9fb425651c3483d531f80cdc7f98df54bdd053581fbeb3455b7129446c0ff20bfdc9e6d588101bdf4c2a785e872b992ca1f369857e2517e531d8792b9ea77fe194301c6577028bdfa2d2c60623e7ae78a02447a1c89b03d6a783d86d09aa27f48d47e4a7be8cd9a1fc48e4c872e93c083e915e6856dfe85c88aa1966484f4333db96a8f3d02e9c8b2b7bc296bfe113cbd96b241fc901995628443be22af34dc4974579345e4e6c5670da30f8bf04b58a2a6ba3af57da2ee8383752939ad3eb8a34f773c98c931f20886384774005209baf685a05ff66a7efb63c3b8c2ae401046682b4036d69225f9e01ad9f12018583d0ef6171427ef2bc7ff21cef7bec8ee7bd2099b75d3e0e48600c8afd99fcdf09047c51875916729793eade1866b26d907dfc271dfc0c3531a712274950deab3eb9b8372e4f9e946846e615bd9c563a561fd4050267c6e72bb97f65c1fda338dd37eadae7fd6a30f1744e0b84d5c22a415930792ed2a336e939d0222c803c3ca0d2187144a95cbee8cf26c127c400f3316b72b1f74cdfc2ec90e9c84200b51e83adedffc171c4b9419c927f43a1cd17643d69c22159a532a4359a56ff11e92a4f49aeba5f49200001c2bc429b3b81551c4221b4aed8cf1073bb39bf70a4b598a6a3f76bc6ad6902e37b20cc1da789b1035ce10350bddc3d091251436972212b7d43549c31a5f39db1a1d7e6dd805afd07b0fb942d03f52ca4ac0003b22bcfd45af3a929ca9c36fb93706c964a603963e28d9c0fa18934ac1d5341042ec331e2d90a720da3efa0ad67ba62b79e16b401dfeedd8ab53e4b742674c4d2902048e6aba56a4cea2ccc22b0130da3fcc894f33e15cf9079087a87c5d428c6d37536afdb92419466c15e4591ecbfe7c85be9dd2413d100ca693b01f0b7cfffd5f11c5e426979e73e985e4db701096ea348501fdc54aee7b9cf87b693d6f33d7cd4f59381e8c27d233e9c00e71388f32374be07596b8ad2a70cd56821966a715686334efa435fac0322760c0707e1b76680efa0df19e79c5309753c34b8c2a45651228042304b961bdb9c2a927d51ca359b6ad8ba7a37cdfbd6532b03d252579dcc4f9a705920fd0fc4067782ac334cfa333a7da9663806e9d139a6e51e044a8acc79d13893c2aa17b4b51ba12589e6652261e53e3d8874059b448f8cd2bd62176c403b78a98832e5ff6846c051a19b9832d62a482aedef978ef9ffd527bc4ad41e6bb0e9bb404616d9b424c558411ebcea2a1f1c3e48207afd43e99ca154b23a17a592863a7079f9e93b909cc336d2c367caa0957ab4d11326fa59dee54d7b373cd614389903a0161001db2434f697da4804eb69691623a981821f9b483cf4951007f8057cbae047a93460bd33d611f9afcf3fcec3f929a0288d578f282ec5a6cb8f211dc1962c33e4d7a87ea6baf27caa637c9e0158e2c326ef9e41ee9ecdc6d8f385a0d873d04098e37b379b47106ddceace910cfcfa720209c6b9a9152a503e9f0aecbade59dc9af1dd42227cf092749f9774ae8c3abdd5b332fdd05ee9e69adcb7494ab04d7472d2da321004a0eb796ac4f13d1e6aa17899e8594633c1de866d88cc535a3d453fc08fd252b245aade4dc29689b6273e526c1c9f3c5d5775b36712ec28c9b5b0f2cf1c71cbc1de1238a183320edd57bbe105340c87c8315fef9a0c96431f6f1020fa84e7db4f140e6f98c4b9ea7c6a8547d492175168fc4832a5fe934495d00d3d9d462bc82e250d5360345473b036e3a08751718d70decc97f68bfca63a436d56496a8d4729aec61140562732472e5f446c852af8bcceed849e7a0887fe2542d9e2c7767a34bd00857c00f1da4d733a6987022218d2d7e3d414302b34d2e07c07703553bd5a3760c03aad26345b89959c42b504ac14f4a5fc7f9f01e4190dbe0b7b60242a3c9e7677cd4d66923ec61fa12b49db9b9cda16d14d0bc9241a7d3da2347626920dd8745c4548dbf874f68c9171899da4ad6f3a6b306882029403d1619898889c74a9077b3e477f854e1fa0c3cf47a94dfe864c49672e17ecab818795c70ebfd2a77362912cbb4d278210495f567fa8958610b4fa9a1a36a049ee1a01c13cbf9769307afe26ef611dd0b108e4fe3ccf7c8ad557b53d15a01a850b99f65ef1e1275c0285ae02f73f67fd1c9c226fd70b234e29aefcc1575be8f03955eb69477cc3d949890052e2420d718658c4287bc2dd69822b5f391f63937dfa525f820838049a1055c9379012429a3e68894823051d16a2089586f85d94cc97f4506c5ae3df848197a4fc843100c160e206a65adcd22436d976a7543d9392c590fcde22a21af98c0dd6ca67c296cedb5ea440406f5e5fd66795007c99495294746000814c20ac4d95dd06f49c51a4370a9c1bf127093e3b8d0364352fc91cbe06f708e21985ca1956feddc09a393424dfb34a529da9ec0c8242024938978611501ee78b2d22c346a2e6759f6bf61be89dabccf31818bc43b1411180f933e89045dadb6c17d94a67ab7100a2ec88045c7912d569ef7a1de0231be91aeec71187c315f7c9541f4780dc88316257680208ebf3fc05ab5201026cacee0a1bd7587aafdeb126b0aa80982af2b1bba40d15a52a195714ea738f7c081ec0d762e78441fce0ea89b32c9de3b10a8fec5d2ed8555fffce5eaed3461bb257df31e785e59e1128354803d53ab2b2705a96a7aee10037504fb3b6e91e13c56a011173f1e5fe354f7f24f74c642c5cafc519c461d7ffdffcfbc32224e8c7b5c2f6f37b9f4a7e08a70217979fecc7841e6ce817b823dac291c13914dc00ccc2456d2f3d36dc175581b68924b7f060819f5b91adde85bec665eb9363309927ecd3a072e6a4ebf9dff7c9008b5a36a7d7fd15d4f0818b440526ddf2cf30446e1ff3a1bc80a07c0019dc2dfee84fcf1c112e472c39060196af954d8d177d15b94a444b2b09c1cfa36e08529093514e5b2c9ce52edbb05243b9f85a30706a547949944d612e36e535c991af8a30e8e4d240d964e637d5ba43f89f27232a62249703afe528ddf4941e440801fc48462a79e884f2215d87684821b4e3d08ee1c0df40ae7fcb85e6b01a5ae9a98b479eb1d0d3b6e6517039fe270606336399b664403d1879210f549011f0807e22c95202a08145e01e64bbb0351ed006e39d047750eaf1da7d45329b7a40e60799bfe9e5edf01c45d348ec14de2972bf7a98da49d2435b2e3439b668b669d543ea435c0f6c18e9af4040ae6878ff852389a2da3386615eb8ccdfc668adc941f3409d49a31a8749fb58cb274247e5c2e3cb4b2a471704a6928cb50713d88f52895013b28c7c9c97ba395eafe2dab6e8a3c75a029427746efdc10cc183f17f29fb87d1f08201b852f6d4067488cd44a35e12886dfed0cc5e01377e5416e98099210be1706eaa114db9f61017495ba2724452427820e8f37993571ca73d281d4ee2885c4fb0824a43c1d0e7e02cfd102e4d4f1db3057e67381b73322339842e596a4d2b4e296fd268836047b27ca1ea0169f17c7a61acf14bbe00f97aac8cb28780facd9fd0eb8f955ba240781651750e449dbd31ddc50fc274cb5b14a0e4ebd9048543ba99ca25046cb5371164f6a8775887614a834eaf53ccf2eac281ab9bac2ea880751d3cf5675f074e867e6ba902cf8a6ba460490d6aa381c4fd0264495dad8ed703029b99052a77991d40e7c6ead5b67bdb27d863d1025f6fd346f7c4c8edc692fe66285cdcd28d7a44c36033c6760f0a2607fada0f89ad49dde1f6412ab2d07528f78abec2117c9d2c4ab67abc0141982b6db4f3b33dbc0c5feb09865668e77d62b7a2f29733c5256d304295d40e6603f08c917bf907ecbfe5b646db9c87ca00f797810eaa72c8e9bab88865bd6509bd3069c91461af3ad0792432176e8111d4eb8dafb6bb8cc5093cd8b061c78021025f06e741607b4c75c714968fa5ef8d3fadd1351ccb6a314f3f5ebe627e05aa86e70c0f56e9a5977202cd447e756a41cf9ea9b8bc7610683f9156c84daa49d73c1e90ca42969350f307840a80454e9881fb448bcd4c086673d3d783f7c6abe423fa97384610f62aceff204cc41a23c43863a1c10d290d17d8ae57f01cda4fe26b49251498b7348be4cc43c452b2ada835de987dc054a5d1ee71e17ebf83ec3f4a31ec31b762241e39db20bea90828443376a21eb670a504a31a2edadabb0665987045cd89781c061a0f5e66770cca3d08acfaf11db394d3b3892cf4293bd9018c47f04d110f503e70dc14ed4bbf06f887caa804cc4cb5dce9e8765148045dce9d98edf5226ec740ca34acac86b857816c1c5e484cc37f9a8dcb9c230b8c9ff46aaca9c050f5ab0ef8a4fd8ce1de4cd49e8fe4e1aae5fbf0306faf38a7cb7e3a27e2239740a284541645571413a603bacecae41457d47086a85559b3a2863507279796c625286567ce9358f89a58233623ca87f5c5deb972fc286ca3434cb0c094a279935d53e02602c96fddf3b553f5bf2f9807e87def19d8c00391bb9cef6e52e943e89cbf50b9ddee545182714229a9170da8fd5e53d5cbfda30f7569aa6bca034d99ea2f65f15b61f76ae514212c0711cba63ba49d8233a0be4ec18a81455379f62514eea5adfe46e3a7033dd803bd55484fefa49adaa151580f243f90c37aaef472984748d636a8f874ee6c295f03a13c17f87d6c955f0ae432c7e188879865957300f8ac624c0b0f42ce3d55a3647f98184a213bca0ff12d232a0efa53a235c06412e90ee4e662cd42ec7ec5be2e113018f42b46f67eeae1e26ee3578b9b51cb778de8204f4635e9d0b2e955b96c7b17d31efada48b07ca3934fb9903b1b3d12dc0edbdbd95e0f3c504e5a388573bfc5371b55cd66bd0c6fc193d8b6fc1bd51474a821b4d104a77f4c725bb53bc2071c192e4c0d4e874742647b476f6fadebd2db3722fc0fcc090b8fa0fdbeaebfeb403b8c58765d9eee984a6bf42758703939862091e2daec3a4b694097bf387ee6ea3370b3472f1b0b041bc0739452e15167e535c7fe01904c090afa2f05b40233a40c287d76c2680897bde238afcfc8bd8c1d2169dcfa2ec191cd6d75af758b968e683e05858e5f233cb30ef759f25ee92f0db641fc1f1817705eec668560dfe452872d622eb1a25208950708affdb0a41c822c938ce3a739b989365dd3e44810e3d2b567fde7f0eb0ac41f0257702df19af13099d6383b7874d2c4051bad4de44da53d10a29145ca29ca77cb9ec933c4213662438113d32c2d28819ab484f498fd1325f6be1a35f6340e4d2402355066d89d49ea7dcec8a24e16a192510349f5b2cccc655bd3b0a5f80e3d5553b182a6c120cb277df22b17b61a2a811998b55c2c27a7d897a48b284c8a5edaff58dbe504905e2e11499520523012e9d3acb3f78f6d614b7a495b1e7d1bafb2d477a2e578494d31f9a688ebce3700a66c871c703003ba003562af4fceca70568250e1135cb29522cef7acd23933d7743c4243ee994a70bdc6791291884b2c58f4400943c8de385eeeaab0a34fd3220f6132f3c1d9c9ee28b0fba4005652aabd2da7a682c0fd9a506506bc6a96cb38be47f1e78c3e1c570da8001943dae61bbb0e8e82b6cab71caffb4cd683b7b0e74188a8d84622234b947a6949046529fb5e0bb4ef281475ee11f6f13a0fa66bbc7260fede45132ec5d353431a174ce0227d6eca5437dc30d8e14f025eba8090538ffd69e09afd28a364b16b1076b55e0332282c7ecbf2d3aed3cd9d660bbe64a973d104ec1096b391aa5b0545aaf97a0c27da69ea72a846b06d37ff1a1f1e14f107c85522900088a86be5bae770cb1c4aa8227286c448f708471a141f0710f9208a6ff956af7f180fad6e70c12b1d7a5f96a5db9feedbf154e788593d4d28186667b6a936d1d84ac481be7d7f8fe1ecdf312c2a2974819f88690f15a7bc8c20430569a3ae21b09233fa33c6e1fb712f1f9c45a3131b1ff0d6f70000320366fda1435e48cbde7dadb50188c5c03d02d58b4975f6c98ff25d770445228fe14d8960dd1bedb21dec7012c8f71be627c04045fcea7be56229a05eea12a8e867e8a45562600628081da48cf6eeb999116d4fd777988d831a70778e7301edb23bb3adb97d6d8fc96a1105ee99345292dd1a3356edba89e1a622dbe39d0ebb6089fe2ed8aa10d313703364b1e983e9779316e244cf6630420aeaee85522a90c3bc319d26ad31537d920610c19b96c7efb74216d50fb88a43ea02b18b99e91906bcff593a12520b0fa78cd7651e50abe69755a16fbf811256c27e153f764f1c02a7f5d49ea252672f0bf42865c9dec09e12146c7ac5c80567a9fbe91b5ae335fae54b71f778fea92693d3eb55e8aab5b4ce218695acc63a5ba8bf585df521d00600d2c21b891b2c7e9643c79419052ea356cd932a01b4621a2f5fe1bb8ff373f88880bf1259dd226cb4121af3368c5c47299d37545910b8de3684128784746499cd3153233c3a809a2d34ded50f6daeff0284393fb44be8bb940168b3f9ce4f002ed3c7f66a96c4f6ef8e5d12053c06523dbb9ad09087d89683cf99524fa2d0b976ce66762f1a8d608db0a2f2a7ae34cb6a52aa43806681a89e73f478d16d2116ecaf2f24e1700ec7082f6dd7a5441dafcd58644c9527da9228aed81234457c7669c1f5a31513dc55b238f4ff46e83368b5d315b128ab65392aaa9c159c0c58d4b1f2a259e91d03e27d541dbcf48a8d4cde0915b327abd08048e68fc689c04a194c94f8c9099c264f201f905d10b94d531692073e0d9904d4109a568507945c610c84e3b146368f1c9dde153f4e3b4facdede0e0fee11c724b5cd432209aacb6ad2bf93700533f91da8afa052986255949d21d35cdba0b90d746452724aae83350a0e4a709752f11b358084445252a27ac1c5026c07bd2ee30c6823b593edf733c2680c43cefe2aa3555bd2ffd0d9bcc890c395380d06cd48c68469a8491a3eab02a63a2d26fc304644f14785921a0be623d87f25f5a690fe5e52bf4c7d3df1d4c88a2eef77dcf2c94215d527f5c024b614784c2d1d82aa22fb709073b4db4a191e243ed59355a5cc372da7d51892611e152b7fe3fb96d3af3b5ceffb054891a176eea9eeeab94fd95d2242bbdbca5d8a121ad2e9871a966688fad393cd334ae60223c324ef2d9e182dac0b7eb8b0f79856409abb944c6fe6721a0078eaa574996d4899d04a825a8123464ff71dde06a8e616f31ba527fe919c2f19de1f9c2ee49d84e612293d83ffd4ef3392075dc2ddb88b63b6e01ae733422a8990ed6d28c7b25d82df556af71f9b46498fa58b6a2cebdc112f2c2b8e7f3ef04ae25966a0c2bc6dbb0a7759a0d15b13cd8bd35b3610bc705ba57f9881342f34bb26fc3da1ae7d483b4ff2d46728764f2ac62a1380b3ac2cc2c75f5420b0248298041bc47c94ceb6d504bc3eec66549799ebbc46f88f974f4f6d83419ad5274b696101db86b9ac414e3749a15538d5fee5a981fcebf2aed04100ca17294f4efefca2ada8117f51dcc93bafb882d60b9b4bfc53a080fb4bf0321c212eee528480bfa04c20c4ca15860d40ff036f311815711878ca689e81af392234aaefbbda6b8ea2ca5b4cb0a5aaf409a45cb91853070b89613477ff03463410f7feb4a09c450c16850d64cfb6cfa55398915c2eab77bceb366414d6c36b48e8784afc85784e36ed6ba95f863a9c400be986f09c34774e7c63062c804099130a9aa71771ee0b529b8f382fedbd58ec4e92f929983e4f2abc11f189bb9f67202726092eef4b0cf4324f613859e72eac25de4034b8e2588bfd1261b1aeebfd227755869bd42093c572aea7a62452b0a54a8d97ce911a24f427cb9d57a88857f16445e8a3dfb3a5035810c6c9cbae301a34b7a3dace657b7d5bbdbe444cb8117f56e0a7ad36bd1ee05c43a4b7faa02addd4a5374cc53886376445c9f753a79c6344591a0f78d4a625f83169a1057a200e1337e7a05e490863e090d196a1e2acb5ed8cd2323fd8514d3d0c13bcb446ac827e8126c11bc86f2dc980ae3fc73c2d9ecbe58e149f57a5f7143ad2b5459eef0d82cad016ac8b4f3281f62ee4f1564286a45c55c8bcc4165d351453a9de02496c4ed40cd678aed24a1e906afff1d56bb917120c7fa9c0e6410d62b153ed3ca29f3f761ce0c8b6125cee270409cd88c67f83a8bfb5963b4639a4ce7298c87f6b441829ab30473cdc854c93ab5a87431c031d748eefb4dff3d6da3349254fab6f5a42855b0347ab2c67cc996ccb8daee17fa7f1d442b76029168b9184d84161ab5c29f2e5db2a9a61366e7611a7912b0abe72615a04313fb71bbf542819b0b4b78ab24ae793905b637409b7c3f442f955a7899dfbd7f417af8ef2dc79b2e6a7d1134041fa7e5e3d122b5803cde6ceeda72519663842c544f86aabe17323c1287d6b94af1c19d24f29bfa83b576ddeedc0446f3f3d48f94b9b333c2e99655e771a65df9cd019df98c77f749d46a226bcd884b4fd82240efd79fb4277ab0b38d8ab95cf5e2a649733498aa610acc4861cb38149a038090aa530a89adc718c17ab0f1d97bc3066dcac55a37760a9f3f41bfc9ebeba5f90219712dd18ba839a794611a3f9764bac2ee83a07b893029827439f0125b2656f04245a034bcd33257e13bb78b85d1928598631c693d7c3142bc5a292c2a6d11628e1453b5d319e0be99826db31f4f96b04e40954a30e67c4c9c03ae1617b8793a2e170c68d2d041325dfc829bd1f2afa3e5643bf7d819b20102ea7f3b238e4af5e83b5473cfc1dfe9715ed14e63ff4bda3b56b885d251ae2a4270b70aceceee95e00eab11d0fb04bcf0342e4af935ac645a378eef89d6db6c585d9a46ae17bbe000a4e89ddc7cb52f962dc176d549af8ab7de1ef912d1b6b865beb06cf3712924f598bd41ebb2dca050321d94e58308bfd3c007a8900d21cf148239a75270126155111e71afda5f1fc4a62a9a7006c59e57c9815097ee086322be199dbfe9ded67da0e0c0dda57a7bf5a19ada312bcfd6f0e69e7f7743c2f002a477a94ccff151e20aa6004fbb717b20893bb4e5d2c7f9b80eea07a48b4eeaa2b515f4b771c7ed065a1ce8b61027b1d39644950450074329a02767900175030bbd6f2ac6de8c06e55c0392aa0bed58e7c933ff7a91c1c4a6a596d80bfc75c5f829517cbd23c5dce2105e67f05e30ae4bb32299e8203c86d7a81a5dbd5f1aa49814c15c8a174dc43846a842a664cdfeeff5a2b6737b832b72a0c36f6e4ae6bf23ebc839306c0fa1b21bab1c73876f94cdf105202ed55f59c47ec832f43db69e263c063ed0e559ad2818f8017448c366ad7349e3bd540a2cbe0322c8b6d264fbaa62d71450ee6888e9f9a1de6e83057ae106b79d604dcd02db7cddfc6a41dc82d4bbd2fe7a25e48c6ee05abf2354455a0ccc17a9e06bea101c254525c2539cae2b40455ac7a822beee61eb10f53894a8f2f69e0114555126964d131193337fc831b9eee7aa0d5938b282bf6635e1c6d5afa43ff2316ce278d9e42a88edfc5f3a7a82b1d729a051473105833efa6bf004c7c4173e2cc5f127fd27295e592fc857beb4a40d3b4bdd32c8c6329528ea18649c8cb9f244808bbb6cf778155541066155865f182648ced1be7e939c55293f8694d38c2f827d57155e5852f7fe888a35896f6b25f4e7aeb8115c8c47f77845bac36dc38efbef3d00612bc08a0fa0048f39fc1517316c2b6935c9e7f00613ebb57011656be8a93b6b282da71112fcea018279a8e661098df2dba2680cb85cd439fb96a4e440bc1e5f4494a75e850adbb902b278d5f57bf451c5a865a0964b4b8876539ac9c2af962dd1b14f949feca0e1457b4e7a8cec15b3e94d6f4f9a79511a9314eec626ca43c9bb725f3f94a8112daa4a69b13938de7b95d472ac0c34572d64bd235495c78dc07e90ec4f72122b7f216da9cb445bd3e5173a4e4d2726c009f7fe66cc7d0de35300078ef4d32bcfdea778feaf8bf13d1b848962860f7e4f2fb6f1cd5f30f3638fbc6eaf8919bc9a38f7f487140213e2747ce838f352c966ed8b4df1d4f3fdb05c461b4ebb6c1fbab78277d8bebc293156c9e0bb6c9110f635478a696d046d1a457ca41df80b5ebd90f86963b4351576a95610c459c53287b285923e6c80aa9edcb9df943316b6b950bd42f0b848b0a5f7100a26c2c9892159192a474d54337d5aaf73e769fd78f444520e6b64cca12d6cca6d5944cdd6002b39f43081c3e4629edf69527ae908d8f205e8390fe30024b80e87e72ce194b0d346bd2f30b304335f171a4072b187d128e6b78ebe7652a0dd916ec8a50e7c94df9e5ac4e63757fb61d0e6e29497e82526dc9d46ea8a4d9799fbbffdbc678cce537055b0b2ddc7ffb6e7df6dc0c4ec312a1861b98ea18f5b1fbcf2ffa141b05ed0d7108b7c51d42d83fa0dd0c721ab2c2d580968dd3b5e98edaac3ff43d2990b0325b78e2e52b5db7b9bd6504bad398b5715dd0a3c87e660047dba7dd98395fe8af087eb6c110f5b309e8b36bae3e1aba9bc186a3c451d8ce610082e68c6dc04d99fdbe326c5a7ba9d10a9028295b25840909ffae8866c329effea53ec89111400d04a30071148ec59f3d47e6525690cb109814fd1f616e674353de39e902ca114527b2756bc6a70e840a7fd8082264202c6f499679bcb46a461607512c60dbd56f0f50e2fcf88798deaafadacb34b09ecf53208b952963206bc1aa582e7ea1f2a102c03ba1eded1581e5e9107177c928fed0962786015fa1b932e151f40824b16dc9122c38cbd7323981a2630df5fe372e70c4b21aeab460dcd3ffd43125d7b655e746b043a32fb5744410277abb67d3ae21248f12b0838eb44b81de527371f8d798ac97a8814723a43a58bb20f3eb2a8823243039a7f86a56d889818c1e6818fdd575e757548179cdfae270cc45a774575e9906f1a8bb49aa4adf14446ad008c6ab3c57849f8041cc4156b39440fe43732b0e9b846e9cc148d1eb2d82494a111a088c7d90b50ac3d5337b102dd98c9cc4dec34a799ee9cc56032ca4f458d4a13de73f5d614716b7d940d3823dd78a4f9d97a5c445d1506b67fdf5a284523885049290c1989fdbe3faa98407cc7e7d5a4ad1e830c5d2258c1b7c7f307c384b8623cea2723f450b8bf86114f952ac293e4edf0c6eb2b0fac118887cfcba7875af9b14063ee8ba6e28a85bbb87a49e06366beed9b3e1254c19b13536bccc2ad44559ca81c04210e8adfd59edfe2735c98f41f3c35cb72207a2188caf9391893dc367f5765923b0430fbf72f022da2c73ab0f59a73814c2202e8cad0b80f3b96093050b391d0cf47e07302044171530ae7a14c28671011250f02de05ac459bede9ed2839785a42bc7f724d8ccb14394be2eafcbb684917f26d02f272b5f2f3e4e7cb71b8c3fc1c7628c7a9147978e1a2e3ca05f9ea9c89c525b929bdebeaeb5e02b6a2f36eff1e81cc9ce14c5741f9011c676358e7ea596c1f3e674b4216d3e393065ac6c909af9903b627f682b3f51bc0c804fb2f13a88eb46d1e2a9eed89c6491b538a46a1a585849d1f7ebb1bccb641683ac73bae51604305af71205096d9367f6b624cc540e39d026163b7f2a2e4cc4df76bc7a6217c497dedb071b0affc451e430e7a7ed02d94d114e18d23cc666c9e27de99f22449c5fe9fd9e1362a392903b59da4e09c6817cc8d2405ad5bed5f848acf177b6b9f8bb93a6a0c9d3e9b4403a1120400fe517bac154405d27f16890a8bdbe4793525cdb3beb78eb04b5da50014c8ac2ceb1bf21ce1c65e26125c03f1a72362effd832bd57100eeedb2fa09c8ac61040c0c1ecbe9a345e0089c2802a733da80bac7dd451c82145fafd8e66a50d2138054520d5ba031f91026e1c1046c1253496c4182a78c127ca0fddd9d93d5e4a8c2a7b01378d511d92e0bb2694736e7fee406bb3945388e8179702c2886122034132895c1435dd84e98a460a06d1922b6b0882e3fa02e43309d3b058598cad3da8253d470174391840093e1b73e53b28999f9712ddb814ebf242c75d377a050f2c921b76c0e63d07bfebf93eb28d0bf251e03af5ae1b79939fb52697dc44c16e1dc94cde8e3893bd26441f64c4f05067b52455bf38434cbe42d7189589d6e4844036f22817d1a5952379651ae5973c269502589c79b1b12df4ee155594b5c994c5648a4fab4aa0a116a19dd69d9eeb09b49b6b946d7740938636e4b194374c166cabcd40f01d2649e75b9bea1f892040479cd81a638caeb5bda423e52d98919a343131e397a9d76e2d115526d1e9d0c7584520a3dac293ac760d272c0cd644df120674bac1fdaf8e3f461381178b3a241c6587d2c1a1c90a280627a789c0ef53d26234752182eecd499839466b6ce241a0be09016fc27248bc4fc1459922d4160b752a97a217c4b25070032599f6fa76b47bbf59a98f44e78f46289654decf65c094e554cc28ebe8aa28f2a97a5316c871823712b6af57c33af74e84e7bee7642c10b3de00546262fa13845a12fb022f393968ee8d78fa76b7c2c5a290f5bac62d2e1ef3b7f2e9d36e0efc53bebab7d0189190df91299af7e314a4b423df5cc3e7038d0e430ed05285c5040b93fae4ddcf821c92d15b5f4c4689f04a16d91dec4e3207c94a5b004ae9941976c457ebb43ac7a3e151762600f81195beba60969b974f9a37051a838ca55bccb54db202b5c9bf7f7c9c2174f4ae63b87696ae8eec327cc42f7f39b7aed9661b934fb5ecd795248ae18bef95d04eb2893e8a32864b3d4b436e66aa2caa95fbaf37e3cc212b48d211d466ff79143f80e042e8bcd1d12821f16abd4398d70f72da5719b3f9ed364d365846469858e9abe7a328a8adb546ee5df69f4995d65125072d337b04a89ba2c758b508467baae1c8e908fef7edb0ea04901ae3a75d0d9458f6a6a2c8f882b14c257f2a2cbc5f4a3dd9c0cab737b608f98e5aab44af0245571065127c8b7d87ff2091a6588869f45924ec6eac1e7b7c214b8958dccec5b1ad23c4113dbb7a086cee6d37bd418ef211e768c70dd0a2ddc054bd00c6b4251a4675f459a53bbe2407b23af4594c04d09d55f80c1277b1ff595d0ff6c7883713b4822c48bdac676df8b2363fab71ca592e69da76038f0d686159edc674f7e3c19a7ea672835b683e29f0cdbee7abb995c74229fffa2aaffe43cc41a26bc1bbdb7498028cdf51e394d9c0b204203a71250e1b0618831649b0ef20e0c74909f0530afffb38ed7e4dffe75f626598635c3e3b340c420696615a592760bdd8686b1a1b9316ad71c53fbbe2cd87eceaa40610b063f476e37569b169e2ca439d1f9af9e8d23863b913fc6863d69abf938418790fe3794e3c6dbc4cbece03b5dbd5830be46843b1b8f23b4b623f9294888b1cae37e2ebb85d8d381562c3e11753fcea36e5d0670733c01d1754dc02538c7ac675688cdbd4b5cb832179e9512867a2dfa39581517ace6655b8a9c0a8a98474ec0465eff52ce06f711d324171540e212455d2f297a130e73cc8ab82696746bed2621bb9fb17c0b8e431c0047cb061798722b9f6f58644ac6c735e6686ace4ccbc750ff978f208db885e3b11c765023e8d554724b44bb5ea87edc1e7fa59b3e5a341e12aac979f1e9ce9b793e8bf671426aa250ec03316d2c0f6f77fe67fc60dc7760fbf4696e79a33b992300584fcba9a502fe1905757f15534b347c7b643db3926eb0f86a7fa0ce25f3e2fb880893f9d4bf9a510c2cf05218ce216ae38738f032a86fbefa94ffaa6056bb72948725815fbae3262133be0d4932a70f3d006e2bfd9d450251d25bb3c777553bd59b5f4b73f851af6c05a5db7f02a6274b82bc4a7e9368f9f1ce621bccde0b07f3f19828ec7d96bc8f69891ede53a807bd58e25cf2da372181a45b99b9915535eebb505aac94f4625b002541cc9a7941323b1bd3672337406eee40a16f169b23aee122255369982ea24605245c626b613b968ef4514a747dbc9180ab04c9a8aa0f35ad89f8a53eb892b7b9367440dbcae889661e56043978d24e8884d23f772b2789287049f308d266cb02a4635afcfa584e6c3d27eee09043cda7681ecccaa9e40aae3bc9a3d0cf18a8eb9b5ec1fe460dd495732334c98f35707aa4364b870e43d3386c8a168e4ec0aaa4183c9b8da4d6a3a0afb2f4aff4a6b3a94c518811ca7a291dae6c070c646df3b82095901b755ae0c42428d2361e09fb0168a8564380f20d582a584472ac1ff5b029d05e8c94c0961ab8a6e25814887e0ae9e060866e21a081dcdccc0726903df0ed29092f6253ba087cd176d9c1f429ce3dc77e22031f880949af1ee8fa6965dd25247f815437783731b6cefa6b51d95fd20d4e7a8aaa6abbe7192fb6ebad6a35c8876afb480a06b439b6404612be9b246e7ca2876868434fc244edf09991806a26d48d83e32349cb29eafd93bcc8d177288a7d4826df46ced05eaec1b569a0d1e148501615a79cc77e14b366f3c458f211374e4d4c7962c2834bee162ec637a5a740e0d76432fb9798ac2529337b51495de4f87c652e81182b64e1e4c3019b423bed0ce180f6b544c0ee1b56dfcb306cd0d7481d20f109a33438e80954e63a8725e2270f3331078766f44b0e7ef9dcc86563b896d86ea732cf2e0c8a522413d58bc6941ea3ffcaf643d62ea115e7aa66077a745e2b8ca9886ae8170995a5ead51e94481d685960e2fc4c681e0f0c95801a5ae219a3a60699133158a2e11f52f366cd75481321a4489952f5e092facecd34023adbd40226814230fd32fa02327bbdbded81f017fe8ff784f10b8b20e9dbf45d15fea577f4193b421d69a5bc3566acc88c368cd91a13e509b65de000a6354edbbd58fa8499958096b50d2f07f3924b4fac6300174b2e2ecb1e22936cc62161e7c8d30ea47bf20fee52b06cf65b68aabfcf34e993f8f8c313aeb37af400506d8b5021900317a374f0c7e602180cb1b57f831a2540a95cb51956b5c37eb46a25cb337dad25ab903b2513750379af5f3c0def5235530b06540612da868c1fb83370392da35e812b90eb5d4129ee60f80549e4b2e560a00e6011a740623a3722269444723adea361b792d7c46b3fa6b3411a9073e1ee2bdc59f98ab8820810501d21475dd5cad9261f47d8756148f7e996b990f50ab8c019eb710930b53acdcbbd21b13e33a06c355a938937dd62bf662f4d935c4c389c7f4c3cb49ef534e09af72b6fd03beaa98992cf81d47a2b85cf60c6af9a672524a73f75b3855fb8a852ab9bf196128f04bd9e2f66e8eeee2c3fc254818c16e00e7df69f10259237a10831c3293d4e0e662aa5ac85ae38b4c78b421d58c9da69b05bfb388f943699c5dbdf9ff8a5e0d39910850ab47fc0b5d0c3db8cf14ce6a42be38bebf1c3e3515d9b785a7ebeb4017265e3e62de90c8f59e79a559d6175402a8a1c1a10f4a5dd4ab6cb220eff898425e7356b2125c0477803f05b21e251120087c6c9f0d6c17f6c97d0671a662457ad01940bda068c1029ee999302b96a0e373420479a91985f59c356433d67fe91a00e5f63adf4be4cabe3e58272445e9c31a2c015b2ae9210adcacee2f05c665cc7fdbf8812c92c6c029174a22e1813924c4e90bbd2ff800d9996a39735971c85564185e9b2ee701909225c1384df268d8ff45c96823662843f3e9c096465e072b6bd6307073b85bcbe4c4492e46f93251007bd0e3aec3b9271ee856ca04e833089e3807b48a1b61b6c3f3136095ae45bbe65e912eeca5e8ca5c4aa0f10db888856f30296c7d400ac6f539b352023f2f5cd96923c13d92c4b66d4b0277316669575f985714a601b45605eaec37bc58405c15251d995e9d11ac14e395bb95c541da5711d5c5eb65568f3138b1cc1a2418b262c28118f0fff3d7ef73bdd824e1c6c541d03f927ea1fb557cc7f1dcdda93bfbf66de630d0ff0db5465a6b9b344208217befbdb7dc3be10b700ba60b3e5bb02ace85b105016f45fe0859ca50c28d6f197145deda2ba18c2ca19411a339f9a227f2d8a18486080a7858d200cdc528124407816171eda977af48962fc2882390d0c9f21568169897fbf2ddcb22cbee25b94256c1a326c66558aabb16d02c3740b3d4cb873b3b93ad8d517f3407ff727798971bff8a2bdfc95b2fb6c0b7d2f188b7028fc426c9708bfc6034a4c1c24e911d2c9250040b2a7cf25b921f16455092adecb14c99e3bac5370704e2b2efc1f822e8d4d9e2ae092b5c8abb227277a2322f2cb631b3bd95813d72ceea302f57f8643da67abc854536b7c2f1fd99dce1c8d2665ae34cb69bcc952bd680c01e463756066f982064487e56040127c7c8cf8a20ec6488d1b888dcee872cdf13b2bcfc0f2e52de045b76c429cd4999841bdfcfc80fe8b378cbd64a5dbafeaa6f08be9b6c4e7bc80457fb8974edf3743b913e3a257de6fa4b7a3f5966ed16099f28b631737dc59289e6b4bf9facddaa794623c55d1dc2c260a8503f117a7d087ba9434447d9a08b8df4a71cf43e39551c3bc7bdf747b57434ea1ea31b333aadfa0938c3ba40a97087bbcea37b2fb6f4b33ae9a2b863d157dcd5a0afafa47a6f15914e6d220c66be8626e9267a2fc260e8632e263dc386b8327ddc998fd6c492d6934420ec259ba35a475bad5b7773ec1c4cccfc2377a24bd149ef1d70063da97be44e342fa6cd238b4817358f2c3a8ece75c68b63ea654c16bc669cf3cf44da6aadafac08537b9788b2c1bbd844a7e446411ff127d89e624be390ba3b4993841b27b6c49d147c6a1cd176ba7d76483ad1b8138be4d84f64d929ec25eb27e00c6bc58c465082a87e02ce80595a18485ad65a224ac291bd687bd0fe6edd0d3dbbb613fdcb0fe3c8dd6972242c7365fa32333ddd40dba56da2672ede235f48f2cdb6d22dd03de8424243933ebba4bf7e5d5afc42d22cd7e941d80bb5ac9ad0b3cdfeb4ddb7246ddded1c229148093d5d24ea6426cd6b1a9e22eda221b24b0b3823e5b3c3d1383abb18227381bbcedd8524cfcfa758d9353a9fe1186dce6b24ed139fb2dfdbaec6a5ed124594747b330cc6c5298e01b9b826b208ca897571b1fbcb859545ae4539b1b01e5aa6517a7a3b07894452923b7b29b2bfbf7ae08cf9db5d3eb9b324aacd0b89bdb7da3b12ee2e24a46b2775240dc7d08b4e9aa677bdc3beee6a813e7a259e443ab5bf2299b58dd26716cf5f3db0872cf4d32642d93acd64ebb4ec2e6d7d944825d29ca5123edd8b7844e823aec4c7eb23c7efe4489f1d450405e99a088ba0b832a63c9f1df48ac1ccc79070072936c49567efc8597c68833e488fbfb4a01781fe4891843b9938b117d068e6bbbd4cbb0ee4d1ad7865b7de923c29c59dcc5b12337ae836460f3d1b8d4e37996fe8a14bdbbadef14e59ae4b7f690167c00b49e8126dd5b3cd1289b097eaf0ca46d80be8d608924817ddad337517c8d3226d302f93de5810557512bedc25910ef332099f448f21bd5e84eb437fd826ba4bcc3e9ac9739331e510beb4803d64bf9b4b3c68c3acea545561286b85399e200b19ba5adc8bf860ecf72eec08b753516996ebd63c678c320e79dccbf2efb1bca020098f7bb9cb2a335a01ced07bf85139642aa092e58bfc1eb52ba5cc8b069d9ac8517b5dd07234201acec8460332b2d172b41c244e64a2b844fc9080e32283cc7561810221594b49e2269116bc103e4208cc10c0c27c70e1bba208ce8d0e9a9e2c84ecf8e8b185122357e4e0d4e840d23344c80f1f3f5b1c3112e3c5832193446be48a1c1c59237520e9192224fe883e7eb638624427a76b7c901019027f2cf9810ff6e0363ed683fbf083c14d1906c8ef12420334f7e8f5b66d5e8262d017bb4ed0a906cdfd262e18249240bbca60d5cc5a1fb73925c5dd4c9631bdc818ada5284bd9c963da59e3d63ba465a59cb7b3d66dd2f842c19903cbb2208426ec0542cc4473ef472e24971657cfa545d75c482e24540e0584c2b13614106b43e5503956dc22927b5f2e21b793812322c036c293b2ff64bf8e514af827432995c0111290690eca54389d689fe61e95fd85c3edc146d2d35f689f190a65159c48426fcd79256adcbc81be3c0e98a3b4def8595957317cb5b6ce3ad588af8f38c6ea1ad6638ddeba7923cb1b90d810f52d97b895599847bd4abc04ccb3e22ebe8aaf38466219d3346ec02cb1bd01734b17f4c91ea65e7ea6611e7d0f9f969019fe4636932536c4fcc34bc0dc8778099863f552710cc501734c05e19218242053d0410c151e79c26993e7bb141df18aaf3746a69bb40f86d4018fcf6bb9decafc9c4f25cff722cf2c600f4462599a6e796f4549b7ccccbf36491ed2c7bc6d085fec8fc4c8b127aeccd6d123cf53f1a75766e3ce4e7a7b768dbd5edf17695412f1c95ebe69dca226cec34c2ccf5f55b8f5f1d86b894aaee783fc943be04ed2e4f9ce459eb8236d5a73b306aecc9b4cf7bed835f3d42543578c9a1871c6ed5d0367a4e0b5803edf27782cd4e7bbee3293da35f171f5a0adbe955a8f6df557561bab7ff495a296d2486b8c466a5f60888197170621ec7e5242188525ad68656f56fec93fd88669ed166f73f293dee2db586b4e360c2bca2c6f7583209ae00cf804e9436779883b999780594a9b14e8c831025b64fa89248b9c437e4e188190dce180399e526a3bc17a0aab7c4adcc8f212325fc721b3bd3537eaf5d5295ae983d1593e28b184cc5803e6e99b3fd9e072779a31b0833fd580d5eb0ba54dffe897b0400d780a7fb2e720a6a79893d5464d7a08e71be24b0bf79d28f7209d98c2f7e083015f3477ad0d5e47c9379f7cbed77b3e3484349ea34a471dc80d0f9123475bdbd65ae303711e8b2a6a1a83998f32be0153c93c8cb1d60e421bf035ddb2c437b20c672a8439b0665429e54d69ae13f01270e194b75c9625a948732567ed768961363c102bbed85692a2284dc6396f2b9573e267658c29c48aa65b1b5075a8de6b1d5b2b1d2aa55942fc8319bec793b11680b67c9bc5a52b1e5d839a49b323757c620f8f223f4a70a00d8f148d46b0870ba149a35591dce9e182bc5287148a2a90594fe942256700451e265917517206194d204803229e0ff2527282419801c182362831c29432c42f03c2c55ee28902451c230643654d05d90fb21f6441b220990f321f50616488385061840a2354f450c143c5112ab2a00209153dcd5161848a2b9a7b4550714422e15d1b57e01bf6a0a7681b5b344dd3b9711b41d687b7210f7a0757cca4b85d4abe72777a3e48f91e0c197bca48c907adad95ce94520ec9a9d4bae03c84104208a10ee6b55db1af29270f2ec01869b060e54c86f0c77d504697173a1feded4e72ec77bf71a55d64b3545567eceaed92d6453e97070352f8e885d998f1466e0302d2800c800e882d43bc2d705598b1e5016179e0831134a90d79fb0aa331939b06bd9c72ceb781ece5cbc4175224c8647e6f38630baf5fcfec0166899f173e850b95809a29e5f554530032059b81d79363ee141655843c800728fcdeca8b91ba2c976780a00e6bd8df8071903aa801500f08bb613646994a25a89434561354f1129de9af8a6134e4a59859c078e468c3031d45e1fb91be9eb0a7a8b751d3b49447b53c464d7bd75fcc46bd7dc56c4ca06aa69418e35c62f2b8a6d099c46cc427311b0f5ad1cda2c4657c4b1b6dc303397ec6447c210401cde6ad8b420fe98c8417c864b7e8a852da889797a7db3c10d60bc0948f8155d801bc2173ca83f11e8c960fda214440425160938da08492560b01a683db5df9d9be661a422158ef75bf207e4882656124b8dddd2d25758ab2da0808638c31aa7456e69aa378e6459e87944e4b12a9554a225146890439c408890449244a2231c64b9f2c21067157ec25269155764c423515c5107da218600ff0f33109159348b97d6ed4a994ad7b9a93524a297b70bb46923b99f9c38f1f7efc00831f7c74cf0f30f8e1c70f3fee555b70672c9821618aebd257c4d69a466ff91dd3b93bf27b3f9ddbb9b18e8cf1a0938931eea06996fc34f7e0cdb98d3b99b637f8457350458a37fe91fbef58ceede04d06e283c407098f1d385a368e96271b8d8b30c28ef760162ca883f8dedb29a2041e9cdcd88bc580dc4ee629b5b8d2222822821db90823d448214f4373d5f0e85153434343c3a3a663f77b53b05e5bafe7a494d2392ba5d6565ae79c93523ad920a59492464a245236b4b0e79c2de79c6ba8b6d6aaaab6d6bf9423f4f3b55a6b6dad94b5554559aaf204b686a01505249a3bc2084893848cf560923e407dd7d71a63adf0303ef002c38db7666735b856c63721a517177ed88161b8f1dfa1a78f1bd4698e1e3a7167f2833af47047a7598288a21033f9876691916b8c5cb95cff5aa746a813573a4629a10fc4aa450f9334128efb4d4868c4104973f43db748a6a71da5fdc74347a6f4ef47924c2fd32cdd8583890bd224d12c1484548cb656996679b1b728ae1b115b2e7a4aa911f41d8410f44414115b2cc7590e7a810c360d6fc8f4162341d236b7089ba6fdddb287b722a669d9bda15b084f59f6ec5e7b485d8ffaac5615ba46a3afb5dd30c3a77b7fdbda5fd775dd2cc606410829e86d2fbb8d685c9917397b2814023d93b920cc0ee1d63591e3bde205817e6d7dd0756134de7597285f7d3582b2ec5d61356cbeb7b22d26fbcd70e846cdf5f7622fd7ea7a646d963db4dd97af84c144a797f652d4346b2189442299dc64a36e516af2d72cb7efcecffdbcf766ef9c658799656599c9e106b3c4b0605f4fdf69568656b8109a3e213cb44ef0b0fa7df510655d0fdbb4d0cd2c6b74fbf770ebdf2ceb772fbcdbe8702bfd9220659dfa9d25780d92ee6128f46eed333994e11b35879ac8d9083e1bddf7067ffbd9c8a6b9063ddbe2e17681fe8255d88b3ce81083c1da255e42989a09b4a560dbb9e6264fa7e5ebba22f682fdde7b98972fb6f617dbd7cfe42db47512349321e87a5f4fc3c295b6b9eeb418285360d4d42c9df5fecb2e71762b6234e62d86490c94615373afb3837ebdb72e03bd418f69dcd98744ef401f892c9941d7aaaac21de8a08fee1241377264ec0fabe1d2a2ed46cc188e911f6d58255f615104238dbd80703703b3e8d84d98c4685497f7a02de63ea6efb12d347fbdcb70371fd24ccd3dfb108eb9af1e637f9bbbaf7aca2a69c82dc6186fe7bbc1ce4f6ef0c1a0b35e37d1ceb677bd2f2c631993b8bb0e3a86bbeb6d1d04bdeb5ff4b4be4d9a13b7fefd36f7b69879fb6a33d94ecadcc90c731f6648df9245c68e9dfb5ddb4e36004605d0ee383bf7cbfd97d2dcbb0d0d6267b0aebfcbeb6a16cc20f9328669d875611bfc253384580d0bcb5355adb9f6d5a76c6e0b95e076dae7539a45d2197d4060e9086ed737b77f34277d34873367d3f8ae56b8c35e1dd46173bbf6a15d5e5a2c8f8de07657965be4ae7fb48f19dc0eeac8f23670060f1f3fb2bc455bae4b07518411ddd23e7a45bec747c9fbb1c1d1c972a70a277464d18d908720783e84e08c7bdb737d4c4a7e8210a25bb4579712fbf5ee0db90c538c8e6bd1f622b2c93ec241724bd13bd8d1b6d72bd583611be2d0c01c9ce6e0e1cc6deac6a216242f7b2b826ec13ecc11741a395ae146ac1bc38e61eafa6bd02fe8e5ecb2b9ec322ff229621b533ed1df0cc11d1c1d2806291a309f3a5bd8c2ac702d4a25f6f94ed2374c5b2bdc6e9917d97e76bfad205ea6f8d48fc1229b8b8dda7a014db6e0d5db751f4a72c4b618ec16c5ecad972dd263167389d84c86588deb16ccee953dde8f36255e44d89e06a6f3e2e4f8514e8eb893814c640c82aee5743319b3b04b83b6ae896cc1f8f7a6e1c01ee0c82642780a7b51a23ac4603098eab0b98bf1e027ab706327f32fbc57b0a0f491d81226cccb8f978fb153d3438eef61d083a104508e877959361783266250b8b7639c51aee009b7d3bae12594500a6b3e93c1b5a7978fd8063ab6854eafba615ab85d4a9e5d7f8ee45a6bac5998b7284603f4429fb728c55e1a143aa8b197ec55d5a25f9fc975eb2e6aeb70dc3f8c9ac9d405bd82ec7541db6559d1aa2a09639e6fce080d828541e1c24c06579e6e33996e90c7275b51668bbd24e146ac791e8cf71c529c6830f1975f5c9191df63d0180f7fbef7768165c19b614fb8598e5862412405f96135b26c296069e42d1bd3adbfa6363ab105d270f7b6c2d4e6deed8642a15b1dafaabaf54046aef5ac973d71adeb92cd324138f57a32749ba36410c9934596b7301fdcfbcab22c18eb2159adbbc4ea2e51fee0b544590459051659bebb419ce624cce915794824cb9fe0ce44d337b1264b7883827bbdbedbe5f9f0327c2b277fd49050919377d24868eb6cae769aa5b3b24810792ffbd6c8e5691edec99e2c83544c891a5bbe986beee42ab7de16ca300deba187f083d91262265bef68f1b07590c6ccc9ad785db0af93579b101964c7a56e217682a9c7ab6c2fc7d06dee7a7cab6c30439a48afa8479ae502d121d4268b1c6fc55bd1d2b275652b87334143433382166ac359d7ba15baf5eb9006ce50b9ac2e0f45f05878b8bc99f4f77cc8ac93cec3161f675d65cb36eb2bd639eb9505439b088a10c442f8ddcaaaaaec2aaf6e61a02c002b01e0818787ae823b98b3c7c3123257bf91e50a1f005de12560ce7017baf598d0b1ca0eb7ea91ca63b2c3a6b2499ddea27f7a45564ca86c306f17e284e00dbc81379287b770979743e415f288cc42129145a411c993250f97416410b9238564a71b3087be84ccd66f64d9c20078476d86d86ee187d1081dbf945d9eea348b1584dac41689133bcb84ca43313bf0701589b3c3267578d8648e955d32880c929de0ba889759d53b43689166717119244b791aa16339b89dc4c131a1e3c7489de6644e73529ede488a93a5ca7db64ed22bd2f46a8342e00e0c8223bf03eeac54c1f0a639f98be756b764906651b93cc94e4e96e5a182a94d7394c7bdbcf257157e006c4934278f371c306f184773f2396cddf58eda501bf9cb53a9a5a49872cadf4bf926779a93af3629a43919c326b6407b9f0e5806694ebe83349d0c92036c8878ebd2a6596e288eb504cc3987ed65175bcad65da99d04c9b2833759deb47570487795b6ce244fda94a856c39d952248f3a25fa6ead56265fc7019f62f0c60fd66d87a854156c526d79d4a7e7d7345dd64739646a539115841f20f17f247929f9b8cc114ecd107c364bcecbe73082bcb3e86c1884e6f7a59f6cbb29388b1fbe8dd63322955d64a8ba39bea535189b428510c7086754b2b1b936479ca6a69dd9a286d80956f894fd56d127125deec22719aa5a898c45e7461302ede96b44fa2b3f9e4af5b4eae430e38c5d28a52d4854d39e12bbb56feb02aab9252d29fdc498ad2471f3843de4625b48b49ec672563923c8fa373d3575747714c9f3a959d0434ad7d91925685e5a30fec611e65c3ee1245dda78b35e1ce5e13dc5ddc994af8c71d17a242949c330912dcc95c12c7d4ee36ea6528844fd7b3963f3e89d8727c01f0ee7f67dade5d0c000c6db0b5dbc5a04cbec360e463288b0d71657968933dfe8a01ce9098ccd548f2c5ef05abd7b5bd5cadcad653af9fb55ed42b4b6b72ec1a49eebf7e677a510c3d56ce9672eb9e2512d9869aaf3866a6701403ec21498649f2dc92885c7c7c7c777c4ab063a18a14fce48ea1fce2add81284bc1d848cc26230cf8da3a83910f2563724723b19412ca059ba670628bfc508c38d6f25110741f8696b06d1dc027c8c686ec2dbdddd31c31cb4733437214d73f34c3437e523bc6996ee2599357047b39cec5fee1e11c863faa03e65926816ba75ef489e2fe288eaf348348bf5f90a5cc08866b93ea957eaa2b32a6201cdcde7496401cdf259e108deca9cf3d48c419eb7873a1e8cf926dc96b263c3ae696eeec813ea5053d05b9488e690688e1ed11c8d8f39524a296c41a61c782b14c7834129ed2cbcc1813950871e06a13b995a718322b9833e287d5964fa239aa502cdd25d179049028966c14e25162f6c1d616b4509d11ce53215a259386aedb9d8026ff056e8a5c8f60799c22264fa8ae92d86370f06d6841bdf37991e1a9182ad2c9d9505bd9a2069adb5d53b57d656d6c2aa7a652d865dc7dedd31c6183f9fbc2e2be68a62b79894a1f0a2b5d6ebba2e7cc21e733d1ebb1e7f5d98ddba2b5b6cbba83893ab4de6ca14a680c01eec4da67789f507f4417bb05364ce9e73ce0965c4d9b367f7fc9c73ce29e3c198c79670e5718ce6e667912b1fdf4f65368c3490b7b81f6414d19cfcb342965c08836c79c023e2c19037c0c1440e48532f0f7bc01fb026cb2d200fe863eb64e44a55970f4208cb5aabd68aa264d85ab98ab9ce668eb3d7746843810421376533d9645ea8f4914db3bc1289a40111c9f41fb6a2d1b5265df0a659fa8e6c4637ad018133b49c3cdab2dfcd1a5df0823ec25ee8bd0fd55a7f2291b0c8e6564c0f7a22ed7eb4cde427ba77892e1d92efbeacc768c9599b7db5bdc7bddb889927bf581991efa6911c29dc3c0f86bc0af7a26e138eaace49734cb062a62b0340407edf2836c07b2a2931bf144c2303d73c4c05009c70d3c4153983fe7d7b33288647600f74c6e3e4630063f0038a6e7a354d6c51003c0267c8803dc8cb2a6429adf0015221cb5b3da15661989dc2f2c8f381a75928dc3a4030b8251def05f77ab433ec216e1a1f97fabc6deebadd948052be697eb2bc751c12e4829e0f5e018d5c9025ba28924e4ffe4c47b9c95144505c8a9dd2a3348d7ceb90bd234b2a895c6f551bb19e1830f3b981996c310774c8149f2afa8ade629a44eef979bb7ae3eec6cd4b9c3245eb69ac98013354c7faeb131342bbfdc48280593b7d3c88a274a3b439f141a73846bbc53128a7ade188520305cbe64e70866573268dadd2ad92d516ddac1963865803bab64efb33b39a844918dd4834469a3dc68f68a4a353900be6b126c2a71ad8fb98688a44a277d44b007593c86d4fa7d3f67a4593754e910877d467b559ef6b6f677da8fa9cfda959ac611416fdba99dead4b225f20ec2e5d8394310c8d6cf08ae6e44133b8f0485440cad8f286bc0e8e204b88822c0f5b90258c419642b2fcfbb1c91dccc9523e868cd892c34fef6c4a0ed80536a5e0137eef05dd609e8a8e490fa08784d7de1835f9eebaf2ef3d8b8f680e893e00fc7bdd8c68c1cd524a4a25863f7a14017fc88d0811dc8aa10e1a8a85680e42165ca8652362cb6532fd1c7745218828c26616237a057e4ed9f86423471c43b10c03cc4d668c84113d9000b1e076170f3c0422e519a05b0eff43b73c20e017d02c2fcb2c02d99f8f346fe5e5061029c15b79b9cb1a90849fb7f2b2c41701ae49c640ee53b999d4608747493492e52bce7dbf52899767ffa04ffb984c11f2882dd0c78707843c200f087d20843e10fa40e803b7282e86575c30b78fc974efc39e70bb8853d3b2411d133434b048efd410e91d21cd023fc400b1a659b03edc948070eb0c90e507f05a62cdbbcf879766a1c2270617382b1c0aa6252020cbc713bcd08b4104ce07d781c906178a2c6d64419627596e59ae886c6e902c2f5bda34277b27aec85b1f8877a61862243692eef6b1d747deea2cbaa791b44f6c8953642984f68947c8f2edd33ccdd32c3d21ec35d7b86d608e4e07e99bac27088431cc3667d32c6ff2e8018b6016fec0253adada078b64194d9065ac7930baa704d6643e705a07ee341cb2c1ac81e7438419e4236f32dd7bad7bc570bb1b8118877b0b5e1b6b24cd8de2fef5dd608f5e91b74857c360446e077db66896ee29c9d2075e5e1d5288a0b87d7bd00b0804318b290a3b05028140f8943d0674fb0c04cae0d65d19e2909dc51a93b9f28529b875f6c6dae08fe62845318bb5e664126e632be9c370653472f549fed3dec2a6a051f1bce669235d336fedbd26d3297cb5cd6db6e4e81579cbdd1826191fd2dd48acc1f91f7b64556398e0ee19e134dcbd222f755d137b64c4e08e5c91258e44e2105b61d933858f1094bc5cdf5d48de09627b6a4f4f4f6c89d6dedb53098935b1a573f056e4a7a891afb88f005ac1eda44ebe4e10b9235f7371482c728591782412a9aa1ecd597b88104434cb02aec2bd8871bd7c61a7eb2f5b55dc22fe481a5923796459bdbe543ecf074ac4aa17ac3693cf4d48b35ccdc24435ab6d8224082ee81d0c0253716e177bba26b6c02513a482fbe0922c0f878c9ec3bb182ede7129ef8ef2ce7462bb26460eb88bc13d05772f1d0577a67bdf931f9db6f6c9b969ee84e58fd8226d856dd7e8e8e8bc9c05f6a2135b648f44c1f24873f2279be4692e8be664cf096bda2bdc3563b89d3492e5a794469a25e309bd626fca4b1ff384d32cdd95f3a3594e2ee1cd0996f3843b0b6d7cfc903aba58b3a359ba0683ecd12c3d487c924425718b66f9894b248d8c43641c92e5adb991bae6c1905813ee6bce84bb97c5e3c9f3fd1d8f3c6aa954925787974c3f6d302f574739d94e557e98490cb7c1f07c88cf622ff3d646e55853b5959a50938abeda281cd8830d05a49a1595436b55cdadda08eaa0b6759d832c3fda3a00647951d764099219068142e85b201012de73b97b4b5a071c23776f49de1ebcd99ea5369c0c73323c7c3f1942083f694e1d802cac8ad3c35b89b7f6f68081e1f625755dd4df2f8aa2ec7f6defd13ab6854b5da6ebd44dd74537b9b6759ae99d4db86b229be077afcb2447b38cae526934c2a7ebfa552a456962323d9af0f550c8c4e45936cab29b989834914d27dbcbd949663a29957e6dd94b26d31b7bc94a77e997b3ecc464cab66ee646cd2777e9ece4e5eca6ed7a693bc964966532afc7badbe8a2cd243ba6854b3d898bca28eabab2995d5945dd8a1f3d9353d9881a35f6a2994c26133e9d3cc6f4ec27a66737e193873e932f9313d366f565529ac925d2e8d748ab9a25748a129daa7c88a0b8b76096ddccda2076511745559455555545adc1d2ac53f8ea577f7b2979613d770bd73ef44e1bddde7eb495ae8dde7984bb262e45598a844f14752acb2426f30877a39b3c7a7f843b99eb6edd4cbe9a76108804025dd39ac81a6824cab2531be819753cb2d1b21108748a128d46a7a883402391e8813edaa8679b0824aa74b699c75199c2127ba96eab97abea6dc12bc6cb402757bfdeb4b0115ccb9a50b87e512a6557d8768a9fc911db4ed667b285b127dc1d69ab0ee6e4ead8568f55180c4e73d52746c3c6e6564854873bb17289d853a96457cf6670bbbaac6ea57d655dc72ccbc2f1158e699d9a8621351684b0a3b8b0938d2343dc9d208c651d5e3fc5f463e82b0b86516c084f7e43f2c362888f6c75632fb36737f602f30c4e7d412d6dfaaeed5adb6d8ede9469672af3e26aade50dc9b3e535b59bbd70a8a7faf7ae9f99dedaa839e6190a561e5c08b3be656530cbcf076a11bdb3133f036130d9df73827a37c7cef16af5f164a51de451258ea187f1ef7ee2e80aaa15d3c37732978df937f1a946bfcff71ef6b0dbf75ed3aa84d0f85eec1c7076d8eb9c0ddfd8ac38e6bd524821dc91b1531cd3cdf90ea9cc747befb13e8c1db45d176da1cbad1f8410d2834090bed3f7b1772c7b27439b94b92a06031f03c2177cbfba2bdd5ea6993a01676031f5a03fdc65358bf3d64227e00c0877c837d661bfb6eefe893ea63f4ffb33190728c39dccd82b7402f68051bf360bdbdb7aa2f835d133598bac103d2a0b2a27d264b72c79ab8b7104257e99b5c96996f77810f612e5269ba316f5ba61bf1bece26649195f6bc571e57b55bd8b632a0a089c11fa13bd8a3095935fa89379ef557d8fd83e7cb287a7de23ee6abcc777186b8d540e0883113d6222785df1542743892e0ce63d46be4b1e84450f771ba0aa2c72356915638d9d830999777247bda270e08c77d8513ab9a364557147e5640a9e92c71d9593a9ff55f9e4e5d6c9c418a95338b087780a83c11ee8da36fadda86318b63628d7f507cade7176795965193e5140a84d04c5ede2ad0e7b676f3285af4df60a751056e10b539635b3a5d95240e08c0ef664ea9dcceba928aab2371490774943f336f8186f6fbae53505c4da442a27cbedba31d4a42a348b1027e2f940c483f1ae664ab90af722868c1f8a3c24b702d7857550315cebd55da2f552d1960bd45b4cc9edec49722d3fb18d2d5007d4f16c46a20246c496208a987789725edea279d2ed34f3cc627097b0403743611b233ed9987966dbd24251c5133fdecaadd50baebe568c0bf2e53ed673ad53c7b6007ae92957d144d79e6d5df61b306f6086f4ce24dc3591495aac6016e56816d211b93b912e9b2b5de6451689f089443aa954d25e9148c73011869d4422754de492c9f63266f2aa64a261d75e6dd8b5eaa56d622f181ed9bceada5dda2a55954955dda8d9c444db5ec65edaaa6b9b497357c6b0ed867e37d2374c0bb73e89aa62b556179b5885d97a2b620f9df49a85480f5db435f6222a954a8779b9844f2637291d7b097326cf3e93b70e47d66eb2952ad236fac334d23693b551e855e82ed1a545367bbdaf21bc59f002c9994c55b5aad556cadeda7046f781dc3166b9c53c25b6da5b96ac91fdf770571f71746ed8dd97f146ec2aadb5d6d7d30d85bea9a364d17adb6e7c59d6ad6b61d62fcbb27e5dd82f2b74eb2fb68c4499955ddbfaa3edf58a68cb7007c232a6d0f67aa5316c5f856566bade7169a15d59533bd96db39ce5a22eaa22dcc50c5f9574925c67c59dfcacea84970fada877d79cc955cada3944225185f7d7ad5724ba20917c772f6bd73b327508a7083ef46c0b617c27c4a2fb18b23e0fdab66babb056b5e620c517a4a95b60e1851ef542425d3dd4a5c4be4ee60ee2171f0a76908fb8526fadd802696e7deb74e3aed94357741b7aafd8cddad315eac2165b1096794bde921d711341712d2c63cab53a85a938a6c270877c849f59468c7555028e56813d2a6eabe2275350adc9e382507063eca735290be279abbeda90d9e24b0b5831c48139cde9c4c31c9d66e9276b0a64bdb4803d54ec93e1414f5c7a08754028b8f0532748f528abcbda2e0ac9f161a1454e0c7269017b807f98c5570fec414e397b5c89537137b324db77b1a64a4b5f2bb66f88927ea277348ffcac8841cf1022d4c0aa6214567fd4037960f3409e8ed04810cdf51530f00a1e125c53e6a2c947bea259faf048ed183b7684102302f4f1a35b6c4b38a4b9ce16ffe8956eb1700f0cd2a0105cd8b255d6c69082834aea1a2cabaf960a5722971bc6ce94abf74db1c5c614ed27b654f8519890bb725189233ec12b217367a25b4a34994638f9264b7c13119ac712951d432c6e9b2b3f8c07d726b6c420f682109ee2759a8338cd4178141ed78487bde3630717ca1d4ff4e477040735553cc1f322924b5fe4a5acc50fcbe6a0769add2fc822a7e17ba42b84bc908e101b29d5705f9e32deda60ae2cebd65a95555dd5856155c55e2c56597b5595b5d57555c7acc7cb82f10755d12a56902467f959f1e32377fd01c14905aa1fb3da95698b7c2436c67d473c8a3a967329dcc95098bd284a0a8aeabeae0b77f5fa4cbeba4b402db9a8254bba56aa5254373ed990a7fe4cee1fd952dd52dc0e2e29c1ede092a6d151b3a3591ac71eed23ebebba407311091c2428d02dd075734fd46732157d408804096ed73faf079a72104b75901f10056a10a863c3c723cd41aa93b92024c2ed601012dc0e06b1acad9bea7e3dbde160104288bbee9b115c1a1b908edbbd9ea99291882d90a757e2adbd97a707f2d834cb4de3340bcc11ff543f15fea9f04ffff44a7c75a8925376ced0859fe6647e10ffece0baf44f7351c7ed20cf0f8f0f7cec2ce093c0c70812c1dd116b9a8b22b8367434d73fbd648b34cdc5c39fd8344da48bfca8a0105c789393a1922d3a78935d624bf35893e95e6bef35996cb655a060493e407e55a0c08a6c41d8a357e2618ee9f5c01f36b125be79624bcce995782226213706c9f171b60d8d1f305c98fb27c73f08dfc9cda53918317872434f15f95921041f99477e560c81060db72f0425b0fb5d6407ce11f2abe20448f2098ee457851357e40eea805d24f729fa00897830227ed4dfa3e6abf33256d8cb8b35462ae74df95eb70975e07b30b0296ef7e22a29a61468a5d46e8cf142a8036bc3958f14ac8c106584524ab933b5dcef237de4c8157342088358f23e126226f5e4754ef9f90be44ec694b3885fb77027e7b6d8832377322610f662d24c6eed2dedbd43d3a40e97fd44d64f60303bdcc5a58b83e2535e4ef9e95dc3c3f4006c3a9cb499cec356ffce30ca65a9b4a33449a5d2c97bc749f798a51394c943e9a4148097500280bb2e954aa51226c94f94d79b1e710763cf2887181feb2c954a37edb07530ef701006331fb178d3ab2cdd74ed3a946ae73092f1d5540d394141d95e3e8931c668ea1cd3d43f4e6e5935f2d5344b589a4c559a6e2201e09da632690240fa4d972728241396ef1db007d25d6200361dcec366fa0edbdb81741a15dc591600601d0e71624b6fc7386678d6594b379d74976e9dcee11beeeeb13ce926141d50b0e934f6e480eb491fe17ec2a4bdb54fec452b9d9c904e3efa49034e4e642e812e41ef1ad143d7fa8988c194623ccae71461a413094f8230994ca613d308f71329d73617583607fa693b99fc6510ad17c6830b27057172f7401cd0bba654c21792e640a351f3a0aa2da447ee26cd9288274d6ce15e274daebd625d3d5d2325965b34b7e43af5115ba22d511fb97f24f25c5ac423daa5c529e5a3483ac1ed9a075debe426459f6639d178f07c80293f9d82a699a774318f5eb1d3bd9e09e5a5d2f64e4c348782d2f98472d91cca2166824f12a39c9ca48444ba65b2954a72b34c36d31fe68093ec27d96bf65b6bad8ff6a2cf6494ede4a49f6c33d964428a1928bb62f4893d20276e273b53d72ded753553ca55aa0e0f2d001ccbeac97d0b36a6d58fec8e5b2f1f1ff3284952be538dd2492fe128524ec25de9a71aa49b3cc6f41a26273d8674933f69914ca6934c276d30c76b622f992563ac51abc0f6a2b98e9732c61aab3c156be49a7bb4dac7eb68cdc846cb69eec4148707e37a476ba40e499324779626463c69624bca9b5212577a94336bfd69aea5c4758bbaa4a2c9b44649ee2fc91a105880889fe81b98c932fe46cca59865d7448e19ddf437134370bb1887dcbf9a280e251313d3ef7662c2b2b967d55bff6a8da9e1cee4a71ada4b8f39f9cb27f854a374ed31da4bf5f596a69d5cdb608e22e52e3d4f2ce843c50f1286643885fcb018e2932d7b51ecbe985cb4590b7613b6ba4a27d5c94b5be7ea044b982beca59452b5975e2ff3a2622f27f8a45dc3cfcaac8bbd942edaee9f887488dd5ba1cbe4143add64eb1c3a6199432429b9f8b01a9d49f82471bc4bcba7dc6ee91a29e55136d3269b135d9eb4d9928bb70b9275afd7b75b684d09e5a592754ba55b2add77be255a135b6493684de928d836572a7dfea2bc622f28f854bac9c936da54489bc96669e2f514d3768964fd49cedc4d622f15c36a74d66eb25d77bba24d238d36158c86cc1ab6a7f912ee649e2169c75e4220b8b46647b384dea73c9a45e2d3d45eaef67b91484492f2a1b991a23667f28c9b4b1485248e12521ecd35ad09d1dcfe85599adcbf36aa62eaeeeeeec62fdbe6f0299ebece64a9e5d700219a7b3fd920e2c178bfac1e94ce41093b8a64f18d616ab83cb287bd3c5bd9ca9eb2a7286beb9cd6d66dde80f6d4f6624f6f1fcc9b12fec88da5e1be8ce513ac3e0aa6058804facc1fb8c4c6891e28d0c9af8a1d3b59467e55ece0c9981a7e2e99a33998733b950c6116cd4d9ee6aaaaa2d88b057a4cfd3c088b6c2ef5991cb70e479432565585617ca792214e4e968f3937e2686f32863b1abb81525ce803e67863ca29fee5f97c34cbf3d1dcc3d27061868185a1e156d7755d57160328ac33b48e0cdf3a72efc8b021a556de4c1f4fbdbf183f238cb16b42bed7f3ddafa947e54c3adf1b2ab9fded343fb3c45027c39d0c61900c77662848b560c9b69c30829afcac00028fdcd90ced10ef2efdb628645ee448821ff94b86a0b93fb13bbb527a88d5a0b9e228ec1fd6c40b20a490180d9ae5e72bb621253eddf804eb35cb06608673cac3889f32c6dcc7f0d5b0019768d83162d85ba0dff1a193a38de2525ae1688abb53ffd967ab8824d22e2269966551d2368fd069e4c8e421912609b4912e4191748dd093f0adb0b9780a34aa1689443d25c5d42cd4744d1cbd45daba2660be2ecbd2a8d61cc534a539537397b56a04d5506c1841f520d049d4e19d469a8bf4f66aa66d64cb70bb67165d8af0e82e3d8d80ac6df2dc6bf2348b853bebf5f15dbde491db3c62bdbe9b46acc754d2cb306364b8dd34d22c348a94d794cf23dd9282bbc73cd22d1088289b07911b974425710b3b8d90f0a9621b33d711c6b6703b95ac9d9e45ce095dbdc19ccd74ab545ffa6b96d2db84f46e849349b886b0212c18ed158e6c6efa19f6d235f4bb857eb291dea2ce010281b49d37bad7701ee8be8a0e04ce40f93b0d213ae1aef3bd96931fcad689debd07028970cc158140156b22dc8d44273df4ee37a98240352793ae6518cce90d3a914227854e18e957e8440a59595cd1bdf7b173687527772150e8a46b244d2777a13e08f4a785480fdd8a3b2d27872a7e31dd85505c80dec10cbaa12b0ae1774dee624bf9c956af6df72e1d23474cdb5ed680984826d27b267c22bdfec6fe7d85b0a7a4bd1bdde4d26bc5a1d34e26b1ada8b46db5b5450c198d04002000d313000028100c888462b1603c281986393b14000da1b24e6c4c9986490e44c818630c011010000001000010040018d4f3d8b44e15c51b36eb3474b49d030e513955cddc970e0217fc91498341fe38757064ab78827d7ec7751ca8d6843fd83e132d52decadebb85b0d5e8877b363d0fc0e6cabb5efa74a00f2444ae6b8336b0ab4f6744347a24fd1652fb82e1831171e58c792ce8c2ef36afa16302f8aa553c679565d297fb504bc22ed7e65643dda3791f71f29868435f7d79c060b2d281903a6e0062cf35bd5d43a5db27add816e0e8897b736259b506fa9d65d73a78b2557f58171c262099a1a3359b886e3a66f41c4e30f091b72728685d76822d164da5d49091ce08a1e6207ee0ff80f10398ca30fb31365eabbbf15d7da355bd355f3055fd47486cb6a013eda35663a0f7c1b3fa2bd5e933f0c3b2cc66219361c7321044cf43523c3980ad0bdcbfa823e438b0f2b161ff3004087c54e57804b7e8545f90fb93f119c8b3b5455f4b193aede098d061557fb6e02e0a0a51f03a41497887b19c5727e8d7fd0af838615343c7836864aad0240211e8b40742c0b4d4a9cc331269dbc92a91b87b1fa7d5a8679182e736f4880bfa923ad5c704e45d9dfb21b8fce90d644934cc673c0c796e98489b1611ce1b22754a9826ee74a1385097e1557d8de15e5f56a714e81c30a1c021cb252a2194a98da15f3a45acfc4d2f52e8408d11cc4ab6aca0a0e3c103ec6f51f2562777588320dde1b54ef0efb4c104cd6772baacae2ccb77c8dcc702e62a0c9e400a27f421824cc052362805982345cede15e9b3e2fc4bb75c4c3f314a2fcbb701750e177072ccd228fdd5c8888a82554dafe9fbd14cfe8cd940d929ede5a46752e403add92465b7a55637e0a245ac0a0b28d6939e35400ee2b1df118beddde104af32414f4f1489b1e2f7b4bb6caf059e6e69e6f1cdf98eda469b925b1d32c17a5b31572748ceb32d4ea0c8b100a83cb085153e8a88dddf51d90e81a0cfb15fee5ccf4ee073135808463a0267fdd085b282df928fe283f7180582b473896b574a2d259722da497c4b41f404c9c7078113110f9646ecef57b177751960b849a12ae8bb7887d20016c51e8294a43268f269e9f7fe243e1898ae0375acf02665f23c7eff19c27926f8433a35fbb7ab8e9a021ac9d505a05bd7593b98ff8b7dc8582a20f1956f81d8e3606d95318352b9dc927f999503fd96d2e81528df1e66c30b8c95994586715e35279792362f0c482aa08ed7708b97836336434f32d7e19eebd00dbb1c0a4ce9c1887c9a08ea14b8dabc0e3f69e551fc7da1bed1b454e6a410863da5c82bc46b270d9456696d5cbd13e5ec0966fc33bf640e240e66d3773becbacf6b5dba82bcfa2ba6368d12c5cad8fcc97d3065b574b0334fd8e8b545a21f8adb7c3be5d881f1cd4b216e9c94797383ec3a586d4bc82ee325662d2714207d81f31a98d5a546cce8adb90c592be968afc84a90189df285c3ecfb5106ef4008c2e995d530bc87999686c6b3be0f29d04710a86cb5a69cc93cc35c1014a723281c1c438d1a41211fbf2c817ca391346f18e862899f508425acdce14afe999d5ec178851c8845ce652aaf3c47d74703bcaff01078daadca9547557831438f10135f60675c8190639a843047caae8d91c1476056bf442d0359a643cbeaab3b1fd7b79a10c6509acd760f6a000d5a2b45938cfec461e5e1f85d2976b59c80b96b7dd91c101a3bcdb60971a3bbfa5c2dbef136e551c057fe185aa4dedeb0e8d173bcb49865fd115fb9c9b1f667baa5fd0467af9d3341b91ff42cfdd4ff8c7d04c3f56be8dba23600404afffc63a03467641e10c9612897034e20c01db0bba3fbe37ddf09cd6656e4be177cef296ce83c79c1a3c2b118f1e1017adb2c11b68016ee0377630e3a588e7a038e4708d3b5f2432d93aef208f58a9611fb935039e7c85c9a372185306e72385ecf1578a36143da3730a329667d33c234813d8b0ddcb0365a9023a30bb00c3f4e63b0a6ec4ff5ad035a2f5adefec00ec11ac5f784d1fba4bfc534887390f3ac94c9c5332da1a82211f2a6fd2e01c4ac092d900afbdb8ac19e8f1d56df257a8149a5415d6981a2727604abd10f88df5b9c4e0269f540bad8b8f9eaba22cf34734e92f4271e68bd74e839f934674edf2757af695d90742956d4e6c0e3d48c0cd5e41929ec7e842de0685ec9c2d6dfafaad3d73dee61b28bce7f1ca57630079d806aa71424c06594b59adc9a2eab13418cc9d007f61621f8b05dcf00e7fcc66908733c597cc20fee048319dfec710c40a0055f20e3047dff753ce2c479e6306253f037bb3b3dee6aaf1e33592e6b040066d2bc3d511222c9baa2800efd892804c5e86e6963ada4eb44710d5b4d58328c8ca275a189f97e96ec9fa9e5608dc5a94c45d5beb41a875886ddb09b750848fa0a7431a9e8c57e2eb8d3c6b69a600469e379dd0ae347a424e890b6d66dbbbf14ba6417a97f80b00738ad0bfa6180c4e27652667ed7c36064a41faa101b7b3a814e775b2df61246e8920cefd7b23dcb4644d52236248521a9b8bc5948d147d9a8312fa90d5e38632e0214cbe24e8486f593767d8185ac0fab4c5060cb5418b9978492ac24a538b3a997c8bc12936aa4345a951a6c020890d83031c2962a61b9b01d0fbe211411d62d8ecf2afba9bf0eba8092dedd20858190f532a66add448456c0780c4a603c49a1e1aed2a953b959227ff043b172a9dda00e4916c03e5615aac650ce8496f269386bcbf968e95400591ca595c59e48dd400c351c1147506c7afa8be11c7c1d287217a999298644419915df8043a3dce916d5bb9c982dff32c6e352b94eb83f0c7f9f1458dea70b35b7c0670aa834c0202506b28526b46d8e7f44248395c7d8a512f05a0b7be2d9363748aceddac8deb4cd893dd40adda3df56c44fbcf390d8aeda2646da7a8e1f909909abb49708c51dfcba66add79f2791fa61c72f858d435a48daa3898b3f9f88a9106f2c835912854a6754a162c71b444c5063299c6d4d3525c7b8eb0a83bf1e124266f51cff439396f370b60b866fbc8f296eac4efabcbd78bd1395782df932c2c78fff3c2f798323b05e6c25df991fbfec5f420886386abb031e1663006cf87b8a07c085e42a2293d8982f53014118acc5547815bb3390e39358755038ed22ba678a078593d845537c578870fb64af5a5298d1b94899af625ec8e9ad0de613f7102799d7210a757d4b9e01f35aef2ec43bd4bcc087ec5d47aad5607cc6667052820fa9d4af6fdef44013d8a573c86f0d3fbdc01b2eafaa73be4d3c436dfb1cc730e24827c1e8abc7d07acacef24751d49a41248ce84013e7a9fed244e397bf7ce7d59844d593ae0d8565e05aadb63b331bbb922954419bd9a8d2509c841599a84ec1498b8fa8c0b216594918dd94630f34e3d1774ecec029b92e3a850c065f16818d21018b68e3e88f4a2d0b70fa2f25ac870b820e74a8792c0455dcb482b46223ba66ce7307adf653f497089c966933a6dc8007861f5fada900eeb1bb9d515fa265ea66d72157f1f196e9cc649cffe9b66651ce21c6feb21dfa748ebd75c115013973b0e91938365878305898a5f6f96fb333a7a9d23a8fdbbc0ace23c66a9f33cd2cf992910df44f550a109028fa31cb77d9c929cb2e3dd4475132a2e1a37d7c3a300a21bb8599f2fde6c1127127b7ae0f10f1a475056d969d7a37220a4921361bb646ef04f358ab22c254cf62b0b3a444e093abfa8ee41d2f1075ee45543dad6c9a1fc4c961dc5e58ac182a426fc692335b45b261fb2fb437401db8ea1c1abbce24bca604e9188c219422ac41d01aa1d0464d96c5c5a10bac7a6bec649033da0a3a1c9bcf738e5eec206f809f013d17f849a22b5dc432f4f0d27827ea0724d4f4b59503f7a00775a9fe5797f79eb745a80ec1d7d36c9c92d9056367d2fbf471d3a83f9639af81e73e2f2e5a2760957f774fa40ade2c57af471b7268912621bd4259e0791689b6ff7180adf13751f6d555c56073be47f170f7711a00bb30f58035b11886c74e7ed988310d429987912e0b813d8b41065bbaf387fdfcbbc48c9eee3afd0a4d64a4c63a50667423038cd642844e459a36379ec701e719ce2cca9bdb9fde13ab1317233c367d951500154cf97999d94a0c3e03905f10a4b0805329dcee9de63152f6d4368f4f63139a772a4f7cda26bb9e742927bfede05244a6ff4e6347cc14dbbb03902095c134460f7ac4755319941b0a454db79400528c247947aa85d7116e0863ff11d8d2f553edee5997aed4d61b2ab663a631f56d21d13e8f7f468119852990410c53f161099a54c0fde0deb1b4637cb41f1b3d85aded0742381743cc0a003514fe12c8f88ae1e610968262bcbb4923b6215dd1b01cbf61d26a52ec7582c4ff78deceb9ed84c8d6c0f1255a642a5c1e12df9ba42b4a6d9e61fe188f629784e9db9cb01686fb6acb46c3ccbea48635eacc597af153e9fcf028f19633e565e7c5e19043572e330e55775d2285ba2073fb44732b501b1de5adea1a8d555a7237a292ce7b2c0e7e025a14526e2b6b7cee4a9058d984838d24d5250fb397a0449e920bded150d8096af831d1d15ecf02977fd5076a29adf04e988adc68d88d1c9c10de38242495055639f2744a33ef44f2c62171a201cd3442a2ec6aec823cb241bf324c8fe112a1acb07612a2e91751d1fc2edb5d6a4317a3d5a96dbe5840352388a50314044a86ef63b31d3729ecafe4dad240738da386f412c4515b91b8afdddd8d4ad6d0fb580e967cf59bd847d6d2797b9f3bf35176308e807476f2b80bf03c26331b5a3292b74373ad469ab843c06d3f2135a1632442fbe6e41055cdb7d7aa7653a061811098df7047f3fd8f10594c5635acf95d7f2f84909557d660382bcbf1871961562928a8a9bc42de260053439398452369e0480fd6fd42e4ab29da70c1c5a2d99b1493546f7318b5175655cb4485b9947543eeb17b2b4d6c91df17da9806862339df9331a2e52d81bea5ab909aa0e4bae7ad22f43247b79541a1b27f4c7f1258b99ce7442fc25b6fd1bb300d9d18df5a56023ad669990dfe7553420905e7c2e5f2d133cf9c4446e29207f14fab57af7edae062e249bb409d1e189d1824c0d681b6d1fc204215b3eb5ff80fa25b4f05a2fec4b62f80d9e1fd3d81aa3087b44227626b41914049e8fd53a7088f6cba505b006ec4024bfad47b9869b07baee2a8b8386ba7ae58eff0078af3453f86d1295ce302d90a46f2029e2396f5be08e4d8e5c028e37f207607d416534c00575ee8a43eae7eb1df90e990a8c69d98096b983462c0dc55d0f021cc6435b4ea570df79272f7b2d220d19698c8e8472b5c9fcfdec290458acee3c5934d39a95998c38545748963b30540c0f224e694bb90101385567d78131439d0e031a1b49c3272644c6ab21013b1f0ab31a7502bb116f9ba1afd95e93d7f8fc91b6f013556ce34af85185a975bbbeb773fc5de83f3cd94ff24cbf9e4f36f0014cd690e7da73f4d62927f04b42c6471364935c16349af4e47d4c9573f87bf0d1834b142d1cb134f7be1e49d729c10b9bf73e29180e9df8e5414a1beb7812194461047bdb258c8c3273b4fef944546715ed075382d4511860fa724e6ca022151145530ef90c8c54d6bd0a51a3f63955e32c94baba4925a45d1c93ba51312f95d85a5ea2b305a602eeb59da8ba5303ee66ec0b6ba15fd8fbdb7534c0648dc198a5bc852e4eb2f85f935b1ea1ecd531e8f3e945eaf750337e3faa118891d823f1001098f33dfa6a8c6f845598013209ff449d4a7c1ec1c43325feab58a907eef64481ff63c3441541a50676c4b4a5c0048055b9368449c3d9d46b6f0aac162006abac96209e66a2a3bb5706ebf48a592a31863821c065c37459a697b761a05f9e184a8b8bebb62d431a357e4cdbb83cd83f9a738db91153b1744bb82605ed296a6ad482e694514793c87eac79420d81a61c545b2278049b18ea204c3490a07172ae7b6421848237d0a95be46051f097c59e8ed29587cc1cb3a054cedabcf035de31102c2eef62594dd6afadebb90e1a825ef4edd7593be6be867217cadb93352296b0af893ce40cac4759dfa33acf977803aa23414706f2211ac53d8282e68a736d9b2da93535a1e07ef407aaf26eec75fd8e6f50562bd60f85514a607db7bdf688b19aa17da951dcefa13f1805f9c5b451904097847842e41684a1914198a3c1e85f5cc320c751b2696e936eb0c0219500929d0250d093035881a37f4787786b1c521df43e270e9eef53ede0c505986a7666766b9aef8e5cfa249a77aa36d6fafa05b284ac787a64aa30e1359469dc6333cc3b53e0b83f094406ca47e665bc983aa23b70822e796d232cfe1a77e60cc1e73a88a4b242befef5f8f9e4953c552156176dbfa6cb14926d48244986c320f12cb9496a58eb70b73809412f51d465c7713d7c90ca818af49e24129f81f59a7402134c8cda87f1f40067e57f3320b438e6086afacb256986743c3a962d8496b839907034e3f0319c0c38f20cab802ebe14ca4c23045f1fb6ddb937320b15fe294b4f0095dac63169ca77045d15bc750d0ab724ab188f303c8b48bfd115416ea30154a2db07e3baa60e794112c3cf31a3ce758f874ef42c7c2e1d5704148c6035486be410dbae426050380aaff6ce9fb783a614d5101eaca23011a006dbdb07c2a143c0e039b4276cff66745f5538273a3c8ac123c7310be5507820dce2247bedf53ec5e08a2db4edb083c98d623d687005664addadb48452e20e83af39cbe54c8d39fe88d1af8d0ebf4b3d5572c8bcc9949ae85a9a01963b2578d7870b129eccafca9558453e3ac44f3cad841ff4f7e30805e987efae60fd2fed051cd7a2a601c75e5824ca3b4e3d01aa4fad13f1b88cd1b78e250c57acf57012c8879c1ef03aa6d6daf1ef647a60a9d8bd9f767d48ae6e302071dddc64ac049966e2dc5590586b681e0b6accd57e2b58c27d1a8bd5959366b09a99ab6fd1683fba1f25ef42adac80932fbe0863cc152dfc377434c35faa0021f5ddf15908f0e7236191742ed5a3ecaaf0120707eadd1889a9809cb021593db39421a283c3655fce0bac2c2b0160c8ab703c70802ef25d0e37e42517d3c0c2dd82d4ead3ca94cabf0a63839ada04f30b8a65cd556677972cd71d470f99c23e464c6c32cc8bae147a1b0bb171c5b1796a88774585e7e5b155230e417c0df938dfc896b6f4b0b5a9a2f61077d3b54e47fce85b71b7641364105a28297c184e00f7b6ff92f067f95ad9b3712e0951d2d903a0fdc5c77e3230fa7d82bfb58c03dd6a811289c077db6916d467897bd1d33d98bac8bb518c9b0371b29bf85920f8791503dbd790aef479937ad0e4fab34e8d7df475b562b7f8ef0b296c83a261011c8c8c56effb2fdcb476ec0040dce1f0ada437f07d57f721827462c78ffb62642cb892555631d4544f5451a377605b06e903cc7ec8f9ec342a9db0c746a9f631d06e52e7c9e95e5842de7bf4c76a87e8a5648501365a538c95d366a0ff3869ead2804bafdb26cf60174257cd56791df74c298527911d7a33d80dbe703b40524d7705a6c049ff4cda1ccdcb3745326a0c0af48f16d7e67552c6091386fb09b65282697c76ebf1f74675609050a663735544dc42214f436c2239c1e4ec0c67270b47ceb81dabb070c40d656238affea8bb7be6f2aa6c57e43d0f9f080434397e7f95ebcf737eafdaba7d38f4b9279a922384739490d1f7d88f88e45ee6afd5cff7badf844a38c86f3ef0ecdfb52b9df144b0eb3c39905e10e5cf77648b1bd9623a75ea4cf564dc7e4ba276d8326edd38cecc9d9d67f67eb1834fe294a96522414a576ed3df2a9476485128c82292f973172cb131b07c0a602f928244a0a023bab824551bc196795267c4d3ceca32ac365d3a61d22fe6b2e5272842c47dd79ee2511c8a27076ea13fbe358877737b82003bf065f4f17d1800c2bec1faa292639fcedea63231a4f12118692730b71ade94286bb1a7b9f5ff1e2c6cc4ac0df7d815977480f127c9e3072cd022c8d11ababfabe65018865051e35c9ea731089909ac06f0fac50c9f1be070b12ead3280dfa299534f2f8292e62d98d30a0cb090f11acd52bcf95e2bbe9012db75e001df4d57bc839c94775f89ec6232285e4839a81594ab21e2707d5a645fcaa13921fcf19385404dc929002bfc52fd47d583013d01953697001985ffe1a65639c2655ae926243450ede4a81a9e8ee38c2f97250daf69734c4f2298caa098c34c4c41ba64dee0ba8884270e4ce623bb5edf3c0fca3613044e7527e5c08ad867b2e7c5f8fb96417c68a17c23288d27d89861a8732e37360007fc11b8320c6325ce157cf0e3008ca1176f7a1ede953ef94ad3b1da00e02971981df701de6bf6a90079579b4490d1d97245518b07699040c542505a824386e06311a8718a78a315cdd5b1274ae450e63573835b532b6342f1c230150a265e54dc998c39084855d0222ee9a8cb9c9c2727993ec9dbf2feb0e8f86a20d082e0fa1d6f3aa793938f6dda93f01e85a76a5f98730f6336045c71c8156e967c2c7e28a0fbe1956bada5e8eced1c52a43b3edada8f7bfc8d5ba9ad9905494456e2a27e7c0c31f4cd8d681498c239324c0ed26d073688e65a14737ec67b4222863b65df1cbf365e829ee76023e4a6cc00551729dfa510be76c3f28d17f5445753d1583a165c9df699f617864bab665e5ce4d0011aa13e41e95b043fe86f2bf8c17b5ed0c607f9a859c47dd51a4c751e2884ad041e4358219fc4f135f26fb681636f702f29898177cbae4961b2b015f01dfa1331101b6a42832f4ad0cf0fc138522a5ad63171212d8176ad2953a3995cf73338ea8c5df905ee93983fb001e95decb0ba9d4619490482dd9eccd0000ed84d63efacaa8689bbe5553ae114a2f423d99f1eb7606384c756e00df9d7083af4251db259f51ab65ec1a569ce7d649fd5e29809070442ea560d2dbf5945dab89a1172ef8eea4cfd45aedf22029a9a7e63d8a9c5ec60b0926f9aeda6fc039a3bba731bd74fae83af298d04ef386b2227db162d383544594efa415f424deb31f23a55ec8b1cdd544f07ee7478860f4082d54875d5fab4dbd7c97f8e2ea4a348cb751df501d0105b3ec1e942241028c6c323a7e6cd83c89cc306031816b43395250bf34dfb63b747ccb824fb800f1daffa1141e546ccdd78214b1a4d3e34cc70e7f027b9fd3abc03872bff08b4487fb35ef15947c9da7d26676497e9943e9956d65570731937e3cd60908e50b0582f5136618e38e07f731f071da635b7383408bae3158e015425b4657eae04bc7e87abed73161bce3f9342ab740813842fe56be7b9be6716887b41d095b73ab0a2b4b298b77b7254eabf087d9825ad5844ba66c8000ccc5705bbc2de541e39155ec62307bab4e361ae8deca529cc2e7f0b2ef2e77b8d1d310049beac49c4ed74fdba863534137d60b5096b02809d51fc4d355cb9ea2f37ed793acb9ec2d3c2d128c2d903bc11bbc7512b2229fbc91f8e4bfb72e364e1813110cfb88ac1e9e447c5ebf50b6083f606c02464e422edf93c27d8d4bcdd748ac9d067182d5ca8d416d6bd680152ad30f204a11a0309c923d968e2ccd1b10fb305f4007a7deb6e0092108a779437a2fc6782a7d8404c563e8be4981450f9efff7a25f75a80c16277df8b2f6a5a47a6028bb5901cce29e57d0940c92ca54d37a52cd73dd87bcf9ce9ca598519fd82b7d89d6d2c183fa592afba07a02735c2277223ea5475f0ab219f12cb4cc523d356868ec26b2832aa53ecb9615f2e192ed157617804d0c33c9ee556543664497e2a41bd7ecc56f28ea3c855af6115eea54e665f50a9e0f0da9d3489427ae36a79ea596a4544fbd4032802c26853599bfd3095297ba22161770d8552e0d3481267477cf392357b66b887c9473bf813161b490dcb2de9a40046f8ee9304ab4e452826c215ed95e249732175fec2fae3f09443e574168de8f2ee3f482054c3d31b776e05da9dee60ab3d96eb17861ffd5f0c9a5c55498636ff2ae46645ed0d9c5de880f88019e6f74e3606d44ff967d971f8545dd681d7fd6aa22a99727b650e1736babec29bb8b907c071f387892c3e962d07ef97297af991c88941ce582cadc1cabfa97055c4df17494fa7810b5f55124b458018d32a88dd6a2c0440de8a2dd0dd5d7a2ed99c8f3c2924eaf0e82c2cc7d1f450f8dc44c539f9206d8b167b11f96be7a6ffbfdc9afede64cf9a3964b53467047a641ba611e0eb899d19a176b631915d194410b02b2e3a0eef27af1e7e10b103d0abc329e6515369dcdb1c38ea110542ee42d7cd5013b53f6c11742285bb43edaf98dfd7fbb63e16df341e7c93d05d68156c16c490d25d57b8753c4271670391b3658b0b6dba4a7f7816995796b48e82cdacf3ae7100cb02f4a0d724ff2c22fbd95d343cb551776e6b6329e55eb8fed880498e004099df4a3ae4305d967803fe16b3ef72a8d14a0a641a8e74a708229584afc89533a636e6567cfee7e3b9ef6fd9486ff78869b6fda446eecbe800c32ac7e0bdfddad29bdbe8e0fd81098ed96c57f47fc23d744f2e892f90426ef62c15aeb1580fa72d5f95e0e172fc125bf6cab7509779b03f43ccc1cdebbf468aed6fe86932a3d4e8bc663e36d262b18666090d46b33d4301af094d5a24158e65d543fb1ddb680bdf66722fb31b81cbb7179f3569bcce53046c423ec4c20bdf826e41b0e6e96cac9b0bccb2a5a516077cbe8dd31fe87682cc9bf5ac109ef5bcaeb7a0efb9e68486a888646a616944850111af7c1781446c3f486ebd01bb963bc30b51a30cf2fa832d7eaf8c81d9bbb52a2a6a71fa52aa59fa1cb27ab14cda5e23c1c18ec08a44b51e5123be5c5d20eb73c59f2f827e940bf197792c6128062933d336bcf063c7cf96ad5fcc4e8f9b09f6260acd7af767485ef16eeae9a07a520131ba75fd613a5b4a1ed8e318aeaa10db46a2e15b68f69d93920138162f67404498a43cf046d8cfd68e313ce513862741f3d1eaa54187309164fa5d0765db5c220108dd77361e1e223ba8df6a189232007bac45c75d28da23ae7abd382dc62070c508ca248999c62070bd86114cde24c452666664c45904c2ce5005207e06a5e77e33d8abaabd35234760733343aefc5fd7adbf6c67809129bcaef4ae04ceb8d1014bbe8839cdbb0800845925adc17cb8d130f6e6b5a72fc4cc774ea11ee475159186fdbaf47b61bb4197d5d600b058019178fc397b32c1a6833f7ef15c81f381a45350441d9d9c67bd5962b625c247219731f4838092f962ff42224b100e10aa6d539cd0eb3405b953d823b277599554a416c797711ac0089390bf15c0dddb6ff24214f982ec9e88df9d9707d996464353abc67ff5bfa0a4fd539c166cf4c941c4e2eeb0f7371ba25f28cdc1001e00447e7c895651d8cc0c90d7fe0fba6f31dbbdbda36be0d657d7762287a51b70bd925f547b538ae1be245da25d74076c4bba49e5aaea10cb23670b5d1037cbcd6bf7deab5bd4ada001f1c9f71080eb7081a5d1a230ff2eb090ed6ae9781b273b8d0e4f465f07df0c8b09cb5b80881fb077d436df1213614942e16b2516a23848cea14407e5d41fafd7c5cb590d6c6609ac29fe86058fcd3cbc7a40d7383f3b116b631d6972fc7bafd6492e6a7a7e50a5564c477fba868f8297490c6c48c191503e0ba34d4780d577459878465bc70cf8636e12d318f9fabfcda0ad682b4ec7ec44f8a661602d2a90e550cbf222912afcf0f7222485413d7f9ab195ea55c7810c27c410880f79c38fbc505a8ca4f2ef4fbf2d21c7c186b9f8276fb61708a600a786f33c6f8ab36eb1d723c08f246aa9416446b30d64f1f7a4b5e6eeea68bc3dff0bbdb0567bdd20db6bc194cb751418a569f6d8ea8086895dc58b2683f3037b83ddea43eb460112c93b203ec13cdba5ff46d5e35a99fd911e8b54a42a15aae6260f3de19b54151591869054454c2822fd328eb6ddc62e49d82fb740de58788804ca6b073b530b21296c8a19c0871221d936c0c95eaad450116b4db40b10218ea8465f618e83e54ba708730a431db07de226de53369f932607488dff1ba62abd9869513f18c682b2f0390613709982d42812f9dd6affe103f8a01429ad2e933dad03d85ee537d8dc0dfb79627f70be60f8e7da4a5a6f0c462b8102bc8dcbaaff8e12cda165ba29f3352602a97a865e0304bac8a0541046337151c249f6ae50e3a1eec4850f43d7608caa41d3a226886360d6ad660b22f6e5540ab19acb3fb3e544d013243a4efa1ab892913eb43f3c1814a3524cedb913d461073aef032a748199030127cc3a705c7c9816eb6df87a60c5ad93ea6d0316c151e40b642a82814fd483e1ac51002200575c5c3c28083007c02adec50c308d0bf4eb47d3ff7a9b8699aeb92cc9ecd4cd37939223035153fb6f83ca5e385deb95f71e4a258cdfe56615875ffe3002c3b4b7c35b5f3251859267eea7ab7392152ed4536a7a8076f2f1648fb54d74b8ec580c3f5f6bae14bc18198522488f9d9529ee8633224b6e58608456b1f88f28409508bf044e21617b14776b0f5f858d8a1d38c1703bb413f1790d294a251162896adfa83b7e6799f71c2f679b290580138be9dfe7f8b33510fcc2fc97fedcc6196d8ac51dee0c7b85860dd299b1cecb79b4f2c7ff79ada3abc4563fa83274053988e5780107fc595cbd36f40df106dabc94505a0d1394ff0682a591e606929e0bab2d52edf1b68d730458b71e0f7772882843c208ce5f27c5f1a68345f5540cc3d79a65abb0d9bf3cc595d6f6f136c87a38d05095b81f9b3015eaf6974b2009eb51a793361e23a6ffcb41f311e6df36401a837c6048515cc41ad447269ad7aa55b6364602a330dd986e4673d270ba0653854290d424003f02bdf1bdc6869724557973421f9915cf3ec93053c7cb0c6f0f84e302e609c6f83028664e624ba63f70663d6062ac77d261c04062e0ef44c0d2bbdcd6d1aa9b917e77f809c1081f9e101598099d900a381bc4d8ce1da023ad65f8f954f8a9389bcb652916f156a000a70c1bbc1e3da02f435612411b0dd18084dd825392d7b6d8154a768094e271075435b93ac8e9e5ff342cdf1fb4b87497eca1fb8c1b46da2401e96d3de9bd816775f630d6123ac58601c71120fe24bcb83fb30bc29be0bc26ba2904e1a149e63a934bd69de81d20d89e49975077c819bc73b6180fb7fd00609f50c7e3ce3771fd1f4dd4840d98cc9215389dbe717b489fbd941f02897692e526060d395b52e0a7b5ce27845ff99a21065c1a2e1f38883bc0ddeb98937f4b2788862e18327178ec7525b52b05ea9e39cd8fa44467a028d3a01f70967e9984ad7d1ff92d17219bf82e0dc771b191108147c86c7c681acb8a36771b903474eda4a7bfc9b6ab33cb5b49eabca65338e6d508e77eec5ec1ec703ab24a2ec900dd9bfb4cc0abc860691fdeda2504d94290dc40383ab035f6f4e665eace094d68b066713800569aaf357fa29c5e67b05fe270b4e59cf06f6e70a593e22aa7e482372fc394c563d4b990c53afb8da0c662da82d85f976fcca7088c64558e80a63ac20bf497c7b3766062463ad52e00836eb8060aa0cf16363ba6352848d8d992fa965156febbc092843d8489fb4f9e5bcd127d92a4505355a0ed2760dacba42bba7fb7b446251a98a94d006525015a6b6273e19c636b62e015d10cc14e49d329f8c32adcefe5fab349783d69ba0ccb96d8ac33e6918c21a8e6c330c94c4eb8b249b67c46836d264f8ad0e3d0427a2427834c3fec1f0c8cdeb2cc0b59ff04a0563613e015fafb64505a632006e96aa150a56e14a2d0377c4c4db724a292e1d04c7aec289c187922eb16531686b3ed055ff0252a557438b6e0be68794d1edcbeaf5be52550c64ca4ba7786664c2fbdbf0037a0a5a885f91ace6696698abd1d4c71231c66793e1ea6fa94f6c70fabcedd5ac0c9474e63be3ae7666fb776ddc29911e4287cb84202c3d1da254ac8398efc9f5ccccb62c20829eb0bec16e816daab14f903f9a056ab31e6685db716dfeb43473186a9a3eeb068943bb1fc01d68ac6422bdb728ede9ab932fefd874bf9018c701acec29e142911667b4b22b6fe03f9fdadb59f69f2530875d2af4162f4f66184562fa641ea76a80ca4bd1a9d9fec85f5c29d209ff3af68c5fcf215b2ab2d798b589c1b8acbb1ed8fafe9c7894598aa20813bdfa497c79884f03a29e9c3f57684d41a67828c605012b39f360e4395fafa09ecc695394a0a647c4e520b6f6fda10992455a85b5b41511ac3f2c0f9adba525bdfdf7fa7afb928da4d6c3d9240a4e0e35de25aa9d87b13a70e0fb87b21d580c28c403f26329ec1ddfe239af9b2515e86613b8c8bfe930111a5b066a8584eb11a18da6b869504340a6c95b356d00697ff12d366e02bcbb6f1ddb5350c8447c018435d6289e7a9d7bcb088c1fb8cc3449aa5e0e4d4c3720374e6c1eb76d81c13f0bd514be11c07c2528aeadb5cfb3bdbdda9d1c78acc375afa0d8576372ba512bac2778f96be345dcb6657e553de651fbf68e7b5df8d6d121be4e280a1865ac20a647a73e6170970c8b69bba9d80ab9e2828057a73efa0f3349abbc6c5ac561af98542d7de42a8fdfd58125fc28ca7b730715a3322f0d1f209f34cd850ebbbf1cd0a73cd0f065af4d990bbbb5b102f0eda384ea552d2c507eeb2637dc22ac5a205fc0c8400ea5387fc9a4b32eac65ee897c7a95cdc368d87f4878368117d501f68f8a0ffe5cbce11685b711582373fcb5b542d370e68b9d7a5d756ba13fe94d2430bb671d83935ad363710a43152734cae8cb30b96aed7984506845cdfbde1d442e3b60c7dcf051ff1024bc823174ee0cf3491ac03a47f8cfdb03472bb086ae03f742836ab63150373166f09df7f28f0321802cd55c63cf9f7a43e9dce44f79cadfb57ee5a910d164a43502763e1ca635449928afdb4352952490f65867eb805ac1696387876822857e2afa3fdc142004c66480c3c7b8e28b2dd668d9c63080b568c83482a2db6c6d49495a78cc03f1701448e3863cad27a79b204a72c29aa6ef4a8c9cfe816e4a1c6e2aeccd758fd66919c516619904b3402e8553e25d169779810dda0409ac5893465d809d1a56c5e8a4595f3306001eaf001419c047819686b68daf6405ab2e8a908a5bba06182f89c3fe1b85bba954923a5f9103a5a07aa8dc68a8d505dd0ff6663911b5734de86815c43f9798e8bc43dca2cb0db35b35eb7a259d135b4d1a4e0b72ba93eade6df6c561c015dd0b8a18b4a339ea94da6b1bb345388193df012590811c9c558182ada0ba995f08ebb66c311edb020b12225c82039495a10cb916d9e7cf0206712dda3abb97f63445a7f0612ac4db82a316c37ed9f0244b89b695f23a90a02075aec45d9556445c6003d42674a641f9402b6b13990965cef6479ef9352b723005efce8113204fa6a98777421bbb11fce54cbddf3949173eba987fa8de4a57c22c3aab95b9318ab2502e076217f1e115f295e04a5b5ddc41d7ac74e1aa1a4b8e2a97590c95e6949c0a55ecadbdc850fca97967b1ffa6e61ee7d8b5376ecf1c241125c3de180497fac0361350c0dd0bd87652085b136bf552a487cb73a93f50283a1a3f514c4c503670839fc5fbbffdfef4051811f4e10d12318926d623c023c73107c651e2f79c82c3f71038c2dbc3dd1248112ef292f25040e44b1cefcd478f9d5f865c0a8c307b16fe1d4a681f199b9f21b300d058a4f7a0aa902c72cd3908f636733bec81d98d73c0d5f47fbdd84e31b3fd860e54852c2d16539fb7db7b92cc70daf25c8d8f74025496610ada1d55233d74df1826ef415916f51d48237238208eacaf9540587580db15e5e267f7a606cca89996bb9381788679687ae179c7c4c82b08b7cc269fbb231b4a612f5367fce328303d8c7d3af92949b049bfc103e347ab442e124934ac1da53f1f03194efe3575054336926b141b3b19362a462311ddf0fd14832ce09e3f3cb28409c5b30b5f8260ddf94a51348ce53df432a47e441a366c049aa5f7829de50f97d7bb3d78e54ea8a8f4a73148fa2e8c21866550efcf3cc9d066bd93076d172daa723da47b60540384980eb599480d8ba4ed3b44951a082dbaf4ba92428962e0efeb75c54cd84639bf2b49dd58738798d424c80a4a1a8052b278e0f8483559395c584989dae02f5234190f809e76918f01de1ca38c9e6789b6a0318b8c5e6586599b2a88dd622491d10a539ecd3d64a4aa5a83159970ae52842b93f7528ed76d33ce3c380422cb96530e54f9d54699805610ae5f21e74542f1d21a400ce8a1a283b603752faa10d4777808bc52f9847fc6e412258c3fcffb938e237785381c87e358079dc9edc6f7fb4df276ff72dc2222182168a69ccbdcda2f29286f8c264983c9486fa3ac19d0f4f10e2b143204682a0ce795ad4135abc5e77c2a4906785ec16431d27df5d9b5e3c1ee3e99ebe14e6114d7c92df508a7dccdc3b2466bed60e5a4fb6a71121525e27d093f5a69800a2aac42add42bd4545669ad327c380df69bc80ee4f133fd6b3457e66370628ecff23aabc85c39d510407b208dcc8a0242e3b3a383a144871fa80e56f01c687b747bdac997ce968ce73ab97e050aea8d24439c1aaac67c8963baccdcd3b5673179def6f084bfd5c4e04448ffac4246f1ef9c344a3e9a946dd4a6dbdd46c40d9b62ffd92da41edbc72242af970230bbbdf11a461b83a237abdc087664fc5950342f9add882b3e464a36889fe63182531a8fc1a6170bd7e8c26e8bec704b52aca61f3af4ced469f7410b34e0296c6b5802d2fcb4b238f7282ee9c8aa71f1fbc318befcf589c03333aecf42219e434c873ccdd0bfb39011bd37313a7fd06f7e73558aafbbdc1acabaae5d7305add71615248da94c6ff747402444ea8cc548d1f7c257a7ff1e6a2828b1284d1ad355e47df96d7d0bd3343003f61041ca04b071b42ba49d6a811ec54c37355be3cafca5246c52e4d4d2fdc92443dede4413e4201fe1270bff8649d95b7ee09d100e688697f179077458718f597625192408efb79c2e03107a017372be5c5461d38ba1f99647fecf9f9257befcf2dcffe254c33149f23275e83e338530b07a07f0e1ed28cae2704fc741d37b82fb812c47795fbcb1735f4d46e1e146bc1063446c5197666a47e707023fc70f91744129f88ca0ae741936bb0c92e9e9b69190237e9ea8b80cd0f6f91bd7395f0e85e55593eee0fcda6f44e58e5b84ced3a745875ef89a9376678cf744fe64b9f3440038360dd47fd5588f4ac5fb788f372368ad8fb00ac040a249870c0463c38c8b9ce54948092c131d7c425eed507835bb9d8ff237d96ac3dbaf83513009f00b6d9c21790c28e30abd494bef729bdb4dc8fe0728b1d714ea5361e9af98529a1a519ca11f03fcae3c3209186563e88076e93204bf74bff7bfde5181a221e61efbfa9ad842efbe215ad4359b63c2e6103157ea348d4403559b386d910eb836a2260f4ddd29fb56a8fcb39624f9be577e9ecef75992e796d8645443d7bb8fdf178255331fa8b209b44adae2b6f134c0260bfa09810fb30693d1334cc03098b2c3d0ffcc6f7a8d2fb0cb9de40a0673d9ed52b7e5a7c24e547976263f0c2b26f0e7d793692d6a75d469291c578b1f13a83fb2eb84ebaec52f5482a0200a351f385bafea96b013e80e052cb2a8bace11319ed39cd37aef6d486f0480c208ac92152f08c1804196ec73dac6d77c1e45ebd617c36aa0f6a695fe8694a9f15773624b7b61a3bd90151ab0ab24f40d78866a3a37b2c43a4042dcce899dd277c92ed9d1f136907069ec84eb58d05ad1ece272fb3b13be67c27b73ab26f1dbb19351a4ff83d807e0ab17aab2e6ed83940166e5cecbc4741bec3ab10878274aa5a8e76930bc4373594501dc489c27baeb38ffa2481206cf4a8f2c24be0a3170d94085b0e1f26218d8286cf296bef27b39bceaa575d81b32ca8542b4d57f311bfe7d99cff5aafe5704610ba58630b923d475b3036a5095a8a527edacf9c13786d5ccb007619d052606b73d2d8eefd71e6484727826ef894fee14701b3939fa8e22297554e1f5a30fbf2e148282b6f43845225b4fd7d9bff12fd10723611ae5df18dd229840a1a204ae56d0474e55a29b2c831f4a4c3d8d1762e3d0630746fcdea3eb0d826e5edf3b44713ed4ed571494ca4cb8133350a5f12b8e0a24def2eea92271f169b387ea3d8d3f5971676b8900de493db60d57772a131cb52c35c078379d9cf4f6b8c065a1de8b37a5f1446c0b7d691c57ffbb2fa980b37e55a2be7c18957cf7f35d0f0ac3885c40266d252a017e07108628ea3da9c30e91270f5b5022d83c626ae4a803b5df60094c5d3e0022018d062bd17322a2f57b93fdb618f8aed5b3dd5ce5b4ba4aa8a0ed88ee00b1f96dfb93d4b0172023815b72885bd0e685cc07d7b4d89b59af83c4b87385f683b46e896932b0e5397ece78ecfe6c4bae708c6731897f9f6bb7c29615220cd0b34d512cd831bc38bd978c377b9caf0a4aad682360b3c1256ef3099399fe2d9c9f0ae0db64af060d9991188787aee8e55a8d6cdd285545ffd06047c1e03274ed23e1ab3b215cbec8074ae5141a6aa00cea240c6a9d99f3f3b5c6bea82201603cd852d1340b1ba88c1535b07acc67774d097eec89accf3439ea81fe62aa80aae8c25ad6fe7f91e75ee33f2e34dc8827134eebe17f1c14a1a3dd1f13e5b22fdce2d78c84b5e20efd013dc62820a11ec85454bd4ee21d0048de08b2d22525e6a8109ce3990cc39ba026b0cb852c15088a8c043a2dc0d3dcf3bf8146d075b5eeae405c40e9c30019265915e78a95e205761b3f43e802f45e8e593cb2030fc06c21649b8185ea3f44379dcbc7c685ee82d9068a7f36a743f39901a6fa6c0baa4179a78018addf15b2f27595fd290032f5633b2beece63c8757cadf34080575bf2010ca055215d3f8765c04955b9a718bedffb03fb17d28454d484b5e14ac0f6b7c82d5ba12f81eb1c97f10c9190cabc4107cf995ad8aa559885df172ab953d1a865f1f4f58cc4bb1cdeb8d6e04e5594515b7e86a8da6d42c94429e18e37dc28d7966f41fc87adf00988361aabc5714361b7036c3d23adcb23fd84c5ebedb159214aed2c5b65744ff04169ed8232ce5aeb763f8722b691f0b02ea81024ac3d52d180a29ae4866fab11ea091e2a1916ed895ff446ee9f9304c45d5148a265c8523889442d14cbbeff1eb4e94bd2513d6048ed4688f185e30d02d204861e40cd56b7ee2aa3528b1725bbc176f4e92f9b602f58d09d90cae6e21cbb3b07f598d14ff62e5f9e9c15e436df95991e4c55148209d44f9c83aca34fecc096ac184b7ee0c7a10834c2ae318d068b0bb5d2008472652f2863970dbbf7d303734bf158501ae07c7e37b65f1657300920a7888142531f0cb2e07b22732cde5786fb857fd38cbf44ee4886077fe1616e258a50905f986e6939d45a07fdf388de8b621891c052a1b7c39394310cd498146064f9ddfa44c88337ab77b7cd1fb2e653d150a94aa5271d069a5530cc99ef3b7fc08f8d008f722d79e9b5d58d6d7979b0b344e514845b2528c55a7481cd83b6aa664b2e6df82447ae2c94c4ca11e3f17c48a269f0c35c3dc277f6f43054766dc388fcc0fc5af202dc84f5ed5f7227c5836e903db8443cb4d2890db99a0ae2e588a124a225b37f5219cb935c61b0ec6bf137164a2660a270aa406f5b362447393e7f474f3186b2dfee6a343b0b29aeced09203c001cbce31e16718ddb55be14f05e4ee2873bd0dfc9894883364c401ff5ec6e9ee41197ac6e861709f14f4428f660cba52f3033f3b4914b898778a260cafc69240e72facfb6da2eab0cc8fe42a8300d0631fb73f6f510d4845ab9034bce1d3603f329eecedcde097424f996e86ad6fddd16862566150345090b18fa199e0d99175cf2d36b5b339921461be99296d1b111c2570cf5ab8f365c156d424a42738c8c603c2ce5c1e09497ff2c1e5ee7af9a43a3786376a767acd1a82af878118d510ea84950633b63fd3187108f845baa9b570d824d84030085c049265e6c1a0320c0ed3b2b1949a582d25b6780a0aaaa1a5c56b320ff3522c2a52c56f935b51ad46fc4cb82c04d4736a28065949e29af50f3a33baa0e0821343aba07836bdae1209437a0d07ae8be02bb18e12c9b79c3bc0e89fd6857a92fd0907680b45b93660467e100e85384388f51ce28838effa6762f4bc228c66ac1a4fe7ee43df9413c1c0162785ef8ea8e5da2a1882bdb7c95aedee4c6caed945aee0444ef839f5a146a6a43ffcff8ae66612938c71b8aa39e3580352c256c063bd969cdd1251d9dbe03d1473edbf401142b81c2d8bc3ab71b522931bd7d62b4de5dc92e8196109fb940f10b71dfe9789152514fe89d87e4eb6980ff384c386278310f03b75da7b8456e55c111888d7424b2304a5d0de8ca5f8bd9f576044ac96387e6a2cf4cbda61d717c20a47264761048f0d071298bc0500f750466e76a01d1d8a31916cde0223b9ebc036e068c54e72528d82039f7f3a9cb733118b873df8fb4a86647fff7400d644041cb11ae352d97d0525ee19f372184f9dc8525d4f09008ffc0a6e00b69be56e0add5d9e2f003c3b909320556352c0432fd1be36fbde0070e45c8371c3c9a6d50306287c7c06c0326e39432f06653767804df3bc0943de457a69af0ea0638c3d972cd3a773d7497ab2eb1a119cbac8e3962afddacfb0904a11e6793ad1234e234d0c9fbf1098708a55ca851aa9853f3394b148162c57878704949231bd90ee11ec43dd877b7ce1623b77de38a55a9262560d3a4428c7e618eee0825dc2812e5458d035ce0c1f9e590d205f21dbb0ac0531ffc0f723ca9d6fabe755630c8ad98466979879724b96039949f3fc8a0c7d87a3148d8c31e480caf2b335112510386d31ae379c95440a5e99030fac85739425465293faf4f87b3595cc07849c7b699bb5b029ffe366cfe7f68f0d59d4c03564d394ad2554005adf9d08d7747d1544ab819b55ecdc0786aaa15a347c4bff84af4bc34ad41f5799d7a0c798eaf831ebf924b288382c75de2a61ee312a50e4dce3e1a470ceaf3308d610ac752f5770ca1190a77941233fb6595f8024566dfd6259073e4652b7ee86988a6d85f958afab12c79aa182a4abb66d4fbaa56b642084f697d0ab1c768636a9f83e92e52c62f1cb27497bf79aff1400b43544baf588038a3f48c7d867cf34f8620f2b2be44f4c7a12293fb605d866fedb84d1c8ce5486286c7b96838a2b64c04918a37585adf8c358177856be13a4588cc198c2f2255e64d12a98536e973f18534b2c0b4de7606b9ff8e4a1f111e3993f77b260b1b38908d9ffff2ea48d8f86d6a3a54fd2e45324802318250d46eaff519388f7ec046876e8c8ad056d86160dcc4ea85b7fe580b993a548ebbef730293a8ce0dc73d152539b69ba15c92928412e61a1ae36c03a264d26c03a32230e8c071954d45ae32267e076514f9e086755ef50dc6f497c59991f038e80bcedddc372b8963c82c2caebc2132e6971f103850d0e0afda86c18121d04ecf2c7b0c3aef07b4eb64c5b16c0064f789564d65ec80a918655717a9bfc403b178c0040d2951d1463927c17b9cdc115d78b19d5681f88020a5438f055ddc8accca41624e9e47ba6e5ebf493e0d24189f65c0562bf0bdf3b681d1a911ded75394c60cbc9b6d1510c5bcdacd07da56d389d9623dd2ec92c3b010a422a7972b7b0c22518bb48fb4492d9b2c4ffd3b0e81fa451bac7923d17f41b7b1a7b027cff10d9d78114509c4a2670c80bf57f49da6f8bc2d0e70f3fc2a37e1348a17ed8bf05049eb20db2e6e2443e09d7a256e70b592776063acd79ccfaa8ed93d51360b5397acbcaef3e151cd95af701fe3f23b9ba1783271bd625946db063f500df76f83d0c09fea6fcf53e002b68c7a29463e86dc98cbe4de99ccb7cdcf3361a189f1fe53c2682d4e9cf346627c440cceb3464c558ad7c4e8ccf1403cf2f5cca31b32b67101a2180bf9a861426da99edd05029c992e3de84558bdac6128cff7b3dbea951e0d83ad336f584208e28bcc8cfc8bd3bd9b142cc33d8d8d8138eba454eed1108ba8eb66ee5a8e755b2f94235c5c463852e039a4a8a45c5dc1b0d571dd289e7f0f6a27dd88a61ffb50d216bc20efbebcb569762bc9e582423e2a3b039935d8599268c83a7ef948c2d44e8c47fa6d7da96ea9e145fdfd5e8072adcf45300c812a8514bf69c2d8598fa1ea189b44cfa0418200c90e7ae8d019240c058671d9e47378a87048b290f12cd612c2050b5a11f15f2ac2f1ddf2da1a65a57675518f133f46b4c8edccd27c5a073191f71aa433bc57dabf69d0685d7ca62a93cdf9ad3755bdb0f3a211142b124a54d4420c22ed1cd412b6dd2b60c7736150442f0a1ef8f7c257c80c2ebc5df0ac9789936dac4a6eeabeb1cecc121514650acb82e6e14abd167f08e15906b6285a3fa12b0483046373d5d306cf3eff924cddac20226ba6cbbd2295a69b60c1c1e7bea26e5a70b10ab37b16bdb033398aa43b730ee1d4d065b9a73843a57334b954c68d884c803556085f78c83e44ed52006b19351413f26cd963df255f4ef41ef3fd3a4777fde0befc64e9c0399b23b9d047c0a3760dee376fb43cf6492018fe3429502384a2f9818e895080665e8c7f8a2a2e57be52462284438ea684ca57a26440c3c83139a8e30ebbf572ceb4813193ed2558885b05dfdd2e501412b295717a1f786abab79fcc482cc9742bb63806957fd8b047fdd2db583d30830e98e9e489f44cf2d980633c7ad1511e29d1ee67812ebb9daa4ed27ac94edc435a452a49274b1874fe70953416b4f4aa3c47bb538a63a9562b36cdb5fea899129653702560fd5f467c4c5cc3fe713ea77b8081bc42515b683257608d774849450ae3baca742167773f9f3b44c62f85250b2d5abd6a46ce7eb5b0e5e330c49033a001894fa26a5c1b370eb2faabc283bd92f53b3469058e8fa4af3c694a665a4df1b59482c88602f5bdd98710e3b288928d1fab089be58586dd4ec192b786a133b8ec6348a2207f77292663886094134f5d518db2b9a7831689bf4d3660c0de3b20635b1c6014043cc5965e093bf5ec941adcb2ef69536fa9db6ea8d88d9574abcdbb6612d45d9539d88a9a183dab812407a638d78580a0b36a809dc584a111b3e09162accdee9915f865c61741059546f8b0d877a1e73f58f0ba36dfa307c102fc330b2d092087a6e8270c45e77ab16958934783ed100e8294a58cdfe6d8b12856c228175f1bc2cccc27225953654bcf2964559d44e6d147f6e481bea5e01f03e0013a38dc1eddef91c79821977661a20d8aa893112ead6a72a4724bbd93d2961af9ac19eaec341d879f2a70e9c0a237f838c93126e655c36304ea7439a5b213fdcd959d70521cc67ce6518fe2ab8e3e505abbd74c2ae2767bbf0a4348d240a4749456a5825ca5b8dc24df8ac675be6f098734aded5e208fd64e3b601abd50009508f046c046520edef41b6147366e46c673be5cf1e88a2b8ec82f28578eb83ca0ecd068331871c371e302b47a9445cc20dba32909977276d9ee4d5deec90b9fcbb6b15b5e31d2a0c44e70e72f76cd8a930681478417f42871e8ae06ea3dc9b46d79c1cd874ef3c0972186aef1cb733a723c969bd77f86431c019f1f2a4e9de55b5630066067508d67b09bae0b03f10a73e2bd53926058e55124bebfe278a556929ade2e394737ed9ef275798b8bbe58dba1b3b9d36b7bbae4b12adeb820b78fe7e0bac2bd1a042a8e908ea5c52b33410aba0a254d14ce8b5f811fa30a9e319f49298b552bcf11fb3db3704c6f81e6e0bafdcf8cef2a070819e96d74b3fc08e10f671655473f0d3c722d62d7f9a72042be570cb03c49b1b9004e13687955cd26e3de0ec89fa3bddcf91c7d40c5f5b9cd82b464a31f7bd06885d7d6b9d173059627c98ee136768680112b5f509c93173ba0975e695bf42ba3839a0f10872576b3c5a907f8b2970243443d8b42a07a0d264c0a0c9f88c6cbdc52c200005f557cdac3a51829efd5fee670238378b989b5a78baf60376a486d915a727f50f62a0724d29191ed1ff5324333c2721fa71e58bc2a99948b119d8b2f84c970a0bcb8de4074fc7b50fc8a2681938c681653373b24d3aeb52e3ae290202f46b30f95e4a332b4f4ba8c4ebde405aac82bfdae328ddd8c7534d1866e8292e44c7a43f70ae7fcd9282174a1f7f74eaa7449ff64ab34057ebb2793b6a82c8e1c6c68750ed654224ad6c04ebc55e4383abb2d060f1ec5f1c8918c9c5af3cdaca2877391374d7b8a4ceff0109a9eaafe1acb5515fe68e67c9237c29f0933f276778472268639b0d57cf265ec6790d56f5e466de5958f544f8f6c6328292b784d6a88385a273303a690f094af7ab2e7950de16da12134d3f812dd26611843d5ab5da405984caf480e88106c240b5a7d505e1077c0886765d57fb6bc3fc94abffdf8a04058dda83fb3525a66adde05bd1c6baef4e48d192623174b000d7e4404665e242d4cf98ca9ccbe896c3a19d0b41394d80288957fd09a53fcd7369822e65ca20dfc52a1fc33e86a1ce0a222f9e09bf05c2b3792d4cd7d6ef1dcdca60085895c406aeee57d6f386b0cf58c133633cf9c5e94349619fd035ea4cc35eaf1b37562f169c3313ead7489ef7e22a93d3eecd04e24edf0c59314dc52dc9070896923e0ae663cc0da33c0df682954051c6950128a822c41a124e32eaf1701c19d34cfab0dcafd06c57d17b98615c138fdf44c7c238b00e14827f89452f247811fde440aa9d8995f6b7577e611ee3f111d92d83336df21adc3a578f2d02a163f18c8e412b1b90f222f05c040d1aaa8c16b8ba4a8794eb9ffe48a34bb2050f1a4c05206d039b54856688d506be78eb7f26147bf3309c8efd69ec5f5978e1951677410c4e2c6f837f3767a1165e2a145daecc19ee4b1991a1e576ca970684f6cbc192457229d49e6c63a39dd2d013a92fd5bde09e51bd51fc03607f1cb4cc326d79beaecb2080ff0e28ce12b08e014778a50f53b2a539a18c98846bce48a134172e3e2b11a109dcee0f1287aaf8082a131a9b3edb070731052e34bb6cb256bf587b6429c847d9564af95e93c63f5d6e46f9bf3c479bba53c5bb6bdf3bf24b2bd993be2bbdb488ec466e85d10bb43886f7395d6de0f795d926f4c48e9d4064a23df8a6a10670f9af607a43ef42e11c017eb2a6400a2d0d2418087201f0692a2b50070ca495d4a4be4e929ee4e8f20f5df20ab5b94d1118b1a1f04bc510159374038389196f2ac7fa4da05288f2a2d0cde5009fa62eb7057832bb1d7770dd913ce7744bd134f2c7355e51e0f5064a5a5565645862fb1c6559f083640c4f6d31aba0360d3ffd03e63d01d4de347853d65104d0c00d5ee3cf776e8a7004465ee62943354cc4c320653ff618a6c6a40fc496f2f7326c874602728bb88566b0397109882b0b57def8727225f9e6b8118c89564e244d2af562e88fc3b3437b16d2bedb184f378e9256876c394e07122dc7df26bfad3679d04be1093cb6c4e4f3d9f05562e2f4f2487de9b3fd7de0c0f2b83de9a2642e53e08286e0d0716f6e19c567cb84f8044a7d74d391c1cd7574ce347288d465bb3de3f3da669a2e30d71a80caed9b4d7e17cd3b5f432e75d62a0ca41fc7178299be225b957a4d54630e67646fd88842c7a8515a5ba74b103f62d3744a9d30b6651a69ebe66a5fa30dbbe510d625e418346d75e7f9c4d872367282d52de473ab40db151055c3a671bb0dbd469db2175223b840f978914864bc1c0bae4bc5b18602ea8332c8f98e349b0a2e6362233a4dda82c5d763efb9a1c71ed1b14d65ad35f7de058a118b6361a02137d8e82407fe176734c1c171e5eb3d8bff12b94d3fdf6727b63a88210608e475507fd8618aa32e3a7e3262616e2cb68c5d98512c3b7f7688b3a8957eec37b397fa9f524a1e2909e4c7f4c1db86cf47de578fe9d5798c3e1ee33b710c8705ee763796cc00fb47552c76e0e4e653e119b1413f9c263cc42d4ed353c2369843e05f87ee1847b83fb45be0f3501b131f0fcf61db6198efcefdd49829ea3e2417f068ee967ad7e169a48b3129b8b0a0c258513f1498a053b0f5aa5120e7b5693f051f61cbf2f09e68540662afd2c38aa61efa6b261de7d802c783a3ffcfa61eb1641b4ec580127dc3bcb1dcc043ac89523165abca582c10cc8b723a749db90fdebeadfc179f674864adc9e3f5e4790291dc784e46f0b70e9987012dd214aef1b33204b4bcc0195a96b420784e88d2c62e8780c28ce4744bf2dcfb33d68b7ff22f6cbe2a7188a10cae0af69e9af6329c3d448414ddbc9fca55a496f780b22b4380101a80ab5ea987d9e75f974341cbcfbe31a6024c684af46d6e0b626e64b5d0c69ec4aba364e39c2200d222011f5edefa74f10cebd38f4c01436e5653dec01985c10394fc3962075655f376c9ac6a54e3aa7ed531025513dbed6e600e1b4106a77a82465318517a768cbbf405e25fb6931cc98fd06035bf798508ba9bf5299fe18ce51615ba1b1e70220f677de5e1ade6c0314938999d3b3cd07e887536a71e5daaaca8dfc380c8ac5d8e14ee134ac8db92ad7f12f98ed50e4feefad7f331a216047fb67b60ac35b5ca96c80dca80025b493360f388b297cc3b26631c1b96960f354b3a248df8b5648f60fdc577755bd66d5164e81fc45c5c4d73db426c74046965ade636b55e8bd54f485990874875dde408228ecc17db935eaf8fd39c9600cafcda3560fa69b9dab780a30dce070369090d99243446302e17853af940ed4ac650b34dc293c4465a419a3605ecd3485968d059ab8f5d9e38f3556eef3d9bf16370ad28344911e01999e54c8268adb4f208e7a6bfe86932abef9a4137520690d831fd2cf093115c056b0efcc6c956dec1cb57b49d3b47cd1cd7dd02ab0a0f9a53df61fb0cbf05c2420baeea7f8b4d1296c64efe6905c3c2ad5dec089eb801c87a66a5957ad2e5921854d29551ab7af00a2ad94fb443eb3ea309fa350a75a8700c8b5c8c9ea17a00cdd24646efc07fbfce3129b1164af75730ef4d254723600eaa0592eb1609572d76c572d11b989dc1f517ceeabb0fd8167b7315d3d9a596e51d458fda6c9e59b1c18531de420f616ec296e55b89a3acfe86fe49d145ea75eefb80ff0a8b067a7efd57053018da74dda40073436cdbf20952ae0ebd307fd49f4b45774594214a0d0996cf80329a6a990384ce5274bca846859a34de2e8705dfb0a76f24828f8f8a1f4c7b455e4d545d9dc1141ab469863cc959df3f63c21c3cd540fa3caba1f7de785c4a773f9c4cb71bb310afab4217355704ad481022671b35e884a8dffae6ada5968d123623df7c6e28142ae1151c0e1707522aab18211e6adb0a25658ec4cea7d5be043c7bb982a9f74d65db1c3fd05ee31faeb1af5597c6951f6aa9b2f1031c374f652182e66a74925c88a79ecdf49f940bf119ab886e210a22987d79d9c75375bf96b39263b39762318b23144793961db03f43c6a3152209bf205b4de389faaa36bc993bcbc8439ce3b60c7c2e0a6b3ada15981f39df9d735a7bbf70292cf022f0197f8be2d99851718da0c90c1981590914428655c10ac8aeb044dcc50f9b2b5160d45781123f5a1e62107537aa023667d5670022f0cff72a44dfe59c6375dcbafd21d88c79c6b94fdce94d496ba0f401755993f1bdd909d9f9c16db0371248d5e6a82ebc8c511a98a04d20153e91af26c0ca41aa84d7fccbcb5618dc6ce54ef3d2592b8333e7bc2cfd32fb86f4566a01f413c5defe56b89e51050da9ef6ca3a9ee75b3e1460bf5d8215c79279dc75ff43dde8bc2bc3082092a4e271368e72fc70decb2a12668d2ffe3c6baef50cf7c8103194f6cf3b1b0bb3c11d99dc3c1fe4a4104dbda5b09c1e315ec9553ee4adfa70a3b55c9113804094d43f6640c01eed5a6222ac6d844842133421336051ce059225d96c94d50660aeba8e3a2299d21169011c5488dcf00b20524471044b2ade59ceec0c496bc19ec4a31624fd1fb208b2b5eafd2806a40837b0451ca359cc5d916c4727b118746a3c1e16bbd05e33debb99d2efd74d1769ea9df006e46cc81994cdf46bfa7aee54e803f08b1efb0607c5179329709409e0917576906bdfcff1898ac86ad43e5c62d9acba8e1a5985b70d181964249ab1683927552752586866c498e3f0e2e858a97a038f53a7d35b3827d1bdd8f235433d0dfdf596ce7fb7305636d1e600ca85c2efca02d1694323bfb2f2404c2fcdab205c1871433c6d4cd19fd8b7ad1ce2d8aab0411472e0ac8eae8b830729ae5d648beaab616fee909d005b1041a52cd894a1b0ae635f31c3a496ac7464af859ccf83314b877c687da4f89ae87e715cf5ca6362838ad97db03538b3e10d6473dd4fe798b26c98201887662a2a3c4f0c068a13948b7285710344f923c09bad0a30cfafaef7c7939e52c49af4ec491ddfa30a41d525c03e4eaba07c1ddf56fe4fe20f7a83523276f0c7de70928c68818fa836118e64000094876da39e58bd3567684e230172a9891f0ef672de2de46d8c5086d26733e4915fe25cc029a3ca743f6a65388ec1514a6ee8b6852889d6bb479dcb25fc85e911383a0d09cfb4c2807b991843e20aefca38b35675490284ff76f2407324b0df9de47109284339426070108177c78311dce41d47f834dbd2704e89807c0dc8ce72f288fe123772682441aeed5f5ac8a0eeba93dc6c91d4a6715c9f2e166b7df163104e976ebb6c2ce3c11d878f4b041969f15c65d5042be2f471b50f6f14272ce382aa0c7535484583edb94e56e64aa98432b2b5a3a246b5d5d29f0c4203690a00ec0b1d88a55d4bf57358e4ae950746f1c0ce1e7e0f1c847551e4e0bb0dbb29ca06edbd324ecf4c904384783d0383edc6276490d614097d243f9a65c5dd169dffe48866d007964405e0a48fa081eeb366410fa8965899513e6794e0ad5bc0b53632d954db32b6ca531f6b0bccf25cba9cd3350d009e4f70169738e966609ef1de9e1746e340ed1770cb3ff5b445e6c4d95b7e5e5965f74bffa040fc20572cbd26011448db92828e4d5cb1f52f61361eb9d05ad26a820dd602ef7a61a4be64d61b404e50998d386b6b85b929aceb282ab8579a18f91361e58e5b646a4ea3127c41e4944e019d34eb6f2d2ed254cfd55b2dfe343a8ab6081aa940e5992dc9ac1f60c69b9168063e93ee575801bcb8866843092ed977d201c62748b7a588f77150bd9c9bec191fe3a0a0cb0a430e6c8d49700b70aa68a6890764e3d345d2f3055002929f241594ddcd64e2ef1e963bbc4d4ea9e1810dd79f46f497d2e5945a76e32a22769221fb6a81d1bafcef1f4ce290e85ef744e29b30f9905613a16a4159a7625f68b03c656d6fe3656d50c30dcaccb3294ffa37337ea84137b0679fa2c38bac7bffc4c56e8539dc6757814d848e248b004ebd4784f53a7c2bcad5b7e0c123a41ad6329df4ed1e673eed785d633c7677e4a0083332ee77c81fe05b711b0dd5ef2aa4839c6f6232d0ec96d922f001169c94595dca2202d223429a485e4094ca31649c6be425a030f97149303e1b24b9e280e48f47d40cbcdce15e6f003211cab55e71282adaf8b0f898945f55cbe3c6db93010fa86d0ac6a04f3e30d24a824823f603ea3a547e519c23cb88250f1d601bb3899a56ea0eb4818426cd18761f77edcd5b8cece809e6e3fed525678decde502a6868552697caf58d1f4dee6dce77ef84c0699e40e44b4e75edd60da9212e2afd2d3517c8f60407f6ddf19ac197bbca3145ed88d62cb373b92d572ca94b74da53407d67fb3ce27e7394b8aed9f4b934a50c2235dc6c2f4ea3ca95b0b75d314e5625beac4ecce966d35667ba357753cfdf6b61469bad069843569b4ec7cfb307b473d28554ceca3e1b9d350c9149fcb35eb3fe1aa74c541805bc77c2dc5a1e9f8ee41684d1746de3a53e2704e12f9c5a332261b30a53aab3e52801324157dbfea7b782ab4ed7d3f3181b35e012ea7bae0ab0bcdc748e4e3c3b41ad5d7ff83a1abe48a74a39bf207c0d785ad98414bd8bffbba8877643ceda5545031e37403d5da9bfd6ca99884e63d18b1fa4d5d61abdf379628218f1ebb273fca6caea3719e8e1d63521466c2e21a8dbe3262e7b3d96f61c544dc42d0fd1b887c05b43a63fb142b77641e87c15da55985a4e5686c7f63fe18ee0cc3068c3d7112d19e0f75f065af382ba2620116662ac8647ff32490e4445419f74196716c08a511c3affc323787d8a33049670a894211ca1fa1633c2612b301c3c332e19cb9509c22dfb84cb41174ec2ff01a432c0bb6dd97f03ce1b697c7e244ff078a1ccf96cbae19aaacc6667370a52d9748df88262b5310ec0a5d88616515b92b6875a9a9884b4145fdfd0be2a5d5e4e830a8273cc4ae5282b8976189a94a48f67af7dde2c7a6d488fbfdc063956fe6129b1c9b8fb6adace8994d3f2901c64bdacc2e170c93a28d79c88b3b590ae6da97ffc6e10ab929c1b732f43f1758bc9a61be968cec89d9cabf10f33766c851d27eee2ed7c18bd8ad5e39146d035dc0f8c0b3c2951f7744dc678d81feeca46dd6973c02f994d2a12f353b1a30d8b4dfd87b5da02f3f08a413b3561fc7ad4d0cde0723aff83aa158c02c503764f9c2e32618252b060bf5d422c9699d6f016178e36eb56a3b5e06539c520339ba156f8042ebc7b5436d8a2ec91427182e1744df65cdd0e50194fa2ca071208b205d5584f19a429025ba133ec52f88af37d5ca80ef94b6234c6c23c1b39348ee685bd5b5e755b0561f423cc1e333d7fd4e19be89a70d238bf0c39320a2e0b21ef56202dcd44aac5a0dd7bcaa90908aae3a4f264f1c42e428a52a3ec556b6661fd7a563671de4a290298ad3a601e05aea72ec5f314535f559ffd5b342c44f83dab1e20bf8415a55353fc7be52b0a2b31943819930407b2282e8bf28144a647e9d7c57f94d28e6aec7c5969533db2767fca072e5301c8b6461faf8c05011792ad4a9216017b2a0944c483a0f966f746c8576a17d606acb61a8f00d0303ee7224a1dda1685f71f5faf9222b95079585be7c59c93f53c5724a56e37bd14db73c1104b4fd34a9d82ac43782a19edaa54c5295e1c6464372c8f25c9fefeadb998486a11e2ec30081fd7843aed1e3b7cb2332b853d9245c6c4c5621033f94c0d5261b89376c61bf5468a8755b66c7e213b6d80781cfc1da1f0967c8349a2001aa6b9e2466f40d258ca4af89a5ec153a0da07a22204aecb8c70de3713495fb1f5a8dfcccaf6cb2b488ba8c9dc42c79458b4d37779360cc7aae64cec049ecf9539f4b6e38e4e77998d897fc4085bce170b0de3436b7a10010f169383112fd72f97256cb7b3f87400c4e2e59afd02d942d669602e1c5860fbe65b405c8643ec1df27cf2f2c8ea9233eff2e737f45b4b647427cc5e95cdbc5a867b00aca446cda29e46117b8cee87b58d2d26baf90d62a5eed190deb68ca837c0877f27d6516f92e8ce7f1718ebd642c060b7d3ece04cf9a2ac6f72ed8227b89e8844e3fac27618e3dd02b03c7942b96d07c4376175a559ccb23ce7c11192c90e8fc465c4c66d4558232c2b6328121e063c1065718afed382de57eb41c3fb0088451ca2610a0a93d725641693a968c84a58500a9024898bda1d7caee2ea048a47af5f588b48a9771c4fbaaa716dfe2a0fa543130670bfac4cea66afa95599ed9ec65720e40a5ce7d22294afa8f8a01d23c8788db1b0fce7e53259838e0184c1f813f5bd30784bc9e20e6ab80e2114a5ad0c85a49a68727f12900f3df7701c2d3425bc150c419ad78f41f4bba614808969c269c36d4a8b75ec1d38247692d9cb434d016186a4a2bfab4e70f8a18e0f8a4cf369942688247428e5c0b5e66b9977fc9296dea33402b29b2d3d1cef505761021e9a301b0c3f3ed5cf4c4188146f0629b20e5a841c0a0f5f668a91b6846e0ca61a339734cdc29cfda4a1fc0c17031888754e10f2d4831157df9c38699510315adce6530c5cc692a44c55f87843bb63a98582b38f072406e3bee3b026de9a88a00768dc0c49b89d44a86cd386dddab212460208dc205069cbd60852106015983b61e4b1190d38ca16be58037c61b24960f469ae531bf834100696ff700a39996ec48db2664fa0c324f1652f2601cb24d09675e7895be145c784c66d694879073b23e481fa0df2265ebfa8db47fdb0132a64999cfee1238c342b37d6752d1998e4609eaf0da52d10a096cf34632925a3631d204546ee92233414f9389042e291974e4e48725c0d994f050a01b635baeadb297fa3601a1194932e379ec15bd6dad1296aad4dc89082bc0629a6a2a96fecae6e31ca4e630338aa227cba6cfa0950ea138507ee17c58b905c907525ec1b803042e432abccb1afee47627b743a5092c4147fb626851b66c2f47bfe50705497c69126ed17c59d2a9ad0c681624adea03d3c1cdb8d131655bd16d6e9696ca46cdc4badc4359052d6351d25471b28b7ab7b91b0c49aac3030e09fdab5d9a58687495cce19afde9612415fa09d865735cd395d80381c92ac9352a850cb841f451996b1d96fb90f06c774ac22c1fb7804a69249f8b901dcc51dcb323d40fe9e3f1ee4f176125da685c6222eb514730281e75a20609353d4365495f2a56b0a0585466b7f4230261dcf243cde303ce366b8086a43705b787c69503e1199f34d1b100f908858a0955490ac06bb336281df6025a26704f89b2b0f9859d1cc79f80b3d4033e4ec2294cd8f5181ee6beaa651abdb0a5331764749641f918e480c05239e885992f53d43c55097fa985a851cd01cd61865556ab701fac3736458383752c4d115ceb684595e586f0c5a0724529e7e7cf0a626e3e070ba211a4d5ce6f52fc9bb47ec4211e871fe8fcb255d8d405051cfa6d484081e2558e3828072620808cab2758a4252b83b9dbbf1cd911f84abfe2a9a0af4054672960f2c3a6b0e20d6c8d6aa93b427c9080575abf5758b9de9e63e4e27b6d0ef3835719e2bcb131efdf06a7ad5448477381ccc8360fcf206a19df0365e87834c272ebdf8695b7574203b2701193e9dc1dd80ff920e5b0c51e060e46f2d7799bb50ebc8eb9b078b0c902c0419ffbec875c0875cd4baed9c503a690d314d237a5eacf5d00cb5231e0c7cfecba9e72a25ec4b7aa9748c65901ad55187d0899efd86d00113fc9b7cf0d48d52d1a6dc8470a279f50f4b130ef9365f4aa5349d6541644d613546ab045034a43f4e91f9e4d7cc528d50145ed516b75ae1d2d76bcb0e853671d0197bfa4c24c247f986328decdac261652a1025d70c692429164c942cffea01618358669d6bb82e3f6f37117c1625607055dc2ac0312a6aa394126c9278b29e7c251ee4bb81af11d5ce5f6c95c0faab7365e54e8bb1dc682e81fc28ca77701e78959c346ef5acfad3ee9c71720a3a2c836fdd74801be1e6746e3a9ecec6512ef4697de728b10350466cb31e7f16a1cfbbee39487930528a63cd9e27ad1431128607a6513a711cb16a21399b17b2a3991e0b8d6e20593a3cb2ff6d352398db497c38705eece44483a04442e13b091e21fa73961da2ffd53c9184c17ec279d6ae2a2803e8ffee038bb40e9346ef6ee367da391f971abdb3be699f57d80bac15620883a32f5190bcea0988d0a072cc4eae65a9aefde7f25b3917fa9f41681af8350100e110cdae6002d06847ef7f0961c7008ecbbb79d6d05211cea2aa000f0c36ea971b0a5660730f8dca4677cfa01489267c71c3e8816d0c53c621cb611aa56b676b9679e3e253b970166cb2ea9d4752480632651d40b41db10a0681512d85b072e499607643282170a6ff2e7ab9adfef8b258343f8b25122c8c51907d1fecb9e5255203e0a4fc0a77de559ae7321af4e415c071a9546086a78769990ae2e858e80460e3b1cba0edaae4e5862ac93cfcfda431c971d103a22ef6e60f6660c1aafb8ad1cd52d8d8049d83866050609cf36699fbbdeb424ae5d627f914e74f80980487813d37f7eb17d21bcf47ca019a16328a92e0e871da69b4dffa7247cd308149ae5017b08da7c5bdf0ce0d308daa296eb482019718db89627e4c9233d4707eaca2074be4c07166e7346839579c7de1dedc5c76642077e31631f9f2d07ef6da068fe3718c33fad5428b8ccaf4be84db2203dd9f2545de6abc84b9275a6be575ba26e305330ca821a2d87b2ea5be933898c3ed138014b73128cc2a7fbd030302ce0a63e94ac90d102aa6cf8705f0cde1fcae7c62aa3aafee5789a58a7a136945044d58108ff20a8d2a90c0a0fba6e358a94a618d7f28689717a959cc3f250f0ffad658939587bf8d0226cc2ea470f6c0d25045166597b9129e563d9bcecd138f6f3fd158f7c9b07a87dc33058b2b4f029431c97fe0d2d97922fb9bf57a66f3e5e4002e99744bef4201b9a5c2699b86add4419dec32713d65adc65cbd17b30c903146c075cf44cc1e7486e7d66519c186103606289afcbcbc67c0c6be0b0654a53ee54b339bb8a87c103a96ab147a886d4969e06c3a73a1d9f8a7098d319a0c9d8d17edcfebc8ddde53322594d709886f6ee276832eef5243dc790667239b2727d9677f9639783219be8d4d99c4d32953c65b08bfe81328ac3a7208f696a8b0325b7abdcdf6525ec5262b7203402748bc1b8b7ea16a84039a477584c51df900d68112dad1750bdb2b493e2122bcf9b5029875349c86d972db20f7d7e26fd721a6c56027a3b26611f01b16991d5220a13198ddefcbfae0cc70afd02c7147864a13e14e9c745db473694f68e3b41c000e3392b333b74b0ff27116a89bf62b06ca21e6dc1075ea35a7d125b193008b6d3d3c0be363e204ec96ccba6fbc93130fb8d5dcce95d1bd72178478bd522e848f78be2969a7b8eccc2cb3911e39a2a11050bf2488bc51435c7fafd51bef0ad99e277e2e0dcf72ea503940e0e82d0168c733f5c6c906034a01f175836c546e8997b29433960f49d4e363ceccce42103fbc81f9fa6fd166e0d0ecc5134a085fb1e06dae70d308f8f39eaeafe01d12e1351f9a4217b0ec3e283794737d78589168879b75522af059cc2798b08b66455cb0d5ee4cd6da0843aa0e524f119cf5589aba6e2a7bead2a1e023ca6a1678cd5c517908972c795e925f0f506f31e52db21cb18ff8c487f0f6ec2fe4e77eafe444e798fab733f67ec5af79715505850e950c10b80fc06cfd989fe6c1fd166a01899d35edf7682f3d08a8b578a0876cd5662cf417348bf3b84944d81f477ba7f688b45dd912f2e8ab8cbf0601ae8b7dc8162230354480baf090c1026e8acea17d73f5a8f13bed706a650e12c38e7ad812b6a9b4c5ead46867e4f5018695cbcd531cca58f53f08393d21f3e42ab200354076a4d83dd15407ad070a3aac2616a6a87554a72614f0bb04b8981e359f95d9d0b210f864c1a481506067a89488e3e0674b97813554b36d931e863ca7f970a9ea336c9e786253fa8f2a033ba457bff45a1751e2893a3d75b50b7ffa35131843884ce7ac106d8340700e7fb3d38709c6cfbc34181a97d86191b728cac662f513c3aa22b199c04a3cfb374b37b61a1074aacac16199ffbbbd4c9766c7d539ee81f17bc33831af560fb15d563cba94dca77d867ec3c0dafa665027203eaada17ee3b96d792d2d300b13c82201ae95d050cc32907c051c0af877bc7a740c530e6f86d1aea09212de7b4e634b9cf8493f37fa53afe934a114c1ae341094a33e168ec4003424d0c45dd12b5eac9acf2bb7929c62aa33da318e856ac0db6a215b9289185199da54519ab01a21e86ad86f7e6e017a3198d76958fb54afa805d27f63e0497f8a1b684faa3971d6895c01b2b1869a95f23872bdbc7c5dfa19982bf4abfa6a94b331880c3a917836918c2bcca5d6c41e63ea30069e70d692490a329dbd19ed93d39f67cc99d8a8cb2cc981c9c2b9782c29dc17cae4013f7f06947c47e13ae2f533ae5e94bf21fcafeb3872da8d40906abdd10b562439f4153dfdc954f287bbc279905be8245d1222bd2ef9d0b517ad145547571ba17bef2143b5b460414cd2de6eb480c0b0680819fc08e22bd27e50219443e59db74999dd48dc4870deeb0fed561c47444930f85b9de9b70c633a8cc2b61990846d4a9b5bbef6e796c3fe394b51b74acff9e066f538aed6619eb89502722066d1fdf93f14c717e4cf5861f950326b4b9d5c853133de34773cedd918f9adfdb0725d2febe3d3441d76aad01b37723c7cde492fc7f89f0f0968e2c53e82db6b37bb9bc98c4f8f26b6911abf372e1a415137a2310afe15b782c1720255a9611d4048ebd14bbda5ab82f70a586971ae0d9ad0b3edbded8c1e62c27a23acc614269a410d6e4696a34220df77f26ac10732a9a6368574d6c353d54a95c0d9f98be0d984aec10fe625a450d9a6dc2c928f5b3ca2765151fb97c6012c67640c06c68fe2978cc22522436832d95a69429349ad4b3bb44c6e555aa1cb64aba50d5d26b72e8dd032b95569863613ad57f2a569d5f23aa6a2e3e444b24bf6c04a13c8f915dc8ce7a3c32684df9a6f58e9872b10841d29afbe4cd89a5527ebd23eb1eab25569852e93555ea0439bc9564a23f44c5b0163254c392ba6153a265b07b212a68435765ae5298d31632b3d722aa0ad2688e8481962cd1b3b0ea0492e9e4c32ff66083092362655c486324b124e27b76687ab7df790ac5bc24f0684481e4244067380694fb49d88e10dc5c25267f4e8640a9a560ddae122813b7dab8a4ca534f0165d2c1619a4c3941500308e855d42b9b087fe3c3619e21c9e254655435428d9a99a501638ab25d155512767661465810c7d1ff5ce361535f55152ea50696707d0812114b00e79d9822ef516760f8718db85d93d9f18118620f74bb29ff616b37880689b4db6e49276594ec88c0fcd3505f73f36d34636e8e9f06f932c84728240b6fd9a49287c5be1cfa23212655248efe02ab6660e3d77deff5c6536e8b509f27e869f4cf8c622cf466b5a49fa5a48ba3ec127dbf38e22da3848418826b5c20895b04e176697e6a59ac8314b79a2194a4cefcaa1f5193c03c259bfa7209a66b3ab543c13e72b9b7922a9afb170c53f9d2977a22045a46fe049b2774897f04f56177a734381c7cf95f5c28e4fddbb8439ae34f1603fc1ede59bddcd64fe4f6f13fb18e7fb55bdbb81e0f1f3c97ab5e3b78e25d88956fb096e2fdce48e3af37c7a5aece34959fc385665979775e2a6821688e86260d25499d6e506213c3ca94e7babf936d504b95c8614bf2f49b743562df8981b464f9257f553ec9a97ab931a57ba1419b93395190ac199b7dc1c52137b5ef3a21dc73ad14bf8a6ee24e2b8ee8057966f22a86f6635ae56633292d8b63836d7aced9f756b5c2657d61b254c838888222b8088f645f313bd4cfdfbd8847f58411ff0dfe34b263fc1fa41d1fc242216ef1b2ca768d367bf111728cf5da9b4b770e1bdf0fd29efcfb9a28ee8beaf56752c5472751b966c4816116ce4800e493313b32c32443b1686bc5d3f2ddefab7e206cfe4ea6bf88bf6fea4e881feaa631f2243b4908939ac8927961390739cabe00caa8170ba28caf0c1290cd738a2b756fd91176126b1045d266bde61e3749d084ac944a710a9bac1136d318c48be31aef6cd6a498dfcea692e189cf2cac88679a1b0873529061ca41a69bcb2cb8c239196f54ad9643108ff367d70ba4b89da5f21fa858a345a5c8514e7cb9d30da136bec010aff8156661dbddfc9c80b1c078079d39d57146a1d496f6472aa6c0cc30b897628e79c60544221b80ae641dcf5ca1ec5e1cce4fe3dbe5f5e005487ca66168fe2986acc77eaa6381e34d352958aab8e702c03af0dbbb19a16dfa9564e154e55b4db0eac1f79bc5544c19c4b80462fff1bdc463955314bd2a528d894e79997b6fbf867f24994ecc2716c5564bb68e79fbe31e98def73eea1c1575e6504aa2eb8f5edc40483c24d521568d45ad276e9bfe7e50f7f0e4c0f779b1583a7e45d9e1a69128f1d7c6bcdf6f779ed8d3d8410d2f53007690b5e9c6d4537f93811503d94f76ea02f7a88eaf39d588bd52c14c679a0aa7e7d3c2289be5c591b8eb1a4fda8c2c1c5cba6b6a50bad00adfb8797bb8a949503dd58da0c93ab53da17ed06bab546fc06ba5733ac73788d66a818eea0191295040e56b022217f86343d34ca4043d0d64ca215850617ae2963e75f45dabf67d148279c0a44605020676655ec323c0f97a50d82c7258b84b46f841041928089e679ca4a9e2468aecad9a6d0081cfd0b75ff4bfac336a790437f10d518742b67249d69273a21c1c66bcfb76a82beaefd89f3da96b496d1366d6a9ce70447da434b41f4872bdd1db933b4f82f31883abf6ce98d667aa41a038baaf6642471927e30ee70b9ce0bdc6f9c5393a99b3728c1409c58b77aec0f7aac1c93a682ef8ad4fbf84cee07b4492e54e12789ca5c352745406f07a8e69bb58a4d04b8df556680486643f182550d9f6cea372881f5d39269dd7c0027d52c8867f1566f165728a462b5b7586f8dde113a932825789fe824bd0be271759f24aa25c0a09486f6bfa07238e58df28e229ae208112d80f66e38ad24880216039117f71452cfe05f3f66a28276a7d5dedac743db7537522a9eb2da35ceadfcf63fdaeb8243cb0ad501d9302cc311a54c881f1b7e9b4e2cf23442317d85ba006c6f4ab8b9989f1a64bf25e5987d2b6580484c5f31d71258ddb06b2b0d4ae3b0c06193f196759b000f858099462c966118737225f2aeca1feec902e63b66313b9b93ae331c5e9da36dce732dd99ebe92899e90724210168df1f6a632539e2aee69e7a340f39ce5efddc680114a65fcbd7cfa68bc705002acb5212c4b899a601111d6e6789a456a1241a0aedd80e523da8bf511216c7d8caf9752a9cc59c5d8a22530effa28fe76d00e740729360b3059d8a3459717b08c79cf55cc652cfe4ccf711a2a7be0f85b835654ab5bb6d366e2f933283eed531a3f2c4cd50c9d4c449c2184a00be1d7e57e30b5582bceaed586119861cf018ff957c151d54d3000edef62b3446311912da18cdcfc2aa297e96cd3a891f02c4840865de22859d355845cd82aa749fb368c42a89350f70b430d0eb7b688c302f084078b7c60a92bc520854eacbeba0bedf153cc30b9dfa8b4e092798356d916c12125c6c1e704a97501e133691b21669780ace1794786e1152ce8fe8d293d4005e56198fa112b224a8bc9d21f0eab5ca32ff18ae3dcfa0bb089cfcd5763dbc4c279ded709390043f7926c06c1e3406a31e149d70b72e16fd6820b93b8a77d4256a8331f727c51670223659780384bc87be5c851135c5f606782b4fa19218da243068e33556c0eaac0aa53f0444194e80eb8b9c4e89a72c141a026e794173fd3e01e5117a7bcdea439db1d22df1b5fd57ee8eaa7ef5dfe354399341177c8a5c65c84001304f2dfc732e4e26ac77c18d7bc25ccc763dd5244afa356e3ad499c8d01669f49036bb18dea3a6e09f37ee4bee23eaff72289f3ac11c882d09116f45c4a96b4a897c7d26c69b798da7dbf675f9e97458667b13d5d149f77e68c459f0b0bd161390ab33b07dc3e2b3abce009365e723bbcc488d4a73105f73276206641b11bb86dc6ab029c2364ce0b7e019124cd018be6332890c0637d022f0d337ff36680a07ef24fbc23795bae126b1dfe1fe8a0efc19ee3ac00e7b41c73b7354e0fed4bc191c68ce730c7bfae62387935d7f588b539f68f4558c9a5858b7e34d0e42c4a6a5b99bdfe4107937c65037f0187945a177560aee6992f2798f998e80971abe544c406e029d8e9336ae9b4b14cb1f00c9807aca5230ecfd46dcee8afa5f752007635426e39a6cdd2d5e27754285b3fc79456ca26a9b468aed9d837939ead983b1a012a01963bd22dfbf3028edb6434a2511be8d38af8fd25a4e19a7eb6b0bd9292e95f41c9baa13eaf01884ba29c6733f36836792f2c33718fe11ddb750c30e33f9af00c69fa2c1ecad9e079442b96c5978e4fa2c1ca8d64423b92e230795aac7a23fc03905ff129ed2518b785db9efedd82454a6159b3b5962ecd681b559c86de349b640bf33f5930ae49b4ae6154ba29e7c064d26f16ac7b1da866cb701e5a6030cb94ab01a9de1e816c285b69261a501900b76152c79cbb6bc3e7c3ecb098ad6fe3ae9927db4e670485d985e2763e9c33689bf69ff84fc12a14e467d873f4cc27c60d555679c8c2a48407c0ef114577fa30d2ec20036a42cee22ff67278243f952f57b7e37dc7d7504fe5d004c5ba98c5989d331db9965463a8b6be2720321a545c1ec20f68217d846781cf07635fbaaae552300f76fc54a1c9a0323ec152ea3d973bccda6f6712b94ee094816621916fdbb4b77162ae5a03fe9af721ce90c4d08d7ffcfc4e54afde55d5e87f0caf6da6fa854bd53311951eaaa23b7c9f3048345651d7848d0951ca8ec5b55c3c95db34f51bbab3138d481794b3ada68bed6bba63560dc583248fa14258b205ca4289078f387d9fb09e87c29abed1503e8de4a22bd5f15e22f2a5dfacab2511dfa8d60402010fdd019c08e6b61734130468a6a454ea95cb38f2de105813c17f2136871656a448beea7ef039bfdf980ae4c4d2bf3e8bc64f1bf7ea632f96b61cee9171388632227a7c1f76d1085bf6af82ec104224eb93a46bd77f7dafa199397c60cb7f2b827525045d7d08ccbc83d71fd1c467e316fdc8cfd83624134b12a0ab65ce12735ab199fe8084f6ff77dd190384e07be853e4ab0af181254f6f5a59bfa36cd5bbd68ccc5e3e78b70480d6b8e5935870f770663576f07acd7993199409eb99dac21414daa21bf453845627b13bf2c5a9035eef97d9fac3a59aaa9b8e2572e2f66286f681533eb394195b444207f75d3b61d0ca895a20f6be758341ea7d2dc353281dd53c8c7dcdbe2540add78e9a43dd917989051f0bf429ac267d0c876046bf0ebdb1e8d3278fc74295e3114c71cf8deeff43ecb7c13d7c80fd36947b5aa5683fcffd18a1ee72871be9d8f7c610461e1df616091ba6dc4de16b845de9b435a1f1262c7580379e62c873ffae96ccadf8cae33c8bee1bdc3612d861efbd21f4fac0737752acc168950abc821e0e53b9e681f42d1ee16dc460e1d6ab88875bce9372f6ba4892984bcc49622b59029d8ee203bdab2dd8bffe1de97201fc84d22333313a5ec6a7a434e20753fbd4c502bbabf7f700fe199a6ccf9af8cbd795f9428da16af6c8b6f9c90276e8cacd308be5e23d4858aaa34202b983cd4e09612fdd748dcbb68612a0562b26f526e0b82feb0c9dfd093afd31ade094323884d7eb4ccbbb74029ce76ddc0b0b97c189be5af666c2650e790678e32b918531df6d40a16a726cebb0363cb644862ac22b61c3d71d01e9c60302afeae6d49774bac6ddf0b5060336581ffaa08ffa583e64b1ad360213a680afe491a986af09a62332b322b50b28444f4639801fe5be277b689f1a15238fc4c634b8812f47f2d9539c1c55f381f3f6e2aa00779ef471c4a418d14209ee6fb245aa860ab33613efe16ce4bf38274517c1176957bd8ddc9975858499f20c3aaea69378179ad1d24962958f57b3e9dbe2373aca2bd432e4c42a1bc2acbce3bf94039a10b11e3c6b1050826a8124bb1f8f247938017bb570d8b800a70767fcd1674930a9158891efb492e20f144ecb88457658ff3680947db98220aa83b586dfe1707860200f5cffebb67304e510a404bd6fdff8b54d166bb50b8f236d511befd0dc66dd9f014c8988f470831474c0b7d046435641e02288e89c830a74c139e56e2e8c918adf80bdd54444779a60e60dd1b6531a117cb6ad90e28f38d3b67ad72c2322b3948baf0bc43c55e174060bc3b009d8e176405bba2cc785fb93df90ab137d1ac0d32f0b51ef08ab1634278913cf0768925ce3f19d798d83a1d0fd7cebdf8c682b07521fe3b9737d53d28f148ebc0eee0f51ad027eb472a0bc0ef623e0b1a101a5e8d01199cf567aedebc7400bf5a7c42550036f46eb8b1b467456fa3fbb874c75eb37c8004ca665da582441032b9ab778a35300d38ce7d7abb989cd70a45eb05abc61159d9fe0ff50a52674d9bdb796e27111cd2386714a90a76c0e1b8b8a190c6d966adecfac25aae431a17f292987d187d78551bb8388f04af99af35f77ba18a89448b03ae7b51ab721c7c5328a9c4f0771712ace2f6f6114b8c051492b3818b3130f4405522d323cacd0a55170cf5bb3ecc20e8e1ef18c5ef7d9e83961aae70e8edcc9880fc0881c871cf59200db3e0b9328364664c5637e2eb9cbb8f27da69357a0650d1bdb08ee85bf9064c6791d4a18deacc9c63413f82c4782f66c2a3348cde5d3299c397a0d0cbdf50697094053892ae7655d698e6c2afc7643f6c45f2d8435aad56451c049ad1b82b83c0344b50db96ff1e54659a414d9da026dc2bf256d8e9bed45ebd9f9f2d04962396b54681559d7106e964c4e6e5e0bc5550269f6d1bcf4c2be090c7fa89ca6798b972c4a46199981a38fc8e96015f5bfd345ff0b90458529ed5e2f932e7951029f6b784eaa04dad52103cbc5eb8ce5eac8d2662c27a917e3bf6a330e8b01d96e50f8e7203445587548528aae61106a0044f4ea8b698caa5f5e8a31280047d23e32c75672b4ce420776174ebc4ef8f27089020e4fd517a00a6c85df913bc5cda89758010f700f27dcb57d43e8ce2ad26007d9d94b072d0a7c3e12a093cb504f763690597e49592b868147c65e554dd1eba8374ce5bee1962b024fe2081345af6c1cf68fecec4ae667398e69248a6ee13173d706e55c0b1e9f6c4c2601de7ef31021ac96a7d0ef0fca62966cfebf79711365ea59002815aeb9948a8a9415bdf0418ec3a745ce7517924cb2554cf833fe510fd010f7b5351b3620f6ec12a513ffe4cad4911d82c5d94d428bc751e4c3357018cced91c84ce3d885d4e01cbe975797335ef57004dc0cb12c0c6b0b16999bd21ecc969bb3bcd020dd05d311bb15b8e9a4023d19c13070652e3f92db24e16d0fc4cd754155d8ea544c2991380fee8495ac4c41934da74bd3dcf105dd3ced9a26d57b2d284c0b64ad76022978410acbc929fbbd6e2ec9c2e21456d57b8d409e711a5e2564e16c998dd3f865bf7f771f40f5b5e4c0bc845932aa620a1b70aca257c0a3a50c0b7130f1ea7157a83f41259b1b36d112732025765a96b1bef436282f099275fdcd81b90531967498e5de6555461f208c2033d3a53827b3f8e336d56d9248cda3b3a4d166143523c6f27a85fc3f2d02398a591c8a711402c8a7a42107ca0e43d2f71943972a3e515dd4230bd1e2f6704b47917aacde06f60cb5228fa2e87f84466b3616fe3c4b47987c4225b663157397fb6363795e988da5af6d02f5084307102b69a07bdfac26111fdfcad68e0ef748fe70ec103a76be49048e2f59e5f7dd4e1dec9d67a875a7276b9175ba394dcb0ca0e9e3b5b2f2341dec15a4c10562c3665f56ac8433240c22024f61fad9e7c5f72e79ed2be2a395fbe98d086535b7f8c287b79c6f1b135f09cfc6320811beb31a142868ad1db706eacb758cbf9aed032374cdaf0cfb340b45fab050ad22439bcb838aacb2329c4ff2ace4609e46ad0ce7081e32346ba66495e5caf3147066393ac43afa7cfa185147398f179f37c846377fb8bb0d93f001c1e82ad379fc1adacee6b39d43bd297c9122c132da36dae679a68eb0e3ab77a2a12864038e6da043b5e99513306172b32633f333a2541d4a3c65a3fcf8f1925fc4e435e6084b076a301f5bd1f3a437ab6f7952557801bdfcf4766c997ea158d8d19581ea956bf43b4982bbdc2fa6709638fd331a576f842c23433f0e2c054befd0e1418fed42ef498bbc586302aa01e18d7a84cd69edfca4b310d18168ee36405a2267682542a677cf765a46f44fd959404434b544683209277c1d1f98221162630a02781b1e5ed5bb34dd6b6db0353ee7407a9160fd8df78b181c70562d1a3f6da9f90bb762d59286a55ec2365a18661c8d9ec20178a4d4da7f52ec046f67ca18c27037a18d49c19c7aae7a7f4818e2f54647ba40c7b133bef4384399220be77d3836050e80dcda8b2b902b0d6f65803b004bedc06564d08be902d625a64fa07fad153d583af492cf6f1e3ee2f3d799f4026ff0feac8e310f771e25cb21541fc7806e7a5b38aecf613400d0e2a37fc12d051cebd34db631bc7b072851ee2095f964b68b4eae5f6c3a42a7cc1d1844cf5b9047b19604e4f78754c4f76070a18f9f8ae920e7605b297af837b4f46e3828b04c06eec5eceea13188276de869acaa5728018ec7e3889b51807571ac45407cdca5ab04feb20ac06fe1b5a3da0eaf17dc87f7e598cea064ba147669088a57e2e42de8594583e27c6116b3ec3f8114a7920cf4251ab916c1bad9adc57260119369116b30ef8bbc3fa312ae8369a87df2800a81273381c3304a5b23d04b3f28ad12b13c3f256a2f360c666f698e6332174f9ebc6edd4ff9a3e3e16bb718584979bbe0f5c1c753c912c04345467c47b7b7537c3db678e6f180f0928a9606d62b6122279c3b9490fa3f300c304e1ef2cfee74dc61f7ff6a1069a4f90e4fdc4dc5f50748845fe0396c07396085e08ba45887e74a5a03e9d53f69b2dc97043ad233bf55205c914d3a5881339e5ce3c9359efc9019739d4d8746d8a2c9507138c6c3d23a1cac6cde51e48ab045a28e0291a209235e9af1246f60be4b7b6ef440dc05046f694636b48731bbab9935620921441a217bef2df70e510ba20b890ca7a7a1f57e3c514f3900bbaffae35cf5bdc95bbc4f82b6467e470d257b5868fdd534109477881b375aacb45a6aefb7aa3494b7efc4140e7bd27d091262d621414dd3344dcb5593adc9cc94cae286277712ad72e54cd79d0d58b8d2a703f665a967bf7a405656550506ee943be54e9f9d4b43f79194b917b91303783fff0d81f47dd23550529999ecab285d3367972739bb49ab50cd7a48d0736f15791351ba64cece899a16da48a245ed15572d94f4e331c96a15373a79b491ad3c0a41abc82c6e70b23d095aa5ca2ca6e8c9f6291647b6da7c9e763cc1c8737e4ad79c4ad86a6551962cfb0f08a0bdb42926bfd12a36a4abb37d120b902e99eda4337407924ad20cb9cf7324d9dd972bd2395228f6b672a266d2839480d493422766aee1cabd1d327315bbc08db4bb862b2bfdf915da52807fc2ce29837b74e7708d6ed19dc32dba47f7280c25ad32a44a583d89fb131e897a3a68c5da43b092dd06c932294896ad46bb4b4fca72d01e5dd77536b396748ed6eae920cdcc5a9276ad566bbdb7d9bdaff7de693f67c55d37b916225313e964aec4cd9bced119327166ced739376d45eb5449bd7f781ea4a637e9defbceb91da3f3a673e68b5e93572359315fa5cbef5e12d83a59862579edf597746f5b4192e18a9492cd7b755a27cf7bd7741aae5099e50443fa6d1c32e7d659b8919a482369b76379dcb4ad619b765ba7add86eebe4ed433a53f9ad84bae05642dd4df3b69c8942af793cbc7747e1d33ffccd1b90b6a4945a76eddab665a46f5eb66da4b0ade826ba5576da4af987b993176a29f4ddb5ad98afad0bdb8a79536dce72368a20d7b662bee4b89daed4df6bd73e36d1d51bb61513857675ea643a71da8a79d33a6093b84801041a1a20d008618a3e570a3ba773623f18d8f169950e996851eac0785aa567891290d21a70b543cb52b2ba4ed81c6a71c2482c06903b88dcb731b272bbc02398083427fb0d29cd096b662ab13236a6c5fe11303802063f2dca9c448bedc349501b69112daa3079be67774889248bb6aa85790919c279ef770932f7c6f382788809b6ff08829482a75fa138e0e7350f08cee0ed0c3d4d3a3ef57146a62499964c97ad4a4f8526ca3c0dc78af34ba7ac5f1d92ac4ad4e27c15fa9e0a4715ecbb49c8c2d4210b235d740a16a65556e04936a655442ad4e2769a69b6da4b789c91b750c8cca39d11323357ef7d5e8a918f1ebed7a719a561fd82008e9d4365baa5fa7ce748173d898b8f601ee93003616d91c40d332ee89047c22df23c0e4caeef51bfe2dd0b7124694aef570a1735a7569d776175815d5ec4c8b9c65507270d20f7b1e2ccc65c58755a9c3b66a6efcf579a1599b7cf572aa4cb3e480948bdedce5adb2dd3229116a7a5e9933933340490e64831f21c4d81e848c883e61baeec49610d5776a6553a24a26cb09f713952a2308794474a542b4d9e9f328b24d04c9b3cffd980f332db9d295d12366ae2b45acec57e83aad640af9b20d5f17a9c642ad4e26c2ca4e649d3a2ddc9f36d77fab333923569c0f9d1d2a25659b137a247d2957d9ed6e1de76db86b5b853634300c79ae77c8509a95467a755faf3b2c65a85e6dceed8989da1ad190779323feda0416167da053d833ced0ca8a239dcd7599856b132daa4b5a76ca116838a68ab97204fe6499a091c67e439d42d59c4f70370a439799c2fadb2a2a20e2c68a8eb94d57eab1e35d720730b0db4982359939b1e0f2d6bb96e91c799bb9baeac7a58b155c4121f80f735ec3ec9d364d262f6bb5ad5294857109f31e9aa49b4789f5d3b7343baea350d045562ce9174e67c66be909028fd7985ee586e588bb33b27fc21593d19fc7a008ef3e541e4596162e80ccdac9936adb2eab9c412b627d53fbce09e9bb75324959d64dd2a5bf77209c9ba57c208e0d8e52b8366cbbc9f2f17903ef9b6701c031590af054c94fb7b26a42bfb0dba37571c0bb8f7ca2d540dd6829325ac25068a9d2cad0005ce6d4f0b2598d54c0217182173275d14e6a68093269ca681a0ea46ae373a45660d7c4b2cf21c4010795eca9606e849bd0de99a796a80d21bd22cec2a401c560979677f33bc2159b5250698280238dec8550e79a08995fa0dc897c944a9bfafaa3a44c311cc37a48be67ad16464e5f93ae7fcc843ae29170d598bdb460f5c70e370f54065a276ac0ecd79adabda0c583fde59d2ae751df8ee971bae9aa669f79dafb6699bd6554f0777af6ddbe7dc362edbb2a91dbcf75aaeeb2a0988962ff574d8d96277eebaaefb96dbdb41ff994ba5d23b596b9e5b6cb1457db6ea118e1d50e595fa44eefe449d73ceee9dbb2ecbb87b1f97855bd75dd3baae93599b975e76e7fc86533553d3bee1ab75aeb30b47d5bc76b57b0d6b977aa44eb6d52e829f11b76d9d5518c41224fd7a3a4ebfa773f2350f12c9e04d3bc69abf73260c9e9ba0e6bdf491e3bc73de5738ef5e178e9ee7ddf3bacc2b79e79ebd1f49d0001c9bf80fe9c3ebe11d0c47d3bdaf542a954a2ad555e1185adc999baf9a0a8f4eccfc3dc323232fe1ce5b07e2fef47490bc846926c132d3cc81e03fbc52fa47827580af1e0fca6303024c965966ae4ed95f295cf5e8fcbdf3a7694f5dbb76255a45d3813ae7f1e8ee5dca17f852d8f93b15c243666d760dd25455d992fbcc44134936b614ee4687c31be2cb3c64ced162fd9052e621b3015afca495f40b32b7b8f77d5d6c5969a514c404dfee02af7af4bf0b299d3b786e044bc9da9e9f413c236b1a87c12c65ea0a1b9d97dcddb31d4ac3677982f552a10435d2559974295b4ad0939ac2a4a3f0cc273c6119c4b34f0411a450824a702530c4dd376ab936914b1f1e53d9b31e900ffc56befb4ce6eaf9989f29dcd1ddbb465bb288aa5d4cc141611b1efbdb3b0b581152ab8ca47f26d23f8d72df90cea483600bc071d2196466ef4ecccc9d6b6ebbffbe538f47a973fdb0fdf4766c42a6647d1f523387471364efe321f3f7598f87cc9d4b786e91271e4d70022da4f092278f99579e0eefa4ea81cfba79aeca2ae907bedbf09c9f8783982083975848e9de39c9fa30016464f03264c03287e516b984573d88c8e089c8e0374c44969e8f67ed39c8cce03fe9994e3d1d2612febeef3369f7c2510504672d7c6d91ca6eef1f177a78d5c3fb770f3c57ba94acf1fbf61cf4eb01be3b58e26e3f0f7721d781541e9bc8376c2295e7edfcc85c0d49204e360094595cc193672665710550d6a8a578859e0ed1e2ec414399e7b78b0449bc2fb58b01e7cbc032e79c33cbb26cce9e3db367fe7410664f7738699a93e21604516b2239e826cd1eda2ad1cc97d6c31c9a91ebe7cf16845cafa53ea7c5a613789003633460387dc45c351f7903f0f4eeee8bea7bd4ec016f3fce9e53456121f3622173c64832b57e360067265d3ba9add4f02442c27366fb04b973df77b053a6cfcfae9f3fb367fa4c20f0f68dc15f3c7a20dfd9b7fb13f5890a41302377b83af54f7d14eadda8d9f7bea3e0e7ea86478007c37b0d65ea0decc6a009ec70c57454b80263884923bdb9f73c3753a9d44c354edd67a9b78bd4db86e1c87d08cda9b1762a1c39d73d418ea32f759b3ec3550fd2e74920f88ff48ff40ffcf7e1113c91fe7d07ef9df3740a51a8394d9fdfbbdfef0b47f0744a7de28b3aea17f5d4bd779ad3840a4d3d9af41b0ea9190cbf6b606e4f7ba7711b8914ce9949a406e07c085aa5de48b1b100e99a5945e27889894121a8268fb2888c8f520bb94a30e42acd90ab6443766d68238505c5e6db0198a36f44ff30116da4b4cabd7799516f1ddc5b85d2d70e3ce2895cc3ceeb5a278f5e87c2e04d26d379c86c32dd9a6e6f0a2d085610972ebd0ae22772c54d74b914b61513c53bf5bc7aab7556f64c1c234223725015805d77777777eace000d45b20f596a81869e6e7be98d47d0a5148eb344549a4f505a3bbb24da2428957afb4aa5ed9eb77dde39aed45e6fd9b55da5a5eeebfeba9277aebfee5a3b3db7855a38e6cc076798e1de7d4b90fb643822434f3e034c16b3d4c20c43f92707204b2dcc403376383e410213e47e76033e844527224cee211c55200fe108d60847cd24ac1268be6811a54540c732873d618f4f2eaa5a54a22a5481e06062c213f4131414f483b1cc346c309e9986cdcd143158ce10c2b006e3cec79db92c27f3c972329f122cb38c123cb38cea525f6a0a2a4d255263ea0afe3093c97032994c2693c9706260996708cd3c4368680c95e5a8a80c326404c5c09d4b8e8e8e8eb218159619064c459b00e4c3439f505242757e8c7e8c5e6099572ff0cc2b4c67e80d680ebda13534073060849db99be3939393e393c232bb383a2a22071a435f680b1d542aa33c6570aecc95c1c93493f88ce099497c7eb05034149405172ec29e3c3ba78e6eccd18d01b1cca753df344e1381a7775aa78d904ab5cd8fd1cfcf0f0a851ba65950d3332dd32e389d422959608e4f4e4e8ecfd0374464866e29628406100ca70c8e95b1323899beb453da895d11f4d31385ef0ba56475ee8e6cccd1d1918d99333453073a383693075d17fe18fdfcfc1865fa2245264b1da6cc84992e76d8b6b03357737c7272727c327d267fa6168c88843419dad25868c5992fa99219bbdea8370090e7690f8c4ba602c8b23045b266e74d6552a2b174378351d9f3058145bab44cffe180fd964c9b65b479de244f0a93699e1f33327c6600658b191932168d4563d15834962cc8e6d9d258fae4250920c502c893694f472fd32ae4694fcf7ddd94f633034ba6f695c492258d272c30e70cde506f3a78af439d8eb2269376d30908575b45fe9a6436c93b877bb4793aec498729941cc57789f9ea264adfebce76a089527aea6e4da7cf19e6a0a613edc2d2ed7c753f8df4e4514a3bd33cddc7295c950ecea79ac586a3e9e04ff6274bed3dcae0e6f138dd924ee0774d28cde361fa0a0dc7997940ba6cfa2c92ba26f0deac6696a6358563b52693e994b598e6efc15fa3f9321d1c6f91bc523d20f4a63b877bfaa8d9ee285f24e46b8989d23ffd8774917e0aa77d77c1104877fb1c6daa79626b5fbaa1e9d768a2d063f09b29bc454bccd7b4f9424f02f5b499af5964a2f46de68d690ed6644fbf0f4d3fd970750fbec87c3131368b3de5003c9df200bfe201e93278264eefd34749861f92753a85a07d09656fbea308b2e91cdd726b1c277bf28ae99fe933994cb7a44feae930d1a3ccd5ce648f84291ca516e01ce66b66fb1fd265ba0d8be43dca393afc2159935a6f0777c23c4ef7de5e8f9316dd4f28f0dd57a8af9c7e8f3a85abd4515fa5421f624685cfa0f44ea7f390f9149e66c067301cff4436e1efa5e768db84ecc95f78e730512829aca3d4225fbcb28d9a3098904aa552a348bf85d9c78bedeb4d659a5ad56bb762e67e434ecc35dbd1e1d89ff7db589fe156b5484f5662aee1102dd26da3dbc7546ba99c12b3c543b48aac4ce0a4c97871744ba5925990c95892e5451bad72a365043dc9bccb6ba46dbe4a56be64572b95d6fba8b99048a5d2bba3304347d15174141d85cc1da3e91c476503d2e4ec4d64ecce7db4473dba70ebb1bd147ecfd1737b6fb7dbed767b8ed47536731f9ea3858d16365a988589429ab7a333150deb22dab2b0266a95bed647204c157b4fc332120c4682c1babbbbbbbbbbbbdbc2b68f7dae47df0bda5101a5d4b4987d65c3ee94c62bdd4f0b6b95edd66ede4bf5e787fb76ae3b6fb87641ba8a8c35c81e8148f621cb2ca828ca5ca9f4e109529a164f7225bd496fd2b36f09e048676e0d9da269d399265958fd912c987471e1153b4276e8ebfb8f72dfb86fdc37eea5ef3be7bd61b9053a35ba7a4e520bb3f494fe7a4636ecf045ea42cdeb412a7dbe7ff280f43db5b0163391fb780d546d16366e9f61879d3455fa20a54c7f45228f51bd80b72567594b1b591eeb93b37befbdf7de7befbdf75edc895e8f936c5b5afcac00fb586b930e5bc9ea97f09d282df634a26394d26df3be93d7e3f3aefd3e5bbe8ff4ddc7e9d2838663093c670f627b0ed77f4b00470bbb42868059bbe91bee62dbe9473b859cf28070dfac0feedbc3516b91d21e60fa03c9ca70a4f08c25d0eb510a9d98193c77eec35f0ed87446babcb007d225696ae80e8d71d786aa24f529e146b513672261db26ce3671b689b37d2465a7dbe68d9b06a41476a113337be1b81df47a7cdf56ba15f043b60ba91568dbbed91630940265e3270507d703d7528639baf6d079f5a776a17aa102d5a07a5484a5c5cc08a39c55a29c9d23ddee48977519e5cc4e81276796c8168d6d8f7a74587de864abdc40838dd750c50047b2f4b2bc952d0bb3519099b8869741f505300ba3ad422691de955640b6a63c213d3b13d2f5fd2f5e984c92f49194919ca8f97b9052febe691e90ef5c88f2760c99f90bbbb1fe54cb9232d2555c8148baa006faa9427d92fda7fe8cf5475577be2480a46bb9ebcfa445be67a7471ebeb9ceb48a89aae4a8b6d8c87ec391f4edde39175dc5d6c816b548d462f689ed908d4956f60edb294856f612b65498283cb6a7c5cc0b8fda08dab23e7d929d4573016172a6ba327746c67206fba8a83fd295e58ce677cbc2ea94affaf30ec7264cf2ca899ac1072965f0a473da170410265d5ace6e776891900a0ca016b090b38f568b9c61419e641f2d16393bf591462a225068a44045397bc685238cd61f151886a3fdc9d9ca76cec4882660707e6691999d1e7ab9f121225d9d2b2c83188877d20b6fc776eedc8b8ecb1fd6414fc27667c3361664e68da74fb2e3c84229403e1ee9247647b2b2576c6146f83e3f5a58ceaeb5c863653b77d2fcf1ba7045ba975d8ad3b32d94025525ab1b158a740607ac67d5c1f07674b8f26df75ab870457a4767c42fa4332d66b4d3526089ea59c3c098aae73f6276eea4fbac876dee86ccece3d97ec32a9a9de73e2250ceac50ce54d912c156b22c746266cf48af99a485d9e5da070ab8a0ee39d42d5bc0d9225ff3041245ba261492555f6f38593e243021abd4666a3a2dc301274b76e9c972022bfb763aa93e824d6e8c01ac81166b4f16496dc8448b35d5e29368713e94d7a46a51b6a8013bb3365041c58d14a4701364052d6c4cc60a93b373dce5a74bce84947cf5ecf365669f315386e4d9e74cabb878f649937a867a767af6893373a64eab949e7dc2e6ce8c4d1efb4c7b0634b3eed927517698692493677296919c2339a7bd354d9b5568dde1ed8d63bcd67bef611217c4042c41e61654d3c076818165906015d7ea1a7a926dcf1a1634f68f96294cceb27f4700b9afb2772478e612eeab933c2024e7ae793e48ce919c2321e93a1998e2e4e8c4c0b5c5a54fb24f0e03f244ba34996532cb56241da9d3328591fd45765576124c61d947b2a7b2a3b29f30f5a13f1488067d980a613a4489b2dbac48656931fb0c5c5bfa247b87eb0be63133a5e117b44369e80cadb191b3cb1cbd691e127d6dbbf6ed52d336d24ba84d8bd94d8b59efe4cc8627d33eb616f9d703a2bd7b27e6436b2d53e52c0b572a8caa4b5a509daa8880eee9170450087dc9ed3ba6ed9b48aba86e496e5ddc7ee436758bba7de3e480b7a59d5689354ff7b44fffb44a76abddbe83ba35e3f63dd444dda214c6a848a6332dd99e6bee5e4a8a917598bb07a2f06904a7ee028f9c04bbb80a93bc9a8025df9760ea42358d5aab592ce330f0ea2f308cabf08bda42841857c18b98f1af0a70bebe744ba32796c6569f6cab16b2fd770490bec49e3423ab70100fe4927b1e10d5e93bcf87ead4f3a122912e03539b1b9c189816a92c2ed27585cc5ca2e2e86c4a3ffa12acdd15a6362ddac3c0f486e2b468afc234a7457b124c755ab47781298ceeb4689fc2344679684f531ffad3a27d0953202a44873025a245d4881eb5683f03d3227d624fc2b525a4346700e9a4111a02ca23cdf9fe640a265aec3ee95b59e427d9fe72240f094a83698d18c0b1bab8641a185ba3c8f6f5c5deb05d5a45c284f53a31f30da78bb5b6ed7db72672178b253e00b34b8f87d6b8d622187da2829b5d254b7e8229a509ca4f1faa6529fbab9225311e4d4c5a949740b57eab1e33ec49b3c049137b32bd7aae92b62a5f6558c35abf53fc9d931c7d0db909d279aab54ebf97524ad3a5042f65c9fb09a7348771ad250f8f3348df3e4c0a552dde5f233e2c80638a04adf2a3c5fb4b5f57b42b6995fb31c638d2dfeb3dd94a829edc570f47a6a12a06e0f7278e79d4d31a9a8b4ee99bbe913426cbf614d62aa95b2260aa248f14c644674407bd6ae45c259139e8bbaee35e741d9e2338d38064cd98cc5152ed6f3441a6616d963e816cb9b8a5b7972723b255b8ec2f5e8c7c348d34cbc8c8c88394f2c8c86538635a2cd983265b947dc4ebd12ab57126d82d6dc38d74282223db38410dd43f9dd3a2d56162be9a0a8962ed613c554832642d8f847d930528a5eb4a97cc34a75bcd424fec35181853f5fc275b91521817364d632b8da1304500e78c8de053a7d0be59baa55d6eced12faa929b8cdc74e9ed381d0c290c95e9f03c51985639d9647b2135774b8b76c4d42c2f311a0b0b6a82ed62eb473e360b3290d42b4bab8cdcba4897e723f55abb8a1315f2059aa4abd403aab49aea46b0cd3940ab02414d03c1f123c714a6c57b6f0ad07e9c31d2575227fad3795a250b57d9bb4be1e2b68b69b1a75552a11424b747610d48963dc56b4843b637d900459270c6b808896834a08d0ae73adc08ed2947ecf785df6bfebc100392653f77a46b0b7790c221d33ebb212d22420732958274e667faae9eeda66d4a8f0423d46490a35d27595000658b3485aa54a586d52a23d79289523b6b525fff62225f58a2a480c60af20ccf3239da49396d53295e5932eb2de57b4bf2add63e9b543b046b4b99b6674309565b3bc70d79acd20a50b4216700fc72965680420d5008b5a10d56687141f6a44b5aa1058a2cad00451cb207f6096c15a94a764ae72947b5d5eb65361b679fde0164b60790b977f4b35bbcb29ff662ae371469af6148a6b1b0b00cc93e277c4de066cb29024e9a8c932557b0e7ac6234a1dc570558ff2a612a1c1f13c07a3b25ab279d03273357d2214d7804995be42959932be928e938cd80fd1643ea7175ced9f320f5fdb13ad13dec33204ebe6c0b78b2f66bdf2929292929292b1f62b691b53ca69ca20083cc1b4166cd36f298721f150fa5b5e6fa5bc3956b3f367a4c622594a6744acd4ab44a9f7200b69861d807a9ed75b8727f5f6d8f203367d9fcca9c3ea5d9b4da9b69a48debbcaf049a4ea8d4880b12d50b18ab4e8e00943026e0e4ec5c8cae5bf4e63919dded441b2930a0fba8248f8c8c8ca9c6ec068dd1133a748ec6684bd3868486ec877094a852a0647da4d4335f2689629f024a967d36940d6544dd42c1f5e3e1ebe113bf937f5f8dcfe41f37e43254613efcaf73f968fce362b158ec0bff714242dfc7d5d4d4d47c31fe713b3bdf8c7f9ccbf77d9f8c7f1c0dcdf76d43434343df8b7f5ccbf7adfe6db1984f0ce87bc73621a1efdb6a6abeef1bf9b7edec7c24ff36979798effd6d3434df471aa22776a8e8fbb696ef43fd23c562b18f14fb4eff484213c50a7d1fa9a6e623d57ca49a8ff49d8fb4f3915cbeeffb3e12cd97fdbb079a90ae522c16fb9e8415fade43be34a19a9a9a4fabf9ba7ff4dfcef71af2a5edb8c876f93497ef9bf2a5d17cf5dff79d88e1a8ea81871a261f16600973439dcbd0d050e792ed65d0f8c298500cc76242d93ec6bd185ccd4e0d57c3d5ec64fb92199d0cce85a3e15c62702e9c0b4793ed5725dc8b21ae656868886bc9f630561b8c9850ec452c2694ed5fa848235bcd4ecd56b3d5ec647b1589e66273d968369791cd6573d968b23d492a330d6d2d43270d6d2dd9de05ea9e62b4d5c9908454a690148b0965fb11d0926a483b5f3876372a1548aa21ed64fb548605c98544437221b9905c4834d9cef97de1f8c5d0182d26148bc584b27d8962fa6e86353b3535353bd93e45b34c63b405d20c0429a8711fee723a1c0c66433634991ab2fdb7038edad09046d42d20fe4ad3ad6cc8871f6a7836ee692d279e78ef95a55b190d131ebc1eee653fa107d4ad4c4646c8b39e97e178bf779ad3ad7b7444614734e679598ce79dc674ebfefc5022d6b39e778dbc92a26edd1ceb59cfbb3e9ef7e6e9d695f164803cefe278ef1de952ddb347473747399e77633cefddd22dfbf3d32f3f9e678dbc5450b76c4ece504e91e7591f9f2ffc489749a75b5646664786c74382b41e78ef53a65bf5e868ce1cd5789f9e8d19d2f65a11ef1c90af6ad4ddf3de8474cd2130c87b12ddaa39f4c47aef215fd5674766881bd9bb8d6e55991fad42efdd08e9aaf72eb60a37f690bdd790af8ae3353d6ad1237a629f3d13e9faee795e586f00d65c61d94636a4b5d418fa0f6136e4c3d049a8b5647b29e62f0a60a9872cfbe121ac6112663934c230b447e14f46966532b22c3bdd9b71b32be3decc14a38371ef8571ef054bb8d5bd17c6bdb7f4627371affda722911cdd98231747f6de8876fa31fa39fdfcd877a90c65737c4e39f69ce96a56c6e2d86fa0cd8e6c8c2a1c55a0171e75b5547faad10d7fecb52880348f1950cdf1c9c9c9f139d526fd5caa4cc5a9381faee78ab27aaf088d6e8234bc1e27f923d2c5b4b8e19b3dec1d79469d4c1753a44f2c91f68aa8cc1723f311c9b9399a1251225a448d7874f68ab868349a8be6a2b96834d9beaa6a89a654d32d20bc22d53b6a956ca2e029d4279647d3963644bb97a9521dd121dbbd64fbce157b45ba9821daea64fa65beba28248abded6e542ab087ba194896fdd8c120db6f1d0c14c0aed4034a5082d22bea137b0bd42aa3579409b58acd825a057b459e51b758b7f78a7c2c5f4bab64dd866da632b4253b187a22345fe044b1a71d4c8c763031214d034195ea5226db7bac7397ca4c8a9c3ce94e071995fb632793ede6f178ceb8ef0a30fb8e004ac1baf6198e9e51b6e7b49e546af6a3af9984271078fef489b5d75858d527f615dfdaf9b1d022da4a89b3c3514543f0678ce91695a11d4cabd40e46aa83c9f65486d29667d427f616cc36d7d02b6ad1bee3825794edbb9782566438c99568772a93542b0ba22738a03599663213853e0ba22d29335f594e8ecce470498a336765ab94d20a9b93335f59d044b1b82451529c24d5e40599d679ef0d4b1e359756c9c208adc242afb550ed25639aab57ff99011c2b11062e6001930ad0608202130825b0440420f007cc50c2010d90c180052820012508582501e4003090f8e1e308550f122378ec70a1c3000528229551390840846900a7215e2e5088128e968a174400047003005fc7a5a000c1f2e1071ba44d3b117be0a1c6cd3070010b5480090a4c40024b4400020f50c2010d60c002149000042401e40048fcf071440f2378ecd0618002149173108088010cf1720981a3a512c40d01040000292840b06cfce0c389d8030f356860994d784c787aa63014f453851a3562614d586303841d1d1c294c4c6868ccc032bf65e6b7b89c604606060a1a34586404c9081202032d624424861f68c60acb5ca2b3c23397e8c096f0d31363c28c1939306460c810a1018e0d8d0d4a4a62562aa3995546476f80716189c36a55e4a2c7458f8f164442405750a978485058e694cdcc299b9b2962b09c219090d464938bc9e5250534446256904ab59c706794cca0102834340696a3a232a05041383232323830d84e1326900fcf133a9764ee887444666e90735393835249e68874248bc821e6a5850e5d67b4e1ce9ce6a3f9fc60a16828280b1cd793725350341b198d6ca55ce063b5b1c0c78a5381d7191dc8d70f263e561ee4be6c2541818fd565021f2b8c04fab448abd0789fd641be9a58a2f1fb1448b63c10818f5408021f29d1033e52a32c430bf2355f94f848b9e0235d2539b235611cf091c21af091c618f091f6e419730131dea731b2356f14f0911249c0474a83803e85a12f90afc993c4471a83dca72fd2a52a92add903e4631f1de0236541a257af49f4e363af6148ba5ef0c8d62cf2f1b17d8ef8d8403d3eb650ac555c60215ffd62c4c7ce6247ba5235b2d5303c3ef6cd8e8f9da3e334dd03f96a1b037cec1fe47ecf48d7886cf54d013ef64b111f3b26f7e90ef2d53c393ece2d8a481718245bdd43808f7388888fb36800477541be9a68888ff30b3ab2d5afc923c4c7e98304f9a22f1385e2f83895205dddfb5346b6284cab674d10bff171ce20f76714f2456d86c816bd11400a07e48bf2c04897f7be079a902e0da56510eb49c816eda127d4460ff9a2441385fea487c87d23f76dfc3042ba48cf2e7eec21f7f0f1b9c16cf291d25c43be2a8c8974d91a3351fad905426a8170047305c251957fcc5706c44412b4950551a067023d126862beb29f25887296434fe823008107842ff395f928f102435b998c03c2516b000c03c25195339c05d8dcd0d63d52c04d026e1070c3335f594c123c3db4757f80f41ca00789907e12cdd735fa118e9d6f8e8f5974c42cea41af8f112f30b4756578c0ec80d10163335f17c7003699daa302dc1471936f78e6ebc6e4e0c9f4dd435bf6879e10a087889e0184a32a37d17cd9219a2853fa325f96c2d096a536f3655fae2e12a28b7c323d8e97168c4a184478c326d37a444f78e6cbc64c941edaaa3ff48468beaa006e027003809b141e94705481408423c8eab11112659a03335fd5a7ca549c8942ff4351168f7366cd96688ca7c51e18b8faf4494fed19e23a484a01e3399ab650171861500775d04a7f88a53fadb222330d1a43a524da514abb4f63ad72c3d5fd17ce70756f0f25427520ed675cf52214323325f2fad32a23f5c9f50303582fb3070470a4451f1048d40dbdd01650035bc8eb719285d469526b8f64d515fa2133cf4a89b4cab8723f69eae914d2d5afd70332e77c3fd322daf4e80be0486372ada74563cd63078955a41d74d42a6dd42a479d037952ff036d0860152a26e416ea20fa12546725ca15365fe7bcaff9d218993a611d9e3a33270a79523fe3856ac25097ada8beb61cd197167bf2487f7ba40b0b2bf4733edf39e73ca5a1a41f7b14733d7d69155268634543f12dd67a0dd4b166b0e73dfda5f4da6b298b8bf661059cfc7a542d66374cd19405b40a49fb28bd2a408dc77c225feef2b8b7b7ce7aef01203579495e00c7929cfd25ad22a777c41359c31ca9d693acbda18314dea03d33998d26c8a430a302288fe298b291425b9aa64aa9609048dadb238512d448da65d009744af35366dd59d657cb56ec87f800bc95aefb9559db039265f7da2ba50267adf691de37bdabee8b997419ae5a1bfbf5e2515b699e2c5d74b2cc32732d5d428294b294acdae2b45fd55bbcaa148ff2096d761860e69ce138249332d370a457753c268d07805e3c5d9a61ed396f386932665f0df84466912c7b6e669983008e76a645bb2f31d9fe9e40be2c1122178a6c7f59ec1da4dd22dbdb22b60ed2da21dbdb2339484b876c6f8dec1ba48d43b6b745960dd2ba21db5b229b0669d790eded903583b468c8f656c88e41dadb200b0669c590ed2d90ed82b4b73f560bd27221db5b1f2c487bdb63b590afeb2351acbd42b6b73c760af2757f248abdad82c542be2e9044b1b759647bbb63af90af1b2451ec6d14b2eda109f27585849e90edad8e74f1707bbb04f9ba4312c5de3221db1ab7b74890af4b2451ecad12b2bdc5912e93db5b2ae4eb1649147b5b45b6b737d245e3f69608f2758da435427edd238962235de1ad1d42b6b74090af5b44a258ef568a6c6f7f302451aea504c15c5e642fe84bb4840f6270c64d334c321e43468cd55dab8f9d5d97b2e4af928f9d5f977288212ee5000670298920e2521280009732478e4b99f3a52ca2884b59800218e0521a40c7a5d4b1e352eeb8943c7818d1e3081f3f2ea51197b2c7a53ce252fab8943fce09f10fdf9ff0f7117c7a2a751526790f3dbc06e6e126b8c66960938798c63d1cbeb1f7e3fe0f3cd2a12247464544434241403f3e3d3cb11d984e0ece8d4dcda521325520a811c916a4449d053833fd68676e85d91d193b94ed57051046875a5459dd0fdd1aa385b00aabb00a93919191911912454ab51a036ae91a64abd619b84055a100e36067706cddcc8aa4c1c2510bbb90cea02cb4332ed05a36fce00cb90716c6d0837aa84707c4d1ac4de9a6d7a2ac2853a940302bd2b470a4373fb00f7c04ee818dc03cf00eac031b008f1728db1700178133ce81098089c003c043e01776e1f1e28cf7e6dadc9af1d28c9688965fdcc3b4055317fa2259f62698c2601a2359f63cd8f7802911c9b217b1907982479a826cef83fd0f78a42fc8f636f048a318290db23d1018058f540723ed41b607001e2910b27d00f048a7c8f602c02325c248a9c8f641e0912221dbabe0912e21dbb7f0489b90ed71e0915e31522c463a85916a91ed67e0916261a45aa05da060c816bf66dc7566463573ac652a345f153651ec2918e4ebd2d0880148ba505d90af5b53f3856c4f7fa4cbc5edb5b1b11c16e4ebdedc64a147ba485ac8d7c5c1b9028f746d5390af9b9353859874552ce4ebeae864b1235d425c215f17068b024cbaba26c8d7ddd979828e74694b90af1b8b3121dbd31ce942827c5d9e8962cfa3846c4f716ecf44b13df64404f9ba3e53c8d7fd01827c5da01ec8d70d9a2856ba466aa42b4533235daafb1801480a7baa03f9ba423790af3b34512c0de4eb1245215fb768a25822d2b52ab98ccfb810e501910d8a66205d50d8bfeed144b1d9ed690ae4eb16992856baf0ede909e48b0ed923ea02ba826ccfc9c0313e03cb380ac3b80b8c3a875dfc78c64998fb8649af78bb100f431a344c4c6ad4e081871e7a10c593131f7cf8e1071b36582c2080404149490140000470238820627c854baec2ab9390347ef1131ef9874fbff83bbeefb07621f0ed4c769bfd26db9c30ac301a1a34686a4c4c6a6c6ad4b0b9e181871b9c1e7ac0c911c51c9d93131d980f3ec0767ef8612766c3468c87c5e2e90102881e1f1494df9f94a77cec0b0480a000dcfe0a0960e80651100700500082b200846e0c054194ed6f918a51eb0887102a452d231c1f3b0bf11742e0d1e6ec38700b8f5eceae8283c0e348ce7e030b008f3172f600e011e7ec00c0638d9c3d05a3e09195b303814700e4ec2c3c0691b3dbc0a31039fb0f781c40ceee031ee51539fb091e251639bb884739859cbd87ec3ce05162416a216737c1a3ec42ce4e038f120c397b884739869c5d4a966465c75893acecc79d64659f8141c9ca2e03a7242b7b0cac92acec25b844b2b2aff0252bbb0a9b485676122c4a56f614b62159d947708a64653fe11b9295fdc338242bfbc5434856760de730801192957dc33f242b3b09272159d939bc00c9caee022b2159d9517809c9ca0e03332159b26bd7afe6dcf6d29b88b354bdd0b49dd99351b2cce2084099ab36abd99c950be2b1ea0568ca6e767a9cf40ac463d50b49ca3348da986590ad59853cb1a74163b542a1f238b93ccea01665e9137bc942592a8c569848540570bc47f948babedbcfa090bed8539b56397dc4d298cc425bb486a306822a55165514c99672d66f7c2267a72c1546612c20bd51e1a8301612ab050976595c582c4f8bb6a74f6c0fe489fd9158f49999d1ce4ca151012e417545b637349265dfe1a11b20647b5bc4e844aa21cbc3052cde041512404b049b857646b22c15c0ec23c8c917505015b02a88e4ee06e810f51178b254a94e9fe0967d9e8e0aab3badd2425da48b05d265833903a9d2cdd72d328d0033a5983cc8b688cc59d61d09ea82ba70b13a09afea4956f7a4d5950209245a3a373b39164672d23912c98a44fa7d255dcc2f4e427a21e423bd5e4aba5f7df6ec25cf47adf519cdc695910f39fd74d41ba6480603936545eae5c9d48da450972a1f4e7272883e8d7cb894e787bf0f7b1fee3ecc7d78fb30e9c3da87b30fdf1ffbe13a61686ee2c3fde1995d84365aec146e20928b6e829bb44f91ae2b4e961fa98a0660c2428b2c650691edb99abd66dcb7118f074937c1ed53824f105c68bc1859d6c89264e547549dec9ee1baa39a70190633874b791291aceddfd19739ecf97899c3dd4d9739ccd1172e73781bda3287493ba4cc61ad67b4cce1acc8fde1b0c5c175c248d686696ec208c9da9e753691ac2d4637c12914e3a5c5c6e9569e0e4a5beccc69c0dbdb29a40b0db60c52a59bafb6d922db9ea1831b6c0ccf47f7accbb2ac6bd7cc54c28da30165fb9c6ccf5d3c360e65a127b60668c667a6c5588b4c18294bdfe421d1bfa0d7a37f6b0d7760f50ae9eadb580e52d266febafb3d835a6ccbc323c489ca7a228fabd6421ba3692160e6243eb60f908fcd7300242c02423b134a31891f884a758050f6090b12a1ec13966cefed4c8b344e00fbab9f90fa201ec8f76302987dfb68676adf503bb3d23e8a3b569a66632d5a3b53334f905994b55f4f4785656b5f6175a755b6ecdd924504f1c0769407e434fb998f7ed6cfb295903a855ab4e3079eb01fe2e9c87ec39598692d616c5a658b90add827dd41305550b6dc8eede22a5936f7b9ecca99bde6ac9fd9278c6812488193269cc5ad01976c4fe574a9dd35e4bacafebc1d34cbb07ea727f61a2c064a117c8f5c4e99c01cf4c4a22e9918395d9ee3f0ccdcccd67e5300e9b96b613872ac4da552a89f503fa17e4abda65e53afe74cdd0453ffac006f3d75586aa8d3ce497dec25e4532a95ea9b542a15a29ac69af634fd691ad474c8d3d19d4e2a16108b1aae92452b461d75eeeb3c8f476cc64eb0eeee2f0616a5b891ae48a7554c1446c59f5a0d468b813daa9f078943a873249d9594d2143d13f335250a2503089eb48c76335634a750fa531f418d14a1660c3563a88f2654ac847a095542a15097a814eaa9af5c7a3c50e14a09f5540915ae504fa14ea1094f9816659f9c4ecd723a9d4e1f456f1b674c87258763364bb31c86ca9c8efa486f90ed5140503fc1c45019e992a81075c24ecc8c0a03aa0ea8f4cb36b6491032440300000000000315000030100c068462d1602c909551660714800e8ea0505e529d48b4204691103286208300000c0000000000800100bed418b07f358df66c33a4ad9c81b2c03720895d4dbcb5fe4e9164a4ab296484c07957c2d8320b44e39fdc522a1b4a3177e5234aa3434fcec69c8d767a7cd61362d530b2d83aedeaa67a5354f412a27871c315b4dad6fc92cfc5f716b30e80188c1f8a6749a98ba5a50a96b5c7e7ef97812c19ce5ede2714b01976197396af328f05438d210bddd940649f9cc4772c5da6ecf39208d2ec155ecdf263506584b6dc4925897c47cbb2491a209a5fffb2ecdb1ccff6d09c6564e574368bf1cbb2f2ea080bfe2a7933f884d925e7dff794c7b71180f2b04e38fd9ed30050c8bdc583ad411aad07685a1888622461702e68fec751cf8bf82476aaee26dbd298d5b4061ac7b2dbd729352f920c89f3b6ebb281a826b080be092e9589ec625f371abd75bbb6240802d2962ecc978b589cd4dfd68ee28357ba400bcfd6c3af8c763ecf62a330d2dca19e096aa547d961a46da7d337d0d54eef909e5c6acdce0e8a5a20910cd8fa7d305d762927b7b3ad6c57c659ad6898ce4a1869816158661cb865b55d601df1b25c9f3640c257a3beb9d0b87cf77c64d10ad4326e862cf637d5315533fd79629dd856f1bc7692676b6e8a863a49df07cd24d7aaa8cb6bc749f1f59f822866da4ff0b6f5b2e61f70e9c127bb14e52278af1d1286fb8292667c5fdd36a82dcabd98b9abf6ba323b7171c10f1022b5298d6c8472363792b0a8c7985aca3eae3d511682cca96f16750320e2001de3e944e00f14c6414fb0f597b3d50739ecb735a99fed8a1f07193ba4b4818b5e30c161df95ac082c3dab7ecb81bdd5692287a9ae73ecbf8713b9bb3b2b2d5f79d18c0600cd05c9b80c00e069aa18355769841daab2d9d4ad43ed6adf5ab2d6164ac7bd74bf15e920e162ecbead60a7209bc3c8a9b06533fdc97f049e8947ad3dee894d28e2f21810eeced30f7ab669e63b6a7b5d72f59b3d0ea6b3cc4f9b2e1516854b981f7327c43a385e79bbf642e9674ea7257706640e92633c4fcda59f9e440b5fe42b7a44a0a46a6e4faf6d06d77cab6abac055aa514a07fa5941fa522498ca5f217aa5c623db0dbcb0b253a350668f354f37e798380f19888e7b15d4d6f752a8da2ebfae85a7b403bd9231bb44b0685d1746cd3feed192b1601c42f069f96589a04c8b729d357744b29d25cdc954ec77495e49f53ab89db9d14f5ce33460ead4123d5a96426bb8b780ed581f9f9e69d85002b0cb9cd9bfafcd9fb26699f2d20fd264e45469d43169eb983ff140414499c388e0492ccc228f4c7b86013b459377e00b0438e1c19229024218cb8d1619d9aa251098119469c8be08d518b9254ba393e3aaf7050f83cce83899bf383159a32144a1ec7c4b782da672dd6ec068993bff97409f2e074cc368ed4e1c9368570ae48b768549a261d3983eeba0ad879c4d4ab1b21c1f35778a763943370a148bcaaf38262b7643371af598108e2f16e9289146d2456852c42bd2f47b941f173b450bb5f2bc9f235efccb98b60375a7e8c15a7239ed6aa8c93460b47fd02e7bebf2f0773cb4f23acf81a816b2680dc4a8977c56f1349a9edf571b565c2debe9db2d8a98ef886f3c81e1834bcfe56105d7700586b0ad692b5e57a68263787a1794597693c93144c4378ab87deebef90ade9029b517e8e49960deb84db46a663a72d6189fe18ecff64913cff26fde4616af0e5f8eb55039908c8b5598b1d9cbbfbe69478ad5227bc3e576f2b3e0c4ebee94593793c254d2db2162e106a987ea80daa7ec42ff8a97457bac2e317f026e6a4c59b0cdcd51e7163e619bd099e5735068a95af53daddb57c47142d75d302403520734c8f5660097405759290afeacea28043d9006d8aaa5f92cb0bb7959d5655600c717a794a70733a42d1e4a488d61f88ad1319eefb1999b8d9c6d984be840625784a4602689d8e06dc5b1ca08340729d267c59bca32427126bd237154d9cbbbb9cc696362b0dae4c9682de35ea7fd1309390df205bca2ff78f20c283d098a4109d8fc43ff1cd2a3731f5b84eb0561e132253720401af158544166b978733c7d9a948b600b52bc822768bb49aa8cfcd77201b448845e2d97eefc44486ab9ecce2f42af964b777e2224b55c76074f5e69b190f128e5b230388513964b776ce5950b943180592ed63cc4e79566c6fa7b2456f30ada123106147ac1e218c010b9a70502cb2e7b97d84e4d56d4aa872dbd8f4809e94e2cdcecc2bb15acfd84fdf6b20b0eb14c340dc23fb640df7dc4b2fb58628ecb2e0a2c21b994b5546b28dc39271612465dc0978fc192b422afcab8223355595dcee2cd9d74dab794cbdb70db75c9e443e2212b4f1090e539483a4be3dff506db38894027b550277741477a414f5613285d1f251db885ab3d22e7f98381bcddf0e23b73ed6600975741dcd435c85cdb6742f1380e05a6c10d039bdd393b0343b5814e56af094639e1c33a0b2cb88e799184092c3221d90a8abca3dc7fb4133a42f4d0a0825c404443b14ba588e3b78b696c8a0a41128ab50f6d9286f0413fa7d82fe50073f1461072f55580166eeb0ad111b7cfed79bc997d5af65f32ff149e9d426a4eec4cded14aaca696c25b5977ccce3f0fbfa67a36b55d3bf171dda4e74e174807f27052c9350b4ec7c75c21a422e611e7aa53f225c5a745d2224ab68103783b422ca7737aa24ab3cc6e96a67b44b6486edfa0098fbde65b7d863e4118640ed1365a41fa5689bbc49c1ae13796723a21d56c32317e84a09e604306c7d87c1d25daef4bc1fae54c41529454c27ab409b0889c80d7d904e8fcd0f84d6c93970f9e47901657adfe6f73171e3cf7142cb4c7cf2018f707942c7532201f8a2895f6901ce65d22b6437afd33a31958f4b02e85616996b1cb8509b9248d9c2cdca9bd3702d6cea04ae8873e97c3645fb5befc35876441edbd19a8906aff60a29040174cd038fe035c837d13c7fcd4b29b398816db16af51aea3e799dee9894241c762a7fb04e968239024f8a7dbe7cd68607ff4e52d333633aa68153140f1282f4a659809206a847b5994212ce561243adabea58738004fb23c24dd86ac1858bafb611150019ebb2db0648eb9924af1b7d82ca58bac780d2d1c1097cae2a261019fd5da3d16b5e70df8603b19c6bfd36892b5127f70ac80bb73f6808dd5a39f7f811f6020790b2e1a092045cbd4c08ac54ab565397996eef39b558553e52514946ae6b87ab548ee46b59d0428b48d88e1823a557e3067ed6f92c5a01a27ce0c94c2d015a80813ee91b7952e31965f597cb9e8241a8ea7c7fc81385376ff06610e7dc2114a71794b1f2ac0c83584b77ba68dc56e3f1c4a92c3091598f4c4d394849d4556d0810fae8b6b5f2da7b5b39b1d9c78ca420a5651e100c29aa497b6bd4997891e5c353b7fc12037b8066ded73b9c9ef38fb07ab4ddcb4b43ff93651caecb3af24a18c17fcaeffc09506b542967d17838b4477e9a2e82036df2be6f616522d01c4d34464b0186dc3e4f5cce45abf2013aeef7cd89dcd6b37d8c463ceace01679f22fe8d31866fb15ff7679e7e8724686dea360f34942f8c1a6eb471ddaa3766e685cda864222a045102b4e2a869eb2ec8c7705530ecc14ccd57512fa694e1a3585b386246f0800c97fef8be9d1d92e389e679df9965cf7aac651b8aabf1e25de0b7d94165bc3743d5bd197752dac60f0a3a4989d97c4fb4b2f871fd1591476bf3f47ec07a19ed1d564b587d61231419a806c1e9fd0d057e0ecbea80a7edf204951233e6afb1f5cb13cb26f535e253e3dd2769654cc0b8a55f435fd7bbaefd49f12bfa7fa4eff29f973a2cfd4dfd2bf27fb4cff2df57337bfe573b4825d7530d14a75ec27cf37fdf1051f218be10b568f85a7e53da7315a6b7728f6da9236b3f03552afd002e7a9735a2a7c8185ac3127644424d7ca50e7cbfb21fd3cdb2d2c4f949535fc66c5e32dfa37c2d85afe07c3f2d76aff7dea4cac23ad6cdab6df3e7126d5d1561e6dcdbf1fd205876fcb9ad3be371ee2da63cb848d22546f09b4465893af69f21ff2a6a59fd9ce5c186ef07618dffe0ca109564ce1b2bd01e18def2741d63719003c482a4c0b0c28d5810a1fe69340105d101a639f68dea2a8462f15a9fb8cdb52cb99c0046ef533505d500d7d694080a6a67a34b9ff5a58b307407b9347f93790e6fe23d6d60c90922cfabc7d72241fee1350c457ebef21fb822ea2caa326bef6da6f5d433bd7e34d7b66b2f8c14f7b2ea2604fc24392f0b5c48ab69e8c67b7158b6f820d5923c3c5d94cdf62cf9feea733803db69df59673a0bf57b60d0fdc7a858f355a02115a212e4f343d9fc21863f06ffbee102d96b871c7848fa1cef057005232ee5409145c96196fa846138b120564ed4f8cbdb22bc3c329d9fde6d1236976672f938defd58ab81581a4b4669625e76448dc6f6514b66dddf555f43409820c474f97072a3c41e348ec2d1b42c7c391a1c08a5d01114395aaa723f6940b0dd4554ec1729d3b0be4be1e49e64f41996e3d1ba4e01b49329c4a027a130288ac4297a31aac7f28f1f0e024872cc93756dba29d544e9ccf9534750dcd9ca2c28f602e9fa32482d1309c7cf8881325551905bda3f3530e7b204b4c9a5b5897260c52ac705b3c33844412ef566c6e0c0cb4ac665b1fa10085637f8a30a5fc0d1980ee52ece005fb6280764bee74574299f983647b7bc14807227f1bc7d4df3c31fbbda8f5933d73c4ed8819c741658487ae453d0edddd9d249d5dee1dd7102ab5ad8275f7e5a0b5270e4315110963322e05dd7f8251d83fcd373c34c43af0ed2a61401b4b930dd2e9857b47721906c090ba3ab22abb532147be7a0cd711a7bdc56c52085979a1155f2112bb93af35fc4c0d9aa36a0c9b71a575a7557066ca362f1244806ede18acf74e5d25175134643432a3b5d77769b6d5a70f871466fc6ed5df4d57deeb92c56e77f33caa3d4d422cb9e8d6f6efacbb7113f78d13dd49c16288e33b4d5c8e2d20f194172b2d0da47fb37592fe368e682c4080fbf0a01b23107744b9d3abafc9d3bae40b8d42bc01442ed2ffe21b91f5641b221c1c11af5dbfb186f4edff015122273d32574c521e156e809423ac001afda372998fff8f02f99a4698ce0083a593161bf818a3af5601e03ff6b6f0d17f9669b21581ef8775a648e09885dbe3b3dd2d8dec27495d05ba5e32d965a98a478aa56c1074c944c093c75845eecce61609274ec7d78d0c811842523ff85567fa46963816e3bc4d985da9198a95c1f63bc62779c2b43c9d7aab9e74a36b310f2b304dd0b12710f4febca7a652330364e77dff2855a08c785003cf23545ac8ad0036b2c64befd28c4aa9d8b0bc8cfa72fc4a780805e28b15079516c2f1722ac6728dc0db3f7200b34e8bf307d7e3241ab4d690bd2bdf136de75276162fe5c2178ad3ec4c3296cd7ce0a5d47bd3f780fb49804d8ab37d596b7e6cfb40fef5a351dffac1d15f6dbae8a56eb1a89ebe324a4bbb307aa3ed138dd02fc8b72e7e31ca75269a3dd72a481931dcc33623970a5fa89789bb29bf3234e3c6a383101fcf8e70c26a0cb4d4f0ba00a1f87908122cc591e448f7b9a08e83e6127997350803d11354ee01aff09276eb49af724de6252d638c9ac9f9be10f624bdafcd90797eb2ccc79e490fd48c8cfa2a3c1dd3a09de2c84d5e0ab5c4ad4db095b49c90e6b6dfe77a54e4d5b43f868729f508e5daf71cae7ac11a2afc35d237be733200575dfe0309f3474bb35944ddd579e2f91ed58d62fb99d159950124bcb12ed6878a6ed66d432a7a63b33e68888b2188132165864937f55f388cde0f7ad376eac701c42608a293613b76e1cb886cc30eac0212124b9bcd722503c40dd386c42a0ddfa565a6ac6d281c0c9a9d41da26efe220d3bb86b94e9c577b219b2f82b1ed2870fba4fb88fa8b63613225f5debea36467011e48997209622d7a2708d0a10eeb620bdad3acd80524f93cbc8e8c5186308fe62bb4686590fa043f13b14e595d2b8df46d0d0df8e82e66556e43cb2394ab24253ba5ed86888d78670024bcbfc280709165d8e8165ebbdac68bfbfa2b1474ac8b7b33282e0882982951ff70d12d2d2abe2be2d4d4a3de1344640fd580a0c127aa26475cad6fcf530dda734632fd8b773048d6fd2a3103244b2ebc2726dd5ccea21e5d2d185cc322930bb2c294c366c95f4b9e2e2a06611f5185cdac8c29e07e0d0f53340043d1f65edcaa890ebfbf8912163d0d5c0e1c8ec3f0ba8674b1bde970c84bd5c9670a68b976f9e8f5929594afeeff07cc212945048eb1ab969b2174a50b8eac2e461203d64d9336f846061d339e2d105f82934922b144a91db115f627845d253e9b6de9d130cb65b5f07ece911c809ef70334fb695bbcc24416ffaf310dd0435046252e070a8e615accacb39d2121109432295ab608aa8df1b31020a015c9de2e8386353375d8005b6c88bd4aa66eb5c2e9ebb26ad1e50d68587014386d4a0f21febce4e8cd30222e9845ddd369432d8b6d966783a4beabd32593e55a898b3496633f7ae470fa2365db19251f790458b839164afdb2fc7ebee1f35965097fc645f9fd453d11363d7140de974a281e5516ecabc2875ec6fcd3a9bb46c7fa467a6fa012841d515a242508e37494103d5020f361b88366aefcb88149c464fc88679db08cce16c1127028118f5953089ab451be3016d8cf60c87786f8d7081ef61fd47161543bafe7e2b24f8c5bbfb645bd48baa56480b6deacf9e4b9f0c432ce938cadaba32cf7799b555093bf65788c2bbb774b04f8e7c67a26fa3fa77f0cd5bcb8b4dedae244ac99003fe36ac3310ceed8e756334144e6445222f52cc596761346c728454bf14c0848bcd5609fff82647830c6904c2483a920fe4e0e92fb56af181417eeeb3b8b817c4edfccbd4fedebac185843068477c8a099c84a2499644a5cded1f7b58c8ebd811a46bf53d6e7cd3af84c057b15b72c07e2d9bfdda2c64611644cda9ad9534309ede8146e56fdfce1993731440fa23bc382b9ac39cbf565eebcda93e272b39892829b9156a596caa45e5bc1e917950abc16a32b060627cdbbb6301bfc484f981ab0d119c78b03a56b1688f0a18bd6143cc2929705f6a284ab7f56c88b7cf06abcd1b013ea0fbfae5f101f3f3870eb86f733df0846d206fea24c9f028f9be1bdfc46c3bd6675e8d1b17fb0f620fe07144439fb906f86b8d753d8b429e777100500540011c0ccd26d0071e9e01d99254cac2266c02c067494e296a312ef584464793de54c6fa1d21d480bc3a00a785dd8dbdb64f43d37591837cf2397604789c88ff0b7e5a8387c3c69c3f20af99bd46600cc0a9c3b9e8e4380fd3ac96452319d6effcfd2290146bb75669871e56e4e9ad1ccc6579e17ebfe1279dcf654701606056f8da7c14826902304af64569623160c3240d8cb45c62a460bd69da576a66d03a688e0e453daa0e65fc20b719e9f19a5a669c41637e34db7ffe658954209eb00645b163abf6ceaa65677eed1aa3d4090ddafa969442222a8b299c0410a7fd59ccd5ae1721b69fcc6a9d19aa0f9b9eacf24ddbf80d3f49a5fbb3ccb263a466cd237a2d40ca5514d09fd9c435a779ac9d06580b4e754181eec6e4e50e5ec5731549946faa233efcfe495de2be9b73d8ae583e1d8abb3b16f5bfafd108fef466a939868a5d928f2dd2464bc203085f96a3f627ff0f4804fe49e9023979b5722fa72ec949f60301d4af016768e2a18db7d55ae90f60359dc7542074c7c01bda84cdb15359db40216d88811432c205401dc8d571243ae2975b5ed2bc3963f36f0bc4fa15d5cc191acea6b1bbce7b1037dd313bc94c288a5d70aae31ef9b121aef803706b7f3df2c15ee359d667533b240163843bed1bf797f23aae99defc52e57e3e886e11a0523b6c6066fce8812e81231534b33cd353b8df26de3909bcdea5230e28c9996cacbf653c7d98765707c53b0cc78a0f38d9750ee608b4f0bd04da37b3d42aade982f62497290844f1479f4770b3341202097f514ad5592b722e079855e1b9dab9de0550aa1c63b3cb2d1ac68af3fc6823f15824efad860e855b189a427be115d57f6f28cbf24691a8b457fdee1dc773d78735bd0a525d1f1cd1ad872a62f3a7171cc26cd02f54692925d267998a80d30d647f580909f48f2eb920a89788f1ff443ca83d63681e168c1ca629844d020d1558aed816272ee96b10cace4795d3a8c5b82f1478aea0dfe89085d41e971cc76847f1aa32cc35ad8349d0a150c44d381f84c3ecf28609b05c99b0c60220245a7041fa902d1fd6c71f23b5e794158ca331fde2642934d6ffd2efc86b1ec4b7e2f1bc579abeebb28d56f5868eb545a0e20486d1e78d42f9b041eff1110b385298798047076be1f93e1cd8e5073015ee7b6bc781ee110ed870047cbff808736c290c27bb1357809432aa40d40b24a55832f1f51254af309d22d7fdab4b28f5dcff79df52f95ad8a336f38c8850533360ec01188ccd42d2071fa1ba2eab59bef9fe835e7cf72e58ac598ed7e5f3c086d6210146ee1e7a45f4bd31bd7c71dc95d6ec3908eef954f293835340171fe01fe141b80d33993b0727819f32d6405a534a78b099aee86bccaedfa6df5c20c32991a0dabb1c57ea77a0152dea83f740f8654eba2c76a87f3fc198147620d340b6b4347ad7b7f5f4dd6d695d023839f399ae78e9f8c80724e675a148c8f8d4bb84665ed494e479def63c774488ac5c8f187e023a1343daae3efd7c3de3700791f09e590c70c832a941448f438c42be3b6d42f74b298f77ef2e159ec5b6090d5ce423df71c06bb9aa20b363a1c4fddc5bd7325a2139f336a521f695a35e7bf060e3c205a5a223415a19a9680a76c1f96f84abcd61f69a4d4949f484bbee082724bcbaf50c6e0ed27b5531ee4ba2a6fc983b0fae7f74e268e81fb521275fa5ae7c5dab610cb2c0d98e5c857b37dd3f43506b02c1cb31f4ee930e8c42a8fed4cce9f32e2970714289641ae65b7b2b36be51383a63fcd4c69273d622f048095e99c087274fd636b3da14d51b537093b91e6f3273472c6658ffdd844af9c096cf34e900336ad08cd3f6c17635f466b4fd1a25bb1237b4bf832035c2fea36e3f335485c116dc4221b4e4d641d6a26e8c930ef9280bd0773110f83b2b5c69c787d2d2a1e20c5b825bf73d630bcbc3f8ab0074a1b6973860c56915def1651d70bcd1d0004ee9d1018fb88352766d86a3d86eb7bc7c12b6863519c6ce8428e0a54c80213dc3c4cea00a5209b72fcd79aabc9b0c9b85422970d9284a62bf26e7ba38e9f4aa64bc03252bb358b7c174fafcd88ae2f84132ea12dc5a5766726d8c0e14324b50093a594ea4220fae54459aa27c9fd89b4b046f957acd332d8f1100adc76e3d29dbe4759f69f9b66b7c4011f23dc1ada38fe861690901c448de43158d45fa09c4ce9120b030d7e8ec5b0617e0cbbd5440538193466aa49b9cccd8ff3d030241a850c4cd14f26a03bed662167aa24a100e989e634ff389b8febafd53c515c58744d5aa6c72e2d82e9f344c6f840cb662a8eff5fd92d1d7488241079c767c5e83544d3caacc52e30160cb8cbd5b621dc916824f14e811a00a793f9d97ee292f3d404c57911ad338603a9bd7df9e2540012523eebb0e5687f3620fe75acac518b75b99c427c0360f286c565bbad2272af3c0d5bb28d74954fc05071f3a85b474e6fe3076f4650aacfe0803fb9d5c52d61b1d82ca826189663168717dcb6d0c5c1d1c50b52884f73237515e6d4ceb058db90301f0029269f095ccf443f6e94d81f22b937d1a62184470e4c8394380a5a92df5996b2564b6e7ea0c533013aab059fa9ff599d09413c0cff3b893bce29b9680aa5b51fbd61714f23401578ba4d5ecddff9fc04ecb711cbce0a1fa7725becb06e13f5b4816a9789f1d0ae1622df5265006fd251ba67ba16f0c21bb056eb40dbac3f5685d27d3e3a4d30067cad94a2419cd84ac85b9480aad3991acd56e2f4541ae7c8b0d65fd14f98675efe4ab23a87dd3c8022cc0b704561fd79ef3bd74d1fafbe8156233cfbded177215cb12a580c53d5a8b4bf6af144fd1f8eb439bdca9964d98be081f97305365186c3b91119fc9614de7c1913ebe3797bc6d610602d5cde0e09097e4697bd668e51f049a422358709c0ec079bfff735d8e89db40e3cd02d887b2062299f1c7eb672397fe0280119e74d5291f314123d8ab924c61238b8e728fb9b157a5aa46482c24b19ca328bf78482627cdff79ec8f7737af32990b6bc52319703d3253bac404a7604ab07ffac050dba28ef928d681e84cb5eb9b70eafa393e06f48fb24047be5bd123d7aec193ae8507a6cba0eab083cbcc12fee2d5bc9cc4c422e0a42a4cf0534999bfc0dbc744daf3af90395fab09cde5f8b20a8cb7621ae9acf35df5512c6506779838bd7c125aa0548f7a613f4d9d8fdc35a6dfe8fc1e38c02fe2d0866c69ab211bd260de3e1e6aba6503886ea1fd62f8a761cf256110ec399613509f001c6147fe8de893e88dab2e018294f89bba12af0fb9e6241c12d8c2a935f35795ddb4064f4e353503e7b81bfb902932effbdd5d66de136bcb1b4022c2d2e6bf357b7e960e20732af3812c3ab3b20468fa302e80e995e8a40daad0ec9053d945ebb18043af0d1c669eab2889f9fbc3b382bbdb45eeacbf39f0be38ae9b3d788d396edd37915da563dcd46b4052caf0bd6b22cc0e4cda6a169d3cd915da6c498184e43bebb339d1c4ca06b0e5231028328fded950734811ac2e71cf0e72af76ddf78d89207571fba3f4e541128014b13016dcb40ab5ebd95827439963142ca9b07cc5758e08b059947416d96b572c5923715ea36615c80da85dbfa381cd3c8647d32cf0874c32c9e4f3b15eade7376e564b7a58c5c13998d1614e6d6d2a05670d51f9058cf8516b23da098c69c407c834ab17a6fc512d463fbf5c268e3080648daef3ff53a59343073791939a52bafd76159593d2be66f6503e562ef5f048cd9f5a973acec5c329ae0ec862ec4dffab4311c4ddbe56440b06d230503cd2d66bdfe749d1b52bb56e8110de207265a4bb98eab6fc89ef577110139fe6232b7d7f30076108c85e2d8d2ba15f2d237bd792163c8f3799136d93a3df183726ba9a552aad294a6f44a29955295a634a5575aa994ae14a528add24aa774a52a45e94de9b995884e6e00ac9353adc99125a4895c71776a0a6349eae48d296d6f5b22f2cd88609402a0e58ef5b47b40b5839aff70a6eb50b3323f7a218ce27c868459ccd6d0081c8cb74fcd38707f713742c53bfc99e82271cbbcc04de4913333e63a683d8549bcd8e1ff7e0819bff158d17a7165f0de241930a899487d286c1c6c96bd996ae4df31376aafd2667fe9ca2d6d9c9d39c859c2d79a605482b76605b1c91276db6aeaf7403be29d44c4936016109bbcd65a88d95a42a952267e79fea3b644366ac66b2b5a0bb4a44d201c5b2196f358c5fa0d60fb8d66f6b0f300c55a8daac191b79e0d4ae3ae3978af1ee960f3f9f5b5bffe3eb08bd27c1ba7fdc5c677c9e93b7e749128c8f3838ebfa5540c22f5f06b73fc3749edff3f99134481f96f51490fa29dfab70ee6ef12f97ffe193c8882daafc8c4b8bff1c6f0bdcc69c0f5a016e04b4784200a36bfd62143106ddadee063f72ad5ec7ba24e0c9bf505cd4d4282a8f44917d9fcd75841972e28cc8474ec20aacd7f20f1c120dab3d7aa378b17b323e0d01b3d288123544310a9e55fe6a52c88d4cd0925da8653fdfd45e1b7ef29cd491f4582c893f82beacf06d19299576ba198ef3e88c03ea2dd5e81c364045132d5a7c93da84e040ca25dffd38ef5573487341e661005f559882050106d859df3c4c150894c101d4fb7d87fcdb89685362419f8050ee2a57216a18ea4396ad744dd19449859b24add8df65e6aad67f6594f0f222f87fb28b400e04d10c105bf3b88bc497a8d8c46f5ee470447992e2742f31473065190adb3a0a8866ac6a122778843c8ebccf32431d4db50881616026b101d8f5d443584a8c00daa1e22eb778ddbf764aee5eca11325fbaf9b2a47df8636fa12e943a10b21f11ee96d392b9e72a63ae090ddf896f7346df9f21fb745af3ba9f42254f529a9f8d3903b79d31576b957d27dcee21c1e1d75c5d7bbfb079b46bef4edaeb7bca769cb97ffb82d7add49a517a1aa4f49c59f86dcc99baeb0cbbd92ee7316e7f0e8a82bbededd3fc086a80e6278da14e45d500369ef384a61a2ad32d48e4667dbeeb68fe30b093207326c2524cd8826bbfa2694fdd5f05a0c6f3ec25d5d8564c7132435f4668f95bfccec21f306b33c153095bb58e6dd8dee3c18fe230837567d2db487e0062765cdd374d324bfb7b47c886cc109cba32fa69f14b33c37af477b4ae5a2aa7cc01446b1d00f1a2329c7dbb830312f63b4a8b4126aa85e35aa845bdf6255c01c6bd5e9552136cef4a2d2a19574ced87141a3d36cfb5b0e6fa4da59ff5c0ef75926c08ed12114bcc52d6a43f63b6f91ed60a64f6c43579bbb3258d9cc61056f10aa0222600ec649896e7999db1b921925137af762c09651ce5445189018df19ef663c52c4249c3b53aa0606544e355cfd5f4047adb25e4202fbeee9df70c84c91059038985a220948727ff9f2abd921047007c8080ccdbd1b7523c7924e0b40453f220b90823a13402ab95b3f3a2a0e2306e0401a0721d68e0012f12f8a819ca90048e8707ed4725009209bf7f063c6a191ff33c0e320c932eaff2af89162a054a2dd7fac8b3b4912bbb49cffcdad031186fe1a3388a257b3ff57fcf5f8a09790fe22d50e7b0fd8cd54feeb7490751feffddffc175d21cb69b2fd6b30df677bec884053feb5b350454f42ff97d97722037ad3fcb4a222ef8346fcfdc1b070892cfccd84f9e2e9737939f081305108328f3fba3fa47ada8fd182d63bc933fa99635244af88c47e9c16389950b6110d212f0dca871a2cd1adf415294ba35fec368e68e35b5ac681afc70638df5967716f0c63fe57060db741e3a9fcacff4b2adf7c637d0811ad494e066c228b2c1f12096ed517d0241ed48cc3cfe0df39425fdb9e61a4c1480a3c4f0cf5da7b37f8ab953a50f933cb90d92f5d9f169160ba1afeb21d9f89d2286aeb2f3b4f00c344dfb603106b53c26d71e8de9a0e3c21e88cd2ad8c74ec2731558f6e73fd04d07a8883f11c4f735d174495d84ab5d75ad88dde57961d8aa041e6b6446652fb8ce13d3b2b4a69a83822c40b27934dd53b7b7ad9f510289ed1ee0a4ed81341f5f282378d99c403887c45549e5ec681b4b8822ffa89d63cbcd0a7c56cf100c29e08aa177df943f1e1c5248356e5051d6cf4a2c465d30b0208d1ab43b0c9a7d91f5dd7e331e48ac77d2ce480ad21826a5948b37040fc25d9ec91eafd607e6ddc10d2c10210a9dc874c9eb688c6368b119232488b97e1ce93669347072eed96066f0e13d225b8060d303ae182918bda55ad55e9795a23bb693461f3d5b812fceb7b8679efa961695fd4face87f6862685697d3e8f7d83eee93bd6eb93fbe5b5d4982f7ef70922f84d5c9998c579dece02db6840f8f298c8ad747572e8435b7f5b2626308ab640a6944a820d8026e19847a7f6a19672a611103dfd219c9a5630c36218b85f2a84464da1a66c69df13b65b0a44420f45c832f0756b490388d83a96b40e3b5abc8fc5ff03a637a065a73d94c8c86c715e3afcaaee1fa265f52a2fa11d21bf621a7359e3cdfce88128afd57aa8c8c8e850574a10ddb4ff41135234f4b8170074e44081df69131278fe2ab3cfbcf8c4e0f0a45e22bf3da5705de7ac3031810dde011c77415522524cc8684a1b8527220d1cef917b639f326edf49f4adef6af425d76860d115b0dd2274ed1b323c53a5603bc74d123fa733192a8cae791dd01fac0feaa627a76d52b7b8735023b63f616af0b3fbbe04b562e20f60cca28a7c85896aa0c5f86181ec6e09fa082ac4c2f9487a5164a8b59a97dba99221309380131698be45c2f9707657ca954f698ae0c233027ae2f677512710bcaa146bcad17f592f274b5e3344b9c052e488c3d7b54b1d2966a16d3178a70c90cba80a7a9e5f9542eb9c19d0beb771e101e7d7df0b333d00ca00b19065c703152d06ae318b8b68689b669f27f0925d70000dc5ae1d4fc7d137eb2b80d3403646b0d52b8fb5cccb7413284e46a7a235f646561e903d5a0b388a59badf1d1218d53bc5171aefbb1a6158411fdbc90e2e022f0f1368bf7b9cfe46918788860b4e53a78310c2339af78111fa27b228b24d9f4a59f8f2f3014dc0290a43c259aa6f5c7c7c04ab0df65583f150095ee5c6f75e829869024d31be7d47e2a20484ff29a7b0249179eaf84ea59d845d04afd013d031b6a1820bc4d3e2cfc9e5dc15e92a98a72ebece95fa440dd804b5b4b5e8b3f76011cc39f17fa17e4f35dcd85dfc50df16fa6190b92483fee39d5a18d660e90bf28c03212b8d6402f01fd462d287f4a0668e93a8a6641aacf9caf20ccfc8acc00bf1e8b234b48d43576df7c8e31936559fbe7a2c39104cfe80d89265b72a79b0cf836d9dabdf7de33f329e481eddde7aa80733becae19107460bd78272f1c5af59500551a3bdcaa1b0f2ed507686f1bd8c804c85d1ed8e909e5ec2ff352421a15c420e6d7354b7f02b8c3a006ba0a6e69c4f9260dee9be0c8c0eac0027bf49779fc4e9d301639f64f01cc0358faf265d89d04119f3ec7e8ffef183f764df4692d3c08d20234ca94dc21dfac48af85bb59e9b83d6a5d56f836c937e00b2a1a21a6bb8475a6cf91e127fb540a9159430fb146b24fa426c28966d6aec4e29649f95f208b5a7185b535939a5900d2b9513ca96a26ca6c8768652b6ac205f880d45b24d8ddd3995ecb24c81dd1995ecb34c89d50985ac24bb27dca60ac835842c756429adb22a1c2a06d767c8d090d62798c0ba700bbd41033ddcbffbb9b1d2ddd77db8b772ec87bdb95b09ec937ddc59b9ecc75edc5a39f6c3bedcac1cf6cb3eeeac54f6610f6ead0cfb606f6e560afbad77b8b3526e56c4ad95dc60bccb8d95b86f25f7accc4de61d6e5ac91d2b736f056e8ad68b676b1c1b564d03fc51d5d8b1e372aba55def6fadbaeeaa235bb7b612d18a06e86aaf5e6abfdaf0479709b1852d369e57e56ad34b378a3e772ac43732cacc476041e9aa17b744066e9222a75e8d029a0b6cd3ab9b423f373548d5ab53855942015086a31fdefe1dd7d3e3c46879e15bb263cffdeaae6e7b7563ece5aa2aa9da758b70672840d7bdfa9987ddc98787abc19426a584c623bb52a7f50a3286a2236a7941babc30251c377ee8c5f97622235fbcc7cb5b9db20de40c208924ebd657e37a484261cc51e6ef435caa11fa8642a2366a98d2a8f44fdc9696c2d24080fc5304df47803acb6fd6cde1f1deaf625edc42dfb09279b3562150e68ddc9dc4e3758fddab5c87b2518529e66c1114e7fc8c9ee822f43830483436ceccc7a067bb03d4722cbba5db674b0a2f173ab86af90b05bcb670bd284a9e961c8b692848b5da17ce7ef241f2c2ea707d43beb1eaa491dc8b86d17dc4955ac4608ca8f9c2bdf7c1d4120b41feac7237be44d1afe940eab1f08c377e56afd464e7f641cf5f8fabb74a903daa04a41a4c4f841fb99a95607bb1c33be2df973739d6b009ac24fd62ce9ee010b6bb71c28f3909659d809cf5ccc17cd9a1a258c83d06ef6caa1b954d62f455d80fd4c2ea7f01a00f814e2e8c17c56bb0713931dd5a89cbfc9137641916e4c5c2635d574352584a3fdd6374cc580466c171b08cb29268e2e42134079c6317ccb8c2bc35b4bf1c46ed75ad03172c424c3584c96593be93888ae6459962bf430c0ca184c4373d4ec4547e9944338cef3831491b57ca0bc03bf8cefc599e9ca1864306e046d39d4d601921cabedde83649730cf1ec6fc60b729db31403d08b01fa05600d744988eb6649c0fcc1127f550a8c968075d47a6e5dbefe4007662c93e85934fb1e31497b40069fc267f13d810dba0198cef82ccacea131cdfdf5448eb44bfcfdc630a11172dddfe8c0d0a4a937b19f320feb2a9b4c54a73543acec86e4fecc7567f402cc2c8f0141163064416c470c06f48c290480e04a2b6e79abf0c93c17aa9d29d7e877da539d9886e20b28c856b4700079b1003d4c71f271e168c9c79509e4a444863ac2e300daf0072e80c7e7930c25d51074480c96351694c442572e99b515b8b43046a9158f0a5333f30604599a93451ad05ac031f3acf88e3db879c39da49c6ec68a22a864c8ec672415b10f0e8e2c426d5b2b49ca92725f1f88eac07968e0f490ca69509b4e003bf333883a91672a1c2029c5ac9321df5b2227704d2a01a6cdcf29e294b5a93a0c94d293da3bdb246787cad32d02f2b91e34dd69aa7e60702f2b32798f7240cf146f3be300886e739d5f724a921303c22d9306ad4d13fae5ad3a33ebc69b6ada9c5bfa17343881364e2c805e6a0f2830c9507ec46cc0b67d1952a6cc1d3e7888cafecce37680b1f7a583c4ee2f0722eb1f1bc0bc3ae6936b054de0d5091e79a2fdb986cdb56de2f8cd7287c87a6fefcd815716740d07f4bc66edc92584b9ea53ec5a8cc0a3cc09bff815d535e654e66aafc78e095e911995e43ffa804977e44f181a5499a29c7ace3d252a1fc47c8f58a9d1a034446c1b0f4d1c9ca2f2b15f47e20b75b58b2b602e0e28fd67e4b74cd60d6bbea728128dc2eb3be050a9377f8f2a3f1fe2527355a5bf2c76c96348d96465fb2e8ba3677b4ef115c6f66a28339dfc3f99f5726324e6c11413ba4d17b514195a1f5713049d12c8fe2a73cf9cfefaf3fb391528b2f0e1ea726dcabc3d0adae3de1c2d9b77d608c1bf9f7133b00208f71a162c97fb37c824f5627bfe847138b26444d345297f637b2a7ec3bcadaa181f0b69cccdded093d9bf446ac872c8e1bada757bc0bec1fbf1121d0b5e0265cbca501bfcf8999bda5395e64b981b043aad87f75bb33485bbf4ac291161da3cdbea5f868da2f16b66e9e13cebca39ca0a4597bd0b975b9e389d6f880f92ff04d627c3297e61b055bd18f5d7df3cdbc709880bb56465d5b7d2bad82d7bd8af5a9f40f2ef7372df6c6782c40e4a099193c21cfb7ae4221667167f27f9304f3a2bbf9bbb561b687b7ee9d918200168b9fd37615e06bf1fe342936212854ed0e739d940c9f428d760c102b2a8e63590a4ea557ad1a31828daaa8f34b3fbccedf9b3170c67091fe2b48b32933335468d1aaccf9ed0cdad9fc886db1baacf95df1f73b160a4272ff2f19645f7bedf1d9872828dd858816aa79a712290d07903f90ef0e3c7e0fa9e47f267e5b49e9751797d35a8f7a09ef850408835a7d5a03a849a3e2f1ec5f73d0e05d83c93dae3efa7e7aa1a71a5b3c09d06c55a9bc6d4492ba5fe3cd1193921b9e2b2dd132a01d6b4cf63c4a115829f0bf398253ba57ac85f2010442e6563f114868fa661d7921b063773e511213ce062bcdb8960702179a5296479cc155da0dc1684c4c71d2a23cc4daec7f25054daa78c175ac75c0a0a3da66fbdb244f1d175e85dc42ad7ef376865582f04e4f25c3b0b319d2e3ec5c47313b8824cebc8351b19aeb5268dc9bb3c277bd2e61a09981f3cbe46de4a1d4e1148b3711912c480015ed6a77f5da7b5a614833ff70258521610209fe8fa06203449909e00965fa6b11be497ac33bf238e4c4433e4e70b67d8e265129094a058843381505f7e20f766ff2bded63252a42edc633af74788ac10045dafc8c28ce0379ffde30f1326d36e5fd751e6bdb537a50375385e876c43f7e9b30e2983f0132d1d5230cb4fe71c5266c75fa939a49355247ce750e1f99f45bcb7a09065a9468f45b7a542c9e5487b8596218f997c1b56f326331bbf7d24880bb478c1f328a54f8b04da217e7b8a5bdd9f8eca856bf39b0b9bdc34c4ea161bf3ba4a3416482173fd96493d9a98c7228b1e4261a62172c3cce0cdc6564d1e484537db92edebb47e0fdb20ef1ccfa777be405d416dcde9c40a4a31b2933680ab4e9a9b2d1be3a899cd0ffea25b92127032a1e9d50cef46493cbfe2440562451b901037f109099e6d88b4261b96a4b3ad73d4cab2892cd111b6ae4777e3e660862ebea5663b24511876ebe80a298a5ccba853f493933bd3bb59a46fb773513b8b97f182bcd4e00e81bbbe2e12af97cec5224756523d42c85b4d9f746d06f02cd6c7fee79c9f7435d32d41a5c222ef43351cb24488c0b7dec0d149e9767162a990df68e289efcfd75e6b680224b8f453bc19c2535516a80bff4a661010a355dcd094320b868d40aa722c7f5ad3d19efbcffe129eb6b507757e0c1fa83aad407ac2c33d5e82670788f4eb0a8df96deea54dac7a9fef8389f0af103de3ed3cdbb4c3d38a6430b534bc5272afe4ff8f79a2faa0e14763883dbce65ad2fdad6a8b12d759ea7b2a009a485e59500b0e120b0ab1b6a9e980d175cdc33e30188595b21cb01aa6c78859e102d98006b7433c1fe95a75d5e03af58f6f87912dfc9f4bae59bff36736481fabedae077f2f43705fe801040037b823caf537db8bc628b1512f46583282c92b6dccd8bc8c2849c0e96fbb80a616e8abc5c397dee0794c29f3178155d26eb61c5925fb4ebee46a8acb4a0bd410e9a91b50f2c2706ad72cf9351906194716e9567bc81541423aa829e3b225cca3ee8b71292a42612d601dc5a7dc17ba349081bff1e6df820ce12c1c0eedbefd79c05a67a5e6aa990d8ef4c70adf1933939cb622c12cf34a4773cf783a8616fdb8f90e19c190d3a6f2b0f3d2d79862905b4e4d1369f624fb1fb38f60dc387c1a56049b93dcadf2244d784fe63ec1051f0e45ed571b7812ddac0aac50fb65f501ee5663aff452cec4f01dbf9f9d2e2932aefe73d49cd4f754836e4a7e91f60a00f20405ff0b10eb8b6524e689d6bedbd5aff0bac21e02b1e848c8ff96430ace9f76ac6604bf0a56fa0cf8638e6288d3727e6e13c58cae0fbc1ab1ce330941042c0aa87860b9d00a3f405f9e52634477e207b37d11410d4d18ebe301b735abea59fdbd7ae04fc3b8bd2aa94f72dc71a0656e03a417503f0853284752049b32143d8d8fef789b0a24cfe106f0e40ea818d4815ad0a51b104a16e599dd9124f81d570470075fa79c19486cdd640a5a30cfba60b8b2ccffdc41e5860d0474006dbb9ba35f48f05db0dda5a9ed4290e294c16c1897f4962f11a69e9c80e169c7c7fe5e192a01a180a3b416527c130011b0b2c7859ebceaa2d4bc8e6181ccbc25370b63085f52703eb0502c6061568d300ff8b4c70e46fed9fa1c54c80c677105ccd83c8ac565fd1e74af3ee965cb9e53a4016f9a4e8fd4c7a54353dcda63f713af149e256e3dfaaad469c63a0a58795f44ee54b234a00abc96948d1246ac7a2f697e473951cd830408b280a507ba8fabc0fc40b6b946905307d0e3018712e2129e28611d971a74357e0fb0490a084629bf4268b488df9edbd2b352ea2c07cb52e253c62d55a9650caa6bbf9b8b6b4b653468616092839b8f5e086be06b1c8b6dd0846d44166e5e55e9e0180da7db9d94c855272ad238c4597d7389e990c03275e610362477466c3ecf401b047336f61e7b02d11207f4e920c9a55e7f616e8a474a51389bf9f8d781ae03f5d3811d67c733e538b0afa982a03745cb685fd6fb2cdf4a9f48ee9878858701e0bb2ee3edf66038e854d44abc1d44992e900ac6dbc67a239b2904342644248e760d3c6cbcf7954e8f0d25d6f869deb2a611d7b8b3843fa630dadb1c184862f0881b0d0b7b3d469551f4127c233de961ff17e0608c3209980879e5a09883f06e35811c747ebf00b67e0c32dec7b65df8171bce5e52f619cc1d373bbd3774e4013fcda377fe15607515f8ac9544f81f7a52ad25eda0c726855298e626efa6bf42aed25c2a0c7668bdc082de1c2a325a6423a0ebf5ce693754581c1e58ed48ba116d0b68c63a6410b39eadd0b1f907c5cd461edca5f41812214f3100c0bb6527c149b7f03b147008b02569409e3c3c184efbbf8c3b1d0c5c14c5e250fcac72b3ba33ca48f957e5ce45fdc2a92b4864e1b3dccdd0d57c762e83992ea5ef88275b1b4c46882d0e3226a640da132cd51da004a0667607638774a64654829cc64ea2f1c8ebac6f7de0ad1dd9402cdd83883ca69d65d596f9710344dcbf3f7d964723c05ca37512884e98bd16fba0e7c20881b09b341894f9077603cd7393d387c851fa56aa0e42e49e3c7acad967f270c7d613eda55cdf6d6bfbe24e510fab2318540c1f157f38ecf22833b13ab9bb1278136e080639caa7ac5ee7d3d503cf23bf4ebd3b2be17b1f696540619b23df8026fc2a315806b178c5d1bebec2e162dba4a8c9f9125dc0269d1b5b6abcc3e902f82d6c91fca498f8bfc82392eed16f54c6ee1560079a27f9a179138e8b857a1dd8d4f875a43980a45a58983023c9d8abb807b168742472e54e03039903f96546f1bb710ab6e4c24955212ecd5ded05522eef1ad8fe84972f8a738a878598808ab6760218fa3772c0c69e20288971ede9e562050de3b09026d59b6cf77fd64ce687bdc396124b63836bc9dcb3c542fc5934b80c90b954a71c47dea7e55decb096f0db658e67d51f559ae7bb1b48540485b8161ceb28dfe403e85bf5888b4636f60138aceabd6a5ee77acd3f8462675d87c3bdf9ce23405cbec618e113a79dc4d678241452c35d52ef239ccfa6767bffdb12739ce1f90ae366fbb0fe201ed6c36cb610c69cda8edeb739f4f6d6a67b406016c93bb0830df922c242d46ffdd5a253677f608829781e5745756770aecfee3388f3e8a9c2843f5e9618a1f31c0cbdb1f4b2539dd0ac53d8277052fadf24b0f41a9cb9b1341639c779dd00e6a33472bc011ca134d97b7f1d406ff80a2733474f0313776b2de779ea78ee280a5b522586485004d6f9d659ab1e0e2c68f9153ed2cad76d276d46a6bed149e49553db8b7f628ac09b8fcc2c92eb580aad8ca00904e260218cc4095ed28e4ca56934d69d6ba0851938e723b36cada03b0b45432e0519e53195fd7a0a7a0ba7481e172b5efd3e9a60cce4050699d55b44fa3d6eb608c64cd1ad8cdaf493c12ecac98b21e446c56784b74b97128c62f8eb80192f8f6d52f8a2481521e63a9c2c298778ff5702dc41775b40237cb86bf444271e68bc60dbb1ead2cb2a5fb00253b8de5dd94b6a22ce89866c1a1c857a79abe9d0a281422d70587afd65bda63e08f7575d16036ea27179679013d462abadd6beb848cd9d6a288d01a38d759234500dae295df1f026c10156b99c90053ef08bc557a440d5d513e5271489b1376259284b40659e40318ee0ff0394dfdad931c5f6e91f9a1b7abb698fe23f1233d40eb911f238451adecc2575c547b097d5a7232d7380bf2cf52d10dfda80dd9b46a6e4982422f14612eced42c1fe686020e62c9ca1e80b1c38cd190b78c29aca0a66476ef110f13f4b6e463e762640b3e78f431d0d4aa27798281e115a93c8455468f6cc6a096bbaa22b337c67a1749360feaae045ce0b2cbb73c3963048a0c6270431aa3c72c74dff9db5658b8637abe7792c7d13c4d87be70ad26305b4701f0b32d87ad2972b1d69b8fa92599499f0e5a96825bb4da13f1c07008edb44c0d5b1569025eb6d9f59cfb590c197468727b3c105c09bfdb5f1dde6931ed75d140919a2b6fe7da23d4a57cfa3cc421d501d3a3ff64fda043e4662217f55da35fc427db60c3bb03b53938e251268cdbdb6882dfaa828c078c80762673dd216516941543b0dd4f1eca90b3856bf59d563cde9d32a00d1876fb88bbfe13b362774ebb263482f5633a272653f37d8672aa8f17e6b277780adeab2ceb4ff2fa8d0f5b4e7dc15e82f0c090df320de3ca6e8f200943765078846638027271437dcc5195704e87ae611943dd1b50e0edb4bca4ac071ab6b714631c289a06314f5a09d6ec8cc4e0a9706a2004a545e941367e6b8ed22dc9d856f0ac8230c98d8efeb18371f5dbd1aad849fa0b6e147d6f0b4ccb2420ee0d5906fd86639c2c8d222b3a3fec758d50d4ff2b8b331b04b74ce58c9de6c2bfa33d2da94dd573fd7ac03ecde02f69a3dad15281541e5f5dc4eadf998091c13cb0a2502b189b921d7c26adeb3239f3b634754fd5bb15116a6973d6ce6ef26f68000b7c3ad2183ddcbaf5c38493e1168376834efa2743619e14042771746f4ac3dc002cb74dcb1b111227793de00c30fb49b0a551395fb2dba3a7923efb9c71894e6920f5bef8644dc4cce2d7b1c114bade4eb50f842097b9d156933a5fc66790a4f0139e907010748e251dabc7b7d4a9e49eb5eaeeb660bad27bd74da1b4709e1cec5988d9d1fc3a044fe0a3476118e9c223df7a380046b1756321de87f0b7faa4e0e60c7adb6fe101bf96ac431663cc85d2947f0961e6d342f1a262478aa46f2100449c7a1989e0a1d1ca26ae7fb24e46ca738d5dd3e6ae053871a5c46d4ce1e479db55d2cc8f598bbbc97676656620d3a88baa9c4510f7bff2a457b28b0d4bc1ee15100ca39c1622ba51a4cd0d3b33872a013f8d6489418202fb442db528b4220d80dae64759deb39794c9d9567f1da9291230a7a3e23701b0df40b9120f3c3ce54f14c0c4b77c688d2c7628507afd9923590e1058d6e84e0219ade155238c826885146151be243826e808dee776e6a793984000e763697a4c5fdfff0705598d7ed933c970856720c4a9f790316c7eba011d01349c1cb26ac4f8bbcf8f1ab52b4bc1007f156cdb51d1db5b0917ff7818e09d60c92225290e702d97f25b0923778dac8e008e88252d888ece76927b1e9bb94e23828879b6e2d43e7a29a215aee68e5605e19ba18ac3e636b8b9ab81d3432944979955923e440a32d4ce42dc54427f00350d6546639481618ece98cc8bab02593d9411b76a0509efd1d26da011db98500e3d1f3c7b71c8d8378ce60eb2d3114cf5e00df437dae6aa16465dbdb1e71d6491a9f3fb57559e72f806da93ba715498a96fe385782031cb8684276ce528ffe5a34fbed7af93bd1c4a3711e7394532645b2162694700c764a1e526885618b34f3903d499c0393d486bcdbea11424db71f1c17d2eb0f7347f8190c44f260108ab33d62b80e510b0bfe1bc8f3aaf9dcd2a063b6a7263098e9d7528e3a3913685efdfcf2d1220315804fa63bef2d3f8fec3e48820124959248bb8edf8e8a245e13bb269d249774d4679f93a2d0579704ec47e6c58e8022e38cf81aa4f2b095a0168a26150ed1baa733c82e64086bc71fec74aa8ae122565f0e7b0a62bf43d59792fd30cf7ec67dcd5e714f7d0dcdbefff1e5dfef16d607d06bb73a2fb852fd826c8e3cd9b24df2c7d895247d38cd7917715c07e1b3f4be00b6645710af3220c875d1c8879498cf9dcaca69732b869e99090a226fc47fbbdf96a57d39bd922a6ebe209d354ecd13fb5b5dc2da16e7e4a8ae17a0cac236cbccd871bac5cb88e747be1b5af6abcc64eae63c735393b8ecc4edc73939ed4283f18fe25d4df36d9ce953efca89ee84e64f9f240a15a8dd4201bc423ebaf45182d72a131d23c7264e85d49aeeb5978ca568060bcbb0106c7d034629d6c1b7e4e4aa200a0510a43a1bd512d9ee3a9da25ad35c448ae63ca6e73e8a31b2f3806c92193e495c0c9118e748e0704c9985fa66dc8910aac5526c659b7ae97b61f83436390379841903e46880a155eb99f0f2eaf883d868aafd69f267c78497e1f88b78fd57930e02568e8280d33c3985a2c08fe7642cf2f15b5860d25606e8aea47ac7e193975736a4d1a738bc1d19172b5071f341a50b989dfc7a52dcf1b65f415c479471c80d9cc1dc02c6c839e48169753a9f200fc7c2150ea5c184275d978c4d37029aeb55482ba427853646ad427668eefcfb34ad2d0c6664f20a1017a2d747664ff2b96742944eee655ed3e6f62ca03db0959db14934ba6cf1a2b459dcc28aba00663cf11617999f00ec13dcdc14d784fc2d745f919e721f11a0d8e2e37a5f4a7f6a845c3115c514df815bb8f804b059007261d24abb41b0f9a775a7e8953631ccf02b0b111e98039dff592647f1255b9d9948ee1864a94001a4e4ca146bc21450b99b9a491959e3a370606976406ac3940080205744440d3ae7339294fcdcda966b9deeeac1ac2588967842662f663975213d019780cb69d51903013de9eb02f55ea901786c648cc86e46fbeab2b0d5edaa64ab2c654ee4e70159e7d2ff1275103ea3ad3bf284eed14cfa73be5b8496bf75a1ab6198d0b63dd981d17c4d616f4d4789d9aa083bd5c9d61cdc0f143307cca787e73224dde069005d3d120ee7cded38ec3069d9cea01e60914c9bc81831631bd1fe59841d13a6e6f273d7f56681e48fa48576f56cee314766e2cec1523cae8a2a6317a36835df7b82c304c46720a49c5288ecf87e4d94939b2dafe3916b59e28831b8852dfaf9eb9b8deef17bace1ed823eb6037cae7319a4d7ce99918de70890f4fefcfc2774046dfe9008637b5620150086368c4329bc20ed2836be30f2b1acc43cd5b19d0b4a36ab0113b312897f87e09a3d18bda557f43883e8b3c555859757853e2be69b2c9b4ee316e70dd8cbb2b115559aac95039f58ab390cc6689bc8019abceeff2b05a4d388a635f5f60fd9b82636a18059a9cec9eb01419f0c7c19054b8df1904cf009462583d3c38c253b5a329a9293297c57ef0e0803b1d3d67d5065a8ee3c0a91d620763a328641918b52eb1483b6d1f5f70dccefdb821f9806eb946b607391b97efdf66377ffbee9c80b76cf2fced498c9f35b798aacfd18fff3a2a8b09842dbf35a5a16e01bf30a937b9be3e78279fc65b29997ef4aec2d87b31b24f6add28db69fd1145f997434231df521291a8bbb7e90dab2252f92612abd1bc3ac88eadcd7a5d3d3b185d0167f34821401637f843af9d765e262e25560e392007cc41ec0618a0edb1b70110be047697a906741d3be5f3ff76c456d48cfb63780a0dd709d57381f5a39c3a351c1c4e24428708071a64a3b38a6ce3dff6d4e2c2980171d12a376115cb623f45edd5cb5833b7d03c3b1e1669341cc497d4c0f8f30cfb328df587d94a30ee19900ff707244e43920473485302244f8810c66a0b8a4cee8cf5337306df3a1f4962be8cb0f3524736897751d17679e12d3780f73d806b41a8661951f8ab561dc3402aecc28f279cfe6232239abe20c4894a21909d8e4d01811aa25031b28e55ce8bf30a0d9cb81a3451328d6d63858f4beb6b0fcda0ae0e05786ef95855b244134ad87f33bb07ec0f3b3c8278fd8fcb6c0da469ea8782fb9b79ce928f0c0a4fa025752289299f0004c0072d0a8c74056e479e6ecc379a18d5b3b6f3d59488ab26a8e77781faa0c868829b3d710b88cf2d4bbdd0b37950a811c69e12e686b9b26e05330362f8c496306f0727cb71108e06a87c416a5f2c6f30c3ce80b1032a95391de69b748e2083889f38cfb5eda1ae499393323e60e04d7fb19cc75629629ce4dd2add8142ba16f7ed1721c251122e9275102393552b4235659a1f69733bc1048c9a63a819d0aeec14a8c6b2bba0aaf39a138a90efcbce2248288c08502f4e875ba97c91ec148e8e5339064673b27a055175f7c6cc0169a271559d97642b22dd31f9c1c36fbc070ea3251814980245782117aa37189b239d3f9207a16e674ef20736db283fe737db087531035026d5f8b838cf5584a0fd0661430f8016258177c86606248df2e76a8dc81f5b51faba1a4e91fbc125004c6592033e1472c4301f659bd6de84048e9cf615e47e8cd42dac8cfa732ba4e3ae64941907014bd78948b6abff77535bbe6873c2c5a5b538ee136ca4aba09d2544a7ff7def6dd407fcd1072fdbe86a00ac0584d22b6b8a6f056aa60f33684711b7b99db78b8516ca8f25f1d36e0fdd8063a4b8aab8a0a0d4dbc64904daa39ba54bc79abf0053bd4b5776122f4bd44c53fd5b65489d24e1fa387450e669b5a0be05fa76d097683f61c03e1f3b6a0f384a958c9f49f50b3d76d8314776890e05c53085a6d331baf41488def63288896e83113d89f301dfd029d5f9d0e955d070557e69e0e5eb6aec7964dc983c300330b61b1654952207fa041c5360dafeff89f1c1d1182734a1a41d00d4a049c6f153c7886ffa417f1f43b30e7bfebe773def3a2b172479bb1d916d0401e103880a6cf0a26cc38595ca16a9b037bdc995137bc5895f3903dc5e9c31dab64aecf721dd195f5aace2ea8a8624384895a0a06781f674341a6ca6ff0f36703e67aa243504d14c074321e23e7dce90270c0ebf337657071c9334a7f3c0e2d713ee46ca5e59c61917ffe1bb898565a6487088e89302ebd3199520d0096bff7ab23a508252ceaeaf20ef1b4bbadea69eb72b8161aa86b0d3c86c146463cee3a5b0c79f720fc35cccd90130b12d49bda83a3bad8758c4b01514db8108f9322c66cd74e14e0e2fdb7c08af65a6956fa6e40e949114aa35c7ac768e250626dd1717314f0e14a682218f87b97a1220c8b5212b9ad0559ba8773c1989f66b7933ab9f55086d61ce96ec0ce8422e18de764a0902e06a0a7f59f11fb613bd4535fd4460fae71f5651bdd637e84359d0663f3025dfd7f74753cc04946937a11848fe5f057d04a23c12b50b5d99ac0984f5e2399b3f9c9c10907e1795ee64899a8d4337f721cc61957a7d5c48796019026735ea2e1d760a498e20b09d775f90a9c41051f482d0fbdee50c63f4209f14452cd8e1b59ac6f0c8b1aeae1f8ed2001994f2073eb7c126c7419677447a924f38a2375fce4cff1b8f823be0d78e94f37c6c57c5cda87e0f71e9bb991760d0ee4fb23fe1172a3ac3760952da83b448e2dbdcc857fea6d785032da3e1a50634bded279803a2229cd2c8af4ef33c3e5739994298b2e99e4e5066b4349f9d8ff757ab328acd842a4dc9529cd896df54c2d22e97fc67a9e5fb1d880725173e8be4048cb8f5cf593d761ef5ed8fb9c742bd9a0ff0de84942643212f933dcd979df97df55ae4ff9cf1b3bcf75c9363097632900219749d80546c35e1ebf5c00b7ccd000a1e1647af94ef05c185d896b916e2dfc1b3dfd0e8049410cea9f8ba145b9aea37ec1cc45b36fb88713c75301a5df2c5c53df0a1cf1a09b8bb8fe5557e90f86d98390bec6598d46ac5e3a19ced0c8ae3fa11252d9cb90fadbad4fc50e792eccb9c1896b9eb40e9f3c1c5485fea475ca6696503802cf43a15e1a629877faf6c7e97feca563364c8ebf830fea6bb6a7904247492231e965d8582f1bd3127540cb3d0548de6026a85a282bcc9de5802991d78a1b3b026a74432640d2bf08820546239dddf4689c6fb9b576f4c17eef015c4b9faa1de7becaade87389e102f4c1b38fedfbf81d56cb73ca51cea15ac37835b17755cf43096b25fc04cca3a1bb56019e84112dfdd5afd45163351012f54b94f139400666b5b02e517636576cfe34124bcb6922b5809ad582e6fba8e1bdc85e8c8e1eb946f3387370118c1f253bf7437a4a9262f2f7c830fcdd6549b82d78f1f96c1108d7882cfb872ea19c0d5969d898df1e3b4bc3d9990e28d522e5107ff001766bb221e0f634ab5a1b02a410835df1645e6375e0c87a69a846b88884e28c21dec3f1c7088719b6f5bea0ab5db126c5f00ab0701a4e642bf2452f465d6cf926ba34294c28f3ea29aa3006a5685c6ae531b602d7305b8b0325bb12b25a2748e080cdc78a71713a314badeeef6021f4b6f0875fb91a54cb3d054504c2314e5dc425a4249c19ce6000afb840046d885bb28f5a1af80c661647509bd3aaa3c5effc09b1f6fbf7ffce16dc1a52867d90497a84749c3f9d608fc676f309487f3eea7e5a332d7b631a1544f2c1fcb436850f380b127822ee72a7c40fd199ea682a654253f869317d97636f111f35f17c3517cec6874b1952dc168db5764e8b59032860a4b22b74d264d3192099549b70d8c5984aa695dfac6bc77ccb6e5a0e47d5dc6971be592b918ad254f60292693d379527892bd9dc5c85ba797c2f03503524d82e83799b016b6320f81689cf4e34103299e4d7dc1612ed3f885857b14b4eb5c9a33eb1bcffe5038ae245c115480d343e9b602cff2daafb6a2a1bab580d281daa9006a1ca59ea02e87434456b0f1f5588ca4e4f64dfbe1f12aa884f831601ee0d236d9ffb17694a27a43840e4e3ca97b22e34b414719a48ac08798ce763a900961d31c30f6d8694d47a0393f81ef0065f7e591455da8815b4c0835bf56d9214750f718e3aa92b7fa0a59b6a0e2afdfa168fb55a1893537bcc6089b66d0f5d9981ae73b1a775108a8edb40ecd9b9d54c3f8a7a3b1a3cfc2a9ab4765a771bf0955648fa66b926f050e5f80600d1728644147b418216cc3e9fe687911969e63f2b85e7ab44527118fb62507d0652bf754f82eb5bd47a9e572f035ebd1f01cefafd812d704de7206dfece6d44eae2a3aeddbe7b5950521e409a42e7ef1f96ea910515c8ba1cc88976ac816c191722c03b53964318c2bcb3a61129a720b16a10907a94811840aedcb513eb8d6a233cf99293e5d5eabad7aa1226f10e661ec997aca55e2eadddab4da61b7b12d1da513415fa2dfb4e603da1024d7dece08b13527a1bcbd79679a945035dd942d68d2ea019f54de43a073bac043a3b8662c0209c285f6575f684435e30c38a44fc5b2e0bb4ed32a3127d33808053e63379772c4ff0e4ead9d3b404da6ef4dac257aebd471968453d8186fa4139e3c57d1ea83dfe3388d23c41515b76f7a2636d5bf8799df0e98abd14d6b32eb3766df734aa127ca66cf77f75bd7122ac6cce3856d0c63f59122e53a056cb72466e296b5649b036969f460821b624cfddca0a6085ebe093ae71368b0eec6e06b6d405ab18c45297708f052ff5b22d7f713a0ee5cffe684e1e40004729d6567fa362bb41c75f7211a877c5ec091920cb62417a61990b16a811d2067a72f113d7b930db5fcf766f05e3c1f91a73bdf1b8da9cc16b640cd13ed2c11a742ae58c903a8c95ac3ec65235866c41ad5247d9fc97c7d200486eeef58c014160c06c5d6df4bf676e284b54f420afb1c2f6584ec8bdfaedd22358f2e0d7e8331974de6ab4e79e79105de81ecae3e261ef8c70293b4497cd478a9ccdff711633c2fd336d6943e967e7761565cad7b0a28b3f42c564a758d0dccc4620673edc633ba731434bd760015efc41defed8c00830929cdd6fce7fe24c29a80976641f60bab8fc9f7df4e201f6c5244a227ba234e31f452590b9bf017b670c5c17d889073c688ccf7ad265e57a118f22a0d8720888638e14fb57308fcbb64fe232ce1aa0d4e15ba18e1c1fa1f78f491dfb440f2875da3dc5b8b637584b2444eeadb26f508950c46995f0bda377e929393930897f046593bb559a535825652aa97d030aaa20d187d7e43403856e3013847a5cec0912bc02edf04a38ac7d0d4ef70a1d1630e55fc2415129fade4c47e10941001bbf962ac21b1e39eb9a68c2e75a396e267b15ca042b37bd9634c8292d0020b17e8470e6f67a134694d8859c97963f1b2508f8d015a15bd1d870a903382ed8c6730ab4c020e25c3c284754ce08d770067b824ce19c827085bf821d25152272074c226981b2a8813e751cd013829b9c9224bd11b113e3e82506946765e02798b99a352c2d9803cee05df40b4f012f400624952075849dad402ee6235a039c10a86f72e173bc83306656d8265230c85b3e5002c126ed3691d64ae680b9b1ac763dd46cb7fbed39bb83f5995526ceec53735a39936e4104687326f485f96286109215d7bcd71c9d6f30bfc7e6a609fb059d87ea32b8c0a166608c46bea7101f95e10a9647ec18414410979fb3296741ed994b131d0596ca54573f21c774449f425cc72a938f8d8c9460491b55e9ac892cc47c07b8e23f50181e77377774c441fb5e85359a107cebb372ce23d10b1f46316f3697958370b3c2faff2b9f1bb10ca115648bbde778fd992994077961e1e66e8a5497b1044a9579de34cc90c1bade9a03e5a9bfc0779aca5a8028a0d53ce5cf4f5e253b6c3ade4c3f4ecb0bd2b5660fab3b115eea0a3e06b564ddb6d37cb503f00f7259182222f6cfd10de8c38054571d232d863789cfa60f29348c3c9cab2121f43c22b810db877560df0b6c751ae800ed1990d327196dc7435286ff7d3f8d2bdf75c259e8a75dd4a27137ae0aaed7eb9ab74fdf9c58386a4935ab85476b5c6d22da326559c0ab621d0d18abc4c3c4eaa9ce011ef223db6f9ffbbfe8a1ed2b12dbae92f53802a4cbaf1c72144ddaefde6af8c22d93f0ea3609fda6da5de04d2660b49cb698f57efa83ff2992a79de76d6298d555bb7957fefd13257fa18b0677e7153df1d444ec57b8bd8b3d1f4d40938615275306ede222b5520e41a819cd4ae323662c1649a5ffca0aa0e1374404a14b66977d962ed315f805959bedc39b61a697514281345624d4c8333c1571fb39f73caa045a58f2378d5c754cca03b9ed5c189132ca0b4d33abb3a9d70b6db38c2febb51d60dee8881222d040c6a957944da8195b12076e2c20c47f68bdb1901734f116ea07306c87f5f5ac0f99b9e895af67a9054033a79f043903a523ef8f45297c498ca8f952d087ecf92cf7d61bf921c5da16334b6a0b6844760453cf7db18aeff98259c4688dd31b6e56cee99398a33dc4c32616771088cea63da383129a5fe9eb72683f4d40c887128ff64e01c07c58475527aa018a2dc90fdf2fb5ad91047f4b38a79936cd54f991438e7c142e24777c017f8f151a5888d5762d9d3218acbf3df206d310ff37dea37a077395f1b7692b3c250e97ae1c43d462c40f03f98e8beb1e910c717cdce032ac3eeb4f529afcd68f44044deb9a32c1f7c5ba2e52e2c974dd268742a89cdc28e47060f5bbfbb0262df3420ec1324815ab18d82373d669e5ab697a6e5e72a4cdc052134deaca25a5bc4cb69ede3a5912354f0b411b461e105f229d72600c6183c506e6b4345f5c0534a996ea76bad10928c6de65a837230c669766e6bbbcf1ac50ba62e7cd2a548f23a37e4ba3a8a76fde009c6c5cd95b2db8969a6d16b00314c9ee6c20aa9b41021b3b56c8c90b4416bd69c2665f4c8cd96ad971a2df29f9eccf943933b0e8d3303cc3c9204d8c9a74986e7097ba0b6d8e9cbdc5614090c4cbfe08407fd8358bca723b8607f835d8fdcfff0eb5426e838e1f4b52971d56f07d9d9ae9798e433956def8b20248927377743f00d5d22d89b1c5581da43f1b5239b5145e0009afecfe5157fe03384f9e72f14b3a23efed4a63e167ea33780263c044d896c4bd22af94bb32259879290e6ceb1803357c3b90a05c2f2f5dbbeafe1f384e0f296d9336b172e48ab8c0708a262c27ed7394549d45599d93c1fd3b28d1be0770800d2beda2ece65cfa23343287568f9f4f70ddf7a70a67b253ea5c0d7a6b49bb7fca049b4bdfc2931171d02c5306f69be225c263530e5480594f0c5ecede13b5549641a3795541a522718bd2da12087e732e08ca3fd433e2a1cc02d49613951552b432ef6e718988ff1c90e8a5ec817d24e98105a229c69d7ce6394c276a341973dc961a5140d48ee335f58c663939fe179eb5d791403345a2ae56608618a32ebdafeba462a4dea0523d090acff8b7a8e9eec299245e2ebaf5b0d242e5ccbc02ebe6b22a34ce2cd4509f7eda46761fe7776f725394784b132efbd587aad9ec13868c2bfcc1d9450c25cdfc822c086c1a09355bc4ac565e01c0b2df4a822951fcb2d55cef43363a98dcd200153c2a16460d04f78b91a7d29f38cf9dae2ff369b80cd5517e425939df4ea3bf42f738d1600b7aa033d00125be557d8c6fd76dec5dbe0e5bbda597222087799fbb5df0e0f0b9e27c059dd27180f9c4104d68b4d90a8e862116fbfa45fd2cdfddc2ae09da86f51a041cbb6d6b42265976065f8c23402472fd923762d18190215d6b670d21317af635a6451239f19d24926874fbe2cc41d7b6c323d99af45a961f63a2438bbf5df05d21909f92459bec35fcf791e22f0460309d835597e2ba48bd5305020fb93fe5368164a8aeebe3099d83d9d03be6388a216df0cab8b222340ec513bf7cc1a27b868524c08f680e0e450665799d6c7684046d7bd5b8902e5f98ae321464a4a697401cc22a29f303d6e97b31974215a02dcc8c643e3313c90090e856d2961c25eb64bfd8f1f0006f1b3d693cb899a162520c60f8abbdd63bf2b21a834939cbbf027a75afd860e8de1c2592a5f453fc3a5cac3573c672532249cabc257da507f0a80cc2b24594d249873cb6a6cd9eabd76384e5b1c82105c1286e0992b0bec8336439ca02dc1b6d051c4de526c8e48f918d2efb9d56c9d9cb4ce7d4dd0d1b7cdee8c068118fbec889be8055a9057720c36520af453ca49a318eb08cb150817fa5a1d4298944f31d974471da6e7ca13bb37abbb3a08dc8994385306f781f1cb01b10ca5dc5cea2b343232cf970a4ee99aaa3fcb5643736bc8a3b34290872f31dc473a9c9eac31c87af20d6e71b2142ca577366ecbfc39fea6d450d971cd25998857cba9e59ae034d49b2f4213259976ddbb89b69a3164524fec3ff3f58905048cffc8f3f1527518d2add127c74f82d6c6e1907231bbec662ff8436ff50278472e164cd7d48251a6be05db95fdcda9a1d7321f46cd8db549e22d830ba7e24f3a4de74791597f68b38d8d4980a482c9827d009dbed5c95c67f0e7107e30a213739c5108a9e5cdf3c3089937bd418f4c3d67c482a0712c8d2332724d44a964b081012d3486e669354a0556992c3b842aed9c90113b0201ee1021ec6216cc08038800d581843c383766260166e800bbb19e295e5558c43d461491aa121f8c2c9667af3f51d5399b6efae32ae0556d9e4160cd6459f92c2ac60a3657332ebe082aefead63014bd95757c3d724a81bc00f66fe6fe51f9cebb59f3b6338a3aa091106be54e74af27d59944478e54898556725187a80e05a292d30c82447b9bebea127290c3c3777e168c9b20b95ed79f3451ea7024dff5723c3ba33ed20e29042ced342452a659b006e7a808931cf034be30981874bb615b5df28e1dc812a093a5337953a5d0886a5d27c6abb86d70498b0b42c1c187a371d32ae646481a2cf0ad4f3d358828b4e96d14457c9196f31d6c02a3535ce87d75565494c68a0517741269328c1b27ec402d475a6d15d9d80d4ceb84367c25b5492def889f733647c3ec92ee6fb6d68b1ca752f1bfa17b353b4be383bb43687a775d08a70d6af9a79e95b5a404466a3b88dca60fd9af1b04e2796ab68517f186748a01745c66c23db4ac9aef592e23507995d09644a12a53613278d081e10d71d181e3285f8143dc366a00a77c8cd2a3155593e16be0e745d9074a5c59ed7308e81f02b048806d2c210c350f7406f4dea75e11586e8ff19e2a47795d73c6c3bca60f7b9ae4c3db6a792bf1a2544bf734488d3bc444723459570d4d3b9dd5422fbdcefc7a2c421eecf28734f3196f41b89095ddf73a75c448aa392493c35af64c5d20228dbb45ce35bca17781c7c8620873e6a2cb212405e2f756905691c20018c44169b037cb6d1cc1ebe191cb528a3543874e096d7f2f5d6c7aea05cea0a405d3c687119008219baa6534a227f45af7ec4e90245b1b535290be0026ed5f3039b167ef3bdf9e3e554996e9d764db27c741fe65a88bc2e4488c123473ada883c733d962c3b04e66d7a9ddde021103016373ab032e3c0277068305709ba3c74472a6ac938ee63d37d73eceb76b9d75ec863db2d3e48c536721324b14fde8b93ff671c17872372bfb25bdf140da2e4aa29c1d3d8be01124218068b336339676a4516ac0a1469486649aa3821473c45ec1dbde4a990094ea2110c99ab1461373da5e2468a160ca4ad3a152144a04237861a2e772138ac9c0c999fd12bfc5feea02150815a004f7b6c5074ac672d985dfea69bd873ec20566ce45cf8ef86e31a33c1a7df84ae4d533aeb0a0b62c485b2446276f60e04c4797af7a696b54dabc51e9fc4938eaf98f0e9bfa6031f8ec893ba48218398d86b631e0474e25388fb8b8ee21528668428589f7da6852a2d7775093d71812ead9f8822909f1d1c71bff3f10b342a7b74f1d70303912a83f626b677fb2db907b6acc9dce78c8128de8866716f3b100bd3d18e749e3edb9e33032d440adcd70f46eabca259bfe992d725b6a71bf582458f85df33d0b2d306c56dd7936552dd85d603a16298482ae1200ea32a04c10269b39439a48108f24137808a6690fe7e516673e33ad28c4940b801cad174728a63020cfe48e5f6a1a4974e8570252882d422aa0da30622bf0e584104a1f4f4294f474af5a7ab3f9b3cc045ac888a1f14907ded21574383c8aad2d52cb04c6f33428398ce5b3f324338d6bf3a6d0d7bc9cd147273fceab9142f540534880e245f1f0ddf42e4f186062127ad1c0323f37d3c1268505075e33dade2a80043830ea59f111c63883abcd9de74a78c1ae9a3d27808491e4b7833eabccd83412372188b7c85ee3d37dcd0e4e4f2e54a98194eee1013fd2acd1c0e0db2866cfd90fbc8a1415df4555c0efa55047ff60960cc9dc1fd3f9a4c709df257a76c0819e7de2ba141d8782cc7214bc6a7c70cc9e4d8d5d8a8677ea1c01479ee8074f113bcf9dc19d510299f8877b636f11d4db75b33eeec846468d0ec513da1225c44173e26a59b24fd2a200514bf2b763a795efb5e1e32840619e5878dc063cf621aeae4c22410c84717e4cbdc3d1b2ff5b4c9e1a815880d28340855d0454b9e082820839506775e6810ad1c2dd2e317c1de3c5e8896a9689c15688c3c17cb4d83e08120f25893ca28867e0868104e0dd66aa04165a814ab186f8873360ef834c831a83f09de03c8726f3f32646f3316699ee4aed49ba28690ff759ad0a5a376083d7283c4d019e2f782702965cea3e6d0fb388a8c1851b46b5e0905044a855a4271388a53be84ce7351c460139a7f26ce60cd9cad78997896a0d0045fb09f10f5ed8509f02a154c7a887d96d012e676f25937ead5315bdf85141bc0221aef0375f0e1e26a7f1f9c8f468d66de0ee6dbd894451f0e1f4f26725e04d2a7ce655dd11dfd7792620c9f68dc50d5673c0bab1ad6677396c48991a409eb8aa2a2d9246ad5841318e0872bd47899f3a0313283a2cd095501d2436f1a8b35824095829c099e1b4655c400dbd50e05350cb03235d9d553bc473f44b7bbc6429e60fc0ca99af9ab1f0b5774bf828ba9098e8e12cdc6c1c9bcaa268bc14a62c2433b78a627e8252b54c0855cf2ab846b61224320375879be251eb63573ef5ada986d311fe5b57db3cfe51b44bbf152255f383484a8738097c22ddd0d78bc131293c6dd4941cd41d972703c0f49158d372fc0fa2f2891b94ac743594113f22121651102f0e818d4616132bbe4eaa1bba70afa318e32ebe323d81ef056fe6ba08c9550e504b51f540a3b6ae032da89afacf57dba28444ede50f8bea97cde7c5389f8635ead6b280b297ef380d47d4dcb9a9e371bad7daa04d18298b8795a778aed2c12914645cba1418888a40090bda5bd4d1d29538701e73b9807a3849d022b27cf56ffbb6deab32602456eb43511901d0571d1c4602a3566b267581c43754c2511322dd90d4c06719588673e632745c4e45564651fb9a251120be7817d458840fc68639115e8cce2d2a92f4a4a0c80079de3b00a3d8ea0bc4af39a619a17f2d3b6f4c16b3d170c1d1a1747549009207cf292f686765eeac6d43a7c4fea6800e83325a05a3010dc08032a09ca615a605cd5a403e8df0a884db5e1b6a261b60e740008a0be5991a84401124512917809a0352751b280be3c7bcea3508717ee9cb5724f7f5b6698b605a4ac75b2f2aade2194754a342bd7f0e2070319b184c8bdb7947b4b29a54c29c90406d505ec0551405285245398c2149248a14715a840852a506148134e68c2094d34d1c4114d347144134750ea61e15ccc96ef2e9b5041d29d7591b92b26bb321eee7b7e1731dc18775f7a4e720dc610ee82dce55c80dcbb731930c1c4136e6e6e6e6e6e6e6e6e6e6e6e9860828923478e1c3972e4c89123478e1c39c204134c30c144432a7952146c9e94144d54d25a6525a594927ae933aad69f52b5d3537ab1374561f3a4a4484a29a5a452a46e68fff6a6a8ec862d4d3fd1365129a57b8e774a524a2929c7a092a42fa594d2744ae9cbeabbbe6449fa0aa9949224a524eb4f0a4eaa922c6b454d586badb5d65a6badb5d65a29873ca98a9aa6ec29a19456544a8a56d79357bb9392727a29a7977392d3aaa47c4a528e41634029ad28a5945694529c4e8723c92b5e9e54432a65f69448549224499224296320a99452522929a53fdf849a21ada34b184ed124e59ca669a295c2e6494991aca497d5444dd334d13a4d524a29a594524a29a59495be94a4a7e0a5974b43c9bf3aed184c537aeaa78be14b9f8d344bb520f99423d91e9d8f6f551d0e957e92f2294952495fbbb20826a7be41094f689e96122fb726a3d93106e8337d863f89505b6390a2efdb106af61782f8b9992908298410ba6e2cddc3894195fb39f0d3eef9fd8d1f426a4173fe9c3cdb0ece757f45a47e0a4cc26ccd026a17ce39e79c73eeeece9dbb73ce39e79c733a2637c5416d2c986d36eab34d8b9fb8edca2a40c40d2dd3ef999f05397b437b3b44104fdc068439b9b6e676381d4e2c315204c80fef4012244384f0e81d2de3c412468200693c33b261430a75834ae6f6ebb8cd3751c645478eeb0e0935a339ee123773391a3a7f2e3cf2503b403f8b5a6239da690e16d11cb79f1ab93fda98d9bd5b2d3615fff6dcdc4b179d0bf14db388ab1fb489fa4ddda0360acc96923dd1a74825e24d08f5a4b8215ec30bac1f8da4341e724386d0b8216dc91257e3422aa9ce091c483ae046396e7b97a4436e48bb254b1e8d4b695dc54103977d207c257a88ae3f9af868de8fe7a388112915316251271af2a897a72858510889f857466a266f4fd9919748632bd98b4fcd1098228436437f1643cea655da05a1d1d05e29a166b57241f562752f421ae31afa68a4b404d3d03f3f7397ba41f54cd2bc2538c62bc234b4d34c7689dbff82708c67e4c3d00d69333822ee199193cd3e6e5b1aa99450e1cbe877f595e14e0886d2c50e313448b008870f223dca11a42f0c1e0d465fd8307f37c4438e456b7abd5fa25efed1a0a0600a0add35552ea899bbfeaa277ac1a63d9a9bb96b8320b725719b123f94dc5925a1b2f52248a8dc44cd2835e2354ef9c69f1bf122b75f8a34d6cfd6c9516b61b6942235a3149bb3bfc8850f5fc807804cf7374931eed249c58f71d7b9fb4fee195f7efad90d1e0de1dcb720f84e3c0483de4b207ef88f2576783deba231cacf0826936965849a359bea7790d29bcdf419765b7b7f125a7127dbc9bfd6aef8d6f5d23eb68f1f69dc98e1281a376eb3cd7381dafb3f93363159f1662b4fa61d5b6ef5f172caa586c6aa7665f4b5f6c7af3a42ad5c925b3d76ab9fceb16bed2a70a395d18d35d6cdddfd5dd2b7ab3907b528b5c76efcd7ac2b660819ad8b087afe95534f1f5eef41f12128daf642f46d5688be415440e652213846def8b3d92ce5b668e13f1b29ef02e1502ffcea5e464b97bcb21e6ebc3217b7b16b6eceb51437e19982d964a88d0816105d221081e4b646040b8c6e7bd73e8729739b6b6e5f7edfdd5a2da1f2f76d35d7b5f66e6059d6fbf3d7c9759a0e46cba4fb2a1ad49aa6e29eb29994f39df4211eb98a6b1e15f1f9ead609698c723f1b49146e0efe96a85940b0f95b16524fca166a914834820ac3ff97ad1bd2a6eff01dee358d39d7e1284ee73bae3ed79f4d35d5545b2128e6cfe0fbbbc3b8f4cec653d87c3a1d159f14289c7bb78ea6cde09c8e510e93772a3e9f0285ede79bb91a4703990b0d46574e88875578f84f1aec8bfec995c1703d5608be2634f24effcebb4ee7f2249dce77ac27b9f11b67d3bc82dfe15c8ea43d49b3b0751ceb486ea250abdfbc8ce6ab5a4fd2583312fc2cb525d735207a4a940baaa35c50fbf2bb9f6de2aeb21bb7c78af715cf178c534b881a0ceb09d4071e5af1a97585cd7deff1f48de77198eff1d09c04b9ef578c3c54655ce5fd8a91cc8ad18ad17d7dd3dedc960d4e95b1fe357fb52d46b8cc7dfe6e455b8ce018cc6c6d3c1dd7b2f174cc7fdf42d2551e8bd458646b2cf4f9e39ccf97bb9df5cd9df0dd9c8de47b7e8b111c4334e5cfa9b6e8b83e2c5834f69cfd702ad6b261fdf4a2adce8f3a28bd71f9e324d916232268e69a51d2be29b9426ef3e06a28ffeeeeee4e3bebcd3c344b4d9bd3af98f85738adfd0ad0b7c6c2e29bd36cf6b9315c577953a0d61a83c271388da2fa24384616d32fe850d9179c90db2c2cc8aeb0b61f45f51c6f18ced76f5ffb396d41d5f34f12fce61186b7bafbd9bfc9f0892beaa5bd53940e6c3d0c1600f605cbc282ac8378ea3ae356c306d9ac2de1929826259c8ac3f1a640d69acdcda6e6340fae8663b41bd23829a67749d8cc09b9d363970a8e5ce96f30fe28bebd12ba70db67c1b517d7b56faed9cc868b77deaee6542826eb6a565c9d57e1f914170aebca62b8667858f12c869bad74be35510cdf4422d7388dc3dc507cf53ec4f0fff9b4aff8339367ea4f3cd577389d197f9783ebee709b99d88c93e850cfc3a35ca86a6513b835098e51b9b8edb3b64411cdc2d4b7deadaf5fd56a75a835c7834b8263b00c035aa8b51e1a8ce884dcf6cddf6dc68dbe58e63a5f38aef7d596b82e79cbc27577b7990d17b77e5555ceccffd22e5b10e7617eaa95537db5aa97e115b5ae31af7df1cde67dceed0d829f90f04a792c18a509d37fbab3dc5b7387ed8af10bad4b826970ce229993a556c7ae7a758d3133b7955ec1bce182dfa020f882e20d8dbd7727448cfcd66618227e3f2808be41c42b838897ea00728b0356db0cfcf05d305b9bc1dd49fd52f7b394daf38faf6d6527741cebf06f49eeb478e88163f04fb3b26a700c6e14f542df7d0fff9e871a3628e5d2784e7677e75e923cfe4b9eb58c76f7318befce3d3e3b5b836970df62dcb96fefbe3d0f3db024b5247c3bdf3ab606d3e0fc1d77ecde619bb1e49c532e930ed8bc292001cefaa91ba6733cf4c031dc7bd60c25cbc962b2189bb524b79fa5679b35969285694f9f9f7e33fdf6ceb7f1a53d502ff4a5efab3de79abe5ef3a9cb7a7955f46ab7d5e0188dfaccbd37778abecb1adfd367d2b7c9c2481007f7d4d6a8df5f8363b48c3e556d07053526c6a7af2eeb728d55ed299b35d18b3f0cb5d3bf06c7709b41c0d690323a95836731fe4eddf0dc9559755b8369e8b7fcdd871a2dc9f5e670a7c76c29520f9bbbe236257a00b9d369eb10023a86fc4cc261be7b962421e4bff7ec61cccf60b8ef7d6cf17a16c219eedfdea1860b29f8144e08dfc34020bebf458f0582a215101e92f3ab35d9ca328da374f8fb7ada4c465929a9ad31176833f764348d9ff31130e7b434102044fa9c2e1b1e8d6671cefd646d688c0602a2502504bca5d198fbe7a25f7fe1c5773a285df4e77d99f297f17aa5593297e4f2f7d7d052651b01d11fe3c49210101278cddd3f660bd55d8cf703d158ef506da1b6eb2defabe1c56d76d06a8b09ef39cdacbe6faa0a897b0d05e9294a87293da55eac6f6f249372913c269c9ff346926e2408dfb78e7337fc60c7c2cc7f0ffba49174bd0de174f3a1201f7ea49592a497a0f44f82947a792f3df82736fb5413f878a25edec3307ce9b96aa97d503acd949294d59b9bf794dc2cba1b8ee11eda265d5927b9597c6c1d929bc5c08f8ce4c68fe92bb185919ef34fca2639e9f9a575df374c43fc8e658932c99975d66941d4cff70fc75aef55ffa6b5de513ad06eb3a7f4110ad25bd5b7c891400a3a9564a38de5bd24bd6b2cf4df8581318fca01c24b51e96a0bd5a6c22f497c25e9254992e27bef41e91fcff8100a4cc3c49424afd65ac3f8df451be6dc361451982d45b23b7792c6182915638dfedcdd638c14151fbbff74afb250dbcd1cfd34182db32efa4cd1e7fa1e45c97f6957ca7795e155af76554edfba6e5c78c5f8ad6c0da641464f944a1efc8f217d5c21849036c64254774d345e997be79e9dad945458a899bcdca46952233a709b122798b9190c4f510e9f73d39d77779230a98cc9ddb6652028a82c729e9303155fda295a508c263ba8f7f8a0389c5953bc4c41e9cce677b2149f99f088334e4a3d6553c43a27caa53349f9d1664e52547c6a4629677439292945cfac3f296a3ef591f3114548c553f2af784953ca9fa69593c9abb83e7f7255931d5933b9d6d5ae87e3e15094c763419caf3fbd1db28382cc068b3ad45b9d87d40bf530d566fc28f9b95cabd437b11635ada7c3a9aa897a2961aaa72f9da41587b66691928c52caf8f23db2bada4f97d4a15ce2d75a6bfd583f7e8d2f51d4736a4559d357d78d4bd1093b728c51c6a453646136cbf42ea9354bff6ceaef2549a7cbd5f751fa4849708a4f3cf184134cdd109f78e2892ef428e20f207da3f1ba9f229d5c7a14f10798ffae1ed356801e8006fd8f9ad12fb5749b5993831a74958ec625dbf5c257bbcccd1d0616733367299767301d2371ce512eee25f95ecd7bf9f22e237121433af47648d285bc1f6f87871e907e3e6eb33fff7095241d7a4024fb7e3891c6faa3f5218df5d71f6eb32aad0b792e2448103cca9e0eaefbd12c401a0ed2bbab35461be32bce1c615a24a8520b51298158451a3aacac04e91ef7cce322a8d97f4a873eeec5e39146cc18beecb3cd00d1d6a2a4b3d921f89fdb190c6aadb5d83de8c0cd9d9ece807f6324f5f12588b23860d4b19e311f44515710540eac83fbe81607f84d8647908cd005b430cf7591252d25e33699ce4f6b349a257b1be6cf5f400ff3e79c4a6e3b42e6b624b729e123c79d4c49eb62acc1b95f7bf2dd956d7ee47894f021f3ea9d2cf31160c3e77e7c7e585c9e38e8cab6359626bd0df3694c8e3fdfd352269402fcc90e080fa1a0df3c74f213c57572792eafdebbcef7d5b779348bbfdfe88b3f5e2ffddc8e2fd744c0b681105cd827b8b0550fadb80a9411f64f7fcdf23d45b09849b90eff49f7b1b918c2ddce0761dd8eadefd5776cd60f3321744978463ed7421a094a64755ac69a405b6df691a92eda302fc265b78d94dbb5ea87ff43cb999a61f0573cc435ad02ff3f9f0aa1e581baf259cbf0b8f0330e12f23c84dd011dd94af36896aeef23a43612f0b31a6ed63234ae02217ccfb583abc0ef170df32ff28346c80e22adc88d5c5d2f1af3294916086ae64432e0719590818e0b9f9180df10b27706e443e8b6077ecebb71c72dd95ac87a5e698c6d0f4c49caf9a6840c805cc7419640a25920cc38c7850f81b0b2eae9e189570cdf8a6780f087eef07df090c7c2efa159fce10e1e82d2c6c66f540d801f485d98b992841fb9c9c8cf3c190dfde02a12aefc205f669fe9124bce29b601444cb378e8853b24524a293fe31c3e34269f2f07f4d09894ff5eb894527af1f257628a431b921f705cf936e4c7d863bea7e53fb6d22c71dad62a2c65ca952eae7cdee1847766c9d1629b394786524af9376e8cf23d7cd695c08f80ccf0b987efde83d26d3aa37cdf80fba094b65518e1755f0e15d410109e0f8b4fc15cd0c658baf8dfcb8b31ce0925694ead77c0329cd6c69c36e6409056113573d72585cfb9cd7c5fa960c55330174d25a0dd8fb4b9389d72911ec2a7969a149d3cd9dec125c3255aa3b581da6f65abb1c940f5161d12da6baf3d2b03354bb98f5d38e8eeb30f6cf0b94a0377718edd4ab33f619a168e2a29db28fdf473c59449b6a5bc22f2a3b45edc25addce24b56922fdb3f315bb3408b03b5a7e42da32505f075f0dd4187f04949511445bd44491fffc67d5745bdf98d72bf6cd03b815cf9327ca28d456b0335fb4c764500c2922bbd7cc94d1611357392fefbefb9a233822a7d4b2f491b998fd0be99cc9ed6dfb8f0cafe421fe073ff6b31c29d7970425b88f4b7e800cd9f2deaf76f7e6abb9b29bf45bdb4d0b4a85dfe161d5d512e1c29959e73753ef65ba23843de8e258a56c496e83ee74d019ebe85869a4c137d29b3961d93fca9c5088e71f234726c8b8e4bff64ba974c2553fa2c9f5a90a32a4ae9377f9c74484dbd9c3c5327fdb1d90745fc93b61d1454e9853e4cb419d378b2621421f05e594bd9898874cc2cbd2910a38f9b491f3351dadf42c331e877cb8e9b49316bd171b397246dd171a56ff223fd284d1f5f92b2d61839e6fbf9dd2459fa2d34959ed07868858d3ca1b972854d269d9a9a9a9a1acecccccccc4c75918b5c34801722e0194953777c7cf8305ac232353535353535333533333333333cc4c6880889e5aa9a9a9a9a1a6a6666666666e6f653021420043e34242ce346a3d16806fb9a0fd3f00ef6c13f3808ada9a9a9a9995ec09859912f6a86d8ec70231f30ecfff391486a1b7d6cea14dde8b991bb917323189493a8314070fbdf8e95ae2fba9b8d8dac36fb5c9bfd7d3617bb9235a4f28c4182960f346f6c48e56f1b03d0b621813b9d50260140980968b735e62e842dcaf85ae5db283ec27c406d3e6842c99d964a77c7185d62b42ced0d5176848e07ee4fb2ee9e491b5d0fa20d0c7364c9fa464fc63ff5e56499e5f359b7f80cc6cf2694cf2ad0671d9689432a9fad8031bfcdf9838704f04ee553c8f02b04305f86f92a327a3afab9527eba54fc7c8e88c6f2b129b6858d1665b2208b83062a3e037f009489360825756a06dfe925221a4b8af52d3e7318889fb90da07ce63c00facc8540c5678e04d79de0e326be376a9da69f2ceba50c774cbd408ac60dc6e162fdb3f8782a952952bac6ac9f103617110505e541afc3e751a6b7acf930d3d7f77cc4777af779eb678c1405e99b29f19372518c4e6b4e69413f9f291d3e3fcdcb4793cda648290628e532a9197c639a734e13c7345d2b8d4df6456bf341576b18cad51a162f21176b71b586cdcff5a261f309307fb2fe73bd686c7e0d8d4d39df7d1371389ceb009dd39c2f512ed3b4de5dcb6615b82957c637c5422162fed0e25b6bd1e2ad0bc5ae3466d9178db9e6e837258e9871998b226da459b227baeedd0f2d56c8889a25e59da731e95b5c323d54be5e317c3f9646cad5ee0f1e9229dcafb0fe51a162a7cfc50db3bec5e51a363f5e92366c3ee8aacdd3b0f92ad7a7a9cc9f939bb35c2c1a1dcd2245facd52cd20e16d9ad694f38539ea66645a303c532e00781b3806cabb4f408331bd7b1c1a4b0b158f0c1ee9f17c0b1429838a65bd4789e1a700fce84e3fad8a6118f66288e1b100b468f1d6a3705a0b394d930eff93b5acbf50527e461ecdd2e2dd471f1d5279f7d295a05cbee4477710246a9e8572f1fd9f19307117f8c169c0adc0044ec659c0c9a0bc94b5a27cf6b90108c0a3e07819191c4548cb7ef6463afc4f3b04cfb0fe7af727ff9c68162983e38d3ca51dca5bef7e76502c75446d71b912da6408a908e03389e561a1614e00f66da1a9b87716043432400dcca8d827ae7323a6a15da1cde03e73a3f9eeff4e18a0ee041cd27aea889abdb030d65f0fe34b1a732526b43d254b50be67d0588acf7a880c0524304a99f12e8afd01e59db4e255fc9ca8e8f3441f14fb464f0402bd7ca23782e1edf5e27adbf2c20b6ff496340b0ceffe39212f8bca0286cbe128a231f74ebd911c0d19f24600b04ee68dda1099c78525ee8d9e92b70407fff9f585ac82aeccadb8328f0ae764dcc5414faeece3de7309115f78c885ec70ec0c628b6e41427be75417a548455252d984eaedea1557f16f33786cca91ea98a8fddc2f98fc18f6ff7136a81e95bff9f5e03b296bbbcdc6caed7fecf2ba4889b2a738bed1ed88fce8eeeee607610014d366e08f4083d111e0d6ce3e16d0d80e3c94580734e6a26baab3a35af6d118cf80060f6a86d5829ab525cca3886699be35cbc441d3cf39ae83acc38766a136684cb354efdeebca9da2d48707f2dfa1dd1da695f7ce1feb900d4bdc248900ff902e305b4ae63f9cb68e890087ac95dbb025687c01967beaabe9ae1d748ab29ec6bacdd02fd0b8cd64ad9f7f2c6b352b0e2184f046d4389cb86e99478d7910ffe93d2ac3df5730ecfff3a93693143bee1aea06954bb9406bb1a0ba7f1285f03b0890c626db588905a8f1dd3315ab950bbfa270388901126270a185711f5bc6f0ca60a6a3b451314e245e1a8918ee208d79a3acd066f0a64fa8ee9f731312fed006bea0f83092bdc1bd12ca853ea1f2c307d2403a48b348db4a1a43a243bde4c5ca0a86fd03a9d5ff8d44b57f5ced2e105b7abb08f8d23a6351e7701dde3caebf6f3f98d131e3c5a38cb2139964e771d949cd23d7c3b26c8f2822d55fd42c6cf36ca443aeb988045c4a776f4b7ae4a3e9de1e7a4793dcd0e8508f3a8663746b79f7cfb53da28450658faefb0f18424089aa7b970f01be1c222a7f164351548a48c5e6f9d9a1be23a549fd549bc9682d931d1e1f52462ace3fb96ad6f2a2d24e4fc9e770389c20379e58e92ef5263b24db1aa332cf0f8ef4140a0ef529ac6706377e9d23a8e4edf6944109d50c0000144314000028100c07c4628148301a0807de3714000d90a6526c589ba84990c2904186180388010200000000006044a09220008333aea2f94acbad547f4ff44499a8e529fed56a0decfcebd4d08ee32ba130f7114556f7c90187d7fff8bfce2776aa0da5776c8ae4207ba29901b475cf72c003e223734a38768b89179cd95318b709d9a2b8ab48959e6109c21c3cda873e9afd574afe5513efdf477ce837cd43679948dcaf7f2fb8e96f28c82109e966c4477cec64c5e1f1498aa61366ad805b65c39de71a75bd05bc25cd0aebf7c49f988992139e79184b239b447e16e600a91c2655bcf96c0b117f4fd230aeb995778668ba59dc9fc0d6004acd381930d0f3d5e9aadea371f356e7ce638552f40a604167013c58ebe5abc512b0a06dcaf63768eefd8709c3bd3aba25e52fb51a8c778c09cd3fd29ce1dcb2fd40f25dafcd994d9a3ddad40dcc7cfd33b1133b51d89b7def6d9582d4dcb1cd46c7c9c62d64f4d538306d99fdf649259cf4e4c02f81fc4efd71c768716de75a13c71f660a3872dbc0e3b139465a5b3811a1999cb58c70333a0e460cc591b99c758321f4496ae8f35ff0cc7852882ddc990d5e5aa8b03f5239626ea33444c0175295d574c535a492fed009f8662e533eab30093767d93ece17cc44e9b9d62c9fdaca7122f6d94922263f6ad330da76d7c5b3189b364716cfdfb8ee452464748587cf858a61d84c1df372dc4e9a9cec6e4a5a55c2c9383e500e8fbd8fcf1dcee641e046916f7660c85feb51768e313a4c9d334560574f66f29db57bc5dbd93ef6ce4e2c43b76d42684fcb7874aec75891958827ba04d9f15d99957b0086473706485f392757699b1682c8b3e2b335752925fe49d49ce508272dfac60b44201cfffeb708d559fcdc8bd791cac53df876021690dda7945095f79c2daa06c5342fd91bce127d2f9cbc83e7d156b78bae157237832ea046df55306ed9dc2525fc91f724cbcaf3a219df19a46480f1c8f9c04f042c8b1892b3aacf74acce6fcefae7f721065b5cb38bdeb3d693964f1e8130d40b0cf5bc1b68da1eec3e5628502d1d480d4c4a40f8af8e792d7096c86d1826870d95931f398488481ec0d98965658fdf727fc3599c862fa253d271f63a8560933641a3a5d78b53ce0aced643d21f00909118d6411552f51397e36205ea688007a9f956f761d1bbbcbdb3807370700a033a9c65af41d4d93cec444dc6c079f4b0a0c12f12c84c0ec019726657398af543b525dab04a21a2abe735d99b1223209ed2b8f63fd57e282e974479bff2cb67abdb6a299eb9dd390faf08eea9be68812786aa3523edd1a26b7b7a5524739dfe73839dd698a8bee80877b9ed0787be0f3fda6a0984cf80c858be780a3419861551a8498942c0a194a069c8d63d75b84e43a8d14a98d93a46161f144367c0fe82f3fa5475a8804263f7443cc50e9b319399979b9eab71d4ff637f3a74365bd8b23adf0575a196ce0fea3d59f6385ae46a8bac70368a43da1d211dc2fe441cc4cda0304923fc6e4c9bf18986716481ccaf964393b76e0796aa8d9c4bac406c0ec574dd1a576724186b79c09015ccc9d2ca26fc470ff87f85f8acc50c948496fff54369a48e59a68948af58220c0122a949d44c245ee162d452d1955accff33ef3bba3021b3fb149f9305cca064c071764a4e610f31171ab353fc429d9e09bcc8403b33433511929a97782e45fd42fdfdc46674f275a81c5261ec74ee1c0a09a2916690dfc40d7879f4a0c7d8b46edfb322d21b66c3529634383e100119d9b0ec484fc3ec588470d107a277f65d7517065a904460e1317743044d157694b91c8f5f06ee9242ea89c3575ab0f1f3e013fbd9b08211c4439a723806f5ff14dfc2722110c8c42c6afc89a29828eba3d347a22d509884b29d070cdf64004758d2c6ea58880965840d620b89cf508471819e58b8111e0c4b7b1af1f916bda5a1d26134e7c65bc1d778d3a16a10bef8b060e5df9a708f88ff2def22be22e97bd66258f4fcb7063e6677fe5bc592f7744f73d5961ffb2901ed713ff5a6ab777df0f8b70e8f411ee803dfdc5bf39d66b8140a97ce6164704f5bba5200194b348e69017fd91adb3da77487d0754742b125ab69b3bd35510871768c087b022374301f0c23532758e33ae7b9a1bab8fb073814ef1a2d0843bfd08bd5250ed3d90f1e815a7291668867501e47d1a134e5bf13cb5281105454a309d9871eda1e846eec61b39f594f182d89aac7e17acc73341d5698848f8aba00cc8d56cabf61f55232247f3d057d5b37f160610d56db5c47621f52e84970d058a60fb5150ecadd2d88da39f6a02118380755003c7510591a3fd96c250b38fcaa2ee955ece958242e199413ce41f4e21d60119665f1c8c685a0cf4c7294bc22db377a8f12d48e9d74c267e8a17a7b926be822c039a8c8c8fdb7350c2a24d27a0e12eb4e7264273e3b8715df244c9f0d70e81a0075810e62bf5729cdb9b21af8c68d5ddcd46e97b51229ce095af77607123f5b6ad742f58f44129a42125ee4e450f56650a491e2de5e02b49352b550851df15d973983fa1b71340bd9fb8c91902c63fa59d5d2d00b8482390a397e9252f3446d41831816eac11d5d8580c40cbafaf2976a06090500ebebf81c05831b0fb3d67a5bbffcde00d91af3e07693f08513fa00643e5c8fd85e990bdaa727dc09acbd36a754a95c3210ae4fa9b3473845539786bb8f08a230fc88658b445851b809f7b416d2b8565484f51489ee287d4bf749c2389ddc4e70be05911dc1d481e2494589016fb17170a5431420c0efec4ba11828224d06cd1a4a80ae9ca42d158631a8048ce85ba99bcd449e06144c0cc5499814b8336b812cac9f2cfc10a0890793277695fce859b87773f8d9b06a849d4e2773a36998910136f1bae2c2faa082e5333e121ee427f912bec4f28301f517ace6ba8d706fd6b6c227e546a56322e557325a78151cf48be8764d836a51e1e5b078a9b28b1536e13b03e414ab8cb6088270e362bb1c2a144405875d43e06f339d51b1e33e97a180c8032b9c0156b30c7cbfd77c28286edf4784070fcabcf45050d75ce4d15437426f589d953ae497aaf2eb4b5089ed932808119eb35b0327b8919cd0aba8446cb040b8c8d04576e9d701fa1840255b0ce2ffbb21624ada601316430a56211049a220d6d80270e7c03de695cde02d7d7c93e01267a2a0e8e1e803a8950701db9b5f4f3d92551d18286247daabab38e3cc691923ca6f1ae9fabed2894f29f208910a26e3cbb2b2a1461ab799fc008a82e414fc4d275d3fc9136aa347e1c7741eef4afc5dcf1af5740f2e4f3114853034d94f6e67d201cd851e91f39282945e515ebd7f2a292874ddfe69454e9c6621181719f1deed4ae019e39472ec7446a72deb127c70e1cbbf88476ace1aff4593cf366cd17626d625372360411571e6ce1116645f9142c84da3b539b7d209079e4f04198c7d756cb4aba60889cc3f6f8ae3abc3f7e0503ad1ab770ed7327c8bf9f2b86d9a821d6c7c12ce95ad734830ee2a4e30d3a0bb8a0e2cfc2a9cea519246e896909ff2fe34fb219950fd8ae804de66d53ec1a29f77828171ee8c21f92c921686cc3dbf64334d4e70080a23eba66fd95a7fcdfd8194b248467c4fe1ddab8fe3ee320bbb9dcfb393d436ff4235477c0aa2288b7ad9364dc3bb17c49282c244a8efbbed788856866ebccc9badf5ec4c7c8107adb5bdf8826fdc1eb13acb0e49306231f71cd78eb9d480278f759a73e2c429749cf3637fbf788e8adaaef3533ac9a93e577d23f8eeb70d229170c3f056f05bc213d663fd1731be5d7e0a377dd1343c668a52e38ab7d178895c6207690e12a125608c1b40001dfff44a84773829006c33e4483b770c1eb87d8ed2928088c10024fc2a54423ace9da27a7b665a38d93d5ccefce6484379ed11d0fae8c4ebe870d98f5f26a6c12f953e9334b56ed5d5419dc815e1337c93b7ca92136174a056b45bbc6a3558b771e605b9e5f135eafa23f580c40033f2c123a192153a54cf74df39f882ab163e82060321bd304f204b75d3cb1dd353b9576b8735358db5590b31e0681c92f8176d7259ed40059055188ebfc6602505143264b9a615ec45c8283f7b055f82108b8b660f721d66ad8f03b760b02f30753de677f2d097bbc61cee64da0d7a0ec375e27c04e8df0ee288c6e45bc03703af940c2c2cfde45aee45a9b8a4be060aedbc097f293c6df35e92574eb2b0831320be476c6937141791fc79f709b8d3e0b6121b044e617fc5c84f1149db810fe0fa7e7cd3a80ce79441fc3e2008817532cdf61b46da524212dafc95c1ba86b9340f5e50e0cb16e3cb3c8c987b46a2780382a8968c3c061f9d40082622cec3aa8171f85be8c1fdc4303ea3fab41e8e593a02bc77e685f86b00452cee656933479839df34120b5aa485c9c1f6db9925367c5ce8ac6e80fe248ed494e046b0636199cf3e2793353fd7d6a246023f2fd70b12c39bf24304232b36bea1b26b38ebf7b9b34d20e38fb95cfad931908e16c5560a0a6f2a2300b8113cf4fa59d2cd35dac9a4d8d9ed82cae97a2a5f9ed2386576bf47436e2c10a6e5a1237e345962e78833ffbc46ee5278fa3cbc545cd07bcbb195c487dcd98b6d437c83125c4b588d6da3d61cafe77fd0476e89414ca952ba776ab3eccfb293b29b2227a281a19ad839243e2f1f161c882a56c8ead0cc02448070a21f71124dd51ccfff20007b37436d2d49215ffd2c63321e913ce22dd40f3a1b229409d35f60dad7b4ef0c71624ab7409de16716fafd8d00398442c01cc578b275c47c5d741555ba51cf73819a1ca22bec14b740fca7eaba5d19a98da3fc8a6ef7e02ab7aca94120ce46e276648a2b57eb620bcacb7cdc35107c14f9443d082455db9e53e1be6c15bfdf14a8576de1365548a30a44d76c9b0847d459686e9313c5c961da0b63ad4cd471bb55b265c7fe7f2cadec019c0bfc1201e35f6f7ed6ba94f0363d9490f33b9804a65d1d8e356837af725ff84212bfb793eb45aa3c6bca868e2fdf7b1f5734d55cd0c7ad63943ed1b9d908f394f66bb81bb9c1897b50463b1f2fdcad7dd2ebd6e71c3249dba905aa25d58220237edc3a111b2da593acb325a301276e4253d6c73d4ff86ea819e3f715414ce9f7360308635a3ba44ca1310df51ffc6bf56c0aa0ad36cb31eaf1788a4c935101f596b10f431c1fe9f0f8ce8043ba12b250923bbfecb39735ace0b8aec12513ff8caa2fa82c1e5209fbea3839b56123a21300129d403097200c720f2dcf2ee62c4a415c8e121950d9d445dd0d36a8a239d3f02ee654686ca93bb5fb8076905badf8612662a8c9075f219439ea0db6866dde4a9390fa5824a7f95676061f5a303eab01bee75bb91d5f0d988559238bd976caacf827b4815741ef04a5ff2c6b1397e4f448b7a8b5c8eefe083ec0f800d9646066037fc0f3246e7b71de2bb1d9c7db7219035a50db83e6f76a5e325da2cc5c206868a08a9a9632de0c75981d1468dc370f8fe5473abea38bb71ed3e581699fd0c2313358f9dea7bbffa187989363a21fa2cf03be630be41bbdb81f044206c91488375aa530847815ad956e4299b298ce03b7fb7bed9b4217b25f0e4a5da9b0d2c2158348e5a9b4611a4047009c461e392d82981f9abd0fa0ab83671e9e117f3af7f8f2f5f9588ecd2e7c4a30ecdf1db5ae1f602bf2ae594f5d14ad3ac437c4cbfe7a1785e2c832da9da2ca4422693c5fd0bdb98e81d55abce672e0ec7d031d57cf59b36750e54b13f04af5b0c716375097fb764eba298b9d365737a291b36ec210b270fbabbb24c12b82919075f484c9f78c84657b967ceb972e11a60225df015d119ebb5638ef9e31e03108c4786159241f47299f1abe2263d7cd8dc2208c0a8d6f5c0c6b0e2ad217b89cae7069a8a3b6a369d5e0eb48b23c26db67ea92825fe0c704d181fb16fb32f4e0a6de359f409888d8087036d99d0c7f4804b6e35a62073332266613f0ea14ed2a341ed6fdb7f715e127b8596b26e7b15ef6822a60b9feba9e7c03498ba92f3c8cc8f699ee270a1b3f41d7c43fc9c13a6d2124d30771394bb7f89f6dbb8b029c6704b5f10f89f8acb56fcfbb25f45302e0188e153259fd42b4417522476332485cfc0bcb06888f0f7a2dae1fd91c4a231d20383ece020402a82d69a5c25a93f66c0da1e23e01d454b34e3f8117d70443e6685309bc14a634f93bc2959d7ce2974c49e696fa7e3a4b2a1985c8f15dd5d39451307e7c5158359f7c188d6e8237cd15e9e05af38eaad5366862e71e78c8a042e06795a8f2eb75933658d53734131cda3c2a03d61567081db858fcebe7fb405050b02d4b1937118ce927003e9721e00ef4887bc87e529f2efcb93f8079ad5218f377a40fb4371df859999d87ee47e078acc9cb78df83321f45990e2b7e03e0791426f8c0180996b68f615c773ac3c545c334ea3060520a5be11d0686544086ec366fe5bb7ec88d5a1f76d171d09b4fb658d26b11caf41384dea44da68c6af142ea9d7300d062b4bae11649b80f3dd1439bf419e91f361eda1cfdd58c0c68e580e4dd507d1170794f3fc1bd5ee0f86501cd20c4dae49d5a32fb29905259996f6857ae0672df5c3d593a46c522faf89f02794bb72a6854d17a36717af0922da81d231ba8e82c83e84b959eb166a7108c40d1ca5b98dc36a31d8a94833fe32ccc212e1780e8d927b4fb13546603231ac98d456a3a5c78333aa8bb8412f4f1cff89c7459af1d1222099ededf7793c2129de7a43452461772e9acd8a810bac90861c51d08c82a648659f71382bbd5f8e141225687a93466daa4cddea301e82c38ea97d8e3f9a36121b5a169530be1e019d3fd0416d5861c5e5ecdfa7f084377c1cc5feef0c57e92835b55c092f06906b94d2092e2833b38e99aa8c6e2a2598210a2ad31ddbfec22b6b96276ff4ba2bfa7fee117953b0dcdf913d87348348545ac35018c7bc685497a33f95bfd0ab8a457ca292eaf4531afb01c059f06c2d58b5856c76a2d2ce6c7142d9b5d3376fd30cd98c3784d6f6b5d3386bfabcc6f02b4177735bf4a6a9859609aafde6117bb5c70b2fdab0b0324e5acd0e7aaa4f5528686014b6ca6e6eb62227f22133502d261087ee017e69a04c11ab536f0aebf5b9508a34d384d6ad08e94e322f0a60e16f4e3a6044dac26dc1ad05c1221904ab40cb21240c467213e8f7f6fb9658e543d9f790137f600197d9a4fb279df5985a2bd42dc21492c8ea9bdf61e01f246ba2413040ee2112ccef879af48dc2cdaee23dac4b3841cee3a14e28a940f67e6ee83bd9dee9e8f3522b2de17d1f807813a321ea6530b8cb4c54461970e8beb8c8d9b38a92dfd582a523a479864cf43cabd6fa6ec8f440c286175cebb93f42cbaeca2857639b71cd8df1c78e32292340045512328bf148db619d754c300488c067a54299493583be16e161a87154a4fdc47a42d1f7e216714444e50f0afd8be398efe21b1c29f12d99372eac04e35ae653d7e560f41e6f2cd51b8d650f78f1c34864fe3ef7fb60fc12ac4143648c0441c078bd703d0c371c877f256f7b7c5b1f94c3687fa5a3a1736a12e13d6e4d558fe213a791a12da86feca102b43d33047b02814a5d18d018b4eef192ae5b56371608e475086af08cd27a53b36b8a06b18e5189172328967faea839b3ab10ad5ef150a978d2154af5c8fecfd4283f85a4d9acb8db7c21fab6fd1e8fb87dc117d3aefe3e8b29099d1f79cd070235fbe2ed2b771c94e0943fb5644d5f6dce96aea79662e11013cbb5d50c2d36f6fa2a84c6a1304f3164b9f099e57b6e583474cae40c465f561ae2830ac1708bea0b4eb12bb57375cf4f605d8533bfff7c23e10327185a603b31550ce49a90c032c01755e01e69de002f8dbaca4c881651e6b130d8ee4be4ea74348d32b2e884a2b1d6e0b0f91c3c443c7fe5e2856335a1ac0915dbf5a8b88e53c02316603a06362929b1c22d917428d6d91ca49c14c050b4141c8b531790185c4dd5758e5638b98a3365553bb422fe13695e37aaeb6e5befb2438562f6e79c7569b80c5f670d8f2f108e129718cb94c7e32f1f6e58568dc44a7f1c1b08f19753f4fc6f1fdca0e21431eeef26ded408019cb2d67884b0e9c01d1e1d7af1e8c6e713ebb54e085fb801227462e53d2d68e2448850893a6b7437cbdce687bb4544eeaaadd63ff175fd6ac280f12b25f6b12878fcda87cdbd4a2503dc4616c02b8548bb629439e7452bd590a177809e7a9d4fb4425b1eae7c655dff456df28a9bc89d4485438d8141f1ad7b9c4015b1bcbf6e461a8bdfae55289a601d9e8878c802bc464bd72d960d172d199dcac3438d4517fe163c01c11670517664ced1922301ae429742e5ff3eb6ee854c0e17e45b7d469d0ca9134bbe5ac9a60ad000546f8c4c0edf3953c15c4564d6103fef41533052b6ba4a192c32b2a0b41068d05790f0e27247bfaa83d39ebde71ac4b34a23cca500d08ad3d9c5d8c07792ac03ba6f3562e4ffe469e0853e4ea28faed4127359f7faa9cd4d06446a23d10b2749ef80d27c38b0dde0f6e36a0a2a4542f5cc29535ae2e24759788d2abb27a80975fad80679edfaf563dddcc20a8306314738d24b3299fa1338a6d6ad7a94c71757d7511d9954387c5bd880a34019311af782204d5130b2423710bc712339178de0f5f801183cb4c95a428e5378e265ccc1243d7a60874f9b85eed7caba761cfed13625d035f718cbdd9bf5d743fb3d87b3aba82771638ac825b34c4706b9d114700d8de090bf5f2cb8615b355217f48501a9ec96aad68f4c6e7a9aeaede0311e973c33682638102436816f34ed021d10d99e199b3b8f857005f8730cb4a1bae7c5fd5926c76bbd32f9e7650a706f006afd6f04d6a4d8fd0cc90806b2ffbdc23a8e695854b2cc7624d3ba20d6ee1132d5444f13f48b0bb35b5809267fe16e6c75f13da72e40bb0f17d53b8d0b2cfca53b07939c0b07e36ad1560d93800873a40807a64a50b6df3996657f03a3857fe5650528b71938383f7372ae6b4c2f29f8c5242401ab18515b95306f6975e50071ebd21e2a3c207d4afea62eecac2a2086cb6fe9fdb880e47a168118cb103d59950fe1459033862716a114a7a372166c91d077c97e220522082013f9194c34c663729bee629b285367d6e98b85e0ca0c26a40ec88178769ab1f0670d68612d923d6a8ecc747c500bd75b304953b8ab20a1a9c73711e4e0625280cce579b9614e5cabbf3bcdefdc1b4c8d784cb63b1eefd81ee7766fffed751845e7cbef3cb09bccc59a01f0da25274f0abe3f33ee881e0f5610d9acfe085d4f772dfc73699a2ca5f0285b07c8775c9d576e459cc8663d19d598f6f8dfeea4f2ea3a2f9fcb8b95959a59ed29ebded56af758d348c2303f566a1348c42733f113ac0f8949edc25a5d50b7acc7b5cb6a373fbcc6c419b825c12c9dfa637f2d12b61547ca83ef82d898f4c0f5faa02697a175d6c9850db273246fa6afaa01186b84339e49b0ee58457a5167f0df84325614f3dccb78492fded8cbc5462cafa84db2f86791b2a5eec6898f391226b203d9ffd06989efcf9327de4f9a8d94a8c626477c777219c94a1df55dd5ba3bea8601088ffb4f89b503c7b40866595d4cf201e950a046f47574e1b7d11c287ffa9e6a8009688309ec110bf4ae95a9e5d93962e5ad7d08878a3718bf3b0d7775f5718ac0abebb17033805f57a2f31bca8115a9097dffe510b0502142fd3e6825ef669737908ba8e2528aafaa607f51a4014a71be4ae6c2d5eaedaff2ff39bfc21a5e06ace6b4547bcefa83df1f2b098c6e6defd4207eddd1de3469165f46ba5bc47f9e5b0fd98c87507c90a751ad9805bcc8f7abb078c81c758545488b62f0d5b00d86f4d819391af48cba1bd1b11e168e3bcf5bb41e1ed9997be9eebbc940d894f18b4b295c282ac2fccea65f045f817d834bcbe2b674aa827744945b14483459c8477a8c27e96acef827dd0fbb7f98caf3f6667ef366bc0a3f3fee916d744a1b2f49c37519ca5a3cef8690f18453f31912a6df75acb90c0a4b43d2edce5687e61c9590857b12c1b42029e0351b8c7baad8ba4e20e3e06524c9cb069e9dd0ae5b99f83fd5386b0909ded53ef44a2aef64afa8e27e2b73e4f87c0a89657932252a6069c7d17c1026490952628a67276015175a75a01311ec3c866a25e0b7dafccda38cbd6bc2fa09ecdd8d6f1e13277719971b65620186ec586dd064600dffeea7edef10e3dc017c96e8507e11cc47080063a6099005a960719acfa34cd73c10dc32257b76b0dd46bd76e46b2fb7d31d4930e0221e4ee63ba21366502f64a607d27dfdc556397ecd8acbaca96a82568d04d38fba0322c6d79e4a7d300d579ea8f816f7e1886b6d99e7c51453bbfdf27f334bb6ac3fc3aab8237bf3ddbd9c49f252a88e905abda70cd4ec0378c8cb48c0b74589423e7671a88d956d198ec3571312a7b372217af74cbded46ce0446a8407ea66792305b73eb2aa87ab6dd5d2e217b76d1be808ff1e31ef6fa226868c52f1e1459bd106cba4d8b02948faab8cf7d9d58faca65cbc657e5123ac235958b24717f6573d61c723e9e65b8e5c686c70f227511aa42876a570183af588b7e5135505307c55990e36bd5ee8f049a07b544ee483d1c1d682be80466dd1d71b65e70f241ea4e95f0dea3613f2619b099362f4e3772e44b7be2231e29acb961d0d7bbd604fe7cbf5be06a72e7ec19a25a5b47ac060a7c0ddceef421b2227cff13a0a1b3fd2df88a0c005e116127c2ce6e2294e7568078fae3a38469a0e9a44783cc72ca8f89b3118840f34f88c60206810ea7464b5bf33b30e4f385bfc29795371cd53d0da598a2192f09bc9dec88e8a552dbdc3daa92c148e6053192e4ea6012a5ef123537db3b369848f473ad33ea81dd88733f120c4ab5019f9e3b04ee79059c05bdc390df67bd312d01b55f95aa40303551c171e365662f05dc7b9e63e92b5ac72fd44cbd325881b6c930ad2735e912302b199b1c67b5d789f107bc58c2d98e225fe13f27f1403570e847fd2a98f94f95f8521a60dff09fcaf022303ad8895e06866f0e19b2f12a4fd599534b93fd959ed383d73123d213c29e2455f4a3ae42064a8261c121d38f00f6ae6b58ee984903c9654db5bd72484a70a38b06dcb7bdbb084986bc41d416c1e96cfe44719f25c2db3f2ced7fefcff45744060b7c66d7ced31ecbdebe31839d51a4178d8ceda6a3702192031a24cb3b7b4007ded0edd440d0c2658cd7e4203c2c35f78fef68dbeb0172cd19167868d83b7d6e8481b0790967d9a312a9fb333cfe5fab1b5aeedfe06479cffada79adb5f02e23232e26f033de5f2db7aefecb7fd45731fb222e995a3dd1a2de22df0078f80c50bf4b0dff44d237fb774fd082cf044817e4d989a2a4e587c154f38d419c585b2acae3756be6846dd51413a680608adeb827f044bc399a5dcae5047da21c4daf1bb5d9030cbd599a60f6cea557f909f082061c4357ae1f7fcb42d7ef0c88b931d06484a74d8c655e183882e6d79d9a4c86561e0adeebb721ce4b6c37a79cab021e293e50b8ea3042e4420303f9241fa63cd7987cdab28ffa233c834cd32369c70bf60ad01004329862e621090daf40dfaad21948487e86be43f35f5d49a000de4cf16a7b6c7dba329f4b9f2cc6b725b6c9b391ffd3c1da29e5b412148c7eec6fc74e282303c1aaf7307740a7a8cb68965b4a1ff19029582560d6d6cf7e2657d8a79a922ad763316ebfb7eaf4bbedf10bd18d1a4ddbca6871682f8e39ddc37e8d30f11fb14e4dbaa1584698a19c62bb4128232b283b83251e0b4daa6848a127d578e5fcda00546ed9ac08520f7b51284b4142789b4f29b8082a038beeab4b908a2346df0a9e21fdc5cf12db33a81365942d5fa61238b41bae7fd52173e816cdd3b004af3c906ae48afee222f180fc0d815a5363fab28423b76d4971d08cf08b0f243785e063d3d5cc3b0b410aa208f14b9f392d1237b52b86bd61b10a20bf5f81207bc17637a880e25f6137fbb7541948354b5f03ec127347026478b57f7057d078d39f9743860cb3fdd92233325caa7d639b5bacc3da08cf90d288deb817df9cabfa5a6aba4fa6111eeea48eea3ae1b14087d400ec764323fdb7f4baf66f8d5339a9f15d6d009677a160116db8ef2856896d775170bad5952f31fb620608f76c375ad4f271062e67d4737512bb93d6a360f939cf16d6d876a855a3f9fb67d7464b2ac5255375e50f56c120370dae06c24b66b6d3b75d64a8128e0e377f7b0335f0228b73a003f4d7214299880654283f74bf7721e245f5fc0154f30ed1ed96096523ef37417c30542cc6d84fb60d8e7ef44da5ec10b9781788ae24e20e9edc73a444d9cc9acd1952fde4275cc3d08f25243a2b29188e618ddd84d9b0f47b899538cc608f4547e40803e1e5933fa5a78602acba840298e9f2abd216c928c9575f106b5bf9042bf7c6e6975508e900f584df383958321f7048269f8de70db7ec0473c8af8a662a1f144393ebce27468db8df66e4b4c178232bb83e090579cb8ae84326c5663237c892205a1f784836d9a273a1aa62c64458c2222b403f4fe6dc8fe30c811ec06c3d69dff9a6f6949e56ba06412dfa664402e633669563e20ba3dea362ccc032fb2cd5e6f1bddfecb7b8f885cfc136b8c85f95126843e4ac5b0ed00ba1ed10e5060676879093f3ee57c21bb0064e0d2aec3553c29b11800053dbba2b2e1466e05d09ad1907a9d588557497635df944a4192ac3479f562ef44a10ec00b345f3a7a1b4bacef885dbc37294038e5ec50ebfbadcbaecd7841ad93d4d4e0fe5f0a684a240e169f78049228d975c79eb201a5f9ccea94a1f21fb8989e50e45549518cb1b0d239e7672553227f6ba8ae9efe60d02eba0708f203b75612c3a19220b9856b3b050b500ee1a67c9e52487d021bb5037c00918227359e94e1de64296b7d686079b5d02ae4168a98644676602b1387d042d9aaeaf9d154cbefb844a28ded0d6ec87ea6fc97bb26255768a627beea34b87637b2cf520863b572fe666e65d386c5901800dd272b6d0cf973e25b5c5810689ac139b69c02bd5ab7251672bd001589dc8e0b9452f894121024cdfdd8714f13928713ebed2daf6fdd63f7c74113e2cf691db9614f9a192ca7ee7a93f7c704032ddaf8b5646cc376a33d1570505e58a69c6f3448581105ba07671795f0c0ba88ccaf92d73b46d40de0398456f417aabf8a059125ffe43647e777d10b575161aaf115570812065abb097984e8575d2fc9bc88da0ab29d295f6f00a8c5c96957e5f0e537b9814b6ca9ec80ae32d7a6d33b8f5421ee7d427e578977bc7b5c29881ec4812d6239c4eaaa4b31ef1879fffceeeed8de3e22cef7a83ac44f2fe9d1c2613a73e913e6f712b90364927b50edd0c43118733b2a58a6410e9bb9246a9f40904f6e49834eeed20741ee5e767a127449a074f53c1494655888150ed1777e0a32c7f4153820dde82daccea473434a02a690f0696ec45fcc0e098028dc707d32b30ba451060582b8c2eb1203d27172096ef7247f1c33bb1ed4c22cb7c373be236ae689effffb1309f8270da6df34661e7589f3ae96a8f0506d7a9686296deccaf34f1cea8be34550b53b74ddce0cd4b889b80313a288fa07cceccaa06e40f3988f5f2015163e75f7e67ec52b8eb608f94abf7a833eb2210ee3be02b5973224128ecfcad242fc4e51dc8938cfd2452b8b520f9ea6b508c5c0da7508a13a925c49f52495b9234a601a9ab0e06859e9e96706b7d758879547cfdc34393b8248b2e6eadaa77840f26470cc8edcf04ea3cc70b1dd4067d8fd7bb8b5cf466de41bcfa51a5c97ebad6cac3bcef04c1bd304933494d0841be61739a4476534878d29a2a1aa926dbba8bf782dcebf733d956fbcdf7242da1274ea9b7e6fa91fb88285c35e0517b854776d368902972a0cce6ffeaecfa3e97e7b8c7ced054856838bcaeae9cc766f831a0fe26ff4641f1a67e026ca72ea105f0d444dbe0d2779ade275214259d4a39c697862e308fe01306a87d12754a97e25e2b91e53c6b71c02a0c5b4cc6a2068a790a18257721c57565985402041f93fced48bb3d4fd6adc2688aa1646249407eb40c2b554b3a33b73059013cd48056108645f81f8a04e8fc6bf347e5d9de1b8483a8503db140c7798d4fb3240becef75083bf03f822d457862492ec0875acb1faa4805ca47e5dc7057a66ace656688a1ade4cf66e044938f72b24a8990892045de82883f916c3fea91a314d1355ea40df85cc8c9144f3986699df62d62d8e049a01471b896b23f822d399a825c047248e7d64e32a80583f2e0b201c4417fcf55ca63009b33c53b9ba143cce9cc1a8b4bf4ec65493554d02bf64917003ef094906f06b3b8a1af1106fed03e38a16fd99ee99baaefc6bd1c451ac2930bbe99bf9cd18c4317eb1d3e20838f388287fde4d9a807dac6d1bd2e480209337e0096733bc9d09cceb390c9e240efd0bc51ed52e4262dface06832b626000d3041b91af93634f2f3088e429ba9ee15f2459b300965abe42419944d354ff5f51089caa5c5c26e439283a9a9a8186030fd9b2e15fd81feea32ae3da9e6b934f8dea80798c073ceaab5fe31e8b5e0e4b469cbb7527aa8d613d0bb943875231bc19b41ec771e3d8ceeb3abfabb83b166547a007ff400d982381f63e6c83bb2ad88ce3bdfc39d5b17b31e21b2c72b1e227e381b85cab8929895209853f3ddb28d9b2a6ebf2c858aca5308a9eb1f289e5ea09d0865ad9881ac204f42eae996d05dda6e89919136a935de6a83416f4d232ea39536cc0dc0794ad58090e6d14e21cb9ecba335956df9c8882434982679742d3a1cdea3a12998231094c763591c3562ccd8d38fed70de5c9fd9ec93c0fa3ac1ab3d260308f0c2c84884d660c48d2e600c2966b208f2ad9479b794791813f901a9e3c616be8ad803ea209dd994c619186c202a638de5c2397676d19214b70610a28001bb275358928000101055eadd62744858d1307ebcf821684fe728897e88dc76f61195f703613df60a2ffc45fb304fe06a32daa5a5feea048ba04e6b58281b214792cd9c7902113556f22c9526e6e3ec608affbf18185660d572328ec3c7462bd75c2a895994a95ed1ca9063e956dcfdb8512886a318054662a72e3dcd1fb468dfa19bf89a050abf6063573ed7fe4b1812e59a647b84456d09a9cb6c9e40ff3bf7fe097d9143ccf74637aee346733667c043c93af629f4ec859554688999f3f7c4628bc12761a12fd7bb6dacd062ae560f4bcccc9523f2a6943ec0cae60762e7bb8092975fb9a309017c5b26ee67c6c7768b68d2609cf37e83290a8382e1403a807198244d622e5062fbf32f8c218d9c163f233b56622aa147a20f31bc8bf5b7bded8ae26bb9680d55ec7e3d19a280d2183b130604a68aca8b9e62cddb9db3371efa1a5e848a11f74607cbe9f6bb5a9c8b141a7bacdb627ba1f81fa3cc8108c4c697f71565c738819baea5dedbd338dc5aade56838b1247ee26344c602e3bdf23eac7fd49649fd838e38e8258da39a443b6149b1024845f4153766bed1adc571950ac65928ed297fdd59c3807587d43406509e0ef3b8852bddd38fc8ea59220e6a11dcbc18cc93bdde5640ab3e7ddd32ccc33327ea85a19c57efdd8e1075e1b54847a69ae386d94350f71f2cf2c2451d6cc92d46448f89d3787f918196e675013afa8e7e556158928185901ea0a98ee499ada1749ae87e3afe45b174810175cad184fbd781b68d4efaac73610b9e27d7be25a23b2d6e1ec69aee963625d6ba6d4e106d25be4af5aa05c3b07a20bcb5ceaa4d9271b4c6bddfbf9752a040193a8d550064a998e974f509279694d3fe21fde91101d29810f4c82cbca55cc5748eee7301051a8f0aed67eae60f0d2d40e9366b3ce64659dc69f2cc65b0f5bff2ad820c9571742f52b484630532623eac135cd57dc29108333f9d036a79810c8f59488190eb1b63af671b41222fd8bd929c8be5800bff51dbf202df1bdc6d66aaba86a759b52ca0284301acd3ac7b88b5b9c94748579403619d2ba93c9c4ad42247b6a05f51b926f56db528786cf21b6ab778d8e08fc8aea3c3426e1694a812f598a76589e8e2711e5f9929ba459a5aa03e88e38dccfef898def651853c3ee284f9b41f1e524ccdb0aed98488ecfb0149670ebda1b8039059811f32bad5e5d88488d53a36217aeec0e04ea5358ffc3f4203c524397a0ee70a6d7418ed97fa32c4bd5c4f8bcb1065f2b28340b54e661197248591a328f8ad542e1a6b702d6746e29d1f78f3fa669022ab649538980a29c26178fb828eeec06490a3bd03d1a5cd79e97a5d1b5571808a8eac3defe110d4eeefad4c429ba13b110488de7b8d0959036028217e5975c89b184e195e7fac319c455e61ebc81971bd8783ed0f871366f890a8157fbfea46634e5e4d709acafe5a7ec60ac7e80315497f9b7dd806cc29719668cc252816192e71c491db86f48784e37c4a879b45f62834dcc5524a26efc81f4715ffdf800e1b77a37e1bee2c26682296aedb627826630256aa056b5d6f266f3ba86058ae45441f81287486db413fba83ec60bcca68bd783ffd69bb842d9c195ba312ad09c6c7c482da9bef7ede2dc76542a489c95c77e80877ab695781097c2508238808ff3330c23870aa69c6153a6e09837bdd801f39ed5a7d4a9666d67533bdc47b5547a09fb0232ae3b0862a45432943cc51ac32c5c660e264f4f1d6b3a74319b5e55867816288eb4c240f9ca41d700f0a65442e4a00d718343177145dfddb0f96d8e151f1c0d3ae99d2252c3dbd1af6e2ef1d456505f7ba5f716d53ce34934d7d02c456555f651504eddd1870a237134580b2aed0755a9255b73490115527739f0e2e535fb73a0c6ba2bbaf03f798b6937ad79a9bfa4dd5e8718f94c0430657b3fc73baf1403f615ff185edfe585cf39c29514d896c976aee982afb342913621f71ff95ab809ca9eda0abda80df6642eb89fef8c8b2ab435fe708a650fd0c38d906b1456e73646ca69be64d033cb589364431e21506c7cc6f2d34d3ab183109ed79c13dc5b06dbd79dfb5ef9cfdc2077f14da6262fe44cb33680a3983d46d5c7cf92013379c77a3b8fb37244e2e07da6b6512186cd881c1d4ce80d1454670e990f979545d60462ac9b273fed8d95a5686c94c886bb20df217de25b982f6ae2f0c2046a061b1a6dd7a161cf4fe5ab9a9c66909a5a2bb5f9d0f16c541cb5be04997f3da01291cf965a22f6adc4f3be1e175c322b456234f3affe393ad608cebea25cdfc983067e35d8f7038c101997c756c87d22954405c964c5ac2d3159a44ad191d710c1dd623bfb07eb27027de11fe71ad83262db0a3ad9824c5c53effe4bc153a46b5120e2a018da8d9a4bb8adb3907027152903fc10f9b1189501444a095de187e49e6a129bd0c407bf31a5654ecdaf5b1be52a72dff7c921ebf07ed9ebada48ba1ac7ced5bed395afab4bc0e48ecb69a152e231d22f17f8e29b79f2138cf94a62d7dbb562fffba833d5c6de153add8bc04e1e37ed1f6da2e082318f11aa88caeb159ac0449aa945d10628045bdc2f5945f47a873fc02a2b8e7082c3a93f23fd6c53f5493100185bfa149b51c55163fbfd428bd65baa8145b45f041d055781bcbe7f930ef768c558dd3659a3112a3be8655c2d15cfce50f14ac4369fab91df89993e3b9b83e398554832b030760ee8b4f0385fba261fceaea6e061e8b1cb4af290507743dbeea93ad5e7317f96b142c417f12ace44882c1f1026e0bcba274bad684e363ab510c74dccf7c56c96c91a47a5d47b7bdf62ef931ec8733f7c0b383c6416db4d916d8f22f57facd8d428152bd1c56a14e928ac1b9bdb6b6ba8c442377597eba6281ef97d31aac37d30dd1aac7cf1ae4bfa1ca263ebe30dd525c96de2e36fdfa38dda2c09510356d33ad73eec954a73b742de8684304b8afb7528f8a6a47a7cc16a279e316814ef483a6ca71499f0c138009fa231b67161b7d00cfa2f28aeb9eade3aaf7975e0a724e8b15b3c7ce1051f85918b99ac9c537313ae5912d1d7df49b25114b4491ba21dee073e0572a5a47416f785e7b21c34e248fd2aec6212debeaad00e392574c38271c75a196cf6cac05e8e02403173e2a9d00c93d67932c9d3965592c390134d890e81dbf931d2e683e979924612af2cf68106c3d583583e7258a4209e407f6e6d5e4e82e76024da5a55fb458812fd5d598a11dcd000d7ca3e975709afd8023f4c7525d65355626e1d93eb89a5c925dd75fe37b165007c4f66989db2e3a21c22521353259d6a4b1b4d26ce0f2664f7e61947fcb6bdf04880b5d5f5336f89bd8be90ba1a10b89f3b4aafc50f516e7be1d8d183101115526f8ba0816f893bbda43764f1900919e05f88fe55a6a7e6f64fc10f3e8a430b778843defd05b961bf42c9891e653fd530e0806bc67ac2e94eb1d5d228e9c5dbbb48d99e8832ece8b4a6b37081738252f961a8f0e237d44d4efc503ba3dc20c19ee776dc1664502a075c809c1973fe50b0d308cfd9f9bbfc27b8f4c6784528d5bbebe37963e4ccce56611ebed8392baf7318d84a68cae6e6c440368aec05db98b2e3f44b85e6bf4841f3bcfdf88e6066443c44347b21fd83d0d92a30ee0fdb9043b4051228d1972acd851831eed5e32f8f90e650cffdf34b0f23bd7c4365c22b4813e26ad4ad46793bf73314c79171b01468cfccbbbdc93b9527a8658b5055e27852f905338a233fc841f3252cbbbf054e294f9d02e5d2ad9a466c50cab5d7733ae3d230bcfe55344f39573f6986ae0c7ef52b3d59941cf94447164de77440c520256e24eaf68226b13f1141cc4e1ca2149a213b31db19f999cc201696281e5cd20dc093de36f2e77cce30a983f0e17838c24c508c180d1b9594ed12632f03a46433353411e32b80338512c209c0cc4a7c55367194659eac8ab6565d47ed6edd0a7fe10cf8831969e152680c0d4dd01ec1b453889f6cbb9771ed37b0e01711364ada1613e940eeeab14c9306a0340ed793f031fb5fdbc2bd9cab2dcc6a9968f28c79b40a5938fa5f238a8ca14db55ad90dc8d74e9bdc3edb8a93265b0f73e58310a8bbb2e4bdd66e9c97105a3a94b968e221efd33715c00ce27e272e5333779bb2a01255f2468a64399f10a8672c6a272078cbde8266d04f081d7274e6f5b2b52f725f060bcb9f249f723d05bc84d8f49a3f8c888db691a457144ab4cf80bd33ab2058d0353164ce200ea01d8c1f77af6f75f01ec98e8f794b78dc00ea1c4edd8b488214eea50f8c05023283bb12277612157ee8bdd53eb76afbe5a36dfb2f320876288a455b8c76205876aa861eb1b2d26fa7612b91f2311380bc30701b9d081e7394a3d59ed9c8fc063ceba7a116d0a5797acbc883b16881f216bf80aa5ea080d987d4a05bca301d5ad4453485dd442e82dfc307c0a08a0c3a02eb86a01bc422b659aa78db1f8369dd27ac1aa107f96013a91c3f6a06032e5f3a401cdbd214cf77e9355303d45af3c03ae1b640f4b1b33f8edc0ec9bc59741a72128ecefd8f3bd0bba5434d754d8d5249e867d38304cd9b8a4e7b0afed5654fdd69ad66d00f2d5418fd1e805c2c341fde95211d717907004441d90611a1b730a2f8e2dc9b7c90833cf3299fcf24f8d5e30937918d56082840a37b5dbc1c744638ca5ea670af9b61798e735bac9082b4ea41471fc2a0ddc569f0c8aacf4ee0d9b075a7afd6cfd089ffdb8c8b0ca200b0c181695e0590968e0488145d13c815e8a50e15b04225eac488786f3afa79513793a4d9cd6f52520142546b0a5a34d551e767b5b7d93b39dbf904fa522d965a98e92cbf868cf3e825d60956f7101a7877c1c6e1a58e26a95845c66addd40c104e70f9ecf542124eadddf5814206a9a14d44ecf1a8557434a230fa2f1f4f0306c01f2ab1a709f331b76f8e9c0ea4851f60d50cc436af994e9c09aa96bd8189b54c221cee4724f5c95ebec787644f824b5d11c5bf494d0c046ace143de538195ce425436b797749b10254c2383cc4ce471b87aeaecc37faaae3da7c9b18f5021ab9e8f98b624b02f1f8d297a5dea3af1fb66cfd76b46e99d18d05f39297a09badeefc5e7c26fa55e76b3d71f7bf1a2eab58277bf28f6a9055f08c23f8e5fd2bd5e43aff11afe8fcf80f4ba94df9310b9bf51e15b7c2f9cfb7bcbe965067b61b85729edb583bdf05c6f63f722887a4f4869102c67b00f16e57fa3f96c21f840b1d9d470424bc28d311b14d2299916dfc631b9ef9630ac0b306061115fb6cea116f5de04aba98941d8fea356e89a1e6985f9a0f30fd136279499ba6e4163e1cd5ee06f420ccb61d3a1810c563ecca1207cd3e2f1c0f72ce6006d1fc43cef0b091b08a5ad87bba649a9840414772a5820c47f900e0e5cebcfd48c854afd345c7d912053f5e0d51fa994b12a6bea7a279a65fd462ed6dd97f959334c1dc5a2a88367b94529464a42d400927b58e4f8afe5ef945e6ae646c628a4427abebb64f3d10cddbcce08bcc1e8c65ceafde959a05ea2dc3b428cc8310563478e2d1c33424cc15891630bc78e103b31763af566542f97ea2599df658e2d103b626ce15891630bc4881853182b624cc198916327c78e80de10d58b74bd39ae7703f4c28c248ae14b46ae89e85fb599f6fcf2e59198f8d68ab1b96fbb21343b598a82f488dff30fdf9e53dcc5b59aa7ffe5b6ad48fcf88630f19d6422115a4ded81527c51395711902609b759b96d9b71479f49a473b2d715eb78d60b8ea166a763a7737289299e5be4951b7137d437a4c565bd7bc4a51ceb8ce3369c43ab760edbd987f920638d79b08643aff3f796135385eb362912ee6df04a3c03bd6e73bbef6d737169baf429cce6c33f083c53e4ba4d83831be0b9ad75bf6d81c691fa42b641c0c1cdaafb5b792abb95aeb80d8169f156fb1c29b8b1f3cf99c5a0d470930ec0ed2140a669cd2e356ffbed76eece81e9b5593abeb1a2727574ab1f7b45646cd335d221a2e3c74c753c71f44c69b8e9cebaefb434c6c5308cc95017221147ccf1166e651d251beb5c0b81622ccb586664d2f504f7dacce39a0dd1ed9f527230463c00c42d8a6ee43642967e5f103aaa779a536f22719beb90b16a62e53f0fd816c2d06ba8ffff072fcbbfb2f3578d9b8270c8f88ecf1a9db1e3d0ec82d842c98f713b67991c1012f1a484d8fe22d98b808c93a618377cff07b921f6afa25e871361fe49e82c5cc1568afc502ee3a428e76ee7ce7397db0571507d18e46662c99a992d10db476e98d1a595b1890d7eebd039d1147479b58609816d74d36185f7c69d25e516662f327931505d5de136763066a0941bbac9487d343d78c6691b67da45b799d7511b5b8cdd3bc9f8a81d18dac1f851db655f1402a4f6ca3755f004c509f4a8777c8358eadc0b94e637f11eb85b4f2a53e576e0f75579771b742741d4c27ceedb6ec0fd46eb2a372b85abc7ea1565cab736e2a368c3e0980fbbafab21d57b43951b5eb764ddc42ef1c02af4556e8ec0d0024df346a1daf58f901c976183c9df2a4bd85439e6a319bf3ee4e3b29102e7dbd0a27fe446635b420ae42fe7abdc78c6b30b88f769c7aa2ab772aca560b6c5aeb9859b864252b577b7188eda75344c75be8d1c12af1b200ebb65955b72af06cc3b8b58083dbccacd4a5727217c11e55e4ff9ea6229c49a9483a93b73161c96ba49a814e3e0cf6c7513d70df589b9b991aa6def5b44ef454d5274b433eaba3cf0881cafa00dbe491323286960f5000b4e73aec8572a7c7880825186ad39257989b4571dcb8450f23c8e808fbf43adfbb93806680d1575cc8c11a342d75cd0ed7a84909c2c16b852b9bd0c8184af7b8afa285a12051fb001da35e31b9829faa4e7ebbe0017d4932d4d7dae9226d07da60148adb19e13b5ab3916beec6bec33453dfe3ed50099a2aa2ef2be900c005e2b4eb3ef1ce38e6bdf284bbdc318384ecfe7634e2f1f45bfb1c5363d61f7e2e95d1fb4361320200073633ec38990e637178db275cdeccc5247658d2577ac4a990174e380993d7d8598484b74ec70084c94798125407f8c38f642e755737d712dd135eaeff30c12824de61275c8b58c18a855d9bb95df2a76cb23beccc18deec112e2a162b24552ff23d47d7743797e77009f47d0f9cc33a3569dedca84a5a2d40bf57e43dcd9e37d5b0d15cd8e54237c759252dd3255324e44686eb87b592c5912105f45d0a1fb4aa07b9a05c1a8127147dfcfc202ed89aad27bc4553f5d0a67a1b7516cbb621b2a1e7b40e808916daae8c68ae9f242aa1e2d7c7323e8d4e64a247a17df1e045220dcd030bd87806b2f326533d36281162b2a909d82a3eb3ddb9fcbf44886ab2f4e1e22e584e4bf5c2787ef7c84f180156bbb284c53811d075df6da2b3091347b6803843bac8846f0463b618ae67732457ef221c1bc8a78282ac3ab326fa0d4f54808116ee715f5bb1a9e310887074c3912f3b552c2058ac19013d2cffeff298565aac0ad4250cd9ec650c556b39744ac4d46633ce853a1075aa8a0ae0fe86e213cc652f88d1b0b5e64636564772f8369591f2efccaf283feb7386a3be542b944eacc557436a507530794471a0a213763b2334a842c2625c6c5a3639869b1c0a49bf83b7750b1202b36a114b8ffecc16ae632a832e4571777b479c88e6b6cbdd1a5792701e37d3f50a91ab10da7256ea695b27ee74766889a556e3b4754ed8df883091361b5788c76d6da2a51e3bb2d71e447c8d03a8e8a1cd73ee70e2f75652f314af0ae98c5abcb05fb57ade3b17259e65bca107805045814262c510da82538b336fdf01d1668891012de9566584ecc00c8183fab4e84583d795ceb7edfa01215c39c7e45cd9937e475725d172e22b31ecc76f2ae655cb6bdfd2426da81d1a947b39652dd6c883564813511a8fe2f4fd51afa9130053657b57eda9eb2b612994862a1385ee5fc287940f49c1d1f0d5b5d1acbf4a1ed7bb9ba33a48c239e8b9afa6f9e2c4623949cf9ddac169fc384b109604b2b64d25dc8a90319cb1369c7d72a87c61e7cc97c13901c4028025cb623e3d72580b26ca78de40982d25bf210ed1949e0f65af17992a757ac1c85f7a346b843d954715199c4546ce6050aa5d98820124fb5b847a88cef69617b9db1de2cd27cf106e2a6a5fc462b6eef2ebb7b3538f373e0a0fcf9ff4080bb6f028233a61e0970609355113070d008c09f20faa3a7f497460ab72030cf39c1113e2012e8ea48e68947b92e551f8592dfa05b47c4becb84f0ab8a43efac1d98b4e985a85594876a5cee620a3e919767fe05b51624a39752654941c1c29889d5931791c0b0c7080d1d8bd38bfa5578cd31285898707c1dc46889e139607c946fa8bb8f68cb98938923b08318de5dc506d34efc95f708a92ca387ea3552f024d6ef5bf466721dfbfc64feef61104902b74d9636f0efc29e885ce0874bee67d1467915c73475fca789c4824b1a5f615da81935e557e192e9971cd0e6fbf2c4985f636e9a3aa93eb4306b544666c93e41475d376fcaddbc9127e5f250ffd85e18d7dfaa82e3973ca42b0f1cc8d924db3ab2886dae2e56751af5a8256d08b702a4e288d9079eeba1308e3289ca48e1174c6027e119d7a4fc6d691bbe1c238f59d00cae209cd94a293302afb164aaddf5ee17429054ac29143fb0e2c545380e2ecde1c126293cc176da38ae50a9da4e84d9e72ecc09f82c6b9745afe949da072667e4f77e026ae83c88e9891b77554d5c28476b2e1ab34e523ae1a1d221a66607db951cb73b97f614ef8519c81d1bcaed3fd60a8f26d9f01b2c81aed25906219ca2e9fe0337083aa94e2947df097361a2d54a1a9c0f85e2ea9fb174d8223eb281e4dfaa0a1d76d63d3066ceb827f092547cb564a1a542913c97762056659ee98b457f23bc66fe1686da927c9858444bec9279e7e0e1fdad92f31739eeb90dc0a278a5399625aa4f2693680566d1699ae9db2bf8e4028c527cd61bac84de1f7cf86a5dfd807c2ee7b0ab8507b6454a1ce1dd75ea64a6d1296e5cc50da53258442afa8fd60c576890856d040d24b8bc791f8681b86303508111c8c3b68ee1e654df638bc12b6530d0f40619ced8b7928c531e5a7fdc388188a55f629d9c35d565c6fe8b22ad0178c1e5f913bfaa8f148bef31bc131e6fa07a797a91267cb8b2da287620179f576cc7ce8a35aa55d8558c347f0371b06c8cab2c02cac8f6163858fcd8b609e8873225d0182ef0c8e3c90059dfc8b1510086af699f8ff1d1b5a2873447f20bbb2b7c976a331ca685a9fee63f479cc1434224fead5a24797e467061e2ac786d8387233e2198dd4526c38c2adc080a20d607bf4b0b8a61a51f8e716b4f41d00f7b7f75b6214d77cff46867cd58e7933deb89d5ddc7eccc2a8eb26b3e0cc8cff5398d06000e079a5dcc3f43376d260aa2784d3e487c11e36715a1251e89df3b62295ac394e081f98fa509ce666cdafef4ea66766d4597b108c2930ee2b923a9b8b30c3efd9e3c0b0783ef0a09464a1a09e9da0dcdf828088bcf941f565dec2e92116b8464a347ef385f2554262f403deb0185540e1eb356453408dc21f3b6234151578daca2ddab3d061e9558397d44fb032132c383a3cc1d5d45b92c86fbdaa79222a8950f672d90f1660b1621823526cb15879b0b2b7307f89dd117885d66a6c6a48ef3dd1057ee2044f4230bc89dd14598f718821bc687cf01405f3aa368b01fb76393e29bc23927f1b8f695f29ccb2269ee59f976ff202f31afe6498415394643d70569c7b5d135fd87013d0b17823a56cc46c47117e9b8fffc6a3d072069a68e1091167b30a7554f8e3408089b40f6694578a4b25df236ade565134d6e14bc69d1d1e7ee50ccc656114318f698c9622e3f965fa3652e89d52795e6827731b1a196c0363d489617eec9bd244290b1696c1d07f3898a75fe6746473b22e530bc9e00bfd03a35916995658a67dccbeef4d8e87eff43fda983e6debb60fd7660b811d1ed9cd13a73635c0ce8a5b5c1e87bf08e9372910302a384b3cbf6274c48ac04ffe617c5c541822c311a49642872992e87608868570e7263b590d334142e7dffa94c24bf75b804ef851631294fe65b9eb3b2691442db4229cec2bf546278f044ae470eb400ed18fe2352abc0601f190fc5b47c8f63f8ecf9a646ae27beda4d6b157a655d8456bbe7f9c051dd05ff8e2bd2bae493aaedbe208887749ebedb66ab6d04c8d9d3f6e249489c9d25338b6936789dd189298893c4daa62befd511aef7564b6e24744458495402f58bb833853e82e2b9ea7425f8ae58353de91832c735b2cf2c58e4532d46383d9d3aad6ca0040b5bf3e00c4a7f8c1090d9d5bc6cbbbd1f7ffe3849f3c2510954cc2195a1c944eec7e8893b50f6857bf2b6503b27acefe8b3c1412abb3cec5fbebcc5376a2579e7e057030f94ce65470fd23b7052db29014e840b11cec2296893282cd70988becfaefc6466464e07275b6a984735a2724f50a4b1f07f8c6d40000fdad4e359882f604c1e04972eff183443941b5a055ee843dc4904c738b0566fcd724e2c22c3f0462dd23c27affce65bb3cf2b65e5f4e4d6ac1f65dd6bd2c00120e81d4c1d8f60c634d85bec8f356e16a38c0998aa0f7f1b036614e33f3af120848e6e14dcaf2d6a5dad306fbb0ac5c7507b274bd8a276da9a3da290848f541daf330c491a546ecd9ac062300b071c54606cd0be359b44bf116ef7727bc716b5b38c84efa0dc182f0ef88ef674c529cbf70e2e5be22b57f3175875e3a8551848ee0b26662e66363084c2163ef032bd9261fa96a2be14f5be41ae8f03d914eea5ffad597e33ccbce81b0f81d53bc98b871053ecb8d8eb293f3b27fad743da04d4e27cac35836c356ef3aa9bbf641e4e6f4dd3e6890b0962615b516c165d7d53db60dcd626dab91ab6689ab0d9b1355664d6def50c5df31b590dde7a1fa137827933b3b539f0ad3b3f0f79b2cfd66bf22fedf7000add9e1f50c3d00d5b68b8dde43e24d2fe26ed6590450347775e3c28b0e6ce2dcb2a7c6710bc17f7a56a141339a3ac8336ae4996cb8b5225b933f550de36c67343f27e0af3b4af15838ab010017e404c5a5e785154a64c46aa01afe618810d49f7b7d09574b20618c05b1021d5021e4d32eed685a541a2af199c2f0abc772a7151a03ad59ad16cb7d096cc10b3013c03035a83d2875720988d750e5160a5643bbc1c21c5e585632fc74e0033c7f3738044d3104854e18765447bb35ad8f05544f909e0605345159074bde0a14e76827d22e64f971b9e1dd16fd950fd4d2a2e9745db768d2424222111112272cb88082f0862087bd4b1181483b8cd6a5a5efd60549a5a178b70d0b26d9a961f46755d16960eb0c4dcd29595e7c04ac7a52a7b880aa87c6ae36e8a57a8ac5eb56fcccee26291cc8a8bc11988c19cfeb2085de31d75deb1064098f63b02fb68c538f4ba48e32fa8a7f8c11482aa0e77353015bfdd6ff75a203c431735017ebc1faffcf54802fc7664d7fcf1761d3d551bdf09f0eb651cce3f5ec689c14ccb5aef7e3a62f0b7a37ba0bf5d10b9e2b0e5050067429996b3d851f6387f28f02194dd9c7b6437a33add7b00b0578b97cbe5224b69b96d635996db46922ebe21b043add67d7825b0811e2200011e767880031a20cbc9663194023bfb649ecc9381b2cf375b5959a962adb4dc28b79c2a2553696229ecb5f996ed12bbfa631bc5ae7e950d825dfd29bbc5aefe970dc3277ac5ae566157f31a39ec4efe4ca5f1dd07595ad247c68d53a89119c9de5b84d639e723b5a0db63b1989bdef6cce7768c096c0dfc0576105fee91d7580be2525e58643331643a4d6b77bc9e8c36431505f142333f663142662a0dfa44b9674174c875d1218d0ee9139526d56f50ee184aad568bedf047a88645455927bbba17969cb293556a2ab6ac83977979879a7b2b2b2a44c020ac685ddf079ff4525e58603e7ce283952b5a41159546f360c35a2d2a0daddb782bb803964501bb154cc06e5d8180dd2a3a80010a8023c06e0173b7fdeae57e751de337145c2d7763f32dd49ef0a769f96d763824140243a2ece67a425fe87371a6a55029540aadfa88888b951796172a0a5c04f135b68d17d921efb26d7817dbf52f7be653f68baf6dfd2a1bc6db36f9b21df3b10df313d83720a476fd18825a606be083d8a10c0676d813aa3c36e47981c34c6a01f1498b0f62639991e7fd4ed3425c1b5614dc306016b2a21ab60c19d9ee59f03d59153260ade6baffab558f812161e817333146549ab2b7802f3998cf072e5ab480511acdb20514b6eee20293f22d001b377c6e07c00600d420638f6f0a200037d8b0010035c8d83f9a214164e0c6e607179934646b2fa3cea8bc8c4dc61f9549c7dde2e5e2bf06ac62934fc3c3ae4dd41ae3975f62ae4ec8bcbcbcf494ef00d860e373fb06d88b7fd8fe51167b995fb25f037f5df665f0f7d9df9cfde7e3f75e4803b21d6c268659bec8902123c66cc37cf804a66fb952cb58a188612b8b5006ad3e3f6ec3784aa1d3e737519fdf6401d6fdfcfce68e199f0fd8972b7eb4ba5687e6d6631b2788a2504717eac21d35dc85bbd166865a8118b1cd0004c868701a336868e12d204b76f3d340811dd393d3b4dc0d34a83368a0a109799b5ac3f5e5d7685069f4bb1ea5a19911e25ad93d3f3cec871f7e7fed5f1bf6aaa9ac32f861c35e3036305ee010170662979615ad60ad861a40fe45877afeda5649d9ad2bb29bff65b78a5876eb07c6add86a660d359868014b5a2d2076c126b3fdea1440516b905ffe893a5392904c6093a8ac69b2ecf77a7e00a834b5de0afe7ed8aea6e50780fad9fd99b4dcd9069ad4c68c74dc58b65b5664377f6cb7829cb7804e04e0c4c44609cd1d1199c96236b80d550a65e30462c3c6c61d86fb7e17e9e2660f211fbfece6ffcd581c6f0e935802c5b277a5ebd93c31bf2c1f4747b18b93d764bbf98cdeefd863759977e41e0b64333262c08089e932371c0d355ebcb8703941c9b5a0a1b2c232a3a5564363abd93a3fe6f83429333262c8f41818182f5e5cb8b4c8a19cf030d81302350d98756a581416852191da130e69da90c62b2aee088ddad39aa6a6a675b56ddbba5c2e57c80cb586a6060d346c2ca3692ec661d66630a8866586ed8623f9d72c3915695a50d3f289e84454b306a194adb255b6ca56d92afb8d888c466a0814029d279aaaa3119113455395e453947b569f572c168bc51a09aaa1b24bba222123ca3efbd9e71f7dda75dd1315b31a1a1a352c336a6c34b5d597aa2a69656fb80d129cf91a5389e7ffa769419a964d4c5e65b3456c3068b73ff375c5bbce39e731d210698ad1290b78253fcd3ea5ec13109fe49fd918a7bca72a3af0c70f994c2693c94e442adf89482b2132e3e15149a4952da52abbb42be9d8a11ad7af562ffb9a212420c90af5493b4de3019926cbae56ffbaf95f35766948d3f2d3b04db3e483957cb0c71857823dc6f193ee56db3319eef5932e08aa0f4f510dcb0cdbaed9a12e085cb46a9a1dc6f8aad26893a6a626954aa552aa19680dcd38b369667ec6386332d382724283a3eb2ff79b7bf9278fa25440df58f333342a3456562c884e27d372c957b35556645afeda5601b34bd29176baecfeb0afea47cfff238b2c4ad364d975157a4b256785483a2d3253674aad4d155005ecf951e6eea9f994dd43f3b54da2b14f3e359ca4936999e6479f5e3faf3ef6dfd1878997ddfc33b64947a6e5f7d1cfa7d925df2e01adf0936e89f055f4cbd41ae6971f53674afe9adcac79db26e9b29b7f66ef4c999a2f8146a3cf5fb35953d378ec0a049a9166f5adb08b44feccc632e3c63d44e1a25ae3849f7033bbe159f20121f697a49b117b2375a634495f8d9047c8a7933aefb29b7fb7d2c5c8069862e5c166dc431cc551ccf956f907ac786a0209bdeceb0ff4e8ebe39b9c02a3ef06281ff1d756d45f1bd73e42604392e43f685fd74dd480045dbf29720a685ff721933be8410fa288620843d023146798b53892a5393ad15425ad6cc9047242a5542b56ab7dc13e25e2d1bd425c6d8bf53b44f3784621a2952a853a816ceeca3f1ecd03319578ac0d79647025a931598b57fe8269299bb1c2909fbc99c8aa9b44dd4794ea23dfc8a7eaa98ef1c90d46521dfbb0cbebd4a16680ea944cd754e7d3443c28d82c92e6d9aa6b0904b5572dd74b64c766242ec949cd96aea4b0165de6c93ed907140c121a22d23e5d16d2e9743a5d1e2202068b5e21aeb6e5f3f97c3e966e2572d3112cd2ed32ef17243444f4fbfd7ebf1f8fc7e3f1789ac8e7f3f97cbeec937d1928ff72d02a180c0683aadfeff7fba5783c1e8f873af97c3e9f0fc40457825f18140a11017f3f1e8fc7e3f1345190a828ebf22ef33e356aa833218fb6b60d2b72d1be5df952092421d2e7ff0dfba16c98cfd07a025e122c0fbb0ead43ebd03a944bff9a3ead0e5dc96e754c76bb6b79b5135b0d5623647582869465c77e65f357d9239fb25ddb16f9da9e3dca969dc04c5e25242ede83c3b75c37c4cc88d9ae87840e1b168415c17e6cd86c08dd8847b661bdf3f3cf8c98ed5024d25e7a9e0f049eff4020afabffa6783d7f8c973c13ba56d7ea3e87cde2b07fb5f2695cab6b75be56e76b75be9ebf0695a6d6dbddef460de5ce0098316da8495b9d7667a3d5b5ba1b4e4c6cb081dba04aa16c3881d860437b636319b3c562bdad0fd0ef39d0f2c070001b42dbf3016c0c3b14b4c0892cf8189a420422fa1b7fa7d019a469f93c9f80dd3348e34e227d06b3fb047c3bc180ddfcb93c8934ee2cd2b460bea1bb130cf8869e278a368153509ea6a12808f00d45cf118aa2a84f0d6e02c3f48c7e908fc6e56cb69cc661191ed14479f2cd66b3a1e8383245726c8d3f5105e8afa4826fd42b541cf5691c47335467f0a8dce9e963ea91c7c4a70f0dac8f847288519124f251e7113a76b19f26eef4c1e7121867bec95799966fe1bf270dfa8a67e5445a8a7af575c117248ea5496af171ca103a0e752ca94753e8f9df949ebff58ebeacf988e8f45dc189d72efddee3bf4996b0d7fe90992c36e2922449be4abf3f64ffa3d905499ab5bea2bd4e89fcc869349a9a438467946eee104576ffc34aa39a432dd51c264b52a2995c90b4b45f5fce64b1d12c8de64c167ba29cc962236e6934cb596934792f8de62a2d67b2d12cad541acd8372c33ac8d644c122f3070dadb75c037750ee5737495e933c4a22fb9349764281b42127970b55ee16cf3c9eca3e1f5607d93db2d72555ba82a5e55ef555bb2a77ca0c49cbd2da843993c54aa3e98ea839561eb2b4dca61e93cd46b3f47a95603076ff839840dc75746922d873ccf79e954673642ccd4aa3990392924909253577c89722bc347211354769346da572eb5e12e13f93c544784cf6a6ecc798ec5191d94ccd418bd17c3e120de4242ab964253401d99a8a7c8252426b714566b218dfdf026b51a5502d279096965299b52e455f480e7514093938418a874e4d74e6118ecf369a40b1ec7d511c45918b98d439d4a41fa285a31f623dcd73631954dc235d64267b943cc958e71d255371effe1d355371c3faaba3672aee90eeea689a8abbeda89a8abbd551522a6e5647d754dcab8eb268291577aaa3fae8e4ae79eae82915374837f5524755a9b8d98eae5271af1d65a5e22675b4958a5bed689b8a3beda82b1537dacf8ebe52718f3a0a4bc56d76f45311e5a9b8c78ec650592a6eddd1592a82928ccc921c692e96196d2c333e4953866ed63a67722433d90514bb10c7552c47a32fc51f99a3d21c69ade6c07d67ca8c3e6c02e3469f3fecc1a34daa3da98f52e4189fa06a8df1c53ff58f3fe26a0f1647517c91c1826d5a8fd98c82dab116cb91598eca721c455d7ea9aa39f446bbb84fa6cab2e10ef66539fba80d433547598e4c722c47a7788aeb0cce52ad316e41e836ea588b5a7cc9eea86b9ecbbd3d40b1ecc566bed54833df5062b2e6016be6db49c73b645ad6a1877c13c51e7610ffc5443a88e7a9cee8208ae20e6adac33e7bf6ac3262c6b7f5919b943aecd043e98a6ef7a1546be05fd7fc65946f25299eda5cc5d0fa8c1da0a90e1ab7f2989f996fb47cab95e4c80caa79240514cbb21645169bed259713472dfb3771c7bc4f2c8b92651a6273d4da2c7fdac768a2a3f31ca1666a6a9cf926c62ea3ce2614c4fd8d31cd54a2d1286b64a08f5fe2957422a9b9067a948ae27a42ad4fa0522a2d8a2b90164531a5e6b44a5573ac4e2028558ab10b08ab340a51a79297af4b1ae5eb92d57a8c5d5261c720a6d625ead104c2ae2557881e4556d358b64452d79097a84735871ea7d03306862c40bbc863709849646753b506aaaa342f18ec2449350d89ceaceac1f8b5ad4afd6c26228bbdda1d92d2aa3d4c369bcd669ca5fa94d5f835574a354bcd46556af629d5aefd48ce5aaf62a55c28cd8343a5607bd44f28fd43a91a28758af9d6d63b50bad743853f3d654235eac69dba445fad78abf58ab5c640078fc76ab130d8d9ed5a6dc947eb689dae7599f82bd67a582effc845452e14b647bedd271fdb24ffda25ffdb7dd7a67d6b9bfc8aef10998a95a28d680989bbc51f7bd4fd50e8132f906d206c522ed72a64b7af58eb612f16f521dbfaadceac1e0d59970b3a15f9567e8f201fd915c2798a3cd462ed15154444d98a0cccc0907d3d5767628fae5a80f9168b384fa6b15a0f53b1c655ca255369685d2c6aa55a2c910c243232eab3367df142052f445ce9cbcbef781959b1cc3d6b3d8e679ea6e55bee696a4fde69da2ef3f4b29b7bd9977d60290a723ceb7ac2224d2b2a29f9de15c16049084fb54e62a9160b65a5a6e06db1d6c69d87ecf61583bd4cc54ab97a42214dcb85442b2e0657ab7fd7b5d96cbd1d2cfbe8485a6e0edbe39079135ad7d716af507b2cf80bfe82bf149566d457564446d02348abacb4e0161696115e143fbdc272c3b79616d8c3cc3d6bed318a9c1015d8a5fcb2843d4b0b0ee36eb7302cc39206fb988a9572fd2075b08be601a45b6e399cfbe1d499302c7b64b7242df7f80bea1145766b807d39df541ab58b411dec8c3a392e16e988449566d4617cdce580250141c8442ef45191f031c911ea68a186bc9017eee8ad541bef5c50d20fd86437bb68b958ed5027d495a4ae8d7b8b2d5e71ebb9fd8b187cb9bdf4a0c38b5a437711fef2e245b8837dc79fb9675fee3148bc22d3604db0eff8a3e77fc91d72a7f579f579cb88c12f481869c8ba1e863a433e7f71f47fbddf1ceb76d74a5aee546bbfe015cfe544d7c38069916ddc637cb71ef60a51b1c655ca95cb7dd8e6b618c429f9c32c06dba892040404cab717d543f57e40ea0c898a9e89b24bc2d90549a952e30eef704a46c6c5a37e5252b3cd66ffb2b37f4d11940fe5a369d91c19f91820101010b8ea3dec849db01376c24e1810d50302c290e479a2a95aea65598e889c289aaa249818912fbb1904e494b94ed3769a9679c8150f70c5d3513b503d5293689aaaa415c8b7fb5fd7755d7714252b324de35455492b3bc2c8901153b3c9c430e9273125304040d89fb01376c24ed8093b513d2010d5232149201010900710105069576ed564977e25a0122f9552e515a9073ec90fb349409ac6629de7799e67ebdf6ce2c96cd30ed8025b600b6c6dfc0404ac91902410080858eed4a49f94c400c190e4c9080353620202026d561212a152c9c64a645c299780401814149a4bba2b1ee0f8a27a3d9becd350df27a1937df249d5345555f55f36ff480fb357247bc593dd0cb4e2b1e2916fef4a072b1d6017a013f05ccff55ccff55c4f1210b0b42301cd50247d8439cf5cb24b57e093fc260f4f3fe12720a0490c4502d2b812d04ca9a2a87175d22fbbba649378e093fc268fc6c5fc6808c7a88000a35a01cb1e18185eb2a20404964f735fbf9addb1bf24a02010f8221bc088d144d9b4549a114de4688aba24b7d8cdd11ccd31ec23298aa2288aa2dee18eb0d76bb5d05047eb3c4d20a7136a94cbe552a988df544aa552f926ea910c7d27adf589c5865a49ac4aa55267c42742976fd25a9b4c5c17444d49fa572b75466489ba2447721445566bcd8aba54772c5b42cf54a561b592608620454d2c40b12c17bdcce4c89cde15e55b8e249f2092e410890579069c42a449f209f84692580801ff00e34a1d9927bfd6a14b9d92889ebfd491452491c669269ce1083d8b5a480f15699c0e1a591d4406356992c17cd3a5d6247906120b732472722c459df58f5c89f0ebf9052896bd239e7cdb61171f19c74ea1c3cf21d77c24caa48edc4ed6c959f37e9aa7356f24227b644feffa4bf68044d0d8cc3a9f80ec91bd2c946f239e3124918091af72749a216bd555630ad5cdcd962c4b96e6e8445595b4b2a6d4dc58c6047242a554ab76d56a9144cee293fc614bfe505573e456576bdcc2d2d54bcee2d4ccb7b66371cc24153c433e422e4c3194636cd7cbd8edcfecf169b6f93cec1b9f53a025b09bff1d77bafe3107be957cb29b83300e023f8c6b0010c639c087710ff0c1b81d703ca8a4e0606bc5903143c3f275019845d1bf7ef1875d6aaf515e9b7430020881bd6e2526242c2d394391739e028793d7e1e1673f11a6cc8dbfd1dec6ecae6f59791bb3b73905eaf94b3ef9560ae525f0ad5402767d1a570e95ba8071e6094a408d3379a5a27c33794a59e09b9944cd757f35c3df154bc35f569743259fe963d2a139d5a96bc7c8e988b258256ba76117d8f75f3b010fdb0a78da46c097ec033cc92ec09b6c033ccb26c0afec01fcc880afed05a4a83c020630c48005b4ba56a7ddb53b3c1d04d86d6f00bbdd816293f1584ccd40321c66d2c9c94ac5778a1263c2a64af114460021b05dbe365ab2414cb68a84656db16431111873e3144a4446f2ff335adf7b8b0c994e00c203e149c234641ae29ca3ecd6db36ab01bb8787c00edb877fc07ebfdc2339046bb1162b16c46127071f5e09b4be16a8d585ec17cffadf2ede96b2c957d9fa5776eb59f6ed4d36ee49368c2fd9b9a79d6c57db82bd64ae964c46027b959890b0b868ed8ad6262a4daadf2076f2215bf228ea0cedd1906579acce903c8a928e7be6c6e6e1a5dbd96512deea72ca8eecf67a59891435f3f0a8a8a83321fb7b3a9ea605a97cbba379a0026b5778bb83d669bcddede0b56d78d8b6f1b40d802fd9353cc9a6e14d768d67d9347e65cfb0ed9aff1d80afed1bdebc313b00660460c3826ed830201bbb6d7576bbdbedce6e69d8ad8eb6d7eeb8a1d4dc70283ac65b4ed27287dc5cddc499c0485e252e5a2e0c864020cee4e15fe14fd33290a6b99ce9d07a025e122c0fbb0ead43ebd03a846b27a44bb9532e50fd2524c5cbb7f7810f74495176ee6bbbc5ff7671c096807e96cd7a93ddf224bbf525fbf6b44d3e6ce3feb561fc0e3b02e50621f50e040dd8ef4b60873210d8214f4fd80b7d3e38cca4567763f3f0e6a6f971cf7cbb65bc6bc790c0c63223eff33c9af63a5e874205e54e3abb1d9e8e5e7677fc0065bb7c6dbbf8dffa6dfb4565c7fcca269f65b7de64df9e64b77cc986f1b4cd7ad8cefd6be3bedca67eb27b70f8bd7b72c0e1cd368716c0bed2ed783d1e1c1439ec8fd9af23bbbcecee5ef6eb5c6c1890cb86f9ce162693c9049a80ba1d5ecb6e75b4bd1db8201fb8c8e55e945cb8b8dab1ec312f5e5cb8b8dab1ccb56811b2651e65bf8879dbee2f2e5c5ced28d3635ebcb87071b5be31644dbeab3325dfd51ab1d31ec53dbdb179f872af2b35d7fd5fad58d605030bd9f9ddfb40b033c912131296b1d378ab8382f52ed419f25da835221a9386ecfe28fbe56bbbe53f666fd6a7907fb25b2bdb7c960de34d76ee4936ee4b768ba7b95ad7d0abcb7ee4db2bcba2e777c1b0a2172fef73c1df5a7fdfe7c2dfe7e2efb77ce4e6adc56e77b8ddeeb4bceceac86e6e69b176cf8d57fd0d1d09f410811c70d8ad4fa5197bab832264533e863a637e0cb546ec278fcaecddc3030fbd11c596fa37c56b876839d0eab4bb76a7dd61b70fd8ad0376eba369f91bb05b08ec36864a33f613deea644e760f0fbf770f0e9fb29ff73a5ed763f2c071e0dfd33414ea6504c54847b49bb02018900904240993c96432994e3b76ad4e6787a7a3378312f3a281d1843a421e2f44e63503362344c64acf0d9d5d76330e344c78aba3d1137383867a437669c0c0b4583458215b3fca6ef1b5edf2bf5bdeb65dfcdead4fd92fafb25ffccabe3dcbc63d6dc378d826ffb5597f636399728ffd64f7dcf8f1c69b306e3d24fe89f7ed4080ddfcb97f6bae16c3e0adee95fde83f5e6cd80d1748f45c5afc13ef03c12ec75dbce523c7dd704b0bb73aecc2aae92de1ef5a3299b3b9e5cc3133a983ead47dfd2619c62e442644bf6c35bdfe2f29bd7e57eb71584fc44986f4fa572d9545bd10a9fd39ad8fbdfe93ca9ad4c7c9d91f7b21704278ceec69af82fdeb6757aca9f442ac1c2767e4679fe33eecf7e310719259f90b81f239edcb5e7fdab35ab1ec6a653295488fc3e23839264f7b9c9c939f3d4e8ec8bb9f43f223b1fd383932fedfbe10b627e224c39a56a6528984521f2765f2238f9343f2229f73f2eee3e4b0bef539a96f61ff42d43e47f6bc642a9148eafa382a2e044ecec9937c8ec98b3c4ece2af5accf51bd9ebf10209f13f23252894422a9ea9aa59061dfa592d615c5be8d955a7d8e49663da11e07f5383932ecd754a5cf21fdea717242fef538285c889c13fe93be09ff19dbc7c9297deb7172549ffa9cd5b35e88f573b21432a817a284e3e490f09ffc34fe133e4e8ecb7f463fc27fc4cf11e13fe6cff8cff9383932fef3969ff92ac67f5ef443fe9cffbce49b9bffbce69ffc9fffbce78fb88bffbca317b9abfd17ff79459c9cd687f09f77fc540817c7c951bfe56c6e091f2787f42d8e93637a16ff41717256fce7e426ff1971b3f429fe63f2f424833a719c5d880ccefa3932ece380709c1cd29bf84f99a37e89ff905c147b38910419960f11e6842440c20f2464569e43e23f23c7fc47e45886e3cce811502c7b4322971b3363971b56afbae6e9d8f5dfe8ae5d6e5012bbdc8094d805889bf686ecfa86746217206e525df393cb4d9862979b51d7372512ab6bbe5af54d5ac22e372793ab6bee0a41b0cb4d79c22e376bd78f6f545df34f61979bacc22e3767c72e3726167601e2a6d53597e91676b9197b8b5d6e541776b94175cddd10eca21fff0bbbe087696eb256c1be210f741cdaa9818fd0140a7598e82106e1e80f95c86e8e026757f7987e3637219dfc70470f08080f7998f859be399680d7dfb057018d0bb399721eeb16581cc4fb11f2b061fb88895427bdcdd94b4ab8bafa3662d81b7bfa366ddf71933bf920bdc477503bcb7bc86e66f59533a0ec24ae437633ac939c002a4740767b376a1ff11fb29b77d03e7a1b54a853eae5dbac38aa3ebecdc826a493be02245ec3aaa7fc86ec3ea99f5c86a98f4edde42f692f45206e5a9de42c641f790effbae6af0884cd4ab340e837599bd01011b0a743e8e79bcee2b83a7f649a5f66298c4006207461471547883d9c48427f6f4274c8df100b4ce8d9020d313addf99fdd73f42e7364feaa3479f92752081a90cf8e247f73784e784007e3a722af0a3ab420be9945fe6e80064690417f3100ebf9c59ecf50093a1e855a0ba8ca62710c514861c50944239c4c2a78fd0c21832e80c1054350c20f8c85148eb042165608821145ececf4a0e7033200c9600959c4e4a0871048b0848f0a2a28d259ac4122104289ae9ec085ba1674ddf5afcc54c74122a0427f4fe2126418d9411142c8c1042596b083d10e100b784cb153869d3144800c425674a0035e3022099211987085154afc7e782ed031055108c690043304a10945aa25f0d0420c7aaca08a20c8e0c73f903a2e8219ace8afc86618acc45f3d0e478e048bfb2a872714610a43183a83ee87c515473021031d50218927c4408e3e828a181101054664b5e3a022783092dac7f4a5a424824290051645f8f8682e8e7b05e74813e10136979fc01a0b528ff9e3c764cc8a3d3c50451745511445f1c4b22755c73d8668a28374dcc30343fd65cd0f88a1bf6effa1448e2254c1e3033a701449e895000a2a080ac11570a0e087124ef498820c51e011061c4448f1430218b620039d1364f0e309ec58400adaf1d658c7411f00fa427fdd1e86bf02820e9228a282074800a44326eb2b3afe1b722f52c8220965500214b258c20713f6e4cae9b03f3828fb60f34d83a193bf7a57db7ae08418e8f039438f0910c8906f6a1c890f0a1e2888b44084840cc957223fe1ce865f80b182d0117e30c41267e0000e1d261cf1829d202420018bfcd8ecf986a530010c7aa884d843949171a1cec70baee8a58e7dbc2088081f28c9d3cd8ebbc77c8d33f929f720c20452f46842d75a466bbdd27110112de8a58e8388e0915923410750b6b16ca8bd10aa3328eccb66dc9b3d7f0d6ebebd6bcf423dff49966fa10e762c8557878ea7e8a13a13453aaa334c2851a6e44bc661991de4188e23cea38e91b00b0afbdaf8e9867f45ec7afe979e35a98383c25088a5015d85d623b0f871b926a1679a4f927b14ba7a884dd32c4d93b4b1e462325bb2e56a94b92ff43e8b4bb37c5bf9245647e38ed5cc44267f6db4d6da1cc7d1dcef775307f3b3b9c52f4955d452983d33f7723e4287c83b42ece55ecef9847198e1c8c3180d51fe86281aa268c8dff4b34dee2219a2e2a3e93a9aa31fa1288aa61fa3d3d19350344d47afaa6fc843b9053429fdf32cb14f1a3fb7d0d193deb4472bb621e512caa24f52b94dee293779588458f2971c7b4e8883cda40daec083b138922449923870540408d1c957c99eb7a8d2d0785b40d3e0d09a7cac317fd5cf36b993a1aa3e4992afb505740f5f7dfc581d555535a9807d6df4887235fdf1dfac7e8d0fe4bb3ef92c693e49e22ab7d11de52f8ebea730b38b9f3ca525cfabbfcd98351f7d524892c85ed6c1e3effae2280c45b27c722d5fb19bd222c21aba92482412f9241202c25dc8eb2fb99248ebab205fe26fb8d3c9d7a85105a412adc44993f92553899fb84deea4f5d57f4138b7c97d25cf2f22ac911c8bfde4ef28a4a19f0c8b18c5104729971f58c59dfe66340ca998a28f742c051535a04289bed2b1145508861c074e4e0fbfdc951aef37ec891f22b1337ac947dffcf24b1b675ac9dfb4fcf349cf6e9c69a34be2363993dc46ebaecd6d428245f96b93357f47dcc62c227451721fc851e46ff89a874584b5cc4fa43cd2e008922585c41fc5512c8f10b568c48f2449922449d2254937dc028c17b9162e2e5e6a369918b85b0b0b2d4565a59b9cb825282430305be7c71c61bc7871e1d22287bbb5b0aca8a4d05c18181871ec23cc89893b32a6c89dcaa884eb9b69729429532200002000000318000020100a8591284a92200d6a4d1f1480125d8e62504a3892c863a12087911845510c43c610430800c620406686684e006ad2e9c549d8ec5b05c6af15089ef6b67337a444b84864de073085ed3fbedf39d03378375dae78aabad0de1a7a2989d3e194bd6dd8aa2a664f992277e345d22f544e1b28bca86b61e4bdacad812de0d450269700b70821ea6597d649812d0dd7e0e34244619f9c3d2e301313dcfdd5bdd70333e5bbe026624d0ee33b099c1b2a9cbcce7604526663642d01684dd6dae18d921c6330ab7d4875c831429e6e8f7d95a0b469dbb0d1dd0835da7b8d0723fe06abe7df623b49ac0edcd2a1d9edd1a27ec548e2d6e49ea65ec4243a3f803745b712852832595a699240b1475f839bbceaed7da89fd3801aa3c8d1d8aefeafddf1c342d2c65cb26928ee642c2e6793baf10c29ba63d3d4a20193de1a64e7285f1847390b21adf9cb411808f74ae875bf08c6cf196a35156a31fe0d5c60eea32ecb90f595243bde1b06a7cf8fa27d1da9eaabf71d6ec39cd688d0c7bfeeccb30560027bcefa1e5c959d8c00bd91eb5e0fb1f05a626bb612f7a8f78369b8d98471d28879a441d9e6e56885342919027383b10039110188012b21df478f4ce13972200f89e4a08955cadf39486fd06025eafcbc5cb74c0d61a3f0f3cbbf0d73a3d1c6c5617626e57d0b3657ca7ad7a30348aae6d914d8a2481083559b783ca86325c13cd1ace24c060d28a5d0e9298ef8556f29b75d83726d7c5b1aa4f08be120a94e608760d890a28f809009a7704aa0889d625de3ff46aa79d6e03673ba8208d58c2718b515daa0645dc8c704592da5224bca3256830aebed0cc42c52f0a30e4b3207bdd6215383b6b62273f0a14a142296aef4fbdba4b397c6d0969100ab57cdb0adc713f62b8d0a7d6d26fde08adad742227707703b8ecde9497838da5c81d1beaeff793ec2deaf5f89655e9cc7b8b47fcf0eeedf455433979ca32016b663a1b1cad54f89aca236f88cbfca133482db67e7f622dc88e164d5d5e85c0b4c097860feba48acff1f2c0c9f002b5768b7511b0009bfed949baefe29ac52142a1a599e1d62ca9af655a114b97e702e253d391b9d11fab32a1e637e41bc722f1f5974d3ab508104808d6edbea76e4190ff574c5e8d781cb189aabf7d05388f42b32a5ba2305cc386393ede138d990fafd840f177a6fa56e562061c56439b87417ed14dc8f2ef1258f6c3c94a776576117471b66ba01a310a94081b3e311a297e20b5f55c776843817ccd541af21c52e8151ca991ff37c4cc9060e8fb964854a39806a3bfa214908ac9d22ebe1d31270ddd5876b550b926ffd9e5198d3f434b6fbce749aab0f77d21e710394fbfd25506b1a8cd29d3ee6a390c7513832c72ed0c1de00ed46c4b6bb0e206ac70eb82166d511326e85d7fb0c76a574bb6d6540038d35ec8b1b0bba30dfe1437c74078cf4e8e5ee54f48cefe2d5cc846fd0c8a0574f6c63c1cbff05b5c539b162048c82df61f3a746dd5c342d92efd2c0a9152bc910255f12b1023616bcc90887eca6d9c8aea42871a28bb1b7e1e142ef098926ce344bc9f9dac71b6d3266684d0d024eb06a379bd2a0a63d0e855f68ff1a424af861654dfb1c034c798abd1311fb1d81b37b205d56665803b86ec03897abc1a0b76182ec782523e96e6f8b4dd8160bb106513207261d8da13bed19a99047068f7ad10092818fa538337b7e3cd49dd312488550b85327a21646d76f5475ee0a40581d6c5e5874886d40773702c35cd22b0b67a43b0114d630c031e07f693dc015299c03f5c34b03ebb7ad57743a1082f441dbef66d81fca0048fa1a8770bb9ab33f17e87c34d58d13156d6fd2bb134e55b0f7edbe87e277527301240f38516af88fb51f480310b5f3bfec56544658137e3880bde01c2127ac3f7756ab67608b2c3643d6aede8110f69bd006a46368b384beca9925427eb68da35a4a0d7629144b5c10a03c4799c1977468bfdb96692cc6734796fdbf5b0cbc4ff78c70739a2b4bee8c3ca096fab90d4d27daff4af86a38d0780f237b47a7507b24b69908a7643f8970529b79d640c29995f519c2c4041daddd0fc923630212234a1990bb21739a916cd0b560d6e31fdede7075b2e297625b91af7b5c7de68824be4dbdb95f6dacad78c5a57f7d11f3e66ad6635e57453d4a046e00c58800ec739eb3be057890f3a6c9d95df8b4c8aec654627dd9119d1b0804aa07217138a0abd80d1efb783054c56fe33c1ce1ca251127f017978307606427a67652465917ca555bbd87873f2cdecd93a8ac722c3fe2beba9f6be6121141645cb3553fb34b82192f5eb2640f8407490ff99ebd0b67ce06532bf3057a4fd4f74dfd6c0f954c180396525f2a230a0862e04455047e5f416c1978f810ccf8b24e0dce4da13eba53543f968c3ee25869056ea60b689fa125ef733685ca313236167541118b6928cc3e5c57776031800132ad33f37dce9cdb2f57200a5a6ff4054f00047276742c01d13609b47e4b86c2dab59020c56b711f0b9b9c474105476200584590a2afd37ecafaf31260cfbaefcaa500c3b6269fd08c12284fa989ac91b4e2000b1b3d87836c345c6b322df58553643d9abe106442de1f8d16e46879e300edb7f412b031d05f900f4c8462df729a3042ff81c97a0c0e7b338a76d182cb37337ba32e29327c1830a388ee20fe972a19c5743181450c2ec764389d966b57f4ec216f04206fffeac711a3efe0576c9c4f3ddfee07597263c253959638db9d72ac9f113348764ccd301923bbfafaf93956ee4c10fa5a22a79f839e0d708c3ac182e56d23ca6e728b850763676e82ee7fe8baf6a42a99f77f98b809231cfe691f23f052b90c50e767d9d554dcae4d2ea394a4205c48eab1f13d5c31f1adc435be0b4506c56a5a5ad40d9d265c4d6611d656c07ff88927dae418f4439cf74eab10328b1bfd2d625ab1ffeecb80a705957b1e55064726821a592a7019a763962b1205d62aa11b868cbe553531e6130aaa4a88f8e32caec884d574e0103bceb584c0a70c83fd67f54f5885c98f096582077578187145ddb78b070d0762bec9e4b2eda7de0151c88753744de448fbdef6ff9f481dea1d65a71f459abdedd222c30e8bf1867ae51083a85b9a6436e3111325941e628673d19d647ee3ba9ceb333212a2ae7b378d098f835d948c2e0f5280d6a69f688570d588af31cf544c705055506409e1f6e025c15b0435cda0bee666474290218228560100a8af418ff3822d39e16121bf30d0fa7ee400bba6159acd2148fbf6bb7a281e191242306f931a6611c17b66021fabd96c213acd5ec21517a743034d0f11e9cc5970fe043ba39063f456f13f851599da595e03e5a6ad7ec7af8f72c9d0a57848d62341207f15458f222c8faada68460588705209a614290f2e04ce224195c52ac41819210fe66246caa90f3b363c7e902722da68d11cc6b7c9fc9ba41c33795cf673691d474dd48f55f5939512e4b5bc1824775c225c66dc9bad415b3e40ea2390eb1b2c297102bf824c4c850187f9b0742071a15d1e0d05d09661b20dd2ddf06465208d2361bbdba769b86d22270eda2a887ae806e00ec801d3e63e78d4ac2e1124e884649dda5b268f952a0488ab822caff6e66d0fec25d02a46601af30c30570812038966a2844bef38565fce134696c3ed628d49da239af7f216921e48437bd346c5993334f9508be70256f8e7791a129ab179fae8ef385987d616f783cd9f1eeeb2f0372ca444e97b5cc55453e9f5c534e2c0c468a9ec4098eb48d0c148837801cda2540432507e702c6d519774a13ae51ed03749c3d24823afaa4102631728f610881cfe11d113ff385065e9e654e3691da51e4eb18a4da0d4b0b86282cc8ac84130bbf3f1414a762538e18eb413298e687304ac31461041f620c9d9bfc410459fe52823008f6218d7b22f29f0c094951ceef2de2c441f2ed5f894c8ec4b784e9932c97d0cf4d7ba9140d81db12baaf21b19c2bf3fe7ba4a30bd3baa0963d30da19d9e28a8a03b965c4f6666446494cb82569b5163895cd5aeed8c138b1a4514c6882572415b47c66ee11b4711c453165af731056b75da2470e254ee2be54482a9b33872047c8174e23a88ccac399d8f8d0e05938d64fb9bffbc1cfd52084b5496577ffa0eb0b20e5c408ef85b912d091142693195f498122fb771ab9b94a19c9bd8292c8148fc62a338a222c835fddc26ade47df811fd9e8f20154c6b8e1cd69d7ebe93ac23724fcac81d392e14fb51080e9dd254810485f1a3a6959269895444b4301e8c898923e04c4f43313312e656bdd12a41d395ad7bd4578f3ce1017f8bf791e3a94bc029dcaa6528b1124cc28c047520776cb09306e4fc31dd474eb9fec8d59d25250d10145c09e8934b17c0265148ab8465b892818f2feab6aec1137df3e48ee7e6cd681b85b3b34bcc41fdf99a62198b6826a77f7e846a74bc4f4e59314a5011bc90c024eae69b86c7116e1d88b320b36ebf27487c6328cefc26aa4a467dadafde27c7a20206e23eb9ba1ca0b37a66319c4684d1bb10e38556784d91f37ec1da201117b6fd9744c717eaf8c246ff5593d34d9e1f52553de56a5d44dac489ac5d53f64e8aadedfcacc24a221dc821c2ee1f404152b6e8dcc8cbad0e6ea07ab0b98bde3ecadb039b5dede598c1635fae593cf672cc8da0772d2178186fe049c13728424f22cd0401214bc3c4797818f15017584a94bb6b69a27b9763361e3bb96642263966f0d8c96106df3ac05e225175712b1ff6b030dc84b91e5f0e20b6c870f99efeba8bd9a47b2d37bb0e252d33739de10852d39175d4b20cc7c4166c4a57de4c67d06966921c50f3101c5c7e58ab2177e59d4738ad3eeb4a04c68741e9679b5bd599e37c14706e00d903b61663b4fa3d35b41679674837976959cd8e7188662c4e41f2c3b236712b1a90155cbc5ba072fe4192630a4844070b55c09328a024a6806208cebd5dfe0488d57eb2f706b442b103c41ec92388230f476e0dbde31efa0826dde51fd5f2a72411dd4f11330a9b7ecea314503735aa9103390fee650920291046911b23f5e125debefdb7e1e3aed4fbe31114620d901ee1efb21296c9377c4674a860b662a4d7f1e0fd1f61a427b90474c9eafc2e8600312342c35a8cf80d50f6d6a9a4bb10e456a63590087384913a2c547cc41c03538803a2b6acfc1f801725fefac01e0313960292aae86929d142ea62e4756ae2643e8be93aacec8e9c298a36fac00c523ba6ca9332994aba8973664e229d96461a74ee64c79020846927e932786eacaf8df9b284adb8bab9d84d4b4baf5769af962899cbf70f3593f4c702a5c0620e12d86014e5eb342b6ee1e030358ba6862613a244c06227e9b63607164a28a1fa9c0615a4799a57f3748f2c4545b1502eef8d510aba62859a81c7c36176db8be960baf90c08ccfa2d8735ed406be4ca67a2d44548deb5621d2e547ef5a2ffe07d8034e55e44441c8a6c3aac39c524eced620085602c215a36a4f6a170ba3588c4745886c05804087f3a501801f6a532fd905685a40bc5ec7abd6f432271d15d32320f83b9efb08b565016375a929747ba1d94c578b1d0513ca384332cb20f6b16a9abf50a42e75a11edcb0ac7bdedc239c8874915b57ce1f48a9fe7580188afcffe3f1cab8a8f0aaeadec5da3409064aface66110a80576278a3d1a842fd9175e6c19c69801258b1510b16e810c573db622bcdf4ea9e9702083105789fd98209e31e682783d842a82b3d6931f36f38be09e5cca62d91cf93f68db1e75a6e53b3e29277745a6c8f58e946f5d728a003c3b1f2bb77210156956d2eda6168c6a2809e272d73802c42832ac41ba9bbf780bba7538ffd07f3d0ae2ea8491337dcd7195b086c3332a1122106b888194d69bcfe541523bd3fc76ef5bf420dd9452c2876c16c8e09dfe31ef2bd59fa7eeed8cc3904f4414a98b4bef00458b9a53e5ce3cf8b39c821f363b608292ae68af43d9c3a6880c9b3e5a4149d795eacdb2ef007c580a7d659f3748c3ca3351afd8c39a35c6344016dccd14013e2c334d70b1a63638ea6f079274572914effe7b9557bc452c4bc9fd35ef0fdc1531d2a5c2498885f4ba376c452cd1dfcbc876858c85460d9f21a49a39e202656a452d0665105d48283ba6900b05f7321a4d64b4a9b03e8894f1256aa2938ee994c121d434e64a2c44d69f16ba105202937590cbfef9d3db61043ff2b2416c1741a55036e84ab4ef7e0de8957130320dec729399dfe98c3cfcf6da981fca87dee389f2a16cd7076c659c52983c2ca48473760a1c2355441a4410afffcfaf18304a0306bb4ba9f795dc93f4345059dd0bd4d22ccda44cd470e5544c8f58c9f9be71fbc1045f7b4d18fab10c227f5f2ed93ffe4289d43bcae0442259aa801978a33d4ba55c52a772ceae8efd9bbb5e09ec273cb05de988d825a482b2a4fe19db64fe6d4949aefaf22116c025306541433e4d87581b8f631ab7b4f509b09952c44b499f84041485482f886dffcf891905cd94c7815a9a63815fb084b852e10599250849a4aa7e0c3b4e660ba2b1b48c7d672a3d91d4988b39b939cf49ed6842812a7c7cb2f381ce4d4e1079074f7b13d4ae9965d5d88d7461732fe30ace855efb56c2a27b62992d8fcf3054eb46b14b50200ace3fae5eff8d4c36e77c59b316be3997c0385d9671fd7aef0b485c94a6b3d11c81a43185a92dab6785b29a7d9b9a8d0c03ddbc7adcd38a21b03a8e79bd0050f69fc34fa6e1f24b0c9108ff50964f5c17030c1d98115f5ef6277464a60e8655dc4c781c8ba46572a435f6732d3b9acbce195cba6a2337ec89c6e41cc74d8249d018c92dc7f4d9754254311dc8ebfe542d2388b8f2d680538ac35850f0cb8497469e13c4bc1b83d5e4fd3301c8f6b63f3eabbf3c614d749317d1eca791bf3ef07011c0f567696652a9660e79f90098ae4a1e0979e1569dd7a4474ef8ee1bd6fcac0c79a92e6cd3a26812f580922f740a6ae8e152d72427ac8a07a457ff44d4ea4a10b8893a34ec00d67b7227421b725a0fd4ee19701831b15b0508edb9286a17564c1c55a9cf95a056e9de8811b7907d3f0d916a2a0a3283c8fc26b605bcd6e677c95825284b2b7d3a3b36504d0248780947e1d9bd7bea5f6c44cd4f76c0536636220525534fd775ec06b2cbc4ea4b6035e6930bb0ace9d0cd55de82ec67a135e073d604c98938bcd0c7821c8dd623b7525810ace7b0ac79c186fac2201254bcb46d88c5c97098b777bca2f73d63241d1f5e0615063932c92e6268485830854ee0c83346b806d534b0d393128218a9e31d73a61ed81a73d8884397c02bfd51ac104018a9ddd8d95fbeb3c2fe463f54b4dcc302b1e658687fbb2073000bae173caab8a54b9e3c2f88374a7380385ea832f9858467a412c3a5f7f75abc7cbe0f67a4692c038bf8669082623df39eb476a393c35d9061300f08be494376aa9294b782cc05f2eabb87765d92b37334233090b21577a93774f02631f226046bcee7104321b510937fb336834238c72474a14b9d62bff48f20643f26bde24513adbcbc1156d14b28780cc78cd68d0d4f04fb53814dd02aadf2e974e5543927cf0ca126209125700e8e0ad02ad454ec002e6e760e78915d0b2b5c13fab8a19a8314610b8bba9e8aa1cb38460ffa325cfe1576012b00236c3557ee4fef0787b9c56cd52d3af6de8c75bd38fc5b29deee65157426a1ea2df810760def9a6de99154d11c0ca10b3c6a1789f6b45cb41f1c2293c81587513e1af5932c0c0a7ad5f1d40122715287f119b1111ea97f19ce34bf51730627f4136cf4adec2e2e0d61bc9e7581004fa592bfd2da979547ed88453e8b12d0f586392d34a6a694e2fd6062d0c8c386a32689bcc61a9bae3f453f4a3b5d5987f3aff94bc2207bba94b613326708930e828b9c6364021e8ac381f00d1f1aeb74c82fb4e2b4328fafed9b9f2af1d76a8760fc1ca9119b549650cebae0f04cc23fe8c165040559fc9eae23108594fc9c97fc14a606a904ec81be65ce90e2d98830f0aa475e73003d1822fd32f7f363d2e540ad86c22ae78d74514be42634e1de3b248a4bffce3083d8091c6eda452df99b41b1589f856819d77165707023708ddc0445a3349243afbba8578ac2dee2d3038ac461ef3e16cb9488374c0fbe0b6946ade312eaf0892ca61fc84d06c2b088d07a892a98be9399b6a4a6904a0b843e009f4371fca4810268fd47a8e7d26350a988b7a49607005072b47b60e254501978bd5bf203e4eff661b54a61d3b17f2eeb0418260ac3ab89c6b2a72f4d7bc5f0b917c5473b87379a7dbd83d97f882b2848e3a5f04add67be0c01d2f43cbdc5b64b016baaa0f683eddc34e3bb4db30bc9faec9c86cc83d67022a1772b82206fda394dd7754508fd7d276c05669eef473b93f741a318004908325228a61bb4519e39829e540c921a826476f39323056d27c6731ed0a8e305c1bf6ccc226374036c5c7bfd9bee1ac2a0314d6bb9bfa9b288f2bc34232ba850290adec55918075c39c1e2e0d59816bf4ed53b1a9897c2fa5108826fee35281fd37776ab458aee8b94c19787b49d3ad818a45e4a795431b013e384aaf3962c71327f2838b89706418552a536c8f1e77be8f49eb41e65a154c9d2e5a479a836048024784cecccd8d68b9e028b34a5ba0a5d6e265b2e71aa86b2e3b6b60de59ae0227b6fb3a9e30156fa5a119a6becad34715ab92f1bf308fc9ab5ecb965a2c8bd8050bc266500ed0a3147865ccb393d90cc29b169705d203e28a9c5856a016ae636b0d8c1a14c723870c6243d9650c70fb9802dbcf39ea653490328b3e278ecde268e48c5e9122cf74f03178fb700b48a001a1c5466d8fa817a81ed437f43d38fa64a3e3993490ef95d721f132391652a03faa2e1c660555657ca449209ce32505d5dcade773b717ffbd34ad3c942b479451f33459ecc9b501f2a699a2e98800517d6ba13a72f0e5d852abb9a20e677304fd60ee2cb1f23f517d5d30e2d574a535d6caf58f1f13bfaf8203f4ee78f030134bccf2d3d25910f95e2425321f7268e8b524e4551936f63f0b4c83f2ca12cd457d4b3664a06187aacde3179a01cbd58b6760dad9136e8fb014a40335580af3997182cdf2167110adafec55e5a37c6eb990460c490b4fdcbb3889e28ecd1629123d360b90e5ac9a272aa1635acc59249b809aa2acb66e7ce836a2a9e4437efc43b13c26bd683d849e5c758502e3c782346d38c4535842a9af2d71a72f8cc1aabec0348c7263d32628c4d887d83534d9fb198ee54c5010955882897904cb8ca6a53f2bd860c8ed9c63afb02d2d984478cd458d62eb7896a35839eae3a3d16c5f494abcac9c37052d20a32e45ec730fa8295398fd26ab4ddb361002060c7b2d1175761fb584d2d946ca901494556951335a2bfc63a134ec7237b425560184d3f555014ea2c53d269b15ffdeb0f013f758cf6e003d1a686e42bc5a09d9974c660a01a5ce74190a706dcd0c27e464eefc7fdce0fb41a46406a8427435ba0572f6d4b6937ed5187137f8ded8fcd10032f57b70c7e25f2d7d8e831676c629a3e55145901a547216905080c6b0151c68862b8df02a67860f13eb99811a975269bf300a8d8051dbdb5507e03d0ba5d908070cb184a4b0660fbd4e12080e76cd522f2b3fed64ac738bb628b36b6787459fbc6caf07dcd9d27b2c30522ebc3503619bb284d48bca2d00bdf45f289b17e2c95302dac51b870ceb5f27b4d0fee6e81880377c6b54d3488df39bcc1c3d92292cbf6ca37450ccde1b4fc8482b5987ab5defb4c30e01f6b87b255b5406167c2ef13c47bbc853728d87dfc2e72364838327ccfe7bd60a3c3006dc0ca99ccf7348df8d5e118ebee7e01a5f37770f8c7ab39fccbed618de6b96d92d173f8c134f95c35eb03472332750dfce49864c8f0f213904bcb31e9e0b1d9e3f3f750412b310ed38167a4fcf9844299520d4a50a0a3a1a0a0ea4bbc4f1e19308799b01f2e4f2615a268eee8913eebf54884813ab2ec325e57dd92c85815342c8c8f5dcd3e8534fdfca3bf64257ee0d5180be13b6e4f55e11fa2777c1488f9c18e0ea80ca0cfaeaa8c8e76b08d8de67a04189ed0439d9c778255be130ca7803f829e69841eeb7fb69d6f8438d19a71a0df2071926f633dc337ca385ed08872dc447234d1f104b9f46f5dbe8e4c4d70f705a0a5e9eba3bbec274905cd222a604605531c28c239a2386077114f544c86495e9b7bcadcc3ea490096261e939436e33e6ce8a0ad313450e3288dbe6dd0e8aa2dabda048787b1223a03daaa0101e93392c1ea61e846284e602d0e897fff07ab60c6f26503cfe58592a90d01262b9b3774b334c0f85352dce2bae363a8bbd9f121867525e59389ddc6463420a5e429761cf2993232a86446a50850e472168af381baaefdde840118e72493dad8de50cd464a3b141559e130d8c94458cdd7b23bfea47c2b40e91680227965b9e1550805b700fa4b51abbd79658d124e58314648d9e0e87d8d3ac4a8b6142846f3f7de1a6f529e6334b86bac1b7ac66175c5ef36c9360021b3b191e5536dfaea1db655953487b15617a7427e51a432523fa011d5569f72a945bc4515563de3b8dcb84e49224bdab556cd34ade02775bc6e548536aa20aecef690e6555bc591e19f961a17b2c4f9033257f8abfaa49eb20239dbaa0d62dbdabfb71476cfdc163f7f1baca61b190cc64a9eb8982e9a346ff5f33cad4710cf474ac0ea5c6c6cf407319627a0fabdcdc8997e60646eb19181da02c4a32ec26bf8f5d8c210f12b888bc39bb966ded81b238a47fd2870f06122107213d2df13a5f52964f07ea74a9c7fbcde3f51db60bed144099c514d86114c20e7819c4538bcb65f6373d597d1a097d659493181c58de7d6181c0a3a513d60eac4484546a4a8aceaab221cb249f6f9ed5bf00043cd201ccb8f73303b63f1d73eceaf1cd3c7e17e54b9b2cc3621523397866252a463f51519e7fcf39cbdb299f68030086b9255ca023a53c178625c28c488dc4bf0d503b64510dfdcfcfb025be6e8e5bc87f33334ae5f360a08b648bd1cfeb5c0d450e3965b4e974abfbc6d8f47e16ed87da3b0c96e0da4451fd7af513f86e53203bd86fbdd8d16b386729e31103a9f698001e52df2ab5bd3574e6e3c379a4c5f0225ffbd39b4b7b2690c84d188e74f30c547a4a6629a5f998f3100aa1f183893abc33aa47e1b952df9c10f50447e76162ba50bfcba31c3d895c589058457059d6d76744840168c9a9d31e05ac2e1132315641fc5e67c0dfa9bb615748d4c014688915b726e68645d5984598330b5ca7343bb84810d679f6b5a4ffcc584e89ac52dc01a19a3bee56df7a4fbae1d4b8dc45b9191eb86c017d3a72f251a6144c26800882836aa4273410102f12c0325b30b6ff910e927137c32b0295edd700533b12b1b64c96744446f82d37d3cb1f72ce07374938a33d5957e8ad9aa684b61d357ad68e8f9406a7c5c339db60b6b16cd8eede559d8f9542e9f758743b33afb41a13bb8802149584eaa13fe2ff0a4bcd1b43f7413b67c25c567ef4dc9877da2e0735901ee59db25e42321075048d7998ceae35a97d9cb0b650e635110fd6ba982c41e1db67cd3552f748ef5e29e9915151e9346800c08b0aae2f3bedc5b3ee3abdecb16a4e2704a8925ab73806c9cd44a5952a57f9ba089256e600785eac50584398289dbd883a2f2e200419e40e236f220a8beb40b07f03ec5e4a421b1cf247bbf23e7575ab7452f9ef4c81bead2af6b6033a7eecf6ceaefe5108e75ac11bf3c6afe190ee8e33687baadd9dc276a83f1779b850f6ab855257c486fb36c81b7b511370023226e703468a4365d5d85339766d3210c2d71c0e2dbcc646678d41c6cf98a98aedd592ca32ff7377de733042cfdd048370eb2b3a6fc7d965af2d9722ff82a9a0d8d5717c5d9b9874de87f22fb1bfdc0a139132c4357e81580d2cd335dd87966397a2dee3bc49e56906a133deb6fadbc1fd4fe34e57c29e7e02e3376d75111d160a3350ec7a43ce8545554c6582202b0dd728c01c5dc474085a0ce15a858ef9863c4f83d8ec36bd2c65a42a3538e50b93621d4640fda74cd3803c1b1cc5018250c246cff61aa335dacf004c9920731270233407d8e1e2c1301bc93e9156fce53db7db5323c0904f359466681cc729e43862b6b06d16f7ef15b082d82f92cab584da54eea1bba672558a4f7c85333ec2c8d60be76279af0908465fed27760cf707ad2d32d32ea2558bdd9f350ad9ea5771639a5d703787db4dcfdbed7b21fdab3daad20bd673b56b071506a34d9eb799e665638069f40af47b747d3c0bce8470780988e49476ea2ee4526ca84a147c45b1a3e4c407d311da5d94f599e4f334d622e05a80fc17ccc87a10c01a2a4bdab8a92a9f101cb32fe8dd1d4baa51b26cc27c7ef19df5ffd20e7375eb5a5bb8a98719feee9f3435568472190a957c010a00c8fd7d8f47c2c6e6b4d27e8318a744169e52d797878491fde65ef9fc6d8ec7eace147efa1859e34536edbbbb14a33363819f1e2548c20746f808307ce9bcdf6235e3cfaf10b8360bed907395e71a20639af34d6d69dc3a4a9fe6b427eeae2a19d21e40acc7880b4f2bb57c99277dc873199b75b52aee5078606fd561caa2795d729bbf1798a5c3e08031bd29a08f5d8740deef195517f4ec6fcf6f667626c35248124fa6554c510ad4d77a0d5a609ab41de40208a0faf08b9bb4cc624a0e337174d2dfa675b653543b052a06fd0f9ba74ae9181176774901e2af2e618e8c9826bef694a2b5ef24b5bb8f01c0e8aa6ee59eaeffb49a583463eed20a05aabef59410fd2c6628bd5f3e04e22600d11a4cd7c23a68e33f55678974d00458c18721d0a5f4a3337311a0c15856b9e6acc761501ab08f49c8b0695372c4a00dddfa77110603b1f8952be07e8288da95e04f04d849fb40e74bac9774c1b13040f1ae39881647e83bd461daa21833b55003473d87b01655aa3fd580ed28198e6049bb1a1d802833d6d82e8c46327b866bc1149669741886fbbc3cbe79a909e99c2629c3e2bbcd7b7374a71b6aa1686f3b86e74f901e1536b29f3edeee95544cf994edc7327c371cd4128f07e43c28e3d085ed8a3848abf88c988e7cd0ad945f8b61704fd423100a995cc50fa7aaaa98faea63691ac5ec16bfd5732c005f66700d5a962a26fae79dc5e26aa7f2e02b55e86daba4727c984644b94aec02a19a5d0c2ecbcd4511a7c07c36c45d60f3b7490dc66dfe78f018959e6fa67839c82e348f55bf1079ea1a6c658e8289b0c2e7988cb2f96d5ef9e62fceaaaf88eab08d1df28024e9bfa60d7288a7216b40edee222f49d637919a5dc5f56b907d94b4059ade17f6c2451e0619bafe1f9e521f843b374466b533b69c71ceb587a6a2d307085fcad9ab7c28e7b8af8723ec69722fbaa758f55e33449c42c2b7370c5e6fa1c7e20b20b008141bf4e5b7977107c4a56e690256a9e2c9a4ab746e7cdb24a78383f589448c3ff9e230dc55cf3ea1559a5220a2b85e8e34b0fc51c025a9c3b31f314d119a0b9ad9dc07d707335579e451425a02265ae3048664ea6f649505e2b990ce9ff5fafc0ad637542fd83d8524ad34450aec21b26b2d1665f11567bf338f27447e0405de3efcd64ba1482e813ad88f8cc6aae468dec1056c75ebb5f46a885434150d9b7a4f89693af9973cc59379611296763aefead29ca8b33a2e54338ef5bccd5bfd0660ed17e07e71ff457dc5774c402647f3f4894f3628f41264c8bd0bd6676d8375607cb3a61997c2958862ad9d9a939a46b88336672accc62510fe48b8169e5e2b3fd56396ac1c2679efa87a8f6cd82e23a7a722c8443ff4e00d010d9a6f8c469dae80e4ff9420a62b68af27f0c8441f5de8a11091dfebf501a9e1334b926d7094a7668df0c4e685ab6d1eedf7b8532ef8017e05d370a39f71be27e56c6462134a31b33ba4465f35be7931eb570e56ccdeddd2d4a0e7853f23d69c8ec915253a353043af8109bcfd45f411ad5e6d368854340d7969729b87ce525beece6da9ee053ffb17a410cd3f2e3a2bf755b2f19b055b2bc0aae4cd85683059adda6942793408c362864e1554ded2bd2b6b10920b19b7ec620d0df4d9c5cc46f2ae95bc2f7ed6026a1bd17c1458582a458459ff36456bcbeadce36bb674370af7a959cd5cdd4f348c9b88056dd358581b9f399b90dd4870a7c530529acb14240207ad768c596950cea56faee6363592bd21cf1dc6bdab04fac639235ca2433b54c231fd774154db27ea38894a403fa9c58f9d3155f4ad38c16f3339fbf413d2b3e1d74510001aff6a6c61d21ecf72c7b1f0c02469d86e41bb6b61c76ba2657fa430554b545cb2e57d5cb0eb9dc7c638ce9949e37138830c1ff3d08083018993cfecb61edf8e4f23005a2f263ebfb557084a48c2dcd7819bd0145c890bb08d1ae7ace431c88dda4643c5acf79790f0552525a93d567dd2a0203a26a7f6207c4d07c77987bed9d637101fe2d46584c9e4038267909c5fe192c708dcb26f67b1b5070c42d9f9dd632e719e97478f2fb365bc8903cb06403d1273f7c33d076e3b8aaa93bd410a1e2b9e376519555cee3f2e5dd26733edf395f83993756488a9c07b3a421aabc604ef2f44344764d404c4bf808130f804a71abbdab711e6aca256a9a2109cceee5d6e163ea90c7780259b354442e1a0cf2a3fa165adccf66a6bc0c42585f7c2d5284c1198970f9b5c4792dbe9d7d1e248b5500c4ae4ed7572b9147c562979aec6fdcf51af354315f2136bcee07cc8b5243e6309744552341892012b1fd9b96300410640ecee87a0e5cdf7a50f4148309e97bae01a149d406ae13c4259891a76ee217e39f084248c163cd8c39112921944cefccc198b50b6c26e3d270657cab8763115a2f92076e67426be99884c36e09388f400e9a09214939c8ce089e25f0918b130f024f49676e1c8c1937d3336e1af022de786d8b575657574b2e3c4d4901d5ca3fc8c38db758a087dea8441a56bd7a4f4c48788321e254c2618170acbd77a91c1452f3aae4bc000082db262aad55e21831bdc02474f93d7920dc493584a4c83ef23ecb835f1c5d623ccc41851a80688e64402d1f62e2b224d49d8410dc5bc01a72fd96110a6d2ae36b397108a813c6df1040821cf3fc74e44f7f70d9188f49370b83eaa32de614bcb8ad5ba24a21e58f04750bbe430a86aaee9bd0d142d3d49e712cc2d0288ec80e2f156b46639701de130237220c53398af0ad91688e13513f9abdb4a23a339b7f127680221ff5482518770c6adc54a448770cebc21ae282e62c0d920c7cc183aa82a40b15b533b09b7d9cc807fb71b0969795d5ca953fd9699f0208236f45812fb9b1c8cb7cc461315ff403f592634ec5b5dc153b71b18ba915e2f6dc8e49780ba1953c2d443e130b13ac3cc5261526b48b5a03cd781535462713005ca111dcc5c93a84b327f2fe81c3d08de0ae92242f3fdca0845d23bf0a4423a9f6f583023d1a823cfb14658c138a0a958d86ee27669d0d90a3e4928a4a2849d234a398ef0997d24e82bb3e5f80df80a2f4874018bb8ea71c04112094f349ffb858b286a9b3f001601d73110446d63617b6333e14bc5c9a287584062a9bba4e3989f21872dd6d90a58c2426d248cb116193b96fa60a106698c54c2a176d727280604a9b802f5c8391998060e6d0905d464d7e4fe2cf98e358e500d5fd18047921fcd66e43e05dc1c53bd45734a1b4060961f36df140109e9414fb48aa27c4933944461a147290324e906ff66cfcaec9db3b43d954a4d91f2628c07a2dff58e2bd1cc2f9c1cecc302a9cf862a2e9f1d985b57a52805a4ce610a98b45ca0392b69c681ec3cdb8c442a922fa91662d19e7708d88f16b4adcc838f1375b6e928285f24bf4d1cc5a62f6080a529e0f3c730b3d2b5804ef0f911d63392933bbece3830270f0460629a7757e841ed550c1ae3ee93cd11a69e5ac2db3bad48b9d9db3cb498f8b9a5b572310743876b75a8e81e94a5b5fe00767174f823120c870da03ae0c5670b7867726cd1ac918b521a5f5c96a93aa48982a295d264514638017c2c6c66abb46aa001b335efa156ba5aae5d283f4e391e54f6dad323f9993dfcc0940c32ffa8b28392d405f4ce1c84d6e24e6eb1d0def09e607ab2e826bacfc5cf6ac5ecee4f2ad53c22e78468914c148611f388d821e4b4110d20d764b2b76e95a52f8394c2484a589a65a8f1472686913e962c9860f2491e000ed85a59f36d699c29e86b625feb5118260f7cbf252fa48afd5afb4d4461d53b4689530f0a756d6c831a097237d44c69a2efdbff38a3e7dcb52d68b59097f3ae9d089a4bd7859e45a26a7eccbb8b2c4acd665a764c54cbb7969b5596e06778aa102f110996010f94a63c50685cbc8b3cad9ce0a3abccea521a38322d74bdfea7d1b0f9470f22c45633e56b5f5121bba410b0f6581bf4b3fc038d4cedd4ca6a026191b72bcc85cec1652645b384ddb3691e63f6672f4ee317d077daeffc6f5a319c79542f0ccca9e981d3a0ee17e225f452b400740b72712f9dc33c7d5e17b10f312a506a86985f2b2758612596924cc8a1cf8db18dc45efb7d71521cb69964fb427ba9d1d430592485c7f8ee8addad1598ed52ed75a860047677567dd4d1749f7de6bbb3061299df88e3cc293188525594b3e6f758f01524c698f3daf6b2912ab90cb534139310dd9104a2f2a7619c22893a425c4345af8d2ca221c93e41c1667b7d0f5f78dfec688e269079e53785aab5a162e8b93f6ec0f5d6164b8fafb567a66cc20e116a314a5d9fb4ac8c476807b0bdb406f44f336801397f08bf439ff201b2d8df73c67fcace739e05644008adeeab74e6ef2d91e295f1ba139bead3fbf09390079ec4f0019dae3e77bc57da7e070783cd5e01eb75d8e463a950dee0513eadb344bd3ab9c23d1d79251f7de77b276caf8cf915f75f3da054970773e1e8ad74ce05f65313a63c331da0a110ac16dd4a4fe5e816efc80bd96e456e93f28530507ecd2cc6c3184ac9eb34ec7e7b1456cd4f3aec7038862be1fc2ebd0acfc53684f0a3ec218d9a74c0f816c1caeff473b9cbcaa60de0872e25b1c2315f885fb035fc29326111320afce80f7fe161b19740992a26f4a101345459eb95ae25651c1ac56e4af7473fba99581a92de5029f2ed16d37d3f8f2e8d189a6eb0657f3d14083fb6facb29f193f62b6c1b4cba82d12ab60858ec71e63071f36ae2db8cdba7f48ec18245d328d1efe07476609cde3359c50824bef4e8f79dc606f7651b56ad88b5ecc198b8173a7412663b0f030a84b1717e79052b29cefe2b395647eaddd4b500c70f4a697cae760e92179c154b5750de4438c91f66719b9a26c234a41dfe7476c2bdc64f7d2e3a4f3b1750573ed4ac833d5ba5c01f71f41b9ffc2c762ddd3d1466d5aa73cca47ef5a52539ed2daaf9c5744cdfa008400407a5abf741a55a65eca1bc922c210deb288d9755357b9046f26af74625b1531bb6ee82a97c8cd66e1b8e9c24f2a7a8c96afaac3f91dcc2e0bb6dd957795a394a501fe46155c374b4429dca8f45bcbd8eab56ab55ad9116d7aad05c86c0fdb0afae92580a4e126b92eef87bfe5e182911b9f2d0b1f7e28bb2d47a25f3bb7a7be7bad612e2cb874b3c592b10a5f1b9ba8898093ca74176b05e8eda097fac5cf1f4cdd2497dd5864b0f25877cb6cef4663a425ca247f0fcedd2011782adaa8d77ae04b9629d7488ab55ef62ef0b14accf20b419abd5b856772edcc1e0d286d7d6262bae138ca5970758d2443535b30986212e184207e8e97ad3cbb07a8240a82e55870ccd741d0d7911257839f03e391b3e2ca601bd7500eb34b18175c20716ac1faa27f3a20dbd69f5a0e688900b6490bcdd6ed4dc3d6210430e8fe33eaa251b45c196520b25a8c876377995d70353e23b1ed3f288d9fddcffb860e5e209159f3435b73003279f0b672927942e405176fd66dda446d8dd0d33f93d610216fe48a238cbc8c3ddf75ab14120ffec10ba6def88d052059c252fa82abbdd22ee34349b5c071eb5716c95a0f2c3af8c59f0cfc1fc52d9b2b7bf2aa010d0c6e34ae43070cd5f38d80d9e970177965986aba87bee83413046879ef4797b104ed37bb97be9feda79c8e4971135b4bc12099864d1298766aa840994ad643cb85b20506578f49a121b71158ad1d54dafb2d84f78a01260182a86b3f8bac09d9057f24db82c1a529785627200f79aa9f170dbc372df5afbd3d5016ca441aa94c63c7eaf6cc0cb7069802328cd68f2868e624b364cd1ca9c33c33662f390cd9f9de39060bb872bc55000126beaeefae78224098eefbf514d016e8f3eeba2d0546a7c804b8408d90dc27eecbb5a7f831b8aee97760cc8641a95a97f7760269a59021b15cd916df92bce9ce06ba1e2741298a8d34b805e799928fd3041b191c3f671eb6786e441380d3d8a447468c48c3a45db67dbd076731e744621360024e47b1ad725bd44fbbb6f7d9921806404fbb7c50f27e02ee72ddcdb4fc08a3abb0043a0589a328d6512888dc4e4735c8c8db038fff979384650f24f4a302209681560a1a5ca2fd29497a93eca1777d95f100eb353b5a6f6943d92ec8e4f26f4a2d650256483612a905a0c7eebcc01319a0dd5404470144db4a6591da4d9923c86d822eec491e0e36c89a5483ab9dab0af5cdb1aedb639944f601c73ec67b4439980d5d1b99658200ee9ca4dc5929d5cf06b7f5d63a5ce0b713342390ec8f4549db25810cae7ad4fe2ebc22c65db40dbfd9d6faf1d97056a6673edec588dae02ebbfa9f7a4276b7d9c1bc64697bcf9530d811227d3dfc76efdce54caffdb527fd03f6ff7155628ef375dd8dc45f5facbfc6d52a19705768634fa1004938c4db24cb101c5c0341a37cc1a12a47dae16bc2a014740ef3a8d651d42d2f55a3e2359b7aa27bf5caeaa06ff050d30cd690830b3b32db459f4affe05ff97f9cd02df0605f42dc688ce776d9a7ee752fc368f3c52c19a22d9fb61f2315438aca243b79ab085b19a383eb1af7ef18ed7b4b09201d25f0cb2a6d8b0d8a3a138d91d337646e2932747e3c2ba1fe132e9ae087739f83db99b0fe0f70f033815deb0b38e219878fd14bf07230f5bd69b8c3058854155286e9daf52343ae3132be2882e908f13aa4839bc1a149d605508484e605b3397e2a3fa944c2b3e150913c1a9336113f1f12d21aad596257ebf5a7b0ac4e17aa2f514b7f1fddc96b7ff9e710aba2db2e2f60d10f7a000f90e5526c9b6196b241e5cd1521b65344222305d34bffec8e955382eff8a7f6a14c5b7b17a4fe3d28d62e0ff9c9d7ffbdccfa0cdc381ddcce4a2ce3516696416c95481fcb52d488a265ebac175538af7e3d8bd68313284f8604d6858a1b2b905cb3e8dc93c6becb8d8b6246bf8db3b4bb52ad8688f6b83731258d4dd8bffcf6991336ae403d53bd51193870b1bbd83e9bc97a3c35324b93e4265793d2c82c09ddae0ecf4ded78e495239a24b0ee787bfe3ee78cbc7673cec68cb9cee4b79f7be6e723605d52629e4e883dfeff01c00768b92f74693bd03d1f2b01b29c1defba3b78b966639904e67009c388f19713603ba5cd0fdba880d8892d43263de43da6680cb975bcd81207166595329012865ac3432b5cdc5c5cdcb9b9da51d8d295a1c13d2f666627b8066fbe631b89d6b11107607b96809ae05e3071712032a341079a2a79aa66247a316caad1ca179b0bb4bae4200e933cc7a6fe0e1589096e618cda723d6f57c9052ba437af02e0aa374eb182429db0c752deebb5baa4db04f76f695c16e1dd1d1a81e383254cdcc0304807ff0915167fd78322c7a82f6bf332802d44543f5d3e5c952fd9c6bd9a1728aad07e87e60eafb74d445fdf1f5f24c187c39893fc0bf6451a9a06bae384cc16fe4bbc80ec3538ace260eb362812f890d6ab66625cf37ee3fe142c2f98ffcd53821b017fc2d8003a06c9de2c0823b0b1da9fa5033f3680a82ed7444857afc91fae79df8da82e9737dc2fe4ca1d1c53ac19fa7da14ae49c2f4961151ff0c742927493a637517b867bc12adccb8df8278c3b241cb7315282c1765010967aecaa841ee473821b9c712c4f6e5bfc32fef9e91b0346a4ad3751da43d0c88df01af462e06f42d92b92deb4196c51dda339325d17510671c1bd52d118e971a4a09ca16d54ff2c1ff181b1032e57eceb4603534c0f91d88d3950e3d4e50157152609ff7ddf7874bce60761751443fc6e1080e53d3c0f892f40fbdb96eeefd897825eea98240a25c42100bfe026a611817cc5e41a87efb00a05a2a8b741aeb6507fa7d10e8c535680aecb8788a648168059593ea83e5e0e16ae7484a0b4fcd89933e23fb8480d67156ada66081409ee80ebfc558a0eea2b3bf85b817ee2eb42dbc02e55023cb598a52ea98e8adb25db383a1dd167426db5ecd36cac74bdd6eeb1ad9280ec6f40e8e9fbc3582a59173131317ee052210377c674778fdd4f3ef17922ebfc24dcdda41234e713e24832b2e28e32726242215c835839bd0c0d64523982dbfaaddc2e0bd821da774eb3d7572da4aefd1e2b8793cdcc1f6af8b230f77fd5a5c1d93148b7481f4824a35742022b98b211a5a9652f3bbaa1d53f3641a86e7082ce45a6536621169892cccb8ae96b79713a7bc55e8a01d0b0ba33781753883a74e05c8c2957a6312552f1fe219a3fe21bb89bf77b6e54b036186384292eba274a2f38c5fc1a567d25fdb1fd6834b741c19dc1add0818d958e420703f155628cb0aac4c724e37bcb409375221bc445ecc581f2a8880853a927e48a3e900e95de27ae7dddda11159e463e139c7857aa1931206ce5b21ca48b524a9e27cb79a650131b1265f2580fc1a5e1f834f7414a204d70346aa129ba3459746c5e813daebbdd6d876af9a5d88179cbe2be3e9f709a51c43e0124a1bd8693bae8dbfb004e2d447075e8f49891b74318f6ef4b341f31ada4e2ddd5811081f80215cce9fb5a80241852170d4a3893ea1af4baa9cb85a184117468cf9a3db29e1cee3800776ff2b9a62a87bc382022875327130aacbb0596d4103b31222297ada055092fc6e0b756d3ba444c5de23c2782a9511f1e2bd6dd27b9ecba63e2127bf796b8857504c950da49a7db1fda1e46336017021c59476378887947460a3abc877e7bf92e0ce83094aea9e2a3c835dc6b80dd9f2075695835b12ceb07cd03aad89d4a1a582e56fb21bfb5a1a238f12683b5aec577581f2be042cc97365403d9da50a11f94831d7aa154c772b2301ba17cacca8ef78d2806b31b391a8d4f982b366a855a85f72b9f9030a3b46e1304998e85ee6219d787855c2060280c4ec6ee61df0237388f74ff03ae16fddc93b954ea44ab247e0c0ba607869243caf17834c74b9c0d1d5d248c5d1683663cb9210a326570e58e389ca396042ad4f71cf480c0c7e92608fc1c71530b3b7bb279b572d6be8bd441c33dc0acfbbbe58affd764f8f4b2e2df2fafd8bf248601d77e37996222f01e60b2b60f8e8674dcc039476f47bda4aff7982a52fd60129533e592dc7732ae5fc6bb3e76999eb10e03ebca7e136fe005fdca7356cc0f0cd7ea92d1accf5966cdfc4d040e8183e3e57bee1174390a89c7f1bde0bb604d1d5992050cfadb18caf95d795272d3b0e60aa1c7a6bbd3d51503b8fbbcc84cb28efb88bc7653d05baafd0e2408d57c797ee7e8ddb115c9f114b45f407a26e68a2d65c8ad56cefeb65cdde1a8db61aa9d05454c9981a7998854fbaa7df9095a4426aac0aaa8e7825de264f31fc6b443d85518b41c1342a930a3039c4c4064e055b7a40829768bd6b116069f5149b9bed919b415c538c928cb4a7bcfd11b662f1552b8d5d829b9f74903e8a8f888b98ecf30b59c1d78a483d60c0dd43513260c637a008cf81992d63b7dfbdf0eb9fd8de10af8fe32336c60047c3f39337670225e3f99391b582054e98adb0c4fc8ea5d8df744ec90c8ddd9848a1fe12100508ae40269cd1bec98380485e4e6a04d5b623d2dd3a3e7351341940f68b8ef5b25cc791072f8c00ef969a98576b7fe2b48b854f2f5f48e060516916f339d3a5f8320d4f8e813b546dd2ec02791fb637d157f3a9ef3e2007399d6d656d615f063902469565ee848dc5e593ed8b61e3eaec15f8c14d79ac3c5d63371c57e85776d0edf88c181aef75b8a5c0c598d71195fd4f2181fc9a593946eb805da334b6981e380af4edc89810c44031ab203dc001ad03830972b78e1263dd62aa8a7bc93d6690a49baccb1c5d907eeaeea4a926b5743bc638a0dc6936a46e95f976b0a1cc85c3392dfe42c1875ffe555169e09d12e791328c81cb3f12dd40d8c96c1e0cadd74537c8944fdb576badbba24559182ce31838fd088e65e0bba37505b4ebf61667542da4652a8cd9d21c001974cbfbfeef98debe830fe3e0ac1e4148d75c9d63d6f40cc0164304a11ca38661503d4f6eae119a33f780434fde0f09e0345b09e2c63d7f5ecc69f9634c9043e7904810e5b20542ad38c01aa240f07342ad28555ef241ef6aef15d176fa9225e9a8ca35ddbd237112958a5a16d78cc6e7d27696fc89f0ec0008c010f182ddb6aa97ca76477b9180f4bafaf995bc8c588284281506bdd849a880809d9524a2965420ed00d750f37f3e028017b33ff8df4c8decc7b235d626fe6bb911b77d89bf93c568016b1374ce3571867b03774a44df6667e880ac1a959c9890e4d4e7278128fc4a971b6086206c648e2892352bce009a322d46842ca08ca08425254154e34c9a214dcc52169df7befbd320415268c6a28620a12e0c0434aa31182156444c182080bb620efb3931b66b0c33d9de050b44bb19e6a57a77ec171e2b4475a789ac65de39cc84006582cd9989218638c357c45bc841495c90ee9417b0c9493d9d316edf3926dad9ddb55755a5567f628ebafbea00fa77aa10a223536777da91d3f1ffffee0967da2c66e8ccbde3ac5394de3348d8b71cde76b9aa6691ac5fa13c2b9c8a3b38ac0f3a8487deaf52ed7759ea7793b2c17abb18b8fa3de3b71eeef3dc29b55874e3aa758efc1543bb630445d20c2362dd77bf49ed669da3dee9dbef3ea689ac6d51334ad565ad24e6b9ac6e50d6b9aa669ae1a837d1a91cde27c35bd3ad7d613b879effcad31eede7c6b4c3b6c6bf75e3f8af7d5767bf8bb757bda877bdcb9a5b29e634c8d69738e65ccd6a6eb630913479be59c738e386a4ccbd97af6f6945e4de9f599797a31f0a62e79bc4ab96975de388ee3842a776dea82f8d3bcadcb1c97bbcdd33e0cde940dbf5c756aae3a34579d19a6e6c4c994520d7f38766c83edeee36034b1c62cddb6ba8557c4b295e96e559d6b1f8466070d6daa5ff9540bd1bd7dea923ba5dff4b671dcb671dcc6715a29643d8e7bbd3ad772dbc6715cf78e3b377ee2740fa3c4eeb7c63abc6ddb36374ab106f2a0a7db174689f5b3c6bc59edc5da46674ec126cefc0c7bf3384af14b4a29c518538a5f524a4f31c6c714d34d5ff131c618638c31c618638c31c618638c31c618638c31c67847d5b8bba9cedc37fa7975eee6cee93c693d81e332f7cb715cd61b1e4bd8b394810a21d2df4803566a4d4974cb2119dd47ed91b63d24432c715218041108620f4d6c19b6a629b369dfab4bf000967e87c5e30ffd6cda76042ae914ef56febd9c4fdf3d3a85d374ca4332442b17c97cdab4d6b43dc24210cb6b8f76cea2e872393ddd88ab887844b4b50ba5951789366110a7530f44fbdb9e823cf4bdfd0efb626136ed5bc6f0749ae57c1ae7d32e2d757a9202b5c183f52b88942211c37236a54a1ed0696f9a878e65bd5ad57aa4b0a92e3310b385f40f10adb2b1be3869e2545ad20d146edc13e4e2249c6477268964095393d393943be4d5b0c89b302eb89092d24c2c130e2e6aac9e9e06e12020f041f7578b1c39b468123ea573931acb8193a1d458bd3aeba0548aeacc546335a57313d48d1b23f0e07f23c70d1d3746d800f0ed34546e60fc991b239c5e26e7dec768d9d24f903d50cafef513a469bf2101d441dff71500361dd47567e9208e7b0bc64a0ec5c9f92c7ff98328d541db761b777de927881e28c5a4977e28d64118bfe5eaa07b0fb33ac8dadb9cea20cf7b0daa8334edf42edf98549da0544a6f4c9a0897a4be35517582c2506f4d28c948c26f4aaa4e9028ea4dc91149df8ed0db1179885963aa6723aa6f430ce18391aab319295263e3f6b469b135d55893cd6983326e52362ec60d6a356e61c088fc473131462e57266ab5ae88a295ef3323e72a35568328d577a342b72923d30fc09719aaca2eb5a4f1e53685bedc8cd81d2d67d188de8ca4b4912aa0d69c660ca7cca578e3a50b8cb983a3b7293b38f4ac37a997b907bbea596fc497b98c5dcb8cc6aeb3ded478c915ed7a98b00c11887e4d104b0e96a096961170825aad9c209812ec7f0ce7350c9f52a9725a3ac21c2c4b3f2dd7f9ca46869a38368e630387cd08ac5bd8a9ea35b62944aa4e4c07c5623a13b1a1836cd8a8f12ca5862e9f8daa8e8d0eb2b1d1d9488b2b5507a6836030bd5d8132a5eab4e8a09616bd4d61a252755874100b8bdea83031a3eab47450aba537336a2c56cfd2412c96ce4f35a6d241aa03810f523995a20e0a0f04ea1f201a1514eadca4a7846588406a0e3e2443048d3019994e3b38ed1aa6ad89ca942a1c51dd29428f6119620a28f5b00cb1c79c38255866a6d448c31f88f9076211a82275db9d0c556fea6bce4c3567a69a33532e32c3d8e5d6b44d2161975a95cd8856454bd29c5abbc44f569eba5d62228c241c43c4bbbc4bae947b748f2e91abe41ab9435c1fee11b7c84db25099698ab545987c68c226d92396d8219458234832539d52a7542a9648b552afd8a36a8635b244b5a94ad374da216997d54815eac49499983841b152d2a4a42368130e89bda9b409201a2ec9de5425550728cc56ec4dc819415275806832d4942a963271441c19e2a83314d3ae870a0347088bace78c6ebe1fac529fcda829dce08868525472e0cfb07e0443e6787dbe32824c3825c77d4c0bc7fec5b2f4e31d46672b5507886684175d5af1472fe40d2d5bfad9eed22555fd47bb8bce4c55a76cd13f6a8c662655a764d155a76ce92655a76469a7aa53ae3494aa53aa747eaa3aa5a8b31655a70c75965275ca94ce5c549d12d49fe6f2a6e16b6bad95b3514fe841a5b141731a1afa239821b60d016c6c5e52223438e625356263613534d9a6c64c816624e75b322fa279216f5e63e3bfce0100091eb0b9a6a961034000ee1ed0f1344802fce8858495f0219434e1d74328a9f32194dc798c1e42499e0fa1a400be9b791879f38d0bcdeb4ab2a2f008861c6148b2e7f3df4ef8d10b790a14a2f0cfbb6e0940800f40860004780a4730e48c5e2a2d016e68c8efefb80adf6d62b89b97132a851086bc397a2165ac96917939b9a0f6d40c4d86c586e6e5ae7b80e6364034387331319f9691da03b0d778e9528966e64a367a3c8c2e5f68687ed7cdd4658bcc59744b97ac98aff4cb1254ba146d1eea942e9560301b9b1899199acf5ce63157aaa173e8d0a1c79bc76ee335405d7edac379399b38fdacb5f158db8fba066673d86bae3ffea75a96e338c7f1117e8300e38f9c3df2ec5021853106095460869e9186ecbdfeb3afa9e357f1dbebeaf9f530eeb5f5eeb5724d28f094300a608c8167fcb133f2e88c39269430ae20017b33030df309679480bd99c1d290c333d61857b03735f6230609d89b7a3d56609cc1ded47fa461cce1d91156890dc1541621bc09a361967023003746c83102ce08de7fd808b1df8c30c2d10b69c28dee343f7a2149b838df9a356ec3047c981fc1902690c07de625f561cf7facf918d86de6a709f72f25d4bb02307e92902f0380ed31d7fe02a251e508713e977e0dcbe7a885e81e4fefd24274bbb47879f97d39a5c0c01cbf9c5cbc5ed75e4ea898986f2f67183232cf2fe7949999732f27151a2d4437cdbb6b21ba7f6f3ef67d7ea65ee2c8617df5b20496b75ef2b8bce5250a35d72f6793cd612f67131baff1f246c7ec6dd418a9cd2854e91075861ab3d7233522438dd9b78cd4870ad02213182911098cf40802d4688c81128d140a75a24d68d34899d4987d1374498d2919c78f333263cc0833beb8c6900c312443accfb971fb1246b871e34258e40b3858fefd08e647d501a2c1615bf77ef4b2e3f605c88103e7472f2de860bdfb110ca9e3f628e008affd0886c471fb1574accee5501d478b3c386ceaf8472fe4085ab654d21ff0f74730e40dbd54feb4dce5f61f787bd1be15de6e1775cafcd56d77d6adf756f99de52e3aa6e759c6d6f87135bac6d6283aa1a0e1f18edf908d2a4ee1cdaa13342735a34a954d85ca94296184b1a1a0b8e0428a14fde3478dcd4381e2e4d4a449130a28f0f09450420e8e3d1f8bc56030d8ff98981897cbd56ab54451fcbe2fe77c1f6475903d50d53f407307d9cd062f9c38f4214b688658efa4ba3d4d5afd088654a92e84456ac1a2ad97d38c14644521cb8f60c2a06989b72f555a3f7a816ac92d2f271516c77a39b968e958bc965efaf96ecb398505debe902b2d5bfa499d46454555d94730a44a2ffd84b751514578dae59492c3339b9cc2f1c7d4e21bbd30464b858ef634d89d98bdb1b712962102d15db3e6b4e127ed4ac116ead629965699564233c4b00cb1477d10dda22e33e0dae2abce00cd16b5904b75fb1f53cab6130ad4e59cf269dde97236b1cee99267dbaccb9c6d57d80149bd997b5b618725f5666ebbc20e4cf5c6b5ed0a3b34d59b5749d5fc026fba3e7f01e0ca717d1ee672b94c088b8a95c3464723f37a116b3edfc25133c28f5ec89acfdf05473f06a6b2a872b0be1a12f46762461b356c2ea4e6379f8fc1c1d19f8919c1bb6cdcd8cc67120090a365427274c46a5c39c70d97578e1b2f171b969c50c76bc074a46235638e7b18574b6bf589a365472f240e2d3b822147d03221b19a91e6c6615c2dadd585b0e8cb84b8b0a86ef4d2c68a79b7a197becb84d4bc865ee26cf412be109ac3f492bd4c484dd5d71ff5ac26d45fbb68f452a8b316ab09327a29a5c5189d5fa08e81f95c7abac058f452d697a59734ddd24b7ae96ab16608ac39082f27e6614b5c5d88b7432666465c5dc86b82572dc9c0dadb85b89c15ea257b212e517471b5ce025ec8771891ae529f8fcd749ff7d785805f7d9e4788ea319709f90ed3ba4cc8eb2f9f5fc1eec8847c97d14bf93221e063f492769990173ecc8b5edaeee22ec4e5adcfe7d5591792baea32212e7a4988ca84b4e8a5fbef42c4b3e82519582ebb231b42499990965ea2dac5ea74cc4a2f715a54650d13b7502fc9c0da9a7efef492d052d59f90d61057ef929767ceb5ccdbe62f6ff7f638ae96d1606fec53c0f7472f2407443383bdb13f7a21b570057b6343ed1510b9bce91c7b63cf636fec77d81b7b2dfb616fec63b037f612b061154a442b668117d3daf4421a0d5058858848a6eb5840e10ec2b2bb0bdd4de9b70202b4ed202e9b76a00a0810dd415cdbbb106db1505d8a0ff2a2f23ebff28000a9b083b07677216d534f972198a2ab1410209a1d8465d30be1ceeb74093e08cc064a5dc86e140cf2b23b20bb83b836bd50dd5d577a0f02b329a59f657719589bb5ad3495060ab178435b53b5821a28fcaa09347f59cb727e90975d7a5a495614ca6efe110cc93d08cc2e59bbd340349c9615d14445459159cf6a02bd101679f4426e5a06d3b4ec58cb6260c8ab65ae6a02fdd10b69b5ac55032f17291e7d2ab15c9636323262d1473c248bc562e12f6bd7462bd0132f556923154be712c29914b59128c3e5cfdf2f64b1b48df5213c9f0782600a831fc2d3ea52a9548b4d7d080f99ba1006a274f42259ae7414235bdac86848265974abf5f0f3337667ad74944386baf5213c64eb42782b15d1a86d63695a69058220f8f91083572ba952a9540a4810bbad562aa25164ea4aa26eb55aadcf3f0cc3300cc30fe121c38ba2288aa228be8ca9542a954aa5fa101e52b563646474c443ae7e142357373a7291ab17855632b9ba51688588c845166d578888c855914d83885c7d080fb9a2993d40f206da7610bb955a7a9e35ae46d5288ee138ed8df6d4088e221422dde594f2c519bb3e064bc3eca144bb7e937942ed79803298eccd6cb237d3c98a4fd62545d3e5a16c6c8acd39401955eccddc567b35e58557bbbc51ca8363df5f8067ff50ebfda620aa5a257ef770fc92c4ef3955c77b7dbee2b4e088a3c6eeefe9588d65d11b3d2d843d101515e5b4f1ef057836d63f575b7d95eeb679dbed6336dda92b9581376d88e1bd175acc856018be6ced50277ca84b7cfb908e1e0e437d41fdb56a6ca3600ffc5b6915c22cd05490562f3c8f8a734eacbfad6af8f44e8f563ae79cb3e680677504163df0b826ce18367bc0dbec19b3e129a554a3e1a759cf03c70d1cb591def3be6dfc0e03f2e8c0ef20c802fdee516d9cdbda6eacfb1b539f3796b0edd997ae186bf5b43b2c5a5c1d695d1b4190474ba585ec9eacf11347fb8a09318bae1ad32e8eda53de387707f2e8f4145361ea7bf8dd0bbfbb27f869babdf0135b35c68438e93d16ad87f68e7b15bbdb2c44852c0b332a2a2aea560e098fd249bba042b527942e96a07441442db2d4523b034b7fd012ff07ff62ad0b5063da1d0b00f258c1c7eca177fca17747f6e13ddf5b8df7b0aa7355bfe3dcdcaff7170bf7de59f7bce3b1a4bbbcff099f595cf05ede67d5ef983af87cfadd822e78c7baccbfc7ba1c4ac5b41e62d659b8b3ce855f3dfc62aa4e6eb96df90135f74febb2e3f2a5d5a6ae12517b79f77d0e0e6d558d07afa66878abddf35222db6336a9addbdb9a81c317d2360784bb90b65b9f200b1168dd731848cf8903df310c63aace8b4eb085dbf08ef5302df6f66ac6be7fb1507f5987528330f67d7dd8b970cf43753b7eaf23f898aaf39d722e6cf7605567eea12d7e47ad6fd531f5d6f8527df5b9ed5bdf51596f8d2f16b6b3be9da55faabf58583df5d5534f5d753b7a3155c73b65b1c7f8ab51bc6aec31b4454dcf32667b437f521a1c7f2cbed563b995f8da29e842d69a8ec98038a1d8c068830618aaa2fededf6df5b759353b79428636b7e9e985ec6aed4f59d031032ba2b60cef8a65185aa2cab6fa0297edaa95364fc8a61cd8f4da52b6e905661858cf30b328b2afbec0cca2c8b65a49362f247c42420511b969221d33500111395f7549c3041d20da27af868d2fd1bcf37306575b3b718038d2825274c4aab127940fe060aff684f281277becdd29561d8beff10fd6e2d577f64c4bab0d9e03c4bba71332986c7a2f748088f77452c5d3acb11a5cde347c6dbd8068a3b8d933adeaab31ba67530e53ec5d7d0066349408c1b3a723942b56ae0533108dba62b43242c7276804a1d65ab1b602e3de7bb5cddbf3091a52344ddbf213349cbe3d9fa0c164e22df29cf33b5ba63dfa18b77d756df09ee7b445a27da7ec71e30384fbea9206a1bc3fcbdadeb8ed476f2c3d0478607b40be07f12ec4b2f3bd2983389f1041ca16f77c4204a7cde57354d352794b95427483a7af1c5407fd77ccc67123265603e6d29f69c5c0b8c496d6eadb22d5419fa23ae84775ec8eeaa09f600fed02d9a8a87da98e5da98eb9738eed27681cedf9840856eaecb1dbf873f56b85a95f0698dced487705796cfa65ff83bfdde26dbbb66921bb395dde07a17b1389f61589b6366a1744a27d27d541b9276f561d4f071ccb9f0eecd11d5f66f6d0bd3d3bdefa3c561ca2bd0151a0ba9d8c8068fb4c28a2f94a95e5ce3a48f6418940a04b4454c416caa77b822cb8ecac4b0db8beffd47f75fc9a00019a558888b6d06a83b757e8dbe07395bd01a9b0ebc4c91648be90b6c12ff60644b3b7e71148854d274e7dcd806b0bb9f6a6e7c4d1f6f4dfd8e39e1b270e7d1e65268e1d873e7ab5f02647812891d52ec657b35bcd41b8b2dbf8a510dd40d60a512983b73b2bfb4fce98b2ebd17eb1508fb6d76483d1e6d9f8c91952f6378764bb76f70bfa70edee68d7a3b1a4652d2dfef0586a1bdfdb3e3cbe58a8477bf6d8a020405b1d8386367e0aec518982d88d0f96f8ae6e4561764a7d83ee8876bef4b811c8be4e51526354965469c20c262b4cea9526a226d6a8ea000112c4b5f183e48d0f4448c8b5b1d3b6f8c8ee18191915d154f1c8ef452a40bdc8ef464533aa898868c808e4f7212790df8b78888888c8234c7eb7ef11f15d10c00fe5d93d22cf2eef13951f7c9e3d41167836d6402aec0ba547a47b360d19629796ca24efd37dba4ff7e942a926d830768fc8dd3ad99d212998e8c1d3164f4a507440dadb29f6c8ee4cb2044a455022011852e4000864699f8e7ca0063b5c7114c511516469a54c1b546182043b5c0101134990a585caa2ebb11c7bcf035d183777e9ddaa86b58b359cef76459bed2b57cbd954761ed1acf7650559a8363c218291fd7db74dd330bed7da5a6dd825dd1387ea12d7a38dafc7fa66dd59ea2ec802fec1f76580597feff9a85753d9f7f94eac4cd9570bd91d846e2a126d3a8a4496ca79705ac8c95aa86d4523c46db354b6bda552ad19d68abd7289ae51d539ba448ef61099efdaa53844963c3f32d8ad3d689714b880cd4688e56cda7468bf50bfa3002bb400017a02c545b8caf2811a1b4457cd539d31883ff794a8ee1925aa894df73cdd993bea0e8f69c197f669c47d1b8f32b97d8ea5106e1cc2436e41eec67ff9a049f6157c69fac7f6b95f10c952fbcb47b7350d14ee39839db3cba16d4f8928d1cee3b437230644fa49896e124c4e4e4850705284914dbf22a4ec5c0405cf3b9239ca44aca75288f9dbeeb525bd9a18027cadd562f16c186f9aa66d541442d4156b9aa67d7a5aad746adfb4d77baf76a90c43987e39f03a2e6f1abe7bce29da97b5d65c6badb5d65a6bad5e4a081da275bb410e1162e972c51cf6fac17dee6e54f2b4d1101e92bba78f32d965ae1bb7736356da6ec7728697bd6c36911bd8be81fc006c9d7b6dfd6bea1ff9562b609239b8de27a208c4228e2a1199ef4df02893f95529bf9cd7eecdfbb9d1b02fd626fd11aafbea20762b6d603b6bebdc6bab8d8a3a61523aa3a2c8ed4adb2708830ae4a66d199e47c539566daf1def9d2306817c74bbea206f6a57d36ca55aaea76478396f9ba6617cafb5b58a4388f4147fdefca14bf10b3cf148e628132a85485f8a799bb3d671d4e69c964e9c795b2d160fadd5d22c0a21d2e789335ff2ec59e6ec291e11e9a9b7690844a3f6cb47b78168547de2522b8438e79cf3a342503ae7bc3536399aeb66b58bf1b5958e80d211887497dfae3d93eb522f1f5da674ce39ebefe7ede9d49a18027cbf3b71f4a5ad164bb4541442a442ac2ed79ca73a5b4a85486fadb5560891cef272a9970f6edbd36d6bca08464f689911a0fa70b166c50f2be896a46cede10536586badb5d7da56cbe5c319fbdbd3c90f50ecb265b7e89e3675c24f73e2d457f10651dbf45ebd78a354a3df2cc8026d01bdf7de7befbdf7de7befbd9f4e7ad08bc416038c1f8cf1039c264f9a604c91c01a8846617aeab4e7c4c1f41376c521a0343b278e0bd469dbd268d29ab41e88ec328b382787a7ead49d93f82b549dbbf15bf87165e37f55a76e9c8431deee64cfe3c3701ad6da5bf7d4aa8605a574cf536d579b3a9938ab8def0dedaa6973e2d0cf89a35dd3a5f6794defb07356c9353c893988ad4dbfede9a4872176d5f469e7e4008010fe831d50018218d648ad43a8439838f53d415608215823b3c71a9951b6b64626ce0de26a97d4083d2104554412eb35ba63801d789893ea18608710ccd8dc656a57fac49ebb5c6d03ec4064c6e80906d8a1c8d43193be1178f301a27d2df3d6599234451cc4e9840726db090f4cbbdc76e561c9269decd0b4a9d3b6f742277baa4489f6dd2e50a77d35108de20104dbf4461b884651277a828824ce53a5d913448d36559a38f33d414820b1ada64eb327e87eb68062317ba8d3c419291613679e3a993ddbecb11a286ffa6ad1f0a6dd7979ccb59f339fdde77754c0eed87cc27c9e32d99dd7c3d4fff97dd67cb23e1f83dd597dd6f83c45627744d7f5e769b034c9ee8c600e5de69de32ea3d6b3116e1ce72ce3a8677a3cecaab1868dd778ece0783dc3c171967b638d9ec1fe9ae7d146cf70b07c846f238c9ed5b0f9cdb5f1a56baeffe33146cf60625ec7193db379d16ce8659cbd3e9bf6a69c4e8a24ed1c336fe12065661ef3709ccd68a411c819f899c3bc83d1482992e62f3d7b79f8188dc442ceb6c3dc35cebad75b678db357eb314fc53cf59646b2e4cce5e1b97116a39170c899ea31a977bfe32ca5910640cebccf741a29869c7587f94b23b5c8d9ebf7ab7176034723bd90b37c98e3d048999cbd6645b32172048d44c959eb31bfa191347296facc5934d28a9cb13c7ccb38bbd1482f72f63a8e69241839733dbc8d7fe3cc8646eac8d9ea3135345248cebecfdc4623f19033ed3017c7d908d34836c8d9ebf6d74837e4ace521f79f8eb31a8d54c999f89817cd8648ad91522067e167f48c6b9df5d555178f44c9998f1a69879c7187d133982351f2f8f7a719672f3d43a2e4eb33dd39edf432e38cf5183d7bbddc7597b79ce5489484d1b3d96c163e75f0dfbdefb03946214dfb65748d317b33ef32feb037f32d230df6669e659c678d39f6667e35c6606fe6552325b237d3decc87a3686fe653238f0da9d6f8f23ea3f060c5c3a24aa5d22feff38abd7d4b4d6b7f3eaf356573a7fb7b771fa4ecef19c50728b6a771a3f72dd43925d4d8eadda89da7ea889e278e3c2594dc3bb0c7778e5b61c7a6f8e3c6594fc5ecb53e591f37d2bd7a275ea54b106f13517c99935363d613bf6f5c1d1cedce9c0bc823bc87bf10e4740b436085f1340bd67778cbddf77d5fa74b3ab465b09efb3e9047fd6afcc15fdd6379add54a6be958812008ae5abaaca5108ff555b01ee4913a057be8d873e27475779f5910e1607b14f4a9fb7b141f8e00bffac1afd557678daf9547c183c75f753b5fc3e0f77d1fa8b97f87d5a5ddddb5d203df3bbbbbcf283e34b127e8c3eeef29950186760defcf96536d0b43558cb4c7466bb915d1e330bb1caa62823eba5dfe381e4bcee3b2c6b71967ed425ac659cb7a4e1c5ac5f4b8aca7b3caaac2fcc09bb7d6da496d91a32bb6926096824d0131b567911358cbed19a508124017bc463608625340f48a3ced32cf9e222bd8ad1d782d6f7e04126714225776941c86d8f3ad3da3a491b4e93f027894524aa9bd4994d28ba35c1962adb5588b8246151b850895286838d9288e8852841e766b8c5284a31b9e4165e2798cb52d4a0e44b452567ac032f13c2a4e4b7503a2aaecbbf175b6f9cf73c5c45c6cb55cb3a7fb3771b22ebf4d6fa6f41574016ffcac85eec6f7fe36f9b010ddd86a41e4492a8a0ba2c85188a88003f78c42c4891dee19e5892cec6bbd3dbd77ce596ab7afe38b9bafeda5cd5a6bad5e688bc4b969cdc188c9beb7ef993546efb13db69ab518d38b8164d75a1a78f6db4bfccd8e2fbcc36e9addae1ddaf78b321b62e3577a42692f7477e909e5cd3d6adbdddd7a5eced7eed1fc09fae077eff4cfde513dfb69776b1bf55e96ddedb7f1e5e5abfdd569766f2e5c20bbf18e6a338dda9c8e6203939d0f608ff5ad1a0bbc20d1556374baa04055d9a52b9ca73587585fbf3da15cb9b2eb6fd1aecfb13b3b762c597ebbba70885688b39c4d2d404086d9634bd7de31566181e71a22664f285788583e487d019302c79e50d00041093668b520853da1100109660c161759a49abe269048193963c602c297839728304cbcf68462c5886b4f285688761114d74b6a6a37783944fa09054a8a5793903031352515e1ca11a7b44e4beda4336f594f20d5c35f109a8da701380a1a6082b1e7bd1f7b9cbddddb3e3be297f7b314b25f06d8f839bbac7bfbb4af7bce1326174bf6a6cb9b4b271188b68a4988e5e4269412bbb4303908b06212e2a6cbf27e5a7085793590ba95b88be15356accb217005d5b62f1838923bdd150cbb2708c3a6833bb30001de138441cba1086d4f1086011855b15d1c28c97b8230e898811530582e7ad043b72708834dd2095ae05533566003177c7b8230c8e450021f8a00f70461a824302ac3491530b478b144d194704f10060ac60978808410515124f7398a7b8230a86ee005126914c996ec39d5c663a90161e16e7cfb570fbc8168d4eb471125b9e37d375002ba73071234b4b72b60925e8f9f9c7aaa1b3f900395beefb0e08f52f775dc53e0edf81d8f403c708f1fa5d4e79621c856ea74f8b9bf771d993aa7893a123c05629f80f0dc3d3bb270836c25eede0461d02051477e1a8c01d4441d19bed3441dd97d47b5230b00d8a5d277198686d84adcf7efdd84214cbdd344330c12dcd5a9b45be9480592fb9c3d2dd400061367ee166af03475cc3d75792be843fbe4b8d910297a60c905edf3e00b4d905df7c3fb8eea82f6180220041492d3425ac801134240c90113424421b90fed09fafc28719f5b860e6c25224c722d5b3b384a609c39e0bfcb764c7bc3cdf9247e92df4b3125ea25fb0f609741585b86e90409176ced3ff3da654b2fd81b2d9b414fa84ff23352a52b27141b8a6cfcc382ddf341565b863bc50aa25d817e78b707470f78077f14cd86c8214bf84324770bb29002ae626f8a74079f64f4a9836ae29ea068124da24b8ac86643e492fdea07716aa019b54b14f64f10d69681c6091a2c215b7ae17bea44b24aa48b7c92a0955d56a24f1b5592fb243b3dedcdcfd4e53cd63f3e437b3e486bcbb00023667b7e4259a2ca2e6b91a95ff31a80b24b0d3cd9b38892ddb97148253b6d94bbdb11888fbae71530c9ee1e55c02481c2a83d5f9d660fceaef76c1a44f66bdaf185f58fee130a9416fb3535038648ee130ad4d39e50a0a2ec054c92fb553a2a81e43ec10414cd282bdf51bbb1688b8a8ae274c6da17481b7825873ee0be1b74b70bcfd6e0cb3000534d46285e308d50ba88b25b148c255465c9f0c248d5b935b6fa00ab862b79cc2196d5a9955475ca5a9376ad47549d4bb4eb84d245ad5aecfada347be8d4f09db6d255ca7e529c2a3db0ad9e13a7e6a1adce74e1394064f168441dc42be6d0edd053218501a070428f007876744c28210024002047470e1c1c23dcb889d9a86103abd1e39e3fcd8c4ccc0be6c5e5d2c2d262ad546298023fafe3f2a6e1d9736fd37ccea2ebb192a7be690862499b422b37897ee1cded393c54cbecc1df5176affa36cab6a78e4719bef83ccaf25bae8db2d665dabd73a38c3bebdd48c588214b34818108960401d61c0683b52e63b18e04235997756f6939eb0b316c100426b6b04110a8a8542af02a2d53fdbbece5363636ac9696969649832a9c30e102307eb89204044150cbc0b35cd672180c0663b158a9540a0a0e821004151a9c317b227596cb6a6e131313e379de0a74a081152ca09a4200c5bbcc652f87bd5e2f192d43c10527b062044a20510426a2cc652e181818954a45821849c66862c81058f4a06209c390c5e572812018f3411769c0c0c80e681881abeffb58585852a9d48b490539788111327438925aad569ee7a98ac801d1932184714409ef3397d58461288a62b7c4174c3500c2882590103f73d98b8c8cccf77d2a954ab3224410948a74f1450a5a5a5a5ab44cf599cb5a6868686c6c6c66b42c0354c4608c34ca90c2082be0c7cb662ef31f0683a552a9d20745a480ca18455b1091fa7899cc696a6a6a3ccf4bc00a943002041d8a8c6c68696969d132efe36534ffcbcbcba86506e8c10534f8c10b8458c1103d98bc20428923582091848c8b8bcb101557a6a48106104200e1079a56ab35740492232298c28c1bd0d8c10c8bc52af2451551461c21c204894c4c4c0c11309aaca8210645caa042f37abd8cc220e2034e40a0822d803003030333adc0408a0fcc30c2851750d38c16407521c618548871654e09e2481a5338e1840c124c1b6481022c862005085850c6cc824a122c9408c1901f9ed07cdf27001d8a8060a244099a0063c6c6c6a604292700c20b223ed801090c06b37184087610e58a143310a2a6a666e6880d6a98dd30648b18bcc06089113d60c1931138894e20d1a1e8873148c02464430e4b64204593a6243040b1a2c91549000183223109c02205452280628a238cbc6262627c9ef0a189252880c195252a98d7eb459600073fc8c1061c9c6007d0050303934aa590e420041652e0e008aa86148bcbe5f23c6fc8882f88e0830f3a6832240c6fc5c2c2228ae2100f51e050e405471cf1400a315cad562a956aa8861ba0a0046154f1441739507d611882202804273e300326c0f0420c24a0cdf71d4905f24ba55262cc86e081531753b4c08b54cc65301b1b9b1983218a8c8882ca163af8c27b5d560383c16614208e5471c5164564e00311e6b2979a9a23a940d6a854aa1c393829cd6e7084072454aecb5a5e5e8ea402f90282a00d1ba8e0498b279cbeb802b25ce6d2d2d2d2f2c189922a4f517051e597cdbc15131373d9eab2968b8b8be7799a1846784004910e466378ff653267bd5eafcbc2cb58add69154205ba228322006612039c348922c4af0cb681e03030373d9779966b158d7b21e553e9006133e30a20815d0bc684633f397cbe5bacce6b2516b1da365435528f9c20a1a4360228230665e349b39cd655e97c91c868585e532d8651fc7f1a565434b6ac0e20ca4165051411534343497cd645e3493f9cc69602e8bb96bb55a5d567319cd7f182d1b4262848c2830f0c15318456666662e9bc5bc68167399cfb82e7b9d250cc3cb5e2e9ba1a1a17169d99091227c48a2441947aa484246e64824903297bd1e731996cb60befabeefb296cb6466666658b46c0ae10a0e8e20317285524c4ccc65339817cd60fe7accea32d7431b1b9bcb5c2e8b9191915969d9e4424a12299ef0c0862168f17a1d8904f275d9ccf5a2994b063ebc8ce51f0c06bbac75d92b26e6482a9031a1964d272918c10f4804e108164530303097cd585e3463b9eb30df65abdbd4d4d45cc6ba0ce6f57a7d5a8663c6931c82808512a320b85caecb66ab17cd5667b94bcbbcdb5c161ef6f2f27299becc050373241548181b2d830d4112450ba6400193182c2c2c97cdc217cdc2afcea265e261977daf696969b96cbc8cc5e53a920aa40ba66521940e98a670c144124eac56abcb66df8b66dfc3afb44cf59acb6cfee2e2e272d92f5bb1b01c490592a546cb30f0c50e55c0500193276084e1914820c3cb6cfe3d84bda5d56a5d467359b85a1d490572358128356012620857fc0083ef3b1209e477d90cf6a219ec36ffb42cf596cb6aeea265b3238d647d866449d6917048d66533977d9f1d6d64f819120f191e490532f43c6f47b9a20414348e88e2838d8dcd65b39a17cd6a0ebb8dcb652f6fb55aad56abd56a691a5cb46ce8c80f6ad002084a0c7184133018ecb2d9cb8b662faf394ccbc496b36650a954446328a9020c2935585aa2a6e64824903597cd5a5e346bf9cb6bb4cce5ba02f3864e207c1184122ee8e1073c8cf1f2f2d2f2172d0347168bc562b1582c2d01190c2982698a13a4b1031cb4b4b4b46859ea31c08cdc008a3086c08318443c9a1f2b2bb680f221092bbea0429c5961de50954a1543110f6270831122462a50c9ec98371404c1247408a2065dc4e0890f18015d1ec323def39e4aa552433c1401a585132b621471c4e545339754ebaf1cd5c517cd5417ef799e376960a50915479604912444eb45b3d65dced232ef2f97b10ed3d2d2d2d2d2d2f2213c648b8ecd1b0a5ef5a21978d545513c128c14b5ec45cb0490c492275584c0081e30b9b8b85c3663bd68c67aeb2e5ad6bde532978b8b8b8b8b8b8b8bbe983af8a259eae0552ad59160a44acb5ab4cc023510821952882841c1a4d56a5d36135f3413cf7a4bcbb8b72e6351a9542a954aa5d22ecd7bea4533efa98320086a594bcb869c80a2e28c3470d084104ec062b12e9b69e2595a96afba6c058220088220a845ec794fa552478291292d5369d924e3c80b78e0a188299e442145f1482490e26533fca299966d072f0b53a9544ae779438bba732f9addf38e04233d2d03b56caae00a1ccac8210a23b83082947ddaf33cef9e77ef4378484fcb66f745da6533241752fb0c2907a91d890452bbbd6c86f442769f21cd90dd916064f75911f7fca2ee33ee59743d96c3530a6d426a68038678f45696a52caea3ab0b61ba52d1958aa54a7dfe054624c597c89e3d68b2cbbb6d0fa2d89e673f4a6d0f96883f4c3132a5dc0368bb1ca2dcd69ac47a8f8a4c9be68d1ad9f51a9d5231b5727fe9bd4ddad48a664b5b65d353245ea6dd94f25518fcf2cb297a7a9b8890f1113eda2e930635650ab6466e5375a23590f513b4c2c37bf998c7f799f3de7be7bdd3dbacc5f770ad35671f3e57db7a45d027cf79b11e270e067db40d44a37cf03667e6baaef338aecba00f0f8fbde13aecb95cde0bf8e338dc591ff5d8c3f75e5c2fd61ce853e7f43ab0de8ede463f51fc40056cd73ef005baafcf5c616eacedbd97ce9cba3e17ece179413edebedec6dade876e1f3abdefcb600f6e03d1a80c622db4278e8883f774a5cc1edaf249833daf27c50c1e538dd56fdbb631d89b5a752a50a5983a3f33562f65db26d8f595c863aa40d5f19c7ea68ef764c6ea3d2629a35645cba841316feabd2753476362c63ca7aaa32da942a362f678afd7a6983d4ca4983d29d0a2d098664f7ebd06c5ecb9aff798ec13b3c789d9c36a62f6b498983d28983d4bcc9e25b347553d263a80a33741e128876479a9b1a0a6c072134a5a43a5399fb1eb3722ec7a0d8d5dcf7545d8f55f1a3b6ad7839468b7c60c4c9c7ad61864e2d4af460d4c9c89a31a81264ebd38029938f5e1182434712aced7726f543ae2215bde712dcff7db18440ce2da572bb5e8198092cea80ddb847a0fe3b78c348c1b6b6d77373001d51dd4c4a961ece0a833d85a42cea2682dcf166becf39e3c2a7809155654b1e9922dbc7bef8e0a1045ed5e4aeb6ffdab9d3edeb6f7f17cbc5d2fa40ed95a87989b02f2b757ca595badb5d6d29953f6334f9c89336dddb5de5daa54b58715b5387166cbc7d5e3387156f9db67dd2b01f9db5fc85ba75597d20bfa88b5fa840a005fc89a67d33c9a4feadb40342a9c7b037bb054ae6a15119dd55e477357bbd405a2513e342de40ee1ba89e1596b95b0a9241e0861a3f06007cb83215c116c941d98b16ddb4639db7d963a41b2032e78701cd77951a22889b2832b7660054623010aa0f6dbbcf7de5b694ed9d317c9e3ecc99f27d2138cbe0fc9a4d112102042e206a251f9427977074fc5cc9ddcdee1b14e9c5c49701c72722948def9ae76377e1c67cd8cd5a7461bf3a61e1c77e64d556157189cf3cbeffb71e751dcdd78274ec6df3772a4a77ddf98c98df4f2f78d1ae971df3762d2ebbe6fbca4e77ddf6849efdbbe2ec54d6d6cedd448553b35667b23d64db437f4b6807c6ec7d4f652a3253dfaa5c64ad69b7a6ddac7f548709c64f5b6760fc8e7a61513c47504040edb8e3eeedec6cf9bf8dbaeb6b99a57e0f9b0c71fda94d6fa7d3e2ab695f6b8d3567ab10f6e7db44e5a53e550d7bde87fe6ede9b415f4d15e09d8aefd05eef9dbd0b6a00f9e383ef77e5fce17833ef6f5c276ed0ad874b637f6065b0df4a9db5aab8156bfac1d83d06cfbfaaaf6d55a1fa38f3dde5e3e53dfbb3368c11eddecb07ef9e06ed5f30eddea25e17954ccdea454ac13acb6eaabbf1aa34aeaa93893444ac56b67b53c5649a2ce2a20c432b6a70f923d6f89260e0db2f283b5bac766a5da7a2dbe1763ac699aa6e241c5430679a878a852a5a989878a870c1e60dbf71e37f3cbc7be9889773f27a535e75aadd5d3d27a8457ef85aa1ff0f7078636c5b8729d16bb49e4fe679e4e7ac3248d66dcf367de2b01dcf35fe0f40b5e5791ecc49938f608bc5fe1de7b6d5000f702a7b3bdb1371a781180a7cfd0f636f0feb57dcb18dbd1c787be3516b537d0473b055dd0f4cbc7dd38d087eebcb9cc4dcaa5f069d49ea00b9be6c6983467d0a7476ace3d54291dc2abf77a7c9e057df2bc3e5727e0b65ad6e5aa626b7b18ccf9da7b6f8f3b71e8efc499f7c21f46b39b59c803f4f942550fe2057d38d0a71cda17f4f1b8c9f9687ae22c2ecddf2622d26ab1ce7584d9b5c3e16ed4527a91eeecb9cfdf166df7e59bed4ef568ab55c694b0afded04a3fcf7eb5febb2f1ff7de7b650f61da39e1a97610cbfab47d286b52952a3cec408b14014199a3da41cca18c5d9f6a8cde8a07048846799783d51df0f3f96eaf76a3d152bdcdf7eea81591def67963f6b86e1c72b2bb57e7047b789b06a231a79a40ef95f035396d6ebcfb7ae3a4dcd23d123daa36d821b5ce9e20229e27500d4448dcbfdbeb29cba6b7dfad3b464bf455f54daae236d0ea29661e59bf7c7ce77f67ea1a27ceedc63ce69172209773972a856caaf3c4a1cfdec8636fbc9cca79d3ba318fddc8137ef86ae236b9bd691c72cd2df066ddb9e464d1ec32060132880022892c6a2841181707227a300383092747827006a5371041062c3862b6c412241dc852c462c9bdc10b1ed8a003922343b0200bbb6794165c615b708585a2b46736b9428c18804006370041116f424413226ae04d98cb355d2e1a6aacc9afb5619da57dfe0579701cc7e957f71feef81d777c8eebbe7d68d3b1fca8d0be65def4437b2b5947ec12ff607d3fe0bd9584ead8b3e6cb69dbf6db8516637ced43dbe66dbc9f201ddadfbe76ccd4de00aa524d9728014110b4b5b504dc34ae7be71dacb585a4d6f02d24e192da1d467bf875af9b86bfd4b521782f95aa5f92569256925692594b89d6626a219976da69ab6d2dd9b47a2b0251cbddb59732d8efdb412f6541cfbbe71df43cef20a84b1952deea9ef85a2f56b15ef5954a0c53a02e3fbd799d6e256925116bfe01d4ddeec6549bd81b5ea2441445115fb1a5a4a5a495a49544a494de72bbe237fa959e561ccbd692cbe1903e1ca9eeb8534caffa54d1dfaf95a495a495a495a495046a062d259795d442725b4c2d252d25f6c9de6ccfdfb927bbddd3f6e9db55f52d7cce0f594a76be7855a8bd6da4507bebae7d5a866f6fd7ced1df9275442b492b4958b52b1e154aa97854cf3ba3de4675fe86dccf82535524fee4e38dfbee616a45c9d642427550bc515d89aad1d17d7754a76a76315eb2a9ca48d44eeb91aa48a45061d829968aad725b495a4a4a0cc542d9302c95569216928d9584a9ecd242416dfb56127a02d6e510f7925e5b060db19658a8cc52f2d929db1e8ff889b341c8a5be794abfbb5556731fe8438fe6fe4e8978208fcfe3be775d67e4f3f1a552b6a37a470d6fb30b37ebb2daaed3a99732d8d4eb1efcf67dffc0eff6fbbe0dfcf44fc8d987a12eaf0ff71fcb715c66722faddfbde9e12fc5dd5a6b378ee3b8cdeea8b73b250296569794c8de0eb31281b63baadb7fa9bb53e3f7fd76233da23abe5390c7a6b7e751bb6a144f7b745d97a2cf39f51d556fa737a4bc977783d7c679eb8df6f9a4fe93b5544a979aa637e04db5eefefb7ebae77fddbdef1befa679ee4fbc55e7067a02fd6808e9a6e27df53c4ac45afbdb7529702cf3fef227c879babc7b7b370e6d6e94e1db79abd7469c573e5022f4067a446fa0442811fa092b737288384a5705e62bd2ae4d449a76dd2920704499020a070e1c38be69d64a31db60293803d66ab952a083bb4dac41d920881434d9b686b4d6f68006db7a7b4671828a0cac6812cab91de674b2b5d65aaecc1bb65d6badb5360a130858519c18c23761756736c16031288ab04b580e57311287c20caec3f8062c4ec39aa66932f832d630c6f85e285ca06df7defb2bde2850acd4fb7cad0605152a4d73502ca9b058827c390e8a246c390e8a1068b5878de3a000012c3f31c65eed19e5892ebe1460aa599109927db77d6147c08c83b825f13426330d9f71a70fae98c51b7c755fdd2ab3a64921834f8a2bc428cad85d936e0903493850d1441a504d50398a15e18402226630050848301426b5db74f29237ac049e9c1c9b63773afd6cb72bf084e5c48e73342b5a71f1afa6a26bb3b6046c99b88bf1662dc65889d9a36d6c8f60bc650dd3c6d8cbef4d2b75d73a9d536fe8e98d80a1d2b4d53925d41b7a8e89f8da338a911e76b767142348bbccd93457b0073a1747a49f74a852ba84370f4eab8afd309a79c08bd95391590bbc08839ced17a0a0072853ce188215e4ac2aaa670e11d3571d8aec0bba62387266530e546c0c853dad4ad35f8d61dda483d1bef3035e0d71c80656a345a3a5abebad578fa2f5c9cfdaf8d558be8803f1e2761868b61cc41951c6eb41b7cc6bbb38df318b5c8831e24bd3ae1adb9ef5edcdf698bdd9b6ed3e782f18715eef45b4777929d4cfba23d2aa5d221d80e742bf6d5b923761b9c6b40f07628b88df82b7e520b2b0e6b72d48bfa37ad3eec0706c7b1788e5b7ed9d38af6bef02a25a49bbfd1743aba65ba2ad15caeb41374be5adc4798390b26794209c7649b3eb73ec192508239baabe1ac3746e166325fa7ad1f589756792758968775e7edbd2a973ef9dfa55872ca5364c5149c32217a24a7c5dedbabdc18fd91b7ca3252ada402c45f1ad1c7a83278a296f569d192588a33a7a1e1545a325aa8d96e6873c47ecbb6e917ed2677ad7150d804111370822690b1120b936b400c61835e84114688c41c41d52c40c88262db27852021d215c2145071fa6289145cd2e79b6fd2cc56dcbd8b6393f2c518318c221064f9e4cb9028228946ed3ae9508110c71240442cc9e396705e2071f7ae06187223ae44004871b908e6c18329b3d73f6cc1a26d14e9a37f53c3cb326cd9a346bd2ac49b326cd24984b74892ad4252a6b92f884583475ec8cd93077b67943444f9853077da5d9f49a2e695c28116eca61a3aa6b604b255691b5c3f229fed325dddfcb76bd546deddcdc79b976ed74eeb45cd3a50ca368775e748e4b4f7be3a2bd9651b437f8a2a87996c70e609293cc9f3c303a034ad40f43e0010969cf23058924594841daefb03b2840c1e40727302306559220ed57288115337c4006f9021b7c700111c608c1ec05a47d0c2aa0c119528a20ed25607768ac10821355d46008117a40da57e08815159c41daac796acce67c2110cb6f1a77acb530b46979ad6d19654bf9423ab2c5012e6f71d1d3de0c6d0e54c024b3bdb13bebf20739c9965b72823e549b132d5e69dcddfed3699ff9d2c6570f1d7bee926e4e03bfc37eb16d2dc8a36ae316af812cd01455a1628b5564198d9652d758ac8dc56205a8b8a8642b98692335ea944333030c000000a315002030181289c482c12808f33051ec0314001099a646604a17684910c430c818830c410610026444a4044848d200f446d6476df737a1052b1164afef77a6f9565b4d014e2a0223e2acbf80d5f4f00fdc9b516cdbe55f912ceffc2e6ff6d5b1aac02f7a8c19ff59737ad3fcd837f59c1bb4e2582112daa9ae75e74d1190d9b0f6f2500a627e5801d07048019b7f01f3001d7cbbb72e498c1eb4fa97407353f8a4b4b19d788155ca8f950e73bec02223f0ca2e703d10682124ecb13abe6ce45b20072ba53ca084966c9128c06d60ad5780321d320cbd647ecca36a02d5d3d3f7c34aa381d136a1907cd5f379665b13f7d60de5e32698c22659d38d0e3dcadf8c58967b46cbaa5c2d952a6f5b17842cf4d0325d0fc0f41987d9a2defc871d511df3e26ef2d072678161d900bc1d22a404b44a648c8274f18d5320041ebef76315986b361e92f77e4f5e521d8ee30e6d9adca9e3deef1147b6b176dde2fe44f8303242d94b0306cd8352b0846c51093232c661328aae354f8b86e3b25c2eb02a70730951960f14fb60291cbd49d78eaa3d9a8a707336e555863588aeacc7ceac877506412e35591c5b1d0b6d36b090b42cf0104829abde60d499bc585a834a596f0672ddfbd16e030d932533be92609e0f50d0e2b65b2687dfcd8183f7b0811a2c3efea3c13d5d1ecf3296bb71eeb01edf652061be5ac80ad498f4a1710e2dd727754d7ec48b1e3d83dfe099d076b370f6b1f91fdb0f4b689c4058a918ef0f995d82f543f999d0e361c711b597bb26e375bbb962c4e38cd33f200bcaa306ff031831415316b7ad9495f3514f084e4d468f479c013b87b457a05a2fc3a6a668964795a640c58144a77375b0a6875d8d92af19bc1ec5b2122a23193a0fd601bb01d477cd07620d98e1a99daf42d8013615e16e697272fb25c36c8a006b3b8a2f2d06f02912625d0b8b15d1c0c5f33bbdfa15182d992371039a9d967686ff212ae677d8252f1e7cd3f8863a7d8fcf4fa7781bbd5d51c2684335d997c03bb3c88dab53cc664ef1c30753f846e7b51ee7bb1d6248deacd48ac544735e7a11450f1c5d2201f9077ad686f2cb9ce0aaab05fcff8c73ec61b777b0f90e35ebaf7d73c544ae63bbc98dc505e69158184987129f47c0e7137b862d7727cf085e0eb1bc008c9cc13e52bebb569688d6d2478a199506d7bed6a8177e91f1d521fc7f4f39c1adebc2c6905eddc18f6304081af6f7aae44754c4495497a8ca9c330eab4cdc9555260ce3573c257332f74d03f16821bf466ac0812ff422a501d2ddb6ba5ddfa1b318d735285e6048409ac6fabd37fd80749675e3ae652fe84eb5ff004b1c5b80b2f67fa9f8f6de7d20195de413da3d8d44419df7ee5778cca46d19edb2ead0979ac4fde5fc471b251c4096fadd47f49219814bce3f806f697b20998af87da1d745c96d79e27552cc890f040ca7d86fe29a78de18ca5d6cdc8fa0d79eb2d0a519831c37cde3f12883f28c2eb9dd731496b31d8409ef00625342df609c2b650719640369b85fdb2b836eac75e78ffe16dbfb7f62be0e2c20379561fe9e550c75e64fae80a28bf758b090bc58ce4e8753c4e4381579807f3bafb85a7b418c456c73a75786183b685ed910d221bf96a74df49b12c9db190b9aaf8c29d1a59032be8c08237ef89fc7f48205c838b1786fbce7ea42cbfb9b92696ab117317db3709abb6ea0af47251817f3e05830ffe6013538e9ff397254d67a298fcfcf0d919290bd816074e44ce3074f8ea6134c7a4ba9cf5319a025c241a0eb4c89bc4c8dc30ed0e312fa8cfcb45e7593dd1a00636349d41b793f2552273227cb4adf1ce87c5ba2040a9cc63a92a80406988fa397d4b4115018bf55da848a18cf3837c013ca5dbc95fc29f0c5909844af5b0d49e4c538f55117dacb78297dd785a4d2e57cffbe9b6ce4e7241e5118159f5e9f65bfbb80557d1895e688a5504dcd08ba93701abc54595f82b4ceeab79815fa9baf120a421359b6b70c4df8248fb54c365476cf3d553918d07dad3e8c2d85a6fd6cc74101059809b3764654a7a2f392d2d3b4f491d2fef4c919231536688b9b9120e9cc49b46810f80185199dd1303a56eca1b9ab5b88d4dfb072dfa18925e81cb4a0293c07c67e06c1820833eabd10813da11820f9cbfeedfae8f946fd752ee6aff8d0e8757da59c1368e6528ced348de57d9cbbc057b2c7d22bd79cc8d0e116a76cac0febd63b246af91a470d3e96ddff3488b8a2cfb32b803e1815edd8bc3f12ca207a0d306424948be9c48935cd6b4ae67471acf3f3c49ca2ae5bb1976e1d33e7da42101d45bebc31cc8f10f4ad4f5815ed1b706d2f984c53567078cc8d544a6791e7b49cec3720792e1fe56faf5a2066655155a27d9f3eff76995118cc6a369548eab4c04f47ea48e505b096c221012aa13d33c5be540df53f8f5e794799dc164c395ee3d3dcffafc486285353f3165150bb85816b9a4558ca4e12e9a82c4b6d9e1736814ffb85f1563d9a5ba14bb502e37a7eb2f18a734a41f4b1093e49826b91072f04ba64ace511ea8748c2ea6e0d9a6e0dea0aa5a6ae10ef7173b8e4823118fc5ac74bb1462b509ec2858ccb825bb01b70465e76a151076bb7d1067b9c18be7bdc1ba9990ebca1d92ca06ec03214a7552731d4c61c75b7b6751089b5219dcd5b6f97b6a65fd370a45db3d8a318deb61389561381797837f061ba0c963aca4b2c09e382c6f7f390d112bc98b60022953b0b97ce4f04de6ce3a674de960dfb3ac09a9573338a157337e99d179776d7ed8b92a8be241c58bbf93bc843d5e4dc8fbde2ce011ce0a277e14d520cd224ff8656411be94a304efa9cf724f355b6a155a1055652968e15315c007e00aec7a91f8ba94dac9b25c573a6681dba39213af4e91240d3d6e029e7478181251ae1c12c5b9e6d23aa76b8203daad045c7d3bd1076742858f92cd3ce620d9da920f5565560e17f7b54904b36fec5bf1208b384c98469401332acdfbc716ce0d5254a6a8e0075cd4fecca520f1aff9c0a1955c60d4495116e8c70f67abac08c551c2722c54f75e0489c233c1490d24d03403b1c82370a48fa9f742400c713cee3e531ad1bd41c39005210727f6216daca6a40e2152aa6a6e7e63d1ffbd050b6c16612a1d1061023f0505248db6efde795387cc7a095c8060542ff80fa68399f1d61886a9b88666f3f8fcd9ecc04814bde76c737f397d7e31961409ce474a3e4289e0102d0262f9213a9d67b7fb92f50d4b21c0335c4224643608cbd2a44ba304c6667d2658298c3832c8e65cc39a316e6bc609ac6470413b1194a446b6dc95d1ee13fc7ab99028a9e06ec530d1974ee5241dc2cb02b0cfb845749e5a02d2357c083308c488733110b57c382648202194bf2f6ec2c6355a31b708970347efc4368c473674fd3b75a4f0db2d7f38e8a700078f3806f95b452f1c7f78692110f1ed2a6a46120f1d86f931a239c638b008e826b8ec0078dadb93accbb57403eb901f469538a535e181b5815e9564ba5b530a05485917320c20818f8c189435da0420f1967716b414a767671dc9328ff179a11c2b961efa2db120362e7adfc2db399ead960bdac186ca1ae636ed587a1b2b32e2b7d89a4e8acd906161874bfcc384cc8117455b48543e60c25672d297b46b8797cfdcab1defff7f23bf88cc4b90062d16402b03837330a33e488c407608c89a9141756a7afde729fe676d88ef5212b875a899e46d64d3116cec1ee9fa332c49dc2aff86a03fb871542eb1b00545d74d5e08656eb6832456bacf3a6df68b85eab4b2ab33d637eabd94e41079dcb80f1c59654e53723fd133b23aa7077b09de0151d5bfbe99da88d2c528720a9fe989fb1d0098c6c479637a93021937036e4f3d3547f9b2680d5c63e9007c2c7206d6cf21ef596c62b92f7a0f94c2bef5b0120c90e5ab597ff58d4b186f99cd9a96a684e629add043fea0fc748fca8d2bc21bad4458c8d651c405544bac2c75c9606b88d7a4b081fd60ea474c4f6f4f3bdf999f58244ae78edc987fb9991f3b6b4ab69cee534e811cf95ab4fb00240fc491aa4af03c197195224fa102ce4bc0307d2521e418e3ca4ae2c19faa096825c2cd6d2c6e4ed57c81243d6e7731fe718b3481760b6aaff58e12d110f0425880c78b97c9e288429d2166012475078305ef8109a19186d742dad22c2b786bea5e86b770bcc69a19116987e08991cac97af7dc04fe2c9f2b8d344083c17ed5e28177052dc0321e18665ae4216747911efa6a128bf8bb408530daad3854eb003b8b2e8fcaf9312723da27d0c5503849efa441213bacfb998fedd14dcc6d8e0c1194d0db65228e44ff7a48371a8f17d635474605a00781495007243ac942db25803920d89acfe15067d01c18e8112cf2f7d18cb8024a99fb60ec0164c8fc3b70bf84a1192262b57dda3bf011cc7c416bafea1dc015c6553048164bfe2200e3d5f8d62741a38ee126544e3960b914c818ee538471ad816ca382c44fb5aeaae0655c02f6b702f7416b323d54aafdc0b5a6d82fefa1f9d8e2de1093e66932b82a4b64c87843a4134e4c8e6c7e968704bdcdbd2722da68454ab0b2aeb8b43e66bbea83c55677e6a5886de6036008898e3e014fe01ea4e523cd89088c1510a094fdaba1458d7362f6cf00bab3156885b1dd467cbf4280b57b9f06e1f30a3e0830d69f827e0d1b8fa370a06228e44923fca7d4453a0a9a942d43dee1c56eb5e828691795614040cb3e67e618359953e2e8e6eb9d0d46004a7cf353a1cc48ca7c62c7a8e278205498034204969e3d45f7b49fdaf137b9060ae626b5e52e89c0bf572c3f81f848be25ea206d77cf85cabb524fb506aa65300a5130b42286e1bde7a6837e66f0de9d6c509dbf6ea2e9a2c01c65fd88cd2aa1953fded9858a352370d8bcd88d5faa30a59fdfd86de911edb40c0c157d287c9514e858d28fd80a337d324f29aaa5dac23a076ecaa70d2cf5cb7fcfda2f6fa0c96c90aee3c8b13eea53bf409b0f338fcf83b84c6ca9b61088ee24a906a071716894b041cd25059a58b95e28c987a1a002be2a57e6f8ddf423d89053e0eadc5d103436f9e0b85b26800d0af843f5e15014883be0f9ea83df1c032cfaa539b25f84ae94b2a1875cc7d62b931f7fb78785a095722ce63589e29950ecca91b122cc3ee0f770bd92c7eb6ccd03b31ee548b85d660bd8fcbdec2dab271f66aaa8b5d0f756baca32e1619d6c73a12ef5699fb43a806435e0d8f371f4184fb89184e0303e77b14dcd7de7fe3cc88bb35b5ab3f73bba838d61b0c40a949a30f7780e1674a3f35830c95ca1ef4d8040e17d8f7cf14f6c51142dcb573d15007d09615b56fff8c8c471457ae85c6ed27eeb77d10ccbb158d6c87c9a7c9fe10207253db506b5913596aa093ccb816ca8585eaec8d96effdb4bf03eec3a9ab90eb2cd2e77d104ada1538189c77178f6d42540622139b0c11a305431b4af142672ac5ed701fa85581c956467033b0aad5b7e04c94ff45fe95370427cdccd2222e3a34347d380f2fda213565f73356eba905f57fdf51f5b00d88e605fff4c5c769e92bd027aad53dab4f9ca3f234049542b1898554294fe0a24dcf82dc8fee33ef34b5a21fe7d102e301ff2daa13e9420701a300fa68c39b541ad67cb1b06661222f47d066289e987dd017f2913990f976b4ba7735ceeb8652b90d24f3517599238e1bf987f8a286298429c6a588bd53ad25e23ba8c5fceba2d28044181236a4e497a41e7b08c6f7ead8e79d4bf05f75d41fa86b60d9632f7cecc95cb7ed97e9b655359edf5dad721d30233a076f1287cb7232445c099b77fbe8fee0d68220baac1a0c009e04f4eca7eeea7996e1cc84a51ff700c723e8936d817e4907227fe840730f783813b03032d9a2c7ba07227007e21e72c6617369f0efc26b2dcf9878db2df5e972721f1d42a1c91468a471016142e57685171c62ba7f6f4a27f9dd07b6605de876eb45c8ce04c7b1340e924430d788a26b16eb53968b2b3b8997c144afc5df3481b7409b8b6dfbe275790622e8705d2ea76d7f4dc05a5532c981048f40f1416d2d9eef5209761847bc153f6d297131a2be0ff90c8e47d1bea261c8ef6513d0eb7c9dd35f01f407908837901980018e7d5fd1a40502f1c1fff97f08790569e6b5483e977d19fab98589620507227a4245ba4c00eb870721dcd173c9e7a86838aabac46042d299ecdc890705002d0f6c7d08d9235bc91d90479b6cd300af1ca114e9488ba066e74225c407540217b3524655373b716a51ad289da1179ee02d01f043b9ff780f052c02b74b28969236e60bf18190c4753f76f8938ee54db7df2f8983c1a8ea2a8b5893aafdc593fb9ffe1100b2ffe77a42555b208a552523b761c400b4f96973843811987336e38352e44a95ed9631abfe77265819d8008b82cc4cdef8c0657f1f38d12361bbb230252a1322c121a534c8422885fc4e23e0eb8468ee9d2764815507bd683cf6ca766de8fc0507c1f4d7c3ad25b790e7d58b37d78d45a4b3e0d1744599b5f434542273e8c4c0aa7b1557966cb2aff3ab529abf54410639cb1159b411c46688ccd2ce87d498f798e105d973cae5bf72f1986135443026a510bbdff1bce41d9f579b1ab00d33a880eebf639095c06814a7878182047283d1da7d6da628ac4ede253aa3b02f2ee41c141f06dc81ff7c0ee222768094a7f819d4b07903ec531578d4bc363a40c62f2ec5b01782a8b07ac82b56e2090622a17bc2f87b17f60020c20dc13c91b6f0c45fd8f0ac0d156848d6d7063cedcf923f45bf20004d048ac2ea5520fa541d8d2620b98cbe2eac4c53f4e7f79f073a38202823ac1d4723f9316d06909acf84567de18719f70fa05225ee43ad4c454df7edb4fa6bf369a5b663f5a401631d7908464483ef581addac70a5e2cff096a8a6c5f883e426005c725afdda85557007bbdb59686125f4ee3720a6958ee670c9c9ea46e21cec2f52481c7c1287a1980dd60e1502b09d93db66f46b5f2a564d0e696954c6cfe247be125dfdfa3d200791f6809fd5f64bbfacfa699b420f2178f6862cbbb5f8159e1699cb8d16754888b6b371bdfa9709d2f090073991bf4364f2787ca9f71e1a8f5bfde015c9d3b80b4f57c67130ecb8d503db9a6f5dd21e5ce74cf1482bc02e632d52285c2394bfc7ad1e08201d278f22906f86797c181d52bf8293ae89e214e530434bf3d5b753623d9efbd3e045280d3820041143049e3df1e830829d9cef8fa29a2730b513dc898735296d21cfb604f1dbd6777799b3565be0bb47ac4b750ebc94de88db4fc81c48bba712ebbdd521b488bc51cc0c68a81b234aac8b28dc47719912d4523020ab21eabeee0add41395250c6cbcd956cc2eb3280cb2a926037ec3ac416cfb37bbc0383d1544e7aad30bcf79eb215611571b71c1acfa6c9ae2aa85c140e0057627dccf67b196ca1a943fca2770501ac60f89380663f28b13ed67ed9755a00604b5d5b6a593a0b0d792ab15e05f71cbc18ecf8037f3b020a5a7fc99679dcb7ee77d1417627f3c6de7d9fdcf7e9ddf92fa5716a8a80b69e97817ca1e0e7623d0d9cfb7a2d07a29a0ab3003fb1adb3a84cea065b2895b266f2fc8ef27682d8f49c186d6a2140c6d6848ad82227e722a787bc137223ada6501c0f43715d457e29af9df81d5bcdee46d690145e152550293c979fef00018a93ab1b0a86389becc511681e7381daa13eacabe2394fdbd2ecec66ae337a652da684a565dd278a8325bc73b0ac4fc9be39d5df928464e1498178d032b2c384d98120f65a08d8195349c6aac37e3189fa2c0382d54bb304a6670c250cd08b65cf95d740cbd962f9dacf91af78c80c61d29257dc5661f386980fdec0c5c2345b8f9f653e95a414f00dfcb000b74a6a156a50bff69e7e29bc411a547ea172b7837db25c1c29c4e15d11ed9c7c7cc84becc46fa7cf819e3bc8577a39e9d883d2f241c446379005e703f9224a0f09d78e073a92dbad54b277ba82fa62e54c5b61116332e7619baef010fdbe31adae092813603293860e484e579052ea14ad37248b160a5a3594a74d6ba886a1f8c99e20fac6b750fce211751d3a4b662cd8305fd483acc837dbd50e271ec604189b4399a44fdb1d0863f23015f9de93a8cafcf3c036aca07de621efea195d72999cbe35581b6fab88eec1cbb869f9619b8bb24c2ea0bfc2602fb53df3e0b986b846e514f22196c7fabe238fbc2d6aa18478d3c41dfa99afb063d49c63d1c6fb6ec36c835078526885787a50ecc7f45d5854763a1636d9c6a0c1e2ea06c27fcf838bf44cf757492bff4296ec484a3b3ee3fb9f2a7ccec384359ed02b2cdc9c0d325f56617bfc516ed62d23b8104a7615feb81da5a43bd3f988af67281e408bbaa23b6b53c37855dbb0b67a69997b42dfa150847fa2d97fa7c392a6dedcd92a27ec3558409f6b11a18289c30e487d5aa4d5b1c09934f758a0350ba6a38cbc759e18bd093cecc0eb35f40ff74e7df7971bb63c59d70dad106e95bba980d314d5952436ddb11e831b9cd808b4d649ada437f47ad9f0a7afdbc89084cb6da97238852498efaae27efb6a0ac9746ee000d019a7d88e5a74388a522cb70d7cb91dc0116f2cf767f733d83fa51b242459e1d929f35964ec9039e4ef0f1c89953f0fe0a9ba0f7ce17a48df20bba3a0acf05a56fd3be095222fefb0802dbf56f7662b51d5fcf79b67d5c0af81065b358bddc5335bb578732dd65288828a0a01b6bc4c22728d8cfc603e80c72c0b11f74d5875de0b2da82b3c87b3464fc0cd0570e450c4dd81b4e9c47db19d68c274b36932c4dafe830b6169d2abcef19de35da980be906c5b06d0339f9570806ae7bc3eaab8136120c881c558532a4189a2b8e6bf65511a0ea0bc9cc54d147de01655946b21d5203e0ba475d692fb12f3ba664a57349a855dcf4b8a801f315c4c9ed831890e183fb7b2906c6dbd4b6e9846e809493973a9969e526314d5cd37a16b1b75902e8f16652f0d510827e62c3210159e1f3962277af54976c57dce633392b339be7b3a81c14f5f9aec48eb3e81dc9d5919b9ac3632b2d36f6646dd5179ebdfd5a82a79f5e5b5dec9d11181dcc6e806c1f06e1d288c7302e6600eae9e2843cc1c5502034d61d41d011ef6813d472cea93f1c7d22ca213bfe22c6ccec776452385a374b87541264760b1d901b8cd041b7ba633142c047a17ed151121c08d145ffa0022e15d2d08d2a94a49cba71a80692386464920b9d6fcc22de8c5a63fd5b4a3f9741cd250979e08baf56beb93e9d92601c966bac33ec8af239ec36cdf5dee00f9cef83e92c4b21d5d077f8b91c463bd623663240f38d9af6690a80921ea8ada7b4f3d98976d35688171ae5f9281fbc5c2159bc97b5b72d2ad61a0147b408cd82779f801cd586f52f10ba2b19a32982ba0027f3ba6e6e403b9d46f6075f3013cedeb6a5e159260eaf458a35321800021212b7703e75a25dd4530ae4a5173cc28cc6853fdd645d9f039d8092dd02a10dbf8ccea4da91ed19b0284a696daf1ee7d0402e73a027f150a4e50f22df50fe789d25743a03ba38c088b59fb9ed06d68c3a789165040a0fc5b7a3f5cf005dceb08005b330657910ca4d795a0b19e2ed6012364ad9290dd9b347b2118a7cb1001a697fe87ac5397b9efa15956834c87d3aa3cfa1f73c03578075ea55d232d74c1a77e5a996ce50dc0bca4fa6b174b196a2a85f064e367b4efc2366837047c731de9c73d0de657e32e0625b54c009d60c8f4876c3d23f17803c6f083f8b7aae37b3a4376d454be9cd018672bc7daf1fdd6a0c43b2bbedfcb6d3ccff56ab394d22b79a68e46e9674d46dbf1be260697c085d6cfd1f11240ba6eafd7fc9b96e494c2560d7133f6436ab378282f829f6f59cdce7e863f1fb149d72b8c8e46ba3166d9178a4ce69ab596d6109c159a40569cb27364e5bfa65ae74a85b39ac1109bb2442fd0c782a5f3b5d8fd36e006981fd8da13ac4d4270331718aa98f8574e723c04c4172917863394e2da52f833f113385f6692f08cd18eaf428ed2191d2f806cc562cf86db53b25bc2edb5f7033f8ec24ceabcadaa5ed4203786fe2d177a4387dfbbcbcb4637eadaf5c3b4d9592a16c62b419e4316c89279fb3965e530a17fc1c6e0104017ce6cb20aed9194a8c0bca781dbc6217ee28551ea3f22dd06aa473670aadf34e4b0131a13b69e33b4b959a57c23784dadfaaf7756aac0ccaa362eaed21a4bfe9788a742a2f41925045bdbce36c6d9ecd45da87e52ba77d9c31b68c674d850f758421d9a5000308ff9992b37df891926045b4bd154f06887062cb60dae2bbfb9069f34a41aee75c1008fb49c5a23b7a639713c57b6e645a6be0c27bd6fd0d2533325bcf9bbc640439c02fa1f7e42545cccdc73eb3b648404bc775965bdc00b81883485ee341270b50dcacc6c98b600a7a3ead55d5bd368ced6365140fff9600eebebec3df0b8c1efbd3281cb91c925e948f7cff0e16b18f3322fa01a19a5d00619a0be43ff413b6a20cfed95fff6b63ab95f3955643718cbff41d1de5835a1bca8e581666aeb4aff216a81a4d509931104e9693b7a07fdfcf923edfdb7ed93ca53c8fa0165c4581155f1738d492f718f59e46c73bf47b87c7917102d13e3a3d07ffedcc68ef4eb25fa3091904e697bac949f2dc6eb2a1d367a264c242607a140ffc1dcfcc476f604cbd837d5b0a8d5d684dfe6e562651ac1000abe0defd10300078ed4e48ea7ad8a4102fbd27c4a2e002f19fb8b9bd919b08df8f0413eadc00e75b7f5771b834cfe06131355fc77794aaad51f56d266c52b0d96334d19748b1969a8297a6dcc0a01af1257fd34d93c8c1a8061ac9e97b4019a91b57ec902e83b9cb2b35fdd606472fdd51ce5cf3b0107adc18dbd37262215a616ad36b28a04b71f2141454a9ba656cddccb345d35934b6bb2049263ea770783a906952b1ecfd05a7cbd0f997dd33662b176fe7ca861bbf1a7b3bca2ffbf820cc55bdd95a9a0ed700746787673bd6b4342dddcf164e5a23e960c42589d7a531ebc3a3a07b6d7b649a8d99343fa2b77255a8b56fbc2f65f5bb183962c60a68cf4ecbcf67f0a7acbeee730809c59f87c98f5d20587c469e2d3b8328f8ff134c39c523c5db7e43119bf2524928a0c4ac02c4e416cd548b35fde426701980fe27d3f3ef95baf041daa93859e1b108e1dd936bd3bd59ee9dd12b0a60ef84b8b8adb2bf37cf02ce4b33fe8cf229d312c848d318f378c29fad68eb872614c857b5ef9876fe4d38d1df65c22cc50672b3a9bdb6a704eddc4d0e21fad1d10627fc01c118cf12c3e0dcf0be63aa79ec8cf25af06bde68655e142bdd87f362fb83ae7e54eb39ada410fc80b9a5e78b9ba97ad4b9dabdd293170627eaa45f9730f491c90869956563b3e42c1dcb00ef8a19895cc63890b4e9c6ce1c5bfb2ba1a232694a7bd617933d62e2c97e55ba4649ef7b5c4afc75bd933045b964acdb0a6a8672a7cb4121d8f5ed4ca3f655b7c28897e020293759ab9bab6cef4f1205944d3a325e9f3ef0054e87b1aad6dd918a4cdaebd1d49773a0798237a1b0347f93f89b76adf30713815cfd0e92ebe149e6f8a0901046e7d249fe126294d72c2d6c59e84a0639cfc5085f7efe80abb7bcad48ac288aec809437c73909c92be1447b9608b45be64de2c76946b84edddf6afece00a4b1f71c0bbb6484826ef1376839c851518e4a5258fcf5248b93c8816154e31aef7d248b6066daaf625711e6f753489ca844292acd4c526369f58c465a95a4201214be45de83cbf47d9b63e62b7b8373b040bbd858873b779f73e0653227a09a8b7937faf70a5795a388fce05324be4f9447801cdeff9356198cfe883de8ddaae27924efba02cd88a4546322cc32fbdb1e917d794a8993660142e79190f7de13140af57757776dc72345ef91bf204900967a189bf75e632edf4cd0463b215cdc478efc5c7644458b068e000f3bb7fcadac3bce3ac808bdd63f44874b179fea69da2c4a11cd6bc13e201120f3dec32c7089bf143db7a68ecf7a396c805f2f14a42be46c7721bada5782091905b20cfd3e24147a58cc0ac85b63073c847a70310c1209c9e47a306e124c5fcf01bf193a754b44f4a3fe7f7690dcdc002515cd730283907115f711e764d4fa0da80ee1ac53477eed6fce5e57890c01745b4a6b8fad9bfcc6ffb75c2a6196317158059eb1ed6aa31adbe5bf4e4e761f5e0110f86b76e5a4ebf2a59329053590921fdc42530f293757a307fe01cccc1f8c33d45c72fbc22258ef17192a52520b48709dc0983e578945b2cbf70f691705b4c64f6d9c1e93b61a8c4061106bd0f241a2c51347f3e0dd25007c252cbce3295a4f74c7f8dabfd6f5829840d74d81a213c1292f3cb1854895a5c266988561a17619dbae6c8f705e121da5a0c05154d6df2d5dd7cf4e62599a8cf7c1fa10cb243fb2c7b797b5bb88549cab23228321b8a189da5ef0c292549939fff6daeb20561a9c37aa0047849423a3b492a87eb7a2162dc06cf721cece0588c8629c30bbf08822f092095c33141bf29c4992622c62df13196030aca5366a1fdf1905e1f1b63e23ae10f9b86aaaa746d4d6cc9d651b2048dfa47f635744b5ef280c3fee73ffe36ddacbf052d34b5647760dd959661577e3bd512678827e9f9fbb5f8892b0f169736fd548a8bad46e50bc02ed95ab0dbef8e4cc414c85f15ea29f01bbddb5a8a7d6b8b043e6e62ff006911400a5e82383eb91782c8aaa47db268db2120f4166cdee6f20b6cdc7fb6dbfbb759bcb1e1958bc6e7e54ec87fd4542e025cc1c09afec81855ceb585ad784f361b9e795f6eac9c9ded9ceb4f3e4e6c52a32a430b8b16cc366a8a305d67ccc1f4e40e5c5152ea74bf02d9d4d1a800d819c74985a73b588b78bac078ddbc22172fa32ff3126b29c13c035779aac24591903955d89150be03248143fc43c39952b98337dcb050eddf0d83c2df4125dbf3e081c1d895eba63ab77914a352ace3c64d0b609b39b96c295d16954c552cd9cb4b87fba338b3d520f607a6c6355b80806e5b394a14bd7bb9682461eb1cdaf28a53087969a41eadf362bb754a3def26099299266191d4f163d7b8faaf389163d848c1ad7019a2311cbd9ff4718f25d61aaea11722d1ca6323a58cd8567be490112a67f2f76224ec487704fb3f46d0bb21625531c73f8ce8623254677c88127110fba6a1f03bba3543ce8c4b00d553cd6b9baf2f65ddaea7d89ae14038807f69221c9cbe238c4b4a0826297727cfd46c9a0e4be5149fcf6fb3200e2e3843f7a70120931d3174825966ed11af96b6e204f9e882cc4802195d3f80c4119030bc55291b7de751e266af64cfd05a23a7e404135723ed5e369e96cf6db5a601055f01362947f62dd9e94af62f1ea462549f82cff43625e793ffa04abe84b1f0a023ae87ef3ccabda1b190dd43d1927de4811b19726f5ac4efe197b4e71d0faad338ae492b2fd838b46562a8319aab8048116bdd159477bdc7ea77219d9d8dddad7bd3ab694917a2fe0f15e72a2f0944d63ec25ff0ac30b8aafad2c3485a2fb584f924129be0381e3ae2745ce4247bbd1a8cce8841ad5995f4be12d8ad5caf31d97126a9bb37810128ebeb8b224f84f878b51d6b2ade2f39726b1b9f792dbf421523b838a4c3992421aeed09b693f85d0f0355da9542f43014cf9f750e4d7aadc9cdbca505c5404a41ee350e563cc1875d98cc19f794e21a32694855cb203108bf7e8ca05246365c45ede7e1d5292019d4d36e8f05ddfa7fee95a32451e86ce3ce85115945b40fe0a36c06c721a8588a4e8bd673441c7265ce9ccd07405f95650a4a338977896ea37db10b17e39af6da767c6313ac231946f1359f744feffc4d8aaff9d37acad7243d7be7df7fa91e5ef3ffbfb4672b53a2c7d3fc493d5f377bda63036033c2396279dd2ff61e5f0b738393ed839156e4abf137d25c5df9e1b40f1185be988b53672cbe7b8f787e82baf5ce06dd73ce6574ca4fc01fef2cf1559e58395d99ce361074fd5cd1f37b54346a4a15a795ec2bb9bcb171b866772ed3964640a01f74cbf741e861cb19d60d1a8e014e6aeba6713d997ec5f1d9677d47f496e0d6aa970e76ccae380d839ed8a04bf59426a0488f042d8a4253780dd9faa1f9eff1b5415855584031ab05a332f053f0b975ac28ae6bbffbcfc51f3a6770ab4c881e713bf7addde8bff20ff0deeb22ca4d46be397bea1aef450eb6719d64dd64f58807b72e019a7544181e958e8c4e0e91bfa24b4499a8992c836fefbd2f5595abc191ea6509ca96c322984e5ee68d545fc5c96e1394c2b3a3fc4ffab3b492cf082e211480f65272b5e18d001407e7ed895e7133ccc767c8b14068042377cc6529c56c2c2d22d048ae57746e30c56f7513c6767a159600d7cab35a58bb439cd6aad8a380d51738982a2cecbc0a283152503a02c1168369345ca1296246dedad6a5864dd378b048994559e9586f39633be2ce55c3e5bb551138a387e253d3ee660bc01bc234029417614ffb0df9679436ddb994d6b4e0b4f69f27c8dc436278d2494c346d19d1983ddcee7f730243f1bcc2916f75fd29d0806fcbd99dde88f4d6dc48c2ed8b7a4473a9c3446677653e7ff5e37ff785f70c9cef0ade332d06d37bbdfdfa886388d59df8b3f416b4426f12e7233a7a1bfae6277cae9d765c90eb26f99e484e8b16a6775458db1ad51f18ba9c059d01f7aa3a76e34c12525d1bb0b1304320e451412975fab529e8ad3e950800e2ad7ff8d4793274c215f5a92ed840d9f3b33c3d7dd6ed4024dae3a8b397441a93df8a8a2746d732dba8e6887e3a7958cdb56c6859e63124ba7e61dc18af4cb099130e48ff0ec9bc179af9c8696fd41bceedae111fd0b28e262452a0ca7f2c62d27c764bf5624dbd2aa41d62be05a578fa18a3e5852529489c597667644c1375811d0ff7c99420bbd14a87c7a832564b7473ca7e9b4690db16f134a45a6db65fc00470885f54377591996efd61f50083cc06545c8e53ba520b2d48274408cedc33b201ec0696803b2813a6281258a7770dc410e3250d8341a89f456884ac5063dea8ff11e4b2bcaa6cab43109aad6e56faa3595e522b32c3fdb0e6b8531d6eb6a02081095aef21f87f9db2a79f9b92e10e49aa97a78ee3e4c5b881d84bb3856b0970c2868cd73a0dfe73fd821333ecb380b9b0dfb80acad822c35b14d8e98cba79ef0f3b9b365eee701ea01cbbadfc40e49c716686866612f781484a5ef7f204a69cda0045cc8ca2010dcc0cb50d0e0c19750b4e425dc3292a8581011df6631b3fa8096f26d1960ad08a3de505b55cd219f4d4a6d9b303b34635c4f8d81e570e2a7ca80762664ae5384fd3c32ca8fea505010d099f269fe18654dc952899366a9c536c211ba2519ef06a579fdc17a997f889aafbf5bb0122d2a54c0d523fd1989cbd3925cd69063f4c6cd6a453013415ea5104f6662fad9061176d3b45a846c7ce1eb665b614cc2faae07eb8463dab9fe1bcfe8c682bde64d3bfef3cd318c3f1c44687d7afad26a31f98cd5fc17d8f5259cf84193bffc716c187b35d11f78b41c61340ec619bc8a07d58fda5c0efbfa4aed17f72408fb879b2cc4cbff1d1fdaca53e02b99f0e2c03c660727eb18c9deca72ebc621201054ce055235fd995cee916a1fcfdc26012c00c6d75e62a0a855efd1800627faa1980021ffef915ffc14fdaf05caa050196c0e27f95592bf1ced55b1f0418b6d95c8d0820f2a497fbc7e58d3079f781471e8195b74cac16fb8cebbfff7cefde53cebced81db6833cc61f7efaa9e1b745c3f924c091ce1f46db44ebb6564f77f495da3eb0823aae0821b6920e9801bb0e0fabf4683089976f4602bc83551a3ef2dbdeaeaafce889e5b839e95f646fceaf4274fbc9f0102ad95f586b0eafb2b579ccf207ebe15e89de46afab32eaecf387ec60af396846af9b73eaecf387edc0a7947ae1afed91bd33366d0d65921eec58669a0980a934f081f8cd6b142ee056a24de89af16fff2c2fd94e4b356ca7b62d5e5bf2c819e857cb6d2aceabf39313d637a6fa559d97f19223d9ff6de2aabc07568fc468a119c7842f8385a8f35b90834dc90b4b3071a7ec3fdc965fe1fc1375645052558fe31429f5411d444ab8301b7ff7a9bff3f88544c803ed41c4470fdefd6aa3f80aac5c2456bcd2082dbff746dfdfba73a36045ad60e2238fd6f6bd4dfa0fef0c5d7ea32f8e1f63f5d5bff04d5c30ef1239b70fba77c79704d9b80484c021cf88053b0c011ff5e90d5d900108d0f811becf68fe340c9903f43870d2870aa3f8bb182945589841162524d0a47fa7b8e062cf2ed9d008938755ac002dffa5b4de64c3d0d0e60de00ae61eb769fb4379d985728c889dc409af17b6620005cdee7dc50a424b0cd08ee0df43492b88692bb08fac0411b14ceed20258fade8449e7407a5037a108716273d0e6a00347186c4198d213d4804135bba836e4399dbed75774d8ba52d4e41defa1e1088f3a222a2aa7b901307c325d0f7bf9073dda975e3478e2a2720f7befb93fc2b167960bfbe60815f222db7cb526676f9d4ddc261648bd3cfe351883d3e3e7ae6829c44d98af529c45e1f5f7d73019c44d9cdf214629e1d9faab9e04ca2acf3b237c1a1641fbab643115c744e802007573d71106c018e90aabdb4affa6440ce7ba8aebeed552495680f235539585b53106774952c64e786edbecef730383e4a40069c6bed9dfb15b27323a3b6f3388ac81c9ca9e4d92cf3fc958191e251c5391b550d861bfec6f8da324570f0af0137f66cc92ce483f18b452cefd8e28e7c7ab0cd63c5370604beb3dd1af208ca32d3035c4532de916c6e4cf6796571322222b6831453359cd82f6fe655a6901f3ad8717177502dd0c33d9aac042193d84300c1010e2e1d2a0a0fa221d09d091a803106101ce0e0d2a1a2f0201a02dd1998643065903918222e0fa67ea24a2881c2c9996433603463b90d9efccc3475e58fe3e085464845844ec6aa5478a6f7a98fe693c51b5b03c5d07f79e302e0a5f70ff4b525f789843f210fe2c5aafff43f417dd7fdd808f5c5a1f9f630b3b3b555be337ecf7c265e31e2f6d650dc06ffca797268df934a9e6b4d7911cb35622a2388fa8c649729f81167accdb09d797cbb8280cd21a72b53d56b7a9799b37328191290d53240fcc61154c388d0413fba259ea640bdf36bda793f40a3bfc2bed59ea1b75e444f398c77173dd9d52b1d2ec13a9bbfb835a0404813964906c1301c32c375c8146e86739485203bee1c207cbfb89c96494bb9af6bded9947229cc442a4a692d658d095614cb30beba81fc11febb175e1cce29c0aa800ef4bf14483911c6e6cf6bebe6d42756904a115012b82c91c18b3745b26d380183b2060963fb8881e273d8bcad8a972d5c293dd37830b4fecce55eb7b100053a438b832bc61bad85a1a351488a5ef22e0d47d64214a4699b071a53e3c0d6e869cd08b4b3949f9ea7abe536a56b0d1a25474a6956e6ca0a7f4580047e8bfb7ccf5dd19653762f1e7593bffed11bd3c9365bed7e694ec0a9a5e7862f5dc83dcfbd7419ddf5e7f6a2526940a84cf119daa3ba64578c7156319ecbb73f0f23e795a154ef78824213f0a02710e9a46c37ff2dd84a40df616d265b974fa0ee089ecee4042efbeb612d7a9fdcef409d4aaed4d9733f17090b45193544a59bd60a8667b701a6b8ccd0d7c2b1293827dfa3ba725e8596d8d916b15767950035be6b5be8cdd0ab27638e76db52850df4d79ccd3d02c978c95257fb9b390c8e966e0a603a76bf3215200ff60531dffef92f40c3a8aaebe498d65a73d67996c354f4d7d25ed3438884179606fbaaa9746b455d80e1050316ff4dddd0434c29dd7a68492bb0aa965d7b262c93cd568f35fd87f2cc19e02d3178b334271003b305cdb6ecc4a0eb10dcaaecd0b28e2daa290d15b119e4ce35167f38723d290f28a973395130f7ce75c1719d409b8c7a43c623cf8adcc98e046d21fcffaca163724af94b68d83b195d15200a44a6aa26df3bb8434e9a5b0f293d0bf7cbe71e3a6ac50241f774210b6d1b5a44edaab42de121d8dbb454103653022182130f4049b26fbbadae5d7ceee2c36c98a08936123e5582c012089184cb4be726b1490984dc5830f6a893d7bd290fcf64adac4513f4ab23dafe2981b0f0fbbc5ca5744d06bd2e6d92723de18c199aaca0d9619d8efd2a363794cde142a2aa720596a5fdf781d2c20f251a4f06bfa009341ee30f7e83c053c3da819c4d7e76cc77b55be741ccc248e3a532b9ddf77c6a9d9ed0e7e6b701e8cb4d3aaef4b11340fd51ddfc4e478d0e1305fa479c955a3b3182bcc9f5c906c22dbb939dc378d295ffa92fafd73c40aebbaa454bb0fcefaebe1c71c9ddb65862e067ae8bde8a1b498c114050da1dbe007e14e5c591e81b17ab08ce03f09e9e87a94c3420b3c94e4dbe491386290f99bc6fcce855e45fb0ae05f5af022ae6ea552e688b91d95f6fd5c19f4eaf6e0e279517caf106d949ae759e289ffb3a71379761e2d2b50ed1008f034e4f0234be8dc0f1b9252148757e10e04736eb4c10dd1a69fdf7bf2bc75819a24ad1ccb4217058f4b2de13f29621d0fb2958b576a01d9cbae9fc36bcc3e4545d5714c8b8d17dafe868affd25b40d6b361e4d1c1f866b5053916b2a2fcd513a4e39f43302fbb3a417b386dae60fedff5ba28515c3810367347f81d9ab706bedce609472ace878cd0fd037e1c9296a3e182eecf063a64d1f17a1234ab5c9fd5634982d46ff7aaa0f30506f4ffbc7a4fc4939260251dee51f64dd3b89c8c48610e1ea70d9e27c3192474dacb7ca7cf4c6d03ba814e9855005c4614f6929692b2ee2101ac470ad5a6cfc56046d637b85acd1463dd542cb4cb511f1d2d0efd6f6a4aabe87dcfe2c88add73a7a9eb0818cd355c114e5b1c9456ca9ffb4e4fb4f3de2dbb44f19b9033c04856f56e8b77736f331a757b77fdcc7d51f7f28cf21698d193d5605b1d72125a6693ed546902422f3433c6ae49521d203e4bd9a6c89807a77812d659d6b30b895440c05df26f47f45a999190044ecff8c5660e04a02b8014ec4afa2bf6be71a03afaf4f111f81a07d2353e67362d8df54e3001efa798b263ac44a1dce3d00a60448a0b4b4a9a98b03c2b669dc41fbf95a316310381928c96f98ae2763fd36c5691343fd9b461b89befae60ed8a4f403d9b760ad5bd8ceb20326d4a899288044df883f3fb062c7653791ebc78e8b43886688755240f6abd24bb3e075b6401409cd8dcc031aa14ab31cfae32a9244a0c130bf85225e888a74c67d1ad11061ad08e92557e05d81540035ae9411e315589732b26b5f660a8bcac2b739c9456ea8216df980f5ac5541ecd610e1f3fcbbb2c9b3ce3a2adbd8fe336eee38d8258ea086b82201623061e5f4e505e6c21818a0f48e7af5f6ef3dce3a68401fa8ae1ff52a8e7e55b8376d7b6ab9ecaa6a086004e1704fd051b39ec76f397ac5bf2588f4fee9a8430243c7933f0bd6e206fb535b956c07fd41dab7173ab8074f55cea221d56db404acf75ce888564c7837c534413c16c9456b86d4b8f58f1ed8ce4c9d7b77dbd3f56a34a95c4254fca555e5487208e6d49281944b4a1228facde5e8bc311589ec4cfe237af8ef010d0b371b3b17ce16fc38de503a4a82e9014d73263978d6c4c3c861295871dd1950c76c32f750949219a86f8ac82b82dc67488c0c1b6a22b06cdeea444cf5e1e6e3635113929ee8a1d13929d3d178e4d518722baf5800f1823ff747c34ecb14de2453ebe0419b98a066e9117578aa4ac2f6a615277e12de517193a500398f91ed0d635acc9feb2708e7f8e1826eb0d753466589b9a7148c908fc75dd917cf801a74681513d5f494bfa471e890642e5998f27c0421268a079c834b9c960b0c080bfb3035771b945f3d28fd241adc87edd6de1e7d359fa14195b2b4ea8305e89c7f97a1c470887787867bbb3dfdaa297cc3e37b92fa0e484dee4f682f90702afa1e0b86f834210e2b847f0380a4a4b50c0a1335d2d34ec515941b2a4b4dd1cbc8b0949fd46f1d4fed100c4a8b64429352d270cfb440a07faf72a457b181c4929d406a753c26056095989c863a08dfcaa7c9700a68a5c04ca81c5ff05a0d7f786b0b79b897df1ccd138788e8a9db955d7761975dd935f7ab8911f795b6fa0a84e3be3f19c84728952b31e6450069a2a1dde0fa20f8a08bbd1de325ee0834b796c9a95f9403243e925780521bfbb9a2aa2871f22280bb30a50fd4e8f89936e6b7a90eff23dcbf17b35086dc83918207dc633aa5e76ba0ba7d1b4fbe332da3cc796c2cb14d9505b64748d1cc4745833b9252c779a7712193afaab87836f28ac35ab8798a951d0107551e235b8fb3fd35002c9aa99638437c95a01d1f9423e21ad98570bb7e47b9d90f4ba3e55c9069a8e40a2be1028b5d7b9cf9284bfc07f7c0a04aa5bb752e97492cf82d11012a38e9d15701102ae67ab1cd042782025d8361d93acaafedaa97e7ca3c3dd17a2ce4c3dc76858d2aacbd89619af14ebf9f8dbf1da5b890d9456730851c755dc2b92376df033daf5b5baac06433530266a6dbaef1fcb978fbfa8b39b4b3c152044444ba838cee6bc64063d64271e95873fb2afc8735a404d40e207b28b2c9cb581e629e1149260bc27dc03b417d43b3154c4031148959cca171752a87952e890c4a65c8a70649002f5e3d578b03b5854ea64e18314d4a54d7454e0c0d2c90472bf233e5b3e876e67d51734568bc32bb4c85fb37a08283b6c4baa479bc416f1cf2d4a7108d86dea64fb0e667e3db03a1e6d8facf776651b9d5efde0d3d0e1a29835ef305fa13313358bff79ec9c22a1ded10100b43e9453ea15e3e357071bcd9826a09f3025ef13b708b2869b2ac38829ac96e548aeae3c629d6f907197fe2076e23f51c5279bc16345effb885169d37b0b9efa3527fd663eacd046d8c98bce534dab101c0b66672b40fa15f0dcfec567c32103a571cece81a9c8609e67450eed5b3ffcd49718d134cc0dd8e6284660e83daa468cb3cd900402e597bcea701f58c9d249e57fe8293d24a768559635db49a57daefced0a1f3b4093434fdef7483e9a8ac66c214a12e9f4479a9e306682165c919443667ae260d9224bdc94cb61d6ec3a5098eecbf9c9a5a89a9f857e70e42dc0ff26b1254feed9e46428bb7d6293126873f95b97607aa32ef27e819abb0e0f9dd2d12bd7f77d7e2014d6bc4212c361cb1b3b1b1736827df4871ee71900c4f2a4dd2196382dff8a2549e4dd5f1a5b0bd28a0ef7705332d601908829566c23c05af10f8a960b01477df8c51d88994de43ff0067c7efbfaca50f2c8a77cca0979c66add06874679d00bca8e130c8bcdee79111ff6279ebf122b73a816793050dee505059885035003abaddf011b4c9d04a864682042712689c991893c4c92a74fa299514b5421a56ac43b9ffd84319f7712986fe7f311ad7a04511538894b785f460db84352a170cb5d633ebb61b01c05c7fbfd71cf444f98cade0b9af5a80c666b2cb46f6f4d612bc07078f4eb3ed692048bb48fa26946f18d1ce543411b1444442c9bfa7a6f58defc94dfb663efead6dbce996a71f77e099ff2c839e420a7a0a19e091640dd2871103221ad5006f6ffcda43b903fcfbfbd1df24516aa248ec1c820287217e05384e8f83ba3e189200c0e31960833a6502d89f644be49adfeef146c95d0f5e2a6a12b858cac24d43e7847b10109fb717d18858c4594912a14b89079b0f9ba03b6109f736855b6bd126b0d882211d710068126e4d97cbb7b57488330d99554cba0d7253b3b020b44b51820d1b955120bb1be96d3673c38f6842fc9e3d3b58339d37b9796eb0f0a8df67a192431e5c9113fb6fdf701fbeb942334f31b324d84d7151c3aefe10c0351738bd2de29734ceefb0ded8ee41f16de411a1264d0129e8d70765572cd8642ccdf231b2ad288be93b05ed0b0c1732bdc4d9a24ab9971f3d55499ef3c1a510f14633209a076e1b7be2b6002a0f63f68fe8bc7d9077e66980e89fdbd6db3fcd0c2a5df05b1400e57b1a8a1cba2d7417e9cb28affa31be30abeaf74300b7d1b77e8c0b46edcd171a1e0ffa4c32b80b5635e0d14c4bb2b1cda13c4be8906e4757edbcedb4c4717837a42b4ca9bd67fa4743ca4521d7c3f20767611b961a48a6c89260d6b317fb7f12802c315bf3de25d401921ffece4b629dfc3d3ae5bc6eafdf4c3185147c20510159fd33b275931c38e4c59e69ae0a705910481e73b48b08d1783db2a24a59f1c61c90caf368e7fc6700d174a750c3e8938d47de448af3009fb3d3b679a355b0a266d27d2ffa5fc76f71617274c0401750fb750a080e983df408619794d5629f783a9ce671e6160b59f0034013dabcb15d13a61cc16bf96ffc9672e48eaee85400f2795cb7eedd22d05f3c3ebb1dd9922ecb3f1d90bccf14ba382a9eed49b37e0385d5a40a45e1920a2203f88b8121458453096cba7b07134b22bacc10352e728ddf8cb594512c184267887815d422a62cf1a8c8c35f899d258792fe75f9e5ede69e45addb744c0514f087235de03657d279195564056e4053d752013a591506ef12d9c48a0539dac33103e591d6d8abea1b1d334228f92d3b5a49b60362861c6f889388bc88b5645e1be09c4460c81dd144b28271011107e549495e434cd6c1971340662f4d086bec5be7baa2fe77e520815dcad99b570e29495c447de20227346e0caeace70763ff688641cc11e90792f8cc10f7ab67085d0b7e1a0d65c24a4b6360d22a703e9d725765f461ffb38e75921443e0faaf5add41bf8396b4ae36f3bbcdcc0d1f87ccb15fac4c293bb0393dbe736441a3be4334694308e5e9753559706f9e9a7482473e8dba3d8edfdefd05fe54105d17bc8b5738e58572c543c1759a6d641d3411dfd13d7dc8156315394ba595a0a985ea1665a00ddd068c3bb02ffb73e097ef2fda4be85ab1f0fb5aafb526c17bd5240129eb789e23eeb19609977d1430fbfb1c6758601f5e246581c597bf288ff6a46d962706d017b91c3c7d481567ac72315568be6e38c49b58eb38c9645190e3b42df68d3635761441de9c31aa00eb1b1907f08ade10d046611f865eac93c734b611f357e4a72840efb8f8a040b54c500a8f964541bc41caa04364a9e84ae72fb4cff70858dae236558b6b9b5fc45d33de30467c2889fb91205ca10320bc33fa76d16f0d00f7edd20f771e10bfb07e14baef00f20bab8f281d2764bbbf29b26ebc0d374f7e1ea914402085bd705f404e8fd786862852b7ce5b8780b547eba39dcdae965c499f5aa5e804ba0cb894ffa0bc271b7faade5db46f00d99cb404dbeca48fe5bb985b85eb5a9b6e9f203929e435e7a2b15a5c9070f310478ba9550041dccb09119349fcd88f4296f89462d0138169599a7aa261854fb23495b2f07fecdd98da8175e57ee904e7c11c9456a00c1e259557f895472b056215feca2228c00130eacf379dd4ff13cb42115778015984c5ff78872bfd0d97cbb0ab3aa651cb3c903ba670ba1280c85239ad48a7f0f66aafcf4286ea0a168b2b35720a1d7f71e48825074bbaa31f83bf113a70d5430b6e462d0d1611df83d27fd68d544fed1d0678294046302d3db1b6a6d46d5f76820e1f4d798ea28dfff898d339f51fed6ad5e4d5806ae78af71b6dfca8e828939370f594fcd79967c843534f6139268f06bf49130f1b6081704880629680587d02dac1adf2f4053e63f07d49dd0566870e944f0d9bd642a20cf49bff4c0ffb2ee0f0c8367a5c132eee91aa42f56c04a832183e10ac000066f41a0c06462a088104c978f105950b105cb9e2ef21a261158d0da1a4e1433f2228e2577b66639e8b9771b8570baf61d109e8b2a70949f0c2382a043892a0a08dd2cc873924d6aad5ce73ac1f403468077259cd2136c4334d420e2165aa29f00f9bf4378cfc7e48ea7ac8313ca2cc287fc50486bdcada4011da0f131bd76b14573e3898e25fc01782efdb710c6356cfd73e54d797b0e3eb3a947552c4a17312b0af78b944b8a08699157ea19d68c6d81ac49ac93e6b1e26ef8a34bdac029002c2c9b83e0c23f89f711b51472831101ebe3a8f976c42e9319cf5c86672ce63d8eb746902849f3045fee09d1706a9aa4d9e29d1bc25ff8646df85e5a109cd7e2884c3f68c6934d5fe067d166566e2083e8d78bdcc6c29905031fe397506426f20525cd1d72f8f4d230b77261ed4b270eeb0548a5c9a0e8d9267123c3fa24fa00fa1954cd478c293f1c9582d5e7b6d6083d6fa33b695a9cdcd2737ca820ca724a3f6cc2ae182c120bf9f1e30f91f4896c9aa355622fc7d398d0c021a9364b5ddc562bd234bf33e6079e112b86a9614add5b3ec88bb4a479a5c934dda9b3c14fb8b25ae65f0b257b1d229f8991d337e1abb6fd3fa555b5e8793b6555d532f238ef0ec22fd8b2a9efcddf372cce05058c012b69721e921d523492f09e5e989387a25bd657ae4d3cd6999c2482459aa8588e6407259c5408d48e1a5f2f69035fe87e29f29559215eb4f38043643c6964eb809a088881a7a80e6272064ded66105a5c823ed09ba2d5eac31fc72b7ab28b223db64f99fcd5886daf6c5007a43a9aa173065932f82599ccd4a67c86d82db18cee4b533059c33a2928337394525fa0a23e2bb30cbbb3cd227f00165228b8deed0d0e6dd7f8a5fb9b37bbaa7ab32ab3910900343b7a97a8bced7ffb65193ef9ccf70eda841f6863c66085418a4f7c4a7c2ac2de610263675f0eb010bc2122799e42b1ee858437a0eacd34b85629f1ba82e2679bfa8c472173d64f259130bba13913f64ba4dceddd514510ce8f83704b93659b7da18d837a6f45f59d395d04fcc79e7465917826332423e82a79b71cf6ec4050474952df6cbd7347bb8e1268258374fa9947bddc240dc6e404c6f2baf1327c4445f9a56299de61b54ab9134e61f6f193f68f7d1da0297128173fab36e1c9dae19a07a67bff6aae9bede19502f5a2f5d2fbd574d2ffd7a0492dfa6034483a044cfe712f3eab38a01cda74359008ccadbaaade4553ea0d9a3c123cf9d4b2ea692dfcc6a7fa8db33426f57cdd2ab13ccedc64eeaef1fb58f3a882b7851ff34f0aea1065df785b6d7c53d338b429c52738898c24babc0ebcbf173441ed4eb3db39b8417602b5268bb19704489d015922581a78c1bf38cce634cb1d26f7621a47f9680239c01f7c273d09d06950f253b36d7ffebe1e3de12e4c38e18edf3c320b864ea2080e1fec2ae24de81fdb9d95ade976b85f66a47c6c43cc4344072808195b6ee418b15063addd97d75c3953ab1894fa5cb6a7017d7dbdd96ef58dd3df0eaa57cb5085af7586249b8f351fdf636eba9fdc4744146bde75dab1d0775c7b3ee0da8808afde7a7650b4be856397a5ea8874213106746fa2713df8a4ff52caf9d08b7d12e67fd32309eff3f8b7dc7dd94ecc4e4fe09a303779f9bbfb9d10b2d6c9538788020c6fb9077146e96876bd086457cb7f455d70ba85f0deafd9be23e64fa0180fe70d03fe1067e2033ecf7d483ac21ead3d5049b203ddbc13fdaa3b04da340df6c308b9dfd632ebd7def3604ed5f4c3e6086ceb227848223d832517e774ca850dcce9e434d417ce2cd56cc11395ad9af86c0055b07b12b896aa261fa96397465a5561d0be2cd510b1fd2813c0bcd16b54b38c1e8baadf85260fc4f6bfa74edb25ffed17de80aa85df2483367db994eb696b9b21e6244f1f404fc659248a9195bc2005377806ba7b55d06b8c552bcce9d470019c400370c7ea72937311484d39a04cfe703d3cc96e9b3b58efaabd2715a4e404ad8d3990b6cc4a6b21bf4be87057d585d73b91b05025dca44140389d586820804d25d161a9397c97c0769ba2f51b8c3fd047f6a5d824fee74ae57d69edfc3b29a6b57b0847a7d4bc8f162336900f5f0fc93cf78b618ca0c86a2882bbb6f83dae67a1383d30ffd0031d83ed82206855242d295916971e9fd4bf9465a683ccb629e9e700c7270e05c4fc33791bc7cbc77c5e321f788f8310906e2e1bdd891ef173fafaf3e9c998caf7836df66f2280e776597d97350a488eff107645599df609cb4c2771d3493424a44d6cf4341a0d3b5f4626114ed03da146adc7de1230a4e1103436d6b49ca4e77dfc9350504a4802621efea602d9a7eb45e07d3a84c66da661f7aa6dc9b5035b6a6d4b97a361c359a97a07726920fc59519b2943a2cf76c977c0c3cc6c38a85d776c2fef9b0bb7324f178ef9519438ac47ffa40c3e0746293cb58a6cc87bc269f7634ea58e0ea42a470e52a852ca13f51b3d30d11217696e9a381a249f03a869db856fe618640c673974a7ab8a972d81f00c5cb8e4aa1269529bd4076f9dfbdfd5ce4995a5aa7a5f7d4a9c917a9d468eb965d0337a3d360b2ee54583f0d51c102ed78e4a1a8b3099b3c2d0b6929801d48621344bbb7a83b9aac328a46596651c54eca13829b6b6845db1c1e74a1ef5d89a05da120357db66ed7fa340beec8718936e83a61d775e2eb56a757458d529cb26bdcc638e7a34e58cfbbe81c1d8a29d3f9a30aa6067f1d6d9d28ba345c6e38ec74fb586a6c127bc080fc6ca625ced651950cba141505554916329615a8b5a95a84f2f76adae0933baa1b32407d8ff4f8da693feba68ccf6bb39fc832681dbf3faa397e1aaad6c2fa2d54fbb3a85c1626d2794efaefa2a4f3904181c84c261a722d70146a777b718247a427aad474638255e6ebfea2ae598d5d9ee80dfea15e542900a123e162ce9a31ae2ffa5df4f1ab2f36941fe961a5ca1ee75bd6274123e40d32b85051c3beeef644377ddb555905438ef26f8e176a999f757f8ea516605f4cab2d506a4e0bb8fb8a3e798cb51ca9202905753fe0e1901aee81318fa16c258896c4974bed848bb68e4938d7dd2ff6d87cf5a60fb3756d713f134931454f64156421cc9810d4ec91d0ee1d7e9ebf9b3b3a9c0621796392026c00f51234b325083dbac576dff0815818a2e51f654ad144530fcb9c0e6d2978e2d7dc69f62eaa5842da707f27a4624994742d5ad012ed585ce0a2f9e9d241a81ed6bd5b5d01cc3fb82868770cdd8234ddf49cbb411c11689f76c7bd3e43cd2fba3075f8e67b3c5b72f0c0c2e6fb5b1f13257b18d2904d1eaab431fac4a310dd8f216baa6a2786d8c3f19927a7d42371ae72a95667c9a3b72eca2e53aa0f9a2bb8163d0d5543ec631285163b86f8474bea733dc27895535732a56e4bc957bde7b11efa4700b8d3513fa2adc1bb98026fa4ae2e7b9cb3d1746f907cd1a637eb2e1073a988571313b9c86e734981726f058b362accbefea7b32daa6549c90db7778e21cb416bf0d634c069a26458678274d02b70cb4d279d351a9b91a66b931336e3b73e1418d3c3018a7f451de6376a965ac24aa377c2616d7a7aa2fe303e9a9bad14b4ef2e20e74a156d25f987017645704f852920b672521393ec2e7e426a838fa05ef6b8d2d0dba4987e0ca618cc9123aa8ab2b5b6f9274588a0265e11ef296ac494983d0610cc5c6cad77fa5694aac38eea58df13d1d88aee8dc4df40dc0de2de20ce0de26e24fe26e26e24fe26f16e50f16db601e9b5901fe81f4c4c020c508e81f5d02510074c41b8547b7432875694492836f7af52d40af112f5c598af1f2f50f3cd95253a70da547afc753150faffa7b3744fe4e401182077729969ee54d6d51365856954f3188749a28c545df3c59e0d23e2b64648dbd5183dc84a7511cd95753e16356445f5438244c72ea8158eb2a75e6c5fc9f5366a75d50aba2a13701672012c9c72a68d15cfc92ae9c506bc89dd137261c78dc03462ffd89506e6eafd89301be8b0df5a5c3cf6e231e6143ae50617877a6bb8d863b779c7c6548dd64220d490886ffe198105fa444fc5f756a43446cf6763e59f70109d883d56891b11f9bb83fa32ec81df961315852c2b7ebaf01b6d3f03ee97012ec6497eb832b9b1d7ccf2949f7ea8d2ba0faf2400f852d1e9ab5f7ad34825ba7b32a7a95a659765e597681c08318f283e93af39008da8ba3127f2605cc5acb6bfe836953c6779c63e88ea8ff1601e1b5d84330e1cc56bd915f85025cb38c42600a8cd6929d4d0cefe70cbe5532fe581ec766898c1e3e223e319aff9ef8f7e068561f32a250b5866eaa377cd01c445fba7be92b4b1939e4e1ba8c5f13307a0133d5ca184e419da66dc47ea1caf24010d428d76e339d1a5ef2dd98b174b9890d0f20e7906b830b51328e14ba91aba3541fc080740683833fa43b1c2179923005d465d1770da9327b08afb57017709848ee8bfb591d93e57b3aea7e9e9bcc03b2e8764ecb205be5f59b9cdb2e8c1491fd9d2c6330649ea1195acd36e1666158c6ef307aaa7434f59e177a9a111d9de1d4d958c90bee1a40907d862580af3680d5ac6db548b581307757e55809322ffa5308399d2d2a306403b3712d71fcbcfb58ff3c9dcd27a7872be5f0e1a975316ab7368242c4c626aa6eade31d2d42901d3846b96836faa55b2caa2249c1148a9be8c9dde01ea52c44530cb2e5e573d61fb0390881943a6d211ac27b90989005ec245c1945bea85f600592c66273c08362eac5d02530d4986c4b69d9d78023008e5fef565292c71773d6d6abf1d779f608b5859852643f41019d565c82eb99c0042eea5c47308ba00f281932cc3a66ef0f029ab9a35bb03db2c3615e1d02829e5cb9d37bb30e627002173b27975fe010316ffc1b880818d022d9e2d7c25912707ff9c271b2ee8f1ba8f419494446ecb15176515d2b9fffe79ba287f049de6c8c5801aa887970c33a4f75942e4f0eb56f0777d281235c3a7740ab98358fe1ebabd3543b75ac3f93315ce927c4568552306ab53afe61d8fef964930781345f5fef056da13c8974ab6fdbdbbdf36b187d3d4d5ea5f734025af0a42b3b4697fd10396a7140e24860f6ada572d382da80b3506d130b516fc973770eb25901c57aaaec8fe7d45bda3c07eed0996419a5a4ba69021b0ea1ab4422a0253fb83601bcdf7ccd2ad64b4d939f2ad6acdff1159ca8d4e02a9f0e3e99c0db61dc416ba5196f520e6c08db21208b1a750924523c43639f31f76014ae106112b0e3139d7ec3c24c54471ef1e740ff73636ea5df45a7c9142b6ed04c3d31f27749759d80eb05452b46ef638676827e1a8beb11056b6a6813e8b2d88260bfe80b3c06e0ba3fd152376299cb12e74bac5188131083e63309c0ffd01f890dd3901324f7abd643eb8f08b68e167fa05039c858df364aaa36a3a18cd3eaa6f21305b09e66344caa425cc4420e5c8b7667739aa7a821c5cf1aff470f9c08696c43ba38302d640f7b23b8fd286d26a00670649137abdc43477e8ca7f7edb313f117cf641b5d35b4d47e4a1caa746fb40419e234995d06f35d8f9e04dc2297899a0f77cd4318e32c9e7d4cd2cb7a6e415d3b745bc15815d98ff4e717b6441194f3e99ee8a7690783851119d1d7f80e271d32d08b9b28b67ee880e480366c74c1291d0f376ddddb6771107a9c484781aa01a834d12f3d772f94dd8e470f05c1c016c58e04a4d3a33a312eaae293d4b78c3f3e4def4a694d3568d4de2c5d24308b0781b70ff37029a252d18a746d9a290d76be6277f851e3da79656b04282f9461d86c77524389732ca69ae42248d8e64a042d91c75da8507ec00a07dbe22f08a54015f1ba4ae5ea06b027abefdd137cd85d9104ab9eb9a2098cdc7b59ea78a316d1d146a8e075f8b5e80506dfd1328446fb6391f4e699e2a38cad487156bca6afb56ec36ea96f2278a38203d431816bc55cd62a28324b22078542800c25921a5429c2509f169f44189140c93ec12f4a8512d1434d8c0a5a3ee32064bdba8700c3b71798aa209c7eb74502a7d3fe7b240a7648683986a0d184418b6b0c64637a10281a87c529f883fbef14fc10da315b47bcb91d4ca30d97ac9c44a99c3456139c1177220c43a287941afc544fd59b4922c402bdd58782346492b135062dabffb3e1010cf0dcc1978c62f114dc049b2ac7c1444a321eb4564e4163938218f27a7a02f1f2aa79c129e17c090c3c2dcb2cd14a5889662d1adf13b427ac4427fcd84fec8c9a559e62e4fb5802ad6c42d84146854984cc4506e705a58c80e2d10275eb89a6da58a89c6cf0ddb6a54ca882d3e5fcdfaeb5256c69b58f2880442db4782c524a049266b9b97f6a71b8d99ef91d6d70f6d032e08fd4d542b58f888847ce4418ff599d1cbefe643c8fbe58171ab24dd4b0717109ee1913e3f802df8147673432f88ce61ce0aa880cbfbbf29d60d355552c9140af4dcf74c87834b28114d4e6d9a5bc4fc0027e2d037a01ba46087614dbe512eaf1c07a923ba59a1a2422b4e29aaf0a1b2684129af155e220f6b7a8dc82440400db87275233cd8ef9a05c5a0a618acb3f0f1521f86ae182967b88c00a241fb26086ed98ec00a3bad58a8ccbd5d280441813bab0896a805dfa16a89e7a5c821b4390208106827687fdb4d4eccface43952a27aa86b2b4123afc0b1cb9779f706bd44287c00751c1d218fcf001043f0d5a894c6a7814009da82a18d2f36578e8d58b7de502a1027f566e5b21394300615180923439071decbba2c3335d969a36fe8762bb3237780b71d94acef87354065360a5ed3991b748de3d25da5c0e49bc697da726c9edf975070379c9dbb4eef8d740499b9cc3f461964c3b3908111672b835c9b2ea30c04671dacdcac77205b6cc5ff85eca2d18ebb82663c46291dce925cb868f92aeab2b8981860729eae0387d966004d6684b4ccd87b04e50b324519fd3f0b47151744ac7734ac9630dbfa4995848cbd8cac15983b9caa97583c12ba70be10211d3970bd6f08c020ef0a7c8b2a0a5c5cf9a5afce31eceac1e3794d80895a96ff6820063f5325e8c063bda0897b7753e8cc6543d63dda1c40194646eb6eef660e0439233b18ba7d2677dd049d304aeecdc76539751b21b65763b387dd3b97f5d72e7bf929c14f264888a9296e17d822b2e7e23b250cd549985ced248df24852ebcf7456acb15cad278a0597dba5a9013b50291d7ffe5ad2c9a32798b307fea71c9a88a27a17b581f321b216e393b2b4807b27b8e052694ee8a33c32ab4dd6e90222368503645e25377ae1a8347f82b4f86a0863d6a314433fa43a189b0a7fc3127e1b35e04db976391e1d55385d11d11b5ddac2cd34423eaf2dcac5b35d8dd4e10adc23a15cef57999862c7378d8d04c885c32f18dffa5272f9935df4a626f3c065f6a7e1e559a9fa7155b399dc0d85280236d53c0053d8a7b9cd82adc57a8a99daef49aef697bc5d974c013930f1612cc389dee2bf888d635c9e25d763a8f6b353907cb9d54a8409e07463ebc6288b5e010d5b1bb11143fa548143e6fae505b1d84372477de42595cd16315e1bd6711cb8e2312472c44de0a5d7ea0d6ce088b1ecedd994676ce82972931fd51d3a42521c6d9ae5f55adb83cabb98e79de8f706c1d6a1f1e8f5a8020b5987f218efff88add23e282fe460681f6616bd7590cc21fac45a96391afa1c966f2c48b2c39758193751bc1136a4f9b78b4829af85ed29fa6be5c09f1b465e96b5547e26e59dbaa12020b1832954e16212f78ad43e4ed0e305b359560b29cd1749b18cd8443017c6ea2cadc021181712e5a5838a829d1af63bad343853f58839587bd1e0ddda38745f0daeca8af876f470f904156eaec58aea59767d0f9929cbe4348da8c11a9dcb354ec93f707419043e43cdee5e36bc592e0c4ed4fbc4494a93c4191b55eb132f87a2a44d60452bd431cc155c643958125b876c243f340cca88a51b09d23197092211b32763f25598682f7ac45ab00d4c6ae546184d51b1106e0b63e6c3674362857c2042cdfaecac157bc92f995562b6c25fea3082f3065354f42342711617041671b544d9b9213549e3da4e3d5863200741666bbe92f82c4206bb3c73a360b6b2c2dfa021d7db958160baad3a9772df673556eff0d42701a086a2b788c256727ee9eab4d0dd3ee2e0e7707d12c02b6c98b808fe071f560ba5b65c5ef540bb56fc2d93da0920b8b4577d14f94bcd1c8c0600332c2812c77e63b2f197559ec86fd1793dca99f75aaed95e7924999ecece46230fc94f1dc66e8bd48e7bbed13c98be5c6734539fbace0190fb9efd9fc0af4d270f96791afe008328dd7607682c0c7e8db067ecf1ac3e0f9b54cc2266f7a52963dfbd38934f58c5666a5be371c82593c8272d8d9163db636ec4f0f5c28a4d52f0b28e6d9c09567cdb4695d8134921d3d4891c72c677ed9ac620110a2cd469aeb7ae3cf91fc1b3180ec0867cf203a3ef326e41b1618540bf7133daf681f32904d7c353e7253707664999a71a435b4262ff33cf3ba9ca4a5590df5226e7ebda7cc1e97fdb5db50be765f2bac2ec1208666c91286eff70de559badb4a33318a3cc297acf98646298a6a3054a45aaf8fe365b78210ace8e8c59ba151be14dc43b808e99f5995fb4898325a25ce8a85dbfd2ead9255627f6ac08a999d3d50c44881d9426899bc5dd3c6e05e0f645da40d94fdfae97116bdcff440907e80333b17beaecce10510c668d96403358d2c84b3e12b16d4b91f6d4d22bd4be07fcc9e825fb5319050bd3b2c160a4625a9e519e4b96b1ccef22213b5168aadff504749f690c4cbf976dc5876e6ce533870dab48614def054966c46aff9119000d3e6026f084badcb93be4d6afa0d1b0d3c367f4fc2a5e3d2b8802c38c321315b8b8a4b64b373a4cd19c3564436e5009768dd904966812a42fe54bad677a2583c45000ab23f25d1c77c0254d4636a7a0304a882e0b98846606a10763ba03cc0a0ae314bbb5481cd764e615476091668b46302a606151cf56a09aaa00f8cef39c2de24df360e7727c048ac920a9674182e6b625c7362321cf276c8eb0e71d85b3810057412bbfd21fba4a8dcab5279ffa5e8ab9e257fba576068df298baa9b7d6b1c4daf14616a5d91a5d5e02118b18e983b39027a3659c0f7c973f1bf443866d341f7879309b1f0408db9fa7f65217c49623acb3e23c5881e4905dbec04a4e26899a42a367644c3ac9935b6073b1fd1f514641a36dd262efa0435f7ac0bb29566073fa76987a0bde22d8e2f13017082d739f9480020430ee481efec20c2827f590b29c68ba6a52f284b5fc16f5cde0e990493994b0f6bd76332cc0e3ecb609617152b825be56fe4dbcb150f66f6f80618ddf885a8912a930a255bfdf9a0176e3a25314a9afc4b954b62e7f659cc6d27311db37eff3263ce3acce86e97ad79b344c53dedf670e16bafaa4c9d6e0d6709b722639e8742a866e9f0eee09e52b3f2b7be1f403b5610f95e781a470e9ca5d96d61d538209e06abfe1f5f49b3751653a69d894c694ec5921ff09b37920a63d3ca7ba812f4d9a9272a61ab0c0141f2ba7ab85e45b9fc87f43daed896d182530f44222f84824825f16ddb46403863fa9afe18cd88876960371bae47b62c8b998051b4c728b96d1a4cb02de8d290b0b62a1232a61249088da4e0ed4f96d96cd7517ce923c8479061150500a0208b1bc4b947af369765a68282052fae35267181384de3b79e100830a5f309187a096050e52ac26c3e4ed48f051e6675d435042cf7b72e917e2a3f38cc3b87fa63154080007798135964799df4e46546b4f132f53e82459304202a2035c1b2c8a6b146739d25ae1e0b1a9fc9fd2e8d48d6b1fa4128c428b4aa32e8e6cc5df71d28c557a8eecf69ca790411fc0bdb6c86947d504c20f03e88f3ec7cbe5d1f2e65b2eb9ce49f1fee64cbe1c9f8edf2e897a0482bc60dd30ee844219196d02d5697005e8eabb87cde8ab34f93315bea66bd75554dbd14e0e5daed5fc8a764df5fa491f89030dfcc1604edbcdbd70c956639e465bffdd0c389650b0e0cc18f8e6917a798ac97ef1f0356d41add9960d4dfb1721b7a94980930cc2d1da4f6943c08c180f9e77f77baecbab543c6bd821de4364e1bde4bc4a5313c15796055d04cafaf3530355049c8830408356734025de156d90572908b6a13c0aecf901968c5aa1031d80c1f0111d3f0c00135e506320e076d538cb9a663cb714406111bb1c5505b640909bdd57247d0f3148fde511febe9c7c70824c6c4a786b7850d730ceb35756ec2f3fdbc2e889924c87783c777128e09b48b8b9958e892dcdac7c48b500723fd182770f03e9e80c916f3f570440f1f05b0982d0a9918308c389ddf8977b6549dee4898d4db760755574c0666cc8bdb18135f3bf51fba65f291c3b147504ef51e3596c0aeea5c50af73c0f764f93b4983fede52488482bd8cc67a92934ede6529abfd032422042e6a84280f5510280b6d73c95a7126ca6d3c86cccdd3cbfa3ed38f3df0cff71cd6b880449123cbc7a637b4220adc5a9e5699f67df48a39e0410961201df84e849579b63d29e71131a407608bb9d8ea08a3b78d96b697369152ca14fe0e3b0f950e20eb6d66b3d92ca59a928d7e7ef6538418fda45bf0f798bd0feae3e39352f5789f9f5264d423ad1a329aa53c509f74ca683483c068f6329a4dc14633104c578c1562ad59f13a45b5f1afe0eb24b3f7ec2396d9df35c23898e0c77bfdc060361b83c14362afd7db1ffef0d726533dfda712525b9448f0b3dff178964a6d015327e10e5f82ff3e7cab4c74b5b0562692201351f0df6735994882ffce172aea61ab097ecd5a76fc9ad7dcccb0e2abef65bfe6333b5f2ea3b5bf2f63edeff15626ba56383ef2c36bf6977b9f78cad6733d7711052858957dbe9f0075ade749d7229d7415c249d759ec9ae97af6a4ae5ef6f9c05cab4cf481499289c49cae42b31fd35546a6eb89e35aedaf4896ae45423be92a34cb49d7d9bfecf58101ab32d135ca22d3550433a6ebf981b9e9fa1f18f003a33f30385d1dcc5e3f30d8c93895b0cadadf8b595960c068699529b3b575e68c9afdade29afdb9aaf6f7bd9df16587b61e923ffcb7b77dafdff97ddff781ff81e00782af4b3c04a69f05b87c11468d321860c60ca31fa7b87daffa08d4fe34a831d1598af9dcabd3c6efba998a0073d0068e3e8373f31a9c8ca3d3007170705ea3c1d16c7070f499120707a72cc77c73c25e250ebec1317c62187e6111cf741a9d46a7c90ab70079405083afd180200f086a346cd2004110d467341a9d0608823cbf6a352008f21415ed3cf839d4c10963212c34c39c3ffc1c1cdff3e7abd481f27c0e94e77b509ec781f2fc0d54867af6c9156d1d8c93ae622c5dcf5086c3c7b31dceb0902bd569741a9d6607bbacecfceba5cfecb0c16bbe345eafd7ebdb7969355a8d4ef32fd887f7de9dd76876d8ecf515dbd979fdf91acda9d3d8ebeb5bbc7762e1c3603bff2dc6ab56b35fb9a25c91b88a39ab6cffbee04ebac27e2dda3af72696aeb32be259ba0a6d9d0686ea3437e8a5d3e8342216d8bd825dd885c1320c7f0161b0118b7b553ec62af11712567e39ea5cbe9889625fc2cc58599a3fe22eb80b98ee908afb872118866198ae64f824f9182b7c322431162149a2617ed7e1fb977425c9900cef5588bf902489b370564892241992ff0ac3173311ce87e99af3a6f93a7136cdd73161e1f9e1f592838624f83837e898b6ba0bee82bbe02e271693b8b9c102cc97f33ccf0f7f397116cec25d709713636171af608fb160f80bec5758bae2acedfa60b09b9b83737e78b95c2eb80bee82bbe02eb8cb09c321183b662f978eb1b9a5187ee05c853e334e26aefff62ab44b213c248e26071d94e58b2fbe9be51d4331bd5778765d29ee12ee20d1d90ff9e36ccebb57a12fc45df0173676fc7c669cf78e25f91f89bbdca0321d47c74228c805b80bc67291f78ac45df41ed3f5860f435f28ee72b3ce1c7ef6529eae4562d70b5a74cdb6af421714651f9858c6da8431334ccf9c9bdeb9fa89c6c904f65fec61e679f3b11bbb31dfd4684e7d0696ea34374c95e059fe175a82094a4a30c1db7ca14e136be304fffa18133e34ce8b74c343e6c33e7f0e3bb55078e8f53acd0d3a5318ccd920dd1c8d4e034b359a1bf499af2fcd156216edf57b766a83bf16c984c8749ded55b673b8ca4459d1775e571ba47bce8174cce5de1b0603b74d95223270c5016050e9a1871e3220c68983c63a93ab60306bae162fa379d86bb9f115d8dddd3d144351082df7de2a34e49cb36b3c83ea8cdef77ddf48aa41138621599a29e372b95ca509e68b599224499a2f2e573b47bf4ad334cd172ccd953439c0ca8ba6b1c15f57b2d84bce9dbd8adbd18041938504ec6596e4e81243f0d3195f3348bff8cbfacb80551aab7bef07a6e9856b72ce190ccd385381effbbe50cc001661cc30138661283a7a7376958e9a1101d0050579ffe28bcbb842c3bd17e732a8d6f03c46067a5dd068c05ef59253efb0d7d9f6ab33d0d082461534c67dad9c4ab08bb131d516b1d0cbf419adc63192a97bab642225d8073fa6129aa5f1f8c3749589b19e64920b7ee822332c35673bdfd4959fcc686e50a8cfe0d94592deaa6b55251389a7ec7f362b759a0b244ba5a14e73836e7873cc03e7ea596821f19a0fc9178471debf60e31b3c4e41fa8e9b71cea7fe7b85b3f00c0fad536e1201e2b48cc9249908f6af37f1c3d2b5d524ead7cb1ec97f71d963ba6224f72ac5583708a74878963ff433269a24fb64577aa645b8cb05823f7f10a954ec7283f0e38cb3626b9c2b90aca10b80a0f93373e2ff0c965173cf9bbf496128893fdf7c6ad388d622911bd488ef9bffcc0e7cdfc45edfec3cceaba0f3390f9a3d22ec3cce2bd94995e8a49e7d725e74bdf8f8bc7952146f186210ccdfa7b5fe720631dedfcdcddffd810f3e368fc41e7c378de01b582c3d51f35fb0cfb9a25be4dbe29368915ce49e4ecb1ffab7c676fe287a0a863f0f58d1a18431cc9421831a1592d84012207640830656078438c2bdb802830c196b2650811cac8c5106135bb050e38a1ca850e25e81a18c0a64b161851ba654986265460621c6a8a1f585951ca65880e2882934b0058d2a358c74ef035678b8e2080800a15ddbc348808a59050a1d50420c9623b8b02187c3196496d86289aa1c8cfc9dc90d245011c2033390f1020682b861e717b71043f0d319b36854e9818a335d40c0015fe42af704f377e5fbbeef2b4551081cb060a38c153263e040c41946e0980d9859c20244f03006025ec2944153a5296b9330106709750ed637586106bcc1e045cd192b68b10219189ea8a2871664102325874ffde0030666c0801a6238d146152bbeb842092458d8001961a47184517ef72cd00a26f69a83f55fe860f6a148894c860732568a4e1948bc029917349920fea80c0e48e18022c28419c348874185cb0b64c66001092e8c3499145e10ca63e812c778a15d2eeaf0c1f00bbff0cc3ef76f4ef860fec6688d38479fe0f769adb5fe72d6c07775b601e6430268b4404507d490d980105fa068630628d458409b1cd9799e8fbf689d821149740085182a80812c3bcecfff3bbe7c41881b5fa878f012c604c47c018226151b86502387225ab84cd4606a71031a0cb880060d6334907ac1e18b28a20882011800c31c2f936931fc3e30d4e58f41a20c835cefee3972f5953fce56db3ffbebcfffa26b0c8a3306c5e93d47c47d5f5cb7be729ae723f210464250d1c319350c21040e79082ff3802c80a0818a23c0f0c01d3736bf8d37be3e9bfc72cef8de7baf7fe40bdd246cc5319cabde0eee2f7ffb74da45f7a73feafbd8fef4a7bf2fe7de7b8bd09b4b725f70df7d752a661e9bcc18e38b31c6d833f9c29f6217be2e865832304518010d68069011010d6081d1800fa471dfc5211498b040152cb4c0058ba6214acf071c1e2005d615109a86a9242e50c40d4e606106165b0fbabedc02ce8d0c7ee0077a209489391ce386470c3f50a603ce4e3a8e5a5840ebe40cb24643c5075888814ad50c5a902973acc059e2bc09e2156b40793631c2b4a8c1f5d2a2b76396a1534ae086bc023622a1e31a93e5ca147d80855a3ab840ad14707c1b886909dcc85940b1b56c6b0d7e20088263ecc8e28735cff6305bf4f6d763431e4597d66234a05d39e719b656228bd10236d5458d99187ab872c4161946ba85363c88210504ca9002849196c96499884c7768adb5d65a67bd434c9a3c135fdbc388d92245315a642cd1df9bcaa081d40bc8328fbcf0a275c6102dbca065caca14689298428a0a3a8841c61a20088220d883165da198174431ebac5d19cc202846850d822098434c05088220f86959978f89930de345837ac797f307a6b10abf58ce39af09b596410dd1add042034f0cd1c4144c44016a612503665060872972a840ce21be4c66c319593403c4a5ec14c51ce64cce39c42ddbb1c6cebf231b6d61a5016d8218a2cc163f333723022e336baf3b5e6932a84532cedcc8f28b2199582e65a7289e59e42f7fdfa77315e2165fd18fd08c9c6522320d92452ecaa9e7ef6322bc3f32a1d99e0589e2fbbeef0bc1d9f79191c4f7cd8204290a8365af415e510869f11b22ef98fd5f7989378421193288201953c8effcc0f73967200d57de317b331f76d173ce39e759fe72feb24ef3994130e79c73cee09de121cf00147a47fe1de05e5f488decd2601833a8f28359e7bf9f653c331a3639b22ce33bb8459acf3f7d86e1f77ddff77d2118668b32f6f77ddf99e3daebf9a00be4e80ab1803978263bf5a7add06cd161b0a44f4061850e583730c165062b6af0400d49a470258c0187d901fc51758133bce0c0150f00a32be345ca9a2628c0451a10449303a8c51c06cd0a1b04415083200886270862d1c606df0804c3e0409a0648200343f4b48c40158c306e9882430d4f943102f5a7331be597038220088117d6a28bb78c0d822008821a04417009f29e329d0124f09733990a6918be84665c645038a3cd17618821230c2fb098b9a10a0b16e8e200233048061f83af451004779c2f7425008220087efa6f6c0f8345d616b787c1026baf429e259b815cefd67b25efeaae7b8ee86c1eb9bddef6eca978537dc3bd42d9d73709db4c467023f0a53ccb28eafb28f063bad7d3ef5b83ee3a3b7fce7f631fc9eff7fb5231fbf8b9f386ad77f704007f4f91477b8eac5f4e5709b6a7229e89f87ae87add1d054f692cba40864f920e8e632ae2efdeaf0cbd4834e21df80ed39b2b674ed12647d2487824fcd13475f97a85e28f0f338f88e28f47cc310c61621e43334c7bc8f155c2643298691ab9a3f8ab6b7c12866130180c765d7c8dfb651a3145d175c4fcf143d48829fe488ee387a34886a1d34ad388b89bdc36e1b949f3348f98e2a3a669043ccb101dc18da85488f58800fbd72b51123b5fa6912fa65d39e3a8a34396b72ccbb2c4c931cb779da6914c9a44c6274d9df37c8d4fbe7664b29df3849946ce7b9ee7798e39a611d78fa38e59be699ae6e84a9bdc36655a84dc634ac64c233856c262e588ee984ac29bf2c99b721c9f670cd1db66f39823c0fe650058ead948cecdc9c9c9c9712cbecad7318d8c5f963ba6f931732c5de58763dae49a691172977f33923a65881a193f1c8271cc1c31cc31d33275a791ae147cfc3dfa8de4bf608f4e7fc4373a649d60f65f3bd89f289ea74cf6bfcec05b74f3197c11fcb104cf743dcf57bacaf6f766fafb67fb4bc3347f60ba76b0bf29abbdfae78af663d71fb83867fd69adbfeffb401004c122f946e014cf9cd9b94aaeb266cdd5e2656e04ce22f9af3f7effef03e4f7e487fe778a79eb7b5f879fc6d7ec5132ba9488e113618d180135298a370c3108e6efd35a7f39831887e5877e659c775fd737033f7f45c0dde4b601370882398c648945151a32990b967ea14a151961ca00a3881c4997d3f08b391b9721811d6b9cd93fdbc3ace165afb166934c4e0fb38610db83bb6fe0b8d1e1d4791c8f83933fe6e8ebfbcc66e7cf41736f1835caec7b6fec337abe8992a107aff79e9d8fa56b0f929bcfef1b6aedd9498fdca4eeb40cb563ef1b6afb9beffad7ce7b7a8324f619c9f966cf4d7ae44cdd69f9cd57fe1d74353f90fdf99fd9c1ddb2cfe9ebc110eecee7bb19c2ddb2d89fe94d0e672c4522fb9c3291fd9932c9f936c7d2366f9d7fe578f01dc949359ee9a46b9bd31547ea7886f31acf701c04d8f33c0fba46d93cd73c92370fcfeb63e68b3c9ffd5f1f83f17cf822cf67ff9799aee1b779f3ac7e235da7364f7ae3c3cfe611bc6f7c466fc462e91a8bc5beccb15c7ebe89c15326e64d7b8e7cefffbda7ebb84d26668c7c728da1a7c64337fff23f517f4dfd0dec71ec4917f265707a180f60b1c937dfc8a9f32f7475da259edda4abde37ef5edb791e9ef4e6775077da4dcadef3713ee765b27467b693ae7abf5eb0d71a7bf1757070fe447de7a04cdabc71522777c71ef6df77c4fcd8794d24b1587a1eb9794f6f503c9e1f731aec3d159d06fb21f84c9998fffdfa4ac9d7e7fe9cb6f9bbbded4dc2366ddaacd92d3ea2b3bf94097ecfa9df209fc2715f69905cf6f717cbfe1eafa5be1a63adcb5fb5d6bfeaad5b4d8e9fcd23e3af49f698f27ebbd075ea874d8e6beca91b3649a6bcdf63fa9948be2cf56eb246d945ae9aabed7bc5fbba89046ffc59cf20beaeec2c5e89a217b16a67576fe735a21a515f5c85a97055952a9b4714b14471a452452cad9458ca2ae5152a2f38916865c500f18a0ea296078058aa5439df24e2f96cde27f36845bc12adc42bd14abcb212afae402a4c44ae59a3abacd155d6ecfc55707ec55450918bc913963154ef7c2f4a8a5cf04cbc5ad3c689482c36d6d2869dd7328672859dcb2a58585858170b63b9b0ca2a632aaeb94244b1ac229655c4b28a58561171d25576938ae2d51a6295f88963f04cbcc279f32fece65f65c4325788177748e7f38f605c22b22afbe4bc8864bf8e6a6c229e5fc73423067650d1881b3423ab32110f4e8af34ad7bf71d3aac7294eea376815b76638d2bf91be0c9ba968a2be5f1062afa27895bf1462e7bfcac2f3a05526ca6b7a62952af18a95a8850a88a58a6c1cc7b1ac229656ca2a25969d9f079b6595b20a89456291582e1e9728f2a422095231b357511445114b144551144551144551144551144551144551e4e1e191f1f0f0c84451e4e1e111cd2c2f2496288aa2288aa2288aa2288aa2288aa2288aa2288a5859677986a528ba5cae711c499214c771748de337ba5ce338ba46d7388e233992e4389224398e6458966489b2791f489224499224cb922ccbf2659aafcf2ccbd22ccd0f964d190f8ff8899f298a3c2fe3e191a5a2288a595fc4ab2b56c42a62152a22152b51145f57727001ba8469b3b53d4c9b24367895894833a4183b3f8966e797f2657b98365a3bc776313d343b2cd12c3a0fa259766e5838d79bc6cebf8356bd91ce0d0be78a7b1b05b7b9e5575fc93ef9c12bf02a13816ab2cf5526a2da1203b1402f60161e02bf4a5f097bfbdc6bf4952fe19a34be7bd95c367896ef1a92f4d7ebc2cc0fa6ee4a3eec5e589310a3b0276f89abf0909329a6c23d3cf3722e719231348b4e8e78957df29b2888057a41c5ab9baa73c55665cce02131f0d09a4337f865c82d6ce535199c68ccca3e39eb06716bf1b0f358e6879d5fccd157b0682d5a084c8e599928b3c13ef9a9c050d1cabf8e65b095be82ad4678f29e77dc12b988426021cd90627c265966dccae2d66bdcda8aa15974de44c1ac9d1f04bfa42017ecd306cb46377edcd39b04cb312b138154c6ac555fa15ac7ac551cb333952d5ac14424d6f5c92fa6715e36d76795651ce793b93ccff3c9af3ccf27c192873f9df30b6f9c621af00c5bb90f9a44b0159ee53fb73857128b7cc289c832d9277f8ead2419328b9dc92af2f96b91507ef2dc31908b887585804578507e504d519190d06cf62fbb2726bb80574ecb7f23e5ecd93d371ddfc7f8405aa51ba7e018ab4c045e619ffcd86ae73f1d30aa7189b0551eb7ee58813b84f3f9470c5ca29dcf3fa6b9443a9f4918c829631b3bbf5f2137f51b743e7ef29ee41b8172c13ee0154e5759be412bb8b5f39f18e7878157378887d37c9224ef87e900984d825b78b6c219fbf2e017f0ea0a3191984faa2171bade8baea2d486bd930f4bd7fbb07fa1ee34927cd86314cc52636bbf9e7ca530f4fc6b22793df97aa12bb8a53713fc37ed3139c065c3fe9ac143690cd578b6b62f14cc42dbb0da7ac3ce87a5660a9a41412c223805b756df42035f768bf7cd9413e7bbf3f8f1182c6af6cf1863b06ce99f317ec6702d52c0b2dbbc85bd5a5598d12186d14eea2df62cccd0b05bbcf10a802c4e3936fe9b29e79a5260eb72682c36f937534e5edadb64aad3586c9c6a5581c5c6216663ad2abc6c12c9d5793265821f8798eda60738c46c9c36c96ddae864d149c57c21c46e331356e70b465bd88b6f0a8ae1b46ca2e41669862c836760169e652f7896c12d3c84d39e22fe050bfc72a3c2b98257bbc7887fc1fa826741f98dc03e6bd893249979248c5ccb8063f02cbf06c780654033a0185ed3c2437e85788ebd8257e0979dc93298e824b7f090d661afad157a46f9b2b1111b96b5041159ae44b9214a0c5156882265e39cbd424903a58c1828554069020a1728426c0ca5ca950dd342ca4b0f1bff2a85c38ebd4a55edb0610af0e204ad13b49c7005cc8a6a826ac3d66c20898daf36b64a63c37a69885a4fb0886a38796263ad1deb62c37c70b283931a5c6b583058176ecaec9b1c0e932c382476cc3025b1b4c48d2bd4246e587498d9309b0b435944c610d1bad92b912f448a9060af1c5c60632b7be5c08bbd1a6dedecd5a8776682bdeead8d9581d6be59b379a860afbb37e48a8da118d24503ad1d1b0204ce5e873860080c435a18d2dbd8ccc64f84c94a62c39e5862ea092a534ff4cee86d18666354e31b636be326ba68428ba926c278d9b12698d052b371145b5b52e02085980dfb2204135a4c3131c3c6696cd897351b36c51a1b576d98103b36c5d68e75a961e316f63aa5c49520a89461b3615dec75aa87dec6198c9a17d468c00a5b6c2ea0b5a58ba91db250c0cc864dedc0c2c660ec754a872c5e7b9db29266ca4a6fb6d7a91cb680f63a9503117e815e56d65ea770c8c261ac40b98261e32cd6e02eccec18192c7b85a2a5b7f1ef550a869e0b657cf165af522e7441c65ea55c68a167060b1430f72ac54217522c8051238b8526d0ec558a852e1b97b157a9aa2d1bff2aa5c2d5d05ea5a8d248515949a5a066639dc5a8c4981de361632b6cc8d8b014ac365e61af5229b420a50d1b306cd8a8a60d1ca488b1b1949427366e438c0d939282036daf525262d861c3a47a5b4eec26dea68d1a4348f57cd8784baa978354cf0605a869c363af0a50e30105a811d3c3101b26012414d0c5141b07d165c326d0c3c62b949929b8d82b54995e157b852203c40f3b6cd8175d9029f70ad5c5165d48e150ec15aa0b263c20a241468ebd427151012ea6e0228b8b24c6ec158a8b21ca38b105962da8c8b8428b2b2dac2250e3c65ea1b4ba58a1b4bcfcd82b94961119ec15ea09adde8ef5b081c318c970d9b17c04111bbf4339a001376cfc500ea0c14c1a3b9636f115ca012b38a0c7811ec2d878af503d7cb1f1af503d70b1f1af503bf42250f35a3ad4b0f1af50567a3f7b85ca418d6baf5039648562686d58dac4dbb4696363a82aab8dc5ab2a28641b96ae5154b6117205b3c98d94ddc2c80d4bd72632b535b88b0c6cdce47b2a5810450b528a8416b02a12122185288a622886a17880d70ae5003321113f6c2258d8f8cf18220b22aa8640620806b0110e2126dcf245b8058a8d1f082ac21f98d8f8b3c21f7a30024bffe0c40f5dc22d6b84444441a489189a1ec4362c5d150006065c2e97cbe5ca66bc56292a2afd4313590d275ce66b9562c102449ae4f454d0a607371b96ae522ea8d9f8d5b082c5a81143cb35e2de6b9dbad2c35d9cc96cb061c42780864fa0011bbf1767706166023b8ca1620519534b90e1e28b8d5da2cb25ba4451145d47af758a09361b3f155aa40833844bc1c2a8c16b9d6aa20d97a2ea4b0a0e85968ddf35448ad1e51ac7d135bac628af75ea8933409348139dddc27616921c47721c473283d77a640d0dbc442145348da06c8abc0fa36b748de393d76a6bc2080454741c91e3881e2f6e64c9c0e4308287861d3039606c2338616c5c9afab5320993330159151044b1b50902ce86a52b2fcb88b8c4f75a9f6cd9b82449b2244bf4b542ad404ed9589b20e0d8b0748db215aec1016348851d469b8d1ff66e82004bd39e22f89d881bf6be612e66072dbbc5ab9818d2d8ed8d12e260b48585a5b9dc820a46f8b0f1e7a23430013460300f48b3088e7d8efd79bf0579e7b72012a418fef3beffdc271fb69a4f5ef3c9afd4a608bad74b5370d2f98b66d1491dfb846611908b095e519d6b944d3edee47b98365ccc344b169d1f514c95061e5a837b99682705c1a45a3beba02017ec935f44412f60169e7dc133f0d4e2d45af00c0b9ee5d7a8a69dabbe828587566c45037885875c4c4f8c9d1fe472d315f4b2b3b975834bf800e9454e1fa3456bd3b68fd1c2c30677ac2e7757ec065ae4e3c382edef44e78b61087e3ae39d866813a88d499c86e0a7bde6e011517b06cbdece11b3c42c6c75c2de5fef66f93e62abd9f721d6f7615608e6fb50ebfbb0ccf7e1d6f7e199ef43355f900a2aa82008cecda32900b0d5e7e377529dd44a4cc174fd145bf940c132d9e70343437fd01ea8679f9ee781faa09e7d2088207c514b26faf1401e0700e92aa6e97aa2e92adbae4fd7dfae0fd79cb057497e60c02ab04ca8251349f06204e92a9e10a4eb29d391aeb2ef49d79fad33a11be92a248be5282a12129acdfe65b2f31479d2553c4fd9cfeeb5ca41efc64157f16aa560bb3e86aee0567805440209249849f03f509f1d3b76fcec77540d1949f089e229fb5999668d59526cd0a588220a2836e8506c9df5ad274687ecaf4accca3e9f15ae025e11b5dcab112b2b0db166b3d9ec7f4a91d1ecbf17b1bce6593e7cf860a37b463cd229e3959090d0fb3c8f74ca682494560d19f9f855d4e2e3e3e3e3e3e3e3f36b98c583070f1e3c78f0e0f16b08a6478f1e3d7af4e8d1e3d750ebe7e7e7e7e7e7e7e7d7b00c8d46a3d16834daafe116101010101010d04f293202fa353c13141414f441413fa5c828e8d750cd951b64604194b28dd6d0036bb8c6475a667f0f6aad11d564edefc77415cf606d5d95d1b23fadfd8157f627947af609424120f45b937dbea7a1df0ffa9dc93edff740bfadcf07fdb4d00f4cf6f9d4ec2a6be881fd3d296a99fd2a5e8959ae2b27f2f9d9af2e2c57b3fd03e3016cb3ae66e3194ad5e379bc4f249134013ef446b10308925ce87df9fc2e2802387e42401e1918914f2e0ac40ec031bc6172e0daa14370f1e4e49800c70e0fb18707234c2065148142202610a22349c2528738be5a1e72959a821f12bac251077d1ee8a221f7ebc9e8b723e41295b2054484c8c471c74524658310c820e24cefd03b40207a3c5e9a83afc707816b87ce01f39175a0d0030aba7929d374fe149026600627763a2286d0caba2202082c2d4c418162992ca94a480930c100250c40002424f920001dd48a80cc26900000afa998a2cd1968c6d0200309521c37c22b61b0c8e283084434a8e9d02949f08b972e5bb464c1e2001eaee820801905414039e0a08484366cd24832c019699e38dac51610036e29032163a845062b229704cafca0a566c1104f0e398a618e1e25923041bd21092100478a10a905f1e173137b997a07185858e166ab82268818028829020c80684896a3b5d0426300196030e40ac122a67431a2871a667041010dd861871890849081100acacb465e03afd16a3e0d3ea3c7c014f8b6ae17980ceec2c54539066b81b3c05e70119908cc44af77c78d40d4f1e5306f601eac73736238df0d8edd53bf30f98dd8258a380c3fac6fbe1774f7d187fc043f4b2bee3a1ccf2948bded9470baa71ff75b9639782d4ba261710ffa4936e1eea29f2412ee3e819f640c2749e548bfb47b9f9ffdab37dcb7b81712c5b3f16c4c4a6fc37d1b95b48624e1a68d0adcfde52749c4917e890ea7323d799f190617bc0eb7444d42001010d00198968016ccfc2be2eeade308f55c67ecb7879fae2dae2a3a5ccbaab767526a6d4ddfee74cfaabbdbfbcc5425ded38e759de0baf94da7b6b6675d1cdc74eae9ba71f933b54b746aee9776af2ae172ecf3764b3c5bcbbeba4467e3b980dc55c107e69e61818fcfe079bc27251e5f73c7b9bb939fa20f4f126279404d4f3a048c3fe36c06e5a18c3fa302fe15f0ffffee3803778c813bbe4048a8e66b5fb8f8d7ce5086f004773ffd0c57f7af8d5f6b97d4dcff88d4322dc1f1fedb253587c4da30c020e4e00c77b83f8b6b9f92fe599ccad47e8dbde99e7649bfb4fbf669f7eeb836d6beb5fd4d65a284d3d99c402feeea53cee6e49154dd2e04a4b7bd6ef7483e33271bef89eea956d3ed70ad1312a4bfa9b99c4de9003577c7e12758c57349b6f6a90984e25fbba9491f75b3a9bc9cad7d0281b7e3f1883cd9f1783b23b0047720a0d04fb0480747f4bfe082f7317b960774db3505b54d4b2a6fa7e4722fc1cf0f019e84cbf9001a02f24163d536aae5ed926c1fb8439930c45dc88420ee6ec2190ce0c438bfd3b19f9f764f93dcb105eeb8c81d13b9e321773fe2ae2dd03d6edaf8005710c41d0371c73fdcb10f772ce48e83dc31903ba6b9e31f77dce33a39e97ea9dcdd007e6a997adb29a9b70a8084684515b0ee35b8fbcdcf8c85e501a9b9245bcbdb39fd001202f241bb35e1d8a3a41f2a135bce557700b8bbea67d63154c10f1b132b0404c40790108d5599581bd3910eb7536a7149414b3e84842af0614b4afaf1e346e4636988b6c301b16a6be3290145f15a16b744e505ed96f06c4a3d774792ba6bd1dd13e038277cc158788bcb39b18fd4da9076fcc41cb8fb8d9ff88a7bcee664d7a4f270328072771e3fb1cedd73fcc40670770ca0d49b0ec753591dee59dcec756aee2f7835e96fb9a71d13fb3eeefec3fd3eb9bbce717a6adc3dc8fdd2dc3de7312caeb8631eeeef8e6feedee4e7a5a16d5af271f7eb78e6bc1d8fa7becef63755c77b6a555eab539bd49c5313ee5b9c8dd7fe121dae6d6d4dbcddd2dbfcf6e0ee3b3fdd8abb9b7ede77771d3f2fe9ee373587b32d3dabb65149ff4bbbd7ed70adeda378bb26b589e501f9e78e6beedec44f97b93bcb0352529d1c3db129ed72395b5014cfc64bc2e58294786c50122e9784cb053de996d0a09e74b81c4df764a763a27ba205193a1a2a82a20901111101f9086275381b93526eb76b839c9e5af68917c4536f39dc535290dab4630a72c714b8e309dcb104eeaeddbd74c73b729a60437a56afb73de740910449de34203f995126df6b9ca6443c20bd49fe7222150a522e7eacef6b9df3bd48903756eebd2413f09dc8d55aeb073f74e9f05de717c5bf79cdf7bece194badf7412de504bfd8e6175fcca25428a6d8ec20fccc0474a54c40fd53ebd45ef15f2660dae4bbefff5d91373864fc59fcbcc32749f723f9bf94d7ea079de0d76813d91ed326524d641b4cdbdcc1dd62da446a8769d66993bc99f8fb7e37919c1be32864e7fb8b37369cfa7f879889ccf712c54f6214f035cabd5c96b8c45e82694f91f04126619a250a3f986689caafc187821b892895fe5efefb1897495caf512a28e45f5739d5eb91e8140d8cf28f2855d535ca3f450323fdb94421ff7d13a532bfea8dc81751aa5efe9b52a1907ffc296f44a6bdfc634a55f54665eee5c75fa2289822e4cf8f93e027bd1cd1aa5b758d5c4fa2545334301abfea1a914f62b47c5d962914924a24c18dca14ca1625e4df87323e7ea3ef47d405fe35baa1e16ce26d369832c912853f4b947e9d04ff357b44d08f5f497e23f29a49f4bb9924ff670ca6408a99484a2a0afefc51eeeb97daa224fcf1a5a8aadee85302fef7515c5253dee84ba5a2b8d2aa374aa3802fa6556f943f4ca380f7bf3405581bff28e0d5195d14add2545553c44c848d303a650a4a55b5a589069b497edff7a7e8744b130d767ecf220079b383182a2b01989acdca1063fbdf31dbffc3b2fdc32f6262e0b261a47bcd49cf47a4e74b51f7473db8fb86260826085fba4ef9fbdf5b8271e21084f01dfdc97a8326081704d07b3c98edfb37457bee635da4e7de9fedffb3c507eff70249fe38a23d46240a8395ff4aa1bcbe3cdf8da0906ffe00c63f5328b01fdf4ca16c5102fbf1a1c49e7c377223f296234afeebefbf5e3f6524553e2cedb9698f08e4c35ec9c3c6375329d88f2f157b52c769e2f7dcff123d4599f9672a2575669f3c65044ba5b6ac59603f809b06892b7a4650a2a43dd8c3d2285086fcf9b15428fbe42f3fafe7999eb802f15f0ffb55a886579a24fbdc5439dd289b2983869e91396554a25346a493f8a24eae07257a7be59ba9949453c91bc18d98b24ffe2623b8d1eb477023f3f64a5763bedf2bd7ea4a952b47f0eb4b14e67a8dded4c9c55b3f0cbdd8e42981fd2b95927afd2bbd57d9273f2c1dc18da82e56f6c9ff8265d8bbd1ebd77bb5337e8ddec76f6454a2d7c8cd9be6e4643bccbe8dc64f23893f3826a2127fca1bb9a030a87105fa4574cbf76e2271a781e90ae5b40cd5e60fdf316cbcb4c1858d2d3666dcafe39cb3d6503ba34dc29dff83a98dc99c73ce396bcc06e93bd657cf6bf91f4bf1da04ef8f51f09a04ef8f53f0da8e7700bca7ef8fa7780d7d7fbc82d7fefd7195d722787fcc82d720787fdc82d774bc3f76c16b39de1ffbe317bc86e3fd310c5ebbf1fe3806afc9de79de1fcbe0b59df7c70bf09acefbe319bc96f3fe9806afe1bc3f6600aec16bb1f7c73678ed7c87bdbfde1fe38073c056b00e3be006784d7c7f7cc56b616faf622f7cf15d3f3ef9e59bff7ad89f1ffb9bc7f99cd7f99de779d9df781cdff3395ec743f011fca32900760cc93e1a0c02fa81673cb2ae7c07ceb5d4f2528b48e491aea24fba8ab3743d4f19907495a53e52cf024a677bf5ac34157b409f51a03fbd9c6b167fce1e321e3e3e730a9cf6a4ce82220a29be4c41451560acb80202588411938596165b44600c175d902923012fbe3033010d860ee35ce13c63383d6baf23047c8804efaf378b5df5c2c169fa41f455e506e9277b78e89583cff4eb2c48aaadff8ce1fcbd3e0673855cb186d3f467345c7383f48fbd4c24f6b08ffea22221a1d9ec0a96c9ceb3f72f24740661684e264ecf2281d82b78e5f9ba50d09f5d4eb177a5cbb9ea3069d480756ad929ce664242e92a1494ae455a2b78a5b744f13ccb10f5e0453400ef428ffc8876f0246ae44bb4c89b28917fa13e1e8672f027bac1c750a3bf41f7e3a0433e07d5e075d00c7e07fdf13c28062f432ff81ba890c781d6be07b5e073a045af03c847800efda3153c8a06f914157a00a041bf03bd2f010afe04687e65d1f20310af2c5e0b5fff4b8bd7c4d7fffac16baed7ff0282fcf2cd7f3deccf8fbdfe97ceef3ccfcbfec6e3f89ecff13a1e828fe01ffdf401f03b5e826f72db78cd83b43c23006999c6693a0aaf1d494b33a4f05a076989e68bd78ca465195378ad485a924185d788a46506aaf09a8fd49de6038cd73848cb31acf0da066989812bbc669496178080d7765a8a8185d786a4a505c2784d83b4ac80d3b418af65909614c8c26b3f5277da8fd718a465184ed317a425185b784d485a4e20025eaba5a599315eb3202dbfe0c26b4569e945175e234a4b0990f11a90d49d06a48cd786d2924c0569d985175e0b92965c7ce135a1b41c63c66b416919810978eda6e51660780d4c4b2d74185ecb69a97526716a1f3f803c980da6ebb7c19c41f0aa82206920516a0129b57e945a3ed255a6ff84e17c188e70a30ad2550c92aea76b187ed812faf1417fbe707e0d7b157bfa85d25514faa0743d6f7abe9006152aa7874163cddeca44b907245dc51fe97af2f0f9f567bfceb6b67c6409a5412950baa23b7f91dccb4423bca6a56b1192ff495731c9f748d75384e791aeb20fde275d3f849fa5eb0c84a7205d85f69a7b7b845484d45dbb8f76ede907e404e8ed4980965a3b50f00a00a84e51b20a45c7dea3792b02344380ea2b4ed3af03cd594ed39f03257b4ed3df83be9e709a7e1ce8cb09a7e9bf81be9a709a7e997e1ef4c584d3f4efa0af2e4ed3af83be96709afe1cf485e534fd38e84b09a7e9bf415f49384d7f0c7d21e134fd27fae2e234fd30f47584d3f4bfd097114ed36fa2af225e57e88b08a7e91fd1d7104ed3ef425f5b9ca65f445f42384d7f88be8270da2b0821b60c41c45511461cc105892494d82f2faf265e4ebc9e2073d695dccb5be3de7bbb6730a7eb6d02d2d6338973bdbdadff15c40b11c1160886d041448eab9e22701871e30819171e247692d05122070b67899b2e3126ce9717d8ab89d7cb09f3f54449f6c89c35ea2baedc13f35638f676d596d7aef4975abdfcfa7b65758081be90beb117f9ddb5d65a67d75a17f11be41863ec464e30fe6b997372be9cf9d7d2484e73f620ab70ba1371e77c3f5fa14fcd4d0ce7dd2608777f613ad0cb9afc40494f1dfcb3b857f28f84e433dbb58f54aba94e514a909ef635de6e0949758a6a796a2e67db80b7635595559da2da16a784cb3941a93f5445f373e80043b9022f1cf85901943b90ed752a135bcbd4e29efce89e9e985a56559778f000bad9d496d5e11e09e95556005fbba9ad4d49e539a9b76771af7b5a7a16c7a484070f2077e7e167053982847177223f830499e2ee23f81924883b90cac3b1ea8de5ed96a272bbf6919e06a4c335ddf64725fd4dc52de1945427f65f67036183123e4a67fb253adc8d675ba2f29e7dfaa8259cba44b7cbfd4ea7f43695877b6aa28ee0ee22f8f9038d1f687edc70fe58f223e6a30c1f35f890e2c3e714da72f700f8295483100c4245415beeaec4cf202a18f81984a403400d7077d44fa028ee3e003f8166ee2e003f691170f71e3f6958aabb93e027ede6fc9102023f7f1ee0ee04f0f3270420103628e1e769aabaf44d6a8bdbb53a1c0d88f589ca25e15876e7f4379d6df6aabaf451eaed29a7a4c3b13835a906f403c8ed08d2eb6c3c55a72ef9a5ddefafd180a26c3995f7bcddff3821c007086866e3d992fcffb43a95674b7200a45793704e51ede77238023c1053db42a9ad6ea7f4351ad04d675bda39d99440f81a12d2d38098b038deb73635e9a36c4dad4e657338db92eab4d3d96eaaaa7b16a7f2764dbfb47ba4a31629cae6f43ba7a7ff416a525bdc91aa2e212ded9eb76b955427eacdd6f42cee9ba26c4eafaa455ef7b3497896824dc2bfea64d7f4f37fd3d9580a9ed87837a616f7e47fd7ea784f49513c1b4e09b7a4ead424a4ff9bcada92682d8edd39a9b756876bfa5dfb7fe3d99ef0764b7fd3d9fed9dfb5ff5f7bda0321b5ac7a43fa5dfb6a5294ada9c9c6222101f1763f3cdc92a71d8fa9fd9c4d69976bdaf1969a76ecf3d425a6f6e8a6b31da94e51edd1121d8e7d3a3abaa92dae0997bb1d352941b147ed4da7b2eebe819f3cc600626d4c4a385687e3b5506a6b736271b7e7a94edfea6cb66775385babdb6d90c1fff814fdf0f1f43fb30b94d401dcd49bee6967c42af158281b083af57684a7363db1badd061f8cd03eedd4265cab3ac1b54ab6231697c10078473cf5a6db39b12aafb565e053f4f43f454fb30ba28ed4a4285b53d4cda68ec053714bb87617c2121deea65393b04fadad25218aa73e8900c4e29858565552b27d54126b63526ad5a5d7d9947e840752792ad3df54deae655bd696539f78edef9e2501e9008f43005293520923d46a6cad76d4a4048574805799da253a9c9aa4e6724bb86f7536276a4ee5a94a3a9eeaf43c5bee6989ca7bf5b6dbb1ffc3aaac4de955f669f7b99c8dcdd9969e8db2b5ecb7b8bfa92ccec9cdf62c052d8e3d9264ffeb70adcea6363d8b7b16d7846b3f97c33df9261b6fc98ee7a4db7d544e87e4816eea8db753527937d5094e55527951b9dc4d656a7749b36741d8a0845fa2c335ed947438deee792a53ab3211e081783ba6b6b5b5ac4e75629fd8cfd992763923afb6bb269577539928ed72aff26cac9a4bc22dd13d1579a0289ded9770ffa32eb1b5ecb314f096e09a784f4a27e05a1c6f87e339bded59dbdf54964909c7f4e47f6e6aebd4a4b6adeda653d9b77d6be3a98ff403c813d2eb6cff73535b9b122ee7f4c4c103edda5799e0724f5e7df2404bbb6f5299dabfa94dbcdd920ef7ea936775aa92cafb96dd35d93e97539f3c6bfb1f565da2b33129f19e76bf6b9f7d95676bd5253c5bdbb4e32de16eeaab4e6c4cb9dc7ea0679f7ddae1783627ff64a763f2ea936f6d4cedb31fdc70ac6ea7f42acff6ec8ee9c9b33a95a9fd9fa5230ff42c6e69f7206c50c2efda6f734fb625958307f2c9e56c2dee9770efe35bf669fda85ccef6c43ecdfea63ae95435e9591bd3b338a6f67f69f73c35a9e949a73ee56c0378201ecee6f43e482cd2df5427ffb39bfdd2ee99581bd30f55f0e359dc47a91e3c10eb64e3b5dfaa2c6b6371502aabe296e870bb25ba1d50144f7d6a92c3d99cd8be65d5d6967b9549c9abad9a736ada29e98ee8763822399bd2ee03f6a6b6bc9d93d3133b8200545600ba2337ddd3ae48bb0b8189d2ee03dd129cca539dec486877216c9689d2ee03a327bb0f5adece49cda94ada27244c9454a60d6e6a128ea7c4263d91b00be1839bcd48b7c3b13c7500379b910eb704c74372c3e56c4e7447d85665d9cddb3129e96cea081b44b54e4d44d45c12aee5a94e7447da5d081c80a0e6926c3a01e4704554de530e672b72d3a92408e0a6367921ea0938b6e9a90907656bdab59fb33d35515f4dfa5dfb37f5041cabf29ec53dd61e813b5875eae0c0bd4909ea888dd2d940d8ed942cc119ed764a72aa4e6763da6017422ef754447d529f44605a82539ff05427bb23b57d5252a1549e0f9a9a53591a6fd7da72b99dd2918ef794a42ae96c4f2c8b3bcad9da5d7bd4b24febcd76d4aa2cab643bcae56caa135cce76e4a4aa4c8e8e5425de6e89a9b5a9b7a327bb5cce76c45397589d8dd7b6b81cae7d626bb5a356a7b2499b83a5dd8e9724a72ae96c4a47aa931dcfc9c9e6e3e896db31b12a8fa93d3a82b9fb07dc5dc94f0898705f8263716deea9c9022034f5497d6a425397d858566db2f19e6c2f4dba639f1cee36c749a18ceb76b856c9c6ea6c3c253ad509c92e041fb42625a80928b8c0f11077bf8e35b818dcb8f29b7ac339d9f19cd8a7a69b7adb29e9d48fcab54a2a8fb753fa76a77b5b6bbb7dab53792a930ef74a849c6c2ddb846bd525bc2727364a6572d4aaac6e876359952502426355259d2d671b4177643bd99eb0514bb876a76b754f3ba52395b7533a6a59f5a989cacbd99ea83a9517c5dbb52c8ea97d4ac2f15add4e49b73b72daf16e2deea6f2948e54d6c6a4c47b72529f74aa139577d484b3e594744b702c6b53956c39dd0ed7e25a9ecd89557336f648656d6aeea85559dd93ea84a7b24b704bba9d920f20211a4f65b2e32535b1b627453f68aa926d89a636e15add138fe6c4560084e6d4646b59b509d76e3f6fdcdc5de5ed744b8edc5a257555794abaa79d4e6753794f4ebc27a723b5b5a93c2556c5a947b79b4d280a6ac773a2b56a2e6773ba1d45e59c9c6ceacdc65be2f474a49e80e3ed98da275bab539da84750b82595e794cbe198a84b6cbc5dcb32515299948edaa71d538b7b72a4de764a47adcab2ea129bd391934d653a6a559609cfd6a4f284808680d09ad4968d62696a938db764c44f1e20ee4037957d7a35e9db9d4e6753fa26b53dd2b46b7536a62447475f536f3ba55773493626259bcad33dfd4de5a9ad4efd5acea6e3a9ad0ee76a7a481e3685ae5d63703dbc726f5fec860ebbdfce7d5df18726f24c838620c4a2f8e97caff36020c77e45d01a5c0f857c0e03afdf7bbf2f0826b1df7b33eee0def0e27b337802bef8867867847bc11b3abe2650c620381bff6a8cb5d5d5f7e2f172005f977f57e3acdebbe27bb12800dc832f0635be65e07b417c45b83cf7662c4af1e5f07577eebd175b8075c47c75b8f882f85e9c8f2ed6a32b082e6f06e2fd76640c6e88b1f691478caf0f6ce82a40fb208be424d8b5e3075c9d67fac337fbc7805b81ebd8cff3f4eb575f7cd35c11f3b8177f18e77b81dca02bde12638cc10d2e05186b7c85321079439ce09a3738b00df7c337aebef93a4e827fbcda310ef447ba2f6447a5b83e840a9c2f787578cd9bab602cd6f22dca1763fdb874cdee776f28a47d442074f1088a38bcb77631fe2283d78575767253fc746fa65d1789af185e3c04438021c03ab267bfb00b627c4d7d668d6f608c2f78f5d275edb8e487ef775f443df84848baf04d12cb105f11ae786fc657df0eae0e6c01ce81491ce2efe68b2fbeb5ab039338d4377ff95ea29befecba6e79ef157221c0630eafa82fbeb7762fb88373aec6f7d3d7024cde8c21b83963acb1be16607c71e0f1baaea8f1bdb77675e031cc578bfade4bf457c643bc20f7f86ebeaeab65d7e5aa9107e881e002405f8c411caaf7d411223bb8175f7cefa579e84e6e80a9aba60069fc0bc0c319ad02a062a02180ad02430880025bae04f5c249e0410b95045b163e020f5e741801dd21118187554d125b90211ff000011a1dd8720859c0832cc802db2b08220a983b88d0f10e51e07e6008a9cd0e43df860321b101010d490bfed9f261f7f3423c7a5cf9392302a42f23789e1bbf82830047fa64f0d729b8675fc251e2ae802713581e90ea6453625577133c89bb43f1f3868badb5dd8e4c58eec94ef4f40b3dfd01c8e36fc80129a70fbdef2bc309bc90a279f0a1794a2c41802df4f407ae9d31872c859eab00da001902b2054bb18858048e59886cfc20d013b17a3926487111651d365086d1fbdaf4be124717bdb24d9de8b9642f1c0af9823e1aaa74c81b36a0a0a9f460280ccdb45890288bb541e1eb211685176458be40c4d2cb20f808024750885e26411cfa87f491af2ef5cf58e4eb111600c4018ee05ae694392e996b272c40fe000a208e7c85c207a627c1aa9bfe48021dba56c2c098be999637d0494a1e108704087bc2103e9dd705c9fce932e78c7398c5ec1a73e81c0a20bc812519632118de1cea203a485ff146ee91e28ae00ba2f712424a0cd43d220728e83050488fdedbb4318604ca68a1650598a72524c86045c448030c8876ba33d2045170c2c4f000064012421b3163b6882006a12a360cc1210c325954f18412446409029aa086a7360696c7010da831c3c5982990988a9200130c500292108c148900029e1d2f4c4c1131441428473a72ec7411bcc200a34953af4d1b1d50c38403f0ecc4c8544105144f2891441131c0c0c251020a400024211cc9420c15619aa8d2c1175e6451c5144f38e1031617544841036ac870f184135e8ca0eaa9b7234462a69a0b881186155330d185cb0f32bca0de8e1260c20108708488909a4fcccc428c153ae42003957a4b40008e142122c42f20461662aea082892e5c8af86101305021200047301052230ae2c3e726a6bdcd05c400e30a2ba89882892e45fcb00019607881aa0247083041832020366c7ce001ec35811c56545c288100423dbce04214d416579bdc06fec0c7065e430c4203817fc059ee0fec230b5d2092967be4df8900e7b83d218e7ce3ca34cfdd71e1dcdce453c3cad769e2286f9039c6982b14c11083e0f7e5eb20d52801047aa12b74e9973814bab28e528994105271068e62912e615797e0917e92cd1cd36552c0a107be70e8d23fa68ea2217ae300ca9bfe7949c1417a3e01e80b5e10450f830f3888a580f5a83ff0e9400f0328f0d09272bf5a2c822152b0c691694e17cde0ebc1450f04234842a5033352211b91b242ef868b00654e997342111d27911dd5e85dd0ba8c202de00c5dd9fc986816a84811f50bd4716140a48cd1838b471754e21a1b5e98fc86c4420bb407ea01ae50c0f96663917102d004cd23b4008525783082021031ec9c4254a6d2072d7ce9914f4b39a460fe80dd01e4c842c21b4352c6d00270145f99e88891ac632ca2466f0c8f9824881df4102932ba3a2849b85db0448f47ff0c8d600dc704c79f4ca8140bd60d63354b228668464404100000e31540304020108ac66259900661a0b7f814000d77be5a625096c8b328876110840c3284184208000600830088940c1105002bac06311f38aec58d633e266464ea74d90402e575d21899e916cbc940a901e98135c62a9392539dd60fe947713934a3d9566cd1eea365342af974b583e9d99dc0955e3319084cd4e9191409e480cf72456ee6bf56bdfcf7d0f1030f18cb52a2bdc5c04a97aa38aea42f191e9875e5a4c45067c6f87911ef950d7a6e511be10158bad61cb7061bf22e24d0ce84e513337cc6d4cefb3a06da4bbb84245001623d141de3eeaeeb2fc3da8d60a97a09a5ca901e555944ccb261a8aa77248489d7a154fd0cff5d291c0cc1fdc58b8a96a6259dd26021d93f987b686af73b7dec754922cb4abf6750c1597df7741733010195c7bd2c0813d9c37dfb446b5ec9ad2ff8cd06585bbaad78287eaa274d960c3bd9e6b2dc19b493e4922a69f1bb717787ed76149d45e30e6a9c41880fb5d833eed977a5c717fcfde334433bb3b6366d8aba36a5be5bbef8d3d3127cd07c8d71e480818d3a0986a23e24900429dac0b9e0de6075acf67459c058fc603a807a6d980686a92626b5e989682f2eafb941df499ec496cfc206ad921d7690015a755ad6fee33db77ef36110d68ca81f1050bca92c6c97f214bd7b8f8ba46b0a17de4a00c9865fa8a6483b6f4961732b8d83f5e3365a691ba17403e787901d2bfd4e07048230d7dbcad773c100f5bfe3978175796e13f69c7119e8d7f2c6431fa56c4da0c243687ca6dc4b6615c768d45dd60ffa16068b483541ea12231b2600d339194586d2f8265ceb8bdf5243a24be4e401911681c8eb50c02e418d1fac8afe4358d76be4f59c61a5aef9ce8e1748b5ac7413fc9b8c3b6ace6bd928d8afd4b8ddf29d52557a91e1cf4ab4fff7bb3f2f93a2370baf604f241fec01f782e14cdddf3ac358bf07110a0a1a3264de9b2a3117eada75e2cde803647e216ea2be2362aae06104faa3640816ec5471a3766e7e2033a98828a41f35d5fd424d93abc900326f037be7885489a26b3bf44079fc12fbc75ab9c8be63669c13f1ae6d84c4f31ed332e7af5e60f46b602101b31869a51a44ea09bef77f5003de00784d797263a67c70058b30e057aceb0d6ca7810d73cf3cf281e50dbfc1ec060176d57facc55299e1b1dacff5807ad462210a6e0456fe4c7052c6eac0edb8b1e1ddf32b1496cbd8199f8d95430747c03b974bcc9f4230565140548ad17fcd737db1b6560e8180abac222f359ed296db221d85ddbc9ba9ed054fc3431061c1491e6b8642f1ac8c9964a8c1bd58cd75cca559e2458d4e8c1dbc1bb84bd0410975c84d45e27b065f7ff3704405cfc6d377cbea9c7d08b146079ab922e4833838d311b50303d768236444a75dcd69d1fbfd1ffff16e031057154bfe51dad8dc75873a970ab77e332be81604b1c8ac30a1a3d7acbe274533362d55bedbf1dae1b5e3503d52fd63e5de14c644005e7f425debb85c55ba32eb44355cc61704f4a9d73860010d8edb442a6581c11a5af8eeab96ea19c639223236e3d8b1f5e09913bb98b28e4c863078705a4271a85f334306d11f0ee9bb40240bff022e7bd05b0dbc14daa991bda9940d0b28dc5d200273e16a6ca418cf2612414594213a38caeb1e8a4722e1565f9ac81f61acc42a6190e80c18794f76bc8d0c6e84354c3894a3b7cf3d2b50d3f292d950e9b798b814885d38aa3f58e2f5a68d04efe2a0c5690a964228c1693bd8f7106c1ee95bb3a0707e9ba1088a8ac542e8eae82888be0c19634810c0bafd2f277bef5db7597a3d074217f8f193513ef2c238f1908426cfe12b2ed05662b1bb6270ab7b2b71d2aaa9a487eaf49e2b16e5c8cd30e5d34fe151791f4ba37f7a5d742130f0a3f48e13dd86e1e6a09d819f5622b5bd607a90833827b0dbe78a0e8cae171f80e73b630dd34ac14e91dee5c8c7f6a8bb8ceb50420d39269252990ec3bf30cf7dbd28ac3250d22e0738f09e48afb63f5354f44ea11558719a52b4fa1a42b0d46bf59239fd818f971fedba497a76430e76b947e891bdf456e2c1781e67060539f07652241c4898c2773c3fafef1538c2281f9577e2ea2b9772686926ff233d482a86ea8a7815935b537b3986a4bb15a24df23d087ed3f3744a74e27e5b117d324e70b037bb75b0d431889c910645182a6ce3729dbc03aca027c49907121e204aa922b9e045307e34e69719ae39c2e490ec3b8ae3ad7bd065bb8664769ad955093f9e1344a25a0825681304a4703d61026d32aaeb458c1b8ad7e62642179b75dfbf8c2896199f83d60466f8929fe03af50f2b307fad86fd09aa5eeb06287e1883fe8e90ff9ee9ec059372fd129cc3591462728c907512949cccce0d74f6af3d6e7d372c7952feb1e98ffb77e585ecd4d93206a21baa7d22926ac55cbb76af097ae58761b80f3e497028c0815b412b99e80a4e9de6d7e6ffd2b4bfa11375152d0f0787e29845c434e7f874041cd0b55577d28c7820bb5896cfc2cd2d32b9c41164610e65d2f78c9d19a72326b8974e9dc73cb660708720fb766da1bd9ebd53487d0ec876b6a1da90f77167b75b2510ca1cfc97aef5f6b3ec8f171c69f63e28c60de29e777e72007cf89a5fbf6907e6c32b7fb2c9a5b85c9b8b037c1d95d90ba214ef995f2f97b8fd80f52b78ef07db6511bb6cadbca4f2893c3f2d4b9e5acc6ae90c9938cc42a7a760d3142f9b66e37a4ae26bcdbac1d39f82efed9ae0603c4bb925ce544b1f67e1fe85f705cb18cf163fb3f3c6626fb7118448e2250b48d287efd87e9d70f832b5fe1cc3ff286a6b0111e6a551ad36269566d945d649f020f850c66206cb5c480efefd827170ff0f1f5f8a68edc2260f6c3b008a844c4ae1b176d76fc22012135231df30a36f4a6e372297274f7c15d107679c091dba636bb5f846ea5e496bfd933ddd131b397fc6abcbc57f30687f1ab3beb045fed7abe610d34bcdc5efe26e733579090e919d850a4cb9745b4adf82ed9b1c31dca369ef2d2a113ef4d3b6ea7131b15797536aac8ec9802d9594adc38038611117ed92f5f64e77b056de6e323c00e58440986164b9302339ae60d1a145311ccdff6094b80e44f3522e39fa005da48489da750a30c8fdf51c2d540490addfdef2f99b7b59757ce6da84590b0ae794014265f9e45bbc61b30dd4e1220d7ee643e9c2d5d1619695be8457cd02b2d1907d703a758701fe9d2704a7df7a755600e5d49c5003751f26f337f876eb019602349e3254a0ee74d540c0bbdc90ca5d7984ce456e4be30d749fa18c56702fe84ba0bb67f06155b8b82a3acb23abdbe6417c4bb01766c9ed8d2c617ce2f3b3bb988c0946fd427382473c97b23b0bd46521405964c5c431e9f8a97329428e74134d256cde925870710a1b12fb8078cdd345b8184298fcbd657acbbed0a486049756efad183ca62cfb3ee939a65c1702750d3c5ba9c96990d55ee6afbec6bff0ad36c202d9b33d633b747498797ea3e1f40781cc7d0000eb06458ebaa61570f894b327b63a1961b5f80a88fe018d23dee4cd333b1ce6ebb5c29528011f7fa6ca33c0016138e8eda920fb11b0012a3414a52427da80bc5f13a7b8012c99a15c58d3a3bec67133616dee2618763be6aaf046b0960ac25809a1a66d22bf9889ced540aa8a31f3cea91825fc37448d2fee40f43e1147fe1d59ec8414cbb731d6b7b9f1b9402fdcce9036f6e17c47206d326937d43b820b6f11a39821cc1731374ff51e073d15df8c86d4084e34161eb68ea885f29911fa387b38cf5c953daac76327e32d4ae0f70442350c18ba1baa66d047bb191b785f729b64201366e3940e57b2ec61bdcba91653ad1404a3df66630deee561f789d528a94f7c0907db0e9b7cb4d7afd8fa251765d22535fa3224d0ed8a9b496003f481a2302b0716658382e3687384fb2d1c1eb6e0245906d98b215b3750508a6f5a4cd30e63373614e64bc00dbc24e8183fc40974ea29ccd623062b898e5d58781629136e965bad2e7bebe7819c4599f1cb65d656532d945297db1d862bd83355c9311d4af564da8c67937ef4409c1162ad021c1dd954d7e6d41d02fe58f48f94aa2eb8a0fe1ff2f46577c20692e9aab52099085c6a52f336b2a98085eb68f3f133c58f8c2b122725cd64597ebe9cc956885e7fef42381cb7f2d174d5c6c0b697595d79925926ff59cfc5df66f86e3d06e024eb01505f506bf84d3f33add699affb99d208451499937d77f71e5ae9ccf6c595af487e33178855fe62f02d37b5f83eadc072ad94544cd7c0d529888638674c0c31385cc99ca7e5178516b05c1b7b7c7ea3522ee19c15cf928aa86fb85c313946cbd515008c3ae3723b9f83fd9cf8a0dc0fe390968255b8b2ecc6d6062d8ae96f1d9a01fc689d9a98afb1c045c7598530a3559fbea4a6612ea0c260d02e01cd10aa9b6898bf48514b95f556d20131bd8cf004445eadbd3d076cc458b2c5839e6192e570d73818c6952db27aa0cb8608a6e664e8d84ab440809e7eb0b34e09b519b91ba4ac0b7ded11a41739b0fde06e9e1374b2830c165adf47429855da5785d6c7848930eabc78c5e0648949d018a2640724fc43f8b7101482c2b09514cbcb9561e69865ba80fe12d86cbace504a1d634eb53c903dff68c32ad4aafff9986c30ec36a207b1f4ff5c848ba159f3000af0052876dc612863f1dbb927dfb9be82421fce1a94091d73a280790e7e380f57f46e200a2883ef9afc8b625ad0cbb812de355445891074205968bf3727ceacc7be994f7751a07adee8abf5ae1a8761bd4aa79e89394e6adb66a9a1b23f80f7a53c10d492ad3c28038752dc359e20345d3c9cec3ab8ddf246edf1a8ed507bc0ad0bf5834019aab7751fe783be88e86d8dfccb115909cd21a8848454ba6b481f801cd41257e5ae51035b1aa689090975ff510c9d74e812eaa16807a0a18f54b50bf8c8587fc56b28f5b2c2d5cb1f054074d1c848f68c8851e175291fe74dd2fe5bef856641f87cb2d05941695d730a90e44ee3decd426eea59dc27f39a8c324ec0bed5f904515e5a01bd85bcfc651a43cdb186dcf1e5a3057dc2d533f6e39d2c1d843038c447be29729d8ed1b16b9d45254d2c9bec8176a0530ea8c86edab07a3a274474dc72b1ae2f50113abda289706c8df5d856f8b024ece46954778d238bea82d107bff792ecb7666ee183c57411eb23992f6b637725768c0e5e1095cae454f3a4a87cea8de0d2a846214274b721e26d7e23ea4edff4f281be23b1961d11ecece6dbcabf56be721f7ffbd1b9b2bdc654b0a70fe047eb69a63880e866900b61a160cefab91d3a1d845f7016fae90d339c8473f297fb37af0701e945e9fc09a8fe7c49e44eba3f4519590764a1b3803d0d5aac1397db557306cfd0af4f13d08512053bfd677706a08324f2b0cd426b8d8322dd000feb6ed9a1deee8200be216549e52c5791aa871a460e6921fd429b828069eb0a7896bf3926d68a2df2b97cf3c443d71e9eca3848c943e5c66f08d3066a5f2cb70208f530cd112dfe3f32c86aa523046d6947eac66aadab9d552dca97ebdcac904a5cf11e52bb6eb1c5b91c2e7627bd8cd6d12276ccb3f8d2a4f24ca2d52f6e4a98eebf65136abfe151d8a4c10d8b148845ab7e4624e2d578d7c9419edaa0de912b770f2115cd7d909c0bb5276de9f91367049909348200bb9c389f8ca1b933fa7b6f80229fe1669aa3bf6a384f046236aab04fba9445c1369d45c6c13209fc956773f866bc03abd18cf37464893bde974e31a68678ee6eefcacfaef4f59ad448ad7559c3d516088977beef05efdefbe3ac8ebac39c7808c74681e98d66e631b7394c8ca6ec1549bd65bb88eec713fef18707e306c8b5aee10a6cef56f2a36c8888b70858fb449823f7a1b1c35868045ff96e75cb6726da83a01b7efa259b2f53169ba022967f7e3b9b2c6644b99d2cf14b56b887242f45b21ebf096e38107b5e46aa078f4ac739fafee4abc8b787dfe8b7179272219390af1f3481ea6c77aa8052048fe1b78ccddcfb0770bde985d2c40081eae27a20af18102ea0e11784094f03e1c8e358fcdda076d40b422c733be885288fc3d163e35004023181315a02c701a0135ac91f78f0474b8f394332918635fd4c080c3fe1b765cc8954296125fab57bc0b621ace4723f5826df086b71619d442e4bc8eac5b0e72596844ff855bd80e6e81db873392ce4da621b5153406ed562d659be651ee1d5a5e51f85e244a5d6f261dd223836ba529253b0c9d5d8091affa62c316a803109b689e902b3918a983b86a2465ad70ae977c4c2fa2d1b00cee79cc4dca074a178910089dadffa3b58624d346bbef521d8d7c1171c624e9d0ba0d1c3a6adee7d6f6893cb0f033a24f8c1107c0fd324da34e07750583295e4381cc7379c158fe451fc959983c4f5ca6bc2a70da637aa162c22a1decf94f7c93ed4d4a3897ce6d353bc98dae0e8979d1168db554f25189eef2fec9fc790b2aa03e38a1b8c757e6ff6b388a34531f8874b36961fb228cde6014ec863c42c737b1fbad177a41d2cf3829839e17fd7e0cbfff6834ed171278b5c08280ad57be317b45731733138b012a76c8919ad4ba76d9a690e73de9f561387dc889e253ca14b4a7b5c21431c74ae60d65ac5a81fb4983de030027062cea6b04d2b99e1aa7c3027ca511c9f0c5df36e3d73efbe476036b140f7cfc67953e2ecc312aa3c348717f9d94ce8d813926c1629695f907215e4189c215f1aff2cfa5530df4c9688bc7b50565dda698d1ce8708570a0de8ec0c82ba1b7cc4e2a7c6ea971876448f0243913c8c4cb0f48a71b9217151221f412883eb27d0c3518da3c8d2562f45854f65686741f65c19c4c841c77a1131d025c3cacb20b13f009bb06b42d0ad3ef2fff44fbbdb3029a6ac3c0ae5f3a6a68b13af81704c8b04b7568632135c4bffc3d5978fb70bcf2c85429d30d75cd14079974a3338f84a89d6371c96b4a15f3451512706718783e53d4225cb003fd21aa24f939c6943aca644b8231ce4b18eaa45252bd270c63595d7e3e77938eea18133769d3c68f2cefc7f2df83e80f67afa535a83a9424739f9b6843a4b7d09e884de523dd31c48cd98cb6a65129bce286a00c406aac6778dc016f7ba5aa178e10fd7f41d69085f1d22cc05b310def0f455c171728622f64a0095f98c4f408cf32c39134acd61d96e81c4ccea654768db0081b6cb62841c281b69f06770c2d4caa7ce284524632d40f91f93be4ef944d636aa556c18105148112149bb46ea93f8aa2f5998ac27c2352068cf902db251b5829a2ae4d5c83c505706471dadb8a1d2fbc3bcbb1a8880727899057b692d2f74db2b55bf39b7387525b984d116816c77caeb7245e20fb6d588f1a29610b645374fda00b3a1c13d85caf7f4461198d6666b1b93e117aee382addebdb53020377117e3c4cfded0a263fd3c6fe420a8890c57317d258a3052b9425cded3d3e99eab00e09c10ac0b2d8f00885afa6b6ba84fc4a0b8b31a3459a0ac3ba09063515d43b87aa07e4a46286eda75c598e9dc5f4438b80dfa8eb573c1bfa166d672f95cf55d7c664c265b232bb20d978132c526834de30bdd5990f628db60f816d9ef57fe105baa6873962c2719220f65d3530d8001944955b67bba0bdd506f4decaad6603b0a97108e680ff5647a88fdff07ef396cf1fa22694634c8eb95b8b52eb313027463cfd60631195a92a561a1822b7b1a0a7150f67f566fc78f2598734113c9e540f51ce9b5e30de15077beb1ccb1851c4c28dc466462ce6ccf98cd36aefc70e8460ddb02755af1d062384429ac50bef7064be1008797336e92113a198294b4917641556610aef770da223351c3c560d4adf62fea795172ab31e643908e225042712828e29d8f8005c22c51fbe4896db0fa6b5343a4dbc7750b35a491f41c25a7512a3a68e86c9538b4eed0ca9bb41e7c95b6d92beb961682c47520ec5c4588e468067e60406c53f0bb9ccb244013032bdd4c45ade115cc1fe17672c6e823e04d03076befac32fd1a8a7f1aa6d8e0ed738704a46ac175d19f36a3acbddd61e94d4a6e44db6357ed71ac402843257bce95c662236ed02ac7c2984f1c8f9e9fc5d6b6b99d6c4abcc56730589270e7839161acf55085fb11a59a51442e35959db9311bc8dfd60be56b72828bc6409a66b6af0e01905ba652f039a5f25457242a3769f6c023f05a820ab2e1ad89c95a50d5b91764ae560f4f056810f71b7e8ac3840e6fa1deefb40e809cd37bd7c9645168fe3ca6498ee4412728cf05e964d6e158fc74aadff9f64a3fbb178e805e16defdde2d9e8aa561b3847f4f9c06714ef3613c4740b8a7fdfd79a35cc908f1d88b73094feac7cc55b62694888870cface81010574c2d81bde3c0934bd730c96d769417aa2f1fc3c28c5a377cb0a2b1e8e5d121c0bb1c9c7ae541e32b8f0a3cd63a4845761ba1de26a345377c6654c7a69f140fbd0e82691d0c75b66a32db66585d361e4755e630f38a0e04925f26a0e0940958d391d85e98c7ca9aa9f611f9d3933f25bdaa5010895d5d9a79500c593b00281422c50e09d5334a32c0de115a7f37ff0203a57cd0434150cd0e4b3e05bc66493234a5f82811bcc3ca21959218421c6a6d927cdbbcc6eb63771344e03a9df9037ad20143174f6ae5e007e7b2415ac96026d34df7ff5886534e3d069f94224eabc2c681a02a9e852e0800a0f1d571c165a0c0dc5c8aebbb945e0aa7a69992d92789712eb5da8ff001563fe59662b625e673803f8ea947ea2d4e96bc2e7eb6a05f4312221d6b2d88adbd3fefe33a495d53618808c25f2d609d3460bc73b13494b71f681a5d42b2d6b022d4a82ce935b77335295b5841149950f90a19b9f64505cc71ad31527daf99ad0232407d5906404cdbb11dac61d1e6120b0b252243618bde155a725a087ce293a7f7af12165df75e2a700758ce70aa35163ceb05c0dd8755230d6ff6e232dffcea296207a9a9a6c8fa76eb0ecb3f13ae2c8d0d17798e5fa7eb65d705d7bb5c59deb18bfa978f28b7588dde571a76eeb9893d0c6326fb73c39c39eb589ce348aaec93cbe9389a3bad34db163fcc9cdf4b74025407bef90260670621d131ab72d1bc35aeedcdd3017035eb0388072a42c0d54b290d4316e959871c5c5d1fd5a75f2c5544f8f8f8fa9ac63d63f7836f32396efd4a568c64ff7e337daca2de1782165f9ac63206d9ceaef9a05675577f2d6316eac61c0ef17670798e4d3181bd8684b5523e0f333c0fabcefc251d7324964f89d81fa8cc4e8114870778866c45a9f0ef65224d158d8858741661d83cd4c01b26085816e0a5d741d2b9b94fc8e669001778cb917ec246218a1177fa59814b21f3dba3ec7785f1d83401fe9db3258fe123c5644489ac33e0832bfa06a8893d4ffc76833583b216b40247ef896dd3c70312185a072dd7dd980ce573d1309b2450936468d016c39d690984ad4b20578688402f6b03bbcf2eec6e1136ea95bae1fff09b0df890d37c3a4051d2821d299885fc07771437c0c2e4ba6486a855a878ce0fb1c3190ecbc6264be13db2ff2ed15b8ec415825be4733f916564a9e72f3ba6a34acd8d8e6b7102a64d8f08cc28261af662c8453a04a99e7075ba0dc45bc52cb28c9de4e96df0cff2946dc320aff01c87dc2c4829f31fbeea2ba011ff59eb177f3c6cb239f41c013a5b3a88af442670fae59a2348943710ba142e34ec9a4340115099a7f2596cb87ec4661ba158d280689c8183532d1047d15399636cd1681b0631a104c4084f98e2755fec128056c476da53c1d9108321b6b80739f879a43551216fcec171a2c29325305649e18c1ad31d6b7c9579879505a0f9dc7fea0a772ea31cc5ee76489d450247558099107f3c4878d70263327a4ef4505a4bd298608fbcedb536be4c8da20bedc91ae0b6927b79b5188377b50c4d59b8b23119e71a7c5eb38b9c27243113e39e04720b4adaa2cdf51d645288cb013d0661e2fee236cad1725950cdd5f91105e6ecbe623f4183813474b0ab349762d5214437b80a41258d102925643a4486020a6e0da06a94463b4a549e343d8f7657404762830e74315d9b357d73ac56debc536003c5c70e455936b4175cb58ab40a48d09ffdb924120eac301cadd2a61b6fd202280a0e4379dfa94cfcb412a2e0934b34e2657ef268b7baa0a0e31e4863d464b4030a7e84c411fb8ea666edd87e570be5040fd2c80b0d039a5159766ff13bdbd431cb2c3d2865835b5b68c8ef410dadf97f002bb45ce608aa737fbe288108e357af5452c04fca5a72035ebebd750e338454cf5e0600da7b4566cba2b6241ace172f172de44cd21ad15809a26e4ffabbd11c266c4314e4b5785933c336ab81775c7e7e69d363729108a96988878297e11a9681aa9e3804762f81c832d2d47e6065f64a2d6ad4781d35df9bba797412904147f27b6bc2d26228d3d8c0471f84ed6eb4c469d12e928a64476be46e60b027bac3cdf94dc410b0854a92ea1645c5c322c003469be87967eb1f6790b20b1f56b06d5c6d059da9c623b6ede2ab0617d1c6c2182943f2f0c1f2e4e3e1fd1388d566c5028c4bac0c7ac96981dbb0654347b671a71c0b964d78ef99fff580d149eae758b8465f83bd1b1b57d1f9a664fdc1b57e3820cb556704f47d16a05e01117f14c7c7c95d4de0e7c4c1846f8553a2668687427751130237a73341862b8a4cc2c4318cb617b8f1632620b9927ccbda3dfcb263bd7532a0add78cf293b0b4c31a651f048b2ec5113b90cda4d896005d0b573063497a7141d922c8b195c3b2c7764fd65a066912de629bca532b3a6b7666b05cb33f82dcc06f8ed6217b58d43d49dba08524e4d871315ac8cd19e99f21179a3c9374ad1bf7a82a19dbcaa95e8ec830265baae6e7e2ecfff539b06b25b1941d79c6f4a71a9c053aad471cd52c816d85b5b078c762476ee20ac54c60b63128b91b519442e7d6af2cbabc8c88e5a6b9fb831f83eb46e23cbab824c0ee97a6233dc80ec25774e08d2973afbb4559ce7bb5bf019a84426f3f6bc2770926937764093cce7c7c08d9e60983103849aea5df564a4d80a61ccc7499f3d2acc2c1259679047e3f8f4313835cfb735003c0da33adec1180aaa675d56e91e05f6ac71eea2057aa87cf5a808278ff121ee708aae6e10af2483c0688f62566cb1fca2928fe65968e719a969e052a9fa5f97d9a9dc2171e26f6726d8c9677c9157fcc5acdce1b176e3ed7dc31f0871fd4fc963c2b0dcb99f88e556fa2691348368ffe79387cbc754c1e8c20df5b44dc3687c890a81a90003a4757050651ba9240ee0890192b8477cf346194e657d7e96148ab30da76923266cc397f3cca22052fcb712f3c05c5faa8904e1d527cf568c7af6bf250964f0234221a9ee48f4eb742181c27121605033c9d1ec1421cd169613c6c2ebcfb384b750360fe7b6ff16ccb213225de9dac0a338c241a3360ae4eebfda45ce366f7f311c8b8417ddc57968b7c1ab25e54c3919756428d30816eadced4b55ca2b692e47193cb4b2707aa4999d95b14258fc8b8f13752ca10e5afa33c0caf02b7509ec5ea413ce34bf3c3c1a457c287421b43913538a57185c289c10c0e0a1e88a18083d9f3ef8682b6a1a6269ed2573a4b60c6e81f34a2175b37013d485c9fc559125a48c1461a5818dfdb15917ecd4cd285902f0171cee89399ea74aba28a8f60bef19b722793d1554d2750d3b755183be24d86f94645a3ca39e1aa08272a2f87e61bf4ff4cb631fa39968c66622d00abe11b39f07e5a4a26d9d0b02f779af9175e1bdce5e4ca3caee6897560bce9eed24b438a4b1e48166f783342dccb793551e5c72f248822663dddeec751bf6ec37018ac7630de31a193c7680250e28d912b87698e2b539c40f66ada6e4e76b205a21676bf297167324f3f9f847e557d5add7872b586c992ca50510787074476756a3499184450ad3f25c5ae2c18864720edf09446b70ec48e50ea9e51371420c80250100b806945bace3a25418542ff764f60c50acdd9b92eb3b23eba14b04138750da3b21cec9ec856593fc87f76d9dfb26ea8c43dc439cd613c607f1986aaeafcdaf2ebbac8a5337ac27d85bce0c713507034472cbb3715bf4afb09720fcc5edc96a491b6d6aa67dbb8e3bc7f9b8d75840a0c88b4aeeb6c8c1d946cdddfa6cbb53f32851d8c3a6841a590d4566de678ccc5510358c6c6289d6aa621dedb20e39c592356ce9d0ae0e00b6238885984f56002f6180e036cf67e399e68472dcb0370b61d9e1d119a2c12c9fe28e5398d55c923bbd6b90bda16efddd5f74b383fe5a41de84940a5ea33aab603f4ada77d211a6caee505afe59a39640f4981ad97e4fdc209a3c753594c14881c7c099c62ee3ebf0c27d716703ba6937b3c45afd9d95c42b0fa9faa27a7106a86c24d339cb9f23fc399bec7ca5e8002101db5b879e4f4997dcd3ab1a930a9f91411f08028e991e21a310a9405a972e8385d6c5744c891e6b30b80fbf0da81d7931d1900dfc3bbb3c59e714b8f615675c60718cbe2635f94aa601021035f67facdb2edabb6dec289e949d75a3ac77f6a6486e68f886c0cafd0a8d3b218f5ff985af4865b1ceaabb2ad7a93666d8c9030112ac80a82160b31d039849defbe00e00a0151a9986eae59ea2690ca95e8dcaceb5035ff8ee8cffbcee826a51d1ae169c73669a8b2443bb1e8bf9f8536ad98fde0ecd6c6a8bec989add078cd87bc42c832887a4b5cc47420ddcb27854ee318e63ec1f94496d2f4e371d17ed4e7301b9e7b8a86121d7b5a07e1ccdaa524e8a29075a66dcea1f329c7e49ed1e490bf4f4c229555d6dd3834499d1f6c242186f2b50b5bea640b5a646db33195d96e561aca512dff7f6e337d0dba016abe47197371a4492feb9dac0a1768c2433b8f379ee234144c9f7372f7a66f01623f6cf3131d30b2caea803cfe738aada0dae9012c362aa38a7bf160d0263abad0c01e59442259da29f9703a3619b8f16bb20f32ec2f001184c40abc6bfa311d758ce78bef664061c1cf2e9ef61010978725cef9aad4efe0c04ee9162b462b53ccb1d615897a1dc94e0fb6d2665c25ba4d9703094d0a943b879f984991d6540fd7912a95239a260fb44f0ee87c174a0b990c3f7057e7326ffe8d198400a9517676814bd82683da582f17716e1a69bc03aa9823103685935965680df9ab5e34dcdd143f628dbc77c434f33489dd086b62d965a0e0466eb72c42350c40406341f23284619cd4a2ed2ffc35f0b45a5c1b873f105096c6380f548eb20f31fec5d4483ab8924ca74ec12d505b11b0fffa05fa45c68e2326e796ecb171a7d128634e423bb2cc30c7062fb1ae19fe80caccff7d3efc9429bfc1ff529fc3e109bd4a9ff2521ef0b6c2187370a8fa4c1467bd1945d3b6ba99361f4542313952d0215b956d3d3e2a5049e74b5658cbb6d803f844043ead9b64d88f781580986a39442d04677725050d2a2d0815ed2d02e218128402f131d955bdc4540153cc323c422418c5c5a3da1bbab90510ea13dd31c31c20bdef1d8ce025b8a3983338d383d80d21b8ac936279948319bcb2e40a408dc87650a33028af4d48c19c879352224a2357d03287c5014520334428d6a710161165eaa0bda62a7a0d5dc4e2cb85849f43d5da8b0bdce38008b7643833da42483217da900ef5f725c4ec36974b8b17248429d07f839aca8f07e13f4457117aec65f6f98f4f86c02110d21a0cd011c4f5e6d0ef2789ac179837c0a9bc0a6b3040cdde9c96041ce6621005ca1d044295e920eebea95f763a125b0a8b739cc8c486f8aa5f37d08276afb70b6f4f8e9c1e8e6aff0eb7462583cb73dc7e12b81384eb64ad1167e530679d80c5ae4cc3c000ddb72b06e8f8bf7d444351f424a1e2c8e55bb7581f1531ee8490e00ecc2fc8c21d73aec5cc2d8dc36d7541d77afb1331a011bb067e05c2dc6f4c5911b5096d78e89824d5e900ded089d7bab98836d17ae9868c97ec26bd1a1e0168446f0707ec3a971f295a4785b9813380b26031578b0c4526230a528cc2a00ab88c12ae2308bb86157d804f0f2343ddc568d86db619fe995c98f7e2b56d566d06471db314b322ffc2c1250b674275029bdf3d6a0559bc44d9f7b520bf4265e60f926ff5ee443465e9bfb7c9adb85280878058866f0db2211601421c7dc618f8ac850c03186b3053132ffcbf4b3a4c56af55a749841ad2781ebe923b10047303ebb6f7f6624a5bc7b2decec5a694a2d506b363e64be7f5f37a12175031128d5af3c50d19b4fc8a5419b396f7652757837c40732f49a0cdd4240beecf35988c930b81e2fa6579a9fbb4de7ebeec5975927a9599fe1e4272f0d830adab971fa0dcec6acd6609ac62be4f95b273af52e8b0e527b7e7e43940c84ed62be4326b1586eee2cedb9ad0b76c8377e8191500931d32b03825c77eda9af8b4e00fcb686095a609ce6822573b8571aea2fc8fa5e8757687acc399cd17dbdeeb77eaf3b579af1fce55a40b1ac680278d0a3f4bdd2ca21ad57482d971d7f13a07a091a937f5e64eacee9e17767dd49c9eb71d1784ee0abd4d88fd6409e5cbe14af4fb7a74de0f7029021e15f569464bf7fab9880741a87cb70be5971a46c6f41ed13032f8962736a76ce6035e6dcf5545a42c39dadcf7024f090faabc44b1895b3f4087986798ef23ee0eb4de089d2cf7dffd5247474130b597645c2363eb6a102177bcad65d6222f81084c8b9745f33122f388717694d0aa6c976d1eb296b72837b64315b192f0e2b503ec738302e6e945a935d67eb5cd82b9dacdc736410a15c058901566b4366e2b1cc5e6df51da5b210c3028259375127716163ccc2b6baeca0643216932fd473c1371d1518ec34beb0b374a7f82d7e00339e2abe7000f7aa651d8d9e3304b87c82bce2d64730b242ae0efb816a61a7cf7de0fd38d51e2714de543f034161ebb353e9c1b2d64d3378688ceb49c9475be861ce3c0413a5f77a88b62bf6634c9aaf0fb6ab3a850d3050741ac07cf369d05660dbba4fa30b4dda321d0897bd1df0a252e0cc6b71a1c5e68e880d411e0ccbd5c9dab6873618471d0caf7dc94811a41830f404f1a0cd70221de8350881402e769087a7cc262ae82144a021cb11dfc4cf97e4b8d04107ee50934165cc871aa0533115ef301e289ae5829e30b658bacdf8444fe3ce905cb7b11a895f2062145177b059a15d344ec7e226f7bf442d3fb8e00df091477d8193023cb618ed1b926d3a2a3b5ec361528cd4b890a1ce85c8793ce460fb6227574587a2bd764fcd0be7ba0670a8428b5b2c6a7c54402393cdc97a54a7a41f7445f76e4b6303783dc5e6e4a96eef88898a8e3a6b8187033c9b56dd764444c8bda58a6c05ebe6655de634d540ea1e6f3562b2be8037c67c110c2fae1254989681a8056f1dea894d3e5d67205801864ae84f93adf79cbd663c036f3653bb269571a192fb9edbaf0209b63b98b7a63a00749de02204baf65ff84d59eb846b89d9e5ac4ddec7fdffe7505b3191cdd5b78169c416de7f4fa8ad836e27a6515bbf25376bfc2ba5ba1f92676d2420df0936b80401e98b2ee3bc202fa29f1876427b49d6e8c93c8bc68431ad6262712a6a3c7108e4f754711859103a8100071670090f69f3a3d05a216964db1f772dcba9f879f618a19e1e01526b7cf3868a8f91eb666b2bc4f729bebd3b1511b734f5858698653e05c80ecd9e11f23b2ff434d8b5c7c7315a26bf4a8c112a3aa2ad57178493143da74cd95af2f1150661ca330ed0e8166390760ccb415df9290f5623f74f76d90a84da1623527e723813f9c3195174436d4b7ccacf8bab74128d328a877af480a1c171a67971f5858097f983da71acacba030c6f8396a6d8a52b63df139f26e014d109563b9362077251db49ee549185fb43cd90a48f60e5cfa166bc08c07f07073fb375e492a578b8aabec79a282a38b1a791bc7a26a1c6fd04d0d525294250eeabc112f98d7c0579c832fc8a6a9a0397d239bc45e68a29cf80a3face8f6b4baed22507ed6aad68322ebe3c163ee2ae32ace84909b6c8c94319e92af574bc3be9d84371c543bd5e306429c0a1cef03d91daba8007204ab7032339672d10c8fd412609ea86f419cf1386b752933bcd1ba0745f497bc91922ca56fc6bce88b4af41ea54f40ba1983c4bddc8c6457f2a16e1c43bea86f36c5f9a371bc585d786dee488a5f986fba236ce066dd6980a3a691574f43b51d00476028a87724aed051c710f3a5bb7c8d8fe8dff51c7e225a924f324a12b18d4aae5d5c9974cc47e6fadc184b439a0cf754ad2d01427c04b4d66ebcda636b6d2602c16d5ae266839621efe0ac0d0e377d109b4f75f88e5428ec46d58739d04dc1cec1b82dcf8f18df90473ae93fa67563e7ce9f71d100ae7a8168fd44a4cb9212493e9af9354772cf828a175fdd143fc358d3fe6a6af0b8eb42f91c06654cfa21ac2b896ed1fb03a5efcd1d9115722ebf4590df3497ae85fe066a01e202a91ddce018c639ddc77798b2e7e0cab6303060c705b8e824033af98e70064929a2b39a7d554caea3ab8d9990dc345810f24c1c2a9353e8de10f3f5465f60c7e98e8b77cb88f8eb32f8c782c757e4d0eab89d09f9971a72d383954327c83bb3e09b6db670c31e3c7bc063368ed9e75dd957f3c8d0dd12fdc30c1193ecebe95758af43ab1a17e149dc48fd669b001f4d63a95754878ebb54ea867a1c26f607249155720e50b68d8afb432b020dcde4bc00a39f441feabeed21552d31d80977431a89977f62eaf07d68052cef71adb0153d0ee4e3c7b066b14f51d7f9ebb932e6791be36307bd99a6aee847e8a678b8eee6443b003437594f0403cdf995ccc6f07561b957a7f9d5fd9e5a17a405edd69cf43b4d8c067a68d94c9ef4811c8ee0c42a49c3c42758509fb37c278da8a30376224ac3b19e028eac9401615977a101630a57af3c3ab3b39152b3e1f4266806ee85454a0b5d93e8efd3a893937281d47d257e5b9f41bfbcad5ee72645ac48b8f3c45afdd38771c3a075a2f7f820f774cc322ef3bffb6f6b8132c93a4701bc0514822e644cfac0880121d4be8cf9def88400c7fa9a2eeb149e0c43a14496a417af03749aa04c68ac2fc621a5f51249b0b4aa2a4da164b4aaaf013b74a85354aebe1f1c41b38cc55b6733bd0f5d6a4f1068e8aed21b47a8ec14c8de92ff07170c89efcef0dae479ccc58534f85050d542b671237327722a615aeb021f74ca879444f5c1115d144a59ae5940b2720fa78adcb7c03cc36800b632b8c34568d20bb267e80ac2644e2e895cabe309bf38d14d885aefb9a642398bec6e14310f76d5842375405853509ddb9e141ea76be3f41e9935a397746714941b42a5594cf340251ec18d6d450f7634114578ab64b81eec2755d915c57976e85eb0f9b5d81b66a7b13672ea4dc0a7ffab7c2273fb3181579f19c8fc97b86e00a2771e2388962f6faf489693ca308707571c599395653851c8bb149b53eaec1d3816f85a3bef2eaea1ad3e64a29d19d4da4eff7ebe4959c08390d72070abf39f64424baf3c18c2cab857ddc97d5976f2723d6cc47e76c1a4851b95b08b39fa761ca8eaa500477da423d83d32ec0f1139f8f02503e91e2c28cf5a402aeb24dab7f81d9bb14e6d4fbaf2932b1708c5a74a9cca096e6ea8382ae148006ca6dca2e1bcd1a4392402263b9ec368deac5a800e3ce4188560ce8c319316c5c620b8af92a48d309e76b37c586da4d512397cbcad6efcb4f60784d3b892e04ae6b0df341322b08240546a5666fb945bfe27e715a1262161503fa58e2c90d1065595402178095aa8f633352dfab604827d962792e4ab93f71dfc58dc138790d1840d7a237b7c69aabe9477043c6d6b78e9c00b6e96eaf248e75de27a0b740925f5c751fc4ca157a07522f02a1610bdd1abacb0969f5dfe6b852403e88b73752bd5255ef65bd560f3d523d9f6f3dd311c47a71a1e249677b1f60b0b390af6609c5c3e38b87e812b5050528c568b391c8ac0b35cef574a1eb2c3f516958344da74e3273b2c5565ce42cf3cfd4a2fa21ff8b6fd3053b93ab8103c37e10959fc5ecb9a226cedfcfffeff2a672f011085093a6584406f137d2a56a7afb82f58ebb9a80326009ce4fc7cd290c1dce9c65d1472482561141e3a6edb79729852f86fba50fb098016852d662c832941ffb426bcf01c95714726d94f828d216c5dfca135e085e80f6db71f30f693e4bf12d2d478c76bfdc91c9495c10aadd0ea7b97349030da502e8ee0d32696c2078536c2e5a5adecfafeece7249dac9d5e93c6822f9b98d99ed223e157693d810350e531e3587298cd20194a2ce11e5a36afe9c88b787abba1e30e7d9904ac6f083ef91ff881d5129c2aa0f1717ccd6f17b7f1b0e56c10cb28bdc7514ee9cd268cfc9ff2293ad799f73196e2fa7d315a5f065a005df07dc503c9369a7ab25a13d8cce431ffdf633751ee96cde57ba2ddc7d6955f01311d46e39c971c1d2d349485ff90dbf691094506eab0b7e7251d0063994a8726f14dd013d6856b8cc5a3df1f27dfeef56e04eb31a9ff0021a0f19ed808cffac7c46638cb2e07238fc329b83a0835ddda3d76421d8911ff055082a52669dadf00ff4c140e8f1cc31f5dd567a61f4c6bfbcd6248387e3bcdc8eb8f63238be3bed2673bc2605ef9074bc9f4e608cb7a30c666bd70a32e5b595e6176dd5a1d5e42bb2a58169b5bd537d600f923e3406928f7074081a5870a45285a3fc678536db9f6809580fea48f71e7552fce6f3a1299efbe91e5d3347670c1f36a88c50d5a7aa62a918cbf3b5c8103e3d0e737f83cc8d85ce7c1a36ae7713bed62dbfe852c20d74a620ae09248f189ad5ea6487e15d842992babe5c8196a88448cd7250ef2450ba1341b566580e44a50315a75427de60578adcce6a73eb15d8da1bd0122bd9f39fe4cd4f0ab2efac94a38118c12ec758d6f3e4ed998c63b32cabc346185a64925b44d63b446632b977c19d6a8ab561fbc122cd5ce309d6f4aeabff132d69474312ff4f502de0f034e5034e3e156553fa9531399cc74f6c1c2467fc20c1c4a7a9f8725a9681d3f4dfc851b0194b45d5561542c168091ea0643167fdb88a0f391bbd9654ff27171750fb914caa7fbd020ba8be7df640a691f8470884cf41939012d43b8f2371c7129fdd65de40fd554164f646b564cdac9318ab8603fa6ea73c45377fe2555e1fdb0502410d87ce511113194c159dd9329a82424d4a41ac3fc01efb5d2d8013c4ae706251e60f4bdf57c4035cd61b8f3c0509c4250ea7f47007beb8efa2e8a8a2b57e83fcb59a880042b538df4d42c58005d263a14c80ea5e1a1e0f2181ec10710f3853f9d7c998731ede05188bdc9d23979c86905204230078547930fa01d2511ecffd16e8098c33a4297247dc0bc21d18b32ad6fef2ea042a98dd33eb6cdc9950901eb08f048a9734cbbbdee6eb8db22514de24195f5ade531cb69b5a0e8d003386910d06e07bb280353a392f185345b32b359fafcadfc5847a9a9ca4827137812f44f7cdc41cc7743578c22917dbfa11217df0a8986fcd44d9a1962630dcee10e2fa1012fd0e80715869e91cb35be814be2a8452f60e9908334b11cf096e9ba3373d9944f9063091f64b39a72a418998ab4318d823c9390bfa2f6f7c89eedd710885c2537eb7a76ee37a76435c9ba21447aa5aa56cbd030104f49f952e11242ad8a1fdbc4093dc0e418d26bc520433c6fd33da3888c9479763930efe028707ad342e66db5c4e06cccf4db8d232a65edd6cd66a658b024de4a90f5114e72a5868462c6169b2339854e7018c5b746d660cc03eb8836edda2d2e033c9d3bcfd0e63697fbbd6f1d10d66dab557abf4bae92a4994ddc25569aead3b850bdbf3cf0f40f1330fb95d9c6fe9a5122748583137a0ad96e2bd07f1e5d063f6722f1f8dce184e49e96f0b636aaa1141d0245c3c46bcb008082e2e596085f9633253ecbcc58219ba5c48062ae9a17bae4ca7fefd71efa07e0d6d97ebbc55701a10afee21739bf45f2d8cbe8dc8cf4ad0bf253442ae7b6bacd2086fcdebb5bbf8c0aeb46f5d4a6dbdc06d6dd17f47b9e0bcc2e148d095815482c74b3f45802d7310f14a46412c4296b993fff54c8f1ec7437b25860415ce654deff8383709e253daf61481eb16f984ad04bbc35784dde19521e686db8efcb17c2c31604f014aba30ff1547c0a28cb1d9a9cbfd17b36ad2ca071bc4aeb6afd831708bb0223159441759354c42224bdbf4102eb7991ed3bc1c0102c00a1a0ace92240557cf82f320418b65cfbe47307fe338bd8a2dbafb149ab93ba95732f577565bacf15ccbb17a482cbadf9b5b8c9edcde71c232f531f00d6e14b13fa6739cbe52433969c790d65e48950f783240a61b7a6bdfe25a89093e889f99d00d86eeff554fa046a220a2642033841d573bf4e4a8542fa8c2f8cb9d4845a0fc1767beafd19f7eef684996d82a683a37dc3f9dc4768e1f606cb34e7e8ce46f8c100d8e8c0c11900064de651e3290f054326d4af5c1fd04fc2c1ae2330b135c91a2abaad95ad8671a38447880220a4892608195970b060897d8a00321ea2df894f20da0ab587623269239b43db32104569836af0024b5068453d18b866fb33fa862263331ce472d190022d4cd55af0b957d4e84d1d74c503541fb4e680dc3666d7eadb7c5ce3405c9c97716b15a509f4766ff1a2681955c0f95076e8915c138050ddaecccf3ae39e70ec9349580470369ea2abb0834861f67edcfadb68d40e05645c9815eb483c349094cbb2dec663ada820f0a204a504b036de6dd66a8fe325084d87759d185c2fc355a4c6f7d4e148093a11668edb01b34733faac3be3f8938928076e9daa2e996039b0679e989018afa0a73f14c9635e4c57d6de821e73f5d7d9f39b1077badec7f2e13e5eccc8fbf8d933467aabe87aa0515e1b049a7935a10b9dd44ba82ac7f4bf8b02fea7f8eeb0f3ae928c3a98bd2d17b3a9acb7f129894d643e177a6b9b0811b90904b02e118fedc775a49bd433639705ded929ef3c6f062943d3dcbbd8085df5ca977ff76e3a85d770712b9ceb151d973c7f64a66f597a396f778a85e92b39152b9fd4388dda10c9837649c59243b90b367569e16c669027983cc8663ae3cd1f4aec3ef96a1b0071d740ac49b5080656e7f7025fc29ffc04c989d722969dcf17571173c7f2bf5f61b763c1e422277bcae3a7752f6a202297c9550f0c1a610aefa1ecb79b687fd13c4fa1139e7133ef2277c45a71b07e1a9870de21fc41e0c8bcad4fbc3037c0450b81bbc46d5164f1a945ccaeefe8a3f2aa7baa6984628a5e92839f471db753e5bcb2d9db33fa08a4bd08768c8af2073c1baf44356f8a5888dd5bf37b9ed60c1a433acf3d05f353fd19b1ccb5ba6e2c4357664ad298b426e257dc0891bb9233dc410f7808b607716a1d67b44c3d985dc72f881409d8bd40f8bb2c2a8dc431f7a14b42bb78bf507474c24a8af079012217f3b10877431b2d6650504695d8f379185d415e7441d1e8ef8f3a988040aaa7dfc918e893e7b8e7e26a0c375257a1f33c637b790ffc97b42cfc97bff8b150660ea03963cf96bc52959618882070f62b3ce78ea2102fc666905c330327a74220587faf070e30ca6db2000676be276a591772cfcff04ed0303d93c11b902aed254a4ccfd4aa9de2f804b53707b458593c7f0fae2951c1210f7a1406fcb2b575cc7df9168c725ee51e225092df283808c94916aaf8608fc567f7629c073bf952da13a55e5b3ea3fa4bbda4db250e570c5dca1fd565df52f8845a3d9c09ee9af92831046c06efe4cbea47848c20b662b4837ba14eb4c622bce19ecc4b468762972bd0dc11db017a2d0c5b0916d0e67845dc845573832bd770c20585ffdc8c0e232ea21305ff5d085a8a3e075e326b078c86905dd6ba7c7711ece99de3cd31159c40108f88cc1f954433871db39cbef0c4438ac198251a6628c07b9847a97130c8522fb33f685bf3cab31951206c7a7fafc34c3b9b8a4b9922aa012138d1d2ba67b1572b8a45ed0bf523211b2e6a16723ac6785f66a5cd1891278560d2df4ac379ebaa50c54cf088f38f7ec8438be47049280a27d70d627e8b1652a6c4baa9b98bc17c6253758e0c270e617f30862075d0de2f80e7a5671a2911268c47a6ecf0b45e852029392e9486d7b5c3b67ee59d46f32769ca6d3573b15082d0c8b00a654d3f17b00ede2f09c5c2cf8954b5a33810d178f06a5297a80d4b9b1c08ea2474c049fc54a65330231c38490a64a45a272305181faed90bc34c996969b26284d7d988757c7c84df9818fc9b7dbf7899f7b17700a666b872cdc5742242998042fbfc41192720a712046226dd9c9d1f8faeff6888a318a739476be103083bcdcaa834209275c91cfab8e2e9be923decb9f855f6e4b7c737357b3e0708495d5878232baf49ff0b152b6221a0c692c0ba4cc83d0134a1eeab13d45ef1f4b47a844bad34358270eada079c8cac86f5907209a813a563976a966624c1793599607d59a5df1c2e5564733b8fa9cbb6d85ed64d40cb5be96267a13b3cf89c07937a36d5581ed383153fd4279152bceafdfb622bf9870eb2293ae1cf2b78c0f6370925588bcb9eceedf723f4def20338440507f6021d6660e76929b92a3a41ab35d77fb1d71e7c02a4ad0742cdba6e97221a549f45e2ae0137095b6aac3e7c682965ab43823eab46be25b966c80f7eac5f19f1d798aab552915d0ee3279811349c4291c20d812b2a9ade4de0cda81be4cae3a14a06e1ae42e026599c890c69533f25fd1b9ea3ace255527a8520e530833c1b3c3626f546a12960e725580f6c218337b05682aa3e5c10a91eb1826559cae0a2cf034833ba65fe15902574d605284086d33b0b228ad667718eb9f18f7ae419d756034cb271fdf532ba7864974c5c67e895d9cc20efe65244050e75dc8ca56f2ca6c23a4ea5d297e1e5240d25bc8a78c0b0a5fbea97f6a52c157dc2dfcb3475bc895f7176f09724813cfa9025b96ccc26b8708b848df956862f82d5c1201d005afe14a86fc7ec2d433183abe78c0661d6f09ce610315e6185e3e93fe325d2a3456a4e033c227304ba53875c5aba4e80c33749d7c9084fd57b8722a7962e8e1398027621bf78da48b9aeed2fa909351514a69ba27e54058a772a13d11564a75c155a7dafbe59c5c744e018842ecc14c915f2ccca37bfcc44d0c860e222af4d02baf25920b9d5f68fb7b8885b1308906499b74303173ecb06873ef85d3ea9927c83ab2bd2b74390833d3d962d5eef029c173856786bb86695c6fe76486a6e043ab152f1063d5d1d2044f82c799338b3a2317f0d6340d33a93eb4758968344968888098d3100c874e3a256d79f1c32697ba8c8e891538b3b93f27195db08e2cba1daa723a15f40e300632862d9addaa990432f9ae19ddf4a098c87bc8e6f88cf28202ede0a6aa48f509059ad300100de2061afda4409936302961d889c5116e61790a9a5270c52d34e36928d6a0e557e5d9b7444e89ef0d5a11616a3d14351383edc749120eedd4bd3d56dd1ee17a119cdaac4470d2a858106ac7b31b791fb3c6a336826c41ec9d31269631b44253add6238608eddec4f289f462ec37cb7e4efc08d87498f7a9f547d9d24d49d3a8b463458230ee7a33d372bd8443db5d00d91120b276a44d482b92ece453e45385cdbe32ffd9959c1f58209cae39140edacf4efe9078d06611f285d4569241a6ba21f07c27cc29b6824a61287685902d81cd477b944f0e49f30cca08262fa74f1baf4d49375cdd0b8b3ada042c43f7bd0fd5ec1146d73e5b9cd78ff07a639c38bbff8a5c0ff37ce9ea1c4616a52f3008d2f50e6cfcc2e7309359a475d5fa88eb7ade20bb7467bbfe7344b494be58ec95396f1b5fd752e0a69d1657a83bd5a83f9dfe3b588dd8d4e6d9f8d237c9f0689eb3c5e5d0d48865e46c2a18e118218ab03ea6c2d313c9c75c67e8c49e8af8b4f25afb12d7bd2b3a1fa646623cee44372cdcbd0ace5f061db6b8099abb3c055afcd903720b362bbb0c96bced173ad7e13305315b44ee0b53d61c9512067c725cc58ba7a6ae3cf1b0b5969963f0859d5d5d3b6fd0a7f97e9a2c33da6b7cdf3da32fe6f8fcb6b221339c97c07cf4e421cede48a0224634175dc3a11ce449cbab1217f6b7ad690d4984fae28ce1130f167e2ebdf9f69c0416195b224fc810ab94a29fac3399fe0067c47c7329b1316ab92258e71c9c078ebdc8b41ca02518d79793d0394d85a5a6cc231312466c1a15aa17a0a4c1f7de0b2708e752d6401a555f55ea4c1298147d5c753f7fe56d3fd1a86476d182fbaa51aad114ce6c7f4b0e2df8d8484e9ad042cda153960bcf910c1ee50f74f3c0d5dc0b20bbf382ef50e1c9c2d3e48acbbbe6d5808d108f92f31e79b66d0d08248c6952810b4ad1007d51270a919270a801d3f73ce6f0786190b6255a5f34d0110948bdbc6fe5cc6ba72fe1343d26de315c9604459af5d0e7a51f00b7c151a8a6eff22397ba569229e6894bc9fc5a1ae644c7ad4ddafb77a3cf8bbb63de667e96b7a7d270c043216b76c28a560e949c7059b2b41964def3274581e1513dae82258eb37374373bcc5414e473704c54a7273775f005195f4d63436c1b70d189bdf622105eccc5418ff3222ce4dc70fb5ede7db4d2eec4a2cc743de6a7b6f090e8368c21803ee5bf749f8531380e3e6877c6a938861ac6eb4446f5894c4e0013d25276c26850a129f043499af14bdf1e48d278f120e8d061c1233055e6c883988ac12fa3615b9e056ffcf23ce19898ac71c76455149f383f561ac12efff253140531bf11a5835486f303278fc19344e1f8e1ab6d249faa04376d515ba5f770510fefa504674771695065e7ce96f39835c9b3001528fd9ce42fb355f2d9aef0900b387cae5ef2435e286009700d5c2e660fd962c8e11b3946b27bf1e9f4eff08e85369663b4a1fa2ee1fe92d2c07145dbb8109cb02d698ec910a7f6e928ab96b1bd2a8c58ee1e3dac7d47ed59774dc67098770e6f17f463c23711b88a1f7bf5876c6e8c471f7ec3321de5e68d79e5a188704d76ca92d0e18f2d8acea666a2c7ce94253a14a2c95ce8e96a34a30e95d3c8b750145abf438cc6a14a5188341615068e82701a13211135c7e434c67c83625c97e1a56ba0335c4330c8120b665a81dc05240a386a74db2d391ca01380318ab70f86b131519335ab160fd8f22d6386164fc833c60f1c8f0d1ad734035dea0fbf9abb26255566a614acc0bf2319f9f3ea32ebf86a40682a6ede8d98f8c01a61e47eb5a1504fba73223e6f84ee67b1dadf516dd25e6060b310a0339168f09af875b6556516418869650fb49838f0a72e0c9c6e454db16cbd865ce7a29a6a14a0c48a55090d9cf8365e964f5183afb71a9dfa20420ee7fed997063fc49a250dcf920a3a098db1a327725a78cea57e678bf62c560c9f2aa667f1b7f349e975e3843ee4041a7df2499276a30628af90ad5e1270bf9d96169d1e605301f4d30d93f52a50bc5248706f3833bfb81baf9ef0140acddce9a874546c495202c90630c86a1cb02bb89fdd29926ddd2878d282d4700d022fc2f9f2dd610db1f2917f24f2c80fc1031bc9ded7182ce5e38fa0445c8b828bc907c9088dd53691f686634e50a00f00bb9b9e459cbd1028c536f8bdd9148534c6c6d1d372b9df60ab7a969b0ae5b82ad967a4d22d29a568b4ed407c6a4e3add36dd2da774bccbe43e20c452612f898e813bca91f51b22730fc581a3c4ad99f829fce2c8ba7691f5926f6a14733d10d9bc809e9a0c14324e9c4693b85de9aa726fc50108b3581731d013881b5499068ee906a1183b1020b6a60323e301742240394f24f4c076109e6c225147a28fdfb63d4d76c71f7d22db9a3c5ddcb3d99e87844b73e2a2cdad6199fd16c67edb52a670169116711aea70e3f5dc1eb7dd70df993db9ed3a8ade7df48ca64596e7bed3616ae42b1f6e95dd68363b77ebc99d83bb25770e1465eb01e46e38035cdd9aaac2540e55d322da94ffc7f25e15d47d70a76cbbc26ec85531cdb6796a724124ba571e14a0a2c00f054528d060b662d52a4eab216fb6ca6f3e2a0e59bd3b2b0258c1bdc9ea1f6ff77455a2556a7e958d9e22d7b45df9b0cd335b55beb9598e6606363606238017c4b7deb21acea84a4dd7aa8ae6aa8d7c5515a6aabb6fbc5497525d4965232e95867f93f584862acebf89c75754475426fee5da53ac4c55f3f97f7eaac913f9a9b5531cdedcc63605fbea787b3595e7d7b4ced4d1d49a29afff2f45a8540b66aa9c6df5fa66969a2bd5c024f588549c374935e1445274a4f2ff04accc0914fd3faa0651954f206602b809be463d4237918f6af2ffe628ba515b28ead29ba928336f8a6a459d08e5e9df044baac723d7624475f7dd75a83f7166a19a84b2f11faac1b6d3a03869bc20aa72504d4c50638faa5c0258ffa6047c246822019d045fffff09d67f4af4a909aa3f7dfdbf2756ff789b0fd05313d6538c08489911a8f9efba18411e30021b66274fbdf361272a9d74fc739ac5290e8ebc8b6dcae9680e819cd6fe337013abff38550fd34d7cfe3751f93757645ddd9a0e37d9784db0d6d46854b5a6479ab680b5ac3eaefc0ba809eebfd52830132bde02333d32d17567de536026336fa29bdd2c47eb68dacad33a3434d570c6ddb2c5adc8ba7db6fb56b59c1abad58dea3654e51c0edd281bbae1d0cda2972ef5f4529eff37f97a89eeff5fa857249af2bb4b63739658fdbfc9f24c96596e49cdbfe965892eaddb96c686793bf734674b0dccbc8b739504fd7f9b2b9dffbb1da66ccb2be59eae404a829ead5b53232531bf724a474a3526917ab07627bf36cbd1e626f1f99f545cb7ce6bca8f394f8a51b7a67c9d9494c4e75f5293779364a338d49b9514294e3adc2d31d2954831de1ca6755cd5b9ef48dfee646ef90ae9d19bc916d20290ceadeadee6edf56bee51dba39f23508ef8fc9b68aa8feb0accbf3d4c5947261a496ad4a4d157a35e52b5e35ccdbcddc2dca258395e4eea9eae6513e5914de36e5334080a80002ec90816a3358cee189158e468511cae0e397f3fa2bae5ce2067ef99b7fd776af6a8723617dbb3453e16e1075c3dd0e6011f0ff47227f29c6cd38c6e231f1e91765d1b73924daa2a2fe229621579487428119b4846a20b8818fd9bc654abb9efb0deb7aaeabb146964e7a4aac78279da9822da8188c3a15066de45bc43560ec138e4c0102243584c30bcdcf262ee28386f7f67521f3b32c3191a61a8f79f54db75afdac8ee338b990ee071608d030238d0fb67f9902db33c6579deaa962bc4a690903737aa8d762c5f0b3120e488909a3783787b3db398d939de5e09ed2054f66716431b4469d097ff9d55cee1066d2024680141a77a241279cbef52deaaecbee31c4f4e50d9c0010d0869a046031fe89209b65996af38a6ab3682115375e7bc857af8d5d3acd9f2ef4cb6695dda751ba84ca01b811800d203e8f86fee3add9148f4cc62783c178769d61a0e50095b7f2e9968baa67639b9ea2367d3fe40f913e6c7023f86fc6cf123a38facffcf2bd7a9caea9df1f89ac5cccdf2a15dd7ed99d5b45d916752ddad31ed29ba6a3613dd3ae7e936b3a3a179bacd64f7319b288743a27bb37cc872e266f35633cb73ddba893cab9cc3b56bd747bc76d769aedab11ccf98b61ccdc5a1de79c5cb762cc70343d7dd57e417da998969cec8365dd31cb655dd79db85c8bd6a55ddc39e1a91ad6e7b72a8ebb68aa68d765db75fb9b71cd5762a879be36e6c7acc53e36e68aaea1eaa9ab672edba8dc0ced3f81cbaf200b6dc6d1bd3b9ba95b33e5ae41baa723994dd776f7dab5a8ebbad5a3ddab1dc477b44b22c1a9af21e796a4995d56abbd53a389f6bf95d0ac7ee15efade56e2b4febae1d2f0e4a3e0e5db571a7ad6fc1e11eaefa0ed51b45b2fcc8d963d04e450acd36cbd182472352184c6a75c70555bdd11455790f22a0094dd115aaaab51adcaaeee8ba5b2457d5dd1110f22aefbb381764f5d47a13ec2baf69712ea8eab1f3ffbaaf704379b06f960ff59c794d51bd75e496a833cbd1b29d8a3ccb449e87297b0ca2ebe6c4956dab5ace0c4de3569ec67138b316c993fab85b7d97b2c7bea6720ea7dbbeb2ed9dd4682de771783aee170cbabdb1c9c5a9f508aae100c6bdcdc905833f20caf6ff15deeb80b4cf3f215785d9ed4d4e8077e1954750674d761fd55d0c06c5f09bdb9be836924d3b96e365dee62299b7aa4ed9dd1ff9047c171f2d37ffb8ed1febf8c739feb100ff98ed1faffd631c0d3eb859e471f3221ecbf1087a138f1a3c50fef1bce1d1f066b14d0a81772c3d104551208aa26d160987ee3be79d18ac1d582ae76d98aada79d4a4a3d8ea9a02ed8cb13ca37bce4e83645e26aabb6714d5b98e02755b4def52f6987b9bea61df1aadd8f59aaa6ab43c7e663657467c3b22ed38d761f6fbcdd13944e74867ee59649bdc731ccd49d2bfda751b93eb11b5f79213480e52ce8a38a9e2c88973c6710027d1a9eacdf2b5ebb3f3cc44ae8da93e8f69b1ab9b4d87bc779db3307774b3c81649cc5b4c1522e1e0dc80e3e08da037c53765ff6fa23b27c1cef3abb369abc1d0af36d300040c2862808d37b72ad4ebde6eaab8296bb3aa8d016d8ab4f1c0a6111b396ce4d8dc586381d71a465d17378b3c9e70dc2dcdc1366f9f59ac6bbc62f1cc6270d9e4ad3ef236b36716d36b9a5c8179b3c86346f78aa78fbdc7ad515b73d5e431577eb2bbb7a9aaeae1302ecda83482d2a85cef95a62e8d85343d3476d0fc4033e30ca9335fce24cffccca0fa1549cc6bdac37658eee9baf779c3e4ec3e821e7352ab6367de1d89d7766411244fe673b328903cb9f2c826d8591cb65af8fb9d611a59970a8dbaa7e899c5ccbcad12799bf7be4b7b4f8dec3eb69d1b55bd8b6d163bcface3cc3313799be6ddae9ad5c8ba62df2c31ebbce2e9a39c19a3990b65229541e455c64f99e2ff9b6226baea2c977fe799c54c22cf445517b730af3c6b55ff3a9c799efd54553d3cc97822b3c58bcc103263ff295270608a14d9dcaa704d5b628ac78b39ecdc2cf278e6a49007e582665f795c30dbc8e5720fd6a545369b678c65b33c03ddc58e3ce6b623058801e32c76bd76e6b0d3048341f695cfac7351af7927b5ba51b83cdd66aa756bda891ceccbcc2a328b1667b6d327cbdba1be11d691c5b96c8a6df698f5796622578f99450b14d599ddc7aecfcce4b63bc1be887c1f49fb997b9a358a6a961393c3e4b6fbca467517c336ab733feab63cce1e89ea96e36ee8ca63ab42bdf268b95b090d5efe0aff7f7daf2f27589863621dddc1607045f2a8a2dcb88fc976b7ba6eab1bddc7e4dcca893b2d99c759b862d745e0461a495724fafbcd792902bdc4f8fff7a2e13c59fdffaf6f5d1e1d351eb04b9e0776b1024437bae3b6aaa639f0b61bba5b8dca7569c0251477e3b2c5a8b9acbd39145bf99abb2e6e341da648b92da4d89e8a9dbdd7962b5be0fedf64793e37cbd154550fd5b67869e1c31e39bb8f88ad735aa0fca3bb6d53a310a9e5d4e265eebd72024efdcba99925b79d5972e7281bee1ec99d830022077b2f02e8dad4b83fbc170163ff6f6e782f021a64b993e58b9964f7dd4633579eb79167299205c38df7c2f2e85f44dd9a8af1b56bee669e9da3781b0b112c5e8cfedfdcad6691c3e1c6cbec4657a410d99199b799cddb474e9122450ab31b77abaa698e2b765750588164258c15b52aacde5455a089447771d8336f598eb7533427b5f1dc72ebdb993b8a3413d5c515b9ee30bbe1de604956afa9f02bef3a6d4c377b84aba2a58a1c955a54de50514365edbfe53c2ef7cda6aaaa87e7eec8ac6a23cb5774dd46231f6eb6e56ed9ece99a87fbd88f469db793ed26eade22f9b6d3fd87c38dcd2d0ee8c36477ab5574a7aaf03c75e6ed11b3f79a32eb77317714edba8d2b67d33ba171f70e146e1f7270315ec8c9b1bbb5d3c68db23cafa95077bde3b2996cd97de4ec318fb811e345ef23369b63bc9093cbe8dea858bb7164619ab9edf8dd9ae6550f75ab512237c1dacea6c48cee15c91e73db37aaf2af7cdcdbce98dbb2d9b530fffa899ccd731bb9dc99cdbcf2b4786e36b77ccdc434837dddc8e6c97b4691ecb98d7a3dcfcdf2562e9b6de76a5d5dcab63c6faf7c78ae1c2f13d3132ccc6cae8ce537bece5dec9bc889dcd8d3551787db38d46cbb53b6cd7dd7ad759add775b4dd5a1585bac4bd5dc8769db53550596f5add7dd377bdcec57ce3aaf7be3b55b98ebd6f478d46dd96c7baae6d36caecc88eacdae7c4d895cac45f2ccebf2f85936f791b48bad69de75ac56b3eec1e1886bca964dbd594e5c8dbb4dd5e6dedcb299597d0c4bcb66d76bd93cdb2d97cd21678f601dc56afcca3d9bbaad8eb7b9451ec1f29aa6eaf1a85555f7acea96cf65338d4d5591c4beebf4f1a877f1b7f24037775bdfbaaa6a34ce6e202b77db468eb6b643cdb6796a7caeebcd0e57363ed7d1bdddb651bb0df751aeec71be570628c5ae8bdc6da37cf84b1e39dbae3ba8d9764d7fc5aed77ebfb962d7451e1be5687d981e79bbbb2e1edb75dfa9f576dde930251291bdb3c41da75b9dc689ec66f791e0cad7e13ef6bcd6f8fbcdede1cad7ce9a9ddd29cb6ae12d7ce33e12b48ed6d936ab6165435e471ebd972362fa568fb9ce11f5ff55bdfbc739f23b92f32fffaf79111bfeedde6b1dfb179155bd7bd67148604e898f7bc9afff37d7ddf55673abf316268b2da6d0a1813c56ff664ffb9ae67facf6ff0d705ab04d8bb09be545b4aacdf89a1a83c13535a26b6a671cd2a1a93eae3cba4e55e0303df2617ae473ed96db286feb7bdf68b7dc56857ab7dc8d87dd90eb6859aeeaceeebb74e501c7e66963dbb931d8ea60cfd3c6cef294ddedcab6f2c84bd1751f8fc076cbb15b4557bdb6e53a523db24839744512775cdd9aa22a5fcb538b43d73dec3b8dd55b45ab5bd3ce553474b7c73d1c028f46766dddc737bced7634b26b3bcd87195bd970f768c1e2ceaccc38a4fbfd5cdc19877441546dab42244f4b8f401f6f667ad499dd762cc70b1289dccc765db7598c48e4b95bfaef3b35dbbef2e19093e3cdf194dd7046a4315dd3b63348bab3306d7547598d6737d4c2ccf21beb8adc6a5ac46b379188ae7cb87baaaaa9316c45b29a450233120daefcfc4c5eac1011e83e92fe268cf6ffbdc733fa4e5b609e6efbfde6f216ee9e932d9b66913a780930c2c0d972550eedcc2d573b67f791dcd3bb14cd3ab31b5d53bd5bad2287aa1e66dee6ff12ff577fb0e0bf1dea75a3acc63b2283e8ba7b90a3c81e6c77170b06836247e330ef681ce605835f96f9fffe14f0d81759d3dc931a4fd53d80162dc8d29eecbf5f6ff3b6d9ee9e799bc34e76d7e955b79d392954b9db1df4ffc2f7f230e21f9f59e8759a45ea366f8b75a48a342287c1ba35ad537b70b71a3d225154b7eb6ed734b8eff456bb0ea22b170693c356a329baea6150156a76af2af26e4d83edeebf604fee1c667ad472d9dcc7649b93db2eabc8789a4dd56070b7da1ca6aa107954d30144935b77ab872b1fc665b36e4dd9b6e73306b1459e194dd52d44f7ee6d9ab7757ad716db146ff78ee4c96d97778b23b77dd7a568cb391b6c91c762d76bedbad935b7a259d970f7e86aec46f55eb31b7255f8fb0d5376afbbce6ec8d5b3649baaba65795bb1eb35dd6a54b7bb2d6fce474ffb5e370e76a3eb96e3ec0602b7eb585d44dbc395bbd90df7701fe538714d836de7474e5cd93871656b3b872bb6a98f31162c5ae0e9b8bc8daefaf70b1e7bef9b455160daaac8b656082b6415421422142244c80821ff6fe3bf818816ff0dccae07ff1f7caf54c777d66477077e6f0b99e21f176464e6984926b6f25e24b85176f7629bffdf44ddfbdec460304f1bcd548d6c875a55f5d038a47bfbfd7e3fe3306f1bf9b045f2ba354dea63b1eb62b1eb35e3900e4d83754ceabe9ee88aa84613d31e5df736b5bffcbfd981669baebc8887ecbab8e7f0f6eab543a00fa0ed9b2d9267741bd396e5390ccd3a6dd31cc6eebbbd26b596f15e681e3e30cc5391bddd752892adcb3d25f5ffec4e85080889081bfc9b2b3f2233badb216fc37218abb525dde44d55d543be06b79d6ecf5c6c533bdda25adb781da71b68bcb3d4a9e6cdde91fdc8db9e7d9c4762069e1ac0d62d77ee96c8bffade37829e826f827066525591c4ae8b41104412ed62cb79ee1a4f67541781ff6fbe17112cffab9623e23604d610376f9e60390c653b378b6cf59d2e22d119a7aa6ab433eb2d5c91fb8e1b57bd63887088857fd3987566f9306db591b748bd6aa131f794c877714ec824214b84a808a2290895202ede1472f6b8ee3b76b7ab5ed97dc7ebb2aa91ab6ecb26ba8f47ceee62db919cdd75ab46c1789b51bdd935cd5f1969a29bdd4634a976244fef367bece950ab47b5ff8ff15e403e0111f3ff269830ef95e5c2afdca67c050241768d074477b1a76bda530dd7f7beb1f288d3addc0e35d537dae6e4d0e6fabaedf4dcee71e856537d23ed2d892651bdfbca8fc37dccdb2b8f76dd72403a28ffa6dd90ab5955d316dd7b3844f6159977718ece06ab7f93dd44225238d4c79e893cdb0db59a75ee475d64379cb9a619ccdc66fa9537b2f774b31adecb47221f792658dfec316fb7a95967eeafec23e8a3c1ffd7ad698be46c26f29cd478b9a76d58d778e866d31e97de2492bd570f31df23469fe341ca4477bbd9a32a3ce7786ce1623c62982b2ff6144c55f5575ef1f81c296431b769dec335ed7354bea37363dbc8f256a76d8aa6fa88b27372acde44777a9e794e2ed1bfc972a25c93ffba35dd7643ae02e5e8cc647b34eebc59a15cfe7fb3a3988b4bf45babc38d66de8a6d16edffc7de2b6e8b6967cc9cb77bc5e339aef8e6ffaf78afb8b137d115c9897bb8eeb2f7da412acd613bb6b09cb8ceed101a91ebce27cb5956edccbb385796c7cfcccccc5455a3fd7e6668b9eb3c57666606f655661cd295e5f133e390aeed1b35eb66285b1e3f7b0bf356e4cdc2fff714cdbccd756b7a663113ecb88743ad5b5dfc22ea2de7c6c5cdcecd425ba9b62fff66dec522de0abc5d6ccdcd6ebca1cedb2b4fae401689def196b8d58ed4c32decc654ab683a08707b2f1d36ccdea699b7195db55d07e62895a3ceff9bdb88ec9b3d7318926dcffc3bd18d9e59ac2eefe28acc2ccfed961340150002db1fb6edc51684adc1daa43737aa8d7cd5ed4672a3ee7dabb9a7e919ccd3c693bb994955d549754daa75dd78e2e5ba155d816618bad7bab4e730adf3c691074790ffcff15e38bc6e84bab1e5ff5fbcd78d20377a6a76d4c6a8a1a59dfa7fdf7ba5659a92e6c22bad015a1eb4241ad8ffb7bdd7d92617ef7516e7df04fb22a303fb62419773ce62d9ecfc684c734f8d69d8ee291ecb91ea306f7fe50cd66e36efe1ba8b5fd9ec672dbc80b5bc80aa8066deec67163337da864697891c5d9146231f6655e56d9e9aa11abcc607334a6652ccd868c8d2f15e34eed000e24563c6ffd778af325a5e6575cae650bc5799d88c4d33a0cc30ff7fedbd7ea37e5c7eeff5f3f1ebfa7f13ef25e3930c2c325c748a4125868bff9ff15e01c40aa04d00550270f1bdef3535b2bbdd68e65dd5c7ddf6be37717dcb3f31f36c43a3e3c35d87a6ac30ebdc3b67873a838199660e0333cdaf8eecebee2b47fbca612cf2af1fbf4c76175b1649ccebbedbf9ecc3b4e579bb6e4d8bed36f23e0c3bc1862809030303fb5ab76a7ea1bb5d911c4d55815fa89afac83f246941440cf7dd97714877e6fceb3af7dc75368fbbed59d56b4a5c7946b26d1fb2bbdd46cdea6370e546de76e4f0cc7be5684e0e57ceeee351175b5dc4cb4974ef75987771ce3c83e716e6550ffb66f37a4cb699484cd1a4369a44def654cdebbe4bebd0cd227bde76bacd3cd134d8f72606cf34a39be549a490b767127976e6b07ee4c59c64758a6e634eaefa6d45fefd0218c9e34eecea12fcd1459612d3acae5d6066ddca59bcdcae1b4f2e9b5d88ec99af79d5ec59579c0a65889e2bb0af604fcde4b60b8ac07efe719f060c7c24578df27fccc03fe6b3c03fdee3e692578fdb80253ee68dabffdfb02dcf4223921d669d3b9608babab23aef3b2e64db28773b4bdb293b97d155ff7e4223924553a4a69a78b64863b0ed5a3df71d8a8d378512a9ce9a2c6f35bbdb3492290bf851a1f74dbaf4c955eb24cb8ddb98798b6aae227b6a6ae1aa8793dcfce3042c24d16d64a8db56f7df0fdd2bdedb46b9dbd0c73f46e01febb96d6c8d4d71abf024ec7fa37ccd7b4dd535bdfbd28dd8ec312785970d96e08bb4e9807f9c07cf3fbef38fedfce33aff98ce3f9ef38fe5fce3a8208cf38fe3fc6338ffdf7bf3f745052669d7ebfa6cfddff24162f4e84ed956060b734c4c888aed946d7f68409b799e596d75303d4db9f4a8f369eebb6c62376dd85c07be02ca72bcde355c5e4adc2c1c5e7323c68b2e87eab3182f76d767a83efbc76ad2a03963068247ff15cce6e481e04b86c04ec8fff71382b23febd60666b828a84024c8604c67cd610ab8ff7f01e52bac78e85675ab6ffce3325db73aedff376032633688b11026022e000ccc3ffef28fbd74f97f1d668b3caee9b9f78d13796c917b13c3f2dec4e0dec424abb679482e5bb01603c0beec78b0a72658cf49c08239ac7f9d48f295dc76c1a430183483bd6fb66e4d7ba168d0c82c72d8abd241b3fac8852287b528d6aef1b8622042f5ff7ce5928068cdffb77c352b76bd6644ae4123461451e6ed214fa6ef509c43c77cc8cbd02161bb6eb9b41b3e431b1c4864ae4836f336c5eba990ab42d401eee6c05a2158ffc04262d44267a19c7237428a902ca1b741af7c83d49861c5ae8b72d934ea55abc47c0e82214895a03e82c4085a13f4f5e636f2e17177b403cf73351b28d4806a668db269569bc536c54b63c30d02917afc6586854515b7018af3398cbbddc8bcce3c75abd1dfefcc44de816979173bdf78a62ee2c1b5f138f2303bcbea61ee1ad55c453b2f1a8bfda8d7ae33efaa6ecf6dd6993b871dc3e6549dcd76f7629b168729bb8dbb78dc756b9a1ab31e729e41d4be026bacdc473677cab628da21a76e751aca6eb83348de5ba8aedce443bcbd0e514b7b43b30b065575e3fd7ebfdfb1071912242c8bf1babeefd2ba95f39e3bb0b76baa5916efb8b7dd90d38ef3b5649b0ef5510fbb9890dfd5ad28cbf1d0ddae1a5db78e8cb6dc0dd87237ddeab4e419badb549816390bd7df1af5ff1fe07db77aff66db6ea8d9b9628ba678e8aa85417477a4d95ab1eb6250b3fa6886ea5d04721cbf5f5045f52eea20af0b0e57e03f26e01f67f9c758fef1957f6ce51f57f9c754fef1947f2ce51f47f9c750fef1937f3cc03f76f28f9bfc6326ff78c93f56f28f93fc6324fff8c83f4e62bc7f7cf78f91ffb8fdc7fd1fb3ffd8c83f3efe63fe8fd77f6cfcc7ea3fb6fbc745705abcf83cff23d511ff2be1ad0338e7f606842dee6dad5df99194e5c5b38d875b500d07504e4ead2d981607f401c72348076c3bf31f62fa9fa44e026f12b9b23c852fab33e5bfd835aa373f1af92eb88d72e35f1e672e2a743310911e817bb89abb38679e449edfe6f0f95173dab71b725578e5a3ccf9c9cd6120e7807f33290cdb9b988bc334eccc7b138168aae1f6be6176def663da1e8da97a34eaf33c1a91c29c6c775d1897cbe6316d4fded5e20c301579464e9155e4af4c231fd336d86e7506d8316dbf86aa1e0669e464bbd5152b7258db91ec799ed93ca6ad3087811dd336f3f52b8b255556aba97145b262edcad953d546b6af3c5d577e4cdbac75de9b184cfb7976ce471cd3964def46e4dd228fc653d56f27706f6270ab5d0bf7bec1b689c1b4bd6ffc3f8a7f7ceba8c03fba72a11b5b6ef8b841bac96bda7672166d579ed3daac6993a34d053685d8a82bce53ecc8db9e666df258e36acd99356bffa6c60bab43776e35309b9b45d6a5c5bab4b8c556beaa59a5268e1aba7fdee6134cf805a6bfd2ac4a43264d5a1a086816408386a52d1f26856766b70f8d893391ce2c3973fea3abb6d3be332ecc6cfa6f3bd2689a71f3959955064e99b7583e3276c8f420f363188d69f2e619b6a6c9363d4f60364fdef6547722cfc176f7e0b1eb1c4caac1dccff6b86788ad7c0de32b47fb574ec784f9c48812f32569f6b39f28901357b660105db9104d8362bebefbc4fcfbc25cfa30793ade760b534c0a7d61d6de44f79af222394ce66705702a80ca9bedee3db9ed523427fb16ae059c0f2c800610ddc7636a2787b7ddf0b61b8aa240bced06060e6f13d754c7175847e470c8bf246a77328d2da739def7c5c617afff7f2fa4bc88f102b77261c6db6e5ebedecc6b17586f764924c0fbba1ceb589d7689f1dfa5c13f974b6fa271a9f31ac2f0ff1fa7fd9b76432dccc933f715dd79e98d6cf6216736935debc86652d5a8ce6b9aeff2768b34f6339b3d838525db74451a8d7cf815569c1b763674e56746cede687baa83cfb1b5dc8de56a78fb06bf287c59e2b29468917c05b26369000b132c61a7d1a88dbdf55d497465ce15baff1355796bc5022b96ac286005fdd3d4ad4edb6c1a975c75c6db6dca5aa902c93cb7aa72352cb8f23458ecba5825c49b7d05b6eb96a3c2eacdba5498396f7d54d4acbd4ddb2d47a5a8a9c4a022e14d344fb7994876dff19eeece72bcdc83c389a1ac2e265555b7c06cc2b5c5ada93a4fb799690f0ea7553ee42d678f59e57dd7addc980e39397165437751efa4aaf3749bb9553decb9d543ce33485ed34c2472b18dce486a3b3e363646f6c58b68d94c6a63525591c6b4278599b79a08d79ac3d4a8d724da856daaf15ace8579af75a9985e67ec9567f3458b172f60f035e371b62e7723d24736cf73d548239cc88d5d8cdd771c5d75cb5761ee31c79e5b7335a98f9aa37ab378e751b7b57cedec2eb6bcab7b856b43a3cb291aa7cf463cee42b791197af865ac72964f49a25ef92517af23d73f27ae6c68c1e0a9d1727ad42c129816ebb8aa730f6b595eecc462d88811994db7669140363860d2c872221b1c30b8eaa14954776fd3ace6aeed869c3d5b9d69d0387358cfc8aebb50ed61c2a358d749d588ec5de730d4cca63e7b703855ef8eaa1c8fa7783d6fbcb64dd9cdea3417875a2ef7beeb345c6777daefd4dec27747f76fbc3345dc5978145df9d9ff47f84fdf8b8305a403ff2f620e096c2d68d9b44df48eb6144975abbca7f0d19e2d8921d298f1f81aba3b32d859fdff3fea797cbdc99f305eb00876b663c76c1e568cd9b7b06bb64e6caf695b3637d2883ef29c6cdbcd1e873cefff4d96eed9af37a2e8df043b597ea2ac1ef614cd71bb4ec3a9c11969718425d6a643ee3bc6f93737724df97aec7b4d81be238cff8f7b1f47559eb67ce57cb8d0c76324d976b37ddfa9bb1bb7cac3d096afc52152ae5d59997c5df9f474a502b7ee3892cafd7e73bb4e1f8d89dedcaa91849182fa473da2aab0eb64a7c8eece6747678265155f6ff9d7ed586d2c0ed77d66d6e9e35dcab23ab8f261ee1a6df366bbd647ce1e9366e791cd73ab689a0e39f374db5651b676f7b3efdfaf9f4975a7755b4dd175e7af8c6e644eeeb0357dcbe6aa87c82158cf449edb3ee424bead5b479e6e3b83e07135275555ab9a98ee2369df48ceeb32309bedba7baa1e8de9aed3c314d54763daf1c0c456be7ea16b4fd3f65dda59e470c7a1bb23359c2a545356b3fc68dc732890bbed3a0dc7dd765ccbdde6a0fc75f237c8ff17df0b84dcdf6df698b7d1956dd7693833b332b7ce22b9b05897ee359844f566dbae5533b37d2495cb61652c5af0e0edca96c362208f7a17f79154eea8f7da3e92caed3a0de743a93cca8666b2bb25a2bbae4ecf9d6652a8cfb6afa9aed37965f71d8bec2babb7ca39ca8b6dce6bcde6cadadd837bb3adf0f70ba27bcbedcdce9df576dd461fc99d83e537ec383febc99da3cddb6ebdb3f5634777d73afa1cdb0e35bbdbde53cdd74c64cb43cedf8fdd671036d8eeba7c9e794dd75ddcacde603062b408604864bf32baebd86dccbbd8733e92b66bcaa65a2cb9ed32d89a93ea57ca1e8d1ceceb348b812c1bee1e6746772f0e913d13d3368cada7e79a8ab53bc8d71cd65175a77a85cb26bad5541359ce967b8b54d58daab501e36e64bd93eb997d4084986d687440d08058f82142f28f0a7e101a222487a1690e22c4fcc7c21f7b67b29ef6fedfc80b49029f1e819a45024dcd2275b0e56a37d1dd5b1d9654594eccff58b740d99daac77da3cddb6e5b3da6ec1c9f438b5dafd9b1dac8ee56fb381ad9b575ebe8aa0a5cd958b4e0c1e7d024bbeff8b0a7691d4ddb2847db2847c3db6eec06b2b59daf1d8d433a23c6b4b66b1d289b6e755a8b7c3b1ad9b5347473b476cba169dade37d23aaaf7dad0489bd6d13b57e5d49e866e54ce38a46bbbd691c7852b1baaf75a0fb4add556ae6e4dd9ba3a60db531d3db973a07b6593db5d17e1d4563c9e0687c7dd78b01baea329cbeebbb7a3714887eabdb687e91dbbe5da751bebb61cbaf233b40f009f387c7236febfe56a47f7abc959280e5b6deca9f1ff1797e53b1b44f56689e911681a91ebce4382e4ddf634d9759bb62a32b7bdcd2bab76e655f76d34793a62b37b6d44988a62c488bca67984719837c238cccb615fb9abc23a74d5624916350ef356241b4cb268924dcbc6615eb05df79d5a709b810d778f1cf6651ce605d11dec6a2b322ec9a6198774e911681cd29de911a8aafacc6143cedfaf67332c450e4b917f756bbaa6ba1572930d0e080ce6bc3b322d9bbccdbf1439b3baf73ddc6b0e6b57146b4adce8b09fc354dde8dec4206f83289b8f13ef44d936d138cc3b8d43badf79e614d96c43a3cbc160d0ec9122776026a1d32370ae23896c70c06cf2b6a365adc48e98f7ffc5f7ad39d059b3cddbc17f4ce4ff7b8f87fc6321ff38c83f06f28f7fd0fd631fffb88717e6f18fe7e2fc63b9ffef807c1f8e263f5c77ef3b88aedb6ea8858fc5dcc01166f6235f53cda6bdfaa7f5ff49be42507dfc163c0b29aaa409c75d52bf59544d8bc39beaffefdea726e43f88eea40b734c6caf6971a88d6262c71de83692671c77dbedd2dd373a776ff868a130737ad4f93c0ba2e56e26597d6766b3e56b07e6751b79dbef3777e6e2ac04d011f00950089460a38f8d2a3e1bdc46881aa66a80f1d530cf76f76031673bddfe7e689a7666221f6e940fbf32d24896c5c0da2db7be9d99c8d3afdcf39a6eb62375316f0fd36072d56dc1aed5610eee95bb05879c6790a4306b5dcc5d178f6cec3ea25d3562dc06668efebf9b95b1c3353d1a756bf6bc86a9c2b4efce8bc3cc22f98f6cf68db2fa78d419e5ec31a748a1f174de67d9ec769c17f3b98f0457bed1a45045128f48dedeede1ba914676cffa1fc3b1c8fb48da8fc836ebffba7f21ff0ff65fe57d33ca74d6dcc5a11ed6ad69dd9ace589ba1e17f97feb79177e0af0ede76f3fd623cde26fa7e32f2cc90d1e4cd0eb6596efccac955cb00fa625cfa17e616c5ea8b212606dd9b311a70b700f27c0079be00341ebb51be866560006ffa60446279666166f6828117575ee47041cb459c37d1807cbd99c79415d36c2d78b48860e17d2c62f9581062616491c1470689cc8a8fac4856668a99eb56c5f2800079539181641559eeebed3ccfb094e5690f7bcbe64976e6a41a67eab0936d2ccf37e6634cc60a09561cb0428829b6f21ce6334b8841f28919129b2216e71323111629ec49185c180e9f0a593e1588547451619682540a08522849f1e54bd1018c11981330b42f55ff39c526a66aee6d6adc6dff89ed968c2cffde56b081c9651559ae8eab5f6fe7576ed94de427b2aa93c2a1994fb9338785f5ae8f62a6d8ca796431f3049353d554d52bba7ee5b4a76c790b7f67db964d5ec4cb605fb98ba52cefe9d9cfb0f43cf3cf3c832759163b339113539df3749bd9dbdca62bd204cb61615f44ae22bb38b3d9f1b80ecee77ce4e9361edc6d656337cabe11df5ae4db4e598eb76f703ee7c6ddd03d07c84cb3dcacac5d537edcc674aeab1a98a7dbf66689bc0de2edd5c859a20938d3c4d897206582b13d359610c3ee56ab25ea7c2534fcbfd93b57738be4612d52d8359e8f8403ff4f0209897d4cb62db28ed5374c58c64b663b22ce9b422ae6ba8f7096e81bb1362229cc2d70844f8420130cddad082affa64fc49830c352552431e7e1ed1d377e6554ce97a9f832dc87a8f56fa2c28d860d597e03dd78ba38876e74a36b3aec3b8e0ddd7167e86ed7b40dddc5ae5539b6d5c71dd76eb9b6731d6d47a6a11b8fa3ab9a2fc4d7992f840f049f7ff3344355be3657467ceb01a2098818ff0fe27d20bcfeecc3a357f507356fa6c5cc79ebfb70de65a0efc3d8bf799e1f56f83cf03153941bf316ea56dde8ef37b7374b0ce2ed75e5a959d970f7f07998eb40ebffcdb0dc759e9aafc31b5f871e1dae8f03271f07413e0e6e4cb09545ee627ba7e6ede32b8f91ed7d976a3ccec3c7e1c40659be0d7b7c1bd20d2ede64397be3d4a0e7ffe7dea781884f83075f06495ffecd20ba874874b32b8b149e614975b3db185c59a4309875188bc4e3ea16e62312c5ca22cf7c19b42fc3081f060bfecd53b7ea9d4e6abc5cb7b6ba238ba0400c787c18a0bc79b69cb367eee905092e60b920c302280b7e7c16aef82c20f15948514192af02920a267c144221d9f62befbb54d599b7f908b6bbaa895f7a98c3ba2e66553df556391733777a4c87a6396c770eebbb4e278f9b3d86d965cd0a7318d899d78eee5617dbe056853a9beddae17d14f25080f2e6dcdb5b5c4e0a735298d9dd9159f3f051b8e0f369d2f03e5f219fcff8ff26ca8bed3692508d6dd134f30fc9932664c830533b3de43c836839369fefc7fff73cdd6626575523d1f42e45d7744ddbbe91616816b1576ec7d3f26fafa99a8dbc6df95936799b7b9a6616c9d33b5ddcc276d725b5b1efe1ba913cc9964d399e96b3ce47645ef791ff56bc6447f76691c3ddd7f4dcc4550fc338bb8fa429d81422dee2d4806437e0da5c8e539b03aac5e1c86a67794e8e475b36d77de4d944555edc46ed96cf15642f64b0205ba1aac8e38add6ab6ed42645f511cee36d9a6ac3eae30f2a1294416dbb46ec58a5f3fb66df1cc7bcb659388e4779a98d73413b9dd6689195dc38472aad9cfb3233babcf1ed68944be51cd6ee3d989e43ff2799e5949ce66bad569398ccd616665c6b42877d46dbb4ed3c8396724396f353d1af990afe779aa6512d9247234d7b5bd357b9bae48ce6e20ba55771ada0ef54e63ebfbc8deec0d54e56b7aa7c9d5ad69db851a5df7314f2d4d8d2d2ed9a6688a3492d6f538b2e3ec865c4767ed865cc7de2cb1d76995a800cef335b0f1ff3a63ee6b00a12c43f13e068cd2d27c0cb698c2943df33180932b5df8ba36fdbe4bc57c5d6afe4db4ff2ff967f24dfe33bcafcbedffcd7ebfae87bbc7dafbb88270e53789c454a8aa9cdd2a2fe21977b135f256f7b6af3796f69587afcce22b7fa27c780c4ef3612fdfa5f4ff3cde779bf82e0bdf6bf23d1edf6f314f34abeadea836869dc561abcf4e24bbea1c3697cdb61b9143361d6655d5766f67460343fb7acb6226d8b9bb3efbfd4e7d4c0a73df684f8b735fd9447256b79cb39960c6fc95cf1fcf2d5ff37672d5b74edd62742bef56b6e5c8961a5b396c59304f21b207d1ad4f166429e0ead27e9e2b98d8bad976d578489e1c6a7477246f339227cd6da65f44b6bca2752b57756ed5f486694cae1c45ee7ecce8cedb6846d72d572b53ad38645f6fb570d4da40ab11ad3366b0e53c2ed84d5d6c533cadaa3ab8efd28d06bb0e9a1ddd686f26c41f2d44762f022298f5c82472ce1e6789e16eb38a9fc696dd66a15b16ab7fb3efbb54cd2d5f770e598b4c59b23cc422156b4dace29bbd4dcdb327b7dd9683f50956175851609db020bc52e0d5f96aec025b6fa23b2f259e605f1d4d83c96df7fbf5e4cef1fbcd9d190566b383917dbd9d592cd9a67999040913acd875dbbf54bdea6dfcca3ab7696c39ddc2dca258fbe628bbfb9799dc762af289b2ed1c6772db65adcf5d64f50e5c283a9d9535d40cea655f119b4778d9664acb28734fca068670d7968ebb58b8fe5c380b6a384d038f1f920942e985499b4ff381d28d206fd56d05098921180bf4647a10c1a74815fa220b9c25a52c653753b90abb4283e05e2f87ee982fba07258057496452852b248f9bde5c9742cfa481578f2a93a81245a60b7a6655f48541875489c4520b8ce6e5761d4bc8bd9951103e0b9b4a9f0492959b424cceade5075229378470e99888ca1f7acefed08930180a193817801568b8d20708d71432c69f9994dce2213ccc67ced5437143e133f8f972b75ce9704ff432e04a6f60bdb50d0fca35305e5d17a08de10799349430f8dcfb8892182eb916fa8ae8da509e8c9adc0a02387bb4296afe7c65e3baf5b8f31e206428d180e7cd95ae943a862cde800b3860240114efa614e1924282bc3b0241ba193a45b8e55d18de70409b3f41c74c294104432e223255b0ddd7bd665d8dcb0801164c8695865242998c3d8cd19520baaa604873537a0a24ef94198c2429096df9725dd9aa00dbca0cfec69d8bbbc8003cf7876e282d8469f03e30a5dda01787471d7cbda51010fc025590df67a8769388992a8b732c782d42f460396553be8f8350f791d1d1a78a93e21c87bc2c4eb195050ce76011145e6109ec54702d4805567e4a6078f11c25e829e923e1c23021a577a7ad712dc060f0a5ba665d1b4756fd0620679e78a960628a083e8810062e6127ce15a476044b2104e119fda9742dc09a753321cb1e9104a3fe8e04396e9c4e767749201af8cd29bcd885a63b6499e0555d214a374bf8941d3c84e121330ac0663048ea8a61cac63ddbd062427042dd0a02f8c196e21edc3b266a17562726df6580347c632d797d44f6dff199f2b05699c25f1ed8f2694adc9477e0eefe8aa827f890ac3e5f2a89a4ac4002c35bc0f3058f49cddc07a89cc2b0c4d878326a6fb892ad1cb8142721d621e5014c25142cece7b7e78b3f2038918d35586d0234372c0e211c659d543e2a0bddb30672a5012da07fc1e4c85d5355e96692ebf2582ea0aba40957f9b7d26ea5146fae221d58feed5190af8443f89be6c0c16ee856dc1225b4945ae0fcbd90628097a0ce83cfc2460b46036b51c975c8cb8df103cbed81c549a94748a10ba882235da172fc6e2b0352dcafaf1a18ce2b225e6b8df92ba3fee96c68f46e369efc243461de83ad27d7850d085e0107e6674822ea2558d2029379e1f4427208296f50a9721b58f8f05910b1dd1f48e4e00be038dd1d423ce1fec5d56de3baa09c40023365180d4fbad8424430d8a5bc1e140130fc9b56b6bab82c404129e1d697267e2c70a439b7b003248d7e02455eca4917fe0e378dfa1e16316f6656056b3851e1ae619254961883eaef885cae020ad47a2686227853183add2301482a9b78357aba5043f089afba0ba296a38f23a7f85b168dc04728b2f0874228f0006e1bca1a6316ba9878fadd39176a7131a9e1f5b6c8807701384bf098903d65da123e5fcac125cca2ce86d734e7d5e5a423d0bd93e9dc3e82f6dc178e1e3c1f55112e01a58e4ff3e9caaf9870f61b0420bb0a0873dc376f1cbcaa15661ecc9bb8776b787cd20ecb5f5f9b3f9326d1bdf382515e70e8826f75f703df0e6b6e059896743588e0e456606115035f0bb8d286191e8c47d6e7a7b012e0b3c80017c3fd282da91d793ac03365291376bec11d15d8d511b147d52212be0137f66a408df8212041f70b098bdbc0417bd7f37f8e2fce0513e996c641493ec570c14f91b507cf12f4a5d4a307821bc294a99e19b731f7347189eb018a001e342a50ef48cbe8763a0e5fc7024fb702200d4a3947b65eed64e89af0fa7143f98eefe128e9368a73010e9390e99f18cddc3c3d71df240b58998706828b6602a84f53c09b6f3441c9079b00ba3e861029ada03a71d750928323d409e2524a12e0ea0000aceb684a5baf0792705b4c28731b18740613b9a256f6803b551ae1742aa318a2f4775c28aef440d2d5a0c9d27312222ea43f4ab01a2a75df4101f3f270e2e046e2f2b74d835a17001774f82741c6036972ebc69900cd7da065d235b601de05f28cdc501dd8fe436a0086f0000bfe214509d6e4630d5eb37301179b5247aea2490bf43208028181b332707dd13104775923eb81b1b0e4668855a8848b43fb086736fc86449afe0605284abb58f829316077d9704df90793325dcd16e376c043a574206ad5253501052ea005a99f50e6cfa39015e65e39cd4a281d5a5cb8bb4fa55b952c78ce4a9c3243962d4f050f2b2e589be04a35766e30de124db8d7294df9e600f523e51dc262274c5c285b2e954056577e440d2f980f21132f844b74816a1edd08da09ee982db56fe4f505e7f02abe033f5a613e3322b80e941dc16d84440fc4e4011f3585f80a24ccc00cd86273cb405a52be353984ed4c48b9b75032efe878067bf244e5367955888bafa882d16cfcae9c038a708e46702e9235f38c8ac22b628c91eb9629c2fd9445cbb379824059498ff16a69f4e67eea30e1dee92ff7b702ce6399400766401c75a7a4a995a6d618f80f444e94544c4979de96a3340f69781d2568fd8f1f7aee26e3e330101db000410b7a9e9200cb38b528e13152e06ed7aa065c4456e07bb4d5aa572139c18ba2c0d44b2971fedac7cd654097e14db889f34243266e7ee0e93f82399f2613a12fcfecb840347af01e469c3e102d18f7161c047ed487f52d999df9251d28941672607844434d1f81060db84992455c3242f5c01138ac612f3630e0092734955abc12c05c77687a4d2cf44a2f4460b70047f7269da0c23baca03d051a309556f29f777441f1d1800c5c595d6ddfe4d3da5dabb3402908918def7266813765e5d42b11f4df458f939760155e9946908d47b96cbab1c284dd13c3da3b90a40afe936356a9c3109ddb674e9c8b774502fe308a03de2087c85780670357188e2498c9017f7e025632aed18991c7c18ac5eddd35c00e586af31ec87c57102709ee1d5100940974a5ba502285c23b287cba6c6b448c4306d7b3ea332c6da532f1381060722bf8d37b0348d8c29d5eb5b98b2001fa4cb61c6137b08ebd510eb0b988e48a3d0a2d36e05aa64c2e11c02ed8b784abeb47190ee55b724c979c0d2fee9b1d6c4a4d98ec2e72148b07e1a7cb5df22ad60f42d5bb52f284e5841522eea10f444a39374a9eae0d53ca199a7a236aee7c143042eedc0f07ca09b74e5d31019c17c3ac523fc3c924eca891dbe7993009f72094e791cc68b974b83272313148833b4d32f377161cf4ba9be1b7b920a8978756761f050086296991f01cd07a37cf0c082e251cc7af9067ca1fd0e0013c6450d82bd9d00a3f200cf43a4a797a063100b879544b1f24001457100be3d6b2b3e3aad870e7c75654f0096c015e8f0e42778d40bd24ba985c2e2f6d4a10937adc369684e5061627ee05556bc9c0d802658944695c1324535c3ee4c28f3d292f036862dd364a1ebca0178dca0c65487c9241246e2b06c07e519b2edd0cc5bdd78404eea307eaae21176f8f8a45fa0c40a9f9072eb0ba3bc59292021b8bcbe9961b1663c4a46464a25c138e74b88feae49e78b0a25c6401904b3785e319957e5c241240503a90e0d33585aac30f203cf26924689f4e92a79b650cc2d7b900d25504a7f60c30845bc4dcf12e90213e03bae7d25158833735f0e636ea42770538c57c92081e4a2325f2460145022b2152b827a08cc0411c4dc21ca490e97d4969b10ad6c59583e4ebfa189ec26030385c6218b0476a049d4bd7860136e3a3c345614955e9a68045d7d204784a1e1560b90fe69428a35471bb6d1a287021fc6e3c94076ef8549755ffaa899f4f12e7d54dae6ebfc1d3dbb3f240e56e516aef2a98e672b961e216c2913f8132b2ee08238d5e5df978879a7b0dca9c380d57aa924b983097120461ef89241858dd4464a04181211735a6b9656ea1ba1922a8282d3c09fb392ab47c8e0646fd8914492906048b07fbf0f5508adcbd1e0da81e0059aefe889e31374f935a1fb78788a7901abb660c6d287bdce9f1782c41b925ce5260ae022ae1b5558af0963037f8041302a34961414f0a9b4c78ad6ec41d9587049673b2a77450c480dbabd0fe8a52877e55dd8897b3b6e59e298bc36352e2fed35dbb5e30bc78320c40bd1719c4bfb588d43d4549d33d72e5cb77ba9076b586b9cb8148e1be4a205f1e5f936e094068ae0b5628309a1a77f02453a570002d62f5a2ec41a03ce360e10a12e142b909c718ae09b404b88b2220e25048201fd7a2d24bd281e66e9140039335a8f07e9cf8c06dac483fc94a9b3bc194599f001709ccaa92a777e31a82c192149e0f0e2fbfe88ea2ff34e501fc746a0faa015d49094d9f0793448195545ae0ba0ac10187cda282bb2c8460b04242abcba5a22dc37420851779f8d4cb446a0946b3c3530f0452c3cf3223e829280e792c4eea7ca556102e25346517823b7c6ea44ea65e47d7f946625cf9a74a95fbdbd4e629d0d27403ad9a72d520c0dd19a7d92349d8bd1a3b722e1ae7c387d8f1e1d16e04c2293ce0f58e87e4dc427e8270a152385c01648de2ea32a2bda4e6c4f0980985b0240cc127722973bb548928173801e9cf96a07aba245abe0b938c32068d52f713090b250c2718de4889c47380c5ea96e892536a28dbf166e71a962096f64c1e45fa2914f4b9be2c767815a32ab70758976b0b44daed710a7589b6e85c116d627a650d1b1882081dbd3a0d645c37680e95837004fa04e01c7b25dcd4bec68cdeb302813f45d88bef0179f5b7ec88652f0b8e9be55589d20b89530fc8890afce60ed78d64d45416308bc247906503cb18bb75bf0ce9db6894833b84932995dc317bb35626302d506d17902cb75e4eaaffca37a5e7e6e9773d5c21c4255646c7e5304010ae114842e1081ccce1e220babc00966061593d02ee1e4a404a52b41e3d170aa23cde91104fc90d03e50306597e6dd1c63434b9785b4dc0ca092a0c70cff4c1f285d282f7138346d86c78e63d2d9155ea7800c59d3500217c26ee0bfe216e5c6992502ac55421d2f39d68f2750610a06c2341dcc592a7acfc5169c4dd90e843b98208a477b0d6cbba372fae001098ba769bda9e0d19b8df80995c40953efcaa4116dc4e65791718c4ca2f910506173a40cb6df1a55e5860005c5633ee60461e7edc240bbcc13d51affe0d13b8876407d3c553c9065777c0e072f172a80740175ae9f5e72cffacc1e1df03007e53a70e9b601de895d85b7e13a7338f3cb4e37a92d26222c049379013301f8907015f9762c21b4285f9b0535f309e4e9cbe813a0f57d42bba37fb206ce641f351485db907408a71a3c4217102b7487da74f264a4e6008e10cac54f5d45e542ab92554984e1d117f786b528e0972771fe0228169a16597d0a2485d465019f07100741ca786a16f8b02ebc60a42e3ea0a202e1aebc89f70b3f1004c40e6428a42e6467935e91b0de16f00aa42ef874401f802060d5c269134610a6404793b699ddeca8843b7148a097e500317584e5598d245f287d5daee359263941c54d1dd03b6a45d1f62f4f460b5214ab8354630901580ee0f053e611774ebab7dc15082b860d45328baba80b4a3bbd92cc25a1049f0b00468f174c4de8711c3e24095f81d2442d0f350247e4c6dcb8f419add220476e5241d5dbe9583086ea060272cc1d68a5dc135794eaab0e054d287eb4bb5e9c671a5e27680847fedd3976b254e2ee91051f0681c845c0aa4d07b878180eb28cec56dab71a24cf506e47b5fecca546254ae8f2bd1ed0a85e80b0d5100b3a241f60b8cadc1132cb073cf203980d784a1f0b088b8781c43e8fc254a14ee9c1d44ee026d16f4c8bcaa3b418a20a5260524e1e1111f5c38ce3061041258eb1a6100a61b009a987285990f4f06d8ea1242e5c08d5447c50e204cf93d29586009b218ba0e10d92f8b16ac5772c75e365c38bc291b5d7085ece9636c28a59a4a017c19895998ce55a66710614e19a26cee5a4292a4ece202132ee1e30ceeead0e80db8c00457a3162b1e53a1115c7a1841281bb08086b9aebc282bfd70f29ccc925c534b50dd436a62bd05537238730a7b3c3b15604611947c0c3057ef85c8d86b62fff24180cd23fd206f2451644fab02a03b0385a2df10f6bb7a9a44f9319c207dfbaaed2e3a3107b31071c0d5c0e5e02e4f12e1b7462fb8c08015f09cb9c60549d0a44bc94caa1e8736210ee3a244cfad4d037f06cacd95c3aad297154982779880f23306a5e158859a3c870811ca734c810b618c206c2a8791970013d9730895e17a85d16ff62daf26c7c01dc2adbc08332e3f01588bbf76d0944ab0b8b938c0a4625024acd0cd90e7419726172afe01e745976aa6c7af75a9dd560c8077461f4ee505236457c29c58b80004fcbca41e4f2e0251b25c4070c4de4c8cfc665154e11a61c2fc05b4ce9b628028cf032c56697680021f28491dce2340c8a36aa694732ff7567ec8b8305651f0679e528fa3521f6c4006160c2cac213027246cb85c4909c30da04ac32f80233088dfabab66eeecf1f86ecf003ff9da6a599417bc90bb86ecfc5c5549a8bc8ab42fb74f9ea8579169d49b39cede962c237f77cbc1a3d151eb11a501792f744adec8a827af06aebf225926ae150418943c5834709d9c39b9450d0d5c353fb41e0b10741f2a965ad70d34b52e005df63d13a6c36db38a540f0e97849fb40e312d07696e510d19dc4a45991793c97c96cda1a740938e2b84c372bd3edd29759c81b96e1c15ba8f2e55f939a17a3c9207a0ab03eec4c7cae275735402f4abdadeee2a3342f93687d1e39005f6b422ed4bea478ccb401c0925083123b8e2dcb77bebc87a0e8a78dd29692cae111ea1ae9a82424f5598b12bc8ae5259c114dc5d8bdcf2d119d7a55184ec3571e17953a82add550d98fc9cb052b7cc91e0bd53a907269ae9f273fe54287d0a5cba6b007370d99dde0b3bd7450349ee533a2f9c00a538171dd350ca9a12e91a5a3457b6b160752b31c0580987bb47e01113bc470f5a29e35689b2cc88a5fb472d15aec2ead02bf000ed0612a5eaa2fdc0f265ead45c10bbc77ba194fc3427ba3c32a7782b5810e8826971f23a2ce0f38438b8782d561c94168c4af2a615aeb25267f31280c8d20617331cc2ac2ddbd0c87175b8f1c4e5460719a67146a834d3b10707b0e9bc854c5c509668f5870bcb0cf6b97a46101ada4078508588466fc5a9241b584b4e2cd408c0c7852c4c6880b0d021c383e0190a62ff5ff7141c68f8ce9ae826335d88d96d55a36232c85ebc60b1efd8fb494f891722ebd6fd7276e9d24874ebef251bb8fd43f77d223dc03e24c9f81a45d4fc101ba6fe0e842edfa3f5e17eda296ea203cd9d6509ce038270fe8b94c96bd170e64a5114094b509367c2ad5bb742d4da2b862459dd84240eb070d0bdf4b5c54af5c5aed7cccc8266bfdf4ed93678ecbad5685d37fe7ec37df7fba16d0f1e7bab83edbafb1d3f06f3b4d16d2d1834db284743d9fabe63b50a830c19ec69db0c0cf85536dc3d824323c6a011232ab78fa43d88166796c350b6a36eab5b535ed72279f068648bc1a31159d78dc1a3117986eece23f8fb051f0e05dce6ff45f495b77ce5c6b8330867b9dbd9eeac0fc553b83a2250b85e711028fca6ff2f0e5b6d64b1c26c77574b6540854d946ab029254a511c4c65412e743169a000003e051c71c97e8ee0b5823d1ea280e2636242141d63446c89c002cc99041210a34043011a1a519293b624d29c151a9dba56a1e7ee28926cbbee3141b1763d1ad9229a47c2de5027fd634949fe712406652ae0fdf9c7901e6de02282c47ff96a95486ebb374fc72433c9c454dda7b3e6baefda75dfdd25f3387b4453dee3f70b0601f8fd58394e5cd9ccc2ca58be55a156558df6fb01f0fbfd7ec1e0ba51342d06d3a30e1e7b6ff5f6ddc78ad22722ac1a79ad6528e8a1f3a98dd59f4f3658d9f9ffaea7b007d0ebde0fdf2a5aab187dfead32937fabd0555f9f7f3e55b0f2cf38cc53e580aaeaa12a2cf9a76a4e9585fffc4bf528ff527df9fc4b75e65faaaffc63957f7b1351f1f9cf3f54543eff50cdfde71f2a0fa73cfde73af977ea78aa2cff4ef5f24fbfcf94987f5368fe9582d54b09cabf20ef2b25a45489ffcf3f52acfef3cf470a0b29bacfbf0958612ea22e4d80fc7fb3af3f549f0d7f3d6f07358bd448e20ad42c12d8e6a96916093c1a8774c6219d7148c7c3d13f6e8421f8c78cfef1a207feb1a24488fef121432c1bc76e3bce9ea171b7096db0feff869a0b75d6a4d22a1c9618c03334ebffaf994a31e000819b95e6ffb130a5c15b81f262eaff6f97535fc303121dc0e5ff2f2043a3e84899aef8ff973f8e71742859b855c1fff7e2b0910357c14fa200feffd6e812e46d4b95337ffeff5e3915028ce52aa3f3ff58875a0d5157505c79f5ffe5052d8f548511f327d2ff77910103eb4efad9b2f1ff38942b2e4968850115c0ff739539198c292e2a12c1ffdf1de10196cd254096feff4e12b23024248b114e2ff6fb990d8f47209ffbfd4ae4aecb823e508d3ec976b65d913c7e01ed5edbbedba0aa875c6dd76d3c7275df6df4c78d225a0685da6446801b8c8a2469e94eb684b4a2604c160e3c34d11ddb242931f4846210221525de20709ce109d104a840b1dd50332a448a2117940871a00312b12759c15ff87150e3605402654e4c68bbee20280ac5f08e0e48a85205a64a0d350224a0baf0b0e47864421c5906183096e86c519115bb27e33cd103411c91ba28add883126a00d031c646c3d80aae18396a30204a4b19ad14a69a54299517610b51059e98f4529506b294e8ea42c405426d839c48f5179c129314a4d1429ada4e2afa4c1052a00a0ca24bd94229117dcc447958d56912dc87063634fc59e009a751769eda70b5a85b936267ea092ba44b13a207354e83ad4b122c973888b27486541e97893c355376460567fd4055a6972212933b2a7688ac582517ea8c075890a6548243e97420ce9b510bb651d4f160cea6186f4b3020bbb0ba50c7c1d2937d05422481429b8b493df2230486a4d0810df022cec0435149847a96a702a444a28e4820fc41621880959c9fb083ab472c18104a3d62356c40803292f4f059c3b321a2acc8185b0d366f8ce688800312830bb49045e480a67886a90c12852172feb0aa7327079042ab70ac71d243a8a9eb921128c44c1250ed490508b10614ad2eb20c566aa951e21476e44f95072614fac4060f173eaf6cf5a8e0e86a4f13119fd2ffce068d433a394e5cd96ecf13aaefac7914dbc6b4ed622b7ecdaf119e3739e32fd8ff0f799f270f313867f55ed3ac86fbbfa8d48e46762d6fa36c4723bba66a54efb87fec40926d573554efb8b696f3b87f5c080b22246890a00602fd1dac898fe3ccddb93acc79ed9173d58703007abee10000d3c887c4acea1e40e28a70c8f0dfeaa600fe8d757b9afe13f9d64e35d56aff18d0e58250895305e1094d43b73f7299d0b8b823335d909d17969a0f875260b0467d2eaba4ac7290a21f5c004041713bc8a175d99c88dd0e34c8be6762f5324885b9466c4cb917383040e9a80b9d370323d52533ebee7d6801b0060d47ee940afee0adc5d0657106e8226ad575992091705be9c97b3220717098872eff874f58a9874d89bbe64d83de90176298c19e39aeb91b5aee9e376365161eecdb729386eb7745d4a522e596b05c61b9a8f02295bdca8497f268c713d082c67f61650136a0819327f1a5ee97a8a0711158e2761f7c7af47a30192adf2e00e05774522b39242bd7151d908fe04dc895f48b542af1b5b74f0538ca080750c154e620749597083d022bccb8c2bab4282d956a89fc8af57ee45e70f5f861c07540284f8f460a7c3465f2931125e907d0e0051f61dbf303a05529e9906df8b14275bf06c397e7c13ef090b250f08a9cf878577aee6e5e0f2e9cc1c8d8d330d5020f800478553d808411f843e7114911be9fa41f0c80241fb804588d32acc90e4c653575dfc010bbe4623970c79e48979621187e1366ea81a9b0e34a5034e52aba93c07d40c6e1521824e13a7891a4749281ce1dc000f43e4173e67ee852a39cd428c4b75dd6e04b01007f586150090098840b8407fb8d2eb4793a4845659c2e437f27afd67dd1e2149610c7012e069f44bd292d5db808253a75d7bc05e9699073e7faf179784b233476a16905d79919692e900bfee0189a6c5c30583afd1949155c4564454a203b5a7da71d81708e60d7fd800243c9068c9a3b810a5c396b10a72b4814963b8aacd295c26847598648bd17fcd0ab408f31778aab006e1f2e666e154ce74fe1616fdb008dca1c951c6127b1c67a1ce6fce05daa8e6b8a9994bff2834b2f960d06be440560c9072f79175532f2243a21af0d0ae77d222bc50719a2ba723a70fc1d2b0f0f01a4026e2552a9ca30b21cf89f55088b1df35c3fb5a06039450eee9318672fc744ab37b402ef91cd177c856ec42523a4bb5b262180ef44aaf22704a04a2616526c24cd778d121101572091ecad8204f6b26aa8291fb160f5275c59f0b6ee48708127d0301002aac79084ff1464d76d23888047134c7409e8e4a3446487c5d3694340f944bd72e3c46a802910937265254181935bec9a3252f7a6039d3008297337ce13980b09c80a9cc1499da7152ad815e6069252ca25d183e0881eac6182b28b531e2eb7d3222d2597a452f7c52c09be8cf0c3071153032f29d2f6acb64c70836622ae893a119411f8315f87fe53829001f60c64c88013f072e58751346119696a5d20238694051431f500446a545a60db8173bca9d193128a169e22a7d8a3e586081e7bf5a68bcc8d0a4eb2a50c9b203bf13b4c3d5006727376d57c79b76d13a52722460d135105e3da9df8cbb5aad12d5ba4a964591470ab24935c1e8310b80be4c9805dd44973cf2e3cbf86c2d7e563ea0ccfca02e12bf05af16e4e35d78f8c0d2f8bdfba67da60ae9e81cffd31c50b03192162dd42ab7074d9715a738d9736744d9064a044606e458fd2a31e0fe228e4014199f146ca22375559d7fb28d42a2b98d0e9c19e84ba59b4016fc2c19727928af05fe424c1a4405cbaaa36b8282f3868f10e301cb99b15a9674327c69b67cedc387e2c5d36473e5d13a7861ea9321ef79e69ea81c113c1079962a2d71ad1fc2b06887f0005ff9006b1e89df942f43d260c299f0c5a74590d8a2b01f030e4ee9060155ef416e172702079115dca7011edb33f9393e33f04dd8701f4fb05b0b8294b74d073e9408c613439245e820b5a65103015bc861619ff7892e5dec910de07bcf095e4e6002e0d312cb873da20c025b70450e9e8c1a81e8b4aae6e1451747a9a5855f01aa65c79252d7a770294fc702ae8f33a3c81952b4a0ff7139055b8919889fb07cec48f10c5e18f165c28918f543d041468b89a9ed10f00aa808f3545c2753068d0b73872f82fa5f0fca23602e04f93c6ca3d460af7813d5db0026f0f2e22075a7a4b505dc1a4c0886230015302b8c323065c76826efc71d200f88529f4de0d9a5376b990ea8618d400feda50e75a9a70f772c75b6f6b8215df8013850f41c448492a109d17b1c78835ec09e0cff894958958f3390d10a33c21bdb94012b12a258de9294144608bcb04a71ad8ca0f463d46c13df8010843f79c758693a9fcfc2459b56e2855834a3c616c7c29ea5e109e6c7c1c9ede22a34abd005450612f768a7e6dcd9bbbc09d077ed05d9352d32b51b748dbe709f5dfa76910c096e4ee5dd509634336e2fc0f0e5f77538948778d8489d2498337cc848159bdb7d5f553186cc1152cfaf28fd2deb84cc9798159cc4ad1ebf4b570ad2c6d99e2cec25dc4e5813b560186db00915397d328041e530e18f7c86dfd9aaf2765026243ae2a38107c9c142a77c78b12b80b2d298f82929be78252ebfe32e879b3b8b9ebc4969f7fd225eb7eb17508c3212b00abb94cb0fac4c61d60d6977760cf892baa4fa82e186711601818b8f40441f981e9d4cad10b63d79fc91f2f659c67f55a08e30e2954e533647050b662fcdccb03ce758483c8f55132b9ac861470c36694791186b4bc9808ca3c20f4820b05098d12ce24dfbf9ae5a4ec79c53fb400975b0382991b05c7e5925581c37da01498de9f4c63302a3868ba54e2aacb4acea4ae2e1458504a1812a637aa68ea2dcc00bb277684ba884218ba8450d17923615cdd431408ff920a16980e00b2bbfe908563fd0cdd0972662e05086a5c35bfc0254247cebf6ad1e90b4529f4161e482b17e823e673891158c30a47b8c957b204358005655957a1fce0e5c2add245a894f007474903ec4c39e3419c8bc77786dd58527587207183dda869e09a6ad4e372323ade4bc498bb51b0b7b047cae50327e78661a080120312542f812c82b2b896e1599c8183f30a88725fa04db097a82d77432a01ca1e1ed897f0e50e8f45b000239184eafec0c1858d4752e14a64a46e83b9b6976200b15ba88103ca1444f23092f30b533a63f120b8a99b674a971fe0ae15e6d3f374df1220e241a6a03ba555efb63925e5f1ba54f0292a387449f4319652bca87903eea294097ca68b45d2181ed207828f83e3c4dd4001a5f762d57655857000635942e4fee16470113d1ab8d004f001af2a4576331915bd1d4aa1b85071817a0a01c4c039ee20f09764112f274c486e1b1017600efcf122f418f6c2b5af7a26ed2287387a3b0c90ba5f798cdc208e380f658023770515bc8f75e473b5a8cd784d6761de501a19e589a1ad1fb5465f4f47e3e57308f7c4ecc171f55010c5e58747262cd6f5a577a80e808b80a0240c00c882e4fac113a607a5cc710778ebff070cee32d202e1b1aef286384db9954484ba629818cb4848c63cac4e596e5ada93cb65a8e441a0f8dd4a3fc1e799eaf084da5e7d1e03f05c204814c078077cfd95081628f91c695d5414f0f0035d2ab02d38922e57250a5788044cf7479c19170957965e0b32a6301de29e5bc1a02b8f4ac172edee00fa77f4170b8a72ed54c2f46f4e31d7c1830617469ba2fbaa89dabd2088a8a765e629b948aaf04d7cb4f882556c2f04af16c6e263c84d82ba7b55609b6e4fcfa88f106bcc3d03740a5f122016f62241c675638cdc4d54c44e6901a822b88211665d3854135ca9c5b187e7a6ccfd0a11e5459102f3b1b0a8c16374a0ba70abaa32ac0edbb729a0ecbb18d5b99c641929316d7d8b68d973310121f142205170333989746920b05e3b2ba47e48911b8f818bd9050a0978152d2ac0ab2d453d3731afae9c4c3f4a1e6379ee026a46701d3920d19309a572dd49a300a708a4f616e0a1eaa18c8af10f6078f14cea7cba0f1c02f280ce50958e2058b814d44af10c0c882e8e36b90bb61eba4f4acdc205d4cabba4e8ca3e0df0c5357ea972d12952efe44f960f14c8c39351a1e1c29901c01da48342a9258f838f63b3c615e6c7d3278274ab8424c70c3f1acbeb9db1487311c07387abcd928a3b04561c761188d48751a3863df4665d46a7225db208067c9d4e9eca38bacab33a026c80989b0be9800330a44b256e04a5427d2c514f6e47d747b4c9c3656527447963805af7518c429f45c600d884295825964488aea70630dc628a04f0981b427a660d44506a8802a20b491e1a7803253a5c1d4849b83c12609f2c85a50f404e100c070abd7637a0e0304bb3de0d160d3809092d17882a49e5111f78374d9c9e2f8456eaef6e0a8ca5549b0b81844ca5580af671f0ac1f87bf5d12635294b788aaa480c0f758001dc067b28af428bc0954ded832a34bceb0cd7de3a604830ef7ebc66d6a834520275c5a6702bf2691e0cab9cd288f8e68dd37636695741022dc4a6a426ed8af807b0292147c0192403f298fedfee921e60e5242a33c40d4884b83c78f321540c9a3994c49764195ab4646809bc9d11c0c288bab920d84113778e37323d02a855b35c95c96fe8072468a4fb80ebf812f44c982cbb4f579b501d6be800b6bffe0048abf6237e9e6892457aa5ad1765f2930e716313a7857776cca567bd4ae04022a5c459e82dc3d2832952ba85cf0237474782d2f6edc0ba2da9e93da73dba4e97205a5c8801f58882bf5b0b9c3b060c8e97d41eb7323bc8080c199480dd681a6829e871375572c6d124ec0450417139122463380d8634eb270306fce53e2a6b0114e18ee282ab88c72b6e25eb0e8061e42c6ccd5208a979bec60fa43454add015e20f05a22752a599c427b209858949c7c4feea34b08c341ab8445b14dba2b1302aea48fd21502d58772485a872d75d054e2e0e0840199daa3442145511790077a2f0a615e0d9e3e1756bb067f2823e0ff04ddbc00a9ac7c0f0e901e0d870f978bcd4559c00d1ab7558f15d70347af04592aae13194aae9e485a70223279f7824d66ffe0c7768d58d0a10cd1c351a673d41200b2b6bb250fd12de5a0c81562684eefc51ee04dc52ad4753384cfcd711c2ea094c58fc531f27f327d5d0660a0957986e03c900a2856037c70d546a4dd3c31879e8805899e858e50f704e4c348b137bf64824e0fc3568c6b86c7c3f5e201ebae88a3ef2a4c62ae58ab54780d71040b91e07509c043bca7a0902a2dc99a81296c407b852e8dc15e4e90e1065847f7c2ef462fce0c9f1bb60342297c94fc416aa03c06213b9e4c9f302ce7cfd70d53c57e240de8f1283151ea2031e819d884e9e26020eb2fa844e1deea47ee9bb5aed29404115c3c03e67ec6083c8fe5875719800b07f84284283d35176e61018615b87ea538bd041fecbaa8bc40bf4f933b3d330572e0267ce4f09058ebeff130eb4e80c778e54e755d41b6ecfc1d94247748fbea9a184002ec82c9aadf3196e14b85b1e02de94871cbb4fdf8b13b194a190f3cf0acdc08ba33d0387027a80b96432e7b4f802871f59fe10f1054759521e0e522d944a29c0181ca35953c54e6654975ed2a50520a7ae4e3ead801898bc28457bfa6a151c986c3d17de00c0e0c2804acfbc6c8a2afe064ee03c19175b159ebbef9c953ae3274e41b688487a3ac053f021566eea02444fe44a1382509139ade14a6b88a66e00b41285a986848081e51ca00ac0019a58ba48bf7d69ec0ba6217fa25e10710c693232cc9a234f0954c8961428ac0dc21128d29e4a17ab3557c30006e52fd020f707da249ecbd8324a0ecf160fd30fad065314680b7a3520166e074f47d38b0795053f8bd3c34957254310f645156e609c0d41730c54aa9281120dc21cada35374853c9e097777b9f406164115918d19a6fd9224ef80884aebb0025445c92dc7c5c486d03a51c418ebe6c0293520a5da7277b91076f38a4e96a2a97caa3178a37c7e0f8202a70dc3375c4fc952c628f0e693fcd1328af06e775a3f4d05faa8aac9b25ced64d33b43f824865ca062b1ae16402246e964277259aa5df4de1094d979a8c36984eccaa9e222862984821b4520ed9e4157840e5363035e93d81a9baa3defe3c9a3bb43e4426b23f6320c54bcef2dc4058e8cae290d07d2f84b00267ec7e824a14ee5103a71bc44801ee9046cca5f3d5a664a0cd836bc01559f8003989ee1850ae309706153c2c1c79e52c211c7c24cb533611950a67789556ae2a04a1f4e1a542cf1685027e058b54658a1452beced8a71b400f52ff658757e902cf9fc7a283d06590b76f5b16277f210388aba2811ae5021cca7fa024f234b8665d48442c614c94285c3a528e659e1f2a3f06d559ba95f071699c49c2354662cc9d65ba2a4191e592c21d59b7cd15dbc551034f29ebd6a5bbaa34c1808b47791542c25c3d6816bd0f1c68f7c8154aafc4cd85cf44c1996ba34b966b000b4997810bbc4246ad79280f2e28d9e889f00fdc124f43029f7ba41503d72d58e4df54e0c16d8a8c78500780ba17e4b175c18800f57dc230ba35ee6094673a19ff4f97a81b77420d972989eb8190e1068f49722a21b9baf06d1ace709105323d2aae219700302d7e4595e1c3d648f0a0024871159815eb7d6d6edc1349f87c026190603d3d0ef809d29ebca54ee513bac2e90f0881e50b4d6952ee58b4e47d0820e3aa3854045f48d1a524b38b71fdb009d1cbab4302df64bf8838bdc2712cbd3dad4972d753d98f1e849f930baa58e335831aafe4b8eace4a375d379d505c297c10b83b2651c16cd209778f5cd75d318accafc19a51da79c1af51602ae1caae3c02050470b36888731510c1e5afb805b99be82cba653ed0f81f3374f40e7072624f78d34541b6e39eb92abbe4a424cb22b4a694967ea4c0460e18d29bb2c7ce034180c8351285ecde49f41802204997cc202f4fc893950b80a20a8fe7c58c67134707168288e06bf091f5463e51f9ebb384d7acf1f38efe447a0a22c13da1b14c170cc38b9fa1c60877aa2b845db474ca34b61228d940fac2ab1841ba7d3ad4e15af30005ac0145a41e0554cc700900a00b4b08dd774185e5a61813f75c5ae4bd2656606e0c2e24ca5e93375f36880ce65280d597f1ba82618cb0e00a8a40018f4130e6ded984e2b2da30e97661c238a84ac57330335312e023e04dc870556e4a9502233275010b4b74c0700eb0e2b23201d3954440081c01a058a59e3aa9ba3499f952625125c0db4120e272858a7211bd707349d5a8509a521f7d284de6a5934bdd18680e7ca60965b7d62a067e0d02f4f9981d8061c858fb2e7a465d4fb110b89eca0829f93020eb0a00830526ab53e7b70514254725013ff0094ae9a488dc35f3a7496fd0b1eb463131e47af4c50feee10781de8ab8df359166fd544bfe167a70f770ec60f84db15c3d9986225fc6d6a3240449c6ad00509bb7a026ee7e029a70021be43c8c160c7eed0f86cbcacbad87652b824ff96c794a802a3d945764d80e0c3a97d4042b185203b93f934512574fb2a314e0015f29d688c15d832570e1a516e00b1e88954818f0b90e7499285bd559e066a1c4ef2f0cff01f817ae42418c27200a24acdf20b9708a7fbeecc28eef43c11126a426c89f51702c2930a275796845f8015d913bf6e50a0fbab37603bd91c16d807657c82594b5aeb4ba858624ea6529b3712a40af70562104b399077055f86000ce6400092e306b0fb73aa6f93b46df7d62c10fa6c00f99373385121e32cacdbf6da92b4f75b8f16d234b180b28b42bc2d4a0d26d01022e08143b9e2bcaf037e2ceaf2bf5e1c25120764fc47af531ec08b8724a2dfa353958f74b011cb09d0adf838187a9bce1a709d72d4a5b6e318f0b068504799fa0adc242444d789da3075792035c5d2651acfc5a231157569f553a62b55c2c799bae10327497031d86720c1afb7791d09b800a04ae28bbfc901a402f152250fed3a03017548c350c00b2c5c56f5a3c728da3f7b24ac707d5f8b9833014e15a20932b7cea8cb0eb4fa83fef0151282e474c4edc37202270a18261bc663a04ea257101e58dd070548615f1713f0511737b2037f745a5228fc7aeba653229f0a54ef340446c4c6b0bdd3db3409092911d2897c8a817252828e983c8d008aff100e67f183174bf44c0f46dc6847903cc202a87b9130335a4e0dc173202e14d38c660211e9daecd60821be2c8839e0d4268ee141f467a3ac234b9248a14703903252e8fa0995fdfe43ba19450a672f4e4b63d82fbed067d2a151ae10b43e0536112f7dd5183b76333856f7588f45376c095b5547d7a5d01cf937861e232424d943d74c4b907a87d75b5482bf14a7e5e7ab4d8e45c4e807c7491a125f779fc4479342e0495c60bc2b0151c90ded02145784eab54b70b10cf755362c8c59144cd4d64a7784fc1627f80bb3ecfca025d86b102ebc6f1d071063e5db789667325f5062f02040d3cc00ce177c949507639207231119001db11b380dd4278f5424c2a711b941e5cb7c088e02e4724e1415a8efc052450f5ae5440e7cdd8802bc78ea8df404a84eb1528cb0da30657a28afb74fdd059f9324ce6a3ba35e83a0901760340552cb90041ba9638b9b9029055794e4040612a65105d237506fd1620a82e2207d2e00a76d66e9e95126ec0e3d4b73550e45a1046e40a2958e8810112701d0db9d2e5866c004ff91240d98a1325bc66c78843913874e5041a754fb19a74c55278fd91411e6e090a77f8caa5e43f3a64e50d3d91f57ade2294a4a6f0476fdde1394b70dcb42048ca1860665d551022b80758aa745958b9e5004f40dc592d585d25211461096cc9f500a012ebba8a70a9d424fc955cee0071dd51a372694469d4d5667fba013031d1d3b261c8257bf1a774b1e4c2bd1834dd235081dbe702286c8b6eec52b8e0ee49944979b243141e968626df40ed0136a3c2c3ef1844eae2b135e9befa61abdc704e70792801e0cb183170cb8c6a74e5f67cc097d1921291162a259a97230ee2c1f48a0281b938d690fa58980e7149f061edb530f09533e00add4381c8dc32be0bff677d7319581974433119f158cee4ae0b58c192cd9fb84390fc286f026317161f190fa700a32b224386ffb440a6abc76c82bf2324cc1b29c35c3f98085d40915add320b64384bac0b4a4f5c6a975de280cb6b4a90ab26538912898e096f00199e9e2c49b34a2b8f24953724907ab448a76c512cb91c78d02a33a48a707d78a2832d0811e68b201a71510028fd8b59b2ee201879fe851450659e1e3a0f4bcca5b21f9af0458434506668eb741958c440a904887e025c4529f560895272d599bb748e29feb1d117003c9f5e0238b04a04d84a95249474e2eab26bdcaf84b930a5a5ba638268950c6efd3d4335e841dcd273f3605db83f2d30ded78dd789234d0f818b33dc87cb8c5bc74d90e74ac3f2315aa5ba5d0e6df8140584c07b648cf95a3bd3138df8790bf88c95112060c235de7c2807d0d1e909785b817f08e0e0ba8280ace787805397d8a51103b1149c2e0663d99be18980bfc204c65d640bcd4b0203069f0dd8dcb64e6c1792051cbe85059bfe80268cae1db552e58e0366dcbba4106c27ca9e6bb636e30d01d12eabc52c5db145fa01e478dc4e6a8267126204f604c2f712e4d95c4b36059fd5991f3d496f43f00fad7b1d8054ba280858d31332aa011ee18ad285f340d0ff6173545e2800c723e151e2cf090c9ed7395e0d448cb70bd38c9b34027c166e29f0200851aea60707fcb14d883fa724703f39e2726975f1801f4302af299301bf838183ef6c50940f3e65c085147d292148f36021b52c05ac0ba834f3a6c6a19a640cd10c00000010006310000030402c1c8f4805e3019d9f0314000067c260a65ea00ac42ce79432c68011000100000100000800a700f8f44f749763af405bdd0fc5ba92eeeddb0514c1e8fc3df9b244eca122279d212954d994468cbc52aba95e133c64e7ed85f4323181d2b280e7c56d500abea0ccb090bd187d4e6d477c4a91554f5811233081909e6049417446045e2f8e198b8070d1e19999c78cef186dc84af3b9e97ddb5c1f76dfdf4d83f7aad04935b2e48bc525bef35bb0a9504333ece602fd66f0eb0582a43eb9d48ed9d000b9d76a9842c4ece1519f06ec1b5c6499927fcb10b92b381ee27430651d505a1956ccbcb404b15576b8d06a4cde37c670785ff59d1043908d55877a6b5da82b53c9d67d63b0ca585108df5dc0e97b707bfd3c38691715396dd5736b03eddc10c82df34e5c8fc9195cb248cfdd78cc40c9d8ab076880dad5b6e93ad904588e52f99f29e4813c48ea7cd2f1257214bd3424c854b080b20276013d4f83204e147ad60f965a8f4c049ef50b77ca8d41b0cbe8570792c35828f6cf6a2ecc03b6fba965460972648109d451c8c8fbee8d5fb2a0a5fe1add0b1d66b4dc95540506bbff4de448494c7523f6a152d7762f5e6756b0211a065fdd13de07d897e8b7b26db2f4f93d5e4e162bb777acd13be27df9211f4fa7f7e92d78bd9b52afd805ac8a8a4ac73ac31717d24ad92096e67b8c7a3eeaf4f36b58854f529303cee02f1393a9ff2245a1ccf981358db7f4470ff9939a8d383a773877ba6bc71152fb033b117fbf59a51beca5a08c2529a875832e9d8a83d02dc5784f9c56e68d36549bfb45168636969e6f6ffca9eeab880cde1809baa07b667e7e5c8f6513a2345227370bfbbe259fb8cfbb1b05ba6d717881b7701c6603e79a8c85d1dfac5abe23c501ac2e522779b50ab724e96bba46601343098bf72105c07ae913327eb0206cfe8a272cd43b877415799d734bb52d1f2139d39ba827fe34e8fd637381ce32bc05ec5b262ede020bf6c9e386e988c301acbf84e04fdea8a9d218cf6cb6fdeec069404568dbd99b087819e0fcdee6c1f5f23a0428b9418648a405cefb7bee6e7cb6718c129429757d791fcf05ec6e124feaef69331d08508228a038cdcad8944e9eb6f185d6f677aa0db0abb2cde3af6e44fde603b98342862d6efad88f00ad82f5ad5d4bd9c132b5fe12398e31a0417d4df922c65800e1a089c4dec6e8f25e59763e0c8e5e393b51b2e2a0a8710ab4c122380341baf6cca66f706f42934bf3b4eef8b435df904887dd4c3b4ff58ebad3475922455bbe0a1e8780cd1a401a48bc6079f0411174d800d00a85bcace1df02318b65b84149c4e193f6a6c01ba7a3170345ee6c9655a5045cc4cf18b8a29eacaca68898dc844053c51d36c5f92e4137bb570eb0a03b4673f07b07af6022a435efc69e4ee377d0ab49dc37bf567f6935b6e2ce7f930859a5751427ef938cb86b00e4df96f218a0fc7dfb668d76f27bfc55c7a94763ca8f946bc2801a152bf9cfe3be95747ba3ab487dc5cdb46669fa9ddd99d22cc7bfa1e1a52eabafc30755e047a75d1f5b989eac6320186c279a4e618b97685f7d09bd0032f07ffae836855c92449f4d4b622f25e30393b133eae186cc4d665a8d033b1d3915070bbe57736ed1d7c9b7e9b8e2e4564ee228f368b273c7967e75f1f002b16e64db13d5c3176e823e50afce306eec1516373417d8553461e4e42ee0819faf93592bb2c073e659e8bd82e2d30dafd755d1616a0f59ffe06c2a72b3683d8d1aa3cddbfc96f54e7135411df08f46205350d270b2e6387ddc5ebfd99426d691b5c009696dc209ced1e6cfa2795de6564e5bdcdfe6074b785304edcd7ba197a3a2e82fb903fba8e943b8eb23e2991bc769e637f52f7fc5c03b63e715176b660e5bb2a9d7bb4f94b23adf167c2c73cae51bffa8e340b9399e77cb18ee2ab2fa5a145cee3c5e7b7850f6f99c4a7e8a339629a87e0d30996ed11ed7ac5c7d81be8555ab14b1cd7b414789761436f49b27e1498d31b3127a004433da30742003f8986f9b0c54d7a1d54ecd8ec0449a8a685569893b14d7c5830c20478e6b3171b1b675722f09ea4cc420234daea67d36a3bec6a34be6f9b92bd6901eda3b0afad977df666bcf0250cf1873aca6b7ff697307adebda7f27df1b722af821ee9bc8deffec6a8e0ef2c25a5b58340fc59dbe01345ec54f56da5cd2c433812077e9bc438a71c18f48abbca3dd9c7c4a8f547be9981b8177fac38d87e7357e4b95d4d1e4dacac812020e47c454f61a5a6700aca670c6aba64e3b489b6abd3bcce08dbd1e918d4aafab7d0a1cc8d460ec44182f2305805772b586e35ed9864b304f348b38c15745238c1501aa88c2d16be3dad4863a83a6939706bff6ec6a734f9802899dbd0a982a3175f07b878f38bfbb406c3bd6e69ef0081b8ea1b61a0f08ecdd2fefa4b620b2c8a25dced290a612a8cdd0c5a171073272e2bd6ee98cb12d7e8fe9148cb465e09eb14bd8701b74d90cee6399ee562479fc5e6664f8aa613070b4e1a7555ddd4c46785b85c069e6960cb2c202f3e495e4057d49b5ea8a9295da4841d79ee765442302718126e417824ff32c06b82c91d29a75a95868023b6125d09a56cd6cd7838295f3ac76b6b10c5ab930088f9694fff220f4f0a4361b92c560add9b16b7e438054d71b74cde4d5b0de37c4bc6557430daeece45dafe09cd3c7f0b7a1c37583cd39c39f210806b78a9c29b7112eecd8e1b59d414e4ffd9cf0a3e85e788c7b5ff2256d3ebda0dfb21d8af80f62fa566de3e39e00c8f0b7b1c60499457710a05881cfdd61ea0422fd6b80d20bafbd925eba7f323e18986a5405e1d7048ba9a86411ef75ed5e74d773f9c2f15a88c0597db25b1730f566be49f6919de82eda4f89e3fb7994f6ac94fe0c8efd9cf99d85c3288dc16f3675410cd08e9512d6a1ee1ee9731256bbde9b4566aa5149d647df6eef5e1eef2679fafb90692ac6215ef629f2fbaee2481ba00b3e8552265f380965f92ddef9fef4ef2d3b77a8ffb73cc0b9675c3d77a9652f1e8e7bddd755b01532b9dab14c9fc2ff49c37e07d25a9bf3adee26fcb6f2c68bb51561e630ab8b37837f637a3e2689870b8e977eda59a93d8ad5749cf1469fc7c7d20bcf735fede7b8b759d9f48915907ec31406a690878c63c27700c9b8f50d049f5d9fce4f6bd4eea8299ef665c65538c4fe3aeb93293e1e8ce26f452d2c6d30631fe1b28bbb1846b95de42946141d44734c8235fc6c17868b350ef343f05243a5f700e56068b85ed2cfcb07d83d46ee00ee25febfc613fe9fdc6e42d65b350d6b018e4128cd7b9163fd33df2259db38d066491b04cc7cac7dc3b77414327eab17a24b1ea2ae6b1cc3fa511a2a6800a6ce694f6d521e4ae13b5d2f850f4cb9017c8fc73796c72710aed409fbdf9cd2dbd8a0312531ef40b9899a10ac1e950d19839e6a566ac7e69d639e9229a131782c1bdf7dbbcead0e3d1b799765ede6658a6a8c88a82a7014f0e9bc5e535bdb07caafdbab4dc3f18866cfe80e0589874a39fb0f62793946b60857fadab34b201327b6e4faf88d7e84f27a5bc4c6c63f70b4f6683e15f5c1a2dd9fee465a606d833ce7577da91970ac811b97b0bdb92b15c68d7425e0857e84e344c27f0bb9ec3b031b3ddab9d1e58665d2402085866ac39e6674df73bf21c18de0da3f8700f230e1cb0df2bb6fb6504fd2ed981d3225d372d75dcc54110a00da6cd66db86fafd326d4d743c7b9ddacd4b516d6a0f21e5b3302ba80fb83366bc7e7154e10db1705c211a5958411755af089b1be189d0ed9e94e70b200e3ded256f924c609471d2166819025882568c718c58f9bba18f82288e2de2e96f5ea1e2755c6ed4821278173816b3426909273a02e9d5f9df285124fcf99799472c78c8ef5493c918de5ebd00758ef107ffbce34c94c8f084cf879685d9f0919ab0131f00e38f8fbbd19d54b6c9f45c1a80fcab86c0c401f3bcf8f5a57e0167dc3f5e575cf543130d5f526957d1f7424d60fcd3d871ce4f6f7f5b5bd58c669d698db8dba4a9d5dda3be5a12cba8f6f2858950fbc91b84479149d2f20331047eecc3ce07a347efb75e53b8bccf495e588d1fd4304d419ddc384d1344d31f71986aba9aeb019457bb59d99fde26cafb7e72779292da9303a33d8833447530cfbe1e96fb76ad3e55687a0911d62d9b8435c74869857138f920952238c4e0718e4a93208ffebbd90dac46a4f6d7b795dd11ba9c4c9b2ef2ccd803107b49efc5f701cabcdf0b2b7db631311a780d3b38b151c4a4d079c160c99b03a30a9afe97f3613d976eb419055979129a7f19afada077676aa3e8f0c7765c15781e0fb608eeebfecf41c32aae93d538a100cfcc6057d42c3fb23ef63d2a6b71c3328f51a1a103bc07198e9cc4ece678de4e8001f192422cbcad68b459aadaa9c7bf778f7bbfdf56c96cd2aa038e9c6c4bac2b1ce1d3e5f87f36072ed0f40fdd3d8697967c3bbeb6788b15ed638e020308b40df04cfbac05d24f6d03f78c0bad91c99f1f47039fd3f11dc76f2d9951a40c9c0926a7b9f311082be1de42e2df1c91235a4698a6e42e4c668ca4caffa9a1a3b1b9f63d634c823541d1dff14c289e7503df70ec75e4e3efd109a6aff2f78c78d8b185864b96d2538f1e6f7e8f40c4dbb97bc3941fabf48abe15eadce671aa44696b88710f55f41dbd69c0583db875bce0b385f53ff768dc01d6fe1a048f86116285776b1cb05620dd8acac31ad93fce7e55417862fc4e6497e7bae9e52f75cb9afe5f52a6bf7e7810e0dca5f7e36ad56fe953a06aa6ec024f01ec919b767fda7e332ed7d45dbc7d6d8e84efe827ebb0119176c0a686e17093435114eb6e0ebbc112db2202127ae830f5101589cd4d323781b75fb75014cd870d9e7e14a6da975af085b3ff9f4a471c1b2f842a21daca19175cc98e761c9ccd41e5d12b4abe0f8fc98343125b1fd2d6e106800add5cfbf05cffbca6f8bff30c330912dc568e8473acf4489f9e687abb8146f69dcfae331770a50055ccd334e77ae197d3b3936dadb8fa6c0647c428f9d075ef8b829b5a3dfb8490c3b4f4c429efe4180b3817283a439a9704368f1879823d9855030cd225bf23b5b2664a807442259cb492b0d0b3928b774c7369a6bea92687f9b133b6daadbd04425835eb5fdbb77dc2ae9103ea797a8974d634f40e766638d5acfa1013c491ef84202375b0a494115582c00ee8c9f560e01722e8d3b9d2f1a665e636ea0f4113e50ff8ba64b54554e557e23c01c211d321af93816c06693bed1f09ec72358c9cced90aa0cdf8e344fa519db916d85feb0eb2e8b0f6fffc33fd78ee3d60a56dc1fc1f8289fa4941ce4d3f65016cf8539994dafcd31a042c74e6e473d62f024be09987d2798379ed16ebbd2522c8a8e7bade5b9875663285f9dcdd9df590a48800777be2df8de1a0d4457deaf6839c2adc24e887aa829c73b59790baf57516b1e98f5bfed69e569a91530e7fda0e41c307d3a19ac18f26b5c93277b3419900aca76eef6c5162e52e6435c66c7b32c1d067a41c8b86ff8daab34b5d7982a46db43b5b4a68fdde760b26635b6cc2f156b322a155d104208ee22e076e7763ea02c2ca190b6ea0282c0cf9381cb2c8bb28ac7fb5c611c267dd52e17a6962366baee02a8660c3658a24463290f8bbf4c2839e9592dafc4ba849ce17cd8ad2c0d742303d9a2056350d3322d0f00d5e0ffc2d36acba7b5461d16b0b9d3234b4b226cb7dbec9bd700b87c93b7290dce40ea37e91b466d9637aedb0ef6d7585b31416c2329b636cc249c11b3566149efe3385148b96cdd40686caaec702e4f975e9e6ae1f542aef86c1200e415ec234956661e1b6460027a4a0ccf7cac3f808f49a0f1284115dba82e3009fcd1b2725c4ab00ef787be47ca8484b068d181a9f1d24ffb884c963191d3c866c7b8a178f3c6ffa07b8291aafdebc50060a3a35d6f793294721d0a84e0de3b09b191c0431ceb9b6760086f8f6a9a6862a184d13352e8585a018daa5d5f6c10777d66d80fd5cce982fbc9275b724ef3918363b4ed8ad8912f5c1accf8b2bfe4b7d2f9985dda70706317fd27988d493e126f4575a69418645684a9a117f04fdf5ad6581dfd15dfb534388948e75e307d80324f10f9188c488c0a930f83166c615d5329359990e0bb9df991175ff6845379cce92a369168d02d279102ae0fc6e10a6b01cea2d9964105302742a090c21741f0bc073e023ac1299ae6ebb80feb9b6e1bc106f0ff13b6fd20e24f048cdfba1a83aa8fdd4d3f87718807bc9eacb604a74464213c6b32d86bd964b23d1e1db25c0dba01f2880f08adc0e8efa286225ddb7b8e30875bb4c78e8557fc5784f44e284cefd15df06b22e8aeaae671b56a31d6c6ebcdbe3903e015611eb3fae1d6980863f9e301a0fc0a8cd12f4817437b26a674dd5f70ea9979676bdbb4676bd84cc4210b9d1e9a5385f961fcf679bedf0fceb14e86fc59f98c8fb878ea1c3b319a09c30abbd511c23a31e39478e82944a46c032aab83502ae3fa19ac96d78b5820a4f9a54c519fdd5751ea0a93609f9f1a99cbaedb7e897ff5d4021557ec2b858b4ef24f7f02742439822068eaaa2ab233b93d5de9ea80b7f73999cb4602d097acea3447a4923e9c54e0838a80d04fbb7c023f504b30e6bd21e8a40cdfc472900235c8349cfcfbdcd86dbaf144c3336fcda7999f714d422f1c6d8e12df2d9cb76d07e5e068c4d59b37ea7879270c0408ec28d7a469ad52b785887798126fb018d6789b3cdd7018216a539201ef6a59791db84e229bdc2d8d4f4aa43586263c51b6d76fe141d845378e854c6c6adc94ebe0dd90383de4b4debfbf90ca98701f41b1ccdcc24944383c3724054adf664c30b5aeb5a831e5c9c867919113e518274270dbeb0be694ea0e867cec69ceae2fb7749f1e6f6d5faf6aa089fd4d295910f4f2fd005492769571536d4355133a81a778e890d3e026a01d164d9cb962e37c97e938bcacc2b8fc32ed4eff98a3f32a2ec9e0381859b1d9eef75435efa6aa7375c04db51549ddc17776e86f7cb789d30dd2dfde642eab63e7a11bdcb459d2d7d3a9cee37be23519a1fe307883f77b1ec20ef000ebea6891349e536c4f2c68779c9cf6ef8e7f0e86af2cfd439aaef8975f952158229102a353341b620ea1592552519c4b541631c9b7f2ef34c83b2ac239daa275f0a57f8af77179c07c0143f4f442b8b9596ab5c281a4667756f78ed4c964f5d7efc497a894d1307f3a739fab39d6f07ca33dc712f103c57a3825be1ae699dbf1406e291b3bcc5f52db5ada52927c03881f2682c1f1a5b687e9cfcde48453290a5958c3ab9de9e2247afc1e9d0aa5763133916e33f6935034473fc7c8696273298c25b80b42c3ae8a9ec812f86ba924f66dbeebe65ae0d348043700d01381ef4279eadb5dfc932d65470ba6c85cbaf65b8f04595eed9dced4250f82addf32675fb28d3ffb09462007cb59a8b4e34776c8a36c130fc09008bb9069543298ddb30c6ad0ba4403fb2b0cd6804f3ef6b1d01337de0774b48094212ae036a0471a240a1f4b508d0661b95181f262a17af975f6e8685981eaf7e2399332f5bb67c37d5ec09157f2ba146e68109bf933b4372280292be0dcf2c6aad6f16b996c6baae6ebd5fe70774658ab369d08005e0f4baa50ff5bb9b86adc3a5185abe973ee0e2d979796892834200b863bcde77be266f434c402d289a644005627cfed06b2a1b30336147eaec15e458f89c4fd9dd008d4c3bd729f9f2bc06c164db85075e3c1a65d218a88cde50a7508a07638e73f5351d250b1c416a615a862e4fa56d7e4d1257fdc377d65cabf7d9fbedfb4ddeec5af3ecfbcf1da743640f648f5fd916a6e605d2fceeb43908d3616ac107473e43b442b70f4a5da0a8a821fd2402a5ae5187f01a16e3cbec14a82c52f348698b56ec1ef9f261a2ccfbd7ed75b9128ad72181093a160aa0df510eccec7c483b1d9908790f29f15406f9d6ba34da3d67a7e0de2b278c12d89ffffafe41ba7c7b3207d65ada3c00341868558e72d39395a145d5a7c364096a173ef2aa8672088c62474bc396892290cdba9665167bebcdd971e6a05564af8a84ad77d0a51b9928798ebc7fb3914356abe8aa2b49acef8f06443ad9d5d503c0266d9ebb02e18c289aa3e991ff10e8c53c6c1b7e797f9b5e383dac898af001a1188209082c4c30486d44415cca29b8fb0384c7a8928f71dca9cc585d9317f546b537ab626b5758ca5eae67eee46d189131197bcdd82a5f98d50197ea3402c0e48983d903538839f36c49ea7651e5bac31a623948b9b831a5d27db18c884ab7366a92efb23a15ea626b233735067b81d0cd0e73ec1601ee46523a27066519a70a38d6a59a4198680c83b176fd38696b30ceb8b0d2e4667d4440f0a960570aadfaacd1c58650f0a4e743e716ba2a0f3c2e58171a1059ed5e3f18bcfc1547e3c86fd5eb71fb241b14fb97b8bd7102323f8d57ec52edef74bb2328cff10ec67c83e01c633fcf24b54c1f07ab7cbe6e31013ddf51a0316f53b131a935ee005e2d3f37e72ef45cd11c8c50cc94a118584b6d3b0246480b3cf8438a9bfd98f169b572fcc8116217e5c4b48d5713eb4288738d516b1bc137231dad232ad5159eaa01290586b5f53588cc58bda8ae6748e96f7dba5ed591bf5bcd7ab32a8b72da2b209888be1f66626198e48d93d987f4590e525133fec22ca36d116802c44bb4caa3deb203543ea134825c881438b0099517aa67fd108d00d661feb018a02ca66d94639b999220d8ee5e085a66dc161fccc9e2cecea472ec324768cdcd4a38a47ad4141a1ea3ac6ea031af42ac84eb915eb9af1fcc5f15a19b1638d9390989669ea69b336d5aa01d88492453b0d681cdeac08b722ea8cc17d4135cb6ecb13a2dbb34f584290ad5ccb2bf7349bd6c8c723c26348a3085ffd0a6abce6f9b20d39c05c1ca45201feb636127a62337bdda540ed5bf90d1543a70ae39b8cd4f2225443fd020f4f44c2dac0e0c90d1ff27bef90118ab21c19df9ce6b904cdb9c9b187d084849bc5d09489416a2d709ac83ca9f590a762089e198d48ce9b304c0f9973cfcd4ca571e02856903ac93d160ad04ebcfd96a3ee28f00042743c18939a531dddef4b5527e4a07c56cc6bf6cb3e39ac189724d817afc820a519d3450099623d3d66acd81a7d7d9d056ae18cc8a6095f0977bdf8d289235440321a1e81abc6e5e8b4534413a40addf060d5ccde470db94271a76a16f3e788614e0c05f947bac408119bd47e2f4ec6d42df44b71a00be50985315b17c34e4f049f3eceb808770a1354ac12e636996caf1ade8c94bb8bdfded7fd1db2c3cef9f77a5698900049f9b871593f148fdbbb71aa958317b211ce829ac9006bbd3e4186ddd04d9d962f17958976effda58e0318af7bab56119f97e16640e6a9a93166e72b001396bd6f0c97f3a9f6c5ef5043f72f44cf5165bf0cc93338f39ffce69bb7537a83b5892a3091b41e6ef091f1d3b936926146d9eea79c6b23bc85696f7491701c9341e57b137602b6355927dd73b22a2ed1f7bcebeaf88ffb15e51cda3552a1f85fb02deeb55573cb3280004e53a00a72de4ec7ad3eaf69a8156523d925483ca856300d8d86d8a758a04215b07e9fbf01788f08dde7251427cac1e084c05245294110ba79a35d35367fc74278f60603acf165d7963a642c96e0ba1f180afbbda5b556bb12b4cac23cbd0e5a222b7a2e32879c5378f558a001a23e2771e8448a86b661227aceb67aaf7c8c52a32e7543404514d7e12fc439cfc1d78574a129a9290ff53bde8c9521ac1bf2ba321efa6da882374797db5c9287628a90b70f3644292ea41a06e01d1dbf909afa7cb406a79ece2aedb3c20406bd0b3c613099395f29083209ae4b26842f6a0de2c98b091d0636e74f32a25c8fcacbfb796df5fdb18263c915ca208c43ce37670bdb8e23cf2b12756b57faa2504e9d5d2088cef971c9f9216f6f3232ab7642b01cfee358ba64246fff00706dabb2b04b45be1e6e5fdb75d2752f593a3a6fd975d26774c657389a81c06204b2c4ad3c13028bc9baa5bf85c8a720827e4fe66caf8e0c29ba0898f22313571fffea5c6297e0b2704c15e82a597e2395077ce2ec4fbfd129fcadf06c5bd423e9efbbaf73f2708fc46961f7c0a948ee24e2926af4151b41ae2cb418f8a3200efa21dd8e28b0c86063bcf162daff6ee4b12c556de56878b2d6a7e455c50a85587511e4b93d3e999255927f9c1443121258399ff822def75b99050fbe71d0f3b1aa54e47bb64eb5f9216fee66ad082dae42abdb97cf1dd1486ef49729fd897f067e633c4eca5554b2046b6c8bd08779daa77e913e4aa6327647c8ab8b7218847825e91b4605dc99f6d2ee0c9f93b29889038ccd0236d3de8cbfb41520cdd85abbd7d6f1361430e0e1b4b06b3ecef3501401754f18b04067d858bf9d6d36430ee61505cf5bc369fb9e4031b306891540f558e934385531a7d6038df5b026c2d8c2988ea8a3f1ed770aa0408efa0f5845b6fe68bde5f13fedd86e20aed9f76fa59ccb7b89d2bbc5322c1e009fdad4651fae4aed53f344caf8304a8b1f1760872b9cf340a22669ef7dae359d04a844e7026996c808101578a3e8d57d0dcfaedc384d418957a7e8bbb83e0f5f179be4b373b908a6a5f9113e418e1f443687fd057a318d9498d381337aaf3f1d1e859a2188892b69309a20070ede216254315a7d4938224b3a822fea4dcd4c0e1314df43f27fc1459e18a600e475cb52e214022e30fe90627a327c90ff903f697eeeb0e5753c2a63d13816d83ce57e8e118fa803337d2120ee4f1072975efaa493f94d72275ca7caea24930a6c4dbe951d3f89d9c8052141a71a83b5213d460829b3c8d1730420b51cea3d4f7d477f6f37ed50cc33cffabb70f2692a7688a27474a18373b5a8d750c8e529953725fd8fd8d40a578ed99a2691cab9f6c389a0f337b423f1ccde067705db3aa636e11807b601afc5430a2a599a58574fe9594410503b54cb73dcc2e30f5e7154d3669f99d633e1c941bafa10f7fc4776f1c9cdacb31caf1611e6ee2657452f8d3e142e5fef41d19297757f540af741c0e4016481fc2ac740d983617cab1a1dfe858e5e387ca2be60478b12a21ef0c91f67e6e3b151313caca2ba1457062b9f9f22c4a7da6b911eec273da00d8ba2d443b110b446b9ecadeee087d595ac65baa3b5bed644536ea347c451490ca71f77c8a2ff6e0a5d1635c8cbe17fa3eb844a9ea270242c580251e33b98680bcb8a0f1fbe3f3f2d6dd028fa50d9f882721634d7c9d35d06c018d40f6ea403101ce935609ccd9ba53e12bba09a323cd1bbaff0b35f07608c8cbc8aaadd7375157d31765545884b2679ea0b44e33cde486043f6557369ae3e6d0f343a9a42188497ca787facf176fdbb361b36b3905ac6d8a339598036cd4a458c2ecef4f8d2424f52b8745093b1dec5c8dc68a0f2bbcb66516735567f4aee515f2243846e46e545b6d5186c71f544d1ab8ec9d47f2b87f6a57d72ddb9309a272ed05fdb7fb43e929fdf29ced441cd7d595b2d33f11df07b88f2bf2d17cae83bce025829647ec1b0aa91862b7f41eaf1bed2c8649a91a3039ea2f4a5a4815eb946905547998695ad9a6f38a349330859a59ae79147025b102ade04dc07dce47f518e2e49caac4159decf9a1952a9a51db4107a701138b60c610a620790b20657e030060bd9679effc5df6b20735f057eb6f64b72137679ccc18cfa166a31a5e39849547b1a2a7aad876bbf99c82cd19ac0c254f1046a942519998fa836344140d5833fcdff15a99b6602143d05a35ce444755c847f183d991ca8ba3fc63d48c92c3459593e8b26a13065b83ce685b53c4352e4107f1e487dabf548249434c00e71b5a33b0a81c1e41d0f09f09f1a2624bde2863c7a3f7cc70c917f3b96da07a01d3fcbfcda7f656b80b59abdc3ca0111b58714c755253b3b43fa2b320cbb681999245f42b86f1762830f1a7f830bf3ee5a07a23dabc663aec968462bbca93d36d123d18d5ae8e79a5955a931eeafd9678cf2040e054dc0519a5ca56b16623b2895e01f2c45ec2e000e254031d193c5cff84a2461f2afcebad71635fdcaa13e42951b4b66d42e12f75aa24382be5826e589d8b47e111c762850495612efe49b47d4d62fec5ba04bb80ff55286a5018a7805e80f69cbd97450b51c1ae8acf6122f0524a8bfaf40e62aa5ee671280518b36092f9f230c1311067eb1e69a9c0f2461fdfad3c2e65796f95c4b60b4e4001180ebdbc4abcde958a055f777097c12af029b031f0c1e722d6becb4a0d025f4c4afd5ccb98ee20c9c0d5f0f2613e4fd1244289a037bb5fdab53958199b8f24c1db064c503b2ce193b7ae5e37d83538bc21c01c10708db133d677f2b62a5d0b152df13e5ab8bb64d10052efb5fc2c59eb844f50aad45d732d67ce8a0451d798670f31d7f8c63639e5e878cb066cdfeffc43dadb3c1ecb437b044cd69509edfdd855a822fcfe517bba6fc15e8790a419ede33873062e24154de84fd7db63fd521f394a5e7b706b16970281c45c2f505ea62e5635ef6c7436c3670d28e814ff4ed36772dfb52622f931b5771160828f752afbfff475b9b7049ef1262183c448dc027c5332b186e31ee4736499842b56187df18e8986f49f0a104b2179842cfcff6fb9aed74066686c2ac0f224a9ef8b30f41c5effc9177a4199771965531e15adbef7f076a3179a7cfa41e06c36579f1267fd924b410b6850373377496ea9e0d7bcb1cb3113096183be2933e7ab20b3d7fb2ab7fb679e33d413af992735489703c6946a47300acdc22687e460ef8601593d8851b972dc279142604493c649fd184b7ff24c056bd7bb0b72ec937610e892511db69f781eeb7651309bb5c380fca05852c4effc0302250c596bfac80f19805242388f0fd4c80379ae3f01e63d23c5066db108472b547fa734c3d2ecca5fb600ca760d3c0d02fdcc670d2b0b719e8c9b087fc56058ae7aa091fa3366ac666aa11dda6e10a992d07fc88deeafaca928e2078939c852c37e1a148bc3852b1f232f5d80439fa10b90a5fee52a3fea1e2e9b41180db8a6400f9ec32389d2f73f77e8640d99d6cfc5806d0f6a26bad7483528e6612ccbc6447b7829f91cd19d18e01b2cdf9bed303b95e5c11241fdc678e70b884f84d46de4f72ee1d8ff35e67f60024da7cbe13224fd9fcb9827d3a0c8b79f92335b677517cfaf7375a8a8705ba8d01495de830273387be248eb93005431a8c42308d9dc31ced755c5df150b8c570d9fa33f357088306c925734e740399b526c3b692e20288f4775892e56d25959cc58f623c4885c9dd275b557fa769c6065f426411a6a456726cd9d24090c0b617b27c601ae32a57c9ae0e6eba863b1f386692331bbe232b46d0557d49b117049dac12b044726d225263a10d72811aef3aa6bcf86333321d65467f498cffc059104499a35f6d8135f0eb2caac984576f2ce1669fed41f02d542f5aea48cc144e6f711bd1d12fe6e2334059696e4ff6c502e0b667789cf1be72e73aaa4af5cd9fa0430f1d3c321007c5304cbfe64130ff26a7feb75ef84410ebe90ee7d04c728ec8188b505e72658744149bfe4d9d2c2169301953ff06e608d73f1f93969562231b0b743d89e5c12cdbfe0389b442e3c5414dbc4f510033b82207937df805c2573a1cf0b3feed36926bf98fe37c3049f25db8781242af61715f03d7970a1530b3516b1d95236906fc82fd742ea161e3563a04996f056473e5870e87247f6011a337c8291726ec7a568531bf76e8f614e012b2675e0a0feaeffef3cbd040fe32f29117b21a9337f5ec495e2ec82d49842b2113c1ac4dc1e47c5f13f5e4de7615216333b990b231cabfcc2240733206a8199b04fac1543ed8d9b58527da435b93ce45cdea1f6fe763bb3dc0705ea8e70e788b3f7d7bde892bf8e93252a2b3f54700cc31d25e0763d509997fe89f1f5023da580ce58d86acaf58ea8632743b88c76f82e687b9244040b776284d499f209c29726614635dd37ba61add8fd264d0eaa8ee0b7d4ff1bb46826918e191c6bdab1545a56800b2355ebb3dda3219f1f642184018befb4f082330bf221c10696068087c10a8c5472aa1db1f1f1ce14d2912cc31a9987b02c116a411ea37047af78cfcebd49d2187e4d07c0f421e649abdd5d682892674924482f177bb5b5023162228ebd8a53a222d816ee5cc4a1f961569e5c06ea02d685cd01a7fc90e0d60a8d26a4d98044875b78b39189e7cedf28b560928a268cebdf440aeca841c1edbd23b34f81c4c7d6878fb78041183aacd3a6bb97283af513e5cec19f71f947b2c61f064e46fb660851946db1996320e69b0a5a9c7267dc19889e3d19ebb61265ac5f930c36e57fa0ae57ca1efaf1704b238fffa8ed6508c6b571796d1a4cd22ab01b277420e1c43bee40a7b7fa04e6c3dd10bb2b455621d6ee50c19fa6d2e7be65a17ef8cdf4fcf07f6343c354441e4466961dbdb1595a56d02b22b93a1e2b9e4cc9f44c1c7720f260f3c5979e49fca1bb6dbbc541589609081085bf5a30b9e76d5807d2b55fad48feabc4c693251109c868c4be251aab5f1ba727b6e6b2819d8161e7236096046300e3c1146bcb153733d3be0401daaaa1ec01973d6811db748b8ea36e95714b413295fb147b658873a5b71356e5ac9b152adee127fad4f989f8411631cfad92425c9a0ee49c7dae23fdf8bc2d55be9df6d2cecd3c8d2722a0cb392ac504b8ebe4531a60ac7f6f3730a6488267399db172f5a8c8179c425d0a0819fa01aef2607620787535fc9a91bcd1e0c8b1668432b918d580f9dc906f5dd5974a0b8603a811a49de3fad8d0353be5c3abaf64477ba2f02e08bae5621483e6746625a81798c60f466c852d9a17cb2b1b44ed58760782f02c180bc77502ec480f9358a95291f0e63a6a239055cac2a762ba0db06152d2238e197d7cd5b468da4f950d8f530438d22f8e052bebcaf8421b654fff2634449593edced35faa7c44def3f99d20a35f0eacbd479e4bcdf256b02c694ab5816b1088f85fb554b9f1d85b74e9416c9535ba8e28d166412c77a55838bccfd9650e46d73807a813645d75242763dfdfabd8d9fb543a1d5905812e1d2a9a75d2a3ad8743c773baab009777e1afaaa970b0b101a9c36f6c8aede02f6219d98caec7788076c438f00a87e7f809d39449f96f66475d1c21282b5c983f6a8763e4b8356ae0eec27c551d71d15d6774768d07d0c1179b74e0bdccf4feb3bae2d19fcce886c4306c2c7d3bd0fd1ef2e1b48e2453d6591e55121704b6ca220172163148fc7ffed5193df6103dc12e3932b2254f4fd28d66937c5561dd2829f7dd182be743527c6d8489a6caabe24d4cda21e93ea4afdeb3a4de1176d1437b12d870adeeee01fa67426b991d6bdf22d028359555c07fa9f449fb5ab37c5f028cc82286f140aca7858327d545c7d6111c35b218e8c53be5a922edc97b0dc023654733dfeeef8cdf57cd6c1220a5c633e58d6b28430912f9f98f41632d926987b2146580bd647141e4533c5e8cfb05cfb0c06f92ef8f2634524ffbd18da8c87a5db6eaff322e3937060b22d182e5204cd5026193d949a725e167dbd4e1836d1fc685f775e7ee13118649de45e6cecdef147e9877d9f40f1e7271646c33dbb7929b00b90125e54a05c74d96d0bffe5d1d7100f760966102b0c8b71fde1521c7f58e8c5476c334f20489afb8f66f0ea06813acf1bbbe3d43ec636eef46d64774be1148558a08a7eb5c526a2c1c872529a38ea6dba38c4a0dcc259ee566c8c45691650d72b0c19fd8e625f8d40839111fa605add53d56300b4a1facdb092593cfce6aa54ee43b4ced388ff88652e1a558c0c62b3d9eebfa20c3a67ea5db7b49dd860a7f891d2f3c32988cd537641ba7e021f48967684301e60be7751d49f8233d9d5dae543d0c50953c6876be9e1b5e7887cf4e9d8fba914cd6d5ffc3712724c4e2332e808c9c5d6c24d4d322657c284fec0ade482c934304c7a32bed65637f76bae0b940cdd8c02389f319a9048331b5d9a7cad1c91f67daae88b0f149a21905a301f6f334d2efb2719518c3baa138f4ba0e813714c39826cfc8cf8946398b6a1f771c1a75f659a121d330b861de5b1a843ac91ee7c511d4567808f7dca1efa5b9cfc4b6cef57bf0805111ab18ef89229c450ad5f9c3e82cacbc217fed18d64c51f211c64f3ceb534180155bc612accfe49999f3b9674133ca0688808f3814eb94bd227ca8d1e2bd2c8905e641a8f9a99faefe1c237018bcb8146fd414c0b7c77d3b3d2343419c58f7136fa9404a73f419042222e6806e97d17906ae80b5d54113bdc9cfabc2b0c33df58f393c06ee869f0cec4fa021fede753f09cde3ee2cb60c97d53f03944caa1efad2eab73f88394de4227445aa90b81d8a9d109c69444cc655c9371ac8f529d6cc2ea6e723c5991207c879e14b04cf5fc391211be1499e8adb7e05ee89ad43d2874b73194bca875a0b58392cca2225e90dc9e0a8244f9ae690d631d2cdaf4648ab0417869bf43675d5aa03bf1b63a81392978fd6c343bf604335ba6d854831ce84db9474cc70bc7baa770f2e6a591b8c7246b07340af04f8a7bcaf8eb0c1138102a446c563519814a8b15098f54bf0ad43867dc55ac9a5e0f61f5dd12b00cdf38b2429e59c8695927cc09f2409c701c843d4958465a04b52fb0d526d6853aaaa84ac7653a1a6300cc267c74854b881039cb7e132517777f8cfd5f19bdcc627f9521f6ef5593edfe429179aeb2ca5da375568643c77fea504fc07e471fc2c132d9809ff9c6c7a0d3331527739e72eee73046d767a4e2d9bc9a711c096abd769a58efa770d63176fee95fa45dc0c3fa88a649a573d68d4c06c986db44cf1ed6bf8bc87bc4f4eee6d343adefe0ef29295f72e60ae171a4c80c2d60c0a6daee5d85b0f32703452faed9326e62879e68cd331bba87092e28882e3c49baabc2a1826cb543e91bdffc2bcbe70d0c1566995ca865807c3c099342fecfcee3613bc84e22b9bb5ea88475d01b151989a26d1f451dbd70ca4fb774c3c20b2e6b91455716664a170149948bbd46d2477102d7235fe5b0e467221e9af3748088a53ec23e09f7556658efc8c5298989ec8b933e9682de24202446b879f687c2195af290e6b8967ad5cacacab3dd58bb5a5110bd591fd84d372752e2609444afb1db33ef3293845f7ba44782cfd4673416c6dcfa257c4d0810724fe0607b32f2061ab9b2c8bfffc4e986de31cd7623bc55740d40f84b82f421a89dd8f2ab9eb86e12bf46e882ecda12e62bb671edd1075e9658a35a8fb735e703790ac6090fcb21fc3eddd4ebd1dc563bfaeeb00f2df0b18a20accce13be378b3eab7a5b16ed570fd91e58c8f936886fa5975c9e1ebfe13e5678037d99eca18ccc69108e35276749677493d5a98788b1ddbcc4b77b2faa136fa7c6b81a85b77a4f32b4929cf632b50eda61099e26d791b226ff126c77ee7c832829509f0c7903148ec8cd51034d2141a87629e73d0f8b0e5c95d1d9771ac8213771660d5227a37f8475503beb47ca4c8cf8fd87315f7579d5e16e773166c2a7fb91af7219fddda9536a8b538a8b4adf06b3edbe23d6e01a7ce92460100e32d62d9491acf11b59258b9c1335a943147463bbae2c0b7244b17398b9ae8cab6b0c2c5fb84fca2572ad1cd9c004ccbfbc2543d8e0e42913126feff5c3bcba1765ce6dc400ad601284d5a0fb36d6309dd385188449954248c67e82570242af2959a8796e31f1e34566c9114012fdf9e0a12e429eaa2c5d085b683deee43af3458b85fe8b5f0a0697c84338c2d21646a5d41bc24147e1fa319845297e9a41e0860d7d61c20d5d240f5582f53b1fea723c0aa620b05900cf92fd3f26d5d20b41c8636359ca8e34164fea4d5cc1b647bf18569d73e12cb61764d3b042c31126546ea67243749b8f19a87042cb4a8b2ca78d2a13833067f6ce1e00e241eb16c366f36f97db0dbb0f5c33306d1fce7f91e2e0d3a2e232cda65f8186668bf32aa3dbc4987883fd0f0e7a45d06436ed559929ea8ca56041e9946f2282d2f3462fb59138c735a166a6f9b5e43fc1457d9a704dc9921d0debba2dc9e108d186340c03b4916b44078d8c8de3d718cb4add8f3f1306b69c72e770a8092d40b303232d9c51ebd27262c72a2feb0d41278ad4537777c9564f4c8d318b4a67732c23f056602ca11c5d6fa6af5325d229f651d65c4dd4b380957dc64ad71d22ebb96a4d5cca1084b34e15f5793f5517bebcd86d606eef0f03a6859da79554b9c504457412c8ae1c5b1cf1c28622221123feba0b75a7df81e8f1305b0c0f81342fd333059627174c21ab1bfeca65a6a8e7c9dd9e0af4bcd72f7a20a1f81eedf5f2ba8343e18dd50e8158664a3663a1ad9d7cb184316058e5d429fc03fd72ecb1d0407e2ffc1df3d2bbc00222ff7f288ee6998bd637dad43aff173479f5c670cbc12d5ef224c2d9e9ad6a9c0aba2a9602999c1dbf1208c878b464387db2c710b0ae51173340d8e86bbbe4bf8becef5bb89566ea316bf2a9a494edb4f7178ee3b1d95ff67e94e25ff5467b9612bdc5572d59e7d9a34be7ff5a6ae9508a02f62dbff681a7790d96fa1e3ba9afc9fbe5c616374b6279f1b1345aa51d6038b8a37f6cb56421f11981e152810600524ef3bf5e2ac6a0324f05809fac0174472309abda0aaa8171203ef9d0f90a971e0b98d23165e19d6f170519ebdb082fb129082c2fec6e0c27f4753a38097a13060e7748c5a5053444994b0870697b073e12e33e195503cd9933fc5c2785b49aba1178d6b1448ba1a9a2d9ea9c8c75e5d1bed171e5f809a1ff800e64fe4b2aa5de205b71597aea0508c6f3f527f543562cbe09e56276cfec2b22a6c5c90a73e1afd808140fcce9e5df7fcbded756c74a5b9039be4c53ecaed5f9d9118db00c97c90b3d848f3085547c55ad15561e056b8c51fdf70731f3725e32ca9b5d97ce6e69ac7424d749a78064f7bb154f83a01353f27c8e788dd6ccddf42fef5dd765a505942fd9a14cfdfc4ce81067450f0e076fdbbaa8b32c2ffa1ded323bb15e702f714abaf2e00bb3d2752a867153bbb0c2df8a21e6a29e4e8943b2e604674ea432b0593415a1935643108271e47f5d846832b57c647bd5560836c48eeac29c876295914941f67889441c7a6372247c49d4fd6671f7b66b25b300ceb21207d45359e672cf161aa3a4995724d0fa080d8a482e0c5735905e19c19b010da6b8df07ec11b0f2df2ab60debaf8bc8b6cb02bda1ac8aab154486f34f8b532c0041b2a97a60ef8e11112d0771525cb5c406afe48757a522eac89ec01bdc3122991fb1e5b22bf77f6f7938844f6c080b38d5475a9d97214ad9e267e1571acf0fc535ca8b6d5b9eb4ad5fb1a652129c957be7efc982c458dce5122dd75d846c52e2e454a11692621f9dbca76b4e6c715ae5d24db355477dee9e16a6d218afa96a002646d337c8187e9bcabb4de45b5fe26acadc925e00a3491f93c2cb5a52848e9114941edb7cf51fbbec6dd8174f12248bf38f0dcbe2964b6e0b8c7afaa06dd0bf7641202292deee043540ed573a9386e232bb6793e5268cfa127c0f1610c94614afd8d7b885776bcb1f8666b070e6b8a6b8cd8502521f8aae6adcb34c0cdb1cea86fe0dddc71610a4263063423e42d88b2388b88bd896479eb7ab267e2ef51df51229caa0a45b9cee64d5c632e1d6c078d52b756c54fa55504dac88e460671e68d541f295d7d162c4430b18ed24228b9373eb930251659269a805bf2824b50f655951ecfd84c30015634b048e8e1c57fef7333db9c2c2e82ee0dba9e23b0e9425836387b1ebba20adcd529185bbf9b81537d82bd0b073597944d5967b4e5cd6f0134775289964dc613493b038c044dbf49528722620647922a4bb3e49f3ae7a9cc23c5c37e271c91ecfdb94cbcb5ff22a242649ef39883d64b3cd8021d1606a5d08a3e1525a06595022882be0d1714e7dcc6e7035c8a98aa4b068a10f24463716d6281b7dd9fc5ffe158aa576f418ac6ae397d0663a1a5d7cfcb43d456089507d6ead177ab87d025370beb6f2944dfb01d98842e550db095eca97284e869fad34258011d3650d8f016b3e721b078f9d66ac4fb5b6b3bf765c13a58ff0ae9dbcf9dcd7b3e07ea3f9eea719c3e6efede95773687a33e1b4570735e6019117bf9793581a7855bfd13621fd900563539249bec806c8800b7491e2d2ada6061eb7f16bc6dd3f3492f84f33ca9b034a7623b738b64e673f6a13b26447f6b20b0fbe7b187083e6892b5251ed6f045d9cc0d74f09268fe7cc3e60016369000ad0952b27ac2068ae6a5d68046fbd9c61a9e22d5201fc51244837dd860b3a7433080d231d1c03653f23c33ef3e11eeb3fa2a86a5921a50f153ec296cb1991a1c33c487dcb82cbe50f1994d8ccbb031e85fa4929ad32c983ab43c8b808aa5dd8748e71530676b43f2f462edcf53e4e12e65a8de6467c59ef079fdc48d0cb26cbf8fc66c31c44b1dad04cd8d606ac435aae91de1ac53bc30201367c2fc048ebc02b147ca543c264eb0b675731c086b45a502ba310c996720e2709556f8b4192473447cddb62bf05bbe52f929016f5587aa073da6bc94b4aedcb0145483fbdff4fdf746b0fafa5a276deb2ba62c98fbee57b3ba15039e9f9a396c63c51f8401811246dbc7f3a4893f3e247526b3d154c1f2ce5fa13fa932b26f11f17b4740ff3de5fdc83ef44d42c153867cac049e153e82bed837c2c42da00dd0a16628878c5f0269656f1837607576c5d4f7d7f869aebf00258c5e13e41c0120ac3fc39ee0513380938ae60b2f0a1caacd788734d32bc4a55bbeb0603152b2603b491643596a277099815188b044021b1d26373e4e3d07ee06577c33b5752e988111f25c8101383c8ed70d3e0f7261ffb5587fda971779e643718ea55e3a0802556a3955a58fe8f065ec9540d0beab7997f3380dc440b6d95878a85b992101f47eca1b7a0b9e40c2e9b5fbd25345d663ff0dd6c1c1f99b96c995720dc7c121bf35d385f92accb033f9646d68dfb56c16b32fd53965a800e20c4cd52b0173fb2470de266228db2be0b2b30cfc41f68201201df442e0e732b8c0826e5ef5fcf43cde1c9adcc2e98915d968743f0cfc8a4f858769ea5bcefddf82a42306af5ea4ab5beaface6a845164566dd8051eb16ba75aa1fc5232cd6f1356c7b2bc94be4f6c66d7b9aa8aa19d991160b07f4d38284253375927884214f9fc4d2c99fddf5e7f08992c3a9119b88b095ffc9d5b19dc3409be83857b5e896ff273eb1bcc72d4bec03a9c27c2326f5698110c4d23977175eb6a8e81331627623276e2481affe5d9078e4c6dda5b6c115352ba235ef69816db9f074c18329baf19e48a7848f696d3aaf6d16d65cd90e28e75ccf0342e8b9cf1c9b0f502726cdec37b1ca8747ca3bb986a1b3547f7665a54a70d7c863bfe2294a7574f991f52e2c1a72ab86e557c683e7a9c2090d20d34f383bf2fbef8118d262a471898aed25c5f1006e7a780e0573e32184781d4e65285a020db68d849e5e693411dab810ac3629800466d117602c22d812aecda9195201cbf622eb14ef6d561b0d1d72305567a3c91bf5ec0da22b047d0abc97f255e45ae4d5650280a33dc9dc10bc37536b399f7de57c0fd316dd0e8a6e65b5bf151159fbf77adce8e89982d129af041f493afe6d0c114d5341b60f8134fc91a09f41a0781180f26b0d6a1c890f51919b0787fb677d6f325c8182b3f618bf9148c9fda19e0a3424a519beeab13b56d5d45a502abff177ae44d44f0a591e85fd2d6d1476ee8954b52d5d76b4c79d364fd0e148ac4aa10ebef90f738e0f17b1b92ee42ca00f1ef746291b69a31f8097679c183722be3725a6e2f5adc0e4e790380300c5122e6cfaadba289103a1a3044ce256156b4f12f80ebc31629c16b4cd6f6b232ee69b6e5d5f8c06fe912a17417d7166cf4bb7d802e2154ac86d20c40c29418c67813b4239d42897f0e03a70b402c7091d045fe9b90dd81204615c55fc65943f5d98594a8e688b537cf35049bb3fd3384e10ad37bcd9c0cac9321c7bd40dd0577a13c3ac86b3d74e5449f04af3c30fb961e60a2180889df60d44044e0f589304de994643c6e060990624e7e4001a27193bfb3a2e387791a541db29d3b608619d559650ebe81fb06c3511b6efc6d6ed7423686cb06037fc6cbd31bace6df742cc75c400cd69aa3c356d9e275ae733f763cfe58e1f48ed2eb3e6f6cba1b834234436e0fa804010be77916d84a1efde19bb8a368f97b056c360263098ef84905179e5b181eb47d43167f9d82848cf9e87ff9c2dba8bb3b6437eaa1f9314e8600b90328f41c8bda49d993121b784f12c3ca6198f1b0e8db59911e3ca474da10304e27de62b580c2160e7eaa002837816761e0f185e21e930484e7c6a2bb77152765884cd8f5b1b2e20732053101cba11f4a38c1c69e177951018be289c477e66898612783f873e27efb90658cb1ec1b21aa095075d3015fcafc10beac1492e23c1a532519ce786cf1fa04f7ec31c4b3e74166cb0517ff4d6049278c9900fd7afb367d78643eb02f57e0e3b8d3184ab13f1aa3664d508dc8f531620c737c1480fd5e88f3b68fbb7c0944583f59ce1dff8b016b93a0b6927330f78dde54846f154cc1c0c75f277fc4d6d050ab5db19f5465cc0ad8b94db138e456459111b404641d1e21968dec144e27ff78a3d46169dc908ee6dfd6ca5312d0b7d566c08418778e9ff107fb002e7d14d69e08da923f0924a1e22c23f7450471d09875ecff34dd8b0dd505c9ece73bedf101cd7f78dfc7f1313c02173fa266667051d54a87c57de3406fc9830bc0709062785baa8438bc494e7ee3da4e274798690319b3b11d3467f44757d9398a5315fe6d060bcf1c2318d6ffa95cbbc2fb725af2ba8bb3c6bcfc2e1c707a83139ecc7b33cf8979d43bb73139999570b413cfeedc99301f1bc7c2c64ccd18a376d2085f369cb04a189361312d1a93d40d3d8064bf56e2574e4c4553c831f8dc4c87dbcb647612a4c31d0bf7904c10494afa58cf9d94b3727510995b5de34f6c352f1cef9b7327cc5c77e19c51dba8692d53072809663180248d04011b7f516e10c6c389a871a37806565af079026c2c37996ead013df93fded96804b52f3e482dd9344247ab855d3c5f749af0454aff800813d70e115c1aea85ea8cb8547c1f7e65f8772b8de3169e96d2b210eb184e41ae3a41ec8129f6b01a58373e825045ff7e402d472a29f306aafe50eb18406ed1d8a45af9bb2bdf5985fe27ccedf5d08b260632f582ab8570d97fb94ce182819a7a7c515411f9017cd9eb69b7ab4da0149e9d2218bf19f8b1425f472384f6e4e14fa530fb9fa1f33da19a38e2d43eab245b05b83d6c1ae725fd4e3ec364081b152b67fdb77494908e8a9b4c5296b48c6598a2ad70fb13d64446b7710170f4ef0396a53251ea3b4025fc5edc9395af00ee903390019fb35138eaeaea9c0a1a8487d42018f7702d0fc285de0e6a8b35bae98e6f22bffb5d3603a1afad51cc5dd2f0cd82a1b2c80520901d620af1896f4d0e105ea4f6e32e842f01a5310cc6a89d91fb6cc9775dc3d403da101d02201ce43c6cf658ea9480c1995d6a56e13f339c83372e2c3b260bace76812888afb2c55f1f9d286447ab769215ee3a208edd11addc16ae56361673f81f5231d447591e65537e56b8d3c0135219435db439e9db2bad2d83d9da7fce431b478cb40c860c7e098417d7a38c3ff5a9087aeeb4c873013730b7463f4ceb15a1fab9896f10fbb7fc8ac1bed851a8f8cdea4d6f779c18d88605b925945dfb63887af8597262994828b23082e49f8578c07abf175d4f8ce4cab6c18defb1af91cac11eed41fee13efe2664fd561feda1dd26839167e91fa3b7e20be5c7ae07a27b35f86010814bdd95e42c40e1f0a56ea4896d6444d497c85e621cb79927d1a6e943245856eb0923706c1649b640006655d4c947f728ed8748b8ce903df8cff4b7a19dfd29d124785811e77715b10262112c52f2b000d51bdc62fefb12b390b4b2ad48f7a43306fb40f1a8e46b54268e255a1d7635837432c5639f895ea004f78207c5a5f24186530705fd4431d5134465d0482b070589aa1547a9809cff8ccaf2e2662c4bdf9558547cbb81d1bd01a27942b4a495505aae14f1d751c320dd79265185997824a436d3f17818050b6042c5671074db91463ded90536d661f4582163383aeeea66e3408806c39d98e61b68190f3eb3fb757abf515d860dfa14034e692fd4897ede16cf695b069fef462b0d311ca4a5e2f985a9c4a69d5dd556317c09ab051963d9d3351aea408afbd6d66d9b48ae621b131b4635ddd6cb692105f207f4cd88cff110797486fec1de7b11baca04b90984ebd47c13f8190c61de1c249571189828cb907fede328f62cebf2e5a12cbc56be81a405d3672135f3f36abf1582cdd72a4fad252bbc097d71e3d487b66163570529cb211b5885b457634445a67b8f49af9643d62f2ce81c1c452b2f88c44e716d80242f872e659d025dce89bead3a2a65930418a5672f5530f60fe291833522a6879932507e76fea139d272d54b9747c19f7daae92f489ad7ca4365c0681fe02d4bd2d59d96c1fe22297e11f3482bf5f2ea6849f4b65fdad1158469a43096633474f515d36b62146701f0317e53c1052cdeb9401dc469cb8f80d1f8fc47107b25e65b5208c9833d1000bd0bbae87bd06915f086bfb31e387df5c212946550d2c1b6873fa5cce6941c15ce452bfa24a837c14e4444d9f057bd418ba6a49f26fb3824583779e018aa80345baa95e26015ee089d2b250ac3f439596a3c2258447a632bdc6f5f16d9cd3bb9886400c5ce3f0c277f0e1489ed8d92776a3b0e96b79388ac145b52b82eca9daebe2d84bf5a4a91ce7beccebde18dc229c468c69499495c018b32c7dd5c9363d9e7cabfaa0145318399e0597b3b48cd4a2740630e1535d58e94861589830528be681570457a4047922228ab427b9dd4c1b33511c61edb65cbe0c27480c102e6145f965b5f720d3200012e691a86678aecbdc7eab39768937e4c7305cd378800736a96e134a4b2711e3c5ade640521c83b598317cbc1d58914d4da80b8b6a12506f43d2fb914fc096af5a8db60c5f7a229f9977bee3c332bbbd9973a73628d7bf3772248f8a252e66226eb39b513872cf6cd6259c2bab22e2bed17223c0c1fb920a710b58d9bdc52294d7f5895604320cc49d7c5caa8df979b2befb9faec1493f2b023f6c74d82d8ed95f80c66ae6dd3748effaf197a46033d1236ff82c3c0425134bfabe62d157fa1b3164c5be6afd18e67f9dfa153fb40cb2553c76a64bce76d906ea40e0953dd46c30d7bca3a89387fe17c3b3b7fc2563e0a1228896472bd3a5d86cfc5361d2cb0e1b61170fbe4ccd2c88c387fc1f7002383cc3ac9d37dfb7296670965211139b5fccd3dcb2e9394806fd5a931cf64a3a6007579d890dd8bacd498b0e4fe29ad484baa0aaf373b132b2c20764a89c976296a4aa0624b624fccb971b07887750a77507831ce0260c20830d8195a4d155b8551c5d6aa03276d4945ab552dc47733a3b5fc96cf2cfbffb824d3b73bfb842ce0c990c88efdb22eb52c8ecc4fbc0fe8f607316440ac5ce72d8117ac246745e7406c12fd4826578d6f7b43b067af4d69441dddfa05b3c92e61fcfa2e89501811fb081be1ebf27d0e983ce9f45982b39f4e93cec2e7820c778f38810422bcaa976a0c5f896f627765db84b1d5fe83a30484fbaffd8337194d65b166020fd5f807a65f26c979c2dcb07aa30ee9250f16b65fb404e6889be3523d5a209ac98a4f3e7379fa30da10c4582a2959d4010b5f02daae213197d7b034f2e8bc1a99913717e3581700367a0a95472cd1f3baedef4fece20eb3e4a75739a6423dbc0715c8383f09d34e16881d38251908b9abb63ed1674476f431c2883564251f4d5fe02c0f1a24beb81710113944a14975ecdcb4ba53ccfc8baf9061c1516f876ee991f911f37b0532d7835ae780ff1499984ebb96fd533ae210019181e40c75570f1b43851a894206622b2b26f84bf06d7112242f234e396942c60eaa79295032077940eeda896cc66cbb9ce054441a5ee2c4da122e21edafbee0c9d35dfb6f92a61a45a2fa196c57978dc6644b62fd0b5f5722f6b777a586644c8c8e253830c314272705a498e138bdd090ee3d4bb328d71c08ef72b5fe341d029cff3a2257717163d9ffd37d4643ba22e9192cc2cf9986bf26c8037f175b11511daacdf913fbd9150b1e7b9cd2ed3d6680a9a2bdee2bf91f080f201f5e984b68056c5804d7261663bb8030c2883e7ff3b90b88159f61690883a0605fc41b724db8779c4cf657b8d7bcb784a35dcb0d551ab12fa9fbebe7e32f65cd0885a4f42c477e7b308951d0c11af81aec91cd5ab08bbb8c4d0175ca82efeb0b09d686d8af03ff7a34207cdaf7cfb1b3779fab2fd3e4c81c879ff50a2452228b850a29c6f8e7fd899cd680f16239d38621844320347bf4bde73e8e3c77430e198ad5cf6b0886294cff0c8ea7e977c5cc5052428a207301ed31c83c6227179be293e15b2454cf32531965729b4b5d4277b73e6f7a7978905f4993ecaf0bf680d0b21fe38bb6c8c3f560fae4d1bc992c29da8afc88f1bff16d07723ef044a7b0870f967c9f02208ab01f6de6c168e6b43610c21dc1b511c99611873559bd74a4fb4be4d78c9f02d1a941586cf210b601c56aefe52c1843deb82d85e23930b773a8ee944c00d9229bff074801aa5fa996daa8cfef1895228c7c1513f91852cbd608130ac3c2543e1759f654b49edca8e8de05b38de4a670c5c5a66876509f9af79eb9db43b75099e92de0c1358f73ebfedc65b8273377cf816d328d15e61771a281b5c75e1c22a5c1bd253d14cdf648049e17ffea5f58e3811ca610da19d22065fdfe011ff8cee945b97110a025fbb8b5103c450648e8ca9350e619ff9b5c4eb0e582594dea6cfa6861379842ae98d113fdba5be12b21ac8272c5e4bbbbd4bc304b3d2fec1540a5112aa39650dd7b61e3e1b0aa322f48af9c03ba4720ac208eb3f3fb688a2a564b00cced782178f9d5a1b05bf9ef664741cd89c74ccad8902c2225fecba185ad58ce1391900be415a823047b74594385493c2d9cd81fe117c2fbff7a8cbeaa4e86d2c2e5936f6f931bacf794c150f7034e7332ce6ea6d661c4cf4147c35e465578cfc88906dc487e0fb3506ed5e32e11855a8e6cc0aaeb85f8fb466c584e0e37a7e364b79e25261a73097a16c128a2fd3407095b1d8c5ab1109137f387048d8aed1da3a4e9c0c140524d517fce4e00c7d60cca0d268228c991eb5632210d8c0c1d6cda17b99e48e3c35eaa67d70bb70643bbbbc301d93f2bc0ad4a210c301f000a8cf811e7dd05e649d95e9bc05fbf316794dddfcf57e0d7414b4c29b371b746d57dc963eafa670c3c63b3236db1970a44fe4f382ca74a71bc1e239ab77d1ab23af88522e3900fe7e213dfc6f4b64bfb3a0bdef3bd4d343ca78ceb960ec9201bba6527382ed6c0b6b9f9505aaf07a3e3aca2a8dc24063a5569f16942f06c043c76c6b3648195100e3988cbeb2118d2ac2bb59e4138ffd0551716d52bdde88470722f4a754c7a278fabf788a9a7bf9669a9af5bd88e9806875a79e25b0802556fde314041f608d62ec839b8ebaeb912b45f44705b6184aff6589a96e19ef61483af2dcd04872747f87db2f91f804bd592340e13b3366f8a774db9de13917e45504af37628baa0040445435bcd40286ad35dd90e313984254828791e9df2619f60abc1fb6a4ede2ff004c27f8a024de2dec8460c513a18ab074846c56d828eb649cea80b448fea3c21e726a4e2b72d055875c5de23fd21bd7b21ecd0bcb0ae46b1d147bb105c07055148617f9aa778ea777ac17d1d6ffe6c33c3af407bccc10df89cd5c0cfede7bff6c1b0002e816ac2148b0147f49d12702eb531405198fe20c516ac3b733bdcee186863ee8e19d445398da8c1a9d0902b83c5832800a40f38ea96555c7cbea5b10eaf1cdf72838e422476712f392864e6251b1e085c0e37db29e997bcf1f31a61efbd30eba20a18cc80235fc5e2add935b3ef4cf9c1af3d2966357c032e5082e0077a908147338f058fe1b2b63a027c113eb2e9f5f21542f65c50132df614e73a02356950c216d7c9efb2726d4c077acba1576c1bbfb1222404a5eaaa722a57d864f40ad791e229c20c192455cdd13f0d49cbe3bbccf705176c62cb4df2b1a59bdeb987453075cbeab1fa769ae43787a6b704358b926f7ba15cef2bd73e6e6165ff6e249e78aee102b06742c6a636554b929d7331d696250ed78af1be428db542d78524549a34c95a4137c06a42d5ecca3f86e49a0e4915d82c21de718217a5b6c0169d7a32b485fa4f94ac5b737be0e4af6501beafa47f94b9905d66e44bf85dac638e430f05431569a96960c44736ffadfcd39f46a1dc9115786985d7f5a10d961492e4e36e601f52e5a5473c0b69a7d25b5e428e4cefe57aa181705ef30a5bc2279647cc9fa9b73cbcc7f979b3c6846f4f2b45ca0f4da305c5415ba87716ea25ea88c30b7cb16b5701b07c9c529b322ae258690cb8fe49d22a50d42368848858fe52697cc0d84597b5aa43941c94645e9333813b47818e0dd2b860d319e2502d6611ec5f30a24d175774058f710a8638cc0ab32d2ed75871270448ac6944b4c80247520c37854f447c62138efc4011b8147d4ce3cc81402cb00f6fc7edcf988dd19c2d6c5ff89eab21217e55fa685b501d5029cfc3f507d7ed9b1c04542a654899f0ca42210a7d0bc91673bf07f4a8d6b2e5a69fa7e43bb2503b60ef3ca3bb5062186935329adab26dab340812c8c0406af3e95573b43d72b6ab5cc0fae8081b2501080dec033e80beeffd068bce5c869de07974fe8ce4bf8154d48cb46281e7baccbd0a9bcad4e6549e2903d155060a70fea2bb73347c043cf14ff409fd31f541ef1eac95307184d5f8d39e020d5c58b216998b81ac950ebfdf177bc94b0885621f48283de3433400754101106d60c7ebcdab3b8af492bdd65d7183bbfe0e5f8fb965edb9b4b19ddfc99476b7bbbe1cecf52422cd749b5301417cac3e790089a1271e8c31fc434b92c505959900738651fa784bd9f9966f1f3dd9828c805c366021c54b3aace7142d3ce4e4fb6e73c0eb27d5cc4664c3f11b59675e9ed176afd906b4fd8d446a414cacd52cd20ee9c0a1d101ca9cd8dbd98060cf1d334ab447222e0187119da45a60ee6bfe2bb075635b2072b757f912a2ce98a84487e54723f8b7b9166257623648b0ce61949af4c9f95a9059a25d9e6775e68194e1c0f80f1a4bc0c99700eaebbf9e0be34d087cc2cdef2b6ca79b95bbe14e6ac4872a6d157d2c336c86e1fc3eca016a8d9d88fcabdb65679deab64a10d3a9443830a773e505b07463b21134d5ba99f6da930818fb0d2ba53972c494835f2074f6fd4766c1fd25c90221bad0af44d52bb52e2de2879130cb80451407578296f25db287b706a90913e87c0548b4f621c71359057c6dd4b9394009145e1c065886a80619783b5f609e903e0c53d358917a08d909c48320ec5edfa28601e7a9ef33d70627214fb30b4585bfbdb7b18c644d4c3928a39248048cf62088e70ed3703594871fc1449539867ee4f1bb84fd8c8ecf6dd4c771c9604a50be81aceb511262f8b824223791a91ec5bea83fcf0acfacc425853236f950a1b18f3544cd18579dc03e931d5de4cce2608e0816533087157c9e24253fab5aae80717596467c50c864785a6a8682b95304796f59f0e4051f9b4826dad9ee3df5df42c8d4b50c3ce68aebb4b3f0f5e748f8bf89c2a6f04227e8d73980e726ce6884e62e90fa1eb17b56bc3bb63e441cae769b4d9395433b7669453b187cb3fb26d04c52689d3b3f930c6fd2cb1d3f0cd8dc20d5540d6017cb9735692551a0531c704222da4ee79c062798bb222c98497bfe101512b19472017a9dd11851818591069ae8492dca315bed15f4356b6dbf396d7f05e97fab43051e7483d309a17c9692bfda5df2b0c8c88caea9559590c762b5711069869cef8a0666c004fb137d3394ecd0db216a9cc5f00477b71dfd20410f3c548bfb63fa8c2dd3c60775803b1d484a1cb775972e928c445796ffe4cd00128841f337200ca1ce214a24281dbbc757cc90ff09d5163a80d48dcb044c39df768c88e9fe5e1e2d8da32e4dcd76cb857a1ab483aaf4a866a54bb6563be8259bdede0b9bf9a152177430e6a5950c602aad7a895022f423b0e3002b3fa8aca8334a5221cd549a343bab4071b9b20933f6e385a7a206a377d39106be58cf98525ce4bf52a1a95914dd6b1e55268051794f98ceebb4399ad7d7e8df5ac78839efe40758afd245d4becd4f827c1488cfa44102424aa6511b1fff842447e873cb2eb7077708512d33a3e2c54df3f71b9d9a2441472dff2c8f07bee5f5042d4b6e88aa74c972f103592101cb7694289176dff5b69d467cc191c97892da8ba594038098368e73ea5a3b836eb867092001a9994452e71b93f9bfbad7fd4a523cf84f3d7f5d2ec7bf1558320cbda8bba270ce49941970e36283055c3c26d50aa8bfc1c622132d7238fdef3aaa0535e2aa2102a50f66cc8f1c15237ffb5876d0009f1135c24402672b553eb958e204a6e29d4f734e98fa85509ad3e036191524f5caff15bbd5af48b8a10cb18e2567e5ebdff7c52996b824acf25365e9534c01e83fc703e80260ded40ef150f4cdbefda4bffc145c73202e0c61437bf87dd2049beb8ae329bedb1833924ace4fcecf1d4bd3b24cc693cd3e5f11b4a75bc19f25c80d6fbcd5b974c5572a973ea0595f46a81970d8d53953836890d239f041461d3dd921fca5287c8ed6926043bfadecfd486f0f4253af1020cdffe660a8c326ef745c3e3a37ed1701392f8c9713f0e025ab122b4f365e31e0b006f8d560cf438fd42148a7057dcdafeda29e0e9ca5e4905c18e7c603280b08b9f74227f67dfe220231956b367a7e22d3cf966d185381e5dea340ad0d42f4634a8d2e2eb7cd9edadd8ba7b9d25712cc9e4bd4479d5fe332c0df17a4b0656df1ce016f0f57a94a8a1881586d05b1f1784fd0bb07eb21022c9a27c7394f16b81b48308df47a80ba8e0900f83904c2a2f37fb67cccc6711dcdaf1a983cfa6ea088863309167b083148f030350b7882e70e99cc417af7c738c7692e44d8166f963ff72b02be31e6caed2a592bdbe7b62de0b9519d083a479a9456ed5b1793da8e5527fb93eafb28fa77cceaabdab30f1f8aeddaf86b7ea70f69339d5b275426c9e5e61f260b6323feddac66054e61b895f8abbc8cbedf3b7aa77938d26200ccf81b4312a4cf52ef518ca47c387f25cbd6864260cb9deb9ac75d082a0ca8b7e88f6a05e610ec5bdc9b82c21a84f9cce23371c4aa0c0fe4fa13a3f786159971b4baf7234d86f06503ef57a2ba89152722f24f9d597ed339b5f33c0dd14e32afe348653554a90b3c7a6903e027273f1f7018a15bbf664c44d0ffe979a5e3b777b02287db0b2956d3a701f5cdee3c43cf8a696605a106c2d08fa79fc23d9ba70cca3547b8ceeca1dbb07c813308a9eac54ddf3a727bb38f6ed90086c18de8a3b21bcec2735003b14a749a5c80726930321b83cd37a674fced8a228f0f021ece8867df04978ec2bcf3ba04431eaf3949fa3eee445c01e3a5d7f313e9adf35071a572587873ba1bba20997804d7a71ac3505cf126652fc7b3ffb79dae0a1710fdc3278f227984b07d48bc8da0b7d12923049d1978bb0132d1d0f718f74f158f6a6911d3669ad88fa9b529b12c3c05b87dca33ed305bbc53d50b69d5fb0f838e193c056a3bd0b6975263945f46f53275a2eb2eb15a236b9151f3ea56b474a6bc563f1657ed6622e7afc0a893fe334d947a2ac050544722610558e8e86591ae57f262bdf99d986a2db8199226b4d9350c5010a0e5c94145e6fa14254112b95424363e07226ee35aa42517f7145cc18620c186d601de92b251f28caa8f03ac04e6b6ba75046a0f82b132619d948af5c3fe4196d38fe35bb6e97b9baba95fe20f3df5326e2ac39d86fb149c3830fbc18b9daa568955f0f8e54b9685b3eb42982cb7709dd4e4977492ac2d0d94ca0d88bc7810a3b9d93671c552e0b842f88a6f30f634709c3e88020c6c7c4cb5e9c0c79ad01af2592ba6b07f76e0970c2e1318316ad7bed3a674d762339a4cfd9f04746ff2d3f8d13ad2f31df4ecb0550ab206a92b09dfe04674074311beda15de4fe2cf918df8bad91732254c7aa25b38154d70a646e58f2280dc80ef1c1676c8793a5f4bf30935f19bdfe609a06be8ed71702c70dbb1eaad7af310908b889da0095f24be79b88d37bb63267ebbfdc9f982f704f8300a4d961669d692d5067e6061a84f8f26c809dfcc896d1e48b56a4425b4f125186de8ee094f8721cc00d8f9fdfe8015df79256bedf12619b9d3909c0e7f6608596226857cf4f4250d01c7c65c22db663f85ace22f12fbd75f9f94bf13a3f5f428538c634032a5222a5fa470ab8937ddfe9e624d59cfa57e801390db19a7682d756fa9476a45356e8d09612c9cba6fd830f7b9b335c69e0ea0f4ce5584468f5d9488aa28d845228786730c5952cbfbf8833579a1a12a291ea885293722809358ef2bb7b72f481254c8db58b2064fb59d4c90caf45d8e1c0dde3a9a18f195692540c34c31eae275fb844223da2786bc722f624423a5cd551ea22f02d87f0d4c677086569d1df27d8a04d1252db36d60392258a46f166b78f984b8fbf9e2e9db92e6339f734116997f53213917aab17fc175a768b19a814938ff8304b2c0732eb6f3250941c457fcde8bdba1cca2d177af63ebd401a7df3ccb935077fecb0970252e56abb15a557bf165e8b848d4ae1c420e1ec942bcec92190fc8395adeb56063a63a475ca6dd775d5c2cc1264b501f826e55f168637941965252c6ebed86cddd865597b133359cf1733c1e8c19fdcb6637ed1a1c330c0e1493859036ee9aabca664167423875889744503e4dd1dce8ea3b95ba6ac780d4054d893e310b7551f3165466ea02bc5add9a2501c96c8d4716a1983ea42720ee28380eec3ecec57f727a01b035a5669c29be20bb878a3fac6536667361d566fde3dd3da805932768f1cd2afd89f644dbfd34f547d145a7efa898fea931a44e432b04d849f4cd26017f2e062f4f7a9dc8c0379b8fa70072db99500a63dc1782a96cfc106de38563421cd188dee16467d046136351c2cfc9e24ec91f74214bc77f196070a0168f43a565a880d3df82b339fb64ca61870810742aa00e497a0f09db847bb03436e193d560445af6d5e525f43e4aa4ab122cfc943eedf0ede0490445cff9410d3b8076b4ad55ca7000d27f8104c4859d203e3dcd652c81404d8c100b7e4ef026b9fc84e279deba4e42ddec6c4e4ddbad1b2427643c8b0a56614b6cf62be2e1c35ee233807b1ccf70928f65b8cbec1ca50e4a0193f48e1486c79c46d30b9e48c9e393cbd63daa454e6af88bfc55a2ff11be937310b127f4de6df7220da02997a07057052c61f1b0be3599e5ae636d1a3b0b1b9d441c59bc1f31b517c554315f3ef69e93f0903098a4cf449c29d79ba7769e1b50933b980f1b33f1da44a74c46807f7c538cecb85acfa1a69626110d67edab05c739093cca92f92cf92e898c91f45b54e17e3a921eecdf6dd5a263f0e0733955511552bfeb5c7b587d03f57849646a019b0f33c6ffe5b147fe0f9b3772b57efae95f27ff0d39696d9474f022e207473fefaf2a2010d39e501530b8014544dc49142d70842fd016dafed1de70350149e093d077358674dcf90d1948c9ef69d0677d5fdf881a58a6ec9c337d949741e5a0de3a7779c267c8ee82159ca07ea7b071b2c98b72b004ccaacadb21ccb5f2f615cb2bdd6141c3d79befd9f2fd63e1bceef3430c5cfd41d7babceb92a060f6e0d566b9eab964bda2777dbd8d0717611cf43a029b3a3aa000368eb1a5321b56ea1acb6a00ae423f4231aa4ac7b1d9cbf13924bb0fea9a25b824f15c72f4311530657fa45622b3fdaa1e467ae834b8ecd04dd145ef7d617ce26991ed6a9d4bc6cad4a9e61040dc9c5ffb4c160a7f56bb3591ddb8776ad5a3963df185198f88119e4e8fa7831db2838bdeadeb66d2038f386c0bf35a047ca43eef5392f77a700b26d1bf6c7d4df53339ac1bcd069014bf879c653d2060acbc55e509c23fe9ed84b79c39c419a4cf52cc8db94e78ac48cb6b568749fe0a45d79788fa285e090c46bc7309a293c1b55d2ef82ccbde95c88d20f3f0284b5c9972bdca6870c8de755888a1118963d1b82814ffaaea1fa1b7dd02f352325dcbc89220091bac7942be0029b46a8c4cc6149b94a58d1e3de450b8f07f30f701d01c58f8cef22bdb34d3004ca42262598160e15cc1d7f63a75af85e8e88378a2bb1363cb5fbfb3f1e28b7b1882e20503659fe57a5b5dcdcb85873d29a3f0f16c3e0d0b23c5e6858a0191027b5ac33901da450aa6974a283202249a710d6a850d1d47eb6ddff25be568beca8438bf107e571dcd89c68d93ed9a839163cc1ea9eb6e5d4e41d8a5761474b12f5979251dfac11ffd9d189e30eb2a9b723aa30187fa138194057a422439dbf475c018c2e4bcb9b146a3ce86c55b207e9b72858d3e832f872c2b053f3af892b0b92d0f244edf4f70725679feeb990e176e535abe17729b5eb24be3e758fe7099fcfb6c42ffac3432317d38aa4322a7dc748f50c599933cbd20176f4879fc4f72480c35887f67c2a94189989f86f5b275dbe5ecc1aa292b9982c002c062a780a820084c73b1b00516909ff7da2f37b01754e38e9f3e1269a1a827ac1c0395287f7f3b4fe57beaafa78cb07d0674587b6bfd3a5a7371368dc2d12810d8796ef16f7c35b5398b4a02196605cda8547deef48bbb8ad14fe52db7f73a750dc0ce0853862cf44618e60f1b83b2aa63f6db6905e5a3691b86b1fd6d5e8c5d80ed27b70879cacc6f505b6bc1e9e96a383febfa351d7f2ce676cba62dbecf00a4c3c10cbc222b6e7bafaf3d400efbf030f1dd98f8e71bd1ecc08c1505325506d20868b37c5c711794e91c04f80f5c091f244944159e6f8b0d087263b5d4f8893263d1283e04689f9c476c45f1f16f0fca7055775ce24857c03b6eed64f1784c89dab8261eaf9165ea6761e3e8de3725a705b2c6637ca40ed03fce12f466eb6f65a4d093679a4c39d45cca5fe7d81cb26bfa7e02e717347e87b1bec8072eb4efcc6f3e9e444821fb5f379242d5a7c5c70af52c76104fa6317fe3f3aaf35dd0796495f348ad89df31865ed4e165b84328d522cfc449cfad46f56755b826d5cd18550d21671d1cd9dcfbe1a8dcdd711fefe6e454175d8458091867bf15d3ddd72e166e84353e414ebdd21f40d7ab1eaea4565af67d7bb08ec4eb6eb3b6aea9d3d60f08d3cd62eff57bea17651d101617ab3a9c46bf88aca7f83af3c9884d6d598508a3a2c921aea4d780f915b9ebb4c60c464cd550b6d65c79f0d326381ab5f4b4bc2f6e09de4ca53f69c7fbd948564acbcba10a0fda579fe1d532a2854d46299423bd6bbd4d7fb6464c4e0c5087fe6b888a3c6ad5826ea417c750466a04c93c8cf2a3456a8696c5dcd7feccd0a779a47722d49db21bfbd1f5f2476027b54ad1de696435a9f33a75d351ee4b646f3e6df6351b580ae3018fe8bf5429b347029a2af77e230a0f75314b7112b88cd85ee5708a0dfd0da15291aff12dc525a86fe51030b6915c0791276b5b617a6e8e8742829c6cf12ba1a749cf4af0cc275a0877dd3f20ef77edb076987e43f11a960add539fc8fa6b03782dd8f96e99e1f6d8cb06951402aa9279085b6ff55b62de6af59000152d571f5006d8e3e200824b9bbad2f4e0ea71d86fdfb34e803df8ce14bc475cb06721afc33538d204f435aaa65675f5030dc72687d5d0a71b1f865dd462b44abd167ff319463e5e0d1fc2a9a721b299c686237aec29191f4bf9b87c0ea73b657104251d35bbb732312edf2a7dd042a67372dea754fefbecbb436fd92184b4551bfc6beadec1c3b54b969d23e617eb93906c9bed3d8478da35973c018c2d812b7740c8307ab29088bbd01755a10c20b0fb0248e7fbf4e267293efef7f5048ef0deac0d8b45c8d11d45707eee7acd3c41c165c6a1109af2356626e7178b00e93b051bdc9871edfdcd3f0ceae38eb31a4ffdf23ae07db9351fb497f25b70c948f31a5fe7625a633a62f8110b5e29fbb42482041b60cf1ae1ea3e3feb5d44670446f5123ee9c1cd74f41db7a7bf0fdcdaef4e30cd39b6b965a84b66ac331b033c590384c66434e8e803906acf9bd856126c01cb8c186c270861948327ac001243903fb4e8fa832fce5d33853196632b6ba6bfb0b3fa55ef5494c38451a946c8e7768de33bd4da9939e546343dcc00d2a75c298c452ced1fa681a7a1e011c4157e5735b3d93c50c85c1698d2cecf107756c9560cd0a33fadbb9c7dc113805889d793943fae3d63b2d2ce54663f34bfce7596e7460565f6a72f4e5b268cd5450ef3a41ee6c0dc027526e2e0973a5e0f8b868fe1e693de20ea40e78b8dca6c2654938d961edf111e8370a27c0d1548b954eb59ddc1c255a34c814846bf72299108ad442d3ca8889f4a1cf792202b432a62c295f5f492f21f7a1bfdae98a7f250bb042f7a83e23c32f1d5275284c495d5d6d50a04e5dbbd9b941ffd73aaac9655878d7f5865c25448abe3865e8e0f00d3e8c70ffb0b58caad6f577dbcf7b9bf52d52a2dfc5ae54d3683796108397b3db26244a44df4ebaf03a49f09dccc4e2bad001634bae9dae366a1b8c76c2293b8115e8837774f2a2ba7ef6b5d90413662ca1d1e8dab2456482702d20f245a72bbd014cff42202894ba24c441f097ecf8e10c8889ee886af4247a02597847b57a5006a1ce6e9ecea12dacbcdb1bcf30f0ab216a4be83103792343b8c2c4979d761957a88b1e36e54d7db9122feb0f37458856f35a0eb13e46b7fad6e96e11fd170f47a71d9174bded6a11606c95beac033fbe7b4ec8bef87ccb4062b279f71b3fbb6b9507b871c7338d21ecc5d0cc0822a4c968b33a29337222f4e0306130391271e6b01ca0ba9fc4764478aae5071ee5e14e9ea2fa123c73e836cd43d79947ca362dad22956b690d54d04de5e8aafbc91de517ba8266ec8143843f6c50d7716749bbbcc00ccdc5c3335f5c7b61431f1c0febfbb74d9e15e4287db889c2392778af63b92742391a7931bec3102182acd98aec498212e2130c5b010557861ea08f829b7c57eddfa6fffe8efcc06b32a2a19512f8cbb43b00ef9f1e06fcfe57e68cf06164aa6cd0b58ab997aa24b26c665f21f241787d70d853fb94bad18b8d13e8470165de7ec657eed60178db249d1da49897a1476bb2809a1709c4491a591979d2e9941ce7fb084e7cda424aba07e10af5e7e638c9394ac9877c2831eebc13a46feb3faffef9c169ee7072668688fc223cf4c9f9ade0ddba70fedbfbb233d3465cd76d34ebe45a3d39a9fedd2c58ae0b11fb2a3dfecdb77f473ffd38b9433c8dde9a1d6de2d02273a215734d794b39a28feab396c65dda93ede9b22003b81c43beefb4a8057f6611ae6d8df9efa100ca4c135cf0ebaca15704e814b477d225f5d6fe37ee1091eeddd991a4f0c1294d8424ba598bf6387d47bbfa1eff9e4661befc3f151e3a8037ef0eefab0494543702a6b430f48c1eff38594d1065030ad318cf0ed0e0bb42c249715526c061eeca3541535f87e2b758fa5856537c781df83f10909ccfd37a847b853383c3965076bd1f7b9565d6c1d1d7df41c0bd545e165825665c6f2996566cf7f21e55c492c03a73789465af702971b91f1d09df25340f7361bd39fcdc787848890d5bf15a1d413a83fb5a925a80e1c562b543027aca4bd1d799d2d9f0b203f8a6244e3f00222c9b4784f574010806567532665831b5ad21f9efbef6213d1e3c44199c709d34b919ec0bde8db5e5b3424df1ddef3ce2975eac3f46f6b4291c16820adfd41ba35bbeb73e18fb50e372c2f1e1615ab6f63717e97c60570e7a0388f9b7111610d0c29a601a22896f8b43d39089c26bd3ab6ce18bc2e7c6406c7be20d0884f303d3af9edcd5ba2b79422148efa592a870dfb8c4fb45adabf558588015c3d3848589ee7132382cedfaceefc752fce7484d6bee1f4fc90f11a58cd97d65c3b91e47b6f492dde766638fb65bec17105843544725ebc0f82b6ae33e55f248ae701d423f958b9a759e6a328ba974f6bf7a94422c112667e58826ab6126cd0694ba3bc067f926314ebf0c1d05927357cfa7e759800ff8f244da2ad277fb9cceb1bbe2eda62dd2b841e012b8cd08f8a05e6fe8d8191c51b6bb8b0f9e294191d42d08842880a6377fd4628f3f1f506c5ce4a5f5a45c98cd3c492659d92e6111fef98f7515a7a355ca6a1bff4b5ad172ccc0b15f289fcfe4781ba431109c8e8198f911090786685ebd2e6c703375fdccb49197bc1b28ebecc53e9f48fc26d945bb7a3c5bb4c5b6596c672392ad52ccc7f0143acc3d523daa652fd2c239de35d0a50491abc2e53d1be7650cd099e71554cd02fa13de0e4bef7023318c08ef274c04fdab7308ad86dddbbdfde9d3d06157f0330e0ebc1b79b01028f15acfc4ae926236b25293ddf4e42a10433b713d7702cf8ed8c0e1bb6289cedbd8ded1206707b68de90e70c0bc57c4278c1f6d6c93231c7ae9e419b4dd057fc290ab15f06db315e0a8b4dbf74f86c78ae2324099400c2bcec8f3ac9615a199abaa83b3a323b20cd13e82eef32cb0cee8ad653a2485702b3cfe386893dc70d46174b645712912334a8fe7ae04dd8e8e25ea8b2abdab9fec2ae5bbad9d5aa54a6c64121d80c6a52854173e7bf0ee3ce28db5b0692b99387dba0872c143ca4f8a67c5d0771d6b1641bbfb57124563b1e7d61f95389f2e0562b0a3c11420a70e428e3ccb2c9a3a3e5ba4df8d170306c572c6ef92eb5c1922cdb45d44ebcea2430aa0ded472ed2bbd4882fbecec23014377e7982e303da3c2497db0a88b6320e586ad9a7255c87f583eb5f538926866ad6fb8859b68ca273186a00ef2cb562cad12862db6bf1816dbecab61705b47bdf2ef93b6ea52aeb3672a754fcaea06e2c589aa0717fd626da070f88322c3904bd60fff34813f920ff2b6a60435683282e832dc6dafdefd9459e5e60fc377f35301f8d0259b5ef22d621d0c6a31208c1b58f2c85bdc2d1c3248808f166e7b62cf4d6585e6f7f3066230c233b725346b95584bb78efaa21f02ce20789afd15d4a99f577d2e4f021333af43bb3fa0dd1e3475f8abddae60377341623fed0985d14670787148ee6b36cfa938b838b30027162780e1ee8f51ebd6f45b3de1a8e2b2e7b676c5ecc2e597ab2939bcd9282a9505f13a301c1f6bf377ac211e0c1b6808a940da991456273920fcef460db9a2cd03d7616afb50109f57d5047c7a3744477516d4dec8b6da60af40a30310cf0df1ae2588f49ba18ea2ddd5a7e7eb1baa669b880cd05996f86d1d105b856457121dc4021431c5dd1a82e5079f64acaaeac5d0c5c571da53eccc1558d7f5e7ace8cc626c106328a793248ff5b20db759559eaba7939bdccc1bdd4c4debfb2bdb573cd7b1809bd53b28bc420df5cb8420f878eb3142e407c5d63f3ef195baf82ac7dc9be03d20e0a92ea73160939a59b0f51fd020a237703fb7f79a9d6e587ab83332ff2a0404493f1f6706b9188435fd2795f462902f11c71f86a22d57536d20ca91852e5c6a1d3ad8f8c6beab071339ae0890dad14ad5a56480877c73cf9955218117117e2c0a1f6b5c37904b618ea182df28caf3a86f33986bc7299c6750cf66aa145b55183d339cc4d363b242c9574e99d8159cb61f9bfc7f66a3d53952f97b646b0fe074b2d7959ed86051927558b6ced7b5c6bfc4e9ec2925ec3f512f02e9b29d35ac3494b2c919bea99c9e7b63a5b958e5a836b3d8bcef72bfa86761ef6353473aad7b3a5713e5837a546ff62c6ad56b97be99e9b4abba9b6d1936862a1261763c60bd92d87bb5e63ccebec493ac98bb5f12d058ff3e10d86e6152273e96321f94748de62c26296b2f082ae3fd7d95dafd7258992f0b9113a5be6e227c9414200652a66c45051f1f64e09b7b24801910618851f676aa1f99671b42b3217ac7182e8ce4a4c0a086b0d0ea7c468a995f37bceef867ef0e8833f9d563592985f5943a89e4fdd325d50b70fb68ff6ccccf28ccaf818a57892f36360b647bc7716560a64c3a1851cf753c4427073c51a5dd33dba2d1ebff9903caea294a967b4789b5ca798a609d3f96ffd0443260a2a232dfc216191683df0904af6211a2b33ecae2203526aa08d8c2c560ec01d1ddd6fb7d4e48ea08d2fbde3932f1e19a94fd9d5f2db544121396ca0518f442064d40b221ce4eca4399d0716bb5cce388b10e91942a8a9215102fe7d53be71adc64a99f72179dab3f3e6005f9a4d08e2f0c68d6a79e1a16c880c33443881f1d44efdb1fb09118b25fcf2ccea177f9a8f7a623f2bca61aafd3b50608ebcc3cd8e2b8cf2a0094dcca609be79d1693d3010b045e980dc31e786200882200882a88d4025f98cf578ff95b53900006ea66d5b47de8d7a3d0c5baff62dfe37b2650ab7412d42d33e63ba8c9d3e9913a00d19412cbd6cc949f59df8547b41534be6a10655ce83966d8bca05168106643738a52447a0d2e40f8c606a3275ad0e07825346a547ca03106ac212f35b874820d6a5dab25cf8c644cff82af50ea33eb04c3a3fbac1fb9caa0645cf3d60c34ab9f2eaa240456059bdc0f4117f5c84712f009d0c252150441439b925650754234bf24d03ecbed8e9929ca0ba2b57c2dbd531a81c8eeb969efe51a554bed344461fd171a9e130353822a023b6c69f1a7fd1ce9c57853e161c20de814daeef78e0c63e67a4ddb9e6e645ea783c4b1db19e4506c417d8529481b7f43ace2a22d02f8b1636aeaaa505c05ad0044e29f8026ba088ad10240fa80b86d0a176e1c8b20ca8b91624bd328a156fce40b9e0912c54df658e9bef41067fe09086ce8cb4340a3b15b9c99c9c9bdac3ea6d9e24e94529c4e43c19f21c428ece5f9ecadcd2a7b79c6a6e472b20cb9017d9cad074504c831a7051b31d1f21b0148c47dd074a561e13e76a69884b4158aa5035164261d9475f799054491ea756b19b77c44b5af4a45ffc45791106c2ae5652d055ccfe5e72a21da848ac678082e4b4e834ba265667b29aaba31e92842fcf82cb757801901ca7cd68226d4bf2150cbdd89f0583742af9c90dc06eb1621e241f2a8f1c8b6ac1908e030013300f24b65c345406686507864b330c69334a1e4f124a53b7ce126df7176c98cf9afe0b94a35b69c8d268496ab67057e1189ca07a1c04346fb284c599a4d9f12a2a09594f97ba1ef321546b7a84ab19695af34b76612de744ca5a4f3cc9000cb499c90b4e73010406f88a0d94a624c28fadf466435790ab63ed1818d52ca06d5342d1e96461ce583b44352fc397e921ac5c9b5629122c2c2425facb8ff4677e861ab9064cbe231f6d5500a537b9e1ea1a6e203ded9280ff98e1aafdc8ec78b5d0a04eb2f1c8551ec5403d2a8b7ad023456ca716d05c2010db7bf0f47252870356410047d94505b11ce9c0b1d91cf860ff94916a3e5fac64595f7ef4294b90de8456f1d53e142ea8c2940e42e7a375d9d9f146a0a8ec9892cc29d199d0d15d765a8e43ad1c9b69b9a53f5cae8748d80bf60e7c4d415c4f91220cd6d8a525a3309496afe4fdc8714b48c10955686528661484b2fea0c888e4caf4a308267e0010943d62aa4f03fa4ac3eec11a7554cfb5b7384ef95f5bf0ad78f4315563e445601c7c8809e16355696b3480a4e428131234213c66daedefc67151915e45181fed881380cc37a84e16a242b2496b5f6e7fe005a7949c99d78a3996008331f9c11658f876c8c0a6d19811e4316b50bfa9f4d77fe2de3c10082dac151166dee8119dbe01234fabc84371b1250c3a09125a6d25d2e42e90a8c87e5a54ea444ec0b068444e5dac34ed142a2274dc9ba881662a1c8f14860e924955562be20a48090b9c9e331c04ec73ea4f9f4b5e1edb2122a311bf04bdb88ed8175bf8b29555761ad8244686a0e4aff3b83861e56c31f01b46a84da01688db001a1f47dae68e5e2460e5ecdce9208b375d80cdd0a7f4ed68134f743cd3729a252529d2420a0fd84713ccb4da0c09f92f68a821c949d15316ece844566c01d998e1943910ef08551031cb0514edb14b0218c937ec503b0b2040c75302a52f1449f5a7011cf21631629a4a6045cef242d8993abdc85fc8588d40adda67794afccf0f5e0fc2ca4cb7722632f25183179904ca72a5a027912a47b692044bbfc85558426e701aeed0abe0548a94cdc08605dba5ac8ed57bf2c9754c21c9d1078b803e4e99623b503908f7ccba1e4117bbee958c741c26c4fe2f5f6d3485a7bd4c98f3036dc234194ecfbbe94486c57bd5e3740a55332142c5f7b104a3991c78b618361899911ead3e73ea56339b6438d95829b62ea636ae371b9a842205d940aebb6c01d15d5f2a2b0bb69140d5bd72b80882f842d7bace6cca53d6c8bb508a6b2ab3941c569c1eeca83cd026138a416e25a4c563b58a95e1049ad549a8a9327448933f6a3bd2720bdaaee59590ad9a5cfc0d99195f93323b86328985720bd906ac74f89526affacb020bd4820ad3f5bca3580730d0f21b283d729f5187ce2b03f56b3a20429e9080573bea003f5622bfb6e394d561f6d0691d6a42352317b9d853687a9cce0cbb9bb2126c41a3808d16a7f8a3090e5828a83cfc0a095999881305dd6893dce110f075824495d85b748fda2d2d5297d0f5cc7ad85a7993845b8da989287616815ed948eac0d72311455a891128c05b6d50f6c1963579ccd7a5c3953265a614255d66eeda3d0eb5751bb251ee70a1c817a48091853841f3e0036b06d042c6737812c336ddf0b50638b91cafa2d44f120460d5a46c97317094f3081872283d869fa081018b0252976614402c47996101b8a2d4801ca6abfa65c6654c4ec0162318c14ca3be83e80a6413733619b035c7bbf1e55ce214fbc11c6a37b1d3a58144396b3f2140e5a89d33fd76e89aad3048f15c6c9c1c881cbe5e854145fe086ae765c2f11c62697a88eba0fb228c695b7ea0ed9444215bcfc011fea9d17449270a0081c89162e56441611f0920d09a02ac050b94ed3dcb980de10b1f740de5450356aecc8b0e94e55457623ff51b1a1f76d2973a2fb18405db5e42758f14429a46124b6f56525f79c1a13558b19417701ad34f3cd5b89009b89a98a0cbd3646ad1032ce9c88c6cfdc84858e46a34630f3e89d22aa08b4f52da0b942f405811ea7429b038c82908856e936154f0939f2eef9b75872da3834acb6034ead60c7bcec2365854647cdd02449bcbba72f6365c3cf4df0d541d17a941f32ae4e984ec5c39195274ba0188b3de82c0542392b5f7314b061a149e356c8643a45a940240405496203408378b821204506a145bb200299d80901391ea2005217f5d62920616ea6c7329811e7d2a47a79e4274c44639daf5118c690d6d6a9e620f24b6d60607d73467519f1ad4e82af0f6586980133f934620e76875a7975f19ba4ab0ce8deef37a4410b5d9904eac8022ba5823943ab04b2258ea253ae0840cae901ee84a1260012d3afe835583a0a04b0a7a579e146c273bc5060384422eab13702b6dac6caf47354e0c31ab9940f873106a40d851492e759f8428ec14d0b0dfd0b2c3762923ed2720c29c099260dbd9328a6521657a0f26c9c6b265c1ad3a767528007e58183eacc102cb3a5dc65411a090bdd45d9a4f804682c2bcc50a16743e68e6bc0b96821256f53dc08768df82f5e37dc806b166a28a9e084eb2696a1d1b8a940e19439f1afde2c39edc69c7a2a63386c1c5ead83bb8531e86ab3807f1068de5a0a4ec5f0de63a4189b1c69445cb15c1a9c8a9280162615958d82fa0a7af2b681d7e02411c90c753460f52f40008a746abc6f2874730529c349f82edba1c873a6dcb92957ce34d828e5589cd730d629bcf02f44ef270fa05323d6cd2cca06f8174e16788b0fea7c030bb08d3d19388dce8208f1c1f62f7e1345eace85c649ada38c14fbf49cb21d4c0ca436fd8c1890dd468ef4e0059832f98322c7b6acf57163c5ed8137228351478d48d0526b02df2446926483075a7bb0c7da9ca29fbf060eb624d52b1617e1bfec44e33df39f5e223e24061df232a5a4ed355470f60e8b348c75620cac3afa800c3164132a783687ac01e99b3cc31d892f31a3eca2f195e99d1334d57f2ab95d7e4e070507f025825ab90b0c24614da459946ed2ac18d7fb9c27b0bbcbce301d1fd6100946d82004e5f6944202b51a9fa1f4a3bd8a796023d408c9ac36295a94d6099f1e23b7658a6ccdc6f82897ec242c20652c1d736ccf068205d6c3525404fd8e7823710bb3cad97f797c1b2a000de3d51661153c200850837b4aa028c58258f42ddd12c36d954b3c7a144be7a06a5074da212ab36a083d7810820f14dee96a798d028c7056d5d6ec9a7ac044fadf6d341070bab069b9e65d684e5751cd14f02e561f9a43ad49162292c956a99afb2b2049896eb9919041574202a19b2ad18a73e610a8c9c235205a09f511afa1188304216091a1d86fb868d2343ec190ea860b381e3c52068c1c63bf6b4d54cad4c560343632a11216fa93a6a4102dc7a4e9e097d21edd2695d1fb5941e365ad6df591b997cc80020b46a535ea62c16bf0bac9aba24bb6765432b1f8d102209d2d7795c7161617548e615ef5f10c6091f7d07eed9911f901e12c24deb7a41e346d81c79a72c0d3a8ca04bac213e457a162757ac8197cc85286be42eb0489cc8025bf9549116cd614296e359d07eba23094b7563702fa31065366d75deab25c48e85315b15205d99899a3387e9d42a47da50a8b5349900b43222541ed56808dbe666e9166b48d84979656e879495cc442d470bffc0b14ddc9879164089b29a0fad3a801a232dd4c2f63e104dda472d281d09c5df8998887427896464e1291bad82172e3655291b4731bd638fd16927c774b013227962810c103b1328aada100e30414fa2d232a865982f3b51c864254252196ccfed2170208216943cafe24c7f8d9ae61f2541d3693804693c8b966d3cabebbc32626c19b5365ff231f2af0f2a9a45725226752aad55a988c49e7986fa5e1b4af94ef4c3ffeca47a8f58d25e5b91a60594dde53658f8d8496244d800073405a57494b071308e08cd863c7b573410b054a4af4c9d114148a5d0ab03c29bc52eca1371b3224fb60a981a97f1c641637a84d6593e55ff238f8126a1a1cb835b1c3c412f46f9541211efe2004f5759b13d970af1b416217a898b5e0da7e7a78d7b8d0ec24d84c6758357eb806f640505d4801d247d7807b654395710326792878cd0112a25599096385f561c4569212034642c51fa1c50093e6d3725d5bfa438700a2204bda5a426dbc214d773e014b1602da95f90adc955ea34349e10ac0e0189aae3123cea2776678dc468890d1213baaa4960722caa984bec49031c20264ac781e0715068693eea9848b80112a96c6d604ad8be8ae6407848806f5edc6911101e3d91ae4fcd760a2dab4230a8efa8a2e61137921d8729819d104847b35994a943c87ad4290a41fb932c2a9deca1844dc4b7a28dac5ebec557e27570fcda442926af45e3114b6a04ad4e03f3083e82dbe39c127e954df97418591480ad2ab5e93e62d67a75d20e35204e88aad4ac5be954e41b27da5ec5b2ed494c8e67aa212b8ba9a2765c9b5efe02a748c3d9b95ff202460ffa7bbf4a56a55665ab0e1b0054a2d665c3535b95aaee038897f9dcb2d5d21e66a785a14aab5805cd3404d962ef2641ca777092b2a33a4380365c793aa9936195c553f901af1d6c5752d4aef4cc68567b0ffac1991bb9cedba3f6334440be8251f41c651434949080bb05b963ab58f1d2757997320f4b044e6604905dcd5d3bcc4c049b280aa81e0468ac8f00ba9e8415b1a652a0735b2756413b7f40b2923515c1311492b4173843fe1427f82e7d6ab151404d6828708226a340c38110e1b0bd67d9a9a8c461512d63b492095caea2c2d919da9ae53426d37b7418c0967db1d37d98a2b089d4dc68eb3403eba30f8307adb0bd8a66e3940435c967d0a6fccc5d6b1a5be05a122d3bc128aac4e40b7246056b8901c26222953c9f4372fe6a8986202006692c84c705435d71f3204a36f4a6f6549729b3aaa904d9d2b306c9baa01730fe3689d735fc096b4b65fa640191c67ce9454927da80fcda5f93dc8705dc634b9cfe2480a42ee507d0e74830d231a828c979d74c4fe541482e8b408a9da20650702d57a5f7398a6071f4ba70064d54042ff96936991679ac06bce8b9b4089253f0c2d05628ec6111e5c8742e2aba5c40d88f4634467d21a188667b555b4f39e9082e81385f17284d0d50e5a26215ff2803b41730f5fc560b556c24bab61e00a8654f59c9950925ef00310ca8fb21016580552c20ca62ad19b6d525b47c02c3f24b8455476730b4851526c39a0fdfa12bc812a69f6424bc12263887f5161f1083121700e342b3a1a5aa030df1faedf5bb02306f990f95614b7b1dc85cfc90f5978325ec5d24b69e62034ebe0b016d151f3e7c4d544006d0f69747cc49c93c687900de21f2214baa024b7866829ab6a54940508b9b9c35b11a02694581d536320216098aad530479931fc19c5ea4cc9ceea2a7a5ed7c88930744a8f53c0cec5e480090d78225ed65a8340fb2ca0ffb810387bedeadc9a31051388d448d2e690f5cfb9521af85d92803f19c003be4a2cb59c874004d2d3992abe880129ce661d2758b6a0873bcd073453b405d521b24b744ebd345bc20d2ad7ec465219286fcae8b9ba9a4e896e3a4eb3036bc1ccf842b4d6095a247b052e947825cea12b894d911113d77c0345e8794b5ef22a526a7f1d2f44e88a2ff729426ab72defa6a2067d9f187376088006e5a92926a4b2184280f9f7f4106324465211f7a048f47aef4933ccd466365d6a1bc0d73900aa2be2a050f36158ab356f1e814cb690a9c26218a4e53b56c7829291b2bad01e96b6a56e44b659a9ccd053c8f6b6068135f14b213336b2705264623285ac9862895095d3922d44f401582523aa5faceadb72f2425ace44e76f08bc6b0f754d5085a5f47b77e45ea534d54740e1098fa06a2ba5b41f461910cc88de747af5bbd127b8d2086de81cb8be6a005513f1153e90d5c11c97066b39a4f8f222c892800da4388108781e81dd38ab4fcb6a0508e8ef900d4f1a6112be40227e320ceb11fee88c2218d9c7422209b3a7782cb777966b29869db56eb2b160c271ced8948a43b29526c004524b1652ee0b58c4cb49a8e9d1a8ff322571ed2c247f7a121a48ff470755e704a35970d0f3a432b960d602bce4d0cb2d1029ed126432184f5a400af9f51e6e4504398ede3c9cb76827ac985d810d93e6367b22b0a44722f2c7672a30919ae6b1aaa957078c232fa33a6c934c0c42e0a61e5179ef0e52c766e75893412b9d7031e2d049ed41f76446952358e5d6290894375ac385bcaef7e58ce0e40caa66fb47cb9b563a3595ed39c039189f621a6150bc5c85a1674614d50d671408fa94040c8b66a55ffd99481fd9021ce58523161b3f8313baeb798cb0a04dbcd101bff1a318de54a2456959a8ebe42e64fd17bda098a2419eeadd3f9304d75972cd01610694a2f9b373aef048f56b00453a7d27b95ff2e00ea1c2fb6f419040c9de443ade0570c65d332be100ec9442abb9121111432d7e310247d01afd4d9d08dc0dcb0791ac4eda44e23b6cd1e9b1331cb939d550a2724c6d23d218272b95d128e0188a273b2153d84160fbac51db76cca7ad66d62291b4b05265732c7820d5bf63a2d3b6107e5244313723086d5d10b9829e841153426f8f03db521acd5a591c9c40cbb3aa493b720a9412c6ca464a594b7ac776ac2b70438d06478069ac073c0a76868d232e2fce5eb8dee590b5064265dad8655ab434e0125513b7dc8f910bab6b651c9481380c3d307da12b59b93551da0caf7466d6efd56c7416882907749b290d1607ab20d8471c014515c6520914541e64bcc58568502c2790835152671ec233a9d5a801b5c5f4321ed2d03ae6c00517afaaccb19168c2bb0b624eb47c6c1f6e53384d59faa11e7628bf2ba16186a935206e9e696331d069e5e2e87a2a6e3c88e950b04a5fd34d9c5e2e0b4895dba7f99048e59dd7efa04447e4193a525707de971032757a8652908a760515e46d091b72828f45b989e645e664cb4154f2cfa8689139d8bd63463589a7a98b731760096538da10eaa8c04296ca7991b1dc433e1a49080fd862d4c0fc157e332708d7a083f212c2ebf5aada905a5ac05d18786348c9033f502eb0c024609cb46b10da14a86a0261e255a92f4c5175c003deca42c00847a0068359cb05a2f4069d73ba0ead45626943a24114a0e3721ebb60d740f338571166e3ad7da03e14126e1faa12f715ac7a8b3832d9a74076b8f2e4a87a2fe5305152beb47ab3e913ce6397df81eab21000fc92993f14c40fa12e3e319059b1d994f57822b5a50254b1129ea1b464d19032700efa27490e7c876dc5211635e4393a0739ded7a7f91fb7b2cc00ea7dd959c60627b8d7af43cac98dc44063dc7f1a547fedda4df51d2e8abcc10781637abf2e318cde8105d874880253b80d44b284409e64afebeb02fcc3475aa36958414ee6cca729788f948c94b4e904c146cfb88b433135cae70bc7b0b6989cc29155fc3e202804d4424d5a814542f2946a8b60124500e13436a4562b26eabcf85cf9ac588bdaa237dc6477f15115633f10c68b31682fa0c1e98c3520127fb417526b461c648fe05224038e7595e4ba108b88b095c67a9938015a0c748039a0023f74095a5d16a0d6941502af5f114dc73ed02f4539d24bc851b89fee36453068487d56125f1c3b2c0a2e6768d26b04cb406fd45c9592eae41f211637b4e8a828ff395faeb31846e9c0c8b0eef82eacff704a5654b1064e45a88e2808258396a0a70553206476e58162f5ae0dc8952fdf767d06b31805b14852fb915140c5da9171c9b814890b65e09d3065edd359818a6dce242f34029425a4f9d2e5d5d61c636cd241f4da184b514b6e936a050be401299132f8cb5a85b927a441e153912a0541d08828f7e9ae16395804ad24657756c051c6fe39262244fc588c98b58b2172a94426802adc1eb26595f6bccf2c4114f580b85e47c850511ddc5c0b02f5ddac05ef534fa0759307a2cc2890e7588c30ff948d132ac7068029ffe8e868cfba753609e4a2e0d0b6107abf76a4acdc380902eb4e8ac6fe88963e96c8961c3648ac28601b4aba334982d41d735cb90442747e2e0841ddac9d0d5459c5a512051cdc0538b4eb331aa5d9d485ec92943ef83c4260f09bcfa9d1e62d7d164aba5167065138a5efccd8b682ea205da2592bcb106e260eae50a1d2c872624bac8147e92156876d1a819acab60200b6b8d61eb3644685c6818d8207c586529b8440899c499d35462948cab878376f26249ebb986b8ac52731a2ec5a0ade868c342da64f755994ce4062ec2bc4c34caa2f1f0ea85f2de32754c843f4230e5b9be54e811d0e39338bf5a4e0c4777801bd430a428d811c0094db6e711f0c48bb9d673bf0461498973950a50768ef1158b1677164c526490d9041861a971d46e1768105b05d0e213e686f419556e4d26baa7499432f452431259d71d4f2f844046f6ab1abb95838a4d43bbeb55ae2c34a830215e440f8c6e0fade915422af94c98ba4652e8c94358ef58b3be3ca0b453355e221242b4e6b08bcc01d48a4eb0650b479a59070654b9c4872d422e7526f51a255e7e69578ea6a0c94d3f7f10f882bab1fe3288fa279088ec8cafb09642c84fa71915774e693875f403a5270923aa4dac6af13f6782b01aec449dcd9a681e40a5d5074dfb4e06c35887416195db7010c8a3acec68221f00e5b6566f424d6110f217dd00e0d14aa56e2297ea693cf0696710036c2abab89d3aaad94dacd7ebd0d0713d3a809acbf0096ba907914ef422168ba7935e0f00b1832573a9ef019c5d6e6b8d84875ae32833a9f2a391c870d518d4607d1804f018d4347dab4829b6a9c9cae2897505afa14ab4274d89720e22d31ec3e91030c925483d65c003784c1bd45f4e916151c1f168047a7bac59bba871241ac5be4012d6af5ad0c92edaaa7cae0a59160137a8cb68c8c24a49bef724c74f7e02e1d1739049e9269eee7c09a01559ee11a4a31080a3ab463235ae0c626ca40d393280239580ae9880ca8bbad0ca9a501d62c10a14c82f6c9cf582124be085d75407601394cfe432b59a487e2c04150e0e26aa45d7aa7a681c4c17ec5b2dbc2e7313824d852bd3cd8c9af50077757da591df256c62f5eb153d19f94a793349e95be8f6fa2ecb942c4c9ee520cb08c13d3469da0f2309c1b6429eac6a4f0abb2488a0f3a1b2fad27017f71cdf9d608aa0f4365a551f2b43f60e5a82796e8c8b1362f4b25af692b4a9ba4cfd56979a8d9ca9dc82128dd692e345db3163a785137afd080cea5750ba72b18d4df7aa152baf7d4183cdc995719592c5221f5cca96c020730e2f507280241d320805a732d915b76ce0c42bd693045df9c5ad0b415144a2afa28ac0ce1820e5acdaa8605138890dea8dcb82dc7cb8863ca9320550276e421301b6ecf9e3bb8245f21302477ee9902676890a4c2d038094fcea31d1a91acc655022069c56266aaf2d1a94cbc0a20434d166c88518159b16af016da40914e0aab857bda7ae0d9beb4b5b2e90ea2a4c22a949739a11e658005c9bad258ba550c94b67e0d0abbb28c95a8e92d0e5d47abb16514fd84b4c92270426c6de19f3d4682908b16ad8c0e827752b3a0b933f7735a5094b6a53927ed19cc5aed9f3aadb261316881223d9cf9a105f46dc22f9801442d6950b51ef21c1862de5667ca116a882994ac9f5a3b303ec5c9c25cd0a17ae96b37e7a9c3be49bc33fd10d2bede60f8cdbea91a3232c09723d32885de6ba9a8625b947535568475e8ce4346d14da0671118be38edce5c25cbfc9079f5cf772ca89e0f4692784e2af16ca6946ba6900bc100799ffbee0006bdc31f51445f2321ca53dcf5584d3531a3c792d1b5d1a45a16827ed2ef45a9cf10b7a895d9015090743a74a433862e81c16a8ac0a25a1fb2841e6a0622e17865090373069f22b0e47d723026d2c207af50002717258824c5997899cd7def8103a09f4b256acd4094a6013a831c891c5120a16d9170998c1499d2cf48d240a72a511a65ac392bfb840eda4ba3d2122f1835e39c9660a6234a73257d85bae76dc8e102119128ee07f287e514cad3e338b4e7f72e4a03d91d1d1c3bd0bbd600f505e20e45637c042882dc3f279880717fad9c8d684a110500d5d340f789b03bc43604d66ce29616925d9f01950845d4667a97d34399ca921ee2cec1556518824f733566543dc22d48cf8acb51b2b8fc683048bbd31c1ed2fbfe111dcf1d4ac227cc80330cdba8d55a5d8185380f45b02c26fc5f1d0083ad861d1fcf8943fe891054bbc39d0c6eb877cc3428accaad98a6df5a23d678c1bd0da2751b71a43d4573217bda4c4f60360629fc246d35bbcb9d440c25a640735067d0b25032dc5075ac770ab9cce2a2a4da74749763447caaf828c6401d4b65bd1a1e77732013907205dc6e325a68f3fe8b0b75c5c39409b675664654b36854354b0112ba0430910046c3322d771449bac142c553fe901e80bfaa03a1cb4b0dcac84e37daa7cc8a4aab8b88d4a74f2f5135da31133f50c54de6e839d94bb2880f6955b30721067b583a8421cc2a8294d844bae6c6d33a4abf0a0d3a690014e0b2c06d0d19434f9c20b21ac8d2935df421305168083a137b511591a3cd0349c9c1bf080a45497c4ea45ae43d7e4c7509995e354e8a8205061cfc8b16b45af14e5493af2895c62c5024220e1acc8e83a40aab9eb9a70a3117d297b14143c9aca46391c9dd37d44e4ea5404c2d844cf634bc13b90db1aacc8c04272c0a8a047b98a078715ee5a944915c2c2c2e9d0a89574aa918b6a6efc40e84bbc09ea3f3a14fc61ba59038e7ea651907e36e094a708e8d0671ea9c89388f45d2e081d2b298aab0ec2a7fab7959a0e71a1419efbd2e9b3047516ea617b9a2a62ae55f6e6781507f653ab1a1d4d8380151025d3ad9c32b2104fa49ad2de9ea025b1986590778385cba0b4ae096c0dc60245f75991c132c061d7a9b6e060e7b2d06a242d2234d495f15d56aa5cea08acb9ec71d09b7285b82a0e16dac92b30578562045b6bd35e93294564e7f65469340074fe04891916090919c1ba38913a139bb0e00e0d1a7a821b26ec2143cf51c24eb120045d6a417a6c054d48adaf40ab097b0c25fc2539b4ae3126eba5a2a63a011ae2cf3c51915d1c4d1cce10079609d5560f33e1617508f0d00f2a99c9650e28f8280895d8f80a9f768333cb6092061c8aa4b4bee104cb00724985ada01b2d83035db0975551b715f194875b3665351808001b81e1e92a579cec8fbf25ed2a975b834903f64ec727ec8f1f8c9eadb4b16ee738ad2072d9f8cad9ca1c87ae63c69b73726be51c68069d411e532c136880c62587cd87ab2034fc27c32ba9292f61472856068844df25c78610c7195559811d96f512e7caaee810176465604893b8ebc58aba0483a5db1321c8a240976f001489dd25cac38d6c78715c7da66f362a0ea76df82a74d657a181a3c39018c2ce08137c9458476ea726434785d8caa4980d0ec30a66770783d6c4e800eb9ca4e06bc46664e901b03c26e9c49be855e8eaa90becf2f99751a101024c4b4b63135540037cb0262c0732d3d68a3c2d3b4e82b4ae53ecd11954159f064b402f379dfa24011cba140f42ec1f286abc1269dac608097fd44b2d2b3a03e068d60cca6646346a1696e8b04854c43a09226858247fbc5c930c352c5b424b9378650a98265225d6855bc69a8d29c95afe54121ee845a40111ba9131fd35ca3754d06163238c3ea24988e5512cf32c98bef205267c3311987c8d895d97eaf3b2b6f4107c8418b70cc5469e8ef3b4a4938828d480dcb87595d2b061c8d163a1937edc2d1092274293a74d85dac55e1914ca677b003a0f2832dde15900e8165bca90d2b879a01eb33a49090eaba47a8a4d622705b04095c1d738b8c29ac1fad229daa8c97009b2be3076e0739eb7be84c982ada4024f9b25cacb08485479a225abae62ad6b465490fc9126079d8b5322b61d71a11fa188b2445a9de84ab0ce58226e12b26bec032c614682e52336164c2089afcf5c718513e458656ba8bbaef1aa2e8f859d08b6e0c2aa31fcc14d2348ab7df0b19da50e8da603cae51d79258e26b9a36df55f431852b814beb1b66612d5696e98e90d6ed696fad5b519d54bce0184d4430419b2a410204f678b5553c23480751da901b2a0517c212056b225024cb2821271f224396f2e698309e09e0c7b1a919cba2cc108aba37891864d5a00704c365eb5dd120cbfe53a7b45ad0c1d42c55f3f4051a98b9cf863db5975de64974dc707ad1e9497e95447685e06a6a36f2168d368c6747ad8989fef6a7385fd6046426322b3833dd5a703fb664a2c4c4be0cc0aa69860691111c0ba783247b8e091817c474501d0462e230d61cb9255b2c9cdf5b4616d012ed28d2450d2440e007f8bec8fa5c31b7521ac0cb9ea49ca9180e295e9faf49c81524faf4243a5d7043ac232b9537453313337396bf0a29c47ad420819562a8590691930c05a50b503a806d02616001719c23728a6643b76c43a115a25d6c55e320b2945a90350a2d365ada34e2123166b458d48cb6882805523f5d1ad021579d183268765eb0adb290af31eb4a460f564356a465836b1a06eb46ffe2561f138297428c737d6cf84fc584d393fd30043b6c00348b3eaf0cc47a3a1dbd96121f7b8850a58964457a350b02ad8c2d5d3045cf1b92859cb0fca63a2d95c5439944e3fc680177dcbbdd01b2ef9695435c0b280b5bebb1ab1e58838518fc9c2287652905aedaaac7931a2d0748a378e3215087b2f9385455ec4cbd0efc49ce42dd5326d41c7942f300180f4ac37228072c1426cdd0835403d3752f652e6652b79b294cdc87a2064c1273e8958312e288c4c6b0255830d3204d8af7815684a3ef81e092d4ae76a1bd5acac136c340198e6f041c3c554ac6838ead7053915f45104b059c0a49a8820c255bdb3ee81d65ae624875786a0c249cb4172d54afac8c96b204eb5dfaa5bc14aa6a264641f33568e488d1c0a920c56c0a2b1f3fa8af23db8c00e804d7eae5b479acaa40cb722ab51a74174f705ee5827711fb17988c9437822896db6d8fb0e2ca27ea78988ee7067afa938e2d13dac43583d2c8a8dc986851f395db98d57693d0843a5cc609158500c8be675290ac12aeac2a7c71020913bfd58fb0a108a329f82030d87eae981cee4359e180e9712a555236f717a9226934b81403a090cb5fa0099bd3e05d6a515d425c912cc7af49544a846b007794405c4b06440507dc8c4c54e79c4dfc01a590e8910e5621c1a4000a92cf5728919a11b0c083ac9972ca4e34b9081eca120ad3e53996a978ae5f4c14d03dae164e11e34ba19400f7e89929316b045d33d3c75b4831e4af23b86d7e7fe083d9583bbcf81f5c69a45d5b4252eabf280d671587a4e0edc72e66dbb201c47271ded68479d2be9636c596968b17a02e2dc679a23d3019392c1dccd01e7b2bc781d0a26c206158834d88b251ff5e1ed059cb5fe6694914389f280651a38d4677088c8852c756a219b4b0f8126d36dad89c1027180760775602c063e144d5724461ee3a5474f0114c70239193a9a45bb9a80185ecb4569b278ca5275953483806f839e19d49716f6519b40edca8615c81626419666244102905cf4972e34a02d257d7a9775c85e0d81351a4e297ea00b8db71f129de351df5fc1cae7d559e8446f3bfaab14a7ced537a499331e5d815fdf43e8da6bb62a1d3641866737c15124ef9853a7b970491e0827431fcb20aa37b5ad6817064c7d3db39446000403109327b4dc68d61e5bc7498a5cd432fccaa06540e312f36371146af31f155ee52397289cd42b4a6c241459ee64a0a497f02af538628532dba8fe1f6829d68d995d9bf5b9e5566ee21ec2a34946f5c7497fba2b72b92589fa9425339f11a7af73d428b6d18e984c16484d98a5949963faa48055618a543f3fbcc99ac83eb51c5e8e803626b0b89f4b398ea44f91362ed0943dc56f0bf0f286955105c3519522c472e880a24998cad1672064bcd60a593dc6604853b872695a31881e23a59047e160f5641b900b307e69267851d8047bca644f1fe034a841ccbe922a05acd2014a16ceed0143c009c88c28f010f2cab1270f98e12728a97ae2452b73ba0a32ed165a5c6c3640ec0f16d2f2d70c3a9d0b2c4e43996f5a50d89e4c542a5b0b9b4bacacdebb803a551fa362fd915e4eda0f8fa896736be4bc0dc4067b52d7d7110a329d2431f98e72b51b979a7b109797b44705100b2f474d6a04302806f626dfa05403d84590933e31f7e012e6f0b58b3a06990b9442df932af39b30b10ee03aa5a5cc907ed510afd3a6808ee051a93e2279752c5888f41d1bb7fd388960a5c8287565ac3cecb40cd94f153cd855706a3aae1329d6281dd0cf5820842cfe44617df838c3de594287f5a161ac89514099cccc9d775a92e904502fdd0a4c52bb92a1a4558149d5425038731f11553a50955e2ca01c28fa099b4decab3409fde542ca1b8c815e9554c5814228fc899c0c59d3d8986ed36a43467ad8f5041884b49090b05b2cc908a70968e515016c00e1deb75cc5ef036ba28f46e641674ff092926ba78221a4691991dc4a5d0236d8a1d19fc009eea7cc99969576d703089d785b02664f7271348b227fba51a5dc3314f9e84ea964bd6a873d9212b1c3f08038735684be25c4087b6288fcf10d3b8ee499b62b230958178a411e5101446e24474ef710438a2d340b8c85c225e8365a4f3202386af2a31674580de432931b685a102148f96f489de602d7aa6514f8f0e69cb4c6a2a9ca85520899cf18097f306245ce83b834185884fc4483a226d165efef1be67b49926de78bf12e42fcc9647176d8012fd880730566b4050854322d2b75f98f03845c04c6a23611a780add2c2c22a50c405b84704861bf2802b573a55f6304868f9cf80081d415921c73003e3714684752050ba9a0a00198d6188f1857a08db221ae02714e96a39c51547318cf210758dbe65978d96b128ed105ed86003bc085a972a35c1359be8d8641b0538c14c89ec41d002e0ab356d99c8052670492a5399d5a634421bac8b135022d716d0565d79e153b6a268d94c3b51ada4a1a3f5c82c620728d8715691a03c030ed57a0860b158cc78faac12ed8928f674a65c6cd830a4583d025d828e9273bc133162a1cd28df808517eceba2d6a7bcf8f8526b349f514628ec378cdee55307164aa87904cc2fec0b3cbb1a829cb7bf0802a6dfbca0c84748f0e837490bb7b6c96a36344e74125b607d8581c6726a44a42dc978d442dee08ebf3cf3045db9ba3bd5603d41c17ad10a20cff224cd6915e558be50432e7db4299325e9b0cc324d5829b6d69a442c242c0e5e31321f1e230fc4074d43f5dc0471e5b0d58a66c8e94beea886f2a2456342d1e275144cd91863543c979a1c36d41e14eda74d07eb8c9b7b2552640ec414e953ea18f9014003ae46d28e96c02041bb58e4260340d4e2615ee462711deb749c2d5e8243ec7ee45c4d387c1150f661ebe2bc405e6f903d26870f36c3a468fed2a74836e0827a1a0971b9092c05cd8b071916c02e582d7747d59b7665fa0d9445ddea849ce721b3a3a7fca871556e981ccc901d7d1707a2f1c408dd2d8cd94f2195fa442ccdcbaed20e2cb57558981859af9de4307a65b90d174deca1247c58233844b1b4fc7235921439722e4484b21b2a65ec8b34af1e48d9d70a9054e93e0774fc0c222eed5413bcaa0b758de350b4bfe409754648a62f8a59f0169496643b53a87f6a19d098088879a91362da459a8b3f2a15839d02204687d990702f545efda4c2b187e42cf58c3dcd3605c1566624950484e367aea3fce9f932f06745a791a26199c022eb3857b6583bc03d87454db2b6c8fc3ad718a30c6b6c4ad74d59d64aac4bc7900aed7c7ba15e74f3f6d6455903b0f5a0174459f4a839318ba0919b8e54ead4a144a175b00c0cf2264f7f58333bb880687440c864b0ec014a11c5c88a686401d249c26f25a512d4fba420fbc8e403d8e191de876f24b29a19b4ce0948aacc67c383b6c20ac3cb0080d38d789df819695ce339b27adb94e6a3406f6435b2e01acf21b586d71cf59d1bdb5159587526af896e13e1a8810c927d4664d142128508c6b892c94130bd41186c0d1a542d5bc02b4022652ac56bd0b8a0da8f8e0d805bf0e81c9a61046b36d7e57984919b3de0d177b4b0349f2c100f52c174293a0634186bcc52a0a1e22b8a28692d4d0f27924b4397c005e07b8f12e5b94af0475178c162f1e3bb85385358568fb26413cb434007438e99ada0cc6a0075e762442a88ec32a911248091af8c96c7310995f0d9a651b77005cbc9939983b9ed399c34065d2301b343b0207a0c81a63ca1c572477330b21258749ac303302c530bdd19c869d51edab6342c044ffac51e5d6b71f1e4d236766e042ec71f8081ca5e30fd0953f18d61edbc5861bb2091d45526b1019e89a1d75ebe9c60fdd82992356478220442f9790d28d1788ba46f3637d8356ed44e74480e5be8c35e4b87629e444dafef1855241f9ade398933316f25674b57baebc58a4a71e1434cb978951cb6ba41805e2db7aac2534438d0822608f91410858010e25860c91c8fb48f347472860b7b80274249c8299669808d2c20610f50cac02488c05a43a36a066ae2fe29eef13909cc0ea1062d360f24101f4309c7b3248af13712758de25291bb82512353b8c3a32d59d863f7ae19fa0aa9cd4a2eec681d127a75acea800bb0239751acd99511f5358148986ed745b7a95324e1928d5abe0017ad20d3309a985c0dc38b756044664c448a3cc0a339998ea3009fc576468301d61d343535212dae9a0da1286c11466b0eeb4d932c96e4d44294963218324bcde845ab2cc300adbba2d4d6372e8d39acfe63ffda127c978605d98ad4d2683a4a008be811ca06ea16b1b4aa0af2ab691cab63d0335700a208482baa2beb621480adb3a9d86fda74c8b2d2e4c6e0a77a526cce7ed58548b30549f45f2b09ecd442a4cf6168cb687238eab93b28f7f047ef094ae462bfa8da7316791572a7af1f3f6467402e00e2c2e3fc988d85af40e37282a65b36b7329f4e591a0faab3aea5a60d70d2daa19c3462629164d893432181138c346c7bc319713d3bbd5a48438e8563c2ef29fe82b98d19dc3746dd65315278ba4ba9273d024cf3a80ed784e8b6b41dbd60c6a48944e3a1248ab5200841a36282839da065c5fdbc71d45f48d4e85217b204eb143c69465392643b2e7af4a2505484b4d8e0583c22468016e6f4f4ab59436004102b2ee0089a7c24c9a31bf4f1c19a4a42e40dac0cef2811f44f1b68598c08ec2f1eb0f2914f851fc7a462ad3d34353209a1d5e6fce83b5c0eb05978857a170967b7c14a41a7a1a0e54aa664ea45687065202c9ee40f8eacb0449294dd99420570131aaa3f4ac08715425dd4444055e1123f0b9e55456a52712afccfa65eec2c09ec732928d36a08bebe440799ab1dead2458e663296609b160204c1670828d055bb509dca059fd613e392e37c71713e552f9f95e7cb434d3263bfd258c93faad001c220e322e741ca0056c8b1a5278039c4ce0830cc2d7a3809be3263a7a1af7e002d38c8d38b9060c81712ddf8255337da5316e4d3fc005babd79547ea33258325e07537899ad59061c78b8c2ad5327c50ff36e844df9d6a7243b0ce6e424ea93b38c08535a5c8cd595472d9010dbee43b13223a4c2c4b2c9e0312583c91ea722f1678be38ccf2d3794f526cab3869f28a374b0611cbc63b9082b38e0e4cae678a0e8b0206826e7063433f08d6399a235dd830951e5c901c037615901f3dd6822c938061dd566c889dcbe2e550805b1ac80a1e5f524850bb5aa2a5df4c711f27c58b4c2984dbb3aea4e8496dad0ee9cbb5f30460c26e39fbc4ca10758525427688a51301652de451d01702ddf958de94a75d08d162c672358d22589acd019ace923a6c974766ed8617b35f72ab8e2096aa27dac3d456256cd84a44a8ad0ac5a1eb11c2a0d16089f02901b0b427b900ec97d64b06b2e6da2a164c79a6430bb29a5ed1ac494917a112965bc36092019af9805f578645700f009a83f0242b930a65fd1f30717207516f3e648911b66ed8e1a89e6d5f55ddd34b1f6432914c8fda1429572c29052d0ec583b3c91099d2ba84473ad6a3fe533badf654725425d4f22d06cf951e3dfa3aea6995738052c052d020a0e952bc62d188c995d57c080a116d91d14cb800014692e2a98361ace43c44563e828b960b50227735b9b4dc4fd34b9f7a03a1954c15757092aefce789d9977c28cb766851681f6953be85cd5bc3f02088ed2304d139422d6a32a1f68e07012f56061b9ccc098f58539164a9bd2cba02e40b052537354df96200e04def714185c5f506a8d3d892d1740279ba5d1466e97c8075352807b2f11ae707ac8460c9d845683f64672d8488922eb4c2ca0ea931f73d7025d85c7d1b1a48840b6d24ecef7fd07c61fb7acb63596d1db687fa08192abc7dbaea450ede078930ba510264ee4405464ea1039370ac411a1b878617fbc38a9963c873f74084ca7ad08f2cec9c46adfe16eb4713180bf3b8066258134336bd4b8d372d01efc34fa543275ab3a4716c52f3124238742250c4c34112ea4da348af000bf4ab1f5af91220036d6755a94695a1d96e4c7912724072a701081505534170b2463b3e1951dc952ef4a623c880c98ecc6582a7200048b75ad96807cbab3aabb18c9de5440542348d1961d9149a9f06f449caf6625bf45b190af9cd81466da782acdceb8e897e03c7d953de810be192bc242f3cde225f7d55130a872403cdd1428461f5b810592b5c88bca3c615605cb24ef7d883003cf2c112db242d552321c239cf1b20a0f2cb4f8f99e1cb9a0a7d7a251f1cd858bd74753551f1b1a26f5a578d102c99b7ec41b92eb9ef0baf9f285393792481bc0ddb809eb5a54cc7301736d49a513f4375750146540733a30f8b2190880c87c502e10c56169a0a42c946503392c5107502a6f914d61bcedcb59893af139ab00a08278a94cee3a62fc771e1d37dce38c8225051fa16317b7ee202b5fde800d065a6796d4692a22e2341d13f815993cf70e274eb98ab6c48d1a8ae55e04046f58afa5c032acfd186c597932ab55c260f9dcb0001966e46a46672a74f5861c92516cf95be206b46176b61d90308f75cd47080a8b1a49064ea19430edd11a028fd055690c3592b3b5fdcd787aee0b46bc9164b47aec71b5c0091b14c71f02a4b35eca345d58e34a54d1391bb7b1e4a4a6e1402a8997674b0b8b0f0388cbeb63cd737ab93a4fac0a66dd87153ec19218bb3ea6f452b0831f8657aa15e74be304cd193d3cc9d052bb885382bbd03f9c68f4c7f52d4c55a51f835166a9ba35f372e96e3c3636829d06ce6fe322bbc742c145858157bca2ee69633d76589be90a9251f8be3d16e5a533ec0e64d334fc469308ba0f7b160cc57821986fa2d56937c0b07800c6bae5690cca14359d2972b40b612b4ce470508201dea9d9e1462cb5ff89d39d3565b86b394e8515cf9603fad79d0578815d84aa89c8f5b9423f315399ec828323f64e4c2332571c3ee55897111ac8aec1658774764e5d881c46a3c0f1816b97a6840d3e9d2f64b294e64389fc97c16317bd4043b412b46bdbcc797c68e99ea915f446a13ac901975015169728c0bc7d3dc2a9399d638cd2493cb1aa062d6d94607f68c9444efe1e87250ab263d938a5ec6d2d916c2b46a1d5010b4a14479aca80088fad589240ba75183363bd5d7ad548c3882bf2139129411ed46571ab60dd5404e4b25aa9dd8216b2460cabee8d67eb496176fc01578737a0d4081f64387fa3d4672be74c554d3a1c4e917f01469546408651766541e80879015f2847b8628903550a4d5931c5034a924401e448584cd72abec925cb53c202ed55ce2d46ec54fadbea474c346093e683a9044b181eec0741c0d6d6c9e0d60d9559838d8414ec7f5e860123e990e38a6e8a1ec66818083c2f2e977998a5c5126199dc808a44f3af3d5bd887bee4454920d71c3afffca447643bbd4b798e8ca00fa8c3d8f902f960d8e0b1782e33d074c9c9a2d4680f652a399cbee1644a176cd5004545854bbdc5b0381ca4538bc7b0992c30adfb46a597263807a1c7e0b4012b08b6ce8f88bbebf96d1ca38bc0bed6fe278f819a0914c65428fee5067aac7c818ea2851152c6b60d1e774c8d5b7b058b80a320f583504656f4db38e0a386d344cf2bf142574ae2a3ada7d43270333a46011f162b6fb080ceb57c80beb1673dbf11b6b500824b17c9598349c1c240bca90db4f9998bb8c24bb3ecb4a97bed0cb4b0fa0d329bbd94de93b4f38ef1295e8220454c96f6e506ab027245809f18fe613268e0d726071a204043d046477259fdcd84ab8a619c315454058eda5eb10cd802a587979231e4d205e2358aca9426341599ef6349dade0fd9410f6156b91537971e4acde8a641f650cdee4b9f75ca736f505e9dbdb2f544d84cd9fd6042ada4858a060afb8692e6b98a7fd32d079a23a0219c78663afa585c8676fc474045650dec72752d6229645882399cc958e82f21a8c49ed858a2b16cb334253b9f084fd528142e7c0808505f0e6eca4aa93ce884da4db31c2c25a1093d347d27ee4b33c280d4535960d8c50d15f902ceee50d8796442b6e133c06fd42270f0d6415e5ab2ed4ba17368c7e47cb9b7671a954d3cab227275070e4848c76c2186758daaa4aadc7a0f0cb792ad2b0546820ca4164e80026ad05bec5c61c2018e3db182654789ff54f379160ed3336909dedccea487538ba962837d993dca71329e59fe995e9212ebd1dd2a7b726a209ca7c1d865cd418d97171fad1666a886279f0bad289ae85a57387a51fcc38043c90014027f2312777a8d3213309332babd8416c234f88405e1fb4e45667767c517860a21da8ba905b2654ba46a92c2cd0c8a5e5ca54391a173dfa0e54028bc68da49682d0d380f0603a9d5345ce4548af35fc49b28e8cbc5ec10aa1ef8e28af468c120b8993a05ef440149bfc2b91cb68b860e1d4f4b0654ee87cd5296bb17cce0ce44d28a0a76441fa49637664374b445e4bcccf0d0c2d34265da15ea04c279657944adb684a39832e3b4d2480a3cc0441e0c1462b0eead9e6b8dab464346e6e4d03d000b6c9195ce3519064359d02d473ac738e4249a84311e3e879cea8790a0f204135510232a4b0095fd40086f21946732c022faffac38f22390d81826e03a303bb2781886331d4aa7f30a07243add464e400450dbdf1d69912c03826560e3a115a6e00924e9cd59c6abf66de5a5a45cd1d34f092251828f24ab340bd0e70ef51435596af44866b3851e3b25c3133910b6a84617378b081f2f002f68268669b25833d23874b5e85679727cd3d60f1b4d4120a91b5e21c24d8ca4436906815ca11ec091c72fa996f643d6170f49bab9b7609f1fa9f407a1d63d488aba11022f70d9bbc1272eec155a75e8ca496955ffd3e060274071f2abac88806dbc4642513fa4a63e7a4a8d4d3251f219e38527200b942ec9837b91a569f3e2c9f4e9cba960f4deca7414cfa4124072dd6c2518712d184ad8368f925b8627d5417bd6ec3dbd217fccc7507378fd8381793ce614d5817aa91e59a42b9f88734ec01b138329821437a82180b2c52cd5b1761832c134f60b21a175060a3e2b1a17346582bb197cc47a6c7428ad7742734b75ce0429f4ce90fc8e6c1d2d64226b5ec0206045e0bc2a46ed124edd60760d9898357a71a238125c4823a2939435685db5d8b9210770d65cefccaac48799840d5d30a2c6916102e3c0614bc66b520d18ff40dd946a28c8d8349a8831285968178789545cc29ea122d3404357ca1cb1d144c60ef2085e526050f8d88ccd403f41a94e19a8c9d871b8e4cdc90a28d6460d2a672496a336c9b58ebde9e13d0e3205f15a83a933759b94a06d3935aacdc29ebd3916c8175b81fb26ee2ba807d824ad1023634cabd78a1b11d9c42201c0d156c8e46728451785d9bc614123b6b048d1322f36415e5bd69354d76b06a96903c111c5d57b0ebd2613d2e741e3a27cde51d725271a2236ba17999cb6a478ff45e21048357a0b2ec0368b8ee428af43264cef481a3933c0b889b238892c716a153abd7781dd92419909c4a5519f096dd84cc27d08f46144b52a3d2f2a36dc5a9c9526480017f0c68d1d7210decb348c9f76050b90805bb8ee75196abb5555a8f852c5f74c9ef728640691f11c01a85284f39d4a8b1f6220aaeed943e3ea894861380d33b1e3e79d8582a84cfa08109dbaa81886b619125db19c5a73be032c0f645b1d1c03f2cc19843df4fb970591f72c8d8e49e4a3d9ce1a53374d2d24e260db90a32b05cfd32a34d5801d09a20b0b59c1e8b0fc292a33f5918954bb550d25d2e09381846adba8b134b06216572bb2f232ee0129323ba539325c4067ac791338f7312f60f40b42e84614676da7895d340109317eccde9283cccb065afd8d81babac19d685489d2411180b872f54db7aa2a1655ce8c12631ad789215902c45a3a707343534832a185e2531a56d90edfadb21355908414517d9042b0b5ad1e4a704e968a716ebfbbcb9d14bdc2e3dcf4a74b618523e41049383a1a4e58ac6b4020650fbca757654203441a59c07aa4570cd138e3d51472e48050f4cc7f1042bf8a8c6a4af203286f5f4c25477b94164f1d46035030b393e4b0bac372abe3aaeb2372d89d721764b73d990d474c95548d89d02ad40194d1cdb5748f7f4da8203b77638921fcd3afecd1eba4ee3fbf32c8cd0b0801604612f40e9056ce447af938cb106d7f050331d530c58bc1f6b9905181d105123579d2689322f65453a29421b585a9b62b5910d6e2d6da5e77878b9b82a0f487a8cc41ecb0626e36746c13aa514783a158018b77475fb54a3be57f0d1fcf00e91aca379a249f860e643a7ae9943163779c5a647d7b39c026c037b3cc8701258b2933a0d1940769554652395c73925d5a18515a128024189945493bcacdaf483e5db64ddf001694a1d08c1e88b0e3848feb23d78f004a301ec0402fe6b50602d19ce49d082d1981f2d038d0a639ea8f27469c8cdde44fd593519f8d1e61763806ac21a1911b7253f1a271ea6781d69541b6e06a7961e38568ecf2fe6e441840679ca953f8dcf439513983b6cff595b5634aac4ad21178b2549880253959f409aa94ca460adf8cfe4a8f1e179b2e163334ce9a082e1274008ca4cb1e80f202703c9a631ff98a12a0551435e20cfd602b93fc09f43c8293f99db97531bae9676a5cc2f60845eaa9f6ccc1ddbd520b9bf004d47fe32b2493ff2c8900fd8f1972df9d7361ccdecffffffff4fe626c01343cf37828833ae26e48e6c21230046df845a2234bd72248742984613a8911a04171664d180b42a889720922261127e9059300bea6d62ab2385add494070920000230e224bc14a5412838550556a0f0d2c7a2c00f152398d0b113a09719e5b2cd96dc314874a0c4080b7b75970889fc43e6d05a000e6ab1dc54b1a40a971e3469f21acd312040c97d66da5b628a8a468a98f04f211e63c9504b61114f14464aaa3b80735725677eb460f882929ce543958c43483ceca522f000c89184d48f9cb45270f0a4b9028394d85f140670ac5414d24827a82c298027ce5985373a5c74908d81d4a1f0ac42500a2d4d4910b55e7b049078071966966f3c8512bb12a792204b00a08c71a09708d98a9867c6a793925751a5b761dca5bd346a8c89d052b17060824f2240253630c2326545a43d626f707c24bd284097a20964570f382271661884343d3840e0815406f36393200343d04e84eae082d6d3ac93910f7d005ea90ab536460059aa5822d2c6120428236b734646d921007994b2449096c803c511ab3669f40cc0432644944bcf131ccbb70c562c2b9648523196ecf540f3c68030441963770648190abdb3126fd4b86b6e85a826489026d200364b5dc0c8a0a4a12c6e930068dba422480743221fe0208ab2e991d446543f9421e3846645a3565eaa223ea4120a138dac02a60038b34b4d21195b0691118a0a03488c5a4c9602c3581d574162311ab125115448d67422b54208bbf4c60002e9ee073081801c07c51a1e4b8c9c4450dba0c746b0ad80211aa2986646f871852bc194536f89f8c6fe0644d890088c1af7d1eb244543016300be301456e09a5289922a332a71004a0128100011220573d4af244a1a788454f242692c8a052f14383813d8e13560ec2d45467926ab5191e09821a8a85d61ce1cf531f943208ae140921872246860087ac960e90d84b104d6392bb644c1d194080191af0f26bc9c45d70af9d19ac109859e372d89b31c44d218a9b1a3c76d421819055aaa5e6ae43920166838a9451035e7213d41def4003a98cc5c390132d5ce1c9a3b954346e7005522ad812b44434e81d9f290034f2b4279b07c12a371c58b544a033e2508bcd9b992a9b5317baf854ed49985ea0b195e6cb862a0394373e207172a941c32461a0161331721458db557b73468d1e4858929406a6545f6da5865b668a00a64254427e1165c84b72660e806cc9ad3c619c3c3481f3b81f46070f5c1c8da841b3dd2464031c546471c17ac89e54c19102e8f564067c451d5f7ecd5a0c5a164008e6b582e89d3e98c8b25196655efa442c61b546068d3890a8ae68e1472061c3850365cf396a4c9a22e93da0e3cdadeb318021a55c290aaad07a123552b607c8d3ab95103940452b28e138f535a704dcab48b4a0cb22cf1061c4a25a82604f9239524478f4c022e8cad88ff3845e5cac1c70b835b684ed48c6767a8b03a42c38eaed9b268897945b6c055124f71707a0e25bb29d479b01560ce9a54547af572f1f420c20189f0c5ac02372c1dc823a49786039c0c50b18a582561ac0f6bab4e582f25544577724d68350b478006476821adf472baa850a642ae54126a11a8936682b5ae978735c3e2a21ea4ece88ca950f3095629314d480485804e405aca39334305535e9f92a7a710239e630ea81381a35e894496d88c2013d049c9a2bb6221868ad1ab3f1f484032a64875e5d3afb83833f067471e8c1b1b521c28d3c76c031f66da0e0848840fecac68dd98687259818a9058111b71083acd21c3600c337057690a27b332b27ab5a27423c023149554e951341786e6288e9054413b0584b422ce28e0935b88ada10893c54a9125e9cba3445aac9089cd18b0b970f561faca83915e7e18f8985042734bd54c85658aaa4bcb5eb74e647a03620dc8012307fad84a402288065aa28e326294c599a362eb0e273a38af50f93dbff26419c0a5d0ade981b023347690b9a58478c6027250dc9db05556dc175189b8c123b01265756c2b98ccfab2e67a4ab1848b90252906081b2cdfbc1e18686871e081a0503b92c8af3a403439299315b6a00d9e57d3873fb24392a2a4c9d34a8f0ddf033aaed54901017a2cbc62a9e68a8b4c733804e9682ba5e6e2480a534028559b8e0ae1995057e106d90b383a29229561354a080d3a188c20e8144095061c658a9848c329949bda9352ad2615302365ce7800d494293174258855201570cc5a81c8025b8ed26ad0e1d012806c8c0d70a76894d40e0651b10cd5d2e5290b160f0470f1e1f44814188d08b5665d20fb617765acc7a5066cc7a18c3f68025c2750ce8039e3c404ae415811ea660d4786573cc00e1150c12a481d3248c433547251da80b1901b1ba335618289410ae0cc19c2e9c1881684ce62bc7fb9907326a0152013167762f2487b63bcf2b1deb00ba564e4a2c558a31073a3e487d984eb194761705c2a2b3166925926a70d340b7e60bc90d82231408d0430edc8d209d5e4d6581006b0971c09bc801013514405843c9c1abddaa4864c5aa65147331a4952b7ce4cc1d0260a2526ed132e29d05e3f5eeea0b883e20c0e092b3eb004955973d4a60899d6808e1a4218e0adc993d3a42c54092a67faeef4d549e11be3c0038a3c0334a9d10128235516c24e8c4571570c24583b9a3242654ba72d037444e8b1a1ea005ad60450c9aed712316117083c6a9ae0636a4007b2248c04ed4864c9784050911c6b70205b6869f06302083e259058bdc95b2d62325a61aa28c038c97b21ca8e02309c6831113027890032c057211e0c08052bb8429aa7f6450d1826211c68ba2346c0aa44679e24212392aa10d551a705934a4567fd59a0e9928003495a651926cd4152a0849c3c37288b0aacca25671380237d6994386c80d045c78e8cec130a54c432222a5e69348c805e210aa0e50a191d8e643c6a778d819502ce0ebd0218aa8cf6f06a13d4030811c2811988cac6d23c916561fab4bc89a2a08fad3d1f1102887d29d892c40a202d74e438a88389062e025786af78ada97bb32b4c5aadb52673a863179060b97380e0892c326993953c71489c3949f39787d653c402044800ec3943c353daab297c967344cc80f506af051e24690b314c695350af2268c1e24810ad211bf0e2ce8aad5826367a00f9c82027968e154206a59db8c238be50f0a613001badf89662e688490960627047cb93e188ebd451865291cc9e1c68c4830ca0b76883c2a44000be144293030108806284468f66050fb9aae2619618980f090d12d9e0354880072f663e76ec998971bad6c1358b9754cd8c08a72e20ced0cec4f8d6f0c00a744c40a3cc8a67aab156e345160666f2e0486993164db55156d996f8d3a644084e547fe009230d51b337204895b314b8deac69f124644911364a2f9061e8ba94c1a38501904a773bcab009b2293b408a0d0d9d207c71ccf82883868d99260258755923490d54804e276a385161767caa5989b8c8a7607c99128a8784039cc29490cd11b3344a120c229d9ca4f98349111b3c026e9c0d3b7978408a8584294f63852b07d4e2d24c8859b2d5e5540f392500beae0fc834c48aae3a521d22e9a9e352f350ae3e0124b56a8246819651ac882ab1c82d3b36058ca0829322ba8bd3073419264ad961e462ac92f4118b600e358da46492125d81e3520011c75e6e883c2c00c53d8a23618a4a82c0a6491a326c515a230c11b4a1008e2a5ec69845182859cbf07a9ab4b764ac8f0f32678492749aa800182b1dc2b2238083342604defce030c446298a226a9f435eecb410c47a9da571a955e47a81cd953ea35614f07396a153bb8187193d4e2c5d5571398360555a9429d729518e68a28bfc96078b604a8c4b9a97bf198a50b918bc641832bc106626a8ede0f7ea3a2a03940c80ad68a20c0d56052b7a1eeca111c5ad039535c74f9ac4e68ce86ad1c30e053f500ebc10874535661d4ce562a2e908e23abbd38e4a80ad407311c66e43a83d5fb127033943812967d6d620594449401a628b31551d2099b903a98c910b3045e2284264c9c41ba34149587c809f35474ad5a8ea19a1c6eb829b9a531b90abaaa429f03640419a02208e2e500235a5ac85173594c2c811e026232a46c842481bc0a0039544c21a7dbaa025422832626b14f2ad8c8e821259572c2cf9b2a9baa445830cd49b2675e7962e3473c92f13a1060c10904546b69194f313a9481f26c84ca0d4d47cb42a34cb8ed0aa42089c10f09e604143069d5e352df9c0f18961c00f9b804f4a040c27f9a1c0804a08d34d1b07a9d42ea92ae02848a7034a7bc721cb953b490525108cf12ac541d78c1b9e96f02a100b908c14310204f942e6231a93e5613813a1151c03ac0a203d08482b312640080656c760d30ebcb12aaa5e49a325e2281735f0300055872167b8ceec3832f94443571f3817a10c94b111a0423922e5cd8b393e52054a9549958c54630ef8604e8eeebc42c1638f45121699649d4d33499992a18c035190302940653d01276dd2a3b83845893b34bac4db860ab549ac9090310de6bc9aa3674c24246020b04265466b6d09dca707374e31c90c807ad651691350edd07025ae8564d5610ea22b63b888e1885586f987d1002b2e88717cfc30a5c38cfa74d46e72e5d8c3e0515ada0d353eb42a8a76150af04787f726886d0cad0499d6705577c17dd963a8572009a9184d78652cfb7b934103195a3d8464fac40208a05a8a065c65a090d4444a63edd115349fda785cd728c031e0017a7c9982f8487ac11769afacc823b9460918201aa3e992812b8e881407288570d005e74d0f118c91832baf1605f2c71d998f4d411238626121e603721d5306e6270f0a3345a65e64881a384af1072880d283105242079e04d2a407f33a90175443146f3acaca9bf00aca8b30406e8289c8bc38bf860148edb235e005934c4d4a7d11d0c1c3884605bc00827338df9c68fb62448dec529a2e3385de44ba0514705781354306e1d0944f561a6f9a46a1285e8c940911200f1e3c6777b104f49c38b80ecb269b0a2c95076ae86955a050010dbc2aed8213652c10205b7c756851b9b250d3cb03a827d50012a070a65073f4f6d28cf532cb34410b813e43494fb47cb97226254b904e463f3fc65e8020a20714a13e28d540612344c09070c6260629059960177336b55949a3f1a04aa0ec4d17392700a40203a00e972858540162ad4c88822385098b6483525d5a616133e18c418e4d9b68e5a1e963a70011357eca7aa4f1a09312e616428a83bb5265c6d86ca22196a4109a334804b4aaa757204c1419d499ad81c518faa4676d530b5599c03ca22344ab2445c41d4c095045629e390142c327116208b83ca114a42036ea89e4b811b1ea84590421b182218e6042dbe067230b9aa9506814c4b4126e5950bca34207fb189343c01a1225a88619263cc1453d1296b6840aa6cc4e1ccb5df0694f1a1b9889140b892203c6b48810448a6581011792203ceae3a51472119e9e30c73e2f9b35856e0952d5c15319860d708514d542d28858ed4441162346038e53344cabb2d45c70b26905a257172c20c3eea85831233a668c136989cbe9a80419a34974aa50f8d0480d8a0dbdd2caac0110f0e3c3a75b88ce76b160a3926154893495d46cc4388bf56cfc40d3c413850b981549c8689c416a32bab6a16a7954d9728b170160d9d85048d75eb5ad004953335b551570032746cb0f16197c967040d1811d63666c55af125b60608861c9568349243a7cc00be00a52cbb0e3c99b25b3a6401b55300d21d04b82a928c1099adc4c084304ef3509352604488a4d8630581bed727a6236c9f3e82cd48d32516a81e900f2e7500b0ed0a5100232e21cc1d111db0306822d3c4a8b0085190b302447af386da6490b551d1ed4f48c9cdd60d313d40586985d23197b5c98ba9420cd4e832bef90552f48fc7095a6098a18414ccef506a239556238afcc967413447fcc3ce1b283ab809e314e4e2e6ddad51e8434325b636a95329549c5a19b15695eb2256682d3112e00dea0ac4e438e9488204933cb21e994a016454551166c62c151561d687a46424d5800a54ae254d3ecabc69ddfdaa50499fe62b969fa92a3101ad10dd7ad32535a843732219310f25341059a405dfe067cfaa3a896314d03f8c19719291238ec593b83709505404a9d2877983c81af951694412dfca0638326898b1b265c8afa10cc1830a2898d0f2e74781002c448e563298204648d37b3ce9a752fee6ea470d14004ae0a647e6f04a06811c8f9e13287d635c2df11017934a9b1f179f6aa216a6eb8f82a28503bf3c4541395a2ecb2250032174344942cc9260084a6f4b542e40244e200a3151a010da290f1b055560dd505885072c38c5460161d2d00e019336210529609118003213201717839bff628d0d58102910749155a00305d02b124600459456799121152de1500d1d02304da84f8b0c7d3093e1e7f288cb9613305532991b78a1693304722ceca043853a526650e8cf0d49c3c178106783212f0a25cd2628123c41448d2008e983148631c906b02469182a3e46189d3d3481edb191537cda74e3666fc49314292a2332052a5d912248665c52c09ac5a4f2c1f283292629cb4ecc0d1420b0e176a0d48b9f08b11e62c6cd7875a6a30c8a931354d4b75eb0922cd85ae2c6f948e44287d9232ca8cdc570cb118c8a75007d45042f0a0ea4af82359cca880811183150b4479b60634450c41e3072d0fb0cd9b340be812cdca5184860e490fc4c60aad72834a13a90b327c8cedf511f16116200e4d8c51903a3982d2c084c912194e6804064579d385038b2023e6983031a2898ab5e49c3a32065142f3a9535f0e64da9b526560e2bc7a9b8c7cb559c3a84fa5496f000c4092667c11c50c232c2d7439507244d318328d8e280eea9cf0f3a7c64f9c2abc63a63206ba66740a7b6dfd5883e93c422d120a9095eb15b3626296274bc50d5164f4f9527bd5a3c4a53d485855143d9a44c7d51a2664788ed143b4ca60f8f9232abf724c620848aa68a237611384076f06b0c676e346cdae14b4cc4fa4395b32902ca44201b0907d889420020c1da3f8c4800be9e09202192053c3a1b0834f1a833761b09f2032680a382932000908a60e3a5aca54e481d504c098a637152e4ce8f812e5f51cab04230f89002776996980ac33d424d0578010ac107822200a8371968244b5a0d0259e36a1695569ecb980c19e8c2f330e2da94c98cc3bec99a2097460b41989da02528515210e648004ea728169c367aa449d3e5c69e85421821863c17468e2244a1c398dd21649c805c2d1d9872356da24c0356cfc21d3c3ae8f800c6958d0e8f1eb0120d62c378c36316a82011303a3a44aae13246268719383019051c906668754edf559b3f1064396393a3868f4a983760b01a6356ae0b49cc86548edce1c0777621532b94c9a42c24c51a3316bca2b344f66234cf06255484c12b90473de3907635aa0d5c8c12b8d1d114c426d7d4e723c64aecdb91568850e340e74aa2ee0181a2b0e298bd4856ed685304600585d3ac165559a29030a6d3814c751999f166918dd0d7257c821674db2d509c8a6360c56f8d03327c8190e65320a71591345cd8e195550b488a462019b016d763690d561f1e1551e516a88908ae50259814d4c408e53591e2cb7504ada78d3242b75a708c08b0a209d82842af2a96a8290802f648e553a20c0c102a945794553183a6a4618a9c853e98d1c1a3c0c177a8025c14106e3a054c9839b31a8eeac201e8d98fe0900602b44884b599818a0104a93861a53ae052ad5d1b289c91f084e249a2085bdcd512063e5844115e3cc8b015a6944390842c84380235e929081e0a55749a76c8903c5c34a0105859a24f48814728f77026dc804e637a6850b91b65563684218c4ba950ac9223f67bb848caa2eb1dc8192e54aae8c1abb55047e84e2a409501f3d99e234893862413c3326c1986a41a62b75ac6dcd628d2ccdd759350a29b727154289d9c3010d9e26154bd234cdeaf247ce08194ad2b40030080a821d6289708dc1e30a0add1d45b448d4cda03b60e6876749a966ac23ed35168124510f6ed2e848b12ae021029a1e7ccf034de2a52c6249149564b1c985eae9c4031622c204a0cc7a8c1211e70c8d4b17b6a69b831b5126e4ce7f17c36f92c38a0ccd693b85eebdcbe7fb2aeff85ae89d140a3761577a07a546eff85aa8bb7ca3eef3ae0ac75357196def96ee1d5f0b65469dca6875dadad951d0b3b39b42f3946d0765b43ae5b0031370155cac4636202ca539841f1fb32c9d61d0e128d3a71e5e30a0b2b561d09530a72e4479031753090283a8cdf0102c21288e9d6005a295a1c5744a1f0678cb135c4ab64111a48c8c8098f3900a4e123486206b048d0f5f88a683823c22d099630b8d821a868ed405519a2f3234449b14e97542479e1fc9595382ca35757fcab8a0b931066c4786b5bb2e46e8ae842ead696a4dc9a0f46745410400523331b724a7fa24b142d86c19d2fb00ea140615bda26c72e06882820b04ae90e9c161c1a34f2d58371200b9197940328e21fb8d9cacd7914f610977b01b3befa02f8c77a774a5d3094a57afacc89625093b3b0f70c878edc9167899fd67c370818dcd4e0733ea8c3adb78da4ad9fedc094af55095be1d96f02b036d09bf32389d98525f2ad3998472f8cfe1cf1af2a663569149289dd117ea46a15946a19ed4922a64bab4e2ffddc97435e1da2ac4499d49e59d8d182c4efc21f1c783060b744b2d6f7f1e28305040c01f3200c53fc23f59172e5aad529902d5e952a446890afde183870e9c3567c070b932c5499222407ae8bd3b943768b43831a2839f0b7e2accb9f981c0004181017ec6fc80f9f9f2c3f6ca756b56ab54a43c69aa04699121407beec85953c6cb95294e8e00d1933a9577335aa420d1e102dd843e0ffa2ce8b39666d667ecd847d8a775dd8aa5cad4274d65502b9724394a44e8cf9e3a6ed088d962054a92203d62289e8e1a3158940091a18e8f5b043e0a0cf041f241e373c647acc807c8ebafd6ab55a63e71ca44e911a2417ff4cc6993a68c972c549e283112a447fce62973cc607142c4870d16e62438d8a3608fda9e00637b8c7b847b585e7fb45ca91ae529d3243d8b1005e25307ce9a3260ba64a9f2648911215518da4ceac1a127460b14243e64b09b7b8bd020015b81007ac4f48011f504f55cd72d59ae50950ae50913a5488b0c01da43c74d9a315eb45481b2c488901ed3a77b27238f8a121e34d4c1b5313890670d2dcf591e007988ecaeebac56d6509d2c455224888f1d3868c678c942a50992213ef4a4cfa632079e1a30569808b1c102dd0407780ce04933c33366c413c4735db866b1b28ef2a4a9d2234584fee8a9e3260d992e58a6384142e4075ffce63175dcb171c7458a3b223a64b0a3737bb0e080da1d34b3b03bc43bc05e7fb662b54a450ad4264b9114150ad407cf9c376ace84e142645516294c8e0cf1418d3695773460ac3021c2c3053b15ecc0b5d96170c04edad9d8d1ced00ebbeb5a0b962a529f385d82b4a810a03d75ce84c942c5c991213d2404d283772871c8a8e302451d11753e68a81027c181813a6864c73ac23afd674b562b55a74071aae4481121407aecc45963468c972b519c241902a44a470f7e32933866b44851e2c3063a15e2da1a20584ba3731666a413a4d3bbfc68bd52756ad4a7399ae65c8ab4a8d01f3c72d694f972e54992207c3cf2963966b44841c203060a73263898a360ceda9c0173cae680cd11cef95fad57a84675ba144911a13e78e4b84943260c172b529c2421e2430f8636973a6cc468914204073917e4d4c9919333410e03363432394639413999d775562b53a13a618ac408511c427deec05943a64b9c2a509208e183f3963866b03051760348080d7671e6243430906616072cce57afbf5aaeaca2fa2e3d4214a84f9e3a6fd69809d325cb94274a88f8d883f35c1d3662ac380182c30538757070263c8083008e1a9c0170c4e0148370be0b97ac55a6406da21449d1a03f79e8b44943a6cb15284cd22019d2434fbac3cd41e3850a13203860a01027a1c19b036fd4dea4057843f606c09baf37adcb562c55a53c619ae46691a13f7ae8b84113860b162a4e921811e2237e9f269ccb1c3462b4301182839d5cdb023637696e029489b931c29d20730374d3ba6ec162654a94274c9216190ac4070f1d386bc894510fb509d3058b942544aa541e50667b23c68ab23c282644b4d96027e7d606020235016dc28c6d826d5a971f2d5656519d2a39525428109f3a6fd088f992858a93236c84b0f941e591e7e6984186058a111cd860603337818d83046cd4d0ccc28a6c826cfaafd6ab55a64275aa14499121407ceabc4953a60b16296b9cac51b24648750a6d2671c85861e243060b736f795fda1cac51b0b6266d8d803562c535c05e7fb55ca52a158a13a6488d120df2a3a78e1b3566c070b122850912223ff8e2378fa983468b14243c60a0530357a63726c1c101350504a8315323a686a806d82b972d59ad4e8d02d5c95224468704f1a103870d1a315db05489c2e4c8901f7a705e738983868b1527caa8ab4070c09066eead8d411a036a6906a4294b239686184cd3bcaeb560a542630acd284f98263d5234a84f1e396cd0984123460b95264680ecf1d8e1e24033a3c589101aeae0d04440a3604d009a312295139ace552b962a527d9926312a04288f9c3769ca74c952450a932241aad3d799c421a3458a33264274c050e1cc5b840507906967cec6ce18cf00cf5cd72d59abaca54279d2440952a34483fce03103678d19305cac4871b284c80f3e18ea4cde98f142c5093324cc78c850416ec2830506cca4051833033634c3eac235cb55aa52a13c6192c4a810203e78e2b4495306cc162b51941401c2074777e0ddd170a1a204880c14de222c28b346a04c803262658e65866582bcfc66b13a450a94264a9014110294874e59ac07ce9a3260b65891c2c408992064f478ec60266fc45051828c070d15e2243c584086adc08033313247324432c05ed75ab0565945f565aa0469d121427df4d881b3e6cc982e57a63c51420448751a6526ef6ac04031a2438631766328b835303006cd6c0cd898af31b9975f2d57a94e85e28469d223458502e9b103474c1a3166c26ca1e2e448103ef8d954e690b16204070b7212c42c10c326064d8c99180062886282bb70c55a550ad4264b65502d8f141512d4074f983861d290f192654a932341f8a64f3c1d345ca410b1210c86ba307061da16b0a19585390ac35c172e58a844710243e99122427ff8d879b3e60c982c55a2343922c4871e8fa7abe3860c1760509000018603980c76746f6d0d1020d1ccc08881218201f2bada82c50a15a9be4c93187d41f4a5509f3b72d694f972050a92203da679ca3b1a5f5898f88081ce2dc282035fd4027c21fb62fc12ece537abd529509d2e454a34a80f9e386bca84d142c50992205eaad3d799cc11238517111c2ac4b53140e085ad8017332f62452fc05eb96ccd72a5aa549fa6498c0a75e9a367ce1b345dca80d132a5099220d56974c7f2ae4617172a4a7cd0603737e14117055d0c140830d6c5d845d8e57fb55ca522054a9325498d120dfab3670e1b325db854717204889e346fa983868b14223c68a83027c10197045c905c04907101c085c805d8ebef962c57a8487dd254e991a24281fad891d3060d992e579e28290244ef752a6fc8686122c4860b746f11b63440a0b625c096b12dc62d2cafebac56a74271b2f4689116425afe68c9a3658e963566be70b102458911213ef4a479cc36878c155a4878c05007d7c640cb012d6b04b49c8d8111b5047bfdd97ab5ea54284f9626394224888f1d386cd08ce16225ca122341f6e0275e961c345ea420e121435d9c040709b2ac6519709685ec98459825ebba158b152a52a03a65aa1489d1a1417ff2c86983668c972c529a2019d283ca63075e12cbdd8c16264068b0203701c2022c6b6865580060196201f2c225cb552a527d9a2c414a3408901e3a6ed48ce172250b95274786fca04a9dae0e1b31548ce890a14e4ec2150707ae10b8520676e5eb0aabeb56ac55a7406dc21469d120407beabc4123c64b162a51981c19d2c38a1e8f1deedd0c17565690e86061ce8a042b0dacb011b0722666c56845d8eb3aab957554274b90aa2caa7208d01e3b70d69819e3258b9426488654f5a651e7b2bd116345090f18e8de222c58ab8256254095b12ac62ac05ed7ffecc9a8dc8aa56ad4a74c931811f293670e19953565fe0b4f46e58b16322a55a844514265c8ffbe3d7ab470d2edd18ca96f9519a5be55ea5bdd7b9f4fb70ab76e7509b7d578ea84f7dc673c75e1bd3ca50bef495b78af19c3ef5e1586dfbde5137ef7f2e0776f09bf7bcff7dd5bbeefbb176da1ef1edef3dddb6b4e9d7beed4b9077d9753e79e0b3bf7f6f6f2a2ce3d66d4b9e7469d7b7bef32eadc8b3af79e4fe7def2e9dc73f19c7bcd78e9dc7397ce3dd8954e9dcd3da9b3b917759b7b4fd76deec56d73cf7db6cdbdd8dd8ba7ee9e0b7d954f776f093bddbd65d4cd73dd3d772fe9eeb97b4f77daba7b70ebee455f69ebeeed55db3d57afdd73e369bb279db67bcd69bb07bf4e186ef7a02d1edcee51a3cf76eff96cf7f29eed5eec6cf7a2ef9ecb6cabaf1e94fa7cf5de68ab7cbe7a4be7abe72a9dafdeb355be7a4ba79e53efd9c64ebd674b9d3af562e7d4a9d74e9d7a532aecd48b3a9faf53cf65b671d4a9e7eaedb94ebd2afcc64aa7d2a917475b38e9d4d38bb6b01b4f5dbd2aecea45df6a1276f55caa5eecea3d9d4d572f7e525bbd78daeac1afb4d56b4e9dd1562fdaeab97acf564fcfa5325b3d2895aa6cf5e268ab6cf5a653f7e94ea954774a75993035da26616aec4ea554d8954aa9d1574a5d3edfa694fa74ba7a51eab3a54ea9512ad58d526367940a855f3c67941aa52e9f2f5e940a3b5bbc28f55d46a954f7a98c52a3d4f6f974e124945a7d32a9aeb41abf7b713c75eec551e75eec2a9d7b71358eba7bb1abf774f762f719c3d4aa9b0753e3a71b8d52e3364a8da9b19ba4c655386ec654388ea754388e5d29940ac7d527158e5d2a1ce729a970ec3e93d4178eab4de90bc752271c439d709ca774c2b19b74c2f1b375c2f132eac2d5168e9fae5e0bc7cd690bc7d2168e992d1c2b9d78782d1c57df38baf7c6b0bbf7c64bb8dd7be378ea84a96ffc6ce3379e4edfd89d3adf187ee377d946e137769f52e91b4bdfb819bf6ffc7cdff88d5d2a33fac66ef48da16ffc46ddbc378edda7f38ddd3786dde772f9c6d576f9c6af52d926df389eb66fec3e63671c2f5dd8194b9d7135ea8c5da83386dd24d419c74f670cbb4c67ec2e9d71dc8c93506a33de7be166ecea39e166ecba70338e9b4ceadb8ca7ce66ec366317769b71157e5da8db8ce3b619e745db661cf3b9713c756328ecc6d1a81bc7d1367e3edd18daba7153d9bab193af8ddf378edbd88da76d9c376d6357dac679d136ce7bb6711c759f6dfc74b631b38ddd679eb28da554b83a85beb1145a9d3edf24b43a6d2a63e7de941975ee4da1cebd698ce7dc9bc6aebb37855b776f0a85dbbda9b4dd9b4aa1eddede943a8d9555570fa64e612a4c9dba4c983aa54e5d983a75a12e2ca54eabb02ba54ea7d568943a8d52a7b00b8552a7b1933a855b26750abbee72499d3695d469bb97a44ea953f7598da7f1140f8f1a4fe3a8bba4c653f7c96446f39c703c8d5d178ea755a6abf7c6d3e7fbc65337fac6d3a6b285bef1344ff9c6d33ca5339ebe52379e42dd789ad7baf1b4dac653d8d9c65397d9c6533c3ce7b28da7d3a70bc34e2a8fa9dbc375a93ca62edc52794cabd22933ea84a5d269b585a5d3f8954e63f8954ea3ef2b9dc2efd3f94aa7b1533a75f39cd229ec2e9dd2a9ab7436a5d3e7d3954e97ae2b9de6b5ae741add6ba5d367eb8c5be9d4855be9b41953dfa994fa4e9751ea3b7d52df690cbfd32a0cbfd3e8fb4e99d177ea465fbde73b7dbed3a5f3f94e61d7b97cbe53b87dbed318ef9db6cb779aa77ca7cfd6a97ca7b0ab54be5369fb4ef7dee8140fcf1975f3dae8344fd946a7cdf87da15337ef854edda7133a8de1163a6d4ea9cfe9defb8cb6ccf739853a9fd3a9bb743ea7cfa9d4759f5368fb9c26dbe7f4f9a4e64d63d899378d3af3a6cfa7336feacc9b4eddb8cd9bf239a76edc5263e7147661e79419754ea74fe7b4fa7c3aa7794fe7348eba4fe734ca744e5da6eb9cba4be7344fe99c569b53f78987476d4e9954b83995429d7073eaeac5f0db9c4adfe6347edfe6d47d9bd33ce5db9cc2ae9eb339953a9b5337ea6c4e974fd7d99ce2e13961b7397d3edde674e936a72e1ce3b5cde9debb6c9b53293576a7ee338edda9ab9c46dd29fc3aa3ee146ea34ea83b7d3edd699eeb4e97ae3badc6ce377661a53b9dc6ad3b6ddd69920a2ba751d7859553690b2ba731f5554ea3af530abfcae9147ea753a7d2a99c3edfb7a99cba50b7a99cba515739554e5db8554ea3ad725a6da7ae5e3b7599ed94da4e5d25b59d3a9bd3376ea755bca9336ea7cb683b9dc6d4673b8d9fed34fa429fed74d93edbe9d3d94e5dbc763a9dbad1653b9d2edbe9b385a970b5dd8b52e12815769f54984985dd251586dd64920a3fdbea324a7de12693fac2ae3b7d61e8fbc2ae33ef7d61bcf7855d65b485be70ece6295f389e52932f1cbfed0b2fdb17865d3d27fc6ca9b113ce9b3a61f729953ae1f875c2cca8135e3a994e386e329db0fbc473c22e73e984dda5136e2a9db0ab74c2d1379974c249270c6dc6d526bce73af5f4a254178e5b1776c62e1c4f9d5317769f492855397561386e5da90bc35157faba701c75e165d485975127d4855d18ae3e5d18cf753e5dd885f9daa70bc72ecc7461774ac5736168ac749f782e0c2f5dd8859f6ed285a3efd485e1b7470befb570d4d56b61d86db6b00bb7f0d2296d6129b485abcf670b479d2dfc74b6f0eb5cb6709eb2855d650b47df57a96ce1d8994cb6b01bbf541e3d9c541eb193ca234fe9a4f288bece78ea5279c06e0cb7541e506a1c6da93ca83c964e680b57a5eeb3470faa34a64a9b53aad49dc254a93b7da530555a8d9d30555a954aa9d2a7534a95369551aa34eacc9342a952e9932a7d3ea952aa74498d5f580abbd2e90b4bab31defb425f581a2bdd1d2f2c8d9d782f2c75a1ee33f9c252b787139646dfa9139656e116ea84a5eed2094ba5b08be7c2d258e95cbab0d49d3a5b58ea2a9db1b285a5b0bbdc7ba5d51876f55ea9f48ddbf895c6d357ea3e93ce2955fa4a9fef2bad465fe932fa4a5da5f3f94adde72b85dbe72b8ddd570a755fa9cb8c529f5226f529cd53529f5297b9749f5269b57d4addb87d4a9fd2d88db64f69dba38753da54c64ee9f38da74ea92b754a61d78dbe4e695ed4298ddfa753fa7c3aa5d1d7653aa54ca7d455ba4ea90b5363a553aa7446954ee91b6d9dd267ab744eab4da93b6de1a6f48ddfa6d48dbe4d69ec4aa3ef1476a571ac74f160570abb52a92b759fd2d7953e9dcaa82badc6cf36ea4aa3ae34da2a9d4f571a47db69eb4ae1d695c6d468eb4a5d65eb4a5dbd56ea4ea9ad342a7dbe70ec8c5b69336ea531dc4ae317865b691edc4ae3a8dbc2ad1476a5ad14769b71b4953edd682b6d97d1560a7db65257d93e5b29ec94bacfe5b295c6ca56ea46df1eef1b759955be1776a37b6f74ef85f5de67abf7ba4be734a6beb01b535ff87d9731f56dbed1b62985df38ea3ae137da32e17709bfcb377626e1f775a7af54ea94be5157fa569fd1f78dbecee8eb465f57197d6327f4553aa5d0eaf375f59ccf77f97c9fced7553af1f05ed8c5c37bf1f05ef85dbeb09b7c63e51b6d5de50b5395ca37f9ba3055394dbe2ed3f94cbeb1d2dd917cab6ef2cd6bdf981a75a73055498deebdd1a7abf746a36efc46dfa83b7da3cd187ea3ee73f9be51d775a7ce37ea3e936fd4553aa3319f1b9deeb951d8d573a3cf168ea96e34bae736a56e341a2bdd27d38dba7aceb865bad1a5eb46f394d168359eb6d168d499276da3ae9eb38d36e32ab40abbf1b40ac57bbad32a349e52a14ae7940a85be2fdc9c52a150aa3ba542dda7724a85569fd227151a75e2e151a14d2515ea3af55ee894fa42a92f14fac64e2a8fe90b65469d51f885baf00b7db6f00b8dbe2fb41947dfe80b85c26fec8cbe5028f485569feef285ba4f67f285ba3035e67342972e9f13cae784ba796d3c7542a75327b40abb5327147e61d809759fca167642dd981a7542a154a813aa74429dd0e89bf77442e326d3195d3aa1ee73b97442976f1bbbd0e6d4855661d885c64ed8855695b00ba5465d28141a3f5d6875f93e5de8b35dba5065dcba5017a64e5b171a6d5de8debb6c5da8dba385c62d94cf09c72db419b7d067eb84ddb8853695f1b4854ea72d346fea84e1161a479d700b8d3a9949b885ba4f690b8dbece69b485becc680b75a1d116fa6ca1d016eae6b5d0a8b3853e9d2d74e96ca1b11b65b650a593d942972dd4854e954eb8faacc2543eeab3a98c529f701ba53e9f2eb36532a9cf6a9e92fa7cb6d5f7e9eabd4f9719a5becf25f57d5661378edfa7f47d9fcce8fbac3edde8fbcc53be4fd88da7c9f7196ddfe7b27d9f4bb8ede17c3ee3d8f98ca7ce67f4753ef3a2ce6712ea7cba79ce678ce77cba78cea73b8dba4ae7b31ac36ed2f9845be7f3e9eab94fd88d63f7197de156fabacf69f4197da153a7fb5c32dd678ce73e5d98aad7ba4fbcd67dc2eeb2759fd51eed1376abed337652a77bed33bad73ef7da67abd73eabf0324a6d9f4d26b57d469d71fb94beb1b47dbacc96ca8cb64f29b47dba79edb3ba6c9fb0bb5cb64ff84db6cfa79b6c9fc9f6b9f746f3f2946f34cfc57346f35c379ae7dc73e645dd3876e6b9b1332fea3695b1332f76e64da7ce3ce8bb64469d79cba833afea4697cebca5332f4fe9cc93529bcad8cd93ba6e5ed475f3de176e99d1362f9e5277e445a93be0d719a5ee709fcc287587144adde12a9dd41d2e7349dd517d7754dd284c7d77b879ef8ea8f3dd917c773cdd3d7747f5f974774ca3adbb634a7d9fadbba3478f7607dcee805b2ab3dd11bb79ca7647157ef59c503776ba71fb7ca7ce65b47d4e9dd11776469d6eecd48b3adf1876e368d4f97cb6f134ea7461a7741a75469d6eb49d469d55d849e51175c654a9f28d469d6edc42a3ce2a4cdd1175c64a77473476bacfb61975c64ae7132fea8ca32d5ed45985e329751975c2efbb8c3a5de8ab8c3a9d4d65d4e94e9f5429d40975ba512a14ea74a751170a75bad3a91b7d3e9dcf96e93e9fcee7d3e93e97adbb74465fa5d3553a974e38596d2ea36d724a6dc630b55985616ad37d2e616a338e529b559819a536972ebc8c529bf00ba5369bd1164a6dba4c6a33496dc2d4b7f974e3b7194fdfe6f46dba53f86dc22f0cbfcd28fc365de9db5cbe6fb3ea46dfa60b7d9bcd6abb743edfa6fb7c9bcbe7dbacba79cab7594d26dfa60b3b9b5267338e3a9bcfa7b3d9643a9b2fec36a36e53fa749bb10b7dbacdf7f9749b4de6db74994bb759d56b36e367eb3697addb5cc26d8fb699b7d7365dbdb6e9eac16db33985dbe6126e9bf09b84db66552a6d9bcb68db84dd780a6d9bb00b85b64dbc67db849d6d337626db26b4ca8c5299701b3ba9ccf8e9a432dda593ca9c4e5d2a7309b754e6de0b33a5d41766566157f9c2ccd80933f15c27cc5c3a6166d47561663576eab530733a6d61a62b6d61661c6d61a6ab6c6166b28599d1bd9729754ea72ff3e94aa72fb37d3e9df0cb74a7efcb5cbe2f73f97c99d3a9bb5cbeccb87d992ef5c9dc7b9f4c57ef7d32abf0fb6442dff7c97493ef93e94ea550e793e92e9d4fe674da3e992edc3e99cc65fb64ba7a4ee61376325d26d37d32994e668ce764c6d1d6c98ca76f73ea32dda8931a7599ee338eba4c178ebacc29d51975994fb747cb7499d496c974e396299db6cc69cb8ca32e146e994ae7136e99517799845ba6cb6ce196296d992eb4655661f7f96c9955bef6d932a3ce9619bbcc65cbacc2543c57d946a354bca74bc58b9d54182fbaf7c278f1f485f1982f8ce732a34e186f19759d305e73eac2787114efe964b6301edf8b07bbf1f4c57b3aa7d3170f4a855f3c374a8de1172f0f7ef1629709bf78f1d4855fbcf87df196ef8bb7f73e9f2fdef2f9e23d9d2f1ef3c59b52952f1eb47df1aa4fbce694fac45b3aa94f3cd8dd7b9f78f0fb3ef1f294ef132f6edf279ed4f9c4c373dd271efceab54fbcb87de235a7ed13cf85b64f3c66fbc4e373e239f162bc67ab9c3af1a0b013cf859d78b0cb8c3af1dca8136f1975e2419f4e3cf7e9c473e2c16ed289b7e7e2c1ae9e8bf76cf59cf1d4c573955117cf7db65017efe9ba784bd7c5735dbc670bbb4b17af0ab75438e9e2b9b1137ef55a3c2ad319b778b0db8c5bbc671bb778718bd7e241e116af3a6de3688b5755465bbc688b37a5bed016affa74425b3c17dae23d9f2d5e3376b67871d4d9e2b9cb16af1a3fa9ca16cf55b6784a279c6cf1f65e38e9565db7eab670ccc7e74e5f299f1b75b64f299fbb8cb6523e3776f2b94f279fcbe7ba53a71276f95c774a55f2b9eed28d9d4a3ed79d4e5d259febba7acf57cf75f35e3df7e9d4735d65d45d3a9bb1ebbaaeb2da4ea7ee0bbbeef2855d77dac26e0bbb52279587fb6ca93c5c77f94e9d52d78dba51b7a974c25137ea56df1d51d75546db66d475a34e66d475992d33ea5699b01b75ab5037eaba53f85d46dda8bb8cbab0bbf74aa16ef57d4aa16ef475c62fd4755d650b27a16e75f926a16e1edc26a1ae3b7dba6eec7cbaf01bb77a4da9fb74e3a8d209c3eef2e9eebdc9a7d36532dd187ef1f05c376e95d4a51b3b97aecb5cba55a5fb36956e75dabaf1b475ab4fe7b4759b31dcba51b875ab4fb875dd27b3756197d9ba7870bbb7acc6d46553195397f194ba74a3d465124a5de629a9cb2abc8452e1a5f485975227bccc8b3ae1e5f3e98497319e135e425b175eeeb5f052e98ca72dbc6ca12dbc8cbecc165ec6782dbc74f5dea53b7d97f0fb2ee137fa2eab6e5ef47ddf65d4d9943edfe5f3f92e99ef52f92ee13756becb6a8f1e4e3de7d28d6157cfb9749dcb6afb944e9dcba973399d3aa153e7b239754e9dcbe5d4b98c61e712a6b6d337ea5cc26e34ea5cb650a8fb742eabb133ca742e9f6dec643a97b1eb5c3edda9eb5cba4ae7b20ab7cee532fa425be7f2d93a97d01666b6cea51bbf7aeed2553af5dc65ac746377597de178ea2e61672b9dbacbbca9bb8c6177eabad5982a7597cd58ea2e63b895bacb6ad45dc6d367eb8cba4b77ea84bacba5ebc24c7759c573dd69d4c57397b0eb2ee157e93a95ee1276935057e92edd65ab749753279c74976edc26dde5b37597d57619dd6b97b0bb6c4edb2533ea84dbe5d25546dba514da2e5d660b6d97cb76e9c65465f47536a754a5ab0753a94ae594fa3a61aa524a554aa94a17a6c64faad24d5295cf369e566165155636a75458d95446a9b0d27dba532715563e5d2aac7cb64e2515562ef75e18564ea72fac94beb052e98c9db0523a75c24a17ea8495b19ba774c2ca3d1756e6b92eac54425b1756567bb4b0f2b9d7c24ab8396d61a51b6d6125b48595315e0b2bdde7b2859579ca1656baf15b7d9551e794fa2add294c7d95ee534a7d9555b8a9a4becae81bc7af329ebe4af855baf0ab7cb6f0abac4a5f25ec4aa5aff2f9becabcf755bacaa8532ffa2add68f455465f25148e95d465f455c6b0ab8cbeca6ab47da1d057a974425f650cbf78d057e9425fa51b7526a1af320aa53e5f25fc3e5f65d4f92a9fce57e9ba78786f4c8dbeca57194fdb57d954c6d4a71276e329f5a99442a94f6592fa5446f7dea772497d9fca66fc3e95ccf7a9cc53be4fa5d4f9542a9d51e75399843a9f4a3c3ce75399a7743e95ca3cd87d2aa3adfb5456dba772af7d2aab784eb87d2aa5ed53f974ea3995d5d8a98cba3d9cb153e93e95b053197da16d34ea54c22dd3a98cc2d4a6322f4a6d2add679e92da542a9dd1176e2adde9fb3695aed2f9369555370a75369578aeb3a98cba794a6753194fdda6b20ac36e5389e73695eed4759b4a69db54bad3b6a974996e2c6d9bcae9db3695af530a6d9bcabc67db54c2ceb609bbca67ebc2aeb20a53e3a8ab545661f7e92a9951e7d3e92a5dbd96da2ae32693da2a9555f865ba71ab6c2ae356c97c9f6ddc2ae356f96ca9d356e93ee369ab7ca3ce69ab6c4e5ba53b6d9531dc2aab30dc2a63570ab7ca25dc2aa36f126e9570ab8c3adb58da2a9f6dec94b6cae85b8db6ca69ab176d95ee1b6d95d1ea32da2a9f6db455465ba514da2a5bd885b64ae8b355e629a7d458d92add67b255469dc956e932db64b255f6e8414dc26e954a4d56a7d4e474ea469d536a724a4dc653264c4d46dd254c4dc26e5e949a749fcb283519bbcf273599f7a426934f6af27db67878d46475b9a426dd6732494d36a7543819dd7be1249c84be2f9c747b445f3819753e5fa5f285937953279c4ce2b94e3899a774c2c9e4b275e1a4abd7c2c9e9b48593cb680b27a3ce164ec6c9ea9be47b93c9e474fa26995127fc269379ef9b9c52a36fd28dbec924f44d3e9d6fb2ba7c9371fb269770fb269b53ea3309539f4997fa4cba53eafb4c42dfe9f47d2697effb4c2ea3ce6732af759fc978da3e93b19b176d9f4917da3e93eeb27d26f394edd3996cc6b033b9849d496772197526dd3c6772e94cba4f6555e94c2ea36fd54d56e3a99b8c93f1d34dba7aaef3e9265d3cd74dbad598dabac968eb269bcad64dbacf386e93b19b376d934ae7b44dbad336f97461b84d469d2d136e93d5a70bb749b84dc26e34da26db681b6d93ef9b84b6c9e7b34d56a3aff2d926996dd2652edb64f455b649f87dba4d659bac26dbe4d4c9d7eeb54f2ade5eeb42dfa9bbd756dbbdd6cd6bf7da77d92a5fbdb68d42a931b57d636a4b6de32995496d63e792da52e3d65d5263671cb7cba81bc76d9eb28d5b37769fcdb86da1cd78dac653e774dac64f6a3b9db6b1d2954edbbc699b07bf7bce186e63658b07b77b2f0cb7ae9e1386e1d665465b186e9751ea0bb75528dcc22f146e972e146ea170eb529970db465db86d5db8553a6325dc369570fb74c2d2d6759f52690bb7ae54da5699eff395b631dc4adb6ab4adc6ae932fdaba541ece38dab6d1a8bb378db6cf961a8db6aefbcc8bb6cf16ea8cb65598196ddd2733dac22e33dab6f0dbac3edb68bb74a754681b47a92fb465469d3d5a680b53ddb885b6b01b3fdb77fa6cdba6f4d9bad1f7d956dde7b37d3edba8eb3edbe7db7c95cf164a4d3e5b28dc3e5bf73975c2ce1676b65067ebba30b38da32eb37599edd2d9c278ed127ef1f05ad8d9e2e1b54a67f285ddd6553af5966dfca42edbd8095db6b1d2b95cb6cb651b4bdb653b9dba70ac6ce13756b6b1b255b64a673ca5beca76e954b6ae9eeb6c2a95ad52d9ba536ab25dc2d469b28d956d127ea7c9d6d57b93adfb4cb6d177196d936de79cd96794c3d475e1bacc367661d624c7ff1d278b228a4c4a8917767b3ca7b133c986e76c4ea7b00425dc9b043a09716d4af7d92aa76d645106fd12966e4b16454a1685cd7db2b51cc8a0a4fa2f614a4d924159b3248392e43f8c9341799bb66c1148b04ddd0e530e4b17eac624a06e4c62da8c9d32c89ec8f95fc020f25480889fd4a95319e5f016c36df1a448e6e48fcb6452a711a66d73cf4db1e7a620f19278beef0a6787e7fb9258017563122baaf19244deb47d91376d5f3cdf7785fb5c92b873a1ce1887db1b9b853aaa8dbc690bb5b3cb9bb6285cf8f5d0ebfff0218ebad029b37d86504e9d285c3784d49db641dfe51232262e9313677b633636e9bba4be344eb624d6cf6c9f786c2fd85cea4be307dcbccb41b6c4ed8793e026e14d0f4407152d5eb8d0d1ef353d8525b8c9178ea7716d51e71376a16043863d64089121438a439ed9216732c48993bf78e2ff26434e1c3284c890214e86fc4386341952448d2cc9a6ff7112dc24b8b541df25932cc9140306a653370a7d97d116766167a77497aff2c58b9208e14edbe68a0c77b02be56ba1cf367ef95aa83b6d93d369fbcc001747bdc0d742a77d87ec08dff19c1674dcc12f2c611a3ba11aa0cf76da3e3dc0ee8a258916b113ea3c91a76c6317cba7eba4e1017e611884cb6ca75317c574ea2a5f7845dc42e137c4b2edb074f56077eab6705d388472ea84dad94da36ede1bdbd94d63670bc7767631fc4aa5d4e99d0b6d61bc29356e6317ae32da3edb0e7193e974f7ee9e2d3556b62f5e94445319b7d2a9f3433c7d57b82ededdb4da4aa7d3f6a98c9b4ca7bbf7e245493495712b9d3a3f54e1376e97d116c5b38dbeceb8855da98a670b85df14d369fbf4d054c6ad74eafc30adb62bec9eae9efbbc8bbece58d94ea32d1cdbfd7ff0b791ff3c4e5604c37fbabfe1fef33859110cffedfe7efbcfe3644530fcc7f03780ff3c4e5604c3ffb3bf83ff799cac08867f0a4b80ddd879076d5d389e4e70992dfcc6d3094a783ac14d829b04bab8b6187ea552eab4cd85b630de04bc3737b7b9b7369984835b5278c3a36b7b9d12a66edcc24a097cee32ea9ca084a713aa703c75325d78c2cb845be504d85d3e9dd4e40417fa3627bc29a62e8dffc4931559f0a8fb64cbc0b6744297ee33d946412cdd67b29d3a3f445fe734eac67cee5d73da36efdab85db6b10bbd73a12d8ce72e5f14787378722d05bc38b8b937b79593c373e79214e8e05cb816ae0ecfadab8bc38bab53ba72ed5c52d9becdc5cde1c925e5e8dce8e45cb9389742dcd2cde573bb86401f1a4dcdaf21981ad39352c1f5192d7cd8386475f7e1d7284be9a3de949c585ed401ef2134fde8261f1e7d0d905a1d2204af2f4ae180ede547c7a168a1c0d66065740237951a919c84bb90820485e411ca667d2ad8527aacedaacd88fc263dd57ff03c8179025ee557a4a66436545dffb2844a932902d517bc18ca0ea2b87d1422086d28c5f141b4315dea4c9f263b8661b160f2f0365b7a4f63c8ac8d680d7202554ffa8ec5cb7bc4fe3498036f7e2155a1136ae528bbca10d76575b2e40731a0be2001150bc4cfa56c9769ad4b1582d24af8d858180a4e6437abb208c5e6f0694383d464494722579a32d5573d3fee688e1d3b424eb0dfa6253ad4aef65a9403d85d0f1a75a1205358578e1c7c8e81ac16f521504fb932236798f18350a081e5f254f0d78fc3db28b5bc8e23016d0bc459837806ca24780062e5a6b4840bfe38ca12d89ae45663c1d7f9ebd599c194767118691b2ba4bc0eaae39f7e9e1a5506042f54614f2b00d43c1125011acec31f565321b1dba9412b6ba0e3e9ada8706d6042acb613b183b56141549bc1d5eb6d0a0b0dc02fc3ad7e0279c80ffcf49c9f3fb0d03370c18fbc7bce497e20a93751edbf5e781b633cfb2d7813d1fe6bdd9be8e4bfb679069ef891ef335be847c63d5f47134bfc57e7fb087cf6bbf4fc104da4f45f633db3837e24dc59a1f35f83f1363a9ffd2abc8bd4ffafcc335beb479a781f153d5fb5bc8b09fe3f156759e17f7474e097897fedcc7347fa73d8815f2efeb534ef22dafff7e1ecd7a67fadd4db18f7ecc73e779b0c7cf023db375886e73f00cf1ff4f784b711cbb39ffb366679f6c37b1bf93cfb31792e2afa73c9bd87b0fe2b803771c77f65f21efafd5701cf6c971fc976b6cbfcfdf73eaa79beee3d0fd1fc6e71dec709cf7ebdde442aff75cef3d0c7ef56e3b928e9cf55e19925fe6025ce06fdfc6b60cfc3f7774bf13e2e7abe6e390bd4f423e9bc8bc8ff8fc4fbf8e6997dbe5a7a1b5d3cfb21781b5f3dfb057a83a13d5f553d7f122becf2afe53d0f95ff0156efe39de7eb91f791d7f335d2fb48e4f9cae2b98bfc6d7dfe8e6cecb7999fb711c6b39f826716f987c978ee4d7f367b0676f8913b9ed9e78f3c3eb7dddf3e9e9fe2ffab67a3f18d56f79c63fcc00a6f6280ff3af72e32f9fff23c0f3dfc07aaef2392e72b8ce7e0dfbfd6e07918f71f803eb36e7f1898e7fce507c23a5068e95f3bf23ec678be2e78ceb57ee395e7afeabf36e7f939fe5f9f87467eb7e4bb68e5ff1bf4fcb4ff91dec5bbffefc9fbb8e5f9da7c17cffebf0defa390e7ab8a3771d37fc5f53ee279be22791795fc7f779e857afeb5bde74ef467e3f383fc5d7c1bfd9efd16bd8d3a9efddcdec636cf7e3ddec521ff1f9c371882e72bad3791edbf6a781f333d5fd3bc8dcd67bfbab3c154ffda817711c1ff97e26d84f2ec87e31918e14726cf7ee5f8d7d8bc8995feabacb3c148ff9ad933fbe747babd89c9ff4ae4393bf8d9366f6283ff2ae24da4fed72bcf1ff3b7dafbd87cbed63db3f08f0c7b17a9fc7f81dec72bcfd7e53330c08f9cf1fc35e75f8bf42e4ef8ff6a3cb33d7e64d9731ff91bd5f317a47f0dd4fb28e0d90fd5bb98e4ffb3f33c44f01f48f0dc6bfed6f53ece78be36781fc93c5f793c0fabbfdb8c77f1c4ff577d1e7afaddaecf79d56f1cf136e23dfbd13dffd1df15de60fd7ca5f4cc7ef809007866ff1f153d3f4aee037fbf6fe297ffeae75decf2ff297a175ffc7f449ef38e1f48e86d14f2eca7e20dd6e1f98fecb94ffc1de9b9effc39bf89a6fe3ff639b8a58d199efd00bcc1063c5f533db3177e02c27791eeff6b0f1486fad7943c0bc5fe35b7b771d6b35fa167a0de8f2cf22e42f9fffa9c0de2f9d78cef61f15fdbf536fe79f67bf2265affbfe01b2c3f5f2b1df835f7af8539db7f7f8ef0361279f6637116b8e847be79ee1579a8e23fc0e47948e5778bcf0a11fd6b439ed9b01facd373f0c08f8cf4063bf07c9df536b279f6abbe8fb79eaf899ed90e3f81e3db08e7d90fc833a2e7a2ac3f578a3751c37fcd71b6a3fcddea6c2bffdef426a6f9af889e738f1fd8e979f8e977fb3bcb267fe4f04d94fe5729ef6394e76b8ee70ff80fe93957fa8977ef2387e7ebd8dbf8e5d9ef79f6cbef5fabf5fc65fdd754bd8f78cf57ba7731feffc8bc8964fff5edf92bd0bf96e93998fc91a6dec458ff1fd91b2ceef98f7d1e2af80f2c78cef10fb4f42ebafd7f36dec520ff9f9be72f53ff5aaaf7b1cff3b5c9732ef413e59e73fef384b341173f32d4d9af22ffda9d3791cb7fddf3268af9af81de4703cf7eb2de474fcf573767857bff1a8eb3414fff1a81f7f1d5f335d0f390cbefd6e37dc4f47c45f33e7279be3a9fdfe7ef086fa295ff5ae77908fddd4e3cb305feb016cfc3bbff40a9e7fff89bf82e96f9ff24bd8f7acfd7106fe29bffeae8997df523473c0713fc4849cf5f75fe3549cf39f90371bc8d299efd90cfecaa1f19e27d94f0ecc7eb1918e54746797eadbf633c7f85fad7ac6fa2f3bf2e791fd33c5ff5de47f8f9dae15d7cfe7f5c0efcaaf0af897976f43ef23d5f473c37dcdf7bef6296ffcfd0f397a77fcdd41bccf87cf574f60bc8bf56e76c70cdbf267c13f1fe2b923711c77ffdf1061b7bbe8e7a062ef9914e9edfe9ef0fefe280ffcfc3bbe8e4ffdbf30c2cf22397bc8da79efdf0bcc14e3cff15781b553dfbf179ee3a7feee760a27f6dec3da4f55fc9ce361ae097f35f7bf326aafaff866f62dc7f6df1dc0bfee6f12662f9af77de4659cf7e84dec753cf573cef63f1d9afd3f3a0b35f36feb535cfc55cefb109cf2ca91fd9e16dccf4ec97e67d643e5f29bc898ffe2babf731c5f315f92e4ef9ff00bd894dfe2b9be7def07790f710c06fe0c159e1a27f8dc8dbd8e5d96fc7dba8ebd9efd19b98e1bfe2787e9bbf0fbc8f759eaf459e87407eb71b6f639f67bf266f62dd7f95f15c74f4e7a27b1345fc57bcb791efd96fc473eef4133fbd8bcbff8fcadb68e1d9cff8fc0dff513d67507f31ee3ddcf55f0bbc8fd7e72ba3b771c8b3df8ab341e98f5cf5cc86f99107de450effdff35d34f1fff1bd89c6fffae02c90d28f94f3260efaafa1dec701cf7ea9de4749cf5731cfc1e58f3cf5fcb5ea5f63f52c3cf1af8d7866d7fc4806cfbff1ffd6bbe8fcffb2bc87bcfeabdadb48e5d9af7d1f453c5f053c0f5dfc07e03ce71a3f90c5734ef3036bbd8990fe6bab37f1ff75ed6d8cf3ec17e44d8cf05f4dbc8d959efdcabc899dfeebf57d0c3efb617a06befa917cde4625cf7e32dec354fff5eb4d6cffeb9077b1c3ff077d1f753d5f1f3d0f2bfc07a4cf9da0c1143c5f6dbd8993fe2bac7746fb5925678b78fe5c119e3fe76f05cfc1283fd2d6f3d79d7f6dd2731ef303659d05b63fb25ae8396ff981aede443aff75d2fb38e9f93ae61918e64766790f9d3f0207cfc1013f92d1fb68e1f96a7c83b53d5f779d0d96f8917def2298ffafd1d960861fb9e94db4f35f2bbd8b70ff1f8e37b1ca7fa5f336c2cf7e1d9e59533fd2c37303ffcef12e02ffbf0967856affdadc5960a41f19e76d1cf3ec273dcb4afa9115de44e47fc5f03eb678be1e78ce877ee28ce71ef0f78ee75ce207b2ef22f3ffbb72d668f6a6d4bec180cfd74bcf6dfd3381e746fe0d3d9b817f767b135bfc571eefa2ddffe7bec184cfd74c6f238b673f03ef23a9e76b9d67e0961f69e540a1a67f0dc99bd8ff95edf977fe76f0dc4dfe5ef50c7cf323cb3c0fd5fe03c06736c34f60f82ecafd7f77ef6391e76b8b37f1c27fb5f10c7cf123a7efa3aae72b9fe7a19bdfedcebb28e5fffbf336e27af65374f64bcbbf56e87d64f47ce572e097877fadcc738ef203ad6fa29bffdae86c70c18f9c749615f223cf9ed9047fd8f47d047bf6fbf4268afdd709ef228dff2fc9fb58e5f99a7c2e96fa7389786613fdc8b9e716fe7df70663f07cc5f526fefd7fc87731eeffcbf12e96fd7f199e73829f95f23e4678f6d3f5362a9ffde29e8539fe35166781827e249b3711c67f8ddfc73dcfd7256fe38867bf01ef22dbff67e2f975fe6ef036f27af68bf42efaf8ffd69c1582f9d79e6781cb1fd9e359d8e05fabf02e5af8fff09e3bc4df729e7b56ee207f937ace477e60a8b35fc07f8dcabb18e1ff93f12632ff6b92e73fffeeb771c7b39fdd33f0d68f04f42686f8afee335be947dabd898cfe6baaf7d0fa5f83cf4ff337db5961f15f93f03c64f3bbd5791bb93cfb399f73971ff82a3fe7138341213028ec7f5dec39c80283c2669b15760e362b160403068544e2810f0e8941e0573098d916f6017c0959a150d8dd4021b0d800c488c5619ffd3b0b00d0c46c362c3e7f65600fc34e6ca0901d16e8ccfefdaf668dcfb9f389c7dcff502864df6342f6eb281c12870fb2ef2c20ec67c18c0f2b3e77be3077e7043f9065915f038c39c0cf7e3dec6770e07f3c10ec423036837dec78763c10ecf94076783156a816005804703200016ee0cd7660dfecfd063e78a3070144b1af07099cbd82e1f0c60bc5320be0c703eff6fe800303443811c0cf7efcd8801b7f04f90ad43e9cf0a0b18314423cc3db6d6c79e207a438588d8f1de80305bc17b48193ddecc10760a0e47ffe1f60d07f80f777bc0b9cbd5aa40bbf88ff7ffe3f3cf3c2fffdffffc11acfdfc1e7b7f9ff1bb8aa035f3c882379fe6fa9511cac9d9cc77ff66c17e6ee1317fe79c81b74aa3f6b00d6f8ff0338c6695525be2e7c617d5a0febc38a584f8444fc7097b52901e93b1b2aebd3f2f4fd4baeaf4539b076190feb83714c35a957d6b3b5eafbf094f52c82bea709637dd85cdfbf3458afd6a8af4505b09e75d1f7367bac2d4a68fd972b6b535a623dfbd677b736501d56c4faaf0f7d5f73cafa3675ac57bb64edb0157d9d4767fd1a55d60f07584f6016f15fb4ac0f86b13ec8c5daa21258ff55c27a56697d2e64dadbaed6a70de87bda9ebeab6db21ec088bcbb4910f50214be7a47ac3e6bb47e0d2aeb8599ac575b657d504ddfd788b2b6a8a2f5ec82beb33bac0f1eb25e0c4adf859aac07b0a8c759147d2f60ed07d958afe6c8da949af4fd6b56dfd9b97ef08af56c5255b37bd6abd5b27e0d2c6b531ad2d7695c881f8eb27ed8c9fa34217d17d6b09e8d9daf0b4359ff3596e00048e4b52803d6aee4c17a5e09ac67b3768b3ab31e40a31e27f0ec7b9a23eb059c114fe003f1ec84be0f2358af26a9afd9aaf5bc24582f34b3be2d95f54142037aacad1f7ae93b015ac40b4bf59d1db3fe8b42df0b9cea36e52ad5859dac1f0ab01e00a31eff1ad177d6693d9185f5ac6bbd1b97e1d54e591f9463fd90525f534262bddaa4466f43c8fa3023d607cff435cb67fdd080f5ac98b503e8f9faf080f5ec80beb351b2361a06e20ba4ea1625c07a355bd6b3767d6783d60b5b1cf8f1c2d7bf40581f46a1ef054cf58529900fa2e97b1112b1450db076a50fd61e60f7d5a2a4589f16c9fa343fac67ebfa6a94ac1f7eb2b6981062b33dac9d37673d9ba2efc34bd61655666d00bcfa5ec055dfd54859cf8bcd7ab72ec3a3d959bb0c05f1c347d613805acfbed60faf7d5f93cafa6197beab59b29ed701eb8536ac6725f4fd6b57df0198c86bb255c47fb5b29eadd1773543d60e33d177f640a38335223e0d4f5fa7a2d4d794de585fc093f5ec0d551d86a4af0b68207e58aaafcf067d5dadcfda7fc2f4835bac6737581fdc43d6a3e2ac1f16b01e78c47ab545d63e13446c5141ebbf32f45dad94f5ac0d6bef90d5675ff475a50ed62605cbdaa65aa53a9babbeb345ebc306f4759e9cf5434ed6b369ebbb3095f504a4d6b7b1637d814c7d0faeb19e055a5b9498f52c8dbe3605cbda6517beaec6ca7a220ae20138ead1a20058df66cbfa212feb0b5cea17d8d4d7627dbe0e4462fd5787beb31fac57cb643ddbb4b62804d61605b49e8d5a9b6c8e78310a5f4f33d4f7a19ff56cbbea6988ac4d096fd3852eac5dacd00f0a12f0b65bd60b0358df468ff5ec59aab305fa9a542b6b8b62607d508bf55fb1acff82653d0bc0daa930f5f558b65e4d96f54138a0ae26ca7aa1296b538263fd1a21f1413d631ff6593f8c45e0434dd6bb41307516cc7ab64e7db7079dbec695f5ec83be2e6a203e0d9ff5697cfa9e06a8efc21b7d671b65bd1046dfd520adbab0ccfa6118eb5916d60f4d59af66b53e0d92f54201d67fc9b25ec027ebbf48589f4696b5c9e8bece8ae8fbb0cd7a36567d5683f56abcac3d0ace7a007bf4de86cb7ab708a99e46c8fa34417d6775ac7ada23ebdbdc223625e9a65615c2fa35a5ac67d9faae76c8faa127ebd9117d4f23c6da949858af86c8fa618dbe13e061fd108cf500ea9857d3646d52aeac1f6eb17ed8ca7ae10ab60f7559cf76aa5bd4d1faa1186b174f591f6cd377353f534fab633d81d7fab21cd62e86593f74643d5b2c6b879db05e48ca7ab544d6a7e959cf1ead4f135bcf0ead1f62b23e88c5fae018ebd9068d4e74617d5aa3bea644a4ef413b6067cb647d9b9ff569f4ac0f7601f5e09fb317e8647d9b10e28f305e5ce8c97a362e882d4acddaa286d60f09581b2c81f5bc18f49d1784beb309faae06cbdaa288d60f59591f24633d1bd7f7343eac6733887896697ddb2eeb875fac37a2205e0d95f561687d3da002b18b607dad8a84f5693a884d89d94d4948dfd57af59d2db3769891be0f91d617e0d4d76516be3e5c60fd179cb529fde87bf1c257f7aebe0f03583f9cd4f720a000576364bd1112f16deef49dc8cbda600aac67f1ac174a119cb5d077d684f56973acbda3569fcdb1ea0528595f00553f2d91b5cbf0ac1faaeabbdab5ef6a9cac9d0767fd1095f5693fac4fb367fdf056dfd916d6a614ddf4343d7d578b94a9458159cf22fbfe6567edb50ec4b7cdb2feeb95f5ecad67ef543d4c89b5c38e589fa66a7ddb2aeb09c87d5db8c9da6125ac57ab646d4a4aac6fb35acf06abef611536355a06e2bf3658df46ca7a026e8867c3ac4de9ddf4b65ed666f5ace785c07a9644df59057d075022af45ad592f64656d006eac5dc6b43e4d80f56c07eb8567d6ab15ea3b8bc1da63b77ed8cbfa34aff5c201c8b330fa1acdc3d785a2ac4d2989b5557db09e6deb0b90ea0bd3d83ee4d2f7af107dfff2607d014c7d1faa59bb5867fd179df5431a7dff12613df1c2d787b3d4ce3ae8fb1092b5f3e2ac6fe365fd1094b54599592fbcebfb979bf52cd1da00de58cf42e8bb5086f56da3ac1f36b2be0d96f5c12ad6b36d7d3f4222361803eb0154603e6dcffa321cd61e8b23beed94b5cb9ed60f6063eaac99b5cb60ebdb6a59cfaaf55d2d96b545b1d6b355eab304369d95eb0f19585b545b1ff4d277b64bd6870d297296ae7b6c10b1c7a65f6703a1ef6dacac4f5b647df08db5cb62ebbf2ef475aa4b7d1fd6b29e855a5f0054dfc37c14795b296b8b22b3fe18c3c6d55c597bd49cb54521ad6753656d4aeea6ab7db2fe8b95f5c3bfbe0f1d58cf9259fff5d67702d6af0f2df55dac02f1ec5adfc3801439bbc27ab63e6b832db0b65988af1e93233e8dcfda6122fa1e44d4f7e09c628bbaf50460eb8736fa3a55a5bea7ddb13ead4f5fab75f87af08bf54232eb838ac87ac7acfef055dfdb5a591fec53f635b1ac0fa6e93b6ba0d185a8ac575364fdf1cfd7794db09ec5b1ea5f18ac6d96c3fab657d636eb40bc50ccfab01e455e7667fdb052df0b7cea7b81507d677bd5f7e1036bbfb28457cb65eda2b5ef4130d61b2f7c752a4b7d4ff363bd0033e287aefa5a5498f56016886723f47dcd2a6b8791b03e022c6b5382377d4d046287a9e86b510dac0f66e9fb1056df8366fa5ea05477000dc42e6b5a5b1401ebdd26887a9a58d6b3acb5cd0a115b1508ebc12e102fa012f142146a6f7365bdda2bebd9187d17baea6b00bdfa4ea00df16b0e115bed03f1412a7d5723643d3089b5d5b410dfa6cb7a355ca6ae96cadaa2c6ac0fe6116b5523ac67e3d4673d585bd402eb879bac2710b57e28cafa6126eb0b5cad17e6b09ecdb31e001fbd4e1341bcb0cf7ab547d60e0bd1f73440ac57a365fd1081f56c22f4b59894f8027d7d6f1365fd5785beb7d1b2b60008c4a6e4a4ef413e656f23c8fab4437d67b3f543497d6f4365bd7094f5c219d60b4f14f83094b547d1591f96a8af4565ebd314591fc413d6a4e2582f6c656d4a6eac5fd3ca7ab55ba9ce72b0f62839eb87d6be13c8b3b6580862878db05e6d90f52c83460f4b626d4a3efa9e56c8fa30b8489739ade7f5a0ef6c96fa5f757d67d352bd2d97f5bc0c581fac73fc3097f56b4c59bb6c87f5613f8abcf8e7eb1164594f606a7dd08df5421ad6a359e87b9b2ceb5910b14e6cd1773553d60b63f49d45eb3bebd677210b0367bfac2f50c9fa201de3d9a7f54023d6b302face2ead0f3b427c013feb7939e8fb579cb5df58c24613453c2bc37ab40aa01e1463ed516ed636f5aaaf29b5b13e4d0febd3fe585b8c0ff1698cac074341fcd096f542156b67d3646d4a45face06e8bbda28ebddb8229e2d55dfd542595b5401eb876bd64e932336a5207d0d6057dfbfe6ac9df7663d9b27eb87abac2f20ca7a5e11fa3ad5a4be4e95a9ef6c086b536af6c37810399bc17ab655fdb4417d4da6eaabcba6d6b314fa6db1ac674f589b92b39b1294d48377005ccd93f502e4881f42b076599ef56a9dac5753653df1cf57efa0d59de766fd10cd7a364cd6b317fabee695f5052659bb4c6a7d1a1deb814aac6767a4fa9785be8bd9117bd49bb58b767d2fa0a9ef1162597bacd157af7120becd94f5acd0fa612aeb876cd6b70964bd70ccfaa1026b87a1e87b9b39d6b36deaf36ad0d7a28cd6af2165fd1097f5435ad6a7d9b1be4d95f5c417d603b0235ead95b503e4215ec8c97aa109022dea81f500fa109f46b5b62a11d6f35260bddaaebeaf91656d516cd60f6515b81087f569556b9b19faba106ded6bacd687c5479ee6c3fa6124ebd530591ff463f635a3ac570365bdda2ceb79b5f535251e9bfec5c1fa230ae259b8beaf6165edb0147d4f43b55eb8aaefc14268ad0682785e0bac0f96e97b815ad6cea2fa1eb4623ddb24eb8b288857d3a5ea6c85beabfd757adb9ff56c096b66f3cf02fbec87c0e7ce39f820dbcfc0ce40209bd96083bd1f04e6b3c57ff5f0f94121900d3e78620e0e9f81ef6e621f85390b3be7e35bc87e092fbc987078a110f81e760e33beffee9f1864075cf8b3377eecf9c13ef11c8467fff500671f1c3bf1ecdf5dfc8083b16242a0f09905303c9a3dbf59e18dc0136f7cb11ff62cf6b0bff87f00fbe08117deecc7979dc081bf0219f1f9c4fff9c0078373ecd9fbc160f57f70ecc37cf6f9f97ff7f39bfd81fffad71b78011fbb8802079e3d82e7db3d00fbffcaff4cf7560771b29f81bd1fe2c52ff9817fff035b4b4ef0fea0444410215f2226ccff45c988231911c34e141dfe96cd021c9b82a8fb9c2b43a20bc3ad5a702ee7ede732454a8cafd0507664128923379ec9a4333a2c312c712ab45f5df8db552740fd670a03b9646093b6cabb3d371a49088bcf5d4639fc6f512442dcdb13864128dfe7934394e97cb66e0fe7f4c49caf647ea0a3d827215a9d960db904bb4a98c3ff6285970d5972e634563ae3369e0e494e980042affd907c3b4c98e03a97b01bc2d503e1eab51f5ce7136e9b1e926f0757e97c7240c34659002cd8d275a77c6e33d8170fccffbb970591f3ebb9295dbd6ad4193763d73da2c578af8b66dcae58c2ed88e9b4658d2ba224bc2c58c0e6c686c1403043b06dc102360c19d896511758e8c03bc028c0126a3f900470c40f60fab21f333e8525c4f03b9de02a9dcf62dc1496507d523fb0b52806c428f3f1e1d39c2567819340d726e5fffbb21e1e5ea5bb8493f026c1adaded55c2afb2ad2dca744e3f98f830e28de31630fc469b4a17364441422b0b62a0ba6684939b5c1c00e57909369f40c8f05c6301a0851038d62294d0a8b06577a74627eb429416cceb8a1f043fcc38488e3452636107d1caa4f587d1f0678bee0b6df174a5d517b08b777737e57fdecbf8c6bcc4ff4376f8bb67bbf6378525b05f75420a413ae01c2b97e31a6ec6cffe6f335cd81d0117d78beeedcf5eeae4aa2d0c7358ba51279ebb8cc2883a3bb8f0eba1c3d27907a58290f12a5fe5db621975e3a92ba385b653aa09d84de6c16f886ab5c334865b294c7dae583e9d4d0fd56754451bb7cbaa6be25975f3dce0aaae1c1d9fbb8c46e1e232af147fe6126d9b2f5cf845b1229eb6ee899803f48dba4be81b8dbe21a6285ca5f38d51855f445fa7f3c3b3752a9d4b6a89e7d4c9744f3495ee0b570f0d480276f39cd11745ec8cc25892a0e22d9d7fde55a6cb8dbe29cabc650134da6d837180142344c09001039b03bea5b3418ce79eb0b38b9b4c670b58458ce79e606363632bfa60e35b3aef5ce6d2bd633b5140484078fc8421039efb0cf661b8c0c606812dea3eefee96cf978d050b28e75f13e2e9920d0306253c4dbab165084228931efaef2edfa81d396f6c6d1830b04970994b376b297b4d4d6109eef2a5f18284070e2b387860bbc0c6b774b6b4ec8df009d216cae0bf2230e0e3c42dd3a9d74627b82e1c27274ce3a6d1d719cbc5e9d9d9c1c9c5d9d9c1e9c1c590b3b38bd35b92908b111763e94ea976767238ea28d0cdc9c5c165c0f1c666c1c60525cce182d28d616727b77cf1a22432bc785112d30c51f784bb7c93d51645940abf709121ea9e70a12dfc469d5017da465fbc2809f7996cdf144b370a234ff99e885d14f19b62eaba219eadfbe14dc10762f9a4be325a17dabad3671b02c4f3490d11a737cd10b724b1742edda51b85616797c14104b8386a83e9f3494d518d72802a67c0ee940abb52aa0b1729a24e132aa6b1b385e30e2dfc42ca07e27d5f4c5d370433ea4ce12e5fa5fb017ea51fdc1e4e175310cbe73bc27d26db37c5fb62d976783edd784a85e3280c15cba75386fb0176e3a733049e8ae793c3f3c961f974cac8e04ea7d3240737766165f4f58083eead0e875cfc3e3bfc194d0cb72baacbb7c3924475f976c890012eee025c1c8c781a7d3dd4c1bd51eba838dce0e4e8a838e0e29624dc147071d41d7676707a767625f010e00d802af9eefe9705297385fc39978b142a48bcf090024689bb191c5038e326d3f907a674f5eef25a772f830ba5abf722af75652c5b17c67361383d3b3ba51b63ea9472983aa52d33ea7c4aa14ee974efbd3075976ff46edabbca94058e620a4b80b62e1c4fef5c271c7f84863b77f9469d706c67d754da0d21e2e347bbe26fe369a8188e40241289c568cbd6a11ac3ce65cbf6b271780359ba3cd82c40df5619418783bb062ef475a1436faac3acc38a535882de0b8da7c462f47da7d2681b7d55b04539fcf9cf9081d2d5ab3ea32adce58be2f9a6583edf1121e2d6853decbd4fb743f5195501c3c3d27db6d5f6c49b0207881a245cdc7d00f14185871420ee5aa868d102c41d8813303ea840713783030a360b75541bed74a974edecdc76ba548270b104f1bec9137a2e33fa3e3330db1717946e8c15d5d809bf4e584ac2453576c680f1a680db0ecbb6c3740a224ff93e3b3463d8d98103defbc22b966d07f7d9c62e5c981a5343c44e0e5137ef7d317d53a0c87041e9c69832dfa794eab6607bb6510e54b6288729fca660739fd4e50b656b3b48a979efd4d942a36fec4ea751b7c70be3c5d4e4d44db6ceb61947a96c2edc869846a96cd196e99c2eddb8b5e094c0166d9b31ec4cb2b145930ea6ae8b5725c116bf51b6e6b41dd1c26e0b83989b9b9b9b9b9b939393939393939393938b8b8b8b8b8b8b8b8b8b838383838383838383837b7b7b7b7b7b7b7b7b7b7383738373837383738373837383738373837383ab50a142850a152a54a850a1aeaeaeaeaeaeaeaeae8e02050a142850a0408102053a3a3a3a3a3a3a3a3abab9b9b9b9b9b9b9b9b939393939393939393939b9b8b8b8b8b8b8b8b8b838383838383838383838b8b7b7b7b7b7b7b737373737373737373737b7b70a152a54a850a142850a15eaeaeaeaeaeaeaeaeaea2850a0408102050a142850a0a3a3a3a3a3a3a3a3a39b9b9b9b9b9b9b9b9b9b939393939393939393938b8b8b8b8b8b8b8b8b8b838383838383838383837b7b7b7b7b7b7b7b7b737bab5047816e4e2e0eee0d0594c469fcc77bd989187627d4c59170a34e6614048c67ebba70e115503726f11fc2f36526d0feec60f92a5be8b4dd637b47907af26e3a6da5d3bb6a9bf74e3364a8a35e904e6398583adf182ade585a8595bf1c38cdb8954e9830e150480cfa1496e084a97772716d2efc2adba453091224e43df1d8d85c650bb3c56edeb44db2b1459d6d9cd73ed9aa6e125eb23dbb20618204090712244868c006a2c58aba39172e7a54f9f6f4a8f114e6217da3d0bc1dce26b3068d193262c078e1a2058b1529509c305162848810203e78e8c0618284b608bd203c70d080c1020509101c30c0b686542b40200d6d808000bdcecccac8c67a891173581965e63ff4854344dde7dd1bdf49a9777578318ce7b385ba78df171d30e42c7cfa11a59e5426e2c2126e9b6d1f4c7c185175f3daa81fe2f7e9621abfcf69fc3e5ddc49a5ad9d1d3cb5b36b616a7b279d6658ba533be8b4ddbbf06751a1fecc66509f1cbcb05ba3dac1f9611985f17c3a93b0bb62ca8c3a9bf11dec4aebe0de602c5d3e67883e4335651ffefcb7b3bb934e972e9f13c48f978198f1efbd4f3cd8d57bcb4028f81dd4c9564d3aa7b0843b3ab836a53376a1cdd809b76dd23000604763913864d774de901bb4a6372548bdc84c1987be292c61d9ba30198726fc2a27bcce7ca66cc3a5ffbff1033f15c6a346e32415cf5d2a936d853a0a74737271706f6e392c1b73ee5cb83488fdf9a446cb32a4faaf43071b33ea2ccbb0e8005e96e1cb7f69352ecb10c5cba0e3bf0336bb086c766ed4f9648bd0a02c03860c726c6c6ed4197de169cb8c3af9dc26938d197d9f0ef0a2ceb60910d8e0972d459b822d7e990dd8dcd81975e2b14dd9a24e37e9e655dd3c360721dedcdca837dadcdcdcdca8371ab0e938000447041d763a5ceab3d5638bc2ad34840a1042def858bbf0268e13a2b7e8542304531c08e960c7225a84531f21600f0898024a991244d38b520526c19d829469a3a2c4061ba482403f9968732b6c100aa62e2c39f54b10b6c0ae0a5c70cd0112e9caa21b0e9e9c00b2654f38c7008622106595bc41695a2c0099b0e20dde14597e3fae6031f64c6b29fa71a8e82691a107753fa60c99e4497af1f783869a0e210628b9f523019248617a08ef7cc4ca33d2c95577c2479f0fa18c408012c20712d69a9960a8e46385245673765a3cf948b38230808b468e1ea7900009441021a4071d0a743f103cdd1e3e9c34d15107e7ed11826861489b8c273dc6c6e8c26bf2465aed935354dceb81a29a668a180b551661a8c2602726ac19f356bd40aa4c481c57a4fa45a0c3a31bb79aafd28f3969891c4c3e307ccc3d7c70ae1619102b74dde34562831caa3037f616d97131d42ac89c3d2b49b32f80e016ecddb12fda721991ee3590a4e764c2971a7ab4384128f304c8999e2042a176f468fde831619e7ab528c504bd172e9033c66478a2c7d621089c13bc325157b1bb4899359245bd2389578abde120ea9034aeb10a94492d61e652a815728cd433159f844c055d88471de9c015eaa58cdd8820b28ad2963031b5522418718f0131072a8f4fb71699c5465805b2c84893649a69462937d516c0291951790e288a274d9f5d0001e11744a674a4dc26d9d08334cd29a04c7fdc1961486e602d51464caed4620227057d91f170eb12dcc60bf62246960b1eb878150a8006a58f87213839503d3495e211254f9f5d5468e4f1985165c21b07b3183c10ac5140e89fc6a4b0a6af17b92baaa47c1c2c3833d4e685f4081bab0f5f967890ae90a219fa3364d10d21afa92f592041033c7ae760787b2487788446aebc2008d83dc8fba2f746887a021a00f2d7868aa2f9824a4b2e2242a23ca216da20e3da602951a1013a859a143a422949a15f5e04f053a594158ac7c60da30e2a5841e10cc4b0f33245f0dc95114cf1db43e1a928d1b3ca943e5dea14517ae27832301484ce88612e8f567694e35d3ec80bb403d42e21c236794b60087983cbf393e7626f4bad0631dcf2d614104a9226d6891da958751d1228c51d7660c9b5513833dc2164ba83c411344b3df2a4ec708a7ae1a439e64326671bb6506090ec643963540334694ad6706ed823eacd9b14729c4542cc1e704ad069626222c90508538d2699b530495e1f4634f3dc461476d98135e7580d2334baa2a9d94b2cc918ee95311f0140ca52938015330b8f3a355aa93161b6b5a2c84150e21233421f20a7821f92e0ddcaa565d50e3075788592a15ef99973c583b2a397a93b2252f064e01db094c94f8a1183fb6a2d508d207cadbb07cd3d7bbba6728d883822564b512b37c5264b295e4ccddc0170a89b3c48f1b02c05089728ba23dd524e161a787f44c55856f9961f28788869154ab4148b3087cb41538c9293a818333a6ce0a53c63872f8417269c52aab413d202c4a9329294c1e4a4a49703c85ea77e29e926dfda5c8b2ce1b223776c13b554da1b5326121ce5aad52e287449c1abcb908e593a00e950a2a3850e354c324038d2e3ea0ed5a75079c43b597776049c82656787c15d90276606e06005df8db0715706654ec28a2c7272841a0123002a194272b011b6e1e343f628471c84d1a6c353a61c1b801f12a0f8e12607710c5448aa25c371898a630cde9b361c710ee1d88141d4082ca6a038e478a110825a2103c71d492b9998566f340a95b68aac858d1b587edc68e1c6608944c2ed06842372074aad4161e3960d425f4ab83864d562e3499b073e168a4f6cc4b0716008c4c30c5d456ab4ea940cda680b53434f8d227b23ae6e6a7035503851630b6d918a1a3448a502e46c83f688461c0e13c2f4a4c0a1c147c303211a00728df18de0303763d3a24e89c6c83d9861c612a9ba210efccc78f9386bbd03a21915f2660051dc99c6e1802563520607b60ad1792283cbae305860528992b16c345d20c0d510196e31febefca1b1c70e430c4425dc4424bac22b0695e1023195c41cc5b0b101c3232329c8622880214bea6809a14c030c3f3092b480d5a7634019182c3cd10a6027442a182fac2fea9080a3e08a21202f80bc10f182ac0f11872229d2c5a75bd4e4263b4a176afc70a4827343858b699b005732801c5d6488f324ceac168e8ba11333821aa448d0c25219552e9f4d6a2dc4b470b6902310259004598a5a9c05a31e2cae68418b21f760110b1d7ca3c60cc180b278e06385adb32e7ec00fc915835634b95b01438b024f0c2608596100841e5248047151c14a451e028fd48c7109a082888a132a52904af1e6cff9044b85638a3d097d327d5284214507278bdc9812b34c018642138a3228a228ea54b0aa345c6da16061a712bc96ca09496a5434d22f044e6c39a13c31c1c4d5c4a1321353d898a8c1c1448248256295e853024909165d4a9038352541e706891f2442903831a2af3a224d3b02463502039a115f222a890823a2594a445c085e211685b02220c45d0807ab40d402310804930ce2c51c08362a1f567dc0f361c807131f3c8cf2e0c6839e070e1e8e1d327528d301da8142873ce68e0d1224578ba6234ab2491c241c19e2b7c3d012291b686c48700db1fe5f4a30fe2f7d998f4d0ea2eef36ecf8d36b883dba6f26eea9480f774a7152ad455a05081aec25c05b90a7115e02abc5570ab50a1aeae8e421d5ddd5c9d5c5d5c1d5cdd5b9d5b5d050a75142850a0a33047418e421c05380a6f14dc2854a0aba3a34047473747274717470747f746e7465761ae6e8ec21cdddcdc9cdc5cdc1cdcdcdb9cdb5c05b93a390a72747273727272717270726f726e7215e2eae228c4d1c5cdc5c9c5c5c5c1c5bdc5b9c55580ab83a30047073707270717070707f706e70657e1adee8dc21bdddbdc9bdc5bdc1bdcdbdb9bdb5b05b73a370a6e746e736e726e716e706e6f6e6e6e59084008600a4b88c26e1cb7d209703c75d097a24d811775b6e1459d6c11cadc12e8189efa8fe36513d0b84b0a15de24b849786b7397b03bd563a3465d690b6573a5d578cf8d4e6870b609c758d9c26d5319ebbf0e36d865ba71d4592641938e6a0bc3ed94ad758102013ca54cc28dc335da3635e3563aa11abfbd49806b7bf1a2ce364f53b84592ad19b752b6e793839d1dd082813fe8e67ffc32b721ff0dfc413dffe397b90df96ff607f5fc8f5fe636e46e12dc06dc29a668e5e1470bbe516f511d678c889519b200c7f8a2a4b60da93503c58a66a7b530888c0052145c3222928b413eec74ba85668212dddb1308fd32eac56571e0c3cfec829a1c5062b14d9e440837f80c4cf8137343722a038d2fd13f970eccd191f664111aaa0b6476dd6803f2e70a88026fcaa80d667b76a5589200c42b2983ee4c0b302217b3b26bd30a3b5e2e013e3f23b1a62e78694992b4ec50d13284506b50a5174f1e6038ab53e1e046cc6729060e268036443f4bb1eb2657a5a6e8603366aa056c804c320f01073602d4a16a004684ab56cc4902ca38eb23e7c35f0b660649757ea63934d0bd194b2a98b488432b380d3ab0d832d52b472c02932e10d9e0acaaea7558b02e3972941660831b13af8fa2576e171a4fda5c28e363ec112d24ac168991b56ad4280c2f76a56054c0c7da8537715c01376ce7f223c1541c60d1a94608a63428f52733756111a4f680cc8e45b408a73e2a187949a26cd3abc412b507044c01a54c11b2c64f531094fb014cc08b520526c19c7af5d1f1064867d5174d646d5494d860631497d61a8b25b4044855ed938936b792a5b04634315fc0b0441160eac29253bda0f9321c75e287a6369bca0aecaac005971c3d73bed2cc6821835586bcb2e88683272768a09dc1f54cc06ac0da803de11c031888a246ad40144b6d8928b4b22a7983d2b4584843d18902ad1296ac3dd656bcc19b22cbef410007b490a0d6343d40038bb1675a2bd192313f0d4e051112ea8559d14d22430fea9e9d74b1e9d98277c283d890499ea417d75872dcd222545949e9d0d474083140c92d14a7d00cf5b8e1c60ac80c49a4303d8439cee81121094b0058ac1a89e519e9e4aa3391e72ad4d0864f102c4c691f421981000584ac2852f713945ed60367ac3533c140298a110ab284a65eac528849623567a7a5d32622063a98aa5c9818d3ac200ce0a2916301754211580d6cad213b43022410414410240f9e3405b9c0c567cd1805ba1f089e2e051818d8b8845d5b17049c34d15107271ae8d522b9066852f25a00a285216d329c389a55c1900b1168bc9c181ba30bafc99b37938dd619f63cf20343013945c5bd1e279a4090044088d9579b840053c458a8b2088385181a323c120b020a44067662c29a316eba3ef472a5474acb2d0548950989e3885821b3a5cc89512abe6a0820d0e1d18d5bdd01159c146650c1cf11643fe6a42572309b7c64c005f834883146cdca123b6ddcd0c4360415f823d429419c0036760e5ed0d23385843b679117335586c85c2d322056e8bac14ecfa0313fc4fc6424890d72a8c2d8e082eb0993190115153003ecb8186a15444e9c3b33485a21681507294b9a7d0104b740019a8a30318c2098b25c635fb4e53222b560a34e9658942a0d213340929e93095f68300ad0806f901a4f5dd2ca384128f30488d9185049143d098911676b8442ede8d1f6e9b2eb0c8f2e52ea860368cc53af16a590406a6924cad982437129cc5c20678cc9e024d50a6786bd1674527888750802e704af4c2ea64adc081c10698892c6ee2265d62816122468a270e886ee1c3349bc52ec0d07eda0c442818c1c50e402b134aeb10a14cf4f27352cc8e8c432c52acdcca5502be414133d4093e2880a5497de9ca9f824642ac802253f7a6076baf629c6d8910e5ca15e4a001310d59224e5041d32022288aca2b4250c10b40b7d4ed5314b61e4ac522418718f014b33372c47a2e8a1a11ba3f2f8746b5199110f35c6e836e558458500ac025964a44112a692033c09a02cdb140930a3949b6a0ba010004a0ea9615875208f89ca7340513cc94553c13987d78b545da00b2020fc62481932b6a8a876bd48e30c40ca6d920d3d482950e1a29264e961cf0bb21450a63fee8a6892dca90993e78b153db5123e58fdbdb52923e5727c4bd0b6079080282326576a2d21420b8e991a466424e24526e88b8c875b96bc38908187547e23e64d8d17ec458cac16543c0962e204238a571e01bc0a054083e2490da95e640c9848737394c1c981eaa189542b3c5c50dcb970a6760a99a7cf2e2a34f2cc60143a7315e155160b03aa4c78e36006830d283950326428f8a0ab6c8d0242ff34f6828ea153646fd426315a9bbe5ee4aea45252c4d2504dd4843b7a680e169c196ae3428c973e619013b36e1d326363f5e1cb920ed60024c2389216ba63c5d2604c0252411292cfeed40ba579824533f467c8a21b41b42c3854e6c4d9f40d25b32f59204103387ab3c6e221b629144b8d1810103c12905d6052c789830c8ce6a02469c9211ea1912b61463c31ca0738c815cf08d83dc8fba2074b2af525974ace01193152575efd010241ca118f181c49a6184841006800c85f1bca848825bc741587cc9138fb824a4b2e2242de27740ba3327f4066ac448b50ba985c14c49201274a200e060a026d90716d701433690a489140847c552a80013a859a1432e2897304cfd7a4534e528ddd89326721d27bc809181c623ab1b8d0a7c08b007eaa94b27e815884e5d114227f8602b071c3a8830a56158063f632756a2c4e320331ecbc4c0f5c61ed58211bd11f5ea8650453fcf6507051618d451e4e5ec6241120a76a43a22ba2beb63a6a10f52065ad4436a54f973a45947ec8b2c167c7223c48a86a7770188a722cd21689600314182c0baf04180a4267c43097168949a90146058c9c94654739dee5031f9ba22a42bc2ca244e40e50bb8408dbdc0d9a1344d3802224d60c8021e40d2ecf8f1048347a91f5e84b49caf6b6d46a106302171e668f002c6abd4a6b0a08254913eb44833bafca4a70f2226bc500565d87040ab1cf831961afd90f426065b0e4da289cf95a6440d471c627b72ed2b5536c236808614e921e898a22e1ce83146847d02cf5c893629606c3101e816e2dc960e68cd361518ca3d8115eafd10bc21838b1174e9a633e649a82d9f2118f0e58a3a4b185028364176b8fae05789d4451d054c20cd5004d9a9235dab220593b1b12e18d05b13da2debc4911a709a849a7da3c8ac584511a21660f3825e6807a4b71e1838f343d00674c24b90061aa61a2c6995d2c963887368dad8549f2fa30e2c20823bbbeec09624511701b51d86507d6b6c1d2041d82b608e3ce580d2334baa24906338c461a9c28e0adb12596640cf7c8deb480e1c4918206864a010048596a12ac82b342cf8f1241a4248b04e051a7462bb5254dd25cdd2c6c81f1cc8dd58a220741094b65d6da064d80d3600aae027d809c0a7e4032e98d1424b433b03b8202e4d2b26a07180a9c0815c100a70d0f1c2e5932d42b3f73ae3e5a8e57822535e820d576f4327547440a84390359c6747a70679780258ff2d489743c3005951b3ba3fc18da456629939f14e38554d3564c6edcc942b744ae05aa1184af9db682b3e6ca0b044dec06a0b9676f575452623571e541afc8a55c65228e88d552d46aba0053932f39248931426eb294e2c5b48c150f186dac3432856acd0038d44d1ea4783840680c8687a19eb53e65807089a23bcee1617218cbd0bb1357d6b2d0c0fb230a062ca397143b466ce9a1040d84590b373b7bb35e393c5e8da9e6126346c1434cab50221b4f9ed850804242d22a16610e978366183966ccfc50d3344305124d548c191d36ec3a55494c045d4e0605d16266872f8417e6db91354264b8bd90e0c8c4a458c9938384109d2bb8370b0469b2d1214d480b10a7ca440a348245190fb85910c09cc1ada1b02a151a57a8a6c3205b51989410b3e5e20e16db911595d69a5d7218c90c091c40f63a754b778cb8f10337322f1bceee9ce272e46c70ccad32f4825687334863388ef4987425400ef991b4e7a0dd14092cb284cb8e9c5117f46a4296a739507b01ec3c4490722393d2495b261b6787e85cc716cd01d200844f45192763a4f62645d15160ca4482a35cb4ea8071a36292a03b023a40a14b0a5e5d856c95803505c373f0a6c9e6f48ed0d09271607a72a3b8c7400ca6da0119760d648149c20a716d33684e8651408a37129430a0436096180907e27084ba4086268613312d6a05b051c7490a29414eb8a9619201c2919e56b1aae8015142885b1595804fa1f28877b10651ef7690e94523d75a998053b0ecec30388e53af50f5f231e104f28999013858f1e832219389a3233f137006488b544dfda9314a10a83252336d44196550e624acc82227add7a914838a3481865a1b0123002a1941a870899a6252f040aa179ab00d1f1f32476ae0cc5ad1a2ca173c5bcc8230da7478cab416c8c694da21002e3862c00f0950fc7073620b09070fbc80a8220b6d0c5448aa25335be0892ae6f7b220623644461d9eb4b88f15a94360569e709013c37bd386238ea152b0527dd1c406c371a5611035028ba9a718eb86bf596b5a2f09cc78a110825a11c3ca02129628aed24819117324ad6462cab787038f0fb08c1178e8d242a5ad226b516350138fdd0a276a96c4381332c20f074f3e20b14dc9352dd30a4a360637064b24121334a1d8c0c8810a044da8048ec81d28b5e68493389100ac0922c6c3ab00ec8d5479d8305c70b2c9c887d72949b2c6be947071c8aa4569a6ecc21547ac8e840ab479e063a1e8a45218246d7d4281ea212940932f5726583003e01405198bec0c486a201b02f13043571178c5b54c680b0e01b740a764d0461b18237708ed311e8853e582803302147e24089271b58e612a276bdcc8f6465cddd4debcf841b2429364625ba0e6448d2db4452aee6afd30438a470c2e9a04946975c86c8c0621b71a15c0c380cf4719592a40ce36688ff0ee860170b548b4a80fc96142989e1438d22d47594ef49c89ad0c905380c60f96b54054b146a60846a650cb6eac62d5d660102b6b54ce18203a3008c85c637c2338c8bd3163ca840a2c08467c18b0a853a231720f10e4597141439b2263289459225537c4811f15888eba7af9f162e76e8d8fb3d63b203a11f521c1f5bb2279a1b506a67c6e8719275802e9f9d3716a8b198a3bd3381caf545dd87394a845852c13cbe0c056213a4e5c31babe9450b51a1086b62b0c169854a148f5153a61e4f886d609b346d305025c094935c78bb4992956213362646ced294b80e762d00f1aaef80679c993f6e50f8d3d76185a40b262e82ccaaf08ce4ab88948746597274d6c9aa08c717f45c6860bc454127314208401363a65a4e4f2426003864746528c01c2d0905401d1aa54397bc40bc3dc26006ebbbc18d92699513b8c491d2d21946580a245ce85b40d6238221140e5c6121fb2d10f05a722dc7a64d50524400b587d3a0690d1c54a0d50150c48b2341af3442b809d10a8762d31a565cd1b0537980a584136d9c3633a810354d094c8ac374b02f40d885e6bdd500049967baba3c482929180a3e08aa11fac8c0c8e1c8a64a4d544ad439c324ac448f8000a0d19bb52215aec09683ac153e18402f6140b148bf050f25485ac0f118722a9da173d71063b0800487450bb454d6eb293bc3283cf2e07a080746005f8e14805e7468a4f658a76f919b0949354d626c0950c2046536196de925555863821e33c8933ab7d33068d452a3052fa9e5206383123a8410a047644c47d04a421110b2e2ba3cae5b3a98094278ef60d922f5a51d6f2c2880e682832210a8bd36c78dc630aed000ced13a70271c89e120f15cc2852332310259004598aec204578ce29ca53674a2c80d1d10c42cf094526b1200821e8c883f627941b427355539fd80cf990c90da458665ad062c83d50b4aac4ab532043606420c6376acc108ca79f37c1233f4f2a2eaca5d9e83c41183d10052123a25695013ae0cecebaf8013f24296c2122c4c1010e1a2131506756e8c010b883231714c00414472e4d172c16b172e5e8e52349d409a70940d8ceb428f0c4608210384675335e51b31a0d1903a1871412415b6c4113c1598042ca108f4019419330f5b27180048651994805996a0a40e0919a312e6778be9677dca4721d54c7e4646a05c06c0c87182386fef059a5e44a81134eca48f0c193dc52a99222299fd0f40960a60cf5d2152a04e050a9cfa9003d8bc80a10c1f5c9831a1232a83c00d42054a20a05fe9c4fb05430ae7942600f0b00b84c850448e893e993a20b4932fdb86bc3a31088269993456e4c89518a19db850683412430aed4dec421ecb68859aaba620bc61108231f020039a0b4a8079a9a9236383e99d1722949b6cb0f800ab94494a8f48a4e9002553d3cc8e4a15252fce0311640523152d40010ac012a58551aaeb53aa9d1e22a5ae76a29a80c762ac16ba7c469556d12c88a2c35774d8d8a46fa258d4e8f1b636eadeee4ca34c0540e355a73ce6a2c22a1a6460c5c134095a93265aa10ad0f23c6d8b51a01a2ca933a6b7d64a2c03d500ecf11a2d370558f3c684050c802a34306332e8818334baf1655315b66a8610a05d91e5bbc4eb5f1a000ca416cc51ba91518d658c1d50b6940128b04726968aa76a0e9dad04a10af2c5f82a4aae0ca6c949225668c20358983230be0a855ae0462779ecf4fd10cc306b53c8178632d5a95d647841916206e60005e5419da49f32476d656d2fbb46b5091c7270e63a48c90b3618c00004441965cc8d4a2d3283440699c408e20c34676529c4a887889e38854839f9d7a1b436611dca752a4000ade546c1f3bc2c0ce9d313616ad3903ea088d24435e4088a6d58bb844609dc82c85b6cefc01f2f4c4f270230c0f1f775604a9acf8c76025a22109880928baa66c00c61bfe027532dd1ab088af549b1d2e63fad8e23b246a8c0e3c811c88b0b308c71f8534342b2e472f2adc18a37420cbd08d3382dcf0b1d5165152eb4b0935bc03b91edc8964e9410032749045924ba62005042ffec0607f01c33ad86c1f186c60b3c5afeedc442210ac830d240e8743002c7b232b14f6d711280c361a7be18d01802d66341e7833360bcf1a85c2a0b1f88be562f1172b828d090b741fbb8bc22fb6c0c5be9e8b6f60b108c602c3fef7df647f50f82e065f3c7e35f06d46540306ff23f1cd1a872d04128d02586058916dec0b40d1d82d847b7efe02e61e13b6d9000000129c4082239bd3de071eb8580bf38144761826644f207f957d154fec6ea191058e09cf1ebcd989ecf0617dbcd06c6876a0583eb1ac1364225b10f89b890d3030f60381c3e200b41fc50ee0073efc01fc8b282470630f087b9891ede1d7337048f73e2ef8f14dcc01cece0ac916048f67fb0a4ef89784076fbcdabfdee059f8316101b02fb34738f00e30247f769608e0756b615f43fb31ac859d85664204062e06f6160b3ec004e18bdd1fb22ff02f33a3d8179051389178fcf3311cd8c7e72281bf29388be1088e7c18db11bc94903dfe78ef17afc0eceb598617f0afb6488231073bf0130ef7e7277ffcebc3171fe3061e7628c7d2e3935838b8f15380680ffe6bc0b9bcd79cc94f1c3925ecc1dffdff8af01f07f020abe2bfd93df58707effc8b3ad0fbc2fcaf0f3a813e9de385232ebc809f68b8bf38da8560673301e34f6923dcc7742dfad9ffb9a8ffc6fe27205b0bc9af2503611d403fdf727466084efc622cfc6202fc62c27e31c13f0caf3fccf54711e30fe3e7b90c95e732009ec904ff137ecf6322fd6098ff0957ff8948ff975dff970dd98bdb6f22d2cf85d7cfa5d0cf45cdcfc5c16f22eedf42ea194b9e7f8b837f8b315fc9f297a8f37ca5f86b11f597d0fb4b449faba43d5301f16b41f0e3a8f36731f493b03e43d9fbb1e8f94960f9b1387976f2f75f39fbad5cff2a3ffe2a9bfe2a9afe2a753f1502cf4a24fc54c6fe29b2fe29579e91b87a465229caa5e721a81f4aac1fca9f1f8aa3e721f059c8841fcade0f85c0ff84d0ffc4cdf38f35ff1318bf932663fe108afe10819eab78cf7c717e393fff92bf3fc4dcbf04d4bf44d3b31ef35fa2fc97e8f8415cff25fd2b19f583d0f42b49f22bc9fb95344842ea5702e04fb2e44fc2e64fe2fc933057fc20f29ea5959e5f3a3f88f60781e3f98df023593e4f1bfd4802fc4768fd4764fd20203c3f313d43f39e9f74bf91bedfc8a3df48a3df880b23a87e23807e23087e23707e23cadf885acebbf017b1fe4500fd45ecfc45e8fc2decfc45c0fc459cfc45a8fc1ff27e2961fe22c0b82fcf71569e99789e996afe1a787e2219fe0fc69f08fb0fa9f37bc0f41c77f70f913ebb6bfe213a9e5d04cfcb4e7fbf102ccfca0cbf901ecf494ebf071acf4911cfc906bf07b93f08883f48fe8128fa3df40f44f97c97e407f2ff47a9ff47aabfc398ff47a5e71b43fe0e0c7e1f8c7e1f899e6db8f93be4bfc7ab1a6a9e69fcf87ba8f87b94e030e1afb2ca31103dc7e8f257633cc758f06f28f5f33de24bf1fc42eddf90e5df10e2df73b25762eddf433eb3887b6681f6bc62d6f30a5a1b86bf065dcf2b3e3cabd8f3ac82ccaf27f66c404c3e51e5a766783e41f75313fc1a08fc71a33f4612dff8631119f6fc53347f8637ffd4c93fedf13cc2ec59c4dc0f63f9e121cf21863fbc026ef0ecc1cdcfe3cb7307e5cf03c47350c12f85f34bf77e29905f5ae2c790e597129056f8df44cf1aa23c6bc8f1bfd11f7a7dce40e98f2ac210f747a37f74c20f957bc680fca1487e28df0f75fe500ed00c3f34c0ff64fe4f48ff53d0f3853fff33c5b3dddfb39af1d90eca9fe747cc9f07ff1752fc792f9eebd4fc7906fe0b03fe1d9e9ee9ccfc3bd6fcce55bfd3ccefec70a7d9df5cfb9b519ee5a0cd163f13c9cffcfb2d90788e33f4335dfccc1c3fd380074eff10383fde97b3c0f2b3a8f2db99fa2d0c2da0fd78247e3cbfdf2df5ec56fcdda7ddf35feefd4b30ff32c8bf74fe76127e259b5f89e85f1e788620eb579afa95467e65f34feafa95189e211c7fe58a3f79e94fd2f99338fe24813f29f6b7b740fdad9cbf3df3b7757afced89f6c4df8ef875287a5e70a94281bf82ddaf83c87f77fdef34fd7790febb2e7f5da9ff2eefbf4b3eb79d9ee1f6fc758aee0efc453a2378fb73f07a365325479e3f879d3fc79cbfeec7b301237f5df2cfe1fc7360f87334f871ecfaeb26fc3848fd3846fd387efc38f27e1cd16735eb8fc3c3b39aa9ff0694ffc6201d3f0522ff8d03ff9d8bff86f0b7d1eb99c080df062d1b9b7e1b869ed11665f86d0c7f3a5d3fddabbf06de4f63d35f83c15f83c04fa3d5738055cf01d6fc34923f8d033f8db49f86f09f81eb9f31ebd9ccd43fe3cd3fe3ed9f01f7cb70f2cf00fb65d4fa6544fa67bc8a11ff3158fc3204c808f0c328f4c3b0f3c370f3c3e072e487a1fc61b8f86188fd2f6efd2fcafc2f9aff8b11ff8bbaff4598ff85f07721eb77c1ead9a8fc5d84fa5d34faa5d2e7620421bf0b20bf8be9efa2860b122e84bf8be0dfe26feeec6fb1f7b780ff1621fe16167eb9507f0bf66761eb994a28166ffe148f7e16677e165a9e83267e16507e16cc5f8e0f8bbd9fc5f267f1e26781e25968f7cb85f957485ac1e55fd1e35f917f157e54e848c1e14f51f7a7c0f0a6f4bed1289f3e7cde94601ed2373ae34dc97d5392f146737ba3cdf1799fc9799fed799b019a63d6e76dc6e75d26a32cc3bb4c04dc7b8cc17bec0d8d97b7589fb718973f284890203e7f10d32a55a950594d951a250a549fa74e9c3665c274c952254a7ff0cf9e3f3869d29f3f8508fd41557f50cf1f9cf507effc414ebf9828bf180fcf412c3ddf98f4dc82c4bfa1c2bf61c06f01cc731ca6e7061f9e1b94f042b6c5a8c5d3af2546961f7f15bd9f0a977f0afc4ba973d2a489abbf09af268e9a084af233e1f02ba9fe4660fd465a640062e3ffe183cecff7e8dfdbf0ef95f8f56afd7a9ef4d67e3db59f6ae1a70aff98072ce787e7fe3d3552a4a33feae98772c8abfecc183f93c0cf14e0c6f8972dfe65815f79e84fcae918fe6d8a5fc7a6ff0ece7fe7fc7370fa7354fa6d34f86b247f1a2f7e1a1ffe197648fd30b8fc3060b4f8fb5b28fa590cf95980f8550cf959347f1678fe140bfe22039e3bbcfa7550ff2d167e2dbdfe2ca5a4e4f9a10cff27967e27157e27fd33c9f23311fb8fc82852fd89b8fa87047f21f01f84c31f84e863d0df83c8df63c35f8df257a1fc551c7f35c65f5df15747fc55127f35c3cf47e58f0ffc30849f47f47f0ffcd1b41fbaf73791fc78547e97d4efaaf8db597f1be56f9bbf8e2bff8de87f63f9cf58f3cfd8f0c768fe31843f0c30bf0b167f0b537f0b463f0b14ff0a54bf8a387f163dff14503f983b3f9706ff165dff1658ff945dff94babf89df9f84ec1702e8ff11e8d712e3af4af9abee5fcdf1572ffc7a8dfee9839f05a81f2ffe67d4f8610cfa596cf87358fa9938f9b3a0f8979cf8a558f8f1eefc50e46f03c9ff23d39fb4f4e748f3b7f8fa5bd8fd46e8fe14837e27337e29971f1ef4ffa8f2fb60fb7b38fa89a4faa18e7ea6963f8c98ff8b8c3f8b991fc9df1f6ff9794cfa5fa8fdd13ebf0b0d7f16523f9547ff9251ff1249bf90ea4f25f5c7c2ff79ea6faef999297ebcb2ff86fbbf08fb5b38f8b324690af8db0fff9d935fca831fbaeac781f6af80f067e1fb93127e16d07f4b8e1f88d8cf97f75369fc31f18f7ef9a11c7e26ad9fe9e36fc1e4cf22e1c75bf28b81f24ff9f11fd1f5eb75f8794472b9fc3734fc365efd2b3afd2992fc3a46fd59f6fc54eefcd538bf0a27ff9601bf96117f965b7f15b97f48885f48973f76fed33abf8e023f8e3e31ae7f0a53ff94faffc8f0e7c5f9953efe3b5b7f0e464b0afdd245ff0b407f0b337f0b01bf9512ff1404ff9157bf9405ff90f70752ebaf36fa63263f0f12ff8be05f76793be5cf11ff52927fdede8f97e86fe1e50f13e90fa3e5efb2e3c702e7af3ef85f5ebf8be8cfd1eaa731fd5f74f85bd8faa948fa9b54fa8bbcff0f547f93c8cf82d1cfc2f9a788f353dd5f39e62f72ea4ff2fa9f4b7e2973febc3e7f9336ff1112ff5eff3372fc2e0cfd400efc2edfff7cf4a700fbefd8fc2a04fd7cb97e17027eaaf49f2af97590fd3650fd292afc4d782525fd4e86fc59a8bf11593f90407f8f023f8b473f8bbeb7a8f46731f107a1f00399f0bf613fb4d3bfa3ecbf91fc676cfab3e0fa9f14faf91afd70f21f12e4ffa1e3bf03fe3342fd4d32fd6184fc5680bf11463f90317f55c8cff7e407f3f77b797f2d703f14587f921b7f12e13fa47f3d543f2cf6efb0f42f0ffd2d40fd2cf4fc2a92fc5322fdb0ae1fbae3670af87334fa97acfd4436fd49463f93e58f04d2ffb2fa71acf8c320ff246d3f4ce0cf03fe2ea87f8a223f17593f951dff110cff14d42fe5f44309fc3bf23f45cabfc4ef57d2e74fa2e11782e4d773feedad9fc6881f86929f0aa59f060b315f7e17a7fe14707f94d2ff64d11fa5d18f57eb7f3262d72c59b160a5f1b409d3efd6a5d2efd645d2a24487080902e4878f1e3c76e8c881e3064c081f7cd4dfad0bfcbb75799b78cbf60e870d1a3260b860a102c5efc6e57733f1e0d3ef46fcdd8678eed8a953850a901f6ed8ac29a3248991223e7aece94d61f7c032c32fa038d01ec308101e2e2840601c1e602b5d9a74a5cacfd6fd06a6b0b13f5bfe7f870d1b3662c4300201e23ad6ac2183060d1a342c2242840e14aef15ae3b5c68b0b172e5c862c1f3e7cd604ad095a13a4458b162c58f4e8f91a10070a3b341d3a6cd8b061c48891162d5af4e8d103c2cc0c0408468c10211224e83f1d366cd83062c4e8673bf4cd3a7440840811224488c4881123468c1841820409e2c2850b173e7cf8f0d1a2454b870e7af4e8d1a3470f162c58b060c182070f1e3c78f050a142850a152a1d3a74bfcc8c4e9546954695c61120605922f12740804844800001cb12893f01022cf155f1aa785538493849b02e08d7655922110102a15028140a851c84421601070e6cce2d8796430bf9d962317e7b43802067f68b6f06dfcf7eb108107c7d0df764ecc9d08ba1178305b26c5b9b9b5b82046d6d6f6f08e217531753b6117c7dbdbd018108107c7d5dfab3cdf690ed6e7b8ba688a680a280a280fe6cb36d6d6f6f40e089e7893c137926f24cb8b94990301c2240f0f50504e2856019b8b9b92058e0dbfe7f587e587e587e587e60f086e07f87640705ff3974d090e182dd189891f8f51398fb09047f3109bf9882296ed290f95285879d3f6cc81f56e70f8be13f8cc71f86e70fdb6023068b137f989e3facc30f164a52a31f8cd30f2643c40f96e1070bf11f3115ca94e83f62fa8f82fe638cff18e2376a52f41b03fdc644bf51d36fac72e0375ef88d067ea38c0cc618bf510484020202d4fdc50b7f114e4e999ff8e72792f9899a02fd444513201c3050404080b338b79f78e12736a8440853895f0863c42f7c2174f10b1998802082810720020f083c2040d1a62a550664dd803ae0000a02e80270da74e626e6264617800a8c3dd4d9c588fbd9e15c1d700010c1803421b00c78f6b34a8c100942c407111f3d78e8a0e1c2870d1a315cb460f9af87cacff62db0cd96d178bfbe581543060c5a0e0b162c58f0df0515061506dc026ef1aa785538493849b8205c106361ff39f86db226628b678af88e5ca5f5345091678229c29ae8eb8b1dfec763371008e3783c3a8f04081020d0a3468f1a3d6ad0a8d2d8fbd9cc366800830a830a830a830a237e11bf98ba98ba98ba98baf8d9e391018366fbacd128140281c7230306ddc7e3f3c4f3449e891d2548384938493849b0cd114c114c114c114c112e0817840bc205e18258b2c7e3d757cedd0d1a08854060b54aa552a9effbbe6ff46787b9cf0e73ff2c83b3c5219bfb679b1de63e3b64739f65d961eeb3c3dc6719b0c521fb9f03070e1cdec43dff55d37b88eabf0acf0a95fc6bcbe7e1d86fa0cbd920f047167a1f573d5ffbbc8d6e9efd7c3c0fd17e0391de475bcf57446fe2da7fcdf0ccbef9910e9ed9017f9887b340033f12c7fb08e8f90ae5f949fe06f09cb3fcc056ef63ace7eba077d1f8ffd1bd8f1d9eaf64cf5f71fe35486f228dffcaf7fcbebf413c7fc8dfc6f710ec37b0e0394b7f20a5e737fff77a1e3af80f7c781ecaf9ddf63c671d3ff0c6bb08e4ff6bf336227af6c3f2dc83fe3c7c13a1fff5c91b2cecf91aea7d34f37c9dbe8978feeba53771f95f8d9c657ffc48b3b342e5bfe6e26cfefaf3db9b08f75f593cffc37f556f639267bf18cfc0143f52fadc7d7f0ef09cf97ea0a667b6c68f04be8b6affdf88e72f49ff5aa8e7a181ffc0f1394ffa8939dec74fcf5738cf6c899f0086b7f1efd9afd1d9a0851fa9e94d24f35f073d0f39fc07f89e83cd1f89ea6c70ee47627a1763fc7f46dec70fcf57b367219b7f4dfa06fb7abe6a7a0e5aff35036f2286ff7ae3ac90ee5fcbf036da7af643f4fc3f7fb7bd8d359efd1e9c05e6f8917ccf6cf34716cf0addfeb50bcf2cd80f76e21958e347ea3dffcbdf04de464ccf7e689e9bedefe89b48f75f61bcc190cfd7576f63fbecf7f6dcadfebce0f9706cb0d6bfb6e02cdbeb47aa787ef27faee7229d3fd78337d89faf939e3bc6df96ce0669fcc8516fa299ff5ae87d543e5fe3dec417ff75fa2e36ff3f2ccfe7fbfbeb4d5cf45f51bd8b33fe3f24ef628fff4fcd73def0b391de47e7f3b5c2333beb479678ee087fc7cf431bff81516fe2f1bf42781f233d5fc33ce70b3ffbe839c7f985079e8b9cfe5c1bde4424fff5cc732ef203419d05067f248bf7f1cbf3f5f9dc6ffee6f5dc4bfe56f5ecf736aa79f6db7bee3c99adf32323bc8992feebab3711d7ff47e05d1cfe7f71cf5f6dfe3547ef229bff2fd333b0cb8fbcf22e0af9ffde9c15eaf8d75abcc11a3c5ffddec6e7b3df8537b1d17f55f53c74f2bb49dfc71bcfd708efa282ff6fc5b3f1c09bd29f375884e7abaeb7b1ccb31f7c96d5f523533c0f45fc07883c03c57e248cf731ccf335fa269afdd7b8f731c7f355c2f390d1ef96e97948e6770bf33e1e78f69bf59c49fd451acf4243ff1a90b3c10f3fb2d3f347fc67f59cf7fcc241ef6291ff4fcedbe8f6ecf7e747ffcecfb9f903f79e8397feb501efa386e76bd833dbe8475278ee2e7fd73a1b5cf4af913d67bd1f98e95dbcf0ff31dfc7e9f3d5c33370d68ff47336e8e65ffb7ace6d3ffbe65dacfbff946fa38c673f066fa3a1673f2bcfdff4b7873718f1f9cae95d0cf1ff4d9f73819f65f2fcb5e65f6bf43e127abe52797e9ebf1f3c3fd3df19de4743cf572bcf37f5f789b7b1d7b39fa4f791c9f395c6dbc8e4d98fc61b2cc4f35f80b3c1b21fa9e85998e15fe3f03e3278f6c3f53e1a79beba7817c1febfbab34150ff5a810385aafeb52667bf0efc6b5cde60c7e76bdffbe8e7f9eae41958e6476e791fd39efd56bdc1de9efffa6d4cf5ecb7e7d9f8f53eeb73f6ebcbbf76e84d6cfbaf279e8718fe03f1f375fd5de35d24f3ff457a136dfd7f68efa1adff6af62646ff2b94f7d1fa4cf77fd45921f05f8bf03ee278beb63db34d7e24dafb68f6ec67ea6d64f5ece7e7ac91c19b52a07711c2ff17e3fd75e55f0bf45cdcf52e33f126dafd571a6fe39a673fbe37d8daf315d6bb08fdffba3cffc5ff59cf5ff3b781f7b1edd96fd7730ef30363bd8f8e9eaf5e9e1fe0ffa367a0d98f9471f66bd4bf86ea2c50c38f743e7798bffddec402ffd5c2db28e5d9efee6da4f0ecf7f5fcf5e95f3bf50cfcf023f39e9fff133af0abfdd7dabc8b4bfe3f3c6fa38767bfb2b7317ef6fbf0261efaafa5de4445ffd5fa26aaf8afd1f751d0f355ca59e18a7fadc4dbb8f7ec27e239fffdc6476f22a6ff5aeb9995fbc3d43cffccdf6b6fa3ab673f40cf5f5dfe3543cfc1263f92d673e6f51f07bd8f2a9eaf6bcfc3bcff41df3b97fd6cfcfc15e55febf35ca4f4e7b27b17dfffefcb73a7fa33f279a8e7771bf4266afaafb7de44e17f55f03e5a79be2a9f7bf8b78e03bfa8ff9a9cb3414affdad99be8e5bff67906c6f99166de474dcf5735ef2398e72bf45988e75fd37b17bffe3fb8b3c12fff5a7ece337ee08a7711c9ff57e70d86e1f90fec3ddcf12378f03e6678be0278068ef891d037d1fdaf47ce0a5dfc6b26dec646cf7e5d9e73aabf98e34d14f05fe99eb3f52fda781fabcf57126fa28dffdae3f9abfe56f15cb4f516fbf2263afaafabde606acf57576f63a8673f39ef6285ff6ff906037bbe7e7a1b4b3cfba53d177bbdcb4ebc8d749efd88bc8f969eaf669e83537ee4ade77ce2077ebd8f0e9efdaeef63fc7cfdf0dc697fe7bd8f4b9eaf339e83c81ff9e8acd0faaf3179cea2fe22dd1bccc0f355d673d1d29fcbc2bbd8e4ffd3735668e65f8b3e0301fcc8176f6291ff4ae6f92bd6bff6ea7968e6777bf336329ffd28bc8f329eaf0cde460ccf7e60ef61adff5af6ccd2fd616fde4414fff5f96c0cbecd023d0307fc481a6fa39c673f216fb004cfd75aefe386e7abd873c7f9bbd79bb8eaff23be8927fe6bdefb88eaf9aae7f9abcabff6e75968e55f6bbe893cfeeb94e7a19adf6dcefb78e2f95ae06c70cfbf763c1bacfe48576fa29cff1ae9990df083813a1b64f5af2178130dfd7a7f315fc27f4f077eb5fd6b5fde4714cf57b537d8f0f9bae93d94f55fc5ce7eadfd6b5bde4444ffd5d433f0ca8fa4f2dc97fe4cf6cc9ef8096c781b433cfb05781749fc7f7a6f62de7f6df2265af8af359e87827e3746cf4344bf9ba5e74ef277aae7218fdf6dc59b68e4bf967913affc573bcfc3b7ff40f240a1ab7fcdc99b18e8bf7e7a6e527f467b1341fcd7e5fb38ebf95ae84dbcfe7f6a6fa282fffae15d5cf1ff0179ee140d76e1f9eff83ca4fe6e2fde4406ff35c4f3b7fc8df626b6f9af8c9ecfeb6f1befe3a5e7eb99e7c77f07cf021dfd48386fe298ff2ae8b94bfc0de939e3fd40bd3771cd7f55f45c1cf5e70af1260af9af619efbc3df64dec74dcfd735ef6393e76b8db771d3b3df9ab7f1faecc7e87d84f57c15f4fc16ff653d4b7ace4b7e60a9e71ef277a9e7af21ff9a9d37b1cb7fe5f30c3cf22399bc8dd667bf3ccf5f8efeb54f6fa389673f02ef2388e7ebd9f317a57f4dd473d1ce9f0bc259a0db8f5cbec1083c5f59bd89a0feebae6736c44f80ec19d8e047ea781f213cfbe57a13a7fc5739efe1f5bf0e38f04bc0bf96e5f90d58f6fa2351bc8fecb39fa5e7bed24728cf571c67851bfeb50ecf2cf03f363a1bdcf123539d6d1d7f6b7a1339fc571d6fb012cf7f69ef6399e72b7c1668fc912e9e8584feb51f678524fe35106fe38d67bf08cf5f8afeb54e6fe38a673fb6f7f1eef93ae15948f6afbdbd8d579efd966fb000cf57eb5920a21fe9e6b9ddfed67b1f5d3d5f013d77a73f9f3de759bf11c773f1fa1e93f00c2cf123a3cf79ce2f44f02c04f2afe978831578beb67a06f2fd48236fa3a867bf396fa3a3673f2fcf39d04f74f0261ef9af66dec7e5f355ee4d3cf05f399c15c6f8d74e3ce7b89f95f33e9e3dfb9d7a13a5fcd738cf6c961f897c1f8f3c5f5fbc8f6c9eafd5b3c2fed7203cf7ab3f33781b1f3dfb7d791eb6ff8155efa291ff6fce9b78f65fe5dec5e3ff47e11978e347eebd8951fe2b9c6721967fcdf93c84f3bbe5791e86fadd78bd8f6ecfd7bf8973fff5c53370cc8fd4f236d67af63bf41c3cf4af893d3fd2df76cf0ff37781e762a83fd787b34dfd3bd4815f32fe3535678365fe35e0fb48e3f9eae07d0cf17c0df0dc2d32f0cc8fe4f2fc566d0cf3ec177d1365fc57bdb7f17df6d3f0cc8efd607dcf423bff1af51958e5474e79ce197e16d2db287df6dbf02e62f8ff9aef238ce7ab82e7bee7def367e0f3d0fd0fb47a137dfcd72ecf4348bf1ba7b3c00a3f927956d8e35fbb3b2b14f0afc5bd8f629eafefbb08e3ff2bf23e4e79beea781b213dfb817917b9fc7f89dec359ff75ec9dcd7e36c8db68e6d96ffa2676f8afedf3f1feef7a1f113cfbd13a2b64fbd7e8dec60dcf7e62cf59d77f1cf13e0279bea238db7c7f8b3a2bd4f2afed781311fcd70e6f239a673fea33b0d58fdcf3cccef991109ebf6efd6bb4ce061dfc484acf6c8e1f293c2b7cff3519cff9d56f3cf1dc63febebe87acfe6bf12cd0ef47067a132bfcd719cf3ff237d8bbb8e5ff43f49cd37e76c9fb287cf6cbf436c23dfbe5e75ff86fea7d1cf17c1df03c04f11f18f22e82f8ffe067219f7f8defb9bffc6deb79e8e577fbf126f6faff1ebc8960feeb9ff771d4f395cedbe8e7d9cfc9fb48e0d98fd5f317a27f8dd3db38e3d9afc19b08e5bfbe396b1c7b53d2f126c2ff95caf30ffd2df73cccffdaf54de4f5ff2d78fe7af2aff1791b4f3cfb1578668d3f988977b1c0ff27e259e8e65fe3f13cb4f01fe0713648e85f0b7b66effcc8b6e72ff88fe90d46f7fc277c1e86fd0620bc89b2febfb37791c8ff17e70db6e0f97aeb4d1cf5fffd5978e15fdbf0fc19ff6b3dffef6f1a6fe3dcb35ff0994dfbc350bc89ef7f8df2262ef9af6ade4434fff5d07337fa33d8db88e7d90fc9331be54712781e62f9ddf69efbd09f8bcf5f62feb544cf9ff5378c6776c54fc0c37367f91bd6fb28e7f92ae47d5cf37ce57bbedc7f51cfec813fcc7936c8eb5f7bf02e12ff3fb93771c07fa5705698e65f7bdfc747cfd72fefa394e7ebddbbd8ff5f846776d38fc4f0dc19fe06f206ab7bfe1b9e159afd6b70cf433cbf5ba0e71cfd81939ef3819f8df236529ffd40bc8d779efd8ebc8b6dfe3f4d678362fe35f63918e75f1b3eb3697ee4823751d47fedf52612f8af76cf39c50f3cbe8f3b9eaf76efe39ffd263d07bbfc6bfd1c64fb91909e73951fb8ea6d04f3ec077d1377fd7f0ace0245fdc83befe3f1d9afefb908e8cf35e16c90c38fe4745608f6af4d78173ffc7f3cdec43bffd5d2fb48e2f98af63e967abeda7917a5ff1f98e79ce7172e781705fc7f1dde4652cf7e75ce06e17ee4a567219c7f0d7e1ff53c5f959c0daef891a0dec415fff57d1383ff35c1b331ec4de9ee5dc4f2ff157a1b733dfb393a0b64fe481fef63afe7aba4e7f7c86c9c1ff9e05d6cffbf286f229fffbae97908e33f10e77dacf07c253eb30ffe30bee721a5dfadef392ffa8935de460ecf7e63efa38fe7ab89e7a29c3fd7dadbb8ebd90fd21b0cc2f335d7fbd8e5f9bae36d6cf5ecf767d1fb08eaf91ae70da6e1f92fec6d54f4eca7e55dfcf2ff317a17d1fc7f959e9bec6ff76c70d4bfa6f69c5bfd4613efe399e76bfc2612ff6b836776c68f64dfc45bff5fda336bf78b2d781733fc7fcee72194df8dc759e383372541efa28dff6fc9815f0afeb52e6fa3a4673f31ef61acff1a7636cffdec9cf731c1b35fade722ac3f178af7d0d57f3d9e055ef891cdf711d7f355d1736ff93bd6dbe8e4d9cfc6739ef203519ded671b93cf7e706fe39c67bf21cf4349bf5ba7b36c9e1f29e17978f61b88f3dce86d24f1ec8776d6d8f6a634e8f92b9a48eaffebe7a2a03f97dbd9209a7f2df8366a7af653f3360279f643f13ee67abe3a7a130bfdd751efa389e72b8167608b1f09bf8f4e9eaf369e857efeb5ea735ef103c1dec7b9e76bf03d34fe6c7f13cbfc5742cfbde8cfc7b7d1d4b3df9d7751efff33f32686faafbcdec500ff1f873731ff57036fe3dbb35f3f7fe5fad76e3d0f59fc07de3ce7443f91c6f370ef778bf13e529faf209e59097f989537d1edbfaa781fd79efd60bd8d759efd8abc8b29fe3f1fcf5f8cfe354fef638de7eb83f7b17dbebebdc1d89eafb19e7bc6df979e876dff0112ef63abe7eb9fe77ce7172a381bacfb9199de60129eafbcdec405ff15c4f3affc2de04d3cf35f0dbd8937feab8fb3c140ff1a80b3edfcbbd39ba8e6bf267a830178be827a1737fc7f796781c81f79e36c70d3bf96f63cc4f4bbd57a833d78befe3d7fe5f9d7283d77a13f139f81697ea49703858efeb522efa28affefc7f310c9efe63eb7a83f0f781f8dcf7e9edec702cf7eaddec5b1ffefc273aa37f1c17f25f1cc4a7f2480f7d1eff9bae80d56e1f98f78a0f0faaf45790f49fd61fe3f47cf4314ff812567817c7ea49ab3c6b43725e5f3fd9ac8e9bffabd8f5e9eaf79cf3fe62ef0f7f3c02f07ff9a97e7219ddfadcf73f1d39fcbc35960dc8f749f874c7e37a7a5e72ff93bec6dcc7bf6b3f0266ef8af776fa388673f01efa1aaff3a7c0f4bfdd7fc2e8ef8ffa8efe3d8b3dfa8b346b537a5e5fb58e8f95ae539dff8812e9e5fe5ef006f238f673f0ecfec971f69e01958eb47fe7913b5fcd73ccf0ff4b784f711d1f315cb737ef303afcf454f7f2e0e6f23ac673f41cfbff5b78ce760801fb9e87da4f47c25f33efe79be3e791b7b3cfb9538cbf2f99113dec637cf7e3f9e73991f38eb7908e13f20e24d2cfe570767854dfe35f7997df223d39e59eb8ffcf026cef9af929e813d7ee491e761d96fc0cb73fef1033d3db39d7e6486e796f037f5991df32311bc8d5e9efdf29e81317e64fccc5efa91169ebbae8b62febf47efa28bff6fc8bb18fdffbc3cb34b7ee480b35f1bfe3532cfc1253f72d6f341fd1de2b948e8cff576b6717431caffe7e70d26e0f98aea7d4cf57cddf3260ef9af62ce3695bf5dbd89bafebf03cf5f67feb545cfd9d45facf13e963dfb953adbb5fefce06ddcf2ecd73c50e8e95f4b72d698e04d09d0fb989ffd2a3d77ac3f377813d3fe6b85b791d2b31f99e7dcf713439d0d82fd4844efe299ff8fd2dbf8e1d9cfec5d64ff3f096fe3ddb3df84e760961f793d2ba4f1afa1781b4b3dfbd9791b233dfb8579132ffd575acfaffb4fea19b8e247f278ce997ee2a3e722ab3f9789e706f0b7f24d64f25fd79c15a6fd6b72cf79ec673fbc8f549eafedf317967f8dd0bbd8f6ffcd780626f9914ddec744cfd72c671bef6f4eef22d9ff87e1c02ff75f73f33e0679bea678176bfc7f4a9ed9087f189103bfbefeb52acfc21bffda8a3751f95f8bbc8979fe2ba667e0941f29e54d4cf05f3dbc8f959eaf65de4700cf7ea0ce06bf7ea4a13771f85f193cb3877ea4dc73867f60a577b1f8ffcdbd8b3ffe3f366f629fffcae9994df52341bc8f6ccf7eb1dec430ff15d0f390c17f80c3731ef103fb5d34f3ff4d7aeee2dfedfbd8f77c7df3dc72996df3231b3c1ff99fd2d960841f59e97d4c3e5fe19e874a7e37e673f1d49f6bc45921827f8dc2814259ffda933791fdaf07dec357ff15ec4dacf15fab6f62f5bf6e791bed9efd8a6f62f3bf2a791ff93c5f99bcc1463cff0d781fd13c5fa9ef23abe7ab9fe721a1df6dd2db48f7ec277c1f393d5fd9bc8b6bff5f89f770d57f25be8fc867bf57cf2ff2f7f119f8e447427913f7fe6b96e722a63fd785e7bff91bc1db98e6d94fef6d5cf5ecd7e77d0cf0ec677d137bfc572e6759f74712df4500ff5f85e7e1f05fd3f5fc577fb3781ff39eaf16dec65bcf7e899e81097e648eb7d1c7b39f893731d37fb5f53cf4f3bb217a17ddfc7f9bde60169eff8c6fe3f2d94fee39dffa8d8dde4753cfd73bcfdde1ef3167837a3fb2d5f397ff753de74d3fb1ef4d4cf25fd1bc8d419efd52bc8f709eaf40de47e9f375c3815f61ffda95b3eca31f59f73e129ffd38bd89a7fe3fe0fbb8e2f9caf6fc267f933d0325fc48e5d9a0a37fadecb947fc3de7d9d0dbc8e8d98fcb59a0f547e6791f87cf7e9b9e81217ee4f34d24f15f9befe38267bf5bcf6cfe8f8bdec706cf7e7e6f63dfb3df9b3758daf375d59b98e8bf9e3a1b9cfe4856efe1a8fffae70ef0b7f319b8e6478e791e92f80f1c791795ffdf943711c27f2d7116987fe48af750d77f25f0ccc6fa9124dec707cf7e7fefe291ff8fce9bf8eaff037bce397ea08c77b1caff27e84d8cf45f5dbd8dd0673f0c6fa2a0ff0aea7d94f57c25745638e55f633e03bffc482c6783c51f79e8f911fe777a1fa1cf570cef2283ffafc5db38e9d96fcc59e1f35f8bf1dc667f379ffbd19f013c7fcfdf10dec743cfd72b6781a57e249eb771d1b3df967751c7ff07e55998e25f23f1fc12ff5b9dfdc2f4af917a13effe6b90e7a1a2dfedd2f390cfef56e84db4f45f679ded5d7f6e7b1b133dfb65791381ff15c159a38037a5e4f3ad7fa3781bdd67bfb9f711eef99adf47bbe76bf16d14f3ecf73e7fa5f9d718bd8d4f9efd6e3c67e4cf3679fe4af5afad7a1e12f9dd74bc8963ffd5ed1918ff48226f30b8e7bffc3e2a78f6b3f5365e78f63bbe894afe6b9a77b1c1ffe7e27d0cf47c8df236d63dfb0d9ffbc9dfacceb2567ea4da7b78ebbf9ebd8b28febf1e6fa3a7673f37cf7ff09fd3f3d0d1efa6e9acb0c2bfa6e179a8e13f407d1b3b3cfb91bd8d649efd783cb30bfe30eadbd8e2d9efc0f370ee3f107d17e1ff4fcc33bbe547ae3d7783bfe1e7dce1672d3d0377fcc81f6f628aff0a7dce217ee0dfc537ff1fa76736d38fbcf0263af8af239ed9143f010e6f629dff4ae93947fa8938dec5e4ff27e55dbcf2ff113a2b7cf0afd9bd8b7bffdf9903bf78fc6b71dec5eaff97e6acb0d4bfb6e43d3cfe6cfd366a78f60b3b2b84f2afe13d0f27fc0774bc8f2c9eaf069e8738fe03a4de435cff35c0f350caef367d6637fc048acf5ff437dd9be8e2bfc2ef639ce76b903791c57f95be8984feaba8b731cfb35f92f7f17dbe6a781e02ffb5bfe70ef4e7afe767f97bc059b6ec07fb741668e747a2792e1afa73c13dffd2df18de6062cf57516f3013cf7f049e8542feb5f6f95dfe4e7b666ffcc8e0fb58e2f99af69c973f30c7f3d0d2eff6e96db4f2eca77c17a7ffdf98f7b0d77f451ef805fdd7e09c151ef9d794cffde2ef4acf40f8470e791fb33c5ff1dec736cfd71e6f62aaffefeb6dfcf4ec07e72cb0d08f6cf33e923dfb917a834d78bef67ace7f7ea1a56776d18fa47b83159faf9d9e834f7e64ad7771c1ffc7e24d44f5ff099f81643f32c69bf8f55f17bc8970feeba3b35f83fe354d6fa39d673f23ef23a4e72b98e75ce71726380b1cfec816efe3a0e7eb9437b1d4ff97cf0685fc485867bf94fc6b7a9e1febef176f30b7e7bf9f6d1f7f8f7a83913d5f49bd89b3febf00cff9ec6791bc8d9d9efdda9c05aafa917a9e1bd49f05bc8940feeb9737b1efbfe67a1b413dfbc579ce6f3f1be77948e33f20eaf9d233f0c78f4472f6abffb529efa39de7ab9137d8d9f3f5d4db48e8d98fca1becc3f39fd9f310ccef06e4f919fe9f7a1602fad77a3c0f09fc06243d0b05fd6b3e9effe4efb2e77ce607d27a838178fe3b7b835178fefb7a13fdfcd74eef2cf6b31e9e8726fe03489ed9be1fa9e1b988eb2de6e60d36f7fc173c1b14fb9189de470acfb9fe3fb6e77ce507b27a139ffc5737efa3dcf315f82e8afd7f16de452dffdfa13771c6afe977c37516f8e747ae390bccf323d3bc8f4f9eaf379ed9323f32c173e7bf71bc897afe6ba63758d9f3b5d4bb28e3ff3bf23efe78be9e781b053dfb49391bbcf12349bd8961ff55c2d9c6f567086f22a0ffdaf79ced7ed6d1d92f32ff9aa2e7fed3c42dff55cfbbf8f6ffdd781b733cfb497817d7fc7f98dec5b4ff0fc433d1c6bb68e0ff3b71a070d7bf26e5f965fe469e65a7fcc802cf6deacf696f63ac67bf416fa3dcb31ff0f931fed37aee497f167b1e72faddfc9e5fe3bfad7711efffb3f25c44f4e78a7b66f97e24d99b08f65f233cf785bf7f3cf79c3ffff9d2bfd9b3c1093fd2d233fbe247f6fbb8ebf90ae99d01fcec863751c27f45f1267ef8afcab3415cff1a83e7dce607fa3db3707f5899e7a195df4deff989fe9e7b7ea1bfdd9ef3a09ff8f60613f1fc27e06dd4f3eca7e47decf17c2df12eb6f8ff843c7fb5fad75a3d0f65fc07e43ce7e39fe3dec617cf7e09dec51dff9f96e7b3fdaff4dcc7bf93cf5f53fe353f6783d01fa9ea5948e75f8bcf022ffdc8396fa2dfff57e039a8eb5f73f00c94fe48216fe3a167bf2b6f63f5d98fc473f0ca8ff43b0ba4fb91786f639567bfe4fb78e1f97a7c6645fd480eef63a7e76b9bf731c9f335c69bc8f75fb53cb3f18f143bf08bc0bfa6e540a1ad7f0dca9b68e0bf6e7813fffc574f6f6290ff0ae66d44f5eca7e76ddcf3ecb7e44dacf5ff0d78173dfc7fd237188ae73fb56721fcafd1781745fc7ff1815fc67fcdcabb78e0ff43f19c63fdc61befe390e7eb8ae7dce717163a2b8cff351b6fe39267bf19cff9d24f74f42e06ff3fb7b7f1c7b3df89e7ecf71b1d3d7f35f9d7f6bc8da39efde8bc8dd3673f0fcfdde7cfc1b341113fd2d3d960ff4842cfec991fa9e06d3cf2ecf7e24d8cf35f21bd8d459efd5abc8d679efde277f1c6ffc7e43958e547e27a1301fcd7b66760901f99e4ec17ad7f6dd6bb98e6ffb3f4268ef8afccf711c3f315ec0d167cbe627ace4d7ea0a97731ecffb37b83c93dff01df464bcf7e66de4614cf7e6a6fa38d673f086fa2a7fffaf70c1cf223953c0f85fc6e399e87767eb73fcfc14cff1adafb18f6ec17ea4d9cf05f533c67083febe66cbf7ff7bd87a7fe2bf02cb0d38facf326cafd57176fe3a067bf29cfc0583fb2cf7bf8f75fd19ed9ba3f8ccffbe8e1f95af6263effab93379887e7bfb2b7b1c9b35f8df771ccf355fa3e367abe7679ee077f4fdfc4b7ffbae23998f6231bbd8db49efd0cbd8b14febfe473aefbd946efe2dcffa7e3393ffa8937ce7eedfad770bd8f6fcfd77e17f3febf2def23ddf355f8fc13ff5dbd8d809efda0bc89f17fb5f23e6a79be320ffc12f1af997917f9fe3f34cff9c90f3c7556e8e15ff3f03c64fe075c3db3a07ee486f7f1ebd94fd3db18e5d92fc77310ef475adfc4b2ff0af73e8a7abece792e6afa7365780f5bfd57e3d9e29e3f57db73d37551c3ffb7e32c5be44706382bc4fbd75ebc8f749eaf44de47b5673f57ef63f4f99ae16cf0ee47963a5028e95f33723658e75f2bbe8f799eaf49de4457ffdff1d9487c9ff979ce6c3ffbe4acf0eb5f6b7b1f6b3d5f0fbd8fcfe7eb8567b6057beb479e382b1cf3af41df461ccf7e6defe3def355c473b0c08f74f4fc65e95f1bf526eaf8af42de6066cf5753cf6c8c1f99cf36fc37a837f1d37fd5f52e3af8ff5e3c030bfc481bcf4331bfdb95b731faec97e199ddf1238defa38ee7abdb5961887fedc3f349fd3de27938fddd5a9c0dc6f891a2ce068bfcc8586fa287ff9a7c1f793c5f393c1bc1de94729cfddaf3af557a66f57ee4d8fbd8cf7e949ebf7cfc6b749e591d3ff2eb6dacf0ec477cce1f7ef6d27b68eabfb2cf6ca01ff9f62612f9af63de4436ff75d1db78e9d9efccbbe8fe7f559e7bd59fd7ce02e7fcc833efaf59ff1aace7af57ff9aabf731eef9ca9e0d22f991b2de44587f901fd8f7cc467f24d83b87fd2c87e722adb75897e74367856dff9a85e7a2ab3fd7897711c7ffd7e45d34fbff34bc8d469efd5cbc8f6e9eaf3ede4535ffdfa5f7f1c5f335c1739ef0b370ce0651fcc84fefa392e7ab8c67a19c7f6dfa2efeff83f0269af8af3bdec418ff95fa06dbf0fc27f69cd7e4c37f03df452fffdfa267e0f34702791bf59efd42bc8b4ffe3f3e6f628eff0ae4b908e7cfa5f6366a79f6633e03b3fcc82acf5f86feb54def228bff2fc8fb28f6ec27ea996df523453c0f0dfd6e949e3fe5efb3b791d3b31f9be7aef4e7b13798ddf35ff15d24f0ff857806cef891d4e7a7fa3bc5bbc8e3ff0bf3362279f683f15c84f4e7aa7b1e6af9ddaaef628effcfc973a9e71b67bbc6df98ce067ffcc857cff9c02f15ff1a9a3791d5ff577c13b3fc573ccf45467faeb96760da8fac7116f8e947da797e25f60bc3bf36e6f9ebcdbff6e84da4f05f633cb30dfe30bde7e1da7f80f82e96f8fff6dec750cf57396783b2fe3505077ebdfd6b609e3bd39fcbde475acf5743cf17f5b7886746cf43e1bf96eb6da4f1ece7e07dc4f27c759f83a6fe35b667e1827fadee2c30d78f14f47c3d5648e45f4bbec11c3c5fafefe283ff0fc6fbf8f77c6df4368278f63b7b0ecef9d788efa2f0ff7b7b2efaf97349781b033dfb457917f3ffd7f63ece79be0e79135bfd7fc6b32c921f29e04da4f5ff097817c7fc7f90ce065ffd6b099eb3809f4d725618f7af61781f6d3c5f213cb7d8dfcbe72f24ff5a9eb35f7efe354bcfc0e88f0cf29c5bfc4000cf46b637253f6f63a167bf2acf45547f2e12ef61aeff9af66c64df667f9e59b63fecc6f310d0efa6e819d8e64792791feb9eafc3e72185ffc0fb26fef8af5edec4e97fadf206e3f0fc37f60c6cf2239fbcc1483cffa13dbfd1df149e8baafe5c25de4509ff1f8d03bfcafe352c6fa38a67bfb5e7a180df00a4f7d17dbece3d3fcedf09ce0279fdc8416fe394673f1dcfec889f8085f711edd94fd5737ef103d1de4717cf570467f3829f9df21c9e06a894e8dd6a4296c39442c82003061999a199090001d31340e09854220d07b3244e6154b4091400065d9c64f1ac064284903106000000000400004000000001ad10f607323d1ff3e06cd495ab1c7493d74b82073fae0c073e3a2a48619b457377b01d5ed0a5f9eef2704eb48863ca81cfe1d5251fbe8371797dfb5f22b399623324310b11eda36616c0d7c1205deab1722899cf10281c09978c1f52c5a5b2b807bc31efb2d2dbf412cf59fd6a8764b92a43b143db447033df223522b0e1cd50ebd3f222dbc3da38f2ae4e663878fccd5dc4824364c13bf1d022943b23aa43b1e07ac6f394628595e0f65e0dfeffb11dc7a99b64c5bdbb8bd4eda0ae2dd79de58d88d5d1542757dc77f49ea82fc1d3ea5d32697afacf530fcc4c6e59a278e23255c800f690b1b09d3b0517c6c7ca1b3d34b8196b0b47d35a4f4619e82cc94cd6f7e399f0f2d00b6b1054f532c2a8fcb2bd5b15c337a7ece2c36558b9c4ff6a50d87e298a13b208ee529d58dc4ac3a42606fc4ac4961b49e9f068f0c3453aae4f948f15fd9e4d91bc37ad48dd6941412ad32d57593a0da9da092dccdddfece587a5089e59e09afe589702247c729438170181bafec66536339ce39e7d199d6fa61ceea4e8debdf7bbf49044b72088f6d0420bb93a19bf22911ec76ea30b7c89bd6882a082b2fb57715144e593526e93c506f0a2509f498bf609164e1762dec085139f45e799a8c421ed41840cc6fe37b17b3386baf8be04f265c8a228f327a80427c08744d005ff57c4bb5ca169bcf0fa2027bf44a3f1c340d25ae0a01d7e188813bcba9f47c5492bf9e8e8e54cb86d089862da5a735ac1c203270463b1613b183e2ca4470eb277145e73f467e18b8681cf33567dbcff309111017c45cc0da5e23332e3508c3b105d2bb7aea832db1471fd47d60d321d8264be5efdf13c31d648b2efb8c77fba2b9ddbc1ecd4c002f6327c8364555ce106dedf45aee10b720d2a3ff47e7370944cba54529a868f7022961ecbd45e0cb63190967d3b81d9b707b206a30a301fa067c14da122a6bb9cac0a7c06e030bb402fb330e53328bdc38feaa0bd39a7509e20f0be6c51e18792a0b42476fe62fccbabb6784faed9e5349a86994233e03831cd37be4f18720992d073234d2f23fe83dcd4b5241c021ccf86e4c8817642dcabc1d6b5a05c4653bc806e320969f620e7f1d3ab617d20359ac4636d8e25fa8bb75c58cec8dc3513954397e020c17772682c698ba4d3069f50ca78b98790f1bb3f2c1b9a3224b0635183b9067136bfcbaf777d2bf3a44448d8a1edbc193ee0a8574025ed8098904d5e9e0f34a8dfe0161cdf8beaafd7fb984fb8de96be6d2d22ce48db8f82d4e9a8740ff93ffc18ccae489b42e328f67c97848fc842d797c2efebee9ce57ba104e6c20359ca83fa6f726a8000238906111ac54fc4fc19a0b413285e6145e641f19c51df1c00e7c7236da11b1508f9e1813d15881acf8f5d4352d683eda39a08fa51fb1701177c8b279cc92dc0ecfacb5ecd914f3acf6d4e7c2c3d49f94620601c72e0bf31dccb01ca832374ab0af901627834d0f23ca6afdd8758e2bdd5cc449bf51304f64de11980f84b5542887c6d7510f02dcf4945e7341f95ffdcede140d81afc1e0b2db01c9dfa5c2ec94bf8bf28ac982b1b16ff2458241f8a5905bf043e73e059072c77eaaff9cdf0e1133fe6c896455799dbbd4e5ade0ad4135aa32b226f06f0eafb483d061d71034aeae58243f8bebe1bb91b9fad000e3c106f0769928cc80e1f73e8774d070d634f8076c9770a38e96c86dcc676ed94aa16db3c57372b80fba80fce9f3e745b89417541684907c433d633757551aaebdf631277fb3e1d39baead97ef898b71a767b288fc22ea763ff65cd427409518c02ede254c27da870bb646e2c78df1443cc5ffb9731f9f996adf4510c1d88fa0bb5fa2cae29ea5dd5b5317e61006d554453fa477c28120573526afed3c5c4a5f14dde1d5f2be230bd807e4138bbdfb1d2fbf6cf37c243b211dc99a0c12d85a615deddf424b2d2e8c74f4f543c202fd5400fcbfc9bcc058963694c4ea95b9e58ade7f90184ffc18ce883b3d007ede04c5a086d39685596a29a1c151e204c81703fd518f8c9d3e1e79db0a5436a6699ce507c2fa0c4f67d189ba29bfb7fecbeffe8fbafe8a07204cd2b4271c17d520a5bd4660240df0ab834532bb0084daf382fb92956cf4791741e43281e398ebae27843ef9627381e1abb38e5b7ebaef0507a96fba52a43fd0e0a286179df4b24e66ef0c10f2436be063f88703fd5177a0ecb32314600a8ec4e9b86c704c4f701edb9c381e414d43971f7186e5951fa56b2a5bfdad306083b2c4d2ddc70bd6361190f2c4f340c48d12962fb2eb4fca0969c08b4b5888ef52ddf4a74118fa511263ff3ac9b4ff1ea746f5ff6699bdeed71d855875332a1c7af42e46753ae2bff1aca7b5417c278ab477f2a509c1e6ae4a0ef0d16c4afdc26bdde5cb41e9a31c48279c9552f5ccab1b01925171cefb3cbc0ac23f7eeddb90d0bd2550f03c22a7986daf7198ccba1bb365cd57e79ecefe165bd376ef4b1f4950757261cf27186bb6d8459f695e0fbbc31fd123cf29f7f1ef223fb09433ca7c267fdb08c6c5bd3207f82c2022296e07fd9e18c3ecd8c9502fad34c9c3d311a1f2612abaa86a2447d185222bf6f5b478b06349415bdad89bb720f895fcfd6c3c536bfe1d1eb31dc89e6e67cbddec435f1e7f2567cbc3936bdc40ff4a6e006f343e68f7e263cf6fdd2f85f9c00edf5b8cc2f9991999d10324d76f3e0986e6e4df8d1cf5e317de15f897931b19723fff77cafdc47715c270decff3e6798d0163ff5160ceb7fe4c6fb7971e560101b00799a71e95affa62ff13e53c8a7514ed7fb13af6b38121791fe36459ec2cd7f21d79d4866cde041f7037cdc9e647e56d3783b0c1ea189d1e9a5618176bb748de6a27eda95bd9627d717658d760841a6bff2459d7533c31338af9cb24653b943e54c551c238d99e31de3d85d0876abe37273f0b8fc51d4eeb4a2c01aca8d51f14847fe5c75d22f16f24a04da06fb70205a971aed11e33006fede74e1441976e4b41a24966d4043efdf9b1f4c4dd4bd96e0afae53f49c47a59e2f47ee5feea3a7644372ef23930f26278b2cf777d8e35f6acfef0d1a481e8cd406191a91ebb81849343422c8130b95a3307e27e824cfb61ab6aea4db35525f389096bcc35ab172a68e4a616cc7a87fee5a4a429806923b56ea58294f07ca6671d1ab8a962d4bf64ec53a9edbedb8ccce4dbceb9175b06d2c3b8792cefbf765ee74bdb3ebc55089a4c3280f396aaff913f8986fb69e8c71b4d8adb8da1fdffbc603ce796a01d69f80db80ddf45bd62712846b5c145bc56b8503f86ccd7e3b9df24206dddc35f65ea98af6aeeacca573937c9bcd697cc8ebd4c9e98d479fbd71306e2f40be980bcab0dd002a5b1b4fa9c70803ca1353818cdb0da0a255a3147b8c20a07c31159441bb0154b63492520f1105c81f630119b51b40654b6329f41041c0f98b695f80c93f0beb95298e4f3378097c6751e377e9f6b68f37a578cb2b639c22fac8594e649cd54e88966941e063407a9f6bef0cd94081091bd1f592b562c8881c0d3c982a9e3d9cb0115d6f17d9834360fe96d53fc2e6d5c47f7232a9419a40916da52d916a8494112c228a86cf256bc7940d5ada9259b15423a64cf0f25675dff414117792b762e48297b56456db9cf8bd03b948db3f4e66350c52e89957b6cd59a3512a26195e2f293ab6658ddbea3883f165643beaa7f7f4442e62ebd613b707188567e059100cdb00dd6cdcaac8fa2a28985c1bd108cbac50b99c61f03a260cd786e849c55ac73c334f7a17d5162fed704df7e20eb6caa49bd1741c86137d55adaddb63e30d67f61a2d758b74c87e999f3635d9456905e23fce4e887061246cc6eb07e3e6e63269fa31ebabb22f1dcaddb65d2866d9c323b39e23413248c23e11bda4fa58741fe31fe4361c6487a067c519e40b22ded578252ecb936d36c8a7bbe5a4c4b8cea73ab7ca1bb373413dccb99d6861460a6846fe976a65a7d6f6deb1099b76aba6c06af8e56a9be1743fa07602f092bebfc99aba713c9e75792a34fe7833d68bb1599089bcc29a39a682a5e43a7cec12e887e401487d3dad9b65cdee286f2a535a55515c2412b326deede35e96c4074e6d1efe3f02a6c13d42736968693345dbfc60924ea4109a39bcdd57ba6379cd8d092f554eda541ecbe1cdfb4c77ce8aacbd342c61a9b29600ff03bc36f0741e2067cd8c4c95005919b88cafaf0b9221d2f0e6b3a1d7cc1f9899b7d7930e8f7e37fe53b1e5aaf4796a2e4fbc140cf7ae9b515b3f08a70c5f3b87be7318bd5d4b5b4dc9759064ea3076ff09cf0c5974b67a5919025aec5a9b1cc8c8705a28cafa5abfd327ec282ffdf6b8753417a73e9036367c85d71099207474316fcddc7aa66ca53bcee837ccb63999f81879e6fca1b77ee613efb20d5cb00fadd208f8eddae5ad73d1ed2db7616f1cd45cb981c73d6b5a8e1f3300e754bee56f380617f4d1c4c1e5cf51fbfcadc66ae9ecc08e90b2c784d8f52ff6f06a370fa6befbe977d831eccbd565e63b2cdaac5e93427371b016b6912bc0fbe2274a620e96504b36d9526cce62e691f95ad26129e551a2bace2c731c8b01e58c2565ea10395712ad8d78146dc02cb9108785b9b3f25fd26d440e599a0bf545d3843f4e33792e7f358071f5de1a57daa534cbddf64992f7ae0a2bd478c23bab9d8c0072616797394cf5e76f1bf9580ab206affdd038ddd5b938b05fb4f6c699e1faf19125c6c399f53d03cd52b1500d8c73beee3742bd6d6c98c5544b0cb6c72ddf672c1c0d5df59915b16676b6d472326d5dbd12d62d8d8fa098567d69db7d9536c65b01b2f019a74e69847bdd4037b08b8eb199db6b9ead3869ab6afba662f5a6de7255a06094ed2db97d0c68953a6d79ef5cfad759a3d8bbccac5ce0f62cb32b77d536fb1b4bd6ef59fd2c49c25463af55d2a6713667b7261f2427550399b92ae0371fa0356309dd9898349f6b6c5c94a5ccb9ebb4af3aac8ea1e703e2dc0f7d2dccd7a592f0211c073f444484b3d8797f86759a865c5b6d788276f1da74d433f3b55d6bdcc6006c842504fae72f20447b1ceab8beef75b3c6fd871898269f4d8d61e7f65ebf7aef7bc5beca34bd4a9f59b5f7b14091d20d275da8ffe4b24da2fb70a31bc5cc5c3efefb4e93abde7635157b731e69a9dbf4fc344af081ddf7ff31ffb492318196ae078850fa92e9b5b22ca5befd87716577bd33de19adfa27d840e4de7fba2722c9ff764f557b67e562e925960af5e634c9b61b8766fe830171792f3274f4d9ffd523b7c26ca75ed171f3e77d1930a71a0bba71de748be9c3ddd0d59b8af53ebd01bf7e5cfaaf0febc17e641e76a3788161f5b2ea479f09edacd893b43ad73f1d5f98f54ea078ba373a7ab76f6c28614effd63966d81bccd09e1b1570f4ed3f46835d6fb52589f63f8c8918eb08257c1d5cb138e96bf254e01debeab22d1b07437a097b79092afbe9360b3ef61d546b04358fac103cf23236d87a7b20b1c3f9e21f7f5d12caa0be5471ca75331bd90d5e7c68f4ad0d3786cccf6a07f154dbfaa7bd3f55e39c5b41b72e047dd54d6f66d57c480032eb1310d08dfc21b7145adf39cba5e083b9ff88f55bfc6ee4f3777ab5086b537584b245b5d659f51890789010734399de7749731383583d15641503200346675f3bafade5f458682de7101bc28844a35b188ebbf2f5173439f2ac323c1bbcdc5ef8dc7e9de64d7e70392e43e0cb615791b8d58b64b54e5e94e6c0eeb92a31780c31893d764f9055efef1ed50425be30e7f2eea7121a5a8457c09fac4ac5e0c7f0265d9168a70fb029ecec45dd0db47c9f1eb00f2fbe7582dd549c58f6dcf0eec874e6b87dae5d890aef5af389b7309f9963e15c0023d19a273a3722de67d420aaef13eb29ec05040a5c2a2b92e241eab7e79b9a7b36e54676c6178a27ffd9c1c12477711aba2f63373a488c1ace992e0e9bb3de3a93a43787d898083744e573de45e2078a0caa0e1cbad7e75fefabab33379b015b6c53879a76e0eff1692b2f5550ef702d6cec11a05f8b4bb6eaddc5e388ff6ccb18ce91cb1519c4ba2df7f11f16e44fa9b3325da023fd323fe5d2acf1da11461d22d6628c5249b7cdf5f5bcf51b2ba9fdc23ced73d82135bec1638b7be9dbdd7c2cf8f8e625570fba8bb46c6de003c96c1c317c30bfb4d76295f70447a0ea8daef198557819077908e8e7136fc31ae39f7d956a7796432ce74560516f7baa01af16ed50fd72fc714f2620ed4718bd896ccf0a4c4b2bcc6c09ffce84c45c3413e2e92d4e2407b8e0125d0e5f8ed8edea222d37cf9dab3bac5f1a65f1a9fde18ea2970370fd9b4710d93765701515f3e1ead431c329f8f44a58405615406d4984bdb1748fe3ea630eac566c9a20c3cf255bba7d27a59d537fda576dfd4f68a96a28cd792b3a0dac41625c513925461fd785583ce61849ba4dc3f651e57ed3f6a0be9707e3dcb7a1a3815ab396947cd6d468252911ff42a3e121e0aab922ad92356f55bcb8f3957adb12d37d1774dad7202e9c5bfc49818accd686fa1839310d691b4f77a0d6e2c24784f50b59625fa8523ead03176306c64c1f46b449f58f5cf80d970ed6f792acbb6ef6367565ed9a639388fcf5d5dfa51116da0aecaeb2c13957635e86d949801eec2bbee93386e7bfe3880e1b85159375ef980fda075ab0d2074aba8521fc5ef330e0905746ca4b12009304f89e34f38beed86248c95b1934d9d112038d4f5dc19993f6b2b70343a1a5c3e0de8e8c5f1f6a1bdd80399db9ba330b3542386e1afb904e71cb996b0b358bd8deea2e1ddc26281d1c055972a3f27de5704274b60d560110bf5e09cfc6fdd2ef0484e7ebf821da883058e5dc75605b25073d9c47779c54e8b028d7e2fdd7dd9203c19b1fa63e02d97afa429d7ba9952b65ff3c2963dc8a60bd0d746e92baedae897a02dc6db5ce4b3e6ce387304de16f8fed5be6fef4762a20b7aeeac5d7e0f5f3c36370a262fe05585078b51a6b7039d40c10b402800f60abd1e55f636b547f55af08c7aaab587e6c987f9ff778725d287d906c6adb6f52041b078c6525bb9c672fde7cae6a390b1cfcaaebf08a1d17aef461deb3edd48995efea107264dd20117b9dfcb6959ca5e3126e4e89265eda74dc5793515e7135d86d895ecb3f042aadabbb5fdd34f8c74d7f3786ae7f60b8d6fe377b031f5bc8a0cfc5e73612c3ffdffe2713bd23fd0b4c6db218c8923c91d6e612c5dee4d7a55dccda3b5e6e32431e1b304aa44070bcb0fd8098ae7c5c1591df994c5c4c9e754ce6b59cbbd97a2b056e956162cc7020003a2f579cbc5cff2c728d10a9b3595b717498b7211da83d942ddcbe6035123193ced7a7df86510e435e88780ee16158e5cd2345c3d44afacf1b0551f510098f3c3a0b921dd1569bc13cd54e8f7853d3bcae78f62ba428bb7aaf153328926ee77fac5b8a3672136fab27d03e51e485f5230a0362cfa23c31f9be89b6b73d2a15504f00cae99bcc1b122670d1839133ed478d1dc26955b8c0513e72697996eb8be351aa59d8c92cd983b87733cebeb53decc61013b822db066e71bb7f97f800be25fc527ab159dd2af7a0b35d0f4968728ea0d538636b2be9c416e7b5e3b8e03a146afa0586f2a8670ff8caec6336c04550eeec3885b738d400dfc1d9824a0f67a6ce47c13f19b8cf6cb0918e3117cb35729e40e26cb2e373b3cbdb6363006fa47000b36702fbb98cfe9f459e20becd8bba93a1a9f4fdef96a7c88c4fb9e423e1f1abdd03bb0ce3f971bbf341e55501b9a6bfa7bd0b76c8b3f8c9c931b82025c4befe55494fcb775b4bb41ef0bf6a94c35d30953d6e7e4d007cec5c35acf54997226ec6c086f96d956225ed0c129a802f98cebf36a0dbc3c71c1d62657d9d7a51f3cfaeb7fea1c4cec7861011d35fb2a7badb4e2393680fe2f1532e3a59b526235ddd2a6ce5da2cb3e30cce7ae7b4de643356efd0eb23ec6ef07ff426f610af88e4e85f6a0bfc20fffb8c5be35a3d864e59d7ceaaf235ee800fb20082d7f83f6b51d84107b9c74a5a9d78e83c27864ea1b5faaac83686c2ff84f03ff5afd736f5b56207be6b37a29b41f6c7e167cbad938a867d21fcc706f1210ebfc41ff16ed5757b20ed5f81a77c4e99fb0183eafe0c367e0c075569d500e224ea113f10e44a65ebb8fb84187b8a90f74a29afc0e8c99af5a27d2b007c205af4a28ee8afa63cd7cbdf821d71b5ee944266c19bc85ebae4d137752373832ef083f94e4ce133b5d609772c0b4d56a9a059cd70bb6961b03345d377f2e166c281b06f39cfe241342854ebb0878e541efa13d963befe34d0ea1fc2d9bbd254ea2010b26d924049fe58c63ff6c7c5cb8dca78f8b0b2ff06b3d71d3b3b16a303fbe01998d8499d2dfe0f08c09dc1680097ae48c8d64b9b2dc70f2aafe29cf417c0b2e6afb60c70398cfcab66d605dc5f575e6fe41997242360a86bb906dcb8f638572decb3030ea6a19f0403586dfd07ff376d34888cf20641d4f1a2d79e2f6c309bfc9e9fd27ff0baded303b68c7f063aae34cf7429abf37095627faa985d3aa66f48c6cb29344137deaca5a7466e2f384b1e985e7fe69573fd5082b3b42957fb20a473515ae55b15caa0cae23e2d407d0d948ad748e01f1f10be65eb484f4d637ef9417cad8b6131c91970d47cf6a2c2ff0cc995b4a1ac59c1db753d92538d9be9afe786f1013f81edcdd6a52158d30a5d55e8b4752984b2c6dd2785c82e9b2166c36e9fdb0aa6fc53b365d5972ff1b6d44f379e8438fe698c5e528bbd13db5ef39d750e4c8d29d466db7d44adf359e2b70e480e6a1f055c17206aa1171c98054cbb5067dbd5e6e84077c8f91c9aee15913c1a9e9f116cb6f0480664ae9becebdd9933e64950e2ba304fd0e87b087253e0b7bb1624c4cd3ed42e4f72727d262f9bcebd5b07de8c1e13f59ac4a3f3a25c99dd50ac0ca5d510ac86663534bb43af1356f227d19e81e74c557a6f29013fff10302e1403c18f74d4dce6e886ae5fc45c6e90042a2199265840f5ae6783b1d0c11f68f857bc1100344602e746f3e228732e54bb4f27741619c2633ec1bb9bc909c79506647abde091ed7231ab1c79ee2ad667e57cbf490bc6d8f3a682a5dae08b731134b0dbd2b109109905c524f31ab39bf0178127d78fd1cd369ce2e9d41cb9a47ae3b78fc5cff04b4c23fe463c27f75a7887e9a2ffc3857bc575dbf5356d87b7c45d53c627e43613c66ddc7fdacde7d6694b5bdf9f32273641bbea53e5e0202ec878d6c188d95b1a73b13acdb077b3f4630b690ac18eab4aee9df1d87cd066f93b8f1bd37d14e190b2edef06651bffe61ac1f7233dfd79b83c5b2908e7007b8aa21dbb231fc3307ce20fa9a639241921f17d1753d822084bfc0f7536eee4a1a3d3d6df1cff1ebbbfbe45f15c25f851154335c2370ac267d83822e017f279cf91833e6cd99d838cb8f95cfec2aa4d5501f854e908e7c63cc22787987cb18a5ec3b3996d838ba7293a145a802d5fb844ffb6e9d6559ed267fc2a0eda3923b11005c7225c45672698f1a768db1e2f10f027fa100ded30818714ab6c0350fad27bfd4808e84524f10f081ac43ca6fdf79ace3b74ee07e1d3128fd36d40b1e81604df5a9889c596d6431bc3e5fd4e998188ced888e3ec33829656caf4a047fad1875ee95d09b195d8f26c88ac1ce91edad12594f58a4fccbd447f75b60becd9cae487c0907b2eab3b8a45ae21f9230f95ad3f7b4418969eb8c63062cd1ea8d90d51814de91abb516e01adee780a4b8edb2f0ef02388385ce281cec1cbc7b2e882ab3830634d3d639ece9741aee9c291a6a63565e3c6ae3ac816268c065d913ad14e3d8108673743b29239fcb3101ced61b498e9016b8567d23c2f23373be218be4534eea9bae8722d353c805ca9c01e2a3f590fecec279aa568af6df50d7a1a2bdc6d1727a069daad564cca7e079261654ffa69f6ce3a8b3b62a8203b69242e9c459e4a91a33406d74a536796025a8b0e17874284b286a081bc45300904cebcb88cf649d3ade69ab0d698591d1eb8d40cc883a58799735dd439bb72723703042a2070041374a1e18f89b1f820bcf2c88e23c4a759b43b1710770788c1031c1a760a00ed0a2906b3a43bbc13488be48861a2696536634bdd1064e63be1dfcaab8b2e3067133290370483b2dbe878315c159a31946bf113e286ab8e3dfad0cabb417a74a927de7f35ed800a02c3c62c59954a799b2aa38cfa71a507e4520354a012755733db4b27c8603313ddd8fba6554dc85a420a614929ebc22b505ea6dee20bf769116e007dd9967716b39d24c63352ca0cc37011f24117b7b4a0b4892b4b26896e5db3b101574e26c6f53d2e3962a1dddd0cd22055287cc9152dff745928bdfe6f27ff46e7066c87a44cecdd4426b9af3e2b4c5559393ee2d133d9d053458bfc8003fb5580379a0264a052d5d598d4faff648581a0f9046737fd944155e13ea7b8366449b6d8bdc64d8f2523fe088c67d68e5022de83d329704a2a3215283f0f3fb2016691d62dfbe75bf703cee3c12aa6891c1b4378e67c987da3d6f331bb45779e5b062cccda0d87f348aec14c6e1ed0ebdc86ef4dfc4136f4a510d1259d3cab8132f9cb6343c6da184150b425424f7a5167ffe7733d7b0b20acd01abe22393f12bd1be76aeb9d31b3817d6a6820df44476f2e144e4c6738f4a5114958889fa6f895857864fdcfe17cfd0fc10b062c442a15c1b46319e1f5a3393cd6790e8db84d71acdb845b7d71a9110b8610cbfb357019875a18c66b8f5e3bb11da43915368555021be2266d4d836fab4c1941994e592fea433c800f7a9b9fbfe12ba6a6068e1c79f6ef18c8a6379468b23d0caba6abc1a59dd02037401a93ef23e597eb40a8b57358eddc7d6a56206f2737c44e25d83fa44b825ab13d86fc90be874985ce6a9b33570f3b853093a07c7b23a6c3539461d7ab35d1eec3c2916d4e2cdea457d278fe90cdd2d1aa06182e87cb918835427e131437e554c25be8a7e94ecc6fa6bd40d592ef9ae03a743160a70c941172b518549a9c60f37bab480bef8a0cf8d96c80cbc250444e68cff81d9daaca8c20b3b5f2b61866c4ee8260f477d821ca3474d825e25750fcc30ddd4cb826211f38eb42d979f563ac1b3927b4015f5e4f0392538cde0c230dd31d4517e0c587d7089b1de11d12986101f40d4285c8402e284a871236b82a1b1075d2b4e27817502b953cab7132f7457c5195ac60d0027c4c294f4ee4ea93a831882a311e8ba535b19a13454450daa574c5cc4b955a99bad2e3ae7d18dbd4d0ccd30e2e39edce49da0c4a7cbf51d02e115311290e3573c36b3e1bffec621a93e15560fccf5d94e0b192d78f3cbb44aa276cce19aeda1846e96969d65c3968480511272a8a27180af7a61170d6357f3a70880e0518012b26704ae485e49436724369f1127d84b6143da0116f61cf919895109c5d90e9a08afcc5654b3a35d8e40c818c6a293980927fcba55b598dfd734a85fe41244dcfd3510c3c563f64a5316ec0ea75ee593595da94c0e6434efe7d6b4f76b8970a764dc0cdd95a8595a8e8da0ecf9721121d12c542f091b63d21ca35bb168ee309c0f5a508712a98724bfe5e2bbef2d04e3a0c35913087cbd50e8de391b50398367e1e5c390e7947e43883a078727a601a6c23e5107ab054e3c380ace4e04a2503abd5079bc154dc3301926976283ad2ddcb9bec07749b3ca95594dfadbbca97c0cd24ece9cd8753da9e1832a5164c0a24c8c952afd2c9cc8948c38b812443aff69f73a775dd24b9b1c694fa05ea94c4c4e148288eb9e711739a07a56b148ef964f652dae14ab84f93e79d1e7a872dec572ecfd441c7e918951e56cdbb2063675c9188d4bb5b8bd8d2caa171ee2874f03e8ce52d5de766a23184c6dbe819562e7487580671b4b23ccb463905da0741bdaeeb4d91864110aeebbad694691084c3baae6a2dabeea2a6d6295576290f4241505755bd29d36018865555d79b221d866150d575ad29a7611804755dd5da651a06415857557d5ba64110865555afb76512846150d5eb7a5b24611804f5baae3715bbd5bb1cd2d29e5bec6d5dda52cd468cedeab5b5ac79541b19359beec92cc8904da47ae46859a3a952296ab49a3b74ca5c31e55c397a872cb2478dd43db26859a257ed346bb6ca1daa695d5aa335b43f0b6769d6cc7ad968585874de7e2715eb3a5d76415bceaf94a3b3e674c1c2942ebb7829c432d8e91d9a4d6b762884789884948b6326763ffe2c426279cd29b088e0ad84abf5f0591c0244641d3beb9c73dab498c640a342b695f1abc54c5d012fa5dbc82b7f0fe2d1b8167eeb94269cdf1bf505ffa6b31684986dbf70c180d9b5794e58c9a0ee5b9216c03d83a1bdcfc0084284c9ab9257650c10bc91a1c19e4ec84e6df0f1c49caf6783a95302e479a9eb164fec015cb4a3faea4a79c1e259e783f45db0cd00b6d09d7f53ab0c2800ed3fb4f407176e383ed43a95ab60e796153650987c7383e044dd4c22b77703e7806621c2c476118b1ad7da558a57f86b1400ca0d70b27cffc152c31a9a63b23f84985b9c3dd82d0f3cf9d30e80c757db6a13bf43821150e31fa3844273b430246d99ea030535856330c1dec534ab54488e86e2abf7bd5f7458d937317dd735326fbf789bf74f865ea9d507a97ea66454bfa16376c31938f72288d58a3d3944f93c45488af9a970a5c9387c97a05cd9232b4691ec3650f2e01662b6073499c0bb5d9f8512e238e840e2b8c60f2dd5e81f73d7290d88a88b9360966a29ddad29f6bb6cd602b46f65b871c97ceb76917ca3fd23a747200c4a923088946ee3734a0acfe1806b13a15dfb8268097065109903fa7cab49865df264d07015bd6ea0c70f8fe233ccca27ebdccfff3fc527708e237f65c1464ad5097ade5f2f553b419f393688361c5634197410ff210bed7394f354afcca855cd554e9dfeb8cc53d8297a2c9ac4c6fbd94e11eb9fb7a3f3090640629725cb4296f6860e95224fcc27a0dfe0f65e89bc760b8caad18e9dde3e9e97cafa5a907d68b1eed5d91e6083601af98f3dae118c77e405b4cb32359d18a70de0f1b8b2e66e20e4f6dfb7fdcf7b10f60bbe249722978db85e382d0f7abc0f67aa9658be2a2286ff3b35e00ec4d7dc58f244ac96b6626a1d34b66beeb9bece1773dc490a1023d0572709f314a3d0027b8001a02f00448088eba0ddc68bc3af77c0ca99294cf1d1fa534c7790b6151d274c51232ae746500e59b2abd8105c8c35223551bcfd7c5d5e5d73e5d79ac31f0646128e79236008b69870cb9ef6a8a39181c54e5999647e7a267103cc911a65278ef31dc7b3f75975c4c55075ee8dff655cb8d8541e8e42c494d2b6b3c1dab5fd778b88ef6ce79771ef4207114ce877a0b32cf13f24deae0db0c1a938b47a37ddd0bad110804846da4a9544f3404c20e2f4a44117151d13415640377de91a150aa3df2b680e709bf874c51448b31a04318a50a332a4c3a843c02a51cb488af4ae46a093a3567aa4cf284102968e2af5f413271d7789028954aaa144a8740f6a15f491ab2e29967634c982894db71a258997065dc1405d35ac59c6f4cd740c5625eb31aa94e942d2204f4d6b9eaae9ce650daaa46cadb54da3a7b8c10ffad6a8c2699138708deb532b727a2974b053ba4e52a7f3a2d60153efba133c0d291ee425af099aa7cba20756d5eb1ed9d3a7ee01247c4d573e3da40f24edeb8ef8e92af583277f0dfaa75904103e0ad8ce12a8931a086011ec4a05b55e0661d3c1ae0ba16628217452d8b516ea673184530d1b2b87faa187c004b1dd15511324112c35b14551d49b2a42228bbd7551af30824f199b92463d6b231cc4b12775d4bc3c82a38fcd1648cd5b21f165d59e5e5690849b67db457c8400bb10ad6ceff27a85b7e085141c004d6ead4ce443167a3f7ce8d09b54d74f8a91f83e1f31af4bde7957e5b249fe87afd72ad34d4865b105b32ca3d7a19cc0d3b729b648727b3e92fd6d1886fb9ef5ffa7ea8cfa032333b314662e737d12c8a634889508beafd618f04155b2eba69cb23a6742f0a808a007b6e5c120808f3ca8ab212eb04460405827696caaa6c296bd20c5ecb3ff8329ab66e01b5a9ee122f7fa032054306e75fdf7464c3901c9cac292c1735a07586e43145b46b168deb8b808a3c7341a18338d0cb0a9148fc71759b7cb177c7e536963788f40c652d9f3ae14a7f5f3b5b83b80072ea513007ab089cb6f8eb4928fe11d972235fa3369e2b433d9710fb4240d9b381ac30126195624d60d6b81bae98ee5ba749b06ebd2410e72f4222fc6622ca2f6622e9ebb4f153221ac0df55a05a25b6cdc8ea6dce20aabf6dc5110d15f4f3943d7d1634c2043e3d9bc88a2cac1d2e3e182e01a9645094a413527982ad8afe9567fe9a907b93fa235c057c245fd0fbc810d321e99f23f7dfcc77ffc61ca6efc54a89897f89e632a2be9dc1425fa33d45ab7a623112ad64fa3d2c203e9752da4468f0d5291c2dca2f093006140fe50825653366bede4ac98e19dfdfa52b6771a19f9b21aab92be0e7a0b453b650998bc0c21e02a33ead0dc67c4e8a3281106b338fed95919e6e6036bdde8b99b0fc3ddc8c05ff969bce10138fe3afaef3242e40bffcc742fd4e2b0739997fe0e517dbf5be341feedfd0538f0d8ecbc8be2023f6aa2fa6811f6b7d3bb2e8e30fe97bd57be12c27efbc5f95517025c8ef3c903efebcf9fe4f1eddeec65803fcfcab63c9364a15f3b0d6260d0579c113c217b5fb3a10e276503cb1a961733cb23fccf480ac10137e2803775ecbd7ce3b798a4cebc853d4d09929d6da34883676abcdd7cf1b165834d8c2bdaa19d6873a68555dbf60acd748c82803b3a789222406b3128abfc1d68b10d1cf6c93a3c336366c16ebec02f2bf404f798994e7e171c3fe78659162ce43bd7991be797bdcca4fc8b616733e2710e32feb4631577f39cda6e053477370da64bab9c39d21d0f38c50335818dbb7a42935acf871cf0591cb4fb5ac5ac334957fc7a1cb222c068b8679e3be2c21e6df16f9ec6d7aa13ea98f1d63ca79efb3944976ebfb6d7ee297a4b6df2f671844d5ae7b5b79ac1be297f92a151e3ebd7b1793e1ac34cfcd15f0d59f541a11badc0c03046fa86b8fb693ff3b86d677055f8485ae7646f69f03a0d9cf74dc0410ceb601777ac96c9db25c29e350b94e28514d6dbee14b9a26b27dad8c6adee8cc6e1d5ef502635da1d0c7ae35a9b83cb474db2e09ad6026c81b191b1ae0f99ad4fe3bf32d8bf379cdd166fa769209bdd372cc96ceaa85fd4b66eb2f7463fe91b0239e53c3069a4957375028b32d16b74e935427347e5860dd6faaffc5ba791f622368c6eb8f0c4f8724a3e307ea4c7fe99e31a7ffbae9846d1399709e18341b64d7b00b7c85eed63ede5adcfe9676a5c687d64ac3c28937c6360fb152d892d3f8b59f7e86b80f098165b0c18ac07eb1612b23388db00031ca61660fa1dc418f74fc990ad7454ec72fc43efc9be028ded78d2b6dc780f750d51f6979b7280a72cdfb31eece4da8a4c46ce2397d6c58626dd6da8bb3b78deb2365d1c2b6fed4a0f217955a37bcd9cf68b8282baf9dac1321031d94f0f153be701dd562d669c1d6d7667e9c961ee88bc3d4db4b27bf6b79fe6cce9c85fb7c69b838f0c37214df326a3b97c3b9806f89fe063e26c46748e69b3e9e84c3381f6db7c02536dcfe9652def966cb2bf8030b458e0e99ccf16c603069c94b1971d077dc6fd62802dab155f99a31dfdc64f97bcd9d9668058da86f69af5b4397e8f65dfd9de6f56b59c4fdbdf95ffaa66cc7aafd3df85b7912ff7f47ed86f5f4ffad06b8ecd64dde2c406ae7536c1d6781b0ed2900b9fc4813a37f27d6bb2f02b3fe39585dc464b74f2580b6cdf4dc75878b2603a9d3ebc768e7a7c13e6e4d8387cb501f334a171dbe19961d78b9145ce54c7b44784d8d36495a1e85501c3a8d5c175eded5be63fd42867b1990cacc1cfd87a47076ac37fce598259f67b6bbef32fe20521fe3c6f998179072f7ccb154d35e23162ebda788ddbb73d61a1b68f084c5a10ba37b27ae5c97266e752ae533f98744de4c5b6c999b567fe68e56c77ff29104f8a0307cbdc8a73047d882d27fc0001fb4e773462821136c71aaef395878ddb9ff9bb35a2957d5f3b62edadae70c9a664d51161588bc74f1892055dfbd61599323b5b465ab83b54c7b9b141d6eb6b30afb1ef75d4e397e9335e369a2e95162ef604b04e6548578f593f387086c06c34c1b96eb02589cb38c9b5fcb9ef410fdb344c7e1df69a35fad3f4ea5eaf6a32c0ad7782167decf81b4fc212a399c5e8ad68951d9b8e7a4c67f5d8b16b6cb50eb2e96e4fc7bb2073eda5a7dd0703ba80971abfa2bfb961728dcfb8964757861986fbc046f52497c4d593c6aeb7d1800eac87c8cc82ebbe3c52bfdd87638e1e9eb333abf50eb0a58e33034386071b8781e6dc569814bb6ead1eeccb846df0d4f5b7e8c2310374a9c13ff66468ce6b527444b2fead30c0ecb3c15a19128e8b0607969edaa38fc7ccc79ea0b5d1bad3b43360abc9cd744f3f1387b9b9bdcd4fbcc1b3bd5b35a1491cae594b5d2bcdd85f16fed7ea08b88edf94ad9bfba4790c915964d3a6bbb3693576844c091fd0d9f5b05c64038d3daf680a8634fe14cd1e0f5fb788a36e65c073dbf6719d7c6999cdb67e9dcd6fa42b3ec0b40fcc2c7bc6dcf635aaadba78f156f78cc3a0d634afdee75bc233df395fa02e845e9a6e25e747f4bc5fef728f6589c7ae84f6e0d53d5e3b37199f33dad32a3b6a1cec1d9c68b7b5a339fd08de6710073db1f6e6b0326cedb0fb15da867bdef5022ebdd58c5873eb5d6992f9defffeb19459dfc9361f4bc735feed1855ec3663ac7393ac7c2bf39fc1c6d3f14356cb3d66ead8dd9041133a3352eb0ce25ca366c06fb486336393eb66e9b3345b9795f18209bb3ed486e5996d5bb3d1d51737a8efadd277b85a5ed39cc15836039be658767deb9140db7678fa3496ef99963d99937b598d05ad353bd3b5fc73cdb2da9c6b0adbaa7635c6096cbcceb7f5af763678cb697cf3ddaa2d1d36883cec03b88766da9ead9d56fbc63c56c814cebb4337f6124b6ceed47b074d1be1314e49af609c13bce77d994c33f76db367847da666f428e5ea2472c56910e283ea281ff467dc8e70dad57bb4627ed59a0570dc836a8b690ff0cc674be8f1d71ec38f6fb8a08b00db8f384956e6f8c25f7f10a71ac1fc3a2a3465d9500ecf706b2a3387d9719fd9c7cfcc871199155adcf89d0e6e77db1266e484efb7c26866d4e6739fadf9cbf1ff2aebb3f9c77bc2420e5eccbadc0fe6bc516dc416d3f5703a28faeecf6b5b2ee8dbae8a6d3e013a8859c4bd1916ddc34c2fb8494347bc09eb6d64cdd8aedb6851971a786397ce5157ffd4036ad0cc7a714d8e99665cfe36f0d3acb7b7e6fb7574463395e66a4cbcb770ebd675dc0598e7dbebf81d4db2dad59d8f9e58874fcd1ecdf80704d69d53df60b3d5f6ed30da7a8d5e431b45ddbd67b33e71ce12c184cb8138f79e7794eb3d5856e9b3dfc92866f3e91bfde40d78d837c7da4e180c9dc966564fbcb17918a029cbcf7b98d92abaf940dd8cff27a7257ca1316f93317705eb0db21a129b39d24ef7db92bfeccd5db3512b27cdf691804de233613618d028414398f135cb109c26a39a653ee8bcfabc919d61ab9e5bddcc92de39eeb8700316d39c5958de786615264d5423a0d3ddd6dd379f93a7d3e061c4738f7238d9b7cbc199f257cfe7dd77cdfa8d6bc6cdaded3a78dd626a6e956d271fc8b6e3637aef1eb5cd38d536130707010dda90ad1dd75aae5a5513dd61e2435868572efa67c93a7bdbd7e7da286837cc2906c91cf0149a564f8dea740cdca4afaac5a6896fb4469894c76590711aac9ea83ad5a456d9db3f0466de7bad6aa2b7ae980ca80bc74674ca36cfd60dfbe541be84b1d5c9e91ab0fd36233659fb1bf7264c37fde831e5c277cdd5a375cc46db90c5ee9ced30105677db2bdddc7a4f18fac29b30ced1c8c0c86079ae58844369da19e124e565a08de488763e81718eecaad76bf59bbac538a56418703b6667c72acdcfda12039d71ba0f64d0c1acffe485c531bab61a13a7d96e61b203969f9c49f708c0d62b3cd7ba080c67d230e7c5cdb65fe336ce362c76c340c8be81736829db4737a1e5cbfad5f4290734a7e46f4ae3d64d587fa918ecce7ba0bec17cef2ae6e2b21a27f403750479dcca8f992f77da472e56306e0119d7d6332473a365b613535d8025b32b774d93ad76e4e1de0831654e6b3b119b568cf16bb5e300ffdcaaea6eee9e5b5b3bd31327cedbb61bbd61ec83b62a074e96d95dc711a38f30ae03ecfbfab11a7d3f4616f7d8ada7f9ee45d3eebb8bb9f405ed75fcba66d19019b0f95b6d046fb1ab3779cfedfa364c97d99170e9f18d3b4bbde33f74e85db15f3da61f4aeb4453ffba5bc5bca50e4d6da663299271d6196453da828186b79e5df2946e95f766e8ce7ed33b062d1fef716b30f38d0164e34d5b6a41d6f069fd0679a430505c68c3722c1d145bba6b16c6d473256cecaf8cd1dc5f7618bf702bba93c1e92b3404f58634d95cdef6b27bc47fa8d156e7fe275ac678fa3ce244c65caaffcc4bafd01db5fe113e6f87744d5776f4d6b34187426bde1998b85624185cf676b17a8875d94cf85a2a709981ba7468c5623a27a101ba663d836ce1627a86d3d541276fa3b62ce553619416a2e8cd023969285094b8091938b8e498824372b7621b2628dcf4f5d38466e0c2db2e188d0f292b9d8497ec87f0223768c25389f7f2b7d0462dbcef1d2a5d7f76b44b20595780690c6b7ed8e569c035f90f91f9ed052130dc243c751e4824075ae261847e4fb225a81cfe251abc964cfa028e937f27a948a57928f8b0d44e83ce89f718dd2ca2d307d14011db5ae2f55fb34d439d3aba8b49ec65950f44b5c188cea2d55b7e0622eda77952f5c0ecaa3d382678b5e7c5f81da3280db6ea55c8a0d386be4c0ce3c0b4d87b58fbe8112ebdf80298b379bdc5a50f96806e949aeee6f2602ab1185201f2c6daaf4a45e39b965d3b2e66c6531325a2278edfce9ae4028208c24cc43192a59e010cdc1cb164589dd0ca5d1e8c930def3974f5db1ebb7a1b4ec0b9b60bcf6b35044bc10a6e952cedecaeb2b42c60d023cd3fccae994d58bc3b543650cb7a7ea08182407cc95d905ee744ca6e1f443f81be7f3a1ab4e4483f05acffb99d4917bb8bb86bdaf885305d5514b1466c5739012e9dfd7e055ab0ac82860140f858a905ddd412ee022ac7c2fd75457c0d1dd421ebf396f854dbd45443346ae693fb44c90b75384290ec39a3429b45da031b2541ed3442732f95ad489ae1a39b7f577dc4716313e537c80a03adb1e03fcd255a00125cefa0cbb556175dadb8e98fe5e06253f45d5600dc47f799167f6674a20a13ab48403d1dce353de1400377dec02ecd4a258aee08ac8ed5788f20697170a3fd7e021542315ba3bf93188bd00cb5c2d9d2c5311d7cdabce3040e82177f7169b43c7918d8e24315399edee46235fb23879d673023756e0283b159a5df0c431c2187358e1a4332f9db45a3871b92dd90ed7d9c8447c52ec374ba83582b119dd4c5d59fef99975043985ef352ce07470ce14f532cd0904e7f422db2eb26ff079bb6ed3c95bb82e17f1405c35afdb78c0d315be187187e0524b607e947ee86f91f90f0d194c17276eda06e80cd05a2b71af0d6e2db83c00b445595057a0691c0aa0eeb84234602adeb0339aeba9070140cd6fbdcbef331e8a64350bce6f3e74e3a76071c3ff97f34a9bf89908e1498f4b5bd348f253c3667f02fc98b33976f54f405fc329c54e386c506889f8b64aa0b1116bf78550d087f011c0fb7e64d1cb32cf3bf3d7f91f4374241e9a413b5f003458da79863bafcb8bd20bfbc4f11044eded39ab2b19593ef749afab269963f0aca8c743d3d8c08220b1b7cd0b41e343745629f2f8c93c8d3e634ea62db0e4443d090055842707c470c03c856fe10d531e6ea040b0d9b967434e3146e2517471fb26a50726c56c69fca13b5de5e627d2e2fcd1b55d4e88e1a516813ad093484803852d93fefa6ca997e58ffe461507ea38e1c244dbfd3c95797e6882ac143bf85ccd94192de522ca47ea9473542cca01e59f31dcf11ca7b99910795e05358a10cf421e618e091f05fa73f9df81c900ac9701b239a2da4f126de0bd45e30f2f3e15e832b1874df600dad60cfa784e876eb776c72b469741c69dc9593b37477669df96b2306448698e8cf330945ccea5ab808bf69e0482d96687d9fa08079d5753346b86eb66bae007a18c83d34813cf7c4a650c220ee03f2dc5e305811c1c590c5f0375d5fa61f3be73e2cb5162dc3cbdcac9c49011f085a81e0069f9c4944f778bc42f28b0654a4bbddd7b5b452309475d45f7729450e8544febff5c97c2512c910d5448ba0fb11511a97874461192adc476c47d8c7c37d4384d1304e20bd78a2eb16d31dbc28448e6dfd021f1e64500a56674a1e2430dba93186e8d71932637d6ce4a28590454ace37675bedebb9632c75864fdad6456a25d68dd92ee079cbd196340611518c09b6aa1490881f8a31ed2ce27c1e5506c26c4243203f2e2a30e158ef4472bf124e6daca3f72d8f34110e19f5b4c517e42b8a35620798529509730068e63a81922baf11e00a81a18d696d60360cb52d1f5c672020e38ba6d5a1e9213fb42c45ed3980fefc4dffb56231e7f5626fb808597f7427822988857921b96b7dce6ff193ac2bcdc7fada93c46a4e74c2305f15f39fe2f894a42ee692b91d19ae6f8bfb4ad8b1a900f809cb1c4a68b7cbe1d67a896a21e29e7325a12171620ff14a1b601ec80b5bb514b110b8510926ff86f1994b9d184ea61397a93ede1485f4f21bf35db8e7173b50c5756a1138008d5bf53ad6d7d8fd8034ad60733bbc1dd72d6e5260e9662998e74836059510545b832e90c758382b0918a81f2fe868108618eb5a8268e99b09899195a8999b089b774c886ea5dadda5259ca3146d52e2534bb111f03d71b1dec8d4b42fc53f50f51e376a2f34d2262c5298cb709a85eaf6ae7c61f8d8f41e710667f073c3f15da4f7078a8e8bd5a05698c949e41b8cfd1517f8f260971e1438f11d063e1c017dc27fcb2dc70a93c0dbd465f3875f0125b95e4f2d810b1d4458b1b5d236e71a643e4884c871fc1e667127b9431dadc6511b7e379638794b2d21d0ab26e22f936d30f9593c5bfa6d5fd18ec5de342653d939425d8532815e9a49c8ccfd707c3702d8d1e805184b3f98fb69d36b7a4fac995e066ad43975d7bd86f17c1cbb138bd8a2f9689eb35ffb6c18ab52c29e53dcac38fb1007bb4a97d520332038498c320c42630ecbb65538305891109fa3682fdc7b8f268d731bfeb1df8a050c20862635f06c1ea73e1792fe61541a09aea1957f63402a28dd708b2473011d0fc65b1f36b83c6106bb2fae21160133aa47f2b1f33a7716719d77fc9dcaf8a29f87d16df5913856b949376f8815691767eca3b63d8cd1e517d65cf9898fae284586790ce9580d372f36a02006000b17049b3c03e9459b7e23eb1e9c1e0e8527cb715884ddcfc92697bcdea9053158803429ca92f24c2490a5030ffb5aedc71efc1e35f4bb08fafb53c95035138d98cf3b8aa5d868a7731599f3acb7bda61b070976e918aacf04cbf4ae18c6a295ce1199b69d143fc8a116d3c2638b65956de991cf3e5da693f1597c68426560542c993860b825cb1ac0a52d3132af4f94f599c49901d48ed80123e038f7031fe26ec3ae24299407f6b591c2a0ba90f2e57b1b8ce1c209e067e82aa33a04c2f3b242c663b8794752fde042492d73084ccc2b51a044891eb7405a80ac0841d80ced1c3c0d5fb1b12cc78cefe3a7e52dbeb9044e433221db8f491dc801f04c8fbaa5a567577cb0803f4d43bba8b7b0087312dbd94295e10ee47316a334a640a6b5402074a9f917ed38c93b36113cc1aafea5bc8317d66495102f7d2e55108510a955778d35770ee8abd33601d5376cd2f6a170813fd101028f83ffa66910f5cfe0b499a6ba00cace688aff3dcaa340f629ef3b2d9de6029fa96a3b24e072d000cd52060979cfcae320c3b96a9267f989fe8620e20f1142b201bdb4285f284ac88e1431a81ca4ae0d2e7a79f9f38e9ae239a6780f7d017ba8cd2b1182453a2b969c26e214a6aac2355ce0717a420f8c1f81695a7674741a246ad96aef4ac101d94642c88f2140b613dfd5190ad1e36c9cb7b66004f07c9765388aeb8ef425b7141c8bafa50ffb4131caf9227350fb675607397979148591171ba6b966ce0ff72b113bb4b767226d5fcd6837b5f6e7a00c8540f13feb19cfb65859a4c1ab531459420eadee76a69b0da2f15f462e16a6f2dbdac932ca4b2b090589218b7a23f059e0d2124946a5a88b541933d848f2692ca6c648c2ddc9b429e855ae7f27404c8b9c26b060cc0cd06e18b8443403d3c0d9ab8ce604b79845fda0561083f42aebf1a247269e86e4a21771f984da0fa1b22e817496ee2d45049f4a754e01f15e246cc17fe46c7fcfa60b5981ae04b495cf98e405681ae4b420e450664d9888be289942cd030882d2b203ecbe1b5b279ce0635bb01252fe5d7292483188255b791c4546791367bf48fe2cec05de93cba0224c103094a88531083d3ebb72ef95b4631ee60b5a6bd14fd33eeffdffffbfffffffb7bf670cbe06d353d82c2d65a2100d8884260502b7b4b4b4b44429aa8406ec1ab5d6daffc11ef88f4d884c017b206120b023ef717ccf0cef0cf81e355e54152fcd12ef4a83f715c4bb02e27d2db2469380f76450cae2dd41e199d238bb29e363ac351c291f53df1d023c9324efce21df64c13b23df83c67b5ae33932ce6cd078a5997777f82755bcaba2f795c85a16c7ff6c7196d3e46387bca820bee9f5a65c785b51ac3581fdd217ef8c0ddf3df2a600795dd95acd17efa97186a3c4c79078537ebcae5f15015e57eb3d39f9a041d6563b4979ae849705c20b4be23d05e0831a79797ade242aef2ae98595670b82f7197a59322f6c87d70601af84c3d90d131fa351b2e2d5f1c033a52fab8817e63a9b71f29d1eaf6a8cd7f581332739af182c5e96d29b34010d196fea88b7a588920cef8e06be0985b39a1ede93e2e501e14dc2f2a61c795d169ce5ecf14bac351c413e36c6d9cecd3315f149a7885f02c17bf2e2b929d69af4fcd20daf2a7d5d80bcaa365e975206839cad08ff247c79427893b2bca811bc2df00c47ccc7f8785594bcaf11d676c878a611ac692f3c08486d907196837eac03af4d055e09e9dd30dec7837795801776c1bb52df9720673716f8d8f7a638785be98bd2be498737c5c5dbaac0ab72bd2f105e95d2fb92e15551795d6abc3a077826f0dd79e199ea78b512fe279297b5f3c2d677d8f7d0f1ee34e09b3a78d7e37baef818bda819be098e77a6e63b37de9df099c038cb09fa58eb2c2be37fac586b52f34b32acd95479cf032fcb8c173682b31aa5f716b0a673faa5365e56246f98a1351b18deebc0bbfae07d1df1aa54af0bcb9b42e26d29608de78b570ccfcb13803789871755f34d3e9ce5f07c4c917725c5fb92e44d05f2bab4772701dfd4f3a2d06f32c0bb63c937a9f03ad9de3121bcac306f982cef2a87f775c7ab4ae17579f1a250f82630d632a5ffa9e33d41f19c11af8c06be7be24c868c5d36de875cb349e1bd0dbc3c54dea42c94bcbc2cef8549f1ca40f15df9ee60f14c63dee1f81e195e161a2fac04673b6c3c93d2bb53816f02df15eb7d65795141dff4c4ab4df03f819c31f1f284e01523e5dd117a2604bca790cd74df21e03dd57c901daf06e57f2a795316785d21bc291bde1617af6a8dd74582339a46dea3e1d5e6f8a02a673c42bc6234a5a1176581b765c16b73c5cbc1c3abf1fccf1fef7d7c0f0f6737697cccc9bb43826f9a61ada682f78856e95cf14b257857dafbc2e1dde1e1990ef06a707c900cef4ae985d1f0da60e09588789da0af52085e9939bef3e1530d94f708f0ae9c785f15784f6a3c37c68b8ac0dbe2795117785b19acf118e015e373b619fc9309ce789c78c5dcbc27309eabe25d81795f0e7857e7fb02e4eca6c92badf1eed0f04c7bbcab07bcb00fde1d107c130c6b4dfaf825295e5607de249bf7b4c37333bc37fb4a60bcab2cefab904f351f782f91b39b4c3e76c2ab5df9a05dd359fd921c2f0bf5c2b0bc2cf08551f16a7a7cd095b38ce67d8ede94d2eb8ae14d29f2ba566738657ccc8a3775c8ebda596b32c92f8df1f2acf02639f1a2de7869cc7879cc37498eb31c2c7e295b6b42c22f99af86c90789f1b216f086a9f2b29678611458e341e415f3b396ddf13f5dbc3b609e0992339ea557cc0b67371af818f96a1ef8202f5e9b27be8d02bcacef854df1ae2af0c2be37d5c7db6a7a591478c3a89f4e957cb77aefe27b465893f1bef3e03dd97cd01d6738187cccca9beaf2b6fa78592bbcb031ded5eb8589b02663f49d09efa9cb73eabb93816f3ae1e5c9e04d9a4129cc7b22c073482fcb026f182c6b3738bc12085e9ea137298f578b7d901aefce11cf14e6d58e3ea88297077d93e8785548bc2ed7cb93be4975bc33357ce780b59ad47b5aded7d75cef4ef927437437bca805bc2d9ab526ad5ff2624d07e897f6f8a4b1f020e4bc32517c17e5d549e4994a78b3ed7fd2b5264f7e0987b31c998f1de05579f1ba2e59c301e463628451e145f1f04d7cacd984f05e246b59eb7fd07827e37b54585bf50ff2ae84de571f2f2b8f370c09ef36bd0fd1a7cce87f9078537cbc2da6d7468f57e2e0e599e04d22e13db1f15c01de5396e77a781fe47bd2b326ffd2082fab831776c4ab81ff138117c580b775f36a667c5094331ca08fedb03693c77b386b3c0c78c574b0961de07fc47851aeb7a57a5562de5794772688efc69ce1fc7cec86331941be2b7a5731bcaf35de9de09f9cd63200bc0f116b363bbc37e6dd41e49b2e7851167cd3d19b2adfd6122f2b91374c0c2f4f0a6fd212ef2a88f7e5c88bd2c0db0ac0cbf2e28579604de686efbed7c905ef1800bca80abec9e85585f1ba925ed413df74c9d91f5816fac202795322785d2ebc2926de16035e4d920f82e2d5863e68e7ddb1e2993ef0b20e79c3bc70a6538267327a678af84e042f8b016f181b5e169517c6c6bb67bccff7f2ccbc493f6f6a82b765e55535f1ba26f0aa48785d589cd50cf19e17af0acbeb12e4d5c6ff81e4d580fee791f754f45cfaa270f8a63b3e6543fc8f126735aff7027953476f0b9097757a613a9cedfc4c41bc3b8c7cd3064fa4bca7093e889097258137ccf9f2647993f47835047cd0d0cb1ae38565f2a6c0785b92bcaa9cd7659ee594f14b362f8f086fd20faf6a01ef2b837727886792c0bb82f2bec07807e47b7c784f493cf7e55525f2be2c78f7e87db257d5e4759d6bd906fee78d57fbc0077df1f2f0f026b5f1a6c4785b93bc1a1a1fc4c2bbc2e27d65e0dd29e2992cf0a68ade161f6b4dc2fc9218ef6acc0b9bf26a1bfc4f22af1eef0d97578abd4e7a7895c4ac654cff63c73bc3e53b0dbc2825be8992f744c6735cbc2bf37dfda1a4c459ce101f13c1bbb3e5991c795389bc2ed59a0c06dff9bca94c5ed70aef4c0fdf49e0654df286d9f29e2ef8a0435e950caf8b8d339c1f3e86f46a577c50785683c47b61bca83e5e1a3ade1497b7a5c75a16e67fd85893b1e0bb27af4d22afc4c3cb9ae185bdf1ae8a785f107877a63c931c8bc2ab72c0fb0ac0d9cd043ed62949f2eaecf14c13bc282edfb48017d5c54b43c59baae36d89e0dde1c037adb06643f4de08de1425afcb7bb50efe67016733df77867c3a59f21debac6601efeda134c77b04bea78eb36ccaff1cadcd98f1deccbba3f44d55ce70d6f858166fea8db725e68ca702af18a057bbc00745a1b4be2bc7fb0c60cd490cef98f2f53a415e99a4efaa389359c0775fde930ecfc9f0a2b47c53222feb891716e63d49f11c015e1b4a5ea988331c117cec8ff7d7f7cc719683f331435ed521ef0b0067380df8d81aefea901756c1ab95e083d0784f3b1fd4c73234e77be19b1af3ba54785364bc2d4ace70501f83e14d59f1b660ef6e7c0f0ccbcc5cf15ef66a6b7c900baf13a0770c082fcb8d37ccf6ae247961e3190e171f8b62991a3fdeabe355a5f1ba44b066d3c27b1c5032df9d38be297bb3a0ff415f561a2f4c698dc690f762785928bcb02fded4015e97cd7b1a7a8e87b58c8effe162ad0907bf74e51d03df13888ef7aa22795fdf5a93207ec9cbab1de083d657cbe483c67852c2bb03c43341e04521e0a501e45d59e085816fc6fd8ffae994f41dcf9a8c0fdf896f0a92d7f5c1198e071fcbe1d5e2f8a0195e94015e1a3dde1d4abec984777df8275abccbf43e5c5e141d2fcd1a673c48bc6268943610867c57e0fb92e265ad5ed80f67370bf81808673981fcd204af4ae875edf1ae76785f7a9cd980ef4d72f698cfa09077d57ff2c5cbba7961e6998d1caf34c1cb4ae2854de05581e07db5703607329ea6570c0cefa986e75a38abf1e1bd29de191dbe7bbd2927de56035e994cbedbe25d4df2c2467887c0f7c0e135f19e521ff4c71acf14af98d47bfaf29c964f337cbc977386e3818fedb1a6a3c12fddf1fec4a362effafc939ff7d4f34188bca909bc2e0fde54ea6d51795171bc3468bc28f09baa786fba57a2e4654d796167bca72d9e83e22c07fc18ec6591f1c240f0ae2a785f452f2b002fac885785c6eb0ac1cb23c92ba67b751c79a6155ed6062f6c881725e66d01bd280abe098877047ccfd099cc03be1be25575795d09785988bc6160785525bcae2c5e151fefebe63d4d796e006b3557bc77c68b6af24deacbf2f2c21c79590778c3b0b056b3c17b40bc2c0c5e989797c5c20b3363ad89ce2fd1f0aa30f0bec837c5c8eb02c0ab25f2415c5e55d3fbb2f2a61a795d14bc9a94ffb1e4bdf95e29036faa88b79580676824f09e0dad2e2f6b026f18f445edf1d2c8a1b4f4aac4785d1e50dae2ecddb5e37da2bcabd5fb1a3ac3e9f2b1205e27e13ba63bd301e499b67755c80b9be055f9bcae2f6f6a85b7a5c4abc2e47db1f0ae4c785f5abc3c1fbc49e9d96ee07dd03715c2dbfae1acc9e9974a5072e34dedf1b696d63602efb3c3d9cecc330db19673f4b14cde9d2c9e0993b31ca18f5df2ae3cf0c2a2bc2c0fbc4938afd6fa20225e9596d755c89baae26dbd5e1522ef8b82f745bea78c77e7789f13ded396e7ccb52613f825305e1e15de2426de1d2ccfa4801795c43761e05535f2be7cd6de59d62fec863795c2db22c0ebc4e71dd3f3a686de561d3e1abca80fbe29895765f4ba147957147861deab52e47d5df0b29cbc302eef0e20df943abba9e363179ce5ec7c0c91b31a28de2bc09a8dfa1e08d66e6a78a54cde95232f2c00ef6a022f8c7bb5a20f72f2a2f47869e238ab49e4bd3cde9311cf19bdac235e18ec4d51f0b66e7887e47bf0503ae35525bd2f28af13f355cae49d11e23b4cce70a8f8581367d909ff43f4ca3cf1dd0aaf867ed0cd9bd2c0eb0a5f14182fcd15ef567c8f77f6f82b66adc906bf94c3ab2df24145af4e26cff4c3cb8ae2855de0d548f81f0bbca89b6f3ae3785535bcae38de14d3ebaaf2aa06795faa352728bc63507851347c531c6b3442ef816f2a01af2bf5b21879c35859a3217a8f5ce321e415a3c19ab6ff73c7bb22c10b93e15da17f92c5abea5ed713afa6c1ff24e0ddb1c037856b38ddc77a78554fbc2e0abca70c3e28913735c7db02c11a4decbdf40c47f5312a2f6b5f180eef8e23dfc4c1abcae37dd1bc2847de16cebb238067aa626d0b791f2bafcc06be83e25de1f0beec78551c785f299cd548f15e19efeae87d2de0dd397a26d78ba2c0dba2808fd72693579ae23dfdf09cd0ab92e47d856f0ae86d6d7951e637017286a3c5c7a07865a4f84ecabb3ffc13325e94976f6abd27349e0be35551e07d71af0e22cf34c2da0c19efc9bc282d5e9a285e96005e98126b323edf69f0663cef03e675b27ac7a0ce6a44f0de226b1923ff53c6d90cd0770778530c785d159cd558f2de212f8b036f52cdcb33e54da2e245a9df34c8cb82e285497296f3faa500acd5a4f19e1bef89fb6d7c7938f08a31e15d31f1be2039cb71e4977cde19f5bb30afca00ef0bf5a6fe785dbb3458bc2b04bc30009cd58479ef006b2fedaacbfb52641a199def7ade9d149e498d3725c4db2ae46521f1c224f0ce78f9ee0367333fdf05f2ee407926365ed606de2499b39dd53325f16a867c5096f7c4e5b9f33d5df11c136f0a84b735f4cee87c47c78b6a7a5b25bc3c37bc49629cd9f8f14a31bc2c3bde30e3bb28fc13a1778b781f9a17d57a5ba93725f4b6ca7857e37ba6bcab35de97d2cb2ae38589608de685f7b85785c2ebeae24da96f2b8e302bbc2b2eef0b9117e5c54b53c58b3abf29907745e57da971b6e3c633c5ce6c64de6bc03b63f3dd1b9ed0abb2c0fbf25e14ec6db1d66ab2f7d6573be4837e785398bc2e286f8a86b7858512162fea8e97c68d9705c30b5be355f5f0baf47835107c10192f6a8a972689b31c273eb6f4aacad725c6cbeae30d73c29bd2e36dc5d6d6f53e3abc2b05bcb027efaa8e1726f3fec8f7b8717653c9c7c697e78737698f77a9fc1302bcaa1f5ed7015e1e2b6fd21767384f3e56c3bb62e17d15e0d582f8200cde5517ef8b03efce10cf4481b50c90ff01e35d89f0be947859ad370c0f2febcb0b6bc0bb13e599de38cb513f967436337e87c8ab92e37dfdea14f24ce1bba2e07d11adddf4f04a2238bb49fa5809efca8df7c5f43ec5f76c67377f7ccce78c8683f73af071b2d664875fdaf26a4d1fd4c6bb93c837617076d3c8c704f0b2707861769c399979c5fcf09ebc3cf7c3cb427a618cbc3a753c53ea5d1de0853959e351e41513801725c64b93c5cb7ae30dc3bd2a2d5e5706ce683279cf7c773cf04d52de9500de17126738487c8c00ef0e06bea984d7268f570ac09a4e1fcf44c1194e063e76c7cbc3bd49456f6a01af6b82359a0ddee3e05d1dfe09156b4d52bf54e55d55f2c24c7859402fac8957c5bd2e27decc82f729e26555e00db3be3c1978c58cefaae67df1b0d644865f1a7a593f2f8c88331943be337a53dedb02622d1bc1ffd0719613e69740787512f04c249ccd54f01d1f6734b0f76a78378df709df1312cf79511ae25d49e0856d9f64b2ef2a78b7827f42f3ae94785f127867b2efd038b3e9e29576cdc994770c94570701cf24be1bc33f41e2b591e49584389309e43ba2359cf1633ebc3b10f82601bc3b23f826195aead9cdce2bb1f1a62af0ba047086c3fa580c6b3c6dbc622a78350c7cd0141a84f09e54784e5c93f9f96e837779f82756bc3343df4df2ae78785f7bbc36625e098a97e7ca9b94c65a93167e697d4f453c07c4d94d201fd3e0e5e17993c49795c20b03e36c47f03e3fbcac06bc617078552cbcae02284de06511bdb004bc3a7c3c1300de94096f8b883050d69a84f04b3bbc3b463c53242f0ae79bd017f5c54b63c59a8e0ebf748837c48bdae2a591e27d8fefd161adc922bfa4c5abda5e57136b3643ef85e05549f1ba2279751879a614de9d259e899277c687ef20796708f05d09de5318cf59b176094eaf34c68beae19bfa58db4adee77cb50ffea79167684e780fe8bda97925335e2724bc63c477077da604bca999b705e5d5a6f8a0ee4d25f1b616f06a5f3ea8e7656d2fec8965645edf15f1aa30785d4767374c1f5be12c6704bf149ed5a4ef55f1b2c4bc494d5e14226f6b5f9b3a5ec9e7d5943e288db31c9f8f31604dc7845f02e42ca78e5f4a75486739503e668197b5c50be3c07b2ae3392f5e54ea9bb29ce588f14b346b3a54fc12093e9d4af01d00ce662ef8ae8f97d5e58529f2aeaa785f18f854c3c67b6f9ce56cf031475e5498b7d5f38ec2b2e478c37cafb6c707e9f0a6a0bcad24de151d2fec747663c12bddf19ec6f7b0b0a683e59714513a7a535ebcad485e4d021f84c4594e097e495cd368fe278f3785e56dcdf1aa6a5e170fef4ef94c6ebc1a241fa4c4990df91e25af6af5ba86de191ebe8bc0bb33c93791f05ec9f708f26a23f820335ed504de57d0ab9a795d3abc2a3cded7cc9a0d10ef91e0d542f0410538c339e263479ce15cf2b13c5e1604de303bbc63f13de0bb63bc8f082f4f0f6f921b6737887cac83571be3835478b5087c1001ce7018f03135ce6e7a5e098fb3dd5131f1ba24f06e2e6a00dfe4c5bbd3c43365e0ccc68d57e2795508785f4ede951aefab0467332e7cd7c8aba51f84f3a28cbea901efea82f705c48b52e19bc478771af9a69f3735c4db32e45591f1bac4e854606d4685ef14f0aefe78613befa989e7867851a86f1a7a57ae1716c2bb0ac0fb1ae2453df926215e9407de56082f8b8d17c6f46a947c1015af6a7d5d0638cb89e16393bcaa155e571867383a1f7be1078a97358017d6c5d933ec6a8f178673561381f70cf09efe203794ae7c9a01e4bd26ef0ed03361b1e6a486778c94270178557fbcaf26af1310de311cbc2c235ed8eb4d05f1b60ef0ae9a785f1478b789f741ade9a0f04b81bc2ba0f7b584c7c6cbf381574c0a6feacadb02e34579f04d48bc2b1bde171d2fca8a978689b39aa0f7bebca7271fa4888e1e6739477c8c044f5850bac05a8d97f7b8785302785b59ce7034f8980def4e08cf644557e55dc5f1c2f60ce7918fb1f1a68cded61f2f8b8217d6e5cd2e781f235e1e296f52146b4ef61543e5dd2bff648ab31c0b3eb68077878867aac0190ed1c7bebcda0aff9381f737be2786359aa6f7d4339938be137a5542bc2e06bc3cab3709fc3463c97b007867b07c7781351ba1f730395b2dffe48df734f34174bc3b2c3c531cef8e03bec9835713ff6702679bc5fb3c59b391f2de25afaa02efabf3aebc2816bee98c359e335e310078793279c5949f4eafef765ed596d7858017e57dd3146b3a3ffcd207ded3d17343671918ff33c5da0c17ef692fcb861746c73b637e4781d78909ef18f23de57c901eef4a8f1776b38633808f9d675907af41d08b3ae29b2ef0a672ded694331c313e46c5598d13ef91b1a673c12fd5f1aa5eef4b002f4b8a1736c97b1bdff3c2590e15bff42b93c777ebab427a5d0a785596bcaf13ce763678a628ce1603ef639ed520f21e1e6739377c2c0367371cf81809ef8a8cf785c9d98d231f0be1652df28699e15d35e08571f0ee687926069ce130f1b124de5512ef2b02efca8bf7d581331bf13d0cbcab0bbc307213f02e81efb1e34c87e999bebcc878750879a6ef5d85f1be3ef0fec5f798f0aef67dd93013c4bb7ae27d59e07542be63c017e580b7d5e43d59796e8a1206ce9ef29904d668c678af5cb3e179eff5caf4f1dd0f6b3b663c1309d69a68f92520de140b6fcb8997e7e64d12c0ab1ac0ebdae25545f1ba2cf0ee2c7d530d2febc90b0be24549f14d49afb6fd0f045e9ea03789cb7b02e2b9f5452df14d95bc1a08ffc38077a6c97773bc272ccfedf0a63cf0ba06f0ae65574fde979777a5c5fbdac09b8abd2e195e1d053cd3096b273cbed3ce7232f85823efa987e7aabc3a8a3c93092f4f076f92f96a0bf82020d6dee1a403c52f8de05dc9bcaf2baf4cd377659c0d805d8bef11d778ac78c5a8d66478f82e5cb3c179ef01ef82f13e3f6f6a04afeb85b31a0abc37c83b25df638077077ca62fde930fcfd5f0b200f0c28a94b6bc2a48ded7f6da68f1726479558fbc2f0e5e6df53f7dbc2a2baf0b8e77d77f028692166b3a02f8253dd63217fe07e94dedbcad17947e78752ef04c37bca8ec9baebc2b27ef4bcb59ce1abf94f3aa8e785d107879be37c9e8ddd1e299327935ef7f5e2f8ffa26d9f1ce68f9ae92b39c427ea9825713c10795f1cedcf0dd03de9318cf69f1aaa6785d615e99117cf7c559ce7e6c90b52613fc520d2f6b8f378c09afea8ad7858197078337298637d5c2db82e25d95f0beb2785191bcad0b5e9d0e3cd3798673808f91f1ee48f24de4194ed3c704e98878b78ef729cf7216f9250cce6e0cf918072f8a8a97468937e580d785c1bb52e48561f06a491fd4c51acd94f7b67765f4be14f0a634785b3ebc3bdf3379717643c8c77e5e9e0ebc6206f0ae26785f5bde95f24f8478c5bc29ed6da1f0a21ef0b676d69c4079c7a4f0ee94e09b6858db44dec786351df197fc787792be298a52182fca846feae2d5b47c90ea4501f14d8ebcaa9ed7657486e3e463329ce1e0f0312d2f0b016f9816de55d30bb3e1ace687f7b0785112785b4ed6323dfec78b1765f34de6bbeae38535599b81e13b069ce5e8f0b10dbca9d3db4ae175d2f38ea1e04501e09bbaac6586fccf18ef693fe88d4f3353bcb72f8b90378c9477c5c5fbbae45585f0ba9258cbe9f2314c5e55ea75adafb6c307696b32427c77c2333349ef51f0f2fc265df0ea6ce0997a78751cf04c2cbca72a9e53e24d75f1b62c709613fba5f1657df0c29058bbd1e1954270b6737a2630af6a8ff765f3b29ade240bce6ccc7825997727906f52bda902bcad4a5ed6f8c2ae7857e9fb32c05acd93f7baac35b9e2978c78575ade9721af0c20df7139d381c033b5b02673c47729bc2a365e57095e56ce0b43df54cddb2adfa5e09fccbc2b18de571a2fcf25af98125e96d10b53c0271a20de1be17d85cffa7859a917367486d3c3c788949038cb09c0c71af0a6e2785b98bc4fe07bee784f2b3c479ee524fd12f7a6d2b705c77b529fbbf29e34f820052809bd3b333c931e6b37585ea9046faa82b785c3cb03be49486b3c70bc622c78b39ef739e213cd12ef0de0dd8bef29e15d9dde570e6bab80f7b9614da684ef04f09e84788ecbab31f241466b3a35fc12025e12ac9d12f9eee6d50cf041e7d9cd1c1fb3e02c87848f4de00c67928fd5f1a26af8263aded514efeb02efcc96ef32b06683c17b127855595e17216fea87b775c7cbcae28559f2aec1ea64e09976785545af0b9157a604df85f1a260f826365e5413dfb4813715c5db7ac0194e1d1ff3e26561f226edbc291dde56182fea002f8d1fafea83d775c4ab43c733a15ed498b715f4a696785b8d9cd99cf09e06d676c2782611ac35e97ea9875775e575d9f1ae46785f57bc207853afd7f5f3a23ef0b644785724bc2f2cd6749afc121cef4e986f1a5f1510af2b917785e57d11f26aeb07357965c47c87c559cd09ef19b186d3c7c7c27877d6675ae4d56af8108c5823e49599e43b255e5409de96f8b24a78615b9c39b979c56c79517fbc3476bc364cafd4c58b52e3a51163ad0920bf34c5590ed32f912f4b8f374c094a57bcaa11bc2f18de15ec8501ad3519f34b677478bc9a93ff01e4454df04d45efa984e7b8359c143e869ee544e097387877f4f8261c25205e940edfa4c7cb53be494f7489bca7d50705f25e4356f380f7fe78b5323ea83cd309c1337539c3a9f9d894b31cf3631c7867ae7ce77a5581bcaf9dd7c9cf3be6c93ba37d97c6abcac0fb2ae14df5f0b6c878b50bfee7006e64f2a20c7969027935a10f9a39cbe13ee67af789f751bd0be59f20bdaad3ebcae15db5f1be96ded494b7f5c4590e141f637a5376bcad11bc7bfe132e5e0d00ff6380331c057c0c8d57abc00731712673c67757ce76b26712e25dd9f1c2665e1d033c13f7ca8cf92e8b577580f7c55ad3497f2991351917bedb5eed8c0f92727663c5c772d66ae4782f8ef784c37330bcacf5850df2b246786159ace1dcf1b12f5e1518afab032f4b91378c0cefc2f04f8e78531ebcadf50cc7868ffdf0ce1c7d97f4ea4cf24c38bcda201f949edd60e063e2abd25e170e2fcb016f981cd66828786f834fd911ffb3c48b0ac03719f1a22ef826a497d5c20b43e35d09f2c2566738371f6be1d526f04149bc2a155ed717ef05f81e282faa91b765f3cefcf01d25af66c407fdbc3c34bc495dbcac262f2cf59438cb41e4972e78b37d9f2e67376b7c6c82331b00bc073bcbf1e29764de15fabe0239cba4fc8fd19bdade16d1d90d191f5bbd33447c178277e78967dac0bbcaded70d2fab8d17d6b426137ef7c19a4c07dffdbcac08bc61cc7747be678d97c5c30bdbe345b1be69cbbb05789fed5d0df1be1e795510785f1ebc3c406fd2963725c3dbb2e2ddc1e19902797fc0f7bcf1ae28796125bca933ded606dec5e37d5878d7ca3f69e24dd5f0b6b6f8a4c3c42f890003315e94956f9ae36535f286a9e1cc26e8bd30afca8cd795c96ba3815722c0d9cc93effe7877be3c53045e1e17de242bce7290f858093e653bef13c48b82c0db42addd98f04a1c784f633cb7c59b6ae36d79e03ddd7c101e2f8bf5c2b4bc3b767c13cd5a93317e29899705e58589f189267c2f84b3cc84ffe1f2ae1cf0c23a50aa63191a2fef9190c75a13a25f327a75c23c530d2f4bf5c2b29ce128f2b1335e14266f6b7b5558bc2e4a7e69726676359a3ded69e6ec6c465be6949d9d3abd349bd1c89c69356bff0c0dcdcd2972bcab0ede57116b3a507ec900af8acbeb3264ad8920bf44c59b02c0dbb2e1558df0baae38cb39e16314785195bcad0d5e54966f42e44d95e0755179798078930cf0a22af0b62a7877a43c131c6f0a83b7b5c3190e161f7be213cd10ef95f0f26ce01533c26bd3c82ba167a7117e6b3abbb9e3633d6f4a85b785c41a8f1aaf180a5ed6d29b44c18b92f24d66acd578ef09f1b226786144efce0dcf04c819ce051fabf2cea4df457296d3faa59f339948be3be2eca68f8f3d795735bcaf39de5de37dc6b3817e1779572abcaf2fde1313cf817959e50b23e39d59e23ba6d70907ef180d5e2d8d0f9ab22d2eefa9fb8d3ca3e1c07b3cbc2af17551f1b20a79c34c79653ef0dd152faa002f0d17af3680ff91e45571f1ba36f0aee67861dabb23c333e5a174c3598d23eff5f1ee3cf24d1fbc2cd81bc6873305bca9df9609ef0e0ccf64c7bb33fc1325d676e63388e44d35bd2e2b6739357cac92b39c2f1f03c1994d07ef5560ed66ca2b7de04d6df1b628f0de14bd12d38b0ac1dbf25ed40ddf64c7bb0faf3df2b22079c3a46737633e3680331c047cac006b4d6cf8a51f3ecd20e0bd9d7765c80ba3e0b5f1c02b39f1a60a795d37afca8dd7153be3517ac5b8b0962df23f057853376f4bca9accf65d076b4d12f04b57bc6bc13fa9795546bcae079ce558f14bfbae72de97f9a9a692f70c795732bc2f363ed104f21e0c2fcff926cdf1f28c6f52106b362bbc67c9ab02c0ebe2f2aef27861362f8ae96d91f0a2c26fb2e24595f04d5cac3519c12fa5f1a660af2b006f8a7c5b41bc3b63bea985b395e47d845e56951716c7ab9ae37dedab12e17529f16a757c500def6acafb2ae33d691f14c7bb2efc9322de94eb756df0ee9ccf84c89bd9bc0fd28ba2f926a1b5268ffc52172feb821776f4a61e795d19bca8f29b0af0ae027961a957a5c7fbaa596b32f44b5dde1515ef6b92778702dff4ade1c0f0312cef898ae792785324785d30ace9c4f04b07784f323c57be33407cc7819765beb03fdea1f89e10d678a078c5a0ded50bef0b8d5753e2833e38c321e36355bc2c11bc49ab17c581b7f5c159ce1dbfa45acb78f89f21d6648abe2be15541795d603c19c09b32df561a6739361f43c05a132a7ea988b39d9c6732e25595e07dc5f029b3e27f9e785330bcad29d69ab8f14b4daced78f14c2178515cbc3453bc3a629e69fd9429f13f4cacd5b0dedbf26a0cf820a4339937beebe1999947de5bbdac215e18045e4d880fcae0e5a9e14d02e30cc7898f29a1b55ead900fc2f2f2ecf026b1f1a230f0b67cd67690f7a9728673c8c7ca785306785da75795c4eb7abd2cd71ba687f743bee787b52648bf84f4f2f8f026c17176fa7e1be3d5b87c10ebd588f8200dd64e817c37f39eb20f82e36591f0c2b4785391bc2e115e56172f2c694d3bfd0f1e67393d1f63e46c183e83e44dadde960cef89ca7350de1923be1bc19b6abd2e0dce6e04f9d8066f4a92d715f4eefcf04c8d9ce568f14bda8b92f9261edec7f89e14de55eb8509608da700af9809d668b2784f85f784c273e07b14df23c23b83f49d07de9d0e7c53f9ea4ce099a87ccabefc4f12afca8ff795f3ea50f24c5796a119e13d11ce72521f4bc09b92c0ebea40298db31910be1be4dd01dfd3c6d929fc8d8c57c603df59f1f280e01513e52c278e5fda795595bc2f129420f06a05f8a015ce6ec0f858eae591c02b4600cfcc44f29e9317e5c237a9f19e581f648057a3e07f045993b9f21df8ea60f24c59de999defea78513f7c5302de1d249ee902ef0ae97d31f2ee64f24d2f9cd548790f89b5265dfc1201ceb29bf7f1b2b60f789f2befa9830f72e4ece68357aac07b22e2b92eaf6ae775a5ef50341926be83f2de74f04a90bc3b987c930b6faae76de97086937e0c88f7c4c57351bc2ad6ebcaa294c4ab59f141e0ab45f2414e9ce5681f23e46529f1c2207957227861545e56015e5808de151e2face65d19f1be5aaf0e069e298797679257ccf7ae7c785f7e9ce5f4f14b3c6ba741bea37917877f12c58b12e4a5d9e3d51cf04141bca8ef9ba87877bc3c13ec5d0df2c258efa6ffc48bb52671fc921367381df8981eaf4a01ef0b83b39c093eb6c8ab4ac0fbaae04d6d795b79bc3217f82e8957dbc00761f1aa60785d69acd5dcf01e146f4a8eb795c98bc88b6ae19bd2586be2c42f0df14907895f0a81d2f92ec8f7a02fab8e378cb896cdf03f5fce72cef8a59b5735c3eb6ae33dd9f09c0b2f6acb3729f2f224bd624e7847e47b84785723786131bc3c1cbc4942673b1d3cd314ef56f13e152c43f380f7acbcac39de30e0bba3c233adf16ec36793bcfbf13d3dbcab485e58a864c799ce089e0988371580b785be2c2b2f4c8e77cd781fef55e1f0bae8787736f04d29bccbf13d349cddb43e16f4ee48f14c1c589b89df01f3ae0cff248917a5c8db3abd1a131f2480f78c97478337e986351b23de5b529ae165c9f0c2dc58a311e13d10d69ae4f04b5a9691192f9f6c2a782f029f74e0f8a5d8cb03803749857795c80beb59bb115fc912a5a6b5fdf23e326b4d2af8251b5ecd890f12e145217d13045e941f2f4d1d6b5906ef13c5598e003ef680f724c207b53ed170f11e94ce00ef0c98ef3279662693f72a586bf2c12fe9f0f2e4bc4922bc3c25bc4945bc3334dfb5f1a62a795ddf8be2e09b8e58ab99e03d2eef09cc7344af9301bc63467855e7ebf2e39511c1775ebc2c0e5e98112f4ae89bfc508ae4d52af89f415e9b03bc920d6bd923ff73c6ab01f2413ebc17f23d58ce6c7cde9bc09bfaf2b60839cb49e1631578573bef2b7d51317c931bef8d935792e36585f1c2c6bc07f23de6dacd101fd3ce788c5e319fad11ef63739683815f0ae165f5bc30a4b51a33de6be3fd84cff0785521785f2eac9d1af05d9397c5bd3028de95252f2c8577e702df34c2aba9f1412dbc5a161f34be2a0b5e5797f70600af44c7ab02e47de9bc3b7f7c13eacc868d5762bdab0dde9710ef4c97ef2e7979586f52f8a22e795b1ebca930af0be82c6702bfd4c1cba3f32605bdaa3bde97ccab8df03f15586b72fe12d1598e968f8979554f5e9797d78905ef9809ce7216f04b19acf1bc5e3120bcdbc23f21e25599f0bab4785157be298ff7e4fd36829207af8a01ef4b8397a5c20b0b634d0684ef02f0a288f8260a28bdb12643c47728ec02de54166f6b022f4fd19b04c8ab6ac0fbfa795320785d51ded4d2eba2e19dc9f21d06ce76847f32e53d7df11c15ef06f89ef34d35795b2dbc5be59f30f1aec4bc30165e96ce0b53cf6cb678a5d76a32794f9157b5afcb86339c047ccc8c970703af18f05d79795f8dbc3caa37e97b57ebfb1a644d67ca2f0df2a6c0bc2d04bc2811bcad6fada68ef7e478b3dfa7e8d52ef9202dce6ec27c0c5ca389e4bd1b5e194abe6342297d664602eff1bc2810bcadee5591e07d5179554e5e9796b32c7b1f20de1d139ec98cf7057ccf196b3c0e78c578709663815ffa606d8685ef1879531d785d25bcac335e1809ce70bcf8981467351c780f919765f3c27c784fa80ff2634de7855f22e4e5b1e14d0ae3dda5f7d9728673c6c7ae389b617d97c7d94cf8dd21efaa7c5f622815e095897d37c6bb73c533897951ab6fd2f2aaaabcae375e94246f0b83b59aa2f7b4f834c380f754efce14cfd401af87f7a4c47341bc3ca737c9672dfbe035d8de15045e18086ba73abecbd64e877c67b39671e07fe058b391e1bda457e5bd2e285e9e09bc624238c399e3635dacf1d8f18ae951faf2ae68ded70e9f684eef61f0bec5f78c2fca8c97c68b77e6efcc785760bc2f0f9ced7cf04c54acc99cf05d082f0acb3719f29e9a7cd01e6b3a1efc521e2f4ff62661f0b29a786116586b32805f3adf53081f04813755c6dbcac0594e968f7de0d5a2fccf06de93ea830079796cde2410ded424af8b7b514c7c9306d668f6bd9e57e5c8fbdae06555f226ed6bd3c72b7df0b2e078c3746b3520bc87f4a610795da8370be17f7c785356de96162f0a8e97e68cb39aa5f718795593bcaff13d95cf8db06643c62b9d5e14256f4b83f7943e97c35a139c5f9ae12c47898f29bdac1f5e1802de1514ef2b92359929be93e14581f04d4abc9aeb838a38bbc1e2634d5e271bbc633278553aaf0b7d5942bcb0475e1d063c13947767e89916f0ae38f0c2a0bc3a783c13eb5dfdf0be10b02693c4772abc360e78252cef4e1ddf34f3ae32f0c206f09e00f041077857b11756658de6c97b01789d50f08ee17955b1f735c3da8d111f3b9ddd50f1319c17e5c337fd7136b37d57c8a7d305be53bd3b8a7c5306aff6c307a5ded310cf15bd2c9e17b6e5e599e14de2626d678b6702c1bb19df539ec994f15d0eef8e1fdfb4f3aa94785db077f5c6fb6a7a5501785d43bc7bc7fb4879575bded721ef55321cd8c7de58a371f29e066b3538ef65595b2e3a7a3c9393573b3fc8e6ddc9f24c8cbca83c5e1a38de950eef2b8ff714b42c175e581aef0eec9b825e1e0dbc62c87775c5fbaae4ec66918f7d709639799f21ce7260f8d8055e1406dff4e54d9df0b68c7855eaeb0a64cde683f72c709643f3b142ce6cb8f724393bfc6d646d41f827df9a0d12ef31bd1a013e888377c7876732648d2604ef9def6a86f7f5c68bdae19bf6785994bc49bfacd89b0480775580f795c97bda7e0b5f14cf3771795955bcb04ade5db11e79c35c59dba07f22be4e4678c7842fca896fb2e4d554f81f4a5e9e9d37697b5538afcb877791781f9c351a30de5be1e541e14d5262199a9cf79eac6513f81f35d66a74defbe1ec06898fc9bcaa2e5e9725af86f44116ac6dd2fbacef8a012fece7d5bcf82013de5395e75858db2ddec7e7dd61fa261b5ed506af4b8877b3fc9331ce7232f9a5efe5a179933a7817e37b5078598ebc616e389329e43b205ed509af8b8b77d5c2fb3243a9cb9be2795b34bc9908ff63be2928ded6236fea01afebc96b93f44a4dbc2a175ed719afd6fd8febd57cf8209a97e5c40b8be4999934dea3795145dfd4c8bbfaf2be18f0b2c81786c5ab1ae27535e06c0683effc38c391e46374bc4e4e78c790b0b67dbccf0c6f8a87b735c6ab12c0eb42c20d12bc2ced855d39cba9f2314a5e9515af6b9257cbf241a8f794c5734ebcac2f5ed807deccfb1f2c6738217c4c87b59a2def65b1a673f34b6fbcaca11776807785c4fb82bd2c9f1726849204deede19f60f1ae81ef21e4dd199f498c7766e63b36de151bef2bf6da3ce095b29ccdc8f09d236f2a8cb77581b39a22defb62edb4c777a757278f67e239c301c0c766584cce6a2ef05e21efe97cee8635992cdf8db0d684e997d678b5d70719f1b2327993502f4b016f181a5e541b2f4d01d632f47f8a785799bc3017de1925be5b7a776c78a63fce72b07ccc03cbd01cf19e09673714f898f72e0dff6489b3457a1fed6515bdb044de95cdfbeae14dc1f1b6c69ced58f04c4dbc2c2a5e1825af8d14ef06266f0a8db765c99bbae16d75718633f4b1a35745c8fb5aade970f92531ef4eeb9b42784f341f54c7ab59f9108a7835473ec8cb3b23f3dd1a6b4daafc5296331c4a3e66c7a79a2bef45f1cae0f19dfab28e5ed8025e5414dfd481b38b781d0aaccd94df2de06569f1c22e39db413d13015e8d8a0ff25e14202fcd1d6fcac9dbaaf26a441fc4f3ca60e0bb25de8de37d4cf84453be17b47613c4c7b2b39ad67b80bca8325e9a2e5e993dbe1b7a5167bc345fbcd3f13d565ed60b2f4c8d351d1a7ea99077d5781f716dbbbccfbe2a0dbc2f13d6b691f7c1e15d9df0beb8786f3c78250a9cf190e015d3c2bb24dfe3c7a705e47d68785704785fafb59b1f5e49e9dde1f24c0d7877b867dae25d81f0be08f0eee4f04c067879f64dea79b5493e488a77878e6f92595b0bbc4f0fefc9e783167977a078264bde93ce07f1719671f13f517c9a19e43d9d57a68eefce7707926ff25e2d8f0fca614d6686efba77c6e8bb0ebc5a073e888b35adc9ffecf1a204f04d4bbc7bc9fba46739dec75eef26f13e39efe9f44172bc2c3ede3003786752dfdd719603c62fcd9ce55ce09704705673be47c53b23f4dd04ce96f25904de1924be537a59197893b257a79267d2e10c07e963600259cb3cf03f71bca817be698d17a5f4b6c63519f4bbf1cd30781f02bc2908bc2e9ff7e4c67366ac35f1c02f99f1a694785b8c9ce17cf9d810af6a03ef6b00afea8bd7c501250dbc255e9d469e4985339935bedbe15515e0758d795536bc2e39de9d45bee9c919ce0e1fe3a2d43adbd467c0a4d4c6bbeae17df1f1ae86ded7015e1d0d3c130faf4d162f87d08bdac0dbe24089866568f878ef856768b6f704a0c4e55dd1f0bee0787742f04d54d66a6ade1b7a59af378cd0bb50bccf4a6992f744c373529422f0ee5079a63bd67056f858faaa7c785d7cbca8d8db225f95cdebeae195e1e3bb2cef0ed13339e04d9d6f8b8d9745f3c27a78bfe1330c9c39d179c7bc3643c3770d381be2d52cf0414d9ce140e0636d9cd568f11e1aefca83f765c4bbd3c3331572e604f58ed977c9789fa0572bff4703ef585e7badd588f1de1aaf36e683c278578cbc300d5ed51baf6be9ac4688f7ba7851237c93166faa01afeb82b39b443ee6c1279999ef2c389369e4bb20de5519ef0b04af0e079ec95cbb19e1952e39c3c1e4637dd8f19e909ecbf2f208bd496fbc37f23d697c5a31ef83e565a9f1c262efea7b5f519cd990f05e25efea8cf725825727e999d0b31914be5be4ec0b7e4f78770ef04d13bca919ded615cfd0ccf15e0b6737627c4c759633e697bcf73480e7bcf766835782bd2a1d5ed71d2f8f00dea4a1772702dfb4ad6509f81f325ed4eb6dad9ea189e1bdeed542f89f06bc3b0af8269feee84581f9a609fcf1b2e278c378af8a86d7e5c65a93a05fe2e14d11f2ba6aded41a6fab03afca8ef7757a4f157c1002de95cffbfa7256d3f45e236b3465bc17e5d5faf8201e5e9e9a37e9837785c80bb360ed4c9c60f08ea9e0e579e14dbae29559fa8e8c57f3e3837a78554bbcae08bcab3b5e18cd8bfa9b70383b8dbf99f19e5c780e85b31cf2631278531b785de2dac98fef64de9d2ecff4805795c9fb9af2a688785b87bc2823be49927785c1fb3a7a53e1db427acfc0f70c72564382f714f0ca287d27c6bb633ed321af8d21af94c35956fe0f10af9310de311ebca918de56154a689cf140f28a114189e8078b57abe283be339c373eb6c58be2e3a599e35d45795f63bc33eb7792bc2c302fec012f2b7d610638cba1c02f79f0ce9cdf55e0d56e782033d6b23efee78b57d5c2eb32e37522be63bc57dbf241abb32c85ff297a5328bcad23ded5efab86356d8607e1cbab49f1419c520767376f7c8c82f784c10725e06569e04dd2de1dd73789f06a5b7cd008673c45bc6264de9425af6b7c7710f04d14ac9ddec87242f04be0cbbae185d5f19ea23c47c2bbea795f462f8f056f920bef6c7c8f0b2fca866faae3e541bd49dc7b127aceca5a93287e8988f7d4c27327bc29f06d1dbd4ec677cc777673818f85ef03f8ec8e1735c84bc3c7cbdaf2c20ef944f3c37be3cbb3c19b94c39a0c95efb8b505f33e33ef597c4ff8ae32785f482f6aca37a1f1ee30f14c95286df0a21af0b672de1d179e890e8f9037f581d785c2da0d94574a9a11e2dd897d93959787cb9be4c759ce1bbfa4f3aa26785d5bde53019eebe2e511f38a51612d87cbc7c6bca92c6fcb8ef704f441ae57857a5dea7b946511f1c25a4a547cd241fa254c5e96206f98f2550dbdae41de1da4677abdb7be278eb53de47d6a58e3c9e215b37a5733ef4b87b319007cb7c75906fe4f96339b7d8f01efcc10df81e05d85e085c1f0ee08f24dac17a5f34debabb3c8330de0cdbeff193acbf9e0638fbc5a131f14c2198f12af989a77c5bd2f275ed409dff4c5998c23df09b1d6c4915fe262ed068557eac0590e077e697b65f4f80ecbcb937a93ba1725c437c15e1508af8b00673418782f873755e56d65f1a2be7c93eb5da1f0beba7877ac3c531faf2dde1d067c53005e5406df14c4abc13ee808a538de55ea7dadef0c0edf41e04de9bc2d17de1d1d9e6990576701cf84c29916c383e0e55d2cff248c3795f4ba4c78b7e79fdcbcaa0b5e17106fea8bb715e6453df2b674de55e27d9aacf140e015f3c19bc2e16d797186b37ecc68191a34de93f26a407c10005e14d03739f1b23c786104f874b2c077a97735beaf2ade15085ed80b3e5a3e653ffccf11efaa9117b6c1cb03c39b84c5fb01be677d775678a6365eed8f0f127a516ebc3465bc27f23d647847bcaaa5f745c35a8dcc7b589699a1e4bd095e1e2c6f121eefce1cdf743abb79c0c7805e16132fac022f6ae79bb0bca804bcadec2ca7915f7a7226b3c8775ede531acf89718623c4c78858dbb39d099e69894f3a5d7e69cc9a0ef74b7cbc292f6f6b901725c23735e1edf0b2ac78611958cbc6fc8f1cafcc25df49f13af1e155c264adc91abfc4c4dacd0baf2466cd2688f794ded422af8bf55e81efd1e375b2c33b66869705c60b13f32ef49a03d66a7ade2b5a6ba2c52f1df1a6d4785b1c78750c79a6f1dd31c037adde130fcfd1f0ce7cf94ecc9ba2795b2b9ce140f1b1255e56d00b73e2acc690f7ee7879287893a4bca7279e2be255c1f1ba98ce64daf88e873735c8eba25986268af75078f7867f02c59b22c0db5ae465f5f0c2f87857e1fb9ae2dd59c03769f07ec7f7dcf0aabcbcae05bc1a0f1f745abb315f6904efe6f13e53d6b20afccf1a2f8b046f12cfabe2e07511b1766ae4bb9c3775c5db8ac0990deabdd65a36c9ffb4b1a613e59704795763bcaf31ef96ffe4cbcbdae185e921f3c49a9328ef18155e54212fcd1f6b1995fff1f2a622f0ba3858cbacfc4f10af8ae775fdf06a623e088c770a7c4f1e6f8a8ab7e57a792279c56c674e9cbc63562f2bcb0b2be45545e07d89f0a2c86fcae25d2de085f97c3a4de03bd43b17df43be2c0cbc61b8bcaa185ed71aef0e1edf64b3d604865fc2f2a6a4785b1038c389e3635cbc4e04f08ee9e0dd17fe8911af66c707d940b3c56ba3c82bf5f0b268786170bc5a063ec80a252fde25c0fbdcbc373eafe47a53e8db6ae3ac4689f7c478778a9ee99177477ca630de95015e98ea3dadcfe9f0ae7ede1798b39b087c8c3bbb897d0cca2b4382efc0785124785be18bcae19bf0783fe37ba2bca80dbe8988d74900de313e6f6a8ab7d57a53dfdb327a676ebe83634d86fc0e0425315e2d870fdaf3b2d60404bf84c6cbb3be49779ce1a4f1312cce6c68de73c0bb0af3c2c4339a31eff9f06a3fffa3c8194ecec75c589329bf0352da418e1d5ed607dea49c9767cb9bd4c7ebe483774c005e9e01bc4948ac3541e297847879727893d0785741ef8b894e91b31c327ea966cd46cb7b22786f3478a5d7190e093e06c85926fecf0fef7c7ccf0eaf0c07be9be245e1f1d2bcf1a6e8785b2150aae4d510f9202defceeb9b805e56f8c2aa38d3713d930bafce1dcfb43ac3717dcc8d331c1f3e567466a3c62bad5ed4216febd74905ef1827ef0ae77df9f0a632785b3cbc2b22ded7035e96256fd2ccbba3c4336140898c57479267b2e1d508f920f58ce78857ccccd98d161fd3517ae20ca7888f19f19e52782e7c55dfeb8ae245d9f1d2b4f19e36f8a005bc5a181f84c23234a9f77c5e273abc6364f8740ac177139ce518f24b16bc9a181f94c2ab72e27541f23a097ac708e0d5bc7cd0056b3a3dfc1222ef8e08cf74c5194e1b1fd3a29be12ce78f5f72f26ae1ff48e065fdf18649e10c07041ff3e313cd05effdbca810be89899f2bba04bc1a07ffa38057d3c0075df1f28c79c5407935127cd0192f8bcb0b43e455d1bcae1dce70a4f81813efb25075bc346cbc27189e53e1cc068e5772f26a3d7c90cc33323cdf61f06a461f44c1bb93c4334df2a69ebcad1e3e6544ff438077c5789f105ed603de303abc5ad0ff3ce00c67cbc7bc9cd978ef5d40c9e8dda17f22c68bbae2a569e2dda1e29992d6321ffe878817d581b705c27bc2e2b9265e4dfda09c5755c8fbe279d7fc275bbc2b395e58f6aebaf7f5c4ab02f3ba1c797789f7d1393b86e579f226d1b09609f23f617c3a317d47c1bbe83fe9624de7fba53eaed141e39794de95f8bea8785547af6b9157e68fefb69cc910f25d97b32633bf64c2cb83c92b66857731f827a99775c20bf3e25da9de1796b39c223e368277f5811726652dbbe17f8478797ede241dd66c90de2bc1d9cdeb63dbfb1fdf23f4aaca785d98bcaaf0754db1d644fda522cf8957e7906722df9d129ea98c77e78e6faa7979c2bc62b8d78603afb4c45a0d15ef99f1b240f026a5ce7228f898025ed50baf0b8db59cf26391bc4e78de31aa577bc00781795184bc347dbc2bf23d057859585e18216e8ce0dd71e29934f0eeecf04c84bcbbc23ff1f2ee44f24de2bb23e69ba6bca845de96ccebc48377cccfcb92e40df3c3590d13ef8df1a272be297db78cf7e1ce7462cf84b4d6e400bf54c5bb29fc932eef2988e7b6bca82adf24c7bb33816feaded3cf0731f2dab45e49cb9b42c0eb6af2aec8f765c5abc9f14134bc9acfff20f2aa3cf0be567877f8f8a626af8a7c5d56bc2933de96065e56082f6c8957f5e57531f2b272786177bcaa0e5e5711afd6f33f85784cace998bf9480576be3836038db85cf26f0a6b8b755f4aa72785d75bc3ce49b04e6dd69faa61b5e14956f6a632dcb799f2f2feae79b8a7879747893d4786da27837c6bcacee85457146a381f774785317bcad2baf0a7d5d7fbc2c43de302ebc2aa6f755e54c07f64c2fbc3bdb3369f1ae56785f61bca910bcae16ce72daf8a5262f4b8c1786c9bba37a2624decde19f48719693c62fe1bc3650bc1b5d5e9e18de242dd66a8cdedbe25dddbc2fa1f73cbe278737e5c6dbfac07be2e08318f0a6b2b775c27beae23929d69ad0f14b4fac2d24efc3c39b22e16d81596b62c42f05f1ae4af0c26638cb56ef23c45a13347e69895705be2e29ce6c6cde7be47d8eefa9f2f25cf026c1f0ae34f0c24e58fb83df44ded30dcfbdf09e9c788e88339b2f5e295bbb89e195c6bc27f3391b3ed12cf2de0caf2ae87531f1a2a07cd318af8c1ddfa1af4e24cf64e58ce7cb2b265bbb89f24a1e7807e37b06f0ca48f21d12ef3fbc36e65dddf0beea7835373e88cacb8ae185b5f1aa8a785d8fbc2920de961faf93ee1d03f46a28fccf24673601788f02efaacafb8ae35555f0ba8ade91f89d9c978784378988351cf0634267391ff8a5ee4d5de0756daf0cd37705785351de1613efce08cf44c67b7ae1b914dec37c4f1f6f4a84b7a5e5dda9f24c7cbcab28de57989785f3c2ceb39d9a6722e24dedf0b6c4586bb2c22fa5afeae675099ddd9cf1319e571be2837cd664ac7ce7bd3a2078262dafca83d765c4ab33c83379efca82f7d5e50c67fb180f2f6b8a178681f7a1e5f1de2420d66e80582d900f32cf9a64bf44c2ab638167a2e17592c13bc682339e215e31a7f7047ccf189f681c79afcaeb4483770c066f0a03afab5be341c02b6683b526e32ff9f06a517cd0f66a02f81f47d66cc27825ed75f2bd63b65715c4eb6ae4d5ba7cd004af4c06be73e25d85cff6789d9caf1208de99d3776abc1a121fd4c1ab75f14125acd190e03df40cc7e86342bc32747c670625b7aee42aa6e6469a2769aedb90c875517228415515c70f244ff503bd758b6984454522ca22949a5d3ace48452291b8acde8843097e64879e271aa229f8759c3a24725fe8450889cb2ade509a20488a27ea799d788e1ce90d899c16541d42854e1bca9443c1511dbb543cc1133dbd70851aca740dc98e33bfad0bd3f414cd21914bb308a95dc02081214a0108080c916925f4a85c112aad081165c1a29ea92a25ba9a9b187e694aaa1b386ee090c8a9285191d0962d264aaa28510c4451c2f1a84b914743c985e0b8ad26f78de6d8a5a33743a99ee996926ad771ab9786a1178232946b787edb08aedc67aa28ba995e1743b985aba87ae6b89adc06aa9ed9711c95121451d223c3113c3b72fc488e1c612853f23ccd55fd5031fd42d04c8b4678a13c49d22349731c456e04b9f14b8b5c28bfd524bd2e25bb101db9cefc386e6ba13cc50ff440543d37f14cd3ee0cc129e5867e2aaaa21f4a7edbb692e090c8791d0be59686263aa29b0a7a1d98a660c77152ca5505c593f4b66febba71dbc22191034388529aa7098a1d7a9e2249a626971aa565a9aa6af78da9697e1e888aa6372472aa1720b21079113acab2912b9469377aa6e89d1f9a762027a64322b7a50b972c42a96a640e1d8d9c08a5443d7224b933f4c0d154b76e2d42ea3a125428cd755bc32e55c5711bc771352a3acd0d5228c3b31b37b4fb5491e4ce8e0b878e442e44a1fc386e4d3ff34c410f0557721c123995a8680b2a44a47e5185b8141975e13a90ca15a1114e28cd6d55d52ffc4455445195533b14065086aa487a5d878e21e86d691a0e895c97a2a22d421ff77147485eb07c1d47e58a500b2694223aa6e2c67d2848a2de28a24322379a369c2a7c5c48e58a90584299a2deb66e6bb88a9bca9dde39247244ea96944815fa3a5045b36c39025520a11455cf03d7755dcf14edc0d41c1239242f5d88b2803f56a40c1d992c00018560670e1d01011da9273752b922d465843244bb313dc74e3dcd900cbd6f5d8b8490b26ce97244be4096e86672e17aa2e3b7a5e6a79e53c6f20c43f14b430e44c9d323d7cde3c29043d52fde14b1544d6ff3b8aeeb4252243772050222432a5784ceb01457350dc794044fd2ebb8300491b8ac1e5882e2b9a9e7477a1c776ee4c70d89dcd091972e4246456bc8bdf09526788ee329a6a1687ea7e77d432267640ea11b27e49524b971dcf9999b7a865fe86d432297129942424529d7a95f8cd423d548fcb122e527851f283f567eacbcf083aa360461d240e6d095e027821eba912918a6dbf6a943226784742444aa48598c6ea8020404348254ae08f1c0959bf779a1da8123988a1d496e58bc9cea916964547a20952b42346c65da6d62f891a94a8a26679ee090c8ad425d4ca32088bc785cd805959d07a6eab871aa1882dfb7894322a72265310a02e400a8e4b8ae03b70f35bd114d43551c1239a18e4332b91815ad211297d50b45283f8e1b3b3414d30ddd3ef53353a22c4110792922121ab9114228d5500c3755dcc271dcc20e4cbd1f2b527e3acf8b51d10a0484c46505fad93a2f3f5bba70c9f263e5851fa10eecc2258bc7c50b9187c465f54e0194a8baa627b76ddd778626789e432287c46515c24294450b9a85c8ec42aa4645e847e58ad00642d98d2a796e1c48926ab7aa9d382472422287aa5f8482308d4490ca15a1f1032e4545425b522321211b0f8c848c8a8c8c54a1232f4642421574c009156dc152b485020e9e04e0c713da9245cbb641071a78e0e3c1931032e80003107ab60b8490b678315735fdc0820e2a38a280820a00a00535cf2d5b8a8c4098c0548d2a70a26a1142124225c2808753b150c03a423aca22847ac18acbc2854b9107aa2ecbaa1a75d12065018a2352bb74312fd8e102a0a30e91e548c86bd2191de57060d62c46ea17a10d7052f5e848483512328f525415e2b48460d705558f848a90ba803142c59b0ea9a8c84828558dd4d4a84828043b358b1112083642a84317d4ac485e846838f4280b170d66beb0472604bbd3136dec3821218a824012c2429465e8c81cc220bb603921212e42464548265ab4658b298424848ac12a34544b48b22a1793e8a462e28b1e706e1522480a6723f84c049f85e033107c96c967987c36e633319f7de0330f7c96f459073ee3c067977c66c9671bf84c039f65e0b34a3ea3e46c92cf5eaeac109c5b83c4523885d50d4aaf6e08e4d50d60a264f01390ab1bce57375cf0ca06495ed960c82b1bc0786583d12b1baabcb2a1845736f0bcaa8102af6a10a4a6070f5ef560c1ab1e70e65981f0aa0799573c90e0150f1d78c58324af7870bde221014d7256515c78850590575154a8c9e0cba80cba8cca408b0c2b7587cd293b3c90b2039253762c6205d3292b924e59318153562ce09415789cb2628d53563c71ca8a2da7acb0615426329cb242ca292bbe53563c396505eb9415a7535574e0541514f851a826164cc042a550990c312a13a4519960411df1e5d411eba9237048ad36a754a6536a25a7d4d629d591532a21a7543e4ea9769c52d798b14971c0c8290efe38c5411ca7383063141f14a438a0e21407419ce2403dc58195531c4039c5c1778a839f7f07c58525a3b888e495160f4671f11ac50523a3b8386414170618c5c51e3f14a8231a19758401461d71c6a823b818754413a38e301a75440da38e2061d41121fc39165480031432a83eae8cea0386517d9c30aa0f6e541f198ceac3c9a83e7246f13182517c50328a0fd8283e16308a0f3f46f131c7283ed618c5c717a3f8a862141f4c8ce2c388573ccc318a8fa3517c6c19c5070fa3f89061141f5246f131c2283e4418c587cf283e2818c547ce9fd2b16135b9a1d9b1c9690280099cf0ac54a9511b40416de0649406b1511a8860940697bcd2c2334a83088cd2a091511a18324a8339466930c6280daa18a58112a334106294065a4669d0c3280d6418a5810ba334f04669e0334a030a4669a0334a036d47667593d20100ce9526af7208c1ab1c2cf0a356399caf72105fe5b0af7098c02b1c0a7985c319af7078e2150e32bcc221e8150e15fc3e610514f8eccca07c8c78f54239ca2788513e44a37c5419145edb4ed98071caa6895336419cb2d1326a83ef948d0fa76ca69cb229e1940d08a76c263865a383d384677ceff8d77bb7f885f26352453b699996d1e0f8703e1c0dcff02e0378568abc3a4496232f4f8ecc2738a400c30823a8436439caf2c484129ea84364391a813811d421b21419a943141911d5901279298a9212d5a06e394d284845548e5475ad2155b76431a340196184a32c5d4c212ea810e7c1a3cae23f32024ab764f1721e994f908a889e0c219932fca30a553cf0425fa837d050812fb0b34a17407488828a8d8525842c60f81afaf0e23d961a2224de4acd0f31c48349e2cf2a280d1511b068e1a9d8a035fcd9c517b1de0af9387ce0a980c308159e869e57ef10a2e26980e1d5301e089f5f9f3c9535a298e473a8e0a95051a591d7e1863f5dd06323e26f818615a054a9c2c20d346c3c1beb2b1ee070830d3558a942c30c32c4400586175c68610a0b52a2942b4051811cc510fcbc8edb36340259219452f4336810fdcc082110082a905156602184131edfc60c7f92e34f427faa218b16523835e04f769c70f8930d6d44f1272a2717fec4c229ca9fca3fad7052e14f29fc09853f9d702ae14f249cc653f827f04fdfa9fb13f727ad02af51402b24913f34c81641e4b5430a21e4b5413403bc16c81f7deca101cd6b70686ebcd6c61aafa9818619af95f15a015e23e3b5315e13030c2f5eebe2352eb4c8c45f0e4a0ca50aab0b5252d848307823b9f935e41701bf857c06c667697c86c667577c08407c06851c583e6b03898da4e6b321b2203efbb291cc7c66c51b89cc6744ef86156f24a7cfb6309191918df1990f9f9d9fa59fc1f1590f3d59646bc8b17ea6461646f6480018f9009cf13e5bf8641180ec9fc0f1b3f1b7a1c69fc62606265b2c0393fc06071b6828fdb61f38f11f34f11ff14f840f3de880c2004a000a81830d7c5668610a0b52a2942b4051218511c8510cc1cfebb82d084884100400c2071e74d0420b2db030e3f33334d030c30c32c410031518fe272eb880c23fff0e252c8037f019018887b79ddf747e6bf25bce86f3dbcd6fa9df50bff1fcc69a08a17fb2fa27db04bf392161cb7edb3f8446a4fc24849ffce427a027856421e8674a0f3a58a103155668214a0a21a850b6d0420b535a9832654a39254a59b610c20a2ba890420be594164411c429a508650825500825082efc4c07a2148ee3b8effbbe30044120a01042104184eff3bc11462049519c4161068519147e464ab9c2f7799e00cad28a942e5280429022454a092442082d4c8912254a942851ca29e50a53a2941d17a5ecb82865c745e9b8196488810a0c33fccc0b335dbacc74f999ed675a6801a88516a4489122254a942851a24499d2c294124884105a9852aef03350a04081024505155450e1675a985202a590c2082390e428863f53823f5302793f538a1042598ad0c294520020fc4c0b25d0cf78e081073fd3c2945284101a99e28045125184055707531ca0800430b248228ad0f100425c0608040f3dec80230b30a0b0828a2a9e70620926da58030c018640e20822668078190a0840032f5301482020738197e172c4cb685165bebc0c1252962caf72d02087f86e74f05be59783286e14fde9853fb5808523473104c9510cc1cf5329214731043faf9bd380e7fcb2910c5d6428ca720a8165cb16241888c0a4455834e0495115b89870fc5a60d032174d10a22e30d860830d361c698274465260d0d269c0e3791e97aee382ce85cb962d9a2056a4c0a085052d588e12658b14185cd032174d10a22e3154f0423b5b0cf0ab4e0e1dac22f470f4d7e07536189da7600e7d1e398a21f8795d00e902481740ba00d205902e807401a40b205d00f9374cc2c42eb9248c4b2491c4124c22c900269734c0026b74c002314c20592416014422c0c82011b063036e1ca246181bd0639134381006220d706401978401c8227ec4f8b0830f3d2e01048f981d6b5ce2814b24a163031d885d420725bfd1c1081d7260128625721c22c71a61e2c024160bc38e37e040c01a98ac114b03930d8ddf5c69c4b60658a1e2a960408c077eebc06f1cf8ed92df2cf96d03bf69e0b74a7ec3c06f17f84d92dfc2fc16c96f16f80d92df22f09bab01bf39f21b037e6be437467e5bc06f8b207280df0601840f3c7eb3e3373ae2f8ed8ddfdaf86d8ddfd2f857b04165014b0d961a2c35586ab0d460a9c15283a5064bcd468e62087edeb7917b5e0e1750a1c8974974f8e2ab90f05530791e2ad182fe0c618021f35b34f055bafc0c203c1659bc880f26024fc31e54a061a509161b95922c1ed9f261a9a1c203158d0a174438a92c293f6c6432f0f33ccfebbaaeebbaae23e28a1e8af81a62c024094b624881e152c50a158ac4b05f26d161b55204954968d8030c0f54a06104c64a133003c0623304824ac90d1964f108160fb67c5db460a9a99202151e66d042459b810b152e6c80c4860a2aabe4c8c1c951a5503b3a3938363433325a764a954aa550a99d944e2a278593b249d1a46652327b70c3f6397cf054f6a950e0a57b2a384f4584a772f33640f2546c9e0a1529343e55c6a7b8f894169fcae253507caa894f31f1a91c3e85c3a76ef8d4984f89f9d40b9f92f229143e157eaafb54d0a7803e25c2a742f894003ef5c1a73af8d4069fd2e0534f3e85c1a77a3e15c9a72af0a90b3e25814fc13e05814f39f2a9463eb5c8a714f914cfa7589ff2e1535e3e85f4a9a34f197d0a884f75f954d1a7883ec5e5535b3ea5e5533f7c2acba7863e85e553eba7d44fa59f423f757ecafc94ea53a94fa13eb5f3299d4f35f954cea7703e75f3299b4ff97caae653349f9af954059fe2e3537b7c4a8f4fe5f1293c3e75c7a7643e75fa94f6293a3e35c7a7b24fc5f1a97d5b448adfa0f84d8c2d8c2ae5575961539971c69b41861934bc192d3c7933566f86ce97b154c6235f0603be8c432e838d32bc3c1994fc18577e8c127e8ceec708fa3102f06244f26244e0c578e3c398e3c308e3c3f0e2c188e4c180e4c178e3c1d8ff0293ffa203ff0523ff4596ffe2fc2f7af82f66782f34f05e00f25ef4f15e5cf05d20f15d04f1593c9245229fc594c7228bbf828f2bc6b0428eaf428fafa2cb57913e1505782a84a022ca5321be147f140f88e28887028502857742cb3b01c32b21c42b61f44a10bd1219bc1216bc12399f44ec93c0e493b0e49398e49188e29120c023e13d12aa4722e791580248f10498e109107e1123f8228e7e0812fc108cfc10480f869007f3c78319e183c0e483b8c00701c97f49fa2f07f82f75fc1723fecb97f7a2c57b99e2bd6cefc583f792c17b01c02355f24814f8a328fe8889379ae38d7478a31cde48ca1b9df04624bc51f846207c172dbfe5fb2dacdfb2f35a20792dadd7c2c86bd143cbf95a9e3c160f1e4bcdaf1f78959257c3bc0ac8ab6bbcfac5ab4fbcfabd1af4a70b7f867fd6bcd0212f74f442e60bc9f042ab17c2b9874cbe07027c0f437c0f46cf03d3f320e679a0e47980e47700e27720fa1dc61d765e8711bc0e48bc0e32e4d0c1e7c0f339dc7c0ed9e370c9df90c1db00fb2a937c9545be4a1f5f4589af92e5abecf05552f82ae357a9e0abf03c0d2400c1cf10e66790e367f0e26750e267d0f233a83fc3094f2590a772c653a1e2a934f15472782a273c95f0a978608619af85012f82f9c308432cc45f8e80b8682cabe7151c6ca8b2420a43801122882f5e908e8c80e85244c4658b961fb20c6159d5143d3d204731043faf030202020202020202ea388ee3b88e1cc510fc3ef080dbc8510cc1396e0e4803016ce9162184f892e54b96155e9882c39429367022387221f7795cf764058e143548bd76c66b5b1c4506028f5c144555e18a952a34146d99b21dfc432c60b9c34b117ac7d12652172e5ab21c8acc3922135e03d5f88bdf93137c3499ffc93ee093ee7fe23d41b16a582aec2aacfe271921bf99699161f10fd173c1ca21441733bea789cfb87c5680ef21e2b31fb2a1cfd4cfccd7cf76f83ff0590d190d7f3201017b51648fd7f4e809c0ffa5a767a49e09bec7c96756d685b5fc587238edc93e9be133197eb38669d0808a0b2cac15bfc289a0280004ceeba011c6ebc8f13a79bc0e035ea77b1d1814f919242419255e467d4d0d305e33e43504bc16c86b85bc96c86b356cfcf15a1eafad56665d7c94a1afe1684f9e80c99200f30f432b1051a1b2e58517d25798e83341b6fcc2599ef2e4c90a456b1d6882fca47bf2e4559df1ba40f0a2b86f8ae24de5f0b6be78b5463ee8e8bd11c02b59e04c868befac9ce16ce06378bc2a99d775e5acc601eff9f1ae74de177a5633f4de156f8a8db795f4da5ce095c09ccd68f01d20af4c07bea362cd2687f7c49cddb4f13100bc3a1f7826f5558d795f52deedf81e1bce70663ec6c26b53c92b19f11ec9f7f0f1aec4785f625e8de983d838c3b1e2634ebca7259e13e25d61e08591f02e1aef03bea8d337edf09eb478ee8957c3e283c4b39c397e097556b3c87b7a9cd5acef59f1490692ef8c585b04a912ded610af0e24cf14c3bb8ac00b0b7a7792f7f161cd090def9828ef6ae985597953446f6b8f57c5c8fb7a729641f99f2eef4e0dcfe4074d1e6b35e17b605e96f8c2ac78652af9ae895715e5758df1b212f086a1b2d6048c5f42e25d27ff447bb514fe0703cfd408f29e1def0cce7771bc33477c478297c5c50bebc0ebc47bc704bd3c94bc62c237a5c5db82e45d71f0be88586bc2c82f6df1f2b4f02655f1ea50e0996478518cbcad9ab59939deb3795561de17f8aa88785d0e58e3e1e215c3fa9439f13f4dbc2728cf8def547c0ff7ea08f24cdd9b92795b2a9cd598f05e11af4a90f7957a79ba37a9cbabfac0fb2ad76ab8782f8d7775c90b53e16589f0c2987835d6fff8f1ae42785f499c1dc2af22ef4ed03365f1f2686f5206af6a8bd7a581b31c427e8982339c0a7c4c8e7795c2fbf2622d73e37fb47859217893542f2a8d97268cb31a38de83e3dd39e199d0787744f04d31bca90fde1696373580b755c47bbaf25c0cefce03be0984776700cf74c6cb53c92b465c73f2c23b66855735e57595f1f2886fd2975795c1eb427a67d0ef2cf0a2a27c5319af76c2ff5ce0e5c1e14d1ae34d51e07581f0eeecf14d3967361bbc07c98b1afaa643d67446f8a53fce6a48788f88b31b4b3e36c2eb44e8551af33a79f28eb9e05d75e085adf0ee0cf24d3c2f4be885f5f1aa34785d41bc2b17ded719af06e6839ebc29d6db9ae13db5f15c19ef3af13ea9b31b283e66f36640ff73be289f6f02f3ee08f14c1378513ddfe4e50c07848f5d39bb51e263333469bc9a201f842a71f1aa6e785d73bc2b2bde17256732587c47c3eb848757e9034a55acd92cf15ed3cbd2e185e57176e3c4c76a5e193fbed3b296adf13f59bc1af73fad359c2a1fcbb2c6c3c72b268357d3e183322535de999cefe478353e3e68873785c7db2ac1ab29e083babcaa3ede17ce1acf1faf9827ef4d7e405e56102fcc016fea85b725c5cbe3e44d225f1505af8be82c67f5b144d67450bf14c79bc2e26d4960adc91dbf04c5eb847bc788f0cee4f05deb8ce689f74e7859237893582f4bca0b2be35565f1ba2a399349e33b1dd678c478c538517af2a256f826325eed8b0f1ac0a7d398ef9cac6595fc8f1b6b3456bc97c29acefe121b6738437cac881735c43741b2a643f34b6e7c9a29c17b16bca8355e9a31de0df99e2cafce98671a7abfc0f7fcf11ec6f79c709623c4c742f0ca60f29d16ef8e1cdfa49ded04e099a4784f393c47e5d58af8a00dce721cf04b1a9ce148e06370bca97d5b0378b7c0f7ecf1b2d41726c8bbdac00b43e1dd81e39b76ad09eb97acbc27017cd0033ed110f21e95d74907ef980dce6e0af958005e9e13dea4245e9b31af14c5cb2382570c0baf4a02ef0be83d59f1dc126b4e7078c7b0f0a68c785b899ce5e4f14babd72602af54f4f278e01583c2ab69f141e45a0682ff99e3555df2be50785189bc2ded0c026b392c7c2cccbb42bd2ff55326c5ff38f1f258f28a21e1acc683f78e5e15d1eb3ae4d528f0414baccd30bd77c1bb58bc4fcfab61f9209d351d27bf34c7aba2e27549f2aa0c795f137cda12fec90d2f4ff8267979b5301ff4c4abbdf14131bc3bdd3371f1ce24f15d6ced54c877352f8f0c6fd216af8d23afa4be361d782526ce5004f8a649de13fa1c0e6738607c6c8af784f34179bcab23de976b6d6d3e830efc487176e3808f89f0eea8cfa4c8ab63c733a9ce9c9c5e3131bc279e0f12e4d52cf81f4268b438e3417ac5ec9b6ae26d39f2b22e799368d66c54efb95ed522efabe74529e06dcdbc5a023e68cbbb63f44cadf774c673602c33f3c57ba757d581f7a5c2ab7dff037b978af7a1e0e5a9e04d9ab27683f4b17d55525e1719af66c38b50b486d3c2c7d6351c3d3e06c6194e241f9be35d11f2c278de8de27d78d69a70e097ca7851e93709f2a60c795d3aaf96c1ff18729691ffa3e545a1f1d280f16a967c5016efcc0edfc1d69ae8f14b517c9a51c07ba99747cb9bc4c78b5abf899097a7ca9be4c59a8c0adf89f02e1def83c2ab82e27555e0e5c1799342f051dfaff89eef4d65e07581ef0a8df73582b31c293ed6f4ae1e79611ebca93cde96d2bb53c5337960cd6688f7626f0ae96d19e05d15fe09102f4f93370948c98757c5c3ebcae35d2bdec782331c463e96c6bb63c83755b0d604885f3a7a556abcae11bcac2b2f0c8fb39910be23e4d51af0415fce7278f89825ef4e0ccf84c7cbc3c29b34c59a0e05bf44c78b22fa2646ded4136fcb016bdacdffe8f1a6a8bc2d2ade9396e77cf08278f7827f6273b61efc13ef0c67e96306785522785f2fbc3be93325f2aebe785f49ef6a8bf765c9fb24df03c8cba2e30d13bea7189e5b618d6701af180e9ea1c9e1bdefdd89e2992e79550f785f1dbcaaf17555b1b60d789f1cce7234f04b402f2a856f0ae34d69795b78acd9d8f0de075e96052fcce8e59179933438dba1e09998786d0879a51a5e16015e58045e8dfc1f0abc2b82f7c9f2a604795d326738a78f4979773ef04d2c9ce1fcb1f2658d799374d666ec78efe665f9f18641e1d3e901dfe9bccbc4fbecbc3b243c5301ce789878c5d8acad1eef23c3bb43c83739596b52c62f29b17692e3bb7d530a785d4ede93071fd480f764c6735f285539ab497a2f01ef09840f7ae4e581f22635f16a747c909577a7906f02c0bbf2de17146fca87b765c6598e091f83e4d5eef8a01bdee5d913d173e8abb1f1412fac69a8ffe1e3bd8eefa961991927b4264bfc129857c692efa27851327cd31bafca7c5d7dbc270a3ea8901795c54b03c5d98d141fbb39bb09c1c750785759bc2f0d9cedf43c93132faacb3731e06ca39ff9725653c37b4fbca8ee9ba438bbe1e2633b9f686a78cf7b6542f05d172fcf085e3153d66af0786f8ef734c373515e1e0fdea4f3b541e4957438cbc9e1631a785700785f5cde94cfdb125282e3134d1befb1f0b298dea40ad69a80bf24f4b2d678614b6b38247ccc7c9d80ef18ee55fdbc2e306fd6fdcffaa254dff4c3594e25bf24c2cbb3bd4944af76c40705e07d8def69e19d417d67c76b83c72bfdbcda131f047426e3c57735bcda231f24c4da0d0daf84c9eb44f58ec9de93cc07cd718673e5635bce70ce8f75797988dea43f5ecd84ff09f36a18fc0f025e569417568017b57d13142feb8a17a68177e1789f12d66ccef732794f167c90216b4d2af925325e96961766c87b32e1b9ee5d59795f729ce93cf24c53de14075e1709af8e049e098637f5f3b6ced789fa2a8de04565e06dfdac658efc8f196b4d847e89cbbb35fc13275e8d031fb4c5ab63c93309bd1a0e0f84c959ce037e698317b5f4b64638c381e46371bcdbc13f097a35453e88e82ca7838f39e0ace688f7c0784f2c3c67c2cb4a7a936e5e5411df14e62c87898fc5e4e8e1d544f81f07bc97f13d2bac3551fa2535de9d3cbee9e64d8df0b684785575bc2fed0c87838fe1f0cadcf15dfaaa2ef0bec457a701cf24e5655de00da3e5e519e14d1ae26c8689ef9ad678e678c55cf0b26a786173ac3591f24bea5a930afc52183f55acd160794f543ae2dde9e39b745e9e2e6f5220afe6c1ff30f26e947f12c45a930dfc5201ce6ef2f81806ef847c8ffaae007961a8b31c4b7e29e8dd219f698c5715c7eb6a7a353c3e088777af789f0bce6c4c782f036753d6f6e79f70f06a4ffee7909735c81b26caeb448477cc07ef4e0bcf34c78bc2e2a579e24d89795d297c9a79e3bd9af774c273dfbb627a61359cddc4f1b10ace6ebcf818ea454de06d4df0aefc78613aefaac9fb3adf151f2f2ce7134dcd7b19bc3bde3375f1aa76785d78bca7289e3be25d5d795f78bc3b579e499077d7f7c8b1ab8677a6e83b4bded4066fcb5cdb49e3994aa014c499939a578c96d709eb1d937a2f3ff363ad668ff7e8785915bc3020d69aa4f04be83bb3df9df1f25ce015e3bda80ebe89002f0a92b70580f72abea75bd3c1e1970c39bb669dacaf12095e15d0eb5ae23d1df1dcd18b12bfe98a7759f82760ce6660efb15e1409dfb4c59a8c08df71f0aa60ef2b841705c53771e05d2df1be26b0a693c12fd9f16a5c7c1009af9312de31e31a4f1eaf180cde05c03f91599359bf235f270078c7b05e5515afeb02de943785f3b65858db9c93cccd77179ce154f0311ade14ea6dc1f0ce107db7819727ca9b04c5bb227a5f0938dbe17926255e0d8c0f3ae1eca6041f53e11d8def91f2b2665e180fefdaf04f9e7857dbfb6a624d66cb7724acdda4af4482f7f4c10739e06c2783677ae22c47905f02c0d9cd073e66c28b9ae46d3d79772ef926282f0b8817b6c8d98ecf3341f1b24c78615cace904e097f058cb7e5e0311ded4186f0b034a56de15242fac3bcb71e16392acd180f0de076b38327c6ce8ddca6a4b7c1008673417780f871775f44d0f7859e70b03e4aca691f7f8785766bcaf10bc2b21de57035ed47e530e2f8fcf9b64e555b5ded7072f4a836f1aa2b3e16595e04d72f2ae80ef31e36565f0c2bebc3b957cd3003e65ebff187176b3c7c73238cb49e4977a5e8d8c0f82f2a6ee785b24785397bc2ef26ccccbaa7961422f6ae69b7af092785978bc61467835f3836a5e94256fab83f734c57348bc6fe07b0ef0ea54e099667853e2dbf2f2a292de9600de551aef8b04af06810f1ae2ac0603ef21e04d8d6febcb9b92f2b69a78b500fccf226bd995ff017356c3c17b46af06c50705bd3698bc92142fcb8717e6c7da49fc4e106736da7b8ebc2b9ef7f5c3a79904bc877a53366f2bca598e081f83c0598ef8b108bcdad207adb17652c077386b4d0cf9252b7a7078752e79261f5e151def2b3ba3f1e03d0fd69a0cf14b5f5e16162f6c03671909ffb3654dc6cb770338cbe1e2974eef8e04be897b794c78938e789da4af92085ead920faae245cdf1d2a8f14947885fcae4cc46e73d08bc37e07bd8f8a1e2bdf995be7897e27b80ce70943e16c8cbca5e580e6b19ec7fd2786fc057d2c0ab25f1411ebcab455e58066f2a8db775c92b0382efb8389371c07760ce6e46f0b114d668f278cf85339cec63515e96062f0cccdab6f13e24bc290bde560e2f2a93b7c5bd2b385ed82f6afc262c5e94936f227a6f685ea98cf764f41c967785c90b6be14da9de560c673b3acf74c459cd04de13644d8685ef82ce7266f818065e55f6ba6e7853ebdb0a859c5ea5f9a1dec671aa478edcd79da0ab0cc371ecd25534531154d3b36b9529a782a1178a68ea8164ca8d1028c9ed4c3bd253b75315cd501d420ce2f48052edc035f5448f14b71f2b527078a4dc5474edb83555512eedb88d7440499ae0379e28b78a1bf8a56303ca4d5dc37125432e15d713ed3e474a4f35d76d25570f05d353dd440694abd7995c1882e2f885694a828d9428b87961e76da3e88168778a4322a76e318f368f88132325b8925df7ad23a98aa2b9a9e4024a711bc72ee440ee5cb7cff34e05949b798ee167a6dbf7a52488868b949f897aa4a7aa5fc879dd866e318f84380e3cd2826641da38454a311dcdb453550e3d49534cd3444af4e340af1357d3233f9053c50494e06976a7ba81604aae26ea8d8894aa4aaee3e799a1876e2a7a1e52ae20fa7de0f87969ea81608a1a526edeb8815fb876def789a0b922a0fc56113dc3ce23bf6d153d2f2d045d41d50c378eecc414cb01cad54bd52ee444f43b4d2e44b91c4e8494297776e3e88d626a7e9faaaa83941ed9715e28a2aaa78622187e4322a71a79e9d2719d2025097ae2a986e1e87d21b8a1ab014aee5bd57504d1ee4c53533dd340caeddc4e2edc3a153d3912153b404a4ee4cef54cbbef34431344c13fcaf4fbba50244f2f054d9354cd931f25f875e32a7e1bb989e0b816090d1d992c7c7d94a9f8ade1297eeab6a2dee681249cf8284591fcce4e454550dd42d33cf72853f35b3755ed4cf153532e558bd211cb498f921bbfcfe356efdbb6955347f39447098ea6f77d29b88a9bbaaa5f3724721bf7c3098f1224c56d1dd14e34c57335d1b573e107ca0f47b271baa314d7cefc3c6f25d7d3fcb61014b79cec28bdf4e4c2d0db423424bfb44b8744cec84815ea6272114f7594dc689aa4b7a5a6ca89a0d8c5e452fe40f9b1429ee828430e0449955b450e4445b343436e8ed254bf0d1dc16f43c1aef3bcef0341394af0ecb8d5e4d6531d55d04bd70e8f531c65789a5c4a825f3876e67786e357431112d1142a57846438c15182e7c69126387aaaba79e9ca9dde28d18efc3cd5144532e44413698b17a42d5ea0fc0001010101010101adea0a404046eaaaa69bf8692438b951821e678a5eb872231772e906aa716aa35cc5b45bbf74edb66e0d4d8e3cb151a29d897a9b677e68ca81a60a760638ad519aabdaa9a0c8a966ea99e6b99dd4283f7205d7331cd3152451b3eb845091bc70111ab92e8d32e4428f44d7ad1bbf734c49f0088d525cd1d304d76e4b5772544f67946717721f0aaea7677ee1767243228766d97204047432a31cbf941cbd0fecbacecbe3544639aadbb88ea0b99a274a6ea37a2a406976a36a86dfb89a670a8a2b2a8e604e6494dc9676a3b98adfb68d9db7a6a7314ad1dcd03434d1ee0cc790dcd621910ba1c000e5e7869318e5b99123a97aa10772e0770aa31c378e03d38e1c43ee43cd95ebe20446d9ad9fb78adcb7aaa6b882e2f645999adde98d1b697e66aa7e1d3a24729ea8e5e445a99add0aaa63e871e82a7aa8ba7e5e17e5479e28887660b7ae27d979dc911b272e4a511d37b34b470e3441120dd1d31625b77ea0297aeb686e5e689aa778d2a2143fd553377514552e1cb72d5481ca15a1214e5994a979ae9c4a7aa0979addd78ddfd091c9029289121541f942218a82b002043474640e8d5894ab088a26e871a448aeabb9911f3780d315a59a9e2837a69f4886ab9886db90c811097546272b4a114dd3f15c377415b9ed14c12a4a343d53d0343791e4c26f04498e0b8a83bac570a2a2544d7034476e44d7150cc335edc8234e53941eb98e5dcaa1a9fa9926d99dde7792a24cbb511c5311dd38d2fbc094ecbc38455176aa7a72e0969a2917ae1e59e50445c99123d799a6797ea3f7a51df9e5f4444986200772e0378622b86d4245e9487472a21c4d70fc524fe556520dcfcd3c35516ee677862967a2de89893244cf5434c74d14c32f243befb444d98521caad29888ee0e6719e12a59986296aa21cba92ebd7a9e42989f25b3db01339734d3930dcc4318b1312a5aaae2b777ae23aa6e4079a6253e98de7a892df2aaaa0ca7961c85492e7787edcf799ea478eea111855cb071a694ba51872a169a25e68822b4872e2172b4d8e43d54d14bf74e3462eec8644a18d43322a3afa34a5b2e34e534dd7ad0bc36ffb42b004e52a92279aaa1bba9da3c9a920094a743bbb30ecbc2e05475086ebbaaddc49765b088264068626825244396fed40125d39d0333b510b41e9a5e488762927aedfe79d9baa81a00ccf94034174ebb8d0434175cda40c5514454f725c3d55ec546ec4a40c372ff446af0bcfefdb36551d537e2bd77d1b67925b88a6a887d4f9a0892945cff3cecf0bcf0fedce500c8744ee28cb1675fc4049a29d8982e167ae5cc8ad23e98152354911e546f02343130cbd534b2a4d9433c18fdcc0d5fcc6aeeb3a50a2dc177ea8989e29f76d27c7691c28b9d55b3d32454fb15bd54f1cbd04689794e4c8a11ca7822229a2a6096e432257547655344bca2f1cc5efdb40923cb98ef44242a101e88b917ac475605022d5c8c80b2a86b601d44bc17125b7503c87446ed440b98567888eabc7a127099ea9aa65a03cd791fbd20e15c1504c4faf23aaa4e440f324cd3315b754dd3ab5a3a45c373435c70f55bfd10b8981121cd3d0f344f0e4c2eedcbad044325321920397d02629c77133c3cee452eff4d4ad2b4ab7ee02251782a07a72a24886a23782e890c87ddc11c91d2165e192654b38832649e9a9a46a92aaf879e40a8660b674e9620a15a5e1105a98525d43f03c55913cd3550c577348e4364e2cca22d4e5088ca43455ef1c47504439d2343f8ffb340b94e189aa6287a22627a2a9b892e38f1529404069111424b54a17ad02a5799e63f8ad6aa792283a7a219189761c054a0e4dc7745b41ce133bf2234323d38b1423158cf902121a24e538aaaae7a11f187edee871a736815225519204b753edcc6df5507048e4908a8a8c38aed324509ee9896e69d8855f1872a3786e836811283b13ed527215d5cee3ce51110d568ea8e96de6c991e0da716b4822797995e2988edbd9a1aa989e6a2a7a9f219aab54d714ed4c4e25cfaffbbc900323449151975639ae27ba9e1f4886297a86e1d97185681028bb0ff5d48d14bf3525d573151f50aa1bb77a5f78ae9fba89dfca79dc23a5da795d27aaea089220eaa1e8d735a239a044c3540cc951e4425504d3f368e41a507ee718aaa1b885a0f78da248728e94e206a2293986e0c87d26077a9ba731a024bf55e53a7024c930e4547224029342f94943af91f21bb90fed3813dc4c944bbb949172fc44b1fbcc105cbbf314bf8edb03680b28c1ef13c96e4b4f5345c5f50383d014506ee2198226c9919dbaadabba0d89dc88a42d52a25fe781eb179e21287aa0c881a129526e9c6a76a2f9a1aab98529c8454ba4ec4494ec468f1c515535c98d4b40b9a9a03a72e2b97d5e279ae0f96dd1102949af0bcd30ddd2cf135774f586444e8822158080525588a2331489b4434a0e0cd7754d45530453941b3913c95c61ca6884d4e54833a4e4c0cddcc2534cd1740bd7f13c85ba2e55896a00024a1150aa1f48ae2018821c1a6ee7868246485d78d00a2941141d517323bfd0dbc06dfd48aed30e5076e128a69dd9a99f3824aa5b4ed3088d9052e54eb02343f043bf313c49f0fb0629c75505bbf1f4c0f55c3dd44335414a53e450513d3ff04453ee044336340394dc087621979a61e86d9c287a8194663aaee1f675aa7772e4468a46eaca09711fb70cd00029cf8e1bbd0e15d594db4e944ccdd0fe28478edb44f223c7503c4dd2fbfc28c150fdd6935c373444b98f43fb284f914bd1750b41545dc56ff5d6d0f828b76e3b557245cf7005b71414f728bb6e14d18de3c091ebbc6e25534d8f72f34c53f5b8355dd77014bb8ecca35c3f5355c171edd0eff33ed1fba2e1518aea788260aa9edc8682277a0aa1a6441e27c475dc1da5276e29b88e24a886ebd7ad3f507e42cf8e725b456f25c18e4ccdee1447914452ab6875946ba78e5eb88ee2f951f981f2c3c28f95177e8080c42f4040e2978e3480464799a21c1a7a2b398adfb8a5a43747b9a99bf889dff975a7d799abf786264799aae7ea7deb9972dd776e1caac5519ae6f891dcb7ade4869add6776709467b76eaa478e5d0a76e397866e686f94e73892e7fa75dc2a8a9b789a432227d6a1b9518621e9ad20686e69d771de760e895c1733bc810ead8df204c37423c7502541933cbf543542c120ba3934364a6f0b394e0dbdf0fc52921b3d6d8d92e4d68d54b751454f4f35c14f53a334d1703545731bcd7448e44844b4344a4efd46134db9710453cffcd0219143808646297aa1f77da8b86ede897adc3924725c97887646d9ad27198ee0b87d5fa786a43824722951d129040645528f46eec78a94a320c8ef10cd8c92e338154ccfee2351b3fb3c540849c5227248aa942bb432ca133c5554f44872e34212ecbe2191fbb80294aa9a86aa8a9e20288adee6a94322d7316964945e179a29b99ee4ba8a1f7a8e519a676a76de6776282782607a8ae3179a18658a92a0c7a9e0c98d2b87aa269a65cbe945062020202020208e134d232fa66887164629a61d97729ea882a448a2aa51102a51c80d2199327c434866970f0c52a7820646f971a8c7a56a1a6e62178e2aa9760163050848dd62a2474661685f9462e8ad9ec7a55e3876a7ba6e9a17e5baae6037ae244aa2684a829c205a17a5297ea7d87de9266e9fba9a2417a5b99e5be88d6ada8928796ed962a21f51948ed3b6283bce1c4dd20441f2ecc28ddbb88c485e60208a026a5a9421c891aac789dd3872dc379a5cb20c1d01a95b4e13e9cbf6695994e1d685e2679ea667a2ea8a8a4322b70a21a947aa51c765318dac64c192c528060d8b9233c593dcbc351cc7b33bc18edbb42b4a0f0dd75144b72e254d744c4d2b4af403d7d03b57141c49ee3cb52aca4d0cc9ed0b530e0d4d72ec3c2a4a4e2451ef3c455535c9f4fbd41eb4294a50554990eb3c103cd714355720a0113c3a412a57846ad0a42843f10cc735f456f1ebbcb0f386448e53a1eb46f0e8d4a228bdd14b450f0ccff0334d1425cda1113c3a1a833428ca543cbdf53bd7d30bcd701bbb274a74154574f348715d57f5f34ecd89d2ebb6b04bb76e3bc90e1c4d301d3923f5c823416ba2dcce91dc5473eb4c5125516e1b12395234528f3c8d89d2fb462e25c14dec366f34536e48e484309188c01c8520186a4b94674a9ae27a72df877e21f89d4322077e31684a94e42a7a239a9ae48776ea77aa36684994e197a2e9969e1fd8a9a4b76d1a12e548921fca792b9782e1a692699722a3158080ba1419a5aa6a84740201914d65ea819f88869b499a6adaa1dd18ae195379aa6838929bba9129f8a16b3a247263c779114a8dd421b254010232528f80808080964a35dc54ef04b96f04513425bb113c3a122a42023b544d8ba08ce051153214a95c1172218b956b0776dbf8819f9a8ee6d8ad4322d7754a25eaa15b9a7a6b3a766068865e911091897a25283d522439eee33cf223bf55043791ca1521352341697e1f2a82aaa97a21288a1e1a8ea0f4be0dfcc6cdfb3ad3fc506e14a95c11ca9289a014c14de45470e5507214cf8dcb42507aaabaa55e687a27189ee62982a0ecd67315c535dc4c4e3c5590cba43c550f254571e5561005396fc5a4043b91dc42cf4b438e34cff31b53aa9ca8ae2909aa22c87969aa7a9e985244d5955b378e34bf0d4dd3f30365fa8160878262caa55b6782e0f779a044d70d44b7ee04c1140d41d4f4bca4f23bcf0f15d5f3143b506e2718921b2786a9996e1b297235641c28c571f4528e53d34ef5d62e54bd4bcaeedbc42e44d56d544dd3f4c62191cb2c2957b2db42d4e3364ee4c62f4c3750861f7872a047a66afa69a044418ff454ee3bd36e235190cc40897aa9c875a6388a9bf9992a2a829594640a761cd77de467a65bb78a9f474939a2211882eb46926a0a9ae80804f461a0ec5075db36103d43555dbd494a0edc46f0f3440e1ccd8d3bd7cebb40d9715f3872a2ba79eab789660a4586177e404232494ad3344f33444d5245414ff5ce30e5a97ea7b98da0398a5d24e5e79de43792e697a2dcd99164e759a0f4cc93544d4f1dc1b50bb72ead40998a9e2a76e306929b19ae2305caeee350545dd3ae13b710fdbc0d8a0c92b223439354b7d044cf75033fb09b4099825cb886e63a8664789267da49a05441750db76e13456f5d4f6f4bd114a0283f507ea600017531b900011d19a94040462a529628b20894ea1a6e9b8982e0e6a5e2d69119ac1cbd513c49703d53740d5152fc5e65c7a5e128862b088a26d979de7747e62ac313f5cc50ddbe2e3cb753f4ba56499e9cb89de84792dc11044a8f53c1d13c556e3d4df304c123ef0165fa991db78e63a7aa2188ae5b15d92325da89a7e9a51d487a26a78648472a92f9829844e680f2f3ba74fcc873fb44d034d1b5eb1a509a69378ae24a76eac6a51d37769d2325879222b8816b086e21c96d6ac78052fd3cd53453b31bbf46cad1fb3e941bb915e438cefb56464a343c538e33c5ef0bd10d0cc9ae5b40896eaae78923da7d64ba719dda750a2857cefc3ecf4bc38d1453f41ce5c81629c91105d1b113575504c9140dbb4e9132dc567455cfae1bbbce23bdb54ba4e4c470fcb6af0b4db453cf10dc1250aa262a92e4a89eded6995f28723d64889424ca9964a8aa66087ae908a687941b47aae7468ea2078a27997e8694db38aa5b2a7edc48a226baa19e9721a03c4db40b51ce145192f3c86d9c232ba44cd5f15bbf35dd3cd444bb6ff4bc0394e9769a27a77ae2a87a1e2165c7899fe9ad2b777ee7a99ea7e70d52769de879e21972db487a5cca9154ae081d9109529ee2f671dce7815e178620e919a034cd6e24cd505ccff11455aeebb627b2404ad53c3bcf14c513f4d674553b40ca70f3d6f0143972f3c43034d53455918c8aba9086ec8f5244456e0b5574e45675ddc2cd8f72fdbe0f35d13155bb94e4b6f48cac8f3255b771dd3c343cbb5555498e8f52e540af5b531154b9b3e3ce318b6c8f325d3d9233cd8f244d34053fee28d3a32453530541525439ae4b532ef5bac8f228d56fec3c9053d313f55653f4c6c8f0283973ddbe90e3b8314d4593cbee28c77513d7900ccdee33372efd323bca8fdbc28d33bd341dd5b553bb34b23a4a75ed542f154d7545d56ef5d229323acaef5bd56fed54b02353f0ebc039ca35e5d2b323cf2e4dbd30f5be6f94a31cc1ce0b49135537b2ebd06d75228ba3dc44d1333d5135b7f54b4f12fce02849310d53523c3f74f5cc94f4de28cdf5e4c2f43cc74e45c7eee3dc284355345393ecce2e05d19f54f4f2a31a79392ada82fe9ca2971fa1b11bdb28c1f053c1cd4c552e24436e5bd9283933e4d6f42455724b41d14cd728576ee33cd2534dd2133b2e0487440e8cea02a97a6a9426a88e62e871e3fa9166777e64646994e1a8aa24397ea1e98522ea89de8746c99da2987eab19a6a99a921fda9d51a2a689aa6a078ade178620b97e1ee8456646099eded975a4f97962d7a1e006c60869f4c01859e9be0001ad9195516e6a089a5cf881e0c875a01816a0fcd213453bf45b55eee4b6f57c2123a3f442d53341904ccf30ed423315c728c7adf3b64f4dcdd11b55aee3c428576fe4b6ae03bfcffbd06f5bb3304a4fed402fdc5074053930e4d64f8d0c8c12dd4053143f2e3d53cef4d6f1fbbe28c3f534bd500c43d353d1d14c91bac400043452b922f425f3a2f4d2d004c570ec38afeb42b0eba2e4bab0233b9413557445d7f50302fabe9042dcf745c645399aa3b79aeaba921bba7d5bb64569a26b38aa5b378ea229aa211b9916e516aae21786200a9e62d78164964589925bd8912719a29f9aa6613a24729161519adeea9d9eb8a29dfa91deb6665794689a9a21a79ea818761b3a8e276456949ea972ab07aaa92a8e63078e6a9a55518aa96a86e40a8e5eba7621a7aa671623f4485d41089424a3a214c78f5cd1d023bf5325418e3b2259c8a650539d4c8a32353f721b4195ec467334b1106501936e5c1112d114b0eb6272f1408ed3218ba2e456510c4d144539355c47cffc81425494058b4a0310101050c70971e01f1914a5d971a687766a1aae2407a65d141a7e98c89e285530fdc8cef3ba6e033bd55cb7cea32373c2fc3671edc0affbc0534ad644e971e7a9aae2887ade0692a497315192aa478ae7ba9d63f8752718664b9464277adf397a2728aa5d1aaa4322a772f172747a391241ee28dc3a2a578460c89428c1cf5c4392dbc0f3eb3c303487448ec8c8cb49c210da902551a6e4f68d1f2a865e076e2ab77a65644894667a9a22a7825cd889dc1992432217723f565ef8410202fabc6e1bc36c53198a9be989241986619892a2c95486dd399e1be8a9e0488a23d92d95dd399ee1b88aabb7ade1099a9e172bc17103b7145dbbf504370ee43c4fa934d52dfd481424d1ce5b535024edd81294deba72ab2a8eea3a76e8977e5df7a52341797ea1e9ada2caada42aa61c3882f24bd7edf444702547cedc36741441099e1cd9a9ab1a86e04872e318827253bb533d5530fd4690e4c60e0425baae2b8a86e90982298772a2e7655272eb669ae9fa859fb7aaeb360a711d26a579aa1d388ae78672202786df98120cbd555539d25337d223476f48e44231e54a7a6a78a6e4a8866a7a9ae707ca0d54bb0eec3ab0fbc8b53b4fcf03a5c8a99b4a8e9e18a25b678eab97549ae04a72e2baa6a70776a177761d284d544551320cc1901b4f6e8b172e425f152020208e89e540d991e3ca8d5f7882aa87a6dd7949b9811c287a660a92df697622372472a91016a22c475e3c4eca5a52aa2aba89e1d8a9216a9e9eb96da0ec3ecfdb4c143c3774e4d2311d123922750baa7e1122b928ab8132e5c25424b99414576f3cbf6d48e442750527360325a99a6087a65cf771a9687ae6d875316108b92e2617a18d43da4aca6fe4b84e25bd4ff556f2f4404a4a6f13d593144dee1331508e9c7a86e9988ae6289262d14a94450849c51216ad4453b6a84444e6101424550a233b49798eaa08aaa899aee836929cc845453a0273c4791728b7ed43d54fec509424c313e5dc58494af124c32d0d5512543baf03d55568e31eb061ca70eb3ef5134f54453f8ee3ba219168cad1511846527e66a77e6a08821c67862b780e89dcc70171507eb6f24786177e2c50aedfb882e6a8a21b1aae61ba0e895c2a74940508888b90aaae2ca86a941f283f475fb29837fc5879e1e7088948252aeac2a5c848c562129930a86a142e280e40405888b2a8eada890ed80a94e646aaa349925d087ee7b96d2051d1162840402a51d11631a440798ea0c871a6ca999ea7ae9d0a49998a9c9a72ea0aae1dfa79ddb87142dc04ca930351733ccfef0c53130d372ea810421b146e8695407986e8666edbb6ada029761dd7c534ba2102a5089e27aa8e1fb7aa230a8aab109853e8f390b29c63c7091d7d9cc7c14a8ffb3c3554d7d423437115d75749aaeb4672a1aa9a6a8a7261b7aef24b4f72dd4e553dd1b3fb3c232c425cb7ad12fd407053d7f4e35455ddce50a47245686821508228b8a1287a82237aa62b19724171784089a6a81a7aa6077ea10aaae223a5b9a2eaf6799f8a8e9cd981df90c81da9ea2ac47158d601e5a9a6630a729d6792eb09a2de90c88d462a11d1942c5d8aacd4308494e506202020204e8b6d40f96d9d998e20697a1d1a9a9fe9481986e286721bb9799da79ee637247240a8409440405db88ccb80721cb92e04377305c34ffd8ec68d6ba43c3f7043532f3d43f4133d5365a41c3df5134f521c537035cf714a0a1d1dbb80b203bd4d25d1901c491214d34d01250886e0998edec8811f388adc19bb48b972e6d96d204792201aa226aa48d999e9fa6d643a76a219825c2804c60869e3c2d8444a931b411524494f245173e35264b47eddf2b1092851904bd71335d1955341724587442e35f27244a4821cc985352c2265178a5be775aa899a21787ee496330bea25558dc4ad8b3da4e4427244c5f30cd5735b512fedc22b6b48f9ad9f3a9ee7d68923d7795f382472e46784d4c55b0494dcb6aadd279ee8c9851db76d43221744d19623d22885904389c01c7d03d8424af53357d213c1ee43cd7135c9ad8d3d40997aa6daa5620aa6deba75dcd791425c77c51252722ac7a964ca7d2777ae28970e5282a7f7a124ba8ee24776ebc92119993280498b54f03c41ca50553bce54c973f4d2f51357d18c3540496ea9a872a4279ae7789a62d875819427da815c387a6998aaaa88729e074839a6e446aedcf68123c88da729c4795c57c7fe51865e78aee6487e648a86646a825b15eb47a9aae989a229078aaaa776e2e7797d94a0d7adeab785dd0992e066aa1bd7c1b17c949e17aae2b975a0da7ddec7895a8c8a824052a9ec519e9bf8a1a9a87aa8f9a9a037020179a410e7e9519a20986e28da759c987aa7099279942bf88163aa86a637aaa4fa711e29c48dde970e8dc5a3e4c6ee0bbd5115c36f0b436fef284f73fcbc1444bd730ca95c114ad78e521dcf10f43c92dbb8d3344fae0a10101050c70971a8fa85e348046c1d25f7791d6a8a22a876e2b87ade4647397a20b79ae96982e2d9792b3a4739a664caa521b992eab8aaa72747d985e866725b38a6e83676eb070484a41e1d99601ce5ea91eb796ee4f98128d9812b29c4c1517ede9876de0a8a9e29a69b78be518a5f67a6a2ba71a8798ee7086ed9624211e2ca58374a4fddbe7433d1334439d0dc46928d6da3e4c26d45bf1304b76e4341d463a3f43ad2ebd0703dc995fbb8cd5ba33cc92de4c25045d7144453f153a34cbdb13b49931b539114cd8e4ba3ecd6f1ec4050fcb64e0d3d9544f2d205c94b0c54ae08112d1aa5b7ad22b8919b79aaab9a926017fab067945b7a8edb7672e096a66a87a643220704d479a319e5aaaa63687e6beaadaaba859f0c5b46d9ade61a9a1db9795c9aa6a039247232d0f0f323031816a0e4d475dd4e313d37d0ec48743b0202028d90ba905186619776a49aaaaae9ad9b778e519e274a72ebba85600aa6a3391aa95a8a8c4e4e8c5224d35524cd314451500c576e5569800244c586517e9d8772a6c7811b18aedb8a0a913f56a470421cd8bdb16094e8e6a5e8989a609a72a9f9a140401c27c48d80ec1725f87d21f79da3ba9a1eb9ae1d16eb45999aab1aa2e038762308a6dbe77d9e18db45c989dd4a8e21b879e1b77aa267c572517a2a9776a62992a6479eebaa72294291bcc4f07d01020a4921eefbd27164ec16a57a9222fa711e79725f7a9a27101010d01129c4914760ac16a5378e1cd8752b89a29d8aaae428c46d526c16e5e771e429aade3882617a92e890c8211165192a5a81808080b88d13e24635168b92433774144f8f24c58de47eac482152bf94e338c45e517eea896ee0c78d5e67726467aa5eb69836a85eb6985fc02cd68ad2533bee235714edb6ad53d3cde3b64e684b172e593c0ee4a85c11cab8d82acacd0b439224d1943bd78d0cd5b408252a431055d3225448080c521623b3cbc851b92274c252517ee1988aa93882e419a69ca762f152222f23093b45b99adea99260989aa399a2e24a51a2a3277e9b29769bba9e26d80d891cf96345cacf1423a328a8daa50a10101010aa7e21a95c11da26368a12ec506ffc3a6e53bb535c557448e47eac4881f2d379508cd421a4188c6250b998558080d0a32c5c54d4dcd24588288b509722a3b4fb4823a42e1ecd230b45c97de61a9e21e99de490c8a5aa9a7220e8659f28b971fd387135416fe34653f586444e28e4364e1cc3105d274ad0db44131dbbf324d734fdc21f2b527ea4d8f003e5c7851f2b2ffc0001010189cbc536518a67179aa4a8a25b88862b990e891c18558b5017930bd76d5c17938b6754448a1e47e58a90d03251a65c687a9ea9a25ca8ae66fa0d891c16a22c4842a6d0e9e548a80889288bd8a5538cd825ca3424b730e5b8711bc9ce14c1219113222ae2ba1f2b527e904e7505a42ca96a04a54b91d10a4431a800e5274522f25284a2298021c292a50a10d08f15293f36000121a16a9723a22940403200010101fd5879618aba65b5f2c24f0d443100117911222a0202e2920508e88848d552844483ca250b9291171580808080448ea37245c8a69155a244d374f34e151cbdd503d333ccb24994e01a82ddfaaddf18aae6a77a2a2c1265a8aee8c69da0489eebe77de258854b9151142020202023a4a3908b17a22c042837ee1b4995f442af23c72e25855059305283287aa1ebe188b203b773333b74f4b8504dcfee0b4f234a325d415134d7d413c7edf4d221910b4225f28cd421d4c854ab182155e13822231509c9ec221a211d15516edf976e22ca715eeaa9297772112252b7889c10982e4528f893825e7eacfc9ca0971fb25389284772544f312453541cb90e553d1686283b30fd3e3545bd93fb3a8f0ccda128604a72db4412f4ce0d34cd6f535721d2293f565ef851b11099340001a958884c2320a0a32e459c48e58a502a44298ee2a7aee6169ea2e9a9ea2844724648476010e5376eebaa9aa779a6ab79a229488a5f4af43c5194dccc753445f32457ce08e9c8ebbc94eb36a2a6298ea6f86deada09a10a85dc8f15293f46e6507794160d21a95c44237308fdaa2095eb26ae67ca71aa6772646a8a484747a56aa2e4ba9de2ca81a4ba72df96a2a2f3a372458805a3721dbf0d54c3d124d35414577148e4843a21754b172f484258bc9c6a0a762510a528929d979ea7688a1dc98de290c81521116511fa422f42ea9191d9e5e3be2e2549aaa2488a67a79aa3f791e2d8715cbc10794245a5c98d2a08925b2a82a908221d657981242a41744c3d54ddb63025c931dcd42d5dbc58d948e0527e9d48aa9ea86e1b8882640822a16a1721d42da5f78527d9912adaa960f7719e96522449905bc7f41b3b70e440ef871214538e23bf2f14cf73fcbc354b9672153ff25cd1d34b3b8e53475145ea62a629a4a60c40406348e58a1096a17215b951354593fb4e6f43c17148e4c02a1c7764a48e485cd612b094a1b9792989a69fd875a7b9824322c7a5c868258f904623a423cf5b4b7423d56d33bbf353371335cd219113dabc14d492fbb6d11341f45443b1f3d66f48e484c0a45db7658b8a7e645a7a64f771a94a86ebd6ad247822798121a472456807b4443b74e54eaf23c971db40530502dab24545b790ca15a11cce120c5530543d7555c30e44cf6fdbc1f43aa0524c371235bb2dec3af2ec56d2f4a11c5571f55675e3528f13cd30fcb122e5e7c7ca0b3f4546ea15a2222a40404546ea1044454440405cbc108922952b423f089521498a20f98129b76eebd89120952b4238f4508e9c487a1d7a865eca85dbd77d1f0fa56aa6dd99a6e8489e63a76e5c141a7ea4ec50a2220a7a1c97821d8aaea6fa9124e8506ee6c891278aa2a9189aa2f80d895c6a76f180ae04940ca8f4edef94402009410cc60000035074ce981999b301c31340502020140f88a43289a4473b3e148009327ea28626362389c210419003310c82200681200618438c31c628a418546600d0351d347048af7265740980022dbe7b53505a6db898b2c90867ac693de346640f80ce160c16bdf46cce72f4ab026223a5694ffb5587c29bd926eb1975632e2eac9c29b00020799cd5a3daca41604b6330011fca0150cc003ce58707a2a6b88a6eedf238039323fc8856c310d8a2397c22a53681fbc5527861289217c094a7afb223485748bc1781fe8569475bae391a43fa1deaa0c6c82adc5ae8f442dac8b11b4c0516c8217be50a5c1b483b224fc91cdde77bd0092a591ecdcf704bb26a77a594ea404f421c87bed74f1e1c54a12a46ac5da733b2a74a2c8204639b5f49c9d57bd181e6f0147f540ac442b5e07a9403f495d551c251313c05d90de741f853af41671f3be868d94ad9d015b5213226efc6f95e2c1269531674c0a5b5bac0616d7a670bf610c3a771be5587de7e1e4870b932edf53fc35c54c06e6abeeecefe57fe41dc118c9d12759cdc92c605812405f7c8495c5cdd8ce56080f2928975055e30d0857c0d279c50c2adc29850f11b70b256467b3ddee394720cc7682f4f0fe4644714aacc680fc8d354d00785e17df1bc8bc8f9d2bda088e9ef3a3940a243c28b02480858324756c61eb243176a269cb676af206575e2e4a3290ceefde8fe77930ff81f347e27721dbeb4e62b9260ecd9c941cc327ac0107833f8137f039c085741729edc00d8189d8f57360cc00000fc6d805ff17c1c1487f0dd02532018ea0d5195f02cb141dff614f6b1d111a0084a2b2bb4660dc4c0aa8f54331f24976f98722bab5856711b48e8612c5c254e121b8616f5fd1bb4ae6869a29c685dd6ffb804e02e6ae4c39a90472990bb573b23ac564b340bf5970459249d9513deb4e4e21a2231bc1b696045c7cdf2ce842060d23574a275b0d0456707f41515679021f7c74e9db38b148f16107ee73c19aad19d57430461fa676229bb2eae645dac0fd99674daf8eb6d8f85b5984eb718e0eb0e8967b7f104b54b8b1adebaf49596d4e01050228570449ceeeb21d4d76933c48943bf3b9c06426e356074a575d2920ee0e382a22c221d5e93cee57819099e12520717815e30577068f8533a1769c859e0b3fa0cba8e8292fad1eae7972577e8f74275992f820e2b37f14b0cfbc0a35e62f36b43d037a60d9249c9d114e182b052574aed9dce905278335593b32b3c7101a84dde773d66b870333fc18cc22303f975063ff228dc3bf76bdd9e08d0440419cfca73962bcb52b509ee5c1acdce1ea31dde2ec7b47346716cd14b9928038c849031f47e609cc5bb4234daefc55afd603d9227a456c4523096968425e808a6f8ac50b6279823a2fd5b3e70a5e102f9c8cb573ec3e203bde5de33ac5b0784afb01eefdb02f71c7d8a96b17d1bda61d99caf1bbd2e68c62ef333ca15995b6ea9b29c576ea58c651bf22f86e0551c68fca43b16c6cd8e215e34c3c3eba9856a0e07bf8d290012ed78c36f1a10a6f1c65cc00be31008e3b6aad145071397d4a017f3475b312c647131dc1b39090ee756fec06a903991b945930a9d69c3ed228597d0ef6d6acf981cdcfb33fbaae321a6d06fc3e2d53632f4c4156a026876a6ff10d849318cdc90edac3d5c906b01e994dd6190fa8ac257a2d69d13e33b40ec698cf6bc98322e3fc0b9ed4225eb701ceed684a5a2adca01370bca5649c40ec98de0fe1db6548a3c3632e7f103a96200301b0b05f80e37d524bdae5b88978d2827fe29469a01262eca2401ffa0443607383e30f04e2739d0bfd0ee574cee8f6ac9df3b13431153fd176459069cb4565fd6f2f339aa52ed68b2c1a08378abba594a486bace575b006de299c209547d690ca4200ed68da626b1c071a1d1bb9f0341f3dc24edadbfb04befc0d1fca1aaeca951a25caa205ed7cde8209907d74a8e4354a52de50c3bac15c519d4e4eface24a9661d44ec1931da103eab47ea5423c4471286aa050bf13bcce49b41b2a616f4c5f5b10582249c2ad0a51e50e0e18c150969faaf8f1c59e61b7d3e96a1839efd3e73bf00494f2bba304b28a12420bbdca3ee222b747d99f0d5bbc95f275b5d63895bd2d7b8777045c2c80075b994d35aea3f6d92292df5ceb6ba055381017e298c10f384105e595ee8a93a1e4c374b05e9e8a247efd2b45a3ee3c5aebcb4524715c851c5af83c7bc32a696e758484a030d315c1fdc775fb797bb26dd0e8c21231597c0cb7e8098943cd8e2ec231b4c74404acbe43abb3fa5b6ee94703195e7d994247ef9403ec96f088a510523a27a002547bfbdd8e5b8f21194699c322476c8ff250d4ceda120413f2cdaccb14237d386a493e9f4faa933b609e08e968cac8a36932cf0c55951beb5c8b80345e9c49c39a07d48b24791e17ec5f5c776e6a66d3d90c91029af158fae1694b7217286439abc0dc375971ff80cc1df399635403f73ce82233a74e472354093ee13f387872207116eac1b4662d67220a67016a219805092524be8251fac0dde99f5d637f5501da2ed2ba59571a007a7b3e3b9c0c6490a92c4a40cdaf97b226f60952e0988d641d0dde5e9f2d42ee06328173394eadf6f7afa845875fddc8f495373acc3e57f9a5c46f1ac549b70f031721882d4b846a03a0a519e0dddceb3f5f16f507c1ea5a75a7f3fbaa944c7ff19660d346e1dffb2a7d0524e02ab7dceeb8f25168ebb54a5800564a0b0ab82be2cf9f0c65110d04c61d30c628d6b421f809d5bcbcd1e9424b5cc21ab30e489dd885b07119f2fa0f40defe155eb2b002068b48ced2c40b5b7793eba6596c84990c756511e283784597325e332cd1cd91a48495c67805ea01baddff6a240ad4966e62e79c5d8eb166c00351962898a73c325c9c69e9fbe8080a670429fcba250a67c9c085a770194f024bcd92154092db295772c6b59988b37268bfd49624793002e442c47cc04509933d49985a382183e3e8ea098eb407a647b3d8a2a2792aae192b11c29c57032ce0a8d4a28041b007decc0e08db19989851b06fc0b8b804946042955a9088ef85be5c523ccb20eb8da9c76b0d7c93b0cd5f927e3cdc2cb1d1e4c5d7e100770a466303dc9e743940bd3fee46b8ac02b518e269ad55754b91d0d8781e45fd6a5b6cafc68d412858621b34e03d28f7e5ad7fd92fb72d53a45bfbb3ade309c6c442131e1825783d6a4100437a12308ea9724b2c4ec6c78f34175ddd438487da3db2b514d9910952e0b7d29c484b2e536eaf7583afc4e676b2a26ca85cdef6e7dc188a7fdd0661fa77110d2463799f2af0b9bce3961bf1187e3ec68336dd340e37f039d7f8acd4c67d7c49ece6045e979dc9e42214e2841b8a0efa4327d1d9e37c7033fcce2e472f52a793a1f87f842207d2148b0a67574f39e1c932e663386aa93ea56ef9f1a64affb6f4b0bf8c295c34cf3d3a5b5d555ef6dd0438319652c71ffda573304dfa5fefc8cc0ee1acb0c0e8ac5d55c061f4020b5f814d4f659217b20613629bf8bca79cb200e78228d3b5906647e92b36b6c36648811230858df148449a6c9693a4aaef18945980cea75e7148fd7fddcef1855616752a35ed56ad64b14d9c67063a7c94077fbc304bddfb2020439190266b698e29f4b24f281c7fba950aa730a079dc89f08dc9e5b80f690479b5872e6fdd11d3d832f18d11ef6c3030ca533dd92b1559da2a15b48a64900656be3572b0d96f087b45fc73ba3a3695d66609b28ea00b7d1b48f567841cb63090520c371ff000c5a4678fd27a9b083f7e0e1b39ce20a9fc7f2ac0a723e495484085d98d5f42e06033d41887738652bc8d846927c28cbf890051943e060fd8f357b23f9b7fbe484030e0a18ea53dba96a5ab83f0c4761b70fbb5147e1254bf61c804c7bc15fcfbe7c0c7abac274950c9217ded8d1f09e6c40e0910fd5d41ec8ccfb367effde77e45bd2b28b6918d4371840114c06bf33b302c8049123e953ea461537ed750eb9457ac14126a9d6814fa13c4427f2ff457273f38f72161fe3c860276ab84e58ea09ebb4b81451228660282dcc0cf97c3715cefa55e34160d35eda1651e2c5dee41df5e28312cdd1940f28bfc12bf1ea2d40b1479005a4c90f2630eec877391124e706f73d937ed855f355bd7fc3b1005564fa1cc3ad31bd13244a727d1b8d7e789e869dabbe9cba37f96f3b8bb01c86f7fd0a28d2df6cae65fee841c42cb0e3a63ffe6df8cddff84891b0af76c7e855c2f2a2320132a7340b6ec2580835c08b24cbc96f47ab44b10fffc020b811e82cd4b98d1f4583508f1162f68280d668188fad9399f44554a284b895b446d34046700fd7acc564932a4a2db2492af26a4b16037b6f12280f54811c94109c799564ccdb17a5d477a7528d22794bd31a10c21f45ca407ba00de8acb1b7b0a420c5a9da1c4b7f26bd4594dc421a4314fde56bf7a5d45f3d8719371a47f1b3f21a483650c567df12c2b78a90bcc263e3660ca58198fcc65326fb6a98d4f9f7f9974ddf89425d82db88f26d8427fc1fb86464b6b1fcd0f6246420cb152f774bcac9a9eae42ed12aa95e3bf68174048c743e35daf0895906f585cfa351df5924d6cd29282bf6f3fd5569b3a4b8a9d3e630888171f6ebbdc46e6d8590fc8e7e2b05024871ca24cbb335c20a4beda11a39628ef3965558c94d723597d22a0dd8e77d5278bb24f8a66f56ae8a834e8135bb8d31e4cb64a9594689fa6122be3aff6a56e83fb3bdca2e6c532b0c6963b129b336f1ed9d0c43f867f7c682409c970b33b3b1f9e8a160ecdb796b319eb32329c86f18827d7b4e3142d3c2504a4bc7e22e4512fb33a8613d39cd75d70326f155340957c059b3ee323d96e78901f02c874c6ff70b1ed9f92e439780e930c6323241de4cb092fc95d64b8d82484b53603dcc1d8a4e04a64d93c144ab7683341845024dcc83538996ef2af076d7c7fb15c661697d436694d7cfb4c40392a9622ba0efcf49afaa682f018f5afdc6818eed63b870ade3d3801587f319f1a7bc778aac4f08855de6e2eb4216b64b1338080d541c4e6b9493c121a40c3977b91f968d197501c3d4b897eb85ec998962ab02c009a6fa613154a8a3b9350673918c0fea1364e59767b20607d9e716e4f74fda84914a7e023481aa8f8cebfec2328f2cad198ddb8461262ce724a3d9851b61524d2d77a015facc1e66c5884af21480ba83828d3359692155165f9b1816d5aafb0ea5dd2e4923433a89eb39f07a9ff18e96ce36238453011709eac688791814e0a80938dc3af443ff8e5c8ab5cb73dbdc200781d7a069089235cd589fdedf839d6134e85268e523f718b8b842f387825ff5955457f0f8ff5aa7c50e88892dbe2e1ddef11bb79eea89d5af3d534dbd5242ac6877aa5d7b7e689b9a2226ae86c78792b4ed362de7a1e0674767734080f09f66edab87bb4a284a54ed7184acdd72875d0513df2ec5d607e751e4f7b4d6d0f951dc6beacd1ff28328ca33f38f5ca2fc07c0a77344a36c4a5ae9cece73f7f0c5d0912d40f27345c6dafb2c0f4ad062932a85422927db64b1c1af8604f95e42c1b8daad6a1dc66242aa195f978ef0e6e110caea9d107dbad89e4e75a76f9082d2bdd497f2d148d5898e6ac9dc7fb41cdd7df1ed1351dd3c8e05db4a3517b2d5ccd924db31a3317012493db9fa40866b29206a5e49d9ad99027a499bb1e58c287835c606e58e0922d59a7f8887e0a629286f391dc492968917a437cc45ce055f5dabd1e9a5bd9bf3bc01d08106b3195fb6a2827d292a114911b67390d375bb01bb8c9e9f8e0cc172777f3739f9fc28b0fe7054a02162a6bb00ce5992d79ea2c372e2d615a8061a37b5b3972390b071bd277dcd35f9756cc52847f72340fcc2c20be717d256e4f263607b5e471fce7233d99316f5ca5f21ed0f3a51517be4dd348b8fae5d22da8280d26a89dda9dd2fc44e1b16eb49a4e9dd0833a9d29c3f32368f856e7c2572b3760eb69b27481e74a01392804bef98821273ade2cdf370105268b82b68bb279889eb83367abb62edf7b7b2cb896970bef7b6131159c6a5b89b4a4e39d7f64113a9b71a84cf36010f0c5c46351acf7325015ac807098aa7f78086060f292cbb3ea31932b94238f23da00862847619f7533d42d53451d53cfa1d7a7c216bf0525b9423dce082235aa84eccd33df0b34b0f6a1326e0e4af1b5f69fd72d26b93b296d62adbcda39a103a63375b666ef20a5804247f1b0c0929c9bbe47f6da6e4fa3804125270e391a7f013c35d4e0ae964463c22585708c759ff7025953fdfa5252c855c4f627ac9c247894dba0e0b7c74ac1e15502d28ee5662c24bc3d4dd012a991be1a67ea471b805b44c8675a241628e8cc57880dc9ac30821f06458b873b526623fb5bfa965d6ecb6ae16192ff64495d577222aeeedb0a38c3290028c4f4f9cf91b49f1cbe140bb6d42a82e16f762daf0fa4becb7a00098e7a0de3d9673fadcca2bbf0eb2c009fd4334b97894abc63df91de9e3453e0f7f2ac69dd428e235e022d6dbf340e3533e94a40f507540e6e310edf01c91caad88098752eb15267ed468655dd6d34b6e26f30e56728938331f2a5ff2174e36cbcf4d449668dcbe4fd9d54fd2776f98df2b13fec9e11e255cb3866b223ed0e9e2be11d1f9cd5cd13c9d5f2680043e96e11801f0783838cb462374c88e2255815bccf9946d0028cdc5b4e3c52c17286ea230bbe894d089f1022bf7774926d22a208d110cf5adba993eb9afbe82170b25747db09acf738a963510e88c8b00f46af48f353838c66a362835d3457fa496c823060b4f6fe64f8d6c803c87f1e270be475c06d88e02af94491f6667d450a102f3aa4e1b047ab0893f2f52caaa711aaa8c91a98e82a9b531a1868cda627e9e0fca1724c73bc645efc23668fb2653036a151aa9f0371230a119fe0d9bbc6f61c9ccd0a3acc08d288e3eb1106a499b678dc3f4be7d60d286b37da86bf6d2af3f75fc2f2fd4fb3cd20154aba1300886144848a3ec3fec0f51d02fa1ff3e31750c59f07491125e1642bf318844f201ac22879a19b2ed538884fefca3f86491e16df958d3ef3f0fd00ed35fb0b7f75e22207bc7ed20bd0bb72a88b2424a2f8c5eb1dd2230ad2a5cef5b6a69a44c280b4c347b8f92811d20fe0a42ccc2e5a92ee00b0b5f10bbc49b0ba9f280dc5bdadd863c72e7ff85d887e0dab346f2dc4035f0b1b971289bcca5833cca5506680226216a6d63019cc4dd14c33cf8bf37b02115a6e620469dddfda2c119ee44329cb06641efbc14482cebdf22e5043391465b933b6118680a1a9784dd25e5033fce98fffa42129320eb5eceb75d62b483152736009b46902f1cb2533e69c260447900052b66dd102c1c397ca70402c7263c0e1c4597eeec864af4ad614c1f41b37436d83986e026a748ccf122e88c7353e76ec805061ca8624b781ee97fe7a0c40f1454c84ff893bb2bf363d0baf6d083bfcb30a007a65f19aad06a9b30751af8243173cd53dc6f9c2cc058ed29c33906e0cfa468496dba9be87dbfa56619ae24f48abaa3088a399d7d60a9bfcea6f7862c8f5a9d6d02c03f58a4b52f5355e34bb35a83c57657d9ca504a501ec036f393df41d68d9a33ad7acf2f40df285cd0094abf907e7ec6ae2b23cfaca5cb44322cfdff77d49c74fe75056fc164dcf01c1cb0b8dd97e0565f03e209c8639120dea542225ad43aba4b18dd9f929038af91c02a78b89b1000826987e837ec1ba6256619deade8b5fbb1da2baa78eb8fdcb43414e03f6e6e191f00b6172fce9bb64815f3c65115b5faec49dd49e07e960fdce6b9448fd7b294bd4e7eb1cdb841c82e493e8a81f57fc3e592b30b2fa8b0df3030f9cc7348f594c2de094764f52972a4ad672ba8e9ffef398ebf6917591cb020a62971e368c04490c894b78e7f24ec71fd43f4042386bf1f34266d8bac678923948937d947ad111f10bd5f009fe6055d7c16ebf50bfed207e8837c9d114eb0c50e09699cd4779d24f5564e92d2fe254db625191266b052affde1ab9404eef817d345e25277ec830753c720425fb54ff45eb56ddb3bcbb6635d0c128d4b3c8690c917f41dc1ddc6f21a862bcf186b7ccf543ab5a92d2978506d8c40909a6a2cccf2ac65508df4f86e3a3340007fa1217193e26f7b84b3cb14dce83c13b53c707df793560176242069cbbe01743dcbcf9001c4a67bd6b50e2f14a703b910857159e1689ed0d4dedb73efcc7c47aa01f710a1e5c4772239ff9bd255c5fd1c1a15d05bc8c935f2f0facad29acc00c3ef961aebc9e06a2a657b4fdd436bfffb26b0783bf5c1948f678de06df0bab717e9361e562b092ef2d5431f6e88f983c89b0e0e836fe1a3ba1ed8b8b497632bd49b5cbe26b4a3b9a9d429d379cc8a31add1d24d901fecbc0b4e77afcdd0d344aeaeff0384356af7e84c91f0f0d70b43d38d8059de88f3e5feac81f854b0f25d469c11a7df71e26071b464771494114c6ac2f1d82054a45b3386847fc0da9bb8d81235b22d3ec1203efac68d76326b91fc45bdf589fa4bf4eb88e828060ef9dd40883bbf7e5b6e634854db2a7498a55bd81dd381a1669a95b6b96ffa2977396e7aa12624cc142927e1001a7b98478528071b5cf362aac608cd6ce7155cd0d5ab48775c1eb7bcbd9d7749332edb3c72214d2cce20ab4225bd2c9f72b383fa91d3f2af394b5e7f10b89ce327f66e005ba3d1d0576e288122a14f0eb59b524afaf39738afaec936b93fedd863c338fc3d0a5aa31bf0933c87972b701111383ab16f95b9550a38bdd998952b0b00a64cc0518d6030b7a94c572b50e22085f9d4c998e3270f2d4d31d1474186272cef3167e8dde81e34f70423f6dc0d1d1e5d9b81af972f01e1fc4d42a3791b297409975e05351a9a897c61bfbcc26654176df2785bfd8ab79f58c64e4ee1c4978f82acc228ec4c14252994b35035dfd93eee97480aaf1ef9f8e66772a6f4eed5af2a92232b6da04b040a93e90ba8d1486d76ff43500b3fa2a6f67cdae7fb0228b978f84c3d1d6380cf7d840cb8cde0e7af9b417780efa63d00496fe837f4920a30355e62f52434520636034a4ff081ee6aa538081f2c1860a611d43d0c06349cf08aaedf2d81a43c3945ecbf003468343a91b40bd4a6c44fca95a66ea253c5e0ae2bcf187c2ac5f5d650f9a8c64bbd0c77c2e915b4f1dc4711afb9c69bc63757bd1e3958196099cc355887178d10fbda9ec7f02be66c5434f01e5f6bbd1cd786767ceb5053d83c6861bcae6274f3ca1d68a4c2af466193d97a5d5f3c85ebcc12125ff041b699a856c715adb512fff7e06b79433282a623aafa9325c9093490f8d5287882311df180a50194e30b6f2031f7153eb3065346e1db19cb70345021e415cb60a48bf44cc68d00d6afd765987395d83571ae4bbb88f701bce2380515ca181c5f8b166145067a6a30957a3c6fe063928f13ef6e2b2c485bd910fad1bb915bea8be7227d69d28d57298618d98680c5225d6fcb5360f8ec376f67688e79eb9ebb2fe1d413ee7270a3408fde6b472f04302d68703c26f7e15f42693e2684733d3af957e94f71cf91eeac90a9ed44f7dde2eb16249b5e0f6fe0087c9aadbbf627e835f5b87d4aeafa72e68851749a7feca03bc1d6dbfe1e7eec1b1b13deaceb1f643348cca099326e7d919625fa4892a01ce270599247b1398c7cdf2669ea3516fe8cefe9545f1a33ae833cc3f4ec256db50c45a45f50660d9725d195529e21a55a6f061f49a9eb2e35740099a1df75757a312100fd69bffe3a0082af1fa271e157ae822dad3a51983632369d63ffd7472d754edd74f5c2f4c5062e8c992183450334baf131cd928f3a247e70c6399eeb823a3813d424faf67523af29a764f60a59ae1bc522a34447ca71d1392522a6cf36ffbdb90f56a3b4051cf1d5c7f3992cbdd2bdbea7a8661954e72a8ffb6648d482a18b5824dbdb359decb58df5c579e5a0d03f73e4166365c7821d721d12b27f6f80929140d8c3fabccb0fd77de5d9d6b4cfffc000df19e76b168c184765abb48847464076dc8c4d52a19063830a0b27e27cf2d630c0ee194e7c17e6c0f0d555aa9cbe49096be8f141e239b706debd0033a265e9d8710e456538948d09956e6dc1a472bedb8dca15ab61e66335118250cec5796ad93ea5c7b32ab3ca403d67d25f49d366469ed328879296e9fe1a411207b2d160ef47218ff81ad554dcf03ca900e049ebd297c1479264484461931c0c8fef1e2cb9674bcbd2593a6b5c646a84f43f897286e250139550d839b4367d31ef1e7332dc1813070290d7544f5341852e0a6a87141746fe8730859f373d64143993a1d33a79ecdfaa7c1a94610fa5a968cde73758298154697333eec86a1cec9e626fcc14ebfcfe8a2457737d623ce4b89389a21d6dde26537e3285791fba518ab54ab45d5376500b32d6cb7f089846ae23719c59f89b7f92c747671ddf2d57215c9055de4cb4eef0811290be08a439e17c51b4059fda18d626256c39cce2e2d65677fb3fe33cfc229b44a39617d2e992c3098e65074f24458c2cc73f4ca4a45609033dafdea263f84cd323b6fc988b0418ab3ccefa4cc9d7f2c7bc117935e4585d34365c45afd247084372c28598f0a3df8437bab18a88dba94a7c5198791a9f040b2fdd2dd5558ee31a45c2373033683a0e2a27b53dcef8958f089a1c40794b423bff96b0d7a68001ba412d2d0681a744509e0bd851f7777a0fc59aedb6890b84ec02f2e3db040cbcb108ee7dfb74b0fbbf6831c436a8385b051b5e5bb6f0b27fbc85a0b56b610f8f23a23331236126f280080e3c9fcd5979708d31c5b47d0e0e9efd1abc2681a718c0fadd765252485b4773748078251014dcc2565cfbd70e1dd8261b9b70eb51b1a0d283b0d6153243573b974044c97d7172f4cd7b3f982ac560795e5a77262e39d937132e7241fc7e12ff4dc9f789cf746d6ff22dd0808cb6bfddadd63f78f0f667b9006129cd185d0cb7682cc6277ce51c29d3f8775eb0d77e36d5f002edd352ef0a031772ea4d12d8c6a1b6afe29fd177f4dea51c72fb2636effe37625cf2e5283cebfc0320efef9ff038735bc2beb5cf9a6a684ba557c3b7e23bd07735f32a6a25082fb4dc81cc1b13987e904ec397bdfd86e625c1824ac655c8f4ed6b83f58c26ad006e99bc48e92754fef5f682e40d107774ecd36e2ded20f307e4a7e7731608c0c0ee0f2ac412b673af6512a19bdbace86545f297ac37e813847ce64ddb5207b5517cf0714bda73f9ccdf6089935c811b779e29547786e6d7a0c0f036f90d9c6add9b77ea2fd72df72dbb9aeeb0731c1feed348f0e17122e8e69b3d7e0b9bc9f2aeda3b4684f4c49f5a5eefd60a98d3ee0976fa669ba27e711c3b1aaa08331f8bcded1d9fb6c51b3b3ccfe5b666dbf1d70f5ee94ded4e227e2ab3ad333e63e557f757dabd589e8b0c6d25de2e11bb669a992a47066303fb2ed5ddf8c7162458087633fb20dde4ee9702770398e95e6f080a1b485794c83cdabb2e84f873e4adb69a6eab334c318c46a6fd3352f4d416c569aada566907c73a8b847ab50a45d2117ce28c82b923c429105585ab22961c6dea61dbc22af5c40fb1f0d64b159d79c95b7ea477b2e07bb65636d3efb1cf4b3771df7be3b8521273650663dd602760d400c87ed3cbfbde5f5f76f2f908103790b8fd14207d4701250dca78dfab091a74623735b70b95e3f696f5d9c7563b015c6de19c29df80744a8fb3d00aa80e5b039fb4daca60b75e446fe78381876231cb720af3b5d07c5379dc5deb790b1899bb3a98b5cee51e1a98ba38ca58eaf814b85742d8bdce0b6c72cb9aa5c7d6fabf30641b8b201ca7433f0758cfd170cf826edb4c616b96efdaf9cc9725a4732e020f3cba07b5b42ecf02333f9c5fe9ff50b612cf17b69985fb25056f8536bfa227f2a2b49df355b2661b874e954f32c669275fd74c98dee866c73f29c87b5f3c2aa2b7794b9c9b3ff3e93f30dae707d95d7f4089ee26b8a3e589bcd1d3848f70130c4cf0ca7e2672c2fc84e5bb35c1d9bc3e91393deb6385f78297edf3faf7cded8f397fc2dfe91b4ffeb19cc8bcbfe74b8c36adf6b3c2a85ad256fb7e3f30eab74ab2ade91d81b3c22df08149fc5e5cd96983e63c7e687102907ad4601aaf7982a7e7a8f5d738bf4c5da71b7c12e31f0e575d6cfcea10bb87a19d8018da61a7830541b7f616b4e7bef2e1c81b26a6fc0b7a1054eb1abd2bfd0672457190ff740ff9ea4406fd6738fe259a3cecb4cd2fdc818167d2ebf196d2c0347a7dd8b2cdd0ac7335b35e0cda4859c0424a7ac687992ed387df14f9d4c0ba9f281db5d3b422b6c613645e76f97b974d2fb9a284d4be6c94fa63d0c8dde717d5041964bb7c0f268b17403a4ef206027bf6f2c7d009c76be27c7a0fb7eda3c05d8bc4eb42567a31c17f40586bedc90b8b71d0bcec7d0af90a3b1e19355bb42da50ce9d0a9cb25bee8d36cbae95f4ecce5810cf35177631e33f3d99335f739e8ddb30c272bf01e4e3de0e93ed0287b0976e6c49913cf9c3761fac4a973a7ce9dba55a67622aa57ac59b7703c1df119229eb69ff13dff179f48d3d013df82fa88ff981f44f27aa2d38d8e272547c6c8c853be48cf2573fb78d7e3d0c9fdb5c2fd9fbccccf985bf3ca447d116fc44cd99dfc7231cfb6cddafe6ce0773696cd212477ff0cd9abd494c7739574bb9db15a1e45ffe8c9f6335e8befc7f60607f812e109c4fe722f2f05d2446c7f50e9f53b32bdc79f05fd671fd6fbea3a9b733fd56451f8673f3826cf6b7687458bdee3276e8f8c557d6e8f983db5ceb161c5775b54f95b2ff1bf3ca1b35d0df7f7905f5e60ee624a7d17f5aa6da4defd56ff2dd9b5729a269678ce88993555b382f6401c76f46c3bb2fe36e073f29615291c9b3490e8ab18e7255c6c60e66a48407018193f81f18f8bbd49789a1e7b1eedcdf8f54f0a56bf2bc7fc4e0fb65eeced8bf2b0b29c5fe2549b415a1feb342950a847cffec9dd70a28007065f0f9f22ba189174c5d712b4c3cfa35760f81296453e70cb12ab75af7daddbbee0f82ce130f6b290ff9561aace7fedc013f854ae55bd3b92ff2ab44d993c54f5945833130bd39fde2af9364da7e5483f77f4449ae05f705ce3bd8397fa0614f78e777865d4cc065e071431d30cedc7e4450a0a5f0e6fb62c1e1c15e16b6ad789e7e34cbeea0972657cd23b1b117d17eb5e09a823d7021ea3d0df335b1f557e517dbcb083a712d5d91a231faa42ea287d194f451565fe322bb880d738fe2a35c18439ca1ccff5b2a5c2df7cf5006aec8685553326dd035c15bc3188dd519aff783e8fc88c50c42f79be4f91c22f219594e01a4bdb33d9f59ab5cb1c3edce8dca388fb67a4f541d67ab688acf7324cd01ed7dfb883db5890f26ce4796ae246ec8f9b32e0d03926fa9871b5b4ee1e5f9a1669b3b551732324a323726106750c8f5b6f137ccaf654afe906f246d3733989e7f76f3f38bb5db8e0ada73edbdcd2102fab8c6af3fd662ae08940df371d1e41db039cdd0dfab7f178bf7c813b7785f7c25fad353808756eb25ba89dd2f22aff74d62d2e76b6363303988f098dd99bc1e2c164262c5671b4a5e9a2ce0644fab1612f49dae6b7ffe5b43cae7e703d8cb667452cd379551c6d69081d3ac5c07f3cf361c2e6df9bd63775da2fbdc2449f6b27d7b535d3e4635cbb22cde263cebb0ea3b56fd146f96adecb3ef0da241ca735e4086fd0203c909eee011d3993ed9af56af49f6eb24b4a6cd970cb462dda376cdab069dbb4fee1b740298e17fb4e8cd457c66879030cd159398669cd8f83b895d21cfb9ce1d95f9b1fd5073085d10e6106da7f29fad93dc92874e3e7eebac4ac8595ae4a38b8b62cddfcf3250f7b22f4adb6a6ae6ce625f0fdbfabf970054e8370d7c4213bf1fdbd77cfd4015d49f3d08bfaed3bcbedcfe335c59e45b75fc7cd507fd0cb4ffe0db10ada45b7feecb3239cb9b2bdb927cf5cd06ae4e3a898f4efa7f458e949f3f242b887ec1a92cbc45b2dc6b677f23d6cdbe929dc350c8ee33ef4536139b8e1c1f228fc5bebb6db819d1e2f0e1120fa87eb7601ed4fef715e7a86b65ec60b09ec593cfbcc363ff73791fa8b5fc569e21eb9487d63313b8579f27a23e20e4a42f7c33e4f3c0c50770f47b2dad13916f03a5f1be51cc0ed8d185d1462ac759fd6d28679678487fa172ce58d8ef28d794473ee246ff0d04e5d999ca52d04770a8f3f8fb33f80e64cf9547d9c04b4779a6038a607d9cb10e978a269c652c160ed356d0b86711e36375669f3ea82b468fbeedf9db7c38c7b5f296edd947343c73fa138fef4ef48e227f0d6e55909bf3b1d16d9a00f679de161e8a16fe4cdd47433bfcd7ea31326776e2cf09f31bbd8017a66dcc271b729e55657f7f08f3e491ba8738157f6bd864f0577c60684bbe6cd3f1dfc2bfb543ae8e308f2c992e3189d1be275516a3746f646f47f7ccac3cb673707530fed1991e0688b4bcbb806cf1357babe8e131ff6812ce439f699a87909be67156b3065609248f644415b98fd2ab07532a7c02ec23fec9a57fa297e88b8b310d439188f9c7f82f5953cc7c1ee874196e80eab5c4b3da3fe1b1268e1d6b87c64cbdd7544d7eb7682fd3ba4b3e6c2930610fcc28fa59ee9ddf51e709c8a0d0e376da4f3d74f864bead734cd9093de3343838e622bc3b95a1c589eb1ef2c3b8c6f2863c731059e2fe56999c8509e73c0c2968df5110566e29ff08d1dba8be9037137c8769e0d7fb685da8e12ab8f314b41ee0da682155df356703bc682716f41f7bb20a174129dff3188efbd6c77dd21123fa3a4ef7068cfc9ee9e7724cf922061c6983907874e970d60fa42871bd7c78b51f78ee2829f0ec484234fcd75ab65c9295f40f20654f20704d66745f78054ed07755f3cfec690ff1c5d1eed349ebfbf249c7d0ce4dc354de8f5c1dd92fb282a7d9c2d0e7564187997d04de89f6f47f19e06ba0aff15ee2cfc5bd0bb90fe021c067d0c7919f633280fd6e6b0972793395bf903af4ec31269f968c885d5717a5cbf3ac141ecb655eef377f2348fbbfb40f8ea0b520dd223b4e38cb87594621eadcda8a63b74af2888354a8bd08e33e2b6a394f2603da31aefd839f635e46029778707e061655aa95dae9c4bb514345088f001860c1b3460d0b035389617e87526d679444cba4b318b466657d315ea942037799c5ab7a8ef46f6ac17a17de690c86a284f3de91d3760a339dc900e06fb359ad7338d5cacf84f4c6df061ca1ffdd466421f9c2df6540b180cdc7ad635539ccdca0ca0f22a9bd1b20d5994b6565f25f47b4f05d02b2a9bc518994d6765a12c348f2477132a79ae9f79ab1efa0096960d807d89122fae382c62687f8b0ea9c9826ebdd1ef9f569870a0a30f361a559ee0faf9ec112fe58d869ad19a5bce9b2f9291b59e28c2ad16634b9b80d8c8dccdd99c751ef7c8d8ccc549c6afaece59fd5edf56b2519f7cee41b14393568649ccfb53726f92e6aac1ea782de58563f77f46aefab062b3aa8b564f50791042ba88c47672f3ef2069d4166a89db7aa3a9a12938d556bea60132fb0f64fc0d9a7b1b8f770bc94902e3ad7efd713e57be24d9c7ffd7ec3ae94768a9efd8633c301fee14327aea10877d8f437de36625dada0db4c369cca3c7f4f772149f9eefd1d149ab1d23cca3ece25b4ed2880f02eb5b21069f320e3dff32ff0dc335e2fd6ad7ddbee0c41fe07da436632277c794f0c0f0d4d914bd6ed40d74e774ff080bfb73b8631f67081d9c53299bd3cdf475e0a6e738e6bda684d1a2f67c1787b1a3327233e3491e9d1ad513da067834cb6d4d01f96dc4601fa9f792cd0b37e7b9e60da4ba67fcbd798307407ece950e5ac262c734f60671b898a749bd068edefa26e8a984bfd4b770e82aad1c16bc3a125f3d86948c9ea236e832be5dbdaa70f4d3f914fef773b13fb2ff5d184795d591dd9e1c4e207af7a8add6544341fad4300a5f0d11e962c38fff61c36fe7b2e1dfd1e5dc9f750364f3e6486de178977313564e4f78e1eb45030d091dbb99b81fbd89de11c0f31ef02fe6067d9653566f7fe1ad97f16b7e17a72121f4532b8bcd5e9eedbb4fc6f31e34aee558c5b16fdd343f980bebda0096d20530fe36927ecd39d77436e8253a8d4199905b32876f2b5a929b9ba0d7732b74d2c791271eb47e8bded1d5f83617fe61f95ff8a0ee36176462614c6776c20f2fa4f2d8749c5d4586b076fc2680d6e4c1af3d19253349e36ce356685217eab41a16de49cd0152e305a45d696d77f1cc61a368da0de2728dd156ad6d6ad9f1a7705b9b4287c749a241c9c44cbe775694f3ccd2ae3a29ff75ed0dd67c966d94b96d28a3dba356f47c03e9a8973eb1996f64643858e96c5ac2bd363baccd480602487ab5c9a57bef8c9cd9cc1d79d2954726685237ed9175df532c22e37be86d3dd2d3dee14f6799fd4a7227b236079a33f1652293b88df19cd9eb9690bef170378ce69ae97232d46e3bb2744b0c2c522f5825b7a0eb3f560a19adf7f1a7d33655ca9a3b7a20962bccf9a245e6442fb7d74c7a75c6e5b06cdb4af2b1f7618c99e8576d86eeb3af8cd36cd1129a1d887aa0235bcab2a3c67067794e405a87925065b8172cf5a56c99a9b98f4ef4b487b22807cd8b423e9b0adcba3971b51206abc927ae490ddc2ceded86f3ff4bcab34f143096d02d2a024fb72fdefcb76811c7afd45d58fe89f3223737a5732f023fdc9dde5a2a75706465465813da78736dca7a68ed17d3d00ece005d92ce0139c28c42e7ce0e053dba4821ee6d93e3d8be2adbe71c99c5ab7d9955473d3ba8d9818557c66caaf51db1fef99a75993d73a054596bc414ee762a19bbc69aa64aefc309d6bab9a33326ec24c9d21d9d84991ff0b89524579a8c5eba4589bb5e5f6a9c6afc93d277527642192acdbc888942d3254e6929039350ac2f318d1f5aab8d0dc524317823b3c8c68bc848ba6f7a96893bd91a3e779496887b5c40f2defa7031e5169874597bb91cfa8df21f046443fecc2b43ae6d60288a19cbb868a9cc19aad6aa8536c57d8bab54f73f94a5d90145b1063b3f97fec19d65a969465cf6aa5d14c73a9f16ab672916e1cb8b98a2d4a61fe14cbbedc740ff5ddd182e799f448bcdab964e447ba7c04d52dcba368d2232c2d2225c56f5ade410333cf2511bcf95c6f39c98f6709752df7831df5c0518ee336257fa638cc94a7261916da3463734cda09491462a7ad7f69d312d370ae8ff072e876875105c296606855bc6261f7a430680ac7739409ae75d937f457f40bc09d9b053133aa3b7474f6d2645e8e5c546fc83feb91faa8fefef920b0c820f5df018ea6d92d25271a563bcdd81d000ee9b024459f64d853536c09906e7fc76a3e5ad4c4fe9ecad20fb7461643ae95a03c284662dd53544ca9abd9b7aaf9f58348d8994e23186c94d6c4433694b2511a2512a57d2168b2b5d8b067f77fa4c14f1ab83757dc523a198cc99f0c4fd816b2d1ef78762fc92882dcd1bc76f253fcfa07a61a612c9d07b9da12c3f159e99ccc2a1a7d7db24091d4dd829390140fac86ed9345314e6ec789a64df91de8bae3b92e0eb094ceb69bd6cb1f3ba3b06b27922838d62fc4b2c4cc9a155eb27c417511b05c9aaaf91919f91d8893162b3373e4c62c122f14261af6d4a325d86fae1c9fe05e53c32d1f9cecec798a37e28988a53175b831a81d41761b60cbc96deda0e509d45777c3ff3070083cf9f667739b8a2763abaa59120f8487f4ee54e2e397e3a057801d7ce87489bcbbee3c64f8fe6999e56b665629dcdfa4fb211fb4ecc745e6b454b5173fbdae5e86e652f1edeac05ea3c92537f568624f1d0feae19ccc60200e04b20d9a80ccd9a9d4c4f30d52fe0dc43ee500cf3b09c765d4b78de4fcb4f0c47f9d2f740e449e0d558e75c46b4593cee428835c0651ce4518df24193d4f4e3d485e8079a5a5b95421fb0fdc2ea9cb689cccb7674978ddccd83135dba9c71e610355d2b701e5392b2ee67929fbe90e71787258bbaea596f2e32d23933b756fc101e4aecd7137af1d7e6243d406ebd5324462d40129a9b80becac4d9c375f130aa665d8e4808be7a5f6e8b55a3ee8cfad26b7696b6685d1a64d82d9f44a500ff4b8b2326badd96e104c8b0fc4de6b6f6a72b94310e1dbc5d77f98fa44784ec2250253e17bf5b5d559b0ca9a06dc31f50969764ae97825880d595677858bfcffd7387bf62eef8ef843e3ccfdbfb8f0d93f19085873805b481f3751ccc755c19d14a29f793977562a1fef4f3fb6434926c2390038cb62b30ba6a2324122366519297bbe07b75ec0f99ba2473dc8e7b1cc83f4d2de58154f84fa5bbbb102671e631f214b605e4f3020ea2841c0c3c60b46439330f0f0f0f0f0f0f0f8b036e24596413d22ad94d2629c970b335dcb65369c4cd24534a29a5247607efc2eee01dbc8377f08a209becbdefdf3f0c010cb60be711a9b5e517cc276a8557ece46b2af582c94b25a17290bf0b26155f2da71551a74c870b86f124f673be249e62be05d397db87935366976d2d18b353ee967f993a979805e3df2bad7527cba2f6c28259bade8688ac6d71cb154c5ba6da673f0aa52e6405a33ce5eafce4cc6e3c5530b8db79fd0a7d2ec3a48261a4860cf95abf821a794cc13052a687efa465a689a5600a42d75b5cae3826f62898eef5ddf694502533858229a9ced676e923f27a8229d72b573baf35373f24d709070f279873d4499a8e5814192f748247134cb6b2d24b9cc9d1156582f9821491a69349957fd2259852963a05f76e5309c6ef509dcc95a9602ef48147128ca22d6d65f6f9040f249872abf152622c2821a48e60d2f2df565dfda5eda487118c755a54d645a55bbaf62882f9eccc638ae7a0a7c41e44307bf42ca4922144baa80cc1a473d433a48a3aebd196f4c88896427808c1bc6e41568a7d10ccf69ea2b9f6179add7900c1d85a43b9ce9f4c9abb3c7e60707d957715ea936c95870fccd2e3a2493bad1ff7cd2069a4e1d103b3ecbd0aada3b00b5aeec10363b87c9bb455ff5099c70eccbde5a273a7ef26521d982c9b86659da32d3bca7ee09103f3ad7cdc92cafb7b4f0f1c986b47aea7289f1b984c69a83d39ad83ce52363086d6ce0731dfd2c3af81290a5de16d4777060f1a982b990e25c5f4eb67cf0ccc6c2957e8fd2a038349a54e4356aa60f93682470cccd9d392fcba510b6a1190e8ddb871705cc0051e30300a0ff15e734267d9b2c70b8cf1ab525d07153b6b210f1798b55b141e46b9e58f751b3c5a60564aa8399523f36081495b4646a494f244655678acc01c3652e4c34e54334e8a023c5460d6c29490a127f79d5ba930aed4496d954e273ac90c5418572b5151bed6a62c770af39bd653f3d11095f31da630fab7281b3b3331657794c2a42aaaa82a29f59c528e490e1b0fb8d1410aa3c8b938327a334e748cc2a0dce5e870d2a59fced0091da230bac9a6ae450b32f2224304c78c93c59f9ce4f89313244547284cfa4ea950556bde31280c42691342a8a936f1c925747c42157755181b4f7bc2f41adfe1f9b459ae64278cf28354faae79b1834a72e3eea48313c6f9915df53f6fc26ce1c7a2de11b929571d9a3028f3cba7464de851c14c9893f2f8bc26674ecd0913e6189dcf7d74f6b88a5fc2a895847aa79dbc00c1af0244b6b02201d96109e36db7d8d5eae91ae238376a98a4fe3483060512a095302511963f29113a77669430875a95bbaecd8267a592be1b3552e2c0822761f63c26f7a5676562250983a5cfe1225554f2cf48464678d01109f3c9ae15fa3aeafc9acb043a2061924a3fef7fd63fc2785e59dc9b7ca94d2839c21ce54917d3d9a216b5f9a0a31166d3726496ade843e860847163565b16d7734a7e42e3497098f45ec7228c264c46c7e71ea9d456d287c384a46f0b8ecf3143657428c2ac832c792df5b2bf1a25a218df4823c2e4bd2ac44b57897d9fc3c649c702056f9c1a7908a316a9f315d745e52a49eed8c081c5dff8620b1198e0c061a20006c01f3a0c61baecfaad72cfbf78a18e4218bf452953ad6f51c52684316ca4ef892d0d25bc931a690483306ca7a8defff00e211484499dd69ff1f23a9be94bfa484c72d8161d8130a85ce679d9363fa80c011d80306f99ab7e7edd4bb3fa83f1d5cc09adecdca316ea03bc28a14003d80f667bb114b4ea85116d25c064d978c0c9c971d4c951eeeeeeeebc34e7151a2011c0133afa6056ae2a6b28bbf041cbea021d7c309d8a5f3321db3d68a93d98d2a9c8987d4efa8457d27b397098dc18c15bd1a107d30aad742e579deff5dc9107834e42fade9f18cfa6462634b820490874e0c1f062b24bbcf8ec204a9206dca0e30ee6bdecb2294abe5bf0edaed00089093aec60b4a483ecfef78debc9840676914cace822999c9c2b344022828e3a986db5ec78a76f5abea783e994e5952b4c2cab94e21c8c5abaaf95b93861af2f699683b9557bfa96afe48e854746ec74d0110763be4aebdd1efa1d5ab8703005d7a77ee282d29655de6058addec2f6c2f977b2041d6e30ebcb5c2547deff5e8ac6c1d106a3f9e894f561e4cd8d3ad860d4f2514635557d3ee91acc3116f684ca42865a4e49a7a0430dc6f8cae29ecafb3ddd92bec542471a8c2955d4aea1ab957794e344afd08106c39d4b1d74dfdbc9703bce608ab7162b6f9810a2c20e3318dffe94cdeb747172ec59a0a30c26214fff84caf25955470683ca1f5dc839e9adf753d2e7c50949768cc12c6d45c79099154dec2c3ac4609413a65cc58c23c78d8e30982bbeac57d915a46fc5111d60300b8ba2e3ad48cfb24dbbe8f882d995e7d7494a0bea73f010f534d0e105a3adcb8aba4a27253afb27dad105f3bcc751e9937cade22824e8e08251e924e3c2e8b0132e43945e58a1630b263fa13b89d616796154d297a7a9430be6babca7bfea727736b01819b16142858e2c98c467c92d3d3ba3eac182d9e5a8242f7dd2422b47848e2bfcffa71694593aac605a17aef55392de328d53e3e060c095a1a30ae6d75b97a488f3a49c417272f2890a4619ddd51ba232d49d5a818e2918f566bcb2ae731d6d96f4dd69d1210593cebf341d73c9fea54047144cda83b61c75aca8df5fff6b64646484e4040aa6242efabeccc495e9559f60d61815d54a27e9abb5994e30d9aca99696f2d2e86047134c51448dfa14cc2c7f8e09e6e4aef5ecbefa25b1e61a3a9660d2d17a5b285579bd6549e3e5f9073a94601639f55245f582c60d1c5e9c90281b5eacc7a187818e2418d34ee46a21df2c6bdf8b363a90605233e579a23fbdcbb4a42f470d1c58d8781c270b921a69042c878e23183f78a715a52bec5436be140d0132a0c308069b137add2b9636d1318e1c373a8a600ef3fc38beca6cee238239fe270f1d2a423cc5390493dc76153d28132ade47219855c79cb29c933a8260542a677f6e3be5df4240307efeef574aa9e41efa8e1f98e3c8553e42def8bc4e870f0cff42c8d0c2c7b2574a491f89892a1ce8e881f1f5e5fd53572bc5b73a78604c9d841ef1a6337d2ed4736163c6e20e0c17b4cfeaa45fefc3a84307c64fb18465a166eacdcc81f147997af8aa16db5f070ecc292a3b15efd3a23abba42f045d90acf33448d4021158e7ad902192c30b098c8ce0f82e709c1aaf4cae1819796562030370878e1b18c4c9d54137742a2d8f0d2e669cbca0c30626e51d3e58d0a59e4d1c19b91f74d4c0d81d35fffe835ef5621ae7739cec896cd10013195cd898e1f8cac4867e070d8ca92dba8a0be3eaccb166949c90d83099b1d8310353961d2be6ebdc5298b4430626574fe929aae5d022150373bbae53d2a2d659346d134374c0c028365cb4d5475d2e53257dbb828e17185f48372d3fdc2913a792be1c336acca08123c7898d198999261d2e305f586d32948ab72afa46e868815985fef55013cba1f5383e87175a88c810b1e204c78760c602161638be24c70c563174b0c0f8e53bbf6a313b95540e111922225b582172c549023095aa44c70a0c7bda4e8b1a1dc5aa53870a4ca67d173b4ad395334a853956ee0edad5b66b6f5161caa12f5cd651a99c423f85b94ca71e8f59a7c46e728324c749394c6c1c8a6cd1801be71d202243e449308302278bc65385290c32f4c5128fd11b6f6415a530ad694a0ff33522457232c34ffc115590c25cca2c5afcacb592da3061f55cd838255850c0640b131c38ce152323364c6ad430492427558cc2fc36db79447b96228bc2a89397a9cbd794e25aa130ad4eb6b1ad464b295450184ea80b4284cadf513d9f30ad6a099de2a97e3eea09a38ed3d2c45ece4a7fb0130629434c77e4a28a25e68441dd2bcf22f46751dd69d038242529c74123238b882a36615057adba29b7f28585015568c29cbfe4cb9ad4f19c5a15993059965a895613b19863c2ecf16a51e5be945ef5258c2a5c5eddde9676334eec401596308cdeee278fa55045250cd2f35dccf7153aa7b2a48f29612e694adde794c47727d915aa9884410a55391129a45a9957210983107316a5eaa49256ea8c8ce865000e5145248c1f376a94ea5c22643a038da00a48984ceb84e5a4c509b5aa47986545876ad5b0a42f4fb95c04aa7084599e88accfa95f95fed89871723c0e0c54d108d3cab4972e9f3d33b42a1861562adea212f922ef4b3aa86211a6d515ad4eca9129d58585c8165688e43031d182ab5084f1cc72f672215ba2748930aba8173d4733f5d3458459bc4ceda9cf93adfd210c2e773bc84e51c5c7cc10060f2fc654f4b4f37faa2884517baa453152a5dfb6364c48ec8a9b2a08617629d2bb4c8eb66fe920ccfa65ff8bfd8230767239df279e5dda2510e638a95e0abd1555b7632309d9a24d5c0208128c97f3aa5acf954f8714821cc1205bfbe3d3a5e58e006204d305e1b99229b33c17562b82e9c755846fafa9930b110cfab12f4ae17ef3b9dd00640866114a0965da3d6b6d219834ee4babce7ecac91204c32af962f6a7ae6d424030c90fb92585769ef9b4a80f407e6050dbb1a9ffab773bb301880f0c5a48bdfbdab29bec9c1f80f4c0302b5369cfba5ce854391540786054f7b1f2c688d888d20e8ca6dce58d7f166762b702880ecc1d5f6f34b6c402480e8cd23f5816cab3bf833e89008203b34ada2cdb7f52c13be70626a5ecf2e90c7d424f2d02880dcc2fc2f76d4bdf493162442780d4c020e7857e76ad742eca6fd438394ebec68c1302080d8cab4e866efa7e874e6e184066603e55ed526ebd568c2e03b3d0e231ab4ce81d211303480c4cf1546ae1af4d69955fd601080c8cf7972b9afe3b31957f81d1e26d78abd1a182bb031017985f74d393129e73579ee45000d202734575714e25a53f68e1165688a8018405060f72ff4e5bd83addda14405660d67697da4548e9f15d0e405460ce7fe1cd538ea142fb51f84885f1b427fd9539131b5a5498d3fe64ea4a42ab522aa730870f5acc6a791435a9620ad398bc655d27c5c505abf0510a53ac20669530e525949814664b5be31e4a2bc52d0b9bc2c7284ce2e7f5996b2b230b1fa2308e4e69a5785928cc4a077f25fed427f320509894c90dd5d174c647a54f98c7435caf5e4eab6247307c78c2e029ea0f235a76c228b255ca9f7a5db81c4e98ec5f978e8fd838d1da84e9d5a8b113f23aac14e262c609c995e143136633d1d2d6c2ca5eb965c2b842ad788eeae5abef31618c17426b88370f17fa91914b18352c278dd3df59d29368098392972a972c6172ff8a6c6185c8c8c847250cb633aa2c9c76e17d213995c107254cd9b5d84ea6bf43786912066f537bba535239179284297b6b559e75d87ea913912182e367e4b5e12312c66db1551ef2734ba5b938392860c364860f3e2061aed75a1d649b8591e9238c3fa693d7df99504d86880c768449591297cf65e6a78a8d3087cae9adaa839d698f11a6d723c3b28d6d29911de16311e6539ab11ddd83cc17528451a97eb1f2737b6aa824c23c7e2695d0d0f89223224cde991ff651a5ac750ec9f51b3e0e61b27cab4b5c12d3163d8630c95cd0f64994be284334283032b226344e8a0b3e0a61d0f9fa44c4a71c634c84309ee9aefd55ab024464882440240b2b44d88b8f4118c34e29aff53c32845210e617723da6c77931e705c29c1d2a8abd6535ad5c4018e33feb2ba9a62fe9fc1fcc5b42bd4aabdf4c29ad1f0cdae6e5315bab145cd507e378d65f91f18be1ca0f7cf0c130a273a450fb56775a275a8c8ce4b0f135be3837481885f0b107c3760b2d173deb984ad683d9b3f8b2adbb245cb43c18e577941ec5d3af765ccc0b1f7828fdf76aa91e5e2323232324c71f7730cae9e7eced0f774a680b2b98f06107b3d21d3379ba7f3ef53a98d743659db6ecd257490793744b2fb58abdbd2edaa1163ee660d4902b7264d8ebf8580d3ee460d09f3aa5d54f4abac9e2600aab3d69279d72b3da427243f88083514fc8fcdcda7469e939f97883299e56fbfde2d9ad633798a50e2df7171d2d556983b9e79508e11b3ae5206a13c3071bccd95c2b1d4346885be51a8c5a3fa9b111cbed42c8858d19a806a3e64a37f16fa7b3b34c4c70649177e3a47bf09106c3eaeaa496579bf2180d0615d256a7de5b2197543ece60105a84eabb089d524a3a2c7c98c1583953eb8c8f7557e93298544af39ef5aa71532a32186ed545c898b07c523606b369e1492f2bcba245c52706e30b153d69c9abb82b2609835146a5eb5367ea659a9ae44e52f0010673a96826d685d6aaa54771f0f105d3d6c512d3975fb6de0573f0e105b390b1cf2a7d94ea32ee824946db5c97947d379b123eb860986df7fca4eb53f5fb838f2d98b7e2afb29f719ddcfb935392457ff1a10593947bbe666b1ef54a838f2c98858ad253bad2a53b4ab1606e15ed5b3945215b9eae605c7d21435a5a0f622392e3c601840f2b18f4d89790aa171b4f8324473a74c047154cc964fc4ba524e5dea682416deb20d5d86ccd8b171d3032d22477b2370583b0a8bfeea2540a26cf32be3cf5e5aadb3ea260f04ba3df95942beb23281847cdf5a3eb14af35a4616282f6090615f5a9ff1cbd57697ba1dbc587134caf665dcd2dabac433dc04713cc725b9e3ce92ad4954b722df8608279df73485d1b61e6dd271b265714e16309c659ad822cd793cf2b2518b5541461ea3283e4ae9360d62e94f417efa8a3ce21c1a8f696d3a4fa50f07104f3ab27e92d75f4c30826bf74faee430b731116c128574ba9f7729a970811c1a4a4d0290b95a4e8c7ec63086691afdb5a79a6a310af5ea10112087c08c1e84aabe5a0bbfd08023726abb4dfe21f4030896727a9e3c892f2b99293e3001922bc6822822d14b0802f7098d478098c8c2045c247878f1f183b6cb8f4d341c707edc307c6cc5897ea6941ed453f7a60d47a5fc91261a3f46debb538792e66a8919134636444051f3c30abd737dd74d579fab7437cecc0e8655a29214d968df498244017f1a10353f0d1d7325fcbf3515bf1910353ced1c5c377fdc58692101f3830aeb2202ec2b3cfed5cd2473263ad1b351ef085024e4e4e162348f8b881d93f8a7019bd1cbf943e6c60ee15fafbf5a7f9f0aec6127cd4c03c5f41ece6ddb3ad94b3e0830606dfdbd529a5fd9455b632b0109121626ae030f998813947e68954f2fe908129b69273a56a84e8942ae9d7c3470c8c2a4735bd4ab7e2770e068693a3cb82109e17185329aff75851cc48f9870bccf257bb70293d2e97ca470b8c3afdbc9f162b3df98b64c6c8887b7bb0e18305a67c5acba898253fc7b102390e0d068c8cd8f81c390e8d1d7cacc020647da796ce426d941f2a30ff8e25a1b6b3948f5cd267e359e0910a935211dff69febc40aa1c21cf425532e75927aaf750ab3c7502a87a6d232e71a1921394a7898c2a8b5da78ef97972fa74adaa314c611d57efdabcab556521845bbacc925757e73de62e1310a63ddda25d3979d063c44613859166564abf55f7128cc6fba5afe8828754f1fc0031426d5b9214d9e0e91d57e17e9e0d882027a24572e121e9f30a9a0b3abec31fc5f7a3c3c612c0fb22fc5aba04eea3c3a6178199d54482ff5b41c4e9853258fb66a7e1677b50993ebafbee0db31b4d7257d232324ca861788f0d084f992cb3b3127f75d2a65c2a0f92b5669a595fe620f4c182eae16f25a659955a24b18bbf3767ccd197d5797b4031c20224344013f03c7162323232324374ed0c3120655fe76d95c9f47ad37342cf1a88449ea381bfaf4a7d8e77b50c264e2d674daccb7acf54998a57d52ba656344ebffb9f09084e1540a9d852a5f21b416071e9130f6de5b7e991647cd08ef7ae00109737493de292be6694b7984d1e3abcfe79fede97384719452714bacd2a31106576aab837b8b9552278706498e19346e9c9807234c5f72e567517b51289592be2c5ec81011c9428b2cac10c9c20af4588459f7e3be54e95f85be4bfae49f012323fd858722ccc1dd92aad0a743b94a224cc2a4dab1db4ccbed418451f3a33275358f52bccae310e6942f225bc507f90f7918c2f0299a5c71ba97f4d90d8f4298a54bbbe0adde2d9837bec851238d209110a61fd93a27a4b8961a478e1b09028f4198e45edaeed43d8bc261b248f24e70fc4a18e85b1e82306ec58bf6e172d495f21a0883d0babe9576a975769580308a16d3dfafdd6486897f30e7f3dbd2ea65f68cce0fc67f65aa63399dfc677d30aaca5572da423e60de8851df7b7a954287e86ead073cf660d2b93f846cef90f1427a306d9897ec5341cedf9807e388be5cdf2bda73943cf06094e24bf6e7adf8207bdd80c71dcc4157b09896e58793d30e46213b5d5b98bf9d7a3dea60f0ac0f52cea3c5fccc830ee6ac6d5cd5761e35cae5310793a5d5624ecfaba724f49083516967f175f75a6e8d10e360ee8f6d1d5d76555c25b782071c8c95ff44d484d4d1dace7c835169fdf1be3e280fadda81871b0c2774f62ef9524ad8db06b3ccc7bf2773d5e7a2071b0c3f6a9536d5dd1acc1b27ed3fbf6bd3545583415f165ddac4cacaea330d06d392c2830c1d2ee720a2c1f4af72cd5cca239ec194c543bd565e4bfa4f33983ea70c5b7996cc3218b4baa0636aa15daad749287890c11cafe39fb23c42ceca3cc660123aca0a35e7c2550e4a31185e2eadabec327514157a84c174a6765d7e4e3afb3c3018e795526b7f0f04b3a7b99cb3c5b99cf2873808f981f9e3c717a7553ad7a286f8c0f897d54a9553a82c3fbf4908e98151cbf90a9e82369db287f0c0a0f58be5d7e97d85663b30aad6a7f45e0cdfae5507a67071a4d459e3b63f290706255bac749bd55fb775108203a3d2e9166577d4ab7e1c197142c80dcc314d8b6765522d688f0d4ce97b5c43fc43aa14cc42480d8c6a2efedeef6ca7b66960deb5b9fd9c55ce123a928b4208fb233a4a05219581795f56d0b1b5ce93150b8981f983d2d5ce7a4b40b9101898578a49bd2a8dbafdfe05667d79b2b30e2fc405e62cef3bc999fd18ff3f03720c212d309ab05c7aa72b8bc53b8405867b5f5d5be797cf43ab82901518e4e8f7d22dff54ad8e21401784a8c07ca73a3bef05fbff12622a8cd973fa39f6a70e1e23a2c258c9e5854ab942bb3e84710a831c7df6a6434634854108715fd3a73653b9104b61d29442a9a82d4597f2472485b995a9adf4ec4cf55288a330e76fad9a13fb21ef228ac2205c5be564f2e93c55324361922d2b4b37e34d970a0ae3f927252e9e2af313a613ebef2e5c56506a677ac22452a9ba2c6d2952290d46274cf9dde6e49eeb2d29c54b80c10963ca11e293c678be2894b909f388baebaa88ec97a90993e9fccb5dd99309a37add5acb351313a67cc1d3aaeebc07b9332f6132efb4f7d7490919b719018625ccabb7b3ceb27e0acaac84f1e3c9aab49f7fc5a59430e8603ac4985e17df0e8c4918dbf6be820c4f12463d42f7ca95a274bc5f482e6300231246d941b83c9bcd0b1f42c2686feeae83d4d2c95c188f309d54faf757afd6c25f3b80e108a39c1cef7c426bf92ba4018c461874bb106a5df4ae593808301861f64b7acfef5f949a6d0a3016613e5991b352c592685714617aa9e5e715fa55894b521dc04884416a9d54e547bde65a8908c3fbe9acc22e7608a378e5902a2e27af1c1ac2a0a221ebb37811132f8c42986407178f1fd9a36d3f31c962083008619026ef54562fdfddc741987c5cc7bffdaee897250883cab22a6e6a199ffb0b8461e75356aab796980c0883ce799f9e53e54e1efc8351c7be9a52e2b1b458fc605e59dd9d3f7f4b47b70fc6cba2c2ecabcd078334fbdf96f3a0d43e2c7b30c891afbc724e798f3ab92b020c3d18bb449c54e74908755501461e0ca63bfc2b71eed2ddc3835929ed9e5a47f9cbf512d9c20a919191be8361947855ed29b594b7aadac1b45eaed56bdd69448f7530a7eb8eb25de185ee9f0ea6a8559e0bb99694f69c19261620b91a30e6607cb1dd49e87b78adf4723066fe6ccb8b511c0cb2e655e925b51360c0c1245a7df4549f5d37a59393ffa5fc06b3c717964ecfb4fca66e30ae4e1685d6fd12d776ca00461bcc9e26abc26c7a865eb1c1982a9b96eb165b5c34d7e0e928746d4b3d3a393464b81a0cb32aa7ee94f5bf52370d66d73ae72f0feae453478349b4b57a871cf57ef12fc03883c9b52a9da2458751752b0c3398e2ddae5629e7c7fdac91913218a5ce0e52996c0b9d3e0787191939387e060c32187c476eb375e954d78fc1e0269e4b6b695ab25262309ea7beea581febb90a83694d25fdac2bcc77b06dcc60000c30184f56d5e9cba58b9323812f98f5be7608792a28254a2f985f55eb182677950bad2e986694875ea5dff2cd30b8603c9d7d1d4febb76014d53a532fa75b27d482d956b8d6593ca67e27b360363de1b9b2ea28bb2a160ceb6a4dbfd62623a3362bc0b88249e774f8a720758eebf20630ac60ec3265fe2a3aa78eb90d6054c1f8ba2afb45dd496fe8a860d4db7549a720afb2ce4dc19cc2afc8932fbd4cbc5600430ae694cf50da72a85739c28882c9cca4fc9eaf7200030a26a99459cad1c5b98efe04738c0771f12443c8879c60ce6642869637a69df335665891216817c06882e1657e14f75e519b1033c1a89d53ceed17ca63664b30e9d54af16f85cea24a2598c5c98baaee65b6cf97048358a1562eea57cb42878104f3a538ba8248cf15601cc1ac22e4b854a2b2c5ba8900c3082695e3af8f16b1537917c1ec61e9c3eb58ad469e3088607c9dffef598588db118c2198b2e957e6e6d25bb7c92780210473e76c2a5e25e923564130a59cea45be36d1d22b4030eb2864caaf6c7a36ff81f1454dd58893a9c557bc07307c606cd7f1f4a07e295a12460fccae55c5b73479e27306830706f9325e0bd7f19e6f547800630766f768e63f1f8490c2d381c1a59b669bfa4afa74726076e9d147b6694a171f83810373f44a6f7add1b18f354ee18a32cb8c86a03937d6a399e524b79188100a3060695b44c9aac37d38b5dd12880410353f28fd195f3c79ace6660da6f7fd5909d465c5c06e6b4523ea91effd0f363600c5dff1ec48de89f0b03f3aab5ac6a332a086d798139769454a55caf52cf61b8c09465b7c98a2aec6d5a60b4c058b759ee3682c1027396ed7b196fd970b905305660ccdd55b1b35dc548a517c0508129cb9f692584bbd24f2a8cbaa56a687aca7edda1c2ac3b7876b0a0aebe544f0039853969ac6ef7b1afa8575398c2a59caad38589709506298559b7ad0c53e976552c496196a69d5df9c54527f928cc1aabc25fd4332e36cb201185512993bb22ea518b564361dad650cb1784129643a0308e9faabca8f76e4dfc84f1855d0e2b75678b174c27336a289b01c413465953b9d2e788c56d75c2f8daa4f6fdcab94fce6000e1842945ade5cfb57e13c655d17527f9c1b50e441326132e367664f9092dcc84c175645dae17312da39111c584598a91f2773b35ffd427209730a8926fe73f1f966e75be2401260d304b98544e97f47818a41226a1b5efe6a952fff62b40095394277b85c8f10f3a57d237e36446b9131a33727861009049184e8e89d0a3b142255b12e61caadea59c7ea1a456244cf63aeba4b744e9a8363d8040c2a45b5a8757f37ea9553f803cc29c7f5ba8a87f940ea742b2208e305db6a042b7b61f0d0da411a6b09f5ee75c76ea9fc208c3c8ce650ff92c4e4b8b30e91db542ee53c6a84b11c6ce42c8f9a7b6b93c11c612d5a9c27938d14c3da001223244182022630122321061cefac5784ad333214e12003984b9bd4d5ed0e5f57bfe18400c61104fda5ae5f953736521cca7626448317ec9728b10a6d36622af55b4f0d4066196a93f859252aab03a250893bea84c9584091dfc0b84f1b3aae55c52a5ede90061f6ffac85560a96e4bc3f98d53dec6a997d722de607e3e99c42b547f59684b40fa6bc9f222f7d98acacf1c1a45d0857a63f8ec7f7604e26bfa55679e356957a305ffa512f375689d3511ecc5a7f4daa79794aa5762d00c183d1a4799a9e8c94d52e0b40ee6014b79bd5a25cbc5451103b18b41ea15ac7558c6d790820753009bdb735a3b4b58bd7710e2074306aa14a9db8e7e660ce62b792c9552a272a39183df5f96e6ca75a90c5c11c5bf4d7c85afff0351c4ca7fae9c3ee9998ee241c5e9850c086c90cc337184eabcbb9ae4bc8f4789901881b0c272bbc7f4365b7ae510d2fae10d9c28a9191126e83c95b7918b7642f95b8b0c1a8d32d8be52cf399a90c40d660164a8adc5739aa4a8e2323a8066329bdd71f9e3c223264546064e46d78d105903498b4eb741eddb29c555204206830eb931d5b9aee0ce6109784d29a9faf98c300c40c66af13294d7798015ad400298341aa75cf5aabbb5fdfea3c5a3218b5bb5a19a1c3920c7d016330777df65ef9293cee4705206230d6e9d0b39fa6a5ce522527c701242527a7c4710b206130ea97acead322b7e5f50210309883899eeea05c57f63b32c205902f18cc645db858adad375e00cf00e20553ca275674f46cb2716400e98259cb8c6f7fce3152c912f822c78c850308170c2b44a4eeab72a9e91ab923c92d18fb447e2ed339aa6a492e07cb01440b266dd7162f5de8fcce96c3034816ccfe297cb4c57ce99a8e8c8c8c608e11bc1d2040b0600ea354fa3eade4c8996e805cc1b0a9d753745f67874ff300c40ae6205e666a294dc8716d154cf2b27f5015945411a6d203102a98fbc49f9b149d53af3205534c91f7e9750755539282797dcfc395f8abb03b0ac6cf1432332dc907b50001040a061d5b32e3745510293ec1e0795de7ef2046944e2d08204e30e764e9dc54bbae985b138cabca558eb987972b3e49d77fda47cb7509e691e1db2fd5c90fbea9124094609affacdac4c66b11a92498774d8c6903a6ef91adf3898a290c675aa7cb95b37deeac1466194a4ab916f6a4de9b1466219f628aae68ea2e3e0ab396a1725041ec8577164314e6ae57eef5d93332427272f238b87c8183a4ecc949c60885593baf12db0fa7ea6d415c400c509865a6eb2844aad295fa8459084f530c4f18b58f4b51ddafa5aaac923e04c4e88429b6f6959582fcea396196625aa6d0eb2bd6d26881189b30bad23d672b65e99c865881189a306b28e129aad9eab88fe3dc505e9930b70a42a60b25575b0e3191e7f52c43bb72cc389961812c46464646f61246d54baf5b89bf9c3c9af7312c61acbda8b7b2547fa54e2e6c98d0a884e172f257a6f5f44ec5c6db30b931825709c4a084f1b3a2ab28a47b8d5763467a3509754e6e41ee7d3b99118225400c49183cdfb93a0f6d254a2b6244c2fc398970e9e1e4c9f89434c977716ad841c230fa75b4fd9b58bc0d2cb688f108a3d459ef97146b731e3bc2b04aa7d6dfb62fe676238c3ad52acbe9e1b4728d11c6bea0e3ebc6a7d21f0f044f012f3116614eaacdc7b5872f912f4598728aabee723717549f08832751975551c5e7558830cad2427374e80b0d621cc2dcae4677ec8a14751f4318dcd2ac56754dd5d94bfa30bd20d9ab21c2002d440a611ca95f9ee3630ec42084b1bea3691b51a782d0c51884e9f27d49cff22cd35479366208c23c2a459d525ed1098e93059e026204c2b0264ce57e6c907c61e330163100618e23c4a64b1dd45bc35331fe60cec136f62cb79079a75212c30f06a9e7c48486e79c1bc7e883d9c249ef4ce152bc8a49463063e11920061fccba3db68f16dddb2977b207f3beebbfc96cf9528a9563e8c1ac5a2b7ec9cd867069492b1bdf26461e0c9f941457c2531463ae0d1198d8e8c2861778308bf3a4bed2e7d82afa9319357263dcc11c7ba4be8eebe1839876409ba997d7c1ac93aaf421d65be67e3a18c4b52e2dd47f756e770e8695d7bbe5af75eea096832925954fc98f7437558983e995a893254a9569d5c1c11cc464afe9da7ebcd11b8c7af5827491aa7ec56e3077cc4551f14c5dba701b4cba2973eaf1b45cd53d0546465e8d8cd430c961e394642182186c30eaeaefbc2993aaa3bea4cf86090d93449223fd07904999408c35984cd8fe9cae4c1d5d4ab2b7aff0158eef186a306813722a09fdaefe212dce6b11230d069d717fee417dd536669074f17fa34f8b051cbec22c62a0c11c4bf57c6489331d544aface02389e467a454272e3c4066083186730a9edb9be0d97198cad4628552d62b49062198ca393f878d2f5b9f28c0c46bd23bc93163d331b8dc1ac9ff2ef8f7b5b90153198ef557c7fd912f623f2186130dd69dc2a79630c3098455d2bf1202a2a71717ec1a43e4ad379a35eea98c5f08259c7ca5adee2f3e8aa5958215284185d30fb6c9716619fc5bcd3410c2e182ceaad894fafbcfc82b805e3b9694fd9a42a86160c9f3d748ae87dabcbc6c8826153f9b6f8e70ea9322c1845ba8e23e4eac751638c2b18f4a557d97eef54523931ac6054fe1d5c7f9815ff2f46158cbdca54eb67cbd3af23210615cc222ad72a2d55ce4b360583489551520791da394ac1f4f1e24b78ca146af32818b5b691974c09db783d148c16b45b94bbe88ea12798dd2ebfd6d0dbabe939c1a83fc996a946f683184d30293dfafa9307e5e1d54c309a6e9552ae7d4f615e4b30eb16e6e23eccec694809265f75af5fa726c1a0b208a1a5c2994aab4782593fe91cbba4795d501ec1f44aba54a12f7cea798c60ca2a478dba56276f4a45309ac7cae1292d99782911ccc9bccea279967c960fc1e8263ee508b50bc1bca9c5e5a0948260f4d3af4a674556a610104c9762e914199d6abefe8139deee05315a5aac6cf18149a7e8175cadc89642db0393892ad9bb4a29b16df1c024b588cc16aab22c69d9815943e98856e562f95b33c4d08171c57e102e2b64cb28330746dda9b5ad367d9e298c8103a30a2a2b8ba346be974e1062dcc06ce7794cb63fe9be7c0c1b986d3c3f7a9b0a39425d0363c9c98a1f549eda3bc5a081417676f1a0d57495dc316660d61ed9aa964cd5183230fd0b2174cb5019428c1818b5eedaa7fb199dc620060c0c62fea553bc2fc60b4c4a3e5c709553af6e15c305062d65cd78d0e1b512718c16982ca71cadb6d2d87b198305a6eda85d6becfcbfd4791c2ab24503727871c3c60362acc0acfa1f6adcb538fe699ca484182a300a296b59aa5bb8b0da905418f363de69db892dcf4bfa2850e33c8e1c2730084185d94b86142f4eff684fe1290c96f6b5d66676318549f58dd0ec8b2a7fc410520af3df5c927a59a4307650325596b9c09b42c828cc1dd492dd472d947ece03ae509fe3e46d905c0f42446134f7381ea5e9a130fde9c5baa872a030cbd05aeb4ba5d38a7395b4046478d1783872dcc81026847cc2a8abbdfda59dab7515e209e3aa5d34af755f8f5a9d306e05cf346d1546ab16274cf25475e45912b7a80e81498dd7c206162232b208d984b15e87beac73be8f3fc78cbba7811c0c219a30ddb7d4df2fed0a0d90e008c98479851625daa1840973b9d41d37dc4fa9472f61d459bbcaa6936b09d3adcb89527aefa2d055c234ff4abdca15cb598bb1c710420973695579849f9330c910f16059376ef39584c1c67b4367dd4ae7c48891307b5442b7cac13fc8974afa4a727061e3699018244cca3cefd2b9cab93597f424421e611615bfb63c6fc70e2aa685880c119b80880c11cb4244868861212243c4ae1091216212109121625688c810b10888c8103108886461051f00d810e20883ab893d1db58434c29cb22e7795dd97a29a6684800c218c308c9a7b94adf128b72f6708598429b8fcf4d86fe2553cdd851045985d0ad51f5e2a8aff9d0883d6c9575c68931f2d8620c2acd5652d8492b6aa6ef5001b58e0151a2009001b420e61d8eea83584106af4d3104318d4aa88d314cb494a6121cccaa3d0caf93cae894a08210cabf2a37379d8a9bfb40021833077a7b4e2946957216e411853ed3c4b4b580aaf12085374f1dcaab2722faa94f48560860a418e1938cec8c809204cae83e9d6a3ae3a0993c8160d1039e9c2c4c40101b043c81f0c2a6829bf531017343f242439d2c19163047fc880103fa064e48ff872bdfb60b8ff913afb699dee89c98d344708e183399deaae94a3ba0c89e0e020493508d98361545512a66d2e5ce6257dbf67b842f460344bc1b43d9d8c5a561ecc9f6bebe26e970a3ae886175d989858d17508c183613b4f0a3959f9b55e4bfa38e40ee6b06e3185bdcdad4ced605e95a5c5d6f9d2b81092d830c907d81842ea60ce292915b466eddc761f08a18349bed0beee61e4c7d3903918a5d0977467a8f78b1f1b1f8227c9f1278f23440e66f16aa4e8eca1ed2d1d07637c5cda3a2594f8150a818349aaa9d44a6ca89cd3172e42de60d052cb4244abb87d4f021b587c21c40d06171b22fe46ebe7d329e9ebc2c4a484e469a81924364c7009216d3069d5df42fc7aa98aea0842d8605a79cba3ddd999f1cf682884acc11c5ef7aa54b04f255f6a30084d65c1e4aadb9c5cd2f7c5a1a16898a4130e44481a8c5a05f13ef2dee67f6486103498828aa1c3987e957bcf601e35e29229312a754e33189550612f69cb2d5d25f909216530bb12512fb4b6edf4a092beb481858c921c332e704283460244648888c810912162d4ee433023840c06d52ac991b32937db2de9fb1c2743c66052f39e2f7c580d9542257d8d23c78d1031185b95e9e0512b058b10120684885e2954564a216030abd96d8b9732c43d5fd2a71008f982b94fa8e67c655befd40b26bd164b4a796a53aad41787c63b6064e48b43e3c6c90c35822f09e98241caf6e82fdba1e3bb42b860d26f17722b887525a442b660181df5f2767d0e959e164cefae5268f5a0b4cc3a0b86afb4fade3b7736b162c1a063bb05afb41dbb735730eea93bf55539a75db782395e72e9a1a192da13f11707078e5323a40a267dd2555ca998ba2e8b0aa6cfd2ff54964cb14a680a06d7aa739f4e2905f30bad614998ce4fb58444c16cc9bc944bedf9ce567c8506483610020573f0dc7379fe4bcf74257d24666676225b582122e30e214f3029954fbc47bfb09e3c1ce204c3aaaa47755aab09267d419f85d349374b0f618251b43069675b6157052dc1782673e92feab6f59560f6b01f47cbe8778ae28c1c244014d9a2017b214930b9d2ddbf42dfcbd3291908418261b4a4b66e952b8745f036ee44b668c0851cc1fcda766fc6822146309fa97cd27d84214530efc5df12abb7f7511e4204a3eed52928312ae7772de9533466788156648b068c8c9c74616252e283902198d4658bdb172e7752a6100cdaf326737e413bb49020982c6abb6cf2d3c58ffda874b285b248200c05a21886610080e3e71100631200000010168e8503129950b0afdb0714800443362e463432262e221a168f48238150180c85430141201408c241100561188d7434466b001708dd705e364364e817cf1b98360d18d6d8c42d0c1f897e559809560eabf1b27fc43b8d85e27e90d44a21157ddbe961f0d245ebb7e344860a99a9484b4e094d13290449689f8ea8129adc251e0215ea148ab7c0504691152a37188e984a940ace33d5ec356ec460c3bb5e6354e02a2d02101ec63541eafd7f3b20ac62be7c58c3266a32fb93ccbfc63e57b4a5b435b99ec27bf21b63a0b94242677465dcf7dd82c00e0d04041d9fc0cc402726b98de0ef8e27b49f37370f31d4b36e3831612060e4138f11cbe07aeea821dd52c5ae978ed07a8ee595494b001272874ca5339ddce801ba569a2a4bce59ba4154bebe193cc89950d7572883833a2d55262d78aa37134250970ab216a431ec47b9dd7bf2484dc9c82b2152d748941fc2ada9a7c6a251056430e78a5fd154a53f362ab8d29b36e35172c187c0852b0ae8367881547c943e602b6b6d5dd71ef4f377c77e7d49a80515101a88ad9805b6b30696a5247c0de170d86dded7ffe2de6038af671fc7dda7b878704b9fa042508c78873d5bd305755fc3e8d87c029463d3f83c5c4436bb442e92e034052a96e02f64ac6e5ea1f2e3296eb86bd8aaff016a7092eed23d2b8734fa8a6b37e1d09b08ac6b4452b35a26a004fbb1a477ca79f11e20b4a963b1bb18bac546e3607ba91eb4f07a0513a9b934512d3eced3c57722604b56988dcae73c0cb4fbb8561d3c05be8f8303f6e7e6169eab555ca1e72cb034517fff5dd5534b58ae90a82057c1a784a91054b9ad8b7b264194d263a13e0ea6fc3e1c3b5add3f5b716a4d2aec14243351344fdc6496b28c189fda22ae9de4730a3cbb5c9ee1874b97f54255af08e544cb95e6a3cea3a94bd555d7d2bb0df7f6933343384827bea24e55fbcc0468c773014a5003b26108907158d725c7ae29d9894a250758d86a44c39b52b81e40aecf0bbc1d9793d0a2cbf04ebca5538458038f2815235289a86c206478a347ece271101611bf35efda3a4e0d67e50fd54899da50aa279322ffe08df84647c29d8a77c206003a00d600a4320018de33d489317e2bc6505f838e006aa79625faa7624f8e31820e56ab686ef16b7fa69e7afc2b747cf9110c4186b1ad3e8ae4c5c1f8660988d9f0e97b8a2a3007d035003a2472c9d78de8913959f19c6e8161433c394fa9ff9375b3d53bf959c68e8d7446fd1abf018b6173ae708fdd5d5b8d35df630c922e4fda4885fdfb206bb3830584be4220732d448e245221774517d5a6d361eb185c46d1a98b9b2937ff9f9dce934fa76ceff3eaea88945f3a422684a252f3d946bd1d5887f838181b2e8c2cf63a14031072e0fe395be6d014a06645dfb0836b057d6fcd6af07544df6b241fc2bf27cf10dbc1f4a8b255685a1157ab21a3eb15d7635989b2e216ff7002fbf135cb3c05eb987c4116db09c1d7ec87f4a1b715625a212d57438195e90c40d2aaf1ad40b3158e56416bc575565fd2ac86b87386f76235942a96eadb0896bdac7667659d56e965d59dd5d663356c4f4e315219ae419c2c06e5a30b83b5c6c403a19bffa11f40ef7f27738d80583645176f4632523b737af06d1c516da2cff2d7315301a61780506042154882140550a5b638cc46bd27e1ed0da7410a7f84050ec54b7f8864dda0fd8a0b4e8a986a100eb9e26321eb65d485b6c3f2fc0a697fb199513ce57389da81981301862a46d4e984252619f2f117291fc0abc265b01e9cb95bb388116516044caee5ffe9b4728151c575de087dc106cac201f772d0350cae16ee1de053d8b7c8abbac924765025136d3644e7e18127ec40440169f075e21f46b5cd22dd195309a9139de37805fe9aa3028fd17cb0fafba4a82212819a9f9d37e0c293c24e3958eea42eff4a808a257a732e5a71a5ede01b292f14548da85fc7000ed5b4c42678223e468f2226122a1615da40d8c8a91116f5e8a646497c692a502554df4ef21aa2304291f0ad7cce9d175b43d85b35dbccecda2e789486d3c675ba87822841c03cd16a86012397e43ed638a38376c90618931a2cf3ce3ec3246cf6481f30c5206a5fe243310bc44d80964b37aa0be2128a363da505fe56af7fb92e70404f2929f3549dd64539c7cf298a8c7f0c334e66e4a0b80e544570f4ae9e574c358a39e3b63b2ce0b522b74518afed81ecb471e8448c3a9645612d749b8b1b514bc19791eb062bd7f6e5ac6d2a7fbe73c898ebb5ffb521d2616ef6d23201622d55cea92bb878e786122c7863aead1e879df3940c6d0587c536dc3ba7250c6135ff09ea540546f25d62aa2fd84cb4f0a005f645c3f364d8279147d18ee7a9a031049d3fc400b646818fd653aa63010beba1a2db30a5daf7849c246538fed01a0d79a15057aba6e6829530ce45841f9387391243564ecb142a8c1b88886ad73bb38d122852cc9951688063f646c1a310c2f3fcca5970cf11fcbf2946722b1938c955461379516f29eb61f073d8d6d2e53b7839c326e268a28c0ee962dba7652da518cda1c25f807977243a19525bef860684b50b5b56ae29e4851c4a058fbb57d8e1161b9be1642cd7850a6240bb9a60f0ed34fea17f8ddbd0716578ad80ba7782c73e9119f22f718c7dd47a9d6ccbe848016ffcbcb774e56162f9f6cd02711474965a3b61003a28fd5b3fe3f2fedb52abf85f2aff799c1efea5fe21f92361000e7b06538fcce6da9bcb589216fae989fb230f4def43d11c9e14eaa46fb8928ad695b5bac3854f1088fc67b2220a10e8249368e4dc24453939cab50077198397fa1715c5b8b049ccab92966bbc8b3ee7a60790114e3f6efc6342f4fb88350db1a46e914f71869020ffc65d602e3b8272aacd286a9195f3539da92c962d483a87d830b80132441991305bdbdb4c68c05620628cc06ab22900c662a4bbc1826e96d7089a4ede615474662f8778b4d31cf9863bf9879b4e50203a2530b5cc4b6d35ddcb17b7eb73bd76ed81dd928980bf194ce56e8b378e9b38365a985367d85f88f5f91e79e835f173be1bbbe89a548cdafc117d504019f7e2df9c3b4a8453f9b1f90fd6dc52f9f0498d3432c2fd4626f26c941c721ce20c17c893faf9032623d92d60dd6f6bd070838e3bf55d5817cea118e6de0b1c17f43f57291187517bbd504c92b824bf388528e31b55cf66828f50e45118430b88d1d1bbdd4f9314b437b66912d8d4320bbbe945b43e38619733fa73724a10517645c8295799ff9ed55c69690269d141fcb86e5a92dad3e49f47448c3f4f747c2b252669081ccc037789fdb6a1698fbeebcecdacdd79ac30f458d8eab1581ac6da766a1b09ecb65c79aa5c033b266f2de7ecb2c49320513efd055997a1a3c62a40c5fc4863da29d161a87169348ffac3b02d7ca802ed945d0e3b6b511e76dcbea93576db6642272809f926332cc283e67069b904affbde0d6583aa55f0db03c0c0b5b53d7b6494c436faa2bfae9fbccbec6b251c7e56f2162c0f59dcbdc07f4049f0a9510eb1cdea2de377501d6528577519e629792d4520eed5a31e9893064c229a3222c32321db098dc64bf2db848c19e15f5e1f4c09fed951d401df9dd5e7605365804fdcb57390ccc661f11cf505bafa54949451c32d83db16f15e98fa620a29014001e032a5496b1cdffc53ebc83db92ab0dc6bff2570fec3bd04312c2c1e5ad65c2b37c51bbae059a7b55c4dab412771137cf50c22a16d09a7a6649a31d0f9143fbb5a1956cb2adc4a160080470232dfafeca1aa951b6d109129584ae672a9214e63c9ba2e773bfb40395b5604c948574b63344cdb1db6ff4732318dc34403a4de54301aefc3501dc850a387790045fd64f02a44130ccea03c5483c5282201b4333b50224d7f75e5af7df9d99cfe081bedcc7a895d39831e305ecdc85075d8707fae215e89298a51486b7992aa3b6af37bc02c766df2930ae9818b63a18309e393963197f676bbbdeb7724bc3a3f6f96eb5820464a806247f727061454df3164d83e0dae9bc28df47dd0b10493e21d39341b85c8e493a0858a1907c0ea68651ab2de2d005c97426b5ead014b88e49ccfe0b808d939c2abeed4fb6ae4bd0c628ed2d645c560e8f9d838de692ec90b2d9bcd6b8b9842dafba6fc9374b60078b45217f72c2d52981e719059a8b362146d1da96da12714b76794217590cf664291ed33502f91e6807a58d03ba4a3e9acfbad25302784dd045fac07df2e8a80386b8e8cc1aa529c5ed210be58013426198f058ccfa333efc629af2d40c96d292f5017f2ee2664fed26d7ce4eb76d7a595f897c5fcac51130e3271deac743a155fde611cb875b769fc49ca93bb37a303eacc6421573eb8851927540bc76e935174890faad62b773d48337aa61f0b5146b00bae790a4a0792b7f63035e0edee3879cbf68e262d432a147af2600ffa031e14ce752f1a1c69dbc902ff80f208c71f746953a3b46002f33750ae05207b566691c162a106d5c04019376d24293a73c3ff7592961c6e73a41b4710b2e8c4ea25c5ff8b984f1efb180e0011486026993923a782c558f2bf8a02a82e4860342f8d173fa40446436fb0a3c4def1899e442d8492ccc9f5489d4a76418e6b1434722202526644c9b99f6458483c29e0f7d0f326ea0df5d648826b8f377b5410728eebb2ed59041b505ddb541fc1311d41f4f0574835386475080ab950b41dc9962e9c7ee18daa82dbb0f8205f7f6c898c7cf1c3c0be5ce0c118a450d290836eb303f4a549846076c9a7fe47cf6358400218c37eee8faabeaf2a2bd812f64c8d9550076d5a703d21d2bb00777ed23ce00efcdb2ebb8d70e8b5935e67bd26931ad3d8a7a17383ebd6f2e73706d0e155666378d556d190d9e56b32f3f2cbbc5787f1056699bba11b82fdb6f9387fccf84c699bb05f6aaa47ee006b407f4508a4e5c9dc954d8fa95202143afb184ebd607805ecb4f8c18adb5e86a921809470df84a85e4e09afc11c4903590546bd5846763b4f57b082695c5e074cf40005896fa3873cbdba22e00ac7dcbf317a1d56a139211d205100060180519d8d7e4da02aedc6026316beace70b8d07869d50158ef617f2701bd30c438141adbbdb99d05d15bea3f032e0b7784c48bcdf618b996cc9dd3bd80c8f9a7bcac355974aef0c7dc2912a0595d83a2046f86811a2c740a4d46f1be315919339ac89e15994295265d48c703cc5cafb239c0112d9f10ebff244427061184914ea754c8520f482c474b98e8cd4a4e00b20ded42b1afa38a25a9d4ec75613a0e064e402843e7f1024e35b0479388dd6c19fbbcb102fa42e659a6c2385ac60f605c88720386effbe7d31bef5235da12df9c04856a5d3affab80c1eeb0419c918f27484c64f35b7337938c68b9554a6e83998757364b6da65851cb2085033e948706cc0f4eb8fe50d7ebf1070593f0030954e86aa9393656c6899045d94b3cc9fc6ac4c2899e6eab52b9bf24287262dae1fd33ece6a80d02e9231f3d9364e32b49055401fd8ea253bbdc663acf392ed6ab5fb32f495f487d3e1e5d70778ab0795afe1363454c893cc3e4a430d89422982e88dae8ef5341ba7125fe13d02b81bef17c0b66a9584e9b60dc793e9044dcccd293065720181b7e85a3ca49967fe4a81f2470a4c54c65143446653dee20a589839e575b493c4ce4c445cd004497259066cc13c79e8c220ebeb8387bba02c071031441f3aebd336d0e74ca827dfe9d061fb681471aa5cd5bec0710234dd2d36eff703f7c7ce5a0975fb4571622840022a15d13514b64802298a4444d82eae2ecdf8bbb0234d0f5f483d84ca219de88fa2885bf244dbf4ffa7d458a1588f5a17616cacadf770b222a76250e47dcc620e4b676e2d2a580855cc76b26720efc82d7a730d750e7c262c10298e81968054ddb4b90a2f827728d3fe45843e7ad188882d263977975414aae3312166d43b025381cc6467f03220f1def61cf2efd2fc4ca13995e5601706312056e04b881d25865e1cb58f6aa4a83a69055c1fd9ae47109426089d2d779ba1e5792f822b922626a398202ad05a3493448eec6999a390a69672a04345f16d39f459eb925ca8db3a9d9dc8de9a85e0452ff470089caea0441965d44bf8fafe9006d35500dfeddc2a0efb842905145cceb8139303c8c83fce1cf48518e417e43cd8560041f1c9bf4f2ed7911cf52587ff9801dc74c451cddce9f9187888c70f06945047e4715aed13724c2920609a3bc8ce2f3a80b8a60ec4e13cca90cea919968791b68760f60d29467b75ad124543de9c6bd9ac50794e52885b841d59a2ba8a52a1fb40e87389492e236348bb28dec8bafa3c71bfea4089d64281879e7612de501866c128202e004091d5409c7d20faa0dda3443322760be81179f076b8e18584719be745da96899f03eb8e0d5f2d7e24c8cc4a587ed9d9027475793592613d902b7cf041aa72b1e585ea2425236ee4ea6848c2835ccde8751746de9814200e6dc58306c7512cd994497b2138ef89902df96bf91cb3759ab8a294bd008bf56d983103b519bdb557e9af9d504c7e9ca260e542a4bd00524fba984b3c4ec66ae4a412398f38ad48f23ef49131db76c6acea54ce022c596047c0a851e25c9bd8a5518c2134ea75241f8d7941a0418128984bc92ae9a37db7433b3ca4c3335ea0a46a88e5e619118530e439e134d8bca7864b70b6285fbaa08ff387ca86f69930baebbd5aac0d3109254b0aa617ceed3daa26032cc5f17ae5e0cef826a0994ae5648f7bb1f4119f12be98a28c7e6e7d4a5a96263746c1b10c53b120299e24361fb52f5e732c26c4dcd8432fa65f5a80ee85180891a72932a185fa580529a3e3dc4c4bb3c7ef1352e2dca47d4073c45f24359c213a45bb175b11768442afb57a08780b4741e144ee008d1271d7f41deefeb4ddb28785a3b52fd771d27a4b27287715aa618efce77730e51a058e33df2cc4170fd9ab5cf28de71da517331a444905efded3370367d801c6be150a0532eb72840b719ec36d5a85f8ced0181a906168a8188a071eff3d1cb47656b674c70aff9bec67ae7291cdcc769258e1d4b99bceb469e458fa89fa862576ab136639ad41d1ae012960504978701a40c8e8e6d1be9b1c3e0e554091568e35caf083387b6a19f7c75b489af15054a9820905dcabaf59b049811060dcf6bc06ae76515e81f060a3ac08eda4e83287c50e77f37e8070ef883069a5fd1c57da35dba119a70a59868d43ab0986c80e4eab43215a2f166f13f9279686bf6cfccc4d46b50286160ccd8c1414bac7e73a2e8affdafac79f28588a6241282bc94acabfc636cd727e406d7ad6404529315e961e39706f20592f087272b1ab81ddd4d4e3a51de72300790d1d3eda9c5a3cebd358ab4514654f5095f5b73576991c36a49fe647195b479b2b7a58a8efb5dc7ee24aa6cdebbc23fa4229092316dc1177d556f3b1d2748c23b1143946838138b66b57d63c00879b0fc9666cec650690c7b6a740d2924a6d8278f3f1f0fe272ded871afe17afbcc6faafdc96f4f9b1a6d22cf40e44a96bd9117cf83c10fa1d04452992e69bf7f601c26c1b99ca6c6c1645629c1ad944907852921e3495b9040b6d51e333608d19ec4e5d6e4531fbce66b7ace6e90244c75e064d20cacf73ccfd815129d272e64a3c3329363580eb1abaf1eede839e198b4a4818a5b712713ec0deb2d476e0a440da3c5a84f4879a824796785a7ce68edabc62432d0948844f9ee5f1400547a230f5e2f53cebe3cd830c18aec21845ed9f5de358be809cfdb32b5abb217b34969c57c75950f9f5dfb3b9bb979a1104f279a919407b56ce2e07aa300eb128a628d1863d255a9461ce7e0263b3e1193d6200b4faa02921e587d253261125b5884c8737a83428cf4255a7b47ffc05b2a757d91323f7a1e8d747ed65d960a89f9b8b9b51ca7b762a049c02bac18f418ca6f8c04b96e204f74852829973bf7b10fd912bc61e7344df6fca6aaecb97c68cd9500ab102b269cfadd5c2f3b845f9444532590da2158490e0d443f9d32d673d25c829043d9183705b15c247b2c45ded1a8375ed0fe1d88249920a8aad4c13bf5d21996e22441e19c89a59e04d209f2876460dcc8ad48a434bce781b3cd7ce2b68b73c656a8b8a8bb1fd57fc342f1576f553f122169638b0201a7a56a3550ad2d91ea0980dc2ec8f3624d189eae3273de62ded9303ff52628c302c48265f6114b6112c4103b0caf3f498efbb8febcf0400783619e451f6abe4256768352e538f8258881734903c03c47d4fca8c42bb448c8fb30044e5a440d4f6dc318ec2858ac11eb340a4f8575416f9b0e9b3c79690a86041867d48058fda26c9f8f8db13e0bedeba7e7c95c8d7c87f939a52b317246f5887d30c7e57e1079c09c3bd7f9c037ac6e1ab04a92c71c2106c315cd1b414e9148e8e01940cf814d5044ab6000d5ff88b3db2641d75dfa71ea6a9eb13f8db8f3986b1e9b533a4f7362c21e62de376d20d80f84c89902cace39f8dfdf27e40ef76c12c671f40037d302e096c667a6bab339187ffd8fb5420acedb156090d755957418af750c764a3d3b7eda50dccacd8849c1e8217735dbef4e8663a23fa67c833b0a4da5e5540da2a86e107973347fe6caece97e1422f304688c841435056bbb7f7e679908f443bd6edfb6f22834f043aaed9cc2712eabdb3c06ce830f274b52e264ce17ea97031c4e4a2443d239261cbf043ffc296cd1254bc1020a1203e2efb4f10d3a818de24c98619a8969a2cece9419096f8b1387490dac576b90b51a01e6004058b4fc0e09b2cd67e0e281172d2000d0898832505a61a5cebdc9a20c69b64ebbe0a58f50f604b5bf769e7b263d4cd200797ac90fff5e0b14e8285b39ae123e05eca18d38432356212f3fad7982ea23b90621b453474d523c23299b1685f0fb944a095eed65344ad84da8a71f7dac192b271b0f9399a2d0e2eb40935fc63127e11e373e46c5448ba82928f056db87d89bbdcbd34577e5855e1c695df5e55620ce27cacb0ffbcccb22b497aeeda3338051665058c854f4f7f5f52fade323e2bc13fd9d4b0bbc7a6aa9ab3abb277d1cb435f04ff3e651da617cf16b06752602a7ada0a1512ad596854bfd8955a411eb76014115f619415bb1fa8972286fbc59494be58ac156200825aa84eb07d31d8c8d8e7cde843d6166ecc308fdecdb1ed6118782f7dc90809c81734dc2ca78251007faadfa563747da68e06f5418db4377b43c579ee5a36761ca0ea95057ffbaa57cfe04e3c39ac7e86cff5367b0014bf1209053f2111361b007760819a7d01a581e86402696485f0788a50124a0ee6a3156526c6fa62d59f5e1528a5402c7bd7d5a48e19258763e927d80d4f16d9a2f3b61efdc53a2aaef65ec81bba39adf59f6eda1e754137ca328e78f4a2ebadc385a070b552fd1d05e401855e6bb9a376a958376d550e43e87a2273d9ee1b910ddfb21384f8788f4916a6f86b8e20e2fb9dce2dc5142f0dbc48902f361295192a382f43a4b65a7ad4cada67a821a291b2e8262f19c206bc58ba4cc328dd2cde76860aa8e4888f19fa4e1703685b3f890cdb5dfeb398a3d018f28f3b832de9e6a78d5d6349ca6b41e0ac965a23235bb3924a234a589acb4677bb5689d7c70a0bec12c0b775c7b45c13b2eac9cf3b2cc75bd25319612f23bbbd82614060085b93515751c414d824a20314220912bb59a1f803e6c05bd617a3c4360a6a37793ba6dcc13e2492836fa4f0a48f19ce35690a28e85d0038a806d3023a079952a8963bb25592d1421660e43132293498c19cd94fdd90b562584abc6c83164dc18d6ae00348fe8bd6b6a34eea9c9f0a23d12b3aa90e820000ebbb1893937288b977dcc5fdb60a02eb0154644a3fae27836ddde1f81222d137d01ea5027bf426e32c31e89d423d57c78280489da1232d44c0d571138299cfaf1ff086c378be100cc697044d1b7d5524da1ebaea4b202c544152fd0c1f46b125ee63be605d5bf8598f006fca8c0fdded79e2e82e31a922a89afbd3b0daab8ba9b8fbd979ddfd901cd5d73abbb7ae0dc0e32159cf077cf3055aa6e9810de4ca0c7414cfb4e22201e3b7383546394ce24ce72e6baf8e39a93e94ca400dea757f012327d012a95d640554a8b3903acf4728774bf94c20a8b90706a78adf2d7ef0a77a58d0342baf0cea8567739eabb0d0900194dcc9f809849e69030a5030d79c044dbc30f321d3ff6714612f506cb59383da4b818d4a5e67a144a5c2b27928230e49f8ef9bb81e195c8f548cae54b5aa1552f7cd02df00a8a0c3e1cca05b0690c710d329f34f16da68309db9900327852506412d81436cc698c25fef9b225646e1e5a48a9340da50316b9adb63d7194266a7a127fd5fcc13602d200e416a2633347fd169322dc1e3e183f5e06ba4074f9387241b4c2d6cd124dcc787bfbe50f6a8302257077aa5857d25f1fb245e14d7ed3d727c946affba13e46fdf05821347a2c4d69d2963946251c93f3493d7e7d991573305d88e1dee9009ea18eaca43a51a770d100ac3095193b33749e30bc4b828af8a14ade32ed02f34ec65e95029491389d89452d31c90e3f5cb053ab166336696c464a6cadaaff29b9485adb39521a0349c2a4be70ad9690bc640046534abccb653eca4496809bc3f70450f491f980610a500150eb01a4bb01add9713b15bd8531d533eec4ba66a76a06d6c24cb0cb010b29bacfed6c09b5dc18eb970e76c1c9261616a544758fa2b6abb7833d618741859bdd089fe1fcd76fbdea616e2338a686dd460216580379097640844613f71753963b9d7051f4f4208e84310a4db7e397ab1f501da2a9af7f784eaefbe1298754d2412326cbbce5c7af1d344ff796af2d1363f657882fa2721f6a23c93d56f9d47930d1a5665d85e6f5607167404205648ddf964087d95989da38571278d268a8c8864bc04134901a838b48121e22a74084f1327fd55da837b82fd3870e2be86f5fe03089885e359284dd08bc545b8a04d514e84adb7ac00d09d625a92b3f486c703b3c2c47466efa4c7a778098ea80f0bbdec5c6e99ceee9df2bd98a8d08cead5aa1cdf3e0ca47b363a6b61066ce3d037f7e9043023ad223540605f63d8706251418d415b68661e35f6b69d7f1ffc09440ddcad13aedfe158d502371e9227da9462802953569efc0e889f3d608fda3c40747b578388a3326fc8adc25cbb14a45034a10de348aef853391b36cb0178ec4263d253bbe51b014db24368a3ccc7e07294cd767680515f1e58a0264e60c18c0f07de58396a85ffaa15463094c687f85fb176c110f017d176528b31bc4ffb5eccc40378c25d66688b3d0ce89b2278b8b4f0802d00030f2eb6475c843ca4f54e01e88cc4250ba094a18098382e8484d4ff58d566f454b411f2c297d39cc774504e76cad97a3ed657671642edc9830c19c70b9f67d6803e697e45d567bf1d3cca5695abe26ca789603e9ecdc35c245e62d562aa32fcec3b9140a12345fdbefc70a9220be0e2cbefaf5c9e7dc9d038cfa7009def7973558ef8f8e8cb5bcda43415222a50c80111f0629d4e8158c8782707beffb593d21815dfd679aaf552813a95b406735d192d67f89a4010b7053a01c1a465ce2e39374e77c29a27406580903a4f5af34f0d5068155cd5baba9862a8a350fa81fe2e8689de9222a2de4c2ff484a817fa4a085aa949838b2cc26b961ef7d14fd038225868b8a42de498531fa4eb07bee0ef6439ea37a7a9064e275437447e3e45d5e7af7649ca104a9f9342dbc6c37b41ce68b28119c672e315e0a36b0471d2efc19848356a5ba6156a6f837ae7386273e4f12b285c001127c29b40c63d6108f24ca93ada9220c00d8f216f794512aa57e653bd594fd57e7de85d8c00a8ff7d3e1a155237b42b926ee07dd61078617fdccff9ce6b8dcc442676a10b55f520ecaf0bceaaff338bef3b4c05e9eb62b259dfc396bc648e000e307efbe8a5861d98ea04356782195d1219b858542a2c04cb8b60540682551c12e6e22221e206b2c7fd9308e78ebafc0b1598726d2a8d99e1dc034e9c069df0fc019ed8732494184a7ddee070648ec7d360de8045fa77458076036a9b274a6244acb2ed9d4b9e88815106d9f4d5f59c4c992d9bb543b38e3a3a856ba2d7f5d9db001fa9cf92b7c28ae6e5f1065590f511b736b58769e30bbe11cc48a9c721f84ad87527c89a3b0c31ba7ed8dbddb72ac017c21de01398606d529837ca7f71dcd879e9d5c5cc1a7d1c749c93093b44be532cc3ff4b33ace0c54986cceb9d69e04b36625a6a460e6448a6c10300303edada4a8ee76ff05364780d36588bcb6ece501090bf6e82c3c10a29d3841c0ef2853d7ca99d28c495f3d66d707620403e018574626813fd0f86f3c22ab546cfdbe8f9149e7b353edd54670fe8195fbb14239296288100a6856e796f73eff26bcb37e40e1b9017a2c4084a11f469d85103815873e8b3d3e264d68a6265519b87ed6009a10f102ce30584430883798511816fc9010b22b0de1f0bf7714934ee1876fa588da1cca650d39bfb222eec2f6bcc00e315c61f705f68049f3023a5a3334e603273eef03f8aa7ed522ff182c1d4d61f339110aa1899291052884d1248c30e72d4616d08191c467064079b2d91e7e99eb2146a92864238c2b421696f41009a31a2449261de4088989130ab9e2242a1dab22449aa45d08ab68bb2dc3fa000cb024ae7672db6b59f24179092e48271dc16b97f4068f039c8716edea5d9c24a160cc1b3543c340a6368db1cdb6c1634113be6e07663e55bdbc6252472a4359e1d258100d7a3a9e21c663f806206972936889064c9aec64490281c60dfb02db48aab46f23b421245380ee2e320321c05dcfc498558d6cbb18879ea586fff804ef2db12894fc96f1ad66cb6eae7d593c8580e7ac8ac68410b5a572772ad00adcd130ce64a99855d9f8e323404c30a5212c7b4b08cfa2b6de94e61cfa92bb8d9cde6a87f73d609703f73b5f1bba5b00e9cbb176760123ed2201592464614ade90182713440ef68765f3c5b1fc7f944e3cec50b1b41c1b27805a823b08826889e2871deea55e80593daf0d9b372e4da74682fdced606a8f3d94d3ceb38571cc7fea6a17b2998902bdc7b7c4e0bc9e278ab7daafe7dde6c7b4c73c8de01be8c8355d033ff1b37fdea07df5fc9ff1ef5eb9fa01ba8e7d5068729652bc86ef28dd9be7b893f614868c092ef32a727ba073bf9807e29ca58aaffa58883098adf98b9a08e7ae1493af1200b2903c43cbe251af85a1b0189408cc910012300778317d64f5a12d68dde58ab084afd6897f5892e4903a483f5c2d2117336528aec277649ecfd52afb3581512bf3ec11d2626c9cba5311b8a3064e2cd756326bb91324308c4baff979d47384bb24e50545ad6598fe44c62135f5a6297e64c5ca0bedfdba8e22f80af97d8861ca700a0541f602e3938cb530832f382800bab23b46b2c9d7995ffdc4f47bda097a735e14a20bc1d03bd0189e65fe17b1656f191b5f4272bf1fefc73c1656bf9d6321f9743fb1b7afa68d9593f8a1cb4ea05792cad0f7ef951a6f01358202a99a4ce6ab5a7c920ed7bd41579d2e1478ed75fcad3bdf952f0803beae06799c3e08ec5e40dbfdbdf5620e7d91810055884f7f58926814ac9d836293d6a1cca86003e389f36429d21c2ea90d956cae21cc1e8760a79b0cb23a5cc896c344e763866f9a6996037585567939811dc19fb317c28498c695312ae5677fd24d7014b58d2d9b6505bed7b679cba0559419e9ca5c68d52c7df75fd5808b697afae5672888f64e672d1b712c7e9c808824c93c632a91c0a1d4270b3af3f266037b33336995021895736896dc0c9fa5a3e90611fd58ef4b877313e0fdb521a7b41d633cd8b585984c5fed95aae84d390512cbd8f95be489388010c34822001f266ac0f2d385e7de823c3636ff57a08c558b6ebae4d402359c21bf7b2fa2d0a902813ea25ee966c0ea865b55e963c84cc882f1f47fe8244d181d9f09956325c8126927092a4aa7dcfb1f1170e5cd627206169786effe08fc2ebe6586327a1db26bf1760aab0fc80d4c4ce5fb52405c829df98bc7ebef4208f8eedefa064309c5b738b7597677193c4e96df63e2aecf3de2e6e32aeff2aa84b770fa3168c6901cb39b0da5c2ac4b8c018528534c78f5d26b468275866922356ac86c14286784cab5f13d1e4ce7afe76453be18bca97b3d2fde20c3692e606e4d64acc140eeb931fd32c7848b9b89c2924d8e883dc7c2b37e421b2b00aa4c8bb82125bde88356c89b36998edec167c290d094cc476495203dbd1fa6e08e0a11a074c822f5d4877aeb7916307175dcd815d7860a9f3db28c6e41879b7071ccb8a1992ac601d033d3bdb200514b84b6bd3ce247a5902118fd914e971ad7397823e35803ade9ca892647e331c0fe3a0be96d9e3bde23fd35518caa8ff078f829b35ea40e24357c3a15f488e595f67f6b1886248ec3e63d6f3f572f3f9bac4e69d6a00738baeefbc462de113a850c2dc744478d5f67e7e1ef22be0594a9fa961c4994a5ce83248276cb6ff6d27570543c53df17bda6187f16abd5a783d7898d08877f421111304476752cff8f08060532409df17a3ec103826d489861dfaa9666a6d585c009ef85eec08975b22ee6367e5bbb8c17164089fb932fc3ff43de8a82033150c60fcd1cccbb9c07cf0384071f815b0403d7147ab9eddb45456a626999195ae27c17eb2576ed31154abfd8d697ff2264fe06f42bc725ddecd371ab17f47ae481ef774083c6c63411b24f9b2854bb849eb32393721a041330d778fa91325ada96aee6ab5e874b7927fe087500477c58b9d2c45134963267c981284aa64809ebb2770f1c23c03cd66b5331da7d64a3d3dd3cdb181e2b25bc1e9ad50c5161a0cf16e1ca823a9af5566998b729fd55554d51e64b083829c37db1aa0e97c489cb69aba5d7d90d2d45399fefb12bca0825dcc64c921d66129a6f6812dcb011f769462b9f97b7958d8c1a89b6fa67bd3389e80d215e4e87ad747d391e01654f904da1a00bbad1100474442fd817db0a4e81bcbfcaf0b14b0081f6429fcb7d13742db44e299703f7a09673891585a63f43958cfbffacf02d8a8af7384937b3b53ad32d3f6d4509088bf86c5116e1349658d99013de41415704f78e19485a0b261f00fc5292b0f4a0abc0c1ccc4abab0f2cc2a074bd8c90f26727c2708e6d139a961a05d11c1b3411084501ef9c1a8f6d2176dcb1ea2124a25bf222f4ee91b01e3b242e5357097e233d98da6fd9f683f4a10ee652e24486806f1d0189af76ee34cd9d0073c4115802c81740e859463be596162d5273d4473559a1cd2e41750c0f20363b3db8e35a7022cd6327b7aadb1c33ba7d1428a90f1ada973e605b4ee0a0c242bae7271716a8ed375039c6922fe4ae2ea2fc7493911d0b178284f7bc45a4da283dd8c34d6fb62e0a00608c2bd244e5ce1ecb3d81150cbd32c331c750e78f5c50b3ae322b4c3c0adab6ecc3040b219110c0779c10f4211585d5cb4db7053bb8ba0234f2bce1b77c8697ac46816ccdc4b06d9de1437cce8a50af7dc2e997b7d83ae22dfb0ae0e8a24a8d18a574c8baf2a94a667cf9f4164fd28332b0874307fea846136f75dde0388bf2ec9f0cb513e41d98bd426e650b35c88f9b5ebe52371d808f07ad0b56ead6917d59eb311c2297e54110e41691cd874ac9432d10ee0d15c20d2f5571b3728d2bb90cd59abfb2781b4e833281c88a212f87e05f29da95dd132c65a37d95763d47aab2604c7fd3bf370ea5a2452b719da96a25b622d9a6f099ea4dfa20e81e88db4196bb666d4d129a6960c93988065c13f558a095e3853df7e2901ebce1f276d8cac4ab9e2e37e7f6104a2416048d40ca87d5e86634264df8257b0f291c351dc5a6e7abf140321240d709f46462454b9739da35e30678a76661c9dab336094fee25f1d06491414af1d8a4669509b600ee2270b02e393b38945aa33590ee8cd8b2f64ceccf43a1d6a856ff72b54133a70723e9a64692e7c6ad400591500d50b9b820f8700e2801815c08d2c85eb331fc38a287cb77b6e14520c9f93ed1469985b8586aab016293cd42db48fb11404cc7d21a3a5feb5a0f534cc7d06b55f1fe9000245eee94034222bfc63769e11f5b2e2e9a84b9abe67f914e63264fb62b73b037c105348b94458fa52c3940a801ffd8b00a2dfc0450e19ae382ab114fed956244a38332a102a579f65aca379d82fb93f8e88163b09b5d81e1c05f72b7e3cfa5d18dcc8624c4dfbab5bc3b64f0a23fc5835599ddd1ce0f2d946cdfc872e93498d2d7b56010e500a11274c9d17baaedec77d557e7e4c0cccb815097efb2b0694816cc9d02b1c2737b2089f4360e1127d01b6c9ec6245c0a063f6d95b1a8eb89dfabe2dd19056604775f4693af0efceab3a8334864497cbe391932c4419028d8ab2f42bdd5e51d3a59764dfb7db5cb7ea693ddd487a3a449bdbd76ef43aedeb1c7f49f64031449b060aa0c76c60f8bcd0f3a77781af7a8c2703b769aa53f523aa5f9dde1614043e041356b023acb4ba33730ee34c3013cc3333cc3333cc3a39f35fd68948491b6a76e68a72425b5ee2042782cfebfa04899644a296552f830f8c70363d444a4d90c670af509250a09195deae8b78eb95bee1f8d48fe8978fa0c5a2ad7b3dce5a50f4624ef36e81bb1a32ab5b8e5d3f8939533707c2c22d9ab73bcbed8a3cba2292f616973263845a446cad8a721babad165d7f291887431dbbc8cedf832f60fc2072252faffb662558dbe7fd8f07188c4277bed71dee5c310e920a38edaa3720fedab7346f828445a85569ae5df3b6245844876761d17b29bb5bde201e1631069f5e89ecfc588f0f031e1f3942e4194861f8248e7286c47cb2ff6894a3e0291cc2e8c902d84383f5d99071f804899f9bebabfce6d2f223e737f482a5f0de7c53af58ca367b18490f0e18764cc39afb5eac1bb68bff721351ad4eb728b2b97d9251f52afb634ba2877a8125c516931e34b72b08ca025078984a425076f858f3da4737c763bd79f0b3a3d8b870f3da4bfb4e331bfa77eac3b0f49d1aa5e3ca4cbe56a5c8b6e2db43eee902e7c39f6e665f47d59b3433a5e9ab96ad830fa551364d2f8a843528e54d97bdb2b85cb7c4b7b0e17d8071dd251888bb697d934cad1c71cd252eaacb27e273a7ffe0d1f7248ff6bf6dd56975f2d0ec1471cd2e55aa9214f8467213f296cb000e5030e6915b3599d4ec3174765c4c71b9279b370d9e7baaafd32613254f0451b3edc907ead736c1522eb348a484832cdf0d186848aebb2c9668edf2265435aa7155ae8f17231c8bc3ed6902e765973701d4a737d1b06b3243ed4901022f5766aaf15ff5226ec5b0609c95a39988674cadc2e17b597644c33d040e917e41a3ed090d0c9efe4e54e63fe2d573ece90f8c2f81784ab7df6c6b0000a1f6648db6f8b32f3cdca65aa0cc91c5d8ee905b51e83504a56544ad8e8953d147c9021e9254ffdd3f8c584cfee543ec690b4bd4f5b5d19575c837af021867416d2ebbe98eb57cb921595963e76828f3024be2cc49dcb67dd7a4f140c0911bb5a55adf2da91052658f9cb97e1282726619064e1e30b69d99a5ab37393eb1ce385b4d4b229c3b5e85c5ecedda18f2e24fc3ea4d0cf258d72f70717d2653f59b53e5e7ef1390c1f5b48eab2de24a72237a5a98594ae06d1992d6417eeffc84232f9ea9c936f9acdfac3426a43bdb4ad66cc55f457488ff0317b6126baadfbb04272d5777f8c0daa32721512dae9cb31a78c15b5171552da5ad5a8f5e419b3d0123ea690d621428967dfeef2d852488a95eb31b8f072b98e4521e1e59139632e6b792fcb0f28245dc57941ca67d6d7e113d2390b259ae1457e9688081f4e488bcfd9542ad7f4fe054de1a30929d92cd5bbf8b7da5d9609c98d26373322aaa6dfc712923a6eac780bd5234323840f25a475b8fa3c176abba4f6471292a3eaa2551774b8923f9090b06da9ba2037986f673b3e8e90def7f4f4e5c2767851e7c308c9d1a52f8fb4d9fc5ada0e3e8a90f47c1b26f205edd8d13a7c1021395eec4f23e3e5626a8976858f2124446818a9c163264cf226abc320217993d52b2d01c4f02184b417de4cc54b9d66f4ca8485052424791f41487c177c34a667ed3c0d0889d13269d857cdc528633e7e90982fbca767faca1cb309dfee404961c1ebe0c48ccc6ce3a38db48b4ba54285b6cbcc32e1eb45427b47291ec6e745facb1db3b6cf9b715c8938f0d84532f6baaacedf5317097fd1b1496fe367b07391d6df19b5ce49ed460617e972fced0c1aedcb3983b748ee76bc3dcd71b31e94133550fec0c316e9a065e8cc2cec3dc88d3af0a8455a672ee766c8ffcb74596891f41aedb859bee4d19d454297bb9b7751288b64cc6776f8f8a75ab209dfedf088454265b356f76ad4db8d4cf872071eb048bd5a1311fa0b3b5abe22a14abb3fcb8e9af01daa78b822a9a516fd85771fad37d38a8418cd85f7ce31ba5f2eb278b022e571fb2373cbb0f99955a4c7b5f4a232d11d140f55a0ef99eef9a742383c5291d423a3c8973d74d6bca7818ab4b78ef2dfabf383dee61409339379254abcbd34b3a898b01a1ea64886dff45954de08d598097f71860afe64a5e5c4a314092feb976c46113ab64d8aa4ba6b7e47a9338ae4eafcd8967ab251b71ea24869525de5b2be1dafc66289472892e541ed37a4e8d2a7c7840fc7b79c94b0d12b79667880223922d3557fffa9ce3f8b9ec0e31376177597563db654c04697310212fcc9ca19068f2712afc3e81ca3b92cb3beb034cafd0bc62021d1f56ba04751ea24c5dcc0a31369991d63438516196d9bf0957c8aca211828288dc30c3e1478702299a93674daccf3a3719594db44ba45694ba1553497556d8230be1023055f84f18509d6f0d0443a779459bfd8c16647c72313e92ca42eb8dabc136dbd4a4b0926921b1a1a5c7674f99d9af0a15b316847038f4ba473876dfb1cb3521a858c7795310e8f8146497a5822f19bbc33e4767f41d7267c298d9243b909de55c62069c3a312698d652722e651177438c5831209cf519fc67531df8e1283c72412ef055d1fd733ca7df590443a43577a517fcead0b72203c2291fa2fcad81c36aeca9723820724d25247ddb7215cc62e968f48e8face60eb72af636ec27a7a1e8e48ff68ecb2d3cd05295f133e944e59f9164696151d613cc1a311201e8c60791c9d5212018f45a4367deeec6ab3dfcfd26260e0a188b488d741c897b9b96761c9b1c71e89788007224a20e0718892931c60a89c81010f4370c0a31038560e18e04188b4074d5fce1521f6b72983c7204a1ae0210896ff4bc9053c0241010f40fc5f4a32e0f187f40b1f9151b5acf8a229e90a1e7ee880471fcc830f2a97941314940e630f7ad880471e1242cb155eb28d61930ae121031e7748c6d0fe9cdafb41f7ab24e5528283a5d928f9965e6961c9b443dae3cbf2d587d0aafbacc30e0b78d061021e73a880871c92f26ba5f4911bc4bd748ebc34fa0c334abe65c708c2202161f9961daaa42445e524141e7160d9f16a5cc0030e17f078435297b763fadc233586f4534e140e958567dc90461a7d06073cdac08674a87fa185a84dab6ab386b4accb1632a3c20ba622c1430d16f04843043cd0905c399b53b50be7b24b40789ca144021e668080471992217331a8d5fb9863220949143cc8902e7aafffd7c71e6348f7ca54ed2f6bd15fda3cc490962e9bf57e86cfcc5d3cc290d0450d593e1e939735ed0186a4fb06b939fbdde56290093cbe90d6b2cf7c33857ee9bdbc1d2b87041e5e486a217755cbd8baf75e4cf872241947f0e8424ae37fb8d1b26ff35f0d1e5c48ae3c97e5fd9e77f74507e1b185b4a99c7799353e67565a727868a125e091856446f9a59f17195db4c7030ba915a1b5b6f1e2a6aaf9ea718564ec7241aee61832fea3de0bf4d6202139c14989de1ab972529283453dac30e25185b4dc6aadd098f5476a4df850ba84b1e504c7094a89494b8ef71c785081011e5348eaa031b3e872d1c5ecc1430a094d113a6574d6d4dbd0c1230a490d1ab450d5b71d3ca070c3e3091e1e4e484a4fcf29ef3f695adba3092519f060c2023c96507292038c5ed10106033c94f09f6bddd5d47e12d22fa46b0d19d367f5e2945e2c1e48488af996f9ff357cd0f01192b5ba2fd4abcc08e9723173f98b5aba3c8ac07924cb8308268fa43d8690bcdde41dbaacfc3dac2b2d3954e02104cd23496ccd6695c2566af68f1aa31e4148ec8c10bdd26e20a48b5964e3da98c6d61e8f1f244b4fa44b5d2e2651ee8de1d146625ec5baeebc52ece714d08bb4dc93dbf19cf20b3a2f526fb7c2de6c5c68195e514949e3c4d450c02e029828d55ca972a78e1028243ceaf9763957ea7dc19027a4343de6e26df038afb18438212d5a978554cd6d32e2e0110d214d48e617451763bd85862e6765a59cb01afd30600f6142ea379dabeb4f5fd4e179aba09815842c21994767f11a4f934347891a2a2a28a7055f84812e4409c9f0e263ee15ab0bab83429290ceb221527717d3e7592121e95e546523b39f5f9c51821d9f821c214748ae8fc75721f44648cd77615537486d2fcb22a4f5458a5a9551a98c22212142ba658c511fd35baf78703cb3113284c40bd5d632964acf7934438810529af368cd9e7572a93c214148d7e7ee5ff1b91c8400215dde34ef961bca3765945e791c6f922391906c5ec80fd29963eacfeee66f1a9690369222737e8d6a73685a9d1e905e24cf73e1cfcd44b41095099ff21296b66301630424f05f9dc38c1d9f821e80f022251e5af75b9727f3281306d9454a363d261b4d57542201a28bb49757e3288fef9f63169d2440729174ffe2bc8bd610951dc335b848173db47e979f3fe7f0dc22ad579cca38dd6e19ae09df4a1f455ba40b9a5d2abcbc4bc506a9453a7f31e868ab356669ce195301428b74b963ab79912fd1dd9a453acd973dc8e4eb22527964916e1973a7cdd423bda88f455a56e47a86ce79d5e8608109fe64e58c4980c02271df597ec8117f5e5e9178dd7621753975b88c72455a17766372ffb91dbd1569fbf7283d7b78c72ec88a7469a4bb8ac48bf792cdd947fd7953457aa3f61eb1d9db73414e45ead55fc6f5242374494345f2562a352fca185d5ecf299265b2195e6ece6d9a31455a5c7ee9be3eb8ff079522dd6aa397938d67f93d7b5fcc59bcb347661a06192af8428c32be3848808c22a94cbd5e6bd4a248eb2f672134eb3c14495d9bb5cb79c3cbc5121449ff7266316db539be984fa48ba923eba39427d2e24b22a56be77422599ad2bda0398a13094fd5d62c5bc7e0a93d01d944eaeda59049b3b68dfd494848485872a86822f951ec878c314af5aace4452b9508f32c7161df2020413e92dd7c1c70bfa9578614412805c2229bdfd8ba145688eba5c4cf8cc8d01c41209b9bd1ed353e7c000a944b2c3df9c467ded42eb12c69fac9c61890238020825929a8b494667eaa2bf90555af2f4804c2299bc47d5bf3b3f879624d229a3d6907a7572cd191a209148ab902f235e7a741f2d4824659cf528dee2f58f8e3d22b5a24197bba57cacd025850d9532401c918e9a21d3632ad1cdaf1d208d4887980d52a46726f50da6008411e92f67d617f3df219045a4e45f888989ac789da0b094a034a622d251e698cb31b9f8a266cc9d014944ba18e5a6d90f52991755267c3b9c1191b04df1a1836654afcb99f0d913400e91ceba858c9757a6fc3fd504c4106953f5a2b85eed1bc3cbc1c9a510e9926a1764d43f6eb39010c9d278bebbefbb397c8348c87751031144baa0e1e55e4aa12dfa719040a4b53c17765ea7a770b7e4534e50524e140410498f3777e2255921ef1f1d28207f48e8d7d99e63d681f8216177bb427dbfc6d773258501d287649637cd79dd81f021a12a6e8458a9cb391bfb00c81ed2e5513bcfcd79efcbaa00440fe911ddb19f4147f64be621d9b265ee52fc36d637615b03081ed2eb51befbbe18fd6d84a252c277486ed435c28be1a94b6f8b0e1c6980d821f9a373b54819fb12e100a9436a73355daa2ebb765968c257e2bfa2d24287a4963bde2db5175bd68e6280cc212dbe98396ae3aa7a1153d850d994435ac839aff172c1dbd5207148e6ff0c2b64bcf66584a35756c0202111a38c2f4848c000028784bfb7a62ebc4c7d57544e4a8e05206f48a8dc9e2f17c417438f2e1b06881b52de653942decab421fda1d7cb217a237331c586b4a676175d66b457a9ae2179b251081dd253435ab98d7419524d433a368950ea2af647656848a84d75a37551b72e7ee80c09315f0c21a51cf97214198819d2aea3f36dcb9851d5d9819421f5ba3aaa1ad12985a9eb2a6318261032a4349dbd47818c21f5427ace2cbfe0e582aa023d54021031a43e6ccc0ebada74850a240c292fea0d2e758ffa18bc280e9e04eb5f70f83a70f05eb27e0d4406081892e145cd5efca03c5564be9090ba20a4ee97b3bcc0abb3b70bc9ed7e9dcb9b428d1017840b4953a5bf2fc2c6c45bdb42526cc3beebfabc49935a488f4e1b625ed65c8c51b3902ea7eca2de8e25e2b6cb1440b0903e171ac3ebe8288376b94242675d521d590f55cdfa9395335a74ac00c40ac9d7f7fafb43cbac0bb22a24936ab9e63a95a6f201a1425a85102a3eaf692e6b272b2dd60190292464165f7acf2ea5503e4a216d779b75504ffa65bd45219d3526ffd779224d44298040212da5ccc5f2f845d551b50a8b4134061a252800794242eb28c3cca366962303e284b44eb3f3d2ec8dee5a419a90ece84591c16f0761423a79d45c5ed60f640949b55f325a69f8975e5e09c94c35edbdd956a91e4812d232c377e8a8ff43e612040909d9ef627ed8155ece721b801c211d346d85eb583defc584006284948b76fb38bab5f6d245487e0c5ece30ba1cbfacc54448d87679a41754de8710418690565d8e52a4fd98cb1f0d014408e982fccf396f87792d070942ea3bab501fda9af0edad800021f1a75ef6458db9ecb7a4741a8b4d00f94172533eecdf0a17dfdfda00692329467a280d1a33b588a517494f9e5d0b2147fecb2c2728df7262c28b74b144f5c85ceff2bea8925da4fda3f45c162fd5a1a28b74868d2fbfdc3ddb32978bc4d67fd97483fe393bd358f1435ca4d55b8c90d1753179613e61436f91d2dc25d14595c9b57a69363ee52f6d91103ac4c8f3919f8bafae453a8f4efa625e128d5999f019a44532c71c4397a5a7cbcba282b2d8101fb348ea502ff3e6d1ab367e8c133e64912eaa175ab37ee7c99d86b148fe6a3dad8fd2a58b2eb0486f7ce1652ea67a3df67a455aab6cc67c31724542e7626f487b7735d7c5846fb1091fad48bf561993bc0adb98ae0f56246d447ff96354afeca70746ae22699e52fe0b8f33db5e5491f2f41fd765d6b119ea1615959583858f54a48bb69db9ac2936cb175d34061a25f8818a94ca5c3a95724543954bca09c2f83845c263ce72b2da645f7b3145da57c78cb553add378ff518a74d14ebdf05e50f3f3ba902219e3ecea5ffb7241378a74bcf6cf17225b275f14097f179e212f64bcd55024352653715f72d1ab3550246c3ca716f5aed7518be65a72f089c6c72712eb9e66fe83ebae2f060f173e3c919ab5ff8cb9758d5009de191f9d4877eed477ae7f3fbadc072752f75af966e86c4ea5b0d139586e13c934b2b9689f4bfea2a09ca8a189849ad958f667b29937387c6422a13366d54328d36c1b444c2444abf32f7aae6b501913be96c661887789a4961e67e49f89ed082f5d9277ec04f16189a49ef762561532eb9c3b6af8a844f2a396f1da45d7a8596ec2b7e3d5309448fb7959e7165dd4249259d8dec8caa87551755412a9d77f997378c7a8b5432bf8884432bf2c642ee77afb0d66422229b38ad8d1ed72f218f30d3e1e91f6f2ea77dd348d1d37a7e0c31101f9c60c9145a43cb8b9af58d34ffd2822d92f1ba3d457edfa2c11470f493a173634c82e690e11e9cf1aeac5789d5fccba09df1e2221b2a30e9b44aa6bd672f49b94a8b420ee483939c3dcfa3576a49cbc40c4102971add3c6130f8c535288f4975dcf9cd6a74aa55a121142243744d7a88d26db51fa252b2a2732888498c7525d98dfd579edc5f2253978931511419c9a23496b10cdb2c57f4ca3771a880422a951bce87ad447e8ba80488988ce5d561dd37c528521f28784dea02aef959cfdeb45fc90980d3ae35e182d83585d90a2c28202913e243ecbe6361736113e24cfd3566b6f9c88d6b3ee21a95f7bfef098753ff7480fe92cd5863e0d238508ad2088e421195c7997c53bd7e39b69a49cac941394153c24b57fd62d47ebe8c7fd84848465c7ab4142c27748967a1c2ddf76bc1ac80e6921a5ccfcea97851052133ebc6561f9951c78db262c06db8445c740a3c444ea90eca84b5e7be1bf28e5299d23076f767c0a5a7e65bd192729272b48457935584c840e69fde58e22328a51109943ca5ea68856d3a8af5139247588522927fe3ab42f1287c46f58d1592b028784149bef57e897b9a328f286942bf92f978b35372bef12714352c6d85ece1ea396a3a1494b93414262d2d24242d2226d4806adbc5cf49c5b3e57cd86f4a990325367ed89c81a123247a9215478ec8c9d22a286a4e6dc17579dd385ae59584e5a521ac7d9d1d2f22b398c8e1d2b99868494b1ddf1bf30761a113424de7398f5cfbaac538a24242424bc46e241e40c691d467adab82d4df5ce30435abf94a9a9cbe5cb9074ff20bfa42a3d5dc8c9908e327cb9a4413f654ced189241bc7d14bf311131247454cb149dde95952ed9914365a5c58840240c69ef2feb6c2ea40aade3d53958bee5c404062d272839cc58696111014362a5ad8caad37e3e6d95a49cdc0a0e8a7c219d3b7e08d5ba3a973724e285640a1d3436bdcc9d8ba54817d2b99cfe994196db173c5c487baf4b3dff5cd4a9e35b486ee662f66b86f2d7e5a8a4886821a5e62adf695df5b44d912c24a4be66fa2f684a95970816525ffcb2dcd1b712b942323eedeaf8beafae61112b243f7f84178597937f525521191ff5bc487351fdb9645221a136a45c4f198a4c211d85ae797c350f196e1129a4456711db9ca5abbfe844240a89f3f6a83faafeb2969a3644a090d6713d6ebafd2de42ff28474c96572d95efc449c905ce9abebb6bc77d41dafc6af28a6915206a221d28474c154ebbdf8b22ee6dca791f228286db2b604224c48ae88ed88ba5b2f882a86c812d29e3546dbea91d7d6b9961c272beb3c20a2848412d3cf3c22912424b5a89997761fd587f21c3c33d02b95951616937282b2f238cc1868941c112424f36f75da302e639499b9cbaad16bbd1a660c344abe103942f2f5fd4ff4b5e7b296891821d926e2ba8b42434139b9d88591464a1924249ed22b65ac939426838444a408c97f2fa9d96afb1bad24426a3d3d754b3d0f14448670e9fb5d6aadbf8b8810d2a1735d3feb9769ef8b894810d2d9a6e549a3ca7c41de4b1001423a6f482dbd2cfb5ecc4de3449578ff4a5e0c447e90109a1e84f8b2a88b47c940a48d7497b310cf902eeabc642fd23217663f0adb9ced258e15153170aca85c105e00c410c28b74ac8d51dfcb23f3fb4bc82e929e32175bec26ed1b5d882ed29f5dae97d3349f852717c9d94d5ffc20de105c24d366b7ce5f7cbdb07bc82dd2a3e365d6fe5597e6242139e3597a0424240b89105ba46bf3bfa03947f5af5d6f05a920a416a91321b585cb58cf32bfc649cbfa1ba8ac90b13508a1454af63ca64dbbf18296777e71460912893b187801b845c82c52a37d23d5f676bd8e267c25ebc45354c220213951218305e54fc6f89493cb050c1212961259a4e53f83ab528d695d63c297031555a2c66a14851ea545e9603163a051a2819058a4eee37d3153d67631d8267ce60c43038b948667fcce51aa7770a1e8580109090909a60e12922fc428e30b4a84bc2299c3eb6bd4bc691d3860c15321bdaf96beeec5d5403654726c2b9231ba166a9f2e9e568aa5cfa2c1e4287f228661453ad9fbbff8ca2fae2777ac3c0e95345a525470e8d1206415c991b9eed5b6d6980be3091b2525ae82a3c49411a28a8446bd17b9217388950e964468c65969945e2c0653471a2e21232415297df9be6fa931e1632931782d2c2a2625252ca84508414552455f872e7b9659329f222de47f598cfefd0e5aa770083145d2e64447a9bd5d6ff44a915ef1a729857aae0e27299219a39651480f93b37314a95f5d2cfff3551449fdb73aa2ec5eca8d4391de243a6a7e512a8c3d848022a11ae3175eb81c9b1d8710f289c4ceea4fa9737646d72ddd628627923af996dcc62e280fe66a21a413c98f5ddccebe2527125e2ee7dcf925dff4f10bd944329497ce46bdcf96290949882692f1c6bbb45f7e9bee15928974ebfcdf7ee9adfdd6104c2456c8967175ebdcd63817422e916e196f3b5f109a193d2196486706e1abf7e556339e4a2474d8f672c7f0aad91542896450fea3ad5a7c41563689b46ed032b6345357e69144d2cb1f447b31566aadad0993900ce016219148e6e7da8c2f87171b732190488b6cd17a44c295666f905e0c5e7035c41189f5949717f7d5bc5d421a915a2dee6bb5fdbad04f0823d22722eabdb1ad31be218b48c748d5aba961b4978542149110bd69c42aef820cdb0a494452875c2fc820b3104310910c323d7739b5f010c9a8bab4efb5dc511b443444da75ec7ed9f562e4e7100b91fc6db1a34b6a239ab510ad11291f421c44625e6cdaf788d1c52e280591d0baa875d4ad19c317f7244348209241a98c994e948868901a42009196dde275306f1f8de2cc3fe4aee3bec67c34d30f682eae6fb972bf0f09edbaa94b7ebbbad47f42081f12ae32dd3e9ee66cf443f6908e9a2f745e77e51442f4902e1773946daa06a14727240fe998ba0b1bbe98cb6a552178488a8d7a856a0d2621e40e8917badc3b625e8f3acf0e29cfafbdb52175486c8afa2c65bcb8104287b406a9ba2c5f4c1a57ca750ee9f83ae6a507a194437a4606252bf7d3bb4ea21442e290b8152d7da4e77f2d35040ec9105f76e1b93cbacf3e216f48a917b3bbdcd5fa4799103724c58bedc5609aa395d88684da1c84b87fcda8b5101b92daf2bce0ab74559626640d4917eabcb5967b713d9a19a28684665d3e2f898c42d2906ea952cc8f2ed10e42d090982f08d3f9a2173b44e60c89b37bd9efa1f52d8de3a485e5757062c6454565850cd50b31435267d18c59f59817b5aa0ce9b28e1f3554ebf7a2afc890bef18ce65aba798f38640cc9d4f8059526f7c5b6590ca9794d4de227ee850f8621e99a22e5f686c6f0620e18d2df5a3bf48da99eac4c0a42be909e4f9e8b219ef9bef0c1102fa4858b67078da9bdd8cac141481752ee1f5dd7a58a0bc95c14772334a5561ee2902d24cc36ea188de92084682121f75517b5ce385fab9d21240b497b0d97ab352ca4d3e8a8fc337acc1a5f340e2157489c6673b1efa51b4fe6bc102b24ecbe6caa3f72b3507d4815525f969fb3ed6473fa62ee10428594a6d414ff70324e21f1a20b3a66539952dc2e8584260feabdba5223240a4939031be0eb405a07140d2d809d7ceedf1ab080913093dc04560b8b8e9502046087ea80470058567ee97095951c090001cb498ef608c0220010000074800421620080854591104000ddd24e528000744bbbca0ac901003000072460e54bd458ddc2525212028000dc38400e1c5e48bb4e5ffe1c5da22fbd2ea45b37b83c195555de73e10037b6902c7d995d64f0a8cd5c381e079b1b5ad89163652135775e7cddac5fc63b267c3b722c1c8fc3e0781c7b030b79d2e22a38ae90272d6b9cb88e929204dcb042ea9599b78a321fe19d091f3c1d78387ea5648d132ff1d7c109ba969493644939694139b931d02869c18d2a644909026e50214f5a564c58504a4a1270630a79d28262c2d2252509b821859667c1f1380c70230a295dd71a5c5746ede5dc8042b285101a450ad1395e18b1e45029b10fdc784232668f591daffda9bd091f4aaff17a82432565078a0a19ff621c6411e3861312baf01be2fb4519e489379a90149ef653d6db75baf20613d26557aebd9cebd273883796909cfbd474217478d4e586124ccd9ddc484242751745fb679161e47b030989134fa3fee3c3a8180d1292d7c189cacaeb608f90d62e4d85762f8f16fb1a272befc606378ca06a8ec46f14212133eeb9bc1579199a5625262c3a72a019dc2042d2cbb3aacd65a93bec299fa3a465458505594890f82c2a39709ca4e8b821a4f3bfbc5a97b74f4f3eccc19ba0bb2184f4eace972ff37b4117342d9fc68d20a465ebd605f582d452688e09df49491a8a253780902eebe720377dca848f6f6ffc20217ca4d6de05fd9f5e75a38de478d611b2b79a7b847a91bad7f6e22e3d5ea473597e8a0c62dcc3c7ec22e1a15ef66598b6df175d24458c947a5b8e6afb6c2e92e6b177e631ea9c75595ca47f3edf4517e75ba4cb2a55af8bfce671d716495d2ee9bc7f6294f1c5da2961a4162917f62dbd3d3fa50bd1221923fe445606cd2299aecb625575d57c7ab248c817bdd15506152144b148bba8d995d9bfa35d18162977df2f07ffe2af4898978cbff4f9607ebac2d0b42299d57b369bfdc28a846d9622f37db9ac22a942c6943ad6a88ab497a62eab2f69a9486697299a61350acf172a52db296643d49775a8f3146999f5f6666ee5ce698a745986fc18bb52b7e7bd1449fdb2665d969fedf66d52f0992349d7a77657192a472b7914e92f687ddb0e2251a4cb3c881023b32614c9d59abc752e9fc7389109dfabd16780221d743185bfd2d8d0a34f24eb8b71bfa85aabae1fcf13c9175dd40597596812239d48cab96bd0397eabf81127d2f5bae51edb85fda27a13c9245ff6fbe05f4eed42554d2447c6367bb14bbeebba8e6422253be26c376d3091ceccad3e6bf660afcc4b24b48b9af6346a8994b7ca2b9142ddbdc72b917c155d1be5d39bfc4989c48ecbf55c72fbccba9a444a366a8f0e9d4b22f1dab3d6050d2f1ddd4622a18b9ef37a39ea0289a446e905bf790f7ba327794452caee76a8e6173555e888a44e2bd3977e45eb34ae298c3422b5f1737bc6b05297a3cc0823d21e55674b6bb47fbfa230b288f4e772d17c356dabaa5c1189cd1874798ad3a04bee482292ed27ea11442474337aab50de9b79cb3c445a3ba667cbd1ababb30c91fcdacf6ce75a662152f69bd1c31774c18b5e2e998448b62e677deda5979b32287310c97dd14565ffaa1f4ecf1444da3bf768591b7ff3db2381488fda7b69fe2eae943120d27b9f0b769bc5976f75997f48c917c5338adcb12d35d30f697df7a34ed40b1446fa90d0a0566ea310fd71657c48beac5c2ddd34ea51e6c81ed2326a7ca99a64ce3e5fc2881ed2514b376a6e4eaeb385ba309287f4dbafcaf6ed205de677040f09d52f436a6f1d2e5dd9c6c81d92597ff2d2174584ba50238ed8213532eaf60ff5de59785618a94352dc68be33d39ceef7113a2446870bfd596f640ee92ebc4a5fa9c5237248bfcc85cdf590fa60240e692923f734eae208841138a48ba1bbd5caf4b4d25d78a430f286840c23e63e796183c879c40de95ca92d5ff2d01db65d307aa40d29adcf3faf19852ea7ce081b9873640d099dd94b1f7517a31733460de972996e99ce2a57bb8da42131f29acbaaa3949b2f5a328206e43c921b8c9c21b923b45dbc8ea6bc098b8e1c3cc6881992d9bbf05e2e8698594d4f1992befbeddd05d749bcc5644877ceea9d0baf3463487ec98b59cacc45570cc9a80be24499775851ae8621a1b6f93acf7e6976996048baae97acf8557e2139ca735ca1cb256d6def8817d2f98579a19bf754ea74a40ba9955f2e66a86c2ebcfa5c487ab9b4de397ef8a21e31b7a0984792235a486c77fee6eaebe7e792b390ce526bbedb8ca19b0bc2c4423a68e8b27b695eb367e40ae99dd7f159d363db218c58211d3e978bfa27cabc2896933d2363a40ac9a0b9a0d573d466993c292354489c867818d3d9cfb08d4c21e9e6c530df1de4d9e846a4902e7f505e982f67610d46a290fed845d9e5783f8e402169ba3a65d59e234f486fca74ad95bc469c90f820e3c4eecfd595965a1869425a173f3c0a9d5228bdc4a8008c11b0400525303bc2848497bbbcc2cbe56cdfd11b5942323e97cb52974b7f9f5b3ba284d4e7cdb9e0ba58b3eb45d950098384840d959595208c2421dd2a5e504f5d16e23f348284a4f2fcb934e6922347486b3122e3cbc58c374ace91438711d29e56367e3a11e5e2929567430505478a90f6ce723575dc4688904c59eb256af3c810d2416d0be9517bd176be1121a4f463bb5495b6229e1b09426a4f93eafe8c468090f86287f28dab8b524a7be407492fcbcd6a44cd888e37d246e263da7b596755b789bd48b95cfff89c7c115ea4633cba1ee15f522fc7a0b4c82e923a3533eb3ce28596bb2e925f924f259ffa8bb5ca453a7a327b35e6598468ed40041769d1ef2f46ce97bda37b8bb4ed8a17bcac79923688d8229dedb58f0e792a7366535e0d95334a4e0722b5488b7da9fe69ff3146a6456236c68e41b9ac1fadcb2c125ae8f2db9a4861ef6a13d680882cd259f686a6a9169dc31f8bb42e8677697a864dd350418145529736b393b7d095d2cb0a445e9196eff219e517cf658cebb9221dc4fb5cf4ab15c9122984d87bdd2ed5b2b022a17e2b5abe77c76c6a1509d5418e70a52b3d774a15c9ef726cfdec92ae5a5d2a52e6bac37ed6741f2b2a52b25206d7ca8550a144a748979febf49a2542eaa81a889822e5f14df46ebaac7aedb014a9d3e8c5ecd275d3ebe53b438414692f76aaf8cef15f16916514c96c2da5a24817748ed0bf6f9e316a30100945ba85909d3bacd4ebf9074532bdbad4ca750eba5ffc13e994327afecceb0944bdf8bbc9e74b26914e24367e647edf3861bcfc7256651a3db789a48e394347f5a258f34f13091d56b5aa0873b16215c9446acc0ba3346c2c10c14432e468abefd776b32f7b0956446289946c70ddc5cc5a5e37c6042295489fef8e4e7122f24415a14432c3efcb858777d82f229348a8176d5f545e6c2fb6940b229248a62e66975fcca5f3820e4522251ae74774419eabfc9fe506229048eb147aa4d6e834f2b5931517f023125ff40fcfb0a523ccf2f062c9f6d7083d87641991545efe64aee3dd2b8c5ea8c4302613691c1206845110c4300008cea32000c311082020301a0ec703029960aeab761400035b4c2c4c422e282c208984c2a148241608038140300462180863208a81400c2881d875635647b87ccdbe230f18701ff75e04a8ac4ea45dda0b950564eb7d84cc3f5a0ff2fd64201c5940d937664e1fe2102e0a24c1b8996d8880027a6a33ff9ba473f87fb4888679caee2d399e1ac9b3a3bcdd6e904a6e790bdbefcd298b2fa5dc169d150815c0d50a9a20f2cfed3bc8dfa766116314043773fc366f63c7437e49c3a446133a9976886dfdf6223831855fd0f003efea50866771fbc70c4ea82b73fc74a6ef5d29d23ef1496066efe3fc1c74079951c1e697818e08252041a58cc4e009975d33bff9cda9e2e5ef810f94950497bb3240054e066bdfa042373fe590971e5938f0ca2368f3592432c91b53aafb0d23306aa4e316c5464a70fa1978301d257d1b635224b21a790f7c26a036774ba7daf2931aa7abdf858358fbacbb2525127cfa653f569a87aadf0f785834e031d2f987edba9e8e6040c80ee698d6fa49f8d07785b609b287eaf907eb6694ae884c0bc08f2be8e919d5ada9cd2e176abc236dd6e0d5d8ea4b68148f9ca59f9b4c2062ddaebba436bcad6b7919debafa23edb0b6b23685acf651ec29da3010b5367dc6dc02785e0fc7311cae8d9aa357e922ebbf6be9e93368ff7d8658ad950f666cc66773994e551bc944d8421a7995eafc4b4a61e28858913e65ae30dd672e597adcc02c3eeb0f6c4b1e3e71af8502df63ca717e094b421077abf650cf6330af1f8cd4edb0e90b11790cd64be9a787bd4aae93538f0f24bcfac315b9f71cdf79d38f92e2425945c0ce8155005530c286600c9f6d5b30c90f224209cbaeaa8388ffd2bd07344307ae8804be702b2b9fb8d62d44b3602e34208588209f31b1cbf2a6811b77b128dd1cdf5fdec27059d1218a130e4dee4c03805489132f6de6c015911f944e8d15700d14989726be13f572b872373fed2fa8446e6fb2b02ad9a28fe2dc0ef9a350d55f6008986bdf39bec818c43ff1aa07d3617cc4835515cdc83fdcfbaafd72fa2c680f15112146ec23ce4b99911f82871b067d1641724b44af116d63476a44b7c98c76fd7cc2737ed103c58f63e28bb6022a01d756cdb4903a808518833d0086ca88c15675da0d1628f3c98c1069c10aec9e20320fad47881e4bcc94dba511969a63f664793f73cc4f56841722c296a6cd4fa05737bcefc91d01da6cff0c2a38fa83715bcaf411e75cd71e856759845d5b26758bce73116c56b2e1ddfded07fa5cd1fa3a78729f83ff0afae2946478a08af8d5629bb7c4fd001b0354963657e62be908c0d1021cb51be8803af215c13bf035158a4c117d0157e1a36c40a2e719d89e6aad433585518bf4a06c24c1ede01e807ac5452f28f184305132e187108098bedf2bd1dda989812a6a1bee2027db1c278b834a7787327525af28e66d1675cd1b6cd9b914d0bed84a601a265234dd3014611050f39f635320156fbc501f745321ebf6d3cbb0ed1fc5b29558f7a5898879ddac0a707c65bc30839f21539176c8bc3096241e0c696da6a07af0708559682e63c340fdf6a6ee1548822ee22279c67bfe536160bbf0994e80ab9bcde2b336d4092fc650b79369530bfe6f00c0a53e83d780980791240444dcac1b0a07ebaca7abf25fecd066f5625a6fbceb1918512e6a3477024a7c0e0c31e06cd5f36993c66d59c21b68e72de1e43f65678c3f45602a264845612ea9255fd27d82a1b08bc940cdfca3bcaa1a4e1b9c9e28af4505c0fa495be20df71bb7e90690ec8686b9906a2ffeb69932177482a10f31719bae8163d961d49e3dd83bad6ec121d4dc1e6df3ad20b4596ff4e459b11c328943dd18be8575cec50dea4056b7e3d7370d0bc80f939ed86ebb61333fb58ce2e73719526e26c8cad2e743b42caf27d241682a09a96a9828fb6446c82845c58c12658673ff89d5163b1ab01a65981a03a7410e726ffb8a32581469b0ca342713406d0de2468d54cddc3b431933e070007088d37fcb9f1d96892f6dae5ea7f93093038721e22bd982c4744d058c5379a7db688038b3f14c5a1fc75a9d290d9319b102e292df3fc6813e652cc2f48e29a0a3049917b423df93fef9698b2c469a090ffae68e8c6a7e9daecc1d62766acba3bbc2a38c12337cfc04bb52da8a57db046ddf49a9297096775bb6be05e1fbe78f023763e6c02bb8a13643351508283a95ac2bfbefa7eebb709104cc9baeba66e411e1cf51b76d9814b626bcc4fee007417e4e0022d7053ec6aa630446ec028a091509b64074f00d883138023281385b5cd81cc46f87c2b73880f62e886348b70e815a5cd8e9b9e2487641695be0aa92e6237f0b6569e962fb0e346799a13f2a9c785db815731c351818551bf1c056927002b7dc3b16992d606491b462d9bb03ad55335df11839d0d14c49c6d04cb72c0338005bab7be98dfbadda4d756666f7dc5d54d18d1137bab2ef04e48cf9a7c5acd2829b80b757f9afda71eb086ab8309aa63bb3bca675b609ef133f13210ab11b0d54f3317b49d3885414ad2a4a48ae50c3c491d45acb10ba60f30017ce16419a01d5cecaa922202ce75172a3f5cc6a1d59fc1dc1f0105d8ee72b2af027c77de94f676d5cfcce0b41d61dc28e97fc22920b0cfdfbd6ce2c958de5b2730587d800747cfcae0c311c960e3a6f29cc7383de497be0aa975dbafa84d698611bca406151e9c62f7c1ac857e9eaffbb301d5625d6a6050a0a0f00f59d7d06591115c2f54ca6f400d308dfe7156326d1bf2321af805b112c70b98378024c93bc2cd005fa77e8067967bc4dd5f6036a71a954e299bb1196023b5404c437c7333a1fc07bf276d1f5e823aa87aa22f06a3bfdeaea8411092e395d92c2520a72251b4a281bbedbc6428590cb04ed9218266551f5c6b7df09113938b440bd60f57c03527387ee8c024cb58d7d0326ac6509ec710bea7780d732091a5c465228c629185c6be7769b6b1be64f3482f269b6d4592f001012b49d6cd9383272c47a01f561a5ec7784e0c936a35604a2f794f60943fe3f32ddd66b1077879910f5153a8502aa665219953b7bbb3c38d43778b2ec7e04244f56d068e18594c423d215969d778d33ca9215d01c28678a4744984fd1127d4fa1b5580c35077d0055600229cc4d4d74150ad47447ca2178ccd07b428593caf2a3eb432b43223c41b3260e9eaff5d60d000aedc91ba944f9560c96f232c60e98a2888f39177a7db09013006a2ad016418c30c91cebfb502c7cf474c4a89d311371500b5e28c7608c8a4e9ea24c37ca7f12e8e8d1a0d54c83a49df514d781532767945238a0766a4a13c69ebadac6ffcd9c50420d904d4d2982d0159bf214b4ecf75989be8348a9d98153d10a113170c45f39e74ec6c64c6945556640b06630883db229e1f246d664eee97414999b446fedd17e5649511cc0cd99b510a2004e93f809934fbcf44fcc8ef2b405a5f02400fbe6ffcecfafd607744cc42bb3d3601c2768070551d74354e530f1d2c951d06db5718c8e74a9997f10133fad21a0a3962786f5db88af0d39181847f6d3973160744416baaf23b8a8840c84a612183a165dc5923d4493943610bfced0e0af5aa72fdd00ee435aee27312bc23165832b62eb6515838822a6d9012e850568a7938df0810cc8e8d74443476bdaaac38db0132ddcdff1d6f2c3895d3a34478d3a3a128024fe7a5b019093a7857f5e0c265ef7bda4065af1fd764c605a492039b262157fa160ddd4419ce819b7d70474c25e56aa97a679ca0d0a7d786d25cf3dab7fb76954f01aeb9cfcfcb31c849b120324e5f47a939b337c10bf4fb253d4762dcae01461ab8df66661022977924916c7bc9b6a55a4782fdd3e51eaf2ec835b1e91db1fbab52e9d54990658cfa0c5a66ab8587878157a39585bce70af906d6b3cca382975d3fc5bc6dae39e301edc59c62561501101df094a4ff853dce575d473f6d0ca7eedf721627509d1319faf9c32c49f6b28d5bb3f25f3061b09f7e340497fe17fdb2b748c2629176db3506b831876c6622128ee9616b6beb2552cbf8801947ad629b7cec7498052fcfbac9112246c5d43225d6cc907e17b07e827a28c7efa8c0821d118a39b93de134344141eec6b0942a0e4ca8373d14d4de5a06a5011ec56aca91dc33a4653948860b0e7bd455376affa517d2c9e83e60d548329505f2d8cf33f0d626bb01320ab31dd7ec00778efd756174303ece6706a9c86c937bee7a18bc9bba753c7736d9c47defed902ae71090cf0cedc4be255ee253a1f8149047a1a6bcf7f70e83c94941c32dc23e4048bba96388b5612ae32eb674096da615b18a57e021ecf0be630bce0b675a78ea759172cd11efe72e8017daad6912855f3874863fa8341e43a2d6d99a6e2ef712a40ce9de1c8a77141731a7bc31495fad18beaf2dfff1cb07f0434e804ca08eeb68534590b5022c6046e9108261a9a07416108e8c18851f317339ee91655b985b7816a72cf0a3973e4a9a3d1e2d8b92b526e09107767d7f6cd121f63b29c890444e11bbe32b32b1fe259fa7f1c2b2c98bb4e50aa08dfce23c01c3b719a26129cde71e5606ee9652403f5fe0c1e407fc3db0a5170aafc1641e6dba42838f8c6fcb63044ff59230238a6cad1642233e8de1674cf533505d520e636c713b492acbe4960b231e713183493322f5f83b9008332e087ac8b40bc726e55ff649ef645d3c045e895567c75099d36075b65c63c34ef4807ed0e23a57a8405110ab3e683176afb22bac7ea4382d80e35ff1e031b94caa7f4da11fb165ceacc01b7db53409ea17ae3e0341ff580a443bfa4b8e21ce3f079385286cb925b559944ccf2aca1e8dcd643fc4418939b9c954733c617c7a9ba5d89a4005c98d4d52a5491ad2b61470323c65778d920ed4dd7c7782f4e4732893d929a8ec75ffd6d0fc0d18eff447204c80c141856e04b2d02db4c49ddad31da43a8eb25d3ecb73861e2129734a2c4ee3ba3acd52525395c491d8bd0b3fe0c5abff44dab1574809e10d1e46a581009d586051aa1df97fefc247c0bc16b4464f8d5db09ad064901f7456eb39b0d5de69216df06e535cedff77559b52424bda36a509e8bcab5b34b42ae4614dc7a9f5cea8fd99c79de7f1253c6a631a1d98a6c516eea23173884113b819044c435f618237be93bb0c82e6995e814d2782ebdf5f5c0a138002b0afff91ade2277e828dcdcbc9c383919a3a9ceefb82f57ba8913e6bc2550eb6b1e4693d7310814074eb6208496986cd277396a884955494d9419acf1c77542e99972dc4b8e46a9095185b315f83a0f5e3b11924702c6914f2770166cce8c97830d2d3372208d4b8b57da3208ac8cd3c2ad56d3abb78aba02de82ed58d579e8825301663babdd7a66d0de1b3a427b6e911063957690aeadd47193ce03739d717300f0918492989b7074f1835663551a6c7331849452027d1dd8a2499f8e20c18581813e67f4093afcc34d6a4fd0c4c2123941e3e2c06a925fdabb8a7033d0934bf57d60725ed9dc40b97902ffe301d9fcf9c3448d88432749871f5cfe6fc549d1b75b4a76c6b69401ae21a71a5e791440a922053fe832e04bb7cd59eb08f29d8347593f28838ea5aedadf3bfb42865a7d5a6e01e68b1fa12b7a3bdbcef4fe8cf199d8225caf1987815bfb9c2ae19d0c3af7524018d95e20737961cef3f08ab7275ff9b7a61e0c99e21845ba43c5554ba39840a547027014532444fd46258bd8ab74053f6f3819ca8c2bf58ba28cbfff5088457e867900f77eb17b09652323595ddd976e31d28fe8c6ae6cb94648ba16f235d05a536ba82a4a7f6c29efc0aca00fc4c74198f26e0d37521f28f7161cba255b63b4b97458c44adb9fdbc51bae7e80267851d19b512d3d140a1b7be6001435975c75f293865f66db5e3b984d483d9300db2fadcc23b41fc19b97a4e513907c3f3d8c838bf015e5a3f384e17d4c88d53761cfec5323cd2ff5fd2eed6cbdf36488d12883466b6e232dafc88a4e6bd0ad6caf72cc1fd406138f36f461efce4f3bd9e6d23cb3d47fc4035202fa6e08189cc4daa82cc5efe856324dd069c5491be71714974312a5ab564d4b32216555633c749d5aca1f45a70a561a347777a81b026bc9d5166c1e75b6f3b34081df7dad44e25ac4632cd21303ac74ad4c6e6af94a1029a0bec6188a98b1db61d1a8526e56853dfa40055fd86e4e1d185285c1c56baaf8d03972d473ca8878756d849c6857b17cbd3e3276bf4e2e1c1ceecc5c8fc6e5e0a47745c3fd51f07a3a9c8878a781a3555ec16a0b503cbb0bb7ab73d057212f771783a6c8585ac2c5620c8a54b382c9c6d92b4515662ab5f64368a7dd696f77c4887f26508ea4c82f750caa47041291c2c1e08258af9b4e383c1f84f278d994dfc375e5ed91a8b3b03849312433e9c46c59b00acc63a23b4f049d7eb2cdde918d5a0008358952d47c4f29ae469d7eca9ba64485eed639238deee16a7d709a254a905613e9a5c4b5f4411bd0c795db11977cf8d82d8c8e4cce6920f32f93133fc9b75f20677ae9e052f90dd60ac761cae70eeb113d226eaf176311c7e8731571f8859b78cd86039b0d659d38b03ccee3968af48a8d87f3f6b5b611cd6073e41a069bce5d74a10cf909bd8e45676430e49f6ca16973dfc8d02ee60893028229045bfd8d41b1bd122096ff410bd826c2e408d9269b6960820b244822195ff74c0e2374f7c73145383ba9e4a2a66118c5943232047007e649afd67513214881996af349b81be70c0ea6d4c326b00fe05192340c2e848a43146b10cbe25c416a38d80b11edf3afd71c182694715aa89ab202756f60a2a174cea98fa6daa073ad216a26e6c40d1a1aba3ffa535ca8158ca0424b9f189056151703ecf1acbec8f068133f0a7e5259c64e3701ebbeb04c9cc4a9b0f010ae73a452ceba74c539cbd86f62562d6a3398ca06e61bb31d9bcb548a89da2d3c3cf7f7adea9926a3f1870f2da4e606a7c6564e579f83ec43fb983f0a82815027a2f4974841bca206ff32154556b5547381775595c38271aa5b8a8a0f341cde584015289ea569f515c37bd8a8cc5c5f70c2a84590e14e07974b2bd18d69d3d0b772b48416d456052f98df9c96c9324345aa4a03c55ffe4929820407e8d69eb248dedfc4753b867f7764b383ffc78d48377c7c49bcb8b67f41d55375290a0ab18a440df77d465e8cbda4d33c653a534a91064424090a7e5342819961bfe7dff5d0db3ddeaec85b1c3ace0925702ce88238be3854369f68a28513a986e0e4169b94cb852066385cb3e3e8508726e39b0285d17648f5f7ae68e61052fa2f1354ef85f832c64595d4791033cd76297799f6fc5c150be8d14154a98d5f0e558e0c70f69ef63364b9fdee2bd160356485f596abb96b1302a61a5c9398821fe3932c5aa6a91355ec0ba96960131e5a6d8acb2b3ea8c654d5c7271b06367d9193500d162957b2f1bb3eacfb048e817b985f9c8a3aed9ddadbf2cda1718658f4a25bd69c05218617b325787fd153b170a8874da3031a74330dfdf5beae7e0b09670ec48a2e0237734db4a313fec3c75b93df4f39787a09cf96e4eba2081044bbe7834da18681ba45d8f302304904c561b4f6b65851f1d6d6d318431c95a2e600844ad3ee24cfa0eba94ae93c408da23aff9090a51eb91ab91e12083b18377aa1665eaf30eb91ee8398ebc1c74e0506115d0e76b064e6b7ef186d87f388bdcbae8f365a7d2ae327f3f90c7c39d613b3445be4315e184906035ac1c22a634f8cf9ce8aea7cf8d6b1d3dba83fa018976742ff6f4372d26f3a6c023e744074ef78aeba84e38291202d254551e4726c3c5185a33292341e4222b56e629de813203c839b4e012445849b22edd3135942f4d6e473f7e2dfe752787cacd14a5bc9de32b8d83aa12eac91d39965c8843a6390ff96601bfeb9da58ab831da867693e716269a1d66c381145501196a8b13beddeb5e030a3b595f89b3c87b6b26cb310f0d36f7f0c302ab087704f5c4694e09203c993bd0ebbba399f14f5ce603f41e112466b7ebabb84d724438f8cb97f9ff1f37492a2bfa751b27c4bb5155667264b24b6bb018baf0742f1c3a74f7e5131f54f24b8ef2837fba23844c53fc52841eacf091cfc9a9ca8c93f9675ac515c717386745c681aa40a08315bc9ad8c79706948d8f21b324dea6fbf739ca120edd41be39980fe3d54fdc754ec9aff7978fbd59d63958a69e6918a7885fc3e7eb240c54afe7a0b1abef12c89122738eee1cda555730fe1188a9be0ac10a2fc88aea20ae681d619f9650edbd961d2a85cef414491fc24a9806d087901a26247fae1b572410db416f5e9268b2431b4962668a6b7143e2f59e516d02cdc29daf2ffc74a071c98bb69c445c8844a48d5c9d3bfc4fe3f2f500a937032c25d5049fb0d1742419dca3884289a7c0dd91c55a9bb23d7f7f698e236515eb52002d38399a8298eca195c22228a1d5e5f96f7121e4371f06367f1e8abfbb7aedb08d6e82cacd126dc1fd8ff7767a153a1605e880e55bd742724f99c4ebca80356403c04f6b1bba995d2d665d4562ec1299c04bd94da3a12e4bff4c02531d685ce5bb7ae88be012d79fc83779409f1aa2d71039710191f4f410d2db218c3c1ad8fb53f48bc37ae2adf8f73a7bd57b7005b4f65019d8cad44eb56e11ce9a345ee8c43d3d89d3f8ab7f68c26121bf0b5ebe68d161c10847a8cd0aa67a839def2091ae4f3f4bec7ac62967119bfa915a2a350964ce93a8e42a9780e2044a7a3d14d8cddfafeaabb74cd4f9cf8a8c89c327e74aa8606f97f49751e47687b6e1b38c8713f3868733a038287d4b0817ba56f8fc0a75c8793e9c8f13a2f4b88bfc3fa1bab0d1768c2b99313844a1bb793f5f408d745d7ed5370c48df4f1c7a18d1189516b7967645a67bed2ba23c0509aba4e73c4be9d22858f415157317b444506f4b26da3f58865ca245308229bec930a0b964058c2e78a51afc4920075479314e0b5da1dcbafdfa284add0e6dab0289b3452d8f339d806b17b7d0bc4ba8520c070c7e497b70bcc8b8997e04cd10cda89e3d72369155b1fa1f32372203947303c2b9f9ccdb0608450d7621aed73e22be22c618ebdcfafddce27728581b99f80a0563c55fa9b6a2b2ae756bd90676f2a60779d9ec773370cc14a5b1132108dced1c34b0848d922f1dda44fda6fac31a35d8a854c2d3e5eab0fdc6509ae842a2c2a4b9cfd9e41e0e226090791693bd253389d80ecbc7482867d5bd5e1e5bd6d0a96bc3762c9da38fbb0a37552f261243cd815388ec8ef00cc40c6092f574d3ca64f24de4496f6eccb0a2fd7d099b49b646225bc98d5fa305bd51994d124e677619085672893bd52d8e7b6905b155c65e5fc5aeca02d8629e19953447c8143a80359111b36fa9c4ce73eb6ac982e18f22e0ebe5ead6db1789e9b29552e0f78efb9f9ce634b478154ca8cca2a92425a48dc59a88e9499355244ac1ddb4dc0f65abaf9fbf5e00baa809766a5921f86f22f1fbd35749d34f5374ec77f268f9b3082b1be312886a85b980567165bf420fc4a12464c1b96ad37f6900101a69a00e8e3d141be16b016babaa8d7f3353a1f80c16aa5d0f955fb02c3e4fcd80d653afc8a32bdb74cce935024b42f2ad89134e5cbe6375e1907a2aab0627d56581130832a68de4225f21a95b094013e947958623b1fae7581034d3986ef6c9b9cf14c8b5d5fa4e1599da83ae79f53cab1e712f13f53a7cb7c0db3f9818dea93a936d8cfde62d6ff94ec61580b27a59757fe2eabc3947eef1b7631ba3fa07d95bdf03685604da227b03027cc94215b96cf6c7bd0ca5a84d6183e33b58360dbaf54fdf37fe16002cf0eef09eee375c385127efdb1a21d7d802319b405d3bd4b0acdcf708c4990857159c60b753c409da32886854211e6ea384bc67e0d0187f06aa703b63524ef604a63a5ae1b558a1ba892f40da8a7e869e52296586b55f8c4b0db87af1f62f53ea41d381107a7a2308e639809fdc03fdaa465643fdf95b5da99db0fa03333440cd8d918946f50f35a13b8f03f560e3d22b8cb52952146c4762da860c0f204b4afa7ec1bb4a2e15e57ef056cf546322df80b8ce696489e62c05ceb7e1b7954b9544141e90683807c751645784b2f2f363e7eb74e6e1c007e52e03b0e0fef980bca2e7033ea30d6ea26d154d52ca07c423f4caab424681751ba0db42b8e2bf5b2abc5e6c375d8e4ff5d9dd5c00f4f2f8ac2ece79acfd668ac797821748044b1de4799cc3fabf7b8ef12a83fb912235d5b0274945a175f0738a951198ea5ba13fb72ca3b87c7482484eb7aa7739430501427519498a791566e4a7178800e7b93707e00d1f2a19b01816f20f8734dc8b3ec61f48a7299fd712f2ff2eeef324a528238bb94fcc98c3d2e6a288bd641e12992200688f1d363c3766b4366c0bf1c26a857e584bd04fa3cc36a15447dfdd8c7884f14e2f0a40c0fa806854f932625cd6018b2b458526174174d36a086472a896c0ceadb38dd41f800f8666c92dc36e5761a95276f7df96d21dfa012d44ac8e9effe8cb2ec54d482f893602e5e9da95d049aa242b8b251ba87e3f8115c2b35f0a292b3dac22ab1c222f2ee950cb43be1060f72e64ea741fbd126f4329ead878904393d7599f5f7461fae09b13100103c458da4f649857e4d698a16d03d74ec23eea47fab337ca92cf7ae37f19e4c8b3517cb6b95d9ebb78b36954fe6f381232cb005e13f6a68b511682b94bd377501aefe2f165a2a0e5a1b16dec7b8ff10e4cddccd069dff3a91a55a60bd3a697e0ab17f80653c7f14c9fd98dd1bfc906a4f99703d5d36162ab3d522ef0e851a15f057b0f9ad1dbcb4f83115bb3ae333ed8cfbcd4d16f56124ff603b6d1bf249aad9475e9d95a42645627f45636d0a0d2830e90fa8087aaa9768d4dd0ce9c465e61745a215cf046d0ff72b28e3defb4617d7db17c37bf0363cb88da5b03a06dd3370697e3d8a09ba2f761f8af1a30e3b5a1cfebd4974cf20182cff940d8756841ab7007b372bae7d28bcd1c3e65cf8abaa11f05b5acd561501bdf5077a07ee458d0fa244f596eb99088309f6c93ef1ef8ad712efe75b8b5d2e8108b67c8ed1e263f1ff95c58cf7f50473d4d4824bf8ee8092583f01119e6913348919eaeb9bf626c66194ce06dec23f4ec494a461ee1a46c53f47f2be96e030715b0b9862995c95c578d2a47508d82d4fe072cd1e2152233114764f8846d0c2a933c7ace68437e650c113c084e39de628d253e72c1e5ad1ba48479ce37a5c8ca971ac5b133db5f0ed72ab26f2cb2093db10f22c2775cf747509c1225e2e63789e6582ea49090a14810e392042472a3bcfbaa072810db2887d32869951a0f041bedf0292f534a6ce3fb0ccf727d30e5a951eec112f42bfb79fe477ca4d3691c153a161d14756326053d73b4abb667532dc6609f8340ff030fbb0b274b4fd9c47fd14fc20753c4133695bfd62d3f9d5674e072b1d400705a4f73ba2a700111cc63253898f4ea270d10c9b28a4d3b53b62c4b577b9d68ec6800f1746c124a5b26f46922c2cbfc799f51f95f8cd67579199278627ace6b7d2e8b25310820ff7d553aa4a1bea47b33060583da8fdc4e048887e5fe4a9bdc39a3636301256f5c44e3b61588da6633d9526df8d21eecc0d622366b8ce095dfa85f1c1a9ec054c211c50ce0e605647629cc22df1b9424ae06371876e898e5f02e5303fc162073e5285ab1bb52393c7aa1a415722d45381c80908cb833bc3d5fbb5529dab1db8865138f3d983d2a46fb0a92afb15a1b6085adb02e00bf8cdeb3df416c71c103852f5cc0ebe8b48f1bade303169ae00fac07b7f6b731a3a94721366f3bbcd87a4a1479bf9acf12e246a5f354c5da5a136cada71389be14d77a7233a092fdbc5866787f11e676748da41572be0a35ad65f84a7cdeae24334251213fdf64d75742bfa6bd3a1c91eb8b81aa307c3bf752273dc75db2846f36d2088ddd1616ac6927cc2037cd23fd73f7a75b7d0840fff17ab6987342c14a3be9e32ee75ef4aa90d3270d5c1ac8f88d97b065ee004b69d59fe9d42eef2f60200df93015f641731e75be9d41a0e7ae1e06fadbcb8e70f6013894aa7a15505fe8713a636ead0d2939b81e9253e7c01929915040a6ca747ffb6fd4100224473b8dcd7d72ab1e9f8176be3a9a002401962545302406e6947fe63d6f36e2485afa5b116445c690a70c48f29e3efb81d117f609a7198a47a72bf1e7f04b97fd4b585d74dc18b888124cf15b701f38492e42a8cf4b4bbab9ed25d38d8ddc22fae004bf98da86bcc70d3656c12cbb91b483d06509272192251f7f89bfcdef570b21535c6f53a010d43459ce49d00668e9407446792fd0b5b5f89f358ae286d1180b1c7f8b7ff6ac14e396a628a6dac99c30ea70b029c5a8a7638a017ccb05820251212cdc9254cb391ada6907bc52505cfb54096deeca0708c4bda291817eae0e46688a2a5ce560e669f57d68a509199a0153007d5ff00a6b090a2bda6b51d4dbd29e9fc9b9f1db27c8b0bd24f7dbde59e42f065d7e916b2fbd5091c821c0a841d314a635406bda84634b183171b2e78dedcfb2aec50b2eca64676ceaefa2f58b246e69f4330380a24bb4843db0d1fa020230f0de090822bc3518d84382e148aa6691f7503ef0b8ee71c67be15fa4355cfc10090b260033ac6845ce4d04f7bee281d6fdd4b670cf2e10bcb7a81e956862c81d99baf896b3d5f48de20ae8f8a5829d5ae3e699942610a692a295ddbcecf09b03b778d19d7ba08a29271c341eb34b83197a2783d95c9847beea1ef717d7cf5fecd7250473d4195808ffddf7b870f52b8aefc42b3e89ab9baac3dd13197c1c5ad34e4884cabb1147aea4803d0019aa21f80cf73278e2906185d743f759122e9f3194b8328b8ea58a181eb3869dd187701628dbef97adb404130795b5d795541347f3b1c25eed7edfa8902228941196a9a0f2cfa345aa99bee485832ec0410a2361e44cc951f1917d06d4cbdc957f947b43f7132f5323581c2e3733830e111aea31664f19df8ea6841cb79786abb52500c01103beb827281cd5626a06f2c009c08b157128b68389160e28c09d6c819719c2083cc57f5241443173e8d2657148d07949aca1017af85937e16fe8c14666336d81b8c9b7b85db672a", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0x3c311d57d4daf52904616cf69648081e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3c311d57d4daf52904616cf69648081e5e0621c4869aa60c02be9adcc98a0d1d": "0x1ce063247ca37058db551a8d99f2f15cfede61fc796acc464a9cdce4c18f6a46597283ea6b8648673305a3e06be6dd83b7bc1840081d50d4deef1ce53eba21e914248dbf89d86998772b66900d78e98980ea2afc3c8fe5b93f4b38052f3018a2301c346cb44aa03f8995eeee230970772d6268cd7606740f269bb4e609a01a3a15dcaa0b4c6840028f6d4fa8c460d5a7d687d1f81c9de453ef2f5ead88767fd22af0d0e90c36f95605510f00a9f0821675bc0c7b70e5c8d113b0426c21d627773b4a69b6ec0eda668471d806db625681a147efc35a4baeacf0bca95d12d13cd942", + "0x3f1467a096bcd71a5b6a0c8155e20810308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0x3f1467a096bcd71a5b6a0c8155e208103f2edf3bdf381debe331ab7446addfdc": "0x000064a7b3b6e00d0000000000000000", + "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0200", + "0x4dcb50595177a3177648411a42aca0f54e7b9012096b41c4eb3aaf947f6ea429": "0x0300", + "0x57f8dc2f5ab09467896f47300f0424384e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x57f8dc2f5ab09467896f47300f0424385e0621c4869aa60c02be9adcc98a0d1d": "0x1ce063247ca37058db551a8d99f2f15cfede61fc796acc464a9cdce4c18f6a46597283ea6b8648673305a3e06be6dd83b7bc1840081d50d4deef1ce53eba21e914248dbf89d86998772b66900d78e98980ea2afc3c8fe5b93f4b38052f3018a2301c346cb44aa03f8995eeee230970772d6268cd7606740f269bb4e609a01a3a15dcaa0b4c6840028f6d4fa8c460d5a7d687d1f81c9de453ef2f5ead88767fd22af0d0e90c36f95605510f00a9f0821675bc0c7b70e5c8d113b0426c21d627773b4a69b6ec0eda668471d806db625681a147efc35a4baeacf0bca95d12d13cd942", + "0x7474449cca95dc5d0c00e71735a6d17d4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x79e2fe5d327165001f8232643023ed8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x7b3237373ffdfeb1cab4222e3b520d6b4e7b9012096b41c4eb3aaf947f6ea429": "0x0500", + "0xb8753e9383841da95f7b8871e5de32694e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00000000000000000000000000000000", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb31a487e6cc5c348d84c0aa0240b2d7485675e52cdb283a87973652f6acb42c830a5a5faa80f7a707e": "0x1c346cb44aa03f8995eeee230970772d6268cd7606740f269bb4e609a01a3a15", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb32d8a863b519f21b700f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127": "0xe063247ca37058db551a8d99f2f15cfede61fc796acc464a9cdce4c18f6a4659", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3313cc789e5100ca680b6f570f356fef7b891afa2e1c30fca89bc7a2cddd545fd8a173106fce3a11f": "0x4a69b6ec0eda668471d806db625681a147efc35a4baeacf0bca95d12d13cd942", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3624ad9e747f9464220d8c795eef2620fba2bde74dbc36461c07998ebf600ed265b746c1e05c70606": "0x248dbf89d86998772b66900d78e98980ea2afc3c8fe5b93f4b38052f3018a230", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb368a58d3eb991155a689e1a66fa33b75f66415021aacc4fa23f49306a3c21407748b8b2d39b4abf63": "0xf0d0e90c36f95605510f00a9f0821675bc0c7b70e5c8d113b0426c21d627773b", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb39f053c2746e722456610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38": "0xdcaa0b4c6840028f6d4fa8c460d5a7d687d1f81c9de453ef2f5ead88767fd22a", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3d21fbce2a623d5a4049bec59fb5fe6adea4578250578e89dd7e51ad88c7c92493d6f451c6680925c": "0x7283ea6b8648673305a3e06be6dd83b7bc1840081d50d4deef1ce53eba21e914", + "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19507a74cdfcf05a85226175726180f0d0e90c36f95605510f00a9f0821675bc0c7b70e5c8d113b0426c21d627773b": "0x689e1a66fa33b75f66415021aacc4fa23f49306a3c21407748b8b2d39b4abf63", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19507ae4ba128e21c1c36175726180dcaa0b4c6840028f6d4fa8c460d5a7d687d1f81c9de453ef2f5ead88767fd22a": "0x6610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19507c0de6df75d99ec461757261801c346cb44aa03f8995eeee230970772d6268cd7606740f269bb4e609a01a3a15": "0x4c0aa0240b2d7485675e52cdb283a87973652f6acb42c830a5a5faa80f7a707e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19508f578a71d93450886175726180248dbf89d86998772b66900d78e98980ea2afc3c8fe5b93f4b38052f3018a230": "0x20d8c795eef2620fba2bde74dbc36461c07998ebf600ed265b746c1e05c70606", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950a074c92c894f26b161757261807283ea6b8648673305a3e06be6dd83b7bc1840081d50d4deef1ce53eba21e914": "0x049bec59fb5fe6adea4578250578e89dd7e51ad88c7c92493d6f451c6680925c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950a273e8876df6fc8e6175726180e063247ca37058db551a8d99f2f15cfede61fc796acc464a9cdce4c18f6a4659": "0x00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d017874cd5fa32f261757261804a69b6ec0eda668471d806db625681a147efc35a4baeacf0bca95d12d13cd942": "0x80b6f570f356fef7b891afa2e1c30fca89bc7a2cddd545fd8a173106fce3a11f", + "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x1c00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127049bec59fb5fe6adea4578250578e89dd7e51ad88c7c92493d6f451c6680925c20d8c795eef2620fba2bde74dbc36461c07998ebf600ed265b746c1e05c706064c0aa0240b2d7485675e52cdb283a87973652f6acb42c830a5a5faa80f7a707e6610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38689e1a66fa33b75f66415021aacc4fa23f49306a3c21407748b8b2d39b4abf6380b6f570f356fef7b891afa2e1c30fca89bc7a2cddd545fd8a173106fce3a11f", + "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x1c00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127e063247ca37058db551a8d99f2f15cfede61fc796acc464a9cdce4c18f6a4659049bec59fb5fe6adea4578250578e89dd7e51ad88c7c92493d6f451c6680925c7283ea6b8648673305a3e06be6dd83b7bc1840081d50d4deef1ce53eba21e91420d8c795eef2620fba2bde74dbc36461c07998ebf600ed265b746c1e05c70606248dbf89d86998772b66900d78e98980ea2afc3c8fe5b93f4b38052f3018a2304c0aa0240b2d7485675e52cdb283a87973652f6acb42c830a5a5faa80f7a707e1c346cb44aa03f8995eeee230970772d6268cd7606740f269bb4e609a01a3a156610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38dcaa0b4c6840028f6d4fa8c460d5a7d687d1f81c9de453ef2f5ead88767fd22a689e1a66fa33b75f66415021aacc4fa23f49306a3c21407748b8b2d39b4abf63f0d0e90c36f95605510f00a9f0821675bc0c7b70e5c8d113b0426c21d627773b80b6f570f356fef7b891afa2e1c30fca89bc7a2cddd545fd8a173106fce3a11f4a69b6ec0eda668471d806db625681a147efc35a4baeacf0bca95d12d13cd942", + "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd5e1a2fa16732ce6906189438c0a82c64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xe38f185207498abb5c213d0fb059b3d84e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xe38f185207498abb5c213d0fb059b3d86323ae84c43568be0d1394d5d0d522c4": "0x04000000", + "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000" + }, + "childrenDefault": {} + } + } +} \ No newline at end of file diff --git a/cumulus/polkadot-parachain/chain-specs/coretime-polkadot.json b/cumulus/polkadot-parachain/chain-specs/coretime-polkadot.json new file mode 120000 index 000000000000..f6f2bc686917 --- /dev/null +++ b/cumulus/polkadot-parachain/chain-specs/coretime-polkadot.json @@ -0,0 +1 @@ +../../parachains/chain-specs/coretime-polkadot.json \ No newline at end of file diff --git a/cumulus/polkadot-parachain/src/chain_spec/coretime.rs b/cumulus/polkadot-parachain/src/chain_spec/coretime.rs index 742f41f39e9d..fec3f56e6d35 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/coretime.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/coretime.rs @@ -107,8 +107,9 @@ impl CoretimeRuntimeType { CoretimeRuntimeType::Kusama => Ok(Box::new(GenericChainSpec::from_json_bytes( &include_bytes!("../../chain-specs/coretime-kusama.json")[..], )?)), - CoretimeRuntimeType::Polkadot => - todo!("Generate chain-spec: ../../chain-specs/coretime-polkadot.json"), + CoretimeRuntimeType::Polkadot => Ok(Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/coretime-polkadot.json")[..], + )?)), CoretimeRuntimeType::Rococo => Ok(Box::new(GenericChainSpec::from_json_bytes( &include_bytes!("../../chain-specs/coretime-rococo.json")[..], )?)), diff --git a/prdoc/pr_5436.prdoc b/prdoc/pr_5436.prdoc new file mode 100644 index 000000000000..ea624b7bc32d --- /dev/null +++ b/prdoc/pr_5436.prdoc @@ -0,0 +1,20 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add Polkadot Coretime Chain genesis chain-spec + +doc: + - audience: Node Operator + description: | + The Polkadot Coretime Chain can now be run as all other system parachains without specifying a + chain-spec. However this will soon be deprecated and `--chain ./chain-spec.json` should continue + to be used instead. + + - audience: Runtime User + description: | + The Polkadot Coretime Chain chain-specs have been added to the polkadot-sdk repo and can now be + pulled from there along with all other system chains. + +crates: + - name: polkadot-parachain-bin + bump: minor \ No newline at end of file From e600b74ce586c462c36074856f62008e1c145318 Mon Sep 17 00:00:00 2001 From: Egor_P Date: Thu, 22 Aug 2024 13:57:37 +0200 Subject: [PATCH 143/382] [Backport] Version bumps and prdoc reorgs from stable2407-1 (#5374) This PR backports regular version bumps and `prdoc` reorganisation from the `stable2407` release branch to master --- polkadot/node/primitives/src/lib.rs | 2 +- prdoc/{ => 1.15.1}/pr_4791.prdoc | 0 prdoc/{ => 1.15.1}/pr_4937.prdoc | 0 prdoc/{ => 1.15.1}/pr_5273.prdoc | 0 prdoc/{ => 1.15.1}/pr_5281.prdoc | 0 prdoc/{ => 1.15.1}/pr_5321.prdoc | 0 6 files changed, 1 insertion(+), 1 deletion(-) rename prdoc/{ => 1.15.1}/pr_4791.prdoc (100%) rename prdoc/{ => 1.15.1}/pr_4937.prdoc (100%) rename prdoc/{ => 1.15.1}/pr_5273.prdoc (100%) rename prdoc/{ => 1.15.1}/pr_5281.prdoc (100%) rename prdoc/{ => 1.15.1}/pr_5321.prdoc (100%) diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index 24c69582731b..685a9fd337df 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -59,7 +59,7 @@ pub use disputes::{ /// relatively rare. /// /// The associated worker binaries should use the same version as the node that spawns them. -pub const NODE_VERSION: &'static str = "1.15.0"; +pub const NODE_VERSION: &'static str = "1.15.1"; // For a 16-ary Merkle Prefix Trie, we can expect at most 16 32-byte hashes per node // plus some overhead: diff --git a/prdoc/pr_4791.prdoc b/prdoc/1.15.1/pr_4791.prdoc similarity index 100% rename from prdoc/pr_4791.prdoc rename to prdoc/1.15.1/pr_4791.prdoc diff --git a/prdoc/pr_4937.prdoc b/prdoc/1.15.1/pr_4937.prdoc similarity index 100% rename from prdoc/pr_4937.prdoc rename to prdoc/1.15.1/pr_4937.prdoc diff --git a/prdoc/pr_5273.prdoc b/prdoc/1.15.1/pr_5273.prdoc similarity index 100% rename from prdoc/pr_5273.prdoc rename to prdoc/1.15.1/pr_5273.prdoc diff --git a/prdoc/pr_5281.prdoc b/prdoc/1.15.1/pr_5281.prdoc similarity index 100% rename from prdoc/pr_5281.prdoc rename to prdoc/1.15.1/pr_5281.prdoc diff --git a/prdoc/pr_5321.prdoc b/prdoc/1.15.1/pr_5321.prdoc similarity index 100% rename from prdoc/pr_5321.prdoc rename to prdoc/1.15.1/pr_5321.prdoc From b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1 Mon Sep 17 00:00:00 2001 From: eskimor Date: Thu, 22 Aug 2024 16:15:18 +0200 Subject: [PATCH 144/382] Don't disconnect on invalid imports. (#5392) There are numerous reasons for invalid imports, most of them would likely be caused by bugs. On the other side, dispute distribution handles all connections fairly, thus there is little harm in keeping a problematic connection open. --------- Co-authored-by: eskimor Co-authored-by: ordian --- .../network/dispute-distribution/src/receiver/mod.rs | 7 +++++-- prdoc/pr_5392.prdoc | 11 +++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_5392.prdoc diff --git a/polkadot/node/network/dispute-distribution/src/receiver/mod.rs b/polkadot/node/network/dispute-distribution/src/receiver/mod.rs index 2409e6994f60..77c1e41aac05 100644 --- a/polkadot/node/network/dispute-distribution/src/receiver/mod.rs +++ b/polkadot/node/network/dispute-distribution/src/receiver/mod.rs @@ -66,9 +66,12 @@ use self::{ const COST_INVALID_REQUEST: Rep = Rep::CostMajor("Received message could not be decoded."); const COST_INVALID_SIGNATURE: Rep = Rep::Malicious("Signatures were invalid."); -const COST_INVALID_IMPORT: Rep = - Rep::Malicious("Import was deemed invalid by dispute-coordinator."); const COST_NOT_A_VALIDATOR: Rep = Rep::CostMajor("Reporting peer was not a validator."); + +/// Invalid imports can be caused by flooding, e.g. by a disabled validator. +const COST_INVALID_IMPORT: Rep = + Rep::CostMinor("Import was deemed invalid by dispute-coordinator."); + /// Mildly punish peers exceeding their rate limit. /// /// For honest peers this should rarely happen, but if it happens we would not want to disconnect diff --git a/prdoc/pr_5392.prdoc b/prdoc/pr_5392.prdoc new file mode 100644 index 000000000000..aeeb05de0bc3 --- /dev/null +++ b/prdoc/pr_5392.prdoc @@ -0,0 +1,11 @@ +title: "Don't disconnect disabled nodes sending us dispute messages" + +doc: + - audience: Node Operator + description: | + No longer disconnect peers which we consider disabled when raising + disputes as this will affect the approval process and thus finality. + +crates: + - name: polkadot-dispute-distribution + bump: patch From 6d819a61f0bd337383b9e0f355753802737e710c Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 23 Aug 2024 10:38:16 +0300 Subject: [PATCH 145/382] Remove the need to wait for target block header in warp sync implementation (#5431) I'm not sure if this is exactly what https://github.com/paritytech/polkadot-sdk/issues/3537 meant, but I think it should be fine to wait for relay chain before initializing parachain node fully, which removed the need for background task and extra hacks throughout the stack just to know where warp sync should start. Previously there were both `WarpSyncParams` and `WarpSyncConfig`, but there was no longer any point in having two data structures, so I simplified it to just `WarpSyncConfig`. Fixes https://github.com/paritytech/polkadot-sdk/issues/3537 --- cumulus/client/service/src/lib.rs | 91 +++++++----------- polkadot/node/service/src/lib.rs | 4 +- prdoc/pr_5431.prdoc | 20 ++++ substrate/bin/node/cli/src/service.rs | 4 +- substrate/client/informant/src/display.rs | 10 +- substrate/client/network/sync/src/engine.rs | 55 +---------- substrate/client/network/sync/src/lib.rs | 2 +- substrate/client/network/sync/src/strategy.rs | 95 +------------------ .../client/network/sync/src/strategy/warp.rs | 86 +++++------------ substrate/client/network/test/src/lib.rs | 18 ++-- substrate/client/network/test/src/sync.rs | 2 +- substrate/client/service/src/builder.rs | 16 ++-- substrate/client/service/src/lib.rs | 2 +- templates/minimal/node/src/service.rs | 2 +- templates/solochain/node/src/service.rs | 4 +- 15 files changed, 107 insertions(+), 304 deletions(-) create mode 100644 prdoc/pr_5431.prdoc diff --git a/cumulus/client/service/src/lib.rs b/cumulus/client/service/src/lib.rs index 9b5f0bec5387..7f656aabca7a 100644 --- a/cumulus/client/service/src/lib.rs +++ b/cumulus/client/service/src/lib.rs @@ -28,10 +28,7 @@ use cumulus_relay_chain_interface::{RelayChainInterface, RelayChainResult}; use cumulus_relay_chain_minimal_node::{ build_minimal_relay_chain_node_light_client, build_minimal_relay_chain_node_with_rpc, }; -use futures::{ - channel::{mpsc, oneshot}, - FutureExt, StreamExt, -}; +use futures::{channel::mpsc, StreamExt}; use polkadot_primitives::{CollatorPair, OccupiedCoreAssumption}; use sc_client_api::{ Backend as BackendT, BlockBackend, BlockchainEvents, Finalizer, ProofProvider, UsageProvider, @@ -43,7 +40,7 @@ use sc_consensus::{ use sc_network::{config::SyncMode, service::traits::NetworkService, NetworkBackend}; use sc_network_sync::SyncingService; use sc_network_transactions::TransactionsHandlerController; -use sc_service::{Configuration, NetworkStarter, SpawnTaskHandle, TaskManager, WarpSyncParams}; +use sc_service::{Configuration, NetworkStarter, SpawnTaskHandle, TaskManager, WarpSyncConfig}; use sc_telemetry::{log, TelemetryWorkerHandle}; use sc_utils::mpsc::TracingUnboundedSender; use sp_api::ProvideRuntimeApi; @@ -467,12 +464,19 @@ where { let warp_sync_params = match parachain_config.network.sync_mode { SyncMode::Warp => { - let target_block = warp_sync_get::( - para_id, - relay_chain_interface.clone(), - spawn_handle.clone(), - ); - Some(WarpSyncParams::WaitForTarget(target_block)) + log::debug!(target: LOG_TARGET_SYNC, "waiting for announce block..."); + + let target_block = + wait_for_finalized_para_head::(para_id, relay_chain_interface.clone()) + .await + .inspect_err(|e| { + log::error!( + target: LOG_TARGET_SYNC, + "Unable to determine parachain target block {:?}", + e + ); + })?; + Some(WarpSyncConfig::WithTarget(target_block)) }, _ => None, }; @@ -500,67 +504,37 @@ where spawn_handle, import_queue, block_announce_validator_builder: Some(Box::new(move |_| block_announce_validator)), - warp_sync_params, + warp_sync_config: warp_sync_params, block_relay: None, metrics, }) } -/// Creates a new background task to wait for the relay chain to sync up and retrieve the parachain -/// header -fn warp_sync_get( - para_id: ParaId, - relay_chain_interface: RCInterface, - spawner: SpawnTaskHandle, -) -> oneshot::Receiver<::Header> -where - B: BlockT + 'static, - RCInterface: RelayChainInterface + 'static, -{ - let (sender, receiver) = oneshot::channel::(); - spawner.spawn( - "cumulus-parachain-wait-for-target-block", - None, - async move { - log::debug!( - target: LOG_TARGET_SYNC, - "waiting for announce block in a background task...", - ); - - let _ = wait_for_finalized_para_head::(sender, para_id, relay_chain_interface) - .await - .map_err(|e| { - log::error!( - target: LOG_TARGET_SYNC, - "Unable to determine parachain target block {:?}", - e - ) - }); - } - .boxed(), - ); - - receiver -} - /// Waits for the relay chain to have finished syncing and then gets the parachain header that /// corresponds to the last finalized relay chain block. async fn wait_for_finalized_para_head( - sender: oneshot::Sender<::Header>, para_id: ParaId, relay_chain_interface: RCInterface, -) -> Result<(), Box> +) -> sc_service::error::Result<::Header> where B: BlockT + 'static, RCInterface: RelayChainInterface + Send + 'static, { - let mut imported_blocks = relay_chain_interface.import_notification_stream().await?.fuse(); - while imported_blocks.next().await.is_some() { - let is_syncing = relay_chain_interface.is_major_syncing().await.map_err(|e| { - Box::::from(format!( - "Unable to determine sync status. {e}" + let mut imported_blocks = relay_chain_interface + .import_notification_stream() + .await + .map_err(|error| { + sc_service::Error::Other(format!( + "Relay chain import notification stream error when waiting for parachain head: \ + {error}" )) - })?; + })? + .fuse(); + while imported_blocks.next().await.is_some() { + let is_syncing = relay_chain_interface + .is_major_syncing() + .await + .map_err(|e| format!("Unable to determine sync status: {e}"))?; if !is_syncing { let relay_chain_best_hash = relay_chain_interface @@ -586,8 +560,7 @@ where finalized_header.number(), finalized_header.hash() ); - let _ = sender.send(finalized_header); - return Ok(()) + return Ok(finalized_header) } } diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index 5ca566d2c962..e1f42e1ca86a 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -764,7 +764,7 @@ pub fn new_full< ) -> Result { use polkadot_availability_recovery::FETCH_CHUNKS_THRESHOLD; use polkadot_node_network_protocol::request_response::IncomingRequest; - use sc_network_sync::WarpSyncParams; + use sc_network_sync::WarpSyncConfig; let is_offchain_indexing_enabled = config.offchain_worker.indexing_enabled; let role = config.role.clone(); @@ -1037,7 +1037,7 @@ pub fn new_full< spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), + warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)), block_relay: None, metrics, })?; diff --git a/prdoc/pr_5431.prdoc b/prdoc/pr_5431.prdoc new file mode 100644 index 000000000000..9f6db7136a58 --- /dev/null +++ b/prdoc/pr_5431.prdoc @@ -0,0 +1,20 @@ +title: Remove the need to wait for target block header in warp sync implementation + +doc: + - audience: Node Dev + description: | + Previously warp sync needed to wait for target block header of the relay chain to become available before warp + sync can start, which resulted in cumbersome APIs. Parachain initialization was refactored to initialize warp sync + with target block header from the very beginning, improving and simplifying sync API. + +crates: + - name: sc-service + bump: major + - name: sc-network-sync + bump: major + - name: polkadot-service + bump: major + - name: cumulus-client-service + bump: major + - name: sc-informant + bump: major diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index d58ca888d419..d45713db5222 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -37,7 +37,7 @@ use sc_consensus_babe::{self, SlotProportion}; use sc_network::{ event::Event, service::traits::NetworkService, NetworkBackend, NetworkEventStream, }; -use sc_network_sync::{strategy::warp::WarpSyncParams, SyncingService}; +use sc_network_sync::{strategy::warp::WarpSyncConfig, SyncingService}; use sc_service::{config::Configuration, error::Error as ServiceError, RpcHandlers, TaskManager}; use sc_statement_store::Store as StatementStore; use sc_telemetry::{Telemetry, TelemetryWorker}; @@ -517,7 +517,7 @@ pub fn new_full_base::Hash>>( spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), + warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)), block_relay: None, metrics, })?; diff --git a/substrate/client/informant/src/display.rs b/substrate/client/informant/src/display.rs index 655bf21c7115..f5e042103ea7 100644 --- a/substrate/client/informant/src/display.rs +++ b/substrate/client/informant/src/display.rs @@ -101,17 +101,9 @@ impl InformantDisplay { _, Some(WarpSyncProgress { phase: WarpSyncPhase::DownloadingBlocks(n), .. }), ) if !sync_status.is_major_syncing() => ("⏩", "Block history".into(), format!(", #{}", n)), - ( - _, - _, - Some(WarpSyncProgress { phase: WarpSyncPhase::AwaitingTargetBlock, .. }), - ) => ("⏩", "Waiting for pending target block".into(), "".into()), // Handle all phases besides the two phases we already handle above. (_, _, Some(warp)) - if !matches!( - warp.phase, - WarpSyncPhase::AwaitingTargetBlock | WarpSyncPhase::DownloadingBlocks(_) - ) => + if !matches!(warp.phase, WarpSyncPhase::DownloadingBlocks(_)) => ( "⏩", "Warping".into(), diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index ee7576c22f16..a25db4f789dd 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -32,7 +32,7 @@ use crate::{ syncing_service::{SyncingService, ToServiceCommand}, }, strategy::{ - warp::{EncodedProof, WarpProofRequest, WarpSyncParams}, + warp::{EncodedProof, WarpProofRequest, WarpSyncConfig}, StrategyKey, SyncingAction, SyncingConfig, SyncingStrategy, }, types::{ @@ -42,11 +42,7 @@ use crate::{ }; use codec::{Decode, DecodeAll, Encode}; -use futures::{ - channel::oneshot, - future::{BoxFuture, Fuse}, - FutureExt, StreamExt, -}; +use futures::{channel::oneshot, FutureExt, StreamExt}; use libp2p::request_response::OutboundFailure; use log::{debug, error, trace, warn}; use prometheus_endpoint::{ @@ -257,10 +253,6 @@ pub struct SyncingEngine { /// The `PeerId`'s of all boot nodes. boot_node_ids: HashSet, - /// A channel to get target block header if we skip over proofs downloading during warp sync. - warp_sync_target_block_header_rx_fused: - Fuse>>, - /// Protocol name used for block announcements block_announce_protocol_name: ProtocolName, @@ -309,7 +301,7 @@ where protocol_id: ProtocolId, fork_id: &Option, block_announce_validator: Box + Send>, - warp_sync_params: Option>, + warp_sync_config: Option>, network_service: service::network::NetworkServiceHandle, import_queue: Box>, block_downloader: Arc>, @@ -404,19 +396,6 @@ where Arc::clone(&peer_store_handle), ); - // Split warp sync params into warp sync config and a channel to retrieve target block - // header. - let (warp_sync_config, warp_sync_target_block_header_rx) = - warp_sync_params.map_or((None, None), |params| { - let (config, target_block_rx) = params.split(); - (Some(config), target_block_rx) - }); - - // Make sure polling of the target block channel is a no-op if there is no block to - // retrieve. - let warp_sync_target_block_header_rx_fused = warp_sync_target_block_header_rx - .map_or(futures::future::pending().boxed().fuse(), |rx| rx.boxed().fuse()); - // Initialize syncing strategy. let strategy = SyncingStrategy::new(syncing_config, client.clone(), warp_sync_config)?; @@ -460,7 +439,6 @@ where genesis_hash, important_peers, default_peers_set_no_slot_connected_peers: HashSet::new(), - warp_sync_target_block_header_rx_fused, boot_node_ids, default_peers_set_no_slot_peers, default_peers_set_num_full, @@ -635,17 +613,6 @@ where Some(event) => self.process_notification_event(event), None => return, }, - // TODO: setting of warp sync target block should be moved to the initialization of - // `SyncingEngine`, see https://github.com/paritytech/polkadot-sdk/issues/3537. - warp_target_block_header = &mut self.warp_sync_target_block_header_rx_fused => { - if let Err(_) = self.pass_warp_sync_target_block_header(warp_target_block_header) { - error!( - target: LOG_TARGET, - "Failed to set warp sync target block header, terminating `SyncingEngine`.", - ); - return - } - }, response_event = self.pending_responses.select_next_some() => self.process_response_event(response_event), validation_result = self.block_announce_validator.select_next_some() => @@ -898,22 +865,6 @@ where } } - fn pass_warp_sync_target_block_header( - &mut self, - header: Result, - ) -> Result<(), ()> { - match header { - Ok(header) => self.strategy.set_warp_sync_target_block_header(header), - Err(err) => { - error!( - target: LOG_TARGET, - "Failed to get target block for warp sync. Error: {err:?}", - ); - Err(()) - }, - } - } - /// Called by peer when it is disconnecting. /// /// Returns a result if the handshake of this peer was indeed accepted. diff --git a/substrate/client/network/sync/src/lib.rs b/substrate/client/network/sync/src/lib.rs index 9f6c0f45d089..ca7280edba5f 100644 --- a/substrate/client/network/sync/src/lib.rs +++ b/substrate/client/network/sync/src/lib.rs @@ -19,7 +19,7 @@ //! Blockchain syncing implementation in Substrate. pub use service::syncing_service::SyncingService; -pub use strategy::warp::{WarpSyncParams, WarpSyncPhase, WarpSyncProgress}; +pub use strategy::warp::{WarpSyncConfig, WarpSyncPhase, WarpSyncProgress}; pub use types::{SyncEvent, SyncEventStream, SyncState, SyncStatus, SyncStatusProvider}; mod block_announce_validator; diff --git a/substrate/client/network/sync/src/strategy.rs b/substrate/client/network/sync/src/strategy.rs index 58befe94e84a..2c9799a9d836 100644 --- a/substrate/client/network/sync/src/strategy.rs +++ b/substrate/client/network/sync/src/strategy.rs @@ -30,7 +30,7 @@ use crate::{ LOG_TARGET, }; use chain_sync::{ChainSync, ChainSyncAction, ChainSyncMode}; -use log::{debug, error, info, warn}; +use log::{debug, error, info}; use prometheus_endpoint::Registry; use sc_client_api::{BlockBackend, ProofProvider}; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; @@ -462,39 +462,6 @@ where } } - /// Let `WarpSync` know about target block header - pub fn set_warp_sync_target_block_header( - &mut self, - target_header: B::Header, - ) -> Result<(), ()> { - match self.config.mode { - SyncMode::Warp => match self.warp { - Some(ref mut warp) => { - warp.set_target_block(target_header); - Ok(()) - }, - None => { - // As mode is set to warp sync, but no warp sync strategy is active, this means - // that warp sync has already finished / was skipped. - warn!( - target: LOG_TARGET, - "Discarding warp sync target, as warp sync was seemingly skipped due \ - to node being (partially) synced.", - ); - Ok(()) - }, - }, - _ => { - error!( - target: LOG_TARGET, - "Cannot set warp sync target block: not in warp sync mode." - ); - debug_assert!(false); - Err(()) - }, - } - } - /// Get actions that should be performed by the owner on the strategy's behalf #[must_use] pub fn actions(&mut self) -> Result>, ClientError> { @@ -600,63 +567,3 @@ where } } } - -#[cfg(test)] -mod test { - use super::*; - use futures::executor::block_on; - use sc_block_builder::BlockBuilderBuilder; - use substrate_test_runtime_client::{ - ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt, TestClientBuilder, - TestClientBuilderExt, - }; - - /// Regression test for crash when starting already synced parachain node with `--sync=warp`. - /// We must remove this after setting of warp sync target block is moved to initialization of - /// `SyncingEngine` (issue https://github.com/paritytech/polkadot-sdk/issues/3537). - #[test] - fn set_target_block_finished_warp_sync() { - // Populate database with finalized state. - let client = Arc::new(TestClientBuilder::new().build()); - let block = BlockBuilderBuilder::new(&*client) - .on_parent_block(client.chain_info().best_hash) - .with_parent_block_number(client.chain_info().best_number) - .build() - .unwrap() - .build() - .unwrap() - .block; - block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); - let just = (*b"TEST", Vec::new()); - client.finalize_block(block.hash(), Some(just)).unwrap(); - let target_block = BlockBuilderBuilder::new(&*client) - .on_parent_block(client.chain_info().best_hash) - .with_parent_block_number(client.chain_info().best_number) - .build() - .unwrap() - .build() - .unwrap() - .block; - - // Initialize syncing strategy. - let config = SyncingConfig { - mode: SyncMode::Warp, - max_parallel_downloads: 3, - max_blocks_per_request: 64, - metrics_registry: None, - }; - let mut strategy = - SyncingStrategy::new(config, client, Some(WarpSyncConfig::WaitForTarget)).unwrap(); - - // Warp sync instantly finishes as we have finalized state in DB. - let actions = strategy.actions().unwrap(); - assert_eq!(actions.len(), 1); - assert!(matches!(actions[0], SyncingAction::Finished)); - assert!(strategy.warp.is_none()); - - // Try setting the target block. We mustn't crash. - strategy - .set_warp_sync_target_block_header(target_block.header().clone()) - .unwrap(); - } -} diff --git a/substrate/client/network/sync/src/strategy/warp.rs b/substrate/client/network/sync/src/strategy/warp.rs index 00855578695d..4cc3e9f3a68b 100644 --- a/substrate/client/network/sync/src/strategy/warp.rs +++ b/substrate/client/network/sync/src/strategy/warp.rs @@ -26,7 +26,6 @@ use crate::{ LOG_TARGET, }; use codec::{Decode, Encode}; -use futures::channel::oneshot; use log::{debug, error, trace}; use sc_network_common::sync::message::{ BlockAnnounce, BlockAttributes, BlockData, BlockRequest, Direction, FromBlock, @@ -104,8 +103,6 @@ mod rep { pub enum WarpSyncPhase { /// Waiting for peers to connect. AwaitingPeers { required_peers: usize }, - /// Waiting for target block to be received. - AwaitingTargetBlock, /// Downloading and verifying grandpa warp proofs. DownloadingWarpProofs, /// Downloading target block. @@ -125,7 +122,6 @@ impl fmt::Display for WarpSyncPhase { match self { Self::AwaitingPeers { required_peers } => write!(f, "Waiting for {required_peers} peers to be connected"), - Self::AwaitingTargetBlock => write!(f, "Waiting for target block to be received"), Self::DownloadingWarpProofs => write!(f, "Downloading finality proofs"), Self::DownloadingTargetBlock => write!(f, "Downloading target block"), Self::DownloadingState => write!(f, "Downloading state"), @@ -145,37 +141,14 @@ pub struct WarpSyncProgress { pub total_bytes: u64, } -/// The different types of warp syncing, passed to `build_network`. -pub enum WarpSyncParams { - /// Standard warp sync for the chain. - WithProvider(Arc>), - /// Skip downloading proofs and wait for a header of the state that should be downloaded. - /// - /// It is expected that the header provider ensures that the header is trusted. - WaitForTarget(oneshot::Receiver<::Header>), -} - /// Warp sync configuration as accepted by [`WarpSync`]. pub enum WarpSyncConfig { /// Standard warp sync for the chain. WithProvider(Arc>), - /// Skip downloading proofs and wait for a header of the state that should be downloaded. + /// Skip downloading proofs and use provided header of the state that should be downloaded. /// /// It is expected that the header provider ensures that the header is trusted. - WaitForTarget, -} - -impl WarpSyncParams { - /// Split `WarpSyncParams` into `WarpSyncConfig` and warp sync target block header receiver. - pub fn split( - self, - ) -> (WarpSyncConfig, Option::Header>>) { - match self { - WarpSyncParams::WithProvider(provider) => - (WarpSyncConfig::WithProvider(provider), None), - WarpSyncParams::WaitForTarget(rx) => (WarpSyncConfig::WaitForTarget, Some(rx)), - } - } + WithTarget(::Header), } /// Warp sync phase used by warp sync state machine. @@ -189,9 +162,6 @@ enum Phase { last_hash: B::Hash, warp_sync_provider: Arc>, }, - /// Waiting for target block to be set externally if we skip warp proofs downloading, - /// and start straight from the target block (used by parachains warp sync). - PendingTargetBlock, /// Downloading target block. TargetBlock(B::Header), /// Warp sync is complete. @@ -274,7 +244,7 @@ where let phase = match warp_sync_config { WarpSyncConfig::WithProvider(warp_sync_provider) => Phase::WaitingForPeers { warp_sync_provider }, - WarpSyncConfig::WaitForTarget => Phase::PendingTargetBlock, + WarpSyncConfig::WithTarget(target_header) => Phase::TargetBlock(target_header), }; Self { @@ -289,20 +259,6 @@ where } } - /// Set target block externally in case we skip warp proof downloading. - pub fn set_target_block(&mut self, header: B::Header) { - let Phase::PendingTargetBlock = self.phase else { - error!( - target: LOG_TARGET, - "Attempt to set warp sync target block in invalid phase.", - ); - debug_assert!(false); - return - }; - - self.phase = Phase::TargetBlock(header); - } - /// Notify that a new peer has connected. pub fn add_peer(&mut self, peer_id: PeerId, _best_hash: B::Hash, best_number: NumberFor) { self.peers.insert(peer_id, Peer { best_number, state: PeerState::Available }); @@ -592,10 +548,6 @@ where phase: WarpSyncPhase::DownloadingTargetBlock, total_bytes: self.total_proof_bytes, }, - Phase::PendingTargetBlock { .. } => WarpSyncProgress { - phase: WarpSyncPhase::AwaitingTargetBlock, - total_bytes: self.total_proof_bytes, - }, Phase::Complete => WarpSyncProgress { phase: WarpSyncPhase::Complete, total_bytes: self.total_proof_bytes + self.total_state_bytes, @@ -614,14 +566,12 @@ where state: match &self.phase { Phase::WaitingForPeers { .. } => SyncState::Downloading { target: Zero::zero() }, Phase::WarpProof { .. } => SyncState::Downloading { target: Zero::zero() }, - Phase::PendingTargetBlock => SyncState::Downloading { target: Zero::zero() }, Phase::TargetBlock(header) => SyncState::Downloading { target: *header.number() }, Phase::Complete => SyncState::Idle, }, best_seen_block: match &self.phase { Phase::WaitingForPeers { .. } => None, Phase::WarpProof { .. } => None, - Phase::PendingTargetBlock => None, Phase::TargetBlock(header) => Some(*header.number()), Phase::Complete => None, }, @@ -759,7 +709,13 @@ mod test { #[test] fn warp_sync_to_target_for_db_with_finalized_state_is_noop() { let client = mock_client_with_state(); - let config = WarpSyncConfig::WaitForTarget; + let config = WarpSyncConfig::WithTarget(::Header::new( + 1, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + )); let mut warp_sync = WarpSync::new(Arc::new(client), config); // Warp sync instantly finishes @@ -785,7 +741,13 @@ mod test { #[test] fn warp_sync_to_target_for_empty_db_doesnt_finish_instantly() { let client = mock_client_without_state(); - let config = WarpSyncConfig::WaitForTarget; + let config = WarpSyncConfig::WithTarget(::Header::new( + 1, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + )); let mut warp_sync = WarpSync::new(Arc::new(client), config); // No actions are emitted. @@ -936,7 +898,13 @@ mod test { } // Manually set to another phase. - warp_sync.phase = Phase::PendingTargetBlock; + warp_sync.phase = Phase::TargetBlock(::Header::new( + 1, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + )); // No request is made. assert!(warp_sync.warp_proof_request().is_none()); @@ -1193,7 +1161,7 @@ mod test { .unwrap() .block; let target_header = target_block.header().clone(); - let config = WarpSyncConfig::WaitForTarget; + let config = WarpSyncConfig::WithTarget(target_header); let mut warp_sync = WarpSync::new(client, config); // Make sure we have enough peers to make a request. @@ -1201,10 +1169,6 @@ mod test { warp_sync.add_peer(PeerId::random(), Hash::random(), best_number); } - // No actions generated so far. - assert_eq!(warp_sync.actions().count(), 0); - - warp_sync.set_target_block(target_header); assert!(matches!(warp_sync.phase, Phase::TargetBlock(_))); let (_peer_id, request) = warp_sync.target_block_request().unwrap(); diff --git a/substrate/client/network/test/src/lib.rs b/substrate/client/network/test/src/lib.rs index 9285306948a6..b5aeb162e9fa 100644 --- a/substrate/client/network/test/src/lib.rs +++ b/substrate/client/network/test/src/lib.rs @@ -34,7 +34,7 @@ use std::{ time::Duration, }; -use futures::{channel::oneshot, future::BoxFuture, pin_mut, prelude::*}; +use futures::{future::BoxFuture, pin_mut, prelude::*}; use libp2p::PeerId; use log::trace; use parking_lot::Mutex; @@ -67,7 +67,7 @@ use sc_network_sync::{ service::{network::NetworkServiceProvider, syncing_service::SyncingService}, state_request_handler::StateRequestHandler, strategy::warp::{ - AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncParams, WarpSyncProvider, + AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncConfig, WarpSyncProvider, }, warp_request_handler, }; @@ -701,7 +701,7 @@ pub struct FullPeerConfig { /// Enable transaction indexing. pub storage_chain: bool, /// Optional target block header to sync to - pub target_block: Option<::Header>, + pub target_header: Option<::Header>, /// Force genesis even in case of warp & light state sync. pub force_genesis: bool, } @@ -865,13 +865,9 @@ pub trait TestNetFactory: Default + Sized + Send { let warp_sync = Arc::new(TestWarpSyncProvider(client.clone())); - let warp_sync_params = match config.target_block { - Some(target_block) => { - let (sender, receiver) = oneshot::channel::<::Header>(); - let _ = sender.send(target_block); - WarpSyncParams::WaitForTarget(receiver) - }, - _ => WarpSyncParams::WithProvider(warp_sync.clone()), + let warp_sync_config = match config.target_header { + Some(target_header) => WarpSyncConfig::WithTarget(target_header), + _ => WarpSyncConfig::WithProvider(warp_sync.clone()), }; let warp_protocol_config = { @@ -919,7 +915,7 @@ pub trait TestNetFactory: Default + Sized + Send { protocol_id.clone(), &fork_id, block_announce_validator, - Some(warp_sync_params), + Some(warp_sync_config), chain_sync_network_handle, import_queue.service(), block_relay_params.downloader, diff --git a/substrate/client/network/test/src/sync.rs b/substrate/client/network/test/src/sync.rs index f1c1b7414303..9edd68c2ed9f 100644 --- a/substrate/client/network/test/src/sync.rs +++ b/substrate/client/network/test/src/sync.rs @@ -1298,7 +1298,7 @@ async fn warp_sync_to_target_block() { net.add_full_peer_with_config(FullPeerConfig { sync_mode: SyncMode::Warp, - target_block: Some(target_block), + target_header: Some(target_block), ..Default::default() }); diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index 90bfd9ec27f7..d9a91e715fd5 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -55,7 +55,7 @@ use sc_network_sync::{ block_relay_protocol::BlockRelayParams, block_request_handler::BlockRequestHandler, engine::SyncingEngine, service::network::NetworkServiceProvider, state_request_handler::StateRequestHandler, - warp_request_handler::RequestHandler as WarpSyncRequestHandler, SyncingService, WarpSyncParams, + warp_request_handler::RequestHandler as WarpSyncRequestHandler, SyncingService, WarpSyncConfig, }; use sc_rpc::{ author::AuthorApiServer, @@ -756,8 +756,8 @@ pub struct BuildNetworkParams< /// A block announce validator builder. pub block_announce_validator_builder: Option) -> Box + Send> + Send>>, - /// Optional warp sync params. - pub warp_sync_params: Option>, + /// Optional warp sync config. + pub warp_sync_config: Option>, /// User specified block relay params. If not specified, the default /// block request handler will be used. pub block_relay: Option>, @@ -801,12 +801,12 @@ where spawn_handle, import_queue, block_announce_validator_builder, - warp_sync_params, + warp_sync_config, block_relay, metrics, } = params; - if warp_sync_params.is_none() && config.network.sync_mode.is_warp() { + if warp_sync_config.is_none() && config.network.sync_mode.is_warp() { return Err("Warp sync enabled, but no warp sync provider configured.".into()) } @@ -869,8 +869,8 @@ where (protocol_config, config_name) }; - let (warp_sync_protocol_config, warp_request_protocol_name) = match warp_sync_params.as_ref() { - Some(WarpSyncParams::WithProvider(warp_with_provider)) => { + let (warp_sync_protocol_config, warp_request_protocol_name) = match warp_sync_config.as_ref() { + Some(WarpSyncConfig::WithProvider(warp_with_provider)) => { // Allow both outgoing and incoming requests. let (handler, protocol_config) = WarpSyncRequestHandler::new::<_, TNet>( protocol_id.clone(), @@ -939,7 +939,7 @@ where protocol_id.clone(), &config.chain_spec.fork_id().map(ToOwned::to_owned), block_announce_validator, - warp_sync_params, + warp_sync_config, chain_sync_network_handle, import_queue.service(), block_downloader, diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index 89d563001cd6..ed108a3102bc 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -82,7 +82,7 @@ pub use sc_chain_spec::{ pub use sc_consensus::ImportQueue; pub use sc_executor::NativeExecutionDispatch; -pub use sc_network_sync::WarpSyncParams; +pub use sc_network_sync::WarpSyncConfig; #[doc(hidden)] pub use sc_network_transactions::config::{TransactionImport, TransactionImportFuture}; pub use sc_rpc::{ diff --git a/templates/minimal/node/src/service.rs b/templates/minimal/node/src/service.rs index 44b374fcc0a4..decb9d6c636e 100644 --- a/templates/minimal/node/src/service.rs +++ b/templates/minimal/node/src/service.rs @@ -138,7 +138,7 @@ pub fn new_full::Ha import_queue, net_config, block_announce_validator_builder: None, - warp_sync_params: None, + warp_sync_config: None, block_relay: None, metrics, })?; diff --git a/templates/solochain/node/src/service.rs b/templates/solochain/node/src/service.rs index 5e84552f4ccd..7eef9766b107 100644 --- a/templates/solochain/node/src/service.rs +++ b/templates/solochain/node/src/service.rs @@ -4,7 +4,7 @@ use futures::FutureExt; use sc_client_api::{Backend, BlockBackend}; use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; use sc_consensus_grandpa::SharedVoterState; -use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncParams}; +use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncConfig}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; use solochain_template_runtime::{self, apis::RuntimeApi, opaque::Block}; @@ -175,7 +175,7 @@ pub fn new_full< spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), + warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)), block_relay: None, metrics, })?; From 4ffccac4fe4d2294dfa63b20155f9c6052c69574 Mon Sep 17 00:00:00 2001 From: Gustavo Gonzalez Date: Fri, 23 Aug 2024 05:17:28 -0300 Subject: [PATCH 146/382] Update OpenZeppelin template documentation (#5398) # Description Updates `template.rs` to reflect the two OZ templates available and a short description # Checklist * [x] My PR includes a detailed description as outlined in the "Description" and its two subsections above. * [x] My PR follows the [labeling requirements](CONTRIBUTING.md#Process) of this project (at minimum one label for `T` required) * External contributors: ask maintainers to put the right label on your PR. * [x] I have made corresponding changes to the documentation (if applicable) --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Shawn Tabrizi --- docs/sdk/src/polkadot_sdk/templates.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/sdk/src/polkadot_sdk/templates.rs b/docs/sdk/src/polkadot_sdk/templates.rs index e87eb9c2bc8a..03a7aa98198f 100644 --- a/docs/sdk/src/polkadot_sdk/templates.rs +++ b/docs/sdk/src/polkadot_sdk/templates.rs @@ -40,9 +40,14 @@ //! //! In June 2023, OpenZeppelin was awarded a grant from the [Polkadot //! treasury](https://polkadot.polkassembly.io/treasury/406) for building a number of Polkadot-sdk -//! based templates. These templates are expected to be a great starting point for developers. -//! -//! - +//! based templates. These templates are a great starting point for developers and newcomers. +//! So far OpenZeppelin has released two templates, which have been fully [audited](https://github.com/OpenZeppelin/polkadot-runtime-templates/tree/main/audits): +//! - [`generic-runtime-template`](https://github.com/OpenZeppelin/polkadot-runtime-templates?tab=readme-ov-file#generic-runtime-template): +//! A minimal template that has all the common pallets that parachains use with secure defaults. +//! - [`evm-runtime-template`](https://github.com/OpenZeppelin/polkadot-runtime-templates/tree/main?tab=readme-ov-file#evm-template): +//! This template has EVM compatibility out of the box and allows migrating your solidity contracts +//! or EVM compatible dapps easily. It also uses 20 byte addresses like Ethereum and has some +//! Account Abstraction support. //! //! ## POP-CLI //! From 559fa1db0594a81d5dbf343613ba2f3fc16708da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Fri, 23 Aug 2024 12:32:43 +0200 Subject: [PATCH 147/382] Add initial version of `pallet_revive` (#5293) This is a heavily modified and stripped down version of `pallet_contracts`. We decided to fork instead of extend the old pallet. Reasons for that are: - There is no benefit of supporting both on the same pallet as the intended payload for the new pallet (recompiled YUL) will be using a different ABI. - It is much easier since it allows us to remove all the code that was necessary to support Wasm and focus fully on running cross compiled YUL contracts. **The code is reviewable but can't be merged because it depends on an unreleased version of PolkaVM via git.** ## Current state All tests are passing and the code is not quick and dirty but written to last. The work is not finished, though. It is included in the `kitchensink-runtime` and a node can be built. However, we merge early in order to be able to start testing other components as early as possible. Outstanding changes are tracked here and will be merged separately: https://github.com/paritytech/polkadot-sdk/issues/5308 ## Syscall Interface The syscall interface is best explored by generating the docs of this crate and looking at the `SyscallDoc` trait. Arguments are passed in registers a0-a5 in the order they are listed. If there are more than 6 arguments (call, instantiate) a pointer to a packed struct of the arguments is expected as the only argument. I plan to create variants of those syscalls with less arguments specifically for YUL. Functions are just referenced by their name as ASCII within the PolkaVM container. Rather than by a syscall number as it was the case in the last implementation. ## Changes vs. `pallet_contracts` The changes are too numerous to list them all here. This is an incomplete list: - Use PolkaVM instead of wasmi to execute contracts - Made Runtime generic over a new `Memory` trait as we can't map memory directly on PolkaVM anymore - No static verification on code upload. Everything is a determinstic runtime failure - Removed all migrations and reset the pallet version - Removed the nonce storage item and instead use the deployers account nonce to generate a unique trie - We now bump the deployers account nonce on contract instantiation to they are bumped even within a batch transaction - Removed the instantiation nonce host function: We should add a new `instantiate` variant as a replacement for thos - ContractInfoOf of uses the indentity hasher now - Remove the determinism feature: User of that feature should switch to soft floats - The `unstable` attribute has been replaced by a `api_version` attribute to declare at which version an API became available - leaving out that attribute makes the API effectively unstable - a new `api_version` field on the CodeInfo makes sure that old contracts can't access new APIs (necessary due to lack of static verification. - Added a `behaviour_version` field to CodeInfo that can used if we need to introduce breaking changes and keep the old behaviour for existing contracts - Unified storage vs. transient and fixed vs. variable sized keys all into one set of multiplexing host functions - Removed all contract observeable limits from the `Config` trait and instead hardcode them - Removed the Schedule - Removed all deprecated host functions - Simplify chain extension as preperation for making it a pre-compile --------- Co-authored-by: command-bot <> --- .config/zepter.yaml | 2 +- .github/workflows/checks-quick.yml | 2 + Cargo.lock | 235 +- Cargo.toml | 14 +- prdoc/pr_5293.prdoc | 22 + scripts/generate-umbrella.py | 13 +- substrate/bin/node/cli/Cargo.toml | 4 + substrate/bin/node/runtime/Cargo.toml | 2 +- substrate/bin/node/runtime/src/lib.rs | 102 + substrate/frame/revive/Cargo.toml | 123 + substrate/frame/revive/README.md | 104 + substrate/frame/revive/build.rs | 78 + substrate/frame/revive/fixtures/Cargo.toml | 32 + substrate/frame/revive/fixtures/build.rs | 214 + .../frame/revive/fixtures/build/Cargo.toml | 19 + .../revive/fixtures/contracts/balance.rs | 36 + .../frame/revive/fixtures/contracts/call.rs | 49 + .../fixtures/contracts/call_return_code.rs | 56 + .../revive/fixtures/contracts/call_runtime.rs | 42 + .../contracts/call_runtime_and_call.rs | 53 + .../contracts/call_with_flags_and_value.rs | 51 + .../fixtures/contracts/call_with_limit.rs | 52 + .../fixtures/contracts/caller_contract.rs | 145 + .../fixtures/contracts/caller_is_origin_n.rs | 38 + .../fixtures/contracts/chain_extension.rs | 42 + .../contracts/chain_extension_temp_storage.rs | 65 + .../fixtures/contracts/common/Cargo.toml | 11 + .../fixtures/contracts/common/src/lib.rs | 157 + .../contracts/create_storage_and_call.rs | 58 + .../create_storage_and_instantiate.rs | 58 + .../create_transient_storage_and_call.rs | 60 + .../fixtures/contracts/crypto_hashes.rs | 84 + .../contracts/debug_message_invalid_utf8.rs | 33 + .../debug_message_logging_disabled.rs | 33 + .../fixtures/contracts/debug_message_works.rs | 33 + .../fixtures/contracts/delegate_call.rs | 49 + .../fixtures/contracts/delegate_call_lib.rs | 49 + .../contracts/delegate_call_simple.rs | 36 + .../contracts/destroy_and_transfer.rs | 86 + .../frame/revive/fixtures/contracts/drain.rs | 44 + .../frame/revive/fixtures/contracts/dummy.rs | 42 + .../fixtures/contracts/ecdsa_recover.rs | 44 + .../contracts/event_and_return_on_deploy.rs | 36 + .../revive/fixtures/contracts/event_size.rs | 38 + .../fixtures/contracts/float_instruction.rs | 34 + .../contracts/instantiate_return_code.rs | 52 + .../fixtures/contracts/instr_benchmark.rs | 41 + .../contracts/locking_delegate_dependency.rs | 68 + .../revive/fixtures/contracts/multi_store.rs | 43 + .../contracts/new_set_code_hash_contract.rs | 32 + .../frame/revive/fixtures/contracts/noop.rs | 44 + .../fixtures/contracts/ok_trap_revert.rs | 44 + .../fixtures/contracts/read_only_call.rs | 50 + .../revive/fixtures/contracts/recurse.rs | 53 + .../fixtures/contracts/return_with_data.rs | 47 + .../fixtures/contracts/run_out_of_gas.rs | 32 + .../fixtures/contracts/self_destruct.rs | 55 + .../contracts/self_destructing_constructor.rs | 32 + .../fixtures/contracts/set_code_hash.rs | 37 + .../fixtures/contracts/set_empty_storage.rs | 32 + .../contracts/set_transient_storage.rs | 41 + .../fixtures/contracts/sr25519_verify.rs | 48 + .../revive/fixtures/contracts/storage.rs | 63 + .../revive/fixtures/contracts/storage_size.rs | 50 + .../revive/fixtures/contracts/store_call.rs | 42 + .../revive/fixtures/contracts/store_deploy.rs | 42 + .../contracts/transfer_return_code.rs | 38 + .../fixtures/contracts/transient_storage.rs | 55 + .../revive/fixtures/contracts/xcm_execute.rs | 40 + .../revive/fixtures/contracts/xcm_send.rs | 42 + substrate/frame/revive/fixtures/src/lib.rs | 79 + .../frame/revive/mock-network/Cargo.toml | 88 + .../frame/revive/mock-network/src/lib.rs | 152 + .../frame/revive/mock-network/src/mocks.rs | 18 + .../mock-network/src/mocks/msg_queue.rs | 186 + .../src/mocks/relay_message_queue.rs | 52 + .../revive/mock-network/src/parachain.rs | 346 ++ .../src/parachain/contracts_config.rs | 27 + .../revive/mock-network/src/primitives.rs | 23 + .../revive/mock-network/src/relay_chain.rs | 239 + .../frame/revive/mock-network/src/tests.rs | 207 + substrate/frame/revive/proc-macro/Cargo.toml | 23 + substrate/frame/revive/proc-macro/src/lib.rs | 616 +++ substrate/frame/revive/src/address.rs | 68 + .../revive/src/benchmarking/call_builder.rs | 217 + .../frame/revive/src/benchmarking/code.rs | 69 + .../frame/revive/src/benchmarking/mod.rs | 1815 +++++++ .../frame/revive/src/benchmarking_dummy.rs | 37 + substrate/frame/revive/src/chain_extension.rs | 358 ++ substrate/frame/revive/src/debug.rs | 112 + substrate/frame/revive/src/exec.rs | 3948 ++++++++++++++++ substrate/frame/revive/src/gas.rs | 416 ++ substrate/frame/revive/src/lib.rs | 1354 ++++++ substrate/frame/revive/src/limits.rs | 60 + substrate/frame/revive/src/migration.rs | 650 +++ substrate/frame/revive/src/primitives.rs | 285 ++ substrate/frame/revive/src/storage.rs | 482 ++ substrate/frame/revive/src/storage/meter.rs | 888 ++++ substrate/frame/revive/src/test_utils.rs | 38 + .../frame/revive/src/test_utils/builder.rs | 218 + substrate/frame/revive/src/tests.rs | 4166 +++++++++++++++++ .../frame/revive/src/tests/pallet_dummy.rs | 53 + .../frame/revive/src/tests/test_debug.rs | 243 + .../frame/revive/src/transient_storage.rs | 691 +++ substrate/frame/revive/src/wasm/mod.rs | 350 ++ substrate/frame/revive/src/wasm/runtime.rs | 1936 ++++++++ substrate/frame/revive/src/weights.rs | 2120 +++++++++ substrate/frame/revive/uapi/Cargo.toml | 31 + substrate/frame/revive/uapi/src/flags.rs | 89 + substrate/frame/revive/uapi/src/host.rs | 643 +++ .../frame/revive/uapi/src/host/riscv32.rs | 561 +++ substrate/frame/revive/uapi/src/lib.rs | 131 + umbrella/Cargo.toml | 42 +- umbrella/src/lib.rs | 20 + 114 files changed, 27825 insertions(+), 25 deletions(-) create mode 100644 prdoc/pr_5293.prdoc create mode 100644 substrate/frame/revive/Cargo.toml create mode 100644 substrate/frame/revive/README.md create mode 100644 substrate/frame/revive/build.rs create mode 100644 substrate/frame/revive/fixtures/Cargo.toml create mode 100644 substrate/frame/revive/fixtures/build.rs create mode 100644 substrate/frame/revive/fixtures/build/Cargo.toml create mode 100644 substrate/frame/revive/fixtures/contracts/balance.rs create mode 100644 substrate/frame/revive/fixtures/contracts/call.rs create mode 100644 substrate/frame/revive/fixtures/contracts/call_return_code.rs create mode 100644 substrate/frame/revive/fixtures/contracts/call_runtime.rs create mode 100644 substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs create mode 100644 substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs create mode 100644 substrate/frame/revive/fixtures/contracts/call_with_limit.rs create mode 100644 substrate/frame/revive/fixtures/contracts/caller_contract.rs create mode 100644 substrate/frame/revive/fixtures/contracts/caller_is_origin_n.rs create mode 100644 substrate/frame/revive/fixtures/contracts/chain_extension.rs create mode 100644 substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs create mode 100644 substrate/frame/revive/fixtures/contracts/common/Cargo.toml create mode 100644 substrate/frame/revive/fixtures/contracts/common/src/lib.rs create mode 100644 substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs create mode 100644 substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs create mode 100644 substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs create mode 100644 substrate/frame/revive/fixtures/contracts/crypto_hashes.rs create mode 100644 substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs create mode 100644 substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs create mode 100644 substrate/frame/revive/fixtures/contracts/debug_message_works.rs create mode 100644 substrate/frame/revive/fixtures/contracts/delegate_call.rs create mode 100644 substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs create mode 100644 substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs create mode 100644 substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs create mode 100644 substrate/frame/revive/fixtures/contracts/drain.rs create mode 100644 substrate/frame/revive/fixtures/contracts/dummy.rs create mode 100644 substrate/frame/revive/fixtures/contracts/ecdsa_recover.rs create mode 100644 substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs create mode 100644 substrate/frame/revive/fixtures/contracts/event_size.rs create mode 100644 substrate/frame/revive/fixtures/contracts/float_instruction.rs create mode 100644 substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs create mode 100644 substrate/frame/revive/fixtures/contracts/instr_benchmark.rs create mode 100644 substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs create mode 100644 substrate/frame/revive/fixtures/contracts/multi_store.rs create mode 100644 substrate/frame/revive/fixtures/contracts/new_set_code_hash_contract.rs create mode 100644 substrate/frame/revive/fixtures/contracts/noop.rs create mode 100644 substrate/frame/revive/fixtures/contracts/ok_trap_revert.rs create mode 100644 substrate/frame/revive/fixtures/contracts/read_only_call.rs create mode 100644 substrate/frame/revive/fixtures/contracts/recurse.rs create mode 100644 substrate/frame/revive/fixtures/contracts/return_with_data.rs create mode 100644 substrate/frame/revive/fixtures/contracts/run_out_of_gas.rs create mode 100644 substrate/frame/revive/fixtures/contracts/self_destruct.rs create mode 100644 substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs create mode 100644 substrate/frame/revive/fixtures/contracts/set_code_hash.rs create mode 100644 substrate/frame/revive/fixtures/contracts/set_empty_storage.rs create mode 100644 substrate/frame/revive/fixtures/contracts/set_transient_storage.rs create mode 100644 substrate/frame/revive/fixtures/contracts/sr25519_verify.rs create mode 100644 substrate/frame/revive/fixtures/contracts/storage.rs create mode 100644 substrate/frame/revive/fixtures/contracts/storage_size.rs create mode 100644 substrate/frame/revive/fixtures/contracts/store_call.rs create mode 100644 substrate/frame/revive/fixtures/contracts/store_deploy.rs create mode 100644 substrate/frame/revive/fixtures/contracts/transfer_return_code.rs create mode 100644 substrate/frame/revive/fixtures/contracts/transient_storage.rs create mode 100644 substrate/frame/revive/fixtures/contracts/xcm_execute.rs create mode 100644 substrate/frame/revive/fixtures/contracts/xcm_send.rs create mode 100644 substrate/frame/revive/fixtures/src/lib.rs create mode 100644 substrate/frame/revive/mock-network/Cargo.toml create mode 100644 substrate/frame/revive/mock-network/src/lib.rs create mode 100644 substrate/frame/revive/mock-network/src/mocks.rs create mode 100644 substrate/frame/revive/mock-network/src/mocks/msg_queue.rs create mode 100644 substrate/frame/revive/mock-network/src/mocks/relay_message_queue.rs create mode 100644 substrate/frame/revive/mock-network/src/parachain.rs create mode 100644 substrate/frame/revive/mock-network/src/parachain/contracts_config.rs create mode 100644 substrate/frame/revive/mock-network/src/primitives.rs create mode 100644 substrate/frame/revive/mock-network/src/relay_chain.rs create mode 100644 substrate/frame/revive/mock-network/src/tests.rs create mode 100644 substrate/frame/revive/proc-macro/Cargo.toml create mode 100644 substrate/frame/revive/proc-macro/src/lib.rs create mode 100644 substrate/frame/revive/src/address.rs create mode 100644 substrate/frame/revive/src/benchmarking/call_builder.rs create mode 100644 substrate/frame/revive/src/benchmarking/code.rs create mode 100644 substrate/frame/revive/src/benchmarking/mod.rs create mode 100644 substrate/frame/revive/src/benchmarking_dummy.rs create mode 100644 substrate/frame/revive/src/chain_extension.rs create mode 100644 substrate/frame/revive/src/debug.rs create mode 100644 substrate/frame/revive/src/exec.rs create mode 100644 substrate/frame/revive/src/gas.rs create mode 100644 substrate/frame/revive/src/lib.rs create mode 100644 substrate/frame/revive/src/limits.rs create mode 100644 substrate/frame/revive/src/migration.rs create mode 100644 substrate/frame/revive/src/primitives.rs create mode 100644 substrate/frame/revive/src/storage.rs create mode 100644 substrate/frame/revive/src/storage/meter.rs create mode 100644 substrate/frame/revive/src/test_utils.rs create mode 100644 substrate/frame/revive/src/test_utils/builder.rs create mode 100644 substrate/frame/revive/src/tests.rs create mode 100644 substrate/frame/revive/src/tests/pallet_dummy.rs create mode 100644 substrate/frame/revive/src/tests/test_debug.rs create mode 100644 substrate/frame/revive/src/transient_storage.rs create mode 100644 substrate/frame/revive/src/wasm/mod.rs create mode 100644 substrate/frame/revive/src/wasm/runtime.rs create mode 100644 substrate/frame/revive/src/weights.rs create mode 100644 substrate/frame/revive/uapi/Cargo.toml create mode 100644 substrate/frame/revive/uapi/src/flags.rs create mode 100644 substrate/frame/revive/uapi/src/host.rs create mode 100644 substrate/frame/revive/uapi/src/host/riscv32.rs create mode 100644 substrate/frame/revive/uapi/src/lib.rs diff --git a/.config/zepter.yaml b/.config/zepter.yaml index 24441e90b1a0..7a67ba2695cf 100644 --- a/.config/zepter.yaml +++ b/.config/zepter.yaml @@ -27,7 +27,7 @@ workflows: ] # The umbrella crate uses more features, so we to check those too: check_umbrella: - - [ $check.0, '--features=serde,experimental,runtime,with-tracing,tuples-96,with-tracing', '-p=polkadot-sdk' ] + - [ $check.0, '--features=serde,experimental,riscv,runtime,with-tracing,tuples-96,with-tracing', '-p=polkadot-sdk' ] # Same as `check_*`, but with the `--fix` flag. default: - [ $check.0, '--fix' ] diff --git a/.github/workflows/checks-quick.yml b/.github/workflows/checks-quick.yml index ee4bd62a558d..9bdf64c9979c 100644 --- a/.github/workflows/checks-quick.yml +++ b/.github/workflows/checks-quick.yml @@ -100,6 +100,8 @@ jobs: --exclude "substrate/frame/contracts/fixtures/build" "substrate/frame/contracts/fixtures/contracts/common" + "substrate/frame/revive/fixtures/build" + "substrate/frame/revive/fixtures/contracts/common" - name: deny git deps run: python3 .github/scripts/deny-git-deps.py . check-markdown: diff --git a/Cargo.lock b/Cargo.lock index a67f9fdee31b..89cc7cfa8d49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9571,6 +9571,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +dependencies = [ + "memchr", +] + [[package]] name = "oid-registry" version = "0.6.1" @@ -10357,7 +10366,7 @@ dependencies = [ "anyhow", "frame-system", "parity-wasm", - "polkavm-linker", + "polkavm-linker 0.9.2", "sp-runtime", "tempfile", "toml 0.8.8", @@ -10417,7 +10426,7 @@ dependencies = [ "bitflags 1.3.2", "parity-scale-codec", "paste", - "polkavm-derive", + "polkavm-derive 0.9.1", "scale-info", ] @@ -11412,6 +11421,115 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-revive" +version = "0.1.0" +dependencies = [ + "array-bytes", + "assert_matches", + "bitflags 1.3.2", + "environmental", + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-assets", + "pallet-balances", + "pallet-message-queue", + "pallet-proxy", + "pallet-revive-fixtures", + "pallet-revive-proc-macro", + "pallet-revive-uapi", + "pallet-timestamp", + "pallet-utility", + "parity-scale-codec", + "paste", + "polkavm 0.10.0", + "pretty_assertions", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-std 14.0.0", + "sp-tracing 16.0.0", + "staging-xcm", + "staging-xcm-builder", + "wat", +] + +[[package]] +name = "pallet-revive-fixtures" +version = "0.1.0" +dependencies = [ + "anyhow", + "frame-system", + "parity-wasm", + "polkavm-linker 0.10.0", + "sp-runtime", + "tempfile", + "toml 0.8.8", +] + +[[package]] +name = "pallet-revive-mock-network" +version = "0.1.0" +dependencies = [ + "assert_matches", + "frame-support", + "frame-system", + "pallet-assets", + "pallet-balances", + "pallet-message-queue", + "pallet-proxy", + "pallet-revive", + "pallet-revive-fixtures", + "pallet-revive-proc-macro", + "pallet-revive-uapi", + "pallet-timestamp", + "pallet-utility", + "pallet-xcm", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-parachains", + "pretty_assertions", + "scale-info", + "sp-api", + "sp-core", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-tracing 16.0.0", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "xcm-simulator", +] + +[[package]] +name = "pallet-revive-proc-macro" +version = "0.1.0" +dependencies = [ + "proc-macro2 1.0.82", + "quote 1.0.36", + "syn 2.0.61", +] + +[[package]] +name = "pallet-revive-uapi" +version = "0.1.0" +dependencies = [ + "bitflags 1.3.2", + "parity-scale-codec", + "paste", + "polkavm-derive 0.10.0", + "scale-info", +] + [[package]] name = "pallet-root-offences" version = "25.0.0" @@ -14304,6 +14422,11 @@ dependencies = [ "pallet-recovery", "pallet-referenda", "pallet-remark", + "pallet-revive", + "pallet-revive-fixtures", + "pallet-revive-mock-network", + "pallet-revive-proc-macro", + "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", @@ -15093,9 +15216,22 @@ checksum = "8a3693e5efdb2bf74e449cd25fd777a28bd7ed87e41f5d5da75eb31b4de48b94" dependencies = [ "libc", "log", - "polkavm-assembler", - "polkavm-common", - "polkavm-linux-raw", + "polkavm-assembler 0.9.0", + "polkavm-common 0.9.0", + "polkavm-linux-raw 0.9.0", +] + +[[package]] +name = "polkavm" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ec0c5935f2eff23cfc4653002f4f8d12b37f87a720e0631282d188c32089d6" +dependencies = [ + "libc", + "log", + "polkavm-assembler 0.10.0", + "polkavm-common 0.10.0", + "polkavm-linux-raw 0.10.0", ] [[package]] @@ -15107,6 +15243,15 @@ dependencies = [ "log", ] +[[package]] +name = "polkavm-assembler" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e4fd5a43100bf1afe9727b8130d01f966f5cfc9144d5604b21e795c2bcd80e" +dependencies = [ + "log", +] + [[package]] name = "polkavm-common" version = "0.9.0" @@ -15116,13 +15261,32 @@ dependencies = [ "log", ] +[[package]] +name = "polkavm-common" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0097b48bc0bedf9f3f537ce8f37e8f1202d8d83f9b621bdb21ff2c59b9097c50" +dependencies = [ + "log", + "polkavm-assembler 0.10.0", +] + [[package]] name = "polkavm-derive" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae8c4bea6f3e11cd89bb18bcdddac10bd9a24015399bd1c485ad68a985a19606" dependencies = [ - "polkavm-derive-impl-macro", + "polkavm-derive-impl-macro 0.9.0", +] + +[[package]] +name = "polkavm-derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dcc701385c08c31bdb0569f0c51a290c580d892fa77f1dd88a7352a62679ecf" +dependencies = [ + "polkavm-derive-impl-macro 0.10.0", ] [[package]] @@ -15131,7 +15295,19 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ - "polkavm-common", + "polkavm-common 0.9.0", + "proc-macro2 1.0.82", + "quote 1.0.36", + "syn 2.0.61", +] + +[[package]] +name = "polkavm-derive-impl" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7855353a5a783dd5d09e3b915474bddf66575f5a3cf45dec8d1c5e051ba320dc" +dependencies = [ + "polkavm-common 0.10.0", "proc-macro2 1.0.82", "quote 1.0.36", "syn 2.0.61", @@ -15143,7 +15319,17 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ - "polkavm-derive-impl", + "polkavm-derive-impl 0.9.0", + "syn 2.0.61", +] + +[[package]] +name = "polkavm-derive-impl-macro" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9324fe036de37c17829af233b46ef6b5562d4a0c09bb7fdb9f8378856dee30cf" +dependencies = [ + "polkavm-derive-impl 0.10.0", "syn 2.0.61", ] @@ -15157,7 +15343,22 @@ dependencies = [ "hashbrown 0.14.3", "log", "object 0.32.2", - "polkavm-common", + "polkavm-common 0.9.0", + "regalloc2 0.9.3", + "rustc-demangle", +] + +[[package]] +name = "polkavm-linker" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d704edfe7bdcc876784f19436d53d515b65eb07bc9a0fae77085d552c2dbbb5" +dependencies = [ + "gimli 0.28.0", + "hashbrown 0.14.3", + "log", + "object 0.36.1", + "polkavm-common 0.10.0", "regalloc2 0.9.3", "rustc-demangle", ] @@ -15168,6 +15369,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26e85d3456948e650dff0cfc85603915847faf893ed1e66b020bb82ef4557120" +[[package]] +name = "polkavm-linux-raw" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26e45fa59c7e1bb12ef5289080601e9ec9b31435f6e32800a5c90c132453d126" + [[package]] name = "polling" version = "2.8.0" @@ -17638,7 +17845,7 @@ dependencies = [ name = "sc-executor-common" version = "0.29.0" dependencies = [ - "polkavm", + "polkavm 0.9.3", "sc-allocator", "sp-maybe-compressed-blob", "sp-wasm-interface 20.0.0", @@ -17651,7 +17858,7 @@ name = "sc-executor-polkavm" version = "0.29.0" dependencies = [ "log", - "polkavm", + "polkavm 0.9.3", "sc-executor-common", "sp-wasm-interface 20.0.0", ] @@ -20312,7 +20519,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", - "polkavm-derive", + "polkavm-derive 0.9.1", "rustversion", "secp256k1", "sp-core", @@ -20501,7 +20708,7 @@ dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive", + "polkavm-derive 0.9.1", "primitive-types", "rustversion", "sp-core", @@ -21546,7 +21753,7 @@ dependencies = [ "merkleized-metadata", "parity-scale-codec", "parity-wasm", - "polkavm-linker", + "polkavm-linker 0.9.2", "sc-executor", "sp-core", "sp-io", diff --git a/Cargo.toml b/Cargo.toml index 397163b3cce1..275efe1df638 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -395,6 +395,11 @@ members = [ "substrate/frame/recovery", "substrate/frame/referenda", "substrate/frame/remark", + "substrate/frame/revive", + "substrate/frame/revive/fixtures", + "substrate/frame/revive/mock-network", + "substrate/frame/revive/proc-macro", + "substrate/frame/revive/uapi", "substrate/frame/root-offences", "substrate/frame/root-testing", "substrate/frame/safe-mode", @@ -894,7 +899,7 @@ pallet-collator-selection = { path = "cumulus/pallets/collator-selection", defau pallet-collective = { path = "substrate/frame/collective", default-features = false } pallet-collective-content = { path = "cumulus/parachains/pallets/collective-content", default-features = false } pallet-contracts = { path = "substrate/frame/contracts", default-features = false } -pallet-contracts-fixtures = { path = "substrate/frame/contracts/fixtures" } +pallet-contracts-fixtures = { path = "substrate/frame/contracts/fixtures", default-features = false } pallet-contracts-mock-network = { default-features = false, path = "substrate/frame/contracts/mock-network" } pallet-contracts-proc-macro = { path = "substrate/frame/contracts/proc-macro", default-features = false } pallet-contracts-uapi = { path = "substrate/frame/contracts/uapi", default-features = false } @@ -950,6 +955,11 @@ pallet-ranked-collective = { path = "substrate/frame/ranked-collective", default pallet-recovery = { path = "substrate/frame/recovery", default-features = false } pallet-referenda = { path = "substrate/frame/referenda", default-features = false } pallet-remark = { default-features = false, path = "substrate/frame/remark" } +pallet-revive = { path = "substrate/frame/revive", default-features = false } +pallet-revive-fixtures = { path = "substrate/frame/revive/fixtures", default-features = false } +pallet-revive-mock-network = { default-features = false, path = "substrate/frame/revive/mock-network" } +pallet-revive-proc-macro = { path = "substrate/frame/revive/proc-macro", default-features = false } +pallet-revive-uapi = { path = "substrate/frame/revive/uapi", default-features = false } pallet-root-offences = { default-features = false, path = "substrate/frame/root-offences" } pallet-root-testing = { path = "substrate/frame/root-testing", default-features = false } pallet-safe-mode = { default-features = false, path = "substrate/frame/safe-mode" } @@ -1060,7 +1070,7 @@ polkadot-subsystem-bench = { path = "polkadot/node/subsystem-bench" } polkadot-test-client = { path = "polkadot/node/test/client" } polkadot-test-runtime = { path = "polkadot/runtime/test-runtime" } polkadot-test-service = { path = "polkadot/node/test/service" } -polkavm = "0.9.3" +polkavm = { version = "0.9.3", default-features = false } polkavm-derive = "0.9.1" polkavm-linker = "0.9.2" portpicker = { version = "0.1.1" } diff --git a/prdoc/pr_5293.prdoc b/prdoc/pr_5293.prdoc new file mode 100644 index 000000000000..90528a224e8d --- /dev/null +++ b/prdoc/pr_5293.prdoc @@ -0,0 +1,22 @@ +title: Add initial version of pallet_revive + +doc: + - audience: Runtime Dev + description: | + Adds initial **experimental** version of the new pallet_revive. It will run PolkaVM + contracts which were recompiled from YUL using the revive compiler. Do not use the + pallet in production, yet. It is work in progress. + +crates: + - name: polkadot-sdk + bump: minor + - name: pallet-revive + bump: minor + - name: pallet-revive-fixtures + bump: minor + - name: pallet-revive-proc-macro + bump: minor + - name: pallet-revive-uapi + bump: minor + - name: pallet-revive-mock-network + bump: minor diff --git a/scripts/generate-umbrella.py b/scripts/generate-umbrella.py index 3293c30bc828..5b9cc89c5308 100644 --- a/scripts/generate-umbrella.py +++ b/scripts/generate-umbrella.py @@ -64,7 +64,7 @@ def main(path, version): if manifest['lib']['proc-macro']: nostd_crates.append((crate, path)) continue - + # Crates without a lib.rs cannot be no_std if not os.path.exists(lib_path): print(f"Skipping {crate.name} as it does not have a 'src/lib.rs'") @@ -92,10 +92,10 @@ def main(path, version): for (crate, path) in nostd_crates: dependencies[crate.name] = {"path": f"../{path}", "default-features": False, "optional": True} - + for (crate, path) in std_crates: dependencies[crate.name] = {"path": f"../{path}", "default-features": False, "optional": True} - + # The empty features are filled by Zepter features = { "default": [ "std" ], @@ -108,6 +108,7 @@ def main(path, version): "runtime": list([f"{d.name}" for d, _ in nostd_crates]), "node": ["std"] + list([f"{d.name}" for d, _ in std_crates]), "tuples-96": [], + "riscv": [], } manifest = { @@ -159,9 +160,9 @@ def main(path, version): f.write(f'\n/// {desc}') f.write(f'\n#[cfg(feature = "{crate.name}")]\n') f.write(f"pub use {use};\n") - + print(f"Wrote {lib_path}") - + add_to_workspace(workspace.path) """ @@ -188,7 +189,7 @@ def add_to_workspace(path): manifest = re.sub(r'^members = \[', 'members = [\n "umbrella",', manifest, flags=re.M) with open(os.path.join(path, "Cargo.toml"), "w") as f: f.write(manifest) - + os.chdir(path) # hack os.system("cargo metadata --format-version 1 > /dev/null") # update the lockfile os.system(f"zepter") # enable the features diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml index 5b827c9d718f..6e734a723cd3 100644 --- a/substrate/bin/node/cli/Cargo.toml +++ b/substrate/bin/node/cli/Cargo.toml @@ -104,6 +104,10 @@ try-runtime = [ "polkadot-sdk/try-runtime", "substrate-cli-test-utils/try-runtime", ] +riscv = [ + "kitchensink-runtime/riscv", + "polkadot-sdk/riscv", +] [[bench]] name = "transaction_pool" diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index 2ad655883916..c262d74fa8a8 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -71,5 +71,5 @@ try-runtime = [ experimental = [ "pallet-example-tasks/experimental", ] - metadata-hash = ["substrate-wasm-builder/metadata-hash"] +riscv = ["polkadot-sdk/riscv"] diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index ea9a66862d64..ef5c52bf6e6e 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1381,6 +1381,34 @@ impl pallet_contracts::Config for Runtime { type Xcm = (); } +impl pallet_revive::Config for Runtime { + type Time = Timestamp; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type CallFilter = Nothing; + type DepositPerItem = DepositPerItem; + type DepositPerByte = DepositPerByte; + type WeightPrice = pallet_transaction_payment::Pallet; + type WeightInfo = pallet_revive::weights::SubstrateWeight; + type ChainExtension = (); + type AddressGenerator = pallet_revive::DefaultAddressGenerator; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; + type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; + type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; + type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; + type RuntimeHoldReason = RuntimeHoldReason; + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations = (); + #[cfg(feature = "runtime-benchmarks")] + type Migrations = pallet_revive::migration::codegen::BenchMigrations; + type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; + type Debug = (); + type Xcm = (); +} + impl pallet_sudo::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -2482,6 +2510,9 @@ mod runtime { #[runtime::pallet_index(79)] pub type AssetConversionMigration = pallet_asset_conversion_ops::Pallet; + + #[runtime::pallet_index(80)] + pub type Revive = pallet_revive::Pallet; } /// The address format for describing accounts. @@ -2541,6 +2572,7 @@ type Migrations = ( pallet_nomination_pools::migration::versioned::V6ToV7, pallet_alliance::migration::Migration, pallet_contracts::Migration, + pallet_revive::Migration, pallet_identity::migration::versioned::V0ToV1, ); @@ -2593,6 +2625,7 @@ mod benches { [pallet_collective, Council] [pallet_conviction_voting, ConvictionVoting] [pallet_contracts, Contracts] + [pallet_revive, Revive] [pallet_core_fellowship, CoreFellowship] [tasks_example, TasksExample] [pallet_democracy, Democracy] @@ -2955,6 +2988,75 @@ impl_runtime_apis! { } } + impl pallet_revive::ReviveApi for Runtime + { + fn call( + origin: AccountId, + dest: AccountId, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + input_data: Vec, + ) -> pallet_revive::ContractExecResult { + Revive::bare_call( + RuntimeOrigin::signed(origin), + dest, + value, + gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block), + storage_deposit_limit.unwrap_or(u128::MAX), + input_data, + pallet_revive::DebugInfo::UnsafeDebug, + pallet_revive::CollectEvents::UnsafeCollect, + ) + } + + fn instantiate( + origin: AccountId, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + code: pallet_revive::Code, + data: Vec, + salt: Vec, + ) -> pallet_revive::ContractInstantiateResult + { + Revive::bare_instantiate( + RuntimeOrigin::signed(origin), + value, + gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block), + storage_deposit_limit.unwrap_or(u128::MAX), + code, + data, + salt, + pallet_revive::DebugInfo::UnsafeDebug, + pallet_revive::CollectEvents::UnsafeCollect, + ) + } + + fn upload_code( + origin: AccountId, + code: Vec, + storage_deposit_limit: Option, + ) -> pallet_revive::CodeUploadResult + { + Revive::bare_upload_code( + RuntimeOrigin::signed(origin), + code, + storage_deposit_limit.unwrap_or(u128::MAX), + ) + } + + fn get_storage( + address: AccountId, + key: Vec, + ) -> pallet_revive::GetStorageResult { + Revive::get_storage( + address, + key + ) + } + } + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi< Block, Balance, diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml new file mode 100644 index 000000000000..747c2283e217 --- /dev/null +++ b/substrate/frame/revive/Cargo.toml @@ -0,0 +1,123 @@ +[package] +name = "pallet-revive" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +build = "build.rs" +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true +description = "FRAME pallet for PolkaVM contracts." +readme = "README.md" +include = ["CHANGELOG.md", "README.md", "build.rs", "src/**/*"] + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +paste = { workspace = true } +polkavm = { version = "0.10.0", default-features = false } +bitflags = { workspace = true } +codec = { features = [ + "derive", + "max-encoded-len", +], workspace = true } +scale-info = { features = ["derive"], workspace = true } +log = { workspace = true } +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } +impl-trait-for-tuples = { workspace = true } + +# Substrate Dependencies +environmental = { workspace = true } +frame-benchmarking = { optional = true, workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-balances = { optional = true, workspace = true } +pallet-revive-fixtures = { workspace = true, default-features = false } +pallet-revive-uapi = { workspace = true, default-features = true } +pallet-revive-proc-macro = { workspace = true, default-features = true } +sp-api = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +xcm = { workspace = true } +xcm-builder = { workspace = true } + +[dev-dependencies] +array-bytes = { workspace = true, default-features = true } +assert_matches = { workspace = true } +pretty_assertions = { workspace = true } +wat = { workspace = true } +pallet-revive-fixtures = { workspace = true, default-features = true } + +# Polkadot Dependencies +xcm-builder = { workspace = true, default-features = true } + +# Substrate Dependencies +pallet-balances = { workspace = true, default-features = true } +pallet-timestamp = { workspace = true, default-features = true } +pallet-message-queue = { workspace = true, default-features = true } +pallet-utility = { workspace = true, default-features = true } +pallet-assets = { workspace = true, default-features = true } +pallet-proxy = { workspace = true, default-features = true } +sp-keystore = { workspace = true, default-features = true } +sp-tracing = { workspace = true, default-features = true } + +[features] +default = ["std"] +# enabling this feature will require having a riscv toolchain installed +# if no tests are ran and runtime benchmarks will not work +# apart from this the pallet will stay functional +riscv = ["pallet-revive-fixtures/riscv"] +std = [ + "codec/std", + "environmental/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-balances?/std", + "pallet-proxy/std", + "pallet-revive-fixtures/std", + "pallet-timestamp/std", + "pallet-utility/std", + "polkavm/std", + "scale-info/std", + "serde", + "sp-api/std", + "sp-core/std", + "sp-io/std", + "sp-keystore/std", + "sp-runtime/std", + "sp-std/std", + "xcm-builder/std", + "xcm/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-assets/try-runtime", + "pallet-balances/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-proxy/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-utility/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/revive/README.md b/substrate/frame/revive/README.md new file mode 100644 index 000000000000..5352e636c252 --- /dev/null +++ b/substrate/frame/revive/README.md @@ -0,0 +1,104 @@ +# Revive Pallet + +This is an **experimental** module that provides functionality for the runtime to deploy and execute PolkaVM +smart-contracts. It is a heavily modified `pallet_contracts` fork. + +## Overview + +This module extends accounts based on the [`frame_support::traits::fungible`] traits to have smart-contract +functionality. It can be used with other modules that implement accounts based on [`frame_support::traits::fungible`]. +These "smart-contract accounts" have the ability to instantiate smart-contracts and make calls to other contract and +non-contract accounts. + +The smart-contract code is stored once, and later retrievable via its `code_hash`. This means that multiple +smart-contracts can be instantiated from the same `code`, without replicating the code each time. + +When a smart-contract is called, its associated code is retrieved via the code hash and gets executed. This call can +alter the storage entries of the smart-contract account, instantiate new smart-contracts, or call other smart-contracts. + +Finally, when an account is reaped, its associated code and storage of the smart-contract account will also be deleted. + +### Weight + +Senders must specify a [`Weight`](https://paritytech.github.io/substrate/master/sp_weights/struct.Weight.html) limit +with every call, as all instructions invoked by the smart-contract require weight. Unused weight is refunded after the +call, regardless of the execution outcome. + +If the weight limit is reached, then all calls and state changes (including balance transfers) are only reverted at the +current call's contract level. For example, if contract A calls B and B runs out of weight mid-call, then all of B's +calls are reverted. Assuming correct error handling by contract A, A's other calls and state changes still persist. + +One `ref_time` `Weight` is defined as one picosecond of execution time on the runtime's reference machine. + +### Revert Behaviour + +Contract call failures are not cascading. When failures occur in a sub-call, they do not "bubble up", and the call will +only revert at the specific contract level. For example, if contract A calls contract B, and B fails, A can decide how +to handle that failure, either proceeding or reverting A's changes. + +## Interface + +### Dispatchable functions + +Those are documented in the [reference +documentation](https://paritytech.github.io/substrate/master/pallet_revive/index.html#dispatchable-functions). + +## Usage + +This module executes PolkaVM smart contracts. These can potentially be written in any language that compiles to +RISC-V. For now, the only officially supported languages are Solidity (via [`revive`](https://github.com/xermicus/revive)) +and Rust (check the `fixtures` directory for Rust examples). + +## Debugging + +Contracts can emit messages to the client when called as RPC through the +[`debug_message`](https://paritytech.github.io/substrate/master/pallet_revive/trait.SyscallDocs.html#tymethod.debug_message) +API. + +Those messages are gathered into an internal buffer and sent to the RPC client. It is up to the individual client if +and how those messages are presented to the user. + +This buffer is also printed as a debug message. In order to see these messages on the node console the log level for the +`runtime::revive` target needs to be raised to at least the `debug` level. However, those messages are easy to +overlook because of the noise generated by block production. A good starting point for observing them on the console is +using this command line in the root directory of the Substrate repository: + +```bash +cargo run --release -- --dev -lerror,runtime::revive=debug +``` + +This raises the log level of `runtime::revive` to `debug` and all other targets to `error` in order to prevent them +from spamming the console. + +`--dev`: Use a dev chain spec `--tmp`: Use temporary storage for chain data (the chain state is deleted on exit) + +## Host function tracing + +For contract authors, it can be a helpful debugging tool to see which host functions are called, with which arguments, +and what the result was. + +In order to see these messages on the node console, the log level for the `runtime::revive::strace` target needs to +be raised to the `trace` level. + +Example: + +```bash +cargo run --release -- --dev -lerror,runtime::revive::strace=trace,runtime::revive=debug +``` + +## Unstable Interfaces + +Driven by the desire to have an iterative approach in developing new contract interfaces this pallet contains the +concept of an unstable interface. Akin to the rust nightly compiler it allows us to add new interfaces but mark them as +unstable so that contract languages can experiment with them and give feedback before we stabilize those. + +In order to access interfaces which don't have a stable `#[api_version(x)]` in [`runtime.rs`](src/wasm/runtime.rs) +one need to set `pallet_revive::Config::UnsafeUnstableInterface` to `ConstU32`. +**It should be obvious that any production runtime should never be compiled with this feature: In addition to be +subject to change or removal those interfaces might not have proper weights associated with them and are therefore +considered unsafe**. + +New interfaces are generally added as unstable and might go through several iterations before they are promoted to a +stable interface. + +License: Apache-2.0 diff --git a/substrate/frame/revive/build.rs b/substrate/frame/revive/build.rs new file mode 100644 index 000000000000..ca8e62df6047 --- /dev/null +++ b/substrate/frame/revive/build.rs @@ -0,0 +1,78 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::io::Write; + +/// We start with version 2 instead of 0 when adding the pallet. +/// +/// Because otherwise we can't test any migrations since they require the storage version +/// to be lower than the pallet version in order to be triggerd. With the pallet version +/// at the minimum (0) this would not work. +const LOWEST_STORAGE_VERSION: u16 = 2; + +/// Get the latest migration version. +/// +/// Find the highest version number from the available migration files. +/// Each migration file should follow the naming convention `vXX.rs`, where `XX` is the version +/// number. +fn get_latest_version() -> u16 { + let Ok(dir) = std::fs::read_dir("src/migration") else { return LOWEST_STORAGE_VERSION }; + dir.filter_map(|entry| { + let file_name = entry.as_ref().ok()?.file_name(); + let file_name = file_name.to_str()?; + if file_name.starts_with('v') && file_name.ends_with(".rs") { + let version = &file_name[1..&file_name.len() - 3]; + let version = version.parse::().ok()?; + + // Ensure that the version matches the one defined in the file. + let path = entry.unwrap().path(); + let file_content = std::fs::read_to_string(&path).ok()?; + assert!( + file_content.contains(&format!("const VERSION: u16 = {}", version)), + "Invalid MigrationStep::VERSION in {:?}", + path + ); + + return Some(version) + } + None + }) + .max() + .unwrap_or(LOWEST_STORAGE_VERSION) +} + +/// Generates a module that exposes the latest migration version, and the benchmark migrations type. +fn main() -> Result<(), Box> { + let out_dir = std::env::var("OUT_DIR")?; + let path = std::path::Path::new(&out_dir).join("migration_codegen.rs"); + let mut f = std::fs::File::create(path)?; + let version = get_latest_version(); + write!( + f, + " + pub mod codegen {{ + use crate::NoopMigration; + /// The latest migration version, pulled from the latest migration file. + pub const LATEST_MIGRATION_VERSION: u16 = {version}; + /// The Migration Steps used for benchmarking the migration framework. + pub type BenchMigrations = (NoopMigration<{}>, NoopMigration<{version}>); + }}", + version - 1, + )?; + + Ok(()) +} diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml new file mode 100644 index 000000000000..1b668c061f85 --- /dev/null +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "pallet-revive-fixtures" +publish = true +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +description = "Fixtures for testing and benchmarking" + +[lints] +workspace = true + +[dependencies] +frame-system = { workspace = true, default-features = true, optional = true } +sp-runtime = { workspace = true, default-features = true, optional = true } +anyhow = { workspace = true } + +[build-dependencies] +parity-wasm = { workspace = true } +tempfile = { workspace = true } +toml = { workspace = true } +polkavm-linker = { version = "0.10.0" } +anyhow = { workspace = true } + +[features] +default = ["std"] +# only if the feature is set we are building the test fixtures +# this is because it requires a custom toolchain supporting polkavm +# we will remove this once there is an upstream toolchain +riscv = [] +# only when std is enabled all fixtures are available +std = ["frame-system", "sp-runtime"] diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs new file mode 100644 index 000000000000..ed981a75467b --- /dev/null +++ b/substrate/frame/revive/fixtures/build.rs @@ -0,0 +1,214 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Compile text fixtures to PolkaVM binaries. +use anyhow::Result; + +fn main() -> Result<()> { + build::run() +} + +#[cfg(feature = "riscv")] +mod build { + use super::Result; + use anyhow::{bail, Context}; + use std::{ + cfg, env, fs, + path::{Path, PathBuf}, + process::Command, + }; + + /// A contract entry. + struct Entry { + /// The path to the contract source file. + path: PathBuf, + } + + impl Entry { + /// Create a new contract entry from the given path. + fn new(path: PathBuf) -> Self { + Self { path } + } + + /// Return the path to the contract source file. + fn path(&self) -> &str { + self.path.to_str().expect("path is valid unicode; qed") + } + + /// Return the name of the contract. + fn name(&self) -> &str { + self.path + .file_stem() + .expect("file exits; qed") + .to_str() + .expect("name is valid unicode; qed") + } + + /// Return the name of the polkavm file. + fn out_filename(&self) -> String { + format!("{}.polkavm", self.name()) + } + } + + /// Collect all contract entries from the given source directory. + /// Contracts that have already been compiled are filtered out. + fn collect_entries(contracts_dir: &Path) -> Vec { + fs::read_dir(contracts_dir) + .expect("src dir exists; qed") + .filter_map(|file| { + let path = file.expect("file exists; qed").path(); + if path.extension().map_or(true, |ext| ext != "rs") { + return None + } + + Some(Entry::new(path)) + }) + .collect::>() + } + + /// Create a `Cargo.toml` to compile the given contract entries. + fn create_cargo_toml<'a>( + fixtures_dir: &Path, + entries: impl Iterator, + output_dir: &Path, + ) -> Result<()> { + let mut cargo_toml: toml::Value = toml::from_str(include_str!("./build/Cargo.toml"))?; + let mut set_dep = |name, path| -> Result<()> { + cargo_toml["dependencies"][name]["path"] = toml::Value::String( + fixtures_dir.join(path).canonicalize()?.to_str().unwrap().to_string(), + ); + Ok(()) + }; + set_dep("uapi", "../uapi")?; + set_dep("common", "./contracts/common")?; + + cargo_toml["bin"] = toml::Value::Array( + entries + .map(|entry| { + let name = entry.name(); + let path = entry.path(); + toml::Value::Table(toml::toml! { + name = name + path = path + }) + }) + .collect::>(), + ); + + let cargo_toml = toml::to_string_pretty(&cargo_toml)?; + fs::write(output_dir.join("Cargo.toml"), cargo_toml).map_err(Into::into) + } + + fn invoke_build(current_dir: &Path) -> Result<()> { + let encoded_rustflags = [ + "-Crelocation-model=pie", + "-Clink-arg=--emit-relocs", + "-Clink-arg=--export-dynamic-symbol=__polkavm_symbol_export_hack__*", + ] + .join("\x1f"); + + let build_res = Command::new(env::var("CARGO")?) + .current_dir(current_dir) + .env_clear() + .env("PATH", env::var("PATH").unwrap_or_default()) + .env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags) + .env("RUSTUP_TOOLCHAIN", "rve-nightly") + .env("RUSTC_BOOTSTRAP", "1") + .env("RUSTUP_HOME", env::var("RUSTUP_HOME").unwrap_or_default()) + .args([ + "build", + "--release", + "--target=riscv32ema-unknown-none-elf", + "-Zbuild-std=core", + "-Zbuild-std-features=panic_immediate_abort", + ]) + .output() + .expect("failed to execute process"); + + if build_res.status.success() { + return Ok(()) + } + + let stderr = String::from_utf8_lossy(&build_res.stderr); + + if stderr.contains("'rve-nightly' is not installed") { + eprintln!("RISC-V toolchain is not installed.\nDownload and install toolchain from https://github.com/paritytech/rustc-rv32e-toolchain."); + eprintln!("{}", stderr); + } else { + eprintln!("{}", stderr); + } + + bail!("Failed to build contracts"); + } + + /// Post-process the compiled code. + fn post_process(input_path: &Path, output_path: &Path) -> Result<()> { + let mut config = polkavm_linker::Config::default(); + config.set_strip(true); + let orig = + fs::read(input_path).with_context(|| format!("Failed to read {:?}", input_path))?; + let linked = polkavm_linker::program_from_elf(config, orig.as_ref()) + .map_err(|err| anyhow::format_err!("Failed to link polkavm program: {}", err))?; + fs::write(output_path, linked).map_err(Into::into) + } + + /// Write the compiled contracts to the given output directory. + fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result<()> { + for entry in entries { + post_process( + &build_dir.join("target/riscv32ema-unknown-none-elf/release").join(entry.name()), + &out_dir.join(entry.out_filename()), + )?; + } + + Ok(()) + } + + pub fn run() -> Result<()> { + let fixtures_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into(); + let contracts_dir = fixtures_dir.join("contracts"); + let uapi_dir = fixtures_dir.parent().expect("uapi dir exits; qed").join("uapi"); + let out_dir: PathBuf = env::var("OUT_DIR")?.into(); + + // the fixtures have a dependency on the uapi crate + println!("cargo::rerun-if-changed={}", fixtures_dir.display()); + println!("cargo::rerun-if-changed={}", uapi_dir.display()); + + let entries = collect_entries(&contracts_dir); + if entries.is_empty() { + return Ok(()) + } + + let tmp_dir = tempfile::tempdir()?; + let tmp_dir_path = tmp_dir.path(); + + create_cargo_toml(&fixtures_dir, entries.iter(), tmp_dir.path())?; + invoke_build(tmp_dir_path)?; + + write_output(tmp_dir_path, &out_dir, entries)?; + Ok(()) + } +} + +#[cfg(not(feature = "riscv"))] +mod build { + use super::Result; + + pub fn run() -> Result<()> { + Ok(()) + } +} diff --git a/substrate/frame/revive/fixtures/build/Cargo.toml b/substrate/frame/revive/fixtures/build/Cargo.toml new file mode 100644 index 000000000000..7dead51b2306 --- /dev/null +++ b/substrate/frame/revive/fixtures/build/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "contracts" +publish = false +version = "1.0.0" +edition = "2021" + +# Binary targets are injected dynamically by the build script. +[[bin]] + +# All paths are injected dynamically by the build script. +[dependencies] +uapi = { package = 'pallet-revive-uapi', path = "", default-features = false } +common = { package = 'pallet-revive-fixtures-common', path = "" } +polkavm-derive = { version = "0.10.0" } + +[profile.release] +opt-level = 3 +lto = true +codegen-units = 1 diff --git a/substrate/frame/revive/fixtures/contracts/balance.rs b/substrate/frame/revive/fixtures/contracts/balance.rs new file mode 100644 index 000000000000..4011b8379cbf --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/balance.rs @@ -0,0 +1,36 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::output; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + // Initialize buffer with 1s so that we can check that it is overwritten. + output!(balance, [1u8; 8], api::balance,); + + // Assert that the balance is 0. + assert_eq!(&[0u8; 8], balance); +} diff --git a/substrate/frame/revive/fixtures/contracts/call.rs b/substrate/frame/revive/fixtures/contracts/call.rs new file mode 100644 index 000000000000..a75aee65c205 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call.rs @@ -0,0 +1,49 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This calls another contract as passed as its account id. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + callee_input: [u8; 4], + callee_addr: [u8; 32], + ); + + // Call the callee + api::call( + uapi::CallFlags::empty(), + callee_addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &0u64.to_le_bytes(), // Value transferred to the contract. + callee_input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_return_code.rs b/substrate/frame/revive/fixtures/contracts/call_return_code.rs new file mode 100644 index 000000000000..32654d59ef1f --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_return_code.rs @@ -0,0 +1,56 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This calls the supplied dest and transfers 100 balance during this call and copies +//! the return code of this call to the output buffer. +//! It also forwards its input to the callee. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 100, + callee_addr: [u8; 32], + input: [u8], + ); + + // Call the callee + let err_code = match api::call( + uapi::CallFlags::empty(), + callee_addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &100u64.to_le_bytes(), // Value transferred to the contract. + input, + None, + ) { + Ok(_) => 0u32, + Err(code) => code as u32, + }; + + api::return_value(uapi::ReturnFlags::empty(), &err_code.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_runtime.rs b/substrate/frame/revive/fixtures/contracts/call_runtime.rs new file mode 100644 index 000000000000..2b132398fb68 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_runtime.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This passes its input to `call_runtime` and returns the return value to its caller. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + // Fixture calls should fit into 100 bytes. + input!(100, call: [u8], ); + + // Use the call passed as input to call the runtime. + let err_code = match api::call_runtime(call) { + Ok(_) => 0u32, + Err(code) => code as u32, + }; + + api::return_value(uapi::ReturnFlags::empty(), &err_code.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs new file mode 100644 index 000000000000..1323c8c5d55d --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs @@ -0,0 +1,53 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 512, + callee_input: [u8; 4], + callee_addr: [u8; 32], + call: [u8], + ); + + // Use the call passed as input to call the runtime. + api::call_runtime(call).unwrap(); + + // Call the callee + api::call( + uapi::CallFlags::empty(), + callee_addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &0u64.to_le_bytes(), // Value transferred to the contract. + callee_input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs new file mode 100644 index 000000000000..a078162d7995 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs @@ -0,0 +1,51 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This fixture calls the account_id with the flags and value. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 256, + callee_addr: [u8; 32], + flags: u32, + value: u64, + forwarded_input: [u8], + ); + + api::call( + uapi::CallFlags::from_bits(flags).unwrap(), + callee_addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &value.to_le_bytes(), // Value transferred to the contract. + forwarded_input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs new file mode 100644 index 000000000000..a5356924f24f --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs @@ -0,0 +1,52 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This fixture calls the account_id with the 2D Weight limit. +//! It returns the result of the call as output data. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 256, + callee_addr: [u8; 32], + ref_time: u64, + proof_size: u64, + forwarded_input: [u8], + ); + + api::call( + uapi::CallFlags::empty(), + callee_addr, + ref_time, + proof_size, + None, // No deposit limit. + &0u64.to_le_bytes(), // value transferred to the contract. + forwarded_input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/caller_contract.rs b/substrate/frame/revive/fixtures/contracts/caller_contract.rs new file mode 100644 index 000000000000..2fa11df82d04 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/caller_contract.rs @@ -0,0 +1,145 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, ReturnErrorCode}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(code_hash: [u8; 32],); + + // The value to transfer on instantiation and calls. Chosen to be greater than existential + // deposit. + let value = 32768u64.to_le_bytes(); + let salt = [0u8; 0]; + + // Callee will use the first 4 bytes of the input to return an exit status. + let input = [0u8, 1, 34, 51, 68, 85, 102, 119]; + let reverted_input = [1u8, 34, 51, 68, 85, 102, 119]; + + // Fail to deploy the contract since it returns a non-zero exit status. + let res = api::instantiate( + code_hash, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &value, + &reverted_input, + None, + None, + &salt, + ); + assert!(matches!(res, Err(ReturnErrorCode::CalleeReverted))); + + // Fail to deploy the contract due to insufficient ref_time weight. + let res = api::instantiate( + code_hash, 1u64, // too little ref_time weight + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &value, &input, None, None, &salt, + ); + assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); + + // Fail to deploy the contract due to insufficient proof_size weight. + let res = api::instantiate( + code_hash, 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 1u64, // Too little proof_size weight + None, // No deposit limit. + &value, &input, None, None, &salt, + ); + assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); + + // Deploy the contract successfully. + let mut callee = [0u8; 32]; + let callee = &mut &mut callee[..]; + + api::instantiate( + code_hash, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &value, + &input, + Some(callee), + None, + &salt, + ) + .unwrap(); + assert_eq!(callee.len(), 32); + + // Call the new contract and expect it to return failing exit code. + let res = api::call( + uapi::CallFlags::empty(), + callee, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &value, + &reverted_input, + None, + ); + assert!(matches!(res, Err(ReturnErrorCode::CalleeReverted))); + + // Fail to call the contract due to insufficient ref_time weight. + let res = api::call( + uapi::CallFlags::empty(), + callee, + 1u64, // Too little ref_time weight. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &value, + &input, + None, + ); + assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); + + // Fail to call the contract due to insufficient proof_size weight. + let res = api::call( + uapi::CallFlags::empty(), + callee, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 1u64, // too little proof_size weight + None, // No deposit limit. + &value, + &input, + None, + ); + assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); + + // Call the contract successfully. + let mut output = [0u8; 4]; + api::call( + uapi::CallFlags::empty(), + callee, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &value, + &input, + Some(&mut &mut output[..]), + ) + .unwrap(); + assert_eq!(&output, &input[4..]) +} diff --git a/substrate/frame/revive/fixtures/contracts/caller_is_origin_n.rs b/substrate/frame/revive/fixtures/contracts/caller_is_origin_n.rs new file mode 100644 index 000000000000..fd6f59802fa0 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/caller_is_origin_n.rs @@ -0,0 +1,38 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This fixture calls caller_is_origin `n` times. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(n: u32, ); + + for _ in 0..n { + let _ = api::caller_is_origin(); + } +} diff --git a/substrate/frame/revive/fixtures/contracts/chain_extension.rs b/substrate/frame/revive/fixtures/contracts/chain_extension.rs new file mode 100644 index 000000000000..474df00d6912 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/chain_extension.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Call chain extension by passing through input and output of this contract. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(input, 8, func_id: u32,); + + // the chain extension passes through the input and returns it as output + let mut output_buffer = [0u8; 32]; + let output = &mut &mut output_buffer[0..input.len()]; + + let ret_id = api::call_chain_extension(func_id, input, Some(output)); + assert_eq!(ret_id, func_id); + + api::return_value(uapi::ReturnFlags::empty(), output); +} diff --git a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs new file mode 100644 index 000000000000..c7596e44dab3 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs @@ -0,0 +1,65 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Call chain extension two times with the specified func_ids +//! It then calls itself once +#![no_std] +#![no_main] + +use common::{input, output}; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + input, + func_id1: u32, + func_id2: u32, + stop_recurse: u8, + ); + + api::call_chain_extension(func_id1, input, None); + api::call_chain_extension(func_id2, input, None); + + if stop_recurse == 0 { + // Setup next call + input[0..4].copy_from_slice(&((3 << 16) | 2u32).to_le_bytes()); + input[4..8].copy_from_slice(&((3 << 16) | 3u32).to_le_bytes()); + input[8] = 1u8; + + // Read the contract address. + output!(addr, [0u8; 32], api::address,); + + // call self + api::call( + uapi::CallFlags::ALLOW_REENTRY, + addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &0u64.to_le_bytes(), // Value transferred to the contract. + input, + None, + ) + .unwrap(); + } +} diff --git a/substrate/frame/revive/fixtures/contracts/common/Cargo.toml b/substrate/frame/revive/fixtures/contracts/common/Cargo.toml new file mode 100644 index 000000000000..7dadb7b821a2 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/common/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "pallet-revive-fixtures-common" +publish = false +version = "1.0.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +description = "Common utilities for pallet-revive-fixtures." + +[dependencies] +uapi = { package = 'pallet-revive-uapi', path = "../../../uapi", default-features = false } diff --git a/substrate/frame/revive/fixtures/contracts/common/src/lib.rs b/substrate/frame/revive/fixtures/contracts/common/src/lib.rs new file mode 100644 index 000000000000..6631af8292fe --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/common/src/lib.rs @@ -0,0 +1,157 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![no_std] + +pub use uapi::{HostFn, HostFnImpl as api}; + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + // Safety: The unimp instruction is guaranteed to trap + unsafe { + core::arch::asm!("unimp"); + core::hint::unreachable_unchecked(); + } +} + +/// Utility macro to read input passed to a contract. +/// +/// Example: +/// +/// ``` +/// input$!( +/// var1: u32, // [0, 4) var1 decoded as u32 +/// var2: [u8; 32], // [4, 36) var2 decoded as a [u8] slice +/// var3: u8, // [36, 37) var3 decoded as a u8 +/// ); +/// +/// // Input and size can be specified as well: +/// input$!( +/// input, // input buffer (optional) +/// 512, // input size (optional) +/// var4: u32, // [0, 4) var4 decoded as u32 +/// var5: [u8], // [4, ..) var5 decoded as a [u8] slice +/// ); +/// ``` +#[macro_export] +macro_rules! input { + (@inner $input:expr, $cursor:expr,) => {}; + (@size $size:expr, ) => { $size }; + + // Match a u8 variable. + // e.g input!(var1: u8, ); + (@inner $input:expr, $cursor:expr, $var:ident: u8, $($rest:tt)*) => { + let $var = $input[$cursor]; + input!(@inner $input, $cursor + 1, $($rest)*); + }; + + // Size of u8 variable. + (@size $size:expr, $var:ident: u8, $($rest:tt)*) => { + input!(@size $size + 1, $($rest)*) + }; + + // Match a u64 variable. + // e.g input!(var1: u64, ); + (@inner $input:expr, $cursor:expr, $var:ident: u64, $($rest:tt)*) => { + let $var = u64::from_le_bytes($input[$cursor..$cursor + 8].try_into().unwrap()); + input!(@inner $input, $cursor + 8, $($rest)*); + }; + + // Size of u64 variable. + (@size $size:expr, $var:ident: u64, $($rest:tt)*) => { + input!(@size $size + 8, $($rest)*) + }; + + // Match a u32 variable. + // e.g input!(var1: u32, ); + (@inner $input:expr, $cursor:expr, $var:ident: u32, $($rest:tt)*) => { + let $var = u32::from_le_bytes($input[$cursor..$cursor + 4].try_into().unwrap()); + input!(@inner $input, $cursor + 4, $($rest)*); + }; + + // Size of u32 variable. + (@size $size:expr, $var:ident: u32, $($rest:tt)*) => { + input!(@size $size + 4, $($rest)*) + }; + + // Match a u8 slice with the remaining bytes. + // e.g input!(512, var1: [u8; 32], var2: [u8], ); + (@inner $input:expr, $cursor:expr, $var:ident: [u8],) => { + let $var = &$input[$cursor..]; + }; + + // Match a u8 slice of the given size. + // e.g input!(var1: [u8; 32], ); + (@inner $input:expr, $cursor:expr, $var:ident: [u8; $n:expr], $($rest:tt)*) => { + let $var = &$input[$cursor..$cursor+$n]; + input!(@inner $input, $cursor + $n, $($rest)*); + }; + + // Size of a u8 slice. + (@size $size:expr, $var:ident: [u8; $n:expr], $($rest:tt)*) => { + input!(@size $size + $n, $($rest)*) + }; + + // Entry point, with the buffer and it's size specified first. + // e.g input!(buffer, 512, var1: u32, var2: [u8], ); + ($buffer:ident, $size:expr, $($rest:tt)*) => { + let mut $buffer = [0u8; $size]; + let $buffer = &mut &mut $buffer[..]; + $crate::api::input($buffer); + input!(@inner $buffer, 0, $($rest)*); + }; + + // Entry point, with the name of the buffer specified and size of the input buffer computed. + // e.g input!(buffer, var1: u32, var2: u64, ); + ($buffer: ident, $($rest:tt)*) => { + input!($buffer, input!(@size 0, $($rest)*), $($rest)*); + }; + + // Entry point, with the size of the input buffer computed. + // e.g input!(var1: u32, var2: u64, ); + ($($rest:tt)*) => { + input!(buffer, $($rest)*); + }; +} + +/// Utility macro to invoke a host function that expect a `output: &mut &mut [u8]` as last argument. +/// +/// Example: +/// ``` +/// // call `api::caller` and store the output in `caller` +/// output!(caller, [0u8; 32], api::caller,); +/// +/// // call `api::get_storage` and store the output in `address` +/// output!(address, [0u8; 32], api::get_storage, &[1u8; 32]); +/// ``` +#[macro_export] +macro_rules! output { + ($output: ident, $buffer: expr, $host_fn:path, $($arg:expr),*) => { + let mut $output = $buffer; + let $output = &mut &mut $output[..]; + $host_fn($($arg,)* $output); + }; +} + +/// Similar to `output!` but unwraps the result. +#[macro_export] +macro_rules! unwrap_output { + ($output: ident, $buffer: expr, $host_fn:path, $($arg:expr),*) => { + let mut $output = $buffer; + let $output = &mut &mut $output[..]; + $host_fn($($arg,)* $output).unwrap(); + }; +} diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs new file mode 100644 index 000000000000..d0d3651dfe4d --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs @@ -0,0 +1,58 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This calls another contract as passed as its account id. It also creates some storage. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + buffer, + input: [u8; 4], + callee: [u8; 32], + deposit_limit: [u8; 8], + ); + + // create 4 byte of storage before calling + api::set_storage(StorageFlags::empty(), buffer, &[1u8; 4]); + + // Call the callee + api::call( + uapi::CallFlags::empty(), + callee, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + Some(deposit_limit), + &0u64.to_le_bytes(), // Value transferred to the contract. + input, + None, + ) + .unwrap(); + + // create 8 byte of storage after calling + // item of 12 bytes because we override 4 bytes + api::set_storage(StorageFlags::empty(), buffer, &[1u8; 12]); +} diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs new file mode 100644 index 000000000000..918a4abe6b21 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs @@ -0,0 +1,58 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This instantiates another contract and passes some input to its constructor. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + input: [u8; 4], + code_hash: [u8; 32], + deposit_limit: [u8; 8], + ); + + let value = 10_000u64.to_le_bytes(); + let salt = [0u8; 0]; + let mut address = [0u8; 32]; + let address = &mut &mut address[..]; + + api::instantiate( + code_hash, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + Some(deposit_limit), + &value, + input, + Some(address), + None, + &salt, + ) + .unwrap(); + + // Return the deployed contract address. + api::return_value(uapi::ReturnFlags::empty(), address); +} diff --git a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs new file mode 100644 index 000000000000..a2a0e85bcf64 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs @@ -0,0 +1,60 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This calls another contract as passed as its account id. It also creates some transient storage. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +static BUFFER: [u8; 512] = [0u8; 512]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + buffer, + len: u32, + input: [u8; 4], + callee: [u8; 32], + ); + + let rounds = len as usize / BUFFER.len(); + let rest = len as usize / BUFFER.len(); + for i in 0..rounds { + api::set_storage(StorageFlags::TRANSIENT, &i.to_le_bytes(), &BUFFER); + } + api::set_storage(StorageFlags::TRANSIENT, &u32::MAX.to_le_bytes(), &BUFFER[..rest]); + + // Call the callee + api::call( + uapi::CallFlags::empty(), + callee, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, + &0u64.to_le_bytes(), // Value transferred to the contract. + input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/crypto_hashes.rs b/substrate/frame/revive/fixtures/contracts/crypto_hashes.rs new file mode 100644 index 000000000000..35cc03f1e723 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/crypto_hashes.rs @@ -0,0 +1,84 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +/// Called by the tests. +/// +/// The `call` function expects data in a certain format in the input buffer. +/// +/// 1. The first byte encodes an identifier for the crypto hash function under test. (*) +/// 2. The rest encodes the input data that is directly fed into the crypto hash function chosen in +/// 1. +/// +/// The `deploy` function then computes the chosen crypto hash function +/// given the input and puts the result into the output buffer. +/// After contract execution the test driver then asserts that the returned +/// values are equal to the expected bytes for the input and chosen hash +/// function. +/// +/// (*) The possible value for the crypto hash identifiers can be found below: +/// +/// | value | Algorithm | Bit Width | +/// |-------|-----------|-----------| +/// | 0 | SHA2 | 256 | +/// | 1 | KECCAK | 256 | +/// | 2 | BLAKE2 | 256 | +/// | 3 | BLAKE2 | 128 | +/// --------------------------------- + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 256, + chosen_hash_fn: u8, + input: [u8], + ); + + match chosen_hash_fn { + 1 => { + let mut output = [0u8; 32]; + api::hash_sha2_256(input, &mut output); + api::return_value(uapi::ReturnFlags::empty(), &output); + }, + 2 => { + let mut output = [0u8; 32]; + api::hash_keccak_256(input, &mut output); + api::return_value(uapi::ReturnFlags::empty(), &output); + }, + 3 => { + let mut output = [0u8; 32]; + api::hash_blake2_256(input, &mut output); + api::return_value(uapi::ReturnFlags::empty(), &output); + }, + 4 => { + let mut output = [0u8; 16]; + api::hash_blake2_128(input, &mut output); + api::return_value(uapi::ReturnFlags::empty(), &output); + }, + _ => panic!("unknown crypto hash function identifier"), + } +} diff --git a/substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs b/substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs new file mode 100644 index 000000000000..6c850a9ec663 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs @@ -0,0 +1,33 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Emit a debug message with an invalid utf-8 code. +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + api::debug_message(b"\xFC").unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs b/substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs new file mode 100644 index 000000000000..0ce2b6b5628d --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs @@ -0,0 +1,33 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Emit a "Hello World!" debug message but assume that logging is disabled. +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api, ReturnErrorCode}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + assert_eq!(api::debug_message(b"Hello World!"), Err(ReturnErrorCode::LoggingDisabled)); +} diff --git a/substrate/frame/revive/fixtures/contracts/debug_message_works.rs b/substrate/frame/revive/fixtures/contracts/debug_message_works.rs new file mode 100644 index 000000000000..3a2509509d8f --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/debug_message_works.rs @@ -0,0 +1,33 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Emit a "Hello World!" debug message. +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + api::debug_message(b"Hello World!").unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call.rs b/substrate/frame/revive/fixtures/contracts/delegate_call.rs new file mode 100644 index 000000000000..d03ddab1bc58 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/delegate_call.rs @@ -0,0 +1,49 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(code_hash: [u8; 32],); + + let mut key = [0u8; 32]; + key[0] = 1u8; + + let mut value = [0u8; 32]; + let value = &mut &mut value[..]; + value[0] = 2u8; + + api::set_storage(StorageFlags::empty(), &key, value); + api::get_storage(StorageFlags::empty(), &key, value).unwrap(); + assert!(value[0] == 2u8); + + let input = [0u8; 0]; + api::delegate_call(uapi::CallFlags::empty(), code_hash, &input, None).unwrap(); + + api::get_storage(StorageFlags::empty(), &key, value).unwrap(); + assert!(value[0] == 1u8); +} diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs new file mode 100644 index 000000000000..055760729bd2 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs @@ -0,0 +1,49 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::output; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let mut key = [0u8; 32]; + key[0] = 1u8; + + // Place a value in storage. + let mut value = [0u8; 32]; + let value = &mut &mut value[..]; + value[0] = 1u8; + api::set_storage(StorageFlags::empty(), &key, value); + + // Assert that `value_transferred` is equal to the value + // passed to the `caller` contract: 1337. + output!(value_transferred, [0u8; 8], api::value_transferred,); + let value_transferred = u64::from_le_bytes(value_transferred[..].try_into().unwrap()); + assert_eq!(value_transferred, 1337); + + // Assert that ALICE is the caller of the contract. + output!(caller, [0u8; 32], api::caller,); + assert_eq!(&caller[..], &[1u8; 32]); +} diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs new file mode 100644 index 000000000000..cf3351c52fdc --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs @@ -0,0 +1,36 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(code_hash: [u8; 32],); + + // Delegate call into passed code hash. + let input = [0u8; 0]; + api::delegate_call(uapi::CallFlags::empty(), code_hash, &input, None).unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs new file mode 100644 index 000000000000..8b74493a1e38 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs @@ -0,0 +1,86 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +const ADDRESS_KEY: [u8; 32] = [0u8; 32]; +const VALUE: [u8; 8] = [0, 0, 1u8, 0, 0, 0, 0, 0]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + input!(code_hash: [u8; 32],); + + let input = [0u8; 0]; + let mut address = [0u8; 32]; + let address = &mut &mut address[..]; + let salt = [71u8, 17u8]; + + api::instantiate( + code_hash, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &VALUE, + &input, + Some(address), + None, + &salt, + ) + .unwrap(); + + // Return the deployed contract address. + api::set_storage(StorageFlags::empty(), &ADDRESS_KEY, address); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let mut callee_addr = [0u8; 32]; + let callee_addr = &mut &mut callee_addr[..]; + api::get_storage(StorageFlags::empty(), &ADDRESS_KEY, callee_addr).unwrap(); + + // Calling the destination contract with non-empty input data should fail. + let res = api::call( + uapi::CallFlags::empty(), + callee_addr, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &VALUE, + &[0u8; 1], + None, + ); + assert!(matches!(res, Err(uapi::ReturnErrorCode::CalleeTrapped))); + + // Call the destination contract regularly, forcing it to self-destruct. + api::call( + uapi::CallFlags::empty(), + callee_addr, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &VALUE, + &[0u8; 0], + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/drain.rs b/substrate/frame/revive/fixtures/contracts/drain.rs new file mode 100644 index 000000000000..f5c8681c9382 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/drain.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::output; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + output!(balance, [0u8; 8], api::balance,); + let balance = u64::from_le_bytes(balance[..].try_into().unwrap()); + + output!(minimum_balance, [0u8; 8], api::minimum_balance,); + let minimum_balance = u64::from_le_bytes(minimum_balance[..].try_into().unwrap()); + + // Make the transferred value exceed the balance by adding the minimum balance. + let balance = balance + minimum_balance; + + // Try to self-destruct by sending more balance to the 0 address. + // The call will fail because a contract transfer has a keep alive requirement. + let res = api::transfer(&[0u8; 32], &balance.to_le_bytes()); + assert!(matches!(res, Err(uapi::ReturnErrorCode::TransferFailed))); +} diff --git a/substrate/frame/revive/fixtures/contracts/dummy.rs b/substrate/frame/revive/fixtures/contracts/dummy.rs new file mode 100644 index 000000000000..c7294e99139e --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/dummy.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![no_std] +#![no_main] + +extern crate common; + +use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; + +// Export that is never called. We can put code here that should be in the binary +// but is never supposed to be run. +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call_never() { + // Make sure the 0xDEADBEEF pattern appears in the binary by + // making it opaque to the optimizer. The benchmarking code will + // just find and replace this pattern to make the code unique when + // necessary. + api::return_value(ReturnFlags::empty(), &[0xDE, 0xAD, 0xBE, 0xEF]); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} diff --git a/substrate/frame/revive/fixtures/contracts/ecdsa_recover.rs b/substrate/frame/revive/fixtures/contracts/ecdsa_recover.rs new file mode 100644 index 000000000000..0f28ca2c8198 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/ecdsa_recover.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + signature: [u8; 65], + hash: [u8; 32], + ); + + let mut output = [0u8; 33]; + api::ecdsa_recover( + &signature[..].try_into().unwrap(), + &hash[..].try_into().unwrap(), + &mut output, + ) + .unwrap(); + api::return_value(uapi::ReturnFlags::empty(), &output); +} diff --git a/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs b/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs new file mode 100644 index 000000000000..9186835d2911 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs @@ -0,0 +1,36 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + let buffer = [1u8, 2, 3, 4]; + api::deposit_event(&[0u8; 0], &buffer); + api::return_value(uapi::ReturnFlags::empty(), &buffer); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + unreachable!() +} diff --git a/substrate/frame/revive/fixtures/contracts/event_size.rs b/substrate/frame/revive/fixtures/contracts/event_size.rs new file mode 100644 index 000000000000..2b56de4bd3fd --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/event_size.rs @@ -0,0 +1,38 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +static BUFFER: [u8; 16 * 1024 + 1] = [0u8; 16 * 1024 + 1]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(len: u32,); + + let data = &BUFFER[..len as usize]; + + api::deposit_event(&[0u8; 0], data); +} diff --git a/substrate/frame/revive/fixtures/contracts/float_instruction.rs b/substrate/frame/revive/fixtures/contracts/float_instruction.rs new file mode 100644 index 000000000000..b1eaaf8543c6 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/float_instruction.rs @@ -0,0 +1,34 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} + +#[no_mangle] +pub extern "C" fn add(a: f32, b: f32) -> f32 { + a + b +} diff --git a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs new file mode 100644 index 000000000000..90884f1a2a68 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs @@ -0,0 +1,52 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(buffer, 36, code_hash: [u8; 32],); + let input = &buffer[32..]; + + let err_code = match api::instantiate( + code_hash, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, /* How much proof_size weight to devote for the execution. 0 = + * all. */ + None, // No deposit limit. + &10_000u64.to_le_bytes(), // Value to transfer. + input, + None, + None, + &[0u8; 0], // Empty salt. + ) { + Ok(_) => 0u32, + Err(code) => code as u32, + }; + + // Exit with success and take transfer return code to the output buffer. + api::return_value(uapi::ReturnFlags::empty(), &err_code.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/instr_benchmark.rs b/substrate/frame/revive/fixtures/contracts/instr_benchmark.rs new file mode 100644 index 000000000000..c5fb382c3276 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/instr_benchmark.rs @@ -0,0 +1,41 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![no_std] +#![no_main] + +extern crate common; + +use common::input; +use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(rounds: u32, start: u32, div: u32, mult: u32, add: u32, ); + + let mut acc = start; + + for _ in 0..rounds { + acc = acc / div * mult + add; + } + + api::return_value(ReturnFlags::empty(), start.to_le_bytes().as_ref()); +} diff --git a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs new file mode 100644 index 000000000000..3ed886b49489 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs @@ -0,0 +1,68 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This contract tests the behavior of locking / unlocking delegate_dependencies when delegate +//! calling into a contract. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +const ALICE: [u8; 32] = [1u8; 32]; + +/// Load input data and perform the action specified by the input. +/// If `delegate_call` is true, then delegate call into the contract. +fn load_input(delegate_call: bool) { + input!( + action: u32, + code_hash: [u8; 32], + ); + + match action { + // 1 = Lock delegate dependency + 1 => { + api::lock_delegate_dependency(code_hash); + }, + // 2 = Unlock delegate dependency + 2 => { + api::unlock_delegate_dependency(code_hash); + }, + // 3 = Terminate + 3 => { + api::terminate(&ALICE); + }, + // Everything else is a noop + _ => {}, + } + + if delegate_call { + api::delegate_call(uapi::CallFlags::empty(), code_hash, &[], None).unwrap(); + } +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + load_input(false); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + load_input(true); +} diff --git a/substrate/frame/revive/fixtures/contracts/multi_store.rs b/substrate/frame/revive/fixtures/contracts/multi_store.rs new file mode 100644 index 000000000000..079a4548e78d --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/multi_store.rs @@ -0,0 +1,43 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Does two stores to two separate storage items +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +static BUFFER: [u8; 512] = [0u8; 512]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + size1: u32, + size2: u32, + ); + + // Place a values in storage sizes are specified in the input buffer. + // We don't care about the contents of the storage item. + api::set_storage(StorageFlags::empty(), &[1u8; 32], &BUFFER[0..size1 as _]); + api::set_storage(StorageFlags::empty(), &[2u8; 32], &BUFFER[0..size2 as _]); +} diff --git a/substrate/frame/revive/fixtures/contracts/new_set_code_hash_contract.rs b/substrate/frame/revive/fixtures/contracts/new_set_code_hash_contract.rs new file mode 100644 index 000000000000..2a59b6e33d89 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/new_set_code_hash_contract.rs @@ -0,0 +1,32 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + api::return_value(uapi::ReturnFlags::empty(), &2u32.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/noop.rs b/substrate/frame/revive/fixtures/contracts/noop.rs new file mode 100644 index 000000000000..48d8a6896d62 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/noop.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![no_std] +#![no_main] + +extern crate common; + +use common::input; +use uapi::HostFn; + +#[polkavm_derive::polkavm_import] +extern "C" { + pub fn noop(); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(rounds: u32, ); + + for _ in 0..rounds { + unsafe { + noop(); + } + } +} diff --git a/substrate/frame/revive/fixtures/contracts/ok_trap_revert.rs b/substrate/frame/revive/fixtures/contracts/ok_trap_revert.rs new file mode 100644 index 000000000000..55115f8642f3 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/ok_trap_revert.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + ok_trap_revert(); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + ok_trap_revert(); +} + +#[no_mangle] +fn ok_trap_revert() { + input!(buffer, 4,); + match buffer.first().unwrap_or(&0) { + 1 => api::return_value(uapi::ReturnFlags::REVERT, &[0u8; 0]), + 2 => panic!(), + _ => {}, + }; +} diff --git a/substrate/frame/revive/fixtures/contracts/read_only_call.rs b/substrate/frame/revive/fixtures/contracts/read_only_call.rs new file mode 100644 index 000000000000..a62bb205039f --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/read_only_call.rs @@ -0,0 +1,50 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This fixture tests if read-only call works as expected. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 256, + callee_addr: [u8; 32], + callee_input: [u8], + ); + + // Call the callee + api::call( + uapi::CallFlags::READ_ONLY, + callee_addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &0u64.to_le_bytes(), // Value transferred to the contract. + callee_input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/recurse.rs b/substrate/frame/revive/fixtures/contracts/recurse.rs new file mode 100644 index 000000000000..f4dfd6c965d0 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/recurse.rs @@ -0,0 +1,53 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This fixture calls itself as many times as passed as argument. + +#![no_std] +#![no_main] + +use common::{input, output}; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(calls_left: u32, ); + + // own address + output!(addr, [0u8; 32], api::address,); + + if calls_left == 0 { + return + } + + api::call( + uapi::CallFlags::ALLOW_REENTRY, + addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much deposit_limit to devote for the execution. 0 = all. + None, // No deposit limit. + &0u64.to_le_bytes(), // Value transferred to the contract. + &(calls_left - 1).to_le_bytes(), + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/return_with_data.rs b/substrate/frame/revive/fixtures/contracts/return_with_data.rs new file mode 100644 index 000000000000..47a1cc911192 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/return_with_data.rs @@ -0,0 +1,47 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + call(); +} + +/// Reads the first byte as the exit status and copy all but the first 4 bytes of the input as +/// output data. +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + input, 128, + exit_status: [u8; 4], + output: [u8], + ); + + // Burn some PoV, clear_storage consumes some PoV as in order to clear the storage we need to we + // need to read its size first. + api::clear_storage(StorageFlags::empty(), b""); + + let exit_status = uapi::ReturnFlags::from_bits(exit_status[0] as u32).unwrap(); + api::return_value(exit_status, output); +} diff --git a/substrate/frame/revive/fixtures/contracts/run_out_of_gas.rs b/substrate/frame/revive/fixtures/contracts/run_out_of_gas.rs new file mode 100644 index 000000000000..11eaaa7c8624 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/run_out_of_gas.rs @@ -0,0 +1,32 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + #[allow(clippy::empty_loop)] + loop {} +} diff --git a/substrate/frame/revive/fixtures/contracts/self_destruct.rs b/substrate/frame/revive/fixtures/contracts/self_destruct.rs new file mode 100644 index 000000000000..10f33226acf2 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/self_destruct.rs @@ -0,0 +1,55 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::{input, output}; +use uapi::{HostFn, HostFnImpl as api}; + +const DJANGO: [u8; 32] = [4u8; 32]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + // If the input data is not empty, then recursively call self with empty input data. + // This should trap instead of self-destructing since a contract cannot be removed, while it's + // in the execution stack. If the recursive call traps, then trap here as well. + input!(input, 4,); + + if !input.is_empty() { + output!(addr, [0u8; 32], api::address,); + api::call( + uapi::CallFlags::ALLOW_REENTRY, + addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &0u64.to_le_bytes(), // Value to transfer. + &[0u8; 0], + None, + ) + .unwrap(); + } else { + // Try to terminate and give balance to django. + api::terminate(&DJANGO); + } +} diff --git a/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs b/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs new file mode 100644 index 000000000000..28bcef978231 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs @@ -0,0 +1,32 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + api::terminate(&[0u8; 32]); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} diff --git a/substrate/frame/revive/fixtures/contracts/set_code_hash.rs b/substrate/frame/revive/fixtures/contracts/set_code_hash.rs new file mode 100644 index 000000000000..e3cf4becfb97 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/set_code_hash.rs @@ -0,0 +1,37 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(addr: [u8; 32],); + api::set_code_hash(addr).unwrap(); + + // we return 1 after setting new code_hash + // next `call` will NOT return this value, because contract code has been changed + api::return_value(uapi::ReturnFlags::empty(), &1u32.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/set_empty_storage.rs b/substrate/frame/revive/fixtures/contracts/set_empty_storage.rs new file mode 100644 index 000000000000..f8bbfe3faa5b --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/set_empty_storage.rs @@ -0,0 +1,32 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + api::set_storage(StorageFlags::empty(), &[0u8; 32], &[0u8; 4]); +} diff --git a/substrate/frame/revive/fixtures/contracts/set_transient_storage.rs b/substrate/frame/revive/fixtures/contracts/set_transient_storage.rs new file mode 100644 index 000000000000..a8a1fbd65148 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/set_transient_storage.rs @@ -0,0 +1,41 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +static BUFFER: [u8; 512] = [0u8; 512]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(len: u32, ); + + let rounds = len as usize / BUFFER.len(); + let rest = len as usize / BUFFER.len(); + for i in 0..rounds { + api::set_storage(StorageFlags::TRANSIENT, &i.to_le_bytes(), &BUFFER); + } + api::set_storage(StorageFlags::TRANSIENT, &u32::MAX.to_le_bytes(), &BUFFER[..rest]); +} diff --git a/substrate/frame/revive/fixtures/contracts/sr25519_verify.rs b/substrate/frame/revive/fixtures/contracts/sr25519_verify.rs new file mode 100644 index 000000000000..8920ce0d4f6c --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/sr25519_verify.rs @@ -0,0 +1,48 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + signature: [u8; 64], + pub_key: [u8; 32], + msg: [u8; 11], + ); + + let exit_status = match api::sr25519_verify( + &signature.try_into().unwrap(), + msg, + &pub_key.try_into().unwrap(), + ) { + Ok(_) => 0u32, + Err(code) => code as u32, + }; + + // Exit with success and take transfer return code to the output buffer. + api::return_value(uapi::ReturnFlags::empty(), &exit_status.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/storage.rs b/substrate/frame/revive/fixtures/contracts/storage.rs new file mode 100644 index 000000000000..dc21e322466c --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/storage.rs @@ -0,0 +1,63 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This contract tests the storage APIs. It sets and clears storage values using the different +//! versions of the storage APIs. +#![no_std] +#![no_main] + +use common::unwrap_output; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + const KEY: [u8; 32] = [1u8; 32]; + const VALUE_1: [u8; 4] = [1u8; 4]; + const VALUE_2: [u8; 4] = [2u8; 4]; + const VALUE_3: [u8; 4] = [3u8; 4]; + + api::set_storage(StorageFlags::empty(), &KEY, &VALUE_1); + assert_eq!(api::contains_storage(StorageFlags::empty(), &KEY), Some(VALUE_1.len() as _)); + unwrap_output!(val, [0u8; 4], api::get_storage, StorageFlags::empty(), &KEY); + assert_eq!(**val, VALUE_1); + + let existing = api::set_storage(StorageFlags::empty(), &KEY, &VALUE_2); + assert_eq!(existing, Some(VALUE_1.len() as _)); + unwrap_output!(val, [0u8; 4], api::get_storage, StorageFlags::empty(), &KEY); + assert_eq!(**val, VALUE_2); + + api::clear_storage(StorageFlags::empty(), &KEY); + assert_eq!(api::contains_storage(StorageFlags::empty(), &KEY), None); + + let existing = api::set_storage(StorageFlags::empty(), &KEY, &VALUE_3); + assert_eq!(existing, None); + assert_eq!(api::contains_storage(StorageFlags::empty(), &KEY), Some(VALUE_1.len() as _)); + unwrap_output!(val, [0u8; 32], api::get_storage, StorageFlags::empty(), &KEY); + assert_eq!(**val, VALUE_3); + + api::clear_storage(StorageFlags::empty(), &KEY); + assert_eq!(api::contains_storage(StorageFlags::empty(), &KEY), None); + let existing = api::set_storage(StorageFlags::empty(), &KEY, &VALUE_3); + assert_eq!(existing, None); + unwrap_output!(val, [0u8; 32], api::take_storage, StorageFlags::empty(), &KEY); + assert_eq!(**val, VALUE_3); +} diff --git a/substrate/frame/revive/fixtures/contracts/storage_size.rs b/substrate/frame/revive/fixtures/contracts/storage_size.rs new file mode 100644 index 000000000000..617e8d2ea79f --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/storage_size.rs @@ -0,0 +1,50 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +static mut BUFFER: [u8; 16 * 1024 + 1] = [0u8; 16 * 1024 + 1]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(len: u32, ); + + let data = unsafe { + &BUFFER[..len as usize] + }; + + // Place a garbage value in storage, the size of which is specified by the call input. + let mut key = [0u8; 32]; + key[0] = 1; + + api::set_storage(StorageFlags::empty(), &key, data); + + let data = unsafe { + &mut &mut BUFFER[..] + }; + api::get_storage(StorageFlags::empty(), &key, data).unwrap(); + assert_eq!(data.len(), len as usize); +} diff --git a/substrate/frame/revive/fixtures/contracts/store_call.rs b/substrate/frame/revive/fixtures/contracts/store_call.rs new file mode 100644 index 000000000000..b08d445191e5 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/store_call.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +static BUFFER: [u8; 512] = [0u8; 512]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(len: u32, ); + + let data = &BUFFER[..len as usize]; + + // Place a garbage value in storage, the size of which is specified by the call input. + let mut key = [0u8; 32]; + key[0] = 1; + + api::set_storage(StorageFlags::empty(), &key, data); +} diff --git a/substrate/frame/revive/fixtures/contracts/store_deploy.rs b/substrate/frame/revive/fixtures/contracts/store_deploy.rs new file mode 100644 index 000000000000..e08c79d78f3b --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/store_deploy.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +static BUFFER: [u8; 16 * 1024 + 1] = [0u8; 16 * 1024 + 1]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + input!(len: u32, ); + + let data = &BUFFER[..len as usize]; + + // place a garbage value in storage, the size of which is specified by the call input. + let mut key = [0u8; 32]; + key[0] = 1; + + api::set_storage(StorageFlags::empty(), &key, data); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} diff --git a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs new file mode 100644 index 000000000000..d3f6a1dd3a08 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs @@ -0,0 +1,38 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let ret_code = match api::transfer(&[0u8; 32], &100u64.to_le_bytes()) { + Ok(_) => 0u32, + Err(code) => code as u32, + }; + + // Exit with success and take transfer return code to the output buffer. + api::return_value(uapi::ReturnFlags::empty(), &ret_code.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/transient_storage.rs b/substrate/frame/revive/fixtures/contracts/transient_storage.rs new file mode 100644 index 000000000000..aa0a69435a69 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/transient_storage.rs @@ -0,0 +1,55 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This contract tests the transient storage APIs. +#![no_std] +#![no_main] + +use common::unwrap_output; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + const KEY: [u8; 32] = [1u8; 32]; + const VALUE_1: [u8; 4] = [1u8; 4]; + const VALUE_2: [u8; 5] = [2u8; 5]; + const VALUE_3: [u8; 6] = [3u8; 6]; + + let existing = api::set_storage(StorageFlags::TRANSIENT, &KEY, &VALUE_1); + assert_eq!(existing, None); + assert_eq!(api::contains_storage(StorageFlags::TRANSIENT, &KEY), Some(VALUE_1.len() as _)); + unwrap_output!(val, [0u8; 32], api::get_storage, StorageFlags::TRANSIENT, &KEY); + assert_eq!(**val, VALUE_1); + + let existing = api::set_storage(StorageFlags::TRANSIENT, &KEY, &VALUE_2); + assert_eq!(existing, Some(VALUE_1.len() as _)); + unwrap_output!(val, [0u8; 32], api::get_storage, StorageFlags::TRANSIENT, &KEY); + assert_eq!(**val, VALUE_2); + + assert_eq!(api::clear_storage(StorageFlags::TRANSIENT, &KEY), Some(VALUE_2.len() as _)); + assert_eq!(api::contains_storage(StorageFlags::TRANSIENT, &KEY), None); + + let existing = api::set_storage(StorageFlags::TRANSIENT, &KEY, &VALUE_3); + assert_eq!(existing, None); + unwrap_output!(val, [0u8; 128], api::take_storage, StorageFlags::TRANSIENT, &KEY); + assert_eq!(**val, VALUE_3); +} diff --git a/substrate/frame/revive/fixtures/contracts/xcm_execute.rs b/substrate/frame/revive/fixtures/contracts/xcm_execute.rs new file mode 100644 index 000000000000..1d570ffead71 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/xcm_execute.rs @@ -0,0 +1,40 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(512, msg: [u8],); + + #[allow(deprecated)] + let err_code = match api::xcm_execute(msg) { + Ok(_) => 0u32, + Err(code) => code as u32, + }; + + api::return_value(uapi::ReturnFlags::empty(), &err_code.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/xcm_send.rs b/substrate/frame/revive/fixtures/contracts/xcm_send.rs new file mode 100644 index 000000000000..6d4629e748a7 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/xcm_send.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 512, + dest: [u8; 3], + msg: [u8], + ); + + let mut message_id = [0u8; 32]; + + #[allow(deprecated)] + api::xcm_send(dest, msg, &mut message_id).unwrap(); + api::return_value(uapi::ReturnFlags::empty(), &message_id); +} diff --git a/substrate/frame/revive/fixtures/src/lib.rs b/substrate/frame/revive/fixtures/src/lib.rs new file mode 100644 index 000000000000..1b6103f57aee --- /dev/null +++ b/substrate/frame/revive/fixtures/src/lib.rs @@ -0,0 +1,79 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +/// Load a given wasm module and returns a wasm binary contents along with it's hash. +#[cfg(feature = "std")] +pub fn compile_module( + fixture_name: &str, +) -> anyhow::Result<(Vec, ::Output)> +where + T: frame_system::Config, +{ + use sp_runtime::traits::Hash; + let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); + let fixture_path = out_dir.join(format!("{fixture_name}.polkavm")); + let binary = std::fs::read(fixture_path)?; + let code_hash = T::Hashing::hash(&binary); + Ok((binary, code_hash)) +} + +/// Fixtures used in runtime benchmarks. +/// +/// We explicitly include those fixtures into the binary to make them +/// available in no-std environments (runtime benchmarks). +pub mod bench { + use alloc::vec::Vec; + + #[cfg(feature = "riscv")] + macro_rules! fixture { + ($name: literal) => { + include_bytes!(concat!(env!("OUT_DIR"), "/", $name, ".polkavm")) + }; + } + #[cfg(not(feature = "riscv"))] + macro_rules! fixture { + ($name: literal) => { + &[] + }; + } + pub const DUMMY: &[u8] = fixture!("dummy"); + pub const NOOP: &[u8] = fixture!("noop"); + pub const INSTR: &[u8] = fixture!("instr_benchmark"); + + pub fn dummy_unique(replace_with: u32) -> Vec { + let mut dummy = DUMMY.to_vec(); + let idx = dummy + .windows(4) + .position(|w| w == &[0xDE, 0xAD, 0xBE, 0xEF]) + .expect("Benchmark fixture contains this pattern; qed"); + dummy[idx..idx + 4].copy_from_slice(&replace_with.to_le_bytes()); + dummy + } +} + +#[cfg(test)] +mod test { + #[test] + fn out_dir_should_have_compiled_mocks() { + let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); + assert!(out_dir.join("dummy.polkavm").exists()); + } +} diff --git a/substrate/frame/revive/mock-network/Cargo.toml b/substrate/frame/revive/mock-network/Cargo.toml new file mode 100644 index 000000000000..0d597bbdc228 --- /dev/null +++ b/substrate/frame/revive/mock-network/Cargo.toml @@ -0,0 +1,88 @@ +[package] +name = "pallet-revive-mock-network" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +description = "A mock network for testing pallet-revive." + +[lints] +workspace = true + +[dependencies] +codec = { features = ["derive", "max-encoded-len"], workspace = true } + +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-assets = { workspace = true, default-features = true } +pallet-balances = { workspace = true, default-features = true } +pallet-revive = { workspace = true, default-features = true } +pallet-revive-uapi = { workspace = true } +pallet-revive-proc-macro = { workspace = true, default-features = true } +pallet-message-queue = { workspace = true, default-features = true } +pallet-proxy = { workspace = true, default-features = true } +pallet-timestamp = { workspace = true, default-features = true } +pallet-utility = { workspace = true, default-features = true } +pallet-xcm = { workspace = true } +polkadot-parachain-primitives = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, default-features = true } +polkadot-runtime-parachains = { workspace = true, default-features = true } +scale-info = { features = ["derive"], workspace = true } +sp-api = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-keystore = { workspace = true, default-features = true } +sp-runtime = { workspace = true } +sp-tracing = { workspace = true, default-features = true } +xcm = { workspace = true } +xcm-builder = { workspace = true, default-features = true } +xcm-executor = { workspace = true } +xcm-simulator = { workspace = true, default-features = true } + +[dev-dependencies] +assert_matches = { workspace = true } +pretty_assertions = { workspace = true } +pallet-revive-fixtures = { workspace = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-proxy/std", + "pallet-revive-fixtures/std", + "pallet-revive/std", + "pallet-timestamp/std", + "pallet-utility/std", + "pallet-xcm/std", + "scale-info/std", + "sp-api/std", + "sp-core/std", + "sp-io/std", + "sp-keystore/std", + "sp-runtime/std", + "xcm-executor/std", + "xcm/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-revive/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "polkadot-runtime-parachains/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", +] diff --git a/substrate/frame/revive/mock-network/src/lib.rs b/substrate/frame/revive/mock-network/src/lib.rs new file mode 100644 index 000000000000..2e4f273a0103 --- /dev/null +++ b/substrate/frame/revive/mock-network/src/lib.rs @@ -0,0 +1,152 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +pub mod mocks; +pub mod parachain; +pub mod primitives; +pub mod relay_chain; + +#[cfg(all(test, feature = "riscv"))] +mod tests; + +use crate::primitives::{AccountId, UNITS}; +pub use pallet_revive::test_utils::{ALICE, BOB}; +use sp_runtime::BuildStorage; +use xcm::latest::prelude::*; +use xcm_executor::traits::ConvertLocation; +pub use xcm_simulator::TestExt; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; + +// Accounts +pub const ADMIN: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]); + +// Balances +pub const INITIAL_BALANCE: u128 = 1_000_000_000 * UNITS; + +decl_test_parachain! { + pub struct ParaA { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(1), + } +} + +decl_test_relay_chain! { + pub struct Relay { + Runtime = relay_chain::Runtime, + RuntimeCall = relay_chain::RuntimeCall, + RuntimeEvent = relay_chain::RuntimeEvent, + XcmConfig = relay_chain::XcmConfig, + MessageQueue = relay_chain::MessageQueue, + System = relay_chain::System, + new_ext = relay_ext(), + } +} + +decl_test_network! { + pub struct MockNet { + relay_chain = Relay, + parachains = vec![ + (1, ParaA), + ], + } +} + +pub fn relay_sovereign_account_id() -> AccountId { + let location: Location = (Parent,).into(); + parachain::SovereignAccountOf::convert_location(&location).unwrap() +} + +pub fn parachain_sovereign_account_id(para: u32) -> AccountId { + let location: Location = (Parachain(para),).into(); + relay_chain::SovereignAccountOf::convert_location(&location).unwrap() +} + +pub fn parachain_account_sovereign_account_id( + para: u32, + who: sp_runtime::AccountId32, +) -> AccountId { + let location: Location = ( + Parachain(para), + AccountId32 { network: Some(relay_chain::RelayNetwork::get()), id: who.into() }, + ) + .into(); + relay_chain::SovereignAccountOf::convert_location(&location).unwrap() +} + +pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { + use parachain::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (relay_sovereign_account_id(), INITIAL_BALANCE), + (BOB, INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_assets::GenesisConfig:: { + assets: vec![ + (0u128, ADMIN, false, 1u128), // Create derivative asset for relay's native token + ], + metadata: Default::default(), + accounts: vec![ + (0u128, ALICE, INITIAL_BALANCE), + (0u128, relay_sovereign_account_id(), INITIAL_BALANCE), + ], + next_asset_id: None, + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + sp_tracing::try_init_simple(); + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub fn relay_ext() -> sp_io::TestExternalities { + use relay_chain::{Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (parachain_sovereign_account_id(1), INITIAL_BALANCE), + (parachain_account_sovereign_account_id(1, ALICE), INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext +} + +pub type ParachainPalletXcm = pallet_xcm::Pallet; +pub type ParachainBalances = pallet_balances::Pallet; diff --git a/substrate/frame/revive/mock-network/src/mocks.rs b/substrate/frame/revive/mock-network/src/mocks.rs new file mode 100644 index 000000000000..bf3baec7a524 --- /dev/null +++ b/substrate/frame/revive/mock-network/src/mocks.rs @@ -0,0 +1,18 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +pub mod msg_queue; +pub mod relay_message_queue; diff --git a/substrate/frame/revive/mock-network/src/mocks/msg_queue.rs b/substrate/frame/revive/mock-network/src/mocks/msg_queue.rs new file mode 100644 index 000000000000..6e922c16c298 --- /dev/null +++ b/substrate/frame/revive/mock-network/src/mocks/msg_queue.rs @@ -0,0 +1,186 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Parachain runtime mock. + +use codec::{Decode, Encode}; + +use frame_support::weights::Weight; +use polkadot_parachain_primitives::primitives::{ + DmpMessageHandler, Id as ParaId, XcmpMessageFormat, XcmpMessageHandler, +}; +use polkadot_primitives::BlockNumber as RelayBlockNumber; +use sp_runtime::traits::{Get, Hash}; + +use xcm::{latest::prelude::*, VersionedXcm}; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::storage] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + #[pallet::storage] + /// A queue of received DMP messages + pub(super) type ReceivedDmp = StorageValue<_, Vec>, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + ParachainId::::get() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Some XCM was executed OK. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // DMP + /// Downward message is invalid XCM. + InvalidFormat(MessageId), + /// Downward message is unsupported version of XCM. + UnsupportedVersion(MessageId), + /// Downward message executed with the given outcome. + ExecutedDownward(MessageId, Outcome), + } + + impl Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + pub fn parachain_id() -> ParaId { + ParachainId::::get() + } + + pub fn received_dmp() -> Vec> { + ReceivedDmp::::get() + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let mut message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256); + let (result, event) = match Xcm::::try_from(xcm) { + Ok(xcm) => { + let location = (Parent, Parachain(sender.into())); + match T::XcmExecutor::prepare_and_execute( + location, + xcm, + &mut message_hash, + max_weight, + Weight::zero(), + ) { + Outcome::Error { error } => (Err(error), Event::Fail(Some(hash), error)), + Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))), + // As far as the caller is concerned, this was dispatched without error, so + // we just report the weight used. + Outcome::Incomplete { used, error } => + (Ok(used), Event::Fail(Some(hash), error)), + } + }, + Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + max_weight: Weight, + ) -> Weight { + for (sender, sent_at, data) in iter { + let mut data_ref = data; + let _ = XcmpMessageFormat::decode(&mut data_ref) + .expect("Simulator encodes with versioned xcm format; qed"); + + let mut remaining_fragments = data_ref; + while !remaining_fragments.is_empty() { + if let Ok(xcm) = + VersionedXcm::::decode(&mut remaining_fragments) + { + let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); + } else { + debug_assert!(false, "Invalid incoming XCMP message data"); + } + } + } + max_weight + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let mut id = sp_io::hashing::blake2_256(&data[..]); + let maybe_versioned = VersionedXcm::::decode(&mut &data[..]); + match maybe_versioned { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + }, + Ok(versioned) => match Xcm::try_from(versioned) { + Err(()) => Self::deposit_event(Event::UnsupportedVersion(id)), + Ok(x) => { + let outcome = T::XcmExecutor::prepare_and_execute( + Parent, + x.clone(), + &mut id, + limit, + Weight::zero(), + ); + ReceivedDmp::::append(x); + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + }, + }, + } + } + limit + } + } +} diff --git a/substrate/frame/revive/mock-network/src/mocks/relay_message_queue.rs b/substrate/frame/revive/mock-network/src/mocks/relay_message_queue.rs new file mode 100644 index 000000000000..14099965e3f1 --- /dev/null +++ b/substrate/frame/revive/mock-network/src/mocks/relay_message_queue.rs @@ -0,0 +1,52 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use frame_support::{parameter_types, weights::Weight}; +use xcm::latest::prelude::*; +use xcm_simulator::{ + AggregateMessageOrigin, ProcessMessage, ProcessMessageError, UmpQueueId, WeightMeter, +}; + +use crate::relay_chain::{RuntimeCall, XcmConfig}; + +parameter_types! { + /// Amount of weight that can be spent per block to service messages. + pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); + pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueMaxStale: u32 = 16; +} + +/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet. +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + id: &mut [u8; 32], + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + xcm_builder::ProcessXcmMessage::< + Junction, + xcm_executor::XcmExecutor, + RuntimeCall, + >::process_message(message, Junction::Parachain(para.into()), meter, id) + } +} diff --git a/substrate/frame/revive/mock-network/src/parachain.rs b/substrate/frame/revive/mock-network/src/parachain.rs new file mode 100644 index 000000000000..3def48cca96b --- /dev/null +++ b/substrate/frame/revive/mock-network/src/parachain.rs @@ -0,0 +1,346 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Parachain runtime mock. + +mod contracts_config; +use crate::{ + mocks::msg_queue::pallet as mock_msg_queue, + primitives::{AccountId, AssetIdForAssets, Balance}, +}; +use core::marker::PhantomData; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{AsEnsureOriginWithArg, Contains, ContainsPair, Everything, EverythingBut, Nothing}, + weights::{ + constants::{WEIGHT_PROOF_SIZE_PER_MB, WEIGHT_REF_TIME_PER_SECOND}, + Weight, + }, +}; +use frame_system::{EnsureRoot, EnsureSigned}; +use pallet_xcm::XcmPassthrough; +use sp_core::{ConstU32, ConstU64, H256}; +use sp_runtime::traits::{Get, IdentityLookup, MaybeEquivalence}; + +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, + ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, + FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, IsConcrete, NativeAsset, + NoChecking, ParentAsSuperuser, ParentIsPreset, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, WithComputedOrigin, +}; +use xcm_executor::{traits::JustTry, Config, XcmExecutor}; + +pub type SovereignAccountOf = + (AccountId32Aliases, ParentIsPreset); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type AccountStore = System; + type Balance = Balance; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeEvent = RuntimeEvent; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type WeightInfo = (); +} + +parameter_types! { + pub const AssetDeposit: u128 = 1_000_000; + pub const MetadataDepositBase: u128 = 1_000_000; + pub const MetadataDepositPerByte: u128 = 100_000; + pub const AssetAccountDeposit: u128 = 1_000_000; + pub const ApprovalDeposit: u128 = 1_000_000; + pub const AssetsStringLimit: u32 = 50; + pub const RemoveItemsLimit: u32 = 50; +} + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetIdForAssets; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type AssetAccountDeposit = AssetAccountDeposit; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = (); + type RemoveItemsLimit = RemoveItemsLimit; + type AssetIdParameter = AssetIdForAssets; + type CallbackHandle = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); + pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); +} + +parameter_types! { + pub const KsmLocation: Location = Location::parent(); + pub const TokenLocation: Location = Here.into_location(); + pub const RelayNetwork: NetworkId = ByGenesis([0; 32]); + pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); +} + +pub type XcmOriginToCallOrigin = ( + SovereignSignedViaLocation, + ParentAsSuperuser, + SignedAccountId32AsNative, + XcmPassthrough, +); + +parameter_types! { + pub const XcmInstructionWeight: Weight = Weight::from_parts(1_000, 1_000); + pub TokensPerSecondPerMegabyte: (AssetId, u128, u128) = (AssetId(Parent.into()), 1_000_000_000_000, 1024 * 1024); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; + pub ForeignPrefix: Location = (Parent,).into(); + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub TrustedLockPairs: (Location, AssetFilter) = + (Parent.into(), Wild(AllOf { id: AssetId(Parent.into()), fun: WildFungible })); +} + +pub fn estimate_message_fee(number_of_instructions: u64) -> u128 { + let weight = estimate_weight(number_of_instructions); + + estimate_fee_for_weight(weight) +} + +pub fn estimate_weight(number_of_instructions: u64) -> Weight { + XcmInstructionWeight::get().saturating_mul(number_of_instructions) +} + +pub fn estimate_fee_for_weight(weight: Weight) -> u128 { + let (_, units_per_second, units_per_mb) = TokensPerSecondPerMegabyte::get(); + + units_per_second * (weight.ref_time() as u128) / (WEIGHT_REF_TIME_PER_SECOND as u128) + + units_per_mb * (weight.proof_size() as u128) / (WEIGHT_PROOF_SIZE_PER_MB as u128) +} + +pub type LocalBalancesTransactor = + FungibleAdapter, SovereignAccountOf, AccountId, ()>; + +pub struct FromLocationToAsset(PhantomData<(Location, AssetId)>); +impl MaybeEquivalence + for FromLocationToAsset +{ + fn convert(value: &Location) -> Option { + match value.unpack() { + (1, []) => Some(0 as AssetIdForAssets), + (1, [Parachain(para_id)]) => Some(*para_id as AssetIdForAssets), + _ => None, + } + } + + fn convert_back(_id: &AssetIdForAssets) -> Option { + None + } +} + +pub type ForeignAssetsTransactor = FungiblesAdapter< + Assets, + ConvertedConcreteId< + AssetIdForAssets, + Balance, + FromLocationToAsset, + JustTry, + >, + SovereignAccountOf, + AccountId, + NoChecking, + CheckingAccount, +>; + +/// Means for transacting assets on this chain +pub type AssetTransactors = (LocalBalancesTransactor, ForeignAssetsTransactor); + +pub struct ParentRelay; +impl Contains for ParentRelay { + fn contains(location: &Location) -> bool { + location.contains_parents_only(1) + } +} +pub struct ThisParachain; +impl Contains for ThisParachain { + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (0, [Junction::AccountId32 { .. }])) + } +} + +pub type XcmRouter = crate::ParachainXcmRouter; + +pub type Barrier = ( + xcm_builder::AllowUnpaidExecutionFrom, + WithComputedOrigin< + (AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom), + UniversalLocation, + ConstU32<1>, + >, +); + +parameter_types! { + pub NftCollectionOne: AssetFilter + = Wild(AllOf { fun: WildNonFungible, id: AssetId((Parent, GeneralIndex(1)).into()) }); + pub NftCollectionOneForRelay: (AssetFilter, Location) + = (NftCollectionOne::get(), Parent.into()); + pub RelayNativeAsset: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId((Parent, Here).into()) }); + pub RelayNativeAssetForRelay: (AssetFilter, Location) = (RelayNativeAsset::get(), Parent.into()); +} +pub type TrustedTeleporters = + (xcm_builder::Case, xcm_builder::Case); +pub type TrustedReserves = EverythingBut>; + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToCallOrigin; + type IsReserve = (NativeAsset, TrustedReserves); + type IsTeleporter = TrustedTeleporters; + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetLocker = PolkadotXcm; + type AssetExchanger = (); + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = AllPalletsWithSystem; + type FeeManager = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; +} + +impl mock_msg_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +pub struct TrustedLockerCase(PhantomData); +impl> ContainsPair for TrustedLockerCase { + fn contains(origin: &Location, asset: &Asset) -> bool { + let (o, a) = T::get(); + a.matches(asset) && &o == origin + } +} + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type TrustedLockers = TrustedLockerCase; + type SovereignAccountOf = SovereignAccountOf; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type WeightInfo = pallet_xcm::TestWeightInfo; + type AdminOrigin = EnsureRoot; +} + +type Block = frame_system::mocking::MockBlock; + +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<1>; + type WeightInfo = (); +} + +construct_runtime!( + pub enum Runtime + { + System: frame_system, + Balances: pallet_balances, + Timestamp: pallet_timestamp, + MsgQueue: mock_msg_queue, + PolkadotXcm: pallet_xcm, + Contracts: pallet_revive, + Assets: pallet_assets, + } +); diff --git a/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs b/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs new file mode 100644 index 000000000000..49f53ae5bc3d --- /dev/null +++ b/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs @@ -0,0 +1,27 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use super::{Balances, Runtime, RuntimeCall, RuntimeEvent}; +use crate::parachain::RuntimeHoldReason; +use frame_support::derive_impl; + +#[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)] +impl pallet_revive::Config for Runtime { + type AddressGenerator = pallet_revive::DefaultAddressGenerator; + type Currency = Balances; + type Time = super::Timestamp; + type Xcm = pallet_xcm::Pallet; +} diff --git a/substrate/frame/revive/mock-network/src/primitives.rs b/substrate/frame/revive/mock-network/src/primitives.rs new file mode 100644 index 000000000000..efc42772f88a --- /dev/null +++ b/substrate/frame/revive/mock-network/src/primitives.rs @@ -0,0 +1,23 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +pub type Balance = u128; + +pub const UNITS: Balance = 10_000_000_000; +pub const CENTS: Balance = UNITS / 100; // 100_000_000 + +pub type AccountId = sp_runtime::AccountId32; +pub type AssetIdForAssets = u128; diff --git a/substrate/frame/revive/mock-network/src/relay_chain.rs b/substrate/frame/revive/mock-network/src/relay_chain.rs new file mode 100644 index 000000000000..8829fff3d043 --- /dev/null +++ b/substrate/frame/revive/mock-network/src/relay_chain.rs @@ -0,0 +1,239 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Relay chain runtime mock. + +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{Contains, Everything, Nothing}, + weights::Weight, +}; + +use frame_system::EnsureRoot; +use sp_core::{ConstU32, H256}; +use sp_runtime::traits::IdentityLookup; + +use polkadot_parachain_primitives::primitives::Id as ParaId; +use polkadot_runtime_parachains::{configuration, origin, shared}; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, + ChildSystemParachainAsSuperuser, DescribeAllTerminal, DescribeFamily, FixedRateOfFungible, + FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, WithComputedOrigin, +}; +use xcm_executor::{Config, XcmExecutor}; + +use super::{ + mocks::relay_message_queue::*, + primitives::{AccountId, Balance}, +}; + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Block = Block; + type Nonce = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; +} + +impl shared::Config for Runtime { + type DisabledValidators = (); +} + +impl configuration::Config for Runtime { + type WeightInfo = configuration::TestWeightInfo; +} + +parameter_types! { + pub RelayNetwork: NetworkId = ByGenesis([0; 32]); + pub const TokenLocation: Location = Here.into_location(); + pub UniversalLocation: InteriorLocation = RelayNetwork::get().into(); + pub UnitWeightCost: u64 = 1_000; +} + +pub type SovereignAccountOf = ( + HashedDescription>, + AccountId32Aliases, + ChildParachainConvertsVia, +); + +pub type LocalBalancesTransactor = + FungibleAdapter, SovereignAccountOf, AccountId, ()>; + +pub type AssetTransactors = LocalBalancesTransactor; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +parameter_types! { + pub const XcmInstructionWeight: Weight = Weight::from_parts(1_000, 1_000); + pub TokensPerSecondPerMegabyte: (AssetId, u128, u128) = + (AssetId(TokenLocation::get()), 1_000_000_000_000, 1024 * 1024); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; +} + +pub struct ChildrenParachains; +impl Contains for ChildrenParachains { + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (0, [Parachain(_)])) + } +} + +pub type XcmRouter = crate::RelayChainXcmRouter; +pub type Barrier = WithComputedOrigin< + ( + AllowExplicitUnpaidExecutionFrom, + AllowTopLevelPaidExecutionFrom, + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<1>, +>; + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = XcmPallet; + type AssetTrap = XcmPallet; + type AssetLocker = XcmPallet; + type AssetExchanger = (); + type AssetClaims = XcmPallet; + type SubscriptionService = XcmPallet; + type PalletInstancesInfo = AllPalletsWithSystem; + type FeeManager = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type TrustedLockers = (); + type SovereignAccountOf = SovereignAccountOf; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type WeightInfo = pallet_xcm::TestWeightInfo; + type AdminOrigin = EnsureRoot; +} + +impl origin::Config for Runtime {} + +type Block = frame_system::mocking::MockBlock; + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = (); + type MessageProcessor = MessageProcessor; + type QueueChangeHandler = (); + type WeightInfo = (); + type QueuePausedQuery = (); +} + +construct_runtime!( + pub enum Runtime { + System: frame_system, + Balances: pallet_balances, + ParasOrigin: origin, + XcmPallet: pallet_xcm, + MessageQueue: pallet_message_queue, + } +); diff --git a/substrate/frame/revive/mock-network/src/tests.rs b/substrate/frame/revive/mock-network/src/tests.rs new file mode 100644 index 000000000000..9259dd6f169e --- /dev/null +++ b/substrate/frame/revive/mock-network/src/tests.rs @@ -0,0 +1,207 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + parachain::{self, Runtime}, + parachain_account_sovereign_account_id, + primitives::{AccountId, CENTS}, + relay_chain, MockNet, ParaA, ParachainBalances, Relay, ALICE, BOB, INITIAL_BALANCE, +}; +use codec::{Decode, Encode}; +use frame_support::traits::{fungibles::Mutate, Currency}; +use frame_system::RawOrigin; +use pallet_revive::{ + test_utils::{self, builder::*}, + Code, +}; +use pallet_revive_fixtures::compile_module; +use pallet_revive_uapi::ReturnErrorCode; +use xcm::{v4::prelude::*, VersionedLocation, VersionedXcm}; +use xcm_simulator::TestExt; + +macro_rules! assert_return_code { + ( $x:expr , $y:expr $(,)? ) => {{ + assert_eq!(u32::from_le_bytes($x.data[..].try_into().unwrap()), $y as u32); + }}; +} + +fn bare_call(dest: sp_runtime::AccountId32) -> BareCallBuilder { + BareCallBuilder::::bare_call(RawOrigin::Signed(ALICE).into(), dest) +} + +/// Instantiate the tests contract, and fund it with some balance and assets. +fn instantiate_test_contract(name: &str) -> AccountId { + let (wasm, _) = compile_module::(name).unwrap(); + + // Instantiate contract. + let contract_addr = ParaA::execute_with(|| { + BareInstantiateBuilder::::bare_instantiate( + RawOrigin::Signed(ALICE).into(), + Code::Upload(wasm), + ) + .build_and_unwrap_account_id() + }); + + // Funds contract account with some balance and assets. + ParaA::execute_with(|| { + parachain::Balances::make_free_balance_be(&contract_addr, INITIAL_BALANCE); + parachain::Assets::mint_into(0u32.into(), &contract_addr, INITIAL_BALANCE).unwrap(); + }); + Relay::execute_with(|| { + let sovereign_account = parachain_account_sovereign_account_id(1u32, contract_addr.clone()); + relay_chain::Balances::make_free_balance_be(&sovereign_account, INITIAL_BALANCE); + }); + + contract_addr +} + +#[test] +fn test_xcm_execute() { + MockNet::reset(); + + let contract_addr = instantiate_test_contract("xcm_execute"); + + // Execute XCM instructions through the contract. + ParaA::execute_with(|| { + let amount: u128 = 10 * CENTS; + let assets: Asset = (Here, amount).into(); + let beneficiary = AccountId32 { network: None, id: BOB.clone().into() }; + + // The XCM used to transfer funds to Bob. + let message: Xcm<()> = Xcm::builder_unsafe() + .withdraw_asset(assets.clone()) + .deposit_asset(assets, beneficiary) + .build(); + + let result = bare_call(contract_addr.clone()) + .data(VersionedXcm::V4(message).encode()) + .build(); + + assert_eq!(result.gas_consumed, result.gas_required); + assert_return_code!(&result.result.unwrap(), ReturnErrorCode::Success); + + // Check if the funds are subtracted from the account of Alice and added to the account of + // Bob. + let initial = INITIAL_BALANCE; + assert_eq!(ParachainBalances::free_balance(BOB), initial + amount); + assert_eq!(ParachainBalances::free_balance(&contract_addr), initial - amount); + }); +} + +#[test] +fn test_xcm_execute_incomplete() { + MockNet::reset(); + + let contract_addr = instantiate_test_contract("xcm_execute"); + let amount = 10 * CENTS; + + // Execute XCM instructions through the contract. + ParaA::execute_with(|| { + let assets: Asset = (Here, amount).into(); + let beneficiary = AccountId32 { network: None, id: BOB.clone().into() }; + + // The XCM used to transfer funds to Bob. + let message: Xcm<()> = Xcm::builder_unsafe() + .withdraw_asset(assets.clone()) + // This will fail as the contract does not have enough balance to complete both + // withdrawals. + .withdraw_asset((Here, INITIAL_BALANCE)) + .buy_execution(assets.clone(), Unlimited) + .deposit_asset(assets, beneficiary) + .build(); + + let result = bare_call(contract_addr.clone()) + .data(VersionedXcm::V4(message).encode()) + .build(); + + assert_eq!(result.gas_consumed, result.gas_required); + assert_return_code!(&result.result.unwrap(), ReturnErrorCode::XcmExecutionFailed); + + assert_eq!(ParachainBalances::free_balance(BOB), INITIAL_BALANCE); + assert_eq!(ParachainBalances::free_balance(&contract_addr), INITIAL_BALANCE - amount); + }); +} + +#[test] +fn test_xcm_execute_reentrant_call() { + MockNet::reset(); + + let contract_addr = instantiate_test_contract("xcm_execute"); + + ParaA::execute_with(|| { + let transact_call = parachain::RuntimeCall::Contracts(pallet_revive::Call::call { + dest: contract_addr.clone(), + gas_limit: 1_000_000.into(), + storage_deposit_limit: test_utils::deposit_limit::(), + data: vec![], + value: 0u128, + }); + + // The XCM used to transfer funds to Bob. + let message: Xcm = Xcm::builder_unsafe() + .transact(OriginKind::Native, 1_000_000_000, transact_call.encode()) + .expect_transact_status(MaybeErrorCode::Success) + .build(); + + let result = bare_call(contract_addr.clone()) + .data(VersionedXcm::V4(message).encode()) + .build_and_unwrap_result(); + + assert_return_code!(&result, ReturnErrorCode::XcmExecutionFailed); + + // Funds should not change hands as the XCM transact failed. + assert_eq!(ParachainBalances::free_balance(BOB), INITIAL_BALANCE); + }); +} + +#[test] +fn test_xcm_send() { + MockNet::reset(); + let contract_addr = instantiate_test_contract("xcm_send"); + let amount = 1_000 * CENTS; + let fee = parachain::estimate_message_fee(4); // Accounts for the `DescendOrigin` instruction added by `send_xcm` + + // Send XCM instructions through the contract, to transfer some funds from the contract + // derivative account to Alice on the relay chain. + ParaA::execute_with(|| { + let dest = VersionedLocation::V4(Parent.into()); + let assets: Asset = (Here, amount).into(); + let beneficiary = AccountId32 { network: None, id: ALICE.clone().into() }; + + let message: Xcm<()> = Xcm::builder() + .withdraw_asset(assets.clone()) + .buy_execution((Here, fee), Unlimited) + .deposit_asset(assets, beneficiary) + .build(); + + let result = bare_call(contract_addr.clone()) + .data((dest, VersionedXcm::V4(message)).encode()) + .build_and_unwrap_result(); + + let mut data = &result.data[..]; + XcmHash::decode(&mut data).expect("Failed to decode xcm_send message_id"); + }); + + Relay::execute_with(|| { + let derived_contract_addr = ¶chain_account_sovereign_account_id(1, contract_addr); + assert_eq!( + INITIAL_BALANCE - amount, + relay_chain::Balances::free_balance(derived_contract_addr) + ); + assert_eq!(INITIAL_BALANCE + amount - fee, relay_chain::Balances::free_balance(ALICE)); + }); +} diff --git a/substrate/frame/revive/proc-macro/Cargo.toml b/substrate/frame/revive/proc-macro/Cargo.toml new file mode 100644 index 000000000000..7b47d6053504 --- /dev/null +++ b/substrate/frame/revive/proc-macro/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "pallet-revive-proc-macro" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true +description = "Procedural macros used in pallet_revive" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = { workspace = true } +quote = { workspace = true } +syn = { features = ["full"], workspace = true } diff --git a/substrate/frame/revive/proc-macro/src/lib.rs b/substrate/frame/revive/proc-macro/src/lib.rs new file mode 100644 index 000000000000..95f4110a2d76 --- /dev/null +++ b/substrate/frame/revive/proc-macro/src/lib.rs @@ -0,0 +1,616 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Procedural macros used in the contracts module. +//! +//! Most likely you should use the [`#[define_env]`][`macro@define_env`] attribute macro which hides +//! boilerplate of defining external environment for a wasm module. + +use proc_macro::TokenStream; +use proc_macro2::{Literal, Span, TokenStream as TokenStream2}; +use quote::{quote, ToTokens}; +use syn::{parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, FnArg, Ident}; + +/// Defines a host functions set that can be imported by contract wasm code. +/// +/// **NB**: Be advised that all functions defined by this macro +/// will panic if called with unexpected arguments. +/// +/// It's up to you as the user of this macro to check signatures of wasm code to be executed +/// and reject the code if any imported function has a mismatched signature. +/// +/// ## Example +/// +/// ```nocompile +/// #[define_env] +/// pub mod some_env { +/// fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<(), TrapReason> { +/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) +/// } +/// } +/// ``` +/// This example will expand to the `foo()` defined in the wasm module named `seal0`. This is +/// because the module `seal0` is the default when no module is specified. +/// +/// To define a host function in `seal2` and `seal3` modules, it should be annotated with the +/// appropriate attribute as follows: +/// +/// ## Example +/// +/// ```nocompile +/// #[define_env] +/// pub mod some_env { +/// #[version(2)] +/// fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { +/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) +/// } +/// +/// #[version(3)] +/// #[unstable] +/// fn bar(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { +/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) +/// } +/// } +/// ``` +/// The function `bar` is additionally annotated with `unstable` which removes it from the stable +/// interface. Check out the README to learn about unstable functions. +/// +/// +/// In this example, the following host functions will be generated by the macro: +/// - `foo()` in module `seal1`, +/// - `seal_foo()` in module `seal1`, +/// - `bar()` in module `seal42`. +/// +/// Only following return types are allowed for the host functions defined with the macro: +/// - `Result<(), TrapReason>`, +/// - `Result`, +/// - `Result`. +/// +/// The macro expands to `pub struct Env` declaration, with the following traits implementations: +/// - `pallet_revive::wasm::Environment> where E: Ext` +/// - `pallet_revive::wasm::Environment<()>` +/// +/// The implementation on `()` can be used in places where no `Ext` exists, yet. This is useful +/// when only checking whether a code can be instantiated without actually executing any code. +/// +/// +/// To build up these docs, run: +/// +/// ```nocompile +/// cargo doc +/// ``` +#[proc_macro_attribute] +pub fn define_env(attr: TokenStream, item: TokenStream) -> TokenStream { + if !attr.is_empty() { + let msg = r#"Invalid `define_env` attribute macro: expected no attributes: + - `#[define_env]`"#; + let span = TokenStream2::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into() + } + + let item = syn::parse_macro_input!(item as syn::ItemMod); + + match EnvDef::try_from(item) { + Ok(mut def) => expand_env(&mut def).into(), + Err(e) => e.to_compile_error().into(), + } +} + +/// Parsed environment definition. +struct EnvDef { + host_funcs: Vec, +} + +/// Parsed host function definition. +struct HostFn { + item: syn::ItemFn, + api_version: Option, + name: String, + returns: HostFnReturn, + cfg: Option, +} + +enum HostFnReturn { + Unit, + U32, + ReturnCode, +} + +impl HostFnReturn { + fn map_output(&self) -> TokenStream2 { + match self { + Self::Unit => quote! { |_| None }, + Self::U32 => quote! { |ret_val| Some(ret_val) }, + Self::ReturnCode => quote! { |ret_code| Some(ret_code.into()) }, + } + } + + fn success_type(&self) -> syn::ReturnType { + match self { + Self::Unit => syn::ReturnType::Default, + Self::U32 => parse_quote! { -> u32 }, + Self::ReturnCode => parse_quote! { -> ReturnErrorCode }, + } + } +} + +impl EnvDef { + pub fn try_from(item: syn::ItemMod) -> syn::Result { + let span = item.span(); + let err = |msg| syn::Error::new(span, msg); + let items = &item + .content + .as_ref() + .ok_or(err("Invalid environment definition, expected `mod` to be inlined."))? + .1; + + let extract_fn = |i: &syn::Item| match i { + syn::Item::Fn(i_fn) => Some(i_fn.clone()), + _ => None, + }; + + let host_funcs = items + .iter() + .filter_map(extract_fn) + .map(HostFn::try_from) + .collect::, _>>()?; + + Ok(Self { host_funcs }) + } +} + +impl HostFn { + pub fn try_from(mut item: syn::ItemFn) -> syn::Result { + let err = |span, msg| { + let msg = format!("Invalid host function definition.\n{}", msg); + syn::Error::new(span, msg) + }; + + // process attributes + let msg = "Only #[api_version()], #[cfg] and #[mutating] attributes are allowed."; + let span = item.span(); + let mut attrs = item.attrs.clone(); + attrs.retain(|a| !a.path().is_ident("doc")); + let mut api_version = None; + let mut mutating = false; + let mut cfg = None; + while let Some(attr) = attrs.pop() { + let ident = attr.path().get_ident().ok_or(err(span, msg))?.to_string(); + match ident.as_str() { + "api_version" => { + if api_version.is_some() { + return Err(err(span, "#[api_version] can only be specified once")) + } + api_version = + Some(attr.parse_args::().and_then(|lit| lit.base10_parse())?); + }, + "mutating" => { + if mutating { + return Err(err(span, "#[mutating] can only be specified once")) + } + mutating = true; + }, + "cfg" => { + if cfg.is_some() { + return Err(err(span, "#[cfg] can only be specified once")) + } + cfg = Some(attr); + }, + id => return Err(err(span, &format!("Unsupported attribute \"{id}\". {msg}"))), + } + } + + if mutating { + let stmt = syn::parse_quote! { + if self.ext().is_read_only() { + return Err(Error::::StateChangeDenied.into()); + } + }; + item.block.stmts.insert(0, stmt); + } + + let name = item.sig.ident.to_string(); + + let msg = "Every function must start with these two parameters: &mut self, memory: &mut M"; + let special_args = item + .sig + .inputs + .iter() + .take(2) + .enumerate() + .map(|(i, arg)| is_valid_special_arg(i, arg)) + .fold(0u32, |acc, valid| if valid { acc + 1 } else { acc }); + + if special_args != 2 { + return Err(err(span, msg)) + } + + // process return type + let msg = r#"Should return one of the following: + - Result<(), TrapReason>, + - Result, + - Result"#; + let ret_ty = match item.clone().sig.output { + syn::ReturnType::Type(_, ty) => Ok(ty.clone()), + _ => Err(err(span, &msg)), + }?; + match *ret_ty { + syn::Type::Path(tp) => { + let result = &tp.path.segments.last().ok_or(err(span, &msg))?; + let (id, span) = (result.ident.to_string(), result.ident.span()); + id.eq(&"Result".to_string()).then_some(()).ok_or(err(span, &msg))?; + + match &result.arguments { + syn::PathArguments::AngleBracketed(group) => { + if group.args.len() != 2 { + return Err(err(span, &msg)) + }; + + let arg2 = group.args.last().ok_or(err(span, &msg))?; + + let err_ty = match arg2 { + syn::GenericArgument::Type(ty) => Ok(ty.clone()), + _ => Err(err(arg2.span(), &msg)), + }?; + + match err_ty { + syn::Type::Path(tp) => Ok(tp + .path + .segments + .first() + .ok_or(err(arg2.span(), &msg))? + .ident + .to_string()), + _ => Err(err(tp.span(), &msg)), + }? + .eq("TrapReason") + .then_some(()) + .ok_or(err(span, &msg))?; + + let arg1 = group.args.first().ok_or(err(span, &msg))?; + let ok_ty = match arg1 { + syn::GenericArgument::Type(ty) => Ok(ty.clone()), + _ => Err(err(arg1.span(), &msg)), + }?; + let ok_ty_str = match ok_ty { + syn::Type::Path(tp) => Ok(tp + .path + .segments + .first() + .ok_or(err(arg1.span(), &msg))? + .ident + .to_string()), + syn::Type::Tuple(tt) => { + if !tt.elems.is_empty() { + return Err(err(arg1.span(), &msg)) + }; + Ok("()".to_string()) + }, + _ => Err(err(ok_ty.span(), &msg)), + }?; + let returns = match ok_ty_str.as_str() { + "()" => Ok(HostFnReturn::Unit), + "u32" => Ok(HostFnReturn::U32), + "ReturnErrorCode" => Ok(HostFnReturn::ReturnCode), + _ => Err(err(arg1.span(), &msg)), + }?; + + Ok(Self { item, api_version, name, returns, cfg }) + }, + _ => Err(err(span, &msg)), + } + }, + _ => Err(err(span, &msg)), + } + } +} + +fn is_valid_special_arg(idx: usize, arg: &FnArg) -> bool { + match (idx, arg) { + (0, FnArg::Receiver(rec)) => rec.reference.is_some() && rec.mutability.is_some(), + (1, FnArg::Typed(pat)) => { + let ident = + if let syn::Pat::Ident(ref ident) = *pat.pat { &ident.ident } else { return false }; + if !(ident == "memory" || ident == "_memory") { + return false + } + matches!(*pat.ty, syn::Type::Reference(_)) + }, + _ => false, + } +} + +fn arg_decoder<'a, P, I>(param_names: P, param_types: I) -> TokenStream2 +where + P: Iterator> + Clone, + I: Iterator> + Clone, +{ + const ALLOWED_REGISTERS: u32 = 6; + let mut registers_used = 0; + let mut bindings = vec![]; + for (idx, (name, ty)) in param_names.clone().zip(param_types.clone()).enumerate() { + let syn::Type::Path(path) = &**ty else { + panic!("Type needs to be path"); + }; + let Some(ident) = path.path.get_ident() else { + panic!("Type needs to be ident"); + }; + let size = + if ident == "i8" || + ident == "i16" || ident == "i32" || + ident == "u8" || ident == "u16" || + ident == "u32" + { + 1 + } else if ident == "i64" || ident == "u64" { + 2 + } else { + panic!("Pass by value only supports primitives"); + }; + registers_used += size; + if registers_used > ALLOWED_REGISTERS { + return quote! { + let (#( #param_names, )*): (#( #param_types, )*) = memory.read_as(__a0__)?; + } + } + let this_reg = quote::format_ident!("__a{}__", idx); + let next_reg = quote::format_ident!("__a{}__", idx + 1); + let binding = if size == 1 { + quote! { + let #name = #this_reg as #ty; + } + } else { + quote! { + let #name = (#this_reg as #ty) | ((#next_reg as #ty) << 32); + } + }; + bindings.push(binding); + } + quote! { + #( #bindings )* + } +} + +/// Expands environment definition. +/// Should generate source code for: +/// - implementations of the host functions to be added to the wasm runtime environment (see +/// `expand_impls()`). +fn expand_env(def: &EnvDef) -> TokenStream2 { + let impls = expand_functions(def); + let bench_impls = expand_bench_functions(def); + let docs = expand_func_doc(def); + let highest_api_version = + def.host_funcs.iter().filter_map(|f| f.api_version).max().unwrap_or_default(); + + quote! { + #[cfg(test)] + pub const HIGHEST_API_VERSION: u16 = #highest_api_version; + + impl<'a, E: Ext, M: PolkaVmInstance> Runtime<'a, E, M> { + fn handle_ecall( + &mut self, + memory: &mut M, + __syscall_symbol__: &[u8], + __available_api_version__: ApiVersion, + ) -> Result, TrapReason> + { + #impls + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { + #bench_impls + } + + /// Documentation of the syscalls (host functions) available to contracts. + /// + /// Each of the functions in this trait represent a function that is callable + /// by the contract. Guests use the function name as the import symbol. + /// + /// # Note + /// + /// This module is not meant to be used by any code. Rather, it is meant to be + /// consumed by humans through rustdoc. + #[cfg(doc)] + pub trait SyscallDoc { + #docs + } + } +} + +fn expand_functions(def: &EnvDef) -> TokenStream2 { + let impls = def.host_funcs.iter().map(|f| { + // skip the self and memory argument + let params = f.item.sig.inputs.iter().skip(2); + let param_names = params.clone().filter_map(|arg| { + let FnArg::Typed(arg) = arg else { + return None; + }; + Some(&arg.pat) + }); + let param_types = params.clone().filter_map(|arg| { + let FnArg::Typed(arg) = arg else { + return None; + }; + Some(&arg.ty) + }); + let arg_decoder = arg_decoder(param_names, param_types); + let cfg = &f.cfg; + let name = &f.name; + let syscall_symbol = Literal::byte_string(name.as_bytes()); + let body = &f.item.block; + let map_output = f.returns.map_output(); + let output = &f.item.sig.output; + let api_version = match f.api_version { + Some(version) => quote! { Some(#version) }, + None => quote! { None }, + }; + + // wrapped host function body call with host function traces + // see https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/contracts#host-function-tracing + let wrapped_body_with_trace = { + let trace_fmt_args = params.clone().filter_map(|arg| match arg { + syn::FnArg::Receiver(_) => None, + syn::FnArg::Typed(p) => match *p.pat.clone() { + syn::Pat::Ident(ref pat_ident) => Some(pat_ident.ident.clone()), + _ => None, + }, + }); + + let params_fmt_str = trace_fmt_args + .clone() + .map(|s| format!("{s}: {{:?}}")) + .collect::>() + .join(", "); + let trace_fmt_str = format!("{}({}) = {{:?}}\n", name, params_fmt_str); + + quote! { + // wrap body in closure to make sure the tracing is always executed + let result = (|| #body)(); + if ::log::log_enabled!(target: "runtime::revive::strace", ::log::Level::Trace) { + use core::fmt::Write; + let mut w = sp_std::Writer::default(); + let _ = core::write!(&mut w, #trace_fmt_str, #( #trace_fmt_args, )* result); + let msg = core::str::from_utf8(&w.inner()).unwrap_or_default(); + self.ext().append_debug_buffer(msg); + } + result + } + }; + + quote! { + #cfg + #syscall_symbol if __is_available__(#api_version) => { + // closure is needed so that "?" can infere the correct type + (|| #output { + #arg_decoder + #wrapped_body_with_trace + })().map(#map_output) + }, + } + }); + + quote! { + // Write gas from polkavm into pallet-revive before entering the host function. + let __gas_left_before__ = self + .ext + .gas_meter_mut() + .sync_from_executor(memory.gas()) + .map_err(TrapReason::from)?; + + // This is the overhead to call an empty syscall that always needs to be charged. + self.charge_gas(crate::wasm::RuntimeCosts::HostFn).map_err(TrapReason::from)?; + + // Not all APIs are available depending on configuration or when the code was deployed. + // This closure will be used by syscall specific code to perform this check. + let __is_available__ = |syscall_version: Option| { + match __available_api_version__ { + ApiVersion::UnsafeNewest => true, + ApiVersion::Versioned(max_available_version) => + syscall_version + .map(|required_version| max_available_version >= required_version) + .unwrap_or(false), + } + }; + + // They will be mapped to variable names by the syscall specific code. + let (__a0__, __a1__, __a2__, __a3__, __a4__, __a5__) = memory.read_input_regs(); + + // Execute the syscall specific logic in a closure so that the gas metering code is always executed. + let result = (|| match __syscall_symbol__ { + #( #impls )* + _ => Err(TrapReason::SupervisorError(Error::::InvalidSyscall.into())) + })(); + + // Write gas from pallet-revive into polkavm after leaving the host function. + let gas = self.ext.gas_meter_mut().sync_to_executor(__gas_left_before__).map_err(TrapReason::from)?; + memory.set_gas(gas.into()); + result + } +} + +fn expand_bench_functions(def: &EnvDef) -> TokenStream2 { + let impls = def.host_funcs.iter().map(|f| { + // skip the context and memory argument + let params = f.item.sig.inputs.iter().skip(2); + let cfg = &f.cfg; + let name = &f.name; + let body = &f.item.block; + let output = &f.item.sig.output; + + let name = Ident::new(&format!("bench_{name}"), Span::call_site()); + quote! { + #cfg + pub fn #name(&mut self, memory: &mut M, #(#params),*) #output { + #body + } + } + }); + + quote! { + #( #impls )* + } +} + +fn expand_func_doc(def: &EnvDef) -> TokenStream2 { + let docs = def.host_funcs.iter().map(|func| { + // Remove auxiliary args: `ctx: _` and `memory: _` + let func_decl = { + let mut sig = func.item.sig.clone(); + sig.inputs = sig + .inputs + .iter() + .skip(2) + .map(|p| p.clone()) + .collect::>(); + sig.output = func.returns.success_type(); + sig.to_token_stream() + }; + let func_doc = { + let func_docs = { + let docs = func.item.attrs.iter().filter(|a| a.path().is_ident("doc")).map(|d| { + let docs = d.to_token_stream(); + quote! { #docs } + }); + quote! { #( #docs )* } + }; + let availability = if let Some(version) = func.api_version { + let info = format!( + "\n# Required API version\nThis API was added in version **{version}**.", + ); + quote! { #[doc = #info] } + } else { + let info = + "\n# Unstable API\nThis API is not standardized and only available for testing."; + quote! { #[doc = #info] } + }; + quote! { + #func_docs + #availability + } + }; + quote! { + #func_doc + #func_decl; + } + }); + + quote! { + #( #docs )* + } +} diff --git a/substrate/frame/revive/src/address.rs b/substrate/frame/revive/src/address.rs new file mode 100644 index 000000000000..5758daf7b1ff --- /dev/null +++ b/substrate/frame/revive/src/address.rs @@ -0,0 +1,68 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Functions that deal with address derivation. + +use crate::{CodeHash, Config}; +use codec::{Decode, Encode}; +use sp_runtime::traits::{Hash, TrailingZeroInput}; + +/// Provides the contract address generation method. +/// +/// See [`DefaultAddressGenerator`] for the default implementation. +/// +/// # Note for implementors +/// +/// 1. Make sure that there are no collisions, different inputs never lead to the same output. +/// 2. Make sure that the same inputs lead to the same output. +pub trait AddressGenerator { + /// The address of a contract based on the given instantiate parameters. + /// + /// Changing the formular for an already deployed chain is fine as long as no collisions + /// with the old formular. Changes only affect existing contracts. + fn contract_address( + deploying_address: &T::AccountId, + code_hash: &CodeHash, + input_data: &[u8], + salt: &[u8], + ) -> T::AccountId; +} + +/// Default address generator. +/// +/// This is the default address generator used by contract instantiation. Its result +/// is only dependent on its inputs. It can therefore be used to reliably predict the +/// address of a contract. This is akin to the formula of eth's CREATE2 opcode. There +/// is no CREATE equivalent because CREATE2 is strictly more powerful. +/// Formula: +/// `hash("contract_addr_v1" ++ deploying_address ++ code_hash ++ input_data ++ salt)` +pub struct DefaultAddressGenerator; + +impl AddressGenerator for DefaultAddressGenerator { + /// Formula: `hash("contract_addr_v1" ++ deploying_address ++ code_hash ++ input_data ++ salt)` + fn contract_address( + deploying_address: &T::AccountId, + code_hash: &CodeHash, + input_data: &[u8], + salt: &[u8], + ) -> T::AccountId { + let entropy = (b"contract_addr_v1", deploying_address, code_hash, input_data, salt) + .using_encoded(T::Hashing::hash); + Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) + .expect("infinite length input; no invalid inputs for type; qed") + } +} diff --git a/substrate/frame/revive/src/benchmarking/call_builder.rs b/substrate/frame/revive/src/benchmarking/call_builder.rs new file mode 100644 index 000000000000..654ba3de4f72 --- /dev/null +++ b/substrate/frame/revive/src/benchmarking/call_builder.rs @@ -0,0 +1,217 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + benchmarking::{default_deposit_limit, Contract, WasmModule}, + exec::{ExportedFunction, Ext, Key, Stack}, + storage::meter::Meter, + transient_storage::MeterEntry, + wasm::{ApiVersion, PreparedCall, Runtime}, + BalanceOf, Config, DebugBuffer, Error, GasMeter, Origin, TypeInfo, WasmBlob, Weight, +}; +use alloc::{vec, vec::Vec}; +use codec::{Encode, HasCompact}; +use core::fmt::Debug; +use frame_benchmarking::benchmarking; + +type StackExt<'a, T> = Stack<'a, T, WasmBlob>; + +/// A builder used to prepare a contract call. +pub struct CallSetup { + contract: Contract, + dest: T::AccountId, + origin: Origin, + gas_meter: GasMeter, + storage_meter: Meter, + value: BalanceOf, + debug_message: Option, + data: Vec, + transient_storage_size: u32, +} + +impl Default for CallSetup +where + T: Config + pallet_balances::Config, + as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, +{ + fn default() -> Self { + Self::new(WasmModule::dummy()) + } +} + +impl CallSetup +where + T: Config + pallet_balances::Config, + as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, +{ + /// Setup a new call for the given module. + pub fn new(module: WasmModule) -> Self { + let contract = Contract::::new(module.clone(), vec![]).unwrap(); + let dest = contract.account_id.clone(); + let origin = Origin::from_account_id(contract.caller.clone()); + + let storage_meter = Meter::new(&origin, default_deposit_limit::(), 0u32.into()).unwrap(); + + // Whitelist contract account, as it is already accounted for in the call benchmark + benchmarking::add_to_whitelist( + frame_system::Account::::hashed_key_for(&contract.account_id).into(), + ); + + // Whitelist the contract's contractInfo as it is already accounted for in the call + // benchmark + benchmarking::add_to_whitelist( + crate::ContractInfoOf::::hashed_key_for(&contract.account_id).into(), + ); + + Self { + contract, + dest, + origin, + gas_meter: GasMeter::new(Weight::MAX), + storage_meter, + value: 0u32.into(), + debug_message: None, + data: vec![], + transient_storage_size: 0, + } + } + + /// Set the meter's storage deposit limit. + pub fn set_storage_deposit_limit(&mut self, balance: BalanceOf) { + self.storage_meter = Meter::new(&self.origin, balance, 0u32.into()).unwrap(); + } + + /// Set the call's origin. + pub fn set_origin(&mut self, origin: Origin) { + self.origin = origin; + } + + /// Set the contract's balance. + pub fn set_balance(&mut self, value: BalanceOf) { + self.contract.set_balance(value); + } + + /// Set the call's input data. + pub fn set_data(&mut self, value: Vec) { + self.data = value; + } + + /// Set the transient storage size. + pub fn set_transient_storage_size(&mut self, size: u32) { + self.transient_storage_size = size; + } + + /// Set the debug message. + pub fn enable_debug_message(&mut self) { + self.debug_message = Some(Default::default()); + } + + /// Get the debug message. + pub fn debug_message(&self) -> Option { + self.debug_message.clone() + } + + /// Get the call's input data. + pub fn data(&self) -> Vec { + self.data.clone() + } + + /// Get the call's contract. + pub fn contract(&self) -> Contract { + self.contract.clone() + } + + /// Build the call stack. + pub fn ext(&mut self) -> (StackExt<'_, T>, WasmBlob) { + let mut ext = StackExt::bench_new_call( + self.dest.clone(), + self.origin.clone(), + &mut self.gas_meter, + &mut self.storage_meter, + self.value, + self.debug_message.as_mut(), + ); + if self.transient_storage_size > 0 { + Self::with_transient_storage(&mut ext.0, self.transient_storage_size).unwrap(); + } + ext + } + + /// Prepare a call to the module. + pub fn prepare_call<'a>( + ext: &'a mut StackExt<'a, T>, + module: WasmBlob, + input: Vec, + ) -> PreparedCall<'a, StackExt<'a, T>> { + module + .prepare_call( + Runtime::new(ext, input), + ExportedFunction::Call, + ApiVersion::UnsafeNewest, + ) + .unwrap() + } + + /// Add transient_storage + fn with_transient_storage(ext: &mut StackExt, size: u32) -> Result<(), &'static str> { + let &MeterEntry { amount, limit } = ext.transient_storage().meter().current(); + ext.transient_storage().meter().current_mut().limit = size; + for i in 1u32.. { + let mut key_data = i.to_le_bytes().to_vec(); + while key_data.last() == Some(&0) { + key_data.pop(); + } + let key = Key::try_from_var(key_data).unwrap(); + if let Err(e) = ext.set_transient_storage(&key, Some(Vec::new()), false) { + // Restore previous settings. + ext.transient_storage().meter().current_mut().limit = limit; + ext.transient_storage().meter().current_mut().amount = amount; + if e == Error::::OutOfTransientStorage.into() { + break; + } else { + return Err("Initialization of the transient storage failed"); + } + } + } + Ok(()) + } +} + +#[macro_export] +macro_rules! memory( + ($($bytes:expr,)*) => {{ + vec![].iter()$(.chain($bytes.iter()))*.cloned().collect::>() + }}; +); + +#[macro_export] +macro_rules! build_runtime( + ($runtime:ident, $memory:ident: [$($segment:expr,)*]) => { + $crate::build_runtime!($runtime, _contract, $memory: [$($segment,)*]); + }; + ($runtime:ident, $contract:ident, $memory:ident: [$($bytes:expr,)*]) => { + $crate::build_runtime!($runtime, $contract); + let mut $memory = $crate::memory!($($bytes,)*); + }; + ($runtime:ident, $contract:ident) => { + let mut setup = CallSetup::::default(); + let $contract = setup.contract(); + let input = setup.data(); + let (mut ext, _) = setup.ext(); + let mut $runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, input); + }; +); diff --git a/substrate/frame/revive/src/benchmarking/code.rs b/substrate/frame/revive/src/benchmarking/code.rs new file mode 100644 index 000000000000..eba4710d8a2c --- /dev/null +++ b/substrate/frame/revive/src/benchmarking/code.rs @@ -0,0 +1,69 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Functions to procedurally construct contract code used for benchmarking. +//! +//! In order to be able to benchmark events that are triggered by contract execution +//! (API calls into seal, individual instructions), we need to generate contracts that +//! perform those events. Because those contracts can get very big we cannot simply define +//! them as text (.wat) as this will be too slow and consume too much memory. Therefore +//! we define this simple definition of a contract that can be passed to `create_code` that +//! compiles it down into a `WasmModule` that can be used as a contract's code. + +use crate::Config; +use alloc::vec::Vec; +use pallet_revive_fixtures::bench as bench_fixtures; +use sp_runtime::traits::Hash; + +/// A wasm module ready to be put on chain. +#[derive(Clone)] +pub struct WasmModule { + pub code: Vec, + pub hash: ::Output, +} + +impl WasmModule { + /// Return a contract code that does nothing. + pub fn dummy() -> Self { + Self::new(bench_fixtures::DUMMY.to_vec()) + } + + /// Same as [`Self::dummy`] but uses `replace_with` to make the code unique. + pub fn dummy_unique(replace_with: u32) -> Self { + Self::new(bench_fixtures::dummy_unique(replace_with)) + } + + /// A contract code of specified sizte that does nothing. + pub fn sized(_size: u32) -> Self { + Self::dummy() + } + + /// A contract code that calls the "noop" host function in a loop depending in the input. + pub fn noop() -> Self { + Self::new(bench_fixtures::NOOP.to_vec()) + } + + /// A contract code that executes some ALU instructions in a loop. + pub fn instr() -> Self { + Self::new(bench_fixtures::INSTR.to_vec()) + } + + fn new(code: Vec) -> Self { + let hash = T::Hashing::hash(&code); + Self { code, hash } + } +} diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs new file mode 100644 index 000000000000..b4bd028d6f03 --- /dev/null +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -0,0 +1,1815 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Benchmarks for the contracts pallet + +#![cfg(all(feature = "runtime-benchmarks", feature = "riscv"))] + +mod call_builder; +mod code; +use self::{call_builder::CallSetup, code::WasmModule}; +use crate::{ + exec::{Key, MomentOf}, + limits, + migration::codegen::LATEST_MIGRATION_VERSION, + storage::WriteOutcome, + Pallet as Contracts, *, +}; +use alloc::{vec, vec::Vec}; +use codec::{Encode, MaxEncodedLen}; +use frame_benchmarking::v2::*; +use frame_support::{ + self, assert_ok, + pallet_prelude::StorageVersion, + storage::child, + traits::{fungible::InspectHold, Currency}, + weights::{Weight, WeightMeter}, +}; +use frame_system::RawOrigin; +use pallet_balances; +use pallet_revive_uapi::{CallFlags, ReturnErrorCode, StorageFlags}; +use sp_runtime::traits::{Bounded, Hash}; + +/// How many runs we do per API benchmark. +/// +/// This is picked more or less arbitrary. We experimented with different numbers until +/// the results appeared to be stable. Reducing the number would speed up the benchmarks +/// but might make the results less precise. +const API_BENCHMARK_RUNS: u32 = 1600; + +/// How many runs we do per instruction benchmark. +/// +/// Same rationale as for [`API_BENCHMARK_RUNS`]. The number is bigger because instruction +/// benchmarks are faster. +const INSTR_BENCHMARK_RUNS: u32 = 5000; + +/// Number of layers in a Radix16 unbalanced trie. +const UNBALANCED_TRIE_LAYERS: u32 = 20; + +/// An instantiated and deployed contract. +#[derive(Clone)] +struct Contract { + caller: T::AccountId, + account_id: T::AccountId, + addr: AccountIdLookupOf, +} + +impl Contract +where + T: Config + pallet_balances::Config, + as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, +{ + /// Create new contract and use a default account id as instantiator. + fn new(module: WasmModule, data: Vec) -> Result, &'static str> { + Self::with_index(0, module, data) + } + + /// Create new contract and use an account id derived from the supplied index as instantiator. + fn with_index( + index: u32, + module: WasmModule, + data: Vec, + ) -> Result, &'static str> { + Self::with_caller(account("instantiator", index, 0), module, data) + } + + /// Create new contract and use the supplied `caller` as instantiator. + fn with_caller( + caller: T::AccountId, + module: WasmModule, + data: Vec, + ) -> Result, &'static str> { + T::Currency::set_balance(&caller, caller_funding::()); + let salt = vec![0xff]; + + let outcome = Contracts::::bare_instantiate( + RawOrigin::Signed(caller.clone()).into(), + 0u32.into(), + Weight::MAX, + default_deposit_limit::(), + Code::Upload(module.code), + data, + salt, + DebugInfo::Skip, + CollectEvents::Skip, + ); + + let addr = outcome.result?.account_id; + let result = Contract { caller, account_id: addr.clone(), addr: T::Lookup::unlookup(addr) }; + + ContractInfoOf::::insert(&result.account_id, result.info()?); + + Ok(result) + } + + /// Create a new contract with the supplied storage item count and size each. + fn with_storage( + code: WasmModule, + stor_num: u32, + stor_size: u32, + ) -> Result { + let contract = Contract::::new(code, vec![])?; + let storage_items = (0..stor_num) + .map(|i| { + let hash = T::Hashing::hash_of(&i) + .as_ref() + .try_into() + .map_err(|_| "Hash too big for storage key")?; + Ok((hash, vec![42u8; stor_size as usize])) + }) + .collect::, &'static str>>()?; + contract.store(&storage_items)?; + Ok(contract) + } + + /// Store the supplied storage items into this contracts storage. + fn store(&self, items: &Vec<([u8; 32], Vec)>) -> Result<(), &'static str> { + let info = self.info()?; + for item in items { + info.write(&Key::Fix(item.0), Some(item.1.clone()), None, false) + .map_err(|_| "Failed to write storage to restoration dest")?; + } + >::insert(&self.account_id, info); + Ok(()) + } + + /// Create a new contract with the specified unbalanced storage trie. + fn with_unbalanced_storage_trie(code: WasmModule, key: &[u8]) -> Result { + if (key.len() as u32) < (UNBALANCED_TRIE_LAYERS + 1) / 2 { + return Err("Key size too small to create the specified trie"); + } + + let value = vec![16u8; limits::PAYLOAD_BYTES as usize]; + let contract = Contract::::new(code, vec![])?; + let info = contract.info()?; + let child_trie_info = info.child_trie_info(); + child::put_raw(&child_trie_info, &key, &value); + for l in 0..UNBALANCED_TRIE_LAYERS { + let pos = l as usize / 2; + let mut key_new = key.to_vec(); + for i in 0u8..16 { + key_new[pos] = if l % 2 == 0 { + (key_new[pos] & 0xF0) | i + } else { + (key_new[pos] & 0x0F) | (i << 4) + }; + + if key == &key_new { + continue + } + child::put_raw(&child_trie_info, &key_new, &value); + } + } + Ok(contract) + } + + /// Get the `ContractInfo` of the `addr` or an error if it no longer exists. + fn address_info(addr: &T::AccountId) -> Result, &'static str> { + ContractInfoOf::::get(addr).ok_or("Expected contract to exist at this point.") + } + + /// Get the `ContractInfo` of this contract or an error if it no longer exists. + fn info(&self) -> Result, &'static str> { + Self::address_info(&self.account_id) + } + + /// Set the balance of the contract to the supplied amount. + fn set_balance(&self, balance: BalanceOf) { + T::Currency::set_balance(&self.account_id, balance); + } + + /// Returns `true` iff all storage entries related to code storage exist. + fn code_exists(hash: &CodeHash) -> bool { + >::contains_key(hash) && >::contains_key(&hash) + } + + /// Returns `true` iff no storage entry related to code storage exist. + fn code_removed(hash: &CodeHash) -> bool { + !>::contains_key(hash) && !>::contains_key(&hash) + } +} + +/// The funding that each account that either calls or instantiates contracts is funded with. +fn caller_funding() -> BalanceOf { + // Minting can overflow, so we can't abuse of the funding. This value happens to be big enough, + // but not too big to make the total supply overflow. + BalanceOf::::max_value() / 10_000u32.into() +} + +/// The deposit limit we use for benchmarks. +fn default_deposit_limit() -> BalanceOf { + (T::DepositPerByte::get() * 1024u32.into() * 1024u32.into()) + + T::DepositPerItem::get() * 1024u32.into() +} + +#[benchmarks( + where + as codec::HasCompact>::Type: Clone + Eq + PartialEq + core::fmt::Debug + scale_info::TypeInfo + codec::Encode, + T: Config + pallet_balances::Config, + BalanceOf: From< as Currency>::Balance>, + as Currency>::Balance: From>, +)] +mod benchmarks { + use super::*; + + // The base weight consumed on processing contracts deletion queue. + #[benchmark(pov_mode = Measured)] + fn on_process_deletion_queue_batch() { + #[block] + { + ContractInfo::::process_deletion_queue_batch(&mut WeightMeter::new()) + } + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn on_initialize_per_trie_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> { + let instance = Contract::::with_storage(WasmModule::dummy(), k, limits::PAYLOAD_BYTES)?; + instance.info()?.queue_trie_for_deletion(); + + #[block] + { + ContractInfo::::process_deletion_queue_batch(&mut WeightMeter::new()) + } + + Ok(()) + } + + // This benchmarks the weight of executing Migration::migrate to execute a noop migration. + #[benchmark(pov_mode = Measured)] + fn migration_noop() { + let version = LATEST_MIGRATION_VERSION; + StorageVersion::new(version).put::>(); + #[block] + { + Migration::::migrate(&mut WeightMeter::new()); + } + assert_eq!(StorageVersion::get::>(), version); + } + + // This benchmarks the weight of dispatching migrate to execute 1 `NoopMigration` + #[benchmark(pov_mode = Measured)] + fn migrate() { + let latest_version = LATEST_MIGRATION_VERSION; + StorageVersion::new(latest_version - 2).put::>(); + as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); + + #[extrinsic_call] + _(RawOrigin::Signed(whitelisted_caller()), Weight::MAX); + + assert_eq!(StorageVersion::get::>(), latest_version - 1); + } + + // This benchmarks the weight of running on_runtime_upgrade when there are no migration in + // progress. + #[benchmark(pov_mode = Measured)] + fn on_runtime_upgrade_noop() { + let latest_version = LATEST_MIGRATION_VERSION; + StorageVersion::new(latest_version).put::>(); + #[block] + { + as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); + } + assert!(MigrationInProgress::::get().is_none()); + } + + // This benchmarks the weight of running on_runtime_upgrade when there is a migration in + // progress. + #[benchmark(pov_mode = Measured)] + fn on_runtime_upgrade_in_progress() { + let latest_version = LATEST_MIGRATION_VERSION; + StorageVersion::new(latest_version - 2).put::>(); + let v = vec![42u8].try_into().ok(); + MigrationInProgress::::set(v.clone()); + #[block] + { + as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); + } + assert!(MigrationInProgress::::get().is_some()); + assert_eq!(MigrationInProgress::::get(), v); + } + + // This benchmarks the weight of running on_runtime_upgrade when there is a migration to + // process. + #[benchmark(pov_mode = Measured)] + fn on_runtime_upgrade() { + let latest_version = LATEST_MIGRATION_VERSION; + StorageVersion::new(latest_version - 2).put::>(); + #[block] + { + as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); + } + assert!(MigrationInProgress::::get().is_some()); + } + + // This benchmarks the overhead of loading a code of size `c` byte from storage and into + // the execution engine. This does **not** include the actual execution for which the gas meter + // is responsible. This is achieved by generating all code to the `deploy` function + // which is in the wasm module but not executed on `call`. + // The results are supposed to be used as `call_with_code_per_byte(c) - + // call_with_code_per_byte(0)`. + #[benchmark(pov_mode = Measured)] + fn call_with_code_per_byte( + c: Linear<0, { T::MaxCodeLen::get() }>, + ) -> Result<(), BenchmarkError> { + let instance = + Contract::::with_caller(whitelisted_caller(), WasmModule::sized(c), vec![])?; + let value = Pallet::::min_balance(); + let callee = instance.addr; + let storage_deposit = default_deposit_limit::(); + + #[extrinsic_call] + call( + RawOrigin::Signed(instance.caller.clone()), + callee, + value, + Weight::MAX, + storage_deposit, + vec![], + ); + + Ok(()) + } + + // `c`: Size of the code in bytes. + // `i`: Size of the input in bytes. + // `s`: Size of the salt in bytes. + #[benchmark(pov_mode = Measured)] + fn instantiate_with_code( + c: Linear<0, { T::MaxCodeLen::get() }>, + i: Linear<0, { limits::MEMORY_BYTES }>, + s: Linear<0, { limits::MEMORY_BYTES }>, + ) { + let input = vec![42u8; i as usize]; + let salt = vec![42u8; s as usize]; + let value = Pallet::::min_balance(); + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let WasmModule { code, hash, .. } = WasmModule::::sized(c); + let origin = RawOrigin::Signed(caller.clone()); + let addr = Contracts::::contract_address(&caller, &hash, &input, &salt); + let storage_deposit = default_deposit_limit::(); + #[extrinsic_call] + _(origin, value, Weight::MAX, storage_deposit, code, input, salt); + + let deposit = + T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr); + // uploading the code reserves some balance in the callers account + let code_deposit = + T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &caller); + assert_eq!( + T::Currency::balance(&caller), + caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), + ); + // contract has the full value + assert_eq!(T::Currency::balance(&addr), value + Pallet::::min_balance()); + } + + // `i`: Size of the input in bytes. + // `s`: Size of the salt in bytes. + #[benchmark(pov_mode = Measured)] + fn instantiate( + i: Linear<0, { limits::MEMORY_BYTES }>, + s: Linear<0, { limits::MEMORY_BYTES }>, + ) -> Result<(), BenchmarkError> { + let input = vec![42u8; i as usize]; + let salt = vec![42u8; s as usize]; + let value = Pallet::::min_balance(); + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let origin = RawOrigin::Signed(caller.clone()); + let WasmModule { code, .. } = WasmModule::::dummy(); + let storage_deposit = default_deposit_limit::(); + let hash = + Contracts::::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash; + let addr = Contracts::::contract_address(&caller, &hash, &input, &salt); + + #[extrinsic_call] + _( + RawOrigin::Signed(caller.clone()), + value, + Weight::MAX, + storage_deposit, + hash, + input, + salt, + ); + + let deposit = + T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr); + let code_deposit = + T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &caller); + // value was removed from the caller + assert_eq!( + T::Currency::balance(&caller), + caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), + ); + // contract has the full value + assert_eq!(T::Currency::balance(&addr), value + Pallet::::min_balance()); + + Ok(()) + } + + // We just call a dummy contract to measure the overhead of the call extrinsic. + // The size of the data has no influence on the costs of this extrinsic as long as the contract + // won't call `seal_input` in its constructor to copy the data to contract memory. + // The dummy contract used here does not do this. The costs for the data copy is billed as + // part of `seal_input`. The costs for invoking a contract of a specific size are not part + // of this benchmark because we cannot know the size of the contract when issuing a call + // transaction. See `call_with_code_per_byte` for this. + #[benchmark(pov_mode = Measured)] + fn call() -> Result<(), BenchmarkError> { + let data = vec![42u8; 1024]; + let instance = + Contract::::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; + let value = Pallet::::min_balance(); + let origin = RawOrigin::Signed(instance.caller.clone()); + let callee = instance.addr.clone(); + let before = T::Currency::balance(&instance.account_id); + let storage_deposit = default_deposit_limit::(); + #[extrinsic_call] + _(origin, callee, value, Weight::MAX, storage_deposit, data); + let deposit = T::Currency::balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &instance.account_id, + ); + let code_deposit = T::Currency::balance_on_hold( + &HoldReason::CodeUploadDepositReserve.into(), + &instance.caller, + ); + // value and value transferred via call should be removed from the caller + assert_eq!( + T::Currency::balance(&instance.caller), + caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), + ); + // contract should have received the value + assert_eq!(T::Currency::balance(&instance.account_id), before + value); + // contract should still exist + instance.info()?; + + Ok(()) + } + + // This constructs a contract that is maximal expensive to instrument. + // It creates a maximum number of metering blocks per byte. + // `c`: Size of the code in bytes. + #[benchmark(pov_mode = Measured)] + fn upload_code(c: Linear<0, { T::MaxCodeLen::get() }>) { + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let WasmModule { code, hash, .. } = WasmModule::::sized(c); + let origin = RawOrigin::Signed(caller.clone()); + let storage_deposit = default_deposit_limit::(); + #[extrinsic_call] + _(origin, code, storage_deposit); + // uploading the code reserves some balance in the callers account + assert!(T::Currency::total_balance_on_hold(&caller) > 0u32.into()); + assert!(>::code_exists(&hash)); + } + + // Removing code does not depend on the size of the contract because all the information + // needed to verify the removal claim (refcount, owner) is stored in a separate storage + // item (`CodeInfoOf`). + #[benchmark(pov_mode = Measured)] + fn remove_code() -> Result<(), BenchmarkError> { + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let WasmModule { code, hash, .. } = WasmModule::::dummy(); + let origin = RawOrigin::Signed(caller.clone()); + let storage_deposit = default_deposit_limit::(); + let uploaded = + >::bare_upload_code(origin.clone().into(), code, storage_deposit)?; + assert_eq!(uploaded.code_hash, hash); + assert_eq!(uploaded.deposit, T::Currency::total_balance_on_hold(&caller)); + assert!(>::code_exists(&hash)); + #[extrinsic_call] + _(origin, hash); + // removing the code should have unreserved the deposit + assert_eq!(T::Currency::total_balance_on_hold(&caller), 0u32.into()); + assert!(>::code_removed(&hash)); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn set_code() -> Result<(), BenchmarkError> { + let instance = + >::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; + // we just add some bytes so that the code hash is different + let WasmModule { code, .. } = >::dummy_unique(128); + let origin = RawOrigin::Signed(instance.caller.clone()); + let storage_deposit = default_deposit_limit::(); + let hash = + >::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash; + let callee = instance.addr.clone(); + assert_ne!(instance.info()?.code_hash, hash); + #[extrinsic_call] + _(RawOrigin::Root, callee, hash); + assert_eq!(instance.info()?.code_hash, hash); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn noop_host_fn(r: Linear<0, API_BENCHMARK_RUNS>) { + let mut setup = CallSetup::::new(WasmModule::noop()); + let (mut ext, module) = setup.ext(); + let prepared = CallSetup::::prepare_call(&mut ext, module, r.encode()); + #[block] + { + prepared.call().unwrap(); + } + } + + #[benchmark(pov_mode = Measured)] + fn seal_caller() { + let len = ::max_encoded_len() as u32; + build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + + let result; + #[block] + { + result = runtime.bench_caller(memory.as_mut_slice(), 4, 0); + } + + assert_ok!(result); + assert_eq!( + &::decode(&mut &memory[4..]).unwrap(), + runtime.ext().caller().account_id().unwrap() + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_is_contract() { + let Contract { account_id, .. } = + Contract::::with_index(1, WasmModule::dummy(), vec![]).unwrap(); + + build_runtime!(runtime, memory: [account_id.encode(), ]); + + let result; + #[block] + { + result = runtime.bench_is_contract(memory.as_mut_slice(), 0); + } + + assert_eq!(result.unwrap(), 1); + } + + #[benchmark(pov_mode = Measured)] + fn seal_code_hash() { + let contract = Contract::::with_index(1, WasmModule::dummy(), vec![]).unwrap(); + let len = as MaxEncodedLen>::max_encoded_len() as u32; + build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], contract.account_id.encode(), ]); + + let result; + #[block] + { + result = runtime.bench_code_hash(memory.as_mut_slice(), 4 + len, 4, 0); + } + + assert_ok!(result); + assert_eq!( + as Decode>::decode(&mut &memory[4..]).unwrap(), + contract.info().unwrap().code_hash + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_own_code_hash() { + let len = as MaxEncodedLen>::max_encoded_len() as u32; + build_runtime!(runtime, contract, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + let result; + #[block] + { + result = runtime.bench_own_code_hash(memory.as_mut_slice(), 4, 0); + } + + assert_ok!(result); + assert_eq!( + as Decode>::decode(&mut &memory[4..]).unwrap(), + contract.info().unwrap().code_hash + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_caller_is_origin() { + build_runtime!(runtime, memory: []); + + let result; + #[block] + { + result = runtime.bench_caller_is_origin(memory.as_mut_slice()); + } + assert_eq!(result.unwrap(), 1u32); + } + + #[benchmark(pov_mode = Measured)] + fn seal_caller_is_root() { + let mut setup = CallSetup::::default(); + setup.set_origin(Origin::Root); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]); + + let result; + #[block] + { + result = runtime.bench_caller_is_root([0u8; 0].as_mut_slice()); + } + assert_eq!(result.unwrap(), 1u32); + } + + #[benchmark(pov_mode = Measured)] + fn seal_address() { + let len = as MaxEncodedLen>::max_encoded_len() as u32; + build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + + let result; + #[block] + { + result = runtime.bench_address(memory.as_mut_slice(), 4, 0); + } + assert_ok!(result); + assert_eq!( + &::decode(&mut &memory[4..]).unwrap(), + runtime.ext().address() + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_weight_left() { + // use correct max_encoded_len when new version of parity-scale-codec is released + let len = 18u32; + assert!(::max_encoded_len() as u32 != len); + build_runtime!(runtime, memory: [32u32.to_le_bytes(), vec![0u8; len as _], ]); + + let result; + #[block] + { + result = runtime.bench_weight_left(memory.as_mut_slice(), 4, 0); + } + assert_ok!(result); + assert_eq!( + ::decode(&mut &memory[4..]).unwrap(), + runtime.ext().gas_meter().gas_left() + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_balance() { + let len = ::max_encoded_len() as u32; + build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + let result; + #[block] + { + result = runtime.bench_balance(memory.as_mut_slice(), 4, 0); + } + assert_ok!(result); + assert_eq!( + ::decode(&mut &memory[4..]).unwrap(), + runtime.ext().balance().into() + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_value_transferred() { + let len = ::max_encoded_len() as u32; + build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + let result; + #[block] + { + result = runtime.bench_value_transferred(memory.as_mut_slice(), 4, 0); + } + assert_ok!(result); + assert_eq!( + ::decode(&mut &memory[4..]).unwrap(), + runtime.ext().value_transferred().into() + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_minimum_balance() { + let len = ::max_encoded_len() as u32; + build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + let result; + #[block] + { + result = runtime.bench_minimum_balance(memory.as_mut_slice(), 4, 0); + } + assert_ok!(result); + assert_eq!( + ::decode(&mut &memory[4..]).unwrap(), + runtime.ext().minimum_balance().into() + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_block_number() { + let len = as MaxEncodedLen>::max_encoded_len() as u32; + build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + let result; + #[block] + { + result = runtime.bench_block_number(memory.as_mut_slice(), 4, 0); + } + assert_ok!(result); + assert_eq!( + >::decode(&mut &memory[4..]).unwrap(), + runtime.ext().block_number() + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_now() { + let len = as MaxEncodedLen>::max_encoded_len() as u32; + build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + let result; + #[block] + { + result = runtime.bench_now(memory.as_mut_slice(), 4, 0); + } + assert_ok!(result); + assert_eq!(>::decode(&mut &memory[4..]).unwrap(), *runtime.ext().now()); + } + + #[benchmark(pov_mode = Measured)] + fn seal_weight_to_fee() { + let len = ::max_encoded_len() as u32; + build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + let weight = Weight::from_parts(500_000, 300_000); + let result; + #[block] + { + result = runtime.bench_weight_to_fee( + memory.as_mut_slice(), + weight.ref_time(), + weight.proof_size(), + 4, + 0, + ); + } + assert_ok!(result); + assert_eq!( + >::decode(&mut &memory[4..]).unwrap(), + runtime.ext().get_weight_price(weight) + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_input(n: Linear<0, { limits::MEMORY_BYTES - 4 }>) { + let mut setup = CallSetup::::default(); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![42u8; n as usize]); + let mut memory = memory!(n.to_le_bytes(), vec![0u8; n as usize],); + let result; + #[block] + { + result = runtime.bench_input(memory.as_mut_slice(), 4, 0); + } + assert_ok!(result); + assert_eq!(&memory[4..], &vec![42u8; n as usize]); + } + + #[benchmark(pov_mode = Measured)] + fn seal_return(n: Linear<0, { limits::MEMORY_BYTES - 4 }>) { + build_runtime!(runtime, memory: [n.to_le_bytes(), vec![42u8; n as usize], ]); + + let result; + #[block] + { + result = runtime.bench_seal_return(memory.as_mut_slice(), 0, 0, n); + } + + assert!(matches!( + result, + Err(crate::wasm::TrapReason::Return(crate::wasm::ReturnData { .. })) + )); + } + + #[benchmark(pov_mode = Measured)] + fn seal_terminate( + n: Linear<0, { limits::DELEGATE_DEPENDENCIES }>, + ) -> Result<(), BenchmarkError> { + let beneficiary = account::("beneficiary", 0, 0); + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let origin = RawOrigin::Signed(caller); + let storage_deposit = default_deposit_limit::(); + + build_runtime!(runtime, memory: [beneficiary.encode(),]); + + (0..n).for_each(|i| { + let new_code = WasmModule::::dummy_unique(65 + i); + Contracts::::bare_upload_code(origin.clone().into(), new_code.code, storage_deposit) + .unwrap(); + runtime.ext().lock_delegate_dependency(new_code.hash).unwrap(); + }); + + let result; + #[block] + { + result = runtime.bench_terminate(memory.as_mut_slice(), 0); + } + + assert!(matches!(result, Err(crate::wasm::TrapReason::Termination))); + + Ok(()) + } + + // Benchmark the overhead that topics generate. + // `t`: Number of topics + // `n`: Size of event payload in bytes + #[benchmark(pov_mode = Measured)] + fn seal_deposit_event( + t: Linear<0, { limits::NUM_EVENT_TOPICS as u32 }>, + n: Linear<0, { limits::PAYLOAD_BYTES }>, + ) { + let topics = (0..t).map(|i| T::Hashing::hash_of(&i)).collect::>().encode(); + let topics_len = topics.len() as u32; + + build_runtime!(runtime, memory: [ + n.to_le_bytes(), + topics, + vec![0u8; n as _], + ]); + + let result; + #[block] + { + result = runtime.bench_deposit_event( + memory.as_mut_slice(), + 4, // topics_ptr + topics_len, // topics_len + 4 + topics_len, // data_ptr + 0, // data_len + ); + } + + assert_ok!(result); + } + + // Benchmark debug_message call + // Whereas this function is used in RPC mode only, it still should be secured + // against an excessive use. + // + // i: size of input in bytes up to maximum allowed contract memory or maximum allowed debug + // buffer size, whichever is less. + #[benchmark] + fn seal_debug_message( + i: Linear<0, { (limits::MEMORY_BYTES).min(limits::DEBUG_BUFFER_BYTES) }>, + ) { + let mut setup = CallSetup::::default(); + setup.enable_debug_message(); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + // Fill memory with printable ASCII bytes. + let mut memory = (0..i).zip((32..127).cycle()).map(|i| i.1).collect::>(); + + let result; + #[block] + { + result = runtime.bench_debug_message(memory.as_mut_slice(), 0, i); + } + assert_ok!(result); + assert_eq!(setup.debug_message().unwrap().len() as u32, i); + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn get_storage_empty() -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = vec![0u8; max_key_len as usize]; + let max_value_len = limits::PAYLOAD_BYTES as usize; + let value = vec![1u8; max_value_len]; + + let instance = Contract::::new(WasmModule::dummy(), vec![])?; + let info = instance.info()?; + let child_trie_info = info.child_trie_info(); + info.bench_write_raw(&key, Some(value.clone()), false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let result; + #[block] + { + result = child::get_raw(&child_trie_info, &key); + } + + assert_eq!(result, Some(value)); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn get_storage_full() -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = vec![0u8; max_key_len as usize]; + let max_value_len = limits::PAYLOAD_BYTES; + let value = vec![1u8; max_value_len as usize]; + + let instance = Contract::::with_unbalanced_storage_trie(WasmModule::dummy(), &key)?; + let info = instance.info()?; + let child_trie_info = info.child_trie_info(); + info.bench_write_raw(&key, Some(value.clone()), false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let result; + #[block] + { + result = child::get_raw(&child_trie_info, &key); + } + + assert_eq!(result, Some(value)); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn set_storage_empty() -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = vec![0u8; max_key_len as usize]; + let max_value_len = limits::PAYLOAD_BYTES as usize; + let value = vec![1u8; max_value_len]; + + let instance = Contract::::new(WasmModule::dummy(), vec![])?; + let info = instance.info()?; + let child_trie_info = info.child_trie_info(); + info.bench_write_raw(&key, Some(vec![42u8; max_value_len]), false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let val = Some(value.clone()); + let result; + #[block] + { + result = info.bench_write_raw(&key, val, true); + } + + assert_ok!(result); + assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn set_storage_full() -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = vec![0u8; max_key_len as usize]; + let max_value_len = limits::PAYLOAD_BYTES; + let value = vec![1u8; max_value_len as usize]; + + let instance = Contract::::with_unbalanced_storage_trie(WasmModule::dummy(), &key)?; + let info = instance.info()?; + let child_trie_info = info.child_trie_info(); + info.bench_write_raw(&key, Some(vec![42u8; max_value_len as usize]), false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let val = Some(value.clone()); + let result; + #[block] + { + result = info.bench_write_raw(&key, val, true); + } + + assert_ok!(result); + assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value); + Ok(()) + } + + // n: new byte size + // o: old byte size + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_set_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + o: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + let value = vec![1u8; n as usize]; + + build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]); + let info = instance.info()?; + + info.write(&key, Some(vec![42u8; o as usize]), None, false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let result; + #[block] + { + result = runtime.bench_set_storage( + memory.as_mut_slice(), + StorageFlags::empty().bits(), + 0, // key_ptr + max_key_len, // key_len + max_key_len, // value_ptr + n, // value_len + ); + } + + assert_ok!(result); + assert_eq!(info.read(&key).unwrap(), value); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_clear_storage(n: Linear<0, { limits::PAYLOAD_BYTES }>) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, instance, memory: [ key.unhashed(), ]); + let info = instance.info()?; + + info.write(&key, Some(vec![42u8; n as usize]), None, false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let result; + #[block] + { + result = runtime.bench_clear_storage( + memory.as_mut_slice(), + StorageFlags::empty().bits(), + 0, + max_key_len, + ); + } + + assert_ok!(result); + assert!(info.read(&key).is_none()); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_get_storage(n: Linear<0, { limits::PAYLOAD_BYTES }>) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]); + let info = instance.info()?; + + info.write(&key, Some(vec![42u8; n as usize]), None, false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let out_ptr = max_key_len + 4; + let result; + #[block] + { + result = runtime.bench_get_storage( + memory.as_mut_slice(), + StorageFlags::empty().bits(), + 0, // key_ptr + max_key_len, // key_len + out_ptr, // out_ptr + max_key_len, // out_len_ptr + ); + } + + assert_ok!(result); + assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_contains_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, instance, memory: [ key.unhashed(), ]); + let info = instance.info()?; + + info.write(&key, Some(vec![42u8; n as usize]), None, false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let result; + #[block] + { + result = runtime.bench_contains_storage( + memory.as_mut_slice(), + StorageFlags::empty().bits(), + 0, + max_key_len, + ); + } + + assert_eq!(result.unwrap(), n); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_take_storage(n: Linear<0, { limits::PAYLOAD_BYTES }>) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]); + let info = instance.info()?; + + let value = vec![42u8; n as usize]; + info.write(&key, Some(value.clone()), None, false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let out_ptr = max_key_len + 4; + let result; + #[block] + { + result = runtime.bench_take_storage( + memory.as_mut_slice(), + StorageFlags::empty().bits(), + 0, // key_ptr + max_key_len, // key_len + out_ptr, // out_ptr + max_key_len, // out_len_ptr + ); + } + + assert_ok!(result); + assert!(&info.read(&key).is_none()); + assert_eq!(&value, &memory[out_ptr as usize..]); + Ok(()) + } + + // We use both full and empty benchmarks here instead of benchmarking transient_storage + // (BTreeMap) directly. This approach is necessary because benchmarking this BTreeMap is very + // slow. Additionally, we use linear regression for our benchmarks, and the BTreeMap's log(n) + // complexity can introduce approximation errors. + #[benchmark(pov_mode = Ignored)] + fn set_transient_storage_empty() -> Result<(), BenchmarkError> { + let max_value_len = limits::PAYLOAD_BYTES; + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + let value = Some(vec![42u8; max_value_len as _]); + let mut setup = CallSetup::::default(); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + let result; + #[block] + { + result = runtime.ext().set_transient_storage(&key, value, false); + } + + assert_eq!(result, Ok(WriteOutcome::New)); + assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _])); + Ok(()) + } + + #[benchmark(pov_mode = Ignored)] + fn set_transient_storage_full() -> Result<(), BenchmarkError> { + let max_value_len = limits::PAYLOAD_BYTES; + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + let value = Some(vec![42u8; max_value_len as _]); + let mut setup = CallSetup::::default(); + setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + let result; + #[block] + { + result = runtime.ext().set_transient_storage(&key, value, false); + } + + assert_eq!(result, Ok(WriteOutcome::New)); + assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _])); + Ok(()) + } + + #[benchmark(pov_mode = Ignored)] + fn get_transient_storage_empty() -> Result<(), BenchmarkError> { + let max_value_len = limits::PAYLOAD_BYTES; + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + + let mut setup = CallSetup::::default(); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + let result; + #[block] + { + result = runtime.ext().get_transient_storage(&key); + } + + assert_eq!(result, Some(vec![42u8; max_value_len as _])); + Ok(()) + } + + #[benchmark(pov_mode = Ignored)] + fn get_transient_storage_full() -> Result<(), BenchmarkError> { + let max_value_len = limits::PAYLOAD_BYTES; + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + + let mut setup = CallSetup::::default(); + setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + let result; + #[block] + { + result = runtime.ext().get_transient_storage(&key); + } + + assert_eq!(result, Some(vec![42u8; max_value_len as _])); + Ok(()) + } + + // The weight of journal rollbacks should be taken into account when setting storage. + #[benchmark(pov_mode = Ignored)] + fn rollback_transient_storage() -> Result<(), BenchmarkError> { + let max_value_len = limits::PAYLOAD_BYTES; + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + + let mut setup = CallSetup::::default(); + setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime.ext().transient_storage().start_transaction(); + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + #[block] + { + runtime.ext().transient_storage().rollback_transaction(); + } + + assert_eq!(runtime.ext().get_transient_storage(&key), None); + Ok(()) + } + + // n: new byte size + // o: old byte size + #[benchmark(pov_mode = Measured)] + fn seal_set_transient_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + o: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + let value = vec![1u8; n as usize]; + build_runtime!(runtime, memory: [ key.unhashed(), value.clone(), ]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; o as usize]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + + let result; + #[block] + { + result = runtime.bench_set_storage( + memory.as_mut_slice(), + StorageFlags::TRANSIENT.bits(), + 0, // key_ptr + max_key_len, // key_len + max_key_len, // value_ptr + n, // value_len + ); + } + + assert_ok!(result); + assert_eq!(runtime.ext().get_transient_storage(&key).unwrap(), value); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_clear_transient_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, memory: [ key.unhashed(), ]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; n as usize]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + + let result; + #[block] + { + result = runtime.bench_clear_storage( + memory.as_mut_slice(), + StorageFlags::TRANSIENT.bits(), + 0, + max_key_len, + ); + } + + assert_ok!(result); + assert!(runtime.ext().get_transient_storage(&key).is_none()); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_get_transient_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; n as usize]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + + let out_ptr = max_key_len + 4; + let result; + #[block] + { + result = runtime.bench_get_storage( + memory.as_mut_slice(), + StorageFlags::TRANSIENT.bits(), + 0, // key_ptr + max_key_len, // key_len + out_ptr, // out_ptr + max_key_len, // out_len_ptr + ); + } + + assert_ok!(result); + assert_eq!( + &runtime.ext().get_transient_storage(&key).unwrap(), + &memory[out_ptr as usize..] + ); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_contains_transient_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, memory: [ key.unhashed(), ]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; n as usize]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + + let result; + #[block] + { + result = runtime.bench_contains_storage( + memory.as_mut_slice(), + StorageFlags::TRANSIENT.bits(), + 0, + max_key_len, + ); + } + + assert_eq!(result.unwrap(), n); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_take_transient_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let n = limits::PAYLOAD_BYTES; + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + let value = vec![42u8; n as usize]; + runtime + .ext() + .set_transient_storage(&key, Some(value.clone()), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + + let out_ptr = max_key_len + 4; + let result; + #[block] + { + result = runtime.bench_take_storage( + memory.as_mut_slice(), + StorageFlags::TRANSIENT.bits(), + 0, // key_ptr + max_key_len, // key_len + out_ptr, // out_ptr + max_key_len, // out_len_ptr + ); + } + + assert_ok!(result); + assert!(&runtime.ext().get_transient_storage(&key).is_none()); + assert_eq!(&value, &memory[out_ptr as usize..]); + Ok(()) + } + + // We transfer to unique accounts. + #[benchmark(pov_mode = Measured)] + fn seal_transfer() { + let account = account::("receiver", 0, 0); + let value = Pallet::::min_balance(); + assert!(value > 0u32.into()); + + let mut setup = CallSetup::::default(); + setup.set_balance(value); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + + let account_bytes = account.encode(); + let account_len = account_bytes.len() as u32; + let value_bytes = value.encode(); + let mut memory = memory!(account_bytes, value_bytes,); + + let result; + #[block] + { + result = runtime.bench_transfer( + memory.as_mut_slice(), + 0, // account_ptr + account_len, // value_ptr + ); + } + + assert_ok!(result); + } + + // t: with or without some value to transfer + // i: size of the input data + #[benchmark(pov_mode = Measured)] + fn seal_call(t: Linear<0, 1>, i: Linear<0, { limits::MEMORY_BYTES }>) { + let Contract { account_id: callee, .. } = + Contract::::with_index(1, WasmModule::dummy(), vec![]).unwrap(); + let callee_bytes = callee.encode(); + let callee_len = callee_bytes.len() as u32; + + let value: BalanceOf = t.into(); + let value_bytes = value.encode(); + + let deposit: BalanceOf = (u32::MAX - 100).into(); + let deposit_bytes = deposit.encode(); + let deposit_len = deposit_bytes.len() as u32; + + let mut setup = CallSetup::::default(); + setup.set_storage_deposit_limit(deposit); + setup.set_data(vec![42; i as usize]); + setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone())); + + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes,); + + let result; + #[block] + { + result = runtime.bench_call( + memory.as_mut_slice(), + CallFlags::CLONE_INPUT.bits(), // flags + 0, // callee_ptr + 0, // ref_time_limit + 0, // proof_size_limit + callee_len, // deposit_ptr + callee_len + deposit_len, // value_ptr + 0, // input_data_ptr + 0, // input_data_len + SENTINEL, // output_ptr + 0, // output_len_ptr + ); + } + + assert_ok!(result); + } + + #[benchmark(pov_mode = Measured)] + fn seal_delegate_call() -> Result<(), BenchmarkError> { + let hash = Contract::::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash; + + let mut setup = CallSetup::::default(); + setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone())); + + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + let mut memory = memory!(hash.encode(),); + + let result; + #[block] + { + result = runtime.bench_delegate_call( + memory.as_mut_slice(), + 0, // flags + 0, // code_hash_ptr + 0, // input_data_ptr + 0, // input_data_len + SENTINEL, // output_ptr + 0, + ); + } + + assert_ok!(result); + Ok(()) + } + + // t: value to transfer + // i: size of input in bytes + // s: size of salt in bytes + #[benchmark(pov_mode = Measured)] + fn seal_instantiate( + i: Linear<0, { limits::MEMORY_BYTES }>, + s: Linear<0, { limits::MEMORY_BYTES }>, + ) -> Result<(), BenchmarkError> { + let hash = Contract::::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash; + let hash_bytes = hash.encode(); + let hash_len = hash_bytes.len() as u32; + + let value: BalanceOf = 1u32.into(); + let value_bytes = value.encode(); + let value_len = value_bytes.len() as u32; + + let deposit: BalanceOf = 0u32.into(); + let deposit_bytes = deposit.encode(); + let deposit_len = deposit_bytes.len() as u32; + + let mut setup = CallSetup::::default(); + setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone())); + setup.set_balance(value + (Pallet::::min_balance() * 2u32.into())); + + let account_id = &setup.contract().account_id.clone(); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + + let input = vec![42u8; i as _]; + let salt = vec![42u8; s as _]; + let addr = Contracts::::contract_address(&account_id, &hash, &input, &salt); + let mut memory = memory!(hash_bytes, deposit_bytes, value_bytes, input, salt,); + + let mut offset = { + let mut current = 0u32; + move |after: u32| { + current += after; + current + } + }; + + assert!(ContractInfoOf::::get(&addr).is_none()); + + let result; + #[block] + { + result = runtime.bench_instantiate( + memory.as_mut_slice(), + 0, // code_hash_ptr + 0, // ref_time_limit + 0, // proof_size_limit + offset(hash_len), // deposit_ptr + offset(deposit_len), // value_ptr + offset(value_len), // input_data_ptr + i, // input_data_len + SENTINEL, // address_ptr + 0, // address_len_ptr + SENTINEL, // output_ptr + 0, // output_len_ptr + offset(i), // salt_ptr + s, // salt_len + ); + } + + assert_ok!(result); + assert!(ContractInfoOf::::get(&addr).is_some()); + assert_eq!(T::Currency::balance(&addr), Pallet::::min_balance() + value); + Ok(()) + } + + // `n`: Input to hash in bytes + #[benchmark(pov_mode = Measured)] + fn seal_hash_sha2_256(n: Linear<0, { limits::MEMORY_BYTES }>) { + build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]); + + let result; + #[block] + { + result = runtime.bench_hash_sha2_256(memory.as_mut_slice(), 32, n, 0); + } + assert_eq!(sp_io::hashing::sha2_256(&memory[32..]), &memory[0..32]); + assert_ok!(result); + } + + // `n`: Input to hash in bytes + #[benchmark(pov_mode = Measured)] + fn seal_hash_keccak_256(n: Linear<0, { limits::MEMORY_BYTES }>) { + build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]); + + let result; + #[block] + { + result = runtime.bench_hash_keccak_256(memory.as_mut_slice(), 32, n, 0); + } + assert_eq!(sp_io::hashing::keccak_256(&memory[32..]), &memory[0..32]); + assert_ok!(result); + } + + // `n`: Input to hash in bytes + #[benchmark(pov_mode = Measured)] + fn seal_hash_blake2_256(n: Linear<0, { limits::MEMORY_BYTES }>) { + build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]); + + let result; + #[block] + { + result = runtime.bench_hash_blake2_256(memory.as_mut_slice(), 32, n, 0); + } + assert_eq!(sp_io::hashing::blake2_256(&memory[32..]), &memory[0..32]); + assert_ok!(result); + } + + // `n`: Input to hash in bytes + #[benchmark(pov_mode = Measured)] + fn seal_hash_blake2_128(n: Linear<0, { limits::MEMORY_BYTES }>) { + build_runtime!(runtime, memory: [[0u8; 16], vec![0u8; n as usize], ]); + + let result; + #[block] + { + result = runtime.bench_hash_blake2_128(memory.as_mut_slice(), 16, n, 0); + } + assert_eq!(sp_io::hashing::blake2_128(&memory[16..]), &memory[0..16]); + assert_ok!(result); + } + + // `n`: Message input length to verify in bytes. + // need some buffer so the code size does not exceed the max code size. + #[benchmark(pov_mode = Measured)] + fn seal_sr25519_verify(n: Linear<0, { T::MaxCodeLen::get() - 255 }>) { + let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::>(); + let message_len = message.len() as u32; + + let key_type = sp_core::crypto::KeyTypeId(*b"code"); + let pub_key = sp_io::crypto::sr25519_generate(key_type, None); + let sig = + sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature"); + let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec(); + let sig_len = sig.len() as u32; + + build_runtime!(runtime, memory: [sig, pub_key.to_vec(), message, ]); + + let result; + #[block] + { + result = runtime.bench_sr25519_verify( + memory.as_mut_slice(), + 0, // signature_ptr + sig_len, // pub_key_ptr + message_len, // message_len + sig_len + pub_key.len() as u32, // message_ptr + ); + } + + assert_eq!(result.unwrap(), ReturnErrorCode::Success); + } + + #[benchmark(pov_mode = Measured)] + fn seal_ecdsa_recover() { + let message_hash = sp_io::hashing::blake2_256("Hello world".as_bytes()); + let key_type = sp_core::crypto::KeyTypeId(*b"code"); + let signature = { + let pub_key = sp_io::crypto::ecdsa_generate(key_type, None); + let sig = sp_io::crypto::ecdsa_sign_prehashed(key_type, &pub_key, &message_hash) + .expect("Generates signature"); + AsRef::<[u8; 65]>::as_ref(&sig).to_vec() + }; + + build_runtime!(runtime, memory: [signature, message_hash, [0u8; 33], ]); + + let result; + #[block] + { + result = runtime.bench_ecdsa_recover( + memory.as_mut_slice(), + 0, // signature_ptr + 65, // message_hash_ptr + 65 + 32, // output_ptr + ); + } + + assert_eq!(result.unwrap(), ReturnErrorCode::Success); + } + + // Only calling the function itself for the list of + // generated different ECDSA keys. + // This is a slow call: We reduce the number of runs. + #[benchmark(pov_mode = Measured)] + fn seal_ecdsa_to_eth_address() { + let key_type = sp_core::crypto::KeyTypeId(*b"code"); + let pub_key_bytes = sp_io::crypto::ecdsa_generate(key_type, None).0; + build_runtime!(runtime, memory: [[0u8; 20], pub_key_bytes,]); + + let result; + #[block] + { + result = runtime.bench_ecdsa_to_eth_address( + memory.as_mut_slice(), + 20, // key_ptr + 0, // output_ptr + ); + } + + assert_ok!(result); + assert_eq!(&memory[..20], runtime.ext().ecdsa_to_eth_address(&pub_key_bytes).unwrap()); + } + + #[benchmark(pov_mode = Measured)] + fn seal_set_code_hash() -> Result<(), BenchmarkError> { + let code_hash = + Contract::::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash; + + build_runtime!(runtime, memory: [ code_hash.encode(),]); + + let result; + #[block] + { + result = runtime.bench_set_code_hash(memory.as_mut_slice(), 0); + } + + assert_ok!(result); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn lock_delegate_dependency() -> Result<(), BenchmarkError> { + let code_hash = Contract::::with_index(1, WasmModule::dummy_unique(1), vec![])? + .info()? + .code_hash; + + build_runtime!(runtime, memory: [ code_hash.encode(),]); + + let result; + #[block] + { + result = runtime.bench_lock_delegate_dependency(memory.as_mut_slice(), 0); + } + + assert_ok!(result); + Ok(()) + } + + #[benchmark] + fn unlock_delegate_dependency() -> Result<(), BenchmarkError> { + let code_hash = Contract::::with_index(1, WasmModule::dummy_unique(1), vec![])? + .info()? + .code_hash; + + build_runtime!(runtime, memory: [ code_hash.encode(),]); + runtime.bench_lock_delegate_dependency(memory.as_mut_slice(), 0).unwrap(); + + let result; + #[block] + { + result = runtime.bench_unlock_delegate_dependency(memory.as_mut_slice(), 0); + } + + assert_ok!(result); + Ok(()) + } + + // Benchmark the execution of instructions. + #[benchmark(pov_mode = Ignored)] + fn instr(r: Linear<0, INSTR_BENCHMARK_RUNS>) { + // (round, start, div, mult, add) + let input = (r, 1_000u32, 2u32, 3u32, 100u32).encode(); + let mut setup = CallSetup::::new(WasmModule::instr()); + let (mut ext, module) = setup.ext(); + let prepared = CallSetup::::prepare_call(&mut ext, module, input); + #[block] + { + prepared.call().unwrap(); + } + } + + impl_benchmark_test_suite!( + Contracts, + crate::tests::ExtBuilder::default().build(), + crate::tests::Test, + ); +} diff --git a/substrate/frame/revive/src/benchmarking_dummy.rs b/substrate/frame/revive/src/benchmarking_dummy.rs new file mode 100644 index 000000000000..6bb467911272 --- /dev/null +++ b/substrate/frame/revive/src/benchmarking_dummy.rs @@ -0,0 +1,37 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +//! Defines a dummy benchmarking suite so that the build doesn't fail in case +//! no RISC-V toolchain is available. + +#![cfg(feature = "runtime-benchmarks")] +#![cfg(not(feature = "riscv"))] + +use crate::{Config, *}; +use frame_benchmarking::v2::*; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark(pov_mode = Ignored)] + fn enable_riscv_feature_to_unlock_benchmarks() { + #[block] + {} + } +} diff --git a/substrate/frame/revive/src/chain_extension.rs b/substrate/frame/revive/src/chain_extension.rs new file mode 100644 index 000000000000..ccea12945054 --- /dev/null +++ b/substrate/frame/revive/src/chain_extension.rs @@ -0,0 +1,358 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! A mechanism for runtime authors to augment the functionality of contracts. +//! +//! The runtime is able to call into any contract and retrieve the result using +//! [`bare_call`](crate::Pallet::bare_call). This already allows customization of runtime +//! behaviour by user generated code (contracts). However, often it is more straightforward +//! to allow the reverse behaviour: The contract calls into the runtime. We call the latter +//! one a "chain extension" because it allows the chain to extend the set of functions that are +//! callable by a contract. +//! +//! In order to create a chain extension the runtime author implements the [`ChainExtension`] +//! trait and declares it in this pallet's [configuration Trait](Config). All types +//! required for this endeavour are defined or re-exported in this module. There is an +//! implementation on `()` which can be used to signal that no chain extension is available. +//! +//! # Using multiple chain extensions +//! +//! Often there is a need for having multiple chain extensions. This is often the case when +//! some generally useful off-the-shelf extensions should be included. To have multiple chain +//! extensions they can be put into a tuple which is then passed to [`Config::ChainExtension`] like +//! this `type Extensions = (ExtensionA, ExtensionB)`. +//! +//! However, only extensions implementing [`RegisteredChainExtension`] can be put into a tuple. +//! This is because the [`RegisteredChainExtension::ID`] is used to decide which of those extensions +//! should be used when the contract calls a chain extensions. Extensions which are generally +//! useful should claim their `ID` with [the registry](https://github.com/paritytech/chainextension-registry) +//! so that no collisions with other vendors will occur. +//! +//! **Chain specific extensions must use the reserved `ID = 0` so that they can't be registered with +//! the registry.** +//! +//! # Security +//! +//! The chain author alone is responsible for the security of the chain extension. +//! This includes avoiding the exposure of exploitable functions and charging the +//! appropriate amount of weight. In order to do so benchmarks must be written and the +//! [`charge_weight`](Environment::charge_weight) function must be called **before** +//! carrying out any action that causes the consumption of the chargeable weight. +//! It cannot be overstated how delicate of a process the creation of a chain extension +//! is. Check whether using [`bare_call`](crate::Pallet::bare_call) suffices for the +//! use case at hand. +//! +//! # Benchmarking +//! +//! The builtin contract callable functions that pallet-revive provides all have +//! benchmarks that determine the correct weight that an invocation of these functions +//! induces. In order to be able to charge the correct weight for the functions defined +//! by a chain extension benchmarks must be written, too. In the near future this crate +//! will provide the means for easier creation of those specialized benchmarks. +//! +//! # Example +//! +//! The ink-examples repository maintains an +//! [end-to-end example](https://github.com/paritytech/ink-examples/tree/main/rand-extension) +//! on how to use a chain extension in order to provide new features to ink! contracts. + +use crate::{ + wasm::{Memory, Runtime, RuntimeCosts}, + Error, +}; +use alloc::vec::Vec; +use codec::{Decode, MaxEncodedLen}; +use frame_support::weights::Weight; +use sp_runtime::DispatchError; + +pub use crate::{exec::Ext, gas::ChargedAmount, storage::meter::Diff, Config}; +pub use frame_system::Config as SysConfig; +pub use pallet_revive_uapi::ReturnFlags; + +/// Result that returns a [`DispatchError`] on error. +pub type Result = core::result::Result; + +/// A trait used to extend the set of contract callable functions. +/// +/// In order to create a custom chain extension this trait must be implemented and supplied +/// to the pallet contracts configuration trait as the associated type of the same name. +/// Consult the [module documentation](self) for a general explanation of chain extensions. +/// +/// # Lifetime +/// +/// The extension will be [`Default`] initialized at the beginning of each call +/// (**not** per call stack) and dropped afterwards. Hence any value held inside the extension +/// can be used as a per-call scratch buffer. +pub trait ChainExtension { + /// Call the chain extension logic. + /// + /// This is the only function that needs to be implemented in order to write a + /// chain extensions. It is called whenever a contract calls the `seal_call_chain_extension` + /// imported wasm function. + /// + /// # Parameters + /// - `env`: Access to the remaining arguments and the execution environment. + /// + /// # Return + /// + /// In case of `Err` the contract execution is immediately suspended and the passed error + /// is returned to the caller. Otherwise the value of [`RetVal`] determines the exit + /// behaviour. + /// + /// # Note + /// + /// The [`Self::call`] can be invoked within a read-only context, where any state-changing calls + /// are disallowed. This information can be obtained using `env.ext().is_read_only()`. It is + /// crucial for the implementer to handle this scenario appropriately. + fn call, M: ?Sized + Memory>( + &mut self, + env: Environment, + ) -> Result; + + /// Determines whether chain extensions are enabled for this chain. + /// + /// The default implementation returns `true`. Therefore it is not necessary to overwrite + /// this function when implementing a chain extension. In case of `false` the deployment of + /// a contract that references `seal_call_chain_extension` will be denied and calling this + /// function will return [`NoChainExtension`](Error::NoChainExtension) without first calling + /// into [`call`](Self::call). + fn enabled() -> bool { + true + } +} + +/// A [`ChainExtension`] that can be composed with other extensions using a tuple. +/// +/// An extension that implements this trait can be put in a tuple in order to have multiple +/// extensions available. The tuple implementation routes requests based on the first two +/// most significant bytes of the `id` passed to `call`. +/// +/// If this extensions is to be used by multiple runtimes consider +/// [registering it](https://github.com/paritytech/chainextension-registry) to ensure that there +/// are no collisions with other vendors. +/// +/// # Note +/// +/// Currently, we support tuples of up to ten registered chain extensions. If more chain extensions +/// are needed consider opening an issue. +pub trait RegisteredChainExtension: ChainExtension { + /// The extensions globally unique identifier. + const ID: u16; +} + +#[impl_trait_for_tuples::impl_for_tuples(10)] +#[tuple_types_custom_trait_bound(RegisteredChainExtension)] +impl ChainExtension for Tuple { + fn call, M: ?Sized + Memory>( + &mut self, + mut env: Environment, + ) -> Result { + for_tuples!( + #( + if (Tuple::ID == env.ext_id()) && Tuple::enabled() { + return Tuple.call(env); + } + )* + ); + Err(Error::::NoChainExtension.into()) + } + + fn enabled() -> bool { + for_tuples!( + #( + if Tuple::enabled() { + return true; + } + )* + ); + false + } +} + +/// Determines the exit behaviour and return value of a chain extension. +pub enum RetVal { + /// The chain extensions returns the supplied value to its calling contract. + Converging(u32), + /// The control does **not** return to the calling contract. + /// + /// Use this to stop the execution of the contract when the chain extension returns. + /// The semantic is the same as for calling `seal_return`: The control returns to + /// the caller of the currently executing contract yielding the supplied buffer and + /// flags. + Diverging { flags: ReturnFlags, data: Vec }, +} + +/// Grants the chain extension access to its parameters and execution environment. +pub struct Environment<'a, 'b, E: Ext, M: ?Sized> { + /// The runtime contains all necessary functions to interact with the running contract. + runtime: &'a mut Runtime<'b, E, M>, + /// Reference to the contract's memory. + memory: &'a mut M, + /// Verbatim argument passed to `seal_call_chain_extension`. + id: u32, + /// Verbatim argument passed to `seal_call_chain_extension`. + input_ptr: u32, + /// Verbatim argument passed to `seal_call_chain_extension`. + input_len: u32, + /// Verbatim argument passed to `seal_call_chain_extension`. + output_ptr: u32, + /// Verbatim argument passed to `seal_call_chain_extension`. + output_len_ptr: u32, +} + +/// Functions that are available in every state of this type. +impl<'a, 'b, E: Ext, M: ?Sized + Memory> Environment<'a, 'b, E, M> { + /// Creates a new environment for consumption by a chain extension. + pub fn new( + runtime: &'a mut Runtime<'b, E, M>, + memory: &'a mut M, + id: u32, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> Self { + Self { runtime, memory, id, input_ptr, input_len, output_ptr, output_len_ptr } + } + + /// The function id within the `id` passed by a contract. + /// + /// It returns the two least significant bytes of the `id` passed by a contract as the other + /// two bytes represent the chain extension itself (the code which is calling this function). + pub fn func_id(&self) -> u16 { + (self.id & 0x0000FFFF) as u16 + } + + /// The chain extension id within the `id` passed by a contract. + /// + /// It returns the two most significant bytes of the `id` passed by a contract which represent + /// the chain extension itself (the code which is calling this function). + pub fn ext_id(&self) -> u16 { + (self.id >> 16) as u16 + } + + /// Charge the passed `amount` of weight from the overall limit. + /// + /// It returns `Ok` when there the remaining weight budget is larger than the passed + /// `weight`. It returns `Err` otherwise. In this case the chain extension should + /// abort the execution and pass through the error. + /// + /// The returned value can be used to with [`Self::adjust_weight`]. Other than that + /// it has no purpose. + /// + /// # Note + /// + /// Weight is synonymous with gas in substrate. + pub fn charge_weight(&mut self, amount: Weight) -> Result { + self.runtime.charge_gas(RuntimeCosts::ChainExtension(amount)) + } + + /// Adjust a previously charged amount down to its actual amount. + /// + /// This is when a maximum a priori amount was charged and then should be partially + /// refunded to match the actual amount. + pub fn adjust_weight(&mut self, charged: ChargedAmount, actual_weight: Weight) { + self.runtime.adjust_gas(charged, RuntimeCosts::ChainExtension(actual_weight)) + } + + /// Grants access to the execution environment of the current contract call. + /// + /// Consult the functions on the returned type before re-implementing those functions. + pub fn ext(&mut self) -> &mut E { + self.runtime.ext() + } + + /// Reads `min(max_len, in_len)` from contract memory. + /// + /// This does **not** charge any weight. The caller must make sure that the an + /// appropriate amount of weight is charged **before** reading from contract memory. + /// The reason for that is that usually the costs for reading data and processing + /// said data cannot be separated in a benchmark. Therefore a chain extension would + /// charge the overall costs either using `max_len` (worst case approximation) or using + /// [`in_len()`](Self::in_len). + pub fn read(&self, max_len: u32) -> Result> { + self.memory.read(self.input_ptr, self.input_len.min(max_len)) + } + + /// Reads `min(buffer.len(), in_len) from contract memory. + /// + /// This takes a mutable pointer to a buffer fills it with data and shrinks it to + /// the size of the actual data. Apart from supporting pre-allocated buffers it is + /// equivalent to to [`read()`](Self::read). + pub fn read_into(&self, buffer: &mut &mut [u8]) -> Result<()> { + let len = buffer.len(); + let sliced = { + let buffer = core::mem::take(buffer); + &mut buffer[..len.min(self.input_len as usize)] + }; + self.memory.read_into_buf(self.input_ptr, sliced)?; + *buffer = sliced; + Ok(()) + } + + /// Reads and decodes a type with a size fixed at compile time from contract memory. + /// + /// This function is secure and recommended for all input types of fixed size + /// as long as the cost of reading the memory is included in the overall already charged + /// weight of the chain extension. This should usually be the case when fixed input types + /// are used. + pub fn read_as(&mut self) -> Result { + self.memory.read_as(self.input_ptr) + } + + /// Reads and decodes a type with a dynamic size from contract memory. + /// + /// Make sure to include `len` in your weight calculations. + pub fn read_as_unbounded(&mut self, len: u32) -> Result { + self.memory.read_as_unbounded(self.input_ptr, len) + } + + /// The length of the input as passed in as `input_len`. + /// + /// A chain extension would use this value to calculate the dynamic part of its + /// weight. For example a chain extension that calculates the hash of some passed in + /// bytes would use `in_len` to charge the costs of hashing that amount of bytes. + /// This also subsumes the act of copying those bytes as a benchmarks measures both. + pub fn in_len(&self) -> u32 { + self.input_len + } + + /// Write the supplied buffer to contract memory. + /// + /// If the contract supplied buffer is smaller than the passed `buffer` an `Err` is returned. + /// If `allow_skip` is set to true the contract is allowed to skip the copying of the buffer + /// by supplying the guard value of `pallet-revive::SENTINEL` as `out_ptr`. The + /// `weight_per_byte` is only charged when the write actually happens and is not skipped or + /// failed due to a too small output buffer. + pub fn write( + &mut self, + buffer: &[u8], + allow_skip: bool, + weight_per_byte: Option, + ) -> Result<()> { + self.runtime.write_sandbox_output( + self.memory, + self.output_ptr, + self.output_len_ptr, + buffer, + allow_skip, + |len| { + weight_per_byte.map(|w| RuntimeCosts::ChainExtension(w.saturating_mul(len.into()))) + }, + ) + } +} diff --git a/substrate/frame/revive/src/debug.rs b/substrate/frame/revive/src/debug.rs new file mode 100644 index 000000000000..467f4e1ad491 --- /dev/null +++ b/substrate/frame/revive/src/debug.rs @@ -0,0 +1,112 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use crate::{ + exec::{ExecResult, ExportedFunction}, + primitives::ExecReturnValue, +}; +use crate::{Config, LOG_TARGET}; + +/// Umbrella trait for all interfaces that serves for debugging. +pub trait Debugger: Tracing + CallInterceptor {} + +impl Debugger for V where V: Tracing + CallInterceptor {} + +/// Defines methods to capture contract calls, enabling external observers to +/// measure, trace, and react to contract interactions. +pub trait Tracing { + /// The type of [`CallSpan`] that is created by this trait. + type CallSpan: CallSpan; + + /// Creates a new call span to encompass the upcoming contract execution. + /// + /// This method should be invoked just before the execution of a contract and + /// marks the beginning of a traceable span of execution. + /// + /// # Arguments + /// + /// * `contract_address` - The address of the contract that is about to be executed. + /// * `entry_point` - Describes whether the call is the constructor or a regular call. + /// * `input_data` - The raw input data of the call. + fn new_call_span( + contract_address: &T::AccountId, + entry_point: ExportedFunction, + input_data: &[u8], + ) -> Self::CallSpan; +} + +/// Defines a span of execution for a contract call. +pub trait CallSpan { + /// Called just after the execution of a contract. + /// + /// # Arguments + /// + /// * `output` - The raw output of the call. + fn after_call(self, output: &ExecReturnValue); +} + +impl Tracing for () { + type CallSpan = (); + + fn new_call_span( + contract_address: &T::AccountId, + entry_point: ExportedFunction, + input_data: &[u8], + ) { + log::trace!(target: LOG_TARGET, "call {entry_point:?} account: {contract_address:?}, input_data: {input_data:?}") + } +} + +impl CallSpan for () { + fn after_call(self, output: &ExecReturnValue) { + log::trace!(target: LOG_TARGET, "call result {output:?}") + } +} + +/// Provides an interface for intercepting contract calls. +pub trait CallInterceptor { + /// Allows to intercept contract calls and decide whether they should be executed or not. + /// If the call is intercepted, the mocked result of the call is returned. + /// + /// # Arguments + /// + /// * `contract_address` - The address of the contract that is about to be executed. + /// * `entry_point` - Describes whether the call is the constructor or a regular call. + /// * `input_data` - The raw input data of the call. + /// + /// # Expected behavior + /// + /// This method should return: + /// * `Some(ExecResult)` - if the call should be intercepted and the mocked result of the call + /// is returned. + /// * `None` - otherwise, i.e. the call should be executed normally. + fn intercept_call( + contract_address: &T::AccountId, + entry_point: ExportedFunction, + input_data: &[u8], + ) -> Option; +} + +impl CallInterceptor for () { + fn intercept_call( + _contract_address: &T::AccountId, + _entry_point: ExportedFunction, + _input_data: &[u8], + ) -> Option { + None + } +} diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs new file mode 100644 index 000000000000..9740707ae706 --- /dev/null +++ b/substrate/frame/revive/src/exec.rs @@ -0,0 +1,3948 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + debug::{CallInterceptor, CallSpan, Tracing}, + gas::GasMeter, + limits, + primitives::{ExecReturnValue, StorageDeposit}, + runtime_decl_for_revive_api::{Decode, Encode, RuntimeDebugNoBound, TypeInfo}, + storage::{self, meter::Diff, WriteOutcome}, + transient_storage::TransientStorage, + BalanceOf, CodeHash, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, DebugBuffer, + Error, Event, Pallet as Contracts, LOG_TARGET, +}; +use alloc::vec::Vec; +use core::{fmt::Debug, marker::PhantomData, mem}; +use frame_support::{ + crypto::ecdsa::ECDSAExt, + dispatch::{DispatchResult, DispatchResultWithPostInfo}, + ensure, + storage::{with_transaction, TransactionOutcome}, + traits::{ + fungible::{Inspect, Mutate}, + tokens::{Fortitude, Preservation}, + Contains, OriginTrait, Time, + }, + weights::Weight, + Blake2_128Concat, BoundedVec, StorageHasher, +}; +use frame_system::{ + pallet_prelude::{BlockNumberFor, OriginFor}, + Pallet as System, RawOrigin, +}; +use sp_core::{ + ecdsa::Public as ECDSAPublic, + sr25519::{Public as SR25519Public, Signature as SR25519Signature}, + ConstU32, Get, +}; +use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256}; +use sp_runtime::{ + traits::{BadOrigin, Convert, Dispatchable, Zero}, + DispatchError, +}; + +pub type AccountIdOf = ::AccountId; +pub type MomentOf = <::Time as Time>::Moment; +pub type ExecResult = Result; + +/// A type that represents a topic of an event. At the moment a hash is used. +pub type TopicOf = ::Hash; + +/// Type for variable sized storage key. Used for transparent hashing. +type VarSizedKey = BoundedVec>; + +/// Combined key type for both fixed and variable sized storage keys. +pub enum Key { + /// Variant for fixed sized keys. + Fix([u8; 32]), + /// Variant for variable sized keys. + Var(VarSizedKey), +} + +impl Key { + /// Reference to the raw unhashed key. + /// + /// # Note + /// + /// Only used by benchmarking in order to generate storage collisions on purpose. + #[cfg(feature = "runtime-benchmarks")] + pub fn unhashed(&self) -> &[u8] { + match self { + Key::Fix(v) => v.as_ref(), + Key::Var(v) => v.as_ref(), + } + } + + /// The hashed key that has be used as actual key to the storage trie. + pub fn hash(&self) -> Vec { + match self { + Key::Fix(v) => blake2_256(v.as_slice()).to_vec(), + Key::Var(v) => Blake2_128Concat::hash(v.as_slice()), + } + } + + pub fn from_fixed(v: [u8; 32]) -> Self { + Self::Fix(v) + } + + pub fn try_from_var(v: Vec) -> Result { + VarSizedKey::try_from(v).map(Self::Var).map_err(|_| ()) + } +} + +/// Origin of the error. +/// +/// Call or instantiate both called into other contracts and pass through errors happening +/// in those to the caller. This enum is for the caller to distinguish whether the error +/// happened during the execution of the callee or in the current execution context. +#[derive(Copy, Clone, PartialEq, Eq, Debug, codec::Decode, codec::Encode)] +pub enum ErrorOrigin { + /// Caller error origin. + /// + /// The error happened in the current execution context rather than in the one + /// of the contract that is called into. + Caller, + /// The error happened during execution of the called contract. + Callee, +} + +/// Error returned by contract execution. +#[derive(Copy, Clone, PartialEq, Eq, Debug, codec::Decode, codec::Encode)] +pub struct ExecError { + /// The reason why the execution failed. + pub error: DispatchError, + /// Origin of the error. + pub origin: ErrorOrigin, +} + +impl> From for ExecError { + fn from(error: T) -> Self { + Self { error: error.into(), origin: ErrorOrigin::Caller } + } +} + +/// The type of origins supported by the contracts pallet. +#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, RuntimeDebugNoBound)] +pub enum Origin { + Root, + Signed(T::AccountId), +} + +impl Origin { + /// Creates a new Signed Caller from an AccountId. + pub fn from_account_id(account_id: T::AccountId) -> Self { + Origin::Signed(account_id) + } + /// Creates a new Origin from a `RuntimeOrigin`. + pub fn from_runtime_origin(o: OriginFor) -> Result { + match o.into() { + Ok(RawOrigin::Root) => Ok(Self::Root), + Ok(RawOrigin::Signed(t)) => Ok(Self::Signed(t)), + _ => Err(BadOrigin.into()), + } + } + /// Returns the AccountId of a Signed Origin or an error if the origin is Root. + pub fn account_id(&self) -> Result<&T::AccountId, DispatchError> { + match self { + Origin::Signed(id) => Ok(id), + Origin::Root => Err(DispatchError::RootNotAllowed), + } + } +} + +/// An interface that provides access to the external environment in which the +/// smart-contract is executed. +/// +/// This interface is specialized to an account of the executing code, so all +/// operations are implicitly performed on that account. +/// +/// # Note +/// +/// This trait is sealed and cannot be implemented by downstream crates. +pub trait Ext: sealing::Sealed { + type T: Config; + + /// Call (possibly transferring some amount of funds) into the specified account. + /// + /// Returns the code size of the called contract. + fn call( + &mut self, + gas_limit: Weight, + deposit_limit: BalanceOf, + to: AccountIdOf, + value: BalanceOf, + input_data: Vec, + allows_reentry: bool, + read_only: bool, + ) -> Result; + + /// Execute code in the current frame. + /// + /// Returns the code size of the called contract. + fn delegate_call( + &mut self, + code: CodeHash, + input_data: Vec, + ) -> Result; + + /// Instantiate a contract from the given code. + /// + /// Returns the original code size of the called contract. + /// The newly created account will be associated with `code`. `value` specifies the amount of + /// value transferred from the caller to the newly created account. + fn instantiate( + &mut self, + gas_limit: Weight, + deposit_limit: BalanceOf, + code: CodeHash, + value: BalanceOf, + input_data: Vec, + salt: &[u8], + ) -> Result<(AccountIdOf, ExecReturnValue), ExecError>; + + /// Transfer all funds to `beneficiary` and delete the contract. + /// + /// Since this function removes the self contract eagerly, if succeeded, no further actions + /// should be performed on this `Ext` instance. + /// + /// This function will fail if the same contract is present on the contract + /// call stack. + fn terminate(&mut self, beneficiary: &AccountIdOf) -> DispatchResult; + + /// Transfer some amount of funds into the specified account. + fn transfer(&mut self, to: &AccountIdOf, value: BalanceOf) -> DispatchResult; + + /// Returns the storage entry of the executing account by the given `key`. + /// + /// Returns `None` if the `key` wasn't previously set by `set_storage` or + /// was deleted. + fn get_storage(&mut self, key: &Key) -> Option>; + + /// Returns `Some(len)` (in bytes) if a storage item exists at `key`. + /// + /// Returns `None` if the `key` wasn't previously set by `set_storage` or + /// was deleted. + fn get_storage_size(&mut self, key: &Key) -> Option; + + /// Sets the storage entry by the given key to the specified value. If `value` is `None` then + /// the storage entry is deleted. + fn set_storage( + &mut self, + key: &Key, + value: Option>, + take_old: bool, + ) -> Result; + + /// Returns the transient storage entry of the executing account for the given `key`. + /// + /// Returns `None` if the `key` wasn't previously set by `set_transient_storage` or + /// was deleted. + fn get_transient_storage(&self, key: &Key) -> Option>; + + /// Returns `Some(len)` (in bytes) if a transient storage item exists at `key`. + /// + /// Returns `None` if the `key` wasn't previously set by `set_transient_storage` or + /// was deleted. + fn get_transient_storage_size(&self, key: &Key) -> Option; + + /// Sets the transient storage entry for the given key to the specified value. If `value` is + /// `None` then the storage entry is deleted. + fn set_transient_storage( + &mut self, + key: &Key, + value: Option>, + take_old: bool, + ) -> Result; + + /// Returns the caller. + fn caller(&self) -> Origin; + + /// Check if a contract lives at the specified `address`. + fn is_contract(&self, address: &AccountIdOf) -> bool; + + /// Returns the code hash of the contract for the given `address`. + /// + /// Returns `None` if the `address` does not belong to a contract. + fn code_hash(&self, address: &AccountIdOf) -> Option>; + + /// Returns the code hash of the contract being executed. + fn own_code_hash(&mut self) -> &CodeHash; + + /// Check if the caller of the current contract is the origin of the whole call stack. + /// + /// This can be checked with `is_contract(self.caller())` as well. + /// However, this function does not require any storage lookup and therefore uses less weight. + fn caller_is_origin(&self) -> bool; + + /// Check if the caller is origin, and this origin is root. + fn caller_is_root(&self) -> bool; + + /// Returns a reference to the account id of the current contract. + fn address(&self) -> &AccountIdOf; + + /// Returns the balance of the current contract. + /// + /// The `value_transferred` is already added. + fn balance(&self) -> BalanceOf; + + /// Returns the value transferred along with this call. + fn value_transferred(&self) -> BalanceOf; + + /// Returns a reference to the timestamp of the current block + fn now(&self) -> &MomentOf; + + /// Returns the minimum balance that is required for creating an account. + fn minimum_balance(&self) -> BalanceOf; + + /// Deposit an event with the given topics. + /// + /// There should not be any duplicates in `topics`. + fn deposit_event(&mut self, topics: Vec>, data: Vec); + + /// Returns the current block number. + fn block_number(&self) -> BlockNumberFor; + + /// Returns the maximum allowed size of a storage item. + fn max_value_size(&self) -> u32; + + /// Returns the price for the specified amount of weight. + fn get_weight_price(&self, weight: Weight) -> BalanceOf; + + /// Get an immutable reference to the nested gas meter. + fn gas_meter(&self) -> &GasMeter; + + /// Get a mutable reference to the nested gas meter. + fn gas_meter_mut(&mut self) -> &mut GasMeter; + + /// Charges `diff` from the meter. + fn charge_storage(&mut self, diff: &Diff); + + /// Append a string to the debug buffer. + /// + /// It is added as-is without any additional new line. + /// + /// This is a no-op if debug message recording is disabled which is always the case + /// when the code is executing on-chain. + /// + /// Returns `true` if debug message recording is enabled. Otherwise `false` is returned. + fn append_debug_buffer(&mut self, msg: &str) -> bool; + + /// Returns `true` if debug message recording is enabled. Otherwise `false` is returned. + fn debug_buffer_enabled(&self) -> bool; + + /// Call some dispatchable and return the result. + fn call_runtime(&self, call: ::RuntimeCall) -> DispatchResultWithPostInfo; + + /// Recovers ECDSA compressed public key based on signature and message hash. + fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()>; + + /// Verify a sr25519 signature. + fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool; + + /// Returns Ethereum address from the ECDSA compressed public key. + fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], ()>; + + /// Tests sometimes need to modify and inspect the contract info directly. + #[cfg(any(test, feature = "runtime-benchmarks"))] + fn contract_info(&mut self) -> &mut ContractInfo; + + /// Get a mutable reference to the transient storage. + /// Useful in benchmarks when it is sometimes necessary to modify and inspect the transient + /// storage directly. + #[cfg(feature = "runtime-benchmarks")] + fn transient_storage(&mut self) -> &mut TransientStorage; + + /// Sets new code hash for existing contract. + fn set_code_hash(&mut self, hash: CodeHash) -> DispatchResult; + + /// Returns the number of times the specified contract exists on the call stack. Delegated calls + /// Increment the reference count of a of a stored code by one. + /// + /// # Errors + /// + /// [`Error::CodeNotFound`] is returned if no stored code found having the specified + /// `code_hash`. + fn increment_refcount(code_hash: CodeHash) -> DispatchResult; + + /// Decrement the reference count of a stored code by one. + /// + /// # Note + /// + /// A contract whose reference count dropped to zero isn't automatically removed. A + /// `remove_code` transaction must be submitted by the original uploader to do so. + fn decrement_refcount(code_hash: CodeHash); + + /// Adds a delegate dependency to [`ContractInfo`]'s `delegate_dependencies` field. + /// + /// This ensures that the delegated contract is not removed while it is still in use. It + /// increases the reference count of the code hash and charges a fraction (see + /// [`Config::CodeHashLockupDepositPercent`]) of the code deposit. + /// + /// # Errors + /// + /// - [`Error::MaxDelegateDependenciesReached`] + /// - [`Error::CannotAddSelfAsDelegateDependency`] + /// - [`Error::DelegateDependencyAlreadyExists`] + fn lock_delegate_dependency(&mut self, code_hash: CodeHash) -> DispatchResult; + + /// Removes a delegate dependency from [`ContractInfo`]'s `delegate_dependencies` field. + /// + /// This is the counterpart of [`Self::lock_delegate_dependency`]. It decreases the reference + /// count and refunds the deposit that was charged by [`Self::lock_delegate_dependency`]. + /// + /// # Errors + /// + /// - [`Error::DelegateDependencyNotFound`] + fn unlock_delegate_dependency(&mut self, code_hash: &CodeHash) -> DispatchResult; + + /// Returns the number of locked delegate dependencies. + /// + /// Note: Requires &mut self to access the contract info. + fn locked_delegate_dependencies_count(&mut self) -> usize; + + /// Check if running in read-only context. + fn is_read_only(&self) -> bool; +} + +/// Describes the different functions that can be exported by an [`Executable`]. +#[derive( + Copy, + Clone, + PartialEq, + Eq, + sp_core::RuntimeDebug, + codec::Decode, + codec::Encode, + codec::MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ExportedFunction { + /// The constructor function which is executed on deployment of a contract. + Constructor, + /// The function which is executed when a contract is called. + Call, +} + +/// A trait that represents something that can be executed. +/// +/// In the on-chain environment this would be represented by a wasm module. This trait exists in +/// order to be able to mock the wasm logic for testing. +pub trait Executable: Sized { + /// Load the executable from storage. + /// + /// # Note + /// Charges size base load weight from the gas meter. + fn from_storage( + code_hash: CodeHash, + gas_meter: &mut GasMeter, + ) -> Result; + + /// Execute the specified exported function and return the result. + /// + /// When the specified function is `Constructor` the executable is stored and its + /// refcount incremented. + /// + /// # Note + /// + /// This functions expects to be executed in a storage transaction that rolls back + /// all of its emitted storage changes. + fn execute>( + self, + ext: &mut E, + function: ExportedFunction, + input_data: Vec, + ) -> ExecResult; + + /// The code info of the executable. + fn code_info(&self) -> &CodeInfo; + + /// The code hash of the executable. + fn code_hash(&self) -> &CodeHash; +} + +/// The complete call stack of a contract execution. +/// +/// The call stack is initiated by either a signed origin or one of the contract RPC calls. +/// This type implements `Ext` and by that exposes the business logic of contract execution to +/// the runtime module which interfaces with the contract (the wasm blob) itself. +pub struct Stack<'a, T: Config, E> { + /// The origin that initiated the call stack. It could either be a Signed plain account that + /// holds an account id or Root. + /// + /// # Note + /// + /// Please note that it is possible that the id of a Signed origin belongs to a contract rather + /// than a plain account when being called through one of the contract RPCs where the + /// client can freely choose the origin. This usually makes no sense but is still possible. + origin: Origin, + /// The gas meter where costs are charged to. + gas_meter: &'a mut GasMeter, + /// The storage meter makes sure that the storage deposit limit is obeyed. + storage_meter: &'a mut storage::meter::Meter, + /// The timestamp at the point of call stack instantiation. + timestamp: MomentOf, + /// The block number at the time of call stack instantiation. + block_number: BlockNumberFor, + /// The actual call stack. One entry per nested contract called/instantiated. + /// This does **not** include the [`Self::first_frame`]. + frames: BoundedVec, ConstU32<{ limits::CALL_STACK_DEPTH }>>, + /// Statically guarantee that each call stack has at least one frame. + first_frame: Frame, + /// A text buffer used to output human readable information. + /// + /// All the bytes added to this field should be valid UTF-8. The buffer has no defined + /// structure and is intended to be shown to users as-is for debugging purposes. + debug_message: Option<&'a mut DebugBuffer>, + /// Transient storage used to store data, which is kept for the duration of a transaction. + transient_storage: TransientStorage, + /// No executable is held by the struct but influences its behaviour. + _phantom: PhantomData, +} + +/// Represents one entry in the call stack. +/// +/// For each nested contract call or instantiate one frame is created. It holds specific +/// information for the said call and caches the in-storage `ContractInfo` data structure. +struct Frame { + /// The account id of the executing contract. + account_id: T::AccountId, + /// The cached in-storage data of the contract. + contract_info: CachedContract, + /// The amount of balance transferred by the caller as part of the call. + value_transferred: BalanceOf, + /// Determines whether this is a call or instantiate frame. + entry_point: ExportedFunction, + /// The gas meter capped to the supplied gas limit. + nested_gas: GasMeter, + /// The storage meter for the individual call. + nested_storage: storage::meter::NestedMeter, + /// If `false` the contract enabled its defense against reentrance attacks. + allows_reentry: bool, + /// If `true` subsequent calls cannot modify storage. + read_only: bool, + /// The caller of the currently executing frame which was spawned by `delegate_call`. + delegate_caller: Option>, +} + +/// Used in a delegate call frame arguments in order to override the executable and caller. +struct DelegatedCall { + /// The executable which is run instead of the contracts own `executable`. + executable: E, + /// The caller of the contract. + caller: Origin, +} + +/// Parameter passed in when creating a new `Frame`. +/// +/// It determines whether the new frame is for a call or an instantiate. +enum FrameArgs<'a, T: Config, E> { + Call { + /// The account id of the contract that is to be called. + dest: T::AccountId, + /// If `None` the contract info needs to be reloaded from storage. + cached_info: Option>, + /// This frame was created by `seal_delegate_call` and hence uses different code than + /// what is stored at [`Self::Call::dest`]. Its caller ([`DelegatedCall::caller`]) is the + /// account which called the caller contract + delegated_call: Option>, + }, + Instantiate { + /// The contract or signed origin which instantiates the new contract. + sender: T::AccountId, + /// The executable whose `deploy` function is run. + executable: E, + /// A salt used in the contract address derivation of the new contract. + salt: &'a [u8], + /// The input data is used in the contract address derivation of the new contract. + input_data: &'a [u8], + }, +} + +/// Describes the different states of a contract as contained in a `Frame`. +enum CachedContract { + /// The cached contract is up to date with the in-storage value. + Cached(ContractInfo), + /// A recursive call into the same contract did write to the contract info. + /// + /// In this case the cached contract is stale and needs to be reloaded from storage. + Invalidated, + /// The current contract executed `terminate` and removed the contract. + /// + /// In this case a reload is neither allowed nor possible. Please note that recursive + /// calls cannot remove a contract as this is checked and denied. + Terminated, +} + +impl CachedContract { + /// Return `Some(ContractInfo)` if the contract is in cached state. `None` otherwise. + fn into_contract(self) -> Option> { + if let CachedContract::Cached(contract) = self { + Some(contract) + } else { + None + } + } + + /// Return `Some(&mut ContractInfo)` if the contract is in cached state. `None` otherwise. + fn as_contract(&mut self) -> Option<&mut ContractInfo> { + if let CachedContract::Cached(contract) = self { + Some(contract) + } else { + None + } + } +} + +impl Frame { + /// Return the `contract_info` of the current contract. + fn contract_info(&mut self) -> &mut ContractInfo { + self.contract_info.get(&self.account_id) + } + + /// Terminate and return the `contract_info` of the current contract. + /// + /// # Note + /// + /// Under no circumstances the contract is allowed to access the `contract_info` after + /// a call to this function. This would constitute a programming error in the exec module. + fn terminate(&mut self) -> ContractInfo { + self.contract_info.terminate(&self.account_id) + } +} + +/// Extract the contract info after loading it from storage. +/// +/// This assumes that `load` was executed before calling this macro. +macro_rules! get_cached_or_panic_after_load { + ($c:expr) => {{ + if let CachedContract::Cached(contract) = $c { + contract + } else { + panic!( + "It is impossible to remove a contract that is on the call stack;\ + See implementations of terminate;\ + Therefore fetching a contract will never fail while using an account id + that is currently active on the call stack;\ + qed" + ); + } + }}; +} + +/// Same as [`Stack::top_frame`]. +/// +/// We need this access as a macro because sometimes hiding the lifetimes behind +/// a function won't work out. +macro_rules! top_frame { + ($stack:expr) => { + $stack.frames.last().unwrap_or(&$stack.first_frame) + }; +} + +/// Same as [`Stack::top_frame_mut`]. +/// +/// We need this access as a macro because sometimes hiding the lifetimes behind +/// a function won't work out. +macro_rules! top_frame_mut { + ($stack:expr) => { + $stack.frames.last_mut().unwrap_or(&mut $stack.first_frame) + }; +} + +impl CachedContract { + /// Load the `contract_info` from storage if necessary. + fn load(&mut self, account_id: &T::AccountId) { + if let CachedContract::Invalidated = self { + let contract = >::get(&account_id); + if let Some(contract) = contract { + *self = CachedContract::Cached(contract); + } + } + } + + /// Return the cached contract_info. + fn get(&mut self, account_id: &T::AccountId) -> &mut ContractInfo { + self.load(account_id); + get_cached_or_panic_after_load!(self) + } + + /// Terminate and return the contract info. + fn terminate(&mut self, account_id: &T::AccountId) -> ContractInfo { + self.load(account_id); + get_cached_or_panic_after_load!(mem::replace(self, Self::Terminated)) + } +} + +impl<'a, T, E> Stack<'a, T, E> +where + T: Config, + E: Executable, +{ + /// Create and run a new call stack by calling into `dest`. + /// + /// # Note + /// + /// `debug_message` should only ever be set to `Some` when executing as an RPC because + /// it adds allocations and could be abused to drive the runtime into an OOM panic. + /// + /// # Return Value + /// + /// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)> + pub fn run_call( + origin: Origin, + dest: T::AccountId, + gas_meter: &'a mut GasMeter, + storage_meter: &'a mut storage::meter::Meter, + value: BalanceOf, + input_data: Vec, + debug_message: Option<&'a mut DebugBuffer>, + ) -> Result { + let (mut stack, executable) = Self::new( + FrameArgs::Call { dest, cached_info: None, delegated_call: None }, + origin, + gas_meter, + storage_meter, + value, + debug_message, + )?; + stack.run(executable, input_data) + } + + /// Create and run a new call stack by instantiating a new contract. + /// + /// # Note + /// + /// `debug_message` should only ever be set to `Some` when executing as an RPC because + /// it adds allocations and could be abused to drive the runtime into an OOM panic. + /// + /// # Return Value + /// + /// Result<(NewContractAccountId, ExecReturnValue), ExecError)> + pub fn run_instantiate( + origin: T::AccountId, + executable: E, + gas_meter: &'a mut GasMeter, + storage_meter: &'a mut storage::meter::Meter, + value: BalanceOf, + input_data: Vec, + salt: &[u8], + debug_message: Option<&'a mut DebugBuffer>, + ) -> Result<(T::AccountId, ExecReturnValue), ExecError> { + let (mut stack, executable) = Self::new( + FrameArgs::Instantiate { + sender: origin.clone(), + executable, + salt, + input_data: input_data.as_ref(), + }, + Origin::from_account_id(origin), + gas_meter, + storage_meter, + value, + debug_message, + )?; + let account_id = stack.top_frame().account_id.clone(); + stack.run(executable, input_data).map(|ret| (account_id, ret)) + } + + #[cfg(all(feature = "runtime-benchmarks", feature = "riscv"))] + pub fn bench_new_call( + dest: T::AccountId, + origin: Origin, + gas_meter: &'a mut GasMeter, + storage_meter: &'a mut storage::meter::Meter, + value: BalanceOf, + debug_message: Option<&'a mut DebugBuffer>, + ) -> (Self, E) { + Self::new( + FrameArgs::Call { dest, cached_info: None, delegated_call: None }, + origin, + gas_meter, + storage_meter, + value, + debug_message, + ) + .unwrap() + } + + /// Create a new call stack. + fn new( + args: FrameArgs, + origin: Origin, + gas_meter: &'a mut GasMeter, + storage_meter: &'a mut storage::meter::Meter, + value: BalanceOf, + debug_message: Option<&'a mut DebugBuffer>, + ) -> Result<(Self, E), ExecError> { + let (first_frame, executable) = Self::new_frame( + args, + value, + gas_meter, + Weight::zero(), + storage_meter, + BalanceOf::::zero(), + false, + )?; + + let stack = Self { + origin, + gas_meter, + storage_meter, + timestamp: T::Time::now(), + block_number: >::block_number(), + first_frame, + frames: Default::default(), + debug_message, + transient_storage: TransientStorage::new(limits::TRANSIENT_STORAGE_BYTES), + _phantom: Default::default(), + }; + + Ok((stack, executable)) + } + + /// Construct a new frame. + /// + /// This does not take `self` because when constructing the first frame `self` is + /// not initialized, yet. + fn new_frame( + frame_args: FrameArgs, + value_transferred: BalanceOf, + gas_meter: &mut GasMeter, + gas_limit: Weight, + storage_meter: &mut storage::meter::GenericMeter, + deposit_limit: BalanceOf, + read_only: bool, + ) -> Result<(Frame, E), ExecError> { + let (account_id, contract_info, executable, delegate_caller, entry_point) = match frame_args + { + FrameArgs::Call { dest, cached_info, delegated_call } => { + let contract = if let Some(contract) = cached_info { + contract + } else { + >::get(&dest).ok_or(>::ContractNotFound)? + }; + + let (executable, delegate_caller) = + if let Some(DelegatedCall { executable, caller }) = delegated_call { + (executable, Some(caller)) + } else { + (E::from_storage(contract.code_hash, gas_meter)?, None) + }; + + (dest, contract, executable, delegate_caller, ExportedFunction::Call) + }, + FrameArgs::Instantiate { sender, executable, salt, input_data } => { + let account_id = Contracts::::contract_address( + &sender, + &executable.code_hash(), + input_data, + salt, + ); + let contract = ContractInfo::new( + &account_id, + >::account_nonce(&sender), + *executable.code_hash(), + )?; + (account_id, contract, executable, None, ExportedFunction::Constructor) + }, + }; + + let frame = Frame { + delegate_caller, + value_transferred, + contract_info: CachedContract::Cached(contract_info), + account_id, + entry_point, + nested_gas: gas_meter.nested(gas_limit), + nested_storage: storage_meter.nested(deposit_limit), + allows_reentry: true, + read_only, + }; + + Ok((frame, executable)) + } + + /// Create a subsequent nested frame. + fn push_frame( + &mut self, + frame_args: FrameArgs, + value_transferred: BalanceOf, + gas_limit: Weight, + deposit_limit: BalanceOf, + read_only: bool, + ) -> Result { + if self.frames.len() as u32 == limits::CALL_STACK_DEPTH { + return Err(Error::::MaxCallDepthReached.into()) + } + + // We need to make sure that changes made to the contract info are not discarded. + // See the `in_memory_changes_not_discarded` test for more information. + // We do not store on instantiate because we do not allow to call into a contract + // from its own constructor. + let frame = self.top_frame(); + if let (CachedContract::Cached(contract), ExportedFunction::Call) = + (&frame.contract_info, frame.entry_point) + { + >::insert(frame.account_id.clone(), contract.clone()); + } + + let frame = top_frame_mut!(self); + let nested_gas = &mut frame.nested_gas; + let nested_storage = &mut frame.nested_storage; + let (frame, executable) = Self::new_frame( + frame_args, + value_transferred, + nested_gas, + gas_limit, + nested_storage, + deposit_limit, + read_only, + )?; + self.frames.try_push(frame).map_err(|_| Error::::MaxCallDepthReached)?; + Ok(executable) + } + + /// Run the current (top) frame. + /// + /// This can be either a call or an instantiate. + fn run(&mut self, executable: E, input_data: Vec) -> Result { + let frame = self.top_frame(); + let entry_point = frame.entry_point; + let delegated_code_hash = + if frame.delegate_caller.is_some() { Some(*executable.code_hash()) } else { None }; + + self.transient_storage.start_transaction(); + + let do_transaction = || { + // We need to charge the storage deposit before the initial transfer so that + // it can create the account in case the initial transfer is < ed. + if entry_point == ExportedFunction::Constructor { + // Root origin can't be used to instantiate a contract, so it is safe to assume that + // if we reached this point the origin has an associated account. + let origin = &self.origin.account_id()?; + let frame = top_frame_mut!(self); + frame.nested_storage.charge_instantiate( + origin, + &frame.account_id, + frame.contract_info.get(&frame.account_id), + executable.code_info(), + )?; + // Needs to be incremented before calling into the code so that it is visible + // in case of recursion. + >::inc_account_nonce(self.caller().account_id()?); + } + + // Every non delegate call or instantiate also optionally transfers the balance. + self.initial_transfer()?; + + let contract_address = &top_frame!(self).account_id; + + let call_span = T::Debug::new_call_span(contract_address, entry_point, &input_data); + + let output = T::Debug::intercept_call(contract_address, entry_point, &input_data) + .unwrap_or_else(|| { + executable + .execute(self, entry_point, input_data) + .map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee }) + })?; + + call_span.after_call(&output); + + // Avoid useless work that would be reverted anyways. + if output.did_revert() { + return Ok(output) + } + + // Storage limit is normally enforced as late as possible (when the last frame returns) + // so that the ordering of storage accesses does not matter. + // (However, if a special limit was set for a sub-call, it should be enforced right + // after the sub-call returned. See below for this case of enforcement). + if self.frames.is_empty() { + let frame = &mut self.first_frame; + frame.contract_info.load(&frame.account_id); + let contract = frame.contract_info.as_contract(); + frame.nested_storage.enforce_limit(contract)?; + } + + let frame = self.top_frame(); + let account_id = &frame.account_id.clone(); + match (entry_point, delegated_code_hash) { + (ExportedFunction::Constructor, _) => { + // It is not allowed to terminate a contract inside its constructor. + if matches!(frame.contract_info, CachedContract::Terminated) { + return Err(Error::::TerminatedInConstructor.into()) + } + + // If a special limit was set for the sub-call, we enforce it here. + // This is needed because contract constructor might write to storage. + // The sub-call will be rolled back in case the limit is exhausted. + let frame = self.top_frame_mut(); + let contract = frame.contract_info.as_contract(); + frame.nested_storage.enforce_subcall_limit(contract)?; + + let caller = self.caller().account_id()?.clone(); + + // Deposit an instantiation event. + Contracts::::deposit_event(Event::Instantiated { + deployer: caller, + contract: account_id.clone(), + }); + }, + (ExportedFunction::Call, Some(code_hash)) => { + Contracts::::deposit_event(Event::DelegateCalled { + contract: account_id.clone(), + code_hash, + }); + }, + (ExportedFunction::Call, None) => { + // If a special limit was set for the sub-call, we enforce it here. + // The sub-call will be rolled back in case the limit is exhausted. + let frame = self.top_frame_mut(); + let contract = frame.contract_info.as_contract(); + frame.nested_storage.enforce_subcall_limit(contract)?; + + let caller = self.caller(); + Contracts::::deposit_event(Event::Called { + caller: caller.clone(), + contract: account_id.clone(), + }); + }, + } + + Ok(output) + }; + + // All changes performed by the contract are executed under a storage transaction. + // This allows for roll back on error. Changes to the cached contract_info are + // committed or rolled back when popping the frame. + // + // `with_transactional` may return an error caused by a limit in the + // transactional storage depth. + let transaction_outcome = + with_transaction(|| -> TransactionOutcome> { + let output = do_transaction(); + match &output { + Ok(result) if !result.did_revert() => + TransactionOutcome::Commit(Ok((true, output))), + _ => TransactionOutcome::Rollback(Ok((false, output))), + } + }); + + let (success, output) = match transaction_outcome { + // `with_transactional` executed successfully, and we have the expected output. + Ok((success, output)) => (success, output), + // `with_transactional` returned an error, and we propagate that error and note no state + // has changed. + Err(error) => (false, Err(error.into())), + }; + + if success { + self.transient_storage.commit_transaction(); + } else { + self.transient_storage.rollback_transaction(); + } + + self.pop_frame(success); + output + } + + /// Remove the current (top) frame from the stack. + /// + /// This is called after running the current frame. It commits cached values to storage + /// and invalidates all stale references to it that might exist further down the call stack. + fn pop_frame(&mut self, persist: bool) { + // Pop the current frame from the stack and return it in case it needs to interact + // with duplicates that might exist on the stack. + // A `None` means that we are returning from the `first_frame`. + let frame = self.frames.pop(); + + // Both branches do essentially the same with the exception. The difference is that + // the else branch does consume the hardcoded `first_frame`. + if let Some(mut frame) = frame { + let account_id = &frame.account_id; + let prev = top_frame_mut!(self); + + prev.nested_gas.absorb_nested(frame.nested_gas); + + // Only gas counter changes are persisted in case of a failure. + if !persist { + return + } + + // Record the storage meter changes of the nested call into the parent meter. + // If the dropped frame's contract wasn't terminated we update the deposit counter + // in its contract info. The load is necessary to pull it from storage in case + // it was invalidated. + frame.contract_info.load(account_id); + let mut contract = frame.contract_info.into_contract(); + prev.nested_storage.absorb(frame.nested_storage, account_id, contract.as_mut()); + + // In case the contract wasn't terminated we need to persist changes made to it. + if let Some(contract) = contract { + // optimization: Predecessor is the same contract. + // We can just copy the contract into the predecessor without a storage write. + // This is possible when there is no other contract in-between that could + // trigger a rollback. + if prev.account_id == *account_id { + prev.contract_info = CachedContract::Cached(contract); + return + } + + // Predecessor is a different contract: We persist the info and invalidate the first + // stale cache we find. This triggers a reload from storage on next use. We skip(1) + // because that case is already handled by the optimization above. Only the first + // cache needs to be invalidated because that one will invalidate the next cache + // when it is popped from the stack. + >::insert(account_id, contract); + if let Some(c) = self.frames_mut().skip(1).find(|f| f.account_id == *account_id) { + c.contract_info = CachedContract::Invalidated; + } + } + } else { + if let Some((msg, false)) = self.debug_message.as_ref().map(|m| (m, m.is_empty())) { + log::debug!( + target: LOG_TARGET, + "Execution finished with debug buffer: {}", + core::str::from_utf8(msg).unwrap_or(""), + ); + } + self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_gas)); + if !persist { + return + } + let mut contract = self.first_frame.contract_info.as_contract(); + self.storage_meter.absorb( + mem::take(&mut self.first_frame.nested_storage), + &self.first_frame.account_id, + contract.as_deref_mut(), + ); + if let Some(contract) = contract { + >::insert(&self.first_frame.account_id, contract); + } + } + } + + /// Transfer some funds from `from` to `to`. + fn transfer( + preservation: Preservation, + from: &T::AccountId, + to: &T::AccountId, + value: BalanceOf, + ) -> DispatchResult { + if !value.is_zero() && from != to { + T::Currency::transfer(from, to, value, preservation) + .map_err(|_| Error::::TransferFailed)?; + } + Ok(()) + } + + // The transfer as performed by a call or instantiate. + fn initial_transfer(&self) -> DispatchResult { + let frame = self.top_frame(); + + // If it is a delegate call, then we've already transferred tokens in the + // last non-delegate frame. + if frame.delegate_caller.is_some() { + return Ok(()) + } + + let value = frame.value_transferred; + + // Get the account id from the caller. + // If the caller is root there is no account to transfer from, and therefore we can't take + // any `value` other than 0. + let caller = match self.caller() { + Origin::Signed(caller) => caller, + Origin::Root if value.is_zero() => return Ok(()), + Origin::Root => return DispatchError::RootNotAllowed.into(), + }; + Self::transfer(Preservation::Preserve, &caller, &frame.account_id, value) + } + + /// Reference to the current (top) frame. + fn top_frame(&self) -> &Frame { + top_frame!(self) + } + + /// Mutable reference to the current (top) frame. + fn top_frame_mut(&mut self) -> &mut Frame { + top_frame_mut!(self) + } + + /// Iterator over all frames. + /// + /// The iterator starts with the top frame and ends with the root frame. + fn frames(&self) -> impl Iterator> { + core::iter::once(&self.first_frame).chain(&self.frames).rev() + } + + /// Same as `frames` but with a mutable reference as iterator item. + fn frames_mut(&mut self) -> impl Iterator> { + core::iter::once(&mut self.first_frame).chain(&mut self.frames).rev() + } + + /// Returns whether the current contract is on the stack multiple times. + fn is_recursive(&self) -> bool { + let account_id = &self.top_frame().account_id; + self.frames().skip(1).any(|f| &f.account_id == account_id) + } + + /// Returns whether the specified contract allows to be reentered right now. + fn allows_reentry(&self, id: &AccountIdOf) -> bool { + !self.frames().any(|f| &f.account_id == id && !f.allows_reentry) + } +} + +impl<'a, T, E> Ext for Stack<'a, T, E> +where + T: Config, + E: Executable, +{ + type T = T; + + fn call( + &mut self, + gas_limit: Weight, + deposit_limit: BalanceOf, + to: T::AccountId, + value: BalanceOf, + input_data: Vec, + allows_reentry: bool, + read_only: bool, + ) -> Result { + // Before pushing the new frame: Protect the caller contract against reentrancy attacks. + // It is important to do this before calling `allows_reentry` so that a direct recursion + // is caught by it. + self.top_frame_mut().allows_reentry = allows_reentry; + + let try_call = || { + if !self.allows_reentry(&to) { + return Err(>::ReentranceDenied.into()) + } + + // We ignore instantiate frames in our search for a cached contract. + // Otherwise it would be possible to recursively call a contract from its own + // constructor: We disallow calling not fully constructed contracts. + let cached_info = self + .frames() + .find(|f| f.entry_point == ExportedFunction::Call && f.account_id == to) + .and_then(|f| match &f.contract_info { + CachedContract::Cached(contract) => Some(contract.clone()), + _ => None, + }); + let executable = self.push_frame( + FrameArgs::Call { dest: to, cached_info, delegated_call: None }, + value, + gas_limit, + deposit_limit, + // Enable read-only access if requested; cannot disable it if already set. + read_only || self.is_read_only(), + )?; + self.run(executable, input_data) + }; + + // We need to make sure to reset `allows_reentry` even on failure. + let result = try_call(); + + // Protection is on a per call basis. + self.top_frame_mut().allows_reentry = true; + + result + } + + fn delegate_call( + &mut self, + code_hash: CodeHash, + input_data: Vec, + ) -> Result { + let executable = E::from_storage(code_hash, self.gas_meter_mut())?; + let top_frame = self.top_frame_mut(); + let contract_info = top_frame.contract_info().clone(); + let account_id = top_frame.account_id.clone(); + let value = top_frame.value_transferred; + let executable = self.push_frame( + FrameArgs::Call { + dest: account_id, + cached_info: Some(contract_info), + delegated_call: Some(DelegatedCall { executable, caller: self.caller().clone() }), + }, + value, + Weight::zero(), + BalanceOf::::zero(), + self.is_read_only(), + )?; + self.run(executable, input_data) + } + + fn instantiate( + &mut self, + gas_limit: Weight, + deposit_limit: BalanceOf, + code_hash: CodeHash, + value: BalanceOf, + input_data: Vec, + salt: &[u8], + ) -> Result<(AccountIdOf, ExecReturnValue), ExecError> { + let executable = E::from_storage(code_hash, self.gas_meter_mut())?; + let sender = &self.top_frame().account_id; + let executable = self.push_frame( + FrameArgs::Instantiate { + sender: sender.clone(), + executable, + salt, + input_data: input_data.as_ref(), + }, + value, + gas_limit, + deposit_limit, + self.is_read_only(), + )?; + let account_id = self.top_frame().account_id.clone(); + self.run(executable, input_data).map(|ret| (account_id, ret)) + } + + fn terminate(&mut self, beneficiary: &AccountIdOf) -> DispatchResult { + if self.is_recursive() { + return Err(Error::::TerminatedWhileReentrant.into()) + } + let frame = self.top_frame_mut(); + let info = frame.terminate(); + frame.nested_storage.terminate(&info, beneficiary.clone()); + + info.queue_trie_for_deletion(); + ContractInfoOf::::remove(&frame.account_id); + Self::decrement_refcount(info.code_hash); + + for (code_hash, deposit) in info.delegate_dependencies() { + Self::decrement_refcount(*code_hash); + frame + .nested_storage + .charge_deposit(frame.account_id.clone(), StorageDeposit::Refund(*deposit)); + } + + Contracts::::deposit_event(Event::Terminated { + contract: frame.account_id.clone(), + beneficiary: beneficiary.clone(), + }); + Ok(()) + } + + fn transfer(&mut self, to: &T::AccountId, value: BalanceOf) -> DispatchResult { + Self::transfer(Preservation::Preserve, &self.top_frame().account_id, to, value) + } + + fn get_storage(&mut self, key: &Key) -> Option> { + self.top_frame_mut().contract_info().read(key) + } + + fn get_storage_size(&mut self, key: &Key) -> Option { + self.top_frame_mut().contract_info().size(key.into()) + } + + fn set_storage( + &mut self, + key: &Key, + value: Option>, + take_old: bool, + ) -> Result { + let frame = self.top_frame_mut(); + frame.contract_info.get(&frame.account_id).write( + key.into(), + value, + Some(&mut frame.nested_storage), + take_old, + ) + } + + fn get_transient_storage(&self, key: &Key) -> Option> { + self.transient_storage.read(self.address(), key) + } + + fn get_transient_storage_size(&self, key: &Key) -> Option { + self.transient_storage.read(self.address(), key).map(|value| value.len() as _) + } + + fn set_transient_storage( + &mut self, + key: &Key, + value: Option>, + take_old: bool, + ) -> Result { + let account_id = self.address().clone(); + self.transient_storage.write(&account_id, key, value, take_old) + } + + fn address(&self) -> &T::AccountId { + &self.top_frame().account_id + } + + fn caller(&self) -> Origin { + if let Some(caller) = &self.top_frame().delegate_caller { + caller.clone() + } else { + self.frames() + .nth(1) + .map(|f| Origin::from_account_id(f.account_id.clone())) + .unwrap_or(self.origin.clone()) + } + } + + fn is_contract(&self, address: &T::AccountId) -> bool { + ContractInfoOf::::contains_key(&address) + } + + fn code_hash(&self, address: &T::AccountId) -> Option> { + >::get(&address).map(|contract| contract.code_hash) + } + + fn own_code_hash(&mut self) -> &CodeHash { + &self.top_frame_mut().contract_info().code_hash + } + + fn caller_is_origin(&self) -> bool { + self.origin == self.caller() + } + + fn caller_is_root(&self) -> bool { + // if the caller isn't origin, then it can't be root. + self.caller_is_origin() && self.origin == Origin::Root + } + + fn balance(&self) -> BalanceOf { + T::Currency::reducible_balance( + &self.top_frame().account_id, + Preservation::Preserve, + Fortitude::Polite, + ) + } + + fn value_transferred(&self) -> BalanceOf { + self.top_frame().value_transferred + } + + fn now(&self) -> &MomentOf { + &self.timestamp + } + + fn minimum_balance(&self) -> BalanceOf { + T::Currency::minimum_balance() + } + + fn deposit_event(&mut self, topics: Vec, data: Vec) { + Contracts::::deposit_indexed_event( + topics, + Event::ContractEmitted { contract: self.top_frame().account_id.clone(), data }, + ); + } + + fn block_number(&self) -> BlockNumberFor { + self.block_number + } + + fn max_value_size(&self) -> u32 { + limits::PAYLOAD_BYTES + } + + fn get_weight_price(&self, weight: Weight) -> BalanceOf { + T::WeightPrice::convert(weight) + } + + fn gas_meter(&self) -> &GasMeter { + &self.top_frame().nested_gas + } + + fn gas_meter_mut(&mut self) -> &mut GasMeter { + &mut self.top_frame_mut().nested_gas + } + + fn charge_storage(&mut self, diff: &Diff) { + self.top_frame_mut().nested_storage.charge(diff) + } + + fn debug_buffer_enabled(&self) -> bool { + self.debug_message.is_some() + } + + fn append_debug_buffer(&mut self, msg: &str) -> bool { + if let Some(buffer) = &mut self.debug_message { + buffer + .try_extend(&mut msg.bytes()) + .map_err(|_| { + log::debug!( + target: LOG_TARGET, + "Debug buffer (of {} bytes) exhausted!", + limits::DEBUG_BUFFER_BYTES, + ) + }) + .ok(); + true + } else { + false + } + } + + fn call_runtime(&self, call: ::RuntimeCall) -> DispatchResultWithPostInfo { + let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.address().clone()).into(); + origin.add_filter(T::CallFilter::contains); + call.dispatch(origin) + } + + fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()> { + secp256k1_ecdsa_recover_compressed(signature, message_hash).map_err(|_| ()) + } + + fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool { + sp_io::crypto::sr25519_verify( + &SR25519Signature::from(*signature), + message, + &SR25519Public::from(*pub_key), + ) + } + + fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], ()> { + ECDSAPublic::from(*pk).to_eth_address() + } + + #[cfg(any(test, feature = "runtime-benchmarks"))] + fn contract_info(&mut self) -> &mut ContractInfo { + self.top_frame_mut().contract_info() + } + + #[cfg(feature = "runtime-benchmarks")] + fn transient_storage(&mut self) -> &mut TransientStorage { + &mut self.transient_storage + } + + fn set_code_hash(&mut self, hash: CodeHash) -> DispatchResult { + let frame = top_frame_mut!(self); + + let info = frame.contract_info(); + + let prev_hash = info.code_hash; + info.code_hash = hash; + + let code_info = CodeInfoOf::::get(hash).ok_or(Error::::CodeNotFound)?; + + let old_base_deposit = info.storage_base_deposit(); + let new_base_deposit = info.update_base_deposit(&code_info); + let deposit = StorageDeposit::Charge(new_base_deposit) + .saturating_sub(&StorageDeposit::Charge(old_base_deposit)); + + frame.nested_storage.charge_deposit(frame.account_id.clone(), deposit); + + Self::increment_refcount(hash)?; + Self::decrement_refcount(prev_hash); + Contracts::::deposit_event(Event::ContractCodeUpdated { + contract: frame.account_id.clone(), + new_code_hash: hash, + old_code_hash: prev_hash, + }); + Ok(()) + } + + fn increment_refcount(code_hash: CodeHash) -> DispatchResult { + >::mutate(code_hash, |existing| -> Result<(), DispatchError> { + if let Some(info) = existing { + *info.refcount_mut() = info.refcount().saturating_add(1); + Ok(()) + } else { + Err(Error::::CodeNotFound.into()) + } + }) + } + + fn decrement_refcount(code_hash: CodeHash) { + >::mutate(code_hash, |existing| { + if let Some(info) = existing { + *info.refcount_mut() = info.refcount().saturating_sub(1); + } + }); + } + + fn lock_delegate_dependency(&mut self, code_hash: CodeHash) -> DispatchResult { + let frame = self.top_frame_mut(); + let info = frame.contract_info.get(&frame.account_id); + ensure!(code_hash != info.code_hash, Error::::CannotAddSelfAsDelegateDependency); + + let code_info = CodeInfoOf::::get(code_hash).ok_or(Error::::CodeNotFound)?; + let deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_info.deposit()); + + info.lock_delegate_dependency(code_hash, deposit)?; + Self::increment_refcount(code_hash)?; + frame + .nested_storage + .charge_deposit(frame.account_id.clone(), StorageDeposit::Charge(deposit)); + Ok(()) + } + + fn unlock_delegate_dependency(&mut self, code_hash: &CodeHash) -> DispatchResult { + let frame = self.top_frame_mut(); + let info = frame.contract_info.get(&frame.account_id); + + let deposit = info.unlock_delegate_dependency(code_hash)?; + Self::decrement_refcount(*code_hash); + frame + .nested_storage + .charge_deposit(frame.account_id.clone(), StorageDeposit::Refund(deposit)); + Ok(()) + } + + fn locked_delegate_dependencies_count(&mut self) -> usize { + self.top_frame_mut().contract_info().delegate_dependencies_count() + } + + fn is_read_only(&self) -> bool { + self.top_frame().read_only + } +} + +mod sealing { + use super::*; + + pub trait Sealed {} + + impl<'a, T: Config, E> Sealed for Stack<'a, T, E> {} +} + +/// These tests exercise the executive layer. +/// +/// In these tests the VM/loader are mocked. Instead of dealing with wasm bytecode they use simple +/// closures. This allows you to tackle executive logic more thoroughly without writing a +/// wasm VM code. +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + exec::ExportedFunction::*, + gas::GasMeter, + test_utils::*, + tests::{ + test_utils::{get_balance, place_contract, set_balance}, + ExtBuilder, RuntimeCall, RuntimeEvent as MetaEvent, Test, TestFilter, + }, + Error, + }; + use assert_matches::assert_matches; + use codec::{Decode, Encode}; + use frame_support::{assert_err, assert_ok, parameter_types}; + use frame_system::{EventRecord, Phase}; + use pallet_revive_uapi::ReturnFlags; + use pretty_assertions::assert_eq; + use sp_runtime::{traits::Hash, DispatchError}; + use std::{cell::RefCell, collections::hash_map::HashMap, rc::Rc}; + + type System = frame_system::Pallet; + + type MockStack<'a> = Stack<'a, Test, MockExecutable>; + + parameter_types! { + static Loader: MockLoader = MockLoader::default(); + } + + fn events() -> Vec> { + System::events() + .into_iter() + .filter_map(|meta| match meta.event { + MetaEvent::Contracts(contract_event) => Some(contract_event), + _ => None, + }) + .collect() + } + + struct MockCtx<'a> { + ext: &'a mut MockStack<'a>, + input_data: Vec, + } + + #[derive(Clone)] + struct MockExecutable { + func: Rc Fn(MockCtx<'a>, &Self) -> ExecResult + 'static>, + func_type: ExportedFunction, + code_hash: CodeHash, + code_info: CodeInfo, + } + + #[derive(Default, Clone)] + pub struct MockLoader { + map: HashMap, MockExecutable>, + counter: u64, + } + + impl MockLoader { + fn code_hashes() -> Vec> { + Loader::get().map.keys().copied().collect() + } + + fn insert( + func_type: ExportedFunction, + f: impl Fn(MockCtx, &MockExecutable) -> ExecResult + 'static, + ) -> CodeHash { + Loader::mutate(|loader| { + // Generate code hashes as monotonically increasing values. + let hash = ::Hash::from_low_u64_be(loader.counter); + loader.counter += 1; + loader.map.insert( + hash, + MockExecutable { + func: Rc::new(f), + func_type, + code_hash: hash, + code_info: CodeInfo::::new(ALICE), + }, + ); + hash + }) + } + } + + impl Executable for MockExecutable { + fn from_storage( + code_hash: CodeHash, + _gas_meter: &mut GasMeter, + ) -> Result { + Loader::mutate(|loader| { + loader.map.get(&code_hash).cloned().ok_or(Error::::CodeNotFound.into()) + }) + } + + fn execute>( + self, + ext: &mut E, + function: ExportedFunction, + input_data: Vec, + ) -> ExecResult { + if let Constructor = function { + E::increment_refcount(self.code_hash).unwrap(); + } + // # Safety + // + // We know that we **always** call execute with a `MockStack` in this test. + // + // # Note + // + // The transmute is necessary because `execute` has to be generic over all + // `E: Ext`. However, `MockExecutable` can't be generic over `E` as it would + // constitute a cycle. + let ext = unsafe { mem::transmute(ext) }; + if function == self.func_type { + (self.func)(MockCtx { ext, input_data }, &self) + } else { + exec_success() + } + } + + fn code_hash(&self) -> &CodeHash { + &self.code_hash + } + + fn code_info(&self) -> &CodeInfo { + &self.code_info + } + } + + fn exec_success() -> ExecResult { + Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }) + } + + fn exec_trapped() -> ExecResult { + Err(ExecError { error: >::ContractTrapped.into(), origin: ErrorOrigin::Callee }) + } + + #[test] + fn it_works() { + parameter_types! { + static TestData: Vec = vec![0]; + } + + let value = Default::default(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let exec_ch = MockLoader::insert(Call, |_ctx, _executable| { + TestData::mutate(|data| data.push(1)); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, exec_ch); + let mut storage_meter = + storage::meter::Meter::new(&Origin::from_account_id(ALICE), 0, value).unwrap(); + + assert_matches!( + MockStack::run_call( + Origin::from_account_id(ALICE), + BOB, + &mut gas_meter, + &mut storage_meter, + value, + vec![], + None, + ), + Ok(_) + ); + }); + + assert_eq!(TestData::get(), vec![0, 1]); + } + + #[test] + fn transfer_works() { + // This test verifies that a contract is able to transfer + // some funds to another account. + let origin = ALICE; + let dest = BOB; + + ExtBuilder::default().build().execute_with(|| { + set_balance(&origin, 100); + set_balance(&dest, 0); + + MockStack::transfer(Preservation::Preserve, &origin, &dest, 55).unwrap(); + + assert_eq!(get_balance(&origin), 45); + assert_eq!(get_balance(&dest), 55); + }); + } + + #[test] + fn correct_transfer_on_call() { + let origin = ALICE; + let dest = BOB; + let value = 55; + + let success_ch = MockLoader::insert(Call, move |ctx, _| { + assert_eq!(ctx.ext.value_transferred(), value); + Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }) + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&dest, success_ch); + set_balance(&origin, 100); + let balance = get_balance(&dest); + let contract_origin = Origin::from_account_id(origin.clone()); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, value).unwrap(); + + let _ = MockStack::run_call( + contract_origin.clone(), + dest.clone(), + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + value, + vec![], + None, + ) + .unwrap(); + + assert_eq!(get_balance(&origin), 100 - value); + assert_eq!(get_balance(&dest), balance + value); + }); + } + + #[test] + fn correct_transfer_on_delegate_call() { + let origin = ALICE; + let dest = BOB; + let value = 35; + + let success_ch = MockLoader::insert(Call, move |ctx, _| { + assert_eq!(ctx.ext.value_transferred(), value); + Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }) + }); + + let delegate_ch = MockLoader::insert(Call, move |ctx, _| { + assert_eq!(ctx.ext.value_transferred(), value); + let _ = ctx.ext.delegate_call(success_ch, Vec::new())?; + Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }) + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&dest, delegate_ch); + set_balance(&origin, 100); + let balance = get_balance(&dest); + let contract_origin = Origin::from_account_id(origin.clone()); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 55).unwrap(); + + let _ = MockStack::run_call( + contract_origin.clone(), + dest.clone(), + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + value, + vec![], + None, + ) + .unwrap(); + + assert_eq!(get_balance(&origin), 100 - value); + assert_eq!(get_balance(&dest), balance + value); + }); + } + + #[test] + fn changes_are_reverted_on_failing_call() { + // This test verifies that changes are reverted on a call which fails (or equally, returns + // a non-zero status code). + let origin = ALICE; + let dest = BOB; + + let return_ch = MockLoader::insert(Call, |_, _| { + Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: Vec::new() }) + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&dest, return_ch); + set_balance(&origin, 100); + let balance = get_balance(&dest); + let contract_origin = Origin::from_account_id(origin.clone()); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 55).unwrap(); + + let output = MockStack::run_call( + contract_origin.clone(), + dest.clone(), + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 55, + vec![], + None, + ) + .unwrap(); + + assert!(output.did_revert()); + assert_eq!(get_balance(&origin), 100); + assert_eq!(get_balance(&dest), balance); + }); + } + + #[test] + fn balance_too_low() { + // This test verifies that a contract can't send value if it's + // balance is too low. + let origin = ALICE; + let dest = BOB; + + ExtBuilder::default().build().execute_with(|| { + set_balance(&origin, 0); + + let result = MockStack::transfer(Preservation::Preserve, &origin, &dest, 100); + + assert_eq!(result, Err(Error::::TransferFailed.into())); + assert_eq!(get_balance(&origin), 0); + assert_eq!(get_balance(&dest), 0); + }); + } + + #[test] + fn output_is_returned_on_success() { + // Verifies that if a contract returns data with a successful exit status, this data + // is returned from the execution context. + let origin = ALICE; + let dest = BOB; + let return_ch = MockLoader::insert(Call, |_, _| { + Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![1, 2, 3, 4] }) + }); + + ExtBuilder::default().build().execute_with(|| { + let contract_origin = Origin::from_account_id(origin); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + place_contract(&BOB, return_ch); + + let result = MockStack::run_call( + contract_origin, + dest, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ); + + let output = result.unwrap(); + assert!(!output.did_revert()); + assert_eq!(output.data, vec![1, 2, 3, 4]); + }); + } + + #[test] + fn output_is_returned_on_failure() { + // Verifies that if a contract returns data with a failing exit status, this data + // is returned from the execution context. + let origin = ALICE; + let dest = BOB; + let return_ch = MockLoader::insert(Call, |_, _| { + Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![1, 2, 3, 4] }) + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, return_ch); + let contract_origin = Origin::from_account_id(origin); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + + let result = MockStack::run_call( + contract_origin, + dest, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ); + + let output = result.unwrap(); + assert!(output.did_revert()); + assert_eq!(output.data, vec![1, 2, 3, 4]); + }); + } + + #[test] + fn input_data_to_call() { + let input_data_ch = MockLoader::insert(Call, |ctx, _| { + assert_eq!(ctx.input_data, &[1, 2, 3, 4]); + exec_success() + }); + + // This one tests passing the input data into a contract via call. + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, input_data_ch); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![1, 2, 3, 4], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn input_data_to_instantiate() { + let input_data_ch = MockLoader::insert(Constructor, |ctx, _| { + assert_eq!(ctx.input_data, &[1, 2, 3, 4]); + exec_success() + }); + + // This one tests passing the input data into a contract via instantiate. + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let executable = + MockExecutable::from_storage(input_data_ch, &mut gas_meter).unwrap(); + set_balance(&ALICE, min_balance * 10_000); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new( + &contract_origin, + deposit_limit::(), + min_balance, + ) + .unwrap(); + + let result = MockStack::run_instantiate( + ALICE, + executable, + &mut gas_meter, + &mut storage_meter, + min_balance, + vec![1, 2, 3, 4], + &[], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn max_depth() { + // This test verifies that when we reach the maximal depth creation of an + // yet another context fails. + parameter_types! { + static ReachedBottom: bool = false; + } + let value = Default::default(); + let recurse_ch = MockLoader::insert(Call, |ctx, _| { + // Try to call into yourself. + let r = ctx.ext.call( + Weight::zero(), + BalanceOf::::zero(), + BOB, + 0, + vec![], + true, + false, + ); + + ReachedBottom::mutate(|reached_bottom| { + if !*reached_bottom { + // We are first time here, it means we just reached bottom. + // Verify that we've got proper error and set `reached_bottom`. + assert_eq!(r, Err(Error::::MaxCallDepthReached.into())); + *reached_bottom = true; + } else { + // We just unwinding stack here. + assert_matches!(r, Ok(_)); + } + }); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + set_balance(&BOB, 1); + place_contract(&BOB, recurse_ch); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, value).unwrap(); + + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + value, + vec![], + None, + ); + + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn caller_returns_proper_values() { + let origin = ALICE; + let dest = BOB; + + parameter_types! { + static WitnessedCallerBob: Option> = None; + static WitnessedCallerCharlie: Option> = None; + } + + let bob_ch = MockLoader::insert(Call, |ctx, _| { + // Record the caller for bob. + WitnessedCallerBob::mutate(|caller| { + *caller = Some(ctx.ext.caller().account_id().unwrap().clone()) + }); + + // Call into CHARLIE contract. + assert_matches!( + ctx.ext.call( + Weight::zero(), + BalanceOf::::zero(), + CHARLIE, + 0, + vec![], + true, + false + ), + Ok(_) + ); + exec_success() + }); + let charlie_ch = MockLoader::insert(Call, |ctx, _| { + // Record the caller for charlie. + WitnessedCallerCharlie::mutate(|caller| { + *caller = Some(ctx.ext.caller().account_id().unwrap().clone()) + }); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&dest, bob_ch); + place_contract(&CHARLIE, charlie_ch); + let contract_origin = Origin::from_account_id(origin.clone()); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + + let result = MockStack::run_call( + contract_origin.clone(), + dest.clone(), + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ); + + assert_matches!(result, Ok(_)); + }); + + assert_eq!(WitnessedCallerBob::get(), Some(origin)); + assert_eq!(WitnessedCallerCharlie::get(), Some(dest)); + } + + #[test] + fn is_contract_returns_proper_values() { + let bob_ch = MockLoader::insert(Call, |ctx, _| { + // Verify that BOB is a contract + assert!(ctx.ext.is_contract(&BOB)); + // Verify that ALICE is not a contract + assert!(!ctx.ext.is_contract(&ALICE)); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, bob_ch); + + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn code_hash_returns_proper_values() { + let code_bob = MockLoader::insert(Call, |ctx, _| { + // ALICE is not a contract and hence they do not have a code_hash + assert!(ctx.ext.code_hash(&ALICE).is_none()); + // BOB is a contract and hence it has a code_hash + assert!(ctx.ext.code_hash(&BOB).is_some()); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + // ALICE (not contract) -> BOB (contract) + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn own_code_hash_returns_proper_values() { + let bob_ch = MockLoader::insert(Call, |ctx, _| { + let code_hash = ctx.ext.code_hash(&BOB).unwrap(); + assert_eq!(*ctx.ext.own_code_hash(), code_hash); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, bob_ch); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + // ALICE (not contract) -> BOB (contract) + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn caller_is_origin_returns_proper_values() { + let code_charlie = MockLoader::insert(Call, |ctx, _| { + // BOB is not the origin of the stack call + assert!(!ctx.ext.caller_is_origin()); + exec_success() + }); + + let code_bob = MockLoader::insert(Call, |ctx, _| { + // ALICE is the origin of the call stack + assert!(ctx.ext.caller_is_origin()); + // BOB calls CHARLIE + ctx.ext + .call(Weight::zero(), BalanceOf::::zero(), CHARLIE, 0, vec![], true, false) + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + // ALICE -> BOB (caller is origin) -> CHARLIE (caller is not origin) + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn root_caller_succeeds() { + let code_bob = MockLoader::insert(Call, |ctx, _| { + // root is the origin of the call stack. + assert!(ctx.ext.caller_is_root()); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + let contract_origin = Origin::Root; + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + // root -> BOB (caller is root) + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn root_caller_does_not_succeed_when_value_not_zero() { + let code_bob = MockLoader::insert(Call, |ctx, _| { + // root is the origin of the call stack. + assert!(ctx.ext.caller_is_root()); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + let contract_origin = Origin::Root; + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + // root -> BOB (caller is root) + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 1, + vec![0], + None, + ); + assert_matches!(result, Err(_)); + }); + } + + #[test] + fn root_caller_succeeds_with_consecutive_calls() { + let code_charlie = MockLoader::insert(Call, |ctx, _| { + // BOB is not root, even though the origin is root. + assert!(!ctx.ext.caller_is_root()); + exec_success() + }); + + let code_bob = MockLoader::insert(Call, |ctx, _| { + // root is the origin of the call stack. + assert!(ctx.ext.caller_is_root()); + // BOB calls CHARLIE. + ctx.ext + .call(Weight::zero(), BalanceOf::::zero(), CHARLIE, 0, vec![], true, false) + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let contract_origin = Origin::Root; + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + // root -> BOB (caller is root) -> CHARLIE (caller is not root) + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn address_returns_proper_values() { + let bob_ch = MockLoader::insert(Call, |ctx, _| { + // Verify that address matches BOB. + assert_eq!(*ctx.ext.address(), BOB); + + // Call into charlie contract. + assert_matches!( + ctx.ext.call( + Weight::zero(), + BalanceOf::::zero(), + CHARLIE, + 0, + vec![], + true, + false + ), + Ok(_) + ); + exec_success() + }); + let charlie_ch = MockLoader::insert(Call, |ctx, _| { + assert_eq!(*ctx.ext.address(), CHARLIE); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, bob_ch); + place_contract(&CHARLIE, charlie_ch); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ); + + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn refuse_instantiate_with_value_below_existential_deposit() { + let dummy_ch = MockLoader::insert(Constructor, |_, _| exec_success()); + + ExtBuilder::default().existential_deposit(15).build().execute_with(|| { + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap(); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + + assert_matches!( + MockStack::run_instantiate( + ALICE, + executable, + &mut gas_meter, + &mut storage_meter, + 0, // <- zero value + vec![], + &[], + None, + ), + Err(_) + ); + }); + } + + #[test] + fn instantiation_work_with_success_output() { + let dummy_ch = MockLoader::insert(Constructor, |_, _| { + Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![80, 65, 83, 83] }) + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap(); + set_balance(&ALICE, min_balance * 1000); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, min_balance * 100, min_balance) + .unwrap(); + + let instantiated_contract_address = assert_matches!( + MockStack::run_instantiate( + ALICE, + executable, + &mut gas_meter, + &mut storage_meter, + + min_balance, + vec![], + &[], + None, + ), + Ok((address, ref output)) if output.data == vec![80, 65, 83, 83] => address + ); + + // Check that the newly created account has the expected code hash and + // there are instantiation event. + assert_eq!( + ContractInfo::::load_code_hash(&instantiated_contract_address).unwrap(), + dummy_ch + ); + assert_eq!( + &events(), + &[Event::Instantiated { + deployer: ALICE, + contract: instantiated_contract_address + }] + ); + }); + } + + #[test] + fn instantiation_fails_with_failing_output() { + let dummy_ch = MockLoader::insert(Constructor, |_, _| { + Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![70, 65, 73, 76] }) + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap(); + set_balance(&ALICE, min_balance * 1000); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, min_balance * 100, min_balance) + .unwrap(); + + let instantiated_contract_address = assert_matches!( + MockStack::run_instantiate( + ALICE, + executable, + &mut gas_meter, + &mut storage_meter, + + min_balance, + vec![], + &[], + None, + ), + Ok((address, ref output)) if output.data == vec![70, 65, 73, 76] => address + ); + + // Check that the account has not been created. + assert!( + ContractInfo::::load_code_hash(&instantiated_contract_address).is_none() + ); + assert!(events().is_empty()); + }); + } + + #[test] + fn instantiation_from_contract() { + let dummy_ch = MockLoader::insert(Call, |_, _| exec_success()); + let instantiated_contract_address = Rc::new(RefCell::new(None::>)); + let instantiator_ch = MockLoader::insert(Call, { + let instantiated_contract_address = Rc::clone(&instantiated_contract_address); + move |ctx, _| { + // Instantiate a contract and save it's address in `instantiated_contract_address`. + let (address, output) = ctx + .ext + .instantiate( + Weight::zero(), + BalanceOf::::zero(), + dummy_ch, + ::Currency::minimum_balance(), + vec![], + &[48, 49, 50], + ) + .unwrap(); + + *instantiated_contract_address.borrow_mut() = address.into(); + Ok(output) + } + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + set_balance(&ALICE, min_balance * 100); + place_contract(&BOB, instantiator_ch); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new( + &contract_origin, + min_balance * 10, + min_balance * 10, + ) + .unwrap(); + + assert_matches!( + MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + min_balance * 10, + vec![], + None, + ), + Ok(_) + ); + + let instantiated_contract_address = + instantiated_contract_address.borrow().as_ref().unwrap().clone(); + + // Check that the newly created account has the expected code hash and + // there are instantiation event. + assert_eq!( + ContractInfo::::load_code_hash(&instantiated_contract_address).unwrap(), + dummy_ch + ); + assert_eq!( + &events(), + &[ + Event::Instantiated { + deployer: BOB, + contract: instantiated_contract_address + }, + Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB }, + ] + ); + }); + } + + #[test] + fn instantiation_traps() { + let dummy_ch = MockLoader::insert(Constructor, |_, _| Err("It's a trap!".into())); + let instantiator_ch = MockLoader::insert(Call, { + move |ctx, _| { + // Instantiate a contract and save it's address in `instantiated_contract_address`. + assert_matches!( + ctx.ext.instantiate( + Weight::zero(), + BalanceOf::::zero(), + dummy_ch, + ::Currency::minimum_balance(), + vec![], + &[], + ), + Err(ExecError { + error: DispatchError::Other("It's a trap!"), + origin: ErrorOrigin::Callee, + }) + ); + + exec_success() + } + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + set_balance(&ALICE, 1000); + set_balance(&BOB, 100); + place_contract(&BOB, instantiator_ch); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, 200, 0).unwrap(); + + assert_matches!( + MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ), + Ok(_) + ); + + // The contract wasn't instantiated so we don't expect to see an instantiation + // event here. + assert_eq!( + &events(), + &[Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB },] + ); + }); + } + + #[test] + fn termination_from_instantiate_fails() { + let terminate_ch = MockLoader::insert(Constructor, |ctx, _| { + ctx.ext.terminate(&ALICE).unwrap(); + exec_success() + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let executable = + MockExecutable::from_storage(terminate_ch, &mut gas_meter).unwrap(); + set_balance(&ALICE, 10_000); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, deposit_limit::(), 100) + .unwrap(); + + assert_eq!( + MockStack::run_instantiate( + ALICE, + executable, + &mut gas_meter, + &mut storage_meter, + 100, + vec![], + &[], + None, + ), + Err(Error::::TerminatedInConstructor.into()) + ); + + assert_eq!(&events(), &[]); + }); + } + + #[test] + fn in_memory_changes_not_discarded() { + // Call stack: BOB -> CHARLIE (trap) -> BOB' (success) + // This tests verifies some edge case of the contract info cache: + // We change some value in our contract info before calling into a contract + // that calls into ourself. This triggers a case where BOBs contract info + // is written to storage and invalidated by the successful execution of BOB'. + // The trap of CHARLIE reverts the storage changes to BOB. When the root BOB regains + // control it reloads its contract info from storage. We check that changes that + // are made before calling into CHARLIE are not discarded. + let code_bob = MockLoader::insert(Call, |ctx, _| { + if ctx.input_data[0] == 0 { + let info = ctx.ext.contract_info(); + assert_eq!(info.storage_byte_deposit, 0); + info.storage_byte_deposit = 42; + assert_eq!( + ctx.ext.call( + Weight::zero(), + BalanceOf::::zero(), + CHARLIE, + 0, + vec![], + true, + false + ), + exec_trapped() + ); + assert_eq!(ctx.ext.contract_info().storage_byte_deposit, 42); + } + exec_success() + }); + let code_charlie = MockLoader::insert(Call, |ctx, _| { + assert!(ctx + .ext + .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![99], true, false) + .is_ok()); + exec_trapped() + }); + + // This one tests passing the input data into a contract via call. + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn recursive_call_during_constructor_fails() { + let code = MockLoader::insert(Constructor, |ctx, _| { + assert_matches!( + ctx.ext.call(Weight::zero(), BalanceOf::::zero(), ctx.ext.address().clone(), 0, vec![], true, false), + Err(ExecError{error, ..}) if error == >::ContractNotFound.into() + ); + exec_success() + }); + + // This one tests passing the input data into a contract via instantiate. + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let executable = MockExecutable::from_storage(code, &mut gas_meter).unwrap(); + set_balance(&ALICE, min_balance * 10_000); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new( + &contract_origin, + deposit_limit::(), + min_balance, + ) + .unwrap(); + + let result = MockStack::run_instantiate( + ALICE, + executable, + &mut gas_meter, + &mut storage_meter, + min_balance, + vec![], + &[], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn printing_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + ctx.ext.append_debug_buffer("This is a test"); + ctx.ext.append_debug_buffer("More text"); + exec_success() + }); + + let mut debug_buffer = DebugBuffer::try_from(Vec::new()).unwrap(); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 10); + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + MockStack::run_call( + contract_origin, + BOB, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + Some(&mut debug_buffer), + ) + .unwrap(); + }); + + assert_eq!(&String::from_utf8(debug_buffer.to_vec()).unwrap(), "This is a testMore text"); + } + + #[test] + fn printing_works_on_fail() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + ctx.ext.append_debug_buffer("This is a test"); + ctx.ext.append_debug_buffer("More text"); + exec_trapped() + }); + + let mut debug_buffer = DebugBuffer::try_from(Vec::new()).unwrap(); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 10); + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let result = MockStack::run_call( + contract_origin, + BOB, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + Some(&mut debug_buffer), + ); + assert!(result.is_err()); + }); + + assert_eq!(&String::from_utf8(debug_buffer.to_vec()).unwrap(), "This is a testMore text"); + } + + #[test] + fn debug_buffer_is_limited() { + let code_hash = MockLoader::insert(Call, move |ctx, _| { + ctx.ext.append_debug_buffer("overflowing bytes"); + exec_success() + }); + + // Pre-fill the buffer almost up to its limit, leaving not enough space to the message + let debug_buf_before = DebugBuffer::try_from(vec![0u8; DebugBuffer::bound() - 5]).unwrap(); + let mut debug_buf_after = debug_buf_before.clone(); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 10); + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + MockStack::run_call( + contract_origin, + BOB, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + Some(&mut debug_buf_after), + ) + .unwrap(); + assert_eq!(debug_buf_before, debug_buf_after); + }); + } + + #[test] + fn call_reentry_direct_recursion() { + // call the contract passed as input with disabled reentry + let code_bob = MockLoader::insert(Call, |ctx, _| { + let dest = Decode::decode(&mut ctx.input_data.as_ref()).unwrap(); + ctx.ext + .call(Weight::zero(), BalanceOf::::zero(), dest, 0, vec![], false, false) + }); + + let code_charlie = MockLoader::insert(Call, |_, _| exec_success()); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + + // Calling another contract should succeed + assert_ok!(MockStack::run_call( + contract_origin.clone(), + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + CHARLIE.encode(), + None, + )); + + // Calling into oneself fails + assert_err!( + MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + BOB.encode(), + None, + ) + .map_err(|e| e.error), + >::ReentranceDenied, + ); + }); + } + + #[test] + fn call_deny_reentry() { + let code_bob = MockLoader::insert(Call, |ctx, _| { + if ctx.input_data[0] == 0 { + ctx.ext.call( + Weight::zero(), + BalanceOf::::zero(), + CHARLIE, + 0, + vec![], + false, + false, + ) + } else { + exec_success() + } + }); + + // call BOB with input set to '1' + let code_charlie = MockLoader::insert(Call, |ctx, _| { + ctx.ext + .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![1], true, false) + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + + // BOB -> CHARLIE -> BOB fails as BOB denies reentry. + assert_err!( + MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ) + .map_err(|e| e.error), + >::ReentranceDenied, + ); + }); + } + + #[test] + fn call_runtime_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + let call = RuntimeCall::System(frame_system::Call::remark_with_event { + remark: b"Hello World".to_vec(), + }); + ctx.ext.call_runtime(call).unwrap(); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 10); + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + System::reset_events(); + MockStack::run_call( + contract_origin, + BOB, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap(); + + let remark_hash = ::Hashing::hash(b"Hello World"); + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::System(frame_system::Event::Remarked { + sender: BOB, + hash: remark_hash + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: BOB, + }), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn call_runtime_filter() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + use frame_system::Call as SysCall; + use pallet_balances::Call as BalanceCall; + use pallet_utility::Call as UtilCall; + + // remark should still be allowed + let allowed_call = + RuntimeCall::System(SysCall::remark_with_event { remark: b"Hello".to_vec() }); + + // transfers are disallowed by the `TestFiler` (see below) + let forbidden_call = RuntimeCall::Balances(BalanceCall::transfer_allow_death { + dest: CHARLIE, + value: 22, + }); + + // simple cases: direct call + assert_err!( + ctx.ext.call_runtime(forbidden_call.clone()), + frame_system::Error::::CallFiltered + ); + + // as part of a patch: return is OK (but it interrupted the batch) + assert_ok!(ctx.ext.call_runtime(RuntimeCall::Utility(UtilCall::batch { + calls: vec![allowed_call.clone(), forbidden_call, allowed_call] + })),); + + // the transfer wasn't performed + assert_eq!(get_balance(&CHARLIE), 0); + + exec_success() + }); + + TestFilter::set_filter(|call| match call { + RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { .. }) => false, + _ => true, + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 10); + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + System::reset_events(); + MockStack::run_call( + contract_origin, + BOB, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap(); + + let remark_hash = ::Hashing::hash(b"Hello"); + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::System(frame_system::Event::Remarked { + sender: BOB, + hash: remark_hash + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::Utility(pallet_utility::Event::ItemCompleted), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::Utility(pallet_utility::Event::BatchInterrupted { + index: 1, + error: frame_system::Error::::CallFiltered.into() + },), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: BOB, + }), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn nonce() { + let fail_code = MockLoader::insert(Constructor, |_, _| exec_trapped()); + let success_code = MockLoader::insert(Constructor, |_, _| exec_success()); + let succ_fail_code = MockLoader::insert(Constructor, move |ctx, _| { + ctx.ext + .instantiate( + Weight::zero(), + BalanceOf::::zero(), + fail_code, + ctx.ext.minimum_balance() * 100, + vec![], + &[], + ) + .ok(); + exec_success() + }); + let succ_succ_code = MockLoader::insert(Constructor, move |ctx, _| { + let alice_nonce = System::account_nonce(&ALICE); + assert_eq!(System::account_nonce(ctx.ext.address()), 0); + assert_eq!(ctx.ext.caller().account_id().unwrap(), &ALICE); + let (account_id, _) = ctx + .ext + .instantiate( + Weight::zero(), + BalanceOf::::zero(), + success_code, + ctx.ext.minimum_balance() * 100, + vec![], + &[], + ) + .unwrap(); + + assert_eq!(System::account_nonce(&ALICE), alice_nonce); + assert_eq!(System::account_nonce(ctx.ext.address()), 1); + assert_eq!(System::account_nonce(&account_id), 0); + + // a plain call should not influence the account counter + ctx.ext + .call( + Weight::zero(), + BalanceOf::::zero(), + account_id.clone(), + 0, + vec![], + false, + false, + ) + .unwrap(); + + assert_eq!(System::account_nonce(ALICE), alice_nonce); + assert_eq!(System::account_nonce(ctx.ext.address()), 1); + assert_eq!(System::account_nonce(&account_id), 0); + + exec_success() + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let fail_executable = + MockExecutable::from_storage(fail_code, &mut gas_meter).unwrap(); + let success_executable = + MockExecutable::from_storage(success_code, &mut gas_meter).unwrap(); + let succ_fail_executable = + MockExecutable::from_storage(succ_fail_code, &mut gas_meter).unwrap(); + let succ_succ_executable = + MockExecutable::from_storage(succ_succ_code, &mut gas_meter).unwrap(); + set_balance(&ALICE, min_balance * 10_000); + set_balance(&BOB, min_balance * 10_000); + let contract_origin = Origin::from_account_id(BOB); + let mut storage_meter = storage::meter::Meter::new( + &contract_origin, + deposit_limit::(), + min_balance * 100, + ) + .unwrap(); + + // fail should not increment + MockStack::run_instantiate( + ALICE, + fail_executable, + &mut gas_meter, + &mut storage_meter, + min_balance * 100, + vec![], + &[], + None, + ) + .ok(); + assert_eq!(System::account_nonce(&ALICE), 0); + + assert_ok!(MockStack::run_instantiate( + ALICE, + success_executable, + &mut gas_meter, + &mut storage_meter, + min_balance * 100, + vec![], + &[], + None, + )); + assert_eq!(System::account_nonce(&ALICE), 1); + + assert_ok!(MockStack::run_instantiate( + ALICE, + succ_fail_executable, + &mut gas_meter, + &mut storage_meter, + min_balance * 200, + vec![], + &[], + None, + )); + assert_eq!(System::account_nonce(&ALICE), 2); + + assert_ok!(MockStack::run_instantiate( + ALICE, + succ_succ_executable, + &mut gas_meter, + &mut storage_meter, + min_balance * 200, + vec![], + &[], + None, + )); + assert_eq!(System::account_nonce(&ALICE), 3); + }); + } + + #[test] + fn set_storage_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + // Write + assert_eq!( + ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![4, 5, 6]), true), + Ok(WriteOutcome::New) + ); + assert_eq!(ctx.ext.set_storage(&Key::Fix([3; 32]), None, false), Ok(WriteOutcome::New)); + assert_eq!(ctx.ext.set_storage(&Key::Fix([4; 32]), None, true), Ok(WriteOutcome::New)); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([5; 32]), Some(vec![]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([6; 32]), Some(vec![]), true), + Ok(WriteOutcome::New) + ); + + // Overwrite + assert_eq!( + ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![42]), false), + Ok(WriteOutcome::Overwritten(3)) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![48]), true), + Ok(WriteOutcome::Taken(vec![4, 5, 6])) + ); + assert_eq!(ctx.ext.set_storage(&Key::Fix([3; 32]), None, false), Ok(WriteOutcome::New)); + assert_eq!(ctx.ext.set_storage(&Key::Fix([4; 32]), None, true), Ok(WriteOutcome::New)); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([5; 32]), Some(vec![]), false), + Ok(WriteOutcome::Overwritten(0)) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([6; 32]), Some(vec![]), true), + Ok(WriteOutcome::Taken(vec![])) + ); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 1000); + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + contract_origin, + BOB, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn set_storage_varsized_key_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + // Write + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([1; 64].to_vec()).unwrap(), + Some(vec![1, 2, 3]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([2; 19].to_vec()).unwrap(), + Some(vec![4, 5, 6]), + true + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::try_from_var([3; 19].to_vec()).unwrap(), None, false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::try_from_var([4; 64].to_vec()).unwrap(), None, true), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([5; 30].to_vec()).unwrap(), + Some(vec![]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([6; 128].to_vec()).unwrap(), + Some(vec![]), + true + ), + Ok(WriteOutcome::New) + ); + + // Overwrite + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([1; 64].to_vec()).unwrap(), + Some(vec![42, 43, 44]), + false + ), + Ok(WriteOutcome::Overwritten(3)) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([2; 19].to_vec()).unwrap(), + Some(vec![48]), + true + ), + Ok(WriteOutcome::Taken(vec![4, 5, 6])) + ); + assert_eq!( + ctx.ext.set_storage(&Key::try_from_var([3; 19].to_vec()).unwrap(), None, false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::try_from_var([4; 64].to_vec()).unwrap(), None, true), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([5; 30].to_vec()).unwrap(), + Some(vec![]), + false + ), + Ok(WriteOutcome::Overwritten(0)) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([6; 128].to_vec()).unwrap(), + Some(vec![]), + true + ), + Ok(WriteOutcome::Taken(vec![])) + ); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 1000); + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + contract_origin, + BOB, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn get_storage_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + assert_eq!( + ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![]), false), + Ok(WriteOutcome::New) + ); + assert_eq!(ctx.ext.get_storage(&Key::Fix([1; 32])), Some(vec![1, 2, 3])); + assert_eq!(ctx.ext.get_storage(&Key::Fix([2; 32])), Some(vec![])); + assert_eq!(ctx.ext.get_storage(&Key::Fix([3; 32])), None); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 1000); + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + contract_origin, + BOB, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn get_storage_size_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + assert_eq!( + ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![]), false), + Ok(WriteOutcome::New) + ); + assert_eq!(ctx.ext.get_storage_size(&Key::Fix([1; 32])), Some(3)); + assert_eq!(ctx.ext.get_storage_size(&Key::Fix([2; 32])), Some(0)); + assert_eq!(ctx.ext.get_storage_size(&Key::Fix([3; 32])), None); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 1000); + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + contract_origin, + BOB, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn get_storage_varsized_key_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([1; 19].to_vec()).unwrap(), + Some(vec![1, 2, 3]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([2; 16].to_vec()).unwrap(), + Some(vec![]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.get_storage(&Key::try_from_var([1; 19].to_vec()).unwrap()), + Some(vec![1, 2, 3]) + ); + assert_eq!( + ctx.ext.get_storage(&Key::try_from_var([2; 16].to_vec()).unwrap()), + Some(vec![]) + ); + assert_eq!(ctx.ext.get_storage(&Key::try_from_var([3; 8].to_vec()).unwrap()), None); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 1000); + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + contract_origin, + BOB, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn get_storage_size_varsized_key_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([1; 19].to_vec()).unwrap(), + Some(vec![1, 2, 3]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([2; 16].to_vec()).unwrap(), + Some(vec![]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.get_storage_size(&Key::try_from_var([1; 19].to_vec()).unwrap()), + Some(3) + ); + assert_eq!( + ctx.ext.get_storage_size(&Key::try_from_var([2; 16].to_vec()).unwrap()), + Some(0) + ); + assert_eq!( + ctx.ext.get_storage_size(&Key::try_from_var([3; 8].to_vec()).unwrap()), + None + ); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 1000); + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + contract_origin, + BOB, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn set_transient_storage_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + // Write + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([2; 32]), Some(vec![4, 5, 6]), true), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([3; 32]), None, false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([4; 32]), None, true), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([5; 32]), Some(vec![]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([6; 32]), Some(vec![]), true), + Ok(WriteOutcome::New) + ); + + // Overwrite + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([1; 32]), Some(vec![42]), false), + Ok(WriteOutcome::Overwritten(3)) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([2; 32]), Some(vec![48]), true), + Ok(WriteOutcome::Taken(vec![4, 5, 6])) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([3; 32]), None, false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([4; 32]), None, true), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([5; 32]), Some(vec![]), false), + Ok(WriteOutcome::Overwritten(0)) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([6; 32]), Some(vec![]), true), + Ok(WriteOutcome::Taken(vec![])) + ); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn get_transient_storage_works() { + // Call stack: BOB -> CHARLIE(success) -> BOB' (success) + let storage_key_1 = &Key::Fix([1; 32]); + let storage_key_2 = &Key::Fix([2; 32]); + let storage_key_3 = &Key::Fix([3; 32]); + let code_bob = MockLoader::insert(Call, |ctx, _| { + if ctx.input_data[0] == 0 { + assert_eq!( + ctx.ext.set_transient_storage(storage_key_1, Some(vec![1, 2]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.call( + Weight::zero(), + BalanceOf::::zero(), + CHARLIE, + 0, + vec![], + true, + false, + ), + exec_success() + ); + assert_eq!(ctx.ext.get_transient_storage(storage_key_1), Some(vec![3])); + assert_eq!(ctx.ext.get_transient_storage(storage_key_2), Some(vec![])); + assert_eq!(ctx.ext.get_transient_storage(storage_key_3), None); + } else { + assert_eq!( + ctx.ext.set_transient_storage(storage_key_1, Some(vec![3]), true), + Ok(WriteOutcome::Taken(vec![1, 2])) + ); + assert_eq!( + ctx.ext.set_transient_storage(storage_key_2, Some(vec![]), false), + Ok(WriteOutcome::New) + ); + } + exec_success() + }); + let code_charlie = MockLoader::insert(Call, |ctx, _| { + assert!(ctx + .ext + .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![99], true, false) + .is_ok()); + // CHARLIE can not read BOB`s storage. + assert_eq!(ctx.ext.get_transient_storage(storage_key_1), None); + exec_success() + }); + + // This one tests passing the input data into a contract via call. + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn get_transient_storage_size_works() { + let storage_key_1 = &Key::Fix([1; 32]); + let storage_key_2 = &Key::Fix([2; 32]); + let storage_key_3 = &Key::Fix([3; 32]); + let code_hash = MockLoader::insert(Call, |ctx, _| { + assert_eq!( + ctx.ext.set_transient_storage(storage_key_1, Some(vec![1, 2, 3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(storage_key_2, Some(vec![]), false), + Ok(WriteOutcome::New) + ); + assert_eq!(ctx.ext.get_transient_storage_size(storage_key_1), Some(3)); + assert_eq!(ctx.ext.get_transient_storage_size(storage_key_2), Some(0)); + assert_eq!(ctx.ext.get_transient_storage_size(storage_key_3), None); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_hash); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + assert_ok!(MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn rollback_transient_storage_works() { + // Call stack: BOB -> CHARLIE (trap) -> BOB' (success) + let storage_key = &Key::Fix([1; 32]); + let code_bob = MockLoader::insert(Call, |ctx, _| { + if ctx.input_data[0] == 0 { + assert_eq!( + ctx.ext.set_transient_storage(storage_key, Some(vec![1, 2]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.call( + Weight::zero(), + BalanceOf::::zero(), + CHARLIE, + 0, + vec![], + true, + false + ), + exec_trapped() + ); + assert_eq!(ctx.ext.get_transient_storage(storage_key), Some(vec![1, 2])); + } else { + let overwritten_length = ctx.ext.get_transient_storage_size(storage_key).unwrap(); + assert_eq!( + ctx.ext.set_transient_storage(storage_key, Some(vec![3]), false), + Ok(WriteOutcome::Overwritten(overwritten_length)) + ); + assert_eq!(ctx.ext.get_transient_storage(storage_key), Some(vec![3])); + } + exec_success() + }); + let code_charlie = MockLoader::insert(Call, |ctx, _| { + assert!(ctx + .ext + .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![99], true, false) + .is_ok()); + exec_trapped() + }); + + // This one tests passing the input data into a contract via call. + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn ecdsa_to_eth_address_returns_proper_value() { + let bob_ch = MockLoader::insert(Call, |ctx, _| { + let pubkey_compressed = array_bytes::hex2array_unchecked( + "028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd91", + ); + assert_eq!( + ctx.ext.ecdsa_to_eth_address(&pubkey_compressed).unwrap(), + array_bytes::hex2array_unchecked::<_, 20>( + "09231da7b19A016f9e576d23B16277062F4d46A8" + ) + ); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, bob_ch); + + let contract_origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let result = MockStack::run_call( + contract_origin, + BOB, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ); + assert_matches!(result, Ok(_)); + }); + } +} diff --git a/substrate/frame/revive/src/gas.rs b/substrate/frame/revive/src/gas.rs new file mode 100644 index 000000000000..2034f39e9bc5 --- /dev/null +++ b/substrate/frame/revive/src/gas.rs @@ -0,0 +1,416 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{exec::ExecError, weights::WeightInfo, Config, Error}; +use core::marker::PhantomData; +use frame_support::{ + dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, PostDispatchInfo}, + weights::Weight, + DefaultNoBound, +}; +use sp_runtime::{traits::Zero, DispatchError}; + +#[cfg(test)] +use std::{any::Any, fmt::Debug}; + +#[derive(Debug, PartialEq, Eq)] +pub struct ChargedAmount(Weight); + +impl ChargedAmount { + pub fn amount(&self) -> Weight { + self.0 + } +} + +/// Meter for syncing the gas between the executor and the gas meter. +#[derive(DefaultNoBound)] +struct EngineMeter { + fuel: u64, + _phantom: PhantomData, +} + +impl EngineMeter { + /// Create a meter with the given fuel limit. + fn new(limit: Weight) -> Self { + Self { + fuel: limit.ref_time().saturating_div(Self::ref_time_per_fuel()), + _phantom: PhantomData, + } + } + + /// Set the fuel left to the given value. + /// Returns the amount of Weight consumed since the last update. + fn set_fuel(&mut self, fuel: u64) -> Weight { + let consumed = self.fuel.saturating_sub(fuel).saturating_mul(Self::ref_time_per_fuel()); + self.fuel = fuel; + Weight::from_parts(consumed, 0) + } + + /// Charge the given amount of gas. + /// Returns the amount of fuel left. + fn charge_ref_time(&mut self, ref_time: u64) -> Result { + let amount = ref_time + .checked_div(Self::ref_time_per_fuel()) + .ok_or(Error::::InvalidSchedule)?; + + self.fuel.checked_sub(amount).ok_or_else(|| Error::::OutOfGas)?; + Ok(Syncable(self.fuel.try_into().map_err(|_| Error::::OutOfGas)?)) + } + + /// How much ref time does each PolkaVM gas correspond to. + fn ref_time_per_fuel() -> u64 { + // We execute 6 different instructions therefore we have to divide the actual + // computed gas costs by 6 to have a rough estimate as to how expensive each + // single executed instruction is going to be. + let instr_cost = T::WeightInfo::instr_i64_load_store(1) + .saturating_sub(T::WeightInfo::instr_i64_load_store(0)) + .ref_time(); + instr_cost / 6 + } +} + +/// Used to capture the gas left before entering a host function. +/// +/// Has to be consumed in order to sync back the gas after leaving the host function. +#[must_use] +pub struct RefTimeLeft(u64); + +/// Resource that needs to be synced to the executor. +/// +/// Wrapped to make sure that the resource will be synced back the the executor. +#[must_use] +pub struct Syncable(polkavm::Gas); + +impl From for polkavm::Gas { + fn from(from: Syncable) -> Self { + from.0 + } +} + +#[cfg(not(test))] +pub trait TestAuxiliaries {} +#[cfg(not(test))] +impl TestAuxiliaries for T {} + +#[cfg(test)] +pub trait TestAuxiliaries: Any + Debug + PartialEq + Eq {} +#[cfg(test)] +impl TestAuxiliaries for T {} + +/// This trait represents a token that can be used for charging `GasMeter`. +/// There is no other way of charging it. +/// +/// Implementing type is expected to be super lightweight hence `Copy` (`Clone` is added +/// for consistency). If inlined there should be no observable difference compared +/// to a hand-written code. +pub trait Token: Copy + Clone + TestAuxiliaries { + /// Return the amount of gas that should be taken by this token. + /// + /// This function should be really lightweight and must not fail. It is not + /// expected that implementors will query the storage or do any kinds of heavy operations. + /// + /// That said, implementors of this function still can run into overflows + /// while calculating the amount. In this case it is ok to use saturating operations + /// since on overflow they will return `max_value` which should consume all gas. + fn weight(&self) -> Weight; + + /// Returns true if this token is expected to influence the lowest gas limit. + fn influence_lowest_gas_limit(&self) -> bool { + true + } +} + +/// A wrapper around a type-erased trait object of what used to be a `Token`. +#[cfg(test)] +pub struct ErasedToken { + pub description: String, + pub token: Box, +} + +#[derive(DefaultNoBound)] +pub struct GasMeter { + gas_limit: Weight, + /// Amount of gas left from initial gas limit. Can reach zero. + gas_left: Weight, + /// Due to `adjust_gas` and `nested` the `gas_left` can temporarily dip below its final value. + gas_left_lowest: Weight, + /// The amount of resources that was consumed by the execution engine. + /// We have to track it separately in order to avoid the loss of precision that happens when + /// converting from ref_time to the execution engine unit. + engine_meter: EngineMeter, + _phantom: PhantomData, + #[cfg(test)] + tokens: Vec, +} + +impl GasMeter { + pub fn new(gas_limit: Weight) -> Self { + GasMeter { + gas_limit, + gas_left: gas_limit, + gas_left_lowest: gas_limit, + engine_meter: EngineMeter::new(gas_limit), + _phantom: PhantomData, + #[cfg(test)] + tokens: Vec::new(), + } + } + + /// Create a new gas meter by removing gas from the current meter. + /// + /// # Note + /// + /// Passing `0` as amount is interpreted as "all remaining gas". + pub fn nested(&mut self, amount: Weight) -> Self { + let amount = Weight::from_parts( + if amount.ref_time().is_zero() { + self.gas_left().ref_time() + } else { + amount.ref_time() + }, + if amount.proof_size().is_zero() { + self.gas_left().proof_size() + } else { + amount.proof_size() + }, + ) + .min(self.gas_left); + self.gas_left -= amount; + GasMeter::new(amount) + } + + /// Absorb the remaining gas of a nested meter after we are done using it. + pub fn absorb_nested(&mut self, nested: Self) { + self.gas_left_lowest = (self.gas_left + nested.gas_limit) + .saturating_sub(nested.gas_required()) + .min(self.gas_left_lowest); + self.gas_left += nested.gas_left; + } + + /// Account for used gas. + /// + /// Amount is calculated by the given `token`. + /// + /// Returns `OutOfGas` if there is not enough gas or addition of the specified + /// amount of gas has lead to overflow. + /// + /// NOTE that amount isn't consumed if there is not enough gas. This is considered + /// safe because we always charge gas before performing any resource-spending action. + #[inline] + pub fn charge>(&mut self, token: Tok) -> Result { + #[cfg(test)] + { + // Unconditionally add the token to the storage. + let erased_tok = + ErasedToken { description: format!("{:?}", token), token: Box::new(token) }; + self.tokens.push(erased_tok); + } + let amount = token.weight(); + // It is OK to not charge anything on failure because we always charge _before_ we perform + // any action + self.gas_left = self.gas_left.checked_sub(&amount).ok_or_else(|| Error::::OutOfGas)?; + Ok(ChargedAmount(amount)) + } + + /// Adjust a previously charged amount down to its actual amount. + /// + /// This is when a maximum a priori amount was charged and then should be partially + /// refunded to match the actual amount. + pub fn adjust_gas>(&mut self, charged_amount: ChargedAmount, token: Tok) { + if token.influence_lowest_gas_limit() { + self.gas_left_lowest = self.gas_left_lowest(); + } + let adjustment = charged_amount.0.saturating_sub(token.weight()); + self.gas_left = self.gas_left.saturating_add(adjustment).min(self.gas_limit); + } + + /// Hand over the gas metering responsibility from the executor to this meter. + /// + /// Needs to be called when entering a host function to update this meter with the + /// gas that was tracked by the executor. It tracks the latest seen total value + /// in order to compute the delta that needs to be charged. + pub fn sync_from_executor( + &mut self, + engine_fuel: polkavm::Gas, + ) -> Result { + let weight_consumed = self + .engine_meter + .set_fuel(engine_fuel.try_into().map_err(|_| Error::::OutOfGas)?); + self.gas_left + .checked_reduce(weight_consumed) + .ok_or_else(|| Error::::OutOfGas)?; + Ok(RefTimeLeft(self.gas_left.ref_time())) + } + + /// Hand over the gas metering responsibility from this meter to the executor. + /// + /// Needs to be called when leaving a host function in order to calculate how much + /// gas needs to be charged from the **executor**. It updates the last seen executor + /// total value so that it is correct when `sync_from_executor` is called the next time. + /// + /// It is important that this does **not** actually sync with the executor. That has + /// to be done by the caller. + pub fn sync_to_executor(&mut self, before: RefTimeLeft) -> Result { + let ref_time_consumed = before.0.saturating_sub(self.gas_left().ref_time()); + self.engine_meter.charge_ref_time(ref_time_consumed) + } + + /// Returns the amount of gas that is required to run the same call. + /// + /// This can be different from `gas_spent` because due to `adjust_gas` the amount of + /// spent gas can temporarily drop and be refunded later. + pub fn gas_required(&self) -> Weight { + self.gas_limit.saturating_sub(self.gas_left_lowest()) + } + + /// Returns how much gas was spent + pub fn gas_consumed(&self) -> Weight { + self.gas_limit.saturating_sub(self.gas_left) + } + + /// Returns how much gas left from the initial budget. + pub fn gas_left(&self) -> Weight { + self.gas_left + } + + /// The amount of gas in terms of engine gas. + pub fn engine_fuel_left(&self) -> Result { + self.engine_meter.fuel.try_into().map_err(|_| >::OutOfGas.into()) + } + + /// Turn this GasMeter into a DispatchResult that contains the actually used gas. + pub fn into_dispatch_result( + self, + result: Result, + base_weight: Weight, + ) -> DispatchResultWithPostInfo + where + E: Into, + { + let post_info = PostDispatchInfo { + actual_weight: Some(self.gas_consumed().saturating_add(base_weight)), + pays_fee: Default::default(), + }; + + result + .map(|_| post_info) + .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e.into().error }) + } + + fn gas_left_lowest(&self) -> Weight { + self.gas_left_lowest.min(self.gas_left) + } + + #[cfg(test)] + pub fn tokens(&self) -> &[ErasedToken] { + &self.tokens + } +} + +#[cfg(test)] +mod tests { + use super::{GasMeter, Token, Weight}; + use crate::tests::Test; + + /// A simple utility macro that helps to match against a + /// list of tokens. + macro_rules! match_tokens { + ($tokens_iter:ident,) => { + }; + ($tokens_iter:ident, $x:expr, $($rest:tt)*) => { + { + let next = ($tokens_iter).next().unwrap(); + let pattern = $x; + + // Note that we don't specify the type name directly in this macro, + // we only have some expression $x of some type. At the same time, we + // have an iterator of Box and to downcast we need to specify + // the type which we want downcast to. + // + // So what we do is we assign `_pattern_typed_next_ref` to a variable which has + // the required type. + // + // Then we make `_pattern_typed_next_ref = token.downcast_ref()`. This makes + // rustc infer the type `T` (in `downcast_ref`) to be the same as in $x. + + let mut _pattern_typed_next_ref = &pattern; + _pattern_typed_next_ref = match next.token.downcast_ref() { + Some(p) => { + assert_eq!(p, &pattern); + p + } + None => { + panic!("expected type {} got {}", stringify!($x), next.description); + } + }; + } + + match_tokens!($tokens_iter, $($rest)*); + }; + } + + /// A trivial token that charges the specified number of gas units. + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + struct SimpleToken(u64); + impl Token for SimpleToken { + fn weight(&self) -> Weight { + Weight::from_parts(self.0, 0) + } + } + + #[test] + fn it_works() { + let gas_meter = GasMeter::::new(Weight::from_parts(50000, 0)); + assert_eq!(gas_meter.gas_left(), Weight::from_parts(50000, 0)); + } + + #[test] + fn tracing() { + let mut gas_meter = GasMeter::::new(Weight::from_parts(50000, 0)); + assert!(!gas_meter.charge(SimpleToken(1)).is_err()); + + let mut tokens = gas_meter.tokens().iter(); + match_tokens!(tokens, SimpleToken(1),); + } + + // This test makes sure that nothing can be executed if there is no gas. + #[test] + fn refuse_to_execute_anything_if_zero() { + let mut gas_meter = GasMeter::::new(Weight::zero()); + assert!(gas_meter.charge(SimpleToken(1)).is_err()); + } + + // Make sure that the gas meter does not charge in case of overcharge + #[test] + fn overcharge_does_not_charge() { + let mut gas_meter = GasMeter::::new(Weight::from_parts(200, 0)); + + // The first charge is should lead to OOG. + assert!(gas_meter.charge(SimpleToken(300)).is_err()); + + // The gas meter should still contain the full 200. + assert!(gas_meter.charge(SimpleToken(200)).is_ok()); + } + + // Charging the exact amount that the user paid for should be + // possible. + #[test] + fn charge_exact_amount() { + let mut gas_meter = GasMeter::::new(Weight::from_parts(25, 0)); + assert!(!gas_meter.charge(SimpleToken(25)).is_err()); + } +} diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs new file mode 100644 index 000000000000..303c649bc8cf --- /dev/null +++ b/substrate/frame/revive/src/lib.rs @@ -0,0 +1,1354 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![doc = include_str!("../README.md")] +#![allow(rustdoc::private_intra_doc_links)] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "1024")] + +extern crate alloc; +mod address; +mod benchmarking; +mod benchmarking_dummy; +mod exec; +mod gas; +mod primitives; +pub use primitives::*; + +mod limits; +mod storage; +mod transient_storage; +mod wasm; + +pub mod chain_extension; +pub mod debug; +pub mod migration; +pub mod test_utils; +pub mod weights; + +#[cfg(test)] +mod tests; +use crate::{ + exec::{AccountIdOf, ExecError, Executable, Ext, Key, Origin, Stack as ExecStack}, + gas::GasMeter, + storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager}, + wasm::{CodeInfo, RuntimeCosts, WasmBlob}, +}; +use codec::{Codec, Decode, Encode, HasCompact}; +use core::fmt::Debug; +use environmental::*; +use frame_support::{ + dispatch::{ + DispatchErrorWithPostInfo, DispatchResultWithPostInfo, GetDispatchInfo, Pays, + PostDispatchInfo, RawOrigin, WithPostDispatchInfo, + }, + ensure, + traits::{ + fungible::{Inspect, Mutate, MutateHold}, + ConstU32, Contains, EnsureOrigin, Get, Time, + }, + weights::{Weight, WeightMeter}, + BoundedVec, RuntimeDebugNoBound, +}; +use frame_system::{ + ensure_signed, + pallet_prelude::{BlockNumberFor, OriginFor}, + EventRecord, Pallet as System, +}; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{BadOrigin, Convert, Dispatchable, Saturating, StaticLookup}, + DispatchError, +}; + +pub use crate::{ + address::{AddressGenerator, DefaultAddressGenerator}, + debug::Tracing, + migration::{MigrateSequence, Migration, NoopMigration}, + pallet::*, +}; +pub use weights::WeightInfo; + +#[cfg(doc)] +pub use crate::wasm::SyscallDoc; + +type CodeHash = ::Hash; +type TrieId = BoundedVec>; +type BalanceOf = + <::Currency as Inspect<::AccountId>>::Balance; +type CodeVec = BoundedVec::MaxCodeLen>; +type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; +type EventRecordOf = + EventRecord<::RuntimeEvent, ::Hash>; +type DebugBuffer = BoundedVec>; + +/// Used as a sentinel value when reading and writing contract memory. +/// +/// It is usually used to signal `None` to a contract when only a primitive is allowed +/// and we don't want to go through encoding a full Rust type. Using `u32::Max` is a safe +/// sentinel because contracts are never allowed to use such a large amount of resources +/// that this value makes sense for a memory location or length. +const SENTINEL: u32 = u32::MAX; + +/// The target that is used for the log output emitted by this crate. +/// +/// Hence you can use this target to selectively increase the log level for this crate. +/// +/// Example: `RUST_LOG=runtime::revive=debug my_code --dev` +const LOG_TARGET: &str = "runtime::revive"; + +/// This version determines which syscalls are available to contracts. +/// +/// Needs to be bumped every time a versioned syscall is added. +const API_VERSION: u16 = 0; + +#[test] +fn api_version_up_to_date() { + assert!( + API_VERSION == crate::wasm::HIGHEST_API_VERSION, + "A new versioned API has been added. The `API_VERSION` needs to be bumped." + ); +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use crate::debug::Debugger; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use sp_runtime::Perbill; + + /// The in-code storage version. + pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::config(with_default)] + pub trait Config: frame_system::Config { + /// The time implementation used to supply timestamps to contracts through `seal_now`. + type Time: Time; + + /// The fungible in which fees are paid and contract balances are held. + #[pallet::no_default] + type Currency: Inspect + + Mutate + + MutateHold; + + /// The overarching event type. + #[pallet::no_default_bounds] + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// The overarching call type. + #[pallet::no_default_bounds] + type RuntimeCall: Dispatchable + + GetDispatchInfo + + codec::Decode + + IsType<::RuntimeCall>; + + /// Overarching hold reason. + #[pallet::no_default_bounds] + type RuntimeHoldReason: From; + + /// Filter that is applied to calls dispatched by contracts. + /// + /// Use this filter to control which dispatchables are callable by contracts. + /// This is applied in **addition** to [`frame_system::Config::BaseCallFilter`]. + /// It is recommended to treat this as a whitelist. + /// + /// # Stability + /// + /// The runtime **must** make sure that all dispatchables that are callable by + /// contracts remain stable. In addition [`Self::RuntimeCall`] itself must remain stable. + /// This means that no existing variants are allowed to switch their positions. + /// + /// # Note + /// + /// Note that dispatchables that are called via contracts do not spawn their + /// own wasm instance for each call (as opposed to when called via a transaction). + /// Therefore please make sure to be restrictive about which dispatchables are allowed + /// in order to not introduce a new DoS vector like memory allocation patterns that can + /// be exploited to drive the runtime into a panic. + /// + /// This filter does not apply to XCM transact calls. To impose restrictions on XCM transact + /// calls, you must configure them separately within the XCM pallet itself. + #[pallet::no_default_bounds] + type CallFilter: Contains<::RuntimeCall>; + + /// Used to answer contracts' queries regarding the current weight price. This is **not** + /// used to calculate the actual fee and is only for informational purposes. + #[pallet::no_default_bounds] + type WeightPrice: Convert>; + + /// Describes the weights of the dispatchables of this module and is also used to + /// construct a default cost schedule. + type WeightInfo: WeightInfo; + + /// Type that allows the runtime authors to add new host functions for a contract to call. + #[pallet::no_default_bounds] + type ChainExtension: chain_extension::ChainExtension + Default; + + /// The amount of balance a caller has to pay for each byte of storage. + /// + /// # Note + /// + /// It is safe to chage this value on a live chain as all refunds are pro rata. + #[pallet::constant] + #[pallet::no_default_bounds] + type DepositPerByte: Get>; + + /// The amount of balance a caller has to pay for each storage item. + /// + /// # Note + /// + /// It is safe to chage this value on a live chain as all refunds are pro rata. + #[pallet::constant] + #[pallet::no_default_bounds] + type DepositPerItem: Get>; + + /// The percentage of the storage deposit that should be held for using a code hash. + /// Instantiating a contract, or calling [`chain_extension::Ext::lock_delegate_dependency`] + /// protects the code from being removed. In order to prevent abuse these actions are + /// protected with a percentage of the code deposit. + #[pallet::constant] + type CodeHashLockupDepositPercent: Get; + + /// The address generator used to generate the addresses of contracts. + #[pallet::no_default_bounds] + type AddressGenerator: AddressGenerator; + + /// The maximum length of a contract code in bytes. + /// + /// This value hugely affects the memory requirements of this pallet since all the code of + /// all contracts on the call stack will need to be held in memory. Setting of a correct + /// value will be enforced in [`Pallet::integrity_test`]. + #[pallet::constant] + type MaxCodeLen: Get; + + /// Make contract callable functions marked as `#[unstable]` available. + /// + /// Contracts that use `#[unstable]` functions won't be able to be uploaded unless + /// this is set to `true`. This is only meant for testnets and dev nodes in order to + /// experiment with new features. + /// + /// # Warning + /// + /// Do **not** set to `true` on productions chains. + #[pallet::constant] + type UnsafeUnstableInterface: Get; + + /// Origin allowed to upload code. + /// + /// By default, it is safe to set this to `EnsureSigned`, allowing anyone to upload contract + /// code. + #[pallet::no_default_bounds] + type UploadOrigin: EnsureOrigin; + + /// Origin allowed to instantiate code. + /// + /// # Note + /// + /// This is not enforced when a contract instantiates another contract. The + /// [`Self::UploadOrigin`] should make sure that no code is deployed that does unwanted + /// instantiations. + /// + /// By default, it is safe to set this to `EnsureSigned`, allowing anyone to instantiate + /// contract code. + #[pallet::no_default_bounds] + type InstantiateOrigin: EnsureOrigin; + + /// The sequence of migration steps that will be applied during a migration. + /// + /// # Examples + /// ```ignore + /// use pallet_revive::migration::{v10, v11}; + /// # struct Runtime {}; + /// # struct Currency {}; + /// type Migrations = (v10::Migration, v11::Migration); + /// ``` + /// + /// If you have a single migration step, you can use a tuple with a single element: + /// ```ignore + /// use pallet_revive::migration::v10; + /// # struct Runtime {}; + /// # struct Currency {}; + /// type Migrations = (v10::Migration,); + /// ``` + type Migrations: MigrateSequence; + + /// For most production chains, it's recommended to use the `()` implementation of this + /// trait. This implementation offers additional logging when the log target + /// "runtime::revive" is set to trace. + #[pallet::no_default_bounds] + type Debug: Debugger; + + /// A type that exposes XCM APIs, allowing contracts to interact with other parachains, and + /// execute XCM programs. + #[pallet::no_default_bounds] + type Xcm: xcm_builder::Controller< + OriginFor, + ::RuntimeCall, + BlockNumberFor, + >; + + /// The amount of memory in bytes that parachain nodes alot to the runtime. + /// + /// This is used in [`Pallet::integrity_test`] to make sure that the runtime has enough + /// memory to support this pallet if set to the correct value. + type RuntimeMemory: Get; + + /// The amount of memory in bytes that relay chain validators alot to the PoV. + /// + /// This is used in [`Pallet::integrity_test`] to make sure that the runtime has enough + /// memory to support this pallet if set to the correct value. + /// + /// This value is usually higher than [`Self::RuntimeMemory`] to account for the fact + /// that validators have to hold all storage items in PvF memory. + type PVFMemory: Get; + } + + /// Container for different types that implement [`DefaultConfig`]` of this pallet. + pub mod config_preludes { + use super::*; + use frame_support::{ + derive_impl, + traits::{ConstBool, ConstU32}, + }; + use frame_system::EnsureSigned; + use sp_core::parameter_types; + + type AccountId = sp_runtime::AccountId32; + type Balance = u64; + const UNITS: Balance = 10_000_000_000; + const CENTS: Balance = UNITS / 100; + + pub const fn deposit(items: u32, bytes: u32) -> Balance { + items as Balance * 1 * CENTS + (bytes as Balance) * 1 * CENTS + } + + parameter_types! { + pub const DepositPerItem: Balance = deposit(1, 0); + pub const DepositPerByte: Balance = deposit(0, 1); + pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0); + } + + /// A type providing default configurations for this pallet in testing environment. + pub struct TestDefaultConfig; + + impl Time for TestDefaultConfig { + type Moment = u64; + fn now() -> Self::Moment { + unimplemented!("No default `now` implementation in `TestDefaultConfig` provide a custom `T::Time` type.") + } + } + + impl> Convert for TestDefaultConfig { + fn convert(w: Weight) -> T { + w.ref_time().into() + } + } + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] + impl frame_system::DefaultConfig for TestDefaultConfig {} + + #[frame_support::register_default_impl(TestDefaultConfig)] + impl DefaultConfig for TestDefaultConfig { + #[inject_runtime_type] + type RuntimeEvent = (); + + #[inject_runtime_type] + type RuntimeHoldReason = (); + + #[inject_runtime_type] + type RuntimeCall = (); + + type AddressGenerator = DefaultAddressGenerator; + type CallFilter = (); + type ChainExtension = (); + type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; + type DepositPerByte = DepositPerByte; + type DepositPerItem = DepositPerItem; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; + type Migrations = (); + type Time = Self; + type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; + type WeightInfo = (); + type WeightPrice = Self; + type Debug = (); + type Xcm = (); + type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; + type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; + } + } + + #[pallet::event] + pub enum Event { + /// Contract deployed by address at the specified address. + Instantiated { deployer: T::AccountId, contract: T::AccountId }, + + /// Contract has been removed. + /// + /// # Note + /// + /// The only way for a contract to be removed and emitting this event is by calling + /// `seal_terminate`. + Terminated { + /// The contract that was terminated. + contract: T::AccountId, + /// The account that received the contracts remaining balance + beneficiary: T::AccountId, + }, + + /// Code with the specified hash has been stored. + CodeStored { code_hash: T::Hash, deposit_held: BalanceOf, uploader: T::AccountId }, + + /// A custom event emitted by the contract. + ContractEmitted { + /// The contract that emitted the event. + contract: T::AccountId, + /// Data supplied by the contract. Metadata generated during contract compilation + /// is needed to decode it. + data: Vec, + }, + + /// A code with the specified hash was removed. + CodeRemoved { code_hash: T::Hash, deposit_released: BalanceOf, remover: T::AccountId }, + + /// A contract's code was updated. + ContractCodeUpdated { + /// The contract that has been updated. + contract: T::AccountId, + /// New code hash that was set for the contract. + new_code_hash: T::Hash, + /// Previous code hash of the contract. + old_code_hash: T::Hash, + }, + + /// A contract was called either by a plain account or another contract. + /// + /// # Note + /// + /// Please keep in mind that like all events this is only emitted for successful + /// calls. This is because on failure all storage changes including events are + /// rolled back. + Called { + /// The caller of the `contract`. + caller: Origin, + /// The contract that was called. + contract: T::AccountId, + }, + + /// A contract delegate called a code hash. + /// + /// # Note + /// + /// Please keep in mind that like all events this is only emitted for successful + /// calls. This is because on failure all storage changes including events are + /// rolled back. + DelegateCalled { + /// The contract that performed the delegate call and hence in whose context + /// the `code_hash` is executed. + contract: T::AccountId, + /// The code hash that was delegate called. + code_hash: CodeHash, + }, + + /// Some funds have been transferred and held as storage deposit. + StorageDepositTransferredAndHeld { + from: T::AccountId, + to: T::AccountId, + amount: BalanceOf, + }, + + /// Some storage deposit funds have been transferred and released. + StorageDepositTransferredAndReleased { + from: T::AccountId, + to: T::AccountId, + amount: BalanceOf, + }, + } + + #[pallet::error] + pub enum Error { + /// Invalid schedule supplied, e.g. with zero weight of a basic operation. + InvalidSchedule, + /// Invalid combination of flags supplied to `seal_call` or `seal_delegate_call`. + InvalidCallFlags, + /// The executed contract exhausted its gas limit. + OutOfGas, + /// The output buffer supplied to a contract API call was too small. + OutputBufferTooSmall, + /// Performing the requested transfer failed. Probably because there isn't enough + /// free balance in the sender's account. + TransferFailed, + /// Performing a call was denied because the calling depth reached the limit + /// of what is specified in the schedule. + MaxCallDepthReached, + /// No contract was found at the specified address. + ContractNotFound, + /// The code supplied to `instantiate_with_code` exceeds the limit specified in the + /// current schedule. + CodeTooLarge, + /// No code could be found at the supplied code hash. + CodeNotFound, + /// No code info could be found at the supplied code hash. + CodeInfoNotFound, + /// A buffer outside of sandbox memory was passed to a contract API function. + OutOfBounds, + /// Input passed to a contract API function failed to decode as expected type. + DecodingFailed, + /// Contract trapped during execution. + ContractTrapped, + /// The size defined in `T::MaxValueSize` was exceeded. + ValueTooLarge, + /// Termination of a contract is not allowed while the contract is already + /// on the call stack. Can be triggered by `seal_terminate`. + TerminatedWhileReentrant, + /// `seal_call` forwarded this contracts input. It therefore is no longer available. + InputForwarded, + /// The amount of topics passed to `seal_deposit_events` exceeds the limit. + TooManyTopics, + /// The chain does not provide a chain extension. Calling the chain extension results + /// in this error. Note that this usually shouldn't happen as deploying such contracts + /// is rejected. + NoChainExtension, + /// Failed to decode the XCM program. + XCMDecodeFailed, + /// A contract with the same AccountId already exists. + DuplicateContract, + /// A contract self destructed in its constructor. + /// + /// This can be triggered by a call to `seal_terminate`. + TerminatedInConstructor, + /// A call tried to invoke a contract that is flagged as non-reentrant. + ReentranceDenied, + /// A contract called into the runtime which then called back into this pallet. + ReenteredPallet, + /// A contract attempted to invoke a state modifying API while being in read-only mode. + StateChangeDenied, + /// Origin doesn't have enough balance to pay the required storage deposits. + StorageDepositNotEnoughFunds, + /// More storage was created than allowed by the storage deposit limit. + StorageDepositLimitExhausted, + /// Code removal was denied because the code is still in use by at least one contract. + CodeInUse, + /// The contract ran to completion but decided to revert its storage changes. + /// Please note that this error is only returned from extrinsics. When called directly + /// or via RPC an `Ok` will be returned. In this case the caller needs to inspect the flags + /// to determine whether a reversion has taken place. + ContractReverted, + /// The contract failed to compile or is missing the correct entry points. + /// + /// A more detailed error can be found on the node console if debug messages are enabled + /// by supplying `-lruntime::revive=debug`. + CodeRejected, + /// A pending migration needs to complete before the extrinsic can be called. + MigrationInProgress, + /// Migrate dispatch call was attempted but no migration was performed. + NoMigrationPerformed, + /// The contract has reached its maximum number of delegate dependencies. + MaxDelegateDependenciesReached, + /// The dependency was not found in the contract's delegate dependencies. + DelegateDependencyNotFound, + /// The contract already depends on the given delegate dependency. + DelegateDependencyAlreadyExists, + /// Can not add a delegate dependency to the code hash of the contract itself. + CannotAddSelfAsDelegateDependency, + /// Can not add more data to transient storage. + OutOfTransientStorage, + /// The contract tried to call a syscall which does not exist (at its current api level). + InvalidSyscall, + /// Invalid storage flags were passed to one of the storage syscalls. + InvalidStorageFlags, + /// PolkaVM failed during code execution. Probably due to a malformed program. + ExecutionFailed, + } + + /// A reason for the pallet contracts placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// The Pallet has reserved it for storing code on-chain. + CodeUploadDepositReserve, + /// The Pallet has reserved it for storage deposit. + StorageDepositReserve, + } + + /// A mapping from a contract's code hash to its code. + #[pallet::storage] + pub(crate) type PristineCode = StorageMap<_, Identity, CodeHash, CodeVec>; + + /// A mapping from a contract's code hash to its code info. + #[pallet::storage] + pub(crate) type CodeInfoOf = StorageMap<_, Identity, CodeHash, CodeInfo>; + + /// The code associated with a given account. + #[pallet::storage] + pub(crate) type ContractInfoOf = + StorageMap<_, Identity, T::AccountId, ContractInfo>; + + /// Evicted contracts that await child trie deletion. + /// + /// Child trie deletion is a heavy operation depending on the amount of storage items + /// stored in said trie. Therefore this operation is performed lazily in `on_idle`. + #[pallet::storage] + pub(crate) type DeletionQueue = StorageMap<_, Twox64Concat, u32, TrieId>; + + /// A pair of monotonic counters used to track the latest contract marked for deletion + /// and the latest deleted contract in queue. + #[pallet::storage] + pub(crate) type DeletionQueueCounter = + StorageValue<_, DeletionQueueManager, ValueQuery>; + + /// A migration can span across multiple blocks. This storage defines a cursor to track the + /// progress of the migration, enabling us to resume from the last completed position. + #[pallet::storage] + pub(crate) type MigrationInProgress = + StorageValue<_, migration::Cursor, OptionQuery>; + + #[pallet::extra_constants] + impl Pallet { + #[pallet::constant_name(ApiVersion)] + fn api_version() -> u16 { + API_VERSION + } + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_idle(_block: BlockNumberFor, limit: Weight) -> Weight { + use migration::MigrateResult::*; + let mut meter = WeightMeter::with_limit(limit); + + loop { + match Migration::::migrate(&mut meter) { + // There is not enough weight to perform a migration. + // We can't do anything more, so we return the used weight. + NoMigrationPerformed | InProgress { steps_done: 0 } => return meter.consumed(), + // Migration is still in progress, we can start the next step. + InProgress { .. } => continue, + // Either no migration is in progress, or we are done with all migrations, we + // can do some more other work with the remaining weight. + Completed | NoMigrationInProgress => break, + } + } + + ContractInfo::::process_deletion_queue_batch(&mut meter); + meter.consumed() + } + + fn integrity_test() { + Migration::::integrity_test(); + + // Total runtime memory limit + let max_runtime_mem: u32 = T::RuntimeMemory::get(); + // Memory limits for a single contract: + // Value stack size: 1Mb per contract, default defined in wasmi + const MAX_STACK_SIZE: u32 = 1024 * 1024; + // Heap limit is normally 16 mempages of 64kb each = 1Mb per contract + let max_heap_size = limits::MEMORY_BYTES; + // The root frame is not accounted in CALL_STACK_DEPTH + let max_call_depth = + limits::CALL_STACK_DEPTH.checked_add(1).expect("CallStack size is too big"); + // Transient storage uses a BTreeMap, which has overhead compared to the raw size of + // key-value data. To ensure safety, a margin of 2x the raw key-value size is used. + let max_transient_storage_size = limits::TRANSIENT_STORAGE_BYTES + .checked_mul(2) + .expect("MaxTransientStorageSize is too large"); + + // Check that given configured `MaxCodeLen`, runtime heap memory limit can't be broken. + // + // In worst case, the decoded Wasm contract code would be `x16` times larger than the + // encoded one. This is because even a single-byte wasm instruction has 16-byte size in + // wasmi. This gives us `MaxCodeLen*16` safety margin. + // + // Next, the pallet keeps the Wasm blob for each + // contract, hence we add up `MaxCodeLen` to the safety margin. + // + // The inefficiencies of the freeing-bump allocator + // being used in the client for the runtime memory allocations, could lead to possible + // memory allocations for contract code grow up to `x4` times in some extreme cases, + // which gives us total multiplier of `17*4` for `MaxCodeLen`. + // + // That being said, for every contract executed in runtime, at least `MaxCodeLen*17*4` + // memory should be available. Note that maximum allowed heap memory and stack size per + // each contract (stack frame) should also be counted. + // + // The pallet holds transient storage with a size up to `max_transient_storage_size`. + // + // Finally, we allow 50% of the runtime memory to be utilized by the contracts call + // stack, keeping the rest for other facilities, such as PoV, etc. + // + // This gives us the following formula: + // + // `(MaxCodeLen * 17 * 4 + MAX_STACK_SIZE + max_heap_size) * max_call_depth + + // max_transient_storage_size < max_runtime_mem/2` + // + // Hence the upper limit for the `MaxCodeLen` can be defined as follows: + let code_len_limit = max_runtime_mem + .saturating_div(2) + .saturating_sub(max_transient_storage_size) + .saturating_div(max_call_depth) + .saturating_sub(max_heap_size) + .saturating_sub(MAX_STACK_SIZE) + .saturating_div(17 * 4); + + assert!( + T::MaxCodeLen::get() < code_len_limit, + "Given `CallStack` height {:?}, `MaxCodeLen` should be set less than {:?} \ + (current value is {:?}), to avoid possible runtime oom issues.", + max_call_depth, + code_len_limit, + T::MaxCodeLen::get(), + ); + + // Validators are configured to be able to use more memory than block builders. This is + // because in addition to `max_runtime_mem` they need to hold additional data in + // memory: PoV in multiple copies (1x encoded + 2x decoded) and all storage which + // includes emitted events. The assumption is that storage/events size + // can be a maximum of half of the validator runtime memory - max_runtime_mem. + let max_block_ref_time = T::BlockWeights::get() + .get(DispatchClass::Normal) + .max_total + .unwrap_or_else(|| T::BlockWeights::get().max_block) + .ref_time(); + let max_payload_size = limits::PAYLOAD_BYTES; + let max_key_size = + Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize]) + .expect("Key of maximal size shall be created") + .hash() + .len() as u32; + + // We can use storage to store items using the available block ref_time with the + // `set_storage` host function. + let max_storage_size: u32 = ((max_block_ref_time / + (>::weight(&RuntimeCosts::SetStorage { + new_bytes: max_payload_size, + old_bytes: 0, + }) + .ref_time())) + .saturating_mul(max_payload_size.saturating_add(max_key_size) as u64)) + .try_into() + .expect("Storage size too big"); + + let max_pvf_mem: u32 = T::PVFMemory::get(); + let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2; + + assert!( + max_storage_size < storage_size_limit, + "Maximal storage size {} exceeds the storage limit {}", + max_storage_size, + storage_size_limit + ); + + // We can use storage to store events using the available block ref_time with the + // `deposit_event` host function. The overhead of stored events, which is around 100B, + // is not taken into account to simplify calculations, as it does not change much. + let max_events_size: u32 = ((max_block_ref_time / + (>::weight(&RuntimeCosts::DepositEvent { + num_topic: 0, + len: max_payload_size, + }) + .ref_time())) + .saturating_mul(max_payload_size as u64)) + .try_into() + .expect("Events size too big"); + + assert!( + max_events_size < storage_size_limit, + "Maximal events size {} exceeds the events limit {}", + max_events_size, + storage_size_limit + ); + } + } + + #[pallet::call] + impl Pallet + where + as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + { + /// Makes a call to an account, optionally transferring some balance. + /// + /// # Parameters + /// + /// * `dest`: Address of the contract to call. + /// * `value`: The balance to transfer from the `origin` to `dest`. + /// * `gas_limit`: The gas limit enforced when executing the constructor. + /// * `storage_deposit_limit`: The maximum amount of balance that can be charged from the + /// caller to pay for the storage consumed. + /// * `data`: The input data to pass to the contract. + /// + /// * If the account is a smart-contract account, the associated code will be + /// executed and any value will be transferred. + /// * If the account is a regular account, any value will be transferred. + /// * If no account exists and the call value is not less than `existential_deposit`, + /// a regular account will be created and any value will be transferred. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::call().saturating_add(*gas_limit))] + pub fn call( + origin: OriginFor, + dest: AccountIdLookupOf, + #[pallet::compact] value: BalanceOf, + gas_limit: Weight, + #[pallet::compact] storage_deposit_limit: BalanceOf, + data: Vec, + ) -> DispatchResultWithPostInfo { + let dest = T::Lookup::lookup(dest)?; + let mut output = Self::bare_call( + origin, + dest, + value, + gas_limit, + storage_deposit_limit, + data, + DebugInfo::Skip, + CollectEvents::Skip, + ); + if let Ok(return_value) = &output.result { + if return_value.did_revert() { + output.result = Err(>::ContractReverted.into()); + } + } + dispatch_result(output.result, output.gas_consumed, T::WeightInfo::call()) + } + + /// Instantiates a contract from a previously deployed wasm binary. + /// + /// This function is identical to [`Self::instantiate_with_code`] but without the + /// code deployment step. Instead, the `code_hash` of an on-chain deployed wasm binary + /// must be supplied. + #[pallet::call_index(1)] + #[pallet::weight( + T::WeightInfo::instantiate(data.len() as u32, salt.len() as u32).saturating_add(*gas_limit) + )] + pub fn instantiate( + origin: OriginFor, + #[pallet::compact] value: BalanceOf, + gas_limit: Weight, + #[pallet::compact] storage_deposit_limit: BalanceOf, + code_hash: CodeHash, + data: Vec, + salt: Vec, + ) -> DispatchResultWithPostInfo { + let data_len = data.len() as u32; + let salt_len = salt.len() as u32; + let mut output = Self::bare_instantiate( + origin, + value, + gas_limit, + storage_deposit_limit, + Code::Existing(code_hash), + data, + salt, + DebugInfo::Skip, + CollectEvents::Skip, + ); + if let Ok(retval) = &output.result { + if retval.result.did_revert() { + output.result = Err(>::ContractReverted.into()); + } + } + dispatch_result( + output.result.map(|result| result.result), + output.gas_consumed, + T::WeightInfo::instantiate(data_len, salt_len), + ) + } + + /// Instantiates a new contract from the supplied `code` optionally transferring + /// some balance. + /// + /// This dispatchable has the same effect as calling [`Self::upload_code`] + + /// [`Self::instantiate`]. Bundling them together provides efficiency gains. Please + /// also check the documentation of [`Self::upload_code`]. + /// + /// # Parameters + /// + /// * `value`: The balance to transfer from the `origin` to the newly created contract. + /// * `gas_limit`: The gas limit enforced when executing the constructor. + /// * `storage_deposit_limit`: The maximum amount of balance that can be charged/reserved + /// from the caller to pay for the storage consumed. + /// * `code`: The contract code to deploy in raw bytes. + /// * `data`: The input data to pass to the contract constructor. + /// * `salt`: Used for the address derivation. See [`Pallet::contract_address`]. + /// + /// Instantiation is executed as follows: + /// + /// - The supplied `code` is deployed, and a `code_hash` is created for that code. + /// - If the `code_hash` already exists on the chain the underlying `code` will be shared. + /// - The destination address is computed based on the sender, code_hash and the salt. + /// - The smart-contract account is created at the computed address. + /// - The `value` is transferred to the new account. + /// - The `deploy` function is executed in the context of the newly-created account. + #[pallet::call_index(2)] + #[pallet::weight( + T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32, salt.len() as u32) + .saturating_add(*gas_limit) + )] + pub fn instantiate_with_code( + origin: OriginFor, + #[pallet::compact] value: BalanceOf, + gas_limit: Weight, + #[pallet::compact] storage_deposit_limit: BalanceOf, + code: Vec, + data: Vec, + salt: Vec, + ) -> DispatchResultWithPostInfo { + let code_len = code.len() as u32; + let data_len = data.len() as u32; + let salt_len = salt.len() as u32; + let mut output = Self::bare_instantiate( + origin, + value, + gas_limit, + storage_deposit_limit, + Code::Upload(code), + data, + salt, + DebugInfo::Skip, + CollectEvents::Skip, + ); + if let Ok(retval) = &output.result { + if retval.result.did_revert() { + output.result = Err(>::ContractReverted.into()); + } + } + dispatch_result( + output.result.map(|result| result.result), + output.gas_consumed, + T::WeightInfo::instantiate_with_code(code_len, data_len, salt_len), + ) + } + + /// Upload new `code` without instantiating a contract from it. + /// + /// If the code does not already exist a deposit is reserved from the caller + /// and unreserved only when [`Self::remove_code`] is called. The size of the reserve + /// depends on the size of the supplied `code`. + /// + /// # Note + /// + /// Anyone can instantiate a contract from any uploaded code and thus prevent its removal. + /// To avoid this situation a constructor could employ access control so that it can + /// only be instantiated by permissioned entities. The same is true when uploading + /// through [`Self::instantiate_with_code`]. + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::upload_code_determinism_enforced(code.len() as u32))] + pub fn upload_code( + origin: OriginFor, + code: Vec, + #[pallet::compact] storage_deposit_limit: BalanceOf, + ) -> DispatchResult { + Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ()) + } + + /// Remove the code stored under `code_hash` and refund the deposit to its owner. + /// + /// A code can only be removed by its original uploader (its owner) and only if it is + /// not used by any contract. + #[pallet::call_index(4)] + #[pallet::weight(T::WeightInfo::remove_code())] + pub fn remove_code( + origin: OriginFor, + code_hash: CodeHash, + ) -> DispatchResultWithPostInfo { + Migration::::ensure_migrated()?; + let origin = ensure_signed(origin)?; + >::remove(&origin, code_hash)?; + // we waive the fee because removing unused code is beneficial + Ok(Pays::No.into()) + } + + /// Privileged function that changes the code of an existing contract. + /// + /// This takes care of updating refcounts and all other necessary operations. Returns + /// an error if either the `code_hash` or `dest` do not exist. + /// + /// # Note + /// + /// This does **not** change the address of the contract in question. This means + /// that the contract address is no longer derived from its code hash after calling + /// this dispatchable. + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::set_code())] + pub fn set_code( + origin: OriginFor, + dest: AccountIdLookupOf, + code_hash: CodeHash, + ) -> DispatchResult { + Migration::::ensure_migrated()?; + ensure_root(origin)?; + let dest = T::Lookup::lookup(dest)?; + >::try_mutate(&dest, |contract| { + let contract = if let Some(contract) = contract { + contract + } else { + return Err(>::ContractNotFound.into()) + }; + >>::increment_refcount(code_hash)?; + >>::decrement_refcount(contract.code_hash); + Self::deposit_event(Event::ContractCodeUpdated { + contract: dest.clone(), + new_code_hash: code_hash, + old_code_hash: contract.code_hash, + }); + contract.code_hash = code_hash; + Ok(()) + }) + } + + /// When a migration is in progress, this dispatchable can be used to run migration steps. + /// Calls that contribute to advancing the migration have their fees waived, as it's helpful + /// for the chain. Note that while the migration is in progress, the pallet will also + /// leverage the `on_idle` hooks to run migration steps. + #[pallet::call_index(6)] + #[pallet::weight(T::WeightInfo::migrate().saturating_add(*weight_limit))] + pub fn migrate(origin: OriginFor, weight_limit: Weight) -> DispatchResultWithPostInfo { + use migration::MigrateResult::*; + ensure_signed(origin)?; + + let weight_limit = weight_limit.saturating_add(T::WeightInfo::migrate()); + let mut meter = WeightMeter::with_limit(weight_limit); + let result = Migration::::migrate(&mut meter); + + match result { + Completed => Ok(PostDispatchInfo { + actual_weight: Some(meter.consumed()), + pays_fee: Pays::No, + }), + InProgress { steps_done, .. } if steps_done > 0 => Ok(PostDispatchInfo { + actual_weight: Some(meter.consumed()), + pays_fee: Pays::No, + }), + InProgress { .. } => Ok(PostDispatchInfo { + actual_weight: Some(meter.consumed()), + pays_fee: Pays::Yes, + }), + NoMigrationInProgress | NoMigrationPerformed => { + let err: DispatchError = >::NoMigrationPerformed.into(); + Err(err.with_weight(meter.consumed())) + }, + } + } + } +} + +/// Create a dispatch result reflecting the amount of consumed gas. +fn dispatch_result( + result: Result, + gas_consumed: Weight, + base_weight: Weight, +) -> DispatchResultWithPostInfo { + let post_info = PostDispatchInfo { + actual_weight: Some(gas_consumed.saturating_add(base_weight)), + pays_fee: Default::default(), + }; + + result + .map(|_| post_info) + .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e }) +} + +impl Pallet { + /// A generalized version of [`Self::call`]. + /// + /// Identical to [`Self::call`] but tailored towards being called by other code within the + /// runtime as opposed to from an extrinsic. It returns more information and allows the + /// enablement of features that are not suitable for an extrinsic (debugging, event + /// collection). + pub fn bare_call( + origin: OriginFor, + dest: T::AccountId, + value: BalanceOf, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + data: Vec, + debug: DebugInfo, + collect_events: CollectEvents, + ) -> ContractExecResult, EventRecordOf> { + let mut gas_meter = GasMeter::new(gas_limit); + let mut storage_deposit = Default::default(); + let mut debug_message = if matches!(debug, DebugInfo::UnsafeDebug) { + Some(DebugBuffer::default()) + } else { + None + }; + let try_call = || { + Migration::::ensure_migrated()?; + let origin = Origin::from_runtime_origin(origin)?; + let mut storage_meter = StorageMeter::new(&origin, storage_deposit_limit, value)?; + let result = ExecStack::>::run_call( + origin.clone(), + dest, + &mut gas_meter, + &mut storage_meter, + value, + data, + debug_message.as_mut(), + )?; + storage_deposit = storage_meter.try_into_deposit(&origin)?; + Ok(result) + }; + let result = Self::run_guarded(try_call); + let events = if matches!(collect_events, CollectEvents::UnsafeCollect) { + Some(System::::read_events_no_consensus().map(|e| *e).collect()) + } else { + None + }; + ContractExecResult { + result: result.map_err(|r| r.error), + gas_consumed: gas_meter.gas_consumed(), + gas_required: gas_meter.gas_required(), + storage_deposit, + debug_message: debug_message.unwrap_or_default().to_vec(), + events, + } + } + + /// A generalized version of [`Self::instantiate`] or [`Self::instantiate_with_code`]. + /// + /// Identical to [`Self::instantiate`] or [`Self::instantiate_with_code`] but tailored towards + /// being called by other code within the runtime as opposed to from an extrinsic. It returns + /// more information and allows the enablement of features that are not suitable for an + /// extrinsic (debugging, event collection). + pub fn bare_instantiate( + origin: OriginFor, + value: BalanceOf, + gas_limit: Weight, + mut storage_deposit_limit: BalanceOf, + code: Code>, + data: Vec, + salt: Vec, + debug: DebugInfo, + collect_events: CollectEvents, + ) -> ContractInstantiateResult, EventRecordOf> { + let mut gas_meter = GasMeter::new(gas_limit); + let mut storage_deposit = Default::default(); + let mut debug_message = + if debug == DebugInfo::UnsafeDebug { Some(DebugBuffer::default()) } else { None }; + let try_instantiate = || { + Migration::::ensure_migrated()?; + let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?; + let (executable, upload_deposit) = match code { + Code::Upload(code) => { + let upload_account = T::UploadOrigin::ensure_origin(origin)?; + let (executable, upload_deposit) = Self::try_upload_code( + upload_account, + code, + storage_deposit_limit, + debug_message.as_mut(), + )?; + storage_deposit_limit.saturating_reduce(upload_deposit); + (executable, upload_deposit) + }, + Code::Existing(code_hash) => + (WasmBlob::from_storage(code_hash, &mut gas_meter)?, Default::default()), + }; + let instantiate_origin = Origin::from_account_id(instantiate_account.clone()); + let mut storage_meter = + StorageMeter::new(&instantiate_origin, storage_deposit_limit, value)?; + let result = ExecStack::>::run_instantiate( + instantiate_account, + executable, + &mut gas_meter, + &mut storage_meter, + value, + data, + &salt, + debug_message.as_mut(), + ); + storage_deposit = storage_meter + .try_into_deposit(&instantiate_origin)? + .saturating_add(&StorageDeposit::Charge(upload_deposit)); + result + }; + let output = Self::run_guarded(try_instantiate); + let events = if matches!(collect_events, CollectEvents::UnsafeCollect) { + Some(System::::read_events_no_consensus().map(|e| *e).collect()) + } else { + None + }; + ContractInstantiateResult { + result: output + .map(|(account_id, result)| InstantiateReturnValue { result, account_id }) + .map_err(|e| e.error), + gas_consumed: gas_meter.gas_consumed(), + gas_required: gas_meter.gas_required(), + storage_deposit, + debug_message: debug_message.unwrap_or_default().to_vec(), + events, + } + } + + /// A generalized version of [`Self::upload_code`]. + /// + /// It is identical to [`Self::upload_code`] and only differs in the information it returns. + pub fn bare_upload_code( + origin: OriginFor, + code: Vec, + storage_deposit_limit: BalanceOf, + ) -> CodeUploadResult, BalanceOf> { + Migration::::ensure_migrated()?; + let origin = T::UploadOrigin::ensure_origin(origin)?; + let (module, deposit) = Self::try_upload_code(origin, code, storage_deposit_limit, None)?; + Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit }) + } + + /// Query storage of a specified contract under a specified key. + pub fn get_storage(address: T::AccountId, key: Vec) -> GetStorageResult { + if Migration::::in_progress() { + return Err(ContractAccessError::MigrationInProgress) + } + let contract_info = + ContractInfoOf::::get(&address).ok_or(ContractAccessError::DoesntExist)?; + + let maybe_value = contract_info.read( + &Key::try_from_var(key) + .map_err(|_| ContractAccessError::KeyDecodingFailed)? + .into(), + ); + Ok(maybe_value) + } + + /// Determine the address of a contract. + /// + /// This is the address generation function used by contract instantiation. See + /// [`DefaultAddressGenerator`] for the default implementation. + pub fn contract_address( + deploying_address: &T::AccountId, + code_hash: &CodeHash, + input_data: &[u8], + salt: &[u8], + ) -> T::AccountId { + T::AddressGenerator::contract_address(deploying_address, code_hash, input_data, salt) + } + + /// Uploads new code and returns the Wasm blob and deposit amount collected. + fn try_upload_code( + origin: T::AccountId, + code: Vec, + storage_deposit_limit: BalanceOf, + mut debug_message: Option<&mut DebugBuffer>, + ) -> Result<(WasmBlob, BalanceOf), DispatchError> { + let mut module = WasmBlob::from_code(code, origin).map_err(|(err, msg)| { + debug_message.as_mut().map(|d| d.try_extend(msg.bytes())); + err + })?; + let deposit = module.store_code()?; + ensure!(storage_deposit_limit >= deposit, >::StorageDepositLimitExhausted); + Ok((module, deposit)) + } + + /// Deposit a pallet contracts event. + fn deposit_event(event: Event) { + >::deposit_event(::RuntimeEvent::from(event)) + } + + /// Deposit a pallet contracts indexed event. + fn deposit_indexed_event(topics: Vec, event: Event) { + >::deposit_event_indexed( + &topics, + ::RuntimeEvent::from(event).into(), + ) + } + + /// Return the existential deposit of [`Config::Currency`]. + fn min_balance() -> BalanceOf { + >>::minimum_balance() + } + + /// Run the supplied function `f` if no other instance of this pallet is on the stack. + fn run_guarded Result>(f: F) -> Result { + executing_contract::using_once(&mut false, || { + executing_contract::with(|f| { + // Fail if already entered contract execution + if *f { + return Err(()) + } + // We are entering contract execution + *f = true; + Ok(()) + }) + .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed") + .map_err(|_| >::ReenteredPallet.into()) + .map(|_| f()) + .and_then(|r| r) + }) + } +} + +// Set up a global reference to the boolean flag used for the re-entrancy guard. +environmental!(executing_contract: bool); + +sp_api::decl_runtime_apis! { + /// The API used to dry-run contract interactions. + #[api_version(1)] + pub trait ReviveApi where + AccountId: Codec, + Balance: Codec, + BlockNumber: Codec, + Hash: Codec, + EventRecord: Codec, + { + /// Perform a call from a specified account to a given contract. + /// + /// See [`crate::Pallet::bare_call`]. + fn call( + origin: AccountId, + dest: AccountId, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + input_data: Vec, + ) -> ContractExecResult; + + /// Instantiate a new contract. + /// + /// See `[crate::Pallet::bare_instantiate]`. + fn instantiate( + origin: AccountId, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + code: Code, + data: Vec, + salt: Vec, + ) -> ContractInstantiateResult; + + /// Upload new code without instantiating a contract from it. + /// + /// See [`crate::Pallet::bare_upload_code`]. + fn upload_code( + origin: AccountId, + code: Vec, + storage_deposit_limit: Option, + ) -> CodeUploadResult; + + /// Query a given storage key in a given contract. + /// + /// Returns `Ok(Some(Vec))` if the storage value exists under the given key in the + /// specified account and `Ok(None)` if it doesn't. If the account specified by the address + /// doesn't exist, or doesn't have a contract then `Err` is returned. + fn get_storage( + address: AccountId, + key: Vec, + ) -> GetStorageResult; + } +} diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs new file mode 100644 index 000000000000..1a714a89d486 --- /dev/null +++ b/substrate/frame/revive/src/limits.rs @@ -0,0 +1,60 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Limits that are observeable by contract code. +//! +//! It is important to never change this limits without supporting the old limits +//! for already deployed contracts. This is what the [`crate::Contract::behaviour_version`] +//! is meant for. This is true for either increasing or decreasing the limit. +//! +//! Limits in this file are different from the limits configured on the [`Config`] trait which are +//! generally only affect actions that cannot be performed by a contract: For example, uploading new +//! code only be done via a transaction but not by a contract. Hence the maximum contract size can +//! be raised (but not lowered) by the runtime configuration. + +/// The maximum depth of the call stack. +/// +/// A 0 means that no callings of other contracts are possible. In other words only the origin +/// called "root contract" is allowed to execute then. +pub const CALL_STACK_DEPTH: u32 = 5; + +/// The maximum number of topics a call to [`crate::SyscallDoc::deposit_event`] can emit. +/// +/// We set it to the same limit that ethereum has. It is unlikely to change. +pub const NUM_EVENT_TOPICS: u32 = 4; + +/// The maximum number of code hashes a contract can lock. +pub const DELEGATE_DEPENDENCIES: u32 = 32; + +/// How much memory do we allow the contract to allocate. +pub const MEMORY_BYTES: u32 = 16 * 64 * 1024; + +/// Maximum size of events (excluding topics) and storage values. +pub const PAYLOAD_BYTES: u32 = 512; + +/// The maximum size of the transient storage in bytes. +/// +/// This includes keys, values, and previous entries used for storage rollback. +pub const TRANSIENT_STORAGE_BYTES: u32 = 4 * 1024; + +/// The maximum allowable length in bytes for (transient) storage keys. +pub const STORAGE_KEY_BYTES: u32 = 128; + +/// The maximum size of the debug buffer contracts can write messages to. +/// +/// The buffer will always be disabled for on-chain execution. +pub const DEBUG_BUFFER_BYTES: u32 = 2 * 1024 * 1024; diff --git a/substrate/frame/revive/src/migration.rs b/substrate/frame/revive/src/migration.rs new file mode 100644 index 000000000000..b67467b322f5 --- /dev/null +++ b/substrate/frame/revive/src/migration.rs @@ -0,0 +1,650 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Multi-block Migration framework for pallet-revive. +//! +//! This module allows us to define a migration as a sequence of [`MigrationStep`]s that can be +//! executed across multiple blocks. +//! +//! # Usage +//! +//! A migration step is defined under `src/migration/vX.rs`, where `X` is the version number. +//! For example, `vX.rs` defines a migration from version `X - 1` to version `X`. +//! +//! ## Example: +//! +//! To configure a migration to `v11` for a runtime using `v10` of pallet-revive on the chain, +//! you would set the `Migrations` type as follows: +//! +//! ```ignore +//! use pallet_revive::migration::{v10, v11}; +//! # pub enum Runtime {}; +//! # struct Currency; +//! type Migrations = (v10::Migration, v11::Migration); +//! ``` +//! +//! ## Notes: +//! +//! - Migrations should always be tested with `try-runtime` before being deployed. +//! - By testing with `try-runtime` against a live network, you ensure that all migration steps work +//! and that you have included the required steps. +//! +//! ## Low Level / Implementation Details +//! +//! When a migration starts and [`OnRuntimeUpgrade::on_runtime_upgrade`] is called, instead of +//! performing the actual migration, we set a custom storage item [`MigrationInProgress`]. +//! This storage item defines a [`Cursor`] for the current migration. +//! +//! If the [`MigrationInProgress`] storage item exists, it means a migration is in progress, and its +//! value holds a cursor for the current migration step. These migration steps are executed during +//! [`Hooks::on_idle`] or when the [`Pallet::migrate`] dispatchable is +//! called. +//! +//! While the migration is in progress, all dispatchables except `migrate`, are blocked, and returns +//! a `MigrationInProgress` error. + +include!(concat!(env!("OUT_DIR"), "/migration_codegen.rs")); + +use crate::{weights::WeightInfo, Config, Error, MigrationInProgress, Pallet, Weight, LOG_TARGET}; +use codec::{Codec, Decode}; +use core::marker::PhantomData; +use frame_support::{ + pallet_prelude::*, + traits::{ConstU32, OnRuntimeUpgrade}, + weights::WeightMeter, +}; +use sp_runtime::Saturating; + +#[cfg(feature = "try-runtime")] +use alloc::vec::Vec; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + +const PROOF_ENCODE: &str = "Tuple::max_encoded_len() < Cursor::max_encoded_len()` is verified in `Self::integrity_test()`; qed"; +const PROOF_DECODE: &str = + "We encode to the same type in this trait only. No other code touches this item; qed"; + +fn invalid_version(version: StorageVersion) -> ! { + panic!("Required migration {version:?} not supported by this runtime. This is a bug."); +} + +/// The cursor used to encode the position (usually the last iterated key) of the current migration +/// step. +pub type Cursor = BoundedVec>; + +/// IsFinished describes whether a migration is finished or not. +pub enum IsFinished { + Yes, + No, +} + +/// A trait that allows to migrate storage from one version to another. +/// +/// The migration is done in steps. The migration is finished when +/// `step()` returns `IsFinished::Yes`. +pub trait MigrationStep: Codec + MaxEncodedLen + Default { + /// Returns the version of the migration. + const VERSION: u16; + + /// Returns the maximum weight that can be consumed in a single step. + fn max_step_weight() -> Weight; + + /// Process one step of the migration. + /// + /// Returns whether the migration is finished. + fn step(&mut self, meter: &mut WeightMeter) -> IsFinished; + + /// Verify that the migration step fits into `Cursor`, and that `max_step_weight` is not greater + /// than `max_block_weight`. + fn integrity_test(max_block_weight: Weight) { + if Self::max_step_weight().any_gt(max_block_weight) { + panic!( + "Invalid max_step_weight for Migration {}. Value should be lower than {}", + Self::VERSION, + max_block_weight + ); + } + + let len = ::max_encoded_len(); + let max = Cursor::bound(); + if len > max { + panic!( + "Migration {} has size {} which is bigger than the maximum of {}", + Self::VERSION, + len, + max, + ); + } + } + + /// Execute some pre-checks prior to running the first step of this migration. + #[cfg(feature = "try-runtime")] + fn pre_upgrade_step() -> Result, TryRuntimeError> { + Ok(Vec::new()) + } + + /// Execute some post-checks after running the last step of this migration. + #[cfg(feature = "try-runtime")] + fn post_upgrade_step(_state: Vec) -> Result<(), TryRuntimeError> { + Ok(()) + } +} + +/// A noop migration that can be used when there is no migration to be done for a given version. +#[doc(hidden)] +#[derive(frame_support::DefaultNoBound, Encode, Decode, MaxEncodedLen)] +pub struct NoopMigration; + +impl MigrationStep for NoopMigration { + const VERSION: u16 = N; + fn max_step_weight() -> Weight { + Weight::zero() + } + fn step(&mut self, _meter: &mut WeightMeter) -> IsFinished { + log::debug!(target: LOG_TARGET, "Noop migration for version {}", N); + IsFinished::Yes + } +} + +mod private { + use crate::migration::MigrationStep; + pub trait Sealed {} + #[impl_trait_for_tuples::impl_for_tuples(10)] + #[tuple_types_custom_trait_bound(MigrationStep)] + impl Sealed for Tuple {} +} + +/// Defines a sequence of migrations. +/// +/// The sequence must be defined by a tuple of migrations, each of which must implement the +/// `MigrationStep` trait. Migrations must be ordered by their versions with no gaps. +pub trait MigrateSequence: private::Sealed { + /// Returns the range of versions that this migrations sequence can handle. + /// Migrations must be ordered by their versions with no gaps. + /// + /// The following code will fail to compile: + /// + /// ```compile_fail + /// # use pallet_revive::{NoopMigration, MigrateSequence}; + /// let _ = <(NoopMigration<1>, NoopMigration<3>)>::VERSION_RANGE; + /// ``` + /// The following code will compile: + /// ``` + /// # use pallet_revive::{NoopMigration, MigrateSequence}; + /// let _ = <(NoopMigration<1>, NoopMigration<2>)>::VERSION_RANGE; + /// ``` + const VERSION_RANGE: (u16, u16); + + /// Returns the default cursor for the given version. + fn new(version: StorageVersion) -> Cursor; + + #[cfg(feature = "try-runtime")] + fn pre_upgrade_step(_version: StorageVersion) -> Result, TryRuntimeError> { + Ok(Vec::new()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade_step(_version: StorageVersion, _state: Vec) -> Result<(), TryRuntimeError> { + Ok(()) + } + + /// Execute the migration step until the available weight is consumed. + fn steps(version: StorageVersion, cursor: &[u8], meter: &mut WeightMeter) -> StepResult; + + /// Verify that the migration step fits into `Cursor`, and that `max_step_weight` is not greater + /// than `max_block_weight`. + fn integrity_test(max_block_weight: Weight); + + /// Returns whether migrating from `in_storage` to `target` is supported. + /// + /// A migration is supported if `VERSION_RANGE` is (in_storage + 1, target). + fn is_upgrade_supported(in_storage: StorageVersion, target: StorageVersion) -> bool { + let (low, high) = Self::VERSION_RANGE; + target == high && in_storage + 1 == low + } +} + +/// Performs all necessary migrations based on `StorageVersion`. +/// +/// If `TEST_ALL_STEPS == true` and `try-runtime` is enabled, this will run all the migrations +/// inside `on_runtime_upgrade`. This should be set to false in tests that want to ensure the step +/// by step migration works. +pub struct Migration(PhantomData); + +#[cfg(feature = "try-runtime")] +impl Migration { + fn run_all_steps() -> Result<(), TryRuntimeError> { + let mut meter = &mut WeightMeter::new(); + let name = >::name(); + loop { + let in_progress_version = >::on_chain_storage_version() + 1; + let state = T::Migrations::pre_upgrade_step(in_progress_version)?; + let before = meter.consumed(); + let status = Self::migrate(&mut meter); + log::info!( + target: LOG_TARGET, + "{name}: Migration step {:?} weight = {}", + in_progress_version, + meter.consumed() - before + ); + T::Migrations::post_upgrade_step(in_progress_version, state)?; + if matches!(status, MigrateResult::Completed) { + break + } + } + + let name = >::name(); + log::info!(target: LOG_TARGET, "{name}: Migration steps weight = {}", meter.consumed()); + Ok(()) + } +} + +impl OnRuntimeUpgrade for Migration { + fn on_runtime_upgrade() -> Weight { + let name = >::name(); + let in_code_version = >::in_code_storage_version(); + let on_chain_version = >::on_chain_storage_version(); + + if on_chain_version == in_code_version { + log::warn!( + target: LOG_TARGET, + "{name}: No Migration performed storage_version = latest_version = {:?}", + &on_chain_version + ); + return T::WeightInfo::on_runtime_upgrade_noop() + } + + // In case a migration is already in progress we create the next migration + // (if any) right when the current one finishes. + if Self::in_progress() { + log::warn!( + target: LOG_TARGET, + "{name}: Migration already in progress {:?}", + &on_chain_version + ); + + return T::WeightInfo::on_runtime_upgrade_in_progress() + } + + log::info!( + target: LOG_TARGET, + "{name}: Upgrading storage from {on_chain_version:?} to {in_code_version:?}.", + ); + + let cursor = T::Migrations::new(on_chain_version + 1); + MigrationInProgress::::set(Some(cursor)); + + #[cfg(feature = "try-runtime")] + if TEST_ALL_STEPS { + Self::run_all_steps().unwrap(); + } + + T::WeightInfo::on_runtime_upgrade() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, TryRuntimeError> { + // We can't really do much here as our migrations do not happen during the runtime upgrade. + // Instead, we call the migrations `pre_upgrade` and `post_upgrade` hooks when we iterate + // over our migrations. + let on_chain_version = >::on_chain_storage_version(); + let in_code_version = >::in_code_storage_version(); + + if on_chain_version == in_code_version { + return Ok(Default::default()) + } + + log::debug!( + target: LOG_TARGET, + "Requested migration of {} from {:?}(on-chain storage version) to {:?}(in-code storage version)", + >::name(), on_chain_version, in_code_version + ); + + ensure!( + T::Migrations::is_upgrade_supported(on_chain_version, in_code_version), + "Unsupported upgrade: VERSION_RANGE should be (on-chain storage version + 1, in-code storage version)" + ); + + Ok(Default::default()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { + if !TEST_ALL_STEPS { + return Ok(()) + } + + log::info!(target: LOG_TARGET, "=== POST UPGRADE CHECKS ==="); + + // Ensure that the hashing algorithm is correct for each storage map. + if let Some(hash) = crate::CodeInfoOf::::iter_keys().next() { + crate::CodeInfoOf::::get(hash).expect("CodeInfo exists for hash; qed"); + } + if let Some(hash) = crate::PristineCode::::iter_keys().next() { + crate::PristineCode::::get(hash).expect("PristineCode exists for hash; qed"); + } + if let Some(account_id) = crate::ContractInfoOf::::iter_keys().next() { + crate::ContractInfoOf::::get(account_id) + .expect("ContractInfo exists for account_id; qed"); + } + if let Some(nonce) = crate::DeletionQueue::::iter_keys().next() { + crate::DeletionQueue::::get(nonce).expect("DeletionQueue exists for nonce; qed"); + } + + Ok(()) + } +} + +/// The result of running the migration. +#[derive(Debug, PartialEq)] +pub enum MigrateResult { + /// No migration was performed + NoMigrationPerformed, + /// No migration currently in progress + NoMigrationInProgress, + /// A migration is in progress + InProgress { steps_done: u32 }, + /// All migrations are completed + Completed, +} + +/// The result of running a migration step. +#[derive(Debug, PartialEq)] +pub enum StepResult { + InProgress { cursor: Cursor, steps_done: u32 }, + Completed { steps_done: u32 }, +} + +impl Migration { + /// Verify that each migration's step of the [`Config::Migrations`] sequence fits into + /// `Cursor`. + pub(crate) fn integrity_test() { + let max_weight = ::BlockWeights::get().max_block; + T::Migrations::integrity_test(max_weight) + } + + /// Execute the multi-step migration. + /// Returns whether or not a migration is in progress + pub(crate) fn migrate(mut meter: &mut WeightMeter) -> MigrateResult { + let name = >::name(); + + if meter.try_consume(T::WeightInfo::migrate()).is_err() { + return MigrateResult::NoMigrationPerformed + } + + MigrationInProgress::::mutate_exists(|progress| { + let Some(cursor_before) = progress.as_mut() else { + meter.consume(T::WeightInfo::migration_noop()); + return MigrateResult::NoMigrationInProgress + }; + + // if a migration is running it is always upgrading to the next version + let storage_version = >::on_chain_storage_version(); + let in_progress_version = storage_version + 1; + + log::info!( + target: LOG_TARGET, + "{name}: Migrating from {:?} to {:?},", + storage_version, + in_progress_version, + ); + + let result = + match T::Migrations::steps(in_progress_version, cursor_before.as_ref(), &mut meter) + { + StepResult::InProgress { cursor, steps_done } => { + *progress = Some(cursor); + MigrateResult::InProgress { steps_done } + }, + StepResult::Completed { steps_done } => { + in_progress_version.put::>(); + if >::in_code_storage_version() != in_progress_version { + log::info!( + target: LOG_TARGET, + "{name}: Next migration is {:?},", + in_progress_version + 1 + ); + *progress = Some(T::Migrations::new(in_progress_version + 1)); + MigrateResult::InProgress { steps_done } + } else { + log::info!( + target: LOG_TARGET, + "{name}: All migrations done. At version {:?},", + in_progress_version + ); + *progress = None; + MigrateResult::Completed + } + }, + }; + + result + }) + } + + pub(crate) fn ensure_migrated() -> DispatchResult { + if Self::in_progress() { + Err(Error::::MigrationInProgress.into()) + } else { + Ok(()) + } + } + + pub(crate) fn in_progress() -> bool { + MigrationInProgress::::exists() + } +} + +#[impl_trait_for_tuples::impl_for_tuples(10)] +#[tuple_types_custom_trait_bound(MigrationStep)] +impl MigrateSequence for Tuple { + const VERSION_RANGE: (u16, u16) = { + let mut versions: (u16, u16) = (0, 0); + for_tuples!( + #( + match versions { + (0, 0) => { + versions = (Tuple::VERSION, Tuple::VERSION); + }, + (min_version, last_version) if Tuple::VERSION == last_version + 1 => { + versions = (min_version, Tuple::VERSION); + }, + _ => panic!("Migrations must be ordered by their versions with no gaps.") + } + )* + ); + versions + }; + + fn new(version: StorageVersion) -> Cursor { + for_tuples!( + #( + if version == Tuple::VERSION { + return Tuple::default().encode().try_into().expect(PROOF_ENCODE) + } + )* + ); + invalid_version(version) + } + + #[cfg(feature = "try-runtime")] + /// Execute the pre-checks of the step associated with this version. + fn pre_upgrade_step(version: StorageVersion) -> Result, TryRuntimeError> { + for_tuples!( + #( + if version == Tuple::VERSION { + return Tuple::pre_upgrade_step() + } + )* + ); + invalid_version(version) + } + + #[cfg(feature = "try-runtime")] + /// Execute the post-checks of the step associated with this version. + fn post_upgrade_step(version: StorageVersion, state: Vec) -> Result<(), TryRuntimeError> { + for_tuples!( + #( + if version == Tuple::VERSION { + return Tuple::post_upgrade_step(state) + } + )* + ); + invalid_version(version) + } + + fn steps(version: StorageVersion, mut cursor: &[u8], meter: &mut WeightMeter) -> StepResult { + for_tuples!( + #( + if version == Tuple::VERSION { + let mut migration = ::decode(&mut cursor) + .expect(PROOF_DECODE); + let max_weight = Tuple::max_step_weight(); + let mut steps_done = 0; + while meter.can_consume(max_weight) { + steps_done.saturating_accrue(1); + if matches!(migration.step(meter), IsFinished::Yes) { + return StepResult::Completed{ steps_done } + } + } + return StepResult::InProgress{cursor: migration.encode().try_into().expect(PROOF_ENCODE), steps_done } + } + )* + ); + invalid_version(version) + } + + fn integrity_test(max_block_weight: Weight) { + for_tuples!( + #( + Tuple::integrity_test(max_block_weight); + )* + ); + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + migration::codegen::LATEST_MIGRATION_VERSION, + tests::{ExtBuilder, Test}, + }; + + #[derive(Default, Encode, Decode, MaxEncodedLen)] + struct MockMigration { + // MockMigration needs `N` steps to finish + count: u16, + } + + impl MigrationStep for MockMigration { + const VERSION: u16 = N; + fn max_step_weight() -> Weight { + Weight::from_all(1) + } + fn step(&mut self, meter: &mut WeightMeter) -> IsFinished { + assert!(self.count != N); + self.count += 1; + meter.consume(Weight::from_all(1)); + if self.count == N { + IsFinished::Yes + } else { + IsFinished::No + } + } + } + + #[test] + fn test_storage_version_matches_last_migration_file() { + assert_eq!(StorageVersion::new(LATEST_MIGRATION_VERSION), crate::pallet::STORAGE_VERSION); + } + + #[test] + fn version_range_works() { + let range = <(MockMigration<1>, MockMigration<2>)>::VERSION_RANGE; + assert_eq!(range, (1, 2)); + } + + #[test] + fn is_upgrade_supported_works() { + type Migrations = (MockMigration<9>, MockMigration<10>, MockMigration<11>); + assert!(Migrations::is_upgrade_supported(StorageVersion::new(8), StorageVersion::new(11))); + assert!(!Migrations::is_upgrade_supported(StorageVersion::new(9), StorageVersion::new(11))); + assert!(!Migrations::is_upgrade_supported(StorageVersion::new(8), StorageVersion::new(12))); + } + + #[test] + fn steps_works() { + type Migrations = (MockMigration<2>, MockMigration<3>); + let version = StorageVersion::new(2); + let mut cursor = Migrations::new(version); + + let mut meter = WeightMeter::with_limit(Weight::from_all(1)); + let result = Migrations::steps(version, &cursor, &mut meter); + cursor = alloc::vec![1u8, 0].try_into().unwrap(); + assert_eq!(result, StepResult::InProgress { cursor: cursor.clone(), steps_done: 1 }); + assert_eq!(meter.consumed(), Weight::from_all(1)); + + let mut meter = WeightMeter::with_limit(Weight::from_all(1)); + assert_eq!( + Migrations::steps(version, &cursor, &mut meter), + StepResult::Completed { steps_done: 1 } + ); + } + + #[test] + fn no_migration_in_progress_works() { + type TestMigration = Migration; + + ExtBuilder::default().build().execute_with(|| { + assert_eq!(StorageVersion::get::>(), LATEST_MIGRATION_VERSION); + assert_eq!( + TestMigration::migrate(&mut WeightMeter::new()), + MigrateResult::NoMigrationInProgress + ) + }); + } + + #[test] + fn migration_works() { + type TestMigration = Migration; + + ExtBuilder::default() + .set_storage_version(LATEST_MIGRATION_VERSION - 2) + .build() + .execute_with(|| { + assert_eq!(StorageVersion::get::>(), LATEST_MIGRATION_VERSION - 2); + TestMigration::on_runtime_upgrade(); + for (version, status) in [ + (LATEST_MIGRATION_VERSION - 1, MigrateResult::InProgress { steps_done: 1 }), + (LATEST_MIGRATION_VERSION, MigrateResult::Completed), + ] { + assert_eq!(TestMigration::migrate(&mut WeightMeter::new()), status); + assert_eq!( + >::on_chain_storage_version(), + StorageVersion::new(version) + ); + } + + assert_eq!( + TestMigration::migrate(&mut WeightMeter::new()), + MigrateResult::NoMigrationInProgress + ); + assert_eq!(StorageVersion::get::>(), LATEST_MIGRATION_VERSION); + }); + } +} diff --git a/substrate/frame/revive/src/primitives.rs b/substrate/frame/revive/src/primitives.rs new file mode 100644 index 000000000000..a4a1133b7104 --- /dev/null +++ b/substrate/frame/revive/src/primitives.rs @@ -0,0 +1,285 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! A crate that hosts a common definitions that are relevant for the pallet-revive. + +use alloc::vec::Vec; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::weights::Weight; +use pallet_revive_uapi::ReturnFlags; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{Saturating, Zero}, + DispatchError, RuntimeDebug, +}; + +/// Result type of a `bare_call` or `bare_instantiate` call as well as `ContractsApi::call` and +/// `ContractsApi::instantiate`. +/// +/// It contains the execution result together with some auxiliary information. +/// +/// #Note +/// +/// It has been extended to include `events` at the end of the struct while not bumping the +/// `ContractsApi` version. Therefore when SCALE decoding a `ContractResult` its trailing data +/// should be ignored to avoid any potential compatibility issues. +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct ContractResult { + /// How much weight was consumed during execution. + pub gas_consumed: Weight, + /// How much weight is required as gas limit in order to execute this call. + /// + /// This value should be used to determine the weight limit for on-chain execution. + /// + /// # Note + /// + /// This can only different from [`Self::gas_consumed`] when weight pre charging + /// is used. Currently, only `seal_call_runtime` makes use of pre charging. + /// Additionally, any `seal_call` or `seal_instantiate` makes use of pre-charging + /// when a non-zero `gas_limit` argument is supplied. + pub gas_required: Weight, + /// How much balance was paid by the origin into the contract's deposit account in order to + /// pay for storage. + /// + /// The storage deposit is never actually charged from the origin in case of [`Self::result`] + /// is `Err`. This is because on error all storage changes are rolled back including the + /// payment of the deposit. + pub storage_deposit: StorageDeposit, + /// An optional debug message. This message is only filled when explicitly requested + /// by the code that calls into the contract. Otherwise it is empty. + /// + /// The contained bytes are valid UTF-8. This is not declared as `String` because + /// this type is not allowed within the runtime. + /// + /// Clients should not make any assumptions about the format of the buffer. + /// They should just display it as-is. It is **not** only a collection of log lines + /// provided by a contract but a formatted buffer with different sections. + /// + /// # Note + /// + /// The debug message is never generated during on-chain execution. It is reserved for + /// RPC calls. + pub debug_message: Vec, + /// The execution result of the wasm code. + pub result: R, + /// The events that were emitted during execution. It is an option as event collection is + /// optional. + pub events: Option>, +} + +/// Result type of a `bare_call` call as well as `ContractsApi::call`. +pub type ContractExecResult = + ContractResult, Balance, EventRecord>; + +/// Result type of a `bare_instantiate` call as well as `ContractsApi::instantiate`. +pub type ContractInstantiateResult = + ContractResult, DispatchError>, Balance, EventRecord>; + +/// Result type of a `bare_code_upload` call. +pub type CodeUploadResult = + Result, DispatchError>; + +/// Result type of a `get_storage` call. +pub type GetStorageResult = Result>, ContractAccessError>; + +/// The possible errors that can happen querying the storage of a contract. +#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)] +pub enum ContractAccessError { + /// The given address doesn't point to a contract. + DoesntExist, + /// Storage key cannot be decoded from the provided input data. + KeyDecodingFailed, + /// Storage is migrating. Try again later. + MigrationInProgress, +} + +/// Output of a contract call or instantiation which ran to completion. +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct ExecReturnValue { + /// Flags passed along by `seal_return`. Empty when `seal_return` was never called. + pub flags: ReturnFlags, + /// Buffer passed along by `seal_return`. Empty when `seal_return` was never called. + pub data: Vec, +} + +impl ExecReturnValue { + /// The contract did revert all storage changes. + pub fn did_revert(&self) -> bool { + self.flags.contains(ReturnFlags::REVERT) + } +} + +/// The result of a successful contract instantiation. +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct InstantiateReturnValue { + /// The output of the called constructor. + pub result: ExecReturnValue, + /// The account id of the new contract. + pub account_id: AccountId, +} + +/// The result of successfully uploading a contract. +#[derive(Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)] +pub struct CodeUploadReturnValue { + /// The key under which the new code is stored. + pub code_hash: CodeHash, + /// The deposit that was reserved at the caller. Is zero when the code already existed. + pub deposit: Balance, +} + +/// Reference to an existing code hash or a new wasm module. +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum Code { + /// A wasm module as raw bytes. + Upload(Vec), + /// The code hash of an on-chain wasm blob. + Existing(Hash), +} + +/// The amount of balance that was either charged or refunded in order to pay for storage. +#[derive( + Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo, +)] +pub enum StorageDeposit { + /// The transaction reduced storage consumption. + /// + /// This means that the specified amount of balance was transferred from the involved + /// deposit accounts to the origin. + Refund(Balance), + /// The transaction increased storage consumption. + /// + /// This means that the specified amount of balance was transferred from the origin + /// to the involved deposit accounts. + Charge(Balance), +} + +impl Default for StorageDeposit { + fn default() -> Self { + Self::Charge(Zero::zero()) + } +} + +impl StorageDeposit { + /// Returns how much balance is charged or `0` in case of a refund. + pub fn charge_or_zero(&self) -> Balance { + match self { + Self::Charge(amount) => *amount, + Self::Refund(_) => Zero::zero(), + } + } + + pub fn is_zero(&self) -> bool { + match self { + Self::Charge(amount) => amount.is_zero(), + Self::Refund(amount) => amount.is_zero(), + } + } +} + +impl StorageDeposit +where + Balance: Saturating + Ord + Copy, +{ + /// This is essentially a saturating signed add. + pub fn saturating_add(&self, rhs: &Self) -> Self { + use StorageDeposit::*; + match (self, rhs) { + (Charge(lhs), Charge(rhs)) => Charge(lhs.saturating_add(*rhs)), + (Refund(lhs), Refund(rhs)) => Refund(lhs.saturating_add(*rhs)), + (Charge(lhs), Refund(rhs)) => + if lhs >= rhs { + Charge(lhs.saturating_sub(*rhs)) + } else { + Refund(rhs.saturating_sub(*lhs)) + }, + (Refund(lhs), Charge(rhs)) => + if lhs > rhs { + Refund(lhs.saturating_sub(*rhs)) + } else { + Charge(rhs.saturating_sub(*lhs)) + }, + } + } + + /// This is essentially a saturating signed sub. + pub fn saturating_sub(&self, rhs: &Self) -> Self { + use StorageDeposit::*; + match (self, rhs) { + (Charge(lhs), Refund(rhs)) => Charge(lhs.saturating_add(*rhs)), + (Refund(lhs), Charge(rhs)) => Refund(lhs.saturating_add(*rhs)), + (Charge(lhs), Charge(rhs)) => + if lhs >= rhs { + Charge(lhs.saturating_sub(*rhs)) + } else { + Refund(rhs.saturating_sub(*lhs)) + }, + (Refund(lhs), Refund(rhs)) => + if lhs > rhs { + Refund(lhs.saturating_sub(*rhs)) + } else { + Charge(rhs.saturating_sub(*lhs)) + }, + } + } + + /// If the amount of deposit (this type) is constrained by a `limit` this calculates how + /// much balance (if any) is still available from this limit. + /// + /// # Note + /// + /// In case of a refund the return value can be larger than `limit`. + pub fn available(&self, limit: &Balance) -> Balance { + use StorageDeposit::*; + match self { + Charge(amount) => limit.saturating_sub(*amount), + Refund(amount) => limit.saturating_add(*amount), + } + } +} + +/// Determines whether events should be collected during execution. +#[derive( + Copy, Clone, PartialEq, Eq, RuntimeDebug, Decode, Encode, MaxEncodedLen, scale_info::TypeInfo, +)] +pub enum CollectEvents { + /// Collect events. + /// + /// # Note + /// + /// Events should only be collected when called off-chain, as this would otherwise + /// collect all the Events emitted in the block so far and put them into the PoV. + /// + /// **Never** use this mode for on-chain execution. + UnsafeCollect, + /// Skip event collection. + Skip, +} + +/// Determines whether debug messages will be collected. +#[derive( + Copy, Clone, PartialEq, Eq, RuntimeDebug, Decode, Encode, MaxEncodedLen, scale_info::TypeInfo, +)] +pub enum DebugInfo { + /// Collect debug messages. + /// # Note + /// + /// This should only ever be set to `UnsafeDebug` when executing as an RPC because + /// it adds allocations and could be abused to drive the runtime into an OOM panic. + UnsafeDebug, + /// Skip collection of debug messages. + Skip, +} diff --git a/substrate/frame/revive/src/storage.rs b/substrate/frame/revive/src/storage.rs new file mode 100644 index 000000000000..87274ce407fa --- /dev/null +++ b/substrate/frame/revive/src/storage.rs @@ -0,0 +1,482 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module contains routines for accessing and altering a contract related state. + +pub mod meter; + +use crate::{ + exec::{AccountIdOf, Key}, + limits, + storage::meter::Diff, + weights::WeightInfo, + BalanceOf, CodeHash, CodeInfo, Config, ContractInfoOf, DeletionQueue, DeletionQueueCounter, + Error, TrieId, SENTINEL, +}; +use alloc::vec::Vec; +use codec::{Decode, Encode, MaxEncodedLen}; +use core::marker::PhantomData; +use frame_support::{ + storage::child::{self, ChildInfo}, + weights::{Weight, WeightMeter}, + CloneNoBound, DefaultNoBound, +}; +use scale_info::TypeInfo; +use sp_core::{ConstU32, Get}; +use sp_io::KillStorageResult; +use sp_runtime::{ + traits::{Hash, Saturating, Zero}, + BoundedBTreeMap, DispatchError, DispatchResult, RuntimeDebug, +}; + +type DelegateDependencyMap = + BoundedBTreeMap, BalanceOf, ConstU32<{ limits::DELEGATE_DEPENDENCIES }>>; + +/// Information for managing an account and its sub trie abstraction. +/// This is the required info to cache for an account. +#[derive(Encode, Decode, CloneNoBound, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[scale_info(skip_type_params(T))] +pub struct ContractInfo { + /// Unique ID for the subtree encoded as a bytes vector. + pub trie_id: TrieId, + /// The code associated with a given account. + pub code_hash: CodeHash, + /// How many bytes of storage are accumulated in this contract's child trie. + storage_bytes: u32, + /// How many items of storage are accumulated in this contract's child trie. + storage_items: u32, + /// This records to how much deposit the accumulated `storage_bytes` amount to. + pub storage_byte_deposit: BalanceOf, + /// This records to how much deposit the accumulated `storage_items` amount to. + storage_item_deposit: BalanceOf, + /// This records how much deposit is put down in order to pay for the contract itself. + /// + /// We need to store this information separately so it is not used when calculating any refunds + /// since the base deposit can only ever be refunded on contract termination. + storage_base_deposit: BalanceOf, + /// Map of code hashes and deposit balances. + /// + /// Tracks the code hash and deposit held for locking delegate dependencies. Dependencies added + /// to the map can not be removed from the chain state and can be safely used for delegate + /// calls. + delegate_dependencies: DelegateDependencyMap, +} + +impl ContractInfo { + /// Constructs a new contract info **without** writing it to storage. + /// + /// This returns an `Err` if an contract with the supplied `account` already exists + /// in storage. + pub fn new( + account: &AccountIdOf, + nonce: T::Nonce, + code_hash: CodeHash, + ) -> Result { + if >::contains_key(account) { + return Err(Error::::DuplicateContract.into()) + } + + let trie_id = { + let buf = ("bcontract_trie_v1", account, nonce).using_encoded(T::Hashing::hash); + buf.as_ref() + .to_vec() + .try_into() + .expect("Runtime uses a reasonable hash size. Hence sizeof(T::Hash) <= 128; qed") + }; + + let contract = Self { + trie_id, + code_hash, + storage_bytes: 0, + storage_items: 0, + storage_byte_deposit: Zero::zero(), + storage_item_deposit: Zero::zero(), + storage_base_deposit: Zero::zero(), + delegate_dependencies: Default::default(), + }; + + Ok(contract) + } + + /// Returns the number of locked delegate dependencies. + pub fn delegate_dependencies_count(&self) -> usize { + self.delegate_dependencies.len() + } + + /// Associated child trie unique id is built from the hash part of the trie id. + pub fn child_trie_info(&self) -> ChildInfo { + ChildInfo::new_default(self.trie_id.as_ref()) + } + + /// The deposit paying for the accumulated storage generated within the contract's child trie. + pub fn extra_deposit(&self) -> BalanceOf { + self.storage_byte_deposit.saturating_add(self.storage_item_deposit) + } + + /// Same as [`Self::extra_deposit`] but including the base deposit. + pub fn total_deposit(&self) -> BalanceOf { + self.extra_deposit().saturating_add(self.storage_base_deposit) + } + + /// Returns the storage base deposit of the contract. + pub fn storage_base_deposit(&self) -> BalanceOf { + self.storage_base_deposit + } + + /// Reads a storage kv pair of a contract. + /// + /// The read is performed from the `trie_id` only. The `address` is not necessary. If the + /// contract doesn't store under the given `key` `None` is returned. + pub fn read(&self, key: &Key) -> Option> { + child::get_raw(&self.child_trie_info(), key.hash().as_slice()) + } + + /// Returns `Some(len)` (in bytes) if a storage item exists at `key`. + /// + /// Returns `None` if the `key` wasn't previously set by `set_storage` or + /// was deleted. + pub fn size(&self, key: &Key) -> Option { + child::len(&self.child_trie_info(), key.hash().as_slice()) + } + + /// Update a storage entry into a contract's kv storage. + /// + /// If the `new_value` is `None` then the kv pair is removed. If `take` is true + /// a [`WriteOutcome::Taken`] is returned instead of a [`WriteOutcome::Overwritten`]. + /// + /// This function also records how much storage was created or removed if a `storage_meter` + /// is supplied. It should only be absent for testing or benchmarking code. + pub fn write( + &self, + key: &Key, + new_value: Option>, + storage_meter: Option<&mut meter::NestedMeter>, + take: bool, + ) -> Result { + let hashed_key = key.hash(); + self.write_raw(&hashed_key, new_value, storage_meter, take) + } + + /// Update a storage entry into a contract's kv storage. + /// Function used in benchmarks, which can simulate prefix collision in keys. + #[cfg(feature = "runtime-benchmarks")] + pub fn bench_write_raw( + &self, + key: &[u8], + new_value: Option>, + take: bool, + ) -> Result { + self.write_raw(key, new_value, None, take) + } + + fn write_raw( + &self, + key: &[u8], + new_value: Option>, + storage_meter: Option<&mut meter::NestedMeter>, + take: bool, + ) -> Result { + let child_trie_info = &self.child_trie_info(); + let (old_len, old_value) = if take { + let val = child::get_raw(child_trie_info, key); + (val.as_ref().map(|v| v.len() as u32), val) + } else { + (child::len(child_trie_info, key), None) + }; + + if let Some(storage_meter) = storage_meter { + let mut diff = meter::Diff::default(); + match (old_len, new_value.as_ref().map(|v| v.len() as u32)) { + (Some(old_len), Some(new_len)) => + if new_len > old_len { + diff.bytes_added = new_len - old_len; + } else { + diff.bytes_removed = old_len - new_len; + }, + (None, Some(new_len)) => { + diff.bytes_added = new_len; + diff.items_added = 1; + }, + (Some(old_len), None) => { + diff.bytes_removed = old_len; + diff.items_removed = 1; + }, + (None, None) => (), + } + storage_meter.charge(&diff); + } + + match &new_value { + Some(new_value) => child::put_raw(child_trie_info, key, new_value), + None => child::kill(child_trie_info, key), + } + + Ok(match (old_len, old_value) { + (None, _) => WriteOutcome::New, + (Some(old_len), None) => WriteOutcome::Overwritten(old_len), + (Some(_), Some(old_value)) => WriteOutcome::Taken(old_value), + }) + } + + /// Sets and returns the contract base deposit. + /// + /// The base deposit is updated when the `code_hash` of the contract changes, as it depends on + /// the deposit paid to upload the contract's code. + pub fn update_base_deposit(&mut self, code_info: &CodeInfo) -> BalanceOf { + let info_deposit = + Diff { bytes_added: self.encoded_size() as u32, items_added: 1, ..Default::default() } + .update_contract::(None) + .charge_or_zero(); + + // Instantiating the contract prevents its code to be deleted, therefore the base deposit + // includes a fraction (`T::CodeHashLockupDepositPercent`) of the original storage deposit + // to prevent abuse. + let upload_deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_info.deposit()); + + let deposit = info_deposit.saturating_add(upload_deposit); + self.storage_base_deposit = deposit; + deposit + } + + /// Adds a new delegate dependency to the contract. + /// The `amount` is the amount of funds that will be reserved for the dependency. + /// + /// Returns an error if the maximum number of delegate_dependencies is reached or if + /// the delegate dependency already exists. + pub fn lock_delegate_dependency( + &mut self, + code_hash: CodeHash, + amount: BalanceOf, + ) -> DispatchResult { + self.delegate_dependencies + .try_insert(code_hash, amount) + .map_err(|_| Error::::MaxDelegateDependenciesReached)? + .map_or(Ok(()), |_| Err(Error::::DelegateDependencyAlreadyExists)) + .map_err(Into::into) + } + + /// Removes the delegate dependency from the contract and returns the deposit held for this + /// dependency. + /// + /// Returns an error if the entry doesn't exist. + pub fn unlock_delegate_dependency( + &mut self, + code_hash: &CodeHash, + ) -> Result, DispatchError> { + self.delegate_dependencies + .remove(code_hash) + .ok_or(Error::::DelegateDependencyNotFound.into()) + } + + /// Returns the delegate_dependencies of the contract. + pub fn delegate_dependencies(&self) -> &DelegateDependencyMap { + &self.delegate_dependencies + } + + /// Push a contract's trie to the deletion queue for lazy removal. + /// + /// You must make sure that the contract is also removed when queuing the trie for deletion. + pub fn queue_trie_for_deletion(&self) { + DeletionQueueManager::::load().insert(self.trie_id.clone()); + } + + /// Calculates the weight that is necessary to remove one key from the trie and how many + /// of those keys can be deleted from the deletion queue given the supplied weight limit. + pub fn deletion_budget(meter: &WeightMeter) -> (Weight, u32) { + let base_weight = T::WeightInfo::on_process_deletion_queue_batch(); + let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1) - + T::WeightInfo::on_initialize_per_trie_key(0); + + // `weight_per_key` being zero makes no sense and would constitute a failure to + // benchmark properly. We opt for not removing any keys at all in this case. + let key_budget = meter + .limit() + .saturating_sub(base_weight) + .checked_div_per_component(&weight_per_key) + .unwrap_or(0) as u32; + + (weight_per_key, key_budget) + } + + /// Delete as many items from the deletion queue possible within the supplied weight limit. + pub fn process_deletion_queue_batch(meter: &mut WeightMeter) { + if meter.try_consume(T::WeightInfo::on_process_deletion_queue_batch()).is_err() { + return + }; + + let mut queue = >::load(); + if queue.is_empty() { + return; + } + + let (weight_per_key, budget) = Self::deletion_budget(&meter); + let mut remaining_key_budget = budget; + while remaining_key_budget > 0 { + let Some(entry) = queue.next() else { break }; + + #[allow(deprecated)] + let outcome = child::kill_storage( + &ChildInfo::new_default(&entry.trie_id), + Some(remaining_key_budget), + ); + + match outcome { + // This happens when our budget wasn't large enough to remove all keys. + KillStorageResult::SomeRemaining(keys_removed) => { + remaining_key_budget.saturating_reduce(keys_removed); + break + }, + KillStorageResult::AllRemoved(keys_removed) => { + entry.remove(); + // charge at least one key even if none were removed. + remaining_key_budget = remaining_key_budget.saturating_sub(keys_removed.max(1)); + }, + }; + } + + meter.consume(weight_per_key.saturating_mul(u64::from(budget - remaining_key_budget))) + } + + /// Returns the code hash of the contract specified by `account` ID. + pub fn load_code_hash(account: &AccountIdOf) -> Option> { + >::get(account).map(|i| i.code_hash) + } +} + +/// Information about what happened to the pre-existing value when calling [`ContractInfo::write`]. +#[cfg_attr(any(test, feature = "runtime-benchmarks"), derive(Debug, PartialEq))] +pub enum WriteOutcome { + /// No value existed at the specified key. + New, + /// A value of the returned length was overwritten. + Overwritten(u32), + /// The returned value was taken out of storage before being overwritten. + /// + /// This is only returned when specifically requested because it causes additional work + /// depending on the size of the pre-existing value. When not requested [`Self::Overwritten`] + /// is returned instead. + Taken(Vec), +} + +impl WriteOutcome { + /// Extracts the size of the overwritten value or `0` if there + /// was no value in storage. + pub fn old_len(&self) -> u32 { + match self { + Self::New => 0, + Self::Overwritten(len) => *len, + Self::Taken(value) => value.len() as u32, + } + } + + /// Extracts the size of the overwritten value or `SENTINEL` if there + /// was no value in storage. + /// + /// # Note + /// + /// We cannot use `0` as sentinel value because there could be a zero sized + /// storage entry which is different from a non existing one. + pub fn old_len_with_sentinel(&self) -> u32 { + match self { + Self::New => SENTINEL, + Self::Overwritten(len) => *len, + Self::Taken(value) => value.len() as u32, + } + } +} + +/// Manage the removal of contracts storage that are marked for deletion. +/// +/// When a contract is deleted by calling `seal_terminate` it becomes inaccessible +/// immediately, but the deletion of the storage items it has accumulated is performed +/// later by pulling the contract from the queue in the `on_idle` hook. +#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, DefaultNoBound, Clone)] +#[scale_info(skip_type_params(T))] +pub struct DeletionQueueManager { + /// Counter used as a key for inserting a new deleted contract in the queue. + /// The counter is incremented after each insertion. + insert_counter: u32, + /// The index used to read the next element to be deleted in the queue. + /// The counter is incremented after each deletion. + delete_counter: u32, + + _phantom: PhantomData, +} + +/// View on a contract that is marked for deletion. +struct DeletionQueueEntry<'a, T: Config> { + /// the trie id of the contract to delete. + trie_id: TrieId, + + /// A mutable reference on the queue so that the contract can be removed, and none can be added + /// or read in the meantime. + queue: &'a mut DeletionQueueManager, +} + +impl<'a, T: Config> DeletionQueueEntry<'a, T> { + /// Remove the contract from the deletion queue. + fn remove(self) { + >::remove(self.queue.delete_counter); + self.queue.delete_counter = self.queue.delete_counter.wrapping_add(1); + >::set(self.queue.clone()); + } +} + +impl DeletionQueueManager { + /// Load the `DeletionQueueCounter`, so we can perform read or write operations on the + /// DeletionQueue storage. + fn load() -> Self { + >::get() + } + + /// Returns `true` if the queue contains no elements. + fn is_empty(&self) -> bool { + self.insert_counter.wrapping_sub(self.delete_counter) == 0 + } + + /// Insert a contract in the deletion queue. + fn insert(&mut self, trie_id: TrieId) { + >::insert(self.insert_counter, trie_id); + self.insert_counter = self.insert_counter.wrapping_add(1); + >::set(self.clone()); + } + + /// Fetch the next contract to be deleted. + /// + /// Note: + /// we use the delete counter to get the next value to read from the queue and thus don't pay + /// the cost of an extra call to `sp_io::storage::next_key` to lookup the next entry in the map + fn next(&mut self) -> Option> { + if self.is_empty() { + return None + } + + let entry = >::get(self.delete_counter); + entry.map(|trie_id| DeletionQueueEntry { trie_id, queue: self }) + } +} + +#[cfg(test)] +#[cfg(feature = "riscv")] +impl DeletionQueueManager { + pub fn from_test_values(insert_counter: u32, delete_counter: u32) -> Self { + Self { insert_counter, delete_counter, _phantom: Default::default() } + } + pub fn as_test_tuple(&self) -> (u32, u32) { + (self.insert_counter, self.delete_counter) + } +} diff --git a/substrate/frame/revive/src/storage/meter.rs b/substrate/frame/revive/src/storage/meter.rs new file mode 100644 index 000000000000..8735aa823421 --- /dev/null +++ b/substrate/frame/revive/src/storage/meter.rs @@ -0,0 +1,888 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module contains functions to meter the storage deposit. + +use crate::{ + storage::ContractInfo, AccountIdOf, BalanceOf, CodeInfo, Config, Error, Event, HoldReason, + Inspect, Origin, Pallet, StorageDeposit as Deposit, System, LOG_TARGET, +}; + +use alloc::vec::Vec; +use core::{fmt::Debug, marker::PhantomData}; +use frame_support::{ + traits::{ + fungible::{Mutate, MutateHold}, + tokens::{Fortitude, Fortitude::Polite, Precision, Preservation, Restriction}, + Get, + }, + DefaultNoBound, RuntimeDebugNoBound, +}; +use sp_runtime::{ + traits::{Saturating, Zero}, + DispatchError, FixedPointNumber, FixedU128, +}; + +/// Deposit that uses the native fungible's balance type. +pub type DepositOf = Deposit>; + +/// A production root storage meter that actually charges from its origin. +pub type Meter = RawMeter; + +/// A production nested storage meter that actually charges from its origin. +pub type NestedMeter = RawMeter; + +/// A production storage meter that actually charges from its origin. +/// +/// This can be used where we want to be generic over the state (Root vs. Nested). +pub type GenericMeter = RawMeter; + +/// A trait that allows to decouple the metering from the charging of balance. +/// +/// This mostly exists for testing so that the charging can be mocked. +pub trait Ext { + /// This checks whether `origin` is able to afford the storage deposit limit. + /// + /// It is necessary to do this check beforehand so that the charge won't fail later on. + /// + /// `origin`: The origin of the call stack from which is responsible for putting down a deposit. + /// `limit`: The limit with which the meter was constructed. + /// `min_leftover`: How much `free_balance` in addition to the existential deposit (ed) should + /// be left inside the `origin` account. + /// + /// Returns the limit that should be used by the meter. If origin can't afford the `limit` + /// it returns `Err`. + fn check_limit( + origin: &T::AccountId, + limit: BalanceOf, + min_leftover: BalanceOf, + ) -> Result, DispatchError>; + /// This is called to inform the implementer that some balance should be charged due to + /// some interaction of the `origin` with a `contract`. + /// + /// The balance transfer can either flow from `origin` to `contract` or the other way + /// around depending on whether `amount` constitutes a `Charge` or a `Refund`. + /// It should be used in combination with `check_limit` to check that no more balance than this + /// limit is ever charged. + fn charge( + origin: &T::AccountId, + contract: &T::AccountId, + amount: &DepositOf, + state: &ContractState, + ) -> Result<(), DispatchError>; +} + +/// This [`Ext`] is used for actual on-chain execution when balance needs to be charged. +/// +/// It uses [`frame_support::traits::fungible::Mutate`] in order to do accomplish the reserves. +pub enum ReservingExt {} + +/// Used to implement a type state pattern for the meter. +/// +/// It is sealed and cannot be implemented outside of this module. +pub trait State: private::Sealed {} + +/// State parameter that constitutes a meter that is in its root state. +#[derive(Default, Debug)] +pub struct Root; + +/// State parameter that constitutes a meter that is in its nested state. +/// Its value indicates whether the nested meter has its own limit. +#[derive(DefaultNoBound, RuntimeDebugNoBound)] +pub enum Nested { + #[default] + DerivedLimit, + OwnLimit, +} + +impl State for Root {} +impl State for Nested {} + +/// A type that allows the metering of consumed or freed storage of a single contract call stack. +#[derive(DefaultNoBound, RuntimeDebugNoBound)] +pub struct RawMeter { + /// The limit of how much balance this meter is allowed to consume. + limit: BalanceOf, + /// The amount of balance that was used in this meter and all of its already absorbed children. + total_deposit: DepositOf, + /// The amount of storage changes that were recorded in this meter alone. + own_contribution: Contribution, + /// List of charges that should be applied at the end of a contract stack execution. + /// + /// We only have one charge per contract hence the size of this vector is + /// limited by the maximum call depth. + charges: Vec>, + /// We store the nested state to determine if it has a special limit for sub-call. + nested: S, + /// Type parameter only used in impls. + _phantom: PhantomData, +} + +/// This type is used to describe a storage change when charging from the meter. +#[derive(Default, RuntimeDebugNoBound)] +pub struct Diff { + /// How many bytes were added to storage. + pub bytes_added: u32, + /// How many bytes were removed from storage. + pub bytes_removed: u32, + /// How many storage items were added to storage. + pub items_added: u32, + /// How many storage items were removed from storage. + pub items_removed: u32, +} + +impl Diff { + /// Calculate how much of a charge or refund results from applying the diff and store it + /// in the passed `info` if any. + /// + /// # Note + /// + /// In case `None` is passed for `info` only charges are calculated. This is because refunds + /// are calculated pro rata of the existing storage within a contract and hence need extract + /// this information from the passed `info`. + pub fn update_contract(&self, info: Option<&mut ContractInfo>) -> DepositOf { + let per_byte = T::DepositPerByte::get(); + let per_item = T::DepositPerItem::get(); + let bytes_added = self.bytes_added.saturating_sub(self.bytes_removed); + let items_added = self.items_added.saturating_sub(self.items_removed); + let mut bytes_deposit = Deposit::Charge(per_byte.saturating_mul((bytes_added).into())); + let mut items_deposit = Deposit::Charge(per_item.saturating_mul((items_added).into())); + + // Without any contract info we can only calculate diffs which add storage + let info = if let Some(info) = info { + info + } else { + debug_assert_eq!(self.bytes_removed, 0); + debug_assert_eq!(self.items_removed, 0); + return bytes_deposit.saturating_add(&items_deposit) + }; + + // Refunds are calculated pro rata based on the accumulated storage within the contract + let bytes_removed = self.bytes_removed.saturating_sub(self.bytes_added); + let items_removed = self.items_removed.saturating_sub(self.items_added); + let ratio = FixedU128::checked_from_rational(bytes_removed, info.storage_bytes) + .unwrap_or_default() + .min(FixedU128::from_u32(1)); + bytes_deposit = bytes_deposit + .saturating_add(&Deposit::Refund(ratio.saturating_mul_int(info.storage_byte_deposit))); + let ratio = FixedU128::checked_from_rational(items_removed, info.storage_items) + .unwrap_or_default() + .min(FixedU128::from_u32(1)); + items_deposit = items_deposit + .saturating_add(&Deposit::Refund(ratio.saturating_mul_int(info.storage_item_deposit))); + + // We need to update the contract info structure with the new deposits + info.storage_bytes = + info.storage_bytes.saturating_add(bytes_added).saturating_sub(bytes_removed); + info.storage_items = + info.storage_items.saturating_add(items_added).saturating_sub(items_removed); + match &bytes_deposit { + Deposit::Charge(amount) => + info.storage_byte_deposit = info.storage_byte_deposit.saturating_add(*amount), + Deposit::Refund(amount) => + info.storage_byte_deposit = info.storage_byte_deposit.saturating_sub(*amount), + } + match &items_deposit { + Deposit::Charge(amount) => + info.storage_item_deposit = info.storage_item_deposit.saturating_add(*amount), + Deposit::Refund(amount) => + info.storage_item_deposit = info.storage_item_deposit.saturating_sub(*amount), + } + + bytes_deposit.saturating_add(&items_deposit) + } +} + +impl Diff { + fn saturating_add(&self, rhs: &Self) -> Self { + Self { + bytes_added: self.bytes_added.saturating_add(rhs.bytes_added), + bytes_removed: self.bytes_removed.saturating_add(rhs.bytes_removed), + items_added: self.items_added.saturating_add(rhs.items_added), + items_removed: self.items_removed.saturating_add(rhs.items_removed), + } + } +} + +/// The state of a contract. +/// +/// In case of termination the beneficiary is indicated. +#[derive(RuntimeDebugNoBound, Clone, PartialEq, Eq)] +pub enum ContractState { + Alive, + Terminated { beneficiary: AccountIdOf }, +} + +/// Records information to charge or refund a plain account. +/// +/// All the charges are deferred to the end of a whole call stack. Reason is that by doing +/// this we can do all the refunds before doing any charge. This way a plain account can use +/// more deposit than it has balance as along as it is covered by a refund. This +/// essentially makes the order of storage changes irrelevant with regard to the deposit system. +/// The only exception is when a special (tougher) deposit limit is specified for a cross-contract +/// call. In that case the limit is enforced once the call is returned, rolling it back if +/// exhausted. +#[derive(RuntimeDebugNoBound, Clone)] +struct Charge { + contract: T::AccountId, + amount: DepositOf, + state: ContractState, +} + +/// Records the storage changes of a storage meter. +#[derive(RuntimeDebugNoBound)] +enum Contribution { + /// The contract the meter belongs to is alive and accumulates changes using a [`Diff`]. + Alive(Diff), + /// The meter was checked against its limit using [`RawMeter::enforce_limit`] at the end of + /// its execution. In this process the [`Diff`] was converted into a [`Deposit`]. + Checked(DepositOf), + /// The contract was terminated. In this process the [`Diff`] was converted into a [`Deposit`] + /// in order to calculate the refund. Upon termination the `reducible_balance` in the + /// contract's account is transferred to the [`beneficiary`]. + Terminated { deposit: DepositOf, beneficiary: AccountIdOf }, +} + +impl Contribution { + /// See [`Diff::update_contract`]. + fn update_contract(&self, info: Option<&mut ContractInfo>) -> DepositOf { + match self { + Self::Alive(diff) => diff.update_contract::(info), + Self::Terminated { deposit, beneficiary: _ } | Self::Checked(deposit) => + deposit.clone(), + } + } +} + +impl Default for Contribution { + fn default() -> Self { + Self::Alive(Default::default()) + } +} + +/// Functions that apply to all states. +impl RawMeter +where + T: Config, + E: Ext, + S: State + Default + Debug, +{ + /// Create a new child that has its `limit`. + /// Passing `0` as the limit is interpreted as to take whatever is remaining from its parent. + /// + /// This is called whenever a new subcall is initiated in order to track the storage + /// usage for this sub call separately. This is necessary because we want to exchange balance + /// with the current contract we are interacting with. + pub fn nested(&self, limit: BalanceOf) -> RawMeter { + debug_assert!(matches!(self.contract_state(), ContractState::Alive)); + // If a special limit is specified higher than it is available, + // we want to enforce the lesser limit to the nested meter, to fail in the sub-call. + let limit = self.available().min(limit); + if limit.is_zero() { + RawMeter { limit: self.available(), ..Default::default() } + } else { + RawMeter { limit, nested: Nested::OwnLimit, ..Default::default() } + } + } + + /// Absorb a child that was spawned to handle a sub call. + /// + /// This should be called whenever a sub call comes to its end and it is **not** reverted. + /// This does the actual balance transfer from/to `origin` and `contract` based on the + /// overall storage consumption of the call. It also updates the supplied contract info. + /// + /// In case a contract reverted the child meter should just be dropped in order to revert + /// any changes it recorded. + /// + /// # Parameters + /// + /// - `absorbed`: The child storage meter that should be absorbed. + /// - `origin`: The origin that spawned the original root meter. + /// - `contract`: The contract's account that this sub call belongs to. + /// - `info`: The info of the contract in question. `None` if the contract was terminated. + pub fn absorb( + &mut self, + absorbed: RawMeter, + contract: &T::AccountId, + info: Option<&mut ContractInfo>, + ) { + let own_deposit = absorbed.own_contribution.update_contract(info); + self.total_deposit = self + .total_deposit + .saturating_add(&absorbed.total_deposit) + .saturating_add(&own_deposit); + self.charges.extend_from_slice(&absorbed.charges); + if !own_deposit.is_zero() { + self.charges.push(Charge { + contract: contract.clone(), + amount: own_deposit, + state: absorbed.contract_state(), + }); + } + } + + /// The amount of balance that is still available from the original `limit`. + fn available(&self) -> BalanceOf { + self.total_deposit.available(&self.limit) + } + + /// Returns the state of the currently executed contract. + fn contract_state(&self) -> ContractState { + match &self.own_contribution { + Contribution::Terminated { deposit: _, beneficiary } => + ContractState::Terminated { beneficiary: beneficiary.clone() }, + _ => ContractState::Alive, + } + } +} + +/// Functions that only apply to the root state. +impl RawMeter +where + T: Config, + E: Ext, +{ + /// Create new storage meter for the specified `origin` and `limit`. + /// + /// This tries to [`Ext::check_limit`] on `origin` and fails if this is not possible. + pub fn new( + origin: &Origin, + limit: BalanceOf, + min_leftover: BalanceOf, + ) -> Result { + // Check the limit only if the origin is not root. + return match origin { + Origin::Root => Ok(Self { limit, ..Default::default() }), + Origin::Signed(o) => { + let limit = E::check_limit(o, limit, min_leftover)?; + Ok(Self { limit, ..Default::default() }) + }, + } + } + + /// The total amount of deposit that should change hands as result of the execution + /// that this meter was passed into. This will also perform all the charges accumulated + /// in the whole contract stack. + /// + /// This drops the root meter in order to make sure it is only called when the whole + /// execution did finish. + pub fn try_into_deposit(self, origin: &Origin) -> Result, DispatchError> { + // Only refund or charge deposit if the origin is not root. + let origin = match origin { + Origin::Root => return Ok(Deposit::Charge(Zero::zero())), + Origin::Signed(o) => o, + }; + for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Refund(_))) { + E::charge(origin, &charge.contract, &charge.amount, &charge.state)?; + } + for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Charge(_))) { + E::charge(origin, &charge.contract, &charge.amount, &charge.state)?; + } + Ok(self.total_deposit) + } +} + +/// Functions that only apply to the nested state. +impl RawMeter +where + T: Config, + E: Ext, +{ + /// Charges `diff` from the meter. + pub fn charge(&mut self, diff: &Diff) { + match &mut self.own_contribution { + Contribution::Alive(own) => *own = own.saturating_add(diff), + _ => panic!("Charge is never called after termination; qed"), + }; + } + + /// Adds a deposit charge. + /// + /// Use this method instead of [`Self::charge`] when the charge is not the result of a storage + /// change. This is the case when a `delegate_dependency` is added or removed, or when the + /// `code_hash` is updated. [`Self::charge`] cannot be used here because we keep track of the + /// deposit charge separately from the storage charge. + pub fn charge_deposit(&mut self, contract: T::AccountId, amount: DepositOf) { + self.total_deposit = self.total_deposit.saturating_add(&amount); + self.charges.push(Charge { contract, amount, state: ContractState::Alive }); + } + + /// Charges from `origin` a storage deposit for contract instantiation. + /// + /// This immediately transfers the balance in order to create the account. + pub fn charge_instantiate( + &mut self, + origin: &T::AccountId, + contract: &T::AccountId, + contract_info: &mut ContractInfo, + code_info: &CodeInfo, + ) -> Result<(), DispatchError> { + debug_assert!(matches!(self.contract_state(), ContractState::Alive)); + + // We need to make sure that the contract's account exists. + let ed = Pallet::::min_balance(); + self.total_deposit = Deposit::Charge(ed); + T::Currency::transfer(origin, contract, ed, Preservation::Preserve)?; + + // A consumer is added at account creation and removed it on termination, otherwise the + // runtime could remove the account. As long as a contract exists its account must exist. + // With the consumer, a correct runtime cannot remove the account. + System::::inc_consumers(contract)?; + + let deposit = contract_info.update_base_deposit(&code_info); + let deposit = Deposit::Charge(deposit); + + self.charge_deposit(contract.clone(), deposit); + Ok(()) + } + + /// Call to tell the meter that the currently executing contract was terminated. + /// + /// This will manipulate the meter so that all storage deposit accumulated in + /// `contract_info` will be refunded to the `origin` of the meter. And the free + /// (`reducible_balance`) will be sent to the `beneficiary`. + pub fn terminate(&mut self, info: &ContractInfo, beneficiary: T::AccountId) { + debug_assert!(matches!(self.contract_state(), ContractState::Alive)); + self.own_contribution = Contribution::Terminated { + deposit: Deposit::Refund(info.total_deposit()), + beneficiary, + }; + } + + /// [`Self::charge`] does not enforce the storage limit since we want to do this check as late + /// as possible to allow later refunds to offset earlier charges. + /// + /// # Note + /// + /// We normally need to call this **once** for every call stack and not for every cross contract + /// call. However, if a dedicated limit is specified for a sub-call, this needs to be called + /// once the sub-call has returned. For this, the [`Self::enforce_subcall_limit`] wrapper is + /// used. + pub fn enforce_limit( + &mut self, + info: Option<&mut ContractInfo>, + ) -> Result<(), DispatchError> { + let deposit = self.own_contribution.update_contract(info); + let total_deposit = self.total_deposit.saturating_add(&deposit); + // We don't want to override a `Terminated` with a `Checked`. + if matches!(self.contract_state(), ContractState::Alive) { + self.own_contribution = Contribution::Checked(deposit); + } + if let Deposit::Charge(amount) = total_deposit { + if amount > self.limit { + return Err(>::StorageDepositLimitExhausted.into()) + } + } + Ok(()) + } + + /// This is a wrapper around [`Self::enforce_limit`] to use on the exit from a sub-call to + /// enforce its special limit if needed. + pub fn enforce_subcall_limit( + &mut self, + info: Option<&mut ContractInfo>, + ) -> Result<(), DispatchError> { + match self.nested { + Nested::OwnLimit => self.enforce_limit(info), + Nested::DerivedLimit => Ok(()), + } + } +} + +impl Ext for ReservingExt { + fn check_limit( + origin: &T::AccountId, + limit: BalanceOf, + min_leftover: BalanceOf, + ) -> Result, DispatchError> { + let limit = T::Currency::reducible_balance(origin, Preservation::Preserve, Polite) + .saturating_sub(min_leftover) + .min(limit); + Ok(limit) + } + + fn charge( + origin: &T::AccountId, + contract: &T::AccountId, + amount: &DepositOf, + state: &ContractState, + ) -> Result<(), DispatchError> { + match amount { + Deposit::Charge(amount) | Deposit::Refund(amount) if amount.is_zero() => return Ok(()), + Deposit::Charge(amount) => { + // This could fail if the `origin` does not have enough liquidity. Ideally, though, + // this should have been checked before with `check_limit`. + T::Currency::transfer_and_hold( + &HoldReason::StorageDepositReserve.into(), + origin, + contract, + *amount, + Precision::Exact, + Preservation::Preserve, + Fortitude::Polite, + )?; + + Pallet::::deposit_event(Event::StorageDepositTransferredAndHeld { + from: origin.clone(), + to: contract.clone(), + amount: *amount, + }); + }, + Deposit::Refund(amount) => { + let transferred = T::Currency::transfer_on_hold( + &HoldReason::StorageDepositReserve.into(), + contract, + origin, + *amount, + Precision::BestEffort, + Restriction::Free, + Fortitude::Polite, + )?; + + Pallet::::deposit_event(Event::StorageDepositTransferredAndReleased { + from: contract.clone(), + to: origin.clone(), + amount: transferred, + }); + + if transferred < *amount { + // This should never happen, if it does it means that there is a bug in the + // runtime logic. In the rare case this happens we try to refund as much as we + // can, thus the `Precision::BestEffort`. + log::error!( + target: LOG_TARGET, + "Failed to repatriate full storage deposit {:?} from contract {:?} to origin {:?}. Transferred {:?}.", + amount, contract, origin, transferred, + ); + } + }, + } + if let ContractState::::Terminated { beneficiary } = state { + System::::dec_consumers(&contract); + // Whatever is left in the contract is sent to the termination beneficiary. + T::Currency::transfer( + &contract, + &beneficiary, + T::Currency::reducible_balance(&contract, Preservation::Expendable, Polite), + Preservation::Expendable, + )?; + } + Ok(()) + } +} + +mod private { + pub trait Sealed {} + impl Sealed for super::Root {} + impl Sealed for super::Nested {} +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{exec::AccountIdOf, test_utils::*, tests::Test}; + use frame_support::parameter_types; + use pretty_assertions::assert_eq; + + type TestMeter = RawMeter; + + parameter_types! { + static TestExtTestValue: TestExt = Default::default(); + } + + #[derive(Debug, PartialEq, Eq, Clone)] + struct LimitCheck { + origin: AccountIdOf, + limit: BalanceOf, + min_leftover: BalanceOf, + } + + #[derive(Debug, PartialEq, Eq, Clone)] + struct Charge { + origin: AccountIdOf, + contract: AccountIdOf, + amount: DepositOf, + state: ContractState, + } + + #[derive(Default, Debug, PartialEq, Eq, Clone)] + pub struct TestExt { + limit_checks: Vec, + charges: Vec, + } + + impl TestExt { + fn clear(&mut self) { + self.limit_checks.clear(); + self.charges.clear(); + } + } + + impl Ext for TestExt { + fn check_limit( + origin: &AccountIdOf, + limit: BalanceOf, + min_leftover: BalanceOf, + ) -> Result, DispatchError> { + TestExtTestValue::mutate(|ext| { + ext.limit_checks + .push(LimitCheck { origin: origin.clone(), limit, min_leftover }) + }); + Ok(limit) + } + + fn charge( + origin: &AccountIdOf, + contract: &AccountIdOf, + amount: &DepositOf, + state: &ContractState, + ) -> Result<(), DispatchError> { + TestExtTestValue::mutate(|ext| { + ext.charges.push(Charge { + origin: origin.clone(), + contract: contract.clone(), + amount: amount.clone(), + state: state.clone(), + }) + }); + Ok(()) + } + } + + fn clear_ext() { + TestExtTestValue::mutate(|ext| ext.clear()) + } + + struct ChargingTestCase { + origin: Origin, + deposit: DepositOf, + expected: TestExt, + } + + #[derive(Default)] + struct StorageInfo { + bytes: u32, + items: u32, + bytes_deposit: BalanceOf, + items_deposit: BalanceOf, + } + + fn new_info(info: StorageInfo) -> ContractInfo { + ContractInfo:: { + trie_id: Default::default(), + code_hash: Default::default(), + storage_bytes: info.bytes, + storage_items: info.items, + storage_byte_deposit: info.bytes_deposit, + storage_item_deposit: info.items_deposit, + storage_base_deposit: Default::default(), + delegate_dependencies: Default::default(), + } + } + + #[test] + fn new_reserves_balance_works() { + clear_ext(); + + TestMeter::new(&Origin::from_account_id(ALICE), 1_000, 0).unwrap(); + + assert_eq!( + TestExtTestValue::get(), + TestExt { + limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], + ..Default::default() + } + ) + } + + #[test] + fn empty_charge_works() { + clear_ext(); + + let mut meter = TestMeter::new(&Origin::from_account_id(ALICE), 1_000, 0).unwrap(); + assert_eq!(meter.available(), 1_000); + + // an empty charge does not create a `Charge` entry + let mut nested0 = meter.nested(BalanceOf::::zero()); + nested0.charge(&Default::default()); + meter.absorb(nested0, &BOB, None); + + assert_eq!( + TestExtTestValue::get(), + TestExt { + limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], + ..Default::default() + } + ) + } + + #[test] + fn charging_works() { + let test_cases = vec![ + ChargingTestCase { + origin: Origin::::from_account_id(ALICE), + deposit: Deposit::Refund(28), + expected: TestExt { + limit_checks: vec![LimitCheck { origin: ALICE, limit: 100, min_leftover: 0 }], + charges: vec![ + Charge { + origin: ALICE, + contract: CHARLIE, + amount: Deposit::Refund(10), + state: ContractState::Alive, + }, + Charge { + origin: ALICE, + contract: CHARLIE, + amount: Deposit::Refund(20), + state: ContractState::Alive, + }, + Charge { + origin: ALICE, + contract: BOB, + amount: Deposit::Charge(2), + state: ContractState::Alive, + }, + ], + }, + }, + ChargingTestCase { + origin: Origin::::Root, + deposit: Deposit::Charge(0), + expected: TestExt { limit_checks: vec![], charges: vec![] }, + }, + ]; + + for test_case in test_cases { + clear_ext(); + + let mut meter = TestMeter::new(&test_case.origin, 100, 0).unwrap(); + assert_eq!(meter.available(), 100); + + let mut nested0_info = new_info(StorageInfo { + bytes: 100, + items: 5, + bytes_deposit: 100, + items_deposit: 10, + }); + let mut nested0 = meter.nested(BalanceOf::::zero()); + nested0.charge(&Diff { + bytes_added: 108, + bytes_removed: 5, + items_added: 1, + items_removed: 2, + }); + nested0.charge(&Diff { bytes_removed: 99, ..Default::default() }); + + let mut nested1_info = new_info(StorageInfo { + bytes: 100, + items: 10, + bytes_deposit: 100, + items_deposit: 20, + }); + let mut nested1 = nested0.nested(BalanceOf::::zero()); + nested1.charge(&Diff { items_removed: 5, ..Default::default() }); + nested0.absorb(nested1, &CHARLIE, Some(&mut nested1_info)); + + let mut nested2_info = new_info(StorageInfo { + bytes: 100, + items: 7, + bytes_deposit: 100, + items_deposit: 20, + }); + let mut nested2 = nested0.nested(BalanceOf::::zero()); + nested2.charge(&Diff { items_removed: 7, ..Default::default() }); + nested0.absorb(nested2, &CHARLIE, Some(&mut nested2_info)); + + nested0.enforce_limit(Some(&mut nested0_info)).unwrap(); + meter.absorb(nested0, &BOB, Some(&mut nested0_info)); + + assert_eq!(meter.try_into_deposit(&test_case.origin).unwrap(), test_case.deposit); + + assert_eq!(nested0_info.extra_deposit(), 112); + assert_eq!(nested1_info.extra_deposit(), 110); + assert_eq!(nested2_info.extra_deposit(), 100); + + assert_eq!(TestExtTestValue::get(), test_case.expected) + } + } + + #[test] + fn termination_works() { + let test_cases = vec![ + ChargingTestCase { + origin: Origin::::from_account_id(ALICE), + deposit: Deposit::Refund(108), + expected: TestExt { + limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], + charges: vec![ + Charge { + origin: ALICE, + contract: CHARLIE, + amount: Deposit::Refund(120), + state: ContractState::Terminated { beneficiary: CHARLIE }, + }, + Charge { + origin: ALICE, + contract: BOB, + amount: Deposit::Charge(12), + state: ContractState::Alive, + }, + ], + }, + }, + ChargingTestCase { + origin: Origin::::Root, + deposit: Deposit::Charge(0), + expected: TestExt { limit_checks: vec![], charges: vec![] }, + }, + ]; + + for test_case in test_cases { + clear_ext(); + + let mut meter = TestMeter::new(&test_case.origin, 1_000, 0).unwrap(); + assert_eq!(meter.available(), 1_000); + + let mut nested0 = meter.nested(BalanceOf::::zero()); + nested0.charge(&Diff { + bytes_added: 5, + bytes_removed: 1, + items_added: 3, + items_removed: 1, + }); + nested0.charge(&Diff { items_added: 2, ..Default::default() }); + + let mut nested1_info = new_info(StorageInfo { + bytes: 100, + items: 10, + bytes_deposit: 100, + items_deposit: 20, + }); + let mut nested1 = nested0.nested(BalanceOf::::zero()); + nested1.charge(&Diff { items_removed: 5, ..Default::default() }); + nested1.charge(&Diff { bytes_added: 20, ..Default::default() }); + nested1.terminate(&nested1_info, CHARLIE); + nested0.enforce_limit(Some(&mut nested1_info)).unwrap(); + nested0.absorb(nested1, &CHARLIE, None); + + meter.absorb(nested0, &BOB, None); + assert_eq!(meter.try_into_deposit(&test_case.origin).unwrap(), test_case.deposit); + assert_eq!(TestExtTestValue::get(), test_case.expected) + } + } +} diff --git a/substrate/frame/revive/src/test_utils.rs b/substrate/frame/revive/src/test_utils.rs new file mode 100644 index 000000000000..2bfe754f86cd --- /dev/null +++ b/substrate/frame/revive/src/test_utils.rs @@ -0,0 +1,38 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Shared utilities for testing contracts. +//! This is not part of the tests module because it is made public for other crates to use. + +#![cfg(feature = "std")] + +pub mod builder; + +use crate::{BalanceOf, Config}; +use frame_support::weights::Weight; +pub use sp_runtime::AccountId32; + +pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]); +pub const BOB: AccountId32 = AccountId32::new([2u8; 32]); +pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]); +pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]); + +pub const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); + +pub fn deposit_limit() -> BalanceOf { + 10_000_000u32.into() +} diff --git a/substrate/frame/revive/src/test_utils/builder.rs b/substrate/frame/revive/src/test_utils/builder.rs new file mode 100644 index 000000000000..bf8cbcd5a01f --- /dev/null +++ b/substrate/frame/revive/src/test_utils/builder.rs @@ -0,0 +1,218 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{deposit_limit, GAS_LIMIT}; +use crate::{ + AccountIdLookupOf, AccountIdOf, BalanceOf, Code, CodeHash, CollectEvents, Config, + ContractExecResult, ContractInstantiateResult, DebugInfo, EventRecordOf, ExecReturnValue, + InstantiateReturnValue, OriginFor, Pallet, Weight, +}; +use codec::{Encode, HasCompact}; +use core::fmt::Debug; +use frame_support::pallet_prelude::DispatchResultWithPostInfo; +use paste::paste; +use scale_info::TypeInfo; + +/// Helper macro to generate a builder for contract API calls. +macro_rules! builder { + // Entry point to generate a builder for the given method. + ( + $method:ident($($field:ident: $type:ty,)*) -> $result:ty; + $($extra:item)* + ) => { + paste!{ + builder!([< $method:camel Builder >], $method($($field: $type,)* ) -> $result; $($extra)*); + } + }; + // Generate the builder struct and its methods. + ( + $name:ident, + $method:ident($($field:ident: $type:ty,)*) -> $result:ty; + $($extra:item)* + ) => { + #[doc = concat!("A builder to construct a ", stringify!($method), " call")] + pub struct $name { + $($field: $type,)* + } + + #[allow(dead_code)] + impl $name + where + as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + { + $( + #[doc = concat!("Set the ", stringify!($field))] + pub fn $field(mut self, value: $type) -> Self { + self.$field = value; + self + } + )* + + #[doc = concat!("Build the ", stringify!($method), " call")] + pub fn build(self) -> $result { + Pallet::::$method( + $(self.$field,)* + ) + } + + $($extra)* + } + } +} + +builder!( + instantiate_with_code( + origin: OriginFor, + value: BalanceOf, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + code: Vec, + data: Vec, + salt: Vec, + ) -> DispatchResultWithPostInfo; + + /// Create an [`InstantiateWithCodeBuilder`] with default values. + pub fn instantiate_with_code(origin: OriginFor, code: Vec) -> Self { + Self { + origin, + value: 0u32.into(), + gas_limit: GAS_LIMIT, + storage_deposit_limit: deposit_limit::(), + code, + data: vec![], + salt: vec![], + } + } +); + +builder!( + instantiate( + origin: OriginFor, + value: BalanceOf, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + code_hash: CodeHash, + data: Vec, + salt: Vec, + ) -> DispatchResultWithPostInfo; + + /// Create an [`InstantiateBuilder`] with default values. + pub fn instantiate(origin: OriginFor, code_hash: CodeHash) -> Self { + Self { + origin, + value: 0u32.into(), + gas_limit: GAS_LIMIT, + storage_deposit_limit: deposit_limit::(), + code_hash, + data: vec![], + salt: vec![], + } + } +); + +builder!( + bare_instantiate( + origin: OriginFor, + value: BalanceOf, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + code: Code>, + data: Vec, + salt: Vec, + debug: DebugInfo, + collect_events: CollectEvents, + ) -> ContractInstantiateResult, BalanceOf, EventRecordOf>; + + /// Build the instantiate call and unwrap the result. + pub fn build_and_unwrap_result(self) -> InstantiateReturnValue> { + self.build().result.unwrap() + } + + /// Build the instantiate call and unwrap the account id. + pub fn build_and_unwrap_account_id(self) -> AccountIdOf { + self.build().result.unwrap().account_id + } + + pub fn bare_instantiate(origin: OriginFor, code: Code>) -> Self { + Self { + origin, + value: 0u32.into(), + gas_limit: GAS_LIMIT, + storage_deposit_limit: deposit_limit::(), + code, + data: vec![], + salt: vec![], + debug: DebugInfo::UnsafeDebug, + collect_events: CollectEvents::Skip, + } + } +); + +builder!( + call( + origin: OriginFor, + dest: AccountIdLookupOf, + value: BalanceOf, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + data: Vec, + ) -> DispatchResultWithPostInfo; + + /// Create a [`CallBuilder`] with default values. + pub fn call(origin: OriginFor, dest: AccountIdLookupOf) -> Self { + CallBuilder { + origin, + dest, + value: 0u32.into(), + gas_limit: GAS_LIMIT, + storage_deposit_limit: deposit_limit::(), + data: vec![], + } + } +); + +builder!( + bare_call( + origin: OriginFor, + dest: AccountIdOf, + value: BalanceOf, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + data: Vec, + debug: DebugInfo, + collect_events: CollectEvents, + ) -> ContractExecResult, EventRecordOf>; + + /// Build the call and unwrap the result. + pub fn build_and_unwrap_result(self) -> ExecReturnValue { + self.build().result.unwrap() + } + + /// Create a [`BareCallBuilder`] with default values. + pub fn bare_call(origin: OriginFor, dest: AccountIdOf) -> Self { + Self { + origin, + dest, + value: 0u32.into(), + gas_limit: GAS_LIMIT, + storage_deposit_limit: deposit_limit::(), + data: vec![], + debug: DebugInfo::UnsafeDebug, + collect_events: CollectEvents::Skip, + } + } +); diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs new file mode 100644 index 000000000000..52ee7b310542 --- /dev/null +++ b/substrate/frame/revive/src/tests.rs @@ -0,0 +1,4166 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "riscv"), allow(dead_code, unused_imports, unused_macros))] + +mod pallet_dummy; +mod test_debug; + +use self::{ + test_debug::TestDebug, + test_utils::{ensure_stored, expected_deposit}, +}; +use crate::{ + self as pallet_revive, + chain_extension::{ + ChainExtension, Environment, Ext, RegisteredChainExtension, Result as ExtensionResult, + RetVal, ReturnFlags, + }, + exec::Key, + limits, + migration::codegen::LATEST_MIGRATION_VERSION, + primitives::CodeUploadReturnValue, + storage::DeletionQueueManager, + test_utils::*, + tests::test_utils::{get_contract, get_contract_checked}, + wasm::Memory, + weights::WeightInfo, + BalanceOf, Code, CodeHash, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf, + DebugInfo, DefaultAddressGenerator, DeletionQueueCounter, Error, HoldReason, + MigrationInProgress, Origin, Pallet, PristineCode, +}; +use assert_matches::assert_matches; +use codec::{Decode, Encode}; +use frame_support::{ + assert_err, assert_err_ignore_postinfo, assert_err_with_weight, assert_noop, assert_ok, + derive_impl, + dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo}, + pallet_prelude::EnsureOrigin, + parameter_types, + storage::child, + traits::{ + fungible::{BalancedHold, Inspect, Mutate, MutateHold}, + tokens::Preservation, + ConstU32, ConstU64, Contains, OnIdle, OnInitialize, StorageVersion, + }, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight, WeightMeter}, +}; +use frame_system::{EventRecord, Phase}; +use pallet_revive_fixtures::{bench::dummy_unique, compile_module}; +use pallet_revive_uapi::ReturnErrorCode as RuntimeReturnCode; +use sp_core::ByteArray; +use sp_io::hashing::blake2_256; +use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; +use sp_runtime::{ + testing::H256, + traits::{BlakeTwo256, Convert, Hash, IdentityLookup}, + AccountId32, BuildStorage, DispatchError, Perbill, TokenError, +}; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Balances: pallet_balances, + Timestamp: pallet_timestamp, + Utility: pallet_utility, + Contracts: pallet_revive, + Proxy: pallet_proxy, + Dummy: pallet_dummy + } +); + +macro_rules! assert_return_code { + ( $x:expr , $y:expr $(,)? ) => {{ + assert_eq!(u32::from_le_bytes($x.data[..].try_into().unwrap()), $y as u32); + }}; +} + +macro_rules! assert_refcount { + ( $code_hash:expr , $should:expr $(,)? ) => {{ + let is = crate::CodeInfoOf::::get($code_hash).map(|m| m.refcount()).unwrap(); + assert_eq!(is, $should); + }}; +} + +pub mod test_utils { + use super::{Contracts, DepositPerByte, DepositPerItem, Test}; + use crate::{ + exec::AccountIdOf, BalanceOf, CodeHash, CodeInfo, CodeInfoOf, Config, ContractInfo, + ContractInfoOf, PristineCode, + }; + use codec::{Encode, MaxEncodedLen}; + use frame_support::traits::fungible::{InspectHold, Mutate}; + + pub fn place_contract(address: &AccountIdOf, code_hash: CodeHash) { + set_balance(address, Contracts::min_balance() * 10); + >::insert(code_hash, CodeInfo::new(address.clone())); + let contract = >::new(&address, 0, code_hash).unwrap(); + >::insert(address, contract); + } + pub fn set_balance(who: &AccountIdOf, amount: u64) { + let _ = ::Currency::set_balance(who, amount); + } + pub fn get_balance(who: &AccountIdOf) -> u64 { + ::Currency::free_balance(who) + } + pub fn get_balance_on_hold( + reason: &::RuntimeHoldReason, + who: &AccountIdOf, + ) -> u64 { + ::Currency::balance_on_hold(reason.into(), who) + } + pub fn get_contract(addr: &AccountIdOf) -> ContractInfo { + get_contract_checked(addr).unwrap() + } + pub fn get_contract_checked(addr: &AccountIdOf) -> Option> { + ContractInfoOf::::get(addr) + } + pub fn get_code_deposit(code_hash: &CodeHash) -> BalanceOf { + crate::CodeInfoOf::::get(code_hash).unwrap().deposit() + } + pub fn contract_info_storage_deposit( + addr: &::AccountId, + ) -> BalanceOf { + let contract_info = self::get_contract(&addr); + let info_size = contract_info.encoded_size() as u64; + DepositPerByte::get() + .saturating_mul(info_size) + .saturating_add(DepositPerItem::get()) + } + pub fn expected_deposit(code_len: usize) -> u64 { + // For code_info, the deposit for max_encoded_len is taken. + let code_info_len = CodeInfo::::max_encoded_len() as u64; + // Calculate deposit to be reserved. + // We add 2 storage items: one for code, other for code_info + DepositPerByte::get().saturating_mul(code_len as u64 + code_info_len) + + DepositPerItem::get().saturating_mul(2) + } + pub fn ensure_stored(code_hash: CodeHash) -> usize { + // Assert that code_info is stored + assert!(CodeInfoOf::::contains_key(&code_hash)); + // Assert that contract code is stored, and get its size. + PristineCode::::try_get(&code_hash).unwrap().len() + } +} + +mod builder { + use super::Test; + use crate::{ + test_utils::{builder::*, AccountId32, ALICE}, + tests::RuntimeOrigin, + AccountIdLookupOf, Code, CodeHash, + }; + + pub fn bare_instantiate(code: Code>) -> BareInstantiateBuilder { + BareInstantiateBuilder::::bare_instantiate(RuntimeOrigin::signed(ALICE), code) + } + + pub fn bare_call(dest: AccountId32) -> BareCallBuilder { + BareCallBuilder::::bare_call(RuntimeOrigin::signed(ALICE), dest) + } + + pub fn instantiate_with_code(code: Vec) -> InstantiateWithCodeBuilder { + InstantiateWithCodeBuilder::::instantiate_with_code( + RuntimeOrigin::signed(ALICE), + code, + ) + } + + pub fn instantiate(code_hash: CodeHash) -> InstantiateBuilder { + InstantiateBuilder::::instantiate(RuntimeOrigin::signed(ALICE), code_hash) + } + + pub fn call(dest: AccountIdLookupOf) -> CallBuilder { + CallBuilder::::call(RuntimeOrigin::signed(ALICE), dest) + } +} + +impl Test { + pub fn set_unstable_interface(unstable_interface: bool) { + UNSTABLE_INTERFACE.with(|v| *v.borrow_mut() = unstable_interface); + } +} + +parameter_types! { + static TestExtensionTestValue: TestExtension = Default::default(); +} + +#[derive(Clone)] +pub struct TestExtension { + enabled: bool, + last_seen_buffer: Vec, + last_seen_input_len: u32, +} + +#[derive(Default)] +pub struct RevertingExtension; + +#[derive(Default)] +pub struct DisabledExtension; + +#[derive(Default)] +pub struct TempStorageExtension { + storage: u32, +} + +impl TestExtension { + fn disable() { + TestExtensionTestValue::mutate(|e| e.enabled = false) + } + + fn last_seen_buffer() -> Vec { + TestExtensionTestValue::get().last_seen_buffer.clone() + } + + fn last_seen_input_len() -> u32 { + TestExtensionTestValue::get().last_seen_input_len + } +} + +impl Default for TestExtension { + fn default() -> Self { + Self { enabled: true, last_seen_buffer: vec![], last_seen_input_len: 0 } + } +} + +impl ChainExtension for TestExtension { + fn call(&mut self, mut env: Environment) -> ExtensionResult + where + E: Ext, + M: ?Sized + Memory, + { + let func_id = env.func_id(); + let id = env.ext_id() as u32 | func_id as u32; + match func_id { + 0 => { + let input = env.read(8)?; + env.write(&input, false, None)?; + TestExtensionTestValue::mutate(|e| e.last_seen_buffer = input); + Ok(RetVal::Converging(id)) + }, + 1 => { + TestExtensionTestValue::mutate(|e| e.last_seen_input_len = env.in_len()); + Ok(RetVal::Converging(id)) + }, + 2 => { + let mut enc = &env.read(9)?[4..8]; + let weight = Weight::from_parts( + u32::decode(&mut enc).map_err(|_| Error::::ContractTrapped)?.into(), + 0, + ); + env.charge_weight(weight)?; + Ok(RetVal::Converging(id)) + }, + 3 => Ok(RetVal::Diverging { flags: ReturnFlags::REVERT, data: vec![42, 99] }), + _ => { + panic!("Passed unknown id to test chain extension: {}", func_id); + }, + } + } + + fn enabled() -> bool { + TestExtensionTestValue::get().enabled + } +} + +impl RegisteredChainExtension for TestExtension { + const ID: u16 = 0; +} + +impl ChainExtension for RevertingExtension { + fn call(&mut self, _env: Environment) -> ExtensionResult + where + E: Ext, + M: ?Sized + Memory, + { + Ok(RetVal::Diverging { flags: ReturnFlags::REVERT, data: vec![0x4B, 0x1D] }) + } + + fn enabled() -> bool { + TestExtensionTestValue::get().enabled + } +} + +impl RegisteredChainExtension for RevertingExtension { + const ID: u16 = 1; +} + +impl ChainExtension for DisabledExtension { + fn call(&mut self, _env: Environment) -> ExtensionResult + where + E: Ext, + M: ?Sized + Memory, + { + panic!("Disabled chain extensions are never called") + } + + fn enabled() -> bool { + false + } +} + +impl RegisteredChainExtension for DisabledExtension { + const ID: u16 = 2; +} + +impl ChainExtension for TempStorageExtension { + fn call(&mut self, env: Environment) -> ExtensionResult + where + E: Ext, + M: ?Sized + Memory, + { + let func_id = env.func_id(); + match func_id { + 0 => self.storage = 42, + 1 => assert_eq!(self.storage, 42, "Storage is preserved inside the same call."), + 2 => { + assert_eq!(self.storage, 0, "Storage is different for different calls."); + self.storage = 99; + }, + 3 => assert_eq!(self.storage, 99, "Storage is preserved inside the same call."), + _ => { + panic!("Passed unknown id to test chain extension: {}", func_id); + }, + } + Ok(RetVal::Converging(0)) + } + + fn enabled() -> bool { + TestExtensionTestValue::get().enabled + } +} + +impl RegisteredChainExtension for TempStorageExtension { + const ID: u16 = 3; +} + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max( + Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), + ); + pub static ExistentialDeposit: u64 = 1; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; + type AccountId = AccountId32; + type Lookup = IdentityLookup; + type AccountData = pallet_balances::AccountData; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Test { + type ExistentialDeposit = ExistentialDeposit; + type ReserveIdentifier = [u8; 8]; + type AccountStore = System; +} + +#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] +impl pallet_timestamp::Config for Test {} + +impl pallet_utility::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + +impl pallet_proxy::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = (); + type ProxyDepositBase = ConstU64<1>; + type ProxyDepositFactor = ConstU64<1>; + type MaxProxies = ConstU32<32>; + type WeightInfo = (); + type MaxPending = ConstU32<32>; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = ConstU64<1>; + type AnnouncementDepositFactor = ConstU64<1>; +} + +impl pallet_dummy::Config for Test {} + +parameter_types! { + pub static DepositPerByte: BalanceOf = 1; + pub const DepositPerItem: BalanceOf = 2; + pub static CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0); +} + +impl Convert> for Test { + fn convert(w: Weight) -> BalanceOf { + w.ref_time() + } +} + +/// A filter whose filter function can be swapped at runtime. +pub struct TestFilter; + +#[derive(Clone)] +pub struct Filters { + filter: fn(&RuntimeCall) -> bool, +} + +impl Default for Filters { + fn default() -> Self { + Filters { filter: (|_| true) } + } +} + +parameter_types! { + static CallFilter: Filters = Default::default(); +} + +impl TestFilter { + pub fn set_filter(filter: fn(&RuntimeCall) -> bool) { + CallFilter::mutate(|fltr| fltr.filter = filter); + } +} + +impl Contains for TestFilter { + fn contains(call: &RuntimeCall) -> bool { + (CallFilter::get().filter)(call) + } +} + +parameter_types! { + pub static UploadAccount: Option<::AccountId> = None; + pub static InstantiateAccount: Option<::AccountId> = None; +} + +pub struct EnsureAccount(core::marker::PhantomData<(T, A)>); +impl>>> + EnsureOrigin<::RuntimeOrigin> for EnsureAccount +where + ::AccountId: From, +{ + type Success = T::AccountId; + + fn try_origin(o: T::RuntimeOrigin) -> Result { + let who = as EnsureOrigin<_>>::try_origin(o.clone())?; + if matches!(A::get(), Some(a) if who != a) { + return Err(o); + } + + Ok(who) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Err(()) + } +} +parameter_types! { + pub static UnstableInterface: bool = true; +} + +#[derive_impl(crate::config_preludes::TestDefaultConfig)] +impl Config for Test { + type Time = Timestamp; + type Currency = Balances; + type CallFilter = TestFilter; + type ChainExtension = + (TestExtension, DisabledExtension, RevertingExtension, TempStorageExtension); + type DepositPerByte = DepositPerByte; + type DepositPerItem = DepositPerItem; + type AddressGenerator = DefaultAddressGenerator; + type UnsafeUnstableInterface = UnstableInterface; + type UploadOrigin = EnsureAccount; + type InstantiateOrigin = EnsureAccount; + type Migrations = crate::migration::codegen::BenchMigrations; + type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; + type Debug = TestDebug; +} + +pub struct ExtBuilder { + existential_deposit: u64, + storage_version: Option, + code_hashes: Vec>, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { + existential_deposit: ExistentialDeposit::get(), + storage_version: None, + code_hashes: vec![], + } + } +} + +impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + pub fn with_code_hashes(mut self, code_hashes: Vec>) -> Self { + self.code_hashes = code_hashes; + self + } + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + } + pub fn set_storage_version(mut self, version: u16) -> Self { + self.storage_version = Some(StorageVersion::new(version)); + self + } + pub fn build(self) -> sp_io::TestExternalities { + sp_tracing::try_init_simple(); + self.set_associated_consts(); + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![] } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.register_extension(KeystoreExt::new(MemoryKeystore::new())); + ext.execute_with(|| { + use frame_support::traits::OnGenesis; + + Pallet::::on_genesis(); + if let Some(storage_version) = self.storage_version { + storage_version.put::>(); + } + System::set_block_number(1) + }); + ext.execute_with(|| { + for code_hash in self.code_hashes { + CodeInfoOf::::insert(code_hash, crate::CodeInfo::new(ALICE)); + } + }); + ext + } +} + +fn initialize_block(number: u64) { + System::reset_events(); + System::initialize(&number, &[0u8; 32].into(), &Default::default()); +} + +struct ExtensionInput<'a> { + extension_id: u16, + func_id: u16, + extra: &'a [u8], +} + +impl<'a> ExtensionInput<'a> { + fn to_vec(&self) -> Vec { + ((self.extension_id as u32) << 16 | (self.func_id as u32)) + .to_le_bytes() + .iter() + .chain(self.extra) + .cloned() + .collect() + } +} + +impl<'a> From> for Vec { + fn from(input: ExtensionInput) -> Vec { + input.to_vec() + } +} + +impl Default for Origin { + fn default() -> Self { + Self::Signed(ALICE) + } +} + +/// We can only run the tests if we have a riscv toolchain installed +#[cfg(feature = "riscv")] +mod run_tests { + use super::*; + use pretty_assertions::{assert_eq, assert_ne}; + + // Perform a call to a plain account. + // The actual transfer fails because we can only call contracts. + // Then we check that at least the base costs where charged (no runtime gas costs.) + #[test] + fn calling_plain_account_fails() { + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 100_000_000); + let base_cost = <::WeightInfo as WeightInfo>::call(); + + assert_eq!( + builder::call(BOB).build(), + Err(DispatchErrorWithPostInfo { + error: Error::::ContractNotFound.into(), + post_info: PostDispatchInfo { + actual_weight: Some(base_cost), + pays_fee: Default::default(), + }, + }) + ); + }); + } + + #[test] + fn migration_on_idle_hooks_works() { + // Defines expectations of how many migration steps can be done given the weight limit. + let tests = [ + (Weight::zero(), LATEST_MIGRATION_VERSION - 2), + (::WeightInfo::migrate() + 1.into(), LATEST_MIGRATION_VERSION - 1), + (Weight::MAX, LATEST_MIGRATION_VERSION), + ]; + + for (weight, expected_version) in tests { + ExtBuilder::default() + .set_storage_version(LATEST_MIGRATION_VERSION - 2) + .build() + .execute_with(|| { + MigrationInProgress::::set(Some(Default::default())); + Contracts::on_idle(System::block_number(), weight); + assert_eq!(StorageVersion::get::>(), expected_version); + }); + } + } + + #[test] + fn migration_in_progress_works() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(1).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + MigrationInProgress::::set(Some(Default::default())); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + vec![], + deposit_limit::(), + ), + Error::::MigrationInProgress, + ); + assert_err!( + Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash), + Error::::MigrationInProgress, + ); + assert_err!( + Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB.clone(), code_hash), + Error::::MigrationInProgress, + ); + assert_err_ignore_postinfo!( + builder::call(BOB).build(), + Error::::MigrationInProgress + ); + assert_err_ignore_postinfo!( + builder::instantiate_with_code(wasm).value(100_000).build(), + Error::::MigrationInProgress, + ); + assert_err_ignore_postinfo!( + builder::instantiate(code_hash).value(100_000).build(), + Error::::MigrationInProgress, + ); + }); + } + + #[test] + fn instantiate_and_call_and_deposit_event() { + let (wasm, code_hash) = compile_module::("event_and_return_on_deploy").unwrap(); + + ExtBuilder::default().existential_deposit(1).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + let value = 100; + + // We determine the storage deposit limit after uploading because it depends on ALICEs + // free balance which is changed by uploading a module. + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + wasm, + deposit_limit::(), + )); + + // Drop previous events + initialize_block(2); + + // Check at the end to get hash on error easily + let addr = builder::bare_instantiate(Code::Existing(code_hash)) + .value(value) + .build_and_unwrap_account_id(); + assert!(ContractInfoOf::::contains_key(&addr)); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::System(frame_system::Event::NewAccount { + account: addr.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: addr.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: value, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::ContractEmitted { + contract: addr.clone(), + data: vec![1, 2, 3, 4] + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Instantiated { + deployer: ALICE, + contract: addr.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndHeld { + from: ALICE, + to: addr.clone(), + amount: test_utils::contract_info_storage_deposit(&addr), + } + ), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn deposit_event_max_value_limit() { + let (wasm, _code_hash) = compile_module::("event_size").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + // Create + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(30_000) + .build_and_unwrap_account_id(); + + // Call contract with allowed storage value. + assert_ok!(builder::call(addr.clone()) + .gas_limit(GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2)) // we are copying a huge buffer, + .data(limits::PAYLOAD_BYTES.encode()) + .build()); + + // Call contract with too large a storage value. + assert_err_ignore_postinfo!( + builder::call(addr).data((limits::PAYLOAD_BYTES + 1).encode()).build(), + Error::::ValueTooLarge, + ); + }); + } + + // Fail out of fuel (ref_time weight) in the engine. + #[test] + fn run_out_of_fuel_engine() { + let (wasm, _code_hash) = compile_module::("run_out_of_gas").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(100 * min_balance) + .build_and_unwrap_account_id(); + + // Call the contract with a fixed gas limit. It must run out of gas because it just + // loops forever. + assert_err_ignore_postinfo!( + builder::call(addr) + .gas_limit(Weight::from_parts(10_000_000_000, u64::MAX)) + .build(), + Error::::OutOfGas, + ); + }); + } + + // Fail out of fuel (ref_time weight) in the host. + #[test] + fn run_out_of_fuel_host() { + let (code, _hash) = compile_module::("chain_extension").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let addr = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + let gas_limit = Weight::from_parts(u32::MAX as u64, GAS_LIMIT.proof_size()); + + // Use chain extension to charge more ref_time than it is available. + let result = builder::bare_call(addr.clone()) + .gas_limit(gas_limit) + .data( + ExtensionInput { extension_id: 0, func_id: 2, extra: &u32::MAX.encode() } + .into(), + ) + .build() + .result; + assert_err!(result, >::OutOfGas); + }); + } + + #[test] + fn gas_syncs_work() { + let (code, _code_hash) = compile_module::("caller_is_origin_n").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let addr = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_account_id(); + + let result = builder::bare_call(addr.clone()).data(0u32.encode()).build(); + assert_ok!(result.result); + let engine_consumed_noop = result.gas_consumed.ref_time(); + + let result = builder::bare_call(addr.clone()).data(1u32.encode()).build(); + assert_ok!(result.result); + let gas_consumed_once = result.gas_consumed.ref_time(); + let host_consumed_once = + ::WeightInfo::seal_caller_is_origin().ref_time(); + let engine_consumed_once = + gas_consumed_once - host_consumed_once - engine_consumed_noop; + + let result = builder::bare_call(addr).data(2u32.encode()).build(); + assert_ok!(result.result); + let gas_consumed_twice = result.gas_consumed.ref_time(); + let host_consumed_twice = host_consumed_once * 2; + let engine_consumed_twice = + gas_consumed_twice - host_consumed_twice - engine_consumed_noop; + + // Second contract just repeats first contract's instructions twice. + // If runtime syncs gas with the engine properly, this should pass. + assert_eq!(engine_consumed_twice, engine_consumed_once * 2); + }); + } + + /// Check that contracts with the same account id have different trie ids. + /// Check the `Nonce` storage item for more information. + #[test] + fn instantiate_unique_trie_id() { + let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); + + ExtBuilder::default().existential_deposit(500).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, deposit_limit::()) + .unwrap(); + + // Instantiate the contract and store its trie id for later comparison. + let addr = + builder::bare_instantiate(Code::Existing(code_hash)).build_and_unwrap_account_id(); + let trie_id = get_contract(&addr).trie_id; + + // Try to instantiate it again without termination should yield an error. + assert_err_ignore_postinfo!( + builder::instantiate(code_hash).build(), + >::DuplicateContract, + ); + + // Terminate the contract. + assert_ok!(builder::call(addr.clone()).build()); + + // Re-Instantiate after termination. + assert_ok!(builder::instantiate(code_hash).build()); + + // Trie ids shouldn't match or we might have a collision + assert_ne!(trie_id, get_contract(&addr).trie_id); + }); + } + + #[test] + fn storage_work() { + let (code, _code_hash) = compile_module::("storage").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + let addr = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + builder::bare_call(addr).build_and_unwrap_result(); + }); + } + + #[test] + fn storage_max_value_limit() { + let (wasm, _code_hash) = compile_module::("storage_size").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + // Create + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(30_000) + .build_and_unwrap_account_id(); + get_contract(&addr); + + // Call contract with allowed storage value. + assert_ok!(builder::call(addr.clone()) + .gas_limit(GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2)) // we are copying a huge buffer + .data(limits::PAYLOAD_BYTES.encode()) + .build()); + + // Call contract with too large a storage value. + assert_err_ignore_postinfo!( + builder::call(addr).data((limits::PAYLOAD_BYTES + 1).encode()).build(), + Error::::ValueTooLarge, + ); + }); + } + + #[test] + fn transient_storage_work() { + let (code, _code_hash) = compile_module::("transient_storage").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + let addr = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + builder::bare_call(addr).build_and_unwrap_result(); + }); + } + + #[test] + fn transient_storage_limit_in_call() { + let (wasm_caller, _code_hash_caller) = + compile_module::("create_transient_storage_and_call").unwrap(); + let (wasm_callee, _code_hash_callee) = + compile_module::("set_transient_storage").unwrap(); + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create both contracts: Constructors do nothing. + let addr_caller = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); + let addr_callee = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + + // Call contracts with storage values within the limit. + // Caller and Callee contracts each set a transient storage value of size 100. + assert_ok!(builder::call(addr_caller.clone()) + .data((100u32, 100u32, &addr_callee).encode()) + .build(),); + + // Call a contract with a storage value that is too large. + // Limit exceeded in the caller contract. + assert_err_ignore_postinfo!( + builder::call(addr_caller.clone()) + .data((4u32 * 1024u32, 200u32, &addr_callee).encode()) + .build(), + >::OutOfTransientStorage, + ); + + // Call a contract with a storage value that is too large. + // Limit exceeded in the callee contract. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .data((50u32, 4 * 1024u32, &addr_callee).encode()) + .build(), + >::ContractTrapped + ); + }); + } + + #[test] + fn deploy_and_call_other_contract() { + let (caller_wasm, _caller_code_hash) = compile_module::("caller_contract").unwrap(); + let (callee_wasm, callee_code_hash) = compile_module::("return_with_data").unwrap(); + + ExtBuilder::default().existential_deposit(1).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + + // Create + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let caller_addr = builder::bare_instantiate(Code::Upload(caller_wasm)) + .value(100_000) + .build_and_unwrap_account_id(); + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + callee_wasm, + deposit_limit::(), + ) + .unwrap(); + + let callee_addr = Contracts::contract_address( + &caller_addr, + &callee_code_hash, + &[0, 1, 34, 51, 68, 85, 102, 119], // hard coded in wasm + &[], + ); + + // Drop previous events + initialize_block(2); + + // Call BOB contract, which attempts to instantiate and call the callee contract and + // makes various assertions on the results from those calls. + assert_ok!(builder::call(caller_addr.clone()) + .data(callee_code_hash.as_ref().to_vec()) + .build()); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::System(frame_system::Event::NewAccount { + account: callee_addr.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: callee_addr.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: callee_addr.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: caller_addr.clone(), + to: callee_addr.clone(), + amount: 32768 // hardcoded in wasm + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Instantiated { + deployer: caller_addr.clone(), + contract: callee_addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: caller_addr.clone(), + to: callee_addr.clone(), + amount: 32768, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(caller_addr.clone()), + contract: callee_addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: caller_addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndHeld { + from: ALICE, + to: callee_addr.clone(), + amount: test_utils::contract_info_storage_deposit(&callee_addr), + } + ), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn delegate_call() { + let (caller_wasm, _caller_code_hash) = compile_module::("delegate_call").unwrap(); + let (callee_wasm, callee_code_hash) = compile_module::("delegate_call_lib").unwrap(); + + ExtBuilder::default().existential_deposit(500).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the 'caller' + let caller_addr = builder::bare_instantiate(Code::Upload(caller_wasm)) + .value(300_000) + .build_and_unwrap_account_id(); + // Only upload 'callee' code + assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), callee_wasm, 100_000,)); + + assert_ok!(builder::call(caller_addr.clone()) + .value(1337) + .data(callee_code_hash.as_ref().to_vec()) + .build()); + }); + } + + #[test] + fn transfer_expendable_cannot_kill_account() { + let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the BOB contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(1_000) + .build_and_unwrap_account_id(); + + // Check that the BOB contract has been instantiated. + get_contract(&addr); + + let total_balance = ::Currency::total_balance(&addr); + + assert_eq!( + test_utils::get_balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr), + test_utils::contract_info_storage_deposit(&addr) + ); + + // Some ot the total balance is held, so it can't be transferred. + assert_err!( + <::Currency as Mutate>::transfer( + &addr, + &ALICE, + total_balance, + Preservation::Expendable, + ), + TokenError::FundsUnavailable, + ); + + assert_eq!(::Currency::total_balance(&addr), total_balance); + }); + } + + #[test] + fn cannot_self_destruct_through_draining() { + let (wasm, _code_hash) = compile_module::("drain").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let value = 1_000; + let min_balance = Contracts::min_balance(); + + // Instantiate the BOB contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(value) + .build_and_unwrap_account_id(); + + // Check that the BOB contract has been instantiated. + get_contract(&addr); + + // Call BOB which makes it send all funds to the zero address + // The contract code asserts that the transfer fails with the correct error code + assert_ok!(builder::call(addr.clone()).build()); + + // Make sure the account wasn't remove by sending all free balance away. + assert_eq!( + ::Currency::total_balance(&addr), + value + test_utils::contract_info_storage_deposit(&addr) + min_balance, + ); + }); + } + + #[test] + fn cannot_self_destruct_through_storage_refund_after_price_change() { + let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + + // Instantiate the BOB contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + + let info_deposit = test_utils::contract_info_storage_deposit(&addr); + + // Check that the contract has been instantiated and has the minimum balance + assert_eq!(get_contract(&addr).total_deposit(), info_deposit); + assert_eq!(get_contract(&addr).extra_deposit(), 0); + assert_eq!( + ::Currency::total_balance(&addr), + info_deposit + min_balance + ); + + // Create 100 bytes of storage with a price of per byte and a single storage item of + // price 2 + assert_ok!(builder::call(addr.clone()).data(100u32.to_le_bytes().to_vec()).build()); + assert_eq!(get_contract(&addr).total_deposit(), info_deposit + 102); + + // Increase the byte price and trigger a refund. This should not have any influence + // because the removal is pro rata and exactly those 100 bytes should have been + // removed. + DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = 500); + assert_ok!(builder::call(addr.clone()).data(0u32.to_le_bytes().to_vec()).build()); + + // Make sure the account wasn't removed by the refund + assert_eq!( + ::Currency::total_balance(&addr), + get_contract(&addr).total_deposit() + min_balance, + ); + assert_eq!(get_contract(&addr).extra_deposit(), 2); + }); + } + + #[test] + fn cannot_self_destruct_while_live() { + let (wasm, _code_hash) = compile_module::("self_destruct").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the BOB contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(100_000) + .build_and_unwrap_account_id(); + + // Check that the BOB contract has been instantiated. + get_contract(&addr); + + // Call BOB with input data, forcing it make a recursive call to itself to + // self-destruct, resulting in a trap. + assert_err_ignore_postinfo!( + builder::call(addr.clone()).data(vec![0]).build(), + Error::::ContractTrapped, + ); + + // Check that BOB is still there. + get_contract(&addr); + }); + } + + #[test] + fn self_destruct_works() { + let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); + ExtBuilder::default().existential_deposit(1_000).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&DJANGO, 1_000_000); + let min_balance = Contracts::min_balance(); + + // Instantiate the BOB contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(100_000) + .build_and_unwrap_account_id(); + + // Check that the BOB contract has been instantiated. + let _ = get_contract(&addr); + + let info_deposit = test_utils::contract_info_storage_deposit(&addr); + + // Drop all previous events + initialize_block(2); + + // Call BOB without input data which triggers termination. + assert_matches!(builder::call(addr.clone()).build(), Ok(_)); + + // Check that code is still there but refcount dropped to zero. + assert_refcount!(&code_hash, 0); + + // Check that account is gone + assert!(get_contract_checked(&addr).is_none()); + assert_eq!(::Currency::total_balance(&addr), 0); + + // Check that the beneficiary (django) got remaining balance. + assert_eq!( + ::Currency::free_balance(DJANGO), + 1_000_000 + 100_000 + min_balance + ); + + // Check that the Alice is missing Django's benefit. Within ALICE's total balance + // there's also the code upload deposit held. + assert_eq!( + ::Currency::total_balance(&ALICE), + 1_000_000 - (100_000 + min_balance) + ); + + pretty_assertions::assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Terminated { + contract: addr.clone(), + beneficiary: DJANGO + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndReleased { + from: addr.clone(), + to: ALICE, + amount: info_deposit, + } + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::System(frame_system::Event::KilledAccount { + account: addr.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: addr.clone(), + to: DJANGO, + amount: 100_000 + min_balance, + }), + topics: vec![], + }, + ], + ); + }); + } + + // This tests that one contract cannot prevent another from self-destructing by sending it + // additional funds after it has been drained. + #[test] + fn destroy_contract_and_transfer_funds() { + let (callee_wasm, callee_code_hash) = compile_module::("self_destruct").unwrap(); + let (caller_wasm, _caller_code_hash) = + compile_module::("destroy_and_transfer").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + // Create code hash for bob to instantiate + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + callee_wasm, + deposit_limit::(), + ) + .unwrap(); + + // This deploys the BOB contract, which in turn deploys the CHARLIE contract during + // construction. + let addr_bob = builder::bare_instantiate(Code::Upload(caller_wasm)) + .value(200_000) + .data(callee_code_hash.as_ref().to_vec()) + .build_and_unwrap_account_id(); + + // Check that the CHARLIE contract has been instantiated. + let addr_charlie = + Contracts::contract_address(&addr_bob, &callee_code_hash, &[], &[0x47, 0x11]); + get_contract(&addr_charlie); + + // Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct. + assert_ok!(builder::call(addr_bob).data(addr_charlie.encode()).build()); + + // Check that CHARLIE has moved on to the great beyond (ie. died). + assert!(get_contract_checked(&addr_charlie).is_none()); + }); + } + + #[test] + fn cannot_self_destruct_in_constructor() { + let (wasm, _) = compile_module::("self_destructing_constructor").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Fail to instantiate the BOB because the constructor calls seal_terminate. + assert_err_ignore_postinfo!( + builder::instantiate_with_code(wasm).value(100_000).build(), + Error::::TerminatedInConstructor, + ); + }); + } + + #[test] + fn crypto_hashes() { + let (wasm, _code_hash) = compile_module::("crypto_hashes").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the CRYPTO_HASHES contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(100_000) + .build_and_unwrap_account_id(); + // Perform the call. + let input = b"_DEAD_BEEF"; + use sp_io::hashing::*; + // Wraps a hash function into a more dynamic form usable for testing. + macro_rules! dyn_hash_fn { + ($name:ident) => { + Box::new(|input| $name(input).as_ref().to_vec().into_boxed_slice()) + }; + } + // All hash functions and their associated output byte lengths. + let test_cases: &[(Box Box<[u8]>>, usize)] = &[ + (dyn_hash_fn!(sha2_256), 32), + (dyn_hash_fn!(keccak_256), 32), + (dyn_hash_fn!(blake2_256), 32), + (dyn_hash_fn!(blake2_128), 16), + ]; + // Test the given hash functions for the input: "_DEAD_BEEF" + for (n, (hash_fn, expected_size)) in test_cases.iter().enumerate() { + // We offset data in the contract tables by 1. + let mut params = vec![(n + 1) as u8]; + params.extend_from_slice(input); + let result = + builder::bare_call(addr.clone()).data(params).build_and_unwrap_result(); + assert!(!result.did_revert()); + let expected = hash_fn(input.as_ref()); + assert_eq!(&result.data[..*expected_size], &*expected); + } + }) + } + + #[test] + fn transfer_return_code() { + let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + // Contract has only the minimal balance so any transfer will fail. + ::Currency::set_balance(&addr, min_balance); + let result = builder::bare_call(addr.clone()).build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::TransferFailed); + }); + } + + #[test] + fn call_return_code() { + let (caller_code, _caller_hash) = compile_module::("call_return_code").unwrap(); + let (callee_code, _callee_hash) = compile_module::("ok_trap_revert").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); + + let addr_bob = builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .data(vec![0]) + .build_and_unwrap_account_id(); + ::Currency::set_balance(&addr_bob, min_balance); + + // Contract calls into Django which is no valid contract + let result = builder::bare_call(addr_bob.clone()) + .data(AsRef::<[u8]>::as_ref(&DJANGO).to_vec()) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::NotCallable); + + let addr_django = builder::bare_instantiate(Code::Upload(callee_code)) + .origin(RuntimeOrigin::signed(CHARLIE)) + .value(min_balance * 100) + .data(vec![0]) + .build_and_unwrap_account_id(); + ::Currency::set_balance(&addr_django, min_balance); + + // Contract has only the minimal balance so any transfer will fail. + let result = builder::bare_call(addr_bob.clone()) + .data( + AsRef::<[u8]>::as_ref(&addr_django) + .iter() + .chain(&0u32.to_le_bytes()) + .cloned() + .collect(), + ) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::TransferFailed); + + // Contract has enough balance but callee reverts because "1" is passed. + ::Currency::set_balance(&addr_bob, min_balance + 1000); + let result = builder::bare_call(addr_bob.clone()) + .data( + AsRef::<[u8]>::as_ref(&addr_django) + .iter() + .chain(&1u32.to_le_bytes()) + .cloned() + .collect(), + ) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::CalleeReverted); + + // Contract has enough balance but callee traps because "2" is passed. + let result = builder::bare_call(addr_bob) + .data( + AsRef::<[u8]>::as_ref(&addr_django) + .iter() + .chain(&2u32.to_le_bytes()) + .cloned() + .collect(), + ) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::CalleeTrapped); + }); + } + + #[test] + fn instantiate_return_code() { + let (caller_code, _caller_hash) = + compile_module::("instantiate_return_code").unwrap(); + let (callee_code, callee_hash) = compile_module::("ok_trap_revert").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); + let callee_hash = callee_hash.as_ref().to_vec(); + + assert_ok!(builder::instantiate_with_code(callee_code) + .value(min_balance * 100) + .build()); + + let addr = builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + // Contract has only the minimal balance so any transfer will fail. + ::Currency::set_balance(&addr, min_balance); + let result = builder::bare_call(addr.clone()) + .data(callee_hash.clone()) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::TransferFailed); + + // Contract has enough balance but the passed code hash is invalid + ::Currency::set_balance(&addr, min_balance + 10_000); + let result = + builder::bare_call(addr.clone()).data(vec![0; 33]).build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::CodeNotFound); + + // Contract has enough balance but callee reverts because "1" is passed. + let result = builder::bare_call(addr.clone()) + .data(callee_hash.iter().chain(&1u32.to_le_bytes()).cloned().collect()) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::CalleeReverted); + + // Contract has enough balance but callee traps because "2" is passed. + let result = builder::bare_call(addr) + .data(callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect()) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::CalleeTrapped); + }); + } + + #[test] + fn disabled_chain_extension_errors_on_call() { + let (code, _hash) = compile_module::("chain_extension").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let addr = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + TestExtension::disable(); + assert_err_ignore_postinfo!( + builder::call(addr.clone()).data(vec![7u8; 8]).build(), + Error::::NoChainExtension, + ); + }); + } + + #[test] + fn chain_extension_works() { + let (code, _hash) = compile_module::("chain_extension").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let addr = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + // 0 = read input buffer and pass it through as output + let input: Vec = + ExtensionInput { extension_id: 0, func_id: 0, extra: &[99] }.into(); + let result = builder::bare_call(addr.clone()).data(input.clone()).build(); + assert_eq!(TestExtension::last_seen_buffer(), input); + assert_eq!(result.result.unwrap().data, input); + + // 1 = treat inputs as integer primitives and store the supplied integers + builder::bare_call(addr.clone()) + .data(ExtensionInput { extension_id: 0, func_id: 1, extra: &[] }.into()) + .build_and_unwrap_result(); + assert_eq!(TestExtension::last_seen_input_len(), 4); + + // 2 = charge some extra weight (amount supplied in the fifth byte) + let result = builder::bare_call(addr.clone()) + .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &0u32.encode() }.into()) + .build(); + assert_ok!(result.result); + let gas_consumed = result.gas_consumed; + let result = builder::bare_call(addr.clone()) + .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &42u32.encode() }.into()) + .build(); + assert_ok!(result.result); + assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 42); + let result = builder::bare_call(addr.clone()) + .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &95u32.encode() }.into()) + .build(); + assert_ok!(result.result); + assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 95); + + // 3 = diverging chain extension call that sets flags to 0x1 and returns a fixed buffer + let result = builder::bare_call(addr.clone()) + .data(ExtensionInput { extension_id: 0, func_id: 3, extra: &[] }.into()) + .build_and_unwrap_result(); + assert_eq!(result.flags, ReturnFlags::REVERT); + assert_eq!(result.data, vec![42, 99]); + + // diverging to second chain extension that sets flags to 0x1 and returns a fixed buffer + // We set the MSB part to 1 (instead of 0) which routes the request into the second + // extension + let result = builder::bare_call(addr.clone()) + .data(ExtensionInput { extension_id: 1, func_id: 0, extra: &[] }.into()) + .build_and_unwrap_result(); + assert_eq!(result.flags, ReturnFlags::REVERT); + assert_eq!(result.data, vec![0x4B, 0x1D]); + + // Diverging to third chain extension that is disabled + // We set the MSB part to 2 (instead of 0) which routes the request into the third + // extension + assert_err_ignore_postinfo!( + builder::call(addr.clone()) + .data(ExtensionInput { extension_id: 2, func_id: 0, extra: &[] }.into()) + .build(), + Error::::NoChainExtension, + ); + }); + } + + #[test] + fn chain_extension_temp_storage_works() { + let (code, _hash) = compile_module::("chain_extension_temp_storage").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let addr = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + // Call func 0 and func 1 back to back. + let stop_recursion = 0u8; + let mut input: Vec = + ExtensionInput { extension_id: 3, func_id: 0, extra: &[] }.into(); + input.extend_from_slice( + ExtensionInput { extension_id: 3, func_id: 1, extra: &[stop_recursion] } + .to_vec() + .as_ref(), + ); + + assert_ok!(builder::bare_call(addr.clone()).data(input.clone()).build().result); + }) + } + + #[test] + fn lazy_removal_works() { + let (code, _hash) = compile_module::("self_destruct").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let addr = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + let info = get_contract(&addr); + let trie = &info.child_trie_info(); + + // Put value into the contracts child trie + child::put(trie, &[99], &42); + + // Terminate the contract + assert_ok!(builder::call(addr.clone()).build()); + + // Contract info should be gone + assert!(!>::contains_key(&addr)); + + // But value should be still there as the lazy removal did not run, yet. + assert_matches!(child::get(trie, &[99]), Some(42)); + + // Run the lazy removal + Contracts::on_idle(System::block_number(), Weight::MAX); + + // Value should be gone now + assert_matches!(child::get::(trie, &[99]), None); + }); + } + + #[test] + fn lazy_batch_removal_works() { + let (code, _hash) = compile_module::("self_destruct").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let mut tries: Vec = vec![]; + + for i in 0..3u8 { + let addr = builder::bare_instantiate(Code::Upload(code.clone())) + .value(min_balance * 100) + .salt(vec![i]) + .build_and_unwrap_account_id(); + + let info = get_contract(&addr); + let trie = &info.child_trie_info(); + + // Put value into the contracts child trie + child::put(trie, &[99], &42); + + // Terminate the contract. Contract info should be gone, but value should be still + // there as the lazy removal did not run, yet. + assert_ok!(builder::call(addr.clone()).build()); + + assert!(!>::contains_key(&addr)); + assert_matches!(child::get(trie, &[99]), Some(42)); + + tries.push(trie.clone()) + } + + // Run single lazy removal + Contracts::on_idle(System::block_number(), Weight::MAX); + + // The single lazy removal should have removed all queued tries + for trie in tries.iter() { + assert_matches!(child::get::(trie, &[99]), None); + } + }); + } + + #[test] + fn lazy_removal_partial_remove_works() { + let (code, _hash) = compile_module::("self_destruct").unwrap(); + + // We create a contract with some extra keys above the weight limit + let extra_keys = 7u32; + let mut meter = WeightMeter::with_limit(Weight::from_parts(5_000_000_000, 100 * 1024)); + let (weight_per_key, max_keys) = ContractInfo::::deletion_budget(&meter); + let vals: Vec<_> = (0..max_keys + extra_keys) + .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode())) + .collect(); + + let mut ext = ExtBuilder::default().existential_deposit(50).build(); + + let trie = ext.execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let addr = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + let info = get_contract(&addr); + + // Put value into the contracts child trie + for val in &vals { + info.write(&Key::Fix(val.0), Some(val.2.clone()), None, false).unwrap(); + } + >::insert(&addr, info.clone()); + + // Terminate the contract + assert_ok!(builder::call(addr.clone()).build()); + + // Contract info should be gone + assert!(!>::contains_key(&addr)); + + let trie = info.child_trie_info(); + + // But value should be still there as the lazy removal did not run, yet. + for val in &vals { + assert_eq!(child::get::(&trie, &blake2_256(&val.0)), Some(val.1)); + } + + trie.clone() + }); + + // The lazy removal limit only applies to the backend but not to the overlay. + // This commits all keys from the overlay to the backend. + ext.commit_all().unwrap(); + + ext.execute_with(|| { + // Run the lazy removal + ContractInfo::::process_deletion_queue_batch(&mut meter); + + // Weight should be exhausted because we could not even delete all keys + assert!(!meter.can_consume(weight_per_key)); + + let mut num_deleted = 0u32; + let mut num_remaining = 0u32; + + for val in &vals { + match child::get::(&trie, &blake2_256(&val.0)) { + None => num_deleted += 1, + Some(x) if x == val.1 => num_remaining += 1, + Some(_) => panic!("Unexpected value in contract storage"), + } + } + + // All but one key is removed + assert_eq!(num_deleted + num_remaining, vals.len() as u32); + assert_eq!(num_deleted, max_keys); + assert_eq!(num_remaining, extra_keys); + }); + } + + #[test] + fn lazy_removal_does_no_run_on_low_remaining_weight() { + let (code, _hash) = compile_module::("self_destruct").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let addr = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + let info = get_contract(&addr); + let trie = &info.child_trie_info(); + + // Put value into the contracts child trie + child::put(trie, &[99], &42); + + // Terminate the contract + assert_ok!(builder::call(addr.clone()).build()); + + // Contract info should be gone + assert!(!>::contains_key(&addr)); + + // But value should be still there as the lazy removal did not run, yet. + assert_matches!(child::get(trie, &[99]), Some(42)); + + // Assign a remaining weight which is too low for a successful deletion of the contract + let low_remaining_weight = + <::WeightInfo as WeightInfo>::on_process_deletion_queue_batch(); + + // Run the lazy removal + Contracts::on_idle(System::block_number(), low_remaining_weight); + + // Value should still be there, since remaining weight was too low for removal + assert_matches!(child::get::(trie, &[99]), Some(42)); + + // Run the lazy removal while deletion_queue is not full + Contracts::on_initialize(System::block_number()); + + // Value should still be there, since deletion_queue was not full + assert_matches!(child::get::(trie, &[99]), Some(42)); + + // Run on_idle with max remaining weight, this should remove the value + Contracts::on_idle(System::block_number(), Weight::MAX); + + // Value should be gone + assert_matches!(child::get::(trie, &[99]), None); + }); + } + + #[test] + fn lazy_removal_does_not_use_all_weight() { + let (code, _hash) = compile_module::("self_destruct").unwrap(); + + let mut meter = WeightMeter::with_limit(Weight::from_parts(5_000_000_000, 100 * 1024)); + let mut ext = ExtBuilder::default().existential_deposit(50).build(); + + let (trie, vals, weight_per_key) = ext.execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let addr = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + let info = get_contract(&addr); + let (weight_per_key, max_keys) = ContractInfo::::deletion_budget(&meter); + assert!(max_keys > 0); + + // We create a contract with one less storage item than we can remove within the limit + let vals: Vec<_> = (0..max_keys - 1) + .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode())) + .collect(); + + // Put value into the contracts child trie + for val in &vals { + info.write(&Key::Fix(val.0), Some(val.2.clone()), None, false).unwrap(); + } + >::insert(&addr, info.clone()); + + // Terminate the contract + assert_ok!(builder::call(addr.clone()).build()); + + // Contract info should be gone + assert!(!>::contains_key(&addr)); + + let trie = info.child_trie_info(); + + // But value should be still there as the lazy removal did not run, yet. + for val in &vals { + assert_eq!(child::get::(&trie, &blake2_256(&val.0)), Some(val.1)); + } + + (trie, vals, weight_per_key) + }); + + // The lazy removal limit only applies to the backend but not to the overlay. + // This commits all keys from the overlay to the backend. + ext.commit_all().unwrap(); + + ext.execute_with(|| { + // Run the lazy removal + ContractInfo::::process_deletion_queue_batch(&mut meter); + let base_weight = + <::WeightInfo as WeightInfo>::on_process_deletion_queue_batch(); + assert_eq!(meter.consumed(), weight_per_key.mul(vals.len() as _) + base_weight); + + // All the keys are removed + for val in vals { + assert_eq!(child::get::(&trie, &blake2_256(&val.0)), None); + } + }); + } + + #[test] + fn deletion_queue_ring_buffer_overflow() { + let (code, _hash) = compile_module::("self_destruct").unwrap(); + let mut ext = ExtBuilder::default().existential_deposit(50).build(); + + // setup the deletion queue with custom counters + ext.execute_with(|| { + let queue = DeletionQueueManager::from_test_values(u32::MAX - 1, u32::MAX - 1); + >::set(queue); + }); + + // commit the changes to the storage + ext.commit_all().unwrap(); + + ext.execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let mut tries: Vec = vec![]; + + // add 3 contracts to the deletion queue + for i in 0..3u8 { + let addr = builder::bare_instantiate(Code::Upload(code.clone())) + .value(min_balance * 100) + .salt(vec![i]) + .build_and_unwrap_account_id(); + + let info = get_contract(&addr); + let trie = &info.child_trie_info(); + + // Put value into the contracts child trie + child::put(trie, &[99], &42); + + // Terminate the contract. Contract info should be gone, but value should be still + // there as the lazy removal did not run, yet. + assert_ok!(builder::call(addr.clone()).build()); + + assert!(!>::contains_key(&addr)); + assert_matches!(child::get(trie, &[99]), Some(42)); + + tries.push(trie.clone()) + } + + // Run single lazy removal + Contracts::on_idle(System::block_number(), Weight::MAX); + + // The single lazy removal should have removed all queued tries + for trie in tries.iter() { + assert_matches!(child::get::(trie, &[99]), None); + } + + // insert and delete counter values should go from u32::MAX - 1 to 1 + assert_eq!(>::get().as_test_tuple(), (1, 1)); + }) + } + #[test] + fn refcounter() { + let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + + // Create two contracts with the same code and check that they do in fact share it. + let addr0 = builder::bare_instantiate(Code::Upload(wasm.clone())) + .value(min_balance * 100) + .salt(vec![0]) + .build_and_unwrap_account_id(); + let addr1 = builder::bare_instantiate(Code::Upload(wasm.clone())) + .value(min_balance * 100) + .salt(vec![1]) + .build_and_unwrap_account_id(); + assert_refcount!(code_hash, 2); + + // Sharing should also work with the usual instantiate call + let addr2 = builder::bare_instantiate(Code::Existing(code_hash)) + .value(min_balance * 100) + .salt(vec![2]) + .build_and_unwrap_account_id(); + assert_refcount!(code_hash, 3); + + // Terminating one contract should decrement the refcount + assert_ok!(builder::call(addr0).build()); + assert_refcount!(code_hash, 2); + + // remove another one + assert_ok!(builder::call(addr1).build()); + assert_refcount!(code_hash, 1); + + // Pristine code should still be there + PristineCode::::get(code_hash).unwrap(); + + // remove the last contract + assert_ok!(builder::call(addr2).build()); + assert_refcount!(code_hash, 0); + + // refcount is `0` but code should still exists because it needs to be removed manually + assert!(crate::PristineCode::::contains_key(&code_hash)); + }); + } + + #[test] + fn debug_message_works() { + let (wasm, _code_hash) = compile_module::("debug_message_works").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(30_000) + .build_and_unwrap_account_id(); + let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build(); + + assert_matches!(result.result, Ok(_)); + assert_eq!(std::str::from_utf8(&result.debug_message).unwrap(), "Hello World!"); + }); + } + + #[test] + fn debug_message_logging_disabled() { + let (wasm, _code_hash) = compile_module::("debug_message_logging_disabled").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(30_000) + .build_and_unwrap_account_id(); + // the dispatchables always run without debugging + assert_ok!(Contracts::call( + RuntimeOrigin::signed(ALICE), + addr, + 0, + GAS_LIMIT, + deposit_limit::(), + vec![] + )); + }); + } + + #[test] + fn debug_message_invalid_utf8() { + let (wasm, _code_hash) = compile_module::("debug_message_invalid_utf8").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(30_000) + .build_and_unwrap_account_id(); + let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build(); + assert_ok!(result.result); + assert!(result.debug_message.is_empty()); + }); + } + + #[test] + fn gas_estimation_for_subcalls() { + let (caller_code, _caller_hash) = compile_module::("call_with_limit").unwrap(); + let (call_runtime_code, _caller_hash) = compile_module::("call_runtime").unwrap(); + let (dummy_code, _callee_hash) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 2_000 * min_balance); + + let addr_caller = builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + let addr_dummy = builder::bare_instantiate(Code::Upload(dummy_code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + let addr_call_runtime = builder::bare_instantiate(Code::Upload(call_runtime_code)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + // Run the test for all of those weight limits for the subcall + let weights = [ + Weight::zero(), + GAS_LIMIT, + GAS_LIMIT * 2, + GAS_LIMIT / 5, + Weight::from_parts(0, GAS_LIMIT.proof_size()), + Weight::from_parts(GAS_LIMIT.ref_time(), 0), + ]; + + // This call is passed to the sub call in order to create a large `required_weight` + let runtime_call = RuntimeCall::Dummy(pallet_dummy::Call::overestimate_pre_charge { + pre_charge: Weight::from_parts(10_000_000_000, 512 * 1024), + actual_weight: Weight::from_parts(1, 1), + }) + .encode(); + + // Encodes which contract should be sub called with which input + let sub_calls: [(&[u8], Vec<_>, bool); 2] = [ + (addr_dummy.as_ref(), vec![], false), + (addr_call_runtime.as_ref(), runtime_call, true), + ]; + + for weight in weights { + for (sub_addr, sub_input, out_of_gas_in_subcall) in &sub_calls { + let input: Vec = sub_addr + .iter() + .cloned() + .chain(weight.ref_time().to_le_bytes()) + .chain(weight.proof_size().to_le_bytes()) + .chain(sub_input.clone()) + .collect(); + + // Call in order to determine the gas that is required for this call + let result_orig = + builder::bare_call(addr_caller.clone()).data(input.clone()).build(); + assert_ok!(&result_orig.result); + + // If the out of gas happens in the subcall the caller contract + // will just trap. Otherwise we would need to forward an error + // code to signal that the sub contract ran out of gas. + let error: DispatchError = if *out_of_gas_in_subcall { + assert!(result_orig.gas_required.all_gt(result_orig.gas_consumed)); + >::ContractTrapped.into() + } else { + assert_eq!(result_orig.gas_required, result_orig.gas_consumed); + >::OutOfGas.into() + }; + + // Make the same call using the estimated gas. Should succeed. + let result = builder::bare_call(addr_caller.clone()) + .gas_limit(result_orig.gas_required) + .storage_deposit_limit(result_orig.storage_deposit.charge_or_zero()) + .data(input.clone()) + .build(); + assert_ok!(&result.result); + + // Check that it fails with too little ref_time + let result = builder::bare_call(addr_caller.clone()) + .gas_limit(result_orig.gas_required.sub_ref_time(1)) + .storage_deposit_limit(result_orig.storage_deposit.charge_or_zero()) + .data(input.clone()) + .build(); + assert_err!(result.result, error); + + // Check that it fails with too little proof_size + let result = builder::bare_call(addr_caller.clone()) + .gas_limit(result_orig.gas_required.sub_proof_size(1)) + .storage_deposit_limit(result_orig.storage_deposit.charge_or_zero()) + .data(input.clone()) + .build(); + assert_err!(result.result, error); + } + } + }); + } + + #[test] + fn gas_estimation_call_runtime() { + let (caller_code, _caller_hash) = compile_module::("call_runtime").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); + + let addr_caller = builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .salt(vec![0]) + .build_and_unwrap_account_id(); + + // Call something trivial with a huge gas limit so that we can observe the effects + // of pre-charging. This should create a difference between consumed and required. + let call = RuntimeCall::Dummy(pallet_dummy::Call::overestimate_pre_charge { + pre_charge: Weight::from_parts(10_000_000, 1_000), + actual_weight: Weight::from_parts(100, 100), + }); + let result = builder::bare_call(addr_caller.clone()).data(call.encode()).build(); + // contract encodes the result of the dispatch runtime + let outcome = u32::decode(&mut result.result.unwrap().data.as_ref()).unwrap(); + assert_eq!(outcome, 0); + assert!(result.gas_required.all_gt(result.gas_consumed)); + + // Make the same call using the required gas. Should succeed. + assert_ok!( + builder::bare_call(addr_caller) + .gas_limit(result.gas_required) + .data(call.encode()) + .build() + .result + ); + }); + } + + #[test] + fn call_runtime_reentrancy_guarded() { + let (caller_code, _caller_hash) = compile_module::("call_runtime").unwrap(); + let (callee_code, _callee_hash) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); + + let addr_caller = builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .salt(vec![0]) + .build_and_unwrap_account_id(); + + let addr_callee = builder::bare_instantiate(Code::Upload(callee_code)) + .value(min_balance * 100) + .salt(vec![1]) + .build_and_unwrap_account_id(); + + // Call pallet_revive call() dispatchable + let call = RuntimeCall::Contracts(crate::Call::call { + dest: addr_callee, + value: 0, + gas_limit: GAS_LIMIT / 3, + storage_deposit_limit: deposit_limit::(), + data: vec![], + }); + + // Call runtime to re-enter back to contracts engine by + // calling dummy contract + let result = builder::bare_call(addr_caller.clone()) + .data(call.encode()) + .build_and_unwrap_result(); + // Call to runtime should fail because of the re-entrancy guard + assert_return_code!(result, RuntimeReturnCode::CallRuntimeFailed); + }); + } + + #[test] + fn ecdsa_recover() { + let (wasm, _code_hash) = compile_module::("ecdsa_recover").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the ecdsa_recover contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(100_000) + .build_and_unwrap_account_id(); + + #[rustfmt::skip] + let signature: [u8; 65] = [ + 161, 234, 203, 74, 147, 96, 51, 212, 5, 174, 231, 9, 142, 48, 137, 201, + 162, 118, 192, 67, 239, 16, 71, 216, 125, 86, 167, 139, 70, 7, 86, 241, + 33, 87, 154, 251, 81, 29, 160, 4, 176, 239, 88, 211, 244, 232, 232, 52, + 211, 234, 100, 115, 230, 47, 80, 44, 152, 166, 62, 50, 8, 13, 86, 175, + 28, + ]; + #[rustfmt::skip] + let message_hash: [u8; 32] = [ + 162, 28, 244, 179, 96, 76, 244, 178, 188, 83, 230, 248, 143, 106, 77, 117, + 239, 95, 244, 171, 65, 95, 62, 153, 174, 166, 182, 28, 130, 73, 196, 208 + ]; + #[rustfmt::skip] + const EXPECTED_COMPRESSED_PUBLIC_KEY: [u8; 33] = [ + 2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, + 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, + 152, + ]; + let mut params = vec![]; + params.extend_from_slice(&signature); + params.extend_from_slice(&message_hash); + assert!(params.len() == 65 + 32); + let result = builder::bare_call(addr.clone()).data(params).build_and_unwrap_result(); + assert!(!result.did_revert()); + assert_eq!(result.data, EXPECTED_COMPRESSED_PUBLIC_KEY); + }) + } + + #[test] + fn bare_instantiate_returns_events() { + let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let result = builder::bare_instantiate(Code::Upload(wasm)) + .value(min_balance * 100) + .collect_events(CollectEvents::UnsafeCollect) + .build(); + + let events = result.events.unwrap(); + assert!(!events.is_empty()); + assert_eq!(events, System::events()); + }); + } + + #[test] + fn bare_instantiate_does_not_return_events() { + let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let result = + builder::bare_instantiate(Code::Upload(wasm)).value(min_balance * 100).build(); + + let events = result.events; + assert!(!System::events().is_empty()); + assert!(events.is_none()); + }); + } + + #[test] + fn bare_call_returns_events() { + let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + let result = builder::bare_call(addr.clone()) + .collect_events(CollectEvents::UnsafeCollect) + .build(); + + let events = result.events.unwrap(); + assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success); + assert!(!events.is_empty()); + assert_eq!(events, System::events()); + }); + } + + #[test] + fn bare_call_does_not_return_events() { + let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(min_balance * 100) + .build_and_unwrap_account_id(); + + let result = builder::bare_call(addr.clone()).build(); + + let events = result.events; + assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success); + assert!(!System::events().is_empty()); + assert!(events.is_none()); + }); + } + + #[test] + fn sr25519_verify() { + let (wasm, _code_hash) = compile_module::("sr25519_verify").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the sr25519_verify contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(100_000) + .build_and_unwrap_account_id(); + + let call_with = |message: &[u8; 11]| { + // Alice's signature for "hello world" + #[rustfmt::skip] + let signature: [u8; 64] = [ + 184, 49, 74, 238, 78, 165, 102, 252, 22, 92, 156, 176, 124, 118, 168, 116, 247, + 99, 0, 94, 2, 45, 9, 170, 73, 222, 182, 74, 60, 32, 75, 64, 98, 174, 69, 55, 83, + 85, 180, 98, 208, 75, 231, 57, 205, 62, 4, 105, 26, 136, 172, 17, 123, 99, 90, 255, + 228, 54, 115, 63, 30, 207, 205, 131, + ]; + + // Alice's public key + #[rustfmt::skip] + let public_key: [u8; 32] = [ + 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, + 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, + ]; + + let mut params = vec![]; + params.extend_from_slice(&signature); + params.extend_from_slice(&public_key); + params.extend_from_slice(message); + + builder::bare_call(addr.clone()).data(params).build_and_unwrap_result() + }; + + // verification should succeed for "hello world" + assert_return_code!(call_with(&b"hello world"), RuntimeReturnCode::Success); + + // verification should fail for other messages + assert_return_code!(call_with(&b"hello worlD"), RuntimeReturnCode::Sr25519VerifyFailed); + }); + } + + #[test] + fn failed_deposit_charge_should_roll_back_call() { + let (wasm_caller, _) = compile_module::("call_runtime_and_call").unwrap(); + let (wasm_callee, _) = compile_module::("store_call").unwrap(); + const ED: u64 = 200; + + let execute = || { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate both contracts. + let addr_caller = builder::bare_instantiate(Code::Upload(wasm_caller.clone())) + .build_and_unwrap_account_id(); + let addr_callee = builder::bare_instantiate(Code::Upload(wasm_callee.clone())) + .build_and_unwrap_account_id(); + + // Give caller proxy access to Alice. + assert_ok!(Proxy::add_proxy( + RuntimeOrigin::signed(ALICE), + addr_caller.clone(), + (), + 0 + )); + + // Create a Proxy call that will attempt to transfer away Alice's balance. + let transfer_call = + Box::new(RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { + dest: CHARLIE, + value: pallet_balances::Pallet::::free_balance(&ALICE) - 2 * ED, + })); + + // Wrap the transfer call in a proxy call. + let transfer_proxy_call = RuntimeCall::Proxy(pallet_proxy::Call::proxy { + real: ALICE, + force_proxy_type: Some(()), + call: transfer_call, + }); + + let data = ( + (ED - DepositPerItem::get()) as u32, // storage length + addr_callee, + transfer_proxy_call, + ); + + builder::call(addr_caller).data(data.encode()).build() + }) + }; + + // With a low enough deposit per byte, the call should succeed. + let result = execute().unwrap(); + + // Bump the deposit per byte to a high value to trigger a FundsUnavailable error. + DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = 20); + assert_err_with_weight!(execute(), TokenError::FundsUnavailable, result.actual_weight); + } + + #[test] + fn upload_code_works() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert!(!PristineCode::::contains_key(&code_hash)); + + assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,)); + // Ensure the contract was stored and get expected deposit amount to be reserved. + let deposit_expected = expected_deposit(ensure_stored(code_hash)); + + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE + }), + topics: vec![], + },] + ); + }); + } + + #[test] + fn upload_code_limit_too_low() { + let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + let deposit_expected = expected_deposit(wasm.len()); + let deposit_insufficient = deposit_expected.saturating_sub(1); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert_noop!( + Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, deposit_insufficient,), + >::StorageDepositLimitExhausted, + ); + + assert_eq!(System::events(), vec![]); + }); + } + + #[test] + fn upload_code_not_enough_balance() { + let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + let deposit_expected = expected_deposit(wasm.len()); + let deposit_insufficient = deposit_expected.saturating_sub(1); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, deposit_insufficient); + + // Drop previous events + initialize_block(2); + + assert_noop!( + Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,), + >::StorageDepositNotEnoughFunds, + ); + + assert_eq!(System::events(), vec![]); + }); + } + + #[test] + fn remove_code_works() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,)); + // Ensure the contract was stored and get expected deposit amount to be reserved. + let deposit_expected = expected_deposit(ensure_stored(code_hash)); + + assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash)); + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeRemoved { + code_hash, + deposit_released: deposit_expected, + remover: ALICE + }), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn remove_code_wrong_origin() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,)); + // Ensure the contract was stored and get expected deposit amount to be reserved. + let deposit_expected = expected_deposit(ensure_stored(code_hash)); + + assert_noop!( + Contracts::remove_code(RuntimeOrigin::signed(BOB), code_hash), + sp_runtime::traits::BadOrigin, + ); + + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE + }), + topics: vec![], + },] + ); + }); + } + + #[test] + fn remove_code_in_use() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + assert_ok!(builder::instantiate_with_code(wasm).build()); + + // Drop previous events + initialize_block(2); + + assert_noop!( + Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash), + >::CodeInUse, + ); + + assert_eq!(System::events(), vec![]); + }); + } + + #[test] + fn remove_code_not_found() { + let (_wasm, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert_noop!( + Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash), + >::CodeNotFound, + ); + + assert_eq!(System::events(), vec![]); + }); + } + + #[test] + fn instantiate_with_zero_balance_works() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + + // Drop previous events + initialize_block(2); + + // Instantiate the BOB contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + + // Ensure the contract was stored and get expected deposit amount to be reserved. + let deposit_expected = expected_deposit(ensure_stored(code_hash)); + + // Make sure the account exists even though no free balance was send + assert_eq!(::Currency::free_balance(&addr), min_balance); + assert_eq!( + ::Currency::total_balance(&addr), + min_balance + test_utils::contract_info_storage_deposit(&addr) + ); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::System(frame_system::Event::NewAccount { + account: addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: addr.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Instantiated { + deployer: ALICE, + contract: addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndHeld { + from: ALICE, + to: addr.clone(), + amount: test_utils::contract_info_storage_deposit(&addr), + } + ), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn instantiate_with_below_existential_deposit_works() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + let value = 50; + + // Drop previous events + initialize_block(2); + + // Instantiate the BOB contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(value) + .build_and_unwrap_account_id(); + + // Ensure the contract was stored and get expected deposit amount to be reserved. + let deposit_expected = expected_deposit(ensure_stored(code_hash)); + // Make sure the account exists even though not enough free balance was send + assert_eq!(::Currency::free_balance(&addr), min_balance + value); + assert_eq!( + ::Currency::total_balance(&addr), + min_balance + value + test_utils::contract_info_storage_deposit(&addr) + ); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::System(frame_system::Event::NewAccount { + account: addr.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: addr.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: 50, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Instantiated { + deployer: ALICE, + contract: addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndHeld { + from: ALICE, + to: addr.clone(), + amount: test_utils::contract_info_storage_deposit(&addr), + } + ), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn storage_deposit_works() { + let (wasm, _code_hash) = compile_module::("multi_store").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + + let mut deposit = test_utils::contract_info_storage_deposit(&addr); + + // Drop previous events + initialize_block(2); + + // Create storage + assert_ok!(builder::call(addr.clone()).value(42).data((50u32, 20u32).encode()).build()); + // 4 is for creating 2 storage items + let charged0 = 4 + 50 + 20; + deposit += charged0; + assert_eq!(get_contract(&addr).total_deposit(), deposit); + + // Add more storage (but also remove some) + assert_ok!(builder::call(addr.clone()).data((100u32, 10u32).encode()).build()); + let charged1 = 50 - 10; + deposit += charged1; + assert_eq!(get_contract(&addr).total_deposit(), deposit); + + // Remove more storage (but also add some) + assert_ok!(builder::call(addr.clone()).data((10u32, 20u32).encode()).build()); + // -1 for numeric instability + let refunded0 = 90 - 10 - 1; + deposit -= refunded0; + assert_eq!(get_contract(&addr).total_deposit(), deposit); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: addr.clone(), + amount: 42, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndHeld { + from: ALICE, + to: addr.clone(), + amount: charged0, + } + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndHeld { + from: ALICE, + to: addr.clone(), + amount: charged1, + } + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndReleased { + from: addr.clone(), + to: ALICE, + amount: refunded0, + } + ), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn storage_deposit_callee_works() { + let (wasm_caller, _code_hash_caller) = compile_module::("call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + + // Create both contracts: Constructors do nothing. + let addr_caller = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); + let addr_callee = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + + assert_ok!(builder::call(addr_caller).data((100u32, &addr_callee).encode()).build()); + + let callee = get_contract(&addr_callee); + let deposit = DepositPerByte::get() * 100 + DepositPerItem::get() * 1; + + assert_eq!(test_utils::get_balance(&addr_callee), min_balance); + assert_eq!( + callee.total_deposit(), + deposit + test_utils::contract_info_storage_deposit(&addr_callee) + ); + }); + } + + #[test] + fn set_code_extrinsic() { + let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (new_wasm, new_code_hash) = compile_module::("crypto_hashes").unwrap(); + + assert_ne!(code_hash, new_code_hash); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + new_wasm, + deposit_limit::(), + )); + + // Drop previous events + initialize_block(2); + + assert_eq!(get_contract(&addr).code_hash, code_hash); + assert_refcount!(&code_hash, 1); + assert_refcount!(&new_code_hash, 0); + + // only root can execute this extrinsic + assert_noop!( + Contracts::set_code(RuntimeOrigin::signed(ALICE), addr.clone(), new_code_hash), + sp_runtime::traits::BadOrigin, + ); + assert_eq!(get_contract(&addr).code_hash, code_hash); + assert_refcount!(&code_hash, 1); + assert_refcount!(&new_code_hash, 0); + assert_eq!(System::events(), vec![]); + + // contract must exist + assert_noop!( + Contracts::set_code(RuntimeOrigin::root(), BOB, new_code_hash), + >::ContractNotFound, + ); + assert_eq!(get_contract(&addr).code_hash, code_hash); + assert_refcount!(&code_hash, 1); + assert_refcount!(&new_code_hash, 0); + assert_eq!(System::events(), vec![]); + + // new code hash must exist + assert_noop!( + Contracts::set_code(RuntimeOrigin::root(), addr.clone(), Default::default()), + >::CodeNotFound, + ); + assert_eq!(get_contract(&addr).code_hash, code_hash); + assert_refcount!(&code_hash, 1); + assert_refcount!(&new_code_hash, 0); + assert_eq!(System::events(), vec![]); + + // successful call + assert_ok!(Contracts::set_code(RuntimeOrigin::root(), addr.clone(), new_code_hash)); + assert_eq!(get_contract(&addr).code_hash, new_code_hash); + assert_refcount!(&code_hash, 0); + assert_refcount!(&new_code_hash, 1); + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(pallet_revive::Event::ContractCodeUpdated { + contract: addr.clone(), + new_code_hash, + old_code_hash: code_hash, + }), + topics: vec![], + },] + ); + }); + } + + #[test] + fn slash_cannot_kill_account() { + let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let value = 700; + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + + let addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(value) + .build_and_unwrap_account_id(); + + // Drop previous events + initialize_block(2); + + let info_deposit = test_utils::contract_info_storage_deposit(&addr); + + assert_eq!( + test_utils::get_balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr), + info_deposit + ); + + assert_eq!( + ::Currency::total_balance(&addr), + info_deposit + value + min_balance + ); + + // Try to destroy the account of the contract by slashing the total balance. + // The account does not get destroyed because slashing only affects the balance held + // under certain `reason`. Slashing can for example happen if the contract takes part + // in staking. + let _ = ::Currency::slash( + &HoldReason::StorageDepositReserve.into(), + &addr, + ::Currency::total_balance(&addr), + ); + + // Slashing only removed the balance held. + assert_eq!(::Currency::total_balance(&addr), value + min_balance); + }); + } + + #[test] + fn contract_reverted() { + let (wasm, code_hash) = compile_module::("return_with_data").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let flags = ReturnFlags::REVERT; + let buffer = [4u8, 8, 15, 16, 23, 42]; + let input = (flags.bits(), buffer).encode(); + + // We just upload the code for later use + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + wasm.clone(), + deposit_limit::(), + )); + + // Calling extrinsic: revert leads to an error + assert_err_ignore_postinfo!( + builder::instantiate(code_hash).data(input.clone()).build(), + >::ContractReverted, + ); + + // Calling extrinsic: revert leads to an error + assert_err_ignore_postinfo!( + builder::instantiate_with_code(wasm).data(input.clone()).build(), + >::ContractReverted, + ); + + // Calling directly: revert leads to success but the flags indicate the error + // This is just a different way of transporting the error that allows the read out + // the `data` which is only there on success. Obviously, the contract isn't + // instantiated. + let result = builder::bare_instantiate(Code::Existing(code_hash)) + .data(input.clone()) + .build_and_unwrap_result(); + assert_eq!(result.result.flags, flags); + assert_eq!(result.result.data, buffer); + assert!(!>::contains_key(result.account_id)); + + // Pass empty flags and therefore successfully instantiate the contract for later use. + let addr = builder::bare_instantiate(Code::Existing(code_hash)) + .data(ReturnFlags::empty().bits().encode()) + .build_and_unwrap_account_id(); + + // Calling extrinsic: revert leads to an error + assert_err_ignore_postinfo!( + builder::call(addr.clone()).data(input.clone()).build(), + >::ContractReverted, + ); + + // Calling directly: revert leads to success but the flags indicate the error + let result = builder::bare_call(addr.clone()).data(input).build_and_unwrap_result(); + assert_eq!(result.flags, flags); + assert_eq!(result.data, buffer); + }); + } + + #[test] + fn set_code_hash() { + let (wasm, code_hash) = compile_module::("set_code_hash").unwrap(); + let (new_wasm, new_code_hash) = + compile_module::("new_set_code_hash_contract").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the 'caller' + let contract_addr = builder::bare_instantiate(Code::Upload(wasm)) + .value(300_000) + .build_and_unwrap_account_id(); + // upload new code + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + new_wasm.clone(), + deposit_limit::(), + )); + + System::reset_events(); + + // First call sets new code_hash and returns 1 + let result = builder::bare_call(contract_addr.clone()) + .data(new_code_hash.as_ref().to_vec()) + .debug(DebugInfo::UnsafeDebug) + .build_and_unwrap_result(); + assert_return_code!(result, 1); + + // Second calls new contract code that returns 2 + let result = builder::bare_call(contract_addr.clone()) + .debug(DebugInfo::UnsafeDebug) + .build_and_unwrap_result(); + assert_return_code!(result, 2); + + // Checking for the last event only + assert_eq!( + &System::events(), + &[ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::ContractCodeUpdated { + contract: contract_addr.clone(), + new_code_hash, + old_code_hash: code_hash, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: contract_addr.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: contract_addr.clone(), + }), + topics: vec![], + }, + ], + ); + }); + } + + #[test] + fn storage_deposit_limit_is_enforced() { + let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + + // Setting insufficient storage_deposit should fail. + assert_err!( + builder::bare_instantiate(Code::Upload(wasm.clone())) + // expected deposit is 2 * ed + 3 for the call + .storage_deposit_limit((2 * min_balance + 3 - 1).into()) + .build() + .result, + >::StorageDepositLimitExhausted, + ); + + // Instantiate the BOB contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + + let info_deposit = test_utils::contract_info_storage_deposit(&addr); + // Check that the BOB contract has been instantiated and has the minimum balance + assert_eq!(get_contract(&addr).total_deposit(), info_deposit); + assert_eq!( + ::Currency::total_balance(&addr), + info_deposit + min_balance + ); + + // Create 1 byte of storage with a price of per byte, + // setting insufficient deposit limit, as it requires 3 Balance: + // 2 for the item added + 1 for the new storage item. + assert_err_ignore_postinfo!( + builder::call(addr.clone()) + .storage_deposit_limit(2) + .data(1u32.to_le_bytes().to_vec()) + .build(), + >::StorageDepositLimitExhausted, + ); + + // Create 1 byte of storage, should cost 3 Balance: + // 2 for the item added + 1 for the new storage item. + // Should pass as it fallbacks to DefaultDepositLimit. + assert_ok!(builder::call(addr.clone()) + .storage_deposit_limit(3) + .data(1u32.to_le_bytes().to_vec()) + .build()); + + // Use 4 more bytes of the storage for the same item, which requires 4 Balance. + // Should fail as DefaultDepositLimit is 3 and hence isn't enough. + assert_err_ignore_postinfo!( + builder::call(addr.clone()) + .storage_deposit_limit(3) + .data(5u32.to_le_bytes().to_vec()) + .build(), + >::StorageDepositLimitExhausted, + ); + }); + } + + #[test] + fn deposit_limit_in_nested_calls() { + let (wasm_caller, _code_hash_caller) = + compile_module::("create_storage_and_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create both contracts: Constructors do nothing. + let addr_caller = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); + let addr_callee = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + + // Create 100 bytes of storage with a price of per byte + // This is 100 Balance + 2 Balance for the item + assert_ok!(builder::call(addr_callee.clone()) + .storage_deposit_limit(102) + .data(100u32.to_le_bytes().to_vec()) + .build()); + + // We do not remove any storage but add a storage item of 12 bytes in the caller + // contract. This would cost 12 + 2 = 14 Balance. + // The nested call doesn't get a special limit, which is set by passing 0 to it. + // This should fail as the specified parent's limit is less than the cost: 13 < + // 14. + assert_err_ignore_postinfo!( + builder::call(addr_caller.clone()) + .storage_deposit_limit(13) + .data((100u32, &addr_callee, 0u64).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + + // Now we specify the parent's limit high enough to cover the caller's storage + // additions. However, we use a single byte more in the callee, hence the storage + // deposit should be 15 Balance. + // The nested call doesn't get a special limit, which is set by passing 0 to it. + // This should fail as the specified parent's limit is less than the cost: 14 + // < 15. + assert_err_ignore_postinfo!( + builder::call(addr_caller.clone()) + .storage_deposit_limit(14) + .data((101u32, &addr_callee, 0u64).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + + // Now we specify the parent's limit high enough to cover both the caller's and callee's + // storage additions. However, we set a special deposit limit of 1 Balance for the + // nested call. This should fail as callee adds up 2 bytes to the storage, meaning + // that the nested call should have a deposit limit of at least 2 Balance. The + // sub-call should be rolled back, which is covered by the next test case. + assert_err_ignore_postinfo!( + builder::call(addr_caller.clone()) + .storage_deposit_limit(16) + .data((102u32, &addr_callee, 1u64).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + + // Refund in the callee contract but not enough to cover the 14 Balance required by the + // caller. Note that if previous sub-call wouldn't roll back, this call would pass + // making the test case fail. We don't set a special limit for the nested call here. + assert_err_ignore_postinfo!( + builder::call(addr_caller.clone()) + .storage_deposit_limit(0) + .data((87u32, &addr_callee, 0u64).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + + let _ = ::Currency::set_balance(&ALICE, 511); + + // Require more than the sender's balance. + // We don't set a special limit for the nested call. + assert_err_ignore_postinfo!( + builder::call(addr_caller.clone()) + .data((512u32, &addr_callee, 1u64).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + + // Same as above but allow for the additional deposit of 1 Balance in parent. + // We set the special deposit limit of 1 Balance for the nested call, which isn't + // enforced as callee frees up storage. This should pass. + assert_ok!(builder::call(addr_caller.clone()) + .storage_deposit_limit(1) + .data((87u32, &addr_callee, 1u64).encode()) + .build()); + }); + } + + #[test] + fn deposit_limit_in_nested_instantiate() { + let (wasm_caller, _code_hash_caller) = + compile_module::("create_storage_and_instantiate").unwrap(); + let (wasm_callee, code_hash_callee) = compile_module::("store_deploy").unwrap(); + const ED: u64 = 5; + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&BOB, 1_000_000); + // Create caller contract + let addr_caller = builder::bare_instantiate(Code::Upload(wasm_caller)) + .value(10_000u64) // this balance is later passed to the deployed contract + .build_and_unwrap_account_id(); + // Deploy a contract to get its occupied storage size + let addr = builder::bare_instantiate(Code::Upload(wasm_callee)) + .data(vec![0, 0, 0, 0]) + .build_and_unwrap_account_id(); + + let callee_info_len = ContractInfoOf::::get(&addr).unwrap().encoded_size() as u64; + + // We don't set a special deposit limit for the nested instantiation. + // + // The deposit limit set for the parent is insufficient for the instantiation, which + // requires: + // - callee_info_len + 2 for storing the new contract info, + // - ED for deployed contract account, + // - 2 for the storage item of 0 bytes being created in the callee constructor + // or (callee_info_len + 2 + ED + 2) Balance in total. + // + // Provided the limit is set to be 1 Balance less, + // this call should fail on the return from the caller contract. + assert_err_ignore_postinfo!( + builder::call(addr_caller.clone()) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(callee_info_len + 2 + ED + 1) + .data((0u32, &code_hash_callee, 0u64).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + // The charges made on instantiation should be rolled back. + assert_eq!(::Currency::free_balance(&BOB), 1_000_000); + + // Now we give enough limit for the instantiation itself, but require for 1 more storage + // byte in the constructor. Hence +1 Balance to the limit is needed. This should fail on + // the return from constructor. + assert_err_ignore_postinfo!( + builder::call(addr_caller.clone()) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(callee_info_len + 2 + ED + 2) + .data((1u32, &code_hash_callee, 0u64).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + // The charges made on the instantiation should be rolled back. + assert_eq!(::Currency::free_balance(&BOB), 1_000_000); + + // Now we set enough limit in parent call, but an insufficient limit for child + // instantiate. This should fail during the charging for the instantiation in + // `RawMeter::charge_instantiate()` + assert_err_ignore_postinfo!( + builder::call(addr_caller.clone()) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(callee_info_len + 2 + ED + 2) + .data((0u32, &code_hash_callee, callee_info_len + 2 + ED + 1).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + // The charges made on the instantiation should be rolled back. + assert_eq!(::Currency::free_balance(&BOB), 1_000_000); + + // Same as above but requires for single added storage + // item of 1 byte to be covered by the limit, which implies 3 more Balance. + // Now we set enough limit for the parent call, but insufficient limit for child + // instantiate. This should fail right after the constructor execution. + assert_err_ignore_postinfo!( + builder::call(addr_caller.clone()) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(callee_info_len + 2 + ED + 3) // enough parent limit + .data((1u32, &code_hash_callee, callee_info_len + 2 + ED + 2).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + // The charges made on the instantiation should be rolled back. + assert_eq!(::Currency::free_balance(&BOB), 1_000_000); + + // Set enough deposit limit for the child instantiate. This should succeed. + let result = builder::bare_call(addr_caller.clone()) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(callee_info_len + 2 + ED + 4) + .data((1u32, &code_hash_callee, callee_info_len + 2 + ED + 3).encode()) + .build(); + + let returned = result.result.unwrap(); + // All balance of the caller except ED has been transferred to the callee. + // No deposit has been taken from it. + assert_eq!(::Currency::free_balance(&addr_caller), ED); + // Get address of the deployed contract. + let addr_callee = AccountId32::from_slice(&returned.data[0..32]).unwrap(); + // 10_000 should be sent to callee from the caller contract, plus ED to be sent from the + // origin. + assert_eq!(::Currency::free_balance(&addr_callee), 10_000 + ED); + // The origin should be charged with: + // - callee instantiation deposit = (callee_info_len + 2) + // - callee account ED + // - for writing an item of 1 byte to storage = 3 Balance + assert_eq!( + ::Currency::free_balance(&BOB), + 1_000_000 - (callee_info_len + 2 + ED + 3) + ); + // Check that deposit due to be charged still includes these 3 Balance + assert_eq!(result.storage_deposit.charge_or_zero(), (callee_info_len + 2 + ED + 3)) + }); + } + + #[test] + fn deposit_limit_honors_liquidity_restrictions() { + let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let bobs_balance = 1_000; + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&BOB, bobs_balance); + let min_balance = Contracts::min_balance(); + + // Instantiate the BOB contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + + let info_deposit = test_utils::contract_info_storage_deposit(&addr); + // Check that the contract has been instantiated and has the minimum balance + assert_eq!(get_contract(&addr).total_deposit(), info_deposit); + assert_eq!( + ::Currency::total_balance(&addr), + info_deposit + min_balance + ); + + // check that the hold is honored + ::Currency::hold( + &HoldReason::CodeUploadDepositReserve.into(), + &BOB, + bobs_balance - min_balance, + ) + .unwrap(); + assert_err_ignore_postinfo!( + builder::call(addr.clone()) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(10_000) + .data(100u32.to_le_bytes().to_vec()) + .build(), + >::StorageDepositLimitExhausted, + ); + assert_eq!(::Currency::free_balance(&BOB), min_balance); + }); + } + + #[test] + fn deposit_limit_honors_existential_deposit() { + let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&BOB, 300); + let min_balance = Contracts::min_balance(); + + // Instantiate the BOB contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + + let info_deposit = test_utils::contract_info_storage_deposit(&addr); + + // Check that the contract has been instantiated and has the minimum balance + assert_eq!(get_contract(&addr).total_deposit(), info_deposit); + assert_eq!( + ::Currency::total_balance(&addr), + min_balance + info_deposit + ); + + // check that the deposit can't bring the account below the existential deposit + assert_err_ignore_postinfo!( + builder::call(addr.clone()) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(10_000) + .data(100u32.to_le_bytes().to_vec()) + .build(), + >::StorageDepositLimitExhausted, + ); + assert_eq!(::Currency::free_balance(&BOB), 300); + }); + } + + #[test] + fn deposit_limit_honors_min_leftover() { + let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&BOB, 1_000); + let min_balance = Contracts::min_balance(); + + // Instantiate the BOB contract. + let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + + let info_deposit = test_utils::contract_info_storage_deposit(&addr); + + // Check that the contract has been instantiated and has the minimum balance and the + // storage deposit + assert_eq!(get_contract(&addr).total_deposit(), info_deposit); + assert_eq!( + ::Currency::total_balance(&addr), + info_deposit + min_balance + ); + + // check that the minimum leftover (value send) is considered + // given the minimum deposit of 200 sending 750 will only leave + // 50 for the storage deposit. Which is not enough to store the 50 bytes + // as we also need 2 bytes for the item + assert_err_ignore_postinfo!( + builder::call(addr.clone()) + .origin(RuntimeOrigin::signed(BOB)) + .value(750) + .storage_deposit_limit(10_000) + .data(50u32.to_le_bytes().to_vec()) + .build(), + >::StorageDepositLimitExhausted, + ); + assert_eq!(::Currency::free_balance(&BOB), 1_000); + }); + } + + #[test] + fn locking_delegate_dependency_works() { + // set hash lock up deposit to 30%, to test deposit calculation. + CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30)); + + let (wasm_caller, self_code_hash) = + compile_module::("locking_delegate_dependency").unwrap(); + let callee_codes: Vec<_> = + (0..limits::DELEGATE_DEPENDENCIES + 1).map(|idx| dummy_unique(idx)).collect(); + let callee_hashes: Vec<_> = callee_codes + .iter() + .map(|c| ::Hashing::hash(c)) + .collect(); + + // Define inputs with various actions to test locking / unlocking delegate_dependencies. + // See the contract for more details. + let noop_input = (0u32, callee_hashes[0]); + let lock_delegate_dependency_input = (1u32, callee_hashes[0]); + let unlock_delegate_dependency_input = (2u32, callee_hashes[0]); + let terminate_input = (3u32, callee_hashes[0]); + + // Instantiate the caller contract with the given input. + let instantiate = |input: &(u32, H256)| { + builder::bare_instantiate(Code::Upload(wasm_caller.clone())) + .data(input.encode()) + .build() + }; + + // Call contract with the given input. + let call = |addr_caller: &AccountId32, input: &(u32, H256)| { + builder::bare_call(addr_caller.clone()).data(input.encode()).build() + }; + const ED: u64 = 2000; + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + + // Instantiate with lock_delegate_dependency should fail since the code is not yet on + // chain. + assert_err!( + instantiate(&lock_delegate_dependency_input).result, + Error::::CodeNotFound + ); + + // Upload all the delegated codes (they all have the same size) + let mut deposit = Default::default(); + for code in callee_codes.iter() { + let CodeUploadReturnValue { deposit: deposit_per_code, .. } = + Contracts::bare_upload_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + deposit_limit::(), + ) + .unwrap(); + deposit = deposit_per_code; + } + + // Instantiate should now work. + let addr_caller = + instantiate(&lock_delegate_dependency_input).result.unwrap().account_id; + + // There should be a dependency and a deposit. + let contract = test_utils::get_contract(&addr_caller); + + let dependency_deposit = &CodeHashLockupDepositPercent::get().mul_ceil(deposit); + assert_eq!( + contract.delegate_dependencies().get(&callee_hashes[0]), + Some(dependency_deposit) + ); + assert_eq!( + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &addr_caller + ), + dependency_deposit + contract.storage_base_deposit() + ); + + // Removing the code should fail, since we have added a dependency. + assert_err!( + Contracts::remove_code(RuntimeOrigin::signed(ALICE), callee_hashes[0]), + >::CodeInUse + ); + + // Locking an already existing dependency should fail. + assert_err!( + call(&addr_caller, &lock_delegate_dependency_input).result, + Error::::DelegateDependencyAlreadyExists + ); + + // Locking self should fail. + assert_err!( + call(&addr_caller, &(1u32, self_code_hash)).result, + Error::::CannotAddSelfAsDelegateDependency + ); + + // Locking more than the maximum allowed delegate_dependencies should fail. + for hash in &callee_hashes[1..callee_hashes.len() - 1] { + call(&addr_caller, &(1u32, *hash)).result.unwrap(); + } + assert_err!( + call(&addr_caller, &(1u32, *callee_hashes.last().unwrap())).result, + Error::::MaxDelegateDependenciesReached + ); + + // Unlocking all dependency should work. + for hash in &callee_hashes[..callee_hashes.len() - 1] { + call(&addr_caller, &(2u32, *hash)).result.unwrap(); + } + + // Dependency should be removed, and deposit should be returned. + let contract = test_utils::get_contract(&addr_caller); + assert!(contract.delegate_dependencies().is_empty()); + assert_eq!( + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &addr_caller + ), + contract.storage_base_deposit() + ); + + // Removing a nonexistent dependency should fail. + assert_err!( + call(&addr_caller, &unlock_delegate_dependency_input).result, + Error::::DelegateDependencyNotFound + ); + + // Locking a dependency with a storage limit too low should fail. + assert_err!( + builder::bare_call(addr_caller.clone()) + .storage_deposit_limit(dependency_deposit - 1) + .data(lock_delegate_dependency_input.encode()) + .build() + .result, + Error::::StorageDepositLimitExhausted + ); + + // Since we unlocked the dependency we should now be able to remove the code. + assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), callee_hashes[0])); + + // Calling should fail since the delegated contract is not on chain anymore. + assert_err!(call(&addr_caller, &noop_input).result, Error::::ContractTrapped); + + // Add the dependency back. + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + callee_codes[0].clone(), + deposit_limit::(), + ) + .unwrap(); + call(&addr_caller, &lock_delegate_dependency_input).result.unwrap(); + + // Call terminate should work, and return the deposit. + let balance_before = test_utils::get_balance(&ALICE); + assert_ok!(call(&addr_caller, &terminate_input).result); + assert_eq!( + test_utils::get_balance(&ALICE), + ED + balance_before + contract.storage_base_deposit() + dependency_deposit + ); + + // Terminate should also remove the dependency, so we can remove the code. + assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), callee_hashes[0])); + }); + } + + #[test] + fn native_dependency_deposit_works() { + let (wasm, code_hash) = compile_module::("set_code_hash").unwrap(); + let (dummy_wasm, dummy_code_hash) = compile_module::("dummy").unwrap(); + + // Set hash lock up deposit to 30%, to test deposit calculation. + CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30)); + + // Test with both existing and uploaded code + for code in [Code::Upload(wasm.clone()), Code::Existing(code_hash)] { + ExtBuilder::default().build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + let lockup_deposit_percent = CodeHashLockupDepositPercent::get(); + + // Upload the dummy contract, + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + dummy_wasm.clone(), + deposit_limit::(), + ) + .unwrap(); + + // Upload `set_code_hash` contracts if using Code::Existing. + let add_upload_deposit = match code { + Code::Existing(_) => { + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + wasm.clone(), + deposit_limit::(), + ) + .unwrap(); + false + }, + Code::Upload(_) => true, + }; + + // Instantiate the set_code_hash contract. + let res = builder::bare_instantiate(code).build(); + + let addr = res.result.unwrap().account_id; + let base_deposit = test_utils::contract_info_storage_deposit(&addr); + let upload_deposit = test_utils::get_code_deposit(&code_hash); + let extra_deposit = add_upload_deposit.then(|| upload_deposit).unwrap_or_default(); + + // Check initial storage_deposit + // The base deposit should be: contract_info_storage_deposit + 30% * deposit + let deposit = + extra_deposit + base_deposit + lockup_deposit_percent.mul_ceil(upload_deposit); + + assert_eq!( + res.storage_deposit.charge_or_zero(), + deposit + Contracts::min_balance() + ); + + // call set_code_hash + builder::bare_call(addr.clone()) + .data(dummy_code_hash.encode()) + .build_and_unwrap_result(); + + // Check updated storage_deposit + let code_deposit = test_utils::get_code_deposit(&dummy_code_hash); + let deposit = base_deposit + lockup_deposit_percent.mul_ceil(code_deposit); + assert_eq!(test_utils::get_contract(&addr).storage_base_deposit(), deposit); + + assert_eq!( + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &addr + ), + deposit + ); + }); + } + } + + #[test] + fn root_cannot_upload_code() { + let (wasm, _) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Contracts::upload_code(RuntimeOrigin::root(), wasm, deposit_limit::()), + DispatchError::BadOrigin, + ); + }); + } + + #[test] + fn root_cannot_remove_code() { + let (_, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Contracts::remove_code(RuntimeOrigin::root(), code_hash), + DispatchError::BadOrigin, + ); + }); + } + + #[test] + fn signed_cannot_set_code() { + let (_, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB, code_hash), + DispatchError::BadOrigin, + ); + }); + } + + #[test] + fn none_cannot_call_code() { + ExtBuilder::default().build().execute_with(|| { + assert_err_ignore_postinfo!( + builder::call(BOB).origin(RuntimeOrigin::none()).build(), + DispatchError::BadOrigin, + ); + }); + } + + #[test] + fn root_can_call() { + let (wasm, _) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + + // Call the contract. + assert_ok!(builder::call(addr.clone()).origin(RuntimeOrigin::root()).build()); + }); + } + + #[test] + fn root_cannot_instantiate_with_code() { + let (wasm, _) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_err_ignore_postinfo!( + builder::instantiate_with_code(wasm).origin(RuntimeOrigin::root()).build(), + DispatchError::BadOrigin + ); + }); + } + + #[test] + fn root_cannot_instantiate() { + let (_, code_hash) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_err_ignore_postinfo!( + builder::instantiate(code_hash).origin(RuntimeOrigin::root()).build(), + DispatchError::BadOrigin + ); + }); + } + + #[test] + fn only_upload_origin_can_upload() { + let (wasm, _) = compile_module::("dummy").unwrap(); + UploadAccount::set(Some(ALICE)); + ExtBuilder::default().build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + let _ = Balances::set_balance(&BOB, 1_000_000); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::root(), + wasm.clone(), + deposit_limit::(), + ), + DispatchError::BadOrigin + ); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::signed(BOB), + wasm.clone(), + deposit_limit::(), + ), + DispatchError::BadOrigin + ); + + // Only alice is allowed to upload contract code. + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + wasm.clone(), + deposit_limit::(), + )); + }); + } + + #[test] + fn only_instantiation_origin_can_instantiate() { + let (code, code_hash) = compile_module::("dummy").unwrap(); + InstantiateAccount::set(Some(ALICE)); + ExtBuilder::default().build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + let _ = Balances::set_balance(&BOB, 1_000_000); + + assert_err_ignore_postinfo!( + builder::instantiate_with_code(code.clone()) + .origin(RuntimeOrigin::root()) + .build(), + DispatchError::BadOrigin + ); + + assert_err_ignore_postinfo!( + builder::instantiate_with_code(code.clone()) + .origin(RuntimeOrigin::signed(BOB)) + .build(), + DispatchError::BadOrigin + ); + + // Only Alice can instantiate + assert_ok!(builder::instantiate_with_code(code).build()); + + // Bob cannot instantiate with either `instantiate_with_code` or `instantiate`. + assert_err_ignore_postinfo!( + builder::instantiate(code_hash).origin(RuntimeOrigin::signed(BOB)).build(), + DispatchError::BadOrigin + ); + }); + } + + #[test] + fn balance_api_returns_free_balance() { + let (wasm, _code_hash) = compile_module::("balance").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the BOB contract without any extra balance. + let addr = builder::bare_instantiate(Code::Upload(wasm.to_vec())) + .build_and_unwrap_account_id(); + + let value = 0; + // Call BOB which makes it call the balance runtime API. + // The contract code asserts that the returned balance is 0. + assert_ok!(builder::call(addr.clone()).value(value).build()); + + let value = 1; + // Calling with value will trap the contract. + assert_err_ignore_postinfo!( + builder::call(addr.clone()).value(value).build(), + >::ContractTrapped + ); + }); + } + + #[test] + fn gas_consumed_is_linear_for_nested_calls() { + let (code, _code_hash) = compile_module::("recurse").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let addr = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_account_id(); + + let [gas_0, gas_1, gas_2, gas_max] = { + [0u32, 1u32, 2u32, limits::CALL_STACK_DEPTH] + .iter() + .map(|i| { + let result = builder::bare_call(addr.clone()).data(i.encode()).build(); + assert_ok!(result.result); + result.gas_consumed + }) + .collect::>() + .try_into() + .unwrap() + }; + + let gas_per_recursion = gas_2.checked_sub(&gas_1).unwrap(); + assert_eq!(gas_max, gas_0 + gas_per_recursion * limits::CALL_STACK_DEPTH as u64); + }); + } + + #[test] + fn read_only_call_cannot_store() { + let (wasm_caller, _code_hash_caller) = compile_module::("read_only_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create both contracts: Constructors do nothing. + let addr_caller = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); + let addr_callee = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + + // Read-only call fails when modifying storage. + assert_err_ignore_postinfo!( + builder::call(addr_caller).data((&addr_callee, 100u32).encode()).build(), + >::ContractTrapped + ); + }); + } + + #[test] + fn read_only_call_cannot_transfer() { + let (wasm_caller, _code_hash_caller) = + compile_module::("call_with_flags_and_value").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create both contracts: Constructors do nothing. + let addr_caller = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); + let addr_callee = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + + // Read-only call fails when a non-zero value is set. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .data( + (addr_callee, pallet_revive_uapi::CallFlags::READ_ONLY.bits(), 100u64) + .encode() + ) + .build(), + >::StateChangeDenied + ); + }); + } + + #[test] + fn read_only_subsequent_call_cannot_store() { + let (wasm_read_only_caller, _code_hash_caller) = + compile_module::("read_only_call").unwrap(); + let (wasm_caller, _code_hash_caller) = + compile_module::("call_with_flags_and_value").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create contracts: Constructors do nothing. + let addr_caller = builder::bare_instantiate(Code::Upload(wasm_read_only_caller)) + .build_and_unwrap_account_id(); + let addr_subsequent_caller = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); + let addr_callee = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + + // Subsequent call input. + let input = (&addr_callee, pallet_revive_uapi::CallFlags::empty().bits(), 0u64, 100u32); + + // Read-only call fails when modifying storage. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .data((&addr_subsequent_caller, input).encode()) + .build(), + >::ContractTrapped + ); + }); + } + + #[test] + fn read_only_call_works() { + let (wasm_caller, _code_hash_caller) = compile_module::("read_only_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create both contracts: Constructors do nothing. + let addr_caller = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); + let addr_callee = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + + assert_ok!(builder::call(addr_caller.clone()).data(addr_callee.encode()).build()); + }); + } +} diff --git a/substrate/frame/revive/src/tests/pallet_dummy.rs b/substrate/frame/revive/src/tests/pallet_dummy.rs new file mode 100644 index 000000000000..2af8475d17ed --- /dev/null +++ b/substrate/frame/revive/src/tests/pallet_dummy.rs @@ -0,0 +1,53 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use pallet::*; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use frame_support::{ + dispatch::{Pays, PostDispatchInfo}, + ensure, + pallet_prelude::DispatchResultWithPostInfo, + weights::Weight, + }; + use frame_system::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::call] + impl Pallet { + /// Dummy function that overcharges the predispatch weight, allowing us to test the correct + /// values of [`ContractResult::gas_consumed`] and [`ContractResult::gas_required`] in + /// tests. + #[pallet::call_index(1)] + #[pallet::weight(*pre_charge)] + pub fn overestimate_pre_charge( + origin: OriginFor, + pre_charge: Weight, + actual_weight: Weight, + ) -> DispatchResultWithPostInfo { + ensure_signed(origin)?; + ensure!(pre_charge.any_gt(actual_weight), "pre_charge must be > actual_weight"); + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) + } + } +} diff --git a/substrate/frame/revive/src/tests/test_debug.rs b/substrate/frame/revive/src/tests/test_debug.rs new file mode 100644 index 000000000000..166a0a8606a6 --- /dev/null +++ b/substrate/frame/revive/src/tests/test_debug.rs @@ -0,0 +1,243 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use crate::{ + debug::{CallInterceptor, CallSpan, ExecResult, ExportedFunction, Tracing}, + primitives::ExecReturnValue, + test_utils::*, + AccountIdOf, +}; +use frame_support::traits::Currency; +use std::cell::RefCell; + +#[derive(Clone, PartialEq, Eq, Debug)] +struct DebugFrame { + contract_account: AccountId32, + call: ExportedFunction, + input: Vec, + result: Option>, +} + +thread_local! { + static DEBUG_EXECUTION_TRACE: RefCell> = RefCell::new(Vec::new()); + static INTERCEPTED_ADDRESS: RefCell> = RefCell::new(None); +} + +pub struct TestDebug; +pub struct TestCallSpan { + contract_account: AccountId32, + call: ExportedFunction, + input: Vec, +} + +impl Tracing for TestDebug { + type CallSpan = TestCallSpan; + + fn new_call_span( + contract_account: &AccountIdOf, + entry_point: ExportedFunction, + input_data: &[u8], + ) -> TestCallSpan { + DEBUG_EXECUTION_TRACE.with(|d| { + d.borrow_mut().push(DebugFrame { + contract_account: contract_account.clone(), + call: entry_point, + input: input_data.to_vec(), + result: None, + }) + }); + TestCallSpan { + contract_account: contract_account.clone(), + call: entry_point, + input: input_data.to_vec(), + } + } +} + +impl CallInterceptor for TestDebug { + fn intercept_call( + contract_address: &::AccountId, + _entry_point: ExportedFunction, + _input_data: &[u8], + ) -> Option { + INTERCEPTED_ADDRESS.with(|i| { + if i.borrow().as_ref() == Some(contract_address) { + Some(Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![] })) + } else { + None + } + }) + } +} + +impl CallSpan for TestCallSpan { + fn after_call(self, output: &ExecReturnValue) { + DEBUG_EXECUTION_TRACE.with(|d| { + d.borrow_mut().push(DebugFrame { + contract_account: self.contract_account, + call: self.call, + input: self.input, + result: Some(output.data.clone()), + }) + }); + } +} + +/// We can only run the tests if we have a riscv toolchain installed +#[cfg(feature = "riscv")] +mod run_tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn debugging_works() { + let (wasm_caller, _) = compile_module::("call").unwrap(); + let (wasm_callee, _) = compile_module::("store_call").unwrap(); + + fn current_stack() -> Vec { + DEBUG_EXECUTION_TRACE.with(|stack| stack.borrow().clone()) + } + + fn deploy(wasm: Vec) -> AccountId32 { + Contracts::bare_instantiate( + RuntimeOrigin::signed(ALICE), + 0, + GAS_LIMIT, + deposit_limit::(), + Code::Upload(wasm), + vec![], + vec![], + DebugInfo::Skip, + CollectEvents::Skip, + ) + .result + .unwrap() + .account_id + } + + fn constructor_frame(contract_account: &AccountId32, after: bool) -> DebugFrame { + DebugFrame { + contract_account: contract_account.clone(), + call: ExportedFunction::Constructor, + input: vec![], + result: if after { Some(vec![]) } else { None }, + } + } + + fn call_frame(contract_account: &AccountId32, args: Vec, after: bool) -> DebugFrame { + DebugFrame { + contract_account: contract_account.clone(), + call: ExportedFunction::Call, + input: args, + result: if after { Some(vec![]) } else { None }, + } + } + + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + assert_eq!(current_stack(), vec![]); + + let addr_caller = deploy(wasm_caller); + let addr_callee = deploy(wasm_callee); + + assert_eq!( + current_stack(), + vec![ + constructor_frame(&addr_caller, false), + constructor_frame(&addr_caller, true), + constructor_frame(&addr_callee, false), + constructor_frame(&addr_callee, true), + ] + ); + + let main_args = (100u32, &addr_callee.clone()).encode(); + let inner_args = (100u32).encode(); + + assert_ok!(Contracts::call( + RuntimeOrigin::signed(ALICE), + addr_caller.clone(), + 0, + GAS_LIMIT, + deposit_limit::(), + main_args.clone() + )); + + let stack_top = current_stack()[4..].to_vec(); + assert_eq!( + stack_top, + vec![ + call_frame(&addr_caller, main_args.clone(), false), + call_frame(&addr_callee, inner_args.clone(), false), + call_frame(&addr_callee, inner_args, true), + call_frame(&addr_caller, main_args, true), + ] + ); + }); + } + + #[test] + fn call_interception_works() { + let (wasm, _) = compile_module::("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + let account_id = Contracts::bare_instantiate( + RuntimeOrigin::signed(ALICE), + 0, + GAS_LIMIT, + deposit_limit::(), + Code::Upload(wasm), + vec![], + // some salt to ensure that the address of this contract is unique among all tests + vec![0x41, 0x41, 0x41, 0x41], + DebugInfo::Skip, + CollectEvents::Skip, + ) + .result + .unwrap() + .account_id; + + // no interception yet + assert_ok!(Contracts::call( + RuntimeOrigin::signed(ALICE), + account_id.clone(), + 0, + GAS_LIMIT, + deposit_limit::(), + vec![], + )); + + // intercept calls to this contract + INTERCEPTED_ADDRESS.with(|i| *i.borrow_mut() = Some(account_id.clone())); + + assert_err_ignore_postinfo!( + Contracts::call( + RuntimeOrigin::signed(ALICE), + account_id, + 0, + GAS_LIMIT, + deposit_limit::(), + vec![], + ), + >::ContractReverted, + ); + }); + } +} diff --git a/substrate/frame/revive/src/transient_storage.rs b/substrate/frame/revive/src/transient_storage.rs new file mode 100644 index 000000000000..298e0296fe69 --- /dev/null +++ b/substrate/frame/revive/src/transient_storage.rs @@ -0,0 +1,691 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module contains routines for accessing and altering a contract transient storage. + +use crate::{ + exec::{AccountIdOf, Key}, + storage::WriteOutcome, + Config, Error, +}; +use codec::Encode; +use core::marker::PhantomData; +use frame_support::DefaultNoBound; +use sp_runtime::{DispatchError, DispatchResult, Saturating}; +use sp_std::{collections::btree_map::BTreeMap, mem, vec::Vec}; + +/// Meter entry tracks transaction allocations. +#[derive(Default, Debug)] +pub struct MeterEntry { + /// Allocations made in the current transaction. + pub amount: u32, + /// Allocations limit in the current transaction. + pub limit: u32, +} + +impl MeterEntry { + /// Create a new entry. + fn new(limit: u32) -> Self { + Self { limit, amount: Default::default() } + } + + /// Check if the allocated amount exceeds the limit. + fn exceeds_limit(&self, amount: u32) -> bool { + self.amount.saturating_add(amount) > self.limit + } + + /// Absorb the allocation amount of the nested entry into the current entry. + fn absorb(&mut self, rhs: Self) { + self.amount.saturating_accrue(rhs.amount) + } +} + +// The storage meter enforces a limit for each transaction, +// which is calculated as free_storage * (1 - 1/16) for each subsequent frame. +#[derive(DefaultNoBound)] +pub struct StorageMeter { + nested_meters: Vec, + root_meter: MeterEntry, + _phantom: PhantomData, +} + +impl StorageMeter { + const STORAGE_FRACTION_DENOMINATOR: u32 = 16; + /// Create a new storage allocation meter. + fn new(memory_limit: u32) -> Self { + Self { root_meter: MeterEntry::new(memory_limit), ..Default::default() } + } + + /// Charge the allocated amount of transaction storage from the meter. + fn charge(&mut self, amount: u32) -> DispatchResult { + let meter = self.current_mut(); + if meter.exceeds_limit(amount) { + return Err(Error::::OutOfTransientStorage.into()); + } + meter.amount.saturating_accrue(amount); + Ok(()) + } + + /// Revert a transaction meter. + fn revert(&mut self) { + self.nested_meters.pop().expect( + "A call to revert a meter must be preceded by a corresponding call to start a meter; + the code within this crate makes sure that this is always the case; qed", + ); + } + + /// Start a transaction meter. + fn start(&mut self) { + let meter = self.current(); + let mut transaction_limit = meter.limit.saturating_sub(meter.amount); + if !self.nested_meters.is_empty() { + // Allow use of (1 - 1/STORAGE_FRACTION_DENOMINATOR) of free storage for subsequent + // calls. + transaction_limit.saturating_reduce( + transaction_limit.saturating_div(Self::STORAGE_FRACTION_DENOMINATOR), + ); + } + + self.nested_meters.push(MeterEntry::new(transaction_limit)); + } + + /// Commit a transaction meter. + fn commit(&mut self) { + let transaction_meter = self.nested_meters.pop().expect( + "A call to commit a meter must be preceded by a corresponding call to start a meter; + the code within this crate makes sure that this is always the case; qed", + ); + self.current_mut().absorb(transaction_meter) + } + + /// The total allocated amount of memory. + #[cfg(test)] + fn total_amount(&self) -> u32 { + self.nested_meters + .iter() + .fold(self.root_meter.amount, |acc, e| acc.saturating_add(e.amount)) + } + + /// A mutable reference to the current meter entry. + pub fn current_mut(&mut self) -> &mut MeterEntry { + self.nested_meters.last_mut().unwrap_or(&mut self.root_meter) + } + + /// A reference to the current meter entry. + pub fn current(&self) -> &MeterEntry { + self.nested_meters.last().unwrap_or(&self.root_meter) + } +} + +/// An entry representing a journal change. +struct JournalEntry { + key: Vec, + prev_value: Option>, +} + +impl JournalEntry { + /// Create a new change. + fn new(key: Vec, prev_value: Option>) -> Self { + Self { key, prev_value } + } + + /// Revert the change. + fn revert(self, storage: &mut Storage) { + storage.write(&self.key, self.prev_value); + } +} + +/// A journal containing transient storage modifications. +struct Journal(Vec); + +impl Journal { + /// Create a new journal. + fn new() -> Self { + Self(Default::default()) + } + + /// Add a change to the journal. + fn push(&mut self, entry: JournalEntry) { + self.0.push(entry); + } + + /// Length of the journal. + fn len(&self) -> usize { + self.0.len() + } + + /// Roll back all journal changes until the chackpoint + fn rollback(&mut self, storage: &mut Storage, checkpoint: usize) { + self.0.drain(checkpoint..).rev().for_each(|entry| entry.revert(storage)); + } +} + +/// Storage for maintaining the current transaction state. +#[derive(Default)] +struct Storage(BTreeMap, Vec>); + +impl Storage { + /// Read the storage entry. + fn read(&self, key: &Vec) -> Option> { + self.0.get(key).cloned() + } + + /// Write the storage entry. + fn write(&mut self, key: &Vec, value: Option>) -> Option> { + if let Some(value) = value { + // Insert storage entry. + self.0.insert(key.clone(), value) + } else { + // Remove storage entry. + self.0.remove(key) + } + } +} + +/// Transient storage behaves almost identically to regular storage but is discarded after each +/// transaction. It consists of a `BTreeMap` for the current state, a journal of all changes, and a +/// list of checkpoints. On entry to the `start_transaction` function, a marker (checkpoint) is +/// added to the list. New values are written to the current state, and the previous value is +/// recorded in the journal (`write`). When the `commit_transaction` function is called, the marker +/// to the journal index (checkpoint) of when that call was entered is discarded. +/// On `rollback_transaction`, all entries are reverted up to the last checkpoint. +pub struct TransientStorage { + // The storage and journal size is limited by the storage meter. + storage: Storage, + journal: Journal, + // The size of the StorageMeter is limited by the stack depth. + meter: StorageMeter, + // The size of the checkpoints is limited by the stack depth. + checkpoints: Vec, +} + +impl TransientStorage { + /// Create new transient storage with the supplied memory limit. + pub fn new(memory_limit: u32) -> Self { + TransientStorage { + storage: Default::default(), + journal: Journal::new(), + checkpoints: Default::default(), + meter: StorageMeter::new(memory_limit), + } + } + + /// Read the storage value. If the entry does not exist, `None` is returned. + pub fn read(&self, account: &AccountIdOf, key: &Key) -> Option> { + self.storage.read(&Self::storage_key(&account.encode(), &key.hash())) + } + + /// Write a value to storage. + /// + /// If the `value` is `None`, then the entry is removed. If `take` is true, + /// a [`WriteOutcome::Taken`] is returned instead of a [`WriteOutcome::Overwritten`]. + /// If the entry did not exist, [`WriteOutcome::New`] is returned. + pub fn write( + &mut self, + account: &AccountIdOf, + key: &Key, + value: Option>, + take: bool, + ) -> Result { + let key = Self::storage_key(&account.encode(), &key.hash()); + let prev_value = self.storage.read(&key); + // Skip if the same value is being set. + if prev_value != value { + // Calculate the allocation size. + if let Some(value) = &value { + // Charge the key, value and journal entry. + // If a new value is written, a new journal entry is created. The previous value is + // moved to the journal along with its key, and the new value is written to + // storage. + let key_len = key.capacity(); + let mut amount = value + .capacity() + .saturating_add(key_len) + .saturating_add(mem::size_of::()); + if prev_value.is_none() { + // Charge a new storage entry. + // If there was no previous value, a new entry is added to storage (BTreeMap) + // containing a Vec for the key and a Vec for the value. The value was already + // included in the amount. + amount.saturating_accrue(key_len.saturating_add(mem::size_of::>())); + } + self.meter.charge(amount as _)?; + } + self.storage.write(&key, value); + // Update the journal. + self.journal.push(JournalEntry::new(key, prev_value.clone())); + } + + Ok(match (take, prev_value) { + (_, None) => WriteOutcome::New, + (false, Some(prev_value)) => WriteOutcome::Overwritten(prev_value.len() as _), + (true, Some(prev_value)) => WriteOutcome::Taken(prev_value), + }) + } + + /// Start a new nested transaction. + /// + /// This allows to either commit or roll back all changes that are made after this call. + /// For every transaction there must be a matching call to either `rollback_transaction` + /// or `commit_transaction`. + pub fn start_transaction(&mut self) { + self.meter.start(); + self.checkpoints.push(self.journal.len()); + } + + /// Rollback the last transaction started by `start_transaction`. + /// + /// Any changes made during that transaction are discarded. + /// + /// # Panics + /// + /// Will panic if there is no open transaction. + pub fn rollback_transaction(&mut self) { + let checkpoint = self + .checkpoints + .pop() + .expect( + "A call to rollback_transaction must be preceded by a corresponding call to start_transaction; + the code within this crate makes sure that this is always the case; qed" + ); + self.meter.revert(); + self.journal.rollback(&mut self.storage, checkpoint); + } + + /// Commit the last transaction started by `start_transaction`. + /// + /// Any changes made during that transaction are committed. + /// + /// # Panics + /// + /// Will panic if there is no open transaction. + pub fn commit_transaction(&mut self) { + self.checkpoints + .pop() + .expect( + "A call to commit_transaction must be preceded by a corresponding call to start_transaction; + the code within this crate makes sure that this is always the case; qed" + ); + self.meter.commit(); + } + + /// The storage allocation meter used for transaction metering. + #[cfg(any(test, feature = "runtime-benchmarks"))] + pub fn meter(&mut self) -> &mut StorageMeter { + return &mut self.meter + } + + fn storage_key(account: &[u8], key: &[u8]) -> Vec { + let mut storage_key = Vec::with_capacity(account.len() + key.len()); + storage_key.extend_from_slice(&account); + storage_key.extend_from_slice(&key); + storage_key + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{test_utils::*, tests::Test, Error}; + use core::u32::MAX; + + // Calculate the allocation size for the given entry. + fn allocation_size(account: &AccountIdOf, key: &Key, value: Option>) -> u32 { + let mut storage: TransientStorage = TransientStorage::::new(MAX); + storage + .write(account, key, value, false) + .expect("Could not write to transient storage."); + storage.meter().current().amount + } + + #[test] + fn read_write_works() { + let mut storage: TransientStorage = TransientStorage::::new(2048); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![2]), true), + Ok(WriteOutcome::New) + ); + assert_eq!( + storage.write(&BOB, &Key::Fix([3; 32]), Some(vec![3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), Some(vec![1])); + assert_eq!(storage.read(&ALICE, &Key::Fix([2; 32])), Some(vec![2])); + assert_eq!(storage.read(&BOB, &Key::Fix([3; 32])), Some(vec![3])); + // Overwrite values. + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![4, 5]), false), + Ok(WriteOutcome::Overwritten(1)) + ); + assert_eq!( + storage.write(&BOB, &Key::Fix([3; 32]), Some(vec![6, 7]), true), + Ok(WriteOutcome::Taken(vec![3])) + ); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), Some(vec![1])); + assert_eq!(storage.read(&ALICE, &Key::Fix([2; 32])), Some(vec![4, 5])); + assert_eq!(storage.read(&BOB, &Key::Fix([3; 32])), Some(vec![6, 7])); + + // Check for an empty value. + assert_eq!( + storage.write(&BOB, &Key::Fix([3; 32]), Some(vec![]), true), + Ok(WriteOutcome::Taken(vec![6, 7])) + ); + assert_eq!(storage.read(&BOB, &Key::Fix([3; 32])), Some(vec![])); + + assert_eq!( + storage.write(&BOB, &Key::Fix([3; 32]), None, true), + Ok(WriteOutcome::Taken(vec![])) + ); + assert_eq!(storage.read(&BOB, &Key::Fix([3; 32])), None); + } + + #[test] + fn read_write_with_var_sized_keys_works() { + let mut storage = TransientStorage::::new(2048); + assert_eq!( + storage.write( + &ALICE, + &Key::try_from_var([1; 64].to_vec()).unwrap(), + Some(vec![1]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + storage.write( + &BOB, + &Key::try_from_var([2; 64].to_vec()).unwrap(), + Some(vec![2, 3]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + storage.read(&ALICE, &Key::try_from_var([1; 64].to_vec()).unwrap()), + Some(vec![1]) + ); + assert_eq!( + storage.read(&BOB, &Key::try_from_var([2; 64].to_vec()).unwrap()), + Some(vec![2, 3]) + ); + // Overwrite values. + assert_eq!( + storage.write( + &ALICE, + &Key::try_from_var([1; 64].to_vec()).unwrap(), + Some(vec![4, 5]), + false + ), + Ok(WriteOutcome::Overwritten(1)) + ); + assert_eq!( + storage.read(&ALICE, &Key::try_from_var([1; 64].to_vec()).unwrap()), + Some(vec![4, 5]) + ); + } + + #[test] + fn rollback_transaction_works() { + let mut storage = TransientStorage::::new(1024); + + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + storage.rollback_transaction(); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), None) + } + + #[test] + fn commit_transaction_works() { + let mut storage = TransientStorage::::new(1024); + + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + storage.commit_transaction(); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), Some(vec![1])) + } + + #[test] + fn overwrite_and_commmit_transaction_works() { + let mut storage = TransientStorage::::new(1024); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1, 2]), false), + Ok(WriteOutcome::Overwritten(1)) + ); + storage.commit_transaction(); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), Some(vec![1, 2])) + } + + #[test] + fn rollback_in_nested_transaction_works() { + let mut storage = TransientStorage::::new(1024); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&BOB, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + storage.rollback_transaction(); + storage.commit_transaction(); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), Some(vec![1])); + assert_eq!(storage.read(&BOB, &Key::Fix([1; 32])), None) + } + + #[test] + fn commit_in_nested_transaction_works() { + let mut storage = TransientStorage::::new(1024); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&BOB, &Key::Fix([1; 32]), Some(vec![2]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&CHARLIE, &Key::Fix([1; 32]), Some(vec![3]), false), + Ok(WriteOutcome::New) + ); + storage.commit_transaction(); + storage.commit_transaction(); + storage.commit_transaction(); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), Some(vec![1])); + assert_eq!(storage.read(&BOB, &Key::Fix([1; 32])), Some(vec![2])); + assert_eq!(storage.read(&CHARLIE, &Key::Fix([1; 32])), Some(vec![3])); + } + + #[test] + fn rollback_all_transactions_works() { + let mut storage = TransientStorage::::new(1024); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&BOB, &Key::Fix([1; 32]), Some(vec![2]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&CHARLIE, &Key::Fix([1; 32]), Some(vec![3]), false), + Ok(WriteOutcome::New) + ); + storage.commit_transaction(); + storage.commit_transaction(); + storage.rollback_transaction(); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), None); + assert_eq!(storage.read(&BOB, &Key::Fix([1; 32])), None); + assert_eq!(storage.read(&CHARLIE, &Key::Fix([1; 32])), None); + } + + #[test] + fn metering_transactions_works() { + let size = allocation_size(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096])); + let mut storage = TransientStorage::::new(size * 2); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + let limit = storage.meter().current().limit; + storage.commit_transaction(); + + storage.start_transaction(); + assert_eq!(storage.meter().current().limit, limit - size); + assert_eq!(storage.meter().current().limit - storage.meter().current().amount, size); + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + assert_eq!(storage.meter().current().amount, size); + storage.commit_transaction(); + assert_eq!(storage.meter().total_amount(), size * 2); + } + + #[test] + fn metering_nested_transactions_works() { + let size = allocation_size(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096])); + let mut storage = TransientStorage::::new(size * 3); + + storage.start_transaction(); + let limit = storage.meter().current().limit; + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!(storage.meter().total_amount(), size); + assert!(storage.meter().current().limit < limit - size); + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + storage.commit_transaction(); + assert_eq!(storage.meter().current().limit, limit); + assert_eq!(storage.meter().total_amount(), storage.meter().current().amount); + storage.commit_transaction(); + } + + #[test] + fn metering_transaction_fails() { + let size = allocation_size(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096])); + let mut storage = TransientStorage::::new(size - 1); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Err(Error::::OutOfTransientStorage.into()) + ); + assert_eq!(storage.meter.current().amount, 0); + storage.commit_transaction(); + assert_eq!(storage.meter.total_amount(), 0); + } + + #[test] + fn metering_nested_transactions_fails() { + let size = allocation_size(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096])); + let mut storage = TransientStorage::::new(size * 2); + + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![1u8; 4096]), false), + Err(Error::::OutOfTransientStorage.into()) + ); + storage.commit_transaction(); + storage.commit_transaction(); + assert_eq!(storage.meter.total_amount(), size); + } + + #[test] + fn metering_nested_transaction_with_rollback_works() { + let size = allocation_size(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096])); + let mut storage = TransientStorage::::new(size * 2); + + storage.start_transaction(); + let limit = storage.meter.current().limit; + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + storage.rollback_transaction(); + + assert_eq!(storage.meter.total_amount(), 0); + assert_eq!(storage.meter.current().limit, limit); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + let amount = storage.meter().current().amount; + assert_eq!(storage.meter().total_amount(), amount); + storage.commit_transaction(); + } + + #[test] + fn metering_with_rollback_works() { + let size = allocation_size(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096])); + let mut storage = TransientStorage::::new(size * 5); + + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + let amount = storage.meter.total_amount(); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&BOB, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + storage.commit_transaction(); + storage.rollback_transaction(); + assert_eq!(storage.meter.total_amount(), amount); + storage.commit_transaction(); + } +} diff --git a/substrate/frame/revive/src/wasm/mod.rs b/substrate/frame/revive/src/wasm/mod.rs new file mode 100644 index 000000000000..784993ca793d --- /dev/null +++ b/substrate/frame/revive/src/wasm/mod.rs @@ -0,0 +1,350 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module provides a means for executing contracts +//! represented in wasm. + +mod runtime; + +#[cfg(doc)] +pub use crate::wasm::runtime::SyscallDoc; + +#[cfg(test)] +pub use runtime::HIGHEST_API_VERSION; + +#[cfg(all(feature = "runtime-benchmarks", feature = "riscv"))] +pub use crate::wasm::runtime::{ReturnData, TrapReason}; + +pub use crate::wasm::runtime::{ApiVersion, Memory, Runtime, RuntimeCosts}; + +use crate::{ + exec::{ExecResult, Executable, ExportedFunction, Ext}, + gas::{GasMeter, Token}, + storage::meter::Diff, + weights::WeightInfo, + AccountIdOf, BadOrigin, BalanceOf, CodeHash, CodeInfoOf, CodeVec, Config, Error, Event, + ExecError, HoldReason, Pallet, PristineCode, Weight, API_VERSION, LOG_TARGET, +}; +use alloc::vec::Vec; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ + dispatch::DispatchResult, + ensure, + traits::{fungible::MutateHold, tokens::Precision::BestEffort}, +}; +use sp_core::Get; +use sp_runtime::{traits::Hash, DispatchError}; + +/// Validated Wasm module ready for execution. +/// This data structure is immutable once created and stored. +#[derive(Encode, Decode, scale_info::TypeInfo)] +#[codec(mel_bound())] +#[scale_info(skip_type_params(T))] +pub struct WasmBlob { + code: CodeVec, + // This isn't needed for contract execution and is not stored alongside it. + #[codec(skip)] + code_info: CodeInfo, + // This is for not calculating the hash every time we need it. + #[codec(skip)] + code_hash: CodeHash, +} + +/// Contract code related data, such as: +/// +/// - owner of the contract, i.e. account uploaded its code, +/// - storage deposit amount, +/// - reference count, +/// +/// It is stored in a separate storage entry to avoid loading the code when not necessary. +#[derive(Clone, Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)] +#[codec(mel_bound())] +#[scale_info(skip_type_params(T))] +pub struct CodeInfo { + /// The account that has uploaded the contract code and hence is allowed to remove it. + owner: AccountIdOf, + /// The amount of balance that was deposited by the owner in order to store it on-chain. + #[codec(compact)] + deposit: BalanceOf, + /// The number of instantiated contracts that use this as their code. + #[codec(compact)] + refcount: u64, + /// Length of the code in bytes. + code_len: u32, + /// The API version that this contract operates under. + /// + /// This determines which host functions are available to the contract. This + /// prevents that new host functions become available to already deployed contracts. + api_version: u16, + /// The behaviour version that this contract operates under. + /// + /// Whenever any observeable change (with the exception of weights) are made we need + /// to make sure that already deployed contracts will not be affected. We do this by + /// exposing the old behaviour depending on the set behaviour version of the contract. + /// + /// As of right now this is a reserved field that is always set to 0. + behaviour_version: u16, +} + +impl ExportedFunction { + /// The wasm export name for the function. + fn identifier(&self) -> &str { + match self { + Self::Constructor => "deploy", + Self::Call => "call", + } + } +} + +/// Cost of code loading from storage. +#[cfg_attr(test, derive(Debug, PartialEq, Eq))] +#[derive(Clone, Copy)] +struct CodeLoadToken(u32); + +impl Token for CodeLoadToken { + fn weight(&self) -> Weight { + T::WeightInfo::call_with_code_per_byte(self.0) + .saturating_sub(T::WeightInfo::call_with_code_per_byte(0)) + } +} + +impl WasmBlob { + /// We only check for size and nothing else when the code is uploaded. + pub fn from_code( + code: Vec, + owner: AccountIdOf, + ) -> Result { + let code: CodeVec = + code.try_into().map_err(|_| (>::CodeTooLarge.into(), ""))?; + let code_len = code.len() as u32; + let bytes_added = code_len.saturating_add(>::max_encoded_len() as u32); + let deposit = Diff { bytes_added, items_added: 2, ..Default::default() } + .update_contract::(None) + .charge_or_zero(); + let code_info = CodeInfo { + owner, + deposit, + refcount: 0, + code_len, + api_version: API_VERSION, + behaviour_version: Default::default(), + }; + let code_hash = T::Hashing::hash(&code); + Ok(WasmBlob { code, code_info, code_hash }) + } + + /// Remove the code from storage and refund the deposit to its owner. + /// + /// Applies all necessary checks before removing the code. + pub fn remove(origin: &T::AccountId, code_hash: CodeHash) -> DispatchResult { + >::try_mutate_exists(&code_hash, |existing| { + if let Some(code_info) = existing { + ensure!(code_info.refcount == 0, >::CodeInUse); + ensure!(&code_info.owner == origin, BadOrigin); + let _ = T::Currency::release( + &HoldReason::CodeUploadDepositReserve.into(), + &code_info.owner, + code_info.deposit, + BestEffort, + ); + let deposit_released = code_info.deposit; + let remover = code_info.owner.clone(); + + *existing = None; + >::remove(&code_hash); + >::deposit_event(Event::CodeRemoved { + code_hash, + deposit_released, + remover, + }); + Ok(()) + } else { + Err(>::CodeNotFound.into()) + } + }) + } + + /// Puts the module blob into storage, and returns the deposit collected for the storage. + pub fn store_code(&mut self) -> Result, Error> { + let code_hash = *self.code_hash(); + >::mutate(code_hash, |stored_code_info| { + match stored_code_info { + // Contract code is already stored in storage. Nothing to be done here. + Some(_) => Ok(Default::default()), + // Upload a new contract code. + // We need to store the code and its code_info, and collect the deposit. + // This `None` case happens only with freshly uploaded modules. This means that + // the `owner` is always the origin of the current transaction. + None => { + let deposit = self.code_info.deposit; + T::Currency::hold( + &HoldReason::CodeUploadDepositReserve.into(), + &self.code_info.owner, + deposit, + ) + .map_err(|_| >::StorageDepositNotEnoughFunds)?; + + self.code_info.refcount = 0; + >::insert(code_hash, &self.code); + *stored_code_info = Some(self.code_info.clone()); + >::deposit_event(Event::CodeStored { + code_hash, + deposit_held: deposit, + uploader: self.code_info.owner.clone(), + }); + Ok(deposit) + }, + } + }) + } +} + +impl CodeInfo { + #[cfg(test)] + pub fn new(owner: T::AccountId) -> Self { + CodeInfo { + owner, + deposit: Default::default(), + refcount: 0, + code_len: 0, + api_version: API_VERSION, + behaviour_version: Default::default(), + } + } + + /// Returns reference count of the module. + pub fn refcount(&self) -> u64 { + self.refcount + } + + /// Return mutable reference to the refcount of the module. + pub fn refcount_mut(&mut self) -> &mut u64 { + &mut self.refcount + } + + /// Returns the deposit of the module. + pub fn deposit(&self) -> BalanceOf { + self.deposit + } +} + +pub struct PreparedCall<'a, E: Ext> { + module: polkavm::Module, + instance: polkavm::RawInstance, + runtime: Runtime<'a, E, polkavm::RawInstance>, + api_version: ApiVersion, +} + +impl<'a, E: Ext> PreparedCall<'a, E> { + pub fn call(mut self) -> ExecResult { + let exec_result = loop { + let interrupt = self.instance.run(); + if let Some(exec_result) = self.runtime.handle_interrupt( + interrupt, + &self.module, + &mut self.instance, + self.api_version, + ) { + break exec_result + } + }; + let _ = self.runtime.ext().gas_meter_mut().sync_from_executor(self.instance.gas())?; + exec_result + } +} + +impl WasmBlob { + pub fn prepare_call>( + self, + mut runtime: Runtime, + entry_point: ExportedFunction, + api_version: ApiVersion, + ) -> Result, ExecError> { + let code = self.code.as_slice(); + + let mut config = polkavm::Config::default(); + config.set_backend(Some(polkavm::BackendKind::Interpreter)); + let engine = + polkavm::Engine::new(&config).expect("interpreter is available on all plattforms; qed"); + + let mut module_config = polkavm::ModuleConfig::new(); + module_config.set_gas_metering(Some(polkavm::GasMeteringKind::Sync)); + let module = polkavm::Module::new(&engine, &module_config, code.into()).map_err(|err| { + log::debug!(target: LOG_TARGET, "failed to create polkavm module: {err:?}"); + Error::::CodeRejected + })?; + + let entry_program_counter = module + .exports() + .find(|export| export.symbol().as_bytes() == entry_point.identifier().as_bytes()) + .ok_or_else(|| >::CodeRejected)? + .program_counter(); + + let gas_limit_polkavm: polkavm::Gas = runtime.ext().gas_meter_mut().engine_fuel_left()?; + + let mut instance = module.instantiate().map_err(|err| { + log::debug!(target: LOG_TARGET, "failed to instantiate polkavm module: {err:?}"); + Error::::CodeRejected + })?; + + // Increment before execution so that the constructor sees the correct refcount + if let ExportedFunction::Constructor = entry_point { + E::increment_refcount(self.code_hash)?; + } + + instance.set_gas(gas_limit_polkavm); + instance.prepare_call_untyped(entry_program_counter, &[]); + + Ok(PreparedCall { module, instance, runtime, api_version }) + } +} + +impl Executable for WasmBlob { + fn from_storage( + code_hash: CodeHash, + gas_meter: &mut GasMeter, + ) -> Result { + let code_info = >::get(code_hash).ok_or(Error::::CodeNotFound)?; + gas_meter.charge(CodeLoadToken(code_info.code_len))?; + let code = >::get(code_hash).ok_or(Error::::CodeNotFound)?; + Ok(Self { code, code_info, code_hash }) + } + + fn execute>( + self, + ext: &mut E, + function: ExportedFunction, + input_data: Vec, + ) -> ExecResult { + let api_version = if ::UnsafeUnstableInterface::get() { + ApiVersion::UnsafeNewest + } else { + ApiVersion::Versioned(self.code_info.api_version) + }; + let prepared_call = + self.prepare_call(Runtime::new(ext, input_data), function, api_version)?; + prepared_call.call() + } + + fn code_info(&self) -> &CodeInfo { + &self.code_info + } + + fn code_hash(&self) -> &CodeHash { + &self.code_hash + } +} diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs new file mode 100644 index 000000000000..de910e79e73e --- /dev/null +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -0,0 +1,1936 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Environment definition of the wasm smart-contract runtime. + +use crate::{ + exec::{ExecError, ExecResult, Ext, Key, TopicOf}, + gas::{ChargedAmount, Token}, + limits, + primitives::ExecReturnValue, + weights::WeightInfo, + BalanceOf, CodeHash, Config, Error, LOG_TARGET, SENTINEL, +}; +use alloc::{boxed::Box, vec, vec::Vec}; +use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; +use core::{fmt, marker::PhantomData}; +use frame_support::{ + dispatch::DispatchInfo, ensure, pallet_prelude::DispatchResultWithPostInfo, parameter_types, + traits::Get, weights::Weight, +}; +use pallet_revive_proc_macro::define_env; +use pallet_revive_uapi::{CallFlags, ReturnErrorCode, ReturnFlags, StorageFlags}; +use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256}; +use sp_runtime::{traits::Zero, DispatchError, RuntimeDebug}; + +type CallOf = ::RuntimeCall; + +/// The maximum nesting depth a contract can use when encoding types. +const MAX_DECODE_NESTING: u32 = 256; + +#[derive(Clone, Copy)] +pub enum ApiVersion { + /// Expose all APIs even unversioned ones. Only used for testing and benchmarking. + UnsafeNewest, + /// Only expose API's up to and including the specified version. + Versioned(u16), +} + +/// Abstraction over the memory access within syscalls. +/// +/// The reason for this abstraction is that we run syscalls on the host machine when +/// benchmarking them. In that case we have direct access to the contract's memory. However, when +/// running within PolkaVM we need to resort to copying as we can't map the contracts memory into +/// the host (as of now). +pub trait Memory { + /// Read designated chunk from the sandbox memory into the supplied buffer. + /// + /// Returns `Err` if one of the following conditions occurs: + /// + /// - requested buffer is not within the bounds of the sandbox memory. + fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError>; + + /// Write the given buffer to the designated location in the sandbox memory. + /// + /// Returns `Err` if one of the following conditions occurs: + /// + /// - designated area is not within the bounds of the sandbox memory. + fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError>; + + /// Read designated chunk from the sandbox memory. + /// + /// Returns `Err` if one of the following conditions occurs: + /// + /// - requested buffer is not within the bounds of the sandbox memory. + fn read(&self, ptr: u32, len: u32) -> Result, DispatchError> { + let mut buf = vec![0u8; len as usize]; + self.read_into_buf(ptr, buf.as_mut_slice())?; + Ok(buf) + } + + /// Read designated chunk from the sandbox memory and attempt to decode into the specified type. + /// + /// Returns `Err` if one of the following conditions occurs: + /// + /// - requested buffer is not within the bounds of the sandbox memory. + /// - the buffer contents cannot be decoded as the required type. + /// + /// # Note + /// + /// There must be an extra benchmark for determining the influence of `len` with + /// regard to the overall weight. + fn read_as_unbounded(&self, ptr: u32, len: u32) -> Result { + let buf = self.read(ptr, len)?; + let decoded = D::decode_all_with_depth_limit(MAX_DECODE_NESTING, &mut buf.as_ref()) + .map_err(|_| DispatchError::from(Error::::DecodingFailed))?; + Ok(decoded) + } + + /// Reads and decodes a type with a size fixed at compile time from contract memory. + /// + /// # Only use on fixed size types + /// + /// Don't use this for types where the encoded size is not fixed but merely bounded. Otherwise + /// this implementation will out of bound access the buffer declared by the guest. Some examples + /// of those bounded but not fixed types: Enums with data, `BoundedVec` or any compact encoded + /// integer. + /// + /// # Note + /// + /// The weight of reading a fixed value is included in the overall weight of any + /// contract callable function. + fn read_as(&self, ptr: u32) -> Result { + let buf = self.read(ptr, D::max_encoded_len() as u32)?; + let decoded = D::decode_with_depth_limit(MAX_DECODE_NESTING, &mut buf.as_ref()) + .map_err(|_| DispatchError::from(Error::::DecodingFailed))?; + Ok(decoded) + } +} + +/// Allows syscalls access to the PolkaVM instance they are executing in. +/// +/// In case a contract is executing within PolkaVM its `memory` argument will also implement +/// this trait. The benchmarking implementation of syscalls will only require `Memory` +/// to be implemented. +pub trait PolkaVmInstance: Memory { + fn gas(&self) -> polkavm::Gas; + fn set_gas(&mut self, gas: polkavm::Gas); + fn read_input_regs(&self) -> (u32, u32, u32, u32, u32, u32); + fn write_output(&mut self, output: u32); +} + +// Memory implementation used in benchmarking where guest memory is mapped into the host. +// +// Please note that we could optimize the `read_as_*` functions by decoding directly from +// memory without a copy. However, we don't do that because as it would change the behaviour +// of those functions: A `read_as` with a `len` larger than the actual type can succeed +// in the streaming implementation while it could fail with a segfault in the copy implementation. +#[cfg(feature = "runtime-benchmarks")] +impl Memory for [u8] { + fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError> { + let ptr = ptr as usize; + let bound_checked = + self.get(ptr..ptr + buf.len()).ok_or_else(|| Error::::OutOfBounds)?; + buf.copy_from_slice(bound_checked); + Ok(()) + } + + fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError> { + let ptr = ptr as usize; + let bound_checked = + self.get_mut(ptr..ptr + buf.len()).ok_or_else(|| Error::::OutOfBounds)?; + bound_checked.copy_from_slice(buf); + Ok(()) + } +} + +impl Memory for polkavm::RawInstance { + fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError> { + self.read_memory_into(ptr, buf) + .map(|_| ()) + .map_err(|_| Error::::OutOfBounds.into()) + } + + fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError> { + self.write_memory(ptr, buf).map_err(|_| Error::::OutOfBounds.into()) + } +} + +impl PolkaVmInstance for polkavm::RawInstance { + fn gas(&self) -> polkavm::Gas { + self.gas() + } + + fn set_gas(&mut self, gas: polkavm::Gas) { + self.set_gas(gas) + } + + fn read_input_regs(&self) -> (u32, u32, u32, u32, u32, u32) { + ( + self.reg(polkavm::Reg::A0), + self.reg(polkavm::Reg::A1), + self.reg(polkavm::Reg::A2), + self.reg(polkavm::Reg::A3), + self.reg(polkavm::Reg::A4), + self.reg(polkavm::Reg::A5), + ) + } + + fn write_output(&mut self, output: u32) { + self.set_reg(polkavm::Reg::A0, output); + } +} + +parameter_types! { + /// Getter types used by [`crate::SyscallDoc:call_runtime`] + const CallRuntimeFailed: ReturnErrorCode = ReturnErrorCode::CallRuntimeFailed; + /// Getter types used by [`crate::SyscallDoc::xcm_execute`] + const XcmExecutionFailed: ReturnErrorCode = ReturnErrorCode::XcmExecutionFailed; +} + +impl From for ReturnErrorCode { + fn from(from: ExecReturnValue) -> Self { + if from.flags.contains(ReturnFlags::REVERT) { + Self::CalleeReverted + } else { + Self::Success + } + } +} + +/// The data passed through when a contract uses `seal_return`. +#[derive(RuntimeDebug)] +pub struct ReturnData { + /// The flags as passed through by the contract. They are still unchecked and + /// will later be parsed into a `ReturnFlags` bitflags struct. + flags: u32, + /// The output buffer passed by the contract as return data. + data: Vec, +} + +/// Enumerates all possible reasons why a trap was generated. +/// +/// This is either used to supply the caller with more information about why an error +/// occurred (the SupervisorError variant). +/// The other case is where the trap does not constitute an error but rather was invoked +/// as a quick way to terminate the application (all other variants). +#[derive(RuntimeDebug)] +pub enum TrapReason { + /// The supervisor trapped the contract because of an error condition occurred during + /// execution in privileged code. + SupervisorError(DispatchError), + /// Signals that trap was generated in response to call `seal_return` host function. + Return(ReturnData), + /// Signals that a trap was generated in response to a successful call to the + /// `seal_terminate` host function. + Termination, +} + +impl> From for TrapReason { + fn from(from: T) -> Self { + Self::SupervisorError(from.into()) + } +} + +impl fmt::Display for TrapReason { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + Ok(()) + } +} + +#[cfg_attr(test, derive(Debug, PartialEq, Eq))] +#[derive(Copy, Clone)] +pub enum RuntimeCosts { + /// Base Weight of calling a host function. + HostFn, + /// Weight charged for copying data from the sandbox. + CopyFromContract(u32), + /// Weight charged for copying data to the sandbox. + CopyToContract(u32), + /// Weight of calling `seal_caller`. + Caller, + /// Weight of calling `seal_is_contract`. + IsContract, + /// Weight of calling `seal_code_hash`. + CodeHash, + /// Weight of calling `seal_own_code_hash`. + OwnCodeHash, + /// Weight of calling `seal_caller_is_origin`. + CallerIsOrigin, + /// Weight of calling `caller_is_root`. + CallerIsRoot, + /// Weight of calling `seal_address`. + Address, + /// Weight of calling `seal_gas_left`. + GasLeft, + /// Weight of calling `seal_balance`. + Balance, + /// Weight of calling `seal_value_transferred`. + ValueTransferred, + /// Weight of calling `seal_minimum_balance`. + MinimumBalance, + /// Weight of calling `seal_block_number`. + BlockNumber, + /// Weight of calling `seal_now`. + Now, + /// Weight of calling `seal_weight_to_fee`. + WeightToFee, + /// Weight of calling `seal_terminate`, passing the number of locked dependencies. + Terminate(u32), + /// Weight of calling `seal_deposit_event` with the given number of topics and event size. + DepositEvent { num_topic: u32, len: u32 }, + /// Weight of calling `seal_debug_message` per byte of passed message. + DebugMessage(u32), + /// Weight of calling `seal_set_storage` for the given storage item sizes. + SetStorage { old_bytes: u32, new_bytes: u32 }, + /// Weight of calling `seal_clear_storage` per cleared byte. + ClearStorage(u32), + /// Weight of calling `seal_contains_storage` per byte of the checked item. + ContainsStorage(u32), + /// Weight of calling `seal_get_storage` with the specified size in storage. + GetStorage(u32), + /// Weight of calling `seal_take_storage` for the given size. + TakeStorage(u32), + /// Weight of calling `seal_set_transient_storage` for the given storage item sizes. + SetTransientStorage { old_bytes: u32, new_bytes: u32 }, + /// Weight of calling `seal_clear_transient_storage` per cleared byte. + ClearTransientStorage(u32), + /// Weight of calling `seal_contains_transient_storage` per byte of the checked item. + ContainsTransientStorage(u32), + /// Weight of calling `seal_get_transient_storage` with the specified size in storage. + GetTransientStorage(u32), + /// Weight of calling `seal_take_transient_storage` for the given size. + TakeTransientStorage(u32), + /// Weight of calling `seal_transfer`. + Transfer, + /// Base weight of calling `seal_call`. + CallBase, + /// Weight of calling `seal_delegate_call` for the given input size. + DelegateCallBase, + /// Weight of the transfer performed during a call. + CallTransferSurcharge, + /// Weight per byte that is cloned by supplying the `CLONE_INPUT` flag. + CallInputCloned(u32), + /// Weight of calling `seal_instantiate` for the given input length and salt. + Instantiate { input_data_len: u32, salt_len: u32 }, + /// Weight of calling `seal_hash_sha_256` for the given input size. + HashSha256(u32), + /// Weight of calling `seal_hash_keccak_256` for the given input size. + HashKeccak256(u32), + /// Weight of calling `seal_hash_blake2_256` for the given input size. + HashBlake256(u32), + /// Weight of calling `seal_hash_blake2_128` for the given input size. + HashBlake128(u32), + /// Weight of calling `seal_ecdsa_recover`. + EcdsaRecovery, + /// Weight of calling `seal_sr25519_verify` for the given input size. + Sr25519Verify(u32), + /// Weight charged by a chain extension through `seal_call_chain_extension`. + ChainExtension(Weight), + /// Weight charged for calling into the runtime. + CallRuntime(Weight), + /// Weight charged for calling xcm_execute. + CallXcmExecute(Weight), + /// Weight of calling `seal_set_code_hash` + SetCodeHash, + /// Weight of calling `ecdsa_to_eth_address` + EcdsaToEthAddress, + /// Weight of calling `lock_delegate_dependency` + LockDelegateDependency, + /// Weight of calling `unlock_delegate_dependency` + UnlockDelegateDependency, +} + +/// For functions that modify storage, benchmarks are performed with one item in the +/// storage. To account for the worst-case scenario, the weight of the overhead of +/// writing to or reading from full storage is included. For transient storage writes, +/// the rollback weight is added to reflect the worst-case scenario for this operation. +macro_rules! cost_storage { + (write_transient, $name:ident $(, $arg:expr )*) => { + T::WeightInfo::$name($( $arg ),*) + .saturating_add(T::WeightInfo::rollback_transient_storage()) + .saturating_add(T::WeightInfo::set_transient_storage_full() + .saturating_sub(T::WeightInfo::set_transient_storage_empty())) + }; + + (read_transient, $name:ident $(, $arg:expr )*) => { + T::WeightInfo::$name($( $arg ),*) + .saturating_add(T::WeightInfo::get_transient_storage_full() + .saturating_sub(T::WeightInfo::get_transient_storage_empty())) + }; + + (write, $name:ident $(, $arg:expr )*) => { + T::WeightInfo::$name($( $arg ),*) + .saturating_add(T::WeightInfo::set_storage_full() + .saturating_sub(T::WeightInfo::set_storage_empty())) + }; + + (read, $name:ident $(, $arg:expr )*) => { + T::WeightInfo::$name($( $arg ),*) + .saturating_add(T::WeightInfo::get_storage_full() + .saturating_sub(T::WeightInfo::get_storage_empty())) + }; +} + +macro_rules! cost_args { + // cost_args!(name, a, b, c) -> T::WeightInfo::name(a, b, c).saturating_sub(T::WeightInfo::name(0, 0, 0)) + ($name:ident, $( $arg: expr ),+) => { + (T::WeightInfo::$name($( $arg ),+).saturating_sub(cost_args!(@call_zero $name, $( $arg ),+))) + }; + // Transform T::WeightInfo::name(a, b, c) into T::WeightInfo::name(0, 0, 0) + (@call_zero $name:ident, $( $arg:expr ),*) => { + T::WeightInfo::$name($( cost_args!(@replace_token $arg) ),*) + }; + // Replace the token with 0. + (@replace_token $_in:tt) => { 0 }; +} + +impl Token for RuntimeCosts { + fn influence_lowest_gas_limit(&self) -> bool { + match self { + &Self::CallXcmExecute(_) => false, + _ => true, + } + } + + fn weight(&self) -> Weight { + use self::RuntimeCosts::*; + match *self { + HostFn => cost_args!(noop_host_fn, 1), + CopyToContract(len) => T::WeightInfo::seal_input(len), + CopyFromContract(len) => T::WeightInfo::seal_return(len), + Caller => T::WeightInfo::seal_caller(), + IsContract => T::WeightInfo::seal_is_contract(), + CodeHash => T::WeightInfo::seal_code_hash(), + OwnCodeHash => T::WeightInfo::seal_own_code_hash(), + CallerIsOrigin => T::WeightInfo::seal_caller_is_origin(), + CallerIsRoot => T::WeightInfo::seal_caller_is_root(), + Address => T::WeightInfo::seal_address(), + GasLeft => T::WeightInfo::seal_gas_left(), + Balance => T::WeightInfo::seal_balance(), + ValueTransferred => T::WeightInfo::seal_value_transferred(), + MinimumBalance => T::WeightInfo::seal_minimum_balance(), + BlockNumber => T::WeightInfo::seal_block_number(), + Now => T::WeightInfo::seal_now(), + WeightToFee => T::WeightInfo::seal_weight_to_fee(), + Terminate(locked_dependencies) => T::WeightInfo::seal_terminate(locked_dependencies), + DepositEvent { num_topic, len } => T::WeightInfo::seal_deposit_event(num_topic, len), + DebugMessage(len) => T::WeightInfo::seal_debug_message(len), + SetStorage { new_bytes, old_bytes } => + cost_storage!(write, seal_set_storage, new_bytes, old_bytes), + ClearStorage(len) => cost_storage!(write, seal_clear_storage, len), + ContainsStorage(len) => cost_storage!(read, seal_contains_storage, len), + GetStorage(len) => cost_storage!(read, seal_get_storage, len), + TakeStorage(len) => cost_storage!(write, seal_take_storage, len), + SetTransientStorage { new_bytes, old_bytes } => + cost_storage!(write_transient, seal_set_transient_storage, new_bytes, old_bytes), + ClearTransientStorage(len) => + cost_storage!(write_transient, seal_clear_transient_storage, len), + ContainsTransientStorage(len) => + cost_storage!(read_transient, seal_contains_transient_storage, len), + GetTransientStorage(len) => + cost_storage!(read_transient, seal_get_transient_storage, len), + TakeTransientStorage(len) => + cost_storage!(write_transient, seal_take_transient_storage, len), + Transfer => T::WeightInfo::seal_transfer(), + CallBase => T::WeightInfo::seal_call(0, 0), + DelegateCallBase => T::WeightInfo::seal_delegate_call(), + CallTransferSurcharge => cost_args!(seal_call, 1, 0), + CallInputCloned(len) => cost_args!(seal_call, 0, len), + Instantiate { input_data_len, salt_len } => + T::WeightInfo::seal_instantiate(input_data_len, salt_len), + HashSha256(len) => T::WeightInfo::seal_hash_sha2_256(len), + HashKeccak256(len) => T::WeightInfo::seal_hash_keccak_256(len), + HashBlake256(len) => T::WeightInfo::seal_hash_blake2_256(len), + HashBlake128(len) => T::WeightInfo::seal_hash_blake2_128(len), + EcdsaRecovery => T::WeightInfo::seal_ecdsa_recover(), + Sr25519Verify(len) => T::WeightInfo::seal_sr25519_verify(len), + ChainExtension(weight) | CallRuntime(weight) | CallXcmExecute(weight) => weight, + SetCodeHash => T::WeightInfo::seal_set_code_hash(), + EcdsaToEthAddress => T::WeightInfo::seal_ecdsa_to_eth_address(), + LockDelegateDependency => T::WeightInfo::lock_delegate_dependency(), + UnlockDelegateDependency => T::WeightInfo::unlock_delegate_dependency(), + } + } +} + +/// Same as [`Runtime::charge_gas`]. +/// +/// We need this access as a macro because sometimes hiding the lifetimes behind +/// a function won't work out. +macro_rules! charge_gas { + ($runtime:expr, $costs:expr) => {{ + $runtime.ext.gas_meter_mut().charge($costs) + }}; +} + +/// The kind of call that should be performed. +enum CallType { + /// Execute another instantiated contract + Call { callee_ptr: u32, value_ptr: u32, deposit_ptr: u32, weight: Weight }, + /// Execute deployed code in the context (storage, account ID, value) of the caller contract + DelegateCall { code_hash_ptr: u32 }, +} + +impl CallType { + fn cost(&self) -> RuntimeCosts { + match self { + CallType::Call { .. } => RuntimeCosts::CallBase, + CallType::DelegateCall { .. } => RuntimeCosts::DelegateCallBase, + } + } +} + +/// This is only appropriate when writing out data of constant size that does not depend on user +/// input. In this case the costs for this copy was already charged as part of the token at +/// the beginning of the API entry point. +fn already_charged(_: u32) -> Option { + None +} + +/// Can only be used for one call. +pub struct Runtime<'a, E: Ext, M: ?Sized> { + ext: &'a mut E, + input_data: Option>, + chain_extension: Option::ChainExtension>>, + _phantom_data: PhantomData, +} + +impl<'a, E: Ext, M: PolkaVmInstance> Runtime<'a, E, M> { + pub fn handle_interrupt( + &mut self, + interrupt: Result, + module: &polkavm::Module, + instance: &mut M, + api_version: ApiVersion, + ) -> Option { + use polkavm::InterruptKind::*; + + match interrupt { + Err(error) => { + // in contrast to the other returns this "should" not happen: log level error + log::error!(target: LOG_TARGET, "polkavm execution error: {error}"); + Some(Err(Error::::ExecutionFailed.into())) + }, + Ok(Finished) => + Some(Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })), + Ok(Trap) => Some(Err(Error::::ContractTrapped.into())), + Ok(Segfault(_)) => Some(Err(Error::::ExecutionFailed.into())), + Ok(NotEnoughGas) => Some(Err(Error::::OutOfGas.into())), + Ok(Step) => None, + Ok(Ecalli(idx)) => { + let Some(syscall_symbol) = module.imports().get(idx) else { + return Some(Err(>::InvalidSyscall.into())) + }; + match self.handle_ecall(instance, syscall_symbol.as_bytes(), api_version) { + Ok(None) => None, + Ok(Some(return_value)) => { + instance.write_output(return_value); + None + }, + Err(TrapReason::Return(ReturnData { flags, data })) => + match ReturnFlags::from_bits(flags) { + None => Some(Err(Error::::InvalidCallFlags.into())), + Some(flags) => Some(Ok(ExecReturnValue { flags, data })), + }, + Err(TrapReason::Termination) => + Some(Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })), + Err(TrapReason::SupervisorError(error)) => Some(Err(error.into())), + } + }, + } + } +} + +impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { + pub fn new(ext: &'a mut E, input_data: Vec) -> Self { + Self { + ext, + input_data: Some(input_data), + chain_extension: Some(Box::new(Default::default())), + _phantom_data: Default::default(), + } + } + + /// Get a mutable reference to the inner `Ext`. + /// + /// This is mainly for the chain extension to have access to the environment the + /// contract is executing in. + pub fn ext(&mut self) -> &mut E { + self.ext + } + + /// Charge the gas meter with the specified token. + /// + /// Returns `Err(HostError)` if there is not enough gas. + pub fn charge_gas(&mut self, costs: RuntimeCosts) -> Result { + charge_gas!(self, costs) + } + + /// Adjust a previously charged amount down to its actual amount. + /// + /// This is when a maximum a priori amount was charged and then should be partially + /// refunded to match the actual amount. + pub fn adjust_gas(&mut self, charged: ChargedAmount, actual_costs: RuntimeCosts) { + self.ext.gas_meter_mut().adjust_gas(charged, actual_costs); + } + + /// Charge, Run and adjust gas, for executing the given dispatchable. + fn call_dispatchable>( + &mut self, + dispatch_info: DispatchInfo, + runtime_cost: impl Fn(Weight) -> RuntimeCosts, + run: impl FnOnce(&mut Self) -> DispatchResultWithPostInfo, + ) -> Result { + use frame_support::dispatch::extract_actual_weight; + let charged = self.charge_gas(runtime_cost(dispatch_info.weight))?; + let result = run(self); + let actual_weight = extract_actual_weight(&result, &dispatch_info); + self.adjust_gas(charged, runtime_cost(actual_weight)); + match result { + Ok(_) => Ok(ReturnErrorCode::Success), + Err(e) => { + if self.ext.debug_buffer_enabled() { + self.ext.append_debug_buffer("call failed with: "); + self.ext.append_debug_buffer(e.into()); + }; + Ok(ErrorReturnCode::get()) + }, + } + } + + /// Write the given buffer and its length to the designated locations in sandbox memory and + /// charge gas according to the token returned by `create_token`. + // + /// `out_ptr` is the location in sandbox memory where `buf` should be written to. + /// `out_len_ptr` is an in-out location in sandbox memory. It is read to determine the + /// length of the buffer located at `out_ptr`. If that buffer is large enough the actual + /// `buf.len()` is written to this location. + /// + /// If `out_ptr` is set to the sentinel value of `SENTINEL` and `allow_skip` is true the + /// operation is skipped and `Ok` is returned. This is supposed to help callers to make copying + /// output optional. For example to skip copying back the output buffer of an `seal_call` + /// when the caller is not interested in the result. + /// + /// `create_token` can optionally instruct this function to charge the gas meter with the token + /// it returns. `create_token` receives the variable amount of bytes that are about to be copied + /// by this function. + /// + /// In addition to the error conditions of `Memory::write` this functions returns + /// `Err` if the size of the buffer located at `out_ptr` is too small to fit `buf`. + pub fn write_sandbox_output( + &mut self, + memory: &mut M, + out_ptr: u32, + out_len_ptr: u32, + buf: &[u8], + allow_skip: bool, + create_token: impl FnOnce(u32) -> Option, + ) -> Result<(), DispatchError> { + if allow_skip && out_ptr == SENTINEL { + return Ok(()) + } + + let buf_len = buf.len() as u32; + let len: u32 = memory.read_as(out_len_ptr)?; + + if len < buf_len { + return Err(Error::::OutputBufferTooSmall.into()) + } + + if let Some(costs) = create_token(buf_len) { + self.charge_gas(costs)?; + } + + memory.write(out_ptr, buf)?; + memory.write(out_len_ptr, &buf_len.encode()) + } + + /// Computes the given hash function on the supplied input. + /// + /// Reads from the sandboxed input buffer into an intermediate buffer. + /// Returns the result directly to the output buffer of the sandboxed memory. + /// + /// It is the callers responsibility to provide an output buffer that + /// is large enough to hold the expected amount of bytes returned by the + /// chosen hash function. + /// + /// # Note + /// + /// The `input` and `output` buffers may overlap. + fn compute_hash_on_intermediate_buffer( + &self, + memory: &mut M, + hash_fn: F, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), DispatchError> + where + F: FnOnce(&[u8]) -> R, + R: AsRef<[u8]>, + { + // Copy input into supervisor memory. + let input = memory.read(input_ptr, input_len)?; + // Compute the hash on the input buffer using the given hash function. + let hash = hash_fn(&input); + // Write the resulting hash back into the sandboxed output buffer. + memory.write(output_ptr, hash.as_ref())?; + Ok(()) + } + + /// Fallible conversion of `DispatchError` to `ReturnErrorCode`. + fn err_into_return_code(from: DispatchError) -> Result { + use ReturnErrorCode::*; + + let transfer_failed = Error::::TransferFailed.into(); + let no_code = Error::::CodeNotFound.into(); + let not_found = Error::::ContractNotFound.into(); + + match from { + x if x == transfer_failed => Ok(TransferFailed), + x if x == no_code => Ok(CodeNotFound), + x if x == not_found => Ok(NotCallable), + err => Err(err), + } + } + + /// Fallible conversion of a `ExecResult` to `ReturnErrorCode`. + fn exec_into_return_code(from: ExecResult) -> Result { + use crate::exec::ErrorOrigin::Callee; + + let ExecError { error, origin } = match from { + Ok(retval) => return Ok(retval.into()), + Err(err) => err, + }; + + match (error, origin) { + (_, Callee) => Ok(ReturnErrorCode::CalleeTrapped), + (err, _) => Self::err_into_return_code(err), + } + } + fn decode_key(&self, memory: &M, key_ptr: u32, key_len: u32) -> Result { + let res = match key_len { + SENTINEL => { + let mut buffer = [0u8; 32]; + memory.read_into_buf(key_ptr, buffer.as_mut())?; + Ok(Key::from_fixed(buffer)) + }, + len => { + ensure!(len <= limits::STORAGE_KEY_BYTES, Error::::DecodingFailed); + let key = memory.read(key_ptr, len)?; + Key::try_from_var(key) + }, + }; + + res.map_err(|_| Error::::DecodingFailed.into()) + } + + fn is_transient(flags: u32) -> Result { + StorageFlags::from_bits(flags) + .ok_or_else(|| >::InvalidStorageFlags.into()) + .map(|flags| flags.contains(StorageFlags::TRANSIENT)) + } + + fn set_storage( + &mut self, + memory: &M, + flags: u32, + key_ptr: u32, + key_len: u32, + value_ptr: u32, + value_len: u32, + ) -> Result { + let transient = Self::is_transient(flags)?; + let costs = |new_bytes: u32, old_bytes: u32| { + if transient { + RuntimeCosts::SetTransientStorage { new_bytes, old_bytes } + } else { + RuntimeCosts::SetStorage { new_bytes, old_bytes } + } + }; + let max_size = self.ext.max_value_size(); + let charged = self.charge_gas(costs(value_len, self.ext.max_value_size()))?; + if value_len > max_size { + return Err(Error::::ValueTooLarge.into()) + } + let key = self.decode_key(memory, key_ptr, key_len)?; + let value = Some(memory.read(value_ptr, value_len)?); + let write_outcome = if transient { + self.ext.set_transient_storage(&key, value, false)? + } else { + self.ext.set_storage(&key, value, false)? + }; + self.adjust_gas(charged, costs(value_len, write_outcome.old_len())); + Ok(write_outcome.old_len_with_sentinel()) + } + + fn clear_storage( + &mut self, + memory: &M, + flags: u32, + key_ptr: u32, + key_len: u32, + ) -> Result { + let transient = Self::is_transient(flags)?; + let costs = |len| { + if transient { + RuntimeCosts::ClearTransientStorage(len) + } else { + RuntimeCosts::ClearStorage(len) + } + }; + let charged = self.charge_gas(costs(self.ext.max_value_size()))?; + let key = self.decode_key(memory, key_ptr, key_len)?; + let outcome = if transient { + self.ext.set_transient_storage(&key, None, false)? + } else { + self.ext.set_storage(&key, None, false)? + }; + self.adjust_gas(charged, costs(outcome.old_len())); + Ok(outcome.old_len_with_sentinel()) + } + + fn get_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result { + let transient = Self::is_transient(flags)?; + let costs = |len| { + if transient { + RuntimeCosts::GetTransientStorage(len) + } else { + RuntimeCosts::GetStorage(len) + } + }; + let charged = self.charge_gas(costs(self.ext.max_value_size()))?; + let key = self.decode_key(memory, key_ptr, key_len)?; + let outcome = if transient { + self.ext.get_transient_storage(&key) + } else { + self.ext.get_storage(&key) + }; + if let Some(value) = outcome { + self.adjust_gas(charged, costs(value.len() as u32)); + self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &value, + false, + already_charged, + )?; + Ok(ReturnErrorCode::Success) + } else { + self.adjust_gas(charged, costs(0)); + Ok(ReturnErrorCode::KeyNotFound) + } + } + + fn contains_storage( + &mut self, + memory: &M, + flags: u32, + key_ptr: u32, + key_len: u32, + ) -> Result { + let transient = Self::is_transient(flags)?; + let costs = |len| { + if transient { + RuntimeCosts::ContainsTransientStorage(len) + } else { + RuntimeCosts::ContainsStorage(len) + } + }; + let charged = self.charge_gas(costs(self.ext.max_value_size()))?; + let key = self.decode_key(memory, key_ptr, key_len)?; + let outcome = if transient { + self.ext.get_transient_storage_size(&key) + } else { + self.ext.get_storage_size(&key) + }; + self.adjust_gas(charged, costs(outcome.unwrap_or(0))); + Ok(outcome.unwrap_or(SENTINEL)) + } + + fn take_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result { + let transient = Self::is_transient(flags)?; + let costs = |len| { + if transient { + RuntimeCosts::TakeTransientStorage(len) + } else { + RuntimeCosts::TakeStorage(len) + } + }; + let charged = self.charge_gas(costs(self.ext.max_value_size()))?; + let key = self.decode_key(memory, key_ptr, key_len)?; + let outcome = if transient { + self.ext.set_transient_storage(&key, None, true)? + } else { + self.ext.set_storage(&key, None, true)? + }; + + if let crate::storage::WriteOutcome::Taken(value) = outcome { + self.adjust_gas(charged, costs(value.len() as u32)); + self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &value, + false, + already_charged, + )?; + Ok(ReturnErrorCode::Success) + } else { + self.adjust_gas(charged, costs(0)); + Ok(ReturnErrorCode::KeyNotFound) + } + } + + fn call( + &mut self, + memory: &mut M, + flags: CallFlags, + call_type: CallType, + input_data_ptr: u32, + input_data_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> Result { + self.charge_gas(call_type.cost())?; + + let input_data = if flags.contains(CallFlags::CLONE_INPUT) { + let input = self.input_data.as_ref().ok_or(Error::::InputForwarded)?; + charge_gas!(self, RuntimeCosts::CallInputCloned(input.len() as u32))?; + input.clone() + } else if flags.contains(CallFlags::FORWARD_INPUT) { + self.input_data.take().ok_or(Error::::InputForwarded)? + } else { + self.charge_gas(RuntimeCosts::CopyFromContract(input_data_len))?; + memory.read(input_data_ptr, input_data_len)? + }; + + let call_outcome = match call_type { + CallType::Call { callee_ptr, value_ptr, deposit_ptr, weight } => { + let callee: <::T as frame_system::Config>::AccountId = + memory.read_as(callee_ptr)?; + let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { + BalanceOf::<::T>::zero() + } else { + memory.read_as(deposit_ptr)? + }; + let read_only = flags.contains(CallFlags::READ_ONLY); + let value: BalanceOf<::T> = memory.read_as(value_ptr)?; + if value > 0u32.into() { + // If the call value is non-zero and state change is not allowed, issue an + // error. + if read_only || self.ext.is_read_only() { + return Err(Error::::StateChangeDenied.into()); + } + self.charge_gas(RuntimeCosts::CallTransferSurcharge)?; + } + self.ext.call( + weight, + deposit_limit, + callee, + value, + input_data, + flags.contains(CallFlags::ALLOW_REENTRY), + read_only, + ) + }, + CallType::DelegateCall { code_hash_ptr } => { + if flags.intersects(CallFlags::ALLOW_REENTRY | CallFlags::READ_ONLY) { + return Err(Error::::InvalidCallFlags.into()) + } + let code_hash = memory.read_as(code_hash_ptr)?; + self.ext.delegate_call(code_hash, input_data) + }, + }; + + // `TAIL_CALL` only matters on an `OK` result. Otherwise the call stack comes to + // a halt anyways without anymore code being executed. + if flags.contains(CallFlags::TAIL_CALL) { + if let Ok(return_value) = call_outcome { + return Err(TrapReason::Return(ReturnData { + flags: return_value.flags.bits(), + data: return_value.data, + })) + } + } + + if let Ok(output) = &call_outcome { + self.write_sandbox_output( + memory, + output_ptr, + output_len_ptr, + &output.data, + true, + |len| Some(RuntimeCosts::CopyToContract(len)), + )?; + } + Ok(Self::exec_into_return_code(call_outcome)?) + } + + fn instantiate( + &mut self, + memory: &mut M, + code_hash_ptr: u32, + weight: Weight, + deposit_ptr: u32, + value_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + address_ptr: u32, + address_len_ptr: u32, + output_ptr: u32, + output_len_ptr: u32, + salt_ptr: u32, + salt_len: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::Instantiate { input_data_len, salt_len })?; + let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { + BalanceOf::<::T>::zero() + } else { + memory.read_as(deposit_ptr)? + }; + let value: BalanceOf<::T> = memory.read_as(value_ptr)?; + let code_hash: CodeHash<::T> = memory.read_as(code_hash_ptr)?; + let input_data = memory.read(input_data_ptr, input_data_len)?; + let salt = memory.read(salt_ptr, salt_len)?; + let instantiate_outcome = + self.ext.instantiate(weight, deposit_limit, code_hash, value, input_data, &salt); + if let Ok((address, output)) = &instantiate_outcome { + if !output.flags.contains(ReturnFlags::REVERT) { + self.write_sandbox_output( + memory, + address_ptr, + address_len_ptr, + &address.encode(), + true, + already_charged, + )?; + } + self.write_sandbox_output( + memory, + output_ptr, + output_len_ptr, + &output.data, + true, + |len| Some(RuntimeCosts::CopyToContract(len)), + )?; + } + Ok(Self::exec_into_return_code(instantiate_outcome.map(|(_, retval)| retval))?) + } + + fn terminate(&mut self, memory: &M, beneficiary_ptr: u32) -> Result<(), TrapReason> { + let count = self.ext.locked_delegate_dependencies_count() as _; + self.charge_gas(RuntimeCosts::Terminate(count))?; + + let beneficiary: <::T as frame_system::Config>::AccountId = + memory.read_as(beneficiary_ptr)?; + self.ext.terminate(&beneficiary)?; + Err(TrapReason::Termination) + } +} + +// This is the API exposed to contracts. +// +// # Note +// +// Any input that leads to a out of bound error (reading or writing) or failing to decode +// data passed to the supervisor will lead to a trap. This is not documented explicitly +// for every function. +#[define_env] +pub mod env { + /// Noop function used to benchmark the time it takes to execute an empty function. + #[cfg(feature = "runtime-benchmarks")] + fn noop(&mut self, memory: &mut M) -> Result<(), TrapReason> { + Ok(()) + } + + /// Set the value at the given key in the contract storage. + /// See [`pallet_revive_uapi::HostFn::set_storage_v2`] + #[api_version(0)] + #[mutating] + fn set_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + value_ptr: u32, + value_len: u32, + ) -> Result { + self.set_storage(memory, flags, key_ptr, key_len, value_ptr, value_len) + } + + /// Clear the value at the given key in the contract storage. + /// See [`pallet_revive_uapi::HostFn::clear_storage`] + #[api_version(0)] + #[mutating] + fn clear_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + ) -> Result { + self.clear_storage(memory, flags, key_ptr, key_len) + } + + /// Retrieve the value under the given key from storage. + /// See [`pallet_revive_uapi::HostFn::get_storage`] + #[api_version(0)] + fn get_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result { + self.get_storage(memory, flags, key_ptr, key_len, out_ptr, out_len_ptr) + } + + /// Checks whether there is a value stored under the given key. + /// See [`pallet_revive_uapi::HostFn::contains_storage`] + #[api_version(0)] + fn contains_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + ) -> Result { + self.contains_storage(memory, flags, key_ptr, key_len) + } + + /// Retrieve and remove the value under the given key from storage. + /// See [`pallet_revive_uapi::HostFn::take_storage`] + #[api_version(0)] + #[mutating] + fn take_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result { + self.take_storage(memory, flags, key_ptr, key_len, out_ptr, out_len_ptr) + } + + /// Transfer some value to another account. + /// See [`pallet_revive_uapi::HostFn::transfer`]. + #[api_version(0)] + #[mutating] + fn transfer( + &mut self, + memory: &mut M, + account_ptr: u32, + value_ptr: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::Transfer)?; + let callee: <::T as frame_system::Config>::AccountId = + memory.read_as(account_ptr)?; + let value: BalanceOf<::T> = memory.read_as(value_ptr)?; + let result = self.ext.transfer(&callee, value); + match result { + Ok(()) => Ok(ReturnErrorCode::Success), + Err(err) => { + let code = Self::err_into_return_code(err)?; + Ok(code) + }, + } + } + + /// Make a call to another contract. + /// See [`pallet_revive_uapi::HostFn::call`]. + #[api_version(0)] + fn call( + &mut self, + memory: &mut M, + flags: u32, + callee_ptr: u32, + ref_time_limit: u64, + proof_size_limit: u64, + deposit_ptr: u32, + value_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> Result { + self.call( + memory, + CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, + CallType::Call { + callee_ptr, + value_ptr, + deposit_ptr, + weight: Weight::from_parts(ref_time_limit, proof_size_limit), + }, + input_data_ptr, + input_data_len, + output_ptr, + output_len_ptr, + ) + } + + /// Execute code in the context (storage, caller, value) of the current contract. + /// See [`pallet_revive_uapi::HostFn::delegate_call`]. + #[api_version(0)] + fn delegate_call( + &mut self, + memory: &mut M, + flags: u32, + code_hash_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> Result { + self.call( + memory, + CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, + CallType::DelegateCall { code_hash_ptr }, + input_data_ptr, + input_data_len, + output_ptr, + output_len_ptr, + ) + } + + /// Instantiate a contract with the specified code hash. + /// See [`pallet_revive_uapi::HostFn::instantiate`]. + #[api_version(0)] + #[mutating] + fn instantiate( + &mut self, + memory: &mut M, + code_hash_ptr: u32, + ref_time_limit: u64, + proof_size_limit: u64, + deposit_ptr: u32, + value_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + address_ptr: u32, + address_len_ptr: u32, + output_ptr: u32, + output_len_ptr: u32, + salt_ptr: u32, + salt_len: u32, + ) -> Result { + self.instantiate( + memory, + code_hash_ptr, + Weight::from_parts(ref_time_limit, proof_size_limit), + deposit_ptr, + value_ptr, + input_data_ptr, + input_data_len, + address_ptr, + address_len_ptr, + output_ptr, + output_len_ptr, + salt_ptr, + salt_len, + ) + } + + /// Remove the calling account and transfer remaining **free** balance. + /// See [`pallet_revive_uapi::HostFn::terminate`]. + #[api_version(0)] + #[mutating] + fn terminate(&mut self, memory: &mut M, beneficiary_ptr: u32) -> Result<(), TrapReason> { + self.terminate(memory, beneficiary_ptr) + } + + /// Stores the input passed by the caller into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::input`]. + #[api_version(0)] + fn input(&mut self, memory: &mut M, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + if let Some(input) = self.input_data.take() { + self.write_sandbox_output(memory, out_ptr, out_len_ptr, &input, false, |len| { + Some(RuntimeCosts::CopyToContract(len)) + })?; + self.input_data = Some(input); + Ok(()) + } else { + Err(Error::::InputForwarded.into()) + } + } + + /// Cease contract execution and save a data buffer as a result of the execution. + /// See [`pallet_revive_uapi::HostFn::return_value`]. + #[api_version(0)] + fn seal_return( + &mut self, + memory: &mut M, + flags: u32, + data_ptr: u32, + data_len: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::CopyFromContract(data_len))?; + Err(TrapReason::Return(ReturnData { flags, data: memory.read(data_ptr, data_len)? })) + } + + /// Stores the address of the caller into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::caller`]. + #[api_version(0)] + fn caller(&mut self, memory: &mut M, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::Caller)?; + let caller = self.ext.caller().account_id()?.clone(); + Ok(self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &caller.encode(), + false, + already_charged, + )?) + } + + /// Checks whether a specified address belongs to a contract. + /// See [`pallet_revive_uapi::HostFn::is_contract`]. + #[api_version(0)] + fn is_contract(&mut self, memory: &mut M, account_ptr: u32) -> Result { + self.charge_gas(RuntimeCosts::IsContract)?; + let address: <::T as frame_system::Config>::AccountId = + memory.read_as(account_ptr)?; + + Ok(self.ext.is_contract(&address) as u32) + } + + /// Retrieve the code hash for a specified contract address. + /// See [`pallet_revive_uapi::HostFn::code_hash`]. + #[api_version(0)] + fn code_hash( + &mut self, + memory: &mut M, + account_ptr: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::CodeHash)?; + let address: <::T as frame_system::Config>::AccountId = + memory.read_as(account_ptr)?; + if let Some(value) = self.ext.code_hash(&address) { + self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &value.encode(), + false, + already_charged, + )?; + Ok(ReturnErrorCode::Success) + } else { + Ok(ReturnErrorCode::KeyNotFound) + } + } + + /// Retrieve the code hash of the currently executing contract. + /// See [`pallet_revive_uapi::HostFn::own_code_hash`]. + #[api_version(0)] + fn own_code_hash( + &mut self, + memory: &mut M, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::OwnCodeHash)?; + let code_hash_encoded = &self.ext.own_code_hash().encode(); + Ok(self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + code_hash_encoded, + false, + already_charged, + )?) + } + + /// Checks whether the caller of the current contract is the origin of the whole call stack. + /// See [`pallet_revive_uapi::HostFn::caller_is_origin`]. + #[api_version(0)] + fn caller_is_origin(&mut self, _memory: &mut M) -> Result { + self.charge_gas(RuntimeCosts::CallerIsOrigin)?; + Ok(self.ext.caller_is_origin() as u32) + } + + /// Checks whether the caller of the current contract is root. + /// See [`pallet_revive_uapi::HostFn::caller_is_root`]. + #[api_version(0)] + fn caller_is_root(&mut self, _memory: &mut M) -> Result { + self.charge_gas(RuntimeCosts::CallerIsRoot)?; + Ok(self.ext.caller_is_root() as u32) + } + + /// Stores the address of the current contract into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::address`]. + #[api_version(0)] + fn address( + &mut self, + memory: &mut M, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::Address)?; + Ok(self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &self.ext.address().encode(), + false, + already_charged, + )?) + } + + /// Stores the price for the specified amount of weight into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::weight_to_fee`]. + #[api_version(0)] + fn weight_to_fee( + &mut self, + memory: &mut M, + ref_time_limit: u64, + proof_size_limit: u64, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { + let weight = Weight::from_parts(ref_time_limit, proof_size_limit); + self.charge_gas(RuntimeCosts::WeightToFee)?; + Ok(self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &self.ext.get_weight_price(weight).encode(), + false, + already_charged, + )?) + } + + /// Stores the amount of weight left into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::weight_left`]. + #[api_version(0)] + fn weight_left( + &mut self, + memory: &mut M, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::GasLeft)?; + let gas_left = &self.ext.gas_meter().gas_left().encode(); + Ok(self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + gas_left, + false, + already_charged, + )?) + } + + /// Stores the *free* balance of the current account into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::balance`]. + #[api_version(0)] + fn balance( + &mut self, + memory: &mut M, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::Balance)?; + Ok(self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &self.ext.balance().encode(), + false, + already_charged, + )?) + } + + /// Stores the value transferred along with this call/instantiate into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::value_transferred`]. + #[api_version(0)] + fn value_transferred( + &mut self, + memory: &mut M, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::ValueTransferred)?; + Ok(self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &self.ext.value_transferred().encode(), + false, + already_charged, + )?) + } + + /// Load the latest block timestamp into the supplied buffer + /// See [`pallet_revive_uapi::HostFn::now`]. + #[api_version(0)] + fn now(&mut self, memory: &mut M, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::Now)?; + Ok(self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &self.ext.now().encode(), + false, + already_charged, + )?) + } + + /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::minimum_balance`]. + #[api_version(0)] + fn minimum_balance( + &mut self, + memory: &mut M, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::MinimumBalance)?; + Ok(self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &self.ext.minimum_balance().encode(), + false, + already_charged, + )?) + } + + /// Deposit a contract event with the data buffer and optional list of topics. + /// See [pallet_revive_uapi::HostFn::deposit_event] + #[api_version(0)] + #[mutating] + fn deposit_event( + &mut self, + memory: &mut M, + topics_ptr: u32, + topics_len: u32, + data_ptr: u32, + data_len: u32, + ) -> Result<(), TrapReason> { + let num_topic = topics_len + .checked_div(core::mem::size_of::>() as u32) + .ok_or("Zero sized topics are not allowed")?; + self.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?; + if data_len > self.ext.max_value_size() { + return Err(Error::::ValueTooLarge.into()); + } + + let topics: Vec::T>> = match topics_len { + 0 => Vec::new(), + _ => memory.read_as_unbounded(topics_ptr, topics_len)?, + }; + + // If there are more than `event_topics`, then trap. + if topics.len() as u32 > limits::NUM_EVENT_TOPICS { + return Err(Error::::TooManyTopics.into()); + } + + let event_data = memory.read(data_ptr, data_len)?; + + self.ext.deposit_event(topics, event_data); + + Ok(()) + } + + /// Stores the current block number of the current contract into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::block_number`]. + #[api_version(0)] + fn block_number( + &mut self, + memory: &mut M, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::BlockNumber)?; + Ok(self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &self.ext.block_number().encode(), + false, + already_charged, + )?) + } + + /// Computes the SHA2 256-bit hash on the given input buffer. + /// See [`pallet_revive_uapi::HostFn::hash_sha2_256`]. + #[api_version(0)] + fn hash_sha2_256( + &mut self, + memory: &mut M, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::HashSha256(input_len))?; + Ok(self.compute_hash_on_intermediate_buffer( + memory, sha2_256, input_ptr, input_len, output_ptr, + )?) + } + + /// Computes the KECCAK 256-bit hash on the given input buffer. + /// See [`pallet_revive_uapi::HostFn::hash_keccak_256`]. + #[api_version(0)] + fn hash_keccak_256( + &mut self, + memory: &mut M, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::HashKeccak256(input_len))?; + Ok(self.compute_hash_on_intermediate_buffer( + memory, keccak_256, input_ptr, input_len, output_ptr, + )?) + } + + /// Computes the BLAKE2 256-bit hash on the given input buffer. + /// See [`pallet_revive_uapi::HostFn::hash_blake2_256`]. + #[api_version(0)] + fn hash_blake2_256( + &mut self, + memory: &mut M, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::HashBlake256(input_len))?; + Ok(self.compute_hash_on_intermediate_buffer( + memory, blake2_256, input_ptr, input_len, output_ptr, + )?) + } + + /// Computes the BLAKE2 128-bit hash on the given input buffer. + /// See [`pallet_revive_uapi::HostFn::hash_blake2_128`]. + #[api_version(0)] + fn hash_blake2_128( + &mut self, + memory: &mut M, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::HashBlake128(input_len))?; + Ok(self.compute_hash_on_intermediate_buffer( + memory, blake2_128, input_ptr, input_len, output_ptr, + )?) + } + + /// Call into the chain extension provided by the chain if any. + /// See [`pallet_revive_uapi::HostFn::call_chain_extension`]. + fn call_chain_extension( + &mut self, + memory: &mut M, + id: u32, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> Result { + use crate::chain_extension::{ChainExtension, Environment, RetVal}; + if !::ChainExtension::enabled() { + return Err(Error::::NoChainExtension.into()); + } + let mut chain_extension = self.chain_extension.take().expect( + "Constructor initializes with `Some`. This is the only place where it is set to `None`.\ + It is always reset to `Some` afterwards. qed", + ); + let env = + Environment::new(self, memory, id, input_ptr, input_len, output_ptr, output_len_ptr); + let ret = match chain_extension.call(env)? { + RetVal::Converging(val) => Ok(val), + RetVal::Diverging { flags, data } => + Err(TrapReason::Return(ReturnData { flags: flags.bits(), data })), + }; + self.chain_extension = Some(chain_extension); + ret + } + + /// Emit a custom debug message. + /// See [`pallet_revive_uapi::HostFn::debug_message`]. + #[api_version(0)] + fn debug_message( + &mut self, + memory: &mut M, + str_ptr: u32, + str_len: u32, + ) -> Result { + let str_len = str_len.min(limits::DEBUG_BUFFER_BYTES); + self.charge_gas(RuntimeCosts::DebugMessage(str_len))?; + if self.ext.append_debug_buffer("") { + let data = memory.read(str_ptr, str_len)?; + if let Some(msg) = core::str::from_utf8(&data).ok() { + self.ext.append_debug_buffer(msg); + } + Ok(ReturnErrorCode::Success) + } else { + Ok(ReturnErrorCode::LoggingDisabled) + } + } + + /// Call some dispatchable of the runtime. + /// See [`frame_support::traits::call_runtime`]. + #[mutating] + fn call_runtime( + &mut self, + memory: &mut M, + call_ptr: u32, + call_len: u32, + ) -> Result { + use frame_support::dispatch::GetDispatchInfo; + self.charge_gas(RuntimeCosts::CopyFromContract(call_len))?; + let call: ::RuntimeCall = memory.read_as_unbounded(call_ptr, call_len)?; + self.call_dispatchable::( + call.get_dispatch_info(), + RuntimeCosts::CallRuntime, + |runtime| runtime.ext.call_runtime(call), + ) + } + + /// Execute an XCM program locally, using the contract's address as the origin. + /// See [`pallet_revive_uapi::HostFn::execute_xcm`]. + #[mutating] + fn xcm_execute( + &mut self, + memory: &mut M, + msg_ptr: u32, + msg_len: u32, + ) -> Result { + use frame_support::dispatch::DispatchInfo; + use xcm::VersionedXcm; + use xcm_builder::{ExecuteController, ExecuteControllerWeightInfo}; + + self.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; + let message: VersionedXcm> = memory.read_as_unbounded(msg_ptr, msg_len)?; + + let execute_weight = + <::Xcm as ExecuteController<_, _>>::WeightInfo::execute(); + let weight = self.ext.gas_meter().gas_left().max(execute_weight); + let dispatch_info = DispatchInfo { weight, ..Default::default() }; + + self.call_dispatchable::( + dispatch_info, + RuntimeCosts::CallXcmExecute, + |runtime| { + let origin = crate::RawOrigin::Signed(runtime.ext.address().clone()).into(); + let weight_used = <::Xcm>::execute( + origin, + Box::new(message), + weight.saturating_sub(execute_weight), + )?; + + Ok(Some(weight_used.saturating_add(execute_weight)).into()) + }, + ) + } + + /// Send an XCM program from the contract to the specified destination. + /// See [`pallet_revive_uapi::HostFn::send_xcm`]. + #[mutating] + fn xcm_send( + &mut self, + memory: &mut M, + dest_ptr: u32, + msg_ptr: u32, + msg_len: u32, + output_ptr: u32, + ) -> Result { + use xcm::{VersionedLocation, VersionedXcm}; + use xcm_builder::{SendController, SendControllerWeightInfo}; + + self.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; + let dest: VersionedLocation = memory.read_as(dest_ptr)?; + + let message: VersionedXcm<()> = memory.read_as_unbounded(msg_ptr, msg_len)?; + let weight = <::Xcm as SendController<_>>::WeightInfo::send(); + self.charge_gas(RuntimeCosts::CallRuntime(weight))?; + let origin = crate::RawOrigin::Signed(self.ext.address().clone()).into(); + + match <::Xcm>::send(origin, dest.into(), message.into()) { + Ok(message_id) => { + memory.write(output_ptr, &message_id.encode())?; + Ok(ReturnErrorCode::Success) + }, + Err(e) => { + if self.ext.append_debug_buffer("") { + self.ext.append_debug_buffer("seal0::xcm_send failed with: "); + self.ext.append_debug_buffer(e.into()); + }; + Ok(ReturnErrorCode::XcmSendFailed) + }, + } + } + + /// Recovers the ECDSA public key from the given message hash and signature. + /// See [`pallet_revive_uapi::HostFn::ecdsa_recover`]. + #[api_version(0)] + fn ecdsa_recover( + &mut self, + memory: &mut M, + signature_ptr: u32, + message_hash_ptr: u32, + output_ptr: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::EcdsaRecovery)?; + + let mut signature: [u8; 65] = [0; 65]; + memory.read_into_buf(signature_ptr, &mut signature)?; + let mut message_hash: [u8; 32] = [0; 32]; + memory.read_into_buf(message_hash_ptr, &mut message_hash)?; + + let result = self.ext.ecdsa_recover(&signature, &message_hash); + + match result { + Ok(pub_key) => { + // Write the recovered compressed ecdsa public key back into the sandboxed output + // buffer. + memory.write(output_ptr, pub_key.as_ref())?; + + Ok(ReturnErrorCode::Success) + }, + Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed), + } + } + + /// Verify a sr25519 signature + /// See [`pallet_revive_uapi::HostFn::sr25519_verify`]. + #[api_version(0)] + fn sr25519_verify( + &mut self, + memory: &mut M, + signature_ptr: u32, + pub_key_ptr: u32, + message_len: u32, + message_ptr: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?; + + let mut signature: [u8; 64] = [0; 64]; + memory.read_into_buf(signature_ptr, &mut signature)?; + + let mut pub_key: [u8; 32] = [0; 32]; + memory.read_into_buf(pub_key_ptr, &mut pub_key)?; + + let message: Vec = memory.read(message_ptr, message_len)?; + + if self.ext.sr25519_verify(&signature, &message, &pub_key) { + Ok(ReturnErrorCode::Success) + } else { + Ok(ReturnErrorCode::Sr25519VerifyFailed) + } + } + + /// Replace the contract code at the specified address with new code. + /// See [`pallet_revive_uapi::HostFn::set_code_hash`]. + #[api_version(0)] + #[mutating] + fn set_code_hash( + &mut self, + memory: &mut M, + code_hash_ptr: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::SetCodeHash)?; + let code_hash: CodeHash<::T> = memory.read_as(code_hash_ptr)?; + match self.ext.set_code_hash(code_hash) { + Err(err) => { + let code = Self::err_into_return_code(err)?; + Ok(code) + }, + Ok(()) => Ok(ReturnErrorCode::Success), + } + } + + /// Calculates Ethereum address from the ECDSA compressed public key and stores + /// See [`pallet_revive_uapi::HostFn::ecdsa_to_eth_address`]. + #[api_version(0)] + fn ecdsa_to_eth_address( + &mut self, + memory: &mut M, + key_ptr: u32, + out_ptr: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::EcdsaToEthAddress)?; + let mut compressed_key: [u8; 33] = [0; 33]; + memory.read_into_buf(key_ptr, &mut compressed_key)?; + let result = self.ext.ecdsa_to_eth_address(&compressed_key); + match result { + Ok(eth_address) => { + memory.write(out_ptr, eth_address.as_ref())?; + Ok(ReturnErrorCode::Success) + }, + Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed), + } + } + + /// Adds a new delegate dependency to the contract. + /// See [`pallet_revive_uapi::HostFn::lock_delegate_dependency`]. + #[api_version(0)] + #[mutating] + fn lock_delegate_dependency( + &mut self, + memory: &mut M, + code_hash_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::LockDelegateDependency)?; + let code_hash = memory.read_as(code_hash_ptr)?; + self.ext.lock_delegate_dependency(code_hash)?; + Ok(()) + } + + /// Removes the delegate dependency from the contract. + /// see [`pallet_revive_uapi::HostFn::unlock_delegate_dependency`]. + #[api_version(0)] + #[mutating] + fn unlock_delegate_dependency( + &mut self, + memory: &mut M, + code_hash_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::UnlockDelegateDependency)?; + let code_hash = memory.read_as(code_hash_ptr)?; + self.ext.unlock_delegate_dependency(&code_hash)?; + Ok(()) + } +} diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs new file mode 100644 index 000000000000..6a0d27529d87 --- /dev/null +++ b/substrate/frame/revive/src/weights.rs @@ -0,0 +1,2120 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `pallet_revive` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-07-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-yaoqqom-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// target/production/substrate-node +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_revive +// --chain=dev +// --header=./substrate/HEADER-APACHE2 +// --output=./substrate/frame/contracts/src/weights.rs +// --template=./substrate/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_revive`. +pub trait WeightInfo { + fn on_process_deletion_queue_batch() -> Weight; + fn on_initialize_per_trie_key(k: u32, ) -> Weight; + fn v9_migration_step(c: u32, ) -> Weight; + fn v10_migration_step() -> Weight; + fn v11_migration_step(k: u32, ) -> Weight; + fn v12_migration_step(c: u32, ) -> Weight; + fn v13_migration_step() -> Weight; + fn v14_migration_step() -> Weight; + fn v15_migration_step() -> Weight; + fn v16_migration_step() -> Weight; + fn migration_noop() -> Weight; + fn migrate() -> Weight; + fn on_runtime_upgrade_noop() -> Weight; + fn on_runtime_upgrade_in_progress() -> Weight; + fn on_runtime_upgrade() -> Weight; + fn call_with_code_per_byte(c: u32, ) -> Weight; + fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight; + fn instantiate(i: u32, s: u32, ) -> Weight; + fn call() -> Weight; + fn upload_code_determinism_enforced(c: u32, ) -> Weight; + fn upload_code_determinism_relaxed(c: u32, ) -> Weight; + fn remove_code() -> Weight; + fn set_code() -> Weight; + fn noop_host_fn(r: u32, ) -> Weight; + fn seal_caller() -> Weight; + fn seal_is_contract() -> Weight; + fn seal_code_hash() -> Weight; + fn seal_own_code_hash() -> Weight; + fn seal_caller_is_origin() -> Weight; + fn seal_caller_is_root() -> Weight; + fn seal_address() -> Weight; + fn seal_gas_left() -> Weight; + fn seal_balance() -> Weight; + fn seal_value_transferred() -> Weight; + fn seal_minimum_balance() -> Weight; + fn seal_block_number() -> Weight; + fn seal_now() -> Weight; + fn seal_weight_to_fee() -> Weight; + fn seal_input(n: u32, ) -> Weight; + fn seal_return(n: u32, ) -> Weight; + fn seal_terminate(n: u32, ) -> Weight; + fn seal_random() -> Weight; + fn seal_deposit_event(t: u32, n: u32, ) -> Weight; + fn seal_debug_message(i: u32, ) -> Weight; + fn get_storage_empty() -> Weight; + fn get_storage_full() -> Weight; + fn set_storage_empty() -> Weight; + fn set_storage_full() -> Weight; + fn seal_set_storage(n: u32, o: u32, ) -> Weight; + fn seal_clear_storage(n: u32, ) -> Weight; + fn seal_get_storage(n: u32, ) -> Weight; + fn seal_contains_storage(n: u32, ) -> Weight; + fn seal_take_storage(n: u32, ) -> Weight; + fn set_transient_storage_empty() -> Weight; + fn set_transient_storage_full() -> Weight; + fn get_transient_storage_empty() -> Weight; + fn get_transient_storage_full() -> Weight; + fn rollback_transient_storage() -> Weight; + fn seal_set_transient_storage(n: u32, o: u32, ) -> Weight; + fn seal_clear_transient_storage(n: u32, ) -> Weight; + fn seal_get_transient_storage(n: u32, ) -> Weight; + fn seal_contains_transient_storage(n: u32, ) -> Weight; + fn seal_take_transient_storage(n: u32, ) -> Weight; + fn seal_transfer() -> Weight; + fn seal_call(t: u32, i: u32, ) -> Weight; + fn seal_delegate_call() -> Weight; + fn seal_instantiate(i: u32, s: u32, ) -> Weight; + fn seal_hash_sha2_256(n: u32, ) -> Weight; + fn seal_hash_keccak_256(n: u32, ) -> Weight; + fn seal_hash_blake2_256(n: u32, ) -> Weight; + fn seal_hash_blake2_128(n: u32, ) -> Weight; + fn seal_sr25519_verify(n: u32, ) -> Weight; + fn seal_ecdsa_recover() -> Weight; + fn seal_ecdsa_to_eth_address() -> Weight; + fn seal_set_code_hash() -> Weight; + fn lock_delegate_dependency() -> Weight; + fn unlock_delegate_dependency() -> Weight; + fn seal_reentrance_count() -> Weight; + fn seal_account_reentrance_count() -> Weight; + fn seal_instantiation_nonce() -> Weight; + fn instr_i64_load_store(r: u32, ) -> Weight; +} + +/// Weights for `pallet_revive` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:0) + /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + fn on_process_deletion_queue_batch() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `1627` + // Minimum execution time: 1_915_000 picoseconds. + Weight::from_parts(1_986_000, 1627) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `k` is `[0, 1024]`. + fn on_initialize_per_trie_key(k: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `452 + k * (69 ±0)` + // Estimated: `442 + k * (70 ±0)` + // Minimum execution time: 11_103_000 picoseconds. + Weight::from_parts(11_326_000, 442) + // Standard Error: 2_291 + .saturating_add(Weight::from_parts(1_196_329, 0).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) + .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) + } + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:2 w:1) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:2 w:1) + /// The range of component `c` is `[0, 125952]`. + fn v9_migration_step(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `211 + c * (1 ±0)` + // Estimated: `6149 + c * (1 ±0)` + // Minimum execution time: 7_783_000 picoseconds. + Weight::from_parts(4_462_075, 6149) + // Standard Error: 5 + .saturating_add(Weight::from_parts(1_634, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) + } + /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn v10_migration_step() -> Weight { + // Proof Size summary in bytes: + // Measured: `510` + // Estimated: `6450` + // Minimum execution time: 15_971_000 picoseconds. + Weight::from_parts(16_730_000, 6450) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::DeletionQueue` (r:1 w:1025) + /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) + /// Storage: `Contracts::DeletionQueueCounter` (r:0 w:1) + /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// The range of component `k` is `[0, 1024]`. + fn v11_migration_step(k: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `171 + k * (1 ±0)` + // Estimated: `3635 + k * (1 ±0)` + // Minimum execution time: 3_149_000 picoseconds. + Weight::from_parts(3_264_000, 3635) + // Standard Error: 559 + .saturating_add(Weight::from_parts(1_111_209, 0).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(k.into())) + } + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:0 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn v12_migration_step(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `325 + c * (1 ±0)` + // Estimated: `6263 + c * (1 ±0)` + // Minimum execution time: 15_072_000 picoseconds. + Weight::from_parts(15_721_891, 6263) + // Standard Error: 2 + .saturating_add(Weight::from_parts(428, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) + } + /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + fn v13_migration_step() -> Weight { + // Proof Size summary in bytes: + // Measured: `440` + // Estimated: `6380` + // Minimum execution time: 12_047_000 picoseconds. + Weight::from_parts(12_500_000, 6380) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + fn v14_migration_step() -> Weight { + // Proof Size summary in bytes: + // Measured: `352` + // Estimated: `6292` + // Minimum execution time: 47_488_000 picoseconds. + Weight::from_parts(48_482_000, 6292) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn v15_migration_step() -> Weight { + // Proof Size summary in bytes: + // Measured: `594` + // Estimated: `6534` + // Minimum execution time: 52_801_000 picoseconds. + Weight::from_parts(54_230_000, 6534) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + fn v16_migration_step() -> Weight { + // Proof Size summary in bytes: + // Measured: `409` + // Estimated: `6349` + // Minimum execution time: 11_618_000 picoseconds. + Weight::from_parts(12_068_000, 6349) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + fn migration_noop() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `1627` + // Minimum execution time: 2_131_000 picoseconds. + Weight::from_parts(2_255_000, 1627) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:1) + fn migrate() -> Weight { + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `3631` + // Minimum execution time: 10_773_000 picoseconds. + Weight::from_parts(11_118_000, 3631) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) + fn on_runtime_upgrade_noop() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `3607` + // Minimum execution time: 4_371_000 picoseconds. + Weight::from_parts(4_624_000, 3607) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + fn on_runtime_upgrade_in_progress() -> Weight { + // Proof Size summary in bytes: + // Measured: `167` + // Estimated: `3632` + // Minimum execution time: 5_612_000 picoseconds. + Weight::from_parts(5_838_000, 3632) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) + /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + fn on_runtime_upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `3607` + // Minimum execution time: 5_487_000 picoseconds. + Weight::from_parts(5_693_000, 3607) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn call_with_code_per_byte(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `800 + c * (1 ±0)` + // Estimated: `4266 + c * (1 ±0)` + // Minimum execution time: 247_545_000 picoseconds. + Weight::from_parts(268_016_699, 4266) + // Standard Error: 4 + .saturating_add(Weight::from_parts(700, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:2 w:2) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::Nonce` (r:1 w:1) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + /// The range of component `i` is `[0, 1048576]`. + /// The range of component `s` is `[0, 1048576]`. + fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `323` + // Estimated: `6262` + // Minimum execution time: 4_396_772_000 picoseconds. + Weight::from_parts(235_107_907, 6262) + // Standard Error: 185 + .saturating_add(Weight::from_parts(53_843, 0).saturating_mul(c.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(2_143, 0).saturating_mul(i.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(2_210, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Contracts::Nonce` (r:1 w:1) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// The range of component `i` is `[0, 1048576]`. + /// The range of component `s` is `[0, 1048576]`. + fn instantiate(i: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `560` + // Estimated: `4017` + // Minimum execution time: 2_240_868_000 picoseconds. + Weight::from_parts(2_273_668_000, 4017) + // Standard Error: 32 + .saturating_add(Weight::from_parts(934, 0).saturating_mul(i.into())) + // Standard Error: 32 + .saturating_add(Weight::from_parts(920, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn call() -> Weight { + // Proof Size summary in bytes: + // Measured: `826` + // Estimated: `4291` + // Minimum execution time: 165_067_000 picoseconds. + Weight::from_parts(168_582_000, 4291) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn upload_code_determinism_enforced(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `3607` + // Minimum execution time: 229_454_000 picoseconds. + Weight::from_parts(251_495_551, 3607) + // Standard Error: 71 + .saturating_add(Weight::from_parts(51_428, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn upload_code_determinism_relaxed(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `3607` + // Minimum execution time: 240_390_000 picoseconds. + Weight::from_parts(273_854_266, 3607) + // Standard Error: 243 + .saturating_add(Weight::from_parts(51_836, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + fn remove_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `315` + // Estimated: `3780` + // Minimum execution time: 39_374_000 picoseconds. + Weight::from_parts(40_247_000, 3780) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:2 w:2) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + fn set_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `552` + // Estimated: `6492` + // Minimum execution time: 24_473_000 picoseconds. + Weight::from_parts(25_890_000, 6492) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// The range of component `r` is `[0, 1600]`. + fn noop_host_fn(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_528_000 picoseconds. + Weight::from_parts(9_301_010, 0) + // Standard Error: 98 + .saturating_add(Weight::from_parts(53_173, 0).saturating_mul(r.into())) + } + fn seal_caller() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 643_000 picoseconds. + Weight::from_parts(678_000, 0) + } + /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + fn seal_is_contract() -> Weight { + // Proof Size summary in bytes: + // Measured: `354` + // Estimated: `3819` + // Minimum execution time: 6_107_000 picoseconds. + Weight::from_parts(6_235_000, 3819) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + fn seal_code_hash() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `3912` + // Minimum execution time: 7_316_000 picoseconds. + Weight::from_parts(7_653_000, 3912) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + fn seal_own_code_hash() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 721_000 picoseconds. + Weight::from_parts(764_000, 0) + } + fn seal_caller_is_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 369_000 picoseconds. + Weight::from_parts(417_000, 0) + } + fn seal_caller_is_root() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 318_000 picoseconds. + Weight::from_parts(349_000, 0) + } + fn seal_address() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 590_000 picoseconds. + Weight::from_parts(628_000, 0) + } + fn seal_gas_left() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 660_000 picoseconds. + Weight::from_parts(730_000, 0) + } + fn seal_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `140` + // Estimated: `0` + // Minimum execution time: 4_361_000 picoseconds. + Weight::from_parts(4_577_000, 0) + } + fn seal_value_transferred() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 560_000 picoseconds. + Weight::from_parts(603_000, 0) + } + fn seal_minimum_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(610_000, 0) + } + fn seal_block_number() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 557_000 picoseconds. + Weight::from_parts(583_000, 0) + } + fn seal_now() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 550_000 picoseconds. + Weight::from_parts(602_000, 0) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) + fn seal_weight_to_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `67` + // Estimated: `1552` + // Minimum execution time: 4_065_000 picoseconds. + Weight::from_parts(4_291_000, 1552) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// The range of component `n` is `[0, 1048572]`. + fn seal_input(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 487_000 picoseconds. + Weight::from_parts(517_000, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(301, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048572]`. + fn seal_return(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 318_000 picoseconds. + Weight::from_parts(372_000, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(411, 0).saturating_mul(n.into())) + } + /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:1) + /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:33 w:33) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::DeletionQueue` (r:0 w:1) + /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) + /// The range of component `n` is `[0, 32]`. + fn seal_terminate(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `319 + n * (78 ±0)` + // Estimated: `3784 + n * (2553 ±0)` + // Minimum execution time: 13_251_000 picoseconds. + Weight::from_parts(15_257_892, 3784) + // Standard Error: 7_089 + .saturating_add(Weight::from_parts(3_443_907, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2553).saturating_mul(n.into())) + } + /// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0) + /// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `Measured`) + fn seal_random() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `1561` + // Minimum execution time: 3_434_000 picoseconds. + Weight::from_parts(3_605_000, 1561) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `System::EventTopics` (r:4 w:4) + /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `t` is `[0, 4]`. + /// The range of component `n` is `[0, 16384]`. + fn seal_deposit_event(t: u32, n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `990 + t * (2475 ±0)` + // Minimum execution time: 3_668_000 picoseconds. + Weight::from_parts(3_999_591, 990) + // Standard Error: 5_767 + .saturating_add(Weight::from_parts(2_011_090, 0).saturating_mul(t.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(12, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 2475).saturating_mul(t.into())) + } + /// The range of component `i` is `[0, 1048576]`. + fn seal_debug_message(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 443_000 picoseconds. + Weight::from_parts(472_000, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(1_207, 0).saturating_mul(i.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn get_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `16618` + // Estimated: `16618` + // Minimum execution time: 13_752_000 picoseconds. + Weight::from_parts(14_356_000, 16618) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn get_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `26628` + // Estimated: `26628` + // Minimum execution time: 43_444_000 picoseconds. + Weight::from_parts(45_087_000, 26628) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `16618` + // Estimated: `16618` + // Minimum execution time: 15_616_000 picoseconds. + Weight::from_parts(16_010_000, 16618) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `26628` + // Estimated: `26628` + // Minimum execution time: 47_020_000 picoseconds. + Weight::from_parts(50_152_000, 26628) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + /// The range of component `o` is `[0, 16384]`. + fn seal_set_storage(n: u32, o: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `250 + o * (1 ±0)` + // Estimated: `249 + o * (1 ±0)` + // Minimum execution time: 8_824_000 picoseconds. + Weight::from_parts(8_915_233, 249) + // Standard Error: 1 + .saturating_add(Weight::from_parts(255, 0).saturating_mul(n.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(39, 0).saturating_mul(o.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_clear_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 7_133_000 picoseconds. + Weight::from_parts(7_912_778, 248) + // Standard Error: 1 + .saturating_add(Weight::from_parts(88, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_get_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 6_746_000 picoseconds. + Weight::from_parts(7_647_236, 248) + // Standard Error: 2 + .saturating_add(Weight::from_parts(603, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_contains_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 6_247_000 picoseconds. + Weight::from_parts(6_952_661, 248) + // Standard Error: 1 + .saturating_add(Weight::from_parts(77, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_take_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 7_428_000 picoseconds. + Weight::from_parts(8_384_015, 248) + // Standard Error: 2 + .saturating_add(Weight::from_parts(625, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + fn set_transient_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_478_000 picoseconds. + Weight::from_parts(1_533_000, 0) + } + fn set_transient_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_485_000 picoseconds. + Weight::from_parts(2_728_000, 0) + } + fn get_transient_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_195_000 picoseconds. + Weight::from_parts(3_811_000, 0) + } + fn get_transient_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_902_000 picoseconds. + Weight::from_parts(4_118_000, 0) + } + fn rollback_transient_storage() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_571_000 picoseconds. + Weight::from_parts(1_662_000, 0) + } + /// The range of component `n` is `[0, 16384]`. + /// The range of component `o` is `[0, 16384]`. + fn seal_set_transient_storage(n: u32, o: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_250_000 picoseconds. + Weight::from_parts(2_465_568, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(201, 0).saturating_mul(n.into())) + // Standard Error: 0 + .saturating_add(Weight::from_parts(223, 0).saturating_mul(o.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_clear_transient_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_012_000 picoseconds. + Weight::from_parts(2_288_004, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(239, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_get_transient_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_906_000 picoseconds. + Weight::from_parts(2_121_040, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(225, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_contains_transient_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_736_000 picoseconds. + Weight::from_parts(1_954_728, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(111, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_take_transient_storage(_n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_872_000 picoseconds. + Weight::from_parts(8_125_644, 0) + } + fn seal_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `140` + // Estimated: `0` + // Minimum execution time: 8_489_000 picoseconds. + Weight::from_parts(8_791_000, 0) + } + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `t` is `[0, 1]`. + /// The range of component `i` is `[0, 1048576]`. + fn seal_call(t: u32, i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `620 + t * (280 ±0)` + // Estimated: `4085 + t * (2182 ±0)` + // Minimum execution time: 122_759_000 picoseconds. + Weight::from_parts(120_016_020, 4085) + // Standard Error: 173_118 + .saturating_add(Weight::from_parts(42_848_338, 0).saturating_mul(t.into())) + // Standard Error: 0 + .saturating_add(Weight::from_parts(6, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 2182).saturating_mul(t.into())) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + fn seal_delegate_call() -> Weight { + // Proof Size summary in bytes: + // Measured: `430` + // Estimated: `3895` + // Minimum execution time: 111_566_000 picoseconds. + Weight::from_parts(115_083_000, 3895) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Contracts::Nonce` (r:1 w:0) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `i` is `[0, 983040]`. + /// The range of component `s` is `[0, 983040]`. + fn seal_instantiate(i: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `676` + // Estimated: `4132` + // Minimum execution time: 1_871_402_000 picoseconds. + Weight::from_parts(1_890_038_000, 4132) + // Standard Error: 24 + .saturating_add(Weight::from_parts(581, 0).saturating_mul(i.into())) + // Standard Error: 24 + .saturating_add(Weight::from_parts(915, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_sha2_256(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 966_000 picoseconds. + Weight::from_parts(9_599_151, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_336, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_keccak_256(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_416_000 picoseconds. + Weight::from_parts(10_964_255, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_593, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_blake2_256(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 821_000 picoseconds. + Weight::from_parts(6_579_283, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_466, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_blake2_128(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 773_000 picoseconds. + Weight::from_parts(10_990_209, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_457, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 125697]`. + fn seal_sr25519_verify(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 43_195_000 picoseconds. + Weight::from_parts(41_864_855, 0) + // Standard Error: 9 + .saturating_add(Weight::from_parts(5_154, 0).saturating_mul(n.into())) + } + fn seal_ecdsa_recover() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 47_747_000 picoseconds. + Weight::from_parts(49_219_000, 0) + } + fn seal_ecdsa_to_eth_address() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 12_854_000 picoseconds. + Weight::from_parts(12_962_000, 0) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + fn seal_set_code_hash() -> Weight { + // Proof Size summary in bytes: + // Measured: `430` + // Estimated: `3895` + // Minimum execution time: 17_868_000 picoseconds. + Weight::from_parts(18_486_000, 3895) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + fn lock_delegate_dependency() -> Weight { + // Proof Size summary in bytes: + // Measured: `355` + // Estimated: `3820` + // Minimum execution time: 8_393_000 picoseconds. + Weight::from_parts(8_640_000, 3820) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `MaxEncodedLen`) + fn unlock_delegate_dependency() -> Weight { + // Proof Size summary in bytes: + // Measured: `355` + // Estimated: `3558` + // Minimum execution time: 7_489_000 picoseconds. + Weight::from_parts(7_815_000, 3558) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn seal_reentrance_count() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 299_000 picoseconds. + Weight::from_parts(339_000, 0) + } + fn seal_account_reentrance_count() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 324_000 picoseconds. + Weight::from_parts(380_000, 0) + } + /// Storage: `Contracts::Nonce` (r:1 w:0) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + fn seal_instantiation_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `219` + // Estimated: `1704` + // Minimum execution time: 2_768_000 picoseconds. + Weight::from_parts(3_025_000, 1704) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// The range of component `r` is `[0, 5000]`. + fn instr_i64_load_store(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 766_000 picoseconds. + Weight::from_parts(722_169, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(7_191, 0).saturating_mul(r.into())) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:0) + /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + fn on_process_deletion_queue_batch() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `1627` + // Minimum execution time: 1_915_000 picoseconds. + Weight::from_parts(1_986_000, 1627) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `k` is `[0, 1024]`. + fn on_initialize_per_trie_key(k: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `452 + k * (69 ±0)` + // Estimated: `442 + k * (70 ±0)` + // Minimum execution time: 11_103_000 picoseconds. + Weight::from_parts(11_326_000, 442) + // Standard Error: 2_291 + .saturating_add(Weight::from_parts(1_196_329, 0).saturating_mul(k.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) + .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) + } + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:2 w:1) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:2 w:1) + /// The range of component `c` is `[0, 125952]`. + fn v9_migration_step(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `211 + c * (1 ±0)` + // Estimated: `6149 + c * (1 ±0)` + // Minimum execution time: 7_783_000 picoseconds. + Weight::from_parts(4_462_075, 6149) + // Standard Error: 5 + .saturating_add(Weight::from_parts(1_634, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) + } + /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn v10_migration_step() -> Weight { + // Proof Size summary in bytes: + // Measured: `510` + // Estimated: `6450` + // Minimum execution time: 15_971_000 picoseconds. + Weight::from_parts(16_730_000, 6450) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::DeletionQueue` (r:1 w:1025) + /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) + /// Storage: `Contracts::DeletionQueueCounter` (r:0 w:1) + /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// The range of component `k` is `[0, 1024]`. + fn v11_migration_step(k: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `171 + k * (1 ±0)` + // Estimated: `3635 + k * (1 ±0)` + // Minimum execution time: 3_149_000 picoseconds. + Weight::from_parts(3_264_000, 3635) + // Standard Error: 559 + .saturating_add(Weight::from_parts(1_111_209, 0).saturating_mul(k.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(k.into())) + } + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:0 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn v12_migration_step(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `325 + c * (1 ±0)` + // Estimated: `6263 + c * (1 ±0)` + // Minimum execution time: 15_072_000 picoseconds. + Weight::from_parts(15_721_891, 6263) + // Standard Error: 2 + .saturating_add(Weight::from_parts(428, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) + } + /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + fn v13_migration_step() -> Weight { + // Proof Size summary in bytes: + // Measured: `440` + // Estimated: `6380` + // Minimum execution time: 12_047_000 picoseconds. + Weight::from_parts(12_500_000, 6380) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + fn v14_migration_step() -> Weight { + // Proof Size summary in bytes: + // Measured: `352` + // Estimated: `6292` + // Minimum execution time: 47_488_000 picoseconds. + Weight::from_parts(48_482_000, 6292) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn v15_migration_step() -> Weight { + // Proof Size summary in bytes: + // Measured: `594` + // Estimated: `6534` + // Minimum execution time: 52_801_000 picoseconds. + Weight::from_parts(54_230_000, 6534) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + fn v16_migration_step() -> Weight { + // Proof Size summary in bytes: + // Measured: `409` + // Estimated: `6349` + // Minimum execution time: 11_618_000 picoseconds. + Weight::from_parts(12_068_000, 6349) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + fn migration_noop() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `1627` + // Minimum execution time: 2_131_000 picoseconds. + Weight::from_parts(2_255_000, 1627) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:1) + fn migrate() -> Weight { + // Proof Size summary in bytes: + // Measured: `166` + // Estimated: `3631` + // Minimum execution time: 10_773_000 picoseconds. + Weight::from_parts(11_118_000, 3631) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) + fn on_runtime_upgrade_noop() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `3607` + // Minimum execution time: 4_371_000 picoseconds. + Weight::from_parts(4_624_000, 3607) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + fn on_runtime_upgrade_in_progress() -> Weight { + // Proof Size summary in bytes: + // Measured: `167` + // Estimated: `3632` + // Minimum execution time: 5_612_000 picoseconds. + Weight::from_parts(5_838_000, 3632) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) + /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + fn on_runtime_upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `3607` + // Minimum execution time: 5_487_000 picoseconds. + Weight::from_parts(5_693_000, 3607) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn call_with_code_per_byte(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `800 + c * (1 ±0)` + // Estimated: `4266 + c * (1 ±0)` + // Minimum execution time: 247_545_000 picoseconds. + Weight::from_parts(268_016_699, 4266) + // Standard Error: 4 + .saturating_add(Weight::from_parts(700, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:2 w:2) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::Nonce` (r:1 w:1) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + /// The range of component `i` is `[0, 1048576]`. + /// The range of component `s` is `[0, 1048576]`. + fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `323` + // Estimated: `6262` + // Minimum execution time: 4_396_772_000 picoseconds. + Weight::from_parts(235_107_907, 6262) + // Standard Error: 185 + .saturating_add(Weight::from_parts(53_843, 0).saturating_mul(c.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(2_143, 0).saturating_mul(i.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(2_210, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Contracts::Nonce` (r:1 w:1) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// The range of component `i` is `[0, 1048576]`. + /// The range of component `s` is `[0, 1048576]`. + fn instantiate(i: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `560` + // Estimated: `4017` + // Minimum execution time: 2_240_868_000 picoseconds. + Weight::from_parts(2_273_668_000, 4017) + // Standard Error: 32 + .saturating_add(Weight::from_parts(934, 0).saturating_mul(i.into())) + // Standard Error: 32 + .saturating_add(Weight::from_parts(920, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn call() -> Weight { + // Proof Size summary in bytes: + // Measured: `826` + // Estimated: `4291` + // Minimum execution time: 165_067_000 picoseconds. + Weight::from_parts(168_582_000, 4291) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn upload_code_determinism_enforced(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `3607` + // Minimum execution time: 229_454_000 picoseconds. + Weight::from_parts(251_495_551, 3607) + // Standard Error: 71 + .saturating_add(Weight::from_parts(51_428, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn upload_code_determinism_relaxed(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `3607` + // Minimum execution time: 240_390_000 picoseconds. + Weight::from_parts(273_854_266, 3607) + // Standard Error: 243 + .saturating_add(Weight::from_parts(51_836, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + fn remove_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `315` + // Estimated: `3780` + // Minimum execution time: 39_374_000 picoseconds. + Weight::from_parts(40_247_000, 3780) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:2 w:2) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + fn set_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `552` + // Estimated: `6492` + // Minimum execution time: 24_473_000 picoseconds. + Weight::from_parts(25_890_000, 6492) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// The range of component `r` is `[0, 1600]`. + fn noop_host_fn(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_528_000 picoseconds. + Weight::from_parts(9_301_010, 0) + // Standard Error: 98 + .saturating_add(Weight::from_parts(53_173, 0).saturating_mul(r.into())) + } + fn seal_caller() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 643_000 picoseconds. + Weight::from_parts(678_000, 0) + } + /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + fn seal_is_contract() -> Weight { + // Proof Size summary in bytes: + // Measured: `354` + // Estimated: `3819` + // Minimum execution time: 6_107_000 picoseconds. + Weight::from_parts(6_235_000, 3819) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + fn seal_code_hash() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `3912` + // Minimum execution time: 7_316_000 picoseconds. + Weight::from_parts(7_653_000, 3912) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + fn seal_own_code_hash() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 721_000 picoseconds. + Weight::from_parts(764_000, 0) + } + fn seal_caller_is_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 369_000 picoseconds. + Weight::from_parts(417_000, 0) + } + fn seal_caller_is_root() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 318_000 picoseconds. + Weight::from_parts(349_000, 0) + } + fn seal_address() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 590_000 picoseconds. + Weight::from_parts(628_000, 0) + } + fn seal_gas_left() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 660_000 picoseconds. + Weight::from_parts(730_000, 0) + } + fn seal_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `140` + // Estimated: `0` + // Minimum execution time: 4_361_000 picoseconds. + Weight::from_parts(4_577_000, 0) + } + fn seal_value_transferred() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 560_000 picoseconds. + Weight::from_parts(603_000, 0) + } + fn seal_minimum_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(610_000, 0) + } + fn seal_block_number() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 557_000 picoseconds. + Weight::from_parts(583_000, 0) + } + fn seal_now() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 550_000 picoseconds. + Weight::from_parts(602_000, 0) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) + fn seal_weight_to_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `67` + // Estimated: `1552` + // Minimum execution time: 4_065_000 picoseconds. + Weight::from_parts(4_291_000, 1552) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// The range of component `n` is `[0, 1048572]`. + fn seal_input(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 487_000 picoseconds. + Weight::from_parts(517_000, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(301, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048572]`. + fn seal_return(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 318_000 picoseconds. + Weight::from_parts(372_000, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(411, 0).saturating_mul(n.into())) + } + /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:1) + /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:33 w:33) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::DeletionQueue` (r:0 w:1) + /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) + /// The range of component `n` is `[0, 32]`. + fn seal_terminate(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `319 + n * (78 ±0)` + // Estimated: `3784 + n * (2553 ±0)` + // Minimum execution time: 13_251_000 picoseconds. + Weight::from_parts(15_257_892, 3784) + // Standard Error: 7_089 + .saturating_add(Weight::from_parts(3_443_907, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2553).saturating_mul(n.into())) + } + /// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0) + /// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `Measured`) + fn seal_random() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `1561` + // Minimum execution time: 3_434_000 picoseconds. + Weight::from_parts(3_605_000, 1561) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `System::EventTopics` (r:4 w:4) + /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `t` is `[0, 4]`. + /// The range of component `n` is `[0, 16384]`. + fn seal_deposit_event(t: u32, n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `990 + t * (2475 ±0)` + // Minimum execution time: 3_668_000 picoseconds. + Weight::from_parts(3_999_591, 990) + // Standard Error: 5_767 + .saturating_add(Weight::from_parts(2_011_090, 0).saturating_mul(t.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(12, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 2475).saturating_mul(t.into())) + } + /// The range of component `i` is `[0, 1048576]`. + fn seal_debug_message(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 443_000 picoseconds. + Weight::from_parts(472_000, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(1_207, 0).saturating_mul(i.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn get_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `16618` + // Estimated: `16618` + // Minimum execution time: 13_752_000 picoseconds. + Weight::from_parts(14_356_000, 16618) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn get_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `26628` + // Estimated: `26628` + // Minimum execution time: 43_444_000 picoseconds. + Weight::from_parts(45_087_000, 26628) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `16618` + // Estimated: `16618` + // Minimum execution time: 15_616_000 picoseconds. + Weight::from_parts(16_010_000, 16618) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `26628` + // Estimated: `26628` + // Minimum execution time: 47_020_000 picoseconds. + Weight::from_parts(50_152_000, 26628) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + /// The range of component `o` is `[0, 16384]`. + fn seal_set_storage(n: u32, o: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `250 + o * (1 ±0)` + // Estimated: `249 + o * (1 ±0)` + // Minimum execution time: 8_824_000 picoseconds. + Weight::from_parts(8_915_233, 249) + // Standard Error: 1 + .saturating_add(Weight::from_parts(255, 0).saturating_mul(n.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(39, 0).saturating_mul(o.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_clear_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 7_133_000 picoseconds. + Weight::from_parts(7_912_778, 248) + // Standard Error: 1 + .saturating_add(Weight::from_parts(88, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_get_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 6_746_000 picoseconds. + Weight::from_parts(7_647_236, 248) + // Standard Error: 2 + .saturating_add(Weight::from_parts(603, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_contains_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 6_247_000 picoseconds. + Weight::from_parts(6_952_661, 248) + // Standard Error: 1 + .saturating_add(Weight::from_parts(77, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_take_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 7_428_000 picoseconds. + Weight::from_parts(8_384_015, 248) + // Standard Error: 2 + .saturating_add(Weight::from_parts(625, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + fn set_transient_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_478_000 picoseconds. + Weight::from_parts(1_533_000, 0) + } + fn set_transient_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_485_000 picoseconds. + Weight::from_parts(2_728_000, 0) + } + fn get_transient_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_195_000 picoseconds. + Weight::from_parts(3_811_000, 0) + } + fn get_transient_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_902_000 picoseconds. + Weight::from_parts(4_118_000, 0) + } + fn rollback_transient_storage() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_571_000 picoseconds. + Weight::from_parts(1_662_000, 0) + } + /// The range of component `n` is `[0, 16384]`. + /// The range of component `o` is `[0, 16384]`. + fn seal_set_transient_storage(n: u32, o: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_250_000 picoseconds. + Weight::from_parts(2_465_568, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(201, 0).saturating_mul(n.into())) + // Standard Error: 0 + .saturating_add(Weight::from_parts(223, 0).saturating_mul(o.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_clear_transient_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_012_000 picoseconds. + Weight::from_parts(2_288_004, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(239, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_get_transient_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_906_000 picoseconds. + Weight::from_parts(2_121_040, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(225, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_contains_transient_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_736_000 picoseconds. + Weight::from_parts(1_954_728, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(111, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_take_transient_storage(_n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_872_000 picoseconds. + Weight::from_parts(8_125_644, 0) + } + fn seal_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `140` + // Estimated: `0` + // Minimum execution time: 8_489_000 picoseconds. + Weight::from_parts(8_791_000, 0) + } + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `t` is `[0, 1]`. + /// The range of component `i` is `[0, 1048576]`. + fn seal_call(t: u32, i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `620 + t * (280 ±0)` + // Estimated: `4085 + t * (2182 ±0)` + // Minimum execution time: 122_759_000 picoseconds. + Weight::from_parts(120_016_020, 4085) + // Standard Error: 173_118 + .saturating_add(Weight::from_parts(42_848_338, 0).saturating_mul(t.into())) + // Standard Error: 0 + .saturating_add(Weight::from_parts(6, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 2182).saturating_mul(t.into())) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + fn seal_delegate_call() -> Weight { + // Proof Size summary in bytes: + // Measured: `430` + // Estimated: `3895` + // Minimum execution time: 111_566_000 picoseconds. + Weight::from_parts(115_083_000, 3895) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Contracts::Nonce` (r:1 w:0) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `i` is `[0, 983040]`. + /// The range of component `s` is `[0, 983040]`. + fn seal_instantiate(i: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `676` + // Estimated: `4132` + // Minimum execution time: 1_871_402_000 picoseconds. + Weight::from_parts(1_890_038_000, 4132) + // Standard Error: 24 + .saturating_add(Weight::from_parts(581, 0).saturating_mul(i.into())) + // Standard Error: 24 + .saturating_add(Weight::from_parts(915, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_sha2_256(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 966_000 picoseconds. + Weight::from_parts(9_599_151, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_336, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_keccak_256(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_416_000 picoseconds. + Weight::from_parts(10_964_255, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_593, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_blake2_256(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 821_000 picoseconds. + Weight::from_parts(6_579_283, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_466, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_blake2_128(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 773_000 picoseconds. + Weight::from_parts(10_990_209, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_457, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 125697]`. + fn seal_sr25519_verify(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 43_195_000 picoseconds. + Weight::from_parts(41_864_855, 0) + // Standard Error: 9 + .saturating_add(Weight::from_parts(5_154, 0).saturating_mul(n.into())) + } + fn seal_ecdsa_recover() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 47_747_000 picoseconds. + Weight::from_parts(49_219_000, 0) + } + fn seal_ecdsa_to_eth_address() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 12_854_000 picoseconds. + Weight::from_parts(12_962_000, 0) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + fn seal_set_code_hash() -> Weight { + // Proof Size summary in bytes: + // Measured: `430` + // Estimated: `3895` + // Minimum execution time: 17_868_000 picoseconds. + Weight::from_parts(18_486_000, 3895) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + fn lock_delegate_dependency() -> Weight { + // Proof Size summary in bytes: + // Measured: `355` + // Estimated: `3820` + // Minimum execution time: 8_393_000 picoseconds. + Weight::from_parts(8_640_000, 3820) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `MaxEncodedLen`) + fn unlock_delegate_dependency() -> Weight { + // Proof Size summary in bytes: + // Measured: `355` + // Estimated: `3558` + // Minimum execution time: 7_489_000 picoseconds. + Weight::from_parts(7_815_000, 3558) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + fn seal_reentrance_count() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 299_000 picoseconds. + Weight::from_parts(339_000, 0) + } + fn seal_account_reentrance_count() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 324_000 picoseconds. + Weight::from_parts(380_000, 0) + } + /// Storage: `Contracts::Nonce` (r:1 w:0) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + fn seal_instantiation_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `219` + // Estimated: `1704` + // Minimum execution time: 2_768_000 picoseconds. + Weight::from_parts(3_025_000, 1704) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// The range of component `r` is `[0, 5000]`. + fn instr_i64_load_store(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 766_000 picoseconds. + Weight::from_parts(722_169, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(7_191, 0).saturating_mul(r.into())) + } +} diff --git a/substrate/frame/revive/uapi/Cargo.toml b/substrate/frame/revive/uapi/Cargo.toml new file mode 100644 index 000000000000..862bf36f07cd --- /dev/null +++ b/substrate/frame/revive/uapi/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "pallet-revive-uapi" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true +description = "Exposes all the host functions that a contract can import." + +[lints] +workspace = true + +[dependencies] +paste = { workspace = true } +bitflags = { workspace = true } +scale-info = { features = ["derive"], optional = true, workspace = true } +codec = { features = [ + "derive", + "max-encoded-len", +], optional = true, workspace = true } + +[target.'cfg(target_arch = "riscv32")'.dependencies] +polkavm-derive = { version = "0.10.0" } + +[package.metadata.docs.rs] +default-target = ["wasm32-unknown-unknown"] + +[features] +default = ["scale"] +scale = ["dep:codec", "scale-info"] diff --git a/substrate/frame/revive/uapi/src/flags.rs b/substrate/frame/revive/uapi/src/flags.rs new file mode 100644 index 000000000000..17b91ce2b3be --- /dev/null +++ b/substrate/frame/revive/uapi/src/flags.rs @@ -0,0 +1,89 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use bitflags::bitflags; + +bitflags! { + /// Flags used by a contract to customize exit behaviour. + #[cfg_attr(feature = "scale", derive(codec::Encode, codec::Decode, scale_info::TypeInfo))] + pub struct ReturnFlags: u32 { + /// If this bit is set all changes made by the contract execution are rolled back. + const REVERT = 0x0000_0001; + } +} + +bitflags! { + /// Flags used to change the behaviour of `seal_call` and `seal_delegate_call`. + pub struct CallFlags: u32 { + /// Forward the input of current function to the callee. + /// + /// Supplied input pointers are ignored when set. + /// + /// # Note + /// + /// A forwarding call will consume the current contracts input. Any attempt to + /// access the input after this call returns will lead to [`Error::InputForwarded`]. + /// It does not matter if this is due to calling `seal_input` or trying another + /// forwarding call. Consider using [`Self::CLONE_INPUT`] in order to preserve + /// the input. + const FORWARD_INPUT = 0b0000_0001; + /// Identical to [`Self::FORWARD_INPUT`] but without consuming the input. + /// + /// This adds some additional weight costs to the call. + /// + /// # Note + /// + /// This implies [`Self::FORWARD_INPUT`] and takes precedence when both are set. + const CLONE_INPUT = 0b0000_0010; + /// Do not return from the call but rather return the result of the callee to the + /// callers caller. + /// + /// # Note + /// + /// This makes the current contract completely transparent to its caller by replacing + /// this contracts potential output by the callee ones. Any code after `seal_call` + /// can be safely considered unreachable. + const TAIL_CALL = 0b0000_0100; + /// Allow the callee to reenter into the current contract. + /// + /// Without this flag any reentrancy into the current contract that originates from + /// the callee (or any of its callees) is denied. This includes the first callee: + /// You cannot call into yourself with this flag set. + /// + /// # Note + /// + /// For `seal_delegate_call` should be always unset, otherwise + /// [`Error::InvalidCallFlags`] is returned. + const ALLOW_REENTRY = 0b0000_1000; + /// Indicates that the callee is restricted from modifying the state during call execution, + /// equivalent to Ethereum's STATICCALL. + /// + /// # Note + /// + /// For `seal_delegate_call` should be always unset, otherwise + /// [`Error::InvalidCallFlags`] is returned. + const READ_ONLY = 0b0001_0000; + } +} + +bitflags! { + /// Flags used by a contract to customize storage behaviour. + pub struct StorageFlags: u32 { + /// Access the transient storage instead of the persistent one. + const TRANSIENT = 0x0000_0001; + } +} diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs new file mode 100644 index 000000000000..6eb662363f7c --- /dev/null +++ b/substrate/frame/revive/uapi/src/host.rs @@ -0,0 +1,643 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use crate::{CallFlags, Result, ReturnFlags, StorageFlags}; +use paste::paste; + +#[cfg(target_arch = "riscv32")] +mod riscv32; + +macro_rules! hash_fn { + ( $name:ident, $bytes:literal ) => { + paste! { + #[doc = "Computes the " $name " " $bytes "-bit hash on the given input buffer."] + #[doc = "\n# Notes\n"] + #[doc = "- The `input` and `output` buffer may overlap."] + #[doc = "- The output buffer is expected to hold at least " $bytes " bits."] + #[doc = "- It is the callers responsibility to provide an output buffer that is large enough to hold the expected amount of bytes returned by the hash function."] + #[doc = "\n# Parameters\n"] + #[doc = "- `input`: The input data buffer."] + #[doc = "- `output`: The output buffer to write the hash result to."] + fn [](input: &[u8], output: &mut [u8; $bytes]); + } + }; +} + +/// Implements [`HostFn`] when compiled on supported architectures (RISC-V). +pub enum HostFnImpl {} + +/// Defines all the host apis available to contracts. +pub trait HostFn: private::Sealed { + /// Stores the address of the current contract into the supplied buffer. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the address. + fn address(output: &mut &mut [u8]); + + /// Lock a new delegate dependency to the contract. + /// + /// Traps if the maximum number of delegate_dependencies is reached or if + /// the delegate dependency already exists. + /// + /// # Parameters + /// + /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps + /// otherwise. + fn lock_delegate_dependency(code_hash: &[u8]); + + /// Stores the *free* balance of the current account into the supplied buffer. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the balance. + fn balance(output: &mut &mut [u8]); + + /// Stores the current block number of the current contract into the supplied buffer. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the block number. + fn block_number(output: &mut &mut [u8]); + + /// Call (possibly transferring some amount of funds) into the specified account. + /// + /// # Parameters + /// + /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. + /// - `callee`: The address of the callee. Should be decodable as an `T::AccountId`. Traps + /// otherwise. + /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. + /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. + /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a + /// `Option`. Traps otherwise. Passing `None` means setting no specific limit for + /// the call, which implies storage usage up to the limit of the parent call. + /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. + /// Traps otherwise. + /// - `input`: The input data buffer used to call the contract. + /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` + /// is provided then the output buffer is not copied. + /// + /// # Errors + /// + /// An error means that the call wasn't successful output buffer is returned unless + /// stated otherwise. + /// + /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. + /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] + /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] + /// - [NotCallable][`crate::ReturnErrorCode::NotCallable] + fn call( + flags: CallFlags, + callee: &[u8], + ref_time_limit: u64, + proof_size_limit: u64, + deposit: Option<&[u8]>, + value: &[u8], + input_data: &[u8], + output: Option<&mut &mut [u8]>, + ) -> Result; + + /// Call into the chain extension provided by the chain if any. + /// + /// Handling of the input values is up to the specific chain extension and so is the + /// return value. The extension can decide to use the inputs as primitive inputs or as + /// in/out arguments by interpreting them as pointers. Any caller of this function + /// must therefore coordinate with the chain that it targets. + /// + /// # Note + /// + /// If no chain extension exists the contract will trap with the `NoChainExtension` + /// module error. + /// + /// # Parameters + /// + /// - `func_id`: The function id of the chain extension. + /// - `input`: The input data buffer. + /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` + /// is provided then the output buffer is not copied. + /// + /// # Return + /// + /// The chain extension returned value, if executed successfully. + fn call_chain_extension(func_id: u32, input: &[u8], output: Option<&mut &mut [u8]>) -> u32; + + /// Call some dispatchable of the runtime. + /// + /// # Parameters + /// + /// - `call`: The call data. + /// + /// # Return + /// + /// Returns `Error::Success` when the dispatchable was successfully executed and + /// returned `Ok`. When the dispatchable was executed but returned an error + /// `Error::CallRuntimeFailed` is returned. The full error is not + /// provided because it is not guaranteed to be stable. + /// + /// # Comparison with `ChainExtension` + /// + /// Just as a chain extension this API allows the runtime to extend the functionality + /// of contracts. While making use of this function is generally easier it cannot be + /// used in all cases. Consider writing a chain extension if you need to do perform + /// one of the following tasks: + /// + /// - Return data. + /// - Provide functionality **exclusively** to contracts. + /// - Provide custom weights. + /// - Avoid the need to keep the `Call` data structure stable. + fn call_runtime(call: &[u8]) -> Result; + + /// Stores the address of the caller into the supplied buffer. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// If this is a top-level call (i.e. initiated by an extrinsic) the origin address of the + /// extrinsic will be returned. Otherwise, if this call is initiated by another contract then + /// the address of the contract will be returned. + /// + /// If there is no address associated with the caller (e.g. because the caller is root) then + /// it traps with `BadOrigin`. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the caller address. + fn caller(output: &mut &mut [u8]); + + /// Checks whether the caller of the current contract is the origin of the whole call stack. + /// + /// Prefer this over [`is_contract()`][`Self::is_contract`] when checking whether your contract + /// is being called by a contract or a plain account. The reason is that it performs better + /// since it does not need to do any storage lookups. + /// + /// # Return + /// + /// A return value of `true` indicates that this contract is being called by a plain account + /// and `false` indicates that the caller is another contract. + fn caller_is_origin() -> bool; + + /// Checks whether the caller of the current contract is root. + /// + /// Note that only the origin of the call stack can be root. Hence this function returning + /// `true` implies that the contract is being called by the origin. + /// + /// A return value of `true` indicates that this contract is being called by a root origin, + /// and `false` indicates that the caller is a signed origin. + fn caller_is_root() -> u32; + + /// Clear the value at the given key in the contract storage. + /// + /// # Parameters + /// + /// - `key`: The storage key. + /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn clear_storage(flags: StorageFlags, key: &[u8]) -> Option; + + /// Retrieve the code hash for a specified contract address. + /// + /// # Parameters + /// + /// - `account_id`: The address of the contract.Should be decodable as an `T::AccountId`. Traps + /// otherwise. + /// - `output`: A reference to the output data buffer to write the code hash. + /// + /// + /// # Errors + /// + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] + fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result; + + /// Checks whether there is a value stored under the given key. + /// + /// The key length must not exceed the maximum defined by the contracts module parameter. + /// + /// # Parameters + /// - `key`: The storage key. + /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn contains_storage(flags: StorageFlags, key: &[u8]) -> Option; + + /// Emit a custom debug message. + /// + /// No newlines are added to the supplied message. + /// Specifying invalid UTF-8 just drops the message with no trap. + /// + /// This is a no-op if debug message recording is disabled which is always the case + /// when the code is executing on-chain. The message is interpreted as UTF-8 and + /// appended to the debug buffer which is then supplied to the calling RPC client. + /// + /// # Note + /// + /// Even though no action is taken when debug message recording is disabled there is still + /// a non trivial overhead (and weight cost) associated with calling this function. Contract + /// languages should remove calls to this function (either at runtime or compile time) when + /// not being executed as an RPC. For example, they could allow users to disable logging + /// through compile time flags (cargo features) for on-chain deployment. Additionally, the + /// return value of this function can be cached in order to prevent further calls at runtime. + fn debug_message(str: &[u8]) -> Result; + + /// Execute code in the context (storage, caller, value) of the current contract. + /// + /// Reentrancy protection is always disabled since the callee is allowed + /// to modify the callers storage. This makes going through a reentrancy attack + /// unnecessary for the callee when it wants to exploit the caller. + /// + /// # Parameters + /// + /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. + /// - `code_hash`: The hash of the code to be executed. + /// - `input`: The input data buffer used to call the contract. + /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` + /// is provided then the output buffer is not copied. + /// + /// # Errors + /// + /// An error means that the call wasn't successful and no output buffer is returned unless + /// stated otherwise. + /// + /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. + /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] + fn delegate_call( + flags: CallFlags, + code_hash: &[u8], + input_data: &[u8], + output: Option<&mut &mut [u8]>, + ) -> Result; + + /// Deposit a contract event with the data buffer and optional list of topics. There is a limit + /// on the maximum number of topics specified by `event_topics`. + /// + /// There should not be any duplicates in `topics`. + /// + /// # Parameters + /// + /// - `topics`: The topics list encoded as `Vec`. It can't contain duplicates. + fn deposit_event(topics: &[u8], data: &[u8]); + + /// Recovers the ECDSA public key from the given message hash and signature. + /// + /// Writes the public key into the given output buffer. + /// Assumes the secp256k1 curve. + /// + /// # Parameters + /// + /// - `signature`: The signature bytes. + /// - `message_hash`: The message hash bytes. + /// - `output`: A reference to the output data buffer to write the public key. + /// + /// # Errors + /// + /// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed] + fn ecdsa_recover( + signature: &[u8; 65], + message_hash: &[u8; 32], + output: &mut [u8; 33], + ) -> Result; + + /// Calculates Ethereum address from the ECDSA compressed public key and stores + /// it into the supplied buffer. + /// + /// # Parameters + /// + /// - `pubkey`: The public key bytes. + /// - `output`: A reference to the output data buffer to write the address. + /// + /// # Errors + /// + /// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed] + fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result; + + /// Stores the amount of weight left into the supplied buffer. + /// The data is encoded as Weight. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the weight left. + fn weight_left(output: &mut &mut [u8]); + + /// Retrieve the value under the given key from storage. + /// + /// The key length must not exceed the maximum defined by the contracts module parameter. + /// + /// # Parameters + /// - `key`: The storage key. + /// - `output`: A reference to the output data buffer to write the storage entry. + /// + /// # Errors + /// + /// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound] + fn get_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result; + + hash_fn!(sha2_256, 32); + hash_fn!(keccak_256, 32); + hash_fn!(blake2_256, 32); + hash_fn!(blake2_128, 16); + + /// Stores the input passed by the caller into the supplied buffer. + /// + /// # Note + /// + /// This function traps if: + /// - the input is larger than the available space. + /// - the input was previously forwarded by a [`call()`][`Self::call()`]. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the input data. + fn input(output: &mut &mut [u8]); + + /// Instantiate a contract with the specified code hash. + /// + /// This function creates an account and executes the constructor defined in the code specified + /// by the code hash. + /// + /// # Parameters + /// + /// - `code_hash`: The hash of the code to be instantiated. + /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. + /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. + /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a + /// `Option`. Traps otherwise. Passing `None` means setting no specific limit for + /// the call, which implies storage usage up to the limit of the parent call. + /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. + /// Traps otherwise. + /// - `input`: The input data buffer. + /// - `address`: A reference to the address buffer to write the address of the contract. If + /// `None` is provided then the output buffer is not copied. + /// - `output`: A reference to the return value buffer to write the constructor output buffer. + /// If `None` is provided then the output buffer is not copied. + /// - `salt`: The salt bytes to use for this instantiation. + /// + /// # Errors + /// + /// Please consult the [ReturnErrorCode][`crate::ReturnErrorCode`] enum declaration for more + /// information on those errors. Here we only note things specific to this function. + /// + /// An error means that the account wasn't created and no address or output buffer + /// is returned unless stated otherwise. + /// + /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. + /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] + /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] + fn instantiate( + code_hash: &[u8], + ref_time_limit: u64, + proof_size_limit: u64, + deposit: Option<&[u8]>, + value: &[u8], + input: &[u8], + address: Option<&mut &mut [u8]>, + output: Option<&mut &mut [u8]>, + salt: &[u8], + ) -> Result; + + /// Checks whether a specified address belongs to a contract. + /// + /// # Parameters + /// + /// - `account_id`: The address to check. Should be decodable as an `T::AccountId`. Traps + /// otherwise. + /// + /// # Return + /// + /// Returns `true` if the address belongs to a contract. + fn is_contract(account_id: &[u8]) -> bool; + + /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. + /// The data is encoded as `T::Balance`. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the minimum balance. + fn minimum_balance(output: &mut &mut [u8]); + + /// Retrieve the code hash of the currently executing contract. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the code hash. + fn own_code_hash(output: &mut [u8]); + + /// Load the latest block timestamp into the supplied buffer + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the timestamp. + fn now(output: &mut &mut [u8]); + + /// Removes the delegate dependency from the contract. + /// + /// Traps if the delegate dependency does not exist. + /// + /// # Parameters + /// + /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps + /// otherwise. + fn unlock_delegate_dependency(code_hash: &[u8]); + + /// Cease contract execution and save a data buffer as a result of the execution. + /// + /// This function never returns as it stops execution of the caller. + /// This is the only way to return a data buffer to the caller. Returning from + /// execution without calling this function is equivalent to calling: + /// ```nocompile + /// return_value(ReturnFlags::empty(), &[]) + /// ``` + /// + /// Using an unnamed non empty `ReturnFlags` triggers a trap. + /// + /// # Parameters + /// + /// - `flags`: Flag used to signal special return conditions to the supervisor. See + /// [`ReturnFlags`] for a documentation of the supported flags. + /// - `return_value`: The return value buffer. + fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !; + + /// Replace the contract code at the specified address with new code. + /// + /// # Note + /// + /// There are a couple of important considerations which must be taken into account when + /// using this API: + /// + /// 1. The storage at the code address will remain untouched. This means that contract + /// developers must ensure that the storage layout of the new code is compatible with that of + /// the old code. + /// + /// 2. Contracts using this API can't be assumed as having deterministic addresses. Said another + /// way, when using this API you lose the guarantee that an address always identifies a specific + /// code hash. + /// + /// 3. If a contract calls into itself after changing its code the new call would use + /// the new code. However, if the original caller panics after returning from the sub call it + /// would revert the changes made by [`set_code_hash()`][`Self::set_code_hash`] and the next + /// caller would use the old code. + /// + /// # Parameters + /// + /// - `code_hash`: The hash of the new code. Should be decodable as an `T::Hash`. Traps + /// otherwise. + /// + /// # Errors + /// + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] + fn set_code_hash(code_hash: &[u8]) -> Result; + + /// Set the value at the given key in the contract storage. + /// + /// The key and value lengths must not exceed the maximums defined by the contracts module + /// parameters. + /// + /// # Parameters + /// + /// - `key`: The storage key. + /// - `encoded_value`: The storage value. + /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn set_storage(flags: StorageFlags, key: &[u8], value: &[u8]) -> Option; + + /// Verify a sr25519 signature + /// + /// # Parameters + /// + /// - `signature`: The signature bytes. + /// - `message`: The message bytes. + /// + /// # Errors + /// + /// - [Sr25519VerifyFailed][`crate::ReturnErrorCode::Sr25519VerifyFailed] + fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result; + + /// Retrieve and remove the value under the given key from storage. + /// + /// # Parameters + /// - `key`: The storage key. + /// - `output`: A reference to the output data buffer to write the storage entry. + /// + /// # Errors + /// + /// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound] + fn take_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result; + + /// Transfer some amount of funds into the specified account. + /// + /// # Parameters + /// + /// - `account_id`: The address of the account to transfer funds to. Should be decodable as an + /// `T::AccountId`. Traps otherwise. + /// - `value`: The value to transfer. Should be decodable as a `T::Balance`. Traps otherwise. + /// + /// # Errors + /// + /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] + fn transfer(account_id: &[u8], value: &[u8]) -> Result; + + /// Remove the calling account and transfer remaining **free** balance. + /// + /// This function never returns. Either the termination was successful and the + /// execution of the destroyed contract is halted. Or it failed during the termination + /// which is considered fatal and results in a trap + rollback. + /// + /// # Parameters + /// + /// - `beneficiary`: The address of the beneficiary account, Should be decodable as an + /// `T::AccountId`. + /// + /// # Traps + /// + /// - The contract is live i.e is already on the call stack. + /// - Failed to send the balance to the beneficiary. + /// - The deletion queue is full. + fn terminate(beneficiary: &[u8]) -> !; + + /// Stores the value transferred along with this call/instantiate into the supplied buffer. + /// The data is encoded as `T::Balance`. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the transferred value. + fn value_transferred(output: &mut &mut [u8]); + + /// Stores the price for the specified amount of gas into the supplied buffer. + /// The data is encoded as `T::Balance`. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `ref_time_limit`: The *ref_time* Weight limit to query the price for. + /// - `proof_size_limit`: The *proof_size* Weight limit to query the price for. + /// - `output`: A reference to the output data buffer to write the price. + fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]); + + /// Execute an XCM program locally, using the contract's address as the origin. + /// This is equivalent to dispatching `pallet_xcm::execute` through call_runtime, except that + /// the function is called directly instead of being dispatched. + /// + /// # Parameters + /// + /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), + /// traps otherwise. + /// - `output`: A reference to the output data buffer to write the [Outcome](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v3/enum.Outcome.html) + /// + /// # Return + /// + /// Returns `Error::Success` when the XCM execution attempt is successful. When the XCM + /// execution fails, `ReturnCode::XcmExecutionFailed` is returned + fn xcm_execute(msg: &[u8]) -> Result; + + /// Send an XCM program from the contract to the specified destination. + /// This is equivalent to dispatching `pallet_xcm::send` through `call_runtime`, except that + /// the function is called directly instead of being dispatched. + /// + /// # Parameters + /// + /// - `dest`: The XCM destination, should be decodable as [VersionedLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedLocation.html), + /// traps otherwise. + /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), + /// traps otherwise. + /// + /// # Return + /// + /// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM + /// execution fails, `ReturnErrorCode::XcmSendFailed` is returned. + fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result; +} + +mod private { + pub trait Sealed {} + impl Sealed for super::HostFnImpl {} +} diff --git a/substrate/frame/revive/uapi/src/host/riscv32.rs b/substrate/frame/revive/uapi/src/host/riscv32.rs new file mode 100644 index 000000000000..0b7130015f13 --- /dev/null +++ b/substrate/frame/revive/uapi/src/host/riscv32.rs @@ -0,0 +1,561 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![allow(unused_variables)] + +use crate::{ + host::{CallFlags, HostFn, HostFnImpl, Result, StorageFlags}, + ReturnFlags, +}; + +mod sys { + use crate::ReturnCode; + + #[polkavm_derive::polkavm_define_abi] + mod abi {} + + impl abi::FromHost for ReturnCode { + type Regs = (u32,); + + fn from_host((a0,): Self::Regs) -> Self { + ReturnCode(a0) + } + } + + #[polkavm_derive::polkavm_import(abi = self::abi)] + extern "C" { + pub fn set_storage( + flags: u32, + key_ptr: *const u8, + key_len: u32, + value_ptr: *const u8, + value_len: u32, + ) -> ReturnCode; + pub fn clear_storage(flags: u32, key_ptr: *const u8, key_len: u32) -> ReturnCode; + pub fn get_storage( + flags: u32, + key_ptr: *const u8, + key_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> ReturnCode; + pub fn contains_storage(flags: u32, key_ptr: *const u8, key_len: u32) -> ReturnCode; + pub fn take_storage( + flags: u32, + key_ptr: *const u8, + key_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> ReturnCode; + pub fn transfer(account_ptr: *const u8, value_ptr: *const u8) -> ReturnCode; + pub fn call(ptr: *const u8) -> ReturnCode; + pub fn delegate_call( + flags: u32, + code_hash_ptr: *const u8, + input_data_ptr: *const u8, + input_data_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> ReturnCode; + pub fn instantiate(ptr: *const u8) -> ReturnCode; + pub fn terminate(beneficiary_ptr: *const u8); + pub fn input(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32); + pub fn caller(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn is_contract(account_ptr: *const u8) -> ReturnCode; + pub fn code_hash( + account_ptr: *const u8, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> ReturnCode; + pub fn own_code_hash(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn caller_is_origin() -> ReturnCode; + pub fn caller_is_root() -> ReturnCode; + pub fn address(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn weight_to_fee( + ref_time: u64, + proof_size: u64, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ); + pub fn weight_left(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn balance(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn value_transferred(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn now(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn minimum_balance(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn deposit_event( + topics_ptr: *const u8, + topics_len: u32, + data_ptr: *const u8, + data_len: u32, + ); + pub fn block_number(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn hash_sha2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); + pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); + pub fn hash_blake2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); + pub fn hash_blake2_128(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); + pub fn call_chain_extension( + id: u32, + input_ptr: *const u8, + input_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> ReturnCode; + pub fn debug_message(str_ptr: *const u8, str_len: u32) -> ReturnCode; + pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> ReturnCode; + pub fn ecdsa_recover( + signature_ptr: *const u8, + message_hash_ptr: *const u8, + out_ptr: *mut u8, + ) -> ReturnCode; + pub fn sr25519_verify( + signature_ptr: *const u8, + pub_key_ptr: *const u8, + message_len: u32, + message_ptr: *const u8, + ) -> ReturnCode; + pub fn set_code_hash(code_hash_ptr: *const u8) -> ReturnCode; + pub fn ecdsa_to_eth_address(key_ptr: *const u8, out_ptr: *mut u8) -> ReturnCode; + pub fn instantiation_nonce() -> u64; + pub fn lock_delegate_dependency(code_hash_ptr: *const u8); + pub fn unlock_delegate_dependency(code_hash_ptr: *const u8); + pub fn xcm_execute(msg_ptr: *const u8, msg_len: u32) -> ReturnCode; + pub fn xcm_send( + dest_ptr: *const u8, + msg_ptr: *const u8, + msg_len: u32, + out_ptr: *mut u8, + ) -> ReturnCode; + } +} + +macro_rules! impl_wrapper_for { + ( $( $name:ident, )* ) => { + $( + fn $name(output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + unsafe { + sys::$name( + output.as_mut_ptr(), + &mut output_len, + ) + } + extract_from_slice(output, output_len as usize) + } + )* + } +} + +macro_rules! impl_hash_fn { + ( $name:ident, $bytes_result:literal ) => { + paste::item! { + fn [](input: &[u8], output: &mut [u8; $bytes_result]) { + unsafe { + sys::[]( + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + ) + } + } + } + }; +} + +#[inline(always)] +fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { + debug_assert!(new_len <= output.len()); + let tmp = core::mem::take(output); + *output = &mut tmp[..new_len]; +} + +#[inline(always)] +fn ptr_len_or_sentinel(data: &mut Option<&mut &mut [u8]>) -> (*mut u8, u32) { + match data { + Some(ref mut data) => (data.as_mut_ptr(), data.len() as _), + None => (crate::SENTINEL as _, 0), + } +} + +#[inline(always)] +fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { + match data { + Some(ref data) => data.as_ptr(), + None => crate::SENTINEL as _, + } +} + +impl HostFn for HostFnImpl { + fn instantiate( + code_hash: &[u8], + ref_time_limit: u64, + proof_size_limit: u64, + deposit_limit: Option<&[u8]>, + value: &[u8], + input: &[u8], + mut address: Option<&mut &mut [u8]>, + mut output: Option<&mut &mut [u8]>, + salt: &[u8], + ) -> Result { + let (address_ptr, mut address_len) = ptr_len_or_sentinel(&mut address); + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); + let deposit_limit_ptr = ptr_or_sentinel(&deposit_limit); + #[repr(packed)] + #[allow(dead_code)] + struct Args { + code_hash: *const u8, + ref_time_limit: u64, + proof_size_limit: u64, + deposit_limit: *const u8, + value: *const u8, + input: *const u8, + input_len: usize, + address: *const u8, + address_len: *mut u32, + output: *mut u8, + output_len: *mut u32, + salt: *const u8, + salt_len: usize, + } + let args = Args { + code_hash: code_hash.as_ptr(), + ref_time_limit, + proof_size_limit, + deposit_limit: deposit_limit_ptr, + value: value.as_ptr(), + input: input.as_ptr(), + input_len: input.len(), + address: address_ptr, + address_len: &mut address_len as *mut _, + output: output_ptr, + output_len: &mut output_len as *mut _, + salt: salt.as_ptr(), + salt_len: salt.len(), + }; + + let ret_code = { unsafe { sys::instantiate(&args as *const Args as *const _) } }; + + if let Some(ref mut address) = address { + extract_from_slice(address, address_len as usize); + } + + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + + ret_code.into() + } + + fn call( + flags: CallFlags, + callee: &[u8], + ref_time_limit: u64, + proof_size_limit: u64, + deposit_limit: Option<&[u8]>, + value: &[u8], + input: &[u8], + mut output: Option<&mut &mut [u8]>, + ) -> Result { + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); + let deposit_limit_ptr = ptr_or_sentinel(&deposit_limit); + #[repr(packed)] + #[allow(dead_code)] + struct Args { + flags: u32, + callee: *const u8, + ref_time_limit: u64, + proof_size_limit: u64, + deposit_limit: *const u8, + value: *const u8, + input: *const u8, + input_len: usize, + output: *mut u8, + output_len: *mut u32, + } + let args = Args { + flags: flags.bits(), + callee: callee.as_ptr(), + ref_time_limit, + proof_size_limit, + deposit_limit: deposit_limit_ptr, + value: value.as_ptr(), + input: input.as_ptr(), + input_len: input.len(), + output: output_ptr, + output_len: &mut output_len as *mut _, + }; + + let ret_code = { unsafe { sys::call(&args as *const Args as *const _) } }; + + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + + ret_code.into() + } + + fn caller_is_root() -> u32 { + unsafe { sys::caller_is_root() }.into_u32() + } + + fn delegate_call( + flags: CallFlags, + code_hash: &[u8], + input: &[u8], + mut output: Option<&mut &mut [u8]>, + ) -> Result { + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); + let ret_code = { + unsafe { + sys::delegate_call( + flags.bits(), + code_hash.as_ptr(), + input.as_ptr(), + input.len() as u32, + output_ptr, + &mut output_len, + ) + } + }; + + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + + ret_code.into() + } + + fn transfer(account_id: &[u8], value: &[u8]) -> Result { + let ret_code = unsafe { sys::transfer(account_id.as_ptr(), value.as_ptr()) }; + ret_code.into() + } + + fn deposit_event(topics: &[u8], data: &[u8]) { + unsafe { + sys::deposit_event( + topics.as_ptr(), + topics.len() as u32, + data.as_ptr(), + data.len() as u32, + ) + } + } + + fn set_storage(flags: StorageFlags, key: &[u8], encoded_value: &[u8]) -> Option { + let ret_code = unsafe { + sys::set_storage( + flags.bits(), + key.as_ptr(), + key.len() as u32, + encoded_value.as_ptr(), + encoded_value.len() as u32, + ) + }; + ret_code.into() + } + + fn clear_storage(flags: StorageFlags, key: &[u8]) -> Option { + let ret_code = unsafe { sys::clear_storage(flags.bits(), key.as_ptr(), key.len() as u32) }; + ret_code.into() + } + + fn contains_storage(flags: StorageFlags, key: &[u8]) -> Option { + let ret_code = + unsafe { sys::contains_storage(flags.bits(), key.as_ptr(), key.len() as u32) }; + ret_code.into() + } + + fn get_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result { + let mut output_len = output.len() as u32; + let ret_code = { + unsafe { + sys::get_storage( + flags.bits(), + key.as_ptr(), + key.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + } + }; + extract_from_slice(output, output_len as usize); + ret_code.into() + } + + fn take_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result { + let mut output_len = output.len() as u32; + let ret_code = { + unsafe { + sys::take_storage( + flags.bits(), + key.as_ptr(), + key.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + } + }; + extract_from_slice(output, output_len as usize); + ret_code.into() + } + + fn debug_message(str: &[u8]) -> Result { + let ret_code = unsafe { sys::debug_message(str.as_ptr(), str.len() as u32) }; + ret_code.into() + } + + fn terminate(beneficiary: &[u8]) -> ! { + unsafe { sys::terminate(beneficiary.as_ptr()) } + panic!("terminate does not return"); + } + + fn call_chain_extension(func_id: u32, input: &[u8], mut output: Option<&mut &mut [u8]>) -> u32 { + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); + let ret_code = { + unsafe { + sys::call_chain_extension( + func_id, + input.as_ptr(), + input.len() as u32, + output_ptr, + &mut output_len, + ) + } + }; + + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + ret_code.into_u32() + } + + fn input(output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + { + unsafe { sys::input(output.as_mut_ptr(), &mut output_len) }; + } + extract_from_slice(output, output_len as usize); + } + + fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { + unsafe { sys::seal_return(flags.bits(), return_value.as_ptr(), return_value.len() as u32) } + panic!("seal_return does not return"); + } + + fn call_runtime(call: &[u8]) -> Result { + let ret_code = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) }; + ret_code.into() + } + + impl_wrapper_for! { + caller, block_number, address, balance, + value_transferred,now, minimum_balance, + weight_left, + } + + fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + { + unsafe { + sys::weight_to_fee( + ref_time_limit, + proof_size_limit, + output.as_mut_ptr(), + &mut output_len, + ) + }; + } + extract_from_slice(output, output_len as usize); + } + + impl_hash_fn!(sha2_256, 32); + impl_hash_fn!(keccak_256, 32); + impl_hash_fn!(blake2_256, 32); + impl_hash_fn!(blake2_128, 16); + + fn ecdsa_recover( + signature: &[u8; 65], + message_hash: &[u8; 32], + output: &mut [u8; 33], + ) -> Result { + let ret_code = unsafe { + sys::ecdsa_recover(signature.as_ptr(), message_hash.as_ptr(), output.as_mut_ptr()) + }; + ret_code.into() + } + + fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result { + let ret_code = unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) }; + ret_code.into() + } + + fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result { + let ret_code = unsafe { + sys::sr25519_verify( + signature.as_ptr(), + pub_key.as_ptr(), + message.len() as u32, + message.as_ptr(), + ) + }; + ret_code.into() + } + + fn is_contract(account_id: &[u8]) -> bool { + let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) }; + ret_val.into_bool() + } + + fn caller_is_origin() -> bool { + let ret_val = unsafe { sys::caller_is_origin() }; + ret_val.into_bool() + } + + fn set_code_hash(code_hash: &[u8]) -> Result { + let ret_val = unsafe { sys::set_code_hash(code_hash.as_ptr()) }; + ret_val.into() + } + + fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result { + let mut output_len = output.len() as u32; + let ret_val = + unsafe { sys::code_hash(account_id.as_ptr(), output.as_mut_ptr(), &mut output_len) }; + ret_val.into() + } + + fn own_code_hash(output: &mut [u8]) { + let mut output_len = output.len() as u32; + unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) } + } + + fn lock_delegate_dependency(code_hash: &[u8]) { + unsafe { sys::lock_delegate_dependency(code_hash.as_ptr()) } + } + + fn unlock_delegate_dependency(code_hash: &[u8]) { + unsafe { sys::unlock_delegate_dependency(code_hash.as_ptr()) } + } + + fn xcm_execute(msg: &[u8]) -> Result { + let ret_code = unsafe { sys::xcm_execute(msg.as_ptr(), msg.len() as _) }; + ret_code.into() + } + + fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result { + let ret_code = unsafe { + sys::xcm_send(dest.as_ptr(), msg.as_ptr(), msg.len() as _, output.as_mut_ptr()) + }; + ret_code.into() + } +} diff --git a/substrate/frame/revive/uapi/src/lib.rs b/substrate/frame/revive/uapi/src/lib.rs new file mode 100644 index 000000000000..e660ce36ef75 --- /dev/null +++ b/substrate/frame/revive/uapi/src/lib.rs @@ -0,0 +1,131 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! External C API to communicate with substrate contracts runtime module. +//! +//! Refer to substrate FRAME contract module for more documentation. + +#![no_std] + +mod flags; +pub use flags::*; +mod host; + +pub use host::{HostFn, HostFnImpl}; + +macro_rules! define_error_codes { + ( + $( + $( #[$attr:meta] )* + $name:ident = $discr:literal, + )* + ) => { + /// Every error that can be returned to a contract when it calls any of the host functions. + #[derive(Debug, PartialEq, Eq)] + #[repr(u32)] + pub enum ReturnErrorCode { + /// API call successful. + Success = 0, + $( + $( #[$attr] )* + $name = $discr, + )* + /// Returns if an unknown error was received from the host module. + Unknown, + } + + impl From for Result { + fn from(return_code: ReturnCode) -> Self { + match return_code.0 { + 0 => Ok(()), + $( + $discr => Err(ReturnErrorCode::$name), + )* + _ => Err(ReturnErrorCode::Unknown), + } + } + } + }; +} + +impl From for u32 { + fn from(code: ReturnErrorCode) -> u32 { + code as u32 + } +} + +define_error_codes! { + /// The called function trapped and has its state changes reverted. + /// In this case no output buffer is returned. + /// Can only be returned from `call` and `instantiate`. + CalleeTrapped = 1, + /// The called function ran to completion but decided to revert its state. + /// An output buffer is returned when one was supplied. + /// Can only be returned from `call` and `instantiate`. + CalleeReverted = 2, + /// The passed key does not exist in storage. + KeyNotFound = 3, + /// Transfer failed for other not further specified reason. Most probably + /// reserved or locked balance of the sender that was preventing the transfer. + TransferFailed = 4, + /// No code could be found at the supplied code hash. + CodeNotFound = 5, + /// The account that was called is no contract. + NotCallable = 6, + /// The call to `debug_message` had no effect because debug message + /// recording was disabled. + LoggingDisabled = 7, + /// The call dispatched by `call_runtime` was executed but returned an error. + CallRuntimeFailed = 8, + /// ECDSA public key recovery failed. Most probably wrong recovery id or signature. + EcdsaRecoveryFailed = 9, + /// sr25519 signature verification failed. + Sr25519VerifyFailed = 10, + /// The `xcm_execute` call failed. + XcmExecutionFailed = 11, + /// The `xcm_send` call failed. + XcmSendFailed = 12, +} + +/// The raw return code returned by the host side. +#[repr(transparent)] +pub struct ReturnCode(u32); + +/// Used as a sentinel value when reading and writing contract memory. +/// +/// We use this value to signal `None` to a contract when only a primitive is +/// allowed and we don't want to go through encoding a full Rust type. +/// Using `u32::Max` is a safe sentinel because contracts are never +/// allowed to use such a large amount of resources. So this value doesn't +/// make sense for a memory location or length. +const SENTINEL: u32 = u32::MAX; + +impl From for Option { + fn from(code: ReturnCode) -> Self { + (code.0 < SENTINEL).then_some(code.0) + } +} + +impl ReturnCode { + /// Returns the raw underlying `u32` representation. + pub fn into_u32(self) -> u32 { + self.0 + } + /// Returns the underlying `u32` converted into `bool`. + pub fn into_bool(self) -> bool { + self.0.ne(&0) + } +} + +type Result = core::result::Result<(), ReturnErrorCode>; diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 2bfed05cd178..9c2ed30c527a 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -120,6 +120,9 @@ std = [ "pallet-recovery?/std", "pallet-referenda?/std", "pallet-remark?/std", + "pallet-revive-fixtures?/std", + "pallet-revive-mock-network?/std", + "pallet-revive?/std", "pallet-root-offences?/std", "pallet-root-testing?/std", "pallet-safe-mode?/std", @@ -304,6 +307,8 @@ runtime-benchmarks = [ "pallet-recovery?/runtime-benchmarks", "pallet-referenda?/runtime-benchmarks", "pallet-remark?/runtime-benchmarks", + "pallet-revive-mock-network?/runtime-benchmarks", + "pallet-revive?/runtime-benchmarks", "pallet-root-offences?/runtime-benchmarks", "pallet-safe-mode?/runtime-benchmarks", "pallet-salary?/runtime-benchmarks", @@ -433,6 +438,7 @@ try-runtime = [ "pallet-recovery?/try-runtime", "pallet-referenda?/try-runtime", "pallet-remark?/try-runtime", + "pallet-revive?/try-runtime", "pallet-root-offences?/try-runtime", "pallet-root-testing?/try-runtime", "pallet-safe-mode?/try-runtime", @@ -486,6 +492,7 @@ serde = [ "pallet-parameters?/serde", "pallet-referenda?/serde", "pallet-remark?/serde", + "pallet-revive?/serde", "pallet-state-trie-migration?/serde", "pallet-tips?/serde", "pallet-transaction-payment?/serde", @@ -643,6 +650,10 @@ runtime = [ "pallet-recovery", "pallet-referenda", "pallet-remark", + "pallet-revive", + "pallet-revive-fixtures", + "pallet-revive-proc-macro", + "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", @@ -757,11 +768,15 @@ runtime = [ "xcm-procedural", "xcm-runtime-apis", ] -node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-parachain-lib", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] +node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-revive-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-parachain-lib", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] tuples-96 = [ "frame-support-procedural?/tuples-96", "frame-support?/tuples-96", ] +riscv = [ + "pallet-revive-fixtures?/riscv", + "pallet-revive?/riscv", +] [package.edition] workspace = true @@ -1334,6 +1349,26 @@ path = "../substrate/frame/remark" default-features = false optional = true +[dependencies.pallet-revive] +path = "../substrate/frame/revive" +default-features = false +optional = true + +[dependencies.pallet-revive-fixtures] +path = "../substrate/frame/revive/fixtures" +default-features = false +optional = true + +[dependencies.pallet-revive-proc-macro] +path = "../substrate/frame/revive/proc-macro" +default-features = false +optional = true + +[dependencies.pallet-revive-uapi] +path = "../substrate/frame/revive/uapi" +default-features = false +optional = true + [dependencies.pallet-root-offences] path = "../substrate/frame/root-offences" default-features = false @@ -2024,6 +2059,11 @@ path = "../substrate/frame/contracts/mock-network" default-features = false optional = true +[dependencies.pallet-revive-mock-network] +path = "../substrate/frame/revive/mock-network" +default-features = false +optional = true + [dependencies.pallet-transaction-payment-rpc] path = "../substrate/frame/transaction-payment/rpc" default-features = false diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 5820fa803547..b7b9c15fe588 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -576,6 +576,26 @@ pub use pallet_referenda; #[cfg(feature = "pallet-remark")] pub use pallet_remark; +/// FRAME pallet for PolkaVM contracts. +#[cfg(feature = "pallet-revive")] +pub use pallet_revive; + +/// Fixtures for testing and benchmarking. +#[cfg(feature = "pallet-revive-fixtures")] +pub use pallet_revive_fixtures; + +/// A mock network for testing pallet-revive. +#[cfg(feature = "pallet-revive-mock-network")] +pub use pallet_revive_mock_network; + +/// Procedural macros used in pallet_revive. +#[cfg(feature = "pallet-revive-proc-macro")] +pub use pallet_revive_proc_macro; + +/// Exposes all the host functions that a contract can import. +#[cfg(feature = "pallet-revive-uapi")] +pub use pallet_revive_uapi; + /// FRAME root offences pallet. #[cfg(feature = "pallet-root-offences")] pub use pallet_root_offences; From c66f7bd423608c219e832bfc5e77aa3a57448df2 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Fri, 23 Aug 2024 12:51:14 +0200 Subject: [PATCH 148/382] Improve the appearance of crates on `crates.io` (#5243) ## Context Currently, many crates have no readme files, even though they are public and available on crates.io. Others have just a couple of words that does not look super presentable. Even though probably nobody starts a journey with `polkadot-sdk` or its documentation with a readme of a random low-level crate, I think it would look more mature to have a little better readmes there. So, in an attempt to improve [the aesthetics](https://github.com/paritytech/eng-automation/issues/10) of `polkadot-sdk`, I propose a set of consistent, branded, better-looking readmes for all published crates. ## What's inside - ~~New readme files for published crates.~~ - A python script to generate new readmes, for the crates that have none. - It will skip crates that do have a readme, and private crates. - Added a new image asset to the repo - logo with a background. - The main readme of the repo uses a [nice trick](https://github.com/paritytech/polkadot-sdk/blob/ce6938ae92b77b54aa367e6d367a4d490dede7c4/README.md?plain=1#L4-L5) to accompany both light and dark modes - but that doesn't work on `crates.io` so a single logo with a background is needed. ## Example ### Current ![Screenshot 2024-08-04 at 16 13 36](https://github.com/user-attachments/assets/3ae0881d-0f40-4614-9d2c-3c95029f0820) ### Changed ![Screenshot 2024-08-04 at 16 12 28](https://github.com/user-attachments/assets/fa7eadc8-aec8-4f77-84d9-54d63ce189cd) --- .github/scripts/generate-readmes.py | 136 ++++++++++++++++++ ...adot_Logo_Horizontal_Pink_BlackOnWhite.png | Bin 0 -> 207179 bytes ...adot_Logo_Horizontal_Pink_WhiteOnBlack.png | Bin 0 -> 205264 bytes 3 files changed, 136 insertions(+) create mode 100755 .github/scripts/generate-readmes.py create mode 100644 docs/images/Polkadot_Logo_Horizontal_Pink_BlackOnWhite.png create mode 100644 docs/images/Polkadot_Logo_Horizontal_Pink_WhiteOnBlack.png diff --git a/.github/scripts/generate-readmes.py b/.github/scripts/generate-readmes.py new file mode 100755 index 000000000000..f838eaa29a74 --- /dev/null +++ b/.github/scripts/generate-readmes.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 + +""" +A script to generate READMEs for all public crates, +if they do not already have one. + +It relies on functions from the `check-workspace.py` script. + +The resulting README is based on a template defined below, +and includes the crate name, description, license, +and optionally - the SDK release version. + +# Example + +```sh +python3 -m pip install toml +.github/scripts/generate-readmes.py . --sdk-version 1.15.0 +``` +""" + +import os +import toml +import importlib +import argparse + +check_workspace = importlib.import_module("check-workspace") + +README_TEMPLATE = """
+ +Polkadot logo + +# {name} + +This crate is part of the [Polkadot SDK](https://github.com/paritytech/polkadot-sdk/). + +
+ +## Description + +{description} + +## Additional Resources + +In order to learn about Polkadot SDK, head over to the [Polkadot SDK Developer Documentation](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/index.html). + +To learn about Polkadot, visit [polkadot.com](https://polkadot.com/). + +## License + +This crate is licensed with {license}. +""" + +VERSION_TEMPLATE = """ +## Version + +This version of `{name}` is associated with Polkadot {sdk_version} release. +""" + + +def generate_readme(member, *, workspace_dir, workspace_license, sdk_version): + print(f"Loading manifest for: {member}") + manifest = toml.load(os.path.join(workspace_dir, member, "Cargo.toml")) + if manifest["package"].get("publish", True) == False: + print(f"⏩ Skipping un-published crate: {member}") + return + if os.path.exists(os.path.join(workspace_dir, member, "README.md")): + print(f"⏩ Skipping crate with an existing readme: {member}") + return + print(f"📝 Generating README for: {member}") + + license = manifest["package"]["license"] + if isinstance(license, dict): + if not license.get("workspace", False): + print( + f"❌ License for {member} is unexpectedly declared as workspace=false." + ) + # Skipping this crate as it is not clear what license it should use. + return + license = workspace_license + + name = manifest["package"]["name"] + description = manifest["package"]["description"] + description = description + "." if not description.endswith(".") else description + + filled_readme = README_TEMPLATE.format( + name=name, description=description, license=license + ) + + if sdk_version: + filled_readme += VERSION_TEMPLATE.format(name=name, sdk_version=sdk_version) + + with open(os.path.join(workspace_dir, member, "README.md"), "w") as new_readme: + new_readme.write(filled_readme) + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Generate readmes for published crates." + ) + + parser.add_argument( + "workspace_dir", + help="The directory to check", + metavar="workspace_dir", + type=str, + nargs=1, + ) + parser.add_argument( + "--sdk-version", + help="Optional SDK release version", + metavar="sdk_version", + type=str, + nargs=1, + required=False, + ) + + args = parser.parse_args() + return (args.workspace_dir[0], args.sdk_version[0] if args.sdk_version else None) + + +def main(): + (workspace_dir, sdk_version) = parse_args() + root_manifest = toml.load(os.path.join(workspace_dir, "Cargo.toml")) + workspace_license = root_manifest["workspace"]["package"]["license"] + members = check_workspace.get_members(workspace_dir, []) + for member in members: + generate_readme( + member, + workspace_dir=workspace_dir, + workspace_license=workspace_license, + sdk_version=sdk_version, + ) + + +if __name__ == "__main__": + main() diff --git a/docs/images/Polkadot_Logo_Horizontal_Pink_BlackOnWhite.png b/docs/images/Polkadot_Logo_Horizontal_Pink_BlackOnWhite.png new file mode 100644 index 0000000000000000000000000000000000000000..ef2b997100ea26310f6588b5b932e3c9cc7d2604 GIT binary patch literal 207179 zcmeFZcT`l%_Aa^rK@cPh2rW?s1%(!YCI=;ofTAElqU0=DQY$Eu*$RRZ-3Z7AL_$jr z5>=9d2vNMZ;W^E7}nT(sH@kinl-~Wzd37l2dJsYA3w%;41%EJ ziV9b6LJ-0hf{uPACIVOdzlwQ+KS=Bqv>YLblp6jYucgm>8-iFN#jDb{++t_?@Te;I zAiwv{O;h^cJB~)(k%G*6B*0U+20DH#2wo1?xOGwpAJ=E#x>jw-fB01Dg#*Wb_&D68 zm-s(?eDBZFzia1@!hhGvA2<75C(xhZIz%adAmtFH{DG9;mGTEte%Hw#NI66&&>u)S zL@R$F<#(n0ft25M@&{55(a9f3IZP>kAmw+h{DG9;b@Kloq%3q66PyGBp`~}**VdIz zL+A_tvHkJ>jO~#_|c=Cx6JvZ#wxyPJUC#|7ql8sWa~9uXh3dE$pB8{lBMwYL4AstEQoEk}ne42e9TA1&X0T5NZvagl(fHQLV)$3ScNxu0leZ=CAg&8T z|EX7!nCJH&yCE&q|FOxVH2tg`YS(y~7q@Y(Bv55J7x%NGykj&}+3FRxR@51z~|A8=mN=X1d9icGDWE;!t0 zaE)b_V{4s1bS7|-&|U{_O`$8@Pnak#)=ZqMSikfsdUtlzp+}nc>*z|M;itvhgNrQ- zjvBqVT3DY*u}c3rkrz|5)VJDP8C+#BB0X+Icu8=HeQ>??{YWyCBT_Hx_KrIaG0;n# za1*QI$MOelhi-9ZqLb=xk%UYhJs0TH!qap}tI)>BgjL9mLtBxUJhc@wTpg{ZP&?qwhDe*I!^r}C_G^={@6pz)~%ca^$f_9)5pKry3fux!EtZJ(n*HM;nFGEak={aIF1P9;=nNDlR>|G9gT=BOnS+8CIXoYOay5 zPawY%`dnx{mH^ko(%6R;ZoWnKxQT2d5ch143JVCTGZ7)Ic(35{nTk(XrHpo22nDhD zXV-H6CGI(;p{9mu)<1%f)GnhNS$V~eL!lKlR4`n9irLlUe4uk7TaGJDld3K)`7!Wx zXHy(vqV%yY<8I&GI9wOP>5%tjnO5@iElVW99*M;^@8HvDOK5i-yb}(V!FIm z+<$VNl}2+KS(?MZlEqV7y`52qBRbHo9B>@lw_{C7-Bp#^Pluw%G)snb|HK%6!nMow z+gQz-y)N$vNW#Re8!y#^<+nw6+teBj7nHDi-s z!ohb_J)iCgRo!HytUr#;qJ6NCwO+dE5oefzlx5o5x;uY#^h#)o-|%p3?=2(tLH+x{@yus^{J+)idUr74C4*!o5iF#qw_^+vX(*1+mO6nAlajRm zq1zrP`LdvTP?$en9agiSaL63-5{pak6*s-ZI!E5?9wpVm=ucd$nV3!%uVb6iOry0BFpD5*zwU{^t#JzXN?D1+% zEgjrfOo6Tzdk=pM8jZwi!Lx3<(J(I?u$}8@s^|i3mRhD%O*(O0Da#!j(6pL%=>lYM zu6suuP5?(19zaXN2^>qY@?u3C76xR`dgTUUz^aH@iv0S(-C)&riJ-*?jkwqhg8=O5 ziFZQVU54Lh!yqsQ-M0oHpvmYu{B!EM84f%*h`=sr^uq=-+%=U1Ldyk-#(!BNL7$M} zMW=p^vy0D$}PPV8yFOT8=vGZmTd9pz60 zHnhxeknce;tX7g-L5c&x2>TeGnl#8D1;90mZ3qJ-1c#dbge zK+F!W>$TPsENHa4(qm(`L~ls}$BLfBux18_L6%I)^_1M)x|>B_VfU&)JFF{&*Z5~` zY#ojz2fg~3*PP&#jMDB2@Y&wldn$;((9~l3W>$d-$1~8pl{Fs&SqU@9jCv}?T8NBo z@f?5EQY18=xw?^x%-v-H9N+ya4q3g!dSHO5C=>?7&TPXQWMt-(XbM3G40A4%el4JN zVI+q?T2WjiK9>eK;gUVbId)NH)HX*38{S-3?0F`LBOsvuwoU^+2Db}9J3P+7wHL*2 zT!5OI0p`EW@)>%J1HIQTf}+ONuLXv6^Ivqz~P|id@NW?bS^&NFT1;W9@sC8q~fts&vQX88yo-+ zZBt`y?u*Ery}BmJ0)loa916bLotP(U@%(TFLW8NbM7|#@(!s~`NyR@|L+1rbPeFx9 zfbLV%`Y0UW4rTQ+uh9aN%{zBD{+;Wo4|de6*b^am-sS|3g-#p$el!U8-2)p|qde!g z($}Kt@Uvj{9yHnxO3C%A_5|Y+J;&3BKaI957S~&W(WC#aPZmcvu@u{zOr@B}52U#6 zXJR5F^8H5=IP5|X;OvXDEt*KwrxH5oF%r-MpT12;<^E{L%eWo~ZnXfmXGdjoB=K#F zOY-q+IykH=C4gm0Zh_De0F!OOAU4|A&TjWwQaTq=k0V98+5%9rpcijhPpat$0 zjg&@QcdujbmH`;*^kTjaWL+0%{e%T~t7*zCu17leA0gM+W!~9&_ne?YR3YZ&2b#k* zgEoS31s#a8ww7C6NOL5pm=FKJZ^L!?-B1md=$`o6V_CE2v(uf1Hz@w*_;npzOdJV^ zg4E|Ym)v#RjWT|MilYiTuLN-*0(uxOQPIz)c?QBhKKx1xc|OfcKE-zLa8b}QaBQHd zML|*mX+8jNknGL%>pi$6$%Y+UN-+)qZkIl@FfSDQIxo}07?(Ga!_mo=23%K#6q=~0 z#gXS|vF(kBuZG6_D$MzDbWoFYcV`~vaET#WU)WzG8a~^KmhDYWZy!AoP1^Cu*K_MI7!=?n+Hn7aR#U)|XDlXG#Qqd}vP#>AuM;weZCu$R}Q`)*kk2aKm9k4au?E*moP@ z5acJ8P$~@qDS{zBnGXzy-*+IwU}#4SU{`>i2CH=s=Y2l>W@t1FRxxd_p>Zn-9_vx- zONZYlC4qI!ho`cHl0aP`1ATg!_t_!_*S_en`+8qho3ex_720Wf_@wAK z9XoB~+HHUMq>vo8DR&HRRv}T8-uP&C#h#s{1sq;bp@7|(H0b*{orkzsnaB^HmQNdN zDhRZQmOtpI5JH3zf?_tphu;h(VbcL&xQ&?eS%`~I2iK$``;*p9gBlNwpF4(YI&9_= z?iiS^{yg%5A5ZFG@&(lx9FtbmV9`9bUNByS3W`8q$H68Uj>D$t9`Gof2T^$d7PBR zr^3qY&Ta#0^xf!}ky)7$t*AE(Jm=1Vyl8V85pYR!QE5ICxpHCn$4&Mcg|3S~Fr;7J zkFcNXa=222xvNz~KYVgm_pA$9R(vkpyM2>;awHb=PeunH7T=OKEyF{Jje4x=JIyIk4+vuF$t?N?nYKDT)I-Ow@? zj7Gmx*nE?N6#fzi)zD!h5eu@csbzLMtu2t$qZwTD?O>CyFs4g8`!W>H>LGU6Y@jdW zvoEDhmhBxzNFn^ctm_}|^1#p|)7S!Hm39aV^AJCLR%i$7*TTzJJo<7VsmhrZT=(re zY~GpiBamWl2M+i<(9PG@CYpMDh~&TCXKGdRzp*L~ks#>wH5l;jn>$+ke=*ItMXH%1 zlz3!j1IK~~x>^FddIm|6bS~kDuT5-x;g|%16tKPU|JTQbgh>v+zY7q~14lH@!K#II zZ+=x#RUQ%m4B{+@-yA%UmD@deA>A@P&gm1c$C`1GFtNE-~BI;LZ=(_a9K(c zYxyfi0N4co`(#!x2q9|IEF5{`2MacDy#84G)C7QE*E9|zZ({TCee*;KYb}V?aP`6A z+ePnU854pL*q07ch%Uv2kUm(iOeLx#jDuKZSKV+;hfP4Sn|H5#CxLF>3&XVwEKmKE zCVhhwQh4@X;4$^dE8`bL5cPkZ?PIM1>SpF0W=KJyw`YVIhdB<+7Fybd$Q6UaDgR5V z2|X^bXYLNMvaRAc)x3gw6m2VH3J9U zFb|PKWrd&}?BOgS>+%t(#*`O_8Bt>U3@S3s^#Tn<8(oSxe73;_Y^31bN@?=rXbVgR zY%9>it+yVB@1hXcX(=D6LTbvZIG8573X2(p4xZx~@Ps-3xHf(fONyTY?_fZ%WW!X~ zVLsFYb-1XI|2}~oU2o8;2Rzeg9tYP(d18fRwYwfiBLB~cYeWzs<=RYvD-J2_3t;Of zN`}{wffv6UQ~}Hg;NkQ32QfpK#fSt32F)6X2Nj zdn9$}-B<7Zpl|{Q)ld)q!(;{54hlNg`>U~Mj-V0mIdMtbI-fxbRg_zQ(8!sX*V$`` z&({<+^l;|bTFF)6wZ4#5-u{M2&dM-RVmnK}!@o+Z^u^9*<~U2$U)pEL1m!XyyVJXc zA`WA^ZcZy7r12}*0z4L*nmrfJg52JoZLwIT2J(OvQKcSNr3 z;c}mz{*-Ih9IvGZCGr>@Ia8}LyhJg(vXIwG8gq@?f46VG`jj!xnCr^WLUHZWM2@V} zyIQ;Rn?6s>`r<$0NhO?0)2L+8*yBL#AOnwjWIL+-teTRe`^)D!*N8XcD@6S+&wT9j z>LuEVvxhHF+nWH|?dW^6erg}QJxAHTuAeu8xQK2VH7tTX=bEhxsE*J3HOUJ#5<{CA zs~a`;)(H*O=F*1e_^x*WwM&uQC>5}i8g6D)kJ@o<%^@qwU{5^?cY3#7B?O=q~4 zrX2T~Jyhmg$)}z1m3w>J1X9d33C$|`7j>At*@EiwHDv6DY+EKcWtMOV4Zo_RrfWhh zu>0!aH*GoxF+p**;ZKf8N~d|%4zeTA<|qaCo)Fo0|Bx8vZLOKl+i^VY1*Prh`qcao zhr`pSa{x~gQ6Dsgauke%6FmpCNt%nq)7LntaTeo3jzl19AM1kQ^ARv%Yx?RR0 zypC_QN$dpba+V?(9B5I5hl)E$2}k_~9S0Y(o#6_XPW`lo6$Me9eC6WH+@Z`g{vq?s zXIoiOz7AOqjRPkkl+>cxJJ)U&CI>~h9$F)Htr{t`+it+fFU~8IHP$^%>cml?c)=~G zQPp69S~X*!z)rpCJFh#OfP|4?km)y%1S826->y(yPOh%H`@2iEhzZ7Zi)%(#}|fZe1*4 z3T-Z-2buk|mt9G3RaaJJQhNS%VraE1fP13ybJto3=Bl6$cUPg3-C|Epn9)GZQ)4mB zH04rm)Zux(LK&LFulupv71IYczYN8t#f z-Z;IKc}^(H->grsOCArhn^C|}12^>tJk9W|%KOEIQ^l95yd}klVh=YxdItvbUza&Gi532 zYV}go2)|kG(Ov<@C8pS#eT`=5d$0TA^Xh7aZ_k-rzeo^4 z>(>{}UNhvLJ%tq6GZ=r7@R<4pK`2tf=a-wnB&@L25~H_%##topt8Ko|O8wotcN>|r z&hPpZ_sx|B|E-<*L@p_NVy{du%PdTD!?n@WQNK*%cjXS!1J;7p%%`!Y6+P$waq%y; zxN-(SV8Lc@&I!DGoH@1v?^f^pF4?lmmN9|SCk+pAYh^Dg;Je_SbJYv~z`L!8*>E>U z%268MbI7N!VOZmTamPh6Ot#$;ld!$iOb}if_WRNcH({gplL)@hnOzDR>Ri6sRTmzd z8mCv#vCcch4!J!9c$~NG!nZk`JeoM3L4iGU6G$q?$`{RiGpV@7%8I;#yQx zRK3KzH^E_HX0PkGYMEWVOKDuU-H#md&Mq}fkhTRs%DGhkP_)_o+x@iB|8$^cIFC-Y zkMVUfAS13irPzr?NyG2=C{)fZ4A*;?78NnJx3}}4vhH#IED#+ppmFOx<@NhP5gH$^ z8uaEYQt9DXXTDo%qa-GTlA!Lly-O}{b>fO*g{jq<-n}KPg5yPZ*6I!43Y)T$Je}Yl z5Xkb!YW~q(6HAirksc{t=M!4a;>Ym$uW#SJsbzKOZ~m@bbS%8Q zw*_bCy4zgLLPifin>VOR=$_C^koksCRVh|H-w=I$;LI}n2Jn+UZ2)v3m{LaEV-=SZ z_J|%-TYkW&2|=br6P)l3TBLFzJn70FvY1^`VvVQprMW6JM2_RTCFZNO;DtjDcHLo* z-@ZjwARfD;H0U=#Ys?8kvxbR=>PjZX?t}ChV<@p&#p{6tY2gh|WVc(n`(3~I0b1W> zPO|}eYXGErT`CbsA_n7NB(>AWdk-NViITHusySR_uxJin(o!SyKP^pCOAcd2r|i^b*z;98D^5E_5WJ;H(WdqG~_J+0RjXE`|& zax1dxb5CbG5TQ=}cO|HlR6JY}&iA(%t>a z1W%8v7GYfBFhS)T`ulnjuKk9x(TlsqLecRwO?z^d4v0o3I1<)=Lm%Z_EZorJ7^I z4?M^qp_S&yQh zN}r`aT`p;H+c|0-u_qjuRR>MFcR6tEitP65mCO`mZ>{Zz8+v)xd%$7M?1W2yRBuq< zyzrB|^P=t3D>M5apN6aYn?He9^-c0#v0K!cR|jTB-!RD?(+XEH5^&@y=Zw zpaS4qqY7h{#ji#79xj2NjuNll_oAhz^hGYkeK}!A6uys+j;hLFJg(&hUe5pcVZQ1; z(zF%2*d=fVdz+d$h=}mO{>na{x2qXFxhbP6!7s^pJtJE=xso9Hgx#*VfI#G3dludjxRawG+pmB0 zokn`I{_}ul+o#0-c54nC{XCeS*>TI;!w$x4$SMbdeTjc^&(BNBjf{*)Zp_F8i|il5 zKZapjc5Ppogvy`9hu-ADq*Vk9lAr_8JwacUJxOvcf_TV0kdcACzh*daF zg_DPe=U$jwwiPq46CuiaF=arMs%~93y)A`OHYx9VxNfIt3F9v}lH*|Y*%?C#bW!0OyP z0Z$ie=Nc~5u|oI905kswxH=O}PM0>x+MZ|WjBz6CF!HUS4-$4ya8!BB&e>U@_^Y$G zw*(I_uZ|j8la{w&pm4}F=^G#10B3c*`Bb1x+0(}K!2|8!tt5ydtfQ{QeWUd3atPz6jnkJFHitbF77M5buAHxT;HcFf`InhjHM=D8 z`9wrEw|D9ul*apJ?T0(|dnbXWxfp5@z>;S%p_rubCui$&5~(LU_4 z6P%luucfyg%gxO-w6I8v;Lp-X@0vzB5a~UCSQ=0l;#T#&rlzJvWu_(hn$|YvoB8PA z!Ff7*`25xc-QT427w^qW3dS@dJO!{iSA!3V)lO5Gl^+Xx*Z;wkw*R1HCkj0k8!*Wu0bt+y5474;;K?8NEdkG1Yq}-9?Kru(0?rJ2;Kl zfw>1+3l@iX3#r(*Me0KIU&Y2Ym<|{08x_hrt5-)iV5Os@!(Yf!W2HDeL6?%6>bSUn zKqvJXP7GAmS+;&5Y2|=3J5S4A>fflVGklLUlBQWQ7}s#y?h=CQEib3-=eaQ9=TL2c z9bu~#lN0vP4@Rn?$K`&29heY8gKC-Y-aRQSESxdwu+6tZLibIboGz21YDwp1;naW@ zI#aUR^66N^IBTE+x9}m!FaRhX1y6*wj3dW`qfX1KiKzfJS4`>!) z*v}z#f4WTHM`@?u>8yq$yNEv@kq@ENT-evuho}bb2rFPwM=hC4*Fjes$id{2|D%{dirbDSVmTS-mzd6jxGazL3BxA z_Oqk>`IVq5%AoozvirHq-g2&lQ043YT7nQlM-Bc>Ye%lDiBUY**ENZ#iv_zV1$FjO zD3N8j(P-qlOGkTq15;wfRjd+A1BDiYvPfNv@A7*b*LGKvL$%$-|qe2*!y^-|~;-CMuobnXUHifJSS z+jJm=w#;E3BX}%6`CL3{-@du2L7SSSZ6m1ZF$iV8M-(k)YEl`AaAQxVmv)y0@$H zqp9~8z(}hr=c)}pi_V5!aH(W&@;(9Tkh`l$=sg9@fmE8x>>@~tI||Mks9GS>G-RCT zJV7092)+{2DFoz#$z6P3C#zntepW{&42%)qD=TGI8Kv`kR^$y>yncS>?6R7}XkU0a zPvVlNojKzyTNkq9JXHC3W=Ev%;ANLD)f05&PuBKLJ6Hk7O8fy2zeA%W?|05CCNpxO z?>qh9c~Hp<1|biRp(>bvSNH{pt80L!w_o?R>@)>b!4x3W5kjATOb|apvxHT$-&|Zm zHv-0(F(f}fjl|XrKex9O%%qu=1sOw5mrH&*N2HjgS8BOJp@3CdqbdLAdH4Y^T!K3C z`U)JXl*ykqU%+0ecnEu)*#822?Y=hyktse_J_rDNyU^(wK*8ba=)ksF2Eeu;Z!>_N zf9;F+ZYs-?Ay(|o5$%KP3JiMdL5}Qj0W&pa>EbZm((-HwFFZhqFl@25K(YV{pH93< zZ=Jk1u?uK|Mj?g7&T=cyGZI<4Fs4Q4s+tCU6DSF*pv3I~O=C+PghYL?s+^C+js zUh1R=P-x!?_~=k^8t)M$u2SQuvobcjMqPDk#|{AU;uZtc=}V*8^!R!1JkxEGf7d?| zx@6tur3;1(mZ6be;I-nJ8ztYS+tM{dNYPP@r%p9qYzPZ`P-?3NmI{CBQhX{UC8ZKD z#xt`?*P<>y-q_e^@hpE+b6uUH?lCES&WK#52KPw*zHX)d_Ou*d+cQjnMxvC% zCdx=a$yd)<5vH=YlcS)hXpQNp0=(v+JG6igQVCxP5Xd^1L6xLBdS^#cpLdI4u<%zG z45TdH6TH?Tco&7*2?uXiGx^o+aP(ujGS|CxMu*@m=i;<%?DM$VNkfBvH4`u?Q9iBHg?7?0MyzkBcgwV=SmElx9O7!ZHb zxr>lyGXH9z{8TPSWv+o@Hht8YPNq(Q)${qKmX5uZA&Ls)Q;w_=QVQeEi@~!?4;0%W z8Jl&_T74FuvI)-t`j(|Qp@=u9jV^3^F8MCkV8XuW9fQh73isBpZ%&SLPdb@MYbg!e zg}Ao#%{Ask;W0bRtwgmp=j&tW@Sz2Qn3xy~E30dU5GoJU6-QCE_0mH?1$;W}eD%3Gn?GX0 z8!YH!=~c<^*cI)Y9=Pe@7W`#}ds27FB>CK54qL4e0(NgozZJ|RLtGg3Akws!qGxpN zdgndPDUGR2k3?h+aCw{@%x=1vpn7d~AcY@uh7gd(<3zsOd~@FASqu>EIN$>AXk*V> zc)`{SkMKJ>I-D((hz*VDP?w#H?i^6Fb83m#md1_gu z1XBv8y!=`_d9M$a7J>}ux_n|)eo6IJLpA*rga{MO3~L?G7@+DVR;BtVzUcj0m;J3( z+K#sNgZX@S)NyEx(#ktyXYnL!Ubg4(D^+t2F|7tlqZfms)R2_Bjh$U;Tib043_jnD zsv$lb8=GV>#43>Fx8W62u6U}3%}*;QMEBKwj4(Z?%;cY2H|tja;@Yi>WP6h%BXr9BqbL zf-JIEBnbsybn09AQ1%iZwFh3Np6o0OUJz9}O+MV;)%EQ@q*DFmi_)&vX$CmxRYI#M z%&LZmjTp&@(r%_jl+r3cM1|*q`cEiDH-iaO2yN3VIZ^xgfIc@~_PFbPNlEP5{+q>} z`E9M1gwS)EA!8q_5{^77axQ$&xx5m)FX!<~UUX+APe^)lP%IuXD68&fw(`takzuOm zez)Q$SoyKqn$469X$9<1R`amP%F0TH2@iFn&sHciD{I2P#Lm`sJV6x*Sh47i;&xdd zS`K2ak&YbajLlm=LLvLS;#m02*-=powq z-9eY4($#@Uy}cJxm26kvwgLr4z8hta7+-3nXrb?r08S5$S32%3#w@osY90}MwyVA6 zJ1B>GlUqO0dUGp(QrSk41P>bjT1DVIVyFm3pA8BM+K?Wm8fGwr%g%~>ZPU)V!zUdl zTav%q=GW^Hsi-x~ySTby44#0pB~>N$`T0ntu50@wwIU+GthcD|-G_$o`~h!K8*A%` zQc#J5@t!3;HIf7hbj@<*-bvWpmRxT7FM8zY zmnW>;GRCq~NNrBPTLfwys>xtW5MFKoedqCzuX0dIm-%HSQBl#!%EzQ>UDhdjS*iM9 z89uhfzMc%X)vE^a1eC((7K=Ia99FBf|E16FYUUSTYr7m*rrN$wI(^aGchSx$bq_F< z2%(w)%4gqdw=C=~YurZt&EKZ>KLWf%CGf?_Xkh2`1+Bm}5*EA18xy6|v&cKahJ zJrap+8rto+&e6WTzZ1wx&AiCUli=56;4DMg{{yt@a!9yF}Yh=P#Fu>on zyM|&33xwV)O)G7Vah6|eey6K;3Vn{o$}!_xwg#!q=$3Ae^r8_R6+bA*O*Q>=Nozh4Wj2igCM`UKi}zjJ`LFxYdDsLl=S z>FELB32O)v`)g1R;*vUYy0?o;3jeu%z6XEbbl*)K??LMw&j)2oa{uU-KWn=yBn?(=TD9eB^%~V(i+Na0Go$4bnxJc%I1rH7cdj*io(gx(Es(0H@OUx<1{_7(eSsKDIUbv2+l|Rz&Do|sl>8@L;2I?YhMTf zeJS*OXKycUH>wUm7~%UH1$Tt}1e>x@lpTl@`&%EO_E48$n&_ylmFv2;FJYLorJbFf zss%t^_Vq!S>-3vvSlR~zFPeP(F-EEBVef3OGJprxzS;g;C^WuU@qM-(>!nCIi{HP` zdXU}&iRylQAC`fAP((*E3;ddbx~YeU=-tu>w{;*c*pMu9U(GLX!)~u;xgV6-GbYg< zNYayJM8LlZ0|t^o<3uj$uV+o!cZ7!d|HkIEeIb?AUYLMm36T07JF@gu8%=Y68FOA+ zJQD%E_mN>58ol~e)ohgd|4cSM7rs>jPtxBr;>U~&9BPJ%MZhd?tsRc$GE1z@K>$D}_ z_7ISs02jmxh9(J`=$(;ll5F1pnt269TsH_;b$ZCHh?r?(tDEJqU?qvj+GahVX@3(U zv@^!`HF*q=urrL-v1@GH7%P%0MOIcLA4y~*>ALk7KY9;cO?y9e#0_T))&;*~!E+~l3Vv1Bs1_Tj1 z4DgM3rz#1}C?jfCraRPgyLdd{XaWxWx~)|S4^nkaU2)j#nGyvm^b6@pu*8$6HWX-f z?N?~_S(js$BdQpwVLWMNlJW9U8R~q8y?u_+3o8DQXd3_T-F z^VCOxDgs?dKj0%IBqUID^+}Z>b32KAc6Rn_?=zPeLt0i>UApSA>q8ZOojyGDYiZbj zRV2X-Q~m0D=?~slUK$*`I{5TGRUm5x)!^b0!JBynE;MM0RyI?nGVoIbv9Tz>o~jIJ z;T}kmz%Ry*;uFI^Hee7hOR0HecY;Ft~`2Grn4sJpN4b`N#H z)-BN3`6b2xHGl(E);y||=7OEHcWMj(67^|1KfNh%@ttHZp@IfSQGQI&s@Y}6WRTcy zWpyl7fwKk3p#PrM*{A7mKp!f5p_U38SV-J9-`Y*h*-t#>^-;wpku`vom z1Pw-4v-N^*faIMM5Gpz{GICq(sa(=)*k2nSug4c-Fv%~^oX8D@o%oWy6jg72S=l@5 zGq*D}S`GL>U4T^t#HBE!mG$o3c{9bn5_f)|_WdrFlI@C97a(C_fDoqiM9r!?KMi;~ zm;+MWgQ0PsaYNHoIzm7ECC>H_cSejdg9HGJem?x_c#39&n9(X8V0@jOos``GSijG@ zEL~xH)tFNC8E7fmUF${i?5s@)Pj<9(w1d4G)w+`VasMk1v-X9dt=%;)EVSECG-YQ& zC#_80oI|-ho09eHjIzz-G*tI7W-@heWqwi_^Y>^)LlB0iJ?~DBQAiMm{Oo4|CS@TQ zp6fWs^#M`7VFRK5UKh*km?jT^ZJtsCx11Gdt~IeeMO;m9`{KLBmA@{&zce~&%1I=x zXZ9cC0MNP5iQ2z50IffY@rQ;Cut#rX{dCq{~DmzmROvTdYKvD?hUQK`YIQFLH~nf8#45^ zU)OfdTJiF>zug}tI8nVz}VWu9gwtcah8oO+}bxlXrRO$mt$*7*}aetYE?QBg}3zQp$dD4;E92DCu&yYkrXc6!@o z1~OVdmbOW!mZ_d-#o(P<)eL>>8ThN^d*{Z z;#}@W=(!c!Nmg~XM=fv-MQ^)nwmIpu;~}D+X->xHklkMWipEMcLt2LEC7nYE>|5#$ z3Pu|EP{p?zwqE9Ygy@mh`L$NSXIg7(pH#uG_C||tBM9<3+g!AGcXi)JT%ur)5x|boUL&iiMW1d7tLrF9_IREt<#Xe|P`cKWXFn-> z9RC=-)wK-dzC;wtG#%!AJ$~S2CRz^Qujmoi*=`H~IrY@m8(v_WDKdUbD_dTTgGU>9 z*O9&-S!d_w{-KaGIH=+V`99?5w}r@edSGETaR$J+nvx8Yp@e7Gt}^FKZBi@Vq^XIxB^YGubKC-PBfO%G^MNEm#D^bwoO2`AXgg0=2WQ zqC$Ww#1{5!~G|2V#e6ub(HIRW)HQrhjuYQv+69*4mHH4}+1U=4LJYA}%>gDF=?3=}#GYr8=MC3}Vvur_Eq-XPW84`^r= z1gWx-9fMLd5h#a^pJU6L(^(TAPev-q=7W`@qd}3dxl@L}bQKVW;vAl9pn$Zjq#^mm z7|KDsKQA!D!XNsQD2zlCLJ-;=1n>C3uf*`GkfWMvJm|gLc%QFz>~7Aohu0I~6A8wZ zpmP>~5kHZg_81gx3z7gvJM+8wWhYSVq60v#ROonjS6w&W+{8qdm}#K9zh6Z&OYb&i zJeO$<75RQ&0a69ksJ6EDO^*0ae|^X5CIVEVaYq5svHvuSkvvxXNOY`EsuhNKI)PmzhizO3X$|ndXPwcAI7JuF}2D?18aCw!bD@-LdNo8N0 zXP_V**EmA5UAm}JB-wEJ#OJvENEFV>~sAelM<=1X@ zwH}xI(--4I*519B`%(cUDv!>t&X9%6(-C1nwoIEbRvkBi%Yy&bz)D=op3SrwHQvXG z2r%q>;y>25j(sdci5-+RsZ_n6yzjkq6v0}GixYTpL$d$tkhRVH%M40`wi79W>gUa- zCt8~Y^%cu!b}sHrt?H!R-%-+r^9H71kRDG(s$^|goMFDkO|{m8zdvbF@9`yv>qKLe zxpYGzfD~~8Vq&U}csgKy8Bo{{wnpzl%~CznrB7JPV0oPEP0{>bdpyJ7gL;u*?(froFrp9HHi4<0O zw?KdC5Xp_RWGkS{_j!92o|wu6)QCWc zhLJ~z`4!u-`BXRfFyO{`J8%(qDdQ4QC6HSBaoh*1)A7Gt=Lu=P4Sq%Pe)L7CA*-*i z53W+0x7I=X2~wrT^W5>)4?%m?=QK!1Bcdxb_gD80Hake-@hae@3VvSv6u7w`;`b~F z1EF3K_XKoQc3&IVoxe)i0fIU;>p}j{wXPk*lrIgjpnJ&Nw6474xv4nPA(L!?uN#Di zjDc|%CUNSRY3sm+Ed zpVMj{i}9WJwi_`k@f57MS7YYejL+KsF#1~wAU$#Kcp}P}A*n)?8a?!72o4x<8Uspr z(0Ft@1$55B$2-mky1GS?(N{nMg9rNW>YX`IHHh#3GhPN(4z~=`-o-qs_0TO}WfZ;g zoszm?%=^`r{5|;r3di8`a><6gM**VhnWN=lGK@p_Y6KXhHqVwIO$Ew-l&}E?%oPHX z4G4oL1X7AKGc%+9TGbCXG1}LAz#?wd!0{eIN7)zl0S)2`+>s~ei2rNZbH8>q;V7U& zzZ3w+26HQR7OV@IyrMo$RmJF$l>)vx;yb%jF?@Ayve6@bbJydBzQ*4z4Bv|vCo}g< z@z!IdRSqa1B5>m}6+q@{S(c}qg{028MW2Aaqz=rdf}HCwB*=KY9Ny~aum^jE^zwt! z%LwwegAByw=RuDm10e~I2)O-d+9D~~uR7A#e3`WErCxemhT@Y^3-pzHgZ`4t+qBIDB1&9 zJuy};>4x^*yVvc?cU-zVI~U-iL$o{6-k|+Bc6BIG0c4~DqpV89MwtL7UhuiyW%Bih z#6AQmJsl4{uSpU+d4l3uvsA}Dd_;3`s_)BQ7f{TpxGSJd`>9GiAq`Y+o=c)sKBxA` zqQ&2RMArp$T|aUu0aS7lP=&5z;zcN;2b|QZXLVf5)JVrRQnryF2JfTY-d0m{BKwL2eYm-A~y8Hu*vmVUeAU_idZ{T-S7 z7!$?3#p0F^diL(~F=*IS7x&WDfYUmB=E+JLI~qe zn+oqGtrc%~19I1YRf-0ZdTJjE+y*pIb^*Gh?>nrs*{l8AM60a(QM0NF8)!&c5I)G% zVI^L;Mb&>6MfPQ4Xlqw-ey<>Y{U6uqnDVmcM@1x-`PPwMG<04*(UE^Y0c>1eCZbHI zoY**6t#(0qw}X`Pr`eH|9@h?4U@0w?1}E=t?o}&CAU;>IhGURt2PhOkqZiPtybm8z z1c6S@80nEAA`bfK5ZBH8p4+PA$v4xuzyS*w$=GP(JBTJaaOg@!yWD3`2^6%tA{lv9 zn4J0e{ot>4#6H_YyAFYaHT)Qs{%yL~K~!bycT>*;WEzLnj=4QWPD(%z^j^X%GK;`nF{$*(x`4YzRYakrmDXVqZnE4-_%IZeetQ72mp z8f_qX2+%-o87nI*+Y3P&>B&-HHph$gIp62kFfYL|y=9=L3_19{59|muI(TJi&_E@f z9%{7)MYLDh3a2>i%+U_WkpM058i?2eNSzCp;oe%8^*A2zhN zijJ1n4YB<>%f|ZmXGi-|f%NsgsG~SCXZ-kaeDYC1ydL2}2$~MP3h9~*PE+uLOKubB z+f{g9bu&)-MhX8K7+;-8T<9wPQ6o4NNxOn~!XZ{j@v1bo84!4n0&W0cGk8#R`HXT63xhZXgNp-zrg9wMG2!5- z*MiKR%Ofef@<1DgH8Y&1Qa~GA^*I1+SI8k$_&4(imbY^T0C&+FNCvW>^B(rJkmL~|WEukNBMImObfSmg^x#-Atanv9tw1EPJf>;y__{9L5T+fM~)RWiHbzK-|iQMUWJgAIy&$BtC}F zzP_3E);Qpf5SI+7nB}puL1GhtMSx+i0CR<7d!25QfEu-sB~k*KaDo~#sMK}DlMQ&O z*6;+_?Jf(QBK z)){31Tu5|9A8W zJV-eg?pSkmx3_D64&oFj7x}I<1f6>XeYl@(t9Im9p+V8*JNg#%{~58WRZ{}GKp_Ed zV#K~4at9{fkkUodt4Qv^msC(i-cJzIeCI$+SHe8o^r<^ima;!7ecV! z(|Q3&injgf)&1AWuyi~&8hZc>s0XB0P#hN%cnsn*d{>B8e5!S$AsDoXB4H&fQe)#; z6c(0%vR-zOTb6Qt|7ch0{s2DVTaENDSY=CDfbwJTYOjS?EPSZD4vQ+rZYqZq1zME@ zR8=2ndVjJVP?T2pZ9cbb!CF18rCbE^80+Tg*qi z9>ZZcm~G@j%YPbl`dTKPmO)I@VRxSrQ$=yE^2?V%krz*$ec3@{53s@S`$z*d7dhMm z6Tw1p@q;ixq+{Q25rWUfz|SWFQ8gMujh@hgU;RK^3>j)bgD4WLcj=AS9QZ%**GC80 z2kj4XB~a)8oUvI*QUMgFw5Ouqf_Bs~vIBJnoxIub45;(5zZU4jfx;71Dt$nblwe3O za1g4#pWS?w>Q}B0FYEF_fS%LGzd8?EUJc-*|A(wE52$hb-ruJ*Y7o&#rzA-lG#ly| zN+^;HB}p_0X+WWLGBqiMMj?dC)NrE|r9n|DG8EA)Qz{`z^?UZI-23_d-oNg^yWhR{ zTF-jcv)10neaFL&l{M&%uynU?{wk_v40c5U!6V4pvf?ye6+pM@RNDRfY9OV={&B3t z>M?xG=uoV~Xg+58Q*MUojkT+n(b@`fW zJ)(p?fpT18zrVOC5X^areg?`CLDM1~t%t#j2YoG^x3NC#p7W_7vhKg1tlG>T08dG$ zYpx#=JELTW0BKY#Sx`^N^riHg$AaA&>R0&D#s{x@DIAi95*K#eW*AiLMNdw>w)R@c zuxX{x%(j`v(YtT%rBu@~WtUY%J0LZ4x1@Bz>@feXFT3aL$6)Ssax49B;fhXXn%lXEzJSPc9&)7wrTd6+SeXf1 z?aai+q(#x?L=ryodjti8Q;((%4S!%A{Xhp!6VN*gd-^UxvCJ#628axI-0oa^PYSFg z!%X_*1vr+v!X&?8-M(-$_boZGE&n<{A{Gx+#$_DfU~%T`VAy~XEmFk)edqMRk6ffi zsZa*3xt7t6`?)dgq>HA`y;t_lK^dpykgXoYYUbo@!`FLXT3Mv{!KWi5{~j zyP@0`kb>fUNLymp4jeQ>8ICh`eWm;UtB%0b!lxmbSJ2lU5rlAWFDSIt*l=1{S)(%1 z7#pd-xqH5mU3bF5BU*-eo!M_XCVk_B-Ruk6;>&mZz$&>x-(Rl?afHyrE~M>ju*l8S zB4dMM=a&e%rVPY$agzz{tc}DzzNAPGt*-cPKg=ptqsJkH$-aTcEAa<+>0d!O$rv(S zz{wG0^Z8wlcm5E2CTSEZ=q8o`-2iQ-vfNZ}l zuRrc0T`i4OnukNIpCbns6eeb7D1oCxLxX6%fRJnnXh6?~G?qz2UQ_*`j|)TgC{VXe zUu4bZ!II6x)P{^}5^=ZsN;4__Dd?#iQj;^f+F^YJT%)1=&)WlMajvuHz}l9q+qd^# z^fx5z7Igct3n75d*#6Ffwx*JBr)d@-Uagof_ z2vCJ{bkWC+EV=_-boc8HN)t0@5cX1Pr4{jLNI;$LKDJf7A}>|}j2$)&X7b;i4rq?U zDv z2|=;uCC3FxqRSK)5G(U}&ogh|j?Pf6nF~PSij!D<1hWaBrqC6oK$!u3n05Y1%ona} zX5h2Jndch98w<15ef|9A|L7mEjM6%{rQ!AKWfE$(rcaz!<1DX64UG(2FTKy*wyV|N zm%d?zNyQZh6CPrblx?8xCdor$b-E)~&ijHTU}-A+^AuC%x1V`J|FomNZ1g=L@kG=w zfyDIbyEoR|N=y}hvY=_lu3ha7*A%j={R(GvK-FM`d)&H4mY_|OhImsNi+p;usQZz| z#U;m8oYoLxj9z(<`u zWp{7C4l=!of6lqxIm4$9>qDu&z>KCH7v>M!EDL`cC98^52PaH!499HTwr`*5FA4Z% zU();=M?}mo^Mn44#ARZNDmCgB<7DFoKr`6`q2mhq3-W#YGCJG((=szp@#(vvw=q)) zF`pvDA{m_q^8-`)n&`f3OmuWr5*gH{6m9UFAFWdVn!Ad=Nec0bxL$w+PCpW-;=XQ79JR`G%=`YYT(iLcqf(ftbd+}ncKq1y)!o2N zDkLbYs%<|bmvk@kaA&~BJ*gRqu=NS=s^>H#vgp>nhd1BjF% zznCV{ohS>OEg8kxZ`Wmb(rIYzYSGoxtHD_S)ZtRC(lALiySxtu-o*YByrz-$b+6kM z^_zZ4F7!*yvSK%vsE#F#8nVe_NyQ~7wRbBqhCY+VEu0MimVp^wL(>RS`+$B(2|TSC9yQ9HiWd_Xvuw6SsDIV_Ehqo=k=LiNr5)- zsNnv9SI^0ii{ce2*30SgJr&hgThO_d@VF&x{#dKA>4R?_1j*wj_z`H?1& z44$-+!rwSh(m57E`|sVP_|xDc`TE2Ch878(TQ6LlrjUZ*33Q-(w4k5gHLx_D9p`@g zpxvbrlbGpnWn$(mI0#xN&_i621y97n(+0i98M0$ot3ZAN{ggtQt4GqFlk-glGthes zX5a~P_3cSujo}mAInUtIQ|0{9cUm%}U@4Wo@1q(a$Ww8AbrjDN06yics+w=Oh)ALN z*@B@ho#9b$<~;j{apy*)S?8uK_V%g9?1ZrKi3x*@h+V9A0g^l1zD^u>m+Qeg<_AF` z9+CN6hdh)g1j~*OlKqs>?OJ@H{O8(5Mb>rETO+RuG$tH8rxJbDc0<;hh9y&a`&M}8 z6N7C3q5I3JrZEW7F@@j0Jg}WN#bWNYpbq^WPvvBEa` z?)gnSp=#wsn(eS{LNagYw{Q_3Ij-Y`Z7Vxv%CU>e-w~f0XH|qQy-@S<2>LOij&uNb8mlFn~l9+Mtly7ipm2iqo8>*fiOug65+ z9FP_uc{RR<>zK*u*KD2YJNCXZ>hz2&`dZ#L`}*3M`6dOGp;f7p@AS{E&_R8nQ5d7k z+bJ$Ew3S{csAfK#h%$c-nlAjQhBxO-c%7Y#e*c!UQ+`%+ku!>znD_7VPhT~~d^3w= z+w{5oN;$?Lwwc-Qf1mI7U*(jj+kAXx<=sKQKj-6lNnTXZ^)&0VE85ou1b6K1_m)X% z5KsMeWhi$}P(XNh`=0Kc33`2-eC9JPiDH_-2{|K?mTrC7$&*v`V5{5lX(er9eLMX>2~cG2xv^?r< zDv@YV1_vi3Y@G8(Ik|(A+_fHtjfmb9u& z#fU`r_?^Ss7?sMr5iXGs^XM7O1N z;y;x`$M1{epLQ1He*TPa)&;ofnenge-pK{b38}iN&)whK?f*%Rbg6453+>%_>(vIn zPPB3B4YDRm|Tn~F6(sMdP zbx-W$(S76psTN^+wZ`K;l)kcOO-&c>-P4gcAlW{(GLM_QGN#M(ZPc6vEjH-&-_a2z z_KTWdfA&3?TF;tQB2`-&cz)lBF$6S!?cN6^n`;{ezxVr{39gF|Z77v|zxRT2$PeSz z?z(+qACxP^;^hZtoiMqiG3CS_af}!Za4W7;+ZUu|Y5HK)pj`~lU*nGI8wA3%DxW*` z({7`GXh~t5H)*t?JZ|0K-tSR|=!!-h)gRk`qRxMtE=ha8aHFoHhUE1jBUJhJNVXU& zUpk!jeCeOFVfjVf?S-E=hf4&6Ljaihyjo(DZ@S2KzkyvK@674%OOnt5zYwZ?xtWh! zL`WR|D}C-8oeb%51p#$yp8I2q7y zwl~)P7S$0GO)t*hIMe~l@NDH;Y&q_Jv_wOksrWo@Qjx3BgSPmlML z2~!8=a(%!y;V5-%Nmhqn^}CwIge{!@lPjKCS?nfkBrLc83WN1By4+E@e0J|_i>F0_ zqD2oL1h8lNmM zdbOiU&!`E9w9jJlP*%WN(VXLQVE>uvKef+!8_k*X7^jC=4st5W-|c&al55QqeK9xe1H8hurt?m!2}u zXWY-;UQ`M!4Qn+w`F0=OLqBM8=Ypi>Z$-Y@+EG@(5YD2E`?8T4i8=seB$jUTTb|y| zhF4w3LM|j2%P-<4rc@_ty4u#$o|poch2% z@t8Z0W?A=#56Wg&YkrvYq2i}2D)v4n9HO^P*vHjrTjhEC>dMcY z@M&0FpD#=z4mFyvkDnd(&rOU(O&WcbNWn4d!wmEB&W;{VyrdO?l^XKE13CKS&4-(t zx_yEcV%$n!!%cEH*Fan{aM&qdQJy_Z?wm|gbIx!?VRZ?gT#VxmK=B~PL8yzjv@E0B zexDN{Y;l<8)-Su3Y}#g2tt@xDS6h5|vR5>XB-I7%JtT*S{iF*9`R<2%j??CUu zYcHNn%;v?CeO_dSNtin#yuW|g2QoIX3OO5nnsb6AS!4}*lywl`rPbeQHqM`8bn1~} zFB~J7uJ7~EZoQ_fQK8S-PJOF~ZimC+Vs#{|wLP3BIXe@UA$tAXRtJaf-s~+4rlR+{ z)IKLrRPNZofZyAIBh5{h(ZG`gAKD^dKlLcQAzZ5aY_Rz7g+)k==@Vf<5ojMmed5Pv zKQ7Je$UmTT*kIOpYF^(FKMzX!-T1+3`IFKgd}ZVV7JKZS> z&Nq<7NO2~c7E&uq{OI7a1*PYN8Gs^|AmT&g0Bma&mT_PwccoI4;qCv_rRMCF-)#qQL#X#ICE#@3lo# z=Lrm51Gy+e^@o91%|~sz@e>gU%PJbe?+fOiR!iWE|5)_%9NLLE8~=CgoS-wQnDu_w zJ8znRZmKcmU2QR&j$w~axi9VBm8>l22ns2^h{(*qVSXU-i`f(IYehbWRym3;b$3`Q z_i1y1xn7*vcgcJ{!lISU+23lu?eppTkP^)uwtKt#k}V8!yVyp^B~^cUsFn^%%#n4u z3}e4AC?X>AQsVmgbLWm7=>7UpIqzzQYLU;pwP5&(nq$%;55;-d`8q3b%x-v>YwxgT z170)%XAeU*`(1mn?65#+iGpg(@}q>@G;^QIGC3$YV4NH9@ORq1t2u;haG1rb9Tz7} zwAbaC>G6szm?)C5&}(<@+aC+U+N;h7;;tKn_uUeM1r)+uu-ek{_uqgo(y^NzTZVzIv{b0#F9+He^AC?m3J8QYa_O4z(jugCHAIdOy zzRgRXv?{ozdPYo)pT(;`1;Nz)h*8(C?k#~fFy_}ic8gRF$@DbkEKoTI5=kF%W3jc@ z*8JR)cozq+L+BLEl_orCTh|O#f_)81Z$v;(-8TL6=UsKGUG(mrchMjP(R45g=j|R3 zd5L!D*gYwajqgT?GQzvu0DRaGw#{4vgQqN&&*%q_6{`}qMC`f%0cjd%MAmzxk+jd9 zuvSkr7DGmml>Y}EOQXZdnQYpwRqaDLr4+GWe!YY(8=KBx^{7r7yYcdPK5Ka+F4?9h z?|b&enkl8iQlGE&y%msJalEIw+3H_b%`eHkfK8(gI@O@G;!W%|Y#3yu#HLE;t4(03 zPgs}Uei0A^Cg^UT4FEentMWBrJEeU*DQ@Fe?O`tlxGyzH4X=x z#!=Lt9jP)4;f>P^;QlgfF%Ht>Y~o|!qxLhOc`w($D~lUP8dDJgv}wG{l61+Fd3#%l zo9u|wk&58X)Fmz=fz~N1yjccig2kjXJ96mPFCV{x$)1yn1ZDTb!#r7S~xjqk4T_Sh!#1RNs1iHa@j@Y$*rG?NMqc-Rl@86E7f{`OEU zJ(4&m>qMO}U@W9|B#~*hYDMkm{coymoi#X1+&Ge+zhTE~sw++OYVoDSchnEMRTo-E z*S!k(gwm@DJ}gW-&P+S{PKZ~OK9sL)Rcxs6&!P|8J9sj>o_4Jc<0ZATx6fuuIJuE( zAy6thW293OG!hy3zx`{jS;Viw60-~v$UKE+1X?T9`gV4C>q^Nse$bgCdy zPkNVFdb~sAb5VH^_6Nc?DOiIL=aM{Pnd%WoWi|%Bfag7l7b-JO$SJ9lAr_lFGTU(A zcYz9&B|-kYRd?P9119I~`=+>i&SYbSxtiJO%7XMnqO1JGf0i?d_o-a8teZ@1$b z-!)vjA?y#Wt&2zFOK@`zYP~-U#q%>o*OMB@yEUFDZeJ`k_Fz}z0yx7bW8LPatCF-7ZNZ03gBsU=*Lp!3 z2}X+>E~Se?joc+cvYy{RNm)>bi>R4nhq*^~czb{<DaBIjOqJ2he#(#ll87KrHzpelak}SUNGF93=@w&DGIs$8=_2Qe9>`;` zuOXeOW*Hs7|8gx;=FTy1Z5d*}Wc?Y2f#zXu_V0|klc}~zV{O;wzO|DR-1n_@DJ`2W zX^C=_LOp{19MfF1SIsEixAJci6*|(q*fkJos_^DNBuySu6H>dad;a9q*;7z6OKs7%r>1lK49OyweLpJvya}f74Jl&zYn(t(nFha760!2Db8ZYb#!rk7pdKOPc_k!)}^&bviY{DWb+? z{tI}&c$cD^gN14f`(bcrg`>5rYUdMNLp7Gf$?!zJQ^X*9kLyTB$Y>dmWQjAX zDa%}@M!a^>7Hl}!zw^V7{jSTWBFcpU`1YP05g8#@bl)2$ee7YzeP6_?y?-hp)r(I( zTD&Rev_855vuO*aa5vQjfUjp+Yu$u9UGzyoc)AI<=LBa@4_!X;{BUS?%444Tf;~Zj ztu2>3&-)MN{C=hn<6Hpaw3mpJIJFR6>7~cv4&cFpVBwSs;h8N_HI5}%nTvljZ@ni) z*q;%86XorW=RaM>oJ*s{WMW^s{u0Ar_8D&W{-A&bp(P9NBr6`f#;^_Cn|n_r`@px> zN57lhTtbkB!Ftnp=)H=3@-i;%P}*PpUwOX_SBo-#=8lg(=_-Q(2U-!^+GO;N&`VOR4l z#f=XoF9N&LW*)f}84~AV*j#Z_wYOcHb6xcBzU{$N>5jN?SE6LHk#La%|yhUwI$>HlAx4rEef0sC$|LJ_$-JR}Q z!>{?Ad5k8+UqUdKdrSew4o>zk%3v4w^w~~MuAqHBiNW=vMQcE$&}SZ?^Jf19KwPjoC`^E3W>2Ox>KQAr)smxhFZaW^91!TfmqPV9fCbZ|mdh zB4qCw?i*G*$PC8SX8-yU95@E$&F0nH!TTUxk~A$!63azn%;X$>^LZn1C{98Z1>VvdoJR;27&4dN;q}(WNTt@9jGWn-u##4dnQKsI(HXP1u&Jr(cR_HM$tPEB>5u3vPg|qr z$&3O|;z<2}_bcQgKiGBu9E**p+Zn@1Jiff)A@-~3oSxG;)udL_k&Z2710~Mt$V6Pz z&kW6+OYHB|#x7qYlEIa$Xm|<|6X4|c!j6jeo*$ji8w+lOU@yE{BY;C-v$Q-sJx^jC zi+e}b5v-8&CD^-5oW0Y*-i09qS*-T=?wcEte&X19S0lhlrW3cP(_TK3>zuy+gJ4v) zsYI=grX-Ws{7Yx&XW=X@=`d~hTdlMN^CV^S^YlzNYe*);Zs_yb3o5|9vD83nGsltw zCD>x}g^`dmIpW5KqPg*+B(3Fo9Xp|Dqxd`@Vlz zr5v)b281bWgIdf5)#XR8&em%2EtY!dkX{B&;xD2c8YejZ8z&k__ZDH*mtxagKXjhD z`kHs^=a(!pj<7ba&t({B6=A!YuwCdAriqtWtu;*!eY#kOlcTzQu5ZQ~KFlc#u!0%V zoDl76!?Swuj%PgJW*H@(;eNSRPjUKElw^wokac=ENm<$DPS$-l;iE$!%ty0iA~N-h zLh-{m_WhI&O1N|Hao@dLe2SrO^1A0HLb3pcT5Sn~h!Aqy_gCd&mHT=yZ#)RGi-uX? zmks#W_$$gGY3o(LN6pM!Covppbb5M&t4)}@;?_=lMYZ<3INW=uO&9JP3TA&S&ux=}Rd7E*Jz>ul zVZK(?%5TkPqNH|;jMvf!j(1sai`%2&Q1*uB1KT`P)?tTCVC5X3ufI49^rN}fVtDi? zmLs74pEo;?jzIkyP?R6QUP|o3@+AE)ZDhT~J`G}@!gORK=4#C+wQ4e6DUY#qH;?F9 zO0;yZzePYkYzxrEK)-tU7DINZ~s~z|IuwMsn&(4nKE{m(1LUvT47U-wq!O*kt zE|jU;)6In9jes~s1XC?lF%qlKHj(>*pS6|qj5E6h{u9e=LN?6?oWD)*hXtt=I_-DR}g%kK;C z#FGf$v$>IRi~9vfZq|P4YxoN0r;B4!1>tkg7>+f%A-Iz(V{fMSTu{xEG_iA?d9$rK zUWDoF@le9qj}*Mmwuqq|UVD}A?fJgHEOyly0GU9ow9hpz_myV0t)~ zUrB#;UOW$%{?HlOm)=X5_dI3Nx}R-1dQI&5!DG z`BuN4@Qs^P6V2muZ*~w*=l6W%425eX@2&Po5vZD_Gj|L6&<@jySb9csr=aSURe{Xl z-!i@LVMVcFHsom}v?lA#PDr>n7h~5%4rmTu`$_8hO3sz3kGK6BP0x@`{aWo1r7-mIxt893J7w4@}3V;7{@Z>-08Z7atH4xSmWI`YEx z@-3@oKPi%p)Q-&koAppA#=FG@9;$AAU&PIe-Zx}|M1*KOQcKaBC-ue zay82s|}((hIQ367=R=JqN1B1&$)^2`x(v`(EJrH`dkoMHkk=fVruk8?Nl z{_1YA5I+h`Adirgmn3CCmkN^d@KBlYx6c7gLB4?rJ4G}w6MPqgOcewu?J%Wya6)=a#$k>*j z)X=Id<$TzCJKphs3$!Ej9GE>y&u4(DBdr|XwBM!Q<8G59;|vY|n8C-SA4-chPQ*yr zh9Hz{ZoTq~m{|7g!Gi@pU5x?nGxu;D%nIeM=O+~*r7UzR^j5O$9<8L=AS-1#4r{Ah z^NnUe=HRG01tR-32TeARh5T@^)A)(8&G>yCJ5BB}?9W?}1mIc$oiQ1E5W-O=8{kS? z)9rZ@(FhyFa^$Kt#nR?f$WMie8#0=LQy5R6KVufJ^tEnQx#gymB(O>*ai>D=-inpw z?Mm!FU-4t=U zh$EkfcUB}d!uqLfS`cn<#;|5Qdt4#v(=)gUYmxP5MxMhYe)92bLPD}iR9)=Yu20tb zQ%$0!S9*(u{455@?FA-1hu+qE(7Q-Vf;bmbhXXC_iK64J>0$Hhj zU~%Ycf18Om|7Z0=w%^NsPgjrUT-6FMAg7`u_733D7Wyc_<5Ol~JTD43$Irh4@u34vut>P%*xUd!jZ6 zHdEN5GtA3=AG+O$U0)JspMdJli#oVAl8--jzPeN78OG_&B-eE{wzGls3bqvCSv zimPzCf(?*Q0mrBDqhc$rh5%}82N!)*(cc3?%NOYd*A>;i?gf)s7f_Xnvq z4Q4zAbMo4&Aa_OxU^?rXcW6}dGuW;eN=4!$SKgbgvscOMe(i24y`U^e&y1**Koqup zA0Y}3J6eHc)0UPz8M=7rtvc)O1X3#g)QzP=cnzEK3l3Xf03N zb<}F6@tpBwysN^}&Cqq7b5=zkJxcE=jY2{#mai4%7zac6o5WMRc!s^Cik&R*0{I8H zNHQu*1XeXKQy)GwQ<_l1OUS!WqD$*5YF=eQ{~?D3kj%RW5V)x( z!EsqrG@%ttqT&vVR*nWCn2wgaInGK$?_wI6b3CP|RW>_aPU3q{g?b~icGg)`W`5DO>7X#575 z`_SR*WStfeJv=P^D{Oi&C{)&zO5hbTrX`~)&Y@iqinAq!{u+QozR_^Iz^uMuOIv*q zmiF!eg*v9!%OhB_xXlTZxkzV~aXl5p3SOY+${m#R(2gVA)|iAGEuxHM1f$kG+;szC z+7wfWW6I6+0;Ep41Ts)o&;fZl|0?UyLoppScH*htd$aIRL0 zk25c!E0;FSS#1;F`ZtKWh^N=BWw`Pj_VSa?T}bS({0>$QtJPRKnq~I$rd(5abyIaY zUnYW~cYIa5;9^Iohx;_QGynSOMRY}oD598;!;Qp+hFlsaOo0gn(FyYhS9}ck;#d88 zteikQ#MJZ6uQhIpF+82=XjP7Plk73jp1{CBiusDKDj%S8xN+x5p@OqUD!sG+t@)2Z zhlk?1Cm@=SIfmEsAc?bGA$0ll&ls-W%h{^8U7I&(z!7g1XEmL(-^JF^T1%<$>Vj{%9g-F@UXPcSG@>$JRE3g5opuW!h`;VNeh=7+;s<5 z)G-$F5AFRGHygMz^JNgZ6)VqG(4Vf!*&WmAQ#?`5QhA)U|Ie>8Z{l^)@!7H6z#ey*ifA1V5`Li2}+E- zdotpxKY|#12x@zBbRm~`V6aUd7z~RhM=A1X6=#?(0ISu`m10sZ@d!~@tzHf*Q>KI| z1yhju4t4F9NB6yxSh`KP z6qpF2#$zlZrAejnGn`c;v@AwTy_8WlpN+H1)8Z!d++1y9PhE=VuqAMkUVO39xL3zL zI{2Xv855sOPEp2kvP$|4iN@c><&XIe2MBuYJd%O;-tvm$p25GVJhYB$Mr^uA#1)1n!2vVN`NknX8h1j7 zO!bL)0WHqPRda#rVGB>J($*LcWsVO z^?1<~^XALi%v4h;Q&mf)_-%G^U$mD_ue9=IHC~NV6uq#troZc!U)u|t)0gi#J9W-) z?`J=Fwsc(VP}940r1Qpzr6(YoSG-FQanA?wsLSS|-m} zHpw}pN_f{8w$X(PYUcYQC)&@&1?-FC!`9z=^}->2oGlDzpXMWdJvA!wxQxP(P-4() zCzrruNYr$G==gNZvA^Ac$@NAjBj%dof9t*DR*HJG zRUsNG4KD@0&b3vk5pYc|*J%JDontBla9`JxMAh8I6iZbF|xx*Rnf8oWGu(>%I7FD+pU67hyGN8!1i< zlYQKDF4#(lDvWp%Eo;mazS1(6EDm0NUbf}wR408|K}8agK!ZltnU51N#@@ECPd+Ui z^8C1&j-wEBJYjs|Ryx)R?dB^p)HAqtG@u(dIaADY5e|2sVeCk9aNlBptZcLajAS9U z4uaa1g~e~QOh+u&Eun$W$8f+hHsIpMxBl0@Bb--9B#c)+t7SrdUF!2ylh(edEMeb| zOB!{ioG?Dusy!Ei!&C0WFK`%Ufh#Y|q{Giw{6QHFC9yn}9{C{+8pJxc&QVp{=DxwpgR@myV9$ zd5@%?VRzcl9P`50uveY^P}@_ve24DL`H)~oJ83RQ#~sP*1dVBZcnMxeuPQW#cl$pE3Kyz<%(&-Pj)jZmk7Ci`LX}vO3QC_xnc%Lic7172+Vp^ zQc3jWTdDxfTp1JoQmb_;WQryV2#~DOHs?Pzo2*=0vLOGiLpIX4;KgoI21=}y1RXBc z8jz}0iDKA}gj-OFX3UBy$MuQ9;Qr!eNb)2L)`|#2a`S=@;9-|Ic-WQ;ndi`+2G0qd z1C{Mw*#G^-sN-&hlOCIY{@{#aR=Uv6F=){?P_mfq97Yl^s67D;H2O-_mnoHrA%iyi z$wfub)FyZ3=%X54QC3jm*AK&;_=>k&DcPLYZ>cg<`TJ(xd$zu7?%CcvBB}grMW^SU zJwrP~MG4C?a*N4L0gH?J+mCDE=4jwca&-CSA6LbUb4dHlb48b)mZ?OXzAzSL6Qu(T zOLr&RN#^#ZX!H)9%9#+s{+o1Q=m?pt!KB%7X2s)4t>j@)g%5q-gS)s&?)tVnzc_k7 z0F4ky6AheNu_x!9!dFYu4@nlpsynA+Y7t(&lkxN536C!!e1p)Xt5Tu$FLuU0acX95`GmunY}tLCF;u^#n{Oh8 z&0kP;b(5gFMN;`4f18c6c6WXevSV>;e54oG=`uP-jYZ5A?PokLfd}(9y}j@E#x z=#-5hZ=)Cu|0MP`0HNB5_8Rr0la!xrzoeJC{FKZjIaG;460|&5vd0@77?Zd-{^Q9* zfaaeX*^$qo$;Sf67N8Bd?Mgaeet~L`5}F`xf>Be8ew^#3*eU`zC<~jfm6D9`s`~^e zBfQVhPvIX{+_zR{~p8{5FQO_bvP4*30q&^oJ;Ft8d_97MON=a$J z`{%RpuLc>;N=LhWR8dZNNUcD=%6a9HNy?R3hrMUPls8P{<5|{9$JmyY^eKh&}wqYQ!(A_gOso77DhE3PKBOsmTLBnQ2XwlU|LGkuagBvDI zCTuTx2~0+4Jo_ZDk_V38(b{LWEItudItbbHL5E_K*+tdNy6fM^MX;}=(A9!+`)~6K5G9O|0h~>DnCXs0#yaKGH_*tOS64s2@GwtHx8f$|TY3ODD*>=!W1#L*GWB=PI zvH0FM7@VPqb~`6lntzFlDZjkuGRM@%klP?8?BauW_OGSjIZ^rH3iV^Hc{?9qX6qtP znYamBgyf_*Pq%IdLE0v32FUm5{^|L7*J0gNllZo-JZ2d>x_i z7U+!TP=Na6x@i;dVFEi5#+d01Qw%3sCMx@nxTk407NgQM6bU8(GjIOLmiCAJ=9&3S$W*PWr*(uFqNHgL z=f#V|rE^qR0tv=k7KiF~IGx?Lc$FGU&t)D<#qKLgw7!pesUrq&Suk9qrX{(Nt04v3 zG0HG`ahh}1X7$NxFOX3spuqV3fXf7y=FQ1@ThbZCWm=93T;?QcH5+!B564{QjT5fE zkn+NH+A%-~?lN=Qwyh%0!@6E4^3Z5D74;zOKUloD3QUG$RTm_%0TYx9M>jwQ8xUfv zrn8{e(gm1zzV0BOt8JAs?;`h209| z>HDf2XYExB+hrBg`KO|g3ny61Jf^ zOEYub*LT;|%-z=0*Nf-4wdN564-)~F8hj47w2xoJ7IHbhVzp*6-1dVm7M~_+8RK~G zpZu#~t~!xts}_2K`^i4eC{J9oz<>piF*MGjxWT^if#pVnlR1Nw#V1G%FUB1rCvoT0P5nuYf z{5&HLcgZP+BMkH)rDiRn5lKEG?CiU?PjEH*euc$fWeha%{xw|Hdq!g46a=xTB(WTr z%kDR>S*gf^*9^NHPuvqHnu*p^(HxIO$`TH~4nSpK&PBMn_^j4mgJVvJzeM<+?e!z$ z${r$eTpR0mcsmloMXmQExw%^lt*4QVS2bd;z+$0Dd~2LGHa%B-u9=ET)JG%C$}90#~~z)jd9C}~HZ0Q>~>96jX1wdgOQN^}btp*ki{ z)KZe`cu3_Lx;&F_oD-0*-WT(5IQ1Nl?J!o8HPB?eGPpJ-v0NM-c|eOi7Yo*e@;1vq z@aiI(dS23W9_#p2KWc>W(T!-_sCV)|3Dr9}qQ47xr2QRP;W)0dul&CUpp>5I;_1NT zt5jipn2k^haHW6s?nNSQywqCalMq6@kG-z=~IwuKgyZjVHR zzff0lPKvm2bGFvwmlc0N-_MCn&$3RE%RCC{q0&@pnY>9hPF?&MX;3Xkw8})jlM$(P-aT z5hQEi=?K?6pxvZIGfO-XfqlMi)Bb2~o7m{grYq==5K=w#E zAaVAQLyIvh3xG!5s?L>ylvv)Tgfx-0&dB%L{++$T%khQ=1<$rJbojD6EWlik?Ps)J zYv|mtvC^zY92hzU7)n!CrHw15i*>bQ3y@hfPWng`>YL|Wr?Nu^8@O^TwEIz z2<6Bd5~l9TMH}NS;i>A_e3(o#+H3IJ!>DSMWQ?Pv>;8zy@^QC~uaM~$Z|`-A_@wJJ zWdt-Q&O&%}Y1D(5Bl;oX-QnKUv3}fO-Ho}6D`j)ESg-W4MB7JFMi~x*#yHP|Wms!_ zF?rerIR5l6fWl+94l~tNnaeqOGK0OAvocKTtu(d+etktF=PnTex!VyA?Y2u{CY}3k z-19(%!>A-pXx+2VS1lK12#mJjSxD8J;>1y+v`Anf=CifrZ{BueGxewizfTyZ)ni+^ z%PxR`XCi3f!hg#3x5Q7ZE_TftZ-gZuH?rg-oWXm<+K>Dnn53a`Cn6DxTU{dl0OM`< z@6t{yiNIGkuuFeP}pXIh?NP6B0xg`lK0uxKz(At&MT%S#4p}(3< zZ@uaNmA79PBk=Olz-yQ|K~3cMix&fTWz39BwGvg?z35t@O4afgWMne}*fZ}jqL?MNLo=#+i_UGaUd!bU`LfbYLds9Kulzgxe4>o~tQ zMoCWDWSEbf$@`xSkQ4r5 zZTv~%PWsJdwa}Jd96;;L4v_ri>+4DPoPNVb4dRt{N z0lWMl(l?jG?>YJ>mERBMt4Gr@2|2nNa?U&2CCPDnsxiTLAm|Y0BBYUPhMw|8!$fqM zo*iYNbnHx|!!*%SKHNUv88UE*2r$b5Hlrf#bbAwrjM+4n5=0@oQFa4xo!9C zV*YQnOA<-Ju$DqIQ(aIp!qlSvPPxTe$PgV2_PR3!v9wH7>{6M?2ubD!PQ!q;hSiC1#v;42f}Ld%&i{nYgD zzR{)>O4%Ldkw*W;RZ86ZF%zcV&x)jrON2HB<_ti^IoVYg&@y@jnH_UUUWy7Ys+84|xY+m2Q-7NCx@eE`^pH zS>qK`au5f)-a-e_IbUTQY=Y_&!8Z^VFTiZp)UYsxa>T$Ax%CUsJP}a#WN2saE~<=_BPQNTjN_DW>EEA+^6<>1TjJ%S zOEyjz20Q;shjjhHgh~C+Ubtp;|H9o(EmhlG+wrHLd4l2o{DQBd+2nA=s@jHH&hA>u z&5>(FqxxyX^z12nIy7)8|BfssC5WzC7Z)=vnz)k>h9jQiVE+)%gqlPmz0B=&ODlPJ z%0Ij*bb8g*=Bm#>?s|Dhg_!`}hJG8JukXzJRE59%C-OCqrN$AgOIhR~lEjyC#6^p` z!JJReewjdIm{rb)%RPM?ufVi9_X5lsZ}2;_YUuKQ!vhZ1h~gW3;-aps(2J)_OYSb6 z%4ezRlPLRj4liYtvV$!)braPLs}fFU8EMXe7pIUV-|REPVoF581PL_LO=3_E?2#CJ{kQk0FPyj zHNH8xE7yPZ)_Y#{=A7o zylIgk^Dd5r($h(X6H==}4u05I{V4P7{^ZpI6Nv7O!6|bG{MJ6UcxWCJp!mb$&U-@L!sw~pucJ90mL zzjy0^Jz0Tdb)rr#VVwb8B-INv`QJlMyNvu~Qi$z*&!4y64Q(ig6;3ZMpZ58Ojj0PH zTdYLtUHPI%$AqV!F(|IO`_%1B!sUJ6T(p1R{kZ5YY2MnRV=hm=B~ncRa=L2S$EH^I zNt%$(xyn}gIf*yKbZZ&Ts0^{&bGzpAkMJs&75=0#Fg3qug^2Tx&u5p+_|z2M5Y6}8 zfHi;d^msr2H(fBW*X?gyPpl9L3O2g)@vNNAk{yTpmsqB&^C}lz^ZaTq~kTn>rLbtfb>k zzq#wb|Elli>cX)dm!`3Wg^qHu$-jn3c?(2}lqd{x8zrzDu$;QKU)g#FLas}5ogI5X z7?ls*UE!|S$pO&RT-rJ((UOX6iiR=Q;p37o9pU5XGw5P^<0!^!{c-YAMDMhi(~$&f zCj>)gX@?Pw;1a<=0W?2Vh^Jp)n@l+v*#E^`u^ZaVSxws{Kc!hYC!U&@}~)QS$8Oi)PG~MKl!e zV;S1#3a0g+9mUnKX^pv^G+XNT6A@`O`)?r@4)*pOGtmo;h zE6`$Pn>0ecTM}^d0jT&~aVK9WO!&g-o|fK8=-4UtkqvZ&p)2C1mGe{v-j9G1sc7gf z7q*_3svU5{V9f3AAtSZV3pW?(5TcvixxLkKtsF`){aqV}^h**SHr6)c17h9Wwt4>S zOTjw^y1Kfmk`53&UME=gd5QoEp!ZhRJ<>?UTbJ`-TN#GH|8cFOg;%7WTp_c3cENjd z^^Q9@gs~dKMBg6oT%WXM)G-D$Vcs7J#rCe<+v+ASR(|wJRzIe$^trR(9^rR^z1SYq zmU5$sEBP)8gDiFIRM-Nx#S)&lJ?ydb3W^CuPXK*B3+NP}AP~~+H|WYr5}fcVU(ohL zRU z*f7sHJ#w$1Wm7re)7w_INvr6li_}21#Jl$M*G=y*`6!%((oM&}-q8k1uk$=LB zRX=KO?M74XbzpC zy3gM!MZrI+gx^Zu3dZXz?7uyIRwFbc5!|<2(;S!aFS7S}Uu#hK)B-kFS51<5$Gs-o z1n-UdS|(&FI4LbHP59Uz z*ONP$I~q9<1$9!NoB;T-sUBhyf@<#acHVlv;ZBPq6<<+z#nr8no*g!8#NvpRt5Kpp zXNk*jp8C$Fn=FmE5q9cL*x@?Xj8pr)_C-AWn$9!MW6<+`k4Z#4vQ2?OflKqF+R>@9 zzSVaX4<3GT#4s8Enf6_9P{i=>*};mQ5IR;!aE2##hyb#*<;`qF`&iSD-@x+v+pIFf zvmKzEIVyYeRi6B_R%hMK_Mvk3oclFL`IcLbq+A3g{J`W&8{Y7pFNF6;clt%vySa>Vg_h`$7eG2W36-W|bAA#Mj4-vkTs< z^P>yb7M6d1yEfeMg_R1)rj!+HMx=AiT|F)6dm`=)-REXo1>t}&p*_H<@e8C)DKdz* zX{vJbL%+99q`<-9VPv=seAah=aDHO}iJdoGeL!XBL7BZa9PxTd^d3 zj0PdASJBO(ND~tO_^QU;?VhTdqK{xIgsd)xy@D;e4G?s%oklgr3=)vV6S`+yikDrtetCV*+LaP#0fj5_ z@1pav5@>4HCK8(3D9~?NHNteW!!7b6({G7g>Mj-att*LF8UlvNjT7H1Ws=9(O^^T? z;x!HE09{@4+FDk84=7g4yl6@`*lho%@jQ4#moOaOpPUsGxhe7|M$r@EthaMll!dpe z;KHj_t5z!l=Xa~1d0&*2m6Z!E5c|c0bUQ{{lUspYYKP+cbX+b5Ds4Rwf@s8uF#IO@ z(vIh!oau(m>>sqxyiwQ&fUUja$QzD&Ij+gBkAoGS^42usW>_rH>b4o?Ltsdd_D3P@ zPVCRc4h@YVVIK0Cyc?^oSpP5IzAe5y`ZUSw{psf>_)o{8e>`k-rnQRxaQT|QyPpd~ zfrGn^u_s@`!a~!*%XD;fO4~iicJ6BABb8^XrNTjNrT!bs3jzjyzl1P{TpXV^pB;aD zhx?$nWT4gNP?NPFe!8ebebJYWD@Q#i($j-U^~mM+@YIAG^Ns^Ky&5?h14X^m=3|*f zhX~FvMD^OFZT;x{s>xO=_vy`!sxXGy0|QVDAz?!Jf?_;;;&M{X1zrGi61Nm!CvEbsFoX2ySobUU696CW1ZJ2u( zXCq<2bmz%+*3S9VfOs}i>v!{47dQ>R&b?^($|iBjO2D8Lb5er0q5-E#9i}h`cDQNs z0V0|;cjhJLz1+Sp(gtOiLmF^Z7~ZTBup;IdK{-%m@v%usS^`Gwe4OJodQ!^xhEBe* z1Bf~c5|6L{B&%Ow<*`>=I#QiOAyTo)kKM82oICXKP}8bn4Q50ITs--HT?9^V%TPTl z@7Exg3E)+q1J%|u9o^^VBvqcmKO*l8Dk%YTLuxZx!aJKCJAPN07;y35Rd6Bv?H>-Y zPMwT+IBMA7d1Y{3GsKz{{!*wBwgtWsi16QBXll}q7O#iV|1Vz2QdbiU!sT-D%fI=Z zd{w^%C6);XEqBK7A?5l32HDffX{+-Fj@@U4LXnRo9ztSIJ@42e3?y`7J`$SI)Z{x? zV7ZWt-+HttsC^%^T75VyTr+Y>SCFoj0Sqg4!EZf%ed*1$M?&N;J*>m812#w0f&k6d z+S=G2zPvLxy7PV$XQQ$9ox3Nag*k|ZB>%l3#86|45=?&BwI2x1?H%TjIu52?;Pum3 zD741XMh3B2anY2E{80;cZo*C4kh%pJ__wiQwHLSGY$=Ye zH=O+azZi4$h9R3TWVX!)T&J`-WnH1b-h3IW7cS<|`2|Z`2-AX$>anYGy|A0{XJ(4v zPW#8X^H|+Sn(UBN6)0!pA7A^A1AS76xBhgR8?%0gVPe!N&U1vkySh|^Zhs=h?$)ge zL=M{`Bb%oOPbFO&F1?cmp>foB-+UPV^7ht7Qw{f>Skv{bD?bARyBCeXa8(1|6HvKQ8L(A;m5<}mkO=hfHF+Mt!_8S^15xr*ivfX;Re`_!Z?JNeC2pG` zKTxF$O4_y^rRGI>gxh!588snrQplB>9&No|QKd1Ha|3 z_DvC7hZ7gqw0C|lf^kx0#8|WmFrd|Kfk_efkn4!G+>P3E6k46g z-|&jg$9XP-xjmhf)39+CmK!+N1#*_3=d&l}+(6DU9exx=APqrwd<>Gr@(8q0!ekkGNF8ov&-VS9w{MiiUx9+1l*+N^XogLsU1oL{h5B@^a1 zj0B2IP-$qLn5)^T4*giLXW{!gni~YpeP59w<|2tD%?wxr(FagYj(qTtUnfM-*tL%) zox{M(d9d6TZ=>H}{?J#Hbo;w%x^wePB+Vx_dlF1d&2=6qz2g_TuRa|iU=Pn3_F$ zn2XQ+PR^k=G4RBj$`2mrQLlPU|D4^?C#>aRQGxDUDu2Q z176qbU)|n%sugUe(IH1A!Tr{BA})To_1P?|AfMJquVBF9yY-QIH)^QgZJzJlYc7Wj z#9D|V?qes8we}ATBp$p8DndLGk=XSF%iZR7_N=yVsqfmLlr+ODB`+cmRc`nzxtPO* zH!8p6Ev+dYN-8Q;G(5>KYVWPSIc;rI zeYR}u8BP+(s+hq=fOngB4G4|8ZUqDmHA}Re30i6ut;5n8a@NCeN(^nS1-*3|7c*N$y*g*c7Zl1k`zX1|P?I>0t4vEz-RSxzzcCbsyyyHvkK-HPc;S7u*L-;c)2|C9?iT|Bd;h&FpU!~ksc#3szk#R%Al2@3vHrEjp2`dR0Rzq8!s^@*0Rejco~ zG{3*;?BeoG7&CNq1M7WlysESD5IGDRBAq^f~P^4d8(xU*E@W-x&v4JX%==^*5i8Q9|+hL>}#u>Gexu{K##nsDu5++~>Uk_||^ZPAT%U;Sju{VZfHy|}39y3yf zsSHo_Y~Bz~RmPeSx0On$a*dbYva+(~1k`A_6SSn(KLVdujt@5^ z+88|#S~sKlG_LB*w>%3Af>27PjzHGF(Mj@EhQii4Q-bjA=Po7_pP_I>1iDi98D1%Y| z%@f(7^`n)T<(8I5?u-TRPP0We4j}hB53{Ka*ZHv~X$G(BRo%N&EnYMD>XVX$S?rcB zNxCpU-t*bK8(tj~`R??#hsjJ!NlO_Y(l1@y_<8v5FfYLlZ8uE!C8km<%+Due_SA1> zhVBKPMscoa;VZ<85B&lk>rTW1edLJ{65%|HA)j>6No>^r_U#){1;=Q~>Uyycqqk_4 zx-0pA5C=r60JGg!m+NXcW&I#4>T{MtV>WF?<;K0?B1V5xB+K2BMN z8kdlilEQc@EcDab%S{i?kDc$1ANW@j%rgA~i*}%&YjT6A^_FBwqu6ybdu9BIi=!Gs z-WOk!VH#uYj}T!3W9_r;v7)9>Hm|gekfuvJf1N_W;8YgnN)*0Fyg1P-h{WBwet_4N z+6Wp^%_^?`ZO=$OEqB974PUd_&PjePaop$`?ZX(LhEXC^L=5~$G;_=&Vujmn8{dEZ z`s`JFF1Qz4L^E`$nw8;YvtSF1FK~Bvx0MWfp>NalS)Vi1CZCq@zU4qV#Tt^xJukKE zMIQ>!{GbdIKa}m!gC6xqNJ=?{=gJ~hx^FlL<*xBBIIco~5&MGib#292 zxy7rAZFAyrp;wb1%$5ytnbl$nBr&&Q4*9*g@=N{#iXpE1PyQw-v|g28d=ehcDBPpv z@HmDnPx#hca$S+XeI||JB^lu}vMMEy$`fd`YxF*X-t&N1=y-OPVb98&E}+uSqTqO2DM;h2|C~qfg4`_2#<`g9V4cnr!&t-W zr>CZxdE8G0A4aT1hwoEVyDwtfy0QEBOrJyV;JU?R+YzAS%&X|8LfdQDcj(=Hx=oN!(uqWKz}7S{{VtL!S_Th|vFG;$ z9CPcgin zf$PPcfQOh$J4NMcB1b{^9Z$(wqQVb90eAOt5Ntl(kz&dd3C*_B zE7H%=Jfk$bI%fHjT2}{=lOYT%wcis zr5cwAYD7}kEnv0trO;bDhuYVcu#q05r3d;C>{x3%Xv&Qr%UgLu-~-VoX_GE^Rc#> zRzPy@`uVA*lw`4*ppR^JzFnrwiNVMP22|`V+xcpv%^4qMAZLh!tTg;6bhUTl- zxnS6;*&em0=8W(_%gr07NDaTDkWKL*3x?iLDzbArh}Y$CCFst7U#ny7pmaH=YPdpG ze`J6v15R7X7!*;fJnmnV#mdNVt{m>Drpq#hW8MB z(DA{|f%r(4Bnn@k1V8wltOvOUg*upW-P_H*-3M}iE)JQCg2970V#4Vtq2#Q!O}pf;*;!>3I*Qf=s+vGk zy}CA#u-rVXw{4eLg&~~yW6<{16q$x)Z8)LeZzSJ!4L?h@fxzh&NJ5IE<@I`x;!3g% zsEoL!i;RQq%8`p2ma|kNcIOQZCz-Ez-RjOTBRTJH6P$h7$!jFzptVO7P0AbJ3Uid+ zFwkZg;v*ZFyV?DY=RHt)c7l&8oV505$WU7j?xEo6?!M#}GE2FRjFdOy(|#gDF9x|^dLHG82+9C4IzdXZ~Ra5j1l>WKYrXC6{l%R_bi2piEB7|b?j zW^9>b!@D7y|6oAqZ~gIsW-9BTrl(oo1(?`7d)$!36=9O<%OUx6xOzm|Sx-(6pEq;; z#0MYDkOA+#%!$|)3FPC?pFca?ZrML2RCvGw>ioI9mv)o?iFo@xK&WD6R>mQGqClaX zsbB#@xbZIq&64qrXlu;mzEb9yeRwf=_#fkH#1#iemkAwY@P!L*;tM>$#FttNDXN=# zDH138GPpLPAWL+$xllxle@4^xntAc4QV3M!keug5hCX3GqN8$O}AQgwfU^gOTf@B0#BYE6X6aKMLy=AMa_z} z2$Nvqof5EoyDCZ+a=1}cRV8IcxKdGG{#sf_hM6Dx@o>R~f0%II47lOJ!K68DCpHGM zKz7p+!qH(!P%zVn?H|KHhPP$eTe`bZqBA=o;NHD-J7%ouG$xx7T3%SA%W~@r1*s9! zBTmg=sqSMObwJ!TS*yX-Ge~&^&YR(d(eZ2M^*5!c-^CF{5ckm&$3)XHG|0t?{gP)x z0{Zc)f*pLY+L|2)&fawz@vg!Aem)^UtTaY1K%bRKV_=f1!Xfz_k@33h@S2fwB_PwB zd%{1z9VxK*8rDwi=Vu@d7KWA7)Lgsm;*!%sT?~oHOww5N!oZ?}77Lv! zs(y)@-|+GzmaBHQ=3wRoPf;U1hRneumJX4c2{+%o@csj7!_uE0`W^f6FDm=~1n{wh zO$3rpOgF?FE`6&~sUY6qOWiLs)08XBDM{UWe21{EXY-|UrhC)kAEw&Dv)U7tzMRjT zhiOUvU49z&PuttuRV_)&5XQu^xO`4DouU^NX{Mig?`Lu%CE(BaBceQ6FFw$|T`vBd z>PY0L{#GS^q?9k-m!XX|3r)KpsI7i~B=jKG{Qk+kTHx=A;!X!fBgp3+qY+BWiMZ>j zcOgU{c8!m>h3#IyQ*gLxcttp(_Eo_cg4}pp*P80L=v(w zO2^Vf-_Q~=^L*m7`LzTY%scI&Nrqdh>gst`)qX#-sUo61dzY6z+ zwu-lzPzbnlXH>%Elp{OCBkE}(_qx;nFd;K>WnEBJs5CE9;>5}Q;Gb|+Oswe`TtbMy zMnF`&9ubD}0c9sWekx~=KIAjT? z?ve`Vu5HG_JA37qpiUOSN1XPu=~c$5WI>NEH}R)&@UFxELU-?y^fyT&2du*G8pe>% zI=p{hQ_1L zUKArtG+X&nwI{238^ibyLl*pl#O%-BFTWM+oRP@3#0=i?SC{&@cj`3+4{Q9*iRD}S zht-e~=dU%Gf5k$SW^{Yx?ezzt`cGS$w15*xh;8AWv;ooBo2~i!3Sz{~TWIv}nJG<$ z_kGFOvMJ&#EeG{LclRG_m?TdRkCQvejDJJz^s3Cr{|1J=r+T$wNMbaIjOxd+-SV64^tb|YD=AkfF=Q|b+wk72(Yez&k~uVn@&Uv9C` z^%BOW@PbA8ew+ubM&<&@e22#RJdTwtnwgo|)7#rynazgOoZ%e7)?V-op6J|s3TqEM z!dB3%2Un54=0RoN(`F{>Yg{1cFi$M|c29YU2H_o_n!>B=);JikAfA&x$IItfa2L_tt&QSYnv$0A7-?uzdc~P*C>aeUibCZgm(cKj0h+_Ulb6VaaP0NX(KM4?jS!uo{eUn*IVNidt@j= zmXwAV^FI|Nvc1z?W;ns^x7$qO*?@&UPqPOnVP2u9M zUcFi=U)kvg?ny&y!&W`XA`ISj=7&JMQt8%`M~ebESqjOGD%*$ihm_1L=>9=^b;881 z;@yL_3;4WE=N=g(i;=EQ9L);y^tGAIbZUkb0w}P6h1d}!pG;R84@?oqz`$X=rgqi= zlMjFo-1!AlAW$`Q8#>31uWq?{!e!MypHSiI?d^~_$}(O8J@z?}xJ^$_e|k!dy~?XJ znUw7Bg7hBoy{i-$vvtp4IVjKjidUK&BbrgZyFA^y7Wsm| z#!PENMdz`Vmc>RKs4 ztJ{WC^PNg*DPaTwJtMi<{Y!`R=7$I#XG}^_#(~`I6GX|Z>b{R{-btupoy4`?9NsXv zu7u?33{`-^>VaU}Uy0N`#5FXP$0vHr+1V5pmp5`)h`Z1H=YGfbls-uO@nvxE4McFv zSL%NpP)?vmW^AMA{uPPl#(5KMM&?~hF?9lzPSr6A%7=F4F;82&5Tp1jMmTusClq5| zRs27+1&OEY<=u3$WZGCIU>2n3ctswD4XTH@#ou#tmmx(<7QyMK>pWGm(5m{p1|ugh zbPOlndQ!V8>|gu^OI3tfTz^Y;{`m1DKeS%S04<>Ze!kxD(wVuo0w!9b?H>`=AwMGd zkb;2aub63c_~v8P)$cV-Qdk0-z94Ww?Ct@zttFix7w4gxmvdtTnWc44tE0x5gG>+6 zC>ZMvE!_)oQAQQYt;%cRhLr};LKeBq8E1IJkIgM%9pSt0dj@<$hN;1!p*(1T(q$<) z9B&V0k~P&05QyW{SD&wIJAMxL*IiQAyCWvhFN(R8eJ$$>bqv%;jN!H&PW!R?z2~O& z5hK(r+Ip4&gCNCHGfC1)W%&w*n}ktSZ&A1Y&9yJz&P?xxSnJyD#6{kDgQ>K zxAF538iv!>f@#pz(Xo_~-*72kPGDec|1vTlPs!PEJ}Lq}mIOX`$@A;xlE~c?jSBtRlS91|R>NAO7|&fld@5Tfzxr zo_qduRm67CmZqZi|0*`9#0b`j;;W&l5W=F48g?YN@rK~+rmI}~P>ixVJSLc|-4DNa zD^qORNFVhGh?Sw2FeQN~jJUsymliE;-dh{Y=o3SpTZs^M$^caw)bkpHLC_b%c$*(w zPgU71&G(*=K4mlkFe0Y++xvO0RgOmwK&!f{`Z|9rt0i| z=gOpcovcg>uZbHCp`2}?xzML-s4^R($zVQ-Q?+z6FX4>qiAk_1;y!P|t2C~e^4Wrp zQxcFZ@iR2Ht{X5%D8G#j@_g35 zbuI7L!21*6xfbQN7}%*tW+fdzEDz5}4WEV61psDI`xw$W$A_t6v$vOF{62JekPlbN zI!TZODZH30zOc#NjQ?t;xkjs=I5szgmK6b~?s868-&&0lE+OjXR9**?2kGd6LaWwo z*IpL{`wfqMjXHRc3PTP;pI+K*NCA9yYe-kONTRxcTz27$)q*IHG z=@pq9hBHt9{Yw$fN`*joWeARFwX4*(nRbi;L^}Stz;1;uD#tK+rv%k+!j)!T$Ld&K zGyS^qjK+T~gb~VV7W<0YkA+}FN0i&v&11{=3Mcm=SuC^8d?j zsNzuWleUd_4Ce{7u1H(#iuZ&S6x@j#zc}<9!Kp!_R~JEM z%a%hoK5Not35`Z9r73_0*XBSWzm)nQe}MBp*9~@MQWA6~H-#5;ChdPZQ?^Hx-g!JP zy{;Zw#?ki^tz@4~mKVrCi)q!}moHz2P;xKZ>fWVTWeeg0hI}2SYSz>-JX@9umxTY=R`~A#y$E5%4QvW+aE3!n-D@c#r_(%Q6!?K} z)H$2m1kKHj7+-QN9m>kgZGaE2%!{j!Xc}AdJ63!W5wF{3u8rDoY(I#>`74zmOL6^6 z;1&Djan$rKxnV6gyOxAtwdP$GL6RVZKm*aNEE)!$>Nxk^vGv*_Hi`wAZh<!S zpN8F!VMV(&(mc++1`HG2xmvucu*s=IX0OwECbBB}cK8AV25@_gZ?#cWW@6%1=sqJk z@0!f>$NUIl3sOPoK>J0{vaKtK=~*vfvV0%4U7`jR!Kc+?PgV+r&A1}@d}cBWj+(*c zs>~#Gp@0=e59eJ|bKy3=faTcAs74JJ>ONfQ3YS0eGMT-haG6}8At-l>KiCa=>KbJ9 z&ZGs1OF5-{zS7a&{zM8F0v51`^RqU?ht-M(D^4_Ys7kLJ0&$$j5JD|zT43~{cO13r zkv~#Oy!%q!=wv7lAiR+`nPZE23X~lmBa~=YQ9bfGZo(cnM!d41FkClT!z-;w1Aj>CrEl+9;=u#4&ae>RL-mTw34zzG8n2vmRm=tRt$KhE%Qob-LK%if8$zd{3zU~EWueuor`p%&mQKEo z+S$tWDczx73Bma4Qy=nNal zOMoaQ+1(Pw_VsvEsArz9cn5?zZeqWJ6i({eO998Z1(tYz;0uB|Dq@Rjtn!=)UJ$Ze zL5_*ys;b|Da=35?mr9lCS=dNAONIrAkdCXw9tr`xHB?}4v&+wpxE+(hozE_S9a|3; zo=nK^q^OY|ZpIqxg9ld9&{#QVMug@*$@H`AF(Y@WT0d=wGhgPI+Jmde(42r!A(QC= zeo7NH#AxjmCN|u;FRv#hHSAK1CAJSyS-udn>dJwpRl~!_W8g96 zqvb7n9bv&->jiBZQXYT%tNYkMYJy%`z}_zL5L-NvmaGO833zP+)cer1#HbJ}KjzHN zqRrr_S)dMF2C0bNd9_rhJcX)dK{`_8{?UjvI}G;h&=v1M0r<+WG}Z_NM^GU5LVK$1 z`J|#GmDqNG~bX~h}e&`|*F>>*d!R&r;`L(ermr%hW&7H9Pob8`|p)7zYP852y^0Jhw z6>B5Y=d>}g85jkG5!{rtsmc9thRX2MA>X0XbrVtkH9nrMtOjIHWbTYMt6nGaRt0PJ zl538+^a{hdmbvp^xNnd7YF$p>exb(hTZCXwS3~v?BnDj%D9uClCR9#NF5NJTg1I5* znaOw7Xu-VZ(B+)LUlbA&vQ~mARg<^TgMj){&78Br z%Yor_R^dGvrh#|x_ zm)(GnaJmno9UTFJx0>Rj>}Z571P4&_@b>yQlHuLD^H%{5>>4;*+SUtkgAjo@!+Bps1q-t8Vf zCk3Xwc2G%8EopAf%F^N|mr(Oixdrx`a5Iv>+MqZL97eZ!nh^Z+q z{^9UJ;OxVgb$O%)R+=r16ejk;N$&~@{+pe40zE{FaYSlYmGo+x>;sO$(;B>xx9s&W zAH(FwSzLDvMT{;(@Q}=`-@VDUYt`>41toYbd~evKnuEw?fojB)V=94onwJWrkQPsu zVb<-gTU``fBVK34IS>JfhyXwT=RSS1PW6#@Uk`gJv>9MXKRLd`?mIPI(thaYEjx+4 zd+H-Qkd&y@YA)pFh*;SwC+&5S6!-jz5PSgLPiDdypu$kZ+H}naDkca0R=k%M){3UL zwnK%`hb_(hUD()a!RjSntc6UU7k2)S-{R(Ao&Hr0>i1QSPEM_6e{{<5#8*chrs$FU z=WCESQAmjM86J%cJmHJLa=bWfF3dIUJ>?MW>F0MV-9diQsQ>7kYh4BMuC24Pb2FE{ zRvFrj!B%x#u{~Sppo^x=qk;Asuy0 zHUy`kncEk)4p#4nK6iwyWjJ7Bbo`!v?1PuJpJQ;{@ksu z{=-5?dQ%vfR!x(uJuO;4TA-}1esUWj8!sQczEqf}p{RJg3mG_5ePA|4+R*I8e|8nc zjA_?1ulX{~KSyEMF@RxF>JWfFq>RFe)_xf$+=#B@eT0%!|NCDxd*1TV%;^5w9+$g5 zKFT|McRINU-(Y?K3c|{Ul#mZ4?AG{f!oE1Yyx-Q=_IlWJg2_~9u>u=>mZbL1l1Qzc za5FE7tTX2doo5@{4CnO;ytS5Rt3>Rh3d5L?cb6TVY8@qm$!K7694UD=7gqFX?!1%$ zGL7!RLn~QXLB3a_Ub9wj45X13cSYEHd$sq8sTtc{LxD5cy#BXjx@su2Nz;A{KsM8ZDQNDqApobpced9v7>{0kV$6GDwY%Xq!G`dJOW?MXyZ6jSR6odyAXG2aWl|106g zczSs$UfaQ4PG?vF7c*%vmZ9metc`|G=MZJvlL?n#+_E;E=~2v2z%i-1FeK{4(_uZ$ zO3|CuG^x}DPyBQt7l0vc7Pf9~-PC)s&<*GE(WKy^RsE=t_Opt0X@Of+>uChgKSKK_ z;&G??T=rBaW6pE8*^uek&CLP18xr6wY1s%$i+lCDBtvGp(iHt$AspslikV}a zcj|SIxna;J0BJmcC7)U>j02h2R{YSBbM$^(mT(b`-E^0ncBY* zWi$jhVP&Wky#Rp&hLZRPAf!u_G-UcTNJfO}K%j-%7w_-UD4gMLM4?*vk(W*HUOOq}i@tt)ddKFIqj2r``<>4W}oYf=b zJ;XWEhhp9n6L$aBjkJXQ_1+HC*h)G&z(kUVT@o-aLpi0fbwIhT;#U|#c8~89eJV(b zsWds!xBn>%G6*ctPkQ(<5VDPCgbUC*V*@eH5Ff^NA8MRGxQ^XAy?nRER=H1p$>>|M z&#rxIS#-$(`BuFJ!Z1+?hn98H!BnCtNb4t{=`Lsy@siA0<*YbKt4egiQ@ zzbce18@cn?Yjyrl>_tTr&}qxC0$XvmY4N!WURQt5&N_NABbVfN1os}ZdFS{)F2E03 zy|c_J_MH@cgyfrONw|xK=a60&>|JYD4D)i6v8u#>(%6S6>ju&gd{APN9E12O#1Vpw zZ(bM$>+Y=kE{(40vmOAcxcrGI>(rDhl={Y)`kWLSLd5;&@f6k6Z4KOX!~~|4)8Q}aJS_lzW1x2FKjmXc`)OKRh!Z0twc=go4z<4r zs0?Bn!6!PvQ|HH8+qgT8SpN9c3F%moE<(Q0E(O=CZ+AJ%%txqrT9J)@J6;5zn`lEr zvVv@ya&hPH%&vXVc0xh;UHbqWwm(LY_>@NH3KhkEh6>|h`5MYLLOKhYFxg-X^TA2$ z*)8lbhk1ywC%RJ=@jTk5%z5vbjy4(A?unnK{x~~7M1nxgOM#g2A~Rn}+jm<24^u>=4Rp zT&PL8gktUW;4wy$ZW_cF+xGrLzL&k0u}p^;r2bnW!ND~2F`VoK_h9U+aEPE#V6FEM zb101(*_|PRU<>=8ZN3N%@u7r^*YN5#3?(Slxe^#2+Pay)fBnLHof^C8A|F@+r4&Iy zr1HTQ-^lyB%diROV<`sLq!h(6C%y6XYdv>AeXTOoaFpZd5$rR!C&-d8IJibMGCx_p zP7!|jc9-Nnz7}|ywc6`D8bcJsPK899W&XFsZUme=;) zW!_B41Ks_35VJJwJeS`2IEJlM8mDsSw|%F8nBnRhUMtgPoXYWCdn_?I*%W1mK#f98 z6)8;}8ylO3+OR9e^Qm=B)%^4yAoD$LhH9yjwYc8(uhl(+9qCW~Y* zWc0#=qa`U=VO8s;I=jhZ6uWkM1)oTZOG@tynW(uJ?IZhdkQVn}MG7F1Z)XjxMz}Ze zQ1kb^uXA?rC&;iiHNo@goOtcb_^%7SVUS2P(j)(*oTYVkIS~&_Xi58x$G~|C(jsJ@ z`cZN{RhAIyB1-5**mQ6vs$aZ~x7PjyzK$5ZrV72!i0NUc_FT0h+6Q81Zp+A5Q`TYA zRC`Ba%gw=@R*3l)T$8M?0SCw%pRo;4!GF38FAJvAyOE!kBGhU;Ud2Hclq>%MP5M-2`KLrvQ8XI=aAX~T}1H|mOi6(B>e9sFd0b?VABNR2e^ z*1ye)5YGTBk$i@m0QI>wS(4;p_-r##q~i(zlkTW14b{m^OiA$_ku=~5++~Y^a|2>_ zglJ`uGVi8UeqRnuYKu-?{=+id-wzQbL5t(FoglrW$W$82x?@Xy+N-@ z1@1m4zZy*{UV+iVHiwPLJYX{d9O&e0)}1xm-AH|?SC|zXFrm1ah;{1Dy-;NOp!|%K zjJz6xKMG-)W&sDT#L~*jwEO3~nR?uHP&aMfeUs+bEG^w3n9C~L+S;0K2#g1;Arb0kiYh8{76?|WogRFmkC}$-T+*CQj$R^O^~i+VvKB{M8`Y5Z6EbO;jTUOL0vw$vtf<~GO18+N3c4* z1?H`G1_{Mdx&4RV$rh_1cx5!1NDUx3tEj2{US}(yB1)CSp|_V#{lTk7wgV^+ZD0Xs zwoCFDC01VH4EDQo=epU*J`OJD+vk%UJeU#BV+Q3N9UT$M_wKKqL|i!`l>EBKi+a%! zAvyyw9_lVhfstDQHMBc>kwf;QZnNBkb>Kyktv~RY7jNG7U+s_qa3}Bz%Kce@A~q?y z@Ksy?NvEssT&AZ%Rd1t?fZmj9D(|W@3{Tyr5^vvpnFKjE`qZPaK#tnlLc&SaO%vUP2e29IjLt;YO(xknVUNsMHKNLS!}8jX!9A@Kli#QtBIAmAZwh6EINd^N)b! zoNO*5`gO0$itk0tWHx$`CBX1Vqh0jXP+2Eq9{aAgb^m9rHU~z))spv`kJ-IRke7)O zL4Uf-I@$KNv$Nwv7V885SV8yR^|i5~3%Yeg2z-d8qk zaS8A=`PIuX2`pponFSYa3!E%I2)xup#OMPB;}2VPW7A`S8VPj4L*IOEo&(LtzsPkj znrQ1+aI!Xwg@@~({{H<#k*x%*;<$I2C?xujSK%>aRoe-?fxVPykcZ`<>Et_*A2kB)r<>Kcpn|O+81e`8H91)h}*O z4}3k9ty^vW80)G;2O2yr7#gYQ#(DVkI@=y7uhjp~<~OuG>4_)^>6_SMm)=@)#+=Og zJoL7vC>04EA1)t%?lo{;*BMEQphiIZz$HL2ld(MQ(x#e;)`xpjEOX3y=)NsT6W^v6 zb*W0rcS5sW6f$&ZIQ*A^=HSiqiDggN4j^VV_YLn=X)3&nfCG+LLbO$b8jkm$wt8XV z#CeTtZr2dA6wu2xA|GKLRs6#r_`EL%u5Rb|q{!@Kq7``_^!rPgGuj&@7RwHVqBn&C z{=Xzwc2XKVoNk9+spJ8$bqQ?7AR^N>*=x&^6+O#o-xdO;hBaX7DVBAjaVTvGv~ZRJZZ}_}j=x zD3x-EN>&NwAe)qojA&3<$v7l3;@B(h`_{=`hm7o!WF&i)l5uR2RdLA9vFG>ouKV-t z{`|hb^H=}malNnWb-mW}`FdUgo&-JGe3uVnV;R1ffAV`T+^{(Ey1abKXt&fns3_ck zq)GS6DfrNor0G5-SxBjn7_*&y-ar=d&HH%dO;=ReC$7WD>=3XFJO`PT#3AjrPQ^an zQFG(9pEn&>UuxW0do!8rZ8Qj$isA_=&&jrj!6(834K7GqSR1-vWcaH!hebK@ESJNQ#it`mrF&7t?+p+_ViPuuJmypc|1qP(+ zMimLV=6&$TkS53bRNm;d6+!L1;rj!t;7(2 zICcLoU;5esO|0Br=EfI3Y{lI4vQ(vath!DWZcuN|GDILe7A{ew>O&Lg}kMa{*paRr$m8cpYTQd;5yr+5^6dbL6Tf5 zwNKS1j~@#A&Oa&0Z0(n^y|)xS+bFJMcUnRcnV|mN>hhgc*B3wZ*6^THh$X~NDEBdh z$JB%<)wjP2D9ysWDfU`(;BiFf0r|zBek5k0{O8jYZFC4mN&Kz?BNm-%NQr{#+FU)0 z5pw=1$Gq1smcHf%U8k-w(zG@|A4&D=*pEDbY`R;3v{Md)GWwwvG!R60xIPPlNIYl_ zf5dp8yD2pjd-JA^P0mXyr5kAe*(03MJB9jVI(Tt{wmn~iTH)8pZ3+^&JKpgS`6<$Z z%K6&=cC?Jb=uSgXWn<;Qq_{aBAWvh1MmTKf7RH(dU9b8w&yNBiZNtCkJEhJ@WaV+N z1b0JUUy%wiNPShcu3>OiNd>(9MA*s(XE&Pa{Yp=b?d#L*Q|V=Oe!+&Q=~APd7}JXd zAGB3xY9)rO?lCMo*N#us7<&QxVtFJimja>5u5>h0U_ z$6<9eM>64zS1(qDkXx|?cm?cZSsr-KrYGRAgfLkh*K-oa<~Cq#QatDuTF5*Hz~*B% zGx`blY=(91!GHN)a~LLw=rz&7#2S7Zx^}IEW;7MQx*HJ`ow9ee{5937bP3!PmX&>V zKH20t6bG9eSkOcRivD)>PC3dIr~!rwVi1|zSSU%t$lJOQg(M2PKIw^T&g?d+m{Nhljsn5rxNx&7S_GDs^|ZvTeyr0UW(CGsemu))@hZ3lNZu# z5tO6&P3XLqv@0!=d!E_`=t+-HKYbPht(oP8u_hZV#^t^<8$aMANJ{Qp)rPhxt#1$- z#U23d!+7ggx9CRlLoY$h_aV#b_4*Z`m)prZb2~4=i!g!WD&kB02+Ll|?JImp!X#O$ z*$JcTM^c)wof)(swo=AdD_)vQ_u}}0GDEBmjjz;{O&UE>Xz9+ByheWB)RSDsm-Ipa zn%G4(WUui9%7d?RU}B^k>{okg?aYkKICy){s-I{6wf=vcn4GnGDsB3pWdjYbr_(RO zM;Zc&G@CoqgCy;{hTfv<+4o(`Z&gVa!Q7`it&8Pypa!e_F^c;HRl2+T3AV<1UaF4L z`cwV_N8P9+1dt@(e-6Dp5cxfRzbZHTW9E487*6TVuVymx8c1R~W~_mlQEJm@Z2XH*o956aKY<6}v(@-tC44Q%e@5HeKhkmCIVYWlXewgKJOrU*!} zTH1+(#`r8Q+JT`V$%Z2fN@||8QkNO2RM_g z%+1XmAj$5s{$-#%ct!mt|6d&Q=14E5t`l?Rt)@uLceXnQ35&iC+=r1*`PKVU6ruK; zuQaLN^kfs9KlH&zarNFsK~v6Ana#eTVV3Qw@*RoQCeaJ5lUI3MhbI_ z_!5$A6;V@mR3@D+2K^AErlf@hE0ZDwcWpAF9*5Bq*CDe58L0toML(30N$Cx$tj?jC z-BI5L^SDjC*DPjKl#anQJ7gH3(=rXEl*A(|4gIv{(;e8o{Yv2A7DI2c7ol$0bZGik z3OmU6kjkADkomXJ#kF{P&LbXLl(*(ZseET|8XCq=3lzc!@t_|h?(=4+BrC2|--j!=>rb~;g0Xq+Oc38IWEUpBYz_-hoL!#G)GhJcbS$c2!Yp35^>1^O(foA zHYD!Ka+%dj!%D{AN1|M9u+@T^k3h`>Kkw2v>vj$`|LZJTh5@ohBpPLXhPL!+)J`9j zpdwFg)iiWf=z;vA_hlM3jr`YpQ@A11>b9H(7PtzA-ByVeAz5i_eZhrkGU6C6$ycpU zp1P6HFd#J%Fu=NB$wpxpNuVhF4a$$+%81!DGY^Kh@dWWL)fhBviLXNAXkp_}QZ8dJ z|JKnbp`l(j$VfbR@kJOhMihQzQ9bde{q)Ig`Tkd>Ad6pMnCH|UO-N4>1KO$3#X)S*nOc3M zh5|ChCokFg`1k1-TS4E<>9s7qOkDMrBM07Q?PNsu-kxv zJB$na(h;Pkdtk!vVP`Ip&GDSk;9G8L6HvJb5Gkv!PVN%d(bm4L_-k|doxWhp=#X{M zjV!}ss&Fmn}tHEl}z@E-5Nr5ll94a4_#T$r{ckTxwe zL4_yeU9y+%CN3P|N2gy^qQZ+Y{riNr;zw!S4qa=Dn9?E~;;~j?;6?gDEA8M;_Z_cT^Db;b= z0`qan(jTM9v-zWGM`4W_zE!tFv=@Ry;a2t2&3Dz_Yxn0xO}0BnSE`>3UlZ?1ez|F> z7rfrtW%g#reLkdnxM;*=7c!WMHn$-G^O5$Hdd3arW3Z{93>=~r%gSJw^&PjWCG_HNzK zm=(x*Ex+tT@^t*aX2#M!qfx>GsNTP|S;t8=G7dQe|J-n& zIQ1`cP07Z7+L=kHj^Erv=d-GyG-4vM{b1f+YG)v!R$ED_j%s@}WSGeYAQB|z_|ji1 zwrrg;&DB0jqyV3_g~5@8V8vx}-KNd=l>o)T?Ncs?k+}V}1Z;6Z!9p6{b!1V8NKc;U z5cYQnUDn`Tha3(KDF3`DZKs)`c-OmGpZ^^YQ7dlWbC9_2m){NaRu`N=t{XHd$Nf2};$Gl-jY5*qm9L+j(1Vza_D8Z%it3U2{8zR$?1e zs(3cYSjiplZ0VTg1v8r6~KwNdZHCEb_jslyVsqUjWHj?8?kHm%cbIP}g;AVI6 zWpnn{dj;r$1OKP_3(y5_i^jDN4GrBLWb8j~W{t`S=$!wMo11&NCz`^2hS@5SPU2zT z`uh5))qJ>XIxgRQ=zUf-QUap}y}}8$rPTEV5OFBe%^ByEd3sqrC^`2H6d)v;vz~z6}NuVWr$E;Ag zmNWgHNJ&WzfO7)sb^?%LSd+TOX|`N-YS zCtYXZb`vI~R03(%=^+ePdEN4IGq)MXmz4C-DruqDf`W{jB9oLpDYW9AVo~DM3ZU7Z zdEvvoIli?Mzd46{`(4<3>HQpive%)ybIg`po`9q4f-OG}_Q?~)l z9R-#+mH-fmrOk)_#M_ftcvO0+&{7v!yo3<|+@S24@Vlj@Vt$U(PB1nMYL+fD^$5cB zz}s8oBQc}tm^pKmwGG^UN9(6D>3w-AoJ1HMJHUPUMMk45u>hw=SE#;!XIclYOu}Uq z&cQFKMhnexLE=5!9R}xz3KztgU9PSKBzL+qlRqSKt{+?4Xl~3ho1EKm*76rnSd3gN zC_TGK>hCA!KXeCzzu7xP%!eF4tYK#lao6GD!=4}?7Nl8gIcIb_gyM;GyDtgk$s=|W zb|bIC713|Lv#A?=C6vndrnRpIV_h_|e#&WADnVF-MUr@FHn20$oUDLG!)x=CnXP7D}q#)B= zHd?j5Q|!a0TNb@(lAg|)@u1nypFgiEB%X2lh*L68mRpvxc#>HBYVH-w;{R{~audb{ zC{`L;x$~Dc~bA1o!wzy zy@gyLiDQ%6^rUGPyxq$BTS_u*58_SmCge}?vN|+Jx(}pq4t$vi$-R;E^l51llCR{? z=@aHUTCxAd3RRl`xYEcKyCq(P4t(41<)>&f-YG3FcUr=|Q@!B34bKUYcMC_+5N6Ox zt{4HzJa#Ly(7@BT&OX>h!3)`&ydXi?`MF-^Sv@GV|8X ztk}mU(@AG4#O?jCIWRELr67kitL5)0YQoCNJN?{z^JK-72UFPmRDXlG{bm@`|6%Ki zf*Jv35){+=?$R{%8Cq%0C;K`HHz3#Q8ckr_+6sIJfVL*D`2ui+ zq>l^qTy1YWRfY6aAYqznA{M-$dL(^!(^3$8mBPg_hs3NXG56t4 z2KPN$>*m#B>PTlG8r-^-(8Zg28pPWLR-`D$;|8G#c_LfIn%9m3qQ$j9VVI(3f|OE% z(WbdV<7HvOLQ)JyXF1;3*tkc5Yca0JRyRgkN_ZHX{a~F}&g+ccjn+;4dRo<i594@J zg0X{E?SaZA!7H>ARe?#({j|~qW3Hs2G0mEbL}J}DFH|fcu>n*v_?Zo}RqEoJd2lEF zL>Q_4BtLpQpbmmm<~D0w_}WoBJ|#Nxsjp)3m$d3Jk5-m7&s#4H@xKdK=x}HLq;50@ zSflCzU~HIw-za_C&^a^j9cE5$w`t8cC>6E55B%kEwN_hqZ>5qkJXZ6i+zO&R(#A43 zRGF}LPkVUGIJ)u}H5Zk2;q(=qIckWV3h2<+?_ZbNQD|d7q;69r#uV&fIr~NhLt6=g zmCI(nJ*c|gW@je-`;I7!{$?}d;13-Yg7nXM3GcqaCvEU(nVEW5ZLiz5v>0X&jGI)t zTeRV(7Z;t;5{1UlubEzPXOq$&>_-ige)MR2xxhJxkfj`Ac}GmvNx5IccTY3K$JP}; zLYMMiyk6$4!cpPUuFI#hU)0<=aZY}?^vCUiA^+v3vYX^K!&s@%pf%0P<5LQ4*IKvE zd%ysyR;z-R&)Gwyr0uxvVGT{KhZr@v!FokEd;98*UCp8RvG0zdpAPfYw67gU`j0v- zPn$J|R_#LM{Gc-W{mX8*O7DCW5)5KJhy`i#p&x=@1Nq)9fB^G?_vJTui`oT;MyHNAfrAdVJ)(;>+JZlubMwNG}IS*gXM#891DcBIaO+YAGp;d=jA|YF3`5WOjwE7sV(`8?tJM4{g4k z>p0kRC*|?9D{!t=_mh=Gm+q_;&X>tf>mcY9OoKk?|V;c(VH3<^dyv)uS+8+JLUI>`u3lD zcaafD!jfXti)t+A!JPZ~EWV;U#QZe&GL3}6;~!@Eo0mCdyZBqUGZklL=g(2~3hQBJ zZK>7uQSUo0=Q1y%&WQ7lZE(ongJX;F=xb>q{0!y*($5|Mg)oXQM-^CGv{de}eHNVd z9gm|s_nfos;beT?9IU7nd%PFWN@yovVb zAXOu2HQZKa2h&q!k-=CR-O?=Eh1zEswYnI|0y$ifJ2Y;0*Kk$%92O>YRVZ%7(R`ji z#~ff7g*~vgNg)xW2tfE?0Y0T)?mMvm>l=G@ef#$|MKomLO>XQD?gNBEXR@j7dQKy_ z&oTwE0NUJf?&P^}2VXBP3!u0a;^7tu7*F_0V#+B}48l7>O*M$RgsjMw6 zEG!n}^){ZhoUPt#gOFlPmppwczUbeqqt^?#{8yU9(f$i5OwHPa!=K`2#Wft53RXW%YMQ;hlAl zb#pDolpRS)N&ebNylCqjuC2_;84(6)n1AnOwmOUU=B#VAQw#0{eXQd=o$u`aw%P&) zf8#tZ%jRS3^f{@exc$Xg@rQ!BdyYMs55Q`YR;b8A%u^`;=nTPYYpFLfU}4b&Q_l_A zXa|-*op@1`W)VEbcNTGtwDgL!GS^N^YWJ^rwvP$YdfpYCg$$iF@ju<}RT#>&yfKqp z=2CXT(^;@2ZK=+4b;@5`d%zRKDyyjI>XV_LYjR#%nv!Ux+FFsu4VTS|Ls>U82xnP(daaL3G6vvgUNb^Sq>L&?0S0Ni7X<1< z(;v9k=p&bO3bQD5#_JKa%niXdH?++n%)xC*Yl zgulQ?56@+9izl>AVGU({qNZEH7X5vF@wqD0I##58%_6ot@je2H>TNVyW8QMi7hQFN zcsACjUUk3z@wg%q;&Yj4n{R%@`;VJWn4|Z%)n6tC-0hqGl95B9P#VV>5MLhr64Up8 zvovH$ZG;LNoymzfo3I!%*xfbk@cnB{jYcpy-=d8xY`(Iq zt@is_*5o{=)z+{thT@qMbv`ujj}_|+Nc(wfuZ4< zo~un&yULDB%rvuh6@C42_JjJCSgK#=h`!VG*hh*ag+yxwJvgF|9zm{T=7*J`o=Jgy zEI(Y{#Bn-gl>uOb-#^`OtcgEAT-V1uN*~6;CL#N8`q+`)j)cUzQVn~#5mypqN6XXv zZkDGrdgN&mFWY|8WM(_ddpi{Wv+o(mlunsF{Tr(PxDu?U$I@hUP?lI)*9SdLeNq+^ zn{JBwF;4@V3C6j-pVhuFI^5;JW6I)Y zCqcwF6tJRyKM7tSVju|NJ~hBoh|H0k*68I8$?*3IE0k zHmWDW5gWX6S}>c=S@(S?IpCI*c|7feuDFC<+0hL|R8-XHl-oP(Uaxg+qBP4)lx?K# z-EDQ|hVMT|3t2)es};5ICU6Mn{u}}$%EIHN^1c6Mc(}JBDnr99MWr(&_Q9cVEIcKp zrPAjV6_dH1wH9`GJ-?Ny<=L6S+sn2-@$K6kws(6nayaz;$L)=df&AW@FwS}7&$aPn+(P7chD#qJBrJ*$jh-bU(_O| z@>lHB*gvfUWOxMutwuPHbGR%^@?GSF2fp1KjfiF&{n|CQAkWoc{QY9)T(;Cms$mZLZ?j4aY zI;G}(k(|NEg;W{&w8#&*(|g5Q&a5SlQ<6yXQAhK+6ztA%u>i}6z_uA!qgpn-gCfEk2kp6w8KPr=G~4>PcHFms5|`tOa9I% z-VcqTX}$}Zf9Uo0%{A!OkFUQ`G;}YAm~q!?Cz8!2(@re zgf!2k9)L;7w&qlk_!}zv5V0D<8ag(Gr($e|#mc+XK%T5?=0{Yn7m!lRtgoI@#xQn$ zbfsE7W=%$8qLPgpi|vQ`2aI-}Zf;`duiD$$**U;`m?7tDZq;N9<1OeNG*-xX%%p^{ z(ON#y;k3}w7nIrD`}y2U`&-GmH}bremB&3V4V9)e6%13e*LwR9J77rQ|M?P5CqUDg z%~@DjZcE%W16`s=24S8np--MXU%mC!+%e1t^En%j(Vvs@P}{$E;u-WnT@?ON+n?^4 zlp-YYz3f*92FALol=b6T$DKx#XbaV~PNdT0ge%3S|I8?6wTT{i3;>FHSWr;Uyc6U2 zN2l|J_DL$)Or8eIt~sT;JL<4>leo~j=YQQ5YH1}Vt%!$5Dw(zmEK^RJgiN#YQdiqdhR}q(ofFnV!#t}8R<5m-^6;NG_b_lSY02}4TI51TfeNpq1*ZqbgT~DQSNyAc_8^FNS0`^ zD3p)SAHXdGRDXtXJut7)>JYKxqn}nw#}cg8 z{Iyy0Ld*4atbrN&gN*ixU5c{gRe7)VWhX=QxVrvs!S_kZ!bdmAR zy`*To)lgGG9<$NH^31K?kWxp>z|1Z=s{h|l;RL<;5g_t(Ka=Rtja_R>R2=rnR>#P} zw2C%S1#go2H`j$0FD&!x)KlQka-dq-gKOr>73m=g)_MhuYnXRd6z1UTr`Z%njzr{X zI6SpNisK}U*$>lLWSh?moev)Q0E>rlsh8^6kGBZ(Zv#cgHVR5gtct9kPOsepD!-XE zVR~GuI#UvA{?3q8e73bbX%Nt+d?YAZ%7xUgm<5uj)Maa*LmC{0G9KUI+liy-%r zBc*yps>H!cJf&fmq3>e~kiF)1wByk7g3IAyXlm5b(vtetiR9B0Af?OH`*%FL8G{%@EImf(pJ@mGx}}NlAtZ7mfCNhiwJb zb>y^w?@qUjkZ@HtnRi~R0;Ph+6@g)1YbJ_E{|QP6+9C?HxhoGB!&5J$sb3)~jB*Up z`AE_sXFvI^X3f@S=~Ei}@iD|SDnt%ont!cpF%cdSFMj>nY!B5<5z+<}^|60nZ}c;4 zA&^B0B@+2r?44vBj(cMJJZ|msSRr3UxQLot43Vd>qbLz5rQImph1DL*S-8IpG=P>Y zQ&q!Z66K300Z!#DNmN2D?HweTS}#-cu?1SpM-&?54^>5PWaRtU6hiWDXtmPHSl9#~ zlIEG5!VGtwJ;!!@nzrk>lV8ORcn+eRd?0q*qeq)IsB>4}cUsCwE`wH%A2lLJ_;b@= z-}}_v7Lj?cd6%^m;jZ)Td!?|@Zr#my96Pl1LST|!Uigr6a9R0-6_ zd(fp5kg_*Q*rGm=zRZPo@#7H3Cp^`1Lz5uYO(`^0L-dfDfFAbw|U9FB-n48zfb=m-=6?ycQ zV2k@9tv*>gWFGq`LHU3tTo6P0r?$6;j@hGcfnF*smrn5BNu6{2yuH5eF2=E=A;qmhGtE*Nn$D(5X^qL(KS~bpY*W z6w6{?Oe&QKiE)|h@^~cG@o;%joE6PZq&b1v0I=h48}j8YgzN<1QR^&hLF-pWtte|= zKYvQ>nJQoyZzQXab&MPfo~ew?-&4nlD{rRh+IGvUxa1z3PD?2)V`xpKU13%j1JzEw zsh+k2mXUk&yTm#UA1)Iv9)bQUEFWz}cTw1)Xq!QumYernY=1`hW@DrJOuf=BI5Ik* zC`P)g*D3g}eOx(3ih=4}{V5x}P%*o~n_MT*De3hy7B8`k50vWI)x#G@zwr?FDai=& z&4Q89e=mW&WucVT*nAP86q$(d_w+6`tq4l&nJ9pMZ7qx|*s9s1t+d(Rx;oRU1vvn4#6NjP zc$|@F^6J>&m4G+k5_0XcHS52n=S9vvRXW1nXeiZxbfr2tn7-G42>i&I63ZWsNhgx) zJ-Jf}j#Z*AfJs43uHn^yzx=-!pIOVNB@HA5ZCuKUBEp;g;bB)hvf6%$_4~HAngR4T zFS_yC!GO@Xh7uB$a{o3fv#B5e^Q`!d<(ZN07dMFUb&oslo+8<-7_2Kf*{+{lnVg!k zoyy8TI+-kI2@}_cuSihtuiu9v$ic-$0-najMAoyNI4ZmJDKM+<_-FG^1YVD1g-d99$V{y+J6 z)}MS_-Xc4ek2~dKQ<>YYgSniNP)e#Ff5e8Zco_n< zbcz{f4l3|uInAz8vxhl46{2(o$di*mAcqP>XQX|0;Y%9HMb%^fG+O!917ae z+g|JeP4E*H^_!giyvjQGZ3Esr#$ff$bA~53p8j|N-AHvpi+s$RPaY-;Y2^4Ktw}b> z`9*#wt3Uq4FCAPa#4iV$qD#bio0JO)8H_>vv=k=~k5h*ujeq%!YMXhbL!;8MnJ3!# zG4k_Q<8>6|eg zsbJ4M>bo3J)BJuSGS|cUoLVyE$6B-9jF||#&Q@fWZd!_JA1Q-dg~wu8wdc&hm(;BS z`fX6Si+(1!;uFKS6pW%jI6IkBWox?9t-JOIuohtVXG>_1k^h_l z>sZQTvgI!>1Fu7xEYM0U&vw=$Td@>&pvQ0ko#c$n z`?r+S`{E9G>Iek2_1C_J_rRQ!n*3`fBIP)tcs9~^KQ@&P;724$B42LuupIx&6xKPI zI6CA8r~t9^RB5sRBC;Vl(3>h-Q2p8XltzKQpT*ni>XNzmA!#|e#0pvBtlObG7XX4(qxi5DXIW!EFQ4>W!&yj$ zYEg5^I#dw*9qIyiKzfKKbz7;-_wV25w?a}Gr;M-`@_UdG{GaQ&q=~MTl9zcTlZl zV+ot`dHC@9hHlMeJ$tnP^%lJ!yi?qZ@4+37R^q(81|=S`>+X!6u{qGiPr09ZcCis| z?%nMfZTL3{9avlQ+56e6YrLm_`_bD!_{ur;fc^)W%R-4$(Em zWWK;T((|Oj*qTkl(nqI@`t}+Q!`Yohe(TT|nNgt&H4=M&-h{9Ism;#i9AVC5IlJYz^_E4g@)x?Nw>Ni@U0z>;?$ zUQ%s~+A`X!9A3q02#p`xB(JZRQPCo9bbs8xN}Z9l4H{Ueg<&SD>_jl0_I1l8mM_p@ zP}s&@<)a z1XE=WO*d3AZYr~FJ&*!nr0R8{5E(^Ahw&+fE}dxMaV#3ODotE>~n9PC0< zl4!M-*a`!lPp4gi6q;l_>eS8R^(}_(co3N!i)F_Cp&_^Za>YpPaTV}1=LVd~O zYc$Qw01RX-lvgm zsx9hG36f>OBAwPrn3u0!>5!fCg~6gG{XKQbv2bj3!3|xHr_OSR+JZW58@8Aw-gcDZ z!M}I0LwIR&QbQ}wct5vFFJxi2D8B5PMsiCJR- z=@PrphVo45vnCX&Bblve+eB`-D(vJ7`*X&FDPt{$=#hEPKTgmJYn_#Y4erXOGkwq@ zh6)t?)hQG&dg5*Pf}>7bdQje{%44YnB9-0?&*hRgZ*KdgL2QL|7Zc4+;_-4`c+GHI9r5i3U$|wWYu4s5zHxru8<*2qO=jz^0KJ1)gtC z%AkO`)Fu}VxW@YFIR%hi*~@^Sox~2|=#vbgsV_2ZxzS$Hv7*Fv0i9z5kE#IE$RGYo zyJ-{ES5D%c5V8%8y1NyA@yeIXCQw`FCz*_UWgC+BfbNC@jWD5@DXFPk{1*%m_n)$E zUxK5MawNugP4lxOQEs2=uD5(G^mb(z6IqjwZo2zla93tzuJ1LE7oWU3>n zPipnwW_4P@X1jFCUz%)eGifco5;49jBus*<*sknkDq&fyzNDap&sHISPmhS3$Xx5M z#-^#{AFH@@Z!{Av<;bU6A{<{;qMaG%4p8>eb!X|QSp}MstIwgW*!pSdqtE;JHjh#b zLibuV@DVr>2kYeIpYbWQPL*ZQu?2mf%m>q{U2iB=PN2|(Mwjc?uhXG%9V5GFbbijW z=f|^V^TM6hn}W_NNm4l>^;vhhXREZ6`HI>r!}1Eo877ZzKGtJfw!KfPU{uX&0zvRc zl65+(+WErErVF>--?c?yP9Zh-{$m@Te}}_n9Tp?LqMy)a66{pRxxcQip8Iw%#-kn{ zn0U3#KN&`6gE&JzX*svf15j5O{8phjpnu3ePbrX=2thjX*Dk2wf;?FA5o^q-9IUFvnBM!g#IUW5#cFfS;UjXp^4 z4N^aZOQB1CQ&ly)-KuB3ShL~DEqGBp*xkVD89e>6!RPePZT(`qIMCW7k8(^6shx&i zqp2Ks`H=G`;Kz)>X8)>f`gu z@66LkOUf+$ymOVPFldeqo;X1TJVl|q(B;s*{?03at%FNf@VsJrt+~yYntN=tnIcMd z3)A`h8nL6sFLNZE^(RMig6qwv|IP??v>X8$l;RAhz)Te{)Uh4iO%~c%1N#5tGf%_4 zIPg+eSA4Vj01}1-8*`fd)5cz%j7xj8;`dV{&Hrg%1u_prjQ}^<5Vzpw;9z?7M+KLz zgpMGvT}^1rWne5*&Lk-rBcE?Q8F(48Y;$FE=)zs3ISd)lL>1KPjpfaKeTHZfq@LCV zb+5ANRe`UZLn=T18F&Fbxq%m{^<83aCtY^0f8+`hVI#7*u=%SBEf1g3fjq4);|I)- zh;gpCv$wzrj7`&P#UJ{A*Jo-jqQrr__tfKgL(k%ShjY%SnVL2S<^4rqu&0nRXx7d( zWR+rkS^4}X5Xk}|%%T0kxAxAr$PuMM{w2TB`nhlySDVPv^x_j`clx{fD-M=v9ZAgk zxXHE7PniW&0ZJbDb1rCJ)-@2H1$}N0zKK1sx^ho@M}9hXK8cFNGeJ79?PJ-RnW+%V z|Jt~1;hxLro_ep-86c3b&4zHMB)hh>=0ywJ|OtW5+JyO%cl|?O^w~)Z(oq8I+wgMGn zn`(Ac@g}Xvu!;JU*It=a zU;h%s0mLV58*7M*L1B}l9M9O54_VtVZ$0qwv9Ek8uR=-8x90+6q`S>^4S5Qh<6H zpRPU$bGt;*?gFy%jk`(Db0ijn`vr6?bcDSkDmke;f9y^@TT|1|yKAiUj;JgptbZA; zelnJtU!~^KN|8Nwg_z0*d{}+l7A1WgW~cuID0&omg8*e28I)cc4E$pbWp_UMD^5qF z3(jVO*{MeNz)0CxMti6#tMET`ZZ1t=v3H_UUgM3hTbZ;gg!@;%1X*StGlg)k8ezk? z*-NCiD}gAQ6Ww=c*OnQ$_OT%ylk^~S)=!HZ@8^MXCRvvi3d{$?6Hjn%;r)`$(7V!SINXggEC3?xQP@*- z`#fi%f>q1@^@~RWveeqjHWaP02I>%w(H?XgiF~gc?*8Vr72wZ{}2j8M8$}=-lsnJTs6*t>vzyL^rzE{epuW6|0 zTZ^X>+DyQfyT`6zx;qZVlOWQRCKV;=a*TNOa0@k7?b2wHM|Hg3cG!@6#Y_xk3wj!# zeeNWESiRR4RSL)M^yk>Wb6Nj7_9PL48ceOiU}tvwEg-YKKP}AsQz9&VCQ!0&GeiAe zm93aP`G||YcxE7wslT!x1R4VL_U)tdAHvpLXV*aNr8BLId%TR=g-Rg8k!tWLksl zG}_YXMsSXY`ORd{>rNDcY?f0%9oy>jMjE?V{sv-e{hjpJ^1}XQW|g-+egVR;clo8I zIIdg5OF7u5vNAHyy#!798IQiLs_LoOSCt@}l$dyU_f61VSWMu5BNx5S?c>(%{X&o=C}koZ~@nCuw(QkFY96o}t)m9~b#mCk#=R3S0!s z3=J&BT~>n;u>MoSl!0F0iK2U{Zeta1RZglcp^fXXJkOcBlZ#upN}h8znv@2{lS?|P z6~hx@1#h}MeVxicI;?T_Lnad-3`gkLKREea%uh~c{W*J?ae1BQf9)vDYL*9LBQh;8 zF;t$nSuTyW?ZC^TkvW5gw4qC$71ZfZH=0IF6NvP=hd2;~W2MSZir(?&2pvai^N~IJ8PksGb`zXB_ zU90&_c!Zplppb`T%WGUA06(dOeAAp`(1*c1B%h>&1}@fa07)q)k$%_z+F3J}qF)VR zo>+1kJown<8+9`M!PC7t3mt0A{Tlnt4U(GAP^KT@9aE~cp_de4b3}iDBMi@zoyKc9#=3Z7~?$bS8LmBOt3lASSPAYZeek*i=372i3b2-c>w} z$W_$SnEV3TTn&*uke@!7rHyhSCmBq4brK1Hb55b-_xCou~Cw`H_B01{xP;L9Bx z98z*5y4A$_K55Vpp%_lnIEC(MRyz$PCP=^Q(*6q?Fxn#aYZ$uvMVI=?e zx}I$4KgCGba#^RVCZG$AwN%2LWT801$SY$;7TV;^J4e7P=7vBYz+u=rw0WoF>MV$B zFQ@v(pQw=`seYxIH(xV0kj)?jN}f0Rofwtf!(t4lQoGsu1y>nX5BN<*6~NUL7COEZ z2n{naTt=?eZo9xSr{xel)VLB2n}4}l5ewMq{-4z~ z=8r=bV@Rc{P0=-CHlEdY48Yriw6y~{JUth`J8GP{k$VY4ofB{c`f;+OvQLBL{EJN` zanz?bnG`My(%X$yF}qyZ8O@R7oOnKM!o?Dt$MX_o=cc#zuAV@vQez#Vy0_LZ0wtfj zD$9N*LAi8))JuB!E5xJ25vQ) zq&W6#N1xHneB1a4es2dz@l3+ev;Jv4?4?rK!P=E~yM>j9@$U^&i z57SF0clXJygD0Q;eOjmZ%^OQ&9V?638RJX0ppVKRPtA?yAb=|dme>#9RLqEmLXdy_ zbC+1k5ywn`cbd9Q@57TuwPPOOt~7iO!=ssh?DZ0AuaL+WYrgG=nGKY{MC87b0l)@x zG#LO)+?~s(aIogcl?80rBYY_w&YOd%Tq|x5|LQYtz}nw?nU{7rECNVmGa!~m>ywem zw0V1ScNI_3mDxe?>JRKvj@u;5MPqguMVGFCkD0o3%A)#;O&AjTdRa}Kyo`-5JkNM3 z_)X(bZdV!G0s5j~7Xflhl6NNB1wuJa|6MNbf(qJ^mC%fA=W>Sfc~Ls$Cgx;xEI%vp zaHlHZ^i`BLJ?d-m0`wbG-`vsXC5>QE%M8W|^UKO?gkw(-zQZF8FE+oV=3p;YLL~6r zr*O>WRBcUB3y6bzNL^KfyQMdr{-W95`qT!&cif?spa))zme_8vOo1mTIn-_nBx@}( zStkQCq>-CC?och^GTt>a2Q;fNDv0m-|LiTgSQb2$#HV9fT$j(CJJ)6%^&I|JJX3Qm zB>48JE^N;pz$1WlN}3t%G|eIWtV?RXZ!OKEQsPyNK&uF(7N{mQo5HPpX8T|mP-1(n z&0x|3Z{o-r5UN>N1HOd!!i#BWrBh$@TerT`q@QEoak2D6PIrguGA9*jERi!Kd$99r zkKX<#^mb03p2BZ=ZZ@w(;uoskb-}p8>y&+S%?L8~CqI4C@mJ8Vz7SAS8un#tPllUluQf-fvZ} z-EgDPM|Zp2kJCC|y})%(^7hhR7t$H zhag-wJU$K>|J7FYw*u{wIS=L_;TCDTV}oth?~$oN(api@{~sf!w{Ut>Z;nDHx{+Mg zjb?!;CVYgB=Gc9w<9i|{q52S831(@V@WVUSRI}jx8ThR48UM}$4RR~St(u{tv{^4q zje}sc^uW!(-TK7Qkn7NQkMk7qlgW8Nxr#Sd>K#4OJ&=IvEHu|aaS!+H@5FL>Y3VaT zMH)8j_#K-5)8qHSQf~8+>ty%SB-8AkN4+h)!tt+jm^g|ytK67$1Gvhdf52VgzN8Vr zyV?rwmPW=Y%<>wXapgw&#GB4e{bARMznTrrjA5RQV7Ch^niYsW(h*ql>%O^nemjvn z0T(p@`c*f#?+Wt!(LmoP0IIow-Jxnn%+7IP_;2zuzkJl7G}xtpKPj=hjNi$AE~ls+ zCf+?x&foi$9o``74rkgG@MwB5mDMMClljIIYZHAlyX>ftPoTE&@1l>~g|;hni5HDn zZjbM+18p!{=3#ZGNpLloC(>=BesB8v`Zy|p;^N}>#K6QP8GAGs-^TTdB~P3WQkp}y zTECO9Lnr1|;i1`Un=f_yvGbY%7u|@tt_+Nsr!#F6b`(8aDc^fs$c(EE_sgHQUTT*w z{P~ab96x8ju_e2(K$G!Rj`%U;E>$I$(xQoOt_$7XU3YOQ&wZ1Om~w8q{a_X8*St7z zuAJSL@96#y>2?p+WO|jopSVpa@POh>bv8qj=#9Swv0+$BP1cA z>}zIRGb6cHQFa+2Ba{`_-lbHsN6JWMLN-yd>t>WqRAgp{=X~}1J-_Gm^rt_1eeUP` zIp@63`@GLN+A|N=(^k^lN7H-M?Ik7XgtYI*zdd&U{q%@o7u3q`&=JnK*#;V>WL`V& zCX9}u4>MFJR&ukL#j^csJ|{i(9gx9*_3w_aL}wD@)BmnWVhS(BFa4C(Dc>tkBFF_q zeptz%)AmkD_w6w(f6y-nIqke9#G@S4}PW;&AMGC9q9-4?*fQYuHeC`ix#q z#>I((Uh5Lq&fFO3t9%zQl>#fb^~H*2@+ZuvQm^K#ipUZZHmSW6yJuS8G^TumlbxMo zXGd^X{+FEC*7Ru6uqKlv^Oze)C73{mwvO`^A9ab#6Lo&KZ@ zwA7>zMi-p4MtzOAn+lGAIp*V!+tpo6q>k_^9?dDH8Ukcsvg?U^WF1G$-yyN$p6 zTymrQ>x(Z13D_sndQYqJ={2&| z?{bQ9RE~CiHnns}T-yKjN4duuW3$IBh46P6l$muJ55c=F3Y!@i99;Tje{sT`hdMGp zLcKLV!mQ>xu{<}4EJ00uv$$GYhY|z zsH=BU^7QE>xyloI6wD;A>Sl{&YA$GVWa)7vYjRWRFa5o7Ft8|lqVB;(x$bkR_rkJV z)p`+$U)LJGfR3VZ#8A1w$oe9=s}g;%5w%*zLF?bLi5E=I2xN{f*n#!8Abryvr2L8f z?u<>nc~LL`&)sQ&vrFv(Re2+<$gwrsYDSPL*i8B#w@!>u$7YX(GJtUoGOxd?81=vU z*}=FM+=|?~dv+U{5ra;-#LK|WiA^2YljbZWaZNu$#C4pdGn0fa&p7%81N}B?g@Gon z6r$mqKc3yk;zA`6TP8_g^qIR@^%#ep?|6?;b5elq-*UcZ z)M7phDoD3y9-{B>b#oWXSdZopJ6C7nj-ck`O@k2p{)b9fdoLtl*gGhGjV(2_!c4TJ z#;A!Fm(IH7XK>Emh4r#V%Q2sZuwFux9JTY1sY7dahHXmf^P>N10fx@bvt=JH&$;x{ z=4?O43e+sV<-6x_CKC!CB?;uFskJ=$4dzsfW+}`Qr3JLPWOlK+QSiKAVUX%wW{uVn z?mShIhcicC>QBC)vA%cQQDI$VKo@{H*s}-l&(|N&)7Yx7Aoaehb-iG*;$2N?8l*GU?bwjlf$=kBW z`^okX`n11HVEl)nTEY5Iv!%GqWlfHWA(l?@)Qe`GeG`e~NF2JT_}@P~cQ2N{uh+RE zRGQ>5rqhyKQkfN~Zu7rg4|oq5Dv`fEU?18!{rrml=P4`kJm&bQnTzh!@|=w2IWMEf zzE>`!rKR<)79r2y-v0gdmae85T^~@hv0TriY7aVM#;Pk7i)lP*^1Hx4adk0%JxZ!C z=5qQ}H{YdG?5P6aV@gIz**gA)$h{jBO=|g?=-DzDbus4}I|*_NHmCmQhz?vyuEI>T z3DM>DcrrsB?5QYb$`S9GcsU|NlO?j{OU5 z8W^r?bhtA@h?H%8ZLLOxKJs0A2HYj&C5B2^eKtDELJB8o;XcxY%m-xrsz*hYj&)iU zMH+TozRc|T9L(?B)w4{V+g~z0^tSI+6(zA`a~A}cn$`OcALAWMB#Km??LL|hl+8;j zJ=s7poO#DLO{j$#<+>SAh`zr)O(U5uLzCB1{RJCcEP%7;S6vqM*URU8UtO--Ss2;a+Y{Q|4DDMlV?Ot4 zuBGdx0!%XSKQT;!e7(;pjIX8xwbLQ=(bc?!&zI2<}LdYdj3^ z#U)NW&$3s|KE^OL$#wYgU;Aa;AXYzkdeK_YJ0fBCOPFN)b+H>;hvNu#7t015xkAm0 z?AR}ZNkWmgd@D6m;O+4I^5*lO{Mk^`w3gS*0}CjH0peW9D8IvC7o(fWntX0)^vXm0 z8)K^cSBQ7 zF~;ugiTm58+Z|GXjtcggUUc3eYxGx@Inc6!J@0%rTpm9nIJ zV(LAa`5CZAFslZ6OS7Ud;%;3LMWOd-67y^q)+J2C7tbK4|ALj2#W#XqGciO6WB zwm03~jCrwR6F~^F(P*^rFDg2y@ORc_>DI!kjXmx!H9CUA}y zx`VKkrPiK`{%9;m$Me7iXqz&`GC3T~q=IngCbLw)K}WgfH6F!fh^u7JW)AdDBGrE( zt0-aIQYX5GuJdxUuiQ13sGD*G`~2 zK#)fhOR^V?R9G*&=|xxFnT-Fw_a*xGrL~Xqx$9z%X}mZu<9g_H4-#hBVURA&Dxq#aX@KQNQVtO1g4gA?>m&`DeRy^Xc_?XJ`Uz5-Z{#%999bOUv3s zcU$Isb~~P7HE}l$rd*fChpZn>ZmBZs`yNAq;Of8&#TkZ>|NS}j*#E^SW!p;id}H|8<1i8^j4WNEG@1L+h|-PT5?>N$8MFX(L0!gf34vDp*xI^*lTz4~z{4T*!DTLov#54wvmYn)2c?lt98W|LqEy5&)AChX7rPTJsw zc~JsLvHKC$v-4!;eY}kHMN;98ngR~3oeZmTo^txGqh4<}{py@Tgp7|B@-~rUzlHj> zj1<~#Jv!CJNIgvqQ zvOIYQoSi6ltTY;vj{WC5w)L;9%;}y^x3##ZaG0q$h~*`9WlZ|Q&Dv5r7t93ebmcnL zjIaIO>;&lYYAkDa*}D4ij{TpNxrI9c4{LjTo=es%B68a7b$xQNp5CJ5#+R{}sIaJB zLyeUie3!|^-mgFSdt(_wQMHgXCW3icKALd)Mv#U;k_;BQmBHmKRZMJ`S64eKG8c0G zvM5==!akQ1Q+#XQp||l*sAoKF4;Y7jJ^d1&lM!W|T5WvRluc{g^duhRdq4ZSdAAM3 zTv*?f`aKcJsG9R6xoq{jqpw zZyse=F@deCwUU;n$_6j+DI~lw5j4VN{uJE{D}6W9J#q+79yptQJ)JNUxpEMe<4Nm9 zNn0?^kP>Vi%dfPHANcXb6ZK|O;YUj4P*b-{ByHFp*857+pjZTlLGaxIE!R(10ErWKVx^S5_(U6f>@ zi&2cL&Xv;1F(A`9!a%rQM3~W=d~uI8P|nyXN8#^lZ0B3Nd84l zGOsQz-Ohq;lzG57gZfA%8W6g*hx?z9+*?{vo_*XJgZ2(Pz0Ivsg{LZ)GU8cFi}5Vn zh(z9>xW8x8NkRDP2H_u)mNYvE|G})2&-FVo8A)6p1X-I5=sK_SN87fdjbjfVs+T06 z>|02xj|;N9P?oz6`D$yrn##nu>r)UVULNlc{0dvp94zn|_V%whaK|RhPtZG&TpBMp zyZ=BTrDn2=C2$=uJ$-87?UJ0^#gBDoZ6aMgGOsj*KCM6(Sdo}&rHFbsC;S6J=x3}u z)kHQQyC+{QwR`;-kA@6a<@k8@f}KUp_O954u@jUoHUMYl9pD$@bmTWSF2d(jq@vO< ztfZU)yWM}cjf?fPuH+e#>{E6SZO_4zX=T`8TUvgvX8df1-teki_vE&gD1^of3_baY zqOOfT99|Enzh}dGBzOBXErEHqfWo}@()Rh}mtknnQ&>mvjEW+1=E)e#$BaY?S0e|O z0%eGayj_;)1T>*XTnYhQOo$o~91!=@PInoq91D3yrdv8sd1<^xk>41~FjKIO8VT@3 zpWpW%!E?QXLm7q|AqU>=f44x=fcovD&=UqTJjqZ*pqT2HG|U$8P#(Tro4{0Zi}xD( zR(%HUt1EWbDSEy9sX~ftK`j!H{rzfc$Cz}WWv`J*92UO&{!X8ET1)R#U@2_sbON?D zVC?F|2&et=$_(2J`+jDdG7q~`$1wVQz#VWszOYv4$;D|Htc#f zI0G<1kz@D@CAq%Y{jTYDA8pf@@LXklA&Qy`A_8q-n~TcSoCOTO1X*W&(wNW3Q+>pY z-U0u)tE|N!ecjo_1+wd&-?!y0SV75Um?Re-@gfHDX~qqm41enphSk1{Ig`JX8i~pL z!KgvzfkAGw8|rifg4*4bnTEOXr?N^Q`dTI2ebY`k3`MszvFKF-T`~TnVN}HBEhh6lehZO5yV-u3H@U8~9_D`x^kqhOs>!AG793F`YH3kCJNkK74;_XAlOO zg>?C`yn^~bBV)xfA-f%zhl;K;&Ey!VOBSS*DhvNytw1Wlq;z;`1z`9bRdc#60Dx?G zi|`9W&na$V_ic9feC=J&MXR-(_)*)d=j%V1`%Fq)K1%rL3O)y$7dRckJI8Dl3%k1> zhbMv1*L+JTO*DrN?3685y_Lf&A2MxA!l`k`?5so;cU8*;5Vw5}49QnZ#d_tG|7n@mI2;X3FZFOYvgV7pXpu9hX8`TobMgpAushZW?A! zXS>^h3!i@9{t`%-femNVr3IZ3^)pxiy|Mmi@1<(z{o{0xIxHfc`P-|x^Uu=lMo-jy z{{_Dc*EN4)_)4D%M`|biDy6q{ZSu};r-hB;(B5Ea7b#-%8TcOvWo{w#cNmB)v4`cP zEZyI=r}VlKtRc{w_>BhI^^#LoW_Ziw*-w3APs(|y^*O0QbtM}bRXh8}XrPt4bjhoR zPJ!YhgHGsgHiOp#Gxp%Ij#rkpUAJDW#CAI59U1@fNXb&J|>R-8SzjccQk^rX{)KO0thE2oNVNM9llJui? zatecVs)NJN@3imgRDiAUYx5ght_Yqh{FbItFzbL^ugDfx?h51Cd*eLn4?(}@!ehL<0 zIm!Z=r9QD?(GXmds%5Cq6oc6I4LGq7>J#&B*nbT}FCmlUCjTRAG!D$s6A;-{40E{GV2_iNOWcB`$2Jiz>oE6LbLo6F;)Ca-a`#5_6pisy+4$= zceXy|2Je4Gr^CKF**)2~5PPq>ii*pDqvXGGzWo+TKc`HVVcI;7-w+9ns135M7E0r6h{_ZwB)|O{cEfl zrFC|^{&o-2zPcPkkAwnC>Z#@)hO%gxltMwQlkc^U^_W6Hxhffv;|0Dg%)Dsk+#%B| zSf9m$AX@`rUPkyG-1BTf>6%ublkcfXFN6GiQ_{2-ieaqGh9L`FYSFbiju$v7W<@WJ z52_fUJ##@#2f+BDiQ!QQ$C6n`8NW)%vt&E6gmH5cx%(@-*_NG9^JEA6U=vVg+biBN zUvu^8KQ&z>M9RMB<>7d2Yy%z-B}y!%Esjl|Ylj^+@#n(AT&e>6&TMyoyHghMcf%q? ziWDC~eGmObGLLA#oXTbJ`UC-FIn?E2CVZF0R4U|B<^is}TsA2b(wDXcQZLCz#=ZRM zaV2n+@|O+FBa}HWQ9*DhUbuk9+8qhb1D?87yGt_h(}N8HvZ?o$NYmQtm}h_8t_}(!L1;jyZAk_+n$)(_cI_Ob z;qC1PYK!meT&HDwo^}z+%`i|Z9_j?a( z|Fk+%(h^Mnq5V*Yh=g!qW!7g0UWX+%#822Hp&f(zM>>go&A`TEACX@=;^^miz+RHX zDf=*qb`q6DcrCB~AaUnKE|uCotI*&=wt7V3_r}4*%Q(+>;`C7%-{d3;QvO`uQX24V zQA=J0FH|$F1Kw{cXIxl2uGzWmET_{^mT8av$dm_2+I#qk%XRz+t_0`=Czj0E01E7UidfLezpOK}o8D_Ji#v0>OM=Yl+L2$c635_k#YurVF_2*I|dgR3bdUrhb_du`b~v&7VU!p0YVl0Mb|4MFUPain?#(DXxR|1k-Yde|Mm z_2D~r)yHVIc6OJ~Wxt^R!Z51o`w7FJM$wmm6Zn<34+xcusnkF|*>sYi4Cou~~@Jn|uBbYD>3|l2PM?YKU4;z*F9}EgQ5@VV$%SOJwmJ`@~iA z8)^c!aGMRhl*@FK!E+lMm8A6th`Lze zbr{@L`mJ6v>egy7xuqz z`yPusSe$R1b1vaLO-a|+cV*r>OfynLCyWzdRm8t96+wM7WntJurmB>hZ38^Qce|hO zCj*$I;X5^`qhMj%dc%M1hts^WyN#ai=HJx4`0|4R5AFsmJ_#h}lnK9Oa=Av1mq_v{ z%CePOD?(AYi8YiLnT_y`oId(JYV3jz2cLdT_HhyhR#P9WhvCo~M!;66k$S#I2 zjtG7%!Cn9tPZjkdkriOX;fyq5jAo1MYqWxGV=&JqN)^>06B}yf3DD>qC^_yEXrgN8 zpypnQ#k}aBwFz!)Ads~|Rw*!J&3|>M;>G8@S)A3@PMz-wyew3*%0?UFv-*Cg3Pxr3 z(IZohTTsW4V?+qnkV59lWX4XNB0alrNB)(2Wq{`P(ty8cF@{_je-%wad?y3C(I}^>0~gCvJr6b#L^@_h{eUpFeK^NF%-Rf)X)(3&r$va%n;nyua#pA}gAhO1Q-&!xVJ4JSC1_N(?o9Pj}E=LPZ9 zVXhGt?MgnZrhXkk=;`a-`(QreKTJS>$+>pck7J{y=k45?@0+TFAyu)V2VMJOXmdHCg@bm$6XaC`>02%LKLH)I*&w+GzQK`##Is5ZA;z zBxNn=@9K(2p}}c~K~nyVx7OX?t~}uSy|5t6)=Qc_^)IYCxvl+yEuWqlB6{DP%CAte z^QC&9<3DhHMsvwIfnW@NFvEeaL3S>IWV8i7jkgQke}2rb@!!>hCvNY3`Tp!MgVWOB zw*}zY{#H26{s_1ZNKJGREXZ&Kh$<)1jf%Oye=3BSLL-xg12#7-_SudMvSOxA56zRT zA@ACwm$(vN&|_QFIIj(z2S+}c&Kw=ujo=Tknq7`|RC)juDD!I)!Wq5)#M@t4;2L#K zP#(!$xy!vqV=Y*is9@STK5m6_6&!|77x>hG<8QBG#A*FvF3`#)wQ=u?;PR|Cb9c=+ zQWLG@&?UH-O?}fNJ(CZWXTXMe0?~T3z2?b&e$NNxeOTVt)7t%Jv>i%3)qWU(Afa92 zrp?;3-fqa7^A!UGn6vpSBT^#ZByFrgHcCxIIsp}Na3%^`-+mcc`^?Uv6EV8 zJ}+F49gvE6Q3T6z_u(qB#CrF}2=WECU!)3V;Y*5`W}*>&{q^fcq~?5AJDM~36kKpd zsu`@=*Y^TXALxCi;g+U2l0$)LbV6i`a`&Ci2SfyV0K7PAH7&3Kj6~~bdadxtT}9N* z{WtDL!!N_x$R*uP>!gw@16(0Q^E`4(leoCMG#Su_QvJ$Eu~?@^Tf3V3*S71+se31c znvS11XF>_4_Ll7e*q6_p+`2Ss zBeVW&--T}|2422X{NDQ(`DxG-J;g`duCAqXCO1xyJ++AK`dtcRl0blkW`(x9|A9|7 z&pa3iO97)BMr*<25X55r=Y!yd;@_fq@p^0eS(TLxC%xwADpESzghsAfP#DPo0-aT{?5E#urTkT z+JG*KFWHH#YM%_rHTe792eDG9{2DUyRcNcxAS@TE;czb?jlmCZhuWeEuO0LI-gdDNiCKNDd7Gk9v4Iz(Fv2x z1UyZ;)nA=vJqyMCOJfy*K7(Jsq66(qA(6g(&_UV2Dhd-@z>THkJmmI-6a_GeAS_W_Yv& z@ZSVJAj*H6iNH_1wPL^KEqP+70}41S*9C+fd;`{79S5~tYNoA87EP0WHhq^r^JmGs z(;K8pF<7MPt-Y7v>k=QX*LotNYP}ce5#t2)gZrEYnl#G}NdbHlb48b_qQB+m?do>c zH!`He^96^}Jui~#N6R1dO~siL9w>BQcshrs1&0c4m#{PbqY2s3Vc%t*#AtYS{0a}d(gfn@9vs^cOskL&nJT^R|-K zvUZ{SNzaI#386v}9$S_O2Xh2fC(qRZg3vZ{8~SAJ#E-F05;cLUMFCl8}z zhp}U8l6YSl{GLZ{X*txyzu0b@li(_#&j7%O~;J;_F94J-FUG?W6wBVzAWa&E@5t3h+lhb2O z*9rMDGBR#D8XwE<(dwz zJ5{}o#y|lO*htvTRUOWZbr`{%B>e;`vJy(-Sd)9S*|K>nno6 zwDej3p}8sZv-QhmFUuXBG?z@gu#Y(bmBJR;JPQ?_7ID12s9ID8-j^LWLKHTJ{wkZN z-80h@m?jx+D6MtP*9hi;V9iR7gN>HvB#{Ma30C@Nfdke=_() z5B*6bi{gBgZj|ey&omRm7v!#vGin*3%f@ueDYb-vfOj-O`JF=E_CW+sE^lp|u>KB~ z#=?}`3~Q9g1!^|4y{UD})-A1b-Psn+Rc1G=xd_RBDP^y?q145YWmnA;D@?<&hzz>1uvaO|^%Hakqd$0(?s2G1?j#0xS- z)BKgh3!_5CunSN3RPL$;`^_pxVcdng={a2-{=E#C5Vh~mY(_3ROs|?ou<~G(z%$@6 z=%G{pwZ>%TK= z@zS7?a#8+;2Bftzd!!$d-=2P1nxk|qt9Q*qR$^`HBRq(F>+W9nIAY4xu^{ms3@U3x zeIglBxOGXXKX(}$*4uLm5sX}JD^ zTC|^%Ad6?qS9G+80!{w8*+U3k2=x$4C{KLrPhh*!BfTg_v z+0?gb#aZS>Ik)NAtR<5VL}lWa=oq$WF>1r{MkbT^*HFW!dlV>F{_Q3l_Tw@;4|>>n zVNCYFt92#A(@wXo+5!DrbqMq?P(8z`sp0}%Nt5PAVs%SL>zB?bQJ{xB;@DtJ+nRJZ zN#K8n%-6NQCV%|+af$!uVn3K+=c$sE5H^0*4N6qqhRL$>GMW9BI}XJhNP6rFPZj+5 zTJG4s`D_d06xM712;|L|_rO;d|&_U2vwWgY-YwyNEhqAfpn3T7A*V0ZLLsu9~EPfwQv-_xYDl z#*}kn&4`rG)Twc0`>*0As8NqS;(1*HD!zvm)SfUtkGsG@Q6amK(Ve^|BqlVDEyrmv427Y*itHwcu zg~cx8p>>yYQ5cb!_F?x(+(B<(uT643i*_M6D^%gW)&%|nx?3|yp?0?*)?k4?3N^&x>CV{M8;YagS3r=?hTn!_S}#4(y1!g!v>STt~Cb=r#C zZM6;CW5qt;Yz|!FMjr_2C;o^kNd(WcZT?T;O@e6g!bz{ArQbumcT6e0nwS^r(jmj5 zM;&okWVw04Cc!0|)rv?%0*&qPg7aa~cI^!=6$b2kDn#D^v{3YilhMc)RpX*+e+`=| zqo8nm$5KtN;-1KXZl8g(y_G)jobxd+Mi+daAffv8mPSy=z*>n-y&85tXGd6jd<{5t$?7wZ`c+TrdE~9$qVZ!u2iuA$O7b*fX2hj~7K47kSsoVJ! zS?sJ#au;f4=`9Yd(~)h~gZl zZTUvjJ4j$0tBgOld_Cx;AfZCAy@dkZD-$ihVZbT+1V*slNJe2ujo~*Z$)kNrM_?!b zW$DR(30ATK9S>gh=u?n7KvP_mX>k^+x_)+F;fjAaq4Vf$1B@fT{6ra6?UEhDe3UV{ z^t?{9H-(YgWqJ(vMaEs5BLwdXPoLx`>K6#I;JOR1#)33%#dKdbmca_a?GcYMEm~Go zG}R~f>sWWD5r#A8J}_=>xpL8-+ti#$_v|(`&V3*22tr=R>UNW0f6H$_40GE3_emIH zaZdN_J*fGoyhkJCd;g=+E@Uj=V=ZrC*L%i0U>m*@B$f5ll3S<2&lk08Q97omMq z{s)8V;Wuk!0)lI9yz!CIs<+l87tJZXXIFiG4I@PTmtSs;J9N}B?@JgAgV7fZbHZM3 z0-fumP)b_?;9nd=Jbf=NtOWL{{(5QzXcTXowGJW6qq! z11Z^e@7MMXN$bjk6@qSEt*5DzED5lvabk{+bRzWbIen{I{ zCfaKkodH4SyH7uWV+9Q2qq_+Jys$MaBFrJ~vtju8x6_^ayJM$%68g%wBL zuKw+@OV_av-WKeTAX4nDfG%< z9wr(ktb*yKSYxrTUbrq*k?~z2muNt#AdQtxIT+9;F+J7F&YnwlvURGlio&{{WqIm2 zCz5e?t?nN#^F&WPu-TS_W^7jBpW0;(`uM0REp@Rh=3A}bOcb7a_ zxXFtAwf<}W`u)`DAm$=KhddX3o|%{r21FmCHz7ZI^X-oMvZXm_r%^}ISswT)*E>Gw z?zgR|U6H=tf_AjRFAIi-*$-U@58N%2wxA3KGQ-9g*f^~tj-|%QEv)n!=LH!oSN4N1 zj3P|dj;C6NE)yg!C-)dYvA6qBEH8+SJ49f&0(cTLHe)?v0e4)#J9`qeGs=pqj>A+C zB`O-`*>}JqkC|xZ%TkzzxvNj{_xU46r{6t=cPn*u=qjz+2}F@Os*;(CGbRs4J%I~kXnaZqE!H}aZGVcsVok)5449(!_2}8?g7A#>t4r;S z10uZxrk~F*XJ@j)VW|_sJwb1LW4ebEa*2l%bquR->>JQV9A@JONfKD?j`0w7gV$bt z_PYljDg6#YYrlLz{s~6cyz`HREaEt|Gmmf(TXJV@s9cMwFS;^?40+ze9p(w4=o&SG zKM8)8#>3@ri#Mqfk?}0 zYqk^-X+?0((DX*3)43lFp!W9F%1tjma{47mK_mhhp`F02la*E7mC9zZitOV=%GsA% zdO3vlTU`-yE$l-(bh&Q_9OYnTda6d@x8JP9F!6p~-RZ9gE^+Q|NujhOn9}~k{D4v) z0J)m!WvW3L=cI6$6#`aVg|Tl7E8U07M9i}{)D5+9@GNIBeQ{#=YQOMQF42+9CVHg~ z=Pt9-xZg~c4^5LerTG&uiX=-lLy0_sS^|toL%8os$#hgXGoG$ojqnc>h-Q(jLAK2% zZ0}MDiQYY(LToRU@r8n=8LhD%dH(ymrkn?`+So-y zyBBnT3@XW<1Ae4Te+&sE9Pfic{b)B-lSL^7bR$H)OZIx50M>g$#*jY-UDSnSj^cKO zr)O?!HljR8IcobKYM1KS9Qg3$5r3_=<@ZhhKnv@czAG>Q#MmJkNd~PJ)R|Pt=DbdR zYCoH-3kC&a`k^BVi8~W(s-Y&Huz%2(#7-_Rf!zhvrl`QC5+`a1@KAL_W>D;%rVQ6P ztD1rBYP-eQr-f;O>*SF;zVDaXYU&0%%G*A}yQ)Lr!U47d$QjY2Kyq0i7*KeD+zJ)h z;Z+&|y6jt%#uPAIeOGWLS#MY*)Dg+(Frbk(tL+KD)t=Z&%gQ=Hb@Ul1YP?7Yrg_KW zm8*xluDp6u;zSLUlFb*+^o=>SGNVpEE6;t=!pqf~u<6K|nOHU{Q*WA_NC`Zzgl?ho zn24HNS454f*V~mSu%}Dhx?oRd97sSL^RpRu5tO)ZNf5k%uSG$-9}|+{gZd^^T}>zc zNEM14NW&Jm&rKo=iR69Tw{Se@80klNNM@Y>G>@D4n6AwC-Ah>7WJr8II_8EXnLuE$^vl7le7?1 z$=vmOKRicEvERjXETOT2_zha8W6l-if*NRHFs--8bcy}=!Egl@V=+Fd&| zFTga$PN{jx;+eaK9-j8)>SQcO7CIg>FHn;pP+xvxwJ)Ig!d=P0X$MIQt935*zWpSe ze$9}7{uP6zyZsAVB+eW<$dKyHte;D$9A6|Z-hLT6PUwX32OPArdwzPvT%*|ZkB@4d z-fwx7D|*7@nNV2)6Y|;IjrHnBExJ~SPcjA}(5ruW#6_wiA50L-GjnC^`il>t&KEm> z`cTv7Kv{+%jkJ`j_CkPB`5i_Oqr-N2fRRViJ)xdA19f@<4Y1;qddJBZx0KY%xUTe$oE;z zb2;~~brd$z873!jH`u!Joj^Ea?S=McE%s!AJW+q$h`xL;hD$&nXHb)tXju9Bev_7r z0R4(r-(7Cjf%?tqWWY@#eZ@h!CINqX&$p}ZAVDj9+V!LToG18Fr^^epo;gHu=0?ex zkdbILXB>uPNykS+t#7*+cM~GJ?wUqYdu?b9 z+~Wcr$z!M}m>ss~^tJdWiqe75ehU>1o>Sa;H6BWHbOdno;vR$~pNOPuL=KW%Vfr-^ zq_2NrJ%Qdu;+!(Lbl9B(v|=v1uupuDjK?bAX7rdR7atp@K`MQ%#NNwmvX%ZC-I=KA zzYcv3lf(pm?V|ppeiV-`0{U3|n?()t!KCA9$A99?-kJBnRIXVy=Z@Lr_C)_>>w1b@ zRy%*qM(@$BrC}f$p&i8mrW(Y>gErd1K9e#8-Lpx3GvuwFFL|yM{S5>u6bd4OJgy8V zR#bYRiS_1`La*^?>}Nt}w(SL)5_2ZZ7j`J(f1K2@gz8J_$w#)e#cyF0l1-|9Cu=3l z*D6I*4<-IN7sM!Iy3NSmP-kRfxf`-&YjalyG3M3BmG>{?7+j-06EQXggYs=~Yz?8*DjXn= z2-a7%jHxxupqZO2%Qv<_%M0p-Phsd8!)0HJ!$R* zW+%?U(?7ckS+gbgjb*(2%H~kY6P`B}74+AGFYv|#v36gF`dgK-{L{+rzijBX_527k2$|DsSvaw}+;EXGZSK%RD;cU*Sf1 z0OySzr$oFYf*Uic?ktRWl6kAxdZkf{c|b9W^L+0QmLDf#+cnh4-uZYr$7w;h+gT9y zU!oNay^Kw~P`GZN#81*4ZTfp+LfKS0iG`iU)u~IUl71%HL(n0H?(uV4_&<&MsWHvl zafO@C`oa=B9w+CI03%jHQF_gloA|y$SbtFvZ9b!vBJ~(s+H?tJq_IJR9G*;tEz_@y zljLJ|qXN><9zEUfme+tXY8gvY`gFlHd5b)6c!GMJs`(Cr!ceTH> zNWylq`rV>Hg4s`Hx|_rSmJhu!UDGp=;}+N}jM6@Ml_8i-)24bj>QqgZ@A|yN%P(33 z1Q*6&%Nh$AUY&wKQ|Xl{Ng`G6b7=#kYpTo>+c6|YI88d|Z0#^SvAry`FeNQ5N)V|% zbw?=rt=ZS*<8Wy%aA`bitMhIv9@hrg9(66k44jPnbT9}oLi~etGlv31%R$*y>Pn6Fq9&rO~rpdkqMlgxO%i)I>AR{H|N07!nj>W9KaymzZ>{6#@DIN@oVuK20B6&tCo_9`fwzl>aMpW4DV4bW z`H2s!l&bQ7wEzc!i8uxiejvVmPcXaZs8lms{o5_#L-2Yn(Y3K~nZjLMBp;ulmszco z^LaK<6>1!hyiNwQ{S0bt#dTRD$m{2cdgc=~#@AH!dyo>22Zx+0zI|;*T=Bga?6Rbt zdc%68)$z4E|M}9drqM6`?;g}Uu|PqO-PS3a#84E>3pL17NKF06FWZlw4%4W(-;uv z3T#>24C`Ews>S2!-#{iN)DkDKy6rJOKV0o_Jka_b=Ok>YqE*kCTJ^kj5O*06^8l&I z@_g`CE+pFJz(jkCgKHM;{FE7x=FA~cU%w(8hK3NesM-s2H>_RA{zR8)5K8$zQXm`J z@oiIEpC%w6{aE}&r*!nQ zH(qycEK^M>$DXaQYM?cXQkw0duse^_oS>VXm}pAs<)1O|jB+RJ_*?k0XgGa6r#bul}1%yls2TNJO@EyuSRZDeucDoGkj@!>W0))xiV`d1_TaGzBO&Mm%Qsu{gZ z;HNB$-xJ#9Af0MTJY8XRNIl`w%f}m^4tPuBAHrUSS+$veZ;zR{LRKZ-5Q~}W{_c7| z9P@V?jAWh6kvy&|Uc32l2Bk&X&h%Emch}YXeQeMItxtEeX0RIBzeGBrEE1#~;Awwn z@%&JgfiIuErn3$+EEl9JBQ3q{>1h*ae^ZWw)>?h4Jli_m3^pMlZ|xQ+Y6=EEL)&L0 z^La=`tQ99xG_(IX&F;Ls5GpcDrfN{=;Tj?*sxQ6 z3$+kmz*m23ZDWy7-lcs!#~m2R@O8%iPPORey)$nC3==M{Nf1NL!(k1*yBQUsr(h2d zRIXj)yb~4-ErLiHy;`WqYCahHZ6OeqBIoawa-LE=C{?MXx4iOqS zM0zUH=NZ_nqxuZp^`dU)iWRfiDd5p&4#bti@Hkr4BO`adFIMNgYP-%Ah9DFoV7?D> z4sjefWjI|Huv@%&%Z7yph@JZb&)%6+vF_{eB2Hk+5K?j|Dh;Jo|QE@=oy zc~^@1t+h{sCutyU5wT2Fd3rHt!t~?};-OSXKP5!TOy;Qv`&<8GA*3u|As^};VG7gN zor#@k2z}8|xnwt+)3Lr!N_;=agWJVGvRUpu`>LYQos(HxgP--~&5Em{r}#m;aAa=X zcUIMrQfgYoTw7pm$WxS^dQ61tGTDYuZ;U&b@}cwphpG3Dr}}^Y$DfB#GDzdYMNNS}J6(tRvwVrC~dfl9|1}*VF6u{``Lb`OEEbo{wwX zulsesu7?UIL(nkt)>eR_OrAR~4Wj355st>sIs0M1QkEt599SdG6R(Ij{cUQ9HE0>Y z@r;Z1njKxOmi~47Mtc@;p$ur|TQK+XF8Dm_RJXtYC4BsmWsx^3m|qktDgk$RTnSME za;QT*I|w(3oK6-ptE#SMJ)hJIxi52tzmRj_5p##7LGwsU0ErG^(tkx81e1roj&;Ma z2lXev(-M+NIW_zhj2$jhF@hJon+3RtBA%%o_wdZg9c80L$i&@f9Jc!BH+$_0zRPM#-FkxFaA`+ zpC4e!^p~W*Ye&fgoTMS>6d8zI_psBZgm?V&C^?5 zFgHSbmj}b*|29M-#q0`Z618X$U}jIFR>5^eY}+S!NUTFG5Feall+dx zNaplr+v_ik98 zGNx~zn9LkV15Cue;ol`v`M71Tgkg~7fLqz;`<-o5zbUVz3!y>gV~SlKljb#>>y(NF z4EENE78G|-ckELSzJ{fm2l+DF$ZN=cS!`YRnr#Ke>>F3E2#BKr zC%Ns^w1F(`D;*AC)$-;|D5iZfeQj5e!nV>dmVzzO2jE8!X`+rF^rbuf?_&(kS^S;#%WkNiw`&bYm^z2#VHQ6bTy4K%k4)S7vh zi~2vud`6F^U}sk0H5q^T;`xP0ufhpQUr!D6L`4d`UJA&z6^xLie`gQ*Lp6&PBe;eWX zDSax?zlaPPfim1`NAJVgYY5^X4;ds({CDL0y_@0g%L(Z_UXxeTihL7j zVBpJHW|{p>Yg7$w{UX>N#qgXgV*1v(zyq%Mw-}#ngy$Kko4$cy_7Rot9Fxd{6TUN1 z4r0pb#(x9_Fz_zhZY9w6761COG{X}iDfy;h6-zL{>*R=8_FQitpHDCPZ_4K#qHRM& zzy7)10jn;R#s&VfL_lWpG%>`81Xc?9hsV$v%aGZBPfHY~{)(SHM13K|9j)!iX48@( z?#{wVY5qUz?uH5qYc2?O=g+(O&PMP z*?9!B@EFPziEc~x8lT{tIhZS5Ph>RiF*H7}L}X1&)wWFI`GKarkp88)m-|Nirdx?_ z^ON&z$jU+?Dom#V`erBFaU^%kOSBQ=I4aXNk!Z2SHD$xL(~NH^c;Gzs{Q24N1AaMjT_L?ewW|h1C`&G}2S$H6k5oq$8{w-_L}PRe zb_br-T%%OpfE9=(x9x>x%U$Bn7|mBNW>6cw`FiUG+e^Uf+8^(sV{XQGwqEVRfAy>e z)1t-2#R-6?keG4sLLD6Uf#22lu3U2`jpR$tSs%2PNAZ8(w`U4t1j^Eq=cR{zl&_ot&`tpp73pYgCelj@j$IUv?vAadNF(Y z%YLc~Pe*N@U&#hHDy(9}Gtb%WNofMdP6J?LEjZk1U|h?X^8N(DJR>f(!9DBU=4WV< z1K)r>ebxHUy#szXAWC=i(k1Kx;NX|@F)|2t^#k$d&0Hyn4$zjh6~*A8j0Lz`#hM01wlGSiz##P14fjyd~O5JE95HrqIU zRZT;q)WjYN2Q%(Cy$;|p&J*`W+#`HIQ!~S=G0JH!z2>J?eYHANw8qyb8nQTBy%>K% z{6u$CZyBS2xTj-|=ELtj8EGlDbH}}#S2)pd=?M`^foY;cWtesPL%~%6C7m8H*D*c% zUu*3|be8P@4m`|ROI!UoKV82gWZV5cE$|NcUlf{#Fe4XK;loC@J0sY5$bsM0gH~T( zk!F_-Ja=!)U9YM4jegVjqJMol#Cif_e4EhY^*tyYBo0wL?)IR{VFrp!uqa#b$k5 zh>n}#U!KjsE>}Lok0*>$h zk(X?84?yF&D4?WVGV&Mttvo=@a!eM6J<)?ABL>@Zoki)blXYw%aLWm{ z2?qiJHhy>t;fADbwNM_qv%RG2AJ-+3Se=#v9fhYZSb6Y0-Q>t1PUIy0^0H4NGr~Iz zoYMt7{VA`FwIH{`wj|omST`Yk0F4 zjaM(fPmA#OIxD)M#rp5qld?+ZkEIS8v@ITxh=$-jHs;H>`E>VMBLonNHXof&RG8ex z){slG=&&+=AR{~5@{q;6n{ar6I4;B}m0^X>$kszRQY^nHAt1yoy~EKKoMdosA?e8? zhfkG@zH__#VT5=FDgcDHTV>$@-V`Dt6fbw@+o_v<}VI|8))So_Lx*=C^)m3q~$Ls`2}55 z-v&hhd+kuLWDxe|S(SveE)y+Q15iUq|(b%M3IGZ7(BPG&T#T-(43h@VS-Y^dbwSCshPt?VX@YBwcy zqUl3A_4M!N2Cnjp{KFp%+7$Lkw7~-DC82#wn+|j7`7b%mW>!E$Ee4pz!f(AEOCu1s zn?)+tHaNj$wQfq^#e%ENL+l+SCeW zXz6(QV4y8Ni1dS4_xMfC=y}Q+>YD;8ol?&2D*MeYzq~kRkWYb7*V)zel7kM4uszi$ zJE;wBD5pn@n=&j3<&ISsGK4Ia{3wGn$-@oa!etT5rh4LD~W=-yio_DLHSJ;iZr$l)Xu? zgz-q4Iz=0@Kjei*FKPj~WoPnPDAw%)-#T4mAS4&W)xcwXVkKi$8m`UA6`ytQj$(QV zpt|nqoG&xwU)rC`x{}kUNF_SYC&D&cx4Bl6e5V-Q%MX+l`F(PGKO$$fRkyDf7ghIfk0M=Ty46;^|#;pHF`k zX@v;LWwn#F-keqNiy6qw$ha&lASEx&_zeoVG5wpg=H0O4^O^D?iIaWFMg2Epn;Qz7 zUnW$Fn=LS4j96B_z5D)BNqFCE{bpOBq4rBy%US^d1tEPLj~!OJDy{WS8W5gc;-dZx zGlB)~c`uUtce7rvARHpsV@xhYE<>wzF*akq1%Wcra8-T~+uL5*SAk${mN&M1bTo-t zL0(Vxbo$^2I0puwQr2Lw4HPyuSFiLHINPcMaBx5XvU7_X#J1V917=r!W(a0}UV}mPYKq zKd7SzJrKxxem}-AU_;c?bF@GnQ_K-Ff?CvMM#+!+$?q5n#ttx;(iy$6c=>{jPCUOT z+1)-@WSG`y>`aDv-{LOX*v*MRCL`FVnweYA)&f?(EP!z*oQmhK&_s|e$}w()+yV`u znc+J3Yr>zBFC`}d@-ehp6?@Qdjo7V2YwKPHD@wpb!NOBJ9M1>tMc&#K(QX|GEb$%k zZ*@tk>2{`A(UDNzz{|t}Lc1k;Ypb=tuF#|i3`H+^#cRvB1@~zr_CR|NW@8^wHG34r znP;f{a0e9i{m;AQqvWLFbiCo#2{#>9WmPz zh!s$Q?DlOT>Rg6^sgt`g?D0P8e#}!mPtP{>!j2srt`Zty>{;6KB);YuC<8 zL9PxDSRd*3aRZwVMcF%>7ND=;vB#nvhDaUxyOR(fjAUytdEHiV=8FELy8{nd6_tu& zCuy-+z(YI;yYuIE)$duHDLv%Gj3#o+R*1W?bF<3+Z&3>8H*iead`v0zNA2Q8f3Xh) z79>=?{nzLY8yxXWxXhQSRpJfOiwtfeG{e&6 zAZ8cOo)id*#TWrMwz5%EIwnE+yA*9Tpb%*e;o?SLBq7H4+V*3&OHlHorQZ+es361x zSF2Y%7by!lKyKpioMPgyPzPsy3J81d1!SUun!qyu)wxlhtdS(PYIP1PLs8mEs+6WR zmO?+eiT_{RckeFPFVO{rz=bCgwNlh-(IChBlypbI#7&CUyg?8WHVc}to4Wljeb(Qi zlJt*2M5BF)UYOL6HGwrio@o`h|_JMQCL&0|Sw)0=N~@D10<6T?m$(fbpwl zYXb-C)wuPcrt=KDRPy!Kz560o9yCV9HbpPF4AEmj%0@%JyD{Rwm=Uo6BIIFSiTH>R ztO;PO61GxvfP%agY~Y=&{;IITPs^jTS-#M+AYs<_e->u6@N=_pCI2OarUVFM%)k$O zoTkmkw9a*GdlqN!q<^7CuoxlV1YW)oa)vb(Zx(=vg70l@hF&P?5EchT8+UwzOvQ+i z5l~B_^GqU;F&9RBjp=)*%z34Nalj6#=jTYzcXSdsjO>{AQd%H)&vo4S9^*v5UB#w# zoP*j?=`$^EG9%Uw=XgPUxYs05}6OQ*RMmV`9mQaezX zE0<|=4qWX(b45wcFW%&gS`*-vbfFB@Gu8s=+D?QnBc%!q>Kd9D!Zfc2^`Yc&9yNkz z(0#?Hb^<|sqGFx7`w*-Klq6oWLp8tkI6-a)nD8P$i~rn6m}ycIplpcd*QlD|Nstu9 z098$YnZi|#et;W9hOB;uGBYW<0hVxXwYZ*gtK^s>m@wt8{6M{Xg=(GpF z4XL*S_>WIe$IY#u>56frA-l`y>nCD=!m>?)_5-|4D|djA|b$WsTT=` zdUlST;LBTZr6%cCpM@>bFX#+a$0^DXc*{MHW}U~*RCA~)J6&9w*SP<`oZfcgXy^nqoSJd;}DWjn<|G6r$a7dfHkA;>S5r^{1%C}~Oie}qxXjrtL@`R-$y3}vc2cP8) zggR9t;VUl~JvMPKwb;i^2OMP3RhqZ#6>GPvN)338tED~xxw&%G3_fArxsB6Cw8Y7? z{{cuczt-!A5L4D0Pt>^$J=%J)Hkz(g8iODn_GkIkD?7X4$g$udKU2%4<+HnrcAOP~ zcaQ5=JTJz^VHJh~N%XWH(1GGk{nY&L%@WE~?fjuKdBLKKdrD1d0{`%AX>)B-FxWZ} zDa1^Nb9yA-3Kdf_P}pN!Fpn`)@)DRPJ(h@y2Mrp0-;!pjkiRJBiiCd$Heq|FLF)>L z1ytXXI^Z}cCXyJ2d<9yfrPsh6jHoJVh>N1&-x+@fPSeWK(XPlUMpHK#aH=;S1tgqE z%!oguf9!0K>5<`rFsmlDW}pI>1ok;)47Aw3IiSMS;zduo(#J=#xStpPKP^B9dc{7+ z>Lbk6b?`}kJBdjO`J2rvum9Y(4CHh+gt$lMFS>FXP=Lmux(oP-JsSoYi4qb|7~nKr6a*OQ>HtL`R}1pL1EdZsW~P@%?(QQV67yBeWJK6K@QiJklxg>$(<$ zDSMYI={$q4XUoYpa9S(0;@qFqoso8lZTK=}i8o1|m-H1^QKHG432u9?cdw)$gk$^Q}uX(cu@GjwYjx zB;|B27b-5a(6kDmHX6GXol;G`NKB+IbRIBcUIS83XgE1g$7XEfJq()KU6A-}f9#DP zR+%AEP&827=7H0J$O%gZ!gG`|yeRd%u^Y;C`$xwM(UYOvl8wmt;YV6NRkyhF6_^u}AAdIj%|fV0A`CHSuU#pGnNwBZdq zs+NXTc4~@rg!0Tv;LWCU=<)E3g-wTLyABXpJ!Ks{RkBxy7}yCll@*JA?0`ITm(`iLXKxYl0?^cyJBUCB^;aT%UiwJw)ju(woT> zIV7TsMa>qNJ~jr_ywcbsGQ#@=ZRfo8bJMYb2olWM>;+yi27L$%!u=JVq9eFKRgqy^ ze1^YV67}qd3OK30QCtPSIEM&*Fs!qx#w@#um+0` z>Z%&N5B` zeH5&MAVt}}q3W%PrwX0f9Mqy{w0>#uVP$x;a6^b@4Q37NHtPc*(sl6 zIbU<%(Y^YGno2FoogH{JaDj-@>X;umNgLjbbs+)$b6y?*lU+%7d>t|e9*DBRsYC#r zw3Q7XMgty_SY1~xmUv@YAi(L`|5q*AanORH181++*d&|$A4 z6uFX1IUpX5I0JX!p=mcJdz&bzp1$PQ`UQ z_d(6JpPu)DJG*y-?|z$ymU{f-~>8GD}x4deB~Er>J*u*@wB52*Fk%F_xu#w>_Gv#=bt_tCuF#%60u39gyZkBaG zq`TsRAmw?aLg6x61g{5l+rS6!<28IeoI1Kz6LfAUorRXG9{HMjt(F%sA9A z*srf%y{fJn#gD0!S;>ZH*v#``a71|}>$$-@sRz6Gp^}Iwu%jVCY57{rUO2)O{gh^c zrDt=;rg9d0fuL*-UxVDhV?m^!H#e`||ubV#xq|+nyeBi?z&$v}f{T9_1r&-#B zHenDUEp2;qZ7}&?h`h_3{2tGl!!)ms)g+|zcZ3dY&UIv4K;y{Ia2I^$VT?vUvJeB) zrl{((&*b-NF}Zjf@|QEF4jJe^p(F0;zQ_7M$3XiqFhT#L3V?Eq2U0co3c5BYjb~9H zplUw5pEtE3&RctnQ?3zzB)$#6o?(!J6|_w5y(q9ONd6{{Vq?)LcQSwlxbLy=MNo26 zs&lV_G=Y2;4T9zrU<1;#t+soT;I>c4@N&aUMjou`P^#kU^IN1NzAT@kcWvI-N8CfJ zl9U#Fg%53|+aEg!Pqjl@bB7)L*{jsf`GM;JZ5!08%5jBLF&M_`C^!#{dO1T4r-Z!q@bURE9XowW zCJ8;-Q9?|B9&t}+tD!4Ut>XIKkFsOzej{F{4{B=qwN;$cg^#?SwIug+2##4KJ z6m1Fy)dU7*o}f+<1G?^~iPilr`Y1NCO}8AxNzb zBWh9Z zkTadFif-@)J2LO(?R6<-2|Yf#)e;K?iu_nX9|xu)>iRFpAHr(EHGT2~)H?J90)MR; z%FH+e#PRIN;FdG^X^^K3XkKfm zS0LK&hCdYEfkbdFgY*Dn^aC8#5`=7~G2PC8TQPAejocXAcn?ou`6?~N1mqDA!?+JS zoHRL2zwosZT;gvlAfz+)Z{G zu0l6t^=zWB{wwIY4Y!CYA~;qZ?(D;4&K<>iY6)$tfbe%?8hZ&RKzwGL9I1Xv^Ld+^ zB!Io7W@q1ir-11v+!vpGh?bhsANhs>4U(_Wq6LO=ne%!QePx#fO!!lFrbAhrj=oz`X`d|8}3Z zynKA}VV^#(xmdt2q)Hm6Cqe%cd8p3Jj z+KVnDm(lqKmqAcAvR(~7phk$KR*PzgBS`)0%`q7~q<;cJDzACiarNEq{(S8y;WuFC z3^Wrr6N`5RP^^FcmqdhMRn1FMG{%Snz3Nu5$!`H6*zt}!TEwlZij{Ol(I2EkpQCc2 zkA&at=#!jwJ3Tv12t6_Li)GDX=ZVJ#O4HO~c2PNTuzPKK>b{^{P%38+lN$LlkLo*8 z+ouDG3bP*qpdy3_N(G8M&afA{noNa-Q*w6$Lcf`fV-RvExJ$|3ON}b`4oKKA5QBrZ zzoUEsD!%&^ZKaB{#rJ^t0HQP&mURf7xd{_8HMJkP8P(mloul?_9pW$n?gqNm%_%2}GdXS*(#YKX1LaJQMVjICI#Le)-mjgks*)n-I8N zZR_9-RxzlSRyaq#Fer%mLdylh5^InY4UT&d-;-#g{=b3;bNJ`m!d{YedMW>3Kca;K=f0*^tSGf8+}x0H91|-5A&GeR?;i z3M-c+!wYD{j9PD83Xoa${*V>rerQdT@?L;z?3h9a0UD=aY-h%~ zZW>U^6Mp}zjM~}t|KDjj)Ee{983kQb!P@uc(6U@`CSlwNDIBw2e?=7Q>qa}J&7-0- zRNK)(E}IC9@vU<$w^O_jqowU7YA`!N6|gBg?i-+x+Z}F(Uv|TBB0rxySJD<%{LN&J z<27iWt>mj!!u$om<8fj>^BRbl`7W%zsuaY_`VaS%f~>n`GrYk6Gi!E+Y858l(8OeB zAOZ93n8abyS18_=1S`JY5-XrFEjzudOxZ#QBu*D41WQn@kH`LK?HJD34(+(vsPf_n zg4C=m&CL5SF_1Yx-c$JmUYhvE{-gGzN9#nz^>~#~-|WNdoI3+x$E;aAAHQr)&i?%h zw&D$ihgrFQDm4XM4<-O~Q0efUpm(yMA=q*#P#FN4_EHtr2eJ*5%r9Ccu#zG7pme`w z(n?$NwWF`u>$^{F0o7iL*c+Q%En5n(hEQ04lt>PA^{knqH))QSCgt#KK635F)l*_n4t^UM zzI8%t$4%es=|siT?yl{d97iQT8INzI3*J|$i%V~~5At5?=&fY{Syz(+yRR@uj;ol^ zcNUKIXEYS@T#FTO0@6n@Lk1k*~%G}u=y750vy z7=vTG+b38GAO2}Le0a;{cHq}h<$YMcej_wGO(eo>Ru`sKI)-JpLmL62{46bcrCHM% zj9C`~Y3xru#VdW~5v;gdFLrLdnQPx%3A|0V+f>eSJ-I&px^;D@yE;o2s75U&u^KMY z>a;+M6J075q0GsPu3V<^Io=9%v>ZUh(CYs~Xiq|wSD*(Z4l`umy>zG|F=DCj!&fE` z=b;MTJG0B`auF?VWtN_TJPRHpmmi0aP9(BGGi_Ni+L74bfsbtX{C$|6W81@11X&Sy28p-+Q52jEQd+@G&~D5>NmRk~?+_A)RAz z{cD{jTQDWpxEL%oqMRW^jr5_dnLV%49~be{i;{WHer8S=Pi0Q8t6f}Shs~cu(N-(b z!RllMh@QJv17n!a#vi%N*=+LJk;aQ_U4PmwYV53a?6?+xE!mzu4{NU1Id*<$AAZL< zc2&!gu)SGFM|A6|>m605uw&yPPfCFtqY;E#47Ak7?MKESQHFMZ`Fw?~`Z9wDT4m87 zbk{t%BS$ilC!5Ka)wC-7-@W4+F@@;^?O&`7WsJm^syqlOQ2|%!fx`*C3Xdvwao!z1 zV1hG>%&lJzV<&6v0uJ2TX(`y+F?;IOezFQ!PvsNXlP>YWc)V)tOY_K`OOhnHQokKt zY9&UCJ;c)y63KU3T(I$v=ecv0VRwyYXB6m=JZ3I(zzoc~9PU)(=h2O3d@snqjm&RC z_zN1jHdYhp=_xqk-Ki$t)q5bD*Z(4>b%gi6ncvyZ#*D0ze({J}i~?@u^Upiqh`1&% zSt6178UOQ zK&iMEL0Un9PvD6oOvQ*XuSE?*@8hKggUHC4H?nD1%-?LmnmJo&?V+WYB%+Gw*s8KN za``=U&qL<;;UiX3#pK-1af7;X`2a#b4}h+xXWyEa8iQoM%y2zWh4X2pw9d1oqY0-D z{>%eP=gMUS3DAw-|M!7{5#G)DmBA5uFBrrU;V#vtU$`Zp((T8HLIF)hPXV?L%U`1q z6Aowizh60_rKM6I{_86pRV0fJ$JpThNML1~|Is?8_|U4YIZS8KBv%9%K$L-!Ilk|2 zZ#cy3P+1t)FVxsmLa_pJiEsBl`+c`Ng;Vn;Sw1$&hgILAN403KA+A!UJH;9vQ)oS;FWf zha1y-HE|W8S*;T%4lyIoIM)0!ICLZ#vI@Wd_~GzJP#_GX`}eUIGZK3rK)`wzPMA!( z<9-cx!|sj>)p1t~reNiQ(c}hF61X_i64DLfGW1}{$ly76JPYlkVi18h@Y8a!7lBYx zvUzd{%)c4TAD$EW*ZfVH8v{nagBU(eUi-Y(hzAho&R$2+sh?Y~{Fez*OS<(rz&FX~ zbIw#@Bn!f*cDw!iUDn$@eS9uJLcqq+L30vvhyf+iecK!&=sL=D*{;<|%fkF&Y=$z< z60eC<-bX$h$7ZN)2MctxCKq@lofi;_UNT+f*eyx9-%FLt@G$ML52`LGh;-82%==e( zeA2!iDo^x-LC4pP37FJq3Wj+qAq7~Ga#HzJnRer+YD z?>K~E5D7EN+r(a~zYk!}%gxHhCPvzDpu$UE0PauTUzu34dvHfGbwly}f3Up6y$fzhbi92OosLyHIAYb9ko6lFn zp;9~7d-hV_)xqx$9bEk+0FTh-V$OecemI1j)gc7aNtuNnoPMJ{!*r;1^akF7Q@vPuONd| zip)$^jGw;1;C3suz_PJ61MGA6d$H8`%vz7`IX*kvt7Qr5D+}!zdJ5Kttref+CnFN z&*nV^Mxv3tGql8_B-YXU(HFkG*%btyztn$*P?+QKU3t=rIBZI<6>n9tiO*Dd-9aK07 zOaF*YvcCj>FCAYwN~Xej5{)7XdL3~A2vKS*!@_3wHu;BNzLnd6Qh5{lho(ZtJz=|c zb(0an_E276W&1=h&p?~+z{wN|<3w}3V6c}FXANV%0<*pcL35ex)P52=Mg5CqnFqTV zf7T{wt`~r?QMYb*c*{~LEPrlO+^hTb-1<5k0l~f;dx5T=Jzjc#(*Ag+BR}=G(WkfR zOv7QFjAveH#?8?)`|+<#h4YPQRo-<4)O2d>nQjZ=LIluOQ_9bd#^HV9gm8C`gqZyF zA{k`_F^sWN!d=Ak78p{QH%2<8z-s7Nn94CfRm-SooJ{x38{kfD^f`s647e}VR#sLN zKB}KDIGZ-+MjNvkC8EUUU^4c-6J+)YKB{AEJ}e!xBh3u4m}e}r*X}0YxMSfo1m3sF z55vk=MqEliXfTvK(q^@?wszp>7_iy?pB8|6Ie(ck_Zlet>5d%qZQ3&9AmMg&V2n?$6ST6XNR50s_Y%21!Z%U{tu*keIqJ26H&~EZ|^@=W?ZGK|Ho;rI@e>|E?EaAJ_F#WK8H?SewkA`-SK zFq>>K9Dbe6Fe~Ja1~Z}KsW$1BXB_xZ?yb7FfEUIZFS>s*F_!# zpG!IymQkP!Hyv*EfebrO9BB|gCgXO@PZ&`vFk`3|ka1~A(d|&>U#=x<@Q199%8L74 zInSpPAX&?A3;Zf~wAd0tz7L|x1w}L~2eqgNB18$0HtL35^zsb@7F(1FDX|zBuc)pr zi82>jKgBulMRVrXn_Y~t8$hC^ps|UFR!#5M} zpK3gzV>$APbA}lrhmGymhOl% zezZ-oO---X)|9Ozx|oZ23kbJn&<2+G1OO(L_S9q|-{b6rR!>vb2l#9zGc#}HlpwCC z1G8cJ_QZJANqYMq-@m#n! z^Yp3dK0Hp;(GFM8dbEXU+*+=I> zXok3JrCwk!*8a2Ae1{q#OQ=Z_c$5qv7QZ*X666gdK*%8M8@bR+5!8SrR1t|!f(5xb zXef5B8y_DZ9A0qcsS2Dp@5fK}oZkomiJJp0K0@_5@y1N+L>uu4f-C!aE2g-(cmOtb zKp(MvFW4Dn0*GTFGcNGXHfxy=BMh(z1??x9kMyr78q+N-B&}W8y6&1+a`Z2;1mvVehQwZ#Ep_ho8S_4PQ&YfZzJk5gB@Y2t z{0S_gvuPP=xsAIN8w_uNqb`sFF2Lq@y4PDkVc1$r7QR@j18nO$m>8>PLQgn^ezbDJ zX)e&Mslvn|wiOV_CqN*{Y4Q$7bA{!0zaC;CNqaVE`KwbNgRnw*;z>TkNZq^Zuwmdd z1HvdT9r5(*;)Gjcg#?nG*vMwy&tb!Sy_q8&o*MDrS@I7 zJd(xyniGj+w4~}?t^AC@~Nv^*8i(@bLeRERfWl!&TS~9sbIy&lhK*Yay`k`#h@Dgl2DW$$MQS0gJci(|$F#sp^n z=cn}5fJm;>yj>hz8Xx<b+$;)=H&jJ z!HUmluJWj{*D6lvtk5AOpgIt?3WZ9k66|gCHg(}P zFXCW~(Io--L_U-Hwj9k*pN?%J1<7K}eXz75%Il78)S(pZ?KEacQL_A@oAa+mactBj znH**$@u^3gtUmt!H?Claf^U4~c{5lGd?s4Smk}JhZ$$O}DQ-0lgnSWppp@6vz6&vT zgxRB1%a57t%RYX763{aG2b$Wp?eCjp-%9%f(+5^Oqm0?9Fc$e~mgC z=v7Aj?+zt%%FDo6dKp3<A1B+Yf1SFdu`D;L6ZD(sz9EF_$J%5nIXW z4fC1ro?;6b?$P{uY+~}s);WE}BCz5~bM599Y=7y1#1vEAttEj**lO&6XfeMHuV<+KRs&J2?UJj2MJ0aJSbWI@rxdu`z$YdKvzEj8`2LTOr=@+N{{G z>X-1x3zE-Af{%E*y#dY6fKGiDrS#toM8uPIwZH-s^2L7_p}_K`Cag_f?Vk+&{v!iX zGxPd4sps_n{uihnK4{GM3LuX7gX0<+Y$NF1vft%dg!a{qn8hj>S#SbIg+!@lA%&ZOJtJ{~FuEQ;@r%9#82 zVHKnZgl>5H8voU*u@3DzzsedGM}gDBAGe>cUhUnnn+M{+nDPjzh_SHi}v(WvuCU`}Xpaokb7Tfx)Tx^~=>OH5-^!Nx(-(ZaZymjD2qw zzE1o0Z^yJ8zy-wg8A%?x1f_b&hx~Iw4)X`4;MgVEj-)<8+EpkEqkJBZ315}Co93yR zp`^_%)EX0{nWru_L<5Ck*bYnD*kSGOFt_b<5OKcDU}kciQ)83xwA7!>zz_LDBkYsQ z4;8GFu2%s5ZS$3aFb?bmXa{^TQv=x2H7LF8a6nB_YDORG;=!F~mF~g*{uLWn`$F9# zj<>A&PCJd)=FWy7e!?4j5%cLk^VWj2ZM{Zl`?$lex_f^ zEgaE1JGN2d8?$wF&(mQc$hz1b0aPepJ{|A)3p)WRQFP@1z-N{UQNoE*J-o{)>a$-` z_oIk98+`m5R&?x%RtKT*t{C%;^N@J}UGbuh*Ae67rr2&UU){+i6uz=Y=3rO1^Qk?E zqEx3gJotD)KrN!$dpJn5AHMP}E_xlNj4Ke}Jrzih@x8rsmj^Ksx86WuUT{{ucs#Ij zcx%SD>UtcQy5T7;m&VD-$sL6zdEB|XVVDI7nAqka^-b&f38LIb?7MBBwX(6c=1U>) z9j8TcA4oD_9ZD?)N>rw`=CZhMH(3f5f(?ee@eEdM`EW5sx)|<@bqb5Pzgs}t>Fqiw zz6EyI&PI$aZQKZ{5}$oS7P!wI6-)+kw>d(G1b>Kh3D zUDW30Zemee5vgoYiI|r9RFzK__3S#)nQnJ<%=V4J`c?Mfhg1qJ}##t`BI?6lT zJuyDKUx82>`jP(!AK=Dik^DWl{Fc3@apeOv4&)RRfq(n+_pf;}oy);Gr5=X@CI|Tf zDgG=Taz-RGKRay6pJIz@ISg+!qZXa(xxX+r_!(vyAy=1oRl*F>9>Zglyzh@|ZOh2+|1}DK z1DjjD=H(BY%cQBJn-NSN0StzW2D2o6a=2$*nj0^aI*O|g9i^nE$fTZDI*1sYVmKdR z&NcAGwRcJp?2b@a5XY5e!UWO6hZeCA{AEu2{5+iwPn9rj?R(c|Yja}I{H=K3KVZJY z(d)lEkXF)#8egOmEeN>>xOQwFg-vhSxsgExRwkUf$JB05K@4ql%5?}iK$IXQj~sLR zL?e`!H-hI)9${{!2&VD5*k=K*Gc6kRDCemjdU`!-d|owdhdwQLUZpT*ve5A!LWo!k zvH`4M!I;Dv=vgV1rS<*KXL@QvX8Ob^fqI1l!7E2C{)T-?H+5wIHqcZGXB53jP#)9? ze+R;sDhqRl6Vqu>3=P)FbY8;zg!GAVnbX@L`T;9pN_}PH^XtQ~ti(`uA4S$B7B*rK zgVLtVXgPqZLx?rD29_LJv?A~(UM{?30{c29VY1|xS5Q#(M*7X~0=H&qVrF?5PP#mn z-&$HK^#R1vT>l8(0N^rsC@42Lu|^I3{KEyxda@Z8z~P7{y4JAatkPG%iHm2tu^9Pq z$k)Dg+;e`U;jT%)_4qKlQE0I4Q_VH}4D>LFV(w?R1^Td`v)qbeTHEE&(i|z0#F0p% z7~Io$sJ$YkgKoO1HO=9$`3WIrLNO}~xmdWQNw?#&o!*BLcMbmTzdsfhj;$QU%J69h zU4|FNFVcPw40}(!$^scV2J|O45CP1*&F$0&DPQ|{@ZisQLhNf$t>y4~P6~FWcvU8F?tETVX{|eqUqrM3`wr4qg^2=lWf`c=y6~~8pcb41iZ9^HzI0(Ld zl+IPR`R7An9@JL?-)=txo)jezk+~Ql-}}5Q0#4`7-~T3r{OILOJk~@LYz(`Gi8g8> zP1NR%`YU~+E(Y_0VrVp$PUD+stiM$788T^2cGaBKrxCgevBD@??O%cjA>LW<&vbHM zh46Rd#kJ~xSq7M4V+w)tWva~xKJy5IeG9u-p=OjhQ0j5@vE{(1!`0<>kHC3`crSZ@ zt8*_dJ|EQZT)Bt&22=WY?wXek$FcK}m+!zZh*aVUssc$W^EO?p4@8U#>TbQalzZ?0 zKc22Tp6dVmzppDRDK1MfRD`8@O5&lbfCgx-&3YG=rATUlAz!>-Rz z*hhkz*Zg%H2AfMw@OYnQPae5=WUv2C_q8nHCAo$5L*2vo{lk!ll44Tv+yF720>rqX z?zB5h;B%v*DrKF%Pxf4qb8|Ni>{|F|VcO$4v>9Sr%6#Cy*)2Dw#xKH0;?waR`J5u2tA01ArLU8j$V84pBz_5Y_KLwGo;)f`KNTwj zUXk!z9E71>3WOy??vxV{a?g#aHHL(UVQPJi1Q`O~qu{u#dMm4;?Id8&R5RF;TQ&KE zs$oHj=W$0^f^vjumcz@tKTPVBcfg9$l#Qea$UvM~lCzGPyz^)J&SIN23QwN3arChF zzg!kHAK>-7Y^xXHg$CurZXcR>P;Pm-02=td8z-!Ii^7~*2eKn?#V2t_TsbsYo?I*B zHN}lnHD}tTJj5#>O((c%2&RzXd0cedWH6hX9)`XAGnA)*HaK%8OSz%iAsA+fP4XT@ zggy{r!6YQGiIbx$Z7WTo=CR3(ONTdE zHkU@cc=vZmlC76aRUdR_7&++1@Aoj#5@=`mYPB{RgL%gYLu2q~n8QZwx@Ay%;9QB7 zsdGA$^#r`->ns_fVo7jDN4y4Wn7{WeTscsNi}YTSNgk3#!%S?L#IC}x$ryQtRu_GS zEfMu@JZpnu#nI49{ai>)a(*j@M*Zc)PXzb$@Y&0GnPR}E7H@XBO^LDZkXcpgS2K3_ zagfu9sm|QG{be%*Ss?4uwGOB4sx}49D_I1XK=CFO=HU?Upr(U@_LNPjriXE!*zO$q zP8>Tz=*vRUfJ$d4mnT%w1Kk%$z-f{bZZl2^|9#DHqa{1Zv%jP~uo$6bOVzlE6s!=& z;$`2B6>7p6yryn)*?8wkiPJK0KDb+B!=c`CoZ z`VG=E-ofp#%Tnp>QJW)oECbfrLBPP74rNLP@n$E%wvBf?i_*GtC(nZV3hOC z8Nw_+T#M7}1JJ?&7=i)&j{K;MHOGC`eLbJ36Be!^T($;MZBa_J@#C;F>Nq^>&ZSKS zKEoy2Dh0gS?jl)*gcppNQ`!k~hZiPc@Je+rqMCH8_683gwr|TL9EWE3NO6}Fp)-bg zl3v94Gtz~!{)zC zdF;0I`+OllVr3z7!Til60hp$MwPy)+PFtgC6%L|ZjIozN6Bu|xT416w>rc!d#aIWd zemZI*oz=ks52E1eyeDt~WoVpQ&wU3j-5ZsH8`KhIbZ(jKXiyl{ef=pdC2cwh#3m_l z*P$|A>D6<5QMW0o^6bLHfbw?QTG5(%*xACG!*YiEi#5_k)(S>1-oGP0kJ}(ewWEhD zCwT#H1&*z(Mw!2a)>mmb*|HEF6Y|L-gpF{`n^eVydE?{P;f1NSJ}H6_40Pj$6bF4| zR8-V_XoQcxOF%&N4K_y4h^cdq0v(F&fuow(vF`iCx33cy9-KZA9UwAsgerl;C+gRe zO}_HD?lt3BLBQUT`*RG@)D~fz%(|yK_z(;Io{NB$^B>QExK?H58j?+hHB@ByHIHZ* z?q2_+1=hM13xRkaC63_$$w*Anke%-p1%h**Um)}yh`t6C@e{)n(HaBl7OEy>c@(>M z?&w?2Sj<9b`jmIu)XiA6aU(~z;0);Vh*77YH`qtln(t5me(Q();f+jiaKJnhTHY%O z1b1f~gT(#VvS8?dk<4^@-CVfhP(P<@Kc+2D=v%C=CMOV=fn zzN({-H2?eN;S+&h6qtSa{m7Mry@2)8f8$wo6NBJc$|mz3#;i~pD7?U9b2WsSU<4nA z4}IRx>=`)_Td%l~0dFlcqC7NgyJLCZt?33PmBDH+_dpBz>t`0v@f^M^0lKSQV<~rK zx>_m}VCZ^L0O6PF+#T3qO3_(y2igSGY!0Jwf`|3Vog4m+lZ$7(8eJp+?X-X4!2A06 zh&(+Qp&%$x)u}+wuLMsNI{~>`2KBMXvGmX4As{)+Q!Y>^;$w{VPR6ej=4W?_5`@G8Qn>z@C7NW39Vk35~rcNQ`sfKa-*EkIQK8H@1 z{j2N^m4~7<7O#=+&0nh&SiIy-75mJa8toG~)i-+`QMv6Nh}gz?p>9^BZXX+8B_OMW!}D*OSwF24Z8*%uv`CI4o2`m(h7!kqf_iGBRCv)7lM7bTX}4bxrD(h+h2z>!-R zRNfeIEyy%vp_;G3LQeK5jn`&k?mPDABV-ONwCcsSd|g}I`sK#%*1o$aB&NKHR#27_ zx4=x&78`|V0BH2q+3PCJ*_}$ulCZIYblEZQlX@jb3eE%^0VKb#Zk^^{+*nMD-4kZV z-%@{^nbZzFnH=B;tnv5V=?0o(5;vm!)}DPi4we-Yd-@}62PmFER1Qp2Z%|!mRKkqf zT64rye(8D9H@84_8Lik3d`=l5BRV=#QD}?_hXI#`Z{qqLJT^MR^p44Zt#N!Z@f|1* zl>f`h2&ws;nhVSX$1R$0NoWg5bcIF_6xd4b87Ubev}*2v0kG?~tQ}}=3rbW_Rvxal z-}zkwrO5hdwqc_Z4xya)X*jG`l+-4huBKqUah!1nPz{wKZoCX|7#p(&dC+IYx09z>8w^gP=o6t#+8$E*f3lfwTXmHXm1(!M0v)%@?=R zAN8RP7VoHvFKV>t8K(OC-roWt(pxy$V?UaNSEl!{Oc_aqF*1iOS}#t6bP{BZa5>SD z!jQ=yB0DLjs+ixBa`1sCS8WMc0E8VR*x1V5d`QMuO;XsD;OW2A_JQ{IiL8P> zCC9qm%6sAgf-z96f@2bM8ScFG{~-Hz&o$CcX6F`=30;UvhBAol*9A>SY{p;?>YfHx zb@M@#Ddbh{9Up$4sd*lV?ob3Eh`D{rr=101(^&D|z6ZI0jkXxg@_kbQ+#7!neG_^v z&^p0xb+CA+ZFLOeg#OocJBg_7fv)M>aZ)>Fv{-6##zNnD`<3sz>^h{I9)a)1Ip*L zAJ99U2T||h{tUI1|MddAqQXCcf(nD*RjJ#A)Vyye(0&La$tGt7XPSSI;)1S}dGlxk zz-%q@hzq9f;eY)kbZ@{sQ^7pRy%d}22U;ygr8`j&X03M^nfOmlL)pJ`|s%=O3SPH*wOOp%&t=G`YK$qrpv_3r9T=nlEn zk~l4>>ypk_c3{WFz8^aYq}04r#?(h;z`|Rx4C-9p1RdohstyqB;C;mqN~LY7ziu!7 zdR?!CAe<3{;%+#khX(aDcjcgM=b3|`6Kq{e9Wv==LLPM?9e@NNj{EL^kSa}1UVh-W z5`LqCZ;1)ZJpMLwWdY{GR1mEekUr2;%>ycOKxl&wP|78Sz{5lq^bqSN7Rwc8X~*H0 zX8oiRzI3?~ed|4P4G%q@%R6X`h|1)NcO4%#$iY(%eD{&j=s&6}$>U6-`|56axBvC` zwB>wtQifdjF#^b!&Yv-oP&Ge9_Vp5-{>)8R{k3KU{fL>L)cwen4~7dZoD z?v0-nD)tZZ9bB*etuLAGlJ3z5hb=}9icx(;u%Fy%(wres(tDgwF$Vpf&vfG7MUShv z=6#=^7lrl^{!K>&9pYfL`v(C_DpoHyseE7EvF)D*ISGcGs~lz&zEjACRDS<=^}-W@ zaP>Lo1c5n@n`%iQg=URU3)+VTNy!azEl{HB%W{$bSHBp@=PvsLly*yJ zk@GsE&V5T=N(e8j?w3)SV%#AO(U3iTm-%-e|so= z(nY(yqFS{hoYIY~blQmYiST}WSXay8JpE1Z1J&4@Bzh>1NGv}# zV+d;)TC&V)U*!x5^4kCEdbt@pT(>pVNxWj3 z1cX+>NGwdtP4QA;a;X@7+|!A|1rMi=QYAJxK_N(geg+GMda@?5w3aGA9UK5{Cy8e? z34pW)1wtC1@P~t?rMS=@wh`YbWCq?UE`V=BL|JVuR!Mht=fY}pn0_X0tMWlTO@}m9Rmo>)MUY;mv^dW|RUxp5 z_nt=W4!V_#LXWRT=!eE33VwaVqlwSDsLbt#n=+r3D3Le7Vetzr zrTpo110S{JF91G@=jb%rTT_>rK(7Sf|DWp;u8V}qq&qo7ql0u3xq8l~%4l(hT10wCWwZZEQ zo8?&`U-7nwHz*eiW9jVEWAZo#e93BEa9d6uwG2qw7Xt>T`OvajCR{aOD6OV{Y2UFe zls|A5-fk-wH(X&X<7w0S;b}BLeTpn$8r++)E|vKc%BG&KfcCqxJJA#NGRX($)(4uq zIUxS)E`nHcPJ39fup0~dDW9@`+2`_tg!LZ5(qe64lE$EqZ9bDU6110op zgebONa}0Rz{RuGq5q^5l^CLaUg6tQjWRLP)VSyx@SG<-PCf6T4nnNjOvC|kZCB_~L z)`UUqNr{Q@B8NgMp9s@-G6g6f4Z+~|1mj}f&TjgJwz#Ya4h&YbMB$ny$ zsN0f6R=9pfA6~A2bz+V8^(0DOCqn`cX-+=u|3Mj_w zTU*|n53nf}fx|~RMjO}O#F_RHcWmY*{^`Dw%k&R`L%S5C2k?r$Y~NTFowrgOO@{fH z^G1Cz^(BT|pN4X8Mg1no3l^+?3sL0YKi>_(7US@tXB+vWRAQ*t>$G?X-SZb6R4e(s z+Pb(@*M;=&Os_&$G5K`ildUiBL}B=mFYYS|lK3-FM_1NWJ(m2?fb>l!x%K5IH1=Fv z*sT~TgHUf?j*`bv0P(n}_*r>pUdrco1Jr&J*K7HfI0IqlG=^p$Po4~Ffz9V?RUV2c zlitqoj&LgV`Y#O1+3vq!}4j9HEjBa4kYl{Vi_5{ z(t4yQQI+UpmG5V4wuv+a1vn0j`LIo2teaair!5J~9Y@*^?F5ybr7Zw8Coj2n;eFK> z`q6AqoE&&^Xy*pu7a7jvkEHoqIUtdl;ME=j2KNXVWE@TM(%U=6f=Gu`g^48_Qt93R z@yFuYokhHtaf-lKrVc8{(GFcI znz+c*#jZ;X1nwbvrwb~rXQ8F(-xSO&xqvW*lVBbN8f*SKSiwOz1+aoOzq#Sc!zcsv zp_qT`uO_?MLXP8qB16SyVo|`tp3oV``pi)M%WRnZ=qt#=deBB!`Z5Cc%2_N5G^Hs% zR%@_rikwz1(V5QxV)CLO*GB%2NbYy#BJ*ck`MQKRLs!ePhLno?pF^8Ud00?F_=qDt z8>*twM@D33-M3QVHv-DOTx)FGrN5>B4WfKi$lJT-Lm>08NdO}ddG%Y=q$z5HqkO%_ zp=Ixvf&t#FetMmMH-O&*8XrYq|IqW}(Et_{=ru{A#6+Orf?tOFPj&07JhnXA?6+@; zYi#>@0n5bo%ArNablk7Q`o>(svcCgIh)0{(Xg2bw&_5#m?f`3Ds)n>5kZS=K9uj)V zHIJ{t7L1(#JWHnL)6nSR+x)VjGE=Sn?EihHvz~IHA|f$r|2GFKNl9TG<&;(-{3`Q5 z!->^~Wxt_JLIrC6^t^nBQA4mwlU#;)c8+|cj0_paVC(eq`KIt`Dsuq%UemdqCBay^ zhiBAZ1O8dFetiWx*qa_yQ)P>{abGVha;I~1_x8rOvX+>MNq-DuX=)JAHROv{P{YZM z>7j=Xtkbq@4QMSg3>~=B4)Fa_{D*bl>Y>y?mv+d{UY$$k7ziJB_&0lW&G{!&J(42VBS(fzWzT_cR!SQKU48oHOY%aU>u;rBwRPFsDt0H&LvW_$AcI^e=3S)^Bbs> zq9E`#DFYGfbTkibFo({Mo4ORQk#zyKE&}<|nd5;`!tMM!wJYZyf$V6&lc58c*GBB> zGiwL<%5M;~k^^>EK2{EzighzKd_T3xY@_>l^=K!C)hfB=Skai$yl>B-xfhrccx;KxC_;J6p_0A`B#IqTNS&z?I(Rwbo5X-7Wfs{E0@ zM4n?|OWIngu$nRk+C<62Xrz6^a=)2AQ`#)8{1+rl-Q*ByWkN-{=sC_zx!Nd@SpFhW z7JK|m@pV5neBM@}i)0dqDNYiaUm9nab+3r%_*3fh&Y!@;hUzlEX$=eAN{$CXn|c=5 za!R^4bXrE(s^WACBWr{G@%p@s4iNo71gsEafbzl&h%Xv`kh|*_Ujjp69TuyeMl995 zJ?BtO_cEf94tltI(@}t5Y~&2$IR6cOu|MQ;Dq^Z5Mhz8lKf`e}Nm)5Rk6!r_yf*-a z7lf4As0WS`1wJlc)KbOzgi~Gd2va@%9}r8C?v#Pe@0BAjP=p1Wq2=LEVZw=Z4DEqh zI#?VKhw2BKlByj{t(OWd$o;nQdc2ISQMCdvz0Qj!a&?Hg_Fbyc z(*}YWN+&O95tHzxuRW9qDG(52NgX+aS*@n!P!w&nN4-{HLTSKwDLP9`z`g+`dm?#pgbl!sAKSvj~xMvMnT#0+S zfqD3`JdEED*bgvaRb$`LFbEH(^cn;;a{&R@L&|A7xa~0o*7ZAhz1`o9bsk8XI8_Z#8#&lH~gLnp!sjYFtQ%j%APxXXs};u9+b;lo#vk6z5CqZcyEB=tCBv-z+)TSX?roDurU4@ ztZj*n&tK2iSMyBaQIonATIc;y_07ck!ofFc=)oTt$U~GS=CYeoPp2JCyNsB#s&fsQ zjQWgS^+BHg9bSia;-Rs&nU|A)&%G8JIN|$a8&Ks@M2nsc(8LVA6h8?ncGe*JD4?zn zlM;8)U57kV$QLZ~2om{Ce2w;Bj`?|n3Q*G>)nB*W9^fv6j&j3el907`q2K;& z8*;Jb3JTW;%M+1SawmdDpxHOxPw@Q z(4S5+T@ex?WpzlJ9(Ax@+^)}eL%HF z?CNyGhL6{Gkf*lZ^gZICu>}uNRabhBf<8J;=egO@m>Rg3E6krFMuZR#@C!%`dHBF| zg8b^78Gj|vH}%H7$xs33D;S1$V}DaM0(N4c&`K{7{2`w_&bk%y^bDGHM_ubOR`q`F zO<<}oMPzgAe$#cMXtv6~Vezl)n|5cpfyt}3GjL?n)_mNb#pi~sgB$Jcqtl})a+D8dd%AONU?gqSOx?NUQ)&%;Q$J`!oIcbXNmcn?# zi4=(?(yh)37VnE~lBB?u2Q{!L&xc=RRv88exiWlTk0`0A9CFA{^6OuK{_IIHx_$%d zu%GWco_aHx!;lNlBR7A4(Y$@6Zh9w5!cVeiko*T5?pXB`ZYq^;HA4e#Nys1r$iXgW zCQ#$5vEQN9e783z5M~TGp6EUv6LgF2Q?H!!ueMBC0_1Se9(H8I5*&-A9KO2ha)K1Qn&{-MFpV*32EyDy;H5G4~6|5Y8BVBl+n}`;G;NlHXkf)A8$9ST5MUF+B+F zB>oxKByMUN%NR)~w*tUCrQ1esa#o6QO{31B;U}F zNI|6V3#wXmDOgvrsu3VH?1X@4plAmgDttaaSRHfN(ko3jk}9}IxlEToGG{A$Cg13Gq%?Jj9f+&&v*wCLBbhD`Om$0_QeZm)^ zjL==jDD^Y6=xg!B`0VV{zgS+=hb^8Nb;c{ z>pu5Clo1U5@Z!=l?!6x|Asj?UZ^8KKslb?^_lP)qyEZza>0(MoV>LI>xKy!4WzOV? ze850XPQ)`V_%+k?)<2k&!zjFT(3z3#V2~o&ew)$A5xq95iQ|s94J*;4Kn%yfK9t$X z2GmnXgx_;uSolgDn>CxO1S@m_l)+a+Vv*Zu@T1jk{P}VB6$O6F6?#%CGy(zkvRfxy zHO{;1$ia)wKHg@?@c7;o&>WL7Ug)a9R^GbeM=1rt>h55sb^JyLy>fcAgDS{#;?i#< zN@4>zZ!#D#kq>tMj_%&WNp#Uq8z&lO1UEZz6Zf)b9wD;w{d!k3=2jbZ18!bkQIXB{ z_(5I0F?Dy6MX1c9imWWbhOhE^@R1y(1n8k6dXR0EcQ%D?DYeSy&>Q3D-=HG{5KUno z2M4S4MUvu*EbK@Citn6c;l3_@rm){&$;*z`OjbCWa`$vT;(TCX9dwhMW z+dqe8T`RXuB9jU%6ehlI*O=iWpH4VJAL*q-(h3MMBS^s3#eo4}klU%*QFV*_zE+SX z2-QmRQr`rQJQOr%9&kov_;iFkRQcoGcLe?~-J7EM{~rZnIqpX(yoVAc3__I~X;!^{Q0p_??!sdyr-5f1~Tzh=;j zZNezMzLf7Qv+h#B1?qLUl%9Z}oo9*6US|ID^Kxf6=P$1okwGC!dT!mf@xRIrozh-I zB{jO~+j#D$M<0ITOrXc&iW=>$P?@Jp@1D8a8Kox&P{uI&oF3Gx>IQmsfTK?5ecf}2 zVAt+0eX51EHJf#*M~v9LrmGGiGmnxh=|W|UsO-UD$wi4ta9$6`E?1*J)7VUZYP38F zlIfXn*bg!_5519x!*&(0?W8#fom0?gJy9?SLZ~+Rs#wey`Xs1JQAcZ4Kv!td`>?f% zdiUUpzwCLNduaM2Yy}G zKU${rEqBw6sl$8+2hSOW9kz`<%Be+(RaMqL*mGVad1vB(-#G&SU1_^clA?bbhDC~J z5G3N!Gn8B;#=K`1!G}i0#>31uLa=LU$Op0Kw|7D!HL^N^|7DMMi9?{YKrbOMN zww^Pz+Bd@(wK9(;qmH3i7tsY)SqxhHxSaS|D!)@?-aVv@-Dav{-wSe*@a=zcpI+3i zlVzE(Qu1vf%sKkA?&r)4HmOf5Hq+XvnFqSZS2{M$pg_5lvbICfqZ!R;RSsP*A)wSC zPl1R=peaSQ-&GK{nFqAEvj4>SJzj)1&XC8C4`w(XJh0WAIlr(Fo&57gO7pk;$6s0% z3buQ21fS+Ljd1b7`(D^VGfae}rNdXpXgcBv>8q_3--odL)K4tZ5@GQ0B}Y{~46I5U zK4Hy>F41Va-LFC|8dWSDpKaHyI3XHWJ%L0Cf(fFBEvvunxa=f`vgEgwir2`%)iPXv zK-F{XO?l4@awUzsC&5sefoGBOt)gCP{gzItwa==rPjOG{kOwDI$2XdORzuB6sg z5}YHwjDfIBQ$DCtQ|Az`y7f5rRC)X*OakW0##UtY0PxdZiF8> zdf$C$o$Z$>Q9%oQK6=Z{ztUfKB z1WZ9IN;FY77B&+>xqkIY)N8HE%F36rY}uTWPOq!07e=jx$a*Si88jmzo}H(Hd@zh* z;|A9l@}4C4+!&HfY)wg+Iu|nl8*-!RPkB*OQ;$#Gs`DU!WTMv&oSW<2P*!gA@88 z4LNlh9|o;x!+>%-@AlC+g={SQRrn|ADEj`)2mLJLwQ71>8mDYsSh_MFMS-dRqi82k z66@ohKx_4y5@eg2_Ws~p1Pv0ME&ZL|qY%soZif75B6rjjW_7J|rVuS6R`aGDZ|&h1L+Rt%YIR*GBKnVlT! z5yhBOe2y9Yfb~Vcv`eLEUj8j->2qm2fv~|$VpPcyqdHZsC?&N=o$KZ6>uAnqXVa#a z3@38>^FC~-&oSA@+n3vtXq;>;e%4Z4Q3NW6$8G12>Cy(iHHPBL)}Jr3cY|PT_=tOP zODEzOYij1`hKMGPYHD{7xDcytw<{d%pp*L~+ODbOlgsJ(g~dfoaU?YGsHO4T?VbHz zuIk_*x&HHqv2IOL#QNt7A6e~8&L^J`?mIvG3 zd;8VtE=)eW3!~qdhC%xLXj3@mNrU82_IT2Y2pDow+-p2Wsjd|a;v1~?@jRz~@yX+* zxC(rY>bc;0tHH0|WRDiFiakx1bCRNQqD(khAML}AQn5{?zz6q)LAfaUU`fZHKQ-5~ zd?~DuTv-zx<^e=DZiL5A@%yiqeurcT&mlB;HouSMVr$~;jhvK(Mpl6S5~WM8)^9!R zr9WX1t&+AkY=uU!A?2I~aXq6(QE033o1xk9H)ALftMW^iFMs?_Rd~$dXH4$Lx}R9} zTqiIr%Ha!|txt@4?$1Hj{>z}ljoI}VhBc=6;z!NTs3~7bJC`*HAu+429mc^A-Mgn% zEIY@sYzzi0)3g~PJkenR9r&}77f#lngPqXb1a2#Bc+R~nR+`&N&865=JQ=Dbu`w3f z(5UcINW*5SOzv2>QW%F>2n}*w|9amSE2DK6LF? zrCC8RzN95Ndw6s+PD9%z%aeY~od&Fd#IRb`Rd#ToRbJ|icFYH|7g4E;pDzfJTxD=# zWb8hGIm5NEu&@q?70sjZ)%fhWjlEORJ?C3%uf#u>LKk|21<22A{C+dM&`HeL_jGw7 z>Jp*PhdY<_d1^S*Vnbb2A9FH2C8g)rt9FGWXT;lQAnX{u zX2x75iU6}AU@I|s@WU1P!Qq<_O$Rl$m_JS zGCStXoXt|4)K#2u0cuTbdqcmqXE#dY)9^)vT^}Ot*?_9Od*mPT`zG1b#3N4 zkYlEZ=bwM<OX@m`6! zgx|~*nzFQ|J?BAty{OhTkhLZ?PYqd(CrzwnMmZQ z`)TxwS&VuuR&J}lKIqy0S71_Coo?RzdYKw)+M8j_7u+{lIRaN;>`ZZmm+TNXR{k6! ze}1j<-cd9IQfM<}gFD$Xh!DrO*QPat4`aL02%bGm(j=4|EWk(HU{dLzPm=kp+6bq5 z{fa<_Onj!D8Mtrll|d_uo==71)~;RiM!~+r!t3Lo*Qwg^>?awXRUgWa@T9xK?HMh} zQj`)vjVt}Cb0#rIBw%U~u)zmTEA2Sn#%GsbnMpp0YpI7s)7)Z9)lp9^7@zg{@#7U` zdO|LAOUh{Oo|&?Q@~9Zyen163GSQ+D;363-1Ma-=@++|m4{F;Phc_Yl?;fPJUYwMc zMTn|W=Gsjk?&!ksEm5QRdcx$&_{SRQ?_mZst@rTHzPsf6*foY6GKeI)Dz)s1Xq*i8 zSr`-%vd6(pAAJw+Y2)wzI%IASzCYQ&R4H)xYnf`8En^^zO@;rsf6ltd95jRpWZ+-QC{@_WisRHy9?x=@kqpBDe~ zPuHmA7rqt0VmS5hF3b}#R~?<8jUR?`I<4%~An+28Qs2LP>C%0Aoml7A-@gM6r5~Sk zsUlGf+_bkp`y$Ux+@dP~H;VzT=4?JNLqbpPoN^x4_0+M7-Tbl(K^^1g=XY5-N-4`E zlv3SCw&S8n*4izqfg`H7l9Q7^(94Uhb6!Jx+#`9+DAz|%>b~PjvAx%*{leg63TCoq zb2wvRb%C~ny&#U(`FZkEV1g59>bg^1k%UKaa0^0i1HH#^sW|066e1c`&S8L-mLb^r z{Mju(kU^vS6F&aj4C=FoxA#mP&&5xu2H^GXF9qPAaT~E!T`k3)?KOME0%0vyE{1A< z{AhW7RryDQJR%cZ#P!qMHE)JTIa`ae)ApN7n&fGn62V=a=zmN_>=h<4!fSBEmRL*; z&R**XP-|=cyw*KTaiz-N`Pn^Vu8e4p`r%;3+_WC9NSwVN9(otjgFh-_l@ds-K&1i_J^q%d`cqR;@)K6b^6 zlzf3@<)NPjX{l^^DP?;_b|j5urT47+{Wt&p1a1GSy%XvT9s}gGq7FfHti`B2QU@@& zHT-!kK$bXlP}dF}7#zf6MK3a%l{5XQ2st>}Q52e4GPJSrmP84m?f&#RrRUOyk)xVZ ztx6vNm>Ur8hR8_1NKH-k3gpXv`?jcPj9A3>kWb|jQw9uW;iSCN%`7Qu?pdk^096&j z)FJQFp7$XIBWOTfyEJ*Eq_c`IVF}c5Vi;2sN$fYidZ=z(udAZHWy928;zVGOaeMGi1>d>gu5o;)$>s zmJ)`~@l-N$luYLF=uij6C4zucqDu(&<35OO5$su^A5b?gNbj8^iZwe&E1q726tvUvt)iZ;Z)VGe%v)CMkCq%D z3f0DKYT0C`F2W^N<*N_pX^X>Ra-ZCOcoLE(6KOjk&)5ns$dG6GG~5>BHaUzU#I8;N>}f2SyIZ(DR2TI8*t# zv_>Ucw7Z{F(9~s8zS+;-s6ZEl6tfOAd79-7TPfE`!fiy)Rd>h$pnI#Wb>`h+LItfs z9z#oc#;td)c5$KGYFrs6l6ewLc{f&T+ANs;EPvbu@l22Kd!{BPuJs~|6BFU-dG(v z`k|l3X+j<#?8|khQH8s4H$JWlku914bC$FmZmL^`X4|d+Y9AXw( z3+fTEO1kakRecm+HJH^aLP}y;eqZU3%Vc)j%`G!RHX4SA6<-=3>7mI_miyCsMV~PB zSrOK=I3-G_Ryh|@BNbN8&h5PNB&ig`+1&Yy2C_L0P?4$rjxZbsdy-sQ?{@j)-oWV5 zgsbVD~j#wsu5LET~Rvz^@>vmoI-RdLJIx#LWPt(dw4Ixd*xm3b|t zdD7vJT?<)C*nDpHvTlx7$=4?J(Fd)q&w%gbWKT`~_6X=@B`KvT`PmJ{U(zmg+X&?5 z<;CFS!BcN+Y;H0f+ds`syY@(0%Dwd#bmQ)1b|${6{R$rC;M0DY<=1p-C!QXvuY{PgXm13a2 z^h2_(t~N9KvP?nWmavUUNG;-g{ecQk2J``J53N7XVko*Lcq&|M;M=$2MB16`d5LyY z*@eeansR$PTPxSIv;=HZf!y5 zo$xE!=f+*Xi`RXlN%EF;55(8be=B4P&uy+wl79dE8?F!X+#q$)#{nKK$@XS`wyjbl zv~qj=o=LcQxO;`Et`)PokKs zIbczZUIE2Chez0yL&Hsy=ZXBLd4!DvgImT00(35@mxAjw?k?;COZoE*Mrib~Moj-V zTc!FGz5#K8Uh+Q{92~q-?E^&*|CuAw%n2_>mblPUvyd$YuCX@{H}~kZ*5f7X3z60% z;0&z)V-E%?aQ3_3f1U`Y{r7Sbvo9qOIg!>$gh9xrSP0&YKv`@q%dl2lc71TeJ&%5n zVm=)!>&BnPvUCzb353i<{*3>A!JU~&U^N!BDcz)+i z8ZsnM6)XIy!gKNC*@wK*OS&_eNA=TYy6d|7*IEysmKqR~bAJ2gjV+^Q7SM8j^eBur zq_va3&ep#C&4R#xbx%y;Y9{OME~B&yW1;?GE_p|1%?5w?d$edW6;sb)1~Hz(X;>{z)fZ5o%T`dzLsO*6lj1Ky|WItl_Jd`Z`Lsc6_roK82(L^?{6*5IukYGEp1 z#o3K#_0La5^et_I{926P@i`XAjb1w2zAY`iQA^pv?{2!3oz6NyGnCD%$9e=Sw<8a5 zKAJl9abZS}vQ_{5h|K{&JHEhUzVvL+-C$G>+%4PCQQd#Uij82$0?()XKL}&HkcgUz zAya*)Q3(6{!TmFjdYX(*ph@N2#0<99RWP}ut;!h%A+klH4x+E>(b8YPeKT(V9!+Lb z^>b<3pZI3WHT3tJK$NBcyJOeQoB3@J)vOS$686-))}57Xd40UA_j&25KHSypd`|R9 z#%8jU=ob>bG#wYE;WxxvG3uQwK;_1+!9LKQ+T%H~vc=6_3VXus7r-$%#g%WZO}9s5 z_doVJ-^*S4_HEDOnG$m9xP6d7%$B*HZsBgbxMU{um*(9?;evs5C2fAHPprOr!pn?? z#9DJ_I~t0USD4+!NhgfBbfW1AcJ5Z8Mp*1V1kYC=jWs8hPmJDg@D^1g=1v9rZ7pX| zF?kHTobCyKaDp6IA$!DX=R#mj(CB6q#x?-ZxjFUaLYF&Hn1#DR@qDicHZ$~ALHU$e z$$?{^xKmhGE!s@qkAHk<-mVR-Pc&D1gD9o!pXB+u)=Cq+poitu*xcM)uaU`4w zW@3h)?i4 z9v#D{QL?Lg3wA%LiUT>8 zndgyH%evUNnjn2~`K_0gE_ zZgx4!1UpmWF^MQ{H~nKDp-FO1>*8unpJJL9s28(IkNc9(q!)Jper2*>Oax_8pQL!3 zpCUs-x7k6v*WtO3Og=;9fL}{H z=*V0|EyPwNo}#uIo1edFHqMsYrfWN#M#^1ZUtjr6Dc%3=8&@Be6fu}AIsh}00qi=b zXxrb~2gyLis4Wp)5(0J2z|1>8ZybVk8wlZe{ykI)sDs8^vjT@x>EM0-+l0ZD8g(}- zzTC&^_sX5S6tAxFJI32xTVpgN{RV1TW9uTU893JH9&B4Y{KPmSIQRJw^#n6y^C6C~18=X6M;~T9y%NT{IX_{QpRa%?tYFhHnl9=e8geOtN zyFx}w+C%$Ci&{RiCY;2F%{BY_Pc5{kDY-A?>)j1?{2Hg`C+GZ5a>B1W@MmL_uPwzA zOIm=dt6@W}B+mpF8%c?LYphGJh8KQM4!<&EBfa_K^6POu`k&KmHA3s0q$k`i^oeD0 z3+F@#w^;|ZxInSs0n>cmC!wm=?d|O{R|Cd_Q#C;taCi=CA1`z%^4h&ADmqMfaX5`D zK4x08X2~k4sUbCri-r7ROT@qR$I#(z>_zsGVXo8_Mk~c{>_^Lb8?Q z=jCIr;Qo#>4<)LTrb^K>mtbm9(-4xqCj8DkDe>hip`U>?9!+Z(3js~i4t(-qlXe{z zT7Nyyc!`MNU52j!P!fYzF}&THa)q^T-hBBsH1uUYDE*UCtZ)W~+OQIO zUkjPRMKwkp(u_?i;Ov7tWx*PdXsc?y&1ZGZ<4n(`LHH;1cEs}Qn-){5lE(i2 z{<7`D!kr2LE4{tF&y6oMV-yoTJzof`I@Cx&9^ik%lW8Yx_L1k)`+jO&Ydlo1zV-KW z-brt$^6=5WG;6y3d&PfWt}CQ%!flV;ENzC<-1(rwE5`Pv`B%jf>p_-EVdHm<+D%T-u{EMC%z+;;2YfgSR=R+9RVa{tgpE zf(QtCist+Xn4(v^?U2>;%zZCcRbptv*DA})xq7vxnJ85C@Z!D#64&*XPLd?|!(EPU&tDES#Y%GpMycH^=V02T^F|a_<^}1;_x%ij!VE!jN2D2Ke@&7wQ0^xg(m#F zZqbAO4XPoZL?Px@qG*Q)ZA#Z@r2?gln|c5KZ#k!wh%ff9?P}GTI&x9GC9tgCG_$ni zdo^bxc9y5*+xO|NFadk>=bRnFir{vgI+fI(~*A&P2FSVt@&=C zg?=~hqkvyXVl|r6Zrx9~{pp(glgCpzpu?H@e^mW>Je2?UKLB4d7-XvyvLq>rEHU)Euk?f2pZBmhCgz>hQElaX1MfS;>o$R{^+3z!be&73X-~H7ik4IkDb)DBa z&+|ObbB;cP`71l7n>TNcyRo#MEZ4T=tu$dLIilOW0O&!NFsUH*^aGCbJkUGNKu~#n z&h0hko6DW;*KS`1I~{GP&VqLnTK9#7LsBzt^>^X9-wKO$s~~{k>FlNK{5E&Yd+R zV#QNXP>@~MgBShtoO0_YVYJ10kPGqMRq}X)z2Mp$AfseYxPDgaN~o=d=ICl6$GjaE zZwZq_QI5HT(U7YnWBgV~t?F=EM9Rp8>q#eE1`=M$S`M4j-b5S{`BT=o8x9N%jE`)Uo`_J*6ZvMFH1aXrcuOAg3zhPW1GxX!+ooxL{6v20f{J*AO8-@ZhC>;L6Bw zU-(~ImK(!eex_O;y_#3o0%ONC8SC8c++mZD<&Qu-f~Q};KpHkQD5^NkdTCA! z3x~U|`J;PZ)h#5wk0#ogMYg=Baxi?rr-W6X`thaKCrZ6QBWhk_tIuEuz}K+!Vfv|* z^R4#=Sng4}d6emwR0S-)V(&jA&liH3bF35|6PdHt;*GEyAQi}soS=ON%Gw)rl%~3P zKRLKmk<*60n>0aVdU{cS+V`B9YbPGh6` zVl2h5gEZ&1WJ(D%1S{Obs`gA2akq)DPxIL~h&61u=-vQ^-=7wLA5ywnxuL$@D`W}8 z!++NQ#$Z4rf)2l-PQh~y>SPEk)V}12`}Cw)`z4k1M@@of+6jMlp3L(Js^8s95)-+` zs0JScg^+aumivVoP@0mk!?tF&9qwgMB+{|P?Ntf=tS!w^{JQLG&y9A7nyW$T*5j4d z0rnETX|!JKDu*J+TnI-!wB#myJ0|@3oQvZR&StR?1u3(n4Ch=NE0gZ__4l)f-}$0# zYe7^UzF^3q($DXFXllE*(56}{`&mM4soKI69nM85KK?>*+t@PkO^TWAsv7<`<7HT(@Z*^NkqRd#)1pEP zOWadnd=Fz2Dg%ZZIm})_y;yWFJAael2}66jg49P7VS~fpO^mCGD3UJCW=w;MX2+;>3f^%c7|LsRI8Kef zw71x3a8zl{m z<#5`WSgFbUg@ZZm^|m^?I_&C2w`>`#kXDvGS16;V^+8FA1nPz6>rMY8U*BGPPLK#R z%&V^m`&t}&JeJX6Rzj=I&(C17wyQaQ6VI;o;Pz^S<9-#J?+7O+=kxi3Y?nz68jZ#( zD(zR(1BJau`Et&K;>zAo#g8*O6I_UP^!iQ< zmvi>4PW9}X-1*B1go%Lyi0q#D7%0%xc7ZTRi#0X*_)lVEJEln568>?-!0LQWP18=RF(s z)BrujlfhUm-nHqji!6JJZ?U&EUOwHXefHYVk(pz4(i>HN=jdphUFd_gX_57A^`bh; zEZU2s(5&C{ zJo&--^T^g_V^D5aIp4@1zsvf}CAaWPpVs$aew>>;Dy|1AZcBS>RR>dksEGUi#XURT z?FT=AJ81cFP<}AtA1uAsbQZ(Dm>;~!?Z+Lkk*$n&$i72r9y=b_y&SYDmym}-WG_(X zdE#lEiVF+HT>rjQs`j7a)eh+x#6PrNpGrlBoxu#lT8r2W<8 z6INLu&?1%(ArR7H_F-VtUF*V}vFzzVEq2bS?Md0GJpw|(z!ZP2r#kPy_*1=a>EIECdrpa)VSaQ0eG;}Gn)^64h4lD9EM7u~{F4cbfiaJ?Wju+|XNY75b+t{5n8 zAF8Aen&T_Eg%IX@z|;<^expWZA)81TRtny*faxE`^k~9t=vZb|P0(-d=Zr`#=iZwA zLGI>aU$5{*($!_AZSqfoH-_g&F5(cOn7VY8BXQRxO`Tg{*LR@f=v`Pnn>I#WH|9S0 zRgC%<){UUUcz9~(;=!yh+IBJ?BynfcQuNuse!g)1`gQELcUM(HJ4exWpx}#2KjHu7 z?`Zkad$HyE`)MEj%(v8X5|gUI%`U!Y!~g9M@6@w43F78L*k^(`!twV-aLOiiZ49speulIg^-~mLXVmG zuSH^k4zD!tcU8+NJvQMPol}~+or$y94Ql)ntV~m)oyu08T8wp`oLVekLqD1+9~=RD z6mPqy(<8%YkK5VX|C`T$;A48{UZaOkrjj4j_jb~hkkaG<~6<6?ZD zTky_+6T5l0aQH9i`g5+KKQ>yX+?jqPvR($~(*Kk4yjL^4R>@KRkSTV0jFQQFhk1GV z_DppLt$pZ~lN`N-+xBM{#dLq*O z9>J*x9}SarhRG%%f*74zd!!y%*($O6q}gk=ad2oDV_ezQxVrN*V>#&46Sxdzu15AA ze18Ns1aKx0e7H6*B1F%ggD-Jt;CS&PO50w`n>P<)ooT>}*LI{|KU;i+$YCcFeTdm{ z3jPjVVYH6`M)$^IXk;F5Zd+Ai9h#-C?Q5|YOJwU0q|x4VqH*+7bPUTm!hx*=3W=op z6y_^MOHx&ca{SZC5obLjoeC(r_{I!e{M9n^2!2nz=s%gx?QC(C?fQ#38Y!5(kK3It z)b8QS(TDukH~!J1R$Q-5qSzY7)BRJwO+M*NrHs9ZaQIi7pn?TroI0{@0}EiBQi@xP zJy+jLGgBe!(sFVf2W7ilax%P$Wm}U~yV|Dw%>jW1B2sSQQ;o}65{T1Fara@RE;|pt zV{tVR5ezAy1tcd*@q^^_psjx7PqoZeu=!PZ4M+^1#JjF{$P5PdWGVAPj!Mek8?*db4zmywb2`Qa<(LHxDa z>95{kun#bL`04_JYJyDNjkOhi|x;@1hH- zWxga+J!-c1pr4(}g?#0_zCWvq!NsO!#1?EO$w-@Ns(fFYo|u^6Kd_G~+nn6GKgAY1 zeWG)Ti9ngGYbLRLeK}1~j9;o3gV3mPk!yL-1;E zd`(UEeI{&kXQpmolpcfs3=$Wcqi~XotE?K~x<3yC>8RZpyl(<2Ex473AdLqkIq1n^V_ zMDuk(_%%c;g?#_W?LB`dG z0=b~ZSV`xSh6Op&UE}hTA{NzMj}uM<@Tb^3uNQ@lx#Q_zE$_2JL@7}r=9wqN_1;HB zL?kbAOOa2a(f#4=GsrgcU>%PMKT&prgc4-}$MO3GDhm%h`O{BDThE3QBWmtmB!eg8 ztgbYJ1T;~uV~|V39S1vmb%6a=J&DAO&UR)p@`|wn_wr5r9l7ocRH~$VGP>kvU*D{Y ztLw-8k?Rih$Xbl|l-H&X>@8qtC9XrvCzm*tc6vqOG(TL$_^AWZwSYL9HNyv`_q^K(aXo0 z!DPuLI4=>K*!d^g_eibkd4hEhSuf|~N@9UJk})bVM&Xp(AxpNg(mr_iJlxtgdBl{7`Sw~ z8<-?Ljj3xJw~($&kU3uY#~h?4wwoKlZ%3vCB(l2xBlAa}&VQ?S0nJR{XzN*B2N@UWlq16=tNqsBZ8YOh;DDa& z9G`hdsiy#j8+C9Wj0{GGBQsCC=9x_;pMj8UlY*qNXx`8Sv zRev0CqqloZ)+5grlm~Xm;7{=NzQ=J7$?t3s?tIn$kCCzh^7&e>IO%39qeu#l8FWP& z%%@w`VZWuj_0%H-=c~eD0`MeSOuN&r{PX|phOb98L|CI}$Alt_a0g@@>=@+RL}K>{h@_s@0~eZ2`4Xy_HRo!5Ve{xt#|Yx#RJn;8lR0pX!8L@`aZ5 zK{7eF)}C0v^==BH-6a5QwY2h6TWlz@1=8vQSxo4ZWMu_B z7$icj34o6|u|9yvX=IIXy?55qO_A5JGpfQ)s5!VYT5$OamXBC>_ z*3X|mmjvJTbVoDv^>=JmxC1;A_UK&?s8s&4=IO{ ztr{>EqbjIo8G61}Qd^j~-Xi!u`tZ78Sh@KAJG8#09b<4J_e`CKd#A5*z2hwdvGr)b z@jy~!i=1kzK1I+`Xv*+B=#cOSRC;sbgpa$s`*^aXb_LnmuMMBwNC!)Q7E>Nq<+5@g z6yAA%*3*F`xpXc8A3&G_+~)S7wn$GlDcnC?Q^=hHt@;x-*KG-(;Z5LWmcU#8ww zKnArKPb&uR4|ZFR4SGkvv^RG7EF!V?uMGx3s$Ycfj$JdDWNPl-G@`Qa1M$y;S&gIFSjdxmcsk+0#fWqKTWS_ehC)o2NgGxPrjvJ{>jB1raf6X z<0_aQC6|M}k?RB(RGiea&!b3zeI&}W4qq6M+G|G#vu4>5EfMM?k&%&Hmu=36 zU*O&SynP&&7CQU;S9rU-k22z&L9thRO)THQjcSyv!@tl4ZsX7Ck`Yy(*`BQAefU-S|C0kuR8gIcJ0RIDSr8+R z_Q!1pF2Xu1QO0pJxNplo`H=+pq=z@1!)C!>KcWP@K(&E`sf zM2S;?9SzSV#XDP=Adp`u@`Y`^p8?Nyn;6T2!c>$!!mQyb84ZMZ-wP>|C$Yrm)2VM| zL;u1bOsH~H&>4jNwtj285=`~G#E9*v+}Mr-|DM~@5G!LLd4rma{moDLpN2NhSK%Dw zdcx%mfjJ?t5AunQc}oWW*ddz}c|1Qp`D*LA5?*93<&|){^kaIY7XH6mcsiugM1`Z1 zcr0divN^U28vLwlTfoboUU_qra`*?;k~NWm4A}r5*UvG8nV?t7E2UVwaj<&0d!3ry z9xi9zdJufEVOxkgv;K}pdtST28zAn4NvfQnb^Rm6fSbC$A%I?yB)bIy)G`+WMR&Up zafbBprUH{3`d zOE$Xc0)5 z7c8rt@bOmoWHO`SOa#C8*)VRZbduwYe->;TiohNtOR{fG zT}QMu0p4k@gIxX=Bm)>S2q$`EA4$JXi3^0rb4u!$DCAQfS={-V4#_fC;b86lb1VZE zF3`r$%UPO}PxW&8(K=uGFIYlRf7Bt(HcYqV2SiLi5IO;KpjTB%7o%Ts!;pe-8$O@a zx-9wgo$=R7D86r#2f=Vif|jK)n4t-pCD&3BFIi~yDn(lgkiTEKTtdxEVDVa62=a7` zP8kf#)4q)gYRGy{mys^mQA&=-Rul2@Q>h4IcB5uw8Q^t=D`KSWz^i@ce*l6k>tYut zogEIvF~blXiO0?bK`0!7#!lP+M*ug4_RHn)EA3Ap(0sbE<9Kvo;LOw-G`szUeoe_hYXWY?Xzc#Pz_w35;?pr5Xw6X$A8nazI%VF-K?e1@UM zs(+1co@KLyfeo030>M8|&!@EaWIwIx=W0vDCWxNPnp2=Lft=N0PQsn1o-0n|SB6wM!7 zR!sdLBUc?XHagSVhA+d%-oA(~A72%h+6e%h_*@w22EqLG*l?Ly>^)}lyQ9)P)~S9^UufQXtSx(`6yv@5DY!I6YV;e;s6a-BH_y(Y!rwS zbfNP>v)i$VJ9EE(eR~GL@FDm!=fO^#;ljegAMo*(@~)@aSHrwfdXbbYY3gfYxp{fz zi`BbLKT=rbSTRWU!74CF1lOdbxM}EtMB;qGlAQ&!Rq^ zhaiM)THJB2(oG6*)WgTqz}T%gB~M|?>)EJu7E0U@`F4cEON@J?$^`p&TXncp(i$nH z3{;Z5V0uMK4h-Nr3!@0z8D#AN%M;O>v1Pt<%Y$#{sr6#RBqhgoj2uoBT+lXd09=vP z3%ys%mIEmeWM zh;P`@!Nl-bnP~g$gOBMtKQ`&|^U}KwCTNVhc{C5MSSwOL$M=66?EBvEgTSe2xY&KUI7rreXOcZc|!>7Dr2d zvuR}O^o~5JCUXENvZtPT+~sGmroKpxz)Y)u+NX0c9{?%;&U3k61>yL=qB!|DmCPZw z>P70yy&lkn!*P6y3THz5L`GKDsq5Oh5bA=#gPR1F7+Xe0y~sxYEFpiVRZlza!?>H; zMUfZ1ob0GBur(tV&ucrKfclOX`8gDL7;Ru@uFLM=iyAs%|OY-ttTBPpIm+qXgf;H*fU zy==3G#=AOtTx|in@B_r#;>AOOS3k+ggT}$&yszuf2X#c8ac5D16@G zewx-FU4B@k^QMW(ENE=x;Okxt9y36b?r|>$4exAx>#Yj*-B5add`0JAVDF&r3i@dy z=fqCz$nM|BozCXq69?Y;J4?H^%N1)QHERcpf0H-o61?{VWB>8yqeZ$hhu%iX_sAdh#nSz13(*bg=n98HAT9+RKy0ZfA5r3Al%v|M6W7=(qx zB~-+cj+v|Dr~A+Fr0PB3dqZx@2N`*}@aA7=mc5Ia5@vYS@?%P{Ro-y_X=NGaK%umIWty)B4{?Mu@HOor-eu z@?K15I#nZ!*uK=bX_^1o)b+m;qs_x0XdBwuSeRB;N083Hb!+)SyA}KETS|L>dt(oF z!bF0+e;4ZaiYrG#6?kqacpMwt&QYl}6M2c7-*v=d=XbtH_}H!eWfYP(9y)f=K0H*{ zSRB;!iKJ~CS z9i@&RGFLVl6T9^8ldyK7dbwtyLA|wFcoN%GNyF5$gAcm5o3}?BwtjNgaG(0yhOqMH zdTsPOEBP-@2dCT|K;f6??#bFh3tF7}ZAEDSQinUjJnr88F!uNx2PU+iPpnc{+anj0-tg0G$IKTdR|*2OOyWQw&x`88=MY z?EEYk=?#YMd&W+DImt=%<>TiUtl7l(7b+7mysQ9Ah1VgAG z_b#Ckl=GT1qS|M{F}Jq2_X>zM!wZpF$TdS4iHi zs{W6`vbe>`YLiTM7(YWC=`fb??~{Ef8RND&YEqYPQFAK<_Bk|ME8Y8BWRuURXA2Wh z1L<5YjO2~cI=Ww{#0xMBP_DnSTuDzqJ0gl0z1=&G)RD>Se&5;t_mg1IZPazL9R|a6?Vmy_AZ)P4vl-IY>9< z8zWhQ`*O#S+Rn5iQg5S~B2Wn1qku4&He?E|BhRIfH&`VlkuxEth9Vgj0ndj{_AqRJ ztKjQDt*zf#c{%kwF&VCHMTw^>ue1LH@rRD6t_FBlrL=bvC(5?!BRGO=aU&D-I_Az> zCUwlRj!cYty0nMi4+QO+cql>qV70^;bqo%gnHU?-%v%Uq&GkiElzDthqtH`Vc}Tof zSfiCVI@OwB$4LBdAs=C50Y~4}5g8lQ&EU7a+ZN7u`0ukYe^fV#&i)wbyZ)qi$6oH> zLEYwlu;R&?pLAZ7s_i8-@=NJuH!4av5FZu>x*6*5c?4mL!6vuu8+!U9@5mQ2!s&vV z9Ia)l1!X~rnqqBu&`RDDN%h)Z@)edLu{u7D#xy$GG|bpAR#pHtLCJC^C&$&mjTz1K zr@5s?YozYxqgJ@ENth$2u2c2pzu%#_CJrV&B*moCQ}ylJH;?gphBZc9`owg<=D$*5WHraHp| zQ^oSX1FJK0{*&!e`?(7v2caZYoeMQ7yD_T?O>TlUA9j0I6Pn#bYCi9>gp3tYQ&bC< z5W4Olqb5NAKWE~PiDbhfFgnP1_4h(k)6?G=qCpts!4EiQSwBZCS`85`u%1M1FbR@~ zqwK7rte>y9e#2o={KrO<2H6_t>z)+bRsjJ4mSaM7(F#7yp*0BMj+4{7Bb!A24>zda z@kzW46QDP6`n6RS@5V9-W$!y86!t9 zRQL9jBKOr~6z=y})*@%wmTM%r?;j*6S;+sC95iZsX2@Eh?6I{bHL}mj_EtW2Z)a=#={C=?x(NlLk7iZtT z@NHfoCCaUOD}$TO{WK&A5l(qdV8BdO$Q#+^hUE0l*=VSd+}ISMDQ*(IblkwX@qlYt z0mW95q2!6dQw_$e5-&UPh0vBT2^xxqk;LTmPz$&jT*R(r6WRn?2#sRpR-M1st=`Hi(YR z;gd7kZkN_1X!Ie^E?vAf-+1(Ope|P+xa}!+E zRX0sN7>!IXZe(@)_J1%CEY2HIb+WUA&x*xu#K_Q5)XsoB%hCu)D?NXRDd+NYYIx33 znhHCk+{G+h6)z-@>=)e28xrEyuDrItx9irF`hNMJ-{Ohry7hmLbU$c(D|cEdc|_4& zY~wb`HM8a$=kC?ZY*>w-`XJ;%pv#_!bDAFTuqIT?OG`XN zop>#W53HFYgdpkv!ny-Lt}6RtcU;Mlv#B9=L^yHYyLsAM@q)$TeR}?gm=T|#`K{LJ z(q!jTy()RM$}imS{qF&xvv2gAhu$HbCM!7vQ9bMbV{+1VoT*lC3xO8hTGbk3s2q+8 z?g5!6*|Z0faR&z})MCoRNcB#dyvix2NcXGS2Yr2g^(~L>9Y&=MLLF8;!xW@(5`c;Z zR`-A2;$kbIBt!~azHe*$EeTpo5|WcQmpIw&Zr@fpCtXW3*{3Kml+%ES9?Aq&0poLd zMEm#FqT3g5{Dw^MGIw)dUvh%4&!ey(fgh^PtBntoCJ*x5VneF}>!@_^&JFghmUgl> zKkE!0VMt?ErE33Jv%N$1QL4124huc=lj+X+1V7}3&7C_rFhubL_Jxa+lg1^OR;MZ) zA-OzK<8~Th`!r1bIQzYF=TE~QB8rjDk=VNGyW^?@;US6zuB7Wd`lDGL3qhWqhSq89 z-{sRyYCKJl)Hn5c+RpuN{aYa25nPc%&Dj8H|2xH`M-4xx{&+hJ{Z;j>SH;kfd5x;& z%sV{_eO7AAxcs8b?VILlN$awiClB~i}jdJXcj&2H9i_B~UUOULjQAHk2W z9683{{f7^hMt5Ce*M1%unIME<;Ihwdnx@3|kk9AksP54{mk7%ex%VZA5-pVzKmP+xz!FqJ!y( zE*{r24@taSXoSH({gS%~y~*4-O!?^Z5awInLfB3MLtyj}Y9{Q{`~Z|uj(5=nlIT2IB;Gg+_8RAvn0ex6m%bJ9 zQ}pwe#CBOCqgg=GaA#y@MgY6O8yl$%5M3gR($aEu%zH@!?qnmGG76HpC^3E?EYor4 zFp1j5ft^V=iqB9bGNewU({{7f4+4Hc8^!ahghP_BSV;|Trm=5BUk2X>t!}#lg8$*< z`S|Gyo8`~KgVwV8ci&~t*-+z^8>OsgBdtalT4PN`kh+WF;^Y_J1XT_kZ^TegRi(7o zj|mmcw`KP}8+DqJpYFE%wtN4@&Y}eF==}R~LB~XKW%VAplZ5$O{zc38 z^S_xzk*;spkWOZorEQ>*hLm{WJ>q$wNv^%BnuTz=g%FoxVq-amMo&ETyBL^eHz*9F z@m#pk+qh9dm&|xhcQ(iSBE;+@149i8edG~;2$iG2Pn!*@S}PlcJL58hu%4fx6wh@A zR6@C9Hz$EJ>E6~vMRdQ1@v6o=UUx(0kUuwFZ^Q8$O9*nH093nkv((Z6V?yt#~k&BjP?A{YARA~lP*o)VA{RrG)PBBr%GvwfQ@u} zqO!<}NUW(y~+l>TPl&?aoy}^`E`|fuk~!o7Re?%7lH(l8{0LRhV7t2i0?XPcv3DR?wquUPAGK= zQ~zanumabQ&oKUf>cF$y4Ku*Tp_9(7)3&}MSVQ}>TqZ)V%7v!e)@Gw$!8#VpDkuzX z|8881F1m!&@f82wSvC80l9JxQ-09So+;F+qdvq`}GUs&n?vEl&u~}R9q{1m=FUsC) zItG;`Kz^`WXsTOBhG`Ajcd!t(;FpH#KbN{lx! z^t}SuFYxu$n)^`*1&H1?sIQU>nBl$%r^%Plj-L*e#ygvl&XbI6{e9RscVNLf`mlqBrp%UN*u1?ZY=2C>d zc7G__jVnAkB}0Y05ZJ7QlxZ9z6f3*j)TA*Gjv|V9eEbDlDV$;vBLX-@)3KIxIxxMI zTW_l!oU$dXU?7SLr{igVafhT^4%SYB2#Ee`v9SNog7$;+{geA9Fk$J=M1L ziK9QGb2iRXCr_ka!rY0DveO$QCt%RCJPe!s-z^hJr)Ry<9~00FF|~E zT9=!_27Jpjg3sbr8>`eD#-p;gC_-N%?`88u%5Fd=c?}YM9M?qP7BuZENUX4$Fd2j* zqksMEk0ye+at<&Si5*TIA4Z{n1CS$#?03J7^Sm(OCBA@HZW1PmCb5eU}$4#9#bjt8lozwY4OW z4cX4fgBxC0P(XSfOyhprH^$i9eEq_2N)thQJ3E2RO>|a8fMME;^?S_B%qmUz>UaJO zXW9j;)u={&J@T(I(c3rS8(>ff8#CT|uG?er1Cm7nwo|O}025Tct;@&7KJjg~Mk=wZ zJ~n|#bGmUS0GpJj$X#zaOMule`~a1dAUxU+VMq_noSR$^i3(SFs( zdiWty8#PfOA|X8L2q7i8Juh?4nivwB3ZHL4cnam)x z&Un4T+%YsXoT9@oH#UR=KQzaQ1dd)>+JIry>V2V~nS?G}U_0*cA#AJfM(HJXqv@)L zm~Nzjzxa2)yc%M{te`2M@RAL`f27~X2TX5Cq4vqyw|vV?gj339W#PsgD4#*d(Kq+9 zr|Cyc3PnD~Qc73e68)&af_RZR@#}s-EP#Qbj($CKZGCHzshPiXIhu?PcDL$l-az=A zIwbuuhsQ*-FvG^Bk)7h2Z6szrmZ0v$kC5CRaLAlzP(+X{P_1fqoR0<~v|}R@4V6aD#GR>4+R%{ufKN;LP8w{CYSUJt*p8<} zs72jv3gR05v^rei=w-9xfk0ln2*WbokS{k{{zg(!E-Mn=`Tl&?Bw9}ja%u;mu?W3j zd;4Hlmh5N|Def3u)?pEHB$+j2!ofzT;Zum_2c?$)GyvYNr_PCi_NU3z*Vd#UF<3iNrfzA zCXXADH%}@{Ff~N}jc_12!E3M9NtC#*A$qs9G3pQ4){&Rop@9Mz6APC5tnS$P| zhma#<;$Mid6?!1>{iKk;1ThFqNYnvrr9RF&fj|)R0r>fPMHPR)dAsK0$DTc74*Y)O zw!o(k=9TvUap!QX*ucHD1ynkN(@gtaxRQNeb1i6Iv388cB|vsrff(2kXX-HiJ54{g zei%{XE&l!2U~Q9u#ob7o(PcqB!agXdnmzag@<@1DZSDEkE}J{&m+weY&gM{>=W(Pf zp6p$9eR#V%$q9sv>fHN8wSPF1yCKL>ws$#3r1T`=u%!+837QDeCFh-wJ%3%r-W5qw z1rG-ot@Q|kl)Pc`e+WySbP3P5zBYS+>qy;+l7l53bY!AL)qtdhIR7Pw!96gR^sGXZb* z9L0Ha_?fK`YBV3lo&-wK;)Xzwf`IvG+S?y|{(A8!IWbSxsxkb>z4Wy*HGLkgeu4<_ zIvez4y5?>KDoR?fTa6Cxi1Zs?*aiLp=I=}xf6#wZe= z`ly?`?>!wohA}l_-gUp~3vlQ>UU4NQC56SsHuWLQ&89%0I-Wvl%;u53g&&9`Y2YG^ z-UMs9)z-E@`1~T4Ou0;#DqSEa;3v%k^8j#+}@V3lps31ZBnsmoxzH?A*&A>tSErJTiMgmr#(tbODh`M zn5-#Q*is_G`ROtJJpQWFpLTf1KNS{sgasd}#bsHo_kQ`J616W=ixm3~wbu9pe?-O_ zm~>XYi551J?i>p;(#2fZzB+UU(FSD)Qhf<*sID(QSk#dI+YPzMrcAnJ?@R4u3j#NU zIhmE1V{TGc7-Os5+>fcVY{zuNKt?3%8FoXxNE$&L9$@p24bzacvaO&k$YF+Fl#(!u zsHK<5%Udkvvq!6}rSYWK-<8+ixH@ct?7kt@r!r$GT4TKQ&2wu!l1*;ia_vx}C5nVe zZdfeIOt&RE_bbuOFweW52hrn@_c;WsE24GP4NV*|5V0^b!{*_fC%JA6%+n&+NVow$ z&sS8ls*8%0q%`Tf{x8gtp?bS(kRId427|Poze#gphOJ98du$0KrZI`(9jGi2J%08e z=y}7!hbBO<%@`O}=Ay7%q0}fb%>l5eJ!(R zS&^FFpsly>651syum@%!_yINt#VeA8^Wilb7&(NxjjJ{u5&m}-s(0^i0rmRmghNiw zU6c93Dci+RY6|J{C&@iB)bA4 znHq2?IL|-Bk?+?j>Ru|8OuG<;{B|x(Lz4{|maZHFb0@U5?Or0&i3=n$Hd=lZ8|X2a zxcZF(Wry>(X>IP0l~`FDKs(RoFZ309Q@kUC)6~MEBI^Yi$?#3E7=f_}WL**pPf!5I z3Q@6%Nm!3&!6<-;6PzJ@eR{zv95RfbFXA}TQ{Iv4qsZ2B`n#*EgR)tBP ze6cN!tFoyY9vr*?2*{3+Eq0Fijo$1FY2fEJ@h&#>yah2|rs#*@b{AUY} zjJi`wcj#`oi<)NJre(b9$3UZ>wyti16cpcKWD{k^02}^?o;c=3Q#q<3%2|!q zK8rOa;?J{DHsWlD{53tTgUS7|J=(6==_fy$6wZ&kF)?Dzm!yDh-uT5^0JCxxDTE}T ztWx}&2Chk09mFH4;)^_!NNSgy1&Va+%LkMLFZp~|zi?RnxpnLBLz7WsN=Yir@cKxUjB6-_#0OsDogl;W)v=VRyCiNxy1SWE{l5V7Hzp?L`N^)Zm&dn-+7};K z-%hPI~wA7APFiMYECF9&y!B7aO|YQam$Y)sZs3Lms}vQ%o*d-)AwI_$|GK4 z|8qoLM-T_H?%$n0v!N{}Ij@GN^2no!uCk&@H-NdHeDm_53fNXsf#yJAa#%;>B&3{0 znLcqHYb z1+F^q)}5|Al2rjSyOb|pnmB=;OJVJgt2)^YX5sK)xQ|0Qq#J-omrFe(*0AT672RHJ zIT>mGh$9dLz(ST>a6%JYCL1r>t|MN+(RB_v`&!W81`=V;wY0;|DPU;=hx$a4g)1$9 z{YbeOhVpY<>K41d<8`UU&{kpp&SY>bxuO|LDT-Dy-L>`g9jl?G9gjcyXSR91V^(zh6xtN$^Ojd(YwDl}H~SAvwA(R&y5oH?MC{0ncsI=0p?erg$*@`!}hymE&u!sPEq>24)iL|qre1rlQ z{dJL*TjYCv3^GzQXyAy^t`rg2OFF!+2!=o6+3F7~0&BK9?BoVfnMumd62j;VC78J5 ze|`W#ITrTxaR2wLi9lF%nVpIP2a^@ml?NQaptHb* z<>AbXjDAIW(m4}<&z`}4Ib&Z7r(i}=pR)Y)YLTlnS?}e&rw7geleWpWVT!B-!i^y^ z9euu%dWGkVD2<4Y`2`WJw^AQ>Ygy+9CdW15<^u!Cxv8^sR)9vuBTkkJ-k( zz5&!;Ea9>fGlFQ7ckUp&*U(=(q#>NwTBe;aVQ1Z%IsB-3_)#k8<^p!^P=?zM;rBIC zd$+cHQxEaN4erggf&ukL;48gIbTTa0MNL6H0Le>6>m}q3Z}EpD@=yM|e~lY_fQ+Bl z;py{_QcIO?@KUoq2P?MvisPLaNb6EZ-kn)qUMxXI!zrcExa3`**&DVCF)t}+O3iMr zn3MTpK=eY%X1@Pw=RXl6t)8izA13dM?CQMx29yudS;qR&2;rQzKZvnwG{jS}Ju5#> z;ttY$+g4W#>#vBOCP;V~o8(g!=u=90T<&3*sYy>yhdbzjRo8?^Rcy*R?dCy$}%=E*QHFMVzsFTU1EKlp|3(D%k?cvm!}= z$pCcC@%XUxVlCYVF7d#<-<~PQR)mte2m7^nH(u{)Z|ZCWf(_c)@<8!IC&F^BnAx{m zj{oZ8caa$QbKB)Q?U@`ZDCMxPlGsJqdKvh&cuPg93)tc!auCluEdXmX0uFoBJ1|31 z?0w*>qq6*yX=G%C4#gp=?Lr1l=&u7CK z)6zQE+SEtQL81qsrJRr}*ZY`Ly!;emGHiCyvW8HU3dzr`q>nlXP>34sqM-loE5(SU zL7yN3QUf^<(B?(ivv5$+j|{+-luIxB*}@SisR+kUM5hF>i@iwH86IS1*<)x4T68~I z={L^^`L?o>^^!uWLcrf;38r_cULz$nM zQKI=J#{v$~s3GqjvPgIZ&eh#FcD!>ppZjUHG&jEm6^%LK{Oe)<7=hmD>+D>hRafPj zR_#4HdE;~f(q@&9@DO-zVD@A9Ycn4}JkvNx(RX8tvoOo)G)&LIiAHn+wF8inJ_Kxh zb@y)@gps;GkSL7ML$UuOy>977Fi22VEet2>oyraE*4uG|;aZCu(e&L? z$07eWLXu`K?T%gby7U1&LO!j?+gJ#9#l|t_?=0zf>Q|b#uggEiVQMKG;ERd|5a$)* z_37lZ#7b%l$_)}YK5&<#?&nvMWt0tYWjDEpUjbz2SJT?6v8I#iEW5%*!(A>_Hcw?? z{_XRp_j>*%p0}#If(t^D`s?51+p2j^Dv!&p;yMrCmUME6*y4l;!a$0pO$h+6+d2GQ z=dtq=9;9CF-Bzh=P}v~x7~l{}LEnGRbkqVYjY;?*l`a$81pyrf=dOFi7!>dyz!o>) zjJa;25RwN>Ze0-sojP2*&bbs%Z{RAFEjWZI;sCVeeI-4M0v*CM=`aNOhCyFl^TPBG zLGHZ%I0t+C8o>9PJYU^7jZ*zN@PMpsKF|AdS=R5ha_c`@cLJZ4Ul0BUSiXyaDHmZK zP@;j-jPz7Mme0zU?CbOu_b51ZN^+_u9eths11H8HxTPT^tk*KS3 zjIOZ{E%K#DcLGqXI5wpnqU!I4De)vR<{b1dV}D{XF_T-wNrsDuwLkfJuzw+69%h8y z8S}nSX7DE>E*chX+f|9VwT$%Nc1R$k<=!q^fth=|d)KcRdODsQ@7Ckmf)Lu%ONYhB zr~qV@**f(VxNX@3OIz3(>Q~CjgS{GGzkc8+KLIo=>DeI2Vd?h1;= zsZTT4G(D>Kx{Q>%WbsM|YbmQuY!4`Fq)53QFfDke)H>-fuyj#OPSW9+TU&gPD14U> z>a2i{OHR?(U7vXc#{kC4s)CNgGmC8v@kE$Li%7o!rOTEP8=mXyC}xHr$-YL?#pO~m zy@N|ursc>XBV32j&nbFXe7taAqOO1zdxI{s%^^S`5^I2<1V+8&p^pT+nykkjhXb=L z=P!1o>j+Aru9_SvEGRIoj#F#?Awv5OU79?5tR}HZ8by__X*#QI^<7^-ldK^7&ZziO zOi~f#)p%HquvFrrlEufzM~(D)m2Wq7&tWG~*QQj3R+*N}xUls+;0fT0tydUo++1}& z{Y5i3FJZ(lPCMDV$@v%MKx8zi-14I7IFm>Oor z7-JWgZrvja>;wZCffMJ;aJ{95EUs&>`K%bJ?arnh_R#V*WUrZi7mLaDLQo|X45Y+{ zA~Mf@Mk^mX7Vrhy87$jp-G^Ze-xav_@uD%M-Ym8~Tj1$B7>Qz4?d>XC%N1657xhwwf&^`l6iszlw3**-kK=FX@-8hlE<@Qu;C>3UWZ+7l7LgSe17 z24;;msf>uwiO3>h(QFR_?r&-f^Kf*Ktp{!w&<=ngYPwTyNJ7i<4XoTh2!&zxzZ^Vf zxlmeS^Z@WMEZ*aDJsMns5`{%{Jp!Iv7dB7;eBKqzwhF`}RthD|KK7ExcmVt;?&zt) z9p_VpD+PnTbL+oq%zWaxV9yWiHe8+Ci<^;0lo{GsRZE^(JJeOqdZa`k=UFQagim!v zRkr-G#sf_R5GQcWv&|#?Ls$6xhnfcr0CMNCylTF?)7$AMgC<3412>MbQ2O0`woGnQ z(L{YeZep=6l`1k`33P9-IE^ymQ;8coyA`v4#3mv-t9|* zONOWVsz=bcWEzph=>1LEf?eoY>mdmL3q3GulCD!|WDY_4&>~ByOqT0A(#Ep@Rn@zq z8ZVk6?cd>~y_yc35|z)Bzghxr?c_*L4WKJ7)K+A4JhUCh65?JSvuHF^N*k26J`Sz} z>-F(6y;>h481K|U3JSrTYo_VnFpD`Wv^-tTP(opSyfgL*+idh<5VV>t@gVi#+}iT~ z$}|1SsHU*BE+fbeZ*tqETJoaZiy{WHfpKTd`Dq5vjgD+jN{2FXH!y#r zs1K*P#w%>hy_A*Bxh~gqGTc7KaY6VTJ*7u@g z?I|}{(8>{aKo!sTcMIU!yGs(9edj<~KO}EKGo%vioZ^GVmbmW}bmB1AmukqOO|@?@ zC#E|9)FhT(;_hs4=bewVIr7OZ6Adfv?f1a81f1nidT>GV&b-6}48st7o46sl#2uu^ zSl?SAQfFt5mi0QoqqvShU=|oiZC)2g-0mx0$eR{vKF-P%)hahV*Ui)4WYhtdTss_O|B^m_K!vi1)YZEDuviP8s;B*p?PW;kcQ; z&6S|^Zrc}FojY;1c5aK=wTsnEb0_;4#Ql^58Q#MCfa2{AKNCIr_D}5ZgxhCaxg+= zWH~%@iF?y#$pDVUS+G%Pv72=QOb*co?uD{%)A{Ov%FC2^&-lR*QO0E&5 z(uA=7NeSBiFqdm`d2>DahL7<-fw|>g6??&`cK%4w)p|g@kEs{wuzlzkc9*ww2*aUnQ zNWI9B_^&L@zKb(eg&+yvIKjz1;wu*uEnaDKbQE0RX`lNG6t`_;HGO{eGh@IR)f>jS z+Z5A`aS=8aa7=*oUR=EmEr`lE_IdJD7oVxGJSO+Y9WfI;wF^6(CF94g)a0%V7O|0( z5CqA0-pz@&cXXuXHa=P%WYIJgHDRHTQfV+7>j4F!Si`cu0z0Lokm5WSf!--FH!BO^ z!t|@1$IiJfQO`7XJB*wT0MeML%Qb~caec3B9LO!gl!GD3ex|9 za^j6^?X+|6@m#Q%B^oQoBjYs&&LH=pcO8vGI9u{l{(HsOA^mMq&AqZVN`!`DMrfoR(m23XJOm?c_Yh(# zCLVxM1T;CA6+&N=Y*k|QEu}d2V(C5D$NthhC_{W}L=Au)0897=xCN6Z@N3tob@cNB z%N4mxv9K+F$8i?{6nutR050WheFU73y`Y*7q|Rru8y2_;QYPwXQ0g1iDF4%FE)wTv zjRBA}R2l!oM!LGE;%UOIR*=c;uTy(=niEUks`y+~e`M;?l?l zs-qFG2o7(G8>mkT1eSY1?v;oymP82G2wp>GkJeGp)PgW-J~(>|K0d!BxjGzH2OHI# z6dfj-xpJ5t13p(g@X9(_@pMGw9p{Vz86K+E9ZvqVr=c5la<${Q(<<;`@VU^M zj*V>Sy<_9ssmY=Tp)sVnPLc6m-@T0>xZ)F}p$9fon7qhiucVXY=0$(jmg@>xw+0?G zs}s*j3Rm+wkE+K2h~&+@^?c_O7}iwaS$Cmfpyx-T;$?lf;avEpx&pv3ZfiyV05R=afOfasLa8$d_yvZ1>E6@9T?9%a@Kn%mJ-iM)*HB8V_oIZ) z3Q+B`lQcj@pVL~yWOfZGY{Tu=CilaP0;FV;*^02HfuO#b%|vq|$joSsm{*86>(Ygm zZ=qqMP;{he4V=HUd4q`tlqh~B!INAR zsf8nfIgFxoak5Z7Fn?Oyat9u&rKGnWTHGG(5eKskX&I%2CNIbyk?!6eTWpR}M}C~+ zsm%a3RS@Bx6j04%o{X@DxSXgDl6<`SR^84?m>^gHPPk}hX~I))*Zdd* zfPCHK)B~tR&p}wJ^3spN*JzR2R7p-mmLn^DApk4Il^%rG0us9tEHxup+!1MoW2`rI8E4cu#-<)rs3w%?Ns^@tS+#m@_wFW-&jqETk27pQq3&!0BS^%%Kjz>I9?vIc%`k*KdXVDk2#iFHuydprO*0|mt z9{agy)QJ+hd0nt_wOWxCM0KU+!4mjX?@F;fJ*+|E3>2UOHpWBdWSbl={^<8T-Q9vQ zwAmmL^d+xbm=7LUgzf%5S`Qy)5xgmEba4eYASd_S)vqY62s$~tvyLh)7@uq4v=6+0 zcBN)BQlSD~RS$K9$!@gEO9yTZ|D7w~9zxNF~7d6SmyqmEj3_1V70u+5+Rd6!sQ9h<1P$~~R z+d=rValj9GYgQ-UcSKjA_=(#4;zZ2pkL;xt5-ww>dY0&*C~Z2AQBEj=pvVe9Di^7* z0w~pt4+a2N-|HBsx@w1)RnE*<_z&VrYIG}nYwO?n>_*%cq;jHsGx`Ee0QE)OxX~I7 zhtD>O5kl4NI;$)#ExT&v$RAtf0a7jI_iP$3X1u;6iy=7`y4`-PFLFRUCB8Lpk$F`` zws+ZdE_G0pjVwNRE*+QzZEmcq4c_M`XF32(rjFjf-9?i<`_1`Z;vL)kOIyo*C%UEz z2{fi_&`hwMbiY#{$PSM=;&Zk`E2iS>-r@`c)Y?I0{!5fleXxFnw7^cftNjvbR_%3S zj>Okp1u^WzGwPK5&paLx?r>Bn6F5L*{G;C&v8xww{F7>%%rZ?q{1~kpvv+NysX5~_ zKtW=}Am^3IyA@sf8v~rav;dDjs?-vVb_X>VglZc}Hu6wxCq@G+>B`s>Jr@9Lykz+T zeOE0W#gu^l21Po$*iE@gp?;K(yf z&%A}W@_xC!xdqeTT64SmehG_Y!)ru3=UV439IS`l}c4Hrjp3B8&2 zf-?{=hJ+T}m~_}MB+$(SxoA8?w#7i>zhY21X9=SyCev>KD4_{Q$tt*7AI|~TpBW86 zM~%X)F__BAoNmzHPp&-(q_V9=kwT;iFDv-s{@HIJz$a{I3L8(??Z!t~@TpF=V4q1;9AV95H^0_Nx(p64$Ho3qT=q+o?7B zH#ePgjV5}@XnZOO8gEcclVyVBsv%cyx_86WP=#WT@VMh3rsxbjflJqCxL!yiV5lp1 zu-326_tTwiH}Ux}6?Wt=g+mhl*yD|Q@RY7=-i*|QjK7g7ZJRH3i~yEzSFxLAdDrpxy4Xcv$+ zb}!d}x{=1IMDO9lDGE!F1m|~P2tOFjuu|p@xY=fP{vo+A$F98{ciB4THg)%6RJ0FIw#?q zZdd(si3JXnSwVE8v{RDmyp@3Y4N~jL^JBF(?JChe1(Ru2Vgl!~-&~oDj{X(XyQOIy z&^nFva9$q2wT5(HWx!#6ZS_jD{OLom#76Kz=x*Y{$I?JE^RP!7So%+Zuf9%{RM38E zWwAL&lh(Q(%m)AK|8YeIb1*bw9$!Du1W+;<%x>KFXdroj6<4?HD>BJr zK1+E7yWUtX570dsu18|%-vXOx=Mdg#Q9IUI6=`BJ4f$zB;Co9w`XyR5kT~jQ!{1+h zVXy7ej8rn;jP!Ikyq<4I)^&G}R9|SN4P=mWT%P5!b~V|Ja(NV!7B_k9 zV#MYGAdmbr_fycwuLq7OVYj@I*N-e@(Br4woqr*^|^);ZaWjBJrb|QAh1BN^&#g-iF z`VC{hVZc&7xW-b}(ZKbyU+?bR$j^z_m5h+jMb)fD*~rmz^-saY+_RM5fDeF#B~A+M z0Da_L8J?Ihsoyp=wz2tY4(h79F)_VS4yV#+(EfOIpnLCkQwJ~vP{wtm)!e&jRS z_yQSlQ=ik0i^k{t^*rU6bFJCNf*3D=huTZxTVyT;%Kt`EvyJ6tm-kU>&ytjAxwcj% zN}cHsw}oP!w@N&xCT&!MF^U*lA#KNksK{!&w^#<41RQ|_6korKg|%E@0s;HCAHl<& z{Y4DGjDal;X>knt1Fb0MmnFc#F2fE|*crqNpbH_F$N^qKmH^NX5zi>DlfHHRK@Q5K z>6jG623E0wlrL?(oOAu@MFRtp05Ic050ii-6AiaN@hf$a^zElY&{4I0l?AuhI>U^5 z8?9-0ziTqRoqmA*Cd+ALoZh3>tWxhn?N~s@@aM|W-`9IbL@s8A8NiAWg|1@~&dT;m z^sEpE6-A8dl2sRPKh3Df+^$TUqnZqJXRbUb*76SXJSt@VGX%;qbl-H5T`y^+w6PEs zK|pB$(hhaOG1U#&wfi^77=;7OY+Sq0OcKK1)s1k;@#}T(O%TD&E8ILkRjzhV_X2G2 zDMKlc|D8!SL7>JCIDva66nJ9x^s~{16qDR8FD_`Nhvy=%uWOAev!mje47`6V^L4)DQp>3sz}{l>1*g{ct%8I}bLbI8|1N#eX6uxW9+39? zJ%uzU*sTIMpH#Jer8^=o@(b)z2uo((|5+q8%wra0W$e>JKq*iWd}ow>!Z5i z#UYhm=nxbWg$0OsZKd0po7dg1z*1RzTN&>H@7C7$NUIVP4JK-EJJMrn>$=U)F(ehi zW8OW1J;4xDVv%O8U^AS9Pnrf!@3Slfeg%jms6PxJOeX4{(|zf(#2quq%{ppFk7$glv3|zkIOp&^9nIj&lkST+6la1g+7{Lu~}QPUjvt>6z>33 z)PQJn3Lh=oVq7f6Hi~HjkMi{2AXRse`~6WhI{f@sON~f7Qc=Iwz_0gu0K0O0dJ*}D zv_Ax@LdV(b#ql*ni+A)YCobUUA9bB^DP~BKL%*+A&8vY`2x38DI$PxPJ2n^DR4P>QSr1M zwpJ zpJ&{YZhEQ05G0`h*zLS6tcznV>=n9xTl9n!cZa|+g+mV^Eru9cB~p+Dhnr*Q`_1AM zLrkF?D!1?UeP+d#xiIov?_C(ewe1lANZcSZK(XA3$QyMqwmML}bfv5*fj=1Y8>nQ=Vw;uX zB;&E*R?FF49Y=Z=U4hSE)_}38faA2|kzoFdgeY60woV{A3Vc001f@_-9zuSL_p?=aG$da9TbCbrgbVUii?@ zo10vIhpYjGf_5rKpGG-BtS?WhK6rDL%Lj4qOv^8SrVu5LQH_s5xP(H|>hDkJ9^sTN zJ~kjeJmuF>FAHImxu|`G<8_Kgb>*bo$1_JnFFjGbB(KmuxHJ8xgJ#8dXl+KMa7&`> z*{fbr@u4E1Y0U}n8~Dp~P#G|ntfxp;%6f4`oY^f=V4(2d^i?+IY^48!IgZ@~xb&@G zf?uTkOBtIaXa+LRu!zQ-Iy0b*qXCyaA&SCLOu8Xu3Mrb?b3f+b%cjNEhVOgHdt6g} z(a+;@ndRE&vEfd_sgvWSMINkSYgyKrRJH^I|B`%4JnhhqR#fZ@Aw0GNExRmTvqjQw z2$d;=5GJG>4OPv)!Ur!T#wLGZp0nS%m#wfZwdb@b;vR+=fWE9cVU{{b`m`R8%_f_; z%;vUBo#xC0G6DnQl)e(rU-|d~Z?e>7P~*n*C@tG;Uvc-=00DbOhmq}=LRO>ky%R1x zkj}{|uW))H(iFYvql!ycb6zC9bYT7xdfBQ~VAMZ6SEz(?^9HX#>apKUVM)p|(mx7> zO#N^IN{s$^Ln{?_L=n;eB@tMrz?5zbv6gy$q06n3p8zQH*ymrM9mX;FcW@PxoHZL3`vRtYZOon>&r>WN~k3phvx+y@SSWkyCF!D6# z7w*L$P zTRrs%?`xkPz${SLD!%p1ms5J!Uh@_j9jyhlRCyYICPMb-wy}IM|gr@3;)S>rh zMrr3_C$YQR8+}ECy~j{gPt)0v^1TIiwL3H6DFR>g&SV~Cdgo~W0vV4G;(EEsm_yhi zadi8xUwvkPYgEJKp1RNP;g@cI!jotE@4R&siRc(u#p};6#&~GAPfMt|b~9nIMVy(^ zBoz!(Yk9LE5^{XpJ4IS7<&ra-qZEWUTil%i=jID0<&2ED2ZkD&N5Uc^Lk(xR6(zNgv3gi%Oy$Of!1N_TWp(j2 z?90TTJ=%JlHh8!~+CVj1)%7Zq%b5rP(jx6?&MMbb>au}rZm(9tlH}1kb<9oGx0dqC z3vEV}=HAyAA!xL`&91G#$}iqIlX<@KN`wHSm8(5_L81s`lEvI9OmX|Nb|Qz6FF8(c zfa*OlhQ~rh4w>rdqV&hHwx28NTWI7zm&fZ;k$DX+NQHU*LFvp<;xCGMOY*ynXRG$b z5-^(xoK?fb&s~eCxJ(aaokwcybDdX$=^&OjAec8WZi5@vR9+nyXw@27wqpI|KYrpU{i_zN#E12Wi?D9yeqQ5qFhcoq=Dm8&f? zmjTBGbppA6yh(apNtd50zrB2vqipb+V=cH;p(h`a4}d!v2-fTyXu6mA^20&pj(a<6 zE^CbVnzOOUYj7h@d`QQf&T)bDWCSN= z*$*f7{iH8=uGO3F=ZD0ok&2tP3fVc`@5knU4gRIQ(u2G zhDSx&1|o3oFc4{3(_6$nfBq%(jzZjS$!4?s`&~~SzUl2b%Pb2=oms+H-cSSY2HDY} z@JOl8;2ImjS0_A4#ZsCCMPxjFTwz~*n}*n|+C^Dl`o>F*V@@q8e8wVtu0hH!bqx(t zW`5M_ZyiIcRjHnfHq(Dc8aR5FXLkVA$ps-0!-L=OP7$Q~j*JsKLv_(q1|0O>BbW2E6ZC z+QaiPIwj$IqBCh$@FG#2(ReMF^Obk)f|yN~CR|eNPAphfwgNjfc|CbLC-Ym9ejD?H`s7dnv6 zT+d*(8ooE&n~rRhYF})$Y&)We6|FdlDBn;i9V3Cp1-5co?)7E)ue{#@W21o8rE=%5 z8&1wejsiPw6D4JNa@USDO+5bu<}?B?#8z#cqKSiD{8TOi((N7(NnMT)S?eh~`Fbo= zfKh6p=XQ#trIXq0?YaCXj~BH&tSF&OHI_y{d54G(G=1Q@>I{VcC_t-$GU`VZ+4gSv zg1cAsq*%#PG+mdarKMv*_D}DI{HwrzLo3lqM%x?SEdU9FoN9)^_uF&c0X@+feb)E;)f|Jd^UpIf)Haq}q8H!_z?Bl|>Hu zz~@3eHY0R)?YsJFdv4E)TomAgJNkpBpbBvN%EKnp1(&s-8B(*G77&h89J>{fITgC) z?838SF&MnO?&Sjb#81(uPd}`=IM~eI?yw?<=8r8H++_LwP*2a{6UCp^neO7zA3(3N zI6(-_ACz~6QYJ_hxl)eeRc?(LX0%l83NZRB$DhhCHYnhaIr`yJg46KGqrMknoTGHO zq!&(7NV(p8Y*qLP_V1r;?#CA&$6I`$HlTZI+mKdB_ERRy$({G1Q;f4!45iDWU(mDp2!5x-Vg&;I%7Z-Lmcz*?;s_am)T(8w>s+(KXd#>?4X{o0l8 zGTIO%4=1pt^!Ih$6Zo+(2RLGjqPT)S)y)Q7rDVokK7W;%sh+*H^p^l7;=X9m0ZLvk z*LQntKe=*Aa?s~nPjEe>J{1DuoFWyKxcc@^o* z{Ax>%&r4jg8q0CeUft;C6WM7nvt9v3DjRT>TeT^ORo$9naRpANtun|Q--lN%<-5xU z6sbhH)e%THw51{SZ!{WnOu>X+n;AF+v37X4r`6JT~cEw;?1hx8PA=gbB)>z(z<_wGUzz*oI%Ar3nO(C{_# zc$%XqMvj1|pxCywr+@GA&+V#-1X!0&fzLvZ!yBLrhh3>PGEE zTD~#~v$(Ot6V9Cx44d7KFjQ8yJ-#*v#K$?2Jj0;}BQVtr|8$v{h>uS159kQ3Kvr35 z&wnNX@Cc9*AW*gd>|e{(3(i393v~i#kfZ8%mPkISV|D&40HGjspJF`aw`KtQH-7YZ zq5F=rK<1?g9nuV-Asbh-X)*#uS@6*;H2xuc2`i6L#q08~YJIT$v8%LgONSU(nekX- zr!f~@bn))7MwO~TqSqy)|6nKy^u7$T{%tr$!ZDfN#RBn+^0ZQ4Mbu#s>D<{;ROeKG zyJHaW^!;$;Fu3JpZ;?e`fEB9#XjbGHn(OhfbemrwHa7DfDTGvk`Q$g2p}fMyS>Qo z@Azh#BbQ-^Ktj`dw!=Uu`*71~D=-ZLg;drt0MtIxtxhC;16!bsYzPIf*q%Q;?uTqy zV!TL@!^xOO(CX_-*L*R(ho%nbn%X$C2p##opW>-h*5-HC&Cngy92xC(z}XQgP5o#? z!4m&I^0HY1J?efNyQ0D69Y%uO#_4C3jWW_2v8SUjyCp31Z)16;bx66s?y`RVt@&!j zKAqBuk4`;zvczM%Q;gvCvnYCOx+s8>w|6&d`)OzVsq{%HSE<^JBnZmjl&-2m3b<>H zt*8}MeVXmm%0NJToPd8C2T;XNk&C)B|KWW%D}I#4)^s;nPJMji(e5xTD=aA>n%}9B zVJ+`}nptA2Mlv_5UVF^@-9Ad-1uDAxAu)1D>8LLsK%=z)yz!-tc321U^p`RP##M@* zL^7z!FAZGD9xT6Cxc7E~mdL#> z+#!UEM6^O@NBt!*Kh(<4LK*xbn{~9?-F?t`kta2@?^|hbgSA4u9SM$<9O|P#Kg9fW zzabRW^xz7f&S@@18KM7WPJ{MGJ)_K>2Oey?WLbZf#GgIDaaUume41KIkDl`@4ldGr zD}YhM38%&k1_~ADxOrSVP5ot-;Igps0DwJ%$Q;-jcC#3qdMThr8(8}o?Rwa)1XQu& zBUK)s%@dLX?XL($@bKSE5V6v}ZYa{H%9F%%XkFj~*@DYLrA^dB4mRLtf!Y#Mtov8L=Yc+(9Z4T`i(aJ0fFH4b&T~lKMjgS=*o*NM&0Tv zIvO|T)PS;rmh~+A^eq1LXLHB4`D~J;K@q84)>quNT#pqrzC(3<*_k`)O#^Y&Y$>#1 zUU0`?1Az3^en>k;GX9J|qZCW)*hk(-(Kg`g>Ygy+8L**ZB%~qYD+Jkg%3?N#F`M?i z{cUqCSw(xgX{W@(b!5aL^^ZGdh1*g6&*v@Ux*%u|uA|q;+QweBz69))w9UORFZDAi zm!7EuyY{KxmP=PoJtiD{$Xo{Gbf~9kwmBS%ali?8vTfgu&Yf~+j3U*!{iJV&24O8q znO6D<Eoet};h}i*p>q}U46`$edB4NSe4Y$p2-+#q^oNj7isCXHu zbN2lZ-UwK9%E_Ywg)>F!!Cs@BS9c>Z!B#QKL&wh2L)o`j{QQnofrPYs&qWoy-T`0b zG70nPBp}AvXUtab-QB^>r|2%)+es|ft=c6wv#cD+3mVri1i8OKN1-6y#C+{%S?)Aa~+6S%efQ3KrI6=SKns#CFJ z^>bTmZ=mtgou&0%ESF+5p?(A!ap{ZMyk+`bHb#?SdJTe>2}|wvJHM+W$Hi3$K(BX$ z+0S@rMy^A%bSW*it~_7?`#bJjyk=7K!orv3%H^xh*wL!u*oD%;yFdG9fT5FgYcK(7 zJbBb~tzk4HtzY!rw)tXUi1dnkMa4=wxkpiRAHv^^f#(YT1f?)Lw0F2c!(DgRC0Hb2KC7guV;|_ zpV5Kq_k6(VfQ$>M<~EjEJ(v|CrmqgdyUOdQL9zQE;9}P-_Nw~&dQ1nN^fV_P+$_5N zK-5rs0Ru5>kx2Xp3$V{?b>S*)5W06vWLIS7qBJT1LOq7%9oAv50tHlys*xjAM+O6q zF5bVpv(~x1Y%37-rE|K+e)x5qRH{1NHR9TyW@qZ;e| z9ss|+8^%=8Qh-5pgdL7$l7x6)bb%5{`!#hqX$NXlhy582b`|po8QBlQivHl){TiMH z-cx*BiT3)BMud!KE4UM5zlQq?t4}zJE}hQng`b#x=%7Q#g|3)_>(%ycPe3(72%=qf zT%h-%g$^YFRb$`wj)(_C*TH56Fy1RDAoAB*r-hjJ?F``t;c^iWz~p_Oqd5hkaS^op zcT}e!t^vEA)m%b!VJz}8WQLaVo04}v4IZ>xa$ z4TO~W%>bbvdZP|_DeCX3LD+|~F>yo2s2l(V+pp<6!fr^JLHk*E(-#n)yw!HUUH~;T zNIWmkyYsiojv!vi+|)`Zg#pTbgCt%|_J7}}EHDh?9^}6V9bQ3}uIF z9W*?)zr*8+jAi~e@B1DF;d}qv;3S>$EdKi>_Mag9tJdG8HG+8oBtx09aX{CVzhvQ5 zP;7V|!jrbT?%xmR46oV2?eFIf{ClGoyp#tvTsx7~Jm09QiKU^2Y|FrLH8_CfrNBw} zpfNqm$St+$KH#mUpx)wvp92h#58ko^n~WLpn17E7k3pS<8WE;a`*)R*B%U~ffWTPv zfA6>dJK$f_h}3uB-~Z)ki!MnT{P#V?eFvB13jX)e%6JAupZG%tStPX|M7@ec>KzB( zegnW;?o1u97n4MW|6gXOjt9<=3KEF#zkbgD7uJUy(o#t~h~>c*!64qo7bqT3%;mok zx0Q+-)4w5F;E!mo-gqic3h^Bmk~)Y5LI02waBco8K3dGtN)2h<@IPoOM9IqA8Yw6( zjD{NJOo0lyU;9V8#Pxym`fvJy>!TAH7|F#EGLcs;0uN%l|M1gt1x(Vvky)rgz?}I_ z#rGQ^V9xN(kC04xrDc(sBZR-!YQKL!+~!}>^VccW|2OiSxo{z!g9LX{z2$(@=R?&0 zj#~oF_ut5Uk;_i}(Rrc<;Sj$3L#U$a0Xy@L{6*Ys(7CJvHR_v+?LQq*{D7#`G8H~8 zM4UHcvXFnC8dN_qb!@dcp!eTNp@01Lcc2C8RuJ+4wF9M16k!}- zm`cFkaf%-@4od00RdK*tG5uBS*{!JENpK1WDziRnL}R5I_%{EIcFN8fJk~rOa!#HC zqCFxcZVE$1_G=YHE{Ggm{(cwkiZ?od@4N%I5dkx$ZoEzdDJE_C8^Y_~`V@aqZ z2|RBKXbYJ`>Zly#e%}V+t-lJVC%jagV^y@Lq2s)!wymc7_l9bS0tPt=50(-lza4+T zlGebeI0!lUrza6@uWY4qc(AUJ`jMPd`+puUL7Y_ie+J6|9cMSSX*JrvH}uaS zEL|rUeU3nf@Z1NjEPM?bgzkY?w)pZBCWQWr4<{8>50J$F41yeZ@df=L)K$|<`+r_0 zpGf+Lfi%6IY^f{!4nmRL@`^usz#!DQVcFbxO!|IaL_qXu!6-;QKBf4U*8vO6``7w- zIXrAQF+C(yK@ADL>S7k!*SP@902?BEIVmRQaL!-%{pAEm-76*kmdimekA!83@p~BI z0=)S&7a$t-)~=NN{U-{&BN9e-UG<-QTj#-#2IM%zD=lUr#{IkF7XOarijapGJeF(- z1>G`?Z{N@D0N&6@L)`Pk$}smBcwLI1M#WnX<3Z)CeOu$-^PC7AeTglHHE9K)<)+S` zX2nQQ@npXygC%mRlj87bVCD;IP<=>>i7Nba?|***Mm{Ym*d-rk@i5{Ru9`ZFt?PT3 z_m$@~nmsI`>?{{~sLIP9+AnPq*H2$uJE1*xV)|;=v9os)RM5xcvagfsRHpav2}WkJ zs7&W?`d?BOM$qhvU`LnUd>$6#HCv0LH>JJT>yKZ`DrXe9InS!{9 z(EmNz|9KP@;SDrceHE1q?N`%XVp9~jwIEAOLEQgz_}~BV;~xYEhlz8geI+~(Wi|p? zLldCBD1bWb*UF3;U@39wc70Y#ZfN{%!spXa-KCX5am{w8twG^+fxT^G6BtF+%}z;89O@KRQ`YG65%Ecx$C zLq*)ZmA0G*Q3*Y9vW8A(4~mXLx=JFKdcoxPZ8g`3FYZbHq-i+xeL4?7OD=_38WgQ}PK==gs<(tO*>|EtrP=G4$6?jgZOo{}1w_lUlpaRPRWeoY}0GOq;0_+=iS|VB z2;08>A!vjfK4jZIj-1dF9#FM)--gnK_XgL06x@&f`6~G5zO9nD67#-4m=V0pO$V$m zZs9Ea!J;?t2RRP>U*@rpsivLS!coey)Y`poEO+Y3Umx()fSo3BIpw=89fsJHJp$b`P%6+( z?L=^7bC@sX|3)2PQ$xEgAs^p>xB7<2J@ByXfA#WkmJxN?OQl?%UiO2Q80t%Co?F{W z0)u3E-fMZZcr==KR#I3E3ed5i(QbX%5g&m8{?L6}S)_I<68z0tjk2Av;Jf zptm7%Cecd*td`d@oJ4fD^PY@DMJ+#`cLJ7dI5hOx;DAZa#}T(bc#RNZH9XNlufmEG ziP)XI?ONd6hTCs$;5X5WMB1`7&lO(?Oe|F_)%*?kipJ!FD4oKi!LMoM5BSyqFOj_^ zV{#u}&2Zv*x`Lh{{CI6SraFLObiVTqlvu;_t#xrFvq_H9!~U z6GbcFFzwf@TZuCp%i`oD3h*K_R_#|gicJ=M z{_Yu>r*OwUwPsa6tbM~5oolq;x4>@!!NkX0?F+9hM>u@QTl;MfDe)HC^G~Kt-KrOy z0=S3$+CUzWFI1Qn_P(@Sx;brbZ$XVGGsQanNt*)Hmw9u)hrn+)UlBjo13o9Av%}%O z(LZN#NZ;EH5XB?sk)@Q;_aNxGZ?k?&T$iT#p}~9G*pcqvCH-G6;f`K*%0C*LuIvBu zbyhpYz7IlWCPap$P@C26ynL?&UK%kR$Gud{ExlqS;4x+gd~pCxoMSxL_a6eerLU9q zq%$RP9O#|4#P#*nFnDziIx$2&0g5-n4BxPgbTHg^P$=S}dSgX8la-bwYF z=LXhmXXb_g4Vp6zP^o&@os%lrNz?~_+qcCNXULly?a#BYG&48cDKaHYYfW`WgmEai zTkoocdnMCBgXJR9lj9L znP0?J9ab$nsvmgptBBI~hyZDTnkL(iQXs(q_apIpt|a+dT8I%G>bD0z`h@uCd;yRs zYMLHQS6RqeJijWHR~|Qu(%q2w=ww9LcL{-lMqK-{NC~sFDgro}(e>%45Q*xMwZmWy zD>bm?atEy;BfN&t+}GY&8j^tSHE|7*{iDICV;dKDJF?ag zP<^;5|5YKv#RCpE?kNwvS=LN|D7HW4uo6lhh$s$dsP<~VfEO5y8@znqHg83ot)c;D z_?K?kmmF5{NF71)n^!IP15-zJGrtH+8;S#3K3S=TELbGoh7$>q+n#c~$077^4_}U@yXKzzlvqZCg9{~uneH$*A zXf&dxU&;PhSQt1rL>15T6L}})(!(nvjt8)Ah=+LO^xpwlFCZTf2sml6qyfO@utPVl z%4(Q7pb|{68@2a`H^Q~e?;o@_QLMzZxs{W(aQ0PBmDh{m)tvyz@8=?mdK^!gNigPY zoSNEq$Z+J79X_<`6otKKzI98}$^}g1XLaUiiW=qQOK>wvGkYUjvLY}F2e=TS3Zhw! z_p=v{DS9O;W##ap|HAYsy42f*p}I%IcR#~tH{pP9qqbp5300Qc7cvmQw_^aE+tX2^ zoy49anV%XhWF2@In8b(&aQJ%l(cN(MgIM^x0QjUqdvs#Eo7NX%a>W5NNRpcX>R!|X z^B;nx#Ido| z{hig!a~3wspMZ`0VbaRUcRga%b6CjzB#@D{2ONoith6#;K%HJM;=?(UXQVm(Cmz%b zx*RZCh$yOXoH=Y**Dq`~J&wn~Li*Ibd0*7?qHDX3y)}=`?6}DRrxxn_`}A-aNx;^n zs^j)5o{F-Heca^8J3$NaVV;%CAVL5L8 z>1(HW{=bP!H``ksmAw311XuoJuwVom(lZZOPwlt6!)@`dUH03=U;G4?=n1?0xkgp;%>h^_n_X}@OZrWJf3~``EB0q7q9%Y z_OC?prz&6o{hS&89_Y5iACKdRPKG_;fdmE>&1W+f_5F_Pw^jR_C#WCy`{%wd>+Wl} zCACeh5);;U?q2WxUaRuktFx~Kj~#s8y}8)~SF(2C1iS1&p%>$cxxMcu{%N~a^KbS8 zB^>FI;XSz5k4u(e%C)J$V@GjHGcZ7Ef1C=YOatdKT=D@?;7GwG?T`uf3@&MgrQoIq zPU%t0aRkk1u;U1r(FBJhV1{y51I{|go&foa=hOCm$HSElP6O3TIKd4e&`2##5d8?) zi@*sIyg>~noFJzY*BC8GOM`A9u3{Y|y+IZ4I+|@qaMr{?%?Cz34GKdPFdEY+!2#xs zCU1-|8O_reLBcRvkYR+$Xz7g+B%@UpW{@z9*3%e)GTJ!72oi?T#tCMaj5ba%!eq2@ zf)OO6jT6iu8Eu?khRJAu8Z%5NX`C$h_O5{qXk_AqzrnX3H!Cqj;vFhcz&})yf`6z) z2k%hH1pGrKoA3^mIN%>DIe~wCXw=gfscJN)F@l6)G8fA~1UV(9RH z_}Ki<(7$WvkHUY~$safST_@0=;5tAle<0-mrTl@E-<9$QQhwLTA4oYsC(s{AIY29a zAmw+Z{DG9;b@B&N4$#RTNI6I;e<0;|t^9$M-*xi;AEYexSK`qDfsp5RKdo;loq>=% z|FQj%&+2gK0QrBNDsA{rt(Cf%a`InS9`L`P;y<=+^v4wrxarp^W99$Ym$3_E!1;CQ z@BjZy`gfiDvGngc`QzTd>EusL|E3b?50L$?lRxPFn@;|utlv}u{mIk6>EsVN`AsFz zA9C`$PX3US-*obaocyMe|I^4xSed!que|{O682Ag|4SFYUj2#hUz+&)|Nouw-7qqI z|C7hmEyfyqYS-5;rH|>aEF6z8y-j{O4QUcW^(ax7Ma1E4$SjHVY+bowk?J>#VHL{A z=dJRLg}tC}u0+R9hoaEp&fBarRm^?(gfr!W-9}EaauB4a{2zNQ z);wSSQ#U28|&IL9}}7ylmY zf4p$K0=owCoyNToqWIUc%(7c&ds<95$(y}Al2Vt;f9T4x&SIPQ(ou{ds}k>vn;j9# zozz^dPCb&>TEJkj=h1*}%*3{J3W-sdqdD7s&JG>f)1kuk=Q_}qOwweM3zlEq{)d3S z-uB2~2HXDYT#_?uQE&GQaTP`AI>$RJf#!hj^srx-{g40ha~RdS-ju7Jz6h17Qf|j-Jj8K_pk&O zGsqKR?kY}JXQ`4prFih8A!}*aR!C937R^tBH~i^ih31-^F2lBVu1ZNBYB%e~wxey8 z9rn{60wFrh$R-L~gRWQKg}&rEH7E>p=U9<9Q@9Lu_B_EhqaBQF!o46&)F;?CPXZ5D zq%GR=c)Yk$tJQ)wWRk)bvLElmCX;^>r9Po2o9VkI^u&J}u zQCF_o`+6h&G=kT~0iR^`0+sVn>-s<|hgTOi$7)uDoifeAQm{oyHBjWd@`;O#j#KB{ zgj_ALXjEJe;~lqq)c8`(c@uTpyxE2iNsjxbOxie1Z_S%W$yi~T8}c1SrTn~v1x+O# zD1)gX9La@%`AJW=i;P&h#A8kvr(!Iluo-H3ro*zN#5%sAwV;XB-ec7mO)v)dt5^sr zYZVuy9&seCioCX#&(Cs!AEsO(hFSKOHWD%E#U>~e_ zGE6MPhUL~OI*gOO(>@<+8{b~h;h{fvc{?K6pj(v+fvs)t`vkUgok?8lNuWbd>JQxy z#PGu!IkpD6k_|e)&0Btbk4-1B7(qvx6{xdxCJ>g@Ed1JK%lSd#zR^;oSnh%LPQu>2 zRB9=hX&TDjfI-U*c+C#0Xf{ToIgj^Z1Ozn*->>p!Sb0#&Z>bVRu%?%eRBUBh3mT>M7!MIiJ%TzI-?Mf|!syuz>qsyq$G;#Jmc_w6jt&grViQH~H;zqqzmbQ7P^(Ms%sBEi9O zrgdWUxXj&O9vxnk@?iw;Yt*-)PSp0*IxNd2$WjV;8Rm^WYer$prFw<56{R|7sXGWE zvNyZO4;}<64u+M{bCng0(#vi2$q1?=0`S8-{SXPxA9&cGf^Fp*Y|K)ZNbZEifznuE z>5&e{><+n$;bH>@;UvuH4nlO3*Sn?+rVQxEWPoRSM=}>BKDEO3=qs2D9=EM+oolBGQ?brCa~ zYz`@$md0{ZNFqkx+`#X(wLwU?<9zG~6G~zLbLzsP?mBS{#&zaD)~JXoitm0Nj3eB# zBi%}~DFDP9z%mjoAx69rAfB3TC-X|AGLCRRCNi(BC1La5i8HWgeeBblLao~SHrAJYoZb_5}kA$2!jk&2?g+MFcS)wQxujf2~o1@}@K~x=l3&bdb5zbJwC@LrW zW$KYw9P5H>Yc3LE2$8a6c-`}W1DU=r+&dM5ipMLuUD?5qYfFxF$Mhy|C7qb7(*uHjn7@fApC^dQWUYo z(wGkh(3-7IX;j1z#dp;O3a5fkjRG?nD%4;c!7k;F!fOAkoG}8`C z{6X9Fa9oU4gG99#uLc4z;E*wQ8$mP=XCfAGfcT^$CtuwFLXR{?Cv|(OdPzYxW*7~) z9K)gk(j)&fR{%~ija3b9x20{zq_Cbr6hffP9OwM4V*Xs`@4>~^~KL#`aSVabyy zzD=wm&X=u<8VVI?^WEj~Yps@c6F&G(q=Tf(Utd7J>m#>6l=iC4Et-T&A4^`B7sG^i z?G+%;OG-y9iv22zF-0`6vo#SKSmmGtxq!(vzR}h!kUh%?z|Mn?o~v+nspESjirb?- z4j0@99mANqcbgJ;{b$uGEFO}kj|r@4Al28S*rhrKJ09n=P{=jZ9Se()7sH^)Ohzo9 ziy_cN*V)1K6@oQJ=7VGddF}-~aN2}Y=Io$Bhc{a#AT+L;19gS|SSR!igV!hS@X)~0 zRu%eNP-2NKYi3y2U-P*nQc)d0C?VwY&kcpx^t4ZyTt8aI6sCOWB7^ui26aTi7nlw@ zkZZ6ekfeI;2D?e45;D+sP+_D67dA04Df4TbzY0pJOG*mbepnc7A%yK>S&YmPzMcSt zL1G!e854`j+4d0fN;z_nC_sf@Feic@-DEe$}9WCuc#71igis`0QcuZd$hFyK4st|S=2>K z?RXA;Ce(>3czB8;&I+7`#7!-1+o!NRR^1OXF~L~Grz<#PwEYGLKM`q(@ipp!j_WZ( zbI&0s;FOVn2qrOvZ&jAE8`GSfP=xHl=k3@k)AMoA*uV@UJK#Y(GHkmx$Fn#vWJ5$reJ0v z5Wj*&a620oBBU;d(Uz0G?(OqXCQc$2Z&6pnTxQIm+u=%}dKeN>7p<@pI(U3&6~nDa zN~5)=@sRWdo!BnJASg*mWFuf4-jF(rMPj=GHZhqoqcgRg~FF)l??9Ia&q#4PH?b{VF8BPoih0Akuf>Sh*M?Uq=?3L=b(08b52xbrF5S8vrQyYL~B>!n+iUn~w509ml_PCDJsG;rkc2Ar^5 zhQ?e*02&~Gf^$OFX@y@b+OZ%NEg6i)*2og?F%_uCxD-pf;-{QAqJ>}oi_4}!)61JJ z`3OXxZRarnxbuNW7)DPYhxBBfPD9o>iKGW!1@SY%CQ^6diog9|TxJF;0$vx@LlBR; zXtf=-;-(n`3^sPGMO}ChkLe&5<@}VeUrAMh3@UzPPq^W?w!l%`CN#Rf;pie7_wH`? z5TpNn(RchY8yAjxS*F8F#%O7uRsj4!vxL4Gsa5;B<$J;p4I^dMYF4|`~w`64f zV0`cK!f3{9Tovl!Uc=IKMvhRzP?bR6Azx}0f$rO(-U-})Q~m5m&`#Ifqu>+hC?3wAvhbluio!K zz5gFPMko%41qVr;KaB;jzCXkmy#pBiGL(itzllYzY7G8j`4Avp4Ez|ZM~@DyXsn04 z8OBvQi7k^OA$g+y6>P3@0q#rKSlWux9)XIHGT3Oh7!0gf4`dUJk=PIVeTTF>wH|s1 z(yL3s;$%Gx80Qhvh!>&&5auQO&nKq6sRA`u;2QT#Og`Z_x8GWxM)NKZ9PG|nqI(19mD zsy^|{!~Xd{Z5=~%nsUuQxdTEp|Lvb2|NX8SvyLSq8eK7TrSB1xhSQc}h%K=}Ka<7+ z7@HgtM#^9r+t?nH2s&?ejD{YD=<8Cj@Qi#>jEqyD;Y3v72v|;Yo1x(vHuADR@gK+@ zO6a?o7nW9c-DkLwg_Ly?@DpQ$zM8Ee{+{VSULZ>_*~Mqf^kq@RmI6NHlR5vzf)7iM z`8lD~W8S#PU2`%lSLtBP-NUXrop23Dp1Cjpi)&|Na_Uz~;1-=jBD~O77K=q*FY$lK z_pdj$m{rXmEi(o}@TF@G|N7|PUm;5{hV+@ti|+!?MhAVrTWA$qjcwtf1e5N3%ulT& zM8!ggp&#-CMeQS4D0|&!484_gk?eynMap1#Y8n&9p4~&LVpc1m1vv7d(baF}v332k zWiVZd3mGn*{ZLxd{C}=Ne5^TLDiZV+;JRk*YvBznx>et=E;3Xn3*Y0&lMJL|$w;%8 z;3)d>F}h>Q#(|C*`gnODv7HIa{N*uSY z(3WDbX8G@C`nT6VMHmX)?u0L$Z~eE={{7|Z_i#||FQvMfm;cx}pYlr9p-o-%g@(^a zE1;J({0|{TuCI+Zj&6?K zvg>>}yFFnt&5D|6a+LEk5OXP$+-teer5wZ+FM_2Eqn~4byd8kXh3v1L=3sTZF1vf0 zL9qPHqrw>Ki7?if>#}?69Y-XWb=ta@OSWwZin`w7$cOVx`lfU<7cSW!CyOydCp~{a zt9)_y*0T_7x|R!9@~EbElc z_*oRoc3fW}&!SbH2riPzONwUsmV=I)uV$c7jLYL?XIrYs#k?^$3HJCOqEj5g2V=8* zDvT2eX15N%7(E@@u4Q_bM(E}xM*r*Xaovdg0=Yd%T@kfjbOX_3w{e%kihG*PtFYqz zH2e9Le>cs)UVp~{&Yb2~a_ZxT*}`-EON?@zB;<-r9Cy={JwDunhdh zTQtqH(5JE;ic_7VzSMGX^Awo?N8FF*m#(ibn+2v-MSKAyZN^8rA@W6-g%x1T+I@ir zxlG0SgJz-Og%MzxabQ;Z#YIMZ)r0+A=K{z?^3~CYl>$X#j8vJdWhTk?{Q)&Xjz%b%a#PP?hb&Pgcy@$ja)cdoza zizjBATDNlr<8IpFBc)++Lb+BKg5dwfgdUD`I$!vO%PD>OtX)Rg2bdfZg zRGY;utLKxice5(AE@2NHu*%t`CwH<7&l!m5M)2R)?OS^RAu~!N zwmev^1fwN_2b}JO+AiAFvIHW zDfKx^|II0-8(r1EW&s|(=1|i%V^9=9QhGL-u|4nNlV=)d*7ff> z6k0OLd0EKx6)Y=3zVJT2rSD2h&l~8;rajJ;KjgUkEPeAdjnFWdLlVUeA33kV8e%Bl z&T;xYm4CYKnz@VWl*Ke3$+a#c)oDE{=i^v5MvR?;g=a6rnC$h}z_BYr$(>>z?6D1M z^e-<48Fm)I>fgA6y7QEx=%s{`>u&Y@TBZ7hXrx(ScWTQhLTb1arZn>3=0BIrJvI8P zr$@ciadbD`XrZO$@~XQ5YOn0WOm@q4*UDjoT3?@my{-88_*u00p1rrX_oW)`-&Hz> ziNNMETD#*?w*o*ouxy^zh_~IkF5Gs*gG;}#=36@ig=_i}dI~V>q!A1xkZf`g77B1t z+NO;txog3@QO0Jx<%)GqKVmH`|M<={go{quu5z*Y^z`&}?aK5b;Yoky-!W_~64nHY z($21|%;i+-l)_-_Qvm=^dCM=I$RX*5WY-l_*^cuIx+xFcc?Oq#*Ghfp^sd08Sy#R= zXQdFh)+t&J|BK5P=?pHhW5_3@1X;hWa#0|LL0a4g>rMgf0DlW z)>F9QlU<9IL|lC~m@QO?M^uq|1Hq;dU&KM;EXXGndk!<&gBM>K6@6p{1s{L;VuCka zz^%X(+r&tN7p|Nh{<}7Nn&D|FGIYq3PFFCtt7bLeiMfc?w#IW$rd+y(JVDo0v7HNR zl#H>3*@d-;`Z-S9W=E6kK?b6=mIQk_BpFvg78GvE!|`71-(gRi6ABc*(J z;WL&CQ^^Kx(H(k{K*NQ!mfrnl?(Y4k>yIV?4>AoKB|9Kb)g1+i5Lr4HrAlSzeKv8h z=D0LIx@qc|u&4_u$;U;iRHT`>dWMn(Qb&%6PQ2~P6 zzn&fv{Isc{K)}pjR#ZR$eR5ofX<}lc>G!p~Is~ZNans z&-Qkt?-jJ)fiP3$MVY?L;R7AZA=4!1ThFg0k$<*8AePT{6aZe=yt(Ni2dwlz4{o^J zurEBD%-OUCI&62eL*31Uh%Hb0b=BLo{wU=GZFpCqJ1dDvt>+i$98)Rn%}Z`}=5Q{1 zO5L;tq}nQTCD7au}4Yn~2Oz7YEE z-Mg4$naP2HfrJ8z2W&b;DgcUtWc&Q2Q_K8n%YzcI{9n0p&LP8=n&jljVfk7Ld%Li! zGn3ax5V`LXxTYmhghfW1IDBqT&L z{OQxDvzud=P@7*+Z+v%t+@HSL*1SCZOqv-_80^AkK&>p_X-8*HH2qckv$jrfoS6$# z9?i<%cr%IM_URN*HoTJ*AOC8fe9BM2uKENxP)4B`pG8r{b%wuj^QkZr>?7DwC@zp_ z+wA&c*mtshE}XSrTvGD$lhx$pYM$AC6&&s{C+r3u^Y95P;Uxs~`V5 zzi{Jje{j6$N$}MRvs;FLH0BNV7~B>tiyoeZydtozR95rw04 z%5c~@=wsXYh3R1f;Dr&t@}hhyM{^p6Y1x}Y(D&-d$jI6AePSc-EO071x)j%jzaA{M zUE{m+1_j&ACgVs!lj7O#PVa4Wv#DMF(KcJftI`e~U0vGEAy3%VBpomsg5=4BH+_hP z9oFkbSW){R{4ofSyzlAt#FHR{WO;nr{>sH_y6n9RE2sQnKcG3>gpCY1nf}X_P5beA z`9hRSs_o{DTHBsGT~mowPn5fS0OClQhC*3p=zCjeGd~^i#*$w?UWhU*Cf$ zQO)?UaW3kS*^vSdLxL0o50Y1mVr{mU?Ij$dHf%k_f67?bW;3`&ZE0o2T(v7MB&2aa zZ+}OR9^4-M(FszW`I@C!!kz+N!ipRSHVu9Ur2`&6Vk_KIZ1; zX4|Fxc8|O*oY3SXVv>9DehkAkt#Gq30fN(O<+^AhCnU3u0n@?=FY4NJNqarVyTFh7 zW@b^?aMw49j#|^Ck+QL~GhbvJ-hf+DIwTI>8y6fN9G2^Ho(KR@{aVENS12@VE4Yl;1-l6nA%9g3y2NRuW=J zGJy}qXd+0yJ381vIp2A-#)q0YP3;k8@3WeXwRPsvyz1Q_bDx~zWj80E%vweQ%IJe$ zeXGRP9J=f{))L9`$t_-~u3$*M_m`YCw&v!~JxA}^4#ebD?ds(2x$Nw_-gmeV`luBc z4funHNOuBx=<2zeE~yIhI+9`$hUIId=KGbLc0K4=aZH0cw|!6^CH*x=x^K-QtsVr) ztn}puCJ1TBAr{OdHgJN8sdNPLQNd_VCl^6kyIkdzF(Ff>Hok$L=-7uK zkbR(7O@!6wn&Y6rhbw*t{O*KTNkli>5&61b_;>+;E|B%Bovrm7-Q=#4C**LszJ7eA(d)9Xva%Ao znPU7t_h#kmX{r-q+Mh6CIL9dVQz%Lx)QP`^f@|{I2thuWaIh7J9oAR@_4Y0g;l*1x z?x$HD)&T3_iZ4Nuu&t@+gK|_NfvB4Kw2mgIANAn@(IQKq1CUmQDy6tb^g|-pF|=< zub57l545BKH)*HNmjv|KEu#ETsmpRI@t=MyZaRAKq;JY?){cil5VwBC`#GcCmvQ)Z#Yvc3VY&WQK7g%2ywryPB?`q3SxsRS-{5D!U`b+UB z?`5(pyYKGd)_oRFns$-q)AQN*baz|ehM=v4p0TYcQ2enR{IRDC&l4|5#J=j#O{3;G zH|?@{b1)W%$z99{<(jJt_V+TqGU)$-Zf3S}b^Ul6C*N+mJ2h{OjAjO8W_dsTs!bCR z+U2$;2t;8}L1%Y&*{@bs{H+y1j3a<48u}UNQ&Lj)&V`SZ?VnRa z^#K6SCjdQx^+Ktebx&sp0Fmi(-&M%rB9KGLq7*E-GVX`iXU-AMK{ zS;nugox1cW^9?NedugSdZ}UDgzW<6MyMVy%jS#w@*zBwJ6gAwJ^HtBGhD2{8`OGBXOPX1va-lW$VrzM{9RRJ!{;wd_sS z?2dO@r2YB)+S**B>eZ^%-I~75TiY(%8SX5(4T^|YkBl0N&`v}8tF&VTc@2vb0cZxV zQxIg7UtD}VLoJn>7?LWjsL(wwJ{5ldEpR;JDsz*u&z}?Ck*({-F^Wa});dqXBj$%Bn4NC-uefIe? z(s{~@DE*QL3&wi$LE{QgzjF9R7GG+$v+VZ=-Q@_Jg$<8iYgB30tU9LFFyZ`s_8O%5 zSKb67DWq>g$$OF}&2%JWv03JeI!Adlcgc=wQs`*Y%Mh@Wn43AR<2+BmEG|DUPVnhp zdjYr;6B94pomgHj3|NB|c&M|pv$4Wun!EGSwYNW1c=;fH&&#rm`qreB!Ug{Wm>LU{9p{+zp zNXCTi^@lv3`zpI}>FU3$Or_YU86{4a$7PqDFE+6i#9+c;7&zK&TE!*^A&<7+F9#`< zM*`X!f9p@w=oM)Sr^dDW7dGGc|YU&48XO_ z&##Fn#4YlK1SrSyQrYg8{dNNdmFD=+yC*2K+90cF(YK-&ce{nq5&fK;L{+K{1MZx@ z*P`5U`E?PF-7>Q4o-aasqx(FS7)-nnr4d38lSIHbng^EUhf7LCUW7vPS#FglxVX3) zPc14dD>rVz-|NZ0liL||!dLtUB6K-TX@zo4f0tBJ__p*UJ&MV36xI*0uO_NGySG)tC#cBzO?;s@W|1DRZyCDKsq%Sx(_Ws#B;O%F86IodcL6C@xrJnLbSEFSk1Ln z4v6io+C=JvTqDC#hQNKz1&joeOgGh z@b7xRxpf9LcVy+&(7U*}XocVPiCU=~7|>PbSKz(*=6JFy`;Q&ppO%D-8zzMuzPSw+ zhD2&)s5UMyBYMp+GeG8uuUU~7oZ*Bx`tCb(7>46tI+IVN+aHSxb!;Xy&CU;>@N4qi z_5;D$?DuG|lA{sdczc_7mv%I;2(475HUCX>5csV02{J7ig8l+OnmsFN%A9e@&D!R@ zV`recL)ShZuH#5dNC*NgUmXL3kgbKvJPyb0GQTY`TR3mpL6X!R)vspr4S|%GZhiK3&i?^%TF(6jNiU=X2@9b<+vhRNBV>jC2`$hFIHb>xscI@=#3UZG+kK}S3QC9WFiP1 z+$x7Ndu_&<%qkpRC6i5geyXnaFg_qieckiPMPjc#=1M!FNEpt8_ep*U@rQykgtVo!)_*S*+v1|YDJF5(5OsZS1N zW)>DN#M4YnOzQPOBB&#s{1PC+XSt2SiTU)ny0El#s^{Yo<6rS}ynIibZdn&#SL5qu zjDDc97of6Kioidj*4%gT$b}`RAqKLhQ}37Yfj^?ZZFnRF^-b)6gkYSCGEDM9!1BDC zMv2@s9K=Dj$3FmoibO}Dv;iB{CPN1HSPLJAtB^WU9v+@cS-lja53I`uNza*ooo9H$ z+9%j|e1Fun3nLRTq^_VzE(d+{;<`t_ zvTAl-o<1+c_GxO$5}m&{JNx<)2m|CGKVOOp07FYl%V&LaS~5o;p7bl@HLT+JeB}FT zo}Ub8KL3nZ$e2tdv2&bBD@U7h>yXE4?+r5thum2KjKp=F81IsLUQE9BX$NhFVi~%& z&?y(x9CaRNK9c*!uE}|p;NC2Eddl&AAAyn8g#`uV-`w;^D8GXA)%XLG_8~EG zw^(V&c#A3I0zlhv8A`IiB&jw;LZdrR4ILk~g(*bb4DxG1-&9H#ZAgw251|*k6_rF$ z%K&(6x~;wa)mto3bYvqXfa6QI&~M+~^9Q{;!NogtHd`RLUDn=1bMxa;AH6;OY?eRI~W@>7xZ7<~ zf{cz^m_hR@3r{Ozr&3-QM}BLw6$e+XBitf|LXs>%N|RjXAAa$Il?zw?g$R8p-~)U? z3{uIey%Rvjw6`&_u{?hGlicPeCQo7chQt^XBsUSG0GEN_7zcugb!Dfgb|)rRr1#b6 z{T_}1UdYKxS9H0^UFjCfeEN;?^#a;FU0gTr_2udD_wM2gRHy5x%}pj&&<_h0+${Fl z!ubb22v*s!#Rgrli>mHX%e~ibmG%WTCL94Hkd&?Wrd>+)cvK@kF){0>7M9}W+O2jQa3HaaVseGY`YOu0zw-c<1gl`H!qSxOP2s-xDGSRg>j)H zsh+u#H9g$`$r>=vxA>f2qBnd%qShR1R0ni@`_!&#Isd)F>~)lT`(GdlMoM2S>zlg- zKv4p+`vB;G>8)F5MMXs~f+6ZIwscp30;e>ND!M34HA7>Sw*RHSzrT*IF1h|C3+MC; zF8l7qp*&J#Xk-*?b!iwlSz}QcMm%-r=3Va8^=!|h(2@s`uz06zV~~}!zld18ZxRA~;AMB4MsEq!z`7&P&a1zLH16S1}17AjY->NW(m={76# zRhu`}muBbWP;C+RM%kvm$Rkzzsaay6*~V(y*E4l@@tk+JJ+jDP+2wnyU2pV)uMc)> zd_J76hI8!Qnjx{NfMIYt+7vea3sGo-cfD}v1H!RgW6^iz33?W1@Yg+cu} zsu`3ZHH1-#iizD=-S3R0Ih{N$OO6kD0}KfX-(ktSF7hLXm$DgV0AJGv%4@}BDkP_rTP%tYsRrsO^m43oLr_6R zVaJqQliI|qjZ0M&wKTH)%ZR)-8Lo9+e}j3EJ{x{n1odMfV4AkrfQ+ur&MVZAQVd~l zULMGv_#E&~QDXycUS3Z7=MhDrV0F*LNs)+%NQvR>jnn5A^XO;2qAP`S8&uOSlsD+ZKnj(GIQ75o7bolqfHag=-ZzP$~FZ^(FKnG;q>> z_vM%aM4Jm*U0>ZQ8@+g8HAwpF8+0$?=@%3ipJA7sSkm0LhTFUKg^H$X7*rtWkvPbT zEjUO{;1CltO5Tj?%`4jBkxtTK#~0bdo5_iA=Yv5p=Pof4wBkH~%TKX;th>>bv6zP#bhIwBuu3#kK584rt5tnYUkK1_* z8HP+xgo1LNnt=ArkS(ISwVHkX^qn>?GfFsKARG50wZJJ`W)fJ&bZ{{XfmU}X)rrA4GpS^ zS0b>hpTWtGBo0}(xw>zKiYT7|hh@aSVtfFMr<2G(akhNByS&tgh@8WuB@t{KC zJ~{(@N80!fpa5=_JeQz;_D}_uPknu6_Vzrpt8V~3fQ*eKwj=aGzIZ|+R+688lGdT^ z%PVjm9Cm%6iYV6Osc^>YhQDnNzL<5J@bvhT*8z}x35WfI*2nnTH^y1A+H}MjOW=T= za6YpTm~aWtVQz=^IDV)cC|^OJCb)qAga$fbLn;`ba$K{DiLY5dh7vMP1k?j=(3;{Q z$@yuCgYGXBVcP9$dew&EfPI!EEQTOl2uT?uh>dy{*S|3g{RNN@HGT(U3lJSUgphbR$_Ei$LqgNB5nL6E&zgJwiZMTJBl4qz#G3*E_5H;#*0 z>cZ9O{EH+SE~5!IK+7lJk<2p;1^((R<+LsKczp`hpK+Be#Q1euA?z`9aL&?|wx~CG z%Rz2pup=sJ2GDkEVsk?^No)Ktm0M1>!1}#?L2#QQZH3~bC6rs^LE}LG30a{iy(|Mx zU1of8a;Rb6bPv30x%P2<{8?QEPr%7#4}d0uUrK*9knFU4{c5zcT7wf4;^WEp>09(F zt1_89m3e(#-ZasLwoTL|JLTm5Fa!S-`tv9M;N<9=XKr8U|jG__`JZ?&jTP6fBm zlHX%FLD-5DCQ5_oyHo^FIi7_&#Q$cwF3@GVs}Up~&Er9eexyapQ1`L~%Y@saPve zuB^JBJu7*-pF=;^w-Kg?`<3wSnT5jdkV+vpscv9K)Xn+_TQ-b^4JyyM-QY5I*Il-T z(rO|D-HrAo<`xZ!^zNLp6*GFbsn^w4{Fee-w5?#AorM0TYm=bG3mnMdlrn>DNrF3M zOgxE$H#av|P*r7D0Y;$>N}_w0Ts&w`ho)-`wex@X^V{P6N7tDLf>bkD*kM^9&MWZL zxq=Vvo=pk$dW%!RKf@H}l_NmO7d+xiTN?=HVCp>y80nKYdfvV}+}3AVqp(tvkT>A{ zjnnq|QG+M-lkIfMU3yWVr#h5$p&Z9}J4xC@5EO$Q9UVGwyF=+ost}cJfmWxbhW7DV zLeL#OW*7KNo^nbc-KA>7Sh$}Bij?Q&qYsgl8yb0U-3a7VSS6K?RLqX@?x z?d%$WlTv|LO*>91XD)SIjAB8k;OejDumk%dJ8FIMizKJG@t+E8aZ;{(ZcpO+s95PlxZEeI0Sv~tOa~ws#<2!mCz*xT7(fT97 z8XIkH9AG;ue2lp*d!h5G7G(CpP77qTIPrG0IcUKY<*T3i;GEBK)Kf|}e_qp&x&_q@ z+C+ekNK>pgp*MoZyS?7lqw_Tun@{>O_=T*X^BPli6OL|!wUn4XbNSl_iPaq7S2Q-F zV3P@g@K7il6s6{bg+wF6Mj=j0`OYgeKN+IRN(ju^QhC#jU%W?2AC z;J4}+xj4ET)0G&WKC5|MPF`~|7}eE`8fQNdrYB7i>Ml+n#Q23Ymr7)J!G>L>c^y(L`eJRSub@vx3ky4;JSJ&Tu?^SA}1~mmI?>esWokBZyKcMmMUBALdb6?8E zYUl%k`UYgKJrOjU2DuLvvPZ$(us9yB^uqYCK`@R);-=enm3ucnl$oHKVb+Obd=J#( zRmb)nEE%`qP zYWEp)`WW<=M4Gg&(;c7In>AfbReIxbFTyr6)=d;zqc_SqLfS?Lw}t$w!~k*ky1$Nl za{X@7d&W>2(2LUCl!lNt&S|`m<~tA~rk0j$fO3FJL;p4yxTdZuE*aPWk;NbI5@QDl zvS?kb?6VQ^*nc}z4|f4BLm}zh_Fy?ll<84r4zoIFvO8a>XUrFqHJXa^^HbZvWq!Br zOB{4vfi*P07O^PD;AcyPe7oNOGXR*OPS+?OB0*yk8(9>j-L&t|SQ-no?BF*20SSeiUoD%qzz*BAr*y zOFu0pFRy>+oHwq}Pq=uKr(23Sc4E^#eS@au9xBg}$E72c+ohwAWBwyBRpa77$-O4O zK(gcK>mTLU;`O!bUhT3|ZO{eq9t~=1Zed7MC95~cp-ewY%)!o1#}}yS=%xg@u@nHY zDAAuvdVwCscY8Kq$GMLwX$aVRV*si(PB=G=TR%OnH%B z&}}d>3g2(u7mmW?cLd+HFZziYkcABhp?#I~BLKh$u z;?#i_^jqmvYxbr~0HZi#BbxZ$S~!~r(j3~}_P#a}M|czN3N-`AQNOb4DWjJY(@0oq z0XibanDHU`vp{W_mv+hH3Q$wim(0ezaa|&Uq3bbkaGp&6xZb?ty!mp^C%hqpa8k~_ zWDM=}G1xX>ZjY`q78TqV6gv`pu8L-dVRs;>P3&%~WjMYAyOl(Eqgt>nA%wj01pg`o zoo$zZQ*mb}l^+;OorhHJQ0cD~H7Kf8<}yYAWDbleI)M7<1lDRF#vQsAQ+Ag0{%pnC zo7&+yxjc4&C%L<;_0M|H3AT;l&L!MGQnFrzKUvs$=8g7?<+zz@6eD1H*6c^@c%IyH z8*a_mj~&V}%jGv*R+4^Ha(86F$^au-5iZmR(nI%AvXV0;y1t`XG!%Fvu zd(rus?a>n>6Mr?Y2Q4@9`EK$k(-e85J@0RPCE6o~jQbHUpA;oiI%|Id9T(sw1!+(n ziA{+fSR3J0O%f*u9SWGGWgf=2n{S$$db!Qn;uE&^A zO(lFgARRKW_`0AJ&Rz$g81*CjX|yTx&cTkamTi3(BX`9Vss;cnbfv zmGQmXk|)}U?0&6XLmW@MFk4$8XfPc_Q>5?-jRFcaxE7ZOV?dOrtz`Hbo;1!ALdHpe zib18Xdu^ytp@0cm!XLMM37FUJBFUs)2RRu&kcD8$3<#~}29^>JlVVOO0n%P~+4EG& zBd@oy5b~QBKj{?`(Dy4}!*v5bQ5m-b+6qF1U{BUyBr0^+q3`K7xR0{MO2)Wyo?$-b z?G5-b5R~h@7dhNA&4_uE76ii}0`9J(#Z66+C*s5I1BdHp$*o2vw+*OuAt9*g8Yuh} z(oMdBRpwW{HjQ^@Nt?jUag3mKzEMS3`Q=!HP3x9Qj&|11wm1Z_qIX;|&qscP1M0HG zLXU6SA<#GB0Ns$O1fu?&Bpej?VaRyMJ&p?Me`Sjbg$pJCig8e~YO`RW?T)X}BdT#( zN{bdc&`g4l2VN2Lj~BTyuQ61MmQMFa0i&AUG2=c`#hZ`7`XqQV$AzwY$Ln#SiKcTy zP@RLh$3$CO5WI4aJTkU)p$+uxP&%g;zzQ9t=CRfDYcBx&Pz)@aH+x%Q+#2~Bqwakq z2%8bNJ_ldJdD`k+YS)98+!7GKC}SoO6m<@yN~mr_W9Ty7kDVGuu7;X(bfDy&+^?^Y z;J2?ZhudXPU$A_Emkp2I(I-wj9+1v<8A6_N>BNs>wK^}sfD6&mkbcC6+MZz?tg2+t zo~tF-pxo&kl4@d1)6JHjwgv2%0SEHl^iY3}LmAfv%v((5vb7NJ&XE$9r+YX}c%1`m z)uff|Gmwu*h(-yWhLG1{$%;PN$dchesMh5s&>s&35Ew#-vW z{hy^#dD9)5ex6Z%%;VEu0|&{^11%mj89MF~d@i#*i5!&;j2C4ACM7?hnkst&-~|E{ zk+dvRjZx=t^h^51IPU){zio%^o0owl1Y(S0GtYpOH=hV{8_jqtD0`{lvuWc9h3_-j z*014}SC)=<*-%|R___!e{PT8yKNGBrzE+e!!kDzbbT9w9Zw@3H^ZMNKJ&d;HwLoYY zk8X1?fflXx1w80=jyGz!vdBf9ph!9Fo0$;=kd8VCZaT8R$*FilD@&5osT$rAO zt1^`JOtK7&2uY|Chc_ByBM5K^eDObIyNTCvigiJ}MB2ib8BAFARD?>6Ai4b)&moa3@lrfbIDJ1n< z`(Apz-k$%q12Y3sktn1Xg1L4a6<1y-$0@MKW`T9l7mm)-EMlP=B z?6`N!zYMQ#dE6~6Do+AhLQy#4e%UyT|1SAe$sGk?TKCZe>;Fm&%zjB(E}RkO@4LHs z*5mu3Xe@!Gi?SjifrA!W)Tb3!KmKOzYDBp-1=&WP?MX>)%a{oi86;mDFw^3mM-W^z z0u?%F)ekC6gYxK#IqaW~w*O`v$qz&SJ@6Ctey7!~-m8$zY>sq#wDM~9q6hj&ZnQ9u1A7Vc$n`q_I0W~8$#+gOHU zp8+=f98CapL!HW%A@8hi?OwagcwHUP(QTfX)uz@<#8m8=_LsOqaa*2*tyfCBAms;X<7WdO1B<_UFd`KxnNRo&+C^UL@3 zefWuh@I=|*FBB1%xpk)s zJ;owHreLW#MMVxww1`c7<;f@I4=@x)6T;j{-lZ>wFSlDfJ_@U7e(3effUGg%MqgIldnr{8`=Bk;-?W5hjPg2fLKkMm z`BU=z|Gezu1Dl}itZkg1k8G5-Q6J2o5PtT4+~{{DbYz0LtADM$)2ChfV>JII6H4C( z1{(D>z-v8}nwD6QKyd9CUis;t_%-rK-UFu)9n+mYc`&I18MO_>Z&_0pEbUKM@c+1u z>mLkdCQfNtUwYISQS~b?%dP6Y14!y%TtY(Whs&|4zvt&&tgQ$0G^1gT=?F$PM!>KG z&Ra$8o$iiSOpn(F!utvnx|U|~cn{2Mz(X_x`J3GSxw5%AA^@bs9}&-YV*68czMOFV z-4njR4EgzKc^djaHEzsHnWjS!CZj6t=6|`jCThbZOyz9+<3EqSrRv25O=OKUc6D{V z_`N_tUl{TJZMUUasVDMCVHsasZ)dn?uo7r6Rv{1u^m0Ho)*oP|PW!?jjBGH1Gw{_TZ-lybQ(r70Y%A3|z3ZZj@b1)}u$f_nu9gPPlG9Qxf}YqUKMc z=Hl2v?F-htK#T3?#yiHn-s-{Y@>t^?htwmtImfe&`g#+{T65!sreQ z*m*}jWawcpBPy)@5*)8Te3K24okZ*zQI-DA*1UoO-tXQ$UGO|nH4yY*JVM?0dfCx` zgTBk3@CYYo=OAvdF}~^f@y)Vz69JF2vi`|@mvK0LUT9&g<-s7dZbM)ukMS0wnBWBV zs1!y!J%o5Rz_X%-^>&BUlj)Gup-bvYFXh>gyz}(Bdp;e2GoW?jkbGh5DFceJ@C7No zg#)dzG)w{^^;f)(A&hMt|tv13n2@Iwh4GYnzDAM9@imMfF8 zJm;v2Sv-zja27^;=5~%^R?|3h-=urj_<6(r!s6l#cng?c28%S8CQ^Jebn|Nbk>NTJ z=}PRfRXre$;6Ok4L5DEJXP2>N36SURL(=7z@&-#}JrQQX?3Dz-c&LH8qj>YWI=WxL z`OllZV1(W;46`;j8*Qxxdp}2Aj&I-w04!>Hu-q_|waLyHP_7G?Dw1b~BFVfRR{Ih` zr}CzMvK$A8Yk(1?Q!-L6?-bZ_w%4d+zsXEe{Uzbk{s;C4s|yD2Qxt+O*U;a&o;Mu#+#>l>7)6EUH~!>1Yx>5;e|XzD$1YykJ!IQUH` zDgt?-l&UdvCnRsSJE~qzhdQJ&zKPthLgxtv(4=wjgv}h9S+{KS#>l+7Kx4u~!v9b9 zFzP-jDw5WPf4{G3>?^g9>l!b(qw6+3^m>KriJK76n4VqD zwEAq_HbrK{&xq~f_ORD3OZ+D{8S^$H%Lf<{@6UkgTF$5NpHBYZSJ0<+%L>EO7c5e* zAO=^^k|Sv$G>SUcLJ66^@cCDklBT9y?2ZUiyOG3munRxDi!Vi9O3-Yi;05?ssMo%U z1yP+JirFq0a;*-LZX5=x4r4I%HLKGpj_(j#ps2AP!~}1KsK4DJXp)7~r@}*A0ccbB z>6k+K&HJ0)-;m5OvayRf6qgu%X7SUdgYBWSK0BMd zFH^`uGGoYzF87plb9JC5OMt6_8)ElMM&4;0eFHu*`OKY8 zTFtk1I5CFkL4B=ss` zyQkRzgxyKDk;!?!J&a7_z$><>;JhT)Zg!HXFGR>95Qzi83k(c=>zF?SL{vXxUGVFZFu?D#^JwiZFXymw zt6Ue?r9HgRj0l;)#9^++p)F5c^H`ulYa6?qxf98`uj{?JIA=n|&mE2qRp0MTNhMZr z3b3M*!;jk!iFH`n71eodOOU;6F`^XM1EX%xm3yf}snToA;OXs{m{kU_+pi9buQT7E zrEVs$i$hi)Vm(}9K9@I$U*rRqZDu{wZMN}4$k+kSi}nQ45pGBtdW(8KJbVM}+H?hP z+NZMN{o{DW)3TQDQ`R1hJGXfH*Rx&%k{gnj$!c>_S=eVe;Is2jrOv0(fK59m{x}P! z5UXdtWzBT&yZh^)h9H+T;x-BR-2S+_x7R{QNGKi9HboJhsd9ML@ZLNNMRPZoWqnTN zDM}gymcmmLUmw^d_`K&)pFewanczgv^Jf~>;a^u1kO||f5LD7MG0+ZuOg}m-_$C&I zw(m!LR&x&KUZX>e!|R>Z?p9h&MbeivT1dtJUVL5k@eI3J-SCBVQI9XqPv<3tHeTD2 zC{%XvkL(#8X0_lCE`=I()8Fg>U=m+bA1rVf9;wI+=YP1ke#nNs_R=th07tfGW@g?c z_>B0x$D;RT_z#;Epelp&bZUg!Zauz^ zuaOkPP9nH{=p@K&u*@hsekbz=V0{*BLLT%kp=N0H5sUA zqBfNt?<;cO^XJ)qwl0mGJ1v+155#W-W&J7(@KXFSXdQrcMGkNN@>z4xP~(QE5oZOl76R+pb3aZU|V!3o6P<#@uV{vVWv>@L`5-;KO=% zoN>UpayTOTjx?*8gXo;H1E);B z!KqUlvP)&FwTGDsjN<%!*YtUXGN9iLCyiBJy*}A-?~jnE===S3UGcwmrpZ5Boo#&j z!m112=Mx#WvAJKh-FvyV{Db_DA3r?6n5AxC*jkj*DSK+KqEXGO$R8G^i%kb?aJL?B@usT%XqR z@EC8IyT4i85LGT{axvU29v?5K8`=ACqPr-0f<3&UL{vn?`z0f42!PP~`|Gh1K?to^ z(DCt;8f`>GTC?x8+(bA1*O>e@{$S?Tzx4L7%%*Ckv!Kqrv&Fp+P> zi+jjxy7Bz;>_0gY&FgN@Jp268929WFjI@`dto_Zv_SNbs6MD2KvFxKO>cGr(lw?9^ zn(2Xt5_&Ww33XZOiYZ$xiyr{&13&HKM-`3N4=Xn|M&Z<%CdY${#Z z>hW!)zR+_bktE}#!?sypb7S*26?4l=)8aL&RF3{!5aI~TefI1DtVlF${#6EvF)<k13;}tOGQT*%p+LcVTTvt7NFKl$~ zdm+4M&z@Y9%~6~xb))w#J10O^v54T1PUn%%Rn4t5b&IPyl~Y7 z$b_r~QW#&7!oKF4o9o{ol0qj*&z$4Qu1jN93ve#Tu+|OZ%r*CXruTK1UQ~ss;8-5i zQ{-N)1+@;DA0n)ak5ASu##M8=9Lt}wy4tU!FwKtDQ2FYWp9LQ|BjXgDyFrwNV8%A9 z?Q__hmBe%bBH2bT1yg^5zLhZ(I^}jJT1dzy;G5?}Zz(_Mf3k44@3Q8LU(yPbVO^7o7HiwD95VJk{Fp{Y8n?yK&M>qL>C*mbb~1Om?U|4X$arv z8eqw6`TYHMAfRs)=j7>m^z!Rn=(xIuYE9*GSej&y*nbswmL>KQE|q^Uc)Y^s$h&Fy zbOzNac)o;A<^G@#nci@z$pAt@9us|BRozPTM!Yb*820+zTcu(u^5D)J&pjuqta6*y zs6n1)uHC&K7Sumwv0rwiYIw3+?B8r!be)HY&cTSFGB|;NT+i129mN_jGCKppj>Vg7 z&4yXr)l6)}ykF8HpQV=M8)<9na2{OE8usw)VWoC)j`~-?Cf)Y;t_&|(Suy?E)N(!8 z!9K8@DsGAn$KaHS@CqDs>TL4>sybqt19F(!0&h*_1wcdOv1VU7>B8`7KyM%51VLV` zT@S#1as%So%mSkeCia__){nBabGXKYlumSAzpG-iOBj^4Xh|QoymCyJ*(H$yNv+<5X(!+~p66sX<_+{e7kFdWuSjJTGJ`!OU!WGEe_Oi<+9+ zbr^X*`q&}_5JBTx1*gi$G(i)CnHraYwp8T+R<(B$pM6z`U`;v^j zAyCGPYN&!9l@K z(~D5L+86V5nH-7U$@i#lBF2IaktMN5&s+Nl0q{M;EHJk37daP>IkaVGQaTcslxiw~ znw(?@(jdB=++IKWZE4fkpKn%MCtN+Gdl}c{osjjbxIpWF(;gHoYeRJYN9(gd3XsQ` z{QX1*XQBI}?;=jr>bqb6*(_x3(w{0B}(Otbs7eYzd50G!~ z^p)>e)n9u{Gm|IdGBScnOLq)|cm;MNL3NOKC9KFFTD|mbSd#Aw8+V8-cd)oqb6!AN zd`HBsb6V~M)K+n9rM-fpGO~Z<5$nt*bY&Yj*vOp& z4{H4d&X#^Rh6r8_E@ti(u-)^kS`#d+w9EV1Ryj###la^jwL8hO%b0cGtX+>-;9L8Y zTX^m9@o@*)%k2}hYv_?0P9XvC-qAMLGhX`MK<>!Wo^2e`Zrh$l#BXYKx*W1C0VA=E zqgx@C`?0H@?Y-IQlvX>*4bJ@r(1X(sz=5vwC)GjOdUAx=xi;`-xQKI1`y|2bq_GR? zPBcKK^`GDVjR%oAsOwt$WW6IM@8IDDwp^vnir5mHO&}^3UB8QCiLD;t7SwKZLLb&V zI`Q(W0aPP7-%HxVg8Qr7pmqx$1+dykF<>?Qe+k4lq>LP!r z@>*o6pH)fXzLQHJeuKdR^Hg4D(YGGr%sKBtD!T8kjD3OX;+Heuk6v`-SC-s-ZaiwN>u*F^>!!1;nvw zY;GLh&w^ZxVSfT&VJE?URSU8sad4!gZJP~|wXO@1)rD+RAMvz~h{=S}3AO0_UR+!p zCU{DjvdOr71fTEFfh6h%rhYrXrHHu*qBvDgVm|&GR^ww9NNKtg^^q#0=XuvW{szrtr{=m!^mfO|NJo=dVH zJDWsBc=qDE>B>2Ghd& zoy`_xmJ`*_1lVa?l&vdce!HEpFdBHA+3~*eC_rKCWYZ5c0ygxHwwf!DUG|kCKJn`VHbp#-R4wN>{`L| z;kk>6FtY5)dQCD)i9H@yNPmDI;=bTBQvAERO+Q3B zv>L3*;6%lqWAHTle)0EJ?#hF(vpM^9bJt2TgY8+A(}S~1j0^qah{V;>o97_9=bFqV z?o)66@PLQfB4b~w)Bl^)cjch}ut7@j)K`zfs}PP1C_WC0ycp1$5MMs8R9U%K)?0)G z%eq!dX~&hOt`$N#3D~wg!q)Vk6BZKMOfM9A>sISctCW5nW-}1x8B4$rbRBQj6^5JX zB!}a7d*R}OxUwp}{soleBzVFX@i<~nMbWv6n7`jLlFPqlHLN$NY(1@y&Mj`ityUv6D~|d4IHMf zg|K4#ptnMj)E)iS5_`%k+Bh>+2T>kkJHA7+MWn1Xi>n0nK4||u zCXlaI!xn#p?UFvBY++%+aql3m)L=YSNdRoher$*ECj~`W2yW_-&Cbz0hcdh8$-dDe zuzs8*qiWe_pbtMS3G>=K*sl>;s>}}|mB9S91_F6%sADHbUWC71q2IQVZx>bumV5nK zoV8YnO_vC{eW@J+j>-|XyN@$;FQ?bn6$-&rQmTl}hvn-1$@Ax#95z7c0c80H$2e-m zE-<9~bkNPL)ric0aG-ilmREoS_SW3BIr}Zr4Yzf5kYq+qoZzOXb4_4>HsOg9(gt3a zFQ3qI$ZFiV;I!5r9zN#=7|SUofrqEoHEVGG8|fe}L{52SMSclJv<>d^uitP~C_X#=ul zco7jip-)rX(_`7tpsH;5`$^>y#=_q zbC8U&bqAi;WtHl=B9fpQ=UA9e@yuea+{?o!1U53NZu^KM?0QIm@CctwxEk5rOg8gw z1-k(V3m4gCY$ai+zeErVwD&Fu^ehgo1I9k-(L`4$SM-@thkTno@kZSUvfQ3RAv-_I zJl+~n_6{|a`|Lcy)NNkseeQy*s*?7RBei{Jzzg&N#Lryq!Wn~(|6kVbbwk6gZDF)} zp#=^_nk|RU?pMf2N|g78l_6dSW&8#-uAfzgs8eo|OpwscD^g0PM=E#AvxfQ=%==4H zNB3{ruK*z}u4?bA5b1y5ku49goDk@3b@CbFL3oaZbOu7PmJkxqij7KjKMHv1>JG}uzYu|{G?f_ zw=xt}U3uRD_DO>31Tk7mi4B3=;bXV{Z)Ni3xY&}Lf0|wR`38G8I-utwPPzSjji~)P zgA^c%;B!MNQTYM}oAS1y4kwPMM(vQWcUulO=WP_`2ILpiXbe*YWf&-gtRsa80rKbV zi>*1S5DS^?=zj~`hUc(J{m9-}h|foXDNMkdIuDyVK9$5vr|r$97y?#e(B?>d*qkL> z{{@liBk3MH)uqZpaQ+-2JAJ^9`^2UBRFNFmV-gc=>omOSb^F7#K};=YZS&{vx^jfI ze^FUP12d81hb#zHe=s3759Y%inU@6l!@(?Q#nd&tCcqaFWVzcklaQ*LSFDr=mJ%9L zG}JF9ljD5x;zd{BTENh~Um5GZl4?5zA+vb=%a@M?Qx;2^z)_}uSzBCb*eDoc*48zC z_Xvha=z&DxD$C%WAkL?pQdFD`R-$%Vg?!ldJj#j1DF6b(HHwa!hK8Al+N$1~0)R5! z(2dKA4i>7R#gPA2tx(Xzg#Fw$gTAS$+)c3k5bHkJU*>vPM$cCuW`>n!msc?weIcjAHz=D3bdcVK8QOseRa~+Pv z>U`>9!C-_Rwy!Q?@mcD6;NYzUnFQH(jj!768$>S}oP z^KkUH(N;S=s@|jol>$(#FWhW@d|EQW+V1Om$C!A=KhG6l#=(jGv?|%d_5#XTYgVC; z@!$J_$w`;By@e*5XKseHJ3oV$6;1=Cn`1=Rf&Dn$S_^_kAT16qoovE33S2x3ex|sp z?TYYZz+zg~;zD|*RG0sqXsB^X*(b-Dq8c`HqBeLHWXoSqw?d+yd4A+xjVTH`K6DB?;Sik86SOaK%-HostKz%!^y5V-k?yjgN#Y-4oD^Qv+ih(M$a90cc<$mtHCv zc;;MM0hH|58DS0%R~J5~!G_@yi$5n$>lmi(8|{bJjo)Ei_#GUKP$5(T9S*S|$nA6+V6?UJ!fy3gQ3B-tea8TjyuN zkLkZt31ub2$L%eU=!{{DyC4tS?$ss8L$RW*tuAeRrv%qP90^V6V#J zvfCt_wjK{X4HEmTgIV)>vKOb7u{Z3tAW>PEWGafXjHvJ0WRA2#Sm)ugPbn|o{UO9D z{`~WC7vO4LoN?bm)?F%Co$>#Gb65N^A$x&~i?2^YrVytYmr4hqu8BeOEp$Pinj$o} z-&nXx>~`J@rEm18;T}$=+|K0JMqY!zf4>mPGXz9(4IaER^@8h>`2%{svYMM=@GJ-> zZvX3-T-|#mX{44LNIPWU691<-0KY6K{s!LL`7OV-E|Laz+z<`;1)3zX4IVhX9UOC> zKyd^n2M09?evUxb*{|lMw${L?pOS~$5D&<@iNyeclE3-t|4H6Aa95lPxr%|J7obCS zIIH^OTcwuVyL4*fC-W70e zo^4;@ykD3qP=amLq`e>CB$+QPDk@{8WfuRRuE9j8%@DPaQtFL;{c1sh#;T|^K_3=V zjPyJcjZ^J&vSu7?sENnnObmRyELAV>r4l`m1%Y54me#TcaWma|A3}0if62m9MPI&t z&5$q!ZZWxK?4Kh?Qe*s9YN%2-hgdH1)!5L2^#NM2jx5JH8TIeSK1|lEc-}?FwdzEr z>KllL^03yySOOho9x?-_as9K1;eZqPo}KM$^J$|7qOe*hD2_!`0;sLoHP`Tu>~cF~ z1Jv8X>Gf3h^v0Ljjm&^~s60*w@!7!cGlc8cnYSK-Sb8nDir(wZN-op@z_IllFhbXe zxj1ou{8%H0PDjgbg!k6t;PG1}0UZM>uN7hge!i$s4crZ1f0=oQ=ERmk1yErE)2C3vud2s5aEwsWI>&bk}A9x$N&qH^!Faz{`S$+CP6k9DPj4hEZ3Q zf2EL-iw*2QeQCCAB7Q}%e79WOuBT%_rQGNe|Lr{S??8h+*T7HhK z#DBmJvG@$yyg_`7bi_-*{bcB#0jEm6+c;V~SygFe=q+uMJhL4*e}g>_T5bT(d#%;h z)@+&cUrw(sZ6P;-=80Ynk%MQVZm8dPElS@_U5a-CFXgkQPl_H9cbk4DXq1rHw|4J~F2yjNKMC!qH-Dy$8yrUi6QTQ~3oZ12## zBe6%wI{wi=e2k7~V!@=M+{?^+E$mq)`+IX7C)vY+*Gr-cOY;dNC!k^(=R|>sb@lK_ zS)Zl=t zvqMaD*a`x@t_K2LJO1_Srj3{ynLOkV=a1#ZX5{dW%>vWap9Kr8tJkJ>2cdP1&1AWv zuK@d8ABOFd3QxXtO7Kn8A)+wk1(lk7ucO=1?R7gZ8)8em$R&G`nMF|x)xi5n+RuB8 z4?E^c^YuS4lGlO_t!aZ}9(5kH7~-AnheET=@NTTDDRyQY-tfiJC;GZZ6|dvKpc+x` zG_vy$B8!0G3l6~S5wt4CQPAv$OE(v$(r0i8u{icEHD46c{C@d*CIacFQyp&85H3mY z5S*QzJ#sBS2P$LKP&OPmWMyl13ksO=^Ur4#!u%+Z7V%ZK(_X=pN+i%sWMPpSLJVTC zEM2$szGEZ3WSj#dV>+!#dQiti5aWb0_q57gY#?;Nih!$3=eWV!MsFcY9)i&b=qvxv zJWUvkww@zXK?Vf)LBQc0y|DyRF%B0b+;#6A{(|&o2jzefH(?s z0%|_TmT#bbu2ip+aQg*!7dGhgDSMX>h-`MRYF^O|1tWs zd@*Dj0KNvHc34b?@>ws55rnp>>P<>N!{;4f*ymWCH(&@Z?fu4ZM#_&M~;W4h%^W-DT@(=Cc{9=a|bp|y@t(Y8+3rS-mO0z z5)Bg$L4blvcnMBJIVPLUMhq|&wrY-zx(+UumvA`f1cECH^9SuK7jvDhnlOhIW{&tk z9}ERN;brjJRL|B@$A#2NfeCYLwK^nA8LJ!p*75%RA!TD71ITxi#bzT!jKH^g{$B(R z=aL6u1RDeaFP576TXC`o`7QQzmE{=ZTQB}lXnc_k9JbtPyNd3trK6a3+|lEJ2p->C zdT|zV)81i|8$7K&V;(#}fO$Qf>1}d;w&n(=+ag9d3}TiYG=fI6(`xgnhW&ULfZ8O_ zdaE3hL%$hDhOnW1$YKSVDKd~+Fr$o@sqgJge3Prt<_i{$jwe86gFFaZ7Nw>akC?{WStlMk;Gf##n;`!`4OGFKN%&diA^sTaWk zRxqL>o6XwN#f9>ABegBKX9k}69R>+2tUlb88-3h2kqL2c(ul6_^%B8F9kJXU7s|HO z0HUa?x=m4Az5}SF%FKB4YBSh!Dgh$?UqfwXJ&>sq3!p%g#yx(p7C7sJCrr(GbG zb`sVPXbuRT0%2^-S_LMyn}O7BZ7qx4GVI_s+?qNP<$iAQ=Q&0u1QU=j;Zqkwl;nZS3@z zv`_~E#w1tft+5-~>(83df#)W8GznRVWv^+8TLpyD4K(RS8(APU>|s*_YB-~|2?p%$;y<`|w#xM8*o=BefKcD@6?V;XQHNSx2jccd23Xr^@pk`tuptR^Tgfh5rd zsb$WkVft#4t?eetjh9G^U5_SxmKSVvQ9Eqj5Dn=8O>SNa&O-)Vd5msTrL@8bOKf;< z&IHd`0g=C77Q=vU>Kf7ZGO7k86V|I{2Mx!tt*vQ}+nOy*(6virL5;vM-|zX+x9=0i zQomAa-&7Cgvbj57eFBPTbc>}|XE`Ag+)1Z?#}70H7Z*T*aOyl$OULJf|5*mWT9MxX zM9e5)$B;#2zBmc?P;wnQ6Jmv{`hC~~cdr@gR!avB#{=(gcqH1Js&_c}U`#@cetFQL ztk?E`Fw<;#Sg`MNM0AGGZ2ZyfX|$kJ?`4)g0_s;b-A;E$nvN2TOX4xHOmO}`_e!^4 zkN#H7LGQ!QA7&i?x{8iYPqDW!VEFtU?G@LF@}We3Ng|tW$Xi)%B6oy2UrM++Z(6Ii zvBW=e#a04_#3x~Khc|o)U}tQ)$!J1)%C#pWWS3y$-d*TIa_qD9M>jX*U7jC{jY^&E zyD)L@&m~cLFApn*4@EwI;xA6jH+DXvxyzkSws|oKDNQwgM$?=9x2YW^^LXl0#fPqT zF|xVhSAu84cPOT&&oU!jMG()`_)x?6kie^g>0O}%i*xcS`GKHGIVtz*x^)D{+jc>t z>}MHBedHwBGZ(_w^xJe7y6#Tt40>hB1+y705DZ2uc?P=+MG^(0?i0Yz@uf~$fBJ6H zmRVf;_YGa7B!~ZYc&d7@QF|4VY5=EQJ-ug&J&7q})~)3DMR=mQh@mjlz)R@AG#y}U zUml!=Hi|Q=J~EbhYb&!7Vks)Sur&b2+2CB`GE>$@cHIsqm0UB~N-$fv$ZlUnr7IUO zNbKuSJoE;>R0cnxE#$USXBYe7@#KyQ&cVg5a`4WWD0Q2W2&C*@z=G~6i-cOs^IvDn zmU_derVgJ@TYaXW4CWJ&7@!zeva(&(xwj10xR*VPWj2&&hyzyrC^$hl^u|eLwH_j? z`3n*bP&&QTUaUQ_#*ysUJ~-aB8y4F7TEeeuxYH_eD;;{7A|J!eGfWpBFR1EVVVk$g zHK=6>B2|tYna8f`JNWcgfe=Sddd9!w>b>WZ!{9K4&)<*1+14Zr!-<2^P+jx>T~D-H z_3GO6-r6bW@pa%?cZMQR13#f(KA`C7u(mNNGP<$*uIkyKZI$|xtF;&r>5apsz0v~e zt>rsbr}8hqIhF2^#zj?@H^1hZ# zkQ?DZuUpa0c^}zno(G)}cv|%pnRQ)k4f*U?qjKQP|5A%!NDXiihr2b6XqrLfqhDaT z*WZ+B6<`PydS1j=?8GKd-TICham>Mzr6P`h8N7o+)~Rf(}iUm*CM*$SAkwxN3O zb-#>K?uj-Lh)QhX$LaFj+gK2ryYy~Fb5cvVzo6>fkdKF=egSa(bV`#*RgSyA(O+Bh z4C7I_*BF4UP57d%?ptvtny?DKu-3Qw1^M#a_3CsHhFibxR&d`>I;MV_fpi_F$PCGA z66$4ucDyO7q4e8ZH&Be zE>(Gl70gN~(Dq=xYvtkECU6MdNNYM}ya9nv&53-bgaa|JLY(}CUrw%*o?mKzMz&#j0W_zl1GQE+(+lA!N`k2F2%F^InZS)>UqxyRG&y=K4=T}udI-;3)CD=K+si&6g9o3d3Zmha!pxy~V)k_1 z)8&^M5%kY{UNYq@3m~yq!YTd0hQ{CjN^|q^PhmDrr@!#~| ze5_H)1W0uz1X53jBAmry3n1}!d}#Gm-t^|i0luk76I0+%3O2bn)a@WR4t-fg5^zY< zV+GGIF7MaT6%4JWS}{~*x9_&W82py&Yj9-lUaof`UW$3*t|LwXLEe{<<@=S;*^2nK zmHQZ3w|#KS3J}+gu#0s0Za-LvAv_~SSMHmUzAnj1X4h6G=qwX=tUQs-Q_uN1e3Z%m z75Rs}0qMCU&9=&(JM+L9=2xli`2Ba({a#Vjp@J1YH0AY=z27Gv@JIFD{7C>A*XwpD zGBc)!GvB=mlSjpa&~r5Qw;BqD;K*6eDP4&QsKJp*p0)-|+rSveyK285 zty|!Kd~F){vjPSdB9O8nQ(NjKUdQ5Uc;{7qpB}l~A0mU|chDJ#FDd@(*23@zAU3>U zBNFj^hp@__?#=zoEDnIo_wGVUL#jyQZ>zj`=~uZqhiSti_9r4sJcknBlggH)yfY8b~s=HL=0A2iOc& z+l8Ule@;@t68ZUb>WxaPlL1mxOz}|@bbdSRW9~2x^Kw^!ab*D;W7|%3$bfAIp^Jrr?Sp4zu+ZMbE4d#c9UEF}lc-HcWLIiU z-J)HDdeT0i)EXcj$$y3xmviGO33&bED5R|lU!aWf^@&&N-$blE0jM?wglEw4Y+%(o z#*As*62S%6YHVg13LERYdB~Ct>j~+vyfkh>P|SAFHk2hkgGO2ln_-2E8V@j=AJyxH zq_iMbLbfjhxh+i~Kr+5LizgXhUY;j|DpuC9)2Z=k^bSt?ZCLL1?W*{?J*rsL+ zF%$rpTtyx%A!3@DArY>z80PMvhxtb$NNgu3HM22yt6WhJu1`&j4F z)_}&JYYh@~r|h%j3N&aw~v+<#J50&sB$d#9I}AbIz3C~;T94j#b5si_zm zyTaE^mh;KgS0T&L7)0|r5dhJ~O4!5|k}XS4*J=e$fX(nPU<7Xc{RV@ixR!ToYvM!1oZJ~ zovETH_ocjVGxc zI>gjP_C|V;Com=g{9p&CH0&CA6kAR=6N~lX2rrD;uI^A=T4M;nSEturH9w2zB4;ZD zb8~Cpz=NxG=8A!gAM-RK>+<#iTni|Qg4v~tRhHFtqwab-r``5})yDwCo&2^Yqd87- zuz3H277U66D0n$2&p$;<+vIf?`eXf!b{DzxMz3#Ophp`qqSz%8l4L+vS|I8<=v{Iq zoB-z%#FpF2u4uArkR*H*hsFBwo(2Oj;d`t3-*6m%l(ro%$nRX|Ft!tY_&$|}`6xFa zE0V(?lq4~Dk9(J_frz!99_1WT2&^~bB{#3h@_3Ige12$hLaYK!Sn&jd{js7Yh z@)y2_76&cm(|2+_eL!{YyHj3Hjr&(Qo+pr6EM3Hj>-@b;r2KljXgP%f(n_g2rTBpH zql{f7RMX(&xa8^?n#US3yq0$eCyVdiTW&{V#1#jBfG_OjB`{wBAn4d--CO`$2mHbN z0yIQIStOc{w-07RnZ?VGNC{CzP9sl|@30d$pz;_~W`DnPscS+!2-||sm7e{D0lBSuwsW4EwjXgY?Z;q(vPyz9*`dtR{*^)Zl z;{fc=-+dMG6e{paF0hzg;>^0Py#}06+%K0WVo0R-hEZ zsP%@ML^)gr0qiP-C)hPo5IaaW6RxO>e*UxDyvqmz%X^J#hto=K2S6geA4={RDG{^q zE9~Rz$XugQw5>qZz(ZpyFmgP08TCKKnGh=az5IR_Kb=c#PlB(nQI~Bx+8{;v7ztrv zheIWB6wmA3A%v2y*>Xfd&!QrCJy0OH7A-Oh`cEwijXj+H)IGn(E2pwxltR1x;M8?_ zy3%j9K3q_iYyn!jG(z2Zq!{@Q-UO9QD#%Dcap20(aZ=#^p(`Z-JAVQ*ZnE!~J5A+U z^!&8PFIyC6d`R{4s*5EwjpLG<%}eQ88Osb6EJD!Lb2BH0#v9T#@j(Hq1rG5QI?l#W zhLGJFf~i3CX7wPvXP6)8NB6e^^%s<`Sb*Oligqd77v)#A$qj_;yXw}zh&O8=;Xt{fCnWYS}YAdZ=ucV6N4P|Nx)R^m|@h4CeFlBxQ#t&FDqtWs&N!Av&!XKLN_au$jb$1 zCk=rr0XS@GP8PUNzvBrl*ufiw>AOZ=#M0Bx1mx+W^9i(+*Th*n+^pLi)D4)l0-=hww^HpSK`nbX_8hzOYgiy5H6UQ{zi^UUiw$L=V!WY@m$|x%r>mC2ap)liC z>DNXBp}6w+Sq`)9IC$R{3Gw5iE1fiS8t^VTwH5ZGJ3@(FK|kY%_XBUgj%KaH1lq?K zz`EKX_)Wv0?s&of!PEb00)XIBZT=AJBK5JuHIA8z;W@ul21M3%I|*7iLYMOyP=;~} z&Zmn{EFNb|RjY`EGID~fIv~*wC+EL&9QH|CA4f3=s#>ZC{;g<}ZDXh?iuUwOQ#o98 z+b8Ph#4d5mtYAgNXa&B8cz(-v^-KoK80orH2E539Ey9_bI^t9Gh$O6%aA1pQw)*k( z;HrmF^7k4m!3XdKCBYJyHO$kK{sfJ+7ARwhdZE?gyI8)I)K2*Zv%vRihAN1nYX7-6 z{AJF9l4c4nEa4qEYg#0AX@^7!7ct$O99C(yYMJQ+@hqjchmhXDKaUQEEVHt2P92CZ z+6rd9jc25}%`ty^y$rF@K7s@4$&E`Ig~?f<&;piuM0r?GA@;B+bC)}n`dzNS&2e5^ zdhje-O|b&-$|~j}*O40Z{EevudcL0)Ly?d~HDrXI4KXxMC0c$m+NfKV=3xIqSNPTq zb}%9z1ka{s2%ao{3Fjm%f}uPjKy_i6GoRouwAS({mike4D%xrb#TDgDsNHrDL&D!O za_eL?H=z0dgI$YYG8ki%k2mq%= zYkt1>+I~qu^td-1r!Z4L4FB9?%i;Ql0Fb~J%iv)D=jkv7d2Du z>0ov85(uu=>)EQuFW5S$(AM+fpTSb_WR+yf`% zZ>RqvV0RmRiGwrn{9jRu-v$A<@_YIwGt?cykRJOrl4J!Q82u-5wr(-F*#(6b`E2W# zCAyZp8l&^{n!8h4B|;O&+@LK$r{LKj1PP==goY6D);oNmG=SH)qEnPZgg(L0DVY1e zv^lVAHMB}S9&G9{WOQ0FN>-%Ze0c7~FpTubX>kBnd!Guq3eY(SWJj7Q;S5xjRR15K z349qSEXQG5{Wn6TGjaWw11c-7z)}%muP-I_xGMKvhsvN5Y(}p}Xq&qO$^1hdH_1F1 zS|`#F9##DX9m<=aOzYU<=B4ZEZ1!KUL%HZ2r2v2%(!s|yaC3|gmu?>7H%Zw30a_T} zz3>Uwl&vLv$4VaZ@;O3D=lym8kTAeLp9j2vA+;83QpQBxOvk0}5WOjSJ-9QY zjdOxJ6vqbGd0_fE*WxiPV4sg435>Er$9Hrq8U{)J%dsuhd9;dqSrF|wY?ZB^x-&^g zI8O^(;b5Mmx@^U2rjH8Py17FNaV>$3aK@kD^!L^aOLQ05bWkzXrkC0d9|bTD@%{v< z2h(c6lJZ2UpC9Q0MQx`Uu~akmlx2UmN*FW)mGq++pPdZ zZaU_E&4ABSKkDzXJ6-SJuVy~>-bv-F7^uGYZw?}kV*6;?P0K;Ift4;0b04pR_x(tN zE&F4*0n@2r!=v14fsP|kXyOyG|EFxn#Hkf70h;zeMPdmRiAOlqI56}D{;w8FzT;8q zo+G{Zk?kw=j|1fiJOk{K8OK#yj_*zA+FxF-W}zBB+YN#5ENp*0+!eF$xXxL=YmZlb z1RbKVhFzqG`$1ppZ8EN%@cl$rS|%?u(=ss3@IR&5zkcCefsnZk083)1S%@lb6ahpe ztqv>l9O;K=j7g@Eh;T=u=9lqt(|yNUe&xV?E42z(f(p=PlljLg0h&VZ&K;tV zT3X&zP7@(vI&Ur@2`BS11?EOGA+lb)IbG8^49{0Plf1oFL8K?YtQvHMSr3{0r{hH1 zPASB;iqNtB1rFPwq)=-*#*y^51lyq~k^Y?-6~BR?Wc?F(FAw4av!IFNP++Msy19~1 zFD>D4I?$d*ZPx#n@1n*#xHTe)6bhWJI5=pn z2Zs4T%zdivH5i1Pj<|DI^InK=P^-K`D*sP}145z@AHw#xQT7P%AF`}2T`S7Z4ZD4? z*?qdM9`4krN<}T#xwD}JB4C(A5>bQAo=VB50VOf4J;+`ARZ4-0<);=IN=%itz4k4O=;~OA9xSBYjrJ>s#Hxl@l(xRM&wg~6W zJ;N_N)P$UOT&f+O?st#sflpBWw0 zG=U`PMS{LuQj&5#)ebFZ9fpX~4L3iB(<|yE8)A-2{}2#sB7w~EBEQ`^PH6b-jJ^z{ z@OC~x)Bp^Bw*2w&dijm42|}7Y?Z_wfiVK{E96A%2#E}0R5o4g}Ksm{{s|f&o9)7f2 zVb7oBIUS8vp{C%og`9GE&24XW4&luL6#}lhQY1a_q9nD{CS9^`h;e&)$Df~*(>DIV zgCkkfHVX4~mv{b}ec?9z=IpQ8qZo|U`fI9QOm>QjRlYEKNR~^t}9+6uix<=Gq`j__zshS6E0Q==x`x!+^_-ok!t?aut2o{Nm*0 zSpdn%gMS`h?(KVd;lej9QB*AR@-v6##D_vi2B}ruccwK|NiXb~jw|1bYJRa)yd;dL_L0L+RjIENTvJ^9xY$FdUNu?)Cwib*ndx#hj^++mP z#x}A)DY8U}ky7?4vSp1yS+nnc=dI84{q<*FFYfn!-{+j`T<5yaaZ<;5GP!gNV8WMc z2Oci?l#P(MT^M!I2AkZGr=Qps+O9RTDzFmYv@?rYk9(%6{oN*Dgy0O~Omv;7?^ zz^kbLmGh;&gn-r1h-P>jMcg4Yp4kDx(78|!CC>GK>2!y6=Z?8riDcC!kJkhK3R4Xs ziTW)~kYk{&PeA|Sy}@*8c-p0=oTpY}WRYb(#>-;4){M$O`mFk>9FNipe6hFOr<*h5 z8GEy94#+tX=1qA{T(7T^4-Gw`pz$-Af{o8tv**R!svj`j5U-3eEN&UP)Yv@ir^KDO zQ{)xHIV6Lb?(|=qS1GM;L;WJ+h(tVIP0Aq{y2% zLOe={j11w5$StK9zqVly|$252{zUn~U$@z72$zgrS`jip9T_82A+Rr*$Y7k|t! z>YuZpj~fA5ioC4#`&ifE2L9hnZ~nC#btyUEs=#NZNND^q=$ER{-HUb0r)Xj(FUE%I~FeLP#>UdO?@=5tM z@c9w@hqGil-gNKU|_Rv!2Fh;hh4tWEzb0ZN)JqJQw`jkzg{?toliT z>gojqYx7WBW4{M-E(X_CW%>r(z#QRp}k%niyezNUi?k=RGp=89vL}6!R zV0hT_`u~OE?F`CZZ8z*^t}8u}Q@K3D<1L4r6(nCu= z?LxIfY5%BD zZTO zemI7wkZ{!TeS4<_F~L!+d2%-uuZ{}Y4u zUez|sM4t9L&NfN;eyA!>|BzsXK=%6T{YjM*Yc=~pr0)ZBq-g65je(PhF?a#PIS(pVh`e5Yd?pRsNN6-+Rv8jl;by1=0=AZ$!C~9{1?WwuIrs&hJx226b}4+={TO04ErIp zw#T`rbpGf1!}a~h`7@DM1X@p>UT*q(_;sjzrw#3)*)5eBFG!**1ZaG=)5;Qu-N(zl znhe(d?LFo`zP7Z1UGf9({dlTHxt$5QRTX!?#5NsyG?M(Ind>IYn`;`AHOV!70&gHH ztIc_h8*QkKFRq z_HX69*LK@#`C@(g81=C*hbjz5C%u(W zB6!Swu!gHh=U%o!=I)|jNOEp%dO=2?55fF(r=$AIk>+x`Ii6+<9{xStI&L>2LVi>> zz2_(kLRT~TLC^wBP0f^RZ%){}x3mj#ed3-OZBNn|P*c}{tn_j0QmXx zKa6ny{hV24vzDoHicDQVtPf9O^3pe}Y@5fav)2$`o4WG_+mZ2q`d(i|K7rT!i$`)Y z+FKS#09JtGx24=S8rCsqfKw#$*+Ig)?DjU+91^Ibp;s~MG2!)g_AIkW^lq?Ab+wvP#VFMwE3Ahb(O2VtgRxoN3Y(hJPQuW?`mWHhGZW5b-}8 zZ)sWKx7@$H1g88UOt!+CZe_g7acP2*(Y@>&S*hXE{>(?G7v6Jw)~%IhYnFOVS(L+E z2e#$ADd8Ua@*jSWXlyQ5-N{y6@SgGN-H79b{CMqR+rkYlk70?p%%Qj6VG`$^zI^$a zz8ACC$d+|eo_zyR)HYB>?Lh(I=Pk}z&<~;Nxp{~DFzfREnI4O`V#zXAz(WCjgJU|qal~EgLrQ4A-B)M~>652> zjQXUaZlJnzZUCHv#U<9;63I@TL%X|9xDP+vcln?|8NatSN9Af+rLT)KN1l`Z^^0Bt z-+!;oHh+R>*Z!#zM{|zuJMN?VRK)90o>1}4o)aahwGnEEkhyr=p_N0k9pz!osxf^s z0aNbBcnarR4GosxG5`B5;qMU@6>L(AQB&AeMMnR(aw8}In%qf~_`say7Ly&co2F8G zlZUO*EOA%mFYPCr?o(HpTsZk4-6vJ`>rm4m4Gq8rh-idIe1xcd*0Yh%gxe00u|@O*Trey02V>}VYsLI zYl?5a@UiCG6a#;wYy#_khnm@P@jlJZnva%U{h4Vapp~U^1qlQ`t=Oe;KX%akI4~u3 zYwb(LzfZ0`ux3NGwB%Q{v0V_UwZ~2#t=wF#3^>opK}?)cAqELPs@~7j;e^{+?OZqY zCSxE7TSb$p3)u(l7#|d+7D0R44J%1r&l0cZvD`vi8eg81Pe^oQMdI~D!RA}k=I$rN zMN%CXQl{sx|B`Ydb#2L&5`K$=SejPA5+JjqDG=N=nB^+n5LIq-R!7>rf54pU`f) z=rSW^2iWYaZ{As+v0f&8s~1$5ZCCwdCKFo}I!NyFIP)2^xopl%*Io9jpSAa*PA`M( zE>laeeXNM2dDSO?QZ%ys4(fUL_{i5#@gef5e09-c8*yG{?c4+~R7y@7VV3*4z=uZH zt7%RG6;5@|-;eA19HSM^8a!QZG0?a(trA(O(kUp$ zrj2+cR{e^4e%SxNq$k9YGd8@5Af=|}qyAL?80J>A3U7y6=oCqguc2>X%^a$gmTmg@ z<@x4-jal!3dwfIf(=P-U>Y|UbRr2}7TwYzjG7%6^S3A6c+ zWbKgE)H3+7&qXPZ$MxONWJBdyd01`VaflF-(o@_sy&8+^Z`tb1&IL%3Zzn#u@7pHQ z?+wbMA4N;szc4>?f{*@?E@ zWtoQpZzTn6EPGGB9rME-FT0 zNB?y+9Vtr&ooBSo(weA>B>3&@9}^}Os?`0b62U$z}Oh*%=ll>hPLYq4@9z-KR%(69olH54HarPMb z`YL2cRm@a*w^L0JS#D~Q7}=RgUmMt|ZdgS(Gc;S`w&-Fm6}5cc7*TaJ&%D!2I-Yri zjjA{EkpW5&eUeywzwNoxLT>x#Z^*swDtx-^+Ixj5>Tz>qx3k0Ub~WhKmQ%2U+5%Q7 z1l~pS+*2(VC@%fYMwYI{$m?qbEJnEa>5I-#n!e4f9}2QKJOGoGf3T0Ip@BK)xQ4x( zm2bkHYUg%8^fAI0CaR%aY`vx)nbODYg&%Q1G*onlP05NZ*X5uaC7-?MEA*ZrNcvyTo z^K3Uf{~vDWaA;UtnVE4@uY9w~UVr+fyK9Z)RsK7V`;Cm-zI}^6&SEOz`7g`9fc5!y z26RP;6EuGPz39ieSj*?=< zx==>+Ohq81Fa8G|?&a_!lX@?C$r-M3(bhfP-3>gDnjsUBI_KXiTx!)+|}JHTDw>+%rR$3 z;gTr)TV5A2ZaQcsK0C(J@XS){&K(YyhJJzzHSgQEkI;|~ zc8p`|bIi4X4%_E@SM_Ofd#kQM7rC0(tQVP2KW3(hr!8EKVDWy+Qk@nz*lro1)|YwV zC@Xnn*D>b!FY}1*YDDX`&7Gvu;AGnf`H>LASY9*E#>eh##UtN2@`GOO>R>1G4XsA| zn6;TKtYS)aLHYhvT~=O;y}NU8#>waHS|#K4e&A;M`SWd-Zca?Uyvj6r7S2JuR@;Xy z(H?BQx0jp!O}pp z@g&f`Y={<>Okwd~DnpZbu*}UIxL*r?DwS6-k37DS@VxLHnKu%n*ZoI!VM@`*x9}B) z=vU;{$DiB{d8JBi*8vB1a#^Kf>k$lzJJuxoK;r{sRIw^tO3RSb_@xHM~#Skc;l@#P0 z$d5rvwxc)bgY{*5C{`lII=FPE`SbxFii#gt8%2f4mUcW3(Y5+kZkeS$TfK3L{htsu z^b@*`PBP`|^mcY0za<@u6DK-Bj}Iu^GzeIU2nre(BYIa@21pHDL=kDGFS9}oi*)`& zq*twad?R5q-<@70MBcUT3}B%gT&4I(aA<@s?CL*dG~}#Ul;jtmP==w7T?CgPz4#JC zf=xyAR?Or*`nh{0MPl0P*+Re{mK5iR5^t9&0kUZUa$gfVz|G-<6 zmY277V@{0TBBz#epO0VMRzxCqh``l(x-3mI7aNs5fH_%GdO#m(Y2~#tu{(AG7e3tB z*3{Hg=c0tqAbJ)HxZ7I%D(6JZ7e>E`29DA%DqBYN-Mp*RwO9Q5<`Yw}<@l&6=Y`6Q zmY;r>hLU?d!VujqsrjUY-}*1vkd=e8#jXGF=I{`G(RZ(3HxFClPWH#C%UDKV@c9my z3z3y__$NmiNz&f30BbYj5NtQH8@^T`Hmvt8zL2Ej_>!$Bw$F#jl)#F$|B5`pK^3yp zH6rZ+&(;BGw^jOBLS2qO_t9xO#U_RpR=0RSb9bkx@`)2iu3WiN!)yE&f2*M!e^qJ0 z9vP)1XrAF3URyKUf1=Rru2R0mx%HRQ30J6lp{bg9nogAI7u4i=Ja_qX_uNvUU0~ng z^qBgk)ptA-)TMD{V#a;tT-0~)-c{qr&#Q3sI7L%-aV}rY^LS?EM&dq1<%E&E4D-0VgpC#`?&~%ai>-N96jEsz|PbA{> z`s3t`8da~`+I}K>o~IlX^Wf@NpK*G2R(y7abXO8j!zyT#A$^#hvVGiYMODeSP(N{k z(r)xUuXv=JsYs~|$uX?3d}|wzyI`+Ptdu%QW>NZjDSLHDC7OBx8C4kwM<$@3BK7WK zoo&d@r3arF-)G!>3`yYm&uh2s?R$B=fIZ)$G-Mmr%> z#HFcAGf(p|ZA)5K0L{g9EItI$MY<&WaBL zXZofroG(@pnt`?2i0~d_8T3ER>YtS(Jfu}F=`ZW~6nF0fuSzY=A>`=K@TVLmx+vZcxdc~uCM^nO)X^-rPQax6>FgVd8-no7@`vOJEFQ`l9 z#`17NUXdqf;(lWSlSkFzAQ}#lE5kIU;B6Z6j|)$XhpX#V_5uE{_a$d1AtE(?CHo3+ zVi{{|e!U(S3fTI6;2~K^e0;oI;<<2ZgDj2 zy;a-J?j0@Z)b_LrksiI)a0bXH8a$XS0ji{Oag({H*$~8MrJgW z{jAUiPJP-@Ibx_+jOhDk*MBf6O(#~y7}RBzJK-UeMb~$UI_w}nhvf&6GW57)@2R zj#Yt4uU_TQ>|}#sz6kck!NQ%$q%C*>VRfS3|89!>L*jCY-{0JImYl#%=P8&3-H>M5 z+VTG_`Fz7sx4bDC+Lo$zREE_ zfJq`&c5^$PB_AakrE{0DyPTc9ff(Km|3&A3xqJG3n{`i1&SD zu~arPVc`XjLg+HFLZg3_XEoECd zr!KX?0k?Nkm^fe(Z zAZx^T&rDhJ=0lZPiaMN`;h0h(D#FH5X95Y^4|yOmUd*y9ot3&AKt2Px;~QtCP`}Q0}BNP zJmC%?lOF{IjH4416MfbdJi9&fHcv^LT)}kzU7_LQ%c>3V}RiUZ&(h@v-~o)WgvHPAbt&23ekZ+3Yz8Z+ivmf z7->bkie&dDPVXCd)Fcf$J4Fq#D`BtCuNdR=)kg#VdJN74M^*QprcP;98D<$0i`gSF z-W3hu&`&~50GT{w`xsiKHNFmvt`j79cIW(@?KKet%#LDZha=>X(-3o{5|WcuRM_w* zhkL=s0I%ivP3n6MIg16zH}yhOu;q+l$D>H&A#|6koah>rhXW3Sr&IL-|9m`Or`D8p zln=cQXp9HENS;||$RLw80RaKMJH#6m%>QAf*nAnkDiq#EwmH4)NY+5|=31uf_yd+# z#x3WA7O``NZ3!m689LbJr^_L8fwoO_pMsBTe-dj_m3f{mzX4;-Zw0yF3x?&ey5?ISJ}(2KFfn zKQRz?V!#W9A4rTP z7|WbXPDo%^#NcFOx9_5+`m?NGHP@A9>R72(4UE%YO8rj@V2VF?Z{u_x(SP-BS;^5x z$&`0DLu=W`SiO(i(Gw|GdNYw7&{ayK;dP=WQyWalu(jwj*KTZj%WCg>6XQ+MZd?J9 zsmO=}Z_%~}Kb@s^mRCZYHQ#lT6L|q05*|tsnNhlTRDbVJI=$s~tK$hho+ClfzN) zIBy<7N;o6E5Kpz7SwEFWp54|RI=`(j@)$q)_Ka}jD!xe3U6j00r?}sJJTh{ZyCZ~H zj9gLk?zpjwVe7j6r+G_WXPGb;p1y1IEyU+(Le+0`X7ab%P6;(ULVMG{iOG`1 zjE;^QY_6xM`tAD1h$<~+wU0& zJxh6>yv?c37iLB-OE`Rn$wH!>^kZm^225} z=^=PBLcVsh>M4FSGfXG-OL>(RZWqQw6NkyG(Y>qE&Jn z(|7rzGfIgC3@?i3)!xNlaLUnlMsu=tR*!?Oy0vDrik%^dP;}pdY`udp z$S>(9OzN|Yh>DEjM9LuqA6TCw1wHQ?gGdO~uWvuQMbz*{NpfOu$j=Ppa#xR~AH!BO2ZX+c4wN{!?{mZo2z~&CV`KcDV1P)EsNLJ&M`dV6ydQ zA@@+1zFA{!69c0yWJ-FXjA!>#$DoQ_E<>$g=^0o&EB&kHzhygR$EBNiwLQs`0{LNP0R-i7<;BM%+{l>NF#)Oxq(YZ6Oip-&p=~3m+y7hWZo5a4r-H51tHhD0 zsgb>ToLq9GGQ3z*-|LJI#N@O$g=Y&0R1^GM24Fvdsx`Ieu1AWpwrhGpSBSnP=+&5c zc2BnUp+nrHkXBZ{t8$2=#qb&QlG`*6pG793+@Hb2tPORqc4ksRWMt%WT-~bM%|O#` zuOR2MQshDQY!yX?7 zXI^eCU4HLCxXBpOLxtD2hW|rUdHvT!=tgfH3rJ_y`m2}&*od;}on1eFW{cD4ZuT`> zTn2^beD%kV_gsS?orx?7=FEz_5MQ+2vG?FJMNiEsAM)_&$jR294e>~qsP6=a(&}7` zLA{I8+z|QP=eW8AiBcYjO;5Y_&_#+zT2AJWE4n&6F=5ii|to5t~FG}WnoNZH*M>&7L2qic+wptO z%r3utDc#I7`VRgrFPc#m3k`pLyt{V`5ax^L?|qIX92m`LZR8zhebHaO-SM1#0BmiA z;?v<=1aH*a5*r%}EfRgsAY8@lItDa;V*8Nlg={oE_S6=_k9ifP>{=EAoP#qEjpPSyP%SVbj z0)i*&{Z15d&)ShQ>s933T~^0>o;p+2t{@$P?nlYPD6PxHp|QgcnGgkFh>Y$U2&Y>t z_xx*#K!CY>0nOXHV?uCw4}W^tuaOF2l;N_V@t_a63}r>Y(dukBJ=ypSCnF>C@H!d| zxfB%@XKnr=u0nIwCV`F4qgF!TZ^PwtH2obSYtsNP#lkhx;-4Eqd(H%XrlTfIp)&%QEp1-92kF-*Cnz`y4SD2T1x6U6yibH6lEDA@Bj!v%1Iy=_i7qHO#w__p;c1wrm`1>&b0eXAx{m605s-ZE(N z2vOy0g&^Ibd7HCGdpHigZ1eL)uYGW$IugSwM;rh)J@ZQtZaJnUBfb_^eBdnL+-T~ zbDnUR&jL)1+{sqr8AI~*U9}9UR@S%A+hp&FT>1F?CKoMCMOpbUe3DMyN4HO0NQdg` zIMN7h&#bh2rIYzhNf`9bBJTxKoLYr3B;HV(aU1xi<1GDb!+WLlA~`8Ku%o50e#m&z zV#>#Uvh{h`|AvL#UYZ^#_lGkdIh?<RNfEl}eimw#l>KZpvrE@ZAOUxYw^^5R z08UH`rv;1tW{gLA`}!2$LqBQ{6wPm);W)<>mpzPvn24GpE`)t+MoO!v$-OYYqPXQHT>E zJ(`$s0@sDv>S?EPS0Lr}7I0pRn${ty6LkNu#PA(7C%8A>dAr@WQ;4v>c_n4#FG>hb z2h1vGY=!Y9Rrihv(~*+WQn`oplP1mSfqiXlF`wALf)2)bt)MJ`B!;~4HA{e+RIUfD zn0ypyLXh}2j6CNkzlURiD+V1VV;~J)R%C*&Z>UZbxf$9TfAJF=LV@YkM0tn`#wnX$ z{}RHTMpc8f7KjBlhVJ00KcKLQtZSHJ|ukjE=V!=ToZCudmv@($xv5yvKY>~Vx;&tHnKHkY!t2z$nxt1rUYL8={3U zUx$+X)8PI-WUL^I<9mSf8$SsEQ%gpuIgiiJ1Re+pS$f{Eb++AK4wfHxI85bl#bHRe z(2=boOm~KfM*SH{yyIOg4-#L@?tE5M&q}^EEL+UoVWr9!<=d!2`4;sXE^W?8;rD@o ztIKF?w>ma`2;==P{+Bx32XT!tHWA2g@PQjH=&-j6v+Vh>H+#QX8ft)sP_U_b#Eu6VeE70SrektqJDxO0fu0 z1A`S6B6|0OOEf?``*K+vo)WO>*s)}GiXV$P2TmA!kRyQ@FNMsU=_l7{w-jAWC zz^IB9la>o%6xV=JnaW?(d9Uq_UTU>Oe z_X6?YD~qSJU@*jxpz0rg5@}So8iH#Z4WGH{^S1mj>Yl03b6)4S)ls$^ixg=i=qNOA z8L5w$lgQ1MoMhaq+}v={qmg0nDZ7{p!-gyK)WJJHzu7IqWSOl!c!`vR=Ht%pP~|;Q zcvo3+t@VewzlX;qVO4LdU?j`_bwH{*%iIMfn;C8o7;jF|YuraUYO5&`SzCUK3)ouc zZq<=;3^fkCm;^H&0o!dGs;rY>xGuN3RO1d2Z(QnCXocfF`jikmtz#U~y4&yy3lQf@ z@hBATS4g3^TKQKUNvo><`Sa)6^St2?fSAg6wrK@9{lTtk*K!}Ahyb5M4yj#L^E4e` zE_~q3vGxME{N=Ll4Qb@Xq4J%h@!v7>O(Ga#f%Qu!yn#J?wmA>LO*ZPiV8xp;vlR4# z!MqMDec@Q=Iu%?_bzOLU-xhUG*-|$BMAFb~JL$&bkv^5fTS@4k_g+ z<+!tTU%otnL)`gZpxs~{ULo{%qIU3Q#QJjY(PVzNtZ~=nH*5c{y)Cy;KUN4k>JU?8 z+;+nx9^;uF^Ney3!G|+<{c#CQ)654%TB#SX1ZiBO0|iT$hV>E9^&L1c7j#D)MKq1| zD@Vq1z&;Iv+IYK9^-15o0mR9Q`fkMWJ}9rk_7Y&K)c7(JF$~c5@TtW8m9+ydquGeZ z!|9rNw{42s0s)@5jg<=j?ged#I-7#T)a)AfwLM0sfp}cO-2ilOyYBph5aBJ1y+Fwt04T2X9-AH$GgWMgbFy^~tT?ml zk}rA`u!Y;Xd3fpp$E+n{k-~_A?Rh3VBI*gr6K9|CAw98+?`}{3;=c~-9O1*DfQ5!F zzu2odo|gutM@B582!h*nN!Ux=L5ISeZ}~RNF{vT!L__5SeJ;BVETWz2f8?uVkTnU8 zSHsuW7ck;o@Fh_EvX(TVP+Tk7LsPIt`#Ta~@KKD8?NSn5u{hqsH-ee|SJ-roUjfo} zXzFD-yr_IVf<>e~U0nyhRACu{ro*ku-#Lf@Lp`}(AMKS#Al^T zR!&8QGGbdX0@za1Hb;{(jI4N3b?Xic^+E8_*EyYzm$%#=;y%3h50CD1z>cC2cNVYD zJerMPf;#5H(p3&h`uQ+g!p8aS&-VuA=J9i^o;YzrZp9;HisQ~xLU~3zQWyxk=yY+h zsdEY9YA1#mmd#f^3@@kq`cVc1q05Ke&YS)GVxU|c!N>RN{XzO#{ze;DM+aEfGS<=` z)_o6w2Zz*w{9E-n?h=h6ErfDb3$rDI*$%K)=Ca|vFpl#3+Ce`6yR9L&$NT%KFbsNi z7nv_Dg>6d=fRnla33ZMUli^I+t_AIO*h#+Z0BeH}y;6wN6XzIMBNx0|1R+y$C_EyX z$gtpyclGuro#UdrFJlvulKy4OB~DI3^i9`b+KM6_k3pZn2$PdvbJ2~cFJ?etcG(#M zANkg2oQ%WUoS)>f(~alXnaLSn&&F<17Me7+qGy&amT>8DyP2nhP)es5Q=&n*j^a!- z1{yB}-`;^*&-{Ml)Q07mctgEZ2h?w?KcD^Djn_YCxcPMsM}AX_JJffaEM8~kD;F09 z5|Cy#|e*))CT63X7aDmOrf~U#vtoLzUtdpGm)tLpM+MtG%!hlIRr-J;%)yXeLPv@6ouq`1W-3#X z2vy*#d))B%jp zLZ4$^M3)&$5Qa+ao(vnX_IcFWL(=X1*=cv*vKEyj(9Bi+mR$S;y;Ow-WI+NJSdnd# zc3_8#BE{!?7{MG6&QSnp*Lzpky^2qU|-W_#D#C! zBJHi(v>&>)p5|Mm6;;|eF<4<(F-F{*ZVhse+0x@4wSZ#%?l6S7iAL6&I(Ilaf?M^( zBTvI5o*EJ4MBRpHjz!zNgoK0+4%AkP)$1(`4m~V+*v)_E7PW?_WtC4Hr!7`U7C;&B zB^o#n!Agaq36^dCCd^EI7era8KLu}QZe@cVJw1fIm#+F(6@Uk5-qbO}v-YdQm{V|9 z3O3V%xEqFBJm&aLD73k_yBh-LdL6vy^O{V!6)%a$Ja(8y7mIU{DEy)#NS3Lu2)NtA zOU35NWXBGTUs!mIAy1xh;xZ1`-mwC3W+%4aVrJdt^76;*bil$;#o3~NxTbyLqK)bU ze0R_F`Z5Y;W@kP@w922-0#l>=n5fG$_fMsYYOx9`}|EG5DSv0 zSvlUZkZtO3^KW?x`i{+lwNzD^zT1LxZGa25JVlA$U<7PBRU#kTV0z5qHQ z$07i`5&f}rO}hmx;4@XvBDbxN?TbR|HA2SOENRJ!i5#cc$igmrg)WPZ{57-68_Z3U zn-PK(xMl}38U>U(&u*$FSRekcyW46Q&cbZ(B3}pPzsW=g@JNj>&>T zZ^sDuWNo!=TeSoH=P^bQYHgw^ifEo|8D&(j!q~+F_>el1uh4)y?w~yy*2-kK3sPOZ z0@>I!5LA7~5Qq1GX7im0+pis*jZR_WAA64@tx*)?yJyJ(Vo>J$kK-HX2%pRT=)bG= zbyhkM#G+?%Q2^#=D(gOmpUM|uBKBEZ#1e0lO=r&4nq48DK<0oN$^yj;7~P~O^Z9R8 zRhpe(H(87QIf@7#oeg9IbQAqp>dY-4vx<4+(Q>%XvB|MU^&{59S3%}*ES7Ms?H?2Q zlkMr)XQulh$Om<{_JfZYxGPAoi>f-=CPmrJoOc9T2f>630m~9?L}05>_D=i{r*b}! zCc7dvpbkN=4|z$zh1V@FzYS?e&eyH^N=6A}i4n#Vv<+o}J952+`K7F;mKdtQL_0F1 zGAQ_?N6l|3PkQqb^bm*PDO+WZ>O<6iFNTQl_QdG(=nbex-SKC*1*#X?9-64waB-MN z2lu?`_)i)5nn)9xg8Xg9R&!-@S91)ZWwPvHrZ1 zKpUoq zMD3~N%6tw84dcl$4rPp?79Fbb0Pz5BM@pI zl)4yF3kSaCVT>~q0$dIDAf%%s1DLG>w3gU2E3=K^-}Kv-Q6|fOFBd!pn2T5#w?&y; zu?gpJily|8Y{XzlI~zT+tF$j8*Fc`4bUu7pyE6Uf4{z1U)niB=#_rgm(ZC8l63ZV( ze&PNi7VuYr+nqa3QUggd+~*>PPn>o6PYVE!*J^aMC@gF{R|F|>{N?b}EyzNvnaLx- zKy({52@44d?o))m!6RP=K3a>&`7WIlz;pz|xU3Og4EeI#qn%VA)L(U=?x20S~Xs&Yj&GO9>P>Tho| zaQ$3-(`V5frX80EPvzip4X3!%))|7Qw)VLr$}h;RQmRN8A-{RnXtN@V!9NCn5TYU( zaM&^`?$ci{ATq?DCYDuh4#)w1F@pU288^$A8TM<8E0QQYZSR*ok0E|X2`6rDrenD9 z+ZxzbYyU4AxJ(TFklh@0S@uqv#aU@EuqRWDwzkWCp@G}V75n`!M)EDO_Ps4BnHEa< z`=ZwgW#!*=q@Q(2W!p4f**9u_01^fmJTYwhG;sBMPtTq%cA{un4TQ?ks|fP)c-&c4 zDKZu$^mLanBGXlt!jEY56=Co&5@#Mj>nVhG<_4_Lwv9^1W>4HP8eFZJWukfttKA+- zc^w535oUJUnwvsvTCH0pDFI7tN&ESDxN`2v?c-Z11j_YDvro!6L<8a$#rCyiJ$dul z;uOUl5;h+jWcn^N3F-vy-j8OGcKOboI}>1E)664#`#183L4~*rR(l;-vXbI$NMXqA z^dIF7tNIbk7}w=^UF}pcGF)d1izB%l2VhIB82JkgIK0D`oYN__7r*O;^B`T~Tvd?W zt151xv9}2m*AximW@h`Eg?Ga5HcVg6on~9nK?MF&KST6tjy+-Of0U`RUpHW-B{{)_ zx@WI1H1?n~Gn2(&K4){~OJsYtp>4$kC(LM#?>gRXFv*&jkg$({;6~==F6r+>NcUh$ zgyx@15aa#M@4SWd=7j5)Q=qu1ldXG!GAwMs*wRd`r6~lvwha1T*+0|YfHO#olOd2G z37?$2IrWKL+910k>hM(688S}%H<+2Mm1=};d|(0o>e~8`cbaHzZc7{NjrGJYWkdD} zz9XD7Jl1OoaDtZ-a_Om+!YtA1zA-&blFQ;hT()8+C8Rp>H;C>(V7nxkK!(`dSfa{zpX?1AL?jAOs&r$8O7d2?ihI+sqlCzG_%Fb!R>-n&5D`qp)@QVm&iiVORI( zd{Fv;btdV}ie~8-Y)oDWRq&)AEDdhe&tI+^D3Cy>ty#v6;v+{p&-AZhQnsnrRqkgs+?%-Q!2_5SPWgn7I{0< z3?SUQ^h$a{LMP;Xm&Jn^iSF$;0q)RWhIGU1I2ao5vj&f3Is<}7P$T<3By3t?2{YWT zKVqVztcR&2SDz&e`No!;Zj`O*E^GN@`dwQBFitAWF)s*dG~L^Fm{1lASeuUF(AydH zKA*RQnHT^?;ut6g2VgFN?{b1mM=CITe%5=NM>Mv5D|JBW5o?1R;cSE~wyMgyQqjuo z;TH#L{Wn+pR&UWsCvPBD(1w90Tw(5^3qR5?!=m`UAkK)Hei>e7n=knemm_pRsB$J@ zi9u-UkR4GM`(D5#9v(6=j2O)fjrzV|pgj;e&ZOE#I-cOv_9o9hj9$|^32*|vvY{)O z6(>*;Oli@!k=}Hhqf0Ccj&YbH#%A*y1_65nGJ-#dZ+?BtDg*1e&K?uFXqt^QIMNVxcza(?eYFMd`yt)0392UCe+is{>ud|*{ zT-H{EU4;@4br+mA9?6KzuaP%O#WwP+4uWx={FwFLm)XA%!WJYojrINfGb&&F9*ftl zDXs3Om^0H>I2J-C<@>P6Wc7xqY9jha_K(=l=ns}rB@wHFaU*7YGMGb*p6JpApDUxt zH#E9dO6JA@;g5xeHy}sBDLVAz_@sz~AM$vDokhjDI$D2`g$2TOgf+gIo14d3dYs4E zLGodz#6z?t%@O&s`3%F3ZEP%cHv6|h+Ql1gIG}3Td6^`Ml-XfhOGQ`fLQQ0>18~@xvj#4xjG-cpMOI>kR+}5=` z^&CBsUiU&~5syX)rOK+Rb7ntxgY@;9dAkT@zz|rkJF&M{Z^#{n9OjP5Ht!4wbPldK zyPXFI1@|J3G45yS`orI0w4X~Km;V{4v6@H8vjD61&i~DD1(Ijdgl8%Yf=zvN>bmzp zxRhvs3Mv%Ukj;(pstz@=Tz@Cvmk5Ku6Fp|Isq3UJt3wF;m zz^94$8K7GUP2cJv06HhTHwib|6kyk&UtYl6cZ3AlB_dS7?z~pw$M`a1<{ZUSzqzEI z@B^OPkij$2^-OlzIwcU?+UllNv)`fqvHq(nXG}VWB2gnfZVrm!lBO>BcQ&IAzZqe`=Fxk#1G&E%63oo7gw%3LG~Q-j}8C^r13^G zwp0^Vna8;rF)fOIxAtUSdPb|!Fb&D`=i}p>^;ASAKzBh6`g&{we3MTI%4+j5BCId< z%7B+o0fviwaoAxfC%V9gDli!@PBGPmro8dZqjr1kt=@s zrm~f4q8f6=1=PKhaM~j27wVzXYRls_-FC&fGxoXbco0H#XZ$~|zB``k_x=AE8D*48 z95RxkWFD)`lr5`5naL(`NcM~dNjBO0oh`{;?<9_W5SgiCh8%nTu2=8(_tWR|JAe7h z<9^-ubzj%>dS1`#x_9_$jty%d%i{wKbp<)^$by0bc!}O)2RHhe+;0tAcr4Wj;Is>V zYDHtq-Mt)oHCCT)$zMJ1b8Fwwm;vUPLB%Rs6g$vqpm5~j_xJc=9jw|A5>ls|7QYyA? zj+2XoxjEGty0FN4l^qULw>{xiw=pcx54t`B;0A2lNU?Q!TCXCiApIiQy;)6t0m?R3-qF&8^KQ?3xpTA zHIt*28$#3${{$_GpNv{07s>5+@%1K1WJeU^ujC3rn8P>w8WV-($xo1)^ zqFDdLTS67q;_&~q+9oH{v{R#@IRHtN%xwl-_s%>HMU&U{o&p*?fN~;vrTSM=stU;F zV1Vd-8R~Z-$ogU+#WGhZLhOFYxv->YZL%)-Hfci@_dB7q&!~5Zel&SA*UP}icwc}2 zj?AxCPbd!c)BA^A1(q0<`ZB-d6v;~i&pvN>)gwD*7>!!}h(HUw7#K&3NNOOLW|qg~ zzxE4XM^uL6Dj6Y|Um@>5d(lC}>O-0^0Ftt2%0uNRewcZrxm}>%zRU0TwfSjYPcf_j zcBvJ)r?kBQuSCM%UI!ZL9eIwxYJwN~bskE!t6oLecT0hPvr>RwZ=>p#tP(bD{m&*e0IK$+uC2t0LSb1 zNEKz+DT3-iv`D!f-s0OA$`?R&-^Gqn<%Rx15I?Rt^Sr4*`Srs~XT&wQ_eftr2uKNH zfvh@o#HAJBD6#nIKjp!`jY(LuUwJE9%I;VG=Q6Pz79A0_pM0Vi)J$T^-U5t)QtN{V z;JfW%5VrQAvXU1Bfr`}@y;7I_6XBvvC~fg4TiXOr>qa**ihWj%qz=~CA1x^oHa8!$ zT{wio$F5btQ_8=)|8ism%G`?^KJ3;)#=0$HorhGse4}KeV%rl*VG0k*RH5`-*RYh` zYJR^>4!%|AqFM)Exd^<26Q-OCgn^%rN@OPMxsY2bo9sW+-O(s1BNGS<#;|Dl%{%|W zR+*72FL7?#qcn@>foM&)W(X@JN!>$K&)2PareC7`ZYj%kfXS6)>ETOBCO(5GKnuL0 zr}$;+NY%$+MuSVf2cES@QFowvE!S16GbVr_7UTUJg=ijTR zYu%QlQ|RSxmZQv`PuICERn}uWmyUEFcUS7F_81!?F7Atu4N-EHmmvE_=9hrHYnJek z4A0#tBAHJ?JafWBAEQa;r}?(Lhpaiuqhv!`b=g=iyymzEdnUP^MyNJAgAo7W{d>Z8 zlgL?|o;87>aJqC}~Ixdu{@xt9yG54ig?G%3DU&qIbN{bW&SdQD$p`s%8 z!e+}!8j1lnr_!F3u-;E0_&V-8`jbkpPj{Gzf9gmPJE+;p5Zj&9>lh!DLCOW)YVKBD z)t+OMME(>vk8R%aV*KAaEIV0MOem@cnPnibB8*nMQ81)3*hJkIkt9Y1`hm^1JeD)A z^U6aGKYc)vo+iADH;W3!mhZspc5m9goUM@#(|&TVnK%OMDflzRurY)QoQl{GDd(Fx zh1japP=%bvO5HpS1xkvaGq_xZo@&~kB$Z}|g``CRX5@@-mJ#Y`Pe|D8*Aw(QWSQdF zT3t6QI(%(>incNcCgKN$Takq6a(b9AhoVs(`$4>s;pL2}V?XD9^`-2;TZhL& z!gQHmOof^z@E){FNxd+cHd25}9qcioAD9ZtlY zj)UG^GooX{0e-jCJ=Yn@#7+5D9;CSK*33^Q$u8~l8QU7=xo53MqnS|4moz@`%EPmN z9{EK@;&3#D5?EJY3S|x+%eUkZI^U%;exD|zJ2rV6yw(FSFKuv)x z+K+zw7I9-7Q*vtJ1Cwu`)I0M%&klCcgQ_jJjueb+(c~cTNPctQktNq}xQ=p*k`U(i z8d{2MjeH&dCYkWWaHNs593hzlHpd04Tf2Z`2^e-h=YDZ_Z>9{g6txFjzaDs zBZ^v92S?fh-A|~L)jgDwJgJ3+)PV!hB;lZ7tGl=o1A;q7S&g!W=ocLEVnjl zO?)Y=q(Kq2iD?XrN{#22J3TNsD5`dE5%Zaeg7yW8=_7~*x5PY5neZ_D(|^??(>Rla zgZqP{piBHzy*sf*`j{ohyNJ3H3R2V*P*H@#&97l?D8|NZE^~2O2leF>@Sz~3Zqo)u zk0Wr49?QVto57_JG!9@PdHi~Vj5Rz_)?xoHQI=*Tw2pmX^S4b{5Gw+AjbOcg!a6L< zVRBE+ne&XYg1L*~s1}jU>Pe6M`t>dumDV%Xwg7iEbN=m1OGnX+WOCx=Vjign$a2$G zJ*d30qA*cGX{tXBYiTUGV5JDqb=B$%_@Qzd>5wg^TE8UtDE{sdWJ3W>md`hfwk;*? zp-~Mv@F7eUzTdY=mkoV;cWd^xMhTxf4u*D#1QW6PzTMm{+Nn)y>LbW~JuS`|28(UT zQCDCkMsBws^z0UOuw(2x;LA#!c7jYq54v|h5sDIIAM9V(AE+agrXSZ=ELFcGYS~5#Q)Y5%nE~HP?&R+p~Er z7Xh?K1zV!OG1d3yV-8BU&cpZBkM8=E4In!)SZ0%OP_gxvpqY?Tfw(z)%w#q>r2)y@ zD+OyoN1NW=^`CdAMs#hiEy+P|jILb<<+cWjgg;jPCOhoJZf+^49O0rS@FyFaqtBkOp zC<~z%LuH&TXEed?6Ex`uUjmKy0J< zdVl^b_+=SI4kzjkCpx^h+XTQV1n$)E#?k^ai7E0PxzT0#AHHx;cZUx)qoTj1xVH)BZ}9z#27WtGb}j*AVO5E9Bmx=hCw zmzG3lm-1Mp))rAHqBdl$+uR+sspPIx9v>XFp6^g7M7Zxj1#$k6NY#fA)`Ll@0t~^A zyuAyy&eGuEN>&%X*?4LrF@?!>Y8|Wd{<@E0cUMZFxI!@U+P3l;X-3V6$j>%}oPD^L zhjaHg7>ext5_gxZ!HYXJkMR4TbB)&17Zw+X`{=adyArau%3shml%5rws7UA+!wLBk z>WLc`%17mhuC0xjd~HGaYLwtPSzDj7Evt_3dTJ;2RCB&EHe&mGI^yDdeZAcQ3rhcu zeX`HqcKNZK$iBY5bwP09QI@`vEQ#PjOS&=o|`!Z-cMk1F4cAs!c!1zlzsaA>uxU$s&jKqXn zBD*+zbjIEeX2gjRF#+PTE7cSxu4LzP{evb1(eWsuEZG6fKx^VZ*VFlm1XGaD?<{wb_*H(EiGJhlluVy`=j%ahIb~{5HtnwH3MkJ|`ZUl| zof}S(8^vKrZCWF&cXYTl{kv&~`}KNtmR>T{i-@B~D5VX~(ems1 zf%9|-!%0zFMz$y#I64>QIO+r*;~;B~75#=uN(shz7)Obo{+fqDCkIOsK3$PU5u-ew zON!Hc0{yV~WN5s?4Knnn?C z=DdGh{6Ws=c??bP$Ip+a*(8h-Qz#?}?n$x*v}9VU$)d8=Ih5g^s@!W|SCbb?v{_6Yu>z7mYXEr}40}~U+ z#CKE<=Gwjay2|Rl)^})7?Q0)TnY^PdGwWGl%jLXc^h2;(u-+rnMjphrr#3b=9Hbex z@432`*Vf0MYFq%((Qqnos;eC^^Em;=KnxyYoPc>4w6?e!)dFnXN^%TZb3J}>VF540 z8u|sC3}NM}JYl5Rt1q=>Hhg*Yqq31Cae6jc>o2ssK>OiQ2Q44xeOUIW}JELnQ zq-fh?{a#>1pIqNrD>^NdQT^h@i}##IT$Pz3x44YBySe2#x+&cSZZvemna-1lPzJx9 z_&+UxFneYZc`LYV)^o&M8~u#sF((x&jj^vm62gj2hN*Axx5jGR&99kJgI}zRe_ zX5~vT7V%=(xWUO1j6j?JoHAF5zKO{JK}nY>E~QKMzz~aNys)t~SY*QDa`}pcgf%3( zHXIjUZA}RH{CY7AZze)#TOvFR$zxE3Z|O_DE+Q0{RLSAZEk{9^o|~IXY*3uz_&cJc zgp#qX_05$XM*J;PA%-`}OCv7)4;eP|>RsnMA>$xv?z1^z)}%z2BBlk(c43Hgg8K#AOq5IrjUV$U9E zdoUwH8!I*ui`j zeDHj!H89=ay`(Hn&(@YZB25@>d<|aGyDVH*wy%Dp9|BcXtZ!A3$y2n;#@2MoU3$;- z5zkUfD0<00^%X!(rTNgwQ9`ct&$p(EN&5XFbg&F2-9X$WC=UXu1kVhG)|Hh_y)M;C zFy~95uq|d?3c=K|<0TmLq}6?$QD3DglBWTkI3$q1_~!kJLAd_&&LP)szE?WrXN%R{ zAN=siHe;L-TUXFwdx#ZZIJHGjRe?`es9bMJ*QHEiw94ma$LnQ~vag zgPZkFra`;qa4zzhwrB8&=gP}OcF#VNxY+zP$bH~LmHz8P>72Bi;0$O(QCzPBwUE%O zV()Oa}Opy*}fLKa+R`>+3+1YsT&QVFQS5X66AXOq1v(YCWmf}o0qd= zsh5c^hEt=-S>WrJFM|7m84#~QC952kV(gp}vY5>&L~VcKQ3F<;jJ2P3=E|D-48L7G zkPM0pht)c+m(5O zO!nDPoa}R^;~8hMjI4BD6m{=%?IKBif%nR`!sZa&Q9N>xpa1j2D@Ky5Zhm_qFdV#y zqA{V9`;lDunes&pg@VnO)|JejIp9$)qOi;RX2lpC;{K`;OpTes&{%5sYJERdzxm`> zb{W7nOW}B?R@L3@!IxRRNyA6mU$^AAo9G=683Hv&#hL}kGVi-RdSu8R2g69P;awZb z5&}kAzOOLc(h@x1iF4NHO`IZOq6||rh=I2Jb>b)I?+@4B>J^2`oLKcSr`)KR{+u=s zQ|oF~o^pBFVX=LaI)u)eS300k`s#jkDz-(k$fW*J)~vej#TfZqlnW7xbUxwux|uSZ zKoGit$gM9u&c3uB6|?M*=Q#e&n)a#8u^G}bU9Mo^?43|>MHKjD&c3Zx=*YpEE|Y_T z`c3DS@G36Ygz! z+}ptp96sBKR>NV);F8g^*dJv-`sG1$V=&nVv->_&G&+1=0}@Z3+p|n zSLmulF>N&w^N^;tqu;2;Rm)br*iDmrix;76%+xmogP_OQqO5j%nbsj#M`w$0_Ve9A zry_%h+~Ty++&@nD*Cxsw?>^EMDL00TSeZ&nz8*)>3G-f<9b;~kImCrYD|H(s%f|;= z)gP1k4si$*Uf$%~VVDx%p zeiPzaQ54#OHNU9my6x+{pUuMGnJ;_4JZwpxY6ThBKi?^*QOdiqHa}P>ne-+(`919@ z3DbL4oi;_cjw|}XzoRABi`XR}Is*srjNWk(#VsFIPIJ++16sA)ik`gED$lR$HNU9&2r)0q0vi zz1Dp!UXr)JYN;9$Fx?BWfJQ;vK~DdivS}*I*zyBh{qar^?-e@BCI_cipKo^a-rt&@ z`$D5QrXnC9U?eyrr$2S?{=OvbQSpP!+nw^I_Uk`;`{AMF9e7A%;>!Dj^HcviS=5^H z1c^&@HVbku4X7OM8}#};{h>_86OuK#)hl72~jkuls>|WC-TwWtiia zsG^`DY%++6a|gna+P1}2%OdPyX82)kt7*4 zYT4|Mdp#reLnq+e2&y@jLa<3vKL$hI?`JK!GCG*amVACA--fK5_5HfcJJ!Bn`bs(| zk6ty-eXdB4i}Vo6vL0)1RFoJU zeR7am<{tV>+*U-Tg!gN2GE!EOU})0)f+g>ICHZ*0LS3Qn&}Tsb_fLtPc>)m5LP99b zHOd$6n)2xMb?(Y(uq5+i-{9$wqa|o%L_Zn7lyVd~P=82(Q6yXE`&+SJlYL^i*SL2c z&3@b8A9H_z@(ce13QnpJxo5t?V24c-8bi9gWJm@=`83e90HseYkA~#9*HVMAErxzFP^>cFOM_Z!NO8JvKyBfVSFhQShkEyUb?-<;qy; zI7)cJ5vKl1WBk%iEZxQF+D<^HWDApJ6h&@cc%_~QIohx}M1zb)SK*X;%PXpf_ajEw zCv9|&zaKx!wz1DTQpMbURxTtV&qjoNi1Gtqj6027+5R(qZoN3CnH)5Gm21qL{J6-LY6(PBdZ*dpj=HGb-!dJud zn@>YRLI~f!UFF#J)0j>H=KSYckWd?1lrLd#-^IfC5Ng@!&mCcaNf%9BKIyeW5~akj?#D0s zzL`tT^u~AZVv{DMsiQiLmvEXZbbs9z;jc`tEOhs+s;tcOmSBpI>Uttxs=VFY9bJA4 z<)?`Zsrly@{bhLAId2xS>;V)hP50cKt!MXLbMF|9zUhx`ZqP$km2JYzmgRoOXk|o2 zB;3%ADGnH^;rm%0mt2v(x0$A?Hnw{ETuz%|VO4QWUJR~8EUcc0BWzcov zIo)`|K!phVXJbeIu&J1hoS+fVW54|@godvAvpajUZ9Bq9HQ_&q=vQF4<2xftEr9|=7(0ad|W=+(Ki=u$yT|TAT7v=pX zD_22;@GFQJrkB6-RKd*LQ+8geSXAj)VM{g*!@TRZm~OibgCi8W-yL$}4pOdlgouGzHMUQ$0vQGiboJ%<1j zhre>%(YnZo#E&cR+O|N6Nx?|CD7X+(;84z_tSy6qOZ|0bkMbgbT>I;*2#>+em!To2 zp4{637>3-4CDLVwZrmvouO14WW~}~|;BYnTskXM~$I?B(33Nb{O10QM=}@8enr(Qe zN=0 z(qSa6tITudyUny9?03nuA@MFw{2>{>ooc*c?&waMuz*QjzPCiKGC^s`&dRvssDW>0 zk#KCW?2BO#BU0Ocr*y94@|1<-kA^Og`$NQa<)a@wE*ZIlo*k*;{d-rm{8>|<@H=nb zDMR~Bwl)qxPoZ9@v9gFZ@#)7_e<|9GT!VxhBt__w_Yd<@mtolog@Owc7X>fvaN0$Z z)Tn!%x9I29qrTP2pNc;+Et1*Di#u0dCe_#fiaDXCd3!`B!{*C_#nA-brm5_CS?W7& z8Sk9c*8k7GGae2dPr=A zbIb6%aV02=$3AoXkW6tqA9o&Z%mu74Ji7hI%t8&D?qZ5EFNWEog1Nf(gTxB+^FP?G zHbINfkrECYL;FR|0@#q@z^+9}sp|>SU`_AkQv@+v3s?2flJH=$)9=gU9cYkcunp-> zl{jt&nmG%SD(OWq1{=8HkR93b_(FH8OHZcTs};~q%{f#weoLx*gT_Z}W2&HE@zIr# z$2;o>6YR1VbJ{7s%KVYyIKM2Wr}gj+@5b|MBsFuZHbn* z8BVUQzAI>#l>HlPF&4j3ns3+qs^;;prnP&8Z z3%!-qcUIxr3;V_sr+4T@RRTkP^Bz$fC#zcJ@gquoM_Gceg4hpfX$*F;q`Q5}4&G+2 zYs6-i*o+O~Ys?-|voF(q*5dxqDdKlWJ@L+wP=8^((B_FWg=M3r-n@Q*vn&_PeYvFg zC)Kv<4582AlE-<^{jpll>85U$p)N-s4F^CJNmgl3^X;a+sA#SyZ@KFQxPNw+oM}pi zr>Fk^wPOyE&D67MNl8jNWkI`SwI|frkCdXAr=z2zdk&HXp)t3g?ct;Fw5STl@%L+y zUhhq*K*nQq-ZI-p@;3-3&w-LLR7g&k7Cf77eZ#$Ei%x8^*GU;48)KAdF#RF<@X;dz zsm>vdf;1^l%dHcx8k@J*Sq`h^h#|aa$633@fVzp<6>rdI+1AT$vB{gnjrX08H-S#% zs&hBF<|6=iJ6z(o+tIq(8Djt7!TS^eCQ{Gs_}Dk`*xOP3D}18xh|c3b0Fg;D?8EFF zKwc)&Rm0>BfO`DLYhROKoMC9&{7LKN z7D&|sHI{5v8}c#EsV8WU&ck)!9h-jq9Df3Gy|9zG8Q_!)SQ6g~-M3k>D>`oO26DMN zI|I!pE5fG&G=ZNt5n9SVN4*0q-vfwU32sYcmFwNwbxK<@KRmhBLoCYnl}*mR>)A~} z$K5UCF<9!n+%`IzB$P8KCMGt_ahIpV8cr4k(v8E|m>%;?;x=_nawRoq1K$ z24H`j0+^wl=iSot#R?jOxdwoog`62{KzabouUIk|&@q$23q1}sJ!L(-JcEf2L1!Aj zUen(2s1d0vQU1tsTvmVSEyR_o!uaEqOto%w-io*`Q5dvUEBLyY`mF7H@(zhU;S z=ndtiS5c-uw@)=nA~!k*L^>7a4`?;a#HdNsXt~1t&02Miz+fmRTIK=}lD|)mO+9Ps zLoU9;)L4n<5BmqDY$T1mWdnvSbi5)37;Uy>CdvNjW)~dxlD{y6h{w< zSfZu6Pc^a3R^zBAB!j$lp1alkTft?ITwBP${+DaBy^d1*29GEWQz<051Q<;qdjA~^ zSN^(FUMv__B&9hpFc2!7^4(ay!#QPjUzrw(JZrSBHu`Vyt}C_2bDPgd&MUHi->)bw z{dPz24v4Y|1AIoEk>9a#U0kc$of}z;6j=zE%IlH{hot;}4Cq7VMGzKj^U_8Iz3RQ&)(6LBP66W@YvX#Kf_Pp?jNY&0 zKMJ>?!Bkl0I~R=sNc1^;Bz^z0IXK6#W4A<>EEm|=*b<=7iIAY?&iVo5_j8Qnmv8Em zd#W3}hwiQg{*36#H6TW+vmGC3n0zPZ)+w0`^=YaMRL`^-3&VE*0p(m4y8PS{tuWb| zntphE+|6Pp3lQV`&!>nP&(P-46!g@e+WjqP*=AsR&+09-Y(Y^`wTx6e0%~h6X^7_q zmY;*7*WVjIr00P%{?HJL6o|HJFQd(05e5*8=gx>seO{w$^PK8M#>+-?yl9?5zyhwC zxeVCmM?VcOp<@KP-jGXM7%>x6I-Xdxp*ui4Zr8xW%_NZGG#xOL@kX?=x*F>+b9(-G z)O|>*-A$mU>GB_g*Aa8q4M42rwI$0`%2ks#fYV#E`fWzbbVSFQaB}G%TjpU;-7D)sU}KfH;AWW*<(04KL{C2N=#4rr z#SW3#L_BxuiFqTJELj?4&fR|!hzJ-U!ho5imjsJJmkmQiQ-MtG-2U}7IPCp(eAJ70S zYyTv3SkTtCl!%zki8qp(q!;`?wqdWI4*Zm>t#y(3pydK_3D7INBQhcy%+}`9P{W0! z5KjX7bgr0JMc{ ztOt#GlsAjPr-zOq`|WJ-oHCi-^YoDegBUzJf}dnFoFPVl@46duU(0z>nuvR&;$PP~ z#xv6fuHNDS<^P<)u{L?;j*gi&d5{G|RsHDqVmtJLFgx}8uYhl=LKV>+Iqt)t@_o_9 zVc|m%ya;kuGEGjuFdfY7Ux->%-|HEP>Pt$4hJsX9{d zv#RS7qlqk6Qgt2+vycGrgSb@bj90a+;P40Rm}>C542$&M#U&*@$s|Gi`EEVYYl1LL z@xQ$MeQ-Np;*Jy>US(l^&N>XN2QflX7dBHdR$Bs?*c3-}hX}ooDrGzZ_^t5Op} z;7wYdcH%?l)7>wC$^n0I5>>em&Z?aL5bo?+FeVPq7cEq}*r;!4WK_PS;W%FBs*y&C zmvs+CA1eP`b2pd9+!=}zo%N$#W^oM0>1@|{fq89t{iI#$JFtEYB8Mx(3A4k_in)tROG&vHF~7?X1LR_hr<2au|Ez0$Q|;1iJ-v=wOkueC zjrH|~w7_G<>&hzY>7~~3FOcf18-;*p(xG$_%e>>$(hM=5Jq{Ud?siA-S)x!~|CM*O z_}^A77CL1r2@A$hJGeablvHmE>GF%W3R5c*<}xrJ|jf%^Ot3ss}eF*VB{ zWeR!&O)$7l^FI_zXee1h)C647Z*WDeA#x8-D7LkmO+qIsS9@w!kJp%{XhIHSrQ7rn zReSmnRZ4mU8TZ+!L=Szt^q`#`&!wQ~xL_xnJBQ2EApbkwsQ*K<1d-Vb>o>Wkje~Wo zH`S+77zltg+^K*WYTJpAlA7EpnCP*OR6p-abx{KPa$qVQfp%rqG0Crr<@G zMv*|dsz3KFFT6SD{%QJ_P9Lt8GbAkT;qL^FwKTs;dYvy@sysw*+jUX_;Ps$P$mo?k zjm7b;-R4rN2lsD7#V6XG9o0bmuV~tnnpD`!s*{2C`>4{K3F8L2I#ci0!H|PCcsDc> z9eiOU7`mt_lnYjd>MJXq_6*QrY@0{_4MU48QD>H2cb)576VijT1QTt_Zf>Zx*h4I? zjcaU6_yh%PQSBK?SEuBw4w2|Dj(;=V@6sj}^K&WzE1sL>Q?>mH4b+cC^bJDPM{&$% zwrk%$9vMu|f?y}KDV4a!UsY9K?+8h*6)OoU8Burt^9yYS=iCzKlC479@GaA<7pnWm ztRD8J;^mW9oLf6dG=eYCh7y?vf)ak#+-y7M=4*{?D}<)bH~m1 zL-mLOLETeSS?`lJl0Zd^So8`+aCo|{T=WrBb zS7<5uC$z++uvV#BPeJuv+wxWKD$@1&@gm5xnB zVEA)Rh)#KAQm0j&ga{d`NjRD3u!KmlP6$Tg2XvLxHhJ-Kk7*QK;2E6`GKv--0|Z9zKjEpR)QH0f8{S0?!O3>M)T&&@0Pa+S$TBU}NN!?;Zz}YT;i?XV{uOSXuaq>C;PiX04 zYxFImOc*QAbrA3;a{;3&#Fpn!@3VKufyFZF@}1NC>IMyS*J0ymqxOji0a(wOW?4hkGonN4*}v=?=39L7AHL%U|h!!Pw=a?GxTl zb5;z}F7CLJpe+9@@HPEIIHuOHE>^>{h&<04`vmi9`ulIuhBCjwB)ZA^61eHd}w!UZPS@{z<-F2cP#AkB~t{vf(2f2$Rnpo&|hciN7Vr<(v zs+pxkV-$a!6ova!u?h{M9YJE9oPo{C<42DyZ!*`gYKdy{EWyi#WabpmupwoVoPzA> zT#!1bw3(0m&S-vD*jkng3Jc#a3I89U3)6vsb;w^kDai3Ly#)_s!Z2vMD40aIc_I(b z%fy9c3Rbqhj8~yTXMOSgBn5ftkyuNuAh9OyvzCch;AEH!Pl(0aGh=;*RhXN6l>!Gf;K@+Mx}TAHeln!5679_ ztH`9hVbvr6pTHP;JurV}Esif#(~lvuRpf_V<3onp9b9@p>}yd?p6D9~11*%5_v=&O7y2 zQL*yai^ri|RKHn)|9^d^=KONvlJ8)2w9N_TXiVZjYhgYn;>6fTEpWijD^j)a3`ja( zs0SzM7oJI|N=n@RyDX3aV%d`zS}ohJps*>YC&Qi9&;j6z_sWYAr4K$sys1%OCWV)A zzuyj6R8g_Wf+NDI>{HXM#DBGa#fG; z6onf7FE?NOubbCEzb3FDFoB#on)O7a7vh<0}F>ZVHJgpL?u#PnZ+l#JJFiysuqA_O}< zv<5kiGrhEV95F10Bdo9Aref-cVB-KYDB)Il@TY|tq?8e;r_#Y=p0ys`gVo0QHMj)J z<4{q2+6V7+}dwnt;6CPJK#ds+v6T7buH2r^eNizav z!oAOc1nm-!S5E-($Ccs^o~&oH?XdzzBTdfZeswTchWNvvA>BGaPyAos4k#tvY(nsR zt?b5QI~?RST|s`@)eE;&6?A7r;O0W*`)(nBcyDe@NvjvUnl`?cxe?TcS}H@*nM+!C z7PS%Wd9*Q{`&Qd;Eg-`mOl#`G{I+7sXG>H8KMMVX9M{0*4r@p^q za&*S;(;%#USQzc#YhySJucxp|l_aIGkk_~p<^bS!rrk|RoMl#Fz>=1T+kogM(^994 zMJE-i`@c9S36}S~@@3>8xN!Linl{*x=RNn{^(ynyQuACipBs};(MV(>dl8{Xf7L;s zJY;MBU0vOSMU`W)VlJ}>=;Jy8<=Ojf8&Vr<|Cg)`8xV#TvCAh(vR7fQ0+Xy=f>iX@ z6@^%j&ilenr)Nde!Wu%pGeggqr9DrRNbv8)28rDX@&EYC(Dc~C^t3|F72SZ^eYog< zG8BK{l$@z&&uX+|_{cUtu)b_dJAnPj_Q9<6EKDqnzv{@1v4kU)W>M^f%Z_NM#!f*x z{o(fQ+lI;>YSvuKLIZ5PpRvKOORf4?6J@T*$S@959f?7IgI+WPzcEIfrTG)i zO_5p$P`|S8s;Y)Tz$*?eJK>IQ7)CceACrotV1KQ?KG#z0g_edMMv3d*82QYg>gp>~ z`p2Mn`N~awDR%GmMi{(SG8{r;`PviSvPj-|w3+O6K0mQnsc(&LC&hFZFZlRE7kO~! z4v)7R=w=s-4s9MqBGE{)4x+HZMh7H)UAC6NY%_rKQkU@<{FFh&O-Bz~d!e6Hi%+bj)ZH23q zzt(L>?Qp`2-0OLRt7>I!t!T`N2#+REeK%&tmWn~obWGQf0Kowu9TV|1lJVVPLdh}z z@%asio}!~2RVVCVy*JnY*3!b_W3E;h2BXm@(AGxc0V{rs(I&E?+MxyLiu_Sr(zclq zl7L`8dSr6Gy37O#$f`nJpG_;5{t;-R>Tar^Xby7j)?56lbqYw`wle>;w*8M#uGsFm z3suQ6dVftMH6^S?oe6zWr)r(yacVViZx@$1p*R-%&k|u0#D6a_wfBwuQ5vJHt+ObN zvg+!ufVpog*!>k1Y_heh`%=+%HU6A!w?JJu{EL>V4`Wg3@d_ZM%ZLg(RBG7QeP;fX zU?M19*F9I^1GuQhBiCL6a%;Z21VjBE`#yPYeXm^?)B%ZuBKq;3)q_1y2-3S6?x!?V zCI<;L?<5or1~g~0K#;c1nuw-YRp`V+mFEK*2XmWZ8h^0BrmFffq5Pcx*nk{j19YXN z*Ty{ldOd8?2y@1$*Rk06UTr!^t)n-0JuWF99_sKC5EZ}s$+`a~VwwXe46E6@- z=)^|Y57p)#h-q(~nnM|OqZ9a-74j}_`gAK*y48Da^_KfT&-GrpX?ud^7GVt5R>)o( zwWpXTPb$?i%*7r5weMEvVQ$vb+vhyXpiZg9+RZ+Y6ekWv>6JzG-}0cgKS?xM)-hTn zQsMbD6tUf|Aa9uVv$e^+X`CQseNUYo8dJ7A6*Q@DS|qbB6up?&Koql1;W|C_-DGU4Bf2v+ph!{vT?Mb23VlBY7ryvtsk*4H8}O?A8(+A?$HWR zFqR8*Zsnys4jLc~I?EQO9uY{fg7NAjcW075=auhKdTN!O1zELpjueYXa5j=!Yz#==uBAJ{!`8F zDO!3nHu9~0h1$5^i8K@>J~;9jqusfmUQL45h3+CL%Kr9=KFql+N9Ti14@#|+{N}f= z#G3&u%xAEU+dMmAcul|Q*5Qv3GKDgz=EGg0u?&kUs~WZ=NX_!dY8;xD7*$SJ>=;|9 zQ_&Avt5nuMJ1sZ#=I1h#k!0&({8`F7&PVKPn`!Wa**&z_ln0Hx+I&f3=r*T)Y0eA+7$5s&+p@fT*$E4Az$;@|q_B*6mM z;V-x4h|a3YeViJP=gkV9>Ew%Fy3zC;nz8!x^Yig57Zrr4S-*V!Y6sO8+6z)=T!Wyc zDh~7~?-n zmFm>}sx+sPe)fy1+Nhq*v&#ZH>FuYxZ`NY5n%P#MP!@k+6`eB=2$*n(Vs6#p z6B@@> zHhTYKldUq#NJQCtdl2489;J+gviHtj5tSq*dps(uBpKN&k?j!?S=p)Vne{s#z3=z? z`})&g_j7;lbIx_HbIx_b*fMos9wYTSBGk!Z4Ali2`WHPdTRmqh7Cg+toLd)vdCGZx z$qnD<+>4jI>2E5~Hq3zF1pdcv@G7RZR(dQ`goTbPO<7dTTT6|!HT8s>;P{V$r;f-b z1RtZlY4f}aoR@XjE<_;57;lv9-tz zLPNbu1m)+xWU0ZSp`uG9zx>6RypB|!?q;PXB^r3x-%@<{!}-v5F+f#W#zt-$l7pjJIiM{?Ca;O>MJ?qdP;ao(Ky}x94XBtlVdM^Ib`- z#R)2DOZwwOr49>SJ&f(T++hTesTabbg_ zBA-e#@_mck-5RaA0&s~2F%$SNc48ehwT$=b3(J*p=S-{i}`*67+m=uAb|M<8b#}xsty;o3wwj4T^|@eC?7NvDd|>*ZzPgTE1$AU&EiC~_F3V=rEFsS@ND#;{zAq{g zqwH*}loqK2om#`|*Tvt4YC}WR`!SVsQ#A_8G05^4)(gL%MitTE&JnlarC9i$+PD!g z-dv2m-0`zQU^d6@smUgVM&v@f_cBhRA#g8SsN;El5;%tc%~nrNVx>UlgVOVhulXcY zDe5)KL!b|Ia5KUlL}{6gj8(WT4B1(No_4$N39^|Kr-F2gI+&LjUh76QkQ8lg+r|ZC z&~z35BZj0Dc0?4HrbrdOHp`l z*dHpnk`c$gSA4XZs$u|{VTL{5LVGJf?qdd2V(+F&}X8w^r2fUVqD ze1l*!Y9^n_WhqSOA7)osC|=#EvAb*XdMcHCJQbV)-2)8M)DXxQjg&q=4nFar1~(It zVWK!?oTX{E6}e`PAbiG-cQ^0LFsUE!Bi(5qN>cwpON3yl|JM<4p-_cMBSfgx#%#1q z>CYUu&g+|(pm?6XbNusE==+xJq2w?1?#sK`N5K3Ol0|tenaf=t?&Fb&%lYcdR|8uC-u2L&9CU%>Yj#&*kt(aJ8~dS5 zB*C+&HtT<(etJ;*IrT3uWOK5bj>+l-s~4=%Z>|fB!uHkzx;5_cbE66%UQZMljXA&VsW& z?)(8IuN#zxs@tkgQtqKoh^2*cstb~LFW&(v-P{VjJG>%MAmVc$>N@2?dwvNA%=koT z;~flo;!iKY%SHsv{mkmD&-_nTyrpoJwy{czEG4)xo=@)-d$?(#fDHSvMiUJKrXPCj zqD&SmA{*S@3!a>vsu2BSBXgp1Uf`oR1%iV)g8znp)r#y`1#Zcu9dRQx=1*xLcMKN; z7g)1(gA4YDhf#SR#UE+!)Gm$7Zf$hGES!C;J8hrN55sS-n~>1Z?mJfFw{CB6K#tFa zshh#{CzZ?s&JwIFhezz^tKTbEMr~-Q%ZPjERQK_rql8+h!|&#s;W?2e1t1Gr=e0X_ z!_Q6)ivDSWFFif@0JJ10$q_8U|2|Fn!Ek>O=5iB(^I@zwry_p6T9KviksL=H!wJEv zc!`gT*OoMZP(p>GfNV57pOef9)?S{|<={~ScMhc+41~|5h)gRE3{%Y{Q0JmliI=Fk z+xEpGY3*L(WkTBL_XU}0+%U>UZ&$qm~tIA zu79$6OVE`bj9gWU{%Bke$3`+=Q-D{Hk-?w27csX)m_2_RL^fOfmY=P)zlD8NNRxbf zvYGnoQ}V(g$gxZcX&#Vem*6)$+oo|onO;S*i%2i;5ihNT4!7hSt#JS4y8%DUoM3U$+qav6 zuw7t;`Dg6NkvXXHA(RCi>|wOu?6!O4tJvm2__I{%!$?PJEfpX66do zn@ld|^S=pq0XZe+MbYxXSo7rad8&yl!$9-N1K*LKW2?6me8+;@Ic&HHZGvfzZt3i* z085S_h5B8MEX5j6g7K z#($!bUpSmV2jXDL-88$4)L#a_eTxFLz-@A9UArgjUy|;{hRP(*UAZh3Y2};Cbmf6H z&0cIb!B1nY!U0dW?V9%FG+#xNE+2*TFO4BZ#4|lFe|XN1(kR3&G0kLNQc1D0eZgsb z_(J?uFg(NsSDyGPuC{nT#a&>L`sqQwcT^nl5 zcYlVd9!pvJXuw6XdE{sd-6!3;BQ$iJ=&$uu={MV#V$awsjV?`G^Hh+6&DnL|drKH;a`5R61H{l?i8 z^@LSOxGLXa<5#!HJoFz6Y)ntP=Ydr9uXeq_a+cGCrmekRL5INZayplcqM{cy`ohiG z=Z}?qfqZb)L}CFtO9M%AAN~z$>UnOxB@pwC(%9x`7niWcq}x3l{apg~a%plF5G=UB zd+5(-DXZ_ptaS#F1h`F!=b6O9_Ykj^2(@i7PpOkDsuKFbX1iUBC%!;P%(lY3Uc%0C zQ0MUpIy{_LYgg+`ygbbGM~#SCOH?U9$o5P5%&SXL^(|N)Q$pR467XS#WNdG z9#sNuER;xyJtQ2sTq=q!X9Y=0``t?lrF~)k-XZm1yLCrs|9(FGpIx}-U0*r&>+|%% zr$~!gJA6;h1bzSK};>ix6-jnu&dyz$^Kk|IcAi z4KE;A!>0e(q#_h*geWjbNrdFaLjw?Fe@E9Nuokzdw8Yu}J#O5nR~VddL*QgX(X}#Y5u7ojH$KOfW-WLPhv)-?`)Tg; zJ*tB)e}#H=Tl02>K7QNR3cCE^@w^~4Z$C$^f0k$A;2Y zG-`mZ^emsZjCXaakLfla{chFiUm^Z+`OGrM^3yG+UAgSvJL&{Dq|?(#zxKq8xe!)M zDWRwW8zE&cLfnZq&P;ggJQUYSzQ`-e8f`)}_pX}O$0+5pM71e<0Z$fScFq3PzGd_$ zkHY7%j>eOmYn--B46&}lz8U`ZYogz=<+tHtReE+-z+_7_wh9Kw=V6Djo$B`BKoeaU7D7`nv%Zj0#&O)ojA#REF3` z8FZTP)y6MpMes z?+?d=3t?)x>Eq1FVo4W&l@fRuUB70gaIUE<(d9v{aU@CHQycQD~^ z+v%RCNV~lMVQ{CbW#@uYN>K$DR)Q{0x2jn~wav-J$-Ny9iIL&PHt&39F$Ns4eUu#L}OrtozAA$S1m}>zOjk z_*UaqRx%}$HVNDmq@W5N&`=dvfR&~OC zuV9;!$ESHM%8vs|5W5$oPP0dAyaPWIVMZorqrm<;P$jx3v(yjKY4Hz~DkMpaEH3I$ zOS>b*z^8U4Rg!T2v}@Xv>YvSxQ?mR!MG`!XJ$LO1X5B~88kRx`Wvh4uP$hp+4f-$# zEPd*$mf!eDnsj%f*jjd zpT6VH$q83r7J4t!_bf2JyLs8^lejzvlP*y41#XU6G53N}8Sy7Pac6#LI~vlHzkHVR zB}JQFy)x;yw>{n?xlt%`9at6d&1OyJJaMu-#Y~B=MXVw%m zSr*8_fEagy`N7(Vawr#D?3(~4{E8u)(jMDRTYq8`2!ta4)7I0lXknSGmUof*PJgHe z3>g2*a%qh%vTfgJQ!Hz!uksdoyUJP?=#Xm=A?4?+SN6m6Rg$DDzi;}5f0US>IXtW# z;J4+liMDNn;@!9>?i%2p)>;Bu_x**V5-KusuF~uOXc+@HlCIr_<~;tE$lph~d{KdY z@zd+&TMBj}#sWm_&P_kxu^k)-2hyc_LOH6qmX;EJYe|>%EfGqG0P*w`65`LMx*yH2 zxwJa0H*Q%4ux3;lBPCDNpb_RS9mbH`^SV_3y29HSfZqIdi->pc?%gM1t}Vkw-_Qy^ zbiS@;X#&fX)$MW5u?@sS$L@>~SDu+YY2=~^vsLRj>-kOz6)?Oa)bIjnhInT&K$;a$ zmbQ<3W@cj20B$B%DAGQWTmWwBVJ`Ge#=3fr3n!LgpNuz8CoDSFpK^xo++75*M|V8T zjz%TeD0^y>DpemHOWxZ(y>G!dvGp^t@(%ngd||>ZFGgg`TH)tq&yJ{N%U!X3F8Fwz zK?u(2FOtS6Pq)j%Q-ls_B@(iQwANKOLw$CTThuBJErJ=vQKbRQj(Tf2;$Z1Qgx zGS%%eLA(57OJ-j(Xl#=U4RJ#?jJi!#%O3=GW1Rir2`@H}eO%vCN554vZ0Uk;g}Xq# zrYUt(nbzS1O=N+Br-@YQ#%J%nhq5}5cfN!6r(dD|u&7K_r2gTOBs!_0ELP|Z@^_1k z7=plIhqlFT2yAp-L}X?w?en6I@u z9RSt*;rF`HUy(LQvM>t^22PUu&$&k@1bDPa{ zL=g2sqge6;XlmW#6{(l)Z0A?!9^1z&JiGj-pO`o03~V~Le@~hK=;Re0L4L>kf&`9k;sc^4?MyF-d6*gVHBU@-U zWp<_S+9}S>Kk$p8LU8QhKm4X371&wcGpgrk=MOstc2$fg2jM#eXr<8Ty~hJvS%%SX zw(J7v%{E|FhV5hYtEf;5WPsohd!G+f9t@b{u~P5*hf7!>euXdl(_grme4<{pPcbRdy0qlN zXdQ9)XJTNKc`ZlmfU@v^@GhZ#Z%V-*JmVHte&<|$<5WVV(FJrbZHUXjYP3 z+56{6EDeKLuS+*7#`9WYU-Qz|H`*#`Ul^QI;oe*LoEydfgo+!wxHb--g9&%^sr}a{ zz!UM=7^Y`MI$5JNK1AkS?K-~dK~_kFL~WJ__&0C?!5rr#)Hul`rl# z3^P2B-eul~*bjR8ea-3n?jEBV>PZQNNFOAj7+OmlW*@H%`aPi2?ncPA^IJ=SK{>iE z^|qG#we+1<-wk+c%bC}De@H+$P$6U2a$+baUo~R1LXCq1EMNN< zyeQOHgS!&jOhr)amzYtnu@IMW*X5(uu1FAz*~f%t(vTtB?DpqHvW);m+`?&OXze>V zgsoun*YDq*tRJr^11|);$8s8(kN}&a&oz}g*aUS*0c=t?bjKC`()lEIVjH@<1K5P@ z92~TxDUCmP$5u{{W=ry7@2oSgF8#_Bgw{0^O8YZl;7`Uiv>5z8?j_pvhEr)ucasWz zs3G)W{x6g&fhol6M@a1}j6OnxWTLV>t?OYNk=d8aCMAR4u&{DDzf1Lx>BBSB)Ml&J z3T(k_F`t5?f*D51F@j<;uy2cT8Y@(ifYTUvhF-m5rdqoQ_#O@3>_}oH?>(3W7)HT& zn;6-tsm;@B1+eRmGKg(19_8mxd>JMjYN!?WO1YG?F%WVJEFnRX+^Mjrh+&}MhKFG8 z7fUfFm7~fgwG<{++;v$LmV38$AYJ5oKgk5d6$>ByFuz4M(R+H;T!pb@esb(lF_UD= zW0M&Zy<5ZP1X%t^m@tXT$J1=DEcyOpAr;y$asgerVvG<|eot4pMN)dY;>0`9 za0{&p;J5_QiO9Izs|$-VX5eYtu zfWEx1C0*0OLynKS6Uq~j%k5jIY9)f;_pEh2^N|>hF_?rDvT0VeXd_-y<~xucoqsb7 z3GT)3vs}^J1JIUjKv@3vho@YXfvvzmYQUhWxa53XXCJ(phq8zWQ#9QnI}Y$$mGqRr z3&%gcKl=qU=*z*$DeTbX*{S(Yd4Eoq@M^OOUFQMei!-bE{P_2Dzf?xRG+fDXdJrf} z@^@kcx)f3>As2Gz}vi3u)-3L*%p27EAden%|Ra&hrAAJ-FhSA;e6Z z2;08A#p-E&l>{MY4V8;?Q*#IrtT6cJlV?g| zUs;A6S`OLr>n*+Ax2Wax!)NjAM=dAXrOAJ}DC!@caFDo9p7{|su70gNQNEF7HS6B4 ze{@RdD_~ZcYElw5ccD9cBak^a*4iGaw}Q2YA3(bpO^w>qL6|-qJR9Waps(_<$M)EN zE<;MR@YeL})wh?TA*n=Hu1}fwZ@vkq~nDh<&m-QvMABrD*20 znZf!~Ff~j}L$rxHI3r;qqz_V6Ok~Zm*Q#uJCZxuen7P}#9uV6}fr=05YGrNXGmSZ; zIw|<-S!reW#6@b7kW*qZRE#`yq_qnQaiYO1i!dy33%~^r?l#MCcC53!&-ZjWD{;wa z^$>N#S+zR56;)`KU5g?Wi7TYqj5diHtM)A>siW}J?Jpv^jPajNFDpIeH3Z#YlFv!_ zT8)fmGHWmNuKfCe>ThD4ir&L^cl-L#=A^PKPcXL^;umjI+{?ODLH4Q{5%>_q;;P)= zEzss~fgtOUnBw)iKTa;kEC(jU7+V%L=}sed-cFreJw2!iRX50tUpNwgaIUJ@a3@jIq7r2=iH(4V*m z6?9MF-S!1WADvsi*RJ_SY;Iz5OC^A=?g2;BCsGdhQvH6Am2OU0tsk{d$f(F4p>QEK zr%&#|wncITZIuRJk#cQ8K2b$gte!bYQmghP#XDUScVIvL<(Q)&2lvJN50W}Op_Ow?lxQQop$g!5Zvdl zy(fHw9GS^Z61pnua;d7eHfZdCcJ})tZxD7j>rQ!V7ohe`tXwL%D~x&a5z}#Eal#u3 z-G-fUL+bZXkuHwcpQ(JfN{^-Ur^B|*7UA)7r7?rB&ph}JYz8`aQ zExxcWll7|@YF))W5ml?p5d5>J#t10Lc~@{E^wXNBS!6u}62om*S$( z{r&x*!*rF37hg^z;$Eo=zHfwj+iJT_#1$n&O4r1-6 z_l5d+W(OBkFchOnWD8Bc!HixuF^mW@&kb3)n#M9r!x-~~iHIGWtXh_KQw*X9eou%F zK53~xFMEul*HL3W<)=oy-yWHjDM7WU0BXotPb{7;TYEqmwNxy+P?3?6?)uD6Ant}@F29N{oQr$Rj~+)&jaHb zDjDy=*6xWv>pumzba4JqawZ|fXin`{&z=ON5iTx|IbO72R~Lq6urD3Uo!&-NTBh~F zXN`jBU%_~c=W&|6m6JIPCM9B{s2(ZG$cTJJFjlGGtn@Zu+Bq5aEaA9E9~?65)6buY zg^wpJ(=My*+Hatd*~?ehG<^?q-AZRsS~QyDlgkZmM5rUvBz|xtV^Ljc(K`~8-fo^B zS<@lE*f1@0(z90n;_~x@y_$Vrsg%~omk=RMTMK!sr(~U$x!PSuH4uTw-l73yKe{}?65SoyqxT=l2X3xB8V|XOCZnW zTzmve-UW)4=!uAW^<1Pz4CA2QgHm2l?THf_mWH-(>rjuz?}djyV|jTwKf>t|G7|f< zku2?L$Y=MMM$p&PNIi&f=hsyN|Lv-soBzb?3*A^6c}C6<(X4ndx| zLk$v|I%dTYgmuM$lpQxx@bSevjxq0Na}TLHP{RWpD8vfi=UI!SO@ZQFsn0w{lkLVF zh{8j)3YcF=$c!Ge#CqJn4b`{Gho3BTV1xW!2*|2Z9x}j>#4B=bgiGOXE8`kXX^*fgU$g zyQn##R0;86?zDx;1sz+9Dl2Z5&fUByURneLjmUB4(L=(J9HIE(^(Rrbnc%e^&E%Oz z9vI#2oJmyG#a)N|K=NJowe2LL+%~9t2KvuDuf6cJBPut_Lj&_o<~7F7X)O4q#`Vk1 z|Mdcx&J6Ye6aEi54T6<*uw}Fo2F^nk9G&s65g)GFqq-I6J+B1?DL>-)=%573afrZN z>D&H9dmc}tprGOzGK3ZaD*{7vPL7a_=C=DNJVHA#s1bqFVV_7Y>)r9PuOnB&g-b~b zDagp6BlL{Bym&>JJhFd@S7CAHsSlHzW{>nbjpdoOOiGw#&}sEBC+y2p>uR{$ia2=y zup0<}uJBVPLDFTDdH$BmvD`bp)UG+HW3RaXR^mIM5|6)(LIcz4Un_WDgNfL!Pg_CN z9B(TV;k+hV1^gaPNe&n1D)YCke$yfgJhapH*^~$#w&Paa1O==`HH?l#4=JM6B1syD z)5D@oiCSz9CKUtiRwN&*zl zs=aU&=#Gx2yu0us*rroQe6MSXznvY1{+D++M#9eoA#!L639*Ek5Rv!?@FQgnlklhB z}0qf#}Gm)QRy&N5G6_@cdpMbD_x>#;m1^J?|Q zb)UeJW$t#U1wPG|u{nR}70zy2<6Z{LP@Lt3`taap7Gs6(d@bNyl^ABmX+=>8$WVf^ zO|qGA)A?p(4=?r^NM@)kk74l!3UTq-JIx@x7_jv{Qk0p>cG?|KJCae>XkQ?c~`n>6r@95<=qewUe`%JRURuRV?$i>%vU}uX~09s;W3sB5<<+4 zwR<=9`k{5c6MDPxV!#6lQPeB}TcR9OMuOHOmTW3CI!bR9uw>Hq5fYTAFkOdvJ2hL2 zGD{DOT;oxuqD)qRcCwDrfKuCF3LTcb=f8PFQTF2A`(bGSrW5^duA_nRdXdI@&0?T# z+L8E>MbSf`y3K_7f`U0k>g31K?;{&C-{oBHVj*yP=Km-?EN*6)a5nWv4$b7U45~mR z2~J`5Hh%+(REtq3)>5^sTFtCmd3voG!J04l){Hj@eK)w9h37IaS0j5@-H0m# zc7QZQ2+KCF1YYdf;wyO|+3{$nvY;DUs++J%2pffR^2awmia>kZ2moB2-X?jofZ8LP zJn*SxLq0WO$YGS=EP3ZIR<8P#>{a|`1Qn>`@TntgS%!5Rku|(pb}O~9-F3SM$|q=U z=qC1Bis@BF@PK*=FJFZ5wD|csVJs5_GplH|5tOzK6~kshyG<%R-Z(u>zo9zaH8_*^ z@6}RCBWdR8!+{$$hH*O(cURHdJx_X!{0y#p^u9||U6cGBSg(%wogN6OVC<0OTD#g$ zTr5S7px0WGfp2z(l=0X9cdemqR+B+E4wPDoaKWR*5*J&9bZKoTYZp8a3t)vy!(~z0 za3*H{O5q8hZ=hAIJh_io*#kOMhVI&&K!F4W3-6?<`j?J|P$`dVp`Vk7gI_9&aR$P- zIiBf0GlXE$P>AGf&G@gWh1yIk`-rD|@z2%dOupNa38E9en~)FAA^t|eo;!7-gUVc0 zKDBi;T}Y4z9}|Kb0oghm6%ZGy0uJ|TJ9YprI2U%e&qX?3<1Kl>AmK5W@VD84M8a#V zW#&E%&nHhb@_=?i6S%0%FrL`xWy!bMmssvu{U+~IN2pfaH6nSzKYS6Zo`=3o8PKNj zxa}_)m%VX5;P_*jtH3|Hxx3#=B?uXk`Il9aK+|GMG(b=-J%cv3U5S0A(P$XXW~P*-)jk7JmXo1cN*aNTPVf^aa(}MmUg=C+WfazLV(i)fYHX1Ggt*pd?d@OnY`^T^I<`wi{Jmbg;l z*LozYeySKbNm1C{IS<)816uY1W}9_m0Gv~?X;jWclh5a?AyvPc%O;b6DW7DD0JLU) z|KUV5di@u^3mI{D7=N#8UZkeR(8|U^c!PD{RXRsm=5ex02sSwxMDjzvq84{~HjrD5 zt@uX=@F9EVm*)EmQ;Xb6XHR*t#(e6J#=L=AU4nIKpH3pxRbW=YHN2VW5Cl8Z#K?KI z_-!5rH2c_YsSwx?QM+kTf2+v!*DeRV;Qcm9#I}ED`PDgOGggHi!rI4YK7DlX7(7i1 ze!EAXDWL~dTsmAG8YXOWS}mV%dqrBU9zci%pL-l)8CxeT4&QFO z><$RQIGlGo*k3$w#`<646YvrlAf1Pe(d6LI>iwWFd=e~9V(yKP5R{>!zN@4^`h@1) z(9+0u27JS#Hpv_4p~;5L`i~S_lVc8K&`VWHHuKKlrR`IBN`_RnP>%l2fyAr!=N_mM zL;m$L-mQal>#&iTraYWY+PR}v4emDpV3{IY-qnS>M;Zy|<7S1SF)gJ}aPi8ASGk`( z<)Q&$T2Ph5iA?0r!gR_R^C}uJ#eg9t^x7n5wC$Z-cwZ&b{X&UfHc67y}AlyD})v-8^~BZD+%Y zXz(Zq9f_Wx*#VJQtme@~V4c9mgqS*h{Q4ze>O1{x(3{*)se-23cQyN*_8agMR5I9e zC^mz4>v8SjtE4a@r&)LrbgB)j-O9q8X>CakM0j=Rwk)_FIbCsM)#NXTJiOo2YLw~Z z)W^r4i;YiSs}`z;L*vxksS_MzGoxMIjKn*JxRAZ?hv$YtHv5mI#6_*kpr-(*)4vFJ zj61@bc-S5g8%>iLes+Jr4dcQHhG?*(mdyiGJVMR_x#?>qD%U|#rM$@M? z@)S_nW$Bw&_3h-+jCTm$#P7&J*;k06Y4BUUKa0K--{D0Lw}~npMyt z>rh0EI`bh^u{@t@f2+*r3}izvKZxM0+~34Iz!q{>UnBVr)>FvdIC{UUFo6>;C6wft zOM3M)YHAOl2OoN!gux{|UVjG)JQEg4H}wY}1JB+8YHu$69oKBR1*HA2^f@W88;C6u z`8+mZ`=F5Bn>TyBlz~_6*-Eg^14`LTuimA}zKJznBls$ZDR7hW!T(bIf zg)xE*sesER0xpDFUSRHrk3bN+%O~;W@=Ww{@0+}sHWnOktiJ`$;!k6y3B(qwEB8{L z+C3^!fCMre3M@M8A-o`n!WPWPyOlK((9PcvP>Po3RgdeKgXqlUix{N`4d&yl4JV4M zYf4LxLp~fq9X|}rX!e&rA!TpGs~tz02!+!=J1Z-m0@E7%j=YC}*GW_#=H2n2oC*@v zZe6VfFqH*SLz8yw}v*n^{xhrF(Fr+OZ;3_1z$p+nPh zZa@h8%v^qTiGb;QD*e^AQVz^F3eoJdGwXZtn~ajKuU?K)V+JUke+i+`kwxX-%Y1=C z1<`bgDXbxB*zkjayXbXFa1Pw6;03vaX)8fdhX;s&aHKl{k_F{lGht`+x_vu4Ni7CV((dnQ^nWWN_zBh4pM)Nliz9+sWm6wp%*S`Lb}EPR z=H75+0Y}Tz&5~!O_iXsBTNn8pVMDaOx~oN9MHx0L)dAgQWC2~gejf|baV;tW#P~cs z)Myk9NbB>3sRC4_76o{w<4zs#Ac~W-e}J^>sIej;f1p&=3<{aCW|yHvkp-!PtE>4# zf{-@bDGWt&@@5pM6T|fp zY4jRp%Xe7tw<`w9fl83m8(v<1r}A6Y!|9b}0W~7gI?5E)R{S2`UnCSPlTGexqJ=wG zwI&oR@{Ni>E`c0xFEuD=2(h=)5p{?{1kQAN5+L)itM)iL=Now6&h^qWNK2oR;Vzgi zWfMpD3^`a?sjHoTfbYmOgC*NLuqU8!F3`f?UtVcpdd~R8g=NTgG4U)!1fBNPT1>F)YQy39 z)ev|$j1CZLRFZwCiKxXe=W_uUV0tgDZCsx!hE-ggB)ot#D+U8z}!vV zUlOD4`~Ab`;^6_gW;3HJ`U&Tsl{V#z5*C_ByaYc#>PzG(T(s6WGh;m#8FB==KpQFH zh@FYh33}L~*Pf0JVa-slw04_HUH{U-Gy_9M(hk>t`cm&b?e2ylPr!3+UGkhg9x6nauknmndc&Zfkd#Q#Ag>u-Z6yJM=kqrcXbS*oi$DNbedC=z zT_S2~hyGLGk}bD{ZZy65wk>cl*f-GCLS5)@>Extih&*duViRs6a2n@v(OSCbAPg!T z_}xOY4r)HGlzZLWY(ujFu(tEdR^thXWw)RN1GtKsZLQB%!u?0yjRhAVNOwU(bC~!-x|sdQTj+(&ZqHc66q$lOeWfM}{O+C&W7N zI4Z_%$YOv_5(1{TKGj|DnZsF-rxK9V2$_biO#~y^#b)jh(kBdN&$D?H!Py%Lvr~~a zC*7s6`5go0;RfGhqPj;DO*j@|g2%ccRZQX>kpr=fO7Jv$GdL<;q_nt>K%x_DA7+NU z28V|w`t?u!#BA7y2({E`te<@~Vf59dYwgOj0Ns|Lb9lzC-+Fiad^Tmuv?)-^;5W*_ z-vajzX{~4nzHpZvN=J;N+YNJ$Fn|gA+Wn*T3*+{i-Z+T-XnxSnvcH7Rq4S4iOgF6GUw+SY) zHSP){IS?1_w9kV){M9LilXFJ^pFB~F-ET7v@wU!;;2n;HWKa9I7yb=MK+0j?4NUN> zQ3-NYLyOV)M7WgdPOc=O04?g5+EPGv-h5MEN)c4}7k5OkvEV_09q8j-zG-1k734GQ1h#=e*1P)dWMF-IodWrmB_EM(o>l89krtRMj(H0w;5aXpCFi1qPBzXD71jt`L4R;vo323mIttiRL@)}1F`h`r|2A` zIN2eqRTR4wHbXjS%XhHz-PN}G<|Rn-l6wgO0y09)LxN7?!#*iqqLfkyru@7LO>@q| z+(MTyVcO%_7{DX*xAg7K1k_MzHI}kd5XU~uqo;cA)hVXHxvDnRaRmR*k?LB|e7*;sI9hxz~xpL5R zfg8SY0SO^Ni5=71a-jPVr4g2)>cc zBJqmUtU)p9!pqu=SD^oG{2b2Bp2;L)mdOjMZc<(Psy=e~;}uMvr~pA!TjIcGg7}_( zQxGLystX1;S8GQmt91XGHl(MxQ)9=6$kRAco58^8qKCLKWp@7%A+EkGFW*Bj2mL9~ zOtwGWGZorqyMmPHNL?a|N8jO;pAlC7&{({sLZP8QMKb(zKKOD_2YP+wl@ZY_O8J2M z^ud$wuwmG8ecf4DuU;vwmQKpKau=Q+JE>A5kqYr(iKworaZ<27 zPul!dn`Ee7Noloh{R`Uv_Tn=9c+x~I84F_1R-lhfgwIZzTn+{(_YAhqt`p+CQb%5d zgD|0IBMo_J}qH$-FJLYWx$U^oNf$EcE7GSxb>?{rPt-2)x_t zX}Yf_PgD?>s4PtSGl~h%hUOYCd?J6Ed3NSICc%gOGS4jIpZ1+1{pkan#r>P7s|twR z%$P}AY+s!cqc`e2Phgjqk0JgiB$ovfe(c^Hqk#7P&>sXHi8`*6R@5Fh{co|gv)12d ztkRH{qAzUiJF-ML`jIF9PR8@Iy%fb~)!jE5?JBZ*mbyFEs@~3zde_l1V@po9%wDST zUI?5`=N=%{IyMU$PU6dd0GIZwVKjp_LAyl^o*bD=&izm_ELS5N0TL3=JVd_!V9Y61 zg%Hc3Qw|cp-Ogp&=u&ihPL~!c-OreaT6lMHe5@yl8eh1vQ~<3+(@pJupI7AF!QNW0 zZqu>!Ui;F@p%-@9#0vcGrJ#G@_TBuJD_?!4Y}Gh#s>|oHTv0LocqN5^E$j_$EcK$= zpP8x)Refu{k>%R3EP(4h#*NB3d}C`vou(E#isIVA>9Ox}23|4Dyc~gM4T2&i^z*Y+ z47f|IzlacpD%+rw3CUF0$=9|k7!LS+S`h4H%YWTqw`rb&U4!xp(k;^fWJhZJ4HUl1 zs*CUYFgmo*2C$5k$ebp!2)S%)+O8ur_R__Tku zJn$){nZ@gVg7=Ou|Bfn{sC_l2h+ry!y_{?KW_KzdD|&c&T;e745e+P-F=@vjEmTM$ zqh&Xdtm{vVe#_AK+T?Z?t!Rm!g$3?Bv_AHekhUa=$~sE1$vUc0IiF5wJwHOB@Ya8G zAq(;zH=J7Cw2(IVn=D)h9_K zYK-2td74wz=nbX=Kj=s*O=!7s+dYv`je$r=(TTW%y^(K;RxBlUf&?nN$ZlI^qO=VG|VNEG(^dK^?gJs@t zySS(^WVM$R&Mb`9oYOcFx5$R!5BN083q78?qA#`flmdOWmNJbsajc>(TR{5U={e^c zn376_Pl$@5d`I$NYJ}{!UYRzH{T@ImYw*d|$ytp{)#;{M2Z{U5Xl%o2y-T>SsPoGy z_i#rPNTo#YLI&W9UgNPaIx3Zrn__5G%LT0g5y$0kx33XkY0+Z^ zpJg@EeDqR}s2f`R|Ime5-UuSmnl_<`Xmv?B5#OFABlQs+!2a*sRckLe_V_wyJ$gvf z=fR$#86b25#~5{&1bHwnuOyx2e6JQJNLl!_g`fAlhtRDvHIJU}cHT`KZC7>Z6W`6) zuqp?y<*J<5nlbG6xe&~E^d-*0OLYAZ(j*eD+Z!VY={`34Ya_V9#+i!@IQ9oXNCXKd ziwDfgkM(RNvjMTH1VXM+=dk(9K8|j40ujfqFg7Z-s9afn&%AX6|5m2z(c&N0MWXIg zT@C?1C-4&^S*;w{Ba%hK;RM2K{G@NxI8sj%NZuFE2iNGJ@itYrl)sBW%ef_${AU$b{s%gJYTG=oz z`;<605?1FT{E9wH3K(U;%azcii>+&WY?pT*X1%@Xw zY<(B=gUXegY}-R|c#?~(&KJaBP?ysK-Y^v(lJU}o1y(j zF)kZ){G2B5{TT@RP*KlQW!bc#{F$zU>48~E0lKs|0}d(z_p0Mf*cJUw$4BE4Qe5zc zX?^hn#Sze7Yl;f%{m9DK^JbSRk?2=Z96}7+My_X?99g52q{(h)C+`tD&ThT zMX1-FRuf8m45IIAD&Vg@BO)J%kv_L_v%W}EHPYo$#RXQfCvHpXbB&ek${l$6{;D|U z+DVTq_V?$sA$BQ!r6rWS@Au;H120lpx^(*}Y}|Uyto5!rwbQ`qxHtIT84wIjI5OY@;3ZKk-Co#PjDj=>^D;E zla|aHjtYuS`yWwUSJ#?2S#Z2=37sJn8!98JDSaZ3zgZW%T|$ltyF21ex|_N`o`h+Y z2Jl`N;ZV0nw1i(C#P0LfJTW;7XWJ0oIQ8z#{Td29ugz1$s`o2BV%xO0v-#GYBjLha zJ0RY#Prf*p^$s)hjY7%qAIqDr7P6@xks%vr{vT0a8VKe4z5k5Ks0@;Q$(kiu zGqP(W3So*CS(1d5Z6rG**^{*tVvuN|MY3;M8p#qWB3o0UWJ&hre;$3lzyAwwygG9~ z_c`Y}*SXHQ+ePAU-aI6@$N}scbc?oc*YB+3mu>-}_+U#M-@p;K=UEF!a}-=|K*B49 z^`7=^KkjAJnEZa51Yi3Y6Nok2UyCwLRrwe6=j}xg^PiCd;}_0tDXO|n3v2WVFIn$(b#h5!LFM$ zM#4$PUWG7$&^JxW=XA1Sw>3=%aZ5)}Ee`a!l-Xxa77&X{*;{7{KdQ5ll7PVZSs&+o(pWerlVzFOytN=s>t4{Y(oP}l<^R0b7YuCb z^WC-jy`_F?7f`7vP?$F2XBMB9`kgIbfi6MgnxOTvJQ`qDi~8Xmy1I40d4^B+Y(B8KQixLzdx=VD z*DT*NmQLr^6JW8<|Mx{Z4W8&BnhL76jTJ|T z5@_xizIC63S;WtkDfQef(#a7wk-8mF)lADgRX=c`G*j@< z>RR`ahylV0tP2RZ$}g8%J(s<%3=iDNr;hgSV6j8yJ8bb~jz@&@W?Zq-h8f=Qp5Ud| zalCJdZc1hB%g_2J=_AAw@Lf|sxa0{v)Scj=nG~_~9Gp~8=N&!CR_j+Wy9?xc@|!1j z(*Fc+s6yE-+anv|YbF)9Y&iu11{U{Ptp>->T(e_1JA7j&^(dM`()9JhA%$40%y=l> z&;v2#0ASK45Z3vKlxsn_mt|JEXKKZU;e+-d_fQ%#Z`O{xz7*)j%cUPJx?a=AATh%d zCLdTTk`KG321e%;Efb#~4h6 zXeQ<=RPHpb1U>pfimt7({QSgZl(ij!zeET}2}`Kd?YEysm9jt2Q5E@5WAoruqvw(X zG;k8N7fF@fQs5&d^dG;HE5hd+%a=Aw1_w^>INWDMCCZ1&k3JVwI(i!8<4dbY@jrU1 zSLS%eFNkib`S^1@Z_d#FR04c*fmm(*Uvfyz&0-_U3u&NNUFL@C!yYq-L!$T?Z||IB z>X!BhQxa8$ujm>m5S%?jo~d+;bT>gF&mX|K#!fk;xgY08Gw}^5q|)1-Y*QT#Odf#r z)|6t~D8lEbM&f9=(I@2>6wsrcm~Jgl5WdV#R-zTM&f1cv1DKG4)+{Rpikq#W{19Tc z>Bp9zA7tg7jRAr7w6%`Wl=b8uB(vF4f@p~rp(>V8M6qZRj!_Ao^(VX>$T4GIu2yoH zQ?~-*ikQxz!ukNnDqpM-&7Lul!*<47q0Z@}D~#+99JFvqCg z)M?mwhb%8qb*K}+ z%hy2Z@;9dO^w*3CqE+0h*J#TyY^nBmi*#&`HA2Ouw@H2}_ZpknnT^|?LSsC&8FRwn zM)FRdWUakK_MQ8Y2$g^rcGx3uDsn$mp;v-~-Tv;mSuH~1cjCU1trMLAZGpz*!2%@) zm(hDP9l2EF8Mg#?C%WVi&ShlR5&8D!pTnwVr-FaNJPG(0HTP84>8+toS2G8V-S4L| z;z82*s8_R52<%cv?P?|<#Fqc+x^CbZ4YD{*Y)#w%&y+-Iv5*s{QbmKibKe*IRZIwV zWOco3nqL@+4BHV#uI<+kTY*FY*0~E1FM^T|zVi1<#`4C&FMKjV(aT2YS60_p*`1Ff z!`lJLAK(pW3d09dw;Ai>4@QD}MW!T}&{VPv`L6T*UPCP7a32aEn8vHG?5&n|0gClh zjCRA+0;|H-ozw3!PB5hCae!pRVPR^h_U$RykT$`Gho+Nj4CRlR)BO%*_3&a;W=tK1 z=zk}VwH|T9yaGYLEpq#Tqo$gT;89?DA7Qr`E9q`HjxoUb_ zbI^6%lB0p?u?@O9L(f4L;d0-F5B2VuMWgZu{@qGsm}t-2%7%wEREW?YtR93+99ou; zu9eX}$J*~bj-BPtIwtO?qCR<5)8qri#g{1w6yo0$k?VrLCi}&N5w$8{Lmwk7QQzld zn_eF`-_7zQwCm7Kt-D8`FspvQk!=0_PxTwJF6)*6Xr}q=3fv`Ch5Y{V>Tp4+{lZr_K@Ib7eTamOncRq9xhj^-CYV)dww2?iITtM359+Atu+TP=(yYm3t{Ir7Tne z^kzR^H?8WYiB3dkWOYfwh-4b?0IjBFygxTHYXB&qM$UqW3U+eh;Z%i*s~>K5-Wt*X zH6mnXM2%`S9zJ(*`6qaWGaux;pT5Hq$P9%=fjeSj6=>>`%8;c+qL`63>4pv4LF9`6i;51Dm2J&(y#b zH_4Kfg=MsRrXn zND=h;^8Ru3KE>szBwzS}FBFpknq~7o0nLI!6R--%KC0}zv*OX%ri14LVw_MCtsqWY zN9UE~mC;IVnkR5+bl#UTAYgSHrY@0jJ`Vjg+tvh0#S($a7~UL)TWQ@I<4cUpk4qS^nz4iFOcx1 z;AKbu1JxHTp(Lvl8;c(Rk?gtmFWC{=cuQzpCtP!Nee4kkTL0{UqjEE5_0)N0`xNR# z0H!Z)800>V2(0s1{BFRK@e|1dj=Kmbp2r1Q%9MLG@bq|;5-A+oa?DMG5dFNFS&r8d zO=n%Hl7pf2oj#wXv;0FU$RS(^)ot@m{Rm#R1=j2`^lM*I8dY|JZN_m?W=VRB4Et?ds$2_@r$3NcuygE03JGNMXKGZ#&et zl$1>#TR3F;_=5}((n^evMr;Mj)!b!R-jqL^Ohafe89zIBxF6G^TpVfIp9E zJdy!`5O$|}Fnt*b@^swhp?2*06dFW1fT%l#706vGet;Xkw%5g_40+M@aqw=4fDJFHNypa|Q=<6pj^0es<@gFI@+=+dO;iEl$A6nNcrwjUoBx>dW;2n9hMCqiDK&0v=45TQKK*xB)O zq#<&=S_R9~R)5QETNUzW$aHDH%{L{GCUWit=;GPBE>c=9A)@X_4^t^wvYiVieLlzKO zj?-J9dTGg1rS4CAoCwMKLPCF1*n{t`GhkIWq?~=E=QF;6e{EP@Mo^B4_p=&*+>>qh z79DkYJRM!mw(CHI=jU~Nmwo8VwE(tB&r`QEAm^A=VOu1Av$y89jsdD|{dOFs=@y*0 z;SGt)rCrS9y8az97=+j)mLER=lWrqjM|qG%4HRcr`Y3=@DQ_w8cNbS6Bcy3VP=%pB zbblL}4}V^Qsdntu;>Rr{`3~oTQ+QW`N6Z=LYiuc!IMsuv+SSHiQ)K2vT{J&IrouL9 z0(>|kcpczFOU=4I7ZB+peX9ihUNI@hRn{;*?%`68J6mvK}-Y%A_@Ed(rf3 zSb7YCwwA+v0}%18`pN|qvK|1)ipb2Aj#rLP-sgiL-?N*%Nc#o`?uY&-y}E z`+VuC&9`mxmi5J6^|!3;J>-cd848K9K6ZTc5<1L0qNLng?zjrdGPDP*R3431?nA$2 zfu)?f+E@Amdm`7bFfGA?dSJf2NA0Ti_U8tNW@zaFtEAaku2!xlppd1hMHd+Y!A=$$ zPPvwzj=vc{>Hj_nz9OTTPrM-d70p40e!a~xd6NObwZB($0fK^AD}VfojDy3-wY{Wt zAkErnuQi{cxjJ^}iYD~$q|?U;Jx)4VE8mIUggLCyPFVc^xFGK{6uh)JaLpRHU|sra zp9vR${Hp|?pw4;{)ZyaTeiRB zVu5jBu`)gUGa4}<<_iWE4?h^?BiudO7(duVv5YGT?NT2bO~|k;Y4K4aM+f$=$xtOx z{z$P@gBI|w<9QC)-{FXLv1-{Xb-F0fug)6e41^KO)AV17l~}=R#m1C1a|r&E(XHlC zCYe30p8tFzR~q}8TIqZrU+yc9Rq=dv$W%T=7-GrCqjzNKFJ14pIYLcGb`C2la}lyla6*B-C`tX~E<~D2I=Ao5|1q z&_H>?CFo}=LNSMsvg5ln$8XVJ!$nJxCjX`NVGw6M>qFgC9P;_OwS0GVz(NuKxp!lK zUfMn_)NzAxH#wGixLX~ZYV_DrS&wM^*znjOWWmswlHaD z*g0C$N?dx>=RkGsl~HxZl;6PQ-BV#T0*-n!nTT+}*G(=MOASbd>$@~C0u3B*NY!wH z2=yQuJG82u#sNta6CWRLX9Woe!D$O+q<`}2czqbtX3(ujD29SeV~*%%A}?PJ-K@}I z{`e$geA?JFfh@uUZP}4>42h}ZlJwb(;>DYX3(*|PKGj|6Gj)dTg-3IX3B=rJfq<`I zUVA{Io|oeXp`iwvA$Vw`W=_RdA zDmhwz)Na(~pAIY^fS;vq{m@sHwv?78Mkq0X*Gh&^0{nhh^93|B6oiebq{y|O z)>$0JK0b6SV;qqH*Ram;By-H*j~{5<^jSVg3Q!|KwNdViLNiTQC{@!E!_T>Sd&EZ; zp$V(u9129f3Ef}lTId?Gg5I#&Gg=!QTs$0H!4QZ$;M7hv@T+_cZRniK$gmE?y5wN? zsrxYr*%B1GNp}QSGyB<1ve27z4kmO^t7`t}76#_xjM&FGfh77ECW~72XUTe?SD<4L z)!|rxi0W7;>aBImYhWDSE*um1CfG%Hw_QX!3D+FdG{>iY`YBQ{F{9{&Qgi2$}|GClBPE?3KS zj$BhD+Gv$#A5|J?;$w#WUR#XN$uBvYftO3>slfAzpNh@EO&x&d{}>sT++DiIgWOS95f=dqP`lZ8qkLC@H@Ev0=dMI- zv3LIJOn3o81xWL_-UQ=;5fr?!j~&PHBqvYReoJ2Hy7dR@lyg9<4Mzm02TNTc9=>_WA{)}vhCD(mOwJB6rK+EmtT=t}T_3Y*^)RhOUC(A7I zRD#FP6$Kp=9C4NQ)W)loCH@|EP;Dw}KjImx0C*O@g;l;?8XqYt<&>cnzvw1Y>NKR4 zPdUz4V7do?-aWbg1bvi`p@$9%6aU0GpB#CshyT+8*e%hk=)-}0h}~(FW8uAnvU$04 zrpuPgd3lU5JA0^oSGg+4k&go&i9CN=93QL8MIQzH!Qo^~rvu#splFAaP;fWeN$uG) zt20LOIGuR-BDH0QZx(z-fQ{S^7ZjB`&O=X&Ct%D9Ps;h%PI$tiKV5XW+gHm?dy4~V z-zX>#&LrM~duBdm#yV^Bzw@HlN4*O%t~JWayY~%DdS`2x`#3`Yp}e{X((;uHU{fkk zPdQ8Cq*&*du#Bsar_Xtogc6I?L_M^|D=?=Akv-Hh79T)je}aVBv29Ix4LI`8&99bC zLIL~kM)3rcSj#gCN++FbPWjiCzNF26|3RYQ=@Jag>4!o1|yvaNS>)7o|bze2nJY(2L5wp*1qCAZNi~vGBPqw?|re4@O|3e zBdYl|eRyHucHiZX{#%hpoDGYi4biyy`Lp~KkZJdZ$fZ!NptUzS4TxGiKMQ2<3#Z1ssW4FScdRJPA_bQMPweX=stx zIk$sqn>pg4HnXxa9L5I6b`Fkh7$Am50#te(6R=kq#J=b^h1^b4Ja05=(5e< z#O-t&nKKhNKxjW_XJ;qukK->{KmC-$90#=f?TmKN35hVAh{7G_Pe@9R9lGG*cvPOh z^d&6Qr}(RPTyL-8-@(q=WAu-S=dlO{Iv77B(@3^# zE;kOq1m;J&{j>rp7TB*#`X^CF2+Ok>c2V#VJ60xn^x4@zO9R6exTb(D()B=>d5dTc z{r2NlqP|s*(o2lR=0%?Y{@B4rI>P5dy4c}J!3<`k#ACm{DX330pD}=rPR3 zc!O3~p({YVEbGFb{&HL%4ti8E85-nE2yz4f$FRZe)^8sYnt5eL_Z{#sfo?8}jl#Vo z$jW8U@wjJ+VSkcd(Y7HISK$TLFDwC^wdKIDh&qv~4pVkIA2ksUEFHFN{PTt)!zwy) z8e^oK)ls;uDN2;HfAZrs!jVaia z&Bo7myLV5?Ld4JlWkJ*94A|3Bw5OSgk{@fl;yLoe|8y?%YF(=5NSw7bnb8GJ+=VpB zd7B6xVO#pkH0I>6j90s0OwI(Q?GRE44fAz{>6tJP+%>WF&zZKc%8}2MJfpulQ`nf| zWUr_hIodBnN-Geb{gRNxd+>b`uN|X}IuGKK4Qz7Yid}D@R`Sb%XtDUyxsB_$yFFh_w$xaTAGdKwzP@uf#c>BXUL3 z!9kkALB87b$*XIq7mP8vWPcAE=$T9Hn93AfMZfPRVXI?oHG+M#S)q=NpF(kEECiK~ zA8~WDC*)a3%-|Yp-B8`HJC|FppCjcOFV~rr%^Aq^YeQ>O@_xeq90G#zGag*Q5cIN6 zT-WA}e{)eMi&-VC7`Te7j|X|wWPuO~TUN>p$D9yM8sj7-Ejp^99ZG#@+)I|2(Zzt~ z+A&0pkB=|p>T4`L3mL4%uLbRv{`akE=(j#KE-VWePAiv|jX{Pz)5aM(U;NaK`xJsy z#4tbYJW&NBC7>oM?@LAbNDzwb^}HMfWz3pna?6Ll2iaDgPn+x$0h&uItMU6VL_)W| z`uup9kDwVx+jEwJ(F90gNxY6nqoIVppP7Y5JpO_)Db>HL-Y9{KY9b>x30&azZB3@~ zRlA4sLSFrX?kH!X=3!Q73dF}oup^z@!}vpMKtrhXU{lyjoXLUVei|K`7*$wi(R>M} zF?rXS!fkfEqkg}4v93iIr$Bd`-7%r+07b9+PFf9bXI(ec&Z58%Ku{^fkgh}D#px`k zluIIN8N`tuWd;=w9aA>|*!E{I>OhF#a6k}kHd8{nYPSoM?x6q){idbkKHO{wQuDfS zZuG=&JV^lkrZKV4fznFB@XCUzLBMQ`nn3>>QGpHW1Hby-!a&C)2)QMJY!)XE?3k~!Ps`w*(taul8e=ZtjTLF;@&f7N?=8yA7I8Qy!SU9cQBFlou7wTyI* z47OsNO`Z)Skka0g?Oz2LE^}2f%RFR5;WZfTTgrZM`cu@f6 zte`EiSRD^v5HRz)dQ}pD<+2!+Q|agza0aNKQ#Ej8wB@pu(@gt7dDE^*fcZt?=3n($ z+u=dXe;*gOwmWF=TIt;5IkSuBm%y@Z?)fbz+JU)$Rrl*Ya_{6-ev$o+rIM79sPCVN!OuIEA*UU>t&187zPCKO z*4zz^sO*Od3`QgOjy$B#5qW@c#^F?x(*4(=wEx$q9YJ;USZoI+4NVV2#v$%3s7Z-? zu|03^N)T%aBRDFQ@ZZ_6{7)lQ7*d+^phTTvk-i521!5LKL*{zMZ9#f5TKMta1O`V% zw0Ooh1GKHr_BneNVLha0s_g-f9H{naZ}}9R{XF;mksE5#P;71ST)%KfBX73oa;m#6 zeRz29&0W@y-n^;6jw z5eUu1AnveI+oy8-JQQ(_ol*hfHKz1s4`;}#+n^zjSAK8=vQ{})XLP`2;&{)W?nof) zJPYek<@J^;^ve)DdQlSkN9p)bcj4SMMTQg0C3MP+ z5iN6`xqEt!`5*cESD42S88(CY&=gaUIeUPE6ft&|-9RA~u+zMmzOPz4MB_omnM9~3 z?Hwkeijd4f*;M2DZ+oFhUOzI7mJ&Pk1Yl2j3H3+>>E+&tw+C6&P zSp;M1wqvq8C=eyu(Zw@Af3@vwX$p$*W`l@S)Js zr{?lrncqCaN2t?;+*+>}V?a8e(4sLeJWjWexi=C7q{5#}{Q5Tphq$#@a!Okg{7@xn z)Dx(?x4hiZexpTKr=J^yR1s1*67r>Sa004qZvC`#!dnFo;$1V2O4^&b+xfJHGnN>nwFtj%oJ%tzf#A6E8rMUZR_y6JJzcYP-0Kf zdgg|Y0i!Ae?+1pGCMfSe{efg zFe3;n;bovN4Dxo}B4gHIX!!iII_qKtE4~q0o1o4xJYZb=@;=xuP9CO(V zG_K3@NpG;`-QUjAIhCf$v-<~>(qx*lsO3B9K0z4m;M5t-YDh!}_Yi$PspAp+34%@7 z04fNCF_uxEFRUPBLo-L&-9=NytPcJ72Z1NP>>y@UxsQW`?>t{4;*zP-l)t;(jiQ&7 zY_73zo>GK2Vlui4NHmD{dvx?5X)o37Uw1zOdK#3QI>tJjn1-2Pf8{lshy#O9pKEQuAWRFN##Scqb<#DGN=*w!BTPC<}?2E|tBnmim&Ul2GenW;S+ zWCX3ICyXA7#|{8rE{o^`vE{lsIXWf>dWYS2|U2-Wwwqv-XsEz1FSn{BnG4 z|ND$R+V2U=GaJ7cBugsX0zxaw&BX3MdaXixx z6M|(=S%jd`x57Xku1&#&$mazD7d`0UMM?fa@s9SD90@RHR+FkmCn{Dvu& zf%NK)7gO1uhR2lS7h#SJDoWhp@PyW+SVz{$X~2QK_^hxoj`GBazc1hR!8_uE<-fwF z=C$@jG=nnAGn;qTW0;f^Ow&x{r;YdjU92Ylcx|IYJ>=P;-TqKSb;)bjD4(5~=_Wq? zjH>BzS@03?@BI7~)=n%lwQfi1?DX526?dcm?i4U7Yq_vBd{I?SKA24Vn!F*@G^NBj z>p3&UZ^_pGRgbruqc-tp_Kg-rAatn*ajIRLX;luDm%gvcLv?%&JVX0cT&_8xSBIff zc7Bn7(+b-^AtW8m#GhGSqPN4K(Oj_Tw=LztlzG9!^GI;EcpgBA~#~KB0CU2BJ z4OcH}Hz&M#6V%uFW=>_}&(Do7S2QM9hw?A9ew$_97&@SHS*bB3xJqQLC4`CUYF5)d zB1z{aMiXEN4NB$)s0@CAz^Rpg6v9p5``(Y1Tv|4G544nGL;~zQ_*aHAlBJqDPpcou z^Q(HrXLlA7Cd={s?KQ55O;aC2jLt~xMyHZI3lC+S(Ayjka0Pl^B&;KeLC($>gpz1G zrcOk^h*^=)E)AFcuDrVR+qSQdM|L?mLA$qOHUgng#^H+mrzQJ8PPr-AiDCkF7r_jGs+>^ugI zo<0C6bbeO;Sxal^z=@LwXx^BJW=v3zA@yw3-^By-nRTB#H=U+RXs1SW>O*Z6X>O>$`KYxZG_!vPRUKx;Q zuXS1{T62D9Kvs_tT*3tDeZ-4!Tyyoq&1C0MqC5N8_Tect=d3`D=+}+gya9% z7lhXUe`($48+3jkVptpcUSkXRgxIOpsgVVs?9U- z-5znPyV74Fdi2YH8)C>U=&+4Af;a&9zs@QB7KS~VFU=iHj(maY3)>uXFIwsWv-|nn zX@$UY=pidfI!`eD>VOa8T&x-|q`mWUk?%Z7=C6KT4PZm1eE#2+81;fs1|tsW?6SEm z|Ma|arN@6OjTi^xmYZiq2i72a3l93pqSaY7mpoxJ$Qk;}#C`^RVzD7*-TBE8R{m}T zS>2O5M1$>SR02Fy!{cOpVY35D!GolJ)QW?ssr6?h7WAxLu;_zEnc}a@Y%GmwaS$8$ z)#O;r?py?0b;AVq+}0B8&FB3rEu1eIK|<7hZzB@XD=rg(<9Zp90b?KZSa_hJT!}X1 zpx?+2b%{fcx`v_yTB;Y$<*49bdNHxpz?50hqFoZR%x<#V`i=w>bX|~;w?KDANDjJ2 z?D#hdDR*5GvirNTT{SA0m-;nZLuAO=0HI~3g$aML^P)J_*EMQm^fOukKqvT;cBAQ2 ziFSuy{psscIV8fs)buP!cJwnr_J!VV&=63L>KJRnIsN?aoZ#OD$LTK;yBYAe;GBX1 z4qG|2jDk!O-QRn+r8qRn`TEhbAfZy>kc$Fi#8O>RvA1h<_IjN#Xr&vhhT{pZnU=Z7 zb`<8g}XEwBY5O-=*>{>*H^OL4q!a z9?k5}xpKHm^MEs|lQ?0Q7Aci>PCS*7cOOg*t&tLpSwU9%O&Os`!QqQy>_}(?MHvUH z9oSwP%0xkveR`wVXQL+3=F~Nc0ynd>SmJw)M2i~^b}Z2+6>+=mY%BEWa=O?N}|m zP`wgF31htae8MYs>Rteho2N7-2rgDa0Sm_-g%HE=rnwgEjqcNFLeKDSJ<5D$QJ*}dviwqO6AfpkgR&>H@~_pF-zZMpGz`g z;jShaBcoYaQL*z7%)5rN4O6UPgtY)94@ZCgJ#$%^?CX2le`^6n>y>PTkFNUu^tSyW z*tS{`*U0@Y7Hmk{KDD_01>WQ7g(DSHIz53Gyy#Pn04@bp8FCuR1*$wBED1X8e&Iy^we$Jl| zRv-#59$wOwv+rbF>TC>sWNl*;eL+;rO!3*)w>z)`;-@1_y2uDVx5m#H!{H) z#tr)|c)JV0!-jeJnUS-#Z1=Wx>Hn68b@IXp`P>~1@4n0Xe_!{$f%*2!=qBvyqP@DPoF+rvn?x# z#78!CH4vF-O`#fmIi{&C;}}X!l$VR2y0>RmZ9rh@+oE4UA-z2hpE8_a$YeAF2UUVY zL@tp}QaN3ckiVVR(`BIO_S}N_Uaz+kkwva#!oJ5n6Gl3V?Xegwi*v<&i~aJzSI!MF z2?r)LfAs`?eSgSf$?4r4A=`Oam>1^fjo7dVL8e^>@-wwt&D-?*AHNwTs(_md?4g6k z7uP9`)^hIlm&gxh6*4G=&o7`>>($--|E8^H?0>zDb7b>|EhX$kC|deO{OWuEean#s zg4&|5j?L{f;RZwXIR5ilo&&J-L_p zY+1b>$$zV~MUNq&HtdbEEUv(~DvZVS0gKDM3z}+b2^&~al^U~PoIze>D64YRH8|?g z^L4*_YC;8%=9~W{%8lNN+mcA)e#WHiH~?J0*$ zU~n2%4T-h3Tf>A5LRp9Gt8sBQA=P=*4q-b%QkIV`mi&>=`;NEmj6bBQ*hS0%zYyJpyAW|Jl&a1@Ok$p<{A`jCoz;9q4+!J965nnwOHdU zv<=iC1(n|pqC6%me&A0NzXTqoZY^MbYr2rLvBBUSSIE&NF0j=k6V{zW=(0{)zR zY5)B6*1&qL07L}cC2@?}Jzyj9YicSMi`?U0@MUFVlM^e6(t*T#HD1%|*906fNC&s0 zlJzE?PqMqDX&_LVv;SO7htDZW?xr2X7-ho}0odI1@LK$7P<-jkU}T9_kZ52Sh7OBy zz++maSXXim-~+?gqxc)))@AzpRw^HaL%VjUK~v9ac%1h)-U8^72jEmiFJBLqT0dvH zb5t@f5<_|2(lm}$*KYCSMzp72`uGj+9~hg0#!TO>O4uxY0i{;$)AF!+eh1bD(y&MZ zWBDEu|K^u`V9bSnNy@S_KlUYv7$HP`DSG;}Nlh7^9GKfd!l+-iB&b~vhS=_THSp@y z`JRnT-Y$WSDe3gp#N&;BJl)+p{INu4yx9YjcBQvmVUMWB8Ewdk{-Gf=&|{1mjkS!2 zK5AT2BMR87^*a-G2DqH}IImh>*;>?-0=Mnok(uljWsI3M^_Qr=6X>uFRGH~|d>oXeY`KdzZWnM?Pd z4l#8)!OclHjyN0QP0a(SCiB%7VsuYDXQRj~9l{lysbUWDG7zSLWxkn+>)nnqdo!r` zK^W14!_}IFaGj(|Pd&)8^ne#~B%~OPa&ho2P0Aj|#J3~= zvkJcB*pD9taAt{*AMeeUee7OG`JWcxI=E`hpFe*lhh|*`Dg(YJSF!ylUkpNrTJ~Xy z#u+C%_b;4pbs%vec@RX7fF>EjhY+YbZ?hvm{u55!-~aoMPjclMi+Vm@gm@Q?8}%Xg z6~eis-)fG3mTD%OF?KTWs={0Z=EG`Y4;S?|M#z7?{+pQ1Dt=&XdyYjH>{pWyx0bF*|(2T zA)ZYFf|S0CnV4((`?m!2M;fl50<@=hgD-$}zgz&8!&->!P5Z{u9$>@$bhJ|kO*WiS znVvp9Co!2J z;WGd#)=S;3bgU^5m{!6 z{O6$|pZu&dFhAk5tSOVp_KbN2L^fpL2S{ON-A1QFN2}68|&{c%()-&wHDh8x)(eDcGMO5B}#en?4ub z$sr$lradh;x!88({?4z6@nfy$tI1*-vDEAZ~tB z)eEm!Qh)MNC#w>6XWe(QC-uNb@ zrKL`51#K=RNeVu9H0Ay;*24BlBJ7meo6Q*W=iq1K+9OZT_`(?3JY5uFOvv3NZlUMl)$|5&IJI26-M_mDn9t7-tgZg zC>W)v=~W6l@zDNc4;^r1CxgEK0qAPL^CRN)Oya{HA;6LFEK_F+bf1ys^C2N2{nuG0 z0E|i2+&!-RV7p861tlu;Y^k+;X~<(pq9yC4{_DvE#$2hK{q{rKE8h>W_1>z#NJo`4 zfBjvxx99Nbz~@{34dw3;Qk`SUh+Mg)p>9$Jb3$V`$%kWY%5G4AN zm}q+>U_6d)#|%?3`t5w>Qp9Mm>k&`fckAB#?PK_lVvv~= z8de+fi0Wv4yP*M)7GeouWPsJtEoYDG>IOSjPC7lbW)2l%tPqgSePP|S@H0-9ga#(+ zm18_%HEpQ{S1{ozw5UYT zD`WdqV8uOU40d$K#$ceH^tjSEB7$K@w-tPH$xjxZDUb;Fc$jRiv!aP`NoFlovo zzaqqhB$dAn9AtFh9PG}^i=^yoxdbT+-3}DDE*`X}3XTpGc_<))+s}Q#@WAKq8HmJj zumA2^ao?sBAlmFELi6(jp}Dqjoy0QsKum)&LtRT%#LP2zVZPtJbl>GVO$zM06=A({ zoKVtzX9x0`YwEm;19>#~vO1w5UjY<^C0pF|ZEn;gb$d0~2_O~s<1`PXNc>KP5uVe3 zS-nD1ZOVUcLrD8kznn{dxzm<lm6g?P@Kvs1FDB1o^?#mT)mf zl5J^u+$`h_)J}CUgw}iwf<*ABy5Lxy>o;qt7Zx7Zt%SD6IE@VsW-@M}aKna^sq-)F zv#eO#rolCJVTZs&)qH11{=dArGQ}-S)yZEvX}yEcDyT1m7b>WiQ$rcvJDcxR8}R^Z7u(6h*}<(J~^nI?8DxPD-!RKT(N%ocosp6RF3uVTWX zHnQBzGhsKN6YILc&FDM;Rk32UwZIbr!wyts@V#K|$1qcd=JX~$# zm`B`B**IIo8^u$`laSPOe2RN->C!uk%7kGbb?L_ApEZ9+Mn>jCj%`%)P4JElJqMxo5g$;TgFJtVvN-IlxsfIs!n*H&lJ7UbpL!4}+}WC1Lq%fA9E`#wkP*D+n);L#Hd zS*RRx{pkoEU9%1IFPyGeD0@>q02kg80v&*iSm_p{{Eu5n@y^5BD#tJ)N}H)_=M!rQxOOvY%$Xp@0Fhvf3XccLB<#|& zU3S;F=K@#zIX*mlOZyXqQdGuA{|Dh#eq;1rp*18yDzk4!`%B3BScqGs3cNt-$i7y< zA}lPt@oC2!nm!8-Cmo9h{}r##_iOLRDPo(Jv&KX2t0uL=P~R^}@>`l5^ol^|@mQIm zM=Ukg`bs)8pq|Z6nFh$R&7qBR=6Lg{B~?`X1gzHH`R3YYv5geMijE=HCab5PEak6N z#oyQE-L+$d3JoV?Muaa)GCV>2Z~J;|=m{8AFN^(AvqM5L0o*c%)&YR+2~UZaS&nev zd0G*Ns&z$>b*+7iP3A@waZAIWsUcor#e~n^r7~khhaj~Kmriwvja}>aO25qvV5~$?#IqPmqMr(?*sdTM2&`7u}CK)WCGmd?x?a3 zt`*&ij%wMfd1m+?shZ-b+UI9CmOtIAWM)_S%DZm~D&H35Fogjs(@u(6m+*By>Cc-* zAlZ-wq4Y;v?;{&gk{&9-J%;taqgYTVBR`53edTo?gjMJ#Z}y(r@1!Z6IHoKQ$_s)5 z`4J5F7b>?ES8*uPQ#0b;oL7M{&_j6h2y4E9UiUvsRdYKKQb9o+U#96c*cIW8Lo`Gu zEWp?;dIBrrxSH3lO*R%(|C!4-eI}=Z7bY5L=&$b0%6664jaC1^Cf4jKhU z&)}fIz{`csjer@53)lasTz#JxP|(R$+;`b3Hs)MpkQbT!Vy(1kNd{R%X+tbZ8%}kf z4d<8stbp$aZP0Cc5^+4|6|af`@+cobl?Uo_NX6cWmw#u@6K8=1TZe-GTAHV&mMT>p zc8m1amKU+Sp0^bV`eL_(%n>(^UPNZ(r7W%)^jxjPS9rg1)ed3Ah-->iqGUAkWIOVM z!CNK`7AN#n2Z*)z+VNyElJ1p>!wd1ZVDDi6&x%W{{m|biT`R5N!FLLyhe|&+&UTQ6 zb29v8>Y$NAst^FZ%wzO~L}P}Oqps{!Ay15pB5#zuW-PNeSW8reMk(rH2F`b5)1Eiq zu&5vMSI-%59T_<{evV==@UJv8#q|e-I&>=VrL{T2Q}t)~5gM^7^~(hbr}+|iK$sgf z`U;Spuz9*ry9stcal_IRWfD~;G1p9zW9B|Ppc;lR9Ax&ZkRvR_e`__~B^xfVe;wlA z_`X<*?F`zqTy>l|8yB}IXf_N;naMqN5BmJrk8`CwNdVS=K!+-?Q@jhD3hRD``&JsNOgL!BzT(^SGhPP zO^kTYx-oA%zXu>AqAH-Rwj&ph;$7#C`K$--Hs-RY@qU{g*c^x4IAtEMXcPN7td~08E{$zfy0C8w?YRc;Tu+`}0DLVZSN!pZf6K2~#fP|U8YMGu()rDVb0rO z`sK15BEpe|?Pi3MOXDx8$0o;5dd>##j9rveSCfFU{Nuf;@|qfMP8mR2y3yxKnej`znA;r`Ld`;(dz6=ym_RwdNd-fE2m0fRc^t_t$Yy0P&ba+$OjVj~P z{4MIc*X*`}lZ4lPE0d)GeQK6EN0Pj{UeIg+8p&c22QmJ?e7{Y225LiwH2xkXSg5?* zPTAQ+yaOxBC->Y`mH}D8wMP*3e?bq%GhaAuw0+C8NqgOJU*p2SOyN7Ayvn7@e5Nx0 zoTl7FcjoA#AaiBzlqgfo!b$(~&4b=THlVyLlrS)1r%YUoI&uQUj_{UFQGn1F^BOwgw+pLhpO-k&CTb^blBc! z9^-y0$ZTupF7#}Fp|OmWb1JK=6*>=8JVH4fYdw<%{zo$20?%<*Scvk|7?A191O!8M z)$z|Yd{3`;FA{*T`x;RAoyQ^(Pl?{p*((h(4+@Ep=Z?j%?{!YjNPcywp%qx(btGY1 zTfz8FAFX8x+?!^US27vsPY5%YQP+m?WG99JgMqB5bNf3}fP{ViJA0FR?+w-5n7#tD zbjx3+g7a(qnE@=Rh9kRRNRQZ{V;I6orlf!*_{2E~VTtq@gzcdOY{S(%^k{e=XmX3Y zk?W6oKVeG$A6MTUPW2!D{W*@k8`cq$jAT1Vb}~y=iHwZQLRQE+A}R^rWE`VoQ!3eH zCM)C^$zEBd?9Ax7Pv7tFcRkP3KmE~lb-mB~bHDHLx?lI}KGbAQFHF!;F8aZU9?U02 zi61%y3{T*S8nA?&Ypu4FGslnk`4S_x$4Hi^gxokM?NUAW{rgA!FKv3){>v=5-Vb}- zmb|)ifwcL30A*~GRoFh@)&pgWrH!pvRPT=$;R-K{UhV{@Lx>Q3qVQlU`j8f~Mk*(p zl=ZLYLNZo_Zr;0ZLo(Y4i$ljaouRb(9EloXR|oa5AMS$DM!9HaNP^f_Z|UHo1t>fyAUDo;CB5z`EmyG(VGer< zXrN?6V`FIU;8Fcc-C0myc%TR}Usngm6fi7X0yE9~S(~k|N9V(NSSPI0;l~nFYQ)(O^A1^mNgXPBy7Tj0eQ``&X?0KLM7u#Z0 zt}u6TXE&?}oeliAH?-hSC)P}M-{O#bIORtTffv(XuaNGumTN^MQ?m1x z>=9{9&RcEYw$u3^KYxB^%KF|5?dJa0^j75a1iw#nx8mSQF+6HC0ED287fqQ@rX%fg z7PJn`s?#6TF2DlWZ#D=)!b87p(pwwMC5mjqHed9L*8z=UR9jzKT2FT6%R7J~&nX@C zWwv2k+Hcxwu8pW%yyax41jW>k43re9T%!Yny~xx%5zZ|%PRbUcHtuZ#X{s+LBWPj?)jWZ=$)Kjzqzb(TdaAD6uL0c=P7X}=hK5d zJx^?7KaM>-cr;~;Toz1!DM{Q|SuwbL^RTa`tzVWeyJ|2g?zX%4>Mo#+bpNDUkwy~B zXCmN%agp_3khX+S%LsvX^Mlq1qOV@UJA4~pttGBZh!XTSMS+K>m;&kd(rDFFm`+@c zTd!+S2(Q*Kem?jVLr>w~bDQoXYtuW>C-`PV2>W5P$Gc?WM``%Nj|y%7Xzt5*J7nUH zS1@wrsV`c;Bj7>qE0dQ$511$4L6bVEL5_WRF!jOP#$IHH? zAVvIg6#0FD%84&kBj}gz7Y{M@Tp_7k{lb+PA-Y-0lp*un z=!v7hXneB~rw;G4dVnGSc8(u*s^{QlFkn#UOaX2DUOz>#pU)X$Wk?pgcDm~es{Ajg z^pp-dH91kK5}xWvKO^e<3>joymsya>JJ`)EUCbC-Un-@G{%(#3UePwkRr|>&~Sw=}BH%>;hoXI1+gp{_fQceXE{Wp1e=v4zFRTmgnxIBWb z-Bm3|vry?LBT!Z5dVSDBzjIrrAH&KGUQCk~Jq5|Ju_gw2I~#&KCMzS^)0 z1wnvO=r{lfSi~aJl+Q&5Sry9g{%4n2k{`GM0tb_kPA!_8SpNlXD9tk;`pahy$*#`2tAriqIR?T%+U%KlM((EL&FWTl|3<`q++WG_{r3)m_Xo-lW> zn9!NuI7)UbN>PX2b#!Fph#-b#CkEqN->x=z-%8Tj$!_F?ipd*P8;Ld~x=Rwee&ySz zLQ4+P)WHxF8WQlls2|S-Bcq@K&+!!XNu@swZQIvKB|5{VYH8hn+DJ`ZMo`1l7%Kt=|fK zMJCvO(fA+o@rR9z3CM{-IDEl5N`s3pL9@C$3gi?Wa|hhCxK{- zbm|NZeVyvM(F@i9!vM~IpSc_Or=};y&qE_KcO-}*9>Jj8c`Ij1VboqV=$-&OJ?(F1 z8R6mEFm_Gq@p1c!?Ahx&IN4dvbRhmD08E3a_v*LnGPYEMyBc?|9EZd4vyuA=x!gCD zCMR%Xz8SG}T}B~p-CBB=b)E)H8rtSBg7h#aCa+z6E1$!dMdqvL`SgsbV~WF`Rig*d zegewL>>0Tl&!E?vyFo;`FdMMWAKeIDf8qvU!mtDgJ%CW@vvX~HgQ~)f0*#!;n0;IGkT6GUZXF1QXd0Z&pG|Z0JOi*m4~|v z(E9fyQ+1f$|B-s{b&2oK*MoviOEjuFGZQOLFe-e5c78|u>F77Ay3SE*kWC zzK>Ac-g*QutnNd4*Um2>?)rw(DVb!2d(RwxAm-xPN@zd`X^+ZV&)h@uS;+eJz)4Ov zK^SUly?v$>(@;SYu*5`@-){k9)FT-qP$TxlA2^7w1VOvR3ak+`q=;T-$kbUP<^3Mk zzHm)=F?FrM3##(dsqZ-if~=oUSoREh{k_X=@lWWCiRs$;bY?|-YTd2Xz{}5J=hS3_ zZ!==!)C8Z(ckK+}2r;`t-*Z>>=n0o8M3GCK^BwOezuF08m_#_C{C_^XCL?@y41D%!&t7W@X8Sw2 zpj_OrG;{f{acW}K2aq&bcs-<>ZJr1*qu06@dE(E*mZEkGJh z+#^b+?-N5lomzNG>Z>RS;im%_8!I@DqxZd~v10*D+}%LJ^jGes+}a7{e9PpUpl431 zf$NxEf`33a<^fN$MCn_F%!DINoT$GPMvm=gxnKp|dGqv3C`3qmCKxW2ph;~Y7yTyx zk-!TO=4IPcI{vQ-e7Hw?=6asgFESl0K(WzpVQtNGZ;LRr=vJdbRDC@1sHFlv{WKMl z4AZ)B%4bfKIu#$RreN^=MWgR|V_QPVbb5rt*OexEJ8@%rudTprhG5@@X!hguuD~Y_ z4rws<@O^3oj-6ELmBP>~j-KS!WBTgNX>mVS(s^KQn=0>2Fv6taD&Z22^lSC0zbG2I zW$}SD7=C_Tjc{v~HzVeUGpH|(loGN>xk&5S5WbVz4Y6Zt9`OY5Z02V=Q z`I`_%9~OD=XjF`{6)A1^qULr5N*Ddc#Z=>q0!KP%^(RGMD0<{g&CFzPNDxnKxgh9p zXXD=o2-s1HdX-tlhg#w{?`wD2k!vKcDFAs=(NSb%N=f{G&so4g*02Bd^oz-?D+tbo z{noi>plxUClpab^3V7NQshuWS^LeG%b1=Z57&4FX;62wW{rjm3nY@e@8OeveQ!;9a z*WoTf`Q1iOLEw`jY!Cp@r4^dSY{wQJ4w`EnFf>$X)Oo?Ak#On9T8vN%>V4^{rShTE z2^!<5m8R+1Gszdv7lc7R1Bi*!eR%uH+b8!Wm9#@jj;XGu>vwZZ!s;ko+r4CsWB{0>Ba&QQi7+C`fnXkp{+qwl1 z;af8X%#z9XRNeZU`0Qj3I6_Wn`5^-+C_VOI6=&{i)o>it8GED^a)eICy< zMAml{87RYm&(+c2ka26txKH3$&)SDc&&pSOTgy>gwcz|Knzz>uaDl%f?k zAZ^x05D+q9n&g#H!|m=6(_d7ELwnc1csWd}S<6YeI8QG)WQ?UM%;Cx3zCq87##41& zZ-4(PE|Gcvt4tmmUM%S*zfG{!;Y(f#sQn`$UnJR4yDhjxH!$B3%Jhb>1&yjIAGjEn z6>-b3VEWtkw&@Kg%TZC}<53^as~ zzM4e_8CU(y*g+Ni(kH5wSp0F-eQa%}Fun)$vD9G$_m4fo#Z}3S#+#)i$O_+M=}Gm% zcG3jea6&!MoT+qOE6~vks?k~ro^pn!T7IZ1(}xKU`unhpOrC;R-X=;PdtVSPrCOYl zg;SSO-yL+$On7beCmA`bZg0}qKgxIoJPniQ10RT*TO-J%4Z~+*OYnoeJp>C77VdFi zdfmkk9nK46N0$MexXZ^t%D`0JHD0sH^8jwi$}XdZd}m2Ku(`V0^CD^w+<{;;H$kyv zNCb$5r}b|_`XpP0z9Jc42W=T49r}D&AM;6eJ{QCuPl(StpKqE4fc5RY^pzix2M^So z$9p!ORXh5g-*=E8K1HXWkLIZs-R$+yLi^nXW?$x&2WQj=M@CMABHoGMsEAy3GkWG+ zSC8(l;I|j+_{p}**@yj>$CV7fJv{Trr-J*uJlUz-1+G^auD9sTsi?+l@y989TinOiUzt&& z5=HURoW~lzevp>me6CvWx1_!$pGP1E9BH5m4YQiOCM)suak!;NyGiRoes?@~52B>Q z2al+_jlK!>{BS%Odd8YDfa} zHy{h#xHRJzw}^@tbHN$XkrS+xH!_6r{gAMZL+M<|eDpY;!MRQI@2Ta{q#l75y-5#} zRtLO~i8?-pJh${M8cM%aAxY-%K)qljQ<@Ru^u7+tbH8bv%=62A2U}w%BJ{F zpcFOx1ygxr`TXVLd6(oryrUjIy0tUrgC^YYp!x;~hdUu}dT*#wi&j+{YQ8pN>o@6+ z|H};cTEPq$zzj%-6cYiBY8cmeB=oHKPuRh~Z+TF+Pgsw;x*JFSH=YGB>@I;k9pHo4 z<`ba6VB1-qTdr~RJBrRA)Mpm35-laqIDlG=+FjSTrlymO57vbzhtBYYyFIKQBAJy; zCBJXVuIMKE`{S99?ngda<0M=m4R*<z$O-o`VotLcM_D98g{reu@mpx_Dqgwx+CksK8&23L`= zy=J5fZ2!1ADfK=QJ%Z}}bMX&fZPL+u&=Wu5`L1mh(Pb;E&qt9NsPOC;$TSC0Pp6(0c^FuS%B}4gfWCkMY zoE^3y4FBHlFOS{MUIZ`}Z8QLE3p7<+4eC5O6OYJM(V2}I9S)b(`mQ0k@38cNEARo? z{!yL**bscp@>)*2y2|-3KUlsKI_7L;#i~C2ErmpgD>{xYSU(hoJ(ajGCTClpIVBa} zXh!KB<3)R%4HQ1c{3`jEJr^R_la`iQ?#w^AqteWAZy&KFP%`e{z)4g+kmlSPM}&I1 zh?>9pJU;}5Ab3)`kgKfs5*qn$6;)UNHesM57>^x6WxIAQ11m=x!8TC9vRznaqWTL< zk->;jQvCyY7zwbpcZPxTeL%La0J3cpqc-tx2gZ02p9~Q*k^A0syRi;NOx;j%E5qED z308s>Vw#Jzx~|SJf6}v&b2*5WtCG9i%JuUA#y+4`lfX@R_C)YYNdxi)wtuhm*762g z)tTN^#CT?c6nCNSI&R046H)?)J!roT0;{B>=I@}e?UBDO^NOwRS%VzwT~Cqk);!ev zp0{tu0x(@D{HkvzLxot1wMs8_7QxXnF=XVwcPU%DC@#=823^)>gmbe2d;ydvR-WoU9};>IRGdNQzwV}{ z^wJ{k=IA@$HyaYFaf*AqP&NoX!<-AwBGQCh^=;6}*6NoGkvj5Z_aQ&;HDYXT!>oBT0;B>78sUv$<{}L5p zfQops>s9_EO;DElW%kM>^TI6lE^dibKuaZ?>J-B$Zcx(L5zJd77WIXWLp)VCaoUNb zcFf4=HL*U1j@jZ3=A(MAujQt+=&B}zt_u4qB2YtV5>RxL2PcM7 z<^(^4C6P-9H z)QKMF1aD-!@j#&PGQaY@Jo-%*Z<3p>TR-t8*POn!As|l#Z$YN>imdo3!}5fF$%06fS9?J{{ueXCjN;~A~nG5@?K<&_ueE?vs}5_a|!k^|cqVNwko7u9d6 zpake^nh6t}V50(XU=dZv_E}u7hO4+sAcAeB`gOUn6>7&{Fy&nRA#xfhp3*byb+qyi z1ekh|r~?V=NMYa$d5AnH5vZOc6_M{`kfM}A&T)lV;>_X7sUar+hm;RIF4+i?@t4ST zUHHKj(L`!Be{65c2wL?eC4r|=E9{az_fnqw0M-0NZ zpXlj9KbVBXP?4%g;5&%2KqvNTt_6Zv!i_@6s?A?T5g%_SX*~trAPuNw)y|mr+beQM ziF#kea`yVgr*=9>FZM_)TjkPjO4v)TJQLdi<&g3K8@(6Ri{HP$0JB}5oFu?sYQ}db zQ&R0!t>`sy*{@^cD(QH-`GMJx_Hb9FRPf-y*7__G!-^gSeaQtP=l4mXsNqx3ko&1X ze*TG^-WDiRBD|SSlLMOnyzlMX%K=k<*N9RjaS(6h2_7SYWury>?Q zc~f0k2?Z*8Ep|Uktmj4-)hQxZ&#eWhw;#ntH``_yT6g~s;$@duTU!e?78MLcZvee7 z4|0U6yVg|}3EcNGorMQN4BwP~A+I~Nc`W@;Y$QmN2$5Ab#XZaam9Kv;D3$A+f?Gs` zU#nW}%&#$)vUBLsZ!qwXp)j*J)0TFIn?L<35(LE6F|$d-X>)(%`(KR$QgKnQbKO*o z;PfW%Uo)9+n*uY@6L$$fuv+S;UwHmG2zm?ZkZ~D;{ii4!Q9Ds*KY=o8;ZZZdZ9=fu zb4bn4+XLs>NVlZ$iMi-tCH>S0iFd%eQ09bT>%OTc7P{=2fchCVx9Yn*UUt~}IzBpo z3-D!NHRL}lQFlt`l^`DT50aDDBD?{Jb4r(ZNK$dImckPl6XoiK2w|Dt6$o#nX>4Ae zylxRTgHpL(_@K}%dUA|WC=Ji8x*n*l&oX@u0?Mx_UbF#K*Aw0l$j2hK|4GZI*|FS= zLER#uV%aci2AhB8DXXGV`GfN?Z#JWTeb~#Z$5xSZ3sSbAem?wtq3`-}xwj$6cY8m2 ztawCRK@3XKs$Y;29b~p3=ySVmB2ZaQ*%ZHI&@CZm7iC4t6*0Eu>dtumAieL&C zDWZ(1w!%IBqanJSNa}!Il2(5~kQ4?=AH8yhSWm-;1LidrJ?5-aBS4V6a^f`QpUtPK zIAQkh*Ao5`xK&$7gC#%Ry=$$^bJIPPAJ&vThcgCir`p3*-h)G>4e}I$&x4%zZ2ws2 zvtXO#K@I~S!xX80h|2Hr(5-SV5r&}IO9C^Z2bw_fD?Drls%uL>$jUsl!YtT_+xkd* zkI0D#i zceY)xJv1X)QPgq!Z-PUxQ`Y_C(2mQjO}7&M>veg3(YkpFRK%KXZ3Nr{WMb0vTA7!W z2jIVHy$@@TzsEjf4~I3WqPk4L)MjZa{RjiOFq740FeAJ=^4J0&47*KDDy_%Wfd9_F zQVQ;2eVQxC&rfJg)uca&RKT9ww}pYVK#^Xe#8&Y;AX`sZ9(|O-$hh+`lRO#N@~$d1G_YW+4L%4tw2*V{0SM!dsE8HCLGS73ZfwFEnrOWX5% zASW0YCG1IhY14e3(F4cDVcMki{*6-@pO;j>V3qWhx4x$U9IJY$7ThD!<&{rAP<0Cq z>d|-0?CFI`=MO8DZCf~p^P+=b4HHl*KVxm?_+L-1?<`Hpq@M9dBH7mS z>aRe1k%z&l%0p|1=ZD7CXM}bY&c8Z}?0zI!@l%o&ZvY@-kAC#)?%nqPIdC$DTU7yS z$MflF8fXo0))^d1K79+QwL>8>q}P1nh9F+?e2Lqx4h$cCAeO>6PPo4^dR&l{=%~4+ ztIZmmK5dTGcue^afU@WY*E1bBhYu6O06RIM%^oU!fW>t}L^AMH+^ZG#PJ9haQSV$L zDvbYWw4PLEym9hV2@_07(F{F2)~+N^a9cAPwsqpG$oRwZulFT%_a%bBFWm*Iql?kl zqHUN2ep-U!j~ce@UqPJGnJDt_MdX7GKV%ZNGdrTIgBv{~j4#FfX8Z{^_=4?M zmtO^H#m|sV5hRRwA7wEz`acL9^OuzeHp<8zD?-$YkR?+chq`wmpA;du3<`l@#L9yl z_wtk_+<*bZpP+tx*k9NK<_BXDgrPS!y&7KG&qukpHiu_m{KW+`XcsYVz!H$)2_s{9 zB+>%A(~v`n4#;KWkL4I_UaH(8x`Nhk82_Y+El=hX)GtXz%Jk0QQZ7lX8y53yTR&3d z1MzYbXU18%tuXqNlzug^CR6)-!8E<-(k${*QsO~sa}W?WT%&Qr?K;l*V~;J*v{E@m zkWAs1&aNHFp(zuz-A^*WDppTHM>Oy|-{p^?h>8sK|K?*|Yg331v}0}QMISO z96z+53suh#Ghmm%9B1@AJYUvY{OTsXfmRpjOe|MF0^8Q~?}f@frU@`{Q_Z(?B8?>nhM41oijs`ua@ zoj=`cC=J*@cmu|6q(}$MaiAimZxh+>uq5tTT7U|`01OvBFn;Oed6)-}?vg5v8@05u z!n6!{k-&VU4A{QJTv7qKVlaQ%Z#ll}U4Dw~7(AM*ciC}V%?(NU1!SKe))zp5JbzR* zf&NygVq24qFMM0Yv_UjG^SN)M^O1KxOr)>h@^mN?M}lrR@m;x{ckI6tLa76rms0r2 zA8lx2O#Kkt#rDrQBaK&LE_V=+#8iJV*^2?Csu^o+V`L4n5FQAIAx?TZ;^2P?zK7M< z2@KH0886qtXGEVYE1n+G^6l2MyNk1I#znDX(3ii?rP@lY1RYU~hT!JmVo0U>*zEvbJ3-#*v#)v2)s=1)cP|*J!89u_Z=TfB3VzER8FUw&A%Znf1%V)&HH>o1& zk79Y3sHvUU>v z5GUKFRSKyEYF znv%j>9n0K*gC-yLC$U@|R7*AX(?VB~m`DJN@w5@a*@~>hV~;WtK9W+Q<#TBc)2#@*E9mMdY2W<{N^o|&>`zlrQ!9Z_<~}0^_Q+h=yxSyp!pvDLgxJB>8Rp&?-bm6-*^bW$ut>|UCv%IMx*9Vb+hr- z-IgBjx$WhA@?&6*%}W{Kh1dPJWELa7^x=+K-}Se8U07IYyK;DOadF_h&82YJUN-p= z`#;BuV0O>oDgM-YY{|vVf6yXNz!Fj0`4;g!&#rUm667a@-7($>@b$FN*W!mQ;Ic&Y z8NW9aV$)HGDK{0hX?%1vL*$}0BUU|E{LY;_kFW4y?_^TSd@bL;WIf)iIu$Unm%c-7 zYHEx8;S{Rf3v9(sP_u?%Q?kJ&w;EY)^pw641K zD4UhR0EHy9T0GW(yvD0G<3v~NwtvsuB?7!ptOpRCW)# z^OE$9-ILw>>+4>21JtNn{nMNz&8W@`7`+_%Jb&sh&jJJ%6H7bZ6_{hI$rE@Rqn$S> zExOK9^iS=1)vXn-iK6y-Rk`XeTGE4|Iyf9KU33o$8mkuSY)eg3uBxoOoj|uhzi!|r zy#6Lt`p|G&%|Zx8kss+|XKJN{mlK&7P7xo=ef&a>Mj_tXa}LQ_sL~TPp}uw#a@4Y%_I_BIEVJVvFd#lM zH`dQZqt~}^*fc`Xw^MM+W#ANnTaRa6aM2iFey)B{pFHFwLTP>1>;fb~Yn|bo%zm_T zj-M_~02ZoE4k=ov52dr1>yTs)AD zM0HopHG{C0JOt@*yws8jX_QUK9Sn_@m;>_ zTR z;nlx}b>SYBC=p$ZQua4V2_?ADl>PmnP`PMB>0DNsuXhSxzjh&L%Q#Cs^Nrh~lJR4U zi)Bi1>~->Yb?p*bVF4_nxLJ1>~aS z8{**!7VzPGe+f$fJ!hH+JF(>EJ*cUiC^Z z2LL0={plU*z?_7zu&~ttZM)+xt77j*?^GF4^*ALb>eMza3r59HYA&*`v@&DCDe%nr z=XjjnNbzyF_6$ZXV|X}2aHochX?$ilhmRf0<-i$HC&E7pvIO=*k~Cd% z=dO$B4kV-gJFgYTv#pNE2v(=}6(3V5F$jULvkPaEy_tYH26n)AQiimw;t9A5UV{{k zj?e{3edMRxt}IX$xW2b4@PWU(ep(k|t6miv#V1^YlX1Z|;{}M0`zCxa_$< zvz++mw%sD7X9nE^0|`osD;V%3aQ$gNf;=7Q7KCN2tpq0y3X<_5Z-s{5(KbxexRJBx z`+j^!`XQXNH)lCDP;`j(kd$D@C_RC~#T^-&I?gCVO+|(*FnUQR@J1USD-BCbiHa<^ z|36#+th$xCx!3)LXW@b{-p5f&38iirl2E?QN}#lP(-{@;(si!9Bd$dbBkJ`m@bpDI zUmAF!OClFBCU0K9uAaoUpQRuQ?mP*A)vl6Zq0EF!7mSuy7{hFe%8S^aPEr!Y8m(@Z z)dYZG#eK>ig(~0f?rw%-M4ippb>X^wvr=-a5jn=9VAzBg;OwT;qvjglv7QH910woZ6UtpOp(&>~je zkOhh@QVpbWRJZS4&;zGfaFseSY^Uv5U_ijg=lg8b44H3$3IFpY`mE?8u^^@~YHiFN z9&&*kN+gC9zXS4WZu*4^1Xcw0siF~)WO)uEt&ALQiKOn|L!j;2fZ?F#jEf02_VzhH ze*CE1T#<*9mmIsSsGuMn!Eo3`UDTxolw=94-3)UYY4m$1SOvSpWb)^)7^&1*xUW2R z=;bf2hHninc!H*IZY| zerT8sdROBd3n9HTmEK9s#kHUcMP$H#Gg6m5xOo=l(nRrGB_d2b${Vj}o=2W@5<7oA zEj`^OeMn-8qCeANlyN=8#jwNLs^+OgtL0-XUH^}NuIkj3- ze<7#e7}n!5aDN_psph&lFB!KSye^(FyS-gWijfD%g%iNdn;bv2jIp&|sWUt#i$+Xk zy`piZW5+? zBa#}E$aCg7kFay^U7zRs^10*)t7A}*rD$sIZ0*yMWq8#u3$vDg{^U@ZJ9k{D%+<}U zIyZ2YWKP91779oQ-`^i_I%_R>hbVy#+hm*7XOKV|{z-m{QcuK~!|(8J9UjK_SUa2p zI&8Hd)njN9Jf^=8=iZf;#>MtrVD@$mfo%6yNXYe9c^4bq#Ua?VDS&8|M5tid@Lnh|72BTO;xKw9-b*9`r_A(jg4o&D7&2!_a} zDd5t(bNI>)zo)}w|8b2#6lS5`}AuLPnY*LuQ&xE7DdHgZv;~RPz38x7m*)lkNR292~cfqTi7D`p$ zGv~EM$w*h(~DDO5^Qynkui}e{}A-X&$oc0_3$LSDh{fFaPAvjOnaG zxrqflJTgfyqE+`!nkka?2E*P^iQ`wI(ZWj@O1YWTnPzP;r*_*erC40r#~9v0m#=uk z8pH;tzqvZ;_)tt(BRGCZHgv=#R|Y}=Rv2Wg_7*(2Bl z{UVYW0J$1xjs9zK)WrKY1f|obPrs*HNUNDQw!0L4$SG`M>+a!Ur*Wo^in20(?a0Op z22nD%B98A#T91>4ejO@9qHRy;9%lC(R zkFfzwbPD#H_>k5V$N+=3@=2#9#xwsZ@9j!Pu%*-8W6n)y!s|;eI&+JUCe{^SLXAyX zdMY`O?m_mhyx#^t+iu}$d#XZ4ma2K@s-s}(p=$p6r<*$x=lvZWKc&0tExa2WvkcYMQddd_*~oT#6oMj?OjQtv$6xF*T!o z?`(kDk05($ppM>Wd}{!3iP)LXuD5O_xyv4(U;%%AYhQ|Z2zX_)puuw+@y0=5iwL|d z_EBNhQk{{$*SABCK?EF8QWQ%4*|lpfE{J!OW_5pN<0(Y7f@Dj)XL+LwvBfsjo;Ymu z%n@x`_1nN|sB3`9;xcLr=T_*eX_j4vx1izihPTo~_8e!`6VUXz8N|8$#CoXrymGhv z0vZy%=d&CMKBs&%tPf+*mhm!0{ZG59GF&&`jaz)bMRtskGB7aE*~Ia<#@*=jbu%-` zeENH3x~yGRXXor8T9T1fGbl)C!-KAd2aT6! zm$+Pzk&{5Fx^+fTTNFPJl}ySG#xgGAW)+W@D&4Mdh6ALM_D~nGOHspR;T2aXWb9me z+OnY3>YuJ^%*=lpw;R9prHk5i209`=Wt=DMm1ECRi1a~!W{W&6F`)k}g|{(Tx}Q?l zMrenutSojl*GpDTuGQyJI0oe^-CEDFNJ9)tj-+$XUM|jkx}~>?G%){IS{hcTqK}Z) zZHlj@{hsUH1wcjZ6v73OjUd_GAvjAlJ0HQtp6z~=VGTi8yN=Pbzvw3#T|g584r7s$ zj{>sbn|}WOJz5j}iFsW6d5+m+U!J~Kk0I&BqeR2^)YrwA&AKc?*_7^Xw4LQo zi(5$q5}=oxb?4r<-9katkedC?kpaDLIpn7ums=r?Mct49Q|tKj=?ThyDwWU1s@TEd zwY&&qwqJuR2h_h|yo7LWhoC=4;EBH;KaoJE&P4x-l$Em}){UKwI9}bKA~K*M?+l-y zO+va$4~6VbEiANY_i#9WSV+&-@6A@&x`n5B9}8{40Pb~u_+e^Jf#%p3*_1c+M2zry z{37w?IS-lp`femI0T-+=l-s*`&SHmsNWE~L@n#y?ZevADhrOOIGwX_Q!Iy1;8)opE-q=1-;mU|6Uf&t}RhY$8 z?8YbYfES9TvP2s8>>rZd*W~t}No&gg*;|=X&(ap`D%L-Q+hK-JlmEx+;O*~n3-(}Z zgF4G`ECE6292hC-^;{eZ(ojS_#$JOc631dcmiQJ{9+fV{S4;$(`|+hEG@0u^SN=R1 zs;2w35pasI4w4_%Kqtdy9fPPc0oJZ}kJkLrHdcCl{B9@e%}e?@*B)Qm=JQs2pHm}l zT22b-k8#1KmX?p|7f(mm{N%HZ6NqlT55MQcjp<*VI~jk9-J6t;R|{`B3^13t zq1C-$T<`XSoZ^@#{_Bqy-xT`KDe%sRWRNs(Q`L}#iAY*gH<1*Q1R%AG6X-0zFIiJQnwR?7yXQc#H?ENDWmqCnyQE-jLrKNv*rx$4Q zE~}%~J)>Un-AzBLu6_5xi+NZASafB87q+?ngH2@{Z8dG_8#d!zbbAHSG`Sa&$ZB==>Lm>M}8Ri7iBW9Pl z3JBt6%9g>VN$wpNM$JT;2#N&((k}^sG-YpAy#kS7pAenECjIwqbX@EUl2JrUMxueb ztU2f%@Z974COj4Gt%FfKW6>5Ht;rX8#QzkO=)B$RdWxjv1OpG%QVY7hKRPw?3JVL9 z(DfgFj#RYz++bL&dJPr3iyl9PT(1m(Uv(j-`S5Y@reynH6~4x-9hB-*RMMcncbeC& zL;ODb`Vd*orT4UcRX_I1(vc?KnmDocYM-T6tr+g;Wj*#xDkkoQzJ}hR^{x|Vq=&`j z-S7DMIWcPH!JzA)9W~Jq>Oo9}W$@@OKLUSnl4Vjjw~kPXDRjRzA19((f8!uwuAQjy zp3_X0f!j#~V{z8HyThsSJOjS1v>6I=MLE+5a=90AhRT1Gr(*=l;s7nZdjIB2Gxs94 z`>%Tn@fykGLgsn6;PtxgFpJ0<6{T)m%3;DM#hLVv)gQoZEsi7sATc10l5R^KQ26#T zT=u<0L1Z5VjozoaC%=CAd~LMmza>=0WGcHSXBPhK*|Xx>+Mm~aM{g`oLLFdUe{@5Y zOcV9@3q|k!3+t8zbKp=rkOf?DD}8uQnb|=ChwR~RbQsG z`+i6p=XPY2TGii5`IL%~iZ3rtZ5zIs>VMztXhC$2CZ%eHeC4ITknHp4?*NijdOF#H zHHr{32WIhj+vsdV0H$O%I4mRO>I1T-42O%N@2?E=t~|R&E>|qFq$FU(<78IH@*Lo{ z)x&ZL0Z!ZjTEz({%adm!@Bm5NF2j63QFxQwthcWPYEiqA&*}K_eenU2j@7!7 zSFc3By!I#vO@IEpBA6rpWOsszo(so0&J4W*J6aAqZh?Rl7$&g1j+ekh;fYtWLeli4M(`T# z1GZsmk$Wqexsv{ZO2NMe10H(pgFd*6OBrLlJLh`PBmf)S&jneF)ft9-6ZTFmejXPu zJ-M#axTdwEI~^OY`4{(E?UUQAMP$Jb5fn*C$)8a?qZ-;1#JT$Z}YLG&@JcM?UCQlN|U(;5EX z2XsZ+PggW=PSz}VYTtDUGQcnn*#(=JnBZxhV)|>t<&h&dZ{GZh$&>JbM#3h!j8f39 zcc6;e1*pDTWf&GtYzah_L)P=W`S8W}B|pswG2jr=#Hki&NZ+pg&dyt?_duHDNdJ3e zw<&|&_aLdN)F*@t~dJ@T>p?p1;o{`?S(F~gxTB0MS>w2aUf$qaGb?jzuE>2p$FM+3fZTqSNE-;L2BN7xQ>SmxV1=v)zkg9tSK!ua2zu|!B zSP+`SN~x+cSfkA#|A8K&9F1Fc450I^RGWP>hpp^=e0)aF2u0F*O{hk}?a5@=2|IH$ zl>n;8+%Uilxk#HA--K_`#c@lEgx$v22@hwoq_YoISsY|A(X0cPrMkcU{u8FGoi#OD zqRzBndlzu4&f3VD*J<)L_xg|o{_f+b@2(d_#V2ge_4;#6z3%KFemVScK9teFj&^{| zieF7ZJ|kj_RX+j1_Ac)2 zN25oa55MqWex;zKWN8XSeqW&1WVLky6f|9>#()tbQjA}(5&D&zq4Vlw_bB##IHq^w z;P>x9n=d3YWg{8gT+{od5m~dmZQvLb8e>0V6cZVIUJ0l<*Gb~U+5%7eFzrPS|Ks!G zalpCWbuLPAs1A5So;TId`|F#0zqYf#8c31*_U+pYfltpouiqF?_u>IaL+ohR$KC4X zBfAZ|tR1n^neQ6_-@`8Y7i8e0dK_s;`Vz7ch!WIkxHoZ`0%+Efl}ef!*X(R(JIF6 zp|_~s7QpzmC?tWeu9FEfCuyfY4gu1wAbZQ6hgqz|2s*gi3@f$+T|$CS?Vd z7N=xFCT2R#)78{REAYSK`pbUQlE1byIx+#H@0Crab3e@{I)ZG*FD(|=xz(oB71cEt z->A3RJdu21K$?c3JiV1Udmtb<*i8g=_dIwX@m~`iDhbeT5o07?2rVA?rdHchWJTql45T5<6))XKK~I?JZKt&~UZ- zy~1O*oSqKm?-zHrh}Ow?sJV^s-$+!}tv$QtjyWoQOPo^n?pFEf<(GwK@1r6P zAB$I7*onA=w#2<|KgbB*;lKT8Dw$k1c%_jFAH9CfpV4Wi>jb^?A0HP^nxl6&77VF^ zF@!he<>f6t=)Fh7KK|o%(9YSN#d$($aXH=U?enMhhsRB|2G%^ecvOOKO^}7f?!+sx zBm02c;!X$E|L$)_!8f-y=&wO`wQlptWH-jn{eh1i+q5cQg9OPrZx&TmIg4;g(d2c6 zzQ<0Xb)hTFBiDP+85%HA5nO+}05<;K_kG#Lb^9`v>Y+5+W2ipF^&0^`+h7iEL6u)72a%^-tBN(ggj{HzFx3Eh!x`9f2Y@ z{fF|%)Z=!J{`cedjdv{l^J*5!NIy`5y1*xAjzYD5MuMwTcijlqI``y9WN4Q}f=KJC zT$BS-lR(iSD=~%zZ0LM7)P6QtjPqa*Gqi_e{Xvk!lJ|UkqE6VfvTRVhoXyfVZ1Fuj zxVFUInxmuI&-pvu4|Ci7v}K3iCshywby1b9#VM~}%5_w0ogEe;%Tl;HZM*NCp8-yZ zHq)mGp341^Y|~@Jwy?PCKexN{M6UrqOle>~aINGuHXLXE+F_jWYRZo>oU%uEF9k%c zfYZ5N{>%4v*wstw>zBvKiN=u9`sMtGM6l5;JX=n1$4DSJdFgqwT>+Wb{nf}q;;rf< z4yp{Y6)0%EM6}ZDVTy84suRy(&!jQ0XMfRq|1!kYg*X!_)~N}N>p8Wp{BlHDfmJ(;u@lY3E~_(sJ@Jte&LBvDlzpIbCt8&ZD?a92c{}y%dlz`FKiSh9}KTRKXChx83IlX2Q zQBRkH)RFp|_oH zgGQt;S9PyjkrAGgg4Z>bQ}w5S?O-VcH-X$eRradC5?OeTWxj{`>$K99u~gwHm80Zl zIJ^36c`f|gyhEP&V&`!Bs~{1|X4Mc)>T=^fV9k5J27$uzt7Xd-0fxkPrpCJJd!z3P zf*NZS{Z_3#FHyxA-4Ryn76TQ}6x1ug3wi#1bruHSP( zMV!OkmCG4VXj$X{{H@8OOS4BEYU4OL?!#UD{Gc)*^NkbBcv=tmO{yzeK%4h+hix}Z4Dv&?m6H0=li^#=lAQc zUiB*1b-mB)T#oZNkBbC**O>5%EGzNw@Po)z+I5`&TZKS&6jko1%JLg|7nf!<@!5J3 z&vd9qP0mTRO%_Y`g}R4&v#2qwacMCgNSkYAf zA`$Msk}MUbc-X`OPEbDyip$*BaDEXQ8rmhnQqk4W(6GM7Xs=8_3NPRWIBc~5Am&7O zo{=)*6#bb`GOV>VGH8`TIH#R1JNx^$>jd6XK)6}lT&->RLNeKUwz{m$=7F5hXt<@> zYQ42`GZ9S}XAB($JgQv8D$cc5Hx=ip&d6bL9~&{#U2;*Pg<0NMpY1>OqjfWba!sc# z8JRN%|7oVW}6?zztmOvZ2VvQ4Tz(Ebt!qg5f79RQ9H2` zN$<@P@%Nyv^ORwX&ZieOyxl{ANfkfZgu$ET*FK8klqaHz^rz z$-l-@Mr;%Oti4%jkJWWFtIjo7^9aw!fscGBAdMczWwFxYq%(`&MyrOO1Em;}W)>e@ z`|v>7N;mPh;euT-oI!|sRqWf$Fd#e%A?jBy-)#D6?g{u-R~~O*1vV)-&o{C3?is0p z1{oh;(I-~zFTMGoD&un|C!luH0U2zRS@cjj3B47|$~Fe+@TQG6%Uhb}GdxiYg1NZX z32QA1OUfxN{J6^}mJQnYe)9x5*())%krX-V%87y>{#Ps_7I7)MwULwqdXH~*Xy19&p;6uwE>X=&xEG1k6ZsCtEBdA9T&D<#C}};zbiWpv zuvu<87J7TB$xM|{b3FH9+rpuwzgRMxx8=5inyM#-o#!fM6n57~7pHy*&$Qo+c6_zX zp%Sgx#kI(0%xJq$Y3O*M$V%@exuxwq8S6i$@x`o+k7pp~KX9^@)T45>Tg#?QHta&d z@cj1Hz3SUGCXo)F@^7u48%j_ex7AOLOOCQo3#x7no>dgvt68DD_^b^~=>1!E|c%J54c5-L1Sa~z=U7u!)kTH$^GyJ5-hd~D* zgim2CY(8N_O)rV~NbQZXq;yPk0261RxMUNkwTuqxhuxInPD! zxb1UmcMn^C@xDdXqp(nF9|{<}Z`u5HZ2Cm{pK&?iy}e6Jn5wh$YYkOL?OWpG{%YVq z?Ho4~m>!prZGcj6`zu+XI_`2sP%iXYg^-To{EdTB^fyKXI4A2ZEC zU+S6v43{Z9W%&TF{UeXx{%2p`X*5y@_Gm2a@V~W?mLbh75J`ICw~pC#X%{_?0?^6bYZ96*{@1I@w+dPZD%h%K zMG8%kvKehIW(RMi(M=zV{YZueScos)!zLsR|hzkmNHBB&J@8!zJSOen=qXtg(B zK6@)p+uZ0Ctd!D^ijg|Z@d#zi<~3ZuCsC(5)9t!?EQUY zb#2Xzw}0yAV}nljF3T3;M%Thw`jXQ4M73to)_b7*eLCh zlfo!BW30&j8~k{dtb)Rk*5>Bc_wR0VEV=IO?)>TJ2&ywR-#lykAoh&gZ?+GvawUCm z2}*P&SemPG(N(*+?ZkPkSiIUC>~Zal!dia@zShX$eVX@O%_@n-$&X$VLp;R} zLq`;umez{x2hMXKuM7FPBt7aich^NMV=a2+?q*+)HcptC^sSmsknU!3FID&P&U>FI z(fTug?{XIZ#N_ZWi9dy1<(1dgpV9idsKL0|QKMyET2q5KlhkI^(~R+Q@oHgs2ojP% zd;6U2objP(zwv!^Z;qE7KuA@P2$`(BTg=Ea@qOTpc#6ku0L*WLi|t*jTC4nHE`lII zVjyC-yMFf_o8GsPBw~xT*tb7UlG04f<_|k8K792KGekppvzT?QWumNf!|WxQsl31( zZz#d)n5}e&C_*Pli-0N$tg9!RbL73J|m*VA?nxZP5FzGc3pyv5%;!9YL=C( zi1$>}v`Gaf8>>xc5EQ=`tEed2~COW$c-1J4f8_C8xb&;IInl!mD?5D^^2+- zlniDUpk^cF4It>c{8(DF-sm2$elykAWK2j0D0D<*%t6P-n z$pmi!FE{_>1{syXT2K)6|McXHF~{>*;`UsydgLGVVU1nptM4-`*wsoO&6|2`uYdZ( zKxVu*NJtM`D^*o^SbAL{w*2Q$Bslt_fv@1!@&KpIpI*zqZ(FsFX-?gmy+tmAfTK8f z7oiSUth9!nLS$YuuKygbAKyE**CGY;>$)my&#}!(^D@Ngv7I@!O|cu3(hp zAhoLV6ZfmX-lAq{@9QDq7v0rx-Yl`BvH54R*P@&aJNfV!Tl^YlR%|>A!^Awu1i@4c z8AZ=U(89n^-&b!pdQrjzqCJ8{iAa6y_=w;b;hC+=!X^m0=-%#RxIo8J9-;m@@mkBs zZyr9yik{rx7ekMVgyO~Hh@>;oy9!bP{AcOQ&a^&*=7jK-ThT)-j(XPjQZZU`nNl|( zFY*a@c(_7HI$^-1#;hR-EDM%Ue_IOiqwfzOtMkloMixGg&QjOpBQE{@+q&)E=GW?C z)ys*%A-FEE_;=0;=rsL0FJ5UQ5V;+iTYT8ky?OD?s>xcSkJuY3Rd z(WBT?R3$>Tv{V`)1UFpT`6%nu)%=~+d6{Q&Mt{dr-{X_1uSeM4*u12lpTX}VQ&FHu zu_cnsyGp|lBn-SNKxBA^l|b^-y|&SWULrOp+(9ztTXHBophDF-pU>}+uh8sE!DH}KIlXThBW^uL2*Px`hJx9+2FRc{rT&cwZ^%VoCa zzhIrF5K6r32f5QH4C4rdsZCw?eAx>~W4NrxYHO2WJva~qvHx5pV>r6SOZM1kc~m9L z#z*QI+_YrGkquIh?g9`!!wg@Ts+%HxqTm4Of)nA8O-a8C79s^OXG8g5xs4Ind1bzI z(DgRtkFu|a-l^2TY5Q8)H>p1R3XX@K*y8WQX(wFzMb*YoY1VaY1=H*6>u0v=n`4XOpp{BS$W|4+~&v2w>qi|3ogvEj}8T5eH`rP2Sg(4(*Oq zgz-f4Kr$oKE13K#$j`q!h9R-QU7aT!b3R%UVR-4?f2=b&9PHXD+CRXvfp-Nvw)~8e z&mm1lgR$KMavpAO7RKa3Aab4j`SYjQMmK?piS)z%yeOQ=2cS$9K!sfvMVtg^)q4-C zBnIv6`|#x23cVFjgOHECr~QoJGn(k2i#mL)@Iyxmf;$W^lhxGOz|d6cNS_tai3y?7 zrT&Ufd*2j{NZb72&qq0B(C4eV0FxKDsY&-azctd`**sZu2PUKJ?;sP$lJ5hPzI*p7 z(Dj3aLiNm6FG<3(BJv)sh&VgJDH+@k9UiRo8b#!+B6bpxj+Yl5CT<}GmjQ6>1Z#Tr zd+{#ML0A#_iJI7HsZ&T0O5u5SKHqG2FqEt(N6+`_u<;O3b&)icYfYJ!jK8n$q zrPnwID14IM$JU!tIu?GX79p7u?=c#_o=9i0ulNTnCir}qY^l^4aCj&=dv*U0B7B;T zmmaSxd63`0l~B5cIt9hE)s8n%d@LrTBS|-yj&z4OoOYNm13pre#RKaJHl(Q=MA2dR z!ydT#e_2>ieiVH5Y)dvj3VM)4R>LLpva^zRG7vnZ6y#}?BICAU#B&(^N~LPQPf49bf<&8L%x_yOr@)h7 z2#bAF|9<3ywv}-3F)-yc>%Jxe(3OB2z4@i@B;ks+o1!gpD;m6^K`oL*sgUfP@jrb3 zIw&E?cUG68izz5Al`WPn1v5p4PqXk)iWC^da_70pao5&!JPs+vK~h8DnSXs_!+hgO zFsRERO1IKwQ&;J!1nbXQ?D$AJWWV)efC-VLDx&}G0P_R%xi?hs=WH!{QRWBmw8MZ> z62eC~x%9woh+*TA7xa?j4wGZyi-pyGvq+ubT)wf&pk-8#+O8?2Kh5vqT%K@?)0?4L zY23G7|Kxdn`e7RsW{8`QPW-Q+rouvyn-PY3HrElhc5ot%kgWf>Zy|jXlGX@DJ6Qq- z>2QX;_q0}$IUc1uL1O?0s_lT`>pxMp9I*z{SVR;qfI{`H(jfePI-kh8 zW%O2Urh~)z@LOd5-AvvfJAzc496Hu}CcTK$Y$Bc&k%^$y!DsE}NJ3c{F&SBlql*>Z z)5p;Ij1buoqiah?DnZRzOC6B>ZEVTo5hO>y$DEis-p^nx^n`qzA(`q^{mDWK)aM_^Mg*W+_dz1qq zH*)pIsH7v*2W?-LMc8#l(m?llwC~0f0OBC10`}}PR>cq`RJh&T+?-Z--WG>Aa$8^Y zf)W4UeGuI@kIzKOWIeg$vH0ZwCBJo?=Z_ej&$t|( zY<<7^$F4krF6dw++1r4jawq6S7=i%HSYS1Yq-kUQkw~#0~Fco|WTU=c1 zu-%?!jb6O`@8w^rZh~6^^gF+p-Sjt5zcp9&w&8}rVj;HmX`IEtklBe1)o-nGlo@@By28W!M0-lT77G8Z!f-#-zNb7F~lRY zgi9W6+pgs928#ZwKOOa!?r3u_w$)W|ruSnTZ)hG!OdV*T$zHrjX)J8}au~+++G7p;JnGXU3zjl$8hd-tq#k#=QoGn@y&MMoer=NnO>!s04S$zh{ z*IQiuAAaExO$nw0(6SS_a_>}N&J7gvwI8Z7fo8a2HQP;S03@P@Wu@z9Wzt1JhF$Xp z--O3_$inakqmt$L&*TZo-0=l>#9rL|R3o!rt}g?E{Dw9v8kgsg5=MwL#wV6euiQq& zQ3OsI+Dtb~m|=|oLkxILOncx9Rrf{lA7mHCBpy}UzEI}RHEL*Fk%eh z>Q~!mUDUBBX|dQf^&yV(y^0Bv=McwU0H^9~0};nqaI}Z#d^cuLfAJYL{{`K2fz%*+ zB{^byY7qZpMhFT&T-cGcNE&^z#95H3D0Gi1R2e=~2m@fPT*H4iADoJWPUZZd2GMr@0_z}0U;BMh3eM#du<8n8V=a8)3g3-wc1Dso!8&tVKDV)u-u=g1 z`E(SrfXl-IDkO2v<-aColy$3S8zMzP{=xOvRk|eM!=3RP(be0jN0}~Kn|k^Dx1Nd7 z#(0Ui3=r2e$`cQlj{pG+)g$(5aOJprGd>g}h&~Wpsk+-K+iBYXb-~+x5Rf!U^sD9gQk*@#GQiK4#bBB|nOZ zNUTwB1~WASrhQ1(Rc5}1z9zm7Aec9JMwkaUxBpp@TGmM^VWjVEgm~e9=3P)!Tx@a% zardXGmMKGaORrxj+#32H^Og4l)sS0dc!z1(UnEjr!_I=3r!7cUyq47Swo2cJ2Wt|k z`f-WlHj#;G_|~HM^mkvqC07K&RRuPkxbw~PVn+mo*S`Dq3{)a`&)l19jb_o4<1m5- zzi1^L&vehwCCOXYG3I}_kbMf%NHMo3UOhc7P=mT#tYDDh#1bS=n zm#S@u$;xmIcD}zm<`ID%*?H*gZ44}`!<$Q+2}s!(_*>QuYxr>PvGIB^&v{0xvaJ-Jo>))^y}D*Xz70Pe;AqOfX0thEBT0athvBO1tB-R$Hv7Q2*SNMr&4bqBA0 z`7UG77q9(OZ(){1>X~h>FGh*NY@y}#!&?w*5aP9OgkG zs>_sQt;}a$#Wl#RWCS%Fbw6ANuZh23RSKD1ec4Ae|vqgkGtfUPd<< z3-9&x-{j$&MW_{UTp4ZN^ft7DJd{rKch>Wx>I6y>CP_p0=~{MW74V9xcD^Au!-og& zS79si{qGxDxM#^qoY+37C&cFR()v=Ak)IQvZjq_1}@=2A4JOLN@V`FXn{p?>} z_#}2&iBcGgF2Ix=1|RQIx3s9fk+CSlNFUR?r|&y?j1FmvmF3Y`Z4b}5Z0w^}&P4Fn_Kqa#OjX1w?{BJB|LbjHDj)r~SPp4E1T zIz^3inL?C6pPQHCxI=-V?9`xcN&q%|hiGDITg1V^rvA8HLa}>M@x3%1l2zijSG6Ob zE3}%0uq7_NMEYSI7r8hN)0&)(LwpBGBB*xOL~A^2%q@`nV8GC%VSzQtaHtlkKd?xa zTE5=X{l8!utRviVU}K}ywwD>QXOPxS;A3k6&wZI8O5Ay=W2jWNn=?s_#cuZVS~~4K z$)GIvg*68UEgO7VOLrH9Bb86p)qAfO;!&PF99Ia&N?DnYp`qsAbssFQ2%6E8Com=F z&=YLo{NCD#&Tg?e3Zt|m{{ImhWnTAb+bAIgzgK_%9Pi390y8@8xPXIdGIe7dCKP6v zsYxf!UM@Lg;s#_RkoC}!w7=R#0fgv(@w;aK&a9=PWHwb7m1|bH8Se2NMDgeMe$^7nYtD7`&x04_di}HjWxzjgNRkTM^rOyI1V@(~ntRE24GF6EP)BUgl2%k6 z3hJ%#9JDQrLFoXvXC0oqI!ha0!s#H<=(I{>MU=$w@(AGy$I&((s7T-yQuMC8Z4gr# zf5UuMgQDeG-R11&)|F0>K&G#$HGvG=Mb}$C8VJmGM`b^`4{opE?&6W@#stcCd@mC{ zO}~BiuBOS~pqPcj5-xzNDP7!(z6b=G;fmu(A;hkvd~_%*=lc*&XxI>qU2F*Ha#JOF z=317hlpyj9sEpnIvA`!Uc?Q)~q-q=ZSMifbz{vh%KF7$RhV)+wMnMr6Wm6qctIsWa$SE4r}&4y`C3n`Yaig zRo2n7O`jcqQ`nPEyaB~5G`hr#6f z+NO>^l?H)2C?WZkQ)K=xQDnnOHs*)9Wx{m0okbcWHT=XfRi5LlZh|a`r8+lH3az}! zG(!kyO*o!!^tSp!mxT^j2F&k=N`LdfA+in4&Dl#}`I5`OLdN{+5uz}FT z)Qjw{cc4q~_3K}~{rxVmN}JtM4Hl*Douusw(3~*X_1Vy`+vlEza<#a0v?hdr&|SS= z4RyRD|np-$!A@A^G|@BQ3X>L#e(J%?xVMgi;sLeKR>A?FDESr zQ`uESu7b90>5qQJ*7Cs03~hvF3Cv+5P^+z5(vvbH`%-3aG%fKXHV{9RB+y9ZT@KrR zyPb}~BKBhvjo*3p3W4$C`)4hBZz)h>xA!0>B=}WQeWT+1ttzkD#9zgu)cRnF-O07@ zQ^D2M{NLO*6q%qa2em<%tsJDnXtkbYG_c9|K9TMf$>*9NBpCE47GIr%tjdiHo}6=vm^!4`B5LJY5(+1=k3;W;X^%UVD7^`H}FT zVXlaaSFgOCR^7Lcm)w9EKcD>1m8*}{L#`6uuK10xleTXN&e!j*?;H%0<>qw8n_U^Y<;(cb7FrH@@y*Ls{J8fOtq*D?1LZF$ ztr=IifBLiaXGUFbk$7^=jb{MIfEs{DsHydS+Lx3<>ed6ln0=wx-L*(7kh9Ul_SuD_tgW`Cizi#b>b-)WbYH zQjSSWP7d*Lagsl%J@FSF{++9M_k0^XW+q0@h@6uzgcD0zW)u5_=pLo8s=f^)%)X>VuQFU=$GNcD|*4m!&2 z-G!mBy~(wifEqu4`wd%pb9u(>(_ZBH?)JGo#P}9oEMC^RZrD+XkB={aa~^V99MYFVogQx{UFYC{fG&rhVBjk-O@XEaeP!>305SObGD( zdN{paFj^%RNpbi0_h&+jU3%Do&ZXgNFb|WhIUn7Fr?WMfvSf|sN}p(^f*R;3GF{Gp zH|!&d*d4ve+*ji)rR3FPh9OvJ>_s17Owwwb+PhhK$8y?tiP77iu-_`@r)`>jPVc6L z&r0Ia(2b#i+X^JBcl0Iq#Yf9s>v?t={BqW299DJ}Yx&KxA5A;BS}#;%`$W4J7br~Y znH`Ro-eVla@S>9YIG`XU*!MdBe+^v<2^9fD*SJ%pS{wpz>iMOp7tvHz&24QPFSM~* zKl>PQioj7Cz0WSbg1$e6V|jb2b?tW|2hFq8p(s5FvVB}3@piLmkydi(O2d&$w(F(N zllX;V45_-WNU%C#wi82n0A$&F=wIU#!Wk|9LafdwE7Oj!Fe>_N7C7J#e9P`WvN{?! zXD;dGa4N?qd~k@nH0(_OSZRqJ%eyqRcX8dXwX1vjj77Gk{nqhUOE#As9J;&$_Sf%y zc*HcbNfpRi9`wPiIQ+`F%srJS;%L|YLVLfX*JRp^{1Z0q${@`*L303-td8h8*lR=UxV zuIGE7`fZHux{>XMK`r-1YfcW@b(a0MKYh17ofHRpdk>HpiESH8leRuSJ}G{Mf$5ft zyKNd0u`UQ+{loqOjn(LM?s$N8nlQvyJBN@+G^f6OZE25QekZBQoX+3xS_u!0t=3T$ z6FPg#S9D`(plsB6Pcethg+J&bUHhaVefILqW@C~Y7U_Oxj(Ioi2=9+YzMN?9^|P_( zRBDI2_f{`-d`cAaAmksUX!je_I$__S)DKaXb5F6)0@F*aD=t6#pyQBe+B zZS;qaXJ*>zI&jl*hJ3H9tNT`KB+`6@Kj}@~0iiDX-kgjrtd{eif4|mLdGP1WW&O`N zHpwBOFSpF9!v;4IGj!9}Xd`sIIBLk)*K31)qL^7TK_0hY zklvYM0lSq{RCMsZkn8;=2_uAVIm{Af1VUtx&|?J=+57vyePFhq<(y%B_cj*m^Kw{~ zd_%omeeK7u#`0k4ATnlItL0+RhDkn!kFXDJ>Rncc*p%6ta@;lp?LP26-;^+!gjQap z5!3yT-!zZc*K&<{5*#RKEKs)>A(qwC#EIH^vA2T&&;N~qsURR`VT4zAFI{oRB z%NXol>h4gPu^^5O_l|yar@PE`VQ729!l%=*{encdT!2vN_C}cYE^CN(LgTLC-rs@# zm#s$Ermb7=BmFuYS1Xs>G#LNEHXNPG_Pqp(%g(7aJD={>H|?)!hro@MtCZBH=H?Y2+=V1!aAIPjOvv&-K0JhdHeAh-BDcWGe{B&EqAq^lM6aaR zpQ3A1W3iH!+f*{vz}R^9rt>W?N3t7^@k4=!qOUwu1uJ2G+wV&KE+^@W@O}(8)YzSO zp9gN1>rO0oQ^>0kdhHh3nBcR!st;3+;scHEE4qJKD` z|EL2#r)Tz&?7d9CES{Nq*R;8u`e~@|iMM^@HbN(gDMJr@%Qx63i{pn}e1B4>?r~1U zvVFU_@G!K_nHP~sdwBlca5$KAd-%cvy&OU<7{h)cN*@YW(ogC27coRVQ#G^Qy@Q#2 z%Mp{z!$~cPiVkAdNnpfD9zpC3KL>L+K0je)UpGSRipt9RN)6*7#w@wshkb4opE${w z!~#3at*kTwqdv%im7AiVN^%V&h(qYS`3T1plS@~znWR3{uhNGBg%h+K%t z>CRfw88DbxQL5Ap>o%J)n@;z1-0vou5>eFXaG`p9J8kJ_i^HYrmFjN&C$7%q7C}+&h@w6D;NVe6mOH$JNhW zj7Xdyx~)XFK98NVl^6Grbwd~ zy&fOq&VGC*kt_hn%UY7RkqD?Pj4c0Zx8NducM>r-H?P_7MbeZZcD@NGk@Z&IHUu8o zy8AoEx9448=T6F8wsU|`x#;xQ&4+K&w`Q1DLd&u&*hJq%4Y~WyY&1_6C)x^iD`&G@ zev$iV^>3O(dIOA~ktCdS_HleSmqXx(di{XHVU&}4Wdn)G=AzbpxFfg)*wVXW3$GQv z?>Jf3duWs9b))z9^*pdXe)1-3k}TI9$C1xZTAH3&T9FiW1Ev<8(X2=k9$Va6-0d+H zh$*6{XrH;_cK`m@1b*^!Ij_~sN0YR3faxmNVA4*uuBQL8kY^-Ym!yAGIKPOvd{uFs z6!K=8N)+tOUn%!&Y^rrV9x`Vnr9bpBr%T2*o1?Qih&6k}$>)wl_w}7mgS_-@EbkuA zl;_NSct3sl9ld;kVRe?}w5uK`HNm*dCEdcpf(^d~1171-utU9Q)i}scBo81V0}I$M z9i2(Gv+20karR{mcUG@Z%o8uVew?qRGT)JzIx}-JSKr#LIyX1zoG$CVgtp9~(zE1W z*Y0*_OWC!^Pe5K_2WgH~BybQ~o>~|PVkn|Vr`z}KkBjQ~Juxw{jrV_UtIU4IgrBTF zJ-WoW{1sZrl`t$d!AJV+Ey<#UJ?qnO7NMPB*|+}JPH((2Crig;iLy!y#^k!m>6%Sj z`5O{lVYXRSdP7-q!0=-q!#5PZv=!;To}Fy0QJUsthe@=QVBC3y4}lxKT66yn!fsvp|fWnuidk&pF9PxFWyTTzGTrsCo|t(Do2 zdo9A~Zxy>D!X33)0wq>ad~k!80wQwNN*K{Pih@iV)|Gdvv#Vq7OhrJBx|8L-fO%*( za6`MRa^;%5so%%?8O;HS4+m69xUm@9)7`hTKN;JWPLT1lU-Lw#wI4tZ|x`0`B>pOyI+dv zDTnzFTx-9-*6ap(cX10=Q=PKU4PHo7!?EQWdDQp57g=S|Zk0#Kzowm^{j-20wdL(K zm)50yyLj>*6Zv6-%-vsM=0ofQ(S{O8Bn(3xh2BWrf2crK2kVMca>#@y^6W}%Jyt`pI)M|S1F^j*x+2>yZYINBt>0k z_1_H0s*0qE>SaA8P>4R})egFSOBm`m7sPE~36jL-X6 z{#-t5H;JV5H@bS)us0A9QGW*O(>?cB8L0O6x2Cwg&QCq}Y>nxN4iKz?YW!G7GYo?G-fx?LqB$UQx}$O(<5 zWfe^HB$vg^x&HHr7TRrS(MY1MJ&{qitTdR9HNH6dJ-1HM)z7DRj!;88TAJo;$NDfO zcyac=1LMFe%J(%O6cdhFD{wL;r$bvw%k@`Vs-dars`(MbD{3K$DBTB1t^4vn5TKJR z*k)h5Vilq$IWMPCEl&(<>$nw#Du<9-*3!Cp_9*wjO1akG?n$C;>!x(s8Gvf7kzv+C(En z3P8l3BcY!9?!qn|f{jc^3gU8ArzDQPaEYkob!jCOoGUzp;GV?B@{WG<{|`TmOg;64 za|!7QCNf`hp7ic#_mPy7vu%;ht&*yrm-EjFc??TWJ+O~JWJ}adoH{ZuhSUk$+L|Rw zNEnyU$khzAgDm9Yq3w!(!Q1-!am)I2Q?L5H0;NsgP!tIq&?Fw+eChSItLrv)R|I%M zgVCUu)~AQvd$l4Y#f+>vxQqPY#UhmJkXlq8tvFDoeqq0ztJ%QO%meH1Uv=ZnyE zS1o8_;3RNT%`UPhpT=Pjs{rqq_ZU8;JR(h%((uC)YU+tZ?9cvw;xl>37;b={OuQyw zYgu2aJtdbMIuO0GyS=gGBgjaOu(k!00hsU6s30BxorDwM(&aUb|LXb!c#^W2n-dg3k@qbX1a zv3)#={~H!b5^E!Cao>X`yq0EkRFvmH5AWah_AM72g(w*-VN`vN(_6X3ECz+~Qu<9h zT;J3s)wR^*B+HZJfEh2VyFOfuqX~3Z5>9mVW&i$hRWJ*hBWb%Uh~ZVM(YfUv4+h*D z!2XJSwHoAwM?yV#=n(zL`Kpc*vMnHz%RVQm3b2?YI}|*X^UHi=m^DOkpCU)kyn%`& z<{JL@DkWWX`X?GVAu~YJyNfqv;)ugRdAIL1f9s2oI&Q1pbzUPn_ zsKT4K9H-H*h3?G`fBIy2dmUWE0xz$kb}Cm%&n+?w`P z3z)7brX}G?dvJ-rJJW|t{mB2a!txx00}Y+>M|E#>t&yAu^yjLNlo z@Qz5a4;NjW)CpUSE}YeAFQQd-ho-I^hJ1s&|J!Bj1$c$$X9~uM!iZ7wnHa4lVjovQ z2sucfi}4ya;r6~kc{gUjFeNymw-Z^ZLkXkHeKZ%BD9XmT`FplEzw3+1_ahZ&{AG~# zWAHZW`Rlzo*jfESKDVqF56n2ImZ?QPF#d26@Qa5S`;)D`U998*-mZfkP8cgbh=G&4yq>-~b#^GPClWt)_VAG$ z0;T87Y<8<^=vF^ACWoq||B09PD&qCyg_>&q>QczaHZE82^EV{hetjUt`4CcovMM~a zVI%GH(_)hKHwR25SMCiMp;uaSz|1ToCuTp%CXeyH*mugS!c(>fH6ttW1z}+LNxvXt zY(4;sf6eIy5F^OTd^>UPnhBP)Ol-xhp7xS#_8I95%vMrdPX2SgT=)Xxa2@zdvuWqg zq$EYq6c4l%(5aIcWu}Iv2EAUAo|D^}G+^urR;ZLy!};u1BFiYdOH4w-s}5NeoBx^@ zg)F3xY^e-s(b3!>IxE4N+vcbDWFBgM^>MxWpS4f_@ywk22od=?f)}>c?MlnM0jYis zg`u64!}uFiPsr-AZ{A3C&Bmd6QUxZf1sgOTvagzRqPKSmh;!DBoSvARWM^gichsJg zlga1kQ>qF(C}y!6%Wr!{oIgA0x92lT=Xgk&?@>MOvf{ng51)2wdIPYtFYsMf_j1Gu zxSmjj?DCDZeGc@HteHneMNOmiCI5xo%o98kro?dMR=^7C8zfF57g0K?w=(Rb5k335 zFDOKvT$XQ|UO$uWnOkLGCbh0K3ZWU7X<$&E7j$1NK>d?`z$%pD)Z+rOPG8*f9XMt% z2#vv-oG6lrxEOV964!oI9L&~o6lOX>X74#O!`L6A>IyII4mwLgN$A=tzlP%@pJBG^ zmp3QSIC~{8*3090>!HdJ-_}Fwl-X?yU;?FlxfN5F)H-9oI{=6e`+6Kg5U(X1wX7rG z&@D#ROA^9;qUJ>~Q)o83!26jmSzy;>|9);K$j~EQ=Y^JM!E%Z_Lz0;cMQ!!Fax?E6 zwA&X)ojbbvfaVPWj$EX6H<1_&)MjzDrE4Rh$iORn9k5P;Ywu!qO=Ae9Grtg;KbIO{X%uK zGp@E--Po1)o)t$j$^ox>@Xf!g(J)F%%+{!@B-52}9oGH;UAy#;1z33Yfk9@PJc@%e za!%urQBj?biQtg76zKVt=!&HNeLNrw?k?gnhkEUZ*Ia4+D<=~V%j8c&WLn)07bAAy zbzMf+*FS1gc69W}`kiSmici>Jszx+@7FPd6!JoSZqDvfqxeF^jO1b^UuzHK1a9!5S zl9OdwYt=wQLt`3h2*B--l6@ngvGRGr$;%Gi&uXMZ z7jo9sB(PIJnHZr-1EmWn@dDO{WpGGVV_52XI`5hu-AnW8Ouk&NQq#cWDl%;F z_{Eu?P~zc({MhAKSTr{*`c-AJtycw?P!c0{;g9JhSC%^AHRsDSR7)1S?MvuqJm;;L z-NmV?yxNga>|ceO%a5;TO3Hw6OO=9?w%ApzFh~3oa&z^;o_NIpJX++op9d^+wpZrq8VIJHhM)1fC_i5hw8AJ+bebUf zn5z#JGr=O4iE>Y6i^cAqB7HsL^D5}0G9Tx49Ggq9P;`RMbG@38zj@4Cl?OxeL>L)0vo^V&lz~nt%#w zw%dSUdxOywGFbbW5K9s;ro$Ih$!_2CzoNa6wF~q0{_2h8pS<*D66Hefbm!sDhX7%xB`ig4?t;g7YABXj~#p9=f&8^;f z)=s6!+SMJh%9+J1alC5ye&o`qJv+nZXH)M*pmZ_$+bQ|6*!2snJS4j{cvj}->Y!iC znc8tLu@V3jI`Vbipu2Kc*_6!C+i2qcZZHdaO>|dD-vgzcbm;gnZdb{|@9uFjwZAr0 zwZW&`fzSQ{93>ApXdXCd;t*k5sQD=-hC}Ku>aKGYOWlZ0L2d{70ONjvDHCtjWB3|k zIwv@osO0655R=^>d46|w7+!wxMKN*-1qR4*=Ok%*&}ciTrXHnzi=PJSSTd1ut=K z4YHD}Ap1kS2y719Ua|y^$W@C<(FcL@Teq7#omXK1ci8bTn^npsXUjKqRv8@!j`%F!kTOO*(O%{34LZ&$z3PLP=(tyxJimtL z+k(Yf&OAj^Kz}SQA~W5{;CtfAl)G4e;be!)CQZ?uU$~pazfJ12wn&E_lz3| z@yiod9!-W^(JuB27eeEcdtq?b{xhF{_WpyCvI$3%v&QD}jjUC}hccky0rR96*-gX{9rle{@kzCH`nSf1G zd9jKEB4dD+?v)Ro@9(GmmeQM9A;XcMOx&ly&XaM%Z3MXRndb$ z2vz_N1m!4J^C=@I(hLZc!$jiFLb-oHqvQ{CbT*I?9vVr3Pu~Xv99lX}^d3Rbi3pOm z$ho=%e_BRzUscs1<;N@dc+x@yrqVJiKOa_=a0LrR+CLQ*-eP5CowHLqGbUiL`l97F zVh6z5xgJ{&GiIeBv?sITvmcNaj!rQ&(?jEh3htd*x0y|$4mIG@546eX|8g+z*h4*lsMGxXdjn zDd{7)kNjLM;Hmf7ED_A;dy6L1l;>Y(XEV>u$j&f&t&B?u4B??|{y#2&pFzx_5VdJN z5O0)2raBlm($|#H7gRqKXg-3J7fCg9{U87hSnVM!p z`@3qs7I|~`w?K8^fy7H5c{6*O%y;=Fq&+HJtg?AacZT_7|xl$bSvJ*V#SLs0>FC$l4h3)^gy!M?B^b<2(nc7?6xWgBs?n%uL*MD`wDq))>?uD~`U1S8BhX zWoT@Cuo8f0L!vf7|%_4pyesEYA7Z+Ge2Jcv&c8*_>55`g$#O zbrKnk-ZGrtdK^eD`tJOwN{i0wt#_Dt4g-=16l6qIsD@cFLlJik7XT&frqM=Bn8YSr z`uUkph8go6P=7FEG!44`&1^7;%U7*^o%gZhQvRYX;^*%^YCl=$OQKQbc*zI%$1L%I6D9Wjt0 zEniPmFaeZS^>EtpLUZ;`YdTnDB)EB&6Sm=i2d}ZBpX;|s1!#!JSKlbN39ERdb&`W- z@8J;cQzdyP^7T$BUY2Tg;+gxyhYuaa8IsQEiZ+yMkgXe+YzHNwLdiG^On5Ye9q>#u z&0(7H>jKm;_cQZ$6KS1XLN4M)Gc3Hn;S#tMvZ7AI8LQY{Qv; zIpr`0u^Z7jRh|k@MIy!~3jZ`*^+L~G8DV;5*!lUhau#s5uElUQ`}c~i{0-`(c}9?a z;x(wav9fCm`v@M&eAal_827lq$K1yj>SFa?xEKWhg@j;VANwt#5}DmHJn3AN|1O88 z;MTN|heHy_dC)ugzR^w}ig6^c9Bie0bYpP2q1-mh>u>!X|7tDJqm>>pMzadsC;A9) zX!NPl2`}Vt2AJO)2FyCAV>DnP2db^h7^~-*y$0p^L6%}q^Xda4)S8bLGF@JS$6BDF zM=?Tx!$kbn9l}oXYa`eLzhU7=@`7lLc(+hF%%vP{j_nbfVGPmE&Ix=+7a6Jg8n$u_ zwxV}W;Cs2=(a~lX2%hEy0yI;~AuFa4tvBTr73cYqqOSGCl?fL_UR0ADn>9)(hH83K z;UKd0a%E{@q&5J5UP*tvSI$dy{yW+F0yM_R50D-P)Z!Q&I?7kxY~BY9l-IQ9J$(dF zDfp@qrJi;iL6YofS$ z4?S(2XYPLYF&;@fs&ze|V|?IL^9eS_G+{3ye6UEb!M3)SeQX5R+O#1Sp`=Q#GLPMD z4@qXR2zk=#<7uJhb8P>9ilUn}owEckbHGZwhf)$}o~5y=>Fss~5s}H0x{B~l)VV1n zm-R6phNKBO_e!N_nXif8xi7h#sLLoyK>K<6(|+qLd}tn-j^!UIr^Z*RoO0_Wdxn)efR9oOELue+MTFP3^JR5 zTI4(WQ<)vC%Ic)BUDNq)$(1tmnRKB2pu7nvo}rEDBgjE+Y3NIk+BKHHaA)b)``MMe zNvxYh;gb#=fbuj8ykah&Ed2)*L7FwC`?fe@`uG&G{+37wIy5=p*l_|@F&&upn{3%v zb=dW7d9IQ6VxU^E^AoBch&6Wu6WaQ4&dJj?^(bicA?ZhRpNSyYKUSzQ6Au zInU?0-s}D6cfIYu>e%;v@4eRAYhG)2TlvG$=ws6bAjPQ&Mj#plF{y0)I{h{qung|} zML`{h4TK=qfWSL5!EhVEd=K=1*Yam28*pLEB*N!J%Xh#Ktj)}>+}Y1+>*CVB46a#d z5=j|R@nbU3Nc2H>QA9;eoEwIax2@8m`RrDu-d(qLwBkJ9eT?~qNny>*6|1;mNju{T z#>o@!3S<{&;-yOyvB>fpRy&3P zyOUbO=XmFp0`zbGoLt-~dudKy}wv;PeB((n|BGQm>NEDM`0%d(kaO`iY3F zjsGm7^jMo<&yv|3wSm6%z8qdpMY6QB!b9d_>=YCbW(Rc0F-Zf_ddtIPhxjYr9*i3=##oHUfVj5W5LC z^3KpH_^9oJAZjrn-z;_E)`A%{%W!Ja5czmV8pi$VsRsDMkoW8NeJ*1MLm28ywXhz$ z3laXz$MU8Ck6gWvq(sw8x{QaWhmc=wZ=^`?{q==cg4}hsPr_w?=w;S^8{=LMtE=9l z^-ro8$U~!rx<9`0TX_X%NQF`&FVZZiwltQDIp=~{OCr4dy`8q zxFiYA$5BobbLLW&mUY_?<+UX2A3E+lfSi8Vdk$jn1}RY7klHP>V?g=O20Cb{l)>}j{KqboQCL<6E)5CTreE|1 zaiwHwwTG!5f;&&=cKcL~%bYpy0yWN~gyYF2^Z-I)f^7K{$eg2IyO16RiH!q8giB9I ztGcN$3+y;F81Sz6r!ijXx2B7OxnxFOtUMc@eB|)e0M3~4m=3REpoDblE4kMm_MM%i z_C~E&bnMp>0v@Nu8s@I2wE^I`K*sNYO7oLkCawK#RpxaARwJg_)m z`KsC9x0VhKu{uZ8SDsj-Efe&{%}}P#X|Y`mJ6;4X6uU@QQW|TtGo^#4f*|~Hf(eaq zlad5<#~DPpYBsa3v%gElPaB-}?gA|>Mm~t>iIr%5kg5m?`k^vX2|NSkQNcDeU)nA| zl3^Cj?40N7ehfOB%hk$>JezGy_*^lKSmh@iwhErx7SV}f^;#XGF(L=nF5xZ1TI&NP zE??LAUAp+tnK`CLZ2sA@({oxv)$*MIdCEVz*YZ2K7q1<}RI*`n@--;(%se}hHV=dj zLr5>Nn_rdiN1!aQH!V291Z1_TwW-paDlOlA4MeaX#NiGcq@|k%j^kuv)a0*WI8FE1DooM@0bB9504DDfZD=14@T38`bB306()fVUW9Bp?0 znn>RB2(2zjx2fn->b%oy4Kt^|25*`GUp%slm<^|i!=Ai++9uOtzx~N+TY2^fkoS=b zY92=B`t;wLkDm@o6^la$_CtXr{pfKj% zsp(2LY=6EzGTIH?t}3jQR|n@H9x4g$FmJb1CpQO0UGPPIT*5AtC0eUbYJ;CIDyi7m#0EAZs9u48EA6Wfv5MWc_ zT^k!nZd?I=jUbQiC^rWr)L4pFAsAD=F~6wse9Xlw}@Pw1j*pFeTAe-cAr6V30q z`lw2k?)cnkVK$23Mb6{tc|A&H8mk%~_J6TuEGaO_eq8jl^0uB{4v=8`8YyKgN*KD< zlctq9EP!gjT=US3u60-K-19+aMR3Tkzh<`#OD0$L)SNZSnI=Pe)kZpaU!E-XQA%(L z2JU2F=tJ=j;S_rXI;aH>0Q^nge5vur3PD z1J(OLIDGtgWdsNo>Byp)%R6s?5!-zTTx>H1+NJz{=K8rIP|Zd~o>wD8nGrcBz9^~n1~F6H^Qup_ z!_ion5%*MNy{OAj=-IZm^%&^CBzzpjGiET+*w}cygALQ*h$Y)RE!QLe7GeAy_!pWH zX=uj2`IOBoJI{7)-6!9re&q@hb-y7W_qT@u>0DYo${FQN*B@1Bvih*i4Bbf(ss_&2 zO#!Z$u}GN?ey%xC|J43ue8Z|S)>s(BC*i;A0}j7u+o)WW_#{J-JY(kp?0q>Ob9PZ|*;S+OH8gA!maJP&b+;*-8``HCtn(iw;7lI)y zsnwhslMIJL5?%+S&AD3rGJ~V=3%Hum3inpK19qb-5@*Zby&EmJNxQzd9l7QSQVzfY zXV5G!(VRGu$o<5{pOIggH;(R*rqZT1i_YRu3zgR7p8NBbLz{6rJ02F z6A7RzKqsJxgl8UF6Kxb+?)+C51j6;E(;J!9yP-Pu4YW)S;?k+IJQ@xz}{^@aG zA$zAPo_p=PPkXvK`OXu?UqFL*H!>Mqv=?3RTVd-iulUxwb>Be2QCiQ&C3qDiw&Q8y z6Z?v_gzC8n3{d-v*aQ7p_6H#C;$>b?c-j(+3Mro+oEr10Q~P|Hf0~l1R}T0D0hamr z){^_YCpo>Zl<^Uk(eFXaekfHXZQ+(GZmQ%Q2H=w_1=Y!<_Nw{3JrW~rl z10|lomrK8S-i7 z$Rq&~y)MRmOL+@%qIv(&e z#h<%9cVnl@&+turJ-q}lp&h1%H*fo9!tCvEh2GrPwW%L)%J(k)!nT5r=Pm+cqslZ3 z5X{qI3NCiz+@^;hjJfhswNHn~X#F7}^3aV)mWJ$H z&B%tcth-m#KFC~LLwTR{83X6nJhW9c>fQDL$kZ`@Ym&O?6K`W_ZjL`FA96DOW`>2k z4G?hiv-Mlno2P)#Z%Y(nL!JgA%MB3T2pvb1w@TjJOsz@uV)!gL>)Pc3Z1tCtMZ|3` z%;@+jpcUVZla06o`#W6@qKjLzkWh63TDCUjxmcn39uEnleNb2dKs*Ba`6MmGdpa)A znAP$XNPy7&PkB^uF`DWzV0tk(Xkq2cruM}$1pO|CKW!s4gK7uMA z^+93D)0@s4R)}Z6vVKWH9o35>gMcG4^rS0lHUfmnSY-B^EGXVu}3O(&J5+orujq3oa1E z67Kfe`dLh;tudxR4-`tM%?G6X1!iI3zPKXi$SDpx9<;ih zfHGP`Y#uLi<}OxC5|VH8lZcCAwO!Rj+OJN}Uzy?#)t(yLumkoMi^UL5>v5xWlgBRf zb5wxx-DA*{Nw^n|#99_mWWDa%annB?{h5BPQ*b)WtIO8y$q(LnX2y$u2U-&XJT>pf}oS|DNAPi@i zSvm-s7l!HAWt!Ycl-Pt)uFtpT$b!>44M-Lww4h$mI6PN<`$AnvxMkkNC#Vj!wU5g* zI;f`24V_i*;yjhqb~w>bVQbXyX`!z~5_#0UYWh@At;0I=GbI32aR_SkpYzYH#DQyU z$tJ@$Ha2R?DF?meT{3S)PKZqmAksbq`Q`@!Ulhdx@SE+F?cP0VRiVA&ogT)q7*(bZ zLbYKr-Zy4%x+<(j_NdCYkM&3qCV}KIc33n)?u?i^LXc&uaF=pa3TV3`m%)$eucr@A|o#3qX@w z^CM%Og;}=;8O`Z~AlCPs-Vr1=lu)-RgUw3Nf;tLyzbN!Te|I-htc{Guut8*+L$V!2 zJh(z8#%@Ci5t~GNwVL()jMB6E-ajQ)~dI&}|&&bNv}P z%a=_n!^W>S?o^tEL&mAj$6bN$GQVgYx zfj3-U_#}j?q0$L-HaOTu2V!)^E(7s-S9#13`tRft_fK#->^vB6=t-Wb71_3G3mf&Cmtv&wy^odeWv@WBLjK z)UKiuFRUubEXJp%t_1+kz3b)GwT*}RX^co$*rKa@0D|k+>kWC-`9K9>s0H=8^XIKk zG}dQwPl60Zw~P$feJzw8n0zeFjuC|&d|fG^7MB&+#!kc8zMPLSc%ZrElmT5^X7rUi zGc;9(x;J4jdaDvH3CsC~pjTUkN+viU@e=mkW+jj$79j;XKj(8GEvTt(3u6P4!uhuT!Q!BGMnepLfT6%B`A)G_f{4ZlN#?I&=zO^e^ zRlHsjPvEN&+&%GB1vazl-Flvgc*#q=B2Q)lLGFUOKv-;< z@|4GhDS>_`()-4J)~{HIxe<#jI!PEua$3KK+D@z9XG5foFj_mm=R8<~zYJPS&;yT; z>!W%6?7@M0;d^uvdV-#<2+V~qmy5pv0`^%3J}T)a_>&v+`WPNp?GO7GO-xvA5o+@C zK8C(4H%vJKb zng8gg2o1p#gr6XpdvQXPcVaGG^ZN$dDRoG`ME`bo2_-M!$XmKP&HGRq;Rz>SB)n5; zZM{dxb0_O}gAbnDtP~&A^_`tEG&b?v-)TjYk5fDPLHt)gpN6U6Z|yy>-x<*SBnZm% zFMupZi>X~;voS>)ZvqQvYGUQDVJggprgTD_p;rUaqYaN&1VJ(2=mx~^K_M<*Jh~yn zb=10%R!xC5Ed`VicNh0PV&bRxb@2jPZ~>Iavdzcp-?_svBNwoHwf&dD8tcQ0b~G;< zT^`OGTc#S6qs6%EITRJd5$gQ3!C7UV3*U#B^2r0uVuo~ENfdUCtkTgm-AqB zjbEI(SA9fIyhPZ)Rr&&xjurFEQ^7JtZ^w$l!mrqc8q6v;J|rszA8+UaKI-mqUxqgk>OgG-PpWD2Je6N z1(I8c`{7Jh8(HV)YKGc;VNQK@;*7K-tDOi6TCw>H!F<*ik`dRVxSvhTO5wY zfGdxzt;7Qx%ZqzBY%s?teuf^Lz-(9romf%X$=x+pLx6Icwk4SBP;x|4o!oc`8Zr(Q zesPuMir@kyRDuemO_Cg~{XoHv5WfE5*B5s6_UzSqrRfAQu3VvWR^oA$VQv}HZv|V* z(ubKfKj_3q&gi+km*X*?%fKQ>$OWIf(O49)`y_^`Yn&%!Ms6u~cx<+IA6Kra! zLY7dNcCUtzJ=mlo<50XJKlYb#nXoBUFQ4~A_?T7{p+0n7G$IQ~5KVvc)1)6{?tpsA!r_skN#jkf(bNm~(l;rv9}z5@HCb z612jG*1Nl=JLNDVV$-h_gYJ|YaM9ln_tSA;c}EXYTv0|e4m07vPgsK;CwhyG84UA0 zr~<-($O)zcT|TL$mP`T|e0y!2oIclagVwou&kt!NzT^FPJwr4;RG6Jr)T0|zr%a)y z?+rQvHptXv{GrQ|by`sp_Iqoq4JE!Uu4f%)M`SnxPAFH%>vjkfgNM&AR3g34hEe}G zV}c9?C4Id^ccy^@JXE9)11YV?j}HPzk|5j&AscywjBR)dKzBt?nv##7KvT+9%8x** zqu<0@JXhjGf0SxFD;>ZGh#QE&(j5(uz^gs59*grbnTeB>41b5 z4CmIB9rVFI#Vy|G-BK@IPad)5H7|e1{sYgbG2;3 znS6ne4ONmkrDklp3%yG@_}*NbKkHF0TZ?%=By`rO_c zf=bpyr2~YH9auNc{<>)giWJx$(qHu)>G4Xdx@GiZOVjZPO}gw&!7^ZF@Cvutr3#;H zr~rbJ{cEf3fMGze|QdgP(AJbxB-(Vk#$L*g#1colJ! zo$;RU5Gc;QqrMN6`5t#TI!ZwU^s=!(Ax9?3id+1 zpfRyII&AYUz%4^BmksBadEJurvV;b1#S%|Db7er8mi!(CWg8_NGGB^=vwWF?YVtL# zz9$Zb6&=R&c+VM_wo-gP(i6x)n1;Z$J!)8!2q36NnOWw54;%Q>u{FDQJQ+YaNv_j6 zHPow!;j!bFV*5K=0vAT(cyIF9%EYEsrKQxR?;uqgB-@wgA%=t)XI6K-%0Os`PE|=t zZ>^I^%IRGcqGTws(Gl?wiKKG@2RGKIzGSMxo?^&YP1pxBts0fyp}DA}%hM(%-<>7M zRb;^_%>3-!qPIE|RoLbWkgJT`pTlCNE(>>58BKjF+cI~er50K}GmJx_3UmOc^+uf{|6fjklFCMh;6XuW3IM3=#4*E_<(|uE^ zrRLhcaFnG}H&M{2+ONNa-1VMNb~4xN1DjE9r;{dLD%0MS(H^ZB-qH34Ii37V!5*qO zfzi_YwzNehC6l}1YMpU?f?$KqDl(Gy!aidX6YuRb^-lOAq$Es`hgs&Iy1bM=o2@Xr zFe>BR+P*rMqg=&y&c@MO6X|V6kQKCX=R8?$*IX(wc&M8zV54Eh+YlIcqWeSTJ5w1u z;wu@XxFDpc*)Mm{QW=jeZG?UhG_+g}{W90U)+Y|AB29l(K;K>B>|hX0@Z+^BTHLuNxxv zxoBKoGQ%h}`Hm(FiXh@Eb2UnLzqme32WV!Ku;vu=_TQN{;wRXS(;0VOG&ozU)BN*i z6YeGZv~3kdTbx1r_|km3LP(#BS6q?cTGiWCiDK$@Zd&t-MRr#j%o_dCshEiX=kQjy z5_BTEUc|HWK!7n)c3|QymN9F32kOWS%AYV}Na>8;*>KL{g4U;!4QB_(S@v%M$M_mW zBlfk>4+zHxTGT?KZz#m84hiCgOrmlri|_J=YRmY{a(rM(CZIe%p7f#BZm}n-)@g4E!a=Wfv%?t~B%#V6E)GV()xmv`I$!&{)_$pg$ z!jfX13u62Vq$&x_&si}%MYghxYBXPvJbMC_Nf=W0gK@9hcyod>?(qZ-vkdoC^Zk59 z=5SiFCf3L}kuCAjo<_#j<31e0rzd8)b|#h92+K*;IX8Qf2s=D()DTv!r@BSxaI0w{ z3pjnNwhu(eAR*AE2Z|5#EjXxjSEJ&nWdCqL39AR7Z?*|j^i1(HWfnEp3j^V=3(RJU zfveh9WcE|jLb~y^;#gtRmecXS&p*{jd&yD$k*5~NAmbBk83O0em$67U28L|}L`SHD zU;+0TuH7-07C@|_+%7j+s{jF1NZF7#hgXjNfG=a^ThnT}OS|}Mn$_3dP_&kSKRZsB zcC=J1dda(0eSCemUOL*g>N$c5c{p?7-FUn0jn0ZMw6}G6np2VSqMlgHD$R8Zp~bEE z+(V`G$2#}bM%k<;|61zLV{PNKL0jsJef-ScT49EAlxUwT1zkOwcF(chT(x!aH3yw4 zCj(%tzsH8zIh`~N?;5!h;1tt%{Mm@p`D^%3fW2q!cx<0o#k&w`{NwqLVe4z_DQw;O zKK5ua!-YPr93T4)@jNxGzCFIN+kqO&SLAGKR`5uC)(o-&zdT2Tjf0pl>a5DcjEG;8 z3RWcs=eZcjme0y=$~t|&JTq85m@=5Qy)L~LTMkL9NdM8}yV zi3zC~8+LE`l}2djOAw85ws7kBb?3oM3K8plbZ(4PLQMX%`_f51ZhlEj#rzhTu8i_VML1>WGyPloL1bDkv7^cGS= z9ily#9fiuaB$Sm{TkqStV28m?MFw1OuE9pv1)vJ zGZN!@b~SY8){PPCzJ%>cNe&%65C|BhdIp!pg9Eeu<^3;N&=V;hd@YZFl6X6Y5Suxy z;0I;@AW!1WK9qJ%Z^GqGd);nCY?x{xOKGJE{(S18w-=tHM?O0b)JyP8&|}i;(K2lE za85=1&EvJNRCF^c52uOr1my>bUz!j#ynolYTS~U$wXj=@#eNLq?$fT_oh>DQS-ikn zQkjionw{A$;h@r-M~Iv*PyQ_lLTX%*RQvJlL(r5qc5taOX^Ck>yQ;K#2U7vsmcr7m zt@=)ZDdYEbuL@>;)5nM610zP}oTPTe0D>oXizC!`eI?l(!goX=TnGEZi0_X}eg=!q z_H-Ye!!wMmtk-lqd@{(zSFQu1_`tg3OAd*iNy33U$jcnGqp3NUaIYA+KtVx4165-` zp5Z<_I@)$Bl0eAJsf{!8`+-H=5RhFYNSlRiR=uKlGnR?15o=~+C=NHNN+r1e%bs&?3@YG^%r-+0xqtWzm?0^h>}F6jP60Y8+?4 zO4glAs)#YCPrL-tCLMq8u+KKA&U9e?J*{o?R{A5Vwx@m6XIhe5)(Sxt{?NC@N(HOS z*$d0_F==>bgx%71+nc_+^9VtSy51%#>}49?({YMJwXwrSV>8zb2Xvt6h83^U=B=Hx z2s$$khde6LjPh7QQ6<{9&snz0JIg2xff+9J|3)>z2Vnx>jdpO8k6-+FL6F;}oj>0x zc_4y(;nobxmK}u2`CA2;#)m(I>UtWG9kkZXs>jbjNV}fnqHO8nL8VST$1fz&OKyW) zzj@Y37q?Qpb{MBPW?&@Gp0bh5fuKVfHA?I{hb?Rm>PAQ6XF-2zjY!j- zMFX@4p;HEAtsDn_8qDUsQ;jw2*n@TIx5K=hE}qBNIbAUx+4uADgLQ>%pBZq)t@*gc z!j?E!ll^@CXkp{@!d>=9ai%u5EL%T4@ABp@M6HKdfUe==SpVG(U^|0alm3}H*O~4M z>h9q5V;y+fowKAF`C(^kjV*{; z_xA0?AcIQ>K~HI<@5Uix(e=~nev3M3Bip&h+TyFRj}W?N*_L9)eO`CW+D7ZhcL|pV z|7h~iVj1ezTDKAHI>r5_vl$w^l_*PdcSFT0O)v4H&2t8%e*bYLlZ)+f1o<(J`}j7T zuxlzuvc>Lu#(6C)|I8h7k+-|p*L^yTEABbeX6NYJif60*cY7oVD`ORR#%JD!Q)l=0 z7H*?^G7{d*%J@h$y~%i_3bf`I&P`Pss1XwZv1`%83= z@I8=^IsOLRyc4y^5rj$zY4VEQ;_Y`-wD^IMiLT7JK#3Wv(iS^dYyc^k?NmCkG;6ia z?K4LNrzQWG$s4S5uM%3j*662Pnkqq0Ir_TLGtKXXn0S6PHnH`8P zvfytlql40%;FC1mdqncCk${>!`x;)}HtbvmYs)Jq{$2jvL#ZE&)=${$%sX7BTb%pU zX_ii_KdO%ubc$We_TBIe8ERx%NqgR5M;Pvu4^LN z$YH~b2g=1Lr}!yAMVc?ZY@7UakZC_JAQ0-+p`t*zkqEgduu6mcFPluo38n2Kk?|(; z7ya|>>Tl9H&04eV2I||EE?UHkL0NBv=jKEgxo$@h>=k?iZb7vjcH$gHvZ>E62=fZK z+5YsV;3h|fHCA0;W4ZfqBH5)nN3zSkwiLn}H&4%Lk#o8t-lef^YqmzvO1BjxH_QZ2 z#pZ+Hg8rO<|1Q33dbhwY)i8Qz&A#PS%?w#D0(1NIoo_J{+ZTd74txa-ap7D6lw_G) z_{N0Av)6G6y!&_q!5JGf&%H74aK%pMak1ht(Bpb3+NAdl{9^0W_5)i()bIr||#&@zMHE2me<;4g=+jT*b85p6TJ__QZl) zR9bH!r)vBAXWs-$w-!?Q8ljHdZ}D%o(XnnNTc3T3oj8%~(;~|=HUp5rS>~Q zv3QLo>qWg)a}{6Rtplj7M(+n&anG1UnU092%~t75cD|aGq$Eh9R6D8s-6nok4N}IT z;wGERvD}cTdF6`7TCRp2BBFB;ig5UuSXo|u@y$l1WJH|XR&S9Mq>;CUEiT8;?MhHF zjvBTe{ghbb@7lR-O%1AEA}%Rh|F)Lc?`EJ^j9#UNJOyCVayQddp%RZp)opWV>Hc;6 zu;|p)3ii71H`nMOulLNUpBhRg-!Ew@&?gcOZoI*&S9zUpP>qy=+MY=jm2Pgb$NO?ad^=v~u|cMVqW91|BE|Y*O}VLu1=I}0GrSm{e00f&>V%!GISBwv7x z5v5R;Am1?mT2LBN1XOtgWw<0ALh`@(u2pf&qTf&SHN_?K;~rEkb%_&C`Dw7#tR8pA zrYFK@U6j#=r7T7R{`MeU)|X{q9rRM||8+-KH+$+D&}f?C1U3BL7*!kAR3G_mRe+{N zVEKe=GxH?;`t%Kd2pXSB9Z?we05XLURyVjt*Fl_au-cqsG9D3T6bh3QhfFQ_wC>hD-JZqDyCix)x05(UE7@4wf{pH`Q6I7CdiMB}H>@=1fiK z0_GQJg!0?K)WLxQDA?9W;3L=f3H(9m%tGP1Z*qiLEl>dV8k!OQ8~lObkF_iI6?d1R zv=i1sd(8uz_9bo_1=NvlHUf(blzp{xJooe7lc5gyjw6V+qkQ47baKr898&Y#;A(7$ z{GH&@yADzolTezxUPj?9;H=tfXi@`)1+F?*x*K6aj`=I+>t2(FmDyFK4qqk`6uqQC%kW)_7SW01j^5<;Fg;gQuRfSCS&(=Z#b!Vi(p!;Q+Ag^Ngi+7Bi`)ENDKFER{Y_K)9R zMEqq5bb5rKGakq)i-%Ae!W!5(dkvfq5uJ@3p((-pAl9@j5;WHw0-w+9?!4|pz#io$ z12?s*6M8`s=3WC;O_$B(o$P10;Oe4!ETE8k2C} zB=GG4&(?VerO@HN*CgQK*(Fl2%Fn+zf~g?Z=XerZS%wqsp&Qml0?e$CWh5)Z-mhyD zgpsD1bElxT4MGKizD|1%jNjj1^)H@eE|Ga|K@d9i=yELLZW$G^>C9ND9Wdv5l?MxBoJU#25e7V$&`feY8^`gLu-$Nzn;WoZT=NnPp*w zm=WMklRZZo*T2~No=*~gOav__N&gl4doS6|6HebEA>6*~4H9#Ai0m{*FRVP60#Zb^ zkUC0#bGA0?J|WQf7g@BILIJ*z2G^VdxeP#5O9s(tgBDtQj!m9_v-q|RUD1E@wc@*< z{>}OBKRiwWssEQXomocr{i~0@auT!u^cT#g%)$t_Nt_~-ERh#vRz3XoZ@BH0OZqja z?W71P=o%5Bt99&q@V{V>F`H8MZ2yLL107vwBzzdipnG-%QjGpTTF2_mh~mM8qJ~=sg2r1K^Rg7TbH?fAnQU zDS#%oJ@Z9~83T8-AhG*+qB4O*){P`{lVcE?g(T)p5qUk;xQ{^HYuyXBkY+*dS3Odb z|Dy(@z)Sy)9pHK??<07rE2Np8`&sW5i4|lA!eWg%d&H@=4S{;lVY&DAF=a%}BH$8- z&i7ln%>T{|>Ru4HSpV;~6(%M8tkcZ_NRknD9Vm| zd(XpM`rELPkqb<*` zS4~F3ufC6tiKO3uRxT$8yMzok)?_*UcEQtZo><{$2>NG-lZ5p)W!3ZWm6}xBIGzkwEk`TGkYxu z{Fb2vpMl%Pr`b06ep!5l=8rbgoht_Z#=QpS^GVp=I_GqDaN_!{TsCL`Rhlvu3_Ndp z4P;pmtnggP8F55%f`%OzX4b8UIS;z1bYRJT74lD^=U+%v!TXxohn`&YnfqV z+G{>=S3e{^5v$jzSXD3m_x!hgYI{$H6jNcKyla!cwbUh*?**;XjVZ?JlUNLjgShg{ z@xu6WwEYg0uT&Z~rbuF&nhe8$-&qz+mkJK-gT6JY3lsy0?ll0*gAMN{-;_GNR`YLl z;GZ%2y7njbnhSngIRrx_*5(W0MJW%slGG-KP1oL}^bv{a9Yjoahl%CAuwaJXsg(LY z1Lo(R!wrTUM5U5HwbZ8?b&ed7cBHYz8@+WI;A_ue%?IOs=60i^OcTd~BJ@(n{!S@+ zY?Z`xO)(fGLlz&|5ZNiPX|>ltmBY}0s!xEey(anPUgECS)xU>99g1-J(Ox4h1;!Uf zhtYu|?I_7z|Kx=?Q4#CO17%zz`Ko^6s|Ih%>rA*vHuVS#K#D4*cgBJr!HGY6jdyV{ zW#tRn{1|KRJLEuNd@nlbroB$7$zGGeZ~vL^puP$A6dIIbX0k5Qg8T3}BDMi4dT>v3|Yiu=|?Ifw4fV-Sw@(8&LIFqo);;FM#`*8)Q#9s7a zodfs$1|Ybb#I-53KTjpN2#PI!)I^ADjdeyRY1@;qL7<9ONoBu_+~C>#JN2&i<64(k zg!CbL^{CedgdM5oztTw{J)di0K!guGb}Blp)1owL%r;RF3VGHS?W}*9)FJX@`T!0j z?g?O6GA${6EiMXFL#W1cr^Pb+NC+;-lWFecM#D#-dS|%v3T=o^v^T`-k>@+8zh6}6 zw2*lW45vK@?wdG2OziAQLJ~1^&ZoIXr4^`$JkD7Nbvetb_8{L-EC<56ng$RKodj6qYN@DDZ;8-hZvz;a3)ee7aNQfc%xye)a!1MnfQ*$IL*kxW^kO+uxIGrYq?+6iHo;s#+mILyjFpavc_l%js+Wr1soK z6*{7;A=tHa{D5B3H4+#Ke+**?*>{PB@HM$bXQMj{QtS?sYIrTB8dU`{;r1Mk$;2;x zcI11pxRy+?z4nb6w-23o3}Ch}n$CuN&xfF)lf)C4F{Wa^XxI88>OvAZ^WFcUyiS1j zy#`L1ND+z}{39FsGOckEHr|lJq&s$u3;mSOcbWuS8G25HUxv-a{=VkS=J9vu$Y=vz zpr1^arWP&dl2{+cgd3)#(5h0kG4u20C+>Ws5Jyb^81v%1$NdUV;>6?fVW|pDIsMOZ zdQHY2$fv)-7a^2-WrPHJjdh93bNg{mO2C?rvdW6IH3whfQY zFG7lYeFprd_y{J8HhQvf$UzCDDD62ghlot!-D9x)-F>9wnI;V^ywg%ZNs?cZF?j2q zBZdgZUUl4|A>JBNt2#rJxRF0GBVT=@n-Y}yP0h*P$w$5JYq@Jb=1uZ3YvN=1I{YK0 ziG_g=GcY`wuNZ9w3h?^qSUz8G5=crjhXDt@aR!(@!)0;eju6?)e5|4UsPcHBW%{0* z0K)hI3^&|dLQ}`_A@QVkbIQ=?FVGkEiJrgrm5cJ%Lyr_cw352_e%jZb=7%}gO zlqCKT(7Lm^vjkTnkK?o1arCVD1;8vwaA#;;khs!XWWwz6Ax~bRgsWX@0j335!|_7# zeWH1pAWpdVn71LqGQ{q@cHqO2F(1qKxF;n^6M?!TfUSc9%RPso2F#*|vRyA?Wi2l0 zEM1qW_a3KZuXh%80rrBCo^lkpKy%M0AwZ5W|6~toaHU4n0nU55%$$Cv$;%5JA0wCV zbWU1<6b}$1lC)!=O2vZw0GE^`i;(}2MWQX;-7towks%WLJ;eTyh<4>O3;44M4g3l3 zsVz$ZyY@NyiIoJ>ha8D4fHn}wk_j~i`S26rZJfNFy7FPa8dVaP?MS#KA$p?Sfy?h< zb4qTxafzjnC;M{LYpIr(x};7PI~NgYCLSgSoqYV~4?6{Y*1j9Y+p&(6rN{0KJ_uJp z~i^ML{{D=?E5Qw`7j5MW@y!PD2nfCR{GL;EFE**bN zul}VOiHA{=L=eooUt;09%>Id8!m2t{IMyPns&dc0fgg!ndPwu0taq;f6Hgvme=2y9+2dDLK$7jFtj&c&_*4VPh{y$}eaj z5(YcO|AK;peKjRj)9 z#`|6OZaD9hZnK&7{6=7fg(=THiKM_&fDcg{m3?GsnqagcfmukLiU{gZEI6`t$*ewe zDfy-w1_K&dNsnCZR+w{N#1$=nd7jGkULlmXSvQ(6CQSm0I2t1JO*3>!VBL1`HVu~y zs57dX$V_22EfA|OS=*7nr4H(P_Z)wVb4W8f-t^I>*>$rH0FLCIr7aE%D(?D%^mvn~ z*8+ASN7v5GdHtBH>R~exP)RQ6S@PcVWkBluMI=|;^)rb2hV5a^-4C~SmkNhYkeCit ztHL~)IItTh;%~g&uO=b7$Eg$R1p!$r2dStCQ>`8KSN)M zJdfm`UlL_4$v@xtFOVTxLL{I5v+=z_^M60aKhZ(P|C8TxY&Q> z=-rI4B29AWe~0BdsqtsizeDB^rhkLUAM*YUB!ABI?;wHxFtXo(@`ro>1`_Cx$od^7 zf8_LUF!`gL{00)}k8<)GO#Ub*zk%eBa`HP!{wOEE!{m>0@;glaC?~(ev^+{s{{?Szu`OmJBxBuuWLH)CC3^qpDmnho{-JyS>>v8G z>EEd852k+u3G|1&e}l=NGyNM#pg)Z4cbNR)-oL@*kI4EBB+wr@{TodFXk@>E1p1?# x{0@`N?%Dc7&ti5Jrr Date: Fri, 23 Aug 2024 12:52:02 +0200 Subject: [PATCH 149/382] Change the chain to Rococo in the parachain template Zombienet config (#5279) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following this: https://github.com/paritytech/polkadot-sdk-parachain-template/pull/11 --------- Co-authored-by: Shawn Tabrizi Co-authored-by: Bastian Köcher --- templates/parachain/README.md | 13 ++++++++++--- templates/parachain/zombienet.toml | 9 +++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/templates/parachain/README.md b/templates/parachain/README.md index b7fa2bed0ed9..b912d8e005c7 100644 --- a/templates/parachain/README.md +++ b/templates/parachain/README.md @@ -59,15 +59,22 @@ docker build . -t polkadot-sdk-parachain-template You can grab a [released binary](https://github.com/paritytech/zombienet/releases/latest) or use an [npm version](https://www.npmjs.com/package/@zombienet/cli). This template produces a parachain node. +You can install it in your environment by running: + +```sh +cargo install --path node +``` + You still need a relaychain node - you can download the `polkadot` (and the accompanying `polkadot-prepare-worker` and `polkadot-execute-worker`) binaries from [Polkadot SDK releases](https://github.com/paritytech/polkadot-sdk/releases/latest). -Make sure to bring the parachain node - as well as `polkadot`, `polkadot-prepare-worker`, `polkadot-execute-worker`, -and `zombienet` - into `PATH` like so: +In addition to the installed parachain node, make sure to bring +`zombienet`, `polkadot`, `polkadot-prepare-worker`, and `polkadot-execute-worker` +into `PATH`, for example: ```sh -export PATH="./target/release/:$PATH" +export PATH=":$PATH" ``` This way, we can conveniently use them in the following steps. diff --git a/templates/parachain/zombienet.toml b/templates/parachain/zombienet.toml index 336ba1af4dde..c47a4bdeb6ad 100644 --- a/templates/parachain/zombienet.toml +++ b/templates/parachain/zombienet.toml @@ -1,16 +1,21 @@ [relaychain] default_command = "polkadot" -chain = "dev" +chain = "rococo-local" [[relaychain.nodes]] name = "alice" validator = true ws_port = 9944 +[[relaychain.nodes]] +name = "bob" +validator = true +ws_port = 9955 + [[parachains]] id = 1000 [parachains.collator] -name = "alice" +name = "charlie" ws_port = 9988 command = "parachain-template-node" From e4ffba6ef5d05937a00884a9c56b56baa3bfb8e6 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 23 Aug 2024 14:20:15 +0200 Subject: [PATCH 150/382] [bridges] Prune messages from confirmation tx body, not from the on_idle (#5006) (Please, do not merge until SA, reverted and restored of https://github.com/paritytech/polkadot-sdk/pull/4944) Original PR with more context: https://github.com/paritytech/parity-bridges-common/pull/2211 Relates to: https://github.com/paritytech/parity-bridges-common/issues/2210 ## TODO - [x] fresh weighs for `pallet_bridge_messages` - [x] add `try_state` for `pallet_bridge_messages` which checks for unpruned messages - relates to the [comment](https://github.com/paritytech/parity-bridges-common/pull/2211#issuecomment-1643224831) - [x] ~prepare migration, that prunes leftovers, which would be pruned eventually from `on_idle` the [comment](https://github.com/paritytech/parity-bridges-common/pull/2211#issuecomment-1643224831)~ can be done also by `set_storage` / `kill_storage` or with `OnRuntimeUpgrade` implementatino when `do_try_state_for_outbound_lanes` detects problem. ## Open question - [ ] Do we really need `oldest_unpruned_nonce` afterwards? - after the runtime upgrade and when `do_try_state_for_outbound_lanes` pass, we won't need any migrations here - we won't even need `do_try_state_for_outbound_lanes` - please check comments bellow: https://github.com/paritytech/polkadot-sdk/pull/4944#discussion_r1666737961 --------- Signed-off-by: Branislav Kontur Co-authored-by: Serban Iorga Co-authored-by: Svyatoslav Nikolsky Co-authored-by: command-bot <> --- bridges/modules/messages/src/lib.rs | 95 ++++++---- bridges/modules/messages/src/outbound_lane.rs | 106 ++--------- .../messages/src/tests/pallet_tests.rs | 169 +++++------------- ...idge_messages_rococo_to_rococo_bulletin.rs | 56 +++--- ...allet_bridge_messages_rococo_to_westend.rs | 58 +++--- .../src/weights/pallet_bridge_messages.rs | 58 +++--- 6 files changed, 210 insertions(+), 332 deletions(-) diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs index bf105b140401..c36313a14764 100644 --- a/bridges/modules/messages/src/lib.rs +++ b/bridges/modules/messages/src/lib.rs @@ -70,7 +70,6 @@ use bp_runtime::{ }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get, DefaultNoBound}; -use sp_runtime::traits::UniqueSaturatedFrom; use sp_std::{marker::PhantomData, prelude::*}; mod inbound_lane; @@ -153,40 +152,6 @@ pub mod pallet { type OperatingModeStorage = PalletOperatingMode; } - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet - where - u32: TryFrom>, - { - fn on_idle(_block: BlockNumberFor, remaining_weight: Weight) -> Weight { - // we'll need at least to read outbound lane state, kill a message and update lane state - let db_weight = T::DbWeight::get(); - if !remaining_weight.all_gte(db_weight.reads_writes(1, 2)) { - return Weight::zero() - } - - // messages from lane with index `i` in `ActiveOutboundLanes` are pruned when - // `System::block_number() % lanes.len() == i`. Otherwise we need to read lane states on - // every block, wasting the whole `remaining_weight` for nothing and causing starvation - // of the last lane pruning - let active_lanes = T::ActiveOutboundLanes::get(); - let active_lanes_len = (active_lanes.len() as u32).into(); - let active_lane_index = u32::unique_saturated_from( - frame_system::Pallet::::block_number() % active_lanes_len, - ); - let active_lane_id = active_lanes[active_lane_index as usize]; - - // first db read - outbound lane state - let mut active_lane = outbound_lane::(active_lane_id); - let mut used_weight = db_weight.reads(1); - // and here we'll have writes - used_weight += active_lane.prune_messages(db_weight, remaining_weight - used_weight); - - // we already checked we have enough `remaining_weight` to cover this `used_weight` - used_weight - } - } - #[pallet::call] impl, I: 'static> Pallet { /// Change `PalletOwner`. @@ -610,6 +575,14 @@ pub mod pallet { } } + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet { + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + Self::do_try_state() + } + } + impl, I: 'static> Pallet { /// Get stored data of the outbound message with given nonce. pub fn outbound_message_data(lane: LaneId, nonce: MessageNonce) -> Option { @@ -644,6 +617,58 @@ pub mod pallet { } } + #[cfg(any(feature = "try-runtime", test))] + impl, I: 'static> Pallet { + /// Ensure the correctness of the state of this pallet. + pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { + Self::do_try_state_for_outbound_lanes() + } + + /// Ensure the correctness of the state of outbound lanes. + pub fn do_try_state_for_outbound_lanes() -> Result<(), sp_runtime::TryRuntimeError> { + use sp_runtime::traits::One; + use sp_std::vec::Vec; + + // collect unpruned lanes + let mut unpruned_lanes = Vec::new(); + for (lane_id, lane_data) in OutboundLanes::::iter() { + let Some(expected_last_prunned_nonce) = + lane_data.oldest_unpruned_nonce.checked_sub(One::one()) + else { + continue; + }; + + // collect message_nonces that were supposed to be pruned + let mut unpruned_message_nonces = Vec::new(); + const MAX_MESSAGES_ITERATION: u64 = 16; + let start_nonce = + expected_last_prunned_nonce.checked_sub(MAX_MESSAGES_ITERATION).unwrap_or(0); + for current_nonce in start_nonce..=expected_last_prunned_nonce { + // check a message for current_nonce + if OutboundMessages::::contains_key(MessageKey { + lane_id, + nonce: current_nonce, + }) { + unpruned_message_nonces.push(current_nonce); + } + } + + if !unpruned_message_nonces.is_empty() { + log::warn!( + target: LOG_TARGET, + "do_try_state_for_outbound_lanes for lane_id: {lane_id:?} with lane_data: {lane_data:?} found unpruned_message_nonces: {unpruned_message_nonces:?}", + ); + unpruned_lanes.push((lane_id, lane_data, unpruned_message_nonces)); + } + } + + // ensure messages before `oldest_unpruned_nonce` are really pruned. + ensure!(unpruned_lanes.is_empty(), "Found unpruned lanes!"); + + Ok(()) + } + } + /// Get-parameter that returns number of active outbound lanes that the pallet maintains. pub struct MaybeOutboundLanesCount(PhantomData<(T, I)>); diff --git a/bridges/modules/messages/src/outbound_lane.rs b/bridges/modules/messages/src/outbound_lane.rs index fcdddf199dc6..788a13e82b1b 100644 --- a/bridges/modules/messages/src/outbound_lane.rs +++ b/bridges/modules/messages/src/outbound_lane.rs @@ -22,13 +22,9 @@ use bp_messages::{ ChainWithMessages, DeliveredMessages, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, }; use codec::{Decode, Encode}; -use frame_support::{ - traits::Get, - weights::{RuntimeDbWeight, Weight}, - BoundedVec, PalletError, -}; +use frame_support::{traits::Get, BoundedVec, PalletError}; use scale_info::TypeInfo; -use sp_runtime::{traits::Zero, RuntimeDebug}; +use sp_runtime::RuntimeDebug; use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData}; /// Outbound lane storage. @@ -143,41 +139,17 @@ impl OutboundLane { ensure_unrewarded_relayers_are_correct(confirmed_messages.end, relayers)?; + // prune all confirmed messages + for nonce in confirmed_messages.begin..=confirmed_messages.end { + self.storage.remove_message(&nonce); + } + data.latest_received_nonce = confirmed_messages.end; + data.oldest_unpruned_nonce = data.latest_received_nonce.saturating_add(1); self.storage.set_data(data); Ok(Some(confirmed_messages)) } - - /// Prune at most `max_messages_to_prune` already received messages. - /// - /// Returns weight, consumed by messages pruning and lane state update. - pub fn prune_messages( - &mut self, - db_weight: RuntimeDbWeight, - mut remaining_weight: Weight, - ) -> Weight { - let write_weight = db_weight.writes(1); - let two_writes_weight = write_weight + write_weight; - let mut spent_weight = Weight::zero(); - let mut data = self.storage.data(); - while remaining_weight.all_gte(two_writes_weight) && - data.oldest_unpruned_nonce <= data.latest_received_nonce - { - self.storage.remove_message(&data.oldest_unpruned_nonce); - - spent_weight += write_weight; - remaining_weight -= write_weight; - data.oldest_unpruned_nonce += 1; - } - - if !spent_weight.is_zero() { - spent_weight += write_weight; - self.storage.set_data(data); - } - - spent_weight - } } /// Verifies unrewarded relayers vec. @@ -221,7 +193,6 @@ mod tests { REGULAR_PAYLOAD, TEST_LANE_ID, }, }; - use frame_support::weights::constants::RocksDbWeight; use sp_std::ops::RangeInclusive; fn unrewarded_relayers( @@ -281,7 +252,7 @@ mod tests { ); assert_eq!(lane.storage.data().latest_generated_nonce, 3); assert_eq!(lane.storage.data().latest_received_nonce, 3); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); }); } @@ -302,7 +273,7 @@ mod tests { ); assert_eq!(lane.storage.data().latest_generated_nonce, 3); assert_eq!(lane.storage.data().latest_received_nonce, 2); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3); assert_eq!( lane.confirm_delivery(3, 3, &unrewarded_relayers(3..=3)), @@ -310,7 +281,7 @@ mod tests { ); assert_eq!(lane.storage.data().latest_generated_nonce, 3); assert_eq!(lane.storage.data().latest_received_nonce, 3); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); }); } @@ -331,12 +302,12 @@ mod tests { assert_eq!(lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), Ok(None),); assert_eq!(lane.storage.data().latest_generated_nonce, 3); assert_eq!(lane.storage.data().latest_received_nonce, 3); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); assert_eq!(lane.confirm_delivery(1, 2, &unrewarded_relayers(1..=1)), Ok(None),); assert_eq!(lane.storage.data().latest_generated_nonce, 3); assert_eq!(lane.storage.data().latest_received_nonce, 3); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); }); } @@ -394,57 +365,6 @@ mod tests { ); } - #[test] - fn prune_messages_works() { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - // when lane is empty, nothing is pruned - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - Weight::zero() - ); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); - // when nothing is confirmed, nothing is pruned - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - assert!(lane.storage.message(&1).is_some()); - assert!(lane.storage.message(&2).is_some()); - assert!(lane.storage.message(&3).is_some()); - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - Weight::zero() - ); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); - // after confirmation, some messages are received - assert_eq!( - lane.confirm_delivery(2, 2, &unrewarded_relayers(1..=2)), - Ok(Some(delivered_messages(1..=2))), - ); - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - RocksDbWeight::get().writes(3), - ); - assert!(lane.storage.message(&1).is_none()); - assert!(lane.storage.message(&2).is_none()); - assert!(lane.storage.message(&3).is_some()); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3); - // after last message is confirmed, everything is pruned - assert_eq!( - lane.confirm_delivery(1, 3, &unrewarded_relayers(3..=3)), - Ok(Some(delivered_messages(3..=3))), - ); - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - RocksDbWeight::get().writes(2), - ); - assert!(lane.storage.message(&1).is_none()); - assert!(lane.storage.message(&2).is_none()); - assert!(lane.storage.message(&3).is_none()); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); - }); - } - #[test] fn confirm_delivery_detects_when_more_than_expected_messages_are_confirmed() { run_test(|| { diff --git a/bridges/modules/messages/src/tests/pallet_tests.rs b/bridges/modules/messages/src/tests/pallet_tests.rs index 42e1042717de..f7a288d649a9 100644 --- a/bridges/modules/messages/src/tests/pallet_tests.rs +++ b/bridges/modules/messages/src/tests/pallet_tests.rs @@ -38,15 +38,14 @@ use bp_runtime::{BasicOperatingMode, PreComputedSize, RangeInclusiveExt, Size}; use bp_test_utils::generate_owned_bridge_module_tests; use codec::Encode; use frame_support::{ - assert_noop, assert_ok, + assert_err, assert_noop, assert_ok, dispatch::Pays, storage::generator::{StorageMap, StorageValue}, - traits::Hooks, weights::Weight, }; use frame_system::{EventRecord, Pallet as System, Phase}; use sp_core::Get; -use sp_runtime::DispatchError; +use sp_runtime::{BoundedVec, DispatchError}; fn get_ready_for_events() { System::::set_block_number(1); @@ -99,6 +98,7 @@ fn receive_messages_delivery_proof() { last_delivered_nonce: 1, }, )); + assert_ok!(Pallet::::do_try_state()); assert_eq!( System::::events(), @@ -160,6 +160,7 @@ fn pallet_rejects_transactions_if_halted() { ), Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), ); + assert_ok!(Pallet::::do_try_state()); }); } @@ -220,6 +221,7 @@ fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { last_delivered_nonce: 1, }, )); + assert_ok!(Pallet::::do_try_state()); }); } @@ -395,10 +397,14 @@ fn receive_messages_proof_rejects_proof_with_too_many_messages() { #[test] fn receive_messages_delivery_proof_works() { run_test(|| { + assert_eq!(OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, 0); + assert_eq!(OutboundLanes::::get(TEST_LANE_ID).oldest_unpruned_nonce, 1); + send_regular_message(TEST_LANE_ID); receive_messages_delivery_proof(); - assert_eq!(OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, 1,); + assert_eq!(OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, 1); + assert_eq!(OutboundLanes::::get(TEST_LANE_ID).oldest_unpruned_nonce, 2); }); } @@ -428,6 +434,7 @@ fn receive_messages_delivery_proof_rewards_relayers() { }, ); assert_ok!(result); + assert_ok!(Pallet::::do_try_state()); assert_eq!( result.unwrap().actual_weight.unwrap(), TestWeightInfo::receive_messages_delivery_proof_weight( @@ -467,6 +474,7 @@ fn receive_messages_delivery_proof_rewards_relayers() { }, ); assert_ok!(result); + assert_ok!(Pallet::::do_try_state()); // even though the pre-dispatch weight was for two messages, the actual weight is // for single message only assert_eq!( @@ -852,129 +860,6 @@ fn inbound_message_details_works() { }); } -#[test] -fn on_idle_callback_respects_remaining_weight() { - run_test(|| { - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - prepare_messages_delivery_proof( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 4, - relayers: vec![unrewarded_relayer(1, 4, TEST_RELAYER_A)].into(), - }, - ), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 4, - total_messages: 4, - last_delivered_nonce: 4, - }, - )); - - // all 4 messages may be pruned now - assert_eq!(outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, 4); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 1); - System::::set_block_number(2); - - // if passed wight is too low to do anything - let dbw = DbWeight::get(); - assert_eq!(Pallet::::on_idle(0, dbw.reads_writes(1, 1)), Weight::zero(),); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 1); - - // if passed wight is enough to prune single message - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(1, 2)), - dbw.reads_writes(1, 2), - ); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 2); - - // if passed wight is enough to prune two more messages - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(1, 3)), - dbw.reads_writes(1, 3), - ); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 4); - - // if passed wight is enough to prune many messages - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(100, 100)), - dbw.reads_writes(1, 2), - ); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 5); - }); -} - -#[test] -fn on_idle_callback_is_rotating_lanes_to_prune() { - run_test(|| { - // send + receive confirmation for lane 1 - send_regular_message(TEST_LANE_ID); - receive_messages_delivery_proof(); - // send + receive confirmation for lane 2 - send_regular_message(TEST_LANE_ID_2); - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - prepare_messages_delivery_proof( - TEST_LANE_ID_2, - InboundLaneData { - last_confirmed_nonce: 1, - relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), - }, - ), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }, - )); - - // nothing is pruned yet - assert_eq!(outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, 1); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 1); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().latest_received_nonce, - 1 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, - 1 - ); - - // in block#2.on_idle lane messages of lane 1 are pruned - let dbw = DbWeight::get(); - System::::set_block_number(2); - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(100, 100)), - dbw.reads_writes(1, 2), - ); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 2); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, - 1 - ); - - // in block#3.on_idle lane messages of lane 2 are pruned - System::::set_block_number(3); - - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(100, 100)), - dbw.reads_writes(1, 2), - ); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 2); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, - 2 - ); - }); -} - #[test] fn outbound_message_from_unconfigured_lane_is_rejected() { run_test(|| { @@ -1098,3 +983,33 @@ fn maybe_outbound_lanes_count_returns_correct_value() { Some(mock::ActiveOutboundLanes::get().len() as u32) ); } + +#[test] +fn do_try_state_for_outbound_lanes_works() { + run_test(|| { + let lane_id = TEST_LANE_ID; + + // setup delivered nonce 1 + OutboundLanes::::insert( + lane_id, + OutboundLaneData { + oldest_unpruned_nonce: 2, + latest_received_nonce: 1, + latest_generated_nonce: 0, + }, + ); + // store message for nonce 1 + OutboundMessages::::insert( + MessageKey { lane_id, nonce: 1 }, + BoundedVec::default(), + ); + assert_err!( + Pallet::::do_try_state(), + sp_runtime::TryRuntimeError::Other("Found unpruned lanes!") + ); + + // remove message for nonce 1 + OutboundMessages::::remove(MessageKey { lane_id, nonce: 1 }); + assert_ok!(Pallet::::do_try_state()); + }) +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_rococo_bulletin.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_rococo_bulletin.rs index 5522a325f192..d0a7ed25363d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_rococo_bulletin.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_rococo_bulletin.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-07-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 @@ -60,8 +60,8 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `654` // Estimated: `52645` - // Minimum execution time: 37_206_000 picoseconds. - Weight::from_parts(38_545_000, 0) + // Minimum execution time: 36_836_000 picoseconds. + Weight::from_parts(37_858_000, 0) .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) @@ -80,11 +80,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `654` // Estimated: `52645` - // Minimum execution time: 37_075_000 picoseconds. - Weight::from_parts(37_757_000, 0) + // Minimum execution time: 36_587_000 picoseconds. + Weight::from_parts(37_516_000, 0) .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 5_776 - .saturating_add(Weight::from_parts(11_586_768, 0).saturating_mul(n.into())) + // Standard Error: 8_655 + .saturating_add(Weight::from_parts(11_649_169, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -100,8 +100,8 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `654` // Estimated: `52645` - // Minimum execution time: 42_087_000 picoseconds. - Weight::from_parts(42_970_000, 0) + // Minimum execution time: 42_157_000 picoseconds. + Weight::from_parts(43_105_000, 0) .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) @@ -120,11 +120,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `654` // Estimated: `52645` - // Minimum execution time: 35_055_000 picoseconds. - Weight::from_parts(36_987_740, 0) + // Minimum execution time: 35_536_000 picoseconds. + Weight::from_parts(37_452_828, 0) .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 4 - .saturating_add(Weight::from_parts(2_316, 0).saturating_mul(n.into())) + // Standard Error: 3 + .saturating_add(Weight::from_parts(2_269, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -134,15 +134,17 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundLanes` (r:1 w:1) /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Storage: `BridgePolkadotBulletinMessages::OutboundMessages` (r:0 w:1) + /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: // Measured: `621` // Estimated: `2543` - // Minimum execution time: 24_326_000 picoseconds. - Weight::from_parts(25_169_000, 0) + // Minimum execution time: 25_800_000 picoseconds. + Weight::from_parts(26_666_000, 0) .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgePolkadotBulletinMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -150,15 +152,17 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundLanes` (r:1 w:1) /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Storage: `BridgePolkadotBulletinMessages::OutboundMessages` (r:0 w:2) + /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: // Measured: `621` // Estimated: `2543` - // Minimum execution time: 24_484_000 picoseconds. - Weight::from_parts(25_130_000, 0) + // Minimum execution time: 27_262_000 picoseconds. + Weight::from_parts(27_997_000, 0) .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgePolkadotBulletinMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -166,15 +170,17 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundLanes` (r:1 w:1) /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Storage: `BridgePolkadotBulletinMessages::OutboundMessages` (r:0 w:2) + /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: // Measured: `621` // Estimated: `2543` - // Minimum execution time: 24_450_000 picoseconds. - Weight::from_parts(25_164_000, 0) + // Minimum execution time: 26_992_000 picoseconds. + Weight::from_parts(27_921_000, 0) .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgePolkadotBulletinMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -204,11 +210,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `813` // Estimated: `52645` - // Minimum execution time: 54_317_000 picoseconds. - Weight::from_parts(59_171_547, 0) + // Minimum execution time: 55_509_000 picoseconds. + Weight::from_parts(59_826_763, 0) .saturating_add(Weight::from_parts(0, 52645)) // Standard Error: 7 - .saturating_add(Weight::from_parts(7_566, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(7_565, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs index 9c05dae979da..dc6c917c6d00 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-07-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 @@ -62,8 +62,8 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `658` // Estimated: `52645` - // Minimum execution time: 41_396_000 picoseconds. - Weight::from_parts(43_141_000, 0) + // Minimum execution time: 40_198_000 picoseconds. + Weight::from_parts(42_079_000, 0) .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) @@ -84,11 +84,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `658` // Estimated: `52645` - // Minimum execution time: 41_095_000 picoseconds. - Weight::from_parts(42_030_000, 0) + // Minimum execution time: 39_990_000 picoseconds. + Weight::from_parts(41_381_000, 0) .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 5_702 - .saturating_add(Weight::from_parts(11_627_951, 0).saturating_mul(n.into())) + // Standard Error: 8_459 + .saturating_add(Weight::from_parts(11_710_167, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -106,8 +106,8 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `658` // Estimated: `52645` - // Minimum execution time: 45_912_000 picoseconds. - Weight::from_parts(47_564_000, 0) + // Minimum execution time: 45_940_000 picoseconds. + Weight::from_parts(47_753_000, 0) .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) @@ -128,11 +128,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `658` // Estimated: `52645` - // Minimum execution time: 39_175_000 picoseconds. - Weight::from_parts(41_674_095, 0) + // Minimum execution time: 39_067_000 picoseconds. + Weight::from_parts(41_787_019, 0) .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 4 - .saturating_add(Weight::from_parts(2_305, 0).saturating_mul(n.into())) + // Standard Error: 5 + .saturating_add(Weight::from_parts(2_295, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -146,15 +146,17 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:1) + /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: // Measured: `501` // Estimated: `3966` - // Minimum execution time: 32_033_000 picoseconds. - Weight::from_parts(33_131_000, 0) + // Minimum execution time: 33_107_000 picoseconds. + Weight::from_parts(34_364_000, 0) .saturating_add(Weight::from_parts(0, 3966)) .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -166,15 +168,17 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:2) + /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: // Measured: `501` // Estimated: `3966` - // Minimum execution time: 32_153_000 picoseconds. - Weight::from_parts(33_126_000, 0) + // Minimum execution time: 34_826_000 picoseconds. + Weight::from_parts(35_563_000, 0) .saturating_add(Weight::from_parts(0, 3966)) .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -186,15 +190,17 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:2 w:2) /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:2) + /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: // Measured: `501` // Estimated: `6086` - // Minimum execution time: 36_387_000 picoseconds. - Weight::from_parts(37_396_000, 0) + // Minimum execution time: 38_725_000 picoseconds. + Weight::from_parts(39_727_000, 0) .saturating_add(Weight::from_parts(0, 6086)) .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -224,11 +230,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `789` // Estimated: `52645` - // Minimum execution time: 56_562_000 picoseconds. - Weight::from_parts(61_452_871, 0) + // Minimum execution time: 56_892_000 picoseconds. + Weight::from_parts(61_941_659, 0) .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 9 - .saturating_add(Weight::from_parts(7_587, 0).saturating_mul(n.into())) + // Standard Error: 8 + .saturating_add(Weight::from_parts(7_580, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_messages.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_messages.rs index 386342d7ea5d..1033387b527e 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_messages.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_messages.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-07-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 @@ -62,8 +62,8 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `522` // Estimated: `52645` - // Minimum execution time: 40_748_000 picoseconds. - Weight::from_parts(41_836_000, 0) + // Minimum execution time: 40_289_000 picoseconds. + Weight::from_parts(42_150_000, 0) .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) @@ -83,11 +83,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `522` // Estimated: `52645` - // Minimum execution time: 40_923_000 picoseconds. - Weight::from_parts(41_287_000, 0) + // Minimum execution time: 40_572_000 picoseconds. + Weight::from_parts(41_033_000, 0) .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 9_774 - .saturating_add(Weight::from_parts(11_469_207, 0).saturating_mul(n.into())) + // Standard Error: 12_000 + .saturating_add(Weight::from_parts(11_710_588, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -105,8 +105,8 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `522` // Estimated: `52645` - // Minimum execution time: 45_946_000 picoseconds. - Weight::from_parts(47_547_000, 0) + // Minimum execution time: 46_655_000 picoseconds. + Weight::from_parts(49_576_000, 0) .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,11 +126,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `522` // Estimated: `52645` - // Minimum execution time: 39_668_000 picoseconds. - Weight::from_parts(41_908_980, 0) + // Minimum execution time: 40_245_000 picoseconds. + Weight::from_parts(43_461_320, 0) .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 11 - .saturating_add(Weight::from_parts(2_209, 0).saturating_mul(n.into())) + // Standard Error: 21 + .saturating_add(Weight::from_parts(2_246, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -144,15 +144,17 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:1) + /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3822` - // Minimum execution time: 30_544_000 picoseconds. - Weight::from_parts(31_171_000, 0) + // Minimum execution time: 32_001_000 picoseconds. + Weight::from_parts(32_842_000, 0) .saturating_add(Weight::from_parts(0, 3822)) .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -164,15 +166,17 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:2) + /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3822` - // Minimum execution time: 30_593_000 picoseconds. - Weight::from_parts(31_261_000, 0) + // Minimum execution time: 33_287_000 picoseconds. + Weight::from_parts(33_769_000, 0) .saturating_add(Weight::from_parts(0, 3822)) .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -184,15 +188,17 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:2 w:2) /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:2) + /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: // Measured: `357` // Estimated: `6086` - // Minimum execution time: 34_682_000 picoseconds. - Weight::from_parts(35_277_000, 0) + // Minimum execution time: 37_136_000 picoseconds. + Weight::from_parts(38_294_000, 0) .saturating_add(Weight::from_parts(0, 6086)) .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -221,11 +227,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `653` // Estimated: `52645` - // Minimum execution time: 56_465_000 picoseconds. - Weight::from_parts(61_575_775, 0) + // Minimum execution time: 55_942_000 picoseconds. + Weight::from_parts(60_615_769, 0) .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 15 - .saturating_add(Weight::from_parts(7_197, 0).saturating_mul(n.into())) + // Standard Error: 14 + .saturating_add(Weight::from_parts(7_225, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) } From 4057ccd7a37396bc1c6d1742f418415af61b2787 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Fri, 23 Aug 2024 16:00:36 +0300 Subject: [PATCH 151/382] Reactive syncing metrics (#5410) This PR untangles syncing metrics and makes them reactive, the way metrics are supposed to be in general. Syncing metrics were bundled in a way that caused coupling across multiple layers: justifications metrics were defined and managed by `ChainSync`, but only updated periodically on tick in `SyncingEngine`, while actual values were queried from `ExtraRequests`. This convoluted architecture was hard to follow when I was looking into https://github.com/paritytech/polkadot-sdk/issues/5333. Now metrics that correspond to each component are owned by that component and updated as changes are made instead of on tick every 1100ms. This does add some annoying boilerplate that is a bit harder to maintain, but it separates metrics more nicely and if someone queries them more frequently will give arbitrary resolution. Since metrics updates are just atomic operations I do not expect any performance impact of these changes. Will add prdoc if changes look good otherwise. P.S. I noticed that importing requests (and corresponding metrics) were not cleared ever since corresponding code was introduced in https://github.com/paritytech/polkadot-sdk/commit/dc41558b6ef69aad59bb4cfcd9c19c91c1d5c3a9#r145518721 and I left it as is to not change the behavior, but it might be something worth fixing. cc @dmitry-markin --------- Co-authored-by: Dmitry Markin --- prdoc/pr_5410.prdoc | 11 ++ substrate/client/network/sync/src/engine.rs | 28 ++-- .../sync/src/justification_requests.rs | 128 +++++++++++++++--- substrate/client/network/sync/src/strategy.rs | 13 +- .../network/sync/src/strategy/chain_sync.rs | 120 ++++++++-------- 5 files changed, 195 insertions(+), 105 deletions(-) create mode 100644 prdoc/pr_5410.prdoc diff --git a/prdoc/pr_5410.prdoc b/prdoc/pr_5410.prdoc new file mode 100644 index 000000000000..d0a32bec7423 --- /dev/null +++ b/prdoc/pr_5410.prdoc @@ -0,0 +1,11 @@ +title: Reactive syncing metrics + +doc: + - audience: Node Dev + description: | + Syncing metrics are now updated immediate as changes happen rather than every 1100ms as it was happening before. + This resulted in minor, but breaking API changes. + +crates: + - name: sc-network-sync + bump: major diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index a25db4f789dd..616dd3168872 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -471,15 +471,6 @@ where )) } - /// Report Prometheus metrics. - pub fn report_metrics(&self) { - if let Some(metrics) = &self.metrics { - let n = u64::try_from(self.peers.len()).unwrap_or(std::u64::MAX); - metrics.peers.set(n); - } - self.strategy.report_metrics(); - } - fn update_peer_info( &mut self, peer_id: &PeerId, @@ -606,7 +597,11 @@ where pub async fn run(mut self) { loop { tokio::select! { - _ = self.tick_timeout.tick() => self.perform_periodic_actions(), + _ = self.tick_timeout.tick() => { + // TODO: This tick should not be necessary, but + // `self.process_strategy_actions()` is not called in some cases otherwise and + // some tests fail because of this + }, command = self.service_rx.select_next_some() => self.process_service_command(command), notification_event = self.notification_service.next_event() => match notification_event { @@ -724,10 +719,6 @@ where Ok(()) } - fn perform_periodic_actions(&mut self) { - self.report_metrics(); - } - fn process_service_command(&mut self, command: ToServiceCommand) { match command { ToServiceCommand::SetSyncForkRequest(peers, hash, number) => { @@ -873,6 +864,9 @@ where log::debug!(target: LOG_TARGET, "{peer_id} does not exist in `SyncingEngine`"); return }; + if let Some(metrics) = &self.metrics { + metrics.peers.dec(); + } if self.important_peers.contains(&peer_id) { log::warn!(target: LOG_TARGET, "Reserved peer {peer_id} disconnected"); @@ -1048,7 +1042,11 @@ where log::debug!(target: LOG_TARGET, "Connected {peer_id}"); - self.peers.insert(peer_id, peer); + if self.peers.insert(peer_id, peer).is_none() { + if let Some(metrics) = &self.metrics { + metrics.peers.inc(); + } + } self.peer_store_handle.set_peer_role(&peer_id, status.roles.into()); if self.default_peers_set_no_slot_peers.contains(&peer_id) { diff --git a/substrate/client/network/sync/src/justification_requests.rs b/substrate/client/network/sync/src/justification_requests.rs index 2b50c85602d7..f2d7488b2c35 100644 --- a/substrate/client/network/sync/src/justification_requests.rs +++ b/substrate/client/network/sync/src/justification_requests.rs @@ -21,12 +21,14 @@ //! that don't make sense after one of the forks is finalized). use crate::{ - request_metrics::Metrics, strategy::chain_sync::{PeerSync, PeerSyncState}, LOG_TARGET, }; use fork_tree::ForkTree; use log::{debug, trace, warn}; +use prometheus_endpoint::{ + prometheus::core::GenericGauge, register, GaugeVec, Opts, PrometheusError, Registry, U64, +}; use sc_network_types::PeerId; use sp_blockchain::Error as ClientError; use sp_runtime::traits::{Block as BlockT, NumberFor, Zero}; @@ -41,6 +43,34 @@ const EXTRA_RETRY_WAIT: Duration = Duration::from_secs(10); /// Pending extra data request for the given block (hash and number). type ExtraRequest = (::Hash, NumberFor); +#[derive(Debug)] +struct Metrics { + pending: GenericGauge, + active: GenericGauge, + failed: GenericGauge, + importing: GenericGauge, +} + +impl Metrics { + fn register(registry: &Registry) -> Result { + let justifications = GaugeVec::::new( + Opts::new( + "substrate_sync_extra_justifications", + "Number of extra justifications requests", + ), + &["status"], + )?; + let justifications = register(justifications, registry)?; + + Ok(Self { + pending: justifications.with_label_values(&["pending"]), + active: justifications.with_label_values(&["active"]), + failed: justifications.with_label_values(&["failed"]), + importing: justifications.with_label_values(&["importing"]), + }) + } +} + /// Manages pending block extra data (e.g. justification) requests. /// /// Multiple extras may be requested for competing forks, or for the same branch @@ -62,10 +92,14 @@ pub(crate) struct ExtraRequests { importing_requests: HashSet>, /// the name of this type of extra request (useful for logging.) request_type_name: &'static str, + metrics: Option, } impl ExtraRequests { - pub(crate) fn new(request_type_name: &'static str) -> Self { + pub(crate) fn new( + request_type_name: &'static str, + metrics_registry: Option<&Registry>, + ) -> Self { Self { tree: ForkTree::new(), best_seen_finalized_number: Zero::zero(), @@ -74,6 +108,16 @@ impl ExtraRequests { failed_requests: HashMap::new(), importing_requests: HashSet::new(), request_type_name, + metrics: metrics_registry.and_then(|registry| { + Metrics::register(registry) + .inspect_err(|error| { + log::error!( + target: LOG_TARGET, + "Failed to register `ExtraRequests` metrics {error}", + ); + }) + .ok() + }), } } @@ -83,6 +127,12 @@ impl ExtraRequests { self.pending_requests.clear(); self.active_requests.clear(); self.failed_requests.clear(); + + if let Some(metrics) = &self.metrics { + metrics.pending.set(0); + metrics.active.set(0); + metrics.failed.set(0); + } } /// Returns an iterator-like struct that yields peers which extra @@ -100,6 +150,9 @@ impl ExtraRequests { Ok(true) => { // this is a new root so we add it to the current `pending_requests` self.pending_requests.push_back((request.0, request.1)); + if let Some(metrics) = &self.metrics { + metrics.pending.inc(); + } }, Err(fork_tree::Error::Revert) => { // we have finalized further than the given request, presumably @@ -117,6 +170,10 @@ impl ExtraRequests { pub(crate) fn peer_disconnected(&mut self, who: &PeerId) { if let Some(request) = self.active_requests.remove(who) { self.pending_requests.push_front(request); + if let Some(metrics) = &self.metrics { + metrics.active.dec(); + metrics.pending.inc(); + } } } @@ -130,13 +187,21 @@ impl ExtraRequests { // currently enforced by the outer network protocol before passing on // messages to chain sync. if let Some(request) = self.active_requests.remove(&who) { + if let Some(metrics) = &self.metrics { + metrics.active.dec(); + } + if let Some(r) = resp { trace!(target: LOG_TARGET, "Queuing import of {} from {:?} for {:?}", self.request_type_name, who, request, ); - self.importing_requests.insert(request); + if self.importing_requests.insert(request) { + if let Some(metrics) = &self.metrics { + metrics.importing.inc(); + } + } return Some((who, request.0, request.1, r)) } else { trace!(target: LOG_TARGET, @@ -146,6 +211,10 @@ impl ExtraRequests { } self.failed_requests.entry(request).or_default().push((who, Instant::now())); self.pending_requests.push_front(request); + if let Some(metrics) = &self.metrics { + metrics.failed.set(self.failed_requests.len().try_into().unwrap_or(u64::MAX)); + metrics.pending.inc(); + } } else { trace!(target: LOG_TARGET, "No active {} request to {:?}", @@ -194,6 +263,11 @@ impl ExtraRequests { self.pending_requests.retain(|(h, n)| roots.contains(&(h, n, &()))); self.active_requests.retain(|_, (h, n)| roots.contains(&(h, n, &()))); self.failed_requests.retain(|(h, n), _| roots.contains(&(h, n, &()))); + if let Some(metrics) = &self.metrics { + metrics.pending.set(self.pending_requests.len().try_into().unwrap_or(u64::MAX)); + metrics.active.set(self.active_requests.len().try_into().unwrap_or(u64::MAX)); + metrics.failed.set(self.failed_requests.len().try_into().unwrap_or(u64::MAX)); + } Ok(()) } @@ -210,12 +284,18 @@ impl ExtraRequests { if !self.importing_requests.remove(&request) { return false } + if let Some(metrics) = &self.metrics { + metrics.importing.dec(); + } let (finalized_hash, finalized_number) = match result { Ok(req) => (req.0, req.1), Err(_) => { if reschedule_on_failure { self.pending_requests.push_front(request); + if let Some(metrics) = &self.metrics { + metrics.pending.inc(); + } } return true }, @@ -233,6 +313,11 @@ impl ExtraRequests { self.active_requests.clear(); self.pending_requests.clear(); self.pending_requests.extend(self.tree.roots().map(|(&h, &n, _)| (h, n))); + if let Some(metrics) = &self.metrics { + metrics.failed.set(0); + metrics.active.set(0); + metrics.pending.set(self.pending_requests.len().try_into().unwrap_or(u64::MAX)); + } self.best_seen_finalized_number = finalized_number; true @@ -249,16 +334,6 @@ impl ExtraRequests { pub(crate) fn pending_requests(&self) -> impl Iterator> { self.pending_requests.iter() } - - /// Get some key metrics. - pub(crate) fn metrics(&self) -> Metrics { - Metrics { - pending_requests: self.pending_requests.len().try_into().unwrap_or(std::u32::MAX), - active_requests: self.active_requests.len().try_into().unwrap_or(std::u32::MAX), - failed_requests: self.failed_requests.len().try_into().unwrap_or(std::u32::MAX), - importing_requests: self.importing_requests.len().try_into().unwrap_or(std::u32::MAX), - } - } } /// Matches peers with pending extra requests. @@ -301,8 +376,17 @@ impl<'a, B: BlockT> Matcher<'a, B> { for requests in self.extras.failed_requests.values_mut() { requests.retain(|(_, instant)| instant.elapsed() < EXTRA_RETRY_WAIT); } + if let Some(metrics) = &self.extras.metrics { + metrics + .failed + .set(self.extras.failed_requests.len().try_into().unwrap_or(u64::MAX)); + } while let Some(request) = self.extras.pending_requests.pop_front() { + if let Some(metrics) = &self.extras.metrics { + metrics.pending.dec(); + } + for (peer, sync) in peers.iter().filter(|(_, sync)| sync.state == PeerSyncState::Available) { @@ -326,6 +410,9 @@ impl<'a, B: BlockT> Matcher<'a, B> { continue } self.extras.active_requests.insert(*peer, request); + if let Some(metrics) = &self.extras.metrics { + metrics.active.inc(); + } trace!(target: LOG_TARGET, "Sending {} request to {:?} for {:?}", @@ -336,6 +423,9 @@ impl<'a, B: BlockT> Matcher<'a, B> { } self.extras.pending_requests.push_back(request); + if let Some(metrics) = &self.extras.metrics { + metrics.pending.inc(); + } self.remaining -= 1; if self.remaining == 0 { @@ -359,7 +449,7 @@ mod tests { #[test] fn requests_are_processed_in_order() { fn property(mut peers: ArbitraryPeers) { - let mut requests = ExtraRequests::::new("test"); + let mut requests = ExtraRequests::::new("test", None); let num_peers_available = peers.0.values().filter(|s| s.state == PeerSyncState::Available).count(); @@ -385,7 +475,7 @@ mod tests { #[test] fn new_roots_schedule_new_request() { fn property(data: Vec) { - let mut requests = ExtraRequests::::new("test"); + let mut requests = ExtraRequests::::new("test", None); for (i, number) in data.into_iter().enumerate() { let hash = [i as u8; 32].into(); let pending = requests.pending_requests.len(); @@ -402,7 +492,7 @@ mod tests { #[test] fn disconnecting_implies_rescheduling() { fn property(mut peers: ArbitraryPeers) -> bool { - let mut requests = ExtraRequests::::new("test"); + let mut requests = ExtraRequests::::new("test", None); let num_peers_available = peers.0.values().filter(|s| s.state == PeerSyncState::Available).count(); @@ -438,7 +528,7 @@ mod tests { #[test] fn no_response_reschedules() { fn property(mut peers: ArbitraryPeers) { - let mut requests = ExtraRequests::::new("test"); + let mut requests = ExtraRequests::::new("test", None); let num_peers_available = peers.0.values().filter(|s| s.state == PeerSyncState::Available).count(); @@ -480,7 +570,7 @@ mod tests { fn request_is_rescheduled_when_earlier_block_is_finalized() { sp_tracing::try_init_simple(); - let mut finality_proofs = ExtraRequests::::new("test"); + let mut finality_proofs = ExtraRequests::::new("test", None); let hash4 = [4; 32].into(); let hash5 = [5; 32].into(); @@ -521,7 +611,7 @@ mod tests { #[test] fn ancestor_roots_are_finalized_when_finality_notification_is_missed() { - let mut finality_proofs = ExtraRequests::::new("test"); + let mut finality_proofs = ExtraRequests::::new("test", None); let hash4 = [4; 32].into(); let hash5 = [5; 32].into(); diff --git a/substrate/client/network/sync/src/strategy.rs b/substrate/client/network/sync/src/strategy.rs index 2c9799a9d836..ad3a9461c93b 100644 --- a/substrate/client/network/sync/src/strategy.rs +++ b/substrate/client/network/sync/src/strategy.rs @@ -210,7 +210,7 @@ where client.clone(), config.max_parallel_downloads, config.max_blocks_per_request, - config.metrics_registry.clone(), + config.metrics_registry.as_ref(), std::iter::empty(), )?; Ok(Self { @@ -455,13 +455,6 @@ where self.chain_sync.as_ref().map_or(0, |chain_sync| chain_sync.num_sync_requests()) } - /// Report Prometheus metrics - pub fn report_metrics(&self) { - if let Some(ref chain_sync) = self.chain_sync { - chain_sync.report_metrics(); - } - } - /// Get actions that should be performed by the owner on the strategy's behalf #[must_use] pub fn actions(&mut self) -> Result>, ClientError> { @@ -519,7 +512,7 @@ where self.client.clone(), self.config.max_parallel_downloads, self.config.max_blocks_per_request, - self.config.metrics_registry.clone(), + self.config.metrics_registry.as_ref(), self.peer_best_blocks.iter().map(|(peer_id, (best_hash, best_number))| { (*peer_id, *best_hash, *best_number) }), @@ -547,7 +540,7 @@ where self.client.clone(), self.config.max_parallel_downloads, self.config.max_blocks_per_request, - self.config.metrics_registry.clone(), + self.config.metrics_registry.as_ref(), self.peer_best_blocks.iter().map(|(peer_id, (best_hash, best_number))| { (*peer_id, *best_hash, *best_number) }), diff --git a/substrate/client/network/sync/src/strategy/chain_sync.rs b/substrate/client/network/sync/src/strategy/chain_sync.rs index 52870d5ba151..49e97ab11c37 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync.rs @@ -43,7 +43,7 @@ use crate::{ use codec::Encode; use log::{debug, error, info, trace, warn}; -use prometheus_endpoint::{register, Gauge, GaugeVec, Opts, PrometheusError, Registry, U64}; +use prometheus_endpoint::{register, Gauge, PrometheusError, Registry, U64}; use sc_client_api::{BlockBackend, ProofProvider}; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; use sc_network_common::sync::message::{ @@ -128,7 +128,6 @@ mod rep { struct Metrics { queued_blocks: Gauge, fork_targets: Gauge, - justifications: GaugeVec, } impl Metrics { @@ -143,16 +142,6 @@ impl Metrics { let g = Gauge::new("substrate_sync_fork_targets", "Number of fork sync targets")?; register(g, r)? }, - justifications: { - let g = GaugeVec::new( - Opts::new( - "substrate_sync_extra_justifications", - "Number of extra justifications requests", - ), - &["status"], - )?; - register(g, r)? - }, }) } } @@ -374,7 +363,7 @@ where client: Arc, max_parallel_downloads: u32, max_blocks_per_request: u32, - metrics_registry: Option, + metrics_registry: Option<&Registry>, initial_peers: impl Iterator)>, ) -> Result { let mut sync = Self { @@ -384,7 +373,7 @@ where blocks: BlockCollection::new(), best_queued_hash: Default::default(), best_queued_number: Zero::zero(), - extra_justifications: ExtraRequests::new("justification"), + extra_justifications: ExtraRequests::new("justification", metrics_registry), mode, queue_blocks: Default::default(), fork_targets: Default::default(), @@ -396,7 +385,7 @@ where import_existing: false, gap_sync: None, actions: Vec::new(), - metrics: metrics_registry.and_then(|r| match Metrics::register(&r) { + metrics: metrics_registry.and_then(|r| match Metrics::register(r) { Ok(metrics) => Some(metrics), Err(err) => { log::error!( @@ -676,7 +665,13 @@ where self.fork_targets .entry(*hash) - .or_insert_with(|| ForkTarget { number, peers: Default::default(), parent_hash: None }) + .or_insert_with(|| { + if let Some(metrics) = &self.metrics { + metrics.fork_targets.inc(); + } + + ForkTarget { number, peers: Default::default(), parent_hash: None } + }) .peers .extend(peers); } @@ -883,10 +878,16 @@ where ); self.fork_targets .entry(peer.best_hash) - .or_insert_with(|| ForkTarget { - number: peer.best_number, - parent_hash: None, - peers: Default::default(), + .or_insert_with(|| { + if let Some(metrics) = &self.metrics { + metrics.fork_targets.inc(); + } + + ForkTarget { + number: peer.best_number, + parent_hash: None, + peers: Default::default(), + } }) .peers .insert(*peer_id); @@ -1126,10 +1127,16 @@ where ); self.fork_targets .entry(hash) - .or_insert_with(|| ForkTarget { - number, - parent_hash: Some(*announce.header.parent_hash()), - peers: Default::default(), + .or_insert_with(|| { + if let Some(metrics) = &self.metrics { + metrics.fork_targets.inc(); + } + + ForkTarget { + number, + parent_hash: Some(*announce.header.parent_hash()), + peers: Default::default(), + } }) .peers .insert(peer_id); @@ -1161,6 +1168,9 @@ where target.peers.remove(peer_id); !target.peers.is_empty() }); + if let Some(metrics) = &self.metrics { + metrics.fork_targets.set(self.fork_targets.len().try_into().unwrap_or(u64::MAX)); + } let blocks = self.ready_blocks(); @@ -1169,36 +1179,6 @@ where } } - /// Report prometheus metrics. - pub fn report_metrics(&self) { - if let Some(metrics) = &self.metrics { - metrics - .fork_targets - .set(self.fork_targets.len().try_into().unwrap_or(std::u64::MAX)); - metrics - .queued_blocks - .set(self.queue_blocks.len().try_into().unwrap_or(std::u64::MAX)); - - let justifications_metrics = self.extra_justifications.metrics(); - metrics - .justifications - .with_label_values(&["pending"]) - .set(justifications_metrics.pending_requests.into()); - metrics - .justifications - .with_label_values(&["active"]) - .set(justifications_metrics.active_requests.into()); - metrics - .justifications - .with_label_values(&["failed"]) - .set(justifications_metrics.failed_requests.into()); - metrics - .justifications - .with_label_values(&["importing"]) - .set(justifications_metrics.importing_requests.into()); - } - } - /// Returns the median seen block number. fn median_seen(&self) -> Option> { let mut best_seens = self.peers.values().map(|p| p.best_number).collect::>(); @@ -1264,6 +1244,11 @@ where self.on_block_queued(h, n) } self.queue_blocks.extend(new_blocks.iter().map(|b| b.hash)); + if let Some(metrics) = &self.metrics { + metrics + .queued_blocks + .set(self.queue_blocks.len().try_into().unwrap_or(u64::MAX)); + } self.actions.push(ChainSyncAction::ImportBlocks { origin, blocks: new_blocks }) } @@ -1280,6 +1265,9 @@ where /// through all peers to update our view of their state as well. fn on_block_queued(&mut self, hash: &B::Hash, number: NumberFor) { if self.fork_targets.remove(hash).is_some() { + if let Some(metrics) = &self.metrics { + metrics.fork_targets.dec(); + } trace!(target: LOG_TARGET, "Completed fork sync {hash:?}"); } if let Some(gap_sync) = &mut self.gap_sync { @@ -1549,12 +1537,13 @@ where std::cmp::min(self.best_queued_number, self.client.info().finalized_number); let best_queued = self.best_queued_number; let client = &self.client; - let queue = &self.queue_blocks; + let queue_blocks = &self.queue_blocks; let allowed_requests = self.allowed_requests.take(); let max_parallel = if is_major_syncing { 1 } else { self.max_parallel_downloads }; let max_blocks_per_request = self.max_blocks_per_request; let gap_sync = &mut self.gap_sync; let disconnected_peers = &mut self.disconnected_peers; + let metrics = self.metrics.as_ref(); self.peers .iter_mut() .filter_map(move |(&id, peer)| { @@ -1574,7 +1563,7 @@ where MAX_BLOCKS_TO_LOOK_BACKWARDS.into() && best_queued < peer.best_number && peer.common_number < last_finalized && - queue.len() <= MAJOR_SYNC_BLOCKS.into() + queue_blocks.len() <= MAJOR_SYNC_BLOCKS.into() { trace!( target: LOG_TARGET, @@ -1617,13 +1606,14 @@ where last_finalized, attrs, |hash| { - if queue.contains(hash) { + if queue_blocks.contains(hash) { BlockStatus::Queued } else { client.block_status(*hash).unwrap_or(BlockStatus::Unknown) } }, max_blocks_per_request, + metrics, ) { trace!(target: LOG_TARGET, "Downloading fork {hash:?} from {id}"); peer.state = PeerSyncState::DownloadingStale(hash); @@ -1764,7 +1754,11 @@ where let mut has_error = false; for (_, hash) in &results { - self.queue_blocks.remove(hash); + if self.queue_blocks.remove(hash) { + if let Some(metrics) = &self.metrics { + metrics.queued_blocks.dec(); + } + } self.blocks.clear_queued(hash); if let Some(gap_sync) = &mut self.gap_sync { gap_sync.blocks.clear_queued(hash); @@ -2094,14 +2088,15 @@ fn peer_gap_block_request( /// Get pending fork sync targets for a peer. fn fork_sync_request( id: &PeerId, - targets: &mut HashMap>, + fork_targets: &mut HashMap>, best_num: NumberFor, finalized: NumberFor, attributes: BlockAttributes, check_block: impl Fn(&B::Hash) -> BlockStatus, max_blocks_per_request: u32, + metrics: Option<&Metrics>, ) -> Option<(B::Hash, BlockRequest)> { - targets.retain(|hash, r| { + fork_targets.retain(|hash, r| { if r.number <= finalized { trace!( target: LOG_TARGET, @@ -2122,7 +2117,10 @@ fn fork_sync_request( } true }); - for (hash, r) in targets { + if let Some(metrics) = metrics { + metrics.fork_targets.set(fork_targets.len().try_into().unwrap_or(u64::MAX)); + } + for (hash, r) in fork_targets { if !r.peers.contains(&id) { continue } From d26d7f1da6337789b047e0b3374343d793f18b2d Mon Sep 17 00:00:00 2001 From: eskimor Date: Fri, 23 Aug 2024 16:01:38 +0200 Subject: [PATCH 152/382] Remove panic, as proof is invalid. (#5427) `validity_vote` function is also called from `import_statement` not only from `import_candidate`. The proof still holds if we assume seconded statements always come before valid statements, which should be the case but is not actually enforced by this module at all. On top of this the proof is not local. Co-authored-by: eskimor --- polkadot/statement-table/src/generic.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/polkadot/statement-table/src/generic.rs b/polkadot/statement-table/src/generic.rs index e96ed6af73d9..1e90338a0f18 100644 --- a/polkadot/statement-table/src/generic.rs +++ b/polkadot/statement-table/src/generic.rs @@ -477,10 +477,7 @@ impl Table { if !context.is_member_of(&from, &votes.group_id) { let sig = match vote { ValidityVote::Valid(s) => s, - ValidityVote::Issued(_) => panic!( - "implicit issuance vote only cast from `import_candidate` after \ - checking group membership of issuer; qed" - ), + ValidityVote::Issued(s) => s, }; return Err(Misbehavior::UnauthorizedStatement(UnauthorizedStatement { From b3c2a25b73bb4854f26204068f0aec3e8577196c Mon Sep 17 00:00:00 2001 From: Yuri Volkov <0@mcornholio.ru> Date: Fri, 23 Aug 2024 16:03:34 +0200 Subject: [PATCH 153/382] Moving `Find FAIL-CI` check to GHA (#5377) --- .github/workflows/checks-quick.yml | 25 +++++++++++++++++++++++++ .gitlab/pipeline/check.yml | 20 -------------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/.github/workflows/checks-quick.yml b/.github/workflows/checks-quick.yml index 9bdf64c9979c..ee5ac31e9caa 100644 --- a/.github/workflows/checks-quick.yml +++ b/.github/workflows/checks-quick.yml @@ -156,3 +156,28 @@ jobs: git diff exit 1 fi + check-fail-ci: + runs-on: ubuntu-latest + container: + # there's no "rg" in ci-unified, and tools is a smaller image anyway + image: "paritytech/tools:latest" + # paritytech/tools uses "nonroot" user by default, which doesn't have enough + # permissions to create GHA context + options: --user root + steps: + - name: Fetch latest code + uses: actions/checkout@v4 + - name: Check + run: | + set +e + rg --line-number --hidden --type rust --glob '!{.git,target}' "$ASSERT_REGEX" .; exit_status=$? + if [ $exit_status -eq 0 ]; then + echo "$ASSERT_REGEX was found, exiting with 1"; + exit 1; + else + echo "No $ASSERT_REGEX was found, exiting with 0"; + exit 0; + fi + env: + ASSERT_REGEX: "FAIL-CI" + GIT_DEPTH: 1 diff --git a/.gitlab/pipeline/check.yml b/.gitlab/pipeline/check.yml index 2b8b90ef19a4..bc12dd474b07 100644 --- a/.gitlab/pipeline/check.yml +++ b/.gitlab/pipeline/check.yml @@ -122,26 +122,6 @@ check-runtime-migration-rococo: URI: "wss://rococo-try-runtime-node.parity-chains.parity.io:443" SUBCOMMAND_EXTRA_ARGS: "--no-weight-warnings" -find-fail-ci-phrase: - stage: check - variables: - CI_IMAGE: "paritytech/tools:latest" - ASSERT_REGEX: "FAIL-CI" - GIT_DEPTH: 1 - extends: - - .kubernetes-env - - .test-pr-refs - script: - - set +e - - rg --line-number --hidden --type rust --glob '!{.git,target}' "$ASSERT_REGEX" .; exit_status=$? - - if [ $exit_status -eq 0 ]; then - echo "$ASSERT_REGEX was found, exiting with 1"; - exit 1; - else - echo "No $ASSERT_REGEX was found, exiting with 0"; - exit 0; - fi - check-core-crypto-features: stage: check extends: From 13d43bb7c4f68bb0d1cdddf25894d8aedd069adb Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sun, 25 Aug 2024 01:35:50 +0300 Subject: [PATCH 154/382] Derive `Clone` on `EncodableOpaqueLeaf` (#5442) Needed it downstream --- prdoc/pr_5442.prdoc | 10 ++++++++++ substrate/primitives/merkle-mountain-range/src/lib.rs | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 prdoc/pr_5442.prdoc diff --git a/prdoc/pr_5442.prdoc b/prdoc/pr_5442.prdoc new file mode 100644 index 000000000000..6adc34d71ad3 --- /dev/null +++ b/prdoc/pr_5442.prdoc @@ -0,0 +1,10 @@ +title: Derive `Clone` on `EncodableOpaqueLeaf` + +doc: + - audience: Runtime Dev + description: | + `Clone` was derived on `EncodableOpaqueLeaf` for convenience of downstream users + +crates: + - name: sp-mmr-primitives + bump: patch diff --git a/substrate/primitives/merkle-mountain-range/src/lib.rs b/substrate/primitives/merkle-mountain-range/src/lib.rs index 3740047e0278..061e5dbb6c7d 100644 --- a/substrate/primitives/merkle-mountain-range/src/lib.rs +++ b/substrate/primitives/merkle-mountain-range/src/lib.rs @@ -144,7 +144,7 @@ impl FullLeaf for OpaqueLeaf { /// /// It is different from [`OpaqueLeaf`], because it does implement `Codec` /// and the encoding has to match raw `Vec` encoding. -#[derive(codec::Encode, codec::Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +#[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] pub struct EncodableOpaqueLeaf(pub Vec); impl EncodableOpaqueLeaf { From 475432f462450c3ca29b48066482765c87420ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Molina=20Colmenero?= Date: Sun, 25 Aug 2024 00:38:14 +0200 Subject: [PATCH 155/382] pallet-collator-selection: correctly register weight in `new_session` (#5430) The `pallet-collator-selection` is not correctly using the weight for the [new_session](https://github.com/blockdeep/pallet-collator-staking/blob/main/src/benchmarking.rs#L350-L353) function. The first parameter is the removed candidates, and the second one the original number of candidates before the removal, but both values are swapped. --- cumulus/pallets/collator-selection/src/lib.rs | 2 +- prdoc/pr_5430.prdoc | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 prdoc/pr_5430.prdoc diff --git a/cumulus/pallets/collator-selection/src/lib.rs b/cumulus/pallets/collator-selection/src/lib.rs index 17dc1a552c2d..9d7e62af3c68 100644 --- a/cumulus/pallets/collator-selection/src/lib.rs +++ b/cumulus/pallets/collator-selection/src/lib.rs @@ -972,7 +972,7 @@ pub mod pallet { let result = Self::assemble_collators(); frame_system::Pallet::::register_extra_weight_unchecked( - T::WeightInfo::new_session(candidates_len_before, removed), + T::WeightInfo::new_session(removed, candidates_len_before), DispatchClass::Mandatory, ); Some(result) diff --git a/prdoc/pr_5430.prdoc b/prdoc/pr_5430.prdoc new file mode 100644 index 000000000000..83d6d81e252e --- /dev/null +++ b/prdoc/pr_5430.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "pallet-collator-selection: correctly register weight in `new_session`" + +doc: + - audience: Runtime Dev + description: | + - Fixes an incorrect usage of the `WeightInfo` trait for `new_session`. + +crates: + - name: pallet-collator-selection + bump: patch From 91b5a49a3bfc5eabf92936de950690ce2557ed09 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Mon, 26 Aug 2024 00:20:25 +0200 Subject: [PATCH 156/382] Add symlinks for code of conduct and contribution guidelines (#5447) Addresses [this](https://github.com/paritytech/eng-automation/issues/10#issuecomment-2286391379). > Another side note is the checkmarks in https://github.com/paritytech/polkadot-sdk/community. It seems like even though we have `CONTRIBUTING.md` in `/docs` it is not picked up in this list. I am not even sure what is the benefit of following these standards, but it is probably for the best to do it. --- .github/PULL_REQUEST_TEMPLATE/pull_request_template.md | 1 + CODE_OF_CONDUCT.md | 1 + CONTRIBUTING.md | 1 + 3 files changed, 3 insertions(+) create mode 120000 .github/PULL_REQUEST_TEMPLATE/pull_request_template.md create mode 120000 CODE_OF_CONDUCT.md create mode 120000 CONTRIBUTING.md diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 120000 index 000000000000..3dd90d213692 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1 @@ +../../docs/contributor/PULL_REQUEST_TEMPLATE.md \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 120000 index 000000000000..63b2a0dc1abc --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +docs/contributor/CODE_OF_CONDUCT.md \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 120000 index 000000000000..0f645512e8e4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1 @@ +docs/contributor/CONTRIBUTING.md \ No newline at end of file From 178e699c7d9a9f399040e290943dd13873772c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lech=20G=C5=82owiak?= Date: Mon, 26 Aug 2024 00:30:08 +0200 Subject: [PATCH 157/382] Skip slot before creating inherent data providers during major sync (#5344) # Description Moves `create_inherent_data_provider` after checking if major sync is in progress. ## Integration Change is internal to sc-consensus-slots. It should be no-op unless someone is using fork of this SDK. ## Review Notes Motivation for this change is to avoid calling `create_inherent_data_providers` if it's result is going to be discarded anyway during major sync. This has potential to speed up node operations during major sync by not calling possibly expensive `create_inherent_data_provider`. TODO: labels T0-node D0-simple TODO: there is no tests for `Slots`, should I add one for this case? # Checklist * [x] My PR includes a detailed description as outlined in the "Description" and its two subsections above. * [x] My PR follows the [labeling requirements](CONTRIBUTING.md#Process) of this project (at minimum one label for `T` required) * External contributors: ask maintainers to put the right label on your PR. * [ ] I have made corresponding changes to the documentation (if applicable) * [ ] I have added tests that prove my fix is effective or that my feature works (if applicable) --- prdoc/pr_5344.prdoc | 10 ++++++++++ substrate/client/consensus/slots/src/lib.rs | 13 ++++++------- substrate/client/consensus/slots/src/slots.rs | 17 +++++++++++++---- 3 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 prdoc/pr_5344.prdoc diff --git a/prdoc/pr_5344.prdoc b/prdoc/pr_5344.prdoc new file mode 100644 index 000000000000..9f83c113686d --- /dev/null +++ b/prdoc/pr_5344.prdoc @@ -0,0 +1,10 @@ +title: Fix storage weight reclaim bug. + +doc: + - audience: Node Dev + description: | + Improvement in slot worker loop that will not call create inherent data providers if the major sync is in progress. Before it was called every slot and the results were discarded during major sync. + +crates: + - name: sc-consensus-slots + bump: minor diff --git a/substrate/client/consensus/slots/src/lib.rs b/substrate/client/consensus/slots/src/lib.rs index 7cdf90877dff..06e0756fc968 100644 --- a/substrate/client/consensus/slots/src/lib.rs +++ b/substrate/client/consensus/slots/src/lib.rs @@ -517,16 +517,15 @@ pub async fn start_slot_worker( CIDP: CreateInherentDataProviders + Send + 'static, CIDP::InherentDataProviders: InherentDataProviderExt + Send, { - let mut slots = Slots::new(slot_duration.as_duration(), create_inherent_data_providers, client); + let mut slots = Slots::new( + slot_duration.as_duration(), + create_inherent_data_providers, + client, + sync_oracle, + ); loop { let slot_info = slots.next_slot().await; - - if sync_oracle.is_major_syncing() { - debug!(target: LOG_TARGET, "Skipping proposal slot due to sync."); - continue - } - let _ = worker.on_slot(slot_info).await; } } diff --git a/substrate/client/consensus/slots/src/slots.rs b/substrate/client/consensus/slots/src/slots.rs index 203764310601..c0b412e8ad5b 100644 --- a/substrate/client/consensus/slots/src/slots.rs +++ b/substrate/client/consensus/slots/src/slots.rs @@ -21,7 +21,7 @@ //! This is used instead of `futures_timer::Interval` because it was unreliable. use super::{InherentDataProviderExt, Slot, LOG_TARGET}; -use sp_consensus::SelectChain; +use sp_consensus::{SelectChain, SyncOracle}; use sp_inherents::{CreateInherentDataProviders, InherentDataProvider}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; @@ -87,21 +87,23 @@ impl SlotInfo { } /// A stream that returns every time there is a new slot. -pub(crate) struct Slots { +pub(crate) struct Slots { last_slot: Slot, slot_duration: Duration, until_next_slot: Option, create_inherent_data_providers: IDP, select_chain: SC, + sync_oracle: SO, _phantom: std::marker::PhantomData, } -impl Slots { +impl Slots { /// Create a new `Slots` stream. pub fn new( slot_duration: Duration, create_inherent_data_providers: IDP, select_chain: SC, + sync_oracle: SO, ) -> Self { Slots { last_slot: 0.into(), @@ -109,17 +111,19 @@ impl Slots { until_next_slot: None, create_inherent_data_providers, select_chain, + sync_oracle, _phantom: Default::default(), } } } -impl Slots +impl Slots where Block: BlockT, SC: SelectChain, IDP: CreateInherentDataProviders + 'static, IDP::InherentDataProviders: crate::InherentDataProviderExt, + SO: SyncOracle, { /// Returns a future that fires when the next slot starts. pub async fn next_slot(&mut self) -> SlotInfo { @@ -138,6 +142,11 @@ where let wait_dur = time_until_next_slot(self.slot_duration); self.until_next_slot = Some(Delay::new(wait_dur)); + if self.sync_oracle.is_major_syncing() { + log::debug!(target: LOG_TARGET, "Skipping slot: major sync is in progress."); + continue; + } + let chain_head = match self.select_chain.best_chain().await { Ok(x) => x, Err(e) => { From ad0de7495e0e4685408d70c755cddaa14e3b02ca Mon Sep 17 00:00:00 2001 From: Muharem Date: Mon, 26 Aug 2024 15:23:20 +0200 Subject: [PATCH 158/382] `MaybeConsideration` extension trait for `Consideration` (#5384) Introduce `MaybeConsideration` extension trait for `Consideration`. The trait allows for the management of tickets that may represent no cost. While the `MaybeConsideration` still requires proper handling, it introduces the ability to determine if a ticket represents no cost and can be safely forgotten without any side effects. The new trait is particularly useful when a consumer expects the cost to be zero under certain conditions (e.g., when the proposal count is below a threshold N) and does not want to store such consideration tickets in storage. The extension approach allows us to avoid breaking changes to the existing trait and to continue using it as a non-optional version for migrating pallets that utilize the `Currency` and `fungible` traits for `holds` and `freezes`, without requiring any storage migration. --- prdoc/pr_5384.prdoc | 25 ++++++++++++++++ .../balances/src/tests/fungible_tests.rs | 12 ++++++-- substrate/frame/support/src/traits.rs | 2 ++ substrate/frame/support/src/traits/storage.rs | 18 +++++++++++ .../support/src/traits/tokens/fungible/mod.rs | 30 +++++++++++++++++++ 5 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 prdoc/pr_5384.prdoc diff --git a/prdoc/pr_5384.prdoc b/prdoc/pr_5384.prdoc new file mode 100644 index 000000000000..74d477f8e153 --- /dev/null +++ b/prdoc/pr_5384.prdoc @@ -0,0 +1,25 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "`MaybeConsideration` extension trait for `Consideration`" + +doc: + - audience: Runtime Dev + description: | + The trait allows for the management of tickets that may represent no cost. While + the `MaybeConsideration` still requires proper handling, it introduces the ability + to determine if a ticket represents no cost and can be safely forgotten without any + side effects. + + The new trait is particularly useful when a consumer expects the cost to be zero under + certain conditions (e.g., when the proposal count is below a threshold N) and does not want + to store such consideration tickets in storage. The extension approach allows us to avoid + breaking changes to the existing trait and to continue using it as a non-optional version + for migrating pallets that utilize the `Currency` and `fungible` traits for `holds` and + `freezes`, without requiring any storage migration. + +crates: + - name: frame-support + bump: minor + - name: pallet-balances + bump: patch diff --git a/substrate/frame/balances/src/tests/fungible_tests.rs b/substrate/frame/balances/src/tests/fungible_tests.rs index e1c1be9b1335..ac373a351d34 100644 --- a/substrate/frame/balances/src/tests/fungible_tests.rs +++ b/substrate/frame/balances/src/tests/fungible_tests.rs @@ -25,7 +25,7 @@ use frame_support::traits::{ Preservation::{Expendable, Preserve, Protect}, Restriction::Free, }, - Consideration, Footprint, LinearStoragePrice, + Consideration, Footprint, LinearStoragePrice, MaybeConsideration, }; use fungible::{ FreezeConsideration, HoldConsideration, Inspect, InspectFreeze, InspectHold, @@ -519,6 +519,7 @@ fn freeze_consideration_works() { // freeze amount taken somewhere outside of our (Consideration) scope. let extend_freeze = 15; let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); + assert!(zero_ticket.is_none()); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); @@ -533,7 +534,9 @@ fn freeze_consideration_works() { let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 8 + extend_freeze); - assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), zero_ticket); + let ticket = ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(); + assert!(ticket.is_none()); + assert_eq!(ticket, zero_ticket); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0 + extend_freeze); let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); @@ -563,6 +566,7 @@ fn hold_consideration_works() { let extend_hold = 15; let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); + assert!(zero_ticket.is_none()); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); @@ -577,7 +581,9 @@ fn hold_consideration_works() { let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 8 + extend_hold); - assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), zero_ticket); + let ticket = ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(); + assert!(ticket.is_none()); + assert_eq!(ticket, zero_ticket); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0 + extend_hold); let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index a423656c394f..f635ed32a124 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -92,6 +92,8 @@ pub use hooks::{ pub mod schedule; mod storage; +#[cfg(feature = "experimental")] +pub use storage::MaybeConsideration; pub use storage::{ Consideration, Footprint, Incrementable, Instance, LinearStoragePrice, PartialStorageInfoTrait, StorageInfo, StorageInfoTrait, StorageInstance, TrackedStorageKey, WhitelistedStorageKeys, diff --git a/substrate/frame/support/src/traits/storage.rs b/substrate/frame/support/src/traits/storage.rs index eb63ea59f6cb..2b8e43707389 100644 --- a/substrate/frame/support/src/traits/storage.rs +++ b/substrate/frame/support/src/traits/storage.rs @@ -257,6 +257,24 @@ impl Consideration for () { fn ensure_successful(_: &A, _: F) {} } +#[cfg(feature = "experimental")] +/// An extension of the [`Consideration`] trait that allows for the management of tickets that may +/// represent no cost. While the [`MaybeConsideration`] still requires proper handling, it +/// introduces the ability to determine if a ticket represents no cost and can be safely forgotten +/// without any side effects. +pub trait MaybeConsideration: Consideration { + /// Returns `true` if this [`Consideration`] represents a no-cost ticket and can be forgotten + /// without any side effects. + fn is_none(&self) -> bool; +} + +#[cfg(feature = "experimental")] +impl MaybeConsideration for () { + fn is_none(&self) -> bool { + true + } +} + macro_rules! impl_incrementable { ($($type:ty),+) => { $( diff --git a/substrate/frame/support/src/traits/tokens/fungible/mod.rs b/substrate/frame/support/src/traits/tokens/fungible/mod.rs index 9b7c86971bb8..c67755e133bf 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/mod.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/mod.rs @@ -186,6 +186,8 @@ use sp_core::Get; use sp_runtime::{traits::Convert, DispatchError}; pub use union_of::{NativeFromLeft, NativeOrWithId, UnionOf}; +#[cfg(feature = "experimental")] +use crate::traits::MaybeConsideration; use crate::{ ensure, traits::{Consideration, Footprint}, @@ -241,6 +243,20 @@ impl< let _ = F::mint_into(who, F::minimum_balance().saturating_add(D::convert(fp))); } } +#[cfg(feature = "experimental")] +impl< + A: 'static + Eq, + #[cfg(not(feature = "runtime-benchmarks"))] F: 'static + MutateFreeze, + #[cfg(feature = "runtime-benchmarks")] F: 'static + MutateFreeze + Mutate, + R: 'static + Get, + D: 'static + Convert, + Fp: 'static, + > MaybeConsideration for FreezeConsideration +{ + fn is_none(&self) -> bool { + self.0.is_zero() + } +} /// Consideration method using a `fungible` balance frozen as the cost exacted for the footprint. #[derive( @@ -295,6 +311,20 @@ impl< let _ = F::mint_into(who, F::minimum_balance().saturating_add(D::convert(fp))); } } +#[cfg(feature = "experimental")] +impl< + A: 'static + Eq, + #[cfg(not(feature = "runtime-benchmarks"))] F: 'static + MutateHold, + #[cfg(feature = "runtime-benchmarks")] F: 'static + MutateHold + Mutate, + R: 'static + Get, + D: 'static + Convert, + Fp: 'static, + > MaybeConsideration for HoldConsideration +{ + fn is_none(&self) -> bool { + self.0.is_zero() + } +} /// Basic consideration method using a `fungible` balance frozen as the cost exacted for the /// footprint. From 3cbefaf197b5a5633b61d724f1b9a92fb7790ebb Mon Sep 17 00:00:00 2001 From: Egor_P Date: Mon, 26 Aug 2024 16:44:03 +0200 Subject: [PATCH 159/382] Add build options to the srtool build step (#4956) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds possibility to set BUILD_OPTIONS to the "Srtool Build" step in the release pipeline while building runtimes. Colses: https://github.com/paritytech/release-engineering/issues/213 --------- Co-authored-by: Bastian Köcher Co-authored-by: EgorPopelyaev --- .../release-30_publish_release_draft.yml | 1 + .github/workflows/release-srtool.yml | 4 ++ .../assets/asset-hub-rococo/Cargo.toml | 2 +- .../assets/asset-hub-westend/Cargo.toml | 2 +- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 2 +- .../bridge-hubs/bridge-hub-westend/Cargo.toml | 2 +- .../collectives-westend/Cargo.toml | 2 +- .../contracts/contracts-rococo/Cargo.toml | 2 +- .../coretime/coretime-rococo/Cargo.toml | 2 +- .../coretime/coretime-westend/Cargo.toml | 2 +- .../glutton/glutton-westend/Cargo.toml | 2 +- .../runtimes/people/people-rococo/Cargo.toml | 5 +++ .../runtimes/people/people-westend/Cargo.toml | 5 +++ .../testing/rococo-parachain/Cargo.toml | 5 +++ polkadot/runtime/rococo/Cargo.toml | 2 +- polkadot/runtime/westend/Cargo.toml | 2 +- prdoc/pr_4956.prdoc | 39 +++++++++++++++++++ 17 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 prdoc/pr_4956.prdoc diff --git a/.github/workflows/release-30_publish_release_draft.yml b/.github/workflows/release-30_publish_release_draft.yml index 6d31ca7a7365..4343dbf915a9 100644 --- a/.github/workflows/release-30_publish_release_draft.yml +++ b/.github/workflows/release-30_publish_release_draft.yml @@ -26,6 +26,7 @@ jobs: uses: "./.github/workflows/release-srtool.yml" with: excluded_runtimes: "substrate-test bp cumulus-test kitchensink minimal-template parachain-template penpal polkadot-test seedling shell frame-try sp solochain-template" + build_opts: "--features on-chain-release-build" build-binaries: runs-on: ubuntu-latest diff --git a/.github/workflows/release-srtool.yml b/.github/workflows/release-srtool.yml index e1dc42afc6e9..262203f05005 100644 --- a/.github/workflows/release-srtool.yml +++ b/.github/workflows/release-srtool.yml @@ -9,6 +9,8 @@ on: inputs: excluded_runtimes: type: string + build_opts: + type: string outputs: published_runtimes: value: ${{ jobs.find-runtimes.outputs.runtime }} @@ -74,6 +76,8 @@ jobs: - name: Srtool build id: srtool_build uses: chevdor/srtool-actions@v0.9.2 + env: + BUILD_OPTS: ${{ inputs.build_opts }} with: chain: ${{ matrix.chain }} runtime_dir: ${{ matrix.runtime_dir }} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 98df41090a40..0143c09036d2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -258,4 +258,4 @@ metadata-hash = ["substrate-wasm-builder/metadata-hash"] # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] +on-chain-release-build = ["metadata-hash"] diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 2f244a07e8f1..77130ff846b5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -262,4 +262,4 @@ metadata-hash = ["substrate-wasm-builder/metadata-hash"] # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] +on-chain-release-build = ["metadata-hash"] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index f85861bdaa54..6d0fbd7d5c66 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -305,4 +305,4 @@ fast-runtime = [] # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["sp-api/disable-logging"] +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index a9381501359e..1c9d8c0207b9 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -292,6 +292,6 @@ try-runtime = [ # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["sp-api/disable-logging"] +on-chain-release-build = [] fast-runtime = [] diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml index 43fc9083937c..e98508ea02e6 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml @@ -243,4 +243,4 @@ std = [ # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["sp-api/disable-logging"] +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml index 1fcebb3f16a9..dfa75b8d3cf3 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml @@ -203,4 +203,4 @@ try-runtime = [ # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["sp-api/disable-logging"] +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml index 2920bc428d90..07d133c80be7 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml @@ -209,4 +209,4 @@ metadata-hash = ["substrate-wasm-builder/metadata-hash"] # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] +on-chain-release-build = ["metadata-hash"] diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml index 07a4332800d7..5029c82f971d 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml @@ -206,4 +206,4 @@ metadata-hash = ["substrate-wasm-builder/metadata-hash"] # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] +on-chain-release-build = ["metadata-hash"] diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml b/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml index d20b62a557b9..09b4ef679d24 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml @@ -136,4 +136,4 @@ try-runtime = [ # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["sp-api/disable-logging"] +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml index a732bec2352d..c676587b1de4 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml @@ -191,3 +191,8 @@ try-runtime = [ "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", ] + +# A feature that should be enabled when the runtime should be built for on-chain +# deployment. This will disable stuff that shouldn't be part of the on-chain wasm +# to make it smaller, like logging for example. +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml index 20c7e691ebc8..ab7dd04bb78a 100644 --- a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml @@ -191,3 +191,8 @@ try-runtime = [ "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", ] + +# A feature that should be enabled when the runtime should be built for on-chain +# deployment. This will disable stuff that shouldn't be part of the on-chain wasm +# to make it smaller, like logging for example. +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml index a0ad248bb704..9c905c876277 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml @@ -134,3 +134,8 @@ runtime-benchmarks = [ "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", ] + +# A feature that should be enabled when the runtime should be built for on-chain +# deployment. This will disable stuff that shouldn't be part of the on-chain wasm +# to make it smaller, like logging for example. +on-chain-release-build = [] diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index 50970965e11e..4aaaf94da586 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -339,4 +339,4 @@ runtime-metrics = [ # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] +on-chain-release-build = ["metadata-hash"] diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index 4226595cd2ff..83c0eb037f4a 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -358,4 +358,4 @@ runtime-metrics = [ # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] +on-chain-release-build = ["metadata-hash"] diff --git a/prdoc/pr_4956.prdoc b/prdoc/pr_4956.prdoc new file mode 100644 index 000000000000..a72ca303aaca --- /dev/null +++ b/prdoc/pr_4956.prdoc @@ -0,0 +1,39 @@ +title: Add build options to the srtool build step and delete `disanle-logging` feature + +doc: +- audience: Runtime Dev + description: | + This PR adds possibility to set BUILD_OPTIONS to the "Srtool Build\" step in the release pipeline while building runtimes. + And deletes the `disable-logging` feature from test runtimes to be able to build those with an activated logging. + + + +crates: +- name: people-rococo-runtime + bump: patch +- name: people-westend-runtime + bump: patch +- name: rococo-parachain-runtime + bump: patch +- name: asset-hub-rococo-runtime + bump: patch +- name: asset-hub-westend-runtime + bump: patch +- name: bridge-hub-rococo-runtime + bump: patch +- name: bridge-hub-westend-runtime + bump: patch +- name: collectives-westend-runtime + bump: patch +- name: contracts-rococo-runtime + bump: patch +- name: coretime-rococo-runtime + bump: patch +- name: coretime-westend-runtime + bump: patch +- name: glutton-westend-runtime + bump: patch +- name: rococo-runtime + bump: patch +- name: westend-runtime + bump: patch From dd1aaa4713b93607804bed8adeaed6f98f3e5aef Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 26 Aug 2024 18:37:51 +0300 Subject: [PATCH 160/382] Sync status refactoring (#5450) As I was looking at the coupling between `SyncingEngine`, `SyncingStrategy` and individual strategies I noticed a few things that were unused, redundant or awkward. The awkward change comes from https://github.com/paritytech/substrate/pull/13700 where `num_connected_peers` property was added to `SyncStatus` struct just so it can be rendered in the informer. While convenient, the property didn't really belong there and was annoyingly set to `0` in some strategies and to `num_peers` in others. I have replaced that with a property on `SyncingService` that already stored necessary information internally. Also `ExtendedPeerInfo` didn't have a working `Clone` implementation due to lack of perfect derive in Rust and while I ended up not using it in the refactoring, I included fixed implementation for it in this PR anyway. While these changes are not strictly necessary for https://github.com/paritytech/polkadot-sdk/issues/5333, they do reduce coupling of syncing engine with syncing strategy, which I thought is a good thing. Reviewing individual commits will be the easiest as usual. --------- Co-authored-by: Dmitry Markin --- prdoc/pr_5450.prdoc | 18 +++++++++ substrate/client/informant/src/display.rs | 2 +- substrate/client/informant/src/lib.rs | 14 +++---- substrate/client/network/sync/src/engine.rs | 26 +++---------- .../sync/src/service/syncing_service.rs | 37 ++++--------------- .../network/sync/src/strategy/chain_sync.rs | 1 - .../client/network/sync/src/strategy/state.rs | 1 - .../client/network/sync/src/strategy/warp.rs | 1 - substrate/client/network/sync/src/types.rs | 15 ++++++-- substrate/client/network/test/src/lib.rs | 10 ++--- substrate/client/network/test/src/sync.rs | 2 +- substrate/client/service/src/lib.rs | 2 +- 12 files changed, 56 insertions(+), 73 deletions(-) create mode 100644 prdoc/pr_5450.prdoc diff --git a/prdoc/pr_5450.prdoc b/prdoc/pr_5450.prdoc new file mode 100644 index 000000000000..ccd319cad246 --- /dev/null +++ b/prdoc/pr_5450.prdoc @@ -0,0 +1,18 @@ +title: Sync status refactoring + +doc: + - audience: Node Dev + description: | + `SyncingService` API in `sc-network-sync` has changed with some of the redundant methods related to sync status + removed that were mostly used internally or for testing purposes and is unlikely to impact external code. + `ExtendedPeerInfo` now has working `Clone` and `Copy` implementation. + +crates: + - name: sc-informant + bump: major + - name: sc-network-sync + bump: major + - name: sc-network-test + bump: major + - name: sc-service + bump: major diff --git a/substrate/client/informant/src/display.rs b/substrate/client/informant/src/display.rs index f5e042103ea7..2decd7674782 100644 --- a/substrate/client/informant/src/display.rs +++ b/substrate/client/informant/src/display.rs @@ -65,11 +65,11 @@ impl InformantDisplay { info: &ClientInfo, net_status: NetworkStatus, sync_status: SyncStatus, + num_connected_peers: usize, ) { let best_number = info.chain.best_number; let best_hash = info.chain.best_hash; let finalized_number = info.chain.finalized_number; - let num_connected_peers = sync_status.num_connected_peers; let speed = speed::(best_number, self.last_number, self.last_update); let total_bytes_inbound = net_status.total_bytes_inbound; let total_bytes_outbound = net_status.total_bytes_outbound; diff --git a/substrate/client/informant/src/lib.rs b/substrate/client/informant/src/lib.rs index d44364539a29..0b0e13dc08bb 100644 --- a/substrate/client/informant/src/lib.rs +++ b/substrate/client/informant/src/lib.rs @@ -24,7 +24,7 @@ use futures_timer::Delay; use log::{debug, info, trace}; use sc_client_api::{BlockchainEvents, UsageProvider}; use sc_network::NetworkStatusProvider; -use sc_network_sync::SyncStatusProvider; +use sc_network_sync::{SyncStatusProvider, SyncingService}; use sp_blockchain::HeaderMetadata; use sp_runtime::traits::{Block as BlockT, Header}; use std::{collections::VecDeque, fmt::Display, sync::Arc, time::Duration}; @@ -37,10 +37,9 @@ fn interval(duration: Duration) -> impl Stream + Unpin { } /// Builds the informant and returns a `Future` that drives the informant. -pub async fn build(client: Arc, network: N, syncing: S) +pub async fn build(client: Arc, network: N, syncing: Arc>) where N: NetworkStatusProvider, - S: SyncStatusProvider, C: UsageProvider + HeaderMetadata + BlockchainEvents, >::Error: Display, { @@ -52,13 +51,14 @@ where .filter_map(|_| async { let net_status = network.status().await; let sync_status = syncing.status().await; + let num_connected_peers = syncing.num_connected_peers(); - match (net_status.ok(), sync_status.ok()) { - (Some(net), Some(sync)) => Some((net, sync)), + match (net_status, sync_status) { + (Ok(net), Ok(sync)) => Some((net, sync, num_connected_peers)), _ => None, } }) - .for_each(move |(net_status, sync_status)| { + .for_each(move |(net_status, sync_status, num_connected_peers)| { let info = client_1.usage_info(); if let Some(ref usage) = info.usage { trace!(target: "usage", "Usage statistics: {}", usage); @@ -68,7 +68,7 @@ where "Usage statistics not displayed as backend does not provide it", ) } - display.display(&info, net_status, sync_status); + display.display(&info, net_status, sync_status, num_connected_peers); future::ready(()) }); diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index 616dd3168872..4a57d61df457 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -615,7 +615,6 @@ where } // Update atomic variables - self.num_connected.store(self.peers.len(), Ordering::Relaxed); self.is_major_syncing.store(self.strategy.is_major_syncing(), Ordering::Relaxed); // Process actions requested by a syncing strategy. @@ -761,25 +760,11 @@ where ); }, ToServiceCommand::Status(tx) => { - let mut status = self.strategy.status(); - status.num_connected_peers = self.peers.len() as u32; - let _ = tx.send(status); + let _ = tx.send(self.strategy.status()); }, ToServiceCommand::NumActivePeers(tx) => { let _ = tx.send(self.num_active_peers()); }, - ToServiceCommand::SyncState(tx) => { - let _ = tx.send(self.strategy.status()); - }, - ToServiceCommand::BestSeenBlock(tx) => { - let _ = tx.send(self.strategy.status().best_seen_block); - }, - ToServiceCommand::NumSyncPeers(tx) => { - let _ = tx.send(self.strategy.status().num_peers); - }, - ToServiceCommand::NumQueuedBlocks(tx) => { - let _ = tx.send(self.strategy.status().queued_blocks); - }, ToServiceCommand::NumDownloadedBlocks(tx) => { let _ = tx.send(self.strategy.num_downloaded_blocks()); }, @@ -787,11 +772,8 @@ where let _ = tx.send(self.strategy.num_sync_requests()); }, ToServiceCommand::PeersInfo(tx) => { - let peers_info = self - .peers - .iter() - .map(|(peer_id, peer)| (*peer_id, peer.info.clone())) - .collect(); + let peers_info = + self.peers.iter().map(|(peer_id, peer)| (*peer_id, peer.info)).collect(); let _ = tx.send(peers_info); }, ToServiceCommand::OnBlockFinalized(hash, header) => @@ -867,6 +849,7 @@ where if let Some(metrics) = &self.metrics { metrics.peers.dec(); } + self.num_connected.fetch_sub(1, Ordering::AcqRel); if self.important_peers.contains(&peer_id) { log::warn!(target: LOG_TARGET, "Reserved peer {peer_id} disconnected"); @@ -1046,6 +1029,7 @@ where if let Some(metrics) = &self.metrics { metrics.peers.inc(); } + self.num_connected.fetch_add(1, Ordering::AcqRel); } self.peer_store_handle.set_peer_role(&peer_id, status.roles.into()); diff --git a/substrate/client/network/sync/src/service/syncing_service.rs b/substrate/client/network/sync/src/service/syncing_service.rs index f4bc58afd4fd..08a2b36118a9 100644 --- a/substrate/client/network/sync/src/service/syncing_service.rs +++ b/substrate/client/network/sync/src/service/syncing_service.rs @@ -50,10 +50,6 @@ pub enum ToServiceCommand { EventStream(TracingUnboundedSender), Status(oneshot::Sender>), NumActivePeers(oneshot::Sender), - SyncState(oneshot::Sender>), - BestSeenBlock(oneshot::Sender>>), - NumSyncPeers(oneshot::Sender), - NumQueuedBlocks(oneshot::Sender), NumDownloadedBlocks(oneshot::Sender), NumSyncRequests(oneshot::Sender), PeersInfo(oneshot::Sender)>>), @@ -83,6 +79,11 @@ impl SyncingService { Self { tx, num_connected, is_major_syncing } } + /// Get the number of peers known to `SyncingEngine` (both full and light). + pub fn num_connected_peers(&self) -> usize { + self.num_connected.load(Ordering::Relaxed) + } + /// Get the number of active peers. pub async fn num_active_peers(&self) -> Result { let (tx, rx) = oneshot::channel(); @@ -91,30 +92,6 @@ impl SyncingService { rx.await } - /// Get best seen block. - pub async fn best_seen_block(&self) -> Result>, oneshot::Canceled> { - let (tx, rx) = oneshot::channel(); - let _ = self.tx.unbounded_send(ToServiceCommand::BestSeenBlock(tx)); - - rx.await - } - - /// Get the number of sync peers. - pub async fn num_sync_peers(&self) -> Result { - let (tx, rx) = oneshot::channel(); - let _ = self.tx.unbounded_send(ToServiceCommand::NumSyncPeers(tx)); - - rx.await - } - - /// Get the number of queued blocks. - pub async fn num_queued_blocks(&self) -> Result { - let (tx, rx) = oneshot::channel(); - let _ = self.tx.unbounded_send(ToServiceCommand::NumQueuedBlocks(tx)); - - rx.await - } - /// Get the number of downloaded blocks. pub async fn num_downloaded_blocks(&self) -> Result { let (tx, rx) = oneshot::channel(); @@ -149,11 +126,11 @@ impl SyncingService { /// Get sync status /// /// Returns an error if `SyncingEngine` has terminated. - pub async fn status(&self) -> Result, ()> { + pub async fn status(&self) -> Result, oneshot::Canceled> { let (tx, rx) = oneshot::channel(); let _ = self.tx.unbounded_send(ToServiceCommand::Status(tx)); - rx.await.map_err(|_| ()) + rx.await } } diff --git a/substrate/client/network/sync/src/strategy/chain_sync.rs b/substrate/client/network/sync/src/strategy/chain_sync.rs index 49e97ab11c37..21e474048625 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync.rs @@ -438,7 +438,6 @@ where state: sync_state, best_seen_block, num_peers: self.peers.len() as u32, - num_connected_peers: 0u32, queued_blocks: self.queue_blocks.len() as u32, state_sync: self.state_sync.as_ref().map(|s| s.progress()), warp_sync: warp_sync_progress, diff --git a/substrate/client/network/sync/src/strategy/state.rs b/substrate/client/network/sync/src/strategy/state.rs index ff229863a68b..6f06f238fe3a 100644 --- a/substrate/client/network/sync/src/strategy/state.rs +++ b/substrate/client/network/sync/src/strategy/state.rs @@ -340,7 +340,6 @@ impl StateStrategy { }, best_seen_block: Some(self.state_sync.target_number()), num_peers: self.peers.len().saturated_into(), - num_connected_peers: self.peers.len().saturated_into(), queued_blocks: 0, state_sync: Some(self.state_sync.progress()), warp_sync: None, diff --git a/substrate/client/network/sync/src/strategy/warp.rs b/substrate/client/network/sync/src/strategy/warp.rs index 4cc3e9f3a68b..99405c2e5f08 100644 --- a/substrate/client/network/sync/src/strategy/warp.rs +++ b/substrate/client/network/sync/src/strategy/warp.rs @@ -576,7 +576,6 @@ where Phase::Complete => None, }, num_peers: self.peers.len().saturated_into(), - num_connected_peers: self.peers.len().saturated_into(), queued_blocks: 0, state_sync: None, warp_sync: Some(self.progress()), diff --git a/substrate/client/network/sync/src/types.rs b/substrate/client/network/sync/src/types.rs index e8b8c8900360..c3403fe1e5f7 100644 --- a/substrate/client/network/sync/src/types.rs +++ b/substrate/client/network/sync/src/types.rs @@ -39,7 +39,7 @@ pub struct PeerInfo { } /// Info about a peer's known state (both full and light). -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct ExtendedPeerInfo { /// Roles pub roles: Roles, @@ -49,6 +49,17 @@ pub struct ExtendedPeerInfo { pub best_number: NumberFor, } +impl Clone for ExtendedPeerInfo +where + B: BlockT, +{ + fn clone(&self) -> Self { + Self { roles: self.roles, best_hash: self.best_hash, best_number: self.best_number } + } +} + +impl Copy for ExtendedPeerInfo where B: BlockT {} + /// Reported sync state. #[derive(Clone, Eq, PartialEq, Debug)] pub enum SyncState { @@ -76,8 +87,6 @@ pub struct SyncStatus { pub best_seen_block: Option>, /// Number of peers participating in syncing. pub num_peers: u32, - /// Number of peers known to `SyncingEngine` (both full and light). - pub num_connected_peers: u32, /// Number of blocks queued for import pub queued_blocks: u32, /// State sync status in progress, if any. diff --git a/substrate/client/network/test/src/lib.rs b/substrate/client/network/test/src/lib.rs index b5aeb162e9fa..f84f353fb4a0 100644 --- a/substrate/client/network/test/src/lib.rs +++ b/substrate/client/network/test/src/lib.rs @@ -266,7 +266,7 @@ where /// Returns the number of peers we're connected to. pub async fn num_peers(&self) -> usize { - self.sync_service.status().await.unwrap().num_connected_peers as usize + self.sync_service.num_connected_peers() } /// Returns the number of downloaded blocks. @@ -1016,7 +1016,7 @@ pub trait TestNetFactory: Default + Sized + Send { for peer in peers { if peer.sync_service.is_major_syncing() || - peer.sync_service.num_queued_blocks().await.unwrap() != 0 + peer.sync_service.status().await.unwrap().queued_blocks != 0 { return false } @@ -1036,7 +1036,7 @@ pub trait TestNetFactory: Default + Sized + Send { async fn is_idle(&mut self) -> bool { let peers = self.peers_mut(); for peer in peers { - if peer.sync_service.num_queued_blocks().await.unwrap() != 0 { + if peer.sync_service.status().await.unwrap().queued_blocks != 0 { return false } if peer.sync_service.num_sync_requests().await.unwrap() != 0 { @@ -1094,9 +1094,7 @@ pub trait TestNetFactory: Default + Sized + Send { 'outer: loop { for sync_service in &sync_services { - if sync_service.status().await.unwrap().num_connected_peers as usize != - num_peers - 1 - { + if sync_service.num_connected_peers() != num_peers - 1 { futures::future::poll_fn::<(), _>(|cx| { self.poll(cx); Poll::Ready(()) diff --git a/substrate/client/network/test/src/sync.rs b/substrate/client/network/test/src/sync.rs index 9edd68c2ed9f..4244c49bf7fb 100644 --- a/substrate/client/network/test/src/sync.rs +++ b/substrate/client/network/test/src/sync.rs @@ -1049,7 +1049,7 @@ async fn syncs_all_forks_from_single_peer() { }) .await; - if net.peer(1).sync_service().best_seen_block().await.unwrap() == Some(12) { + if net.peer(1).sync_service().status().await.unwrap().best_seen_block == Some(12) { break } } diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index ed108a3102bc..5fb304edd7e1 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -341,7 +341,7 @@ pub async fn build_system_rpc_future< sc_rpc::system::Request::SyncState(sender) => { use sc_rpc::system::SyncState; - match sync_service.best_seen_block().await { + match sync_service.status().await.map(|status| status.best_seen_block) { Ok(best_seen_block) => { let best_number = client.info().best_number; let _ = sender.send(SyncState { From b34d4a083f71245794604fd9fa6464e714f958b1 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 26 Aug 2024 21:53:56 +0200 Subject: [PATCH 161/382] [CI] Fix SemVer check base commit (#5361) After seeing some cases of reported changes that did not happen by the merge request proposer (like https://github.com/paritytech/polkadot-sdk/pull/5339), it became clear that [this](https://github.com/orgs/community/discussions/59677) is probably the issue. The base commit of the SemVer check CI is currently using the *latest* master commit, instead of the master commit at the time when the MR was created. Trying to get the correct base commit now. For this to be debugged, i have to wait until another MR is merged into master. --------- Signed-off-by: Oliver Tale-Yazdi --- .github/scripts/generate-prdoc.py | 1 + .github/workflows/check-semver.yml | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/scripts/generate-prdoc.py b/.github/scripts/generate-prdoc.py index b7b2e6f970fa..ba7def20fcb9 100644 --- a/.github/scripts/generate-prdoc.py +++ b/.github/scripts/generate-prdoc.py @@ -96,6 +96,7 @@ def create_prdoc(pr, audience, title, description, patch, bump, force): # write the parsed PR documentation back to the file with open(path, "w") as f: yaml.dump(prdoc, f) + print(f"PrDoc for PR {pr} written to {path}") def parse_args(): parser = argparse.ArgumentParser() diff --git a/.github/workflows/check-semver.yml b/.github/workflows/check-semver.yml index d9d918b44a23..4d521db90a4a 100644 --- a/.github/workflows/check-semver.yml +++ b/.github/workflows/check-semver.yml @@ -12,7 +12,6 @@ concurrency: env: TOOLCHAIN: nightly-2024-06-01 - jobs: check-semver: runs-on: ubuntu-latest @@ -20,14 +19,14 @@ jobs: image: docker.io/paritytech/ci-unified:bullseye-1.77.0-2024-04-10-v20240408 steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + fetch-depth: 2 - name: extra git setup - env: - BASE: ${{ github.event.pull_request.base.sha }} run: | git config --global --add safe.directory '*' - git fetch --no-tags --no-recurse-submodules --depth=1 origin $BASE - git branch old $BASE + + git branch old HEAD^1 - name: Comment If Backport if: ${{ startsWith(github.event.pull_request.base.ref, 'stable') }} From 6ecbde331ead4600536df2fba912a868ebc06625 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Tue, 27 Aug 2024 08:14:01 +0800 Subject: [PATCH 162/382] Only log the propagating transactions when they are not empty (#5424) This can make the log cleaner, especially when you specify `--log sync=debug`. --- prdoc/pr_5424.prdoc | 13 +++++++++++++ substrate/client/network/transactions/src/lib.rs | 8 +++++++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 prdoc/pr_5424.prdoc diff --git a/prdoc/pr_5424.prdoc b/prdoc/pr_5424.prdoc new file mode 100644 index 000000000000..a94bf7aaeba2 --- /dev/null +++ b/prdoc/pr_5424.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Suppress the log output for transaction propagation when no transactions are present + +doc: + - audience: Node Dev + description: | + Previously, the log message `Propagating transactions` would always be printed, even when there were no transactions to propagate. This patch optimizes the logging by returning early when no transactions are present, resulting in cleaner and more relevant log output. + +crates: +- name: sc-network-transactions + bump: none diff --git a/substrate/client/network/transactions/src/lib.rs b/substrate/client/network/transactions/src/lib.rs index 31ad0781035e..a241041968fd 100644 --- a/substrate/client/network/transactions/src/lib.rs +++ b/substrate/client/network/transactions/src/lib.rs @@ -522,8 +522,14 @@ where return } - debug!(target: LOG_TARGET, "Propagating transactions"); let transactions = self.transaction_pool.transactions(); + + if transactions.is_empty() { + return + } + + debug!(target: LOG_TARGET, "Propagating transactions"); + let propagated_to = self.do_propagate_transactions(&transactions); self.transaction_pool.on_broadcasted(propagated_to); } From 5a84374f6bbe37b3c89043923375ab2bca05556d Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Tue, 27 Aug 2024 10:22:35 +0200 Subject: [PATCH 163/382] Make the PR template a default for new PRs (#5462) A follow-up to https://github.com/paritytech/polkadot-sdk/pull/5447 Instead of having an option to select a PR template (like is the case with issues), this change will make the one PR template we have the default, it will show up for every new PR. Also updated one link so it works properly in the PR body. --- .github/PULL_REQUEST_TEMPLATE/pull_request_template.md | 1 - .github/pull_request_template.md | 1 + docs/contributor/PULL_REQUEST_TEMPLATE.md | 5 +++-- 3 files changed, 4 insertions(+), 3 deletions(-) delete mode 120000 .github/PULL_REQUEST_TEMPLATE/pull_request_template.md create mode 120000 .github/pull_request_template.md diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md deleted file mode 120000 index 3dd90d213692..000000000000 --- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +++ /dev/null @@ -1 +0,0 @@ -../../docs/contributor/PULL_REQUEST_TEMPLATE.md \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 120000 index 000000000000..7b6b3498755f --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1 @@ +../docs/contributor/PULL_REQUEST_TEMPLATE.md \ No newline at end of file diff --git a/docs/contributor/PULL_REQUEST_TEMPLATE.md b/docs/contributor/PULL_REQUEST_TEMPLATE.md index 9612ab6d8d3e..99455c985076 100644 --- a/docs/contributor/PULL_REQUEST_TEMPLATE.md +++ b/docs/contributor/PULL_REQUEST_TEMPLATE.md @@ -33,8 +33,9 @@ possibly integration.* # Checklist * [ ] My PR includes a detailed description as outlined in the "Description" and its two subsections above. -* [ ] My PR follows the [labeling requirements](CONTRIBUTING.md#Process) of this project (at minimum one label for `T` - required) +* [ ] My PR follows the [labeling requirements]( +https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CONTRIBUTING.md#Process +) of this project (at minimum one label for `T` required) * External contributors: ask maintainers to put the right label on your PR. * [ ] I have made corresponding changes to the documentation (if applicable) * [ ] I have added tests that prove my fix is effective or that my feature works (if applicable) From f0323d529615de0de121a87eb1c8c6da82bd0ff8 Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Tue, 27 Aug 2024 18:28:52 +0900 Subject: [PATCH 164/382] Remove deprecated calls in cumulus-parachain-system (#5439) Calls were written to be removed after June 2024. This PR removes them. This PR will break users using those calls. The call won't be decodable by the runtime, so it should fail early with no consequences. The functionality must be same as before, users will just need to use the calls in `System`. --- cumulus/pallets/parachain-system/src/lib.rs | 77 +------------------ cumulus/pallets/parachain-system/src/mock.rs | 6 +- cumulus/pallets/parachain-system/src/tests.rs | 6 +- cumulus/pallets/xcmp-queue/src/mock.rs | 2 +- prdoc/pr_5439.prdoc | 16 ++++ 5 files changed, 25 insertions(+), 82 deletions(-) create mode 100644 prdoc/pr_5439.prdoc diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index 9e0a68d09a14..bf136dc0644c 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -53,9 +53,6 @@ use polkadot_runtime_parachains::FeeTracker; use scale_info::TypeInfo; use sp_runtime::{ traits::{Block as BlockT, BlockNumberProvider, Hash}, - transaction_validity::{ - InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction, - }, BoundedSlice, FixedU128, RuntimeDebug, Saturating, }; use xcm::{latest::XcmHash, VersionedLocation, VersionedXcm}; @@ -193,7 +190,7 @@ pub mod ump_constants { pub mod pallet { use super::*; use frame_support::pallet_prelude::*; - use frame_system::{pallet_prelude::*, WeightInfo as SystemWeightInfo}; + use frame_system::pallet_prelude::*; #[pallet::pallet] #[pallet::storage_version(migration::STORAGE_VERSION)] @@ -653,52 +650,8 @@ pub mod pallet { Ok(()) } - /// Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied - /// later. - /// - /// The `check_version` parameter sets a boolean flag for whether or not the runtime's spec - /// version and name should be verified on upgrade. Since the authorization only has a hash, - /// it cannot actually perform the verification. - /// - /// This call requires Root origin. - #[pallet::call_index(2)] - #[pallet::weight(::SystemWeightInfo::authorize_upgrade())] - #[allow(deprecated)] - #[deprecated( - note = "To be removed after June 2024. Migrate to `frame_system::authorize_upgrade`." - )] - pub fn authorize_upgrade( - origin: OriginFor, - code_hash: T::Hash, - check_version: bool, - ) -> DispatchResult { - ensure_root(origin)?; - frame_system::Pallet::::do_authorize_upgrade(code_hash, check_version); - Ok(()) - } - - /// Provide the preimage (runtime binary) `code` for an upgrade that has been authorized. - /// - /// If the authorization required a version check, this call will ensure the spec name - /// remains unchanged and that the spec version has increased. - /// - /// Note that this function will not apply the new `code`, but only attempt to schedule the - /// upgrade with the Relay Chain. - /// - /// All origins are allowed. - #[pallet::call_index(3)] - #[pallet::weight(::SystemWeightInfo::apply_authorized_upgrade())] - #[allow(deprecated)] - #[deprecated( - note = "To be removed after June 2024. Migrate to `frame_system::apply_authorized_upgrade`." - )] - pub fn enact_authorized_upgrade( - _: OriginFor, - code: Vec, - ) -> DispatchResultWithPostInfo { - let post = frame_system::Pallet::::do_apply_authorize_upgrade(code)?; - Ok(post) - } + // WARNING: call indices 2 and 3 were used in a former version of this pallet. Using them + // again will require to bump the transaction version of runtimes using this pallet. } #[pallet::event] @@ -951,30 +904,6 @@ pub mod pallet { sp_io::storage::set(b":c", &[]); } } - - #[pallet::validate_unsigned] - impl sp_runtime::traits::ValidateUnsigned for Pallet { - type Call = Call; - - fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity { - if let Call::enact_authorized_upgrade { ref code } = call { - if let Ok(hash) = frame_system::Pallet::::validate_authorized_upgrade(&code[..]) - { - return Ok(ValidTransaction { - priority: 100, - requires: Vec::new(), - provides: vec![hash.as_ref().to_vec()], - longevity: TransactionLongevity::max_value(), - propagate: true, - }) - } - } - if let Call::set_validation_data { .. } = call { - return Ok(Default::default()) - } - Err(InvalidTransaction::Call.into()) - } - } } impl Pallet { diff --git a/cumulus/pallets/parachain-system/src/mock.rs b/cumulus/pallets/parachain-system/src/mock.rs index 7bea72224b8b..b4d118aadf04 100644 --- a/cumulus/pallets/parachain-system/src/mock.rs +++ b/cumulus/pallets/parachain-system/src/mock.rs @@ -49,9 +49,9 @@ type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( pub enum Test { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - ParachainSystem: parachain_system::{Pallet, Call, Config, Storage, Inherent, Event, ValidateUnsigned}, - MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event}, + System: frame_system, + ParachainSystem: parachain_system, + MessageQueue: pallet_message_queue, } ); diff --git a/cumulus/pallets/parachain-system/src/tests.rs b/cumulus/pallets/parachain-system/src/tests.rs index 51c6e83c1131..548231966e42 100755 --- a/cumulus/pallets/parachain-system/src/tests.rs +++ b/cumulus/pallets/parachain-system/src/tests.rs @@ -1127,10 +1127,8 @@ fn upgrade_version_checks_should_work() { let new_code = vec![1, 2, 3, 4]; let new_code_hash = H256(sp_crypto_hashing::blake2_256(&new_code)); - #[allow(deprecated)] - let _authorize = ParachainSystem::authorize_upgrade(RawOrigin::Root.into(), new_code_hash, true); - #[allow(deprecated)] - let res = ParachainSystem::enact_authorized_upgrade(RawOrigin::None.into(), new_code); + let _authorize = System::authorize_upgrade(RawOrigin::Root.into(), new_code_hash); + let res = System::apply_authorized_upgrade(RawOrigin::None.into(), new_code); assert_eq!(expected.map_err(DispatchErrorWithPostInfo::from), res); }); diff --git a/cumulus/pallets/xcmp-queue/src/mock.rs b/cumulus/pallets/xcmp-queue/src/mock.rs index 7fb96de7a4ea..348939de1f14 100644 --- a/cumulus/pallets/xcmp-queue/src/mock.rs +++ b/cumulus/pallets/xcmp-queue/src/mock.rs @@ -45,7 +45,7 @@ frame_support::construct_runtime!( System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, ParachainSystem: cumulus_pallet_parachain_system::{ - Pallet, Call, Config, Storage, Inherent, Event, ValidateUnsigned, + Pallet, Call, Config, Storage, Inherent, Event, }, XcmpQueue: xcmp_queue::{Pallet, Call, Storage, Event}, } diff --git a/prdoc/pr_5439.prdoc b/prdoc/pr_5439.prdoc new file mode 100644 index 000000000000..00fa48de0e25 --- /dev/null +++ b/prdoc/pr_5439.prdoc @@ -0,0 +1,16 @@ +title: "Deprecated calls removed in cumulus parachain system pallet" + +doc: + - audience: [Runtime Dev, Runtime User] + description: | + Call `authorize_upgrade` in parachain system pallet `cumulus-pallet-parachain-system` has + been removed, use `authorize_upgrade` or `authorize_upgrade_without_checks` calls in system + pallet `frame-system` instead. + Call `enact_authorized_upgrade` in parachain system pallet `cumulus-pallet-parachain-system` + has been removed, use `apply_authorized_upgrade` call in system pallet `frame-system` instead. + +crates: + - name: cumulus-pallet-parachain-system + bump: major + - name: cumulus-pallet-xcmp-queue + bump: none From 33010098e1a50f7788e0ae44119844ad4313c7d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 09:58:22 +0000 Subject: [PATCH 165/382] Bump the ci_dependencies group across 1 directory with 2 updates (#5401) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the ci_dependencies group with 2 updates in the / directory: [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) and [docker/build-push-action](https://github.com/docker/build-push-action). Updates `docker/setup-buildx-action` from 3.5.0 to 3.6.1
Commits
  • 988b5a0 Merge pull request #347 from crazy-max/skip-malformed-context
  • 2c21562 chore: update generated content
  • 3382292 check for malformed docker context
  • 3d68780 Merge pull request #341 from crazy-max/docker-context-tls
  • d069e98 chore: update generated content
  • 8b850f8 create docker context if default one has TLS data loaded
  • See full diff in compare view

Updates `docker/build-push-action` from 6.5.0 to 6.7.0
Release notes

Sourced from docker/build-push-action's releases.

v6.7.0

Full Changelog: https://github.com/docker/build-push-action/compare/v6.6.1...v6.7.0

v6.6.1

Full Changelog: https://github.com/docker/build-push-action/compare/v6.6.0...v6.6.1

v6.6.0

Full Changelog: https://github.com/docker/build-push-action/compare/v6.5.0...v6.6.0

Commits
  • 5cd11c3 Merge pull request #1211 from crazy-max/summary-info-message
  • 0aba704 chore: update generated content
  • 23c657a print info message for build summary support checks
  • 16ebe77 Merge pull request #1205 from docker/dependabot/npm_and_yarn/docker/actions-t...
  • 646a62b chore: update generated content
  • d92ab13 chore(deps): Bump @​docker/actions-toolkit from 0.37.0 to 0.37.1
  • 4f7cdeb Merge pull request #1198 from docker/dependabot/npm_and_yarn/docker/actions-t...
  • ad3cd77 chore: update generated content
  • 3efbc13 chore(deps): Bump @​docker/actions-toolkit from 0.36.0 to 0.37.0
  • 2dbe91d Merge pull request #1197 from crazy-max/build-checks
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release-50_publish-docker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index a749c86faa0c..c5d214ec68ab 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -298,7 +298,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # v3.5.0 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - name: Cache Docker layers uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -322,7 +322,7 @@ jobs: - name: Build and push id: docker_build - uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 # v6.5.0 + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 with: push: true file: docker/dockerfiles/polkadot/polkadot_injected_debian.Dockerfile From 7e7c33453eeb14f47c6c4d0f98cc982e485edc77 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 27 Aug 2024 12:05:15 +0200 Subject: [PATCH 166/382] frame-omni-bencher maintenance (#5466) Changes: - Set default level to `Info` again. Seems like a dependency update set it to something higher. - Fix docs to not use `--locked` since we rely on dependency bumps via cargo. - Add README with rust docs. - Fix bug where the node ignored `--heap-pages` argument. You can test the `--heap-pages` bug by running this command on master and then on this branch. Note that it should fail because of the very low heap pages arg: `cargo run --release --bin polkadot --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=10 --repeat=30 --wasm-execution=compiled --heap-pages=8 --pallet=frame-system --extrinsic="*"` --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: ggwpez --- Cargo.lock | 2 +- prdoc/pr_5466.prdoc | 14 +++++ .../benchmarking-cli/src/pallet/command.rs | 8 +-- substrate/utils/frame/omni-bencher/Cargo.toml | 3 +- substrate/utils/frame/omni-bencher/README.md | 60 +++++++++++++++++++ .../utils/frame/omni-bencher/src/command.rs | 14 +++-- .../utils/frame/omni-bencher/src/main.rs | 14 ++++- 7 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 prdoc/pr_5466.prdoc create mode 100644 substrate/utils/frame/omni-bencher/README.md diff --git a/Cargo.lock b/Cargo.lock index 89cc7cfa8d49..35e46da1b473 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5991,7 +5991,7 @@ dependencies = [ "sc-cli", "sp-runtime", "sp-statement-store", - "sp-tracing 16.0.0", + "tracing-subscriber 0.3.18", ] [[package]] diff --git a/prdoc/pr_5466.prdoc b/prdoc/pr_5466.prdoc new file mode 100644 index 000000000000..57f20b3585b4 --- /dev/null +++ b/prdoc/pr_5466.prdoc @@ -0,0 +1,14 @@ +crates: +- bump: patch + name: frame-omni-bencher +- bump: patch + name: frame-benchmarking-cli +doc: +- audience: Runtime Dev + description: | + Changes: + - Set default level to `Info` again. Seems like a dependency update set it to something higher. + - Fix docs to not use `--locked` since we rely on dependency bumps via cargo. + - Add README with rust docs. + - Fix bug where the node ignored `--heap-pages` argument. +title: frame-omni-bencher maintenance diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs index cfbbab4df7ca..471919815206 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -243,7 +243,7 @@ impl PalletCmd { let state = &state_without_tracking; let runtime = self.runtime_blob(&state_without_tracking)?; let runtime_code = runtime.code()?; - let alloc_strategy = Self::alloc_strategy(runtime_code.heap_pages); + let alloc_strategy = self.alloc_strategy(runtime_code.heap_pages); let executor = WasmExecutor::<( sp_io::SubstrateHostFunctions, @@ -753,9 +753,9 @@ impl PalletCmd { } /// Allocation strategy for pallet benchmarking. - fn alloc_strategy(heap_pages: Option) -> HeapAllocStrategy { - heap_pages.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |p| HeapAllocStrategy::Static { - extra_pages: p as _, + fn alloc_strategy(&self, runtime_heap_pages: Option) -> HeapAllocStrategy { + self.heap_pages.or(runtime_heap_pages).map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |p| { + HeapAllocStrategy::Static { extra_pages: p as _ } }) } diff --git a/substrate/utils/frame/omni-bencher/Cargo.toml b/substrate/utils/frame/omni-bencher/Cargo.toml index 89ef2a48e01d..e2ffca8b4714 100644 --- a/substrate/utils/frame/omni-bencher/Cargo.toml +++ b/substrate/utils/frame/omni-bencher/Cargo.toml @@ -6,6 +6,7 @@ authors.workspace = true edition.workspace = true repository.workspace = true license.workspace = true +readme = "README.md" [lints] workspace = true @@ -17,5 +18,5 @@ frame-benchmarking-cli = { workspace = true } sc-cli = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } sp-statement-store = { workspace = true, default-features = true } -sp-tracing = { workspace = true } +tracing-subscriber = { workspace = true } log = { workspace = true } diff --git a/substrate/utils/frame/omni-bencher/README.md b/substrate/utils/frame/omni-bencher/README.md new file mode 100644 index 000000000000..29bfaeb6450b --- /dev/null +++ b/substrate/utils/frame/omni-bencher/README.md @@ -0,0 +1,60 @@ +# Polkadot Omni Benchmarking CLI + +The Polkadot Omni benchmarker allows to benchmark the extrinsics of any Polkadot runtime. It is +meant to replace the current manual integration of the `benchmark pallet` into every parachain node. +This reduces duplicate code and makes maintenance for builders easier. The CLI is currently only +able to benchmark extrinsics. In the future it is planned to extend this to some other areas. + +General FRAME runtimes could also be used with this benchmarker, as long as they don't utilize any +host functions that are not part of the Polkadot host specification. + +## Installation + +Directly via crates.io: + +```sh +cargo install frame-omni-bencher --profile=production +``` + +from GitHub: + +```sh +cargo install --git https://github.com/paritytech/polkadot-sdk frame-omni-bencher --profile=production +``` + +or locally from the sources: + +```sh +cargo install --path substrate/utils/frame/omni-bencher --profile=production +``` + +Check the installed version and print the docs: + +```sh +frame-omni-bencher --help +``` + +## Usage + +First we need to ensure that there is a runtime available. As example we will build the Westend +runtime: + +```sh +cargo build -p westend-runtime --profile production --features runtime-benchmarks +``` + +Now as an example, we benchmark the `balances` pallet: + +```sh +frame-omni-bencher v1 benchmark pallet \ +--runtime target/release/wbuild/westend-runtime/westend-runtime.compact.compressed.wasm \ +--pallet "pallet_balances" --extrinsic "" +``` + +The `--steps`, `--repeat`, `--heap-pages` and `--wasm-execution` arguments have sane defaults and do +not need be passed explicitly anymore. + +## Backwards Compatibility + +The exposed pallet sub-command is identical as the node-integrated CLI. The only difference is that +it needs to be prefixed with a `v1` to ensure drop-in compatibility. diff --git a/substrate/utils/frame/omni-bencher/src/command.rs b/substrate/utils/frame/omni-bencher/src/command.rs index f0159f4307d6..19177ed549b7 100644 --- a/substrate/utils/frame/omni-bencher/src/command.rs +++ b/substrate/utils/frame/omni-bencher/src/command.rs @@ -36,13 +36,19 @@ use sp_runtime::traits::BlakeTwo256; /// Directly via crates.io: /// /// ```sh -/// cargo install --locked frame-omni-bencher +/// cargo install frame-omni-bencher --profile=production /// ``` /// -/// or when the sources are locally checked out: +/// from GitHub: /// /// ```sh -/// cargo install --locked --path substrate/utils/frame/omni-bencher --profile=production +/// cargo install --git https://github.com/paritytech/polkadot-sdk frame-omni-bencher --profile=production +/// ``` +/// +/// or locally from the sources: +/// +/// ```sh +/// cargo install --path substrate/utils/frame/omni-bencher --profile=production /// ``` /// /// Check the installed version and print the docs: @@ -60,7 +66,7 @@ use sp_runtime::traits::BlakeTwo256; /// cargo build -p westend-runtime --profile production --features runtime-benchmarks /// ``` /// -/// Now as example we benchmark `pallet_balances`: +/// Now as an example, we benchmark the `balances` pallet: /// /// ```sh /// frame-omni-bencher v1 benchmark pallet \ diff --git a/substrate/utils/frame/omni-bencher/src/main.rs b/substrate/utils/frame/omni-bencher/src/main.rs index a8893b5a79af..ef3450add8e4 100644 --- a/substrate/utils/frame/omni-bencher/src/main.rs +++ b/substrate/utils/frame/omni-bencher/src/main.rs @@ -19,10 +19,22 @@ mod command; use clap::Parser; use sc_cli::Result; +use tracing_subscriber::EnvFilter; fn main() -> Result<()> { - sp_tracing::try_init_simple(); + setup_logger(); + log::warn!("The FRAME omni-bencher is not yet battle tested - double check the results.",); command::Command::parse().run() } + +/// Setup logging with `info` as default level. Can be set via `RUST_LOG` env. +fn setup_logger() { + let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); + + tracing_subscriber::fmt() + .with_env_filter(env_filter) + .with_writer(std::io::stderr) + .init(); +} From 0d7becf23b95f66d38676a31d2493c569a98278d Mon Sep 17 00:00:00 2001 From: Parth Date: Tue, 27 Aug 2024 19:08:36 +0400 Subject: [PATCH 167/382] Make `PendingConfigs` storage item public (#5467) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR changes `PendingConfigs` storage item's visibility from crate to public. This enable us to read it in our runtime. --------- Co-authored-by: Bastian Köcher --- polkadot/runtime/parachains/src/configuration.rs | 2 +- prdoc/pr_5467.prdoc | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 prdoc/pr_5467.prdoc diff --git a/polkadot/runtime/parachains/src/configuration.rs b/polkadot/runtime/parachains/src/configuration.rs index d09962ef2b44..19df1f8c3607 100644 --- a/polkadot/runtime/parachains/src/configuration.rs +++ b/polkadot/runtime/parachains/src/configuration.rs @@ -557,7 +557,7 @@ pub mod pallet { /// The list is sorted ascending by session index. Also, this list can only contain at most /// 2 items: for the next session and for the `scheduled_session`. #[pallet::storage] - pub(crate) type PendingConfigs = + pub type PendingConfigs = StorageValue<_, Vec<(SessionIndex, HostConfiguration>)>, ValueQuery>; /// If this is set, then the configuration setters will bypass the consistency checks. This diff --git a/prdoc/pr_5467.prdoc b/prdoc/pr_5467.prdoc new file mode 100644 index 000000000000..2634c255e168 --- /dev/null +++ b/prdoc/pr_5467.prdoc @@ -0,0 +1,10 @@ +title: Make PendingConfigs storage item public + +doc: + - audience: Runtime Dev + description: | + Make PendingConfigs storage item in polkadot's configuration crate public. + +crates: + - name: polkadot-runtime-parachains + bump: minor \ No newline at end of file From 7a2c5375fa4b950f9518a47c87d92bd611b1bfdc Mon Sep 17 00:00:00 2001 From: Frazz <59382025+Sudo-Whodo@users.noreply.github.com> Date: Tue, 27 Aug 2024 09:23:17 -0700 Subject: [PATCH 168/382] Adding stkd bootnodes (#5470) Opening this PR to add our bootnodes for the IBP. These nodes are located in Santiago Chile, we own and manage the underlying hardware. If you need any more information please let me know. Commands to test: ``` ./polkadot --tmp --name "testing-bootnode" --chain kusama --reserved-only --reserved-nodes "/dns/kusama.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWJHhnF64TXSmyxNkhPkXAHtYNRy86LuvGQu1LTi5vrJCL" --no-hardware-benchmarks ./polkadot --tmp --name "testing-bootnode" --chain paseo --reserved-only --reserved-nodes "/dns/paseo.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWMdND5nwfCs5M2rfp5kyRo41BGDgD8V67rVRaB3acgZ53" --no-hardware-benchmarks ./polkadot --tmp --name "testing-bootnode" --chain polkadot --reserved-only --reserved-nodes "/dns/polkadot.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWEymrFRHz6c17YP3FAyd8kXS5gMRLgkW4U77ZJD2ZNCLZ" --no-hardware-benchmarks ./polkadot --tmp --name "testing-bootnode" --chain westend --reserved-only --reserved-nodes "/dns/westend.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWHaQKkJiTPqeNgqDcW7dfYgJxYwT8YqJMtTkueSu6378V" --no-hardware-benchmarks ``` --- polkadot/node/service/chain-specs/kusama.json | 3 ++- polkadot/node/service/chain-specs/paseo.json | 3 ++- polkadot/node/service/chain-specs/polkadot.json | 3 ++- polkadot/node/service/chain-specs/westend.json | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/polkadot/node/service/chain-specs/kusama.json b/polkadot/node/service/chain-specs/kusama.json index 5d2413ac1e02..ff38192906da 100644 --- a/polkadot/node/service/chain-specs/kusama.json +++ b/polkadot/node/service/chain-specs/kusama.json @@ -37,7 +37,8 @@ "/dns/ibp-boot-kusama.luckyfriday.io/tcp/30333/p2p/12D3KooW9vu1GWHBuxyhm7rZgD3fhGZpNajPXFexadvhujWMgwfT", "/dns/boot-kusama.luckyfriday.io/tcp/443/wss/p2p/12D3KooWS1Lu6DmK8YHSvkErpxpcXmk14vG6y4KVEFEkd9g62PP8", "/dns/ibp-boot-kusama.luckyfriday.io/tcp/30334/wss/p2p/12D3KooW9vu1GWHBuxyhm7rZgD3fhGZpNajPXFexadvhujWMgwfT", - "/dns4/kusama-0.boot.onfinality.io/tcp/27682/ws/p2p/12D3KooWFrwFo7ry3dEuFwhehGSSN96a5Xdzxot7SWfXeSbhELAe" + "/dns4/kusama-0.boot.onfinality.io/tcp/27682/ws/p2p/12D3KooWFrwFo7ry3dEuFwhehGSSN96a5Xdzxot7SWfXeSbhELAe", + "/dns/kusama.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWJHhnF64TXSmyxNkhPkXAHtYNRy86LuvGQu1LTi5vrJCL" ], "telemetryEndpoints": [ [ diff --git a/polkadot/node/service/chain-specs/paseo.json b/polkadot/node/service/chain-specs/paseo.json index 35ac9aa6aa93..aacfdb025786 100644 --- a/polkadot/node/service/chain-specs/paseo.json +++ b/polkadot/node/service/chain-specs/paseo.json @@ -20,7 +20,8 @@ "/dns/pso16.rotko.net/tcp/33246/p2p/12D3KooWRH8eBMhw8c7bucy6pJfy94q4dKpLkF3pmeGohHmemdRu", "/dns/pso16.rotko.net/tcp/35246/wss/p2p/12D3KooWRH8eBMhw8c7bucy6pJfy94q4dKpLkF3pmeGohHmemdRu", "/dns/paseo-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWBLLFKDGBxCwq3QmU3YwWKXUx953WwprRshJQicYu4Cfr", - "/dns/paseo-boot-ng.dwellir.com/tcp/30354/p2p/12D3KooWBLLFKDGBxCwq3QmU3YwWKXUx953WwprRshJQicYu4Cfr" + "/dns/paseo-boot-ng.dwellir.com/tcp/30354/p2p/12D3KooWBLLFKDGBxCwq3QmU3YwWKXUx953WwprRshJQicYu4Cfr", + "/dns/paseo.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWMdND5nwfCs5M2rfp5kyRo41BGDgD8V67rVRaB3acgZ53" ], "telemetryEndpoints": null, "protocolId": "pas", diff --git a/polkadot/node/service/chain-specs/polkadot.json b/polkadot/node/service/chain-specs/polkadot.json index 553ff1bb3e41..7e1e90f6a8c1 100644 --- a/polkadot/node/service/chain-specs/polkadot.json +++ b/polkadot/node/service/chain-specs/polkadot.json @@ -38,7 +38,8 @@ "/dns/ibp-boot-polkadot.luckyfriday.io/tcp/30333/p2p/12D3KooWEjk6QXrZJ26fLpaajisJGHiz6WiQsR8k7mkM9GmWKnRZ", "/dns/ibp-boot-polkadot.luckyfriday.io/tcp/30334/wss/p2p/12D3KooWEjk6QXrZJ26fLpaajisJGHiz6WiQsR8k7mkM9GmWKnRZ", "/dns/boot-polkadot.luckyfriday.io/tcp/443/wss/p2p/12D3KooWAdyiVAaeGdtBt6vn5zVetwA4z4qfm9Fi2QCSykN1wTBJ", - "/dns4/polkadot-0.boot.onfinality.io/tcp/24446/ws/p2p/12D3KooWT1PWaNdAwYrSr89dvStnoGdH3t4LNRbcVNN4JCtsotkR" + "/dns4/polkadot-0.boot.onfinality.io/tcp/24446/ws/p2p/12D3KooWT1PWaNdAwYrSr89dvStnoGdH3t4LNRbcVNN4JCtsotkR", + "/dns/polkadot.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWEymrFRHz6c17YP3FAyd8kXS5gMRLgkW4U77ZJD2ZNCLZ" ], "telemetryEndpoints": [ [ diff --git a/polkadot/node/service/chain-specs/westend.json b/polkadot/node/service/chain-specs/westend.json index 9059d525689d..08c5bd33b4bd 100644 --- a/polkadot/node/service/chain-specs/westend.json +++ b/polkadot/node/service/chain-specs/westend.json @@ -33,7 +33,8 @@ "/dns/wnd14.rotko.net/tcp/35234/wss/p2p/12D3KooWLK8Zj1uZ46phU3vQwiDVda8tB76S8J26rXZQLHpwWkDJ", "/dns/wnd14.rotko.net/tcp/33234/p2p/12D3KooWLK8Zj1uZ46phU3vQwiDVda8tB76S8J26rXZQLHpwWkDJ", "/dns/ibp-boot-westend.luckyfriday.io/tcp/30333/p2p/12D3KooWDg1YEytdwFFNWroFj6gio4YFsMB3miSbHKgdpJteUMB9", - "/dns/ibp-boot-westend.luckyfriday.io/tcp/30334/wss/p2p/12D3KooWDg1YEytdwFFNWroFj6gio4YFsMB3miSbHKgdpJteUMB9" + "/dns/ibp-boot-westend.luckyfriday.io/tcp/30334/wss/p2p/12D3KooWDg1YEytdwFFNWroFj6gio4YFsMB3miSbHKgdpJteUMB9", + "/dns/westend.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWHaQKkJiTPqeNgqDcW7dfYgJxYwT8YqJMtTkueSu6378V" ], "telemetryEndpoints": [ [ From f90bfa6ada211ef1bb55861ffbb7588b15ec21df Mon Sep 17 00:00:00 2001 From: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com> Date: Tue, 27 Aug 2024 20:22:51 +0200 Subject: [PATCH 169/382] Add feature to allow Aura collator to use full PoV size (#5393) This PR introduces a feature that allows to optionally enable using the full PoV size. Technically, we're ready to enable it by default, but as corresponding runtime changes have not been propagated to the system parachain runtimes yet, doing so could put them at risk. On the other hand, there are teams that could benefit from it right now, and it makes no sense for them to wait for the fellowship release and everything. --------- Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> --- cumulus/client/consensus/aura/Cargo.toml | 4 ++++ .../client/consensus/aura/src/collators/basic.rs | 16 +++++++++++----- .../consensus/aura/src/collators/lookahead.rs | 16 +++++++++++----- .../collators/slot_based/block_builder_task.rs | 16 +++++++++++----- prdoc/pr_5393.prdoc | 15 +++++++++++++++ 5 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 prdoc/pr_5393.prdoc diff --git a/cumulus/client/consensus/aura/Cargo.toml b/cumulus/client/consensus/aura/Cargo.toml index 01e07cb395a9..47e2d8572c3f 100644 --- a/cumulus/client/consensus/aura/Cargo.toml +++ b/cumulus/client/consensus/aura/Cargo.toml @@ -54,3 +54,7 @@ polkadot-primitives = { workspace = true, default-features = true } polkadot-node-primitives = { workspace = true, default-features = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-overseer = { workspace = true, default-features = true } + +[features] +# Allows collator to use full PoV size for block building +full-pov-size = [] diff --git a/cumulus/client/consensus/aura/src/collators/basic.rs b/cumulus/client/consensus/aura/src/collators/basic.rs index 0f9583cd0eb0..d843483b79fa 100644 --- a/cumulus/client/consensus/aura/src/collators/basic.rs +++ b/cumulus/client/consensus/aura/src/collators/basic.rs @@ -237,6 +237,16 @@ where .await ); + let allowed_pov_size = if cfg!(feature = "full-pov-size") { + validation_data.max_pov_size + } else { + // Set the block limit to 50% of the maximum PoV size. + // + // TODO: If we got benchmarking that includes the proof size, + // we should be able to use the maximum pov size. + validation_data.max_pov_size / 2 + } as usize; + let maybe_collation = try_request!( collator .collate( @@ -245,11 +255,7 @@ where None, (parachain_inherent_data, other_inherent_data), params.authoring_duration, - // Set the block limit to 50% of the maximum PoV size. - // - // TODO: If we got benchmarking that includes the proof size, - // we should be able to use the maximum pov size. - (validation_data.max_pov_size / 2) as usize, + allowed_pov_size, ) .await ); diff --git a/cumulus/client/consensus/aura/src/collators/lookahead.rs b/cumulus/client/consensus/aura/src/collators/lookahead.rs index 02d60538a732..0be1e0a23ca5 100644 --- a/cumulus/client/consensus/aura/src/collators/lookahead.rs +++ b/cumulus/client/consensus/aura/src/collators/lookahead.rs @@ -412,6 +412,16 @@ where ) .await; + let allowed_pov_size = if cfg!(feature = "full-pov-size") { + validation_data.max_pov_size + } else { + // Set the block limit to 50% of the maximum PoV size. + // + // TODO: If we got benchmarking that includes the proof size, + // we should be able to use the maximum pov size. + validation_data.max_pov_size / 2 + } as usize; + match collator .collate( &parent_header, @@ -419,11 +429,7 @@ where None, (parachain_inherent_data, other_inherent_data), params.authoring_duration, - // Set the block limit to 50% of the maximum PoV size. - // - // TODO: If we got benchmarking that includes the proof size, - // we should be able to use the maximum pov size. - (validation_data.max_pov_size / 2) as usize, + allowed_pov_size, ) .await { diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index 1fbc0689da86..b70cfe3841b7 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -350,6 +350,16 @@ where ) .await; + let allowed_pov_size = if cfg!(feature = "full-pov-size") { + validation_data.max_pov_size + } else { + // Set the block limit to 50% of the maximum PoV size. + // + // TODO: If we got benchmarking that includes the proof size, + // we should be able to use the maximum pov size. + validation_data.max_pov_size / 2 + } as usize; + let Ok(Some(candidate)) = collator .build_block_and_import( &parent_header, @@ -357,11 +367,7 @@ where None, (parachain_inherent_data, other_inherent_data), authoring_duration, - // Set the block limit to 50% of the maximum PoV size. - // - // TODO: If we got benchmarking that includes the proof size, - // we should be able to use the maximum pov size. - (validation_data.max_pov_size / 2) as usize, + allowed_pov_size, ) .await else { diff --git a/prdoc/pr_5393.prdoc b/prdoc/pr_5393.prdoc new file mode 100644 index 000000000000..7fcf3067fabc --- /dev/null +++ b/prdoc/pr_5393.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Allow to enable full PoV size + +doc: + - audience: Node Dev + description: | + A feature is introduced allowing a collator to enable full PoV size at compile time. + WARNING: To use this feature, security considerations must be understood and the latest + SDK version must be used. + +crates: + - name: cumulus-client-consensus-aura + bump: minor From 38fce08896ee595cb8572923bd0d9f40570e7cb6 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 28 Aug 2024 10:21:18 +0300 Subject: [PATCH 170/382] polkadot-parachain-bin: Remove contracts parachain (#5471) Related to: https://github.com/paritytech/polkadot-sdk/issues/5210 Follow-up for https://github.com/paritytech/polkadot-sdk/pull/5288, as per this comment: https://github.com/paritytech/polkadot-sdk/pull/5288#discussion_r1711157476 I hope I understood this correctly. --- .../chain-specs/contracts-rococo.json | 1 - .../src/chain_spec/contracts.rs | 239 ------------------ .../polkadot-parachain/src/chain_spec/mod.rs | 16 -- 3 files changed, 256 deletions(-) delete mode 120000 cumulus/polkadot-parachain/chain-specs/contracts-rococo.json delete mode 100644 cumulus/polkadot-parachain/src/chain_spec/contracts.rs diff --git a/cumulus/polkadot-parachain/chain-specs/contracts-rococo.json b/cumulus/polkadot-parachain/chain-specs/contracts-rococo.json deleted file mode 120000 index b9f8e8f31e84..000000000000 --- a/cumulus/polkadot-parachain/chain-specs/contracts-rococo.json +++ /dev/null @@ -1 +0,0 @@ -../../parachains/chain-specs/contracts-rococo.json \ No newline at end of file diff --git a/cumulus/polkadot-parachain/src/chain_spec/contracts.rs b/cumulus/polkadot-parachain/src/chain_spec/contracts.rs deleted file mode 100644 index eb10a43ffbea..000000000000 --- a/cumulus/polkadot-parachain/src/chain_spec/contracts.rs +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; -use cumulus_primitives_core::ParaId; -use hex_literal::hex; -use parachains_common::{AccountId, AuraId}; -use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; -use sc_service::ChainType; -use sp_core::{crypto::UncheckedInto, sr25519}; - -/// No relay chain suffix because the id is the same over all relay chains. -const CONTRACTS_PARACHAIN_ID: u32 = 1002; - -/// The existential deposit is determined by the runtime "contracts-rococo". -const CONTRACTS_ROCOCO_ED: contracts_rococo_runtime::Balance = - testnet_parachains_constants::rococo::currency::EXISTENTIAL_DEPOSIT; - -pub fn contracts_rococo_development_config() -> GenericChainSpec { - let mut properties = sc_chain_spec::Properties::new(); - properties.insert("tokenSymbol".into(), "ROC".into()); - properties.insert("tokenDecimals".into(), 12.into()); - - GenericChainSpec::builder( - contracts_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), - Extensions { - relay_chain: "rococo-local".into(), // You MUST set this to the correct network! - para_id: CONTRACTS_PARACHAIN_ID, - }, - ) - .with_name("Contracts on Rococo Development") - .with_id("contracts-rococo-dev") - .with_chain_type(ChainType::Development) - .with_genesis_config_patch(contracts_rococo_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - CONTRACTS_PARACHAIN_ID.into(), - )) - .with_boot_nodes(Vec::new()) - .build() -} - -pub fn contracts_rococo_local_config() -> GenericChainSpec { - let mut properties = sc_chain_spec::Properties::new(); - properties.insert("tokenSymbol".into(), "ROC".into()); - properties.insert("tokenDecimals".into(), 12.into()); - - GenericChainSpec::builder( - contracts_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), - Extensions { - relay_chain: "rococo-local".into(), // You MUST set this to the correct network! - para_id: CONTRACTS_PARACHAIN_ID, - }, - ) - .with_name("Contracts on Rococo") - .with_id("contracts-rococo-local") - .with_chain_type(ChainType::Local) - .with_genesis_config_patch(contracts_rococo_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - CONTRACTS_PARACHAIN_ID.into(), - )) - .with_properties(properties) - .build() -} - -pub fn contracts_rococo_config() -> GenericChainSpec { - // Give your base currency a unit name and decimal places - let mut properties = sc_chain_spec::Properties::new(); - properties.insert("tokenSymbol".into(), "ROC".into()); - properties.insert("tokenDecimals".into(), 12.into()); - - GenericChainSpec::builder( - contracts_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), - Extensions { relay_chain: "rococo".into(), para_id: CONTRACTS_PARACHAIN_ID }, - ) - .with_name("Contracts on Rococo") - .with_id("contracts-rococo") - .with_chain_type(ChainType::Live) - .with_genesis_config_patch(contracts_rococo_genesis( - vec![ - // 5GKFbTTgrVS4Vz1UWWHPqMZQNFWZtqo7H2KpCDyYhEL3aS26 - ( - hex!["bc09354c12c054c8f6b3da208485eacec4ac648bad348895273b37bab5a0937c"] - .into(), - hex!["bc09354c12c054c8f6b3da208485eacec4ac648bad348895273b37bab5a0937c"] - .unchecked_into(), - ), - // 5EPRJHm2GpABVWcwnAujcrhnrjFZyDGd5TwKFzkBoGgdRyv2 - ( - hex!["66be63b7bcbfb91040e5248e2d1ceb822cf219c57848c5924ffa3a1f8e67ba72"] - .into(), - hex!["66be63b7bcbfb91040e5248e2d1ceb822cf219c57848c5924ffa3a1f8e67ba72"] - .unchecked_into(), - ), - // 5GH62vrJrVZxLREcHzm2PR5uTLAT5RQMJitoztCGyaP4o3uM - ( - hex!["ba62886472a0a9f66b5e39f1469ce1c5b3d8cad6be39078daf16f111e89d1e44"] - .into(), - hex!["ba62886472a0a9f66b5e39f1469ce1c5b3d8cad6be39078daf16f111e89d1e44"] - .unchecked_into(), - ), - // 5FHfoJDLdjRYX5KXLRqMDYBbWrwHLMtti21uK4QByUoUAbJF - ( - hex!["8e97f65cda001976311df9bed39e8d0c956089093e94a75ef76fe9347a0eda7b"] - .into(), - hex!["8e97f65cda001976311df9bed39e8d0c956089093e94a75ef76fe9347a0eda7b"] - .unchecked_into(), - ), - ], - // Warning: The configuration for a production chain should not contain - // any endowed accounts here, otherwise it'll be minting extra native tokens - // from the relay chain on the parachain. - vec![ - // NOTE: Remove endowed accounts if deployed on other relay chains. - // Endowed accounts - hex!["baa78c7154c7f82d6d377177e20bcab65d327eca0086513f9964f5a0f6bdad56"].into(), - // AccountId of an account which `ink-waterfall` uses for automated testing - hex!["0e47e2344d523c3cc5c34394b0d58b9a4200e813a038e6c5a6163cc07d70b069"].into(), - ], - CONTRACTS_PARACHAIN_ID.into(), - )) - .with_boot_nodes(vec![ - "/dns/contracts-collator-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWKg3Rpxcr9oJ8n6khoxpGKWztCZydtUZk2cojHqnfLrpj" - .parse() - .expect("MultiaddrWithPeerId"), - "/dns/contracts-collator-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWPEXYrz8tHU3nDtPoPw4V7ou5dzMEWSTuUj7vaWiYVAVh" - .parse() - .expect("MultiaddrWithPeerId"), - "/dns/contracts-collator-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWEVU8AFNary4nP4qEnEcwJaRuy59Wefekzdu9pKbnVEhk" - .parse() - .expect("MultiaddrWithPeerId"), - "/dns/contracts-collator-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWP6pV3ZmcXzGDjv8ZMgA6nZxfAKDxSz4VNiLx6vVCQgJX" - .parse() - .expect("MultiaddrWithPeerId"), - ]) - .with_properties(properties) - .build() -} - -fn contracts_rococo_genesis( - invulnerables: Vec<(AccountId, AuraId)>, - endowed_accounts: Vec, - id: ParaId, -) -> serde_json::Value { - serde_json::json!( { - "balances": { - "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), - }, - "parachainInfo": { - "parachainId": id, - }, - "collatorSelection": { - "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), - "candidacyBond": CONTRACTS_ROCOCO_ED * 16, - }, - "session": { - "keys": invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - contracts_rococo_runtime::SessionKeys { aura }, // session keys - ) - }) - .collect::>(), - }, - // no need to pass anything to aura, in fact it will panic if we do. Session will take care - // of this. - "polkadotXcm": { - "safeXcmVersion": Some(SAFE_XCM_VERSION), - }, - "sudo": { - "key": Some(sp_runtime::AccountId32::from(hex![ - "2681a28014e7d3a5bfb32a003b3571f53c408acbc28d351d6bf58f5028c4ef14" - ])), - }, - }) -} diff --git a/cumulus/polkadot-parachain/src/chain_spec/mod.rs b/cumulus/polkadot-parachain/src/chain_spec/mod.rs index de9c6a889ed0..cd704b5bd938 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/mod.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/mod.rs @@ -27,7 +27,6 @@ use sp_runtime::traits::{IdentifyAccount, Verify}; pub mod asset_hubs; pub mod bridge_hubs; pub mod collectives; -pub mod contracts; pub mod coretime; pub mod glutton; pub mod penpal; @@ -148,14 +147,6 @@ impl LoadSpec for ChainSpecLoader { &include_bytes!("../../chain-specs/collectives-westend.json")[..], )?), - // -- Contracts on Rococo - "contracts-rococo-dev" => Box::new(contracts::contracts_rococo_development_config()), - "contracts-rococo-local" => Box::new(contracts::contracts_rococo_local_config()), - "contracts-rococo-genesis" => Box::new(contracts::contracts_rococo_config()), - "contracts-rococo" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../../chain-specs/contracts-rococo.json")[..], - )?), - // -- BridgeHub bridge_like_id if bridge_like_id.starts_with(bridge_hubs::BridgeHubRuntimeType::ID_PREFIX) => @@ -238,7 +229,6 @@ enum LegacyRuntime { AssetHubPolkadot, AssetHub, Penpal, - ContractsRococo, Collectives, Glutton, BridgeHub(bridge_hubs::BridgeHubRuntimeType), @@ -266,8 +256,6 @@ impl LegacyRuntime { LegacyRuntime::AssetHub } else if id.starts_with("penpal") { LegacyRuntime::Penpal - } else if id.starts_with("contracts-rococo") { - LegacyRuntime::ContractsRococo } else if id.starts_with("collectives-polkadot") || id.starts_with("collectives-westend") { LegacyRuntime::Collectives } else if id.starts_with(bridge_hubs::BridgeHubRuntimeType::ID_PREFIX) { @@ -307,7 +295,6 @@ impl RuntimeResolverT for RuntimeResolver { LegacyRuntime::Collectives | LegacyRuntime::Coretime(_) | LegacyRuntime::People(_) | - LegacyRuntime::ContractsRococo | LegacyRuntime::Glutton | LegacyRuntime::Penpal | LegacyRuntime::Omni => Runtime::Omni(Consensus::Aura(AuraConsensusId::Sr25519)), @@ -383,9 +370,6 @@ mod tests { create_default_with_extensions("penpal-rococo-1000", Extensions2::default()); assert_eq!(LegacyRuntime::Penpal, LegacyRuntime::from_id(chain_spec.id())); - let chain_spec = crate::chain_spec::contracts::contracts_rococo_local_config(); - assert_eq!(LegacyRuntime::ContractsRococo, LegacyRuntime::from_id(chain_spec.id())); - let chain_spec = crate::chain_spec::rococo_parachain::rococo_parachain_local_config(); assert_eq!(LegacyRuntime::Omni, LegacyRuntime::from_id(chain_spec.id())); } From f4be48cf678e1aadabaf11e1642d757cf6c8260b Mon Sep 17 00:00:00 2001 From: Arsham Teymouri <75425316+ArshamTeymouri@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:27:32 +0200 Subject: [PATCH 171/382] change try-runtime rpc domains (#5443) as part of paritytech/devops/3502, try-runtime nodes were migrated to a new provider with a new domain address. The PR fixes all the references to try-runtime nodes on rococo, westend, kusama and polkadot networks. --------- Co-authored-by: ArshamTeymouri Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Niklas Adolfsson --- .config/lychee.toml | 2 +- .github/workflows/check-runtime-migration.yml | 4 ++-- .gitlab/pipeline/check.yml | 4 ++-- prdoc/pr_5443.prdoc | 10 ++++++++++ substrate/utils/frame/remote-externalities/src/lib.rs | 2 +- 5 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 prdoc/pr_5443.prdoc diff --git a/.config/lychee.toml b/.config/lychee.toml index 1de9fcd559dd..b7bb6f0ce495 100644 --- a/.config/lychee.toml +++ b/.config/lychee.toml @@ -32,7 +32,6 @@ exclude = [ "https://github.com/paritytech/polkadot-sdk/substrate/frame/timestamp", "https://github.com/paritytech/substrate/frame/fast-unstake", "https://github.com/zkcrypto/bls12_381/blob/e224ad4ea1babfc582ccd751c2bf128611d10936/src/test-data/mod.rs", - "https://polkadot-try-runtime-node.parity-chains.parity.io/", "https://polkadot.network/the-path-of-a-parachain-block/", "https://research.web3.foundation/en/latest/polkadot/NPoS/3.%20Balancing.html", "https://research.web3.foundation/en/latest/polkadot/Token%20Economics.html#inflation-model", @@ -41,6 +40,7 @@ exclude = [ "https://research.web3.foundation/en/latest/polkadot/overview/2-token-economics.html#inflation-model", "https://research.web3.foundation/en/latest/polkadot/slashing/npos.html", "https://rpc.polkadot.io/", + "https://try-runtime.polkadot.io/", "https://w3f.github.io/parachain-implementers-guide/node/approval/approval-distribution.html", "https://w3f.github.io/parachain-implementers-guide/node/index.html", "https://w3f.github.io/parachain-implementers-guide/protocol-chain-selection.html", diff --git a/.github/workflows/check-runtime-migration.yml b/.github/workflows/check-runtime-migration.yml index 9b7a6fafcd15..b8962f0e07ad 100644 --- a/.github/workflows/check-runtime-migration.yml +++ b/.github/workflows/check-runtime-migration.yml @@ -56,13 +56,13 @@ jobs: # - network: westend # package: westend-runtime # wasm: westend_runtime.compact.compressed.wasm - # uri: "wss://westend-try-runtime-node.parity-chains.parity.io:443" + # uri: "wss://try-runtime-westend.polkadot.io:443" # subcommand_extra_args: "--no-weight-warnings" # command_extra_args: "" # - network: rococo # package: rococo-runtime # wasm: rococo_runtime.compact.compressed.wasm - # uri: "wss://rococo-try-runtime-node.parity-chains.parity.io:443" + # uri: "wss://try-runtime-rococo.polkadot.io:443" # subcommand_extra_args: "--no-weight-warnings" # command_extra_args: "" - network: asset-hub-westend diff --git a/.gitlab/pipeline/check.yml b/.gitlab/pipeline/check.yml index bc12dd474b07..3e94eb77c7b4 100644 --- a/.gitlab/pipeline/check.yml +++ b/.gitlab/pipeline/check.yml @@ -106,7 +106,7 @@ check-runtime-migration-westend: NETWORK: "westend" PACKAGE: "westend-runtime" WASM: "westend_runtime.compact.compressed.wasm" - URI: "wss://westend-try-runtime-node.parity-chains.parity.io:443" + URI: "wss://try-runtime-westend.polkadot.io:443" SUBCOMMAND_EXTRA_ARGS: "--no-weight-warnings" check-runtime-migration-rococo: @@ -119,7 +119,7 @@ check-runtime-migration-rococo: NETWORK: "rococo" PACKAGE: "rococo-runtime" WASM: "rococo_runtime.compact.compressed.wasm" - URI: "wss://rococo-try-runtime-node.parity-chains.parity.io:443" + URI: "wss://try-runtime-rococo.polkadot.io:443" SUBCOMMAND_EXTRA_ARGS: "--no-weight-warnings" check-core-crypto-features: diff --git a/prdoc/pr_5443.prdoc b/prdoc/pr_5443.prdoc new file mode 100644 index 000000000000..0fd396be06a2 --- /dev/null +++ b/prdoc/pr_5443.prdoc @@ -0,0 +1,10 @@ +crates: +- name: frame-remote-externalities + bump: patch +doc: +- audience: Runtime Dev + description: as part of https://github.com/paritytech/devops/issues/3502, try-runtime + nodes were migrated to a new provider with a new domain address. The PR fixes + all the references to try-runtime nodes on rococo, westend, kusama and polkadot + networks +title: change try-runtime rpc domains diff --git a/substrate/utils/frame/remote-externalities/src/lib.rs b/substrate/utils/frame/remote-externalities/src/lib.rs index 40864085349b..955e79008c8c 100644 --- a/substrate/utils/frame/remote-externalities/src/lib.rs +++ b/substrate/utils/frame/remote-externalities/src/lib.rs @@ -55,7 +55,7 @@ type ChildKeyValues = Vec<(ChildInfo, Vec)>; type SnapshotVersion = Compact; const LOG_TARGET: &str = "remote-ext"; -const DEFAULT_HTTP_ENDPOINT: &str = "https://polkadot-try-runtime-node.parity-chains.parity.io:443"; +const DEFAULT_HTTP_ENDPOINT: &str = "https://try-runtime.polkadot.io:443"; const SNAPSHOT_VERSION: SnapshotVersion = Compact(4); /// The snapshot that we store on disk. From f0fd083e4a686ac9bd86a77fb631d612d1a78c5f Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:01:23 +0300 Subject: [PATCH 172/382] Update approval-voting-regression-bench (#5504) The accepted divergence rate of 1/1000 is excessive and leads to false positives especially after https://github.com/paritytech/polkadot-sdk/pull/4772 and https://github.com/paritytech/polkadot-sdk/pull/5042, so let's increase it to 1/100 since we do have some randomness in the system and there is no point in being that strict. Fixes: https://github.com/paritytech/polkadot-sdk/issues/5463 --------- Signed-off-by: Alexandru Gheorghe --- .../benches/approval-voting-regression-bench.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs b/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs index a18e667253d0..41418bcc511f 100644 --- a/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs +++ b/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs @@ -74,11 +74,12 @@ fn main() -> Result<(), String> { .map_err(|e| e.to_string())?; println!("{}", average_usage); - // We expect no variance for received and sent - // but use 0.001 because we operate with floats + // We expect some small variance for received and sent because the + // test messages are generated at every benchmark run and they contain + // random data so use 0.01 as the accepted variance. messages.extend(average_usage.check_network_usage(&[ - ("Received from peers", 52941.6071, 0.001), - ("Sent to peers", 63810.1859, 0.001), + ("Received from peers", 52941.6071, 0.01), + ("Sent to peers", 63995.2200, 0.01), ])); messages.extend(average_usage.check_cpu_usage(&[ ("approval-distribution", 6.3912, 0.1), From 09254eb9f8670f942c68914ff6fc0ffb503aaf23 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Wed, 28 Aug 2024 12:12:50 +0200 Subject: [PATCH 173/382] rpc server: listen to `ipv6 socket` if available and `--experimental-rpc-endpoint` CLI option (#4792) Close https://github.com/paritytech/polkadot-sdk/issues/3488, https://github.com/paritytech/polkadot-sdk/issues/4331 This changes/adds the following: 1. The default setting is that substrate starts a rpc server that listens to localhost both Ipv4 and Ipv6 on the same port. Ipv6 is allowed to fail because some platforms may not support it 2. A new RPC CLI option `--experimental-rpc-endpoint` which allow to configure arbitrary listen addresses including the port, if this is enabled no other interfaces are enabled. 3. If the local addr is not found for any of the sockets the server is not started throws an error. 4. Remove the deny_unsafe from the RPC implementations instead this is an extension to allow different polices for different interfaces/sockets such one may enable unsafe on local interface and safe on only the external interface. So for instance in this PR it's now possible to start up three RPC endpoints as follows: ``` $ polkadot --experimental-rpc-endpoint "listen-addr=127.0.0.1:9944,rpc-methods=unsafe" --experimental-rpc-endpoint "listen-addr=0.0.0.0:9945,rpc-methods=safe,rate-limit=100" --experimental-rpc-endpoint "listen-addr=[::1]:9944,optional=true" ``` #### Needs to be addressed ~1. Support binding to a random port if it's fails with the default stuff for backward compatible reasons~ ~2. How to sync that the rpc CLI params and that the rpc-listen-addr align, hard to maintain...~ ~3. Add similar warning prints for exposing unsafe methods on external interfaces..~ ~4. Inline todos + the hacky String conversion from rpc params.~ #### Cons with this PR Manual strings parsing impl more error-prone than relying on clap.... //cc @jsdw @BulatSaif @PierreBesson @bkchr --------- Co-authored-by: Sebastian Kunert --- Cargo.lock | 76 ++-- Cargo.toml | 4 +- cumulus/client/cli/src/lib.rs | 4 +- .../polkadot-parachain-lib/src/cli.rs | 6 +- .../polkadot-parachain-lib/src/rpc.rs | 14 +- .../polkadot-parachain-lib/src/service.rs | 15 +- cumulus/test/service/src/cli.rs | 6 +- cumulus/test/service/src/lib.rs | 23 +- polkadot/node/service/src/lib.rs | 5 +- polkadot/rpc/src/lib.rs | 19 +- prdoc/pr_4792.prdoc | 62 +++ substrate/bin/node/cli/src/service.rs | 4 +- substrate/bin/node/rpc/Cargo.toml | 1 - substrate/bin/node/rpc/src/lib.rs | 16 +- substrate/client/cli/src/arg_enums.rs | 13 + substrate/client/cli/src/commands/run_cmd.rs | 112 ++++- substrate/client/cli/src/config.rs | 13 +- substrate/client/cli/src/params/mod.rs | 4 +- substrate/client/cli/src/params/rpc_params.rs | 395 ++++++++++++++++++ .../client/consensus/babe/rpc/src/lib.rs | 41 +- .../client/consensus/beefy/rpc/src/lib.rs | 14 +- .../client/consensus/grandpa/rpc/src/lib.rs | 8 +- substrate/client/rpc-api/src/author/mod.rs | 10 +- substrate/client/rpc-api/src/dev/mod.rs | 2 +- substrate/client/rpc-api/src/lib.rs | 2 +- substrate/client/rpc-api/src/offchain/mod.rs | 4 +- substrate/client/rpc-api/src/policy.rs | 9 + substrate/client/rpc-api/src/state/mod.rs | 9 +- substrate/client/rpc-api/src/statement/mod.rs | 2 +- substrate/client/rpc-api/src/system/mod.rs | 12 +- substrate/client/rpc-servers/Cargo.toml | 7 +- substrate/client/rpc-servers/src/lib.rs | 377 +++++++++-------- substrate/client/rpc-servers/src/utils.rs | 213 +++++++++- substrate/client/rpc-spec-v2/Cargo.toml | 4 +- substrate/client/rpc/Cargo.toml | 2 - substrate/client/rpc/src/author/mod.rs | 34 +- substrate/client/rpc/src/author/tests.rs | 27 +- substrate/client/rpc/src/dev/mod.rs | 16 +- substrate/client/rpc/src/dev/tests.rs | 9 +- substrate/client/rpc/src/lib.rs | 9 +- substrate/client/rpc/src/offchain/mod.rs | 28 +- substrate/client/rpc/src/offchain/tests.rs | 17 +- substrate/client/rpc/src/state/mod.rs | 41 +- substrate/client/rpc/src/state/tests.rs | 84 ++-- substrate/client/rpc/src/statement/mod.rs | 18 +- substrate/client/rpc/src/system/mod.rs | 43 +- substrate/client/rpc/src/system/tests.rs | 9 +- substrate/client/rpc/src/testing.rs | 17 + substrate/client/service/src/builder.rs | 33 +- substrate/client/service/src/config.rs | 28 +- substrate/client/service/src/lib.rs | 84 ++-- .../rpc/state-trie-migration-rpc/src/lib.rs | 18 +- substrate/utils/frame/rpc/system/src/lib.rs | 46 +- templates/minimal/node/Cargo.toml | 1 - templates/minimal/node/src/rpc.rs | 8 +- templates/minimal/node/src/service.rs | 5 +- templates/parachain/node/src/command.rs | 6 +- templates/parachain/node/src/rpc.rs | 7 +- templates/parachain/node/src/service.rs | 9 +- templates/solochain/node/Cargo.toml | 1 - templates/solochain/node/src/rpc.rs | 8 +- templates/solochain/node/src/service.rs | 5 +- 62 files changed, 1472 insertions(+), 647 deletions(-) create mode 100644 prdoc/pr_4792.prdoc create mode 100644 substrate/client/cli/src/params/rpc_params.rs diff --git a/Cargo.lock b/Cargo.lock index 35e46da1b473..8bf4c356397d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1465,15 +1465,6 @@ dependencies = [ "serde", ] -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" -dependencies = [ - "serde", -] - [[package]] name = "binary-merkle-tree" version = "13.0.0" @@ -1511,7 +1502,7 @@ dependencies = [ "proc-macro2 1.0.82", "quote 1.0.36", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn 2.0.61", ] @@ -7416,9 +7407,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b089779ad7f80768693755a031cc14a7766aba707cbe886674e3f79e9b7e47" +checksum = "5ec465b607a36dc5dd45d48b7689bc83f679f66a3ac6b6b21cc787a11e0f8685" dependencies = [ "jsonrpsee-core", "jsonrpsee-http-client", @@ -7432,9 +7423,9 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08163edd8bcc466c33d79e10f695cdc98c00d1e6ddfb95cec41b6b0279dd5432" +checksum = "90f0977f9c15694371b8024c35ab58ca043dbbf4b51ccb03db8858a021241df1" dependencies = [ "base64 0.22.1", "futures-util", @@ -7455,13 +7446,11 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79712302e737d23ca0daa178e752c9334846b08321d439fd89af9a384f8c830b" +checksum = "e942c55635fbf5dc421938b8558a8141c7e773720640f4f1dbe1f4164ca4e221" dependencies = [ - "anyhow", "async-trait", - "beef", "bytes", "futures-timer", "futures-util", @@ -7472,7 +7461,7 @@ dependencies = [ "parking_lot 0.12.3", "pin-project", "rand", - "rustc-hash", + "rustc-hash 2.0.0", "serde", "serde_json", "thiserror", @@ -7483,9 +7472,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d90064e04fb9d7282b1c71044ea94d0bbc6eff5621c66f1a0bce9e9de7cf3ac" +checksum = "e33774602df12b68a2310b38a535733c477ca4a498751739f89fe8dbbb62ec4c" dependencies = [ "async-trait", "base64 0.22.1", @@ -7508,9 +7497,9 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7895f186d5921065d96e16bd795e5ca89ac8356ec423fafc6e3d7cf8ec11aee4" +checksum = "6b07a2daf52077ab1b197aea69a5c990c060143835bf04c77070e98903791715" dependencies = [ "heck 0.5.0", "proc-macro-crate 3.1.0", @@ -7521,11 +7510,10 @@ dependencies = [ [[package]] name = "jsonrpsee-server" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "654afab2e92e5d88ebd8a39d6074483f3f2bfdf91c5ac57fe285e7127cdd4f51" +checksum = "038fb697a709bec7134e9ccbdbecfea0e2d15183f7140254afef7c5610a3f488" dependencies = [ - "anyhow", "futures-util", "http 1.1.0", "http-body 1.0.0", @@ -7549,11 +7537,10 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c465fbe385238e861fdc4d1c85e04ada6c1fd246161d26385c1b311724d2af" +checksum = "23b67d6e008164f027afbc2e7bb79662650158d26df200040282d2aa1cbb093b" dependencies = [ - "beef", "http 1.1.0", "serde", "serde_json", @@ -7562,9 +7549,9 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c28759775f5cb2f1ea9667672d3fe2b0e701d1f4b7b67954e60afe7fd058b5e" +checksum = "992bf67d1132f88edf4a4f8cff474cf01abb2be203004a2b8e11c2b20795b99e" dependencies = [ "http 1.1.0", "jsonrpsee-client-transport", @@ -8748,7 +8735,6 @@ dependencies = [ "sc-executor", "sc-network", "sc-offchain", - "sc-rpc-api", "sc-service", "sc-telemetry", "sc-transaction-pool", @@ -9283,7 +9269,6 @@ dependencies = [ "sc-consensus-grandpa-rpc", "sc-mixnet", "sc-rpc", - "sc-rpc-api", "sc-sync-state-rpc", "sc-transaction-pool-api", "sp-api", @@ -15984,7 +15969,7 @@ dependencies = [ "pin-project-lite", "quinn-proto 0.9.6", "quinn-udp 0.3.2", - "rustc-hash", + "rustc-hash 1.1.0", "rustls 0.20.9", "thiserror", "tokio", @@ -16003,7 +15988,7 @@ dependencies = [ "pin-project-lite", "quinn-proto 0.10.6", "quinn-udp 0.4.1", - "rustc-hash", + "rustc-hash 1.1.0", "rustls 0.21.7", "thiserror", "tokio", @@ -16019,7 +16004,7 @@ dependencies = [ "bytes", "rand", "ring 0.16.20", - "rustc-hash", + "rustc-hash 1.1.0", "rustls 0.20.9", "slab", "thiserror", @@ -16037,7 +16022,7 @@ dependencies = [ "bytes", "rand", "ring 0.16.20", - "rustc-hash", + "rustc-hash 1.1.0", "rustls 0.21.7", "slab", "thiserror", @@ -16310,7 +16295,7 @@ checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" dependencies = [ "hashbrown 0.13.2", "log", - "rustc-hash", + "rustc-hash 1.1.0", "slice-group-by", "smallvec", ] @@ -16911,6 +16896,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -18281,11 +18272,9 @@ dependencies = [ "sp-runtime", "sp-session", "sp-statement-store", - "sp-tracing 16.0.0", "sp-version", "substrate-test-runtime-client", "tokio", - "tracing-subscriber 0.3.18", ] [[package]] @@ -18311,6 +18300,7 @@ dependencies = [ name = "sc-rpc-server" version = "11.0.0" dependencies = [ + "dyn-clone", "forwarded-header-value", "futures", "governor", @@ -18320,6 +18310,7 @@ dependencies = [ "ip_network", "jsonrpsee", "log", + "sc-rpc-api", "serde", "serde_json", "substrate-prometheus-endpoint", @@ -18597,7 +18588,7 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.3", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "sc-client-api", "sc-tracing-proc-macro", "serde", @@ -19957,7 +19948,6 @@ dependencies = [ "sc-executor", "sc-network", "sc-offchain", - "sc-rpc-api", "sc-service", "sc-telemetry", "sc-transaction-pool", @@ -20646,7 +20636,7 @@ dependencies = [ name = "sp-rpc" version = "26.0.0" dependencies = [ - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", "sp-core", diff --git a/Cargo.toml b/Cargo.toml index 275efe1df638..a181a935ea06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -812,8 +812,8 @@ isahc = { version = "1.2" } itertools = { version = "0.11" } jobserver = { version = "0.1.26" } jsonpath_lib = { version = "0.3" } -jsonrpsee = { version = "0.23.2" } -jsonrpsee-core = { version = "0.23.2" } +jsonrpsee = { version = "0.24.3" } +jsonrpsee-core = { version = "0.24.3" } k256 = { version = "0.13.3", default-features = false } kitchensink-runtime = { path = "substrate/bin/node/runtime" } kvdb = { version = "0.13.0" } diff --git a/cumulus/client/cli/src/lib.rs b/cumulus/client/cli/src/lib.rs index a7b2eb19de88..564d7b58c94d 100644 --- a/cumulus/client/cli/src/lib.rs +++ b/cumulus/client/cli/src/lib.rs @@ -21,13 +21,13 @@ use std::{ fs, io::{self, Write}, - net::SocketAddr, path::PathBuf, sync::Arc, }; use codec::Encode; use sc_chain_spec::ChainSpec; +use sc_cli::RpcEndpoint; use sc_client_api::HeaderBackend; use sc_service::{ config::{PrometheusConfig, RpcBatchRequestConfig, TelemetryEndpoints}, @@ -423,7 +423,7 @@ impl sc_cli::CliConfiguration for NormalizedRunCmd { self.base.rpc_cors(is_dev) } - fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result> { + fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result>> { self.base.rpc_addr(default_listen_port) } diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs index 2aa2b10fbb67..31eb2cf88060 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs @@ -25,10 +25,10 @@ use clap::{Command, CommandFactory, FromArgMatches}; use sc_chain_spec::ChainSpec; use sc_cli::{ CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams, - SharedParams, SubstrateCli, + RpcEndpoint, SharedParams, SubstrateCli, }; use sc_service::{config::PrometheusConfig, BasePath}; -use std::{fmt::Debug, marker::PhantomData, net::SocketAddr, path::PathBuf}; +use std::{fmt::Debug, marker::PhantomData, path::PathBuf}; /// Trait that can be used to customize some of the customer-facing info related to the node binary /// that is being built using this library. @@ -300,7 +300,7 @@ impl CliConfiguration for RelayChainCli { .or_else(|| self.base_path.clone().map(Into::into))) } - fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result> { + fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result>> { self.base.base.rpc_addr(default_listen_port) } diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/rpc.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/rpc.rs index 283a73d931d7..d156ebe2bd6d 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/rpc.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/rpc.rs @@ -24,10 +24,7 @@ use crate::{ }; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; use parachains_common::{AccountId, Balance, Block, Nonce}; -use sc_rpc::{ - dev::{Dev, DevApiServer}, - DenyUnsafe, -}; +use sc_rpc::dev::{Dev, DevApiServer}; use std::{marker::PhantomData, sync::Arc}; use substrate_frame_rpc_system::{System, SystemApiServer}; use substrate_state_trie_migration_rpc::{StateMigration, StateMigrationApiServer}; @@ -37,7 +34,6 @@ pub type RpcExtension = jsonrpsee::RpcModule<()>; pub(crate) trait BuildRpcExtensions { fn build_rpc_extensions( - deny_unsafe: DenyUnsafe, client: Arc, backend: Arc, pool: Arc, @@ -56,7 +52,6 @@ where RuntimeApi: ConstructNodeRuntimeApi> + Send + Sync + 'static, { fn build_rpc_extensions( - _deny_unsafe: DenyUnsafe, _client: Arc>, _backend: Arc, _pool: Arc>>, @@ -79,7 +74,6 @@ where + substrate_frame_rpc_system::AccountNonceApi, { fn build_rpc_extensions( - deny_unsafe: DenyUnsafe, client: Arc>, backend: Arc, pool: Arc>>, @@ -87,10 +81,10 @@ where let build = || -> Result> { let mut module = RpcExtension::new(()); - module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; + module.merge(System::new(client.clone(), pool).into_rpc())?; module.merge(TransactionPayment::new(client.clone()).into_rpc())?; - module.merge(StateMigration::new(client.clone(), backend, deny_unsafe).into_rpc())?; - module.merge(Dev::new(client, deny_unsafe).into_rpc())?; + module.merge(StateMigration::new(client.clone(), backend).into_rpc())?; + module.merge(Dev::new(client).into_rpc())?; Ok(module) }; diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs index 3b9ae6bd4457..057b8af9af52 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs @@ -281,9 +281,8 @@ pub(crate) trait NodeSpec { let transaction_pool = transaction_pool.clone(); let backend_for_rpc = backend.clone(); - Box::new(move |deny_unsafe, _| { + Box::new(move |_| { Self::BuildRpcExtensions::build_rpc_extensions( - deny_unsafe, client.clone(), backend_for_rpc.clone(), transaction_pool.clone(), @@ -837,11 +836,13 @@ where }, }; - let fut = - async move { - wait_for_aura(client).await; - aura::run_with_export::::Pair, _, _, _, _, _, _, _, _>(params).await; - }; + let fut = async move { + wait_for_aura(client).await; + aura::run_with_export::::Pair, _, _, _, _, _, _, _, _>( + params, + ) + .await; + }; task_manager.spawn_essential_handle().spawn("aura", None, fut); Ok(()) diff --git a/cumulus/test/service/src/cli.rs b/cumulus/test/service/src/cli.rs index 37ca27542cbf..739c2d4bda16 100644 --- a/cumulus/test/service/src/cli.rs +++ b/cumulus/test/service/src/cli.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use std::{net::SocketAddr, path::PathBuf}; +use std::path::PathBuf; use cumulus_client_cli::{ExportGenesisHeadCommand, ExportGenesisWasmCommand}; use polkadot_service::{ChainSpec, ParaId, PrometheusConfig}; use sc_cli::{ CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams, - Result as CliResult, SharedParams, SubstrateCli, + Result as CliResult, RpcEndpoint, SharedParams, SubstrateCli, }; use sc_service::BasePath; @@ -122,7 +122,7 @@ impl CliConfiguration for RelayChainCli { .or_else(|| self.base_path.clone().map(Into::into))) } - fn rpc_addr(&self, default_listen_port: u16) -> CliResult> { + fn rpc_addr(&self, default_listen_port: u16) -> CliResult>> { self.base.base.rpc_addr(default_listen_port) } diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index 503c2f24fd54..bc0fe9090d38 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -38,7 +38,7 @@ use sp_consensus_aura::sr25519::AuthorityPair; use std::{ collections::HashSet, future::Future, - net::{IpAddr, Ipv4Addr, SocketAddr}, + net::{Ipv4Addr, SocketAddr, SocketAddrV4}, time::Duration, }; use url::Url; @@ -79,7 +79,7 @@ use sc_network::{ use sc_service::{ config::{ BlocksPruning, DatabaseSource, KeystoreConfig, MultiaddrWithPeerId, NetworkConfiguration, - OffchainWorkerConfig, PruningMode, RpcBatchRequestConfig, WasmExecutionMethod, + OffchainWorkerConfig, PruningMode, RpcBatchRequestConfig, RpcEndpoint, WasmExecutionMethod, }, BasePath, ChainSpec as ChainSpecService, Configuration, Error as ServiceError, PartialComponents, Role, RpcHandlers, TFullBackend, TFullClient, TaskManager, @@ -379,7 +379,7 @@ where let keystore = params.keystore_container.keystore(); let rpc_builder = { let client = client.clone(); - Box::new(move |_, _| rpc_ext_builder(client.clone())) + Box::new(move |_| rpc_ext_builder(client.clone())) }; let rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { @@ -1006,7 +1006,22 @@ pub fn run_relay_chain_validator_node( ); if let Some(port) = port { - config.rpc_addr = Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port)); + config.rpc_addr = Some(vec![RpcEndpoint { + batch_config: config.rpc_batch_config, + cors: config.rpc_cors.clone(), + listen_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, port)), + max_connections: config.rpc_max_connections, + max_payload_in_mb: config.rpc_max_request_size, + max_payload_out_mb: config.rpc_max_response_size, + max_subscriptions_per_connection: config.rpc_max_subs_per_conn, + max_buffer_capacity_per_connection: config.rpc_message_buffer_capacity, + rpc_methods: config.rpc_methods, + rate_limit: config.rpc_rate_limit, + rate_limit_trust_proxy_headers: config.rpc_rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: config.rpc_rate_limit_whitelisted_ips.clone(), + retry_random_port: true, + is_optional: false, + }]); } let mut workers_path = std::env::current_exe().unwrap(); diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index e1f42e1ca86a..a907d310c105 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -486,7 +486,6 @@ fn new_partial( sc_transaction_pool::FullPool, ( impl Fn( - polkadot_rpc::DenyUnsafe, polkadot_rpc::SubscriptionTaskExecutor, ) -> Result, ( @@ -593,15 +592,13 @@ where let chain_spec = config.chain_spec.cloned_box(); let backend = backend.clone(); - move |deny_unsafe, - subscription_executor: polkadot_rpc::SubscriptionTaskExecutor| + move |subscription_executor: polkadot_rpc::SubscriptionTaskExecutor| -> Result { let deps = polkadot_rpc::FullDeps { client: client.clone(), pool: transaction_pool.clone(), select_chain: select_chain.clone(), chain_spec: chain_spec.cloned_box(), - deny_unsafe, babe: polkadot_rpc::BabeDeps { babe_worker_handle: babe_worker_handle.clone(), keystore: keystore.clone(), diff --git a/polkadot/rpc/src/lib.rs b/polkadot/rpc/src/lib.rs index eb0133b6e8fd..0007df908e2b 100644 --- a/polkadot/rpc/src/lib.rs +++ b/polkadot/rpc/src/lib.rs @@ -27,7 +27,7 @@ use sc_consensus_beefy::communication::notification::{ BeefyBestBlockStream, BeefyVersionedFinalityProofStream, }; use sc_consensus_grandpa::FinalityProofProvider; -pub use sc_rpc::{DenyUnsafe, SubscriptionTaskExecutor}; +pub use sc_rpc::SubscriptionTaskExecutor; use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; use sp_application_crypto::RuntimeAppPublic; @@ -83,8 +83,6 @@ pub struct FullDeps { pub select_chain: SC, /// A copy of the chain spec. pub chain_spec: Box, - /// Whether to deny unsafe calls - pub deny_unsafe: DenyUnsafe, /// BABE specific dependencies. pub babe: BabeDeps, /// GRANDPA specific dependencies. @@ -97,7 +95,13 @@ pub struct FullDeps { /// Instantiate all RPC extensions. pub fn create_full( - FullDeps { client, pool, select_chain, chain_spec, deny_unsafe, babe, grandpa, beefy, backend } : FullDeps, + FullDeps { client, pool, select_chain, chain_spec, babe, grandpa, beefy, backend }: FullDeps< + C, + P, + SC, + B, + AuthorityId, + >, ) -> Result> where C: ProvideRuntimeApi @@ -138,8 +142,8 @@ where finality_provider, } = grandpa; - io.merge(StateMigration::new(client.clone(), backend.clone(), deny_unsafe).into_rpc())?; - io.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?; + io.merge(StateMigration::new(client.clone(), backend.clone()).into_rpc())?; + io.merge(System::new(client.clone(), pool.clone()).into_rpc())?; io.merge(TransactionPayment::new(client.clone()).into_rpc())?; io.merge( Mmr::new( @@ -151,8 +155,7 @@ where .into_rpc(), )?; io.merge( - Babe::new(client.clone(), babe_worker_handle.clone(), keystore, select_chain, deny_unsafe) - .into_rpc(), + Babe::new(client.clone(), babe_worker_handle.clone(), keystore, select_chain).into_rpc(), )?; io.merge( Grandpa::new( diff --git a/prdoc/pr_4792.prdoc b/prdoc/pr_4792.prdoc new file mode 100644 index 000000000000..5ce4303bcf75 --- /dev/null +++ b/prdoc/pr_4792.prdoc @@ -0,0 +1,62 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "rpc: bind to `ipv6` if available and add `CLI --experimental-rpc-endpoint` to specify listen addr" + +doc: + - audience: Node Operator + description: | + This PR changes/adds the following: + + 1. The default setting is that substrate starts a rpc server that listens to localhost both ipv4 and ipv6 on the same port. + ipv6 is allowed to fail because some platforms may not support it + 2. A new RPC CLI option `--experimental-rpc-endpoint` is introduced which allows to configure arbitrary listen addresses including the port, + if this is enabled no other interfaces are enabled. + 3. If the local addr is not found for any of the sockets the server is not started and throws an error. + 4. Remove the deny_unsafe from the RPC implementations instead this is an extension to allow different polices for different interfaces/sockets + such one may enable unsafe on local interface and safe on only the external interface. + 5. This new `--experimental-rpc-endpoint` has several options and in the help menu all possible parameters are documented. + 6. The log emitted by jsonrpc server when it has been started has been modified to indicate all started rpc endpoints. + + So for instance it's now possible to start up three RPC endpoints as follows: + ``` + $ polkadot --experimental-rpc-endpoint "listen-addr=127.0.0.1:9944,methods=unsafe" --experimental-rpc-endpoint "listen-addr=0.0.0.0:9945,methods=safe,rate-limit=100" --experimental-rpc-endpoint "listen-addr=[::1]:9944,optional=true" + ``` + +crates: + - name: sc-rpc-server + bump: major + - name: sc-rpc + bump: major + - name: sc-cli + bump: major + - name: sc-service + bump: major + - name: sc-rpc-api + bump: patch + - name: polkadot-dispute-distribution + bump: patch + - name: polkadot-parachain-lib + bump: patch + - name: substrate-frame-rpc-system + bump: major + - name: substrate-state-trie-migration-rpc + bump: major + - name: cumulus-client-cli + bump: major + validate: false + - name: sc-consensus-beefy-rpc + bump: major + validate: false + - name: sc-consensus-grandpa-rpc + bump: major + validate: false + - name: sc-consensus-babe-rpc + bump: major + validate: false + - name: polkadot-rpc + bump: major + validate: false + - name: polkadot-service + bump: major + validate: false diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index d45713db5222..8fdcc7261b55 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -177,7 +177,6 @@ pub fn new_partial( sc_transaction_pool::FullPool, ( impl Fn( - node_rpc::DenyUnsafe, sc_rpc::SubscriptionTaskExecutor, ) -> Result, sc_service::Error>, ( @@ -318,13 +317,12 @@ pub fn new_partial( let rpc_backend = backend.clone(); let rpc_statement_store = statement_store.clone(); let rpc_extensions_builder = - move |deny_unsafe, subscription_executor: node_rpc::SubscriptionTaskExecutor| { + move |subscription_executor: node_rpc::SubscriptionTaskExecutor| { let deps = node_rpc::FullDeps { client: client.clone(), pool: pool.clone(), select_chain: select_chain.clone(), chain_spec: chain_spec.cloned_box(), - deny_unsafe, babe: node_rpc::BabeDeps { keystore: keystore.clone(), babe_worker_handle: babe_worker_handle.clone(), diff --git a/substrate/bin/node/rpc/Cargo.toml b/substrate/bin/node/rpc/Cargo.toml index d85998e3c87b..02f5d9a4a702 100644 --- a/substrate/bin/node/rpc/Cargo.toml +++ b/substrate/bin/node/rpc/Cargo.toml @@ -31,7 +31,6 @@ sc-consensus-grandpa = { workspace = true, default-features = true } sc-consensus-grandpa-rpc = { workspace = true, default-features = true } sc-mixnet = { workspace = true, default-features = true } sc-rpc = { workspace = true, default-features = true } -sc-rpc-api = { workspace = true, default-features = true } sc-sync-state-rpc = { workspace = true, default-features = true } sc-transaction-pool-api = { workspace = true, default-features = true } sp-api = { workspace = true, default-features = true } diff --git a/substrate/bin/node/rpc/src/lib.rs b/substrate/bin/node/rpc/src/lib.rs index c55e03ee9d6f..988502bb2bfd 100644 --- a/substrate/bin/node/rpc/src/lib.rs +++ b/substrate/bin/node/rpc/src/lib.rs @@ -44,7 +44,6 @@ use sc_consensus_grandpa::{ FinalityProofProvider, GrandpaJustificationStream, SharedAuthoritySet, SharedVoterState, }; pub use sc_rpc::SubscriptionTaskExecutor; -pub use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; use sp_application_crypto::RuntimeAppPublic; @@ -97,8 +96,6 @@ pub struct FullDeps { pub select_chain: SC, /// A copy of the chain spec. pub chain_spec: Box, - /// Whether to deny unsafe calls - pub deny_unsafe: DenyUnsafe, /// BABE specific dependencies. pub babe: BabeDeps, /// GRANDPA specific dependencies. @@ -120,7 +117,6 @@ pub fn create_full( pool, select_chain, chain_spec, - deny_unsafe, babe, grandpa, beefy, @@ -175,7 +171,7 @@ where finality_provider, } = grandpa; - io.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; + io.merge(System::new(client.clone(), pool).into_rpc())?; // Making synchronous calls in light client freezes the browser currently, // more context: https://github.com/paritytech/substrate/pull/3480 // These RPCs should use an asynchronous caller instead. @@ -190,8 +186,7 @@ where )?; io.merge(TransactionPayment::new(client.clone()).into_rpc())?; io.merge( - Babe::new(client.clone(), babe_worker_handle.clone(), keystore, select_chain, deny_unsafe) - .into_rpc(), + Babe::new(client.clone(), babe_worker_handle.clone(), keystore, select_chain).into_rpc(), )?; io.merge( Grandpa::new( @@ -209,10 +204,9 @@ where .into_rpc(), )?; - io.merge(StateMigration::new(client.clone(), backend, deny_unsafe).into_rpc())?; - io.merge(Dev::new(client, deny_unsafe).into_rpc())?; - let statement_store = - sc_rpc::statement::StatementStore::new(statement_store, deny_unsafe).into_rpc(); + io.merge(StateMigration::new(client.clone(), backend).into_rpc())?; + io.merge(Dev::new(client).into_rpc())?; + let statement_store = sc_rpc::statement::StatementStore::new(statement_store).into_rpc(); io.merge(statement_store)?; if let Some(mixnet_api) = mixnet_api { diff --git a/substrate/client/cli/src/arg_enums.rs b/substrate/client/cli/src/arg_enums.rs index b5819d03447a..cd245dc01554 100644 --- a/substrate/client/cli/src/arg_enums.rs +++ b/substrate/client/cli/src/arg_enums.rs @@ -168,6 +168,19 @@ pub enum RpcMethods { Unsafe, } +impl FromStr for RpcMethods { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "safe" => Ok(RpcMethods::Safe), + "unsafe" => Ok(RpcMethods::Unsafe), + "auto" => Ok(RpcMethods::Auto), + invalid => Err(format!("Invalid rpc methods {invalid}")), + } + } +} + impl Into for RpcMethods { fn into(self) -> sc_service::config::RpcMethods { match self { diff --git a/substrate/client/cli/src/commands/run_cmd.rs b/substrate/client/cli/src/commands/run_cmd.rs index c1288b502c95..7245b46e2f7d 100644 --- a/substrate/client/cli/src/commands/run_cmd.rs +++ b/substrate/client/cli/src/commands/run_cmd.rs @@ -20,8 +20,8 @@ use crate::{ arg_enums::{Cors, RpcMethods}, error::{Error, Result}, params::{ - ImportParams, KeystoreParams, NetworkParams, OffchainWorkerParams, SharedParams, - TransactionPoolParams, + ImportParams, KeystoreParams, NetworkParams, OffchainWorkerParams, RpcEndpoint, + SharedParams, TransactionPoolParams, }, CliConfiguration, PrometheusParams, RuntimeParams, TelemetryParams, RPC_DEFAULT_MAX_CONNECTIONS, RPC_DEFAULT_MAX_REQUEST_SIZE_MB, RPC_DEFAULT_MAX_RESPONSE_SIZE_MB, @@ -37,7 +37,7 @@ use sc_service::{ }; use sc_telemetry::TelemetryEndpoints; use std::{ - net::{IpAddr, Ipv4Addr, SocketAddr}, + net::{Ipv4Addr, Ipv6Addr, SocketAddr}, num::NonZeroU32, }; @@ -128,6 +128,47 @@ pub struct RunCmd { #[arg(long, value_name = "PORT")] pub rpc_port: Option, + /// EXPERIMENTAL: Specify the JSON-RPC server interface and this option which can be enabled + /// several times if you want expose several RPC interfaces with different configurations. + /// + /// The format for this option is: + /// `--experimental-rpc-endpoint" listen-addr=,,..."` where each option is + /// separated by a comma and `listen-addr` is the only required param. + /// + /// The following options are available: + /// • listen-addr: The socket address (ip:port) to listen on. Be careful to not expose the + /// server to the public internet unless you know what you're doing. (required) + /// • disable-batch-requests: Disable batch requests (optional) + /// • max-connections: The maximum number of concurrent connections that the server will + /// accept (optional) + /// • max-request-size: The maximum size of a request body in megabytes (optional) + /// • max-response-size: The maximum size of a response body in megabytes (optional) + /// • max-subscriptions-per-connection: The maximum number of subscriptions per connection + /// (optional) + /// • max-buffer-capacity-per-connection: The maximum buffer capacity per connection + /// (optional) + /// • max-batch-request-len: The maximum number of requests in a batch (optional) + /// • cors: The CORS allowed origins, this can enabled more than once (optional) + /// • methods: Which RPC methods to allow, valid values are "safe", "unsafe" and "auto" + /// (optional) + /// • optional: If the listen address is optional i.e the interface is not required to be + /// available For example this may be useful if some platforms doesn't support ipv6 + /// (optional) + /// • rate-limit: The rate limit in calls per minute for each connection (optional) + /// • rate-limit-trust-proxy-headers: Trust proxy headers for disable rate limiting (optional) + /// • rate-limit-whitelisted-ips: Disable rate limiting for certain ip addresses, this can be + /// enabled more than once (optional) • retry-random-port: If the port is already in use, + /// retry with a random port (optional) + /// + /// Use with care, this flag is unstable and subject to change. + #[arg( + long, + num_args = 1.., + verbatim_doc_comment, + conflicts_with_all = &["rpc_external", "unsafe_rpc_external", "rpc_port", "rpc_cors", "rpc_rate_limit_trust_proxy_headers", "rpc_rate_limit", "rpc_rate_limit_whitelisted_ips", "rpc_message_buffer_capacity_per_connection", "rpc_disable_batch_requests", "rpc_max_subscriptions_per_connection", "rpc_max_request_size", "rpc_max_response_size"] + )] + pub experimental_rpc_endpoint: Vec, + /// Maximum number of RPC server connections. #[arg(long, value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS)] pub rpc_max_connections: u32, @@ -410,15 +451,68 @@ impl CliConfiguration for RunCmd { .into()) } - fn rpc_addr(&self, default_listen_port: u16) -> Result> { - let interface = rpc_interface( + fn rpc_addr(&self, default_listen_port: u16) -> Result>> { + if !self.experimental_rpc_endpoint.is_empty() { + for endpoint in &self.experimental_rpc_endpoint { + // Technically, `0.0.0.0` isn't a public IP address, but it's a way to listen on + // all interfaces. Thus, we consider it as a public endpoint and warn about it. + if endpoint.rpc_methods == RpcMethods::Unsafe && endpoint.is_global() || + endpoint.listen_addr.ip().is_unspecified() + { + log::warn!( + "It isn't safe to expose RPC publicly without a proxy server that filters \ + available set of RPC methods." + ); + } + } + + return Ok(Some(self.experimental_rpc_endpoint.clone())); + } + + let (ipv4, ipv6) = rpc_interface( self.rpc_external, self.unsafe_rpc_external, self.rpc_methods, self.validator, )?; - Ok(Some(SocketAddr::new(interface, self.rpc_port.unwrap_or(default_listen_port)))) + let cors = self.rpc_cors(self.is_dev()?)?; + let port = self.rpc_port.unwrap_or(default_listen_port); + + Ok(Some(vec![ + RpcEndpoint { + batch_config: self.rpc_batch_config()?, + max_connections: self.rpc_max_connections, + listen_addr: SocketAddr::new(std::net::IpAddr::V4(ipv4), port), + rpc_methods: self.rpc_methods, + rate_limit: self.rpc_rate_limit, + rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips.clone(), + max_payload_in_mb: self.rpc_max_request_size, + max_payload_out_mb: self.rpc_max_response_size, + max_subscriptions_per_connection: self.rpc_max_subscriptions_per_connection, + max_buffer_capacity_per_connection: self.rpc_message_buffer_capacity_per_connection, + cors: cors.clone(), + retry_random_port: true, + is_optional: false, + }, + RpcEndpoint { + batch_config: self.rpc_batch_config()?, + max_connections: self.rpc_max_connections, + listen_addr: SocketAddr::new(std::net::IpAddr::V6(ipv6), port), + rpc_methods: self.rpc_methods, + rate_limit: self.rpc_rate_limit, + rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips.clone(), + max_payload_in_mb: self.rpc_max_request_size, + max_payload_out_mb: self.rpc_max_response_size, + max_subscriptions_per_connection: self.rpc_max_subscriptions_per_connection, + max_buffer_capacity_per_connection: self.rpc_message_buffer_capacity_per_connection, + cors: cors.clone(), + retry_random_port: true, + is_optional: true, + }, + ])) } fn rpc_methods(&self) -> Result { @@ -523,7 +617,7 @@ fn rpc_interface( is_unsafe_external: bool, rpc_methods: RpcMethods, is_validator: bool, -) -> Result { +) -> Result<(Ipv4Addr, Ipv6Addr)> { if is_external && is_validator && rpc_methods != RpcMethods::Unsafe { return Err(Error::Input( "--rpc-external option shouldn't be used if the node is running as \ @@ -541,9 +635,9 @@ fn rpc_interface( ); } - Ok(Ipv4Addr::UNSPECIFIED.into()) + Ok((Ipv4Addr::UNSPECIFIED, Ipv6Addr::UNSPECIFIED)) } else { - Ok(Ipv4Addr::LOCALHOST.into()) + Ok((Ipv4Addr::LOCALHOST, Ipv6Addr::LOCALHOST)) } } diff --git a/substrate/client/cli/src/config.rs b/substrate/client/cli/src/config.rs index 406d1fb264dd..7c2358477611 100644 --- a/substrate/client/cli/src/config.rs +++ b/substrate/client/cli/src/config.rs @@ -20,7 +20,8 @@ use crate::{ arg_enums::Database, error::Result, DatabaseParams, ImportParams, KeystoreParams, - NetworkParams, NodeKeyParams, OffchainWorkerParams, PruningParams, SharedParams, SubstrateCli, + NetworkParams, NodeKeyParams, OffchainWorkerParams, PruningParams, RpcEndpoint, SharedParams, + SubstrateCli, }; use log::warn; use names::{Generator, Name}; @@ -34,7 +35,7 @@ use sc_service::{ BlocksPruning, ChainSpec, TracingReceiver, }; use sc_tracing::logging::LoggerBuilder; -use std::{net::SocketAddr, num::NonZeroU32, path::PathBuf}; +use std::{num::NonZeroU32, path::PathBuf}; /// The maximum number of characters for a node name. pub(crate) const NODE_NAME_MAX_LENGTH: usize = 64; @@ -301,7 +302,7 @@ pub trait CliConfiguration: Sized { } /// Get the RPC address. - fn rpc_addr(&self, _default_listen_port: u16) -> Result> { + fn rpc_addr(&self, _default_listen_port: u16) -> Result>> { Ok(None) } @@ -504,6 +505,10 @@ pub trait CliConfiguration: Sized { let telemetry_endpoints = self.telemetry_endpoints(&chain_spec)?; let runtime_cache_size = self.runtime_cache_size()?; + let rpc_addrs: Option> = self + .rpc_addr(DCV::rpc_listen_port())? + .map(|addrs| addrs.into_iter().map(Into::into).collect()); + Ok(Configuration { impl_name: C::impl_name(), impl_version: C::impl_version(), @@ -527,7 +532,7 @@ pub trait CliConfiguration: Sized { blocks_pruning: self.blocks_pruning()?, wasm_method: self.wasm_method()?, wasm_runtime_overrides: self.wasm_runtime_overrides(), - rpc_addr: self.rpc_addr(DCV::rpc_listen_port())?, + rpc_addr: rpc_addrs, rpc_methods: self.rpc_methods()?, rpc_max_connections: self.rpc_max_connections()?, rpc_cors: self.rpc_cors(is_dev)?, diff --git a/substrate/client/cli/src/params/mod.rs b/substrate/client/cli/src/params/mod.rs index f07223ec6a73..977b57ba0658 100644 --- a/substrate/client/cli/src/params/mod.rs +++ b/substrate/client/cli/src/params/mod.rs @@ -25,6 +25,7 @@ mod node_key_params; mod offchain_worker_params; mod prometheus_params; mod pruning_params; +mod rpc_params; mod runtime_params; mod shared_params; mod telemetry_params; @@ -32,6 +33,7 @@ mod transaction_pool_params; use crate::arg_enums::{CryptoScheme, OutputType}; use clap::Args; +use sc_service::config::{IpNetwork, RpcBatchRequestConfig}; use sp_core::crypto::{Ss58AddressFormat, Ss58AddressFormatRegistry}; use sp_runtime::{ generic::BlockId, @@ -42,7 +44,7 @@ use std::{fmt::Debug, str::FromStr}; pub use crate::params::{ database_params::*, import_params::*, keystore_params::*, message_params::*, mixnet_params::*, network_params::*, node_key_params::*, offchain_worker_params::*, prometheus_params::*, - pruning_params::*, runtime_params::*, shared_params::*, telemetry_params::*, + pruning_params::*, rpc_params::*, runtime_params::*, shared_params::*, telemetry_params::*, transaction_pool_params::*, }; diff --git a/substrate/client/cli/src/params/rpc_params.rs b/substrate/client/cli/src/params/rpc_params.rs new file mode 100644 index 000000000000..d0ec1fd00443 --- /dev/null +++ b/substrate/client/cli/src/params/rpc_params.rs @@ -0,0 +1,395 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{ + arg_enums::RpcMethods, + params::{IpNetwork, RpcBatchRequestConfig}, + RPC_DEFAULT_MAX_CONNECTIONS, RPC_DEFAULT_MAX_REQUEST_SIZE_MB, RPC_DEFAULT_MAX_RESPONSE_SIZE_MB, + RPC_DEFAULT_MAX_SUBS_PER_CONN, RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN, +}; +use std::{net::SocketAddr, num::NonZeroU32}; + +const RPC_LISTEN_ADDR: &str = "listen-addr"; +const RPC_CORS: &str = "cors"; +const RPC_MAX_CONNS: &str = "max-connections"; +const RPC_MAX_REQUEST_SIZE: &str = "max-request-size"; +const RPC_MAX_RESPONSE_SIZE: &str = "max-response-size"; +const RPC_MAX_SUBS_PER_CONN: &str = "max-subscriptions-per-connection"; +const RPC_MAX_BUF_CAP_PER_CONN: &str = "max-buffer-capacity-per-connection"; +const RPC_RATE_LIMIT: &str = "rate-limit"; +const RPC_RATE_LIMIT_TRUST_PROXY_HEADERS: &str = "rate-limit-trust-proxy-headers"; +const RPC_RATE_LIMIT_WHITELISTED_IPS: &str = "rate-limit-whitelisted-ips"; +const RPC_RETRY_RANDOM_PORT: &str = "retry-random-port"; +const RPC_METHODS: &str = "methods"; +const RPC_OPTIONAL: &str = "optional"; +const RPC_DISABLE_BATCH: &str = "disable-batch-requests"; +const RPC_BATCH_LIMIT: &str = "max-batch-request-len"; + +/// Represent a single RPC endpoint with its configuration. +#[derive(Debug, Clone)] +pub struct RpcEndpoint { + /// Listen address. + pub listen_addr: SocketAddr, + /// Batch request configuration. + pub batch_config: RpcBatchRequestConfig, + /// Maximum number of connections. + pub max_connections: u32, + /// Maximum inbound payload size in MB. + pub max_payload_in_mb: u32, + /// Maximum outbound payload size in MB. + pub max_payload_out_mb: u32, + /// Maximum number of subscriptions per connection. + pub max_subscriptions_per_connection: u32, + /// Maximum buffer capacity per connection. + pub max_buffer_capacity_per_connection: u32, + /// Rate limit per minute. + pub rate_limit: Option, + /// Whether to trust proxy headers for rate limiting. + pub rate_limit_trust_proxy_headers: bool, + /// Whitelisted IPs for rate limiting. + pub rate_limit_whitelisted_ips: Vec, + /// CORS. + pub cors: Option>, + /// RPC methods to expose. + pub rpc_methods: RpcMethods, + /// Whether it's an optional listening address i.e, it's ignored if it fails to bind. + /// For example substrate tries to bind both ipv4 and ipv6 addresses but some platforms + /// may not support ipv6. + pub is_optional: bool, + /// Whether to retry with a random port if the provided port is already in use. + pub retry_random_port: bool, +} + +impl std::str::FromStr for RpcEndpoint { + type Err = String; + + fn from_str(s: &str) -> Result { + let mut listen_addr = None; + let mut max_connections = None; + let mut max_payload_in_mb = None; + let mut max_payload_out_mb = None; + let mut max_subscriptions_per_connection = None; + let mut max_buffer_capacity_per_connection = None; + let mut cors: Option> = None; + let mut rpc_methods = None; + let mut is_optional = None; + let mut disable_batch_requests = None; + let mut max_batch_request_len = None; + let mut rate_limit = None; + let mut rate_limit_trust_proxy_headers = None; + let mut rate_limit_whitelisted_ips = Vec::new(); + let mut retry_random_port = None; + + for input in s.split(',') { + let (key, val) = input.trim().split_once('=').ok_or_else(|| invalid_input(input))?; + let key = key.trim(); + let val = val.trim(); + + match key { + RPC_LISTEN_ADDR => { + if listen_addr.is_some() { + return Err(only_once_err(RPC_LISTEN_ADDR)); + } + let val: SocketAddr = + val.parse().map_err(|_| invalid_value(RPC_LISTEN_ADDR, &val))?; + listen_addr = Some(val); + }, + RPC_CORS => { + if val.is_empty() { + return Err(invalid_value(RPC_CORS, &val)); + } + + if let Some(cors) = cors.as_mut() { + cors.push(val.to_string()); + } else { + cors = Some(vec![val.to_string()]); + } + }, + RPC_MAX_CONNS => { + if max_connections.is_some() { + return Err(only_once_err(RPC_MAX_CONNS)); + } + + let val = val.parse().map_err(|_| invalid_value(RPC_MAX_CONNS, &val))?; + max_connections = Some(val); + }, + RPC_MAX_REQUEST_SIZE => { + if max_payload_in_mb.is_some() { + return Err(only_once_err(RPC_MAX_REQUEST_SIZE)); + } + + let val = + val.parse().map_err(|_| invalid_value(RPC_MAX_RESPONSE_SIZE, &val))?; + max_payload_in_mb = Some(val); + }, + RPC_MAX_RESPONSE_SIZE => { + if max_payload_out_mb.is_some() { + return Err(only_once_err(RPC_MAX_RESPONSE_SIZE)); + } + + let val = + val.parse().map_err(|_| invalid_value(RPC_MAX_RESPONSE_SIZE, &val))?; + max_payload_out_mb = Some(val); + }, + RPC_MAX_SUBS_PER_CONN => { + if max_subscriptions_per_connection.is_some() { + return Err(only_once_err(RPC_MAX_SUBS_PER_CONN)); + } + + let val = + val.parse().map_err(|_| invalid_value(RPC_MAX_SUBS_PER_CONN, &val))?; + max_subscriptions_per_connection = Some(val); + }, + RPC_MAX_BUF_CAP_PER_CONN => { + if max_buffer_capacity_per_connection.is_some() { + return Err(only_once_err(RPC_MAX_BUF_CAP_PER_CONN)); + } + + let val = + val.parse().map_err(|_| invalid_value(RPC_MAX_BUF_CAP_PER_CONN, &val))?; + max_buffer_capacity_per_connection = Some(val); + }, + RPC_RATE_LIMIT => { + if rate_limit.is_some() { + return Err(only_once_err("rate-limit")); + } + + let val = val.parse().map_err(|_| invalid_value(RPC_RATE_LIMIT, &val))?; + rate_limit = Some(val); + }, + RPC_RATE_LIMIT_TRUST_PROXY_HEADERS => { + if rate_limit_trust_proxy_headers.is_some() { + return Err(only_once_err(RPC_RATE_LIMIT_TRUST_PROXY_HEADERS)); + } + + let val = val + .parse() + .map_err(|_| invalid_value(RPC_RATE_LIMIT_TRUST_PROXY_HEADERS, &val))?; + rate_limit_trust_proxy_headers = Some(val); + }, + RPC_RATE_LIMIT_WHITELISTED_IPS => { + let ip: IpNetwork = val + .parse() + .map_err(|_| invalid_value(RPC_RATE_LIMIT_WHITELISTED_IPS, &val))?; + rate_limit_whitelisted_ips.push(ip); + }, + RPC_RETRY_RANDOM_PORT => { + if retry_random_port.is_some() { + return Err(only_once_err(RPC_RETRY_RANDOM_PORT)); + } + let val = + val.parse().map_err(|_| invalid_value(RPC_RETRY_RANDOM_PORT, &val))?; + retry_random_port = Some(val); + }, + RPC_METHODS => { + if rpc_methods.is_some() { + return Err(only_once_err("methods")); + } + let val = val.parse().map_err(|_| invalid_value(RPC_METHODS, &val))?; + rpc_methods = Some(val); + }, + RPC_OPTIONAL => { + if is_optional.is_some() { + return Err(only_once_err(RPC_OPTIONAL)); + } + + let val = val.parse().map_err(|_| invalid_value(RPC_OPTIONAL, &val))?; + is_optional = Some(val); + }, + RPC_DISABLE_BATCH => { + if disable_batch_requests.is_some() { + return Err(only_once_err(RPC_DISABLE_BATCH)); + } + + let val = val.parse().map_err(|_| invalid_value(RPC_DISABLE_BATCH, &val))?; + disable_batch_requests = Some(val); + }, + RPC_BATCH_LIMIT => { + if max_batch_request_len.is_some() { + return Err(only_once_err(RPC_BATCH_LIMIT)); + } + + let val = val.parse().map_err(|_| invalid_value(RPC_BATCH_LIMIT, &val))?; + max_batch_request_len = Some(val); + }, + _ => return Err(invalid_key(key)), + } + } + + let listen_addr = listen_addr.ok_or("`listen-addr` must be specified exactly once")?; + + let batch_config = match (disable_batch_requests, max_batch_request_len) { + (Some(true), Some(_)) => { + return Err(format!("`{RPC_BATCH_LIMIT}` and `{RPC_DISABLE_BATCH}` are mutually exclusive and can't be used together")); + }, + (Some(false), None) => RpcBatchRequestConfig::Disabled, + (None, Some(len)) => RpcBatchRequestConfig::Limit(len), + _ => RpcBatchRequestConfig::Unlimited, + }; + + Ok(Self { + listen_addr, + batch_config, + max_connections: max_connections.unwrap_or(RPC_DEFAULT_MAX_CONNECTIONS), + max_payload_in_mb: max_payload_in_mb.unwrap_or(RPC_DEFAULT_MAX_REQUEST_SIZE_MB), + max_payload_out_mb: max_payload_out_mb.unwrap_or(RPC_DEFAULT_MAX_RESPONSE_SIZE_MB), + cors, + max_buffer_capacity_per_connection: max_buffer_capacity_per_connection + .unwrap_or(RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN), + max_subscriptions_per_connection: max_subscriptions_per_connection + .unwrap_or(RPC_DEFAULT_MAX_SUBS_PER_CONN), + rpc_methods: rpc_methods.unwrap_or(RpcMethods::Auto), + rate_limit, + rate_limit_trust_proxy_headers: rate_limit_trust_proxy_headers.unwrap_or(false), + rate_limit_whitelisted_ips, + is_optional: is_optional.unwrap_or(false), + retry_random_port: retry_random_port.unwrap_or(false), + }) + } +} + +impl Into for RpcEndpoint { + fn into(self) -> sc_service::config::RpcEndpoint { + sc_service::config::RpcEndpoint { + batch_config: self.batch_config, + listen_addr: self.listen_addr, + max_buffer_capacity_per_connection: self.max_buffer_capacity_per_connection, + max_connections: self.max_connections, + max_payload_in_mb: self.max_payload_in_mb, + max_payload_out_mb: self.max_payload_out_mb, + max_subscriptions_per_connection: self.max_subscriptions_per_connection, + rpc_methods: self.rpc_methods.into(), + rate_limit: self.rate_limit, + rate_limit_trust_proxy_headers: self.rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: self.rate_limit_whitelisted_ips, + cors: self.cors, + retry_random_port: self.retry_random_port, + is_optional: self.is_optional, + } + } +} + +impl RpcEndpoint { + /// Returns whether the endpoint is globally exposed. + pub fn is_global(&self) -> bool { + let ip = IpNetwork::from(self.listen_addr.ip()); + ip.is_global() + } +} + +fn only_once_err(reason: &str) -> String { + format!("`{reason}` is only allowed be specified once") +} + +fn invalid_input(input: &str) -> String { + format!("`{input}`, expects: `key=value`") +} + +fn invalid_value(key: &str, value: &str) -> String { + format!("value=`{value}` key=`{key}`") +} + +fn invalid_key(key: &str) -> String { + format!("unknown key=`{key}`, see `--help` for available options") +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{num::NonZeroU32, str::FromStr}; + + #[test] + fn parse_rpc_endpoint_works() { + assert!(RpcEndpoint::from_str("listen-addr=127.0.0.1:9944").is_ok()); + assert!(RpcEndpoint::from_str("listen-addr=[::1]:9944").is_ok()); + assert!(RpcEndpoint::from_str("listen-addr=127.0.0.1:9944,methods=auto").is_ok()); + assert!(RpcEndpoint::from_str("listen-addr=[::1]:9944,methods=auto").is_ok()); + assert!(RpcEndpoint::from_str( + "listen-addr=127.0.0.1:9944,methods=auto,cors=*,optional=true" + ) + .is_ok()); + + assert!(RpcEndpoint::from_str("listen-addrs=127.0.0.1:9944,foo=*").is_err()); + assert!(RpcEndpoint::from_str("listen-addrs=127.0.0.1:9944,cors=").is_err()); + } + + #[test] + fn parse_rpc_endpoint_all() { + let endpoint = RpcEndpoint::from_str( + "listen-addr=127.0.0.1:9944,methods=unsafe,cors=*,optional=true,retry-random-port=true,rate-limit=99,\ + max-batch-request-len=100,rate-limit-trust-proxy-headers=true,max-connections=33,max-request-size=4,\ + max-response-size=3,max-subscriptions-per-connection=7,max-buffer-capacity-per-connection=8,\ + rate-limit-whitelisted-ips=192.168.1.0/24,rate-limit-whitelisted-ips=ff01::0/32" + ).unwrap(); + assert_eq!(endpoint.listen_addr, ([127, 0, 0, 1], 9944).into()); + assert_eq!(endpoint.rpc_methods, RpcMethods::Unsafe); + assert_eq!(endpoint.cors, Some(vec!["*".to_string()])); + assert_eq!(endpoint.is_optional, true); + assert_eq!(endpoint.retry_random_port, true); + assert_eq!(endpoint.rate_limit, Some(NonZeroU32::new(99).unwrap())); + assert!(matches!(endpoint.batch_config, RpcBatchRequestConfig::Limit(l) if l == 100)); + assert_eq!(endpoint.rate_limit_trust_proxy_headers, true); + assert_eq!( + endpoint.rate_limit_whitelisted_ips, + vec![ + IpNetwork::V4("192.168.1.0/24".parse().unwrap()), + IpNetwork::V6("ff01::0/32".parse().unwrap()) + ] + ); + assert_eq!(endpoint.max_connections, 33); + assert_eq!(endpoint.max_payload_in_mb, 4); + assert_eq!(endpoint.max_payload_out_mb, 3); + assert_eq!(endpoint.max_subscriptions_per_connection, 7); + assert_eq!(endpoint.max_buffer_capacity_per_connection, 8); + } + + #[test] + fn parse_rpc_endpoint_multiple_cors() { + let addr = RpcEndpoint::from_str( + "listen-addr=127.0.0.1:9944,methods=auto,cors=https://polkadot.js.org,cors=*,cors=localhost:*", + ) + .unwrap(); + + assert_eq!( + addr.cors, + Some(vec![ + "https://polkadot.js.org".to_string(), + "*".to_string(), + "localhost:*".to_string() + ]) + ); + } + + #[test] + fn parse_rpc_endpoint_whitespaces() { + let addr = RpcEndpoint::from_str( + " listen-addr = 127.0.0.1:9944, methods = auto, optional = true ", + ) + .unwrap(); + assert_eq!(addr.rpc_methods, RpcMethods::Auto); + assert_eq!(addr.is_optional, true); + } + + #[test] + fn parse_rpc_endpoint_batch_options_mutually_exclusive() { + assert!(RpcEndpoint::from_str( + "listen-addr = 127.0.0.1:9944,disable-batch-requests=true,max-batch-request-len=100", + ) + .is_err()); + } +} diff --git a/substrate/client/consensus/babe/rpc/src/lib.rs b/substrate/client/consensus/babe/rpc/src/lib.rs index a3e811baecff..338d71a43256 100644 --- a/substrate/client/consensus/babe/rpc/src/lib.rs +++ b/substrate/client/consensus/babe/rpc/src/lib.rs @@ -25,12 +25,13 @@ use jsonrpsee::{ core::async_trait, proc_macros::rpc, types::{ErrorObject, ErrorObjectOwned}, + Extensions, }; use serde::{Deserialize, Serialize}; use sc_consensus_babe::{authorship, BabeWorkerHandle}; use sc_consensus_epochs::Epoch as EpochT; -use sc_rpc_api::{DenyUnsafe, UnsafeRpcError}; +use sc_rpc_api::{check_if_safe, UnsafeRpcError}; use sp_api::ProvideRuntimeApi; use sp_application_crypto::AppCrypto; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; @@ -47,7 +48,7 @@ const BABE_ERROR: i32 = 9000; pub trait BabeApi { /// Returns data about which slots (primary or secondary) can be claimed in the current epoch /// with the keys in the keystore. - #[method(name = "babe_epochAuthorship")] + #[method(name = "babe_epochAuthorship", with_extensions)] async fn epoch_authorship(&self) -> Result, Error>; } @@ -61,8 +62,6 @@ pub struct Babe { keystore: KeystorePtr, /// The SelectChain strategy select_chain: SC, - /// Whether to deny unsafe calls - deny_unsafe: DenyUnsafe, } impl Babe { @@ -72,9 +71,8 @@ impl Babe { babe_worker_handle: BabeWorkerHandle, keystore: KeystorePtr, select_chain: SC, - deny_unsafe: DenyUnsafe, ) -> Self { - Self { client, babe_worker_handle, keystore, select_chain, deny_unsafe } + Self { client, babe_worker_handle, keystore, select_chain } } } @@ -89,8 +87,11 @@ where C::Api: BabeRuntimeApi, SC: SelectChain + Clone + 'static, { - async fn epoch_authorship(&self) -> Result, Error> { - self.deny_unsafe.check_if_safe()?; + async fn epoch_authorship( + &self, + ext: &Extensions, + ) -> Result, Error> { + check_if_safe(ext)?; let best_header = self.select_chain.best_chain().map_err(Error::SelectChain).await?; @@ -193,6 +194,7 @@ impl From for ErrorObjectOwned { mod tests { use super::*; use sc_consensus_babe::ImportQueueParams; + use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::{OffchainTransactionPoolFactory, RejectAllTxPool}; use sp_consensus_babe::inherents::InherentDataProvider; use sp_core::{crypto::key_types::BABE, testing::TaskExecutor}; @@ -211,9 +213,8 @@ mod tests { keystore.into() } - fn test_babe_rpc_module( - deny_unsafe: DenyUnsafe, - ) -> Babe> { + fn test_babe_rpc_module() -> Babe> + { let builder = TestClientBuilder::new(); let (client, longest_chain) = builder.build_with_longest_chain(); let client = Arc::new(client); @@ -248,29 +249,31 @@ mod tests { }) .unwrap(); - Babe::new(client.clone(), babe_worker_handle, keystore, longest_chain, deny_unsafe) + Babe::new(client.clone(), babe_worker_handle, keystore, longest_chain) } #[tokio::test] async fn epoch_authorship_works() { - let babe_rpc = test_babe_rpc_module(DenyUnsafe::No); - let api = babe_rpc.into_rpc(); + let babe_rpc = test_babe_rpc_module(); + let mut api = babe_rpc.into_rpc(); + api.extensions_mut().insert(DenyUnsafe::No); - let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params": [],"id":1}"#; + let request = r#"{"jsonrpc":"2.0","id":1,"method":"babe_epochAuthorship","params":[]}"#; let (response, _) = api.raw_json_request(request, 1).await.unwrap(); - let expected = r#"{"jsonrpc":"2.0","result":{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY":{"primary":[0],"secondary":[],"secondary_vrf":[1,2,4]}},"id":1}"#; + let expected = r#"{"jsonrpc":"2.0","id":1,"result":{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY":{"primary":[0],"secondary":[],"secondary_vrf":[1,2,4]}}}"#; assert_eq!(response, expected); } #[tokio::test] async fn epoch_authorship_is_unsafe() { - let babe_rpc = test_babe_rpc_module(DenyUnsafe::Yes); - let api = babe_rpc.into_rpc(); + let babe_rpc = test_babe_rpc_module(); + let mut api = babe_rpc.into_rpc(); + api.extensions_mut().insert(DenyUnsafe::Yes); let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params":[],"id":1}"#; let (response, _) = api.raw_json_request(request, 1).await.unwrap(); - let expected = r#"{"jsonrpc":"2.0","error":{"code":-32601,"message":"RPC call is unsafe to be called externally"},"id":1}"#; + let expected = r#"{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"RPC call is unsafe to be called externally"}}"#; assert_eq!(response, expected); } diff --git a/substrate/client/consensus/beefy/rpc/src/lib.rs b/substrate/client/consensus/beefy/rpc/src/lib.rs index 83477d223dd2..ab58f6866275 100644 --- a/substrate/client/consensus/beefy/rpc/src/lib.rs +++ b/substrate/client/consensus/beefy/rpc/src/lib.rs @@ -201,7 +201,7 @@ mod tests { async fn uninitialized_rpc_handler() { let (rpc, _) = setup_io_handler(); let request = r#"{"jsonrpc":"2.0","method":"beefy_getFinalizedHead","params":[],"id":1}"#; - let expected_response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"BEEFY RPC endpoint not ready"},"id":1}"#; + let expected_response = r#"{"jsonrpc":"2.0","id":1,"error":{"code":1,"message":"BEEFY RPC endpoint not ready"}}"#; let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap(); assert_eq!(expected_response, response); @@ -220,13 +220,13 @@ mod tests { let request = r#"{"jsonrpc":"2.0","method":"beefy_getFinalizedHead","params":[],"id":1}"#; let expected = "{\ \"jsonrpc\":\"2.0\",\ - \"result\":\"0x2f0039e93a27221fcf657fb877a1d4f60307106113e885096cb44a461cd0afbf\",\ - \"id\":1\ + \"id\":1,\ + \"result\":\"0x2f0039e93a27221fcf657fb877a1d4f60307106113e885096cb44a461cd0afbf\"\ }"; - let not_ready = "{\ + let not_ready: &str = "{\ \"jsonrpc\":\"2.0\",\ - \"error\":{\"code\":1,\"message\":\"BEEFY RPC endpoint not ready\"},\ - \"id\":1\ + \"id\":1,\ + \"error\":{\"code\":1,\"message\":\"BEEFY RPC endpoint not ready\"}\ }"; let deadline = std::time::Instant::now() + std::time::Duration::from_secs(2); @@ -262,7 +262,7 @@ mod tests { ) .await .unwrap(); - let expected = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; + let expected = r#"{"jsonrpc":"2.0","id":1,"result":false}"#; assert_eq!(response, expected); } diff --git a/substrate/client/consensus/grandpa/rpc/src/lib.rs b/substrate/client/consensus/grandpa/rpc/src/lib.rs index a41b14299089..99f98b81261a 100644 --- a/substrate/client/consensus/grandpa/rpc/src/lib.rs +++ b/substrate/client/consensus/grandpa/rpc/src/lib.rs @@ -275,7 +275,7 @@ mod tests { #[tokio::test] async fn uninitialized_rpc_handler() { let (rpc, _) = setup_io_handler(EmptyVoterState); - let expected_response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"GRANDPA RPC endpoint not ready"},"id":0}"#.to_string(); + let expected_response = r#"{"jsonrpc":"2.0","id":0,"error":{"code":1,"message":"GRANDPA RPC endpoint not ready"}}"#.to_string(); let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#; let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap(); @@ -285,7 +285,7 @@ mod tests { #[tokio::test] async fn working_rpc_handler() { let (rpc, _) = setup_io_handler(TestVoterState); - let expected_response = "{\"jsonrpc\":\"2.0\",\"result\":{\ + let expected_response = "{\"jsonrpc\":\"2.0\",\"id\":0,\"result\":{\ \"setId\":1,\ \"best\":{\ \"round\":2,\"totalWeight\":100,\"thresholdWeight\":67,\ @@ -297,7 +297,7 @@ mod tests { \"prevotes\":{\"currentWeight\":100,\"missing\":[]},\ \"precommits\":{\"currentWeight\":100,\"missing\":[]}\ }]\ - },\"id\":0}".to_string(); + }}".to_string(); let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#; let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap(); @@ -321,7 +321,7 @@ mod tests { ) .await .unwrap(); - let expected = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; + let expected = r#"{"jsonrpc":"2.0","id":1,"result":false}"#; assert_eq!(response, expected); } diff --git a/substrate/client/rpc-api/src/author/mod.rs b/substrate/client/rpc-api/src/author/mod.rs index cfc56f4130ab..c39d6c68355a 100644 --- a/substrate/client/rpc-api/src/author/mod.rs +++ b/substrate/client/rpc-api/src/author/mod.rs @@ -34,11 +34,11 @@ pub trait AuthorApi { async fn submit_extrinsic(&self, extrinsic: Bytes) -> Result; /// Insert a key into the keystore. - #[method(name = "author_insertKey")] + #[method(name = "author_insertKey", with_extensions)] fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> Result<(), Error>; /// Generate new session keys and returns the corresponding public keys. - #[method(name = "author_rotateKeys")] + #[method(name = "author_rotateKeys", with_extensions)] fn rotate_keys(&self) -> Result; /// Checks if the keystore has private keys for the given session public keys. @@ -46,13 +46,13 @@ pub trait AuthorApi { /// `session_keys` is the SCALE encoded session keys object from the runtime. /// /// Returns `true` iff all private keys could be found. - #[method(name = "author_hasSessionKeys")] + #[method(name = "author_hasSessionKeys", with_extensions)] fn has_session_keys(&self, session_keys: Bytes) -> Result; /// Checks if the keystore has private keys for the given public key and key type. /// /// Returns `true` if a private key could be found. - #[method(name = "author_hasKey")] + #[method(name = "author_hasKey", with_extensions)] fn has_key(&self, public_key: Bytes, key_type: String) -> Result; /// Returns all pending extrinsics, potentially grouped by sender. @@ -60,7 +60,7 @@ pub trait AuthorApi { fn pending_extrinsics(&self) -> Result, Error>; /// Remove given extrinsic from the pool and temporarily ban it to prevent reimporting. - #[method(name = "author_removeExtrinsic")] + #[method(name = "author_removeExtrinsic", with_extensions)] fn remove_extrinsic( &self, bytes_or_hash: Vec>, diff --git a/substrate/client/rpc-api/src/dev/mod.rs b/substrate/client/rpc-api/src/dev/mod.rs index 5bee6df73ba9..5c6208ff5d50 100644 --- a/substrate/client/rpc-api/src/dev/mod.rs +++ b/substrate/client/rpc-api/src/dev/mod.rs @@ -59,6 +59,6 @@ pub trait DevApi { /// This function requires the specified block and its parent to be available /// at the queried node. If either the specified block or the parent is pruned, /// this function will return `None`. - #[method(name = "dev_getBlockStats")] + #[method(name = "dev_getBlockStats", with_extensions)] fn block_stats(&self, block_hash: Hash) -> Result, Error>; } diff --git a/substrate/client/rpc-api/src/lib.rs b/substrate/client/rpc-api/src/lib.rs index 451ebdf7fc00..cc580fc0e7ca 100644 --- a/substrate/client/rpc-api/src/lib.rs +++ b/substrate/client/rpc-api/src/lib.rs @@ -25,7 +25,7 @@ mod error; mod policy; -pub use policy::{DenyUnsafe, UnsafeRpcError}; +pub use policy::{check_if_safe, DenyUnsafe, UnsafeRpcError}; pub mod author; pub mod chain; diff --git a/substrate/client/rpc-api/src/offchain/mod.rs b/substrate/client/rpc-api/src/offchain/mod.rs index 469e22d2b3fa..4dd5b066d49f 100644 --- a/substrate/client/rpc-api/src/offchain/mod.rs +++ b/substrate/client/rpc-api/src/offchain/mod.rs @@ -28,10 +28,10 @@ use sp_core::{offchain::StorageKind, Bytes}; #[rpc(client, server)] pub trait OffchainApi { /// Set offchain local storage under given key and prefix. - #[method(name = "offchain_localStorageSet")] + #[method(name = "offchain_localStorageSet", with_extensions)] fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<(), Error>; /// Get offchain local storage under given key and prefix. - #[method(name = "offchain_localStorageGet")] + #[method(name = "offchain_localStorageGet", with_extensions)] fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result, Error>; } diff --git a/substrate/client/rpc-api/src/policy.rs b/substrate/client/rpc-api/src/policy.rs index c0847de89d2c..7e0657db8d17 100644 --- a/substrate/client/rpc-api/src/policy.rs +++ b/substrate/client/rpc-api/src/policy.rs @@ -23,6 +23,15 @@ use jsonrpsee::types::{error::ErrorCode, ErrorObject, ErrorObjectOwned}; +/// Checks if the RPC call is safe to be called externally. +pub fn check_if_safe(ext: &jsonrpsee::Extensions) -> Result<(), UnsafeRpcError> { + match ext.get::().map(|deny_unsafe| deny_unsafe.check_if_safe()) { + Some(Ok(())) => Ok(()), + Some(Err(e)) => Err(e), + None => unreachable!("DenyUnsafe extension is always set by the substrate rpc server; qed"), + } +} + /// Signifies whether a potentially unsafe RPC should be denied. #[derive(Clone, Copy, Debug)] pub enum DenyUnsafe { diff --git a/substrate/client/rpc-api/src/state/mod.rs b/substrate/client/rpc-api/src/state/mod.rs index e38e383c4c15..ee62387b6bd4 100644 --- a/substrate/client/rpc-api/src/state/mod.rs +++ b/substrate/client/rpc-api/src/state/mod.rs @@ -48,7 +48,7 @@ pub trait StateApi { ) -> Result, Error>; /// Returns the keys with prefix, leave empty to get all the keys - #[method(name = "state_getPairs", blocking)] + #[method(name = "state_getPairs", blocking, with_extensions)] fn storage_pairs( &self, prefix: StorageKey, @@ -76,7 +76,7 @@ pub trait StateApi { fn storage_hash(&self, key: StorageKey, hash: Option) -> Result, Error>; /// Returns the size of a storage entry at a block's state. - #[method(name = "state_getStorageSize", aliases = ["state_getStorageSizeAt"])] + #[method(name = "state_getStorageSize", aliases = ["state_getStorageSizeAt"], with_extensions)] async fn storage_size(&self, key: StorageKey, hash: Option) -> Result, Error>; @@ -95,7 +95,7 @@ pub trait StateApi { /// Subsequent values in the vector represent changes to the previous state (diffs). /// WARNING: The time complexity of this query is O(|keys|*dist(block, hash)), and the /// memory complexity is O(dist(block, hash)) -- use with caution. - #[method(name = "state_queryStorage", blocking)] + #[method(name = "state_queryStorage", blocking, with_extensions)] fn query_storage( &self, keys: Vec, @@ -136,6 +136,7 @@ pub trait StateApi { name = "state_subscribeStorage" => "state_storage", unsubscribe = "state_unsubscribeStorage", item = StorageChangeSet, + with_extensions, )] fn subscribe_storage(&self, keys: Option>); @@ -291,7 +292,7 @@ pub trait StateApi { /// /// If you are having issues with maximum payload size you can use the flag /// `-ltracing=trace` to get some logging during tracing. - #[method(name = "state_traceBlock", blocking)] + #[method(name = "state_traceBlock", blocking, with_extensions)] fn trace_block( &self, block: Hash, diff --git a/substrate/client/rpc-api/src/statement/mod.rs b/substrate/client/rpc-api/src/statement/mod.rs index 39ec52cbea01..eee58f506e16 100644 --- a/substrate/client/rpc-api/src/statement/mod.rs +++ b/substrate/client/rpc-api/src/statement/mod.rs @@ -27,7 +27,7 @@ pub mod error; #[rpc(client, server)] pub trait StatementApi { /// Return all statements, SCALE-encoded. - #[method(name = "statement_dump")] + #[method(name = "statement_dump", with_extensions)] fn dump(&self) -> RpcResult>; /// Return the data of all known statements which include all topics and have no `DecryptionKey` diff --git a/substrate/client/rpc-api/src/system/mod.rs b/substrate/client/rpc-api/src/system/mod.rs index c38fa8f3d817..2d6d48e1ddb4 100644 --- a/substrate/client/rpc-api/src/system/mod.rs +++ b/substrate/client/rpc-api/src/system/mod.rs @@ -69,7 +69,7 @@ pub trait SystemApi { async fn system_local_listen_addresses(&self) -> Result, Error>; /// Returns currently connected peers - #[method(name = "system_peers")] + #[method(name = "system_peers", with_extensions)] async fn system_peers(&self) -> Result>, Error>; /// Returns current state of the network. @@ -78,7 +78,7 @@ pub trait SystemApi { /// as its format might change at any time. // TODO: the future of this call is uncertain: https://github.com/paritytech/substrate/issues/1890 // https://github.com/paritytech/substrate/issues/5541 - #[method(name = "system_unstable_networkState")] + #[method(name = "system_unstable_networkState", with_extensions)] async fn system_network_state(&self) -> Result; /// Adds a reserved peer. Returns the empty string or an error. The string @@ -86,12 +86,12 @@ pub trait SystemApi { /// /// `/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV` /// is an example of a valid, passing multiaddr with PeerId attached. - #[method(name = "system_addReservedPeer")] + #[method(name = "system_addReservedPeer", with_extensions)] async fn system_add_reserved_peer(&self, peer: String) -> Result<(), Error>; /// Remove a reserved peer. Returns the empty string or an error. The string /// should encode only the PeerId e.g. `QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV`. - #[method(name = "system_removeReservedPeer")] + #[method(name = "system_removeReservedPeer", with_extensions)] async fn system_remove_reserved_peer(&self, peer_id: String) -> Result<(), Error>; /// Returns the list of reserved peers @@ -112,10 +112,10 @@ pub trait SystemApi { /// The syntax is identical to the CLI `=`: /// /// `sync=debug,state=trace` - #[method(name = "system_addLogFilter")] + #[method(name = "system_addLogFilter", with_extensions)] fn system_add_log_filter(&self, directives: String) -> Result<(), Error>; /// Resets the log filter to Substrate defaults - #[method(name = "system_resetLogFilter")] + #[method(name = "system_resetLogFilter", with_extensions)] fn system_reset_log_filter(&self) -> Result<(), Error>; } diff --git a/substrate/client/rpc-servers/Cargo.toml b/substrate/client/rpc-servers/Cargo.toml index 7e3d3bf55b74..b39c3fdda67c 100644 --- a/substrate/client/rpc-servers/Cargo.toml +++ b/substrate/client/rpc-servers/Cargo.toml @@ -16,21 +16,20 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] +dyn-clone = { workspace = true } forwarded-header-value = { workspace = true } futures = { workspace = true } governor = { workspace = true } http = { workspace = true } http-body-util = { workspace = true } +hyper = { workspace = true } ip_network = { workspace = true } jsonrpsee = { features = ["server"], workspace = true } log = { workspace = true, default-features = true } prometheus-endpoint = { workspace = true, default-features = true } +sc-rpc-api = { workspace = true } serde = { workspace = true } serde_json = { workspace = true, default-features = true } tokio = { features = ["parking_lot"], workspace = true, default-features = true } tower = { workspace = true, features = ["util"] } tower-http = { workspace = true, features = ["cors"] } - -# Dependencies outside the polkadot-sdk workspace -# which requires hyper v1 -hyper = "1.3" diff --git a/substrate/client/rpc-servers/src/lib.rs b/substrate/client/rpc-servers/src/lib.rs index 0bae16b113df..ca74c2371c25 100644 --- a/substrate/client/rpc-servers/src/lib.rs +++ b/substrate/client/rpc-servers/src/lib.rs @@ -23,246 +23,255 @@ pub mod middleware; pub mod utils; -use std::{error::Error as StdError, net::SocketAddr, num::NonZeroU32, sync::Arc, time::Duration}; +use std::{error::Error as StdError, time::Duration}; use jsonrpsee::{ core::BoxError, - server::{ - serve_with_graceful_shutdown, stop_channel, ws, PingConfig, StopHandle, TowerServiceBuilder, - }, + server::{serve_with_graceful_shutdown, stop_channel, ws, PingConfig, StopHandle}, Methods, RpcModule, }; use middleware::NodeHealthProxyLayer; -use tokio::net::TcpListener; use tower::Service; -use utils::{build_rpc_api, format_cors, get_proxy_ip, host_filtering, try_into_cors}; +use utils::{ + build_rpc_api, deny_unsafe, format_listen_addrs, get_proxy_ip, ListenAddrError, RpcSettings, +}; pub use ip_network::IpNetwork; pub use jsonrpsee::{ - core::{ - id_providers::{RandomIntegerIdProvider, RandomStringIdProvider}, - traits::IdProvider, - }, + core::id_providers::{RandomIntegerIdProvider, RandomStringIdProvider}, server::{middleware::rpc::RpcServiceBuilder, BatchRequestConfig}, }; pub use middleware::{Metrics, MiddlewareLayer, RpcMetrics}; +pub use utils::{RpcEndpoint, RpcMethods}; const MEGABYTE: u32 = 1024 * 1024; /// Type alias for the JSON-RPC server. pub type Server = jsonrpsee::server::ServerHandle; +/// Trait for providing subscription IDs that can be cloned. +pub trait SubscriptionIdProvider: + jsonrpsee::core::traits::IdProvider + dyn_clone::DynClone +{ +} + +dyn_clone::clone_trait_object!(SubscriptionIdProvider); + /// RPC server configuration. #[derive(Debug)] -pub struct Config<'a, M: Send + Sync + 'static> { - /// Socket addresses. - pub addrs: [SocketAddr; 2], - /// CORS. - pub cors: Option<&'a Vec>, - /// Maximum connections. - pub max_connections: u32, - /// Maximum subscriptions per connection. - pub max_subs_per_conn: u32, - /// Maximum rpc request payload size. - pub max_payload_in_mb: u32, - /// Maximum rpc response payload size. - pub max_payload_out_mb: u32, +pub struct Config { + /// RPC interfaces to start. + pub endpoints: Vec, /// Metrics. pub metrics: Option, - /// Message buffer size - pub message_buffer_capacity: u32, /// RPC API. pub rpc_api: RpcModule, /// Subscription ID provider. - pub id_provider: Option>, + pub id_provider: Option>, /// Tokio runtime handle. pub tokio_handle: tokio::runtime::Handle, - /// Batch request config. - pub batch_config: BatchRequestConfig, - /// Rate limit calls per minute. - pub rate_limit: Option, - /// Disable rate limit for certain ips. - pub rate_limit_whitelisted_ips: Vec, - /// Trust proxy headers for rate limiting. - pub rate_limit_trust_proxy_headers: bool, } #[derive(Debug, Clone)] -struct PerConnection { +struct PerConnection { methods: Methods, stop_handle: StopHandle, metrics: Option, tokio_handle: tokio::runtime::Handle, - service_builder: TowerServiceBuilder, - rate_limit_whitelisted_ips: Arc>, } /// Start RPC server listening on given address. -pub async fn start_server( - config: Config<'_, M>, -) -> Result> +pub async fn start_server(config: Config) -> Result> where M: Send + Sync, { - let Config { - addrs, - batch_config, - cors, - max_payload_in_mb, - max_payload_out_mb, - max_connections, - max_subs_per_conn, - metrics, - message_buffer_capacity, - id_provider, - tokio_handle, - rpc_api, - rate_limit, - rate_limit_whitelisted_ips, - rate_limit_trust_proxy_headers, - } = config; - - let listener = TcpListener::bind(addrs.as_slice()).await?; - let local_addr = listener.local_addr().ok(); - let host_filter = host_filtering(cors.is_some(), local_addr); - - let http_middleware = tower::ServiceBuilder::new() - .option_layer(host_filter) - // Proxy `GET /health, /health/readiness` requests to the internal `system_health` method. - .layer(NodeHealthProxyLayer::default()) - .layer(try_into_cors(cors)?); - - let mut builder = jsonrpsee::server::Server::builder() - .max_request_body_size(max_payload_in_mb.saturating_mul(MEGABYTE)) - .max_response_body_size(max_payload_out_mb.saturating_mul(MEGABYTE)) - .max_connections(max_connections) - .max_subscriptions_per_connection(max_subs_per_conn) - .enable_ws_ping( - PingConfig::new() - .ping_interval(Duration::from_secs(30)) - .inactive_limit(Duration::from_secs(60)) - .max_failures(3), - ) - .set_http_middleware(http_middleware) - .set_message_buffer_capacity(message_buffer_capacity) - .set_batch_request_config(batch_config) - .custom_tokio_runtime(tokio_handle.clone()); - - if let Some(provider) = id_provider { - builder = builder.set_id_provider(provider); - } else { - builder = builder.set_id_provider(RandomStringIdProvider::new(16)); - }; + let Config { endpoints, metrics, tokio_handle, rpc_api, id_provider } = config; let (stop_handle, server_handle) = stop_channel(); let cfg = PerConnection { methods: build_rpc_api(rpc_api).into(), - service_builder: builder.to_service_builder(), metrics, tokio_handle: tokio_handle.clone(), stop_handle, - rate_limit_whitelisted_ips: Arc::new(rate_limit_whitelisted_ips), }; - tokio_handle.spawn(async move { - loop { - let (sock, remote_addr) = tokio::select! { - res = listener.accept() => { - match res { - Ok(s) => s, - Err(e) => { - log::debug!(target: "rpc", "Failed to accept ipv4 connection: {:?}", e); - continue; + let mut local_addrs = Vec::new(); + + for endpoint in endpoints { + let allowed_to_fail = endpoint.is_optional; + let local_addr = endpoint.listen_addr; + + let mut listener = match endpoint.bind().await { + Ok(l) => l, + Err(e) if allowed_to_fail => { + log::debug!(target: "rpc", "JSON-RPC server failed to bind optional address: {:?}, error: {:?}", local_addr, e); + continue; + }, + Err(e) => return Err(e), + }; + let local_addr = listener.local_addr(); + local_addrs.push(local_addr); + let cfg = cfg.clone(); + + let mut id_provider2 = id_provider.clone(); + + tokio_handle.spawn(async move { + loop { + let (sock, remote_addr, rpc_cfg) = tokio::select! { + res = listener.accept() => { + match res { + Ok(s) => s, + Err(e) => { + log::debug!(target: "rpc", "Failed to accept connection: {:?}", e); + continue; + } } } - } - _ = cfg.stop_handle.clone().shutdown() => break, - }; - - let ip = remote_addr.ip(); - let cfg2 = cfg.clone(); - let svc = tower::service_fn(move |req: http::Request| { - let PerConnection { - methods, - service_builder, - metrics, - tokio_handle, - stop_handle, - rate_limit_whitelisted_ips, - } = cfg2.clone(); - - let proxy_ip = - if rate_limit_trust_proxy_headers { get_proxy_ip(&req) } else { None }; - - let rate_limit_cfg = if rate_limit_whitelisted_ips - .iter() - .any(|ips| ips.contains(proxy_ip.unwrap_or(ip))) - { - log::debug!(target: "rpc", "ip={ip}, proxy_ip={:?} is trusted, disabling rate-limit", proxy_ip); - None - } else { - if !rate_limit_whitelisted_ips.is_empty() { - log::debug!(target: "rpc", "ip={ip}, proxy_ip={:?} is not trusted, rate-limit enabled", proxy_ip); - } - rate_limit + _ = cfg.stop_handle.clone().shutdown() => break, }; - let is_websocket = ws::is_upgrade_request(&req); - let transport_label = if is_websocket { "ws" } else { "http" }; - - let middleware_layer = match (metrics, rate_limit_cfg) { - (None, None) => None, - (Some(metrics), None) => Some( - MiddlewareLayer::new().with_metrics(Metrics::new(metrics, transport_label)), - ), - (None, Some(rate_limit)) => - Some(MiddlewareLayer::new().with_rate_limit_per_minute(rate_limit)), - (Some(metrics), Some(rate_limit)) => Some( - MiddlewareLayer::new() - .with_metrics(Metrics::new(metrics, transport_label)) - .with_rate_limit_per_minute(rate_limit), - ), + let RpcSettings { + batch_config, + max_connections, + max_payload_in_mb, + max_payload_out_mb, + max_buffer_capacity_per_connection, + max_subscriptions_per_connection, + rpc_methods, + rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips, + host_filter, + cors, + rate_limit, + } = rpc_cfg; + + let http_middleware = tower::ServiceBuilder::new() + .option_layer(host_filter) + // Proxy `GET /health, /health/readiness` requests to the internal + // `system_health` method. + .layer(NodeHealthProxyLayer::default()) + .layer(cors); + + let mut builder = jsonrpsee::server::Server::builder() + .max_request_body_size(max_payload_in_mb.saturating_mul(MEGABYTE)) + .max_response_body_size(max_payload_out_mb.saturating_mul(MEGABYTE)) + .max_connections(max_connections) + .max_subscriptions_per_connection(max_subscriptions_per_connection) + .enable_ws_ping( + PingConfig::new() + .ping_interval(Duration::from_secs(30)) + .inactive_limit(Duration::from_secs(60)) + .max_failures(3), + ) + .set_http_middleware(http_middleware) + .set_message_buffer_capacity(max_buffer_capacity_per_connection) + .set_batch_request_config(batch_config) + .custom_tokio_runtime(cfg.tokio_handle.clone()) + .set_id_provider(RandomStringIdProvider::new(16)); + + if let Some(provider) = id_provider2.take() { + builder = builder.set_id_provider(provider); + } else { + builder = builder.set_id_provider(RandomStringIdProvider::new(16)); }; - let rpc_middleware = RpcServiceBuilder::new() - .rpc_logger(1024) - .option_layer(middleware_layer.clone()); - let mut svc = - service_builder.set_rpc_middleware(rpc_middleware).build(methods, stop_handle); - - async move { - if is_websocket { - let on_disconnect = svc.on_session_closed(); - - // Spawn a task to handle when the connection is closed. - tokio_handle.spawn(async move { - let now = std::time::Instant::now(); - middleware_layer.as_ref().map(|m| m.ws_connect()); - on_disconnect.await; - middleware_layer.as_ref().map(|m| m.ws_disconnect(now)); - }); - } - - // https://github.com/rust-lang/rust/issues/102211 the error type can't be inferred - // to be `Box` so we need to convert it to - // a concrete type as workaround. - svc.call(req).await.map_err(|e| BoxError::from(e)) - } - }); - - cfg.tokio_handle.spawn(serve_with_graceful_shutdown( - sock, - svc, - cfg.stop_handle.clone().shutdown(), - )); - } - }); - - log::info!( - "Running JSON-RPC server: addr={}, allowed origins={}", - local_addr.map_or_else(|| "unknown".to_string(), |a| a.to_string()), - format_cors(cors) - ); + let service_builder = builder.to_service_builder(); + let deny_unsafe = deny_unsafe(&local_addr, &rpc_methods); + + let ip = remote_addr.ip(); + let cfg2 = cfg.clone(); + let service_builder2 = service_builder.clone(); + + let svc = + tower::service_fn(move |mut req: http::Request| { + req.extensions_mut().insert(deny_unsafe); + + let PerConnection { methods, metrics, tokio_handle, stop_handle } = + cfg2.clone(); + let service_builder = service_builder2.clone(); + + let proxy_ip = + if rate_limit_trust_proxy_headers { get_proxy_ip(&req) } else { None }; + + let rate_limit_cfg = if rate_limit_whitelisted_ips + .iter() + .any(|ips| ips.contains(proxy_ip.unwrap_or(ip))) + { + log::debug!(target: "rpc", "ip={ip}, proxy_ip={:?} is trusted, disabling rate-limit", proxy_ip); + None + } else { + if !rate_limit_whitelisted_ips.is_empty() { + log::debug!(target: "rpc", "ip={ip}, proxy_ip={:?} is not trusted, rate-limit enabled", proxy_ip); + } + rate_limit + }; + + let is_websocket = ws::is_upgrade_request(&req); + let transport_label = if is_websocket { "ws" } else { "http" }; + + let middleware_layer = match (metrics, rate_limit_cfg) { + (None, None) => None, + (Some(metrics), None) => Some( + MiddlewareLayer::new() + .with_metrics(Metrics::new(metrics, transport_label)), + ), + (None, Some(rate_limit)) => + Some(MiddlewareLayer::new().with_rate_limit_per_minute(rate_limit)), + (Some(metrics), Some(rate_limit)) => Some( + MiddlewareLayer::new() + .with_metrics(Metrics::new(metrics, transport_label)) + .with_rate_limit_per_minute(rate_limit), + ), + }; + + let rpc_middleware = + RpcServiceBuilder::new().option_layer(middleware_layer.clone()); + let mut svc = service_builder + .set_rpc_middleware(rpc_middleware) + .build(methods, stop_handle); + + async move { + if is_websocket { + let on_disconnect = svc.on_session_closed(); + + // Spawn a task to handle when the connection is closed. + tokio_handle.spawn(async move { + let now = std::time::Instant::now(); + middleware_layer.as_ref().map(|m| m.ws_connect()); + on_disconnect.await; + middleware_layer.as_ref().map(|m| m.ws_disconnect(now)); + }); + } + + // https://github.com/rust-lang/rust/issues/102211 the error type can't be inferred + // to be `Box` so we need to + // convert it to a concrete type as workaround. + svc.call(req).await.map_err(|e| BoxError::from(e)) + } + }); + + cfg.tokio_handle.spawn(serve_with_graceful_shutdown( + sock, + svc, + cfg.stop_handle.clone().shutdown(), + )); + } + }); + } + + if local_addrs.is_empty() { + return Err(Box::new(ListenAddrError)); + } + + // The previous logging format was before + // `Running JSON-RPC server: addr=127.0.0.1:9944, allowed origins=["*"]` + // + // The new format is `Running JSON-RPC server: addr=` + // with the exception that for a single address it will be `Running JSON-RPC server: addr=addr,` + // with a trailing comma. + // + // This is to make it work with old scripts/utils that parse the logs. + log::info!("Running JSON-RPC server: addr={}", format_listen_addrs(&local_addrs)); Ok(server_handle) } diff --git a/substrate/client/rpc-servers/src/utils.rs b/substrate/client/rpc-servers/src/utils.rs index d9d943c7c1fb..5b4a4bf22b95 100644 --- a/substrate/client/rpc-servers/src/utils.rs +++ b/substrate/client/rpc-servers/src/utils.rs @@ -18,29 +18,190 @@ //! Substrate RPC server utils. +use crate::BatchRequestConfig; use std::{ error::Error as StdError, net::{IpAddr, SocketAddr}, + num::NonZeroU32, str::FromStr, }; use forwarded_header_value::ForwardedHeaderValue; use http::header::{HeaderName, HeaderValue}; +use ip_network::IpNetwork; use jsonrpsee::{server::middleware::http::HostFilterLayer, RpcModule}; +use sc_rpc_api::DenyUnsafe; use tower_http::cors::{AllowOrigin, CorsLayer}; const X_FORWARDED_FOR: HeaderName = HeaderName::from_static("x-forwarded-for"); const X_REAL_IP: HeaderName = HeaderName::from_static("x-real-ip"); const FORWARDED: HeaderName = HeaderName::from_static("forwarded"); -pub(crate) fn host_filtering(enabled: bool, addr: Option) -> Option { - // If the local_addr failed, fallback to wildcard. - let port = addr.map_or("*".to_string(), |p| p.port().to_string()); +#[derive(Debug)] +pub(crate) struct ListenAddrError; +impl std::error::Error for ListenAddrError {} + +impl std::fmt::Display for ListenAddrError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "No listen address was successfully bound") + } +} + +/// Available RPC methods. +#[derive(Debug, Copy, Clone)] +pub enum RpcMethods { + /// Allow only a safe subset of RPC methods. + Safe, + /// Expose every RPC method (even potentially unsafe ones). + Unsafe, + /// Automatically determine the RPC methods based on the connection. + Auto, +} + +impl Default for RpcMethods { + fn default() -> Self { + RpcMethods::Auto + } +} + +impl FromStr for RpcMethods { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "safe" => Ok(RpcMethods::Safe), + "unsafe" => Ok(RpcMethods::Unsafe), + "auto" => Ok(RpcMethods::Auto), + invalid => Err(format!("Invalid rpc methods {invalid}")), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct RpcSettings { + pub(crate) batch_config: BatchRequestConfig, + pub(crate) max_connections: u32, + pub(crate) max_payload_in_mb: u32, + pub(crate) max_payload_out_mb: u32, + pub(crate) max_subscriptions_per_connection: u32, + pub(crate) max_buffer_capacity_per_connection: u32, + pub(crate) rpc_methods: RpcMethods, + pub(crate) rate_limit: Option, + pub(crate) rate_limit_trust_proxy_headers: bool, + pub(crate) rate_limit_whitelisted_ips: Vec, + pub(crate) cors: CorsLayer, + pub(crate) host_filter: Option, +} + +/// Represent a single RPC endpoint with its configuration. +#[derive(Debug, Clone)] +pub struct RpcEndpoint { + /// Listen address. + pub listen_addr: SocketAddr, + /// Batch request configuration. + pub batch_config: BatchRequestConfig, + /// Maximum number of connections. + pub max_connections: u32, + /// Maximum inbound payload size in MB. + pub max_payload_in_mb: u32, + /// Maximum outbound payload size in MB. + pub max_payload_out_mb: u32, + /// Maximum number of subscriptions per connection. + pub max_subscriptions_per_connection: u32, + /// Maximum buffer capacity per connection. + pub max_buffer_capacity_per_connection: u32, + /// Rate limit per minute. + pub rate_limit: Option, + /// Whether to trust proxy headers for rate limiting. + pub rate_limit_trust_proxy_headers: bool, + /// Whitelisted IPs for rate limiting. + pub rate_limit_whitelisted_ips: Vec, + /// CORS. + pub cors: Option>, + /// RPC methods to expose. + pub rpc_methods: RpcMethods, + /// Whether it's an optional listening address i.e, it's ignored if it fails to bind. + /// For example substrate tries to bind both ipv4 and ipv6 addresses but some platforms + /// may not support ipv6. + pub is_optional: bool, + /// Whether to retry with a random port if the provided port is already in use. + pub retry_random_port: bool, +} + +impl RpcEndpoint { + /// Binds to the listen address. + pub(crate) async fn bind(self) -> Result> { + let listener = match tokio::net::TcpListener::bind(self.listen_addr).await { + Ok(listener) => listener, + Err(_) if self.retry_random_port => { + let mut addr = self.listen_addr; + addr.set_port(0); + + tokio::net::TcpListener::bind(addr).await? + }, + Err(e) => return Err(e.into()), + }; + let local_addr = listener.local_addr()?; + let host_filter = host_filtering(self.cors.is_some(), local_addr); + let cors = try_into_cors(self.cors)?; + + Ok(Listener { + listener, + local_addr, + cfg: RpcSettings { + batch_config: self.batch_config, + max_connections: self.max_connections, + max_payload_in_mb: self.max_payload_in_mb, + max_payload_out_mb: self.max_payload_out_mb, + max_subscriptions_per_connection: self.max_subscriptions_per_connection, + max_buffer_capacity_per_connection: self.max_buffer_capacity_per_connection, + rpc_methods: self.rpc_methods, + rate_limit: self.rate_limit, + rate_limit_trust_proxy_headers: self.rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: self.rate_limit_whitelisted_ips, + host_filter, + cors, + }, + }) + } +} + +/// TCP socket server with RPC settings. +pub(crate) struct Listener { + listener: tokio::net::TcpListener, + local_addr: SocketAddr, + cfg: RpcSettings, +} + +impl Listener { + /// Accepts a new connection. + pub(crate) async fn accept( + &mut self, + ) -> std::io::Result<(tokio::net::TcpStream, SocketAddr, RpcSettings)> { + let (sock, remote_addr) = self.listener.accept().await?; + Ok((sock, remote_addr, self.cfg.clone())) + } + + /// Returns the local address the listener is bound to. + pub fn local_addr(&self) -> SocketAddr { + self.local_addr + } +} + +pub(crate) fn host_filtering(enabled: bool, addr: SocketAddr) -> Option { if enabled { // NOTE: The listening addresses are whitelisted by default. - let hosts = - [format!("localhost:{port}"), format!("127.0.0.1:{port}"), format!("[::1]:{port}")]; + + let mut hosts = Vec::new(); + + if addr.is_ipv4() { + hosts.push(format!("localhost:{}", addr.port())); + hosts.push(format!("127.0.0.1:{}", addr.port())); + } else { + hosts.push(format!("[::1]:{}", addr.port())); + } + Some(HostFilterLayer::new(hosts).expect("Valid hosts; qed")) } else { None @@ -65,13 +226,15 @@ pub(crate) fn build_rpc_api(mut rpc_api: RpcModule) } pub(crate) fn try_into_cors( - maybe_cors: Option<&Vec>, + maybe_cors: Option>, ) -> Result> { if let Some(cors) = maybe_cors { let mut list = Vec::new(); + for origin in cors { - list.push(HeaderValue::from_str(origin)?); + list.push(HeaderValue::from_str(&origin)?) } + Ok(CorsLayer::new().allow_origin(AllowOrigin::list(list))) } else { // allow all cors @@ -79,14 +242,6 @@ pub(crate) fn try_into_cors( } } -pub(crate) fn format_cors(maybe_cors: Option<&Vec>) -> String { - if let Some(cors) = maybe_cors { - format!("{:?}", cors) - } else { - format!("{:?}", ["*"]) - } -} - /// Extracts the IP addr from the HTTP request. /// /// It is extracted in the following order: @@ -126,6 +281,34 @@ pub(crate) fn get_proxy_ip(req: &http::Request) -> Option { None } +/// Get the `deny_unsafe` setting based on the address and the RPC methods exposed by the interface. +pub fn deny_unsafe(addr: &SocketAddr, methods: &RpcMethods) -> DenyUnsafe { + match (addr.ip().is_loopback(), methods) { + | (_, RpcMethods::Unsafe) | (false, RpcMethods::Auto) => DenyUnsafe::No, + _ => DenyUnsafe::Yes, + } +} + +pub(crate) fn format_listen_addrs(addr: &[SocketAddr]) -> String { + let mut s = String::new(); + + let mut it = addr.iter().peekable(); + + while let Some(addr) = it.next() { + s.push_str(&addr.to_string()); + + if it.peek().is_some() { + s.push(','); + } + } + + if addr.len() == 1 { + s.push(','); + } + + s +} + #[cfg(test)] mod tests { use super::*; diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index 1ec7895e3088..ae21895de38d 100644 --- a/substrate/client/rpc-spec-v2/Cargo.toml +++ b/substrate/client/rpc-spec-v2/Cargo.toml @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { features = ["client-core", "macros", "server-core"], workspace = true } +jsonrpsee = { workspace = true, features = ["client-core", "macros", "server-core"] } # Internal chain structures for "chain_spec". sc-chain-spec = { workspace = true, default-features = true } # Pool for submitting extrinsics required by "transaction" @@ -45,7 +45,7 @@ rand = { workspace = true, default-features = true } schnellru = { workspace = true } [dev-dependencies] -jsonrpsee = { features = ["server", "ws-client"], workspace = true } +jsonrpsee = { workspace = true, features = ["server", "ws-client"] } serde_json = { workspace = true, default-features = true } tokio = { features = ["macros"], workspace = true, default-features = true } substrate-test-runtime-client = { workspace = true } diff --git a/substrate/client/rpc/Cargo.toml b/substrate/client/rpc/Cargo.toml index 4a8f4b3ec630..6fe28a3873e9 100644 --- a/substrate/client/rpc/Cargo.toml +++ b/substrate/client/rpc/Cargo.toml @@ -43,7 +43,6 @@ sp-statement-store = { workspace = true, default-features = true } tokio = { workspace = true, default-features = true } [dev-dependencies] -sp-tracing = { workspace = true } assert_matches = { workspace = true } sc-block-builder = { workspace = true, default-features = true } sc-network = { workspace = true, default-features = true } @@ -55,7 +54,6 @@ tokio = { workspace = true, default-features = true } sp-io = { workspace = true, default-features = true } substrate-test-runtime-client = { workspace = true } pretty_assertions = { workspace = true } -tracing-subscriber = { features = ["env-filter"], workspace = true } [features] test-helpers = [] diff --git a/substrate/client/rpc/src/author/mod.rs b/substrate/client/rpc/src/author/mod.rs index 2fc21a238bc9..731f4df2f6f3 100644 --- a/substrate/client/rpc/src/author/mod.rs +++ b/substrate/client/rpc/src/author/mod.rs @@ -30,8 +30,8 @@ use crate::{ use codec::{Decode, Encode}; use futures::TryFutureExt; -use jsonrpsee::{core::async_trait, types::ErrorObject, PendingSubscriptionSink}; -use sc_rpc_api::DenyUnsafe; +use jsonrpsee::{core::async_trait, types::ErrorObject, Extensions, PendingSubscriptionSink}; +use sc_rpc_api::check_if_safe; use sc_transaction_pool_api::{ error::IntoPoolError, BlockHash, InPoolTransaction, TransactionFor, TransactionPool, TransactionSource, TxHash, @@ -55,8 +55,6 @@ pub struct Author { pool: Arc

, /// The key store. keystore: KeystorePtr, - /// Whether to deny unsafe calls - deny_unsafe: DenyUnsafe, /// Executor to spawn subscriptions. executor: SubscriptionTaskExecutor, } @@ -67,10 +65,9 @@ impl Author { client: Arc, pool: Arc

, keystore: KeystorePtr, - deny_unsafe: DenyUnsafe, executor: SubscriptionTaskExecutor, ) -> Self { - Author { client, pool, keystore, deny_unsafe, executor } + Author { client, pool, keystore, executor } } } @@ -104,8 +101,14 @@ where }) } - fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> Result<()> { - self.deny_unsafe.check_if_safe()?; + fn insert_key( + &self, + ext: &Extensions, + key_type: String, + suri: String, + public: Bytes, + ) -> Result<()> { + check_if_safe(ext)?; let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?; self.keystore @@ -114,8 +117,8 @@ where Ok(()) } - fn rotate_keys(&self) -> Result { - self.deny_unsafe.check_if_safe()?; + fn rotate_keys(&self, ext: &Extensions) -> Result { + check_if_safe(ext)?; let best_block_hash = self.client.info().best_hash; let mut runtime_api = self.client.runtime_api(); @@ -128,8 +131,8 @@ where .map_err(|api_err| Error::Client(Box::new(api_err)).into()) } - fn has_session_keys(&self, session_keys: Bytes) -> Result { - self.deny_unsafe.check_if_safe()?; + fn has_session_keys(&self, ext: &Extensions, session_keys: Bytes) -> Result { + check_if_safe(ext)?; let best_block_hash = self.client.info().best_hash; let keys = self @@ -142,8 +145,8 @@ where Ok(self.keystore.has_keys(&keys)) } - fn has_key(&self, public_key: Bytes, key_type: String) -> Result { - self.deny_unsafe.check_if_safe()?; + fn has_key(&self, ext: &Extensions, public_key: Bytes, key_type: String) -> Result { + check_if_safe(ext)?; let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?; Ok(self.keystore.has_keys(&[(public_key.to_vec(), key_type)])) @@ -155,9 +158,10 @@ where fn remove_extrinsic( &self, + ext: &Extensions, bytes_or_hash: Vec>>, ) -> Result>> { - self.deny_unsafe.check_if_safe()?; + check_if_safe(ext)?; let hashes = bytes_or_hash .into_iter() .map(|x| match x { diff --git a/substrate/client/rpc/src/author/tests.rs b/substrate/client/rpc/src/author/tests.rs index 6bcb3e7863c0..bde60960eaf4 100644 --- a/substrate/client/rpc/src/author/tests.rs +++ b/substrate/client/rpc/src/author/tests.rs @@ -22,6 +22,7 @@ use crate::testing::{test_executor, timeout_secs}; use assert_matches::assert_matches; use codec::Encode; use jsonrpsee::{core::EmptyServerParams as EmptyParams, MethodsError as RpcError, RpcModule}; +use sc_rpc_api::DenyUnsafe; use sc_transaction_pool::{BasicPool, FullChainApi}; use sc_transaction_pool_api::TransactionStatus; use sp_core::{ @@ -72,26 +73,27 @@ impl Default for TestSetup { } impl TestSetup { - fn author(&self) -> Author> { - Author { + fn to_rpc(&self) -> RpcModule>> { + let mut module = Author { client: self.client.clone(), pool: self.pool.clone(), keystore: self.keystore.clone(), - deny_unsafe: DenyUnsafe::No, executor: test_executor(), } + .into_rpc(); + module.extensions_mut().insert(DenyUnsafe::No); + module } fn into_rpc() -> RpcModule>> { - Self::default().author().into_rpc() + Self::default().to_rpc() } } #[tokio::test] async fn author_submit_transaction_should_not_cause_error() { - sp_tracing::init_for_tests(); - let author = TestSetup::default().author(); - let api = author.into_rpc(); + let api = TestSetup::into_rpc(); + let xt: Bytes = uxt(AccountKeyring::Alice, 1).encode().into(); let extrinsic_hash: H256 = blake2_256(&xt).into(); let response: H256 = api.call("author_submitExtrinsic", [xt.clone()]).await.unwrap(); @@ -179,7 +181,7 @@ async fn author_should_return_pending_extrinsics() { async fn author_should_remove_extrinsics() { const METHOD: &'static str = "author_removeExtrinsic"; let setup = TestSetup::default(); - let api = setup.author().into_rpc(); + let api = setup.to_rpc(); // Submit three extrinsics, then remove two of them (will cause the third to be removed as well, // having a higher nonce) @@ -214,7 +216,7 @@ async fn author_should_remove_extrinsics() { #[tokio::test] async fn author_should_insert_key() { let setup = TestSetup::default(); - let api = setup.author().into_rpc(); + let api = setup.to_rpc(); let suri = "//Alice"; let keypair = ed25519::Pair::from_string(suri, None).expect("generates keypair"); let params: (String, String, Bytes) = ( @@ -231,7 +233,7 @@ async fn author_should_insert_key() { #[tokio::test] async fn author_should_rotate_keys() { let setup = TestSetup::default(); - let api = setup.author().into_rpc(); + let api = setup.to_rpc(); let new_pubkeys: Bytes = api.call("author_rotateKeys", EmptyParams::new()).await.unwrap(); let session_keys = @@ -244,7 +246,6 @@ async fn author_should_rotate_keys() { #[tokio::test] async fn author_has_session_keys() { - // Setup let api = TestSetup::into_rpc(); // Add a valid session key @@ -255,7 +256,7 @@ async fn author_has_session_keys() { // Add a session key in a different keystore let non_existent_pubkeys: Bytes = { - let api2 = TestSetup::default().author().into_rpc(); + let api2 = TestSetup::into_rpc(); api2.call("author_rotateKeys", EmptyParams::new()) .await .expect("Rotates the keys") @@ -279,8 +280,6 @@ async fn author_has_session_keys() { #[tokio::test] async fn author_has_key() { - sp_tracing::init_for_tests(); - let api = TestSetup::into_rpc(); let suri = "//Alice"; let alice_keypair = ed25519::Pair::from_string(suri, None).expect("Generates keypair"); diff --git a/substrate/client/rpc/src/dev/mod.rs b/substrate/client/rpc/src/dev/mod.rs index 424ef72b694e..3ccda760c3f5 100644 --- a/substrate/client/rpc/src/dev/mod.rs +++ b/substrate/client/rpc/src/dev/mod.rs @@ -22,8 +22,9 @@ #[cfg(test)] mod tests; +use jsonrpsee::Extensions; use sc_client_api::{BlockBackend, HeaderBackend}; -use sc_rpc_api::{dev::error::Error, DenyUnsafe}; +use sc_rpc_api::{check_if_safe, dev::error::Error}; use sp_api::{ApiExt, Core, ProvideRuntimeApi}; use sp_core::Encode; use sp_runtime::{ @@ -42,14 +43,13 @@ type HasherOf = <::Header as Header>::Hashing; /// The Dev API. All methods are unsafe. pub struct Dev { client: Arc, - deny_unsafe: DenyUnsafe, _phantom: PhantomData, } impl Dev { /// Create a new Dev API. - pub fn new(client: Arc, deny_unsafe: DenyUnsafe) -> Self { - Self { client, deny_unsafe, _phantom: PhantomData::default() } + pub fn new(client: Arc) -> Self { + Self { client, _phantom: PhantomData::default() } } } @@ -64,8 +64,12 @@ where + 'static, Client::Api: Core, { - fn block_stats(&self, hash: Block::Hash) -> Result, Error> { - self.deny_unsafe.check_if_safe()?; + fn block_stats( + &self, + ext: &Extensions, + hash: Block::Hash, + ) -> Result, Error> { + check_if_safe(ext)?; let block = { let block = self.client.block(hash).map_err(|e| Error::BlockQueryError(Box::new(e)))?; diff --git a/substrate/client/rpc/src/dev/tests.rs b/substrate/client/rpc/src/dev/tests.rs index 8f09d48e8a5d..ff691af7de41 100644 --- a/substrate/client/rpc/src/dev/tests.rs +++ b/substrate/client/rpc/src/dev/tests.rs @@ -17,6 +17,7 @@ // along with this program. If not, see . use super::*; +use crate::DenyUnsafe; use sc_block_builder::BlockBuilderBuilder; use sp_blockchain::HeaderBackend; use sp_consensus::BlockOrigin; @@ -25,7 +26,8 @@ use substrate_test_runtime_client::{prelude::*, runtime::Block}; #[tokio::test] async fn block_stats_work() { let client = Arc::new(substrate_test_runtime_client::new()); - let api = >::new(client.clone(), DenyUnsafe::No).into_rpc(); + let mut api = >::new(client.clone()).into_rpc(); + api.extensions_mut().insert(DenyUnsafe::No); let block = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().genesis_hash) @@ -77,7 +79,8 @@ async fn block_stats_work() { #[tokio::test] async fn deny_unsafe_works() { let client = Arc::new(substrate_test_runtime_client::new()); - let api = >::new(client.clone(), DenyUnsafe::Yes).into_rpc(); + let mut api = >::new(client.clone()).into_rpc(); + api.extensions_mut().insert(DenyUnsafe::Yes); let block = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().genesis_hash) @@ -101,6 +104,6 @@ async fn deny_unsafe_works() { assert_eq!( resp, - r#"{"jsonrpc":"2.0","error":{"code":-32601,"message":"RPC call is unsafe to be called externally"},"id":1}"# + r#"{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"RPC call is unsafe to be called externally"}}"# ); } diff --git a/substrate/client/rpc/src/lib.rs b/substrate/client/rpc/src/lib.rs index b40d0341e321..33e4ac2f4c89 100644 --- a/substrate/client/rpc/src/lib.rs +++ b/substrate/client/rpc/src/lib.rs @@ -22,12 +22,9 @@ #![warn(missing_docs)] -pub use jsonrpsee::core::{ - id_providers::{ - RandomIntegerIdProvider as RandomIntegerSubscriptionId, - RandomStringIdProvider as RandomStringSubscriptionId, - }, - traits::IdProvider as RpcSubscriptionIdProvider, +pub use jsonrpsee::core::id_providers::{ + RandomIntegerIdProvider as RandomIntegerSubscriptionId, + RandomStringIdProvider as RandomStringSubscriptionId, }; pub use sc_rpc_api::DenyUnsafe; diff --git a/substrate/client/rpc/src/offchain/mod.rs b/substrate/client/rpc/src/offchain/mod.rs index 661673866053..af6bc1ba58c8 100644 --- a/substrate/client/rpc/src/offchain/mod.rs +++ b/substrate/client/rpc/src/offchain/mod.rs @@ -22,11 +22,11 @@ mod tests; use self::error::Error; -use jsonrpsee::core::async_trait; +use jsonrpsee::{core::async_trait, Extensions}; use parking_lot::RwLock; +use sc_rpc_api::check_if_safe; /// Re-export the API for backward compatibility. pub use sc_rpc_api::offchain::*; -use sc_rpc_api::DenyUnsafe; use sp_core::{ offchain::{OffchainStorage, StorageKind}, Bytes, @@ -38,20 +38,25 @@ use std::sync::Arc; pub struct Offchain { /// Offchain storage storage: Arc>, - deny_unsafe: DenyUnsafe, } impl Offchain { /// Create new instance of Offchain API. - pub fn new(storage: T, deny_unsafe: DenyUnsafe) -> Self { - Offchain { storage: Arc::new(RwLock::new(storage)), deny_unsafe } + pub fn new(storage: T) -> Self { + Offchain { storage: Arc::new(RwLock::new(storage)) } } } #[async_trait] impl OffchainApiServer for Offchain { - fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<(), Error> { - self.deny_unsafe.check_if_safe()?; + fn set_local_storage( + &self, + ext: &Extensions, + kind: StorageKind, + key: Bytes, + value: Bytes, + ) -> Result<(), Error> { + check_if_safe(ext)?; let prefix = match kind { StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX, @@ -61,8 +66,13 @@ impl OffchainApiServer for Offchain { Ok(()) } - fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result, Error> { - self.deny_unsafe.check_if_safe()?; + fn get_local_storage( + &self, + ext: &Extensions, + kind: StorageKind, + key: Bytes, + ) -> Result, Error> { + check_if_safe(ext)?; let prefix = match kind { StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX, diff --git a/substrate/client/rpc/src/offchain/tests.rs b/substrate/client/rpc/src/offchain/tests.rs index 7758fac7fabd..41f22c2dc964 100644 --- a/substrate/client/rpc/src/offchain/tests.rs +++ b/substrate/client/rpc/src/offchain/tests.rs @@ -17,22 +17,25 @@ // along with this program. If not, see . use super::*; +use crate::testing::{allow_unsafe, deny_unsafe}; use assert_matches::assert_matches; use sp_core::{offchain::storage::InMemOffchainStorage, Bytes}; #[test] fn local_storage_should_work() { let storage = InMemOffchainStorage::default(); - let offchain = Offchain::new(storage, DenyUnsafe::No); + let offchain = Offchain::new(storage); let key = Bytes(b"offchain_storage".to_vec()); let value = Bytes(b"offchain_value".to_vec()); + let ext = allow_unsafe(); + assert_matches!( - offchain.set_local_storage(StorageKind::PERSISTENT, key.clone(), value.clone()), + offchain.set_local_storage(&ext, StorageKind::PERSISTENT, key.clone(), value.clone()), Ok(()) ); assert_matches!( - offchain.get_local_storage(StorageKind::PERSISTENT, key), + offchain.get_local_storage(&ext, StorageKind::PERSISTENT, key), Ok(Some(ref v)) if *v == value ); } @@ -40,18 +43,20 @@ fn local_storage_should_work() { #[test] fn offchain_calls_considered_unsafe() { let storage = InMemOffchainStorage::default(); - let offchain = Offchain::new(storage, DenyUnsafe::Yes); + let offchain = Offchain::new(storage); let key = Bytes(b"offchain_storage".to_vec()); let value = Bytes(b"offchain_value".to_vec()); + let ext = deny_unsafe(); + assert_matches!( - offchain.set_local_storage(StorageKind::PERSISTENT, key.clone(), value.clone()), + offchain.set_local_storage(&ext, StorageKind::PERSISTENT, key.clone(), value.clone()), Err(Error::UnsafeRpcCalled(e)) => { assert_eq!(e.to_string(), "RPC call is unsafe to be called externally") } ); assert_matches!( - offchain.get_local_storage(StorageKind::PERSISTENT, key), + offchain.get_local_storage(&ext, StorageKind::PERSISTENT, key), Err(Error::UnsafeRpcCalled(e)) => { assert_eq!(e.to_string(), "RPC call is unsafe to be called externally") } diff --git a/substrate/client/rpc/src/state/mod.rs b/substrate/client/rpc/src/state/mod.rs index c9a41e25eda8..d8989b3e1bee 100644 --- a/substrate/client/rpc/src/state/mod.rs +++ b/substrate/client/rpc/src/state/mod.rs @@ -25,11 +25,11 @@ mod utils; mod tests; use crate::SubscriptionTaskExecutor; -use jsonrpsee::{core::async_trait, PendingSubscriptionSink}; +use jsonrpsee::{core::async_trait, Extensions, PendingSubscriptionSink}; use sc_client_api::{ Backend, BlockBackend, BlockchainEvents, ExecutorProvider, ProofProvider, StorageProvider, }; -use sc_rpc_api::DenyUnsafe; +use sc_rpc_api::{check_if_safe, DenyUnsafe}; use sp_api::{CallApiAt, Metadata, ProvideRuntimeApi}; use sp_blockchain::{HeaderBackend, HeaderMetadata}; use sp_core::{ @@ -164,7 +164,6 @@ where pub fn new_full( client: Arc, executor: SubscriptionTaskExecutor, - deny_unsafe: DenyUnsafe, ) -> (State, ChildState) where Block: BlockT + 'static, @@ -187,14 +186,12 @@ where let child_backend = Box::new(self::state_full::FullState::new(client.clone(), executor.clone())); let backend = Box::new(self::state_full::FullState::new(client, executor)); - (State { backend, deny_unsafe }, ChildState { backend: child_backend }) + (State { backend }, ChildState { backend: child_backend }) } /// State API with subscriptions support. pub struct State { backend: Box>, - /// Whether to deny unsafe calls - deny_unsafe: DenyUnsafe, } #[async_trait] @@ -222,10 +219,11 @@ where fn storage_pairs( &self, + ext: &Extensions, key_prefix: StorageKey, block: Option, ) -> Result, Error> { - self.deny_unsafe.check_if_safe()?; + check_if_safe(ext)?; self.backend.storage_pairs(block, key_prefix).map_err(Into::into) } @@ -262,13 +260,15 @@ where async fn storage_size( &self, + ext: &Extensions, key: StorageKey, block: Option, ) -> Result, Error> { - self.backend - .storage_size(block, key, self.deny_unsafe) - .await - .map_err(Into::into) + let deny_unsafe = ext + .get::() + .cloned() + .expect("DenyUnsafe extension is always set by the substrate rpc server; qed"); + self.backend.storage_size(block, key, deny_unsafe).await.map_err(Into::into) } fn metadata(&self, block: Option) -> Result { @@ -281,11 +281,12 @@ where fn query_storage( &self, + ext: &Extensions, keys: Vec, from: Block::Hash, to: Option, ) -> Result>, Error> { - self.deny_unsafe.check_if_safe()?; + check_if_safe(ext)?; self.backend.query_storage(from, to, keys).map_err(Into::into) } @@ -312,12 +313,13 @@ where /// Note: requires runtimes compiled with wasm tracing support, `--features with-tracing`. fn trace_block( &self, + ext: &Extensions, block: Block::Hash, targets: Option, storage_keys: Option, methods: Option, ) -> Result { - self.deny_unsafe.check_if_safe()?; + check_if_safe(ext)?; self.backend .trace_block(block, targets, storage_keys, methods) .map_err(Into::into) @@ -327,8 +329,17 @@ where self.backend.subscribe_runtime_version(pending) } - fn subscribe_storage(&self, pending: PendingSubscriptionSink, keys: Option>) { - self.backend.subscribe_storage(pending, keys, self.deny_unsafe) + fn subscribe_storage( + &self, + pending: PendingSubscriptionSink, + ext: &Extensions, + keys: Option>, + ) { + let deny_unsafe = ext + .get::() + .cloned() + .expect("DenyUnsafe extension is always set by the substrate rpc server; qed"); + self.backend.subscribe_storage(pending, keys, deny_unsafe) } } diff --git a/substrate/client/rpc/src/state/tests.rs b/substrate/client/rpc/src/state/tests.rs index 86df46f63a0f..eef795070343 100644 --- a/substrate/client/rpc/src/state/tests.rs +++ b/substrate/client/rpc/src/state/tests.rs @@ -18,12 +18,11 @@ use self::error::Error; use super::*; -use crate::testing::{test_executor, timeout_secs}; +use crate::testing::{allow_unsafe, test_executor, timeout_secs}; use assert_matches::assert_matches; use futures::executor; use jsonrpsee::{core::EmptyServerParams as EmptyParams, MethodsError as RpcError}; use sc_block_builder::BlockBuilderBuilder; -use sc_rpc_api::DenyUnsafe; use sp_consensus::BlockOrigin; use sp_core::{hash::H256, storage::ChildInfo}; use std::sync::Arc; @@ -39,14 +38,6 @@ fn prefixed_storage_key() -> PrefixedStorageKey { child_info.prefixed_storage_key() } -fn init_logger() { - use tracing_subscriber::{EnvFilter, FmtSubscriber}; - - let _ = FmtSubscriber::builder() - .with_env_filter(EnvFilter::from_default_env()) - .try_init(); -} - #[tokio::test] async fn should_return_storage() { const KEY: &[u8] = b":mock"; @@ -62,8 +53,9 @@ async fn should_return_storage() { .add_extra_storage(b":map:acc2".to_vec(), vec![1, 2, 3]) .build(); let genesis_hash = client.genesis_hash(); - let (client, child) = new_full(Arc::new(client), test_executor(), DenyUnsafe::No); + let (client, child) = new_full(Arc::new(client), test_executor()); let key = StorageKey(KEY.to_vec()); + let ext = allow_unsafe(); assert_eq!( client @@ -78,11 +70,15 @@ async fn should_return_storage() { Ok(true) ); assert_eq!( - client.storage_size(key.clone(), None).await.unwrap().unwrap() as usize, + client.storage_size(&ext, key.clone(), None).await.unwrap().unwrap() as usize, VALUE.len(), ); assert_eq!( - client.storage_size(StorageKey(b":map".to_vec()), None).await.unwrap().unwrap() as usize, + client + .storage_size(&ext, StorageKey(b":map".to_vec()), None) + .await + .unwrap() + .unwrap() as usize, 2 + 3, ); assert_eq!( @@ -110,7 +106,7 @@ async fn should_return_storage_entries() { .add_extra_child_storage(&child_info, KEY2.to_vec(), CHILD_VALUE2.to_vec()) .build(); let genesis_hash = client.genesis_hash(); - let (_client, child) = new_full(Arc::new(client), test_executor(), DenyUnsafe::No); + let (_client, child) = new_full(Arc::new(client), test_executor()); let keys = &[StorageKey(KEY1.to_vec()), StorageKey(KEY2.to_vec())]; assert_eq!( @@ -141,7 +137,7 @@ async fn should_return_child_storage() { .build(), ); let genesis_hash = client.genesis_hash(); - let (_client, child) = new_full(client, test_executor(), DenyUnsafe::No); + let (_client, child) = new_full(client, test_executor()); let child_key = prefixed_storage_key(); let key = StorageKey(b"key".to_vec()); @@ -172,7 +168,7 @@ async fn should_return_child_storage_entries() { .build(), ); let genesis_hash = client.genesis_hash(); - let (_client, child) = new_full(client, test_executor(), DenyUnsafe::No); + let (_client, child) = new_full(client, test_executor()); let child_key = prefixed_storage_key(); let keys = vec![StorageKey(b"key1".to_vec()), StorageKey(b"key2".to_vec())]; @@ -203,7 +199,7 @@ async fn should_return_child_storage_entries() { async fn should_call_contract() { let client = Arc::new(substrate_test_runtime_client::new()); let genesis_hash = client.genesis_hash(); - let (client, _child) = new_full(client, test_executor(), DenyUnsafe::No); + let (client, _child) = new_full(client, test_executor()); assert_matches!( client.call("balanceOf".into(), Bytes(vec![1, 2, 3]), Some(genesis_hash).into()), @@ -213,13 +209,12 @@ async fn should_call_contract() { #[tokio::test] async fn should_notify_about_storage_changes() { - init_logger(); - let mut sub = { let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); + let (api, _child) = new_full(client.clone(), test_executor()); + let mut api_rpc = api.into_rpc(); + api_rpc.extensions_mut().insert(DenyUnsafe::No); - let api_rpc = api.into_rpc(); let sub = api_rpc .subscribe_unbounded("state_subscribeStorage", EmptyParams::new()) .await @@ -253,11 +248,9 @@ async fn should_notify_about_storage_changes() { #[tokio::test] async fn should_send_initial_storage_changes_and_notifications() { - init_logger(); - let mut sub = { let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); + let (api, _child) = new_full(client.clone(), test_executor()); let alice_balance_key = [ sp_crypto_hashing::twox_128(b"System"), @@ -270,7 +263,9 @@ async fn should_send_initial_storage_changes_and_notifications() { .cloned() .collect::>(); - let api_rpc = api.into_rpc(); + let mut api_rpc = api.into_rpc(); + api_rpc.extensions_mut().insert(DenyUnsafe::No); + let sub = api_rpc .subscribe_unbounded( "state_subscribeStorage", @@ -305,7 +300,7 @@ async fn should_send_initial_storage_changes_and_notifications() { #[tokio::test] async fn should_query_storage() { async fn run_tests(client: Arc) { - let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); + let (api, _child) = new_full(client.clone(), test_executor()); let add_block = |index| { let mut builder = BlockBuilderBuilder::new(&*client) @@ -377,14 +372,16 @@ async fn should_query_storage() { }, ]; + let ext = allow_unsafe(); + // Query changes only up to block1 let keys = (1..6).map(|k| StorageKey(vec![k])).collect::>(); - let result = api.query_storage(keys.clone(), genesis_hash, Some(block1_hash).into()); + let result = api.query_storage(&ext, keys.clone(), genesis_hash, Some(block1_hash).into()); assert_eq!(result.unwrap(), expected); // Query all changes - let result = api.query_storage(keys.clone(), genesis_hash, None.into()); + let result = api.query_storage(&ext, keys.clone(), genesis_hash, None.into()); expected.push(StorageChangeSet { block: block2_hash, @@ -397,13 +394,13 @@ async fn should_query_storage() { assert_eq!(result.unwrap(), expected); // Query changes up to block2. - let result = api.query_storage(keys.clone(), genesis_hash, Some(block2_hash)); + let result = api.query_storage(&ext, keys.clone(), genesis_hash, Some(block2_hash)); assert_eq!(result.unwrap(), expected); // Inverted range. assert_matches!( - api.query_storage(keys.clone(), block1_hash, Some(genesis_hash)), + api.query_storage(&ext, keys.clone(), block1_hash, Some(genesis_hash)), Err(Error::InvalidBlockRange { from, to, details }) if from == format!("1 ({:?})", block1_hash) && to == format!("0 ({:?})", genesis_hash) && details == "from number > to number".to_owned() ); @@ -412,7 +409,7 @@ async fn should_query_storage() { // Invalid second hash. assert_matches!( - api.query_storage(keys.clone(), genesis_hash, Some(random_hash1)), + api.query_storage(&ext, keys.clone(), genesis_hash, Some(random_hash1)), Err(Error::InvalidBlockRange { from, to, details }) if from == format!("{:?}", genesis_hash) && to == format!("{:?}", Some(random_hash1)) && details == format!( "UnknownBlock: Header was not found in the database: {:?}", random_hash1 @@ -421,7 +418,7 @@ async fn should_query_storage() { // Invalid first hash with Some other hash. assert_matches!( - api.query_storage(keys.clone(), random_hash1, Some(genesis_hash)), + api.query_storage(&ext, keys.clone(), random_hash1, Some(genesis_hash)), Err(Error::InvalidBlockRange { from, to, details }) if from == format!("{:?}", random_hash1) && to == format!("{:?}", Some(genesis_hash)) && details == format!( "UnknownBlock: Header was not found in the database: {:?}", random_hash1 @@ -430,7 +427,7 @@ async fn should_query_storage() { // Invalid first hash with None. assert_matches!( - api.query_storage(keys.clone(), random_hash1, None), + api.query_storage(&ext, keys.clone(), random_hash1, None), Err(Error::InvalidBlockRange { from, to, details }) if from == format!("{:?}", random_hash1) && to == format!("{:?}", Some(block2_hash)) && details == format!( "UnknownBlock: Header was not found in the database: {:?}", random_hash1 @@ -439,7 +436,7 @@ async fn should_query_storage() { // Both hashes invalid. assert_matches!( - api.query_storage(keys.clone(), random_hash1, Some(random_hash2)), + api.query_storage(&ext, keys.clone(), random_hash1, Some(random_hash2)), Err(Error::InvalidBlockRange { from, to, details }) if from == format!("{:?}", random_hash1) && to == format!("{:?}", Some(random_hash2)) && details == format!( "UnknownBlock: Header was not found in the database: {:?}", random_hash1 @@ -471,7 +468,7 @@ async fn should_query_storage() { #[tokio::test] async fn should_return_runtime_version() { let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); + let (api, _child) = new_full(client.clone(), test_executor()); // it is basically json-encoded substrate_test_runtime_client::runtime::VERSION let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ @@ -493,9 +490,10 @@ async fn should_return_runtime_version() { async fn should_notify_on_runtime_version_initially() { let mut sub = { let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client, test_executor(), DenyUnsafe::No); + let (api, _child) = new_full(client, test_executor()); + let mut api_rpc = api.into_rpc(); + api_rpc.extensions_mut().insert(DenyUnsafe::No); - let api_rpc = api.into_rpc(); let sub = api_rpc .subscribe_unbounded("state_subscribeRuntimeVersion", EmptyParams::new()) .await @@ -518,12 +516,11 @@ fn should_deserialize_storage_key() { #[tokio::test] async fn wildcard_storage_subscriptions_are_rpc_unsafe() { - init_logger(); - let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client, test_executor(), DenyUnsafe::Yes); + let (api, _child) = new_full(client, test_executor()); + let mut api_rpc = api.into_rpc(); + api_rpc.extensions_mut().insert(DenyUnsafe::Yes); - let api_rpc = api.into_rpc(); let err = api_rpc.subscribe_unbounded("state_subscribeStorage", EmptyParams::new()).await; assert_matches!(err, Err(RpcError::JsonRpc(e)) if e.message() == "RPC call is unsafe to be called externally"); } @@ -531,8 +528,9 @@ async fn wildcard_storage_subscriptions_are_rpc_unsafe() { #[tokio::test] async fn concrete_storage_subscriptions_are_rpc_safe() { let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client, test_executor(), DenyUnsafe::Yes); - let api_rpc = api.into_rpc(); + let (api, _child) = new_full(client, test_executor()); + let mut api_rpc = api.into_rpc(); + api_rpc.extensions_mut().insert(DenyUnsafe::Yes); let key = StorageKey(STORAGE_KEY.to_vec()); let sub = api_rpc.subscribe_unbounded("state_subscribeStorage", [[key]]).await; diff --git a/substrate/client/rpc/src/statement/mod.rs b/substrate/client/rpc/src/statement/mod.rs index e99135aec38c..81aa50f73430 100644 --- a/substrate/client/rpc/src/statement/mod.rs +++ b/substrate/client/rpc/src/statement/mod.rs @@ -19,10 +19,12 @@ //! Substrate statement store API. use codec::{Decode, Encode}; -use jsonrpsee::core::{async_trait, RpcResult}; +use jsonrpsee::{ + core::{async_trait, RpcResult}, + Extensions, +}; /// Re-export the API for backward compatibility. pub use sc_rpc_api::statement::{error::Error, StatementApiServer}; -use sc_rpc_api::DenyUnsafe; use sp_core::Bytes; use sp_statement_store::{StatementSource, SubmitResult}; use std::sync::Arc; @@ -30,23 +32,19 @@ use std::sync::Arc; /// Statement store API pub struct StatementStore { store: Arc, - deny_unsafe: DenyUnsafe, } impl StatementStore { /// Create new instance of Offchain API. - pub fn new( - store: Arc, - deny_unsafe: DenyUnsafe, - ) -> Self { - StatementStore { store, deny_unsafe } + pub fn new(store: Arc) -> Self { + StatementStore { store } } } #[async_trait] impl StatementApiServer for StatementStore { - fn dump(&self) -> RpcResult> { - self.deny_unsafe.check_if_safe()?; + fn dump(&self, ext: &Extensions) -> RpcResult> { + sc_rpc_api::check_if_safe(ext)?; let statements = self.store.statements().map_err(|e| Error::StatementStore(e.to_string()))?; diff --git a/substrate/client/rpc/src/system/mod.rs b/substrate/client/rpc/src/system/mod.rs index 8c7510c64cb5..e0752749bcbd 100644 --- a/substrate/client/rpc/src/system/mod.rs +++ b/substrate/client/rpc/src/system/mod.rs @@ -22,8 +22,11 @@ mod tests; use futures::channel::oneshot; -use jsonrpsee::core::{async_trait, JsonValue}; -use sc_rpc_api::DenyUnsafe; +use jsonrpsee::{ + core::{async_trait, JsonValue}, + Extensions, +}; +use sc_rpc_api::check_if_safe; use sc_tracing::logging; use sc_utils::mpsc::TracingUnboundedSender; use sp_runtime::traits::{self, Header as HeaderT}; @@ -35,7 +38,6 @@ pub use sc_rpc_api::system::*; pub struct System { info: SystemInfo, send_back: TracingUnboundedSender>, - deny_unsafe: DenyUnsafe, } /// Request to be processed. @@ -68,12 +70,8 @@ impl System { /// /// The `send_back` will be used to transmit some of the requests. The user is responsible for /// reading from that channel and answering the requests. - pub fn new( - info: SystemInfo, - send_back: TracingUnboundedSender>, - deny_unsafe: DenyUnsafe, - ) -> Self { - System { info, send_back, deny_unsafe } + pub fn new(info: SystemInfo, send_back: TracingUnboundedSender>) -> Self { + System { info, send_back } } } @@ -119,22 +117,23 @@ impl SystemApiServer::Number> async fn system_peers( &self, + ext: &Extensions, ) -> Result::Number>>, Error> { - self.deny_unsafe.check_if_safe()?; + check_if_safe(ext)?; let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::Peers(tx)); rx.await.map_err(|e| Error::Internal(e.to_string())) } - async fn system_network_state(&self) -> Result { - self.deny_unsafe.check_if_safe()?; + async fn system_network_state(&self, ext: &Extensions) -> Result { + check_if_safe(ext)?; let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::NetworkState(tx)); rx.await.map_err(|e| Error::Internal(e.to_string())) } - async fn system_add_reserved_peer(&self, peer: String) -> Result<(), Error> { - self.deny_unsafe.check_if_safe()?; + async fn system_add_reserved_peer(&self, ext: &Extensions, peer: String) -> Result<(), Error> { + check_if_safe(ext)?; let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::NetworkAddReservedPeer(peer, tx)); match rx.await { @@ -144,8 +143,12 @@ impl SystemApiServer::Number> } } - async fn system_remove_reserved_peer(&self, peer: String) -> Result<(), Error> { - self.deny_unsafe.check_if_safe()?; + async fn system_remove_reserved_peer( + &self, + ext: &Extensions, + peer: String, + ) -> Result<(), Error> { + check_if_safe(ext)?; let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::NetworkRemoveReservedPeer(peer, tx)); match rx.await { @@ -173,15 +176,15 @@ impl SystemApiServer::Number> rx.await.map_err(|e| Error::Internal(e.to_string())) } - fn system_add_log_filter(&self, directives: String) -> Result<(), Error> { - self.deny_unsafe.check_if_safe()?; + fn system_add_log_filter(&self, ext: &Extensions, directives: String) -> Result<(), Error> { + check_if_safe(ext)?; logging::add_directives(&directives); logging::reload_filter().map_err(|e| Error::Internal(e)) } - fn system_reset_log_filter(&self) -> Result<(), Error> { - self.deny_unsafe.check_if_safe()?; + fn system_reset_log_filter(&self, ext: &Extensions) -> Result<(), Error> { + check_if_safe(ext)?; logging::reset_log_filter().map_err(|e| Error::Internal(e)) } } diff --git a/substrate/client/rpc/src/system/tests.rs b/substrate/client/rpc/src/system/tests.rs index 03967c63523c..bfef0e429ceb 100644 --- a/substrate/client/rpc/src/system/tests.rs +++ b/substrate/client/rpc/src/system/tests.rs @@ -17,6 +17,7 @@ // along with this program. If not, see . use super::{helpers::SyncState, *}; +use crate::DenyUnsafe; use assert_matches::assert_matches; use futures::prelude::*; use jsonrpsee::{core::EmptyServerParams as EmptyParams, MethodsError as RpcError, RpcModule}; @@ -127,7 +128,7 @@ fn api>>(sync: T) -> RpcModule> { future::ready(()) })) }); - System::new( + let mut module = System::new( SystemInfo { impl_name: "testclient".into(), impl_version: "0.2.0".into(), @@ -136,9 +137,11 @@ fn api>>(sync: T) -> RpcModule> { chain_type: Default::default(), }, tx, - sc_rpc_api::DenyUnsafe::No, ) - .into_rpc() + .into_rpc(); + + module.extensions_mut().insert(DenyUnsafe::No); + module } #[tokio::test] diff --git a/substrate/client/rpc/src/testing.rs b/substrate/client/rpc/src/testing.rs index e04f80a7b9e6..990f81ba551e 100644 --- a/substrate/client/rpc/src/testing.rs +++ b/substrate/client/rpc/src/testing.rs @@ -20,6 +20,9 @@ use std::{future::Future, sync::Arc}; +use jsonrpsee::Extensions; +use sc_rpc_api::DenyUnsafe; + /// A task executor that can be used for running RPC tests. /// /// Warning: the tokio runtime must be initialized before calling this. @@ -70,3 +73,17 @@ pub fn test_executor() -> Arc { pub fn timeout_secs>(s: u64, f: F) -> tokio::time::Timeout { tokio::time::timeout(std::time::Duration::from_secs(s), f) } + +/// Helper to create an extension that denies unsafe calls. +pub fn deny_unsafe() -> Extensions { + let mut ext = Extensions::new(); + ext.insert(DenyUnsafe::Yes); + ext +} + +/// Helper to create an extension that allows unsafe calls. +pub fn allow_unsafe() -> Extensions { + let mut ext = Extensions::new(); + ext.insert(DenyUnsafe::No); + ext +} diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index d9a91e715fd5..9a6c3fde37d9 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -361,8 +361,7 @@ pub struct SpawnTasksParams<'a, TBl: BlockT, TCl, TExPool, TRpc, Backend> { /// A shared transaction pool. pub transaction_pool: Arc, /// Builds additional [`RpcModule`]s that should be added to the server - pub rpc_builder: - Box Result, Error>>, + pub rpc_builder: Box Result, Error>>, /// A shared network instance. pub network: Arc, /// A Sender for RPC requests. @@ -491,9 +490,8 @@ where let rpc_id_provider = config.rpc_id_provider.take(); // jsonrpsee RPC - let gen_rpc_module = |deny_unsafe: DenyUnsafe| { + let gen_rpc_module = || { gen_rpc_module( - deny_unsafe, task_manager.spawn_handle(), client.clone(), transaction_pool.clone(), @@ -505,8 +503,14 @@ where ) }; - let rpc = start_rpc_servers(&config, gen_rpc_module, rpc_id_provider)?; - let rpc_handlers = RpcHandlers::new(Arc::new(gen_rpc_module(sc_rpc::DenyUnsafe::No)?.into())); + let rpc_server_handle = start_rpc_servers(&config, gen_rpc_module, rpc_id_provider)?; + let in_memory_rpc = { + let mut module = gen_rpc_module()?; + module.extensions_mut().insert(DenyUnsafe::No); + module + }; + + let in_memory_rpc_handle = RpcHandlers::new(Arc::new(in_memory_rpc)); // Spawn informant task spawn_handle.spawn( @@ -515,9 +519,9 @@ where sc_informant::build(client.clone(), network, sync_service.clone()), ); - task_manager.keep_alive((config.base_path, rpc)); + task_manager.keep_alive((config.base_path, rpc_server_handle)); - Ok(rpc_handlers) + Ok(in_memory_rpc_handle) } /// Returns a future that forwards imported transactions to the transaction networking protocol. @@ -590,7 +594,6 @@ where /// Generate RPC module using provided configuration pub fn gen_rpc_module( - deny_unsafe: DenyUnsafe, spawn_handle: SpawnTaskHandle, client: Arc, transaction_pool: Arc, @@ -598,7 +601,7 @@ pub fn gen_rpc_module( system_rpc_tx: TracingUnboundedSender>, config: &Configuration, backend: Arc, - rpc_builder: &(dyn Fn(DenyUnsafe, SubscriptionTaskExecutor) -> Result, Error>), + rpc_builder: &(dyn Fn(SubscriptionTaskExecutor) -> Result, Error>), ) -> Result, Error> where TBl: BlockT, @@ -633,8 +636,7 @@ where let (chain, state, child_state) = { let chain = sc_rpc::chain::new_full(client.clone(), task_executor.clone()).into_rpc(); - let (state, child_state) = - sc_rpc::state::new_full(client.clone(), task_executor.clone(), deny_unsafe); + let (state, child_state) = sc_rpc::state::new_full(client.clone(), task_executor.clone()); let state = state.into_rpc(); let child_state = child_state.into_rpc(); @@ -698,15 +700,14 @@ where client.clone(), transaction_pool, keystore, - deny_unsafe, task_executor.clone(), ) .into_rpc(); - let system = sc_rpc::system::System::new(system_info, system_rpc_tx, deny_unsafe).into_rpc(); + let system = sc_rpc::system::System::new(system_info, system_rpc_tx).into_rpc(); if let Some(storage) = backend.offchain_storage() { - let offchain = sc_rpc::offchain::Offchain::new(storage, deny_unsafe).into_rpc(); + let offchain = sc_rpc::offchain::Offchain::new(storage).into_rpc(); rpc_api.merge(offchain).map_err(|e| Error::Application(e.into()))?; } @@ -726,7 +727,7 @@ where rpc_api.merge(state).map_err(|e| Error::Application(e.into()))?; rpc_api.merge(child_state).map_err(|e| Error::Application(e.into()))?; // Additional [`RpcModule`]s defined in the node to fit the specific blockchain - let extra_rpcs = rpc_builder(deny_unsafe, task_executor.clone())?; + let extra_rpcs = rpc_builder(task_executor.clone())?; rpc_api.merge(extra_rpcs).map_err(|e| Error::Application(e.into()))?; Ok(rpc_api) diff --git a/substrate/client/service/src/config.rs b/substrate/client/service/src/config.rs index f8d8511f2718..8387f818e68b 100644 --- a/substrate/client/service/src/config.rs +++ b/substrate/client/service/src/config.rs @@ -33,7 +33,9 @@ pub use sc_network::{ }, Multiaddr, }; -pub use sc_rpc_server::IpNetwork; +pub use sc_rpc_server::{ + IpNetwork, RpcEndpoint, RpcMethods, SubscriptionIdProvider as RpcSubscriptionIdProvider, +}; pub use sc_telemetry::TelemetryEndpoints; pub use sc_transaction_pool::Options as TransactionPoolOptions; use sp_core::crypto::SecretString; @@ -82,8 +84,8 @@ pub struct Configuration { /// over on-chain runtimes when the spec version matches. Set to `None` to /// disable overrides (default). pub wasm_runtime_overrides: Option, - /// JSON-RPC server binding address. - pub rpc_addr: Option, + /// JSON-RPC server endpoints. + pub rpc_addr: Option>, /// Maximum number of connections for JSON-RPC server. pub rpc_max_connections: u32, /// CORS settings for HTTP & WS servers. `None` if all origins are allowed. @@ -97,7 +99,7 @@ pub struct Configuration { /// Custom JSON-RPC subscription ID provider. /// /// Default: [`crate::RandomStringSubscriptionId`]. - pub rpc_id_provider: Option>, + pub rpc_id_provider: Option>, /// Maximum allowed subscriptions per rpc connection pub rpc_max_subs_per_conn: u32, /// JSON-RPC server default port. @@ -255,24 +257,6 @@ impl Configuration { } } -/// Available RPC methods. -#[derive(Debug, Copy, Clone)] -pub enum RpcMethods { - /// Expose every RPC method only when RPC is listening on `localhost`, - /// otherwise serve only safe RPC methods. - Auto, - /// Allow only a safe subset of RPC methods. - Safe, - /// Expose every RPC method (even potentially unsafe ones). - Unsafe, -} - -impl Default for RpcMethods { - fn default() -> RpcMethods { - RpcMethods::Auto - } -} - #[static_init::dynamic(drop, lazy)] static mut BASE_PATH_TEMP: Option = None; diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index 5fb304edd7e1..9e8aed39c897 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -34,7 +34,10 @@ mod client; mod metrics; mod task_manager; -use std::{collections::HashMap, net::SocketAddr}; +use std::{ + collections::HashMap, + net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, +}; use codec::{Decode, Encode}; use futures::{pin_mut, FutureExt, StreamExt}; @@ -85,9 +88,7 @@ pub use sc_executor::NativeExecutionDispatch; pub use sc_network_sync::WarpSyncConfig; #[doc(hidden)] pub use sc_network_transactions::config::{TransactionImport, TransactionImportFuture}; -pub use sc_rpc::{ - RandomIntegerSubscriptionId, RandomStringSubscriptionId, RpcSubscriptionIdProvider, -}; +pub use sc_rpc::{RandomIntegerSubscriptionId, RandomStringSubscriptionId}; pub use sc_tracing::TracingReceiver; pub use sc_transaction_pool::Options as TransactionPoolOptions; pub use sc_transaction_pool_api::{error::IntoPoolError, InPoolTransaction, TransactionPool}; @@ -377,45 +378,64 @@ mod waiting { pub fn start_rpc_servers( config: &Configuration, gen_rpc_module: R, - rpc_id_provider: Option>, + rpc_id_provider: Option>, ) -> Result, error::Error> where - R: Fn(sc_rpc::DenyUnsafe) -> Result, Error>, + R: Fn() -> Result, Error>, { - fn deny_unsafe(addr: SocketAddr, methods: &RpcMethods) -> sc_rpc::DenyUnsafe { - let is_exposed_addr = !addr.ip().is_loopback(); - match (is_exposed_addr, methods) { - | (_, RpcMethods::Unsafe) | (false, RpcMethods::Auto) => sc_rpc::DenyUnsafe::No, - _ => sc_rpc::DenyUnsafe::Yes, - } - } - - // if binding the specified port failed then a random port is assigned by the OS. - let backup_port = |mut addr: SocketAddr| { - addr.set_port(0); - addr + let endpoints: Vec = if let Some(endpoints) = + config.rpc_addr.as_ref() + { + endpoints.clone() + } else { + let ipv6 = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::LOCALHOST, config.rpc_port, 0, 0)); + let ipv4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, config.rpc_port)); + + vec![ + sc_rpc_server::RpcEndpoint { + batch_config: config.rpc_batch_config, + cors: config.rpc_cors.clone(), + listen_addr: ipv4, + max_buffer_capacity_per_connection: config.rpc_message_buffer_capacity, + max_connections: config.rpc_max_connections, + max_payload_in_mb: config.rpc_max_request_size, + max_payload_out_mb: config.rpc_max_response_size, + max_subscriptions_per_connection: config.rpc_max_subs_per_conn, + rpc_methods: config.rpc_methods.into(), + rate_limit: config.rpc_rate_limit, + rate_limit_trust_proxy_headers: config.rpc_rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: config.rpc_rate_limit_whitelisted_ips.clone(), + retry_random_port: true, + is_optional: false, + }, + sc_rpc_server::RpcEndpoint { + batch_config: config.rpc_batch_config, + cors: config.rpc_cors.clone(), + listen_addr: ipv6, + max_buffer_capacity_per_connection: config.rpc_message_buffer_capacity, + max_connections: config.rpc_max_connections, + max_payload_in_mb: config.rpc_max_request_size, + max_payload_out_mb: config.rpc_max_response_size, + max_subscriptions_per_connection: config.rpc_max_subs_per_conn, + rpc_methods: config.rpc_methods.into(), + rate_limit: config.rpc_rate_limit, + rate_limit_trust_proxy_headers: config.rpc_rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: config.rpc_rate_limit_whitelisted_ips.clone(), + retry_random_port: true, + is_optional: true, + }, + ] }; - let addr = config.rpc_addr.unwrap_or_else(|| ([127, 0, 0, 1], config.rpc_port).into()); - let backup_addr = backup_port(addr); let metrics = sc_rpc_server::RpcMetrics::new(config.prometheus_registry())?; + let rpc_api = gen_rpc_module()?; let server_config = sc_rpc_server::Config { - addrs: [addr, backup_addr], - batch_config: config.rpc_batch_config, - max_connections: config.rpc_max_connections, - max_payload_in_mb: config.rpc_max_request_size, - max_payload_out_mb: config.rpc_max_response_size, - max_subs_per_conn: config.rpc_max_subs_per_conn, - message_buffer_capacity: config.rpc_message_buffer_capacity, - rpc_api: gen_rpc_module(deny_unsafe(addr, &config.rpc_methods))?, + endpoints, + rpc_api, metrics, id_provider: rpc_id_provider, - cors: config.rpc_cors.as_ref(), tokio_handle: config.tokio_handle.clone(), - rate_limit: config.rpc_rate_limit, - rate_limit_whitelisted_ips: config.rpc_rate_limit_whitelisted_ips.clone(), - rate_limit_trust_proxy_headers: config.rpc_rate_limit_trust_proxy_headers, }; // TODO: https://github.com/paritytech/substrate/issues/13773 diff --git a/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs b/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs index c0333bb7dac0..c455d8d39b7d 100644 --- a/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs +++ b/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs @@ -21,8 +21,9 @@ use jsonrpsee::{ core::RpcResult, proc_macros::rpc, types::error::{ErrorCode, ErrorObject, ErrorObjectOwned}, + Extensions, }; -use sc_rpc_api::DenyUnsafe; +use sc_rpc_api::check_if_safe; use serde::{Deserialize, Serialize}; use sp_runtime::traits::Block as BlockT; use std::sync::Arc; @@ -134,7 +135,7 @@ pub trait StateMigrationApi { /// This call is performed locally without submitting any transactions. Thus executing this /// won't change any state. Nonetheless it is a VERY costly call that should be /// only exposed to trusted peers. - #[method(name = "state_trieMigrationStatus")] + #[method(name = "state_trieMigrationStatus", with_extensions)] fn call(&self, at: Option) -> RpcResult; } @@ -142,14 +143,13 @@ pub trait StateMigrationApi { pub struct StateMigration { client: Arc, backend: Arc, - deny_unsafe: DenyUnsafe, _marker: std::marker::PhantomData<(B, BA)>, } impl StateMigration { /// Create new state migration rpc for the given reference to the client. - pub fn new(client: Arc, backend: Arc, deny_unsafe: DenyUnsafe) -> Self { - StateMigration { client, backend, deny_unsafe, _marker: Default::default() } + pub fn new(client: Arc, backend: Arc) -> Self { + StateMigration { client, backend, _marker: Default::default() } } } @@ -159,8 +159,12 @@ where C: Send + Sync + 'static + sc_client_api::HeaderBackend, BA: 'static + sc_client_api::backend::Backend, { - fn call(&self, at: Option<::Hash>) -> RpcResult { - self.deny_unsafe.check_if_safe()?; + fn call( + &self, + ext: &Extensions, + at: Option<::Hash>, + ) -> RpcResult { + check_if_safe(ext)?; let hash = at.unwrap_or_else(|| self.client.info().best_hash); let state = self.backend.state_at(hash).map_err(error_into_rpc_err)?; diff --git a/substrate/utils/frame/rpc/system/src/lib.rs b/substrate/utils/frame/rpc/system/src/lib.rs index 8cb7b785bc7c..9fcaa53a35d8 100644 --- a/substrate/utils/frame/rpc/system/src/lib.rs +++ b/substrate/utils/frame/rpc/system/src/lib.rs @@ -24,9 +24,9 @@ use jsonrpsee::{ core::{async_trait, RpcResult}, proc_macros::rpc, types::error::ErrorObject, + Extensions, }; -use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::{InPoolTransaction, TransactionPool}; use sp_api::ApiExt; use sp_block_builder::BlockBuilder; @@ -49,7 +49,7 @@ pub trait SystemApi { async fn nonce(&self, account: AccountId) -> RpcResult; /// Dry run an extrinsic at a given block. Return SCALE encoded ApplyExtrinsicResult. - #[method(name = "system_dryRun", aliases = ["system_dryRunAt"])] + #[method(name = "system_dryRun", aliases = ["system_dryRunAt"], with_extensions)] async fn dry_run(&self, extrinsic: Bytes, at: Option) -> RpcResult; } @@ -74,14 +74,13 @@ impl From for i32 { pub struct System { client: Arc, pool: Arc

, - deny_unsafe: DenyUnsafe, _marker: std::marker::PhantomData, } impl System { /// Create new `FullSystem` given client and transaction pool. - pub fn new(client: Arc, pool: Arc

, deny_unsafe: DenyUnsafe) -> Self { - Self { client, pool, deny_unsafe, _marker: Default::default() } + pub fn new(client: Arc, pool: Arc

) -> Self { + Self { client, pool, _marker: Default::default() } } } @@ -115,10 +114,12 @@ where async fn dry_run( &self, + ext: &Extensions, extrinsic: Bytes, at: Option<::Hash>, ) -> RpcResult { - self.deny_unsafe.check_if_safe()?; + sc_rpc_api::check_if_safe(ext)?; + let api = self.client.runtime_api(); let best_hash = at.unwrap_or_else(|| // If the block hash is not supplied assume the best block. @@ -217,6 +218,7 @@ mod tests { use assert_matches::assert_matches; use futures::executor::block_on; + use sc_rpc_api::DenyUnsafe; use sc_transaction_pool::BasicPool; use sp_runtime::{ transaction_validity::{InvalidTransaction, TransactionValidityError}, @@ -224,6 +226,18 @@ mod tests { }; use substrate_test_runtime_client::{runtime::Transfer, AccountKeyring}; + fn deny_unsafe() -> Extensions { + let mut ext = Extensions::new(); + ext.insert(DenyUnsafe::Yes); + ext + } + + fn allow_unsafe() -> Extensions { + let mut ext = Extensions::new(); + ext.insert(DenyUnsafe::No); + ext + } + #[tokio::test] async fn should_return_next_nonce_for_some_account() { sp_tracing::try_init_simple(); @@ -251,7 +265,7 @@ mod tests { let ext1 = new_transaction(1); block_on(pool.submit_one(hash_of_block0, source, ext1)).unwrap(); - let accounts = System::new(client, pool, DenyUnsafe::Yes); + let accounts = System::new(client, pool); // when let nonce = accounts.nonce(AccountKeyring::Alice.into()).await; @@ -270,10 +284,10 @@ mod tests { let pool = BasicPool::new_full(Default::default(), true.into(), None, spawner, client.clone()); - let accounts = System::new(client, pool, DenyUnsafe::Yes); + let accounts = System::new(client, pool); // when - let res = accounts.dry_run(vec![].into(), None).await; + let res = accounts.dry_run(&deny_unsafe(), vec![].into(), None).await; assert_matches!(res, Err(e) => { assert!(e.message().contains("RPC call is unsafe to be called externally")); }); @@ -289,7 +303,7 @@ mod tests { let pool = BasicPool::new_full(Default::default(), true.into(), None, spawner, client.clone()); - let accounts = System::new(client, pool, DenyUnsafe::No); + let accounts = System::new(client, pool); let tx = Transfer { from: AccountKeyring::Alice.into(), @@ -300,7 +314,10 @@ mod tests { .into_unchecked_extrinsic(); // when - let bytes = accounts.dry_run(tx.encode().into(), None).await.expect("Call is successful"); + let bytes = accounts + .dry_run(&allow_unsafe(), tx.encode().into(), None) + .await + .expect("Call is successful"); // then let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_ref()).unwrap(); @@ -317,7 +334,7 @@ mod tests { let pool = BasicPool::new_full(Default::default(), true.into(), None, spawner, client.clone()); - let accounts = System::new(client, pool, DenyUnsafe::No); + let accounts = System::new(client, pool); let tx = Transfer { from: AccountKeyring::Alice.into(), @@ -328,7 +345,10 @@ mod tests { .into_unchecked_extrinsic(); // when - let bytes = accounts.dry_run(tx.encode().into(), None).await.expect("Call is successful"); + let bytes = accounts + .dry_run(&allow_unsafe(), tx.encode().into(), None) + .await + .expect("Call is successful"); // then let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_ref()).unwrap(); diff --git a/templates/minimal/node/Cargo.toml b/templates/minimal/node/Cargo.toml index da5073ea687c..c6bd89d23370 100644 --- a/templates/minimal/node/Cargo.toml +++ b/templates/minimal/node/Cargo.toml @@ -30,7 +30,6 @@ sc-transaction-pool = { workspace = true, default-features = true } sc-transaction-pool-api = { workspace = true, default-features = true } sc-consensus = { workspace = true, default-features = true } sc-consensus-manual-seal = { workspace = true, default-features = true } -sc-rpc-api = { workspace = true, default-features = true } sc-basic-authorship = { workspace = true, default-features = true } sc-offchain = { workspace = true, default-features = true } sc-client-api = { workspace = true, default-features = true } diff --git a/templates/minimal/node/src/rpc.rs b/templates/minimal/node/src/rpc.rs index 451e7b21dd0c..f51dfe1a1ace 100644 --- a/templates/minimal/node/src/rpc.rs +++ b/templates/minimal/node/src/rpc.rs @@ -28,16 +28,12 @@ use sc_transaction_pool_api::TransactionPool; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use std::sync::Arc; -pub use sc_rpc_api::DenyUnsafe; - /// Full client dependencies. pub struct FullDeps { /// The client instance to use. pub client: Arc, /// Transaction pool instance. pub pool: Arc

, - /// Whether to deny unsafe calls - pub deny_unsafe: DenyUnsafe, } #[docify::export] @@ -59,9 +55,9 @@ where { use substrate_frame_rpc_system::{System, SystemApiServer}; let mut module = RpcModule::new(()); - let FullDeps { client, pool, deny_unsafe } = deps; + let FullDeps { client, pool } = deps; - module.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?; + module.merge(System::new(client.clone(), pool.clone()).into_rpc())?; Ok(module) } diff --git a/templates/minimal/node/src/service.rs b/templates/minimal/node/src/service.rs index decb9d6c636e..d362ae36d23a 100644 --- a/templates/minimal/node/src/service.rs +++ b/templates/minimal/node/src/service.rs @@ -168,9 +168,8 @@ pub fn new_full::Ha let client = client.clone(); let pool = transaction_pool.clone(); - Box::new(move |deny_unsafe, _| { - let deps = - crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }; + Box::new(move |_| { + let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone() }; crate::rpc::create_full(deps).map_err(Into::into) }) }; diff --git a/templates/parachain/node/src/command.rs b/templates/parachain/node/src/command.rs index eba7fdcdae71..f346ae4386a6 100644 --- a/templates/parachain/node/src/command.rs +++ b/templates/parachain/node/src/command.rs @@ -1,5 +1,3 @@ -use std::net::SocketAddr; - use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; use cumulus_primitives_core::ParaId; use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; @@ -7,7 +5,7 @@ use log::info; use parachain_template_runtime::Block; use sc_cli::{ ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, - NetworkParams, Result, SharedParams, SubstrateCli, + NetworkParams, Result, RpcEndpoint, SharedParams, SubstrateCli, }; use sc_service::config::{BasePath, PrometheusConfig}; @@ -297,7 +295,7 @@ impl CliConfiguration for RelayChainCli { .or_else(|| self.base_path.clone().map(Into::into))) } - fn rpc_addr(&self, default_listen_port: u16) -> Result> { + fn rpc_addr(&self, default_listen_port: u16) -> Result>> { self.base.base.rpc_addr(default_listen_port) } diff --git a/templates/parachain/node/src/rpc.rs b/templates/parachain/node/src/rpc.rs index bb52b974f0ce..4937469e11e2 100644 --- a/templates/parachain/node/src/rpc.rs +++ b/templates/parachain/node/src/rpc.rs @@ -9,7 +9,6 @@ use std::sync::Arc; use parachain_template_runtime::{opaque::Block, AccountId, Balance, Nonce}; -pub use sc_rpc::DenyUnsafe; use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; @@ -24,8 +23,6 @@ pub struct FullDeps { pub client: Arc, /// Transaction pool instance. pub pool: Arc

, - /// Whether to deny unsafe calls - pub deny_unsafe: DenyUnsafe, } /// Instantiate all RPC extensions. @@ -48,9 +45,9 @@ where use substrate_frame_rpc_system::{System, SystemApiServer}; let mut module = RpcExtension::new(()); - let FullDeps { client, pool, deny_unsafe } = deps; + let FullDeps { client, pool } = deps; - module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; + module.merge(System::new(client.clone(), pool).into_rpc())?; module.merge(TransactionPayment::new(client).into_rpc())?; Ok(module) } diff --git a/templates/parachain/node/src/service.rs b/templates/parachain/node/src/service.rs index 88f2710a5cb8..b46b6ecfde18 100644 --- a/templates/parachain/node/src/service.rs +++ b/templates/parachain/node/src/service.rs @@ -305,12 +305,9 @@ pub async fn start_parachain_node( let client = client.clone(); let transaction_pool = transaction_pool.clone(); - Box::new(move |deny_unsafe, _| { - let deps = crate::rpc::FullDeps { - client: client.clone(), - pool: transaction_pool.clone(), - deny_unsafe, - }; + Box::new(move |_| { + let deps = + crate::rpc::FullDeps { client: client.clone(), pool: transaction_pool.clone() }; crate::rpc::create_full(deps).map_err(Into::into) }) diff --git a/templates/solochain/node/Cargo.toml b/templates/solochain/node/Cargo.toml index 9dd1b144d229..8f74c6b3cb55 100644 --- a/templates/solochain/node/Cargo.toml +++ b/templates/solochain/node/Cargo.toml @@ -36,7 +36,6 @@ sc-consensus = { workspace = true, default-features = true } sc-consensus-grandpa = { workspace = true, default-features = true } sp-consensus-grandpa = { workspace = true, default-features = true } sc-client-api = { workspace = true, default-features = true } -sc-rpc-api = { workspace = true, default-features = true } sc-basic-authorship = { workspace = true, default-features = true } # substrate primitives diff --git a/templates/solochain/node/src/rpc.rs b/templates/solochain/node/src/rpc.rs index fe2b6ca72ede..1fc6eb0127e9 100644 --- a/templates/solochain/node/src/rpc.rs +++ b/templates/solochain/node/src/rpc.rs @@ -14,16 +14,12 @@ use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; -pub use sc_rpc_api::DenyUnsafe; - /// Full client dependencies. pub struct FullDeps { /// The client instance to use. pub client: Arc, /// Transaction pool instance. pub pool: Arc

, - /// Whether to deny unsafe calls - pub deny_unsafe: DenyUnsafe, } /// Instantiate all full RPC extensions. @@ -43,9 +39,9 @@ where use substrate_frame_rpc_system::{System, SystemApiServer}; let mut module = RpcModule::new(()); - let FullDeps { client, pool, deny_unsafe } = deps; + let FullDeps { client, pool } = deps; - module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; + module.merge(System::new(client.clone(), pool).into_rpc())?; module.merge(TransactionPayment::new(client).into_rpc())?; // Extend this RPC with a custom API by using the following syntax. diff --git a/templates/solochain/node/src/service.rs b/templates/solochain/node/src/service.rs index 7eef9766b107..2b43ecfa1ce2 100644 --- a/templates/solochain/node/src/service.rs +++ b/templates/solochain/node/src/service.rs @@ -212,9 +212,8 @@ pub fn new_full< let client = client.clone(); let pool = transaction_pool.clone(); - Box::new(move |deny_unsafe, _| { - let deps = - crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }; + Box::new(move |_| { + let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone() }; crate::rpc::create_full(deps).map_err(Into::into) }) }; From ef3a0d8f118d381fba1f828209eababe9c24f051 Mon Sep 17 00:00:00 2001 From: A Ahmad <57046949+abbaahmad@users.noreply.github.com> Date: Wed, 28 Aug 2024 14:01:12 +0100 Subject: [PATCH 174/382] IBP Coretime Polkadot bootnodes (#5499) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✄ ----------------------------------------------------------------------------- Thank you for your Pull Request! 🙏 Please make sure it follows the contribution guidelines outlined in [this document](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CONTRIBUTING.md) and fill out the sections below. Once you're ready to submit your PR for review, please delete this section and leave only the text under the "Description" heading. # Description *A concise description of what your PR is doing, and what potential issue it is solving. Use [Github semantic linking](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) to link the PR to an issue that must be closed once this is merged.* ## Integration *In depth notes about how this PR should be integrated by downstream projects. This part is mandatory, and should be reviewed by reviewers, if the PR does NOT have the `R0-Silent` label. In case of a `R0-Silent`, it can be ignored.* ## Review Notes *In depth notes about the **implementation** details of your PR. This should be the main guide for reviewers to understand your approach and effectively review it. If too long, use [`

`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)*. *Imagine that someone who is depending on the old code wants to integrate your new code and the only information that they get is this section. It helps to include example usage and default value here, with a `diff` code-block to show possibly integration.* *Include your leftover TODOs, if any, here.* # Checklist * [ ] My PR includes a detailed description as outlined in the "Description" and its two subsections above. * [ ] My PR follows the [labeling requirements]( https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CONTRIBUTING.md#Process ) of this project (at minimum one label for `T` required) * External contributors: ask maintainers to put the right label on your PR. * [ ] I have made corresponding changes to the documentation (if applicable) * [ ] I have added tests that prove my fix is effective or that my feature works (if applicable) You can remove the "Checklist" section once all have been checked. Thank you for your contribution! ✄ ----------------------------------------------------------------------------- Co-authored-by: Dónal Murray --- cumulus/parachains/chain-specs/coretime-polkadot.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cumulus/parachains/chain-specs/coretime-polkadot.json b/cumulus/parachains/chain-specs/coretime-polkadot.json index 73e104b38290..806231db7646 100644 --- a/cumulus/parachains/chain-specs/coretime-polkadot.json +++ b/cumulus/parachains/chain-specs/coretime-polkadot.json @@ -8,7 +8,9 @@ "/dns/polkadot-coretime-connect-a-0.polkadot.io/tcp/443/wss/p2p/12D3KooWKjnixAHbKMsPTJwGx8SrBeGEJLHA8KmKcEDYMp3YmWgR", "/dns/polkadot-coretime-connect-a-1.polkadot.io/tcp/443/wss/p2p/12D3KooWQ7B7p4DFv1jWqaKfhrZBcMmi5g8bWFnmskguLaGEmT6n", "/dns4/coretime-polkadot.boot.stake.plus/tcp/30332/wss/p2p/12D3KooWFJ2yBTKFKYwgKUjfY3F7XfaxHV8hY6fbJu5oMkpP7wZ9", - "/dns4/coretime-polkadot.boot.stake.plus/tcp/31332/wss/p2p/12D3KooWCy5pToLafcQzPHn5kadxAftmF6Eh8ZJGPXhSeXSUDfjv" + "/dns4/coretime-polkadot.boot.stake.plus/tcp/31332/wss/p2p/12D3KooWCy5pToLafcQzPHn5kadxAftmF6Eh8ZJGPXhSeXSUDfjv", + "/dns/coretime-polkadot-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWGpmytHjdthrkKgkXDZyKm9ABtJ2PtGk9NStJDG4pChy9", + "/dns/coretime-polkadot-boot-ng.dwellir.com/tcp/30361/p2p/12D3KooWGpmytHjdthrkKgkXDZyKm9ABtJ2PtGk9NStJDG4pChy9" ], "telemetryEndpoints": null, "protocolId": null, From 9cf5e8105f0142abf5302bf9be9cffb0106731bf Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Wed, 28 Aug 2024 15:08:08 +0200 Subject: [PATCH 175/382] Use umbrella crate for minimal template (#5155) Co-authored-by: kianenigma Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- Cargo.lock | 35 +--- Cargo.toml | 2 +- bridges/relays/utils/Cargo.toml | 2 +- cumulus/client/consensus/proposer/Cargo.toml | 2 +- docs/sdk/Cargo.toml | 2 +- .../chain_spec_runtime/src/runtime.rs | 5 +- prdoc/pr_5155.prdoc | 27 +++ scripts/generate-umbrella.py | 8 +- substrate/bin/node/runtime/Cargo.toml | 2 +- substrate/frame/contracts/fixtures/Cargo.toml | 4 +- substrate/frame/revive/fixtures/Cargo.toml | 6 +- substrate/frame/src/lib.rs | 27 ++- .../test/tests/pallet_outer_enums_explicit.rs | 6 +- .../test/tests/pallet_outer_enums_implicit.rs | 6 +- .../primitives/wasm-interface/Cargo.toml | 6 +- templates/minimal/node/Cargo.toml | 33 +--- templates/minimal/node/build.rs | 2 +- templates/minimal/node/src/chain_spec.rs | 9 +- templates/minimal/node/src/cli.rs | 2 +- templates/minimal/node/src/command.rs | 3 +- templates/minimal/node/src/main.rs | 2 +- templates/minimal/node/src/rpc.rs | 9 +- templates/minimal/node/src/service.rs | 15 +- templates/minimal/pallets/template/Cargo.toml | 14 +- templates/minimal/pallets/template/src/lib.rs | 4 +- templates/minimal/runtime/Cargo.toml | 42 ++--- templates/minimal/runtime/build.rs | 2 +- templates/minimal/runtime/src/lib.rs | 24 +-- umbrella/Cargo.toml | 172 +----------------- 29 files changed, 136 insertions(+), 337 deletions(-) create mode 100644 prdoc/pr_5155.prdoc diff --git a/Cargo.lock b/Cargo.lock index 8bf4c356397d..885e2b4b4cf3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8726,47 +8726,18 @@ dependencies = [ "futures-timer", "jsonrpsee", "minimal-template-runtime", - "polkadot-sdk-frame", - "sc-basic-authorship", - "sc-cli", - "sc-client-api", - "sc-consensus", - "sc-consensus-manual-seal", - "sc-executor", - "sc-network", - "sc-offchain", - "sc-service", - "sc-telemetry", - "sc-transaction-pool", - "sc-transaction-pool-api", + "polkadot-sdk", "serde_json", - "sp-api", - "sp-block-builder", - "sp-blockchain", - "sp-io", - "sp-keyring", - "sp-runtime", - "sp-timestamp", - "substrate-build-script-utils", - "substrate-frame-rpc-system", ] [[package]] name = "minimal-template-runtime" version = "0.0.0" dependencies = [ - "pallet-balances", "pallet-minimal-template", - "pallet-sudo", - "pallet-timestamp", - "pallet-transaction-payment", - "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", - "polkadot-sdk-frame", + "polkadot-sdk", "scale-info", - "sp-genesis-builder", - "sp-runtime", - "substrate-wasm-builder", ] [[package]] @@ -10957,7 +10928,7 @@ name = "pallet-minimal-template" version = "0.0.0" dependencies = [ "parity-scale-codec", - "polkadot-sdk-frame", + "polkadot-sdk", "scale-info", ] diff --git a/Cargo.toml b/Cargo.toml index a181a935ea06..9d39962f8974 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -584,7 +584,7 @@ ahash = { version = "0.8.2" } alloy-primitives = { version = "0.4.2", default-features = false } alloy-sol-types = { version = "0.4.2", default-features = false } always-assert = { version = "0.1" } -anyhow = { version = "1.0.81" } +anyhow = { version = "1.0.81", default-features = false } aquamarine = { version = "0.5.0" } arbitrary = { version = "1.3.2" } ark-bls12-377 = { version = "0.4.0", default-features = false } diff --git a/bridges/relays/utils/Cargo.toml b/bridges/relays/utils/Cargo.toml index beb03b9381d4..4c25566607dc 100644 --- a/bridges/relays/utils/Cargo.toml +++ b/bridges/relays/utils/Cargo.toml @@ -11,7 +11,7 @@ publish = false workspace = true [dependencies] -anyhow = { workspace = true } +anyhow = { workspace = true, default-features = true } async-std = { workspace = true } async-trait = { workspace = true } backoff = { workspace = true } diff --git a/cumulus/client/consensus/proposer/Cargo.toml b/cumulus/client/consensus/proposer/Cargo.toml index ce91d48bf589..bb760ae03f4d 100644 --- a/cumulus/client/consensus/proposer/Cargo.toml +++ b/cumulus/client/consensus/proposer/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" workspace = true [dependencies] -anyhow = { workspace = true } +anyhow = { workspace = true, default-features = true } async-trait = { workspace = true } thiserror = { workspace = true } diff --git a/docs/sdk/Cargo.toml b/docs/sdk/Cargo.toml index 424758c32b34..adc1c1a8efbc 100644 --- a/docs/sdk/Cargo.toml +++ b/docs/sdk/Cargo.toml @@ -31,7 +31,7 @@ simple-mermaid = "0.1.1" docify = { workspace = true } # Polkadot SDK deps, typically all should only be in scope such that we can link to their doc item. -polkadot-sdk = { features = ["runtime"], workspace = true, default-features = true } +polkadot-sdk = { features = ["runtime-full"], workspace = true, default-features = true } node-cli = { workspace = true } kitchensink-runtime = { workspace = true } chain-spec-builder = { workspace = true, default-features = true } diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs index c45f0126337e..195d1b124474 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs @@ -32,10 +32,7 @@ use frame::{ runtime, }, prelude::*, - runtime::{ - apis::{self, impl_runtime_apis, ExtrinsicInclusionMode}, - prelude::*, - }, + runtime::{apis, prelude::*}, }; use sp_genesis_builder::PresetId; diff --git a/prdoc/pr_5155.prdoc b/prdoc/pr_5155.prdoc new file mode 100644 index 000000000000..373522eea1c3 --- /dev/null +++ b/prdoc/pr_5155.prdoc @@ -0,0 +1,27 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Use umbrella crate for minimal template + +doc: + - audience: Runtime Dev + description: | + Minor additions to the `polkadot-sdk-frame` crate and making it ready for usage in more templates. This PR already integrates it in the minimal template. + + +crates: + - name: polkadot-sdk + bump: major + - name: polkadot-sdk-frame + bump: patch + - name: sp-wasm-interface + bump: patch + - name: pallet-revive + bump: patch + - name: pallet-revive-fixtures + bump: patch + - name: frame-support + bump: patch + - name: pallet-balances + bump: patch + diff --git a/scripts/generate-umbrella.py b/scripts/generate-umbrella.py index 5b9cc89c5308..e1ef6de86f9c 100644 --- a/scripts/generate-umbrella.py +++ b/scripts/generate-umbrella.py @@ -86,6 +86,8 @@ def main(path, version): # Sort by name std_crates.sort(key=lambda x: x[0].name) nostd_crates.sort(key=lambda x: x[0].name) + + runtime_crates = [crate for crate in nostd_crates if 'frame' in crate[0].name or crate[0].name.startswith('sp-')] all_crates = std_crates + nostd_crates all_crates.sort(key=lambda x: x[0].name) dependencies = {} @@ -105,7 +107,8 @@ def main(path, version): "serde": [], "experimental": [], "with-tracing": [], - "runtime": list([f"{d.name}" for d, _ in nostd_crates]), + "runtime-full": list([f"{d.name}" for d, _ in nostd_crates]), + "runtime": list([f"{d.name}" for d, _ in runtime_crates]), "node": ["std"] + list([f"{d.name}" for d, _ in std_crates]), "tuples-96": [], "riscv": [], @@ -120,7 +123,7 @@ def main(path, version): "description": "Polkadot SDK umbrella crate.", "license": "Apache-2.0", "metadata": { "docs": { "rs": { - "features": ["runtime", "node"], + "features": ["runtime-full", "node"], "targets": ["x86_64-unknown-linux-gnu"] }}} }, @@ -204,3 +207,4 @@ def parse_args(): if __name__ == "__main__": args = parse_args() main(args.sdk, args.version) + diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index c262d74fa8a8..6310e16d5a14 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -31,7 +31,7 @@ serde_json = { features = ["alloc", "arbitrary_precision"], workspace = true } # pallet-asset-conversion: turn on "num-traits" feature primitive-types = { features = ["codec", "num-traits", "scale-info"], workspace = true } -polkadot-sdk = { features = ["runtime", "tuples-96"], workspace = true } +polkadot-sdk = { features = ["runtime-full", "tuples-96"], workspace = true } # shared code between runtime and node node-primitives = { workspace = true } diff --git a/substrate/frame/contracts/fixtures/Cargo.toml b/substrate/frame/contracts/fixtures/Cargo.toml index 6b0751571cc9..6cb6447d8fd7 100644 --- a/substrate/frame/contracts/fixtures/Cargo.toml +++ b/substrate/frame/contracts/fixtures/Cargo.toml @@ -13,7 +13,7 @@ workspace = true [dependencies] frame-system = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } -anyhow = { workspace = true } +anyhow = { workspace = true, default-features = true } [build-dependencies] parity-wasm = { workspace = true } @@ -21,7 +21,7 @@ tempfile = { workspace = true } toml = { workspace = true } twox-hash = { workspace = true, default-features = true } polkavm-linker = { workspace = true, optional = true } -anyhow = { workspace = true } +anyhow = { workspace = true, default-features = true } [features] riscv = ["polkavm-linker"] diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index 1b668c061f85..9e54acdace70 100644 --- a/substrate/frame/revive/fixtures/Cargo.toml +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -29,4 +29,8 @@ default = ["std"] # we will remove this once there is an upstream toolchain riscv = [] # only when std is enabled all fixtures are available -std = ["frame-system", "sp-runtime"] +std = [ + "anyhow/std", + "frame-system", + "sp-runtime", +] diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index 235d75a4b752..e5fb15cdd07c 100644 --- a/substrate/frame/src/lib.rs +++ b/substrate/frame/src/lib.rs @@ -177,8 +177,15 @@ pub mod runtime { pub use frame_executive::*; /// Macro to amalgamate the runtime into `struct Runtime`. + /// + /// Consider using the new version of this [`frame_construct_runtime`]. pub use frame_support::construct_runtime; + /// Macro to amalgamate the runtime into `struct Runtime`. + /// + /// This is the newer version of [`construct_runtime`]. + pub use frame_support::runtime as frame_construct_runtime; + /// Macro to easily derive the `Config` trait of various pallet for `Runtime`. pub use frame_support::derive_impl; @@ -186,12 +193,18 @@ pub mod runtime { // TODO: using linking in the Get in the line above triggers an ICE :/ pub use frame_support::{ord_parameter_types, parameter_types}; + /// For building genesis config. + pub use frame_support::genesis_builder_helper::{build_state, get_preset}; + /// Const types that can easily be used in conjuncture with `Get`. pub use frame_support::traits::{ ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, ConstU32, ConstU64, ConstU8, }; + /// Used for simple fee calculation. + pub use frame_support::weights::{self, FixedFee, NoFee}; + /// Primary types used to parameterize `EnsureOrigin` and `EnsureRootWithArg`. pub use frame_system::{ EnsureNever, EnsureNone, EnsureRoot, EnsureRootWithSuccess, EnsureSigned, @@ -201,11 +214,16 @@ pub mod runtime { /// Types to define your runtime version. pub use sp_version::{create_runtime_str, runtime_version, RuntimeVersion}; + #[cfg(feature = "std")] + pub use sp_version::NativeVersion; + /// Macro to implement runtime APIs. pub use sp_api::impl_runtime_apis; - #[cfg(feature = "std")] - pub use sp_version::NativeVersion; + // Types often used in the runtime APIs. + pub use sp_core::OpaqueMetadata; + pub use sp_inherents::{CheckInherentsResult, InherentData}; + pub use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode}; } /// Types and traits for runtimes that implement runtime APIs. @@ -223,11 +241,6 @@ pub mod runtime { // moved to file similarly. #[allow(ambiguous_glob_reexports)] pub mod apis { - // Types often used in the runtime APIs. - pub use sp_core::OpaqueMetadata; - pub use sp_inherents::{CheckInherentsResult, InherentData}; - pub use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode}; - pub use frame_system_rpc_runtime_api::*; pub use sp_api::{self, *}; pub use sp_block_builder::*; diff --git a/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs b/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs index 326f3530e26e..81cdd40d1bcf 100644 --- a/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs +++ b/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs @@ -19,8 +19,6 @@ use frame_support::derive_impl; mod common; -use common::outer_enums::{pallet, pallet2}; - pub type Header = sp_runtime::generic::Header; pub type Block = sp_runtime::generic::Block; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; @@ -75,8 +73,10 @@ frame_support::construct_runtime!( } ); +#[cfg(feature = "experimental")] #[test] fn module_error_outer_enum_expand_explicit() { + use common::outer_enums::{pallet, pallet2}; // The Runtime has *all* parts explicitly defined. // Check that all error types are propagated @@ -90,9 +90,7 @@ fn module_error_outer_enum_expand_explicit() { frame_system::Error::NonZeroRefCount => (), frame_system::Error::CallFiltered => (), frame_system::Error::MultiBlockMigrationsOngoing => (), - #[cfg(feature = "experimental")] frame_system::Error::InvalidTask => (), - #[cfg(feature = "experimental")] frame_system::Error::FailedTask => (), frame_system::Error::NothingAuthorized => (), frame_system::Error::Unauthorized => (), diff --git a/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs b/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs index 4149c4880cca..d2e759640c73 100644 --- a/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs +++ b/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs @@ -19,8 +19,6 @@ use frame_support::derive_impl; mod common; -use common::outer_enums::{pallet, pallet2}; - pub type Header = sp_runtime::generic::Header; pub type Block = sp_runtime::generic::Block; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; @@ -75,8 +73,10 @@ frame_support::construct_runtime!( } ); +#[cfg(feature = "experimental")] #[test] fn module_error_outer_enum_expand_implicit() { + use common::outer_enums::{pallet, pallet2}; // The Runtime has *all* parts implicitly defined. // Check that all error types are propagated @@ -90,9 +90,7 @@ fn module_error_outer_enum_expand_implicit() { frame_system::Error::NonZeroRefCount => (), frame_system::Error::CallFiltered => (), frame_system::Error::MultiBlockMigrationsOngoing => (), - #[cfg(feature = "experimental")] frame_system::Error::InvalidTask => (), - #[cfg(feature = "experimental")] frame_system::Error::FailedTask => (), frame_system::Error::NothingAuthorized => (), frame_system::Error::Unauthorized => (), diff --git a/substrate/primitives/wasm-interface/Cargo.toml b/substrate/primitives/wasm-interface/Cargo.toml index 96ea8f4235d5..9d0310fd22e8 100644 --- a/substrate/primitives/wasm-interface/Cargo.toml +++ b/substrate/primitives/wasm-interface/Cargo.toml @@ -25,5 +25,9 @@ anyhow = { optional = true, workspace = true } [features] default = ["std"] -std = ["codec/std", "log/std"] +std = [ + "anyhow?/std", + "codec/std", + "log/std", +] wasmtime = ["anyhow", "dep:wasmtime"] diff --git a/templates/minimal/node/Cargo.toml b/templates/minimal/node/Cargo.toml index c6bd89d23370..956efca34532 100644 --- a/templates/minimal/node/Cargo.toml +++ b/templates/minimal/node/Cargo.toml @@ -21,42 +21,15 @@ futures-timer = { workspace = true } jsonrpsee = { features = ["server"], workspace = true } serde_json = { workspace = true, default-features = true } -sc-cli = { workspace = true, default-features = true } -sc-executor = { workspace = true, default-features = true } -sc-network = { workspace = true, default-features = true } -sc-service = { workspace = true, default-features = true } -sc-telemetry = { workspace = true, default-features = true } -sc-transaction-pool = { workspace = true, default-features = true } -sc-transaction-pool-api = { workspace = true, default-features = true } -sc-consensus = { workspace = true, default-features = true } -sc-consensus-manual-seal = { workspace = true, default-features = true } -sc-basic-authorship = { workspace = true, default-features = true } -sc-offchain = { workspace = true, default-features = true } -sc-client-api = { workspace = true, default-features = true } - -sp-timestamp = { workspace = true, default-features = true } -sp-keyring = { workspace = true, default-features = true } -sp-api = { workspace = true, default-features = true } -sp-blockchain = { workspace = true, default-features = true } -sp-block-builder = { workspace = true, default-features = true } -sp-io = { workspace = true, default-features = true } -sp-runtime = { workspace = true, default-features = true } - -substrate-frame-rpc-system = { workspace = true, default-features = true } - -# Once the native runtime is gone, there should be little to no dependency on FRAME here, and -# certainly no dependency on the runtime. -frame = { features = [ - "experimental", - "runtime", -], workspace = true, default-features = true } +polkadot-sdk = { workspace = true, features = ["experimental", "node"] } minimal-template-runtime = { workspace = true } [build-dependencies] -substrate-build-script-utils = { workspace = true, default-features = true } +polkadot-sdk = { workspace = true, features = ["substrate-build-script-utils"] } [features] default = ["std"] std = [ "minimal-template-runtime/std", + "polkadot-sdk/std", ] diff --git a/templates/minimal/node/build.rs b/templates/minimal/node/build.rs index fa7686e01099..47ab77ae2969 100644 --- a/templates/minimal/node/build.rs +++ b/templates/minimal/node/build.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; +use polkadot_sdk::substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; fn main() { generate_cargo_keys(); diff --git a/templates/minimal/node/src/chain_spec.rs b/templates/minimal/node/src/chain_spec.rs index 5b53b0f80ac0..0646460acef6 100644 --- a/templates/minimal/node/src/chain_spec.rs +++ b/templates/minimal/node/src/chain_spec.rs @@ -16,9 +16,12 @@ // limitations under the License. use minimal_template_runtime::{BalancesConfig, SudoConfig, WASM_BINARY}; -use sc_service::{ChainType, Properties}; +use polkadot_sdk::{ + sc_service::{ChainType, Properties}, + sp_keyring::AccountKeyring, + *, +}; use serde_json::{json, Value}; -use sp_keyring::AccountKeyring; /// This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec; @@ -42,8 +45,8 @@ pub fn development_config() -> Result { /// Configure initial storage state for FRAME pallets. fn testnet_genesis() -> Value { - use frame::traits::Get; use minimal_template_runtime::interface::{Balance, MinimumBalance}; + use polkadot_sdk::polkadot_sdk_frame::traits::Get; let endowment = >::get().max(1) * 1000; let balances = AccountKeyring::iter() .map(|a| (a.to_account_id(), endowment)) diff --git a/templates/minimal/node/src/cli.rs b/templates/minimal/node/src/cli.rs index 22726b7eb9a3..54107df75a36 100644 --- a/templates/minimal/node/src/cli.rs +++ b/templates/minimal/node/src/cli.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use sc_cli::RunCmd; +use polkadot_sdk::{sc_cli::RunCmd, *}; #[derive(Debug, Clone)] pub enum Consensus { diff --git a/templates/minimal/node/src/command.rs b/templates/minimal/node/src/command.rs index 504be1ccc36e..b09ea1fab237 100644 --- a/templates/minimal/node/src/command.rs +++ b/templates/minimal/node/src/command.rs @@ -20,8 +20,7 @@ use crate::{ cli::{Cli, Subcommand}, service, }; -use sc_cli::SubstrateCli; -use sc_service::PartialComponents; +use polkadot_sdk::{sc_cli::SubstrateCli, sc_service::PartialComponents, *}; impl SubstrateCli for Cli { fn impl_name() -> String { diff --git a/templates/minimal/node/src/main.rs b/templates/minimal/node/src/main.rs index 3cf7d98311ea..8f36da5bf83a 100644 --- a/templates/minimal/node/src/main.rs +++ b/templates/minimal/node/src/main.rs @@ -24,6 +24,6 @@ mod command; mod rpc; mod service; -fn main() -> sc_cli::Result<()> { +fn main() -> polkadot_sdk::sc_cli::Result<()> { command::run() } diff --git a/templates/minimal/node/src/rpc.rs b/templates/minimal/node/src/rpc.rs index f51dfe1a1ace..64497c4bcaca 100644 --- a/templates/minimal/node/src/rpc.rs +++ b/templates/minimal/node/src/rpc.rs @@ -24,8 +24,11 @@ use jsonrpsee::RpcModule; use minimal_template_runtime::interface::{AccountId, Nonce, OpaqueBlock}; -use sc_transaction_pool_api::TransactionPool; -use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use polkadot_sdk::{ + sc_transaction_pool_api::TransactionPool, + sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}, + *, +}; use std::sync::Arc; /// Full client dependencies. @@ -53,7 +56,7 @@ where C::Api: substrate_frame_rpc_system::AccountNonceApi, P: TransactionPool + 'static, { - use substrate_frame_rpc_system::{System, SystemApiServer}; + use polkadot_sdk::substrate_frame_rpc_system::{System, SystemApiServer}; let mut module = RpcModule::new(()); let FullDeps { client, pool } = deps; diff --git a/templates/minimal/node/src/service.rs b/templates/minimal/node/src/service.rs index d362ae36d23a..ce3afe1e6b76 100644 --- a/templates/minimal/node/src/service.rs +++ b/templates/minimal/node/src/service.rs @@ -17,12 +17,15 @@ use futures::FutureExt; use minimal_template_runtime::{interface::OpaqueBlock as Block, RuntimeApi}; -use sc_client_api::backend::Backend; -use sc_executor::WasmExecutor; -use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; -use sc_telemetry::{Telemetry, TelemetryWorker}; -use sc_transaction_pool_api::OffchainTransactionPoolFactory; -use sp_runtime::traits::Block as BlockT; +use polkadot_sdk::{ + sc_client_api::backend::Backend, + sc_executor::WasmExecutor, + sc_service::{error::Error as ServiceError, Configuration, TaskManager}, + sc_telemetry::{Telemetry, TelemetryWorker}, + sc_transaction_pool_api::OffchainTransactionPoolFactory, + sp_runtime::traits::Block as BlockT, + *, +}; use std::sync::Arc; use crate::cli::Consensus; diff --git a/templates/minimal/pallets/template/Cargo.toml b/templates/minimal/pallets/template/Cargo.toml index 9d231fe7d7d4..9a02d4daeaac 100644 --- a/templates/minimal/pallets/template/Cargo.toml +++ b/templates/minimal/pallets/template/Cargo.toml @@ -13,18 +13,14 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { features = [ - "derive", -], workspace = true } -scale-info = { features = [ - "derive", -], workspace = true } -frame = { features = [ +codec = { features = ["derive"], workspace = true } +scale-info = { features = ["derive"], workspace = true } +polkadot-sdk = { workspace = true, default-features = false, features = [ "experimental", "runtime", -], workspace = true } +] } [features] default = ["std"] -std = ["codec/std", "frame/std", "scale-info/std"] +std = ["codec/std", "polkadot-sdk/std", "scale-info/std"] diff --git a/templates/minimal/pallets/template/src/lib.rs b/templates/minimal/pallets/template/src/lib.rs index 92b90ad4412b..b8a8614932a6 100644 --- a/templates/minimal/pallets/template/src/lib.rs +++ b/templates/minimal/pallets/template/src/lib.rs @@ -5,7 +5,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame::prelude::*; +use polkadot_sdk::polkadot_sdk_frame as frame; // Re-export all pallet parts, this is needed to properly import the pallet into the runtime. pub use pallet::*; @@ -15,7 +15,7 @@ pub mod pallet { use super::*; #[pallet::config] - pub trait Config: frame_system::Config {} + pub trait Config: polkadot_sdk::frame_system::Config {} #[pallet::pallet] pub struct Pallet(_); diff --git a/templates/minimal/runtime/Cargo.toml b/templates/minimal/runtime/Cargo.toml index 5d3cf8492e52..49ddf3987e96 100644 --- a/templates/minimal/runtime/Cargo.toml +++ b/templates/minimal/runtime/Cargo.toml @@ -12,47 +12,29 @@ publish = false [dependencies] codec = { workspace = true } scale-info = { workspace = true } - -# this is a frame-based runtime, thus importing `frame` with runtime feature enabled. -frame = { features = [ +polkadot-sdk = { workspace = true, features = [ "experimental", + "pallet-balances", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", "runtime", -], workspace = true } - -# pallets that we want to use -pallet-balances = { workspace = true } -pallet-sudo = { workspace = true } -pallet-timestamp = { workspace = true } -pallet-transaction-payment = { workspace = true } -pallet-transaction-payment-rpc-runtime-api = { workspace = true } - -# genesis builder that allows us to interact with runtime genesis config -sp-genesis-builder = { workspace = true } -sp-runtime = { features = ["serde"], workspace = true } +] } # local pallet templates pallet-minimal-template = { workspace = true } [build-dependencies] -substrate-wasm-builder = { optional = true, workspace = true, default-features = true } +polkadot-sdk = { optional = true, workspace = true, features = [ + "substrate-wasm-builder", +] } [features] default = ["std"] std = [ "codec/std", - "scale-info/std", - - "frame/std", - - "pallet-balances/std", - "pallet-sudo/std", - "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", - "pallet-transaction-payment/std", - "pallet-minimal-template/std", - - "sp-genesis-builder/std", - "sp-runtime/std", - "substrate-wasm-builder", + "polkadot-sdk/std", + "scale-info/std", ] diff --git a/templates/minimal/runtime/build.rs b/templates/minimal/runtime/build.rs index e6f92757e225..2cb2966b5d82 100644 --- a/templates/minimal/runtime/build.rs +++ b/templates/minimal/runtime/build.rs @@ -18,6 +18,6 @@ fn main() { #[cfg(feature = "std")] { - substrate_wasm_builder::WasmBuilder::build_using_defaults(); + polkadot_sdk::substrate_wasm_builder::WasmBuilder::build_using_defaults(); } } diff --git a/templates/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs index 08ad537ecdd1..474d9ddfb9e8 100644 --- a/templates/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -26,20 +26,14 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); extern crate alloc; use alloc::{vec, vec::Vec}; -use frame::{ - deps::frame_support::{ - genesis_builder_helper::{build_state, get_preset}, - runtime, - weights::{FixedFee, NoFee}, - }, - prelude::*, - runtime::{ - apis::{ - self, impl_runtime_apis, ApplyExtrinsicResult, CheckInherentsResult, - ExtrinsicInclusionMode, OpaqueMetadata, - }, +use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; +use polkadot_sdk::{ + polkadot_sdk_frame::{ + self as frame, prelude::*, + runtime::{apis, prelude::*}, }, + *, }; /// The runtime version. @@ -83,7 +77,7 @@ type SignedExtra = ( ); // Composes the runtime by adding all the used pallets and deriving necessary types. -#[runtime] +#[frame_construct_runtime] mod runtime { /// The main runtime type. #[runtime::runtime] @@ -171,8 +165,6 @@ type Header = HeaderFor; type RuntimeExecutive = Executive, Runtime, AllPalletsWithSystem>; -use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; - impl_runtime_apis! { impl apis::Core for Runtime { fn version() -> RuntimeVersion { @@ -296,7 +288,7 @@ impl_runtime_apis! { // https://github.com/paritytech/substrate/issues/10579#issuecomment-1600537558 pub mod interface { use super::Runtime; - use frame::deps::frame_system; + use polkadot_sdk::{polkadot_sdk_frame as frame, *}; pub type Block = super::Block; pub use frame::runtime::types_common::OpaqueBlock; diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 9c2ed30c527a..6d380a4bcbb7 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -536,37 +536,8 @@ with-tracing = [ "sp-tracing?/with-tracing", "sp-tracing?/with-tracing", ] +runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-fixtures", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] runtime = [ - "assets-common", - "binary-merkle-tree", - "bp-header-chain", - "bp-messages", - "bp-parachains", - "bp-polkadot", - "bp-polkadot-core", - "bp-relayers", - "bp-runtime", - "bp-test-utils", - "bp-xcm-bridge-hub", - "bp-xcm-bridge-hub-router", - "bridge-hub-common", - "bridge-runtime-common", - "cumulus-pallet-aura-ext", - "cumulus-pallet-dmp-queue", - "cumulus-pallet-parachain-system", - "cumulus-pallet-parachain-system-proc-macro", - "cumulus-pallet-session-benchmarking", - "cumulus-pallet-solo-to-para", - "cumulus-pallet-xcm", - "cumulus-pallet-xcmp-queue", - "cumulus-ping", - "cumulus-primitives-aura", - "cumulus-primitives-core", - "cumulus-primitives-parachain-inherent", - "cumulus-primitives-proof-size-hostfunction", - "cumulus-primitives-storage-weight-reclaim", - "cumulus-primitives-timestamp", - "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", @@ -580,138 +551,8 @@ runtime = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "pallet-alliance", - "pallet-asset-conversion", - "pallet-asset-conversion-ops", - "pallet-asset-conversion-tx-payment", - "pallet-asset-rate", - "pallet-asset-tx-payment", - "pallet-assets", - "pallet-assets-freezer", - "pallet-atomic-swap", - "pallet-aura", - "pallet-authority-discovery", - "pallet-authorship", - "pallet-babe", - "pallet-bags-list", - "pallet-balances", - "pallet-beefy", - "pallet-beefy-mmr", - "pallet-bounties", - "pallet-bridge-grandpa", - "pallet-bridge-messages", - "pallet-bridge-parachains", - "pallet-bridge-relayers", - "pallet-broker", - "pallet-child-bounties", - "pallet-collator-selection", - "pallet-collective", - "pallet-collective-content", - "pallet-contracts", - "pallet-contracts-proc-macro", - "pallet-contracts-uapi", - "pallet-conviction-voting", - "pallet-core-fellowship", - "pallet-delegated-staking", - "pallet-democracy", - "pallet-dev-mode", - "pallet-election-provider-multi-phase", - "pallet-election-provider-support-benchmarking", - "pallet-elections-phragmen", - "pallet-fast-unstake", - "pallet-glutton", - "pallet-grandpa", - "pallet-identity", - "pallet-im-online", - "pallet-indices", - "pallet-insecure-randomness-collective-flip", - "pallet-lottery", - "pallet-membership", - "pallet-message-queue", - "pallet-migrations", - "pallet-mixnet", - "pallet-mmr", - "pallet-multisig", - "pallet-nft-fractionalization", - "pallet-nfts", - "pallet-nfts-runtime-api", - "pallet-nis", - "pallet-node-authorization", - "pallet-nomination-pools", - "pallet-nomination-pools-benchmarking", - "pallet-nomination-pools-runtime-api", - "pallet-offences", - "pallet-offences-benchmarking", - "pallet-paged-list", - "pallet-parameters", - "pallet-preimage", - "pallet-proxy", - "pallet-ranked-collective", - "pallet-recovery", - "pallet-referenda", - "pallet-remark", - "pallet-revive", - "pallet-revive-fixtures", - "pallet-revive-proc-macro", - "pallet-revive-uapi", - "pallet-root-offences", - "pallet-root-testing", - "pallet-safe-mode", - "pallet-salary", - "pallet-scheduler", - "pallet-scored-pool", - "pallet-session", - "pallet-session-benchmarking", - "pallet-skip-feeless-payment", - "pallet-society", - "pallet-staking", - "pallet-staking-reward-curve", - "pallet-staking-reward-fn", - "pallet-staking-runtime-api", - "pallet-state-trie-migration", - "pallet-statement", - "pallet-sudo", - "pallet-timestamp", - "pallet-tips", - "pallet-transaction-payment", - "pallet-transaction-payment-rpc-runtime-api", - "pallet-transaction-storage", - "pallet-treasury", - "pallet-tx-pause", - "pallet-uniques", - "pallet-utility", - "pallet-vesting", - "pallet-whitelist", - "pallet-xcm", - "pallet-xcm-benchmarks", - "pallet-xcm-bridge-hub", - "pallet-xcm-bridge-hub-router", - "parachains-common", - "polkadot-core-primitives", - "polkadot-parachain-primitives", - "polkadot-primitives", - "polkadot-runtime-common", - "polkadot-runtime-metrics", - "polkadot-runtime-parachains", "polkadot-sdk-frame", "polkadot-sdk-frame?/runtime", - "sc-chain-spec-derive", - "sc-tracing-proc-macro", - "slot-range-helper", - "snowbridge-beacon-primitives", - "snowbridge-core", - "snowbridge-ethereum", - "snowbridge-outbound-queue-merkle-tree", - "snowbridge-outbound-queue-runtime-api", - "snowbridge-pallet-ethereum-client", - "snowbridge-pallet-ethereum-client-fixtures", - "snowbridge-pallet-inbound-queue", - "snowbridge-pallet-inbound-queue-fixtures", - "snowbridge-pallet-outbound-queue", - "snowbridge-pallet-system", - "snowbridge-router-primitives", - "snowbridge-runtime-common", - "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", @@ -758,15 +599,6 @@ runtime = [ "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", - "staging-parachain-info", - "staging-xcm", - "staging-xcm-builder", - "staging-xcm-executor", - "substrate-bip39", - "testnet-parachains-constants", - "tracing-gum-proc-macro", - "xcm-procedural", - "xcm-runtime-apis", ] node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-revive-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-parachain-lib", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] tuples-96 = [ @@ -2655,5 +2487,5 @@ default-features = false optional = true [package.metadata.docs.rs] -features = ["node", "runtime"] +features = ["node", "runtime-full"] targets = ["x86_64-unknown-linux-gnu"] From 97fa922c85976523f755acdd104fdc77ed63dae9 Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Wed, 28 Aug 2024 23:42:37 +0900 Subject: [PATCH 176/382] Refactor verbose test (#5506) A test is triggering a log error. But is correct and successful. This is a refactor without triggering the log error. --- .../multi-block-migrations/src/migrations/v1/tests.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/substrate/frame/examples/multi-block-migrations/src/migrations/v1/tests.rs b/substrate/frame/examples/multi-block-migrations/src/migrations/v1/tests.rs index 838ba29a6212..3d2360d63a73 100644 --- a/substrate/frame/examples/multi-block-migrations/src/migrations/v1/tests.rs +++ b/substrate/frame/examples/multi-block-migrations/src/migrations/v1/tests.rs @@ -27,6 +27,7 @@ use crate::{ System, }, }; +use codec::Decode; use frame_support::traits::OnRuntimeUpgrade; use pallet_migrations::WeightInfo as _; @@ -51,10 +52,18 @@ fn lazy_migration_works() { let mut last_decodable = 0; for block in 2..=65 { run_to_block(block); + let mut decodable = 0; for i in 0..1024 { - if crate::MyMap::::get(i).is_some() { + let key = crate::MyMap::::hashed_key_for(i); + let value = + frame_support::storage::unhashed::get_raw(&key[..]).expect("value exists"); + + if let Ok(value) = u64::decode(&mut &value[..]) { + assert_eq!(value, i as u64); decodable += 1; + } else { + assert_eq!(u32::decode(&mut &value[..]).expect("not migrated yet"), i); } } From c4ced11fcc728f51011ddef2602d825df8a9becb Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 28 Aug 2024 20:40:21 +0300 Subject: [PATCH 177/382] polkadot-parachain: Add omni-node variant with u64 block number (#5269) Related to https://github.com/paritytech/polkadot-sdk/issues/4787 The main changes in this PR are the following: - making the NodeSpec logic generic on the Block type - adding an omni-node variant with u64 block number Apart from this, the PR also moves some of the logic in `service.rs` to the `common` subfolder The omni-node variant with u64 block number is not used yet. We have to either expose the option in the CLI or to read the block number from the chain spec somehow. Will do it in a future PR. --- Cargo.lock | 1 + .../polkadot-parachain-lib/Cargo.toml | 2 + .../polkadot-parachain-lib/src/cli.rs | 5 + .../polkadot-parachain-lib/src/command.rs | 52 +- .../src/common/command.rs | 161 ++++ .../polkadot-parachain-lib/src/common/mod.rs | 32 +- .../src/{ => common}/rpc.rs | 50 +- .../src/common/runtime.rs | 15 +- .../polkadot-parachain-lib/src/common/spec.rs | 392 +++++++++ .../src/common/types.rs | 56 ++ .../asset_hub_polkadot_aura.rs | 197 ----- .../src/fake_runtime_api/aura.rs | 197 ----- .../src/fake_runtime_api/mod.rs | 18 +- .../src/fake_runtime_api/utils.rs | 220 ++++++ .../polkadot-parachain-lib/src/lib.rs | 3 +- .../polkadot-parachain-lib/src/service.rs | 745 +++--------------- .../polkadot-parachain/src/chain_spec/mod.rs | 9 +- prdoc/pr_5269.prdoc | 15 + 18 files changed, 1109 insertions(+), 1061 deletions(-) create mode 100644 cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs rename cumulus/polkadot-parachain/polkadot-parachain-lib/src/{ => common}/rpc.rs (65%) create mode 100644 cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs create mode 100644 cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/types.rs delete mode 100644 cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/asset_hub_polkadot_aura.rs delete mode 100644 cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/aura.rs create mode 100644 cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs create mode 100644 prdoc/pr_5269.prdoc diff --git a/Cargo.lock b/Cargo.lock index 885e2b4b4cf3..07295630b129 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13992,6 +13992,7 @@ dependencies = [ "sc-chain-spec", "sc-cli", "sc-client-api", + "sc-client-db", "sc-consensus", "sc-executor", "sc-network", diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml b/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml index 09bde034cf26..066cbfae53ae 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml @@ -38,6 +38,7 @@ sc-consensus = { workspace = true, default-features = true } frame-support = { optional = true, workspace = true, default-features = true } sc-cli = { workspace = true, default-features = true } sc-client-api = { workspace = true, default-features = true } +sc-client-db = { workspace = true, default-features = true } sc-executor = { workspace = true, default-features = true } sc-service = { workspace = true, default-features = true } sc-telemetry = { workspace = true, default-features = true } @@ -105,6 +106,7 @@ runtime-benchmarks = [ "parachains-common/runtime-benchmarks", "polkadot-cli/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", + "sc-client-db/runtime-benchmarks", "sc-service/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs index 31eb2cf88060..15d21235d1a1 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs @@ -36,8 +36,10 @@ use std::{fmt::Debug, marker::PhantomData, path::PathBuf}; /// The related info is shown to the customer as part of logs or help messages. /// It does not impact functionality. pub trait CliConfig { + /// The version of the resulting node binary. fn impl_version() -> String; + /// The description of the resulting node binary. fn description(executable_name: String) -> String { format!( "The command-line arguments provided first will be passed to the parachain node, \n\ @@ -50,10 +52,13 @@ pub trait CliConfig { ) } + /// The author of the resulting node binary. fn author() -> String; + /// The support URL for the resulting node binary. fn support_url() -> String; + /// The starting copyright year of the resulting node binary. fn copyright_start_year() -> u16; } diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs index 7f915b729e0a..320511ece5e5 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -#[cfg(feature = "runtime-benchmarks")] -use crate::service::Block; use crate::{ cli::{Cli, RelayChainCli, Subcommand}, common::{ @@ -24,32 +22,56 @@ use crate::{ AuraConsensusId, Consensus, Runtime, RuntimeResolver as RuntimeResolverT, RuntimeResolver, }, - NodeExtraArgs, - }, - fake_runtime_api::{ - asset_hub_polkadot_aura::RuntimeApi as AssetHubPolkadotRuntimeApi, - aura::RuntimeApi as AuraRuntimeApi, + spec::DynNodeSpec, + types::Block, + NodeBlock, NodeExtraArgs, }, - service::{new_aura_node_spec, DynNodeSpec, ShellNode}, + fake_runtime_api, + runtime::BlockNumber, + service::ShellNode, }; #[cfg(feature = "runtime-benchmarks")] use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; use cumulus_primitives_core::ParaId; use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; use log::info; -use parachains_common::{AssetHubPolkadotAuraId, AuraId}; use sc_cli::{Result, SubstrateCli}; use sp_runtime::traits::AccountIdConversion; #[cfg(feature = "runtime-benchmarks")] use sp_runtime::traits::HashingFor; +use std::panic::{RefUnwindSafe, UnwindSafe}; /// Structure that can be used in order to provide customizers for different functionalities of the /// node binary that is being built using this library. pub struct RunConfig { + /// A custom chain spec loader. pub chain_spec_loader: Box, + /// A custom runtime resolver. pub runtime_resolver: Box, } +pub fn new_aura_node_spec( + aura_id: AuraConsensusId, + extra_args: &NodeExtraArgs, +) -> Box +where + Block: NodeBlock + UnwindSafe + RefUnwindSafe, + Block::BoundedHeader: UnwindSafe + RefUnwindSafe, +{ + match aura_id { + AuraConsensusId::Sr25519 => crate::service::new_aura_node_spec::< + Block, + fake_runtime_api::aura_sr25519::RuntimeApi, + sp_consensus_aura::sr25519::AuthorityId, + >(extra_args), + AuraConsensusId::Ed25519 => crate::service::new_aura_node_spec::< + Block, + fake_runtime_api::aura_ed25519::RuntimeApi, + sp_consensus_aura::ed25519::AuthorityId, + >(extra_args), + } +} + fn new_node_spec( config: &sc_service::Configuration, runtime_resolver: &Box, @@ -59,11 +81,11 @@ fn new_node_spec( Ok(match runtime { Runtime::Shell => Box::new(ShellNode), - Runtime::Omni(consensus) => match consensus { - Consensus::Aura(AuraConsensusId::Sr25519) => - new_aura_node_spec::(extra_args), - Consensus::Aura(AuraConsensusId::Ed25519) => - new_aura_node_spec::(extra_args), + Runtime::Omni(block_number, consensus) => match (block_number, consensus) { + (BlockNumber::U32, Consensus::Aura(aura_id)) => + new_aura_node_spec::>(aura_id, extra_args), + (BlockNumber::U64, Consensus::Aura(aura_id)) => + new_aura_node_spec::>(aura_id, extra_args), }, }) } @@ -156,7 +178,7 @@ pub fn run(cmd_config: RunConfig) -> Result<() match cmd { #[cfg(feature = "runtime-benchmarks")] BenchmarkCmd::Pallet(cmd) => runner.sync_run(|config| { - cmd.run_with_spec::, ReclaimHostFunctions>(Some( + cmd.run_with_spec::>, ReclaimHostFunctions>(Some( config.chain_spec, )) }), diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs new file mode 100644 index 000000000000..e2826826d40e --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs @@ -0,0 +1,161 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +use crate::common::spec::NodeSpec; +use cumulus_client_cli::ExportGenesisHeadCommand; +use frame_benchmarking_cli::BlockCmd; +#[cfg(any(feature = "runtime-benchmarks"))] +use frame_benchmarking_cli::StorageCmd; +use sc_cli::{CheckBlockCmd, ExportBlocksCmd, ExportStateCmd, ImportBlocksCmd, RevertCmd}; +use sc_service::{Configuration, TaskManager}; +use std::{future::Future, pin::Pin}; + +type SyncCmdResult = sc_cli::Result<()>; + +type AsyncCmdResult<'a> = + sc_cli::Result<(Pin + 'a>>, TaskManager)>; + +pub trait NodeCommandRunner { + fn prepare_check_block_cmd( + self: Box, + config: Configuration, + cmd: &CheckBlockCmd, + ) -> AsyncCmdResult<'_>; + + fn prepare_export_blocks_cmd( + self: Box, + config: Configuration, + cmd: &ExportBlocksCmd, + ) -> AsyncCmdResult<'_>; + + fn prepare_export_state_cmd( + self: Box, + config: Configuration, + cmd: &ExportStateCmd, + ) -> AsyncCmdResult<'_>; + + fn prepare_import_blocks_cmd( + self: Box, + config: Configuration, + cmd: &ImportBlocksCmd, + ) -> AsyncCmdResult<'_>; + + fn prepare_revert_cmd( + self: Box, + config: Configuration, + cmd: &RevertCmd, + ) -> AsyncCmdResult<'_>; + + fn run_export_genesis_head_cmd( + self: Box, + config: Configuration, + cmd: &ExportGenesisHeadCommand, + ) -> SyncCmdResult; + + fn run_benchmark_block_cmd( + self: Box, + config: Configuration, + cmd: &BlockCmd, + ) -> SyncCmdResult; + + #[cfg(any(feature = "runtime-benchmarks"))] + fn run_benchmark_storage_cmd( + self: Box, + config: Configuration, + cmd: &StorageCmd, + ) -> SyncCmdResult; +} + +impl NodeCommandRunner for T +where + T: NodeSpec, +{ + fn prepare_check_block_cmd( + self: Box, + config: Configuration, + cmd: &CheckBlockCmd, + ) -> AsyncCmdResult<'_> { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + Ok((Box::pin(cmd.run(partial.client, partial.import_queue)), partial.task_manager)) + } + + fn prepare_export_blocks_cmd( + self: Box, + config: Configuration, + cmd: &ExportBlocksCmd, + ) -> AsyncCmdResult<'_> { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + Ok((Box::pin(cmd.run(partial.client, config.database)), partial.task_manager)) + } + + fn prepare_export_state_cmd( + self: Box, + config: Configuration, + cmd: &ExportStateCmd, + ) -> AsyncCmdResult<'_> { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + Ok((Box::pin(cmd.run(partial.client, config.chain_spec)), partial.task_manager)) + } + + fn prepare_import_blocks_cmd( + self: Box, + config: Configuration, + cmd: &ImportBlocksCmd, + ) -> AsyncCmdResult<'_> { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + Ok((Box::pin(cmd.run(partial.client, partial.import_queue)), partial.task_manager)) + } + + fn prepare_revert_cmd( + self: Box, + config: Configuration, + cmd: &RevertCmd, + ) -> AsyncCmdResult<'_> { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + Ok((Box::pin(cmd.run(partial.client, partial.backend, None)), partial.task_manager)) + } + + fn run_export_genesis_head_cmd( + self: Box, + config: Configuration, + cmd: &ExportGenesisHeadCommand, + ) -> SyncCmdResult { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + cmd.run(partial.client) + } + + fn run_benchmark_block_cmd( + self: Box, + config: Configuration, + cmd: &BlockCmd, + ) -> SyncCmdResult { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + cmd.run(partial.client) + } + + #[cfg(any(feature = "runtime-benchmarks"))] + fn run_benchmark_storage_cmd( + self: Box, + config: Configuration, + cmd: &StorageCmd, + ) -> SyncCmdResult { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + let db = partial.backend.expose_db(); + let storage = partial.backend.expose_storage(); + + cmd.run(config, partial.client, db, storage) + } +} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs index 89bc7511dac3..907f09263fc1 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs @@ -20,15 +20,43 @@ pub(crate) mod aura; pub mod chain_spec; +pub mod command; +pub mod rpc; pub mod runtime; +pub mod spec; +pub mod types; use cumulus_primitives_core::CollectCollationInfo; +use sc_client_db::DbHash; use sp_api::{ApiExt, CallApiAt, ConstructRuntimeApi, Metadata}; use sp_block_builder::BlockBuilder; -use sp_runtime::traits::Block as BlockT; +use sp_runtime::{ + traits::{Block as BlockT, BlockNumber, Header as HeaderT, NumberFor}, + OpaqueExtrinsic, +}; use sp_session::SessionKeys; use sp_transaction_pool::runtime_api::TaggedTransactionQueue; -use std::path::PathBuf; +use std::{fmt::Debug, path::PathBuf, str::FromStr}; + +pub trait NodeBlock: + BlockT + + for<'de> serde::Deserialize<'de> +{ + type BoundedFromStrErr: Debug; + type BoundedNumber: FromStr + BlockNumber; + type BoundedHeader: HeaderT + Unpin; +} + +impl NodeBlock for T +where + T: BlockT + for<'de> serde::Deserialize<'de>, + ::Header: Unpin, + as FromStr>::Err: Debug, +{ + type BoundedFromStrErr = as FromStr>::Err; + type BoundedNumber = NumberFor; + type BoundedHeader = ::Header; +} /// Convenience trait that defines the basic bounds for the `RuntimeApi` of a parachain node. pub trait NodeRuntimeApi: diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/rpc.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/rpc.rs similarity index 65% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/rpc.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/rpc.rs index d156ebe2bd6d..a4e157e87216 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/rpc.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/rpc.rs @@ -18,13 +18,13 @@ #![warn(missing_docs)] -use crate::{ - common::ConstructNodeRuntimeApi, - service::{ParachainBackend, ParachainClient}, +use crate::common::{ + types::{AccountId, Balance, Nonce, ParachainBackend, ParachainClient}, + ConstructNodeRuntimeApi, }; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; -use parachains_common::{AccountId, Balance, Block, Nonce}; use sc_rpc::dev::{Dev, DevApiServer}; +use sp_runtime::traits::Block as BlockT; use std::{marker::PhantomData, sync::Arc}; use substrate_frame_rpc_system::{System, SystemApiServer}; use substrate_state_trie_migration_rpc::{StateMigration, StateMigrationApiServer}; @@ -40,43 +40,45 @@ pub(crate) trait BuildRpcExtensions { ) -> sc_service::error::Result; } -pub(crate) struct BuildEmptyRpcExtensions(PhantomData); +pub(crate) struct BuildEmptyRpcExtensions(PhantomData<(Block, RuntimeApi)>); -impl +impl BuildRpcExtensions< - ParachainClient, - ParachainBackend, - sc_transaction_pool::FullPool>, - > for BuildEmptyRpcExtensions + ParachainClient, + ParachainBackend, + sc_transaction_pool::FullPool>, + > for BuildEmptyRpcExtensions where - RuntimeApi: ConstructNodeRuntimeApi> + Send + Sync + 'static, + RuntimeApi: + ConstructNodeRuntimeApi> + Send + Sync + 'static, { fn build_rpc_extensions( - _client: Arc>, - _backend: Arc, - _pool: Arc>>, + _client: Arc>, + _backend: Arc>, + _pool: Arc>>, ) -> sc_service::error::Result { Ok(RpcExtension::new(())) } } -pub(crate) struct BuildParachainRpcExtensions(PhantomData); +pub(crate) struct BuildParachainRpcExtensions(PhantomData<(Block, RuntimeApi)>); -impl +impl BuildRpcExtensions< - ParachainClient, - ParachainBackend, - sc_transaction_pool::FullPool>, - > for BuildParachainRpcExtensions + ParachainClient, + ParachainBackend, + sc_transaction_pool::FullPool>, + > for BuildParachainRpcExtensions where - RuntimeApi: ConstructNodeRuntimeApi> + Send + Sync + 'static, + RuntimeApi: + ConstructNodeRuntimeApi> + Send + Sync + 'static, RuntimeApi::RuntimeApi: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + substrate_frame_rpc_system::AccountNonceApi, { fn build_rpc_extensions( - client: Arc>, - backend: Arc, - pool: Arc>>, + client: Arc>, + backend: Arc>, + pool: Arc>>, ) -> sc_service::error::Result { let build = || -> Result> { let mut module = RpcExtension::new(()); diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs index c64eda12d5ef..bddbb0a85d03 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs @@ -34,12 +34,21 @@ pub enum Consensus { Aura(AuraConsensusId), } +/// The choice of block number for the parachain omni-node. +#[derive(PartialEq)] +pub enum BlockNumber { + /// u32 + U32, + /// u64 + U64, +} + /// Helper enum listing the supported Runtime types #[derive(PartialEq)] pub enum Runtime { /// None of the system-chain runtimes, rather the node will act agnostic to the runtime ie. be /// an omni-node, and simply run a node with the given consensus algorithm. - Omni(Consensus), + Omni(BlockNumber, Consensus), /// Shell Shell, } @@ -51,11 +60,11 @@ pub trait RuntimeResolver { } /// Default implementation for `RuntimeResolver` that just returns -/// `Runtime::Omni(Consensus::Aura(AuraConsensusId::Sr25519))`. +/// `Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Sr25519))`. pub struct DefaultRuntimeResolver; impl RuntimeResolver for DefaultRuntimeResolver { fn runtime(&self, _chain_spec: &dyn ChainSpec) -> sc_cli::Result { - Ok(Runtime::Omni(Consensus::Aura(AuraConsensusId::Sr25519))) + Ok(Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Sr25519))) } } diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs new file mode 100644 index 000000000000..55e042aed87e --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs @@ -0,0 +1,392 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +use crate::common::{ + command::NodeCommandRunner, + rpc::BuildRpcExtensions, + types::{ + ParachainBackend, ParachainBlockImport, ParachainClient, ParachainHostFunctions, + ParachainService, + }, + ConstructNodeRuntimeApi, NodeBlock, NodeExtraArgs, +}; +use cumulus_client_cli::CollatorOptions; +use cumulus_client_service::{ + build_network, build_relay_chain_interface, prepare_node_config, start_relay_chain_tasks, + BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, StartRelayChainTasksParams, +}; +use cumulus_primitives_core::{BlockT, ParaId}; +use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}; +use parachains_common::Hash; +use polkadot_primitives::CollatorPair; +use prometheus_endpoint::Registry; +use sc_consensus::DefaultImportQueue; +use sc_executor::{HeapAllocStrategy, DEFAULT_HEAP_ALLOC_STRATEGY}; +use sc_network::{config::FullNetworkConfiguration, NetworkBackend, NetworkBlock}; +use sc_service::{Configuration, ImportQueue, PartialComponents, TaskManager}; +use sc_sysinfo::HwBench; +use sc_telemetry::{TelemetryHandle, TelemetryWorker}; +use sc_transaction_pool::FullPool; +use sp_keystore::KeystorePtr; +use std::{future::Future, pin::Pin, sync::Arc, time::Duration}; + +pub(crate) trait BuildImportQueue { + fn build_import_queue( + client: Arc>, + block_import: ParachainBlockImport, + config: &Configuration, + telemetry_handle: Option, + task_manager: &TaskManager, + ) -> sc_service::error::Result>; +} + +pub(crate) trait StartConsensus +where + RuntimeApi: ConstructNodeRuntimeApi>, +{ + fn start_consensus( + client: Arc>, + block_import: ParachainBlockImport, + prometheus_registry: Option<&Registry>, + telemetry: Option, + task_manager: &TaskManager, + relay_chain_interface: Arc, + transaction_pool: Arc>>, + keystore: KeystorePtr, + relay_chain_slot_duration: Duration, + para_id: ParaId, + collator_key: CollatorPair, + overseer_handle: OverseerHandle, + announce_block: Arc>) + Send + Sync>, + backend: Arc>, + node_extra_args: NodeExtraArgs, + ) -> Result<(), sc_service::Error>; +} + +/// Checks that the hardware meets the requirements and print a warning otherwise. +fn warn_if_slow_hardware(hwbench: &sc_sysinfo::HwBench) { + // Polkadot para-chains should generally use these requirements to ensure that the relay-chain + // will not take longer than expected to import its blocks. + if let Err(err) = frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench) { + log::warn!( + "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\ + https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", + err + ); + } +} + +pub(crate) trait NodeSpec { + type Block: NodeBlock; + + type RuntimeApi: ConstructNodeRuntimeApi< + Self::Block, + ParachainClient, + >; + + type BuildImportQueue: BuildImportQueue; + + type BuildRpcExtensions: BuildRpcExtensions< + ParachainClient, + ParachainBackend, + FullPool>, + >; + + type StartConsensus: StartConsensus; + + const SYBIL_RESISTANCE: CollatorSybilResistance; + + /// Starts a `ServiceBuilder` for a full service. + /// + /// Use this macro if you don't actually need the full service, but just the builder in order to + /// be able to perform chain operations. + fn new_partial( + config: &Configuration, + ) -> sc_service::error::Result> { + let telemetry = config + .telemetry_endpoints + .clone() + .filter(|x| !x.is_empty()) + .map(|endpoints| -> Result<_, sc_telemetry::Error> { + let worker = TelemetryWorker::new(16)?; + let telemetry = worker.handle().new_telemetry(endpoints); + Ok((worker, telemetry)) + }) + .transpose()?; + + let heap_pages = config.default_heap_pages.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| { + HeapAllocStrategy::Static { extra_pages: h as _ } + }); + + let executor = sc_executor::WasmExecutor::::builder() + .with_execution_method(config.wasm_method) + .with_max_runtime_instances(config.max_runtime_instances) + .with_runtime_cache_size(config.runtime_cache_size) + .with_onchain_heap_alloc_strategy(heap_pages) + .with_offchain_heap_alloc_strategy(heap_pages) + .build(); + + let (client, backend, keystore_container, task_manager) = + sc_service::new_full_parts_record_import::( + config, + telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, + true, + )?; + let client = Arc::new(client); + + let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle()); + + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager.spawn_handle().spawn("telemetry", None, worker.run()); + telemetry + }); + + let transaction_pool = sc_transaction_pool::BasicPool::new_full( + config.transaction_pool.clone(), + config.role.is_authority().into(), + config.prometheus_registry(), + task_manager.spawn_essential_handle(), + client.clone(), + ); + + let block_import = ParachainBlockImport::new(client.clone(), backend.clone()); + + let import_queue = Self::BuildImportQueue::build_import_queue( + client.clone(), + block_import.clone(), + config, + telemetry.as_ref().map(|telemetry| telemetry.handle()), + &task_manager, + )?; + + Ok(PartialComponents { + backend, + client, + import_queue, + keystore_container, + task_manager, + transaction_pool, + select_chain: (), + other: (block_import, telemetry, telemetry_worker_handle), + }) + } + + /// Start a node with the given parachain spec. + /// + /// This is the actual implementation that is abstract over the executor and the runtime api. + fn start_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, + node_extra_args: NodeExtraArgs, + ) -> Pin>>> + where + Net: NetworkBackend, + { + Box::pin(async move { + let parachain_config = prepare_node_config(parachain_config); + + let params = Self::new_partial(¶chain_config)?; + let (block_import, mut telemetry, telemetry_worker_handle) = params.other; + + let client = params.client.clone(); + let backend = params.backend.clone(); + + let mut task_manager = params.task_manager; + let (relay_chain_interface, collator_key) = build_relay_chain_interface( + polkadot_config, + ¶chain_config, + telemetry_worker_handle, + &mut task_manager, + collator_options.clone(), + hwbench.clone(), + ) + .await + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; + + let validator = parachain_config.role.is_authority(); + let prometheus_registry = parachain_config.prometheus_registry().cloned(); + let transaction_pool = params.transaction_pool.clone(); + let import_queue_service = params.import_queue.service(); + let net_config = FullNetworkConfiguration::<_, _, Net>::new( + ¶chain_config.network, + prometheus_registry.clone(), + ); + + let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = + build_network(BuildNetworkParams { + parachain_config: ¶chain_config, + net_config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + para_id, + spawn_handle: task_manager.spawn_handle(), + relay_chain_interface: relay_chain_interface.clone(), + import_queue: params.import_queue, + sybil_resistance_level: Self::SYBIL_RESISTANCE, + }) + .await?; + + let rpc_builder = { + let client = client.clone(); + let transaction_pool = transaction_pool.clone(); + let backend_for_rpc = backend.clone(); + + Box::new(move |_| { + Self::BuildRpcExtensions::build_rpc_extensions( + client.clone(), + backend_for_rpc.clone(), + transaction_pool.clone(), + ) + }) + }; + + sc_service::spawn_tasks(sc_service::SpawnTasksParams { + rpc_builder, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + task_manager: &mut task_manager, + config: parachain_config, + keystore: params.keystore_container.keystore(), + backend: backend.clone(), + network: network.clone(), + sync_service: sync_service.clone(), + system_rpc_tx, + tx_handler_controller, + telemetry: telemetry.as_mut(), + })?; + + if let Some(hwbench) = hwbench { + sc_sysinfo::print_hwbench(&hwbench); + if validator { + warn_if_slow_hardware(&hwbench); + } + + if let Some(ref mut telemetry) = telemetry { + let telemetry_handle = telemetry.handle(); + task_manager.spawn_handle().spawn( + "telemetry_hwbench", + None, + sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench), + ); + } + } + + let announce_block = { + let sync_service = sync_service.clone(); + Arc::new(move |hash, data| sync_service.announce_block(hash, data)) + }; + + let relay_chain_slot_duration = Duration::from_secs(6); + + let overseer_handle = relay_chain_interface + .overseer_handle() + .map_err(|e| sc_service::Error::Application(Box::new(e)))?; + + start_relay_chain_tasks(StartRelayChainTasksParams { + client: client.clone(), + announce_block: announce_block.clone(), + para_id, + relay_chain_interface: relay_chain_interface.clone(), + task_manager: &mut task_manager, + da_recovery_profile: if validator { + DARecoveryProfile::Collator + } else { + DARecoveryProfile::FullNode + }, + import_queue: import_queue_service, + relay_chain_slot_duration, + recovery_handle: Box::new(overseer_handle.clone()), + sync_service, + })?; + + if validator { + Self::StartConsensus::start_consensus( + client.clone(), + block_import, + prometheus_registry.as_ref(), + telemetry.as_ref().map(|t| t.handle()), + &task_manager, + relay_chain_interface.clone(), + transaction_pool, + params.keystore_container.keystore(), + relay_chain_slot_duration, + para_id, + collator_key.expect("Command line arguments do not allow this. qed"), + overseer_handle, + announce_block, + backend.clone(), + node_extra_args, + )?; + } + + start_network.start_network(); + + Ok(task_manager) + }) + } +} + +pub(crate) trait DynNodeSpec: NodeCommandRunner { + fn start_node( + self: Box, + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, + node_extra_args: NodeExtraArgs, + ) -> Pin>>>; +} + +impl DynNodeSpec for T +where + T: NodeSpec + NodeCommandRunner, +{ + fn start_node( + self: Box, + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, + node_extra_args: NodeExtraArgs, + ) -> Pin>>> { + match parachain_config.network.network_backend { + sc_network::config::NetworkBackendType::Libp2p => + ::start_node::>( + parachain_config, + polkadot_config, + collator_options, + para_id, + hwbench, + node_extra_args, + ), + sc_network::config::NetworkBackendType::Litep2p => + ::start_node::( + parachain_config, + polkadot_config, + collator_options, + para_id, + hwbench, + node_extra_args, + ), + } + } +} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/types.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/types.rs new file mode 100644 index 000000000000..9cfdcb22451c --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/types.rs @@ -0,0 +1,56 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +use cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport; +use cumulus_primitives_core::relay_chain::UncheckedExtrinsic; +use sc_consensus::DefaultImportQueue; +use sc_executor::WasmExecutor; +use sc_service::{PartialComponents, TFullBackend, TFullClient}; +use sc_telemetry::{Telemetry, TelemetryWorkerHandle}; +use sc_transaction_pool::FullPool; +use sp_runtime::{generic, traits::BlakeTwo256}; +use std::sync::Arc; + +pub use parachains_common::{AccountId, Balance, Hash, Nonce}; + +type Header = generic::Header; +pub type Block = generic::Block, UncheckedExtrinsic>; + +#[cfg(not(feature = "runtime-benchmarks"))] +pub type ParachainHostFunctions = cumulus_client_service::ParachainHostFunctions; +#[cfg(feature = "runtime-benchmarks")] +pub type ParachainHostFunctions = ( + cumulus_client_service::ParachainHostFunctions, + frame_benchmarking::benchmarking::HostFunctions, +); + +pub type ParachainClient = + TFullClient>; + +pub type ParachainBackend = TFullBackend; + +pub type ParachainBlockImport = + TParachainBlockImport>, ParachainBackend>; + +/// Assembly of PartialComponents (enough to run chain ops subcommands) +pub type ParachainService = PartialComponents< + ParachainClient, + ParachainBackend, + (), + DefaultImportQueue, + FullPool>, + (ParachainBlockImport, Option, Option), +>; diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/asset_hub_polkadot_aura.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/asset_hub_polkadot_aura.rs deleted file mode 100644 index f2b8b4d562b9..000000000000 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/asset_hub_polkadot_aura.rs +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -//! These are used to provide a type that implements these runtime APIs without requiring to import -//! the native runtimes. - -use parachains_common::{AccountId, AssetHubPolkadotAuraId, Balance, Block, Nonce}; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; -use sp_runtime::{ - traits::Block as BlockT, - transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, -}; -use sp_weights::Weight; - -pub struct Runtime; - -sp_api::impl_runtime_apis! { - impl sp_api::Core for Runtime { - fn version() -> sp_version::RuntimeVersion { - unimplemented!() - } - - fn execute_block(_: Block) { - unimplemented!() - } - - fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { - unimplemented!() - } - } - - impl sp_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - unimplemented!() - } - - fn metadata_at_version(_: u32) -> Option { - unimplemented!() - } - - fn metadata_versions() -> Vec { - unimplemented!() - } - } - - impl sp_consensus_aura::AuraApi for Runtime { - fn slot_duration() -> sp_consensus_aura::SlotDuration { - unimplemented!() - } - - fn authorities() -> Vec { - unimplemented!() - } - } - - impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { - fn can_build_upon( - _: ::Hash, - _: cumulus_primitives_aura::Slot, - ) -> bool { - unimplemented!() - } - } - - impl sp_block_builder::BlockBuilder for Runtime { - fn apply_extrinsic(_: ::Extrinsic) -> ApplyExtrinsicResult { - unimplemented!() - } - - fn finalize_block() -> ::Header { - unimplemented!() - } - - fn inherent_extrinsics(_: sp_inherents::InherentData) -> Vec<::Extrinsic> { - unimplemented!() - } - - fn check_inherents(_: Block, _: sp_inherents::InherentData) -> sp_inherents::CheckInherentsResult { - unimplemented!() - } - } - - impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { - fn validate_transaction( - _: TransactionSource, - _: ::Extrinsic, - _: ::Hash, - ) -> TransactionValidity { - unimplemented!() - } - } - - impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(_: Option>) -> Vec { - unimplemented!() - } - - fn decode_session_keys( - _: Vec, - ) -> Option, KeyTypeId)>> { - unimplemented!() - } - } - - impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { - fn query_info( - _: ::Extrinsic, - _: u32, - ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { - unimplemented!() - } - fn query_fee_details( - _: ::Extrinsic, - _: u32, - ) -> pallet_transaction_payment::FeeDetails { - unimplemented!() - } - fn query_weight_to_fee(_: Weight) -> Balance { - unimplemented!() - } - fn query_length_to_fee(_: u32) -> Balance { - unimplemented!() - } - } - - impl cumulus_primitives_core::CollectCollationInfo for Runtime { - fn collect_collation_info(_: &::Header) -> cumulus_primitives_core::CollationInfo { - unimplemented!() - } - } - - #[cfg(feature = "try-runtime")] - impl frame_try_runtime::TryRuntime for Runtime { - fn on_runtime_upgrade(_: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { - unimplemented!() - } - - fn execute_block( - _: Block, - _: bool, - _: bool, - _: frame_try_runtime::TryStateSelect, - ) -> Weight { - unimplemented!() - } - } - - impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { - fn account_nonce(_: AccountId) -> Nonce { - unimplemented!() - } - } - - #[cfg(feature = "runtime-benchmarks")] - impl frame_benchmarking::Benchmark for Runtime { - fn benchmark_metadata(_: bool) -> ( - Vec, - Vec, - ) { - unimplemented!() - } - - fn dispatch_benchmark( - _: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { - unimplemented!() - } - } - - impl sp_genesis_builder::GenesisBuilder for Runtime { - fn build_state(_: Vec) -> sp_genesis_builder::Result { - unimplemented!() - } - - fn get_preset(_id: &Option) -> Option> { - unimplemented!() - } - - fn preset_names() -> Vec { - unimplemented!() - } - } -} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/aura.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/aura.rs deleted file mode 100644 index eb6d3fafa3d6..000000000000 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/aura.rs +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -//! These are used to provide a type that implements these runtime APIs without requiring to import -//! the native runtimes. - -use parachains_common::{AccountId, AuraId, Balance, Block, Nonce}; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; -use sp_runtime::{ - traits::Block as BlockT, - transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, -}; -use sp_weights::Weight; - -pub struct Runtime; - -sp_api::impl_runtime_apis! { - impl sp_api::Core for Runtime { - fn version() -> sp_version::RuntimeVersion { - unimplemented!() - } - - fn execute_block(_: Block) { - unimplemented!() - } - - fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { - unimplemented!() - } - } - - impl sp_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - unimplemented!() - } - - fn metadata_at_version(_: u32) -> Option { - unimplemented!() - } - - fn metadata_versions() -> Vec { - unimplemented!() - } - } - - impl sp_consensus_aura::AuraApi for Runtime { - fn slot_duration() -> sp_consensus_aura::SlotDuration { - unimplemented!() - } - - fn authorities() -> Vec { - unimplemented!() - } - } - - impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { - fn can_build_upon( - _: ::Hash, - _: cumulus_primitives_aura::Slot, - ) -> bool { - unimplemented!() - } - } - - impl sp_block_builder::BlockBuilder for Runtime { - fn apply_extrinsic(_: ::Extrinsic) -> ApplyExtrinsicResult { - unimplemented!() - } - - fn finalize_block() -> ::Header { - unimplemented!() - } - - fn inherent_extrinsics(_: sp_inherents::InherentData) -> Vec<::Extrinsic> { - unimplemented!() - } - - fn check_inherents(_: Block, _: sp_inherents::InherentData) -> sp_inherents::CheckInherentsResult { - unimplemented!() - } - } - - impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { - fn validate_transaction( - _: TransactionSource, - _: ::Extrinsic, - _: ::Hash, - ) -> TransactionValidity { - unimplemented!() - } - } - - impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(_: Option>) -> Vec { - unimplemented!() - } - - fn decode_session_keys( - _: Vec, - ) -> Option, KeyTypeId)>> { - unimplemented!() - } - } - - impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { - fn query_info( - _: ::Extrinsic, - _: u32, - ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { - unimplemented!() - } - fn query_fee_details( - _: ::Extrinsic, - _: u32, - ) -> pallet_transaction_payment::FeeDetails { - unimplemented!() - } - fn query_weight_to_fee(_: Weight) -> Balance { - unimplemented!() - } - fn query_length_to_fee(_: u32) -> Balance { - unimplemented!() - } - } - - impl cumulus_primitives_core::CollectCollationInfo for Runtime { - fn collect_collation_info(_: &::Header) -> cumulus_primitives_core::CollationInfo { - unimplemented!() - } - } - - #[cfg(feature = "try-runtime")] - impl frame_try_runtime::TryRuntime for Runtime { - fn on_runtime_upgrade(_: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { - unimplemented!() - } - - fn execute_block( - _: Block, - _: bool, - _: bool, - _: frame_try_runtime::TryStateSelect, - ) -> Weight { - unimplemented!() - } - } - - impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { - fn account_nonce(_: AccountId) -> Nonce { - unimplemented!() - } - } - - #[cfg(feature = "runtime-benchmarks")] - impl frame_benchmarking::Benchmark for Runtime { - fn benchmark_metadata(_: bool) -> ( - Vec, - Vec, - ) { - unimplemented!() - } - - fn dispatch_benchmark( - _: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { - unimplemented!() - } - } - - impl sp_genesis_builder::GenesisBuilder for Runtime { - fn build_state(_: Vec) -> sp_genesis_builder::Result { - unimplemented!() - } - - fn get_preset(_id: &Option) -> Option> { - unimplemented!() - } - - fn preset_names() -> Vec { - unimplemented!() - } - } -} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/mod.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/mod.rs index 29e2736b06ff..02aa867d70fe 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/mod.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/mod.rs @@ -17,5 +17,19 @@ //! In an ideal world this would be one runtime which would simplify the code massively. //! This is not an ideal world - Polkadot Asset Hub has a different key type. -pub mod asset_hub_polkadot_aura; -pub mod aura; +mod utils; + +use utils::{impl_node_runtime_apis, imports::*}; + +type CustomBlock = crate::common::types::Block; +pub mod aura_sr25519 { + use super::*; + struct FakeRuntime; + impl_node_runtime_apis!(FakeRuntime, CustomBlock, sp_consensus_aura::sr25519::AuthorityId); +} + +pub mod aura_ed25519 { + use super::*; + struct FakeRuntime; + impl_node_runtime_apis!(FakeRuntime, CustomBlock, sp_consensus_aura::ed25519::AuthorityId); +} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs new file mode 100644 index 000000000000..442b87b5d775 --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs @@ -0,0 +1,220 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +pub(crate) mod imports { + pub use parachains_common::{AccountId, Balance, Nonce}; + pub use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; + pub use sp_runtime::{ + traits::Block as BlockT, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, + }; + pub use sp_weights::Weight; +} + +macro_rules! impl_node_runtime_apis { + ($runtime: ty, $block: tt, $aura_id: ty) => { + sp_api::impl_runtime_apis! { + impl sp_api::Core<$block> for $runtime { + fn version() -> sp_version::RuntimeVersion { + unimplemented!() + } + + fn execute_block(_: $block) { + unimplemented!() + } + + fn initialize_block( + _: &<$block as BlockT>::Header + ) -> sp_runtime::ExtrinsicInclusionMode { + unimplemented!() + } + } + + impl sp_api::Metadata<$block> for $runtime { + fn metadata() -> OpaqueMetadata { + unimplemented!() + } + + fn metadata_at_version(_: u32) -> Option { + unimplemented!() + } + + fn metadata_versions() -> Vec { + unimplemented!() + } + } + + impl sp_consensus_aura::AuraApi<$block, $aura_id> for $runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + unimplemented!() + } + + fn authorities() -> Vec<$aura_id> { + unimplemented!() + } + } + + impl cumulus_primitives_aura::AuraUnincludedSegmentApi<$block> for $runtime { + fn can_build_upon( + _: <$block as BlockT>::Hash, + _: cumulus_primitives_aura::Slot, + ) -> bool { + unimplemented!() + } + } + + impl sp_block_builder::BlockBuilder<$block> for $runtime { + fn apply_extrinsic(_: <$block as BlockT>::Extrinsic) -> ApplyExtrinsicResult { + unimplemented!() + } + + fn finalize_block() -> <$block as BlockT>::Header { + unimplemented!() + } + + fn inherent_extrinsics( + _: sp_inherents::InherentData + ) -> Vec<<$block as BlockT>::Extrinsic> { + unimplemented!() + } + + fn check_inherents( + _: $block, + _: sp_inherents::InherentData + ) -> sp_inherents::CheckInherentsResult { + unimplemented!() + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<$block> for $runtime { + fn validate_transaction( + _: TransactionSource, + _: <$block as BlockT>::Extrinsic, + _: <$block as BlockT>::Hash, + ) -> TransactionValidity { + unimplemented!() + } + } + + impl sp_session::SessionKeys<$block> for $runtime { + fn generate_session_keys(_: Option>) -> Vec { + unimplemented!() + } + + fn decode_session_keys( + _: Vec, + ) -> Option, KeyTypeId)>> { + unimplemented!() + } + } + + impl + pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi< + $block, + Balance, + > for $runtime + { + fn query_info( + _: <$block as BlockT>::Extrinsic, + _: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + unimplemented!() + } + fn query_fee_details( + _: <$block as BlockT>::Extrinsic, + _: u32, + ) -> pallet_transaction_payment::FeeDetails { + unimplemented!() + } + fn query_weight_to_fee(_: Weight) -> Balance { + unimplemented!() + } + fn query_length_to_fee(_: u32) -> Balance { + unimplemented!() + } + } + + impl cumulus_primitives_core::CollectCollationInfo<$block> for $runtime { + fn collect_collation_info( + _: &<$block as BlockT>::Header + ) -> cumulus_primitives_core::CollationInfo { + unimplemented!() + } + } + + #[cfg(feature = "try-runtime")] + impl frame_try_runtime::TryRuntime<$block> for $runtime { + fn on_runtime_upgrade( + _: frame_try_runtime::UpgradeCheckSelect + ) -> (Weight, Weight) { + unimplemented!() + } + + fn execute_block( + _: $block, + _: bool, + _: bool, + _: frame_try_runtime::TryStateSelect, + ) -> Weight { + unimplemented!() + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi< + $block, + AccountId, + Nonce + > for $runtime { + fn account_nonce(_: AccountId) -> Nonce { + unimplemented!() + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark<$block> for $runtime { + fn benchmark_metadata(_: bool) -> ( + Vec, + Vec, + ) { + unimplemented!() + } + + fn dispatch_benchmark( + _: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + unimplemented!() + } + } + + impl sp_genesis_builder::GenesisBuilder<$block> for $runtime { + fn build_state(_: Vec) -> sp_genesis_builder::Result { + unimplemented!() + } + + fn get_preset(_id: &Option) -> Option> { + unimplemented!() + } + + fn preset_names() -> Vec { + unimplemented!() + } + } + } + }; +} + +pub(crate) use impl_node_runtime_apis; diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs index fc164a9d8907..6aa2f656a48b 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs @@ -39,11 +39,12 @@ //! //! For an example, see the `polkadot-parachain-bin` crate. +#![deny(missing_docs)] + mod cli; mod command; mod common; mod fake_runtime_api; -mod rpc; mod service; pub use cli::CliConfig; diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs index 057b8af9af52..303ec1e3b298 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs @@ -14,7 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use cumulus_client_cli::{CollatorOptions, ExportGenesisHeadCommand}; +use crate::{ + common::{ + aura::{AuraIdT, AuraRuntimeApi}, + rpc::{BuildEmptyRpcExtensions, BuildParachainRpcExtensions}, + spec::{BuildImportQueue, DynNodeSpec, NodeSpec, StartConsensus}, + types::{ + AccountId, Balance, Block, Hash, Nonce, ParachainBackend, ParachainBlockImport, + ParachainClient, + }, + ConstructNodeRuntimeApi, NodeBlock, NodeExtraArgs, + }, + fake_runtime_api::aura_sr25519::RuntimeApi as FakeRuntimeApi, +}; use cumulus_client_collator::service::{ CollatorService, ServiceInterface as CollatorServiceInterface, }; @@ -23,370 +35,45 @@ use cumulus_client_consensus_aura::collators::lookahead::{self as aura, Params a use cumulus_client_consensus_aura::collators::slot_based::{ self as slot_based, Params as SlotBasedParams, }; -use cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport; use cumulus_client_consensus_proposer::{Proposer, ProposerInterface}; use cumulus_client_consensus_relay_chain::Verifier as RelayChainVerifier; #[allow(deprecated)] use cumulus_client_service::old_consensus; -use cumulus_client_service::{ - build_network, build_relay_chain_interface, prepare_node_config, start_relay_chain_tasks, - BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, StartRelayChainTasksParams, -}; +use cumulus_client_service::CollatorSybilResistance; use cumulus_primitives_core::{relay_chain::ValidationCode, ParaId}; use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}; - -use crate::{ - common::{ - aura::{AuraIdT, AuraRuntimeApi}, - ConstructNodeRuntimeApi, NodeExtraArgs, - }, - fake_runtime_api::aura::RuntimeApi as FakeRuntimeApi, - rpc::BuildRpcExtensions, -}; -pub use parachains_common::{AccountId, Balance, Block, Hash, Nonce}; - -use crate::rpc::{BuildEmptyRpcExtensions, BuildParachainRpcExtensions}; -use frame_benchmarking_cli::BlockCmd; -#[cfg(any(feature = "runtime-benchmarks"))] -use frame_benchmarking_cli::StorageCmd; use futures::prelude::*; use polkadot_primitives::CollatorPair; use prometheus_endpoint::Registry; -use sc_cli::{CheckBlockCmd, ExportBlocksCmd, ExportStateCmd, ImportBlocksCmd, RevertCmd}; use sc_client_api::BlockchainEvents; +use sc_client_db::DbHash; use sc_consensus::{ import_queue::{BasicQueue, Verifier as VerifierT}, - BlockImportParams, DefaultImportQueue, ImportQueue, + BlockImportParams, DefaultImportQueue, }; -use sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY}; -use sc_network::{config::FullNetworkConfiguration, service::traits::NetworkBackend, NetworkBlock}; -use sc_service::{Configuration, Error, PartialComponents, TFullBackend, TFullClient, TaskManager}; -use sc_sysinfo::HwBench; -use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle}; +use sc_service::{Configuration, Error, TaskManager}; +use sc_telemetry::TelemetryHandle; use sc_transaction_pool::FullPool; use sp_api::ProvideRuntimeApi; use sp_inherents::CreateInherentDataProviders; use sp_keystore::KeystorePtr; -use sp_runtime::{app_crypto::AppCrypto, traits::Header as HeaderT}; -use std::{marker::PhantomData, pin::Pin, sync::Arc, time::Duration}; - -#[cfg(not(feature = "runtime-benchmarks"))] -type HostFunctions = cumulus_client_service::ParachainHostFunctions; - -#[cfg(feature = "runtime-benchmarks")] -type HostFunctions = ( - cumulus_client_service::ParachainHostFunctions, - frame_benchmarking::benchmarking::HostFunctions, -); - -pub type ParachainClient = TFullClient>; - -pub type ParachainBackend = TFullBackend; - -type ParachainBlockImport = - TParachainBlockImport>, ParachainBackend>; - -/// Assembly of PartialComponents (enough to run chain ops subcommands) -pub type Service = PartialComponents< - ParachainClient, - ParachainBackend, - (), - sc_consensus::DefaultImportQueue, - sc_transaction_pool::FullPool>, - (ParachainBlockImport, Option, Option), ->; - -pub(crate) trait BuildImportQueue { - fn build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, - config: &Configuration, - telemetry_handle: Option, - task_manager: &TaskManager, - ) -> sc_service::error::Result>; -} - -pub(crate) trait StartConsensus -where - RuntimeApi: ConstructNodeRuntimeApi>, -{ - fn start_consensus( - client: Arc>, - block_import: ParachainBlockImport, - prometheus_registry: Option<&Registry>, - telemetry: Option, - task_manager: &TaskManager, - relay_chain_interface: Arc, - transaction_pool: Arc>>, - keystore: KeystorePtr, - relay_chain_slot_duration: Duration, - para_id: ParaId, - collator_key: CollatorPair, - overseer_handle: OverseerHandle, - announce_block: Arc>) + Send + Sync>, - backend: Arc, - node_extra_args: NodeExtraArgs, - ) -> Result<(), sc_service::Error>; -} - -pub(crate) trait NodeSpec { - type RuntimeApi: ConstructNodeRuntimeApi>; - - type BuildImportQueue: BuildImportQueue + 'static; - - type BuildRpcExtensions: BuildRpcExtensions< - ParachainClient, - ParachainBackend, - sc_transaction_pool::FullPool>, - > + 'static; - - type StartConsensus: StartConsensus + 'static; - - const SYBIL_RESISTANCE: CollatorSybilResistance; - - /// Starts a `ServiceBuilder` for a full service. - /// - /// Use this macro if you don't actually need the full service, but just the builder in order to - /// be able to perform chain operations. - fn new_partial(config: &Configuration) -> sc_service::error::Result> { - let telemetry = config - .telemetry_endpoints - .clone() - .filter(|x| !x.is_empty()) - .map(|endpoints| -> Result<_, sc_telemetry::Error> { - let worker = TelemetryWorker::new(16)?; - let telemetry = worker.handle().new_telemetry(endpoints); - Ok((worker, telemetry)) - }) - .transpose()?; - - let heap_pages = config.default_heap_pages.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| { - HeapAllocStrategy::Static { extra_pages: h as _ } - }); - - let executor = sc_executor::WasmExecutor::::builder() - .with_execution_method(config.wasm_method) - .with_max_runtime_instances(config.max_runtime_instances) - .with_runtime_cache_size(config.runtime_cache_size) - .with_onchain_heap_alloc_strategy(heap_pages) - .with_offchain_heap_alloc_strategy(heap_pages) - .build(); - - let (client, backend, keystore_container, task_manager) = - sc_service::new_full_parts_record_import::( - config, - telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), - executor, - true, - )?; - let client = Arc::new(client); - - let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle()); - - let telemetry = telemetry.map(|(worker, telemetry)| { - task_manager.spawn_handle().spawn("telemetry", None, worker.run()); - telemetry - }); - - let transaction_pool = sc_transaction_pool::BasicPool::new_full( - config.transaction_pool.clone(), - config.role.is_authority().into(), - config.prometheus_registry(), - task_manager.spawn_essential_handle(), - client.clone(), - ); - - let block_import = ParachainBlockImport::new(client.clone(), backend.clone()); - - let import_queue = Self::BuildImportQueue::build_import_queue( - client.clone(), - block_import.clone(), - config, - telemetry.as_ref().map(|telemetry| telemetry.handle()), - &task_manager, - )?; - - Ok(PartialComponents { - backend, - client, - import_queue, - keystore_container, - task_manager, - transaction_pool, - select_chain: (), - other: (block_import, telemetry, telemetry_worker_handle), - }) - } - - /// Start a node with the given parachain spec. - /// - /// This is the actual implementation that is abstract over the executor and the runtime api. - fn start_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, - node_extra_args: NodeExtraArgs, - ) -> Pin>>> - where - Net: NetworkBackend, - { - Box::pin(async move { - let parachain_config = prepare_node_config(parachain_config); - - let params = Self::new_partial(¶chain_config)?; - let (block_import, mut telemetry, telemetry_worker_handle) = params.other; - - let client = params.client.clone(); - let backend = params.backend.clone(); - - let mut task_manager = params.task_manager; - let (relay_chain_interface, collator_key) = build_relay_chain_interface( - polkadot_config, - ¶chain_config, - telemetry_worker_handle, - &mut task_manager, - collator_options.clone(), - hwbench.clone(), - ) - .await - .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; - - let validator = parachain_config.role.is_authority(); - let prometheus_registry = parachain_config.prometheus_registry().cloned(); - let transaction_pool = params.transaction_pool.clone(); - let import_queue_service = params.import_queue.service(); - let net_config = FullNetworkConfiguration::<_, _, Net>::new( - ¶chain_config.network, - prometheus_registry.clone(), - ); - - let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = - build_network(BuildNetworkParams { - parachain_config: ¶chain_config, - net_config, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - para_id, - spawn_handle: task_manager.spawn_handle(), - relay_chain_interface: relay_chain_interface.clone(), - import_queue: params.import_queue, - sybil_resistance_level: Self::SYBIL_RESISTANCE, - }) - .await?; - - let rpc_builder = { - let client = client.clone(); - let transaction_pool = transaction_pool.clone(); - let backend_for_rpc = backend.clone(); - - Box::new(move |_| { - Self::BuildRpcExtensions::build_rpc_extensions( - client.clone(), - backend_for_rpc.clone(), - transaction_pool.clone(), - ) - }) - }; - - sc_service::spawn_tasks(sc_service::SpawnTasksParams { - rpc_builder, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - task_manager: &mut task_manager, - config: parachain_config, - keystore: params.keystore_container.keystore(), - backend: backend.clone(), - network: network.clone(), - sync_service: sync_service.clone(), - system_rpc_tx, - tx_handler_controller, - telemetry: telemetry.as_mut(), - })?; - - if let Some(hwbench) = hwbench { - sc_sysinfo::print_hwbench(&hwbench); - if validator { - warn_if_slow_hardware(&hwbench); - } - - if let Some(ref mut telemetry) = telemetry { - let telemetry_handle = telemetry.handle(); - task_manager.spawn_handle().spawn( - "telemetry_hwbench", - None, - sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench), - ); - } - } - - let announce_block = { - let sync_service = sync_service.clone(); - Arc::new(move |hash, data| sync_service.announce_block(hash, data)) - }; - - let relay_chain_slot_duration = Duration::from_secs(6); - - let overseer_handle = relay_chain_interface - .overseer_handle() - .map_err(|e| sc_service::Error::Application(Box::new(e)))?; - - start_relay_chain_tasks(StartRelayChainTasksParams { - client: client.clone(), - announce_block: announce_block.clone(), - para_id, - relay_chain_interface: relay_chain_interface.clone(), - task_manager: &mut task_manager, - da_recovery_profile: if validator { - DARecoveryProfile::Collator - } else { - DARecoveryProfile::FullNode - }, - import_queue: import_queue_service, - relay_chain_slot_duration, - recovery_handle: Box::new(overseer_handle.clone()), - sync_service, - })?; - - if validator { - Self::StartConsensus::start_consensus( - client.clone(), - block_import, - prometheus_registry.as_ref(), - telemetry.as_ref().map(|t| t.handle()), - &task_manager, - relay_chain_interface.clone(), - transaction_pool, - params.keystore_container.keystore(), - relay_chain_slot_duration, - para_id, - collator_key.expect("Command line arguments do not allow this. qed"), - overseer_handle, - announce_block, - backend.clone(), - node_extra_args, - )?; - } - - start_network.start_network(); - - Ok(task_manager) - }) - } -} +use sp_runtime::{ + app_crypto::AppCrypto, + traits::{Block as BlockT, Header as HeaderT}, +}; +use std::{marker::PhantomData, sync::Arc, time::Duration}; /// Build the import queue for the shell runtime. -pub(crate) struct BuildShellImportQueue(PhantomData); +pub(crate) struct BuildShellImportQueue; -impl BuildImportQueue for BuildShellImportQueue { +impl BuildImportQueue, FakeRuntimeApi> for BuildShellImportQueue { fn build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, + client: Arc, FakeRuntimeApi>>, + block_import: ParachainBlockImport, FakeRuntimeApi>, config: &Configuration, _telemetry_handle: Option, task_manager: &TaskManager, - ) -> sc_service::error::Result> { + ) -> sc_service::error::Result>> { cumulus_client_consensus_relay_chain::import_queue( client, block_import, @@ -401,15 +88,16 @@ impl BuildImportQueue for BuildShellImportQueue pub(crate) struct ShellNode; impl NodeSpec for ShellNode { + type Block = Block; type RuntimeApi = FakeRuntimeApi; - type BuildImportQueue = BuildShellImportQueue; - type BuildRpcExtensions = BuildEmptyRpcExtensions; + type BuildImportQueue = BuildShellImportQueue; + type BuildRpcExtensions = BuildEmptyRpcExtensions, Self::RuntimeApi>; type StartConsensus = StartRelayChainConsensus; const SYBIL_RESISTANCE: CollatorSybilResistance = CollatorSybilResistance::Unresistant; } -struct Verifier { +struct Verifier { client: Arc, aura_verifier: Box>, relay_chain_verifier: Box>, @@ -417,7 +105,7 @@ struct Verifier { } #[async_trait::async_trait] -impl VerifierT for Verifier +impl VerifierT for Verifier where Client: ProvideRuntimeApi + Send + Sync, Client::Api: AuraRuntimeApi, @@ -437,51 +125,53 @@ where /// Build the import queue for parachain runtimes that started with relay chain consensus and /// switched to aura. -pub(crate) struct BuildRelayToAuraImportQueue( - PhantomData<(RuntimeApi, AuraId)>, +pub(crate) struct BuildRelayToAuraImportQueue( + PhantomData<(Block, RuntimeApi, AuraId)>, ); -impl BuildImportQueue - for BuildRelayToAuraImportQueue +impl BuildImportQueue + for BuildRelayToAuraImportQueue where - RuntimeApi: ConstructNodeRuntimeApi>, + RuntimeApi: ConstructNodeRuntimeApi>, RuntimeApi::RuntimeApi: AuraRuntimeApi, AuraId: AuraIdT + Sync, { fn build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, + client: Arc>, + block_import: ParachainBlockImport, config: &Configuration, telemetry_handle: Option, task_manager: &TaskManager, ) -> sc_service::error::Result> { let verifier_client = client.clone(); - let aura_verifier = - cumulus_client_consensus_aura::build_verifier::<::Pair, _, _, _>( - cumulus_client_consensus_aura::BuildVerifierParams { - client: verifier_client.clone(), - create_inherent_data_providers: move |parent_hash, _| { - let cidp_client = verifier_client.clone(); - async move { - let slot_duration = cumulus_client_consensus_aura::slot_duration_at( - &*cidp_client, - parent_hash, - )?; - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - - let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - *timestamp, - slot_duration, - ); - - Ok((slot, timestamp)) - } - }, - telemetry: telemetry_handle, - }, - ); + let aura_verifier = cumulus_client_consensus_aura::build_verifier::< + ::Pair, + _, + _, + _, + >(cumulus_client_consensus_aura::BuildVerifierParams { + client: verifier_client.clone(), + create_inherent_data_providers: move |parent_hash, _| { + let cidp_client = verifier_client.clone(); + async move { + let slot_duration = cumulus_client_consensus_aura::slot_duration_at( + &*cidp_client, + parent_hash, + )?; + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + Ok((slot, timestamp)) + } + }, + telemetry: telemetry_handle, + }); let relay_chain_verifier = Box::new(RelayChainVerifier::new(client.clone(), |_, _| async { Ok(()) })); @@ -503,35 +193,43 @@ where /// Uses the lookahead collator to support async backing. /// /// Start an aura powered parachain node. Some system chains use this. -pub(crate) struct AuraNode( - pub PhantomData<(RuntimeApi, AuraId, StartConsensus)>, +pub(crate) struct AuraNode( + pub PhantomData<(Block, RuntimeApi, AuraId, StartConsensus)>, ); -impl Default for AuraNode { +impl Default + for AuraNode +{ fn default() -> Self { Self(Default::default()) } } -impl NodeSpec for AuraNode +impl NodeSpec + for AuraNode where - RuntimeApi: ConstructNodeRuntimeApi>, + Block: NodeBlock, + RuntimeApi: ConstructNodeRuntimeApi>, RuntimeApi::RuntimeApi: AuraRuntimeApi + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + substrate_frame_rpc_system::AccountNonceApi, AuraId: AuraIdT + Sync, - StartConsensus: self::StartConsensus + 'static, + StartConsensus: self::StartConsensus + 'static, { + type Block = Block; type RuntimeApi = RuntimeApi; - type BuildImportQueue = BuildRelayToAuraImportQueue; - type BuildRpcExtensions = BuildParachainRpcExtensions; + type BuildImportQueue = BuildRelayToAuraImportQueue; + type BuildRpcExtensions = BuildParachainRpcExtensions; type StartConsensus = StartConsensus; const SYBIL_RESISTANCE: CollatorSybilResistance = CollatorSybilResistance::Resistant; } -pub fn new_aura_node_spec(extra_args: &NodeExtraArgs) -> Box +pub fn new_aura_node_spec( + extra_args: &NodeExtraArgs, +) -> Box where - RuntimeApi: ConstructNodeRuntimeApi>, + Block: NodeBlock, + RuntimeApi: ConstructNodeRuntimeApi>, RuntimeApi::RuntimeApi: AuraRuntimeApi + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + substrate_frame_rpc_system::AccountNonceApi, @@ -539,15 +237,17 @@ where { if extra_args.use_slot_based_consensus { Box::new(AuraNode::< + Block, RuntimeApi, AuraId, - StartSlotBasedAuraConsensus, + StartSlotBasedAuraConsensus, >::default()) } else { Box::new(AuraNode::< + Block, RuntimeApi, AuraId, - StartLookaheadAuraConsensus, + StartLookaheadAuraConsensus, >::default()) } } @@ -556,22 +256,22 @@ where /// decides what is backed and included. pub(crate) struct StartRelayChainConsensus; -impl StartConsensus for StartRelayChainConsensus { +impl StartConsensus, FakeRuntimeApi> for StartRelayChainConsensus { fn start_consensus( - client: Arc>, - block_import: ParachainBlockImport, + client: Arc, FakeRuntimeApi>>, + block_import: ParachainBlockImport, FakeRuntimeApi>, prometheus_registry: Option<&Registry>, telemetry: Option, task_manager: &TaskManager, relay_chain_interface: Arc, - transaction_pool: Arc>>, + transaction_pool: Arc, ParachainClient, FakeRuntimeApi>>>, _keystore: KeystorePtr, _relay_chain_slot_duration: Duration, para_id: ParaId, collator_key: CollatorPair, overseer_handle: OverseerHandle, announce_block: Arc>) + Send + Sync>, - _backend: Arc, + _backend: Arc>>, _node_extra_args: NodeExtraArgs, ) -> Result<(), Error> { let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( @@ -592,12 +292,12 @@ impl StartConsensus for StartRelayChainConsensus { let relay_chain_interface = relay_chain_interface.clone(); async move { let parachain_inherent = - cumulus_client_parachain_inherent::ParachainInherentDataProvider::create_at( - relay_parent, - &relay_chain_interface, - &validation_data, - para_id, - ).await; + cumulus_client_parachain_inherent::ParachainInherentDataProvider::create_at( + relay_parent, + &relay_chain_interface, + &validation_data, + para_id, + ).await; let parachain_inherent = parachain_inherent.ok_or_else(|| { Box::::from( "Failed to create parachain inherent", @@ -629,23 +329,24 @@ impl StartConsensus for StartRelayChainConsensus { } /// Start consensus using the lookahead aura collator. -pub(crate) struct StartSlotBasedAuraConsensus( - PhantomData<(RuntimeApi, AuraId)>, +pub(crate) struct StartSlotBasedAuraConsensus( + PhantomData<(Block, RuntimeApi, AuraId)>, ); -impl StartSlotBasedAuraConsensus +impl, RuntimeApi, AuraId> + StartSlotBasedAuraConsensus where - RuntimeApi: ConstructNodeRuntimeApi>, + RuntimeApi: ConstructNodeRuntimeApi>, RuntimeApi::RuntimeApi: AuraRuntimeApi, AuraId: AuraIdT + Sync, { #[docify::export_content] fn launch_slot_based_collator( params: SlotBasedParams< - ParachainBlockImport, + ParachainBlockImport, CIDP, - ParachainClient, - ParachainBackend, + ParachainClient, + ParachainBackend, Arc, CHP, Proposer, @@ -675,28 +376,28 @@ where } } -impl StartConsensus - for StartSlotBasedAuraConsensus +impl, RuntimeApi, AuraId> StartConsensus + for StartSlotBasedAuraConsensus where - RuntimeApi: ConstructNodeRuntimeApi>, + RuntimeApi: ConstructNodeRuntimeApi>, RuntimeApi::RuntimeApi: AuraRuntimeApi, AuraId: AuraIdT + Sync, { fn start_consensus( - client: Arc>, - block_import: ParachainBlockImport, + client: Arc>, + block_import: ParachainBlockImport, prometheus_registry: Option<&Registry>, telemetry: Option, task_manager: &TaskManager, relay_chain_interface: Arc, - transaction_pool: Arc>>, + transaction_pool: Arc>>, keystore: KeystorePtr, relay_chain_slot_duration: Duration, para_id: ParaId, collator_key: CollatorPair, _overseer_handle: OverseerHandle, announce_block: Arc>) + Send + Sync>, - backend: Arc, + backend: Arc>, _node_extra_args: NodeExtraArgs, ) -> Result<(), Error> { let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( @@ -747,9 +448,10 @@ where /// Wait for the Aura runtime API to appear on chain. /// This is useful for chains that started out without Aura. Components that /// are depending on Aura functionality will wait until Aura appears in the runtime. -async fn wait_for_aura(client: Arc>) -where - RuntimeApi: ConstructNodeRuntimeApi>, +async fn wait_for_aura( + client: Arc>, +) where + RuntimeApi: ConstructNodeRuntimeApi>, RuntimeApi::RuntimeApi: AuraRuntimeApi, AuraId: AuraIdT + Sync, { @@ -767,32 +469,32 @@ where } /// Start consensus using the lookahead aura collator. -pub(crate) struct StartLookaheadAuraConsensus( - PhantomData<(RuntimeApi, AuraId)>, +pub(crate) struct StartLookaheadAuraConsensus( + PhantomData<(Block, RuntimeApi, AuraId)>, ); -impl StartConsensus - for StartLookaheadAuraConsensus +impl, RuntimeApi, AuraId> StartConsensus + for StartLookaheadAuraConsensus where - RuntimeApi: ConstructNodeRuntimeApi>, + RuntimeApi: ConstructNodeRuntimeApi>, RuntimeApi::RuntimeApi: AuraRuntimeApi, AuraId: AuraIdT + Sync, { fn start_consensus( - client: Arc>, - block_import: ParachainBlockImport, + client: Arc>, + block_import: ParachainBlockImport, prometheus_registry: Option<&Registry>, telemetry: Option, task_manager: &TaskManager, relay_chain_interface: Arc, - transaction_pool: Arc>>, + transaction_pool: Arc>>, keystore: KeystorePtr, relay_chain_slot_duration: Duration, para_id: ParaId, collator_key: CollatorPair, overseer_handle: OverseerHandle, announce_block: Arc>) + Send + Sync>, - backend: Arc, + backend: Arc>, node_extra_args: NodeExtraArgs, ) -> Result<(), Error> { let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( @@ -848,194 +550,3 @@ where Ok(()) } } - -/// Checks that the hardware meets the requirements and print a warning otherwise. -fn warn_if_slow_hardware(hwbench: &sc_sysinfo::HwBench) { - // Polkadot para-chains should generally use these requirements to ensure that the relay-chain - // will not take longer than expected to import its blocks. - if let Err(err) = frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench) { - log::warn!( - "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\ - https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", - err - ); - } -} - -type SyncCmdResult = sc_cli::Result<()>; - -type AsyncCmdResult<'a> = - sc_cli::Result<(Pin + 'a>>, TaskManager)>; - -pub(crate) trait DynNodeSpec { - fn prepare_check_block_cmd( - self: Box, - config: Configuration, - cmd: &CheckBlockCmd, - ) -> AsyncCmdResult<'_>; - - fn prepare_export_blocks_cmd( - self: Box, - config: Configuration, - cmd: &ExportBlocksCmd, - ) -> AsyncCmdResult<'_>; - - fn prepare_export_state_cmd( - self: Box, - config: Configuration, - cmd: &ExportStateCmd, - ) -> AsyncCmdResult<'_>; - - fn prepare_import_blocks_cmd( - self: Box, - config: Configuration, - cmd: &ImportBlocksCmd, - ) -> AsyncCmdResult<'_>; - - fn prepare_revert_cmd( - self: Box, - config: Configuration, - cmd: &RevertCmd, - ) -> AsyncCmdResult<'_>; - - fn run_export_genesis_head_cmd( - self: Box, - config: Configuration, - cmd: &ExportGenesisHeadCommand, - ) -> SyncCmdResult; - - fn run_benchmark_block_cmd( - self: Box, - config: Configuration, - cmd: &BlockCmd, - ) -> SyncCmdResult; - - #[cfg(any(feature = "runtime-benchmarks"))] - fn run_benchmark_storage_cmd( - self: Box, - config: Configuration, - cmd: &StorageCmd, - ) -> SyncCmdResult; - - fn start_node( - self: Box, - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, - node_extra_args: NodeExtraArgs, - ) -> Pin>>>; -} - -impl DynNodeSpec for T -where - T: NodeSpec, -{ - fn prepare_check_block_cmd( - self: Box, - config: Configuration, - cmd: &CheckBlockCmd, - ) -> AsyncCmdResult<'_> { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - Ok((Box::pin(cmd.run(partial.client, partial.import_queue)), partial.task_manager)) - } - - fn prepare_export_blocks_cmd( - self: Box, - config: Configuration, - cmd: &ExportBlocksCmd, - ) -> AsyncCmdResult<'_> { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - Ok((Box::pin(cmd.run(partial.client, config.database)), partial.task_manager)) - } - - fn prepare_export_state_cmd( - self: Box, - config: Configuration, - cmd: &ExportStateCmd, - ) -> AsyncCmdResult<'_> { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - Ok((Box::pin(cmd.run(partial.client, config.chain_spec)), partial.task_manager)) - } - - fn prepare_import_blocks_cmd( - self: Box, - config: Configuration, - cmd: &ImportBlocksCmd, - ) -> AsyncCmdResult<'_> { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - Ok((Box::pin(cmd.run(partial.client, partial.import_queue)), partial.task_manager)) - } - - fn prepare_revert_cmd( - self: Box, - config: Configuration, - cmd: &RevertCmd, - ) -> AsyncCmdResult<'_> { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - Ok((Box::pin(cmd.run(partial.client, partial.backend, None)), partial.task_manager)) - } - - fn run_export_genesis_head_cmd( - self: Box, - config: Configuration, - cmd: &ExportGenesisHeadCommand, - ) -> SyncCmdResult { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - cmd.run(partial.client) - } - - fn run_benchmark_block_cmd( - self: Box, - config: Configuration, - cmd: &BlockCmd, - ) -> SyncCmdResult { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - cmd.run(partial.client) - } - - #[cfg(any(feature = "runtime-benchmarks"))] - fn run_benchmark_storage_cmd( - self: Box, - config: Configuration, - cmd: &StorageCmd, - ) -> SyncCmdResult { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - let db = partial.backend.expose_db(); - let storage = partial.backend.expose_storage(); - - cmd.run(config, partial.client, db, storage) - } - - fn start_node( - self: Box, - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, - node_extra_args: NodeExtraArgs, - ) -> Pin>>> { - match parachain_config.network.network_backend { - sc_network::config::NetworkBackendType::Libp2p => - ::start_node::>( - parachain_config, - polkadot_config, - collator_options, - para_id, - hwbench, - node_extra_args, - ), - sc_network::config::NetworkBackendType::Litep2p => - ::start_node::( - parachain_config, - polkadot_config, - collator_options, - para_id, - hwbench, - node_extra_args, - ), - } - } -} diff --git a/cumulus/polkadot-parachain/src/chain_spec/mod.rs b/cumulus/polkadot-parachain/src/chain_spec/mod.rs index cd704b5bd938..82aec951704f 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/mod.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/mod.rs @@ -18,7 +18,9 @@ use cumulus_primitives_core::ParaId; use parachains_common::{AccountId, Signature}; use polkadot_parachain_lib::{ chain_spec::{GenericChainSpec, LoadSpec}, - runtime::{AuraConsensusId, Consensus, Runtime, RuntimeResolver as RuntimeResolverT}, + runtime::{ + AuraConsensusId, BlockNumber, Consensus, Runtime, RuntimeResolver as RuntimeResolverT, + }, }; use sc_chain_spec::ChainSpec; use sp_core::{Pair, Public}; @@ -289,7 +291,7 @@ impl RuntimeResolverT for RuntimeResolver { let legacy_runtime = LegacyRuntime::from_id(chain_spec.id()); Ok(match legacy_runtime { LegacyRuntime::AssetHubPolkadot => - Runtime::Omni(Consensus::Aura(AuraConsensusId::Ed25519)), + Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Ed25519)), LegacyRuntime::AssetHub | LegacyRuntime::BridgeHub(_) | LegacyRuntime::Collectives | @@ -297,7 +299,8 @@ impl RuntimeResolverT for RuntimeResolver { LegacyRuntime::People(_) | LegacyRuntime::Glutton | LegacyRuntime::Penpal | - LegacyRuntime::Omni => Runtime::Omni(Consensus::Aura(AuraConsensusId::Sr25519)), + LegacyRuntime::Omni => + Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Sr25519)), LegacyRuntime::Shell | LegacyRuntime::Seedling => Runtime::Shell, }) } diff --git a/prdoc/pr_5269.prdoc b/prdoc/pr_5269.prdoc new file mode 100644 index 000000000000..e4401f2406ce --- /dev/null +++ b/prdoc/pr_5269.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Added the possibility to build a parachain node with block number u64 + +doc: + - audience: Node Dev + description: | + Added the possibility to build a parachain node with block number u64. + +crates: + - name: polkadot-parachain-lib + bump: minor + - name: polkadot-parachain-bin + bump: patch From edd6f7176c33e6ba4ac6f7ed2ca7f78b6eb508d8 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 28 Aug 2024 22:12:06 +0300 Subject: [PATCH 178/382] Bridge zombienet tests: remove old command (#5434) Related to: https://github.com/paritytech/polkadot-sdk/issues/3176 This PR: - migrates test 0003 to the new bridges zombienet tests command **This test didn't work before and it still doesn't work. It was added at a time when we couldn't run it because we didn't have the scafolding. It needs to be fixed. For the moment we keep it in the repo as it is since the idea has value. But we don't run it in the CI. We can also decide to remove it in the future** - removes the old command for running bridge zombienet tests - updates the README --- .gitlab/pipeline/zombienet/bridges.yml | 4 +- bridges/testing/README.md | 22 ++- ...ly-required-headers-synced-when-active.js} | 10 +- .../testing/{run-new-test.sh => run-test.sh} | 0 bridges/testing/run-tests.sh | 138 ------------------ ...ynced-while-active-rococo-to-westend.zndsl | 26 ---- ...ynced-while-active-westend-to-rococo.zndsl | 26 ---- .../rococo-to-westend.zndsl | 7 + .../run.sh | 44 ++++++ 9 files changed, 70 insertions(+), 207 deletions(-) rename bridges/testing/framework/js-helpers/{only-required-headers-synced-when-idle.js => only-required-headers-synced-when-active.js} (94%) rename bridges/testing/{run-new-test.sh => run-test.sh} (100%) delete mode 100755 bridges/testing/run-tests.sh delete mode 100644 bridges/testing/tests/0003-required-headers-synced-while-active-rococo-to-westend.zndsl delete mode 100644 bridges/testing/tests/0003-required-headers-synced-while-active-westend-to-rococo.zndsl create mode 100644 bridges/testing/tests/0003-required-headers-synced-while-active/rococo-to-westend.zndsl create mode 100755 bridges/testing/tests/0003-required-headers-synced-while-active/run.sh diff --git a/.gitlab/pipeline/zombienet/bridges.yml b/.gitlab/pipeline/zombienet/bridges.yml index 9d7a8b931193..070bfc8472d5 100644 --- a/.gitlab/pipeline/zombienet/bridges.yml +++ b/.gitlab/pipeline/zombienet/bridges.yml @@ -52,12 +52,12 @@ zombienet-bridges-0001-asset-transfer-works: extends: - .zombienet-bridges-common script: - - /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-new-test.sh 0001-asset-transfer --docker + - /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-test.sh 0001-asset-transfer --docker - echo "Done" zombienet-bridges-0002-free-headers-synced-while-idle: extends: - .zombienet-bridges-common script: - - /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-new-test.sh 0002-free-headers-synced-while-idle --docker + - /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-test.sh 0002-free-headers-synced-while-idle --docker - echo "Done" diff --git a/bridges/testing/README.md b/bridges/testing/README.md index bd467a410d01..158dfd73b1ad 100644 --- a/bridges/testing/README.md +++ b/bridges/testing/README.md @@ -1,31 +1,29 @@ # Bridges Tests for Local Rococo <> Westend Bridge This folder contains [zombienet](https://github.com/paritytech/zombienet/) based integration tests for both -onchain and offchain bridges code. Due to some -[technical difficulties](https://github.com/paritytech/parity-bridges-common/pull/2649#issue-1965339051), we -are using native zombienet provider, which means that you need to build some binaries locally. +onchain and offchain bridges code. -To start those tests, you need to: +Prerequisites for running the tests locally: - download latest [zombienet release](https://github.com/paritytech/zombienet/releases); - build Polkadot binary by running `cargo build -p polkadot --release --features fast-runtime` command in the -[`polkadot-sdk`](https://github.com/paritytech/polkadot-sdk) repository clone; + [`polkadot-sdk`](https://github.com/paritytech/polkadot-sdk) repository clone; - build Polkadot Parachain binary by running `cargo build -p polkadot-parachain-bin --release` command in the -[`polkadot-sdk`](https://github.com/paritytech/polkadot-sdk) repository clone; + [`polkadot-sdk`](https://github.com/paritytech/polkadot-sdk) repository clone; - ensure that you have [`node`](https://nodejs.org/en) installed. Additionally, we'll need globally installed -`polkadot/api-cli` package (use `npm install -g @polkadot/api-cli@beta` to install it); + `polkadot/api-cli` package (use `npm install -g @polkadot/api-cli@beta` to install it); - build Substrate relay by running `cargo build -p substrate-relay --release` command in the -[`parity-bridges-common`](https://github.com/paritytech/parity-bridges-common) repository clone. + [`parity-bridges-common`](https://github.com/paritytech/parity-bridges-common) repository clone; -- copy fresh `substrate-relay` binary, built in previous point, to the `~/local_bridge_testing/bin/substrate-relay`; +- copy the `substrate-relay` binary, built in the previous step, to `~/local_bridge_testing/bin/substrate-relay`; -- change the `POLKADOT_SDK_PATH` and `ZOMBIENET_BINARY_PATH` (and ensure that the nearby variables -have correct values) in the `./run-tests.sh`. +After that, any test can be run using the `run-test.sh` command. +Example: `./run-new-test.sh 0001-asset-transfer` -After that, you could run tests with the `./run-tests.sh` command. Hopefully, it'll show the +Hopefully, it'll show the "All tests have completed successfully" message in the end. Otherwise, it'll print paths to zombienet process logs, which, in turn, may be used to track locations of all spinned relay and parachain nodes. diff --git a/bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js b/bridges/testing/framework/js-helpers/only-required-headers-synced-when-active.js similarity index 94% rename from bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js rename to bridges/testing/framework/js-helpers/only-required-headers-synced-when-active.js index 8c3130e4fd96..61738a21e38e 100644 --- a/bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js +++ b/bridges/testing/framework/js-helpers/only-required-headers-synced-when-active.js @@ -65,8 +65,12 @@ async function run(nodeName, networkInfo, args) { // wait until we have received + delivered messages OR until timeout await utils.pollUntil( exitAfterSeconds, - () => { return atLeastOneMessageReceived && atLeastOneMessageDelivered; }, - () => { unsubscribe(); }, + () => { + return atLeastOneMessageReceived && atLeastOneMessageDelivered; + }, + () => { + unsubscribe(); + }, () => { if (!atLeastOneMessageReceived) { throw new Error("No messages received from bridged chain"); @@ -78,4 +82,4 @@ async function run(nodeName, networkInfo, args) { ); } -module.exports = { run } +module.exports = {run} diff --git a/bridges/testing/run-new-test.sh b/bridges/testing/run-test.sh similarity index 100% rename from bridges/testing/run-new-test.sh rename to bridges/testing/run-test.sh diff --git a/bridges/testing/run-tests.sh b/bridges/testing/run-tests.sh deleted file mode 100755 index fd12b57f5334..000000000000 --- a/bridges/testing/run-tests.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/bin/bash -set -x -shopt -s nullglob - -trap "trap - SIGINT SIGTERM EXIT && killall -q -9 substrate-relay && kill -- -$$" SIGINT SIGTERM EXIT - -# run tests in range [TESTS_BEGIN; TESTS_END) -TESTS_BEGIN=1 -TESTS_END=1000 -# whether to use paths for zombienet+bridges tests container or for local testing -ZOMBIENET_DOCKER_PATHS=0 -while [ $# -ne 0 ] -do - arg="$1" - case "$arg" in - --docker) - ZOMBIENET_DOCKER_PATHS=1 - ;; - --test) - shift - TESTS_BEGIN="$1" - TESTS_END="$1" - ;; - esac - shift -done - -# assuming that we'll be using native provide && all processes will be executing locally -# (we need absolute paths here, because they're used when scripts are called by zombienet from tmp folders) -export POLKADOT_SDK_PATH=`realpath $(dirname "$0")/../..` -export BRIDGE_TESTS_FOLDER=$POLKADOT_SDK_PATH/bridges/testing/tests - -# set path to binaries -if [ "$ZOMBIENET_DOCKER_PATHS" -eq 1 ]; then - export POLKADOT_BINARY=/usr/local/bin/polkadot - export POLKADOT_PARACHAIN_BINARY=/usr/local/bin/polkadot-parachain - - export SUBSTRATE_RELAY_BINARY=/usr/local/bin/substrate-relay - export ZOMBIENET_BINARY_PATH=/usr/local/bin/zombie -else - export POLKADOT_BINARY=$POLKADOT_SDK_PATH/target/release/polkadot - export POLKADOT_PARACHAIN_BINARY=$POLKADOT_SDK_PATH/target/release/polkadot-parachain - - export SUBSTRATE_RELAY_BINARY=~/local_bridge_testing/bin/substrate-relay - export ZOMBIENET_BINARY_PATH=~/local_bridge_testing/bin/zombienet-linux -fi - -# check if `wait` supports -p flag -if [ `printf "$BASH_VERSION\n5.1" | sort -V | head -n 1` = "5.1" ]; then IS_BASH_5_1=1; else IS_BASH_5_1=0; fi - -# bridge configuration -export LANE_ID="00000002" - -# tests configuration -ALL_TESTS_FOLDER=`mktemp -d /tmp/bridges-zombienet-tests.XXXXX` - -function start_coproc() { - local command=$1 - local name=$2 - local logname=`basename $name` - local coproc_log=`mktemp -p $TEST_FOLDER $logname.XXXXX` - coproc COPROC { - # otherwise zombienet uses some hardcoded paths - unset RUN_IN_CONTAINER - unset ZOMBIENET_IMAGE - - $command >$coproc_log 2>&1 - } - TEST_COPROCS[$COPROC_PID, 0]=$name - TEST_COPROCS[$COPROC_PID, 1]=$coproc_log - echo "Spawned $name coprocess. StdOut + StdErr: $coproc_log" - - return $COPROC_PID -} - -# execute every test from tests folder -TEST_INDEX=$TESTS_BEGIN -while true -do - declare -A TEST_COPROCS - TEST_COPROCS_COUNT=0 - TEST_PREFIX=$(printf "%04d" $TEST_INDEX) - - # it'll be used by the `sync-exit.sh` script - export TEST_FOLDER=`mktemp -d -p $ALL_TESTS_FOLDER test-$TEST_PREFIX.XXXXX` - - # check if there are no more tests - zndsl_files=($BRIDGE_TESTS_FOLDER/$TEST_PREFIX-*.zndsl) - if [ ${#zndsl_files[@]} -eq 0 ]; then - break - fi - - # start tests - for zndsl_file in "${zndsl_files[@]}"; do - start_coproc "$ZOMBIENET_BINARY_PATH --provider native test $zndsl_file" "$zndsl_file" - echo -n "1">>$TEST_FOLDER/exit-sync - ((TEST_COPROCS_COUNT++)) - done - # wait until all tests are completed - for n in `seq 1 $TEST_COPROCS_COUNT`; do - if [ "$IS_BASH_5_1" -eq 1 ]; then - wait -n -p COPROC_PID - exit_code=$? - coproc_name=${TEST_COPROCS[$COPROC_PID, 0]} - coproc_log=${TEST_COPROCS[$COPROC_PID, 1]} - coproc_stdout=$(cat $coproc_log) - else - wait -n - exit_code=$? - coproc_name="" - coproc_stdout="" - fi - echo "Process $coproc_name has finished with exit code: $exit_code" - - # if exit code is not zero, exit - if [ $exit_code -ne 0 ]; then - echo "=====================================================================" - echo "=== Shutting down. Log of failed process below ===" - echo "=====================================================================" - echo "$coproc_stdout" - - exit 1 - fi - done - - # proceed to next index - ((TEST_INDEX++)) - if [ "$TEST_INDEX" -ge "$TESTS_END" ]; then - break - fi - - # kill relay here - it is started manually by tests - killall substrate-relay -done - -echo "=====================================================================" -echo "=== All tests have completed successfully ===" -echo "=====================================================================" diff --git a/bridges/testing/tests/0003-required-headers-synced-while-active-rococo-to-westend.zndsl b/bridges/testing/tests/0003-required-headers-synced-while-active-rococo-to-westend.zndsl deleted file mode 100644 index 07b91481dc7c..000000000000 --- a/bridges/testing/tests/0003-required-headers-synced-while-active-rococo-to-westend.zndsl +++ /dev/null @@ -1,26 +0,0 @@ -Description: While relayer is active, we only sync mandatory and required Rococo (and Rococo BH) headers to Westend BH. -Network: ../environments/rococo-westend/bridge_hub_westend_local_network.toml -Creds: config - -# step 1: initialize Westend AH -asset-hub-westend-collator1: run ../scripts/invoke-script.sh with "init-asset-hub-westend-local" within 60 seconds - -# step 2: initialize Westend bridge hub -bridge-hub-westend-collator1: run ../scripts/invoke-script.sh with "init-bridge-hub-westend-local" within 60 seconds - -# step 3: ensure that initialization has completed -asset-hub-westend-collator1: js-script ../js-helpers/wait-hrmp-channel-opened.js with "1002" within 600 seconds - -# step 4: send message from Westend to Rococo -asset-hub-westend-collator1: run ../scripts/invoke-script.sh with "reserve-transfer-assets-from-asset-hub-westend-local" within 60 seconds - -# step 5: start relayer -# (we are starting it after sending the message to be sure that relayer won't relay messages before our js script -# will be started at step 6) -# (it is started by sibling 0003-required-headers-synced-while-active-westend-to-rococo.zndsl) - -# step 6: ensure that relayer won't sync any extra headers while delivering messages and confirmations -bridge-hub-westend-collator1: js-script ../js-helpers/only-required-headers-synced-when-active.js with "500,rococo-at-westend" within 600 seconds - -# wait until other network test has completed OR exit with an error too -asset-hub-westend-collator1: run ../scripts/sync-exit.sh within 600 seconds diff --git a/bridges/testing/tests/0003-required-headers-synced-while-active-westend-to-rococo.zndsl b/bridges/testing/tests/0003-required-headers-synced-while-active-westend-to-rococo.zndsl deleted file mode 100644 index a6b11fc24052..000000000000 --- a/bridges/testing/tests/0003-required-headers-synced-while-active-westend-to-rococo.zndsl +++ /dev/null @@ -1,26 +0,0 @@ -Description: While relayer is active, we only sync mandatory and required Westend (and Westend BH) headers to Rococo BH. -Network: ../environments/rococo-westend/bridge_hub_rococo_local_network.toml -Creds: config - -# step 1: initialize Rococo AH -asset-hub-rococo-collator1: run ../scripts/invoke-script.sh with "init-asset-hub-rococo-local" within 60 seconds - -# step 2: initialize Rococo bridge hub -bridge-hub-rococo-collator1: run ../scripts/invoke-script.sh with "init-bridge-hub-rococo-local" within 60 seconds - -# step 3: ensure that initialization has completed -asset-hub-rococo-collator1: js-script ../js-helpers/wait-hrmp-channel-opened.js with "1013" within 600 seconds - -# step 4: send message from Rococo to Westend -asset-hub-rococo-collator1: run ../scripts/invoke-script.sh with "reserve-transfer-assets-from-asset-hub-rococo-local" within 60 seconds - -# step 5: start relayer -# (we are starting it after sending the message to be sure that relayer won't relay messages before our js script -# will be started at step 6) -bridge-hub-rococo-collator1: run ../scripts/start-relayer.sh within 60 seconds - -# step 6: ensure that relayer won't sync any extra headers while delivering messages and confirmations -bridge-hub-rococo-collator1: js-script ../js-helpers/only-required-headers-synced-when-active.js with "500,westend-at-rococo" within 600 seconds - -# wait until other network test has completed OR exit with an error too -asset-hub-rococo-collator1: run ../scripts/sync-exit.sh within 600 seconds diff --git a/bridges/testing/tests/0003-required-headers-synced-while-active/rococo-to-westend.zndsl b/bridges/testing/tests/0003-required-headers-synced-while-active/rococo-to-westend.zndsl new file mode 100644 index 000000000000..897b79eeff23 --- /dev/null +++ b/bridges/testing/tests/0003-required-headers-synced-while-active/rococo-to-westend.zndsl @@ -0,0 +1,7 @@ +Description: While relayer is active, we only sync mandatory and required Rococo (and Rococo BH) headers to Westend BH. +Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml +Creds: config + +# ensure that relayer won't sync any extra headers while delivering messages and confirmations +bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/only-required-headers-synced-when-active.js with "500,rococo-at-westend" within 600 seconds + diff --git a/bridges/testing/tests/0003-required-headers-synced-while-active/run.sh b/bridges/testing/tests/0003-required-headers-synced-while-active/run.sh new file mode 100755 index 000000000000..8fad38f22052 --- /dev/null +++ b/bridges/testing/tests/0003-required-headers-synced-while-active/run.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +set -e + +# TODO: This test doesn't work. It was added at a time when we couldn't run it because we didn't have the scafolding. +# It needs to be fixed. For the moment we keep it in the repo as it is since the idea has value. +# But we don't run it in the CI. + +source "${BASH_SOURCE%/*}/../../framework/utils/common.sh" +source "${BASH_SOURCE%/*}/../../framework/utils/zombienet.sh" + +export ENV_PATH=`realpath ${BASH_SOURCE%/*}/../../environments/rococo-westend` + +logs_dir=$TEST_DIR/logs + +$ENV_PATH/spawn.sh --init & +env_pid=$! + +ensure_process_file $env_pid $TEST_DIR/rococo.env 600 +rococo_dir=`cat $TEST_DIR/rococo.env` +echo + +ensure_process_file $env_pid $TEST_DIR/westend.env 300 +westend_dir=`cat $TEST_DIR/westend.env` +echo + +echo "Sending message from Rococo to Westend" +$ENV_PATH/helper.sh auto-log reserve-transfer-assets-from-asset-hub-rococo-local 5000000000000 +echo + +echo "Sending message from Westend to Rococo" +$ENV_PATH/helper.sh auto-log reserve-transfer-assets-from-asset-hub-westend-local 5000000000000 +echo + + +# Start the relayer with a 30s delay +# We want to be sure that the messages won't be relayed before starting the js script in `rococo-to-westend.zndsl` +start_relayer_log=$logs_dir/start_relayer.log +echo -e "The rococo-westend relayer will be started in 30s. Logs will be available at: $start_relayer_log\n" +(sleep 30 && $ENV_PATH/start_relayer.sh \ + $rococo_dir $westend_dir finality_relayer_pid parachains_relayer_pid messages_relayer_pid > $start_relayer_log)& + +run_zndsl ${BASH_SOURCE%/*}/rococo-to-westend.zndsl $westend_dir + From f7e0ecc5d407ca0262bcfbb490472d762a7e09ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:14:31 +0000 Subject: [PATCH 179/382] Bump rustversion from 1.0.14 to 1.0.17 (#5405) Bumps [rustversion](https://github.com/dtolnay/rustversion) from 1.0.14 to 1.0.17.
Release notes

Sourced from rustversion's releases.

1.0.17

  • Support Windows builds that have OUT_DIR prefixed with \\?\ (#51)

1.0.16

  • Resolve unexpected_cfgs warning (#48)

1.0.15

  • Recognize $RUSTC_WRAPPER environment variable (#47)
Commits
  • adb11fa Release 1.0.17
  • 8759820 Merge pull request #51 from dtolnay/windows
  • cfafcd5 Support OUT_DIR located in \?\ path on Windows
  • c7bc274 Release 1.0.16
  • 746bf5a Merge pull request #48 from dtolnay/checkcfg
  • 84f01fa Resolve unexpected_cfgs warning
  • 70ca5ad Release 1.0.15
  • 0fa74f5 Merge pull request #47 from dtolnay/rustcwrapper
  • 51f46e2 Apply RUSTC_WRAPPER
  • df7e51d Explicitly install a Rust toolchain for cargo-outdated job
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rustversion&package-manager=cargo&previous-version=1.0.14&new-version=1.0.17)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 07295630b129..17df8d63a3da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17095,9 +17095,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" diff --git a/Cargo.toml b/Cargo.toml index 9d39962f8974..ebcaa0867faa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1115,7 +1115,7 @@ rstest = { version = "0.18.2" } rustc-hash = { version = "1.1.0" } rustc-hex = { version = "2.1.0", default-features = false } rustix = { version = "0.36.7", default-features = false } -rustversion = { version = "1.0.6" } +rustversion = { version = "1.0.17" } rusty-fork = { version = "0.3.0", default-features = false } safe-mix = { version = "1.0", default-features = false } sc-allocator = { path = "substrate/client/allocator", default-features = false } From 942371816fff40e68e08a25ccbce8768a0fd0dca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:14:49 +0200 Subject: [PATCH 180/382] Bump blake2b_simd from 1.0.1 to 1.0.2 (#5404) Bumps [blake2b_simd](https://github.com/oconnor663/blake2_simd) from 1.0.1 to 1.0.2.
Commits
  • 1dfcf32 version 1.0.2
  • b66d6b5 Bump constant_time_eq to 0.3 and MSRV to 1.66
  • d9ce189 stop using MIPS for big-endian testing
  • 9d338b5 use avoid-dev-deps for the MSRV test
  • c347f49 update the assert_cmd test dependency to v2.0.8
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=blake2b_simd&package-manager=cargo&previous-version=1.0.1&new-version=1.0.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17df8d63a3da..bb0f01542d31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1613,13 +1613,13 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ "arrayref", "arrayvec 0.7.4", - "constant_time_eq 0.2.6", + "constant_time_eq 0.3.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ebcaa0867faa..f26a894960a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -621,7 +621,7 @@ bip39 = { version = "2.0.0" } bitflags = { version = "1.3.2" } bitvec = { version = "1.0.1", default-features = false } blake2 = { version = "0.10.4", default-features = false } -blake2b_simd = { version = "1.0.1", default-features = false } +blake2b_simd = { version = "1.0.2", default-features = false } blake3 = { version = "1.5" } bounded-collections = { version = "0.2.0", default-features = false } bounded-vec = { version = "0.7" } From 4096ad7f8f769f740cb43669b4e50feb722d19dc Mon Sep 17 00:00:00 2001 From: Pankaj Date: Thu, 29 Aug 2024 00:48:36 +0530 Subject: [PATCH 181/382] Remove pallet::getter usage from treasury (#4962) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ISSUE Link to the issue: https://github.com/paritytech/polkadot-sdk/issues/3326 Deliverables [Deprecation] remove pallet::getter usage from pallet-treasury --------- Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Dónal Murray Co-authored-by: Dónal Murray Co-authored-by: Bastian Köcher --- prdoc/pr_4962.prdoc | 14 ++++++++ substrate/frame/treasury/src/benchmarking.rs | 4 +-- substrate/frame/treasury/src/lib.rs | 34 +++++++++++++------- substrate/frame/treasury/src/tests.rs | 6 ++-- 4 files changed, 42 insertions(+), 16 deletions(-) create mode 100644 prdoc/pr_4962.prdoc diff --git a/prdoc/pr_4962.prdoc b/prdoc/pr_4962.prdoc new file mode 100644 index 000000000000..0957f85b6628 --- /dev/null +++ b/prdoc/pr_4962.prdoc @@ -0,0 +1,14 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Removed `pallet::getter` usage from the pallet-treasury + +doc: + - audience: Runtime Dev + description: | + This PR removed `pallet::getter`s from `pallet-treasury`s storage items. + Instead use the syntax `StorageItem::::get()`. + +crates: + - name: pallet-treasury + bump: minor \ No newline at end of file diff --git a/substrate/frame/treasury/src/benchmarking.rs b/substrate/frame/treasury/src/benchmarking.rs index 63978c94e682..e63febb5798b 100644 --- a/substrate/frame/treasury/src/benchmarking.rs +++ b/substrate/frame/treasury/src/benchmarking.rs @@ -78,7 +78,7 @@ fn create_approved_proposals, I: 'static>(n: u32) -> Result<(), &'s let (_, value, lookup) = setup_proposal::(i); Treasury::::spend_local(origin.clone(), value, lookup)?; } - ensure!(>::get().len() == n as usize, "Not all approved"); + ensure!(Approvals::::get().len() == n as usize, "Not all approved"); Ok(()) } @@ -130,7 +130,7 @@ mod benchmarks { T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let (_, value, beneficiary_lookup) = setup_proposal::(SEED); Treasury::::spend_local(origin, value, beneficiary_lookup)?; - let proposal_id = Treasury::::proposal_count() - 1; + let proposal_id = ProposalCount::::get() - 1; let reject_origin = T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; diff --git a/substrate/frame/treasury/src/lib.rs b/substrate/frame/treasury/src/lib.rs index 3954489a2d15..edb39f230642 100644 --- a/substrate/frame/treasury/src/lib.rs +++ b/substrate/frame/treasury/src/lib.rs @@ -100,7 +100,7 @@ use frame_support::{ ReservableCurrency, WithdrawReasons, }, weights::Weight, - PalletId, + BoundedVec, PalletId, }; pub use pallet::*; @@ -278,12 +278,10 @@ pub mod pallet { /// Number of proposals that have been made. #[pallet::storage] - #[pallet::getter(fn proposal_count)] - pub(crate) type ProposalCount = StorageValue<_, ProposalIndex, ValueQuery>; + pub type ProposalCount = StorageValue<_, ProposalIndex, ValueQuery>; /// Proposals that have been made. #[pallet::storage] - #[pallet::getter(fn proposals)] pub type Proposals, I: 'static = ()> = StorageMap< _, Twox64Concat, @@ -299,7 +297,6 @@ pub mod pallet { /// Proposal indices that have been approved but not yet awarded. #[pallet::storage] - #[pallet::getter(fn approvals)] pub type Approvals, I: 'static = ()> = StorageValue<_, BoundedVec, ValueQuery>; @@ -335,7 +332,7 @@ pub mod pallet { impl, I: 'static> BuildGenesisConfig for GenesisConfig { fn build(&self) { // Create Treasury account - let account_id = >::account_id(); + let account_id = Pallet::::account_id(); let min = T::Currency::minimum_balance(); if T::Currency::free_balance(&account_id) < min { let _ = T::Currency::make_free_balance_be(&account_id, min); @@ -501,7 +498,7 @@ pub mod pallet { .unwrap_or(Ok(()))?; let beneficiary = T::Lookup::lookup(beneficiary)?; - let proposal_index = Self::proposal_count(); + let proposal_index = ProposalCount::::get(); Approvals::::try_append(proposal_index) .map_err(|_| Error::::TooManyApprovals)?; let proposal = Proposal { @@ -794,6 +791,21 @@ impl, I: 'static> Pallet { T::PalletId::get().into_account_truncating() } + /// Public function to proposal_count storage. + pub fn proposal_count() -> ProposalIndex { + ProposalCount::::get() + } + + /// Public function to proposals storage. + pub fn proposals(index: ProposalIndex) -> Option>> { + Proposals::::get(index) + } + + /// Public function to approvals storage. + pub fn approvals() -> BoundedVec { + Approvals::::get() + } + /// Spend some money! returns number of approvals before spend. pub fn spend_funds() -> Weight { let mut total_weight = Weight::zero(); @@ -803,15 +815,15 @@ impl, I: 'static> Pallet { let account_id = Self::account_id(); let mut missed_any = false; - let mut imbalance = >::zero(); + let mut imbalance = PositiveImbalanceOf::::zero(); let proposals_len = Approvals::::mutate(|v| { let proposals_approvals_len = v.len() as u32; v.retain(|&index| { // Should always be true, but shouldn't panic if false or we're screwed. - if let Some(p) = Self::proposals(index) { + if let Some(p) = Proposals::::get(index) { if p.value <= budget_remaining { budget_remaining -= p.value; - >::remove(index); + Proposals::::remove(index); // return their deposit. let err_amount = T::Currency::unreserve(&p.proposer, p.bond); @@ -982,6 +994,6 @@ where { type Type = ::AccountId; fn get() -> Self::Type { - >::account_id() + crate::Pallet::::account_id() } } diff --git a/substrate/frame/treasury/src/tests.rs b/substrate/frame/treasury/src/tests.rs index f38a06f1fdf4..a895ea8151af 100644 --- a/substrate/frame/treasury/src/tests.rs +++ b/substrate/frame/treasury/src/tests.rs @@ -219,7 +219,7 @@ fn get_payment_id(i: SpendIndex) -> Option { fn genesis_config_works() { ExtBuilder::default().build().execute_with(|| { assert_eq!(Treasury::pot(), 0); - assert_eq!(Treasury::proposal_count(), 0); + assert_eq!(ProposalCount::::get(), 0); }); } @@ -437,9 +437,9 @@ fn remove_already_removed_approval_fails() { assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 100, 3)); - assert_eq!(Treasury::approvals(), vec![0]); + assert_eq!(Approvals::::get(), vec![0]); assert_ok!(Treasury::remove_approval(RuntimeOrigin::root(), 0)); - assert_eq!(Treasury::approvals(), vec![]); + assert_eq!(Approvals::::get(), vec![]); assert_noop!( Treasury::remove_approval(RuntimeOrigin::root(), 0), From 8e0cefc80ebb2df75d507039981cd16dc5d773f5 Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Wed, 28 Aug 2024 20:26:03 +0100 Subject: [PATCH 182/382] Command bot GHA v2 - /cmd (#5457) Closes https://github.com/paritytech/product-engineering/issues/93 - Deprecates old command bot bash scripts. New cmd implementation is re-written on Python - Deprecates `sync` command, which was never used - Benchmarks: - Uses new [frame-omni-bencher](https://crates.io/crates/frame-omni-bencher) - Simplifies usage to only providing a runtime and/or pallet name (even multiple runtimes or pallets) - Supports sub-modules (like `/cmd bench --runtime dev --pallet pallet_asset_conversion_ops`) - Can regenerate all weights with one command (substrate, polkadot, cumulus) for provided pallet(s) name - Adds [subweight](https://crates.io/crates/subweight-core) diff as a result of bench command --- .github/command-screnshot.png | Bin 31548 -> 0 bytes .github/commands-readme.md | 276 ------------- .github/scripts/cmd/_help.py | 26 ++ .github/scripts/cmd/cmd.py | 196 +++++++++ .github/workflows/cmd.yml | 411 +++++++++++++++++++ .github/workflows/command-bench-all.yml | 99 ----- .github/workflows/command-bench-overhead.yml | 78 ---- .github/workflows/command-bench.yml | 124 ------ .github/workflows/command-fmt.yml | 65 --- .github/workflows/command-inform.yml | 22 - .github/workflows/command-prdoc.yml | 90 ---- .github/workflows/command-sync.yml | 71 ---- .github/workflows/command-update-ui.yml | 59 --- .github/workflows/review-bot.yml | 1 - .github/workflows/runtimes-matrix.json | 98 +++++ .gitignore | 1 + docs/contributor/CONTRIBUTING.md | 12 +- docs/contributor/commands-readme.md | 44 ++ docs/contributor/weight-generation.md | 69 ++++ scripts/bench-all.sh | 16 - scripts/bench.sh | 117 ------ scripts/command-utils.sh | 80 ---- scripts/lib/bench-all-cumulus.sh | 139 ------- scripts/lib/bench-all-pallet.sh | 96 ----- scripts/lib/bench-all-polkadot.sh | 88 ---- scripts/lib/bench-all-substrate.sh | 148 ------- scripts/lib/bench-overhead.sh | 66 --- scripts/lib/bench-pallet.sh | 178 -------- scripts/sync.sh | 74 ---- scripts/update-ui-tests.sh | 41 -- 30 files changed, 851 insertions(+), 1934 deletions(-) delete mode 100644 .github/command-screnshot.png delete mode 100644 .github/commands-readme.md create mode 100644 .github/scripts/cmd/_help.py create mode 100755 .github/scripts/cmd/cmd.py create mode 100644 .github/workflows/cmd.yml delete mode 100644 .github/workflows/command-bench-all.yml delete mode 100644 .github/workflows/command-bench-overhead.yml delete mode 100644 .github/workflows/command-bench.yml delete mode 100644 .github/workflows/command-fmt.yml delete mode 100644 .github/workflows/command-inform.yml delete mode 100644 .github/workflows/command-prdoc.yml delete mode 100644 .github/workflows/command-sync.yml delete mode 100644 .github/workflows/command-update-ui.yml create mode 100644 .github/workflows/runtimes-matrix.json create mode 100644 docs/contributor/commands-readme.md create mode 100644 docs/contributor/weight-generation.md delete mode 100755 scripts/bench-all.sh delete mode 100755 scripts/bench.sh delete mode 100644 scripts/command-utils.sh delete mode 100755 scripts/lib/bench-all-cumulus.sh delete mode 100644 scripts/lib/bench-all-pallet.sh delete mode 100644 scripts/lib/bench-all-polkadot.sh delete mode 100644 scripts/lib/bench-all-substrate.sh delete mode 100644 scripts/lib/bench-overhead.sh delete mode 100644 scripts/lib/bench-pallet.sh delete mode 100755 scripts/sync.sh delete mode 100755 scripts/update-ui-tests.sh diff --git a/.github/command-screnshot.png b/.github/command-screnshot.png deleted file mode 100644 index 1451fabca8b975534778e8321facd261e3b803fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31548 zcmdSAg;!hK^ZyMLEf(A%K!M`!TA)~qyIU!xxVsfE?oL|V-QC^Y-Q8V&>FvFr_WKt+ z>sjk0D=W!4nX~uI-ZQUxCrD049Qif=YX}GkWJw881qcYJKnMuPFnCz-JD9iUJP;5_ zf+iv&a*`qu_k#}c+=a2Q}gPs?FF=h=3 zrZ{Si4Q83VCMeuUWbrkGdh9^#Cx{;oc;_KdIDvs>zY?i?K1F;MPL;!#-Bkg>b zp3-YY0g7WlS{Fi6h3An@7zqkcLtXd#*4WCGHALCBsg}i9NvhrcnflIFD`>G~aORbX z1JsAS7aqk{`F+E$eu-z207cU3w*X2p;X>FlLmdaLgz?WG^(lF6M#acNW4kONtqX-P z4@*XSUXN3}E0cN{UuhjtAk9r1zpu_iyxX1lYDQAc_|Hz9V|KJbT(! zcM1dP{k_g`2})i#MPbK>Yei6n89V2@ZF=l=Z6c6LtvmVMVU#K$hcK9Gy8|X~v}dC0 zZtUzk*I7HA#g|GlVmCZ^I5PkQuFz{!`~;+OuhOQPGMr^E;7kw z@^3R-a`4Z-5)kM9-FE}7Fh3y9bpzD8rXVv1ToE_P%i9zKZQtScdfzDAy&a3F5j}-w zY4>#6Y47(r+is;fc)585KBVfuqtxn?fa7PGgrg7g?u0OaXAb-lOt30Yi$jc3;!(=8 z@o~}6ig@J+K^B3?SKRl&SEl_)vYo6?R$k0YCYL00@cn#vU2D;gY}(^+R}uT%kb`yD zR!B#054Bpx%fr{|)hrh3D!76sFXpg98OfW@>)8uVXVhhqz7B&u>|5VM*BARN-~-bF4+SgUX-n8<HI>b_%^a60i5sj(wHJ{d=PVq48=4h*3M5XOkrmORUqKFHBZT`Z(QOD29^ogcohS+OJ32JiPCR`w z0SO>_SSMMZ1WlB8Ab^?$BQZRNbgP%=6RU#H;d>NPs>lloI2!`zz)N6Br0iS&{Pg); zw6A_)qKR)2kwrGf5Wc|jz|e~Zk5TO6SK=@Ear~ef3*NP_#Ble%HiS@Yvtw2qgfuiT zqkH|KZJZf_gMhY+ru1teimCs|9nEyml>v!0nM@B}?JKP|QYWb4j-1tsv#whnH@Ic5 znswE)04I{?P$6<$^h1O+XcT=qQXi&RIpJKO89Cvb6o1#e;L|XPH~Z*CLC02%lo%C( ztbxHn2SHbz7@a8Vh6aK`qT^I^(T$SwZy2aSO2Qf+=HK(k)C=#ExdRI#qg zp+5Hbg}Tyh9$f?Z2=fT?$Om70NIo>9TQxaDC2#tN!~3Hwp7%=%saXQ@O>#}b?k)Ee zS31bw{bOM@eF~U0GZZ!=x}`%sr0cie4!s@H8tO{oWgjthFpaD%tc0q%)#%dDtdud~ z&o;EP{gWNDfkGyP5BSTTF zsk^@0qg%b3HZ&aXCSg?C?V~Rxt#nv2pYbk}S_ok`z8G!B$mA$nr_$H@xyq%>io`1Q zZ|afiO>^$H`P>tHOKw@t!OkGRp!dFAAA>c5dE+oYWE*8~eI2NXJ1HtGiYmlbW7n|P zJW#hSL@Y8bI++_ab2HDLRa(%hpqOVduP~ok(5V(V(mG-}qB&xi%ec0<7Jj(8CL)I? z=MxJQQwp!$`?23@^VTZY-f1`Y0QXpJb9VRECbO$ICUQVLjp%NuZ;HCdq+nu6UL!+T z94quI@zwm?aN>^Eo|`$Fx!ve4cSf>Ui%pp$TCJH?-IkPNsFn8W{QCXW(314J%FgtL z#ai0F&Xz}X-cZPAhR#t4^D0ClET3dTkx#niK2A##4# z96FNS$W+d{I-6~rYQ%99e3tSEZ`G7zaEd!l)k#}UT}v$^%b=RztRSmU?Zqy}XzqXA zH=l4Nz)R!mdMzZQAmh%!&B=ZfA@L!G{%fikpY!}CN?B8_=puKQZGS$sdlXY&HQyUc zVI9S0j>kyL=y?gxNCCQ2>Rh^8CU)!nXOpA}(TVx&1!-NjCv|D)9LNG-owf{dfUMr%#pn$0zStczWv5S=N24ptXQ0;R1jNB-E6Lt#o31LH_tqJ zdD_UBMXl8;y$xZvpH})0>0`mu4EHMij7@UeN-FZ#M$DFHe!AjM_1iPv6nh`$}~mD_DCC`YTLmRRM@)+W_!)Ysce-zH^OVVlMm zJ(V-qE?r5z^xxBTX^}D3T^{( z!UAeYnE4f)m3DT=4JQq|dSJtFh))Db1;$)iHa0inB}L0DjlT`2 z@C@*{KBnB%k?-)2kkq#}jyjDU8HgmCF0`#^dkW4TPcK{bJt<5Wg%1o78S;c_pEcQ> ze{G#&o#C2c&6s@eV9u9#tS+bZW6CmbSYq>XL)l&8gOdF3ZamGtYzb>MX(fm$rsQN6<>V$LLGx2KmLt zoU9}$%{}ej@6u>Ub)jTvrM-EHpqB_2U`wHOR{{Y`o0NvqdT<0W8U^{I;{wkrm5MtoIJzY9-ZXAvUT=jDXC>GWiq62i5*eFJAwl>}9Y4cW^H)hmpcUkPsH>$z=K zAz+PLPjPdma*^%S^85PWoyekKjZpSJl!wI&%zdkyVW3%BF1VuLGE$TLEG-Q|1AYw; z0U2Nd0S$fy34Y*%AK>ES2h@M>Kn4DI_1|mAuwNfO9$UshKnOudioR2Ff;>!xb5nYc z(`EU($uScSvP>Qm6EZeEkVv#k7Jx|B^?kO%AS;k85Ev+dg-JKtRjeili6s|-GHxnN zZY61%g>Y!WmVRCPFRr!J7r)O7xAE|hlZpYX=kZa+FlFYIbS zF|RQYTEgp}1E_&O_;HSi%^F?|kjNA$x@8_M945hL@WHrut;MV=a#~YGW-p2*wx}wO zhC7Ep3PlWnTx1;a4 z8$C-1c}is!P>C@mrIl}3r^-~B{x=6gQ2OFPTw9b1(->NGQZdBwbs*8k21Td>;_r%n}#DC00V_ujR}FF7&%b|2Gm5LTT{68ie(+ zEQqR$wN~&rG%CW#p7Amm-*Y4E1@EU($e1ne!h8~^eqP~!$M$=0w5UjHw=t5E+>7eU zavCFxN=U%%RorvMNsIj7B_R+9m7yYR0#Jye_#dtZiZ}Vo-FGb9J)N+FW2hMc!6w-( zu(%GxbbwGTrF;d(S1U~-x~TeU%0-jZO1)uHIXHu_{!CSb5EllQ(`kg69Bomk+~mS} z3|*?gX5rW49qBG5#?Ai(w(&u@Lf$IpZYjEapKGQzi1E<6^ zs6VHDzq$vHH2d>B2NEhIOBmR*_{Z*Xhuq#k5&ShZaS)Y6Y{Zpm({2Dihd^%WH#Df} zuYn36l1QaOH5uKa5bgjrbRgt^hxaa-9vd+mNhN4`j8Yp_7_-~dI(NPhBjy`lrDX{v zgi#+1_1M*CJ<~syK|udF`*EeG@j#9IrG{TgSbpRk+d6m@3ZzT*XZ!ud{_{w%QGitF zkZIO=)CrVOi4eyw()PTsBZRou+?jvRl_(Wt#?_Cf5GFPRPugD-~K-ra;o~h*TMFITW zN)bVdKEWSovOY@W<50o+@$dU=f@WdUOQKY=1x(;4M*^BcjR*b=m=`n@29pM0eiY9D zB_*-!Z)3{dwbH3b5*SoZpiV3Wm9=>lz4^y+21 z|6Z^Xgx7M%tTRDr)}N18Wq~4y*+cJ?)&c!vVQddOJKTRe^d!0$tS>ej`G5vc0awi2 zvqJx`Qxm<$e1i~Yh~l^^WsiZ0&+VcmCX+5Y*U5;E-#=Si4gNmtVstVg-YB0 zJ}nVm&3pGmV=yKf?eDkcKeSGLGYM9zb>aTXn~eOIFO5Kt2P783ja~Q+O8jT?uZWm} zc7t9=yAs91|9im9&`xK(G=TMn!)1OxMHGMe026cw<>eVLNtF2S+cSvwa$%`hZ~|d) zdh7oe%iy!Tb17E82w`QyF@NPv`pX>eMzmf);C`L!{~29e@R!>QSW&WV~ zejOuEyC7~*w^WXy;gN=*#bC~WAS|XejEHwD?SVLkWc&FoH8pkpe7xfxoe#*BMznx z>DY2wN%M0zN7>h@d>h-9?hSKcs)&!u(u*LxI84NoI!;v#x|Q7z`IQZa8y zW%=*8a@f?!kbbX#qLkiVP|PgJQ41P|U#y0bs-RHiw)$f?XY1|x=_A0;1!)_#raKII zYGvOhm2C+>#L?~DU907mS+3}$wn#*hC1SJa2`ogCn}qYwY1dbV&$d2FA=PxCqoZfp z?J4jaKG%3Wd6qjY@klPG79PaMD2YqO_NSoE)Y?;PR3=z>=DQ583%=0BrZzbANm8W& z0sP!}+e<_ex}E4aIE+REacC1^MGMt)0|hF1WD7NxI9C&*{jv0>8%@x1C7$sm5z+*l zeQhFeFh)}aj)iY1d0Nbs-dQ9Ix6MpqAHCNJ1o|2 z*g9Pk;URYS=GnLxe9_3qz~6cdh%sZhF$?m3$!!$SH^^4u;Jw1cW7);a({517W`j?I zBk}ks@VLA2(WJ>mhw)~awN-r3s6U37`RgUFvq|S$_v!7!icgQVV;l#^OAT2j71-Bq zgS6zEi?;I%7o0gQ6sUstz5)Tc33t6@7nT39cJOZfLv&{N&7yryYsZKGmcV$SkMN>_9Rnqe^DY)z5bz>e zRVJQkjW5Rw^RA!A*JAgl8gzxu{P;@32pCfdMY zq!vFW@lCyqn~jjp7B>lqC{^-h_=Vs;yH(F@a-2Y7~L62g{3XliHaN?2? zVZUyMv`p)ffbS3@BkqF9AbC;H=@WXoJ$9|AqHY1o&- z=pz4j1@H8Svpx`7ZZs;gudX@W3aTy>Oe_zWB^Iy zO83yynK6Msu8;X#x$^VO6Avwlyr5Z`&0(4S)`DeA{D=T3eR}Veg<7 z6?RmLhXyPAYr+!$Tw2)v{Eh0opBttwmzWz_-)_#P#>lJU(Fch=5B0gzJ5LN{`HTiy zp~5!8Epw?_uo<*^yN$7Kx8ilXu6U;N)fMZtrX`u|_#S*v&22V))PkFHS0Mz(S%T>7 z&9{Iuv`cm4>Nv}0>4E_g&qpH|GU!y(Sx&2o>>-!1H_)B_$OL9K(cPhTxCOpTw`Ydp zA1=0Y_A5;%OV7x^&N>^^8=p#7k*sx~E?EY!n(Oa*CqG=rK{_rUSGe>Eq7JXoH$8U? zIP~ItGkmONb`YRY5xm;UWC z;%5~?G8D4yu>caLJ}7HL?0)FzlBRbNP`C2yj41MUrT1u0mYt;ie#SG}Unc7&vcO+& zsyg?r_lIHsFP&k6HV_6@b1yiyb-APR72+uBVOSvf8Poy_lC^F9@Ss0bOlUqd%J6o~ zkYD$JJnQ1&kZPnzh{edZ@!CBejvU=PJ>6Yy%60cYt2wu|`*AE`34QKG528x3Y8*0z zI?bI@EpQK|S0Ww|au5rifO>EIy<5YMH8+PvgAHTDRP89Dz z^uPd=EOR{cT-Q=PQrZr=-AsfMghr!c{S3oF-p2iXVw+<0VAJX1s=k3Z`Wk)cRnkk( zI=trxMnl}$P{$73VGbRlMFPzkJ&!j?B6+yy90dU_k|fw5&J#{e(clTz&J;X@_hi)` z2%p%eG7v(GwCcv8-NUj41R{xLV3G0SB$Zri>3Zl)A64j7ixs#h*ue?JMG)%@ct%E$ zzgc}dvV>AEy&qdzWE*wI75_KOt0RrzfMe!VCG2W^woQ*?(2g_7ol$}dpSLagBENE{ zRHTtgy5jy+rw4O3v*Jbz&?&xwy?8EVxd|Q-81I@a@hn_IhbH@aTS{ti#?xqeV?nK* zAA=&|yZuS6D&#KMa>PyCxNDSUABy)T#o^v*IV)Wv)TwagPu~3K-K&8|L z5%`{F{&-;al{Osvo!6s%1QzlKCRoDhe;TAaW!gDDTR3sUae>Ac{vaU%a9TTVa`hT? zvMqnS?U)fL;3P$#Db~&jHn+3s{yMo4duuvVES;^I^MSG2dMcl2LxpO<%9KJWR6ncC zSxd7Z&g85(-YQ+gOaCy%!Q5>dp}xRjY0WB{#I}Ew*pLcjmyWP~S9?3MawSZl*gC4; zv9+VSojCD>ugLWjJUNNadc>QyZo^FSVT1UiOi{q{3Dh9x3EX6APw zkdr}oqkt3s(m@2IEUb6W+3PCQHKTZ4iUO6Q>6xud-C*pT1-g<@&klcC!J;}8!wL&l zOaY5W=;Y;oOZj&jVkQO3k&yPbJ8)#jkv(axZ5kaLHrH13gOE^aep;1ZhAIYgL1A3 zqD)pd6o1N`dk%(4>-$fG4Q^4L6xIBgvKzez@RVKWi4f0J1YT5|&wr6kK107goDyf? zYRPcH|0sR;96)k(%6fj?oR>55TBUPqibkan*rAI{wp3iFgX8U-=(zIWHIH~^)Ntmg zGr%!puefljXG)BF|FkX>fAG`!@Z{!<3Up_N=u=&XP1kIZ_!9|7NzlJsnlAY_nmkAF zZ4&YoKWb8E&D0DhC}e7V_WVo?T7ba~OS`>SYoe|o@N7CqH)!Fmz#1yW(Y1-T$Gt#; zTTDFLL~#Pb3Eos5DvjuLr};1iVv}px`QD?>=x2OE(siw>Hea}f@4P-*xYzo=_Suo# zaM02Y<_(LXYa(^DPW-TTakqfIKdnuchiuPSrvQ&xGTW zfrgg6S^-lPbE)RkQiqyF_Bp|WPwJOgPrpG1osw(fJ7Z*-B3ZsH7JxIS7?kvz5vyk zm8huw-qN2wm(hlg6IR4@&EEYS)@S>0q85W7kgIv_1KQ2`$qrYJ<;1w{3~|jf(!U-{ z;$6EL6GI;L{057U#Hp)Zx;{1(e;RpVy4|Pt%NV*mR{RD&+bLh8DZcNW&%xCG?CY(8 zIZ~tOxGA(`WWo$Wu?I?U)N(k zoIA{^9H}s6zoI{u=5COANOo?`!JO^Ju(s^8`|lJ$6q`YEVVW_|Wy{W3cs^Q*OaWLf z@;6R6mj%*#1pdH}Ks2xw%;RHblz+@{q7d@ljm5n9 z!yAP{06&C6BEvqj{uzK)TFBR;C7B7~e{41Ig@7>#v13b?`~z=v5afU^6kTqAYz2^m ztuV~YnEUw0jQ>xDOelROWG%+>#oMRc*75tBlR4UlYmx|&0F(g5D>WSSKoh8-AOwX< z34Lny#)KGZW#R9b444yqvrfP44itq#qX3X1aaznV@H$Y4lbKE@LK}R8@F=AqnWyce zb~WnQHdm;&lgv{nBlI))cqu>t9u)vRG?^ICVxdfP+G5tME}lVMSn4e*3?J61+Asdy z?gIf$#`i$o-_b(L{r=@gM|eHHGOgrYc~*NyR3XeiQzTRkO(qPqn6DRcB^yPD?|8k{ zFNgVgq3Giq?{DN@BxV#C!IFLGU6;o82iK_@Z`a|Le|7|9+4z;isB$kS{xeyv7~Z8g zfv1E939nW1gt_^(w(^VyMWNovtwIB_3zTRW-^b*5UQ8d|SEM9F2+10F-yqTcL(Atd z;184s6Kucu$5NwIoJeFxfjNIGpQxL^{_V$&3hCb-53dWQPY);q!uabOxwZYeFS2+D zfsR|cjzMEy8_kZSU%@V9hXJqY4Ug)h{x4nk?xoTb7`Qc?2*$m8>cjrUr5U|Tu>xmz z0I=b&RH?;J2qNf%>7j1v2pH4vLVB}yf8Z^#`g9N^5>dzVyCU}Bq< zn4jM8!>^zLy1@Gu33Up5WjeH#02>%c47JT_F0e5+!BXw}>jLQkzbN_-gLun+l;_b1+f9Pn?t#aL}Ew!X*G z8r`)FWC?@WVg4sc*LPZODTInu`IxlWPqKE-*0&$dw=eN*JbJ>;PeoAGVE;v3zkUfD z{7aY5$CG#GX0FW+M;bOV57#!fEdo&y5fSFg64GGK7a0@7Uv7GHEMeX*Uv%UB3JzM3 zPH(F}F1A0G_JHZ--$l(^f~u8t8-iagUDTSHF}m^V)K|Qld8fLqYTi#L%auE#$!x@i z(GZ<$vDzimCCnvZ!P4+%?7tmY3H=A$ScxWx@U~XVZOo6up(l*!d_z`nfcK=4*4Fb; z(&JXY*sVlor_$2+;_nXHn5Y+VC!tG4AwJKclMbA-{;XV{0);vJnK%WeZFxrk>Oi)1 zGIU3THyt~>a<$nKO>ev=7@2_%=c{N{)24Ybp9PAC5uVuu9b+uMDrvlwRpFc~Sw_r# z8P6Kvn^~bZeYifVr~iTx`tRHcOL`HDhT8>W)6-B9dN!IZ)=F02BNWvvCsC7&H>Hmg z^t<0Ik%JeRXvUU%jrhmTjqrXj95OpOznWR;H7${ixVylatrepJ(cx%~%0D|Qs}dMP z5%K5%;ZTXAcG8@sRvhP(CyuIcVNV>cUN*baIKdS1{UF6Pyfy1us3gnL5T|`pq#U?f zHd);Lw71-p%m&7B$uokbpN}-~1l)YjPS4h7KM*IGewy+pgRmnu1D!Q%>A6Z$zNeEfY?2c8GX(r4lztJxE3}}pF)Fnj~umXjOLwv)$K6X7p6b(03llxX@IS`|V$6;5R zoY`db3go8oIq#?S%<6kzr>=vaa3}G!3@7W92TXh*_xt5T@?xu{9FPYk*gv9XbO~r< zX_U)nj#Pp*hIwCv>EATGwLa_ZK%K8Vgvtw0G%s)A5^Z-}c>y}2)QS&tfr%XPMgCAQ z#Jqs@IOdd0WW!1Q1l7@44@gcCYWY{m`D1CfXQsQEhW)EjjkCx7opD{Bqac{o|0>t( z9{US6W8=kl>st$wQnPr{qyjG=NSu0~^_XUUK7%~2>pVgUnYDoap>=7)_6O4i&LuSm z*GYDaJq+4B2WE9xL6`wLyDX@`f>>10d$2fu*V49F1D^TxM)0L~>1Hvf3t#j-xQati zgKbm>1gP!tIj=+C-Db==6q7ZYiV-?+-4G@+yXQZ$-SFZG9xLySu4!p6U*cKz?N1fF zibJ-BM`JcRCtx|*sXKpmt;jSQV6|IjY`#_4W>nj?T}n{x(XgJq>Zkp*9}{$bG^6{4 zbncDzs=(bOs{q+3Cd3J)KPz)#m}&FF_J=xCp(J<_f}D@;6Q8EU;ThnG6V6ihqqQF4 zM)XGrisP9xbmJb5n-+8s){v|Nl{dj%0pAdhEh7?n#F7+Vq>wdDa+I~K4t7hKR61Ef zcDYpq-*1(xT}K{bad3~Gt*%{ddX8%5ZkY`yu+t~=l@%#NNOGEzDbaL< zI@e$rv&z~;{52?71(cj|a-P=Y-s|F^ot>>2@~rGdf06FW!qRk30;D-`h#)9o8a zi4~jI3l?K0$DWMgg!%&_kL*h&(>gLvXfKZkjGP52F8$ORg$!z?Lig8oeN;mS;80oy zSJmerk9iXXaLd3OZgO^(xn$P-lSYb1QlWzIJQB=HfvB_3{gwT6DzD={sd`F^=d-hm z21GkRnJUfdo7Oz{kl}sDB1zVbbW=oTfL+-wyhCFWrIqqb<(lE;1sKkcJ{S~hw&=8; zuD1Eqp8&7FV~xxbRD6x4G4QQnka!A!kx|T%p2-2!GB$AyHal989G1*^3MJusygbd> zXw}1W&Lorj{>+OGx^TJNm(1k4l#2N$(*|$0azNN!V^z%hJ^+?-3}APuAs&-jYgW2^ z%{DfND&k$rDpoI(7NRxe$dU&84P%79~dsGsg0mOB%x`l&@c zX9NaP#s(c6+>k$*zyI3B!0;0$eT4vGxe1IdtHA$l05j+z<#j+u<18W=&?vL)))}*y zCm=JbtkL;qPy-C~BGE|!WSTV=kxLEHMmZXhhYT9b&P#?quqMPToG5GGV`=tt!CT&3 zX4i+fU8YW$MkQ|~k*#AwKF*OoD$r)yJX0sqAQIJr@1>AV7b(L{NlwEq>@v3}?H=Dz zw;=M4nl}Px7?8o% zG@-=AY*kTq0FK1{lg!cBBd&LEfFpXE;tVk6qGKv1HdZ2&i_O>RcWGn=E>Y^)lyF5N zul9)!Fc2;A{o~gXT@KH@&+k$gp%HPCI4!rM8A+Mjb{6^Dw7SbSx(9mW#p}k>I@F!% zHYNhXmcE6+=PTxf5gc(JerLT&*MrBZX_WINjVZ(J)n-225^!9$ZorhH%Oqy!T6(QV z7uALs4)hIhEWMXsJ?R@PJ_L7a@84|jN2ls9kbC}2`O#R>#^M<%VU0^yP8aY`k{T{_ z+U%YlE}f07kBbR~R;{_cGdup}1!=#3yAOg@#sruIUpR33O z98y#Hi2+~AI7#LqtPP;0bWU(&X50mg!-|F{`!L++!$=oXkm#5=0VNHvfjUIXFT)L$ zPCc(J22a@Bmdc^b-EG?ukj=3NS-aMgtV)q0aZ#%DiGJn$;7TeIqNc9zeTg0-4(kg; zfgeB}>*nO#(|$aN>a$MnIT&Zd4!H&3=%%Hzzq{)Y2$&GwA(pjJ zwa#42HsI!SxsJJsfsWcCljU;Bd3$gNOxhd6P=YLTYQte$p0u%azu|9wfY}Pbx7W#* zp5QIrMR7l+cto0~QExU4#@~;fG1N)D1`g_Vc$w>MWX~b&V%;1SV*$_MT!r{>%c;Sh zQ(G6y0i!LjR= zz;`D$65^X1@AY4tZTEob(^9W`>&CEnr-z@@IO6>T9ba2Sne%rPUsI4rP3{wp;CRQh z+U2CCxtM^_KASr#(%ty)IgtQr!CM8+l~9#ZSG*|lF48#>Uj)YM3|^I*8DFV!(OGzF zyDlL+=-H9x=d7N3pgSzQ#m_41<_#qJ zQ9Vulwn5k&!N-q+iO(ID(NL|b$yj#VSV6T=P2QsRS;N`<5jqu1)&SvCLdGt`B(-98 z<+9rXoj_Z(_Nfht-%X%P{vb!y9+&Tit|vV>=XK*!9P26I&f~l}NTyEvPrD)~_iHB> zB2GUgyfRzTv^b_xq>??yYwy}RmNQi8YOC-YN*jVgXH^Q#`S}r7PzB6Pe=>&J`qM9! zuS4-!;_iu+-s%S2Qo#u@%xEu&1k2aZ!AG_Eq{Rv}xpP*@NJzNyLTlg^onYy8v8|WE zLmRah?=4G{G(F;gJX(aDH#R0XuEml}GLlE69Sf$w9rH`_GbCf%p*b3lLE4N<;t0xN z{$-DaA{KB4?IL2sNvuCe(1l?J#sAKDv_f`(ea7xW|M0wnI8yD2aF=@l7q&;yvAc;! zgHeU$hCMlHW08I!^Ws0*%}+`|&h;H{I>{GWYnO}-tOJfcSB|f0oRc@h)sKE3;tnqF zCD|*6aDsvoUIo%iGeHL_5B}sBv|ng+uF#0F5emz5_!yV$wo^lV{C)8;4lCrK&G}&3 z{Jw4sV5oECg@3PG(pUm0WzSbdeJl4g&@N0~VTB;_=#8{xF<+^EeYix9)vp@y8f*MI z7Ab~vej~0}EhAZN)YCXUm384C%EgVw|2t~I$WJ87LYDy^Qb4 zoPR;UQz|eGcy{l^|3d>>O1iv_>)dbNVmANME}GQr~f2jz(xcx zHK9|ejfkPwR+w0*cQzb{~U}dp1GI0P=LsUmJ z(DXgjo7EsN^yzl}BCwFg@QLy_OUaP=)oI~FV$W9@m*cQpl8JPhTp?~GMSu=~=p+^B zU~a>XH5!6ABRpSj-v zc%Tw&1NDtIxC=G%8dq8WqfnMm`iubY5-|CS0;{u3InAbZ*>20Jwaa~D`%68%XowKBgIn8VzKXi%+~Hy>Szo^hq%2HL zTTn44g(k)v^y*lEG06j=0WHDnj^1P0=``?Z90;N@LTuW;jD$$M8}I)(3?w={gLkP; z7vW-d`F}mze>gMaFN`Z^K>UY21(N>4xRY~cG=JRE3Cx^#{m?IdImhoq2nh|0aeoef zR{VpNS`on*moX;cpDg3I?;8YyF)orlN3zsk7#ExmyZr3r`x6NOkzWWbw_;A|j~S=J z;My9IVw)7Ks{4I-^!+?!FmE?4J;71T*JL z;UMR~0@D8%`Ia-T$opf)svkI6#gQ5m`C}`yD42Zv^CV0EB?c5y_!j{TA3;&$D^3|zWjXNs|I(=z}K z#wR*>FZTr3GC67EO1~;F{_v|-aP2*?O|J=M!1dCI0LGE|8iiT!mQH?iWtHEoTV~LeX*S0K zc@o;I-Qf06VF}XFrw5eO73UE0IQ8c6zYoTawb|+i=ZeakV5*e4g-4%hwbm}+eD3m% zMC3!rSJW=YtDwYeWgDd<=B&KoU!S*DHI=tPVX$0d~{rm|(_wA^gdb zcxqJgMhq^u+tTNs#>H~z`P)qVUX z^RUbr58e{$l<+eJiiq+BHvLHT`x?s`mF_U-xioQ?)rJ#~-fDyl9cSgQ)TEc^y*OK) z7-1tEmX#F5O6_w`yy&^^xS#eN)GQbOWwZ~p1>uMh(_1jwX--=+&4mvNA8#>kRe z@2_6&*nU`^=-Bji;oNtgy|IJ_xB4Vn1UUP^(mkWTl^6JJM0_8K&bzCbt&xg}Vsn2^ z?2aoP7&4DV`(wMHys*YhK1W&=LT&Ha+_63z>6#Z4{riE&mCaYiGMkJok2Jwkqjz8- zq~1ZVkP{l$kL_lQhdnCoJQ^ZiDZ(x{CwqV?20JvF3{b|^*8dXRH16y%Zh?4sIN5o< zozliMvzv}MQj>c9@CdX2!6Zu{91Jqrp^0BPu?XB<+7juW%+{BiKRDdoZpdcY#P*MG zmCJmSyqqu|oFtCU%;Xur`p%$`A!eT{IO^Hg*Y~Ub+j=0`mf9Q7%9PybwmIfLg0}4_ zFW2>a(0;x*jil44R6`-;dwX>-6}^E!8DB9X!1|%2d)(-yA1s6*foe%C&sX_A1S+-|6?#pnM3yYp zrO#{af}iGE2J9afNhY3KlRrxE+PyVI(FW^S&iO%@%iAQ|_scztmRI{zEVk1P+e#$N zhlIJyloR02^k%YUi{jH=1}3A9mW*IgqL!71cy!8M;I-Su(am`cx{2q@MR*LIy26Mp z@7-o}CK!Y!p2N|mI}~ZyIL8lH*KQBVT;UDB81Jzp``15ZEt_@^o2l0bW#$HuG?Mgs%?|JwvV5T`LMtTl>8A6f#mMR8{TDOUH7LVNd-?%{5kH_Us`Ig zFtamz?Se43IHBFU#oLos_UU|!7OW0d&Px007(9_=%v*zV0WWxW$$E8T98{=XBjZkq z`l0JPR;EjpVP>KW$(jyW%~^iavqJRvSbOI^$_A?s0a>Hp zA%XAoZY0x?o{W`-R%r9Wpq!7L~A20x8ufRy*(t2rZDT^&E~k{3s`P0>L(=H zpzXssnVM0nzahq{4ooV2yOWm#x(MQ=!lF@%csC+IQLKt(0`R_Beu==pavJ$GrN7w| zzL*2T`?Rri!wa4IuuOP+ORfSG=BqbRL)u{kv^*WV?g_3gdx4}EPrV$m_DMIn%+G?aE7aY5UC;0Mip1lxY;{XR2mEh=u9bp#CQj!i z7iRFEP9hS8t}Vc#TSPk84&m<4jqDT9A`W(4q!v-zp;8zr+u5gldcko*9P&eb=Qshq zaMrFbKH;}o-xz5)T=%4%ZBjX+CX&8GM1pThh^-l_CdClAC+IfE((PXj7+_ zF&6k*Vx`V|S4lXqjx38y)Z>w8cdj5jyjS#bSGDIZP^7ahVg)O}q^#}EGYtLlR{y)} zCnH%wUNy01zIiGJsQ>dPlB(Bb=X3*Yhb9pX`2q zn~>v@drLauQ_CaHThz#gtFWP5s8cPS9RE@#r3G1_`EpRo|;-09Pg1 zp`pIuoFO*SaoK&v<4mZsnaeGc@4DBA(zIF}$w*%wLdDgyz@O*lL@>hKgE0tz6OQWE zPjvtmu#9Qmw?^-kIV_jSSeMgGayVN}ufRy7?~aIf*3^e+w#Bn0L}A%(X0g8htPFj*!nowP z<B0dcNflf zSH7Cz^#E@!Fbm(;Sj%LFaGsB9R$^D(hjPqPDot#wV`xDf_~oC6G(eGfNKGIQ7h6~4 z?EB4W6>!6VaIslCj_*3*k<&JNvSlaj<9oBGV_tQhFA;~bAcabYd%L`;4DoEY5O7M~ zOXMAh7HIu-SpVi&bs!@gd6NE7VhGvp<#w}pZrR3=jQ3iZEj_0hohg-}NwoU^wfB`# zRXyRmfS@2qN+TSQ4(U!wkrI&ZMj8&?Atln?f}$YZod-nelEe-D=pj8v_&oFD#v2hL-2OSWzMV__uY@w znF9u@q~5fr%Mg+VEJ!AP&&qa@>x`?iFsIKZu?}KGMv|Ec7n+opY&z%=*e2wFX;Kqr zuEDWE;Z>#TkR%4ot+C>@Skpse7f-Kg0>RS~ugfZjL8`=w0vKInUTLM}*pDgpIo2;0 z5tF5CUKT;GTJW9o%O@v}=!R#}>O`(gW{W331{&Bi*6g1KWv*~9TQT4~^%D`x{Jb%q zk|P=Z-mL>5*xSY&*{{!=Aqj`*6FhSg9vjEU-gA?E+Pa8878Xo~Vx&u$*I{IK%>7C3 z63TWQ_KbrJ=5;nmbFzo{t`(2n90N_+iYUMKef12hNam8T>^Vy&MuV-6rL%>y;3<}U z5}SYcqKEddT%q>dim5U*Tx%Vl0-sTR{V8U@&{mJ9Em*l;jagb{ws!~E@bTk6i*uW) zmyxcN`dTV{kw1C_T}xHYbN@l*AINY^dbq-Qq1~w4TRr&kzI|6fW+kdHth&nw1wUWd zK@wz$Za@n--8z_pR5G8f>-{GaRZ=(G_H*{pFVT%*FA~OY>J)Z)2!&+tNaEh`8b;A;H}zdfw4KdY+0(2~4re&Fn(#k|e4E zTa021QU$~l%f z9o1xL+G*A=JHDV&q|MVk^6N=w4|Q}od%U)aiU?sVdX8-9t>;|j+*=2PbC$jlLOIJB zXqewHj64(_yUG+=Xnsi)#+ltp#1GcLR?9NV`BJL#5>#_vfm>ID@GXVvXFLW~j(o=F zp`8QkOxG8*%zG-TxrjOQwDgF|&##ac+X9=y-m{Fc@N3o5?_AqBzhZH_CKFtT@v@#D zg0)K>a{5==i}Fsbo^KlT&sxKRhORnnZWVX8 z@$W`Gfv%oUmz{)jzkG6>4Mcnp6GaRQEj9-orRGItp`ar5#41i%J6oKFp*r1(j$N&j z)uZYFH9Jz7p>+i9FlOViEAH8uMt*B^BFJcM<;(<{aDrYP%%!e63k+VRC7xM~cUv)L#i3!Ev!9IP!z7Q4(C2xq;`+jLcNH0AUZBPo`uf zQI^_^dWJeVzR&T~;JJWFG4DA+m*n-2&eL2jMOXO(p&nP>AF>hD(_$K2ODT`HXs9B3 zlR7;|=sSs{MdC1XRSL_Np1%)c#Gtr<>yPCWbKE7V+P>)YL07ebMVYEOtG>~yzIYfY z=y7^sTo}^?%8WS3fP;Hxewzy*J7~_Rai4({r@6dbL~Ewz9F_kvuJ)i6`4U6IS{VT! zp`D;lhaJ^^y2c!REH{JI=h05PXXVB5mf3_c&&O*YS>xmhBZLN(B#ab%{>7`sgT3y8 zE{50NT|fv7hK(rp|_x;M@uNo@HV|)3l@YS-GfF^Kl!htAt3BjU`S-r#6%_55>vsrn26Y zjf%oKA7)pT#xuhmImYbOh+A#Np;XH$v&T&rAHCXAq76o+I#T|AS$VdES0mMU1pz8Q zOF!EH^YV6AmkiPB!a~@PE4sI87^il=Cg%=Dl7@u&ke$#6nDr{hFjkn{RbzBATObPp zu}%HSHHPqdAU}1V54r5lB&VE``V*I(lXNSxf-Zw{yD`lw0kLMi86Ix!f$NRtGRL(d zv}fb%<6-eksa)J+%f~)1BSJA8-$u19q7$qx)@3G(oCiEPU4M$9?iW%P97&K9q*$-c zOu>~0Gq4Ftsy|*8a$mX@IkvkTx`N|4XTirr6@2e?FseqKv|)Z@n)EiEpH09EIw~4f zz(|)Dp)f>+uaorV`?Rs04{{O@xZQ~Ppe+uO(UIsZZZceiiJtwWe<%kY$*|#KYCEMF ze^d@iq|0mi9yiz|g(LdHWsd1VT%z|vVf7}3JduDpvyuj%amco^G9p(4tm(0`@Q{U$ zOZu1=3&125!9(;kV={}To6cr#Jjy1?f1PI-q=uSYs)L56z=yZIIb3-);LzM`T- z-v-eoSpYv^cfrWUMFqV#Kqc9Rt~cu=Pgx6GoW%|`N}WJ)hv6HI!o{+$0fMN z>pGj$7K+ z!k1#L&U8s)r^noL?q9<+>}}Z;UJ-|gVLoSBG4A{Xd&PEMGUN#ei&i<7J5x9<#=m@> zH{GX@&pb$IWL703hOC{1(qv-62I02lF1prB9o)Oik3E%^g@4+XwkMSqPmIF!w}rC4 z#o0#eYu24?m}R17Xd)h5Ed-O*OA8qYJB_Y7)aE#9p! z)yg+V(F`VfqDAlKbpn6=-k2a5$$g<5D zO6G0)9iNfkvEjSfcfS0Wm#8+3B)mCp-6X{w3GA-)afEE0qD}0Ym4nB*ud*5@J)FXi ze=g3NstC5JTLsf(&j-324R*0w;Bt5$Js+qu^`5S?N4|v7b~Z>@Z5Yo!pc;7wSL+pn zv-&uL)x$YLK`B%XYH4}8ohKNqn_0K0=aT^-)MwZsFEA)l?BXX2xcDL8x^GO|$#3)n zCwuTob~j)W9sr+boSboFX*pIQ=BE0~qvRy_1-`2+*ls=9c4DkF!}?OX1+u{HtmeGx z$Rf?It1VW@@E6(uD;LYEh|Gz2E+O@6uoQH1s-beBI6v~7fATG{3U=Ar>rw%6?PI>^ z;vB(XF3!Y}b*tIxo0_3;95?Y`K`qANLcyTylFMuX<5@tiXL`F7ZLLfnF=&*1LL)q? zP{56f=%=c}cIe1bpmy@#`Fz!THP9Ww6m(54d;Mt4)aA*c5ssIY?GlkV{jA4LrXqJF zv81V9*QONN?1Ot{Ze+%tU2$aVNyM#*sSSR+9X!DH5$%?+yR zB0lWVa49eUj3%D3z7y0eXX^oXcz+n5 zpDpPa*0yP?Y;H83PEs5y(joSUfWwQK*RA2olYf#)({N_ovxqomJK3P2TCDk$&O_-_ zc1mLSg{mThXF0Z8&8l!^C=3t+BWk?{B1h`g7-4IorJM?QDrOugQ7wv;C3e!28M)G< zUe&ds?tZS138AXDQR?|nUwpsyJdh~`fI0bjVt;=LG8ME*=qEmOxPzf?|M*S`AW_NJ z-!y+~O@3jf-vK}=2#pDq`nEs|;^GyaUf3yWo^PQ3Vc?tOUx7UO4F{inq6OS)x- zcXI2+A#zIM{1N`Ogej0JvUH#|8hh zRYdky5E5!*`t+X}E&KqIxL1($AMpyHhd?rvb!5MlKHT-t?5!Z=|LijCr={_i7$kA& z^~#iP{kjv?FkccrM;`3dR=&SiOPt)Vy&aKG^HWJ2qpoaM96hd{=1jV9CFW)5S z{P2k5wvTjne8T6cPOnC*6{T|Ixc5tz(I$w>pjTD#m|0Uin6)Vqy@jCyroW(r*bn`il}jnVZ{sRb$APi6s&<@Z`#GCp#`vstC-oI~sFC3>2?V0+J zZ(-RiX>&>i#CJ#elEd{H9-&sCaXjiVCy8aAjxb=r%qr-$FzMk%)TXujPm&gKB6xEG zb3$_>0uFf|4=ji($S_lkK$XF0I%DzG(!9^#QGxXmPV;`s{mJ{L74ke>G*(CyT&+O{ zAAhvBm*~mhM0j=c{cZhQLU?l`bK;~yJ_A(=o%CCY2tTr_`19p7v2g!?og~h4S8QSY z6HsXNz%6jpRq&3d5BMV4fxd%Wl3Q<$>f2O7MCu0zgv5raJpV}<15!c}_pd_n@cX{7 z^1|z~{1+znYaII_|NesjnQ`eKKJAMrDh13jM=K4syS@Ub(v-j)4%y~XP5B3H|4*_1 z>5%{12)QNv7#jN99A}9Ii;cM_{3msm6p(HT7`P-WQyz6X;}6@jB_3|w-=6YmQ5H)I z{Wr=e{QivZbYR{k0Ba`n zOYa`BqT>Bc93_H4P6a1-&-xy_Ce0`A9-{?d%7;CEMhPsbBfXOVJBT#3^FP_UK%U41 za`(dT72*`(RUu%te0|OMrS|!oJz#|{LepTrTcLq~bTnMW`vX#<@Yp2@q&RYb92Dmi z{xzN>rB|z3nG$ax^=$aITT@Zr)%0m+>R`Tb760ZRM zp2J2A)u*?b8}F$WABQd6a2Z|yIUMASEblCPRwP6+pud!galW!7XAzP1hRsNK|(Q@zYfYs5;1V_4Uz%Go|&z%0z0D<$ju>UPNoHhcmwDZ=;ncQ-~{=Rho zho19`_IrEH{)f@@|3L!t6K76tW9m=owOq~@ZjPQy>`%lcR_!`j-idLinN^- zvDBINVzUWy5j>QJq}S!!cfGPW{H&)IZuJix4L1YKd%LB-frMIh$zh% z4SF1mboqH<1`+@#P9cQD;wibWk@aV%BEoV@;b~73Lt}vKOPB}61q|9HJVzK5X~7LW zE0|{&yOUC0VIy1mg&@(Xr_0(W&DpFT56Z$(U@0^2YqXmE%?x>dOBBV*z%18H&VXDw zvlmA?13Fg!ezeAr!PzORUn=sq8MjK-Z7?!BxIi@0-v#h_sC?aO_bJ+X%xLa}x4>SM z-r5C~py0Odfg= zill-xoA~GcnEei}S#h`?dSs*!dv@01?luu*Qs#D=kh^(iXD$n^>s_8GQrP&)r~#{< z7FgiwAEl3b^&(@BOPz>BdF~SgMkO%SK$3Y5>m+u*;&TOmPlGnpU{GWcu!NG3OiOaAYIeto+ zrbcw1SN4kfZl`{$z&{!v#Eh<_ABjsW_K@C9c5ZIbl3?ILaH1cr;)ZSa z^B(RN@BO&gIl|VYz0>@P2myD7i&p+LcXdh6ld^Co)tV#@fmI$}Xw6R`!5q!Lq@c^_ zu){1Ee^LOAL$^>e9egoe9)zc?@8-!-FuGwPbXw64-Ccya77 zYAm`pR`H_UFoe32IJhTFy~>qP#T!SX=}m3HY#p_-&O^3r=MGIJxze+hIX4H|<5xvr zl|HKzzR}(?)~Gd6X_{2|YEI`d7tYT;Zl`;1z58A;`uA+7FsLSC@!jmm1qh_;rTk;o zdc{cXp`7vqY8Aqzg}5OuIsK-g! zim90D?y<1ugy_sKmA$$j5cV)!JFAwyl{T8FSCcD!RZl6=Ds>B-=BbKI6;!TK%6q37 zg-`cd{ENX07_@oyyAJ;JMr^fiygS?4je_HJFse{Y7FpALY{uvhlU{zAMAP(7FO%st zkDih>bgK8Tod>@;+#t)GkO|fmd;tTAwCD1ETq(>no8*}GIePT;_`E6La=DE>agqa3 zx7Pd9NU^3ur{Lkq(&4qR@YyHmFV#CR7u%vm=1uAs)Q4GemAh^pYlS&qtTZ;*CuY_)^4|zaqZ#ACr*v{(XukG zh5cvUa|WD|&MKNT>wcK_W#S{TY4n@9atEuGIw1QLmRJMs%LII6CS+qDa0)e~4x@5$ zKv?es7>~4yCF=e|+^EjnYup_uw{);XJcjmBEvBS3tc%vjCxGc|p ziF?c2L1xM3=LQ?v#NRAj#`QLiRf5k$qWyYs&Zmdq#JWKzJae=&KL73~p7d~x8w*yIC{%PH6@Sc1!@7@!_^9kB> zquxv|;f60`6@B|WlW&jU-U5#~7`!q$7f9yZ({Y2HVqBMxLl-69b)U@^Ka5}rfe0luYJ+@tV)-~H2~!3Z>_v4A8W4{KGq?BZtyit=}X?pEO~ar;ZL_x zULP;=aXn0D$ws`x)~iUz8l#VB3iuQy^u)8RE2*mMN0hih21&T0==T+fCov%VbSF3* zq%V;>E}u0?^$F%R%I^&ol*?Y(l%Ze3s^5%J*V^=im%-+}VU(HRY0HK~lC}WkOSRrs zAS3b`X22fo#*ae<%Fe8E4L?ZEeyy{D^2b%52w)?`(@LJ(A5=JdSO#Jf#5 zvKqn>=;9dd?eN;Emz5sb%yrX|^_&*76063S-t=clU+`rE);;Ci%Cs-7SN0s%?IuMr zUGZnKEVwg?Z!8YD-Fa~AJ5~7msMD3cgrYO6qN2pA{?i z##+1fBrX;3a73#MT5SfO2l@q#MW32*O0m+|hh&|Wa!35s<<`E}ue&#Paqg;!m8qAn z*i^@~*!SafyaYvnXUPEPH3A=`o6l!dlz6V}hLF$mqO^DCs$)*Acg&u1I5^IJ5nYNn ztBC#J?|26TK|FXqy!H3Shb-@6fUk7%_n86VG^03z7~ zRlTy14{+~s=q4~D%^oAZDWk9sApmd4^H(M7Kci?XiNy1y5Wc<)Lsw%~(?MOpvrr!y z-##aV-jl(jGss5Dueq1JC2hU#$~e<7&PQAAtUN^w_K=P^kIbKJS(SFiBboSqG?Xb)B=SQ39!$R(Q+$b{cPH z_ahtIahti=1oiO}^rJCX6^OLTt;5k(=~bjl{M^s6-!M!xs~_b==vA6j3T32>yw)7G zee*2V7IxDYM0lslcmaf;lf;+}NI^kQ!e_vPI@IQo1fm?yh8JCG;0U9Dug5dPQNiRR zJ<~0c7RQI7qY4A3+9eklUg93_6So^;`H_RN4ffyGR`KcVgNigR+VHub7+5(Op)r&| zja>P9QwlAuvFz~QdY{v(=*b72cs!Vf-)1|5%jV9B0tS$`^2l2z24)a#qIN$hVM{`&1ys6G$P2N?@ zPwM?PQGdFd(11pjT2_)x;Jig#t#$AYN$lxpd2zs-U5d1|`bS-_kCfHWD(qcj!Y#|w zcicb)23pS6gHS@O8ZLRKS-4eF9R`np&_DD;Ybl zSKn7@7m2E-sJo!42;#3zrnExiX#p-CrpzwI!=4>5cf_;2s&KzVtn7Q>qn=qmQd`_O zz`hjAk*7D);nl*|P;lm@)lB2)yXUcRtzhWesqID7ZFtvYcY!C9HmaKx%&P(yVRXup zx)G?V%yH6l3Z;ovf?45?JiOK&CE-0raFKPy2OHfd7HD*O9;=+1J!(u^DxG2?=$>nD z>@#VNo~a*Z?g_}ib}X-o&yO(l`aYA~1!+NPDDB0%&58gu%UEwgd*Y)ax0-nEDP%ER zk3?Ys*gMt!81gyPne1No5nR0#Jm^#A+QTxFD6SXbwKfw??Y*x=WrS4XGr^_o>#GrE z7_<7hKa+h8vv3!W6Izxe91>o!m*Ji{TQQk#iGo$h>6YJfWs-n)ZkI&&leF9-viAc* zurm8(!e-m6H^n~CpvbY44AckXQV;6FG|CJY@yt~5Q#~N;@7AO~D$Hy}WAt~Ca(Lu= zHZBv#&a9o~u%kt~W!ylygfGDny^&=zoktUh_wT|^J;*gc`g<0e!J(Cnr#AL6UzZm`yTJB1q za*cL?)aG-Ni?Bw!JUyFDdpom9|2*4_;<$ExLh55KHr8~nN zkOBGI1i&&y$h*zAbmw^kA7lU+EL;C?pa%E=2jEZVLjNg(y??7(3kbN|v;2y2Jr422 z_~Qrt!Y$5jm5u~t_*E)U`0qH_$lM55+i$fZON4G6N=!GzHr2p*1`YXP0f+D13Ej~T zGB~OI^l?}+aiJ3^&gYE3GnN5u1{996Uo~l*-Tj!T{6Ls5PeQw6#2+jN0k&-Qz7=)8jfwUr*1H@J_~-2@@fKaRa+6!mgJDTyyDLtmqAft!Ai32n_%6_QPJZiy zhu0N(VYI%Jb;at%*Y_sswv+}Dq0E=V?~w~Job}v2E(6qjD_Uy9Vfz5~nUVB_SE5|4 zu6OgQz+zd%+-WlloQ0Tg8C3mJ_dN^}QAZUA)t*(L=8U-saz1(2?(Y`6U$h_v2X(;6 zA^DjhHiDjs<*91^$1%r5LkMl9r5rKQ9bkN@u?l5l0rs?8MiP{Umw#C;n5g_BM51 zeDpFp{0GvnNU0b)!x{on`EP{Ll>##{Eyh7SV*U-4eGDKMVfbl1gb&D%$CTlbqTlqw zrs`8?x!&TJf%RT{PRZFVpF=Oqg}KQxs1I_bCwb~fsDzNEHW9~``!*cqu^ zJ?6B8qIjTj#mKYffXJboYx`nQtmHUNOt33RzlOx4&mRr}xuCq|6oYhfU)SRpY0PFT zHdqL!>S}aC1ubOjD!|YR8RQET0{OO-A(|q!Tl&OymHF~u-2U?GXAw*g=uln+4jvKn z*IO zHN_;p5z;^Vdrlo+Uv+!~;=6O2*TBJ-F4)2aMgB4gcpE`N4spV&!un&EUl6J>{Gbmn zJL*5X#gMuflaR#MG^cx~2e=U-lE0haM~yndu1^KxA`B{h`n!oheTaU}f~*Juch!Ri zwBb#}Ofge0p=Qf>R<*qLV*?$YL-Itn#qYXp81Ih z1F_oZ3eR7YS~QW32?vVV0&4JtU*Gq;I~k*V3j&1;8#8)b!d*xCso=gyE@2B8x2V?X zhJ7)K>E$Te1Z{BttIE+Y?l|r`4j!6Qw-Q+~MGn%{)?Qvwm%1|yQ~fDWLg}PC7>_hA zy%*2MiEGEz$2B9-=*eF@^}`XBe#%Vr2d%OjtURCCNP+h5F!6g2Z53m|i#`8a(|4rX zQp_gL<7*v5-CWG3Hk~6CX3X17R!L%<{)`F{{U?ZV2XV(0pSBYt^97UNThp@&B9xZb zE}R&?@bnz%?JkSBYcHteJ_6km9hB~J0-KqcfmqcPle02c_(ApQwv8!Ow{Sg;WlIa@ zU01*(Qhu=d^8QPGA``UZDsk&=>gz*6Zyo8wzSgFl>z&i5qK3V}a(~UhMS6@Lzf(cG zgNezGPl?1k)(Aq5uBpsXd^(XRobl3H?7wxs3ly}xTjkOp65GwwpQ>&^=>G^ zBNmnV3W0OGyEeeV{g?S2{WFu5q}9i0?XGUTUONt!ECjNTk+qm$l|8JW&C#FNTN@Jt zPn;CI8l#@#KDOQ2sY%}!rZye7O_yB-Cw@XFAdYSmrg#q^SxD`$42jpsXyk+b zMreU~5uaJRqX?x9*$U%I4M#=t4D)0QcrU5y(*#}8CkpKmfwrnAwalFE;#zy1wbplB zP^cx?M@XC`R$+>WsM$mJ*)l^mUk@uy$XgVdpE^8W;nz|k43sDgw$>xZOXBp%T{>xW zKdvfNX;#MF3Bu)ER@Uc>b}vkZ7;5n!5q@(8%j6cTDw|bEO&Rz;jH8>Mm>pMS_`r6f zz?X3{VQJo`%BhYA421bf0P{W_M(7*dI-Wj|?)Ua-K0FKqLuX*i;M1K2jNM*tU6;Lv z>z~udv>_r(NM%0N!;En`n-YiCPx_6Nq8kgMZs?(}WVnHmmQ2NZc}lu@epp)i7=6p@ zC6lIzEDdTFBVzO80mq)7qw{f5xk1W=TS?3~n?dLbS0?5vz5UU)`CFPp=kJ_wQ%Xl7 zmz~UB8{GE&fU93|Gp{3=&xyprr*|HEb56v>2k^CO>uCM`g|qrCW`o5a7FiG^R;3>& ztPDx+j3Q_xxGCu>4Rj|OHm?-K<%?338U2(La{KZYmwHoFXHHi@VK71XxjkcHCmSAS z;k`+$dmj)I>6eR5l1Gx}mips`0+PBI_BJBSE8{owi~O^h_=@7kn>ew#c1j{XNAwe4 zDV>kQG2gEJhe(UeD(O5|dRNNpZ7_k(S_~Jm8Q946(fry#-gv|XudUwBY2D`e+#d9& z+d~A7(3Y1E)pwJJpcdZJek_gj^yevE%1t(1ij(*-l;EW*1FCmdpOjS~S { + for (let comment of comments.data) { + console.log(comment) + if ( + ${{ github.event.comment.id }} !== comment.id && + ( + ( + ( + comment.body.startsWith('Command') || + comment.body.startsWith('
Command') || + comment.body.startsWith('Sorry, only ') + ) && comment.user.type === 'Bot' + ) || + (comment.body.startsWith('/cmd') && comment.user.login === context.actor) + ) + ) { + github.rest.issues.deleteComment({ + comment_id: comment.id, + owner: context.repo.owner, + repo: context.repo.repo + }) + } + } + }) + help: + needs: [ clean, is-org-member ] + if: ${{ startsWith(github.event.comment.body, '/cmd') && contains(github.event.comment.body, '--help') && needs.is-org-member.outputs.member == 'true' }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get command + uses: actions-ecosystem/action-regex-match@v2 + id: get-pr-comment + with: + text: ${{ github.event.comment.body }} + regex: '^(\/cmd )([-\/\s\w.=:]+)$' # see explanation in docs/contributor/commands-readme.md#examples + + - name: Save output of help + id: help + env: + CMD: ${{ steps.get-pr-comment.outputs.group2 }} # to avoid "" around the command + run: | + echo 'help<> $GITHUB_OUTPUT + python3 .github/scripts/cmd/cmd.py $CMD >> $GITHUB_OUTPUT + echo 'EOF' >> $GITHUB_OUTPUT + + - name: Comment PR (Help) + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `
Command help: + + \`\`\` + ${{ steps.help.outputs.help }} + \`\`\` + +
` + }) + + - name: Add confused reaction on failure + uses: actions/github-script@v7 + if: ${{ failure() }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.reactions.createForIssueComment({ + comment_id: ${{ github.event.comment.id }}, + owner: context.repo.owner, + repo: context.repo.repo, + content: 'confused' + }) + + - name: Add 👍 reaction on success + uses: actions/github-script@v7 + if: ${{ !failure() }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.reactions.createForIssueComment({ + comment_id: ${{ github.event.comment.id }}, + owner: context.repo.owner, + repo: context.repo.repo, + content: '+1' + }) + + set-image: + needs: [ clean, is-org-member ] + if: ${{ startsWith(github.event.comment.body, '/cmd') && !contains(github.event.comment.body, '--help') && needs.is-org-member.outputs.member == 'true' }} + runs-on: ubuntu-latest + outputs: + IMAGE: ${{ steps.set-image.outputs.IMAGE }} + RUNNER: ${{ steps.set-image.outputs.RUNNER }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - id: set-image + run: | + BODY=$(echo "${{ github.event.comment.body }}" | xargs) + IMAGE_OVERRIDE=$(echo $BODY | grep -oe 'docker.io/paritytech/ci-unified:.*\s' | xargs) + + cat .github/env >> $GITHUB_OUTPUT + + if [ -n "$IMAGE_OVERRIDE" ]; then + echo "IMAGE=$IMAGE_OVERRIDE" >> $GITHUB_OUTPUT + fi + + if [[ $BODY == "/cmd bench"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-benchmark" >> $GITHUB_OUTPUT + elif [[ $BODY == "/cmd update-ui"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + else + echo "RUNNER=ubuntu-latest" >> $GITHUB_OUTPUT + fi + + cmd: + needs: [ set-image ] + env: + JOB_NAME: 'cmd' + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Get command + uses: actions-ecosystem/action-regex-match@v2 + id: get-pr-comment + with: + text: ${{ github.event.comment.body }} + regex: '^(\/cmd )([-\/\s\w.=:]+)$' # see explanation in docs/contributor/commands-readme.md#examples + + - name: Build workflow link + if: ${{ !contains(github.event.comment.body, '--quiet') }} + id: build-link + run: | + # Get exactly the CMD job link, filtering out the other jobs + jobLink=$(curl -s \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs | jq '.jobs[] | select(.name | contains("${{ env.JOB_NAME }}")) | .html_url') + + runLink=$(curl -s \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }} | jq '.html_url') + + echo "job_url=${jobLink}" + echo "run_url=${runLink}" + echo "job_url=$jobLink" >> $GITHUB_OUTPUT + echo "run_url=$runLink" >> $GITHUB_OUTPUT + + + - name: Comment PR (Start) + if: ${{ !contains(github.event.comment.body, '--quiet') }} + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + let job_url = ${{ steps.build-link.outputs.job_url }} + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Command "${{ steps.get-pr-comment.outputs.group2 }}" has started 🚀 [See logs here](${job_url})` + }) + + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + + - name: Install dependencies for bench + if: startsWith(steps.get-pr-comment.outputs.group2, 'bench') + run: cargo install subweight frame-omni-bencher --locked + + - name: Run cmd + id: cmd + env: + CMD: ${{ steps.get-pr-comment.outputs.group2 }} # to avoid "" around the command + run: | + echo "Running command: '$CMD' on '${{ needs.set-image.outputs.RUNNER }}' runner, container: '${{ needs.set-image.outputs.IMAGE }}'" + echo "RUST_NIGHTLY_VERSION: $RUST_NIGHTLY_VERSION" + # Fixes "detected dubious ownership" error in the ci + git config --global --add safe.directory '*' + git remote -v + python3 .github/scripts/cmd/cmd.py $CMD + git status + git diff + + - name: Commit changes + run: | + if [ -n "$(git status --porcelain)" ]; then + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + + git pull origin ${{ github.head_ref }} + git add . + git restore --staged Cargo.lock # ignore changes in Cargo.lock + git commit -m "Update from ${{ github.actor }} running command '${{ steps.get-pr-comment.outputs.group2 }}'" || true + git push origin ${{ github.head_ref }} + else + echo "Nothing to commit"; + fi + + - name: Run Subweight + id: subweight + if: startsWith(steps.get-pr-comment.outputs.group2, 'bench') + shell: bash + run: | + git fetch + result=$(subweight compare commits \ + --path-pattern "./**/weights/**/*.rs,./**/weights.rs" \ + --method asymptotic \ + --format markdown \ + --no-color \ + --change added changed \ + --ignore-errors \ + refs/remotes/origin/master ${{ github.ref }}) + + # Save the multiline result to the output + { + echo "result<> $GITHUB_OUTPUT + + - name: Comment PR (End) + if: ${{ !failure() && !contains(github.event.comment.body, '--quiet') }} + uses: actions/github-script@v7 + env: + SUBWEIGHT: '${{ steps.subweight.outputs.result }}' + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + let runUrl = ${{ steps.build-link.outputs.run_url }} + let subweight = process.env.SUBWEIGHT; + + let subweightCollapsed = subweight + ? `
\n\nSubweight results:\n\n${subweight}\n\n
` + : ''; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Command "${{ steps.get-pr-comment.outputs.group2 }}" has finished ✅ [See logs here](${runUrl})${subweightCollapsed}` + }) + + - name: Comment PR (Failure) + if: ${{ failure() && !contains(github.event.comment.body, '--quiet') }} + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + let jobUrl = ${{ steps.build-link.outputs.job_url }} + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Command "${{ steps.get-pr-comment.outputs.group2 }}" has failed ❌! [See logs here](${jobUrl})` + }) + + - name: Add 😕 reaction on failure + uses: actions/github-script@v7 + if: ${{ failure() }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.reactions.createForIssueComment({ + comment_id: ${{ github.event.comment.id }}, + owner: context.repo.owner, + repo: context.repo.repo, + content: 'confused' + }) + + - name: Add 👍 reaction on success + uses: actions/github-script@v7 + if: ${{ !failure() }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.reactions.createForIssueComment({ + comment_id: ${{ github.event.comment.id }}, + owner: context.repo.owner, + repo: context.repo.repo, + content: '+1' + }) diff --git a/.github/workflows/command-bench-all.yml b/.github/workflows/command-bench-all.yml deleted file mode 100644 index 4128f86fb7c8..000000000000 --- a/.github/workflows/command-bench-all.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Command Bench All - -on: - workflow_dispatch: - inputs: - pr: - description: Number of the Pull Request - required: true - benchmark: - description: Pallet benchmark - type: choice - required: true - options: - - pallet - - substrate - - polkadot - - cumulus - pallet: - description: Pallet - required: false - type: string - default: pallet_name - target_dir: - description: Target directory - type: choice - options: - - substrate - - polkadot - - cumulus - runtime: - description: Runtime - type: choice - options: - - rococo - - westend - - asset-hub-kusama - - asset-hub-polkadot - - asset-hub-rococo - - asset-hub-westend - - bridge-hub-kusama - - bridge-hub-polkadot - - bridge-hub-rococo - - bridge-hub-westend - - collectives-polkadot - - collectives-westend - - coretime-rococo - - coretime-westend - - contracts-rococo - - glutton-kusama - - glutton-westend - - people-rococo - - people-westend - -jobs: - set-image: - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - cmd-bench-all: - needs: [set-image] - runs-on: arc-runners-polkadot-sdk-weights - container: - image: ${{ needs.set-image.outputs.IMAGE }} - permissions: - contents: write - pull-requests: write - steps: - - name: Download repo - uses: actions/checkout@v4 - - name: Install gh cli - id: gh - uses: ./.github/actions/set-up-gh - with: - pr-number: ${{ inputs.pr }} - GH_TOKEN: ${{ github.token }} - - name: Run bench all - run: | - "./scripts/bench-all.sh" "${{ inputs.benchmark }}" --runtime "${{ inputs.runtime }}" --pallet "${{ inputs.pallet }}" --target_dir "${{ inputs.target_dir }}" - - name: Report failure - if: ${{ failure() }} - run: gh pr comment ${{ inputs.pr }} --body "

Command failed ❌

Run by @${{ github.actor }} for ${{ github.workflow }} failed. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} - - run: git pull --rebase - - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: cmd-action - ${{ github.workflow }} - branch: ${{ steps.gh.outputs.branch }} - - name: Report succeed - run: gh pr comment ${{ inputs.pr }} --body "

Action completed 🎉🎉

Run by @${{ github.actor }} for ${{ github.workflow }} completed 🎉. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/command-bench-overhead.yml b/.github/workflows/command-bench-overhead.yml deleted file mode 100644 index fec8d37bb9ef..000000000000 --- a/.github/workflows/command-bench-overhead.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Command Bench Overhead - -on: - workflow_dispatch: - inputs: - pr: - description: Number of the Pull Request - required: true - benchmark: - description: Pallet benchmark - type: choice - required: true - options: - - default - - substrate - - cumulus - runtime: - description: Runtime - type: choice - options: - - rococo - - westend - - asset-hub-rococo - - asset-hub-westend - target_dir: - description: Target directory - type: choice - options: - - polkadot - - substrate - - cumulus - -jobs: - set-image: - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - cmd-bench-overhead: - needs: [set-image] - runs-on: arc-runners-polkadot-sdk-benchmark - container: - image: ${{ needs.set-image.outputs.IMAGE }} - permissions: - contents: write - pull-requests: write - steps: - - name: Download repo - uses: actions/checkout@v4 - - name: Install gh cli - id: gh - uses: ./.github/actions/set-up-gh - with: - pr-number: ${{ inputs.pr }} - GH_TOKEN: ${{ github.token }} - - name: Run bench overhead - run: | - "./scripts/bench.sh" "${{ inputs.benchmark }}" --subcommand "overhead" --runtime "${{ inputs.runtime }}" --target_dir "${{ inputs.target_dir }}" - - name: Report failure - if: ${{ failure() }} - run: gh pr comment ${{ inputs.pr }} --body "

Command failed ❌

Run by @${{ github.actor }} for ${{ github.workflow }} failed. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} - - run: git pull --rebase - - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: cmd-action - ${{ github.workflow }} - branch: ${{ steps.gh.outputs.branch }} - - name: Report succeed - run: gh pr comment ${{ inputs.pr }} --body "

Action completed 🎉🎉

Run by @${{ github.actor }} for ${{ github.workflow }} completed 🎉. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/command-bench.yml b/.github/workflows/command-bench.yml deleted file mode 100644 index ac879f443755..000000000000 --- a/.github/workflows/command-bench.yml +++ /dev/null @@ -1,124 +0,0 @@ -name: Command Bench - -on: - workflow_dispatch: - inputs: - pr: - description: Number of the Pull Request - required: true - benchmark: - description: Pallet benchmark - type: choice - required: true - options: - - substrate-pallet - - polkadot-pallet - - cumulus-assets - - cumulus-collectives - - cumulus-coretime - - cumulus-bridge-hubs - - cumulus-contracts - - cumulus-glutton - - cumulus-starters - - cumulus-people - - cumulus-testing - subcommand: - description: Subcommand - type: choice - required: true - options: - - pallet - - xcm - runtime: - description: Runtime - type: choice - options: - - dev - - rococo - - westend - - asset-hub-westend - - asset-hub-rococo - - collectives-westend - - coretime-rococo - - coretime-westend - - bridge-hub-rococo - - bridge-hub-westend - - contracts-rococo - - glutton-westend - - glutton-westend-dev-1300 - - seedling - - shell - - people-westend - - people-rococo - - penpal - - rococo-parachain - pallet: - description: Pallet - type: string - default: pallet_name - target_dir: - description: Target directory - type: choice - options: - - substrate - - polkadot - - cumulus - runtime_dir: - description: Runtime directory - type: choice - options: - - people - - collectives - - coretime - - bridge-hubs - - contracts - - glutton - - starters - - testing - -jobs: - set-image: - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - cmd-bench: - needs: [set-image] - runs-on: arc-runners-polkadot-sdk-benchmark - container: - image: ${{ needs.set-image.outputs.IMAGE }} - permissions: - contents: write - pull-requests: write - steps: - - name: Download repo - uses: actions/checkout@v4 - - name: Install gh cli - id: gh - uses: ./.github/actions/set-up-gh - with: - pr-number: ${{ inputs.pr }} - GH_TOKEN: ${{ github.token }} - - name: Run bench - run: | - "./scripts/bench.sh" "${{ inputs.benchmark }}" --runtime "${{ inputs.runtime }}" --pallet "${{ inputs.pallet }}" --target_dir "${{ inputs.target_dir }}" --subcommand "${{ inputs.subcommand }}" --runtime_dir "${{ inputs.runtime_dir }}" - - name: Report failure - if: ${{ failure() }} - run: gh pr comment ${{ inputs.pr }} --body "

Command failed ❌

Run by @${{ github.actor }} for ${{ github.workflow }} failed. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} - - run: git pull --rebase - - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: cmd-action - ${{ github.workflow }} - branch: ${{ steps.gh.outputs.branch }} - - name: Report succeed - run: gh pr comment ${{ inputs.pr }} --body "

Action completed 🎉🎉

Run by @${{ github.actor }} for ${{ github.workflow }} completed 🎉. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/command-fmt.yml b/.github/workflows/command-fmt.yml deleted file mode 100644 index fc37a17ac549..000000000000 --- a/.github/workflows/command-fmt.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Command FMT - -on: - workflow_dispatch: - inputs: - pr: - description: Number of the Pull Request - required: true - -jobs: - set-image: - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - cmd-fmt: - needs: [set-image] - runs-on: ubuntu-latest - timeout-minutes: 20 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - permissions: - contents: write - pull-requests: write - steps: - - name: Download repo - uses: actions/checkout@v4 - - name: Install gh cli - id: gh - uses: ./.github/actions/set-up-gh - with: - pr-number: ${{ inputs.pr }} - GH_TOKEN: ${{ github.token }} - - name: Run FMT - run: | - cargo --version - rustc --version - cargo +nightly --version - rustc +nightly --version - - cargo +nightly fmt - - # format toml. - # since paritytech/ci-unified:bullseye-1.73.0-2023-11-01-v20231204 includes taplo-cli - taplo format --config .config/taplo.toml - - name: Report failure - if: ${{ failure() }} - run: gh pr comment ${{ inputs.pr }} --body "

Command failed ❌

Run by @${{ github.actor }} for ${{ github.workflow }} failed. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} - - run: git pull --rebase - - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: cmd-action - ${{ github.workflow }} - branch: ${{ steps.gh.outputs.branch }} - - name: Report succeed - run: gh pr comment ${{ inputs.pr }} --body "

Action completed 🎉🎉

Run by @${{ github.actor }} for ${{ github.workflow }} completed 🎉. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/command-inform.yml b/.github/workflows/command-inform.yml deleted file mode 100644 index afdcf4c1b7b9..000000000000 --- a/.github/workflows/command-inform.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Inform of new command action - -on: - issue_comment: - types: [created] - -jobs: - comment: - runs-on: ubuntu-latest - # Temporary disable the bot until the new command bot works properly - if: github.event.issue.pull_request && startsWith(github.event.comment.body, 'bot ') && false - steps: - - name: Inform that the new command exist - uses: actions/github-script@v7 - with: - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: 'We are migrating the command bot to be a GitHub Action

Please, see the documentation on how to use it' - }) diff --git a/.github/workflows/command-prdoc.yml b/.github/workflows/command-prdoc.yml deleted file mode 100644 index 3a08b9a5fb28..000000000000 --- a/.github/workflows/command-prdoc.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Command PrDoc - -on: - workflow_dispatch: - inputs: - pr: - type: number - description: Number of the Pull Request - required: true - bump: - type: choice - description: Default bump level for all crates - default: "TODO" - required: true - options: - - "TODO" - - "no change" - - "patch" - - "minor" - - "major" - audience: - type: choice - description: Audience of the PrDoc - default: "TODO" - required: true - options: - - "TODO" - - "Runtime Dev" - - "Runtime User" - - "Node Dev" - - "Node User" - overwrite: - type: choice - description: Overwrite existing PrDoc - default: "true" - required: true - options: - - "true" - - "false" - -concurrency: - group: command-prdoc - cancel-in-progress: true - -jobs: - set-image: - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - cmd-prdoc: - needs: [set-image] - runs-on: ubuntu-latest - timeout-minutes: 20 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - permissions: - contents: write - pull-requests: write - steps: - - name: Download repo - uses: actions/checkout@v4 - - name: Install gh cli - id: gh - uses: ./.github/actions/set-up-gh - with: - pr-number: ${{ inputs.pr }} - GH_TOKEN: ${{ github.token }} - - name: Generate PrDoc - run: | - python3 -m pip install -q cargo-workspace PyGithub whatthepatch pyyaml toml - - python3 .github/scripts/generate-prdoc.py --pr "${{ inputs.pr }}" --bump "${{ inputs.bump }}" --audience "${{ inputs.audience }}" --force "${{ inputs.overwrite }}" - - - name: Report failure - if: ${{ failure() }} - run: gh pr comment ${{ inputs.pr }} --body "

Command failed ❌

Run by @${{ github.actor }} for ${{ github.workflow }} failed. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} - - name: Push Commit - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: Add PrDoc (auto generated) - branch: ${{ steps.gh.outputs.branch }} - file_pattern: 'prdoc/*.prdoc' diff --git a/.github/workflows/command-sync.yml b/.github/workflows/command-sync.yml deleted file mode 100644 index c610f4066a87..000000000000 --- a/.github/workflows/command-sync.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Command Sync - -on: - workflow_dispatch: - inputs: - pr: - description: Number of the Pull Request - required: true - chain: - description: Chain - type: choice - required: true - options: - - westend - - rococo - sync-type: - description: Sync type - type: choice - required: true - options: - - warp - - full - - fast - - fast-unsafe - -jobs: - set-image: - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - cmd-sync: - needs: [set-image] - runs-on: arc-runners-polkadot-sdk-warpsync - container: - image: ${{ needs.set-image.outputs.IMAGE }} - permissions: - contents: write - pull-requests: write - steps: - - name: Download repo - uses: actions/checkout@v4 - - name: Install gh cli - id: gh - uses: ./.github/actions/set-up-gh - with: - pr-number: ${{ inputs.pr }} - GH_TOKEN: ${{ github.token }} - - name: Run sync - run: | - "./scripts/sync.sh" --chain "${{ inputs.chain }}" --type "${{ inputs.sync-type }}" - - name: Report failure - if: ${{ failure() }} - run: gh pr comment ${{ inputs.pr }} --body "

Command failed ❌

Run by @${{ github.actor }} for ${{ github.workflow }} failed. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} - - run: git pull --rebase - - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: cmd-action - ${{ github.workflow }} - branch: ${{ steps.gh.outputs.branch }} - - name: Report succeed - run: gh pr comment ${{ inputs.pr }} --body "

Action completed 🎉🎉

Run by @${{ github.actor }} for ${{ github.workflow }} completed 🎉. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/command-update-ui.yml b/.github/workflows/command-update-ui.yml deleted file mode 100644 index 860177adc879..000000000000 --- a/.github/workflows/command-update-ui.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Command Update UI - -on: - workflow_dispatch: - inputs: - pr: - description: Number of the Pull Request - required: true - rust-version: - description: Version of rust. Example 1.70 - required: false - -jobs: - set-image: - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - cmd-update-ui: - needs: [set-image] - runs-on: arc-runners-polkadot-sdk-beefy - timeout-minutes: 90 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - permissions: - contents: write - pull-requests: write - steps: - - name: Download repo - uses: actions/checkout@v4 - - name: Install gh cli - id: gh - uses: ./.github/actions/set-up-gh - with: - pr-number: ${{ inputs.pr }} - GH_TOKEN: ${{ github.token }} - - name: Run update-ui - run: | - "./scripts/update-ui-tests.sh" "${{ inputs.rust-version }}" - - name: Report failure - if: ${{ failure() }} - run: gh pr comment ${{ inputs.pr }} --body "

Command failed ❌

Run by @${{ github.actor }} for ${{ github.workflow }} failed. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} - - run: git pull --rebase - - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: cmd-action - ${{ github.workflow }} - branch: ${{ steps.gh.outputs.branch }} - - name: Report succeed - run: gh pr comment ${{ inputs.pr }} --body "

Action completed 🎉🎉

Run by @${{ github.actor }} for ${{ github.workflow }} completed 🎉. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/review-bot.yml b/.github/workflows/review-bot.yml index 80c96b0ef537..3dd5b1114813 100644 --- a/.github/workflows/review-bot.yml +++ b/.github/workflows/review-bot.yml @@ -15,7 +15,6 @@ on: jobs: review-approvals: runs-on: ubuntu-latest - environment: master steps: - name: Generate token id: app_token diff --git a/.github/workflows/runtimes-matrix.json b/.github/workflows/runtimes-matrix.json new file mode 100644 index 000000000000..45a3acd3f166 --- /dev/null +++ b/.github/workflows/runtimes-matrix.json @@ -0,0 +1,98 @@ +[ + { + "name": "dev", + "package": "kitchensink-runtime", + "path": "substrate/frame", + "uri": null, + "is_relay": false + }, + { + "name": "westend", + "package": "westend-runtime", + "path": "polkadot/runtime/westend", + "uri": "wss://try-runtime-westend.polkadot.io:443", + "is_relay": true + }, + { + "name": "rococo", + "package": "rococo-runtime", + "path": "polkadot/runtime/rococo", + "uri": "wss://try-runtime-rococo.polkadot.io:443", + "is_relay": true + }, + { + "name": "asset-hub-westend", + "package": "asset-hub-westend-runtime", + "path": "cumulus/parachains/runtimes/assets/asset-hub-westend", + "uri": "wss://westend-asset-hub-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "asset-hub-rococo", + "package": "asset-hub-rococo-runtime", + "path": "cumulus/parachains/runtimes/assets/asset-hub-rococo", + "uri": "wss://rococo-asset-hub-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "bridge-hub-rococo", + "package": "bridge-hub-rococo-runtime", + "path": "cumulus/parachains/runtimes/bridges/bridge-hub-rococo", + "uri": "wss://rococo-bridge-hub-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "bridge-hub-westend", + "package": "bridge-hub-rococo-runtime", + "path": "cumulus/parachains/runtimes/bridges/bridge-hub-westend", + "uri": "wss://westend-bridge-hub-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "collectives-westend", + "package": "collectives-westend-runtime", + "path": "cumulus/parachains/runtimes/collectives/collectives-westend", + "uri": "wss://westend-collectives-rpc.polkadot.io:443" + }, + { + "name": "contracts-rococo", + "package": "contracts-rococo-runtime", + "path": "cumulus/parachains/runtimes/contracts/contracts-rococo", + "uri": "wss://rococo-contracts-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "coretime-rococo", + "package": "coretime-rococo-runtime", + "path": "cumulus/parachains/runtimes/coretime/coretime-rococo", + "uri": "wss://rococo-coretime-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "coretime-westend", + "package": "coretime-westend-runtime", + "path": "cumulus/parachains/runtimes/coretime/coretime-westend", + "uri": "wss://westend-coretime-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "glutton-westend", + "package": "glutton-westend-runtime", + "path": "cumulus/parachains/runtimes/gluttons/glutton-westend", + "is_relay": false + }, + { + "name": "people-rococo", + "package": "people-rococo-runtime", + "path": "cumulus/parachains/runtimes/people/people-rococo", + "uri": "wss://rococo-people-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "people-westend", + "package": "people-westend-runtime", + "path": "cumulus/parachains/runtimes/people/people-westend", + "uri": "wss://westend-people-rpc.polkadot.io:443", + "is_relay": false + } +] diff --git a/.gitignore b/.gitignore index e3e382af6195..0263626d832d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ **/node_modules **/target/ **/wip/*.stderr +**/__pycache__/ /.cargo/config /.envrc artifacts diff --git a/docs/contributor/CONTRIBUTING.md b/docs/contributor/CONTRIBUTING.md index 2e2d7a7fb4f7..53f42b9ae4fb 100644 --- a/docs/contributor/CONTRIBUTING.md +++ b/docs/contributor/CONTRIBUTING.md @@ -161,11 +161,11 @@ test output there is a script * `./scripts/update-ui-tests.sh` to update the tests for a current rust version locally * `./scripts/update-ui-tests.sh 1.70` # to update the tests for a specific rust version locally -Or if you have opened PR and you're member of `paritytech` - you can use command-bot to run the tests for you in CI: -* `bot update-ui` - will run the tests for the current rust version -* `bot update-ui latest --rust_version=1.70.0` - will run the tests for the specified rust version -* `bot update-ui latest -v CMD_IMAGE=paritytech/ci-unified:bullseye-1.70.0-2023-05-23 --rust_version=1.70.0` - will run -the tests for the specified rust version and specified image +Or if you have opened PR and you're member of `paritytech` - you can use [/cmd](./commands-readme.md) +to run the tests for you in CI: +* `/cmd update-ui` - will run the tests for the current rust version +* `/cmd update-ui --image docker.io/paritytech/ci-unified:bullseye-1.70.0-2023-05-23` - +will run the tests for the specified rust version and specified image ## Feature Propagation @@ -175,7 +175,7 @@ We use [zepter](https://github.com/ggwpez/zepter) to enforce features are propag If you're member of **paritytech** org - you can use command-bot to run various of common commands in CI: -Start with comment in PR: `bot help` to see the list of available commands. +Start with comment in PR: `/cmd --help` to see the list of available commands. ## Deprecating code diff --git a/docs/contributor/commands-readme.md b/docs/contributor/commands-readme.md new file mode 100644 index 000000000000..2bb9bd7e7d58 --- /dev/null +++ b/docs/contributor/commands-readme.md @@ -0,0 +1,44 @@ +# Running Commands in PRs + +You can run commands in PRs by triggering it via comment. It will use the context of your PR and post the results back. +Note: it works only for members of the `paritytech` organization. + +## Usage + +`/cmd --help` to see all available commands and usage format + +`/cmd --help` to see the usage of a specific command + + +### Commands + +- `/cmd fmt` to format the code in the PR. It commits back with the formatted code (fmt) and configs (taplo). + +- `/cmd bench` to generate weights for a runtime. Read more about [Weight Generation](weight-generation.md) + +### Flags + +1.`--quiet` to suppress the output of the command in the comments. +By default, the Start and End/Failure of the command will be commented with the link to a pipeline. +If you want to avoid, use this flag. Go to +[Action Tab](https://github.com/paritytech/polkadot-sdk/actions/workflows/cmd.yml) to see the pipeline status. + +2.`--continue-on-fail` to continue running the command even if something inside a command +(like specific pallet weight generation) are failed. +Basically avoids interruption in the middle with `exit 1` +The pipeline logs will include what is failed (like which runtimes/pallets), then you can re-run them separately or not. + +3.`--clean` to clean up all yours and bot's comments in PR relevant to `/cmd` commands. If you run too many commands, +or they keep failing, and you're rerunning them again, it's handy to add this flag to keep a PR clean. + +### Adding new Commands +Feel free to add new commands to the workflow, however **_note_** that triggered workflows will use the actions +from `main` (default) branch, meaning they will take effect only after the PR with new changes/command is merged. +If you want to test the new command, it's better to test in your fork and local-to-fork PRs, where you control +the default branch. + +### Examples +The regex in cmd.yml is: `^(\/cmd )([-\/\s\w.=:]+)$` accepts only alphanumeric, space, "-", "/", "=", ":", "." chars. + +`/cmd bench --runtime bridge-hub-westend --pallet=pallet_name` +`/cmd update-ui --image=docker.io/paritytech/ci-unified:bullseye-1.77.0-2024-04-10-v202407161507 --clean` diff --git a/docs/contributor/weight-generation.md b/docs/contributor/weight-generation.md new file mode 100644 index 000000000000..ebfdca59cae5 --- /dev/null +++ b/docs/contributor/weight-generation.md @@ -0,0 +1,69 @@ +# Weight Generation + +To generate weights for a runtime. +Weights generation is using self-hosted runner which is provided by Parity CI, the rest commands are using standard +GitHub runners on `ubuntu-latest` or `ubuntu-20.04`. +Self-hosted runner for benchmarks (arc-runners-Polkadot-sdk-benchmark) is configured to meet requirements of reference +hardware for running validators +https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware + +In a PR run the actions through comment: + +```sh +/cmd bench --help # outputs the actual usage documentation with examples and supported runtimes + +# or + +/cmd --help # to see all available commands +``` + +To regenerate all weights (however it will take long, +so don't do it unless you really need it), run the following command: +```sh +/cmd bench +``` + +To generate weights for all pallets in a particular runtime(s), run the following command: +```sh +/cmd bench --runtime kusama polkadot +``` + +For Substrate pallets (supports sub-modules too): +```sh +/cmd bench --runtime dev --pallet pallet_asset_conversion_ops +``` + +> **📝 Note**: The action is not being run right-away, it will be queued and run in the next available runner. +So might be quick, but might also take up to 10 mins (That's in control of Github). +Once the action is run, you'll see reaction 👀 on original comment, and if you didn't pass `--quiet` - +it will also send a link to a pipeline when started, and link to whole workflow when finished. + +--- + +> **💡Hint #1** : if you run all runtimes or all pallets, it might be that some pallet in the middle is failed +to generate weights, thus it stops (fails) the whole pipeline. +> If you want, you can make it to continue running, even if some pallets are failed, add `--continue-on-fail` +flag to the command. The report will include which runtimes/pallets have failed, then you can re-run +them separately after all is done. + +This way it runs all possible runtimes for the specified pallets, if it finds them in the runtime +```sh +/cmd bench --pallet pallet_balances pallet_xcm_benchmarks::generic pallet_xcm_benchmarks::fungible +``` + +If you want to run all specific pallet(s) for specific runtime(s), you can do it like this: +```sh +/cmd bench --runtime bridge-hub-polkadot --pallet pallet_xcm_benchmarks::generic pallet_xcm_benchmarks::fungible +``` + + +> **💡Hint #2** : Sometimes when you run too many commands, or they keep failing and you're rerunning them again, +it's handy to add `--clean` flag to the command. This will clean up all yours and bot's comments in PR relevant to +/cmd commands. + +```sh +/cmd bench --runtime kusama polkadot --pallet=pallet_balances --clean --continue-on-fail +``` + +> **💡Hint #3** : If you have questions or need help, feel free to tag @paritytech/opstooling (in github comments) +or ping in [matrix](https://matrix.to/#/#command-bot:parity.io) channel. diff --git a/scripts/bench-all.sh b/scripts/bench-all.sh deleted file mode 100755 index e5512e26bbad..000000000000 --- a/scripts/bench-all.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -eu -o pipefail -shopt -s inherit_errexit -shopt -s globstar - -. "$(realpath "$(dirname "${BASH_SOURCE[0]}")/command-utils.sh")" - -get_arg optional --pallet "$@" -PALLET="${out:-""}" - -if [[ ! -z "$PALLET" ]]; then - . "$(dirname "${BASH_SOURCE[0]}")/lib/bench-all-pallet.sh" "$@" -else - . "$(dirname "${BASH_SOURCE[0]}")/bench.sh" --subcommand=all "$@" -fi diff --git a/scripts/bench.sh b/scripts/bench.sh deleted file mode 100755 index 2f4ef7ec6a14..000000000000 --- a/scripts/bench.sh +++ /dev/null @@ -1,117 +0,0 @@ -#!/bin/bash -# Initially based on https://github.com/paritytech/bench-bot/blob/cd3b2943d911ae29e41fe6204788ef99c19412c3/bench.js - -# Most external variables used in this script, such as $GH_CONTRIBUTOR, are -# related to https://github.com/paritytech/try-runtime-bot - -# This script relies on $GITHUB_TOKEN which is probably a protected GitLab CI -# variable; if this assumption holds true, it is implied that this script should -# be ran only on protected pipelines - -set -eu -o pipefail -shopt -s inherit_errexit - -# realpath allows to reuse the current -BENCH_ROOT_DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")") - -. "$(realpath "$(dirname "${BASH_SOURCE[0]}")/command-utils.sh")" - -repository_name="$(basename "$PWD")" - -get_arg optional --target_dir "$@" -target_dir="${out:-""}" - -get_arg optional --noexit "$@" -noexit="${out:-""}" - -output_path="." - -profile="production" - -if [[ "$repository_name" == "polkadot-sdk" ]]; then - output_path="./$target_dir" -fi - -cargo_run_benchmarks="cargo run --quiet --profile=${profile}" - -echo "Repository: $repository_name" -echo "Target Dir: $target_dir" -echo "Output Path: $output_path" - -cargo_run() { - echo "Running $cargo_run_benchmarks" "${args[@]}" - - # if not patched with PATCH_something=123 then use --locked - if [[ -z "${BENCH_PATCHED:-}" ]]; then - cargo_run_benchmarks+=" --locked" - fi - - $cargo_run_benchmarks "${args[@]}" -} - - -main() { - - # Remove the "github" remote since the same repository might be reused by a - # GitLab runner, therefore the remote might already exist from a previous run - # in case it was not cleaned up properly for some reason - &>/dev/null git remote remove github || : - - tmp_dirs=() - cleanup() { - exit_code=$? - # Clean up the "github" remote at the end since it contains the - # $GITHUB_TOKEN secret, which is only available for protected pipelines on - # GitLab - &>/dev/null git remote remove github || : - rm -rf "${tmp_dirs[@]}" - echo "Done, exit: $exit_code" - exit $exit_code - } - - # avoid exit if --noexit is passed - if [ -z "$noexit" ]; then - trap cleanup EXIT - fi - - # set -x - - get_arg required --subcommand "$@" - local subcommand="${out:-""}" - - case "$subcommand" in - runtime|pallet|xcm) - echo 'Running bench_pallet' - . "$BENCH_ROOT_DIR/lib/bench-pallet.sh" "$@" - ;; - overhead) - echo 'Running bench_overhead' - . "$BENCH_ROOT_DIR/lib/bench-overhead.sh" "$@" - ;; - all) - echo "Running all-$target_dir" - . "$BENCH_ROOT_DIR/lib/bench-all-${target_dir}.sh" "$@" - ;; - *) - die "Invalid subcommand $subcommand to process_args" - ;; - esac - - # set +x - - # in case we used diener to patch some dependency during benchmark execution, - # revert the patches so that they're not included in the diff - git checkout --quiet HEAD Cargo.toml - - # Save the generated weights to GitLab artifacts in case commit+push fails - echo "Showing weights diff for command" - git diff -P | tee -a "${ARTIFACTS_DIR}/weights.patch" - echo "Wrote weights patch to \"${ARTIFACTS_DIR}/weights.patch\"" - - - # instead of using `cargo run --locked`, we allow the Cargo files to be updated - # but avoid committing them. It is so `cmd_runner_apply_patches` can work - git restore --staged Cargo.* -} - -main "$@" diff --git a/scripts/command-utils.sh b/scripts/command-utils.sh deleted file mode 100644 index 252e4c86480e..000000000000 --- a/scripts/command-utils.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env bash - -if [ "${LOADED_UTILS_SH:-}" ]; then - return -else - export LOADED_UTILS_SH=true -fi - -export ARTIFACTS_DIR="$PWD/.git/.artifacts" - -die() { - if [ "${1:-}" ]; then - >&2 echo "$1" - fi - exit 1 -} - -get_arg() { - local arg_type="$1" - shift - - local is_required - case "$arg_type" in - required|required-many) - is_required=true - ;; - optional|optional-many) ;; - *) - die "Invalid is_required argument \"$2\" in get_arg" - ;; - esac - - local has_many_values - if [ "${arg_type: -6}" == "-many" ]; then - has_many_values=true - fi - - local option_arg="$1" - shift - - local args=("$@") - - unset out - out=() - - local get_next_arg - for arg in "${args[@]}"; do - if [ "${get_next_arg:-}" ]; then - out+=("$arg") - unset get_next_arg - if [ ! "${has_many_values:-}" ]; then - break - fi - # --foo=bar (get the value after '=') - elif [ "${arg:0:$(( ${#option_arg} + 1 ))}" == "$option_arg=" ]; then - out+=("${arg:$(( ${#option_arg} + 1 ))}") - if [ ! "${has_many_values:-}" ]; then - break - fi - # --foo bar (get the next argument) - elif [ "$arg" == "$option_arg" ]; then - get_next_arg=true - fi - done - - # arg list ended with --something but no argument was provided next - if [ "${get_next_arg:-}" ]; then - die "Expected argument after \"${args[-1]}"\" - fi - - if [ "${out[0]:-}" ]; then - if [ ! "${has_many_values:-}" ]; then - out="${out[0]}" - fi - elif [ "${is_required:-}" ]; then - die "Argument $option_arg is required, but was not found" - else - unset out - fi -} diff --git a/scripts/lib/bench-all-cumulus.sh b/scripts/lib/bench-all-cumulus.sh deleted file mode 100755 index f4c2a35c6b6b..000000000000 --- a/scripts/lib/bench-all-cumulus.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env bash -# originally moved from https://github.com/paritytech/cumulus/blob/445f9277ab55b4d930ced4fbbb38d27c617c6658/scripts/benchmarks-ci.sh - -# default RUST_LOG is warn, but could be overridden -export RUST_LOG="${RUST_LOG:-error}" - -THIS_DIR=$(dirname "${BASH_SOURCE[0]}") -. "$THIS_DIR/../command-utils.sh" - -POLKADOT_PARACHAIN="./target/$profile/polkadot-parachain" - -run_cumulus_bench() { - local artifactsDir="$ARTIFACTS_DIR" - local category=$1 - local runtimeName=$2 - local paraId=${3:-} - - local benchmarkOutput="$output_path/parachains/runtimes/$category/$runtimeName/src/weights" - local benchmarkRuntimeChain - if [[ ! -z "$paraId" ]]; then - benchmarkRuntimeChain="${runtimeName}-dev-$paraId" - else - benchmarkRuntimeChain="$runtimeName-dev" - fi - - local benchmarkMetadataOutputDir="$artifactsDir/$runtimeName" - mkdir -p "$benchmarkMetadataOutputDir" - - # Load all pallet names in an array. - echo "[+] Listing pallets for runtime $runtimeName for chain: $benchmarkRuntimeChain ..." - local pallets=($( - $POLKADOT_PARACHAIN benchmark pallet --list --chain="${benchmarkRuntimeChain}" |\ - tail -n+2 |\ - cut -d',' -f1 |\ - sort |\ - uniq - )) - - if [ ${#pallets[@]} -ne 0 ]; then - echo "[+] Benchmarking ${#pallets[@]} pallets for runtime $runtimeName for chain: $benchmarkRuntimeChain, pallets:" - for pallet in "${pallets[@]}"; do - echo " [+] $pallet" - done - else - echo "$runtimeName pallet list not found in benchmarks-ci.sh" - exit 1 - fi - - for pallet in "${pallets[@]}"; do - # (by default) do not choose output_file, like `pallet_assets.rs` because it does not work for multiple instances - # `benchmark pallet` command will decide the output_file name if there are multiple instances - local output_file="" - local extra_args="" - # a little hack for pallet_xcm_benchmarks - we want to force custom implementation for XcmWeightInfo - if [[ "$pallet" == "pallet_xcm_benchmarks::generic" ]] || [[ "$pallet" == "pallet_xcm_benchmarks::fungible" ]]; then - output_file="xcm/${pallet//::/_}.rs" - extra_args="--template=$output_path/templates/xcm-bench-template.hbs" - fi - $POLKADOT_PARACHAIN benchmark pallet \ - $extra_args \ - --chain="${benchmarkRuntimeChain}" \ - --wasm-execution=compiled \ - --pallet="$pallet" \ - --no-storage-info \ - --no-median-slopes \ - --no-min-squares \ - --extrinsic='*' \ - --steps=50 \ - --repeat=20 \ - --json \ - --header="$output_path/file_header.txt" \ - --output="${benchmarkOutput}/${output_file}" >> "$benchmarkMetadataOutputDir/${pallet//::/_}_benchmark.json" - done -} - - -echo "[+] Compiling benchmarks..." -cargo build --profile $profile --locked --features=runtime-benchmarks -p polkadot-parachain-bin - -# Run benchmarks for all pallets of a given runtime if runtime argument provided -get_arg optional --runtime "$@" -runtime="${out:-""}" - -if [[ $runtime ]]; then - paraId="" - case "$runtime" in - asset-*) - category="assets" - ;; - collectives-*) - category="collectives" - ;; - coretime-*) - category="coretime" - ;; - bridge-*) - category="bridge-hubs" - ;; - contracts-*) - category="contracts" - ;; - people-*) - category="people" - ;; - glutton-*) - category="glutton" - paraId="1300" - ;; - *) - echo "Unknown runtime: $runtime" - exit 1 - ;; - esac - - run_cumulus_bench $category $runtime $paraId - -else # run all - # Assets - run_cumulus_bench assets asset-hub-rococo - run_cumulus_bench assets asset-hub-westend - - # Collectives - run_cumulus_bench collectives collectives-westend - - # Coretime - run_cumulus_bench coretime coretime-rococo - run_cumulus_bench coretime coretime-westend - - # People - run_cumulus_bench people people-rococo - run_cumulus_bench people people-westend - - # Bridge Hubs - run_cumulus_bench bridge-hubs bridge-hub-rococo - run_cumulus_bench bridge-hubs bridge-hub-westend - - # Glutton - run_cumulus_bench glutton glutton-westend 1300 -fi diff --git a/scripts/lib/bench-all-pallet.sh b/scripts/lib/bench-all-pallet.sh deleted file mode 100644 index e6908045ddbd..000000000000 --- a/scripts/lib/bench-all-pallet.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env bash - -set -eu -o pipefail -shopt -s inherit_errexit -shopt -s globstar - -. "$(dirname "${BASH_SOURCE[0]}")/../command-utils.sh" - -get_arg required --pallet "$@" -PALLET="${out:-""}" - -REPO_NAME="$(basename "$PWD")" -BASE_COMMAND="$(dirname "${BASH_SOURCE[0]}")/../../bench/bench.sh --noexit=true --subcommand=pallet" - -WEIGHT_FILE_PATHS=( $(find . -type f -name "${PALLET}.rs" -path "**/weights/*" | sed 's|^\./||g') ) - -# convert pallet_ranked_collective to ranked-collective -CLEAN_PALLET=$(echo $PALLET | sed 's/pallet_//g' | sed 's/_/-/g') - -# add substrate pallet weights to a list -SUBSTRATE_PALLET_PATH=$(ls substrate/frame/$CLEAN_PALLET/src/weights.rs || :) -if [ ! -z "${SUBSTRATE_PALLET_PATH}" ]; then - WEIGHT_FILE_PATHS+=("$SUBSTRATE_PALLET_PATH") -fi - -# add trappist pallet weights to a list -TRAPPIST_PALLET_PATH=$(ls pallet/$CLEAN_PALLET/src/weights.rs || :) -if [ ! -z "${TRAPPIST_PALLET_PATH}" ]; then - WEIGHT_FILE_PATHS+=("$TRAPPIST_PALLET_PATH") -fi - -COMMANDS=() - -if [ "${#WEIGHT_FILE_PATHS[@]}" -eq 0 ]; then - echo "No weights files found for pallet: $PALLET" - exit 1 -else - echo "Found weights files for pallet: $PALLET" -fi - -for f in ${WEIGHT_FILE_PATHS[@]}; do - echo "- $f" - # f examples: - # cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_balances.rs - # polkadot/runtime/rococo/src/weights/pallet_balances.rs - # runtime/trappist/src/weights/pallet_assets.rs - TARGET_DIR=$(echo $f | cut -d'/' -f 1) - - if [ "$REPO_NAME" == "polkadot-sdk" ]; then - case $TARGET_DIR in - cumulus) - TYPE=$(echo $f | cut -d'/' -f 2) - # Example: cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_balances.rs - if [ "$TYPE" == "parachains" ]; then - RUNTIME=$(echo $f | cut -d'/' -f 5) - RUNTIME_DIR=$(echo $f | cut -d'/' -f 4) - COMMANDS+=("$BASE_COMMAND --runtime=$RUNTIME --runtime_dir=$RUNTIME_DIR --target_dir=$TARGET_DIR --pallet=$PALLET") - fi - ;; - polkadot) - # Example: polkadot/runtime/rococo/src/weights/pallet_balances.rs - RUNTIME=$(echo $f | cut -d'/' -f 3) - COMMANDS+=("$BASE_COMMAND --runtime=$RUNTIME --target_dir=$TARGET_DIR --pallet=$PALLET") - ;; - substrate) - # Example: substrate/frame/contracts/src/weights.rs - COMMANDS+=("$BASE_COMMAND --target_dir=$TARGET_DIR --runtime=dev --pallet=$PALLET") - ;; - *) - echo "Unknown dir: $TARGET_DIR" - exit 1 - ;; - esac - fi - - if [ "$REPO_NAME" == "trappist" ]; then - case $TARGET_DIR in - runtime) - TYPE=$(echo $f | cut -d'/' -f 2) - if [ "$TYPE" == "trappist" || "$TYPE" == "stout" ]; then - # Example: runtime/trappist/src/weights/pallet_assets.rs - COMMANDS+=("$BASE_COMMAND --target_dir=trappist --runtime=$TYPE --pallet=$PALLET") - fi - ;; - *) - echo "Unknown dir: $TARGET_DIR" - exit 1 - ;; - esac - fi -done - -for cmd in "${COMMANDS[@]}"; do - echo "Running command: $cmd" - . $cmd -done diff --git a/scripts/lib/bench-all-polkadot.sh b/scripts/lib/bench-all-polkadot.sh deleted file mode 100644 index ac52e00140e3..000000000000 --- a/scripts/lib/bench-all-polkadot.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/bash - -# Runs all benchmarks for all pallets, for a given runtime, provided by $1 -# Should be run on a reference machine to gain accurate benchmarks -# current reference machine: https://github.com/paritytech/polkadot/pull/6508/files -# original source: https://github.com/paritytech/polkadot/blob/b9842c4b52f6791fef6c11ecd020b22fe614f041/scripts/run_all_benches.sh - -get_arg required --runtime "$@" -runtime="${out:-""}" - -# default RUST_LOG is error, but could be overridden -export RUST_LOG="${RUST_LOG:-error}" - -echo "[+] Compiling benchmarks..." -cargo build --profile $profile --locked --features=runtime-benchmarks -p polkadot - -POLKADOT_BIN="./target/$profile/polkadot" - -# Update the block and extrinsic overhead weights. -echo "[+] Benchmarking block and extrinsic overheads..." -OUTPUT=$( - $POLKADOT_BIN benchmark overhead \ - --chain="${runtime}-dev" \ - --wasm-execution=compiled \ - --weight-path="$output_path/runtime/${runtime}/constants/src/weights/" \ - --warmup=10 \ - --repeat=100 \ - --header="$output_path/file_header.txt" -) -if [ $? -ne 0 ]; then - echo "$OUTPUT" >> "$ERR_FILE" - echo "[-] Failed to benchmark the block and extrinsic overheads. Error written to $ERR_FILE; continuing..." -fi - - -# Load all pallet names in an array. -PALLETS=($( - $POLKADOT_BIN benchmark pallet --list --chain="${runtime}-dev" |\ - tail -n+2 |\ - cut -d',' -f1 |\ - sort |\ - uniq -)) - -echo "[+] Benchmarking ${#PALLETS[@]} pallets for runtime $runtime" - -# Define the error file. -ERR_FILE="${ARTIFACTS_DIR}/benchmarking_errors.txt" -# Delete the error file before each run. -rm -f $ERR_FILE - -# Benchmark each pallet. -for PALLET in "${PALLETS[@]}"; do - echo "[+] Benchmarking $PALLET for $runtime"; - - output_file="" - if [[ $PALLET == *"::"* ]]; then - # translates e.g. "pallet_foo::bar" to "pallet_foo_bar" - output_file="${PALLET//::/_}.rs" - fi - - OUTPUT=$( - $POLKADOT_BIN benchmark pallet \ - --chain="${runtime}-dev" \ - --steps=50 \ - --repeat=20 \ - --no-storage-info \ - --no-median-slopes \ - --no-min-squares \ - --pallet="$PALLET" \ - --extrinsic="*" \ - --execution=wasm \ - --wasm-execution=compiled \ - --header="$output_path/file_header.txt" \ - --output="$output_path/runtime/${runtime}/src/weights/${output_file}" 2>&1 - ) - if [ $? -ne 0 ]; then - echo "$OUTPUT" >> "$ERR_FILE" - echo "[-] Failed to benchmark $PALLET. Error written to $ERR_FILE; continuing..." - fi -done - -# Check if the error file exists. -if [ -f "$ERR_FILE" ]; then - echo "[-] Some benchmarks failed. See: $ERR_FILE" -else - echo "[+] All benchmarks passed." -fi diff --git a/scripts/lib/bench-all-substrate.sh b/scripts/lib/bench-all-substrate.sh deleted file mode 100644 index eeb18cdd8bbb..000000000000 --- a/scripts/lib/bench-all-substrate.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env bash - -# This file is part of Substrate. -# Copyright (C) 2022 Parity Technologies (UK) Ltd. -# SPDX-License-Identifier: Apache-2.0 -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script has three parts which all use the Substrate runtime: -# - Pallet benchmarking to update the pallet weights -# - Overhead benchmarking for the Extrinsic and Block weights -# - Machine benchmarking -# -# Should be run on a reference machine to gain accurate benchmarks -# current reference machine: https://github.com/paritytech/substrate/pull/5848 - -# Original source: https://github.com/paritytech/substrate/blob/ff9921a260a67e3a71f25c8b402cd5c7da787a96/scripts/run_all_benchmarks.sh -# Fail if any sub-command in a pipe fails, not just the last one. -set -o pipefail -# Fail on undeclared variables. -set -u -# Fail if any sub-command fails. -set -e -# Fail on traps. -# set -E - -# default RUST_LOG is warn, but could be overridden -export RUST_LOG="${RUST_LOG:-error}" - -echo "[+] Compiling Substrate benchmarks..." -cargo build --profile=$profile --locked --features=runtime-benchmarks -p staging-node-cli - -# The executable to use. -SUBSTRATE="./target/$profile/substrate-node" - -# Manually exclude some pallets. -EXCLUDED_PALLETS=( - # Helper pallets - "pallet_election_provider_support_benchmarking" - # Pallets without automatic benchmarking - "pallet_babe" - "pallet_grandpa" - "pallet_mmr" - "pallet_offences" - # Only used for testing, does not need real weights. - "frame_benchmarking_pallet_pov" - "pallet_example_tasks" - "pallet_example_basic" - "pallet_example_split" - "pallet_example_kitchensink" - "pallet_example_mbm" - "tasks_example" -) - -# Load all pallet names in an array. -ALL_PALLETS=($( - $SUBSTRATE benchmark pallet --list --chain=dev |\ - tail -n+2 |\ - cut -d',' -f1 |\ - sort |\ - uniq -)) - -# Define the error file. -ERR_FILE="${ARTIFACTS_DIR}/benchmarking_errors.txt" - -# Delete the error file before each run. -rm -f "$ERR_FILE" - -mkdir -p "$(dirname "$ERR_FILE")" - -# Update the block and extrinsic overhead weights. -echo "[+] Benchmarking block and extrinsic overheads..." -OUTPUT=$( - $SUBSTRATE benchmark overhead \ - --chain=dev \ - --wasm-execution=compiled \ - --weight-path="$output_path/frame/support/src/weights/" \ - --header="$output_path/HEADER-APACHE2" \ - --warmup=10 \ - --repeat=100 2>&1 -) -if [ $? -ne 0 ]; then - echo "$OUTPUT" >> "$ERR_FILE" - echo "[-] Failed to benchmark the block and extrinsic overheads. Error written to $ERR_FILE; continuing..." -fi - -echo "[+] Benchmarking ${#ALL_PALLETS[@]} Substrate pallets and excluding ${#EXCLUDED_PALLETS[@]}." - -echo "[+] Excluded pallets ${EXCLUDED_PALLETS[@]}" -echo "[+] ------ " -echo "[+] Whole list pallets ${ALL_PALLETS[@]}" - -# Benchmark each pallet. -for PALLET in "${ALL_PALLETS[@]}"; do - FOLDER="$(echo "${PALLET#*_}" | tr '_' '-')"; - WEIGHT_FILE="$output_path/frame/${FOLDER}/src/weights.rs" - - # Skip the pallet if it is in the excluded list. - - if [[ " ${EXCLUDED_PALLETS[@]} " =~ " ${PALLET} " ]]; then - echo "[+] Skipping $PALLET as it is in the excluded list." - continue - fi - - echo "[+] Benchmarking $PALLET with weight file $WEIGHT_FILE"; - - set +e # Disable exit on error for the benchmarking of the pallets - OUTPUT=$( - $SUBSTRATE benchmark pallet \ - --chain=dev \ - --steps=50 \ - --repeat=20 \ - --pallet="$PALLET" \ - --no-storage-info \ - --no-median-slopes \ - --no-min-squares \ - --extrinsic="*" \ - --wasm-execution=compiled \ - --heap-pages=4096 \ - --output="$WEIGHT_FILE" \ - --header="$output_path/HEADER-APACHE2" \ - --template="$output_path/.maintain/frame-weight-template.hbs" 2>&1 - ) - if [ $? -ne 0 ]; then - echo -e "$PALLET: $OUTPUT\n" >> "$ERR_FILE" - echo "[-] Failed to benchmark $PALLET. Error written to $ERR_FILE; continuing..." - fi - set -e # Re-enable exit on error -done - - -# Check if the error file exists. -if [ -s "$ERR_FILE" ]; then - echo "[-] Some benchmarks failed. See: $ERR_FILE" - exit 1 -else - echo "[+] All benchmarks passed." -fi diff --git a/scripts/lib/bench-overhead.sh b/scripts/lib/bench-overhead.sh deleted file mode 100644 index c4cca8b4c128..000000000000 --- a/scripts/lib/bench-overhead.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/bash - -THIS_DIR=$(dirname "${BASH_SOURCE[0]}") -. "$THIS_DIR/../command-utils.sh" - -bench_overhead_common_args=( - -- - benchmark - overhead - --wasm-execution=compiled - --warmup=10 - --repeat=100 -) -bench_overhead() { - local args - case "$target_dir" in - substrate) - args=( - --bin=substrate - "${bench_overhead_common_args[@]}" - --header="$output_path/HEADER-APACHE2" - --weight-path="$output_path/frame/support/src/weights" - --chain="dev" - ) - ;; - polkadot) - get_arg required --runtime "$@" - local runtime="${out:-""}" - args=( - --bin=polkadot - "${bench_overhead_common_args[@]}" - --header="$output_path/file_header.txt" - --weight-path="$output_path/runtime/$runtime/constants/src/weights" - --chain="$runtime-dev" - ) - ;; - cumulus) - get_arg required --runtime "$@" - local runtime="${out:-""}" - args=( - -p=polkadot-parachain-bin - "${bench_overhead_common_args[@]}" - --header="$output_path/file_header.txt" - --weight-path="$output_path/parachains/runtimes/assets/$runtime/src/weights" - --chain="$runtime" - ) - ;; - trappist) - get_arg required --runtime "$@" - local runtime="${out:-""}" - args=( - "${bench_overhead_common_args[@]}" - --header="$output_path/templates/file_header.txt" - --weight-path="$output_path/runtime/$runtime/src/weights" - --chain="$runtime-dev" - ) - ;; - *) - die "Target Dir \"$target_dir\" is not supported in bench_overhead" - ;; - esac - - cargo_run "${args[@]}" -} - -bench_overhead "$@" diff --git a/scripts/lib/bench-pallet.sh b/scripts/lib/bench-pallet.sh deleted file mode 100644 index 15eac31e3a45..000000000000 --- a/scripts/lib/bench-pallet.sh +++ /dev/null @@ -1,178 +0,0 @@ -#!/bin/bash - -THIS_DIR=$(dirname "${BASH_SOURCE[0]}") -. "$THIS_DIR/../command-utils.sh" - -bench_pallet_common_args=( - -- - benchmark - pallet - --steps=50 - --repeat=20 - --extrinsic="*" - --wasm-execution=compiled - --heap-pages=4096 - --json-file="${ARTIFACTS_DIR}/bench.json" -) -bench_pallet() { - get_arg required --subcommand "$@" - local subcommand="${out:-""}" - - get_arg required --runtime "$@" - local runtime="${out:-""}" - - get_arg required --pallet "$@" - local pallet="${out:-""}" - - local args - case "$target_dir" in - substrate) - args=( - --features=runtime-benchmarks - --manifest-path="$output_path/bin/node/cli/Cargo.toml" - "${bench_pallet_common_args[@]}" - --pallet="$pallet" - --chain="$runtime" - ) - - case "$subcommand" in - pallet) - # Translates e.g. "pallet_foo::bar" to "pallet_foo_bar" - local output_dir="${pallet//::/_}" - - # Substrate benchmarks are output to the "frame" directory but they aren't - # named exactly after the $pallet argument. For example: - # - When $pallet == pallet_balances, the output folder is frame/balances - # - When $pallet == frame_benchmarking, the output folder is frame/benchmarking - # The common pattern we infer from those examples is that we should remove - # the prefix - if [[ "$output_dir" =~ ^[A-Za-z]*[^A-Za-z](.*)$ ]]; then - output_dir="${BASH_REMATCH[1]}" - fi - - # We also need to translate '_' to '-' due to the folders' naming - # conventions - output_dir="${output_dir//_/-}" - - args+=( - --header="$output_path/HEADER-APACHE2" - --output="$output_path/frame/$output_dir/src/weights.rs" - --template="$output_path/.maintain/frame-weight-template.hbs" - ) - ;; - *) - die "Subcommand $subcommand is not supported for $target_dir in bench_pallet" - ;; - esac - ;; - polkadot) - # For backward compatibility: replace "-dev" with "" - runtime=${runtime/-dev/} - - local weights_dir="$output_path/runtime/${runtime}/src/weights" - - args=( - --bin=polkadot - --features=runtime-benchmarks - "${bench_pallet_common_args[@]}" - --pallet="$pallet" - --chain="${runtime}-dev" - ) - - case "$subcommand" in - pallet) - args+=( - --header="$output_path/file_header.txt" - --output="${weights_dir}/" - ) - ;; - xcm) - args+=( - --header="$output_path/file_header.txt" - --template="$output_path/xcm/pallet-xcm-benchmarks/template.hbs" - --output="${weights_dir}/xcm/" - ) - ;; - *) - die "Subcommand $subcommand is not supported for $target_dir in bench_pallet" - ;; - esac - ;; - cumulus) - get_arg required --runtime_dir "$@" - local runtime_dir="${out:-""}" - local chain="$runtime" - - # to support specifying parachain id from runtime name (e.g. ["glutton-westend", "glutton-westend-dev-1300"]) - # If runtime ends with "-dev" or "-dev-\d+", leave as it is, otherwise concat "-dev" at the end of $chain - if [[ ! "$runtime" =~ -dev(-[0-9]+)?$ ]]; then - chain="${runtime}-dev" - fi - - # replace "-dev" or "-dev-\d+" with "" for runtime - runtime=$(echo "$runtime" | sed 's/-dev.*//g') - - args=( - -p=polkadot-parachain-bin - --features=runtime-benchmarks - "${bench_pallet_common_args[@]}" - --pallet="$pallet" - --chain="${chain}" - --header="$output_path/file_header.txt" - ) - - case "$subcommand" in - pallet) - args+=( - --output="$output_path/parachains/runtimes/$runtime_dir/$runtime/src/weights/" - ) - ;; - xcm) - mkdir -p "$output_path/parachains/runtimes/$runtime_dir/$runtime/src/weights/xcm" - args+=( - --template="$output_path/templates/xcm-bench-template.hbs" - --output="$output_path/parachains/runtimes/$runtime_dir/$runtime/src/weights/xcm/" - ) - ;; - *) - die "Subcommand $subcommand is not supported for $target_dir in bench_pallet" - ;; - esac - ;; - trappist) - local weights_dir="$output_path/runtime/$runtime/src/weights" - - args=( - --features=runtime-benchmarks - "${bench_pallet_common_args[@]}" - --pallet="$pallet" - --chain="${runtime}-dev" - --header="$output_path/templates/file_header.txt" - ) - - case "$subcommand" in - pallet) - args+=( - --output="${weights_dir}/" - ) - ;; - xcm) - args+=( - --template="$output_path/templates/xcm-bench-template.hbs" - --output="${weights_dir}/xcm/" - ) - ;; - *) - die "Subcommand $subcommand is not supported for $target_dir in bench_pallet" - ;; - esac - ;; - *) - die "Repository $target_dir is not supported in bench_pallet" - ;; - esac - - cargo_run "${args[@]}" -} - -bench_pallet "$@" diff --git a/scripts/sync.sh b/scripts/sync.sh deleted file mode 100755 index b5d8a5219937..000000000000 --- a/scripts/sync.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash - -set -eu -o pipefail - -. "$(realpath "$(dirname "${BASH_SOURCE[0]}")/command-utils.sh")" - - -# Function to check syncing status -check_syncing() { - # Send the system_health request and parse the isSyncing field - RESPONSE=$(curl -sSX POST http://127.0.0.1:9944 \ - --header 'Content-Type: application/json' \ - --data-raw '{"jsonrpc": "2.0", "method": "system_health", "params": [], "id": "1"}') - - # Check for errors in the curl command - if [ $? -ne 0 ]; then - echo "Error: Unable to send request to Polkadot node" - fi - - IS_SYNCING=$(echo $RESPONSE | jq -r '.result.isSyncing') - - # Check for errors in the jq command or missing field in the response - if [ $? -ne 0 ] || [ "$IS_SYNCING" == "null" ]; then - echo "Error: Unable to parse sync status from response" - fi - - # Return the isSyncing value - echo $IS_SYNCING -} - -main() { - get_arg required --chain "$@" - local chain="${out:-""}" - - get_arg required --type "$@" - local type="${out:-""}" - - export RUST_LOG="${RUST_LOG:-remote-ext=debug,runtime=trace}" - - cargo build --release - - cp "./target/release/polkadot" ./polkadot-bin - - # Start sync. - # "&" runs the process in the background - # "> /dev/tty" redirects the output of the process to the terminal - ./polkadot-bin --sync="$type" --chain="$chain" > "$ARTIFACTS_DIR/sync.log" 2>&1 & - - # Get the PID of process - POLKADOT_SYNC_PID=$! - - sleep 10 - - # Poll the node every 100 seconds until syncing is complete - while :; do - SYNC_STATUS="$(check_syncing)" - if [ "$SYNC_STATUS" == "true" ]; then - echo "Node is still syncing..." - sleep 100 - elif [ "$SYNC_STATUS" == "false" ]; then - echo "Node sync is complete!" - kill "$POLKADOT_SYNC_PID" # Stop the Polkadot node process once syncing is complete - exit 0 # Success - elif [[ "$SYNC_STATUS" = Error:* ]]; then - echo "$SYNC_STATUS" - exit 1 # Error - else - echo "Unknown error: $SYNC_STATUS" - exit 1 # Unknown error - fi - done -} - -main "$@" diff --git a/scripts/update-ui-tests.sh b/scripts/update-ui-tests.sh deleted file mode 100755 index d363e51e4041..000000000000 --- a/scripts/update-ui-tests.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -# Script for updating the UI tests for a new rust stable version. -# Exit on error -set -e - -# by default current rust stable will be used -RUSTUP_RUN="" -# check if we have a parameter -# ./scripts/update-ui-tests.sh 1.70 -if [ ! -z "$1" ]; then - echo "RUST_VERSION: $1" - # This will run all UI tests with the rust stable 1.70. - # The script requires that rustup is installed. - RUST_VERSION=$1 - RUSTUP_RUN="rustup run $RUST_VERSION" - - - echo "installing rustup $RUST_VERSION" - if ! command -v rustup &> /dev/null - then - echo "rustup needs to be installed" - exit - fi - - rustup install $RUST_VERSION - rustup component add rust-src --toolchain $RUST_VERSION -fi - -# Ensure we run the ui tests -export RUN_UI_TESTS=1 -# We don't need any wasm files for ui tests -export SKIP_WASM_BUILD=1 -# Let trybuild overwrite the .stderr files -export TRYBUILD=overwrite - -# ./substrate -$RUSTUP_RUN cargo test --manifest-path substrate/primitives/runtime-interface/Cargo.toml ui -$RUSTUP_RUN cargo test -p sp-api-test ui -$RUSTUP_RUN cargo test -p frame-election-provider-solution-type ui -$RUSTUP_RUN cargo test -p frame-support-test --features=no-metadata-docs,try-runtime,experimental ui -$RUSTUP_RUN cargo test -p xcm-procedural ui From 1c4141abeb4c581e503f07af2a3522e6918db591 Mon Sep 17 00:00:00 2001 From: Tarek Mohamed Abdalla Date: Wed, 28 Aug 2024 22:26:29 +0300 Subject: [PATCH 183/382] Fix benchmark failures when using `insecure_zero_ed` flag (#5354) Currently, when the pallet is compiled with the `insecure_zero_ed flag`, benchmarks fail because the minimum balance is set to zero. The PR aims to resolve this issue by implementing a placeholder value for the minimum balance when the `insecure_zero_ed` flag is active. it ensures that benchmarks run successfully regardless of whether this flag is used or not --- prdoc/pr_5354.prdoc | 15 ++++++++++ substrate/frame/balances/src/benchmarking.rs | 30 +++++++++++++------- 2 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 prdoc/pr_5354.prdoc diff --git a/prdoc/pr_5354.prdoc b/prdoc/pr_5354.prdoc new file mode 100644 index 000000000000..e3037b66fbca --- /dev/null +++ b/prdoc/pr_5354.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix benchmark failures when using insecure_zero_ed flag + +doc: + - audience: Runtime Dev + description: | + Currently, when the pallet is compiled with the insecure_zero_ed flag, benchmarks fail because the minimum balance is set to zero. + + The PR aims to resolve this issue by implementing a placeholder value for the minimum balance when the insecure_zero_ed flag is active. it ensures that benchmarks run successfully regardless of whether this flag is used or not + +crates: +- name: pallet-balances + bump: minor diff --git a/substrate/frame/balances/src/benchmarking.rs b/substrate/frame/balances/src/benchmarking.rs index 5740f8081c07..c825300218d4 100644 --- a/substrate/frame/balances/src/benchmarking.rs +++ b/substrate/frame/balances/src/benchmarking.rs @@ -31,6 +31,14 @@ const SEED: u32 = 0; // existential deposit multiplier const ED_MULTIPLIER: u32 = 10; +fn minimum_balance, I: 'static>() -> T::Balance { + if cfg!(feature = "insecure_zero_ed") { + 100u32.into() + } else { + T::ExistentialDeposit::get() + } +} + #[instance_benchmarks] mod benchmarks { use super::*; @@ -40,7 +48,7 @@ mod benchmarks { // * Transfer will create the recipient account. #[benchmark] fn transfer_allow_death() { - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let caller = whitelisted_caller(); // Give some multiple of the existential deposit @@ -75,7 +83,7 @@ mod benchmarks { as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); // Give the recipient account existential deposit (thus their account already exists). - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let _ = as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); @@ -98,7 +106,7 @@ mod benchmarks { // Give the sender account max funds, thus a transfer will not kill account. let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); #[extrinsic_call] @@ -115,7 +123,7 @@ mod benchmarks { let user_lookup = T::Lookup::unlookup(user.clone()); // Give the user some initial balance. - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); @@ -132,7 +140,7 @@ mod benchmarks { let user_lookup = T::Lookup::unlookup(user.clone()); // Give the user some initial balance. - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); @@ -147,7 +155,7 @@ mod benchmarks { // * Transfer will create the recipient account. #[benchmark] fn force_transfer() { - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let source: T::AccountId = account("source", 0, SEED); let source_lookup = T::Lookup::unlookup(source.clone()); @@ -175,7 +183,7 @@ mod benchmarks { #[benchmark(extra)] fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let caller = whitelisted_caller(); // Give some multiple of the existential deposit @@ -214,7 +222,7 @@ mod benchmarks { let recipient_lookup = T::Lookup::unlookup(recipient.clone()); // Give some multiple of the existential deposit - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&caller, balance); @@ -231,7 +239,7 @@ mod benchmarks { let user_lookup = T::Lookup::unlookup(user.clone()); // Give some multiple of the existential deposit - let ed = T::ExistentialDeposit::get(); + let ed = minimum_balance::(); let balance = ed + ed; let _ = as Currency<_>>::make_free_balance_be(&user, balance); @@ -257,8 +265,8 @@ mod benchmarks { .map(|i| -> T::AccountId { let user = account("old_user", i, SEED); let account = AccountData { - free: T::ExistentialDeposit::get(), - reserved: T::ExistentialDeposit::get(), + free: minimum_balance::(), + reserved: minimum_balance::(), frozen: Zero::zero(), flags: ExtraFlags::old_logic(), }; From 56201964f9184004ca17992a3b4778de855b1a35 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 28 Aug 2024 23:32:16 +0200 Subject: [PATCH 184/382] CI: Add backporting bot (#4795) Adds a bot that automatically opens MRs into the `stable2407` branch when the `A4-needs-backport` label is applied to a merged MR. TODO: - [x] ~~Settle on label vs error message trade-off~~ (resolved) docs: # Backporting This document explains how to backport a merged PR from `master` to one of the `stable*` branches. Backports should only be used to fix bugs or security issues - never to introduce new features. ## Steps 1. Fix a bug through a PR that targets `master`. 2. Add label `A4-needs-backport` to the PR. 4. Merge the PR into `master`. 5. Wait for the bot to open the backport PR. 6. Ensure the change is audited or does not need audit. 7. Merge the backport PR. The label can also be added after the PR is merged. ## Example For example here where the dev triggered the process by adding the label after merging: ![backport-ex2](https://github.com/user-attachments/assets/c7b686db-a0fe-41f1-9d6f-959a5a7097b1) --------- Signed-off-by: Oliver Tale-Yazdi --- .github/workflows/check-semver.yml | 2 +- .github/workflows/command-backport.yml | 62 +++++++++++++++++++++++++ docs/BACKPORT.md | 21 +++++++++ docs/RELEASE.md | 12 +++-- docs/images/backport-ex2.png | Bin 0 -> 101035 bytes 5 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/command-backport.yml create mode 100644 docs/BACKPORT.md create mode 100644 docs/images/backport-ex2.png diff --git a/.github/workflows/check-semver.yml b/.github/workflows/check-semver.yml index 4d521db90a4a..15eb32f4062c 100644 --- a/.github/workflows/check-semver.yml +++ b/.github/workflows/check-semver.yml @@ -45,7 +45,7 @@ jobs: as to not impact downstream teams that rely on the stability of it. Some things to consider: - Backports are only for 'patch' or 'minor' changes. No 'major' or other breaking change. - Should be a legit *fix* for some bug, not adding tons of new features. - - Must either be already audited or trivial (not sure audit). + - Must either be already audited or not need an audit.
Emergency Bypass

diff --git a/.github/workflows/command-backport.yml b/.github/workflows/command-backport.yml new file mode 100644 index 000000000000..4c63297efc18 --- /dev/null +++ b/.github/workflows/command-backport.yml @@ -0,0 +1,62 @@ +name: Backport into stable + +on: + # This trigger can be problematic, see: https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ + # In our case it is fine since we only run it on merged Pull Requests and do not execute any of the repo code itself. + pull_request_target: + types: [ closed, labeled ] + +permissions: + contents: write # so it can comment + pull-requests: write # so it can create pull requests + +jobs: + backport: + name: Backport pull request + runs-on: ubuntu-latest + + # The 'github.event.pull_request.merged' ensures that it got into master: + if: > + ( !startsWith(github.event.pull_request.base.ref, 'stable') ) && + ( + github.event_name == 'pull_request_target' && + github.event.pull_request.merged && + github.event.pull_request.base.ref == 'master' && + contains(github.event.pull_request.labels.*.name, 'A4-needs-backport') + ) + steps: + - uses: actions/checkout@v4 + + - name: Create backport pull requests + uses: korthout/backport-action@v3 + id: backport + with: + target_branches: stable2407 + merge_commits: skip + github_token: ${{ secrets.GITHUB_TOKEN }} + pull_description: | + Backport #${pull_number} into `${target_branch}` (cc @${pull_author}). + + + pull_title: | + [${target_branch}] Backport #${pull_number} + + - name: Label Backports + if: ${{ steps.backport.outputs.created_pull_numbers != '' }} + uses: actions/github-script@v7 + with: + script: | + const pullNumbers = '${{ steps.backport.outputs.created_pull_numbers }}'.split(' '); + + for (const pullNumber of pullNumbers) { + await github.rest.issues.addLabels({ + issue_number: parseInt(pullNumber), + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['A3-backport'] + }); + console.log(`Added A3-backport label to PR #${pullNumber}`); + } diff --git a/docs/BACKPORT.md b/docs/BACKPORT.md new file mode 100644 index 000000000000..0b4a97e6f667 --- /dev/null +++ b/docs/BACKPORT.md @@ -0,0 +1,21 @@ +# Backporting + +This document explains how to backport a merged PR from `master` to one of the `stable*` branches. +Backports should only be used to fix bugs or security issues - never to introduce new features. + +## Steps + +1. Fix a bug through a PR that targets `master`. +2. Add label `A4-needs-backport` to the PR. +3. Merge the PR into `master`. +4. Wait for the bot to open the backport PR. +5. Ensure the change is audited or does not need audit. +6. Merge the backport PR. + +The label can also be added after the PR is merged. + +## Example + +For example here where the dev triggered the process by adding the label after merging: + +![backport](./images/backport-ex2.png) diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 653e6a2a3e92..bea367411359 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -55,9 +55,10 @@ The Westend testnet will be updated to a new runtime every two weeks with the la **From `master` to `stable`** -Backports in this direction can be anything that is audited and either a `minor` or a `patch` bump. [Security -fixes](#bug-and-security-fix) should be prioritized over additions or improvements. Crates that are declared as internal -API can also have `major` version bumps through backports. +Backports in this direction can be anything that is audited and either a `minor` or a `patch` bump. +See [BACKPORT.md](./BACKPORT.md) for more explanation. [Security fixes](#bug-and-security-fix) +should be prioritized over additions or improvements. Crates that are declared as internal API can +also have `major` version bumps through backports. **From `stable` to `master`** @@ -164,5 +165,6 @@ Describes how developers should merge bug and security fixes. 2. The Pull Request is marked as priority fix. 3. Audit happens with priority. 4. It is merged into `master`. -5. It is automatically back-ported to `stable`. -6. The fix will be released in the next *Stable* release. In urgent cases, a release can happen earlier. +5. Dev adds the `A4-needs-backport` label. +6. It is automatically back-ported to `stable`. +7. The fix will be released in the next *Stable* release. In urgent cases, a release can happen earlier. diff --git a/docs/images/backport-ex2.png b/docs/images/backport-ex2.png new file mode 100644 index 0000000000000000000000000000000000000000..97ccf6b00fb91c1691f1dd4eaf69c626ed6be741 GIT binary patch literal 101035 zcmZU51y~%();4az10e*r1lQmm++lDTT!O>k?oM#`;O_1kV6fos!7aGMKfCwt-ret? zr~8?juBua2r>jrZdEe6$rYJ9ghVlUg3JMBMN>UU61qI6k1qJgH2?26N10Vhu6cn_M zxrm6Ol!yqaBFNU%+{y$BiaORvU*AxQo_ff@Kwp1ojE)8c4nNu>S?k>jU;`jWgIH6yw1HKGx^A2eV?4BF@Cx`pb`z!3;;yL z`(2Tc&Pg*;(?X=qK-I#6Wl)5UQAoA(k@l%c+YtNZL#pw77o;$6-c_f?keZP`!o@Je zWW=Vx7_URsI>p8GPsc8_a2E_Ka{B5_#HZnXK+V7R)d=oM?FsG4==s)@(i7JM^PXd2 zHce=Dmkozmg%yXzm^Gixm^p!&nwbkm4COuOJ;2XOA($`N7=a&wh;%u$2P#xf$RLu~ z8L4K5NGLDePi<8+)TGNuf1wU(zug6iMb(S+l>@f#d%b^uuBdA$tG5x2JR669bxRW) zbG7{A>#GA7ENt6H*uM3n*Voszm)F;I6I9gEruXmBJz=5Jf((=TK)PuVb*q_ZNSVsX zLD53eNKnur=1_2u6g1@g068IdCN3BX5%P@p{r$$|YV4fFGlg2KuoQc{p_ zWh0P@iH*ZoTgODZQ5cA*Idc^aM-4ey9wS?8MgwD8LlZ_bwSJg)rYf0p2Zr2n|gL{9o=5l2gYat%2}QW0B_2`L97Gb1y(017E7DIdt#lm{Rx z{@?15U;N}>9Ubj>n3!B#To_&07;QmjOf1~o+)T`@OsuR7kP-|IZZ?hvt_(H~6#o(O zmmE9Z*h+9|1!y5DCB9 zB)|mZ|CXGS+b8hvPEf|2At_D{yF@2&ivFv2ltN-5UcPXW|2#<7v2t?b&xmPkL;s!O z8;<_??_R|Sby5h4eOl)StYQ9Z95GP1L^uEL6{(-6gm1Wfr$>G}*}oJ)>WArrHB+gq zp5?Vk<@K<{UbZ9n`XE?U`oU+yYpO#+0eExp>4k12nc@1dy`BE*6p=(qXAuNCUD3oB za8=Y*^AbBeoGq$~?6_pCs_L*hFm&4;{-l${1T2h1#plpTYx+txTdJP?`?npp(_wx~!;91fSztuL*{4thJ2`4kerq78*hl7~j%G;El(3w7D!{XdaP~kpb%D`Abw< zO8XP5NMmD_K&Qu=+|52!$4fPG9M&3UiR^SHTVUys@zt@y`H_J)yWlncwsyt||G#J3 zeT46NxP0n@MhT#yv=HmvyIuA~#?@BMJ=J23^Kfb&2Cas7sLfMTQ|&e`q8 z>CS}(1+DaRR}ZEao1e1HIc{JE!g1r(Dwg-V*1T-vutaQ=9666^)l10ZJD+vY^eIkM zsZBn~+M)VzUbQO`-!F??JlamzCDhmo*|(kbgdDeBxo3R1+QI-|e0nP|C9T}}Wqu>d z{rX5oQ86ZgLH#st&8q|989W%yyH)w4ytuo2qtOBuF%}tx_&SYPZeATB zlTLo9j(S_=OWAsHiRa}`QJhU#o@DITFgkUC5$-iB96F`A@F>n4jo|;z(F+>rF5FB{ zaNpT-IGwgy;Y3E>grNOaprgey_^j2FzeYtiQlTin&{W;g{r2ROg5a$`F?~81`|Vpw z0Rdg<#PFH~64YX!Oct4ktK-Uw&xQ0!xU?z>wnZ=bi680&$)KGw6886VlP+MXE_deF zV)nA#q(6GUq|#V0WV=3SS`XJa7bmir$;$u$wF&f^Qx@C%%QngHoz7mL9{X-`q1#>t zYZC#@b_T1RNpb|kL2vM28Y@mesI?o@cp<%e--ZJSXUL-a^|R;sM#P=J^{Wy-i)qFz z=hm#5@%*?o==tve)B^}8V?#AABY1DuaHka%vbY$1tk+U1%P&@3|EHRk^J>UcC&whJb1+X`!Mh*NH3&V7P}81l$E|no%QNYA>nf|W}}yxx(RKR z{zW!3Nx?cAK^vQ0I&lnYc^av#id@m`onHQO= zg{Bo+7i$tEGRW-H=qS1v7y3m1jbhkeGCOTz97AHt^)$UUig+87n1v@ zX$80)7~lrN*N0F|1o!lllK^s~@+QO^6IDlWVpVa(M=B~!lGNI$Z#1Kp>^kC#m!~SoZfo^+)eNnBB3qDYmB(c$_g4;TLRxCq zM0e@Ce)z=#>qm6j)7;vIqad^F@!Ve<-AQBV1?snzak&WJW4;8T3=d5<9XMT10A1EC z)l8mSomgGF<&xq8VC_-~oG+ocy`KytCyH4#kE&@mq_eO?zd1Ay2#0mpZF(lPZi7{S zE`PF~7{H;{X%0;Yr>Wp{v3aD6h%*8k8ygQb>3N&B#LrbOcN1Q?Bb$uqm@RGvX|Ald zR|djM{bj8$(ENPW9hn2xi|yYM&HLS<+rsb7mIAfiZw@Q`4=r6iv>HLxU%J7*T#4;C zqx*S4e%s>$m{)C-a@v>hun;ME`HQk}9;YMCy)gh7)xQrooM8J=yKOVvIQjfmM@2El zu^Tw$@31#M-1T~ok;-OS_`;j7lYER#fM;gi`TWCjuJsa}8}z0ExI@Qbc^ z3p+h?K@y)IM{7Ze77q&t%I9+|M$a@AI@6KmdTrqHRvqHuV+^~XOI4nAyF3B-xeQzx zynA<^k@&yXqhD+=(A%xS@00QC_RQOtXUoqR`hthbs=BBkh7asdSjnR2c@x(D0v^%4 zNk^l`WNLpZbv*G7Fe&I}(cs+>zwY;WH41M9Zlz7|y~crRXk!b|jnS8@y)#!>OcTQK z?5T0z|LVMxo`dE_7&&UPn!cXM=2Sn$-?O{#jr#dhakVolv(31KU@(W2)MK@_K&JYj zAqAQ$0m;YsB#j+xJ@);w7i~0?RCiDkwELWX@*y=q-%8N#%(ZTL=VD&35cx%CIBmAf z@gpO(b;L_^@hDd^zoqgI{h$kRf!9OJiqC!S%ohF3GZg+l< zDyYNqb#x=ODkXr6mp}y-!=O@493;vnF3hTxPOeZqPULPlnOq)ii9{MD*y%~ohLthYj?nBUt6ZR4d z(4-4F{WX$fjp_tt`llk>UTqo6gv8mOgny@coZNrfNeG{OaA-N4#?NIdYvE&YD3Zc7 z+;ZScY#=F9tf|FKuXCJ)(Q-JpYnK1-7G$2<&sWLu_$h++XgG4w0jR~z9NP9AWgnN6 z_R+Q_<=jW2Aj)R7u%d&f10DWrcTPGyO$7gNrpq_=N*xSif|M;n)DHxl=Co{k3<<)V z*Rd2K7&6d_2jF-dI%gJdgq;|K_iE(oFeYaK(SoW@w~1YiHukFhI|BK@;0&;eDCYK5 zfu&307hd@RnfwD(O=VS{-IhAD7QWYky)65dbvLtU0wT8Eg)XxRnKtHWqyu8 zgCn_QU)v;4O5(s%dBX#r6b@Occ;g;9RkqHDWx5oAmMV}9-)h@N0NGU3c5KSlM}9Dz(J$=ysp_@ zuW&-Z?AKqvzk_@0bUQPj8paFZkI>-)yxsQ4y+dYc^sS)Zx>>+BXXz2OiUw9+_Z(l(=ha)D!Xe$Wk*Y3p8gAEdMM*3fN3XhF z9w!cjBSlxmN;jhr!1J}b-l3Ob{kJc^w@{l)g9+IB`LEr=nhAtgU8;2ILvsw;`Q~+| zrLUY*Vm-I8CQ7YdGZnikmSJH~)ZB`qb5+`OW$G1ihlgI06{;wpyco}m0rjcFhaX9* zQ#_02@aLba1a1QxZhL%QB<9aw(wRow(yuYW!@re`uG#f`HfhS*tLPY(>zi z4(!e#5)LXgxwD(tm}wta9}ZTqEwb&A*z&E-mlJLF3D&Q@Eyb{aHhH3sl{S~?amHXg ztq)?KSXh+Krr}v%h09~ylxUrww>J@Yn)G~X!iBi~3Xo)|9jZF+of~hogRJRr>C|c) zM>p1&HtS*FL21%xt7RT|*@>AYdM)sMZ&^oMN^Xk#rtDdOueXW2SqJ%SiB= zT2S?#qVMU9&vGObsq6am@qBihQr{xnGa?!_*tt5G=0W4q!TxsDQPMRo6k=T+wDmZ5 zRCU;=$ehcz4h`E>-pOx^$>&26dY#Jd^L;l^^o`>9W>hHN_LqmNwqY<|@XV64x(M~e ztJj4&5dF2=tI~%gB(e9H(0iofV#0@eWiCfUxP*lzJH(Qib+keyCgr`yi#w5(6_Nt`D+6X z)gAHn%q&xTbp#GYvHIBGLnyeuk)bl#oxN;ifrY~4$CX|M`TOG6&(AI+wfaQAt1mVO zvo)D02iThTDtYS?8oLG&nJz!0{AzD;_;rTCPs)&Zy@6G*u$lV7FX-!1WNOld@r{R; z|9wKXcTKswVqBKjVeUZBbj$dlquvKT>g6_H?~I3zCxLCwGFKVxNDYV&r}pZ4aBo&e z&CLaU&QYfRY@WGX=AviGUz9l6v~FM@KxNh+lU|p3v4fXA^5XS)Vu7za%!HQd$V_vAaQ@eIuX)6o$=IdW*dLov;EA;%#GqpRobmZzs@F-rq^C)E-) zpzNHmmIEkvVEey*lbtmdl19Y13=`kpg_Qn{_hX|q?P*^ubg5TJ)Wz6*e7@#o1Pg(K z3Vpa&(wgpM@*#Yp&+A^^4RG#3W}>aLP*td+n|-8TwfkYqn$nN|l0YX_2e};9wddnn zUx1LK@e-JZnjpZIqWX)|*^Gb;&u&2k-kTQ=+cioIzF$bBF8%JPb1S1Qr%A4EMf_y_ z6h0o*+J^@)@PsU1Zg?{E5G=4US6FP?-F>3^_%Xrb`^!JTA!l8dg0nk3wfl1S2WE7S zL^!Qvu(sgB$X33^fpKe}=fo2M*&3HMPhe|_@jFb9@;3>}X7IBaMoD%KpA8?v7H{ey z-d}~2YbOBn`*CyJ_kf2oF47Rto3d!Sd#9y|O6QRk?z#epd7Fn6H;mX3e@5pYiB39~ z#kGxGGUg`V{0R9r6nitGJ(kdw&)$7td61XceBNA^GBoty%QTB}I^4CxlT%XT)^b3_ zW&C@qDnv?(OEh|Ty3o0L@^-1#B)(w3I|wlxUB@AEVcT$2piZZ_9OV>;NErbcvGsPJ zOfHQhexX_8J!gSDSF>$B+uIfH&hem-{NX?NC>s3 z2t#iLT=iPle<~NPh%b6SBkmUO^IhzB)wQ?3O(Iu^eUAU6kRvVgb2m)-yCW4z`)yRc z`W$CG4$P?zH^gbt_2j&kpbKK7ch3_?owYHjtlS*y{6?_SC5!EaF@ZSI8VcaD_qvjz zQ1nJXBQD?WkxmMqWHeTkK0j8RBcZX7(K;^*AF=VHYB@Kd`(-F0yz|B7$l689b@z?4&+GN4#g6Icy|tEb3w8?&OU#?; zv~Agt<-gr~x7__eT0B-;c(!KlFb7m$hkGJ$u2KJJ^D^u@r|r^LbTe&oXkYw(9+JS> z4gN%>H^K*QG~>h2?L?>)yB|M^Au>vj+en`-b?U$H$xPBztW9o7VWzWg&Bdx=QA?Bf z-1#+F>v4M@r+6tBDmY~FEaCVM^iuN-$QX}Fn}HXS^<*D@NDDv(8Lcd^=$fF_&2P3x zB`3RMYs_Jq!I~frlgpNx_uG@PrFEfHwOMXyiN~yT>($U1k2^Kjfq0VpH~^weHuIA_ z#w&eGj{D?j>7VOvB*euv!K-qd7qAQYtlJyyX}rD(FObub4nOfVW46Jk}h&)~<)IW3jI3~fx5*<_OnAOOR zOtR@>lh>n|*yjhx1YSS4k`17r&Xcn_sAEn}llE*o+#Cv67B{DQR>xv#KbT zc`FOce$CC*)#ZyMQ;q!jEg0d#&Y-;oxMxJuWz&umyo<~no2)a~dqU(!;inSLM;)eg`uDs;QR zV`FshVAO&R3^j9caPx`!&arugoLf8UR5Fx<191}KjUMU@F`rfQvtrWq;LBs6cNzbhRkY&(_wYE;vQyjS zjmK%*9XjdWWjE{@4C|Kku&Bk31j%DG4f&AJP5*YZ=5m(WaSds&=S%QO5FMyJ3+)$(>iu=RFb zf{dC@BqX{TYy@9-tXJw%4{?E55O;}aLj_s<@dC)_erRU7pSscItlp=D^>llWruESp zsncWeR$n!H50WZz9rdka+&5=WLm(_C8UR-&PpN0V3G;24f}RP zUOsr7wVRxjsDS_Vgku1(da<%1d5(y~?TxM|7Py3{Pe0}MXC_l+i%fU=_X%!NF zG+3%yOps$1CJ}CAr;xPUpnvr^#6QXu<+@qOd&M!fzEsR&(1KyIA>-2Z>k4NsBh(Y&d^F7VtncRpFE8SG0v#dkm&) z$9Gp~m-zk{x91_JK+?>Ku`%aV4a7kTL=4li!q5jDA@oI@d#fN;mn$MJQCby84IpQQ z9-V{t>nuXp zG1FY*UgxPD9iXAngo%W1f_lnr+x6-fHK19vCXaEOJ0Xxt3T$moh92nKnS3mre-^MF{H(N28KX>6LJyORr03s+}Guz@S|jDYeb{WT}L8u;lh& z#x^IB(*fNu&@L;s5$ylFccjWR4EP1=8l}=}Q-=^To_gSE=Gp z!f6Mhi8#HCPKTTcp`-n15qLk-jLiC+{&rEy;XnoZIi=rW5r^{63N^s!2=}36U#I7s zx>*sNL~MyS>@hKdU>6sesryN%r~x;HBKFm7GMn$WEDlQ-B7V@y!@!L6e(QMLhajh? zz_xtPAQX3B1o$Dws;-AxZ(jaWXhw)09e?^1q9V*FYDKad-=tF^oH!lL&ZoOpVZ6i~ zxBx*Re-s_j(MUlp${8> zMMu-&q118O*>8ZVsBt0ND6`e6j#{~6=F;bN)#X_A(24rJt3WktgZVP!vORUSt6v## zF_3(|)QJEnh(p`&xVYV_h`eTPlpwz+{k?t7DY_3k(Cq5?4U@S^f!j7hD|MW}Mu()h zW(aPL2Zn?xiw970*KU4c`M8a7k>@UbfzC{<$f^b9p|$$gVukAXH(~DPbbOlcoh0Is zoqblVK=R>kKw@z>;|Z_Rbow2Ar5YGJ=r0t<7j6Y>DDmh6Ex*HV{mMc`MEm6&!4mn_ z>6etmV9->H7eRClHy263$C-meFD@>GWNsIxTS9(NAgSVtz@1p;La^cI99yQN6bF1t zWY>+gHsE-I?OMGQgdSlGlL{-AmoeNLa5`?beQLIvgKhU2L_!#AeqlBFUW<`4W}pAr z`?5jZyG+BMb@me=^vI%wGvu81JW0ekqGIrI`a8sujF6~6;bc|p8@_D782u7V! zM)``*F=S5G<5+FDj@EMo`;$=s>b1bBb zNcSOZ5RyOsZQ;CL)iy(;&0=nyo59b-_zk}1SJHsFMr)tpR(4CiaUS0J5H4&DN9N|h zqYQq5zLxS!4*!+((&TUNkQj56F?id|zVn$5$087z*b9~=?jWAZxRIp<+0GVzgMhzs zbc6(RRds8y@p#uB+K8t$nh{9=)dEgeavuEK>KRi4*lI43s2 z4#X-bC*`SKN7I|8Ay(#!y>peoZ9uGjXOw|X`B!_*Qr3{g=?dOtr?ub$1RGH30C(j| zv+NhNJy7)xu!t3x{0GY7E~9EN``!^K{eM&M0@jaD-SH`COMddaNgzuCW*lyXhG%@I zS?<5s?;nnh-Sykw6GgucyKL8>|Kh6tv*-&6e1y=-B!W6?AO4l0Qw9ghbYiIL-bCB` zC?rU>`9)WC#9gwR+_xXUFq*1}Qv0#xpA3A=D-xV+r2LD>2=D*l$eRIh`>oCeLzk_8 zyIB88Bu7HwX37L7;r>l6{D+^{p@A^-l{jv?$^RSWACqAt4#Ji@4F9Y({L4W8hjj9z z6Y~upPb8l>iu-FL^It)%5J8OiT57WYP{kkGD1cD(Er4Hc!2csiMjRrDN@OJbZ)f^H zlduv8nwCdBoUcme{|FjJ3e+i0iwGW+n%%N4@N|Tw(JU^-L8qf4gYRXW-GQlGo4KOo zPIr`z(pw;Vl&?x8`h*8A=?{ROm*@C5<$po(=Oo8>b2HirpGht>36Z?UKDn%2@>H5{ ze0yoK<5y+!n(B1F;#7S+~PV_a$Ao%A! z5`ww^CX_|ZW2b!NwE9ZfXm|4TV=r!X;(J}X@zUJj!MO7t~rNR2h#YG>osY zr%KB$a6?WVw1MD_^MBOVP8DMrF&>IQ0vH}1VDmFE+ghq=DfTOh>zQs zHd<_1x&yLfEJXxAEaPc4o{%yuH%~~m+Me0m-;7MOe}#_t&zy1<>9!R^q{;V6bQXz_ zSthf%Q5){}A&TKA!1k;lUr$0*D9l7}A0)uXrGj|g@nV;I&DJ`8Fz9_>-CIEpD0b#+ zr4`5PZ4&6m>qj*OWbkjK+oFKR5XKSovces0^2}enI{UfG?^5xO*mU_zMG=>4$dAf75o?kj{qyvm8pC=WO0m zb(}_=m$?5nHiXHJ0VefQ$t1p`Mm)`z3mL!Yrihk$?de;^MJ|43n6Uyqi_1Bt)2fuy zS1h}L7CRwZNelQYo%?Xc=w%o8(m`FuWgbINC5Qd=uC{HL%m)r@MkS>nrgfqg^N}6P z?pcR|pV+GKK5n!E7fk9kah-hS9H%cA^Oi` za?rQ~W4(78T=h?V`t|D<-8>RW)zb~#gp`FvsX&=$0eDISZ5*gs7DX&TY1@8RuR1qv z!Z*8p&^{9DO4@qPoG3{uJYJrCSVN;r;uuR0?L|sSDcbqs=?_`N7mi9nz@K8;AmE-V zKv=ZpBerH<8DaKihB^b|l2s&aGMPlt>vGvePb^h_Z*jTTM-i%mg=gL5h&{c&C#EkO zf0|u54(KkN%!^61r&c-*hsdL)Ua3!Bl@#18Ksuh{zRLYTT-uyOLY0&Qv0o0Wes(a; zyZQV22%WJXGTy0Fit#pDCjwXssJOU91zeACozCF%&$`I~-vPM8LqjpDbV<_NN7_S` zM9aqGXI)@xm({9ypO+Vc9(vEN^bnKf7l(W77Q2`BdQ}^lbdK`G59~*h8X74XkezC0 zHlNaXCV!cd)8U+m#k^(lvOTSUZlT<>coY#o`3bK}Ji@oCiQvnzW73bj8&fCL0^15I%(9i79`*)p z)XtWj3H2OX8h-J&T5BF(GqxluPkdJV*nZi1Mf7=Q>8#x!wCa8n3sx$(R365@8=4El-KCdwVnOZWU#G@mLjAkK+~FtZnrOoA3!h0os)?^4=Mp-&4+TvQNG_d-Di2#a#Vltr(n|M~B?YGs1bwO=6HC^oS-EnH^Qki8 zU<{PvvK;NdP)Jh2Q)8-WOpTL_IJ+y_3DaQeiV}_R3}X?`E1JnV6zszV1=0ZNG^XO2 zjLR^1uk4-1uKJHFEp`K&K|2NOIR@@;ttR!7s2M9C@_9ebR+Sa3CT;*U+H_$D4l-mP zH+fHZ-m5pZXQ(d=dh$y7AdETjGXzkCn|r@sSS}FynxJ1DC8$T)tY|OQ20;}*w6n!c zl%*7W)+==Ys?nhJxOyM9EDx-*VT3W#1<^Lt;*HhnlJaQ{C>NBNdDOh-KxJ_r%Br8- zP@3;4fIBA;w8W&HUb7ocN5Y-emDi~rZ;x+w>a9%9@z>kTl2xj{chV*Do;Y9gbK2gZ zN=eJkm;kRCo)97ld38He?@BWG>~%`jy5w80_NlVH?xJ37%9e9tXKWWlJ0Fhotme1T z|Frc?a8Ce7>30_s@cMoL8`_y~G2wS*c(DxPgU5(E2~MA=Mna^GC-u2~z~`{b&UVh6 z%`rbo@n4KJ!zw-$A|#SmlgfEt`Hu?CGKc~}fvz>6IOCgQJ zwg7P&5}C#`PA=kCtg(20Y%x=shuD%hOG*G}(Q@kW(qGpN`$ktbe@@ZtvrZgFk3&Tz zGcdG)m1ol1X8YCUQ-6if{bHL(GVxFJj%yCL+i%JBTM+KMkBSBo?c~*wpPz3fjb5oK z+afWL5SDmTMh0$L@lI>GgE|Nlr`y$OLhxL^F&=Drrs!q2cT=%=Xr7-yMh#Z1 z@4>lcHyq8p=t-|KZVA2=AC`}nR2j>1XAbvcM_54iNFLtICQh2-6sSUIWFUH$EV$=? zILZSVOke}WQrDQIT-^$E%<|2T#$1Qm4HO&I7in!!Zi0Pfg;B< zyaGir)$9+b+aHRVM5sRas(cXC!@jLbK$2)Xw=p#9brXXp|zEfDdU=ZWu{ix&B^?iVL_&dpIT`4l1vV@_nyUdN( zk{<2KO&x%53qD-AwKkmS$-O>5W34J%W~KXf)=B#4C_|TDYAabzadd=s_iaUMjwkW` z&g;xIp4MZAI?DV31=SLNj&qqgtf~N&cl7b=;k#9Nmx=b?eUf}%i?<0$(k^3@)(e+C zXsw6-hu(&dT?I=zD)aWvmrUTp%XWcx#PdS5QR#V~2<5Au)#P8!3mqJ+mht1<#|>f; z=j{e|hOT@TqxRav&&c|J{R-M()8*li_fqHTl+dcTDcbH~Ufd|LHRwfJPY!JeFbr7t z|G;kf<|A#6%66UHAE3P7lWaGWSdpDZi4Kb}HjIWz&Xo}KVx536oxnnfk)mt$glet7 z!+8m|>Ss}&NNTdK_MJO2i=PFd8GI?^1KTKCCm)^--NH(6Xpi=?X4P_zv_!!2s~S9k%)UcW%4u6#8H!4f(2KkKsJ&2nxf3*{z^e!2kRL1-W+VqkJKxK zQ+F;-t6ugU(VwL2wvhPq_a`*zdlr8`Muf&{f0c3_u{Irp-A;}3twY3?I02Wl(@d?& z9`5S*&v&cy4_CZr9vZn3@8T6rR{db${BTKuJ+zRRPn9wyW$iFK^cQ`6eBKvm;NCAL zfw>*vbDjP32mX}78uwD&C#b$!SU(da!Bb&)|N7jE#iO&YM=NJ6u*$E?NUZa7$&Bm$B(L7=UEgWaX4kdM<4k1 z?->>M(@N_Gvq|KW7LA%x1+=OS1=c5}iS7i_?+w33c7uW($Qll3=GESBn+6NNDR&cZ z-~6f|gz0mU4~eKz5oFWoX^%~n=6GbOghMlX%|bJ|Y0#2mx*`&x zo)j9+V0`w;tAQPH854ELrE6JA`qo`o+c9eVu4*mXr`PC6O0=c0_BEm>uCSbaM_e|p z_^9;_!hkqRsr!u&w~i(&^G_E(CWq0^z**bd@e{4d#Tx4fwJ)*VFotLGs~*=5BmFbC zC+gYAA6fJVEN3h8WS@8GypRG`K7v`(<3-i1s}S!L0nEirvnTcy`#r1(RC_;XJSb0A z>}kpp$WYgqtn_kEm%o#Ws(k$@CDBB?Q<<8d)meP?MT)28H(FTRJq68_eoThO?^01t zz^Sja@XDnD269+%cI21t5E*N}npG~idXX_TyI+zFAD8pL%fP^lN5-4s# zCnp+oRABrV0#bAw)g%`?yqN~i1ZDc@tQ-t{_{bh3Y@HoG>?d?S{LN~$URaGDm|x}# z8CFjoEs3v;;F_m9CWtGYO+;6@zrK&z~K*9oHPdcu3$e9M-ld3 z2PuJ|xw5AecBk+JQ^G{iD(2gXo}ik8`MEF+`*VL>2=+tJNxoFf^K90k#m%53lO4fxPOGWq>)N&J#U zFpGXmXHyyxD)#+@4cTTue{&fb_F`(9ZF&3JoS9Wt|{DqfAlO)Skd+CA7|5 ze{1y%8}?6?V!5ik34{W_-iLOB;?r_{i+f<;POYl5^iAKx$b={czFep!#mxL1dr(o6|nCH-CmXN^(10 z*h7*wA6> zo>};_lB%Z~3WSQmo=Lmao%tC{Wr*KR5Pit^Tt?%S>AIMco!aPiY<(LM^narZ-;@_G zb<|SxR0HxeKv%G4x1XADd5NlTr4(fCKHv~O318KXN9Of%lxM6VC8YL?5Y$s`t?1R& ze+j)LFLVC+F0~)l(viJH8ac4r0zN5Jf@XRTm%Cw0xd3i18LnY{DAj5G$iV+Orc{;F~x|&5&Z>^oE`|6p!W1UI-PP)ov3*&|h4N zWsj7jAF1+9Pc(yR4c4|_w{u&H--neXeQx1MIon<3mO8us?~I%Qy?FIC}IAdn~`VKeF+ z%e?450y07x-6%rOd%f`e;IX8MaIy&DZL`(q{RT*3kEHdzs4G^c$8E9-E8Zxvx5>l? zM=fF{Ec1SE!cnU`4LT||$?UA%d#=>Fw|8Nvr#jew$IW^|q81xDQ=ZL_8h#1jW1;!} z5#!>E{z>l!Frrn0VjloxJCj z>GWQSEm>n#NcfTEWNQa$B}V1Y{u_p6$DeZ&Z@1%@AGhxR0D~Y%A;d=-|40;Xh!v&j ztY?dg@{P`K>e6xj#NXwl+j=biST{LmTYm|I>AR7|+-R*P{7jg*fhOl(xvU3USy|-$ zYU4b{8!WgQjygf7QLTBalj>c4pQlZnFh*SRiL3Rfv4~u6x3r$6JjzC`6vFkr6gLLI z6jM1&WtMRZNV~Z5sSNA)y6k|AK9qSfF0FT-*WRhWN|T~^Mzqm-i-XxUDbCTI-%5*D zI?9Y~Wm(NcuMwhWGnwK?X1f9I_km|CFUbOPX5IR@g0;eFO;%-FqS9YE(V82{nBOIl zQNNK4hSF?uFTj<_ce(qGvBIRNm;Dm3b9*3o;F{G58$1NSjfbU6tfN<}!45U|ZCgrZ zD=IK7pjE?CaH@cK7<8nWw#`b~O?FHbGrrw=ep?|ukp6L6ym_lLZKGx{FOeX2+9st( zU2vYCV%ZZHU}lCx?^AU!;7?YpHUml^a)~N_T z+r`osd%r1IXA_>#av+y^3tF~fbxH{2 zANd^T;PcEFMB5a5w?kriTV0)!R=O6j1kd2S(I$=jAUtjx0N+ohNUl8Vw2GW%EqY&= z=JR#ff@f`TnoWYA=G>z}75QCB#4zOyr5~U)#+=Du9i?I@D6z7@sBQ&FFwA(2%#<;L zYwccU@hDt@!~?-X2;MT;akIUZs~ndgf3tnKd)Qp&mG$thlhZ&`)bkVl9i~Q9O^5dh zt@^C}pYPWEqJ#1NHWaP5u$i0-oBSCmfr$EZWjROXO$&La^De%*eS8|b8u-Tw6JzU4 zj@4F+BexHa++kxhASqQevpy$8`kilB`@GF+VnyrBFH|DBz?AQZ0#)Tlk&&HWV;7lU z%cG`+Q7r2*q)-lB=;_%6xIStpXC^ZmNlQoD7f|xCYJg`^lF6lMzO;Gb+FeuHk%%B< z{utAeRM{l2RU43S1O$9V%^~wCE!PO6eS<+Skb)+0EOGY2X-Vf-;xaK#1Ss+rNfgyD zTDwjL0ey2}@wnRB@X${e8 zN--pELa$(&eL0mm0Cnmbs_uKidMY3xi_MDJGeOvMF%GkXxN`(~yNhkg>-HLt^;Z&0 zYf4e8;s(KvvY^$>2~Q#1$zlOc5ACcammc{D2e{-F2>un8jfBlk`iKRmM%-^B^iknS zgGA&B1x|X>B{edW7cNHyCl+kxM~ zJG(Mb$(Vx8YmZ={=k(pd$~#MKJx)4VR8bZ?M&4g>XAIXXFUS3(2< zUXS*ut#9T#m1v{0i&k1y3?t6Y)JB{vn5*JgBJZ?V6OM2UMWZIX$mYYN3=EUz_)Bmn zWNX)|$}UfwTC^qj(ERU5(6WnxP4%ku{9<%4W^+DOz*qfMDFvRg)d58eY!)GCo-U(K z5dIPA_kzm0sLkfatl2iHgU}K+3)ALnG1W7A+`*UK>I|AUqkc>w$O$;q(o3y{^j4S} zV|Hi?{?19K&$Uw{{taK?l6j6DZby~y3|lZ|0y#Do`@#{!^d9{B(OtDqvMalibDoH& z=UO5n89MP?4jk&9^AfhdsPZD|9A8Ld*2Uv(H6sq9xt?lAX~;CMlb0aFz17;GQ^Tfo z|NaM;AmsfC69>rmv8d|N3N@`CElFPWbfj^88htx0_&woetz!8j`K^zZ)Zl(QyK_@u zFkVcT4^S+M+NCan6b*Hr9_=S_13!=DrU}9%pd}NM&QIwC z2I>j4_F@hF-log_IDpu|QwDD$ZAjB6sS)S_$5C`_93dr(;y7YbVnlz}EU8!i;`wyC z+ZOpe>3t*f5`55&Ybg!`P%-Ot&lq~DVD**FY>$9=T4w&s9DAZQBCJevC0X%RuB)#5_9=UpWA^~hyBkZ|q#Z+T{#kfDwq_?+ zcCOnko7fEKcAWw3ZZu3-JR;j2F`yd|Zr){<5kmOtU?c!F2K12~so=?F+Llh7Nf0J6 z_fqhU)kIniM%#@@&nj@J-;?#wrmW3hPED9KV0(D*Cya!JDLA!kAesp4NocmvM&2tq z_kB~KfdRMYy{P+08w6uRy%`T$jab!1Po0Z#LPI9{k}GX46H^nK8Xtp;fvPOO?eTLj z8w)76+3HoGQMarT=L-$4RRhrR6nfLI@;i9os~_=DWB|r9roXk(rl&(pU9ZUNt!t7o z-1vMC#NhC`6+HbiSqItBHT}8Wv!L2Y7Bd?h4icc_`S=mho(r>Gc~ITWYsTgX2a>9k z%%?Q@I?t)RaW#o!-65O{W2`PB{gr}bt!utqYb16eNjYE-HT8G3i20W}j-eNZDB-0m zmnh_yn8lNsSQQhZ*1NvW<055$L1TwHy}{Czkw1)Db+u5caI5Z?XEX4E( zSd?jt3%KN)-8szy0w*A&O-N~1WNkMOXk&GA^4fFHp=?zKIcjf62je3!4QTeT6!k1Y z$plyVzfW}3n-EENAme5|FR=OKt#}7Pg&1f)IZO5!;u6y2H^fl@PLrgxkWrPu$Wt&7L_PR{aBUgcQqI&rF6F(E)#r3GWl88diY}{Hb<_Ek1&VAqw=~^SvM8! z7C1+LLPB&9NGJk*X{Bn84bUO#k)3X9XqR&b7w4!nm$Cayc+OD>l$3j(woKkcR0i5D zhuvTF?MjRltTM7#hJ5}$pej2${Pq(ZO~6UOR}H=2+hHyagi3zJ66htGHU%+vtXr;?;=)T-qe;O*68rQPt|c^7)kru<5o)$y$4=efYg9N|d;>YjD71dX zb>wl^mpU7x#IWQ5OLeN2HiWt;bucC(mBv^gvHt$3DK_&jHd;N` z-(;PhTm0HP@UCn$TDyX8E^2vetbWOU?%USdoc$+b*Ub)jGnhzazTi+~irU%tD7~v} zy}!aZap8joH|?ZL>*EY};&XG+RP zCz@;beS{{@*?&XZom0|#G;zRXDbF9k2b(Q_2WnLHaLRuJsQ*2>3lphg;LrV>p z5Z#iZtIZ&YW~BTW9rtxh6IF!m;GTwmS`us{K)6@#V^fE$lxVb7Bt+W~ks&B2{?IP~ zAc6*%H@_9Z9|efb?={u>hi?-7a}M3Vr2^;!6@ z$mZV!4e&L1xyg%6obkVG72xbZ{0xX^@EZ5Ovebc|lefjioF(ul-2!DDlrGdgq`}p{ zX&L`LdQ$KU_q&>K|5sP{Z`m$U7bso$bJ(8Uf7T}`yZ16aVNquzZZLnAi{A&7F8Vrh z$ms80_mAIsCdg!AE%oC4AOC_l3ZQf;x(HFyMgPp`Iv1!SI+(Zqu4-^lx-vd|M4zMo zWVUp(?{@ zIr-2-ohg8WV=)@n?cE>s$PNc$zhlto2$H-L9!i}q+8!x!6PJ`ERYV*3;Jd!Aw^Ah0 zmk=9(ut)Z~9jZ0kAOFv8SE&sF1W81;`3FOz`H127#&uh--S*riz=ke4j7P;4lQIo@ zB?cZGP0bev6@UC6rog|NyAmG)*t_4QjhKW-%`q<{+MMAU1`K=+1d9BZnE$Uf^@an* z5{bxRD}^!8nv|d>Iw!{e9O~=HATS;uK%gfT#n)d;#JW&MYy2JJsvdHpw}(BF(^ z^A~w3hMx$;QbHG#^|$^rS+oIkP$OSLk<$G2KO6u8SdfgA|4~nq9@$qgRvr!KLjf9; zkeHa5Ky1#)kx58?VPP?r7kRPM#NPIg2gl?hfwrL!q%jo?#Yxr3S~dN#dst()o)7Bd zj(z15tiD!WvLCiw!!1>oL3E{cd{9?hwQn(z3=9mEyodwsW=MXpLbAfWqf(R0J1?3N zpdbef3~u!UsHi5L-A~&s;N1=pjv-Y~4C`@E4;~H$pUXANo8lUwxNmrXjoWmVb0QG) zxc?$6S;(xiQE7JM;Vm@wH^!?ymXi7`)=wF4(rehZwL{K+i^Ahgu{=V0N^R_7@Z!jR z5+u0sZELCntp>%0Kc6_3AO2$Ci}&(y-BY9I!#sa@z=cy?bkqT9DAjB2vdnps!TG!P zImZSbGG^op))~$301OMY?wH=G$qg zdLn1G&<*&l7A)c=Lr|MtiU`19W;|U)~fa_WjeyL9Gk-uX#?l6j41$s zr*QyK^*Fmx@1@e`^K8@oRkm|lDzvV;b2{R6yL8);uB-6_17enU*Sdv+ zd<4LWr4C%x)0RCQ4%gdq&x^&PmWOYB%auSQNi79o*i#D?yePhjnP}8Qb|<}TK2zNf zd&>*1K1x;Un(b%(vC~_AUk4A^(D@SMDWY^^Z@Y&I?L?@{3t4Y$sZAr<+LDl&f@cyMr>e16LiqFX^^pTghtittnJe{Mj z!1ectpOI7YYDiH0usCyaq_dxkxPf{}5f*#NJ# z%H3QW`O!NXs;v7h#`i_pGi0m({=qaW&S7U*vQDZOe*Wbb>0838*WeALh63g7LyA zB!GwKiw;PAP@+{3mLbRdVB7wh_WZns+_LT%Rc(#)9H6f}`yX4J=6~srn)2=fOXai8!hoYEf`maz04dpr{2s2f zv;0~h#3vaqa7|pWAaz4ii_<9G>{m zTxYb^!pfUiq0k=m{b)g%_hsF>+3Oi{X=i=qUmO}EmJ=6qOk>l6!D;FHJG`Fvi{ zQzi~3yIINb`~AF@USqPOBuaD*&V2;hJD8Z49>A)*e65iuuPjfX{XnYN`5@tPvQRj_ z01C;DWW3gWKbldd*zb}#Z$8-7KlCWh%TUFEFM7Lf2U!g9Co^aPG+OQsX^ahqE#~_{ ztS+<}@ESsYXPW{AJ=Llao5j-={da^N>4%h9^L3^&mYBa9Z07b7skKE~ zRwpFyET5A$THkqj4$bTCZ%L;&#swWdy-OMaq=pUe9S7Febd5wa{ zvh7h^E7Pb5dZf21FJqjyQ{J1M+S97fd;+cS=#jyL$47UX=g8V6RzO_%kTh!+iKym< zcGnfEm#6Eg^gBv~aggoJ;1pT-&Wr16(&b_QX)B@klz%LU><5rR)WK!3%KuRMzM$** z>Ei9c&|?-0c-HYEmB_Sv%k^EsS?>*)%57ZQ)6+BXOM>dR;*|Sk89|{6=%wA@l*;7r zW&9&ZS;qH$is`Z8SAl8O>gC4M*-#lnJJHQYmV`H+hwt=HlSSEOgoT1%-{$Djcg8MU zB}X;tp@!zDcJ?A#bmjK|r2@zajS`8}dxu%&>Gm~#7*nT4T|T~F?Vc|s89=69d<+*C z7ff~&M6SKgoED9+RbAldRlYbnSHWJ`P#!dLTAzHJquRT=Hdf|!XCOmC1-k)bBi`^E z$+`&7r^QSOu>vkh9#^iUwkV26;kR=%#Vh#?D^&;;&%H(07LC0xOyJX=i?=J<~2 zCqPeYgWW@`@kw-kvQE~U=lsR}uo0~ghUNtZao(8cWvh~*%h_S?w$tdU?HS0w;iH~{u?5Nmk?_#ZjMHbuWNrZ4qe(| zG~6XrhG4bILC#>?{Z#SJZF1S*{x<;x^{&b zyV!MvgNAelkWmaCuX)c_#`udyBvqf=c~MD4%@iw(dV6Om#DB~-S9Zs>26&xEi)UwWQ+_A8~-_AG? z;MS8DJ5+jl&6?_%+=J2@IV3>9DL z;^Hm-_DbVw0@BbsNNAIGhfvGKmdKGz9!ZTx>!K~+$5l6fJjF0Lt)`@xW(^wxCaL5v znFxsmTW_xyY}zk|Z(f4PQ8^JWoF}FaE|>Fj5o&ub74>NTi)Fe+((&%$Nn&N6t#`JN z`Q+*xOurxDmP?Lpp7D&PbM-eK+ytlY9MRLFQnJr z!sywB`LZAOjnJA{5{Hm3HJT=SKCh;KT{q_IpKWQ~Yc%|&+j8%0QJomyR7P5KMc-@B zya0{v!FPV6K_=PPRByF#}UtMyc)*?Ww~Tw%cBh}1L#CpTNb+v*z!9z&B9sp03U)D%9HUZoNJvPC zxINOlX~<&;T^J;!T6p;_5$yzOFhdd@L84zahNQjFa;&Y>)T1PJ0s;xM1;SX=kl z17=h12Vs|lk>`vkIoyoFpy(s+WgZMyt=j40auaE+W-KR!sCs@SUb1{46_T-^iqb`teE&6*j?<-}|A8P1-?>S| zpt^NUI_)N9ss^nJ1CvxvZ=5n*)_f`77!^B0y zk5ZXgNxRI}FuRlpM_^09dN|&nJGUxRGL-v?A^|@-v$-% zhUl__*|NXvogrVtHWdjN%;ZA@8^=svRfvJ_))=4@^Zd^C2!>-k0R1t9I zW84wF4Q=pq!BYYtU2u0(yt0dK5Rc|1J6|E8#Nl(;vEA1UsNU!k!mOsO zIFel)_M_ShiGbw6hi>F`Cv7bLZhJ4umcQKRbXx0*eLEJTyQ}O?nV%dieo+-%8|Z;I z61V30tKa{I!UaEA`U*fzA^d`_Bu#l}%P=_me0~x95#Jz$)Ag)G%TQ#0QCIflVg?K3 zGs5Rm@Z@8T0MnWU)7C-hRScUg-W0#MrAJNKP)*{=_aN!0o-DZ)ceQjStD&N;h*Cp{ zo&Q77|JWKus!7jf6%LK4%Nfo_uJf5)_^gkX`&G~0s-RZK`FQDz`k4z&Kh!aH`mMu-aF*(z(&rvSOEx^}O7DK1$95apsZY7mhUV-=2;EC&9pW23eS z)CsPmC6vpIIS6e`i@484Hy$I?%&-=+JKscF@%s|#7$AdJ(h3MvKf3kpJ61m%C_wq0 z!IPSfXH3GgO}Lo)I$&fTFohe6O>9Iq9eJ!~SPnrMWHVcD#epbipWw7^vGD;IpCsPX za>+SJO@RXlK6x($LqXm9yGe*fZk@H=1gU%`_hJ_2bUDp?Njz(Q6Tpv0_a0(&tT$Lx zFt1qdMxjs?7(E1DA7FjOM4-3g7<@WwD_EJ~bUCC|>~w6(>+(W`T$QB&7Qnt)C#L_J z;+uyv@YRB+<**Z{DTSe1qh@2F{;m^OgCH%Jfm8bS~Ut}qLd7gAK8-nq?M{`74VX2Do=Y9wNJ;I6P{4x!F&7e zkAEf(W+%guu;ZPYpNj?~pqX%Oe2ET#5KU}d0Ff4Mq)e|=terijenn|t)1Ea{q|@)5 z7?*s6+#0el!Fq=cGww<=&jHm-ijC7fxGJW@-PuW zkYD-~wX>G&sG>F=>Xp!O@2Ahk2empe<{c{0?P;-(NlA$fHgY_d?;1sCTgN5bUN{2E zER~e>%`4(Ui7H{mMQqw%=-bC1zTR4I4F#@Ys#$Ql9O?5NyS#wRWHdtYOPS#FWY=8g zy&j*2Gr;beIH|QcAK6b2f>zlE@$Y!^k$!?mmBI^4F;;4UBB zdTY-If_h(xP#nl?Ayq)M1(6E@%@}<|6+%iOJAHpmoQ4w{o64n#rsSG$qC@Ft9Tc{0 zFO|whuWikj+1@;*jc>0Lke55#wmZ#Ox^rPderch&-u)*_Sh)VM$#KmeD=k}HP7@>& zX@>g=y>h}02sgd5Ja^R}yefZI=ne0VaxVImwU8IPeWd)-`OGUx*hjl)7|lzP&So8_ z>|L3_Xm~c^Ee7-f4>O-GLg!7uQZ{KMER;?L*;O^(ev4aPjxYOcu=e1%N1F`2$3zG` z?~urw;D3GUiGQ51FXcM^K}8B+H~%sAI~D!^5Q z|5IHk%l8n|uIM)d*7OM1EWK(!uXBCT_cFvE&MmR7GESaGTCN!PtQCWGSKVAf=6F{iY0| z?0Mw0xJtT@H@?6cos@o9it~RBvLupBU#TC>FxCC~wL=n%PARFAVCtJIpEe=PS{5y* z?`=k=;gx62!Xi$yCfoS*1ABQx3b(H^B03&rBMEag!E@UKo&giY8smMI-(?tQOekuS zK~rTjT7tkcAx0TUz;H-X3|sR#s=?$ng(EW~chmaZ!~Y(S($siZIi|D0YU+Yt&WflS zinIm>4UkjyC6+Bos7dO_Qo!{i3gyOvic-!?QOH7>8i@2bG%>izY8h_E>AJSA$AreGrM74>z)$Z{+4 zpn(TBoi)XB2X`N`uFM3v3z>t%;91452JfF8uYOFl?VFx1WhXyvocgLoAnf5YejaoF zQe*Er(rl$;`)w_!O8@+QcR9DoS1)r}8Lw`|bjM$?+_NVqjB00kHTEhxpF#o_=r}01 zEXhBAqAOYM%~8TuW!A1qe$(sYRqr(xcyPk>gvrI&F-on;hNVH#`E*>deeJp7kXnpJ z?q>0Yh|g3NFK0sVqPwm$!OzsUyXh)tEqR<+f%1tnN3>JGduXuM>=gg3=dg%2q%4Uc z@x*H zk_U+pNkbD^bDq+KPs!Qw4g*?s$gc#J56rz}J4a{l``oTZWs(@yi9}9MlcLe50Xx_C z&O0|enM$V&_7*0xTEQ?>9a+=BQgOi-6D!rb)U{ijY!}zNop6PXN+j<)E@YY9^ZTW& zj7Ps;4bWnQK9>fxtY&3iJ_zA>P?FvWco_(V&F)k*Xs}J_?{_^v=qBLJWKLg=Uf_#? zUuJmiIB^WMdSG^VB56~B2(c^kK>vKE5COkmI0?>_baHW+W0|Qt3irJB^90@r&l!(8 zN#VcP7WR%=etU+l`*wY(+jldI=iMILj0dfKvs|G*#fQ_%ohINnsw(gaiXsszK^`tQ zS7_5=;y2=5PJ;iR=)Ry?Ia@I@?(6RFdkD|gEFbQ8YknO zI>JuO`3;Alkr-2D5iuiKM`Pm=OM2eBcCbT7_ffwaP;cJm@%2%5x_nhp|HcQkE}^RR z;O60AST`S@Dl{@hMn<;e`cy9?+|OW@d7v!y?!!nSj4$VsCB{?JtP&SpozZ0=;V682-4>6a<1cn}Y~>Z8QIX5-({$ zaL>cd;AO`jP~ti+NaeqW3%qapuer#-;7yQ38f0ugPci)ThiKo|G6jVETHbG5&HWRb z6C>(GTla9fJc=KsxC=}UQurf{F4cd{KQpZ-RG>Qe;}_!%H{ne8#x1!D{|8bS!%q&% z?(w@nZU(8;qd=*9-if(?`Rjs`(*o=tY5G82{>HZ+PGc{F{<;9N{73#TGX3w9|DRa? z!!_f%VxZyCq6U%xXlbCd)G4ZM9_&Ab52d`+2I1Hc$S{%qa#4{v6 zgPQyRUKp^U^&7VxUaT{nU27ipYv&h&ho^GSgc9wQkP}-lZYF2=(=7n7&f^;~0}$tO z6;p_|RsQ-j6i@bj7(GFJtRWR40)ID^iWC+75R)S1y`NAZ>|pm#(ZpGDb*6J&k&qr% zIm|4z*pPiaczW#oa&G7Xv4p8sLB*Zx(@%>v#W7Ig;Gxmco%!!%#cE2?aOaicqRZpo z7dRQ`7lasTqsgT^Qt~`pHVy13x2hxm zG0?Pzp_r!Plo!Nt#JTFm>4LZiTQG5OsA_7A0qm?gVNltz-@hwS6>CjSDKG;_SfqZu z3ldWjN(sQCr93z|KvU#UDpjr|y|MfrA7*@1dWqve&MOcWn4D0&&gAguC1xGTpW zO_4?E-3My=K`N>x1X>ajEp;cQJl_PR6GhceN(ahA#~7u{vkg`{GzAFrNnS;bV%1CD zubz3w;+(WpC85-XMfn+g1!YYH z41L?%Nr3S``j_vpC=uy{V3c?d$Z?5?yvRu^qBiU}(Lg>|qZ?ik9xhIC%845ZBg@Ws zQNTz#Zpn3hUA#Fag?w*5e>hhAQ>5LslR%PFsNJpA-El(L^ZgGh=h5v9T%X z*ea2Y`6&4}jZfHuCMIb;i}^%4cf+BsX+luA9(DSTRJX~h>1z1VTmnp`;!J)4LJMDP z7D}g3mXadn=&DWEbd-W!b0;653uQ8W51381=J`D3(LUyrjuEFWIiv&nMA7gE7HI#P z(*YBhzA5pjX73IXFp$UC7yXxT`OsDfn#hnSRu4X>d-sJ7Yqa%jFOY$ECs`L1S6T|W zXrRg9Fk_QG7LZU(LI{>oT3IZFJ5yrqoVr5ZAR#=B%AWy{jLZ@MZ;fDo8c z;VvWNLQ?rSjQs|d*1H7#p$YEi`yurK3{}=>F4PMfwm7u-*fnH>M0gS+m7MGE?<4Sd zv5^_>QH(j#*N31UkVGZh?CF*YqD#KczYwi|vZDEl*1L|6?eG2@^Wnuapn zA~cF%5?r21w(JNSaR30b|1^XxjQeA*faUtPF>?S`=ao7;fW3=7`<-wN5)E< z9?7|^>uqbf;%RxW%#wf8bFAYuC-8U}hV(v3!Z_e@?(7E!AN&4q<@z<6KRGaJcqa(k zY(h!hx37mb;kW{`EWBqCswYvZClWK6l31EGlPHO>YBUmB(~?L7nuF>^uBHS0!u|tx zdLv|f5Qd)jBDkjxc~W|lQGPy|-hESSA6+q36D+*A{>)5}Jl4D1l3aLY*tAHLchE{m zfyP_%2Qww~j;AsR_>$WJp0npC>NbyzA74_koNqH3>^GC{7C>O|zC5WAW#GkQ`q}yq zgt6W|%ek$jUuWIx-TroOgx=x6+T7Clqn|_3ynPXhMOz!4uPat=U(a}4Q?_6UzGA#x zobj<*6Mo&c=e|E*BRTWF-}3H_oY(cQF%;Q|Y{X3AF7QvE6=LcIzdZF+~FRr-pp$UvO0{TQ$(=-u%Fy47&uC{ z+{J@~Io}ATsj;8Ne2>qGJuSltUNB`dGk{9&+na97kib}!%JG?nWpj3ZO+;9@0LO7? z8$N}|>+sHe{*=efwA*7BSOL5}rt03GBpo&FblB-%Xz-%2={V51dg0jFMmKIF;l6vO z*?n0w9TSwFI*H*=Cyj!rfu}N?G=#KM*7!2 zfoi?8vs%M3>G5(s7nA5w7nRmH&)$0sjB7Rt@24njwQ9SX=5p`hA+k>ItW+3IB z+UIDu4-Y@akRS|3 z?c%Y>V`2hZNVxzRyh_ajx0gqTAg{=nV?#Xp=lW#H0IglFH;|m^Quw@hrx?8#jYD4- z5d!w}kmqd5^rbFE8lk+f@Cds-&*j&4lbWv=fqRmkam!V3&~)D3*Wh*QYpt$)E?8K@ zn@&N^S#fa@1;Z*SThl`KW*j#O2fHuorejQJvlWBH^zOsS-hfctsge3{lWbEce+>+B z8SLP-*GH_v)4D6d-gEsg)g~ujJs%hMus=J&WdScy9Z%YOC4~}b+4xv57x$m3%SuP; z_jT1%ar*EjpMbIfVK%p4dv_d#gj!+}6GJ($bbZL1W!noY!;;6^QA%oc<9_P1g;FSO z7vIaHvS?xswCK&(SoU~b9M_qo=5mvOIc4CXf}OQ@&NQ*aVYVkugN>EvS>m#j)O#7Zmz@{H?EeF$uX`df?CeQpZKO|E~rG_AW1bv9) zu$}}e_~S9zc_q_vw#X6aq(Bs@S#26^$!0e$hGaZGil|GGh1`%J0<(|HNW(Qi z;gXaLpNwmve2H?IhL%<`j&+7}5^q~3`t;A>;WUR}HT&h(?+mrY866Lf9OB~fQ8ELLm!0yBdh~6@Kg5AN=VLhVE7V9t={SI>9EC#5v)BAe zvRK1Cp<5$)kAC5m3097*O{Ys`=ReQ9`>*4&vrUgP` zYi383v1SeXBUt8jJP&TZ%r|UQa@l?kx37;fQ?EYvGGt{Za_l%=qPe)`3XfnF6HW|E zO(@Ps9q+s^MP;+NnGk-Xsp>F)oalYs7T+4Z*cM0cp>x1WqSG~AEwjwkV9Xn?8*jIw z4K47v3!SwjNN8@5KT3OtSkG=wMNQq)dgkR5NkZ~{U(oBvJ)>Hue!O{|n$Ub&{l^j3 z#$O*&{W(y7GSmmPzjX%}?hK&*9NBHAp8FnwilC%27n}E`${JPvZm_8AX2P?OI)zu` zro?@1wQBCJY%LH96(Gy~(1coOA>Miwm7a5@MxMvf(pIMzTOCKqiE97o%45H&hHBz& z5UXWc9%HdGd8?tsul^R}eC;L2IQZX9@A)W^-9rIZ`bTB`t7n8N5t}8>D$AO3(e(_L7oa{g!tr6xY0{pVL@J$*E>C40a|*1!q}JH5~G2 zgX$)U@=PN{4XlhhGfXtW(!-Dq9Q2VKYNys|EPJjm7O@xclGF%cAr?Y3@D{>@FeKZr_lTS(J^q|ulqYLxb#WRKH* z9fKyRCBI}~vi2|@)0L8kfYGYBBP4x}_o}qhY0?ZH->Xx6wanP$lnm=ollyYM-~O)A zmif$Czu94SBfPKotdTZrxMxMSLM${iv^uM+jL3bvn=b`euDd0?>`Ch=At~Ma#rvTh z6^w1|YjjfttGY5t)uBnj9ClgpQEvT!jh8f5Uwzz^W^YQpuwrJDnEB+mw&XXXjcK${ zQ;vMndfk`FywfNP{MxWtm9?_^FWwzbnOx<=yR1}$hC21g)>Q>9ErFdH*&4}C_Vv^3 zw9eYogk{CpQq8qKqbwi6p@o~g!o`yrW_f&%}^Se|2YHloEx za!NcL&LEa#Sufx;*(HoZk(Sk#Ya9qOm(r^F)nOjc$1 zKtpzn=EUX|c{e7*Tv1%2gG554TG~ii&E~#Gg}WJ9WpE~Ki+E}O7UQX<#bS!K zb_hZ$rZbh|Q4&3Hoc-ndfHRMq>*SNj{xa}aWFN`Z!rESV!M4h>4=t?t5I8 zuCJClvqMUBHX(;$>^Vel)s}}r4VJ1G`g<)`k*AU0H8&k*(9k928-?iR2qZ07mliEL zR2n)k?7I~u6EHsnwks=n)+_(e4|PQ+w){}fSx-OrqN3awGRyaTKx|lIuFdfHVr73Y z?N;m^Vl2hC^%1b>cWS9jMk2Y#BxKMrQ(A1iAJZLM*nZ&^@_r2RKPwhr-Y@*n(weJy zCz)?AgaquBj%N!PQ+-N;wfod%w-R|^Y}7+XfLX`*(CJT$3q}_w=(&0Ha!Bf*jEl%q z3@IS6ci-#fl+T87Ux;0?ZF<4m=m5aC%dot`WSyZEo=5jhqx<1vb#D0)9bkumy!3j0OnwVh)lIW4hgj-t=_?T~F_R+(-cAz^ z7&vF)*anVJ=q$$qpUObC;OZ8{1E>fQ>H=QyDbq1~`w+&Zy@XgnYzi#DHIZ527a*_RNPL?RB4 z1~BKJuxi;)5LS}^%G5A3`(qcgEV}2A9X+$SszOu)h$(|hUqcl~N2i!oco(lU9V(L= zmS2Q49K>;|$30ai)3dhr%zP|E2!qi=wn`N6V_Du@$O55Nq1b9r~-{-|Y?wvhtL7JfQXfjnCD7tzvz1qI5 z@ree^->|KQdcSVCl68624$`Z(xH8bUZ|4ofyJ#C7k!UfemN<)sn?OzrTWG4+s(5#( z)S74NADV+!F=sEa938tYnit!K$O7@iX7X6o*>8Icqa-emxuXou5t=p@sP9^;y&PWo zq_mGzFS5|ke=?rrmxxU4qrV_0SFl`7b5x?pm%^hd!Ikh&MCd&MNYg`mYdI(9;imWF z>G~@WF)P+Y8z=H%Ewm?JEM{(`I$dqsUn)zcY+Aoyv83o?9kA(mvl6)NGorgX&3P4BZhh5M#i0q(@PY{!JFMPkKUp?CYQ~|Fp6}F{dPz1cj}e0Z z67>GZPICQy_n~Q(O}L7=S7%k*BPM;*y45o92(fS|D^4L^uB+6x9Y`q-$0C{0r&+Qt zf0XR3iLmCSE94r8(9YKP*~B!=MBrIkPnR?s85`?A7i(#G&Y`qD zE>UdqszlP(h!xYsD`s7`lDUX(NoLftXA&W74%spHg#_kx_8cjNHZ3Ts2MgJoN+R_W zYmNnj%-qKg_(lATXe7mjyAu6wcDT;dof$~H_t|IW67rtAzz-B}cQ5vNd8Qg?Y~ujX z@bp|^$)=lKNDM>6np#$zE%2J2d8k~}4$sDx#x>MQMC~5xAq_e?E;G^2(iJqguvbrf z%eG9OFPEKf+O!Z95@Fko=T&d72yNHT@#K~J9(>PK5NPSSR5`3Dua#%d>mglsW#bJx zx?UIN@%Tq0j(aHyBQo|_-#7j8j>;s{c`w#z=zaSYxU+=JO7|MVc7o>T>a*C3*2tWH zS=jQPM3$V)IGFZAn)8f~6{UjDw%0mOf&qD|E=0ZR_wimx#FWe$=&e_(G}dOkYAPO8 zJU+xerZ()B9h4~F7w1fJra8-2d{Oyu2(5XwthvQ15-cwJoEqLpGx%#;ZSA|!$!tTq z3as|Y7h%{X(d@Gl!w&@bqeboZztCu7d*G~DEI1Rmb0a0ac9ldOt&V^d0Vwz@J{lQI zn`KXLW=XA7o8+cgx!(jWHi*j%oxJ;>o=|u`NB<9*%pv_fqnUx-@h^H9igglMlpe-p zB+%9nA;nV?3E!SlV#h>J#d(pqDFA|s-pQyJN1C(i+v^2O&utKBAe;?TtlF~ZQ= z)Z$a-y2n(O<3;VM4aK85Fxp`mtz9{Jkno_Wo<6lck8|c8R~x0Y;OL6gGV)0MI!}=e zF_YM1+ia57`Q2q6B&6A>Asmz*3U>M*>_qt$Zc^vbSQCB*7j)Q zq=WAj$gZaKP^Bp%xY0FTt%VL=4U{;C*MC++|Hc}9dBurDaupd$%%QWrh1?e9q{U*_OiX zGVwiblk--esK?TIkqyHN^-t!IV~-DmPoCy;crk=5Oz@Cwl2|4UP^Wwj)Fjy#YR9E$ zapn#1<4^uVv%hBXO9_e=DJv_J2@K(2FY?LM3ijttGDMkO#2pNs^7GfGS+BGcJ`EY8 zONi6(urPe&8ltXg{r+B4k}bSdlY6tbolZUuxktRPVQ@<%C;NCx$9*C+Zb%_?=OFq= z_fL7P8cg>rRHn2Zu+W`;DiGfJle|Vi0HaER*>T@p2=5=U+TS@_FB2g$$^Dju5lsPq zba#)ssvWs~jiID^Dp;FeT2W?;!Ki>Bs{nC3`&oQxA0vWLfyj5RNOkW&UI5nu@PRd3 zdd@s)`1)pXYlC0HW2mT-jV%Uwm* z%s?a~{5qkA#ZGQHrh4MP>G{7|;9nwOPTAl2`jSA%9pdK)u{hlq9#-qHcm@z~QMXM} zMJ2hBuB8yihQ&>**~(FKaj4x$zOxY*`K0!|#AJk=+1AD#ufWN`fW?|PyHIVyBQk(= z`ovdfI5=HJWNXJX)(Ij$Vf7JjpKpa{rq|erlRhgCGP#KFoyD>Km5&a2s3`G$vxYjR z#Sv!N&uHbPoN%31VI}JGQ)DGR+6J~P`aaUNCGE{76t53c)5e7O99VlI1xAh%;?g1} zEzM}7L*;YY|L@MbpHP1~ORd;&BqRy17P1xwNRDzRDpAIDe4_zwJ5zU>F z2l<`y*_5_P|9heQZYZ#RfKx)YjX|;coP(pIP_fwO&!O!X;b2ozRu^MXO+o|Zi}hGs z)VQB0nH3EjH+yT*1(mfS6}GlwG82)4+mhyoOmYk^Fu75aGj(G!OukKm>< z_w)VMvUNmB4Kq{jIG#{!4yR8Jy2`@4DUXbfJ`dMt?*?!~ADp&8c+7AeMk zVBf5EnKdo0s$@3YSNp$lL{wiJR8CGG4RsLU*%L>qKJQ7bCNv}F!df*>p@L5_WN}z+ zUFO7k0{y1H^F{f?P)p9 zynfF9e?-Rr&0X22f>=q4$SEQp0RA;r#B$1@D4kj)B5wY=@W>7RE|d2S)!y%~w6w|i zbElOJ%4Po(nD{r}XkrguZvXlrIs_f?XLsDE20f?}&G7%4?qz)hi^jc$GKl`F7gf-M zoD#8*{u1|JyF*}rfH~4lsK0XBA_00(S&!PkZBYI-6a5~2IpgR#69-p>s1#y<OLF7VfjsS*wJAZo_BKS&GUX!}>cYi@%yEIiz@4lDTQ4v+@BMOjqx}->Ek) z?oSN9LPLH!2tem5{qaZ3u1NU^?qU08IbFVAe)^W3TWvO=&m*19h;aA3UKE@eelwc7 zHzxAx<6HCj%%Ro1Xsl<(yvV6EGt9s3^&z%J+x(;X3@nlsJyu}`JN{46C>zJmpd+Ic zgfD;kJQpX&%ZxfU7f?Y|*)8%PGgnSJ{CDfH{at~Mu@yv0JR_U4@62O;=-w*?&B{Ew z+R8ZYgh#$y#NaYT=)GL7JxjvLzjz$p&QOtpqnF0q#qf@oE_S?J8&2f8_{KhX@)EwD zAL-`s5Hs7!$&X~4+H`V*l^Rw}NA&z^QvP~R>e=Ry+C2A|eCv36%po>DAS5iz@wrmdurwhF zz4$FeOO-AEJ4~Uzrt`Sr-WCNg@2+TBJ`GeHQno5SnM`-`%S(iH zYm1&d*VV4ElV1@Z=SJ5$u*iB_BQ3SWFX(AwUn^TqO}K9-vSTyi!kSfe40k`-{4fgG z>HHwM)}J=c+nsWO&IoGS(S^D_tsrF@zMfl#&!S6T)6=9f&5JUEdW4XNkO{hu78eQ)P5OB}9hCg817T|n{yeU%#W?xk?W zems7h|LpC>b&au|f}%)oHL`Itb6!_K!h-sm;QwLot%Bm*x^~f!5G1&}2M8YACAhmb z79hAw;~og!1W0gqhsNDCxVyW%+v%*ezwciw`|P_@b#8VQ)x|~2eCMz^o@b0PcLbk< z>zyIIkHO}yeRMd&e!926w^xFnTL%}CA?m}AHFKa%aJ-MayONo+?wDLAB z7kZn0DoG{Wp4#c92UdzIh} zs5l+_2(99xQ%Fz|w`qOWyly9_V1rys`>vMv6Z9tx7#W@KdL%ra?_(L=)k*2oU-(?9veKHm80B%twRJxmb4K_^MAzZ z_;ZzxLpNXNRb`|`=m%XIFt;f_vWN3YK#y(#m#oYPo4>v79`PWyUqSL!Ws8`=fwzMX zt#_!)(Z}Ly1?z$TV)+p7Y4->>n(2sGi~xg~INc~V%ErlQG`yujFyISttHK;uzYd6z z=g8d25PZTzy1KfRE#}5=WOW#HwKoNr?rXIU^SlU8&P<6Fzf0mQ?RCjMes0boH*^@v zd_J1d?hz*P)kNp-wNQoUA8AYlNgC&WzwbcWtv>7smyM1EGb7Qd~N_AUTVhsln zvprKSaG z(Fz>M6}bu{w>C}ot^q?cxL&9^GT_1qm%PWH)aNoQ-#ZCu75H3NKq=MzSzJ7}-e+QU zv<$S3N}Vav-S4RIvINIuoq48g@z&CEv7*8Ym(QtgPpdmIG!-Y+ZM;bXT>EL>oaJ@N z#XiD_VUlKkm8+<4LA8Y74#tm^;GVqa>vS|X+GsZWPE-TKTHbW*6xioe0ycII!=rr# zrH|#P@^K4`d3)ilzk|Ym{y+%7{Y@L6BK9FI)#Q4G*8%p0dm{bmRr5O%EO=TfRj&L# zu=FJuD6j7*M(R~zA-4JBx4dqw+{YCAM%d%CeDOxoT}g_9YG zqjJ!{lU5qaHYPCjBpU`0nIz9J(#+A<6L7YA>1Y}n>u#;<)jH#$&Ay!7xkx;~BY&qF zuGMW(Wb-*OWzeB<;(&*`Xt((X$%iodXO#(-!JLXGj}qFb8PN7eMRcLvr+lE)z*rA| zxosL8UmmzA*=3C~H|5<=E;L4lW8(!mW%ykKdM-82fF=8anU5pO=Rc*isj}=P_WBj&SW0ViN=XCen5ts<_Qw!vXUue~tB2qa&F) zvp3&JklcPXFI*QJAg(WidJ`7@U5VKUzR4guUb^WE+0Fd`sH(1J1(;-zz#kJX6MOA(Blv`y$VW=XJQkd=OX?=~C0$JS zn7+2jz9J7)Ga(A&SQ;Nx@$?{j9$1`As4_!|;Dhyc%$glpszh|ZFt_yc29K=qMEsBkE%SBJ(#AVvl^HsO`?%+`AKcz|^?7n8U;>-QfhLWN^I?KA+=0kc#ToV@ zL|j!&`+5~l`Xh<2yX*;m*b!Ln&wH&&8%xp30Nu;dx@(xG+e4LTKYGD&b-JeW$B8<# z0TzR{EA11m)l#k6BDlVd^K>818IV6KATV8){e%c(Kf98m>y?AC`G z;IA*8DvW?h_Zssj0GbJ;@}t?*@p+VM|2nTj0fWa(iEh&Jyxv@^!ZpL5c@ohBP>kp~ z-y1TafW_I)P{n`V(h#YtiG$>^>@(x+z z1}LHBfQgQsLp#>!1{VQRxu}>35db~_4$+7M zyrq}i3dxb)yoFK9!69Ms{a9HqOX+nM{gGk8xMopJ=yL6}d1Q5NEC}Q^E-%~Yaz0iDe{#nSSa z@1K*t><)k5UwDYv8q44G7riNQ?a6nb_a2kOzL3GFu16`o}i?9(+qe z1xeq61PSH;dE|$D5h(}c!NB%Lbblm20+1Xe55i_yxq1RsV zkadzg+i|h~@kO(6UER~+B_Q#(J%@HmLHjd#E<)s4D~^Cp1L&`xpdW`$?>x<7NwEL; z*fBL^y+g9PHk|1EQsYHPenM?<_+Hi0Iy%X4XT4KoAeG?{uR(5yVKwY zSqrC7RfD9w6dD3ZrYmA5nDD^!vM4ka8wwf>sZxwigej{;16Eg$O}YMgQ6(ZYZk8kB zX=YMxb`D>R?Cn!Da#(~9VTeso_u9fi?1H!h4%4f;s%%Z~>(5i6#w8XV!S5rx zuFZVH=eS>^DE!mCeiw>^gQUsuGf_VwLqKOdT(8+=Rz<@Iu($y7$-fMs;MpxHm_S4HM{NAn+_HM<@Bc#9oH|h z!A8xsrIuEaR3ApY6Wr%BJN^wV?D9aAtdupZYShaCKuxTuQC(Ba9wqMZxxTajnCCQ- z+S#QTHk&aTvfXX;FS$ZM0V;}JT5$e-1D@H(+bv>livp!E5apN5LSp-TAe%{VC}eht zna?~E!XNtkx201jj1x*|I!z`TVPOfBguNqs8mqAAKJNpoDKPN-pFRYeG~}#l2^6ag z4aM`a4Z%NX0@$6yB7U@;%FN0Erc6I2NrbEI3Fv72LKDpljQm()iKV1J@5`j@W@4v&yYJam;iLKk*&&BWpYe5A>9SRT z{O!jZc9~gCLuz*sMJ%rU(%cz<=G~A)6p0hg69C!vg(?DcW#Th(!iKf2go~7-U;*qju{4aO?b#aFy3PTJdi)CiPn; zhq!0$O>V|N%rp`bKKGcb4&(pjcO=tmAztee+YR)`Wxk_EkmqzqWZ?Z%#TXzCwOK56 zkziLSZT=))4CqcMp{}u@(_>!d)YeD*6(jjjS?Mzv{bCbYo8J_9z zqY8M6u)`V_;+nPcCN2BZ(7nQZhY|wns>thLQbV`LBhiZKj_mv)#RAE8LF_G1I7jda z!2|eE63WC@_NBIML>JiqlIm)an2?dfq%Nc*BqKL8OFmV2CqH<;^n29LufNXUSnF?U z{X;V#TH>cic^Uy4+fzFyO#VZBy}!kW3vm7;R*q`_7GD@&@1NSw3=tpyw^*V-v0QA3 z_?F2>?Eke+UugGmi1tNh82A36ea&K|rL{2Ibt%-VZH z>T(>e%!QkraC*~a4|jf%`_JR}*~%3zN4I z_u=mQCJ)DXODZmpSo<26NDkvEeIwG}nOaDu*vW1?kouhS$Y|KZ$u^QD9dZq~I+|yn z3vk-oGoa+3<%w~KS&q(*`Zgru=f}UgUw>nmgl524O8{xAla!Vh zmjhxw4h`{3?jEdE{}%q;%nL{33o-^ee`Xy)L+geJe-F2B#uLW(xP)NQyYVE!@g9@S z$U)(TyjKq>vF#?c6nd$$n^;usQJW%S_OCKNN6Ll#V`tkm{CxrT6^;%{`Wj*Th0+wb z!WoKbsg1)5fpSJ7&BYIgeLsfwp1fOwS$)z{UG!541Lech!z zHDZiTP9o;PB~|t!brwg7d}L!Kps0WeUSki=?ZTfWIx7!6I)$-7MZ8neA9KX03d1sg z%%>y}kJma~bIiw0IhBko{zPG4#bJHr$Tz&Tg+ofojs-c~^%`-3 z{xi;6pZ7Ihf>uxTgbGSdC-=Jf#`oLTD`W^tO+*F|EFS+W*E+y-mrCk=h*Qw&+5s)F zQiV=I*&)=v`9M}9r2r$5wJqF~?gp59ZvdRE9;OjyA&t!)#*T;#jf^}|P8z=Us&Wq> zaN0rQP4#y~9z~>O7JW7Z;kvkEgfPCGVd2ej? z?nLzCFp@&W8*ULRt~&nrXFT^fj$i6GmPtM@?W7spMR#wn-*`WkxjtOQf!#VADClbK z#ou;72!NjLh=a8Y_l5kktpdNVFh=L_)N5NiY0u|h!X;K-_F74%&(DHRB*gAMK>%Tv za6wCi8lL&f^L!kAvHzx2e)4FF2Yytfw?8$N`|?;FLFM-Qy2;;se=Ng)$)CbXqhh58nnIgzH9-h?P`+uK1;(} zCGlPpa=&ECpbX6bC|@7Ul=kdLY9y*6Ln`WcO;X*E^*pa>R1{{U_V5zc11DW^UuN{Z zksoeiLvNzqXz07Jnxq05Q`25<;1G&*zjjf&B_$3v?4Yzhi*4SE#p#8JhJ@9Isr&+O1h?P;%nzann($) zW`qHbADQ_+goPFxtHV+vG>J;b#S)_=(Qh5$QZcDv!fOmfw07h|-BFGMpck*2eZGzC zuu@48c|04TZ1~pYZ*5=oHbIc6^-o{dGY9wuZ)q-N@&rGjNnMcmbmc!J9p56W{w%5+ z^{#Fee7^RPorsa=M#@WeZa`DUe}B6l_R{9}^pM$Tec?K~nvt1j6*Q%LG1nW_U}22U zZ{rVM-Q3y|@p2M;T;;zUA^A+t9cyP10L=T|&Sxd=LxsbS@bj-phwn97Kub)9 z(k{sM!5*m|4JVx7xLt`^y^WiK3;jNBX^{et^Pb#k0`w~g9A-CgIy3$Rx5zQDxO@5^!H!4TQKl%k~wr;Z`67!HNyKDuKh=Oho zmqXe+za`z^qg<6FoqQ}cZ;&r6EcO*ho(>xKkOB0JSA&)%s?6~Co%x2FPthyIC#$DC z5SF_jY#a9vJcY|VzDovrd_!W*10kXaj0DZ1H}6u^g?QYH$V~%1`(n$LG90_BIn%$~ zlm_4K<$IZyrtoFFI;ic2~0Xv|{ikj+5Mnt@jh|-uW95+7l4LiBrX5`c{ zPMSoS$hwNZA(S1d)T3ynqt0yktsTNA)1GM_ZH|&&kCP_zfFXG56f5jGl`XZvK2`-j z3wkOD{0efq>6q~86;Rp-*wh^{{$_Za?mH@7H=kFG25&>+-Fmp|UT#`LCpA806HcXA z_f9j}L0C;@naL-?R8M;4yplIhZxTMb74GS)J+2TxPN0=KvYMoXuq5QY!}cmn7fU1F zC7C{yVdSJz$HE+=Y>>OWs{vh^cXj$a3rao2TioVLn#T&Ym$Hnu{EPt%LF}szEzaVu zBJ^Dq?8~k8=G91Vcbu+VhLPXee=u*tKwm}>Ls=w6p)_K+wJ~=Gwdosjrw)uR3Ndt{ zISn<^eNk7rq0 zs+SPo!UMSzhxiQ^%NTZ2X)vK~p6&W#ZI!%AT`XGlEi2=aa(=64N42>J73iJp|eMp|x{kH2zA2(R@< zfHhxsax<E8(_0Clm!`1 zX>5*QFX~{$^n-Te=|?tiiB?r@4qM-ML=5<*=VRwN+mG`jf?`#pdo!)COeF{9zri7> zoedeH?dKyBaL;S{nCN>Z6y(xWFvL}W<_qvtc5=v97uH#Mltd$k`)TviyjsvWeY{pt zqP(wGvCvd`Yi&DoG;|Rnd_u5!N){l!5;p+GIqgYfIUiM7Bs`ASNnPvXYB2LYW8V#2 zC7#ig5E8KuLV}3njutFA5EiP98Vz##;Vl;2cyg|^{+liHNVK(VzVa8uGwPC&gpRcS z%7<%%Na`6HOu~T2C`G^e8l4bMVc@mut|Yj%d_hxxkl;Wg!nJ;~+O=W!nnTNU$g<8S zbFY(XU7A76PRd7QGr9PngF@vq&Y97IrjfTWt9G$)5YP_M*8hc1+|Z5#7d(L#l_gSm zJ9?W`qRmlF;{+-8mXMC9TV%p0!(&J!vRMzGFt%o$d>=HhQW{9K9bu*{CVRNw-8#uz~ozpNFnNHkAiy;R~_5E`&4-v9Tml+0}e+=*d8{Y`yNB znIsXGx_gnrt#^NBLr7nSVSFI`t*fPLz%q*g(bV7SJr;?u!6#&v3gz$HHSJ_4Mf=xT z^BPc%1K+-jq^kDCgoG}gYfQ>3&9Jhgh~uP{ZDhZm#*tkRtyQDz+A~cvrAMYEWyCBB z8E(u%#|CHmi!op`=r4dQeB9&~%*L4N0oE{KedPheQs+Tg39G613nEeRu?-E_l~=^z z8gr!=#cDXMoDz=dY6A-K1a64oIfb;iIO=irbO?{syQ(yc8zb>Gdh&Uwa-g`Nc|8Gvtm|Dm~b zE{siz;;8sy?Xw(K&CE&X&X(n>bcIyvl@pifC+hcI1~1pwCA9b$Dj(BFWo5jG)PRnXg0d+WNq9~}m{9QDeyDQRftpzn&L#u)rAO5~CgvE<(DU-i;1 zw9HyTs$TGoM8n>tc|UbQFMb*m@_Fj8?B9!njWC$2EA1vMT;KWj{P6R;Xy!%JdbkUIm6XY=G?P9ZHL2rUj zkkfrHJ4o<9-KD`HLXMRu<-lIFlz^?5D;X40C#kT3x`tSDur(S~b{X%1fquD0G=^lM zS;$mK_3U+USW*BkDXRZdN;3m9zXgr*O?|Yu84OppRB~z0ZSYloR_4Kn=CWaIEM9j( zT`cbTmkD&sryGf9f9kC@a1T5u2Zty+ay~-h@xjF;q=#e1w!vgP(~=C>%Zc)W0E@@T z#$RShNe7aM;oh4cagQ&u?8@yC51A9_yv#bbo2!4SN}!o|pQ>FuuV{;3RK*DbHkW7s zoaQsfhq_j64s-z#Qd5LHgdS2Ct7H`l@ha=S^&NyRdS*7=9%=juNz)~HXD_iqUA zzk&~lImCcJcC|8R6d|Pvhnjn%G@%IPQ!c+a?Vta_#z&>#=Ka(8)Jg+K%3RoJT_N$!VqRdEl}rcerw-wXf@8m(Xc!B?^n4niBB^eNnr8IxVGDh5Nm^R___*WUw(HmL)gmHe8JHn6*fI99Bi<|(AH>@F%!KFNl%8=z zq+A-uq!nKD-g|dp7-qHe!HqlUzxC(o>`=OV)Op!e36#@? zmKYgd9naIxc;KoY1=VBT=1)=Fz6lvpf=q4@=Jl=~vlze9*wZkw^LIhEMN>0ux91y2 z?=`}qi|!eR_U3$>2rsXq;&R#QF2}Sq^>hWnUc+>pisemh$miAcAb2LpcW`&N{4^le z3_LDqDXy)%Py#q3D~$qXmNEdJ@((C-3c-VkgHj8Lz|8Dl(+qOO6}L;Z_NnhFMuW!P zy~cQLZk37xJM=ccAd2rj6U}NgV-uRSKSgfVR+Srfql8H|w&0A~imo(y}xNX8{J{G^MQyN&X zVR(cqz9=afI2K>T*_}`C5|F@s(+}4u-aDZegl;)e{2x9vKI+M4D4U_m&NVc(bI%MJ zfA7~3ja_dZ@Q|t(75i#YR`VCbgS&$vZ+@Eqm;a(Vj;|BzBuPLPd?7uct}kdqvSWfg)Dy&8_3oxfBdv!|d#Qp06{Q)z$kt!eJw!+uSwln$(C{>7YY3`02j^ zTQWTo)lR12ON8SX!$+RRGZR}`U9or*jfq?GGAq!q!(Wuwm-xTnS|*j{?pdUyKgi_E zS5U`U6K@pf{+glvU$K1`I{96?s{2g%??Hu<>9D_X+*hj3QvW86M;m|RxN(9Kf0nvJ z{l;-${nY&vhG+bZ<8F+6|aa>F*5IkC+s~BK+Uw z|KHvJO*gpz|E4_a?k=}w)4Qd0?*lqNK?@J%(+j?89P;C8#Ncqddb{5W;Ow0Hmj$%V zK&l;vl>~IKI052cQbC#Fdwpk!V$rM%aD-TVS3|J&=>`PO>ZNV5q20NRVv+4_`v0)Pb7U+yiG8dL+tA-dHE-R#!op_>tB-2_- zaUNa^T)$*7E*gKpjJ_(vxdO7A+1LnJ$Uv}{;PoS$LD~jbjEn7>hViS~v=a~blaoh` zM>5+P0vQA>E6MMoyHA;K}9#E7^9R{D^2}YVQNqhjn!32A0iGf_gHc$WjL*0h5BA^kb-EJl| zJ~uCqjE?mUqc#Q0q8^vEH}sPL>0D&E|NHlS_cXQvNS#zvnf(iJ5~OnCRqb@NagdW( zx(ht63`qNhsujN*dxvG3i22BqooZ0)Z^dv5^#wSvR8)*jP6mG_9WCJSYF7S$E2`)n z9i!6KF!2Snki;4|OvZvA!^*Rxlgx4&d#%~+q}MHP?TAO4=uClmyoe(!CJ&HC=ex$w zrkwvyY9g*y9uYCrN0JceqyTkILXV+<%e&tFlYwFczN`d2;~q1L$2l}5UZCLEmd)bO z75vLhQAKy^DaFMo62f%E^BiFwE>~v}cpNqAA`uCoKOw-Z3e7zeCuJ`sAkMY|>h&&H z;_VcPbIbpZ8=ls1*}2Aj_pF!IXx!7VMp`-R8@l3MT07dU-oTVgz)C-J%S_jsUi6rW zvD5jo>y?DeV~aiWg3~Uwjtz7&45pSRax+@XX0yw0iZvN%+HrBDuB60rSO$>G?4DSA z$OU~~={%v3Gca`5H33cNr}wm8Psk^PjBQX|9+EDNK9omg*>_aMM68rLyFlvN$vDe= z8u{~batv16IylOTR>}nUEs8dAFst0;4sJ$9Y>T9lb`FtYLt#+FrKK&BW)=u~3yU+4 zH}UecZ5O<*-cznE&N-s43_n#ZC^DJJ4r`QnV(mPt84ce3mROrTRXQ{Ge(5!k-d*3_ zhv;Y7G(|7ycoVnO{GItw>9iiVwH}O#PKp7e;9aHiq23Bm-}%VrY*>&G*h&=9-`@|{ zhLq&0~W<$R{Kn|mjPX`PQahmx}H{=7av*4yFXsxz#tg&2)0^5v-PcIf4Kyy|W((#M~` z#sZu`8ZgcYL$?aKf^(6+=}E*K>wJwc2l*%8D(2l%APMsG-Y& zWBhRA!Ue)R94#|Vb!8hDxc5BRc+VUXi6@9gC*Vo)EDy~`Jlzt6fx?w?7SFpQ{J3!! z*EllM5VYe7$$I{-h~i>|zJ$s)e&S+$L)`lb!W+bD`U&rh!j3DwGsohVNh|1T14r8c zL`Q7UAoHQ;Ti>OCAmtk9i}JFBTkSOrL*+@Ii(I)z11hPJ+PkU4C2W*KvrCcNPnT9KEQE3DtY?> z_v5QLeFwA}+Xa*5ovV{ba}_GX{`1JypKniFnsabDywAEZTKJGoh9TTQF_A%bq%JzTg&-F_pE>$X$h5Zs9f@%KuP)23 zZz0$Bbq2=ifF7X&sfv2{6$CpVcyu`I@q=8D*IqYTR`2?c3Hcxi|7T~L71}WKiR>Z$ z>Ev-%79|yx2uSP4w`NKY*`-fkgA99$S>;wg8ymkB(7XiIYq$u%wrP5@@qFIvjc3~u zt$t{16#Z74NDw*66{T4qVL2Uy2U!DtsH7jofe+f2}_~O(0k=(TR`sf288{Eu_D=V?vDHN*@zq;T8e|I;os%pT?_lA|0C-nl2U`QVg z_?0mcM~jK3R;vw>00s;Om3&8+RLl_9%`(umaU*{=l+nEmo)~4>`-pOo=Ya2`c3nzZ zTo7z{#K6GZM09@RFgG=mV6xJ1Ctv*X$V#u(gi2FTV7OJ^rP*}pQF|W~xoN8AbaO=9 zNNJg=!9rR1c3_1Tmklxss8q|4wE-iNTIm^-NFl6R#@%Ji&cvi4K$mpN%a^aJJE}_R zj9HfF#lhy3Trzgw;#FfoNtJ4snJ_*-m1YRxKjMv^(!u~umk;={FEzGH{(-hF-_{Jt z__nH8Piupozw8h3T@c!15eDZ$=%yyYlMb1|#buux>xMvNDfFHki1-kgtT%dJ+yxXsYB9Rndo4Wyd}SqoS*rHg%X~U*c>gd?(?ohK$gBv zOf7NwVwEd&2>Gu%N&zvTd3+6h5>&J-h0dhJ#B%4Mox0oTdd0IC#xNfSg+U*6Z25*! zy6(~xUiSj93BVIJz-ZLP|;m8ijEMpUmB+Ae(=ESux5kN4PO?^#hjk$VHn6vv&D zlQ*zFZLpn5F43)!qse+Hpf*X5-mape7U;-WH;ZV#q-~13_oU^hOCNp--w(OBuUr-*xPquEA3Qj)0NZGuY2am)C&^W_P-tZDMRc}%Q1 z_PU1|C(Ap>_)DJ)TW_*b1~&w~ob2DpU4a75@oM8*P{k>zGqM6>y-tZCH#}M&tBFNc zjElLS+1&ARKX{tat4kQ$DUtmidS$nB#JRTMQuVm#_7E|iw)CE`{w8(7egbJ`J+;xM z`=^GEVZ52o+q)#MI;}g$3!6}415dJ$ zVN&xeM72x4mOHwPrNsqN1-gz`#<@Pt-U|#f8~zs~ARWttw)+;j6z{@EetPkZ2;a)<*Xe&R1QICRfDqowTh7bd7fW_8H z(tofr_rx~N<;*M_OEJ6&-p%4^!U3T5-o#I$>7(6DP-TvxedDoZgTgSb6bWQ*WWh@| zg{~b;xBCQUxsie!->-dL&6*5Mv8N41H3C={d3 zwsA_}atqx%@dSf*F^&&xo8SfS`&;8_@h83~M;k8s(jW9^`S^@$+E;a4jPQqPZ}vY) zP2?HPl%|-F<=V*P4afUYo-lEo2x|^7h)m>%tRBl zss{WIt--feI<-AIGp^tF@`l-ZiLTf!iUDPwuHMfpa!U*ZGaIQSk8CXBm+f^SF+;DP zNfZQ32{j%9Uk*LMOdM9xqajqLPCaRYVh0!aO{xG-!=IxB?tcS&E6FeM0*b znh7U)(tLx>BjxL)+;Wpp9u#9YEJYn6aB=ZtDJ3Q0Vg+fq(l!Civa@5}gAdKg2q&OP zR2+>XuCD93Uch(ETZLr#y>h%nLcwuKK20D;3h}xq$F{bBXSat;IZIsbVoARQ=0nTA zAcD-G2Z`s0KA%WstrEr9dc5ffzpu7zGvf-~-g>|0<+SD47b(h`Zkz6oi_;e8zryvf zOZYA5npPu{O9)1@Iz=B_KrRTzxTsW?d;9S|{&X%V8kK}eo1--D9Ts^w(E1@(PQKAE zzEi~4mdcySWr0Y+WUb`FW(n{Uu)Wvp38A@xt2+D6+F#o%=h^7)E#;LZj529Ai1N79i@RSo3@5W}SI+~B+qrZg zyp553_z`k_hFLwufDkhwk>=e#S-nhKnBK4GZPw|4^X}cSG_2?TCVWw1@+5q#nojlG z0jc75NJZ&#vfqe)2KPzN?HeM-@+uA+p~94-Cv3jL2}Guf_+Y0dA|*Wt_;KwI2(AcC zy6dCM3Zve-dlTF4@{)BRQXquqwqWN-yoizJd5?$Hsyj$$YJ`efUMHJ${}r)uOFgOY zQ?tO+{lH*ao=jTHQ{~af+_$G|;p#{BhC;Atz9z2Mil&=AWKxXFROHj$^~%N+(Fm=b zO8}{zsvT0>t-92KeL<*vwlr=R!|-bKxmmf=S@*&PF&FmcNNvD~q9pV% z3Vse3uyYafSG{TaCcI7&3X3fv54|Pn{Ui|zTkFHNQA2i8U6OMf=7Ecv1@T#49@1n zg8i=81QR(|rmFzz6~BERf2sZ42!aUS1mI*v&t#GQ3iiT8vjo#Y`Q2iLTMU{2gIe*b zNgAtVuFgV9qs~lTq?>f>mosv0uV7j@?BGImobI8580rW*0@|aMT7_@OkczBGCe5yQ z#^;l1POo?KnE9CouB{gaTRNJ=NCdvfle~=iFj~TVy19Of!K2B#KUu<*$Cyj}O*$cu7-_vk z7K;GZi$7iA`%(_xUj1CL4Ypls3PEgT7|3}y-Epx~89)rpaoqgUasy&hM_zsbvWft{w0&t|u?a??<;ANewq{0sFa9~_*@|>6UioA$W3QnL>Srnk?13wKuJ@C% zxxAW7bV{5w%u3Grlocnw?cZ>UKY6$ueaE(*Pk7k&4JH1&2F`C>id30o5$53QO1+~= z60C-uh|h73#-`p?dHYLQSP^3D6Ef=yokd2>mCxilJ`m50Q-py&^?2D0pQa0`qI3;o zeY<8RbC5a!={l=zeWN&?4QxN&B(H>r0m4eWVjHCk?<7mT_(|1Kv3z7AJ~RC(&lfEW z?53yVhx7ddt+{H{AgCGIV3KSY;+t_97Kh0XN462gI<@U}+f7;OuP+ITz*J~aQRk;d zkpscJ;(F80-p*}eFfq*8m_0GOxeS1Q5d}WWR7qY7Sii?3UKe&|HW6C_^L5T*_tN?K zk{Isy0J9lzScuC0+>ZN3rcsLJONi9xbeL9`Nnf9?UQ({->&D)AFGyJmyG_6mj}u4l zs`%Tm$>Wqj9ItC5yDOsD#6;jE`nFKM*G(rI>)1+J%v2DGmyW>WE$c$F|3S)+AM_&M zmaRkwAKhA$Y)~=2`ETMZv0Wn=Uk|1k$F?Zi=@xU9L=a3TdDZR-x|$JS<3DD zbCOXv|AxNRq`wpwasyFN$A;88;Hr(lCX{2kN)Mh8~cxFx%CEA`e<4y7B zg<302O(}4jHWpB2sovUA2|3XEZx#oW8S3%Hcs+n=t1?o64Ta|HbA?#ZbZ%U4n!9+* zBy4J$Pl?Se#R+CCHkC`e&T`B2 zl3jBCPH8=shEVyWBim8_`UJSY{Y?qv$0P{xIi+Nrf{rf?1!#XA-42m3nZ2xwH!c1VqD5~ zQV+#vW%WiqFF%=ylO&VGLFN42LWZhZn=5II;)p@ZKKXC6C`YFi0?6ivz0rX_xS!sB z`pQhEjbF^|%Anme+QlOT*X~emx2%Kl^{Xh#b@$ZCH0Y=(QJ~_ee4%e{xM7M;R%cxI zM&QGX#|cAYqShJ5tX}}SYli336i&|(IIn?uJ<#9EQ~jN}re*F>vUQQ5`TC6bVCJ&R zinmd5m>I%!a$Fio(9-^}b2C^V5y|Udyt+t0>l{OqBnk?Z2{U-dG|hY56B6?a?9Lb- zPJqMkj24jV;@JE#F|N~mlF28i6n-4o5}K?xjSdr6tI|&hJuOOK3ouRTHM^%Q@>!Mh zxxSb+izmt{y$}~$&g3}4#%Lk=lkqN~KZpNha$BrG3Yu zn~@ZhWBEQ^l;mnQh}fmzbV+$LAj7}&JTw-6bR(B=8@TB3@F4~swWNKWU2S7qku;z@ z!pb=oRqPz5u99f6b!2jl;IH-}p*_l1_FseCo1GYgDvTaD>YwE0!n164^}U}9elY3l z+2HbZ9SY{!j9D+Ze0*qhYM$mx*C}@Fv}bw|DG_NuCrSBgD9?4BA;D(3kH5hm02}Pu z_Pk}9);FBOo1=a-Z?eQQ$?$6(n-Dwd>E%*v&K=JT0l{r~%+MbonW+Q97VcSgh^b_m zp10qs`F3yLKgMG4M3hK858gxB%b_dukRCckgjW{ys(a9p96;qvc@%Xskq#?dQ|7K} zx-G<7^)HLFlH?~VisqCSb!F#HvJ(gO5>NjeV*S!Pc<%7Hn_zXu;5rZDiigN^04~z! z`9asOJf7JrG2G*xCV}}u1ITJ07esiMy85YRO<7 zWxmF&&<7ezmV={8(7dd0E!&RkVyMBoUaVjx#aM*t4e1 z3M)Eecixuz3=in7TKigA18K??@7-U#v~kp{*nx6B%yX9KBz6@WL9Y)s`rpxR9CfAF zH<+l*_{@DZmvN|C+`AoO7;YdSc1$lxgMJU+ZTUB*fL~cC;Ze4i?aqY)kISXx{W355 z^ZE7|cPEW69I1p^y#l00C-nn-xZ?U{2LR98mj@#hQ1uDV%!%d&BY;Gjht9_%43q@W zl&3nhr*WdUL@MS)Wdjv+fUdU!K;ENimHt3mNW;(8BsWk6!D%K?NM%!Y(5-sfRYusi zmVVY-(%(zLa(`Meq*zT%{#H-1wip$jQR5Re`%d_R^rbq9l-zJ1l>?+C{q^PK5!=1L>byuYFY zR_o8S8p7%{$26=+qgE384`Nz`A%s0Mef39~9|4r6?{iaMYE&hdo*vUZCztD{3Cut~@TvF@Z6c zFLT#dU;)GG{VDt1&_T!NYhh|i2EFTE`Z)KkGUidomQRc6q%g2yf#hhPJ|WzH@+p;; zl`VGl5brDP1X~!`Phbv6MHe$#T3d4d$jXx+00&@3XrD%%Hwf+CtMyVg>qIA)IB5#A zZ#g-H*LEEt{fLhrNXvaG{A66DQOb6(5m0pUau2j*ADENg;EX&Y6+5Y2ksXz_&>cEe zFV)<3U@@oVcJ(+P3u0BNuoqrRbk|DEZz9ei`VDyja? z&o5hHHpP_O(ARQ*rM`+ha9z7R-&k|yel92CT@8C2|5UT6|71u@ufby30G zF^(M>?q;kZryK~|)jF7JO#fu3`%UJWbAKK5|FHL0L3M3g{4N?SSa1#Q1VV5LuE8Zh zfQh@iyF+ld;1Ddh!^A^ycXtc!?q_7Lwf0`g`QNI0U+%-Hlgh)aQPhw=T5r9N*1q;@ zui7fGNo(5W1$$xEc{?U4kv(ygZWj;rm15zx=f_dCjK5^hy z#+g7=;3$rdhi;K)W}Oqr@6@zOVpm~{rT|y&BAnTV6jtd^$lG3@Cq8OzQ?nf z&c;N1@0Gy7kts*%nZ92=jP8=0EPkw{A0E9Q+7#-e22z!;Mh#{*YA`)_lLc(f@2@ zMk@h_m53k&?jyVP{5J`6S==#yOZV&JhPFX=CZ6MmYj5VWY2Ei46kFEBIZ&Y>){FTR z6p6TAFWKy$KwIJ6J~y57khvxxeIfK;c?+k z9dn!>1xConzfJ*F@kGl^s_@QsL@hNDvpNO`gK~q$ zR!m%&TsXHcU4r^%>sT1Z=+~{E)g4A@m~8W9Ja{H-}eLN%98yX%m59nIdK? z#9b{r#yWvB$%>og5((LZB|=2*i%zB!AS{{!cD)VTfeKN;srLUu;HWat94#&-3%^8u z@Rv+wmJ6UXp|R;M7&v?t*Cqk>wlvi;{TL&^Vsk(}K|jx6QVA_5II`^uxByDw7iBuh zG{#c-_E-P(Bk;BdkRT@x0ZBJMy2WSTIYmr8STfi{&0g`k{lMRa7_x5QN&M;bv=5-3 z!`f!vPC$97SPkg9+}y!5I~aaeWOwW9m5$r7$KOh$?^fk55%z^eat0I`6e-wpRuj`8 zorf|0jTb##K{vN}O8a<=@?pPVcsp95!-kIYUP?7$rQHBpwk$s>DAQ%X*#7cf!$^O? zm8+`w9p6o5NFPmjliILT!(VgvAEAs}($!MEEFqB(i6u9{4QAR9inr=vi#HHEQvd@##6`#7gy2bT`nx#ZBx#&*~_~FLll9R2V zWj}j0Ho4-qZ{%lUa?GjP{#%zV%K_ELkh(&n;${c${n61;p8iJ|To^q&h1fVpq|9pi zcbv)-m|WN)9$uf7f}_7oP@lVE7youQK$B{CNJEuzO?a*Y|Nn$p{t?3x&yof(F`Brf z2>(CIuWdf~2=!nQ{%X7Y-%2Ck$D23+6YJkJ+{pU(iviFb4WR1V_Crw5|IoJoT%(@? zfK`}X=iZzAb{Y~072rG?V{1&mANFOI05CCWEq3IT-|zD~76byJii1n|?}z{YaQpSE z6~TiKkvv6amk${~{#1^n_?Qd*6@}Pgp6DHy?AQr_75w|ZfGVJvsi<5|(DS?TQLM%b z_-f3g)pbMt1_c4e`4It7gi}PIhYb41Qvd#ok2S!Eg(gy~|M^B>fa8+wA68NS82Zpe zfW#X+Gx$wc5;$4jFUX1p3`g*1i^H*Brl6K#@rOK27A7ii90tc%_}_-`%NEoCKk#87 zuJjKiF&+{)p0`S71NXWf0 z!RQMI&%8VdGqWNcZWfjau0dcS`}gGYMF(zb1DXpuvMDI&=I$<}0D-_5hOBo2gF)`} z^fG}Ji>I$oA~q&NwteFj-t})DK!6FuhR>hjXQY(-d8?O~S=VXbH6y7DB-B8mBlU+^ zbc}P))*Ot7$45#5ZN65%_1zf9zVY!13QEoo=rvKDWP3gGD~tc2&wssDV2d6o8(I8P zD$-jZOX{s`q#}Wp5Jf|iJK)|teBW`SzSq^wrnOq!5<26Q;B!}?okobaLmH>x?GjUv zy_0$iLaJIoZg)gPmf>>KWI%F^ldN6-pWxm>HrW9sYz}OP{ zK_d~uOIK(d6pijl z{jz{{p2y=6Qj3;Qm}XA?d+bI5HYNkZF&SZSQ&W?g)1pk3Uo*Rfi0sEKXmDl%syAU$ z_mhrCNJt_2RF+@B%TF;I1+u}Zb^Cc`7P<+|lymPynb-XXF&;NJH=CU<>%YxZ*eaf0 zqeNHRRwg|z`+OHLU1*IX-~&Hb={FpH)5t5D{H7!(OT?e3u4Fpioaf@|)^}?kh(lhn z4n5PN96Q|8JGbHfX*DhG0+FU6(icne^NTX1GWmz(j`eI5D!-z-=$hR3wf!0P@3Weh z-$ta?GCpf;m^&_iGmfELe5`0e0I(|cDM z8x=$(_{0|@xbP?Pq)=;lepEj-swk+8dqYY?s@&($B=~nJC5)N77IKw1kEn zpamk732sREs;Mwf0hH9=l&K}&+l#Er5!2gn^HdtZt){Haui>q$anZWAsBk$n)CpXF zGU)2Jb1X0?a2)dd=P8oN1ZLP;(kc>3no@Gct7N!Mv*q{+6F6D&`zmu>&l|+j zbq5_#NVy+xvZ}5nXAc^gSdinnNI>`A((I-44K;sV@1D_dYD@jkLL;yS_{cXMC75H8 z@&cPB+SLk?AH@e+t&7l}Ns)T$)gU>~|G66KU3Xk6}^5 zbu~9T`W#?QJ)jhUpSlTG?Uh0L2)jsvKV2TjE%qvD-f^#2EHff06zi?2+kt^I(@~@<<7@il+W~8ZJTs-&%qMPhO~z4kWuH_t(d*6V!xBs5 zH{&s}e8$FeO<5!)BoZ^d(r5v!EA+O|cdNfIASAA)tA^Mmae=2$ zgR`F~B<;PIhTAomeSy!yp*cTwi11*t4}*E5r>~CA`4XQkG4ig4TVNyS>UgPiksGMLV$skvOWp3$!Ym$ZMkM1u8$ zd~+~)!tSVK!{r+U2Xb6+Z|LXq78G9k4gR~R*8(~XBn=xGmFSq5bOmymu>e0-gxhb& z;xJXt<_0ctg)v(hlPc9j=EuKml-qN9O=|ySn@&|3+*TsnQbwMqHsEEab17G4e40BD ziF1w2;JC7P>CN9aeu~n&`?!C;sA-ZQI=-6`wK>CaS14{b$X6c0w&PW=%eY;;?8-~2 zefc0vD;XSly_0y0JmNolS~d@|k?yNVg(a`tPZsImCIQn;q(d=wrI;_>j?)+iF2cok*{L21iaHH zbN9g>Q(rg8eQCX#+58udc5r3bQ5m#e-?4I2%Fyl`(EcbWsw+oK?B|qdiKfG$&y~m@ z;RM*G=}NC#&g!Z?t7t$0B6-j*n=4!F)@U5F@Ka$G!PJVI&a+oTojZy> zLEM?8dfk`fma=_IkW#sPhs6rxjwptAz~TA!NHSZ%0~90$50Azkhsm2%4$JX7KD&Vv zG`o)P?fIF+`9slem-xNim-~i8drDl{MGT5w4}App8`mpaBQWPf`z4`p!PP9r!(a5T ztdYvi^cIKMOV!D}ho5C=MMNZ@1v!#w(P$^{mTYp;CO}_q+n&j?xda1DD*IP;JM!F0 z=c_zUA?KQH(?dooBXt!*HcnG`Ah#10b(uXjP0sO|64f2h;VS*~E4i|Z)!VWi&FV(j z-X@vS4ReNsMa0|!J$Kj=K0;hNAI?&}O?t;J^sQW0M_%L7oL0j_*s1o0CFsCe^GQ=l z^ANdX@ConmrKVG}hKP*msAv35kci|*kex-*1G0}cY=OuF@Mi;Y(fSIT2ULkh$+$d=cc(%Vd=SWFVN7EW2n`Av>&;!gqJjfC$+`Fe#<7 zFQbssM8J1QNxklAusdy1s5+;pH{u#LvV?K$p7 zhpxO_^Ij2S)4wcKE^SO6Y&kFYPVpQ{sgJgHla2`pfNt0xUKrxLuTNj7$7_!+3#%d< z6HYK=%QZTrLzLgh-($|#PvzP*x9=aeRNwUN$z)n-ZODLC>w&Kjw z;Z95k?Hi1U{dp@-GOL6~vlg{hm+ZNcZ;8c{@~V(?N^KS4sxUOUF`7e4g_9LwV0X8H zQ$nd+(S3Y_>&z8{;$^FwR6Dm7HF&rI&9rOxCuhzE;{=iErg_k3_7SfYqihqV8_p23 zaT(@u`rZu6ID?r?)n@ZLe7eh~2y_NwZ_Y~dhLQ{(SxrV3iYhNyQ>Go$(=)`_9hq|r zeLrrseckde8_BPC?Q9O|=6Los7i`6}%l009MA!~Vabk!2KJQv5uk2yqQmL#w)j_M2 z=%ce7sNEzSXg!p2wroXj=PBfv4|;~S&d7PQs8=hgU&K&762&O3@L68f6DWi3@ZQ zZ^QZ7mq{Ek&UykX!+Xy`@#Z9~M7M3f>NwN;Cd1Xo2dYAL+T0~UWS5r!KW@to*8FEV zOEnQD!D7A2k?X2_Vacgm?PQT(8ej0Uy}nGdK2!bf zUkN9By1Nt7*PM4=@H4PT;y-=c1cPsYjC#LmExm3DAoHX2*4D6cxNi)Lo-K~^UY6ICaxX%E4K7yeIaSadJv8+8LL=4~||SlN5jmr5R{ zzAFBI!R*k#*(3GUI$9^(eaEy=t*s_KzIF#0<6mXAg|NV=<0%-zXV(dU)&DnYN8sJR zsT~%8+NrZw1c>F{_DI*ZXQKdQjU}~uEB(EjUA>YGYJ*H4;YA&3(iF74^KvR&d8h4Q zb;54EHPh4Ysa8yh1i?7>kV#A+^^(}cM56%sI^mA~)TN%U3^1qqRYvgX^N#zbC5h!c zcNd7$yFW3$LQ9a6l#tk!*sagX7b=qFx|_G+2z9FIm#O+=@W(g&Nw^(yoF){{S!3 z`DZk8hrPmm^{JmT$&amqYoZjL1k1FhH1OjWcRhyN4-C)?ho9R>Q7IRGdgZRH#M@I} zz1Fpxa@4o<6k-(!(2@4tDi+Vt(l@yN8WQV4Kk(SEJa!~yGF5DF>Admj;}WH%wWp?0 z%eZh+-Of8ynSoZ1S#U1fINueZeW7tv#JZp7w<%-aJcsmL;Aaq5$_Ceg?Vp(-!~eVh z9#gUyl}ByE&?qe1h0?byhcx>+u$0?g>nwrwxMYHOt$8juBL1}skUA;(ir3p{THZI8 z)6}JToP=j-#ke6zC=!)MP&ZHQ@81YGsr~JuC$Vp7rWy(>m1(9`|da-FDYZ6^+;hM$-g>KSfF9x@wGOE=W1J2#Zc5wkR?Rk_5J@=+P0(xEDq z(ubxhQp@3~uz#9wv(9`)$TO-;tW7tQ*r({t973w)eQ7w?3hXnurbWxWpmNh>8>zf` zwIcUL%FUye>s}e#rU*5r@X6OPpenynmX@&*|CYvMyLfS%VL)J88{5)Y$Uw-m&aFTUa zXk=y8nwtJC7a(8Awz!s;l=}ikj;>rQc?=FxX`Z zOZ2w<5C}1YX_|FZL(RsQKw=+>rwJ((#JSvs8ke6F3R82#R==_m-^3FvHI9P#V@EP$ zxYDf>_Dah+dQR#dip4BfW}Z%}IzJqLT&q!Wh|EIQb<7vMB|c1_r}Hox+LcP=CsV=r$EU`Z{dUc^QpGCaC~qX8$D4eATopZJlVU` zQ3QM@k)&@)*N;m;HEEu)KrjoD2%X5W*xw^cvnb#3 z$|zg$P!-5#&iFiiIz2yE!XY7+0Kl`uZK@}&*9t9?0Rc*I{|7v%LpU#{AXWB73ykGl z5=~qyTPgxcN7U5Y++eE_ z$&BeV*7@n&cNfrnP}T>LZLReXC80ToH+s4 z13I(i8fWegVHgqw3HZ+LE;nU{g!$&>`^$EgRN3ESv&0C8L+a(E)R|DC@Sc`bLKE^i zeCKIt)$M)fSL=-GtfOjBa(>7kqsu_{_3J8)i&d9W+ts#FF4f+Txb~~2D$n`=!O|;^ zzFHJR)j_kmrm;yuJ4j~~26AYwrN`_W7%^NlAD4{y8xUuMd){FJrt^>KqriBP^CiAQ z-&FVM^O#SLgdO`k(=gpKNQtPJ_m36B0w-A@^G5*_5X=wV(}L^1Ta0@d3;FWrRC(Q%)#WR!*<)0WJ($we<>*1Pvb#X4|@$iv7;!LHKE6iY# zd%epfTU$>u6b52PTcu*H6h0d;D4GfRzErQWs?Pqtv$m7XQJF;JBMGKXp`eeqETYlW z9$R>C4=<-p@6b=8z~0_{-mMaAvHR=8In%o$)uzZ0ZTxTEh!u83N{FWOFq)$v{c${# z2Z9prH#TdXSFJ5g1Z+M3gfBiGKvx;jbuG)#90_EKlht)4D;c-E98?FoiY;PZ%w!QC z_L`b4#&2S(HlIb`7-)=gHTHh%-cB2vaUIgYUfs@qJ@Y^za_yce7o(_E85>Ps^*@@2 zCFJ_leulb;=nk5#Nol*qd6}IIDtVgG^v>&y!u2%4`^!q?3d_G>9y7`@>Z-qB9vMx7 zOx|?a zeCb+BYH$`tE>4DfH@L%bXSQz=UdcJ>KR{21;ZZ8QcJlEwK04~FmP=#|e*YD}_b~es z=gUjn5!|{#!>vn(IUnz>se4PwkJ&3>a2K9^@DGY0&$#R<JAy=k~K zPj@C=i$@?FYAm3aiKjzC1|TRgAgIpATfNfLPG6S9&H4bG3m}C ztv55sddPbC{LNzg2EVfd1Y>1rRD+zQWP}u9bszFD6SO+ zAE&b0lZbhNDX1Rzx*BZKa;2z^1};$Dx_GMH6Jj3)f7K;+0kvVRTeid2OGSwle7PYq z@U2GBIANSFzT^2eLnFKd;XF|IxX*fI>H}>`fJ*miD`EdBfnTk_;9g|moBt-twj5!_ zlJ`}D%{yC}bgV^Zch-~Vl%tkG#6RBZT-kcaeg<_ij#g7fW(V9=~@S9kJk<@AEB5^S8liuo={i$hm1 zf&vSVuwOq6o=P>MDeVsjNQ|vyns-H?rM5=T*7u1?a6DQJg!kZ86Po48eiZl31+duh zTvhP33`Y~P3=Ev+7*}=Ge{b~r|IF%O6knV`>2C?`!QAdG97|u-FkOeEyVtWChnGC! z*ByQ2sQF~N@U>9FH4F@4u}xRe_!p@2)}&qWAh%OwL1dtnP(y`(3*H%D45nvQ);}aC z$M2Lhu^pZK%5l`%$`)DKlP94EnzbBohT0hRWVU2H^g+*--=7j+HC21Itr1Hp`S4h;+tHfS$)u`l#l7C(!jqr4 zU6zx6K$d;)edZHV$;8A3mBCm^<6;w?Kb=KMOEg?`Q{;GwYPxt$%5odDpc>p=EdZkE zCW@oK_ADYRXx`=iIf|`%b_=SVEesuzCz(Zc#&kDGwN(*Lf7j-bRpz#WD59l!h!5lY zY@>buxFXCty0}RYfOlpz z@{+2pqa3h?r`2wo@h@&PPp$8o@stg3|C3k*D?kJI32fF{)mDz+PFd*N$~c@D|2n2h z+361ynhCT@Un%Im%M_^~DaDG>>mZp@bC~wP`Mo#EeQ`+>g4)44r zoPrTGm$*3fw4<|X6S2OCk6U<{B8$@$xHwvnd%C^i?habnBepUScC;h_8ELuPDm9qS z4o#Duhk!EHI_Mj~hTJ;_9a=l@3x6yHlAtQo2dTBicBwg3p~_(0eF9syM<7A=w1RcX z4saai)Hr63*S4HhJ=4N)$-sezvkjl;>XXm814QluDK>tX5c`mDHUq(5o$%xFt5FYzY}3I$yxs#>aRyj3SkMoH97r@XL<;W=s5c z2pJOzNUd(|Amd=v(r+pggrqlz64W4Z6up9#D>}R%s@f9?PBIQv11++4*~JDF56oxP zA#LpxPN1hoe{H`~a%B2m#B9IBCBcwHvXDlNkOL~f z9-~BgX#tmd3 z5y%;i%&}SAEB!siIS%_ZhqxFx>e-5k_=JUpCml=E_!{I!gZg$A=)Y^f)La6Ug(AcO zEX`C24ZzZzjrS;aE{&)w&gDypdF+z6KQVdrDT%HEo>}SQmALMj$pVo9eX)fN4(!| zvhEx3N+R4|(N{PoE^bTPmYes(M#eDV6y~rJCD=~bfOH13zT^P@%~NPw3*8fyfr@Zq z)AzHfPQ}ui?JI^D#M30^g@mq^PNu)pg954~0%5~l#n8pb(}BHVvwg>bU3g4zi;kGH zM@8?zm4ShQp+Sw)hTjYe{()SIri{)rglW^5y9R_6my5VH0CY>(nN8*J95hG$Vm~qp zfvDWarurr~l~Lcrw6Q2TR#!osyh$yT3Q>aBJ(m?JO8@WP8NO|-Kwg$0i9=ulrm6pX zVvbu`s*S3YfZK);CS1k~&)b{39gMY-0~-)EEKM#3>ptjf>z`^E^Z<9z(D}n-K&}}` zK{6~1%4rN6QrpA+lOOpj9WUSuKRQAQ%LPtqhlFyj{(+em<#e`Rru>D^^-uYFY>4e* zgQp>Q()^c*ntBoG58$fyQ2$yx=5Yq?lN0O6c>dqJO_l+A^n}#kbVDRm#$vyqmhTog zLWo#fpN>xT@$xJV)j6se8GX(z*q7QD!qrVxCJ003{|%kUG60gp(kKc@Y{3*zV({@_5~paD~f(l0&zH}w-B)lLn( zy7k{P0C51v2eqvvV~6@9qCT za`ZC-cc*4%RQeO%QhVL)9@!eQDvG*&rl6!AxIN#dROSDl=;+@DN`~ZOM)YP(R(K%vKk_rk6 ztts80AG9LaZ?s^K%qlj&y{6Xotomm)`ukD>5@bIdj^;-gk7&atWj?JMf?UyJ-be8iqF|LZoL^Je%q5GJOLOHi`9x1jv|7}>9!|w>w*Wy7B-S4w zgp5<5l|^_@pDQQ$ccdyW)n|CkUWzQyIy){&`&HOb7(?qD&-*JjYyy<1W%QQv|ePgRmM#3)O0iJTAt> z#vAO7H!?m8&NgLjExyc6yXWEMg;CCs&k>baYINLcy+2|(<)%c6fBcQTc>y1f1o?s1 zfmXTX4G>Y{NW=x%()sGsl%*|P1($G5^I5<`LyM1O@NNOO9KB%Ow$ynD>H#=-lGVdQ zeGRfTS|G2L&0@E^yKA@h14hsKo>4DEYH)d~5L_WWvD!7IJdo ztHTB3PDyd`V2S6%1rbaP8M-I`P3Rk7R2%c7uHt3WxsvOMy>4_vM)b)7n7?NT(?LTl z;`sV*Nh8wG^u>Ppl2xg{8)zaw=o!Wu*ozvh;_=`b_oL!t#F@dryg1;(KT-(6#>s*- zbUZ83ktwLCQZ^xthtrs_iNM)xmh+R+qA|~SLU1s)%r1CQBN6x<{o9$|^kFy*i6Lqt~F_j4n=6q25thww|+8~AC57z=qe6mwA z9}niLYmUHv-TMDqWj;#6V}^{Hj%NA3!ei~tjji*d?v!?LsIi4f;dQSo(P$W_pn6}v zn>jOQl+80n`GwE*-ors+Y#n z?26HIThHz5MlzdBcYaT>_pT#IY9A=Hs#VdB!Nm%=rFoCC{e07LU1hO5{*b0-;OMS7 zqn_dRQ`J>>(Z0%|Z=KA1MoAcT3YF7%r2BPoW^rR6=gUf{H4Fj*vdYFY`XgiP9%x#n z$k>WAEQ9@@5s`0g82+~$#=r0tVjzK1$+TK(l&Cfu8azP@(r-D~umbAX-|bJA#w|5D zr_z}Lf<&SRo!#C2WtvTccbveuhHm{dsttS`O69--IoYYhOqq2|6_i^~bMXO+TqaCh zMgM34P`&mmZiL}{iKw*Xb~!jNIDLEEd`;|nx}{PgvpZ3r>2T;)isn|A?)u>8u=xIZ z?~3ruMRcv0_vVEB?6e>gzn;EkTDlx|5j|C_`6}?3^cs7$?4=a42EIKu0|Pi6W^++< z)$+o(l@6x?lH*gq+$%V^_@kD)BEzZ6y&uC~qAf3x@(!m6(+Pii{m-s|jYl9hS4EA6 z=j9YSHntHoR40;F;=P=pHx~AJKyT%|F!$C#Qqpv(x^XoDR<0*87Uxz6`KE$CzQkN%bBQQ8u;C(()+e`EEPa&U0lMC<7qYh{=RZ zV#8cikCbe>A~d`q%poAOYbZB5yWfmDal{8hK!)55HEufRd+FVq5b9b9~nfc@N ze-qs&LLggn2JoAdfPL4onRwoA#>u70ikEVg@x|OCoW9@aju=oz@|-HuRF5R!jJ5RE z;vli3{Z>pMBFkRv6I%U%4VI61w)11PnCuO%;;c57Sn+yfn>O(eCvr?0{KB~aKDyi+ zlSwRB(}%|LTU04irIzD81(!C-J4Yp&H#)JkpQLhF$I6o*Gcrj_sgy7$|9lBFh}`o! zX^V;&#dVs9sI%XnDQ5^vq-RNd1%~E38-=Q}??GNTP16QemaUE`{xK00wm}zQVR;+H zdsblqYFqx)vVYWM*m+1UWAUj-%RiaLJ8|auv`G5Vw9%GWBv0G>S=eaz3GiwIOV{Cv z*DIc4RG!@O?}&ZR=5VwnOVw(JtU0|NzVJM(LRC4B5WTj4hGIS&ZZZgI1Z#Rax|1i? zSuW9{@G?$=Zb)AAw48j6f2%SK4!4}G&?3U+7G-={O!9;n0vp<>F>~!U2lgtZ)2`~( zo!G(f6RAv{g&Kpoaw%h5UrmnH z*C(@at? z2+L6mOb78>1g!9ZEnr(T_>^XSvxJ%f5LNQU+EVr|13!_f+`5?036AVx(gp z@2`3vR=g9qF88FGkkhvo%K11hxf~mmX+J2%5VziUTU>+7G8<5^86G+;&i{q<5}>__ z{{TCU4RR<_Do!l5q#zMc@U;*kQ;%5Wi2jMQf0{NLlIGdo$c3u%g<`8-1D(@W)JNxm zYk{HS!*(077ez!Wt2|CK#axwZ^+lVPPak>^jH3Jl+Yz7$FaPtIX~cu zuo1p>#f8OE)tvN6D!V0BhgCxtfxpt*_wV0|!gG((cVfkIfM6=Q3gpt2$7}~djeNw< zd5*nn+ilCPDl>12C;`MKUa43)1vlP#*?na)uq=a)ZR@SA?`} zL+R!igl|N>HrXo5% z56lK-GR9fJL1V%tq*}cu)hBY(5M8?W(U_tbm_m2t1Kprsq2&&&9TUYX`Dmq#4m9WcJmG;2M6wgV(5EVt#bgp>?+O6!YVM4AvCxg~Y_ zgmx#qvl^wQlavOM`hkeV)?TD$uIHxUhUJT#*n zM@{4@LS01^TZH`r(|-Vx;{G==QXKulo30`N#D6O zzxr4VQ}u1=$u(uNNK>Wk&^Em?lI{zK%4fm%2TSkXCUVv7WXWBmR|^aha8+nyzw1S> z3_>CLh$ur&j!EK&T=YWiVcWxger$(a)J1R=WOz5(_w)KYGZX}){oN<~HzG>{sr43? zN-S}1#V9o>V}=n}WG}3~ykWa&+_)Af$r8}eB~WL#WL$txN%34>}jSAgs6 zj`g8pR$x*@sj$d<;;H`-N^FKMEK(`10kPBep)^SMo6xb{jC13&F|_z7^Os$)^W} zRe8Zz$SN4s~hC{ya6cwv|L)6M5HJkqhYzbq;qd74fIjBKNKW(23AYJ0Sw#wUDOVd}awi{3MQR8IH1w659p9ZhXM1 z37%t-bc{%HSih3VcyLAJL#wr#fAt?p-XEMQS4!BP!EA&lN*Gn2?i&;p+jDljQGS$UodK=K^i z*AH+17l8hrKv`kdrLYl6?73-E7g_o!uT(rA5J~BBV(K~Kq`+i>DY+xCx|znPUj>6G zCyxcPvctYG56MXDA+Ut~;VIFxHGa7Jb=B6Pwb~Pui+;pvqwT*xKbl(oskHiTHZ#*! zKQ@$lU%5T*IW5mAovThW@#9>je$xZfv2$_(L$QuuIAk$ezoHx;srgJvVy5vB)6s`n zSox5fh z@0J>`8I|7|l(=tA%^2Lg>#!(LO~jHSnMI!U6fCkS&fN_lXf93adeK8_r8CtlWoslM zIl7OsAEu;};J5nTb1%1hodR!$VIOy0i4j@)4sAhUvMV&sR|zgL*AJ7@P&TaTjr7y? z(*kV~{b{FQCys9rES(L`aUqGL?x3}Ag@`XnfI5+6h0#d|Z)SY_o2Cn*qpA9~UGtHn z^eQ>c%M`%vbgpIl-^+Nm{Mo(_U%wU+2?KB{x>31!)HpjEBX+nGTc<%`XaufBaD zkBIP4XDr#yY%e>XXX_)5EBZInd>YKie_qgRAoxpQg|eNp={LVeb8>90*5{W_)gbBip@ou|twS-MwMhT)`8AnyE;GnNEH7JS&t4 zMODfR)%7=kKHbZQMvS?fFQsXgKS?FUj2VR}EJG7Bzyc>^H{adipX~s`C%JtqskkX> zj%KNk1rCr=XXV~)=CNd7BEqaeuyBd*z|3S$yLl>F2~H^vFJ^Y-xMK{wBQFUzw5q*w z9Xv>iuGDh`mco$_I!n{65EBX*``oIC>$&xN}E@wZ*JTTX~ido5X66Ae&5jqI?09 zgUfu4pCSAsdc`zkw!Rfz4Q=gmgF1Iznw*XeyAhN4Cfg0SzKd|cgwa`#?1cJqGaCl`cFDmiy@@LlL_jApFFj*YX^p@AJv2aq zKNfvesX>9V8XIZY_7h_(OF=c&E-LpdJHw~eo24<=hQs?~@8zUx)(u>ZZscMVGVCW2 z-=>Hh=B*})d~E|G7V{C%RLKYdb<6W|Q@$4v^2t*y7)Hjy3iL6}frn42H_;W@){o+e z&q+8)UGX}vonyLdWujVLS$;ger-?JXlbeu!n6Rv0(Jf&C+-z)l>&=g}^W7feRCxYa zxJA|rqbwE}!HYNbOLo<&C$+*okyIJ5mgL2FA~Q~LS>H79`Naa-F% zV%5%Bh^6syBO%FlRvA@pofaD!Yps8(`Ga|b=VS|21B9O4N3ZEY0~@vgZfvTV-YTPH z8MF?CS~bVim2aHwM@XJukJDgIIZ&m<^%&1Gn7S1JqBJj?B>!b4!z1jnNo1g@&ST9n z^=pWjbGr>K2Q@naJ0<7&>lKN{PzVFACjCjzZalET*h5LaW(^bvqk)g`8Ki-|aDJ1j z+3QdR2>Y3q>(otTR6GzWruVrGQc142w{s-X+Zoycj1;(zF>u;Z(~Wz9OmgnEV$tj4 zcjQN`Mw-%y3}ql4FHc@JhVpVf}og zB%k3vAnfYYyUHS+{A$8F4v{C+!;kas-P`!#fy((zb`c}!t^TeIt(Mv_&hwW9F_tgvmHsfM( zVrt6ct<3^mDCl)(t}nL;?Y}xgY$gbRsYF8gJVs3~g%Hz`3AuW?kDGI*eWf6_cPy`u zmYAImK&mw+@^v9`6EPY*FJk-wJ-^*B_o^yO-7|vRqxZ^#$W*r5Y)#l&E_dcw;4v4s z`=6=@pzYnk(JQuc?dZtYrZG&(wFry#R)p+>x$bpWr`0=fB*-u4Ccbl)>s4m82s4t9 zYnJ4HtcyCPF>&Yl2bTD?!^zr#+oW>%PYqhXoO4B0+Blt*a=Srw4E+~)92QKlO*V<6 zEiclW9%v>muK8UIH^tj^e2tMJKsi;gRx4|@(BaEV_j>agS6-1%JORi`<348(VhHt~h6~gzLVd3(r!0#?RE3XQy?}$3(Zb zdnhTSd(l87O{VqcfF*A3)p}T-3*7g4pM3;~*yQBPmiKOcl(1Hj;Ch0tZA`IDi@!wR z{;EzUIfjO0onDl*+1x1+LL>yE3WnF1E_H)6fJ*8vAvCdxQgsf~rN&wyZoS;O0Eoma zgH^U1xPLr7J>CzbJ1?6udmql!bfXfZ^3GmAoR3Jgc|A(^Z`w7!r_@qn(QlyHH~b))yZ7Pht?`lEH|R=#82D-p>q*&YlzpaELBdYwfHL$4MQ2Z-i6N$O&FUL=x(ITSWAck}fB9hWqK4%OVzu?zVUN)?eR z{&;sO!(L7)mDrocX&>HOs9s+-IT=W;US~lAdf{+;wv_~r3JUp*LTp9HHo|+I#)*tm zHcO3I z!qo07GretuQ%Uk8}(SF6m+`wLgW= zu8~nB-7w|Svp-=aOkSWvM49)m@GZs*c-6WQL^x)l>C*VT>W|V-=)6e!D`*LXeUl4L zj);=hjSlYZF4D<*&=RUDQ4$H8|K*+lL-?K{5CcaD7d2OHEQKgz`)UlW1Q9N>(LKKT za$3F0Vve~mcQ=z%k3@u^0kR#^pG}c2i5=wf5}Ck6W5&t!ZZn&mGy0V=SzmcMFLk}Wt{1$+#N-1y*QTPwnvl9?Of*Z{CNE{P};N% zL8JFX$OD;<)y96IQa|WjpHGgx?$BJTXOpd~utGp^FkJV-d&sVTD8Rq+tS1~~A1TVr zubN4Cx+Cz7HHNI34%tm7@*^ZfUU0CK_{Swd*0aJ9mB{%^gA((4<{G~F;|y00J1QsL z|B=Uix`RG@=fFJ9^68UHQ86e*kqe}5yxti?MP>Zm2g2{ehZy8}$>)O|5pb<85)lwf zx2r(X{Zt@Jh;goOs5Rh9uOOq}k}*2^u^OF6&(jV=lyhN~{g0v64ijjd89Ti?jk%~n zq@}IG-f{R0r3Y1*IkR)eY(7)QCIQKAwP=D4`<{!Mk8hcduF>_}D9+&pP0ren&wegx zU%ucDl_i*|Lu2+Ktu9X&$PxQ)!6C+f%`9(wnx{L%MT4KIFR7V~yz6K!xfq8ZMz-hw z{SE|ZD5KTgc7a4#$vgCXM33(7wBZRkQBk(ssBW?>8*_VQn^#PH6}_tj-rnAEMR+uY zx@dlg1n3wfkE|M#I=ilGfj9=6gycic!P#&+U+4mAQEab=8c?Syr7uLyXy<99ZTC=oa&SKi% zp$H%7CnM-ZzYel0ICby(W*%1n*_XWlbtI%-uQ})SD>!h zFu{|qZufYkMowTCe}vo*JuoChWdGgO0zZ5eBuVNUfXZ~Pu1j~qhAz@~j>D6a;_q!v zbP^X{>)HAF`9miHy_GL`Lekdp_!s`@XaB|(GEa8rg@o7AdbG<6CQcg_;VS6=wpadV zVsrQ^&^XvgkbvH(JuP!kd-MKgZHO3jgm(l}CtS%7%kNEPvO7#y{5lar> z5q1Z2EWe#7T7U__UTCN)EdCO7`D?-awfJxTqB{uOucUvoKZ=jizvvEly3{|Pit`uU z(UZaZzeL7=fEi2-fbP(P95DLBD@aBHbVpyM@yP%3ZvSyST_WGBzsws}|2hmPVt9CX zP`;t~^ziTq$Q$H{FVIUxGo3kyq#kKe3fb%;#(+pNmo0M~jOnwk!LRAL4Q zG-T-%qN1rrD?Zz83v9w9zBSe=kjscZ-yVM2@*?*}{d*uVVIUgIupqhs53@U+QxFgo zgd=FK(h48`c?Cc8A2f30{MS9OmTM~vRtAOUEWcjiuJ-+K9m!rXA z7=w0QTLt$~gLAmU?tE><5ayl7br^<;wl;tJTjSHKEB1Ta!JZ!K?X`hArq|x5XHzd7t)#&L;-vs!VQaU7!7*`yRRmHfLNB zp??&V#7|q!r&QLPuLuft{LnzgU;o*i`dQNdB~rv_Wor{pAvCh6QXv_!*F&=Fx@Ppz zakG2PD?AE7e{9Q7khu%71y?P zfx;m`kYGWAySqbx!U^ty;1E0n_rl#Bf?FU2cXtm?;qLD46mpAm_TJ~&Y40C+Z@YY= ztyZ;atvScEK6>xtw}y?Lp0kd%y~566p7O5&w$(uVp|>j+nxjI=Gm|L#wls~Uo_UGu#=zl@6-?7X{>DA#H_9} zIC=wN*~>7AKD&bnt5AEX+`;J;PJqY73pV#|LaK7NyOb+7qupV<8UCMJJ3RK5hW=BC zy(yg5`U{B{gZS1hmKhHVC(j!rl(~#xkK5w{g<^f!_S?^;3tt(SAt;rvr|whlfyuL% zz(ZdeB1)tuJxVqcNTbQDNH+5-OiWB%dcjF;5RTaE8z}vJ!{M$sRU^9!Vqw@bAK3T> znVX|%kC&yRlRI;v`S5Zb`I0!G4FC zgW)vw`6Stt(5_;>Lg16KveC$~P9+3Ib=ms8b7lXN!*Y6vmjROh-oEWYKG;frb`t?* z@YAc+SzGg2d2|3na%@CFvjf=t? zoU0ePU8`SGhBwRHR0|Coy1))?QMa2mNU3J|*tpoukX~YTLwC(G>;b_Q;8L|;t*+-8 zYzz0&W|YfCWsT|C-fTft`%&!Q#S0`!!XQ&D>9M~cB=NI2$zn_7G+2V312q90;J*U* z=T1TFT&1q?I4$^9@)dt{o}2o63J|PTI@aYI*om9``0_aFg^qJjv?ufJBV&1xwu#7aeBe3t*buq zF`MjS0^)sZD~k5M$uSm11_trgo!d5B2=E2nV|xhq?!@gxTK(?){P{2)&K+~=eUE~q z_o&E83-?|NjSkvdk`_rUF*Gruv$-Y%Sgf~nzTF{NcGu zzr`JVWrOkbW-Xat0tJ$=Dl=Y4U_95(Yz2kzOpAt>>eeWOQNuVgny@mx?&7=H&o?BK z0gi=xFMYFt<>QQ8zvpWRCZhum0uF1I!lSX}%N5!$lGvF6Zg=yk)35bfDz!}kVPTp{n|O& zdjDV#F`G%4S#gyZMeXHqt__@&P5DZd1^KJAJ|SY~$>ep3o!PjW{$RfC-~xU`&1u@M zZGHx6jkiv)+Z`|R@Hm`)@gF|DY?7U;Jnss&9yY>`fEaPA3V(%IdA1l*UEMxDNn`>6 zYVf3KZ~!!5p88RRyiER=Ug(!b*uS-I*?FBCFE8 z+TZ8T*jNNPW|`J8Q8`RI@_YTY6K?mD_7>S8DgyvuZb>W(t=q8W@z1b!_s1pF9xWdPbgB*J)=`N-ue*Tc zN%C%_sVNex`>Va9G#haongqe=0<|pRpW@pDbSlM0&9tSW`my18GY~x6j!PdMsj68L z?)t>J)`d^a0gvZf^&EgZyVYfuu<=|SHzDJ<(iZ|6oqt3 zyW*pt7fUztl5)f?iMud1tTG z6KnuZz}zjgSWcKt8hVRTo6&pZG!3B{4N)N67Yz61+}DYk2Q8+nF%T_0*RIMR3fXJ`4$cXWZ#f; zJBnx`>sta&^KL$V5wT|0N@rJNYRJB^w?HDxyI@Z@I?Mj3T&Q9ucmF)6K?s@vTDLq# zB5r7(SG=byZ|1yq&pWjVnyqQo>8-HuVvB@o_;7ovGNL@gls-&V|E4RDRC`44{>H6? zN0t)HjmK{DyBWKQv?JCEng%&X_;L7C9rWi?cNdqPk~+#L*BhINwUX-MU!S1>cAMoF zx>eH`|416z*&*7C5sPdK+5(JXRDrw6W_wtL;4`8W#CSS{&Y3T_;_B}+F3Il*^-U8< z%d%6bY`M+Ps?U04(|EgpG%P;n(}C3Yjw%|dCTy{XU8 zH0!K`-U4`Ne7%kCXz{Y&toem}=j$@U?;W*nm!ay%Bl@w66FU==6!{wN6h|Y@3tXF2 zFN{WJ4NHUs^xS4pr$B>09$IMd!!x-z|n*vODV zz+bM@wk<5~o%8zXec>oQ?k}kn;C_=R!gMfQ7PB6$(C8p{__$~9YbLl=Aa-4?ix-S_A#BnAaDJ31mTqn&d&{v6PXPJq6CIfUfmQVuCk z`>Z=Hya~VGjA|GmBsa-+giZam6A9sOj>XKq0$itmce>G5(}j{)SK5-3w;=!Ek;K( z4$T+6gxGFwvWZHIuTNgNYT4T+FzI5N*%LsQA|nyeYmY=hSWGHyq6ZfoP=FQ7iAfXCdj)Ra)k$n>0kL%FR= zBGe&Jf$#RGEQ(9Zt8bpXmlzXPkI5`n%}B0y#-FxveGS8xH>#c=J}K@uBz_k2+nvkB z+UGKV(hfmK>F`^B!}5S{IhWnwYIGbJ-w}tE4U1E3NL0F3W?jf_>YI?z^Q7l?WTgZ} zz}}t!K&>YujY?0q;;0se-}GPC4zW z#b)fV-rOa-tKBOM{Ja`3#D3>!DY}bf$GEy@QQp&&6F;A2c0wJ!8!FvefXZRY5 zgiD#K1lG+{&lrZu>zOk&b6~~y6nI6|{~h)D0CF*NZ%-mF4KuP=8;e`4H~uHvCf z8o`X40l&USX3Y6WVe@o|+fWFL^z|=T@{|Pqhs2I>^FF{7y?zA#3mb9vZ@kxRM&DI5 zV^FY;iBv@8L2@3{XaJs$4q;TfHyPZ2p*dnw-dW~KWCm~$ytrHFRh;W~Hbo$PhvDDk zh#X<>lQAj0nGlJ;BkHeTM1zI^sjS|=OaA~yrTieL`SE&rfb<{0E)E1m3xQ&A`U8-a z#DhSHu|1CKN`L6>x?fy-k-k*W{{aM#Lrh`gI7%_1V*icOle~f4$lz6&&R|xH#1)wOP5JEKIPqT@4-zv-%R#!J z-{t!nkDEpp`KY09g#;qgGS<0aju~FO%jEaj z#$l-D$68@TZdT}BB9$kspZHi zZ_|(`f4fOs;AWQDo%$3rzf>Zbz$Wx?q4Is9LyZ!^F{^`pCbdm3&`K-xk>a6NhM4$l zq+}sTJMOd`%``PiPkIT&JT76ilHh>K&$vhL#1S5wH$vW#SIb*h^}d`Dq~`8W));=Y z-kEKHgAEQ|!g0+QkhKch+rBn7=OAv$ln&FIw?KD#Y_1vanBjJz0J;9U)C}MQ&tb<> z%H+yLa)B$SdIeSK`ZLHA7wb!BCWmow*^Q?cR%`tO*3`}Z>2FkYpZx7}Uh|)nT(8kM z)_z07_-;E@0q7h`cTX-&aC;HoZC3FqE=0NTUvhlJmujpmp?H{x<34ynQR=LR|AWA;c zz6PxGQJ!*VA3x~xz+M5rBRc-R_Rhu*Gwgi{dp5Ml?kYAa&%*10+60EE)*>>R1`X$n z**82qtv{K9WU_=8(jO1sVkdYq&{ig5+DPZ_*dKHXH&mW+yJ1(Q)$NE#WssgMUD1_W z=?GBeQ8~|?{B)k_wV|a8WF=tIT&m)_Q~bR{*-*Z2AN4qaX-UJA@gyG^X+cr&c2c?Y zq%TV!1uCV{M#mB?{5!mCP5^M2kd`toYDJ_$9`j*ou50+G`PQbt>Edyj+sRX<(-h)d zt7FxrhgtAY3a?Tm5f23#e+jQc0>I^h@A@a1>Dj~F`N{j2L>S@YiT;c^Hl*&Q!f34 z$asSLB2YH_kRXVVZ3@>y3?e8f+8iQ=-kknL9^3fEl6?HIssDOb0%Zgdz$4&ny|jt@ zR&tt(7zcGEz*v;>_R>izzAn_r>@;QWVSJu=^E^-qq%eDBZ{jf<5oYIfJ!rREbZ(*r zZe3*5)4Yn%*1oIp&nJ=2U~Mz9=33OL6Ki1-3b5wI%3o$sZT&{a(sjv8@c!9dbxPE! z3pjV#6P?EB#Ze4m1WuHDmk*5@_~Ra*r<|mRcku0Yn^XzNwzy_6=m>ceeA0&=kvqqv z-P}Y70+uPaBy0|(t9cS%{dB+TcdN7>Vah8PZj(qAplot}px5)d&I%{u5gE^wp+?3N zJT~Lj{wJ+^`U*0`Q@_)72&(H@8}?CyYtwJdJdQ3`lg3v-@kdP{4J^LiO_MHs%}EKy z$PBYiDaKoNl!3U6(wRQFOWD;B)6-Tau!v#;^=p#Lbsya7jXpue)K11xeMcZQ-br*v z>+pjv6I(v^k`86~O>x)*Yx%5LdX$2(r-+st|8q+cLo=}FN1WEA;YD$>82Q66P%;;A zaJrfw-M@TjjwKjRX)`Fs{$6CwhJYKTltmxd|G@s?CZ^TSl-!-^>FO8HaXTYMPJ0-B zgaksbz3p#oP;39ggiPRoj$_-*);>ru{orSnjoDEYRnGB%;Ll&H-g4{M(SsGqh_9^E zR__M#_1)pGIz7uQZdE^u7{wShE90TSb5T<}plicjwmF_f=P;@CGU(|(S7~cLrq5Z2 z7XPLUvXjmz@a8E^m6++@!}S<)93DG|yXu)tFn5hKBmPD@<1`e@@jDeqeifM+8=%Z> zxfYHVUXS`PZp^*0b5h0Dd7eB6Er(;Ux*Y;KKv!%{=VA80gtbaVhcbLx9vrsj^C}W; z5tqvW76ftNjY<%?otyP%OmtMLfi*k9p7?9*CvN;?(_Hh5ccy zh{_Cl^(r|D=>+;e@$||hm*h~$B^ssI(;8O#0S1r%wl0ood z{)@)NSH3xnXX^fs!hJ@nQBy~&LOlE}b!J_TzPG0%TB@+idaeQmloMUd$?n-kyt)~n z2@02O5!aUO0ETl55tH>f_9zzFu?dFm?K% zM!Gq~wn#55F*6u*a)1ahVu3e|%lr9yhR7h8%OCpw*9hb3cnACDH#BPfER-im8L>%+ zA5DXa%zEFi%s&qlV#%bZ#xv^m2dBtjGs6p@2@NkeL`$m{$vjG=N@uv=ZDl$Zr9U?O zE&YcqJeRxOUHmLIr}VL`pQzZ`5=`YY-=n&P+(lEx=ux*N?UGp>T!yz&A??*}pMH5w zq?nbS+?=6T>_Wi1CZE7oqP@~K3s175lI+l_^O2wX<8Q%UQ4@m*av}AJu^8l`Bn?gp zsvjb7>6^TsS`zTqD50c_D2-%^(;uk%3pj4be?|I@QWHcOYv8rg7M#zFBHYL~6zam` z*abbjwi~$neUiFSd9S(R3B@yHFgrCdO=s`fS0o&)(bvFl(qf&$5L@bCj}ho-ym10* z9n-zw-5S~XL_X;Q>Q&bUT0Jaw(~y~Vi}(MmZR@tF^S>o zJ~PBMooOh@{aIlEcVo;qT2`xZj__TgGVGv(kD`iN1g5*K-@9md)~dsB-HiiqqPk#& zq92;rZgP?EW?Cwr*!bnj58rc2^62rsR(S<4-O>{+f-F7|`3O78nty9)U^CHdLx6Rv z^^Bkv_aiwhB32A}5K3Q4TcXTb_V2Cs<)uoKEyS5p zUMoQPS!n~SvGg}0C@oTVEKHJb74h#aoewznRRGAtMPn%w^PDN#Fjk5m@pPy?8g+xV zV40aDYSZGeiru}RhmJ04sX_}Il{Wwp@CuC=I8N+-uU8aSFd9G%k3TqJ+-T4L{?LPc z#$`^ndrg>^wKif8F2UuaO|Ou9twk7WBZ=r;R;v2Fk4dmBE%l1kAGW zYS7$f(D?N&VqSa^W~rIBwH5C^kv|9`nl}?}TBx+T(oxXu==}KWY-70kONU;iIhc!G zCB}d^^;Tk9Q zFx+5Bf3%|4>0qUuG<^zb9yC!@al}fPP4MR}Hh*nWy^V)X=greP&SQZo307}DlQQuH zk*?VBi z*#9+enLsMUa4~T{%W^PVd{w&A7iMb;uTvEY@Gsb{q$>*ehOnol7MUzZ0CA@CmM;C`0dKo(w>-A$)6N)qboiak2Z@)d!Z z7Mo!<6hNSv7}yM~kM(`!v)bO|@Opr!nT7kBnf>m=XFZ_ds^i&+j_^v!Pt_@h@wA3x zv^Y__-9fh4Ry*u|^-o>9lZv6dG9D5&)zMW=l!~HNpkW9NqK7>L*CP?+g51_fh)JXN zg!}WPbI<5pjbxnUD~rB_Zj3PvKe9bVPw!%?pXJ&X{LJ&+4xP2gSviUU>!%~^ihQEz zzO>S#40^Lre!96Dwz(}w@?keOWh2_`hn3OOHc=D4P0{p>xe@Ku@F}-Jxhwx4fLddh zrjgGv4PxVaoZh6pdvDGYRcvz0F2fNU&3iU{-IJp)z~ozl>Km*0y5!nbLs!pvy%(SS zZ&rV-6Z$jzPlc>#?VT|sj+*Zbv(^5-cjw>6t#|_!V({*^ovDN@>x1R6cL4`-kSjDAGa0a0BKzb7@w}`4^exb&a%@UE} zKNnBmm7J=M-c1rOkZ>SnDZ`VCmJa`J6PRGVS-MhRbm`wXThWrvkpC3U4ZX0oB+y6P z!DSXE7^vD4eQJ$sXI^SSgk6xLmfKji3;uNc`L5Id{&4XbcLX!`N+140qyrWwcKvKz zMfSXGwmZr!ydx87D%1Y*vkz2)=}xl+s0*K3<%g(aHIizks_jka9ML+p&4LQ!@+y2; zR~XCLx}1va;M_iS|Jc4M4_kKNUYI-H=HUBfWn4cnrrVHtT|+T+s^_hB2Z;V8mkuKG~{CFNC7B{ zw!@W0d?t8VaScT00o_2T5-#TzmvbzZkFcYGBCk!WzojfiyFvT4yW~n(n@(iAc;T&8 zeYSTgoOV|)oGCS&FV$(Gi|cvXKkWTy&_SV}N@db&-TH-+*0qM8c2|&x9A6H<6|J#= zq79tU@cAEl72sgey{^7|VM&-;m$fQ%IO)R5$s_x4dRIiHE4fszr_LWo9dmoMjTm6ncaQ+Vag=62LKlTbGWxvfS@MF$;%hPXk74W7e=4? z&K-(xj_MD>Zql_^>iReu5*VCjo`toYJT1)NNR!OTxeIs}m_zcGv2=Yq2{_?A>tVCiusb zgaqLQrFo4_%M}DoDYS|N1-> zy5tLm5q)B6wc=mKIj<195gN z4^2-`@9gX>2I9?v@b+!os1_+Y0RaQ){r}Rc|LuTo{+D()Nf}?!Q_hnr`gt!2aSVFL zaa%hyGNKR=m6;=j|0C0;!S*o)1`bwZ_~D+o|p zp+>eu9k9WS#nY=z-fhs&W{Cfr1&hWFvpW_79eKLx9|VRMi(InX>8DmTpX4YicVE9% zdL)R09B<~Da8VWUke$0eTpo%EbrJtFvR?i{qlY|nMW>T9`R3+rp7hb_(4vMV0zp%~ zPrHw5iSD5LbeRs8@Tm}m@ZlH0_TeH_Kl8tWESM6~X6n9GT%v0}KYlb#O^{e=R$G}i zbF=5la;$vTbMH$1R1+wAsNPoM-G*?1fY-Sm%i~az!jO& zZsGi#dSw|<+R~fqTGvJj1 zGJrQB+SbXT%3GjsuEGvj%!E<_4fXMlB6Gnnh1F(bCyq|pM$5~aPWn0&Pq)?ItZjTl z6S-}lF4H*DuD#a>gvcSFx--AqC?6mD&wR+)Aj%3)#~|Vq8@Qj4-+~%I%26Ty;~)EecI8v z&{$Z4@tVqQFYPdig_K@T=z(XEiP8^dtY+10lmD1}0$<_)erTDH`ARm93m|fvLTSNJ zVdzClBQk?l8psvYr(sD9YGzQONMn8*Arv6^uj!Fw`{nB(hR*lgrFwCbX5(fVAkH1v zU_y8Tpd~}chQ%bTf02f7U+eOU;4c{61`EC4#{Y_B5+C)KSrL{4Ow}PQ982~QQ7VqA zL>sbJ;sUtXbHzLrP^O!zzSBNZ?rtM@0RQY=HwdrY-uhu^S`8YSl0@_khU4bwRRK5ksOE3$KYr7Vq`lDm=G926*{U%z&s)^E%e{$}{+f!qMm_O0sNl3w ztqy})=amTae~lS$7z0UfqI8_s7t@3p>Cw?>!*fzau!t({kRr2rR2-ddP9#=}B6uq_ z0Go|_)xaVbzkz=i^Z%?3!LTpGR_LM@!OlW%sM={@@gT|fHrCF#;)-b4Q9 zr0EN}-Tx~;4;U5Mxjte!ySkEdRf~}LcY~m^Kn<<2Cf=B!R*xbeBF3r|OaM2!{@q6; zK4RjpUd-)U=AaoJO7)Pxdtc&GxP)RZ<5c`Pcw^Ao7YYCQtO$f|ef}#fmQxEwDwWAw z-(vX~v-Tf`!OLPN^cr2L9&h_FsmTx}s-cLTH@NF2^-&Me&P8; zDkaqUveo!-z4)gN1OT}nLS0VqhYo~f?InN++>Za8H8Nh3a2V=;gds(=&CBL0lK-rh<{dwC(Kc3@yS ze6k7(P&x(r_FVldD$qt?03)j#F1;Zhz-N_$$*?%Ko*;ydI4(=xt7DkKVSH|b=9U_t zw$NOMy>ThJNBLY`3Tr1Veraw$-_8B0B05N=PDf*{*XXAT@tVrNvczB-mt3+0L?pdUh=ZoW3%@RA{E2;|QliB=LN^0@S@ zXX>C6x3&IiqQ-#Q-Mc3&n@$zc&{nG5N>Rt*5c_}kxT}yH{)QcOj*|6Ii-2VvY6!y7 zmATqcQ8Sb1rhgzze?LZV+M_$CKT`=}GK4KFl{~&?jEI85MMMC!?c|UJRcbd}gz6!B zg%ZA%eC^d07RMv=xxszUAp96oN1W?;4hfgNKj31=e>puV+EDNBuJ!>Zd*3AGgYSlP zK*$OU?>=KDlEF2#197l7B#hzJ{HS};d1k3T<;tG-kw^8>mo<|a_D06PNT4AkGULU1 z+w=XRGU*oAE12~@JmLidq;M>ZR1V9)a9r+=4XSir=a@1H_cHLndPP)TVBu6@s-?N$ zLvS9`S!=7*MDJD^=fM_)qP5|nH;uB|@6*>{b8OKZLU8z21P|QaT0YWkH1$n@q2Xc^ ztbj_ZmeH)7>;C)e5ym5cP!9seZf4nGWpm1FJ5&;fdGIjxY~%bDL@ykY(FXe|B_IhG z56@VG84FWS)IfBcpA9FDzf1>FfmW8gHO{;u6%Ck0C5`6V8K$7ZuC zboT^4`312tjb+vo$eZoJ!J%(VV9;Qr{oL&T-7`OYj>5~w_xxgaB}b-?2t-QcbxvC_ z^CQb>Fk`rZsqt-mkqYO+biOKIW9p)PTUi@ec=>r3W9I`F-RJKRHL`TW`SVgY*4nqz z%^x2EiiD#{4jD?3{`0>6)vggKlGkYsX+erDPK2~tS+N&4UKNVjD!2toO{EJu@kQz% zr3F-`=RR+&H7XYy(e7A-MdJ5sa<;p!4vW?u=R6s%w}4A#1gMc{^F2}AG7i3ckqU8L z|6LL(-o0iJ|ACE|{d#H&paIf$A>&Nvby;jEHHiPF3}G4XoD4q~$I0PO^LQCTZAiqI z`E^3~_4{lpf%#ms&pDK2==n&=D@xHQ;CL{V2=P1|NG)sm7J0OR zx}3<`7O8AK7xKKR{;o0*Jn(7OKra1DB9oSIEBM>j@W?!yv30w%PL~D;Ry(5i*RW^) z9@HxE`2O8kEqWtqyk;Qt?!?T+eu>aAvmPx|;8$N&%;Wi1#sLr62aoR;D_Y;v=Q_|N zQ<;QL4$dJt6}#>5vyA_qCnRkk3!f_j0cG~mPa7=OYx?WK8@<#_G)^A7t@qBpefv>p zT!++v2P=VyDHD&Mjd+NtKdi6zry@pu+HtuolB7P2WD2%v&loucr*ZIR@q0eh_mAw& zlx0HZrjJ~f^AuI4BMDk{Rv!x|16g~+Q}cCGgNClFo|7mQ08_HH7RwY%8GAJL*o*8A6Gifzwj*vG1v|B>$7tkqX22#AZQVj+3$c_Sb-11PE+++QJT3j(osqbn zC{8T+j*oA*2ctWnhdqfc&y$)jmf>RM?Q^4vYg{J8_Z}^rt4bVE;MK&|eClg^Q)|A< z(dDQy>8(3`J;VSzVex(pOT+{sL=y9p{Tlerp6zx$RB$fY66o7pdZ5W+xcwc0Y6K2i zt@QJ!0kuLpS`O;NzK%z>lM&oNKTgnC7hHs56CUv6^h1mG_0b&V$Tcp>Dav%|C%sYY z@malJkOX(-#Y#z~<)HXPVBRAn|2D>kv}X5Zg-CG9T zkx$x8G(i2yH(sLq`g?LSwBf$!(UK zXZuIEkgF5yxC8?pxS`EkZXHbO{jFY;-SVN24eZ9aFNd@yR z5PPuW5j<2+DV-P-bil>2lgXAkiFT~^WX0IBs zjrM#Swp+XS$4aaG=s_`mFPHD30!wKTnvo{sIzB<84limZ&|8m@BF5u#)PkH&BuRuS2p-UM>6BVcy?VB`0sfsbu7MgKjqQCpvlt!ZFWaV=PEp!G@P%#{b10loMq7R zTBW^nUii%3A%UYFBtIhA$OlVcvzYK}qMy8{8sE+lqD;$ME{Nk@8)9mE5?Ed)FmsvZ zCeKk^>#TGfG|{LtiTLucmO>393X*$He6ue`U-MAo-tdmTWT7^$_K2@ab@ad0$5 zuD9R4Uv8B;mS(MqyeA63uv@V)%v!8h_ZgJIV>lb037E17a+1Br6PTY(qZiYepw9SC z_AxB^IxEx53_$%;;*kMTP7KX|`^w8K+N|n{M0+fyS>r&vjTCWyuiLpjn){b|Su0)I zcq$W+_>)`uY|HHc{y6w|hjMK*`Q=*=R@(shj+~`*{mf$#79}Y(kyjXxlyA3jP!02} zT8138IZhPP0r!I#jGC#WF~sMRkK&c4Gfo;c1_hYY+F0gZ(;t$$ne_mAovJWgQz0A{ zbJ;H9VnUb2_vQr}iBk5Bua6h8hc#JxpPH<2gb7pG9is=fAc zuEsERQaQ^k;BD>Ki4OQdPA}#-uR1HX8BWJ(GAFJ-cet_q761Pvr_Wm8JyMt*)$d57F2S873Pr)_reUs9~M8+Q+M zH>|>whhydDu)JA)9Q8YBpNZMqZ6L7-7HF4ZN(I-X5o)VVNk4PqB> zA`+^|Fjy>YE)hkJiO6{%(E=T-yCs+C^6Y4$9e^d={WW4p(2O)8{3 zu4F6Nr2gUbf(BxY+lJ)1_Z!|r!1b9wnb##7$=xN`;oz)TWz=@9KP2*KrA|!~a96*F z7^Uz&odb(WS*$mo0gIl8>f-BSjeXErWAm*D@tNFq(McG>JD@Ywbdps!%DvEJ`?BW- z=binuEUkKebo0|RpLliW#rf=p0uJh+x2K1kAecCqLN;3MsYWE@jhdO0S^^;!JX8xqqU)9y}T~QPW_~@40 zZsH*1!&FNf>r5Vx0bnGV#ScM5OgaV`$MZJFxGwUbehxbzgI{!VSq8+Z1>K zE+pLs0cK=YGOLkNLX!)8?$SZlD^U3T8vcbf{4-T#eqLEEha&2J*RHQ?t`GvBIOFZN&%YF)RaFFbZvgCdgNQq#Q?+{c$~RD&q-BtvdW_<@`;~$<}~~py(tH{pM=38QnK6Y~Ai?$CFiB zYCh3nA^L6h9PUW(qXi&GL9a;$(+PwJX)Ly+(G9=6<qae;KcrM=Kfu>coDd%#TD5>`qds@;wyt6#&N{oVMIJN2fZ2F6#nnU5Ey zGGWt%vcdQuk2?8jQl>@z>iQhn;;Z_eQ!?T2q_Xr(L#J)_M+~L#I$gbNhs2iUalKKR zMXE_eAqk@$UJGANY33Q-BDJY3(8&q|Lsd3YeC7XE4OiS6;5b=yo(m_ z6N1qQw6!Jr@E)EpjTl5*7g}zH(JOrrsS_SVn-@5}c0c+T_|_iZ4$bVqVk>qmYcX`( z9|F2I5o~PBM^dO_?eD^{mW3&iipx71g(J2wv^ePm;Ed!SCZlUBw|)JHMV1tLyeMwMdl*WJAF?Y(VKX@V&w$7Dy(+!w8OidnYH z-ZM?&Cio8i(9yJAbq9cyQiNX!56pY2^NI?-dy3B7&&x=Ko$34=`3y_nkvGS#GwzW(6>kPolmVA!sIbs_GF(8EmLBm5rnH zJcZ7XEJF2Mcw4D%U^F{=linL4kg&Ea1W#e-g~wr4xZEdyeq~CmIedTAkk*=a5dOgC zHYK#lv$g^a6Xjx^w44Z!D;B(jfMsnuWv^Z%ZixXxV5a^X?A1o-whBDq9e}>a$6#)P zgVQ&kjvJjN>*z++O~mKD>v}5^_VufptH=CFzoK|!5+c0pB`dBsc3(P63WsMJuEAk9 ztuIRnMEvi9r+ndnl2RM{OL{PRX8Ck>HX~gu}$8jUA(H)|?@%&kSnQS&=N&JtFZkA`Xvuvr}JyyT>6iQ0t?y|LB zs8K_6g=3y0x~lI`d<0oR3Roz)^04%GiyOv%OZ4Dj z#G_Uj59%jr9b{ib7y&ouS66U4$4E36ANjh}PZg(17uvN2)5s$s!K@>HqK16s`$OSZ z;nYb35<3iA^S1~hw{e>Q(^K4Gb76U`c@h@JZ5pu7LCig9B4D+3Ne;+CF{0 z)3t_{aQCu`>PmVP+c0D69tcFnmkp>n->>*OU7Wl*g;4uQ`9r;@@jatvk~`Ed*)q)bwhLej-;rkNZYac>~R?`<*~6S=s= zQXN=LuV@BDmJoQ|9M4(ORjU@O%r$G}dSxzOZ%YogMGzTJ^FNCS#+_P^D5bDi70YJs zOZxYKJy(~6KP)o~MsX~JKR1W9SJj0 z2AI!_v3FleZO95k`6KJcBC=X;k^IPh-&r~DV55x<7N`jK&?Jftwse-?2T42M1Wyqw z>_-vuSl9)!WHILI#!*6gqBwX%ec?wa9umU#-6gHp&Cb1s9Oy*KP$C(D_M9nWZ*1ZF zaz-JKC`#Ce(#Km_l!$E`l05j{i60egW3Ne1c}buM*Sz3OMV95es1&~9tRw7PIUPTM zUWby9ETRv^IGlW&EvvCU!1q{ffV%NavGz-&@b9%;-fdOJqgu{42|R$)FBgv@jI)Oi zf+YLD(dgQejsf&@`|BJDLBZ88XW9E4zv;r8x2Wn!g7wWw3=?STT$ zC$}>X+&zfa3oR38_#fZ>Eo@tj{0{(6_AHp!lyS4i3q)4z?<4Be7n~v*x#6Cg24MX^ zy`6PfRNWf(mF^VjMg{>HI){`H1W_sJ5)hDvp&JP$q=pU!0qL$88U&<4N?ICZXoeiV z?fK68zUQ3(zi939vhI;kQ2g)w4dSXPPV-_hSrH;vJ2$Mr4yGOLV`Ff?E@S{OEzww2Ac?Jw{fGGs_;q?6Y5?tFf`4 zGTjP*TKDHwuU|Wk^aydFG+O%Y!)p102jgYNv7XDXeli?SBRAPnZiBOcgJ!Se@!U&8 zz;4No_&uj&LkrebdP0V9Lh^p_dBh?KX?fcF6GwAmLFY{<=nmyGy)J3O8qMxQ`Y&lK z55)sP>iE0J9_KxQY$|K2g{n%%*fA0Jv7bzob`0?3P0O;lVKIeXXGr;Gti#G!6tb#w zjx6Km@snQ$-jWUX9VF?TrosedeIZN-!M!}?197V+Dz8%DzWCDuiu+&Y4VdULdYBTz zcjMJ7fOm(NzA)~}- zMSmSfP$m*1ds_iJJ$=VAa{l0eA+h9h&(pMPdW04(8}5EU<*%`_J+w))xWcS~eamS~ ze>hyVcrvyy)jS;$8wKVr%X$u0P7}1^@I=mkv&0m@bRs75mRf*&ifIl5#so)%v|(M} z9ZqJic&np(m-&+$wli%`IqGTRFJj-!(7Bo`qYtwD&H>akTT)$MK<}fwk-}jIE5;8} z5lSMTqW2b8`ume4K6uaCM24@90rDfo8mTQ`6v*YHv&zQIu?E#V;EVkMX@83E&o%!S zMY3Be7+3+iIjA7%JHkj_vijo9B0A<{)_oiXfH^R3NuE{N>`w4go=*G zz=9bUGqL6~K)u`8Z6OdRAf5l=%;g0=K-2tf8~#_j7-S2$7$f8zFDN281G>s%g(21hr?v#{! zH*)rUmH#EXs7aCdT-3|uVECRVxZ!d6_nmmM-PQ(eDWB6b>&HRC|_#HIuDzr4J`S2;ir6zW!F zgZ4jVd&n}Dqsm(QW&tb?UhZ5qTS>e8TlIu`2XJ zw=_0E0i-+ANd8|7$`OD;CN7^W)K~V|mg2VK7oG@+15#HJR`Be>C-0Z4&+DQS{FXcp zZ#P||u}S|}rPuz)Dji84{PE@X+6H?XW#J|#jtxdUwB!k$>ghDLYSpn_ZA1QOlqfg! zzbBM0&(dh&vr4Q=r$Qf-r>!|MB$@VLUN$X%-u(d`ePkt#d@X+!<$qr%F~-3f%i3;$ za3=LISW-FenLF1_uYbDCVLk%2(mUKToFQbm=(ne{x!-6|WKvV$cXjhb0(ufz>viPP z&_%*@R9w3|s=9G~5rMe*X76~_XgFKiGja4}!6!k`N=+Z`lN`+~HnsOu`%rMoQQBt* z9_6?8J$V2??VRIvSq*0@NqTIvJy1(Z(igJn1Gs@}Kzd(+=AUS^H)9s$mxi*?{l$kv z2DM3&K148LzeDT``wQ3I!eh8j26VjMA6LK4vCOE+Pv3EYAim77z7IB(k$L3`ja^rm zw3}Rq$IJR!F%EoM^%F9Juk|Mm0JVhtw@t1uTa+KAL0k|+r14&(hl8StdU6Y$OAW33_K|r-(4(Ls_ov3bNkcAGmO6m^Wy?ggXb?jvO z*!)&caHw`>ZL+>lK|iqjKW(0bGy8D>Nmqgjo5{B=eCyx!J|0>I3e5$f)@W-uG}7Vt zF>$gNNxb^C&v8z6?XlRy@}#;5J&fO#PJ*9Ky&d1G0$JTIE?7?j1><;PdtHJs5=G~M zvymsQ*Vx!7JjGXE>_lGoBw$y&+!#zSqnxk!M(C>Co}vQVOl zDf!nU@sC+P0_ww?)3^$N?sv-12IAT(K%jTXgbw{~^E5dMWvuqk)u}JGd-t>8;SG1# zK)mWt@ruc+48w_N*J{8KX*%|L6)^1M&F{;sw?dnEhZFl3n`;r|OUh*0Ti5O-QwcKu zK26T(Ouy3}n++k~4z~WBwjkPpxg0>HECSio{87(^Z7&=1wOj<(>cj?H5e_N$eiD>` zuuv);T*$airT3}!RREO6%>bL=enQ5Mc@wl(=xD4khc@1At_dnQS$sL$^zb9oURoR4 zf7+h(G1l6n&T|KqB+2c*s7rSm;B8X6`GD4&D7Gzqbu(odk!P!k%Tp)1@s0 z;oD1Dg1<6Wjt2Ueu{Ed1vPF$0I|wy_T_y|o1o=S9^KG1S7!CYc%cb=K+W#uwreWAs zwJTwMwxpu%A)qJ^;t{9^rSO_?ITaNJCkJe?c2wOQEZ|XEhkIy>+B?S5n}Fb>v@)Hn zKCQIqWFi`I8sn^~>uuc)NQG$|A5fVT*ae#)-q1iwvl(Y{YOK?pb~Vu$*djzoeYb38 z1S+;p^5>FJp{9^})m_e?aiZS5A-!DOFeTw=#~HCDH6+A)==C*_8GMA6a*~jkA;p@u z1n_doe(G6}7wCn0jb+tf5H|AHkveV7mCDW!5frWhRU88p4_|oMkDAw* z54Kr8mKS5yQ(U(}m*;;`EOInVUjcjrX9rK;jY8ZHa!U>2$_8}~HkXrSrly>4B7bdY z&{suGOgYY5zyhz4hMSW1tZ_b(#o85*unDOJN(^eZxtfxgOQ+aR*NT|f74TV&SY6G? zzQH}wJV5t3OxSiJ{h;=QR`B~|JCE{OE-BUopgDhe6ZP_3_22!}!MGuJ785CrL;Hd| zK45k^@m4(lR8!iEQoc^bnad%iJA+N{LKEwH>j#_+H`GeTPuJ%_rm&1p9fEeNIrZ7M zLu|v-hUOUe+0UKIyQKHm7f`ZE&D@H>V{RhxE+b{D&nZ0+V&R{g2zMI>A^S`vJdvfD z(r(IXFzyFVZK&2m^-PFMCnRkoU8DqP%aQ3klw)93C(=Z&=sz_19HyQsD6PP}+ncKw zUSv~HlRpu4`C@4A5QR#y9?F5mg-d3qv-^F0IXkZW+C1ZRe*!HjKCHoMDI9DGMw}<4 zY46X$RnV#A-lkSR<}l}49ZD{~F|P&7m9Egl!&mK?iEnSnYaopCN7 z-ID$kwKX(t`3So~L`#jbjz(?P0fvD`Uwa3Dj%1g}Mcrpu*@2zXJ?oj@m_dZe-ZTyTvX=fS5am&K#>lwW z((IYs%k02amM&}C3^_ZeEH@t7&r!Lx&OYlB68g2fV$B#fSwn}!dNGA+GI4C+@!Q0g z<(!5R5)cT=mS~q)n?CA!pTht7jsjQxJ9;v1)bx&raxE*a)m7Wv>%k(^!GO;evsGF) zLh=#qU&|Rfd}b4RtOXYhq?w@fvfk;3&Fy*P^*V&WeF|u`A$5C%a~9Zf9p0x__UYB0 z)Sut?F9~v|M%pFX@&IR_j|T=8lHm2fB8}uHfyl2vP5Ed?HPD}X_r~h9IJv-oFMVi@8oiSe?gOFi7u~)XxX%1b{8&cpzD-Q zZKM@*E$V(*4E5Wy=>B1uza9#Lud?J`6>Q;SIaj*6l{#HXdS<#73|L_GIc01rzd>7~ zOcNeyDA(}${!~wpeM~azf&6%Kb1c+P^-<}*hua_sjl9UqZJ7+yv)bME7i=B!O^|GA z(~oc{a5QCOuAq6ODNS*)1NtTeNo#rz@*{4Q3?F3Ne~$SPLVcwj0$zr`xU?|OSP zz03I(PrTAu_^XIE2Ovuv$W(zWR3{G_*8T|N?C8R%$?mkI#b4B=q2Z1*{&66hO2uX! zu*K|kue5NF%kKc7oC*5;#+o4754ATBatfq*s8g(~wg0%t-ER7uU!G9?J3Z^~`B8s> zv^6ce%y{fHoM+G)S8oiR(b>JwxQO(eU6CY$#gZAC&TnL`XVXXP*44Mq)2ao%o7o|J z+%PI0JyEz(mEYHzW~1BI5&Ejb;fx9?Qd~CDPVA}b*~H;iUEIjji`OfYsZ*?4eM^givo}CXZ9m8^FQ--2|A?c$PhxJmVRptTg}G-jbfq}+uetG-!LOPv8JkPRMG-_zY9a zZ21pSVTjA*4w#gHl-F2Xuijy{s5i?%(j#ywDP(#pgDR`Smpg|C`23_Ul){rpn<$6(C?SfzuIG; z=1?9npVV>O25W#tC)qd87e!hQQ<|$d1}J*8#>d$>mwKk>y;J^jKOf~OR!Z~i8B-K=JqqH-ew9o!fh6?5*AFT_B$ zDPx`axZQ#%@`=}57F}~PxLa*IW+w~SNE|$rE-Lz~Pk8Yn)c3R7G;(8FtSe$RBXgh; z1}19uZMh`!>Y2qEA(Al-xGr~N3{#&d%)TG?tK-U&Hny-R{OjJ2(Y}j7Nd@Au@b?Zy zYvC^^BS?PNH2*`jWVw$8&6eh9=yDoZ?Xl}WFC)xk|G}fJj}cliP*(Z823fB>{px;x zlT^R(5$0zcsK|NvD+T=Q{2S`nj8j(6`W{le2RU1?7$HMn^y*B>CbXV|0;BI!lk3I% zT1Rw}wpUR;4DF$QQffr8%l{~jFreYNCJE64fAWnVpERDb;|!|JF+-{vAQXK9{Rs~~ z*c!LkC6}e7BB=4GXLv$!Qr|ll5uo~U_e(&pi(A<@ifkX_(G)wg+9E9HrZAi$t+=l2 zX}jqVoVpL~!sI^fP;>X))O3(nhe6d5=DHmi+U}v#9n~mVS1-4zuc+^d5q&%K3;Qp> zFuOuE3cT50RL$3WeAqV4gRS;s1?1)*Cs6UMUr;08(?(X^wg}Fg_}98nc1R`u4tGejq=$mwqzjrHIg?~{vh^IJ(`&%{3W99i| zQPJVM9Q7iwImPg@GYewq#E-jvueM(A+(vV~i?Hi#Tmrb9R`X9+d#`zeFVP$bDcphE zQV%a%&MiUiB7K;UCzI`1L1?Cwn*ryI%y$EaR;_&JwG#jUL2fQhK+*nDMfvYdTX4|G z(3Y&T(y4Ijsl#DafZ@`Vk6(pkwZ_jBe{CGw@@L4P(j)Ig^vg$@wrTa2X*C&b8`FsC z+!RPsOQrkd&fCHYV_}T#lR83*dvt8z;ft4DKVa#56_}5$qv)i8k0T3VFVRg9y5S@eF_sn0DQ6hg& zFMUf((FbT_mn_r0BAXa#z*nC6cvU`B9I@sUtLq1O4rEH}$)7s##mEH>i&)z3p|A#C?v9uGZ_{TE{W6 z2P7*DtSpOOLn|$=qTifXCctek1w0j`>C-ek1m4DG(%%!6bHR1u1!>TL_N1VKi_F^K zt`EYjYwZ*|eFwvA_K4ZQ!Gv&bELI%Z>$M5I#fK*BX2|fyXeJS5SGjm zt{cMUk->Tx8nf2!=|z*nh*=zBRmH>i1XMg}_R|fXnr_dgM2p68DyS4UpQh8lZi)2x znba@1Qy8x+Mk{7pyf*$^h$hx!Bh!1h-u5zNjJfT0*vs!{?XKC>V>xd@BTK}|gkFp6 zT!W+Nz?Xp}y%o0}FNe}SLEFC98L`Gg=z80+;Fnh4^)*}-_isz|Fk^5_6xQu1N+pa` zYJVEQcxUq+yS{C-$}y4sB~mkCVYE^|<`DMit_;-Nyn)`>tS@|#C2ZYW&zCOi6+cy$ z2Gd_%bLNO3Z4^l$5JFT%B64EM6NzJCZB|BdWZlA~JX1@v>9suPnGq<>$08$W~KSAkQaijpA?#ced9IvHd^n60Z z?hk=Z%gYSn|{02_E8q5G~NJlq@Vu41OGd9TrUW8RtdlXgvTv zPOIYo8c^VGSQ8xpeXPHd6UfM}qirU4&|c}VpS>_ty+9e_M;GR=Z~e}*iLZ2HgtU~_5xSChw{lN@-H5x<9)>EXfHTh>kuwDx_;i#SQ-7n)AtYl2kgZ8`I z9)-0Y+Pp8`mF~MquLIa*de%DvL8~{1SL7k6J00vc4Z(EP^(Xr}oJMb>HzZ;%CRe_+ zdxRBYW{b@HLBY28{4(P*IWUTIJer(dl0*fsDt1KuX*~9a+(g{9${|<7i4XJq*B!B66>vYC`3JNzdGe8xzE>F{K=6_k$>%Y=FxEk`KJ1r=(H7Z*Z<{H>rN|e zSbXb7(U-3o4bGO`y=F`yTdOq#pi> zp-QYqp?kR}GM06a=t~)S_Pv~NHoU$L`E~xtPpRprozJy%GNi5+?GP#v55#}n@CiJ0 ziLrxLv|PX*=8rbY8*6Py8jVa&)CF-p&ERv0CIqvL;8#_ncGsAj)My2*1y`I=yAt0{ z&^CepGLi)2M%*!>@u_QJKVoAY_IbPOxgL)}#j#|gc>{uLimilk!2JeRTerpzBRN;- z57zwW%3G@6bognp(Wr!7C$;!OeWk_Wee%6USg|6hg{pOv$jcbd_G{|!FwYVSef2Zk z5v|D`5%oST_4?x}F-hJIfdXD*6zyaH3!f!Bzf(!d!;P0?r&|>JPIxOHeA@mIQaFNC z1CRU-Xma1AV@cdR(^^E2oP9Vx;dFIoxhHu$yB>*-J^Q`LxUzRt?vx&fenJWznWP(r zDYO{E7-ii14AkA1Zk_f?On%~h7QdruXP+o%TQ&M2k|o>cY#zPZGGqAZBtL{&UJ=a3 z9fI$Whh%V#|K>4}G`!&-Os4*=AF-bmF!+0;vMH7Wh0DH!)QxXI49aWvR<;Z>Mngt!*T?FxO}8EXb+la2ox2!i7_zdhs)-}} zhP|gA?Qc+QWlZz6e78!wc$mNm|8)p(=B*exlDl0_DJ}X=DepF#vngGVGGog<;zeM< zDgKNs7bHiDWpdm2EOgsA-OmmRg{{ySx*0i~oO(-19G~Gyy5U#+-{by#0QQ7s-wy%F zDA*o`-$Fw}C^9d;VP2$X)@P7H(0uZnRgrD@e-F+ZB}an03f|o2Tgo@H&kU#_op|dY zvJj`cGrw&Mb;L>3|1OJ@9thf;9CO$V5^~DQ6-qf!`!lzH4QQr{?Yi9GJ9{Mby=w^X zv%5joKi7eQk0%+NUZ(xea=gI8%Hqn>1^qRvJHc`oAR~E9oKS kzvmhxhYc*q|L>znclma2iVymccYv3w(lf Date: Thu, 29 Aug 2024 10:41:55 +0200 Subject: [PATCH 185/382] short-term fix for para inherent weight overestimation (#5082) closes #849 ## Context For the background on this and the long-term fix, see https://github.com/paritytech/polkadot-sdk/issues/849#issuecomment-2247895862. ## Changes * The weigh files are renamed from `runtime_(parachains|common).*` to `polkadot_runtime_(parachains|common).*`. The reason for it is the renaming introduced in #4633. The new weight command and files are generated now include `polkadot_` prefix. * The WeightInfo for `paras_inherent` now includes `enter_empty` which calculates the cost of processing an empty parachains inherent. This cost is subtracted dynamically when calculating other weights (so the other weights remain the same) ## Benefits See https://github.com/paritytech/polkadot-sdk/issues/849#issuecomment-2247895862, but TL;DR is that we are not blocked on weights for scaling the number of validators and cores further. Resolved questions: - [x] why new benchmarks for westend are doing fewer db IOPS? Is it due polkadot-sdk update (db IOPS diff)? or the bench setup is no longer valid? https://github.com/polkadot-fellows/runtimes/blob/7723274a2c5cbb10213379271094d5180716ca7d/relay/polkadot/src/weights/runtime_parachains_paras_inherent.rs#L131-L196 Answer: see background section of #5270 TODOs: - [x] Rerun benchmarks for Rococo and Westend - [x] PRDoc --------- Co-authored-by: command-bot <> --- polkadot/runtime/parachains/src/builder.rs | 15 +- .../src/paras_inherent/benchmarking.rs | 37 ++--- .../parachains/src/paras_inherent/weights.rs | 18 ++- polkadot/runtime/rococo/src/impls.rs | 2 +- polkadot/runtime/rococo/src/lib.rs | 32 ++--- polkadot/runtime/rococo/src/weights/mod.rs | 32 ++--- ...polkadot_runtime_common_assigned_slots.rs} | 0 ...rs => polkadot_runtime_common_auctions.rs} | 0 ...s.rs => polkadot_runtime_common_claims.rs} | 0 ...s => polkadot_runtime_common_crowdloan.rs} | 0 ...kadot_runtime_common_identity_migrator.rs} | 0 ...olkadot_runtime_common_paras_registrar.rs} | 0 ...ts.rs => polkadot_runtime_common_slots.rs} | 0 ...kadot_runtime_parachains_configuration.rs} | 0 ...> polkadot_runtime_parachains_coretime.rs} | 0 ...> polkadot_runtime_parachains_disputes.rs} | 0 ...rs => polkadot_runtime_parachains_hrmp.rs} | 0 ... polkadot_runtime_parachains_inclusion.rs} | 0 ...olkadot_runtime_parachains_initializer.rs} | 0 ... polkadot_runtime_parachains_on_demand.rs} | 0 ...s => polkadot_runtime_parachains_paras.rs} | 0 ...adot_runtime_parachains_paras_inherent.rs} | 124 +++++++++------- polkadot/runtime/westend/src/impls.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 32 ++--- polkadot/runtime/westend/src/weights/mod.rs | 32 ++--- ...polkadot_runtime_common_assigned_slots.rs} | 0 ...rs => polkadot_runtime_common_auctions.rs} | 0 ...s => polkadot_runtime_common_crowdloan.rs} | 0 ...kadot_runtime_common_identity_migrator.rs} | 0 ...olkadot_runtime_common_paras_registrar.rs} | 0 ...ts.rs => polkadot_runtime_common_slots.rs} | 0 ...kadot_runtime_parachains_configuration.rs} | 0 ...> polkadot_runtime_parachains_coretime.rs} | 0 ...> polkadot_runtime_parachains_disputes.rs} | 0 ...t_runtime_parachains_disputes_slashing.rs} | 0 ...rs => polkadot_runtime_parachains_hrmp.rs} | 0 ... polkadot_runtime_parachains_inclusion.rs} | 0 ...olkadot_runtime_parachains_initializer.rs} | 0 ... polkadot_runtime_parachains_on_demand.rs} | 0 ...s => polkadot_runtime_parachains_paras.rs} | 0 ...adot_runtime_parachains_paras_inherent.rs} | 136 ++++++++++-------- prdoc/pr_5082.prdoc | 15 ++ 42 files changed, 270 insertions(+), 207 deletions(-) rename polkadot/runtime/rococo/src/weights/{runtime_common_assigned_slots.rs => polkadot_runtime_common_assigned_slots.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_common_auctions.rs => polkadot_runtime_common_auctions.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_common_claims.rs => polkadot_runtime_common_claims.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_common_crowdloan.rs => polkadot_runtime_common_crowdloan.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_common_identity_migrator.rs => polkadot_runtime_common_identity_migrator.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_common_paras_registrar.rs => polkadot_runtime_common_paras_registrar.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_common_slots.rs => polkadot_runtime_common_slots.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_parachains_configuration.rs => polkadot_runtime_parachains_configuration.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_parachains_coretime.rs => polkadot_runtime_parachains_coretime.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_parachains_disputes.rs => polkadot_runtime_parachains_disputes.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_parachains_hrmp.rs => polkadot_runtime_parachains_hrmp.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_parachains_inclusion.rs => polkadot_runtime_parachains_inclusion.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_parachains_initializer.rs => polkadot_runtime_parachains_initializer.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_parachains_on_demand.rs => polkadot_runtime_parachains_on_demand.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_parachains_paras.rs => polkadot_runtime_parachains_paras.rs} (100%) rename polkadot/runtime/rococo/src/weights/{runtime_parachains_paras_inherent.rs => polkadot_runtime_parachains_paras_inherent.rs} (82%) rename polkadot/runtime/westend/src/weights/{runtime_common_assigned_slots.rs => polkadot_runtime_common_assigned_slots.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_common_auctions.rs => polkadot_runtime_common_auctions.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_common_crowdloan.rs => polkadot_runtime_common_crowdloan.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_common_identity_migrator.rs => polkadot_runtime_common_identity_migrator.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_common_paras_registrar.rs => polkadot_runtime_common_paras_registrar.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_common_slots.rs => polkadot_runtime_common_slots.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_parachains_configuration.rs => polkadot_runtime_parachains_configuration.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_parachains_coretime.rs => polkadot_runtime_parachains_coretime.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_parachains_disputes.rs => polkadot_runtime_parachains_disputes.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_parachains_disputes_slashing.rs => polkadot_runtime_parachains_disputes_slashing.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_parachains_hrmp.rs => polkadot_runtime_parachains_hrmp.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_parachains_inclusion.rs => polkadot_runtime_parachains_inclusion.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_parachains_initializer.rs => polkadot_runtime_parachains_initializer.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_parachains_on_demand.rs => polkadot_runtime_parachains_on_demand.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_parachains_paras.rs => polkadot_runtime_parachains_paras.rs} (100%) rename polkadot/runtime/westend/src/weights/{runtime_parachains_paras_inherent.rs => polkadot_runtime_parachains_paras_inherent.rs} (82%) create mode 100644 prdoc/pr_5082.prdoc diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index b2a67ee8dd24..59afff359d08 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -221,9 +221,10 @@ impl BenchBuilder { .expect("self.block_number is u32") } - /// Maximum number of validators that may be part of a validator group. + /// Fallback for the maximum number of validators participating in parachains consensus (a.k.a. + /// active validators). pub(crate) fn fallback_max_validators() -> u32 { - configuration::ActiveConfig::::get().max_validators.unwrap_or(200) + configuration::ActiveConfig::::get().max_validators.unwrap_or(1024) } /// Maximum number of validators participating in parachains consensus (a.k.a. active @@ -285,8 +286,8 @@ impl BenchBuilder { /// Get the minimum number of validity votes in order for a backed candidate to be included. #[cfg(feature = "runtime-benchmarks")] - pub(crate) fn fallback_min_validity_votes() -> u32 { - (Self::fallback_max_validators() / 2) + 1 + pub(crate) fn fallback_min_backing_votes() -> u32 { + 2 } fn mock_head_data() -> HeadData { @@ -356,11 +357,11 @@ impl BenchBuilder { availability_votes, commitments, ); - inclusion::PendingAvailability::::mutate(para_id, |maybe_andidates| { - if let Some(candidates) = maybe_andidates { + inclusion::PendingAvailability::::mutate(para_id, |maybe_candidates| { + if let Some(candidates) = maybe_candidates { candidates.push_back(candidate_availability); } else { - *maybe_andidates = + *maybe_candidates = Some([candidate_availability].into_iter().collect::>()); } }); diff --git a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs index c5284ba1dd1f..88b90e792e73 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs @@ -26,6 +26,20 @@ use polkadot_primitives::v7::GroupIndex; use crate::builder::BenchBuilder; benchmarks! { + enter_empty { + let scenario = BenchBuilder::::new() + .build(); + + let mut benchmark = scenario.data.clone(); + + benchmark.bitfields.clear(); + benchmark.backed_candidates.clear(); + benchmark.disputes.clear(); + }: enter(RawOrigin::None, benchmark) + verify { + // Assert that the block was not discarded + assert!(Included::::get().is_some()); + } // Variant over `v`, the number of dispute statements in a dispute statement set. This gives the // weight of a single dispute statement set. enter_variable_disputes { @@ -92,18 +106,8 @@ benchmarks! { // Variant over `v`, the amount of validity votes for a backed candidate. This gives the weight // of a single backed candidate. enter_backed_candidates_variable { - // NOTE: the starting value must be over half of the max validators per group so the backed - // candidate is not rejected. Also, we cannot have more validity votes than validators in - // the group. - - // Do not use this range for Rococo because it only has 1 validator per backing group, - // which causes issues when trying to create slopes with the benchmarking analysis. Instead - // use v = 1 for running Rococo benchmarks - let v in (BenchBuilder::::fallback_min_validity_votes()) - ..(BenchBuilder::::fallback_max_validators()); - - // Comment in for running rococo benchmarks - // let v = 1; + let v in (BenchBuilder::::fallback_min_backing_votes()) + ..(BenchBuilder::::fallback_max_validators_per_core()); let cores_with_backed: BTreeMap<_, _> = vec![(0, v)] // The backed candidate will have `v` validity votes. @@ -119,7 +123,6 @@ benchmarks! { // There is 1 backed, assert_eq!(benchmark.backed_candidates.len(), 1); // with `v` validity votes. - // let votes = v as usize; let votes = min(scheduler::Pallet::::group_validators(GroupIndex::from(0)).unwrap().len(), v as usize); assert_eq!(benchmark.backed_candidates.get(0).unwrap().validity_votes().len(), votes); @@ -157,7 +160,7 @@ benchmarks! { let v = crate::configuration::ActiveConfig::::get().max_code_size; let cores_with_backed: BTreeMap<_, _> - = vec![(0, BenchBuilder::::fallback_min_validity_votes())] + = vec![(0, BenchBuilder::::fallback_min_backing_votes())] .into_iter() .collect(); @@ -168,8 +171,10 @@ benchmarks! { let mut benchmark = scenario.data.clone(); - // let votes = BenchBuilder::::fallback_min_validity_votes() as usize; - let votes = min(scheduler::Pallet::::group_validators(GroupIndex::from(0)).unwrap().len(), BenchBuilder::::fallback_min_validity_votes() as usize); + let votes = min( + scheduler::Pallet::::group_validators(GroupIndex::from(0)).unwrap().len(), + BenchBuilder::::fallback_min_backing_votes() as usize + ); // There is 1 backed assert_eq!(benchmark.backed_candidates.len(), 1); diff --git a/polkadot/runtime/parachains/src/paras_inherent/weights.rs b/polkadot/runtime/parachains/src/paras_inherent/weights.rs index 37809396a823..3e84c132aa24 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/weights.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/weights.rs @@ -28,6 +28,8 @@ use polkadot_primitives::{ use super::{BackedCandidate, Config, DisputeStatementSet, Weight}; pub trait WeightInfo { + /// The weight of processing an empty parachain inherent. + fn enter_empty() -> Weight; /// Variant over `v`, the count of dispute statements in a dispute statement set. This gives the /// weight of a single dispute statement set. fn enter_variable_disputes(v: u32) -> Weight; @@ -45,6 +47,9 @@ pub struct TestWeightInfo; // mock. #[cfg(not(feature = "runtime-benchmarks"))] impl WeightInfo for TestWeightInfo { + fn enter_empty() -> Weight { + Weight::zero() + } fn enter_variable_disputes(v: u32) -> Weight { // MAX Block Weight should fit 4 disputes Weight::from_parts(80_000 * v as u64 + 80_000, 0) @@ -66,6 +71,9 @@ impl WeightInfo for TestWeightInfo { // running as a test. #[cfg(feature = "runtime-benchmarks")] impl WeightInfo for TestWeightInfo { + fn enter_empty() -> Weight { + Weight::zero() + } fn enter_variable_disputes(_v: u32) -> Weight { Weight::zero() } @@ -123,7 +131,8 @@ where set_proof_size_to_tx_size( <::WeightInfo as WeightInfo>::enter_variable_disputes( statement_set.as_ref().statements.len() as u32, - ), + ) + .saturating_sub(<::WeightInfo as WeightInfo>::enter_empty()), statement_set, ) } @@ -133,6 +142,7 @@ pub fn signed_bitfields_weight( ) -> Weight { set_proof_size_to_tx_size( <::WeightInfo as WeightInfo>::enter_bitfields() + .saturating_sub(<::WeightInfo as WeightInfo>::enter_empty()) .saturating_mul(bitfields.len() as u64), bitfields, ) @@ -140,7 +150,8 @@ pub fn signed_bitfields_weight( pub fn signed_bitfield_weight(bitfield: &UncheckedSignedAvailabilityBitfield) -> Weight { set_proof_size_to_tx_size( - <::WeightInfo as WeightInfo>::enter_bitfields(), + <::WeightInfo as WeightInfo>::enter_bitfields() + .saturating_sub(<::WeightInfo as WeightInfo>::enter_empty()), bitfield, ) } @@ -155,7 +166,8 @@ pub fn backed_candidate_weight( <::WeightInfo as WeightInfo>::enter_backed_candidates_variable( candidate.validity_votes().len() as u32, ) - }, + } + .saturating_sub(<::WeightInfo as WeightInfo>::enter_empty()), candidate, ) } diff --git a/polkadot/runtime/rococo/src/impls.rs b/polkadot/runtime/rococo/src/impls.rs index a4440a1c6e0b..f01440ea02bc 100644 --- a/polkadot/runtime/rococo/src/impls.rs +++ b/polkadot/runtime/rococo/src/impls.rs @@ -90,7 +90,7 @@ where fn on_reap_identity(who: &AccountId, fields: u32, subs: u32) -> DispatchResult { use crate::{ impls::IdentityMigratorCalls::PokeDeposit, - weights::runtime_common_identity_migrator::WeightInfo as MigratorWeights, + weights::polkadot_runtime_common_identity_migrator::WeightInfo as MigratorWeights, }; let total_to_send = Self::calculate_remote_deposit(fields, subs); diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 31713755b9b2..dfc41b15bb1d 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -674,7 +674,7 @@ impl claims::Config for Runtime { type VestingSchedule = Vesting; type Prefix = Prefix; type MoveClaimOrigin = EnsureRoot; - type WeightInfo = weights::runtime_common_claims::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_claims::WeightInfo; } parameter_types! { @@ -940,7 +940,7 @@ impl pallet_proxy::Config for Runtime { impl parachains_origin::Config for Runtime {} impl parachains_configuration::Config for Runtime { - type WeightInfo = weights::runtime_parachains_configuration::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_configuration::WeightInfo; } impl parachains_shared::Config for Runtime { @@ -963,7 +963,7 @@ impl parachains_inclusion::Config for Runtime { type DisputesHandler = ParasDisputes; type RewardValidators = RewardValidators; type MessageQueue = MessageQueue; - type WeightInfo = weights::runtime_parachains_inclusion::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_inclusion::WeightInfo; } parameter_types! { @@ -972,7 +972,7 @@ parameter_types! { impl parachains_paras::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = weights::runtime_parachains_paras::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_paras::WeightInfo; type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; @@ -1046,11 +1046,11 @@ impl parachains_hrmp::Config for Runtime { HrmpChannelSizeAndCapacityWithSystemRatio, >; type VersionWrapper = crate::XcmPallet; - type WeightInfo = weights::runtime_parachains_hrmp::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_hrmp::WeightInfo; } impl parachains_paras_inherent::Config for Runtime { - type WeightInfo = weights::runtime_parachains_paras_inherent::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_paras_inherent::WeightInfo; } impl parachains_scheduler::Config for Runtime { @@ -1079,7 +1079,7 @@ impl coretime::Config for Runtime { type Currency = Balances; type BrokerId = BrokerId; type BrokerPotLocation = BrokerPot; - type WeightInfo = weights::runtime_parachains_coretime::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_coretime::WeightInfo; type SendXcm = crate::xcm_config::XcmRouter; type AssetTransactor = crate::xcm_config::LocalAssetTransactor; type AccountToLocation = xcm_builder::AliasesIntoAccountId32< @@ -1100,7 +1100,7 @@ impl parachains_on_demand::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type TrafficDefaultValue = OnDemandTrafficDefaultValue; - type WeightInfo = weights::runtime_parachains_on_demand::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_on_demand::WeightInfo; type MaxHistoricalRevenue = MaxHistoricalRevenue; type PalletId = OnDemandPalletId; } @@ -1110,7 +1110,7 @@ impl parachains_assigner_coretime::Config for Runtime {} impl parachains_initializer::Config for Runtime { type Randomness = pallet_babe::RandomnessFromOneEpochAgo; type ForceOrigin = EnsureRoot; - type WeightInfo = weights::runtime_parachains_initializer::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_initializer::WeightInfo; type CoretimeOnNewSession = Coretime; } @@ -1118,7 +1118,7 @@ impl parachains_disputes::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RewardValidators = (); type SlashingHandler = parachains_slashing::SlashValidatorsForDisputes; - type WeightInfo = weights::runtime_parachains_disputes::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_disputes::WeightInfo; } impl parachains_slashing::Config for Runtime { @@ -1149,7 +1149,7 @@ impl paras_registrar::Config for Runtime { type OnSwap = (Crowdloan, Slots, SwapLeases); type ParaDeposit = ParaDeposit; type DataDepositPerByte = DataDepositPerByte; - type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_paras_registrar::WeightInfo; } parameter_types! { @@ -1163,7 +1163,7 @@ impl slots::Config for Runtime { type LeasePeriod = LeasePeriod; type LeaseOffset = (); type ForceOrigin = EitherOf, LeaseAdmin>; - type WeightInfo = weights::runtime_common_slots::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_slots::WeightInfo; } parameter_types! { @@ -1184,7 +1184,7 @@ impl crowdloan::Config for Runtime { type Registrar = Registrar; type Auctioneer = Auctions; type MaxMemoLength = MaxMemoLength; - type WeightInfo = weights::runtime_common_crowdloan::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_crowdloan::WeightInfo; } parameter_types! { @@ -1203,14 +1203,14 @@ impl auctions::Config for Runtime { type SampleLength = SampleLength; type Randomness = pallet_babe::RandomnessFromOneEpochAgo; type InitiateOrigin = EitherOf, AuctionAdmin>; - type WeightInfo = weights::runtime_common_auctions::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_auctions::WeightInfo; } impl identity_migrator::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reaper = EnsureSigned; type ReapIdentityHandler = ToParachainIdentityReaper; - type WeightInfo = weights::runtime_common_identity_migrator::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_identity_migrator::WeightInfo; } type NisCounterpartInstance = pallet_balances::Instance2; @@ -1353,7 +1353,7 @@ impl assigned_slots::Config for Runtime { type PermanentSlotLeasePeriodLength = PermanentSlotLeasePeriodLength; type TemporarySlotLeasePeriodLength = TemporarySlotLeasePeriodLength; type MaxTemporarySlotPerLeasePeriod = MaxTemporarySlotPerLeasePeriod; - type WeightInfo = weights::runtime_common_assigned_slots::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_assigned_slots::WeightInfo; } impl validator_manager::Config for Runtime { diff --git a/polkadot/runtime/rococo/src/weights/mod.rs b/polkadot/runtime/rococo/src/weights/mod.rs index cd3f4689f562..020f8e22594a 100644 --- a/polkadot/runtime/rococo/src/weights/mod.rs +++ b/polkadot/runtime/rococo/src/weights/mod.rs @@ -44,20 +44,20 @@ pub mod pallet_utility; pub mod pallet_vesting; pub mod pallet_whitelist; pub mod pallet_xcm; -pub mod runtime_common_assigned_slots; -pub mod runtime_common_auctions; -pub mod runtime_common_claims; -pub mod runtime_common_crowdloan; -pub mod runtime_common_identity_migrator; -pub mod runtime_common_paras_registrar; -pub mod runtime_common_slots; -pub mod runtime_parachains_configuration; -pub mod runtime_parachains_coretime; -pub mod runtime_parachains_disputes; -pub mod runtime_parachains_hrmp; -pub mod runtime_parachains_inclusion; -pub mod runtime_parachains_initializer; -pub mod runtime_parachains_on_demand; -pub mod runtime_parachains_paras; -pub mod runtime_parachains_paras_inherent; +pub mod polkadot_runtime_common_assigned_slots; +pub mod polkadot_runtime_common_auctions; +pub mod polkadot_runtime_common_claims; +pub mod polkadot_runtime_common_crowdloan; +pub mod polkadot_runtime_common_identity_migrator; +pub mod polkadot_runtime_common_paras_registrar; +pub mod polkadot_runtime_common_slots; +pub mod polkadot_runtime_parachains_configuration; +pub mod polkadot_runtime_parachains_coretime; +pub mod polkadot_runtime_parachains_disputes; +pub mod polkadot_runtime_parachains_hrmp; +pub mod polkadot_runtime_parachains_inclusion; +pub mod polkadot_runtime_parachains_initializer; +pub mod polkadot_runtime_parachains_on_demand; +pub mod polkadot_runtime_parachains_paras; +pub mod polkadot_runtime_parachains_paras_inherent; pub mod xcm; diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_assigned_slots.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_assigned_slots.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_assigned_slots.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_assigned_slots.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_auctions.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_auctions.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_auctions.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_auctions.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_claims.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_claims.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_claims.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_claims.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_crowdloan.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_crowdloan.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_crowdloan.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_crowdloan.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_identity_migrator.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_identity_migrator.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_identity_migrator.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_identity_migrator.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_paras_registrar.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_paras_registrar.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_slots.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_slots.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_slots.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_slots.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_configuration.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_configuration.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_coretime.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_coretime.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_coretime.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_coretime.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_disputes.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_disputes.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_disputes.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_disputes.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_hrmp.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_hrmp.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_inclusion.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_inclusion.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_initializer.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_initializer.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_initializer.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_initializer.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_on_demand.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_on_demand.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_on_demand.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_on_demand.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_paras.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_paras.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras_inherent.rs similarity index 82% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras_inherent.rs index c00966fb8048..b7b3d12d4d92 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras_inherent.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Autogenerated weights for `runtime_parachains::paras_inherent` +//! Autogenerated weights for `polkadot_runtime_parachains::paras_inherent` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-03-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -32,7 +32,7 @@ // --wasm-execution=compiled // --heap-pages=4096 // --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=runtime_parachains::paras_inherent +// --pallet=polkadot_runtime_parachains::paras_inherent // --chain=rococo-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/rococo/src/weights/ @@ -45,9 +45,49 @@ use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; -/// Weight functions for `runtime_parachains::paras_inherent`. +/// Weight functions for `polkadot_runtime_parachains::paras_inherent`. pub struct WeightInfo(PhantomData); impl polkadot_runtime_parachains::paras_inherent::WeightInfo for WeightInfo { + /// Storage: `ParaInherent::Included` (r:1 w:1) + /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) + /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) + /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) + /// Proof: `Babe::AuthorVrfRandomness` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParaInherent::OnChainVotes` (r:1 w:1) + /// Proof: `ParaInherent::OnChainVotes` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Frozen` (r:1 w:0) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::V1` (r:1 w:0) + /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) + /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::DisabledValidators` (r:1 w:0) + /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn enter_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `8967` + // Estimated: `12432` + // Minimum execution time: 144_751_000 picoseconds. + Weight::from_parts(153_966_000, 0) + .saturating_add(Weight::from_parts(0, 12432)) + .saturating_add(T::DbWeight::get().reads(15)) + .saturating_add(T::DbWeight::get().writes(5)) + } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `System::ParentHash` (r:1 w:0) @@ -109,13 +149,13 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// The range of component `v` is `[10, 200]`. fn enter_variable_disputes(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `67785` - // Estimated: `73725 + v * (23 ±0)` - // Minimum execution time: 949_716_000 picoseconds. - Weight::from_parts(482_361_515, 0) - .saturating_add(Weight::from_parts(0, 73725)) - // Standard Error: 17_471 - .saturating_add(Weight::from_parts(50_100_764, 0).saturating_mul(v.into())) + // Measured: `67786` + // Estimated: `73726 + v * (23 ±0)` + // Minimum execution time: 972_311_000 picoseconds. + Weight::from_parts(645_559_304, 0) + .saturating_add(Weight::from_parts(0, 73726)) + // Standard Error: 53_320 + .saturating_add(Weight::from_parts(41_795_493, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(25)) .saturating_add(T::DbWeight::get().writes(15)) .saturating_add(Weight::from_parts(0, 23).saturating_mul(v.into())) @@ -140,18 +180,6 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParaInclusion::V1` (r:2 w:1) /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) - /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) - /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpChannelDigests` (r:1 w:1) - /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) - /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Registrar::Paras` (r:1 w:0) - /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParasDisputes::Disputes` (r:1 w:0) - /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) @@ -164,25 +192,15 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParasDisputes::Included` (r:0 w:1) - /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) - /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::Heads` (r:0 w:1) - /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) - /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::MostRecentContext` (r:0 w:1) - /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_bitfields() -> Weight { // Proof Size summary in bytes: - // Measured: `42757` - // Estimated: `48697` - // Minimum execution time: 437_627_000 picoseconds. - Weight::from_parts(460_975_000, 0) - .saturating_add(Weight::from_parts(0, 48697)) - .saturating_add(T::DbWeight::get().reads(23)) - .saturating_add(T::DbWeight::get().writes(15)) + // Measured: `42374` + // Estimated: `48314` + // Minimum execution time: 361_262_000 picoseconds. + Weight::from_parts(370_617_000, 0) + .saturating_add(Weight::from_parts(0, 48314)) + .saturating_add(T::DbWeight::get().reads(17)) + .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -247,13 +265,13 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// The range of component `v` is `[101, 200]`. fn enter_backed_candidates_variable(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `42829` - // Estimated: `48769` - // Minimum execution time: 1_305_254_000 picoseconds. - Weight::from_parts(1_347_160_667, 0) - .saturating_add(Weight::from_parts(0, 48769)) - // Standard Error: 22_128 - .saturating_add(Weight::from_parts(57_229, 0).saturating_mul(v.into())) + // Measured: `42830` + // Estimated: `48770` + // Minimum execution time: 1_322_051_000 picoseconds. + Weight::from_parts(1_379_846_608, 0) + .saturating_add(Weight::from_parts(0, 48770)) + // Standard Error: 19_959 + .saturating_add(Weight::from_parts(24_630, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(26)) .saturating_add(T::DbWeight::get().writes(15)) } @@ -323,11 +341,11 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_backed_candidate_code_upgrade() -> Weight { // Proof Size summary in bytes: - // Measured: `42842` - // Estimated: `48782` - // Minimum execution time: 38_637_547_000 picoseconds. - Weight::from_parts(41_447_412_000, 0) - .saturating_add(Weight::from_parts(0, 48782)) + // Measured: `42843` + // Estimated: `48783` + // Minimum execution time: 37_550_515_000 picoseconds. + Weight::from_parts(37_886_489_000, 0) + .saturating_add(Weight::from_parts(0, 48783)) .saturating_add(T::DbWeight::get().reads(28)) .saturating_add(T::DbWeight::get().writes(15)) } diff --git a/polkadot/runtime/westend/src/impls.rs b/polkadot/runtime/westend/src/impls.rs index 11665953bd8e..ac3f9e679f8d 100644 --- a/polkadot/runtime/westend/src/impls.rs +++ b/polkadot/runtime/westend/src/impls.rs @@ -90,7 +90,7 @@ where fn on_reap_identity(who: &AccountId, fields: u32, subs: u32) -> DispatchResult { use crate::{ impls::IdentityMigratorCalls::PokeDeposit, - weights::runtime_common_identity_migrator::WeightInfo as MigratorWeights, + weights::polkadot_runtime_common_identity_migrator::WeightInfo as MigratorWeights, }; let total_to_send = Self::calculate_remote_deposit(fields, subs); diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 519c7dcde54e..e8fe11615d74 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1178,7 +1178,7 @@ impl pallet_proxy::Config for Runtime { impl parachains_origin::Config for Runtime {} impl parachains_configuration::Config for Runtime { - type WeightInfo = weights::runtime_parachains_configuration::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_configuration::WeightInfo; } impl parachains_shared::Config for Runtime { @@ -1194,7 +1194,7 @@ impl parachains_inclusion::Config for Runtime { type DisputesHandler = ParasDisputes; type RewardValidators = parachains_reward_points::RewardValidatorsWithEraPoints; type MessageQueue = MessageQueue; - type WeightInfo = weights::runtime_parachains_inclusion::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_inclusion::WeightInfo; } parameter_types! { @@ -1203,7 +1203,7 @@ parameter_types! { impl parachains_paras::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = weights::runtime_parachains_paras::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_paras::WeightInfo; type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; @@ -1277,11 +1277,11 @@ impl parachains_hrmp::Config for Runtime { HrmpChannelSizeAndCapacityWithSystemRatio, >; type VersionWrapper = crate::XcmPallet; - type WeightInfo = weights::runtime_parachains_hrmp::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_hrmp::WeightInfo; } impl parachains_paras_inherent::Config for Runtime { - type WeightInfo = weights::runtime_parachains_paras_inherent::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_paras_inherent::WeightInfo; } impl parachains_scheduler::Config for Runtime { @@ -1310,7 +1310,7 @@ impl coretime::Config for Runtime { type Currency = Balances; type BrokerId = BrokerId; type BrokerPotLocation = BrokerPot; - type WeightInfo = weights::runtime_parachains_coretime::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_coretime::WeightInfo; type SendXcm = crate::xcm_config::XcmRouter; type AssetTransactor = crate::xcm_config::LocalAssetTransactor; type AccountToLocation = xcm_builder::AliasesIntoAccountId32< @@ -1331,7 +1331,7 @@ impl parachains_on_demand::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type TrafficDefaultValue = OnDemandTrafficDefaultValue; - type WeightInfo = weights::runtime_parachains_on_demand::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_on_demand::WeightInfo; type MaxHistoricalRevenue = MaxHistoricalRevenue; type PalletId = OnDemandPalletId; } @@ -1341,7 +1341,7 @@ impl parachains_assigner_coretime::Config for Runtime {} impl parachains_initializer::Config for Runtime { type Randomness = pallet_babe::RandomnessFromOneEpochAgo; type ForceOrigin = EnsureRoot; - type WeightInfo = weights::runtime_parachains_initializer::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_initializer::WeightInfo; type CoretimeOnNewSession = Coretime; } @@ -1360,14 +1360,14 @@ impl assigned_slots::Config for Runtime { type PermanentSlotLeasePeriodLength = PermanentSlotLeasePeriodLength; type TemporarySlotLeasePeriodLength = TemporarySlotLeasePeriodLength; type MaxTemporarySlotPerLeasePeriod = MaxTemporarySlotPerLeasePeriod; - type WeightInfo = weights::runtime_common_assigned_slots::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_assigned_slots::WeightInfo; } impl parachains_disputes::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RewardValidators = parachains_reward_points::RewardValidatorsWithEraPoints; type SlashingHandler = parachains_slashing::SlashValidatorsForDisputes; - type WeightInfo = weights::runtime_parachains_disputes::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_disputes::WeightInfo; } impl parachains_slashing::Config for Runtime { @@ -1383,7 +1383,7 @@ impl parachains_slashing::Config for Runtime { Offences, ReportLongevity, >; - type WeightInfo = weights::runtime_parachains_disputes_slashing::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_disputes_slashing::WeightInfo; type BenchmarkingConfig = parachains_slashing::BenchConfig<300>; } @@ -1399,7 +1399,7 @@ impl paras_registrar::Config for Runtime { type OnSwap = (Crowdloan, Slots, SwapLeases); type ParaDeposit = ParaDeposit; type DataDepositPerByte = RegistrarDataDepositPerByte; - type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_paras_registrar::WeightInfo; } parameter_types! { @@ -1413,7 +1413,7 @@ impl slots::Config for Runtime { type LeasePeriod = LeasePeriod; type LeaseOffset = (); type ForceOrigin = EitherOf, LeaseAdmin>; - type WeightInfo = weights::runtime_common_slots::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_slots::WeightInfo; } parameter_types! { @@ -1434,7 +1434,7 @@ impl crowdloan::Config for Runtime { type Registrar = Registrar; type Auctioneer = Auctions; type MaxMemoLength = MaxMemoLength; - type WeightInfo = weights::runtime_common_crowdloan::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_crowdloan::WeightInfo; } parameter_types! { @@ -1453,14 +1453,14 @@ impl auctions::Config for Runtime { type SampleLength = SampleLength; type Randomness = pallet_babe::RandomnessFromOneEpochAgo; type InitiateOrigin = EitherOf, AuctionAdmin>; - type WeightInfo = weights::runtime_common_auctions::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_auctions::WeightInfo; } impl identity_migrator::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reaper = EnsureSigned; type ReapIdentityHandler = ToParachainIdentityReaper; - type WeightInfo = weights::runtime_common_identity_migrator::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_identity_migrator::WeightInfo; } parameter_types! { diff --git a/polkadot/runtime/westend/src/weights/mod.rs b/polkadot/runtime/westend/src/weights/mod.rs index cb6e2c85e363..1e7b01bc472b 100644 --- a/polkadot/runtime/westend/src/weights/mod.rs +++ b/polkadot/runtime/westend/src/weights/mod.rs @@ -45,20 +45,20 @@ pub mod pallet_utility; pub mod pallet_vesting; pub mod pallet_whitelist; pub mod pallet_xcm; -pub mod runtime_common_assigned_slots; -pub mod runtime_common_auctions; -pub mod runtime_common_crowdloan; -pub mod runtime_common_identity_migrator; -pub mod runtime_common_paras_registrar; -pub mod runtime_common_slots; -pub mod runtime_parachains_configuration; -pub mod runtime_parachains_coretime; -pub mod runtime_parachains_disputes; -pub mod runtime_parachains_disputes_slashing; -pub mod runtime_parachains_hrmp; -pub mod runtime_parachains_inclusion; -pub mod runtime_parachains_initializer; -pub mod runtime_parachains_on_demand; -pub mod runtime_parachains_paras; -pub mod runtime_parachains_paras_inherent; +pub mod polkadot_runtime_common_assigned_slots; +pub mod polkadot_runtime_common_auctions; +pub mod polkadot_runtime_common_crowdloan; +pub mod polkadot_runtime_common_identity_migrator; +pub mod polkadot_runtime_common_paras_registrar; +pub mod polkadot_runtime_common_slots; +pub mod polkadot_runtime_parachains_configuration; +pub mod polkadot_runtime_parachains_coretime; +pub mod polkadot_runtime_parachains_disputes; +pub mod polkadot_runtime_parachains_disputes_slashing; +pub mod polkadot_runtime_parachains_hrmp; +pub mod polkadot_runtime_parachains_inclusion; +pub mod polkadot_runtime_parachains_initializer; +pub mod polkadot_runtime_parachains_on_demand; +pub mod polkadot_runtime_parachains_paras; +pub mod polkadot_runtime_parachains_paras_inherent; pub mod xcm; diff --git a/polkadot/runtime/westend/src/weights/runtime_common_assigned_slots.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_common_assigned_slots.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_common_assigned_slots.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_common_assigned_slots.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_common_auctions.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_common_auctions.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_common_auctions.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_common_auctions.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_common_crowdloan.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_common_crowdloan.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_common_crowdloan.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_common_crowdloan.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_common_identity_migrator.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_common_identity_migrator.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_common_identity_migrator.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_common_identity_migrator.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_common_paras_registrar.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_common_paras_registrar.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_common_slots.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_common_slots.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_common_slots.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_common_slots.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_configuration.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_configuration.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_configuration.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_configuration.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_coretime.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_coretime.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_coretime.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_coretime.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_disputes.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_disputes.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_disputes.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_disputes.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_disputes_slashing.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_disputes_slashing.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_disputes_slashing.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_disputes_slashing.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_hrmp.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_hrmp.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_hrmp.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_hrmp.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_inclusion.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_inclusion.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_initializer.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_initializer.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_initializer.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_initializer.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_on_demand.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_on_demand.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_on_demand.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_on_demand.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_paras.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_paras.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_paras_inherent.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras_inherent.rs similarity index 82% rename from polkadot/runtime/westend/src/weights/runtime_parachains_paras_inherent.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras_inherent.rs index 74dd55cc3f2c..32f6f28f2426 100644 --- a/polkadot/runtime/westend/src/weights/runtime_parachains_paras_inherent.rs +++ b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras_inherent.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Autogenerated weights for `runtime_parachains::paras_inherent` +//! Autogenerated weights for `polkadot_runtime_parachains::paras_inherent` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-03-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -32,7 +32,7 @@ // --wasm-execution=compiled // --heap-pages=4096 // --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=runtime_parachains::paras_inherent +// --pallet=polkadot_runtime_parachains::paras_inherent // --chain=westend-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/westend/src/weights/ @@ -45,9 +45,49 @@ use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; -/// Weight functions for `runtime_parachains::paras_inherent`. +/// Weight functions for `polkadot_runtime_parachains::paras_inherent`. pub struct WeightInfo(PhantomData); impl polkadot_runtime_parachains::paras_inherent::WeightInfo for WeightInfo { + /// Storage: `ParaInherent::Included` (r:1 w:1) + /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) + /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) + /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) + /// Proof: `Babe::AuthorVrfRandomness` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParaInherent::OnChainVotes` (r:1 w:1) + /// Proof: `ParaInherent::OnChainVotes` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Frozen` (r:1 w:0) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::V1` (r:1 w:0) + /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) + /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::DisabledValidators` (r:1 w:0) + /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn enter_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `37553` + // Estimated: `41018` + // Minimum execution time: 237_414_000 picoseconds. + Weight::from_parts(245_039_000, 0) + .saturating_add(Weight::from_parts(0, 41018)) + .saturating_add(T::DbWeight::get().reads(15)) + .saturating_add(T::DbWeight::get().writes(5)) + } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `System::ParentHash` (r:1 w:0) @@ -112,19 +152,19 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::MostRecentContext` (r:0 w:1) /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `v` is `[10, 200]`. + /// The range of component `v` is `[10, 1024]`. fn enter_variable_disputes(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `67518` - // Estimated: `73458 + v * (23 ±0)` - // Minimum execution time: 844_022_000 picoseconds. - Weight::from_parts(456_682_337, 0) - .saturating_add(Weight::from_parts(0, 73458)) - // Standard Error: 16_403 - .saturating_add(Weight::from_parts(41_871_245, 0).saturating_mul(v.into())) + // Measured: `199504` + // Estimated: `205444 + v * (5 ±0)` + // Minimum execution time: 1_157_489_000 picoseconds. + Weight::from_parts(629_243_559, 0) + .saturating_add(Weight::from_parts(0, 205444)) + // Standard Error: 10_997 + .saturating_add(Weight::from_parts(50_752_930, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(28)) .saturating_add(T::DbWeight::get().writes(16)) - .saturating_add(Weight::from_parts(0, 23).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 5).saturating_mul(v.into())) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -146,24 +186,6 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParaInclusion::V1` (r:2 w:1) /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParaSessionInfo::AccountKeys` (r:1 w:0) - /// Proof: `ParaSessionInfo::AccountKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Session::Validators` (r:1 w:0) - /// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Staking::ActiveEra` (r:1 w:0) - /// Proof: `Staking::ActiveEra` (`max_values`: Some(1), `max_size`: Some(13), added: 508, mode: `MaxEncodedLen`) - /// Storage: `Staking::ErasRewardPoints` (r:1 w:1) - /// Proof: `Staking::ErasRewardPoints` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) - /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) - /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpChannelDigests` (r:1 w:1) - /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) - /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParasDisputes::Disputes` (r:1 w:0) - /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) @@ -176,25 +198,15 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParasDisputes::Included` (r:0 w:1) - /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) - /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::Heads` (r:0 w:1) - /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) - /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::MostRecentContext` (r:0 w:1) - /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_bitfields() -> Weight { // Proof Size summary in bytes: - // Measured: `43196` - // Estimated: `49136` - // Minimum execution time: 438_637_000 picoseconds. - Weight::from_parts(458_342_000, 0) - .saturating_add(Weight::from_parts(0, 49136)) - .saturating_add(T::DbWeight::get().reads(26)) - .saturating_add(T::DbWeight::get().writes(16)) + // Measured: `75131` + // Estimated: `81071` + // Minimum execution time: 466_928_000 picoseconds. + Weight::from_parts(494_342_000, 0) + .saturating_add(Weight::from_parts(0, 81071)) + .saturating_add(T::DbWeight::get().reads(17)) + .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -262,16 +274,16 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::MostRecentContext` (r:0 w:1) /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `v` is `[101, 200]`. + /// The range of component `v` is `[2, 5]`. fn enter_backed_candidates_variable(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `43269` - // Estimated: `49209` - // Minimum execution time: 5_955_361_000 picoseconds. - Weight::from_parts(1_285_398_956, 0) - .saturating_add(Weight::from_parts(0, 49209)) - // Standard Error: 57_369 - .saturating_add(Weight::from_parts(47_073_853, 0).saturating_mul(v.into())) + // Measured: `76369` + // Estimated: `82309` + // Minimum execution time: 1_468_919_000 picoseconds. + Weight::from_parts(1_433_315_477, 0) + .saturating_add(Weight::from_parts(0, 82309)) + // Standard Error: 419_886 + .saturating_add(Weight::from_parts(42_880_485, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(29)) .saturating_add(T::DbWeight::get().writes(16)) } @@ -347,11 +359,11 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_backed_candidate_code_upgrade() -> Weight { // Proof Size summary in bytes: - // Measured: `43282` - // Estimated: `49222` - // Minimum execution time: 42_128_606_000 picoseconds. - Weight::from_parts(42_822_806_000, 0) - .saturating_add(Weight::from_parts(0, 49222)) + // Measured: `76382` + // Estimated: `82322` + // Minimum execution time: 34_577_233_000 picoseconds. + Weight::from_parts(39_530_352_000, 0) + .saturating_add(Weight::from_parts(0, 82322)) .saturating_add(T::DbWeight::get().reads(31)) .saturating_add(T::DbWeight::get().writes(16)) } diff --git a/prdoc/pr_5082.prdoc b/prdoc/pr_5082.prdoc new file mode 100644 index 000000000000..d309f4e7266e --- /dev/null +++ b/prdoc/pr_5082.prdoc @@ -0,0 +1,15 @@ +title: "Fix ParaInherent weight overestimation" + +doc: + - audience: Runtime Dev + description: | + This PR fixes the relay chain inherent weight overestimation allowing it + to support more cores and validators. + +crates: +- name: polkadot-runtime-parachains + bump: major +- name: westend-runtime + bump: minor +- name: rococo-runtime + bump: minor From a67d6232687ab5131816b96808cda22ecb516798 Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:17:12 +0200 Subject: [PATCH 186/382] [ci] Migrate checks to GHA (#5511) PR migrates jobs `quick-benchmarks`, `cargo-clippy`, `check-try-runtime` and `check-core-crypto-features` from Gitlab to GitHub cc https://github.com/paritytech/ci_cd/issues/1006 --------- Co-authored-by: Maksym H <1177472+mordamax@users.noreply.github.com> --- .github/workflows/checks.yml | 55 ++++++++++++++++++++++++------------ .github/workflows/tests.yml | 55 ++++++++++++++++++++++-------------- .gitlab-ci.yml | 15 ---------- .gitlab/pipeline/check.yml | 42 --------------------------- .gitlab/pipeline/test.yml | 17 ----------- 5 files changed, 71 insertions(+), 113 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 054c7d786ca9..ad9d0d1a959d 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,12 +1,13 @@ -name: checks +name: Checks on: push: branches: - master pull_request: - types: [opened, synchronize, reopened, ready_for_review, labeled] + types: [opened, synchronize, reopened, ready_for_review] merge_group: + concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true @@ -14,29 +15,38 @@ concurrency: permissions: {} jobs: - changes: - # TODO: remove once migration is complete or this workflow is fully stable - if: contains(github.event.label.name, 'GHA-migration') - permissions: - pull-requests: read - uses: ./.github/workflows/reusable-check-changed-files.yml + # temporary disabled because currently doesn't work in merge queue + # changes: + # permissions: + # pull-requests: read + # uses: ./.github/workflows/reusable-check-changed-files.yml set-image: # GitHub Actions allows using 'env' in a container context. # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 # This workaround sets the container image for each job using 'set-image' job output. runs-on: ubuntu-latest - timeout-minutes: 20 outputs: IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} steps: - name: Checkout uses: actions/checkout@v4 - id: set_image run: cat .github/env >> $GITHUB_OUTPUT + # By default we use spot machines that can be terminated at any time. + # Merge queues use persistent runners to avoid kicking off from queue when the runner is terminated. + - id: set_runner + run: | + # Run merge queues on persistent runners + if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT + else + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + fi cargo-clippy: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [set-image, changes] # , build-frame-omni-bencher ] - if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [set-image] + # if: ${{ needs.changes.outputs.rust }} timeout-minutes: 40 container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -50,9 +60,9 @@ jobs: forklift cargo clippy --all-targets --locked --workspace forklift cargo clippy --all-targets --all-features --locked --workspace check-try-runtime: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [set-image, changes] # , build-frame-omni-bencher ] - if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [set-image] + # if: ${{ needs.changes.outputs.rust }} timeout-minutes: 40 container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -69,9 +79,9 @@ jobs: forklift cargo check --locked --all --features try-runtime,experimental # check-core-crypto-features works fast without forklift check-core-crypto-features: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [set-image, changes] # , build-frame-omni-bencher ] - if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [set-image] + # if: ${{ needs.changes.outputs.rust }} timeout-minutes: 30 container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -88,3 +98,12 @@ jobs: cd substrate/primitives/keyring ./check-features-variants.sh cd - + # name of this job must be unique across all workflows + # otherwise GitHub will mark all these jobs as required + confirm-required-checks-passed: + runs-on: ubuntu-latest + name: All checks passed + # If any new job gets added, be sure to add it to this array + needs: [cargo-clippy, check-try-runtime, check-core-crypto-features] + steps: + - run: echo '### Good job! All the checks passed 🚀' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1be2dd7921e0..25761fb94fd3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,17 +5,18 @@ on: branches: - master pull_request: - types: [ opened, synchronize, reopened, ready_for_review ] + types: [opened, synchronize, reopened, ready_for_review] merge_group: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: - changes: - permissions: - pull-requests: read - uses: ./.github/workflows/reusable-check-changed-files.yml + # disabled because currently doesn't work in merge queue + # changes: + # permissions: + # pull-requests: read + # uses: ./.github/workflows/reusable-check-changed-files.yml set-image: # GitHub Actions allows using 'env' in a container context. @@ -24,16 +25,28 @@ jobs: runs-on: ubuntu-latest outputs: IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} steps: - name: Checkout uses: actions/checkout@v4 - id: set_image run: cat .github/env >> $GITHUB_OUTPUT + # By default we use spot machines that can be terminated at any time. + # Merge queues use persistent runners to avoid kicking off from queue when the runner is terminated. + - id: set_runner + run: | + # Run merge queues on persistent runners + if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT + else + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + fi + # This job runs all benchmarks defined in the `/bin/node/runtime` once to check that there are no errors. quick-benchmarks: - needs: [ set-image, changes ] - if: ${{ needs.changes.outputs.rust }} - runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image] + # if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.set-image.outputs.RUNNER }} timeout-minutes: 60 container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -46,13 +59,13 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: script - run: time forklift cargo run --locked --release -p staging-node-cli --bin substrate-node --features runtime-benchmarks -- benchmark pallet --chain dev --pallet "*" --extrinsic "*" --steps 2 --repeat 1 --quiet + run: forklift cargo run --locked --release -p staging-node-cli --bin substrate-node --features runtime-benchmarks -- benchmark pallet --chain dev --pallet "*" --extrinsic "*" --steps 2 --repeat 1 --quiet # cf https://github.com/paritytech/polkadot-sdk/issues/1652 test-syscalls: - needs: [ set-image, changes ] - if: ${{ needs.changes.outputs.rust }} - runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image] + # if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.set-image.outputs.RUNNER }} timeout-minutes: 60 container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -63,21 +76,21 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: script + id: test run: | forklift cargo build --locked --profile production --target x86_64-unknown-linux-musl --bin polkadot-execute-worker --bin polkadot-prepare-worker cd polkadot/scripts/list-syscalls ./list-syscalls.rb ../../../target/x86_64-unknown-linux-musl/production/polkadot-execute-worker --only-used-syscalls | diff -u execute-worker-syscalls - ./list-syscalls.rb ../../../target/x86_64-unknown-linux-musl/production/polkadot-prepare-worker --only-used-syscalls | diff -u prepare-worker-syscalls - - # todo: - # after_script: - # - if [[ "$CI_JOB_STATUS" == "failed" ]]; then - # printf "The x86_64 syscalls used by the worker binaries have changed. Please review if this is expected and update polkadot/scripts/list-syscalls/*-worker-syscalls as needed.\n"; - # fi + - name: on_failure + if: failure() && steps.test.outcome == 'failure' + run: | + echo "The x86_64 syscalls used by the worker binaries have changed. Please review if this is expected and update polkadot/scripts/list-syscalls/*-worker-syscalls as needed." >> $GITHUB_STEP_SUMMARY cargo-check-all-benches: - needs: [ set-image, changes ] - if: ${{ needs.changes.outputs.rust }} - runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image] + # if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.set-image.outputs.RUNNER }} timeout-minutes: 60 container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -87,4 +100,4 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: script - run: time forklift cargo check --all --benches + run: forklift cargo check --all --benches diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7f2babc6bd47..c51d0ce5627e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -340,11 +340,6 @@ cancel-pipeline-check-tracing: needs: - job: check-tracing -cancel-pipeline-cargo-clippy: - extends: .cancel-pipeline-template - needs: - - job: cargo-clippy - cancel-pipeline-build-linux-stable: extends: .cancel-pipeline-template needs: @@ -370,16 +365,6 @@ cancel-pipeline-test-frame-ui: needs: - job: test-frame-ui -cancel-pipeline-quick-benchmarks: - extends: .cancel-pipeline-template - needs: - - job: quick-benchmarks - -cancel-pipeline-check-try-runtime: - extends: .cancel-pipeline-template - needs: - - job: check-try-runtime - cancel-pipeline-test-frame-examples-compile-to-wasm: extends: .cancel-pipeline-template needs: diff --git a/.gitlab/pipeline/check.yml b/.gitlab/pipeline/check.yml index 3e94eb77c7b4..64bef8d6e005 100644 --- a/.gitlab/pipeline/check.yml +++ b/.gitlab/pipeline/check.yml @@ -1,29 +1,3 @@ -cargo-clippy: - stage: check - extends: - - .docker-env - - .common-refs - - .pipeline-stopper-artifacts - variables: - RUSTFLAGS: "-D warnings" - script: - - SKIP_WASM_BUILD=1 cargo clippy --all-targets --locked --workspace --quiet - - SKIP_WASM_BUILD=1 cargo clippy --all-targets --all-features --locked --workspace --quiet - -check-try-runtime: - stage: check - extends: - - .docker-env - - .common-refs - script: - - time cargo check --locked --all --features try-runtime - # this is taken from cumulus - # Check that parachain-template will compile with `try-runtime` feature flag. - - time cargo check --locked -p parachain-template-node --features try-runtime - # add after https://github.com/paritytech/substrate/pull/14502 is merged - # experimental code may rely on try-runtime and vice-versa - - time cargo check --locked --all --features try-runtime,experimental - # from substrate # not sure if it's needed in monorepo check-dependency-rules: @@ -121,19 +95,3 @@ check-runtime-migration-rococo: WASM: "rococo_runtime.compact.compressed.wasm" URI: "wss://try-runtime-rococo.polkadot.io:443" SUBCOMMAND_EXTRA_ARGS: "--no-weight-warnings" - -check-core-crypto-features: - stage: check - extends: - - .docker-env - - .common-refs - script: - - pushd substrate/primitives/core - - ./check-features-variants.sh - - popd - - pushd substrate/primitives/application-crypto - - ./check-features-variants.sh - - popd - - pushd substrate/primitives/keyring - - ./check-features-variants.sh - - popd diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index 319c95ad6112..85f6e8dc780b 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -403,23 +403,6 @@ test-frame-ui: - time cargo test --locked -q --profile testnet --manifest-path substrate/primitives/runtime-interface/Cargo.toml - cat /cargo_target_dir/debug/.fingerprint/memory_units-759eddf317490d2b/lib-memory_units.json || true -# This job runs all benchmarks defined in the `/bin/node/runtime` once to check that there are no errors. -quick-benchmarks: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - variables: - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-C debug-assertions -D warnings" - RUST_BACKTRACE: "full" - WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" - script: - - time cargo run --locked --release -p staging-node-cli --bin substrate-node --features runtime-benchmarks --quiet -- benchmark pallet --chain dev --pallet "*" --extrinsic "*" --steps 2 --repeat 1 --quiet - quick-benchmarks-omni: stage: test extends: From 9374643b15daa401c73c8082e7bc63267e15b17e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lech=20G=C5=82owiak?= Date: Thu, 29 Aug 2024 13:17:07 +0200 Subject: [PATCH 187/382] Add an utility function to get the first timestamp of a slot (#5316) # Description Add `starting_timestamp` function for `Slot` type. ## Integration This is an addition of public function to a type, so integration should be seamless for idiomatic use of Rust. ## Review Notes Since `Slot` is just a slot number, the it's starting timestamp depends on `SlotDuration` which is a parameter to the added function. This function can be seen as dual to existing `fn from_timestamp`. Because there is a potential for overflow, the return type is `Option`. Q1: should I introduce tests for in this crate and add cases for both case: overflow (`None`) and no overflow (`Some`)? Q2: How can I add labels? IMO they should be `T0-node` and `D0-easy` but I cannot add them using GH interface. # Checklist * [x] My PR includes a detailed description as outlined in the "Description" and its two subsections above. * [ ] My PR follows the [labeling requirements](CONTRIBUTING.md#Process) of this project (at minimum one label for `T` required) * External contributors: ask maintainers to put the right label on your PR. * [ ] I have made corresponding changes to the documentation (if applicable) * [ ] I have added tests that prove my fix is effective or that my feature works (if applicable) --------- Co-authored-by: Squirrel Co-authored-by: Davide Galassi --- prdoc/pr_5316.prdoc | 11 +++++++++++ substrate/primitives/consensus/slots/src/lib.rs | 7 +++++++ 2 files changed, 18 insertions(+) create mode 100644 prdoc/pr_5316.prdoc diff --git a/prdoc/pr_5316.prdoc b/prdoc/pr_5316.prdoc new file mode 100644 index 000000000000..75b431c941d5 --- /dev/null +++ b/prdoc/pr_5316.prdoc @@ -0,0 +1,11 @@ +title: add timestamp function to sp-consensus-slots + +doc: + - audience: Node Dev + description: | + Added timestamp function to sp-consensus-slots to get the first timestamp + of the given slot. + +crates: + - name: sp-consensus-slots + bump: minor diff --git a/substrate/primitives/consensus/slots/src/lib.rs b/substrate/primitives/consensus/slots/src/lib.rs index eb3b3d3a449f..dfa46fcf2571 100644 --- a/substrate/primitives/consensus/slots/src/lib.rs +++ b/substrate/primitives/consensus/slots/src/lib.rs @@ -91,6 +91,13 @@ impl Slot { Slot(timestamp.as_millis() / slot_duration.as_millis()) } + /// Timestamp of the start of the slot. + /// + /// Returns `None` if would overflow for given `SlotDuration`. + pub fn timestamp(&self, slot_duration: SlotDuration) -> Option { + slot_duration.as_millis().checked_mul(self.0).map(Timestamp::new) + } + /// Saturating addition. pub fn saturating_add>(self, rhs: T) -> Self { Self(self.0.saturating_add(rhs.into())) From ba48e4b8cddaf4be4a618b37390fcf6175017daa Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 29 Aug 2024 14:08:25 +0200 Subject: [PATCH 188/382] [CI] Fixup backport bot (#5517) Changes: - Backport bot should just commit merge conflicts instead of failing. --- .github/workflows/command-backport.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/command-backport.yml b/.github/workflows/command-backport.yml index 4c63297efc18..72da40f19839 100644 --- a/.github/workflows/command-backport.yml +++ b/.github/workflows/command-backport.yml @@ -43,6 +43,10 @@ jobs: --> pull_title: | [${target_branch}] Backport #${pull_number} + experimental: > + { + "conflict_resolution": "draft_commit_conflicts" + } - name: Label Backports if: ${{ steps.backport.outputs.created_pull_numbers != '' }} From ddd58c15ada7570189a269289ba92778c60ea24a Mon Sep 17 00:00:00 2001 From: ordian Date: Thu, 29 Aug 2024 14:12:44 +0200 Subject: [PATCH 189/382] inclusion: bench `enact_candidate` weight (#5270) On top of #5082. ## Background Previously, before #3479, we would [include](https://github.com/paritytech/polkadot-sdk/blame/75074952a859f90213ea25257b71ec2189dbcfc1/polkadot/runtime/parachains/src/builder.rs#L508C12-L508C44) the cost enacting the candidate into the cost of processing a single bitfield. [Now](https://github.com/paritytech/polkadot-sdk/blame/dd48544a573dd02da2082cec1dda7ce735e2e719/polkadot/runtime/parachains/src/builder.rs#L529) it is different, although the benchmarks seems to be not-up-to date. Including the cost of enacting a candidate into a processing a single bitfield cost was incorrect, since we multiple that by the number of bitfields we have. Instead, we should separate calculate the cost of processing a single bitfield without enactment, and multiple the cost of enactment by the actual number of processed candidates (which is limited by the number cores, not validators). ## Bench Previously, the weight of `enact_candidate` was calculated manually (without a benchmark) and then neglected: https://github.com/paritytech/polkadot-sdk/blob/dd48544a573dd02da2082cec1dda7ce735e2e719/polkadot/runtime/parachains/src/inclusion/mod.rs#L584 In this PR, we have a benchmark for it and it's based on the number of ump and sent hrmp messages as well as whether the candidate has a runtime upgrade (new_validation_code). The differences from the previous attempt https://github.com/paritytech/polkadot/pull/6929 are that * we don't include the cost of enactment into the cost of processing a backed candidate. The reason for it is that enactment happens not in the same block as backing (typically the next one), since we process bitfields before backing votes. * we don't take into account the size of the runtime upgrade, the benchmark weight doesn't seem to depend much on it, but rather whether there was one or not. Similarly to the previous attempt, we don't account for dmp messages (fixed cost). Also we don't account properly for received hrmp messages (hrmp_watermark) because the cost of it depends on the runtime state and can't be statically deduced in the benchmark (unless we pass the information about channels as benchmark u32 arguments). The total weight cost of processing a parainherent now includes the cost of enactment of each candidate, but we don't do filtering based on that (because we enact after processing bitfields and making other changes to the storage). ## Numbers ``` Reads = 7 + (0 * u) + (3 * h) + (8 * c) Writes = 10 + (1 * u) + (3 * h) + (7 * c) ``` In addition, there is a fixed cost of a few of ms (!) per candidate. This might result a full block slightly overflowing its weight with 200 enacted candidates, which in turn could prevent non-mandatory transactions from being included in a block. Given our modest limits on max ump and hrmp messages: ``` maxUpwardMessageNumPerCandidate: 16 hrmpMaxMessageNumPerCandidate: 10 ``` and the fact that runtime upgrades are can't happen very frequently (`validation_upgrade_cooldown`), we might only go over the limits in case of many disputes. TODOs: - [x] Fix the overweight test - [x] Generate the weights for Westend and Rococo - [x] PRDoc --------- Co-authored-by: command-bot <> Co-authored-by: Alin Dima --- polkadot/node/core/pvf/common/Cargo.toml | 4 +- polkadot/runtime/parachains/src/builder.rs | 31 ++--- polkadot/runtime/parachains/src/dmp.rs | 3 +- polkadot/runtime/parachains/src/hrmp.rs | 19 +-- .../parachains/src/inclusion/benchmarking.rs | 123 ++++++++++++++++- .../runtime/parachains/src/inclusion/mod.rs | 67 ++++++---- .../runtime/parachains/src/inclusion/tests.rs | 5 +- polkadot/runtime/parachains/src/mock.rs | 10 +- polkadot/runtime/parachains/src/paras/mod.rs | 32 ++--- .../parachains/src/paras_inherent/mod.rs | 56 +++++--- .../parachains/src/paras_inherent/tests.rs | 59 ++++++++ .../parachains/src/paras_inherent/weights.rs | 23 ++++ .../polkadot_runtime_parachains_inclusion.rs | 117 +++++++++++----- .../polkadot_runtime_parachains_inclusion.rs | 126 +++++++++++++----- prdoc/pr_5270.prdoc | 20 +++ 15 files changed, 515 insertions(+), 180 deletions(-) create mode 100644 prdoc/pr_5270.prdoc diff --git a/polkadot/node/core/pvf/common/Cargo.toml b/polkadot/node/core/pvf/common/Cargo.toml index bf663b4cfcea..903c8dd1af29 100644 --- a/polkadot/node/core/pvf/common/Cargo.toml +++ b/polkadot/node/core/pvf/common/Cargo.toml @@ -17,9 +17,7 @@ libc = { workspace = true } nix = { features = ["resource", "sched"], workspace = true } thiserror = { workspace = true } -codec = { features = [ - "derive", -], workspace = true } +codec = { features = ["derive"], workspace = true } polkadot-parachain-primitives = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index 59afff359d08..65e56881c315 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -68,6 +68,21 @@ fn account(name: &'static str, index: u32, seed: u32) -> Acco .expect("infinite input; no invalid input; qed") } +pub fn generate_validator_pairs( + validator_count: u32, +) -> Vec<(T::AccountId, ValidatorId)> { + (0..validator_count) + .map(|i| { + let public = ValidatorId::generate_pair(None); + + // The account Id is not actually used anywhere, just necessary to fulfill the + // expected type of the `validators` param of `test_trigger_on_new_session`. + let account: T::AccountId = account("validator", i, i); + (account, public) + }) + .collect() +} + /// Create a 32 byte slice based on the given number. fn byte32_slice_from(n: u32) -> [u8; 32] { let mut slice = [0u8; 32]; @@ -423,20 +438,6 @@ impl BenchBuilder { } } - /// Generate validator key pairs and account ids. - fn generate_validator_pairs(validator_count: u32) -> Vec<(T::AccountId, ValidatorId)> { - (0..validator_count) - .map(|i| { - let public = ValidatorId::generate_pair(None); - - // The account Id is not actually used anywhere, just necessary to fulfill the - // expected type of the `validators` param of `test_trigger_on_new_session`. - let account: T::AccountId = account("validator", i, i); - (account, public) - }) - .collect() - } - fn signing_context(&self) -> SigningContext { SigningContext { parent_hash: Self::header(self.block_number).hash(), @@ -800,7 +801,7 @@ impl BenchBuilder { c.scheduler_params.num_cores = used_cores as u32; }); - let validator_ids = Self::generate_validator_pairs(self.max_validators()); + let validator_ids = generate_validator_pairs::(self.max_validators()); let target_session = SessionIndex::from(self.target_session); let builder = self.setup_session(target_session, validator_ids, used_cores, extra_cores); diff --git a/polkadot/runtime/parachains/src/dmp.rs b/polkadot/runtime/parachains/src/dmp.rs index 54e112d1b8b4..03580e11b8e9 100644 --- a/polkadot/runtime/parachains/src/dmp.rs +++ b/polkadot/runtime/parachains/src/dmp.rs @@ -287,7 +287,7 @@ impl Pallet { } /// Prunes the specified number of messages from the downward message queue of the given para. - pub(crate) fn prune_dmq(para: ParaId, processed_downward_messages: u32) -> Weight { + pub(crate) fn prune_dmq(para: ParaId, processed_downward_messages: u32) { let q_len = DownwardMessageQueues::::mutate(para, |q| { let processed_downward_messages = processed_downward_messages as usize; if processed_downward_messages > q.len() { @@ -306,7 +306,6 @@ impl Pallet { if q_len <= (threshold as usize) { Self::decrease_fee_factor(para); } - T::DbWeight::get().reads_writes(1, 1) } /// Returns the Head of Message Queue Chain for the given para or `None` if there is none diff --git a/polkadot/runtime/parachains/src/hrmp.rs b/polkadot/runtime/parachains/src/hrmp.rs index 8b01a755c3c7..b149404b41b8 100644 --- a/polkadot/runtime/parachains/src/hrmp.rs +++ b/polkadot/runtime/parachains/src/hrmp.rs @@ -1305,9 +1305,7 @@ impl Pallet { remaining } - pub(crate) fn prune_hrmp(recipient: ParaId, new_hrmp_watermark: BlockNumberFor) -> Weight { - let mut weight = Weight::zero(); - + pub(crate) fn prune_hrmp(recipient: ParaId, new_hrmp_watermark: BlockNumberFor) { // sift through the incoming messages digest to collect the paras that sent at least one // message to this parachain between the old and new watermarks. let senders = HrmpChannelDigests::::mutate(&recipient, |digest| { @@ -1323,7 +1321,6 @@ impl Pallet { *digest = leftover; senders }); - weight += T::DbWeight::get().reads_writes(1, 1); // having all senders we can trivially find out the channels which we need to prune. let channels_to_prune = @@ -1356,21 +1353,13 @@ impl Pallet { channel.total_size -= pruned_size as u32; } }); - - weight += T::DbWeight::get().reads_writes(2, 2); } HrmpWatermarks::::insert(&recipient, new_hrmp_watermark); - weight += T::DbWeight::get().reads_writes(0, 1); - - weight } /// Process the outbound HRMP messages by putting them into the appropriate recipient queues. - /// - /// Returns the amount of weight consumed. - pub(crate) fn queue_outbound_hrmp(sender: ParaId, out_hrmp_msgs: HorizontalMessages) -> Weight { - let mut weight = Weight::zero(); + pub(crate) fn queue_outbound_hrmp(sender: ParaId, out_hrmp_msgs: HorizontalMessages) { let now = frame_system::Pallet::::block_number(); for out_msg in out_hrmp_msgs { @@ -1426,11 +1415,7 @@ impl Pallet { recipient_digest.push((now, vec![sender])); } HrmpChannelDigests::::insert(&channel_id.recipient, recipient_digest); - - weight += T::DbWeight::get().reads_writes(2, 2); } - - weight } /// Initiate opening a channel from a parachain to a given recipient with given channel diff --git a/polkadot/runtime/parachains/src/inclusion/benchmarking.rs b/polkadot/runtime/parachains/src/inclusion/benchmarking.rs index 169e858deda8..978ef718ea40 100644 --- a/polkadot/runtime/parachains/src/inclusion/benchmarking.rs +++ b/polkadot/runtime/parachains/src/inclusion/benchmarking.rs @@ -15,23 +15,134 @@ // along with Polkadot. If not, see . use super::*; +use crate::{ + builder::generate_validator_pairs, + configuration, + hrmp::{HrmpChannel, HrmpChannels}, + initializer, HeadData, ValidationCode, +}; +use bitvec::{bitvec, prelude::Lsb0}; use frame_benchmarking::benchmarks; use pallet_message_queue as mq; +use polkadot_primitives::{ + CandidateCommitments, CollatorId, CollatorSignature, CommittedCandidateReceipt, HrmpChannelId, + OutboundHrmpMessage, SessionIndex, +}; +use sp_core::sr25519; + +fn create_candidate_commitments( + para_id: ParaId, + head_data: HeadData, + max_msg_len: usize, + ump_msg_count: u32, + hrmp_msg_count: u32, + code_upgrade: bool, +) -> CandidateCommitments { + let upward_messages = { + let unbounded = create_messages(max_msg_len, ump_msg_count as _); + BoundedVec::truncate_from(unbounded) + }; + + let horizontal_messages = { + let unbounded = create_messages(max_msg_len, hrmp_msg_count as _); + + for n in 0..unbounded.len() { + let channel_id = HrmpChannelId { sender: para_id, recipient: para_id + n as u32 + 1 }; + HrmpChannels::::insert( + &channel_id, + HrmpChannel { + sender_deposit: 42, + recipient_deposit: 42, + max_capacity: 10_000_000, + max_total_size: 1_000_000_000, + max_message_size: 10_000_000, + msg_count: 0, + total_size: 0, + mqc_head: None, + }, + ); + } + + let unbounded = unbounded + .into_iter() + .enumerate() + .map(|(n, data)| OutboundHrmpMessage { recipient: para_id + n as u32 + 1, data }) + .collect(); + BoundedVec::truncate_from(unbounded) + }; + + let new_validation_code = code_upgrade.then_some(ValidationCode(vec![42u8; 1024])); + + CandidateCommitments:: { + upward_messages, + horizontal_messages, + new_validation_code, + head_data, + processed_downward_messages: 0, + hrmp_watermark: 10, + } +} + +fn create_messages(msg_len: usize, n_msgs: usize) -> Vec> { + let best_number = 73_u8; // Chuck Norris of numbers + vec![vec![best_number; msg_len]; n_msgs] +} benchmarks! { where_clause { where - T: mq::Config, + T: mq::Config + configuration::Config + initializer::Config, } - receive_upward_messages { - let i in 1 .. 1000; + enact_candidate { + let u in 1 .. 32; + let h in 1 .. 32; + let c in 0 .. 1; + + let para = 42_u32.into(); // not especially important. let max_len = mq::MaxMessageLenOf::::get() as usize; - let para = 42u32.into(); // not especially important. - let upward_messages = vec![vec![0; max_len]; i as usize]; + + let config = configuration::ActiveConfig::::get(); + let n_validators = config.max_validators.unwrap_or(500); + let validators = generate_validator_pairs::(n_validators); + + let session = SessionIndex::from(0u32); + initializer::Pallet::::test_trigger_on_new_session( + false, + session, + validators.iter().map(|(a, v)| (a, v.clone())), + None, + ); + let backing_group_size = config.scheduler_params.max_validators_per_core.unwrap_or(5); + let head_data = HeadData(vec![0xFF; 1024]); + + let relay_parent_number = BlockNumberFor::::from(10u32); + let commitments = create_candidate_commitments::(para, head_data, max_len, u, h, c != 0); + let backers = bitvec![u8, Lsb0; 1; backing_group_size as usize]; + let availability_votes = bitvec![u8, Lsb0; 1; n_validators as usize]; + let core_index = CoreIndex::from(0); + let backing_group = GroupIndex::from(0); + + let descriptor = CandidateDescriptor:: { + para_id: para, + relay_parent: Default::default(), + collator: CollatorId::from(sr25519::Public::from_raw([42u8; 32])), + persisted_validation_data_hash: Default::default(), + pov_hash: Default::default(), + erasure_root: Default::default(), + signature: CollatorSignature::from(sr25519::Signature::from_raw([42u8; 64])), + para_head: Default::default(), + validation_code_hash: ValidationCode(vec![1, 2, 3]).hash(), + }; + + let receipt = CommittedCandidateReceipt:: { + descriptor, + commitments, + }; + Pallet::::receive_upward_messages(para, vec![vec![0; max_len]; 1].as_slice()); - }: { Pallet::::receive_upward_messages(para, upward_messages.as_slice()) } + } : { Pallet::::enact_candidate(relay_parent_number, receipt, backers, availability_votes, core_index, backing_group) } impl_benchmark_test_suite!( Pallet, diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index 115eee975530..fbf13339dfca 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -65,18 +65,23 @@ mod benchmarking; pub mod migration; pub trait WeightInfo { - fn receive_upward_messages(i: u32) -> Weight; + /// Weight for `enact_candidate` extrinsic given the number of sent messages + /// (ump, hrmp) and whether there is a new code for a runtime upgrade. + /// + /// NOTE: due to a shortcoming of the current benchmarking framework, + /// we use `u32` for the code upgrade, even though it is a `bool`. + fn enact_candidate(u: u32, h: u32, c: u32) -> Weight; } pub struct TestWeightInfo; impl WeightInfo for TestWeightInfo { - fn receive_upward_messages(_: u32) -> Weight { - Weight::MAX + fn enact_candidate(_u: u32, _h: u32, _c: u32) -> Weight { + Weight::zero() } } impl WeightInfo for () { - fn receive_upward_messages(_: u32) -> Weight { + fn enact_candidate(_u: u32, _h: u32, _c: u32) -> Weight { Weight::zero() } } @@ -507,7 +512,7 @@ impl Pallet { pub(crate) fn update_pending_availability_and_get_freed_cores( validators: &[ValidatorId], signed_bitfields: SignedAvailabilityBitfields, - ) -> Vec<(CoreIndex, CandidateHash)> { + ) -> (Weight, Vec<(CoreIndex, CandidateHash)>) { let threshold = availability_threshold(validators.len()); let mut votes_per_core: BTreeMap> = BTreeMap::new(); @@ -528,6 +533,7 @@ impl Pallet { } let mut freed_cores = vec![]; + let mut weight = Weight::zero(); let pending_paraids: Vec<_> = PendingAvailability::::iter_keys().collect(); for paraid in pending_paraids { @@ -581,7 +587,17 @@ impl Pallet { descriptor: candidate.descriptor, commitments: candidate.commitments, }; - let _weight = Self::enact_candidate( + + let has_runtime_upgrade = + receipt.commitments.new_validation_code.as_ref().map_or(0, |_| 1); + let u = receipt.commitments.upward_messages.len() as u32; + let h = receipt.commitments.horizontal_messages.len() as u32; + let enact_weight = ::WeightInfo::enact_candidate( + u, + h, + has_runtime_upgrade, + ); + Self::enact_candidate( candidate.relay_parent_number, receipt, candidate.backers, @@ -589,13 +605,14 @@ impl Pallet { candidate.core, candidate.backing_group, ); + weight.saturating_accrue(enact_weight); } } } }); } - freed_cores + (weight, freed_cores) } /// Process candidates that have been backed. Provide a set of @@ -842,7 +859,7 @@ impl Pallet { availability_votes: BitVec, core_index: CoreIndex, backing_group: GroupIndex, - ) -> Weight { + ) { let plain = receipt.to_plain(); let commitments = receipt.commitments; let config = configuration::ActiveConfig::::get(); @@ -863,38 +880,36 @@ impl Pallet { .map(|(i, _)| ValidatorIndex(i as _)), ); - // initial weight is config read. - let mut weight = T::DbWeight::get().reads_writes(1, 0); if let Some(new_code) = commitments.new_validation_code { // Block number of candidate's inclusion. let now = frame_system::Pallet::::block_number(); - weight.saturating_add(paras::Pallet::::schedule_code_upgrade( + paras::Pallet::::schedule_code_upgrade( receipt.descriptor.para_id, new_code, now, &config, UpgradeStrategy::SetGoAheadSignal, - )); + ); } // enact the messaging facet of the candidate. - weight.saturating_accrue(dmp::Pallet::::prune_dmq( + dmp::Pallet::::prune_dmq( receipt.descriptor.para_id, commitments.processed_downward_messages, - )); - weight.saturating_accrue(Self::receive_upward_messages( + ); + Self::receive_upward_messages( receipt.descriptor.para_id, commitments.upward_messages.as_slice(), - )); - weight.saturating_accrue(hrmp::Pallet::::prune_hrmp( + ); + hrmp::Pallet::::prune_hrmp( receipt.descriptor.para_id, BlockNumberFor::::from(commitments.hrmp_watermark), - )); - weight.saturating_accrue(hrmp::Pallet::::queue_outbound_hrmp( + ); + hrmp::Pallet::::queue_outbound_hrmp( receipt.descriptor.para_id, commitments.horizontal_messages, - )); + ); Self::deposit_event(Event::::CandidateIncluded( plain, @@ -903,11 +918,11 @@ impl Pallet { backing_group, )); - weight.saturating_add(paras::Pallet::::note_new_head( + paras::Pallet::::note_new_head( receipt.descriptor.para_id, commitments.head_data, relay_parent_number, - )) + ); } pub(crate) fn relay_dispatch_queue_size(para_id: ParaId) -> (u32, u32) { @@ -972,7 +987,7 @@ impl Pallet { /// This function is infallible since the candidate was already accepted and we therefore need /// to deal with the messages as given. Messages that are too long will be ignored since such /// candidates should have already been rejected in [`Self::check_upward_messages`]. - pub(crate) fn receive_upward_messages(para: ParaId, upward_messages: &[Vec]) -> Weight { + pub(crate) fn receive_upward_messages(para: ParaId, upward_messages: &[Vec]) { let bounded = upward_messages .iter() .filter_map(|d| { @@ -991,19 +1006,17 @@ impl Pallet { pub(crate) fn receive_bounded_upward_messages( para: ParaId, messages: Vec>>, - ) -> Weight { + ) { let count = messages.len() as u32; if count == 0 { - return Weight::zero() + return } T::MessageQueue::enqueue_messages( messages.into_iter(), AggregateMessageOrigin::Ump(UmpQueueId::Para(para)), ); - let weight = ::WeightInfo::receive_upward_messages(count); Self::deposit_event(Event::UpwardMessagesReceived { from: para, count }); - weight } /// Cleans up all timed out candidates as well as their descendant candidates. diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 3ead456cde5a..95fd66bf8e4f 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -366,10 +366,11 @@ pub(crate) fn process_bitfields( ) -> Vec<(CoreIndex, CandidateHash)> { let validators = shared::ActiveValidatorKeys::::get(); - ParaInclusion::update_pending_availability_and_get_freed_cores( + let (_weight, bitfields) = ParaInclusion::update_pending_availability_and_get_freed_cores( &validators[..], signed_bitfields, - ) + ); + bitfields } #[test] diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index fbe9ebf809b3..75c9e3a5c9b9 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -449,8 +449,16 @@ impl SendXcm for DummyXcmSender { } } +pub struct InclusionWeightInfo; + +impl crate::inclusion::WeightInfo for InclusionWeightInfo { + fn enact_candidate(_u: u32, _h: u32, _c: u32) -> Weight { + Weight::from_parts(1024 * 1024, 0) + } +} + impl crate::inclusion::Config for Test { - type WeightInfo = (); + type WeightInfo = InclusionWeightInfo; type RuntimeEvent = RuntimeEvent; type DisputesHandler = Disputes; type RewardValidators = TestRewardValidators; diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index a4c404de2a65..5048656e6363 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -1956,14 +1956,12 @@ impl Pallet { inclusion_block_number: BlockNumberFor, cfg: &configuration::HostConfiguration>, upgrade_strategy: UpgradeStrategy, - ) -> Weight { - let mut weight = T::DbWeight::get().reads(1); - + ) { // Should be prevented by checks in `schedule_code_upgrade_external` let new_code_len = new_code.0.len(); if new_code_len < MIN_CODE_SIZE as usize || new_code_len > cfg.max_code_size as usize { log::warn!(target: LOG_TARGET, "attempted to schedule an upgrade with invalid new validation code",); - return weight + return } // Enacting this should be prevented by the `can_upgrade_validation_code` @@ -1977,7 +1975,7 @@ impl Pallet { // NOTE: we cannot set `UpgradeGoAheadSignal` signal here since this will be reset by // the following call `note_new_head` log::warn!(target: LOG_TARGET, "ended up scheduling an upgrade while one is pending",); - return weight + return } let code_hash = new_code.hash(); @@ -1986,7 +1984,6 @@ impl Pallet { // process right away. // // We do not want to allow this since it will mess with the code reference counting. - weight += T::DbWeight::get().reads(1); if CurrentCodeHash::::get(&id) == Some(code_hash) { // NOTE: we cannot set `UpgradeGoAheadSignal` signal here since this will be reset by // the following call `note_new_head` @@ -1994,15 +1991,13 @@ impl Pallet { target: LOG_TARGET, "para tried to upgrade to the same code. Abort the upgrade", ); - return weight + return } // This is the start of the upgrade process. Prevent any further attempts at upgrading. - weight += T::DbWeight::get().writes(2); FutureCodeHash::::insert(&id, &code_hash); UpgradeRestrictionSignal::::insert(&id, UpgradeRestriction::Present); - weight += T::DbWeight::get().reads_writes(1, 1); let next_possible_upgrade_at = inclusion_block_number + cfg.validation_upgrade_cooldown; UpgradeCooldowns::::mutate(|upgrade_cooldowns| { let insert_idx = upgrade_cooldowns @@ -2011,14 +2006,12 @@ impl Pallet { upgrade_cooldowns.insert(insert_idx, (id, next_possible_upgrade_at)); }); - weight += Self::kick_off_pvf_check( + Self::kick_off_pvf_check( PvfCheckCause::Upgrade { id, included_at: inclusion_block_number, upgrade_strategy }, code_hash, new_code, cfg, ); - - weight } /// Makes sure that the given code hash has passed pre-checking. @@ -2108,11 +2101,11 @@ impl Pallet { id: ParaId, new_head: HeadData, execution_context: BlockNumberFor, - ) -> Weight { + ) { Heads::::insert(&id, &new_head); MostRecentContext::::insert(&id, execution_context); - let weight = if let Some(expected_at) = FutureCodeUpgrades::::get(&id) { + if let Some(expected_at) = FutureCodeUpgrades::::get(&id) { if expected_at <= execution_context { FutureCodeUpgrades::::remove(&id); UpgradeGoAheadSignal::::remove(&id); @@ -2122,14 +2115,10 @@ impl Pallet { new_code_hash } else { log::error!(target: LOG_TARGET, "Missing future code hash for {:?}", &id); - return T::DbWeight::get().reads_writes(3, 1 + 3) + return }; - let weight = Self::set_current_code(id, new_code_hash, expected_at); - - weight + T::DbWeight::get().reads_writes(3, 3) - } else { - T::DbWeight::get().reads_writes(1, 1 + 0) + Self::set_current_code(id, new_code_hash, expected_at); } } else { // This means there is no upgrade scheduled. @@ -2137,10 +2126,9 @@ impl Pallet { // In case the upgrade was aborted by the relay-chain we should reset // the `Abort` signal. UpgradeGoAheadSignal::::remove(&id); - T::DbWeight::get().reads_writes(1, 2) }; - weight.saturating_add(T::OnNewHead::on_new_head(id, &new_head)) + T::OnNewHead::on_new_head(id, &new_head); } /// Set the current code for the given parachain. diff --git a/polkadot/runtime/parachains/src/paras_inherent/mod.rs b/polkadot/runtime/parachains/src/paras_inherent/mod.rs index 9d27e86ef901..bd8d08a842c3 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/mod.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/mod.rs @@ -347,12 +347,12 @@ impl Pallet { let bitfields_weight = signed_bitfields_weight::(&bitfields); let disputes_weight = multi_dispute_statement_sets_weight::(&disputes); - // Weight before filtering/sanitization - let all_weight_before = candidates_weight + bitfields_weight + disputes_weight; + // Weight before filtering/sanitization except for enacting the candidates + let weight_before_filtering = candidates_weight + bitfields_weight + disputes_weight; - METRICS.on_before_filter(all_weight_before.ref_time()); - log::debug!(target: LOG_TARGET, "Size before filter: {}, candidates + bitfields: {}, disputes: {}", all_weight_before.proof_size(), candidates_weight.proof_size() + bitfields_weight.proof_size(), disputes_weight.proof_size()); - log::debug!(target: LOG_TARGET, "Time weight before filter: {}, candidates + bitfields: {}, disputes: {}", all_weight_before.ref_time(), candidates_weight.ref_time() + bitfields_weight.ref_time(), disputes_weight.ref_time()); + METRICS.on_before_filter(weight_before_filtering.ref_time()); + log::debug!(target: LOG_TARGET, "Size before filter: {}, candidates + bitfields: {}, disputes: {}", weight_before_filtering.proof_size(), candidates_weight.proof_size() + bitfields_weight.proof_size(), disputes_weight.proof_size()); + log::debug!(target: LOG_TARGET, "Time weight before filter: {}, candidates + bitfields: {}, disputes: {}", weight_before_filtering.ref_time(), candidates_weight.ref_time() + bitfields_weight.ref_time(), disputes_weight.ref_time()); let current_session = shared::CurrentSessionIndex::::get(); let expected_bits = scheduler::AvailabilityCores::::get().len(); @@ -409,7 +409,7 @@ impl Pallet { max_block_weight, ); - let all_weight_after = if context == ProcessInherentDataContext::ProvideInherent { + let mut all_weight_after = if context == ProcessInherentDataContext::ProvideInherent { // Assure the maximum block weight is adhered, by limiting bitfields and backed // candidates. Dispute statement sets were already limited before. let non_disputes_weight = apply_weight_limit::( @@ -424,11 +424,11 @@ impl Pallet { METRICS.on_after_filter(all_weight_after.ref_time()); log::debug!( - target: LOG_TARGET, - "[process_inherent_data] after filter: bitfields.len(): {}, backed_candidates.len(): {}, checked_disputes_sets.len() {}", - bitfields.len(), - backed_candidates.len(), - checked_disputes_sets.len() + target: LOG_TARGET, + "[process_inherent_data] after filter: bitfields.len(): {}, backed_candidates.len(): {}, checked_disputes_sets.len() {}", + bitfields.len(), + backed_candidates.len(), + checked_disputes_sets.len() ); log::debug!(target: LOG_TARGET, "Size after filter: {}, candidates + bitfields: {}, disputes: {}", all_weight_after.proof_size(), non_disputes_weight.proof_size(), checked_disputes_sets_consumed_weight.proof_size()); log::debug!(target: LOG_TARGET, "Time weight after filter: {}, candidates + bitfields: {}, disputes: {}", all_weight_after.ref_time(), non_disputes_weight.ref_time(), checked_disputes_sets_consumed_weight.ref_time()); @@ -440,17 +440,20 @@ impl Pallet { } else { // This check is performed in the context of block execution. Ensures inherent weight // invariants guaranteed by `create_inherent_data` for block authorship. - if all_weight_before.any_gt(max_block_weight) { + if weight_before_filtering.any_gt(max_block_weight) { log::error!( "Overweight para inherent data reached the runtime {:?}: {} > {}", parent_hash, - all_weight_before, + weight_before_filtering, max_block_weight ); } - ensure!(all_weight_before.all_lte(max_block_weight), Error::::InherentOverweight); - all_weight_before + ensure!( + weight_before_filtering.all_lte(max_block_weight), + Error::::InherentOverweight + ); + weight_before_filtering }; // Note that `process_checked_multi_dispute_data` will iterate and import each @@ -529,11 +532,32 @@ impl Pallet { // Process new availability bitfields, yielding any availability cores whose // work has now concluded. - let freed_concluded = + let (enact_weight, freed_concluded) = inclusion::Pallet::::update_pending_availability_and_get_freed_cores( &validator_public[..], bitfields.clone(), ); + all_weight_after.saturating_accrue(enact_weight); + log::debug!( + target: LOG_TARGET, + "Enacting weight: {}, all weight: {}", + enact_weight.ref_time(), + all_weight_after.ref_time(), + ); + + // It's possible that that after the enacting the candidates, the total weight + // goes over the limit, however, we can't do anything about it at this point. + // By using the `Mandatory` weight, we ensure the block is still accepted, + // but no other (user) transactions can be included. + if all_weight_after.any_gt(max_block_weight) { + log::warn!( + target: LOG_TARGET, + "Overweight para inherent data after enacting the candidates {:?}: {} > {}", + parent_hash, + all_weight_after, + max_block_weight, + ); + } // Inform the disputes module of all included candidates. for (_, candidate_hash) in &freed_concluded { diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index 59fbb0948373..ad89e68e9059 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -943,6 +943,65 @@ mod enter { }); } + // Ensure that even if the block is over weight due to candidates enactment, + // we still can import it. + #[test] + fn overweight_candidates_enactment_is_fine() { + sp_tracing::try_init_simple(); + new_test_ext(MockGenesisConfig::default()).execute_with(|| { + use crate::inclusion::WeightInfo as _; + + let mut backed_and_concluding = BTreeMap::new(); + // The number of candidates is chosen to go over the weight limit + // of the mock runtime together with the `enact_candidate`s weight. + let num_candidates = 5u32; + let max_weight = ::BlockWeights::get().max_block; + assert!(::WeightInfo::enact_candidate(0, 0, 0) + .saturating_mul(u64::from(num_candidates)) + .any_gt(max_weight)); + + for i in 0..num_candidates { + backed_and_concluding.insert(i, 2); + } + + let num_validators_per_core: u32 = 5; + let num_backed = backed_and_concluding.len(); + let bitfields_len = num_validators_per_core as usize * num_backed; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], + backed_and_concluding, + num_validators_per_core, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], + }); + + let expected_para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + assert_eq!(expected_para_inherent_data.bitfields.len(), bitfields_len); + assert_eq!(expected_para_inherent_data.backed_candidates.len(), num_backed); + assert_eq!(expected_para_inherent_data.disputes.len(), 0); + + let mut inherent_data = InherentData::new(); + inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) + .unwrap(); + + let limit_inherent_data = + Pallet::::create_inherent_inner(&inherent_data.clone()).unwrap(); + assert!(limit_inherent_data == expected_para_inherent_data); + + assert_ok!(Pallet::::enter( + frame_system::RawOrigin::None.into(), + limit_inherent_data, + )); + }); + } + fn max_block_weight_proof_size_adjusted() -> Weight { let raw_weight = ::BlockWeights::get().max_block; let block_length = ::BlockLength::get(); diff --git a/polkadot/runtime/parachains/src/paras_inherent/weights.rs b/polkadot/runtime/parachains/src/paras_inherent/weights.rs index 3e84c132aa24..81c926a90e0b 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/weights.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/weights.rs @@ -19,6 +19,7 @@ //! the relay chain, but we do care about the size of the block, by putting the tx in the //! proof_size we can use the already existing weight limiting code to limit the used size as well. +use crate::{configuration, inclusion}; use codec::{Encode, WrapperTypeEncode}; use polkadot_primitives::{ CheckedMultiDisputeStatementSet, MultiDisputeStatementSet, UncheckedSignedAvailabilityBitfield, @@ -96,6 +97,7 @@ pub fn paras_inherent_total_weight( backed_candidates_weight::(backed_candidates) .saturating_add(signed_bitfields_weight::(bitfields)) .saturating_add(multi_dispute_statement_sets_weight::(disputes)) + .saturating_add(enact_candidates_max_weight::(bitfields)) } pub fn multi_dispute_statement_sets_weight( @@ -156,6 +158,27 @@ pub fn signed_bitfield_weight(bitfield: &UncheckedSignedAvailabilityB ) } +/// Worst case scenario is all candidates have been enacted +/// and process a maximum number of messages. +pub fn enact_candidates_max_weight( + bitfields: &UncheckedSignedAvailabilityBitfields, +) -> Weight { + let config = configuration::ActiveConfig::::get(); + let max_ump_msgs = config.max_upward_message_num_per_candidate; + let max_hrmp_msgs = config.hrmp_max_message_num_per_candidate; + // No bitfields - no enacted candidates + let bitfield_size = bitfields.first().map(|b| b.unchecked_payload().0.len()).unwrap_or(0); + set_proof_size_to_tx_size( + <::WeightInfo as inclusion::WeightInfo>::enact_candidate( + max_ump_msgs, + max_hrmp_msgs, + 1, // runtime upgrade + ) + .saturating_mul(bitfield_size as u64), + bitfields, + ) +} + pub fn backed_candidate_weight( candidate: &BackedCandidate, ) -> Weight { diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs index da1b7a0dad9a..4c6ce8835573 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs @@ -14,27 +14,28 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Autogenerated weights for `runtime_parachains::inclusion` +//! Autogenerated weights for `polkadot_runtime_parachains::inclusion` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot +// target/production/polkadot // benchmark // pallet -// --chain=rococo-dev // --steps=50 // --repeat=20 -// --pallet=runtime_parachains::inclusion // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_parachains_inclusion.rs +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=polkadot_runtime_parachains::inclusion +// --chain=rococo-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -44,31 +45,79 @@ use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; -/// Weight functions for `runtime_parachains::inclusion`. +/// Weight functions for `polkadot_runtime_parachains::inclusion`. pub struct WeightInfo(PhantomData); impl polkadot_runtime_parachains::inclusion::WeightInfo for WeightInfo { - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:999) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(32818), added: 35293, mode: MaxEncodedLen) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Proof Skipped: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Storage: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// Proof Skipped: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// The range of component `i` is `[1, 1000]`. - fn receive_upward_messages(i: u32, ) -> Weight { + /// Storage: `Paras::FutureCodeHash` (r:1 w:1) + /// Proof: `Paras::FutureCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeCooldowns` (r:1 w:1) + /// Proof: `Paras::UpgradeCooldowns` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHash` (r:1 w:1) + /// Proof: `Paras::CodeByHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHashRefs` (r:1 w:1) + /// Proof: `Paras::CodeByHashRefs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:32) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) + /// Storage: `Hrmp::HrmpChannelDigests` (r:33 w:33) + /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannels` (r:32 w:32) + /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannelContents` (r:32 w:32) + /// Proof: `Hrmp::HrmpChannelContents` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::Paras` (r:1 w:0) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) + /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeRestrictionSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// The range of component `u` is `[1, 32]`. + /// The range of component `h` is `[1, 32]`. + /// The range of component `c` is `[0, 1]`. + fn enact_candidate(u: u32, h: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `33280` - // Estimated: `36283` - // Minimum execution time: 71_094_000 picoseconds. - Weight::from_parts(71_436_000, 0) - .saturating_add(Weight::from_parts(0, 36283)) - // Standard Error: 22_149 - .saturating_add(Weight::from_parts(51_495_472, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + // Measured: `33353 + c * (16114 ±0) + h * (75 ±0)` + // Estimated: `36818 + c * (26467 ±0) + h * (2551 ±0)` + // Minimum execution time: 4_829_551_000 picoseconds. + Weight::from_parts(1_892_697_027, 0) + .saturating_add(Weight::from_parts(0, 36818)) + // Standard Error: 793_993 + .saturating_add(Weight::from_parts(126_698_671, 0).saturating_mul(u.into())) + // Standard Error: 793_993 + .saturating_add(Weight::from_parts(144_116_038, 0).saturating_mul(h.into())) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(h.into()))) + .saturating_add(T::DbWeight::get().reads((8_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(10)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(h.into()))) + .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 26467).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 2551).saturating_mul(h.into())) } } diff --git a/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs index 25909beb6a07..36a4c5c24c9a 100644 --- a/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs +++ b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs @@ -14,30 +14,28 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Autogenerated weights for `runtime_parachains::inclusion` +//! Autogenerated weights for `polkadot_runtime_parachains::inclusion` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner--ss9ysm1-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot +// target/production/polkadot // benchmark // pallet -// --chain=westend-dev // --steps=50 // --repeat=20 -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --pallet=runtime_parachains::inclusion // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/westend/src/weights/runtime_parachains_inclusion.rs +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=polkadot_runtime_parachains::inclusion +// --chain=westend-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,29 +45,87 @@ use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; -/// Weight functions for `runtime_parachains::inclusion`. +/// Weight functions for `polkadot_runtime_parachains::inclusion`. pub struct WeightInfo(PhantomData); impl polkadot_runtime_parachains::inclusion::WeightInfo for WeightInfo { - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:999) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(131122), added: 133597, mode: MaxEncodedLen) - /// Storage: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Proof Skipped: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Storage: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// Proof Skipped: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// The range of component `i` is `[1, 1000]`. - fn receive_upward_messages(i: u32, ) -> Weight { + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaSessionInfo::AccountKeys` (r:1 w:0) + /// Proof: `ParaSessionInfo::AccountKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Session::Validators` (r:1 w:0) + /// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Staking::ActiveEra` (r:1 w:0) + /// Proof: `Staking::ActiveEra` (`max_values`: Some(1), `max_size`: Some(13), added: 508, mode: `MaxEncodedLen`) + /// Storage: `Staking::ErasRewardPoints` (r:1 w:1) + /// Proof: `Staking::ErasRewardPoints` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeHash` (r:1 w:1) + /// Proof: `Paras::FutureCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeCooldowns` (r:1 w:1) + /// Proof: `Paras::UpgradeCooldowns` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHash` (r:1 w:1) + /// Proof: `Paras::CodeByHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHashRefs` (r:1 w:1) + /// Proof: `Paras::CodeByHashRefs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:32) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(131122), added: 133597, mode: `MaxEncodedLen`) + /// Storage: `Hrmp::HrmpChannelDigests` (r:33 w:33) + /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannels` (r:32 w:32) + /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannelContents` (r:32 w:32) + /// Proof: `Hrmp::HrmpChannelContents` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) + /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeRestrictionSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// The range of component `u` is `[1, 32]`. + /// The range of component `h` is `[1, 32]`. + /// The range of component `c` is `[0, 1]`. + fn enact_candidate(u: u32, h: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `131297` - // Estimated: `134587` - // Minimum execution time: 209_898_000 picoseconds. - Weight::from_parts(210_955_000, 0) - .saturating_add(Weight::from_parts(0, 134587)) - // Standard Error: 97_069 - .saturating_add(Weight::from_parts(207_030_437, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(3)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) + // Measured: `132737 + c * (15992 ±0) + h * (75 ±0)` + // Estimated: `136202 + c * (76098 ±0) + h * (2551 ±0)` + // Minimum execution time: 18_868_930_000 picoseconds. + Weight::from_parts(6_899_601_016, 0) + .saturating_add(Weight::from_parts(0, 136202)) + // Standard Error: 1_952_665 + .saturating_add(Weight::from_parts(467_810_135, 0).saturating_mul(u.into())) + // Standard Error: 1_952_665 + .saturating_add(Weight::from_parts(551_226_340, 0).saturating_mul(h.into())) + .saturating_add(T::DbWeight::get().reads(11)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(h.into()))) + .saturating_add(T::DbWeight::get().reads((8_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(11)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(h.into()))) + .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 76098).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 2551).saturating_mul(h.into())) } } diff --git a/prdoc/pr_5270.prdoc b/prdoc/pr_5270.prdoc new file mode 100644 index 000000000000..e6d7142cabd0 --- /dev/null +++ b/prdoc/pr_5270.prdoc @@ -0,0 +1,20 @@ +title: "Inclusion: account for enact_candidate weight" + +doc: + - audience: Runtime Dev + description: | + We are now properly accounting for the `enact_candidate`s weight in + processing of a relay chain block inherent. This may result in some + of the user relay chain transactions not being included in the block if + it's really heavy. This should be fine though as we are moving towards + the minimal relay chain. + +crates: +- name: polkadot-runtime-parachains + bump: major +- name: westend-runtime + bump: patch +- name: rococo-runtime + bump: patch +- name: polkadot-node-core-pvf-common + bump: none From 61bfcb846c2582e9fdf5068f91a20c107d6f030e Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Thu, 29 Aug 2024 13:28:34 +0100 Subject: [PATCH 190/382] Add new try-state check invariant for nomination-pools (points >= stake) (#5465) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes https://github.com/paritytech/polkadot-sdk/issues/5448 --------- Co-authored-by: Gonçalo Pestana Co-authored-by: Ankan <10196091+Ank4n@users.noreply.github.com> --- prdoc/pr_5465.prdoc | 10 ++++++++++ substrate/frame/nomination-pools/src/lib.rs | 6 ++++++ 2 files changed, 16 insertions(+) create mode 100644 prdoc/pr_5465.prdoc diff --git a/prdoc/pr_5465.prdoc b/prdoc/pr_5465.prdoc new file mode 100644 index 000000000000..ae185dc250f6 --- /dev/null +++ b/prdoc/pr_5465.prdoc @@ -0,0 +1,10 @@ +title: try-state check invariant for nomination-pools (points >= stake) + +doc: + - audience: Runtime Dev + description: | + Adds a new try-state invariant to the nomination pools that checks that for each bonded pool, the pool's points can never be lower than its staked balance. + +crates: + - name: pallet-nomination-pools + bump: minor diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 44e3463dc9f2..177c5da74d4f 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -3658,6 +3658,7 @@ impl Pallet { /// * each `member.pool_id` must correspond to an existing `BondedPool.id` (which implies the /// existence of the reward pool as well). /// * count of all members must be less than `MaxPoolMembers`. + /// * each `BondedPool.points` must never be lower than the pool's balance. /// /// Then, considering unbonding members: /// @@ -3786,6 +3787,11 @@ impl Pallet { pool is being destroyed and the depositor is the last member", ); + ensure!( + bonded_pool.points >= bonded_pool.points_to_balance(bonded_pool.points), + "Each `BondedPool.points` must never be lower than the pool's balance" + ); + expected_tvl += T::StakeAdapter::total_stake(Pool::from(bonded_pool.bonded_account())); Ok(()) From f7504cec1689850f2c93176fe81667d650217e1c Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:05:08 +0200 Subject: [PATCH 191/382] [ci] Move check-runtime-migration to GHA (#5519) PR moves rococo and wococo check-runtime-migration jobs to GHA cc https://github.com/paritytech/ci_cd/issues/1006 --- .github/workflows/check-runtime-migration.yml | 57 +++++++++++++------ .gitlab-ci.yml | 10 ---- .gitlab/pipeline/check.yml | 49 ---------------- 3 files changed, 39 insertions(+), 77 deletions(-) diff --git a/.github/workflows/check-runtime-migration.yml b/.github/workflows/check-runtime-migration.yml index b8962f0e07ad..b93a07bf61f1 100644 --- a/.github/workflows/check-runtime-migration.yml +++ b/.github/workflows/check-runtime-migration.yml @@ -8,7 +8,7 @@ on: types: [opened, synchronize, reopened, ready_for_review] # Take a snapshot at 5am when most SDK devs are not working. schedule: - - cron: '0 5 * * *' + - cron: "0 5 * * *" merge_group: workflow_dispatch: @@ -24,14 +24,25 @@ jobs: runs-on: ubuntu-latest outputs: IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} steps: - name: Checkout uses: actions/checkout@v4 - id: set_image run: cat .github/env >> $GITHUB_OUTPUT - # rococo and westend are disabled for now (no access to parity-chains.parity.io) + # By default we use spot machines that can be terminated at any time. + # Merge queues use persistent runners to avoid kicking off from queue when the runner is terminated. + - id: set_runner + run: | + # Run merge queues on persistent runners + if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT + else + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + fi + # More info can be found here: https://github.com/paritytech/polkadot/pull/5865 check-runtime-migration: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.set-image.outputs.RUNNER }} # We need to set this to rather long to allow the snapshot to be created, but the average time # should be much lower. timeout-minutes: 60 @@ -41,9 +52,10 @@ jobs: strategy: fail-fast: false matrix: - network: [ - # westend, - # rococo, + network: + [ + westend, + rococo, asset-hub-westend, asset-hub-rococo, bridge-hub-westend, @@ -53,18 +65,18 @@ jobs: coretime-rococo, ] include: - # - network: westend - # package: westend-runtime - # wasm: westend_runtime.compact.compressed.wasm - # uri: "wss://try-runtime-westend.polkadot.io:443" - # subcommand_extra_args: "--no-weight-warnings" - # command_extra_args: "" - # - network: rococo - # package: rococo-runtime - # wasm: rococo_runtime.compact.compressed.wasm - # uri: "wss://try-runtime-rococo.polkadot.io:443" - # subcommand_extra_args: "--no-weight-warnings" - # command_extra_args: "" + - network: westend + package: westend-runtime + wasm: westend_runtime.compact.compressed.wasm + uri: "wss://try-runtime-westend.polkadot.io:443" + subcommand_extra_args: "--no-weight-warnings" + command_extra_args: "" + - network: rococo + package: rococo-runtime + wasm: rococo_runtime.compact.compressed.wasm + uri: "wss://try-runtime-rococo.polkadot.io:443" + subcommand_extra_args: "--no-weight-warnings" + command_extra_args: "" - network: asset-hub-westend package: asset-hub-westend-runtime wasm: asset_hub_westend_runtime.compact.compressed.wasm @@ -143,3 +155,12 @@ jobs: --runtime ./target/release/wbuild/${{ matrix.package }}/${{ matrix.wasm }} \ on-runtime-upgrade --disable-spec-version-check --checks=all ${{ matrix.subcommand_extra_args }} snap -p snapshot.raw sleep 5 + # name of this job must be unique across all workflows + # otherwise GitHub will mark all these jobs as required + confirm-required-checks-passed: + runs-on: ubuntu-latest + name: All runtime migrations passed + # If any new job gets added, be sure to add it to this array + needs: [check-runtime-migration] + steps: + - run: echo '### Good job! All the checks passed 🚀' >> $GITHUB_STEP_SUMMARY diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c51d0ce5627e..56d426218c80 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -374,13 +374,3 @@ cancel-pipeline-build-short-benchmark: extends: .cancel-pipeline-template needs: - job: build-short-benchmark - -cancel-pipeline-check-runtime-migration-rococo: - extends: .cancel-pipeline-template - needs: - - job: check-runtime-migration-rococo - -cancel-pipeline-check-runtime-migration-westend: - extends: .cancel-pipeline-template - needs: - - job: check-runtime-migration-westend diff --git a/.gitlab/pipeline/check.yml b/.gitlab/pipeline/check.yml index 64bef8d6e005..2212c1aeb0a8 100644 --- a/.gitlab/pipeline/check.yml +++ b/.gitlab/pipeline/check.yml @@ -46,52 +46,3 @@ check-toml-format: script: - taplo format --check --config .config/taplo.toml - echo "Please run `taplo format --config .config/taplo.toml` to fix any toml formatting issues" - -# More info can be found here: https://github.com/paritytech/polkadot/pull/5865 -.check-runtime-migration: - stage: check - extends: - - .docker-env - - .test-pr-refs - script: - - export RUST_LOG=remote-ext=debug,runtime=debug - - echo "---------- Downloading try-runtime CLI ----------" - - curl -sL https://github.com/paritytech/try-runtime-cli/releases/download/v0.5.4/try-runtime-x86_64-unknown-linux-musl -o try-runtime - - chmod +x ./try-runtime - - echo "Using try-runtime-cli version:" - - ./try-runtime --version - - echo "---------- Building ${PACKAGE} runtime ----------" - - time cargo build --release --locked -p "$PACKAGE" --features try-runtime - - echo "---------- Executing on-runtime-upgrade for ${NETWORK} ----------" - - > - time ./try-runtime ${COMMAND_EXTRA_ARGS} \ - --runtime ./target/release/wbuild/"$PACKAGE"/"$WASM" \ - on-runtime-upgrade --disable-spec-version-check --checks=all ${SUBCOMMAND_EXTRA_ARGS} live --uri ${URI} - - sleep 5 - -# Check runtime migrations for Parity managed relay chains -check-runtime-migration-westend: - stage: check - extends: - - .docker-env - - .test-pr-refs - - .check-runtime-migration - variables: - NETWORK: "westend" - PACKAGE: "westend-runtime" - WASM: "westend_runtime.compact.compressed.wasm" - URI: "wss://try-runtime-westend.polkadot.io:443" - SUBCOMMAND_EXTRA_ARGS: "--no-weight-warnings" - -check-runtime-migration-rococo: - stage: check - extends: - - .docker-env - - .test-pr-refs - - .check-runtime-migration - variables: - NETWORK: "rococo" - PACKAGE: "rococo-runtime" - WASM: "rococo_runtime.compact.compressed.wasm" - URI: "wss://try-runtime-rococo.polkadot.io:443" - SUBCOMMAND_EXTRA_ARGS: "--no-weight-warnings" From c32160e32e835db7c7a64a097ffd38fbbb65f1c5 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Fri, 30 Aug 2024 12:26:31 +0300 Subject: [PATCH 192/382] Add support for memory-profiling on subsystem-bench (#5522) Add support in subsystem-benchmarks to profile memory usage using the jemalloc builting profiler, this allows us to run each benchmark with profiling enabled and determine if the memory usage patters are in conformance with our expectations. --------- Signed-off-by: Alexandru Gheorghe --- Cargo.lock | 46 +++++++++++++++++++ Cargo.toml | 1 + polkadot/node/subsystem-bench/Cargo.toml | 8 ++++ polkadot/node/subsystem-bench/README.md | 35 ++++++++++++++ .../src/cli/subsystem-bench.rs | 11 +++++ 5 files changed, 101 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index bb0f01542d31..8eb4fa5b4e83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7350,6 +7350,23 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jemalloc_pprof" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96368c0fc161a0a1a20b3952b6fd31ee342fffc87ed9e48ac1ed49fb25686655" +dependencies = [ + "anyhow", + "libc", + "mappings", + "once_cell", + "pprof_util", + "tempfile", + "tikv-jemalloc-ctl", + "tokio", + "tracing", +] + [[package]] name = "jni" version = "0.19.0" @@ -8550,6 +8567,19 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +[[package]] +name = "mappings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fa2605f461115ef6336342b12f0d8cabdfd7b258fed86f5f98c725535843601" +dependencies = [ + "anyhow", + "libc", + "once_cell", + "pprof_util", + "tracing", +] + [[package]] name = "match_cfg" version = "0.1.0" @@ -14927,6 +14957,7 @@ dependencies = [ "futures-timer", "hex", "itertools 0.11.0", + "jemalloc_pprof", "kvdb-memorydb", "log", "orchestra", @@ -14950,6 +14981,7 @@ dependencies = [ "polkadot-overseer", "polkadot-primitives", "polkadot-primitives-test-helpers", + "polkadot-service", "polkadot-statement-distribution", "prometheus", "pyroscope", @@ -14978,6 +15010,7 @@ dependencies = [ "sp-tracing 16.0.0", "strum 0.26.2", "substrate-prometheus-endpoint", + "tikv-jemallocator", "tokio", "tracing-gum", ] @@ -15426,6 +15459,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "pprof_util" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c620a1858d6ebf10d7c60256629078b2d106968d0e6ff63b850d9ecd84008fbe" +dependencies = [ + "anyhow", + "flate2", + "num", + "paste", + "prost 0.11.9", +] + [[package]] name = "ppv-lite86" version = "0.2.17" diff --git a/Cargo.toml b/Cargo.toml index f26a894960a7..7cd18bc8a590 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -810,6 +810,7 @@ is-terminal = { version = "0.4.9" } is_executable = { version = "1.0.1" } isahc = { version = "1.2" } itertools = { version = "0.11" } +jemalloc_pprof = { version = "0.4" } jobserver = { version = "0.1.26" } jsonpath_lib = { version = "0.3" } jsonrpsee = { version = "0.24.3" } diff --git a/polkadot/node/subsystem-bench/Cargo.toml b/polkadot/node/subsystem-bench/Cargo.toml index 9384f8142a97..ae798cf2640a 100644 --- a/polkadot/node/subsystem-bench/Cargo.toml +++ b/polkadot/node/subsystem-bench/Cargo.toml @@ -19,7 +19,11 @@ path = "src/cli/subsystem-bench.rs" # Prevent rustdoc error. Already documented from top-level Cargo.toml. doc = false + [dependencies] +tikv-jemallocator = { features = ["profiling", "unprefixed_malloc_on_supported_platforms"], workspace = true, optional = true } +jemalloc_pprof = { workspace = true, optional = true } +polkadot-service = { workspace = true, default-features = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } polkadot-node-subsystem-types = { workspace = true, default-features = true } @@ -93,3 +97,7 @@ strum = { features = ["derive"], workspace = true, default-features = true } [features] default = [] +memprofile = [ + "dep:jemalloc_pprof", + "dep:tikv-jemallocator", +] diff --git a/polkadot/node/subsystem-bench/README.md b/polkadot/node/subsystem-bench/README.md index 228fba41c46c..8d20f1f49c0e 100644 --- a/polkadot/node/subsystem-bench/README.md +++ b/polkadot/node/subsystem-bench/README.md @@ -260,6 +260,41 @@ This file is best interpreted with `cg_annotate --auto=yes cachegrind.out.` For finer profiling of cache misses, better use `perf` on a bare-metal machine. +### Profile memory usage using jemalloc + +Bellow you can find instructions how to setup and run profiling with jemalloc, this is complementary +with using other memory profiling tools like: . + +#### Prerequisites + +Install tooling with: + +``` +sudo apt install libjemalloc-dev graphviz +``` + +#### Generate memory usage snapshots + +Memory usage can be profiled by running any subsystem benchmark with `--features memprofile`, e.g: + +``` +RUSTFLAGS=-g cargo run -p polkadot-subsystem-bench --release --features memprofile -- polkadot/node/subsystem-bench/examples/approvals_throughput.yaml +``` + +#### Interpret the results + +After the benchmark ran the memory usage snapshots can be found in `/tmp/subsystem-bench*`, to extract the information +from a snapshot you can use `jeprof` like this: + +``` +jeprof --text PATH_TO_EXECUTABLE_WITH_DEBUG_SYMBOLS /tmp/subsystem-bench.1222895.199.i199.heap > statistics.txt +``` + +Useful links: + +- Tutorial: +- Jemalloc configuration options: + ## Create new test objectives This tool is intended to make it easy to write new test objectives that focus individual subsystems, diff --git a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs index 153efdda4056..0f68b905b4ca 100644 --- a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -182,6 +182,17 @@ impl BenchCli { } } +#[cfg(feature = "memprofile")] +#[global_allocator] +static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; + +#[cfg(feature = "memprofile")] +#[allow(non_upper_case_globals)] +#[export_name = "malloc_conf"] +// See https://jemalloc.net/jemalloc.3.html for more information on the configuration options. +pub static malloc_conf: &[u8] = + b"prof:true,prof_active:true,lg_prof_interval:30,lg_prof_sample:21,prof_prefix:/tmp/subsystem-bench\0"; + fn main() -> eyre::Result<()> { color_eyre::install()?; sp_tracing::try_init_simple(); From 18e950407985665a0c260618916ef4517d34bbac Mon Sep 17 00:00:00 2001 From: Javier Bullrich Date: Fri, 30 Aug 2024 11:37:42 +0200 Subject: [PATCH 193/382] Added `mac-setup` script (#5528) This is used for the steps that use the `macos` runner. It installs the Rust version that we are using in the [`forklift` image](https://github.com/paritytech/polkadot-sdk/blob/master/.github/env). To be used in #5386. --- .github/actions/set-up-mac/README.md | 15 ++++++++++ .github/actions/set-up-mac/action.yml | 43 +++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/actions/set-up-mac/README.md create mode 100644 .github/actions/set-up-mac/action.yml diff --git a/.github/actions/set-up-mac/README.md b/.github/actions/set-up-mac/README.md new file mode 100644 index 000000000000..0bbc7112bd1d --- /dev/null +++ b/.github/actions/set-up-mac/README.md @@ -0,0 +1,15 @@ +# How to use + +```yml + set-image: + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT + - name: Install dependencies + uses: ./.github/actions/set-up-mac + with: + IMAGE: ${{ steps.set-image.outputs.IMAGE }} +``` diff --git a/.github/actions/set-up-mac/action.yml b/.github/actions/set-up-mac/action.yml new file mode 100644 index 000000000000..a3b026679402 --- /dev/null +++ b/.github/actions/set-up-mac/action.yml @@ -0,0 +1,43 @@ +name: "Set up rust on mac" +description: "Install the required tools for Mac runners" +inputs: + IMAGE: + description: "Rust docker image" + required: true +runs: + using: "composite" + steps: + - name: Install with Hombrew + shell: bash + run: brew install protobuf rustup openssl pkg-config zlib xz zstd llvm jq curl gcc make cmake + - name: Set version + shell: bash + run: | + VERSION=$(echo $IMAGE | sed -E 's/.*:bullseye-([^-]+)-.*/\1/') + echo $VERSION + echo "VERSION=$VERSION" >> $GITHUB_ENV + NIGHTLY=$(echo $IMAGE | sed -E 's/.*([0-9]{4}-[0-9]{2}-[0-9]{2}).*/\1/') + echo $NIGHTLY + echo "NIGHTLY=$NIGHTLY" >> $GITHUB_ENV + env: + IMAGE: ${{ inputs.IMAGE }} + + - name: Install rustup + shell: bash + run: | + rustup-init -y + rustup install $VERSION + rustup default $VERSION + rustup toolchain install "nightly-${NIGHTLY}" + + - name: MacOS Deps + shell: bash + run: | + rustup target add wasm32-unknown-unknown --toolchain $VERSION + rustup component add rust-src rustfmt clippy --toolchain $VERSION + + - name: Check Rust + shell: bash + run: | + rustup show + rustup +nightly show From 3d4a3355f0d31aeab7b79c23d6b269b5ea669f75 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Fri, 30 Aug 2024 17:48:05 +0800 Subject: [PATCH 194/382] Simplify `SyncingEngine::new()` a bit (#5396) Tiny changes to simplify the code: - Remove an unnecessary `collect`. - Reduce the code duplication a little bit. --------- Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> --- prdoc/pr_5396.prdoc | 13 ++++ substrate/client/network/sync/src/engine.rs | 67 +++++++++------------ 2 files changed, 40 insertions(+), 40 deletions(-) create mode 100644 prdoc/pr_5396.prdoc diff --git a/prdoc/pr_5396.prdoc b/prdoc/pr_5396.prdoc new file mode 100644 index 000000000000..d78e9ac46932 --- /dev/null +++ b/prdoc/pr_5396.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Simplify `SyncingEngine::new()` + +doc: + - audience: Node Dev + description: | + Tiny changes to simplify the internal implemenation of API `SyncingEngine::new()` to prevent panics while fetching the genesis hash and to eliminate unnecessary allocation for reserved peers. + +crates: +- name: sc-network-sync + bump: none diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index 4a57d61df457..4b6ccb085834 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -318,8 +318,7 @@ where if net_config.network_config.max_blocks_per_request > MAX_BLOCKS_IN_RESPONSE as u32 { log::info!( target: LOG_TARGET, - "clamping maximum blocks per request to {}", - MAX_BLOCKS_IN_RESPONSE, + "clamping maximum blocks per request to {MAX_BLOCKS_IN_RESPONSE}", ); MAX_BLOCKS_IN_RESPONSE as u32 } else { @@ -340,12 +339,7 @@ where imp_p.insert(reserved.peer_id); } for config in net_config.notification_protocols() { - let peer_ids = config - .set_config() - .reserved_nodes - .iter() - .map(|info| info.peer_id) - .collect::>(); + let peer_ids = config.set_config().reserved_nodes.iter().map(|info| info.peer_id); imp_p.extend(peer_ids); } @@ -379,18 +373,16 @@ where total.saturating_sub(net_config.network_config.default_peers_set_num_full) as usize }; + let info = client.info(); + let (block_announce_config, notification_service) = Self::get_block_announce_proto_config::( protocol_id, fork_id, roles, - client.info().best_number, - client.info().best_hash, - client - .block_hash(Zero::zero()) - .ok() - .flatten() - .expect("Genesis block exists; qed"), + info.best_number, + info.best_hash, + info.genesis_hash, &net_config.network_config.default_peers_set, network_metrics, Arc::clone(&peer_store_handle), @@ -403,11 +395,6 @@ where let (tx, service_rx) = tracing_unbounded("mpsc_chain_sync", 100_000); let num_connected = Arc::new(AtomicUsize::new(0)); let is_major_syncing = Arc::new(AtomicBool::new(false)); - let genesis_hash = client - .block_hash(0u32.into()) - .ok() - .flatten() - .expect("Genesis block exists; qed"); // `default_peers_set.in_peers` contains an unspecified amount of light peers so the number // of full inbound peers must be calculated from the total full peer count @@ -436,7 +423,7 @@ where num_connected: num_connected.clone(), is_major_syncing: is_major_syncing.clone(), service_rx, - genesis_hash, + genesis_hash: info.genesis_hash, important_peers, default_peers_set_no_slot_connected_peers: HashSet::new(), boot_node_ids, @@ -534,7 +521,7 @@ where "Received block announce from disconnected peer {peer_id}", ); debug_assert!(false); - return + return; }, }; peer.known_blocks.insert(hash); @@ -559,17 +546,17 @@ where Ok(Some(header)) => header, Ok(None) => { log::warn!(target: LOG_TARGET, "Trying to announce unknown block: {hash}"); - return + return; }, Err(e) => { log::warn!(target: LOG_TARGET, "Error reading block header {hash}: {e}"); - return + return; }, }; // don't announce genesis block since it will be ignored if header.number().is_zero() { - return + return; } let is_best = self.client.info().best_hash == hash; @@ -623,7 +610,7 @@ where target: LOG_TARGET, "Terminating `SyncingEngine` due to fatal error: {e:?}.", ); - return + return; } } } @@ -825,12 +812,12 @@ where target: LOG_TARGET, "received notification from {peer} who had been earlier refused by `SyncingEngine`", ); - return + return; } let Ok(announce) = BlockAnnounce::decode(&mut notification.as_ref()) else { log::warn!(target: LOG_TARGET, "failed to decode block announce"); - return + return; }; self.push_block_announce_validation(peer, announce); @@ -844,7 +831,7 @@ where fn on_sync_peer_disconnected(&mut self, peer_id: PeerId) { let Some(info) = self.peers.remove(&peer_id) else { log::debug!(target: LOG_TARGET, "{peer_id} does not exist in `SyncingEngine`"); - return + return; }; if let Some(metrics) = &self.metrics { metrics.peers.dec(); @@ -918,7 +905,7 @@ where ); } - return Err(true) + return Err(true); } Ok(handshake) @@ -953,7 +940,7 @@ where "Called `validate_connection()` with already connected peer {peer_id}", ); debug_assert!(false); - return Err(false) + return Err(false); } let no_slot_peer = self.default_peers_set_no_slot_peers.contains(&peer_id); @@ -966,7 +953,7 @@ where this_peer_reserved_slot { log::debug!(target: LOG_TARGET, "Too many full nodes, rejecting {peer_id}"); - return Err(false) + return Err(false); } // make sure to accept no more than `--in-peers` many full nodes @@ -976,7 +963,7 @@ where self.num_in_peers == self.max_in_peers { log::debug!(target: LOG_TARGET, "All inbound slots have been consumed, rejecting {peer_id}"); - return Err(false) + return Err(false); } // make sure that all slots are not occupied by light peers @@ -987,7 +974,7 @@ where (self.peers.len() - self.strategy.num_peers()) >= self.default_peers_set_num_light { log::debug!(target: LOG_TARGET, "Too many light nodes, rejecting {peer_id}"); - return Err(false) + return Err(false); } Ok(handshake) @@ -1049,7 +1036,7 @@ where if !self.peers.contains_key(&peer_id) { trace!(target: LOG_TARGET, "Cannot send block request to unknown peer {peer_id}"); debug_assert!(false); - return + return; } let downloader = self.block_downloader.clone(); @@ -1071,7 +1058,7 @@ where if !self.peers.contains_key(&peer_id) { trace!(target: LOG_TARGET, "Cannot send state request to unknown peer {peer_id}"); debug_assert!(false); - return + return; } let (tx, rx) = oneshot::channel(); @@ -1106,7 +1093,7 @@ where if !self.peers.contains_key(&peer_id) { trace!(target: LOG_TARGET, "Cannot send warp proof request to unknown peer {peer_id}"); debug_assert!(false); - return + return; } let (tx, rx) = oneshot::channel(); @@ -1169,7 +1156,7 @@ where peer_id, self.block_announce_protocol_name.clone(), ); - return + return; }, Err(BlockResponseError::ExtractionFailed(e)) => { debug!( @@ -1179,7 +1166,7 @@ where e ); self.network_service.report_peer(peer_id, rep::BAD_MESSAGE); - return + return; }, } }, @@ -1196,7 +1183,7 @@ where peer_id, self.block_announce_protocol_name.clone(), ); - return + return; }, }; From 09035a7d5d14fc3f2df3db304cd0fcc8fc9ed27b Mon Sep 17 00:00:00 2001 From: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:39:48 +0300 Subject: [PATCH 195/382] Polkadot Primitives v8 (#5525) As Runtime release 1.3.0 includes all of the remaining staging primitives and APIs we can now release primitives version 8. No other changes other than renaming/moving done here. --------- Signed-off-by: Andrei Sandu --- polkadot/node/collation-generation/src/lib.rs | 2 +- polkadot/node/core/backing/src/lib.rs | 6 +- .../core/prospective-parachains/src/lib.rs | 3 +- .../src/collator_side/mod.rs | 5 +- .../src/validator_side/mod.rs | 3 +- .../statement-distribution/src/v2/mod.rs | 7 +- .../src/v2/statement_store.rs | 2 +- polkadot/node/service/src/chain_spec.rs | 2 +- polkadot/node/subsystem-util/src/lib.rs | 2 +- .../node/subsystem-util/src/runtime/mod.rs | 108 +++++++++++++++++- polkadot/node/subsystem-util/src/vstaging.rs | 105 ----------------- polkadot/node/test/service/src/chain_spec.rs | 2 +- polkadot/primitives/src/lib.rs | 27 ++--- .../src/{v7 => v8}/async_backing.rs | 0 .../src/{v7 => v8}/executor_params.rs | 0 polkadot/primitives/src/{v7 => v8}/metrics.rs | 0 polkadot/primitives/src/{v7 => v8}/mod.rs | 80 ++++++++++++- polkadot/primitives/src/{v7 => v8}/signed.rs | 0 .../primitives/src/{v7 => v8}/slashing.rs | 0 polkadot/primitives/src/vstaging/mod.rs | 81 ------------- .../runtime/parachains/src/configuration.rs | 2 +- .../src/configuration/migration/v12.rs | 2 +- .../runtime/parachains/src/paras/tests.rs | 2 +- .../src/paras_inherent/benchmarking.rs | 2 +- .../parachains/src/paras_inherent/tests.rs | 4 +- .../runtime/parachains/src/scheduler/tests.rs | 2 +- .../parachains/src/session_info/tests.rs | 2 +- .../rococo/src/genesis_config_presets.rs | 4 +- 28 files changed, 221 insertions(+), 234 deletions(-) rename polkadot/primitives/src/{v7 => v8}/async_backing.rs (100%) rename polkadot/primitives/src/{v7 => v8}/executor_params.rs (100%) rename polkadot/primitives/src/{v7 => v8}/metrics.rs (100%) rename polkadot/primitives/src/{v7 => v8}/mod.rs (96%) rename polkadot/primitives/src/{v7 => v8}/signed.rs (100%) rename polkadot/primitives/src/{v7 => v8}/slashing.rs (100%) diff --git a/polkadot/node/collation-generation/src/lib.rs b/polkadot/node/collation-generation/src/lib.rs index d38516a4ff71..50adbddea413 100644 --- a/polkadot/node/collation-generation/src/lib.rs +++ b/polkadot/node/collation-generation/src/lib.rs @@ -45,7 +45,7 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_util::{ request_async_backing_params, request_availability_cores, request_para_backing_state, request_persisted_validation_data, request_validation_code, request_validation_code_hash, - request_validators, vstaging::fetch_claim_queue, + request_validators, runtime::fetch_claim_queue, }; use polkadot_primitives::{ collator_signature_payload, CandidateCommitments, CandidateDescriptor, CandidateReceipt, diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs index 5bcd47a2434c..f276321c87ed 100644 --- a/polkadot/node/core/backing/src/lib.rs +++ b/polkadot/node/core/backing/src/lib.rs @@ -100,9 +100,9 @@ use polkadot_node_subsystem_util::{ executor_params_at_relay_parent, request_from_runtime, request_session_index_for_child, request_validator_groups, request_validators, runtime::{ - self, prospective_parachains_mode, request_min_backing_votes, ProspectiveParachainsMode, + self, fetch_claim_queue, prospective_parachains_mode, request_min_backing_votes, + ClaimQueueSnapshot, ProspectiveParachainsMode, }, - vstaging::{fetch_claim_queue, ClaimQueueSnapshot}, Validator, }; use polkadot_primitives::{ @@ -121,7 +121,7 @@ use polkadot_statement_table::{ Config as TableConfig, Context as TableContextTrait, Table, }; use sp_keystore::KeystorePtr; -use util::{runtime::request_node_features, vstaging::get_disabled_validators_with_fallback}; +use util::runtime::{get_disabled_validators_with_fallback, request_node_features}; mod error; diff --git a/polkadot/node/core/prospective-parachains/src/lib.rs b/polkadot/node/core/prospective-parachains/src/lib.rs index ecb1f3a476ec..b8b5f159e71c 100644 --- a/polkadot/node/core/prospective-parachains/src/lib.rs +++ b/polkadot/node/core/prospective-parachains/src/lib.rs @@ -46,8 +46,7 @@ use polkadot_node_subsystem_util::{ backing_implicit_view::{BlockInfoProspectiveParachains as BlockInfo, View as ImplicitView}, inclusion_emulator::{Constraints, RelayChainBlockInfo}, request_session_index_for_child, - runtime::{prospective_parachains_mode, ProspectiveParachainsMode}, - vstaging::fetch_claim_queue, + runtime::{fetch_claim_queue, prospective_parachains_mode, ProspectiveParachainsMode}, }; use polkadot_primitives::{ async_backing::CandidatePendingAvailability, BlockNumber, CandidateHash, diff --git a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs index 5c201542eb56..401525d02f11 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs @@ -48,10 +48,9 @@ use polkadot_node_subsystem_util::{ backing_implicit_view::View as ImplicitView, reputation::{ReputationAggregator, REPUTATION_CHANGE_INTERVAL}, runtime::{ - get_availability_cores, get_group_rotation_info, prospective_parachains_mode, - ProspectiveParachainsMode, RuntimeInfo, + fetch_claim_queue, get_availability_cores, get_group_rotation_info, + prospective_parachains_mode, ProspectiveParachainsMode, RuntimeInfo, }, - vstaging::fetch_claim_queue, TimeoutExt, }; use polkadot_primitives::{ diff --git a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs index f5c9726f3f6a..cbf00a9e119d 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs @@ -50,8 +50,7 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_util::{ backing_implicit_view::View as ImplicitView, reputation::{ReputationAggregator, REPUTATION_CHANGE_INTERVAL}, - runtime::{prospective_parachains_mode, ProspectiveParachainsMode}, - vstaging::fetch_claim_queue, + runtime::{fetch_claim_queue, prospective_parachains_mode, ProspectiveParachainsMode}, }; use polkadot_primitives::{ CandidateHash, CollatorId, CoreState, Hash, HeadData, Id as ParaId, OccupiedCoreAssumption, diff --git a/polkadot/node/network/statement-distribution/src/v2/mod.rs b/polkadot/node/network/statement-distribution/src/v2/mod.rs index 109c29f520c5..f9c2d0ddbae8 100644 --- a/polkadot/node/network/statement-distribution/src/v2/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/mod.rs @@ -45,8 +45,9 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_util::{ backing_implicit_view::View as ImplicitView, reputation::ReputationAggregator, - runtime::{request_min_backing_votes, ProspectiveParachainsMode}, - vstaging::{fetch_claim_queue, ClaimQueueSnapshot}, + runtime::{ + fetch_claim_queue, request_min_backing_votes, ClaimQueueSnapshot, ProspectiveParachainsMode, + }, }; use polkadot_primitives::{ AuthorityDiscoveryId, CandidateHash, CompactStatement, CoreIndex, CoreState, GroupIndex, @@ -570,7 +571,7 @@ pub(crate) async fn handle_active_leaves_update( for new_relay_parent in new_relay_parents.iter().cloned() { let disabled_validators: HashSet<_> = - polkadot_node_subsystem_util::vstaging::get_disabled_validators_with_fallback( + polkadot_node_subsystem_util::runtime::get_disabled_validators_with_fallback( ctx.sender(), new_relay_parent, ) diff --git a/polkadot/node/network/statement-distribution/src/v2/statement_store.rs b/polkadot/node/network/statement-distribution/src/v2/statement_store.rs index a3b2636d2ffc..56a54f6316c0 100644 --- a/polkadot/node/network/statement-distribution/src/v2/statement_store.rs +++ b/polkadot/node/network/statement-distribution/src/v2/statement_store.rs @@ -292,7 +292,7 @@ impl GroupStatements { mod tests { use super::*; - use polkadot_primitives::v7::{Hash, SigningContext, ValidatorPair}; + use polkadot_primitives::{Hash, SigningContext, ValidatorPair}; use sp_application_crypto::Pair as PairT; #[test] diff --git a/polkadot/node/service/src/chain_spec.rs b/polkadot/node/service/src/chain_spec.rs index f930476a5541..d377a75f1069 100644 --- a/polkadot/node/service/src/chain_spec.rs +++ b/polkadot/node/service/src/chain_spec.rs @@ -25,7 +25,7 @@ use sp_consensus_babe::AuthorityId as BabeId; use sp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId; #[cfg(feature = "westend-native")] -use polkadot_primitives::vstaging::SchedulerParams; +use polkadot_primitives::SchedulerParams; #[cfg(feature = "rococo-native")] use rococo_runtime as rococo; use sc_chain_spec::ChainSpecExtension; diff --git a/polkadot/node/subsystem-util/src/lib.rs b/polkadot/node/subsystem-util/src/lib.rs index 92f2cd189054..4bab4e80fe50 100644 --- a/polkadot/node/subsystem-util/src/lib.rs +++ b/polkadot/node/subsystem-util/src/lib.rs @@ -49,6 +49,7 @@ use polkadot_primitives::{ ValidatorSignature, }; pub use rand; +use runtime::get_disabled_validators_with_fallback; use sp_application_crypto::AppCrypto; use sp_core::ByteArray; use sp_keystore::{Error as KeystoreError, KeystorePtr}; @@ -57,7 +58,6 @@ use std::{ time::Duration, }; use thiserror::Error; -use vstaging::get_disabled_validators_with_fallback; pub use determine_new_blocks::determine_new_blocks; pub use metered; diff --git a/polkadot/node/subsystem-util/src/runtime/mod.rs b/polkadot/node/subsystem-util/src/runtime/mod.rs index 2c9ec8db3778..2f9d3ed7b4f4 100644 --- a/polkadot/node/subsystem-util/src/runtime/mod.rs +++ b/polkadot/node/subsystem-util/src/runtime/mod.rs @@ -32,17 +32,20 @@ use polkadot_node_subsystem_types::UnpinHandle; use polkadot_primitives::{ node_features::FeatureIndex, slashing, AsyncBackingParams, CandidateEvent, CandidateHash, CoreIndex, CoreState, EncodeAs, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, - IndexedVec, NodeFeatures, OccupiedCore, ScrapedOnChainVotes, SessionIndex, SessionInfo, Signed, - SigningContext, UncheckedSigned, ValidationCode, ValidationCodeHash, ValidatorId, - ValidatorIndex, LEGACY_MIN_BACKING_VOTES, + Id as ParaId, IndexedVec, NodeFeatures, OccupiedCore, ScrapedOnChainVotes, SessionIndex, + SessionInfo, Signed, SigningContext, UncheckedSigned, ValidationCode, ValidationCodeHash, + ValidatorId, ValidatorIndex, LEGACY_MIN_BACKING_VOTES, }; +use std::collections::{BTreeMap, VecDeque}; + use crate::{ - request_async_backing_params, request_availability_cores, request_candidate_events, + has_required_runtime, request_async_backing_params, request_availability_cores, + request_candidate_events, request_claim_queue, request_disabled_validators, request_from_runtime, request_key_ownership_proof, request_on_chain_votes, request_session_executor_params, request_session_index_for_child, request_session_info, request_submit_report_dispute_lost, request_unapplied_slashes, request_validation_code_by_hash, - request_validator_groups, vstaging::get_disabled_validators_with_fallback, + request_validator_groups, }; /// Errors that can happen on runtime fetches. @@ -579,3 +582,98 @@ pub async fn request_node_features( res.map(Some) } } + +/// A snapshot of the runtime claim queue at an arbitrary relay chain block. +#[derive(Default)] +pub struct ClaimQueueSnapshot(pub BTreeMap>); + +impl From>> for ClaimQueueSnapshot { + fn from(claim_queue_snapshot: BTreeMap>) -> Self { + ClaimQueueSnapshot(claim_queue_snapshot) + } +} + +impl ClaimQueueSnapshot { + /// Returns the `ParaId` that has a claim for `core_index` at the specified `depth` in the + /// claim queue. A depth of `0` means the very next block. + pub fn get_claim_for(&self, core_index: CoreIndex, depth: usize) -> Option { + self.0.get(&core_index)?.get(depth).copied() + } + + /// Returns an iterator over all claimed cores and the claiming `ParaId` at the specified + /// `depth` in the claim queue. + pub fn iter_claims_at_depth( + &self, + depth: usize, + ) -> impl Iterator + '_ { + self.0 + .iter() + .filter_map(move |(core_index, paras)| Some((*core_index, *paras.get(depth)?))) + } + + /// Returns an iterator over all claims on the given core. + pub fn iter_claims_for_core( + &self, + core_index: &CoreIndex, + ) -> impl Iterator + '_ { + self.0.get(core_index).map(|c| c.iter()).into_iter().flatten() + } + + /// Returns an iterator over the whole claim queue. + pub fn iter_all_claims(&self) -> impl Iterator)> + '_ { + self.0.iter() + } +} + +// TODO: https://github.com/paritytech/polkadot-sdk/issues/1940 +/// Returns disabled validators list if the runtime supports it. Otherwise logs a debug messages and +/// returns an empty vec. +/// Once runtime ver `DISABLED_VALIDATORS_RUNTIME_REQUIREMENT` is released remove this function and +/// replace all usages with `request_disabled_validators` +pub async fn get_disabled_validators_with_fallback>( + sender: &mut Sender, + relay_parent: Hash, +) -> Result> { + let disabled_validators = if has_required_runtime( + sender, + relay_parent, + RuntimeApiRequest::DISABLED_VALIDATORS_RUNTIME_REQUIREMENT, + ) + .await + { + request_disabled_validators(relay_parent, sender) + .await + .await + .map_err(Error::RuntimeRequestCanceled)?? + } else { + gum::debug!(target: LOG_TARGET, "Runtime doesn't support `DisabledValidators` - continuing with an empty disabled validators set"); + vec![] + }; + + Ok(disabled_validators) +} + +/// Checks if the runtime supports `request_claim_queue` and attempts to fetch the claim queue. +/// Returns `ClaimQueueSnapshot` or `None` if claim queue API is not supported by runtime. +/// Any specific `RuntimeApiError` is bubbled up to the caller. +pub async fn fetch_claim_queue( + sender: &mut impl SubsystemSender, + relay_parent: Hash, +) -> Result> { + if has_required_runtime( + sender, + relay_parent, + RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT, + ) + .await + { + let res = request_claim_queue(relay_parent, sender) + .await + .await + .map_err(Error::RuntimeRequestCanceled)??; + Ok(Some(res.into())) + } else { + gum::trace!(target: LOG_TARGET, "Runtime doesn't support `request_claim_queue`"); + Ok(None) + } +} diff --git a/polkadot/node/subsystem-util/src/vstaging.rs b/polkadot/node/subsystem-util/src/vstaging.rs index b6cd73f412b3..b608bb416ff6 100644 --- a/polkadot/node/subsystem-util/src/vstaging.rs +++ b/polkadot/node/subsystem-util/src/vstaging.rs @@ -18,108 +18,3 @@ //! //! This module is intended to contain common boiler plate code handling unreleased runtime API //! calls. - -use std::collections::{BTreeMap, VecDeque}; - -use polkadot_node_subsystem_types::messages::{RuntimeApiMessage, RuntimeApiRequest}; -use polkadot_overseer::SubsystemSender; -use polkadot_primitives::{CoreIndex, Hash, Id as ParaId, ValidatorIndex}; - -use crate::{has_required_runtime, request_claim_queue, request_disabled_validators, runtime}; - -const LOG_TARGET: &'static str = "parachain::subsystem-util-vstaging"; - -/// A snapshot of the runtime claim queue at an arbitrary relay chain block. -#[derive(Default)] -pub struct ClaimQueueSnapshot(pub BTreeMap>); - -impl From>> for ClaimQueueSnapshot { - fn from(claim_queue_snapshot: BTreeMap>) -> Self { - ClaimQueueSnapshot(claim_queue_snapshot) - } -} - -impl ClaimQueueSnapshot { - /// Returns the `ParaId` that has a claim for `core_index` at the specified `depth` in the - /// claim queue. A depth of `0` means the very next block. - pub fn get_claim_for(&self, core_index: CoreIndex, depth: usize) -> Option { - self.0.get(&core_index)?.get(depth).copied() - } - - /// Returns an iterator over all claimed cores and the claiming `ParaId` at the specified - /// `depth` in the claim queue. - pub fn iter_claims_at_depth( - &self, - depth: usize, - ) -> impl Iterator + '_ { - self.0 - .iter() - .filter_map(move |(core_index, paras)| Some((*core_index, *paras.get(depth)?))) - } - - /// Returns an iterator over all claims on the given core. - pub fn iter_claims_for_core( - &self, - core_index: &CoreIndex, - ) -> impl Iterator + '_ { - self.0.get(core_index).map(|c| c.iter()).into_iter().flatten() - } - - /// Returns an iterator over the whole claim queue. - pub fn iter_all_claims(&self) -> impl Iterator)> + '_ { - self.0.iter() - } -} - -// TODO: https://github.com/paritytech/polkadot-sdk/issues/1940 -/// Returns disabled validators list if the runtime supports it. Otherwise logs a debug messages and -/// returns an empty vec. -/// Once runtime ver `DISABLED_VALIDATORS_RUNTIME_REQUIREMENT` is released remove this function and -/// replace all usages with `request_disabled_validators` -pub async fn get_disabled_validators_with_fallback>( - sender: &mut Sender, - relay_parent: Hash, -) -> Result, runtime::Error> { - let disabled_validators = if has_required_runtime( - sender, - relay_parent, - RuntimeApiRequest::DISABLED_VALIDATORS_RUNTIME_REQUIREMENT, - ) - .await - { - request_disabled_validators(relay_parent, sender) - .await - .await - .map_err(runtime::Error::RuntimeRequestCanceled)?? - } else { - gum::debug!(target: LOG_TARGET, "Runtime doesn't support `DisabledValidators` - continuing with an empty disabled validators set"); - vec![] - }; - - Ok(disabled_validators) -} - -/// Checks if the runtime supports `request_claim_queue` and attempts to fetch the claim queue. -/// Returns `ClaimQueueSnapshot` or `None` if claim queue API is not supported by runtime. -/// Any specific `RuntimeApiError` is bubbled up to the caller. -pub async fn fetch_claim_queue( - sender: &mut impl SubsystemSender, - relay_parent: Hash, -) -> Result, runtime::Error> { - if has_required_runtime( - sender, - relay_parent, - RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT, - ) - .await - { - let res = request_claim_queue(relay_parent, sender) - .await - .await - .map_err(runtime::Error::RuntimeRequestCanceled)??; - Ok(Some(res.into())) - } else { - gum::trace!(target: LOG_TARGET, "Runtime doesn't support `request_claim_queue`"); - Ok(None) - } -} diff --git a/polkadot/node/test/service/src/chain_spec.rs b/polkadot/node/test/service/src/chain_spec.rs index bd53fd843c69..8add51b07521 100644 --- a/polkadot/node/test/service/src/chain_spec.rs +++ b/polkadot/node/test/service/src/chain_spec.rs @@ -18,7 +18,7 @@ use pallet_staking::Forcing; use polkadot_primitives::{ - vstaging::SchedulerParams, AccountId, AssignmentId, ValidatorId, MAX_CODE_SIZE, MAX_POV_SIZE, + AccountId, AssignmentId, SchedulerParams, ValidatorId, MAX_CODE_SIZE, MAX_POV_SIZE, }; use polkadot_service::chain_spec::{get_account_id_from_seed, get_from_seed, Extensions}; use polkadot_test_runtime::BABE_GENESIS_EPOCH_CONFIG; diff --git a/polkadot/primitives/src/lib.rs b/polkadot/primitives/src/lib.rs index 73736fd4a3d6..493f9fb5ba92 100644 --- a/polkadot/primitives/src/lib.rs +++ b/polkadot/primitives/src/lib.rs @@ -19,8 +19,8 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -// `v6` is currently the latest stable version of the runtime API. -pub mod v7; +// `v11` is currently the latest stable version of the runtime API. +pub mod v8; // The 'staging' version is special - it contains primitives which are // still in development. Once they are considered stable, they will be @@ -35,7 +35,7 @@ extern crate alloc; // Current primitives not requiring versioning are exported here. // Primitives requiring versioning must not be exported and must be referred by an exact version. -pub use v7::{ +pub use v8::{ async_backing, byzantine_threshold, check_candidate_backing, collator_signature_payload, effective_minimum_backing_votes, executor_params, metric_definitions, node_features, slashing, supermajority_threshold, well_known_keys, AbridgedHostConfiguration, AbridgedHrmpChannel, @@ -54,16 +54,17 @@ pub use v7::{ OutboundHrmpMessage, ParathreadClaim, ParathreadEntry, PersistedValidationData, PvfCheckStatement, PvfExecKind, PvfPrepKind, RuntimeMetricLabel, RuntimeMetricLabelValue, RuntimeMetricLabelValues, RuntimeMetricLabels, RuntimeMetricOp, RuntimeMetricUpdate, - ScheduledCore, ScrapedOnChainVotes, SessionIndex, SessionInfo, Signature, Signed, - SignedAvailabilityBitfield, SignedAvailabilityBitfields, SignedStatement, SigningContext, Slot, - UncheckedSigned, UncheckedSignedAvailabilityBitfield, UncheckedSignedAvailabilityBitfields, - UncheckedSignedStatement, UpgradeGoAhead, UpgradeRestriction, UpwardMessage, - ValidDisputeStatementKind, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - ValidatorSignature, ValidityAttestation, ValidityError, ASSIGNMENT_KEY_TYPE_ID, - LEGACY_MIN_BACKING_VOTES, LOWEST_PUBLIC_ID, MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MAX_POV_SIZE, - MIN_CODE_SIZE, ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, ON_DEMAND_MAX_QUEUE_MAX_SIZE, - PARACHAINS_INHERENT_IDENTIFIER, PARACHAIN_KEY_TYPE_ID, + ScheduledCore, SchedulerParams, ScrapedOnChainVotes, SessionIndex, SessionInfo, Signature, + Signed, SignedAvailabilityBitfield, SignedAvailabilityBitfields, SignedStatement, + SigningContext, Slot, UncheckedSigned, UncheckedSignedAvailabilityBitfield, + UncheckedSignedAvailabilityBitfields, UncheckedSignedStatement, UpgradeGoAhead, + UpgradeRestriction, UpwardMessage, ValidDisputeStatementKind, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, ValidityAttestation, + ValidityError, ASSIGNMENT_KEY_TYPE_ID, LEGACY_MIN_BACKING_VOTES, LOWEST_PUBLIC_ID, + MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MAX_POV_SIZE, MIN_CODE_SIZE, + ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, ON_DEMAND_MAX_QUEUE_MAX_SIZE, PARACHAINS_INHERENT_IDENTIFIER, + PARACHAIN_KEY_TYPE_ID, }; #[cfg(feature = "std")] -pub use v7::{AssignmentPair, CollatorPair, ValidatorPair}; +pub use v8::{AssignmentPair, CollatorPair, ValidatorPair}; diff --git a/polkadot/primitives/src/v7/async_backing.rs b/polkadot/primitives/src/v8/async_backing.rs similarity index 100% rename from polkadot/primitives/src/v7/async_backing.rs rename to polkadot/primitives/src/v8/async_backing.rs diff --git a/polkadot/primitives/src/v7/executor_params.rs b/polkadot/primitives/src/v8/executor_params.rs similarity index 100% rename from polkadot/primitives/src/v7/executor_params.rs rename to polkadot/primitives/src/v8/executor_params.rs diff --git a/polkadot/primitives/src/v7/metrics.rs b/polkadot/primitives/src/v8/metrics.rs similarity index 100% rename from polkadot/primitives/src/v7/metrics.rs rename to polkadot/primitives/src/v8/metrics.rs diff --git a/polkadot/primitives/src/v7/mod.rs b/polkadot/primitives/src/v8/mod.rs similarity index 96% rename from polkadot/primitives/src/v7/mod.rs rename to polkadot/primitives/src/v8/mod.rs index a729d8cee4c7..b6928e372064 100644 --- a/polkadot/primitives/src/v7/mod.rs +++ b/polkadot/primitives/src/v8/mod.rs @@ -29,7 +29,10 @@ use core::{ use scale_info::TypeInfo; use sp_application_crypto::KeyTypeId; -use sp_arithmetic::traits::{BaseArithmetic, Saturating}; +use sp_arithmetic::{ + traits::{BaseArithmetic, Saturating}, + Perbill, +}; use sp_core::RuntimeDebug; use sp_inherents::InherentIdentifier; use sp_runtime::traits::{AppVerify, Header as HeaderT}; @@ -2051,6 +2054,81 @@ pub mod node_features { } } +/// Scheduler configuration parameters. All coretime/ondemand parameters are here. +#[derive( + RuntimeDebug, + Copy, + Clone, + PartialEq, + Encode, + Decode, + TypeInfo, + serde::Serialize, + serde::Deserialize, +)] +pub struct SchedulerParams { + /// How often parachain groups should be rotated across parachains. + /// + /// Must be non-zero. + pub group_rotation_frequency: BlockNumber, + /// Availability timeout for a block on a core, measured in blocks. + /// + /// This is the maximum amount of blocks after a core became occupied that validators have time + /// to make the block available. + /// + /// This value only has effect on group rotations. If backers backed something at the end of + /// their rotation, the occupied core affects the backing group that comes afterwards. We limit + /// the effect one backing group can have on the next to `paras_availability_period` blocks. + /// + /// Within a group rotation there is no timeout as backers are only affecting themselves. + /// + /// Must be at least 1. With a value of 1, the previous group will not be able to negatively + /// affect the following group at the expense of a tight availability timeline at group + /// rotation boundaries. + pub paras_availability_period: BlockNumber, + /// The maximum number of validators to have per core. + /// + /// `None` means no maximum. + pub max_validators_per_core: Option, + /// The amount of blocks ahead to schedule paras. + pub lookahead: u32, + /// How many cores are managed by the coretime chain. + pub num_cores: u32, + /// The max number of times a claim can time out in availability. + pub max_availability_timeouts: u32, + /// The maximum queue size of the pay as you go module. + pub on_demand_queue_max_size: u32, + /// The target utilization of the spot price queue in percentages. + pub on_demand_target_queue_utilization: Perbill, + /// How quickly the fee rises in reaction to increased utilization. + /// The lower the number the slower the increase. + pub on_demand_fee_variability: Perbill, + /// The minimum amount needed to claim a slot in the spot pricing queue. + pub on_demand_base_fee: Balance, + /// The number of blocks a claim stays in the scheduler's claim queue before getting cleared. + /// This number should go reasonably higher than the number of blocks in the async backing + /// lookahead. + pub ttl: BlockNumber, +} + +impl> Default for SchedulerParams { + fn default() -> Self { + Self { + group_rotation_frequency: 1u32.into(), + paras_availability_period: 1u32.into(), + max_validators_per_core: Default::default(), + lookahead: 1, + num_cores: Default::default(), + max_availability_timeouts: Default::default(), + on_demand_queue_max_size: ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, + on_demand_target_queue_utilization: Perbill::from_percent(25), + on_demand_fee_variability: Perbill::from_percent(3), + on_demand_base_fee: 10_000_000u128, + ttl: 5u32.into(), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/polkadot/primitives/src/v7/signed.rs b/polkadot/primitives/src/v8/signed.rs similarity index 100% rename from polkadot/primitives/src/v7/signed.rs rename to polkadot/primitives/src/v8/signed.rs diff --git a/polkadot/primitives/src/v7/slashing.rs b/polkadot/primitives/src/v8/slashing.rs similarity index 100% rename from polkadot/primitives/src/v7/slashing.rs rename to polkadot/primitives/src/v8/slashing.rs diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 27296213e611..1429b0c326ac 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -17,84 +17,3 @@ //! Staging Primitives. // Put any primitives used by staging APIs functions here -use crate::v7::*; - -use codec::{Decode, Encode}; -use scale_info::TypeInfo; -use sp_arithmetic::Perbill; -use sp_core::RuntimeDebug; - -/// Scheduler configuration parameters. All coretime/ondemand parameters are here. -#[derive( - RuntimeDebug, - Copy, - Clone, - PartialEq, - Encode, - Decode, - TypeInfo, - serde::Serialize, - serde::Deserialize, -)] -pub struct SchedulerParams { - /// How often parachain groups should be rotated across parachains. - /// - /// Must be non-zero. - pub group_rotation_frequency: BlockNumber, - /// Availability timeout for a block on a core, measured in blocks. - /// - /// This is the maximum amount of blocks after a core became occupied that validators have time - /// to make the block available. - /// - /// This value only has effect on group rotations. If backers backed something at the end of - /// their rotation, the occupied core affects the backing group that comes afterwards. We limit - /// the effect one backing group can have on the next to `paras_availability_period` blocks. - /// - /// Within a group rotation there is no timeout as backers are only affecting themselves. - /// - /// Must be at least 1. With a value of 1, the previous group will not be able to negatively - /// affect the following group at the expense of a tight availability timeline at group - /// rotation boundaries. - pub paras_availability_period: BlockNumber, - /// The maximum number of validators to have per core. - /// - /// `None` means no maximum. - pub max_validators_per_core: Option, - /// The amount of blocks ahead to schedule paras. - pub lookahead: u32, - /// How many cores are managed by the coretime chain. - pub num_cores: u32, - /// The max number of times a claim can time out in availability. - pub max_availability_timeouts: u32, - /// The maximum queue size of the pay as you go module. - pub on_demand_queue_max_size: u32, - /// The target utilization of the spot price queue in percentages. - pub on_demand_target_queue_utilization: Perbill, - /// How quickly the fee rises in reaction to increased utilization. - /// The lower the number the slower the increase. - pub on_demand_fee_variability: Perbill, - /// The minimum amount needed to claim a slot in the spot pricing queue. - pub on_demand_base_fee: Balance, - /// The number of blocks a claim stays in the scheduler's claim queue before getting cleared. - /// This number should go reasonably higher than the number of blocks in the async backing - /// lookahead. - pub ttl: BlockNumber, -} - -impl> Default for SchedulerParams { - fn default() -> Self { - Self { - group_rotation_frequency: 1u32.into(), - paras_availability_period: 1u32.into(), - max_validators_per_core: Default::default(), - lookahead: 1, - num_cores: Default::default(), - max_availability_timeouts: Default::default(), - on_demand_queue_max_size: ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, - on_demand_target_queue_utilization: Perbill::from_percent(25), - on_demand_fee_variability: Perbill::from_percent(3), - on_demand_base_fee: 10_000_000u128, - ttl: 5u32.into(), - } - } -} diff --git a/polkadot/runtime/parachains/src/configuration.rs b/polkadot/runtime/parachains/src/configuration.rs index 19df1f8c3607..30fe95883e77 100644 --- a/polkadot/runtime/parachains/src/configuration.rs +++ b/polkadot/runtime/parachains/src/configuration.rs @@ -42,7 +42,7 @@ mod benchmarking; pub mod migration; pub use pallet::*; -use polkadot_primitives::vstaging::SchedulerParams; +use polkadot_primitives::SchedulerParams; const LOG_TARGET: &str = "runtime::configuration"; diff --git a/polkadot/runtime/parachains/src/configuration/migration/v12.rs b/polkadot/runtime/parachains/src/configuration/migration/v12.rs index 6b77655687f0..111b1a199966 100644 --- a/polkadot/runtime/parachains/src/configuration/migration/v12.rs +++ b/polkadot/runtime/parachains/src/configuration/migration/v12.rs @@ -24,7 +24,7 @@ use frame_support::{ traits::{Defensive, UncheckedOnRuntimeUpgrade}, }; use frame_system::pallet_prelude::BlockNumberFor; -use polkadot_primitives::vstaging::SchedulerParams; +use polkadot_primitives::SchedulerParams; use sp_core::Get; use sp_staking::SessionIndex; diff --git a/polkadot/runtime/parachains/src/paras/tests.rs b/polkadot/runtime/parachains/src/paras/tests.rs index 732b75417387..6e4f99aa3d84 100644 --- a/polkadot/runtime/parachains/src/paras/tests.rs +++ b/polkadot/runtime/parachains/src/paras/tests.rs @@ -16,7 +16,7 @@ use super::*; use frame_support::{assert_err, assert_ok, assert_storage_noop}; -use polkadot_primitives::{vstaging::SchedulerParams, BlockNumber, PARACHAIN_KEY_TYPE_ID}; +use polkadot_primitives::{BlockNumber, SchedulerParams, PARACHAIN_KEY_TYPE_ID}; use polkadot_primitives_test_helpers::{dummy_head_data, dummy_validation_code, validator_pubkeys}; use sc_keystore::LocalKeystore; use sp_keyring::Sr25519Keyring; diff --git a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs index 88b90e792e73..1742e91276d4 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs @@ -21,7 +21,7 @@ use core::cmp::min; use frame_benchmarking::{benchmarks, impl_benchmark_test_suite}; use frame_system::RawOrigin; -use polkadot_primitives::v7::GroupIndex; +use polkadot_primitives::v8::GroupIndex; use crate::builder::BenchBuilder; diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index ad89e68e9059..e34055bfa9f2 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -20,7 +20,7 @@ use crate::{ configuration::{self, HostConfiguration}, mock::MockGenesisConfig, }; -use polkadot_primitives::vstaging::SchedulerParams; +use polkadot_primitives::SchedulerParams; fn default_config() -> MockGenesisConfig { MockGenesisConfig { @@ -58,7 +58,7 @@ mod enter { use core::panic; use frame_support::assert_ok; use frame_system::limits; - use polkadot_primitives::{vstaging::SchedulerParams, AvailabilityBitfield, UncheckedSigned}; + use polkadot_primitives::{AvailabilityBitfield, SchedulerParams, UncheckedSigned}; use sp_runtime::Perbill; struct TestConfig { diff --git a/polkadot/runtime/parachains/src/scheduler/tests.rs b/polkadot/runtime/parachains/src/scheduler/tests.rs index f3866146e811..5f80114b5963 100644 --- a/polkadot/runtime/parachains/src/scheduler/tests.rs +++ b/polkadot/runtime/parachains/src/scheduler/tests.rs @@ -19,7 +19,7 @@ use super::*; use alloc::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use frame_support::assert_ok; use polkadot_primitives::{ - vstaging::SchedulerParams, BlockNumber, SessionIndex, ValidationCode, ValidatorId, + BlockNumber, SchedulerParams, SessionIndex, ValidationCode, ValidatorId, }; use sp_keyring::Sr25519Keyring; diff --git a/polkadot/runtime/parachains/src/session_info/tests.rs b/polkadot/runtime/parachains/src/session_info/tests.rs index 3e81ca498713..aec0ff56e670 100644 --- a/polkadot/runtime/parachains/src/session_info/tests.rs +++ b/polkadot/runtime/parachains/src/session_info/tests.rs @@ -24,7 +24,7 @@ use crate::{ }, util::take_active_subset, }; -use polkadot_primitives::{vstaging::SchedulerParams, BlockNumber, ValidatorId, ValidatorIndex}; +use polkadot_primitives::{BlockNumber, SchedulerParams, ValidatorId, ValidatorIndex}; use sp_keyring::Sr25519Keyring; fn run_to_block( diff --git a/polkadot/runtime/rococo/src/genesis_config_presets.rs b/polkadot/runtime/rococo/src/genesis_config_presets.rs index 67dcd6cd7a51..17792cff1867 100644 --- a/polkadot/runtime/rococo/src/genesis_config_presets.rs +++ b/polkadot/runtime/rococo/src/genesis_config_presets.rs @@ -20,9 +20,7 @@ use crate::{SessionKeys, BABE_GENESIS_EPOCH_CONFIG}; #[cfg(not(feature = "std"))] use alloc::format; use alloc::vec::Vec; -use polkadot_primitives::{ - vstaging::SchedulerParams, AccountId, AccountPublic, AssignmentId, ValidatorId, -}; +use polkadot_primitives::{AccountId, AccountPublic, AssignmentId, SchedulerParams, ValidatorId}; use rococo_runtime_constants::currency::UNITS as ROC; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_babe::AuthorityId as BabeId; From 9cdf3d999e9de7996495c891af7085f55acc695a Mon Sep 17 00:00:00 2001 From: zjb0807 Date: Fri, 30 Aug 2024 20:55:57 +0800 Subject: [PATCH 196/382] Add more logs for AcceptanceCheckErr (#5513) # Description The error message should be logged out when the check method returns an error. Because specific information is lost when `UmpAcceptanceCheckErr`, `ProcessedDownwardMessagesAcceptanceErr`, `HrmpWatermarkAcceptanceErr`, `OutboundHrmpAcceptanceErr` are converted to `AcceptanceCheckErr`, a log is added to each check. ## Integration ## Review Notes # Checklist * [ ] My PR includes a detailed description as outlined in the "Description" and its two subsections above. * [ ] My PR follows the [labeling requirements]( https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CONTRIBUTING.md#Process ) of this project (at minimum one label for `T` required) * External contributors: ask maintainers to put the right label on your PR. * [ ] I have made corresponding changes to the documentation (if applicable) * [ ] I have added tests that prove my fix is effective or that my feature works (if applicable) --- .../runtime/parachains/src/inclusion/mod.rs | 79 +++++++++++++------ prdoc/pr_5513.prdoc | 10 +++ 2 files changed, 67 insertions(+), 22 deletions(-) create mode 100644 prdoc/pr_5513.prdoc diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index fbf13339dfca..b1d22996fd12 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -34,7 +34,6 @@ use alloc::{ }; use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; use codec::{Decode, Encode}; -#[cfg(feature = "std")] use core::fmt; use frame_support::{ defensive, @@ -381,7 +380,7 @@ pub mod pallet { const LOG_TARGET: &str = "runtime::inclusion"; /// The reason that a candidate's outputs were rejected for. -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Debug)] enum AcceptanceCheckErr { HeadDataTooLarge, /// Code upgrades are not permitted at the current time. @@ -439,7 +438,6 @@ pub(crate) enum UmpAcceptanceCheckErr { IsOffboarding, } -#[cfg(feature = "std")] impl fmt::Debug for UmpAcceptanceCheckErr { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -828,23 +826,20 @@ impl Pallet { let prev_context = Self::para_most_recent_context(¶_id); let check_ctx = CandidateCheckContext::::new(prev_context); - if check_ctx - .check_validation_outputs( - para_id, - relay_parent_number, - &validation_outputs.head_data, - &validation_outputs.new_validation_code, - validation_outputs.processed_downward_messages, - &validation_outputs.upward_messages, - BlockNumberFor::::from(validation_outputs.hrmp_watermark), - &validation_outputs.horizontal_messages, - ) - .is_err() - { + if let Err(err) = check_ctx.check_validation_outputs( + para_id, + relay_parent_number, + &validation_outputs.head_data, + &validation_outputs.new_validation_code, + validation_outputs.processed_downward_messages, + &validation_outputs.upward_messages, + BlockNumberFor::::from(validation_outputs.hrmp_watermark), + &validation_outputs.horizontal_messages, + ) { log::debug!( target: LOG_TARGET, - "Validation outputs checking for parachain `{}` failed", - u32::from(para_id), + "Validation outputs checking for parachain `{}` failed, error: {:?}", + u32::from(para_id), err ); false } else { @@ -1307,9 +1302,10 @@ impl CandidateCheckContext { ) { log::debug!( target: LOG_TARGET, - "Validation outputs checking during inclusion of a candidate {:?} for parachain `{}` failed", + "Validation outputs checking during inclusion of a candidate {:?} for parachain `{}` failed, error: {:?}", backed_candidate_receipt.hash(), u32::from(para_id), + err ); Err(err.strip_into_dispatch_err::())?; }; @@ -1365,10 +1361,49 @@ impl CandidateCheckContext { para_id, relay_parent_number, processed_downward_messages, + ) + .map_err(|e| { + log::debug!( + target: LOG_TARGET, + "Check processed downward messages for parachain `{}` on relay parent number `{:?}` failed, error: {:?}", + u32::from(para_id), + relay_parent_number, + e + ); + e + })?; + Pallet::::check_upward_messages(&self.config, para_id, upward_messages).map_err( + |e| { + log::debug!( + target: LOG_TARGET, + "Check upward messages for parachain `{}` failed, error: {:?}", + u32::from(para_id), + e + ); + e + }, )?; - Pallet::::check_upward_messages(&self.config, para_id, upward_messages)?; - hrmp::Pallet::::check_hrmp_watermark(para_id, relay_parent_number, hrmp_watermark)?; - hrmp::Pallet::::check_outbound_hrmp(&self.config, para_id, horizontal_messages)?; + hrmp::Pallet::::check_hrmp_watermark(para_id, relay_parent_number, hrmp_watermark) + .map_err(|e| { + log::debug!( + target: LOG_TARGET, + "Check hrmp watermark for parachain `{}` on relay parent number `{:?}` failed, error: {:?}", + u32::from(para_id), + relay_parent_number, + e + ); + e + })?; + hrmp::Pallet::::check_outbound_hrmp(&self.config, para_id, horizontal_messages) + .map_err(|e| { + log::debug!( + target: LOG_TARGET, + "Check outbound hrmp for parachain `{}` failed, error: {:?}", + u32::from(para_id), + e + ); + e + })?; Ok(()) } diff --git a/prdoc/pr_5513.prdoc b/prdoc/pr_5513.prdoc new file mode 100644 index 000000000000..0eda8b02cde0 --- /dev/null +++ b/prdoc/pr_5513.prdoc @@ -0,0 +1,10 @@ +title: Add more logs to trace AcceptanceCheckErr + +doc: + - audience: Runtime Dev + description: | + `Debug` was derived on `AcceptanceCheckErr` to print the error. This PR adds more logs to trace the error. + +crates: + - name: polkadot-runtime-parachains + bump: patch From d34f6878a337f6211a4708560e7e669d9bd7c1d6 Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:05:05 +0100 Subject: [PATCH 197/382] fix cmd bot PR context (#5531) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - restore update-ui.sh (accidentally removed with bunch of bash 😅 - fix empty context and pushing to dev branch (supporting forks) tested fork here: https://github.com/paritytech-stg/polkadot-sdk/pull/45 --- .github/workflows/cmd.yml | 49 +++++++++++++++++++++++++--- .github/workflows/command-inform.yml | 22 +++++++++++++ scripts/update-ui-tests.sh | 41 +++++++++++++++++++++++ 3 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/command-inform.yml create mode 100644 scripts/update-ui-tests.sh diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index dac46cf435a6..7a742751005d 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -237,8 +237,48 @@ jobs: echo "RUNNER=ubuntu-latest" >> $GITHUB_OUTPUT fi - cmd: + # Get PR branch name, because the issue_comment event does not contain the PR branch name + get-pr-branch: needs: [ set-image ] + runs-on: ubuntu-latest + outputs: + pr-branch: ${{ steps.get-pr.outputs.pr_branch }} + repo: ${{ steps.get-pr.outputs.repo }} + steps: + - name: Check if the issue is a PR + id: check-pr + run: | + if [ -n "${{ github.event.issue.pull_request.url }}" ]; then + echo "This is a pull request comment" + else + echo "This is not a pull request comment" + exit 1 + fi + + - name: Get PR Branch Name and Repo + if: steps.check-pr.outcome == 'success' + id: get-pr + uses: actions/github-script@v7 + with: + script: | + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + const prBranch = pr.data.head.ref; + const repo = pr.data.head.repo.full_name; + console.log(prBranch, repo) + core.setOutput('pr_branch', prBranch); + core.setOutput('repo', repo); + + - name: Use PR Branch Name and Repo + run: | + echo "The PR branch is ${{ steps.get-pr.outputs.pr_branch }}" + echo "The repository is ${{ steps.get-pr.outputs.repo }}" + + cmd: + needs: [ set-image, get-pr-branch ] env: JOB_NAME: 'cmd' runs-on: ${{ needs.set-image.outputs.RUNNER }} @@ -291,7 +331,8 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - ref: ${{ github.head_ref }} + repository: ${{ needs.get-pr-branch.outputs.repo }} + ref: ${{ needs.get-pr-branch.outputs.pr-branch }} - name: Install dependencies for bench if: startsWith(steps.get-pr-comment.outputs.group2, 'bench') @@ -317,11 +358,11 @@ jobs: git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - git pull origin ${{ github.head_ref }} + git pull origin ${{ needs.get-pr-branch.outputs.pr-branch }} git add . git restore --staged Cargo.lock # ignore changes in Cargo.lock git commit -m "Update from ${{ github.actor }} running command '${{ steps.get-pr-comment.outputs.group2 }}'" || true - git push origin ${{ github.head_ref }} + git push origin ${{ needs.get-pr-branch.outputs.pr-branch }} else echo "Nothing to commit"; fi diff --git a/.github/workflows/command-inform.yml b/.github/workflows/command-inform.yml new file mode 100644 index 000000000000..973463953193 --- /dev/null +++ b/.github/workflows/command-inform.yml @@ -0,0 +1,22 @@ +name: Inform of new command action + +on: + issue_comment: + types: [ created ] + +jobs: + comment: + runs-on: ubuntu-latest + # Temporary disable the bot until the new command bot works properly + if: github.event.issue.pull_request && startsWith(github.event.comment.body, 'bot ') && false # disabled for now, until tested + steps: + - name: Inform that the new command exist + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: 'We have migrated the command bot to GHA

Please, see the new usage instructions here. Soon the old commands will be disabled.' + }) \ No newline at end of file diff --git a/scripts/update-ui-tests.sh b/scripts/update-ui-tests.sh new file mode 100644 index 000000000000..a1f380c4712d --- /dev/null +++ b/scripts/update-ui-tests.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Script for updating the UI tests for a new rust stable version. +# Exit on error +set -e + +# by default current rust stable will be used +RUSTUP_RUN="" +# check if we have a parameter +# ./scripts/update-ui-tests.sh 1.70 +if [ ! -z "$1" ]; then + echo "RUST_VERSION: $1" + # This will run all UI tests with the rust stable 1.70. + # The script requires that rustup is installed. + RUST_VERSION=$1 + RUSTUP_RUN="rustup run $RUST_VERSION" + + + echo "installing rustup $RUST_VERSION" + if ! command -v rustup &> /dev/null + then + echo "rustup needs to be installed" + exit + fi + + rustup install $RUST_VERSION + rustup component add rust-src --toolchain $RUST_VERSION +fi + +# Ensure we run the ui tests +export RUN_UI_TESTS=1 +# We don't need any wasm files for ui tests +export SKIP_WASM_BUILD=1 +# Let trybuild overwrite the .stderr files +export TRYBUILD=overwrite + +# ./substrate +$RUSTUP_RUN cargo test --manifest-path substrate/primitives/runtime-interface/Cargo.toml ui +$RUSTUP_RUN cargo test -p sp-api-test ui +$RUSTUP_RUN cargo test -p frame-election-provider-solution-type ui +$RUSTUP_RUN cargo test -p frame-support-test --features=no-metadata-docs,try-runtime,experimental ui +$RUSTUP_RUN cargo test -p xcm-procedural ui \ No newline at end of file From 95f39778575e46bf830fe1241ee096e87cd31391 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Fri, 30 Aug 2024 17:33:10 +0200 Subject: [PATCH 198/382] asset-hub-rococo: genesis config presets added (#3996) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gensis config presets moved from `polkadot-parachain` binary into `asset-hub-rococo` runtime. relates to: #3944 --------- Co-authored-by: Dónal Murray Co-authored-by: Bastian Köcher --- Cargo.lock | 1 + .../common/src/genesis_config_helpers.rs | 47 +++++ cumulus/parachains/common/src/lib.rs | 1 + .../assets/asset-hub-rococo/Cargo.toml | 2 + .../src/genesis_config_presets.rs | 183 ++++++++++++++++++ .../assets/asset-hub-rococo/src/lib.rs | 10 +- .../runtimes/constants/src/rococo.rs | 5 + .../src/chain_spec/asset_hubs.rs | 130 +------------ prdoc/pr_3996.prdoc | 20 ++ 9 files changed, 268 insertions(+), 131 deletions(-) create mode 100644 cumulus/parachains/common/src/genesis_config_helpers.rs create mode 100644 cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs create mode 100644 prdoc/pr_3996.prdoc diff --git a/Cargo.lock b/Cargo.lock index 8eb4fa5b4e83..c878bd60cfb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -887,6 +887,7 @@ dependencies = [ "primitive-types", "rococo-runtime-constants", "scale-info", + "serde_json", "snowbridge-router-primitives", "sp-api", "sp-block-builder", diff --git a/cumulus/parachains/common/src/genesis_config_helpers.rs b/cumulus/parachains/common/src/genesis_config_helpers.rs new file mode 100644 index 000000000000..d70b8d5b9c11 --- /dev/null +++ b/cumulus/parachains/common/src/genesis_config_helpers.rs @@ -0,0 +1,47 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Some common helpers for declaring runtime's presets +// note: copied from: cumulus/polkadot-parachain/src/chain_spec/mod.rs + +use crate::{AccountId, Signature}; +#[cfg(not(feature = "std"))] +use alloc::format; +use sp_core::{Pair, Public}; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +/// Helper function to generate a crypto pair from seed. +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{seed}"), None) + .expect("static values are valid; qed") + .public() +} + +type AccountPublic = ::Signer; + +/// Helper function to generate an account id from seed. +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Generate collator keys from seed. +/// +/// This function's return type must always match the session keys of the chain in tuple format. +pub fn get_collator_keys_from_seed(seed: &str) -> ::Public { + get_from_seed::(seed) +} diff --git a/cumulus/parachains/common/src/lib.rs b/cumulus/parachains/common/src/lib.rs index 3cffb69daac3..60040fda9928 100644 --- a/cumulus/parachains/common/src/lib.rs +++ b/cumulus/parachains/common/src/lib.rs @@ -17,6 +17,7 @@ extern crate alloc; +pub mod genesis_config_helpers; pub mod impls; pub mod message_queue; pub mod xcm_config; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 0143c09036d2..47e0983a4150 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -14,6 +14,7 @@ codec = { features = ["derive", "max-encoded-len"], workspace = true } hex-literal = { workspace = true, default-features = true } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } +serde_json = { features = ["alloc"], workspace = true } # Substrate frame-benchmarking = { optional = true, workspace = true } @@ -230,6 +231,7 @@ std = [ "primitive-types/std", "rococo-runtime-constants/std", "scale-info/std", + "serde_json/std", "snowbridge-router-primitives/std", "sp-api/std", "sp-block-builder/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs new file mode 100644 index 000000000000..41b7e622b1b2 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs @@ -0,0 +1,183 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Asset Hub Rococo Runtime genesis config presets + +use alloc::{vec, vec::Vec}; +use cumulus_primitives_core::ParaId; +use hex_literal::hex; +use parachains_common::{genesis_config_helpers::*, AccountId, AuraId, Balance as AssetHubBalance}; +use sp_core::{crypto::UncheckedInto, sr25519}; +use sp_genesis_builder::PresetId; +use testnet_parachains_constants::rococo::xcm_version::SAFE_XCM_VERSION; + +const ASSET_HUB_ROCOCO_ED: AssetHubBalance = crate::ExistentialDeposit::get(); + +/// Generate the session keys from individual elements. +/// +/// The input must be a tuple of individual keys (a single arg for now since we have just one key). +pub fn asset_hub_rococo_session_keys(keys: AuraId) -> crate::SessionKeys { + crate::SessionKeys { aura: keys } +} + +fn asset_hub_rococo_genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + endowment: AssetHubBalance, + id: ParaId, +) -> serde_json::Value { + serde_json::json!({ + "balances": crate::BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, endowment)) + .collect(), + }, + "parachainInfo": crate::ParachainInfoConfig { + parachain_id: id, + ..Default::default() + }, + "collatorSelection": crate::CollatorSelectionConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), + candidacy_bond: ASSET_HUB_ROCOCO_ED * 16, + ..Default::default() + }, + "session": crate::SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + asset_hub_rococo_session_keys(aura), // session keys + ) + }) + .collect(), + ..Default::default() + }, + "polkadotXcm": crate::PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + } + }) +} + +/// Encapsulates names of predefined presets. +mod preset_names { + pub const PRESET_DEVELOPMENT: &str = "development"; + pub const PRESET_LOCAL: &str = "local"; + pub const PRESET_GENESIS: &str = "genesis"; +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &PresetId) -> Option> { + use preset_names::*; + let patch = match id.try_into() { + Ok(PRESET_GENESIS) => asset_hub_rococo_genesis( + // initial collators. + vec![ + // E8XC6rTJRsioKCp6KMy6zd24ykj4gWsusZ3AkSeyavpVBAG + ( + hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608").into(), + hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608") + .unchecked_into(), + ), + // G28iWEybndgGRbhfx83t7Q42YhMPByHpyqWDUgeyoGF94ri + ( + hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944").into(), + hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944") + .unchecked_into(), + ), + // G839e2eMiq7UXbConsY6DS1XDAYG2XnQxAmLuRLGGQ3Px9c + ( + hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948").into(), + hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948") + .unchecked_into(), + ), + // GLao4ukFUW6qhexuZowdFrKa2NLCfnEjZMftSXXfvGv1vvt + ( + hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f").into(), + hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f") + .unchecked_into(), + ), + ], + Vec::new(), + ASSET_HUB_ROCOCO_ED * 524_288, + 1000.into(), + ), + Ok(PRESET_LOCAL) => asset_hub_rococo_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, + 1000.into(), + ), + Ok(PRESET_DEVELOPMENT) => asset_hub_rococo_genesis( + // initial collators. + vec![( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + )], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, + 1000.into(), + ), + Err(_) | Ok(_) => return None, + }; + + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + use preset_names::*; + vec![ + PresetId::from(PRESET_GENESIS), + PresetId::from(PRESET_DEVELOPMENT), + PresetId::from(PRESET_LOCAL), + ] +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 4c7356707ab6..896edd012dd5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -24,6 +24,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +mod genesis_config_presets; mod weights; pub mod xcm_config; @@ -40,6 +41,7 @@ use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::AggregateMessageOrigin; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_genesis_builder::PresetId; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{AccountIdConversion, BlakeTwo256, Block as BlockT, Saturating, Verify}, @@ -1769,12 +1771,12 @@ impl_runtime_apis! { build_state::(config) } - fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + fn get_preset(id: &Option) -> Option> { + get_preset::(id, &genesis_config_presets::get_preset) } - fn preset_names() -> Vec { - vec![] + fn preset_names() -> Vec { + genesis_config_presets::preset_names() } } } diff --git a/cumulus/parachains/runtimes/constants/src/rococo.rs b/cumulus/parachains/runtimes/constants/src/rococo.rs index d10b5e7d3af4..56f4868371c1 100644 --- a/cumulus/parachains/runtimes/constants/src/rococo.rs +++ b/cumulus/parachains/runtimes/constants/src/rococo.rs @@ -161,3 +161,8 @@ pub mod snowbridge { pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; } } + +pub mod xcm_version { + /// The default XCM version to set in genesis config. + pub const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; +} diff --git a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs index f6bf6375a353..233ae9866966 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs @@ -23,14 +23,6 @@ use sc_service::ChainType; use sp_core::{crypto::UncheckedInto, sr25519}; const ASSET_HUB_WESTEND_ED: AssetHubBalance = asset_hub_westend_runtime::ExistentialDeposit::get(); -const ASSET_HUB_ROCOCO_ED: AssetHubBalance = asset_hub_rococo_runtime::ExistentialDeposit::get(); - -/// Generate the session keys from individual elements. -/// -/// The input must be a tuple of individual keys (a single arg for now since we have just one key). -pub fn asset_hub_rococo_session_keys(keys: AuraId) -> asset_hub_rococo_runtime::SessionKeys { - asset_hub_rococo_runtime::SessionKeys { aura: keys } -} /// Generate the session keys from individual elements. /// @@ -227,21 +219,7 @@ fn asset_hub_rococo_like_development_config( .with_name(name) .with_id(chain_id) .with_chain_type(ChainType::Local) - .with_genesis_config_patch(asset_hub_rococo_genesis( - // initial collators. - vec![( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - )], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, - para_id.into(), - )) + .with_genesis_config_preset_name("development") .with_properties(properties) .build() } @@ -272,35 +250,7 @@ fn asset_hub_rococo_like_local_config( .with_name(name) .with_id(chain_id) .with_chain_type(ChainType::Local) - .with_genesis_config_patch(asset_hub_rococo_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, - para_id.into(), - )) + .with_genesis_config_preset_name("local") .with_properties(properties) .build() } @@ -317,81 +267,7 @@ pub fn asset_hub_rococo_genesis_config() -> GenericChainSpec { .with_name("Rococo Asset Hub") .with_id("asset-hub-rococo") .with_chain_type(ChainType::Live) - .with_genesis_config_patch(asset_hub_rococo_genesis( - // initial collators. - vec![ - // E8XC6rTJRsioKCp6KMy6zd24ykj4gWsusZ3AkSeyavpVBAG - ( - hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608").into(), - hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608") - .unchecked_into(), - ), - // G28iWEybndgGRbhfx83t7Q42YhMPByHpyqWDUgeyoGF94ri - ( - hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944").into(), - hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944") - .unchecked_into(), - ), - // G839e2eMiq7UXbConsY6DS1XDAYG2XnQxAmLuRLGGQ3Px9c - ( - hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948").into(), - hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948") - .unchecked_into(), - ), - // GLao4ukFUW6qhexuZowdFrKa2NLCfnEjZMftSXXfvGv1vvt - ( - hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f").into(), - hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f") - .unchecked_into(), - ), - ], - Vec::new(), - ASSET_HUB_ROCOCO_ED * 524_288, - para_id.into(), - )) + .with_genesis_config_preset_name("genesis") .with_properties(properties) .build() } - -fn asset_hub_rococo_genesis( - invulnerables: Vec<(AccountId, AuraId)>, - endowed_accounts: Vec, - endowment: AssetHubBalance, - id: ParaId, -) -> serde_json::Value { - serde_json::json!({ - "balances": asset_hub_rococo_runtime::BalancesConfig { - balances: endowed_accounts - .iter() - .cloned() - .map(|k| (k, endowment)) - .collect(), - }, - "parachainInfo": asset_hub_rococo_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() - }, - "collatorSelection": asset_hub_rococo_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: ASSET_HUB_ROCOCO_ED * 16, - ..Default::default() - }, - "session": asset_hub_rococo_runtime::SessionConfig { - keys: invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - asset_hub_rococo_session_keys(aura), // session keys - ) - }) - .collect(), - ..Default::default() - }, - "polkadotXcm": asset_hub_rococo_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() - } - }) -} diff --git a/prdoc/pr_3996.prdoc b/prdoc/pr_3996.prdoc new file mode 100644 index 000000000000..7590d8992368 --- /dev/null +++ b/prdoc/pr_3996.prdoc @@ -0,0 +1,20 @@ +title: asset-hub-rococo - genesis config presets added + +doc: + - audience: Node Dev + description: | + `asset-hub-rococo` genesis state was moved to runtime. + - audience: Runtime Dev + description: | + `asset-hub-rococo` genesis state was moved to runtime. + + +crates: + - name: parachains-common + bump: minor + - name: testnet-parachains-constants + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: polkadot-parachain-bin + bump: minor From 562870d46525ed3ce14d0b59066621815099c4e1 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Fri, 30 Aug 2024 21:45:00 +0200 Subject: [PATCH 199/382] parachain-template: genesis config presets added (#4739) Gensis config presets moved from `parachain-template-node` binary into `parachain-template-runtime` runtime. cc: @PierreBesson --- Cargo.lock | 1 + prdoc/pr_4739.prdoc | 15 ++ .../primitives/genesis-builder/src/lib.rs | 4 + templates/parachain/node/src/chain_spec.rs | 134 +------------- templates/parachain/runtime/Cargo.toml | 2 + templates/parachain/runtime/src/apis.rs | 4 +- .../runtime/src/genesis_config_presets.rs | 169 ++++++++++++++++++ templates/parachain/runtime/src/lib.rs | 1 + 8 files changed, 196 insertions(+), 134 deletions(-) create mode 100644 prdoc/pr_4739.prdoc create mode 100644 templates/parachain/runtime/src/genesis_config_presets.rs diff --git a/Cargo.lock b/Cargo.lock index c878bd60cfb7..3eaa7c24e30b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12236,6 +12236,7 @@ dependencies = [ "polkadot-parachain-primitives", "polkadot-runtime-common", "scale-info", + "serde_json", "smallvec", "sp-api", "sp-block-builder", diff --git a/prdoc/pr_4739.prdoc b/prdoc/pr_4739.prdoc new file mode 100644 index 000000000000..9ca230e3e763 --- /dev/null +++ b/prdoc/pr_4739.prdoc @@ -0,0 +1,15 @@ +title: parachain-template - genesis config presets added + +doc: + - audience: Node Dev + description: | + - common DEV_RUNTIME_PRESET ("development") const added to sp-genesis-builder. + - parachain-templates are now using presets. + +crates: + - name: sp-genesis-builder + bump: minor + - name: parachain-template-node + bump: patch + - name: parachain-template-runtime + bump: patch diff --git a/substrate/primitives/genesis-builder/src/lib.rs b/substrate/primitives/genesis-builder/src/lib.rs index 2cbac305b4d9..b33609464fc1 100644 --- a/substrate/primitives/genesis-builder/src/lib.rs +++ b/substrate/primitives/genesis-builder/src/lib.rs @@ -62,6 +62,10 @@ pub type Result = core::result::Result<(), sp_runtime::RuntimeString>; /// The type representing preset ID. pub type PresetId = sp_runtime::RuntimeString; +/// The default `development` preset used to communicate with the runtime via +/// [`GenesisBuilder`] interface. +pub const DEV_RUNTIME_PRESET: &'static str = "development"; + sp_api::decl_runtime_apis! { /// API to interact with RuntimeGenesisConfig for the runtime pub trait GenesisBuilder { diff --git a/templates/parachain/node/src/chain_spec.rs b/templates/parachain/node/src/chain_spec.rs index 3fa91c026162..cd02bca466ff 100644 --- a/templates/parachain/node/src/chain_spec.rs +++ b/templates/parachain/node/src/chain_spec.rs @@ -1,25 +1,11 @@ -use cumulus_primitives_core::ParaId; use parachain_template_runtime as runtime; -use runtime::{AccountId, AuraId, Signature, EXISTENTIAL_DEPOSIT}; use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; use sc_service::ChainType; use serde::{Deserialize, Serialize}; -use sp_core::{sr25519, Pair, Public}; -use sp_runtime::traits::{IdentifyAccount, Verify}; /// Specialized `ChainSpec` for the normal parachain runtime. pub type ChainSpec = sc_service::GenericChainSpec; -/// The default XCM version to set in genesis config. -const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; - -/// Helper function to generate a crypto pair from seed -pub fn get_from_seed(seed: &str) -> ::Public { - TPublic::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") - .public() -} - /// The extensions for the [`ChainSpec`]. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] pub struct Extensions { @@ -38,30 +24,6 @@ impl Extensions { } } -type AccountPublic = ::Signer; - -/// Generate collator keys from seed. -/// -/// This function's return type must always match the session keys of the chain in tuple format. -pub fn get_collator_keys_from_seed(seed: &str) -> AuraId { - get_from_seed::(seed) -} - -/// Helper function to generate an account ID from seed -pub fn get_account_id_from_seed(seed: &str) -> AccountId -where - AccountPublic: From<::Public>, -{ - AccountPublic::from(get_from_seed::(seed)).into_account() -} - -/// Generate the session keys from individual elements. -/// -/// The input must be a tuple of individual keys (a single arg for now since we have just one key). -pub fn template_session_keys(keys: AuraId) -> runtime::SessionKeys { - runtime::SessionKeys { aura: keys } -} - pub fn development_config() -> ChainSpec { // Give your base currency a unit name and decimal places let mut properties = sc_chain_spec::Properties::new(); @@ -80,35 +42,7 @@ pub fn development_config() -> ChainSpec { .with_name("Development") .with_id("dev") .with_chain_type(ChainType::Development) - .with_genesis_config_patch(testnet_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - get_account_id_from_seed::("Alice"), - 1000.into(), - )) + .with_genesis_config_preset_name("development") .build() } @@ -131,72 +65,8 @@ pub fn local_testnet_config() -> ChainSpec { .with_name("Local Testnet") .with_id("local_testnet") .with_chain_type(ChainType::Local) - .with_genesis_config_patch(testnet_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - get_account_id_from_seed::("Alice"), - 1000.into(), - )) + .with_genesis_config_preset_name("local_testnet") .with_protocol_id("template-local") .with_properties(properties) .build() } - -fn testnet_genesis( - invulnerables: Vec<(AccountId, AuraId)>, - endowed_accounts: Vec, - root: AccountId, - id: ParaId, -) -> serde_json::Value { - serde_json::json!({ - "balances": { - "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), - }, - "parachainInfo": { - "parachainId": id, - }, - "collatorSelection": { - "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), - "candidacyBond": EXISTENTIAL_DEPOSIT * 16, - }, - "session": { - "keys": invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - template_session_keys(aura), // session keys - ) - }) - .collect::>(), - }, - "polkadotXcm": { - "safeXcmVersion": Some(SAFE_XCM_VERSION), - }, - "sudo": { "key": Some(root) } - }) -} diff --git a/templates/parachain/runtime/Cargo.toml b/templates/parachain/runtime/Cargo.toml index 939fa245d2a0..45c77d18e816 100644 --- a/templates/parachain/runtime/Cargo.toml +++ b/templates/parachain/runtime/Cargo.toml @@ -27,6 +27,7 @@ scale-info = { features = [ ], workspace = true } smallvec = { workspace = true, default-features = true } docify = { workspace = true } +serde_json = { workspace = true, default-features = false } # Local pallet-parachain-template = { workspace = true } @@ -126,6 +127,7 @@ std = [ "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "scale-info/std", + "serde_json/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", diff --git a/templates/parachain/runtime/src/apis.rs b/templates/parachain/runtime/src/apis.rs index f5d5d3e63027..243db1b6dde0 100644 --- a/templates/parachain/runtime/src/apis.rs +++ b/templates/parachain/runtime/src/apis.rs @@ -295,11 +295,11 @@ impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, crate::genesis_config_presets::get_preset) } fn preset_names() -> Vec { - Default::default() + crate::genesis_config_presets::preset_names() } } } diff --git a/templates/parachain/runtime/src/genesis_config_presets.rs b/templates/parachain/runtime/src/genesis_config_presets.rs new file mode 100644 index 000000000000..80b763d5bd85 --- /dev/null +++ b/templates/parachain/runtime/src/genesis_config_presets.rs @@ -0,0 +1,169 @@ +use cumulus_primitives_core::ParaId; + +pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; + +use crate::{AccountId, SessionKeys, Signature, EXISTENTIAL_DEPOSIT}; +use alloc::{format, vec, vec::Vec}; +use serde_json::Value; +use sp_core::{sr25519, Pair, Public}; +use sp_genesis_builder::PresetId; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +/// Preset configuration name for a local testnet environment. +pub const PRESET_LOCAL_TESTNET: &str = "local_testnet"; + +type AccountPublic = ::Signer; + +/// The default XCM version to set in genesis config. +const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; + +/// Helper function to generate a crypto pair from seed +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +/// Generate collator keys from seed. +/// +/// This function's return type must always match the session keys of the chain in tuple format. +pub fn get_collator_keys_from_seed(seed: &str) -> AuraId { + get_from_seed::(seed) +} + +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Generate the session keys from individual elements. +/// +/// The input must be a tuple of individual keys (a single arg for now since we have just one key). +pub fn template_session_keys(keys: AuraId) -> SessionKeys { + SessionKeys { aura: keys } +} + +fn testnet_genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + root: AccountId, + id: ParaId, +) -> Value { + serde_json::json!({ + "balances": { + "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), + }, + "parachainInfo": { + "parachainId": id, + }, + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": EXISTENTIAL_DEPOSIT * 16, + }, + "session": { + "keys": invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + template_session_keys(aura), // session keys + ) + }) + .collect::>(), + }, + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), + }, + "sudo": { "key": Some(root) } + }) +} + +fn local_testnet_genesis() -> Value { + testnet_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + get_account_id_from_seed::("Alice"), + 1000.into(), + ) +} + +fn development_config_genesis() -> Value { + testnet_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + get_account_id_from_seed::("Alice"), + 1000.into(), + ) +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &PresetId) -> Option> { + let patch = match id.try_into() { + Ok(PRESET_LOCAL_TESTNET) => local_testnet_genesis(), + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => development_config_genesis(), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + vec![ + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from(PRESET_LOCAL_TESTNET), + ] +} diff --git a/templates/parachain/runtime/src/lib.rs b/templates/parachain/runtime/src/lib.rs index 55714a124813..83ae15700a94 100644 --- a/templates/parachain/runtime/src/lib.rs +++ b/templates/parachain/runtime/src/lib.rs @@ -10,6 +10,7 @@ pub mod apis; #[cfg(feature = "runtime-benchmarks")] mod benchmarks; pub mod configs; +mod genesis_config_presets; mod weights; extern crate alloc; From 824e1cfa62d91635cbe3db13b2839690e3635d49 Mon Sep 17 00:00:00 2001 From: Jan-Jan Date: Fri, 30 Aug 2024 21:46:07 +0200 Subject: [PATCH 200/382] 'remainder' instead of reminder && explicit instruction to clone (#5535) # Description Trivial doc fixes: * Replace the word `reminder` with `remainder` so that the English matches the code intent. * Explicit instruct the reader to `clone`. ## Review Notes * Trivial Co-authored-by: Jan-Jan <111935+Jan-Jan@users.noreply.github.com> --- docs/sdk/src/guides/your_first_pallet/mod.rs | 14 +++++++------- .../sdk/src/guides/your_first_pallet/with_event.rs | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/sdk/src/guides/your_first_pallet/mod.rs b/docs/sdk/src/guides/your_first_pallet/mod.rs index 006d0be7ded3..fcfaab00e552 100644 --- a/docs/sdk/src/guides/your_first_pallet/mod.rs +++ b/docs/sdk/src/guides/your_first_pallet/mod.rs @@ -16,7 +16,7 @@ //! //! ## Writing Your First Pallet //! -//! To get started, use one of the templates mentioned in [`crate::polkadot_sdk::templates`]. We +//! To get started, clone one of the templates mentioned in [`crate::polkadot_sdk::templates`]. We //! recommend using the `polkadot-sdk-minimal-template`. You might need to change small parts of //! this guide, namely the crate/package names, based on which template you use. //! @@ -395,11 +395,11 @@ pub mod pallet { if sender_balance < amount { return Err("InsufficientBalance".into()) } - let reminder = sender_balance - amount; + let remainder = sender_balance - amount; // update sender and dest balances. Balances::::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount)); - Balances::::insert(&sender, reminder); + Balances::::insert(&sender, remainder); Ok(()) } @@ -417,7 +417,7 @@ pub mod pallet { let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; ensure!(sender_balance >= amount, "InsufficientBalance"); - let reminder = sender_balance - amount; + let remainder = sender_balance - amount; // .. snip Ok(()) @@ -433,7 +433,7 @@ pub mod pallet { let sender = ensure_signed(origin)?; let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; - let reminder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?; + let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?; // .. snip Ok(()) @@ -717,11 +717,11 @@ pub mod pallet_v2 { // ensure sender has enough balance, and if so, calculate what is left after `amount`. let sender_balance = Balances::::get(&sender).ok_or(Error::::NonExistentAccount)?; - let reminder = + let remainder = sender_balance.checked_sub(amount).ok_or(Error::::InsufficientBalance)?; Balances::::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount)); - Balances::::insert(&sender, reminder); + Balances::::insert(&sender, remainder); Self::deposit_event(Event::::Transferred { from: sender, to: dest, amount }); diff --git a/docs/sdk/src/guides/your_first_pallet/with_event.rs b/docs/sdk/src/guides/your_first_pallet/with_event.rs index a65aac324f07..a5af29c9c319 100644 --- a/docs/sdk/src/guides/your_first_pallet/with_event.rs +++ b/docs/sdk/src/guides/your_first_pallet/with_event.rs @@ -54,11 +54,11 @@ pub mod pallet { if sender_balance < amount { return Err("NotEnoughBalance".into()) } - let reminder = sender_balance - amount; + let remainder = sender_balance - amount; // update sender and dest balances. Balances::::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount)); - Balances::::insert(&sender, reminder); + Balances::::insert(&sender, remainder); Ok(()) } @@ -76,7 +76,7 @@ pub mod pallet { let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; ensure!(sender_balance >= amount, "NotEnoughBalance"); - let reminder = sender_balance - amount; + let remainder = sender_balance - amount; // .. snip Ok(()) @@ -92,7 +92,7 @@ pub mod pallet { let sender = ensure_signed(origin)?; let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; - let reminder = sender_balance.checked_sub(amount).ok_or("NotEnoughBalance")?; + let remainder = sender_balance.checked_sub(amount).ok_or("NotEnoughBalance")?; // .. snip Ok(()) From ea80adfdbc477c54d1cda9f6e911f917acc3af17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 31 Aug 2024 00:45:15 +0200 Subject: [PATCH 201/382] Bump the known_good_semver group across 1 directory with 5 updates (#5460) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the known_good_semver group with 4 updates in the / directory: [quote](https://github.com/dtolnay/quote), [serde](https://github.com/serde-rs/serde), [serde_json](https://github.com/serde-rs/json) and [syn](https://github.com/dtolnay/syn). Updates `quote` from 1.0.36 to 1.0.37

Release notes

Sourced from quote's releases.

1.0.37

  • Implement ToTokens for CStr and CString (#283)
Commits
  • b1ebffa Release 1.0.37
  • 43acd77 Delete unneeded use of ref
  • 9382c21 Merge pull request #283 from dtolnay/cstr
  • 6ac4328 Add C string tests
  • 9fb0591 Implement ToTokens for CStr and CString
  • ba7a9d0 Organize test imports
  • aa9970f Inline the macro that generates primitive impls
  • ba41109 Merge pull request #282 from dtolnay/tokens
  • c77340a Consistently use 'tokens' as the name of the &mut TokenStream arg
  • a4a0abf Merge pull request #281 from dtolnay/char
  • Additional commits viewable in compare view

Updates `serde` from 1.0.206 to 1.0.209
Release notes

Sourced from serde's releases.

v1.0.209

  • Fix deserialization of empty structs and empty tuples inside of untagged enums (#2805, thanks @​Mingun)

v1.0.208

  • Support serializing and deserializing unit structs in a flatten field (#2802, thanks @​jonhoo)

v1.0.207

  • Improve interactions between flatten attribute and skip_serializing/skip_deserializing (#2795, thanks @​Mingun)
Commits
  • 30752ac Release 1.0.209
  • b84e6ca Improve wording of PR 2805 comments
  • 87a2fb0 Wrap comments from PR 2805 to 80 columns
  • 9eaf7b9 Merge pull request #2805 from Mingun/untagged-tests
  • 7bde100 Replace MapRefDeserializer with value::MapDeserializer
  • da7fc79 Fix deserialization of empty struct variant in untagged enums
  • 4c5fec1 Test special cases that reaches SeqRefDeserializer::deserialize_any len==0 co...
  • 6588b0a Cover Content::Seq case in VariantRefDeserializer::struct_variant
  • 0093f74 Split test newtype_enum into four tests for each variant
  • 171c6da Complete coverage of ContentRefDeserializer::deserialize_newtype_struct
  • Additional commits viewable in compare view

Updates `serde_derive` from 1.0.206 to 1.0.209
Release notes

Sourced from serde_derive's releases.

v1.0.209

  • Fix deserialization of empty structs and empty tuples inside of untagged enums (#2805, thanks @​Mingun)

v1.0.208

  • Support serializing and deserializing unit structs in a flatten field (#2802, thanks @​jonhoo)

v1.0.207

  • Improve interactions between flatten attribute and skip_serializing/skip_deserializing (#2795, thanks @​Mingun)
Commits
  • 30752ac Release 1.0.209
  • b84e6ca Improve wording of PR 2805 comments
  • 87a2fb0 Wrap comments from PR 2805 to 80 columns
  • 9eaf7b9 Merge pull request #2805 from Mingun/untagged-tests
  • 7bde100 Replace MapRefDeserializer with value::MapDeserializer
  • da7fc79 Fix deserialization of empty struct variant in untagged enums
  • 4c5fec1 Test special cases that reaches SeqRefDeserializer::deserialize_any len==0 co...
  • 6588b0a Cover Content::Seq case in VariantRefDeserializer::struct_variant
  • 0093f74 Split test newtype_enum into four tests for each variant
  • 171c6da Complete coverage of ContentRefDeserializer::deserialize_newtype_struct
  • Additional commits viewable in compare view

Updates `serde_json` from 1.0.124 to 1.0.127
Release notes

Sourced from serde_json's releases.

1.0.127

1.0.126

  • Improve string parsing on targets that use 32-bit pointers but also have fast 64-bit integer arithmetic, such as aarch64-unknown-linux-gnu_ilp32 and x86_64-unknown-linux-gnux32 (#1182, thanks @​CryZe)

1.0.125

  • Speed up \uXXXX parsing and improve handling of unpaired surrogates when deserializing to bytes (#1172, #1175, thanks @​purplesyringa)
Commits
  • 5ebf65c Release 1.0.127
  • f287a3b Merge pull request 1179 from GREsau/patch-1
  • ec980b0 Release 1.0.126
  • e6282b0 Merge pull request #1184 from serde-rs/fastarithmetic
  • ffc4a43 Improve cfg names for fast arithmetic
  • 4b1048d Merge pull request #1183 from serde-rs/arithmetic
  • f268173 Unify chunk size choice between float and string parsing
  • fec0376 Merge pull request #1182 from CryZe/chunk-64bit
  • 3d837e1 Ensure the SWAR chunks are 64-bit in more cases
  • 11fc61c Add OccupiedEntry::shift_remove() and swap_remove()
  • Additional commits viewable in compare view

Updates `syn` from 2.0.61 to 2.0.65
Release notes

Sourced from syn's releases.

2.0.65

  • Optimize the implementation of Fold to compile faster (#1666, #1667, #1668)

2.0.64

  • Support using ParseBuffer across catch_unwind (#1646)
  • Validate that the expression in a let-else ends in brace as required by rustc (#1648, #1649)
  • Legalize invalid const generic arguments by wrapping in braces (#1654, #1655)
  • Fix some expression precedence edge cases involving break and return in loop headers (#1656)
  • Always print closure bodies with a brace when the closure has an explicit return type (#1658)
  • Automatically insert necessary parentheses in ToTokens for Expr when required by expression precedence (#1659)
  • Support struct literal syntax in match guard expressions (#1662)

2.0.63

  • Parse and print long if-else-if chains without reliance on deep recursion to avoid overflowing stack (#1644, #1645)

2.0.62

  • Reject invalid unparenthesized range and comparison operator expressions (#1642, #1643)
Commits
  • 9f2371e Release 2.0.65
  • 4cd1813 Merge pull request #1668 from dtolnay/foldhelper
  • ed54092 Eliminate gen::helper module
  • eacc8ab Eliminate FoldHelper trait
  • 6e20bb8 Merge pull request #1667 from dtolnay/punctuatedfold
  • 9d95cab Optimize punctuated::fold
  • 82ffe86 Move Punctuated fold helper to punctuated module
  • 3dfacc1 Ignore manual_map clippy lint
  • 7273aa7 Merge pull request #1666 from dtolnay/foldhelper
  • 8124c0e Generate fewer monomorphizations in Fold
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 398 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 8 +- 2 files changed, 203 insertions(+), 203 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3eaa7c24e30b..d81a9f23e813 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,8 +158,8 @@ dependencies = [ "heck 0.4.1", "proc-macro-error", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", "syn-solidity", "tiny-keccak", ] @@ -285,8 +285,8 @@ dependencies = [ "itertools 0.10.5", "proc-macro-error", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -484,7 +484,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" dependencies = [ - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -494,7 +494,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -506,7 +506,7 @@ checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ "num-bigint", "num-traits", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -519,7 +519,7 @@ dependencies = [ "num-bigint", "num-traits", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -621,7 +621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -725,7 +725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", "synstructure 0.12.6", ] @@ -737,8 +737,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", "synstructure 0.13.1", ] @@ -749,7 +749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -760,8 +760,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -1098,7 +1098,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -1288,8 +1288,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -1305,8 +1305,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -1364,7 +1364,7 @@ checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -1501,11 +1501,11 @@ dependencies = [ "peeking_take_while", "prettyplease 0.2.12", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -2820,7 +2820,7 @@ dependencies = [ "heck 0.4.1", "proc-macro-error", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -2832,8 +2832,8 @@ checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck 0.5.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -3020,7 +3020,7 @@ checksum = "d51beaa537d73d2d1ff34ee70bc095f170420ab2ec5d687ecd3ec2b0d092514b" dependencies = [ "nom", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -4143,8 +4143,8 @@ version = "0.6.0" dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -4678,8 +4678,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -4717,9 +4717,9 @@ dependencies = [ "codespan-reporting", "once_cell", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "scratch", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -4735,8 +4735,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50c49547d73ba8dcfd4ad7325d64c6d5391ff4224d498fc39a6f3f49825a530d" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -4841,7 +4841,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -4852,8 +4852,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -4863,8 +4863,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -4875,7 +4875,7 @@ checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "rustc_version 0.4.0", "syn 1.0.109", ] @@ -4971,8 +4971,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -5031,9 +5031,9 @@ dependencies = [ "derive-syn-parse", "once_cell", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "regex", - "syn 2.0.61", + "syn 2.0.65", "termcolor", "toml 0.8.8", "walkdir", @@ -5080,7 +5080,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -5226,7 +5226,7 @@ checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck 0.4.1", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -5238,8 +5238,8 @@ checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ "heck 0.4.1", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -5258,8 +5258,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -5269,8 +5269,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -5472,8 +5472,8 @@ dependencies = [ "fs-err", "prettyplease 0.2.12", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -5544,8 +5544,8 @@ dependencies = [ "indexmap 2.2.3", "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -5876,10 +5876,10 @@ dependencies = [ "parity-scale-codec", "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "scale-info", "sp-arithmetic", - "syn 2.0.61", + "syn 2.0.65", "trybuild", ] @@ -6071,7 +6071,7 @@ dependencies = [ "pretty_assertions", "proc-macro-warning 1.0.0", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "regex", "scale-info", "sp-core", @@ -6080,7 +6080,7 @@ dependencies = [ "sp-metadata-ir", "sp-runtime", "static_assertions", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -6090,8 +6090,8 @@ dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -6099,8 +6099,8 @@ name = "frame-support-procedural-tools-derive" version = "11.0.0" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -6350,8 +6350,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -7143,7 +7143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -7163,7 +7163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", ] [[package]] @@ -7522,8 +7522,8 @@ dependencies = [ "heck 0.5.0", "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -8108,8 +8108,8 @@ dependencies = [ "heck 0.4.1", "proc-macro-warning 0.4.2", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -8522,8 +8522,8 @@ checksum = "cc33f9f0351468d26fbc53d9ce00a096c8522ecb42f19b50f34f2c422f76d21d" dependencies = [ "macro_magic_core", "macro_magic_macros", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -8536,8 +8536,8 @@ dependencies = [ "derive-syn-parse", "macro_magic_core_macros", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -8547,8 +8547,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -8558,8 +8558,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -8892,7 +8892,7 @@ checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -8904,8 +8904,8 @@ checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" dependencies = [ "cfg-if", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -9016,7 +9016,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", "synstructure 0.12.6", ] @@ -9064,7 +9064,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -9455,8 +9455,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -9631,8 +9631,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -9698,7 +9698,7 @@ dependencies = [ "petgraph", "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -10402,8 +10402,8 @@ name = "pallet-contracts-proc-macro" version = "18.0.0" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -11502,8 +11502,8 @@ name = "pallet-revive-proc-macro" version = "0.1.0" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -11746,9 +11746,9 @@ version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "sp-runtime", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -12392,7 +12392,7 @@ checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -12839,8 +12839,8 @@ dependencies = [ "pest", "pest_meta", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -12880,8 +12880,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -15289,8 +15289,8 @@ checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ "polkavm-common 0.9.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -15301,8 +15301,8 @@ checksum = "7855353a5a783dd5d09e3b915474bddf66575f5a3cf45dec8d1c5e051ba320dc" dependencies = [ "polkavm-common 0.10.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -15312,7 +15312,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl 0.9.0", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -15322,7 +15322,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9324fe036de37c17829af233b46ef6b5562d4a0c09bb7fdb9f8378856dee30cf" dependencies = [ "polkavm-derive-impl 0.10.0", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -15549,7 +15549,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ "proc-macro2 1.0.82", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -15610,7 +15610,7 @@ checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", "version_check", ] @@ -15622,7 +15622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "version_check", ] @@ -15639,8 +15639,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -15650,8 +15650,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b698b0b09d40e9b7c1a47b132d66a8b54bcd20583d9b6d06e4535e383b4405c" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -15731,8 +15731,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -15826,7 +15826,7 @@ dependencies = [ "prost 0.12.6", "prost-types 0.12.4", "regex", - "syn 2.0.61", + "syn 2.0.65", "tempfile", ] @@ -15839,7 +15839,7 @@ dependencies = [ "anyhow", "itertools 0.10.5", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -15852,8 +15852,8 @@ dependencies = [ "anyhow", "itertools 0.11.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -16087,9 +16087,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2 1.0.82", ] @@ -16291,8 +16291,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -16841,11 +16841,11 @@ dependencies = [ "cfg-if", "glob", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "regex", "relative-path", "rustc_version 0.4.0", - "syn 2.0.61", + "syn 2.0.65", "unicode-ident", ] @@ -17335,8 +17335,8 @@ version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -18630,8 +18630,8 @@ version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -18744,7 +18744,7 @@ checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -18782,7 +18782,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "serde_derive_internals", "syn 1.0.109", ] @@ -19050,9 +19050,9 @@ checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" [[package]] name = "serde" -version = "1.0.206" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] @@ -19077,13 +19077,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.206" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -19093,7 +19093,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -19108,9 +19108,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.124" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "indexmap 2.2.3", "itoa", @@ -19184,8 +19184,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -20059,8 +20059,8 @@ dependencies = [ "expander", "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -20443,9 +20443,9 @@ dependencies = [ name = "sp-crypto-hashing-proc-macro" version = "0.1.0" dependencies = [ - "quote 1.0.36", + "quote 1.0.37", "sp-crypto-hashing", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -20462,8 +20462,8 @@ version = "8.0.0" source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -20471,8 +20471,8 @@ name = "sp-debug-derive" version = "14.0.0" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -20743,8 +20743,8 @@ dependencies = [ "Inflector", "proc-macro-crate 1.3.1", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -20755,8 +20755,8 @@ dependencies = [ "expander", "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -21016,9 +21016,9 @@ version = "13.0.0" dependencies = [ "parity-scale-codec", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "sp-version", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -21101,7 +21101,7 @@ dependencies = [ "Inflector", "num-format", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "serde", "serde_json", "unicode-xid 0.2.4", @@ -21126,7 +21126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f07d54c4d01a1713eb363b55ba51595da15f6f1211435b71466460da022aa140" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -21319,7 +21319,7 @@ dependencies = [ "cfg_aliases", "memchr", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -21392,7 +21392,7 @@ dependencies = [ "heck 0.3.3", "proc-macro-error", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -21428,7 +21428,7 @@ checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "rustversion", "syn 1.0.109", ] @@ -21441,9 +21441,9 @@ checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "rustversion", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -21454,9 +21454,9 @@ checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ "heck 0.4.1", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "rustversion", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -21904,18 +21904,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.61" +version = "2.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "unicode-ident", ] @@ -21927,8 +21927,8 @@ checksum = "86b837ef12ab88835251726eb12237655e61ec8dc8a280085d1961cdc3dfd047" dependencies = [ "paste", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -21938,7 +21938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", "unicode-xid 0.2.4", ] @@ -21950,8 +21950,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -22069,8 +22069,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -22233,7 +22233,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -22244,8 +22244,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -22410,8 +22410,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -22617,8 +22617,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -22659,8 +22659,8 @@ dependencies = [ "expander", "proc-macro-crate 3.1.0", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -23230,8 +23230,8 @@ dependencies = [ "log", "once_cell", "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", "wasm-bindgen-shared", ] @@ -23253,7 +23253,7 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ - "quote 1.0.36", + "quote 1.0.37", "wasm-bindgen-macro-support", ] @@ -23264,8 +23264,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -23297,7 +23297,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", ] [[package]] @@ -24363,9 +24363,9 @@ version = "7.0.0" dependencies = [ "Inflector", "proc-macro2 1.0.82", - "quote 1.0.36", + "quote 1.0.37", "staging-xcm", - "syn 2.0.61", + "syn 2.0.65", "trybuild", ] @@ -24529,8 +24529,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -24549,8 +24549,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7cd18bc8a590..24694b185aec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1090,7 +1090,7 @@ pyroscope = { version = "0.5.7" } pyroscope_pprofrs = { version = "0.2.7" } quick_cache = { version = "0.3" } quickcheck = { version = "1.0.3", default-features = false } -quote = { version = "1.0.36" } +quote = { version = "1.0.37" } rand = { version = "0.8.5", default-features = false } rand_chacha = { version = "0.3.1", default-features = false } rand_core = { version = "0.6.2" } @@ -1185,10 +1185,10 @@ secp256k1 = { version = "0.28.0", default-features = false } secrecy = { version = "0.8.0", default-features = false } seedling-runtime = { path = "cumulus/parachains/runtimes/starters/seedling" } separator = { version = "0.4.1" } -serde = { version = "1.0.206", default-features = false } +serde = { version = "1.0.209", default-features = false } serde-big-array = { version = "0.3.2" } serde_derive = { version = "1.0.117" } -serde_json = { version = "1.0.124", default-features = false } +serde_json = { version = "1.0.127", default-features = false } serde_yaml = { version = "0.9" } serial_test = { version = "2.0.0" } sha1 = { version = "0.10.6" } @@ -1297,7 +1297,7 @@ substrate-test-runtime-client = { path = "substrate/test-utils/runtime/client" } substrate-test-runtime-transaction-pool = { path = "substrate/test-utils/runtime/transaction-pool" } substrate-test-utils = { path = "substrate/test-utils" } substrate-wasm-builder = { path = "substrate/utils/wasm-builder", default-features = false } -syn = { version = "2.0.53" } +syn = { version = "2.0.65" } sysinfo = { version = "0.30" } tar = { version = "0.4" } tempfile = { version = "3.8.1" } From 9cf6c5bc6cbf470c1b3c239b360f522e6826ce74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Sat, 31 Aug 2024 12:35:40 +0200 Subject: [PATCH 202/382] Update CODEOWNERS for contracts (#5537) Added the new contracts pallet and also added @pgherveou as code owners. --- .github/CODEOWNERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4fc5b97caae0..343358221215 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -64,7 +64,8 @@ /substrate/primitives/merkle-mountain-range/ @acatangiu # Contracts -/substrate/frame/contracts/ @athei @paritytech/docs-audit +/substrate/frame/contracts/ @athei @pgherveou @paritytech/docs-audit +/substrate/frame/revive/ @athei @pgherveou @paritytech/docs-audit # NPoS and election /substrate/frame/election-provider-multi-phase/ @paritytech/staking-core @paritytech/docs-audit From b7d5f15aede020d65b2b9634e858dac863c0520a Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Sat, 31 Aug 2024 12:13:52 +0100 Subject: [PATCH 203/382] Update cmd.yml (#5536) Tiny fix for subweight diff in /cmd --- .github/workflows/cmd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index 7a742751005d..dfdf771a6105 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -380,7 +380,7 @@ jobs: --no-color \ --change added changed \ --ignore-errors \ - refs/remotes/origin/master ${{ github.ref }}) + refs/remotes/origin/master refs/heads/${{ needs.get-pr-branch.outputs.pr-branch }}) # Save the multiline result to the output { From f0b2add13a741e21d91c7ccb5f26bb2685315e5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 08:57:14 +0000 Subject: [PATCH 204/382] Bump color-eyre from 0.6.2 to 0.6.3 (#5543) Bumps [color-eyre](https://github.com/eyre-rs/eyre) from 0.6.2 to 0.6.3.
Commits
  • f544fed chore: Release
  • 7689b98 chore: don't inherit workspace readme
  • 63cb412 chore: remove old metadata
  • 7e7e173 chore: update changelog
  • 7a5c32a Add color-eyre to workspace (#110)
  • eb8d059 Merge remote-tracking branch 'origin/master' into color-eyre
  • 75beaae fix: remove anyhow feature flag from OptionExt location test (#148)
  • e570151 color-spantrace: bump owo-colors to 4.0 (#156)
  • cb4bab6 chore: update issues redirect
  • 8ebc308 fix: make theme test more lenient
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=color-eyre&package-manager=cargo&previous-version=0.6.2&new-version=0.6.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d81a9f23e813..4cb69d1a178e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2992,9 +2992,9 @@ dependencies = [ [[package]] name = "color-eyre" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" dependencies = [ "backtrace", "eyre", diff --git a/Cargo.toml b/Cargo.toml index 24694b185aec..f8c430f58254 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -673,7 +673,7 @@ coarsetime = { version = "0.1.22" } codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" } collectives-westend-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend" } collectives-westend-runtime = { path = "cumulus/parachains/runtimes/collectives/collectives-westend" } -color-eyre = { version = "0.6.1", default-features = false } +color-eyre = { version = "0.6.3", default-features = false } color-print = { version = "0.3.4" } colored = { version = "2.0.4" } comfy-table = { version = "7.1.0", default-features = false } From 6b854acc69cd64f7c0e6cdb606e741e630e45032 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:05:03 +0300 Subject: [PATCH 205/382] [3 / 5] Move crypto checks in the approval-distribution (#4928) # Prerequisite This is part of the work to further optimize the approval subsystems, if you want to understand the full context start with reading https://github.com/paritytech/polkadot-sdk/pull/4849#issue-2364261568, # Description This PR contain changes, so that the crypto checks are performed by the approval-distribution subsystem instead of the approval-voting one. The benefit for these, is twofold: 1. Approval-distribution won't have to wait every single time for the approval-voting to finish its job, so the work gets to be pipelined between approval-distribution and approval-voting. 2. By running in parallel multiple instances of approval-distribution as described here https://github.com/paritytech/polkadot-sdk/pull/4849#issue-2364261568, this significant body of work gets to run in parallel. ## Changes: 1. When approval-voting send `ApprovalDistributionMessage::NewBlocks` it needs to pass the core_index and candidate_hash of the candidates. 2. ApprovalDistribution needs to use `RuntimeInfo` to be able to fetch the SessionInfo from the runtime. 3. Move `approval-voting` logic that checks VRF assignment into `approval-distribution` 4. Move `approval-voting` logic that checks vote is correctly signed into `approval-distribution` 5. Plumb `approval-distribution` and `approval-voting` tests to support the new logic. ## Benefits Even without parallelisation the gains are significant, for example on my machine if we run approval subsystem bench for 500 validators and 100 cores and trigger all 89 tranches of assignments and approvals, the system won't fall behind anymore because of late processing of messages. ``` Before change Chain selection approved after 11500 ms hash=0x0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a After change Chain selection approved after 5500 ms hash=0x0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a ``` ## TODO: - [x] Run on versi. - [x] Update parachain host documentation. --------- Signed-off-by: Alexandru Gheorghe --- Cargo.lock | 5 + .../approval-voting-regression-bench.rs | 4 +- .../approval-voting/src/approval_checking.rs | 8 +- .../node/core/approval-voting/src/criteria.rs | 174 +- .../node/core/approval-voting/src/import.rs | 12 +- polkadot/node/core/approval-voting/src/lib.rs | 239 +- .../approval-voting/src/persisted_entries.rs | 4 +- .../node/core/approval-voting/src/tests.rs | 464 +- .../network/approval-distribution/Cargo.toml | 2 + .../network/approval-distribution/src/lib.rs | 500 +- .../approval-distribution/src/metrics.rs | 16 - .../approval-distribution/src/tests.rs | 6307 ++++++++++------- polkadot/node/overseer/src/lib.rs | 1 + polkadot/node/primitives/Cargo.toml | 3 + .../node/primitives/src/approval/criteria.rs | 177 + .../src/{approval.rs => approval/mod.rs} | 14 +- .../src => primitives/src/approval}/time.rs | 13 +- polkadot/node/service/src/overseer.rs | 8 +- .../src/lib/approval/helpers.rs | 2 +- .../src/lib/approval/message_generator.rs | 7 +- .../src/lib/approval/mock_chain_selection.rs | 2 +- .../subsystem-bench/src/lib/approval/mod.rs | 14 +- polkadot/node/subsystem-types/src/messages.rs | 79 +- .../node/approval/approval-distribution.md | 36 +- .../src/node/approval/approval-voting.md | 40 +- .../src/types/overseer-protocol.md | 24 +- prdoc/pr_4928.prdoc | 28 + 27 files changed, 4648 insertions(+), 3535 deletions(-) create mode 100644 polkadot/node/primitives/src/approval/criteria.rs rename polkadot/node/primitives/src/{approval.rs => approval/mod.rs} (98%) rename polkadot/node/{core/approval-voting/src => primitives/src/approval}/time.rs (95%) create mode 100644 prdoc/pr_4928.prdoc diff --git a/Cargo.lock b/Cargo.lock index 4cb69d1a178e..9d8f4cef1420 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12989,7 +12989,9 @@ dependencies = [ "rand", "rand_chacha", "rand_core", + "sc-keystore", "schnorrkel 0.11.4", + "sp-application-crypto", "sp-authority-discovery", "sp-core", "sp-tracing 16.0.0", @@ -13802,14 +13804,17 @@ dependencies = [ "bitvec", "bounded-vec", "futures", + "futures-timer", "parity-scale-codec", "polkadot-erasure-coding", "polkadot-parachain-primitives", "polkadot-primitives", + "sc-keystore", "schnorrkel 0.11.4", "serde", "sp-application-crypto", "sp-consensus-babe", + "sp-consensus-slots", "sp-core", "sp-keystore", "sp-maybe-compressed-blob", diff --git a/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs b/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs index 41418bcc511f..0b03f1127ee8 100644 --- a/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs +++ b/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs @@ -82,8 +82,8 @@ fn main() -> Result<(), String> { ("Sent to peers", 63995.2200, 0.01), ])); messages.extend(average_usage.check_cpu_usage(&[ - ("approval-distribution", 6.3912, 0.1), - ("approval-voting", 10.0578, 0.1), + ("approval-distribution", 12.2736, 0.1), + ("approval-voting", 2.7174, 0.1), ])); if messages.is_empty() { diff --git a/polkadot/node/core/approval-voting/src/approval_checking.rs b/polkadot/node/core/approval-voting/src/approval_checking.rs index 96eb25626de8..3774edc69981 100644 --- a/polkadot/node/core/approval-voting/src/approval_checking.rs +++ b/polkadot/node/core/approval-voting/src/approval_checking.rs @@ -22,9 +22,9 @@ use polkadot_primitives::ValidatorIndex; use crate::{ persisted_entries::{ApprovalEntry, CandidateEntry, TrancheEntry}, - time::Tick, MAX_RECORDED_NO_SHOW_VALIDATORS_PER_CANDIDATE, }; +use polkadot_node_primitives::approval::time::Tick; /// Result of counting the necessary tranches needed for approving a block. #[derive(Debug, PartialEq, Clone)] @@ -1195,9 +1195,9 @@ mod tests { struct NoShowTest { assignments: Vec<(ValidatorIndex, Tick)>, approvals: Vec, - clock_drift: crate::time::Tick, - no_show_duration: crate::time::Tick, - drifted_tick_now: crate::time::Tick, + clock_drift: Tick, + no_show_duration: Tick, + drifted_tick_now: Tick, exp_no_shows: usize, exp_next_no_show: Option, } diff --git a/polkadot/node/core/approval-voting/src/criteria.rs b/polkadot/node/core/approval-voting/src/criteria.rs index fb9d281e43bc..669b6001538e 100644 --- a/polkadot/node/core/approval-voting/src/criteria.rs +++ b/polkadot/node/core/approval-voting/src/criteria.rs @@ -16,8 +16,11 @@ //! Assignment criteria VRF generation and checking. -use codec::{Decode, Encode}; +use codec::Encode; use itertools::Itertools; +pub use polkadot_node_primitives::approval::criteria::{ + AssignmentCriteria, Config, InvalidAssignment, InvalidAssignmentReason, OurAssignment, +}; use polkadot_node_primitives::approval::{ self as approval_types, v1::{AssignmentCert, AssignmentCertKind, DelayTranche, RelayVRFStory}, @@ -25,9 +28,9 @@ use polkadot_node_primitives::approval::{ AssignmentCertKindV2, AssignmentCertV2, CoreBitfield, VrfPreOutput, VrfProof, VrfSignature, }, }; + use polkadot_primitives::{ - AssignmentId, AssignmentPair, CandidateHash, CoreIndex, GroupIndex, IndexedVec, SessionInfo, - ValidatorIndex, + AssignmentPair, CandidateHash, CoreIndex, GroupIndex, IndexedVec, ValidatorIndex, }; use rand::{seq::SliceRandom, SeedableRng}; use rand_chacha::ChaCha20Rng; @@ -44,56 +47,19 @@ use std::{ use super::LOG_TARGET; -/// Details pertaining to our assignment on a block. -#[derive(Debug, Clone, Encode, Decode, PartialEq)] -pub struct OurAssignment { - cert: AssignmentCertV2, - tranche: DelayTranche, - validator_index: ValidatorIndex, - // Whether the assignment has been triggered already. - triggered: bool, -} - -impl OurAssignment { - pub fn cert(&self) -> &AssignmentCertV2 { - &self.cert - } - - pub fn tranche(&self) -> DelayTranche { - self.tranche - } - - pub(crate) fn validator_index(&self) -> ValidatorIndex { - self.validator_index - } - - pub(crate) fn triggered(&self) -> bool { - self.triggered - } - - pub(crate) fn mark_triggered(&mut self) { - self.triggered = true; - } -} - impl From for OurAssignment { fn from(entry: crate::approval_db::v2::OurAssignment) -> Self { - OurAssignment { - cert: entry.cert, - tranche: entry.tranche, - validator_index: entry.validator_index, - triggered: entry.triggered, - } + OurAssignment::new(entry.cert, entry.tranche, entry.validator_index, entry.triggered) } } impl From for crate::approval_db::v2::OurAssignment { fn from(entry: OurAssignment) -> Self { Self { - cert: entry.cert, - tranche: entry.tranche, - validator_index: entry.validator_index, - triggered: entry.triggered, + tranche: entry.tranche(), + validator_index: entry.validator_index(), + triggered: entry.triggered(), + cert: entry.into_cert(), } } } @@ -223,60 +189,7 @@ fn assigned_core_transcript(core_index: CoreIndex) -> Transcript { t } -/// Information about the world assignments are being produced in. -#[derive(Clone, Debug)] -pub struct Config { - /// The assignment public keys for validators. - assignment_keys: Vec, - /// The groups of validators assigned to each core. - validator_groups: IndexedVec>, - /// The number of availability cores used by the protocol during this session. - n_cores: u32, - /// The zeroth delay tranche width. - zeroth_delay_tranche_width: u32, - /// The number of samples we do of `relay_vrf_modulo`. - relay_vrf_modulo_samples: u32, - /// The number of delay tranches in total. - n_delay_tranches: u32, -} - -impl<'a> From<&'a SessionInfo> for Config { - fn from(s: &'a SessionInfo) -> Self { - Config { - assignment_keys: s.assignment_keys.clone(), - validator_groups: s.validator_groups.clone(), - n_cores: s.n_cores, - zeroth_delay_tranche_width: s.zeroth_delay_tranche_width, - relay_vrf_modulo_samples: s.relay_vrf_modulo_samples, - n_delay_tranches: s.n_delay_tranches, - } - } -} - -/// A trait for producing and checking assignments. Used to mock. -pub(crate) trait AssignmentCriteria { - fn compute_assignments( - &self, - keystore: &LocalKeystore, - relay_vrf_story: RelayVRFStory, - config: &Config, - leaving_cores: Vec<(CandidateHash, CoreIndex, GroupIndex)>, - enable_v2_assignments: bool, - ) -> HashMap; - - fn check_assignment_cert( - &self, - claimed_core_bitfield: CoreBitfield, - validator_index: ValidatorIndex, - config: &Config, - relay_vrf_story: RelayVRFStory, - assignment: &AssignmentCertV2, - // Backing groups for each "leaving core". - backing_groups: Vec, - ) -> Result; -} - -pub(crate) struct RealAssignmentCriteria; +pub struct RealAssignmentCriteria; impl AssignmentCriteria for RealAssignmentCriteria { fn compute_assignments( @@ -469,12 +382,12 @@ fn compute_relay_vrf_modulo_assignments_v1( }; // All assignments of type RelayVRFModulo have tranche 0. - assignments.entry(core).or_insert(OurAssignment { - cert: cert.into(), - tranche: 0, + assignments.entry(core).or_insert(OurAssignment::new( + cert.into(), + 0, validator_index, - triggered: false, - }); + false, + )); } } } @@ -549,7 +462,7 @@ fn compute_relay_vrf_modulo_assignments_v2( }; // All assignments of type RelayVRFModulo have tranche 0. - OurAssignment { cert, tranche: 0, validator_index, triggered: false } + OurAssignment::new(cert, 0, validator_index, false) }) { for core_index in assigned_cores { assignments.insert(core_index, assignment.clone()); @@ -583,7 +496,7 @@ fn compute_relay_vrf_delay_assignments( }, }; - let our_assignment = OurAssignment { cert, tranche, validator_index, triggered: false }; + let our_assignment = OurAssignment::new(cert, tranche, validator_index, false); let used = match assignments.entry(core) { Entry::Vacant(e) => { @@ -591,7 +504,7 @@ fn compute_relay_vrf_delay_assignments( true }, Entry::Occupied(mut e) => - if e.get().tranche > our_assignment.tranche { + if e.get().tranche() > our_assignment.tranche() { e.insert(our_assignment); true } else { @@ -612,35 +525,6 @@ fn compute_relay_vrf_delay_assignments( } } -/// Assignment invalid. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct InvalidAssignment(pub(crate) InvalidAssignmentReason); - -impl std::fmt::Display for InvalidAssignment { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Invalid Assignment: {:?}", self.0) - } -} - -impl std::error::Error for InvalidAssignment {} - -/// Failure conditions when checking an assignment cert. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum InvalidAssignmentReason { - ValidatorIndexOutOfBounds, - SampleOutOfBounds, - CoreIndexOutOfBounds, - InvalidAssignmentKey, - IsInBackingGroup, - VRFModuloCoreIndexMismatch, - VRFModuloOutputMismatch, - VRFDelayCoreIndexMismatch, - VRFDelayOutputMismatch, - InvalidArguments, - /// Assignment vrf check resulted in 0 assigned cores. - NullAssignment, -} - /// Checks the crypto of an assignment cert. Failure conditions: /// * Validator index out of bounds /// * VRF signature check fails @@ -820,13 +704,13 @@ fn is_in_backing_group( /// Migration helpers. impl From for OurAssignment { fn from(value: crate::approval_db::v1::OurAssignment) -> Self { - Self { - cert: value.cert.into(), - tranche: value.tranche, - validator_index: value.validator_index, + Self::new( + value.cert.into(), + value.tranche, + value.validator_index, // Whether the assignment has been triggered already. - triggered: value.triggered, - } + value.triggered, + ) } } @@ -834,7 +718,7 @@ impl From for OurAssignment { mod tests { use super::*; use crate::import::tests::garbage_vrf_signature; - use polkadot_primitives::{Hash, ASSIGNMENT_KEY_TYPE_ID}; + use polkadot_primitives::{AssignmentId, Hash, ASSIGNMENT_KEY_TYPE_ID}; use sp_application_crypto::sr25519; use sp_core::crypto::Pair as PairT; use sp_keyring::sr25519::Keyring as Sr25519Keyring; @@ -1053,7 +937,7 @@ mod tests { let mut counted = 0; for (core, assignment) in assignments { - let cores = match assignment.cert.kind.clone() { + let cores = match assignment.cert().kind.clone() { AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield } => core_bitfield, AssignmentCertKindV2::RelayVRFModulo { sample: _ } => core.into(), AssignmentCertKindV2::RelayVRFDelay { core_index } => core_index.into(), @@ -1062,7 +946,7 @@ mod tests { let mut mutated = MutatedAssignment { cores: cores.clone(), groups: cores.iter_ones().map(|core| group_for_core(core)).collect(), - cert: assignment.cert, + cert: assignment.into_cert(), own_group: GroupIndex(0), val_index: ValidatorIndex(0), config: config.clone(), diff --git a/polkadot/node/core/approval-voting/src/import.rs b/polkadot/node/core/approval-voting/src/import.rs index 3ddef1e01c45..b163d718eb25 100644 --- a/polkadot/node/core/approval-voting/src/import.rs +++ b/polkadot/node/core/approval-voting/src/import.rs @@ -62,9 +62,10 @@ use crate::{ criteria::{AssignmentCriteria, OurAssignment}, get_extended_session_info, get_session_info, persisted_entries::CandidateEntry, - time::{slot_number_to_tick, Tick}, }; +use polkadot_node_primitives::approval::time::{slot_number_to_tick, Tick}; + use super::{State, LOG_TARGET}; #[derive(Debug)] @@ -574,9 +575,13 @@ pub(crate) async fn handle_new_head( hash: block_hash, number: block_header.number, parent_hash: block_header.parent_hash, - candidates: included_candidates.iter().map(|(hash, _, _, _)| *hash).collect(), + candidates: included_candidates + .iter() + .map(|(hash, _, core_index, group_index)| (*hash, *core_index, *group_index)) + .collect(), slot, session: session_index, + vrf_story: relay_vrf_story, }); imported_candidates.push(BlockImportedCandidates { @@ -609,6 +614,7 @@ pub(crate) mod tests { approval_db::common::{load_block_entry, DbBackend}, RuntimeInfo, RuntimeInfoConfig, MAX_BLOCKS_WITH_ASSIGNMENT_TIMESTAMPS, }; + use approval_types::time::Clock; use assert_matches::assert_matches; use polkadot_node_primitives::{ approval::v1::{VrfSignature, VrfTranscript}, @@ -642,7 +648,7 @@ pub(crate) mod tests { #[derive(Default)] struct MockClock; - impl crate::time::Clock for MockClock { + impl Clock for MockClock { fn tick_now(&self) -> Tick { 42 // chosen by fair dice roll } diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs index d4b6855a44d0..942922cba6df 100644 --- a/polkadot/node/core/approval-voting/src/lib.rs +++ b/polkadot/node/core/approval-voting/src/lib.rs @@ -40,8 +40,9 @@ use polkadot_node_subsystem::{ ApprovalCheckError, ApprovalCheckResult, ApprovalDistributionMessage, ApprovalVotingMessage, AssignmentCheckError, AssignmentCheckResult, AvailabilityRecoveryMessage, BlockDescription, CandidateValidationMessage, ChainApiMessage, - ChainSelectionMessage, DisputeCoordinatorMessage, HighestApprovedAncestorBlock, - RuntimeApiMessage, RuntimeApiRequest, + ChainSelectionMessage, CheckedIndirectAssignment, CheckedIndirectSignedApprovalVote, + DisputeCoordinatorMessage, HighestApprovedAncestorBlock, RuntimeApiMessage, + RuntimeApiRequest, }, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, SubsystemResult, SubsystemSender, @@ -55,9 +56,8 @@ use polkadot_node_subsystem_util::{ }; use polkadot_primitives::{ ApprovalVoteMultipleCandidates, ApprovalVotingParams, BlockNumber, CandidateHash, - CandidateIndex, CandidateReceipt, CoreIndex, DisputeStatement, ExecutorParams, GroupIndex, - Hash, PvfExecKind, SessionIndex, SessionInfo, ValidDisputeStatementKind, ValidatorId, - ValidatorIndex, ValidatorPair, ValidatorSignature, + CandidateIndex, CandidateReceipt, CoreIndex, ExecutorParams, GroupIndex, Hash, PvfExecKind, + SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature, }; use sc_keystore::LocalKeystore; use sp_application_crypto::Pair; @@ -91,9 +91,11 @@ use schnellru::{ByLength, LruMap}; use approval_checking::RequiredTranches; use bitvec::{order::Lsb0, vec::BitVec}; -use criteria::{AssignmentCriteria, RealAssignmentCriteria}; +pub use criteria::{AssignmentCriteria, Config as AssignmentConfig, RealAssignmentCriteria}; use persisted_entries::{ApprovalEntry, BlockEntry, CandidateEntry}; -use time::{slot_number_to_tick, Clock, ClockExt, DelayedApprovalTimer, SystemClock, Tick}; +use polkadot_node_primitives::approval::time::{ + slot_number_to_tick, Clock, ClockExt, DelayedApprovalTimer, SystemClock, Tick, +}; mod approval_checking; pub mod approval_db; @@ -102,7 +104,6 @@ pub mod criteria; mod import; mod ops; mod persisted_entries; -pub mod time; use crate::{ approval_checking::{Check, TranchesToApproveResult}, @@ -123,7 +124,6 @@ const APPROVAL_CHECKING_TIMEOUT: Duration = Duration::from_secs(120); const WAIT_FOR_SIGS_TIMEOUT: Duration = Duration::from_millis(500); const APPROVAL_CACHE_SIZE: u32 = 1024; -const TICK_TOO_FAR_IN_FUTURE: Tick = 20; // 10 seconds. const APPROVAL_DELAY: Tick = 2; pub(crate) const LOG_TARGET: &str = "parachain::approval-voting"; @@ -1607,9 +1607,30 @@ async fn distribution_messages_for_activation( hash: block_hash, number: block_entry.block_number(), parent_hash: block_entry.parent_hash(), - candidates: block_entry.candidates().iter().map(|(_, c_hash)| *c_hash).collect(), + candidates: block_entry + .candidates() + .iter() + .map(|(core_index, c_hash)| { + let candidate = db.load_candidate_entry(c_hash).ok().flatten(); + let group_index = candidate + .and_then(|entry| { + entry.approval_entry(&block_hash).map(|entry| entry.backing_group()) + }) + .unwrap_or_else(|| { + gum::warn!( + target: LOG_TARGET, + ?block_hash, + ?c_hash, + "Missing candidate entry or approval entry", + ); + GroupIndex::default() + }); + (*c_hash, *core_index, group_index) + }) + .collect(), slot: block_entry.slot(), session: block_entry.session(), + vrf_story: block_entry.relay_vrf_story(), }); let mut signatures_queued = HashSet::new(); for (core_index, candidate_hash) in block_entry.candidates() { @@ -1872,35 +1893,45 @@ async fn handle_from_overseer( vec![Action::Conclude] }, FromOrchestra::Communication { msg } => match msg { - ApprovalVotingMessage::CheckAndImportAssignment(a, claimed_cores, res) => { - let (check_outcome, actions) = check_and_import_assignment( + ApprovalVotingMessage::ImportAssignment(checked_assignment, tx) => { + let (check_outcome, actions) = import_assignment( ctx.sender(), state, db, session_info_provider, - a, - claimed_cores, + checked_assignment, ) .await?; - let _ = res.send(check_outcome); - + // approval-distribution makes sure this assignment is valid and expected, + // so this import should never fail, if it does it might mean one of two things, + // there is a bug in the code or the two subsystems got out of sync. + if let AssignmentCheckResult::Bad(ref err) = check_outcome { + gum::debug!(target: LOG_TARGET, ?err, "Unexpected fail when importing an assignment"); + } + let _ = tx.map(|tx| tx.send(check_outcome)); actions }, - ApprovalVotingMessage::CheckAndImportApproval(a, res) => - check_and_import_approval( + ApprovalVotingMessage::ImportApproval(a, tx) => { + let result = import_approval( ctx.sender(), state, db, session_info_provider, metrics, a, - |r| { - let _ = res.send(r); - }, &wakeups, ) - .await? - .0, + .await?; + // approval-distribution makes sure this vote is valid and expected, + // so this import should never fail, if it does it might mean one of two things, + // there is a bug in the code or the two subsystems got out of sync. + if let ApprovalCheckResult::Bad(ref err) = result.1 { + gum::debug!(target: LOG_TARGET, ?err, "Unexpected fail when importing an approval"); + } + let _ = tx.map(|tx| tx.send(result.1)); + + result.0 + }, ApprovalVotingMessage::ApprovedAncestor(target, lower_bound, res) => { let mut approved_ancestor_span = state .spans @@ -2439,29 +2470,30 @@ fn schedule_wakeup_action( maybe_action } -async fn check_and_import_assignment( +async fn import_assignment( sender: &mut Sender, state: &State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, - assignment: IndirectAssignmentCertV2, - candidate_indices: CandidateBitfield, + checked_assignment: CheckedIndirectAssignment, ) -> SubsystemResult<(AssignmentCheckResult, Vec)> where Sender: SubsystemSender, { let tick_now = state.clock.tick_now(); - - let mut check_and_import_assignment_span = state + let assignment = checked_assignment.assignment(); + let candidate_indices = checked_assignment.candidate_indices(); + let tranche = checked_assignment.tranche(); + let mut import_assignment_span = state .spans .get(&assignment.block_hash) - .map(|span| span.child("check-and-import-assignment")) - .unwrap_or_else(|| jaeger::Span::new(assignment.block_hash, "check-and-import-assignment")) + .map(|span| span.child("import-assignment")) + .unwrap_or_else(|| jaeger::Span::new(assignment.block_hash, "import-assignment")) .with_relay_parent(assignment.block_hash) .with_stage(jaeger::Stage::ApprovalChecking); for candidate_index in candidate_indices.iter_ones() { - check_and_import_assignment_span.add_uint_tag("candidate-index", candidate_index as u64); + import_assignment_span.add_uint_tag("candidate-index", candidate_index as u64); } let block_entry = match db.load_block_entry(&assignment.block_hash)? { @@ -2514,8 +2546,6 @@ where )) } - // The Compact VRF modulo assignment cert has multiple core assignments. - let mut backing_groups = Vec::new(); let mut claimed_core_indices = Vec::new(); let mut assigned_candidate_hashes = Vec::new(); @@ -2544,26 +2574,23 @@ where )), // no candidate at core. }; - check_and_import_assignment_span + import_assignment_span .add_string_tag("candidate-hash", format!("{:?}", assigned_candidate_hash)); - check_and_import_assignment_span.add_string_tag( + import_assignment_span.add_string_tag( "traceID", format!("{:?}", jaeger::hash_to_trace_identifier(assigned_candidate_hash.0)), ); - let approval_entry = match candidate_entry.approval_entry_mut(&assignment.block_hash) { - Some(a) => a, - None => - return Ok(( - AssignmentCheckResult::Bad(AssignmentCheckError::Internal( - assignment.block_hash, - assigned_candidate_hash, - )), - Vec::new(), + if candidate_entry.approval_entry_mut(&assignment.block_hash).is_none() { + return Ok(( + AssignmentCheckResult::Bad(AssignmentCheckError::Internal( + assignment.block_hash, + assigned_candidate_hash, )), + Vec::new(), + )); }; - backing_groups.push(approval_entry.backing_group()); claimed_core_indices.push(claimed_core_index); assigned_candidate_hashes.push(assigned_candidate_hash); } @@ -2579,42 +2606,6 @@ where )) } - // Check the assignment certificate. - let res = state.assignment_criteria.check_assignment_cert( - claimed_core_indices - .clone() - .try_into() - .expect("Checked for null assignment above; qed"), - assignment.validator, - &criteria::Config::from(session_info), - block_entry.relay_vrf_story(), - &assignment.cert, - backing_groups, - ); - - let tranche = match res { - Err(crate::criteria::InvalidAssignment(reason)) => - return Ok(( - AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCert( - assignment.validator, - format!("{:?}", reason), - )), - Vec::new(), - )), - Ok(tranche) => { - let current_tranche = - state.clock.tranche_now(state.slot_duration_millis, block_entry.slot()); - - let too_far_in_future = current_tranche + TICK_TOO_FAR_IN_FUTURE as DelayTranche; - - if tranche >= too_far_in_future { - return Ok((AssignmentCheckResult::TooFarInFuture, Vec::new())) - } - - tranche - }, - }; - let mut actions = Vec::new(); let res = { let mut is_duplicate = true; @@ -2647,7 +2638,7 @@ where }; is_duplicate &= approval_entry.is_assigned(assignment.validator); approval_entry.import_assignment(tranche, assignment.validator, tick_now); - check_and_import_assignment_span.add_uint_tag("tranche", tranche as u64); + import_assignment_span.add_uint_tag("tranche", tranche as u64); // We've imported a new assignment, so we need to schedule a wake-up for when that might // no-show. @@ -2704,30 +2695,28 @@ where Ok((res, actions)) } -async fn check_and_import_approval( +async fn import_approval( sender: &mut Sender, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, metrics: &Metrics, - approval: IndirectSignedApprovalVoteV2, - with_response: impl FnOnce(ApprovalCheckResult) -> T, + approval: CheckedIndirectSignedApprovalVote, wakeups: &Wakeups, -) -> SubsystemResult<(Vec, T)> +) -> SubsystemResult<(Vec, ApprovalCheckResult)> where Sender: SubsystemSender, { macro_rules! respond_early { ($e: expr) => {{ - let t = with_response($e); - return Ok((Vec::new(), t)) + return Ok((Vec::new(), $e)) }}; } let mut span = state .spans .get(&approval.block_hash) - .map(|span| span.child("check-and-import-approval")) - .unwrap_or_else(|| jaeger::Span::new(approval.block_hash, "check-and-import-approval")) + .map(|span| span.child("import-approval")) + .unwrap_or_else(|| jaeger::Span::new(approval.block_hash, "import-approval")) .with_string_fmt_debug_tag("candidate-index", approval.candidate_indices.clone()) .with_relay_parent(approval.block_hash) .with_stage(jaeger::Stage::ApprovalChecking); @@ -2774,67 +2763,11 @@ where ), ); - { - let session_info = match get_session_info( - session_info_provider, - sender, - approval.block_hash, - block_entry.session(), - ) - .await - { - Some(s) => s, - None => { - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownSessionIndex( - block_entry.session() - ),)) - }, - }; - - let pubkey = match session_info.validators.get(approval.validator) { - Some(k) => k, - None => respond_early!(ApprovalCheckResult::Bad( - ApprovalCheckError::InvalidValidatorIndex(approval.validator), - )), - }; - - gum::trace!( - target: LOG_TARGET, - "Received approval for num_candidates {:}", - approval.candidate_indices.count_ones() - ); - - let candidate_hashes: Vec = - approved_candidates_info.iter().map(|candidate| candidate.1).collect(); - // Signature check: - match DisputeStatement::Valid( - ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(candidate_hashes.clone()), - ) - .check_signature( - &pubkey, - if let Some(candidate_hash) = candidate_hashes.first() { - *candidate_hash - } else { - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::InvalidValidatorIndex( - approval.validator - ),)) - }, - block_entry.session(), - &approval.signature, - ) { - Err(_) => { - gum::error!( - target: LOG_TARGET, - "Error while checking signature {:}", - approval.candidate_indices.count_ones() - ); - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::InvalidSignature( - approval.validator - ),)) - }, - Ok(()) => {}, - }; - } + gum::trace!( + target: LOG_TARGET, + "Received approval for num_candidates {:}", + approval.candidate_indices.count_ones() + ); let mut actions = Vec::new(); for (approval_candidate_index, approved_candidate_hash) in approved_candidates_info { @@ -2898,9 +2831,7 @@ where } // importing the approval can be heavy as it may trigger acceptance for a series of blocks. - let t = with_response(ApprovalCheckResult::Accepted); - - Ok((actions, t)) + Ok((actions, ApprovalCheckResult::Accepted)) } #[derive(Debug)] diff --git a/polkadot/node/core/approval-voting/src/persisted_entries.rs b/polkadot/node/core/approval-voting/src/persisted_entries.rs index 59a461810051..16e231aa1a2d 100644 --- a/polkadot/node/core/approval-voting/src/persisted_entries.rs +++ b/polkadot/node/core/approval-voting/src/persisted_entries.rs @@ -36,7 +36,9 @@ use std::collections::BTreeMap; use crate::approval_db::v2::Bitfield; -use super::{criteria::OurAssignment, time::Tick}; +use super::criteria::OurAssignment; + +use polkadot_node_primitives::approval::time::Tick; /// Metadata regarding a specific tranche of assignments for a specific candidate. #[derive(Debug, Clone, PartialEq)] diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs index b912449baa4d..7126f209a94f 100644 --- a/polkadot/node/core/approval-voting/src/tests.rs +++ b/polkadot/node/core/approval-voting/src/tests.rs @@ -41,8 +41,9 @@ use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_overseer::HeadSupportsParachains; use polkadot_primitives::{ - ApprovalVote, CandidateCommitments, CandidateEvent, CoreIndex, GroupIndex, Header, - Id as ParaId, IndexedVec, NodeFeatures, ValidationCode, ValidatorSignature, + ApprovalVote, CandidateCommitments, CandidateEvent, CoreIndex, DisputeStatement, GroupIndex, + Header, Id as ParaId, IndexedVec, NodeFeatures, ValidDisputeStatementKind, ValidationCode, + ValidatorSignature, }; use std::{cmp::max, time::Duration}; @@ -139,8 +140,8 @@ impl HeadSupportsParachains for MockSupportsParachains { } } -fn slot_to_tick(t: impl Into) -> crate::time::Tick { - crate::time::slot_number_to_tick(SLOT_DURATION_MILLIS, t.into()) +fn slot_to_tick(t: impl Into) -> Tick { + slot_number_to_tick(SLOT_DURATION_MILLIS, t.into()) } #[derive(Default, Clone)] @@ -647,7 +648,7 @@ fn make_candidate(para_id: ParaId, hash: &Hash) -> CandidateReceipt { r } -async fn check_and_import_approval( +async fn import_approval( overseer: &mut VirtualOverseer, block_hash: Hash, candidate_index: CandidateIndex, @@ -666,14 +667,14 @@ async fn check_and_import_approval( overseer_send( overseer, FromOrchestra::Communication { - msg: ApprovalVotingMessage::CheckAndImportApproval( - IndirectSignedApprovalVoteV2 { + msg: ApprovalVotingMessage::ImportApproval( + CheckedIndirectSignedApprovalVote::from_checked(IndirectSignedApprovalVoteV2 { block_hash, candidate_indices: candidate_index.into(), validator, signature, - }, - tx, + }), + Some(tx), ), }, ) @@ -689,25 +690,31 @@ async fn check_and_import_approval( rx } -async fn check_and_import_assignment( +async fn import_assignment( overseer: &mut VirtualOverseer, block_hash: Hash, candidate_index: CandidateIndex, validator: ValidatorIndex, + tranche: DelayTranche, ) -> oneshot::Receiver { let (tx, rx) = oneshot::channel(); overseer_send( overseer, FromOrchestra::Communication { - msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCertV2 { - block_hash, - validator, - cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }) + msg: ApprovalVotingMessage::ImportAssignment( + CheckedIndirectAssignment::from_checked( + IndirectAssignmentCertV2 { + block_hash, + validator, + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { + sample: 0, + }) .into(), - }, - candidate_index.into(), - tx, + }, + candidate_index.into(), + tranche, + ), + Some(tx), ), }, ) @@ -715,7 +722,7 @@ async fn check_and_import_assignment( rx } -async fn check_and_import_assignment_v2( +async fn import_assignment_v2( overseer: &mut VirtualOverseer, block_hash: Hash, core_indices: Vec, @@ -725,22 +732,27 @@ async fn check_and_import_assignment_v2( overseer_send( overseer, FromOrchestra::Communication { - msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCertV2 { - block_hash, - validator, - cert: garbage_assignment_cert_v2(AssignmentCertKindV2::RelayVRFModuloCompact { - core_bitfield: core_indices - .clone() - .into_iter() - .map(|c| CoreIndex(c)) - .collect::>() - .try_into() - .unwrap(), - }), - }, - core_indices.try_into().unwrap(), - tx, + msg: ApprovalVotingMessage::ImportAssignment( + CheckedIndirectAssignment::from_checked( + IndirectAssignmentCertV2 { + block_hash, + validator, + cert: garbage_assignment_cert_v2( + AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: core_indices + .clone() + .into_iter() + .map(|c| CoreIndex(c)) + .collect::>() + .try_into() + .unwrap(), + }, + ), + }, + core_indices.try_into().unwrap(), + 0, + ), + Some(tx), ), }, ) @@ -1121,26 +1133,18 @@ fn subsystem_rejects_bad_assignment_ok_criteria() { ); builder.build(&mut virtual_overseer).await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted),); // unknown hash let unknown_hash = Hash::repeat_byte(0x02); - let rx = check_and_import_assignment( - &mut virtual_overseer, - unknown_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, unknown_hash, candidate_index, validator, 0) + .await; assert_eq!( rx.await, @@ -1151,59 +1155,6 @@ fn subsystem_rejects_bad_assignment_ok_criteria() { }); } -#[test] -fn subsystem_rejects_bad_assignment_err_criteria() { - let assignment_criteria = Box::new(MockAssignmentCriteria::check_only(move |_| { - Err(criteria::InvalidAssignment( - criteria::InvalidAssignmentReason::ValidatorIndexOutOfBounds, - )) - })); - let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); - test_harness(config, |test_harness| async move { - let TestHarness { mut virtual_overseer, sync_oracle_handle: _sync_oracle_handle, .. } = - test_harness; - assert_matches!( - overseer_recv(&mut virtual_overseer).await, - AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { - rx.send(Ok(0)).unwrap(); - } - ); - - let block_hash = Hash::repeat_byte(0x01); - let candidate_index = 0; - let validator = ValidatorIndex(0); - - let head: Hash = ChainBuilder::GENESIS_HASH; - let mut builder = ChainBuilder::new(); - let slot = Slot::from(1 as u64); - builder.add_block( - block_hash, - head, - 1, - BlockConfig { slot, candidates: None, session_info: None, end_syncing: false }, - ); - builder.build(&mut virtual_overseer).await; - - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; - - assert_eq!( - rx.await, - Ok(AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCert( - ValidatorIndex(0), - "ValidatorIndexOutOfBounds".to_string(), - ))), - ); - - virtual_overseer - }); -} - #[test] fn blank_subsystem_act_on_bad_block() { test_harness(HarnessConfig::default(), |test_harness| async move { @@ -1222,17 +1173,20 @@ fn blank_subsystem_act_on_bad_block() { overseer_send( &mut virtual_overseer, FromOrchestra::Communication { - msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCertV2 { - block_hash: bad_block_hash, - validator: 0u32.into(), - cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { - sample: 0, - }) - .into(), - }, - 0u32.into(), - tx, + msg: ApprovalVotingMessage::ImportAssignment( + CheckedIndirectAssignment::from_checked( + IndirectAssignmentCertV2 { + block_hash: bad_block_hash, + validator: 0u32.into(), + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { + sample: 0, + }) + .into(), + }, + 0u32.into(), + 0, + ), + Some(tx), ), }, ) @@ -1295,7 +1249,7 @@ fn subsystem_rejects_approval_if_no_candidate_entry() { }); let session_index = 1; - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -1336,7 +1290,7 @@ fn subsystem_rejects_approval_if_no_block_entry() { let candidate_hash = dummy_candidate_receipt(block_hash).hash(); let session_index = 1; - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -1401,7 +1355,7 @@ fn subsystem_rejects_approval_before_assignment() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -1424,68 +1378,6 @@ fn subsystem_rejects_approval_before_assignment() { }); } -#[test] -fn subsystem_rejects_assignment_in_future() { - let assignment_criteria = - Box::new(MockAssignmentCriteria::check_only(|_| Ok(TICK_TOO_FAR_IN_FUTURE as _))); - let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); - test_harness(config, |test_harness| async move { - let TestHarness { mut virtual_overseer, clock, sync_oracle_handle: _sync_oracle_handle } = - test_harness; - assert_matches!( - overseer_recv(&mut virtual_overseer).await, - AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { - rx.send(Ok(0)).unwrap(); - } - ); - - let block_hash = Hash::repeat_byte(0x01); - let candidate_index = 0; - let validator = ValidatorIndex(0); - - // Add block hash 00. - ChainBuilder::new() - .add_block( - block_hash, - ChainBuilder::GENESIS_HASH, - 1, - BlockConfig { - slot: Slot::from(0), - candidates: None, - session_info: None, - end_syncing: false, - }, - ) - .build(&mut virtual_overseer) - .await; - - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; - - assert_eq!(rx.await, Ok(AssignmentCheckResult::TooFarInFuture)); - - // Advance clock to make assignment reasonably near. - clock.inner.lock().set_tick(9); - - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; - - assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); - - virtual_overseer - }); -} - #[test] fn subsystem_accepts_duplicate_assignment() { test_harness(HarnessConfig::default(), |test_harness| async move { @@ -1535,7 +1427,7 @@ fn subsystem_accepts_duplicate_assignment() { .await; // Initial assignment. - let rx = check_and_import_assignment_v2( + let rx = import_assignment_v2( &mut virtual_overseer, block_hash, vec![candidate_index1, candidate_index2], @@ -1546,19 +1438,15 @@ fn subsystem_accepts_duplicate_assignment() { assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); // Test with single assigned core. - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::AcceptedDuplicate)); // Test with multiple assigned cores. This cannot happen in practice, as tranche0 // assignments are sent first, but we should still ensure correct behavior. - let rx = check_and_import_assignment_v2( + let rx = import_assignment_v2( &mut virtual_overseer, block_hash, vec![candidate_index1, candidate_index2], @@ -1604,13 +1492,9 @@ fn subsystem_rejects_assignment_with_unknown_candidate() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!( rx.await, @@ -1654,13 +1538,9 @@ fn subsystem_rejects_oversized_bitfields() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!( rx.await, @@ -1669,13 +1549,9 @@ fn subsystem_rejects_oversized_bitfields() { ))), ); - let rx = check_and_import_assignment_v2( - &mut virtual_overseer, - block_hash, - vec![1, 2, 10, 50], - validator, - ) - .await; + let rx = + import_assignment_v2(&mut virtual_overseer, block_hash, vec![1, 2, 10, 50], validator) + .await; assert_eq!( rx.await, @@ -1727,17 +1603,13 @@ fn subsystem_accepts_and_imports_approval_after_assignment() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -1819,19 +1691,15 @@ fn subsystem_second_approval_import_only_schedules_wakeups() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); assert!(clock.inner.lock().current_wakeup_is(APPROVAL_DELAY + 2)); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -1848,7 +1716,7 @@ fn subsystem_second_approval_import_only_schedules_wakeups() { futures_timer::Delay::new(Duration::from_millis(100)).await; assert!(clock.inner.lock().current_wakeup_is(APPROVAL_DELAY + 2)); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -1907,13 +1775,9 @@ fn subsystem_assignment_import_updates_candidate_entry_and_schedules_wakeup() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); @@ -2029,20 +1893,17 @@ fn test_approvals_on_fork_are_always_considered_after_no_show( .await; // Send assignments for the same candidate on both forks - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash_fork, candidate_index, validator, + 0, ) .await; @@ -2072,7 +1933,7 @@ fn test_approvals_on_fork_are_always_considered_after_no_show( futures_timer::Delay::new(Duration::from_millis(100)).await; // Send the approval for candidate just in the context of 0x01 block. - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -2142,13 +2003,9 @@ fn subsystem_process_wakeup_schedules_wakeup() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); @@ -2201,17 +2058,20 @@ fn linear_import_act_on_leaf() { overseer_send( &mut virtual_overseer, FromOrchestra::Communication { - msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCertV2 { - block_hash: head, - validator: 0u32.into(), - cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { - sample: 0, - }) - .into(), - }, - 0u32.into(), - tx, + msg: ApprovalVotingMessage::ImportAssignment( + CheckedIndirectAssignment::from_checked( + IndirectAssignmentCertV2 { + block_hash: head, + validator: 0u32.into(), + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { + sample: 0, + }) + .into(), + }, + 0u32.into(), + 0, + ), + Some(tx), ), }, ) @@ -2272,17 +2132,20 @@ fn forkful_import_at_same_height_act_on_leaf() { overseer_send( &mut virtual_overseer, FromOrchestra::Communication { - msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCertV2 { - block_hash: head, - validator: 0u32.into(), - cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { - sample: 0, - }) - .into(), - }, - 0u32.into(), - tx, + msg: ApprovalVotingMessage::ImportAssignment( + CheckedIndirectAssignment::from_checked( + IndirectAssignmentCertV2 { + block_hash: head, + validator: 0u32.into(), + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { + sample: 0, + }) + .into(), + }, + 0u32.into(), + 0, + ), + Some(tx), ), }, ) @@ -2439,21 +2302,23 @@ fn import_checked_approval_updates_entries_and_schedules() { let candidate_index = 0; - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_a, + 0, ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted),); - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_b, + 0, ) .await; @@ -2462,7 +2327,7 @@ fn import_checked_approval_updates_entries_and_schedules() { let session_index = 1; let sig_a = sign_approval(Sr25519Keyring::Alice, candidate_hash, session_index); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -2489,7 +2354,7 @@ fn import_checked_approval_updates_entries_and_schedules() { clock.inner.lock().wakeup_all(2); let sig_b = sign_approval(Sr25519Keyring::Bob, candidate_hash, session_index); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -2602,13 +2467,9 @@ fn subsystem_import_checked_approval_sets_one_block_bit_at_a_time() { ]; for (candidate_index, validator) in assignments { - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); } @@ -2629,7 +2490,7 @@ fn subsystem_import_checked_approval_sets_one_block_bit_at_a_time() { } else { sign_approval(Sr25519Keyring::Bob, *candidate_hash, session_index) }; - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, *candidate_index, @@ -2887,11 +2748,12 @@ fn approved_ancestor_test( for (i, (block_hash, candidate_hash)) in block_hashes.iter().zip(candidate_hashes).enumerate() { - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, *block_hash, candidate_index, validator, + 0, ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); @@ -2900,7 +2762,7 @@ fn approved_ancestor_test( continue } - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, *block_hash, candidate_index, @@ -3355,7 +3217,8 @@ where F1: 'static + Fn(ValidatorIndex) -> Result + Send - + Sync, + + Sync + + Clone, F2: Fn(Tick) -> bool, { let TriggersAssignmentConfig { @@ -3384,7 +3247,7 @@ where ); assignments }, - assign_validator_tranche, + assign_validator_tranche.clone(), )); let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); let store = config.backend(); @@ -3445,11 +3308,12 @@ where .await; for validator in assignments_to_import { - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, ValidatorIndex(validator), + assign_validator_tranche(ValidatorIndex(validator)).unwrap(), ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); @@ -3458,7 +3322,7 @@ where let n_validators = validators.len(); for (i, &validator_index) in approvals_to_import.iter().enumerate() { let expect_chain_approved = 3 * (i + 1) > n_validators; - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -3763,31 +3627,34 @@ fn pre_covers_dont_stall_approval() { let candidate_index = 0; - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_a, + 0, ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted),); - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_b, + 0, ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted),); - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_c, + 1, ) .await; @@ -3796,7 +3663,7 @@ fn pre_covers_dont_stall_approval() { let session_index = 1; let sig_b = sign_approval(Sr25519Keyring::Bob, candidate_hash, session_index); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -3811,7 +3678,7 @@ fn pre_covers_dont_stall_approval() { assert_eq!(rx.await, Ok(ApprovalCheckResult::Accepted),); let sig_c = sign_approval(Sr25519Keyring::Charlie, candidate_hash, session_index); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -3941,21 +3808,23 @@ fn waits_until_approving_assignments_are_old_enough() { let candidate_index = 0; - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_a, + 0, ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted),); - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_b, + 0, ) .await; @@ -3966,7 +3835,7 @@ fn waits_until_approving_assignments_are_old_enough() { let session_index = 1; let sig_a = sign_approval(Sr25519Keyring::Alice, candidate_hash, session_index); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -3982,7 +3851,7 @@ fn waits_until_approving_assignments_are_old_enough() { let sig_b = sign_approval(Sr25519Keyring::Bob, candidate_hash, session_index); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -4992,7 +4861,6 @@ fn subsystem_sends_pending_approvals_on_approval_restart() { })); } ); - assert_matches!( overseer_recv(&mut virtual_overseer).await, AllMessages::RuntimeApi( diff --git a/polkadot/node/network/approval-distribution/Cargo.toml b/polkadot/node/network/approval-distribution/Cargo.toml index 1bd3d51b5c93..51478dfa4a4f 100644 --- a/polkadot/node/network/approval-distribution/Cargo.toml +++ b/polkadot/node/network/approval-distribution/Cargo.toml @@ -26,6 +26,8 @@ gum = { workspace = true, default-features = true } bitvec = { features = ["alloc"], workspace = true } [dev-dependencies] +sc-keystore = { workspace = true } +sp-application-crypto = { workspace = true, default-features = true } sp-authority-discovery = { workspace = true, default-features = true } sp-core = { features = ["std"], workspace = true, default-features = true } diff --git a/polkadot/node/network/approval-distribution/src/lib.rs b/polkadot/node/network/approval-distribution/src/lib.rs index a1bdc47e9fb2..971b6de5f8f6 100644 --- a/polkadot/node/network/approval-distribution/src/lib.rs +++ b/polkadot/node/network/approval-distribution/src/lib.rs @@ -24,7 +24,7 @@ #![warn(missing_docs)] use self::metrics::Metrics; -use futures::{channel::oneshot, select, FutureExt as _}; +use futures::{select, FutureExt as _}; use itertools::Itertools; use net_protocol::peer_set::{ProtocolVersion, ValidationVersion}; use polkadot_node_jaeger as jaeger; @@ -35,29 +35,41 @@ use polkadot_node_network_protocol::{ v1 as protocol_v1, v2 as protocol_v2, v3 as protocol_v3, PeerId, UnifiedReputationChange as Rep, Versioned, View, }; -use polkadot_node_primitives::approval::{ - v1::{ - AssignmentCertKind, BlockApprovalMeta, IndirectAssignmentCert, IndirectSignedApprovalVote, - }, - v2::{ - AsBitIndex, AssignmentCertKindV2, CandidateBitfield, IndirectAssignmentCertV2, - IndirectSignedApprovalVoteV2, +use polkadot_node_primitives::{ + approval::{ + criteria::{AssignmentCriteria, InvalidAssignment}, + time::{Clock, ClockExt, SystemClock, TICK_TOO_FAR_IN_FUTURE}, + v1::{ + AssignmentCertKind, BlockApprovalMeta, DelayTranche, IndirectAssignmentCert, + IndirectSignedApprovalVote, RelayVRFStory, + }, + v2::{ + AsBitIndex, AssignmentCertKindV2, CandidateBitfield, IndirectAssignmentCertV2, + IndirectSignedApprovalVoteV2, + }, }, + DISPUTE_WINDOW, }; use polkadot_node_subsystem::{ messages::{ - ApprovalCheckResult, ApprovalDistributionMessage, ApprovalVotingMessage, - AssignmentCheckResult, NetworkBridgeEvent, NetworkBridgeTxMessage, + ApprovalDistributionMessage, ApprovalVotingMessage, CheckedIndirectAssignment, + CheckedIndirectSignedApprovalVote, NetworkBridgeEvent, NetworkBridgeTxMessage, + RuntimeApiMessage, }, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, }; -use polkadot_node_subsystem_util::reputation::{ReputationAggregator, REPUTATION_CHANGE_INTERVAL}; +use polkadot_node_subsystem_util::{ + reputation::{ReputationAggregator, REPUTATION_CHANGE_INTERVAL}, + runtime::{Config as RuntimeInfoConfig, ExtendedSessionInfo, RuntimeInfo}, +}; use polkadot_primitives::{ - BlockNumber, CandidateIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, + BlockNumber, CandidateHash, CandidateIndex, CoreIndex, DisputeStatement, GroupIndex, Hash, + SessionIndex, Slot, ValidDisputeStatementKind, ValidatorIndex, ValidatorSignature, }; use rand::{CryptoRng, Rng, SeedableRng}; use std::{ collections::{hash_map, BTreeMap, HashMap, HashSet, VecDeque}, + sync::Arc, time::Duration, }; @@ -86,6 +98,9 @@ const MAX_BITFIELD_SIZE: usize = 500; /// The Approval Distribution subsystem. pub struct ApprovalDistribution { metrics: Metrics, + slot_duration_millis: u64, + clock: Box, + assignment_criteria: Arc, } /// Contains recently finalized @@ -354,6 +369,9 @@ pub struct State { /// Aggregated reputation change reputation: ReputationAggregator, + + /// Slot duration in millis + slot_duration_millis: u64, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -488,11 +506,17 @@ struct BlockEntry { knowledge: Knowledge, /// A votes entry for each candidate indexed by [`CandidateIndex`]. candidates: Vec, + /// Information about candidate metadata. + candidates_metadata: Vec<(CandidateHash, CoreIndex, GroupIndex)>, /// The session index of this block. session: SessionIndex, /// Approval entries for whole block. These also contain all approvals in the case of multiple /// candidates being claimed by assignments. approval_entries: HashMap<(ValidatorIndex, CandidateBitfield), ApprovalEntry>, + /// The block vrf story. + vrf_story: RelayVRFStory, + /// The block slot. + slot: Slot, } impl BlockEntry { @@ -646,6 +670,41 @@ enum MessageSource { Local, } +// Encountered error while validating an assignment. +#[derive(Debug)] +enum InvalidAssignmentError { + // The vrf check for the assignment failed. + #[allow(dead_code)] + CryptoCheckFailed(InvalidAssignment), + // The assignment did not claim any valid candidate. + NoClaimedCandidates, + // Claimed invalid candidate. + #[allow(dead_code)] + ClaimedInvalidCandidateIndex { + claimed_index: usize, + max_index: usize, + }, + // The assignment claimes more candidates than the maximum allowed. + OversizedClaimedBitfield, + // `SessionInfo` was not found for the block hash in the assignment. + #[allow(dead_code)] + SessionInfoNotFound(polkadot_node_subsystem_util::runtime::Error), +} + +// Encountered error while validating an approval. +#[derive(Debug)] +enum InvalidVoteError { + // The candidate index was out of bounds. + CandidateIndexOutOfBounds, + // The validator index was out of bounds. + ValidatorIndexOutOfBounds, + // The signature of the vote was invalid. + InvalidSignature, + // `SessionInfo` was not found for the block hash in the approval. + #[allow(dead_code)] + SessionInfoNotFound(polkadot_node_subsystem_util::runtime::Error), +} + impl MessageSource { fn peer_id(&self) -> Option { match self { @@ -662,16 +721,26 @@ enum PendingMessage { #[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] impl State { + /// Build State with specified slot duration. + pub fn with_config(slot_duration_millis: u64) -> Self { + Self { slot_duration_millis, ..Default::default() } + } + async fn handle_network_msg< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, event: NetworkBridgeEvent, rng: &mut (impl CryptoRng + Rng), + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) { match event { NetworkBridgeEvent::PeerConnected(peer_id, role, version, authority_ids) => { @@ -727,10 +796,14 @@ impl State { self.process_incoming_peer_message( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, message, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -776,16 +849,28 @@ impl State { async fn handle_new_blocks< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, metas: Vec, rng: &mut (impl CryptoRng + Rng), + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) { let mut new_hashes = HashSet::new(); - for meta in &metas { + + gum::debug!( + target: LOG_TARGET, + "Got new blocks {:?}", + metas.iter().map(|m| (m.hash, m.number)).collect::>(), + ); + + for meta in metas { let mut span = self .spans .get(&meta.hash) @@ -809,6 +894,9 @@ impl State { candidates, session: meta.session, approval_entries: HashMap::new(), + candidates_metadata: meta.candidates, + vrf_story: meta.vrf_story, + slot: meta.slot, }); self.topologies.inc_session_refs(meta.session); @@ -823,12 +911,6 @@ impl State { } } - gum::debug!( - target: LOG_TARGET, - "Got new blocks {:?}", - metas.iter().map(|m| (m.hash, m.number)).collect::>(), - ); - { for (peer_id, PeerEntry { view, version }) in self.peer_views.iter() { let intersection = view.iter().filter(|h| new_hashes.contains(h)); @@ -883,11 +965,15 @@ impl State { self.import_and_circulate_assignment( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Peer(peer_id), assignment, claimed_indices, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -895,9 +981,11 @@ impl State { self.import_and_circulate_approval( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Peer(peer_id), approval_vote, + session_info_provider, ) .await; }, @@ -943,17 +1031,22 @@ impl State { .await; } - async fn process_incoming_assignments( + async fn process_incoming_assignments( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, peer_id: PeerId, assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)>, rng: &mut R, + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) where A: overseer::SubsystemSender, N: overseer::SubsystemSender, + RA: overseer::SubsystemSender, R: CryptoRng + Rng, { for (assignment, claimed_indices) in assignments { @@ -978,11 +1071,15 @@ impl State { self.import_and_circulate_assignment( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Peer(peer_id), assignment, claimed_indices, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; } @@ -992,13 +1089,16 @@ impl State { async fn process_incoming_approvals< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, peer_id: PeerId, approvals: Vec, + session_info_provider: &mut RuntimeInfo, ) { gum::trace!( target: LOG_TARGET, @@ -1028,18 +1128,21 @@ impl State { self.import_and_circulate_approval( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Peer(peer_id), approval_vote, + session_info_provider, ) .await; } } - async fn process_incoming_peer_message( + async fn process_incoming_peer_message( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, peer_id: PeerId, msg: Versioned< @@ -1048,9 +1151,13 @@ impl State { protocol_v3::ApprovalDistributionMessage, >, rng: &mut R, + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) where A: overseer::SubsystemSender, N: overseer::SubsystemSender, + RA: overseer::SubsystemSender, R: CryptoRng + Rng, { match msg { @@ -1067,10 +1174,14 @@ impl State { self.process_incoming_assignments( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, sanitized_assignments, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -1089,10 +1200,14 @@ impl State { self.process_incoming_assignments( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, sanitized_assignments, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -1102,9 +1217,11 @@ impl State { self.process_incoming_approvals( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, sanitized_approvals, + session_info_provider, ) .await; }, @@ -1115,9 +1232,11 @@ impl State { self.process_incoming_approvals( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, sanitized_approvals, + session_info_provider, ) .await; }, @@ -1218,18 +1337,23 @@ impl State { self.enable_aggression(network_sender, Resend::No, metrics).await; } - async fn import_and_circulate_assignment( + async fn import_and_circulate_assignment( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, source: MessageSource, assignment: IndirectAssignmentCertV2, claimed_candidate_indices: CandidateBitfield, rng: &mut R, + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) where A: overseer::SubsystemSender, N: overseer::SubsystemSender, + RA: overseer::SubsystemSender, R: CryptoRng + Rng, { let _span = self @@ -1355,35 +1479,47 @@ impl State { return } - let (tx, rx) = oneshot::channel(); + let result = Self::check_assignment_valid( + assignment_criteria, + &entry, + &assignment, + &claimed_candidate_indices, + session_info_provider, + runtime_api_sender, + ) + .await; - approval_voting_sender - .send_message(ApprovalVotingMessage::CheckAndImportAssignment( - assignment.clone(), - claimed_candidate_indices.clone(), - tx, - )) - .await; + match result { + Ok(checked_assignment) => { + let current_tranche = clock.tranche_now(self.slot_duration_millis, entry.slot); + let too_far_in_future = + current_tranche + TICK_TOO_FAR_IN_FUTURE as DelayTranche; - let timer = metrics.time_awaiting_approval_voting(); - let result = match rx.await { - Ok(result) => result, - Err(_) => { - gum::debug!(target: LOG_TARGET, "The approval voting subsystem is down"); - return - }, - }; - drop(timer); + if checked_assignment.tranche() >= too_far_in_future { + gum::debug!( + target: LOG_TARGET, + hash = ?block_hash, + ?peer_id, + "Got an assignment too far in the future", + ); + modify_reputation( + &mut self.reputation, + network_sender, + peer_id, + COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE, + ) + .await; + metrics.on_assignment_far(); - gum::trace!( - target: LOG_TARGET, - ?source, - ?message_subject, - ?result, - "Checked assignment", - ); - match result { - AssignmentCheckResult::Accepted => { + return + } + + approval_voting_sender + .send_message(ApprovalVotingMessage::ImportAssignment( + checked_assignment, + None, + )) + .await; modify_reputation( &mut self.reputation, network_sender, @@ -1396,47 +1532,12 @@ impl State { peer_knowledge.received.insert(message_subject.clone(), message_kind); } }, - AssignmentCheckResult::AcceptedDuplicate => { - // "duplicate" assignments aren't necessarily equal. - // There is more than one way each validator can be assigned to each core. - // cf. https://github.com/paritytech/polkadot/pull/2160#discussion_r557628699 - if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { - peer_knowledge.received.insert(message_subject.clone(), message_kind); - } - gum::debug!( - target: LOG_TARGET, - hash = ?block_hash, - ?peer_id, - "Got an `AcceptedDuplicate` assignment", - ); - metrics.on_assignment_duplicatevoting(); - - return - }, - AssignmentCheckResult::TooFarInFuture => { - gum::debug!( - target: LOG_TARGET, - hash = ?block_hash, - ?peer_id, - "Got an assignment too far in the future", - ); - modify_reputation( - &mut self.reputation, - network_sender, - peer_id, - COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE, - ) - .await; - metrics.on_assignment_far(); - - return - }, - AssignmentCheckResult::Bad(error) => { + Err(error) => { gum::info!( target: LOG_TARGET, hash = ?block_hash, ?peer_id, - %error, + ?error, "Got a bad assignment from peer", ); modify_reputation( @@ -1577,6 +1678,64 @@ impl State { } } + async fn check_assignment_valid>( + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + entry: &BlockEntry, + assignment: &IndirectAssignmentCertV2, + claimed_candidate_indices: &CandidateBitfield, + runtime_info: &mut RuntimeInfo, + runtime_api_sender: &mut RA, + ) -> Result { + let ExtendedSessionInfo { ref session_info, .. } = runtime_info + .get_session_info_by_index(runtime_api_sender, assignment.block_hash, entry.session) + .await + .map_err(|err| InvalidAssignmentError::SessionInfoNotFound(err))?; + + if claimed_candidate_indices.len() > session_info.n_cores as usize { + return Err(InvalidAssignmentError::OversizedClaimedBitfield) + } + + let claimed_cores: Vec = claimed_candidate_indices + .iter_ones() + .map(|candidate_index| { + entry.candidates_metadata.get(candidate_index).map(|(_, core, _)| *core).ok_or( + InvalidAssignmentError::ClaimedInvalidCandidateIndex { + claimed_index: candidate_index, + max_index: entry.candidates_metadata.len(), + }, + ) + }) + .collect::, InvalidAssignmentError>>()?; + + let Ok(claimed_cores) = claimed_cores.try_into() else { + return Err(InvalidAssignmentError::NoClaimedCandidates) + }; + + let backing_groups = claimed_candidate_indices + .iter_ones() + .flat_map(|candidate_index| { + entry.candidates_metadata.get(candidate_index).map(|(_, _, group)| *group) + }) + .collect::>(); + + assignment_criteria + .check_assignment_cert( + claimed_cores, + assignment.validator, + &polkadot_node_primitives::approval::criteria::Config::from(session_info), + entry.vrf_story.clone(), + &assignment.cert, + backing_groups, + ) + .map_err(|err| InvalidAssignmentError::CryptoCheckFailed(err)) + .map(|tranche| { + CheckedIndirectAssignment::from_checked( + assignment.clone(), + claimed_candidate_indices.clone(), + tranche, + ) + }) + } // Checks if an approval can be processed. // Returns true if we can continue with processing the approval and false otherwise. async fn check_approval_can_be_processed< @@ -1666,13 +1825,16 @@ impl State { async fn import_and_circulate_approval< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, source: MessageSource, vote: IndirectSignedApprovalVoteV2, + session_info_provider: &mut RuntimeInfo, ) { let _span = self .spans @@ -1740,31 +1902,16 @@ impl State { return } - let (tx, rx) = oneshot::channel(); - - approval_voting_sender - .send_message(ApprovalVotingMessage::CheckAndImportApproval(vote.clone(), tx)) - .await; - - let timer = metrics.time_awaiting_approval_voting(); - let result = match rx.await { - Ok(result) => result, - Err(_) => { - gum::debug!(target: LOG_TARGET, "The approval voting subsystem is down"); - return - }, - }; - drop(timer); + let result = + Self::check_vote_valid(&vote, &entry, session_info_provider, runtime_api_sender) + .await; - gum::trace!( - target: LOG_TARGET, - ?peer_id, - ?result, - ?vote, - "Checked approval", - ); match result { - ApprovalCheckResult::Accepted => { + Ok(vote) => { + approval_voting_sender + .send_message(ApprovalVotingMessage::ImportApproval(vote, None)) + .await; + modify_reputation( &mut self.reputation, network_sender, @@ -1782,7 +1929,7 @@ impl State { .insert(approval_knwowledge_key.0.clone(), approval_knwowledge_key.1); } }, - ApprovalCheckResult::Bad(error) => { + Err(err) => { modify_reputation( &mut self.reputation, network_sender, @@ -1790,10 +1937,11 @@ impl State { COST_INVALID_MESSAGE, ) .await; + gum::info!( target: LOG_TARGET, ?peer_id, - %error, + ?err, "Got a bad approval from peer", ); metrics.on_approval_bad(); @@ -1891,6 +2039,50 @@ impl State { } } + // Checks if the approval vote is valid. + async fn check_vote_valid>( + vote: &IndirectSignedApprovalVoteV2, + entry: &BlockEntry, + runtime_info: &mut RuntimeInfo, + runtime_api_sender: &mut RA, + ) -> Result { + if vote.candidate_indices.len() > entry.candidates_metadata.len() { + return Err(InvalidVoteError::CandidateIndexOutOfBounds) + } + + let candidate_hashes = vote + .candidate_indices + .iter_ones() + .flat_map(|candidate_index| { + entry + .candidates_metadata + .get(candidate_index) + .map(|(candidate_hash, _, _)| *candidate_hash) + }) + .collect::>(); + + let ExtendedSessionInfo { ref session_info, .. } = runtime_info + .get_session_info_by_index(runtime_api_sender, vote.block_hash, entry.session) + .await + .map_err(|err| InvalidVoteError::SessionInfoNotFound(err))?; + + let pubkey = session_info + .validators + .get(vote.validator) + .ok_or(InvalidVoteError::ValidatorIndexOutOfBounds)?; + DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates( + candidate_hashes.clone(), + )) + .check_signature( + &pubkey, + *candidate_hashes.first().unwrap(), + entry.session, + &vote.signature, + ) + .map_err(|_| InvalidVoteError::InvalidSignature) + .map(|_| CheckedIndirectSignedApprovalVote::from_checked(vote.clone())) + } + /// Retrieve approval signatures from state for the given relay block/indices: fn get_approval_signatures( &mut self, @@ -2468,16 +2660,48 @@ async fn modify_reputation( #[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] impl ApprovalDistribution { /// Create a new instance of the [`ApprovalDistribution`] subsystem. - pub fn new(metrics: Metrics) -> Self { - Self { metrics } + pub fn new( + metrics: Metrics, + slot_duration_millis: u64, + assignment_criteria: Arc, + ) -> Self { + Self::new_with_clock( + metrics, + slot_duration_millis, + Box::new(SystemClock), + assignment_criteria, + ) + } + + /// Create a new instance of the [`ApprovalDistribution`] subsystem, with a custom clock. + pub fn new_with_clock( + metrics: Metrics, + slot_duration_millis: u64, + clock: Box, + assignment_criteria: Arc, + ) -> Self { + Self { metrics, slot_duration_millis, clock, assignment_criteria } } async fn run(self, ctx: Context) { - let mut state = State::default(); + let mut state = + State { slot_duration_millis: self.slot_duration_millis, ..Default::default() }; // According to the docs of `rand`, this is a ChaCha12 RNG in practice // and will always be chosen for strong performance and security properties. let mut rng = rand::rngs::StdRng::from_entropy(); - self.run_inner(ctx, &mut state, REPUTATION_CHANGE_INTERVAL, &mut rng).await + let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { + keystore: None, + session_cache_lru_size: DISPUTE_WINDOW.get(), + }); + + self.run_inner( + ctx, + &mut state, + REPUTATION_CHANGE_INTERVAL, + &mut rng, + &mut session_info_provider, + ) + .await } /// Used for testing. @@ -2487,11 +2711,14 @@ impl ApprovalDistribution { state: &mut State, reputation_interval: Duration, rng: &mut (impl CryptoRng + Rng), + session_info_provider: &mut RuntimeInfo, ) { let new_reputation_delay = || futures_timer::Delay::new(reputation_interval).fuse(); let mut reputation_delay = new_reputation_delay(); let mut approval_voting_sender = ctx.sender().clone(); let mut network_sender = ctx.sender().clone(); + let mut runtime_api_sender = ctx.sender().clone(); + loop { select! { _ = reputation_delay => { @@ -2507,8 +2734,7 @@ impl ApprovalDistribution { }, }; - - if self.handle_from_orchestra(message, &mut approval_voting_sender, &mut network_sender, state, rng).await { + if self.handle_from_orchestra(message, &mut approval_voting_sender, &mut network_sender, &mut runtime_api_sender, state, rng, session_info_provider).await { return; } @@ -2523,23 +2749,30 @@ impl ApprovalDistribution { pub async fn handle_from_orchestra< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &self, message: FromOrchestra, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, state: &mut State, rng: &mut (impl CryptoRng + Rng), + session_info_provider: &mut RuntimeInfo, ) -> bool { match message { FromOrchestra::Communication { msg } => Self::handle_incoming( approval_voting_sender, network_sender, + runtime_api_sender, state, msg, &self.metrics, rng, + self.assignment_criteria.as_ref(), + self.clock.as_ref(), + session_info_provider, ) .await, FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)) => { @@ -2567,23 +2800,48 @@ impl ApprovalDistribution { async fn handle_incoming< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, state: &mut State, msg: ApprovalDistributionMessage, metrics: &Metrics, rng: &mut (impl CryptoRng + Rng), + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) { match msg { ApprovalDistributionMessage::NetworkBridgeUpdate(event) => { state - .handle_network_msg(approval_voting_sender, network_sender, metrics, event, rng) + .handle_network_msg( + approval_voting_sender, + network_sender, + runtime_api_sender, + metrics, + event, + rng, + assignment_criteria, + clock, + session_info_provider, + ) .await; }, ApprovalDistributionMessage::NewBlocks(metas) => { state - .handle_new_blocks(approval_voting_sender, network_sender, metrics, metas, rng) + .handle_new_blocks( + approval_voting_sender, + network_sender, + runtime_api_sender, + metrics, + metas, + rng, + assignment_criteria, + clock, + session_info_provider, + ) .await; }, ApprovalDistributionMessage::DistributeAssignment(cert, candidate_indices) => { @@ -2607,11 +2865,15 @@ impl ApprovalDistribution { .import_and_circulate_assignment( approval_voting_sender, network_sender, + runtime_api_sender, &metrics, MessageSource::Local, cert, candidate_indices, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -2627,9 +2889,11 @@ impl ApprovalDistribution { .import_and_circulate_approval( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Local, vote, + session_info_provider, ) .await; }, diff --git a/polkadot/node/network/approval-distribution/src/metrics.rs b/polkadot/node/network/approval-distribution/src/metrics.rs index 60c7f2f6d3b8..10553c352966 100644 --- a/polkadot/node/network/approval-distribution/src/metrics.rs +++ b/polkadot/node/network/approval-distribution/src/metrics.rs @@ -30,7 +30,6 @@ struct MetricsInner { aggression_l2_messages_total: prometheus::Counter, time_unify_with_peer: prometheus::Histogram, time_import_pending_now_known: prometheus::Histogram, - time_awaiting_approval_voting: prometheus::Histogram, assignments_received_result: prometheus::CounterVec, approvals_received_result: prometheus::CounterVec, } @@ -206,14 +205,6 @@ impl Metrics { } } - pub(crate) fn time_awaiting_approval_voting( - &self, - ) -> Option { - self.0 - .as_ref() - .map(|metrics| metrics.time_awaiting_approval_voting.start_timer()) - } - pub(crate) fn on_aggression_l1(&self) { if let Some(metrics) = &self.0 { metrics.aggression_l1_messages_total.inc(); @@ -288,13 +279,6 @@ impl MetricsTrait for Metrics { ).buckets(vec![0.0001, 0.0004, 0.0016, 0.0064, 0.0256, 0.1024, 0.4096, 1.6384, 3.2768, 4.9152, 6.5536,]))?, registry, )?, - time_awaiting_approval_voting: prometheus::register( - prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_time_awaiting_approval_voting", - "Time spent awaiting a reply from the Approval Voting Subsystem.", - ).buckets(vec![0.0001, 0.0004, 0.0016, 0.0064, 0.0256, 0.1024, 0.4096, 1.6384, 3.2768, 4.9152, 6.5536,]))?, - registry, - )?, assignments_received_result: prometheus::register( prometheus::CounterVec::new( prometheus::Opts::new( diff --git a/polkadot/node/network/approval-distribution/src/tests.rs b/polkadot/node/network/approval-distribution/src/tests.rs index 1ca571721ea9..4ee9320e0e45 100644 --- a/polkadot/node/network/approval-distribution/src/tests.rs +++ b/polkadot/node/network/approval-distribution/src/tests.rs @@ -16,7 +16,7 @@ use super::*; use assert_matches::assert_matches; -use futures::{executor, future, Future}; +use futures::{channel::oneshot, executor, future, Future}; use polkadot_node_network_protocol::{ grid_topology::{SessionGridTopology, TopologyPeerInfo}, our_view, @@ -24,6 +24,7 @@ use polkadot_node_network_protocol::{ view, ObservedRole, }; use polkadot_node_primitives::approval::{ + criteria, v1::{ AssignmentCert, AssignmentCertKind, IndirectAssignmentCert, IndirectSignedApprovalVote, VrfPreOutput, VrfProof, VrfSignature, @@ -34,12 +35,17 @@ use polkadot_node_primitives::approval::{ }, }; use polkadot_node_subsystem::messages::{ - network_bridge_event, AllMessages, ApprovalCheckError, ReportPeerMessage, + network_bridge_event, AllMessages, ReportPeerMessage, RuntimeApiRequest, }; use polkadot_node_subsystem_util::{reputation::add_reputation, TimeoutExt as _}; -use polkadot_primitives::{AuthorityDiscoveryId, BlakeTwo256, CoreIndex, HashT}; +use polkadot_primitives::{ + ApprovalVoteMultipleCandidates, AuthorityDiscoveryId, BlakeTwo256, CoreIndex, ExecutorParams, + HashT, NodeFeatures, SessionInfo, ValidatorId, +}; use polkadot_primitives_test_helpers::dummy_signature; use rand::SeedableRng; +use sc_keystore::{Keystore, LocalKeystore}; +use sp_application_crypto::AppCrypto; use sp_authority_discovery::AuthorityPair as AuthorityDiscoveryPair; use sp_core::crypto::Pair as PairT; use std::time::Duration; @@ -47,6 +53,8 @@ type VirtualOverseer = polkadot_node_subsystem_test_helpers::TestSubsystemContextHandle; fn test_harness>( + assignment_criteria: Arc, + clock: Box, mut state: State, test_fn: impl FnOnce(VirtualOverseer) -> T, ) -> State { @@ -56,13 +64,29 @@ fn test_harness>( let (context, virtual_overseer) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool.clone()); - let subsystem = ApprovalDistribution::new(Default::default()); + let subsystem = ApprovalDistribution::new_with_clock( + Metrics::default(), + Default::default(), + clock, + assignment_criteria, + ); { let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(12345); + let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { + keystore: None, + session_cache_lru_size: DISPUTE_WINDOW.get(), + }); + let (tx, rx) = oneshot::channel(); let subsystem = async { subsystem - .run_inner(context, &mut state, REPUTATION_CHANGE_TEST_INTERVAL, &mut rng) + .run_inner( + context, + &mut state, + REPUTATION_CHANGE_TEST_INTERVAL, + &mut rng, + &mut session_info_provider, + ) .await; tx.send(()).expect("Fail to notify subystem is done"); }; @@ -121,6 +145,41 @@ async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages { msg } +async fn provide_session(virtual_overseer: &mut VirtualOverseer, session_info: SessionInfo) { + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request( + _, + RuntimeApiRequest::SessionInfo(_, si_tx), + ) + ) => { + si_tx.send(Ok(Some(session_info.clone()))).unwrap(); + } + ); + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request( + _, + RuntimeApiRequest::SessionExecutorParams(_, si_tx), + ) + ) => { + // Make sure all SessionExecutorParams calls are not made for the leaf (but for its relay parent) + si_tx.send(Ok(Some(ExecutorParams::default()))).unwrap(); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(_, RuntimeApiRequest::NodeFeatures(_, si_tx), ) + ) => { + si_tx.send(Ok(NodeFeatures::EMPTY)).unwrap(); + } + ); +} + fn make_peers_and_authority_ids(n: usize) -> Vec<(PeerId, AuthorityDiscoveryId)> { (0..n) .map(|_| { @@ -335,6 +394,30 @@ fn fake_assignment_cert_v2( } } +fn fake_assignment_cert_delay( + block_hash: Hash, + validator: ValidatorIndex, + core_bitfield: CoreBitfield, +) -> IndirectAssignmentCertV2 { + let ctx = schnorrkel::signing_context(RELAY_VRF_MODULO_CONTEXT); + let msg = b"WhenParachains?"; + let mut prng = rand_core::OsRng; + let keypair = schnorrkel::Keypair::generate_with(&mut prng); + let (inout, proof, _) = keypair.vrf_sign(ctx.bytes(msg)); + let preout = inout.to_preout(); + + IndirectAssignmentCertV2 { + block_hash, + validator, + cert: AssignmentCertV2 { + kind: AssignmentCertKindV2::RelayVRFDelay { + core_index: CoreIndex(core_bitfield.iter_ones().next().unwrap() as u32), + }, + vrf: VrfSignature { pre_output: VrfPreOutput(preout), proof: VrfProof(proof) }, + }, + } +} + async fn expect_reputation_change( virtual_overseer: &mut VirtualOverseer, peer_id: &PeerId, @@ -378,6 +461,85 @@ fn state_with_reputation_delay() -> State { State { reputation: ReputationAggregator::new(|_| false), ..Default::default() } } +fn dummy_session_info_valid( + index: SessionIndex, + keystore: &mut LocalKeystore, + num_validators: usize, +) -> SessionInfo { + let keys = (0..num_validators) + .map(|_| { + keystore + .sr25519_generate_new(ValidatorId::ID, Some("//Node")) + .expect("Insert key into keystore") + }) + .collect_vec(); + + SessionInfo { + validators: keys.clone().into_iter().map(|key| key.into()).collect(), + discovery_keys: keys.clone().into_iter().map(|key| key.into()).collect(), + assignment_keys: keys.clone().into_iter().map(|key| key.into()).collect(), + validator_groups: Default::default(), + n_cores: 20, + zeroth_delay_tranche_width: index as _, + relay_vrf_modulo_samples: index as _, + n_delay_tranches: index as _, + no_show_slots: index as _, + needed_approvals: index as _, + active_validator_indices: Vec::new(), + dispute_period: 6, + random_seed: [0u8; 32], + } +} + +fn signature_for( + keystore: &LocalKeystore, + session: &SessionInfo, + candidate_hashes: Vec, + validator_index: ValidatorIndex, +) -> ValidatorSignature { + let payload = ApprovalVoteMultipleCandidates(&candidate_hashes).signing_payload(1); + let sign_key = session.validators.get(validator_index).unwrap().clone(); + let signature = keystore + .sr25519_sign(ValidatorId::ID, &sign_key.into(), &payload[..]) + .unwrap() + .unwrap(); + signature.into() +} + +struct MockAssignmentCriteria { + tranche: + Result, +} + +impl AssignmentCriteria for MockAssignmentCriteria { + fn compute_assignments( + &self, + _keystore: &LocalKeystore, + _relay_vrf_story: polkadot_node_primitives::approval::v1::RelayVRFStory, + _config: &criteria::Config, + _leaving_cores: Vec<( + CandidateHash, + polkadot_primitives::CoreIndex, + polkadot_primitives::GroupIndex, + )>, + _enable_assignments_v2: bool, + ) -> HashMap { + HashMap::new() + } + + fn check_assignment_cert( + &self, + _claimed_core_bitfield: polkadot_node_primitives::approval::v2::CoreBitfield, + _validator_index: polkadot_primitives::ValidatorIndex, + _config: &criteria::Config, + _relay_vrf_story: polkadot_node_primitives::approval::v1::RelayVRFStory, + _assignment: &polkadot_node_primitives::approval::v2::AssignmentCertV2, + _backing_groups: Vec, + ) -> Result { + self.tranche + } +} + /// import an assignment /// connect a new peer /// the new peer sends us the same assignment @@ -391,89 +553,98 @@ fn try_import_the_same_assignment() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; - - // Set up a gossip topology, where a, b, c and d are topology neighbors to the node under - // testing. - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), 0u32)]; - // send the assignment related to `hash` - let validator_index = ValidatorIndex(0); - let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), 0u32)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, &peer_a, msg).await; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, &peer_a, msg).await; + expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &0u32.into()); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); - expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; - // send an `Accept` message from the Approval Voting subsystem - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_indices, - tx, - )) => { - assert_eq!(claimed_indices, 0u32.into()); - assert_eq!(assignment, cert.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 2); - assert_eq!(assignments.len(), 1); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert_eq!(assignments.len(), 1); + } + ); - // setup new peer with V2 - setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::V3).await; + // setup new peer with V2 + setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::V3).await; - // send the same assignment from peer_d - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer(overseer, &peer_d, msg).await; + // send the same assignment from peer_d + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); + send_message_from_peer(overseer, &peer_d, msg).await; - expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } /// Just like `try_import_the_same_assignment` but use `VRFModuloCompact` assignments for multiple @@ -488,97 +659,106 @@ fn try_import_the_same_assignment_v2() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; - - // Set up a gossip topology, where a, b, c and d are topology neighbors to the node under - // testing. - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // send the assignment related to `hash` - let validator_index = ValidatorIndex(0); - let cores = vec![1, 2, 3, 4]; - let core_bitfield: CoreBitfield = cores - .iter() - .map(|index| CoreIndex(*index)) - .collect::>() - .try_into() - .unwrap(); - - let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield.clone()); - let assignments = vec![(cert.clone(), cores.clone().try_into().unwrap())]; - - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer_v3(overseer, &peer_a, msg).await; - - expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; - - // send an `Accept` message from the Approval Voting subsystem - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_indices, - tx, - )) => { - assert_eq!(claimed_indices, cores.try_into().unwrap()); - assert_eq!(assignment, cert.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 2); - assert_eq!(assignments.len(), 1); - } - ); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 5], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cores = vec![1, 2, 3, 4]; + let core_bitfield: CoreBitfield = cores + .iter() + .map(|index| CoreIndex(*index)) + .collect::>() + .try_into() + .unwrap(); + + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield.clone()); + let assignments = vec![(cert.clone(), cores.clone().try_into().unwrap())]; + + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + + expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &cores.try_into().unwrap()); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert_eq!(assignments.len(), 1); + } + ); - // setup new peer - setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::V3).await; + // setup new peer + setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::V3).await; - // send the same assignment from peer_d - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer_v3(overseer, &peer_d, msg).await; + // send the same assignment from peer_d + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments); + send_message_from_peer_v3(overseer, &peer_d, msg).await; - expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } /// import an assignment @@ -590,55 +770,65 @@ fn delay_reputation_change() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); - let _ = test_harness(state_with_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - - // Setup peers - setup_peer_with_view(overseer, &peer, view![], ValidationVersion::V1).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // send the assignment related to `hash` - let validator_index = ValidatorIndex(0); - let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), 0u32)]; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_with_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Setup peers + setup_peer_with_view(overseer, &peer, view![], ValidationVersion::V1).await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), 0u32)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, &peer, msg).await; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, &peer, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; - // send an `Accept` message from the Approval Voting subsystem - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_candidates, - tx, - )) => { - assert_eq!(assignment.cert, cert.cert.into()); - assert_eq!(claimed_candidates, vec![0u32].try_into().unwrap()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_changes( - overseer, - &peer, - vec![COST_UNEXPECTED_MESSAGE, BENEFIT_VALID_MESSAGE_FIRST], - ) - .await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.assignment().cert, cert.cert.into()); + assert_eq!(assignment.candidate_indices(), &vec![0u32].try_into().unwrap()); + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_changes( + overseer, + &peer, + vec![COST_UNEXPECTED_MESSAGE, BENEFIT_VALID_MESSAGE_FIRST], + ) + .await; + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + virtual_overseer + }, + ); } /// @@ -653,77 +843,88 @@ fn spam_attack_results_in_negative_reputation_change() { let peer_a = PeerId::random(); let hash_b = Hash::repeat_byte(0xBB); - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - let peer = &peer_a; - setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; - - // new block `hash_b` with 20 candidates - let candidates_count = 20; - let meta = BlockApprovalMeta { - hash: hash_b, - parent_hash, - number: 2, - candidates: vec![Default::default(); candidates_count], - slot: 1.into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // send 20 assignments related to `hash_b` - // to populate our knowledge - let assignments: Vec<_> = (0..candidates_count) - .map(|candidate_index| { - let validator_index = ValidatorIndex(candidate_index as u32); - let cert = fake_assignment_cert(hash_b, validator_index); - (cert, candidate_index as u32) - }) - .collect(); - - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, peer, msg.clone()).await; - - for i in 0..candidates_count { - expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + let peer = &peer_a; + setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; + + // new block `hash_b` with 20 candidates + let candidates_count = 20; + let meta = BlockApprovalMeta { + hash: hash_b, + parent_hash, + number: 2, + candidates: vec![Default::default(); candidates_count], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send 20 assignments related to `hash_b` + // to populate our knowledge + let assignments: Vec<_> = (0..candidates_count) + .map(|candidate_index| { + let validator_index = ValidatorIndex(candidate_index as u32); + let cert = fake_assignment_cert(hash_b, validator_index); + (cert, candidate_index as u32) + }) + .collect(); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_candidate_index, - tx, - )) => { - assert_eq!(assignment, assignments[i].0.clone().into()); - assert_eq!(claimed_candidate_index, assignments[i].1.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, peer, msg.clone()).await; + + for i in 0..candidates_count { + expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; + if i == 0 { + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.assignment(), &assignments[i].0.clone().into()); + assert_eq!(assignment.candidate_indices(), &assignments[i].1.into()); + assert_eq!(assignment.tranche(), 0); + } + ); - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; - } + expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; + } - // send a view update that removes block B from peer's view by bumping the finalized_number - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange( - *peer, - View::with_finalized(2), - )), - ) - .await; + // send a view update that removes block B from peer's view by bumping the + // finalized_number + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::PeerViewChange(*peer, View::with_finalized(2)), + ), + ) + .await; - // send the assignments again - send_message_from_peer(overseer, peer, msg.clone()).await; + // send the assignments again + send_message_from_peer(overseer, peer, msg.clone()).await; - // each of them will incur `COST_UNEXPECTED_MESSAGE`, not only the first one - for _ in 0..candidates_count { - expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE).await; - } - virtual_overseer - }); + // each of them will incur `COST_UNEXPECTED_MESSAGE`, not only the first one + for _ in 0..candidates_count { + expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; + expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE).await; + } + virtual_overseer + }, + ); } /// Imagine we send a message to peer A and peer B. @@ -739,86 +940,94 @@ fn peer_sending_us_the_same_we_just_sent_them_is_ok() { let peers = make_peers_and_authority_ids(8); let peer_a = peers.first().unwrap().0; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - let peer = &peer_a; - setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), - ) - .await; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + let peer = &peer_a; + setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), + ) + .await; - // new block `hash` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // new block `hash` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - // update peer view to include the hash - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange( - *peer, - view![hash], - )), - ) - .await; + // update peer view to include the hash + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::PeerViewChange(*peer, view![hash]), + ), + ) + .await; - // we should send them the assignment - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - } - ); + // we should send them the assignment + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); - // but if someone else is sending it the same assignment - // the peer could send us it as well - let assignments = vec![(cert, candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer(overseer, peer, msg.clone()).await; + // but if someone else is sending it the same assignment + // the peer could send us it as well + let assignments = vec![(cert, candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); + send_message_from_peer(overseer, peer, msg.clone()).await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "we should not punish the peer"); + assert!( + overseer.recv().timeout(TIMEOUT).await.is_none(), + "we should not punish the peer" + ); - // send the assignments again - send_message_from_peer(overseer, peer, msg).await; + // send the assignments again + send_message_from_peer(overseer, peer, msg).await; - // now we should - expect_reputation_change(overseer, peer, COST_DUPLICATE_MESSAGE).await; - virtual_overseer - }); + // now we should + expect_reputation_change(overseer, peer, COST_DUPLICATE_MESSAGE).await; + virtual_overseer + }, + ); } #[test] @@ -830,116 +1039,134 @@ fn import_approval_happy_path_v1_v2_peers() { let peer_c = peers.get(2).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers with V1 and V2 protocol versions + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; + + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 1); + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![(candidate_hash, 0.into(), 0.into()); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology, where a, b, and c are topology neighbors to the node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers with V1 and V2 protocol versions - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology, where a, b, and c are topology neighbors to the node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; - - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - // 1 peer is v1 - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - } - ); - - // 1 peer is v2 - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - } - ); - - // send the an approval from peer_b - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices: candidate_index.into(), - validator: validator_index, - signature: dummy_signature(), - }; - let msg: protocol_v3::ApprovalDistributionMessage = - protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_b, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(approvals.len(), 1); - } - ); - virtual_overseer - }); + // 1 peer is v1 + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + // 1 peer is v2 + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + // send the an approval from peer_b + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices: candidate_index.into(), + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash], + validator_index, + ), + }; + let msg: protocol_v3::ApprovalDistributionMessage = + protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_b, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); + + expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(approvals.len(), 1); + } + ); + virtual_overseer + }, + ); } // Test a v2 approval that signs multiple candidate is correctly processed. @@ -952,103 +1179,123 @@ fn import_approval_happy_path_v2() { let peer_c = peers.get(2).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash_first = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_second = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers with V2 protocol versions + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 1); + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![ + (candidate_hash_first, 0.into(), 0.into()), + (candidate_hash_second, 1.into(), 1.into()), + ], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology, where a, b, and c are topology neighbors to the node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers with V2 protocol versions - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 2], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology, where a, b, and c are topology neighbors to the node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(0); + let candidate_indices: CandidateBitfield = + vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + let candidate_bitfields = vec![CoreIndex(0), CoreIndex(1)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_indices.clone(), + ), + ) + .await; - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(0); - let candidate_indices: CandidateBitfield = - vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); - let candidate_bitfields = vec![CoreIndex(0), CoreIndex(1)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_indices.clone(), - ), - ) - .await; + // 1 peer is v2 + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert_eq!(assignments.len(), 1); + } + ); - // 1 peer is v2 - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 2); - assert_eq!(assignments.len(), 1); - } - ); - - // send the an approval from peer_b - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices, - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_b, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(approvals.len(), 1); - } - ); - virtual_overseer - }); + // send the an approval from peer_b + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices, + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash_first, candidate_hash_second], + validator_index, + ), + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_b, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); + + expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(approvals.len(), 1); + } + ); + virtual_overseer + }, + ); } // Tests that votes that cover multiple assignments candidates are correctly processed on importing @@ -1062,187 +1309,203 @@ fn multiple_assignments_covered_with_one_approval_vote() { let peer_d = peers.get(4).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash_first = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_second = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers with V2 protocol versions + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_d, view![hash], ValidationVersion::V3).await; + + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 5); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![ + (candidate_hash_first, 0.into(), 0.into()), + (candidate_hash_second, 1.into(), 1.into()), + ], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers with V2 protocol versions - setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_d, view![hash], ValidationVersion::V3).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 2], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(2); // peer_c is the originator + let candidate_indices: CandidateBitfield = + vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(2); // peer_c is the originator - let candidate_indices: CandidateBitfield = - vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + let core_bitfields = vec![CoreIndex(0)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfields); - let core_bitfields = vec![CoreIndex(0)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfields); + // send the candidate 0 assignment from peer_b + let assignment = IndirectAssignmentCertV2 { + block_hash: hash, + validator: validator_index, + cert: cert.cert, + }; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( + assignment, + (0 as CandidateIndex).into(), + )]); + send_message_from_peer_v3(overseer, &peer_d, msg).await; + provide_session(overseer, session.clone()).await; - // send the candidate 0 assignment from peer_b - let assignment = IndirectAssignmentCertV2 { - block_hash: hash, - validator: validator_index, - cert: cert.cert, - }; - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( - assignment, - (0 as CandidateIndex).into(), - )]); - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert!(peers.len() >= 2); - assert!(peers.contains(&peer_a)); - assert!(peers.contains(&peer_b)); - assert_eq!(assignments.len(), 1); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert!(peers.len() >= 2); + assert!(peers.contains(&peer_a)); + assert!(peers.contains(&peer_b)); + assert_eq!(assignments.len(), 1); + } + ); - let candidate_bitfields = vec![CoreIndex(1)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); + let candidate_bitfields = vec![CoreIndex(1)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); - // send the candidate 1 assignment from peer_c - let assignment = IndirectAssignmentCertV2 { - block_hash: hash, - validator: validator_index, - cert: cert.cert, - }; - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( - assignment, - (1 as CandidateIndex).into(), - )]); - - send_message_from_peer_v3(overseer, &peer_c, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peer_c, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert!(peers.len() >= 2); - assert!(peers.contains(&peer_b)); - assert!(peers.contains(&peer_a)); - assert_eq!(assignments.len(), 1); - } - ); - - // send an approval from peer_b - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices, - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert!(peers.len() >= 2); - assert!(peers.contains(&peer_b)); - assert!(peers.contains(&peer_a)); - assert_eq!(approvals.len(), 1); - } - ); - for candidate_index in 0..1 { - let (tx_distribution, rx_distribution) = oneshot::channel(); - let mut candidates_requesting_signatures = HashSet::new(); - candidates_requesting_signatures.insert((hash, candidate_index)); - overseer_send( - overseer, - ApprovalDistributionMessage::GetApprovalSignatures( - candidates_requesting_signatures, - tx_distribution, + // send the candidate 1 assignment from peer_c + let assignment = IndirectAssignmentCertV2 { + block_hash: hash, + validator: validator_index, + cert: cert.cert, + }; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( + assignment, + (1 as CandidateIndex).into(), + )]); + + send_message_from_peer_v3(overseer, &peer_c, msg).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peer_c, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert!(peers.len() >= 2); + assert!(peers.contains(&peer_b)); + assert!(peers.contains(&peer_a)); + assert_eq!(assignments.len(), 1); + } + ); + + // send an approval from peer_b + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices, + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash_first, candidate_hash_second], + validator_index, ), - ) - .await; - let signatures = rx_distribution.await.unwrap(); + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_d, msg).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); - assert_eq!(signatures.len(), 1); - for (signing_validator, signature) in signatures { - assert_eq!(validator_index, signing_validator); - assert_eq!(signature.0, hash); - assert_eq!(signature.2, approval.signature); - assert_eq!(signature.1, vec![0, 1]); + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert!(peers.len() >= 2); + assert!(peers.contains(&peer_b)); + assert!(peers.contains(&peer_a)); + assert_eq!(approvals.len(), 1); + } + ); + for candidate_index in 0..1 { + let (tx_distribution, rx_distribution) = oneshot::channel(); + let mut candidates_requesting_signatures = HashSet::new(); + candidates_requesting_signatures.insert((hash, candidate_index)); + overseer_send( + overseer, + ApprovalDistributionMessage::GetApprovalSignatures( + candidates_requesting_signatures, + tx_distribution, + ), + ) + .await; + let signatures = rx_distribution.await.unwrap(); + + assert_eq!(signatures.len(), 1); + for (signing_validator, signature) in signatures { + assert_eq!(validator_index, signing_validator); + assert_eq!(signature.0, hash); + assert_eq!(signature.2, approval.signature); + assert_eq!(signature.1, vec![0, 1]); + } } - } - virtual_overseer - }); + virtual_overseer + }, + ); } // Tests that votes that cover multiple assignments candidates are correctly processed when unify @@ -1256,305 +1519,335 @@ fn unify_with_peer_multiple_assignments_covered_with_one_approval_vote() { let peer_d = peers.get(4).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash_first = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_second = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + setup_peer_with_view(overseer, &peer_d, view![hash], ValidationVersion::V3).await; + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 5); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![ + (candidate_hash_first, 0.into(), 0.into()), + (candidate_hash_second, 1.into(), 1.into()), + ], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - setup_peer_with_view(overseer, &peer_d, view![hash], ValidationVersion::V3).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 2], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; - - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(2); // peer_c is the originator - let candidate_indices: CandidateBitfield = - vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(2); // peer_c is the originator + let candidate_indices: CandidateBitfield = + vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); - let core_bitfields = vec![CoreIndex(0)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfields); + let core_bitfields = vec![CoreIndex(0)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfields); - // send the candidate 0 assignment from peer_b - let assignment = IndirectAssignmentCertV2 { - block_hash: hash, - validator: validator_index, - cert: cert.cert, - }; - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( - assignment, - (0 as CandidateIndex).into(), - )]); - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; + // send the candidate 0 assignment from peer_b + let assignment = IndirectAssignmentCertV2 { + block_hash: hash, + validator: validator_index, + cert: cert.cert, + }; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( + assignment, + (0 as CandidateIndex).into(), + )]); + send_message_from_peer_v3(overseer, &peer_d, msg).await; + provide_session(overseer, session.clone()).await; - let candidate_bitfields = vec![CoreIndex(1)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - // send the candidate 1 assignment from peer_c - let assignment = IndirectAssignmentCertV2 { - block_hash: hash, - validator: validator_index, - cert: cert.cert, - }; - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( - assignment, - (1 as CandidateIndex).into(), - )]); - - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - - // send an approval from peer_b - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices, - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - - // setup peers with V2 protocol versions - setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - let mut expected_peers_assignments = vec![peer_a, peer_b]; - let mut expected_peers_approvals = vec![peer_a, peer_b]; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert!(peers.len() == 1); - assert!(expected_peers_assignments.contains(peers.first().unwrap())); - expected_peers_assignments.retain(|peer| peer != peers.first().unwrap()); - assert_eq!(assignments.len(), 2); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert!(peers.len() == 1); - assert!(expected_peers_approvals.contains(peers.first().unwrap())); - expected_peers_approvals.retain(|peer| peer != peers.first().unwrap()); - assert_eq!(approvals.len(), 1); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert!(peers.len() == 1); - assert!(expected_peers_assignments.contains(peers.first().unwrap())); - expected_peers_assignments.retain(|peer| peer != peers.first().unwrap()); - assert_eq!(assignments.len(), 2); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert!(peers.len() == 1); - assert!(expected_peers_approvals.contains(peers.first().unwrap())); - expected_peers_approvals.retain(|peer| peer != peers.first().unwrap()); - assert_eq!(approvals.len(), 1); - } - ); + let candidate_bitfields = vec![CoreIndex(1)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); - virtual_overseer - }); -} + // send the candidate 1 assignment from peer_c + let assignment = IndirectAssignmentCertV2 { + block_hash: hash, + validator: validator_index, + cert: cert.cert, + }; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( + assignment, + (1 as CandidateIndex).into(), + )]); -#[test] -fn import_approval_bad() { - let peer_a = PeerId::random(); - let peer_b = PeerId::random(); - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); + send_message_from_peer_v3(overseer, &peer_d, msg).await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - - // send the an approval from peer_b, we don't have an assignment yet - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices: candidate_index.into(), - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_b, msg).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - expect_reputation_change(overseer, &peer_b, COST_UNEXPECTED_MESSAGE).await; + // send an approval from peer_b + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices, + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash_first, candidate_hash_second], + validator_index, + ), + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_d, msg).await; - // now import an assignment from peer_b - let assignments = vec![(cert.clone(), candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer(overseer, &peer_b, msg).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - i, - tx, - )) => { - assert_eq!(assignment, cert.into()); - assert_eq!(i, candidate_index.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; - - // and try again - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_b, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownBlock(hash))).unwrap(); - } - ); + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - expect_reputation_change(overseer, &peer_b, COST_INVALID_MESSAGE).await; - virtual_overseer - }); -} + // setup peers with V2 protocol versions + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + let mut expected_peers_assignments = vec![peer_a, peer_b]; + let mut expected_peers_approvals = vec![peer_a, peer_b]; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert!(peers.len() == 1); + assert!(expected_peers_assignments.contains(peers.first().unwrap())); + expected_peers_assignments.retain(|peer| peer != peers.first().unwrap()); + assert_eq!(assignments.len(), 2); + } + ); -/// make sure we clean up the state on block finalized -#[test] -fn update_our_view() { - let parent_hash = Hash::repeat_byte(0xFF); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert!(peers.len() == 1); + assert!(expected_peers_approvals.contains(peers.first().unwrap())); + expected_peers_approvals.retain(|peer| peer != peers.first().unwrap()); + assert_eq!(approvals.len(), 1); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert!(peers.len() == 1); + assert!(expected_peers_assignments.contains(peers.first().unwrap())); + expected_peers_assignments.retain(|peer| peer != peers.first().unwrap()); + assert_eq!(assignments.len(), 2); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert!(peers.len() == 1); + assert!(expected_peers_approvals.contains(peers.first().unwrap())); + expected_peers_approvals.retain(|peer| peer != peers.first().unwrap()); + assert_eq!(approvals.len(), 1); + } + ); + + virtual_overseer + }, + ); +} + +#[test] +fn import_approval_bad() { + let peer_a = PeerId::random(); + let peer_b = PeerId::random(); + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + + let diff_candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 1); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![(candidate_hash, 0.into(), 0.into()); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + + // Sign a different candidate hash. + let payload = + ApprovalVoteMultipleCandidates(&vec![diff_candidate_hash]).signing_payload(1); + let sign_key = session.validators.get(ValidatorIndex(0)).unwrap().clone(); + let signature = keystore + .sr25519_sign(ValidatorId::ID, &sign_key.into(), &payload[..]) + .unwrap() + .unwrap(); + + // send the an approval from peer_b, we don't have an assignment yet + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices: candidate_index.into(), + validator: validator_index, + signature: signature.into(), + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_b, msg).await; + + expect_reputation_change(overseer, &peer_b, COST_UNEXPECTED_MESSAGE).await; + + // now import an assignment from peer_b + let assignments = vec![(cert.clone(), candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); + send_message_from_peer(overseer, &peer_b, msg).await; + provide_session(overseer, session.clone()).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.candidate_indices(), &candidate_index.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; + + // and try again + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_b, msg).await; + + expect_reputation_change(overseer, &peer_b, COST_INVALID_MESSAGE).await; + virtual_overseer + }, + ); +} + +/// make sure we clean up the state on block finalized +#[test] +fn update_our_view() { + let parent_hash = Hash::repeat_byte(0xFF); let hash_a = Hash::repeat_byte(0xAA); let hash_b = Hash::repeat_byte(0xBB); let hash_c = Hash::repeat_byte(0xCC); - let state = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // new block `hash_a` with 1 candidates - let meta_a = BlockApprovalMeta { - hash: hash_a, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_b = BlockApprovalMeta { - hash: hash_b, - parent_hash: hash_a, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_c = BlockApprovalMeta { - hash: hash_c, - parent_hash: hash_b, - number: 3, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); - overseer_send(overseer, msg).await; - virtual_overseer - }); + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // new block `hash_a` with 1 candidates + let meta_a = BlockApprovalMeta { + hash: hash_a, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_b = BlockApprovalMeta { + hash: hash_b, + parent_hash: hash_a, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_c = BlockApprovalMeta { + hash: hash_c, + parent_hash: hash_b, + number: 3, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); + overseer_send(overseer, msg).await; + virtual_overseer + }, + ); assert!(state.blocks_by_number.get(&1).is_some()); assert!(state.blocks_by_number.get(&2).is_some()); @@ -1563,12 +1856,17 @@ fn update_our_view() { assert!(state.blocks.get(&hash_b).is_some()); assert!(state.blocks.get(&hash_c).is_some()); - let state = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // finalize a block - overseer_signal_block_finalized(overseer, 2).await; - virtual_overseer - }); + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // finalize a block + overseer_signal_block_finalized(overseer, 2).await; + virtual_overseer + }, + ); assert!(state.blocks_by_number.get(&1).is_none()); assert!(state.blocks_by_number.get(&2).is_none()); @@ -1577,12 +1875,17 @@ fn update_our_view() { assert!(state.blocks.get(&hash_b).is_none()); assert!(state.blocks.get(&hash_c).is_some()); - let state = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // finalize a very high block - overseer_signal_block_finalized(overseer, 4_000_000_000).await; - virtual_overseer - }); + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // finalize a very high block + overseer_signal_block_finalized(overseer, 4_000_000_000).await; + virtual_overseer + }, + ); assert!(state.blocks_by_number.get(&3).is_none()); assert!(state.blocks.get(&hash_c).is_none()); @@ -1600,81 +1903,89 @@ fn update_peer_view() { let peer_a = peers.first().unwrap().0; let peer = &peer_a; - let state = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // new block `hash_a` with 1 candidates - let meta_a = BlockApprovalMeta { - hash: hash_a, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_b = BlockApprovalMeta { - hash: hash_b, - parent_hash: hash_a, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_c = BlockApprovalMeta { - hash: hash_c, - parent_hash: hash_b, - number: 3, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // new block `hash_a` with 1 candidates + let meta_a = BlockApprovalMeta { + hash: hash_a, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_b = BlockApprovalMeta { + hash: hash_b, + parent_hash: hash_a, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_c = BlockApprovalMeta { + hash: hash_c, + parent_hash: hash_b, + number: 3, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), + ) + .await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), - ) - .await; + let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(0)); + let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(0)); - let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(0)); - let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(0)); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), - ) - .await; + // connect a peer + setup_peer_with_view(overseer, peer, view![hash_a], ValidationVersion::V1).await; - // connect a peer - setup_peer_with_view(overseer, peer, view![hash_a], ValidationVersion::V1).await; - - // we should send relevant assignments to the peer - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - } - ); - virtual_overseer - }); + // we should send relevant assignments to the peer + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + virtual_overseer + }, + ); assert_eq!(state.peer_views.get(peer).map(|v| v.view.finalized_number), Some(0)); assert_eq!( @@ -1691,42 +2002,49 @@ fn update_peer_view() { 1, ); - let state = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // update peer's view - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange( - *peer, - View::new(vec![hash_b, hash_c, hash_d], 2), - )), - ) - .await; + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // update peer's view + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::PeerViewChange( + *peer, + View::new(vec![hash_b, hash_c, hash_d], 2), + ), + ), + ) + .await; - let cert_c = fake_assignment_cert(hash_c, ValidatorIndex(0)); + let cert_c = fake_assignment_cert(hash_c, ValidatorIndex(0)); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_c.clone().into(), 0.into()), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_c.clone().into(), 0.into()), + ) + .await; - // we should send relevant assignments to the peer - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - assert_eq!(assignments[0].0, cert_c); - } - ); - virtual_overseer - }); + // we should send relevant assignments to the peer + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + assert_eq!(assignments[0].0, cert_c); + } + ); + virtual_overseer + }, + ); assert_eq!(state.peer_views.get(peer).map(|v| v.view.finalized_number), Some(2)); assert_eq!( @@ -1744,19 +2062,26 @@ fn update_peer_view() { ); let finalized_number = 4_000_000_000; - let state = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // update peer's view - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange( - *peer, - View::with_finalized(finalized_number), - )), - ) - .await; - virtual_overseer - }); + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // update peer's view + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::PeerViewChange( + *peer, + View::with_finalized(finalized_number), + ), + ), + ) + .await; + virtual_overseer + }, + ); assert_eq!(state.peer_views.get(peer).map(|v| v.view.finalized_number), Some(finalized_number)); assert!(state.blocks.get(&hash_c).unwrap().known_by.get(peer).is_none()); @@ -1779,164 +2104,176 @@ fn update_peer_authority_id() { // Y neighbour, we simulate that PeerId is not known in the beginning. let neighbour_y = peers.get(neighbour_y_index).unwrap().0; - let _state = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // new block `hash_a` with 1 candidates - let meta_a = BlockApprovalMeta { - hash: hash_a, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_b = BlockApprovalMeta { - hash: hash_b, - parent_hash: hash_a, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_c = BlockApprovalMeta { - hash: hash_c, - parent_hash: hash_b, - number: 3, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + let _state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // new block `hash_a` with 1 candidates + let meta_a = BlockApprovalMeta { + hash: hash_a, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_b = BlockApprovalMeta { + hash: hash_b, + parent_hash: hash_a, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_c = BlockApprovalMeta { + hash: hash_c, + parent_hash: hash_b, + number: 3, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .enumerate() + .map(|(index, (peer_id, authority))| { + (if index == 0 { None } else { Some(*peer_id) }, authority.clone()) + }) + .collect_vec(); + + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[neighbour_x_index], + &[neighbour_y_index], + local_index, + ), + ) + .await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); - overseer_send(overseer, msg).await; + let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(local_index as u32)); + let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(local_index as u32)); - let peers_with_optional_peer_id = peers - .iter() - .enumerate() - .map(|(index, (peer_id, authority))| { - (if index == 0 { None } else { Some(*peer_id) }, authority.clone()) - }) - .collect_vec(); - - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[neighbour_x_index], - &[neighbour_y_index], - local_index, - ), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), + ) + .await; - let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(local_index as u32)); - let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(local_index as u32)); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), - ) - .await; + // connect a peer + setup_peer_with_view(overseer, &neighbour_x, view![hash_a], ValidationVersion::V1) + .await; + setup_peer_with_view(overseer, &neighbour_y, view![hash_a], ValidationVersion::V1) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), - ) - .await; + setup_peer_with_view(overseer, &neighbour_x, view![hash_b], ValidationVersion::V1) + .await; + setup_peer_with_view(overseer, &neighbour_y, view![hash_b], ValidationVersion::V1) + .await; - // connect a peer - setup_peer_with_view(overseer, &neighbour_x, view![hash_a], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &neighbour_y, view![hash_a], ValidationVersion::V1).await; - - setup_peer_with_view(overseer, &neighbour_x, view![hash_b], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &neighbour_y, view![hash_b], ValidationVersion::V1).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - assert_eq!(peers.get(0), Some(&neighbour_y)); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - assert_eq!(peers.get(0), Some(&neighbour_y)); - } - ); - - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate( - NetworkBridgeEvent::UpdatedAuthorityIds( - peers[neighbour_x_index].0, - [peers[neighbour_x_index].1.clone()].into_iter().collect(), + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + assert_eq!(peers.get(0), Some(&neighbour_y)); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + assert_eq!(peers.get(0), Some(&neighbour_y)); + } + ); + + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::UpdatedAuthorityIds( + peers[neighbour_x_index].0, + [peers[neighbour_x_index].1.clone()].into_iter().collect(), + ), ), - ), - ) - .await; + ) + .await; - // we should send relevant assignments to the peer, after we found it's peer id. - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - gum::info!(target: LOG_TARGET, ?peers, ?assignments); - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 2); - assert_eq!(assignments.get(0).unwrap().0.block_hash, hash_a); - assert_eq!(assignments.get(1).unwrap().0.block_hash, hash_b); - assert_eq!(peers.get(0), Some(&neighbour_x)); - } - ); - - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate( - NetworkBridgeEvent::UpdatedAuthorityIds( - peers[neighbour_y_index].0, - [peers[neighbour_y_index].1.clone()].into_iter().collect(), + // we should send relevant assignments to the peer, after we found it's peer id. + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + gum::info!(target: LOG_TARGET, ?peers, ?assignments); + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 2); + assert_eq!(assignments.get(0).unwrap().0.block_hash, hash_a); + assert_eq!(assignments.get(1).unwrap().0.block_hash, hash_b); + assert_eq!(peers.get(0), Some(&neighbour_x)); + } + ); + + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::UpdatedAuthorityIds( + peers[neighbour_y_index].0, + [peers[neighbour_y_index].1.clone()].into_iter().collect(), + ), ), - ), - ) - .await; - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate( - NetworkBridgeEvent::UpdatedAuthorityIds( - peers[neighbour_x_index].0, - [peers[neighbour_x_index].1.clone()].into_iter().collect(), + ) + .await; + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::UpdatedAuthorityIds( + peers[neighbour_x_index].0, + [peers[neighbour_x_index].1.clone()].into_iter().collect(), + ), ), - ), - ) - .await; - assert!( - overseer.recv().timeout(TIMEOUT).await.is_none(), - "no message should be sent peers are already known" - ); + ) + .await; + assert!( + overseer.recv().timeout(TIMEOUT).await.is_none(), + "no message should be sent peers are already known" + ); - virtual_overseer - }); + virtual_overseer + }, + ); } /// E.g. if someone copies the keys... @@ -1945,89 +2282,105 @@ fn import_remotely_then_locally() { let peer_a = PeerId::random(); let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); let peer = &peer_a; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup the peer - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // import the assignment remotely first - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, peer, msg).await; - - // send an `Accept` message from the Approval Voting subsystem - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - i, - tx, - )) => { - assert_eq!(assignment, cert.clone().into()); - assert_eq!(i, candidate_index.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup the peer + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + let mut keystore = LocalKeystore::in_memory(); + + let session = dummy_session_info_valid(1, &mut keystore, 1); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![(candidate_hash, 0.into(), 0.into()); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let payload = ApprovalVoteMultipleCandidates(&vec![candidate_hash]).signing_payload(1); + let sign_key = session.validators.get(ValidatorIndex(0)).unwrap().clone(); + let signature = keystore + .sr25519_sign(ValidatorId::ID, &sign_key.into(), &payload[..]) + .unwrap() + .unwrap(); + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // import the assignment remotely first + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, peer, msg).await; + provide_session(overseer, session.clone()).await; + + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.assignment(), &cert.clone().into()); + assert_eq!(assignment.candidate_indices(), &candidate_index.into()); + assert_eq!(assignment.tranche(), 0); + } + ); - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; + expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; - // import the same assignment locally - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // import the same assignment locally + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - // send the approval remotely - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices: candidate_index.into(), - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, peer, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; + // send the approval remotely + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices: candidate_index.into(), + validator: validator_index, + signature: signature.into(), + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, peer, msg).await; - // import the same approval locally - overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval)).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); + expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + // import the same approval locally + overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval)) + .await; + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } #[test] @@ -2038,94 +2391,100 @@ fn sends_assignments_even_when_state_is_approved() { let hash = Hash::repeat_byte(0xAA); let peer = &peer_a; - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), + ) + .await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), - ) - .await; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: dummy_signature(), + }; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + // connect the peer. + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - // connect the peer. - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(peers, vec![*peer]); - assert_eq!(sent_assignments, assignments); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) - )) - )) => { - assert_eq!(peers, vec![*peer]); - assert_eq!(sent_approvals, approvals); - } - ); + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(peers, vec![*peer]); + assert_eq!(sent_assignments, assignments); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + assert_eq!(peers, vec![*peer]); + assert_eq!(sent_approvals, approvals); + } + ); + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } /// Same as `sends_assignments_even_when_state_is_approved_v2` but with `VRFModuloCompact` @@ -2138,393 +2497,589 @@ fn sends_assignments_even_when_state_is_approved_v2() { let hash = Hash::repeat_byte(0xAA); let peer = &peer_a; - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 4], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), + ) + .await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 4], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), - ) - .await; + let validator_index = ValidatorIndex(0); + let cores = vec![0, 1, 2, 3]; + let candidate_bitfield: CandidateBitfield = cores.clone().try_into().unwrap(); + + let core_bitfield: CoreBitfield = cores + .iter() + .map(|index| CoreIndex(*index)) + .collect::>() + .try_into() + .unwrap(); + + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield.clone()); + + // Assumes candidate index == core index. + let approvals = cores + .iter() + .map(|core| IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices: (*core).into(), + validator: validator_index, + signature: dummy_signature(), + }) + .collect::>(); + + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_bitfield.clone(), + ), + ) + .await; + + for approval in &approvals { + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone()), + ) + .await; + } + + // connect the peer. + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V3).await; + + let assignments = vec![(cert.clone(), candidate_bitfield.clone())]; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(peers, vec![*peer]); + assert_eq!(sent_assignments, assignments); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + // Construct a hashmaps of approvals for comparison. Approval distribution reorders messages because they are kept in a + // hashmap as well. + let sent_approvals = sent_approvals.into_iter().map(|approval| (approval.candidate_indices.clone(), approval)).collect::>(); + let approvals = approvals.into_iter().map(|approval| (approval.candidate_indices.clone(), approval)).collect::>(); + + assert_eq!(peers, vec![*peer]); + assert_eq!(sent_approvals, approvals); + } + ); + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); +} + +/// +/// +/// 1. Receive remote peer view update with an unknown head +/// 2. Receive assignments for that unknown head +/// 3. Update our view and import the new block +/// 4. Expect that no reputation with `COST_UNEXPECTED_MESSAGE` is applied +#[test] +fn race_condition_in_local_vs_remote_view_update() { + let parent_hash = Hash::repeat_byte(0xFF); + let peer_a = PeerId::random(); + let hash_b = Hash::repeat_byte(0xBB); - let validator_index = ValidatorIndex(0); - let cores = vec![0, 1, 2, 3]; - let candidate_bitfield: CandidateBitfield = cores.clone().try_into().unwrap(); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + let peer = &peer_a; + + // Test a small number of candidates + let candidates_count = 1; + let meta = BlockApprovalMeta { + hash: hash_b, + parent_hash, + number: 2, + candidates: vec![Default::default(); candidates_count], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + // This will send a peer view that is ahead of our view + setup_peer_with_view(overseer, peer, view![hash_b], ValidationVersion::V1).await; + + // Send our view update to include a new head + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::OurViewChange(our_view![hash_b]), + ), + ) + .await; - let core_bitfield: CoreBitfield = cores - .iter() - .map(|index| CoreIndex(*index)) - .collect::>() - .try_into() - .unwrap(); + // send assignments related to `hash_b` but they will come to the MessagesPending + let assignments: Vec<_> = (0..candidates_count) + .map(|candidate_index| { + let validator_index = ValidatorIndex(candidate_index as u32); + let cert = fake_assignment_cert(hash_b, validator_index); + (cert, candidate_index as u32) + }) + .collect(); - let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield.clone()); + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, peer, msg.clone()).await; + + // This will handle pending messages being processed + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + for i in 0..candidates_count { + // Previously, this has caused out-of-view assignments/approvals + //expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.assignment(), &assignments[i].0.clone().into()); + assert_eq!(assignment.candidate_indices(), &assignments[i].1.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + // Since we have a valid statement pending, this should always occur + expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; + } + virtual_overseer + }, + ); +} - // Assumes candidate index == core index. - let approvals = cores - .iter() - .map(|core| IndirectSignedApprovalVoteV2 { +// Tests that local messages propagate to both dimensions. +#[test] +fn propagates_locally_generated_assignment_to_both_dimensions() { + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let peers = make_peers_and_authority_ids(100); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Connect all peers. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30, 40, 60, 70, 80], + &[50, 51, 52, 53, 54, 55, 56, 57], + 1, + ), + ) + .await; + + let expected_indices = [ + // Both dimensions in the gossip topology + 0, 10, 20, 30, 40, 60, 70, 80, 50, 51, 52, 53, 54, 55, 56, 57, + ]; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let approval = IndirectSignedApprovalVote { block_hash: hash, - candidate_indices: (*core).into(), + candidate_index, validator: validator_index, - signature: dummy_signature(), - }) - .collect::>(); - - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_bitfield.clone(), - ), - ) - .await; + signature: dummy_signature(), + }; - for approval in &approvals { overseer_send( overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone()), + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), ) .await; - } - - // connect the peer. - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V3).await; - - let assignments = vec![(cert.clone(), candidate_bitfield.clone())]; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(peers, vec![*peer]); - assert_eq!(sent_assignments, assignments); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(sent_approvals) - )) - )) => { - // Construct a hashmaps of approvals for comparison. Approval distribution reorders messages because they are kept in a - // hashmap as well. - let sent_approvals = sent_approvals.into_iter().map(|approval| (approval.candidate_indices.clone(), approval)).collect::>(); - let approvals = approvals.into_iter().map(|approval| (approval.candidate_indices.clone(), approval)).collect::>(); - - assert_eq!(peers, vec![*peer]); - assert_eq!(sent_approvals, approvals); - } - ); - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} - -/// -/// -/// 1. Receive remote peer view update with an unknown head -/// 2. Receive assignments for that unknown head -/// 3. Update our view and import the new block -/// 4. Expect that no reputation with `COST_UNEXPECTED_MESSAGE` is applied -#[test] -fn race_condition_in_local_vs_remote_view_update() { - let parent_hash = Hash::repeat_byte(0xFF); - let peer_a = PeerId::random(); - let hash_b = Hash::repeat_byte(0xBB); - - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - let peer = &peer_a; - - // Test a small number of candidates - let candidates_count = 1; - let meta = BlockApprovalMeta { - hash: hash_b, - parent_hash, - number: 2, - candidates: vec![Default::default(); candidates_count], - slot: 1.into(), - session: 1, - }; - - // This will send a peer view that is ahead of our view - setup_peer_with_view(overseer, peer, view![hash_b], ValidationVersion::V1).await; - - // Send our view update to include a new head - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::OurViewChange( - our_view![hash_b], - )), - ) - .await; - - // send assignments related to `hash_b` but they will come to the MessagesPending - let assignments: Vec<_> = (0..candidates_count) - .map(|candidate_index| { - let validator_index = ValidatorIndex(candidate_index as u32); - let cert = fake_assignment_cert(hash_b, validator_index); - (cert, candidate_index as u32) - }) - .collect(); - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, peer, msg.clone()).await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; - // This will handle pending messages being processed - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; - for i in 0..candidates_count { - // Previously, this has caused out-of-view assignments/approvals - //expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; + let mut assignment_sent_peers = assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), expected_indices.len() + 4); + for &i in &expected_indices { + assert!( + sent_peers.contains(&peers[i].0), + "Message not sent to expected peer {}", + i, + ); + } + assert_eq!(sent_assignments, assignments); + sent_peers + } + ); assert_matches!( overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_candidate_index, - tx, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + mut sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) )) => { - assert_eq!(assignment, assignments[i].0.clone().into()); - assert_eq!(claimed_candidate_index, assignments[i].1.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); + // Random sampling is reused from the assignment. + sent_peers.sort(); + assignment_sent_peers.sort(); + assert_eq!(sent_peers, assignment_sent_peers); + assert_eq!(sent_approvals, approvals); } ); - // Since we have a valid statement pending, this should always occur - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; - } - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// Tests that local messages propagate to both dimensions. +// Tests that messages propagate to the unshared dimension. #[test] -fn propagates_locally_generated_assignment_to_both_dimensions() { +fn propagates_assignments_along_unshared_dimension() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - // Connect all peers. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + // Connect all peers. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30, 40, 60, 70, 80], - &[50, 51, 52, 53, 54, 55, 56, 57], - 1, - ), - ) - .await; + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); - let expected_indices = [ - // Both dimensions in the gossip topology - 0, 10, 20, 30, 40, 60, 70, 80, 50, 51, 52, 53, 54, 55, 56, 57, - ]; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // Test messages from X direction go to Y peers + { + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), candidate_index)]; + + let msg = + protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + let expected_y = [50, 51, 52, 53]; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), expected_y.len() + 4); + for &i in &expected_y { + assert!( + sent_peers.contains(&peers[i].0), + "Message not sent to expected peer {}", + i, + ); + } + assert_eq!(sent_assignments, assignments); + } + ); + }; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // Test messages from X direction go to Y peers + { + let validator_index = ValidatorIndex(50); + let candidate_index = 0u32; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), candidate_index)]; - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - let mut assignment_sent_peers = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(sent_peers.len(), expected_indices.len() + 4); - for &i in &expected_indices { - assert!( - sent_peers.contains(&peers[i].0), - "Message not sent to expected peer {}", - i, - ); - } - assert_eq!(sent_assignments, assignments); - sent_peers - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - mut sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) - )) - )) => { - // Random sampling is reused from the assignment. - sent_peers.sort(); - assignment_sent_peers.sort(); - assert_eq!(sent_peers, assignment_sent_peers); - assert_eq!(sent_approvals, approvals); - } - ); + let msg = + protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + + let expected_x = [0, 10, 20, 30]; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), expected_x.len() + 4); + for &i in &expected_x { + assert!( + sent_peers.contains(&peers[i].0), + "Message not sent to expected peer {}", + i, + ); + } + assert_eq!(sent_assignments, assignments); + } + ); + }; + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// Tests that messages propagate to the unshared dimension. +// tests that messages are propagated to necessary peers after they connect #[test] -fn propagates_assignments_along_unshared_dimension() { +fn propagates_to_required_after_connect() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - - // Connect all peers. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + let omitted = [0, 10, 50, 51]; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + // Connect all peers except omitted. + for (i, (peer, _)) in peers.iter().enumerate() { + if !omitted.contains(&i) { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } + } + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30, 40, 60, 70, 80], + &[50, 51, 52, 53, 54, 55, 56, 57], + 1, + ), + ) + .await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + let expected_indices = [ + // Both dimensions in the gossip topology, minus omitted. + 20, 30, 40, 60, 70, 80, 52, 53, 54, 55, 56, 57, + ]; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - // Test messages from X direction go to Y peers - { let validator_index = ValidatorIndex(0); let candidate_index = 0u32; // import an assignment and approval locally. let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), candidate_index)]; + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: dummy_signature(), + }; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; - let expected_y = [50, 51, 52, 53]; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; - assert_matches!( + let mut assignment_sent_peers = assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( sent_peers, @@ -2532,8 +3087,8 @@ fn propagates_assignments_along_unshared_dimension() { protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) )) )) => { - assert_eq!(sent_peers.len(), expected_y.len() + 4); - for &i in &expected_y { + assert_eq!(sent_peers.len(), expected_indices.len() + 4); + for &i in &expected_indices { assert!( sent_peers.contains(&peers[i].0), "Message not sent to expected peer {}", @@ -2541,1123 +3096,1029 @@ fn propagates_assignments_along_unshared_dimension() { ); } assert_eq!(sent_assignments, assignments); + sent_peers } ); - }; - // Test messages from X direction go to Y peers - { - let validator_index = ValidatorIndex(50); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + mut sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + // Random sampling is reused from the assignment. + sent_peers.sort(); + assignment_sent_peers.sort(); + assert_eq!(sent_peers, assignment_sent_peers); + assert_eq!(sent_approvals, approvals); + } + ); + + for i in omitted.iter().copied() { + setup_peer_with_view(overseer, &peers[i].0, view![hash], ValidationVersion::V1) + .await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), 1); + assert_eq!(&sent_peers[0], &peers[i].0); + assert_eq!(sent_assignments, assignments); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + assert_eq!(sent_peers.len(), 1); + assert_eq!(&sent_peers[0], &peers[i].0); + assert_eq!(sent_approvals, approvals); + } + ); + } + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); +} + +// test that new gossip topology triggers send of messages. +#[test] +fn sends_to_more_peers_after_getting_topology() { + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let peers = make_peers_and_authority_ids(100); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Connect all peers except omitted. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let validator_index = ValidatorIndex(0); let candidate_index = 0u32; // import an assignment and approval locally. let cert = fake_assignment_cert(hash, validator_index); + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: dummy_signature(), + }; + + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; + + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; + + let expected_indices = vec![0, 10, 20, 30, 50, 51, 52, 53]; + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; + + let mut expected_indices_assignments = expected_indices.clone(); + let mut expected_indices_approvals = expected_indices.clone(); + + for _ in 0..expected_indices_assignments.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_assignments, assignments); - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + let pos = expected_indices_assignments.iter() + .position(|i| &peers[*i].0 == &sent_peers[0]) + .unwrap(); + expected_indices_assignments.remove(pos); + } + ); + } - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + for _ in 0..expected_indices_approvals.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_approvals, approvals); - let expected_x = [0, 10, 20, 30]; + let pos = expected_indices_approvals.iter() + .position(|i| &peers[*i].0 == &sent_peers[0]) + .unwrap(); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(sent_peers.len(), expected_x.len() + 4); - for &i in &expected_x { - assert!( - sent_peers.contains(&peers[i].0), - "Message not sent to expected peer {}", - i, - ); + expected_indices_approvals.remove(pos); } - assert_eq!(sent_assignments, assignments); - } - ); - }; + ); + } - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// tests that messages are propagated to necessary peers after they connect +// test aggression L1 #[test] -fn propagates_to_required_after_connect() { +fn originator_aggression_l1() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + let mut state = State::default(); + state.aggression_config.resend_unfinalized_period = None; + let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); - let omitted = [0, 10, 50, 51]; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - // Connect all peers except omitted. - for (i, (peer, _)) in peers.iter().enumerate() { - if !omitted.contains(&i) { + // Connect all peers except omitted. + for (peer, _) in &peers { setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; } - } - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30, 40, 60, 70, 80], - &[50, 51, 52, 53, 54, 55, 56, 57], - 1, - ), - ) - .await; - - let expected_indices = [ - // Both dimensions in the gossip topology, minus omitted. - 20, 30, 40, 60, 70, 80, 52, 53, 54, 55, 56, 57, - ]; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: dummy_signature(), + }; + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - let mut assignment_sent_peers = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(sent_peers.len(), expected_indices.len() + 4); - for &i in &expected_indices { - assert!( - sent_peers.contains(&peers[i].0), - "Message not sent to expected peer {}", - i, - ); - } - assert_eq!(sent_assignments, assignments); - sent_peers - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - mut sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) - )) - )) => { - // Random sampling is reused from the assignment. - sent_peers.sort(); - assignment_sent_peers.sort(); - assert_eq!(sent_peers, assignment_sent_peers); - assert_eq!(sent_approvals, approvals); - } - ); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; - for i in omitted.iter().copied() { - setup_peer_with_view(overseer, &peers[i].0, view![hash], ValidationVersion::V1).await; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; - assert_matches!( + let prev_sent_indices = assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( sent_peers, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + protocol_v1::ApprovalDistributionMessage::Assignments(_) )) )) => { - assert_eq!(sent_peers.len(), 1); - assert_eq!(&sent_peers[0], &peers[i].0); - assert_eq!(sent_assignments, assignments); + sent_peers.into_iter() + .filter_map(|sp| peers.iter().position(|p| &p.0 == &sp)) + .collect::>() } ); assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, + _, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + protocol_v1::ApprovalDistributionMessage::Approvals(_) )) - )) => { - assert_eq!(sent_peers.len(), 1); - assert_eq!(&sent_peers[0], &peers[i].0); - assert_eq!(sent_approvals, approvals); - } + )) => { } ); - } - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + // Add blocks until aggression L1 is triggered. + { + let mut parent_hash = hash; + for level in 0..aggression_l1_threshold { + let number = 1 + level + 1; // first block had number 1 + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1); + overseer_send(overseer, msg).await; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + parent_hash = hash; + } + } + + let unsent_indices = + (0..peers.len()).filter(|i| !prev_sent_indices.contains(&i)).collect::>(); + + for _ in 0..unsent_indices.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_assignments, assignments); + + assert!(unsent_indices.iter() + .any(|i| &peers[*i].0 == &sent_peers[0])); + } + ); + } + + for _ in 0..unsent_indices.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_approvals, approvals); + + assert!(unsent_indices.iter() + .any(|i| &peers[*i].0 == &sent_peers[0])); + } + ); + } + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// test that new gossip topology triggers send of messages. +// test aggression L1 #[test] -fn sends_to_more_peers_after_getting_topology() { +fn non_originator_aggression_l1() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - - // Connect all peers except omitted. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + let mut state = state_without_reputation_delay(); + state.aggression_config.resend_unfinalized_period = None; + let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + // Connect all peers except omitted. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - let expected_indices = vec![0, 10, 20, 30, 50, 51, 52, 53]; - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + let assignments = vec![(cert.clone().into(), candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - let mut expected_indices_assignments = expected_indices.clone(); - let mut expected_indices_approvals = expected_indices.clone(); + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; - for _ in 0..expected_indices_assignments.len() { assert_matches!( overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_assignments, assignments); - - let pos = expected_indices_assignments.iter() - .position(|i| &peers[*i].0 == &sent_peers[0]) - .unwrap(); - expected_indices_assignments.remove(pos); + assert_eq!(assignment.tranche(), 0); } ); - } - for _ in 0..expected_indices_approvals.len() { + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, + _, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + protocol_v1::ApprovalDistributionMessage::Assignments(_) )) - )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_approvals, approvals); - - let pos = expected_indices_approvals.iter() - .position(|i| &peers[*i].0 == &sent_peers[0]) - .unwrap(); - - expected_indices_approvals.remove(pos); - } + )) => { } ); - } - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} -// test aggression L1 -#[test] -fn originator_aggression_l1() { - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); + // Add blocks until aggression L1 is triggered. + { + let mut parent_hash = hash; + for level in 0..aggression_l1_threshold { + let number = 1 + level + 1; // first block had number 1 + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - let peers = make_peers_and_authority_ids(100); + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - let mut state = State::default(); - state.aggression_config.resend_unfinalized_period = None; - let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); + parent_hash = hash; + } + } - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + // No-op on non-originator - // Connect all peers except omitted. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); +} - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; +// test aggression L2 on non-originator +#[test] +fn non_originator_aggression_l2() { + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + let peers = make_peers_and_authority_ids(100); - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + let mut state = state_without_reputation_delay(); + state.aggression_config.resend_unfinalized_period = None; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); + let aggression_l2_threshold = state.aggression_config.l2_threshold.unwrap(); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Connect all peers except omitted. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - let prev_sent_indices = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(_) - )) - )) => { - sent_peers.into_iter() - .filter_map(|sp| peers.iter().position(|p| &p.0 == &sp)) - .collect::>() - } - ); + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - _, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(_) - )) - )) => { } - ); - - // Add blocks until aggression L1 is triggered. - { - let mut parent_hash = hash; - for level in 0..aggression_l1_threshold { - let number = 1 + level + 1; // first block had number 1 - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1); - overseer_send(overseer, msg).await; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - parent_hash = hash; - } - } + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - let unsent_indices = - (0..peers.len()).filter(|i| !prev_sent_indices.contains(&i)).collect::>(); + let assignments = vec![(cert.clone(), candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - for _ in 0..unsent_indices.len() { + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; assert_matches!( overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_assignments, assignments); - - assert!(unsent_indices.iter() - .any(|i| &peers[*i].0 == &sent_peers[0])); + assert_eq!(assignment.tranche(), 0); } ); - } - for _ in 0..unsent_indices.len() { - assert_matches!( + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + + let prev_sent_indices = assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( sent_peers, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + protocol_v1::ApprovalDistributionMessage::Assignments(_) )) )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_approvals, approvals); - - assert!(unsent_indices.iter() - .any(|i| &peers[*i].0 == &sent_peers[0])); + sent_peers.into_iter() + .filter_map(|sp| peers.iter().position(|p| &p.0 == &sp)) + .collect::>() } ); - } - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} -// test aggression L1 -#[test] -fn non_originator_aggression_l1() { - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); + // Add blocks until aggression L1 is triggered. + let chain_head = { + let mut parent_hash = hash; + for level in 0..aggression_l1_threshold { + let number = 1 + level + 1; // first block had number 1 + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - let peers = make_peers_and_authority_ids(100); + let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1); + overseer_send(overseer, msg).await; - let mut state = state_without_reputation_delay(); - state.aggression_config.resend_unfinalized_period = None; - let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + parent_hash = hash; + } - // Connect all peers except omitted. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + parent_hash + }; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + // No-op on non-originator - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + // Add blocks until aggression L2 is triggered. + { + let mut parent_hash = chain_head; + for level in 0..aggression_l2_threshold - aggression_l1_threshold { + let number = aggression_l1_threshold + level + 1 + 1; // first block had number 1 + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - let assignments = vec![(cert.clone().into(), candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate( + aggression_l1_threshold + level + 1, + ); + overseer_send(overseer, msg).await; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); + parent_hash = hash; + } } - ); - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + // XY dimension - previously sent. + let unsent_indices = [0, 10, 20, 30, 50, 51, 52, 53] + .iter() + .cloned() + .filter(|i| !prev_sent_indices.contains(&i)) + .collect::>(); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - _, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(_) - )) - )) => { } - ); - - // Add blocks until aggression L1 is triggered. - { - let mut parent_hash = hash; - for level in 0..aggression_l1_threshold { - let number = 1 + level + 1; // first block had number 1 - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - parent_hash = hash; - } - } + for _ in 0..unsent_indices.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_assignments, assignments); - // No-op on non-originator + assert!(unsent_indices.iter() + .any(|i| &peers[*i].0 == &sent_peers[0])); + } + ); + } - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// test aggression L2 on non-originator +// Tests that messages propagate to the unshared dimension. #[test] -fn non_originator_aggression_l2() { +fn resends_messages_periodically() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); let mut state = state_without_reputation_delay(); - state.aggression_config.resend_unfinalized_period = None; + state.aggression_config.l1_threshold = None; + state.aggression_config.l2_threshold = None; + state.aggression_config.resend_unfinalized_period = Some(2); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Connect all peers. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); - let aggression_l2_threshold = state.aggression_config.l2_threshold.unwrap(); - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - // Connect all peers except omitted. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), candidate_index)]; - let assignments = vec![(cert.clone(), candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + { + let msg = + protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; - - let prev_sent_indices = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(_) - )) - )) => { - sent_peers.into_iter() - .filter_map(|sp| peers.iter().position(|p| &p.0 == &sp)) - .collect::>() - } - ); - - // Add blocks until aggression L1 is triggered. - let chain_head = { - let mut parent_hash = hash; - for level in 0..aggression_l1_threshold { - let number = 1 + level + 1; // first block had number 1 - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1); - overseer_send(overseer, msg).await; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - parent_hash = hash; - } + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; - parent_hash - }; + let expected_y = [50, 51, 52, 53]; - // No-op on non-originator - - // Add blocks until aggression L2 is triggered. - { - let mut parent_hash = chain_head; - for level in 0..aggression_l2_threshold - aggression_l1_threshold { - let number = aggression_l1_threshold + level + 1 + 1; // first block had number 1 - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate( - aggression_l1_threshold + level + 1, + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), expected_y.len() + 4); + for &i in &expected_y { + assert!( + sent_peers.contains(&peers[i].0), + "Message not sent to expected peer {}", + i, + ); + } + assert_eq!(sent_assignments, assignments); + } ); - overseer_send(overseer, msg).await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + }; + + let mut number = 1; + for _ in 0..10 { + // Add blocks until resend is done. + { + let mut parent_hash = hash; + for level in 0..2 { + number = number + 1; + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(2); + overseer_send(overseer, msg).await; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + parent_hash = hash; + } + } - parent_hash = hash; + let mut expected_y = vec![50, 51, 52, 53]; + + // Expect messages sent only to topology peers, one by one. + for _ in 0..expected_y.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), 1); + let expected_pos = expected_y.iter() + .position(|&i| &peers[i].0 == &sent_peers[0]) + .unwrap(); + + expected_y.remove(expected_pos); + assert_eq!(sent_assignments, assignments); + } + ); + } } - } - // XY dimension - previously sent. - let unsent_indices = [0, 10, 20, 30, 50, 51, 52, 53] - .iter() - .cloned() - .filter(|i| !prev_sent_indices.contains(&i)) - .collect::>(); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); +} + +/// Tests that peers correctly receive versioned messages. +#[test] +fn import_versioned_approval() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let peer_b = peers.get(1).unwrap().0; + let peer_c = peers.get(2).unwrap().0; + + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + let state = state_without_reputation_delay(); + let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // All peers are aware of relay parent. + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V2).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V2).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 1); + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![(candidate_hash, 0.into(), 0.into()); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.into(), + candidate_index.into(), + ), + ) + .await; - for _ in 0..unsent_indices.len() { assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, + peers, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) )) )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_assignments, assignments); - - assert!(unsent_indices.iter() - .any(|i| &peers[*i].0 == &sent_peers[0])); + assert_eq!(peers, vec![peer_b]); + assert_eq!(assignments.len(), 1); } ); - } - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} - -// Tests that messages propagate to the unshared dimension. -#[test] -fn resends_messages_periodically() { - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); - - let peers = make_peers_and_authority_ids(100); - let mut state = state_without_reputation_delay(); - state.aggression_config.l1_threshold = None; - state.aggression_config.l2_threshold = None; - state.aggression_config.resend_unfinalized_period = Some(2); - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - - // Connect all peers. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), candidate_index)]; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( + protocol_v2::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert!(peers.contains(&peer_a)); + assert!(peers.contains(&peer_c)); - { - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + assert_eq!(assignments.len(), 1); + } + ); - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; + // send the an approval from peer_a + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash], + validator_index, + ), + }; + let msg = protocol_v2::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v2(overseer, &peer_a, msg).await; + provide_session(overseer, session).await; assert_matches!( overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); + assert_eq!(Into::::into(vote), approval.into()); } ); - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; - let expected_y = [50, 51, 52, 53]; + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + // Peers b and c receive versioned approval messages. assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, + peers, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + protocol_v1::ApprovalDistributionMessage::Approvals(approvals) )) )) => { - assert_eq!(sent_peers.len(), expected_y.len() + 4); - for &i in &expected_y { - assert!( - sent_peers.contains(&peers[i].0), - "Message not sent to expected peer {}", - i, - ); - } - assert_eq!(sent_assignments, assignments); + assert_eq!(peers, vec![peer_b]); + assert_eq!(approvals.len(), 1); } ); - }; - - let mut number = 1; - for _ in 0..10 { - // Add blocks until resend is done. - { - let mut parent_hash = hash; - for level in 0..2 { - number = number + 1; - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(2); - overseer_send(overseer, msg).await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - parent_hash = hash; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( + protocol_v2::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert_eq!(peers, vec![peer_c]); + assert_eq!(approvals.len(), 1); } - } - - let mut expected_y = vec![50, 51, 52, 53]; - - // Expect messages sent only to topology peers, one by one. - for _ in 0..expected_y.len() { - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(sent_peers.len(), 1); - let expected_pos = expected_y.iter() - .position(|&i| &peers[i].0 == &sent_peers[0]) - .unwrap(); - - expected_y.remove(expected_pos); - assert_eq!(sent_assignments, assignments); - } - ); - } - } - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} - -/// Tests that peers correctly receive versioned messages. -#[test] -fn import_versioned_approval() { - let peers = make_peers_and_authority_ids(15); - let peer_a = peers.get(0).unwrap().0; - let peer_b = peers.get(1).unwrap().0; - let peer_c = peers.get(2).unwrap().0; + ); - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); - let state = state_without_reputation_delay(); - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // All peers are aware of relay parent. - setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V2).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V2).await; - - // Set up a gossip topology, where a, b, c and d are topology neighbors to the node under - // testing. - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + // send an obviously invalid approval + let approval = IndirectSignedApprovalVote { + block_hash: hash, + // Invalid candidate index, should not pass sanitization. + candidate_index: 16777284, + validator: validator_index, + signature: dummy_signature(), + }; + let msg = protocol_v2::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v2(overseer, &peer_a, msg).await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert.into(), candidate_index.into()), - ) - .await; + expect_reputation_change(overseer, &peer_a, COST_OVERSIZED_BITFIELD).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers, vec![peer_b]); - assert_eq!(assignments.len(), 1); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( - protocol_v2::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 2); - assert!(peers.contains(&peer_a)); - assert!(peers.contains(&peer_c)); - - assert_eq!(assignments.len(), 1); - } - ); - - // send the an approval from peer_a - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v2::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v2(overseer, &peer_a, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval.into()); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; - - // Peers b and c receive versioned approval messages. - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert_eq!(peers, vec![peer_b]); - assert_eq!(approvals.len(), 1); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( - protocol_v2::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert_eq!(peers, vec![peer_c]); - assert_eq!(approvals.len(), 1); - } - ); - - // send an obviously invalid approval - let approval = IndirectSignedApprovalVote { - block_hash: hash, - // Invalid candidate index, should not pass sanitization. - candidate_index: 16777284, - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v2::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v2(overseer, &peer_a, msg).await; - - expect_reputation_change(overseer, &peer_a, COST_OVERSIZED_BITFIELD).await; - - // send an obviously invalid approval - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - // Invalid candidates len, should not pass sanitization. - candidate_indices: 16777284.into(), - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_a, msg).await; + // send an obviously invalid approval + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + // Invalid candidates len, should not pass sanitization. + candidate_indices: 16777284.into(), + validator: validator_index, + signature: dummy_signature(), + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_a, msg).await; - expect_reputation_change(overseer, &peer_a, COST_OVERSIZED_BITFIELD).await; + expect_reputation_change(overseer, &peer_a, COST_OVERSIZED_BITFIELD).await; - virtual_overseer - }); + virtual_overseer + }, + ); } fn batch_test_round(message_count: usize) { @@ -3667,11 +4128,26 @@ fn batch_test_round(message_count: usize) { let (mut context, mut virtual_overseer) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool.clone()); - let subsystem = ApprovalDistribution::new(Default::default()); + let subsystem = ApprovalDistribution::new_with_clock( + Default::default(), + Default::default(), + Box::new(SystemClock {}), + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + ); let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(12345); let mut sender = context.sender().clone(); - let subsystem = - subsystem.run_inner(context, &mut state, REPUTATION_CHANGE_TEST_INTERVAL, &mut rng); + let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { + keystore: None, + session_cache_lru_size: DISPUTE_WINDOW.get(), + }); + + let subsystem = subsystem.run_inner( + context, + &mut state, + REPUTATION_CHANGE_TEST_INTERVAL, + &mut rng, + &mut session_info_provider, + ); let test_fut = async move { let overseer = &mut virtual_overseer; @@ -3817,3 +4293,420 @@ fn const_ensure_size_not_zero() { crate::ensure_size_not_zero(super::MAX_ASSIGNMENT_BATCH_SIZE); crate::ensure_size_not_zero(super::MAX_APPROVAL_BATCH_SIZE); } + +struct DummyClock; +impl Clock for DummyClock { + fn tick_now(&self) -> polkadot_node_primitives::approval::time::Tick { + 0 + } + + fn wait( + &self, + _tick: polkadot_node_primitives::approval::time::Tick, + ) -> std::pin::Pin + Send + 'static>> { + todo!() + } +} + +/// Subsystem rejects assignments too far into the future. +#[test] +fn subsystem_rejects_assignment_in_future() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(89) }), + Box::new(DummyClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), 0u32)]; + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, &peer_a, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + expect_reputation_change(overseer, &peer_a, COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE) + .await; + + virtual_overseer + }, + ); +} + +/// Subsystem rejects bad vrf assignments. +#[test] +fn subsystem_rejects_bad_assignments() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { + tranche: Err(InvalidAssignment(criteria::InvalidAssignmentReason::NullAssignment)), + }), + Box::new(DummyClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), 0u32)]; + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, &peer_a, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + expect_reputation_change(overseer, &peer_a, COST_INVALID_MESSAGE).await; + + virtual_overseer + }, + ); +} + +/// Subsystem rejects assignments that have invalid claimed candidates. +#[test] +fn subsystem_rejects_wrong_claimed_assignments() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(DummyClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + + // Claimed core 1 which does not have a candidate included on it, so the assignment + // should be rejected. + let cores = vec![1]; + let core_bitfield: CoreBitfield = cores + .iter() + .map(|index| CoreIndex(*index)) + .collect::>() + .try_into() + .unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield); + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), cores.try_into().unwrap())]; + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + expect_reputation_change(overseer, &peer_a, COST_INVALID_MESSAGE).await; + + virtual_overseer + }, + ); +} + +/// Subsystem accepts tranche0 duplicate assignments, sometimes on validator Compact tranche0 +/// assignment and Delay tranche assignments land on the same candidate. The delay tranche0 can be +/// safely ignored and we don't need to gossip it however, the compact tranche0 assignment should be +/// gossiped, because other candidates are included in it, this test makes sure this invariant is +/// upheld, see https://github.com/paritytech/polkadot/pull/2160#discussion_r557628699, for +/// this edge case. +#[test] +fn subsystem_accepts_tranche0_duplicate_assignments() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let peer_b = peers.get(1).unwrap().0; + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + let candidate_hash_first = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_second = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + let candidate_hash_third = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_fourth = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(DummyClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![], ValidationVersion::V3).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![ + (candidate_hash_first, 0.into(), 0.into()), + (candidate_hash_second, 1.into(), 1.into()), + (candidate_hash_third, 2.into(), 2.into()), + (candidate_hash_fourth, 3.into(), 3.into()), + ], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + + // 1. Compact assignment with multiple candidates, coming after delay assignment which + // covered just one of the candidate is still imported and gossiped. + let candidate_indices: CandidateBitfield = + vec![1 as CandidateIndex].try_into().unwrap(); + let core_bitfield = vec![CoreIndex(1)].try_into().unwrap(); + let cert = fake_assignment_cert_delay(hash, validator_index, core_bitfield); + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), candidate_indices.clone())]; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &candidate_indices); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + let candidate_indices: CandidateBitfield = + vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + let core_bitfield = vec![CoreIndex(0), CoreIndex(1)].try_into().unwrap(); + + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield); + + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), candidate_indices.clone())]; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &candidate_indices); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + // 2. Delay assignment coming after compact assignment that already covered the + // candidate is not gossiped anymore. + let candidate_indices: CandidateBitfield = + vec![2 as CandidateIndex, 3 as CandidateIndex].try_into().unwrap(); + let core_bitfield = vec![CoreIndex(2), CoreIndex(3)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield); + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), candidate_indices.clone())]; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &candidate_indices); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + let candidate_indices: CandidateBitfield = vec![3].try_into().unwrap(); + let core_bitfield = vec![CoreIndex(3)].try_into().unwrap(); + + let cert = fake_assignment_cert_delay(hash, validator_index, core_bitfield); + + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), candidate_indices.clone())]; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + + expect_reputation_change(overseer, &peer_a, COST_DUPLICATE_MESSAGE).await; + + virtual_overseer + }, + ); +} diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs index baaff9c7c9f6..26a6a907e324 100644 --- a/polkadot/node/overseer/src/lib.rs +++ b/polkadot/node/overseer/src/lib.rs @@ -581,6 +581,7 @@ pub struct Overseer { #[subsystem(blocking, message_capacity: 64000, ApprovalDistributionMessage, sends: [ NetworkBridgeTxMessage, ApprovalVotingMessage, + RuntimeApiMessage, ], can_receive_priority_messages)] approval_distribution: ApprovalDistribution, diff --git a/polkadot/node/primitives/Cargo.toml b/polkadot/node/primitives/Cargo.toml index cd642bf16ff9..7185205f905b 100644 --- a/polkadot/node/primitives/Cargo.toml +++ b/polkadot/node/primitives/Cargo.toml @@ -12,11 +12,13 @@ workspace = true [dependencies] bounded-vec = { workspace = true } futures = { workspace = true } +futures-timer = { workspace = true } polkadot-primitives = { workspace = true, default-features = true } codec = { features = ["derive"], workspace = true } sp-core = { workspace = true, default-features = true } sp-application-crypto = { workspace = true, default-features = true } sp-consensus-babe = { workspace = true, default-features = true } +sp-consensus-slots = { workspace = true } sp-keystore = { workspace = true, default-features = true } sp-maybe-compressed-blob = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } @@ -25,6 +27,7 @@ schnorrkel = { workspace = true, default-features = true } thiserror = { workspace = true } bitvec = { features = ["alloc"], workspace = true } serde = { features = ["derive"], workspace = true, default-features = true } +sc-keystore = { workspace = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] zstd = { version = "0.12.4", default-features = false } diff --git a/polkadot/node/primitives/src/approval/criteria.rs b/polkadot/node/primitives/src/approval/criteria.rs new file mode 100644 index 000000000000..0a1a0ee2367f --- /dev/null +++ b/polkadot/node/primitives/src/approval/criteria.rs @@ -0,0 +1,177 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Assignment criteria VRF generation and checking interfaces. + +use crate::approval::{ + v1::{DelayTranche, RelayVRFStory}, + v2::{AssignmentCertV2, CoreBitfield}, +}; +use codec::{Decode, Encode}; +use polkadot_primitives::{ + AssignmentId, CandidateHash, CoreIndex, GroupIndex, IndexedVec, SessionInfo, ValidatorIndex, +}; +use sc_keystore::LocalKeystore; + +use std::collections::HashMap; + +/// Details pertaining to our assignment on a block. +#[derive(Debug, Clone, Encode, Decode, PartialEq)] +pub struct OurAssignment { + cert: AssignmentCertV2, + tranche: DelayTranche, + validator_index: ValidatorIndex, + // Whether the assignment has been triggered already. + triggered: bool, +} + +impl OurAssignment { + /// Create a new `OurAssignment`. + pub fn new( + cert: AssignmentCertV2, + tranche: DelayTranche, + validator_index: ValidatorIndex, + triggered: bool, + ) -> Self { + OurAssignment { cert, tranche, validator_index, triggered } + } + /// Returns a reference to the assignment cert. + pub fn cert(&self) -> &AssignmentCertV2 { + &self.cert + } + + /// Returns the assignment cert. + pub fn into_cert(self) -> AssignmentCertV2 { + self.cert + } + + /// Returns the delay tranche of the assignment. + pub fn tranche(&self) -> DelayTranche { + self.tranche + } + + /// Returns the validator index of the assignment. + pub fn validator_index(&self) -> ValidatorIndex { + self.validator_index + } + + /// Returns whether the assignment has been triggered. + pub fn triggered(&self) -> bool { + self.triggered + } + + /// Marks the assignment as triggered. + pub fn mark_triggered(&mut self) { + self.triggered = true; + } +} + +/// Information about the world assignments are being produced in. +#[derive(Clone, Debug)] +pub struct Config { + /// The assignment public keys for validators. + pub assignment_keys: Vec, + /// The groups of validators assigned to each core. + pub validator_groups: IndexedVec>, + /// The number of availability cores used by the protocol during this session. + pub n_cores: u32, + /// The zeroth delay tranche width. + pub zeroth_delay_tranche_width: u32, + /// The number of samples we do of `relay_vrf_modulo`. + pub relay_vrf_modulo_samples: u32, + /// The number of delay tranches in total. + pub n_delay_tranches: u32, +} + +impl<'a> From<&'a SessionInfo> for Config { + fn from(s: &'a SessionInfo) -> Self { + Config { + assignment_keys: s.assignment_keys.clone(), + validator_groups: s.validator_groups.clone(), + n_cores: s.n_cores, + zeroth_delay_tranche_width: s.zeroth_delay_tranche_width, + relay_vrf_modulo_samples: s.relay_vrf_modulo_samples, + n_delay_tranches: s.n_delay_tranches, + } + } +} + +/// A trait for producing and checking assignments. +/// +/// Approval voting subsystem implements a a real implemention +/// for it and tests use a mock implementation. +pub trait AssignmentCriteria { + /// Compute the assignments for the given relay VRF story. + fn compute_assignments( + &self, + keystore: &LocalKeystore, + relay_vrf_story: RelayVRFStory, + config: &Config, + leaving_cores: Vec<(CandidateHash, CoreIndex, GroupIndex)>, + enable_v2_assignments: bool, + ) -> HashMap; + + /// Check the assignment cert for the given relay VRF story and returns the delay tranche. + fn check_assignment_cert( + &self, + claimed_core_bitfield: CoreBitfield, + validator_index: ValidatorIndex, + config: &Config, + relay_vrf_story: RelayVRFStory, + assignment: &AssignmentCertV2, + // Backing groups for each "leaving core". + backing_groups: Vec, + ) -> Result; +} + +/// Assignment invalid. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct InvalidAssignment(pub InvalidAssignmentReason); + +impl std::fmt::Display for InvalidAssignment { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Invalid Assignment: {:?}", self.0) + } +} + +impl std::error::Error for InvalidAssignment {} + +/// Failure conditions when checking an assignment cert. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum InvalidAssignmentReason { + /// The validator index is out of bounds. + ValidatorIndexOutOfBounds, + /// Sample index is out of bounds. + SampleOutOfBounds, + /// Core index is out of bounds. + CoreIndexOutOfBounds, + /// Invalid assignment key. + InvalidAssignmentKey, + /// Node is in backing group. + IsInBackingGroup, + /// Modulo core index mismatch. + VRFModuloCoreIndexMismatch, + /// Modulo output mismatch. + VRFModuloOutputMismatch, + /// Delay core index mismatch. + VRFDelayCoreIndexMismatch, + /// Delay output mismatch. + VRFDelayOutputMismatch, + /// Invalid arguments + InvalidArguments, + /// Assignment vrf check resulted in 0 assigned cores. + NullAssignment, +} diff --git a/polkadot/node/primitives/src/approval.rs b/polkadot/node/primitives/src/approval/mod.rs similarity index 98% rename from polkadot/node/primitives/src/approval.rs rename to polkadot/node/primitives/src/approval/mod.rs index 66883b33367b..79f4cfa9e0be 100644 --- a/polkadot/node/primitives/src/approval.rs +++ b/polkadot/node/primitives/src/approval/mod.rs @@ -16,6 +16,12 @@ //! Types relevant for approval. +/// Criteria for assignment. +pub mod criteria; + +/// Time utilities for approval voting. +pub mod time; + /// A list of primitives introduced in v1. pub mod v1 { use sp_consensus_babe as babe_primitives; @@ -25,8 +31,8 @@ pub mod v1 { use codec::{Decode, Encode}; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateIndex, CoreIndex, Hash, Header, SessionIndex, - ValidatorIndex, ValidatorSignature, + BlockNumber, CandidateHash, CandidateIndex, CoreIndex, GroupIndex, Hash, Header, + SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_application_crypto::ByteArray; @@ -128,11 +134,13 @@ pub mod v1 { pub parent_hash: Hash, /// The candidates included by the block. /// Note that these are not the same as the candidates that appear within the block body. - pub candidates: Vec, + pub candidates: Vec<(CandidateHash, CoreIndex, GroupIndex)>, /// The consensus slot of the block. pub slot: Slot, /// The session of the block. pub session: SessionIndex, + /// The vrf story. + pub vrf_story: RelayVRFStory, } /// Errors that can occur during the approvals protocol. diff --git a/polkadot/node/core/approval-voting/src/time.rs b/polkadot/node/primitives/src/approval/time.rs similarity index 95% rename from polkadot/node/core/approval-voting/src/time.rs rename to polkadot/node/primitives/src/approval/time.rs index 5c3e7e85a17a..465aae23c90e 100644 --- a/polkadot/node/core/approval-voting/src/time.rs +++ b/polkadot/node/primitives/src/approval/time.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Time utilities for approval voting. +//! Time utilities for approval voting subsystems. use futures::{ future::BoxFuture, @@ -23,7 +23,7 @@ use futures::{ Stream, StreamExt, }; -use polkadot_node_primitives::approval::v1::DelayTranche; +use crate::approval::v1::DelayTranche; use sp_consensus_slots::Slot; use std::{ collections::HashSet, @@ -33,11 +33,15 @@ use std::{ }; use polkadot_primitives::{Hash, ValidatorIndex}; +/// The duration of a single tick in milliseconds. pub const TICK_DURATION_MILLIS: u64 = 500; /// A base unit of time, starting from the Unix epoch, split into half-second intervals. pub type Tick = u64; +/// How far in the future a tick can be accepted. +pub const TICK_TOO_FAR_IN_FUTURE: Tick = 20; // 10 seconds. + /// A clock which allows querying of the current tick as well as /// waiting for a tick to be reached. pub trait Clock { @@ -50,6 +54,7 @@ pub trait Clock { /// Extension methods for clocks. pub trait ClockExt { + /// Returns the current tranche. fn tranche_now(&self, slot_duration_millis: u64, base_slot: Slot) -> DelayTranche; } @@ -124,7 +129,7 @@ impl DelayedApprovalTimer { /// /// Guarantees that if a timer already exits for the give block hash, /// no additional timer is started. - pub(crate) fn maybe_arm_timer( + pub fn maybe_arm_timer( &mut self, wait_until: Tick, clock: &dyn Clock, @@ -173,7 +178,7 @@ mod tests { use futures_timer::Delay; use polkadot_primitives::{Hash, ValidatorIndex}; - use crate::time::{Clock, SystemClock}; + use crate::approval::time::{Clock, SystemClock}; use super::DelayedApprovalTimer; diff --git a/polkadot/node/service/src/overseer.rs b/polkadot/node/service/src/overseer.rs index add5d239364d..0b57ff6e395f 100644 --- a/polkadot/node/service/src/overseer.rs +++ b/polkadot/node/service/src/overseer.rs @@ -20,7 +20,7 @@ use polkadot_overseer::{DummySubsystem, InitializedOverseerBuilder, SubsystemErr use sp_core::traits::SpawnNamed; use polkadot_availability_distribution::IncomingRequestReceivers; -use polkadot_node_core_approval_voting::Config as ApprovalVotingConfig; +use polkadot_node_core_approval_voting::{Config as ApprovalVotingConfig, RealAssignmentCriteria}; use polkadot_node_core_av_store::Config as AvailabilityConfig; use polkadot_node_core_candidate_validation::Config as CandidateValidationConfig; use polkadot_node_core_chain_selection::Config as ChainSelectionConfig; @@ -309,7 +309,11 @@ where Metrics::register(registry)?, rand::rngs::StdRng::from_entropy(), )) - .approval_distribution(ApprovalDistributionSubsystem::new(Metrics::register(registry)?)) + .approval_distribution(ApprovalDistributionSubsystem::new( + Metrics::register(registry)?, + approval_voting_config.slot_duration_millis, + Arc::new(RealAssignmentCriteria {}), + )) .approval_voting(ApprovalVotingSubsystem::with_config( approval_voting_config, parachains_db.clone(), diff --git a/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs index ca58875c8139..4b2b91696824 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs @@ -16,11 +16,11 @@ use crate::configuration::TestAuthorities; use itertools::Itertools; -use polkadot_node_core_approval_voting::time::{Clock, SystemClock, Tick}; use polkadot_node_network_protocol::{ grid_topology::{SessionGridTopology, TopologyPeerInfo}, View, }; +use polkadot_node_primitives::approval::time::{Clock, SystemClock, Tick}; use polkadot_node_subsystem_types::messages::{ network_bridge_event::NewGossipTopology, ApprovalDistributionMessage, NetworkBridgeEvent, }; diff --git a/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs index 6d3e7dd92db1..da25a3bf3b79 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs @@ -28,16 +28,15 @@ use crate::{ use codec::Encode; use futures::SinkExt; use itertools::Itertools; -use polkadot_node_core_approval_voting::{ - criteria::{compute_assignments, Config}, - time::tranche_to_tick, -}; +use polkadot_node_core_approval_voting::criteria::{compute_assignments, Config}; + use polkadot_node_network_protocol::{ grid_topology::{GridNeighbors, RandomRouting, RequiredRouting, SessionGridTopology}, v3 as protocol_v3, }; use polkadot_node_primitives::approval::{ self, + time::tranche_to_tick, v2::{CoreBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2}, }; use polkadot_primitives::{ diff --git a/polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs b/polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs index 77ba80d4b2bb..709d56d52f0b 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs @@ -16,7 +16,7 @@ use crate::approval::{ApprovalTestState, PastSystemClock, LOG_TARGET, SLOT_DURATION_MILLIS}; use futures::FutureExt; -use polkadot_node_core_approval_voting::time::{slot_number_to_tick, Clock, TICK_DURATION_MILLIS}; +use polkadot_node_primitives::approval::time::{slot_number_to_tick, Clock, TICK_DURATION_MILLIS}; use polkadot_node_subsystem::{overseer, SpawnedSubsystem, SubsystemError}; use polkadot_node_subsystem_types::messages::ChainSelectionMessage; diff --git a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs index 404df2f8c653..f05d061f3fde 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs @@ -49,9 +49,13 @@ use itertools::Itertools; use orchestra::TimeoutExt; use overseer::{metrics::Metrics as OverseerMetrics, MetricsTrait}; use polkadot_approval_distribution::ApprovalDistribution; +use polkadot_node_primitives::approval::time::{ + slot_number_to_tick, tick_to_slot_number, Clock, ClockExt, SystemClock, +}; + use polkadot_node_core_approval_voting::{ - time::{slot_number_to_tick, tick_to_slot_number, Clock, ClockExt, SystemClock}, ApprovalVotingSubsystem, Config as ApprovalVotingConfig, Metrics as ApprovalVotingMetrics, + RealAssignmentCriteria, }; use polkadot_node_network_protocol::v3 as protocol_v3; use polkadot_node_primitives::approval::{self, v1::RelayVRFStory}; @@ -813,8 +817,12 @@ fn build_overseer( Box::new(system_clock.clone()), ); - let approval_distribution = - ApprovalDistribution::new(Metrics::register(Some(&dependencies.registry)).unwrap()); + let approval_distribution = ApprovalDistribution::new_with_clock( + Metrics::register(Some(&dependencies.registry)).unwrap(), + SLOT_DURATION_MILLIS, + Box::new(system_clock.clone()), + Arc::new(RealAssignmentCriteria {}), + ); let mock_chain_api = MockChainApi::new(state.build_chain_api_state()); let mock_chain_selection = MockChainSelection { state: state.clone(), clock: system_clock }; let mock_runtime_api = MockRuntimeApi::new( diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index d067ca468011..854a9da158be 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -33,7 +33,7 @@ use polkadot_node_network_protocol::{ }; use polkadot_node_primitives::{ approval::{ - v1::BlockApprovalMeta, + v1::{BlockApprovalMeta, DelayTranche}, v2::{CandidateBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2}, }, AvailableData, BabeEpoch, BlockWeight, CandidateVotes, CollationGenerationConfig, @@ -879,7 +879,7 @@ pub enum CollationGenerationMessage { SubmitCollation(SubmitCollationParams), } -/// The result type of [`ApprovalVotingMessage::CheckAndImportAssignment`] request. +/// The result type of [`ApprovalVotingMessage::ImportAssignment`] request. #[derive(Debug, Clone, PartialEq, Eq)] pub enum AssignmentCheckResult { /// The vote was accepted and should be propagated onwards. @@ -892,7 +892,7 @@ pub enum AssignmentCheckResult { Bad(AssignmentCheckError), } -/// The error result type of [`ApprovalVotingMessage::CheckAndImportAssignment`] request. +/// The error result type of [`ApprovalVotingMessage::ImportAssignment`] request. #[derive(Error, Debug, Clone, PartialEq, Eq)] #[allow(missing_docs)] pub enum AssignmentCheckError { @@ -912,7 +912,7 @@ pub enum AssignmentCheckError { InvalidBitfield(usize), } -/// The result type of [`ApprovalVotingMessage::CheckAndImportApproval`] request. +/// The result type of [`ApprovalVotingMessage::ImportApproval`] request. #[derive(Debug, Clone, PartialEq, Eq)] pub enum ApprovalCheckResult { /// The vote was accepted and should be propagated onwards. @@ -921,7 +921,7 @@ pub enum ApprovalCheckResult { Bad(ApprovalCheckError), } -/// The error result type of [`ApprovalVotingMessage::CheckAndImportApproval`] request. +/// The error result type of [`ApprovalVotingMessage::ImportApproval`] request. #[derive(Error, Debug, Clone, PartialEq, Eq)] #[allow(missing_docs)] pub enum ApprovalCheckError { @@ -969,21 +969,68 @@ pub struct HighestApprovedAncestorBlock { pub descriptions: Vec, } +/// A checked indirect assignment, the crypto for the cert has been validated +/// and the `candidate_bitfield` is correctly claimed at `delay_tranche`. +#[derive(Debug)] +pub struct CheckedIndirectAssignment { + assignment: IndirectAssignmentCertV2, + candidate_indices: CandidateBitfield, + tranche: DelayTranche, +} + +impl CheckedIndirectAssignment { + /// Builds a checked assignment from an assignment that was checked to be valid for the + /// `claimed_candidate_indices` at the give tranche + pub fn from_checked( + assignment: IndirectAssignmentCertV2, + claimed_candidate_indices: CandidateBitfield, + tranche: DelayTranche, + ) -> Self { + Self { assignment, candidate_indices: claimed_candidate_indices, tranche } + } + + /// Returns the indirect assignment. + pub fn assignment(&self) -> &IndirectAssignmentCertV2 { + &self.assignment + } + + /// Returns the candidate bitfield claimed by the assignment. + pub fn candidate_indices(&self) -> &CandidateBitfield { + &self.candidate_indices + } + + /// Returns the tranche this assignment is claimed at. + pub fn tranche(&self) -> DelayTranche { + self.tranche + } +} + +/// A checked indirect signed approval vote. +/// +/// The crypto for the vote has been validated and the signature can be trusted as being valid and +/// to correspond to the `validator_index` inside the structure. +#[derive(Debug, derive_more::Deref, derive_more::Into)] +pub struct CheckedIndirectSignedApprovalVote(IndirectSignedApprovalVoteV2); + +impl CheckedIndirectSignedApprovalVote { + /// Builds a checked vote from a vote that was checked to be valid and correctly signed. + pub fn from_checked(vote: IndirectSignedApprovalVoteV2) -> Self { + Self(vote) + } +} + /// Message to the Approval Voting subsystem. #[derive(Debug)] pub enum ApprovalVotingMessage { - /// Check if the assignment is valid and can be accepted by our view of the protocol. - /// Should not be sent unless the block hash is known. - CheckAndImportAssignment( - IndirectAssignmentCertV2, - CandidateBitfield, - oneshot::Sender, - ), - /// Check if the approval vote is valid and can be accepted by our view of the - /// protocol. + /// Import an assignment into the approval-voting database. + /// + /// Should not be sent unless the block hash is known and the VRF assignment checks out. + ImportAssignment(CheckedIndirectAssignment, Option>), + /// Import an approval vote into approval-voting database /// - /// Should not be sent unless the block hash within the indirect vote is known. - CheckAndImportApproval(IndirectSignedApprovalVoteV2, oneshot::Sender), + /// Should not be sent unless the block hash within the indirect vote is known, vote is + /// correctly signed and we had a previous assignment for the candidate. + ImportApproval(CheckedIndirectSignedApprovalVote, Option>), /// Returns the highest possible ancestor hash of the provided block hash which is /// acceptable to vote on finality for. /// The `BlockNumber` provided is the number of the block's ancestor which is the diff --git a/polkadot/roadmap/implementers-guide/src/node/approval/approval-distribution.md b/polkadot/roadmap/implementers-guide/src/node/approval/approval-distribution.md index c987b7fe5bea..e09418c7d5ab 100644 --- a/polkadot/roadmap/implementers-guide/src/node/approval/approval-distribution.md +++ b/polkadot/roadmap/implementers-guide/src/node/approval/approval-distribution.md @@ -66,8 +66,8 @@ Input: * `OverseerSignal::BlockFinalized` Output: - * `ApprovalVotingMessage::CheckAndImportAssignment` - * `ApprovalVotingMessage::CheckAndImportApproval` + * `ApprovalVotingMessage::ImportAssignment` + * `ApprovalVotingMessage::ImportApproval` * `NetworkBridgeMessage::SendValidationMessage::ApprovalDistribution` ## Functionality @@ -253,8 +253,30 @@ The algorithm is the following: boost, add the fingerprint to the peer's knowledge only if it knows about the block and return. Note that we must do this after checking for out-of-view and if the peers knows about the block to avoid being spammed. If we did this check earlier, a peer could provide data out-of-view repeatedly and be rewarded for it. - * Dispatch `ApprovalVotingMessage::CheckAndImportAssignment(assignment)` and wait for the response. + * Check the assignment certificate is valid. + * If the cert kind is `RelayVRFModulo`, then the certificate is valid as long as `sample < + session_info.relay_vrf_samples` and the VRF is valid for the validator's key with the input + `block_entry.relay_vrf_story ++ sample.encode()` as described with + [the approvals protocol section](../../protocol-approval.md#assignment-criteria). We set + `core_index = vrf.make_bytes().to_u32() % session_info.n_cores`. If the `BlockEntry` causes + inclusion of a candidate at `core_index`, then this is a valid assignment for the candidate + at `core_index` and has delay tranche 0. Otherwise, it can be ignored. + * If the cert kind is `RelayVRFModuloCompact`, then the certificate is valid as long as the VRF + is valid for the validator's key with the input `block_entry.relay_vrf_story ++ relay_vrf_samples.encode()` + as described with [the approvals protocol section](../../protocol-approval.md#assignment-criteria). + We enforce that all `core_bitfield` indices are included in the set of the core indices sampled from the + VRF Output. The assignment is considered a valid tranche0 assignment for all claimed candidates if all + `core_bitfield` indices match the core indices where the claimed candidates were included at. + * If the cert kind is `RelayVRFDelay`, then we check if the VRF is valid for the validator's key with the + input `block_entry.relay_vrf_story ++ cert.core_index.encode()` as described in [the approvals protocol + section](../../protocol-approval.md#assignment-criteria). The cert can be ignored if the block did not + cause inclusion of a candidate on that core index. Otherwise, this is a valid assignment for the included + candidate. The delay tranche for the assignment is determined by reducing + `(vrf.make_bytes().to_u64() % (session_info.n_delay_tranches + session_info.zeroth_delay_tranche_width)).saturating_sub(session_info.zeroth_delay_tranche_width)`. + * We also check that the core index derived by the output is covered by the `VRFProof` by means of an auxiliary signature. + * If the delay tranche is too far in the future, return `AssignmentCheckResult::TooFarInFuture`. * If the result is `AssignmentCheckResult::Accepted` + * Dispatch `ApprovalVotingMessage::ImportAssignment(assignment)` to approval-voting to import the assignment. * If the vote was accepted but not duplicate, give the peer a positive reputation boost * add the fingerprint to both our and the peer's knowledge in the `BlockEntry`. Note that we only doing this after making sure we have the right fingerprint. @@ -293,10 +315,12 @@ Imports an approval signature referenced by block hash and candidate index: boost, add the fingerprint to the peer's knowledge only if it knows about the block and return. Note that we must do this after checking for out-of-view to avoid being spammed. If we did this check earlier, a peer could provide data out-of-view repeatedly and be rewarded for it. - * Dispatch `ApprovalVotingMessage::CheckAndImportApproval(approval)` and wait for the response. - * If the result is `VoteCheckResult::Accepted(())`: + * Construct a `SignedApprovalVote` using the candidates hashes and check against the validator's approval key, + based on the session info of the block. If invalid or no such validator, return `Err(InvalidVoteError)`. + * If the result of checking the signature is `Ok(CheckedIndirectSignedApprovalVote)`: + * Dispatch `ApprovalVotingMessage::ImportApproval(approval)` . * Give the peer a positive reputation boost and add the fingerprint to both our and the peer's knowledge. - * If the result is `VoteCheckResult::Bad`: + * If the result is `Err(InvalidVoteError)`: * Report the peer and return. * Load the candidate entry for the given candidate index. It should exist unless there is a logic error in the approval voting subsystem. diff --git a/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md b/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md index 9b4082c49e2f..40394412d81b 100644 --- a/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md +++ b/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md @@ -39,8 +39,8 @@ been approved. ## Protocol Input: - * `ApprovalVotingMessage::CheckAndImportAssignment` - * `ApprovalVotingMessage::CheckAndImportApproval` + * `ApprovalVotingMessage::ImportAssignment` + * `ApprovalVotingMessage::ImportApproval` * `ApprovalVotingMessage::ApprovedAncestor` Output: @@ -266,39 +266,17 @@ On receiving an `OverseerSignal::ActiveLeavesUpdate(update)`: 0-tranche assignment, kick off approval work, and schedule the next delay. * Dispatch an `ApprovalDistributionMessage::NewBlocks` with the meta information filled out for each new block. -#### `ApprovalVotingMessage::CheckAndImportAssignment` +#### `ApprovalVotingMessage::ImportAssignment` -On receiving a `ApprovalVotingMessage::CheckAndImportAssignment` message, we check the assignment cert against the block -entry. The cert itself contains information necessary to determine the candidate that is being assigned-to. In detail: +On receiving a `ApprovalVotingMessage::ImportAssignment` message, we assume the assignment cert itself has already been +checked to be valid we proceed then to import the assignment inside the block entry. The cert itself contains +information necessary to determine the candidate that is being assigned-to. In detail: * Load the `BlockEntry` for the relay-parent referenced by the message. If there is none, return `AssignmentCheckResult::Bad`. * Fetch the `SessionInfo` for the session of the block * Determine the assignment key of the validator based on that. * Determine the claimed core index by looking up the candidate with given index in `block_entry.candidates`. Return `AssignmentCheckResult::Bad` if missing. - * Check the assignment cert - * If the cert kind is `RelayVRFModulo`, then the certificate is valid as long as `sample < - session_info.relay_vrf_samples` and the VRF is valid for the validator's key with the input - `block_entry.relay_vrf_story ++ sample.encode()` as described with - [the approvals protocol section](../../protocol-approval.md#assignment-criteria). We set - `core_index = vrf.make_bytes().to_u32() % session_info.n_cores`. If the `BlockEntry` causes - inclusion of a candidate at `core_index`, then this is a valid assignment for the candidate - at `core_index` and has delay tranche 0. Otherwise, it can be ignored. - * If the cert kind is `RelayVRFModuloCompact`, then the certificate is valid as long as the VRF - is valid for the validator's key with the input `block_entry.relay_vrf_story ++ relay_vrf_samples.encode()` - as described with [the approvals protocol section](../../protocol-approval.md#assignment-criteria). - We enforce that all `core_bitfield` indices are included in the set of the core indices sampled from the - VRF Output. The assignment is considered a valid tranche0 assignment for all claimed candidates if all - `core_bitfield` indices match the core indices where the claimed candidates were included at. - - * If the cert kind is `RelayVRFDelay`, then we check if the VRF is valid for the validator's key with the - input `block_entry.relay_vrf_story ++ cert.core_index.encode()` as described in [the approvals protocol - section](../../protocol-approval.md#assignment-criteria). The cert can be ignored if the block did not - cause inclusion of a candidate on that core index. Otherwise, this is a valid assignment for the included - candidate. The delay tranche for the assignment is determined by reducing - `(vrf.make_bytes().to_u64() % (session_info.n_delay_tranches + session_info.zeroth_delay_tranche_width)).saturating_sub(session_info.zeroth_delay_tranche_width)`. - * We also check that the core index derived by the output is covered by the `VRFProof` by means of an auxiliary signature. - * If the delay tranche is too far in the future, return `AssignmentCheckResult::TooFarInFuture`. * Import the assignment. * Load the candidate in question and access the `approval_entry` for the block hash the cert references. * Ignore if we already observe the validator as having been assigned. @@ -309,14 +287,12 @@ entry. The cert itself contains information necessary to determine the candidate * [Schedule a wakeup](#schedule-wakeup) for this block, candidate pair. * return the appropriate `AssignmentCheckResult` on the response channel. -#### `ApprovalVotingMessage::CheckAndImportApproval` +#### `ApprovalVotingMessage::ImportApproval` -On receiving a `CheckAndImportApproval(indirect_approval_vote, response_channel)` message: +On receiving a `ImportApproval(indirect_approval_vote, response_channel)` message: * Fetch the `BlockEntry` from the indirect approval vote's `block_hash`. If none, return `ApprovalCheckResult::Bad`. * Fetch all `CandidateEntry` from the indirect approval vote's `candidate_indices`. If the block did not trigger inclusion of enough candidates, return `ApprovalCheckResult::Bad`. - * Construct a `SignedApprovalVote` using the candidates hashes and check against the validator's approval key, - based on the session info of the block. If invalid or no such validator, return `ApprovalCheckResult::Bad`. * Send `ApprovalCheckResult::Accepted` * [Import the checked approval vote](#import-checked-approval) for all candidates diff --git a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md index c82d89d2d879..317f339ddd4e 100644 --- a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md +++ b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md @@ -111,21 +111,15 @@ pub enum ApprovalCheckError { } enum ApprovalVotingMessage { - /// Check if the assignment is valid and can be accepted by our view of the protocol. - /// Should not be sent unless the block hash is known. - CheckAndImportAssignment( - IndirectAssignmentCert, - CandidateIndex, // The index of the candidate included in the block. - ResponseChannel, - ), - /// Check if the approval vote is valid and can be accepted by our view of the - /// protocol. - /// - /// Should not be sent unless the block hash within the indirect vote is known. - CheckAndImportApproval( - IndirectSignedApprovalVote, - ResponseChannel, - ), + /// Import an assignment into the approval-voting database. + /// + /// Should not be sent unless the block hash is known and the VRF assignment checks out. + ImportAssignment(CheckedIndirectAssignment, Option>), + /// Import an approval vote into approval-voting database + /// + /// Should not be sent unless the block hash within the indirect vote is known, vote is + /// correctly signed and we had a previous assignment for the candidate. + ImportApproval(CheckedIndirectSignedApprovalVote, Option>), /// Returns the highest possible ancestor hash of the provided block hash which is /// acceptable to vote on finality for. Along with that, return the lists of candidate hashes /// which appear in every block from the (non-inclusive) base number up to (inclusive) the specified diff --git a/prdoc/pr_4928.prdoc b/prdoc/pr_4928.prdoc new file mode 100644 index 000000000000..9935652dc511 --- /dev/null +++ b/prdoc/pr_4928.prdoc @@ -0,0 +1,28 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Move assignment VRF check and vote signature in approval-distribution + +doc: + - audience: Node Dev + description: | + This PR moves the assignment VRF check and approval vote signature from approval-voting into approval-distribution. + This optimization creates a better pipelining for processing new messages, because in this way approval-distribution + does not have to wait after approval-voting anymore and it will just notify it when it received a valid message that + is ready to be imported. + +crates: + - name: polkadot-node-subsystem-types + bump: major + - name: polkadot-approval-distribution + bump: major + - name: polkadot-node-core-approval-voting + bump: major + - name: polkadot-node-primitives + bump: major + - name: polkadot-service + bump: major + - name: polkadot-subsystem-bench + bump: major + - name: polkadot-overseer + bump: patch \ No newline at end of file From a8ca172daba31e8fae19d160c3b7e3d5f4370faa Mon Sep 17 00:00:00 2001 From: Ron Date: Mon, 2 Sep 2024 17:00:34 +0800 Subject: [PATCH 206/382] Polkadot assets on ethereum (#128) * Register token from polkadot * Extract AssetRegistrarMetadata * ReserveTransfer from AssetHub * Transfer DOT back to AssetHub * Fix breaking tests * Fix register token * Increase dispatch_gas to cover the actual cost for register token * Add ConvertAssetId to outbound router * Rename to SendForeignToken * Update cost * Move Command.RegisterToken to top level * Use VersionedLocation for storage * Use versioned location * TokenIdOf follow the same pattern as AgentIdOf * Rename MintToken to TransferNativeToken * Rename as SendNativeToken * More refactoring * Remove AgentExecuteCommand * Remove TokenExists check * Update cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs Co-authored-by: Clara van Staden * Benchmark register_token * More checks for the events * More tests for send native token * More tests describe tokenID * More asset identifiers * Add LocationToToken & Always use VersionedLocation * Fix breaking tests * Update gas cost for registering polkadot token * Add AgentExecuteCommand back for compatibility * Split into 2 tests each covers one direction * Revert the change on AH * Switch to native token on penpal for the integration * Make fee asset as configuration parameter of the Channel * Revert fee_asset_id as channel property * Short epoch * Fix integration tests * Fix format * Cleanup * Register relay token * Use relay token as fee asset * Fix clippy * Fix missing dependency * Remove reanchored prefix from asset_id * Fix test * Fix the instruction * Fix test * Multi hop transfer * Fix tests * allow bridge hub assets * Register token from BH directly and remove force_register_token * Decrease gas estimation for PNA * Cleanup * Store Location rather than VersionedLocation * Fix test * Add more tests * Revamp reanchor logic * Improve reanchor & Fix tests * Use secondary governance channel to register PNA * Rename as asset location * Use BoundVec limit size of name&symbol * Describe location of PNA & more tests * Add test * Fix taplo * More tests * Cleanup * Format code * Batch rename token command * Remove agent_id * Rename as AssetMetadata * Add test for penpal native token --------- Co-authored-by: Clara van Staden Co-authored-by: Alistair Singh --- Cargo.lock | 1 + .../pallets/inbound-queue/src/lib.rs | 24 +- .../pallets/inbound-queue/src/mock.rs | 20 +- .../pallets/outbound-queue/src/mock.rs | 10 +- .../pallets/system/src/benchmarking.rs | 23 + bridges/snowbridge/pallets/system/src/lib.rs | 82 +- .../snowbridge/pallets/system/src/tests.rs | 24 +- .../snowbridge/pallets/system/src/weights.rs | 11 + bridges/snowbridge/primitives/core/Cargo.toml | 2 + bridges/snowbridge/primitives/core/src/lib.rs | 54 +- .../primitives/core/src/outbound.rs | 57 ++ .../snowbridge/primitives/core/src/tests.rs | 41 +- .../primitives/router/src/inbound/mod.rs | 175 ++++- .../primitives/router/src/inbound/tests.rs | 66 +- .../primitives/router/src/outbound/mod.rs | 174 ++++- .../primitives/router/src/outbound/tests.rs | 361 +++++---- .../assets/asset-hub-westend/src/genesis.rs | 8 + .../bridges/bridge-hub-westend/src/lib.rs | 1 + .../emulated/common/src/lib.rs | 9 + .../bridges/bridge-hub-westend/src/lib.rs | 10 +- .../bridge-hub-westend/src/tests/mod.rs | 2 + .../src/tests/snowbridge.rs | 702 +++++++++++++++++- .../src/bridge_to_ethereum_config.rs | 206 ++++- .../src/weights/snowbridge_pallet_system.rs | 10 + .../src/bridge_to_ethereum_config.rs | 7 + .../src/weights/snowbridge_pallet_system.rs | 10 + 26 files changed, 1834 insertions(+), 256 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d8f4cef1420..916c0652e96a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19634,6 +19634,7 @@ dependencies = [ "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", + "staging-xcm-executor", ] [[package]] diff --git a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs index 4a1486204eb0..9ccb9f4b64de 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs @@ -62,9 +62,8 @@ use snowbridge_core::{ sibling_sovereign_account, BasicOperatingMode, Channel, ChannelId, ParaId, PricingParameters, StaticLookup, }; -use snowbridge_router_primitives::{ - inbound, - inbound::{ConvertMessage, ConvertMessageError}, +use snowbridge_router_primitives::inbound::{ + ConvertMessage, ConvertMessageError, VersionedMessage, }; use sp_runtime::{traits::Saturating, SaturatedConversion, TokenError}; @@ -86,6 +85,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + use sp_core::H256; #[pallet::pallet] pub struct Pallet(_); @@ -276,12 +276,12 @@ pub mod pallet { T::Token::transfer(&sovereign_account, &who, amount, Preservation::Preserve)?; } + // Decode payload into VersionMessage + let message = VersionedMessage::decode_all(&mut envelope.payload.as_ref()) + .map_err(|_| Error::::InvalidPayload)?; + // Decode message into XCM - let (xcm, fee) = - match inbound::VersionedMessage::decode_all(&mut envelope.payload.as_ref()) { - Ok(message) => Self::do_convert(envelope.message_id, message)?, - Err(_) => return Err(Error::::InvalidPayload.into()), - }; + let (xcm, fee) = Self::do_convert(envelope.message_id, message.clone())?; log::info!( target: LOG_TARGET, @@ -323,12 +323,10 @@ pub mod pallet { impl Pallet { pub fn do_convert( message_id: H256, - message: inbound::VersionedMessage, + message: VersionedMessage, ) -> Result<(Xcm<()>, BalanceOf), Error> { - let (mut xcm, fee) = - T::MessageConverter::convert(message).map_err(|e| Error::::ConvertMessage(e))?; - // Append the message id as an XCM topic - xcm.inner_mut().extend(vec![SetTopic(message_id.into())]); + let (xcm, fee) = T::MessageConverter::convert(message_id, message) + .map_err(|e| Error::::ConvertMessage(e))?; Ok((xcm, fee)) } diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index a031676c6076..5e1a045f8476 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -10,12 +10,12 @@ use snowbridge_beacon_primitives::{ use snowbridge_core::{ gwei, inbound::{Log, Proof, VerificationError}, - meth, Channel, ChannelId, PricingParameters, Rewards, StaticLookup, + meth, Channel, ChannelId, PricingParameters, Rewards, StaticLookup, TokenId, }; use snowbridge_router_primitives::inbound::MessageToXcm; use sp_core::{H160, H256}; use sp_runtime::{ - traits::{IdentifyAccount, IdentityLookup, Verify}, + traits::{IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, BuildStorage, FixedU128, MultiSignature, }; use sp_std::{convert::From, default::Default}; @@ -111,6 +111,9 @@ parameter_types! { pub const SendTokenExecutionFee: u128 = 1_000_000_000; pub const InitialFund: u128 = 1_000_000_000_000; pub const InboundQueuePalletInstance: u8 = 80; + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(Westend), Parachain(1013)].into(); + pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]); } #[cfg(feature = "runtime-benchmarks")] @@ -204,6 +207,16 @@ impl TransactAsset for SuccessfulTransactor { } } +pub struct MockTokenIdConvert; +impl MaybeEquivalence for MockTokenIdConvert { + fn convert(_id: &TokenId) -> Option { + Some(Location::parent()) + } + fn convert_back(_loc: &Location) -> Option { + None + } +} + impl inbound_queue::Config for Test { type RuntimeEvent = RuntimeEvent; type Verifier = MockVerifier; @@ -217,6 +230,9 @@ impl inbound_queue::Config for Test { InboundQueuePalletInstance, AccountId, Balance, + MockTokenIdConvert, + UniversalLocation, + GlobalAssetHub, >; type PricingParameters = Parameters; type ChannelLookup = MockChannelLookup; diff --git a/bridges/snowbridge/pallets/outbound-queue/src/mock.rs b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs index d65a96e2702d..0b34893333e4 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs @@ -164,13 +164,11 @@ pub fn mock_message(sibling_para_id: u32) -> Message { Message { id: None, channel_id: ParaId::from(sibling_para_id).into(), - command: Command::AgentExecute { + command: Command::TransferNativeToken { agent_id: Default::default(), - command: AgentExecuteCommand::TransferToken { - token: Default::default(), - recipient: Default::default(), - amount: 0, - }, + token: Default::default(), + recipient: Default::default(), + amount: 0, }, } } diff --git a/bridges/snowbridge/pallets/system/src/benchmarking.rs b/bridges/snowbridge/pallets/system/src/benchmarking.rs index ef908ad6a3f9..3a1540ebc826 100644 --- a/bridges/snowbridge/pallets/system/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/system/src/benchmarking.rs @@ -159,6 +159,29 @@ mod benchmarks { Ok(()) } + #[benchmark] + fn register_token() -> Result<(), BenchmarkError> { + let caller: T::AccountId = whitelisted_caller(); + + let amount: BalanceOf = + (10_000_000_000_000_u128).saturated_into::().saturated_into(); + + T::Token::mint_into(&caller, amount)?; + + let relay_token_asset_id: Location = Location::new(1, [GlobalConsensus(Westend)]); + let asset = Box::new(VersionedLocation::V4(relay_token_asset_id)); + let asset_metadata = AssetMetadata { + name: "wnd".as_bytes().to_vec().try_into().unwrap(), + symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }; + + #[extrinsic_call] + _(RawOrigin::Signed(caller), asset, asset_metadata); + + Ok(()) + } + impl_benchmark_test_suite!( SnowbridgeControl, crate::mock::new_test_ext(true), diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 39c73e3630e7..4ed14d9c0ca7 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -63,13 +63,16 @@ use frame_system::pallet_prelude::*; use snowbridge_core::{ meth, outbound::{Command, Initializer, Message, OperatingMode, SendError, SendMessage}, - sibling_sovereign_account, AgentId, Channel, ChannelId, ParaId, - PricingParameters as PricingParametersRecord, PRIMARY_GOVERNANCE_CHANNEL, + sibling_sovereign_account, AgentId, AssetMetadata, Channel, ChannelId, ParaId, + PricingParameters as PricingParametersRecord, TokenId, TokenIdOf, PRIMARY_GOVERNANCE_CHANNEL, SECONDARY_GOVERNANCE_CHANNEL, }; use sp_core::{RuntimeDebug, H160, H256}; use sp_io::hashing::blake2_256; -use sp_runtime::{traits::BadOrigin, DispatchError, SaturatedConversion}; +use sp_runtime::{ + traits::{BadOrigin, MaybeEquivalence}, + DispatchError, SaturatedConversion, +}; use sp_std::prelude::*; use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; @@ -99,7 +102,7 @@ where } /// Hash the location to produce an agent id -fn agent_id_of(location: &Location) -> Result { +pub fn agent_id_of(location: &Location) -> Result { T::AgentIdOf::convert_location(location).ok_or(Error::::LocationConversionFailed.into()) } @@ -211,6 +214,11 @@ pub mod pallet { PricingParametersChanged { params: PricingParametersOf, }, + /// Register token + RegisterToken { + asset_id: VersionedLocation, + token_id: H256, + }, } #[pallet::error] @@ -226,6 +234,7 @@ pub mod pallet { InvalidTokenTransferFees, InvalidPricingParameters, InvalidUpgradeParameters, + TokenExists, } /// The set of registered agents @@ -243,6 +252,15 @@ pub mod pallet { pub type PricingParameters = StorageValue<_, PricingParametersOf, ValueQuery, T::DefaultPricingParameters>; + #[pallet::storage] + #[pallet::getter(fn tokens)] + pub type Tokens = StorageMap<_, Twox64Concat, TokenId, Location, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn location_tokens)] + pub type LocationToToken = + StorageMap<_, Twox64Concat, Location, TokenId, OptionQuery>; + #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { @@ -574,6 +592,29 @@ pub mod pallet { }); Ok(()) } + + /// Sends a message to the Gateway contract to register a new + /// token that represents `asset`. + /// + /// - `origin`: Must be `MultiLocation` from sibling parachain + #[pallet::call_index(10)] + #[pallet::weight(T::WeightInfo::register_token())] + pub fn register_token( + origin: OriginFor, + location: Box, + metadata: AssetMetadata, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let asset_loc: Location = + (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; + + let pays_fee = PaysFee::::Yes(who); + + Self::do_register_token(asset_loc, metadata, pays_fee)?; + + Ok(()) + } } impl Pallet { @@ -663,6 +704,30 @@ pub mod pallet { let secondary_exists = Channels::::contains_key(SECONDARY_GOVERNANCE_CHANNEL); primary_exists && secondary_exists } + + pub(crate) fn do_register_token( + asset_loc: Location, + metadata: AssetMetadata, + pays_fee: PaysFee, + ) -> Result<(), DispatchError> { + // Record the token id or fail if it has already been created + let token_id = TokenIdOf::convert_location(&asset_loc) + .ok_or(Error::::LocationConversionFailed)?; + Tokens::::insert(token_id, asset_loc.clone()); + LocationToToken::::insert(asset_loc.clone(), token_id); + + let command = Command::RegisterForeignToken { + token_id, + name: metadata.name.into_inner(), + symbol: metadata.symbol.into_inner(), + decimals: metadata.decimals, + }; + Self::send(SECONDARY_GOVERNANCE_CHANNEL, command, pays_fee)?; + + Self::deposit_event(Event::::RegisterToken { asset_id: asset_loc.into(), token_id }); + + Ok(()) + } } impl StaticLookup for Pallet { @@ -684,4 +749,13 @@ pub mod pallet { PricingParameters::::get() } } + + impl MaybeEquivalence for Pallet { + fn convert(id: &TokenId) -> Option { + Tokens::::get(id) + } + fn convert_back(loc: &Location) -> Option { + LocationToToken::::get(loc) + } + } } diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 09f24195a30a..ba016b9b4854 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -248,7 +248,7 @@ fn create_channel() { let _ = Balances::mint_into(&sovereign_account, 10000); assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal,)); }); } @@ -264,10 +264,10 @@ fn create_channel_fail_already_exists() { let _ = Balances::mint_into(&sovereign_account, 10000); assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal,)); assert_noop!( - EthereumSystem::create_channel(origin, OperatingMode::Normal), + EthereumSystem::create_channel(origin, OperatingMode::Normal,), Error::::ChannelAlreadyCreated ); }); @@ -334,10 +334,10 @@ fn update_channel() { // First create the channel let _ = Balances::mint_into(&sovereign_account, 10000); assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal,)); // Now try to update it - assert_ok!(EthereumSystem::update_channel(origin, OperatingMode::Normal)); + assert_ok!(EthereumSystem::update_channel(origin, OperatingMode::Normal,)); System::assert_last_event(RuntimeEvent::EthereumSystem(crate::Event::UpdateChannel { channel_id: ParaId::from(2000).into(), @@ -383,12 +383,12 @@ fn update_channel_bad_origin() { // Signed origin not allowed assert_noop!( - EthereumSystem::update_channel(RuntimeOrigin::signed([14; 32].into()), mode), + EthereumSystem::update_channel(RuntimeOrigin::signed([14; 32].into()), mode,), BadOrigin ); // None origin not allowed - assert_noop!(EthereumSystem::update_channel(RuntimeOrigin::none(), mode), BadOrigin); + assert_noop!(EthereumSystem::update_channel(RuntimeOrigin::none(), mode,), BadOrigin); }); } @@ -400,7 +400,7 @@ fn update_channel_fails_not_exist() { // Now try to update it assert_noop!( - EthereumSystem::update_channel(origin, OperatingMode::Normal), + EthereumSystem::update_channel(origin, OperatingMode::Normal,), Error::::NoChannel ); }); @@ -419,7 +419,7 @@ fn force_update_channel() { // First create the channel let _ = Balances::mint_into(&sovereign_account, 10000); assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal,)); // Now try to force update it let force_origin = RuntimeOrigin::root(); @@ -463,7 +463,7 @@ fn transfer_native_from_agent() { // First create the agent and channel assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal,)); let origin = make_xcm_origin(origin_location.clone()); assert_ok!(EthereumSystem::transfer_native_from_agent(origin, recipient, amount),); @@ -549,7 +549,7 @@ fn charge_fee_for_create_agent() { assert_ok!(EthereumSystem::create_agent(origin.clone())); let fee_charged = initial_sovereign_balance - Balances::balance(&sovereign_account); - assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal,)); // assert sovereign_balance decreased by (fee.base_fee + fee.delivery_fee) let message = Message { @@ -584,7 +584,7 @@ fn charge_fee_for_transfer_native_from_agent() { // create_agent & create_channel first assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal,)); // assert sovereign_balance decreased by only the base_fee let sovereign_balance_before = Balances::balance(&sovereign_account); diff --git a/bridges/snowbridge/pallets/system/src/weights.rs b/bridges/snowbridge/pallets/system/src/weights.rs index 6e532a0d8a8c..3513097f8b55 100644 --- a/bridges/snowbridge/pallets/system/src/weights.rs +++ b/bridges/snowbridge/pallets/system/src/weights.rs @@ -42,6 +42,7 @@ pub trait WeightInfo { fn force_transfer_native_from_agent() -> Weight; fn set_token_transfer_fees() -> Weight; fn set_pricing_parameters() -> Weight; + fn register_token() -> Weight; } // For backwards compatibility and tests. @@ -246,4 +247,14 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } + + fn register_token() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 45_000_000 picoseconds. + Weight::from_parts(45_000_000, 6044) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } } diff --git a/bridges/snowbridge/primitives/core/Cargo.toml b/bridges/snowbridge/primitives/core/Cargo.toml index f9bee1ff4959..fa37c795b2d1 100644 --- a/bridges/snowbridge/primitives/core/Cargo.toml +++ b/bridges/snowbridge/primitives/core/Cargo.toml @@ -35,6 +35,7 @@ ethabi = { workspace = true } [dev-dependencies] hex = { workspace = true, default-features = true } +xcm-executor = { workspace = true, default-features = true } [features] default = ["std"] @@ -62,4 +63,5 @@ runtime-benchmarks = [ "polkadot-parachain-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", ] diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index ed1af4225d24..b4e9c9b3c857 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -21,14 +21,16 @@ pub use ringbuffer::{RingBufferMap, RingBufferMapImpl}; pub use sp_core::U256; use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::traits::Contains; +use frame_support::{traits::Contains, BoundedVec}; use hex_literal::hex; use scale_info::TypeInfo; -use sp_core::H256; +use sp_core::{ConstU32, H256}; use sp_io::hashing::keccak_256; use sp_runtime::{traits::AccountIdConversion, RuntimeDebug}; use sp_std::prelude::*; -use xcm::prelude::{Junction::Parachain, Location}; +use xcm::prelude::{ + GeneralIndex, GeneralKey, GlobalConsensus, Junction::Parachain, Location, PalletInstance, +}; use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; /// The ID of an agent contract @@ -163,4 +165,48 @@ impl DescribeLocation for DescribeHere { /// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on /// Ethereum which acts as the sovereign account for the Location. -pub type AgentIdOf = HashedDescription)>; +pub type AgentIdOf = + HashedDescription)>; + +#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub struct AssetMetadata { + pub name: BoundedVec>, + pub symbol: BoundedVec>, + pub decimals: u8, +} + +pub type TokenId = H256; + +pub type TokenIdOf = HashedDescription>; + +pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); +impl DescribeLocation for DescribeGlobalPrefix { + fn describe_location(l: &Location) -> Option> { + match (l.parent_count(), l.first_interior()) { + (1, Some(GlobalConsensus(network))) => { + let tail = l.clone().split_first_interior().0; + let interior = Suffix::describe_location(&tail.into())?; + Some((b"pna", network, interior).encode()) + }, + _ => None, + } + } +} + +pub struct DescribeInner; +impl DescribeLocation for DescribeInner { + fn describe_location(l: &Location) -> Option> { + match l.unpack().1 { + [] => Some(Vec::::new().encode()), + [Parachain(id)] => Some((*id).encode()), + [Parachain(id), PalletInstance(instance)] => Some((*id, *instance).encode()), + [Parachain(id), PalletInstance(instance), GeneralIndex(index)] => + Some((*id, *instance, *index).encode()), + [Parachain(id), PalletInstance(instance), GeneralKey { data, .. }] => + Some((*id, *instance, *data).encode()), + [Parachain(id), GeneralIndex(index)] => Some((*id, *index).encode()), + [Parachain(id), GeneralKey { data, .. }] => Some((*id, *data).encode()), + _ => None, + } + } +} diff --git a/bridges/snowbridge/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs index 0ba0fdb61089..77770761822a 100644 --- a/bridges/snowbridge/primitives/core/src/outbound.rs +++ b/bridges/snowbridge/primitives/core/src/outbound.rs @@ -139,6 +139,37 @@ mod v1 { // Fee multiplier multiplier: UD60x18, }, + /// Transfer ERC20 tokens + TransferNativeToken { + /// ID of the agent + agent_id: H256, + /// Address of the ERC20 token + token: H160, + /// The recipient of the tokens + recipient: H160, + /// The amount of tokens to transfer + amount: u128, + }, + /// Register foreign token from Polkadot + RegisterForeignToken { + /// ID for the token + token_id: H256, + /// Name of the token + name: Vec, + /// Short symbol for the token + symbol: Vec, + /// Number of decimal places + decimals: u8, + }, + /// Mint foreign token from Polkadot + MintForeignToken { + /// ID for the token + token_id: H256, + /// The recipient of the newly minted tokens + recipient: H160, + /// The amount of tokens to mint + amount: u128, + }, } impl Command { @@ -154,6 +185,9 @@ mod v1 { Command::TransferNativeFromAgent { .. } => 6, Command::SetTokenTransferFees { .. } => 7, Command::SetPricingParameters { .. } => 8, + Command::TransferNativeToken { .. } => 9, + Command::RegisterForeignToken { .. } => 10, + Command::MintForeignToken { .. } => 11, } } @@ -211,6 +245,26 @@ mod v1 { Token::Uint(U256::from(*delivery_cost)), Token::Uint(multiplier.clone().into_inner()), ])]), + Command::TransferNativeToken { agent_id, token, recipient, amount } => + ethabi::encode(&[Token::Tuple(vec![ + Token::FixedBytes(agent_id.as_bytes().to_owned()), + Token::Address(*token), + Token::Address(*recipient), + Token::Uint(U256::from(*amount)), + ])]), + Command::RegisterForeignToken { token_id, name, symbol, decimals } => + ethabi::encode(&[Token::Tuple(vec![ + Token::FixedBytes(token_id.as_bytes().to_owned()), + Token::String(name.to_owned()), + Token::String(symbol.to_owned()), + Token::Uint(U256::from(*decimals)), + ])]), + Command::MintForeignToken { token_id, recipient, amount } => + ethabi::encode(&[Token::Tuple(vec![ + Token::FixedBytes(token_id.as_bytes().to_owned()), + Token::Address(*recipient), + Token::Uint(U256::from(*amount)), + ])]), } } } @@ -403,6 +457,9 @@ impl GasMeter for ConstantGasMeter { }, Command::SetTokenTransferFees { .. } => 60_000, Command::SetPricingParameters { .. } => 60_000, + Command::TransferNativeToken { .. } => 100_000, + Command::RegisterForeignToken { .. } => 1_200_000, + Command::MintForeignToken { .. } => 100_000, } } } diff --git a/bridges/snowbridge/primitives/core/src/tests.rs b/bridges/snowbridge/primitives/core/src/tests.rs index 725fff1a9c94..3667ff7f03f9 100644 --- a/bridges/snowbridge/primitives/core/src/tests.rs +++ b/bridges/snowbridge/primitives/core/src/tests.rs @@ -1,5 +1,9 @@ -use crate::{ChannelId, ParaId}; +use crate::{ChannelId, ParaId, TokenIdOf}; use hex_literal::hex; +use xcm::prelude::{ + GeneralIndex, GeneralKey, GlobalConsensus, Location, PalletInstance, Parachain, Westend, +}; +use xcm_executor::traits::ConvertLocation; const EXPECT_CHANNEL_ID: [u8; 32] = hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539"); @@ -11,3 +15,38 @@ fn generate_channel_id() { let channel_id: ChannelId = para_id.into(); assert_eq!(channel_id, EXPECT_CHANNEL_ID.into()); } + +#[test] +fn test_describe_relay_token() { + let asset_location: Location = Location::new(1, [GlobalConsensus(Westend)]); + assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); +} + +#[test] +fn test_describe_primary_token_from_parachain() { + let asset_location: Location = Location::new(1, [GlobalConsensus(Westend), Parachain(2000)]); + assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); +} + +#[test] +fn test_describe_token_with_pallet_instance_prefix() { + let asset_location: Location = + Location::new(1, [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8)]); + assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); +} + +#[test] +fn test_describe_token_with_general_index_prefix() { + let asset_location: Location = + Location::new(1, [GlobalConsensus(Westend), Parachain(2000), GeneralIndex(1)]); + assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); +} + +#[test] +fn test_describe_token_with_general_key_prefix() { + let asset_location: Location = Location::new( + 1, + [GlobalConsensus(Westend), Parachain(2000), GeneralKey { length: 32, data: [1; 32] }], + ); + assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); +} diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 54e47a7a8b6a..c3383ef31e81 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -9,9 +9,10 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; use frame_support::{traits::tokens::Balance as BalanceT, weights::Weight, PalletError}; use scale_info::TypeInfo; -use sp_core::{Get, RuntimeDebug, H160}; +use snowbridge_core::TokenId; +use sp_core::{Get, RuntimeDebug, H160, H256}; use sp_io::hashing::blake2_256; -use sp_runtime::MultiAddress; +use sp_runtime::{traits::MaybeEquivalence, MultiAddress}; use sp_std::prelude::*; use xcm::prelude::{Junction::AccountKey20, *}; use xcm_executor::traits::ConvertLocation; @@ -45,7 +46,7 @@ pub enum Command { /// XCM execution fee on AssetHub fee: u128, }, - /// Send a token to AssetHub or another parachain + /// Send Ethereum token to AssetHub or another parachain SendToken { /// The address of the ERC20 token to be bridged over to AssetHub token: H160, @@ -56,6 +57,17 @@ pub enum Command { /// XCM execution fee on AssetHub fee: u128, }, + /// Send Polkadot token back to the original parachain + SendNativeToken { + /// The Id of the token + token_id: TokenId, + /// The destination for the transfer + destination: Destination, + /// Amount to transfer + amount: u128, + /// XCM execution fee on AssetHub + fee: u128, + }, } /// Destination for bridged tokens @@ -89,10 +101,16 @@ pub struct MessageToXcm< InboundQueuePalletInstance, AccountId, Balance, + ConvertAssetId, + UniversalLocation, + GlobalAssetHubLocation, > where CreateAssetCall: Get, CreateAssetDeposit: Get, Balance: BalanceT, + ConvertAssetId: MaybeEquivalence, + UniversalLocation: Get, + GlobalAssetHubLocation: Get, { _phantom: PhantomData<( CreateAssetCall, @@ -100,6 +118,9 @@ pub struct MessageToXcm< InboundQueuePalletInstance, AccountId, Balance, + ConvertAssetId, + UniversalLocation, + GlobalAssetHubLocation, )>, } @@ -108,6 +129,11 @@ pub struct MessageToXcm< pub enum ConvertMessageError { /// The message version is not supported for conversion. UnsupportedVersion, + InvalidDestination, + InvalidToken, + /// The fee asset is not supported for conversion. + UnsupportedFeeAsset, + CannotReanchor, } /// convert the inbound message to xcm which will be forwarded to the destination chain @@ -120,20 +146,35 @@ pub trait ConvertMessage { pub type CallIndex = [u8; 2]; -impl - ConvertMessage +impl< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + UniversalLocation, + GlobalAssetHubLocation, + > ConvertMessage for MessageToXcm< CreateAssetCall, CreateAssetDeposit, InboundQueuePalletInstance, AccountId, Balance, - > where + ConvertAssetId, + UniversalLocation, + GlobalAssetHubLocation, + > +where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, + ConvertAssetId: MaybeEquivalence, + UniversalLocation: Get, + GlobalAssetHubLocation: Get, { type Balance = Balance; type AccountId = AccountId; @@ -145,19 +186,51 @@ impl Ok(Self::convert_register_token(chain_id, token, fee)), V1(MessageV1 { chain_id, command: SendToken { token, destination, amount, fee } }) => - Ok(Self::convert_send_token(chain_id, token, destination, amount, fee)), + Ok(Self::convert_send_token(message_id, chain_id, token, destination, amount, fee)), + V1(MessageV1 { + chain_id, + command: SendNativeToken { token_id, destination, amount, fee }, + }) => Self::convert_send_native_token( + message_id, + chain_id, + token_id, + destination, + amount, + fee, + ), } } } -impl - MessageToXcm +impl< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + UniversalLocation, + GlobalAssetHubLocation, + > + MessageToXcm< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + UniversalLocation, + GlobalAssetHubLocation, + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, + ConvertAssetId: MaybeEquivalence, + UniversalLocation: Get, + GlobalAssetHubLocation: Get, { fn convert_register_token(chain_id: u64, token: H160, fee: u128) -> (Xcm<()>, Balance) { let network = Ethereum { chain_id }; @@ -291,6 +364,90 @@ where [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }], ) } + + fn convert_send_native_token( + message_id: H256, + chain_id: u64, + token_id: TokenId, + destination: Destination, + amount: u128, + asset_hub_fee: u128, + ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { + let network = Ethereum { chain_id }; + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); + + let (dest_para_id, beneficiary, dest_para_fee) = match destination { + // Final destination is a 32-byte account on AssetHub + Destination::AccountId32 { id } => + (None, Location::new(0, [AccountId32 { network: None, id }]), 0), + // Final destination is a 32-byte account on a sibling of AssetHub + Destination::ForeignAccountId32 { para_id, id, fee } => + (Some(para_id), Location::new(0, [AccountId32 { network: None, id }]), fee), + // Final destination is a 20-byte account on a sibling of AssetHub + Destination::ForeignAccountId20 { para_id, id, fee } => + (Some(para_id), Location::new(0, [AccountKey20 { network: None, key: id }]), fee), + }; + + let total_fees = asset_hub_fee.saturating_add(dest_para_fee); + let total_fee_asset: Asset = (Location::parent(), total_fees).into(); + + let asset_loc = + ConvertAssetId::convert(&token_id).ok_or(ConvertMessageError::InvalidToken)?; + + let mut reanchored_asset_loc = asset_loc.clone(); + reanchored_asset_loc + .reanchor(&GlobalAssetHubLocation::get(), &UniversalLocation::get()) + .map_err(|_| ConvertMessageError::CannotReanchor)?; + + let asset: Asset = (reanchored_asset_loc, amount).into(); + + let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); + + let mut instructions = vec![ + ReceiveTeleportedAsset(total_fee_asset.clone().into()), + BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, + DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), + UniversalOrigin(GlobalConsensus(network)), + WithdrawAsset(asset.clone().into()), + SetFeesMode { jit_withdraw: true }, + ]; + + match dest_para_id { + Some(dest_para_id) => { + let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); + + instructions.extend(vec![ + // Perform a deposit reserve to send to destination chain. + InitiateReserveWithdraw { + assets: Wild(AllCounted(2)), + reserve: Location::new(1, [Parachain(dest_para_id)]), + xcm: vec![ + // Buy execution on target. + BuyExecution { fees: dest_para_fee_asset, weight_limit: Unlimited }, + // Deposit asset to beneficiary. + DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, + // Forward message id to destination parachain. + SetTopic(message_id.into()), + ] + .into(), + }, + ]); + }, + None => { + instructions.extend(vec![ + // Deposit both asset and fees to beneficiary so the fees will not get + // trapped. Another benefit is when fees left more than ED on AssetHub could be + // used to create the beneficiary account in case it does not exist. + DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, + ]); + }, + } + + // Forward message id to Asset Hub. + instructions.push(SetTopic(message_id.into())); + + Ok((instructions.into(), total_fees.into())) + } } pub struct GlobalConsensusEthereumConvertsFor(PhantomData); diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index 75670b05c100..282a16443fc8 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -1,6 +1,6 @@ use super::GlobalConsensusEthereumConvertsFor; use crate::inbound::CallIndex; -use frame_support::parameter_types; +use frame_support::{assert_ok, parameter_types}; use hex_literal::hex; use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; @@ -38,3 +38,67 @@ fn test_contract_location_with_incorrect_location_fails_convert() { None, ); } + +#[test] +fn test_reanchor_relay_token() { + let asset_id: Location = Location::parent(); + let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); + let mut reanchored_asset = asset_id.clone(); + assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + assert_eq!( + reanchored_asset, + Location { parents: 1, interior: [GlobalConsensus(Westend)].into() } + ); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); + let mut reanchored_asset = reanchored_asset.clone(); + assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); + assert_eq!(reanchored_asset, asset_id); +} + +#[test] +fn test_reanchor_pna_from_ah() { + let asset_id: Location = + Location { parents: 0, interior: [PalletInstance(50), GeneralIndex(2)].into() }; + let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); + let mut reanchored_asset = asset_id.clone(); + assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + assert_eq!( + reanchored_asset, + Location { + parents: 1, + interior: [ + GlobalConsensus(Westend), + Parachain(1000), + PalletInstance(50), + GeneralIndex(2) + ] + .into() + } + ); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); + let mut reanchored_asset = reanchored_asset.clone(); + assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); + assert_eq!(reanchored_asset, asset_id); +} + +#[test] +fn test_reanchor_pna_from_para() { + let asset_id: Location = Location { parents: 1, interior: [Parachain(2000)].into() }; + let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); + let mut reanchored_asset = asset_id.clone(); + assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + assert_eq!( + reanchored_asset, + Location { parents: 1, interior: [GlobalConsensus(Westend), Parachain(2000)].into() } + ); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); + let mut reanchored_asset = reanchored_asset.clone(); + assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); + assert_eq!(reanchored_asset, asset_id); +} diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index ddc36ce8cb61..4963062f4b4a 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -11,10 +11,11 @@ use codec::{Decode, Encode}; use frame_support::{ensure, traits::Get}; use snowbridge_core::{ - outbound::{AgentExecuteCommand, Command, Message, SendMessage}, - ChannelId, ParaId, + outbound::{Command, Message, SendMessage}, + AgentId, ChannelId, ParaId, TokenId, TokenIdOf, }; use sp_core::{H160, H256}; +use sp_runtime::traits::MaybeEquivalence; use sp_std::{iter::Peekable, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_executor::traits::{ConvertLocation, ExportXcm}; @@ -24,15 +25,32 @@ pub struct EthereumBlobExporter< EthereumNetwork, OutboundQueue, AgentHashedDescription, ->(PhantomData<(UniversalLocation, EthereumNetwork, OutboundQueue, AgentHashedDescription)>); - -impl ExportXcm - for EthereumBlobExporter + ConvertAssetId, +>( + PhantomData<( + UniversalLocation, + EthereumNetwork, + OutboundQueue, + AgentHashedDescription, + ConvertAssetId, + )>, +); + +impl + ExportXcm + for EthereumBlobExporter< + UniversalLocation, + EthereumNetwork, + OutboundQueue, + AgentHashedDescription, + ConvertAssetId, + > where UniversalLocation: Get, EthereumNetwork: Get, OutboundQueue: SendMessage, AgentHashedDescription: ConvertLocation, + ConvertAssetId: MaybeEquivalence, { type Ticket = (Vec, XcmHash); @@ -87,13 +105,8 @@ where SendError::MissingArgument })?; - let mut converter = XcmConverter::new(&message, &expected_network); - let (agent_execute_command, message_id) = converter.convert().map_err(|err|{ - log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to pattern matching error '{err:?}'."); - SendError::Unroutable - })?; - let source_location = Location::new(1, local_sub.clone()); + let agent_id = match AgentHashedDescription::convert_location(&source_location) { Some(id) => id, None => { @@ -102,13 +115,16 @@ where }, }; + let mut converter = + XcmConverter::::new(&message, expected_network, agent_id); + let (command, message_id) = converter.convert().map_err(|err|{ + log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to pattern matching error '{err:?}'."); + SendError::Unroutable + })?; + let channel_id: ChannelId = ParaId::from(para_id).into(); - let outbound_message = Message { - id: Some(message_id.into()), - channel_id, - command: Command::AgentExecute { agent_id, command: agent_execute_command }, - }; + let outbound_message = Message { id: Some(message_id.into()), channel_id, command }; // validate the message let (ticket, fee) = OutboundQueue::validate(&outbound_message).map_err(|err| { @@ -154,6 +170,9 @@ enum XcmConverterError { AssetResolutionFailed, InvalidFeeAsset, SetTopicExpected, + ReserveAssetDepositedExpected, + InvalidAsset, + UnexpectedInstruction, } macro_rules! match_expression { @@ -165,18 +184,33 @@ macro_rules! match_expression { }; } -struct XcmConverter<'a, Call> { +struct XcmConverter<'a, ConvertAssetId, Call> { iter: Peekable>>, - ethereum_network: &'a NetworkId, + ethereum_network: NetworkId, + agent_id: AgentId, + _marker: PhantomData, } -impl<'a, Call> XcmConverter<'a, Call> { - fn new(message: &'a Xcm, ethereum_network: &'a NetworkId) -> Self { - Self { iter: message.inner().iter().peekable(), ethereum_network } +impl<'a, ConvertAssetId, Call> XcmConverter<'a, ConvertAssetId, Call> +where + ConvertAssetId: MaybeEquivalence, +{ + fn new(message: &'a Xcm, ethereum_network: NetworkId, agent_id: AgentId) -> Self { + Self { + iter: message.inner().iter().peekable(), + ethereum_network, + agent_id, + _marker: Default::default(), + } } - fn convert(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { - // Get withdraw/deposit and make native tokens create message. - let result = self.native_tokens_unlock_message()?; + fn convert(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + let result = match self.peek() { + Ok(ReserveAssetDeposited { .. }) => self.send_native_tokens_message(), + // Get withdraw/deposit and make native tokens create message. + Ok(WithdrawAsset { .. }) => self.send_tokens_message(), + Err(e) => Err(e), + _ => return Err(XcmConverterError::UnexpectedInstruction), + }?; // All xcm instructions must be consumed before exit. if self.next().is_ok() { @@ -186,9 +220,7 @@ impl<'a, Call> XcmConverter<'a, Call> { Ok(result) } - fn native_tokens_unlock_message( - &mut self, - ) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { + fn send_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { use XcmConverterError::*; // Get the reserve assets from WithdrawAsset. @@ -262,7 +294,10 @@ impl<'a, Call> XcmConverter<'a, Call> { // Check if there is a SetTopic and skip over it if found. let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; - Ok((AgentExecuteCommand::TransferToken { token, recipient, amount }, *topic_id)) + Ok(( + Command::TransferNativeToken { agent_id: self.agent_id, token, recipient, amount }, + *topic_id, + )) } fn next(&mut self) -> Result<&'a Instruction, XcmConverterError> { @@ -275,9 +310,88 @@ impl<'a, Call> XcmConverter<'a, Call> { fn network_matches(&self, network: &Option) -> bool { if let Some(network) = network { - network == self.ethereum_network + *network == self.ethereum_network } else { true } } + + fn send_native_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + use XcmConverterError::*; + + // Get the reserve assets. + let reserve_assets = + match_expression!(self.next()?, ReserveAssetDeposited(reserve_assets), reserve_assets) + .ok_or(ReserveAssetDepositedExpected)?; + + // Check if clear origin exists and skip over it. + if match_expression!(self.peek(), Ok(ClearOrigin), ()).is_some() { + let _ = self.next(); + } + + // Get the fee asset item from BuyExecution or continue parsing. + let fee_asset = match_expression!(self.peek(), Ok(BuyExecution { fees, .. }), fees); + if fee_asset.is_some() { + let _ = self.next(); + } + + let (deposit_assets, beneficiary) = match_expression!( + self.next()?, + DepositAsset { assets, beneficiary }, + (assets, beneficiary) + ) + .ok_or(DepositAssetExpected)?; + + // assert that the beneficiary is AccountKey20. + let recipient = match_expression!( + beneficiary.unpack(), + (0, [AccountKey20 { network, key }]) + if self.network_matches(network), + H160(*key) + ) + .ok_or(BeneficiaryResolutionFailed)?; + + // Make sure there are reserved assets. + if reserve_assets.len() == 0 { + return Err(NoReserveAssets) + } + + // Check the the deposit asset filter matches what was reserved. + if reserve_assets.inner().iter().any(|asset| !deposit_assets.matches(asset)) { + return Err(FilterDoesNotConsumeAllAssets) + } + + // We only support a single asset at a time. + ensure!(reserve_assets.len() == 1, TooManyAssets); + let reserve_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; + + // If there was a fee specified verify it. + if let Some(fee_asset) = fee_asset { + // The fee asset must be the same as the reserve asset. + if fee_asset.id != reserve_asset.id || fee_asset.fun > reserve_asset.fun { + return Err(InvalidFeeAsset) + } + } + + let (asset_id, amount) = match reserve_asset { + Asset { id: AssetId(inner_location), fun: Fungible(amount) } => + Some((inner_location.clone(), *amount)), + _ => None, + } + .ok_or(AssetResolutionFailed)?; + + // transfer amount must be greater than 0. + ensure!(amount > 0, ZeroAssetTransfer); + + let token_id = TokenIdOf::convert_location(&asset_id).ok_or(InvalidAsset)?; + + let expected_asset_id = ConvertAssetId::convert(&token_id).ok_or(InvalidAsset)?; + + ensure!(asset_id == expected_asset_id, InvalidAsset); + + // Check if there is a SetTopic and skip over it if found. + let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; + + Ok((Command::MintForeignToken { token_id, recipient, amount }, *topic_id)) + } } diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index 111243bb45a7..75dce864493a 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -4,7 +4,8 @@ use snowbridge_core::{ outbound::{Fee, SendError, SendMessageFeeProvider}, AgentIdOf, }; -use xcm::v3::prelude::SendError as XcmSendError; +use sp_std::default::Default; +use xcm::prelude::SendError as XcmSendError; use super::*; @@ -57,6 +58,16 @@ impl SendMessageFeeProvider for MockErrOutboundQueue { } } +pub struct MockTokenIdConvert; +impl MaybeEquivalence for MockTokenIdConvert { + fn convert(_id: &TokenId) -> Option { + Some(Location::new(1, [GlobalConsensus(Westend)])) + } + fn convert_back(_loc: &Location) -> Option { + None + } +} + #[test] fn exporter_validate_with_unknown_network_yields_not_applicable() { let network = Ethereum { chain_id: 1337 }; @@ -65,14 +76,14 @@ fn exporter_validate_with_unknown_network_yields_not_applicable() { let mut destination: Option = None; let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -84,14 +95,14 @@ fn exporter_validate_with_invalid_destination_yields_missing_argument() { let mut destination: Option = None; let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -106,14 +117,14 @@ fn exporter_validate_with_x8_destination_yields_not_applicable() { ); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -125,14 +136,14 @@ fn exporter_validate_without_universal_source_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -144,14 +155,14 @@ fn exporter_validate_without_global_universal_location_yields_unroutable() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -163,14 +174,14 @@ fn exporter_validate_without_global_bridge_location_yields_not_applicable() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -183,14 +194,14 @@ fn exporter_validate_with_remote_universal_source_yields_not_applicable() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -202,14 +213,14 @@ fn exporter_validate_without_para_id_in_source_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -222,14 +233,14 @@ fn exporter_validate_complex_para_id_in_source_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -242,14 +253,14 @@ fn exporter_validate_without_xcm_message_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -289,14 +300,14 @@ fn exporter_validate_with_max_target_fee_yields_unroutable() { .into(), ); - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -316,14 +327,14 @@ fn exporter_validate_with_unparsable_xcm_yields_unroutable() { let mut message: Option> = Some(vec![WithdrawAsset(fees), BuyExecution { fees: fee, weight_limit: Unlimited }].into()); - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -362,14 +373,14 @@ fn exporter_validate_xcm_success_case_1() { .into(), ); - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert!(result.is_ok()); } @@ -381,6 +392,7 @@ fn exporter_deliver_with_submit_failure_yields_unroutable() { BridgedNetwork, MockErrOutboundQueue, AgentIdOf, + MockTokenIdConvert, >::deliver((hex!("deadbeef").to_vec(), XcmHash::default())); assert_eq!(result, Err(XcmSendError::Transport("other transport error"))) } @@ -410,8 +422,10 @@ fn xcm_converter_convert_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::TransferNativeToken { + agent_id: Default::default(), token: token_address.into(), recipient: beneficiary_address.into(), amount: 1000, @@ -443,8 +457,10 @@ fn xcm_converter_convert_without_buy_execution_yields_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::TransferNativeToken { + agent_id: Default::default(), token: token_address.into(), recipient: beneficiary_address.into(), amount: 1000, @@ -478,8 +494,10 @@ fn xcm_converter_convert_with_wildcard_all_asset_filter_succeeds() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::TransferNativeToken { + agent_id: Default::default(), token: token_address.into(), recipient: beneficiary_address.into(), amount: 1000, @@ -513,8 +531,10 @@ fn xcm_converter_convert_with_fees_less_than_reserve_yields_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::TransferNativeToken { + agent_id: Default::default(), token: token_address.into(), recipient: beneficiary_address.into(), amount: 1000, @@ -547,7 +567,8 @@ fn xcm_converter_convert_without_set_topic_yields_set_topic_expected() { ClearTopic, ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::SetTopicExpected)); } @@ -564,7 +585,8 @@ fn xcm_converter_convert_with_partial_message_yields_unexpected_end_of_xcm() { .into(); let message: Xcm<()> = vec![WithdrawAsset(assets)].into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); } @@ -595,7 +617,8 @@ fn xcm_converter_with_different_fee_asset_fails() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); } @@ -625,7 +648,8 @@ fn xcm_converter_with_fees_greater_than_reserve_fails() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); } @@ -636,7 +660,8 @@ fn xcm_converter_convert_with_empty_xcm_yields_unexpected_end_of_xcm() { let message: Xcm<()> = vec![].into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); @@ -668,7 +693,8 @@ fn xcm_converter_convert_with_extra_instructions_yields_end_of_xcm_message_expec ClearError, ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::EndOfXcmMessageExpected)); @@ -698,10 +724,11 @@ fn xcm_converter_convert_without_withdraw_asset_yields_withdraw_expected() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::WithdrawAssetExpected)); + assert_eq!(result.err(), Some(XcmConverterError::UnexpectedInstruction)); } #[test] @@ -723,7 +750,8 @@ fn xcm_converter_convert_without_withdraw_asset_yields_deposit_expected() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::DepositAssetExpected)); @@ -756,7 +784,8 @@ fn xcm_converter_convert_without_assets_yields_no_reserve_assets() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::NoReserveAssets)); @@ -794,7 +823,8 @@ fn xcm_converter_convert_with_two_assets_yields_too_many_assets() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::TooManyAssets)); @@ -825,7 +855,8 @@ fn xcm_converter_convert_without_consuming_filter_yields_filter_does_not_consume SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::FilterDoesNotConsumeAllAssets)); @@ -856,7 +887,8 @@ fn xcm_converter_convert_with_zero_amount_asset_yields_zero_asset_transfer() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::ZeroAssetTransfer)); @@ -886,7 +918,8 @@ fn xcm_converter_convert_non_ethereum_asset_yields_asset_resolution_failed() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -919,7 +952,8 @@ fn xcm_converter_convert_non_ethereum_chain_asset_yields_asset_resolution_failed SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -952,7 +986,8 @@ fn xcm_converter_convert_non_ethereum_chain_yields_asset_resolution_failed() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -989,7 +1024,8 @@ fn xcm_converter_convert_with_non_ethereum_beneficiary_yields_beneficiary_resolu SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::BeneficiaryResolutionFailed)); @@ -1025,7 +1061,8 @@ fn xcm_converter_convert_with_non_ethereum_chain_beneficiary_yields_beneficiary_ SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::BeneficiaryResolutionFailed)); @@ -1056,3 +1093,65 @@ fn test_describe_here() { hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into() ) } + +#[test] +fn xcm_converter_transfer_native_token_success() { + let network = BridgedNetwork::get(); + + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + + let amount = 1000000; + let asset_location = Location::new(1, [GlobalConsensus(Westend)]); + let token_id = TokenIdOf::convert_location(&asset_location).unwrap(); + + let assets: Assets = vec![Asset { id: AssetId(asset_location), fun: Fungible(amount) }].into(); + let filter: AssetFilter = assets.clone().into(); + + let message: Xcm<()> = vec![ + ReserveAssetDeposited(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), + }, + SetTopic([0; 32]), + ] + .into(); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = + Command::MintForeignToken { recipient: beneficiary_address.into(), amount, token_id }; + let result = converter.convert(); + assert_eq!(result, Ok((expected_payload, [0; 32]))); +} + +#[test] +fn xcm_converter_transfer_native_token_with_invalid_location_will_fail() { + let network = BridgedNetwork::get(); + + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + + let amount = 1000000; + // Invalid asset location from a different consensus + let asset_location = Location { parents: 2, interior: [GlobalConsensus(Rococo)].into() }; + + let assets: Assets = vec![Asset { id: AssetId(asset_location), fun: Fungible(amount) }].into(); + let filter: AssetFilter = assets.clone().into(); + + let message: Xcm<()> = vec![ + ReserveAssetDeposited(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), + }, + SetTopic([0; 32]), + ] + .into(); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let result = converter.convert(); + assert_eq!(result.err(), Some(XcmConverterError::InvalidAsset)); +} diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs index d20e059f9fea..7d2de43888b9 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs @@ -20,6 +20,7 @@ use sp_core::{sr25519, storage::Storage}; // Cumulus use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, get_account_id_from_seed, + PenpalBSiblingSovereignAccount, PenpalBTeleportableAssetLocation, PenpalSiblingSovereignAccount, PenpalTeleportableAssetLocation, RESERVABLE_ASSET_ID, SAFE_XCM_VERSION, USDT_ID, }; @@ -80,6 +81,13 @@ pub fn genesis() -> Storage { false, ED, ), + // PenpalB's teleportable asset representation + ( + PenpalBTeleportableAssetLocation::get(), + PenpalBSiblingSovereignAccount::get(), + true, + ED, + ), ], ..Default::default() }, diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs index feb59c411c8d..e7a28ebf4a46 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs @@ -46,6 +46,7 @@ decl_test_parachains! { pallets = { PolkadotXcm: bridge_hub_westend_runtime::PolkadotXcm, Balances: bridge_hub_westend_runtime::Balances, + EthereumSystem: bridge_hub_westend_runtime::EthereumSystem, } }, } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index 30e66ced1fb0..e45d41db8f40 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -60,6 +60,7 @@ pub const TELEPORTABLE_ASSET_ID: u32 = 2; pub const USDT_ID: u32 = 1984; pub const PENPAL_ID: u32 = 2000; +pub const PENPAL_B_ID: u32 = 2001; pub const ASSETS_PALLET_ID: u8 = 50; parameter_types! { @@ -71,6 +72,14 @@ parameter_types! { ] ); pub PenpalSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_ID).into_account_truncating(); + pub PenpalBTeleportableAssetLocation: xcm::v3::Location + = xcm::v3::Location::new(1, [ + xcm::v3::Junction::Parachain(PENPAL_B_ID), + xcm::v3::Junction::PalletInstance(ASSETS_PALLET_ID), + xcm::v3::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), + ] + ); + pub PenpalBSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_B_ID).into_account_truncating(); } /// Helper function to generate a crypto pair from seed diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 5e0462d14882..98f640a46800 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -52,7 +52,10 @@ mod imports { BridgeHubWestendParaPallet as BridgeHubWestendPallet, BridgeHubWestendXcmConfig, }, penpal_emulated_chain::{ - penpal_runtime::xcm_config::UniversalLocation as PenpalUniversalLocation, + penpal_runtime::xcm_config::{ + LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, + UniversalLocation as PenpalUniversalLocation, + }, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, }, westend_emulated_chain::{ @@ -66,8 +69,9 @@ mod imports { BridgeHubWestendPara as BridgeHubWestend, BridgeHubWestendParaReceiver as BridgeHubWestendReceiver, BridgeHubWestendParaSender as BridgeHubWestendSender, PenpalBPara as PenpalB, - PenpalBParaSender as PenpalBSender, WestendRelay as Westend, - WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, + PenpalBParaReceiver as PenpalBReceiver, PenpalBParaSender as PenpalBSender, + WestendRelay as Westend, WestendRelayReceiver as WestendReceiver, + WestendRelaySender as WestendSender, }; pub const ASSET_MIN_BALANCE: u128 = 1000; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs index bf894a3baf58..8112697a07df 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs @@ -21,6 +21,8 @@ mod send_xcm; mod snowbridge; mod teleport; +mod snowbridge; + pub(crate) fn asset_hub_rococo_location() -> Location { Location::new(2, [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())]) } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index b4db9b365f39..4ef5d66fe536 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -16,20 +16,29 @@ use crate::imports::*; use asset_hub_westend_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; use bridge_hub_westend_runtime::EthereumInboundQueue; use codec::{Decode, Encode}; +use emulated_integration_tests_common::{ + PenpalBSiblingSovereignAccount, RESERVABLE_ASSET_ID, TELEPORTABLE_ASSET_ID, +}; use frame_support::pallet_prelude::TypeInfo; use hex_literal::hex; -use snowbridge_core::outbound::OperatingMode; +use rococo_westend_system_emulated_network::{ + asset_hub_westend_emulated_chain::genesis::AssetHubWestendAssetOwner, + penpal_emulated_chain::penpal_runtime::xcm_config::RelayLocation, +}; +use snowbridge_core::{outbound::OperatingMode, AssetMetadata, TokenIdOf}; use snowbridge_router_primitives::inbound::{ - Command, ConvertMessage, Destination, MessageV1, VersionedMessage, + Command, ConvertMessage, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, + VersionedMessage, }; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; +use xcm_executor::traits::ConvertLocation; -const INITIAL_FUND: u128 = 5_000_000_000_000_000_000; +const INITIAL_FUND: u128 = 5_000_000_000_000; pub const CHAIN_ID: u64 = 11155111; pub const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); const XCM_FEE: u128 = 100_000_000_000; -const WETH_AMOUNT: u128 = 1_000_000_000; +const TOKEN_AMOUNT: u128 = 100_000_000_000; #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] pub enum ControlCall { @@ -37,6 +46,12 @@ pub enum ControlCall { CreateAgent, #[codec(index = 4)] CreateChannel { mode: OperatingMode }, + #[codec(index = 11)] + ForceRegisterToken { + location: Box, + asset: Box, + metadata: AssetMetadata, + }, } #[allow(clippy::large_enum_variant)] @@ -61,14 +76,12 @@ fn register_weth_token_from_ethereum_to_asset_hub() { chain_id: CHAIN_ID, command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE }, }); - let (xcm, _) = Converter::convert(message).unwrap(); - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap(); + let (xcm, _) = Converter::convert(message_id, message).unwrap(); + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); assert_expected_events!( BridgeHubWestend, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, - ] + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] ); }); @@ -77,9 +90,7 @@ fn register_weth_token_from_ethereum_to_asset_hub() { assert_expected_events!( AssetHubWestend, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { .. }) => {}, - ] + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { .. }) => {},] ); }); } @@ -127,7 +138,7 @@ fn send_token_from_ethereum_to_asset_hub() { command: Command::SendToken { token: WETH.into(), destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, - amount: WETH_AMOUNT, + amount: TOKEN_AMOUNT, fee: XCM_FEE, }, }); @@ -137,9 +148,7 @@ fn send_token_from_ethereum_to_asset_hub() { // Check that the message was sent assert_expected_events!( BridgeHubWestend, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, - ] + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] ); }); @@ -149,9 +158,7 @@ fn send_token_from_ethereum_to_asset_hub() { // Check that the token was received and issued as a foreign asset on AssetHub assert_expected_events!( AssetHubWestend, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, - ] + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},] ); }); } @@ -167,13 +174,6 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { let weth_asset_location: Location = (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); - AssetHubWestend::force_default_xcm_version(Some(XCM_VERSION)); - BridgeHubWestend::force_default_xcm_version(Some(XCM_VERSION)); - AssetHubWestend::force_xcm_version( - Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]), - XCM_VERSION, - ); - BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); AssetHubWestend::execute_with(|| { @@ -202,7 +202,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { command: Command::SendToken { token: WETH.into(), destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, - amount: WETH_AMOUNT, + amount: TOKEN_AMOUNT, fee: XCM_FEE, }, }); @@ -212,8 +212,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { // Check that the send token message was sent using xcm assert_expected_events!( BridgeHubWestend, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) =>{},] + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) =>{},] ); }); @@ -224,9 +223,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { // Check that AssetHub has issued the foreign asset assert_expected_events!( AssetHubWestend, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, - ] + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},] ); let assets = vec![Asset { id: AssetId(Location::new( @@ -236,7 +233,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { AccountKey20 { network: None, key: WETH }, ], )), - fun: Fungible(WETH_AMOUNT), + fun: Fungible(TOKEN_AMOUNT), }]; let multi_assets = VersionedAssets::V4(Assets::from(assets)); @@ -279,10 +276,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { // Outbound Queue assert_expected_events!( BridgeHubWestend, - vec![ - - RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued - {..}) => {}, ] + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] ); let events = BridgeHubWestend::events(); // Check that the local fee was credited to the Snowbridge sovereign account @@ -305,3 +299,637 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { ); }); } + +#[test] +fn transfer_relay_token() { + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of( + BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()), + ); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let asset_id: Location = Location { parents: 1, interior: [GlobalConsensus(Westend)].into() }; + + let token_id = TokenIdOf::convert_location(&asset_id).unwrap(); + + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(&Location::new( + 2, + [GlobalConsensus(EthereumNetwork::get())], + )) + .unwrap() + .into(); + + // Register token + BridgeHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::Balances::force_set_balance( + RuntimeOrigin::root(), + sp_runtime::MultiAddress::Id(BridgeHubWestendSender::get()), + INITIAL_FUND * 10, + )); + + assert_ok!(::EthereumSystem::register_token( + RuntimeOrigin::signed(BridgeHubWestendSender::get()), + Box::new(VersionedLocation::V4(asset_id.clone())), + AssetMetadata { + name: "wnd".as_bytes().to_vec().try_into().unwrap(), + symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + )); + // Check that a message was sent to Ethereum to create the agent + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::RegisterToken { .. }) => {},] + ); + }); + + // Send token to Ethereum + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + let assets = vec![Asset { id: AssetId(Location::parent()), fun: Fungible(TOKEN_AMOUNT) }]; + let multi_assets = VersionedAssets::V4(Assets::from(assets)); + + let destination = VersionedLocation::V4(Location::new( + 2, + [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], + )); + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(destination), + Box::new(beneficiary), + Box::new(multi_assets), + 0, + Unlimited, + )); + + let events = AssetHubWestend::events(); + // Check that the native asset transferred to some reserved account(sovereign of Ethereum) + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Transfer { amount, ..}) + if *amount == TOKEN_AMOUNT, + )), + "native token reserved to Ethereum sovereign account." + ); + }); + + // Send token back from ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] + ); + + // Send relay token back to AH + let message_id: H256 = [0; 32].into(); + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, + amount: TOKEN_AMOUNT, + fee: XCM_FEE, + }, + }); + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::Balances(pallet_balances::Event::Burned{ .. }) => {},] + ); + + let events = AssetHubWestend::events(); + + // Check that the native token burnt from some reserved account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, ..}) + if *who == ethereum_sovereign.clone(), + )), + "native token burnt from Ethereum sovereign account." + ); + + // Check that the token was minted to beneficiary + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) + if *amount >= TOKEN_AMOUNT && *who == AssetHubWestendReceiver::get() + )), + "Token minted to beneficiary." + ); + }); +} + +#[test] +fn transfer_ah_token() { + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of( + BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()), + ); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); + + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) + .unwrap() + .into(); + + let asset_id: Location = + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())].into(); + + let asset_id_after_reanchored = + Location::new(1, [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())]) + .appended_with(asset_id.clone().interior) + .unwrap(); + + let token_id = TokenIdOf::convert_location(&asset_id_after_reanchored).unwrap(); + + // Register token + BridgeHubWestend::execute_with(|| { + type Runtime = ::Runtime; + + snowbridge_pallet_system::Tokens::::insert( + token_id, + asset_id_after_reanchored.clone(), + ); + }); + + // Mint some token + AssetHubWestend::mint_asset( + ::RuntimeOrigin::signed(AssetHubWestendAssetOwner::get()), + RESERVABLE_ASSET_ID, + AssetHubWestendSender::get(), + TOKEN_AMOUNT, + ); + + // Send token to Ethereum + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + // Send partial of the token, will fail if send all + let assets = + vec![Asset { id: AssetId(asset_id.clone()), fun: Fungible(TOKEN_AMOUNT / 10) }]; + let multi_assets = VersionedAssets::V4(Assets::from(assets)); + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(VersionedLocation::from(ethereum_destination)), + Box::new(beneficiary), + Box::new(multi_assets), + 0, + Unlimited, + )); + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::Assets(pallet_assets::Event::Transferred{ .. }) => {},] + ); + + let events = AssetHubWestend::events(); + // Check that the native asset transferred to some reserved account(sovereign of Ethereum) + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id, to, ..}) + if *asset_id == RESERVABLE_ASSET_ID && *to == ethereum_sovereign.clone() + )), + "native token reserved to Ethereum sovereign account." + ); + }); + + // Send token back from Ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] + ); + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, + amount: TOKEN_AMOUNT / 10, + fee: XCM_FEE, + }, + }); + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::Assets(pallet_assets::Event::Burned{..}) => {},] + ); + + let events = AssetHubWestend::events(); + + // Check that the native token burnt from some reserved account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Burned { owner, .. }) + if *owner == ethereum_sovereign.clone(), + )), + "token burnt from Ethereum sovereign account." + ); + + // Check that the token was minted to beneficiary + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Issued { owner, .. }) + if *owner == AssetHubWestendReceiver::get() + )), + "Token minted to beneficiary." + ); + }); +} + +#[test] +fn transfer_penpal_native_token() { + let assethub_location = BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()); + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of(assethub_location); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) + .unwrap() + .into(); + AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); + + let penpal_asset_location = Location::new(1, [Parachain(PenpalB::para_id().into())]); + + let penpal_asset_location_after_reanchored = + Location::new(1, [GlobalConsensus(Westend), Parachain(PenpalB::para_id().into())]); + + let token_id = TokenIdOf::convert_location(&penpal_asset_location_after_reanchored).unwrap(); + + // Register token on AH + AssetHubWestend::force_create_foreign_asset( + penpal_asset_location.clone().try_into().unwrap(), + PenpalBSiblingSovereignAccount::get().clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + + // Fund sender on AH + AssetHubWestend::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalBSiblingSovereignAccount::get()), + penpal_asset_location.clone().try_into().unwrap(), + AssetHubWestendSender::get(), + TOKEN_AMOUNT, + ); + + // Fund sov of AH on penpal + let ah_sovereign = + PenpalB::sovereign_account_id_of(PenpalB::sibling_location_of(AssetHubWestend::para_id())); + PenpalB::fund_accounts(vec![(ah_sovereign.clone(), INITIAL_FUND)]); + PenpalB::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + RelayLocation::get(), + ah_sovereign.clone(), + INITIAL_FUND, + ); + + // Create token + BridgeHubWestend::execute_with(|| { + type Runtime = ::Runtime; + + snowbridge_pallet_system::Tokens::::insert( + token_id, + penpal_asset_location_after_reanchored.clone(), + ); + }); + + // Send token to Ethereum + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + let assets = vec![Asset { + id: penpal_asset_location.clone().into(), + fun: Fungible(TOKEN_AMOUNT / 10), + }]; + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(VersionedLocation::from(ethereum_destination)), + Box::new(beneficiary), + Box::new(VersionedAssets::from(Assets::from(assets))), + 0, + Unlimited, + )); + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred{..}) => {},] + ); + + let events = AssetHubWestend::events(); + // Check that the native asset transferred to some reserved account(sovereign of Ethereum) + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred { amount, to, ..}) + if *amount == TOKEN_AMOUNT/10 && *to == ethereum_sovereign + )), + "native token reserved to Ethereum sovereign account." + ); + }); + + // Send token back from Ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{..}) => {},] + ); + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::ForeignAccountId32 { + para_id: PenpalB::para_id().into(), + id: PenpalBReceiver::get().into(), + fee: XCM_FEE, + }, + amount: TOKEN_AMOUNT / 10, + fee: XCM_FEE, + }, + }); + + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that token burnt from some reserved account + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { .. }) => {},] + ); + }); + + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that token issued to beneficial + assert_expected_events!( + PenpalB, + vec![RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {},] + ); + + let events = PenpalB::events(); + + // Check that token issued to destination account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { amount, who, ..}) + if *amount == TOKEN_AMOUNT/10 && *who == PenpalBReceiver::get() + )), + "Token minted to beneficiary." + ); + }) +} + +#[test] +fn transfer_penpal_asset() { + let assethub_location = BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()); + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of(assethub_location); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) + .unwrap() + .into(); + + AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); + + let penpal_asset_location = Location::new(1, [Parachain(PenpalB::para_id().into())]) + .appended_with(PenpalLocalTeleportableToAssetHub::get()) + .unwrap(); + + let penpal_asset_location_after_reanchored = + Location::new(1, [GlobalConsensus(Westend), Parachain(PenpalB::para_id().into())]) + .appended_with(PenpalLocalTeleportableToAssetHub::get()) + .unwrap(); + + let token_id = TokenIdOf::convert_location(&penpal_asset_location_after_reanchored).unwrap(); + + // Fund sender on AH + AssetHubWestend::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalBSiblingSovereignAccount::get()), + penpal_asset_location.clone().try_into().unwrap(), + AssetHubWestendSender::get(), + TOKEN_AMOUNT, + ); + + // Fund sov of AH on penpal + let ah_sovereign = + PenpalB::sovereign_account_id_of(PenpalB::sibling_location_of(AssetHubWestend::para_id())); + PenpalB::fund_accounts(vec![(ah_sovereign.clone(), INITIAL_FUND)]); + PenpalB::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + RelayLocation::get(), + ah_sovereign.clone(), + INITIAL_FUND, + ); + PenpalB::mint_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + TELEPORTABLE_ASSET_ID, + ah_sovereign.clone(), + TOKEN_AMOUNT, + ); + + // create token + BridgeHubWestend::execute_with(|| { + type Runtime = ::Runtime; + + snowbridge_pallet_system::Tokens::::insert( + token_id, + penpal_asset_location_after_reanchored.clone(), + ); + }); + + // Send token to Ethereum + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + let assets = vec![Asset { + id: penpal_asset_location.clone().into(), + fun: Fungible(TOKEN_AMOUNT / 10), + }]; + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(VersionedLocation::from(ethereum_destination)), + Box::new(beneficiary), + Box::new(VersionedAssets::from(Assets::from(assets))), + 0, + Unlimited, + )); + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred{..}) => {},] + ); + + let events = AssetHubWestend::events(); + // Check that the native asset transferred to some reserved account(sovereign of Ethereum) + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred { amount, to, ..}) + if *amount == TOKEN_AMOUNT/10 && *to == ethereum_sovereign + )), + "native token reserved to Ethereum sovereign account." + ); + }); + + // Send token back from Ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{..}) => {},] + ); + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::ForeignAccountId32 { + para_id: PenpalB::para_id().into(), + id: PenpalBReceiver::get().into(), + fee: XCM_FEE, + }, + amount: TOKEN_AMOUNT / 10, + fee: XCM_FEE, + }, + }); + + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that token burnt from some reserved account + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { .. }) => {},] + ); + }); + + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that token issued to beneficial + assert_expected_events!( + PenpalB, + vec![RuntimeEvent::Assets(pallet_assets::Event::Issued { .. }) => {},] + ); + + let events = PenpalB::events(); + + // Check that token issued to destination account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Issued { amount, owner, ..}) + if *amount == TOKEN_AMOUNT/10 && *owner == PenpalBReceiver::get() + )), + "Token minted to beneficiary." + ); + }) +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 01a762d4b99f..8c122c461ac8 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -14,9 +14,34 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::{xcm_config::UniversalLocation, Runtime}; -use snowbridge_router_primitives::outbound::EthereumBlobExporter; -use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; +#[cfg(not(feature = "runtime-benchmarks"))] +use crate::XcmRouter; +use crate::{ + xcm_config, xcm_config::UniversalLocation, Balances, EthereumInboundQueue, + EthereumOutboundQueue, EthereumSystem, MessageQueue, Runtime, RuntimeEvent, TransactionByteFee, + TreasuryAccount, +}; +use parachains_common::{AccountId, Balance}; +use snowbridge_beacon_primitives::{Fork, ForkVersions}; +use snowbridge_core::{gwei, meth, AllowSiblingsOnly, PricingParameters, Rewards}; +use snowbridge_router_primitives::{inbound::MessageToXcm, outbound::EthereumBlobExporter}; +use sp_core::H160; +use testnet_parachains_constants::rococo::{ + currency::*, + fee::WeightToFee, + snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, +}; + +use crate::xcm_config::RelayNetwork; +#[cfg(feature = "runtime-benchmarks")] +use benchmark_helpers::DoNothingRouter; +use frame_support::{parameter_types, weights::ConstantMultiplier}; +use pallet_xcm::EnsureXcm; +use sp_runtime::{ + traits::{ConstU32, ConstU8, Keccak256}, + FixedU128, +}; +use xcm::prelude::{GlobalConsensus, Location, Parachain}; /// Exports message to the Ethereum Gateway contract. pub type SnowbridgeExporter = EthereumBlobExporter< @@ -24,4 +49,179 @@ pub type SnowbridgeExporter = EthereumBlobExporter< EthereumNetwork, snowbridge_pallet_outbound_queue::Pallet, snowbridge_core::AgentIdOf, + EthereumSystem, >; + +// Ethereum Bridge +parameter_types! { + pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39")); +} + +parameter_types! { + pub const CreateAssetCall: [u8;2] = [53, 0]; + pub const CreateAssetDeposit: u128 = (UNITS / 10) + EXISTENTIAL_DEPOSIT; + pub Parameters: PricingParameters = PricingParameters { + exchange_rate: FixedU128::from_rational(1, 400), + fee_per_gas: gwei(20), + rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, + multiplier: FixedU128::from_rational(1, 1), + }; + pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(rococo_runtime_constants::system_parachain::ASSET_HUB_ID)]); +} + +impl snowbridge_pallet_inbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Verifier = snowbridge_pallet_ethereum_client::Pallet; + type Token = Balances; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmSender = XcmRouter; + #[cfg(feature = "runtime-benchmarks")] + type XcmSender = DoNothingRouter; + type ChannelLookup = EthereumSystem; + type GatewayAddress = EthereumGatewayAddress; + #[cfg(feature = "runtime-benchmarks")] + type Helper = Runtime; + type MessageConverter = MessageToXcm< + CreateAssetCall, + CreateAssetDeposit, + ConstU8, + AccountId, + Balance, + EthereumSystem, + UniversalLocation, + GlobalAssetHub, + >; + type WeightToFee = WeightToFee; + type LengthToFee = ConstantMultiplier; + type MaxMessageSize = ConstU32<2048>; + type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type AssetTransactor = ::AssetTransactor; +} + +impl snowbridge_pallet_outbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Hashing = Keccak256; + type MessageQueue = MessageQueue; + type Decimals = ConstU8<12>; + type MaxMessagePayloadSize = ConstU32<2048>; + type MaxMessagesPerBlock = ConstU32<32>; + type GasMeter = snowbridge_core::outbound::ConstantGasMeter; + type Balance = Balance; + type WeightToFee = WeightToFee; + type WeightInfo = crate::weights::snowbridge_pallet_outbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type Channels = EthereumSystem; +} + +#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [0, 0, 0, 0], // 0x00000000 + epoch: 0, + }, + altair: Fork { + version: [1, 0, 0, 0], // 0x01000000 + epoch: 0, + }, + bellatrix: Fork { + version: [2, 0, 0, 0], // 0x02000000 + epoch: 0, + }, + capella: Fork { + version: [3, 0, 0, 0], // 0x03000000 + epoch: 0, + }, + deneb: Fork { + version: [4, 0, 0, 0], // 0x04000000 + epoch: 0, + } + }; +} + +#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [144, 0, 0, 111], // 0x90000069 + epoch: 0, + }, + altair: Fork { + version: [144, 0, 0, 112], // 0x90000070 + epoch: 50, + }, + bellatrix: Fork { + version: [144, 0, 0, 113], // 0x90000071 + epoch: 100, + }, + capella: Fork { + version: [144, 0, 0, 114], // 0x90000072 + epoch: 56832, + }, + deneb: Fork { + version: [144, 0, 0, 115], // 0x90000073 + epoch: 132608, + }, + }; +} + +impl snowbridge_pallet_ethereum_client::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ForkVersions = ChainForkVersions; + // Free consensus update every epoch. Works out to be 225 updates per day. + type FreeHeadersInterval = ConstU32<32>; + type WeightInfo = crate::weights::snowbridge_pallet_ethereum_client::WeightInfo; +} + +impl snowbridge_pallet_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OutboundQueue = EthereumOutboundQueue; + type SiblingOrigin = EnsureXcm; + type AgentIdOf = snowbridge_core::AgentIdOf; + type TreasuryAccount = TreasuryAccount; + type Token = Balances; + type WeightInfo = crate::weights::snowbridge_pallet_system::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); + type DefaultPricingParameters = Parameters; + type InboundDeliveryCost = EthereumInboundQueue; +} + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmark_helpers { + use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; + use codec::Encode; + use snowbridge_beacon_primitives::BeaconHeader; + use snowbridge_pallet_inbound_queue::BenchmarkHelper; + use sp_core::H256; + use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}; + + impl BenchmarkHelper for Runtime { + fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) { + EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap(); + } + } + + pub struct DoNothingRouter; + impl SendXcm for DoNothingRouter { + type Ticket = Xcm<()>; + + fn validate( + _dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult { + Ok((xcm.clone().unwrap(), Assets::new())) + } + fn deliver(xcm: Xcm<()>) -> Result { + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + Ok(hash) + } + } + + impl snowbridge_pallet_system::BenchmarkHelper for () { + fn make_xcm_origin(location: Location) -> RuntimeOrigin { + RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) + } + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs index c6c188e323af..3831111f0977 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs @@ -253,4 +253,14 @@ impl snowbridge_pallet_system::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + + fn register_token() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 45_000_000 picoseconds. + Weight::from_parts(45_000_000, 6044) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 7922d3ed02b1..7c61189c9954 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -33,6 +33,7 @@ use testnet_parachains_constants::westend::{ snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, }; +use crate::xcm_config::RelayNetwork; #[cfg(feature = "runtime-benchmarks")] use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; @@ -41,6 +42,7 @@ use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, }; +use xcm::prelude::{GlobalConsensus, Location, Parachain}; /// Exports message to the Ethereum Gateway contract. pub type SnowbridgeExporter = EthereumBlobExporter< @@ -48,6 +50,7 @@ pub type SnowbridgeExporter = EthereumBlobExporter< EthereumNetwork, snowbridge_pallet_outbound_queue::Pallet, snowbridge_core::AgentIdOf, + EthereumSystem, >; // Ethereum Bridge @@ -64,6 +67,7 @@ parameter_types! { rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, multiplier: FixedU128::from_rational(1, 1), }; + pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(westend_runtime_constants::system_parachain::ASSET_HUB_ID)]); } impl snowbridge_pallet_inbound_queue::Config for Runtime { @@ -84,6 +88,9 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { ConstU8, AccountId, Balance, + EthereumSystem, + UniversalLocation, + GlobalAssetHub, >; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs index c6c188e323af..3831111f0977 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs @@ -253,4 +253,14 @@ impl snowbridge_pallet_system::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + + fn register_token() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 45_000_000 picoseconds. + Weight::from_parts(45_000_000, 6044) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } } From 5291412e159d3b99c64d5f7f969dbde39d715769 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Mon, 2 Sep 2024 12:47:13 +0200 Subject: [PATCH 207/382] Swaps for XCM delivery fees (#5131) # Context Fees can already be paid in other assets locally thanks to the Trader implementations we have. This doesn't work when sending messages because delivery fees go through a different mechanism altogether. The idea is to fix this leveraging the `AssetExchanger` config item that's able to turn the asset the user wants to pay fees in into the asset the router expects for delivery fees. # Main addition An adapter was needed to use `pallet-asset-conversion` for exchanging assets in XCM. This was created in https://github.com/paritytech/polkadot-sdk/pull/5130. The XCM executor was modified to use `AssetExchanger` (when available) to swap assets to pay for delivery fees. ## Limitations We can only pay for delivery fees in different assets in intermediate hops. We can't pay in different assets locally. The first hop will always need the native token of the chain (or whatever is specified in the `XcmRouter`). This is a byproduct of using the `BuyExecution` instruction to know which asset should be used for delivery fee payment. Since this instruction is not present when executing an XCM locally, we are left with this limitation. To illustrate this limitation, I'll show two scenarios. All chains involved have pools. ### Scenario 1 Parachain A --> Parachain B Here, parachain A can use any asset in a pool with its native asset to pay for local execution fees. However, as of now we can't use those for local delivery fees. This means transfers from A to B need some amount of A's native token to pay for delivery fees. ### Scenario 2 Parachain A --> Parachain C --> Parachain B Here, Parachain C's remote delivery fees can be paid with any asset in a pool with its native asset. This allows a reserve asset transfer between A and B with C as the reserve to only need A's native token at the starting hop. After that, it could all be pool assets. ## Future work The fact that delivery fees go through a totally different mechanism results in a lot of bugs and pain points. Unfortunately, this is not so easy to solve in a backwards compatible manner. Delivery fees will be integrated into the language in future XCM versions, following https://github.com/polkadot-fellows/xcm-format/pull/53. Old PR: https://github.com/paritytech/polkadot-sdk/pull/4375. --- Cargo.lock | 3 + .../assets/asset-hub-westend/src/genesis.rs | 3 +- .../parachains/testing/penpal/src/genesis.rs | 5 +- .../parachains/testing/penpal/src/lib.rs | 2 + .../tests/assets/asset-hub-rococo/src/lib.rs | 1 + .../src/tests/reserve_transfer.rs | 416 ++++++++++++++++- .../tests/assets/asset-hub-westend/src/lib.rs | 1 + .../src/tests/hybrid_transfers.rs | 6 +- .../src/tests/reserve_transfer.rs | 420 +++++++++++++++++- .../assets/asset-hub-rococo/src/xcm_config.rs | 39 +- .../asset-hub-westend/src/xcm_config.rs | 39 +- .../runtimes/assets/common/Cargo.toml | 3 + .../runtimes/assets/common/src/lib.rs | 3 +- .../runtimes/testing/penpal/Cargo.toml | 7 + .../runtimes/testing/penpal/src/lib.rs | 123 ++++- .../runtimes/testing/penpal/src/xcm_config.rs | 75 +++- .../single_asset_adapter/adapter.rs | 2 +- polkadot/xcm/xcm-executor/src/config.rs | 7 + polkadot/xcm/xcm-executor/src/lib.rs | 133 +++++- prdoc/pr_5131.prdoc | 42 ++ 20 files changed, 1259 insertions(+), 71 deletions(-) create mode 100644 prdoc/pr_5131.prdoc diff --git a/Cargo.lock b/Cargo.lock index 9d8f4cef1420..c2cbb0f6d4f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1080,6 +1080,7 @@ dependencies = [ "impl-trait-for-tuples", "log", "pallet-asset-conversion", + "pallet-assets", "pallet-xcm", "parachains-common", "parity-scale-codec", @@ -12567,6 +12568,7 @@ dependencies = [ "frame-try-runtime", "hex-literal", "log", + "pallet-asset-conversion", "pallet-asset-tx-payment", "pallet-assets", "pallet-aura", @@ -12585,6 +12587,7 @@ dependencies = [ "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", + "primitive-types", "scale-info", "smallvec", "sp-api", diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs index d20e059f9fea..2876474e094c 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs @@ -27,6 +27,7 @@ use parachains_common::{AccountId, Balance}; pub const PARA_ID: u32 = 1000; pub const ED: Balance = testnet_parachains_constants::westend::currency::EXISTENTIAL_DEPOSIT; +pub const USDT_ED: Balance = 70_000; parameter_types! { pub AssetHubWestendAssetOwner: AccountId = get_account_id_from_seed::("Alice"); @@ -67,7 +68,7 @@ pub fn genesis() -> Storage { assets: asset_hub_westend_runtime::AssetsConfig { assets: vec![ (RESERVABLE_ASSET_ID, AssetHubWestendAssetOwner::get(), false, ED), - (USDT_ID, AssetHubWestendAssetOwner::get(), true, ED), + (USDT_ID, AssetHubWestendAssetOwner::get(), true, USDT_ED), ], ..Default::default() }, diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs index 38c94b34aa2e..2c34b7e96f5e 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs @@ -27,6 +27,7 @@ use penpal_runtime::xcm_config::{LocalReservableFromAssetHub, RelayLocation, Usd pub const PARA_ID_A: u32 = 2000; pub const PARA_ID_B: u32 = 2001; pub const ED: Balance = penpal_runtime::EXISTENTIAL_DEPOSIT; +pub const USDT_ED: Balance = 70_000; parameter_types! { pub PenpalSudoAccount: AccountId = get_account_id_from_seed::("Alice"); @@ -81,8 +82,8 @@ pub fn genesis(para_id: u32) -> Storage { (RelayLocation::get(), PenpalAssetOwner::get(), true, ED), // Sufficient AssetHub asset representation (LocalReservableFromAssetHub::get(), PenpalAssetOwner::get(), true, ED), - // USDT from Asset Hub - (UsdtFromAssetHub::get(), PenpalAssetOwner::get(), true, ED), + // USDT from AssetHub + (UsdtFromAssetHub::get(), PenpalAssetOwner::get(), true, USDT_ED), ], ..Default::default() }, diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs index 91793d33f304..92dfa30f2e83 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs @@ -53,6 +53,7 @@ decl_test_parachains! { PolkadotXcm: penpal_runtime::PolkadotXcm, Assets: penpal_runtime::Assets, ForeignAssets: penpal_runtime::ForeignAssets, + AssetConversion: penpal_runtime::AssetConversion, Balances: penpal_runtime::Balances, } }, @@ -76,6 +77,7 @@ decl_test_parachains! { PolkadotXcm: penpal_runtime::PolkadotXcm, Assets: penpal_runtime::Assets, ForeignAssets: penpal_runtime::ForeignAssets, + AssetConversion: penpal_runtime::AssetConversion, Balances: penpal_runtime::Balances, } }, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs index 87a090bf1ae6..f4fe1478f3ed 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs @@ -66,6 +66,7 @@ mod imports { CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub, LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub, LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, + UsdtFromAssetHub as PenpalUsdtFromAssetHub, }, PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, ED as PENPAL_ED, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs index 70dde03d75a2..faff5f7660c2 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs @@ -60,10 +60,10 @@ pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { AssetHubRococo::assert_xcm_pallet_attempted_complete(None); let sov_acc_of_dest = AssetHubRococo::sovereign_account_id_of(t.args.dest.clone()); - for (idx, asset) in t.args.assets.into_inner().into_iter().enumerate() { + for asset in t.args.assets.into_inner().into_iter() { let expected_id = asset.id.0.clone().try_into().unwrap(); let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap(); - if idx == t.args.fee_asset_item as usize { + if asset.id == AssetId(Location::new(1, [])) { assert_expected_events!( AssetHubRococo, vec![ @@ -77,6 +77,23 @@ pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { }, ] ); + } else if matches!( + asset.id.0.unpack(), + (0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(_)]) + ) { + assert_expected_events!( + AssetHubRococo, + vec![ + // Amount of trust-backed asset is transferred to Parachain's Sovereign account + RuntimeEvent::Assets( + pallet_assets::Event::Transferred { from, to, amount, .. }, + ) => { + from: *from == t.sender.account_id, + to: *to == sov_acc_of_dest, + amount: *amount == asset_amount, + }, + ] + ); } else { assert_expected_events!( AssetHubRococo, @@ -388,6 +405,38 @@ pub fn para_to_para_through_hop_sender_assertions(t: Test::RuntimeEvent; + let sov_penpal_a_on_ah = AssetHubRococo::sovereign_account_id_of( + AssetHubRococo::sibling_location_of(PenpalA::para_id()), + ); + let sov_penpal_b_on_ah = AssetHubRococo::sovereign_account_id_of( + AssetHubRococo::sibling_location_of(PenpalB::para_id()), + ); + + assert_expected_events!( + AssetHubRococo, + vec![ + // Withdrawn from sender parachain SA + RuntimeEvent::Assets( + pallet_assets::Event::Burned { owner, balance, .. } + ) => { + owner: *owner == sov_penpal_a_on_ah, + balance: *balance == t.args.amount, + }, + // Deposited to receiver parachain SA + RuntimeEvent::Assets( + pallet_assets::Event::Deposited { who, .. } + ) => { + who: *who == sov_penpal_b_on_ah, + }, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); +} + fn para_to_para_relay_hop_assertions(t: ParaToParaThroughRelayTest) { type RuntimeEvent = ::RuntimeEvent; let sov_penpal_a_on_rococo = @@ -469,6 +518,19 @@ fn system_para_to_para_reserve_transfer_assets(t: SystemParaToParaTest) -> Dispa ) } +fn para_to_para_through_asset_hub_limited_reserve_transfer_assets( + t: ParaToParaThroughAHTest, +) -> DispatchResult { + ::PolkadotXcm::limited_reserve_transfer_assets( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + t.args.fee_asset_item, + t.args.weight_limit, + ) +} + fn para_to_system_para_reserve_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult { ::PolkadotXcm::limited_reserve_transfer_assets( t.signed_origin, @@ -1136,3 +1198,353 @@ fn reserve_transfer_native_asset_from_para_to_para_through_relay() { // Receiver's balance is increased assert!(receiver_assets_after > receiver_assets_before); } + +// ============================================================================ +// ==== Reserve Transfers USDT - AssetHub->Parachain - pay fees using pool ==== +// ============================================================================ +#[test] +fn reserve_transfer_usdt_from_asset_hub_to_para() { + let usdt_id = 1984u32; + let penpal_location = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let penpal_sov_account = AssetHubRococo::sovereign_account_id_of(penpal_location.clone()); + + // Create SA-of-Penpal-on-AHW with ED. + // This ED isn't reflected in any derivative in a PenpalA account. + AssetHubRococo::fund_accounts(vec![(penpal_sov_account.clone().into(), ASSET_HUB_ROCOCO_ED)]); + + let sender = AssetHubRococoSender::get(); + let receiver = PenpalAReceiver::get(); + let asset_amount_to_send = 1_000_000_000_000; + + AssetHubRococo::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type Assets = ::Assets; + assert_ok!(>::mint_into( + usdt_id.into(), + &AssetHubRococoSender::get(), + asset_amount_to_send + 10_000_000_000_000, // Make sure it has enough. + )); + }); + + let relay_asset_penpal_pov = RelayLocation::get(); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + + // Setup the pool between `relay_asset_penpal_pov` and `usdt_from_asset_hub` on PenpalA. + // So we can swap the custom asset that comes from AssetHubRococo for native asset to pay for + // fees. + PenpalA::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_from_asset_hub.clone().into(), + PenpalASender::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(PenpalASender::get()), + Box::new(relay_asset_penpal_pov.clone()), + Box::new(usdt_from_asset_hub.clone()), + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(PenpalASender::get()), + Box::new(relay_asset_penpal_pov), + Box::new(usdt_from_asset_hub.clone()), + // `usdt_from_asset_hub` is worth a third of `relay_asset_penpal_pov` + 1_000_000_000_000, + 3_000_000_000_000, + 0, + 0, + PenpalASender::get().into() + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + let assets: Assets = vec![( + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(usdt_id.into())], + asset_amount_to_send, + ) + .into()] + .into(); + + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + penpal_location, + receiver.clone(), + asset_amount_to_send, + assets, + None, + 0, + ), + }; + let mut test = SystemParaToParaTest::new(test_args); + + let sender_initial_balance = AssetHubRococo::execute_with(|| { + type Assets = ::Assets; + >::balance(usdt_id, &sender) + }); + let sender_initial_native_balance = AssetHubRococo::execute_with(|| { + type Balances = ::Balances; + Balances::free_balance(&sender) + }); + let receiver_initial_balance = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &receiver) + }); + + test.set_assertion::(system_para_to_para_sender_assertions); + test.set_assertion::(system_para_to_para_receiver_assertions); + test.set_dispatchable::(system_para_to_para_reserve_transfer_assets); + test.assert(); + + let sender_after_balance = AssetHubRococo::execute_with(|| { + type Assets = ::Assets; + >::balance(usdt_id, &sender) + }); + let sender_after_native_balance = AssetHubRococo::execute_with(|| { + type Balances = ::Balances; + Balances::free_balance(&sender) + }); + let receiver_after_balance = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub, &receiver) + }); + + // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we allow payment with different assets locally, this should be the same, since + // they aren't used for fees. + assert!(sender_after_native_balance < sender_initial_native_balance); + // Sender account's balance decreases. + assert_eq!(sender_after_balance, sender_initial_balance - asset_amount_to_send); + // Receiver account's balance increases. + assert!(receiver_after_balance > receiver_initial_balance); + assert!(receiver_after_balance < receiver_initial_balance + asset_amount_to_send); +} + +// =================================================================================== +// == Reserve Transfers USDT - Parachain->AssetHub->Parachain - pay fees using pool == +// =================================================================================== +// +// Transfer USDT From Penpal A to Penpal B with AssetHub as the reserve, while paying fees using +// USDT by making use of existing USDT pools on AssetHub and destination. +#[test] +fn reserve_transfer_usdt_from_para_to_para_through_asset_hub() { + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let asset_amount_to_send: Balance = ROCOCO_ED * 10000; + let fee_amount_to_send: Balance = ROCOCO_ED * 10000; + let sender_chain_as_seen_by_asset_hub = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let sov_of_sender_on_asset_hub = + AssetHubRococo::sovereign_account_id_of(sender_chain_as_seen_by_asset_hub); + let receiver_as_seen_by_asset_hub = AssetHubRococo::sibling_location_of(PenpalB::para_id()); + let sov_of_receiver_on_asset_hub = + AssetHubRococo::sovereign_account_id_of(receiver_as_seen_by_asset_hub); + + // Create SA-of-Penpal-on-AHW with ED. + // This ED isn't reflected in any derivative in a PenpalA account. + AssetHubRococo::fund_accounts(vec![ + (sov_of_sender_on_asset_hub.clone().into(), ASSET_HUB_ROCOCO_ED), + (sov_of_receiver_on_asset_hub.clone().into(), ASSET_HUB_ROCOCO_ED), + ]); + + // Give USDT to sov account of sender. + let usdt_id = 1984; + AssetHubRococo::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type Assets = ::Assets; + assert_ok!(>::mint_into( + usdt_id.into(), + &sov_of_sender_on_asset_hub.clone().into(), + asset_amount_to_send + fee_amount_to_send, + )); + }); + + // We create a pool between WND and USDT in AssetHub. + let native_asset: Location = Parent.into(); + let usdt = Location::new( + 0, + [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(usdt_id.into())], + ); + + // set up pool with USDT <> native pair + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::Assets::mint( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + usdt_id.into(), + AssetHubRococoSender::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + Box::new(native_asset.clone()), + Box::new(usdt.clone()), + )); + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + Box::new(native_asset), + Box::new(usdt), + 1_000_000_000_000, + 2_000_000_000_000, // usdt is worth half of `native_asset` + 0, + 0, + AssetHubRococoSender::get().into() + )); + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + + // We also need a pool between WND and USDT on PenpalB. + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + let relay_asset = RelayLocation::get(); + + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_from_asset_hub.clone().into(), + PenpalBReceiver::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(PenpalBReceiver::get()), + Box::new(relay_asset.clone()), + Box::new(usdt_from_asset_hub.clone()), + )); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(PenpalBReceiver::get()), + Box::new(relay_asset), + Box::new(usdt_from_asset_hub.clone()), + 1_000_000_000_000, + 2_000_000_000_000, // `usdt_from_asset_hub` is worth half of `relay_asset` + 0, + 0, + PenpalBReceiver::get().into() + )); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + PenpalA::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type ForeignAssets = ::ForeignAssets; + assert_ok!(>::mint_into( + usdt_from_asset_hub.clone(), + &sender, + asset_amount_to_send + fee_amount_to_send, + )); + }); + + // Prepare assets to transfer. + let assets: Assets = + (usdt_from_asset_hub.clone(), asset_amount_to_send + fee_amount_to_send).into(); + // Just to be very specific we're not including anything other than USDT. + assert_eq!(assets.len(), 1); + + // Give the sender enough Relay tokens to pay for local delivery fees. + // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we support local delivery fee payment in other assets, we don't need this. + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + RelayLocation::get(), + sender.clone(), + 10_000_000_000_000, // Large estimate to make sure it works. + ); + + // Init values for Parachain Destination + let receiver = PenpalBReceiver::get(); + + // Init Test + let fee_asset_index = 0; + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination, + receiver.clone(), + asset_amount_to_send, + assets, + None, + fee_asset_index, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); + + // Query initial balances + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &sender) + }); + let receiver_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &receiver) + }); + test.set_assertion::(para_to_para_through_hop_sender_assertions); + test.set_assertion::(para_to_para_asset_hub_hop_assertions); + test.set_assertion::(para_to_para_through_hop_receiver_assertions); + test.set_dispatchable::( + para_to_para_through_asset_hub_limited_reserve_transfer_assets, + ); + test.assert(); + + // Query final balances + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &sender) + }); + let receiver_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub, &receiver) + }); + + // Sender's balance is reduced by amount + assert!(sender_assets_after < sender_assets_before - asset_amount_to_send); + // Receiver's balance is increased + assert!(receiver_assets_after > receiver_assets_before); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs index a887ee6a532a..f568fb4101db 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs @@ -64,6 +64,7 @@ mod imports { CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub, LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub, LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, + UsdtFromAssetHub as PenpalUsdtFromAssetHub, }, PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs index 49dfe8d58394..975bacea7b4f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs @@ -613,10 +613,10 @@ fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { >::balance(roc_at_westend_parachains, &receiver) }); - // Sender's balance is reduced by amount sent plus delivery fees + // Sender's balance is reduced by amount sent. assert!(sender_wnds_after < sender_wnds_before - wnd_to_send); assert_eq!(sender_rocs_after, sender_rocs_before - roc_to_send); - // Sovereign accounts on reserve are changed accordingly + // Sovereign accounts on reserve are changed accordingly. assert_eq!( wnds_in_sender_reserve_on_ah_after, wnds_in_sender_reserve_on_ah_before - wnd_to_send @@ -630,7 +630,7 @@ fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { rocs_in_receiver_reserve_on_ah_after, rocs_in_receiver_reserve_on_ah_before + roc_to_send ); - // Receiver's balance is increased + // Receiver's balance is increased by amount sent minus delivery fees. assert!(receiver_wnds_after > receiver_wnds_before); assert_eq!(receiver_rocs_after, receiver_rocs_before + roc_to_send); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs index 59f63d380590..53b6939298da 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs @@ -60,10 +60,10 @@ pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { AssetHubWestend::assert_xcm_pallet_attempted_complete(None); let sov_acc_of_dest = AssetHubWestend::sovereign_account_id_of(t.args.dest.clone()); - for (idx, asset) in t.args.assets.into_inner().into_iter().enumerate() { + for asset in t.args.assets.into_inner().into_iter() { let expected_id = asset.id.0.clone().try_into().unwrap(); let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap(); - if idx == t.args.fee_asset_item as usize { + if asset.id == AssetId(Location::new(1, [])) { assert_expected_events!( AssetHubWestend, vec![ @@ -77,6 +77,23 @@ pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { }, ] ); + } else if matches!( + asset.id.0.unpack(), + (0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(_)]) + ) { + assert_expected_events!( + AssetHubWestend, + vec![ + // Amount of trust-backed asset is transferred to Parachain's Sovereign account + RuntimeEvent::Assets( + pallet_assets::Event::Transferred { from, to, amount, .. }, + ) => { + from: *from == t.sender.account_id, + to: *to == sov_acc_of_dest, + amount: *amount == asset_amount, + }, + ] + ); } else { assert_expected_events!( AssetHubWestend, @@ -418,6 +435,38 @@ fn para_to_para_relay_hop_assertions(t: ParaToParaThroughRelayTest) { ); } +fn para_to_para_asset_hub_hop_assertions(t: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + let sov_penpal_a_on_ah = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalA::para_id()), + ); + let sov_penpal_b_on_ah = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalB::para_id()), + ); + + assert_expected_events!( + AssetHubWestend, + vec![ + // Withdrawn from sender parachain SA + RuntimeEvent::Assets( + pallet_assets::Event::Burned { owner, balance, .. } + ) => { + owner: *owner == sov_penpal_a_on_ah, + balance: *balance == t.args.amount, + }, + // Deposited to receiver parachain SA + RuntimeEvent::Assets( + pallet_assets::Event::Deposited { who, .. } + ) => { + who: *who == sov_penpal_b_on_ah, + }, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); +} + pub fn para_to_para_through_hop_receiver_assertions(t: Test) { type RuntimeEvent = ::RuntimeEvent; @@ -493,6 +542,19 @@ fn para_to_para_through_relay_limited_reserve_transfer_assets( ) } +fn para_to_para_through_asset_hub_limited_reserve_transfer_assets( + t: ParaToParaThroughAHTest, +) -> DispatchResult { + ::PolkadotXcm::limited_reserve_transfer_assets( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + t.args.fee_asset_item, + t.args.weight_limit, + ) +} + /// Reserve Transfers of native asset from Relay Chain to the Asset Hub shouldn't work #[test] fn reserve_transfer_native_asset_from_relay_to_asset_hub_fails() { @@ -1133,8 +1195,360 @@ fn reserve_transfer_native_asset_from_para_to_para_through_relay() { >::balance(relay_native_asset_location, &receiver) }); - // Sender's balance is reduced by amount sent plus delivery fees + // Sender's balance is reduced by amount sent plus delivery fees. assert!(sender_assets_after < sender_assets_before - amount_to_send); + // Receiver's balance is increased by `amount_to_send` minus delivery fees. + assert!(receiver_assets_after > receiver_assets_before); + assert!(receiver_assets_after < receiver_assets_before + amount_to_send); +} + +// ============================================================================ +// ==== Reserve Transfers USDT - AssetHub->Parachain - pay fees using pool ==== +// ============================================================================ +#[test] +fn reserve_transfer_usdt_from_asset_hub_to_para() { + let usdt_id = 1984u32; + let penpal_location = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let penpal_sov_account = AssetHubWestend::sovereign_account_id_of(penpal_location.clone()); + + // Create SA-of-Penpal-on-AHW with ED. + // This ED isn't reflected in any derivative in a PenpalA account. + AssetHubWestend::fund_accounts(vec![(penpal_sov_account.clone().into(), ASSET_HUB_WESTEND_ED)]); + + let sender = AssetHubWestendSender::get(); + let receiver = PenpalAReceiver::get(); + let asset_amount_to_send = 1_000_000_000_000; + + AssetHubWestend::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type Assets = ::Assets; + assert_ok!(>::mint_into( + usdt_id.into(), + &AssetHubWestendSender::get(), + asset_amount_to_send + 10_000_000_000_000, // Make sure it has enough. + )); + }); + + let relay_asset_penpal_pov = RelayLocation::get(); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + + // Setup the pool between `relay_asset_penpal_pov` and `usdt_from_asset_hub` on PenpalA. + // So we can swap the custom asset that comes from AssetHubWestend for native asset to pay for + // fees. + PenpalA::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_from_asset_hub.clone().into(), + PenpalASender::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(PenpalASender::get()), + Box::new(relay_asset_penpal_pov.clone()), + Box::new(usdt_from_asset_hub.clone()), + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(PenpalASender::get()), + Box::new(relay_asset_penpal_pov), + Box::new(usdt_from_asset_hub.clone()), + // `usdt_from_asset_hub` is worth a third of `relay_asset_penpal_pov` + 1_000_000_000_000, + 3_000_000_000_000, + 0, + 0, + PenpalASender::get().into() + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + let assets: Assets = vec![( + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(usdt_id.into())], + asset_amount_to_send, + ) + .into()] + .into(); + + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + penpal_location, + receiver.clone(), + asset_amount_to_send, + assets, + None, + 0, + ), + }; + let mut test = SystemParaToParaTest::new(test_args); + + let sender_initial_balance = AssetHubWestend::execute_with(|| { + type Assets = ::Assets; + >::balance(usdt_id, &sender) + }); + let sender_initial_native_balance = AssetHubWestend::execute_with(|| { + type Balances = ::Balances; + Balances::free_balance(&sender) + }); + let receiver_initial_balance = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &receiver) + }); + + test.set_assertion::(system_para_to_para_sender_assertions); + test.set_assertion::(system_para_to_para_receiver_assertions); + test.set_dispatchable::(system_para_to_para_reserve_transfer_assets); + test.assert(); + + let sender_after_balance = AssetHubWestend::execute_with(|| { + type Assets = ::Assets; + >::balance(usdt_id, &sender) + }); + let sender_after_native_balance = AssetHubWestend::execute_with(|| { + type Balances = ::Balances; + Balances::free_balance(&sender) + }); + let receiver_after_balance = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub, &receiver) + }); + + // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we allow payment with different assets locally, this should be the same, since + // they aren't used for fees. + assert!(sender_after_native_balance < sender_initial_native_balance); + // Sender account's balance decreases. + assert_eq!(sender_after_balance, sender_initial_balance - asset_amount_to_send); + // Receiver account's balance increases. + assert!(receiver_after_balance > receiver_initial_balance); + assert!(receiver_after_balance < receiver_initial_balance + asset_amount_to_send); +} + +// =================================================================================== +// == Reserve Transfers USDT - Parachain->AssetHub->Parachain - pay fees using pool == +// =================================================================================== +// +// Transfer USDT From Penpal A to Penpal B with AssetHub as the reserve, while paying fees using +// USDT by making use of existing USDT pools on AssetHub and destination. +#[test] +fn reserve_transfer_usdt_from_para_to_para_through_asset_hub() { + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let asset_amount_to_send: Balance = WESTEND_ED * 10000; + let fee_amount_to_send: Balance = WESTEND_ED * 10000; + let sender_chain_as_seen_by_asset_hub = + AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_of_sender_on_asset_hub = + AssetHubWestend::sovereign_account_id_of(sender_chain_as_seen_by_asset_hub); + let receiver_as_seen_by_asset_hub = AssetHubWestend::sibling_location_of(PenpalB::para_id()); + let sov_of_receiver_on_asset_hub = + AssetHubWestend::sovereign_account_id_of(receiver_as_seen_by_asset_hub); + + // Create SA-of-Penpal-on-AHW with ED. + // This ED isn't reflected in any derivative in a PenpalA account. + AssetHubWestend::fund_accounts(vec![ + (sov_of_sender_on_asset_hub.clone().into(), ASSET_HUB_WESTEND_ED), + (sov_of_receiver_on_asset_hub.clone().into(), ASSET_HUB_WESTEND_ED), + ]); + + // Give USDT to sov account of sender. + let usdt_id = 1984; + AssetHubWestend::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type Assets = ::Assets; + assert_ok!(>::mint_into( + usdt_id.into(), + &sov_of_sender_on_asset_hub.clone().into(), + asset_amount_to_send + fee_amount_to_send, + )); + }); + + // We create a pool between WND and USDT in AssetHub. + let native_asset: Location = Parent.into(); + let usdt = Location::new( + 0, + [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(usdt_id.into())], + ); + + // set up pool with USDT <> native pair + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::Assets::mint( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + usdt_id.into(), + AssetHubWestendSender::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(native_asset.clone()), + Box::new(usdt.clone()), + )); + + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(native_asset), + Box::new(usdt), + 1_000_000_000_000, + 2_000_000_000_000, // usdt is worth half of `native_asset` + 0, + 0, + AssetHubWestendSender::get().into() + )); + + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + + // We also need a pool between WND and USDT on PenpalB. + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + let relay_asset = RelayLocation::get(); + + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_from_asset_hub.clone().into(), + PenpalBReceiver::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(PenpalBReceiver::get()), + Box::new(relay_asset.clone()), + Box::new(usdt_from_asset_hub.clone()), + )); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(PenpalBReceiver::get()), + Box::new(relay_asset), + Box::new(usdt_from_asset_hub.clone()), + 1_000_000_000_000, + 2_000_000_000_000, // `usdt_from_asset_hub` is worth half of `relay_asset` + 0, + 0, + PenpalBReceiver::get().into() + )); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + PenpalA::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type ForeignAssets = ::ForeignAssets; + assert_ok!(>::mint_into( + usdt_from_asset_hub.clone(), + &sender, + asset_amount_to_send + fee_amount_to_send, + )); + }); + + // Prepare assets to transfer. + let assets: Assets = + (usdt_from_asset_hub.clone(), asset_amount_to_send + fee_amount_to_send).into(); + // Just to be very specific we're not including anything other than USDT. + assert_eq!(assets.len(), 1); + + // Give the sender enough Relay tokens to pay for local delivery fees. + // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we support local delivery fee payment in other assets, we don't need this. + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + RelayLocation::get(), + sender.clone(), + 10_000_000_000_000, // Large estimate to make sure it works. + ); + + // Init values for Parachain Destination + let receiver = PenpalBReceiver::get(); + + // Init Test + let fee_asset_index = 0; + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination, + receiver.clone(), + asset_amount_to_send, + assets, + None, + fee_asset_index, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); + + // Query initial balances + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &sender) + }); + let receiver_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &receiver) + }); + test.set_assertion::(para_to_para_through_hop_sender_assertions); + test.set_assertion::(para_to_para_asset_hub_hop_assertions); + test.set_assertion::(para_to_para_through_hop_receiver_assertions); + test.set_dispatchable::( + para_to_para_through_asset_hub_limited_reserve_transfer_assets, + ); + test.assert(); + + // Query final balances + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &sender) + }); + let receiver_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub, &receiver) + }); + + // Sender's balance is reduced by amount + assert!(sender_assets_after < sender_assets_before - asset_amount_to_send); // Receiver's balance is increased assert!(receiver_assets_after > receiver_assets_before); } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 2d1914e059bf..f263baf4bef8 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -21,7 +21,7 @@ use super::{ XcmpQueue, }; use assets_common::{ - matching::{FromNetwork, FromSiblingParachain, IsForeignConcreteAsset}, + matching::{FromNetwork, FromSiblingParachain, IsForeignConcreteAsset, ParentLocation}, TrustBackedAssetsAsLocation, }; use frame_support::{ @@ -43,7 +43,7 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor; -use sp_runtime::traits::{AccountIdConversion, ConvertInto}; +use sp_runtime::traits::{AccountIdConversion, ConvertInto, TryConvertInto}; use testnet_parachains_constants::rococo::snowbridge::{ EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX, }; @@ -54,12 +54,13 @@ use xcm_builder::{ DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, - NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignPaidRemoteExporter, + MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, + WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -326,6 +327,28 @@ pub type TrustedTeleporters = ( IsForeignConcreteAsset>>, ); +/// Asset converter for pool assets. +/// Used to convert one asset to another, when there is a pool available between the two. +/// This type thus allows paying fees with any asset as long as there is a pool between said +/// asset and the asset required for fee payment. +pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< + crate::AssetConversion, + crate::NativeAndAssets, + ( + TrustBackedAssetsAsLocation, + ForeignAssetsConvertedConcreteId, + // `ForeignAssetsConvertedConcreteId` excludes the relay token, so we add it back here. + MatchedConvertedConcreteId< + xcm::v4::Location, + Balance, + Equals, + WithLatestLocationConverter, + TryConvertInto, + >, + ), + AccountId, +>; + pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -407,7 +430,7 @@ impl xcm_executor::Config for XcmConfig { type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); - type AssetExchanger = (); + type AssetExchanger = PoolAssetsExchanger; type FeeManager = XcmFeeManagerFromComponents< WaivedLocations, SendXcmFeeToAccount, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index d61381d3f50b..bc5d07f552b0 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -21,7 +21,7 @@ use super::{ XcmpQueue, }; use assets_common::{ - matching::{FromSiblingParachain, IsForeignConcreteAsset}, + matching::{FromSiblingParachain, IsForeignConcreteAsset, ParentLocation}, TrustBackedAssetsAsLocation, }; use frame_support::{ @@ -43,7 +43,7 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor; -use sp_runtime::traits::{AccountIdConversion, ConvertInto}; +use sp_runtime::traits::{AccountIdConversion, ConvertInto, TryConvertInto}; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, @@ -51,12 +51,13 @@ use xcm_builder::{ DenyReserveTransferToRelayChain, DenyThenTry, DescribeFamily, DescribePalletTerminal, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, - NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignPaidRemoteExporter, + MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, + WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -350,6 +351,28 @@ pub type TrustedTeleporters = ( IsForeignConcreteAsset>>, ); +/// Asset converter for pool assets. +/// Used to convert one asset to another, when there is a pool available between the two. +/// This type thus allows paying fees with any asset as long as there is a pool between said +/// asset and the asset required for fee payment. +pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< + crate::AssetConversion, + crate::NativeAndAssets, + ( + TrustBackedAssetsAsLocation, + ForeignAssetsConvertedConcreteId, + // `ForeignAssetsConvertedConcreteId` excludes the relay token, so we add it back here. + MatchedConvertedConcreteId< + xcm::v4::Location, + Balance, + Equals, + WithLatestLocationConverter, + TryConvertInto, + >, + ), + AccountId, +>; + pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -430,7 +453,7 @@ impl xcm_executor::Config for XcmConfig { type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); - type AssetExchanger = (); + type AssetExchanger = PoolAssetsExchanger; type FeeManager = XcmFeeManagerFromComponents< WaivedLocations, SendXcmFeeToAccount, diff --git a/cumulus/parachains/runtimes/assets/common/Cargo.toml b/cumulus/parachains/runtimes/assets/common/Cargo.toml index c6740269339d..fb66f0de2322 100644 --- a/cumulus/parachains/runtimes/assets/common/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/common/Cargo.toml @@ -19,6 +19,7 @@ impl-trait-for-tuples = { workspace = true } frame-support = { workspace = true } sp-api = { workspace = true } sp-runtime = { workspace = true } +pallet-assets = { workspace = true } pallet-asset-conversion = { workspace = true } # Polkadot @@ -42,6 +43,7 @@ std = [ "frame-support/std", "log/std", "pallet-asset-conversion/std", + "pallet-assets/std", "pallet-xcm/std", "parachains-common/std", "scale-info/std", @@ -56,6 +58,7 @@ runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", "frame-support/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/assets/common/src/lib.rs b/cumulus/parachains/runtimes/assets/common/src/lib.rs index 4bb593f98929..deda5fa4ab9c 100644 --- a/cumulus/parachains/runtimes/assets/common/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/common/src/lib.rs @@ -29,7 +29,7 @@ use crate::matching::{LocalLocationPattern, ParentLocation}; use frame_support::traits::{Equals, EverythingBut}; use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId}; use sp_runtime::traits::TryConvertInto; -use xcm::latest::Location; +use xcm::prelude::*; use xcm_builder::{ AsPrefixedGeneralIndex, MatchedConvertedConcreteId, StartsWith, WithLatestLocationConverter, }; @@ -138,7 +138,6 @@ pub type PoolAssetsConvertedConcreteId = mod tests { use super::*; use sp_runtime::traits::MaybeEquivalence; - use xcm::prelude::*; use xcm_builder::{StartsWithExplicitGlobalConsensus, WithLatestLocationConverter}; use xcm_executor::traits::{Error as MatchError, MatchesFungibles}; diff --git a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml index e16629302be6..96338b645581 100644 --- a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml @@ -42,6 +42,7 @@ pallet-transaction-payment = { workspace = true } pallet-transaction-payment-rpc-runtime-api = { workspace = true } pallet-asset-tx-payment = { workspace = true } pallet-assets = { workspace = true } +pallet-asset-conversion = { workspace = true } sp-api = { workspace = true } sp-block-builder = { workspace = true } sp-consensus-aura = { workspace = true } @@ -79,6 +80,8 @@ parachain-info = { workspace = true } parachains-common = { workspace = true } assets-common = { workspace = true } +primitive-types = { version = "0.12.1", default-features = false, features = ["codec", "num-traits", "scale-info"] } + [features] default = ["std"] std = [ @@ -99,6 +102,7 @@ std = [ "frame-system/std", "frame-try-runtime?/std", "log/std", + "pallet-asset-conversion/std", "pallet-asset-tx-payment/std", "pallet-assets/std", "pallet-aura/std", @@ -117,6 +121,7 @@ std = [ "polkadot-parachain-primitives/std", "polkadot-primitives/std", "polkadot-runtime-common/std", + "primitive-types/std", "scale-info/std", "sp-api/std", "sp-block-builder/std", @@ -149,6 +154,7 @@ runtime-benchmarks = [ "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "hex-literal", + "pallet-asset-conversion/runtime-benchmarks", "pallet-asset-tx-payment/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", @@ -176,6 +182,7 @@ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "frame-try-runtime/try-runtime", + "pallet-asset-conversion/try-runtime", "pallet-asset-tx-payment/try-runtime", "pallet-assets/try-runtime", "pallet-aura/try-runtime", diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index bf39c02a3f59..7d19c0ed8d85 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -35,6 +35,10 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use assets_common::{ + local_and_foreign_assets::{LocalFromLeft, TargetFromLeft}, + AssetIdForTrustBackedAssetsConvert, +}; use codec::Encode; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; @@ -42,10 +46,13 @@ use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, genesis_builder_helper::{build_state, get_preset}, + ord_parameter_types, pallet_prelude::Weight, parameter_types, traits::{ - AsEnsureOriginWithArg, ConstBool, ConstU32, ConstU64, ConstU8, Everything, TransformOrigin, + tokens::{fungible, fungibles, imbalance::ResolveAssetTo}, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, Everything, + TransformOrigin, }, weights::{ constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, FeePolynomial, WeightToFee as _, @@ -55,7 +62,7 @@ use frame_support::{ }; use frame_system::{ limits::{BlockLength, BlockWeights}, - EnsureRoot, EnsureSigned, + EnsureRoot, EnsureSigned, EnsureSignedBy, }; use parachains_common::{ impls::{AssetsToBlockAuthor, NonZeroIssuance}, @@ -67,7 +74,7 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, Dispatchable}, + traits::{AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, Dispatchable}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; @@ -442,7 +449,9 @@ parameter_types! { // pub type AssetsForceOrigin = // EnsureOneOf, EnsureXcm>>; -impl pallet_assets::Config for Runtime { +pub type TrustBackedAssetsInstance = pallet_assets::Instance1; + +impl pallet_assets::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type AssetId = AssetId; @@ -500,6 +509,106 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = xcm_config::XcmBenchmarkHelper; } +parameter_types! { + pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon"); + pub const LiquidityWithdrawalFee: Permill = Permill::from_percent(0); +} + +ord_parameter_types! { + pub const AssetConversionOrigin: sp_runtime::AccountId32 = + AccountIdConversion::::into_account_truncating(&AssetConversionPalletId::get()); +} + +pub type AssetsForceOrigin = EnsureRoot; + +pub type PoolAssetsInstance = pallet_assets::Instance3; +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type RemoveItemsLimit = ConstU32<1000>; + type AssetId = u32; + type AssetIdParameter = u32; + type Currency = Balances; + type CreateOrigin = + AsEnsureOriginWithArg>; + type ForceOrigin = AssetsForceOrigin; + type AssetDeposit = ConstU128<0>; + type AssetAccountDeposit = ConstU128<0>; + type MetadataDepositBase = ConstU128<0>; + type MetadataDepositPerByte = ConstU128<0>; + type ApprovalDeposit = ConstU128<0>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type Extra = (); + type WeightInfo = pallet_assets::weights::SubstrateWeight; + type CallbackHandle = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +/// Union fungibles implementation for `Assets` and `ForeignAssets`. +pub type LocalAndForeignAssets = fungibles::UnionOf< + Assets, + ForeignAssets, + LocalFromLeft< + AssetIdForTrustBackedAssetsConvert< + xcm_config::TrustBackedAssetsPalletLocation, + xcm::latest::Location, + >, + parachains_common::AssetIdForTrustBackedAssets, + xcm::latest::Location, + >, + xcm::latest::Location, + AccountId, +>; + +/// Union fungibles implementation for [`LocalAndForeignAssets`] and `Balances`. +pub type NativeAndAssets = fungible::UnionOf< + Balances, + LocalAndForeignAssets, + TargetFromLeft, + xcm::latest::Location, + AccountId, +>; + +pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< + AssetConversionPalletId, + (xcm::latest::Location, xcm::latest::Location), +>; + +impl pallet_asset_conversion::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type HigherPrecisionBalance = sp_core::U256; + type AssetKind = xcm::latest::Location; + type Assets = NativeAndAssets; + type PoolId = (Self::AssetKind, Self::AssetKind); + type PoolLocator = pallet_asset_conversion::WithFirstAsset< + xcm_config::RelayLocation, + AccountId, + Self::AssetKind, + PoolIdToAccountId, + >; + type PoolAssetId = u32; + type PoolAssets = PoolAssets; + type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam + type PoolSetupFeeAsset = xcm_config::RelayLocation; + type PoolSetupFeeTarget = ResolveAssetTo; + type LiquidityWithdrawalFee = LiquidityWithdrawalFee; + type LPFee = ConstU32<3>; + type PalletId = AssetConversionPalletId; + type MaxSwapPathLength = ConstU32<3>; + type MintMinLiquidity = ConstU128<100>; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = assets_common::benchmarks::AssetPairFactory< + xcm_config::RelayLocation, + parachain_info::Pallet, + xcm_config::TrustBackedAssetsPalletIndex, + xcm::latest::Location, + >; +} + parameter_types! { pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); @@ -642,9 +751,9 @@ impl pallet_asset_tx_payment::Config for Runtime { Balances, Runtime, ConvertInto, - pallet_assets::Instance1, + TrustBackedAssetsInstance, >, - AssetsToBlockAuthor, + AssetsToBlockAuthor, >; } @@ -685,6 +794,8 @@ construct_runtime!( // The main stage. Assets: pallet_assets:: = 50, ForeignAssets: pallet_assets:: = 51, + PoolAssets: pallet_assets:: = 52, + AssetConversion: pallet_asset_conversion = 53, Sudo: pallet_sudo = 255, } diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index d0c421bccaf8..99aadb33b840 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -24,15 +24,19 @@ //! soon. use super::{ AccountId, AllPalletsWithSystem, AssetId as AssetIdPalletAssets, Assets, Authorship, Balance, - Balances, ForeignAssets, ForeignAssetsInstance, NonZeroIssuance, ParachainInfo, - ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, - XcmpQueue, + Balances, CollatorSelection, ForeignAssets, ForeignAssetsInstance, NonZeroIssuance, + ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + WeightToFee, XcmpQueue, }; use crate::{BaseDeliveryFee, FeeAssetId, TransactionByteFee}; +use assets_common::TrustBackedAssetsAsLocation; use core::marker::PhantomData; use frame_support::{ parameter_types, - traits::{ConstU32, Contains, ContainsPair, Everything, EverythingBut, Get, Nothing}, + traits::{ + tokens::imbalance::ResolveAssetTo, ConstU32, Contains, ContainsPair, Everything, + EverythingBut, Get, Nothing, PalletInfoAccess, + }, weights::Weight, }; use frame_system::EnsureRoot; @@ -49,15 +53,15 @@ use xcm_builder::{ FungibleAdapter, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, StartsWith, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, + SignedToAccountId32, SingleAssetExchangeAdapter, SovereignSignedViaLocation, StartsWith, + TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::{traits::JustTry, XcmExecutor}; parameter_types! { pub const RelayLocation: Location = Location::parent(); - // Local native currency which is stored in `pallet_balances`` + // Local native currency which is stored in `pallet_balances` pub const PenpalNativeCurrency: Location = Location::here(); // The Penpal runtime is utilized for testing with various environment setups. // This storage item allows us to customize the `NetworkId` where Penpal is deployed. @@ -70,6 +74,10 @@ parameter_types! { Parachain(ParachainInfo::parachain_id().into()) ].into(); pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); + pub StakingPot: AccountId = CollatorSelection::account_id(); + pub TrustBackedAssetsPalletIndex: u8 = ::index() as u8; + pub TrustBackedAssetsPalletLocation: Location = + PalletInstance(TrustBackedAssetsPalletIndex::get()).into(); } /// Type for specifying how a `Location` can be converted into an `AccountId`. This is used @@ -265,6 +273,8 @@ pub const TELEPORTABLE_ASSET_ID: u32 = 2; pub const ASSETS_PALLET_ID: u8 = 50; pub const ASSET_HUB_ID: u32 = 1000; +pub const USDT_ASSET_ID: u128 = 1984; + parameter_types! { /// The location that this chain recognizes as the Relay network's Asset Hub. pub SystemAssetHubLocation: Location = Location::new(1, [Parachain(ASSET_HUB_ID)]); @@ -284,7 +294,7 @@ parameter_types! { ); pub UsdtFromAssetHub: Location = Location::new( 1, - [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(1984)] + [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ASSET_ID)], ); /// The Penpal runtime is utilized for testing with various environment setups. @@ -316,6 +326,28 @@ pub type TrustedReserves = ( pub type TrustedTeleporters = (AssetFromChain,); +/// `AssetId`/`Balance` converter for `TrustBackedAssets`. +pub type TrustBackedAssetsConvertedConcreteId = + assets_common::TrustBackedAssetsConvertedConcreteId; + +/// Asset converter for pool assets. +/// Used to convert assets in pools to the asset required for fee payment. +/// The pool must be between the first asset and the one required for fee payment. +/// This type allows paying fees with any asset in a pool with the asset required for fee payment. +pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< + crate::AssetConversion, + crate::NativeAndAssets, + ( + TrustBackedAssetsAsLocation< + TrustBackedAssetsPalletLocation, + Balance, + xcm::latest::Location, + >, + ForeignAssetsConvertedConcreteId, + ), + AccountId, +>; + pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -331,18 +363,21 @@ impl xcm_executor::Config for XcmConfig { type Weigher = FixedWeightBounds; type Trader = ( UsingComponents>, - // This trader allows to pay with `is_sufficient=true` "Foreign" assets from dedicated - // `pallet_assets` instance - `ForeignAssets`. - cumulus_primitives_utility::TakeFirstAssetTrader< + cumulus_primitives_utility::SwapFirstAssetTrader< + RelayLocation, + crate::AssetConversion, + WeightToFee, + crate::NativeAndAssets, + ( + TrustBackedAssetsAsLocation< + TrustBackedAssetsPalletLocation, + Balance, + xcm::latest::Location, + >, + ForeignAssetsConvertedConcreteId, + ), + ResolveAssetTo, AccountId, - ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, - ForeignAssetsConvertedConcreteId, - ForeignAssets, - cumulus_primitives_utility::XcmFeesTo32ByteAccount< - ForeignFungiblesTransactor, - AccountId, - XcmAssetFeesReceiver, - >, >, ); type ResponseHandler = PolkadotXcm; @@ -352,7 +387,7 @@ impl xcm_executor::Config for XcmConfig { type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); - type AssetExchanger = (); + type AssetExchanger = PoolAssetsExchanger; type FeeManager = XcmFeeManagerFromComponents< (), SendXcmFeeToAccount, diff --git a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/adapter.rs b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/adapter.rs index fa94ee5f1caa..3108068686f9 100644 --- a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/adapter.rs +++ b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/adapter.rs @@ -136,7 +136,7 @@ where give.clone() })?; - (credit_out, Some(credit_change)) + (credit_out, if credit_change.peek() > 0 { Some(credit_change) } else { None }) }; // We create an `AssetsInHolding` instance by putting in the resulting asset diff --git a/polkadot/xcm/xcm-executor/src/config.rs b/polkadot/xcm/xcm-executor/src/config.rs index 63b113bc250f..5bcbbd3466e8 100644 --- a/polkadot/xcm/xcm-executor/src/config.rs +++ b/polkadot/xcm/xcm-executor/src/config.rs @@ -33,6 +33,10 @@ pub trait Config { type RuntimeCall: Parameter + Dispatchable + GetDispatchInfo; /// How to send an onward XCM message. + /// + /// The sender is tasked with returning the assets it needs to pay for delivery fees. + /// Only one asset should be returned as delivery fees, any other will be ignored by + /// the executor. type XcmSender: SendXcm; /// How to withdraw and deposit an asset. @@ -74,6 +78,9 @@ pub trait Config { type AssetLocker: AssetLock; /// Handler for exchanging assets. + /// + /// This is used in the executor to swap the asset wanted for fees with the asset needed for + /// delivery fees. type AssetExchanger: AssetExchange; /// The handler for when there is an instruction to claim assets. diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 74561e931e7e..a8110ca3d19f 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -83,6 +83,9 @@ pub struct XcmExecutor { appendix_weight: Weight, transact_status: MaybeErrorCode, fees_mode: FeesMode, + /// Asset provided in last `BuyExecution` instruction (if any) in current XCM program. Same + /// asset type will be used for paying any potential delivery fees incurred by the program. + asset_used_for_fees: Option, _config: PhantomData, } @@ -269,7 +272,7 @@ impl ExecuteXcm for XcmExecutor XcmExecutor { appendix_weight: Weight::zero(), transact_status: Default::default(), fees_mode: FeesMode { jit_withdraw: false }, + asset_used_for_fees: None, _config: PhantomData, } } @@ -469,31 +473,112 @@ impl XcmExecutor { Ok(()) } - fn take_fee(&mut self, fee: Assets, reason: FeeReason) -> XcmResult { + fn take_fee(&mut self, fees: Assets, reason: FeeReason) -> XcmResult { if Config::FeeManager::is_waived(self.origin_ref(), reason.clone()) { return Ok(()) } tracing::trace!( target: "xcm::fees", - ?fee, + ?fees, origin_ref = ?self.origin_ref(), fees_mode = ?self.fees_mode, ?reason, "Taking fees", ); - let paid = if self.fees_mode.jit_withdraw { + // We only ever use the first asset from `fees`. + let asset_needed_for_fees = match fees.get(0) { + Some(fee) => fee, + None => return Ok(()), // No delivery fees need to be paid. + }; + // If `BuyExecution` was called, we use that asset for delivery fees as well. + let asset_to_pay_for_fees = + self.calculate_asset_for_delivery_fees(asset_needed_for_fees.clone()); + tracing::trace!(target: "xcm::fees", ?asset_to_pay_for_fees); + // We withdraw or take from holding the asset the user wants to use for fee payment. + let withdrawn_fee_asset = if self.fees_mode.jit_withdraw { let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - for asset in fee.inner() { - Config::AssetTransactor::withdraw_asset(&asset, origin, Some(&self.context))?; - } - fee + Config::AssetTransactor::withdraw_asset( + &asset_to_pay_for_fees, + origin, + Some(&self.context), + )?; + tracing::trace!(target: "xcm::fees", ?asset_needed_for_fees); + asset_to_pay_for_fees.clone().into() + } else { + let assets_taken_from_holding_to_pay_delivery_fees = self + .holding + .try_take(asset_to_pay_for_fees.clone().into()) + .map_err(|_| XcmError::NotHoldingFees)?; + tracing::trace!(target: "xcm::fees", ?assets_taken_from_holding_to_pay_delivery_fees); + let mut iter = assets_taken_from_holding_to_pay_delivery_fees.fungible_assets_iter(); + let asset = iter.next().ok_or(XcmError::NotHoldingFees)?; + asset.into() + }; + // We perform the swap, if needed, to pay fees. + let paid = if asset_to_pay_for_fees.id != asset_needed_for_fees.id { + let swapped_asset: Assets = Config::AssetExchanger::exchange_asset( + self.origin_ref(), + withdrawn_fee_asset, + &asset_needed_for_fees.clone().into(), + false, + ) + .map_err(|given_assets| { + tracing::error!( + target: "xcm::fees", + ?given_assets, + "Swap was deemed necessary but couldn't be done", + ); + XcmError::FeesNotMet + })? + .into(); + swapped_asset } else { - self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?.into() + // If the asset wanted to pay for fees is the one that was needed, + // we don't need to do any swap. + // We just use the assets withdrawn or taken from holding. + withdrawn_fee_asset.into() }; Config::FeeManager::handle_fee(paid, Some(&self.context), reason); Ok(()) } + /// Calculates the amount of `self.asset_used_for_fees` required to swap for + /// `asset_needed_for_fees`. + /// + /// The calculation is done by `Config::AssetExchanger`. + /// If `self.asset_used_for_fees` is not set, it will just return `asset_needed_for_fees`. + fn calculate_asset_for_delivery_fees(&self, asset_needed_for_fees: Asset) -> Asset { + if let Some(asset_wanted_for_fees) = &self.asset_used_for_fees { + if *asset_wanted_for_fees != asset_needed_for_fees.id { + match Config::AssetExchanger::quote_exchange_price( + &(asset_wanted_for_fees.clone(), Fungible(0)).into(), + &asset_needed_for_fees.clone().into(), + false, // Minimal. + ) { + Some(necessary_assets) => + // We only use the first asset for fees. + // If this is not enough to swap for the fee asset then it will error later down + // the line. + necessary_assets.get(0).unwrap_or(&asset_needed_for_fees.clone()).clone(), + // If we can't convert, then we return the original asset. + // It will error later in any case. + None => { + tracing::trace!( + target: "xcm::calculate_asset_for_delivery_fees", + ?asset_wanted_for_fees, + "Could not convert fees", + ); + asset_needed_for_fees.clone() + }, + } + } else { + asset_needed_for_fees + } + } else { + asset_needed_for_fees + } + } + /// Calculates what `local_querier` would be from the perspective of `destination`. fn to_querier( local_querier: Option, @@ -878,11 +963,23 @@ impl XcmExecutor { message_to_weigh.extend(xcm.0.clone().into_iter()); let (_, fee) = validate_send::(dest.clone(), Xcm(message_to_weigh))?; - // set aside fee to be charged by XcmSender - let transport_fee = self.holding.saturating_take(fee.into()); - - // now take assets to deposit (excluding transport_fee) + let maybe_delivery_fee = fee.get(0).map(|asset_needed_for_fees| { + tracing::trace!( + target: "xcm::DepositReserveAsset", + "Asset provided to pay for fees {:?}, asset required for delivery fees: {:?}", + self.asset_used_for_fees, asset_needed_for_fees, + ); + let asset_to_pay_for_fees = + self.calculate_asset_for_delivery_fees(asset_needed_for_fees.clone()); + // set aside fee to be charged by XcmSender + let delivery_fee = + self.holding.saturating_take(asset_to_pay_for_fees.into()); + tracing::trace!(target: "xcm::DepositReserveAsset", ?delivery_fee); + delivery_fee + }); + // now take assets to deposit (after having taken delivery fees) let deposited = self.holding.saturating_take(assets); + tracing::trace!(target: "xcm::DepositReserveAsset", ?deposited, "Assets except delivery fee"); self.deposit_assets_with_retry(&deposited, &dest)?; // Note that we pass `None` as `maybe_failed_bin` and drop any assets which // cannot be reanchored because we have already called `deposit_asset` on all @@ -890,8 +987,10 @@ impl XcmExecutor { let assets = Self::reanchored(deposited, &dest, None); let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - // put back transport_fee in holding register to be charged by XcmSender - self.holding.subsume_assets(transport_fee); + // put back delivery_fee in holding register to be charged by XcmSender + if let Some(delivery_fee) = maybe_delivery_fee { + self.holding.subsume_assets(delivery_fee); + } self.send(dest, Xcm(message), FeeReason::DepositReserveAsset)?; Ok(()) }); @@ -969,6 +1068,10 @@ impl XcmExecutor { // should be executed. let Some(weight) = Option::::from(weight_limit) else { return Ok(()) }; let old_holding = self.holding.clone(); + // Save the asset being used for execution fees, so we later know what should be + // used for delivery fees. + self.asset_used_for_fees = Some(fees.id.clone()); + tracing::trace!(target: "xcm::executor::BuyExecution", asset_used_for_fees = ?self.asset_used_for_fees); // pay for `weight` using up to `fees` of the holding register. let max_fee = self.holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; diff --git a/prdoc/pr_5131.prdoc b/prdoc/pr_5131.prdoc new file mode 100644 index 000000000000..db1003ab4033 --- /dev/null +++ b/prdoc/pr_5131.prdoc @@ -0,0 +1,42 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Swap for paying delivery fees in different assets + +doc: + - audience: Runtime User + description: | + If the `AssetExchanger` is configured on a runtime, the XCM executor is now able to swap assets + to pay for delivery fees. + This was already possible for execution fees via the `SwapFirstAssetTrader`. + A runtime where this will be possible is Asset Hub. + That means reserve asset transfers from Parachain A to Parachain B passing through Asset Hub no + longer need to have any DOT to pay for fees on AssetHub. + They can have any asset in a pool with DOT on Asset Hub, for example USDT or USDC. + - audience: Runtime Dev + description: | + Using the `AssetExchanger` XCM config item, the executor now swaps fees to use for delivery fees, + if possible. + If you want your runtime to support this, you need to configure this new item. + Thankfully, `xcm-builder` now has a new adapter for this, which lets you use `pallet-asset-conversion` + or any type that implements the `SwapCredit` and `QuotePrice` traits. + It's called `SingleAssetExchangeAdapter`, you can read more about it in its rust docs. + This item is already configured in Asset Hub. + + IMPORTANT: The executor now only takes the first asset for delivery fees. If you have configured a custom router + that returns more than one asset for delivery fees, then only the first one will be taken into account. + This is most likely not what you want. + +crates: + - name: staging-xcm-executor + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: staging-xcm-builder + bump: patch + - name: assets-common + bump: patch + - name: penpal-runtime + bump: minor From a13e3f915f762ce8ba4a86b0f520885e84cd37f9 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 19:19:18 +0800 Subject: [PATCH 208/382] Fix breaking test --- .../pallets/inbound-queue/src/lib.rs | 5 +- .../primitives/router/src/inbound/mod.rs | 27 ++- .../emulated/common/src/lib.rs | 10 +- .../bridge-hub-westend/src/tests/mod.rs | 1 - .../src/tests/snowbridge.rs | 16 +- .../src/bridge_to_ethereum_config.rs | 2 - .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 186 +----------------- 7 files changed, 40 insertions(+), 207 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs index 9ccb9f4b64de..676cd8f3a373 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs @@ -48,12 +48,11 @@ use frame_support::{ }; use frame_system::ensure_signed; use scale_info::TypeInfo; -use sp_core::{H160, H256}; +use sp_core::H160; use sp_runtime::traits::Zero; use sp_std::vec; use xcm::prelude::{ - send_xcm, Instruction::SetTopic, Junction::*, Location, SendError as XcmpSendError, SendXcm, - Xcm, XcmContext, XcmHash, + send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm, XcmContext, XcmHash, }; use xcm_executor::traits::TransactAsset; diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index c3383ef31e81..12f00153e20d 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -141,7 +141,10 @@ pub trait ConvertMessage { type Balance: BalanceT + From; type AccountId; /// Converts a versioned message into an XCM message and an optional topicID - fn convert(message: VersionedMessage) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError>; + fn convert( + message_id: H256, + message: VersionedMessage, + ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError>; } pub type CallIndex = [u8; 2]; @@ -179,12 +182,15 @@ where type Balance = Balance; type AccountId = AccountId; - fn convert(message: VersionedMessage) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError> { + fn convert( + message_id: H256, + message: VersionedMessage, + ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError> { use Command::*; use VersionedMessage::*; match message { V1(MessageV1 { chain_id, command: RegisterToken { token, fee } }) => - Ok(Self::convert_register_token(chain_id, token, fee)), + Ok(Self::convert_register_token(message_id, chain_id, token, fee)), V1(MessageV1 { chain_id, command: SendToken { token, destination, amount, fee } }) => Ok(Self::convert_send_token(message_id, chain_id, token, destination, amount, fee)), V1(MessageV1 { @@ -232,7 +238,12 @@ where UniversalLocation: Get, GlobalAssetHubLocation: Get, { - fn convert_register_token(chain_id: u64, token: H160, fee: u128) -> (Xcm<()>, Balance) { + fn convert_register_token( + message_id: H256, + chain_id: u64, + token: H160, + fee: u128, + ) -> (Xcm<()>, Balance) { let network = Ethereum { chain_id }; let xcm_fee: Asset = (Location::parent(), fee).into(); let deposit: Asset = (Location::parent(), CreateAssetDeposit::get()).into(); @@ -275,6 +286,8 @@ where // Clear the origin so that remaining assets in holding // are claimable by the physical origin (BridgeHub) ClearOrigin, + // Forward message id to Asset Hub + SetTopic(message_id.into()), ] .into(); @@ -282,6 +295,7 @@ where } fn convert_send_token( + message_id: H256, chain_id: u64, token: H160, destination: Destination, @@ -339,6 +353,8 @@ where BuyExecution { fees: dest_para_fee_asset, weight_limit: Unlimited }, // Deposit asset to beneficiary. DepositAsset { assets: Definite(asset.into()), beneficiary }, + // Forward message id to destination parachain. + SetTopic(message_id.into()), ] .into(), }, @@ -354,6 +370,9 @@ where }, } + // Forward message id to Asset Hub. + instructions.push(SetTopic(message_id.into())); + (instructions.into(), total_fees.into()) } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index e45d41db8f40..c6b8889730e5 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -72,11 +72,11 @@ parameter_types! { ] ); pub PenpalSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_ID).into_account_truncating(); - pub PenpalBTeleportableAssetLocation: xcm::v3::Location - = xcm::v3::Location::new(1, [ - xcm::v3::Junction::Parachain(PENPAL_B_ID), - xcm::v3::Junction::PalletInstance(ASSETS_PALLET_ID), - xcm::v3::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), + pub PenpalBTeleportableAssetLocation: xcm::v4::Location + = xcm::v4::Location::new(1, [ + xcm::v4::Junction::Parachain(PENPAL_B_ID), + xcm::v4::Junction::PalletInstance(ASSETS_PALLET_ID), + xcm::v4::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), ] ); pub PenpalBSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_B_ID).into_account_truncating(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs index 8112697a07df..756fdbfac9f6 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs @@ -18,7 +18,6 @@ use crate::imports::*; mod asset_transfers; mod claim_assets; mod send_xcm; -mod snowbridge; mod teleport; mod snowbridge; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index 4ef5d66fe536..baadb7cc7ff8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -27,9 +27,9 @@ use rococo_westend_system_emulated_network::{ }; use snowbridge_core::{outbound::OperatingMode, AssetMetadata, TokenIdOf}; use snowbridge_router_primitives::inbound::{ - Command, ConvertMessage, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, - VersionedMessage, + Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, }; +use sp_core::H256; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm_executor::traits::ConvertLocation; @@ -70,13 +70,11 @@ fn register_weth_token_from_ethereum_to_asset_hub() { BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type Converter = ::MessageConverter; - let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE }, }); - let (xcm, _) = Converter::convert(message_id, message).unwrap(); + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); assert_expected_events!( @@ -131,8 +129,6 @@ fn send_token_from_ethereum_to_asset_hub() { BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type Converter = ::MessageConverter; - let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, command: Command::SendToken { @@ -142,7 +138,7 @@ fn send_token_from_ethereum_to_asset_hub() { fee: XCM_FEE, }, }); - let (xcm, _) = Converter::convert(message).unwrap(); + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); // Check that the message was sent @@ -194,8 +190,6 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type Converter = ::MessageConverter; let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, @@ -206,7 +200,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { fee: XCM_FEE, }, }); - let (xcm, _) = Converter::convert(message).unwrap(); + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); // Check that the send token message was sent using xcm diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 8c122c461ac8..d55bb694c8ae 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -169,8 +169,6 @@ parameter_types! { impl snowbridge_pallet_ethereum_client::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ForkVersions = ChainForkVersions; - // Free consensus update every epoch. Works out to be 225 updates per day. - type FreeHeadersInterval = ConstU32<32>; type WeightInfo = crate::weights::snowbridge_pallet_ethereum_client::WeightInfo; } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index c65880771e08..17cc5c4bb6be 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -45,20 +45,17 @@ use bridge_runtime_common::extensions::{ refund_relayer_extension::RefundableParachain, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use snowbridge_beacon_primitives::{Fork, ForkVersions}; use snowbridge_core::{ - gwei, meth, outbound::{Command, Fee}, - AgentId, AllowSiblingsOnly, PricingParameters, Rewards, + AgentId, PricingParameters, }; -use snowbridge_router_primitives::inbound::MessageToXcm; use sp_api::impl_runtime_apis; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{Block as BlockT, Keccak256}, + traits::Block as BlockT, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, + ApplyExtrinsicResult, }; #[cfg(feature = "std")] @@ -79,16 +76,13 @@ use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, }; -use testnet_parachains_constants::rococo::{ - consensus::*, currency::*, fee::WeightToFee, snowbridge::INBOUND_QUEUE_PALLET_INDEX, time::*, -}; +use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use bp_runtime::HeaderId; use bridge_hub_common::{ message_queue::{NarrowOriginToSibling, ParaIdToSibling}, AggregateMessageOrigin, }; -use pallet_xcm::EnsureXcm; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; use xcm::VersionedLocation; @@ -114,8 +108,6 @@ use parachains_common::{ #[cfg(feature = "runtime-benchmarks")] use alloc::boxed::Box; -#[cfg(feature = "runtime-benchmarks")] -use benchmark_helpers::DoNothingRouter; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -519,174 +511,6 @@ impl pallet_utility::Config for Runtime { type WeightInfo = weights::pallet_utility::WeightInfo; } -// Ethereum Bridge -parameter_types! { - pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39")); -} - -parameter_types! { - pub const CreateAssetCall: [u8;2] = [53, 0]; - pub const CreateAssetDeposit: u128 = (UNITS / 10) + EXISTENTIAL_DEPOSIT; - pub Parameters: PricingParameters = PricingParameters { - exchange_rate: FixedU128::from_rational(1, 400), - fee_per_gas: gwei(20), - rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, - multiplier: FixedU128::from_rational(1, 1), - }; -} - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmark_helpers { - use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; - use codec::Encode; - use snowbridge_beacon_primitives::BeaconHeader; - use snowbridge_pallet_inbound_queue::BenchmarkHelper; - use sp_core::H256; - use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}; - - impl BenchmarkHelper for Runtime { - fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) { - EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap(); - } - } - - pub struct DoNothingRouter; - impl SendXcm for DoNothingRouter { - type Ticket = Xcm<()>; - - fn validate( - _dest: &mut Option, - xcm: &mut Option>, - ) -> SendResult { - Ok((xcm.clone().unwrap(), Assets::new())) - } - fn deliver(xcm: Xcm<()>) -> Result { - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - Ok(hash) - } - } - - impl snowbridge_pallet_system::BenchmarkHelper for () { - fn make_xcm_origin(location: Location) -> RuntimeOrigin { - RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) - } - } -} - -impl snowbridge_pallet_inbound_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Verifier = snowbridge_pallet_ethereum_client::Pallet; - type Token = Balances; - #[cfg(not(feature = "runtime-benchmarks"))] - type XcmSender = XcmRouter; - #[cfg(feature = "runtime-benchmarks")] - type XcmSender = DoNothingRouter; - type ChannelLookup = EthereumSystem; - type GatewayAddress = EthereumGatewayAddress; - #[cfg(feature = "runtime-benchmarks")] - type Helper = Runtime; - type MessageConverter = MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - ConstU8, - AccountId, - Balance, - >; - type WeightToFee = WeightToFee; - type LengthToFee = ConstantMultiplier; - type MaxMessageSize = ConstU32<2048>; - type WeightInfo = weights::snowbridge_pallet_inbound_queue::WeightInfo; - type PricingParameters = EthereumSystem; - type AssetTransactor = ::AssetTransactor; -} - -impl snowbridge_pallet_outbound_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Hashing = Keccak256; - type MessageQueue = MessageQueue; - type Decimals = ConstU8<12>; - type MaxMessagePayloadSize = ConstU32<2048>; - type MaxMessagesPerBlock = ConstU32<32>; - type GasMeter = snowbridge_core::outbound::ConstantGasMeter; - type Balance = Balance; - type WeightToFee = WeightToFee; - type WeightInfo = weights::snowbridge_pallet_outbound_queue::WeightInfo; - type PricingParameters = EthereumSystem; - type Channels = EthereumSystem; -} - -#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] -parameter_types! { - pub const ChainForkVersions: ForkVersions = ForkVersions { - genesis: Fork { - version: [0, 0, 0, 0], // 0x00000000 - epoch: 0, - }, - altair: Fork { - version: [1, 0, 0, 0], // 0x01000000 - epoch: 0, - }, - bellatrix: Fork { - version: [2, 0, 0, 0], // 0x02000000 - epoch: 0, - }, - capella: Fork { - version: [3, 0, 0, 0], // 0x03000000 - epoch: 0, - }, - deneb: Fork { - version: [4, 0, 0, 0], // 0x04000000 - epoch: 0, - } - }; -} - -#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] -parameter_types! { - pub const ChainForkVersions: ForkVersions = ForkVersions { - genesis: Fork { - version: [144, 0, 0, 111], // 0x90000069 - epoch: 0, - }, - altair: Fork { - version: [144, 0, 0, 112], // 0x90000070 - epoch: 50, - }, - bellatrix: Fork { - version: [144, 0, 0, 113], // 0x90000071 - epoch: 100, - }, - capella: Fork { - version: [144, 0, 0, 114], // 0x90000072 - epoch: 56832, - }, - deneb: Fork { - version: [144, 0, 0, 115], // 0x90000073 - epoch: 132608, - }, - }; -} - -impl snowbridge_pallet_ethereum_client::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ForkVersions = ChainForkVersions; - type WeightInfo = weights::snowbridge_pallet_ethereum_client::WeightInfo; -} - -impl snowbridge_pallet_system::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OutboundQueue = EthereumOutboundQueue; - type SiblingOrigin = EnsureXcm; - type AgentIdOf = snowbridge_core::AgentIdOf; - type TreasuryAccount = TreasuryAccount; - type Token = Balances; - type WeightInfo = weights::snowbridge_pallet_system::WeightInfo; - #[cfg(feature = "runtime-benchmarks")] - type Helper = (); - type DefaultPricingParameters = Parameters; - type InboundDeliveryCost = EthereumInboundQueue; -} - // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime From 94f1d1b6bc4073438fdcf874cbc8199ee19b8eda Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 19:32:23 +0800 Subject: [PATCH 209/382] Disable register PNA --- bridges/snowbridge/pallets/system/src/benchmarking.rs | 2 +- bridges/snowbridge/pallets/system/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/benchmarking.rs b/bridges/snowbridge/pallets/system/src/benchmarking.rs index 3a1540ebc826..4314c7528af6 100644 --- a/bridges/snowbridge/pallets/system/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/system/src/benchmarking.rs @@ -177,7 +177,7 @@ mod benchmarks { }; #[extrinsic_call] - _(RawOrigin::Signed(caller), asset, asset_metadata); + _(RawOrigin::Root, asset, asset_metadata); Ok(()) } diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 4ed14d9c0ca7..8631a45d4f64 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -604,12 +604,12 @@ pub mod pallet { location: Box, metadata: AssetMetadata, ) -> DispatchResult { - let who = ensure_signed(origin)?; + ensure_root(origin)?; let asset_loc: Location = (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; - let pays_fee = PaysFee::::Yes(who); + let pays_fee = PaysFee::::No; Self::do_register_token(asset_loc, metadata, pays_fee)?; From da6541030f72da5a8c5a60c2ab2dd2c2c136b471 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 2 Sep 2024 16:57:07 +0300 Subject: [PATCH 210/382] Improve `sc-service` API (#5364) This improves `sc-service` API by not requiring the whole `&Configuration`, using specific configuration options instead. `RpcConfiguration` was also extracted from `Configuration` to group all RPC options together. We don't use Substrate's CLI and would rather not use `Configuration` either, but some key public functions require it even though they ignored most of the fields anyway. `RpcConfiguration` is very helpful not just for consolidation of the fields, but also to finally make RPC optional for our use case, while Substrate still runs RPC server on localhost even if listening address is explicitly set to `None`, which is annoying (and I suspect there is a reason for it, so didn't want to change the default just yet). While this is a breaking change, most developers will not notice it if they use higher-level APIs. Fixes https://github.com/paritytech/polkadot-sdk/issues/2897 --------- Co-authored-by: Niklas Adolfsson --- .../relay-chain-minimal-node/src/lib.rs | 2 +- .../relay-chain-minimal-node/src/network.rs | 2 +- .../polkadot-parachain-lib/src/cli.rs | 3 +- .../polkadot-parachain-lib/src/common/spec.rs | 13 ++- cumulus/test/service/src/cli.rs | 3 +- cumulus/test/service/src/lib.rs | 77 +++++++------ polkadot/node/service/src/lib.rs | 9 +- polkadot/node/test/service/src/lib.rs | 41 +++---- prdoc/pr_5364.prdoc | 39 +++++++ .../bin/node/cli/benches/block_production.rs | 41 +++---- .../bin/node/cli/benches/transaction_pool.rs | 36 +++--- substrate/bin/node/cli/src/service.rs | 6 +- substrate/bin/node/inspect/src/command.rs | 2 +- substrate/client/cli/src/commands/run_cmd.rs | 12 +- substrate/client/cli/src/config.rs | 68 +++++------- substrate/client/cli/src/lib.rs | 6 +- .../client/cli/src/params/node_key_params.rs | 1 - substrate/client/cli/src/runner.rs | 40 +++---- substrate/client/network/common/src/role.rs | 2 +- substrate/client/service/src/builder.rs | 97 ++++++++++------ substrate/client/service/src/config.rs | 105 +++++++++++------- substrate/client/service/src/lib.rs | 64 ++++++----- substrate/client/service/src/metrics.rs | 31 +++--- substrate/client/service/test/src/lib.rs | 40 +++---- templates/minimal/node/src/service.rs | 2 +- templates/parachain/node/src/command.rs | 10 +- templates/parachain/node/src/service.rs | 7 +- templates/solochain/node/src/service.rs | 4 +- 28 files changed, 436 insertions(+), 327 deletions(-) create mode 100644 prdoc/pr_5364.prdoc diff --git a/cumulus/client/relay-chain-minimal-node/src/lib.rs b/cumulus/client/relay-chain-minimal-node/src/lib.rs index 732a242e7295..e65a78f16d79 100644 --- a/cumulus/client/relay-chain-minimal-node/src/lib.rs +++ b/cumulus/client/relay-chain-minimal-node/src/lib.rs @@ -175,7 +175,7 @@ async fn new_minimal_relay_chain, ) -> Result { - let role = config.role.clone(); + let role = config.role; let mut net_config = sc_network::config::FullNetworkConfiguration::<_, _, Network>::new( &config.network, config.prometheus_config.as_ref().map(|cfg| cfg.registry.clone()), diff --git a/cumulus/client/relay-chain-minimal-node/src/network.rs b/cumulus/client/relay-chain-minimal-node/src/network.rs index 025ac7a81a21..afe83a2a12f9 100644 --- a/cumulus/client/relay-chain-minimal-node/src/network.rs +++ b/cumulus/client/relay-chain-minimal-node/src/network.rs @@ -65,7 +65,7 @@ pub(crate) fn build_collator_network>( spawn_handle.spawn("peer-store", Some("networking"), peer_store.run()); let network_params = sc_network::config::Params:: { - role: config.role.clone(), + role: config.role, executor: { let spawn_handle = Clone::clone(&spawn_handle); Box::new(move |fut| { diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs index 15d21235d1a1..349dc01d8a4f 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs @@ -322,10 +322,9 @@ impl CliConfiguration for RelayChainCli { _support_url: &String, _impl_version: &String, _logger_hook: F, - _config: &sc_service::Configuration, ) -> sc_cli::Result<()> where - F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + F: FnOnce(&mut sc_cli::LoggerBuilder), { unreachable!("PolkadotCli is never initialized; qed"); } diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs index 55e042aed87e..8e19cf304b07 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs @@ -127,14 +127,15 @@ pub(crate) trait NodeSpec { }) .transpose()?; - let heap_pages = config.default_heap_pages.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| { - HeapAllocStrategy::Static { extra_pages: h as _ } - }); + let heap_pages = + config.executor.default_heap_pages.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| { + HeapAllocStrategy::Static { extra_pages: h as _ } + }); let executor = sc_executor::WasmExecutor::::builder() - .with_execution_method(config.wasm_method) - .with_max_runtime_instances(config.max_runtime_instances) - .with_runtime_cache_size(config.runtime_cache_size) + .with_execution_method(config.executor.wasm_method) + .with_max_runtime_instances(config.executor.max_runtime_instances) + .with_runtime_cache_size(config.executor.runtime_cache_size) .with_onchain_heap_alloc_strategy(heap_pages) .with_offchain_heap_alloc_strategy(heap_pages) .build(); diff --git a/cumulus/test/service/src/cli.rs b/cumulus/test/service/src/cli.rs index 739c2d4bda16..220b0449f339 100644 --- a/cumulus/test/service/src/cli.rs +++ b/cumulus/test/service/src/cli.rs @@ -139,10 +139,9 @@ impl CliConfiguration for RelayChainCli { _support_url: &String, _impl_version: &String, _logger_hook: F, - _config: &sc_service::Configuration, ) -> CliResult<()> where - F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + F: FnOnce(&mut sc_cli::LoggerBuilder), { unreachable!("PolkadotCli is never initialized; qed"); } diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index bc0fe9090d38..a600dcce3d66 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -78,8 +78,9 @@ use sc_network::{ }; use sc_service::{ config::{ - BlocksPruning, DatabaseSource, KeystoreConfig, MultiaddrWithPeerId, NetworkConfiguration, - OffchainWorkerConfig, PruningMode, RpcBatchRequestConfig, RpcEndpoint, WasmExecutionMethod, + BlocksPruning, DatabaseSource, ExecutorConfiguration, KeystoreConfig, MultiaddrWithPeerId, + NetworkConfiguration, OffchainWorkerConfig, PruningMode, RpcBatchRequestConfig, + RpcConfiguration, RpcEndpoint, WasmExecutionMethod, }, BasePath, ChainSpec as ChainSpecService, Configuration, Error as ServiceError, PartialComponents, Role, RpcHandlers, TFullBackend, TFullClient, TaskManager, @@ -194,15 +195,16 @@ pub fn new_partial( enable_import_proof_record: bool, ) -> Result { let heap_pages = config + .executor .default_heap_pages .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ }); let executor = WasmExecutor::builder() - .with_execution_method(config.wasm_method) + .with_execution_method(config.executor.wasm_method) .with_onchain_heap_alloc_strategy(heap_pages) .with_offchain_heap_alloc_strategy(heap_pages) - .with_max_runtime_instances(config.max_runtime_instances) - .with_runtime_cache_size(config.runtime_cache_size) + .with_max_runtime_instances(config.executor.max_runtime_instances) + .with_runtime_cache_size(config.executor.runtime_cache_size) .build(); let (client, backend, keystore_container, task_manager) = @@ -863,38 +865,41 @@ pub fn node_config( state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, chain_spec: spec, - wasm_method: WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::PoolingCopyOnWrite, + executor: ExecutorConfiguration { + wasm_method: WasmExecutionMethod::Compiled { + instantiation_strategy: + sc_executor_wasmtime::InstantiationStrategy::PoolingCopyOnWrite, + }, + ..ExecutorConfiguration::default() + }, + rpc: RpcConfiguration { + addr: None, + max_connections: Default::default(), + cors: None, + methods: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + id_provider: None, + max_subs_per_conn: Default::default(), + port: 9945, + message_buffer_capacity: Default::default(), + batch_config: RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), }, - rpc_addr: None, - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_id_provider: None, - rpc_max_subs_per_conn: Default::default(), - rpc_port: 9945, - rpc_message_buffer_capacity: Default::default(), - rpc_batch_config: RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: OffchainWorkerConfig { enabled: true, indexing_enabled: false }, force_authoring: false, disable_grandpa: false, dev_key_seed: Some(key_seed), tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, announce_block: true, data_path: root, base_path, wasm_runtime_overrides: None, - runtime_cache_size: 2, }) } @@ -1006,19 +1011,19 @@ pub fn run_relay_chain_validator_node( ); if let Some(port) = port { - config.rpc_addr = Some(vec![RpcEndpoint { - batch_config: config.rpc_batch_config, - cors: config.rpc_cors.clone(), + config.rpc.addr = Some(vec![RpcEndpoint { + batch_config: config.rpc.batch_config, + cors: config.rpc.cors.clone(), listen_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, port)), - max_connections: config.rpc_max_connections, - max_payload_in_mb: config.rpc_max_request_size, - max_payload_out_mb: config.rpc_max_response_size, - max_subscriptions_per_connection: config.rpc_max_subs_per_conn, - max_buffer_capacity_per_connection: config.rpc_message_buffer_capacity, - rpc_methods: config.rpc_methods, - rate_limit: config.rpc_rate_limit, - rate_limit_trust_proxy_headers: config.rpc_rate_limit_trust_proxy_headers, - rate_limit_whitelisted_ips: config.rpc_rate_limit_whitelisted_ips.clone(), + max_connections: config.rpc.max_connections, + max_payload_in_mb: config.rpc.max_request_size, + max_payload_out_mb: config.rpc.max_response_size, + max_subscriptions_per_connection: config.rpc.max_subs_per_conn, + max_buffer_capacity_per_connection: config.rpc.message_buffer_capacity, + rpc_methods: config.rpc.methods, + rate_limit: config.rpc.rate_limit, + rate_limit_trust_proxy_headers: config.rpc.rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: config.rpc.rate_limit_whitelisted_ips.clone(), retry_random_port: true, is_optional: false, }]); diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index a907d310c105..ab91c6b9b92b 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -437,15 +437,16 @@ fn new_partial_basics( .transpose()?; let heap_pages = config + .executor .default_heap_pages .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ }); let executor = WasmExecutor::builder() - .with_execution_method(config.wasm_method) + .with_execution_method(config.executor.wasm_method) .with_onchain_heap_alloc_strategy(heap_pages) .with_offchain_heap_alloc_strategy(heap_pages) - .with_max_runtime_instances(config.max_runtime_instances) - .with_runtime_cache_size(config.runtime_cache_size) + .with_max_runtime_instances(config.executor.max_runtime_instances) + .with_runtime_cache_size(config.executor.runtime_cache_size) .build(); let (client, backend, keystore_container, task_manager) = @@ -764,7 +765,7 @@ pub fn new_full< use sc_network_sync::WarpSyncConfig; let is_offchain_indexing_enabled = config.offchain_worker.indexing_enabled; - let role = config.role.clone(); + let role = config.role; let force_authoring = config.force_authoring; let backoff_authoring_blocks = if !force_authoring_backoff && (config.chain_spec.is_polkadot() || config.chain_spec.is_kusama()) diff --git a/polkadot/node/test/service/src/lib.rs b/polkadot/node/test/service/src/lib.rs index a4e58253bb17..b12387884861 100644 --- a/polkadot/node/test/service/src/lib.rs +++ b/polkadot/node/test/service/src/lib.rs @@ -68,6 +68,7 @@ use substrate_test_client::{ pub type Client = FullClient; pub use polkadot_service::{FullBackend, GetLastTimestamp}; +use sc_service::config::{ExecutorConfiguration, RpcConfiguration}; /// Create a new full node. #[sc_tracing::logging::prefix_logs_with(config.network.node_name.as_str())] @@ -200,35 +201,37 @@ pub fn node_config( state_pruning: Default::default(), blocks_pruning: BlocksPruning::KeepFinalized, chain_spec: Box::new(spec), - wasm_method: WasmExecutionMethod::Compiled { - instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + executor: ExecutorConfiguration { + wasm_method: WasmExecutionMethod::Compiled { + instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + }, + ..ExecutorConfiguration::default() }, wasm_runtime_overrides: Default::default(), - rpc_addr: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_id_provider: None, - rpc_max_subs_per_conn: Default::default(), - rpc_port: 9944, - rpc_message_buffer_capacity: Default::default(), - rpc_batch_config: RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), + rpc: RpcConfiguration { + addr: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + max_connections: Default::default(), + cors: None, + methods: Default::default(), + id_provider: None, + max_subs_per_conn: Default::default(), + port: 9944, + message_buffer_capacity: Default::default(), + batch_config: RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), + }, prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: Default::default(), force_authoring: false, disable_grandpa: false, dev_key_seed: Some(key_seed), tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, - runtime_cache_size: 2, announce_block: true, data_path: root, base_path, diff --git a/prdoc/pr_5364.prdoc b/prdoc/pr_5364.prdoc new file mode 100644 index 000000000000..35a72f65fb1e --- /dev/null +++ b/prdoc/pr_5364.prdoc @@ -0,0 +1,39 @@ +title: Improve `sc-service` API + +doc: + - audience: Node Dev + description: | + This improves `sc-service` API by not requiring the whole `&Configuration`, using specific configuration options + instead. `RpcConfiguration` and `ExecutorConfiguration` were also extracted from `Configuration` to group all RPC + and executor options together. + If `sc-service` is used as a library with lower-level APIs, `Configuration` can now be avoided in most cases. + + This mainly impacts you on your node implementation. There you need to change this: + ``` + with_execution_method(config.wasm_method) + ``` + + to this: + ``` + with_execution_method(config.executor.wasm_method) + ``` + + There are similar changes required as well, but all are around the initialization of the wasm executor. + +crates: + - name: sc-service + bump: major + - name: sc-network-common + bump: patch + - name: sc-cli + bump: major + - name: polkadot-service + bump: patch + - name: cumulus-relay-chain-minimal-node + bump: none + - name: polkadot-parachain-bin + bump: major + - name: polkadot-parachain-lib + bump: major + - name: staging-node-inspect + bump: major diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs index 9ac9d8b4f67d..de883d1051f5 100644 --- a/substrate/bin/node/cli/benches/block_production.rs +++ b/substrate/bin/node/cli/benches/block_production.rs @@ -22,6 +22,7 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughpu use kitchensink_runtime::{constants::currency::*, BalancesCall}; use node_cli::service::{create_extrinsic, FullClient}; +use polkadot_sdk::sc_service::config::{ExecutorConfiguration, RpcConfiguration}; use sc_block_builder::{BlockBuilderBuilder, BuiltBlock}; use sc_consensus::{ block_import::{BlockImportParams, ForkChoiceStrategy}, @@ -73,34 +74,36 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, chain_spec: spec, - wasm_method: WasmExecutionMethod::Compiled { - instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + executor: ExecutorConfiguration { + wasm_method: WasmExecutionMethod::Compiled { + instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + }, + ..ExecutorConfiguration::default() + }, + rpc: RpcConfiguration { + addr: None, + max_connections: Default::default(), + cors: None, + methods: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + id_provider: Default::default(), + max_subs_per_conn: Default::default(), + port: 9944, + message_buffer_capacity: Default::default(), + batch_config: RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), }, - rpc_addr: None, - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_id_provider: Default::default(), - rpc_max_subs_per_conn: Default::default(), - rpc_port: 9944, - rpc_message_buffer_capacity: Default::default(), - rpc_batch_config: RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: OffchainWorkerConfig { enabled: true, indexing_enabled: false }, force_authoring: false, disable_grandpa: false, dev_key_seed: Some(Sr25519Keyring::Alice.to_seed()), tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, - runtime_cache_size: 2, announce_block: true, data_path: base_path.path().into(), base_path, diff --git a/substrate/bin/node/cli/benches/transaction_pool.rs b/substrate/bin/node/cli/benches/transaction_pool.rs index 9a71a4ec585d..efec081427f4 100644 --- a/substrate/bin/node/cli/benches/transaction_pool.rs +++ b/substrate/bin/node/cli/benches/transaction_pool.rs @@ -24,6 +24,7 @@ use futures::{future, StreamExt}; use kitchensink_runtime::{constants::currency::*, BalancesCall, SudoCall}; use node_cli::service::{create_extrinsic, fetch_nonce, FullClient, TransactionPool}; use node_primitives::AccountId; +use polkadot_sdk::sc_service::config::{ExecutorConfiguration, RpcConfiguration}; use sc_service::{ config::{ BlocksPruning, DatabaseSource, KeystoreConfig, NetworkConfiguration, OffchainWorkerConfig, @@ -70,32 +71,31 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, chain_spec: spec, - wasm_method: Default::default(), - rpc_addr: None, - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_id_provider: Default::default(), - rpc_max_subs_per_conn: Default::default(), - rpc_port: 9944, - rpc_message_buffer_capacity: Default::default(), - rpc_batch_config: RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), + executor: ExecutorConfiguration::default(), + rpc: RpcConfiguration { + addr: None, + max_connections: Default::default(), + cors: None, + methods: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + id_provider: Default::default(), + max_subs_per_conn: Default::default(), + port: 9944, + message_buffer_capacity: Default::default(), + batch_config: RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), + }, prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: OffchainWorkerConfig { enabled: true, indexing_enabled: false }, force_authoring: false, disable_grandpa: false, dev_key_seed: Some(Sr25519Keyring::Alice.to_seed()), tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, - runtime_cache_size: 2, announce_block: true, data_path: base_path.path().into(), base_path, diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 8fdcc7261b55..4f63473f632d 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -208,7 +208,7 @@ pub fn new_partial( }) .transpose()?; - let executor = sc_service::new_wasm_executor(&config); + let executor = sc_service::new_wasm_executor(&config.executor); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( @@ -404,7 +404,7 @@ pub fn new_full_base::Hash>>( ), ) -> Result { let is_offchain_indexing_enabled = config.offchain_worker.indexing_enabled; - let role = config.role.clone(); + let role = config.role; let force_authoring = config.force_authoring; let backoff_authoring_blocks = Some(sc_consensus_slots::BackoffAuthoringOnFinalizedHeadLagging::default()); @@ -717,7 +717,7 @@ pub fn new_full_base::Hash>>( name: Some(name), observer_enabled: false, keystore, - local_role: role.clone(), + local_role: role, telemetry: telemetry.as_ref().map(|x| x.handle()), protocol_name: grandpa_protocol_name, }; diff --git a/substrate/bin/node/inspect/src/command.rs b/substrate/bin/node/inspect/src/command.rs index e0e25707e31b..b9e5e55be8ef 100644 --- a/substrate/bin/node/inspect/src/command.rs +++ b/substrate/bin/node/inspect/src/command.rs @@ -36,7 +36,7 @@ impl InspectCmd { B: Block, RA: Send + Sync + 'static, { - let executor = sc_service::new_wasm_executor::(&config); + let executor = sc_service::new_wasm_executor::(&config.executor); let client = sc_service::new_full_client::(&config, None, executor)?; let inspect = Inspector::::new(client); diff --git a/substrate/client/cli/src/commands/run_cmd.rs b/substrate/client/cli/src/commands/run_cmd.rs index 7245b46e2f7d..f47baf2644e7 100644 --- a/substrate/client/cli/src/commands/run_cmd.rs +++ b/substrate/client/cli/src/commands/run_cmd.rs @@ -345,7 +345,17 @@ impl CliConfiguration for RunCmd { } fn network_params(&self) -> Option<&NetworkParams> { - Some(&self.network_params) + let network_params = &self.network_params; + let is_authority = self.role(self.is_dev().ok()?).ok()?.is_authority(); + if is_authority && network_params.public_addr.is_empty() { + eprintln!( + "WARNING: No public address specified, validator node may not be reachable. + Consider setting `--public-addr` to the public IP address of this node. + This will become a hard requirement in future versions." + ); + } + + Some(network_params) } fn keystore_params(&self) -> Option<&KeystoreParams> { diff --git a/substrate/client/cli/src/config.rs b/substrate/client/cli/src/config.rs index 7c2358477611..59238b3307cf 100644 --- a/substrate/client/cli/src/config.rs +++ b/substrate/client/cli/src/config.rs @@ -27,10 +27,10 @@ use log::warn; use names::{Generator, Name}; use sc_service::{ config::{ - BasePath, Configuration, DatabaseSource, IpNetwork, KeystoreConfig, NetworkConfiguration, - NodeKeyConfig, OffchainWorkerConfig, PrometheusConfig, PruningMode, Role, - RpcBatchRequestConfig, RpcMethods, TelemetryEndpoints, TransactionPoolOptions, - WasmExecutionMethod, + BasePath, Configuration, DatabaseSource, ExecutorConfiguration, IpNetwork, KeystoreConfig, + NetworkConfiguration, NodeKeyConfig, OffchainWorkerConfig, PrometheusConfig, PruningMode, + Role, RpcBatchRequestConfig, RpcConfiguration, RpcMethods, TelemetryEndpoints, + TransactionPoolOptions, WasmExecutionMethod, }, BlocksPruning, ChainSpec, TracingReceiver, }; @@ -530,26 +530,32 @@ pub trait CliConfiguration: Sized { trie_cache_maximum_size: self.trie_cache_maximum_size()?, state_pruning: self.state_pruning()?, blocks_pruning: self.blocks_pruning()?, - wasm_method: self.wasm_method()?, + executor: ExecutorConfiguration { + wasm_method: self.wasm_method()?, + default_heap_pages: self.default_heap_pages()?, + max_runtime_instances, + runtime_cache_size, + }, wasm_runtime_overrides: self.wasm_runtime_overrides(), - rpc_addr: rpc_addrs, - rpc_methods: self.rpc_methods()?, - rpc_max_connections: self.rpc_max_connections()?, - rpc_cors: self.rpc_cors(is_dev)?, - rpc_max_request_size: self.rpc_max_request_size()?, - rpc_max_response_size: self.rpc_max_response_size()?, - rpc_id_provider: None, - rpc_max_subs_per_conn: self.rpc_max_subscriptions_per_connection()?, - rpc_port: DCV::rpc_listen_port(), - rpc_message_buffer_capacity: self.rpc_buffer_capacity_per_connection()?, - rpc_batch_config: self.rpc_batch_config()?, - rpc_rate_limit: self.rpc_rate_limit()?, - rpc_rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips()?, - rpc_rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers()?, + rpc: RpcConfiguration { + addr: rpc_addrs, + methods: self.rpc_methods()?, + max_connections: self.rpc_max_connections()?, + cors: self.rpc_cors(is_dev)?, + max_request_size: self.rpc_max_request_size()?, + max_response_size: self.rpc_max_response_size()?, + id_provider: None, + max_subs_per_conn: self.rpc_max_subscriptions_per_connection()?, + port: DCV::rpc_listen_port(), + message_buffer_capacity: self.rpc_buffer_capacity_per_connection()?, + batch_config: self.rpc_batch_config()?, + rate_limit: self.rpc_rate_limit()?, + rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips()?, + rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers()?, + }, prometheus_config: self .prometheus_config(DCV::prometheus_listen_port(), &chain_spec)?, telemetry_endpoints, - default_heap_pages: self.default_heap_pages()?, offchain_worker: self.offchain_worker(&role)?, force_authoring: self.force_authoring()?, disable_grandpa: self.disable_grandpa()?, @@ -557,11 +563,9 @@ pub trait CliConfiguration: Sized { tracing_targets: self.tracing_targets()?, tracing_receiver: self.tracing_receiver()?, chain_spec, - max_runtime_instances, announce_block: self.announce_block()?, role, base_path, - runtime_cache_size, }) } @@ -618,15 +622,9 @@ pub trait CliConfiguration: Sized { /// } /// } /// ``` - fn init( - &self, - support_url: &String, - impl_version: &String, - logger_hook: F, - config: &Configuration, - ) -> Result<()> + fn init(&self, support_url: &String, impl_version: &String, logger_hook: F) -> Result<()> where - F: FnOnce(&mut LoggerBuilder, &Configuration), + F: FnOnce(&mut LoggerBuilder), { sp_panic_handler::set(support_url, impl_version); @@ -645,18 +643,10 @@ pub trait CliConfiguration: Sized { } // Call hook for custom profiling setup. - logger_hook(&mut logger, config); + logger_hook(&mut logger); logger.init()?; - if config.role.is_authority() && config.network.public_addresses.is_empty() { - warn!( - "WARNING: No public address specified, validator node may not be reachable. - Consider setting `--public-addr` to the public IP address of this node. - This will become a hard requirement in future versions." - ); - } - match fdlimit::raise_fd_limit() { Ok(fdlimit::Outcome::LimitRaised { to, .. }) => if to < RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT { diff --git a/substrate/client/cli/src/lib.rs b/substrate/client/cli/src/lib.rs index 1bb9fec0e276..4db70f99c803 100644 --- a/substrate/client/cli/src/lib.rs +++ b/substrate/client/cli/src/lib.rs @@ -25,6 +25,7 @@ #![warn(unused_imports)] use clap::{CommandFactory, FromArgMatches, Parser}; +use log::warn; use sc_service::Configuration; pub mod arg_enums; @@ -242,7 +243,10 @@ pub trait SubstrateCli: Sized { let config = command.create_configuration(self, tokio_runtime.handle().clone())?; - command.init(&Self::support_url(), &Self::impl_version(), logger_hook, &config)?; + command.init(&Self::support_url(), &Self::impl_version(), |logger_builder| { + logger_hook(logger_builder, &config) + })?; + Runner::new(config, tokio_runtime, signals) } } diff --git a/substrate/client/cli/src/params/node_key_params.rs b/substrate/client/cli/src/params/node_key_params.rs index 0e12c7a2a2d3..cdd637888114 100644 --- a/substrate/client/cli/src/params/node_key_params.rs +++ b/substrate/client/cli/src/params/node_key_params.rs @@ -237,7 +237,6 @@ mod tests { |params| { let dir = PathBuf::from(net_config_dir.clone()); let typ = params.node_key_type; - let role = role.clone(); params.node_key(net_config_dir, role, is_dev).and_then(move |c| match c { NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f)) if typ == NodeKeyType::Ed25519 && diff --git a/substrate/client/cli/src/runner.rs b/substrate/client/cli/src/runner.rs index b0dbccfa634c..9c5834d8d80a 100644 --- a/substrate/client/cli/src/runner.rs +++ b/substrate/client/cli/src/runner.rs @@ -193,7 +193,10 @@ pub fn print_node_infos(config: &Configuration) { mod tests { use super::*; use sc_network::config::NetworkConfiguration; - use sc_service::{Arc, ChainType, GenericChainSpec, NoExtension}; + use sc_service::{ + config::{ExecutorConfiguration, RpcConfiguration}, + Arc, ChainType, GenericChainSpec, NoExtension, + }; use std::{ path::PathBuf, sync::atomic::{AtomicU64, Ordering}, @@ -262,36 +265,35 @@ mod tests { .with_genesis_config_patch(Default::default()) .build(), ), - wasm_method: Default::default(), + executor: ExecutorConfiguration::default(), wasm_runtime_overrides: None, - rpc_addr: None, - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_id_provider: Default::default(), - rpc_max_subs_per_conn: Default::default(), - rpc_message_buffer_capacity: Default::default(), - rpc_port: 9944, - rpc_batch_config: sc_service::config::RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), + rpc: RpcConfiguration { + addr: None, + max_connections: Default::default(), + cors: None, + methods: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + id_provider: Default::default(), + max_subs_per_conn: Default::default(), + message_buffer_capacity: Default::default(), + port: 9944, + batch_config: sc_service::config::RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), + }, prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: Default::default(), force_authoring: false, disable_grandpa: false, dev_key_seed: None, tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, announce_block: true, base_path: sc_service::BasePath::new(root.clone()), data_path: root, - runtime_cache_size: 2, }, runtime, Signals::dummy(), diff --git a/substrate/client/network/common/src/role.rs b/substrate/client/network/common/src/role.rs index 11b7a7924c46..e2218b37f088 100644 --- a/substrate/client/network/common/src/role.rs +++ b/substrate/client/network/common/src/role.rs @@ -58,7 +58,7 @@ impl From for ObservedRole { } /// Role of the local node. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub enum Role { /// Regular full node. Full, diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index 9a6c3fde37d9..0dc28d1361cb 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -19,7 +19,7 @@ use crate::{ build_network_future, build_system_rpc_future, client::{Client, ClientConfig}, - config::{Configuration, KeystoreConfig, PrometheusConfig}, + config::{Configuration, ExecutorConfiguration, KeystoreConfig, PrometheusConfig}, error::Error, metrics::MetricsService, start_rpc_servers, BuildGenesisBlock, GenesisBlockBuilder, RpcHandlers, SpawnTaskHandle, @@ -29,12 +29,12 @@ use futures::{channel::oneshot, future::ready, FutureExt, StreamExt}; use jsonrpsee::RpcModule; use log::info; use prometheus_endpoint::Registry; -use sc_chain_spec::get_extension; +use sc_chain_spec::{get_extension, ChainSpec}; use sc_client_api::{ execution_extensions::ExecutionExtensions, proof_provider::ProofProvider, BadBlocks, BlockBackend, BlockchainEvents, ExecutorProvider, ForkBlocks, StorageProvider, UsageProvider, }; -use sc_client_db::{Backend, DatabaseSettings}; +use sc_client_db::{Backend, BlocksPruning, DatabaseSettings, PruningMode}; use sc_consensus::import_queue::ImportQueue; use sc_executor::{ sp_wasm_interface::HostFunctions, HeapAllocStrategy, NativeExecutionDispatch, RuntimeVersionOf, @@ -212,10 +212,7 @@ where .unwrap_or_default(); let client = { - let extensions = sc_client_api::execution_extensions::ExecutionExtensions::new( - None, - Arc::new(executor.clone()), - ); + let extensions = ExecutionExtensions::new(None, Arc::new(executor.clone())); let wasm_runtime_substitutes = config .chain_spec @@ -268,11 +265,11 @@ pub fn new_native_or_wasm_executor( config: &Configuration, ) -> sc_executor::NativeElseWasmExecutor { #[allow(deprecated)] - sc_executor::NativeElseWasmExecutor::new_with_wasm_executor(new_wasm_executor(config)) + sc_executor::NativeElseWasmExecutor::new_with_wasm_executor(new_wasm_executor(&config.executor)) } -/// Creates a [`WasmExecutor`] according to [`Configuration`]. -pub fn new_wasm_executor(config: &Configuration) -> WasmExecutor { +/// Creates a [`WasmExecutor`] according to [`ExecutorConfiguration`]. +pub fn new_wasm_executor(config: &ExecutorConfiguration) -> WasmExecutor { let strategy = config .default_heap_pages .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |p| HeapAllocStrategy::Static { extra_pages: p as _ }); @@ -434,7 +431,17 @@ where let telemetry = telemetry .map(|telemetry| { - init_telemetry(&mut config, network.clone(), client.clone(), telemetry, Some(sysinfo)) + init_telemetry( + config.network.node_name.clone(), + config.impl_name.clone(), + config.impl_version.clone(), + config.chain_spec.name().to_string(), + config.role.is_authority(), + network.clone(), + client.clone(), + telemetry, + Some(sysinfo), + ) }) .transpose()?; @@ -463,7 +470,13 @@ where let metrics_service = if let Some(PrometheusConfig { port, registry }) = config.prometheus_config.clone() { // Set static metrics. - let metrics = MetricsService::with_prometheus(telemetry, ®istry, &config)?; + let metrics = MetricsService::with_prometheus( + telemetry, + ®istry, + config.role, + &config.network.node_name, + &config.impl_version, + )?; spawn_handle.spawn( "prometheus-endpoint", None, @@ -487,7 +500,7 @@ where ), ); - let rpc_id_provider = config.rpc_id_provider.take(); + let rpc_id_provider = config.rpc.id_provider.take(); // jsonrpsee RPC let gen_rpc_module = || { @@ -497,13 +510,23 @@ where transaction_pool.clone(), keystore.clone(), system_rpc_tx.clone(), - &config, + config.impl_name.clone(), + config.impl_version.clone(), + config.chain_spec.as_ref(), + &config.state_pruning, + config.blocks_pruning, backend.clone(), &*rpc_builder, ) }; - let rpc_server_handle = start_rpc_servers(&config, gen_rpc_module, rpc_id_provider)?; + let rpc_server_handle = start_rpc_servers( + &config.rpc, + config.prometheus_registry(), + &config.tokio_handle, + gen_rpc_module, + rpc_id_provider, + )?; let in_memory_rpc = { let mut module = gen_rpc_module()?; module.extensions_mut().insert(DenyUnsafe::No); @@ -555,7 +578,11 @@ pub async fn propagate_transaction_notifications( /// Initialize telemetry with provided configuration and return telemetry handle pub fn init_telemetry( - config: &mut Configuration, + name: String, + implementation: String, + version: String, + chain: String, + authority: bool, network: Network, client: Arc, telemetry: &mut Telemetry, @@ -568,16 +595,16 @@ where { let genesis_hash = client.block_hash(Zero::zero()).ok().flatten().unwrap_or_default(); let connection_message = ConnectionMessage { - name: config.network.node_name.to_owned(), - implementation: config.impl_name.to_owned(), - version: config.impl_version.to_owned(), + name, + implementation, + version, target_os: sc_sysinfo::TARGET_OS.into(), target_arch: sc_sysinfo::TARGET_ARCH.into(), target_env: sc_sysinfo::TARGET_ENV.into(), config: String::new(), - chain: config.chain_spec.name().to_owned(), + chain, genesis_hash: format!("{:?}", genesis_hash), - authority: config.role.is_authority(), + authority, startup_time: SystemTime::UNIX_EPOCH .elapsed() .map(|dur| dur.as_millis()) @@ -599,7 +626,11 @@ pub fn gen_rpc_module( transaction_pool: Arc, keystore: KeystorePtr, system_rpc_tx: TracingUnboundedSender>, - config: &Configuration, + impl_name: String, + impl_version: String, + chain_spec: &dyn ChainSpec, + state_pruning: &Option, + blocks_pruning: BlocksPruning, backend: Arc, rpc_builder: &(dyn Fn(SubscriptionTaskExecutor) -> Result, Error>), ) -> Result, Error> @@ -624,11 +655,11 @@ where TBl::Header: Unpin, { let system_info = sc_rpc::system::SystemInfo { - chain_name: config.chain_spec.name().into(), - impl_name: config.impl_name.clone(), - impl_version: config.impl_version.clone(), - properties: config.chain_spec.properties(), - chain_type: config.chain_spec.chain_type(), + chain_name: chain_spec.name().into(), + impl_name, + impl_version, + properties: chain_spec.properties(), + chain_type: chain_spec.chain_type(), }; let mut rpc_api = RpcModule::new(()); @@ -673,8 +704,8 @@ where // An archive node that can respond to the `archive` RPC-v2 queries is a node with: // - state pruning in archive mode: The storage of blocks is kept around // - block pruning in archive mode: The block's body is kept around - let is_archive_node = config.state_pruning.as_ref().map(|sp| sp.is_archive()).unwrap_or(false) && - config.blocks_pruning.is_archive(); + let is_archive_node = state_pruning.as_ref().map(|sp| sp.is_archive()).unwrap_or(false) && + blocks_pruning.is_archive(); let genesis_hash = client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); if is_archive_node { let archive_v2 = sc_rpc_spec_v2::archive::Archive::new( @@ -690,9 +721,9 @@ where // ChainSpec RPC-v2. let chain_spec_v2 = sc_rpc_spec_v2::chain_spec::ChainSpec::new( - config.chain_spec.name().into(), + chain_spec.name().into(), genesis_hash, - config.chain_spec.properties(), + chain_spec.properties(), ) .into_rpc(); @@ -953,7 +984,7 @@ where let genesis_hash = client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); let network_params = sc_network::config::Params::::Hash, TNet> { - role: config.role.clone(), + role: config.role, executor: { let spawn_handle = Clone::clone(&spawn_handle); Box::new(move |fut| { @@ -999,7 +1030,7 @@ where "system-rpc-handler", Some("networking"), build_system_rpc_future::<_, _, ::Hash>( - config.role.clone(), + config.role, network_mut.network_service(), sync_service.clone(), client.clone(), diff --git a/substrate/client/service/src/config.rs b/substrate/client/service/src/config.rs index 8387f818e68b..6f65c2e2d81b 100644 --- a/substrate/client/service/src/config.rs +++ b/substrate/client/service/src/config.rs @@ -78,48 +78,18 @@ pub struct Configuration { pub blocks_pruning: BlocksPruning, /// Chain configuration. pub chain_spec: Box, - /// Wasm execution method. - pub wasm_method: WasmExecutionMethod, + /// Runtime executor configuration. + pub executor: ExecutorConfiguration, /// Directory where local WASM runtimes live. These runtimes take precedence /// over on-chain runtimes when the spec version matches. Set to `None` to /// disable overrides (default). pub wasm_runtime_overrides: Option, - /// JSON-RPC server endpoints. - pub rpc_addr: Option>, - /// Maximum number of connections for JSON-RPC server. - pub rpc_max_connections: u32, - /// CORS settings for HTTP & WS servers. `None` if all origins are allowed. - pub rpc_cors: Option>, - /// RPC methods to expose (by default only a safe subset or all of them). - pub rpc_methods: RpcMethods, - /// Maximum payload of a rpc request - pub rpc_max_request_size: u32, - /// Maximum payload of a rpc response. - pub rpc_max_response_size: u32, - /// Custom JSON-RPC subscription ID provider. - /// - /// Default: [`crate::RandomStringSubscriptionId`]. - pub rpc_id_provider: Option>, - /// Maximum allowed subscriptions per rpc connection - pub rpc_max_subs_per_conn: u32, - /// JSON-RPC server default port. - pub rpc_port: u16, - /// The number of messages the JSON-RPC server is allowed to keep in memory. - pub rpc_message_buffer_capacity: u32, - /// JSON-RPC server batch config. - pub rpc_batch_config: RpcBatchRequestConfig, - /// RPC rate limit per minute. - pub rpc_rate_limit: Option, - /// RPC rate limit whitelisted ip addresses. - pub rpc_rate_limit_whitelisted_ips: Vec, - /// RPC rate limit trust proxy headers. - pub rpc_rate_limit_trust_proxy_headers: bool, + /// RPC configuration. + pub rpc: RpcConfiguration, /// Prometheus endpoint configuration. `None` if disabled. pub prometheus_config: Option, /// Telemetry service URL. `None` if disabled. pub telemetry_endpoints: Option, - /// The default number of 64KB pages to allocate for Wasm execution - pub default_heap_pages: Option, /// Should offchain workers be executed. pub offchain_worker: OffchainWorkerConfig, /// Enable authoring even when offline. @@ -137,18 +107,12 @@ pub struct Configuration { pub tracing_targets: Option, /// Tracing receiver pub tracing_receiver: sc_tracing::TracingReceiver, - /// The size of the instances cache. - /// - /// The default value is 8. - pub max_runtime_instances: usize, /// Announce block automatically after they have been imported pub announce_block: bool, /// Data path root for the configured chain. pub data_path: PathBuf, /// Base path of the configuration. This is shared between chains. pub base_path: BasePath, - /// Maximum number of different runtime versions that can be cached. - pub runtime_cache_size: u8, } /// Type for tasks spawned by the executor. @@ -323,3 +287,64 @@ impl From for BasePath { BasePath::new(path) } } + +/// RPC configuration. +#[derive(Debug)] +pub struct RpcConfiguration { + /// JSON-RPC server endpoints. + pub addr: Option>, + /// Maximum number of connections for JSON-RPC server. + pub max_connections: u32, + /// CORS settings for HTTP & WS servers. `None` if all origins are allowed. + pub cors: Option>, + /// RPC methods to expose (by default only a safe subset or all of them). + pub methods: RpcMethods, + /// Maximum payload of a rpc request + pub max_request_size: u32, + /// Maximum payload of a rpc response. + pub max_response_size: u32, + /// Custom JSON-RPC subscription ID provider. + /// + /// Default: [`crate::RandomStringSubscriptionId`]. + pub id_provider: Option>, + /// Maximum allowed subscriptions per rpc connection + pub max_subs_per_conn: u32, + /// JSON-RPC server default port. + pub port: u16, + /// The number of messages the JSON-RPC server is allowed to keep in memory. + pub message_buffer_capacity: u32, + /// JSON-RPC server batch config. + pub batch_config: RpcBatchRequestConfig, + /// RPC rate limit per minute. + pub rate_limit: Option, + /// RPC rate limit whitelisted ip addresses. + pub rate_limit_whitelisted_ips: Vec, + /// RPC rate limit trust proxy headers. + pub rate_limit_trust_proxy_headers: bool, +} + +/// Runtime executor configuration. +#[derive(Debug, Clone)] +pub struct ExecutorConfiguration { + /// Wasm execution method. + pub wasm_method: WasmExecutionMethod, + /// The size of the instances cache. + /// + /// The default value is 8. + pub max_runtime_instances: usize, + /// The default number of 64KB pages to allocate for Wasm execution + pub default_heap_pages: Option, + /// Maximum number of different runtime versions that can be cached. + pub runtime_cache_size: u8, +} + +impl Default for ExecutorConfiguration { + fn default() -> Self { + Self { + wasm_method: WasmExecutionMethod::default(), + max_runtime_instances: 8, + default_heap_pages: None, + runtime_cache_size: 2, + } + } +} diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index 9e8aed39c897..251eef97be84 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -83,6 +83,8 @@ pub use sc_chain_spec::{ Properties, }; +use crate::config::RpcConfiguration; +use prometheus_endpoint::Registry; pub use sc_consensus::ImportQueue; pub use sc_executor::NativeExecutionDispatch; pub use sc_network_sync::WarpSyncConfig; @@ -95,6 +97,7 @@ pub use sc_transaction_pool_api::{error::IntoPoolError, InPoolTransaction, Trans #[doc(hidden)] pub use std::{ops::Deref, result::Result, sync::Arc}; pub use task_manager::{SpawnTaskHandle, Task, TaskManager, TaskRegistry, DEFAULT_GROUP_NAME}; +use tokio::runtime::Handle; const DEFAULT_PROTOCOL_ID: &str = "sup"; @@ -376,7 +379,9 @@ mod waiting { /// Starts RPC servers. pub fn start_rpc_servers( - config: &Configuration, + rpc_configuration: &RpcConfiguration, + registry: Option<&Registry>, + tokio_handle: &Handle, gen_rpc_module: R, rpc_id_provider: Option>, ) -> Result, error::Error> @@ -384,50 +389,51 @@ where R: Fn() -> Result, Error>, { let endpoints: Vec = if let Some(endpoints) = - config.rpc_addr.as_ref() + rpc_configuration.addr.as_ref() { endpoints.clone() } else { - let ipv6 = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::LOCALHOST, config.rpc_port, 0, 0)); - let ipv4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, config.rpc_port)); + let ipv6 = + SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::LOCALHOST, rpc_configuration.port, 0, 0)); + let ipv4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, rpc_configuration.port)); vec![ sc_rpc_server::RpcEndpoint { - batch_config: config.rpc_batch_config, - cors: config.rpc_cors.clone(), + batch_config: rpc_configuration.batch_config, + cors: rpc_configuration.cors.clone(), listen_addr: ipv4, - max_buffer_capacity_per_connection: config.rpc_message_buffer_capacity, - max_connections: config.rpc_max_connections, - max_payload_in_mb: config.rpc_max_request_size, - max_payload_out_mb: config.rpc_max_response_size, - max_subscriptions_per_connection: config.rpc_max_subs_per_conn, - rpc_methods: config.rpc_methods.into(), - rate_limit: config.rpc_rate_limit, - rate_limit_trust_proxy_headers: config.rpc_rate_limit_trust_proxy_headers, - rate_limit_whitelisted_ips: config.rpc_rate_limit_whitelisted_ips.clone(), + max_buffer_capacity_per_connection: rpc_configuration.message_buffer_capacity, + max_connections: rpc_configuration.max_connections, + max_payload_in_mb: rpc_configuration.max_request_size, + max_payload_out_mb: rpc_configuration.max_response_size, + max_subscriptions_per_connection: rpc_configuration.max_subs_per_conn, + rpc_methods: rpc_configuration.methods.into(), + rate_limit: rpc_configuration.rate_limit, + rate_limit_trust_proxy_headers: rpc_configuration.rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: rpc_configuration.rate_limit_whitelisted_ips.clone(), retry_random_port: true, is_optional: false, }, sc_rpc_server::RpcEndpoint { - batch_config: config.rpc_batch_config, - cors: config.rpc_cors.clone(), + batch_config: rpc_configuration.batch_config, + cors: rpc_configuration.cors.clone(), listen_addr: ipv6, - max_buffer_capacity_per_connection: config.rpc_message_buffer_capacity, - max_connections: config.rpc_max_connections, - max_payload_in_mb: config.rpc_max_request_size, - max_payload_out_mb: config.rpc_max_response_size, - max_subscriptions_per_connection: config.rpc_max_subs_per_conn, - rpc_methods: config.rpc_methods.into(), - rate_limit: config.rpc_rate_limit, - rate_limit_trust_proxy_headers: config.rpc_rate_limit_trust_proxy_headers, - rate_limit_whitelisted_ips: config.rpc_rate_limit_whitelisted_ips.clone(), + max_buffer_capacity_per_connection: rpc_configuration.message_buffer_capacity, + max_connections: rpc_configuration.max_connections, + max_payload_in_mb: rpc_configuration.max_request_size, + max_payload_out_mb: rpc_configuration.max_response_size, + max_subscriptions_per_connection: rpc_configuration.max_subs_per_conn, + rpc_methods: rpc_configuration.methods.into(), + rate_limit: rpc_configuration.rate_limit, + rate_limit_trust_proxy_headers: rpc_configuration.rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: rpc_configuration.rate_limit_whitelisted_ips.clone(), retry_random_port: true, is_optional: true, }, ] }; - let metrics = sc_rpc_server::RpcMetrics::new(config.prometheus_registry())?; + let metrics = sc_rpc_server::RpcMetrics::new(registry)?; let rpc_api = gen_rpc_module()?; let server_config = sc_rpc_server::Config { @@ -435,7 +441,7 @@ where rpc_api, metrics, id_provider: rpc_id_provider, - tokio_handle: config.tokio_handle.clone(), + tokio_handle: tokio_handle.clone(), }; // TODO: https://github.com/paritytech/substrate/issues/13773 @@ -443,7 +449,7 @@ where // `block_in_place` is a hack to allow callers to call `block_on` prior to // calling `start_rpc_servers`. match tokio::task::block_in_place(|| { - config.tokio_handle.block_on(sc_rpc_server::start_server(server_config)) + tokio_handle.block_on(sc_rpc_server::start_server(server_config)) }) { Ok(server) => Ok(Box::new(waiting::Server(Some(server)))), Err(e) => Err(Error::Application(e)), diff --git a/substrate/client/service/src/metrics.rs b/substrate/client/service/src/metrics.rs index a411a83a784e..2bcb83f41979 100644 --- a/substrate/client/service/src/metrics.rs +++ b/substrate/client/service/src/metrics.rs @@ -16,9 +16,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::time::SystemTime; - -use crate::config::Configuration; use futures_timer::Delay; use prometheus_endpoint::{register, Gauge, GaugeVec, Opts, PrometheusError, Registry, U64}; use sc_client_api::{ClientInfo, UsageProvider}; @@ -31,7 +28,7 @@ use sp_api::ProvideRuntimeApi; use sp_runtime::traits::{Block, NumberFor, SaturatedConversion, UniqueSaturatedInto}; use std::{ sync::Arc, - time::{Duration, Instant}, + time::{Duration, Instant, SystemTime}, }; struct PrometheusMetrics { @@ -149,26 +146,24 @@ impl MetricsService { pub fn with_prometheus( telemetry: Option, registry: &Registry, - config: &Configuration, + role: Role, + node_name: &str, + impl_version: &str, ) -> Result { - let role_bits = match config.role { + let role_bits = match role { Role::Full => 1u64, // 2u64 used to represent light client role Role::Authority { .. } => 4u64, }; - PrometheusMetrics::setup( - registry, - &config.network.node_name, - &config.impl_version, - role_bits, - ) - .map(|p| MetricsService { - metrics: Some(p), - last_total_bytes_inbound: 0, - last_total_bytes_outbound: 0, - last_update: Instant::now(), - telemetry, + PrometheusMetrics::setup(registry, node_name, impl_version, role_bits).map(|p| { + MetricsService { + metrics: Some(p), + last_total_bytes_inbound: 0, + last_total_bytes_outbound: 0, + last_update: Instant::now(), + telemetry, + } }) } diff --git a/substrate/client/service/test/src/lib.rs b/substrate/client/service/test/src/lib.rs index 5b9bb537563d..d64581480cdb 100644 --- a/substrate/client/service/test/src/lib.rs +++ b/substrate/client/service/test/src/lib.rs @@ -29,7 +29,10 @@ use sc_network::{ use sc_network_sync::SyncingService; use sc_service::{ client::Client, - config::{BasePath, DatabaseSource, KeystoreConfig, RpcBatchRequestConfig}, + config::{ + BasePath, DatabaseSource, ExecutorConfiguration, KeystoreConfig, RpcBatchRequestConfig, + RpcConfiguration, + }, BlocksPruning, ChainSpecExtension, Configuration, Error, GenericChainSpec, Role, SpawnTaskHandle, TaskManager, }; @@ -235,36 +238,35 @@ fn node_config( state_pruning: Default::default(), blocks_pruning: BlocksPruning::KeepFinalized, chain_spec: Box::new((*spec).clone()), - wasm_method: Default::default(), + executor: ExecutorConfiguration::default(), wasm_runtime_overrides: Default::default(), - rpc_addr: Default::default(), - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_id_provider: Default::default(), - rpc_max_subs_per_conn: Default::default(), - rpc_port: 9944, - rpc_message_buffer_capacity: Default::default(), - rpc_batch_config: RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), + rpc: RpcConfiguration { + addr: Default::default(), + max_connections: Default::default(), + cors: None, + methods: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + id_provider: Default::default(), + max_subs_per_conn: Default::default(), + port: 9944, + message_buffer_capacity: Default::default(), + batch_config: RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), + }, prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: Default::default(), force_authoring: false, disable_grandpa: false, dev_key_seed: key_seed, tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, announce_block: true, base_path: BasePath::new(root.clone()), data_path: root, - runtime_cache_size: 2, } } diff --git a/templates/minimal/node/src/service.rs b/templates/minimal/node/src/service.rs index ce3afe1e6b76..a42eb10ccec6 100644 --- a/templates/minimal/node/src/service.rs +++ b/templates/minimal/node/src/service.rs @@ -61,7 +61,7 @@ pub fn new_partial(config: &Configuration) -> Result { }) .transpose()?; - let executor = sc_service::new_wasm_executor(config); + let executor = sc_service::new_wasm_executor(&config.executor); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( diff --git a/templates/parachain/node/src/command.rs b/templates/parachain/node/src/command.rs index f346ae4386a6..fa94d50a8bed 100644 --- a/templates/parachain/node/src/command.rs +++ b/templates/parachain/node/src/command.rs @@ -307,15 +307,9 @@ impl CliConfiguration for RelayChainCli { self.base.base.prometheus_config(default_listen_port, chain_spec) } - fn init( - &self, - _support_url: &String, - _impl_version: &String, - _logger_hook: F, - _config: &sc_service::Configuration, - ) -> Result<()> + fn init(&self, _support_url: &String, _impl_version: &String, _logger_hook: F) -> Result<()> where - F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + F: FnOnce(&mut sc_cli::LoggerBuilder), { unreachable!("PolkadotCli is never initialized; qed"); } diff --git a/templates/parachain/node/src/service.rs b/templates/parachain/node/src/service.rs index b46b6ecfde18..fc0bcba4c0d1 100644 --- a/templates/parachain/node/src/service.rs +++ b/templates/parachain/node/src/service.rs @@ -77,15 +77,16 @@ pub fn new_partial(config: &Configuration) -> Result .transpose()?; let heap_pages = config + .executor .default_heap_pages .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ }); let executor = ParachainExecutor::builder() - .with_execution_method(config.wasm_method) + .with_execution_method(config.executor.wasm_method) .with_onchain_heap_alloc_strategy(heap_pages) .with_offchain_heap_alloc_strategy(heap_pages) - .with_max_runtime_instances(config.max_runtime_instances) - .with_runtime_cache_size(config.runtime_cache_size) + .with_max_runtime_instances(config.executor.max_runtime_instances) + .with_runtime_cache_size(config.executor.runtime_cache_size) .build(); let (client, backend, keystore_container, task_manager) = diff --git a/templates/solochain/node/src/service.rs b/templates/solochain/node/src/service.rs index 2b43ecfa1ce2..7d37c5ce87f8 100644 --- a/templates/solochain/node/src/service.rs +++ b/templates/solochain/node/src/service.rs @@ -48,7 +48,7 @@ pub fn new_partial(config: &Configuration) -> Result { }) .transpose()?; - let executor = sc_service::new_wasm_executor::(config); + let executor = sc_service::new_wasm_executor::(&config.executor); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( config, @@ -201,7 +201,7 @@ pub fn new_full< ); } - let role = config.role.clone(); + let role = config.role; let force_authoring = config.force_authoring; let backoff_authoring_blocks: Option<()> = None; let name = config.network.node_name.clone(); From f58e2b80c901176ff66376b49973195120559669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 2 Sep 2024 16:08:23 +0200 Subject: [PATCH 211/382] collator-protocol: Handle unknown validator heads (#5538) There is a race condition when a validator sends its heads to the collator, but the collator doesn't yet know these heads. Before it is aware of these heads by importing the block(s), any collation registered on the collator is not announced to the validators. The collations aren't advertised, because the collator doesn't know yet that these heads of the validator are descendants of the collations relay parent. The solution is to store these unknown heads of the validators and to handle them when the collator updates its own view. --- Cargo.lock | 5 +- Cargo.toml | 2 +- .../node/network/collator-protocol/Cargo.toml | 1 + .../src/collator_side/mod.rs | 78 ++++-- .../src/collator_side/tests/mod.rs | 87 ++++-- .../tests/prospective_parachains.rs | 262 ++++++++++-------- .../node/subsystem-test-helpers/src/lib.rs | 22 ++ prdoc/pr_5538.prdoc | 11 + 8 files changed, 297 insertions(+), 171 deletions(-) create mode 100644 prdoc/pr_5538.prdoc diff --git a/Cargo.lock b/Cargo.lock index c2cbb0f6d4f6..2074bb1d67ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13150,6 +13150,7 @@ dependencies = [ "rstest", "sc-keystore", "sc-network", + "schnellru", "sp-core", "sp-keyring", "sp-keystore", @@ -18797,9 +18798,9 @@ dependencies = [ [[package]] name = "schnellru" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" dependencies = [ "ahash 0.8.11", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index f8c430f58254..5ed5ce2f13cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1178,7 +1178,7 @@ sc-transaction-pool-api = { path = "substrate/client/transaction-pool/api", defa sc-utils = { path = "substrate/client/utils", default-features = false } scale-info = { version = "2.11.1", default-features = false } schemars = { version = "0.8.13", default-features = false } -schnellru = { version = "0.2.1" } +schnellru = { version = "0.2.3" } schnorrkel = { version = "0.11.4", default-features = false } seccompiler = { version = "0.4.0" } secp256k1 = { version = "0.28.0", default-features = false } diff --git a/polkadot/node/network/collator-protocol/Cargo.toml b/polkadot/node/network/collator-protocol/Cargo.toml index 8a7c384dcbe8..304cb23bb6aa 100644 --- a/polkadot/node/network/collator-protocol/Cargo.toml +++ b/polkadot/node/network/collator-protocol/Cargo.toml @@ -14,6 +14,7 @@ bitvec = { features = ["alloc"], workspace = true } futures = { workspace = true } futures-timer = { workspace = true } gum = { workspace = true, default-features = true } +schnellru.workspace = true sp-core = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } diff --git a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs index 401525d02f11..97bc66d6058c 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs @@ -23,6 +23,7 @@ use bitvec::{bitvec, vec::BitVec}; use futures::{ channel::oneshot, future::Fuse, pin_mut, select, stream::FuturesUnordered, FutureExt, StreamExt, }; +use schnellru::{ByLength, LruMap}; use sp_core::Pair; use polkadot_node_network_protocol::{ @@ -42,7 +43,7 @@ use polkadot_node_subsystem::{ CollatorProtocolMessage, NetworkBridgeEvent, NetworkBridgeTxMessage, ParentHeadData, RuntimeApiMessage, }, - overseer, CollatorProtocolSenderTrait, FromOrchestra, OverseerSignal, PerLeafSpan, + overseer, FromOrchestra, OverseerSignal, PerLeafSpan, }; use polkadot_node_subsystem_util::{ backing_implicit_view::View as ImplicitView, @@ -200,6 +201,11 @@ struct PeerData { view: View, /// Network protocol version. version: CollationVersion, + /// Unknown heads in the view. + /// + /// This can happen when the validator is faster at importing a block and sending out its + /// `View` than the collator is able to import a block. + unknown_heads: LruMap, } /// A type wrapping a collation and it's designated core index. @@ -1198,9 +1204,10 @@ async fn handle_peer_view_change( peer_id: PeerId, view: View, ) { - let PeerData { view: current, version } = match state.peer_data.get_mut(&peer_id) { - Some(peer_data) => peer_data, - None => return, + let Some(PeerData { view: current, version, unknown_heads }) = + state.peer_data.get_mut(&peer_id) + else { + return }; let added: Vec = view.difference(&*current).cloned().collect(); @@ -1228,15 +1235,18 @@ async fn handle_peer_view_change( new_leaf = ?added, "New leaf in peer's view is unknown", ); + + unknown_heads.insert(added, ()); + continue }, }; for block_hash in block_hashes { - let per_relay_parent = match state.per_relay_parent.get_mut(block_hash) { - Some(per_relay_parent) => per_relay_parent, - None => continue, + let Some(per_relay_parent) = state.per_relay_parent.get_mut(block_hash) else { + continue }; + advertise_collation( ctx, *block_hash, @@ -1282,10 +1292,13 @@ async fn handle_network_msg( return Ok(()) }, }; - state - .peer_data - .entry(peer_id) - .or_insert_with(|| PeerData { view: View::default(), version }); + state.peer_data.entry(peer_id).or_insert_with(|| PeerData { + view: View::default(), + version, + // Unlikely that the collator is falling 10 blocks behind and if so, it probably is + // not able to keep up any way. + unknown_heads: LruMap::new(ByLength::new(10)), + }); if let Some(authority_ids) = maybe_authority { gum::trace!( @@ -1310,7 +1323,7 @@ async fn handle_network_msg( }, OurViewChange(view) => { gum::trace!(target: LOG_TARGET, ?view, "Own view change"); - handle_our_view_change(ctx.sender(), state, view).await?; + handle_our_view_change(ctx, state, view).await?; }, PeerMessage(remote, msg) => { handle_incoming_peer_message(ctx, runtime, state, remote, msg).await?; @@ -1332,21 +1345,19 @@ async fn handle_network_msg( } /// Handles our view changes. -async fn handle_our_view_change( - sender: &mut Sender, +#[overseer::contextbounds(CollatorProtocol, prefix = crate::overseer)] +async fn handle_our_view_change( + ctx: &mut Context, state: &mut State, view: OurView, -) -> Result<()> -where - Sender: CollatorProtocolSenderTrait, -{ +) -> Result<()> { let current_leaves = state.active_leaves.clone(); let removed = current_leaves.iter().filter(|(h, _)| !view.contains(h)); let added = view.iter().filter(|h| !current_leaves.contains_key(h)); for leaf in added { - let mode = prospective_parachains_mode(sender, *leaf).await?; + let mode = prospective_parachains_mode(ctx.sender(), *leaf).await?; if let Some(span) = view.span_per_head().get(leaf).cloned() { let per_leaf_span = PerLeafSpan::new(span, "collator-side"); @@ -1359,7 +1370,7 @@ where if mode.is_enabled() { if let Some(ref mut implicit_view) = state.implicit_view { implicit_view - .activate_leaf(sender, *leaf) + .activate_leaf(ctx.sender(), *leaf) .await .map_err(Error::ImplicitViewFetchError)?; @@ -1367,11 +1378,36 @@ where .known_allowed_relay_parents_under(leaf, state.collating_on) .unwrap_or_default(); + // Get the peers that already reported us this head, but we didn't knew it at this + // point. + let peers = state + .peer_data + .iter_mut() + .filter_map(|(id, data)| { + data.unknown_heads.remove(leaf).map(|_| (id, data.version)) + }) + .collect::>(); + for block_hash in allowed_ancestry { - state + let per_relay_parent = state .per_relay_parent .entry(*block_hash) .or_insert_with(|| PerRelayParent::new(mode)); + + // Announce relevant collations to these peers. + for (peer_id, peer_version) in &peers { + advertise_collation( + ctx, + *block_hash, + per_relay_parent, + &peer_id, + *peer_version, + &state.peer_ids, + &mut state.advertisement_timeouts, + &state.metrics, + ) + .await; + } } } } diff --git a/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs index 74a151c168dc..2f4c768b89e0 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs @@ -299,21 +299,33 @@ async fn overseer_send(overseer: &mut VirtualOverseer, msg: CollatorProtocolMess } async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages { - let msg = overseer_recv_with_timeout(overseer, TIMEOUT) + overseer_recv_with_timeout(overseer, TIMEOUT) .await - .expect(&format!("{:?} is more than enough to receive messages", TIMEOUT)); + .expect(&format!("{:?} is more than enough to receive messages", TIMEOUT)) +} + +async fn overseer_recv_with_timeout( + overseer: &mut VirtualOverseer, + timeout: Duration, +) -> Option { + gum::trace!("waiting for message..."); + let msg = overseer.recv().timeout(timeout).await; gum::trace!(?msg, "received message"); msg } -async fn overseer_recv_with_timeout( +async fn overseer_peek_with_timeout( overseer: &mut VirtualOverseer, timeout: Duration, -) -> Option { - gum::trace!("waiting for message..."); - overseer.recv().timeout(timeout).await +) -> Option<&AllMessages> { + gum::trace!("peeking for message..."); + let msg = overseer.peek().timeout(timeout).await; + + gum::trace!(?msg, "received message"); + + msg.flatten() } async fn overseer_signal(overseer: &mut VirtualOverseer, signal: OverseerSignal) { @@ -603,7 +615,7 @@ async fn expect_declare_msg( /// Expects v2 message if `expected_candidate_hashes` is `Some`, v1 otherwise. async fn expect_advertise_collation_msg( virtual_overseer: &mut VirtualOverseer, - peer: &PeerId, + any_peers: &[PeerId], expected_relay_parent: Hash, expected_candidate_hashes: Option>, ) { @@ -620,7 +632,7 @@ async fn expect_advertise_collation_msg( wire_message, ) ) => { - assert_eq!(to[0], *peer); + assert!(any_peers.iter().any(|p| to.contains(p))); match (candidate_hashes.as_mut(), wire_message) { (None, Versioned::V1(protocol_v1::CollationProtocol::CollatorProtocol(wire_message))) => { assert_matches!( @@ -739,7 +751,7 @@ fn advertise_and_send_collation() { // advertise it. expect_advertise_collation_msg( &mut virtual_overseer, - &peer, + &[peer], test_state.relay_parent, None, ) @@ -849,7 +861,7 @@ fn advertise_and_send_collation() { expect_advertise_collation_msg( &mut virtual_overseer, - &peer, + &[peer], test_state.relay_parent, None, ) @@ -910,7 +922,7 @@ fn delay_reputation_change() { // advertise it. expect_advertise_collation_msg( &mut virtual_overseer, - &peer, + &[peer], test_state.relay_parent, None, ) @@ -1031,7 +1043,7 @@ fn advertise_collation_v2_protocol() { // Versioned advertisements work. expect_advertise_collation_msg( virtual_overseer, - &peer_ids[0], + &[peer_ids[0]], test_state.relay_parent, None, ) @@ -1039,7 +1051,7 @@ fn advertise_collation_v2_protocol() { for peer_id in peer_ids.iter().skip(1) { expect_advertise_collation_msg( virtual_overseer, - peer_id, + &[*peer_id], test_state.relay_parent, Some(vec![candidate.hash()]), // This is `Some`, advertisement is v2. ) @@ -1142,15 +1154,25 @@ fn collations_are_only_advertised_to_validators_with_correct_view() { distribute_collation(virtual_overseer, &test_state, test_state.relay_parent, true) .await; - expect_advertise_collation_msg(virtual_overseer, &peer2, test_state.relay_parent, None) - .await; + expect_advertise_collation_msg( + virtual_overseer, + &[peer2], + test_state.relay_parent, + None, + ) + .await; // The other validator announces that it changed its view. send_peer_view_change(virtual_overseer, &peer, vec![test_state.relay_parent]).await; // After changing the view we should receive the advertisement - expect_advertise_collation_msg(virtual_overseer, &peer, test_state.relay_parent, None) - .await; + expect_advertise_collation_msg( + virtual_overseer, + &[peer], + test_state.relay_parent, + None, + ) + .await; test_harness }, ) @@ -1199,12 +1221,17 @@ fn collate_on_two_different_relay_chain_blocks() { .await; send_peer_view_change(virtual_overseer, &peer, vec![old_relay_parent]).await; - expect_advertise_collation_msg(virtual_overseer, &peer, old_relay_parent, None).await; + expect_advertise_collation_msg(virtual_overseer, &[peer], old_relay_parent, None).await; send_peer_view_change(virtual_overseer, &peer2, vec![test_state.relay_parent]).await; - expect_advertise_collation_msg(virtual_overseer, &peer2, test_state.relay_parent, None) - .await; + expect_advertise_collation_msg( + virtual_overseer, + &[peer2], + test_state.relay_parent, + None, + ) + .await; test_harness }, ) @@ -1237,8 +1264,13 @@ fn validator_reconnect_does_not_advertise_a_second_time() { .await; send_peer_view_change(virtual_overseer, &peer, vec![test_state.relay_parent]).await; - expect_advertise_collation_msg(virtual_overseer, &peer, test_state.relay_parent, None) - .await; + expect_advertise_collation_msg( + virtual_overseer, + &[peer], + test_state.relay_parent, + None, + ) + .await; // Disconnect and reconnect directly disconnect_peer(virtual_overseer, peer).await; @@ -1361,14 +1393,14 @@ where // advertise it. expect_advertise_collation_msg( virtual_overseer, - &validator_0, + &[validator_0], test_state.relay_parent, None, ) .await; expect_advertise_collation_msg( virtual_overseer, - &validator_1, + &[validator_1], test_state.relay_parent, None, ) @@ -1498,9 +1530,10 @@ fn connect_to_buffered_groups() { } // Update views. - for peed_id in &peers_a { - send_peer_view_change(&mut virtual_overseer, peed_id, vec![head_a]).await; - expect_advertise_collation_msg(&mut virtual_overseer, peed_id, head_a, None).await; + for peer_id in &peers_a { + send_peer_view_change(&mut virtual_overseer, peer_id, vec![head_a]).await; + expect_advertise_collation_msg(&mut virtual_overseer, &[*peer_id], head_a, None) + .await; } let peer = peers_a[0]; diff --git a/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs b/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs index ea8fdb0e04fb..d3eae9dbba6e 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs @@ -45,7 +45,6 @@ async fn update_view( ) .await; - let mut next_overseer_message = None; for _ in 0..activated { let (leaf_hash, leaf_number) = assert_matches!( overseer_recv(virtual_overseer).await, @@ -147,18 +146,10 @@ async fn update_view( let parent_hash = ancestry_iter.peek().map(|(h, _)| *h).unwrap_or_else(|| get_parent_hash(hash)); - let msg = match next_overseer_message.take() { - Some(msg) => Some(msg), - None => - overseer_recv_with_timeout(virtual_overseer, Duration::from_millis(50)).await, - }; - - let msg = match msg { - Some(msg) => msg, - None => { - // We're done. - return - }, + let Some(msg) = + overseer_peek_with_timeout(virtual_overseer, Duration::from_millis(50)).await + else { + return }; if !matches!( @@ -167,12 +158,11 @@ async fn update_view( if *_hash == hash ) { // Ancestry has already been cached for this leaf. - next_overseer_message.replace(msg); break } assert_matches!( - msg, + overseer_recv_with_timeout(virtual_overseer, Duration::from_millis(50)).await.unwrap(), AllMessages::ChainApi(ChainApiMessage::BlockHeader(.., tx)) => { let header = Header { parent_hash, @@ -238,124 +228,156 @@ fn distribute_collation_from_implicit_view() { let head_c = Hash::from_low_u64_be(130); let head_c_num = 62; - let group_rotation_info = GroupRotationInfo { - session_start_block: head_c_num - 2, - group_rotation_frequency: 3, - now: head_c_num, - }; + // Run once with validators sending their view first and then the collator setting their own + // view first. + for validator_sends_view_first in [true, false] { + let group_rotation_info = GroupRotationInfo { + session_start_block: head_c_num - 2, + group_rotation_frequency: 3, + now: head_c_num, + }; + + let mut test_state = TestState::default(); + test_state.group_rotation_info = group_rotation_info; + + let local_peer_id = test_state.local_peer_id; + let collator_pair = test_state.collator_pair.clone(); + + test_harness( + local_peer_id, + collator_pair, + ReputationAggregator::new(|_| true), + |mut test_harness| async move { + let virtual_overseer = &mut test_harness.virtual_overseer; + + // Set collating para id. + overseer_send( + virtual_overseer, + CollatorProtocolMessage::CollateOn(test_state.para_id), + ) + .await; - let mut test_state = TestState::default(); - test_state.group_rotation_info = group_rotation_info; + if validator_sends_view_first { + // Activate leaf `c` to accept at least the collation. + update_view(virtual_overseer, vec![(head_c, head_c_num)], 1).await; + } else { + // Activated leaf is `b`, but the collation will be based on `c`. + update_view(virtual_overseer, vec![(head_b, head_b_num)], 1).await; + } - let local_peer_id = test_state.local_peer_id; - let collator_pair = test_state.collator_pair.clone(); + let validator_peer_ids = test_state.current_group_validator_peer_ids(); + for (val, peer) in test_state + .current_group_validator_authority_ids() + .into_iter() + .zip(validator_peer_ids.clone()) + { + connect_peer(virtual_overseer, peer, CollationVersion::V2, Some(val.clone())) + .await; + } - test_harness( - local_peer_id, - collator_pair, - ReputationAggregator::new(|_| true), - |mut test_harness| async move { - let virtual_overseer = &mut test_harness.virtual_overseer; + // Collator declared itself to each peer. + for peer_id in &validator_peer_ids { + expect_declare_msg_v2(virtual_overseer, &test_state, peer_id).await; + } - // Set collating para id. - overseer_send(virtual_overseer, CollatorProtocolMessage::CollateOn(test_state.para_id)) - .await; - // Activated leaf is `b`, but the collation will be based on `c`. - update_view(virtual_overseer, vec![(head_b, head_b_num)], 1).await; - - let validator_peer_ids = test_state.current_group_validator_peer_ids(); - for (val, peer) in test_state - .current_group_validator_authority_ids() - .into_iter() - .zip(validator_peer_ids.clone()) - { - connect_peer(virtual_overseer, peer, CollationVersion::V2, Some(val.clone())).await; - } + let pov = PoV { block_data: BlockData(vec![1, 2, 3]) }; + let parent_head_data_hash = Hash::repeat_byte(0xAA); + let candidate = TestCandidateBuilder { + para_id: test_state.para_id, + relay_parent: head_c, + pov_hash: pov.hash(), + ..Default::default() + } + .build(); + let DistributeCollation { candidate, pov_block: _ } = + distribute_collation_with_receipt( + virtual_overseer, + &test_state, + head_c, + false, // Check the group manually. + candidate, + pov, + parent_head_data_hash, + ) + .await; + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::NetworkBridgeTx( + NetworkBridgeTxMessage::ConnectToValidators { validator_ids, .. } + ) => { + let expected_validators = test_state.current_group_validator_authority_ids(); - // Collator declared itself to each peer. - for peer_id in &validator_peer_ids { - expect_declare_msg_v2(virtual_overseer, &test_state, peer_id).await; - } + assert_eq!(expected_validators, validator_ids); + } + ); - let pov = PoV { block_data: BlockData(vec![1, 2, 3]) }; - let parent_head_data_hash = Hash::repeat_byte(0xAA); - let candidate = TestCandidateBuilder { - para_id: test_state.para_id, - relay_parent: head_c, - pov_hash: pov.hash(), - ..Default::default() - } - .build(); - let DistributeCollation { candidate, pov_block: _ } = - distribute_collation_with_receipt( - virtual_overseer, - &test_state, - head_c, - false, // Check the group manually. - candidate, - pov, - parent_head_data_hash, - ) - .await; - assert_matches!( - overseer_recv(virtual_overseer).await, - AllMessages::NetworkBridgeTx( - NetworkBridgeTxMessage::ConnectToValidators { validator_ids, .. } - ) => { - let expected_validators = test_state.current_group_validator_authority_ids(); + let candidate_hash = candidate.hash(); + + // Update peer views. + for peer_id in &validator_peer_ids { + send_peer_view_change(virtual_overseer, peer_id, vec![head_b]).await; - assert_eq!(expected_validators, validator_ids); + if !validator_sends_view_first { + expect_advertise_collation_msg( + virtual_overseer, + &[*peer_id], + head_c, + Some(vec![candidate_hash]), + ) + .await; + } } - ); - let candidate_hash = candidate.hash(); + if validator_sends_view_first { + // Activated leaf is `b`, but the collation will be based on `c`. + update_view(virtual_overseer, vec![(head_b, head_b_num)], 1).await; + + for _ in &validator_peer_ids { + expect_advertise_collation_msg( + virtual_overseer, + &validator_peer_ids, + head_c, + Some(vec![candidate_hash]), + ) + .await; + } + } - // Update peer views. - for peed_id in &validator_peer_ids { - send_peer_view_change(virtual_overseer, peed_id, vec![head_b]).await; - expect_advertise_collation_msg( + // Head `c` goes out of view. + // Build a different candidate for this relay parent and attempt to distribute it. + update_view(virtual_overseer, vec![(head_a, head_a_num)], 1).await; + + let pov = PoV { block_data: BlockData(vec![4, 5, 6]) }; + let parent_head_data_hash = Hash::repeat_byte(0xBB); + let candidate = TestCandidateBuilder { + para_id: test_state.para_id, + relay_parent: head_c, + pov_hash: pov.hash(), + ..Default::default() + } + .build(); + overseer_send( virtual_overseer, - peed_id, - head_c, - Some(vec![candidate_hash]), + CollatorProtocolMessage::DistributeCollation { + candidate_receipt: candidate.clone(), + parent_head_data_hash, + pov: pov.clone(), + parent_head_data: HeadData(vec![1, 2, 3]), + result_sender: None, + core_index: CoreIndex(0), + }, ) .await; - } - - // Head `c` goes out of view. - // Build a different candidate for this relay parent and attempt to distribute it. - update_view(virtual_overseer, vec![(head_a, head_a_num)], 1).await; - - let pov = PoV { block_data: BlockData(vec![4, 5, 6]) }; - let parent_head_data_hash = Hash::repeat_byte(0xBB); - let candidate = TestCandidateBuilder { - para_id: test_state.para_id, - relay_parent: head_c, - pov_hash: pov.hash(), - ..Default::default() - } - .build(); - overseer_send( - virtual_overseer, - CollatorProtocolMessage::DistributeCollation { - candidate_receipt: candidate.clone(), - parent_head_data_hash, - pov: pov.clone(), - parent_head_data: HeadData(vec![1, 2, 3]), - result_sender: None, - core_index: CoreIndex(0), - }, - ) - .await; - // Parent out of view, nothing happens. - assert!(overseer_recv_with_timeout(virtual_overseer, Duration::from_millis(100)) - .await - .is_none()); + // Parent out of view, nothing happens. + assert!(overseer_recv_with_timeout(virtual_overseer, Duration::from_millis(100)) + .await + .is_none()); - test_harness - }, - ) + test_harness + }, + ); + } } /// Tests that collator can distribute up to `MAX_CANDIDATE_DEPTH + 1` candidates @@ -505,7 +527,7 @@ fn send_parent_head_data_for_elastic_scaling() { send_peer_view_change(&mut virtual_overseer, &peer, vec![head_b]).await; let hashes: Vec<_> = vec![candidate.hash()]; - expect_advertise_collation_msg(&mut virtual_overseer, &peer, head_b, Some(hashes)) + expect_advertise_collation_msg(&mut virtual_overseer, &[peer], head_b, Some(hashes)) .await; let (pending_response, rx) = oneshot::channel(); @@ -625,7 +647,7 @@ fn advertise_and_send_collation_by_hash() { // Head `b` is not a leaf, but both advertisements are still relevant. send_peer_view_change(&mut virtual_overseer, &peer, vec![head_b]).await; let hashes: Vec<_> = candidates.iter().map(|(candidate, _)| candidate.hash()).collect(); - expect_advertise_collation_msg(&mut virtual_overseer, &peer, head_b, Some(hashes)) + expect_advertise_collation_msg(&mut virtual_overseer, &[peer], head_b, Some(hashes)) .await; for (candidate, pov_block) in candidates { diff --git a/polkadot/node/subsystem-test-helpers/src/lib.rs b/polkadot/node/subsystem-test-helpers/src/lib.rs index bdb0647fee6f..5b1f8d3223d1 100644 --- a/polkadot/node/subsystem-test-helpers/src/lib.rs +++ b/polkadot/node/subsystem-test-helpers/src/lib.rs @@ -294,6 +294,9 @@ pub struct TestSubsystemContextHandle { /// Message counter over subsystems. pub message_counter: MessageCounter, + + /// Intermediate buffer for a message when using `peek`. + message_buffer: Option, } impl TestSubsystemContextHandle { @@ -323,12 +326,30 @@ impl TestSubsystemContextHandle { /// Receive the next message from the subsystem, or `None` if the channel has been closed. pub async fn try_recv(&mut self) -> Option { + if let Some(msg) = self.message_buffer.take() { + return Some(msg) + } + self.rx .next() .timeout(Self::TIMEOUT) .await .expect("`try_recv` does not timeout") } + + /// Peek into the next message from the subsystem or `None` if the channel has been closed. + pub async fn peek(&mut self) -> Option<&AllMessages> { + if self.message_buffer.is_none() { + self.message_buffer = self + .rx + .next() + .timeout(Self::TIMEOUT) + .await + .expect("`try_recv` does not timeout"); + } + + self.message_buffer.as_ref() + } } /// Make a test subsystem context with `buffer_size == 0`. This is used by most @@ -392,6 +413,7 @@ pub fn make_buffered_subsystem_context( tx: overseer_tx, rx: all_messages_rx, message_counter: message_counter.clone(), + message_buffer: None, }, ) } diff --git a/prdoc/pr_5538.prdoc b/prdoc/pr_5538.prdoc new file mode 100644 index 000000000000..5924f9789040 --- /dev/null +++ b/prdoc/pr_5538.prdoc @@ -0,0 +1,11 @@ +title: "collator-protocol: Remove race condition" + +doc: + - audience: Node Dev + description: | + Remove a race condition in the collator protocol that could lead + to collations not being announced to a validator. + +crates: + - name: polkadot-collator-protocol + bump: patch From 664e3dc5262907b46656cd4df7959ab60bda2d1e Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 22:28:37 +0800 Subject: [PATCH 212/382] Split half of the asset_hub_fee for delivery cost on AH --- .../primitives/router/src/inbound/mod.rs | 14 ++++++++++---- .../bridge-hub-westend/src/tests/snowbridge.rs | 3 ++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 12f00153e20d..00a7c0cc66fc 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -393,7 +393,7 @@ where asset_hub_fee: u128, ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { let network = Ethereum { chain_id }; - let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee / 2).into(); let (dest_para_id, beneficiary, dest_para_fee) = match destination { // Final destination is a 32-byte account on AssetHub @@ -428,7 +428,6 @@ where DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), UniversalOrigin(GlobalConsensus(network)), WithdrawAsset(asset.clone().into()), - SetFeesMode { jit_withdraw: true }, ]; match dest_para_id { @@ -436,9 +435,16 @@ where let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); instructions.extend(vec![ - // Perform a deposit reserve to send to destination chain. + // Perform a reserve withdraw to send to destination chain. Leave half of the + // asset_hub_fee for the delivery cost InitiateReserveWithdraw { - assets: Wild(AllCounted(2)), + assets: Definite( + vec![ + asset.clone(), + (Location::parent(), dest_para_fee + asset_hub_fee / 2).into(), + ] + .into(), + ), reserve: Location::new(1, [Parachain(dest_para_id)]), xcm: vec![ // Buy execution on target. diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index baadb7cc7ff8..e0dd2a42c340 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -325,7 +325,7 @@ fn transfer_relay_token() { )); assert_ok!(::EthereumSystem::register_token( - RuntimeOrigin::signed(BridgeHubWestendSender::get()), + RuntimeOrigin::root(), Box::new(VersionedLocation::V4(asset_id.clone())), AssetMetadata { name: "wnd".as_bytes().to_vec().try_into().unwrap(), @@ -457,6 +457,7 @@ fn transfer_ah_token() { GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) .unwrap() .into(); + AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); let asset_id: Location = [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())].into(); From 5ef32c8729f8f1f8c41b82a2d87f99e65f450c2c Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 22:46:03 +0800 Subject: [PATCH 213/382] Explicit to use XCM v4 as a storage key --- bridges/snowbridge/pallets/system/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 8631a45d4f64..30aba4588ba5 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -254,12 +254,13 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn tokens)] - pub type Tokens = StorageMap<_, Twox64Concat, TokenId, Location, OptionQuery>; + pub type Tokens = + StorageMap<_, Twox64Concat, TokenId, xcm::v4::Location, OptionQuery>; #[pallet::storage] #[pallet::getter(fn location_tokens)] pub type LocationToToken = - StorageMap<_, Twox64Concat, Location, TokenId, OptionQuery>; + StorageMap<_, Twox64Concat, xcm::v4::Location, TokenId, OptionQuery>; #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] From 37e28ed1f36d6bd706b6db3b908f6180af1da5a4 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 22:54:17 +0800 Subject: [PATCH 214/382] Fix test --- .../bridge-hubs/bridge-hub-rococo/tests/tests.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index e91837af0b21..c866cce5663d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -18,11 +18,13 @@ use bp_polkadot_core::Signature; use bridge_hub_rococo_runtime::{ - bridge_common_config, bridge_to_bulletin_config, bridge_to_westend_config, + bridge_common_config, bridge_to_bulletin_config, + bridge_to_ethereum_config::EthereumGatewayAddress, + bridge_to_westend_config, xcm_config::{RelayNetwork, TokenLocation, XcmConfig}, - AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, EthereumGatewayAddress, - Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, SessionKeys, SignedExtra, TransactionPayment, UncheckedExtrinsic, + AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit, + ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, + SignedExtra, TransactionPayment, UncheckedExtrinsic, }; use bridge_hub_test_utils::SlotDurations; use codec::{Decode, Encode}; From da11c7e9122d1d695a3dea57c34a73815faecf01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:57:40 +0000 Subject: [PATCH 215/382] Bump clap_complete from 4.4.0 to 4.5.13 (#5541) Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.4.0 to 4.5.13.
Release notes

Sourced from clap_complete's releases.

v4.5.13

[4.5.13] - 2024-07-31

Fixes

  • (derive) Improve error message when #[flatten]ing an optional #[group(skip)]
  • (help) Properly wrap long subcommand descriptions in help

v4.5.12

[4.5.12] - 2024-07-31

v4.5.10

[4.5.10] - 2024-07-23

v4.5.9

[4.5.9] - 2024-07-09

Fixes

  • (error) When defining a custom help flag, be sure to suggest it like we do the built-in one

v4.5.8

[4.5.8] - 2024-06-28

Fixes

  • Reduce extra flushes

v4.5.7

[4.5.7] - 2024-06-10

Fixes

  • Clean up error message when too few arguments for num_args

v4.5.6

[4.5.6] - 2024-06-06

v4.5.4

[4.5.4] - 2024-03-25

Fixes

  • (derive) Allow non-literal #[arg(id)] attributes again

v4.5.3

[4.5.3] - 2024-03-15

Internal

... (truncated)

Changelog

Sourced from clap_complete's changelog.

[4.5.13] - 2024-07-31

Fixes

  • (derive) Improve error message when #[flatten]ing an optional #[group(skip)]
  • (help) Properly wrap long subcommand descriptions in help

[4.5.12] - 2024-07-31

[4.5.11] - 2024-07-25

[4.5.10] - 2024-07-23

[4.5.9] - 2024-07-09

Fixes

  • (error) When defining a custom help flag, be sure to suggest it like we do the built-in one

[4.5.8] - 2024-06-28

Fixes

  • Reduce extra flushes

[4.5.7] - 2024-06-10

Fixes

  • Clean up error message when too few arguments for num_args

[4.5.6] - 2024-06-06

[4.5.5] - 2024-06-06

Fixes

  • Allow exclusive to override required_unless_present, required_unless_present_any, required_unless_present_all

[4.5.4] - 2024-03-25

Fixes

  • (derive) Allow non-literal #[arg(id)] attributes again

[4.5.3] - 2024-03-15

Internal

  • (derive) Update heck

... (truncated)

Commits
  • d222ae4 chore: Release
  • a8abcb4 docs: Update changelog
  • 2690e1b Merge pull request #5621 from shannmu/dynamic_valuehint
  • 7fd7b3e feat(clap_complete): Support to complete custom value of argument
  • fc6aaca Merge pull request #5638 from epage/cargo
  • 631e54b docs(cookbook): Style cargo plugin
  • 6fb49d0 Merge pull request #5636 from gibfahn/styles_const
  • 6f215ee refactor(styles): make styles example use a const
  • bbb2e6f test: Add test case for completing custom value of argument
  • 999071c fix: Change visible to hidden
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap_complete&package-manager=cargo&previous-version=4.4.0&new-version=4.5.13)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2074bb1d67ae..7550aab4a9fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2805,9 +2805,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.4.0" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "586a385f7ef2f8b4d86bddaa0c094794e7ccbfe5ffef1f434fe928143fc783a5" +checksum = "aa3c596da3cf0983427b0df0dba359df9182c13bd5b519b585a482b0c351f4e8" dependencies = [ "clap 4.5.11", ] diff --git a/Cargo.toml b/Cargo.toml index 5ed5ce2f13cc..bc5b32559217 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -668,7 +668,7 @@ chrono = { version = "0.4.31" } cid = { version = "0.9.0" } clap = { version = "4.5.10" } clap-num = { version = "1.0.2" } -clap_complete = { version = "4.0.2" } +clap_complete = { version = "4.5.13" } coarsetime = { version = "0.1.22" } codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" } collectives-westend-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend" } From e44ea71e080a41f6abcf459a33484a9d91b19daf Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 23:30:02 +0800 Subject: [PATCH 216/382] Add prdoc --- prdoc/pr_5546.prdoc | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 prdoc/pr_5546.prdoc diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc new file mode 100644 index 000000000000..70cf730f840d --- /dev/null +++ b/prdoc/pr_5546.prdoc @@ -0,0 +1,30 @@ +title: "Transfer polkadot native assets to Ethereum through snowbridge" + +doc: + - audience: Runtime Dev + description: | + Transfer polkadot native asset to Ethereum through snowbridge. + +crates: + - name: snowbridge-pallet-inbound-queue + bump: patch + - name: snowbridge-pallet-inbound-queue + bump: patch + - name: snowbridge-pallet-system + bump: patch + - name: snowbridge-core + bump: patch + - name: snowbridge-router-primitives + bump: patch + - name: bridge-hub-westend-runtime + bump: patch + - name: bridge-hub-rococo-runtime + bump: patch + - name: bridge-hub-westend-emulated-chain + bump: patch + - name: bridge-hub-westend-integration-tests + bump: minor + - name: asset-hub-westend-emulated-chain + bump: patch + - name: emulated-integration-tests-common + bump: patch From 7c46b28dfc7af6681af5e088572285fa4dec0cd7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 17:35:29 +0200 Subject: [PATCH 217/382] Bump toml from 0.8.8 to 0.8.12 (#5542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [toml](https://github.com/toml-rs/toml) from 0.8.8 to 0.8.12.
Commits
  • 3a777b3 chore: Release
  • 7979905 docs: Update changelog
  • 487768d Merge pull request #703 from epage/overflow
  • 6987f77 chore(ci): Run with default opt-level
  • 21f545d fix(parser): Don't stackoverflow on opt-level=0
  • af1f97d refactor(parser): Pull recursion limit out to variable
  • eb86543 chore: Release
  • 246b292 docs: Update changelog
  • d41c62c Merge pull request #701 from epage/cleanup
  • 31457b3 refactor(error): Clean up highlight code
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=toml&package-manager=cargo&previous-version=0.8.8&new-version=0.8.12)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bastian Köcher --- Cargo.lock | 42 +++++++++++++++++++++++++++++++----------- Cargo.toml | 2 +- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7550aab4a9fc..ef6faa5faae6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5036,7 +5036,7 @@ dependencies = [ "regex", "syn 2.0.65", "termcolor", - "toml 0.8.8", + "toml 0.8.12", "walkdir", ] @@ -10357,7 +10357,7 @@ dependencies = [ "polkavm-linker 0.9.2", "sp-runtime", "tempfile", - "toml 0.8.8", + "toml 0.8.12", "twox-hash", ] @@ -11459,7 +11459,7 @@ dependencies = [ "polkavm-linker 0.10.0", "sp-runtime", "tempfile", - "toml 0.8.8", + "toml 0.8.12", ] [[package]] @@ -19130,9 +19130,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -21781,7 +21781,7 @@ dependencies = [ "sp-version", "strum 0.26.2", "tempfile", - "toml 0.8.8", + "toml 0.8.12", "walkdir", "wasm-opt", ] @@ -22520,14 +22520,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.22.12", ] [[package]] @@ -22547,7 +22547,7 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.2.3", "toml_datetime", - "winnow", + "winnow 0.5.15", ] [[package]] @@ -22555,12 +22555,23 @@ name = "toml_edit" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.2.3", + "toml_datetime", + "winnow 0.5.15", +] + +[[package]] +name = "toml_edit" +version = "0.22.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap 2.2.3", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.18", ] [[package]] @@ -24210,6 +24221,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" diff --git a/Cargo.toml b/Cargo.toml index bc5b32559217..6bd401d8e15f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1321,7 +1321,7 @@ tokio-stream = { version = "0.1.14" } tokio-test = { version = "0.4.2" } tokio-tungstenite = { version = "0.20.1" } tokio-util = { version = "0.7.8" } -toml = { version = "0.8.8" } +toml = { version = "0.8.12" } toml_edit = { version = "0.19" } tower = { version = "0.4.13" } tower-http = { version = "0.5.2" } From 071ffbe0cb995fe3538cfe7a7feaee65a40d7100 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 23:37:52 +0800 Subject: [PATCH 218/382] Fix fmt --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 6 ++---- bridges/snowbridge/primitives/router/src/outbound/mod.rs | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 00a7c0cc66fc..fd3f71390bbe 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -168,8 +168,7 @@ impl< ConvertAssetId, UniversalLocation, GlobalAssetHubLocation, - > -where + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, @@ -227,8 +226,7 @@ impl< ConvertAssetId, UniversalLocation, GlobalAssetHubLocation, - > -where + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index 4963062f4b4a..1ad8be72bcbc 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -44,8 +44,7 @@ impl -where + > where UniversalLocation: Get, EthereumNetwork: Get, OutboundQueue: SendMessage, From 22100999a6edbc67ae54b4cd0e0242505226b083 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 2 Sep 2024 17:53:49 +0200 Subject: [PATCH 219/382] [bridges-v2] Permissionless lanes (#4949) Relates to: https://github.com/paritytech/parity-bridges-common/issues/2451 Closes: https://github.com/paritytech/parity-bridges-common/issues/2500 ## Summary Now, the bridging pallet supports only static lanes, which means lanes that are hard-coded in the runtime files. This PR fixes that and adds support for dynamic, also known as permissionless, lanes. This means that allowed origins (relay chain, sibling parachains) can open and close bridges (through BridgeHubs) with another bridged (substrate-like) consensus using just `xcm::Transact` and `OriginKind::Xcm`. _This PR is based on the migrated code from the Bridges V2 [branch](https://github.com/paritytech/polkadot-sdk/pull/4427) from the old `parity-bridges-common` [repo](https://github.com/paritytech/parity-bridges-common/tree/bridges-v2)._ ## Explanation Please read [bridges/modules/xcm-bridge-hub/src/lib.rs](https://github.com/paritytech/polkadot-sdk/blob/149b0ac2ce43fba197988f2642032fa24dd8289a/bridges/modules/xcm-bridge-hub/src/lib.rs#L17-L136) to understand how managing bridges works. The basic concepts around `BridgeId` and `LaneId` are also explained there. ## TODO - [x] search and fix for comment: `// TODO:(bridges-v2) - most of that stuff was introduced with free header execution: https://github.com/paritytech/polkadot-sdk/pull/4102` - more info in the comment [bellow](https://github.com/paritytech/polkadot-sdk/pull/4427#issuecomment-2126625043) - [x] TODO: there's only one impl of `EnsureOrigin` ## TODO - not blocking review **benchmarking:** - [x] regenerate all relevant weights for BH/AH runtimes - [ ] regenerate default weights for bridging pallets e.g. `modules/messages/src/weights.rs` - [ ] add benchmarks for `xcm-bridge-hub` pallet https://github.com/paritytech/polkadot-sdk/issues/5550 **testing:** - [ ] add xcm-emulator tests for Rococo/Penpal to Westend/Penpal with full opening channel and sending/receiving `xcm::Transact` **migrations:** - [x] add migrations for BridgeHubRococo/Westend https://github.com/paritytech/parity-bridges-common/issues/2794 (to be reusable for P/K bridge) - [x] check also storage migration, if needed for pallets - [ ] migration for XCM type (optional) - [x] migration for static lanes to the dynamic (reuse for fellows) **investigation:** - [ ] revisit https://github.com/paritytech/parity-bridges-common/issues/2380 - [ ] check congestion around `LocalXcmChannelManager` and `OutboundLanesCongestedSignals` impls - https://github.com/paritytech/polkadot-sdk/issues/5551 - to be reusable for polkadot-fellows - return `report_bridge_status` was remove, so we need to `XcmpQueue` alternative? --------- Signed-off-by: Branislav Kontur Co-authored-by: Svyatoslav Nikolsky Co-authored-by: command-bot <> Co-authored-by: Francisco Aguirre --- Cargo.lock | 44 +- bridges/bin/runtime-common/Cargo.toml | 17 +- ...ck_obsolete_extension.rs => extensions.rs} | 209 +- .../bin/runtime-common/src/extensions/mod.rs | 21 - bridges/bin/runtime-common/src/integrity.rs | 18 +- bridges/bin/runtime-common/src/lib.rs | 5 - .../src/messages_xcm_extension.rs | 502 ----- bridges/bin/runtime-common/src/mock.rs | 22 +- .../src/parachains_benchmarking.rs | 5 +- .../chains/chain-bridge-hub-rococo/Cargo.toml | 7 +- .../chains/chain-bridge-hub-rococo/src/lib.rs | 15 +- .../chain-bridge-hub-westend/Cargo.toml | 6 +- .../chain-bridge-hub-westend/src/lib.rs | 15 +- .../docs/polkadot-kusama-bridge-overview.md | 18 +- bridges/modules/grandpa/src/call_ext.rs | 46 +- bridges/modules/grandpa/src/lib.rs | 2 +- bridges/modules/messages/Cargo.toml | 5 +- bridges/modules/messages/README.md | 14 +- bridges/modules/messages/src/benchmarking.rs | 67 +- .../messages/src/call_ext.rs} | 356 ++-- bridges/modules/messages/src/inbound_lane.rs | 101 +- bridges/modules/messages/src/lanes_manager.rs | 283 +++ bridges/modules/messages/src/lib.rs | 398 ++-- bridges/modules/messages/src/migration.rs | 146 ++ bridges/modules/messages/src/outbound_lane.rs | 58 +- bridges/modules/messages/src/proofs.rs | 36 +- bridges/modules/messages/src/tests/mock.rs | 77 +- .../messages/src/tests/pallet_tests.rs | 372 +++- bridges/modules/parachains/src/call_ext.rs | 20 +- bridges/modules/parachains/src/lib.rs | 15 +- bridges/modules/relayers/Cargo.toml | 35 +- bridges/modules/relayers/src/benchmarking.rs | 6 +- .../relayers/src/extension/grandpa_adapter.rs | 177 ++ .../src/extension/messages_adapter.rs | 94 + .../relayers/src/extension/mod.rs} | 1517 +++++---------- .../src/extension/parachain_adapter.rs | 182 ++ .../relayers/src/extension/priority.rs} | 33 +- bridges/modules/relayers/src/lib.rs | 45 +- bridges/modules/relayers/src/mock.rs | 292 ++- .../modules/relayers/src/payment_adapter.rs | 22 +- bridges/modules/relayers/src/stake_adapter.rs | 6 +- .../modules/xcm-bridge-hub-router/Cargo.toml | 3 - .../xcm-bridge-hub-router/src/benchmarking.rs | 43 +- .../modules/xcm-bridge-hub-router/src/lib.rs | 342 ++-- .../modules/xcm-bridge-hub-router/src/mock.rs | 35 +- .../xcm-bridge-hub-router/src/weights.rs | 133 +- bridges/modules/xcm-bridge-hub/Cargo.toml | 17 +- .../modules/xcm-bridge-hub/src/dispatcher.rs | 267 +++ .../modules/xcm-bridge-hub/src/exporter.rs | 554 +++++- bridges/modules/xcm-bridge-hub/src/lib.rs | 1677 ++++++++++++++++- .../modules/xcm-bridge-hub/src/migration.rs | 146 ++ bridges/modules/xcm-bridge-hub/src/mock.rs | 244 ++- .../primitives/header-chain/src/call_info.rs | 94 + bridges/primitives/header-chain/src/lib.rs | 37 +- bridges/primitives/messages/Cargo.toml | 5 +- bridges/primitives/messages/src/call_info.rs | 172 ++ bridges/primitives/messages/src/lane.rs | 281 +++ bridges/primitives/messages/src/lib.rs | 116 +- .../primitives/messages/src/storage_keys.rs | 38 +- .../primitives/messages/src/target_chain.rs | 8 +- .../primitives/parachains/src/call_info.rs | 59 + bridges/primitives/parachains/src/lib.rs | 31 +- bridges/primitives/polkadot-core/Cargo.toml | 4 +- bridges/primitives/relayers/Cargo.toml | 10 +- bridges/primitives/relayers/src/extension.rs | 191 ++ bridges/primitives/relayers/src/lib.rs | 28 +- bridges/primitives/runtime/Cargo.toml | 1 - .../primitives/runtime/src/storage_proof.rs | 4 +- .../xcm-bridge-hub-router/Cargo.toml | 11 +- .../xcm-bridge-hub-router/src/lib.rs | 5 +- bridges/primitives/xcm-bridge-hub/Cargo.toml | 26 +- .../xcm-bridge-hub/src/call_info.rs | 43 + bridges/primitives/xcm-bridge-hub/src/lib.rs | 672 +++++++ bridges/relays/client-substrate/Cargo.toml | 8 +- bridges/relays/lib-substrate-relay/Cargo.toml | 1 + .../lib-substrate-relay/src/cli/bridge.rs | 2 +- .../relays/lib-substrate-relay/src/cli/mod.rs | 97 +- .../src/cli/relay_headers_and_messages/mod.rs | 8 +- .../parachain_to_parachain.rs | 2 +- .../relay_to_parachain.rs | 2 +- .../src/cli/relay_messages.rs | 12 +- .../lib-substrate-relay/src/messages/mod.rs | 7 +- .../src/messages/source.rs | 2 +- .../src/on_demand/parachains.rs | 2 +- .../lib-substrate-relay/src/parachains/mod.rs | 6 +- .../relays/messages/src/message_lane_loop.rs | 12 +- .../relays/parachains/src/parachains_loop.rs | 2 +- cumulus/pallets/xcmp-queue/src/bridging.rs | 50 +- .../assets/asset-hub-rococo/Cargo.toml | 3 + .../assets/asset-hub-rococo/src/lib.rs | 10 +- .../assets/asset-hub-westend/Cargo.toml | 3 + .../assets/asset-hub-westend/src/lib.rs | 10 +- .../emulated/common/Cargo.toml | 2 + .../emulated/common/src/impls.rs | 111 +- .../bridges/bridge-hub-rococo/Cargo.toml | 3 +- .../src/tests/asset_transfers.rs | 3 + .../bridge-hub-rococo/src/tests/mod.rs | 59 +- .../bridge-hub-rococo/src/tests/send_xcm.rs | 3 + .../bridges/bridge-hub-westend/Cargo.toml | 3 +- .../src/tests/asset_transfers.rs | 3 + .../bridge-hub-westend/src/tests/mod.rs | 59 +- .../bridge-hub-westend/src/tests/send_xcm.rs | 3 + .../assets/asset-hub-rococo/src/lib.rs | 22 +- .../weights/pallet_xcm_bridge_hub_router.rs | 64 +- .../xcm/pallet_xcm_benchmarks_fungible.rs | 38 +- .../xcm/pallet_xcm_benchmarks_generic.rs | 122 +- .../assets/asset-hub-rococo/src/xcm_config.rs | 11 - .../assets/asset-hub-rococo/tests/tests.rs | 90 +- .../assets/asset-hub-westend/src/lib.rs | 24 +- .../weights/pallet_xcm_bridge_hub_router.rs | 64 +- .../xcm/pallet_xcm_benchmarks_fungible.rs | 38 +- .../xcm/pallet_xcm_benchmarks_generic.rs | 147 +- .../asset-hub-westend/src/xcm_config.rs | 11 - .../assets/asset-hub-westend/tests/tests.rs | 88 +- .../test-utils/src/test_cases_over_bridge.rs | 11 +- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 7 +- .../src/bridge_to_bulletin_config.rs | 192 +- .../src/bridge_to_westend_config.rs | 227 ++- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 81 +- .../src/weights/pallet_bridge_grandpa.rs | 20 +- ...idge_messages_rococo_to_rococo_bulletin.rs | 166 +- ...allet_bridge_messages_rococo_to_westend.rs | 184 +- .../src/weights/pallet_bridge_parachains.rs | 20 +- .../src/weights/pallet_bridge_relayers.rs | 34 +- .../xcm/pallet_xcm_benchmarks_generic.rs | 136 +- .../bridge-hub-rococo/src/xcm_config.rs | 128 +- .../bridge-hub-rococo/tests/tests.rs | 142 +- .../bridge-hubs/bridge-hub-westend/Cargo.toml | 3 +- .../src/bridge_to_rococo_config.rs | 228 ++- .../bridge-hubs/bridge-hub-westend/src/lib.rs | 69 +- .../src/weights/pallet_bridge_grandpa.rs | 20 +- .../src/weights/pallet_bridge_messages.rs | 184 +- .../src/weights/pallet_bridge_parachains.rs | 20 +- .../src/weights/pallet_bridge_relayers.rs | 42 +- .../xcm/pallet_xcm_benchmarks_generic.rs | 136 +- .../bridge-hub-westend/src/xcm_config.rs | 8 +- .../bridge-hub-westend/tests/snowbridge.rs | 4 +- .../bridge-hub-westend/tests/tests.rs | 85 +- .../runtimes/bridge-hubs/common/Cargo.toml | 2 + .../runtimes/bridge-hubs/common/src/lib.rs | 1 + .../bridge-hubs/common/src/xcm_version.rs | 44 + .../bridge-hubs/test-utils/Cargo.toml | 6 + .../bridge-hubs/test-utils/src/lib.rs | 1 + .../src/test_cases/from_grandpa_chain.rs | 10 +- .../src/test_cases/from_parachain.rs | 10 +- .../test-utils/src/test_cases/helpers.rs | 145 +- .../test-utils/src/test_cases/mod.rs | 164 +- .../src/test_data/from_grandpa_chain.rs | 7 +- .../src/test_data/from_parachain.rs | 7 +- .../parachains/runtimes/test-utils/src/lib.rs | 35 +- cumulus/xcm/xcm-emulator/src/lib.rs | 14 +- prdoc/pr_4949.prdoc | 78 + 152 files changed, 9725 insertions(+), 4759 deletions(-) rename bridges/bin/runtime-common/src/{extensions/check_obsolete_extension.rs => extensions.rs} (76%) delete mode 100644 bridges/bin/runtime-common/src/extensions/mod.rs delete mode 100644 bridges/bin/runtime-common/src/messages_xcm_extension.rs rename bridges/{bin/runtime-common/src/messages_call_ext.rs => modules/messages/src/call_ext.rs} (57%) create mode 100644 bridges/modules/messages/src/lanes_manager.rs create mode 100644 bridges/modules/messages/src/migration.rs create mode 100644 bridges/modules/relayers/src/extension/grandpa_adapter.rs create mode 100644 bridges/modules/relayers/src/extension/messages_adapter.rs rename bridges/{bin/runtime-common/src/extensions/refund_relayer_extension.rs => modules/relayers/src/extension/mod.rs} (60%) create mode 100644 bridges/modules/relayers/src/extension/parachain_adapter.rs rename bridges/{bin/runtime-common/src/extensions/priority_calculator.rs => modules/relayers/src/extension/priority.rs} (96%) create mode 100644 bridges/modules/xcm-bridge-hub/src/dispatcher.rs create mode 100644 bridges/modules/xcm-bridge-hub/src/migration.rs create mode 100644 bridges/primitives/header-chain/src/call_info.rs create mode 100644 bridges/primitives/messages/src/call_info.rs create mode 100644 bridges/primitives/messages/src/lane.rs create mode 100644 bridges/primitives/parachains/src/call_info.rs create mode 100644 bridges/primitives/relayers/src/extension.rs create mode 100644 bridges/primitives/xcm-bridge-hub/src/call_info.rs create mode 100644 cumulus/parachains/runtimes/bridge-hubs/common/src/xcm_version.rs create mode 100644 prdoc/pr_4949.prdoc diff --git a/Cargo.lock b/Cargo.lock index ef6faa5faae6..7634cbc166a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -790,6 +790,7 @@ name = "asset-hub-rococo-emulated-chain" version = "0.0.0" dependencies = [ "asset-hub-rococo-runtime", + "bp-bridge-hub-rococo", "cumulus-primitives-core", "emulated-integration-tests-common", "frame-support", @@ -916,6 +917,7 @@ name = "asset-hub-westend-emulated-chain" version = "0.0.0" dependencies = [ "asset-hub-westend-runtime", + "bp-bridge-hub-westend", "cumulus-primitives-core", "emulated-integration-tests-common", "frame-support", @@ -1787,7 +1789,9 @@ dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", "bp-runtime", + "bp-xcm-bridge-hub", "frame-support", + "parity-scale-codec", "sp-api", "sp-runtime", "sp-std 14.0.0", @@ -1800,7 +1804,9 @@ dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", "bp-runtime", + "bp-xcm-bridge-hub", "frame-support", + "parity-scale-codec", "sp-api", "sp-runtime", "sp-std 14.0.0", @@ -1850,6 +1856,7 @@ dependencies = [ "scale-info", "serde", "sp-core", + "sp-io", "sp-std 14.0.0", ] @@ -1920,11 +1927,15 @@ dependencies = [ name = "bp-relayers" version = "0.7.0" dependencies = [ + "bp-header-chain", "bp-messages", + "bp-parachains", "bp-runtime", "frame-support", + "frame-system", "hex", "hex-literal", + "pallet-utility", "parity-scale-codec", "scale-info", "sp-runtime", @@ -2001,7 +2012,16 @@ dependencies = [ name = "bp-xcm-bridge-hub" version = "0.2.0" dependencies = [ + "bp-messages", + "bp-runtime", + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", "sp-std 14.0.0", + "staging-xcm", ] [[package]] @@ -2012,6 +2032,7 @@ dependencies = [ "scale-info", "sp-core", "sp-runtime", + "staging-xcm", ] [[package]] @@ -2026,6 +2047,7 @@ dependencies = [ "snowbridge-core", "sp-core", "sp-runtime", + "sp-std 14.0.0", "staging-xcm", ] @@ -2056,6 +2078,7 @@ dependencies = [ "pallet-bridge-messages", "pallet-message-queue", "pallet-xcm", + "pallet-xcm-bridge-hub", "parachains-common", "parity-scale-codec", "rococo-system-emulated-network", @@ -2180,10 +2203,12 @@ dependencies = [ "asset-test-utils", "bp-header-chain", "bp-messages", + "bp-parachains", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", + "bp-xcm-bridge-hub", "bridge-runtime-common", "cumulus-pallet-parachain-system", "cumulus-pallet-xcmp-queue", @@ -2198,6 +2223,7 @@ dependencies = [ "pallet-bridge-relayers", "pallet-timestamp", "pallet-utility", + "pallet-xcm-bridge-hub", "parachains-common", "parachains-runtimes-test-utils", "parity-scale-codec", @@ -2241,6 +2267,7 @@ dependencies = [ "pallet-bridge-messages", "pallet-message-queue", "pallet-xcm", + "pallet-xcm-bridge-hub", "parachains-common", "parity-scale-codec", "rococo-westend-system-emulated-network", @@ -2368,7 +2395,6 @@ dependencies = [ "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", - "bp-xcm-bridge-hub-router", "frame-support", "frame-system", "log", @@ -2381,12 +2407,12 @@ dependencies = [ "pallet-utility", "parity-scale-codec", "scale-info", + "sp-core", "sp-io", "sp-runtime", "sp-std 14.0.0", "sp-trie", "staging-xcm", - "staging-xcm-builder", "static_assertions", "tuplex", ] @@ -5178,6 +5204,7 @@ version = "3.0.0" dependencies = [ "asset-test-utils", "bp-messages", + "bp-xcm-bridge-hub", "bridge-runtime-common", "cumulus-pallet-parachain-system", "cumulus-pallet-xcmp-queue", @@ -5188,6 +5215,7 @@ dependencies = [ "pallet-bridge-messages", "pallet-message-queue", "pallet-xcm", + "pallet-xcm-bridge-hub", "parachains-common", "parity-scale-codec", "paste", @@ -10194,18 +10222,27 @@ dependencies = [ name = "pallet-bridge-relayers" version = "0.7.0" dependencies = [ + "bp-header-chain", "bp-messages", + "bp-parachains", + "bp-polkadot-core", "bp-relayers", "bp-runtime", + "bp-test-utils", "frame-benchmarking", "frame-support", "frame-system", "log", "pallet-balances", + "pallet-bridge-grandpa", "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-transaction-payment", + "pallet-utility", "parity-scale-codec", "scale-info", "sp-arithmetic", + "sp-core", "sp-io", "sp-runtime", "sp-std 14.0.0", @@ -12100,7 +12137,6 @@ dependencies = [ "bp-messages", "bp-runtime", "bp-xcm-bridge-hub", - "bridge-runtime-common", "frame-support", "frame-system", "log", @@ -12108,6 +12144,7 @@ dependencies = [ "pallet-bridge-messages", "pallet-xcm-bridge-hub-router", "parity-scale-codec", + "polkadot-parachain-primitives", "scale-info", "sp-core", "sp-io", @@ -21597,6 +21634,7 @@ dependencies = [ "rbtag", "relay-substrate-client", "relay-utils", + "rustc-hex", "scale-info", "sp-consensus-grandpa", "sp-core", diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index 36f27b6aa035..b8835d55f0da 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -25,7 +25,6 @@ bp-polkadot-core = { workspace = true } bp-relayers = { workspace = true } bp-runtime = { workspace = true } bp-xcm-bridge-hub = { workspace = true } -bp-xcm-bridge-hub-router = { workspace = true } pallet-bridge-grandpa = { workspace = true } pallet-bridge-messages = { workspace = true } pallet-bridge-parachains = { workspace = true } @@ -43,12 +42,15 @@ sp-trie = { optional = true, workspace = true } # Polkadot dependencies xcm = { workspace = true } -xcm-builder = { workspace = true } [dev-dependencies] bp-test-utils = { workspace = true } pallet-balances = { workspace = true } -pallet-bridge-messages = { features = ["std", "test-helpers"], workspace = true } +pallet-bridge-messages = { features = [ + "std", + "test-helpers", +], workspace = true } +sp-core = { workspace = true } [features] default = ["std"] @@ -60,7 +62,6 @@ std = [ "bp-relayers/std", "bp-runtime/std", "bp-test-utils/std", - "bp-xcm-bridge-hub-router/std", "bp-xcm-bridge-hub/std", "codec/std", "frame-support/std", @@ -74,12 +75,12 @@ std = [ "pallet-transaction-payment/std", "pallet-utility/std", "scale-info/std", + "sp-core/std", "sp-io/std", "sp-runtime/std", "sp-std/std", "sp-trie/std", "tuplex/std", - "xcm-builder/std", "xcm/std", ] runtime-benchmarks = [ @@ -95,10 +96,6 @@ runtime-benchmarks = [ "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-trie", - "xcm-builder/runtime-benchmarks", ] integrity-test = ["static_assertions"] -test-helpers = [ - "bp-runtime/test-helpers", - "sp-trie", -] +test-helpers = ["bp-runtime/test-helpers", "sp-trie"] diff --git a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs b/bridges/bin/runtime-common/src/extensions.rs similarity index 76% rename from bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs rename to bridges/bin/runtime-common/src/extensions.rs index df75092af6e8..dc7e14de28f3 100644 --- a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs +++ b/bridges/bin/runtime-common/src/extensions.rs @@ -18,23 +18,20 @@ //! obsolete (duplicated) data or do not pass some additional pallet-specific //! checks. -use crate::{ - extensions::refund_relayer_extension::RefundableParachainId, - messages_call_ext::MessagesCallSubType, -}; +use bp_parachains::SubmitParachainHeadsInfo; use bp_relayers::ExplicitOrAccountParams; use bp_runtime::Parachain; use pallet_bridge_grandpa::{ BridgedBlockNumber, CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper, }; -use pallet_bridge_parachains::{ - CallSubType as ParachainsCallSubtype, SubmitParachainHeadsHelper, SubmitParachainHeadsInfo, -}; +use pallet_bridge_messages::CallSubType as MessagesCallSubType; +use pallet_bridge_parachains::{CallSubType as ParachainsCallSubtype, SubmitParachainHeadsHelper}; use pallet_bridge_relayers::Pallet as RelayersPallet; use sp_runtime::{ - traits::{Get, PhantomData, UniqueSaturatedInto}, + traits::{Get, UniqueSaturatedInto}, transaction_validity::{TransactionPriority, TransactionValidity, ValidTransactionBuilder}, }; +use sp_std::marker::PhantomData; // Re-export to avoid include tuplex dependency everywhere. #[doc(hidden)] @@ -126,17 +123,27 @@ where /// `(BundledHeaderNumber - 1 - BestKnownHeaderNumber) * Priority::get()`. /// The boost is only applied if submitter has active registration in the relayers /// pallet. -pub struct CheckAndBoostBridgeParachainsTransactions( - PhantomData<(T, RefPara, Priority, SlashAccount)>, -); - -impl, SlashAccount: Get> - BridgeRuntimeFilterCall - for CheckAndBoostBridgeParachainsTransactions +pub struct CheckAndBoostBridgeParachainsTransactions< + T, + ParachainsInstance, + Para, + Priority, + SlashAccount, +>(PhantomData<(T, ParachainsInstance, Para, Priority, SlashAccount)>); + +impl< + T, + ParachainsInstance, + Para, + Priority: Get, + SlashAccount: Get, + > BridgeRuntimeFilterCall + for CheckAndBoostBridgeParachainsTransactions where - T: pallet_bridge_relayers::Config + pallet_bridge_parachains::Config, - RefPara: RefundableParachainId, - T::RuntimeCall: ParachainsCallSubtype, + T: pallet_bridge_relayers::Config + pallet_bridge_parachains::Config, + ParachainsInstance: 'static, + Para: Parachain, + T::RuntimeCall: ParachainsCallSubtype, { // bridged header number, bundled in transaction type ToPostDispatch = Option; @@ -145,10 +152,10 @@ where who: &T::AccountId, call: &T::RuntimeCall, ) -> (Self::ToPostDispatch, TransactionValidity) { - match ParachainsCallSubtype::::check_obsolete_submit_parachain_heads( + match ParachainsCallSubtype::::check_obsolete_submit_parachain_heads( call, ) { - Ok(Some(our_tx)) if our_tx.base.para_id.0 == RefPara::BridgedChain::PARACHAIN_ID => { + Ok(Some(our_tx)) if our_tx.base.para_id.0 == Para::PARACHAIN_ID => { let to_post_dispatch = Some(our_tx.base); let total_priority_boost = compute_priority_boost::(&who, our_tx.improved_by); @@ -167,7 +174,7 @@ where let Some(update) = maybe_update else { return }; // we are only interested in failed or unneeded transactions let has_failed = has_failed || - !SubmitParachainHeadsHelper::::was_successful(&update); + !SubmitParachainHeadsHelper::::was_successful(&update); if !has_failed { return @@ -275,7 +282,7 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { type Pre = ( $account_id, ( $( - <$filter_call as $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + <$filter_call as $crate::extensions::BridgeRuntimeFilterCall< $account_id, $call, >>::ToPostDispatch, @@ -302,7 +309,7 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { $( let (from_validate, call_filter_validity) = < $filter_call as - $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + $crate::extensions::BridgeRuntimeFilterCall< Self::AccountId, $call, >>::validate(&who, call); @@ -319,12 +326,13 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { info: &sp_runtime::traits::DispatchInfoOf, len: usize, ) -> Result { - use $crate::extensions::check_obsolete_extension::__private::tuplex::PushBack; + use $crate::extensions::__private::tuplex::PushBack; + let to_post_dispatch = (); $( let (from_validate, call_filter_validity) = < $filter_call as - $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + $crate::extensions::BridgeRuntimeFilterCall< $account_id, $call, >>::validate(&relayer, call); @@ -342,14 +350,15 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { len: usize, result: &sp_runtime::DispatchResult, ) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> { - use $crate::extensions::check_obsolete_extension::__private::tuplex::PopFront; + use $crate::extensions::__private::tuplex::PopFront; + let Some((relayer, to_post_dispatch)) = to_post_dispatch else { return Ok(()) }; let has_failed = result.is_err(); $( let (item, to_post_dispatch) = to_post_dispatch.pop_front(); < $filter_call as - $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + $crate::extensions::BridgeRuntimeFilterCall< $account_id, $call, >>::post_dispatch(&relayer, has_failed, item); @@ -363,25 +372,37 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { #[cfg(test)] mod tests { use super::*; - use crate::{ - extensions::refund_relayer_extension::{ - tests::{ - initialize_environment, relayer_account_at_this_chain, - submit_parachain_head_call_ex, submit_relay_header_call_ex, - }, - RefundableParachain, - }, - mock::*, - }; - use bp_polkadot_core::parachains::ParaId; + use crate::mock::*; + use bp_header_chain::StoredHeaderDataBuilder; + use bp_messages::{InboundLaneData, LaneId, MessageNonce, OutboundLaneData}; + use bp_parachains::{BestParaHeadHash, ParaInfo}; + use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; + use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::HeaderId; - use frame_support::{assert_err, assert_ok}; + use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID}; + use frame_support::{assert_err, assert_ok, traits::fungible::Mutate}; + use pallet_bridge_grandpa::{Call as GrandpaCall, StoredAuthoritySet}; + use pallet_bridge_parachains::Call as ParachainsCall; use sp_runtime::{ - traits::{ConstU64, SignedExtension}, + traits::{parameter_types, ConstU64, Header as _, SignedExtension}, transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, DispatchError, }; + parameter_types! { + pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( + test_lane_id(), + TEST_BRIDGED_CHAIN_ID, + RewardsAccountOwner::ThisChain, + ); + pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( + test_lane_id(), + TEST_BRIDGED_CHAIN_ID, + RewardsAccountOwner::BridgedChain, + ); + pub TestLaneId: LaneId = test_lane_id(); + } + pub struct MockCall { data: u32, } @@ -455,6 +476,103 @@ mod tests { } } + fn test_lane_id() -> LaneId { + LaneId::new(1, 2) + } + + fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { + let test_stake: ThisChainBalance = TestStake::get(); + ExistentialDeposit::get().saturating_add(test_stake * 100) + } + + // in tests, the following accounts are equal (because of how `into_sub_account_truncating` + // works) + + fn delivery_rewards_account() -> ThisChainAccountId { + TestPaymentProcedure::rewards_account(MsgProofsRewardsAccount::get()) + } + + fn confirmation_rewards_account() -> ThisChainAccountId { + TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get()) + } + + fn relayer_account_at_this_chain() -> ThisChainAccountId { + 0 + } + + fn initialize_environment( + best_relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, + best_message: MessageNonce, + ) { + let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); + let best_relay_header = HeaderId(best_relay_header_number, BridgedChainHash::default()); + pallet_bridge_grandpa::CurrentAuthoritySet::::put( + StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(), + ); + pallet_bridge_grandpa::BestFinalized::::put(best_relay_header); + pallet_bridge_grandpa::ImportedHeaders::::insert( + best_relay_header.hash(), + bp_test_utils::test_header::(0).build(), + ); + + let para_id = ParaId(BridgedUnderlyingParachain::PARACHAIN_ID); + let para_info = ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: parachain_head_at_relay_header_number, + head_hash: [parachain_head_at_relay_header_number as u8; 32].into(), + }, + next_imported_hash_position: 0, + }; + pallet_bridge_parachains::ParasInfo::::insert(para_id, para_info); + + let lane_id = test_lane_id(); + let in_lane_data = + InboundLaneData { last_confirmed_nonce: best_message, ..Default::default() }; + pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); + + let out_lane_data = + OutboundLaneData { latest_received_nonce: best_message, ..Default::default() }; + pallet_bridge_messages::OutboundLanes::::insert(lane_id, out_lane_data); + + Balances::mint_into(&delivery_rewards_account(), ExistentialDeposit::get()).unwrap(); + Balances::mint_into(&confirmation_rewards_account(), ExistentialDeposit::get()).unwrap(); + Balances::mint_into( + &relayer_account_at_this_chain(), + initial_balance_of_relayer_account_at_this_chain(), + ) + .unwrap(); + } + + fn submit_relay_header_call(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall { + let relay_header = BridgedChainHeader::new( + relay_header_number, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ); + let relay_justification = make_default_justification(&relay_header); + + RuntimeCall::BridgeGrandpa(GrandpaCall::submit_finality_proof { + finality_target: Box::new(relay_header), + justification: relay_justification, + }) + } + + fn submit_parachain_head_call( + parachain_head_at_relay_header_number: BridgedChainBlockNumber, + ) -> RuntimeCall { + RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { + at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()), + parachains: vec![( + ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + [parachain_head_at_relay_header_number as u8; 32].into(), + )], + parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, + }) + } + #[test] fn test_generated_obsolete_extension() { generate_bridge_reject_obsolete_headers_and_messages!( @@ -546,7 +664,7 @@ mod tests { let priority_boost = BridgeGrandpaWrapper::validate( &relayer_account_at_this_chain(), - &submit_relay_header_call_ex(200), + &submit_relay_header_call(200), ) .1 .unwrap() @@ -564,7 +682,7 @@ mod tests { let priority_boost = BridgeGrandpaWrapper::validate( &relayer_account_at_this_chain(), - &submit_relay_header_call_ex(200), + &submit_relay_header_call(200), ) .1 .unwrap() @@ -601,7 +719,8 @@ mod tests { type BridgeParachainsWrapper = CheckAndBoostBridgeParachainsTransactions< TestRuntime, - RefundableParachain<(), BridgedUnderlyingParachain>, + (), + BridgedUnderlyingParachain, ConstU64<1_000>, SlashDestination, >; @@ -613,7 +732,7 @@ mod tests { let priority_boost = BridgeParachainsWrapper::validate( &relayer_account_at_this_chain(), - &submit_parachain_head_call_ex(200), + &submit_parachain_head_call(200), ) .1 .unwrap() @@ -631,7 +750,7 @@ mod tests { let priority_boost = BridgeParachainsWrapper::validate( &relayer_account_at_this_chain(), - &submit_parachain_head_call_ex(200), + &submit_parachain_head_call(200), ) .1 .unwrap() diff --git a/bridges/bin/runtime-common/src/extensions/mod.rs b/bridges/bin/runtime-common/src/extensions/mod.rs deleted file mode 100644 index 3f1b506aaae3..000000000000 --- a/bridges/bin/runtime-common/src/extensions/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Bridge-specific transaction extensions. - -pub mod check_obsolete_extension; -pub mod priority_calculator; -pub mod refund_relayer_extension; diff --git a/bridges/bin/runtime-common/src/integrity.rs b/bridges/bin/runtime-common/src/integrity.rs index a0a9367dd140..2ff6c4c9165a 100644 --- a/bridges/bin/runtime-common/src/integrity.rs +++ b/bridges/bin/runtime-common/src/integrity.rs @@ -68,21 +68,21 @@ macro_rules! assert_bridge_messages_pallet_types( bridged_chain: $bridged:path, ) => { { - use $crate::messages_xcm_extension::XcmAsPlainPayload; + use $crate::integrity::__private::bp_xcm_bridge_hub::XcmAsPlainPayload; use $crate::integrity::__private::static_assertions::assert_type_eq_all; use bp_messages::ChainWithMessages; use bp_runtime::Chain; - use pallet_bridge_messages::Config as MessagesConfig; + use pallet_bridge_messages::Config as BridgeMessagesConfig; // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard // configuration is used), or something has broke existing configuration (meaning that all bridged chains // and relays will stop functioning) - assert_type_eq_all!(<$r as MessagesConfig<$i>>::ThisChain, $this); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::BridgedChain, $bridged); + assert_type_eq_all!(<$r as BridgeMessagesConfig<$i>>::ThisChain, $this); + assert_type_eq_all!(<$r as BridgeMessagesConfig<$i>>::BridgedChain, $bridged); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, XcmAsPlainPayload); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, XcmAsPlainPayload); + assert_type_eq_all!(<$r as BridgeMessagesConfig<$i>>::OutboundPayload, XcmAsPlainPayload); + assert_type_eq_all!(<$r as BridgeMessagesConfig<$i>>::InboundPayload, XcmAsPlainPayload); } } ); @@ -174,12 +174,6 @@ where R: pallet_bridge_messages::Config, MI: 'static, { - assert!( - !R::ActiveOutboundLanes::get().is_empty(), - "ActiveOutboundLanes ({:?}) must not be empty", - R::ActiveOutboundLanes::get(), - ); - assert!( pallet_bridge_messages::BridgedChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX <= pallet_bridge_messages::BridgedChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, diff --git a/bridges/bin/runtime-common/src/lib.rs b/bridges/bin/runtime-common/src/lib.rs index b65bb6041d56..ac8b013086b1 100644 --- a/bridges/bin/runtime-common/src/lib.rs +++ b/bridges/bin/runtime-common/src/lib.rs @@ -20,16 +20,11 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod extensions; - pub mod messages_api; pub mod messages_benchmarking; -pub mod messages_call_ext; -pub mod messages_xcm_extension; pub mod parachains_benchmarking; mod mock; #[cfg(feature = "integrity-test")] pub mod integrity; - -const LOG_TARGET_BRIDGE_DISPATCH: &str = "runtime::bridge-dispatch"; diff --git a/bridges/bin/runtime-common/src/messages_xcm_extension.rs b/bridges/bin/runtime-common/src/messages_xcm_extension.rs deleted file mode 100644 index 46ed4da0d854..000000000000 --- a/bridges/bin/runtime-common/src/messages_xcm_extension.rs +++ /dev/null @@ -1,502 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Module provides utilities for easier XCM handling, e.g: -//! `XcmExecutor` -> `MessageSender` -> `OutboundMessageQueue` -//! | -//! `Relayer` -//! | -//! `XcmRouter` <- `MessageDispatch` <- `InboundMessageQueue` - -use bp_messages::{ - source_chain::OnMessagesDelivered, - target_chain::{DispatchMessage, MessageDispatch}, - LaneId, MessageNonce, -}; -use bp_runtime::messages::MessageDispatchResult; -pub use bp_xcm_bridge_hub::XcmAsPlainPayload; -use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; -use codec::{Decode, Encode}; -use frame_support::{traits::Get, weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; -use pallet_bridge_messages::{ - Config as MessagesConfig, OutboundLanesCongestedSignals, WeightInfoExt as MessagesPalletWeights, -}; -use scale_info::TypeInfo; -use sp_runtime::SaturatedConversion; -use sp_std::{fmt::Debug, marker::PhantomData}; -use xcm::prelude::*; -use xcm_builder::{DispatchBlob, DispatchBlobError}; - -/// Message dispatch result type for single message. -#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] -pub enum XcmBlobMessageDispatchResult { - /// We've been unable to decode message payload. - InvalidPayload, - /// Message has been dispatched. - Dispatched, - /// Message has **NOT** been dispatched because of given error. - NotDispatched(#[codec(skip)] Option), -} - -/// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages -/// -/// It needs to be used at the target bridge hub. -pub struct XcmBlobMessageDispatch { - _marker: sp_std::marker::PhantomData<(DispatchBlob, Weights, Channel)>, -} - -impl< - BlobDispatcher: DispatchBlob, - Weights: MessagesPalletWeights, - Channel: XcmChannelStatusProvider, - > MessageDispatch for XcmBlobMessageDispatch -{ - type DispatchPayload = XcmAsPlainPayload; - type DispatchLevelResult = XcmBlobMessageDispatchResult; - - fn is_active() -> bool { - !Channel::is_congested() - } - - fn dispatch_weight(message: &mut DispatchMessage) -> Weight { - match message.data.payload { - Ok(ref payload) => { - let payload_size = payload.encoded_size().saturated_into(); - Weights::message_dispatch_weight(payload_size) - }, - Err(_) => Weight::zero(), - } - } - - fn dispatch( - message: DispatchMessage, - ) -> MessageDispatchResult { - let payload = match message.data.payload { - Ok(payload) => payload, - Err(e) => { - log::error!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}", - e, - message.key.nonce - ); - return MessageDispatchResult { - unspent_weight: Weight::zero(), - dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, - } - }, - }; - let dispatch_level_result = match BlobDispatcher::dispatch_blob(payload) { - Ok(_) => { - log::debug!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}", - message.key.nonce - ); - XcmBlobMessageDispatchResult::Dispatched - }, - Err(e) => { - log::error!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}", - e, message.key.nonce - ); - XcmBlobMessageDispatchResult::NotDispatched(Some(e)) - }, - }; - MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result } - } -} - -/// A pair of sending chain location and message lane, used by this chain to send messages -/// over the bridge. -#[cfg_attr(feature = "std", derive(Debug, Eq, PartialEq))] -pub struct SenderAndLane { - /// Sending chain relative location. - pub location: Location, - /// Message lane, used by the sending chain. - pub lane: LaneId, -} - -impl SenderAndLane { - /// Create new object using provided location and lane. - pub fn new(location: Location, lane: LaneId) -> Self { - SenderAndLane { location, lane } - } -} - -/// [`XcmBlobHauler`] is responsible for sending messages to the bridge "point-to-point link" from -/// one side, where on the other it can be dispatched by [`XcmBlobMessageDispatch`]. -pub trait XcmBlobHauler { - /// Runtime that has messages pallet deployed. - type Runtime: MessagesConfig; - /// Instance of the messages pallet that is used to send messages. - type MessagesInstance: 'static; - - /// Actual XCM message sender (`HRMP` or `UMP`) to the source chain - /// location (`Self::SenderAndLane::get().location`). - type ToSourceChainSender: SendXcm; - /// An XCM message that is sent to the sending chain when the bridge queue becomes congested. - type CongestedMessage: Get>>; - /// An XCM message that is sent to the sending chain when the bridge queue becomes not - /// congested. - type UncongestedMessage: Get>>; - - /// Returns `true` if we want to handle congestion. - fn supports_congestion_detection() -> bool { - Self::CongestedMessage::get().is_some() || Self::UncongestedMessage::get().is_some() - } -} - -/// XCM bridge adapter which connects [`XcmBlobHauler`] with [`pallet_bridge_messages`] and -/// makes sure that XCM blob is sent to the outbound lane to be relayed. -/// -/// It needs to be used at the source bridge hub. -pub struct XcmBlobHaulerAdapter( - sp_std::marker::PhantomData<(XcmBlobHauler, Lanes)>, -); - -impl< - H: XcmBlobHauler, - Lanes: Get>, - > OnMessagesDelivered for XcmBlobHaulerAdapter -{ - fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce) { - if let Some(sender_and_lane) = - Lanes::get().iter().find(|link| link.0.lane == lane).map(|link| &link.0) - { - // notify XCM queue manager about updated lane state - LocalXcmQueueManager::::on_bridge_messages_delivered( - sender_and_lane, - enqueued_messages, - ); - } - } -} - -/// Manager of local XCM queues (and indirectly - underlying transport channels) that -/// controls the queue state. -/// -/// It needs to be used at the source bridge hub. -pub struct LocalXcmQueueManager(PhantomData); - -/// Maximal number of messages in the outbound bridge queue. Once we reach this limit, we -/// send a "congestion" XCM message to the sending chain. -const OUTBOUND_LANE_CONGESTED_THRESHOLD: MessageNonce = 8_192; - -/// After we have sent "congestion" XCM message to the sending chain, we wait until number -/// of messages in the outbound bridge queue drops to this count, before sending `uncongestion` -/// XCM message. -const OUTBOUND_LANE_UNCONGESTED_THRESHOLD: MessageNonce = 1_024; - -impl LocalXcmQueueManager { - /// Must be called whenever we push a message to the bridge lane. - pub fn on_bridge_message_enqueued( - sender_and_lane: &SenderAndLane, - enqueued_messages: MessageNonce, - ) { - // skip if we dont want to handle congestion - if !H::supports_congestion_detection() { - return - } - - // if we have already sent the congestion signal, we don't want to do anything - if Self::is_congested_signal_sent(sender_and_lane.lane) { - return - } - - // if the bridge queue is not congested, we don't want to do anything - let is_congested = enqueued_messages > OUTBOUND_LANE_CONGESTED_THRESHOLD; - if !is_congested { - return - } - - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Sending 'congested' XCM message to {:?} to avoid overloading lane {:?}: there are\ - {} messages queued at the bridge queue", - sender_and_lane.location, - sender_and_lane.lane, - enqueued_messages, - ); - - if let Err(e) = Self::send_congested_signal(sender_and_lane) { - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Failed to send the 'congested' XCM message to {:?}: {:?}", - sender_and_lane.location, - e, - ); - } - } - - /// Must be called whenever we receive a message delivery confirmation. - pub fn on_bridge_messages_delivered( - sender_and_lane: &SenderAndLane, - enqueued_messages: MessageNonce, - ) { - // skip if we don't want to handle congestion - if !H::supports_congestion_detection() { - return - } - - // if we have not sent the congestion signal before, we don't want to do anything - if !Self::is_congested_signal_sent(sender_and_lane.lane) { - return - } - - // if the bridge queue is still congested, we don't want to do anything - let is_congested = enqueued_messages > OUTBOUND_LANE_UNCONGESTED_THRESHOLD; - if is_congested { - return - } - - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Sending 'uncongested' XCM message to {:?}. Lane {:?}: there are\ - {} messages queued at the bridge queue", - sender_and_lane.location, - sender_and_lane.lane, - enqueued_messages, - ); - - if let Err(e) = Self::send_uncongested_signal(sender_and_lane) { - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Failed to send the 'uncongested' XCM message to {:?}: {:?}", - sender_and_lane.location, - e, - ); - } - } - - /// Returns true if we have sent "congested" signal to the `sending_chain_location`. - fn is_congested_signal_sent(lane: LaneId) -> bool { - OutboundLanesCongestedSignals::::get(lane) - } - - /// Send congested signal to the `sending_chain_location`. - fn send_congested_signal(sender_and_lane: &SenderAndLane) -> Result<(), SendError> { - if let Some(msg) = H::CongestedMessage::get() { - send_xcm::(sender_and_lane.location.clone(), msg)?; - OutboundLanesCongestedSignals::::insert( - sender_and_lane.lane, - true, - ); - } - Ok(()) - } - - /// Send `uncongested` signal to the `sending_chain_location`. - fn send_uncongested_signal(sender_and_lane: &SenderAndLane) -> Result<(), SendError> { - if let Some(msg) = H::UncongestedMessage::get() { - send_xcm::(sender_and_lane.location.clone(), msg)?; - OutboundLanesCongestedSignals::::remove( - sender_and_lane.lane, - ); - } - Ok(()) - } -} - -/// Adapter for the implementation of `GetVersion`, which attempts to find the minimal -/// configured XCM version between the destination `dest` and the bridge hub location provided as -/// `Get`. -pub struct XcmVersionOfDestAndRemoteBridge( - sp_std::marker::PhantomData<(Version, RemoteBridge)>, -); -impl> GetVersion - for XcmVersionOfDestAndRemoteBridge -{ - fn get_version_for(dest: &Location) -> Option { - let dest_version = Version::get_version_for(dest); - let bridge_hub_version = Version::get_version_for(&RemoteBridge::get()); - - match (dest_version, bridge_hub_version) { - (Some(dv), Some(bhv)) => Some(sp_std::cmp::min(dv, bhv)), - (Some(dv), None) => Some(dv), - (None, Some(bhv)) => Some(bhv), - (None, None) => None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - - use bp_messages::OutboundLaneData; - use frame_support::parameter_types; - use pallet_bridge_messages::OutboundLanes; - - parameter_types! { - pub TestSenderAndLane: SenderAndLane = SenderAndLane { - location: Location::new(1, [Parachain(1000)]), - lane: TEST_LANE_ID, - }; - pub TestLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = sp_std::vec![ - (TestSenderAndLane::get(), (NetworkId::ByGenesis([0; 32]), InteriorLocation::Here)) - ]; - pub DummyXcmMessage: Xcm<()> = Xcm::new(); - } - - struct DummySendXcm; - - impl DummySendXcm { - fn messages_sent() -> u32 { - frame_support::storage::unhashed::get(b"DummySendXcm").unwrap_or(0) - } - } - - impl SendXcm for DummySendXcm { - type Ticket = (); - - fn validate( - _destination: &mut Option, - _message: &mut Option>, - ) -> SendResult { - Ok(((), Default::default())) - } - - fn deliver(_ticket: Self::Ticket) -> Result { - let messages_sent: u32 = Self::messages_sent(); - frame_support::storage::unhashed::put(b"DummySendXcm", &(messages_sent + 1)); - Ok(XcmHash::default()) - } - } - - struct TestBlobHauler; - - impl XcmBlobHauler for TestBlobHauler { - type Runtime = TestRuntime; - type MessagesInstance = (); - - type ToSourceChainSender = DummySendXcm; - type CongestedMessage = DummyXcmMessage; - type UncongestedMessage = DummyXcmMessage; - } - - type TestBlobHaulerAdapter = XcmBlobHaulerAdapter; - - fn fill_up_lane_to_congestion() -> MessageNonce { - let latest_generated_nonce = OUTBOUND_LANE_CONGESTED_THRESHOLD; - OutboundLanes::::insert( - TEST_LANE_ID, - OutboundLaneData { - oldest_unpruned_nonce: 0, - latest_received_nonce: 0, - latest_generated_nonce, - }, - ); - latest_generated_nonce - } - - #[test] - fn congested_signal_is_not_sent_twice() { - run_test(|| { - let enqueued = fill_up_lane_to_congestion(); - - // next sent message leads to congested signal - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - enqueued + 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - - // next sent message => we don't sent another congested signal - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - enqueued, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - }); - } - - #[test] - fn congested_signal_is_not_sent_when_outbound_lane_is_not_congested() { - run_test(|| { - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 0); - }); - } - - #[test] - fn congested_signal_is_sent_when_outbound_lane_is_congested() { - run_test(|| { - let enqueued = fill_up_lane_to_congestion(); - - // next sent message leads to congested signal - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - enqueued + 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - assert!(LocalXcmQueueManager::::is_congested_signal_sent(TEST_LANE_ID)); - }); - } - - #[test] - fn uncongested_signal_is_not_sent_when_messages_are_delivered_at_other_lane() { - run_test(|| { - LocalXcmQueueManager::::send_congested_signal(&TestSenderAndLane::get()).unwrap(); - assert_eq!(DummySendXcm::messages_sent(), 1); - - // when we receive a delivery report for other lane, we don't send an uncongested signal - TestBlobHaulerAdapter::on_messages_delivered(LaneId([42, 42, 42, 42]), 0); - assert_eq!(DummySendXcm::messages_sent(), 1); - }); - } - - #[test] - fn uncongested_signal_is_not_sent_when_we_havent_send_congested_signal_before() { - run_test(|| { - TestBlobHaulerAdapter::on_messages_delivered(TEST_LANE_ID, 0); - assert_eq!(DummySendXcm::messages_sent(), 0); - }); - } - - #[test] - fn uncongested_signal_is_not_sent_if_outbound_lane_is_still_congested() { - run_test(|| { - LocalXcmQueueManager::::send_congested_signal(&TestSenderAndLane::get()).unwrap(); - assert_eq!(DummySendXcm::messages_sent(), 1); - - TestBlobHaulerAdapter::on_messages_delivered( - TEST_LANE_ID, - OUTBOUND_LANE_UNCONGESTED_THRESHOLD + 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - }); - } - - #[test] - fn uncongested_signal_is_sent_if_outbound_lane_is_uncongested() { - run_test(|| { - LocalXcmQueueManager::::send_congested_signal(&TestSenderAndLane::get()).unwrap(); - assert_eq!(DummySendXcm::messages_sent(), 1); - - TestBlobHaulerAdapter::on_messages_delivered( - TEST_LANE_ID, - OUTBOUND_LANE_UNCONGESTED_THRESHOLD, - ); - assert_eq!(DummySendXcm::messages_sent(), 2); - }); - } -} diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs index 2f248a7162a6..fed0d15cc080 100644 --- a/bridges/bin/runtime-common/src/mock.rs +++ b/bridges/bin/runtime-common/src/mock.rs @@ -18,8 +18,6 @@ #![cfg(test)] -use crate::messages_xcm_extension::XcmAsPlainPayload; - use bp_header_chain::ChainWithGrandpa; use bp_messages::{ target_chain::{DispatchMessage, MessageDispatch}, @@ -28,6 +26,7 @@ use bp_messages::{ use bp_parachains::SingleParaStoredHeaderDataBuilder; use bp_relayers::PayRewardFromAccount; use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Parachain}; +use codec::Encode; use frame_support::{ derive_impl, parameter_types, weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, @@ -85,7 +84,11 @@ pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< >; /// Message lane used in tests. -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); +#[allow(unused)] +pub fn test_lane_id() -> LaneId { + LaneId::new(1, 2) +} + /// Bridged chain id used in tests. pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; /// Maximal extrinsic size at the `BridgedChain`. @@ -111,7 +114,6 @@ crate::generate_bridge_reject_obsolete_headers_and_messages! { } parameter_types! { - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; pub const BridgedParasPalletName: &'static str = "Paras"; pub const ExistentialDeposit: ThisChainBalance = 500; pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; @@ -185,9 +187,8 @@ impl pallet_bridge_parachains::Config for TestRuntime { impl pallet_bridge_messages::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; - type ActiveOutboundLanes = ActiveOutboundLanes; - type OutboundPayload = XcmAsPlainPayload; + type OutboundPayload = Vec; type InboundPayload = Vec; type DeliveryPayments = (); @@ -218,8 +219,8 @@ impl pallet_bridge_relayers::Config for TestRuntime { pub struct DummyMessageDispatch; impl DummyMessageDispatch { - pub fn deactivate() { - frame_support::storage::unhashed::put(&b"inactive"[..], &false); + pub fn deactivate(lane: LaneId) { + frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false); } } @@ -227,8 +228,9 @@ impl MessageDispatch for DummyMessageDispatch { type DispatchPayload = Vec; type DispatchLevelResult = (); - fn is_active() -> bool { - frame_support::storage::unhashed::take::(&b"inactive"[..]) != Some(false) + fn is_active(lane: LaneId) -> bool { + frame_support::storage::unhashed::take::(&(b"inactive", lane).encode()[..]) != + Some(false) } fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { diff --git a/bridges/bin/runtime-common/src/parachains_benchmarking.rs b/bridges/bin/runtime-common/src/parachains_benchmarking.rs index bcbd779b44de..e48a5664e31a 100644 --- a/bridges/bin/runtime-common/src/parachains_benchmarking.rs +++ b/bridges/bin/runtime-common/src/parachains_benchmarking.rs @@ -20,12 +20,13 @@ use crate::messages_benchmarking::insert_header_to_grandpa_pallet; -use bp_parachains::parachain_head_storage_key_at_source; +use bp_parachains::{ + parachain_head_storage_key_at_source, RelayBlockHash, RelayBlockHasher, RelayBlockNumber, +}; use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; use bp_runtime::{grow_storage_value, record_all_trie_keys, Chain, UnverifiedStorageProofParams}; use codec::Encode; use frame_support::traits::Get; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use sp_std::prelude::*; use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; diff --git a/bridges/chains/chain-bridge-hub-rococo/Cargo.toml b/bridges/chains/chain-bridge-hub-rococo/Cargo.toml index 66848ba0e263..23fbd9a2742f 100644 --- a/bridges/chains/chain-bridge-hub-rococo/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-rococo/Cargo.toml @@ -14,14 +14,15 @@ exclude-from-umbrella = true workspace = true [dependencies] -# Bridge Dependencies +codec = { features = ["derive"], workspace = true } +# Bridge Dependencies bp-bridge-hub-cumulus = { workspace = true } bp-runtime = { workspace = true } bp-messages = { workspace = true } +bp-xcm-bridge-hub = { workspace = true } # Substrate Based Dependencies - frame-support = { workspace = true } sp-api = { workspace = true } sp-runtime = { workspace = true } @@ -33,6 +34,8 @@ std = [ "bp-bridge-hub-cumulus/std", "bp-messages/std", "bp-runtime/std", + "bp-xcm-bridge-hub/std", + "codec/std", "frame-support/std", "sp-api/std", "sp-runtime/std", diff --git a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs index 73af997b9950..7920eb934033 100644 --- a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs @@ -25,6 +25,7 @@ use bp_messages::*; use bp_runtime::{ decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, }; +use codec::{Decode, Encode}; use frame_support::{ dispatch::DispatchClass, sp_runtime::{MultiAddress, MultiSigner, RuntimeDebug, StateVersion}, @@ -104,13 +105,21 @@ frame_support::parameter_types! { /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Rococo /// BridgeHub. /// (initially was calculated by test `BridgeHubRococo::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 59_034_266; + pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 57_145_832; /// Transaction fee that is paid at the Rococo BridgeHub for delivering single inbound message. /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_standalone_message_delivery_transaction` + `33%`) - pub const BridgeHubRococoBaseDeliveryFeeInRocs: u128 = 314_037_860; + pub const BridgeHubRococoBaseDeliveryFeeInRocs: u128 = 297_644_174; /// Transaction fee that is paid at the Rococo BridgeHub for delivering single outbound message confirmation. /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_standalone_message_confirmation_transaction` + `33%`) - pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 57_414_813; + pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 56_740_432; +} + +/// Wrapper over `BridgeHubRococo`'s `RuntimeCall` that can be used without a runtime. +#[derive(Decode, Encode)] +pub enum RuntimeCall { + /// Points to the `pallet_xcm_bridge_hub` pallet instance for `BridgeHubWestend`. + #[codec(index = 52)] + XcmOverBridgeHubWestend(bp_xcm_bridge_hub::XcmBridgeHubCall), } diff --git a/bridges/chains/chain-bridge-hub-westend/Cargo.toml b/bridges/chains/chain-bridge-hub-westend/Cargo.toml index 24a196c1d70d..61357e6aa6c8 100644 --- a/bridges/chains/chain-bridge-hub-westend/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-westend/Cargo.toml @@ -14,15 +14,15 @@ exclude-from-umbrella = true workspace = true [dependencies] +codec = { features = ["derive"], workspace = true } # Bridge Dependencies - bp-bridge-hub-cumulus = { workspace = true } bp-runtime = { workspace = true } bp-messages = { workspace = true } +bp-xcm-bridge-hub = { workspace = true } # Substrate Based Dependencies - frame-support = { workspace = true } sp-api = { workspace = true } sp-runtime = { workspace = true } @@ -34,6 +34,8 @@ std = [ "bp-bridge-hub-cumulus/std", "bp-messages/std", "bp-runtime/std", + "bp-xcm-bridge-hub/std", + "codec/std", "frame-support/std", "sp-api/std", "sp-runtime/std", diff --git a/bridges/chains/chain-bridge-hub-westend/src/lib.rs b/bridges/chains/chain-bridge-hub-westend/src/lib.rs index 17ff2c858a1d..644fa64c687b 100644 --- a/bridges/chains/chain-bridge-hub-westend/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-westend/src/lib.rs @@ -24,6 +24,7 @@ use bp_messages::*; use bp_runtime::{ decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, }; +use codec::{Decode, Encode}; use frame_support::dispatch::DispatchClass; use sp_runtime::{RuntimeDebug, StateVersion}; @@ -93,13 +94,21 @@ frame_support::parameter_types! { /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Westend /// BridgeHub. /// (initially was calculated by test `BridgeHubWestend::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubWestendBaseXcmFeeInWnds: u128 = 17_756_830_000; + pub const BridgeHubWestendBaseXcmFeeInWnds: u128 = 18_191_740_000; /// Transaction fee that is paid at the Westend BridgeHub for delivering single inbound message. /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_standalone_message_delivery_transaction` + `33%`) - pub const BridgeHubWestendBaseDeliveryFeeInWnds: u128 = 94_211_536_452; + pub const BridgeHubWestendBaseDeliveryFeeInWnds: u128 = 89_293_427_116; /// Transaction fee that is paid at the Westend BridgeHub for delivering single outbound message confirmation. /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_standalone_message_confirmation_transaction` + `33%`) - pub const BridgeHubWestendBaseConfirmationFeeInWnds: u128 = 17_224_486_452; + pub const BridgeHubWestendBaseConfirmationFeeInWnds: u128 = 17_022_177_116; +} + +/// Wrapper over `BridgeHubWestend`'s `RuntimeCall` that can be used without a runtime. +#[derive(Decode, Encode)] +pub enum RuntimeCall { + /// Points to the `pallet_xcm_bridge_hub` pallet instance for `BridgeHubRococo`. + #[codec(index = 45)] + XcmOverBridgeHubRococo(bp_xcm_bridge_hub::XcmBridgeHubCall), } diff --git a/bridges/docs/polkadot-kusama-bridge-overview.md b/bridges/docs/polkadot-kusama-bridge-overview.md index 08036f0b0722..b1812e4caf12 100644 --- a/bridges/docs/polkadot-kusama-bridge-overview.md +++ b/bridges/docs/polkadot-kusama-bridge-overview.md @@ -25,8 +25,9 @@ You won't be able to directly use bridge hub transactions to send XCM messages o use other parachains transactions, which will use HRMP to deliver messages to the Bridge Hub. The Bridge Hub will just queue these messages in its outbound lane, which is dedicated to deliver messages between two parachains. -Our first planned bridge will connect the Polkadot and Kusama Asset Hubs. A bridge between those two parachains would -allow Asset Hub Polkadot accounts to hold wrapped KSM tokens and Asset Hub Kusama accounts to hold wrapped DOT tokens. +Our first planned bridge will connect the Polkadot and Kusama Asset Hubs. A bridge between those two +parachains would allow Asset Hub Polkadot accounts to hold wrapped KSM tokens and Asset Hub Kusama +accounts to hold wrapped DOT tokens. For that bridge (pair of parachains under different consensus systems) we'll be using the lane 00000000. Later, when other parachains will join the bridge, they will be using other lanes for their messages. @@ -92,13 +93,14 @@ Obviously, there should be someone who is paying relayer rewards. We want bridge can't use fees for rewards. Instead, the parachains using the bridge, use sovereign accounts on both sides of the bridge to cover relayer rewards. -Bridged Parachains will have sovereign accounts at bridge hubs. For example, the Kusama Asset Hub will have an account -at the Polkadot Bridge Hub. The Polkadot Asset Hub will have an account at the Kusama Bridge Hub. The sovereign accounts -are used as a source of funds when the relayer is calling the `pallet_bridge_relayers::claim_rewards`. +Bridged Parachains will have sovereign accounts at bridge hubs. For example, the Kusama Asset Hub will +have an account at the Polkadot Bridge Hub. The Polkadot Asset Hub will have an account at the Kusama +Bridge Hub. The sovereign accounts are used as a source of funds when the relayer is calling the +`pallet_bridge_relayers::claim_rewards`. -Since messages lane is only used by the pair of parachains, there's no collision between different bridges. E.g. Kusama -Asset Hub will only reward relayers that are delivering messages from Kusama Asset Hub. The Kusama Asset Hub sovereign -account is not used to cover rewards of bridging with some other Polkadot Parachain. +Since messages lane is only used by the pair of parachains, there's no collision between different bridges. E.g. +Kusama Asset Hub will only reward relayers that are delivering messages from Kusama Asset Hub. +The Kusama Asset Hub sovereign account is not used to cover rewards of bridging with some other Polkadot Parachain. ### Multiple Relayers and Rewards diff --git a/bridges/modules/grandpa/src/call_ext.rs b/bridges/modules/grandpa/src/call_ext.rs index f08eb4c5d1ab..d964901ba4bc 100644 --- a/bridges/modules/grandpa/src/call_ext.rs +++ b/bridges/modules/grandpa/src/call_ext.rs @@ -18,7 +18,10 @@ use crate::{ weights::WeightInfo, BestFinalized, BridgedBlockNumber, BridgedHeader, Config, CurrentAuthoritySet, Error, FreeHeadersRemaining, Pallet, }; -use bp_header_chain::{justification::GrandpaJustification, submit_finality_proof_limits_extras}; +use bp_header_chain::{ + justification::GrandpaJustification, submit_finality_proof_limits_extras, + SubmitFinalityProofInfo, +}; use bp_runtime::{BlockNumberOf, Chain, OwnedBridgeModule}; use frame_support::{ dispatch::CallableCallFor, @@ -31,37 +34,11 @@ use sp_runtime::{ transaction_validity::{InvalidTransaction, TransactionValidityError}, RuntimeDebug, SaturatedConversion, }; - -/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. -#[derive(Copy, Clone, PartialEq, RuntimeDebug)] -pub struct SubmitFinalityProofInfo { - /// Number of the finality target. - pub block_number: N, - /// An identifier of the validators set that has signed the submitted justification. - /// It might be `None` if deprecated version of the `submit_finality_proof` is used. - pub current_set_id: Option, - /// If `true`, then the call proves new **mandatory** header. - pub is_mandatory: bool, - /// If `true`, then the call must be free (assuming that everything else is valid) to - /// be treated as valid. - pub is_free_execution_expected: bool, - /// Extra weight that we assume is included in the call. - /// - /// We have some assumptions about headers and justifications of the bridged chain. - /// We know that if our assumptions are correct, then the call must not have the - /// weight above some limit. The fee paid for weight above that limit, is never refunded. - pub extra_weight: Weight, - /// Extra size (in bytes) that we assume are included in the call. - /// - /// We have some assumptions about headers and justifications of the bridged chain. - /// We know that if our assumptions are correct, then the call must not have the - /// weight above some limit. The fee paid for bytes above that limit, is never refunded. - pub extra_size: u32, -} +use sp_std::fmt::Debug; /// Verified `SubmitFinalityProofInfo`. #[derive(Copy, Clone, PartialEq, RuntimeDebug)] -pub struct VerifiedSubmitFinalityProofInfo { +pub struct VerifiedSubmitFinalityProofInfo { /// Base call information. pub base: SubmitFinalityProofInfo, /// A difference between bundled bridged header and best bridged header known to us @@ -69,13 +46,6 @@ pub struct VerifiedSubmitFinalityProofInfo { pub improved_by: N, } -impl SubmitFinalityProofInfo { - /// Returns `true` if call size/weight is below our estimations for regular calls. - pub fn fits_limits(&self) -> bool { - self.extra_weight.is_zero() && self.extra_size.is_zero() - } -} - /// Helper struct that provides methods for working with the `SubmitFinalityProof` call. pub struct SubmitFinalityProofHelper, I: 'static> { _phantom_data: sp_std::marker::PhantomData<(T, I)>, @@ -336,9 +306,9 @@ mod tests { TestRuntime, }, BestFinalized, Config, CurrentAuthoritySet, FreeHeadersRemaining, PalletOperatingMode, - StoredAuthoritySet, SubmitFinalityProofInfo, WeightInfo, + StoredAuthoritySet, WeightInfo, }; - use bp_header_chain::ChainWithGrandpa; + use bp_header_chain::{ChainWithGrandpa, SubmitFinalityProofInfo}; use bp_runtime::{BasicOperatingMode, HeaderId}; use bp_test_utils::{ make_default_justification, make_justification_for_header, JustificationGeneratorParams, diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs index c62951b74656..dff4b98fd919 100644 --- a/bridges/modules/grandpa/src/lib.rs +++ b/bridges/modules/grandpa/src/lib.rs @@ -117,7 +117,7 @@ pub mod pallet { /// /// However, if the bridged chain gets compromised, its validators may generate as many /// "free" headers as they want. And they may fill the whole block (at this chain) for - /// free. This constants limits number of calls that we may refund in a single block. + /// free. This constant limits number of calls that we may refund in a single block. /// All calls above this limit are accepted, but are not refunded. #[pallet::constant] type MaxFreeHeadersPerBlock: Get; diff --git a/bridges/modules/messages/Cargo.toml b/bridges/modules/messages/Cargo.toml index 33f524030d26..9df318587e38 100644 --- a/bridges/modules/messages/Cargo.toml +++ b/bridges/modules/messages/Cargo.toml @@ -73,7 +73,4 @@ try-runtime = [ "pallet-bridge-grandpa/try-runtime", "sp-runtime/try-runtime", ] -test-helpers = [ - "bp-runtime/test-helpers", - "sp-trie", -] +test-helpers = ["bp-runtime/test-helpers", "sp-trie"] diff --git a/bridges/modules/messages/README.md b/bridges/modules/messages/README.md index 80fd92eb0e5a..a78c86802498 100644 --- a/bridges/modules/messages/README.md +++ b/bridges/modules/messages/README.md @@ -28,9 +28,10 @@ Single message lane may be seen as a transport channel for single application (o time the module itself never dictates any lane or message rules. In the end, it is the runtime developer who defines what message lane and message mean for this runtime. -In our [Kusama<>Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md) we are using lane as a channel of -communication between two parachains of different relay chains. For example, lane `[0, 0, 0, 0]` is used for Polkadot <> -Kusama Asset Hub communications. Other lanes may be used to bridge other parachains. +In our [Kusama<>Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md) we are using lane +as a channel of communication between two parachains of different relay chains. For example, lane +`[0, 0, 0, 0]` is used for Polkadot <> Kusama Asset Hub communications. Other lanes may be used to +bridge other parachains. ## Message Workflow @@ -142,10 +143,9 @@ and will simply reject all transactions, related to inbound messages. ### What about other Constants in the Messages Module Configuration Trait? -Two settings that are used to check messages in the `send_message()` function. The -`pallet_bridge_messages::Config::ActiveOutboundLanes` is an array of all message lanes, that -may be used to send messages. All messages sent using other lanes are rejected. All messages that have -size above `pallet_bridge_messages::Config::MaximalOutboundPayloadSize` will also be rejected. +`pallet_bridge_messages::Config::MaximalOutboundPayloadSize` constant defines the maximal size +of outbound message that may be sent. If the message size is above this limit, the message is +rejected. To be able to reward the relayer for delivering messages, we store a map of message nonces range => identifier of the relayer that has delivered this range at the target chain runtime storage. If a diff --git a/bridges/modules/messages/src/benchmarking.rs b/bridges/modules/messages/src/benchmarking.rs index d38aaf32dc94..b3a4447fb021 100644 --- a/bridges/modules/messages/src/benchmarking.rs +++ b/bridges/modules/messages/src/benchmarking.rs @@ -19,14 +19,14 @@ #![cfg(feature = "runtime-benchmarks")] use crate::{ - inbound_lane::InboundLaneStorage, outbound_lane, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, - BridgedChainOf, Call, OutboundLanes, RuntimeInboundLaneStorage, + active_outbound_lane, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, BridgedChainOf, Call, + InboundLanes, OutboundLanes, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, target_chain::FromBridgedChainMessagesProof, ChainWithMessages, DeliveredMessages, - InboundLaneData, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, + InboundLaneData, LaneId, LaneState, MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use bp_runtime::{AccountIdOf, HashOf, UnverifiedStorageProofParams}; @@ -74,10 +74,8 @@ pub struct MessageDeliveryProofParams { /// Trait that must be implemented by runtime. pub trait Config: crate::Config { /// Lane id to use in benchmarks. - /// - /// By default, lane 00000000 is used. fn bench_lane_id() -> LaneId { - LaneId([0, 0, 0, 0]) + LaneId::new(1, 2) } /// Return id of relayer account at the bridged chain. @@ -113,22 +111,32 @@ pub trait Config: crate::Config { } fn send_regular_message, I: 'static>() { - let mut outbound_lane = outbound_lane::(T::bench_lane_id()); + OutboundLanes::::insert( + T::bench_lane_id(), + OutboundLaneData { + state: LaneState::Opened, + latest_generated_nonce: 1, + ..Default::default() + }, + ); + + let mut outbound_lane = active_outbound_lane::(T::bench_lane_id()).unwrap(); outbound_lane.send_message(BoundedVec::try_from(vec![]).expect("We craft valid messages")); } fn receive_messages, I: 'static>(nonce: MessageNonce) { - let mut inbound_lane_storage = - RuntimeInboundLaneStorage::::from_lane_id(T::bench_lane_id()); - inbound_lane_storage.set_data(InboundLaneData { - relayers: vec![UnrewardedRelayer { - relayer: T::bridged_relayer_id(), - messages: DeliveredMessages::new(nonce), - }] - .into_iter() - .collect(), - last_confirmed_nonce: 0, - }); + InboundLanes::::insert( + T::bench_lane_id(), + InboundLaneData { + state: LaneState::Opened, + relayers: vec![UnrewardedRelayer { + relayer: T::bridged_relayer_id(), + messages: DeliveredMessages::new(nonce), + }] + .into(), + last_confirmed_nonce: 0, + }, + ); } struct ReceiveMessagesProofSetup, I: 'static> { @@ -173,8 +181,8 @@ impl, I: 'static> ReceiveMessagesProofSetup { fn check_last_nonce(&self) { assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - self.last_nonce(), + crate::InboundLanes::::get(&T::bench_lane_id()).map(|d| d.last_delivered_nonce()), + Some(self.last_nonce()), ); } } @@ -277,6 +285,7 @@ mod benchmarks { lane: T::bench_lane_id(), message_nonces: setup.nonces(), outbound_lane_data: Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: setup.last_nonce(), latest_received_nonce: ReceiveMessagesProofSetup::::LATEST_RECEIVED_NONCE, latest_generated_nonce: setup.last_nonce(), @@ -356,6 +365,7 @@ mod benchmarks { let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), inbound_lane_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![UnrewardedRelayer { relayer: relayer_id.clone(), messages: DeliveredMessages::new(1), @@ -374,7 +384,10 @@ mod benchmarks { relayers_state, ); - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 1); + assert_eq!( + OutboundLanes::::get(T::bench_lane_id()).map(|s| s.latest_received_nonce), + Some(1) + ); assert!(T::is_relayer_rewarded(&relayer_id)); } @@ -404,6 +417,7 @@ mod benchmarks { let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), inbound_lane_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![UnrewardedRelayer { relayer: relayer_id.clone(), messages: delivered_messages, @@ -422,7 +436,10 @@ mod benchmarks { relayers_state, ); - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + assert_eq!( + OutboundLanes::::get(T::bench_lane_id()).map(|s| s.latest_received_nonce), + Some(2) + ); assert!(T::is_relayer_rewarded(&relayer_id)); } @@ -451,6 +468,7 @@ mod benchmarks { let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), inbound_lane_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: relayer1_id.clone(), @@ -475,7 +493,10 @@ mod benchmarks { relayers_state, ); - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + assert_eq!( + OutboundLanes::::get(T::bench_lane_id()).map(|s| s.latest_received_nonce), + Some(2) + ); assert!(T::is_relayer_rewarded(&relayer1_id)); assert!(T::is_relayer_rewarded(&relayer2_id)); } diff --git a/bridges/bin/runtime-common/src/messages_call_ext.rs b/bridges/modules/messages/src/call_ext.rs similarity index 57% rename from bridges/bin/runtime-common/src/messages_call_ext.rs rename to bridges/modules/messages/src/call_ext.rs index a9ee1969ae0c..8e021c8e5e24 100644 --- a/bridges/bin/runtime-common/src/messages_call_ext.rs +++ b/bridges/modules/messages/src/call_ext.rs @@ -16,121 +16,18 @@ //! Helpers for easier manipulation of call processing with signed extensions. +use crate::{BridgedChainOf, Config, InboundLanes, OutboundLanes, Pallet, LOG_TARGET}; + use bp_messages::{ - target_chain::MessageDispatch, ChainWithMessages, InboundLaneData, LaneId, MessageNonce, + target_chain::MessageDispatch, BaseMessagesProofInfo, ChainWithMessages, InboundLaneData, + LaneId, MessageNonce, MessagesCallInfo, ReceiveMessagesDeliveryProofInfo, + ReceiveMessagesProofInfo, UnrewardedRelayerOccupation, }; use bp_runtime::{AccountIdOf, OwnedBridgeModule}; use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; -use pallet_bridge_messages::{BridgedChainOf, Config, Pallet}; -use sp_runtime::{transaction_validity::TransactionValidity, RuntimeDebug}; -use sp_std::ops::RangeInclusive; - -/// Generic info about a messages delivery/confirmation proof. -#[derive(PartialEq, RuntimeDebug)] -pub struct BaseMessagesProofInfo { - /// Message lane, used by the call. - pub lane_id: LaneId, - /// Nonces of messages, included in the call. - /// - /// For delivery transaction, it is nonces of bundled messages. For confirmation - /// transaction, it is nonces that are to be confirmed during the call. - pub bundled_range: RangeInclusive, - /// Nonce of the best message, stored by this chain before the call is dispatched. - /// - /// For delivery transaction, it is the nonce of best delivered message before the call. - /// For confirmation transaction, it is the nonce of best confirmed message before the call. - pub best_stored_nonce: MessageNonce, -} - -impl BaseMessagesProofInfo { - /// Returns true if `bundled_range` continues the `0..=best_stored_nonce` range. - fn appends_to_stored_nonce(&self) -> bool { - Some(*self.bundled_range.start()) == self.best_stored_nonce.checked_add(1) - } -} - -/// Occupation state of the unrewarded relayers vector. -#[derive(PartialEq, RuntimeDebug)] -#[cfg_attr(test, derive(Default))] -pub struct UnrewardedRelayerOccupation { - /// The number of remaining unoccupied entries for new relayers. - pub free_relayer_slots: MessageNonce, - /// The number of messages that we are ready to accept. - pub free_message_slots: MessageNonce, -} - -/// Info about a `ReceiveMessagesProof` call which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub struct ReceiveMessagesProofInfo { - /// Base messages proof info - pub base: BaseMessagesProofInfo, - /// State of unrewarded relayers vector. - pub unrewarded_relayers: UnrewardedRelayerOccupation, -} - -impl ReceiveMessagesProofInfo { - /// Returns true if: - /// - /// - either inbound lane is ready to accept bundled messages; - /// - /// - or there are no bundled messages, but the inbound lane is blocked by too many unconfirmed - /// messages and/or unrewarded relayers. - fn is_obsolete(&self, is_dispatcher_active: bool) -> bool { - // if dispatcher is inactive, we don't accept any delivery transactions - if !is_dispatcher_active { - return true - } - - // transactions with zero bundled nonces are not allowed, unless they're message - // delivery transactions, which brings reward confirmations required to unblock - // the lane - if self.base.bundled_range.is_empty() { - let empty_transactions_allowed = - // we allow empty transactions when we can't accept delivery from new relayers - self.unrewarded_relayers.free_relayer_slots == 0 || - // or if we can't accept new messages at all - self.unrewarded_relayers.free_message_slots == 0; - - return !empty_transactions_allowed - } - - // otherwise we require bundled messages to continue stored range - !self.base.appends_to_stored_nonce() - } -} - -/// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub struct ReceiveMessagesDeliveryProofInfo(pub BaseMessagesProofInfo); - -impl ReceiveMessagesDeliveryProofInfo { - /// Returns true if outbound lane is ready to accept confirmations of bundled messages. - fn is_obsolete(&self) -> bool { - self.0.bundled_range.is_empty() || !self.0.appends_to_stored_nonce() - } -} - -/// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call -/// which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub enum CallInfo { - /// Messages delivery call info. - ReceiveMessagesProof(ReceiveMessagesProofInfo), - /// Messages delivery confirmation call info. - ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo), -} - -impl CallInfo { - /// Returns range of messages, bundled with the call. - pub fn bundled_messages(&self) -> RangeInclusive { - match *self { - Self::ReceiveMessagesProof(ref info) => info.base.bundled_range.clone(), - Self::ReceiveMessagesDeliveryProof(ref info) => info.0.bundled_range.clone(), - } - } -} +use sp_runtime::transaction_validity::TransactionValidity; -/// Helper struct that provides methods for working with a call supported by `CallInfo`. +/// Helper struct that provides methods for working with a call supported by `MessagesCallInfo`. pub struct CallHelper, I: 'static> { _phantom_data: sp_std::marker::PhantomData<(T, I)>, } @@ -142,11 +39,13 @@ impl, I: 'static> CallHelper { /// /// - call is `receive_messages_delivery_proof` and all messages confirmations have been /// received. - pub fn was_successful(info: &CallInfo) -> bool { + pub fn was_successful(info: &MessagesCallInfo) -> bool { match info { - CallInfo::ReceiveMessagesProof(info) => { - let inbound_lane_data = - pallet_bridge_messages::InboundLanes::::get(info.base.lane_id); + MessagesCallInfo::ReceiveMessagesProof(info) => { + let inbound_lane_data = match InboundLanes::::get(info.base.lane_id) { + Some(inbound_lane_data) => inbound_lane_data, + None => return false, + }; if info.base.bundled_range.is_empty() { let post_occupation = unrewarded_relayers_occupation::(&inbound_lane_data); @@ -160,9 +59,11 @@ impl, I: 'static> CallHelper { inbound_lane_data.last_delivered_nonce() == *info.base.bundled_range.end() }, - CallInfo::ReceiveMessagesDeliveryProof(info) => { - let outbound_lane_data = - pallet_bridge_messages::OutboundLanes::::get(info.0.lane_id); + MessagesCallInfo::ReceiveMessagesDeliveryProof(info) => { + let outbound_lane_data = match OutboundLanes::::get(info.0.lane_id) { + Some(outbound_lane_data) => outbound_lane_data, + None => return false, + }; outbound_lane_data.latest_received_nonce == *info.0.bundled_range.end() }, } @@ -170,7 +71,7 @@ impl, I: 'static> CallHelper { } /// Trait representing a call that is a sub type of `pallet_bridge_messages::Call`. -pub trait MessagesCallSubType, I: 'static>: +pub trait CallSubType, I: 'static>: IsSubType, T>> { /// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call. @@ -180,13 +81,13 @@ pub trait MessagesCallSubType, I: 'static>: /// a `ReceiveMessagesDeliveryProof` call. fn receive_messages_delivery_proof_info(&self) -> Option; - /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` + /// Create a new instance of `MessagesCallInfo` from a `ReceiveMessagesProof` /// or a `ReceiveMessagesDeliveryProof` call. - fn call_info(&self) -> Option; + fn call_info(&self) -> Option; - /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` + /// Create a new instance of `MessagesCallInfo` from a `ReceiveMessagesProof` /// or a `ReceiveMessagesDeliveryProof` call, if the call is for the provided lane. - fn call_info_for(&self, lane_id: LaneId) -> Option; + fn call_info_for(&self, lane_id: LaneId) -> Option; /// Ensures that a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call: /// @@ -211,15 +112,13 @@ impl< Call: IsSubType, T>>, T: frame_system::Config + Config, I: 'static, - > MessagesCallSubType for T::RuntimeCall + > CallSubType for T::RuntimeCall { fn receive_messages_proof_info(&self) -> Option { - if let Some(pallet_bridge_messages::Call::::receive_messages_proof { - ref proof, - .. - }) = self.is_sub_type() + if let Some(crate::Call::::receive_messages_proof { ref proof, .. }) = + self.is_sub_type() { - let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(proof.lane); + let inbound_lane_data = InboundLanes::::get(proof.lane)?; return Some(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { @@ -237,13 +136,13 @@ impl< } fn receive_messages_delivery_proof_info(&self) -> Option { - if let Some(pallet_bridge_messages::Call::::receive_messages_delivery_proof { + if let Some(crate::Call::::receive_messages_delivery_proof { ref proof, ref relayers_state, .. }) = self.is_sub_type() { - let outbound_lane_data = pallet_bridge_messages::OutboundLanes::::get(proof.lane); + let outbound_lane_data = OutboundLanes::::get(proof.lane)?; return Some(ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { lane_id: proof.lane, @@ -260,23 +159,23 @@ impl< None } - fn call_info(&self) -> Option { + fn call_info(&self) -> Option { if let Some(info) = self.receive_messages_proof_info() { - return Some(CallInfo::ReceiveMessagesProof(info)) + return Some(MessagesCallInfo::ReceiveMessagesProof(info)) } if let Some(info) = self.receive_messages_delivery_proof_info() { - return Some(CallInfo::ReceiveMessagesDeliveryProof(info)) + return Some(MessagesCallInfo::ReceiveMessagesDeliveryProof(info)) } None } - fn call_info_for(&self, lane_id: LaneId) -> Option { + fn call_info_for(&self, lane_id: LaneId) -> Option { self.call_info().filter(|info| { let actual_lane_id = match info { - CallInfo::ReceiveMessagesProof(info) => info.base.lane_id, - CallInfo::ReceiveMessagesDeliveryProof(info) => info.0.lane_id, + MessagesCallInfo::ReceiveMessagesProof(info) => info.base.lane_id, + MessagesCallInfo::ReceiveMessagesDeliveryProof(info) => info.0.lane_id, }; actual_lane_id == lane_id }) @@ -287,29 +186,30 @@ impl< match self.call_info() { Some(proof_info) if is_pallet_halted => { log::trace!( - target: pallet_bridge_messages::LOG_TARGET, + target: LOG_TARGET, "Rejecting messages transaction on halted pallet: {:?}", proof_info ); return sp_runtime::transaction_validity::InvalidTransaction::Call.into() }, - Some(CallInfo::ReceiveMessagesProof(proof_info)) - if proof_info.is_obsolete(T::MessageDispatch::is_active()) => + Some(MessagesCallInfo::ReceiveMessagesProof(proof_info)) + if proof_info + .is_obsolete(T::MessageDispatch::is_active(proof_info.base.lane_id)) => { log::trace!( - target: pallet_bridge_messages::LOG_TARGET, + target: LOG_TARGET, "Rejecting obsolete messages delivery transaction: {:?}", proof_info ); return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() }, - Some(CallInfo::ReceiveMessagesDeliveryProof(proof_info)) + Some(MessagesCallInfo::ReceiveMessagesDeliveryProof(proof_info)) if proof_info.is_obsolete() => { log::trace!( - target: pallet_bridge_messages::LOG_TARGET, + target: LOG_TARGET, "Rejecting obsolete messages confirmation transaction: {:?}", proof_info, ); @@ -343,52 +243,49 @@ fn unrewarded_relayers_occupation, I: 'static>( #[cfg(test)] mod tests { use super::*; - use crate::{ - messages_call_ext::MessagesCallSubType, - mock::{BridgedUnderlyingChain, DummyMessageDispatch, TestRuntime, ThisChainRuntimeCall}, - }; + use crate::tests::mock::*; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, DeliveredMessages, UnrewardedRelayer, - UnrewardedRelayersState, + target_chain::FromBridgedChainMessagesProof, DeliveredMessages, InboundLaneData, LaneState, + OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use sp_std::ops::RangeInclusive; + fn test_lane_id() -> LaneId { + LaneId::new(1, 2) + } + fn fill_unrewarded_relayers() { - let mut inbound_lane_state = - pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); - for n in 0..BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX { + let mut inbound_lane_state = InboundLanes::::get(test_lane_id()).unwrap(); + for n in 0..BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX { inbound_lane_state.relayers.push_back(UnrewardedRelayer { relayer: Default::default(), messages: DeliveredMessages { begin: n + 1, end: n + 1 }, }); } - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - inbound_lane_state, - ); + InboundLanes::::insert(test_lane_id(), inbound_lane_state); } fn fill_unrewarded_messages() { - let mut inbound_lane_state = - pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); + let mut inbound_lane_state = InboundLanes::::get(test_lane_id()).unwrap(); inbound_lane_state.relayers.push_back(UnrewardedRelayer { relayer: Default::default(), messages: DeliveredMessages { begin: 1, - end: BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + end: BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }); - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - inbound_lane_state, - ); + InboundLanes::::insert(test_lane_id(), inbound_lane_state); } fn deliver_message_10() { - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - bp_messages::InboundLaneData { relayers: Default::default(), last_confirmed_nonce: 10 }, + InboundLanes::::insert( + test_lane_id(), + bp_messages::InboundLaneData { + state: LaneState::Opened, + relayers: Default::default(), + last_confirmed_nonce: 10, + }, ); } @@ -396,28 +293,33 @@ mod tests { nonces_start: bp_messages::MessageNonce, nonces_end: bp_messages::MessageNonce, ) -> bool { - ThisChainRuntimeCall::BridgeMessages( - pallet_bridge_messages::Call::::receive_messages_proof { - relayer_id_at_bridged_chain: 42, - messages_count: nonces_end.checked_sub(nonces_start).map(|x| x + 1).unwrap_or(0) - as u32, - dispatch_weight: frame_support::weights::Weight::zero(), - proof: Box::new(FromBridgedChainMessagesProof { - bridged_header_hash: Default::default(), - storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), - nonces_start, - nonces_end, - }), - }, - ) + RuntimeCall::Messages(crate::Call::::receive_messages_proof { + relayer_id_at_bridged_chain: 42, + messages_count: nonces_end.checked_sub(nonces_start).map(|x| x + 1).unwrap_or(0) as u32, + dispatch_weight: frame_support::weights::Weight::zero(), + proof: Box::new(FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + storage_proof: Default::default(), + lane: test_lane_id(), + nonces_start, + nonces_end, + }), + }) .check_obsolete_call() .is_ok() } + fn run_test(test: impl Fn() -> T) -> T { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + InboundLanes::::insert(test_lane_id(), InboundLaneData::opened()); + OutboundLanes::::insert(test_lane_id(), OutboundLaneData::opened()); + test() + }) + } + #[test] fn extension_rejects_obsolete_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver messages 8..=9 // => tx is rejected deliver_message_10(); @@ -427,7 +329,7 @@ mod tests { #[test] fn extension_rejects_same_message() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to import messages 10..=10 // => tx is rejected deliver_message_10(); @@ -437,7 +339,7 @@ mod tests { #[test] fn extension_rejects_call_with_some_obsolete_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver messages // 10..=15 => tx is rejected deliver_message_10(); @@ -447,7 +349,7 @@ mod tests { #[test] fn extension_rejects_call_with_future_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver messages // 13..=15 => tx is rejected deliver_message_10(); @@ -457,12 +359,12 @@ mod tests { #[test] fn extension_reject_call_when_dispatcher_is_inactive() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver message 11..=15 // => tx is accepted, but we have inactive dispatcher, so... deliver_message_10(); - DummyMessageDispatch::deactivate(); + TestMessageDispatch::deactivate(test_lane_id()); assert!(!validate_message_delivery(11, 15)); }); } @@ -470,7 +372,7 @@ mod tests { #[test] fn extension_rejects_empty_delivery_with_rewards_confirmations_if_there_are_free_relayer_and_message_slots( ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(!validate_message_delivery(10, 9)); }); @@ -479,7 +381,7 @@ mod tests { #[test] fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_relayer_slots( ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); fill_unrewarded_relayers(); assert!(validate_message_delivery(10, 9)); @@ -489,18 +391,18 @@ mod tests { #[test] fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_message_slots( ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { fill_unrewarded_messages(); assert!(validate_message_delivery( - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX - 1 + BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX - 1 )); }); } #[test] fn extension_accepts_new_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver message 11..=15 // => tx is accepted deliver_message_10(); @@ -509,9 +411,10 @@ mod tests { } fn confirm_message_10() { - pallet_bridge_messages::OutboundLanes::::insert( - LaneId([0, 0, 0, 0]), + OutboundLanes::::insert( + test_lane_id(), bp_messages::OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 0, latest_received_nonce: 10, latest_generated_nonce: 10, @@ -520,26 +423,21 @@ mod tests { } fn validate_message_confirmation(last_delivered_nonce: bp_messages::MessageNonce) -> bool { - ThisChainRuntimeCall::BridgeMessages( - pallet_bridge_messages::Call::::receive_messages_delivery_proof { - proof: FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: Default::default(), - storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), - }, - relayers_state: UnrewardedRelayersState { - last_delivered_nonce, - ..Default::default() - }, + RuntimeCall::Messages(crate::Call::::receive_messages_delivery_proof { + proof: FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: Default::default(), + storage_proof: Default::default(), + lane: test_lane_id(), }, - ) + relayers_state: UnrewardedRelayersState { last_delivered_nonce, ..Default::default() }, + }) .check_obsolete_call() .is_ok() } #[test] fn extension_rejects_obsolete_confirmations() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best confirmed is message#10 and we're trying to confirm message#5 => tx // is rejected confirm_message_10(); @@ -549,7 +447,7 @@ mod tests { #[test] fn extension_rejects_same_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best confirmed is message#10 and we're trying to confirm message#10 => // tx is rejected confirm_message_10(); @@ -559,7 +457,7 @@ mod tests { #[test] fn extension_rejects_empty_confirmation_even_if_there_are_no_free_unrewarded_entries() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); fill_unrewarded_relayers(); assert!(!validate_message_confirmation(10)); @@ -568,7 +466,7 @@ mod tests { #[test] fn extension_accepts_new_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best confirmed is message#10 and we're trying to confirm message#15 => // tx is accepted confirm_message_10(); @@ -580,10 +478,10 @@ mod tests { bundled_range: RangeInclusive, is_empty: bool, ) -> bool { - CallHelper::::was_successful(&CallInfo::ReceiveMessagesProof( + CallHelper::::was_successful(&MessagesCallInfo::ReceiveMessagesProof( ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: LaneId([0, 0, 0, 0]), + lane_id: test_lane_id(), bundled_range, best_stored_nonce: 0, // doesn't matter for `was_successful` }, @@ -592,7 +490,7 @@ mod tests { free_message_slots: if is_empty { 0 } else { - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX }, }, }, @@ -602,7 +500,7 @@ mod tests { #[test] #[allow(clippy::reversed_empty_ranges)] fn was_successful_returns_false_for_failed_reward_confirmation_transaction() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { fill_unrewarded_messages(); assert!(!was_message_delivery_successful(10..=9, true)); }); @@ -611,14 +509,14 @@ mod tests { #[test] #[allow(clippy::reversed_empty_ranges)] fn was_successful_returns_true_for_successful_reward_confirmation_transaction() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { assert!(was_message_delivery_successful(10..=9, true)); }); } #[test] fn was_successful_returns_false_for_failed_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(!was_message_delivery_successful(10..=12, false)); }); @@ -626,7 +524,7 @@ mod tests { #[test] fn was_successful_returns_false_for_partially_successful_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(!was_message_delivery_successful(9..=12, false)); }); @@ -634,25 +532,27 @@ mod tests { #[test] fn was_successful_returns_true_for_successful_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(was_message_delivery_successful(9..=10, false)); }); } fn was_message_confirmation_successful(bundled_range: RangeInclusive) -> bool { - CallHelper::::was_successful(&CallInfo::ReceiveMessagesDeliveryProof( - ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: LaneId([0, 0, 0, 0]), - bundled_range, - best_stored_nonce: 0, // doesn't matter for `was_successful` - }), - )) + CallHelper::::was_successful( + &MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( + BaseMessagesProofInfo { + lane_id: test_lane_id(), + bundled_range, + best_stored_nonce: 0, // doesn't matter for `was_successful` + }, + )), + ) } #[test] fn was_successful_returns_false_for_failed_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); assert!(!was_message_confirmation_successful(10..=12)); }); @@ -660,7 +560,7 @@ mod tests { #[test] fn was_successful_returns_false_for_partially_successful_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); assert!(!was_message_confirmation_successful(9..=12)); }); @@ -668,7 +568,7 @@ mod tests { #[test] fn was_successful_returns_true_for_successful_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); assert!(was_message_confirmation_successful(9..=10)); }); diff --git a/bridges/modules/messages/src/inbound_lane.rs b/bridges/modules/messages/src/inbound_lane.rs index 7ef4599a93c4..65240feb7194 100644 --- a/bridges/modules/messages/src/inbound_lane.rs +++ b/bridges/modules/messages/src/inbound_lane.rs @@ -20,8 +20,8 @@ use crate::{BridgedChainOf, Config}; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, - OutboundLaneData, ReceptionResult, UnrewardedRelayer, + ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, LaneState, MessageKey, + MessageNonce, OutboundLaneData, ReceptionResult, UnrewardedRelayer, }; use bp_runtime::AccountIdOf; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; @@ -41,9 +41,11 @@ pub trait InboundLaneStorage { /// Return maximal number of unconfirmed messages in inbound lane. fn max_unconfirmed_messages(&self) -> MessageNonce; /// Get lane data from the storage. - fn get_or_init_data(&mut self) -> InboundLaneData; + fn data(&self) -> InboundLaneData; /// Update lane data in the storage. fn set_data(&mut self, data: InboundLaneData); + /// Purge lane data from the storage. + fn purge(self); } /// Inbound lane data wrapper that implements `MaxEncodedLen`. @@ -120,9 +122,21 @@ impl InboundLane { InboundLane { storage } } - /// Returns `mut` storage reference. - pub fn storage_mut(&mut self) -> &mut S { - &mut self.storage + /// Get lane state. + pub fn state(&self) -> LaneState { + self.storage.data().state + } + + /// Returns storage reference. + pub fn storage(&self) -> &S { + &self.storage + } + + /// Set lane state. + pub fn set_state(&mut self, state: LaneState) { + let mut data = self.storage.data(); + data.state = state; + self.storage.set_data(data); } /// Receive state of the corresponding outbound lane. @@ -130,7 +144,7 @@ impl InboundLane { &mut self, outbound_lane_data: OutboundLaneData, ) -> Option { - let mut data = self.storage.get_or_init_data(); + let mut data = self.storage.data(); let last_delivered_nonce = data.last_delivered_nonce(); if outbound_lane_data.latest_received_nonce > last_delivered_nonce { @@ -173,7 +187,7 @@ impl InboundLane { nonce: MessageNonce, message_data: DispatchMessageData, ) -> ReceptionResult { - let mut data = self.storage.get_or_init_data(); + let mut data = self.storage.data(); if Some(nonce) != data.last_delivered_nonce().checked_add(1) { return ReceptionResult::InvalidNonce } @@ -211,20 +225,17 @@ impl InboundLane { ReceptionResult::Dispatched(dispatch_result) } + + /// Purge lane state from the storage. + pub fn purge(self) { + self.storage.purge() + } } #[cfg(test)] mod tests { use super::*; - use crate::{ - inbound_lane, - tests::mock::{ - dispatch_result, inbound_message_data, inbound_unrewarded_relayers_state, run_test, - unrewarded_relayer, BridgedChain, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, - TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B, TEST_RELAYER_C, - }, - RuntimeInboundLaneStorage, - }; + use crate::{active_inbound_lane, lanes_manager::RuntimeInboundLaneStorage, tests::mock::*}; use bp_messages::UnrewardedRelayersState; fn receive_regular_message( @@ -244,7 +255,7 @@ mod tests { #[test] fn receive_status_update_ignores_status_from_the_future() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); assert_eq!( lane.receive_state_update(OutboundLaneData { @@ -254,14 +265,14 @@ mod tests { None, ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 0); + assert_eq!(lane.storage.data().last_confirmed_nonce, 0); }); } #[test] fn receive_status_update_ignores_obsolete_status() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); receive_regular_message(&mut lane, 2); receive_regular_message(&mut lane, 3); @@ -272,7 +283,7 @@ mod tests { }), Some(3), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); assert_eq!( lane.receive_state_update(OutboundLaneData { @@ -281,20 +292,20 @@ mod tests { }), None, ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); }); } #[test] fn receive_status_update_works() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); receive_regular_message(&mut lane, 2); receive_regular_message(&mut lane, 3); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 0); + assert_eq!(lane.storage.data().last_confirmed_nonce, 0); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![unrewarded_relayer(1, 3, TEST_RELAYER_A)] ); @@ -305,9 +316,9 @@ mod tests { }), Some(2), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 2); + assert_eq!(lane.storage.data().last_confirmed_nonce, 2); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![unrewarded_relayer(3, 3, TEST_RELAYER_A)] ); @@ -318,16 +329,16 @@ mod tests { }), Some(3), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); - assert_eq!(lane.storage.get_or_init_data().relayers, vec![]); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().relayers, vec![]); }); } #[test] fn receive_status_update_works_with_batches_from_relayers() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - let mut seed_storage_data = lane.storage.get_or_init_data(); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); + let mut seed_storage_data = lane.storage.data(); // Prepare data seed_storage_data.last_confirmed_nonce = 0; seed_storage_data.relayers.push_back(unrewarded_relayer(1, 1, TEST_RELAYER_A)); @@ -343,9 +354,9 @@ mod tests { }), Some(3), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![ unrewarded_relayer(4, 4, TEST_RELAYER_B), unrewarded_relayer(5, 5, TEST_RELAYER_C) @@ -357,7 +368,7 @@ mod tests { #[test] fn fails_to_receive_message_with_incorrect_nonce() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); assert_eq!( lane.receive_message::( &TEST_RELAYER_A, @@ -366,14 +377,14 @@ mod tests { ), ReceptionResult::InvalidNonce ); - assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 0); + assert_eq!(lane.storage.data().last_delivered_nonce(), 0); }); } #[test] fn fails_to_receive_messages_above_unrewarded_relayer_entries_limit_per_lane() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); let max_nonce = BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; for current_nonce in 1..max_nonce + 1 { assert_eq!( @@ -409,7 +420,7 @@ mod tests { #[test] fn fails_to_receive_messages_above_unconfirmed_messages_limit_per_lane() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); let max_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; for current_nonce in 1..=max_nonce { assert_eq!( @@ -445,7 +456,7 @@ mod tests { #[test] fn correctly_receives_following_messages_from_two_relayers_alternately() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); assert_eq!( lane.receive_message::( &TEST_RELAYER_A, @@ -471,7 +482,7 @@ mod tests { ReceptionResult::Dispatched(dispatch_result(0)) ); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), unrewarded_relayer(2, 2, TEST_RELAYER_B), @@ -484,7 +495,7 @@ mod tests { #[test] fn rejects_same_message_from_two_different_relayers() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); assert_eq!( lane.receive_message::( &TEST_RELAYER_A, @@ -507,16 +518,16 @@ mod tests { #[test] fn correct_message_is_processed_instantly() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); - assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 1); + assert_eq!(lane.storage.data().last_delivered_nonce(), 1); }); } #[test] fn unspent_weight_is_returned_by_receive_message() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); let mut payload = REGULAR_PAYLOAD; *payload.dispatch_result.unspent_weight.ref_time_mut() = 1; assert_eq!( @@ -533,7 +544,7 @@ mod tests { #[test] fn first_message_is_confirmed_correctly() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); receive_regular_message(&mut lane, 2); assert_eq!( @@ -544,7 +555,7 @@ mod tests { Some(1), ); assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), + inbound_unrewarded_relayers_state(test_lane_id()), UnrewardedRelayersState { unrewarded_relayer_entries: 1, messages_in_oldest_entry: 1, diff --git a/bridges/modules/messages/src/lanes_manager.rs b/bridges/modules/messages/src/lanes_manager.rs new file mode 100644 index 000000000000..4f5ac1c0a403 --- /dev/null +++ b/bridges/modules/messages/src/lanes_manager.rs @@ -0,0 +1,283 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +use crate::{ + BridgedChainOf, Config, InboundLane, InboundLaneStorage, InboundLanes, OutboundLane, + OutboundLaneStorage, OutboundLanes, OutboundMessages, StoredInboundLaneData, + StoredMessagePayload, +}; + +use bp_messages::{ + target_chain::MessageDispatch, ChainWithMessages, InboundLaneData, LaneId, LaneState, + MessageKey, MessageNonce, OutboundLaneData, +}; +use bp_runtime::AccountIdOf; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ensure, sp_runtime::RuntimeDebug, PalletError}; +use scale_info::TypeInfo; +use sp_std::marker::PhantomData; + +/// Lanes manager errors. +#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] +pub enum LanesManagerError { + /// Inbound lane already exists. + InboundLaneAlreadyExists, + /// Outbound lane already exists. + OutboundLaneAlreadyExists, + /// No inbound lane with given id. + UnknownInboundLane, + /// No outbound lane with given id. + UnknownOutboundLane, + /// Inbound lane with given id is closed. + ClosedInboundLane, + /// Outbound lane with given id is closed. + ClosedOutboundLane, + /// Message dispatcher is inactive at given inbound lane. This is logical equivalent + /// of the [`Self::ClosedInboundLane`] variant. + LaneDispatcherInactive, +} + +/// Message lanes manager. +pub struct LanesManager(PhantomData<(T, I)>); + +impl, I: 'static> Default for LanesManager { + fn default() -> Self { + Self::new() + } +} + +impl, I: 'static> LanesManager { + /// Create new lanes manager. + pub fn new() -> Self { + Self(PhantomData) + } + + /// Create new inbound lane in `Opened` state. + pub fn create_inbound_lane( + &self, + lane_id: LaneId, + ) -> Result>, LanesManagerError> { + InboundLanes::::try_mutate(lane_id, |lane| match lane { + Some(_) => Err(LanesManagerError::InboundLaneAlreadyExists), + None => { + *lane = Some(StoredInboundLaneData(InboundLaneData { + state: LaneState::Opened, + ..Default::default() + })); + Ok(()) + }, + })?; + + self.active_inbound_lane(lane_id) + } + + /// Create new outbound lane in `Opened` state. + pub fn create_outbound_lane( + &self, + lane_id: LaneId, + ) -> Result>, LanesManagerError> { + OutboundLanes::::try_mutate(lane_id, |lane| match lane { + Some(_) => Err(LanesManagerError::OutboundLaneAlreadyExists), + None => { + *lane = Some(OutboundLaneData { state: LaneState::Opened, ..Default::default() }); + Ok(()) + }, + })?; + + self.active_outbound_lane(lane_id) + } + + /// Get existing inbound lane, checking that it is in usable state. + pub fn active_inbound_lane( + &self, + lane_id: LaneId, + ) -> Result>, LanesManagerError> { + Ok(InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id, true)?)) + } + + /// Get existing outbound lane, checking that it is in usable state. + pub fn active_outbound_lane( + &self, + lane_id: LaneId, + ) -> Result>, LanesManagerError> { + Ok(OutboundLane::new(RuntimeOutboundLaneStorage::from_lane_id(lane_id, true)?)) + } + + /// Get existing inbound lane without any additional state checks. + pub fn any_state_inbound_lane( + &self, + lane_id: LaneId, + ) -> Result>, LanesManagerError> { + Ok(InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id, false)?)) + } + + /// Get existing outbound lane without any additional state checks. + pub fn any_state_outbound_lane( + &self, + lane_id: LaneId, + ) -> Result>, LanesManagerError> { + Ok(OutboundLane::new(RuntimeOutboundLaneStorage::from_lane_id(lane_id, false)?)) + } +} + +/// Runtime inbound lane storage. +pub struct RuntimeInboundLaneStorage, I: 'static = ()> { + pub(crate) lane_id: LaneId, + pub(crate) cached_data: InboundLaneData>>, +} + +impl, I: 'static> RuntimeInboundLaneStorage { + /// Creates new runtime inbound lane storage for given **existing** lane. + fn from_lane_id( + lane_id: LaneId, + check_active: bool, + ) -> Result, LanesManagerError> { + let cached_data = + InboundLanes::::get(lane_id).ok_or(LanesManagerError::UnknownInboundLane)?; + + if check_active { + // check that the lane is not explicitly closed + ensure!(cached_data.state.is_active(), LanesManagerError::ClosedInboundLane); + // apart from the explicit closure, the lane may be unable to receive any messages. + // Right now we do an additional check here, but it may be done later (e.g. by + // explicitly closing the lane and reopening it from + // `pallet-xcm-bridge-hub::on-initialize`) + // + // The fact that we only check it here, means that the `MessageDispatch` may switch + // to inactive state during some message dispatch in the middle of message delivery + // transaction. But we treat result of `MessageDispatch::is_active()` as a hint, so + // we know that it won't drop messages - just it experiences problems with processing. + // This would allow us to check that in our signed extensions, and invalidate + // transaction early, thus avoiding losing honest relayers funds. This problem should + // gone with relayers coordination protocol. + // + // There's a limit on number of messages in the message delivery transaction, so even + // if we dispatch (enqueue) some additional messages, we'll know the maximal queue + // length; + ensure!( + T::MessageDispatch::is_active(lane_id), + LanesManagerError::LaneDispatcherInactive + ); + } + + Ok(RuntimeInboundLaneStorage { lane_id, cached_data: cached_data.into() }) + } + + /// Returns number of bytes that may be subtracted from the PoV component of + /// `receive_messages_proof` call, because the actual inbound lane state is smaller than the + /// maximal configured. + /// + /// Maximal inbound lane state set size is configured by the + /// `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` constant from the pallet configuration. The PoV + /// of the call includes the maximal size of inbound lane state. If the actual size is smaller, + /// we may subtract extra bytes from this component. + pub fn extra_proof_size_bytes(&self) -> u64 { + let max_encoded_len = StoredInboundLaneData::::max_encoded_len(); + let relayers_count = self.data().relayers.len(); + let actual_encoded_len = + InboundLaneData::>>::encoded_size_hint(relayers_count) + .unwrap_or(usize::MAX); + max_encoded_len.saturating_sub(actual_encoded_len) as _ + } +} + +impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { + type Relayer = AccountIdOf>; + + fn id(&self) -> LaneId { + self.lane_id + } + + fn max_unrewarded_relayer_entries(&self) -> MessageNonce { + BridgedChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX + } + + fn max_unconfirmed_messages(&self) -> MessageNonce { + BridgedChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + } + + fn data(&self) -> InboundLaneData>> { + self.cached_data.clone() + } + + fn set_data(&mut self, data: InboundLaneData>>) { + self.cached_data = data.clone(); + InboundLanes::::insert(self.lane_id, StoredInboundLaneData::(data)) + } + + fn purge(self) { + InboundLanes::::remove(self.lane_id) + } +} + +/// Runtime outbound lane storage. +#[derive(Debug, PartialEq, Eq)] +pub struct RuntimeOutboundLaneStorage { + pub(crate) lane_id: LaneId, + pub(crate) cached_data: OutboundLaneData, + pub(crate) _phantom: PhantomData<(T, I)>, +} + +impl, I: 'static> RuntimeOutboundLaneStorage { + /// Creates new runtime outbound lane storage for given **existing** lane. + fn from_lane_id(lane_id: LaneId, check_active: bool) -> Result { + let cached_data = + OutboundLanes::::get(lane_id).ok_or(LanesManagerError::UnknownOutboundLane)?; + ensure!( + !check_active || cached_data.state.is_active(), + LanesManagerError::ClosedOutboundLane + ); + Ok(Self { lane_id, cached_data, _phantom: PhantomData }) + } +} + +impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { + type StoredMessagePayload = StoredMessagePayload; + + fn id(&self) -> LaneId { + self.lane_id + } + + fn data(&self) -> OutboundLaneData { + self.cached_data.clone() + } + + fn set_data(&mut self, data: OutboundLaneData) { + self.cached_data = data.clone(); + OutboundLanes::::insert(self.lane_id, data) + } + + #[cfg(test)] + fn message(&self, nonce: &MessageNonce) -> Option { + OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) + .map(Into::into) + } + + fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload) { + OutboundMessages::::insert( + MessageKey { lane_id: self.lane_id, nonce }, + message_payload, + ); + } + + fn remove_message(&mut self, nonce: &MessageNonce) { + OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); + } + + fn purge(self) { + OutboundLanes::::remove(self.lane_id) + } +} diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs index c36313a14764..b7fe1c7dbb19 100644 --- a/bridges/modules/messages/src/lib.rs +++ b/bridges/modules/messages/src/lib.rs @@ -36,8 +36,13 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -pub use inbound_lane::StoredInboundLaneData; -pub use outbound_lane::StoredMessagePayload; +pub use inbound_lane::{InboundLane, InboundLaneStorage, StoredInboundLaneData}; +pub use lanes_manager::{ + LanesManager, LanesManagerError, RuntimeInboundLaneStorage, RuntimeOutboundLaneStorage, +}; +pub use outbound_lane::{ + OutboundLane, OutboundLaneStorage, ReceptionConfirmationError, StoredMessagePayload, +}; pub use weights::WeightInfo; pub use weights_ext::{ ensure_able_to_receive_confirmation, ensure_able_to_receive_message, @@ -45,11 +50,6 @@ pub use weights_ext::{ EXPECTED_DEFAULT_MESSAGE_LENGTH, EXTRA_STORAGE_PROOF_SIZE, }; -use crate::{ - inbound_lane::{InboundLane, InboundLaneStorage}, - outbound_lane::{OutboundLane, OutboundLaneStorage, ReceptionConfirmationError}, -}; - use bp_header_chain::HeaderChain; use bp_messages::{ source_chain::{ @@ -68,11 +68,13 @@ use bp_runtime::{ AccountIdOf, BasicOperatingMode, HashOf, OwnedBridgeModule, PreComputedSize, RangeInclusiveExt, Size, }; -use codec::{Decode, Encode, MaxEncodedLen}; +use codec::{Decode, Encode}; use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get, DefaultNoBound}; use sp_std::{marker::PhantomData, prelude::*}; +mod call_ext; mod inbound_lane; +mod lanes_manager; mod outbound_lane; mod proofs; mod tests; @@ -82,7 +84,9 @@ pub mod weights; #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +pub mod migration; +pub use call_ext::*; pub use pallet::*; #[cfg(feature = "test-helpers")] pub use tests::*; @@ -115,9 +119,6 @@ pub mod pallet { /// Bridged chain headers provider. type BridgedHeaderChain: HeaderChain; - /// Get all active outbound lanes that the message pallet is serving. - type ActiveOutboundLanes: Get<&'static [LaneId]>; - /// Payload type of outbound messages. This payload is dispatched on the bridged chain. type OutboundPayload: Parameter + Size; /// Payload type of inbound messages. This payload is dispatched on this chain. @@ -143,6 +144,7 @@ pub mod pallet { pub type BridgedHeaderChainOf = >::BridgedHeaderChain; #[pallet::pallet] + #[pallet::storage_version(migration::STORAGE_VERSION)] pub struct Pallet(PhantomData<(T, I)>); impl, I: 'static> OwnedBridgeModule for Pallet { @@ -215,9 +217,6 @@ pub mod pallet { Error::::TooManyMessagesInTheProof ); - // if message dispatcher is currently inactive, we won't accept any messages - ensure!(T::MessageDispatch::is_active(), Error::::MessageDispatchInactive); - // why do we need to know the weight of this (`receive_messages_proof`) call? Because // we may want to return some funds for not-dispatching (or partially dispatching) some // messages to the call origin (relayer). And this is done by returning actual weight @@ -236,92 +235,89 @@ pub mod pallet { let mut actual_weight = declared_weight; // verify messages proof && convert proof into messages - let messages = verify_and_decode_messages_proof::(*proof, messages_count) - .map_err(|err| { - log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); + let (lane_id, lane_data) = + verify_and_decode_messages_proof::(*proof, messages_count).map_err( + |err| { + log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); - Error::::InvalidMessagesProof - })?; + Error::::InvalidMessagesProof + }, + )?; // dispatch messages and (optionally) update lane(s) state(s) let mut total_messages = 0; let mut valid_messages = 0; - let mut messages_received_status = Vec::with_capacity(messages.len()); let mut dispatch_weight_left = dispatch_weight; - for (lane_id, lane_data) in messages { - let mut lane = inbound_lane::(lane_id); - - // subtract extra storage proof bytes from the actual PoV size - there may be - // less unrewarded relayers than the maximal configured value - let lane_extra_proof_size_bytes = lane.storage_mut().extra_proof_size_bytes(); - actual_weight = actual_weight.set_proof_size( - actual_weight.proof_size().saturating_sub(lane_extra_proof_size_bytes), - ); + let mut lane = active_inbound_lane::(lane_id)?; - if let Some(lane_state) = lane_data.lane_state { - let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); - if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { - log::trace!( - target: LOG_TARGET, - "Received lane {:?} state update: latest_confirmed_nonce={}. Unrewarded relayers: {:?}", - lane_id, - updated_latest_confirmed_nonce, - UnrewardedRelayersState::from(&lane.storage_mut().get_or_init_data()), - ); - } - } + // subtract extra storage proof bytes from the actual PoV size - there may be + // less unrewarded relayers than the maximal configured value + let lane_extra_proof_size_bytes = lane.storage().extra_proof_size_bytes(); + actual_weight = actual_weight.set_proof_size( + actual_weight.proof_size().saturating_sub(lane_extra_proof_size_bytes), + ); - let mut lane_messages_received_status = - ReceivedMessages::new(lane_id, Vec::with_capacity(lane_data.messages.len())); - for mut message in lane_data.messages { - debug_assert_eq!(message.key.lane_id, lane_id); - total_messages += 1; - - // ensure that relayer has declared enough weight for dispatching next message - // on this lane. We can't dispatch lane messages out-of-order, so if declared - // weight is not enough, let's move to next lane - let message_dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); - if message_dispatch_weight.any_gt(dispatch_weight_left) { - log::trace!( - target: LOG_TARGET, - "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", - lane_id, - message_dispatch_weight, - dispatch_weight_left, - ); - - fail!(Error::::InsufficientDispatchWeight); - } + if let Some(lane_state) = lane_data.lane_state { + let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); + if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { + log::trace!( + target: LOG_TARGET, + "Received lane {:?} state update: latest_confirmed_nonce={}. Unrewarded relayers: {:?}", + lane_id, + updated_latest_confirmed_nonce, + UnrewardedRelayersState::from(&lane.storage().data()), + ); + } + } - let receival_result = lane.receive_message::( - &relayer_id_at_bridged_chain, - message.key.nonce, - message.data, + let mut messages_received_status = + ReceivedMessages::new(lane_id, Vec::with_capacity(lane_data.messages.len())); + for mut message in lane_data.messages { + debug_assert_eq!(message.key.lane_id, lane_id); + total_messages += 1; + + // ensure that relayer has declared enough weight for dispatching next message + // on this lane. We can't dispatch lane messages out-of-order, so if declared + // weight is not enough, let's move to next lane + let message_dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); + if message_dispatch_weight.any_gt(dispatch_weight_left) { + log::trace!( + target: LOG_TARGET, + "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", + lane_id, + message_dispatch_weight, + dispatch_weight_left, ); - // note that we're returning unspent weight to relayer even if message has been - // rejected by the lane. This allows relayers to submit spam transactions with - // e.g. the same set of already delivered messages over and over again, without - // losing funds for messages dispatch. But keep in mind that relayer pays base - // delivery transaction cost anyway. And base cost covers everything except - // dispatch, so we have a balance here. - let unspent_weight = match &receival_result { - ReceptionResult::Dispatched(dispatch_result) => { - valid_messages += 1; - dispatch_result.unspent_weight - }, - ReceptionResult::InvalidNonce | - ReceptionResult::TooManyUnrewardedRelayers | - ReceptionResult::TooManyUnconfirmedMessages => message_dispatch_weight, - }; - lane_messages_received_status.push(message.key.nonce, receival_result); - - let unspent_weight = unspent_weight.min(message_dispatch_weight); - dispatch_weight_left -= message_dispatch_weight - unspent_weight; - actual_weight = actual_weight.saturating_sub(unspent_weight); + fail!(Error::::InsufficientDispatchWeight); } - messages_received_status.push(lane_messages_received_status); + let receival_result = lane.receive_message::( + &relayer_id_at_bridged_chain, + message.key.nonce, + message.data, + ); + + // note that we're returning unspent weight to relayer even if message has been + // rejected by the lane. This allows relayers to submit spam transactions with + // e.g. the same set of already delivered messages over and over again, without + // losing funds for messages dispatch. But keep in mind that relayer pays base + // delivery transaction cost anyway. And base cost covers everything except + // dispatch, so we have a balance here. + let unspent_weight = match &receival_result { + ReceptionResult::Dispatched(dispatch_result) => { + valid_messages += 1; + dispatch_result.unspent_weight + }, + ReceptionResult::InvalidNonce | + ReceptionResult::TooManyUnrewardedRelayers | + ReceptionResult::TooManyUnconfirmedMessages => message_dispatch_weight, + }; + messages_received_status.push(message.key.nonce, receival_result); + + let unspent_weight = unspent_weight.min(message_dispatch_weight); + dispatch_weight_left -= message_dispatch_weight - unspent_weight; + actual_weight = actual_weight.saturating_sub(unspent_weight); } // let's now deal with relayer payments @@ -377,7 +373,7 @@ pub mod pallet { ); // mark messages as delivered - let mut lane = outbound_lane::(lane_id); + let mut lane = any_state_outbound_lane::(lane_id)?; let last_delivered_nonce = lane_data.last_delivered_nonce(); let confirmed_messages = lane .confirm_delivery( @@ -452,7 +448,7 @@ pub mod pallet { /// Messages have been received from the bridged chain. MessagesReceived( /// Result of received messages dispatch. - Vec::DispatchLevelResult>>, + ReceivedMessages<::DispatchLevelResult>, ), /// Messages in the inclusive range have been delivered to the bridged chain. MessagesDelivered { @@ -468,14 +464,10 @@ pub mod pallet { pub enum Error { /// Pallet is not in Normal operating mode. NotOperatingNormally, - /// The outbound lane is inactive. - InactiveOutboundLane, - /// The inbound message dispatcher is inactive. - MessageDispatchInactive, + /// Error that is reported by the lanes manager. + LanesManager(LanesManagerError), /// Message has been treated as invalid by the pallet logic. MessageRejectedByPallet(VerificationError), - /// Submitter has failed to pay fee for delivering and dispatching messages. - FailedToWithdrawMessageFee, /// The transaction brings too many messages. TooManyMessagesInTheProof, /// Invalid messages has been submitted. @@ -488,8 +480,6 @@ pub mod pallet { /// The cumulative dispatch weight, passed by relayer is not enough to cover dispatch /// of all bundled messages. InsufficientDispatchWeight, - /// The message someone is trying to work with (i.e. increase fee) is not yet sent. - MessageIsNotYetSent, /// Error confirming messages receival. ReceptionConfirmation(ReceptionConfirmationError), /// Error generated by the `OwnedBridgeModule` trait. @@ -514,10 +504,13 @@ pub mod pallet { pub type PalletOperatingMode, I: 'static = ()> = StorageValue<_, MessagesOperatingMode, ValueQuery>; + // TODO: https://github.com/paritytech/parity-bridges-common/pull/2213: let's limit number of + // possible opened lanes && use it to constraint maps below + /// Map of lane id => inbound lane data. #[pallet::storage] pub type InboundLanes, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, ValueQuery>; + StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, OptionQuery>; /// Map of lane id => outbound lane data. #[pallet::storage] @@ -525,28 +518,7 @@ pub mod pallet { Hasher = Blake2_128Concat, Key = LaneId, Value = OutboundLaneData, - QueryKind = ValueQuery, - OnEmpty = GetDefault, - MaxValues = MaybeOutboundLanesCount, - >; - - /// Map of lane id => is congested signal sent. It is managed by the - /// `bridge_runtime_common::LocalXcmQueueManager`. - /// - /// **bridges-v1**: this map is a temporary hack and will be dropped in the `v2`. We can emulate - /// a storage map using `sp_io::unhashed` storage functions, but then benchmarks are not - /// accounting its `proof_size`, so it is missing from the final weights. So we need to make it - /// a map inside some pallet. We could use a simply value instead of map here, because - /// in `v1` we'll only have a single lane. But in the case of adding another lane before `v2`, - /// it'll be easier to deal with the isolated storage map instead. - #[pallet::storage] - pub type OutboundLanesCongestedSignals, I: 'static = ()> = StorageMap< - Hasher = Blake2_128Concat, - Key = LaneId, - Value = bool, - QueryKind = ValueQuery, - OnEmpty = GetDefault, - MaxValues = MaybeOutboundLanesCount, + QueryKind = OptionQuery, >; /// All queued outbound messages. @@ -561,8 +533,11 @@ pub mod pallet { pub operating_mode: MessagesOperatingMode, /// Initial pallet owner. pub owner: Option, + /// Opened lanes. + pub opened_lanes: Vec, /// Dummy marker. - pub phantom: sp_std::marker::PhantomData, + #[serde(skip)] + pub _phantom: sp_std::marker::PhantomData, } #[pallet::genesis_build] @@ -572,6 +547,11 @@ pub mod pallet { if let Some(ref owner) = self.owner { PalletOwner::::put(owner); } + + for lane_id in &self.opened_lanes { + InboundLanes::::insert(lane_id, InboundLaneData::opened()); + OutboundLanes::::insert(lane_id, OutboundLaneData::opened()); + } } } @@ -605,15 +585,15 @@ pub mod pallet { } /// Return outbound lane data. - pub fn outbound_lane_data(lane: LaneId) -> OutboundLaneData { + pub fn outbound_lane_data(lane: LaneId) -> Option { OutboundLanes::::get(lane) } /// Return inbound lane data. pub fn inbound_lane_data( lane: LaneId, - ) -> InboundLaneData>> { - InboundLanes::::get(lane).0 + ) -> Option>>> { + InboundLanes::::get(lane).map(|lane| lane.0) } } @@ -668,15 +648,6 @@ pub mod pallet { Ok(()) } } - - /// Get-parameter that returns number of active outbound lanes that the pallet maintains. - pub struct MaybeOutboundLanesCount(PhantomData<(T, I)>); - - impl, I: 'static> Get> for MaybeOutboundLanesCount { - fn get() -> Option { - Some(T::ActiveOutboundLanes::get().len() as u32) - } - } } /// Structure, containing a validated message payload and all the info required @@ -684,6 +655,7 @@ pub mod pallet { #[derive(Debug, PartialEq, Eq)] pub struct SendMessageArgs, I: 'static> { lane_id: LaneId, + lane: OutboundLane>, payload: StoredMessagePayload, } @@ -696,16 +668,18 @@ where type SendMessageArgs = SendMessageArgs; fn validate_message( - lane: LaneId, + lane_id: LaneId, message: &T::OutboundPayload, ) -> Result, Self::Error> { + // we can't accept any messages if the pallet is halted ensure_normal_operating_mode::()?; - // let's check if outbound lane is active - ensure!(T::ActiveOutboundLanes::get().contains(&lane), Error::::InactiveOutboundLane); + // check lane + let lane = active_outbound_lane::(lane_id)?; Ok(SendMessageArgs { - lane_id: lane, + lane_id, + lane, payload: StoredMessagePayload::::try_from(message.encode()).map_err(|_| { Error::::MessageRejectedByPallet(VerificationError::MessageTooLarge) })?, @@ -714,7 +688,7 @@ where fn send_message(args: SendMessageArgs) -> SendMessageArtifacts { // save message in outbound storage and emit event - let mut lane = outbound_lane::(args.lane_id); + let mut lane = args.lane; let message_len = args.payload.len(); let nonce = lane.send_message(args.payload); @@ -746,122 +720,31 @@ fn ensure_normal_operating_mode, I: 'static>() -> Result<(), Error< Err(Error::::NotOperatingNormally) } -/// Creates new inbound lane object, backed by runtime storage. -fn inbound_lane, I: 'static>( +/// Creates new inbound lane object, backed by runtime storage. Lane must be active. +fn active_inbound_lane, I: 'static>( lane_id: LaneId, -) -> InboundLane> { - InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id)) +) -> Result>, Error> { + LanesManager::::new() + .active_inbound_lane(lane_id) + .map_err(Error::LanesManager) } -/// Creates new outbound lane object, backed by runtime storage. -fn outbound_lane, I: 'static>( +/// Creates new outbound lane object, backed by runtime storage. Lane must be active. +fn active_outbound_lane, I: 'static>( lane_id: LaneId, -) -> OutboundLane> { - OutboundLane::new(RuntimeOutboundLaneStorage { lane_id, _phantom: Default::default() }) +) -> Result>, Error> { + LanesManager::::new() + .active_outbound_lane(lane_id) + .map_err(Error::LanesManager) } -/// Runtime inbound lane storage. -struct RuntimeInboundLaneStorage, I: 'static = ()> { - lane_id: LaneId, - cached_data: Option>>>, - _phantom: PhantomData, -} - -impl, I: 'static> RuntimeInboundLaneStorage { - /// Creates new runtime inbound lane storage. - fn from_lane_id(lane_id: LaneId) -> RuntimeInboundLaneStorage { - RuntimeInboundLaneStorage { lane_id, cached_data: None, _phantom: Default::default() } - } -} - -impl, I: 'static> RuntimeInboundLaneStorage { - /// Returns number of bytes that may be subtracted from the PoV component of - /// `receive_messages_proof` call, because the actual inbound lane state is smaller than the - /// maximal configured. - /// - /// Maximal inbound lane state set size is configured by the - /// `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` constant from the pallet configuration. The PoV - /// of the call includes the maximal size of inbound lane state. If the actual size is smaller, - /// we may subtract extra bytes from this component. - pub fn extra_proof_size_bytes(&mut self) -> u64 { - let max_encoded_len = StoredInboundLaneData::::max_encoded_len(); - let relayers_count = self.get_or_init_data().relayers.len(); - let actual_encoded_len = - InboundLaneData::>>::encoded_size_hint(relayers_count) - .unwrap_or(usize::MAX); - max_encoded_len.saturating_sub(actual_encoded_len) as _ - } -} - -impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { - type Relayer = AccountIdOf>; - - fn id(&self) -> LaneId { - self.lane_id - } - - fn max_unrewarded_relayer_entries(&self) -> MessageNonce { - BridgedChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX - } - - fn max_unconfirmed_messages(&self) -> MessageNonce { - BridgedChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX - } - - fn get_or_init_data(&mut self) -> InboundLaneData>> { - match self.cached_data { - Some(ref data) => data.clone(), - None => { - let data: InboundLaneData>> = - InboundLanes::::get(self.lane_id).into(); - self.cached_data = Some(data.clone()); - data - }, - } - } - - fn set_data(&mut self, data: InboundLaneData>>) { - self.cached_data = Some(data.clone()); - InboundLanes::::insert(self.lane_id, StoredInboundLaneData::(data)) - } -} - -/// Runtime outbound lane storage. -struct RuntimeOutboundLaneStorage { +/// Creates new outbound lane object, backed by runtime storage. +fn any_state_outbound_lane, I: 'static>( lane_id: LaneId, - _phantom: PhantomData<(T, I)>, -} - -impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { - type StoredMessagePayload = StoredMessagePayload; - - fn id(&self) -> LaneId { - self.lane_id - } - - fn data(&self) -> OutboundLaneData { - OutboundLanes::::get(self.lane_id) - } - - fn set_data(&mut self, data: OutboundLaneData) { - OutboundLanes::::insert(self.lane_id, data) - } - - #[cfg(test)] - fn message(&self, nonce: &MessageNonce) -> Option { - OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) - } - - fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload) { - OutboundMessages::::insert( - MessageKey { lane_id: self.lane_id, nonce }, - message_payload, - ); - } - - fn remove_message(&mut self, nonce: &MessageNonce) { - OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); - } +) -> Result>, Error> { + LanesManager::::new() + .any_state_outbound_lane(lane_id) + .map_err(Error::LanesManager) } /// Verify messages proof and return proved messages with decoded payload. @@ -872,18 +755,13 @@ fn verify_and_decode_messages_proof, I: 'static>( // `receive_messages_proof` weight formula and `MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX` // check guarantees that the `message_count` is sane and Vec may be allocated. // (tx with too many messages will either be rejected from the pool, or will fail earlier) - proofs::verify_messages_proof::(proof, messages_count).map(|messages_by_lane| { - messages_by_lane - .into_iter() - .map(|(lane, lane_data)| { - ( - lane, - ProvedLaneMessages { - lane_state: lane_data.lane_state, - messages: lane_data.messages.into_iter().map(Into::into).collect(), - }, - ) - }) - .collect() + proofs::verify_messages_proof::(proof, messages_count).map(|(lane, lane_data)| { + ( + lane, + ProvedLaneMessages { + lane_state: lane_data.lane_state, + messages: lane_data.messages.into_iter().map(Into::into).collect(), + }, + ) }) } diff --git a/bridges/modules/messages/src/migration.rs b/bridges/modules/messages/src/migration.rs new file mode 100644 index 000000000000..dc9a8119079e --- /dev/null +++ b/bridges/modules/messages/src/migration.rs @@ -0,0 +1,146 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! A module that is responsible for migration of storage. + +use crate::{Config, Pallet}; +use frame_support::{ + traits::{Get, StorageVersion}, + weights::Weight, +}; + +/// The in-code storage version. +pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + +/// This module contains data structures that are valid for the initial state of `0`. +/// (used with v1 migration). +pub mod v0 { + use super::Config; + use crate::BridgedChainOf; + use bp_messages::{MessageNonce, UnrewardedRelayer}; + use bp_runtime::AccountIdOf; + use codec::{Decode, Encode}; + use sp_std::collections::vec_deque::VecDeque; + + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub(crate) struct StoredInboundLaneData, I: 'static>( + pub(crate) InboundLaneData>>, + ); + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub(crate) struct InboundLaneData { + pub(crate) relayers: VecDeque>, + pub(crate) last_confirmed_nonce: MessageNonce, + } + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub(crate) struct OutboundLaneData { + pub(crate) oldest_unpruned_nonce: MessageNonce, + pub(crate) latest_received_nonce: MessageNonce, + pub(crate) latest_generated_nonce: MessageNonce, + } +} + +/// This migration to `1` updates the metadata of `InboundLanes` and `OutboundLanes` to the new +/// structures. +pub mod v1 { + use super::*; + use crate::{ + InboundLaneData, InboundLanes, OutboundLaneData, OutboundLanes, StoredInboundLaneData, + }; + use bp_messages::LaneState; + use frame_support::traits::UncheckedOnRuntimeUpgrade; + use sp_std::marker::PhantomData; + + /// Migrates the pallet storage to v1. + pub struct UncheckedMigrationV0ToV1(PhantomData<(T, I)>); + + impl, I: 'static> UncheckedOnRuntimeUpgrade for UncheckedMigrationV0ToV1 { + fn on_runtime_upgrade() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + + // `InboundLanes` - add state to the old structs + let translate_inbound = + |pre: v0::StoredInboundLaneData| -> Option> { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + Some(v1::StoredInboundLaneData(v1::InboundLaneData { + state: LaneState::Opened, + relayers: pre.0.relayers, + last_confirmed_nonce: pre.0.last_confirmed_nonce, + })) + }; + InboundLanes::::translate_values(translate_inbound); + + // `OutboundLanes` - add state to the old structs + let translate_outbound = |pre: v0::OutboundLaneData| -> Option { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + Some(v1::OutboundLaneData { + state: LaneState::Opened, + oldest_unpruned_nonce: pre.oldest_unpruned_nonce, + latest_received_nonce: pre.latest_received_nonce, + latest_generated_nonce: pre.latest_generated_nonce, + }) + }; + OutboundLanes::::translate_values(translate_outbound); + + weight + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::DispatchError> { + use codec::Encode; + + let number_of_inbound_to_migrate = InboundLanes::::iter_keys().count(); + let number_of_outbound_to_migrate = OutboundLanes::::iter_keys().count(); + Ok((number_of_inbound_to_migrate as u32, number_of_outbound_to_migrate as u32).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: sp_std::vec::Vec) -> Result<(), sp_runtime::DispatchError> { + use codec::Decode; + const LOG_TARGET: &str = "runtime::bridge-messages-migration"; + + let (number_of_inbound_to_migrate, number_of_outbound_to_migrate): (u32, u32) = + Decode::decode(&mut &state[..]).unwrap(); + let number_of_inbound = InboundLanes::::iter_keys().count(); + let number_of_outbound = OutboundLanes::::iter_keys().count(); + + log::info!(target: LOG_TARGET, "post-upgrade expects '{number_of_inbound_to_migrate}' inbound lanes to have been migrated."); + log::info!(target: LOG_TARGET, "post-upgrade expects '{number_of_outbound_to_migrate}' outbound lanes to have been migrated."); + + frame_support::ensure!( + number_of_inbound_to_migrate as usize == number_of_inbound, + "must migrate all `InboundLanes`." + ); + frame_support::ensure!( + number_of_outbound_to_migrate as usize == number_of_outbound, + "must migrate all `OutboundLanes`." + ); + + log::info!(target: LOG_TARGET, "migrated all."); + Ok(()) + } + } + + /// [`UncheckedMigrationV0ToV1`] wrapped in a + /// [`VersionedMigration`](frame_support::migrations::VersionedMigration), ensuring the + /// migration is only performed when on-chain version is 0. + pub type MigrationToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + UncheckedMigrationV0ToV1, + Pallet, + ::DbWeight, + >; +} diff --git a/bridges/modules/messages/src/outbound_lane.rs b/bridges/modules/messages/src/outbound_lane.rs index 788a13e82b1b..f71240ab7c70 100644 --- a/bridges/modules/messages/src/outbound_lane.rs +++ b/bridges/modules/messages/src/outbound_lane.rs @@ -19,16 +19,18 @@ use crate::{Config, LOG_TARGET}; use bp_messages::{ - ChainWithMessages, DeliveredMessages, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, + ChainWithMessages, DeliveredMessages, LaneId, LaneState, MessageNonce, OutboundLaneData, + UnrewardedRelayer, }; use codec::{Decode, Encode}; use frame_support::{traits::Get, BoundedVec, PalletError}; use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; -use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData}; +use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive}; /// Outbound lane storage. pub trait OutboundLaneStorage { + /// Stored message payload type. type StoredMessagePayload; /// Lane id. @@ -44,6 +46,8 @@ pub trait OutboundLaneStorage { fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload); /// Remove outbound message from the storage. fn remove_message(&mut self, nonce: &MessageNonce); + /// Purge lane data from the storage. + fn purge(self); } /// Limit for the `StoredMessagePayload` vector. @@ -75,6 +79,7 @@ pub enum ReceptionConfirmationError { } /// Outbound messages lane. +#[derive(Debug, PartialEq, Eq)] pub struct OutboundLane { storage: S, } @@ -90,6 +95,24 @@ impl OutboundLane { self.storage.data() } + /// Get lane state. + pub fn state(&self) -> LaneState { + self.storage.data().state + } + + /// Set lane state. + pub fn set_state(&mut self, state: LaneState) { + let mut data = self.storage.data(); + data.state = state; + self.storage.set_data(data); + } + + /// Return nonces of all currently queued messages. + pub fn queued_messages(&self) -> RangeInclusive { + let data = self.storage.data(); + data.oldest_unpruned_nonce..=data.latest_generated_nonce + } + /// Send message over lane. /// /// Returns new message nonce. @@ -150,6 +173,19 @@ impl OutboundLane { Ok(Some(confirmed_messages)) } + + /// Remove message from the storage. Doesn't perform any checks. + pub fn remove_oldest_unpruned_message(&mut self) { + let mut data = self.storage.data(); + self.storage.remove_message(&data.oldest_unpruned_nonce); + data.oldest_unpruned_nonce += 1; + self.storage.set_data(data); + } + + /// Purge lane state from the storage. + pub fn purge(self) { + self.storage.purge() + } } /// Verifies unrewarded relayers vec. @@ -187,10 +223,10 @@ fn ensure_unrewarded_relayers_are_correct( mod tests { use super::*; use crate::{ - outbound_lane, + active_outbound_lane, tests::mock::{ - outbound_message_data, run_test, unrewarded_relayer, TestRelayer, TestRuntime, - REGULAR_PAYLOAD, TEST_LANE_ID, + outbound_message_data, run_test, test_lane_id, unrewarded_relayer, TestRelayer, + TestRuntime, REGULAR_PAYLOAD, }, }; use sp_std::ops::RangeInclusive; @@ -212,7 +248,7 @@ mod tests { relayers: &VecDeque>, ) -> Result, ReceptionConfirmationError> { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); @@ -228,7 +264,7 @@ mod tests { #[test] fn send_message_works() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); assert_eq!(lane.storage.data().latest_generated_nonce, 0); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); assert!(lane.storage.message(&1).is_some()); @@ -239,7 +275,7 @@ mod tests { #[test] fn confirm_delivery_works() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3); @@ -259,7 +295,7 @@ mod tests { #[test] fn confirm_partial_delivery_works() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3); @@ -288,7 +324,7 @@ mod tests { #[test] fn confirm_delivery_rejects_nonce_lesser_than_latest_received() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); @@ -368,7 +404,7 @@ mod tests { #[test] fn confirm_delivery_detects_when_more_than_expected_messages_are_confirmed() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); diff --git a/bridges/modules/messages/src/proofs.rs b/bridges/modules/messages/src/proofs.rs index a3318833fa61..f35eb24d98c5 100644 --- a/bridges/modules/messages/src/proofs.rs +++ b/bridges/modules/messages/src/proofs.rs @@ -98,11 +98,7 @@ pub fn verify_messages_proof, I: 'static>( // Check that the storage proof doesn't have any untouched keys. parser.ensure_no_unused_keys().map_err(VerificationError::StorageProof)?; - // We only support single lane messages in this generated_schema - let mut proved_messages = ProvedMessages::new(); - proved_messages.insert(lane, proved_lane_messages); - - Ok(proved_messages) + Ok((lane, proved_lane_messages)) } /// Verify proof of This -> Bridged chain messages delivery. @@ -220,7 +216,8 @@ mod tests { mock::*, }; - use bp_header_chain::StoredHeaderDataBuilder; + use bp_header_chain::{HeaderChainError, StoredHeaderDataBuilder}; + use bp_messages::LaneState; use bp_runtime::{HeaderId, StorageProofError}; use codec::Encode; use sp_runtime::traits::Header; @@ -235,7 +232,7 @@ mod tests { test: impl Fn(FromBridgedChainMessagesProof) -> R, ) -> R { let (state_root, storage_proof) = prepare_messages_storage_proof::( - TEST_LANE_ID, + test_lane_id(), 1..=nonces_end, outbound_lane_data, bp_runtime::UnverifiedStorageProofParams::default(), @@ -267,7 +264,7 @@ mod tests { test(FromBridgedChainMessagesProof { bridged_header_hash, storage_proof, - lane: TEST_LANE_ID, + lane: test_lane_id(), nonces_start: 1, nonces_end, }) @@ -440,6 +437,7 @@ mod tests { using_messages_proof( 10, Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -480,6 +478,7 @@ mod tests { using_messages_proof( 0, Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -490,19 +489,18 @@ mod tests { false, |proof| verify_messages_proof::(proof, 0), ), - Ok(vec![( - TEST_LANE_ID, + Ok(( + test_lane_id(), ProvedLaneMessages { lane_state: Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, }), messages: Vec::new(), }, - )] - .into_iter() - .collect()), + )), ); } @@ -512,6 +510,7 @@ mod tests { using_messages_proof( 1, Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -522,22 +521,21 @@ mod tests { false, |proof| verify_messages_proof::(proof, 1), ), - Ok(vec![( - TEST_LANE_ID, + Ok(( + test_lane_id(), ProvedLaneMessages { lane_state: Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, }), messages: vec![Message { - key: MessageKey { lane_id: TEST_LANE_ID, nonce: 1 }, + key: MessageKey { lane_id: test_lane_id(), nonce: 1 }, payload: vec![42], }], }, - )] - .into_iter() - .collect()), + )) ); } diff --git a/bridges/modules/messages/src/tests/mock.rs b/bridges/modules/messages/src/tests/mock.rs index 99da019dc084..2caea9813e82 100644 --- a/bridges/modules/messages/src/tests/mock.rs +++ b/bridges/modules/messages/src/tests/mock.rs @@ -35,7 +35,7 @@ use bp_messages::{ DeliveryPayments, DispatchMessage, DispatchMessageData, FromBridgedChainMessagesProof, MessageDispatch, }, - ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, + ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, LaneState, Message, MessageKey, MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use bp_runtime::{ @@ -43,7 +43,7 @@ use bp_runtime::{ }; use codec::{Decode, Encode}; use frame_support::{ - derive_impl, parameter_types, + derive_impl, weights::{constants::RocksDbWeight, Weight}, }; use scale_info::TypeInfo; @@ -183,12 +183,6 @@ impl pallet_bridge_grandpa::Config for TestRuntime { type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } -parameter_types! { - pub const MaxMessagesToPruneAtOnce: u64 = 10; - pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2]; -} - /// weights of messages pallet calls we use in tests. pub type TestWeightInfo = (); @@ -200,8 +194,6 @@ impl Config for TestRuntime { type BridgedChain = BridgedChain; type BridgedHeaderChain = BridgedChainGrandpa; - type ActiveOutboundLanes = ActiveOutboundLanes; - type OutboundPayload = TestPayload; type InboundPayload = TestPayload; @@ -216,7 +208,7 @@ impl Config for TestRuntime { #[cfg(feature = "runtime-benchmarks")] impl crate::benchmarking::Config<()> for TestRuntime { fn bench_lane_id() -> LaneId { - TEST_LANE_ID + test_lane_id() } fn prepare_message_proof( @@ -267,13 +259,19 @@ pub const TEST_RELAYER_B: AccountId = 101; pub const TEST_RELAYER_C: AccountId = 102; /// Lane that we're using in tests. -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]); +pub fn test_lane_id() -> LaneId { + LaneId::new(1, 2) +} -/// Secondary lane that we're using in tests. -pub const TEST_LANE_ID_2: LaneId = LaneId([0, 0, 0, 2]); +/// Lane that is completely unknown to our runtime. +pub fn unknown_lane_id() -> LaneId { + LaneId::new(1, 3) +} -/// Inactive outbound lane. -pub const TEST_LANE_ID_3: LaneId = LaneId([0, 0, 0, 3]); +/// Lane that is registered, but it is closed. +pub fn closed_lane_id() -> LaneId { + LaneId::new(1, 4) +} /// Regular message payload. pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50); @@ -343,8 +341,18 @@ impl DeliveryConfirmationPayments for TestDeliveryConfirmationPayment pub struct TestMessageDispatch; impl TestMessageDispatch { - pub fn deactivate() { - frame_support::storage::unhashed::put(b"TestMessageDispatch.IsCongested", &true) + pub fn deactivate(lane: LaneId) { + // "enqueue" enough (to deactivate dispatcher) messages at dispatcher + let latest_received_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1; + for _ in 1..=latest_received_nonce { + Self::emulate_enqueued_message(lane); + } + } + + pub fn emulate_enqueued_message(lane: LaneId) { + let key = (b"dispatched", lane).encode(); + let dispatched = frame_support::storage::unhashed::get_or_default::(&key[..]); + frame_support::storage::unhashed::put(&key[..], &(dispatched + 1)); } } @@ -352,10 +360,10 @@ impl MessageDispatch for TestMessageDispatch { type DispatchPayload = TestPayload; type DispatchLevelResult = TestDispatchLevelResult; - fn is_active() -> bool { - !frame_support::storage::unhashed::get_or_default::( - b"TestMessageDispatch.IsCongested", - ) + fn is_active(lane: LaneId) -> bool { + frame_support::storage::unhashed::get_or_default::( + &(b"dispatched", lane).encode()[..], + ) <= BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX } fn dispatch_weight(message: &mut DispatchMessage) -> Weight { @@ -369,7 +377,10 @@ impl MessageDispatch for TestMessageDispatch { message: DispatchMessage, ) -> MessageDispatchResult { match message.data.payload.as_ref() { - Ok(payload) => payload.dispatch_result.clone(), + Ok(payload) => { + Self::emulate_enqueued_message(message.key.lane_id); + payload.dispatch_result.clone() + }, Err(_) => dispatch_result(0), } } @@ -395,7 +406,7 @@ impl OnMessagesDelivered for TestOnMessagesDelivered { /// Return test lane message with given nonce and payload. pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message { - Message { key: MessageKey { lane_id: TEST_LANE_ID, nonce }, payload: payload.encode() } + Message { key: MessageKey { lane_id: test_lane_id(), nonce }, payload: payload.encode() } } /// Return valid outbound message data, constructed from given payload. @@ -439,7 +450,7 @@ pub fn unrewarded_relayer( /// Returns unrewarded relayers state at given lane. pub fn inbound_unrewarded_relayers_state(lane: bp_messages::LaneId) -> UnrewardedRelayersState { - let inbound_lane_data = crate::InboundLanes::::get(lane).0; + let inbound_lane_data = crate::InboundLanes::::get(lane).unwrap().0; UnrewardedRelayersState::from(&inbound_lane_data) } @@ -454,7 +465,19 @@ pub fn new_test_ext() -> sp_io::TestExternalities { /// Run pallet test. pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(test) + new_test_ext().execute_with(|| { + crate::InboundLanes::::insert(test_lane_id(), InboundLaneData::opened()); + crate::OutboundLanes::::insert(test_lane_id(), OutboundLaneData::opened()); + crate::InboundLanes::::insert( + closed_lane_id(), + InboundLaneData { state: LaneState::Closed, ..Default::default() }, + ); + crate::OutboundLanes::::insert( + closed_lane_id(), + OutboundLaneData { state: LaneState::Closed, ..Default::default() }, + ); + test() + }) } /// Prepare valid storage proof for given messages and insert appropriate header to the @@ -471,7 +494,7 @@ pub fn prepare_messages_proof( let nonces_start = messages.first().unwrap().key.nonce; let nonces_end = messages.last().unwrap().key.nonce; let (storage_root, storage_proof) = prepare_messages_storage_proof::( - TEST_LANE_ID, + lane, nonces_start..=nonces_end, outbound_lane_data, UnverifiedStorageProofParams::default(), diff --git a/bridges/modules/messages/src/tests/pallet_tests.rs b/bridges/modules/messages/src/tests/pallet_tests.rs index f7a288d649a9..ceb1744c0665 100644 --- a/bridges/modules/messages/src/tests/pallet_tests.rs +++ b/bridges/modules/messages/src/tests/pallet_tests.rs @@ -17,20 +17,20 @@ //! Pallet-level tests. use crate::{ - outbound_lane, + active_outbound_lane, + lanes_manager::RuntimeInboundLaneStorage, outbound_lane::ReceptionConfirmationError, - tests::mock::{self, RuntimeEvent as TestEvent, *}, + tests::mock::{RuntimeEvent as TestEvent, *}, weights_ext::WeightInfoExt, - Call, Config, Error, Event, InboundLanes, MaybeOutboundLanesCount, OutboundLanes, - OutboundMessages, Pallet, PalletOperatingMode, PalletOwner, RuntimeInboundLaneStorage, - StoredInboundLaneData, + Call, Config, Error, Event, InboundLanes, LanesManagerError, OutboundLanes, OutboundMessages, + Pallet, PalletOperatingMode, PalletOwner, StoredInboundLaneData, }; use bp_messages::{ source_chain::{FromBridgedChainMessagesDeliveryProof, MessagesBridge}, - target_chain::FromBridgedChainMessagesProof, + target_chain::{FromBridgedChainMessagesProof, MessageDispatch}, BridgeMessagesCall, ChainWithMessages, DeliveredMessages, InboundLaneData, - InboundMessageDetails, LaneId, MessageKey, MessageNonce, MessagesOperatingMode, + InboundMessageDetails, LaneId, LaneState, MessageKey, MessageNonce, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, UnrewardedRelayer, UnrewardedRelayersState, VerificationError, }; @@ -44,7 +44,6 @@ use frame_support::{ weights::Weight, }; use frame_system::{EventRecord, Pallet as System, Phase}; -use sp_core::Get; use sp_runtime::{BoundedVec, DispatchError}; fn get_ready_for_events() { @@ -55,7 +54,7 @@ fn get_ready_for_events() { fn send_regular_message(lane_id: LaneId) { get_ready_for_events(); - let outbound_lane = outbound_lane::(lane_id); + let outbound_lane = active_outbound_lane::(lane_id).unwrap(); let message_nonce = outbound_lane.data().latest_generated_nonce + 1; let prev_enqueued_messages = outbound_lane.data().queued_messages().saturating_len(); let valid_message = Pallet::::validate_message(lane_id, ®ULAR_PAYLOAD) @@ -81,8 +80,9 @@ fn receive_messages_delivery_proof() { assert_ok!(Pallet::::receive_messages_delivery_proof( RuntimeOrigin::signed(1), prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![UnrewardedRelayer { relayer: 0, @@ -105,7 +105,7 @@ fn receive_messages_delivery_proof() { vec![EventRecord { phase: Phase::Initialization, event: TestEvent::Messages(Event::MessagesDelivered { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), messages: DeliveredMessages::new(1), }), topics: vec![], @@ -117,14 +117,14 @@ fn receive_messages_delivery_proof() { fn pallet_rejects_transactions_if_halted() { run_test(|| { // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); PalletOperatingMode::::put(MessagesOperatingMode::Basic( BasicOperatingMode::Halted, )); assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, ®ULAR_PAYLOAD), + Pallet::::validate_message(test_lane_id(), ®ULAR_PAYLOAD), Error::::NotOperatingNormally, ); @@ -141,8 +141,9 @@ fn pallet_rejects_transactions_if_halted() { ); let delivery_proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), }, @@ -167,7 +168,7 @@ fn pallet_rejects_transactions_if_halted() { #[test] fn receive_messages_fails_if_dispatcher_is_inactive() { run_test(|| { - TestMessageDispatch::deactivate(); + TestMessageDispatch::deactivate(test_lane_id()); let proof = prepare_messages_proof(vec![message(1, REGULAR_PAYLOAD)], None); assert_noop!( Pallet::::receive_messages_proof( @@ -177,7 +178,7 @@ fn receive_messages_fails_if_dispatcher_is_inactive() { 1, REGULAR_PAYLOAD.declared_weight, ), - Error::::MessageDispatchInactive, + Error::::LanesManager(LanesManagerError::LaneDispatcherInactive), ); }); } @@ -186,14 +187,14 @@ fn receive_messages_fails_if_dispatcher_is_inactive() { fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { run_test(|| { // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); PalletOperatingMode::::put( MessagesOperatingMode::RejectingOutboundMessages, ); assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, ®ULAR_PAYLOAD), + Pallet::::validate_message(test_lane_id(), ®ULAR_PAYLOAD), Error::::NotOperatingNormally, ); @@ -208,8 +209,9 @@ fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { assert_ok!(Pallet::::receive_messages_delivery_proof( RuntimeOrigin::signed(1), prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), }, @@ -228,7 +230,7 @@ fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { #[test] fn send_message_works() { run_test(|| { - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); }); } @@ -243,7 +245,7 @@ fn send_message_rejects_too_large_message() { .extra .extend_from_slice(&vec![0u8; max_outbound_payload_size as usize]); assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, &message_payload.clone(),), + Pallet::::validate_message(test_lane_id(), &message_payload.clone(),), Error::::MessageRejectedByPallet(VerificationError::MessageTooLarge), ); @@ -254,7 +256,7 @@ fn send_message_rejects_too_large_message() { assert_eq!(message_payload.encoded_size() as u32, max_outbound_payload_size); let valid_message = - Pallet::::validate_message(TEST_LANE_ID, &message_payload) + Pallet::::validate_message(test_lane_id(), &message_payload) .expect("validate_message has failed"); Pallet::::send_message(valid_message); }) @@ -271,7 +273,13 @@ fn receive_messages_proof_works() { REGULAR_PAYLOAD.declared_weight, )); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).0.last_delivered_nonce(), 1); + assert_eq!( + InboundLanes::::get(test_lane_id()) + .unwrap() + .0 + .last_delivered_nonce(), + 1 + ); assert!(TestDeliveryPayments::is_reward_paid(1)); }); @@ -282,8 +290,9 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { run_test(|| { // say we have received 10 messages && last confirmed message is 8 InboundLanes::::insert( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 8, relayers: vec![ unrewarded_relayer(9, 9, TEST_RELAYER_A), @@ -293,7 +302,7 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { }, ); assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), + inbound_unrewarded_relayers_state(test_lane_id()), UnrewardedRelayersState { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, @@ -315,8 +324,9 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { )); assert_eq!( - InboundLanes::::get(TEST_LANE_ID).0, + InboundLanes::::get(test_lane_id()).unwrap().0, InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 9, relayers: vec![ unrewarded_relayer(10, 10, TEST_RELAYER_B), @@ -326,7 +336,7 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { }, ); assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), + inbound_unrewarded_relayers_state(test_lane_id()), UnrewardedRelayersState { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, @@ -337,6 +347,86 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { }); } +#[test] +fn receive_messages_proof_fails_when_dispatcher_is_inactive() { + run_test(|| { + // "enqueue" enough (to deactivate dispatcher) messages at dispatcher + let latest_received_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1; + for _ in 1..=latest_received_nonce { + TestMessageDispatch::emulate_enqueued_message(test_lane_id()); + } + assert!(!TestMessageDispatch::is_active(test_lane_id())); + InboundLanes::::insert( + test_lane_id(), + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: latest_received_nonce, + relayers: vec![].into(), + }, + ); + + // try to delvier next message - it should fail because dispatcher is in "suspended" state + // at the beginning of the call + let messages_proof = + prepare_messages_proof(vec![message(latest_received_nonce + 1, REGULAR_PAYLOAD)], None); + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + messages_proof, + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::LanesManager(LanesManagerError::LaneDispatcherInactive) + ); + assert!(!TestMessageDispatch::is_active(test_lane_id())); + }); +} + +#[test] +fn receive_messages_succeeds_when_dispatcher_becomes_inactive_in_the_middle_of_transaction() { + run_test(|| { + // "enqueue" enough (to deactivate dispatcher) messages at dispatcher + let latest_received_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX / 2; + for _ in 1..=latest_received_nonce { + TestMessageDispatch::emulate_enqueued_message(test_lane_id()); + } + assert!(TestMessageDispatch::is_active(test_lane_id())); + InboundLanes::::insert( + test_lane_id(), + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: latest_received_nonce, + relayers: vec![].into(), + }, + ); + + // try to delvier next `BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX` messages + // - it will lead to dispatcher deactivation, but the transaction shall not fail and all + // messages must be delivered + let messages_begin = latest_received_nonce + 1; + let messages_end = + messages_begin + BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + let messages_range = messages_begin..messages_end; + let messages_count = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + prepare_messages_proof( + messages_range.map(|nonce| message(nonce, REGULAR_PAYLOAD)).collect(), + None, + ), + messages_count as _, + REGULAR_PAYLOAD.declared_weight * messages_count, + ),); + assert_eq!( + inbound_unrewarded_relayers_state(test_lane_id()).last_delivered_nonce, + messages_end - 1, + ); + assert!(!TestMessageDispatch::is_active(test_lane_id())); + }); +} + #[test] fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enough() { run_test(|| { @@ -354,7 +444,10 @@ fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enou ), Error::::InsufficientDispatchWeight ); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); + assert_eq!( + InboundLanes::::get(test_lane_id()).unwrap().last_delivered_nonce(), + 0 + ); }); } @@ -397,26 +490,64 @@ fn receive_messages_proof_rejects_proof_with_too_many_messages() { #[test] fn receive_messages_delivery_proof_works() { run_test(|| { - assert_eq!(OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, 0); - assert_eq!(OutboundLanes::::get(TEST_LANE_ID).oldest_unpruned_nonce, 1); + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .latest_received_nonce, + 0, + ); + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .oldest_unpruned_nonce, + 1, + ); - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); receive_messages_delivery_proof(); - assert_eq!(OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, 1); - assert_eq!(OutboundLanes::::get(TEST_LANE_ID).oldest_unpruned_nonce, 2); + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .latest_received_nonce, + 1, + ); + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .oldest_unpruned_nonce, + 2, + ); + }); +} + +#[test] +fn receive_messages_delivery_proof_works_on_closed_outbound_lanes() { + run_test(|| { + send_regular_message(test_lane_id()); + active_outbound_lane::(test_lane_id()) + .unwrap() + .set_state(LaneState::Closed); + receive_messages_delivery_proof(); + + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .latest_received_nonce, + 1, + ); }); } #[test] fn receive_messages_delivery_proof_rewards_relayers() { run_test(|| { - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); + send_regular_message(test_lane_id()); // this reports delivery of message 1 => reward is paid to TEST_RELAYER_A let single_message_delivery_proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), ..Default::default() @@ -452,7 +583,7 @@ fn receive_messages_delivery_proof_rewards_relayers() { // this reports delivery of both message 1 and message 2 => reward is paid only to // TEST_RELAYER_B let two_messages_delivery_proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), @@ -490,15 +621,15 @@ fn receive_messages_delivery_proof_rewards_relayers() { ); assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); - assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((TEST_LANE_ID, 0))); + assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((test_lane_id(), 0))); }); } #[test] fn receive_messages_delivery_proof_rejects_invalid_proof() { run_test(|| { - let mut proof = prepare_messages_delivery_proof(TEST_LANE_ID, Default::default()); - proof.lane = bp_messages::LaneId([42, 42, 42, 42]); + let mut proof = prepare_messages_delivery_proof(test_lane_id(), Default::default()); + proof.lane = bp_messages::LaneId::new(42, 84); assert_noop!( Pallet::::receive_messages_delivery_proof( @@ -516,7 +647,7 @@ fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_i run_test(|| { // when number of relayers entries is invalid let proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), @@ -542,7 +673,7 @@ fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_i // when number of messages is invalid let proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), @@ -568,7 +699,7 @@ fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_i // when last delivered nonce is invalid let proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), @@ -609,7 +740,10 @@ fn receive_messages_accepts_single_message_with_invalid_payload() { * improperly encoded) */ ),); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1,); + assert_eq!( + InboundLanes::::get(test_lane_id()).unwrap().last_delivered_nonce(), + 1, + ); }); } @@ -630,7 +764,10 @@ fn receive_messages_accepts_batch_with_message_with_invalid_payload() { REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight, ),); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 3,); + assert_eq!( + InboundLanes::::get(test_lane_id()).unwrap().last_delivered_nonce(), + 3, + ); }); } @@ -653,7 +790,10 @@ fn actual_dispatch_weight_does_not_overflow() { ), Error::::InsufficientDispatchWeight ); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); + assert_eq!( + InboundLanes::::get(test_lane_id()).unwrap().last_delivered_nonce(), + 0 + ); }); } @@ -730,8 +870,9 @@ fn proof_size_refund_from_receive_messages_proof_works() { REGULAR_PAYLOAD.declared_weight, ); InboundLanes::::insert( - TEST_LANE_ID, + test_lane_id(), StoredInboundLaneData(InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: 42, @@ -758,8 +899,9 @@ fn proof_size_refund_from_receive_messages_proof_works() { // if count of unrewarded relayer entries is less than maximal, then some `proof_size` // must be refunded InboundLanes::::insert( - TEST_LANE_ID, + test_lane_id(), StoredInboundLaneData(InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: 42, @@ -795,7 +937,7 @@ fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messa { run_test(|| { // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); // 1) InboundLaneData declares that the `last_confirmed_nonce` is 1; // 2) InboundLaneData has no entries => `InboundLaneData::last_delivered_nonce()` returns @@ -804,8 +946,12 @@ fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messa // 4) so the number of declared messages (see `UnrewardedRelayersState`) is `0` and numer of // actually confirmed messages is `1`. let proof = prepare_messages_delivery_proof( - TEST_LANE_ID, - InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() }, + test_lane_id(), + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: 1, + relayers: Default::default(), + }, ); assert_noop!( Pallet::::receive_messages_delivery_proof( @@ -829,20 +975,20 @@ fn storage_keys_computed_properly() { assert_eq!( OutboundMessages::::storage_map_final_key(MessageKey { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), nonce: 42 }), - bp_messages::storage_keys::message_key("Messages", &TEST_LANE_ID, 42).0, + bp_messages::storage_keys::message_key("Messages", &test_lane_id(), 42).0, ); assert_eq!( - OutboundLanes::::storage_map_final_key(TEST_LANE_ID), - bp_messages::storage_keys::outbound_lane_data_key("Messages", &TEST_LANE_ID).0, + OutboundLanes::::storage_map_final_key(test_lane_id()), + bp_messages::storage_keys::outbound_lane_data_key("Messages", &test_lane_id()).0, ); assert_eq!( - InboundLanes::::storage_map_final_key(TEST_LANE_ID), - bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0, + InboundLanes::::storage_map_final_key(test_lane_id()), + bp_messages::storage_keys::inbound_lane_data_key("Messages", &test_lane_id()).0, ); } @@ -851,7 +997,7 @@ fn inbound_message_details_works() { run_test(|| { assert_eq!( Pallet::::inbound_message_data( - TEST_LANE_ID, + test_lane_id(), REGULAR_PAYLOAD.encode(), OutboundMessageDetails { nonce: 0, dispatch_weight: Weight::zero(), size: 0 }, ), @@ -860,24 +1006,15 @@ fn inbound_message_details_works() { }); } -#[test] -fn outbound_message_from_unconfigured_lane_is_rejected() { - run_test(|| { - assert_noop!( - Pallet::::validate_message(TEST_LANE_ID_3, ®ULAR_PAYLOAD,), - Error::::InactiveOutboundLane, - ); - }); -} - #[test] fn test_bridge_messages_call_is_correctly_defined() { run_test(|| { let account_id = 1; let message_proof = prepare_messages_proof(vec![message(1, REGULAR_PAYLOAD)], None); let message_delivery_proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![UnrewardedRelayer { relayer: 0, @@ -947,12 +1084,12 @@ fn inbound_storage_extra_proof_size_bytes_works() { fn storage(relayer_entries: usize) -> RuntimeInboundLaneStorage { RuntimeInboundLaneStorage { - lane_id: Default::default(), - cached_data: Some(InboundLaneData { + lane_id: LaneId::new(1, 2), + cached_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![relayer_entry(); relayer_entries].into(), last_confirmed_nonce: 0, - }), - _phantom: Default::default(), + }, } } @@ -977,22 +1114,99 @@ fn inbound_storage_extra_proof_size_bytes_works() { } #[test] -fn maybe_outbound_lanes_count_returns_correct_value() { - assert_eq!( - MaybeOutboundLanesCount::::get(), - Some(mock::ActiveOutboundLanes::get().len() as u32) - ); +fn send_messages_fails_if_outbound_lane_is_not_opened() { + run_test(|| { + assert_noop!( + Pallet::::validate_message(unknown_lane_id(), ®ULAR_PAYLOAD), + Error::::LanesManager(LanesManagerError::UnknownOutboundLane), + ); + + assert_noop!( + Pallet::::validate_message(closed_lane_id(), ®ULAR_PAYLOAD), + Error::::LanesManager(LanesManagerError::ClosedOutboundLane), + ); + }); +} + +#[test] +fn receive_messages_proof_fails_if_inbound_lane_is_not_opened() { + run_test(|| { + let mut message = message(1, REGULAR_PAYLOAD); + message.key.lane_id = unknown_lane_id(); + let proof = prepare_messages_proof(vec![message.clone()], None); + + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof, + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::LanesManager(LanesManagerError::UnknownInboundLane), + ); + + message.key.lane_id = closed_lane_id(); + let proof = prepare_messages_proof(vec![message], None); + + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof, + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::LanesManager(LanesManagerError::ClosedInboundLane), + ); + }); +} + +#[test] +fn receive_messages_delivery_proof_fails_if_outbound_lane_is_unknown() { + run_test(|| { + let make_proof = |lane: LaneId| { + prepare_messages_delivery_proof( + lane, + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: 1, + relayers: vec![UnrewardedRelayer { + relayer: 0, + messages: DeliveredMessages::new(1), + }] + .into(), + }, + ) + }; + + let proof = make_proof(unknown_lane_id()); + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + proof, + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }, + ), + Error::::LanesManager(LanesManagerError::UnknownOutboundLane), + ); + }); } #[test] fn do_try_state_for_outbound_lanes_works() { run_test(|| { - let lane_id = TEST_LANE_ID; + let lane_id = test_lane_id(); // setup delivered nonce 1 OutboundLanes::::insert( lane_id, OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 2, latest_received_nonce: 1, latest_generated_nonce: 0, diff --git a/bridges/modules/parachains/src/call_ext.rs b/bridges/modules/parachains/src/call_ext.rs index 0f77eaf2c5a9..b67da03a6315 100644 --- a/bridges/modules/parachains/src/call_ext.rs +++ b/bridges/modules/parachains/src/call_ext.rs @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use crate::{Config, GrandpaPalletOf, Pallet, RelayBlockHash, RelayBlockNumber}; +use crate::{Config, GrandpaPalletOf, Pallet, RelayBlockNumber}; use bp_header_chain::HeaderChain; -use bp_parachains::BestParaHeadHash; -use bp_polkadot_core::parachains::{ParaHash, ParaId}; +use bp_parachains::{BestParaHeadHash, SubmitParachainHeadsInfo}; use bp_runtime::{HeaderId, OwnedBridgeModule}; use frame_support::{ dispatch::CallableCallFor, @@ -30,21 +29,6 @@ use sp_runtime::{ RuntimeDebug, }; -/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. -#[derive(PartialEq, RuntimeDebug)] -pub struct SubmitParachainHeadsInfo { - /// Number and hash of the finalized relay block that has been used to prove parachain - /// finality. - pub at_relay_block: HeaderId, - /// Parachain identifier. - pub para_id: ParaId, - /// Hash of the bundled parachain head. - pub para_head_hash: ParaHash, - /// If `true`, then the call must be free (assuming that everything else is valid) to - /// be treated as valid. - pub is_free_execution_expected: bool, -} - /// Verified `SubmitParachainHeadsInfo`. #[derive(PartialEq, RuntimeDebug)] pub struct VerifiedSubmitParachainHeadsInfo { diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index e2c30ce9aecc..bbf6a6600d56 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -28,7 +28,10 @@ pub use weights::WeightInfo; pub use weights_ext::WeightInfoExt; use bp_header_chain::{HeaderChain, HeaderChainError}; -use bp_parachains::{ParaInfo, ParaStoredHeaderData}; +use bp_parachains::{ + ParaInfo, ParaStoredHeaderData, RelayBlockHash, RelayBlockHasher, RelayBlockNumber, + SubmitParachainHeadsInfo, +}; use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain}; use frame_support::{dispatch::PostDispatchInfo, DefaultNoBound}; @@ -61,13 +64,6 @@ mod proofs; /// The target that will be used when publishing logs related to this pallet. pub const LOG_TARGET: &str = "runtime::bridge-parachains"; -/// Block hash of the bridged relay chain. -pub type RelayBlockHash = bp_polkadot_core::Hash; -/// Block number of the bridged relay chain. -pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; -/// Hasher of the bridged relay chain. -pub type RelayBlockHasher = bp_polkadot_core::Hasher; - /// Artifacts of the parachains head update. struct UpdateParachainHeadArtifacts { /// New best head of the parachain. @@ -739,7 +735,8 @@ pub mod pallet { /// Initial pallet owner. pub owner: Option, /// Dummy marker. - pub phantom: sp_std::marker::PhantomData, + #[serde(skip)] + pub _phantom: sp_std::marker::PhantomData, } #[pallet::genesis_build] diff --git a/bridges/modules/relayers/Cargo.toml b/bridges/modules/relayers/Cargo.toml index 27a28546afb4..0bf889bcca0e 100644 --- a/bridges/modules/relayers/Cargo.toml +++ b/bridges/modules/relayers/Cargo.toml @@ -16,41 +16,58 @@ log = { workspace = true } scale-info = { features = ["derive"], workspace = true } # Bridge dependencies - +bp-header-chain = { workspace = true } bp-messages = { workspace = true } bp-relayers = { workspace = true } bp-runtime = { workspace = true } +pallet-bridge-grandpa = { workspace = true } pallet-bridge-messages = { workspace = true } +pallet-bridge-parachains = { workspace = true } # Substrate Dependencies - frame-benchmarking = { optional = true, workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } +pallet-transaction-payment = { workspace = true } sp-arithmetic = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } [dev-dependencies] -bp-runtime = { workspace = true, default-features = true } +bp-runtime = { workspace = true } pallet-balances = { workspace = true, default-features = true } -sp-io = { workspace = true, default-features = true } -sp-runtime = { workspace = true, default-features = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +bp-parachains = { workspace = true } +bp-polkadot-core = { workspace = true } +bp-test-utils = { workspace = true } +pallet-utility = { workspace = true } +sp-core = { workspace = true } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-messages/std", + "bp-parachains/std", + "bp-polkadot-core/std", "bp-relayers/std", "bp-runtime/std", + "bp-test-utils/std", "codec/std", "frame-benchmarking/std", "frame-support/std", "frame-system/std", "log/std", + "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", + "pallet-bridge-parachains/std", + "pallet-transaction-payment/std", + "pallet-utility/std", "scale-info/std", "sp-arithmetic/std", + "sp-core/std", + "sp-io/std", "sp-runtime/std", "sp-std/std", ] @@ -59,13 +76,21 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", + "pallet-bridge-grandpa/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", + "pallet-bridge-parachains/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "pallet-balances/try-runtime", + "pallet-bridge-grandpa/try-runtime", "pallet-bridge-messages/try-runtime", + "pallet-bridge-parachains/try-runtime", + "pallet-transaction-payment/try-runtime", + "pallet-utility/try-runtime", "sp-runtime/try-runtime", ] +integrity-test = [] diff --git a/bridges/modules/relayers/src/benchmarking.rs b/bridges/modules/relayers/src/benchmarking.rs index ca312d44edfd..8a3f905a8f29 100644 --- a/bridges/modules/relayers/src/benchmarking.rs +++ b/bridges/modules/relayers/src/benchmarking.rs @@ -43,7 +43,7 @@ pub trait Config: crate::Config { benchmarks! { // Benchmark `claim_rewards` call. claim_rewards { - let lane = LaneId([0, 0, 0, 0]); + let lane = LaneId::new(1, 2); let account_params = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); let relayer: T::AccountId = whitelisted_caller(); @@ -102,7 +102,7 @@ benchmarks! { crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); // create slash destination account - let lane = LaneId([0, 0, 0, 0]); + let lane = LaneId::new(1, 2); let slash_destination = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); T::prepare_rewards_account(slash_destination, Zero::zero()); }: { @@ -116,7 +116,7 @@ benchmarks! { // the weight of message delivery call if `RefundBridgedParachainMessages` signed extension // is deployed at runtime level. register_relayer_reward { - let lane = LaneId([0, 0, 0, 0]); + let lane = LaneId::new(1, 2); let relayer: T::AccountId = whitelisted_caller(); let account_params = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); diff --git a/bridges/modules/relayers/src/extension/grandpa_adapter.rs b/bridges/modules/relayers/src/extension/grandpa_adapter.rs new file mode 100644 index 000000000000..6c9ae1c2968c --- /dev/null +++ b/bridges/modules/relayers/src/extension/grandpa_adapter.rs @@ -0,0 +1,177 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Adapter that allows using `pallet-bridge-relayers` as a signed extension in the +//! bridge with remote GRANDPA chain. + +use crate::{ + extension::verify_messages_call_succeeded, Config as BridgeRelayersConfig, LOG_TARGET, +}; + +use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; +use bp_runtime::{Chain, StaticStrProvider}; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use frame_system::Config as SystemConfig; +use pallet_bridge_grandpa::{ + CallSubType as BridgeGrandpaCallSubtype, Config as BridgeGrandpaConfig, + SubmitFinalityProofHelper, +}; +use pallet_bridge_messages::{ + CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, +}; +use sp_runtime::{ + traits::{Dispatchable, Get}, + transaction_validity::{TransactionPriority, TransactionValidityError}, + Saturating, +}; +use sp_std::marker::PhantomData; + +/// Adapter to be used in signed extension configuration, when bridging with remote +/// chains that are using GRANDPA finality. +pub struct WithGrandpaChainExtensionConfig< + // signed extension identifier + IdProvider, + // runtime that implements `BridgeMessagesConfig`, which + // uses `BridgeGrandpaConfig` to receive messages and + // confirmations from the remote chain. + Runtime, + // batch call unpacker + BatchCallUnpacker, + // instance of the `pallet-bridge-grandpa`, tracked by this extension + BridgeGrandpaPalletInstance, + // instance of BridgedChain `pallet-bridge-messages`, tracked by this extension + BridgeMessagesPalletInstance, + // message delivery transaction priority boost for every additional message + PriorityBoostPerMessage, +>( + PhantomData<( + IdProvider, + Runtime, + BatchCallUnpacker, + BridgeGrandpaPalletInstance, + BridgeMessagesPalletInstance, + PriorityBoostPerMessage, + )>, +); + +impl ExtensionConfig + for WithGrandpaChainExtensionConfig +where + ID: StaticStrProvider, + R: BridgeRelayersConfig + + BridgeMessagesConfig> + + BridgeGrandpaConfig, + BCU: BatchCallUnpacker, + GI: 'static, + MI: 'static, + P: Get, + R::RuntimeCall: Dispatchable + + BridgeGrandpaCallSubtype + + BridgeMessagesCallSubType, +{ + type IdProvider = ID; + type Runtime = R; + type BridgeMessagesPalletInstance = MI; + type PriorityBoostPerMessage = P; + type Reward = R::Reward; + type RemoteGrandpaChainBlockNumber = pallet_bridge_grandpa::BridgedBlockNumber; + + fn parse_and_check_for_obsolete_call( + call: &R::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + > { + let calls = BCU::unpack(call, 2); + let total_calls = calls.len(); + let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); + + let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info()); + let relay_finality_call = + calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); + + Ok(match (total_calls, relay_finality_call, msgs_call) { + (2, Some(relay_finality_call), Some(msgs_call)) => + Some(ExtensionCallInfo::RelayFinalityAndMsgs(relay_finality_call, msgs_call)), + (1, None, Some(msgs_call)) => Some(ExtensionCallInfo::Msgs(msgs_call)), + _ => None, + }) + } + + fn check_obsolete_parsed_call( + call: &R::RuntimeCall, + ) -> Result<&R::RuntimeCall, TransactionValidityError> { + call.check_obsolete_submit_finality_proof()?; + call.check_obsolete_call()?; + Ok(call) + } + + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &R::AccountId, + ) -> bool { + verify_submit_finality_proof_succeeded::(call_info, call_data, relayer) && + verify_messages_call_succeeded::(call_info, call_data, relayer) + } +} + +/// If the batch call contains the GRANDPA chain state update call, verify that it +/// has been successful. +/// +/// Only returns false when GRANDPA chain state update call has failed. +pub(crate) fn verify_submit_finality_proof_succeeded( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &::AccountId, +) -> bool +where + C: ExtensionConfig, + GI: 'static, + C::Runtime: BridgeGrandpaConfig, + >::BridgedChain: + Chain, +{ + let Some(finality_proof_info) = call_info.submit_finality_proof_info() else { return true }; + + if !SubmitFinalityProofHelper::::was_successful( + finality_proof_info.block_number, + ) { + // we only refund relayer if all calls have updated chain state + log::trace!( + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid GRANDPA chain finality proof", + C::IdProvider::STR, + call_info.messages_call_info().lane_id(), + relayer, + ); + return false + } + + // there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll` + // transaction. If relay chain header is mandatory, the GRANDPA pallet returns + // `Pays::No`, because such transaction is mandatory for operating the bridge. But + // `utility.batchAll` transaction always requires payment. But in both cases we'll + // refund relayer - either explicitly here, or using `Pays::No` if he's choosing + // to submit dedicated transaction. + + // submitter has means to include extra weight/bytes in the `submit_finality_proof` + // call, so let's subtract extra weight/size to avoid refunding for this extra stuff + call_data.extra_weight.saturating_accrue(finality_proof_info.extra_weight); + call_data.extra_size.saturating_accrue(finality_proof_info.extra_size); + + true +} diff --git a/bridges/modules/relayers/src/extension/messages_adapter.rs b/bridges/modules/relayers/src/extension/messages_adapter.rs new file mode 100644 index 000000000000..ecb575524bb0 --- /dev/null +++ b/bridges/modules/relayers/src/extension/messages_adapter.rs @@ -0,0 +1,94 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Adapter that allows using `pallet-bridge-relayers` as a signed extension in the +//! bridge with any remote chain. This adapter does not refund any finality transactions. + +use crate::{extension::verify_messages_call_succeeded, Config as BridgeRelayersConfig}; + +use bp_relayers::{ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; +use bp_runtime::StaticStrProvider; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use pallet_bridge_messages::{ + CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, +}; +use sp_runtime::{ + traits::{Dispatchable, Get}, + transaction_validity::{TransactionPriority, TransactionValidityError}, +}; +use sp_std::marker::PhantomData; + +/// Transaction extension that refunds a relayer for standalone messages delivery and confirmation +/// transactions. Finality transactions are not refunded. +pub struct WithMessagesExtensionConfig< + IdProvider, + Runtime, + BridgeMessagesPalletInstance, + PriorityBoostPerMessage, +>( + PhantomData<( + // signed extension identifier + IdProvider, + // runtime with `pallet-bridge-messages` pallet deployed + Runtime, + // instance of BridgedChain `pallet-bridge-messages`, tracked by this extension + BridgeMessagesPalletInstance, + // message delivery transaction priority boost for every additional message + PriorityBoostPerMessage, + )>, +); + +impl ExtensionConfig for WithMessagesExtensionConfig +where + ID: StaticStrProvider, + R: BridgeRelayersConfig + BridgeMessagesConfig, + MI: 'static, + P: Get, + R::RuntimeCall: Dispatchable + + BridgeMessagesCallSubType, +{ + type IdProvider = ID; + type Runtime = R; + type BridgeMessagesPalletInstance = MI; + type PriorityBoostPerMessage = P; + type Reward = R::Reward; + type RemoteGrandpaChainBlockNumber = (); + + fn parse_and_check_for_obsolete_call( + call: &R::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + > { + let call = Self::check_obsolete_parsed_call(call)?; + Ok(call.call_info().map(ExtensionCallInfo::Msgs)) + } + + fn check_obsolete_parsed_call( + call: &R::RuntimeCall, + ) -> Result<&R::RuntimeCall, TransactionValidityError> { + call.check_obsolete_call()?; + Ok(call) + } + + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &R::AccountId, + ) -> bool { + verify_messages_call_succeeded::(call_info, call_data, relayer) + } +} diff --git a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs b/bridges/modules/relayers/src/extension/mod.rs similarity index 60% rename from bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs rename to bridges/modules/relayers/src/extension/mod.rs index 6ba3506377d0..e1a7abd0ad1c 100644 --- a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs +++ b/bridges/modules/relayers/src/extension/mod.rs @@ -14,207 +14,76 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Signed extension that refunds relayer if he has delivered some new messages. -//! It also refunds transaction cost if the transaction is an `utility.batchAll()` -//! with calls that are: delivering new message and all necessary underlying headers -//! (parachain or relay chain). - -use crate::messages_call_ext::{ - CallHelper as MessagesCallHelper, CallInfo as MessagesCallInfo, MessagesCallSubType, +//! Signed extension, built around `pallet-bridge-relayers`. It is able to: +//! +//! - refund the cost of successful message delivery and confirmation transactions to the submitter +//! by registering corresponding reward in the pallet; +//! +//! - bump priority of messages delivery and confirmation transactions, signed by the registered +//! relayers. + +use crate::{Config as RelayersConfig, Pallet as RelayersPallet, WeightInfoExt, LOG_TARGET}; + +use bp_messages::{ChainWithMessages, MessageNonce}; +use bp_relayers::{ + ExplicitOrAccountParams, ExtensionCallData, ExtensionCallInfo, ExtensionConfig, + RewardsAccountOwner, RewardsAccountParams, }; -use bp_messages::{ChainWithMessages, LaneId, MessageNonce}; -use bp_relayers::{ExplicitOrAccountParams, RewardsAccountOwner, RewardsAccountParams}; -use bp_runtime::{Chain, Parachain, RangeInclusiveExt, StaticStrProvider}; -use codec::{Codec, Decode, Encode}; +use bp_runtime::{Chain, RangeInclusiveExt, StaticStrProvider}; +use codec::{Decode, Encode}; use frame_support::{ - dispatch::{CallableCallFor, DispatchInfo, PostDispatchInfo}, - traits::IsSubType, - weights::Weight, + dispatch::{DispatchInfo, PostDispatchInfo}, CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; -use pallet_bridge_grandpa::{ - CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper, SubmitFinalityProofInfo, -}; -use pallet_bridge_messages::Config as MessagesConfig; -use pallet_bridge_parachains::{ - BoundedBridgeGrandpaConfig, CallSubType as ParachainsCallSubType, Config as ParachainsConfig, - RelayBlockNumber, SubmitParachainHeadsHelper, SubmitParachainHeadsInfo, +use frame_system::Config as SystemConfig; +use pallet_bridge_messages::{CallHelper as MessagesCallHelper, Config as BridgeMessagesConfig}; +use pallet_transaction_payment::{ + Config as TransactionPaymentConfig, OnChargeTransaction, Pallet as TransactionPaymentPallet, }; -use pallet_bridge_relayers::{ - Config as RelayersConfig, Pallet as RelayersPallet, WeightInfoExt as _, -}; -use pallet_transaction_payment::{Config as TransactionPaymentConfig, OnChargeTransaction}; -use pallet_utility::{Call as UtilityCall, Config as UtilityConfig, Pallet as UtilityPallet}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, Get, PostDispatchInfoOf, SignedExtension, Zero}, + traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension, Zero}, transaction_validity::{ - TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransactionBuilder, + TransactionValidity, TransactionValidityError, ValidTransactionBuilder, }, - DispatchResult, FixedPointOperand, RuntimeDebug, + DispatchResult, RuntimeDebug, }; -use sp_std::{marker::PhantomData, vec, vec::Vec}; - -type AccountIdOf = ::AccountId; -// without this typedef rustfmt fails with internal err -type BalanceOf = - <::OnChargeTransaction as OnChargeTransaction>::Balance; -type CallOf = ::RuntimeCall; - -/// Trait identifying a bridged parachain. A relayer might be refunded for delivering messages -/// coming from this parachain. -pub trait RefundableParachainId { - /// The instance of the bridge parachains pallet. - type Instance: 'static; - /// The parachain Id. - type BridgedChain: Parachain; -} - -/// Implementation of `RefundableParachainId` for `trait Parachain`. -pub struct RefundableParachain(PhantomData<(Instance, Para)>); - -impl RefundableParachainId for RefundableParachain -where - Instance: 'static, - Para: Parachain, -{ - type Instance = Instance; - type BridgedChain = Para; -} - -/// Trait identifying a bridged messages lane. A relayer might be refunded for delivering messages -/// coming from this lane. -pub trait RefundableMessagesLaneId { - /// The instance of the bridge messages pallet. - type Instance: 'static; - /// The messages lane id. - type Id: Get; -} +use sp_std::{fmt::Debug, marker::PhantomData}; -/// Default implementation of `RefundableMessagesLaneId`. -pub struct RefundableMessagesLane(PhantomData<(Instance, Id)>); +pub use grandpa_adapter::WithGrandpaChainExtensionConfig; +pub use messages_adapter::WithMessagesExtensionConfig; +pub use parachain_adapter::WithParachainExtensionConfig; +pub use priority::*; -impl RefundableMessagesLaneId for RefundableMessagesLane -where - Instance: 'static, - Id: Get, -{ - type Instance = Instance; - type Id = Id; -} - -/// Refund calculator. -pub trait RefundCalculator { - /// The underlying integer type in which the refund is calculated. - type Balance; - - /// Compute refund for given transaction. - fn compute_refund( - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - tip: Self::Balance, - ) -> Self::Balance; -} - -/// `RefundCalculator` implementation which refunds the actual transaction fee. -pub struct ActualFeeRefund(PhantomData); - -impl RefundCalculator for ActualFeeRefund -where - R: TransactionPaymentConfig, - CallOf: Dispatchable, - BalanceOf: FixedPointOperand, -{ - type Balance = BalanceOf; - - fn compute_refund( - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - tip: BalanceOf, - ) -> BalanceOf { - pallet_transaction_payment::Pallet::::compute_actual_fee(len as _, info, post_info, tip) - } -} +mod grandpa_adapter; +mod messages_adapter; +mod parachain_adapter; +mod priority; /// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. #[cfg_attr(test, derive(Debug, PartialEq))] -pub struct PreDispatchData { +pub struct PreDispatchData { /// Transaction submitter (relayer) account. relayer: AccountId, /// Type of the call. - call_info: CallInfo, -} - -/// Type of the call that the extension recognizes. -#[derive(RuntimeDebugNoBound, PartialEq)] -pub enum CallInfo { - /// Relay chain finality + parachain finality + message delivery/confirmation calls. - AllFinalityAndMsgs( - SubmitFinalityProofInfo, - SubmitParachainHeadsInfo, - MessagesCallInfo, - ), - /// Relay chain finality + message delivery/confirmation calls. - RelayFinalityAndMsgs(SubmitFinalityProofInfo, MessagesCallInfo), - /// Parachain finality + message delivery/confirmation calls. - /// - /// This variant is used only when bridging with parachain. - ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo), - /// Standalone message delivery/confirmation call. - Msgs(MessagesCallInfo), + call_info: ExtensionCallInfo, } -impl CallInfo { - /// Returns true if call is a message delivery call (with optional finality calls). - fn is_receive_messages_proof_call(&self) -> bool { - match self.messages_call_info() { - MessagesCallInfo::ReceiveMessagesProof(_) => true, - MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => false, - } - } - - /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. - fn submit_finality_proof_info(&self) -> Option> { - match *self { - Self::AllFinalityAndMsgs(info, _, _) => Some(info), - Self::RelayFinalityAndMsgs(info, _) => Some(info), - _ => None, - } - } - +impl + PreDispatchData +{ /// Returns mutable reference to pre-dispatch `finality_target` sent to the /// `SubmitFinalityProof` call. #[cfg(test)] - fn submit_finality_proof_info_mut( + pub fn submit_finality_proof_info_mut( &mut self, - ) -> Option<&mut SubmitFinalityProofInfo> { - match *self { - Self::AllFinalityAndMsgs(ref mut info, _, _) => Some(info), - Self::RelayFinalityAndMsgs(ref mut info, _) => Some(info), + ) -> Option<&mut bp_header_chain::SubmitFinalityProofInfo> { + match self.call_info { + ExtensionCallInfo::AllFinalityAndMsgs(ref mut info, _, _) => Some(info), + ExtensionCallInfo::RelayFinalityAndMsgs(ref mut info, _) => Some(info), _ => None, } } - - /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. - fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { - match self { - Self::AllFinalityAndMsgs(_, info, _) => Some(info), - Self::ParachainFinalityAndMsgs(info, _) => Some(info), - _ => None, - } - } - - /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. - fn messages_call_info(&self) -> &MessagesCallInfo { - match self { - Self::AllFinalityAndMsgs(_, _, info) => info, - Self::RelayFinalityAndMsgs(_, info) => info, - Self::ParachainFinalityAndMsgs(_, info) => info, - Self::Msgs(info) => info, - } - } } /// The actions on relayer account that need to be performed because of his actions. @@ -228,78 +97,93 @@ pub enum RelayerAccountAction { Slash(AccountId, RewardsAccountParams), } -/// Everything common among our refund signed extensions. -pub trait RefundSignedExtension: - 'static + Clone + Codec + sp_std::fmt::Debug + Default + Eq + PartialEq + Send + Sync + TypeInfo +/// A signed extension, built around `pallet-bridge-relayers`. +/// +/// It may be incorporated into runtime to refund relayers for submitting correct +/// message delivery and confirmation transactions, optionally batched with required +/// finality proofs. +#[derive( + DefaultNoBound, + CloneNoBound, + Decode, + Encode, + EqNoBound, + PartialEqNoBound, + RuntimeDebugNoBound, + TypeInfo, +)] +#[scale_info(skip_type_params(Runtime, Config))] +pub struct BridgeRelayersSignedExtension(PhantomData<(Runtime, Config)>); + +impl BridgeRelayersSignedExtension +where + Self: 'static + Send + Sync, + R: RelayersConfig + + BridgeMessagesConfig + + TransactionPaymentConfig, + C: ExtensionConfig, + R::RuntimeCall: Dispatchable, + ::OnChargeTransaction: + OnChargeTransaction, { - /// This chain runtime. - type Runtime: MessagesConfig<::Instance> - + RelayersConfig; - /// Messages pallet and lane reference. - type Msgs: RefundableMessagesLaneId; - /// Refund amount calculator. - type Refund: RefundCalculator::Reward>; - /// Priority boost calculator. - type Priority: Get; - /// Signed extension unique identifier. - type Id: StaticStrProvider; - - /// Unpack batch runtime call. - fn expand_call(call: &CallOf) -> Vec<&CallOf>; - - /// Given runtime call, check if it has supported format. Additionally, check if any of - /// (optionally batched) calls are obsolete and we shall reject the transaction. - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError>; - - /// Check if parsed call is already obsolete. - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError>; - - /// Called from post-dispatch and shall perform additional checks (apart from messages - /// transaction success) of given call result. - fn additional_call_result_check( - relayer: &AccountIdOf, - call_info: &CallInfo, - extra_weight: &mut Weight, - extra_size: &mut u32, - ) -> bool; + /// Returns number of bundled messages `Some(_)`, if the given call info is a: + /// + /// - message delivery transaction; + /// + /// - with reasonable bundled messages that may be accepted by the messages pallet. + /// + /// This function is used to check whether the transaction priority should be + /// virtually boosted. The relayer registration (we only boost priority for registered + /// relayer transactions) must be checked outside. + fn bundled_messages_for_priority_boost( + call_info: Option<&ExtensionCallInfo>, + ) -> Option { + // we only boost priority of message delivery transactions + let parsed_call = match call_info { + Some(parsed_call) if parsed_call.is_receive_messages_proof_call() => parsed_call, + _ => return None, + }; + + // compute total number of messages in transaction + let bundled_messages = parsed_call.messages_call_info().bundled_messages().saturating_len(); + + // a quick check to avoid invalid high-priority transactions + let max_unconfirmed_messages_in_confirmation_tx = >::BridgedChain + ::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + if bundled_messages > max_unconfirmed_messages_in_confirmation_tx { + return None + } + + Some(bundled_messages) + } /// Given post-dispatch information, analyze the outcome of relayer call and return /// actions that need to be performed on relayer account. fn analyze_call_result( - pre: Option>>>, + pre: Option>>, info: &DispatchInfo, post_info: &PostDispatchInfo, len: usize, result: &DispatchResult, - ) -> RelayerAccountAction, ::Reward> - { - let mut extra_weight = Weight::zero(); - let mut extra_size = 0; - + ) -> RelayerAccountAction { // We don't refund anything for transactions that we don't support. let (relayer, call_info) = match pre { Some(Some(pre)) => (pre.relayer, pre.call_info), _ => return RelayerAccountAction::None, }; - // now we know that the relayer either needs to be rewarded, or slashed + // now we know that the call is supported and we may need to reward or slash relayer // => let's prepare the correspondent account that pays reward/receives slashed amount - let reward_account_params = - RewardsAccountParams::new( - ::Id::get(), - ::Instance, - >>::BridgedChain::ID, - if call_info.is_receive_messages_proof_call() { - RewardsAccountOwner::ThisChain - } else { - RewardsAccountOwner::BridgedChain - }, - ); + let lane_id = call_info.messages_call_info().lane_id(); + let reward_account_params = RewardsAccountParams::new( + lane_id, + >::BridgedChain::ID, + if call_info.is_receive_messages_proof_call() { + RewardsAccountOwner::ThisChain + } else { + RewardsAccountOwner::BridgedChain + }, + ); // prepare return value for the case if the call has failed or it has not caused // expected side effects (e.g. not all messages have been accepted) @@ -322,37 +206,19 @@ pub trait RefundSignedExtension: // We don't refund anything if the transaction has failed. if let Err(e) = result { log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid messages transaction: {:?}", - Self::Id::STR, - ::Id::get(), + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid messages transaction: {:?}", + Self::IDENTIFIER, + lane_id, relayer, e, ); return slash_relayer_if_delivery_result } - // Check if the `ReceiveMessagesProof` call delivered at least some of the messages that - // it contained. If this happens, we consider the transaction "helpful" and refund it. - let msgs_call_info = call_info.messages_call_info(); - if !MessagesCallHelper::::Instance>::was_successful(msgs_call_info) { - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid messages call", - Self::Id::STR, - ::Id::get(), - relayer, - ); - return slash_relayer_if_delivery_result - } - - // do additional checks - if !Self::additional_call_result_check( - &relayer, - &call_info, - &mut extra_weight, - &mut extra_size, - ) { + // check whether the call has succeeded + let mut call_data = ExtensionCallData::default(); + if !C::check_call_result(&call_info, &mut call_data, &relayer) { return slash_relayer_if_delivery_result } @@ -364,81 +230,55 @@ pub trait RefundSignedExtension: let tip = Zero::zero(); // decrease post-dispatch weight/size using extra weight/size that we know now - let post_info_len = len.saturating_sub(extra_size as usize); - let mut post_info_weight = - post_info.actual_weight.unwrap_or(info.weight).saturating_sub(extra_weight); + let post_info_len = len.saturating_sub(call_data.extra_size as usize); + let mut post_info_weight = post_info + .actual_weight + .unwrap_or(info.weight) + .saturating_sub(call_data.extra_weight); // let's also replace the weight of slashing relayer with the weight of rewarding relayer if call_info.is_receive_messages_proof_call() { post_info_weight = post_info_weight.saturating_sub( - ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(), + ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(), ); } // compute the relayer refund let mut post_info = *post_info; post_info.actual_weight = Some(post_info_weight); - let refund = Self::Refund::compute_refund(info, &post_info, post_info_len, tip); + let refund = Self::compute_refund(info, &post_info, post_info_len, tip); // we can finally reward relayer RelayerAccountAction::Reward(relayer, reward_account_params, refund) } - /// Returns number of bundled messages `Some(_)`, if the given call info is a: - /// - /// - message delivery transaction; - /// - /// - with reasonable bundled messages that may be accepted by the messages pallet. - /// - /// This function is used to check whether the transaction priority should be - /// virtually boosted. The relayer registration (we only boost priority for registered - /// relayer transactions) must be checked outside. - fn bundled_messages_for_priority_boost(call_info: Option<&CallInfo>) -> Option { - // we only boost priority of message delivery transactions - let parsed_call = match call_info { - Some(parsed_call) if parsed_call.is_receive_messages_proof_call() => parsed_call, - _ => return None, - }; - - // compute total number of messages in transaction - let bundled_messages = parsed_call.messages_call_info().bundled_messages().saturating_len(); - - // a quick check to avoid invalid high-priority transactions - let max_unconfirmed_messages_in_confirmation_tx = ::Instance, - >>::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - if bundled_messages > max_unconfirmed_messages_in_confirmation_tx { - return None - } - - Some(bundled_messages) + /// Compute refund for the successful relayer transaction + fn compute_refund( + info: &DispatchInfo, + post_info: &PostDispatchInfo, + len: usize, + tip: R::Reward, + ) -> R::Reward { + TransactionPaymentPallet::::compute_actual_fee(len as _, info, post_info, tip) } } -/// Adapter that allow implementing `sp_runtime::traits::SignedExtension` for any -/// `RefundSignedExtension`. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -pub struct RefundSignedExtensionAdapter(T); - -impl SignedExtension for RefundSignedExtensionAdapter +impl SignedExtension for BridgeRelayersSignedExtension where - CallOf: Dispatchable - + MessagesCallSubType::Instance>, + Self: 'static + Send + Sync, + R: RelayersConfig + + BridgeMessagesConfig + + TransactionPaymentConfig, + C: ExtensionConfig, + R::RuntimeCall: Dispatchable, + ::OnChargeTransaction: + OnChargeTransaction, { - const IDENTIFIER: &'static str = T::Id::STR; - type AccountId = AccountIdOf; - type Call = CallOf; + const IDENTIFIER: &'static str = C::IdProvider::STR; + type AccountId = R::AccountId; + type Call = R::RuntimeCall; type AdditionalSigned = (); - type Pre = Option>>; + type Pre = Option>; fn additional_signed(&self) -> Result<(), TransactionValidityError> { Ok(()) @@ -456,33 +296,33 @@ where // we're not calling `validate` from `pre_dispatch` directly because of performance // reasons, so if you're adding some code that may fail here, please check if it needs // to be added to the `pre_dispatch` as well - let parsed_call = T::parse_and_check_for_obsolete_call(call)?; + let parsed_call = C::parse_and_check_for_obsolete_call(call)?; // the following code just plays with transaction priority and never returns an error // we only boost priority of presumably correct message delivery transactions - let bundled_messages = match T::bundled_messages_for_priority_boost(parsed_call.as_ref()) { + let bundled_messages = match Self::bundled_messages_for_priority_boost(parsed_call.as_ref()) + { Some(bundled_messages) => bundled_messages, None => return Ok(Default::default()), }; // we only boost priority if relayer has staked required balance - if !RelayersPallet::::is_registration_active(who) { + if !RelayersPallet::::is_registration_active(who) { return Ok(Default::default()) } // compute priority boost - let priority_boost = crate::extensions::priority_calculator::compute_priority_boost::< - T::Priority, - >(bundled_messages); + let priority_boost = + priority::compute_priority_boost::(bundled_messages); let valid_transaction = ValidTransactionBuilder::default().priority(priority_boost); log::trace!( - target: "runtime::bridge", - "{} via {:?} has boosted priority of message delivery transaction \ + target: LOG_TARGET, + "{}.{:?}: has boosted priority of message delivery transaction \ of relayer {:?}: {} messages -> {} priority", Self::IDENTIFIER, - ::Id::get(), + parsed_call.as_ref().map(|p| p.messages_call_info().lane_id()), who, bundled_messages, priority_boost, @@ -499,14 +339,14 @@ where _len: usize, ) -> Result { // this is a relevant piece of `validate` that we need here (in `pre_dispatch`) - let parsed_call = T::parse_and_check_for_obsolete_call(call)?; + let parsed_call = C::parse_and_check_for_obsolete_call(call)?; Ok(parsed_call.map(|call_info| { log::trace!( - target: "runtime::bridge", - "{} via {:?} parsed bridge transaction in pre-dispatch: {:?}", + target: LOG_TARGET, + "{}.{:?}: parsed bridge transaction in pre-dispatch: {:?}", Self::IDENTIFIER, - ::Id::get(), + call_info.messages_call_info().lane_id(), call_info, ); PreDispatchData { relayer: who.clone(), call_info } @@ -520,28 +360,28 @@ where len: usize, result: &DispatchResult, ) -> Result<(), TransactionValidityError> { - let call_result = T::analyze_call_result(pre, info, post_info, len, result); + let lane_id = pre + .as_ref() + .and_then(|p| p.as_ref()) + .map(|p| p.call_info.messages_call_info().lane_id()); + let call_result = Self::analyze_call_result(pre, info, post_info, len, result); match call_result { RelayerAccountAction::None => (), RelayerAccountAction::Reward(relayer, reward_account, reward) => { - RelayersPallet::::register_relayer_reward( - reward_account, - &relayer, - reward, - ); + RelayersPallet::::register_relayer_reward(reward_account, &relayer, reward); log::trace!( - target: "runtime::bridge", - "{} via {:?} has registered reward: {:?} for {:?}", + target: LOG_TARGET, + "{}.{:?}: has registered reward: {:?} for {:?}", Self::IDENTIFIER, - ::Id::get(), + lane_id, reward, relayer, ); }, RelayerAccountAction::Slash(relayer, slash_account) => - RelayersPallet::::slash_and_deregister( + RelayersPallet::::slash_and_deregister( &relayer, ExplicitOrAccountParams::Params(slash_account), ), @@ -551,416 +391,61 @@ where } } -/// Signed extension that refunds a relayer for new messages coming from a parachain. -/// -/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) -/// with message delivery transaction. Batch may deliver either both relay chain header and -/// parachain head, or just parachain head. Corresponding headers must be used in messages -/// proof verification. -/// -/// Extension does not refund transaction tip due to security reasons. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, Para, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedParachainMessages( - PhantomData<( - // runtime with `frame-utility`, `pallet-bridge-grandpa`, `pallet-bridge-parachains`, - // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // implementation of `RefundableParachainId` trait, which specifies the instance of - // the used `pallet-bridge-parachains` pallet and the bridged parachain id - Para, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedParachainMessages -where - Self: 'static + Send + Sync, - RefundBridgedGrandpaMessages< - Runtime, - Runtime::BridgesGrandpaPalletInstance, - Msgs, - Refund, - Priority, - Id, - >: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + ParachainsConfig - + MessagesConfig - + RelayersConfig, - Para: RefundableParachainId, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + ParachainsCallSubType - + MessagesCallSubType, -{ - type Runtime = Runtime; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 3 => - calls.iter().collect(), - Some(_) => vec![], - None => vec![call], - } - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let calls = Self::expand_call(call); - let total_calls = calls.len(); - let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); - - let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); - let para_finality_call = calls - .next() - .transpose()? - .and_then(|c| c.submit_parachain_heads_info_for(Para::BridgedChain::PARACHAIN_ID)); - let relay_finality_call = - calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); - - Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) { - (3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) => Some( - CallInfo::AllFinalityAndMsgs(relay_finality_call, para_finality_call, msgs_call), - ), - (2, None, Some(para_finality_call), Some(msgs_call)) => - Some(CallInfo::ParachainFinalityAndMsgs(para_finality_call, msgs_call)), - (1, None, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), - _ => None, - }) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_submit_finality_proof()?; - call.check_obsolete_submit_parachain_heads()?; - call.check_obsolete_call()?; - Ok(call) - } - - fn additional_call_result_check( - relayer: &Runtime::AccountId, - call_info: &CallInfo, - extra_weight: &mut Weight, - extra_size: &mut u32, - ) -> bool { - // check if relay chain state has been updated - let is_grandpa_call_successful = - RefundBridgedGrandpaMessages::< - Runtime, - Runtime::BridgesGrandpaPalletInstance, - Msgs, - Refund, - Priority, - Id, - >::additional_call_result_check(relayer, call_info, extra_weight, extra_size); - if !is_grandpa_call_successful { - return false - } - - // check if parachain state has been updated - if let Some(para_proof_info) = call_info.submit_parachain_heads_info() { - if !SubmitParachainHeadsHelper::::was_successful( - para_proof_info, - ) { - // we only refund relayer if all calls have updated chain state - log::trace!( - target: "runtime::bridge", - "{} from parachain {} via {:?}: relayer {:?} has submitted invalid parachain finality proof", - Id::STR, - Para::BridgedChain::PARACHAIN_ID, - Msgs::Id::get(), - relayer, - ); - return false - } - } - - true - } -} - -/// Signed extension that refunds a relayer for new messages coming from a standalone (GRANDPA) -/// chain. -/// -/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) -/// with message delivery transaction. Batch may deliver either both relay chain header and -/// parachain head, or just parachain head. Corresponding headers must be used in messages -/// proof verification. -/// -/// Extension does not refund transaction tip due to security reasons. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, GrandpaInstance, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedGrandpaMessages( - PhantomData<( - // runtime with `frame-utility`, `pallet-bridge-grandpa`, - // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // bridge GRANDPA pallet instance, used to track bridged chain state - GrandpaInstance, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedGrandpaMessages -where - Self: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + MessagesConfig - + RelayersConfig, - GrandpaInstance: 'static, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + MessagesCallSubType, -{ - type Runtime = Runtime; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 2 => - calls.iter().collect(), - Some(_) => vec![], - None => vec![call], - } - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let calls = Self::expand_call(call); - let total_calls = calls.len(); - let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); - - let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); - let relay_finality_call = - calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); - - Ok(match (total_calls, relay_finality_call, msgs_call) { - (2, Some(relay_finality_call), Some(msgs_call)) => - Some(CallInfo::RelayFinalityAndMsgs(relay_finality_call, msgs_call)), - (1, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), - _ => None, - }) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_submit_finality_proof()?; - call.check_obsolete_call()?; - Ok(call) - } - - fn additional_call_result_check( - relayer: &Runtime::AccountId, - call_info: &CallInfo, - extra_weight: &mut Weight, - extra_size: &mut u32, - ) -> bool { - // check if relay chain state has been updated - if let Some(finality_proof_info) = call_info.submit_finality_proof_info() { - if !SubmitFinalityProofHelper::::was_successful( - finality_proof_info.block_number, - ) { - // we only refund relayer if all calls have updated chain state - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid relay chain finality proof", - Self::Id::STR, - ::Id::get(), - relayer, - ); - return false - } - - // there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll` - // transaction. If relay chain header is mandatory, the GRANDPA pallet returns - // `Pays::No`, because such transaction is mandatory for operating the bridge. But - // `utility.batchAll` transaction always requires payment. But in both cases we'll - // refund relayer - either explicitly here, or using `Pays::No` if he's choosing - // to submit dedicated transaction. - - // submitter has means to include extra weight/bytes in the `submit_finality_proof` - // call, so let's subtract extra weight/size to avoid refunding for this extra stuff - *extra_weight = (*extra_weight).saturating_add(finality_proof_info.extra_weight); - *extra_size = (*extra_size).saturating_add(finality_proof_info.extra_size); - } - - true - } -} - -/// Transaction extension that refunds a relayer for standalone messages delivery and confirmation -/// transactions. Finality transactions are not refunded. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, GrandpaInstance, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedMessages( - PhantomData<( - // runtime with `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedMessages +/// Verify that the messages pallet call, supported by extension has succeeded. +pub(crate) fn verify_messages_call_succeeded( + call_info: &ExtensionCallInfo, + _call_data: &mut ExtensionCallData, + relayer: &::AccountId, +) -> bool where - Self: 'static + Send + Sync, - Runtime: MessagesConfig + RelayersConfig, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + MessagesCallSubType, + C: ExtensionConfig, + MI: 'static, + C::Runtime: BridgeMessagesConfig, { - type Runtime = Runtime; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - vec![call] - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let call = Self::check_obsolete_parsed_call(call)?; - Ok(call.call_info_for(Msgs::Id::get()).map(CallInfo::Msgs)) - } + let messages_call = call_info.messages_call_info(); - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_call()?; - Ok(call) + if !MessagesCallHelper::::was_successful(messages_call) { + log::trace!( + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid messages call", + C::IdProvider::STR, + call_info.messages_call_info().lane_id(), + relayer, + ); + return false } - fn additional_call_result_check( - _relayer: &Runtime::AccountId, - _call_info: &CallInfo, - _extra_weight: &mut Weight, - _extra_size: &mut u32, - ) -> bool { - // everything is checked by the `RefundTransactionExtension` - true - } + true } #[cfg(test)] -pub(crate) mod tests { +mod tests { use super::*; - use crate::{ - messages_call_ext::{ - BaseMessagesProofInfo, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, - UnrewardedRelayerOccupation, - }, - mock::*, - }; - use bp_header_chain::StoredHeaderDataBuilder; + use crate::mock::*; + + use bp_header_chain::{StoredHeaderDataBuilder, SubmitFinalityProofInfo}; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, DeliveredMessages, InboundLaneData, - MessageNonce, MessagesOperatingMode, OutboundLaneData, UnrewardedRelayer, - UnrewardedRelayersState, + target_chain::FromBridgedChainMessagesProof, BaseMessagesProofInfo, DeliveredMessages, + InboundLaneData, LaneId, MessageNonce, MessagesCallInfo, MessagesOperatingMode, + OutboundLaneData, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, + UnrewardedRelayer, UnrewardedRelayerOccupation, UnrewardedRelayersState, }; - use bp_parachains::{BestParaHeadHash, ParaInfo}; + use bp_parachains::{BestParaHeadHash, ParaInfo, SubmitParachainHeadsInfo}; use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; - use bp_runtime::{BasicOperatingMode, HeaderId}; + use bp_relayers::RuntimeWithUtilityPallet; + use bp_runtime::{BasicOperatingMode, HeaderId, Parachain}; use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID}; use frame_support::{ + __private::sp_tracing, assert_storage_noop, parameter_types, traits::{fungible::Mutate, ReservableCurrency}, weights::Weight, }; use pallet_bridge_grandpa::{Call as GrandpaCall, Pallet as GrandpaPallet, StoredAuthoritySet}; use pallet_bridge_messages::{Call as MessagesCall, Pallet as MessagesPallet}; - use pallet_bridge_parachains::{ - Call as ParachainsCall, Pallet as ParachainsPallet, RelayBlockHash, - }; + use pallet_bridge_parachains::{Call as ParachainsCall, Pallet as ParachainsPallet}; + use pallet_utility::Call as UtilityCall; use sp_runtime::{ traits::{ConstU64, Header as HeaderT}, transaction_validity::{InvalidTransaction, ValidTransaction}, @@ -968,50 +453,58 @@ pub(crate) mod tests { }; parameter_types! { - pub TestLaneId: LaneId = TEST_LANE_ID; + TestParachain: u32 = BridgedUnderlyingParachain::PARACHAIN_ID; pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( - TEST_LANE_ID, + test_lane_id(), TEST_BRIDGED_CHAIN_ID, RewardsAccountOwner::ThisChain, ); pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( - TEST_LANE_ID, + test_lane_id(), TEST_BRIDGED_CHAIN_ID, RewardsAccountOwner::BridgedChain, ); + pub TestLaneId: LaneId = test_lane_id(); } + bp_runtime::generate_static_str_provider!(TestGrandpaExtension); bp_runtime::generate_static_str_provider!(TestExtension); + bp_runtime::generate_static_str_provider!(TestMessagesExtension); - type TestMessagesExtensionProvider = RefundBridgedMessages< + type TestGrandpaExtensionConfig = grandpa_adapter::WithGrandpaChainExtensionConfig< + StrTestGrandpaExtension, TestRuntime, - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, + RuntimeWithUtilityPallet, + (), + (), ConstU64<1>, - StrTestExtension, >; - type TestMessagesExtension = RefundSignedExtensionAdapter; - type TestGrandpaExtensionProvider = RefundBridgedGrandpaMessages< + type TestGrandpaExtension = + BridgeRelayersSignedExtension; + type TestExtensionConfig = parachain_adapter::WithParachainExtensionConfig< + StrTestExtension, TestRuntime, + RuntimeWithUtilityPallet, + (), (), - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, ConstU64<1>, - StrTestExtension, >; - type TestGrandpaExtension = RefundSignedExtensionAdapter; - type TestExtensionProvider = RefundBridgedParachainMessages< + type TestExtension = BridgeRelayersSignedExtension; + type TestMessagesExtensionConfig = messages_adapter::WithMessagesExtensionConfig< + StrTestMessagesExtension, TestRuntime, - RefundableParachain<(), BridgedUnderlyingParachain>, - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, + (), ConstU64<1>, - StrTestExtension, >; - type TestExtension = RefundSignedExtensionAdapter; + type TestMessagesExtension = + BridgeRelayersSignedExtension; + + fn test_lane_id() -> LaneId { + LaneId::new(1, 2) + } fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { - let test_stake: ThisChainBalance = TestStake::get(); + let test_stake: ThisChainBalance = Stake::get(); ExistentialDeposit::get().saturating_add(test_stake * 100) } @@ -1026,7 +519,7 @@ pub(crate) mod tests { TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get()) } - pub fn relayer_account_at_this_chain() -> ThisChainAccountId { + fn relayer_account_at_this_chain() -> ThisChainAccountId { 0 } @@ -1034,13 +527,13 @@ pub(crate) mod tests { 0 } - pub fn initialize_environment( - best_relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + fn initialize_environment( + best_relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) { let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); - let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); + let best_relay_header = HeaderId(best_relay_header_number, BridgedChainHash::default()); pallet_bridge_grandpa::CurrentAuthoritySet::::put( StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(), ); @@ -1050,7 +543,7 @@ pub(crate) mod tests { bp_test_utils::test_header::(0).build(), ); - let para_id = ParaId(BridgedUnderlyingParachain::PARACHAIN_ID); + let para_id = ParaId(TestParachain::get()); let para_info = ParaInfo { best_head_hash: BestParaHeadHash { at_relay_block_number: parachain_head_at_relay_header_number, @@ -1060,7 +553,7 @@ pub(crate) mod tests { }; pallet_bridge_parachains::ParasInfo::::insert(para_id, para_info); - let lane_id = TestLaneId::get(); + let lane_id = test_lane_id(); let in_lane_data = InboundLaneData { last_confirmed_nonce: best_message, ..Default::default() }; pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); @@ -1078,7 +571,7 @@ pub(crate) mod tests { .unwrap(); } - fn submit_relay_header_call(relay_header_number: RelayBlockNumber) -> RuntimeCall { + fn submit_relay_header_call(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall { let relay_header = BridgedChainHeader::new( relay_header_number, Default::default(), @@ -1094,7 +587,7 @@ pub(crate) mod tests { }) } - pub fn submit_relay_header_call_ex(relay_header_number: RelayBlockNumber) -> RuntimeCall { + fn submit_relay_header_call_ex(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall { let relay_header = BridgedChainHeader::new( relay_header_number, Default::default(), @@ -1113,12 +606,12 @@ pub(crate) mod tests { } fn submit_parachain_head_call( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, ) -> RuntimeCall { RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { - at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), + at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()), parachains: vec![( - ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + ParaId(TestParachain::get()), [parachain_head_at_relay_header_number as u8; 32].into(), )], parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, @@ -1126,12 +619,12 @@ pub(crate) mod tests { } pub fn submit_parachain_head_call_ex( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, ) -> RuntimeCall { RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads_ex { - at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), + at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()), parachains: vec![( - ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + ParaId(TestParachain::get()), [parachain_head_at_relay_header_number as u8; 32].into(), )], parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, @@ -1145,10 +638,11 @@ pub(crate) mod tests { proof: Box::new(FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: TestLaneId::get(), + lane: test_lane_id(), nonces_start: pallet_bridge_messages::InboundLanes::::get( - TEST_LANE_ID, + test_lane_id(), ) + .unwrap() .last_delivered_nonce() + 1, nonces_end: best_message, @@ -1163,7 +657,7 @@ pub(crate) mod tests { proof: FromBridgedChainMessagesDeliveryProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: TestLaneId::get(), + lane: test_lane_id(), }, relayers_state: UnrewardedRelayersState { last_delivered_nonce: best_message, @@ -1173,7 +667,7 @@ pub(crate) mod tests { } fn parachain_finality_and_delivery_batch_call( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1185,7 +679,7 @@ pub(crate) mod tests { } fn parachain_finality_and_confirmation_batch_call( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1197,7 +691,7 @@ pub(crate) mod tests { } fn relay_finality_and_delivery_batch_call( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1209,7 +703,7 @@ pub(crate) mod tests { } fn relay_finality_and_delivery_batch_call_ex( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1221,7 +715,7 @@ pub(crate) mod tests { } fn relay_finality_and_confirmation_batch_call( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1233,7 +727,7 @@ pub(crate) mod tests { } fn relay_finality_and_confirmation_batch_call_ex( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1245,8 +739,8 @@ pub(crate) mod tests { } fn all_finality_and_delivery_batch_call( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1259,8 +753,8 @@ pub(crate) mod tests { } fn all_finality_and_delivery_batch_call_ex( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1273,8 +767,8 @@ pub(crate) mod tests { } fn all_finality_and_confirmation_batch_call( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1287,8 +781,8 @@ pub(crate) mod tests { } fn all_finality_and_confirmation_batch_call_ex( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1300,10 +794,11 @@ pub(crate) mod tests { }) } - fn all_finality_pre_dispatch_data() -> PreDispatchData { + fn all_finality_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndMsgs( + call_info: ExtensionCallInfo::AllFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1314,38 +809,40 @@ pub(crate) mod tests { }, SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }), ), } } - fn all_finality_pre_dispatch_data_ex() -> PreDispatchData { + #[cfg(test)] + fn all_finality_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = all_finality_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn all_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + fn all_finality_confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndMsgs( + call_info: ExtensionCallInfo::AllFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1356,13 +853,13 @@ pub(crate) mod tests { }, SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, @@ -1371,17 +868,18 @@ pub(crate) mod tests { } } - fn all_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData { + fn all_finality_confirmation_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = all_finality_confirmation_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn relay_finality_pre_dispatch_data() -> PreDispatchData { + fn relay_finality_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::RelayFinalityAndMsgs( + call_info: ExtensionCallInfo::RelayFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1392,32 +890,33 @@ pub(crate) mod tests { }, MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }), ), } } - fn relay_finality_pre_dispatch_data_ex() -> PreDispatchData { + fn relay_finality_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = relay_finality_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn relay_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + fn relay_finality_confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::RelayFinalityAndMsgs( + call_info: ExtensionCallInfo::RelayFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1428,7 +927,7 @@ pub(crate) mod tests { }, MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, @@ -1437,53 +936,55 @@ pub(crate) mod tests { } } - fn relay_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData { + fn relay_finality_confirmation_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = relay_finality_confirmation_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn parachain_finality_pre_dispatch_data() -> PreDispatchData { + fn parachain_finality_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndMsgs( + call_info: ExtensionCallInfo::ParachainFinalityAndMsgs( SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }), ), } } - fn parachain_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + fn parachain_finality_confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndMsgs( + call_info: ExtensionCallInfo::ParachainFinalityAndMsgs( SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, @@ -1492,33 +993,35 @@ pub(crate) mod tests { } } - fn delivery_pre_dispatch_data() -> PreDispatchData { + fn delivery_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( + call_info: ExtensionCallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }, )), } } - fn confirmation_pre_dispatch_data() -> PreDispatchData { + fn confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( + call_info: ExtensionCallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }), @@ -1527,14 +1030,14 @@ pub(crate) mod tests { } fn set_bundled_range_end( - mut pre_dispatch_data: PreDispatchData, + mut pre_dispatch_data: PreDispatchData, end: MessageNonce, - ) -> PreDispatchData { + ) -> PreDispatchData { let msg_info = match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndMsgs(_, _, ref mut info) => info, - CallInfo::RelayFinalityAndMsgs(_, ref mut info) => info, - CallInfo::ParachainFinalityAndMsgs(_, ref mut info) => info, - CallInfo::Msgs(ref mut info) => info, + ExtensionCallInfo::AllFinalityAndMsgs(_, _, ref mut info) => info, + ExtensionCallInfo::RelayFinalityAndMsgs(_, ref mut info) => info, + ExtensionCallInfo::ParachainFinalityAndMsgs(_, ref mut info) => info, + ExtensionCallInfo::Msgs(ref mut info) => info, }; if let MessagesCallInfo::ReceiveMessagesProof(ref mut msg_info) = msg_info { @@ -1545,20 +1048,17 @@ pub(crate) mod tests { } fn run_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); + let extension: TestExtension = BridgeRelayersSignedExtension(PhantomData); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_grandpa_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); + let extension: TestGrandpaExtension = BridgeRelayersSignedExtension(PhantomData); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_messages_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestMessagesExtension = - RefundSignedExtensionAdapter(RefundBridgedMessages(PhantomData)); + let extension: TestMessagesExtension = BridgeRelayersSignedExtension(PhantomData); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } @@ -1571,25 +1071,29 @@ pub(crate) mod tests { fn run_pre_dispatch( call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); + ) -> Result< + Option>, + TransactionValidityError, + > { + sp_tracing::try_init_simple(); + let extension: TestExtension = BridgeRelayersSignedExtension(PhantomData); extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_grandpa_pre_dispatch( call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); + ) -> Result< + Option>, + TransactionValidityError, + > { + let extension: TestGrandpaExtension = BridgeRelayersSignedExtension(PhantomData); extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_messages_pre_dispatch( call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestMessagesExtension = - RefundSignedExtensionAdapter(RefundBridgedMessages(PhantomData)); + ) -> Result>, TransactionValidityError> { + let extension: TestMessagesExtension = BridgeRelayersSignedExtension(PhantomData); extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } @@ -1609,7 +1113,7 @@ pub(crate) mod tests { } fn run_post_dispatch( - pre_dispatch_data: Option>, + pre_dispatch_data: Option>, dispatch_result: DispatchResult, ) { let post_dispatch_result = TestExtension::post_dispatch( @@ -1651,46 +1155,28 @@ pub(crate) mod tests { Balances::set_balance(&relayer_account_at_this_chain(), ExistentialDeposit::get()); // message delivery is failing - let fns = [run_validate, run_grandpa_validate, run_messages_validate]; - for f in fns { - assert_eq!(f(message_delivery_call(200)), Ok(Default::default()),); - assert_eq!( - f(parachain_finality_and_delivery_batch_call(200, 200)), - Ok(Default::default()), - ); - assert_eq!( - f(all_finality_and_delivery_batch_call(200, 200, 200)), - Ok(Default::default()), - ); - assert_eq!( - f(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Ok(Default::default()), - ); - } - - // message confirmation validation is passing + assert_eq!(run_validate(message_delivery_call(200)), Ok(Default::default()),); assert_eq!( - ignore_priority(run_validate(message_confirmation_call(200))), + run_validate(parachain_finality_and_delivery_batch_call(200, 200)), Ok(Default::default()), ); assert_eq!( - ignore_priority(run_messages_validate(message_confirmation_call(200))), + run_validate(all_finality_and_delivery_batch_call(200, 200, 200)), Ok(Default::default()), ); + // message confirmation validation is passing assert_eq!( - ignore_priority(run_validate(parachain_finality_and_confirmation_batch_call( - 200, 200 - ))), + ignore_priority(run_validate(message_confirmation_call(200))), Ok(Default::default()), ); assert_eq!( - ignore_priority(run_validate(all_finality_and_confirmation_batch_call( - 200, 200, 200 + ignore_priority(run_validate(parachain_finality_and_confirmation_batch_call( + 200, 200 ))), Ok(Default::default()), ); assert_eq!( - ignore_priority(run_validate(all_finality_and_confirmation_batch_call_ex( + ignore_priority(run_validate(all_finality_and_confirmation_batch_call( 200, 200, 200 ))), Ok(Default::default()), @@ -1706,28 +1192,25 @@ pub(crate) mod tests { BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) .unwrap(); - let fns = [run_validate, run_grandpa_validate, run_messages_validate]; - for f in fns { - let priority_of_100_messages_delivery = - f(message_delivery_call(200)).unwrap().priority; - let priority_of_200_messages_delivery = - f(message_delivery_call(300)).unwrap().priority; - assert!( - priority_of_200_messages_delivery > priority_of_100_messages_delivery, - "Invalid priorities: {} for 200 messages vs {} for 100 messages", - priority_of_200_messages_delivery, - priority_of_100_messages_delivery, - ); + let priority_of_100_messages_delivery = + run_validate(message_delivery_call(200)).unwrap().priority; + let priority_of_200_messages_delivery = + run_validate(message_delivery_call(300)).unwrap().priority; + assert!( + priority_of_200_messages_delivery > priority_of_100_messages_delivery, + "Invalid priorities: {} for 200 messages vs {} for 100 messages", + priority_of_200_messages_delivery, + priority_of_100_messages_delivery, + ); - let priority_of_100_messages_confirmation = - f(message_confirmation_call(200)).unwrap().priority; - let priority_of_200_messages_confirmation = - f(message_confirmation_call(300)).unwrap().priority; - assert_eq!( - priority_of_100_messages_confirmation, - priority_of_200_messages_confirmation - ); - } + let priority_of_100_messages_confirmation = + run_validate(message_confirmation_call(200)).unwrap().priority; + let priority_of_200_messages_confirmation = + run_validate(message_confirmation_call(300)).unwrap().priority; + assert_eq!( + priority_of_100_messages_confirmation, + priority_of_200_messages_confirmation + ); }); } @@ -1739,26 +1222,23 @@ pub(crate) mod tests { BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) .unwrap(); - let fns = [run_validate, run_grandpa_validate, run_messages_validate]; - for f in fns { - let priority_of_max_messages_delivery = f(message_delivery_call( - 100 + BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, - )) - .unwrap() - .priority; - let priority_of_more_than_max_messages_delivery = f(message_delivery_call( - 100 + BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1, - )) - .unwrap() - .priority; + let priority_of_max_messages_delivery = run_validate(message_delivery_call( + 100 + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + )) + .unwrap() + .priority; + let priority_of_more_than_max_messages_delivery = run_validate(message_delivery_call( + 100 + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1, + )) + .unwrap() + .priority; - assert!( - priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery, - "Invalid priorities: {} for MAX messages vs {} for MAX+1 messages", - priority_of_max_messages_delivery, - priority_of_more_than_max_messages_delivery, - ); - } + assert!( + priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery, + "Invalid priorities: {} for MAX messages vs {} for MAX+1 messages", + priority_of_max_messages_delivery, + priority_of_more_than_max_messages_delivery, + ); }); } @@ -1775,15 +1255,10 @@ pub(crate) mod tests { ignore_priority(run_validate(message_confirmation_call(200))), Ok(ValidTransaction::default()), ); - assert_eq!( ignore_priority(run_messages_validate(message_delivery_call(200))), Ok(ValidTransaction::default()), ); - assert_eq!( - ignore_priority(run_messages_validate(message_confirmation_call(200))), - Ok(ValidTransaction::default()), - ); assert_eq!( ignore_priority(run_validate(parachain_finality_and_delivery_batch_call(200, 200))), @@ -1800,24 +1275,12 @@ pub(crate) mod tests { ignore_priority(run_validate(all_finality_and_delivery_batch_call(200, 200, 200))), Ok(ValidTransaction::default()), ); - assert_eq!( - ignore_priority(run_validate(all_finality_and_delivery_batch_call_ex( - 200, 200, 200 - ))), - Ok(ValidTransaction::default()), - ); assert_eq!( ignore_priority(run_validate(all_finality_and_confirmation_batch_call( 200, 200, 200 ))), Ok(ValidTransaction::default()), ); - assert_eq!( - ignore_priority(run_validate(all_finality_and_confirmation_batch_call_ex( - 200, 200, 200 - ))), - Ok(ValidTransaction::default()), - ); }); } @@ -2103,13 +1566,10 @@ pub(crate) mod tests { let call = RuntimeCall::Utility(UtilityCall::batch_all { calls: vec![ RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { - at_relay_block: (100, RelayBlockHash::default()), + at_relay_block: (100, BridgedChainHash::default()), parachains: vec![ - (ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), [1u8; 32].into()), - ( - ParaId(BridgedUnderlyingParachain::PARACHAIN_ID + 1), - [1u8; 32].into(), - ), + (ParaId(TestParachain::get()), [1u8; 32].into()), + (ParaId(TestParachain::get() + 1), [1u8; 32].into()), ], parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, }), @@ -2250,7 +1710,7 @@ pub(crate) mod tests { // now repeat the same with size+weight refund: we expect smaller reward let mut pre_dispatch_data = all_finality_pre_dispatch_data(); match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndMsgs(ref mut info, ..) => { + ExtensionCallInfo::AllFinalityAndMsgs(ref mut info, ..) => { info.extra_weight.set_ref_time( frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, ); @@ -2356,7 +1816,7 @@ pub(crate) mod tests { let delivery_rewards_account_balance = Balances::free_balance(delivery_rewards_account()); - let test_stake: ThisChainBalance = TestStake::get(); + let test_stake: ThisChainBalance = Stake::get(); Balances::set_balance( &relayer_account_at_this_chain(), ExistentialDeposit::get() + test_stake * 10, @@ -2426,10 +1886,10 @@ pub(crate) mod tests { } fn run_analyze_call_result( - pre_dispatch_data: PreDispatchData, + pre_dispatch_data: PreDispatchData, dispatch_result: DispatchResult, ) -> RelayerAccountAction { - TestExtensionProvider::analyze_call_result( + TestExtension::analyze_call_result( Some(Some(pre_dispatch_data)), &dispatch_info(), &post_dispatch_info(), @@ -2494,41 +1954,29 @@ pub(crate) mod tests { } #[test] - fn messages_ext_only_parses_standalone_transactions() { + fn grandpa_ext_only_parses_valid_batches() { run_test(|| { initialize_environment(100, 100, 100); // relay + parachain + message delivery calls batch is ignored assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_delivery_batch_call(200, 200, 200) ), Ok(None), ); - assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_delivery_batch_call_ex(200, 200, 200) - ), - Ok(None), - ); // relay + parachain + message confirmation calls batch is ignored assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_confirmation_batch_call(200, 200, 200) ), Ok(None), ); - assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_confirmation_batch_call_ex(200, 200, 200) - ), - Ok(None), - ); // parachain + message delivery call batch is ignored assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_delivery_batch_call(200, 200) ), Ok(None), @@ -2536,43 +1984,31 @@ pub(crate) mod tests { // parachain + message confirmation call batch is ignored assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_confirmation_batch_call(200, 200) ), Ok(None), ); - // relay + message delivery call batch is ignored + // relay + message delivery call batch is accepted assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_delivery_batch_call(200, 200) ), - Ok(None), - ); - assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_delivery_batch_call_ex(200, 200) - ), - Ok(None), + Ok(Some(relay_finality_pre_dispatch_data().call_info)), ); - // relay + message confirmation call batch is ignored + // relay + message confirmation call batch is accepted assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_confirmation_batch_call(200, 200) ), - Ok(None), - ); - assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_confirmation_batch_call_ex(200, 200) - ), - Ok(None), + Ok(Some(relay_finality_confirmation_pre_dispatch_data().call_info)), ); // message delivery call batch is accepted assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &message_delivery_call(200) ), Ok(Some(delivery_pre_dispatch_data().call_info)), @@ -2580,7 +2016,7 @@ pub(crate) mod tests { // message confirmation call batch is accepted assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &message_confirmation_call(200) ), Ok(Some(confirmation_pre_dispatch_data().call_info)), @@ -2589,66 +2025,19 @@ pub(crate) mod tests { } #[test] - fn messages_ext_rejects_calls_with_obsolete_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_messages_pre_dispatch(message_delivery_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_messages_pre_dispatch(message_confirmation_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_messages_validate(message_delivery_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_messages_validate(message_confirmation_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn messages_ext_accepts_calls_with_new_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_messages_pre_dispatch(message_delivery_call(200)), - Ok(Some(delivery_pre_dispatch_data())), - ); - assert_eq!( - run_messages_pre_dispatch(message_confirmation_call(200)), - Ok(Some(confirmation_pre_dispatch_data())), - ); - - assert_eq!(run_messages_validate(message_delivery_call(200)), Ok(Default::default()),); - assert_eq!( - run_messages_validate(message_confirmation_call(200)), - Ok(Default::default()), - ); - }); - } - - #[test] - fn grandpa_ext_only_parses_valid_batches() { + fn messages_ext_only_parses_standalone_transactions() { run_test(|| { initialize_environment(100, 100, 100); // relay + parachain + message delivery calls batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_delivery_batch_call(200, 200, 200) ), Ok(None), ); assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_delivery_batch_call_ex(200, 200, 200) ), Ok(None), @@ -2656,13 +2045,13 @@ pub(crate) mod tests { // relay + parachain + message confirmation calls batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_confirmation_batch_call(200, 200, 200) ), Ok(None), ); assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_confirmation_batch_call_ex(200, 200, 200) ), Ok(None), @@ -2670,7 +2059,7 @@ pub(crate) mod tests { // parachain + message delivery call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_delivery_batch_call(200, 200) ), Ok(None), @@ -2678,43 +2067,43 @@ pub(crate) mod tests { // parachain + message confirmation call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_confirmation_batch_call(200, 200) ), Ok(None), ); - // relay + message delivery call batch is accepted + // relay + message delivery call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_delivery_batch_call(200, 200) ), - Ok(Some(relay_finality_pre_dispatch_data().call_info)), + Ok(None), ); assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_delivery_batch_call_ex(200, 200) ), - Ok(Some(relay_finality_pre_dispatch_data_ex().call_info)), + Ok(None), ); - // relay + message confirmation call batch is accepted + // relay + message confirmation call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_confirmation_batch_call(200, 200) ), - Ok(Some(relay_finality_confirmation_pre_dispatch_data().call_info)), + Ok(None), ); assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_confirmation_batch_call_ex(200, 200) ), - Ok(Some(relay_finality_confirmation_pre_dispatch_data_ex().call_info)), + Ok(None), ); // message delivery call batch is accepted assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &message_delivery_call(200) ), Ok(Some(delivery_pre_dispatch_data().call_info)), @@ -2722,7 +2111,7 @@ pub(crate) mod tests { // message confirmation call batch is accepted assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &message_confirmation_call(200) ), Ok(Some(confirmation_pre_dispatch_data().call_info)), @@ -2730,6 +2119,53 @@ pub(crate) mod tests { }); } + #[test] + fn messages_ext_rejects_calls_with_obsolete_messages() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!( + run_messages_pre_dispatch(message_delivery_call(100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_messages_pre_dispatch(message_confirmation_call(100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_messages_validate(message_delivery_call(100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_messages_validate(message_confirmation_call(100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + }); + } + + #[test] + fn messages_ext_accepts_calls_with_new_messages() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!( + run_messages_pre_dispatch(message_delivery_call(200)), + Ok(Some(delivery_pre_dispatch_data())), + ); + assert_eq!( + run_messages_pre_dispatch(message_confirmation_call(200)), + Ok(Some(confirmation_pre_dispatch_data())), + ); + + assert_eq!(run_messages_validate(message_delivery_call(200)), Ok(Default::default()),); + assert_eq!( + run_messages_validate(message_confirmation_call(200)), + Ok(Default::default()), + ); + }); + } + #[test] fn grandpa_ext_rejects_batch_with_obsolete_relay_chain_header() { run_test(|| { @@ -2874,7 +2310,7 @@ pub(crate) mod tests { fn does_not_panic_on_boosting_priority_of_empty_message_delivery_transaction() { run_test(|| { let best_delivered_message = - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; initialize_environment(100, 100, best_delivered_message); // register relayer so it gets priority boost @@ -2890,6 +2326,7 @@ pub(crate) mod tests { messages: DeliveredMessages { begin: 1, end: best_delivered_message }, }] .into(), + ..Default::default() }; pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); diff --git a/bridges/modules/relayers/src/extension/parachain_adapter.rs b/bridges/modules/relayers/src/extension/parachain_adapter.rs new file mode 100644 index 000000000000..b6f57cebc309 --- /dev/null +++ b/bridges/modules/relayers/src/extension/parachain_adapter.rs @@ -0,0 +1,182 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Adapter that allows using `pallet-bridge-relayers` as a signed extension in the +//! bridge with remote parachain. + +use crate::{ + extension::{ + grandpa_adapter::verify_submit_finality_proof_succeeded, verify_messages_call_succeeded, + }, + Config as BridgeRelayersConfig, LOG_TARGET, +}; + +use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; +use bp_runtime::{Parachain, StaticStrProvider}; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use frame_system::Config as SystemConfig; +use pallet_bridge_grandpa::{ + CallSubType as BridgeGrandpaCallSubtype, Config as BridgeGrandpaConfig, +}; +use pallet_bridge_messages::{ + CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, +}; +use pallet_bridge_parachains::{ + CallSubType as BridgeParachainsCallSubtype, Config as BridgeParachainsConfig, + SubmitParachainHeadsHelper, +}; +use sp_runtime::{ + traits::{Dispatchable, Get}, + transaction_validity::{TransactionPriority, TransactionValidityError}, +}; +use sp_std::marker::PhantomData; + +/// Adapter to be used in signed extension configuration, when bridging with remote parachains. +pub struct WithParachainExtensionConfig< + // signed extension identifier + IdProvider, + // runtime that implements `BridgeMessagesConfig`, which + // uses `BridgeParachainsConfig` to receive messages and + // confirmations from the remote chain. + Runtime, + // batch call unpacker + BatchCallUnpacker, + // instance of the `pallet-bridge-parachains`, tracked by this extension + BridgeParachainsPalletInstance, + // instance of BridgedChain `pallet-bridge-messages`, tracked by this extension + BridgeMessagesPalletInstance, + // message delivery transaction priority boost for every additional message + PriorityBoostPerMessage, +>( + PhantomData<( + IdProvider, + Runtime, + BatchCallUnpacker, + BridgeParachainsPalletInstance, + BridgeMessagesPalletInstance, + PriorityBoostPerMessage, + )>, +); + +impl ExtensionConfig for WithParachainExtensionConfig +where + ID: StaticStrProvider, + R: BridgeRelayersConfig + + BridgeMessagesConfig + + BridgeParachainsConfig + + BridgeGrandpaConfig, + BCU: BatchCallUnpacker, + PI: 'static, + MI: 'static, + P: Get, + R::RuntimeCall: Dispatchable + + BridgeGrandpaCallSubtype + + BridgeParachainsCallSubtype + + BridgeMessagesCallSubType, + >::BridgedChain: Parachain, +{ + type IdProvider = ID; + type Runtime = R; + type BridgeMessagesPalletInstance = MI; + type PriorityBoostPerMessage = P; + type Reward = R::Reward; + type RemoteGrandpaChainBlockNumber = + pallet_bridge_grandpa::BridgedBlockNumber; + + fn parse_and_check_for_obsolete_call( + call: &R::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + > { + let calls = BCU::unpack(call, 3); + let total_calls = calls.len(); + let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); + + let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info()); + let para_finality_call = calls.next().transpose()?.and_then(|c| { + let r = c.submit_parachain_heads_info_for( + >::BridgedChain::PARACHAIN_ID, + ); + r + }); + let relay_finality_call = + calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); + Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) { + (3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) => + Some(ExtensionCallInfo::AllFinalityAndMsgs( + relay_finality_call, + para_finality_call, + msgs_call, + )), + (2, None, Some(para_finality_call), Some(msgs_call)) => + Some(ExtensionCallInfo::ParachainFinalityAndMsgs(para_finality_call, msgs_call)), + (1, None, None, Some(msgs_call)) => Some(ExtensionCallInfo::Msgs(msgs_call)), + _ => None, + }) + } + + fn check_obsolete_parsed_call( + call: &R::RuntimeCall, + ) -> Result<&R::RuntimeCall, TransactionValidityError> { + call.check_obsolete_submit_finality_proof()?; + call.check_obsolete_submit_parachain_heads()?; + call.check_obsolete_call()?; + Ok(call) + } + + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &R::AccountId, + ) -> bool { + verify_submit_finality_proof_succeeded::( + call_info, call_data, relayer, + ) && verify_submit_parachain_head_succeeded::(call_info, call_data, relayer) && + verify_messages_call_succeeded::(call_info, call_data, relayer) + } +} + +/// If the batch call contains the parachain state update call, verify that it +/// has been successful. +/// +/// Only returns false when parachain state update call has failed. +pub(crate) fn verify_submit_parachain_head_succeeded( + call_info: &ExtensionCallInfo, + _call_data: &mut ExtensionCallData, + relayer: &::AccountId, +) -> bool +where + C: ExtensionConfig, + PI: 'static, + C::Runtime: BridgeParachainsConfig, +{ + let Some(para_proof_info) = call_info.submit_parachain_heads_info() else { return true }; + + if !SubmitParachainHeadsHelper::::was_successful(para_proof_info) { + // we only refund relayer if all calls have updated chain state + log::trace!( + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid parachain finality proof", + C::IdProvider::STR, + call_info.messages_call_info().lane_id(), + relayer, + ); + return false + } + + true +} diff --git a/bridges/bin/runtime-common/src/extensions/priority_calculator.rs b/bridges/modules/relayers/src/extension/priority.rs similarity index 96% rename from bridges/bin/runtime-common/src/extensions/priority_calculator.rs rename to bridges/modules/relayers/src/extension/priority.rs index 9f559dc13b64..da188eaf5bdd 100644 --- a/bridges/bin/runtime-common/src/extensions/priority_calculator.rs +++ b/bridges/modules/relayers/src/extension/priority.rs @@ -50,7 +50,6 @@ mod integrity_tests {} #[cfg(feature = "integrity-test")] mod integrity_tests { use super::{compute_priority_boost, ItemCount}; - use crate::extensions::refund_relayer_extension::RefundableParachainId; use bp_messages::MessageNonce; use bp_runtime::PreComputedSize; @@ -239,12 +238,18 @@ mod integrity_tests { /// almost the same priority if we'll add `tip_boost_per_header` tip to the `TX1`. We want /// to be sure that if we add plain `PriorityBoostPerHeader` priority to `TX1`, the priority /// will be close to `TX2` as well. - pub fn ensure_priority_boost_is_sane( + pub fn ensure_priority_boost_is_sane< + Runtime, + ParachainsInstance, + Para, + PriorityBoostPerHeader, + >( tip_boost_per_header: BalanceOf, ) where Runtime: pallet_transaction_payment::Config - + pallet_bridge_parachains::Config, - RefundableParachain: RefundableParachainId, + + pallet_bridge_parachains::Config, + ParachainsInstance: 'static, + Para: Parachain, PriorityBoostPerHeader: Get, Runtime::RuntimeCall: Dispatchable, BalanceOf: Send + Sync + FixedPointOperand, @@ -263,7 +268,8 @@ mod integrity_tests { |_n_headers, tip| { estimate_parachain_header_submit_transaction_priority::< Runtime, - RefundableParachain, + ParachainsInstance, + Para, >(tip) }, ); @@ -271,13 +277,18 @@ mod integrity_tests { /// Estimate parachain header delivery transaction priority. #[cfg(feature = "integrity-test")] - fn estimate_parachain_header_submit_transaction_priority( + fn estimate_parachain_header_submit_transaction_priority< + Runtime, + ParachainsInstance, + Para, + >( tip: BalanceOf, ) -> TransactionPriority where Runtime: pallet_transaction_payment::Config - + pallet_bridge_parachains::Config, - RefundableParachain: RefundableParachainId, + + pallet_bridge_parachains::Config, + ParachainsInstance: 'static, + Para: Parachain, Runtime::RuntimeCall: Dispatchable, BalanceOf: Send + Sync + FixedPointOperand, { @@ -287,14 +298,14 @@ mod integrity_tests { let base_tx_size = 512; // let's say we are relaying largest parachain headers and proof takes some more bytes let tx_call_size = >::WeightInfo::expected_extra_storage_proof_size() - .saturating_add(RefundableParachain::BridgedChain::MAX_HEADER_SIZE); + .saturating_add(Para::MAX_HEADER_SIZE); // finally we are able to estimate transaction size and weight let transaction_size = base_tx_size.saturating_add(tx_call_size); let transaction_weight = >::WeightInfo::submit_parachain_heads_weight( Runtime::DbWeight::get(), &PreComputedSize(transaction_size as _), diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs index 2c86ec01f5b9..b9627774db1e 100644 --- a/bridges/modules/relayers/src/lib.rs +++ b/bridges/modules/relayers/src/lib.rs @@ -36,13 +36,13 @@ pub use stake_adapter::StakeAndSlashNamed; pub use weights::WeightInfo; pub use weights_ext::WeightInfoExt; -pub mod benchmarking; - mod mock; mod payment_adapter; mod stake_adapter; mod weights_ext; +pub mod benchmarking; +pub mod extension; pub mod weights; /// The target that will be used when publishing logs related to this pallet. @@ -492,7 +492,7 @@ mod tests { get_ready_for_events(); Pallet::::register_relayer_reward( - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), ®ULAR_RELAYER, 100, ); @@ -502,9 +502,9 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(RewardRegistered { + event: TestEvent::BridgeRelayers(RewardRegistered { relayer: REGULAR_RELAYER, - rewards_account_params: TEST_REWARDS_ACCOUNT_PARAMS, + rewards_account_params: test_reward_account_param(), reward: 100 }), topics: vec![], @@ -519,7 +519,7 @@ mod tests { assert_noop!( Pallet::::claim_rewards( RuntimeOrigin::root(), - TEST_REWARDS_ACCOUNT_PARAMS + test_reward_account_param() ), DispatchError::BadOrigin, ); @@ -532,7 +532,7 @@ mod tests { assert_noop!( Pallet::::claim_rewards( RuntimeOrigin::signed(REGULAR_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS + test_reward_account_param() ), Error::::NoRewardForRelayer, ); @@ -544,13 +544,13 @@ mod tests { run_test(|| { RelayerRewards::::insert( FAILING_RELAYER, - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), 100, ); assert_noop!( Pallet::::claim_rewards( RuntimeOrigin::signed(FAILING_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS + test_reward_account_param() ), Error::::FailedToPayReward, ); @@ -564,15 +564,15 @@ mod tests { RelayerRewards::::insert( REGULAR_RELAYER, - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), 100, ); assert_ok!(Pallet::::claim_rewards( RuntimeOrigin::signed(REGULAR_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS + test_reward_account_param() )); assert_eq!( - RelayerRewards::::get(REGULAR_RELAYER, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(REGULAR_RELAYER, test_reward_account_param()), None ); @@ -581,9 +581,9 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(RewardPaid { + event: TestEvent::BridgeRelayers(RewardPaid { relayer: REGULAR_RELAYER, - rewards_account_params: TEST_REWARDS_ACCOUNT_PARAMS, + rewards_account_params: test_reward_account_param(), reward: 100 }), topics: vec![], @@ -595,16 +595,17 @@ mod tests { #[test] fn pay_reward_from_account_actually_pays_reward() { type Balances = pallet_balances::Pallet; - type PayLaneRewardFromAccount = bp_relayers::PayRewardFromAccount; + type PayLaneRewardFromAccount = + bp_relayers::PayRewardFromAccount; run_test(|| { let in_lane_0 = RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), + LaneId::new(1, 2), *b"test", RewardsAccountOwner::ThisChain, ); let out_lane_1 = RewardsAccountParams::new( - LaneId([0, 0, 0, 1]), + LaneId::new(1, 3), *b"test", RewardsAccountOwner::BridgedChain, ); @@ -676,7 +677,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { + event: TestEvent::BridgeRelayers(Event::RegistrationUpdated { relayer: REGISTER_RELAYER, registration: Registration { valid_till: 150, stake: Stake::get() }, }), @@ -744,7 +745,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { + event: TestEvent::BridgeRelayers(Event::RegistrationUpdated { relayer: REGISTER_RELAYER, registration: Registration { valid_till: 150, stake: Stake::get() } }), @@ -808,7 +809,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { + event: TestEvent::BridgeRelayers(Event::RegistrationUpdated { relayer: REGISTER_RELAYER, registration: Registration { valid_till: 150, stake: Stake::get() } }), @@ -870,7 +871,9 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::Deregistered { relayer: REGISTER_RELAYER }), + event: TestEvent::BridgeRelayers(Event::Deregistered { + relayer: REGISTER_RELAYER + }), topics: vec![], }), ); diff --git a/bridges/modules/relayers/src/mock.rs b/bridges/modules/relayers/src/mock.rs index 3124787896c3..de1d292b7c0f 100644 --- a/bridges/modules/relayers/src/mock.rs +++ b/bridges/modules/relayers/src/mock.rs @@ -18,51 +18,184 @@ use crate as pallet_bridge_relayers; -use bp_messages::LaneId; +use bp_header_chain::ChainWithGrandpa; +use bp_messages::{ + target_chain::{DispatchMessage, MessageDispatch}, + ChainWithMessages, LaneId, MessageNonce, +}; +use bp_parachains::SingleParaStoredHeaderDataBuilder; use bp_relayers::{ PayRewardFromAccount, PaymentProcedure, RewardsAccountOwner, RewardsAccountParams, }; +use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Parachain}; +use codec::Encode; use frame_support::{ - derive_impl, parameter_types, traits::fungible::Mutate, weights::RuntimeDbWeight, + derive_impl, parameter_types, + traits::fungible::Mutate, + weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, +}; +use pallet_transaction_payment::Multiplier; +use sp_core::{ConstU64, ConstU8, H256}; +use sp_runtime::{ + traits::{BlakeTwo256, ConstU32}, + BuildStorage, FixedPointNumber, Perquintill, StateVersion, }; -use sp_runtime::BuildStorage; -pub type AccountId = u64; -pub type Balance = u64; -pub type BlockNumber = u64; +/// Account identifier at `ThisChain`. +pub type ThisChainAccountId = u64; +/// Balance at `ThisChain`. +pub type ThisChainBalance = u64; +/// Block number at `ThisChain`. +pub type ThisChainBlockNumber = u32; +/// Hash at `ThisChain`. +pub type ThisChainHash = H256; +/// Hasher at `ThisChain`. +pub type ThisChainHasher = BlakeTwo256; +/// Header of `ThisChain`. +pub type ThisChainHeader = sp_runtime::generic::Header; +/// Block of `ThisChain`. +pub type ThisChainBlock = frame_system::mocking::MockBlockU32; + +/// Account identifier at the `BridgedChain`. +pub type BridgedChainAccountId = u128; +/// Balance at the `BridgedChain`. +pub type BridgedChainBalance = u128; +/// Block number at the `BridgedChain`. +pub type BridgedChainBlockNumber = u32; +/// Hash at the `BridgedChain`. +pub type BridgedChainHash = H256; +/// Hasher at the `BridgedChain`. +pub type BridgedChainHasher = BlakeTwo256; +/// Header of the `BridgedChain`. +pub type BridgedChainHeader = + sp_runtime::generic::Header; + +/// Bridged chain id used in tests. +pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; +/// Maximal extrinsic size at the `BridgedChain`. +pub const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; + +/// Underlying chain of `ThisChain`. +pub struct ThisUnderlyingChain; + +impl Chain for ThisUnderlyingChain { + const ID: ChainId = *b"tuch"; + + type BlockNumber = ThisChainBlockNumber; + type Hash = ThisChainHash; + type Hasher = ThisChainHasher; + type Header = ThisChainHeader; + type AccountId = ThisChainAccountId; + type Balance = ThisChainBalance; + type Nonce = u32; + type Signature = sp_runtime::MultiSignature; + + const STATE_VERSION: StateVersion = StateVersion::V1; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl ChainWithMessages for ThisUnderlyingChain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; + + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; +} + +/// Underlying chain of `BridgedChain`. +pub struct BridgedUnderlyingParachain; + +impl Chain for BridgedUnderlyingParachain { + const ID: ChainId = TEST_BRIDGED_CHAIN_ID; + + type BlockNumber = BridgedChainBlockNumber; + type Hash = BridgedChainHash; + type Hasher = BridgedChainHasher; + type Header = BridgedChainHeader; + type AccountId = BridgedChainAccountId; + type Balance = BridgedChainBalance; + type Nonce = u32; + type Signature = sp_runtime::MultiSignature; + + const STATE_VERSION: StateVersion = StateVersion::V1; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl ChainWithGrandpa for BridgedUnderlyingParachain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; + const MAX_AUTHORITIES_COUNT: u32 = 16; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; + const MAX_MANDATORY_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE: u32 = 64; +} + +impl ChainWithMessages for BridgedUnderlyingParachain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; +} + +impl Parachain for BridgedUnderlyingParachain { + const PARACHAIN_ID: u32 = 42; + const MAX_HEADER_SIZE: u32 = 1_024; +} pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< - AccountId, - BlockNumber, + ThisChainAccountId, + ThisChainBlockNumber, Balances, ReserveId, Stake, Lease, >; -type Block = frame_system::mocking::MockBlock; - frame_support::construct_runtime! { pub enum TestRuntime { System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Event}, - Relayers: pallet_bridge_relayers::{Pallet, Call, Event}, + Utility: pallet_utility, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, + BridgeGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, + BridgeParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event}, + BridgeMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, } } parameter_types! { + pub const BridgedParasPalletName: &'static str = "Paras"; pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; - pub const ExistentialDeposit: Balance = 1; + pub const ExistentialDeposit: ThisChainBalance = 1; pub const ReserveId: [u8; 8] = *b"brdgrlrs"; - pub const Stake: Balance = 1_000; - pub const Lease: BlockNumber = 8; + pub const Stake: ThisChainBalance = 1_000; + pub const Lease: ThisChainBlockNumber = 8; + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); + pub const TransactionBaseFee: ThisChainBalance = 0; + pub const TransactionByteFee: ThisChainBalance = 1; + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); + pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { - type Block = Block; - type AccountData = pallet_balances::AccountData; + type Block = ThisChainBlock; + // TODO: remove when https://github.com/paritytech/polkadot-sdk/pull/4543 merged + type BlockHashCount = ConstU32<10>; + type AccountData = pallet_balances::AccountData; type DbWeight = DbWeight; } @@ -72,9 +205,74 @@ impl pallet_balances::Config for TestRuntime { type AccountStore = System; } +impl pallet_utility::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] +impl pallet_transaction_payment::Config for TestRuntime { + type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = IdentityFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< + TestRuntime, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, + >; + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_bridge_grandpa::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgedChain = BridgedUnderlyingParachain; + type MaxFreeHeadersPerBlock = ConstU32<4>; + type FreeHeadersInterval = ConstU32<1_024>; + type HeadersToKeep = ConstU32<8>; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +impl pallet_bridge_parachains::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgesGrandpaPalletInstance = (); + type ParasPalletName = BridgedParasPalletName; + type ParaStoredHeaderDataBuilder = + SingleParaStoredHeaderDataBuilder; + type HeadsToKeep = ConstU32<8>; + type MaxParaHeadDataSize = ConstU32<1024>; + type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; +} + +impl pallet_bridge_messages::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + + type OutboundPayload = Vec; + + type InboundPayload = Vec; + type DeliveryPayments = (); + + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + TestRuntime, + (), + ConstU64<100_000>, + >; + type OnMessagesDelivered = (); + + type MessageDispatch = DummyMessageDispatch; + type ThisChain = ThisUnderlyingChain; + type BridgedChain = BridgedUnderlyingParachain; + type BridgedHeaderChain = BridgeGrandpa; +} + impl pallet_bridge_relayers::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; - type Reward = Balance; + type Reward = ThisChainBalance; type PaymentProcedure = TestPaymentProcedure; type StakeAndSlash = TestStakeAndSlash; type WeightInfo = (); @@ -82,9 +280,9 @@ impl pallet_bridge_relayers::Config for TestRuntime { #[cfg(feature = "runtime-benchmarks")] impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { - fn prepare_rewards_account(account_params: RewardsAccountParams, reward: Balance) { + fn prepare_rewards_account(account_params: RewardsAccountParams, reward: ThisChainBalance) { let rewards_account = - bp_relayers::PayRewardFromAccount::::rewards_account( + bp_relayers::PayRewardFromAccount::::rewards_account( account_params, ); Self::deposit_account(rewards_account, reward); @@ -95,35 +293,31 @@ impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { } } -/// Message lane that we're using in tests. -pub const TEST_REWARDS_ACCOUNT_PARAMS: RewardsAccountParams = - RewardsAccountParams::new(LaneId([0, 0, 0, 0]), *b"test", RewardsAccountOwner::ThisChain); - /// Regular relayer that may receive rewards. -pub const REGULAR_RELAYER: AccountId = 1; +pub const REGULAR_RELAYER: ThisChainAccountId = 1; /// Relayer that can't receive rewards. -pub const FAILING_RELAYER: AccountId = 2; +pub const FAILING_RELAYER: ThisChainAccountId = 2; /// Relayer that is able to register. -pub const REGISTER_RELAYER: AccountId = 42; +pub const REGISTER_RELAYER: ThisChainAccountId = 42; /// Payment procedure that rejects payments to the `FAILING_RELAYER`. pub struct TestPaymentProcedure; impl TestPaymentProcedure { - pub fn rewards_account(params: RewardsAccountParams) -> AccountId { - PayRewardFromAccount::<(), AccountId>::rewards_account(params) + pub fn rewards_account(params: RewardsAccountParams) -> ThisChainAccountId { + PayRewardFromAccount::<(), ThisChainAccountId>::rewards_account(params) } } -impl PaymentProcedure for TestPaymentProcedure { +impl PaymentProcedure for TestPaymentProcedure { type Error = (); fn pay_reward( - relayer: &AccountId, + relayer: &ThisChainAccountId, _lane_id: RewardsAccountParams, - _reward: Balance, + _reward: ThisChainBalance, ) -> Result<(), Self::Error> { match *relayer { FAILING_RELAYER => Err(()), @@ -132,6 +326,40 @@ impl PaymentProcedure for TestPaymentProcedure { } } +/// Dummy message dispatcher. +pub struct DummyMessageDispatch; + +impl DummyMessageDispatch { + pub fn deactivate(lane: LaneId) { + frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false); + } +} + +impl MessageDispatch for DummyMessageDispatch { + type DispatchPayload = Vec; + type DispatchLevelResult = (); + + fn is_active(lane: LaneId) -> bool { + frame_support::storage::unhashed::take::(&(b"inactive", lane).encode()[..]) != + Some(false) + } + + fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { + Weight::zero() + } + + fn dispatch( + _: DispatchMessage, + ) -> MessageDispatchResult { + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } + } +} + +/// Reward account params that we are using in tests. +pub fn test_reward_account_param() -> RewardsAccountParams { + RewardsAccountParams::new(LaneId::new(1, 2), *b"test", RewardsAccountOwner::ThisChain) +} + /// Return test externalities to use in tests. pub fn new_test_ext() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); diff --git a/bridges/modules/relayers/src/payment_adapter.rs b/bridges/modules/relayers/src/payment_adapter.rs index f75c409aca4f..3693793a3e5c 100644 --- a/bridges/modules/relayers/src/payment_adapter.rs +++ b/bridges/modules/relayers/src/payment_adapter.rs @@ -103,11 +103,11 @@ mod tests { use super::*; use crate::{mock::*, RelayerRewards}; - const RELAYER_1: AccountId = 1; - const RELAYER_2: AccountId = 2; - const RELAYER_3: AccountId = 3; + const RELAYER_1: ThisChainAccountId = 1; + const RELAYER_2: ThisChainAccountId = 2; + const RELAYER_3: ThisChainAccountId = 3; - fn relayers_rewards() -> RelayersRewards { + fn relayers_rewards() -> RelayersRewards { vec![(RELAYER_1, 2), (RELAYER_2, 3)].into_iter().collect() } @@ -117,16 +117,16 @@ mod tests { register_relayers_rewards::( &RELAYER_2, relayers_rewards(), - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), 50, ); assert_eq!( - RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_1, test_reward_account_param()), Some(100) ); assert_eq!( - RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_2, test_reward_account_param()), Some(150) ); }); @@ -138,20 +138,20 @@ mod tests { register_relayers_rewards::( &RELAYER_3, relayers_rewards(), - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), 50, ); assert_eq!( - RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_1, test_reward_account_param()), Some(100) ); assert_eq!( - RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_2, test_reward_account_param()), Some(150) ); assert_eq!( - RelayerRewards::::get(RELAYER_3, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_3, test_reward_account_param()), None ); }); diff --git a/bridges/modules/relayers/src/stake_adapter.rs b/bridges/modules/relayers/src/stake_adapter.rs index 7ba90d91dfd9..0c965e9e6bff 100644 --- a/bridges/modules/relayers/src/stake_adapter.rs +++ b/bridges/modules/relayers/src/stake_adapter.rs @@ -80,7 +80,7 @@ mod tests { use frame_support::traits::fungible::Mutate; - fn test_stake() -> Balance { + fn test_stake() -> ThisChainBalance { Stake::get() } @@ -130,7 +130,7 @@ mod tests { #[test] fn repatriate_reserved_works() { run_test(|| { - let beneficiary = TEST_REWARDS_ACCOUNT_PARAMS; + let beneficiary = test_reward_account_param(); let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary); let mut expected_balance = ExistentialDeposit::get(); @@ -186,7 +186,7 @@ mod tests { #[test] fn repatriate_reserved_doesnt_work_when_beneficiary_account_is_missing() { run_test(|| { - let beneficiary = TEST_REWARDS_ACCOUNT_PARAMS; + let beneficiary = test_reward_account_param(); let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary); Balances::mint_into(&3, test_stake() * 2).unwrap(); diff --git a/bridges/modules/xcm-bridge-hub-router/Cargo.toml b/bridges/modules/xcm-bridge-hub-router/Cargo.toml index ec7c3b562832..55824f6a7fe7 100644 --- a/bridges/modules/xcm-bridge-hub-router/Cargo.toml +++ b/bridges/modules/xcm-bridge-hub-router/Cargo.toml @@ -16,11 +16,9 @@ log = { workspace = true } scale-info = { features = ["bit-vec", "derive", "serde"], workspace = true } # Bridge dependencies - bp-xcm-bridge-hub-router = { workspace = true } # Substrate Dependencies - frame-benchmarking = { optional = true, workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } @@ -29,7 +27,6 @@ sp-runtime = { workspace = true } sp-std = { workspace = true } # Polkadot Dependencies - xcm = { workspace = true } xcm-builder = { workspace = true } diff --git a/bridges/modules/xcm-bridge-hub-router/src/benchmarking.rs b/bridges/modules/xcm-bridge-hub-router/src/benchmarking.rs index c4f9f534c1a4..3c4a10f82e7d 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/benchmarking.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/benchmarking.rs @@ -18,11 +18,9 @@ #![cfg(feature = "runtime-benchmarks")] -use crate::{Bridge, Call}; - -use bp_xcm_bridge_hub_router::{BridgeState, MINIMAL_DELIVERY_FEE_FACTOR}; +use crate::{DeliveryFeeFactor, MINIMAL_DELIVERY_FEE_FACTOR}; use frame_benchmarking::{benchmarks_instance_pallet, BenchmarkError}; -use frame_support::traits::{EnsureOrigin, Get, Hooks, UnfilteredDispatchable}; +use frame_support::traits::{Get, Hooks}; use sp_runtime::traits::Zero; use xcm::prelude::*; @@ -47,49 +45,16 @@ pub trait Config: crate::Config { benchmarks_instance_pallet! { on_initialize_when_non_congested { - Bridge::::put(BridgeState { - is_congested: false, - delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR + MINIMAL_DELIVERY_FEE_FACTOR, - }); + DeliveryFeeFactor::::put(MINIMAL_DELIVERY_FEE_FACTOR + MINIMAL_DELIVERY_FEE_FACTOR); }: { crate::Pallet::::on_initialize(Zero::zero()) } on_initialize_when_congested { - Bridge::::put(BridgeState { - is_congested: false, - delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR + MINIMAL_DELIVERY_FEE_FACTOR, - }); - + DeliveryFeeFactor::::put(MINIMAL_DELIVERY_FEE_FACTOR + MINIMAL_DELIVERY_FEE_FACTOR); let _ = T::ensure_bridged_target_destination()?; T::make_congested(); }: { crate::Pallet::::on_initialize(Zero::zero()) } - - report_bridge_status { - Bridge::::put(BridgeState::default()); - - let origin: T::RuntimeOrigin = T::BridgeHubOrigin::try_successful_origin().expect("expected valid BridgeHubOrigin"); - let bridge_id = Default::default(); - let is_congested = true; - - let call = Call::::report_bridge_status { bridge_id, is_congested }; - }: { call.dispatch_bypass_filter(origin)? } - verify { - assert!(Bridge::::get().is_congested); - } - - send_message { - let dest = T::ensure_bridged_target_destination()?; - let xcm = sp_std::vec![].into(); - - // make local queue congested, because it means additional db write - T::make_congested(); - }: { - send_xcm::>(dest, xcm).expect("message is sent") - } - verify { - assert!(Bridge::::get().delivery_fee_factor > MINIMAL_DELIVERY_FEE_FACTOR); - } } diff --git a/bridges/modules/xcm-bridge-hub-router/src/lib.rs b/bridges/modules/xcm-bridge-hub-router/src/lib.rs index 607394603466..860c1a83878c 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/lib.rs @@ -30,12 +30,9 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bp_xcm_bridge_hub_router::{ - BridgeState, XcmChannelStatusProvider, MINIMAL_DELIVERY_FEE_FACTOR, -}; +pub use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; use codec::Encode; use frame_support::traits::Get; -use sp_core::H256; use sp_runtime::{FixedPointNumber, FixedU128, Saturating}; use sp_std::vec::Vec; use xcm::prelude::*; @@ -49,6 +46,9 @@ pub mod weights; mod mock; +/// Minimal delivery fee factor. +pub const MINIMAL_DELIVERY_FEE_FACTOR: FixedU128 = FixedU128::from_u32(1); + /// The factor that is used to increase current message fee factor when bridge experiencing /// some lags. const EXPONENTIAL_FEE_BASE: FixedU128 = FixedU128::from_rational(105, 100); // 1.05 @@ -77,11 +77,16 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; /// Benchmarks results from runtime we're plugged into. type WeightInfo: WeightInfo; /// Universal location of this runtime. type UniversalLocation: Get; + /// Relative location of the supported sibling bridge hub. + type SiblingBridgeHubLocation: Get; /// The bridged network that this config is for if specified. /// Also used for filtering `Bridges` by `BridgedNetworkId`. /// If not specified, allows all networks pass through. @@ -93,13 +98,10 @@ pub mod pallet { /// Checks the XCM version for the destination. type DestinationVersion: GetVersion; - /// Origin of the sibling bridge hub that is allowed to report bridge status. - type BridgeHubOrigin: EnsureOrigin; /// Actual message sender (`HRMP` or `DMP`) to the sibling bridge hub location. type ToBridgeHubSender: SendXcm + InspectMessageQueues; - /// Underlying channel with the sibling bridge hub. It must match the channel, used - /// by the `Self::ToBridgeHubSender`. - type WithBridgeHubChannel: XcmChannelStatusProvider; + /// Local XCM channel manager. + type LocalXcmChannelManager: XcmChannelStatusProvider; /// Additional fee that is paid for every byte of the outbound message. type ByteFee: Get; @@ -113,118 +115,118 @@ pub mod pallet { #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { fn on_initialize(_n: BlockNumberFor) -> Weight { - // TODO: make sure that `WithBridgeHubChannel::is_congested` returns true if either - // of XCM channels (outbound/inbound) is suspended. Because if outbound is suspended - // that is definitely congestion. If inbound is suspended, then we are not able to - // receive the "report_bridge_status" signal (that maybe sent by the bridge hub). - - // if the channel with sibling/child bridge hub is suspended, we don't change - // anything - if T::WithBridgeHubChannel::is_congested() { + // if XCM channel is still congested, we don't change anything + if T::LocalXcmChannelManager::is_congested(&T::SiblingBridgeHubLocation::get()) { return T::WeightInfo::on_initialize_when_congested() } - // if bridge has reported congestion, we don't change anything - let mut bridge = Self::bridge(); - if bridge.is_congested { + // if we can't decrease the delivery fee factor anymore, we don't change anything + let mut delivery_fee_factor = Self::delivery_fee_factor(); + if delivery_fee_factor == MINIMAL_DELIVERY_FEE_FACTOR { return T::WeightInfo::on_initialize_when_congested() } - // if fee factor is already minimal, we don't change anything - if bridge.delivery_fee_factor == MINIMAL_DELIVERY_FEE_FACTOR { - return T::WeightInfo::on_initialize_when_congested() - } - - let previous_factor = bridge.delivery_fee_factor; - bridge.delivery_fee_factor = - MINIMAL_DELIVERY_FEE_FACTOR.max(bridge.delivery_fee_factor / EXPONENTIAL_FEE_BASE); + let previous_factor = delivery_fee_factor; + delivery_fee_factor = + MINIMAL_DELIVERY_FEE_FACTOR.max(delivery_fee_factor / EXPONENTIAL_FEE_BASE); log::info!( target: LOG_TARGET, - "Bridge queue is uncongested. Decreased fee factor from {} to {}", + "Bridge channel is uncongested. Decreased fee factor from {} to {}", previous_factor, - bridge.delivery_fee_factor, + delivery_fee_factor, ); + Self::deposit_event(Event::DeliveryFeeFactorDecreased { + new_value: delivery_fee_factor, + }); + + DeliveryFeeFactor::::put(delivery_fee_factor); - Bridge::::put(bridge); T::WeightInfo::on_initialize_when_non_congested() } } - #[pallet::call] - impl, I: 'static> Pallet { - /// Notification about congested bridge queue. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::report_bridge_status())] - pub fn report_bridge_status( - origin: OriginFor, - // this argument is not currently used, but to ease future migration, we'll keep it - // here - bridge_id: H256, - is_congested: bool, - ) -> DispatchResult { - let _ = T::BridgeHubOrigin::ensure_origin(origin)?; - - log::info!( - target: LOG_TARGET, - "Received bridge status from {:?}: congested = {}", - bridge_id, - is_congested, - ); - - Bridge::::mutate(|bridge| { - bridge.is_congested = is_congested; - }); - Ok(()) - } + /// Initialization value for the delivery fee factor. + #[pallet::type_value] + pub fn InitialFactor() -> FixedU128 { + MINIMAL_DELIVERY_FEE_FACTOR } - /// Bridge that we are using. + /// The number to multiply the base delivery fee by. /// - /// **bridges-v1** assumptions: all outbound messages through this router are using single lane - /// and to single remote consensus. If there is some other remote consensus that uses the same - /// bridge hub, the separate pallet instance shall be used, In `v2` we'll have all required - /// primitives (lane-id aka bridge-id, derived from XCM locations) to support multiple bridges - /// by the same pallet instance. + /// This factor is shared by all bridges, served by this pallet. For example, if this + /// chain (`Config::UniversalLocation`) opens two bridges ( + /// `X2(GlobalConsensus(Config::BridgedNetworkId::get()), Parachain(1000))` and + /// `X2(GlobalConsensus(Config::BridgedNetworkId::get()), Parachain(2000))`), then they + /// both will be sharing the same fee factor. This is because both bridges are sharing + /// the same local XCM channel with the child/sibling bridge hub, which we are using + /// to detect congestion: + /// + /// ```nocompile + /// ThisChain --- Local XCM channel --> Sibling Bridge Hub ------ + /// | | + /// | | + /// | | + /// Lane1 Lane2 + /// | | + /// | | + /// | | + /// \ / | + /// Parachain1 <-- Local XCM channel --- Remote Bridge Hub <------ + /// | + /// | + /// Parachain1 <-- Local XCM channel --------- + /// ``` + /// + /// If at least one of other channels is congested, the local XCM channel with sibling + /// bridge hub eventually becomes congested too. And we have no means to detect - which + /// bridge exactly causes the congestion. So the best solution here is not to make + /// any differences between all bridges, started by this chain. #[pallet::storage] - #[pallet::getter(fn bridge)] - pub type Bridge, I: 'static = ()> = StorageValue<_, BridgeState, ValueQuery>; + #[pallet::getter(fn delivery_fee_factor)] + pub type DeliveryFeeFactor, I: 'static = ()> = + StorageValue<_, FixedU128, ValueQuery, InitialFactor>; impl, I: 'static> Pallet { /// Called when new message is sent (queued to local outbound XCM queue) over the bridge. pub(crate) fn on_message_sent_to_bridge(message_size: u32) { - log::trace!( - target: LOG_TARGET, - "on_message_sent_to_bridge - message_size: {message_size:?}", - ); - let _ = Bridge::::try_mutate(|bridge| { - let is_channel_with_bridge_hub_congested = T::WithBridgeHubChannel::is_congested(); - let is_bridge_congested = bridge.is_congested; - - // if outbound queue is not congested AND bridge has not reported congestion, do - // nothing - if !is_channel_with_bridge_hub_congested && !is_bridge_congested { - return Err(()) - } - - // ok - we need to increase the fee factor, let's do that - let message_size_factor = FixedU128::from_u32(message_size.saturating_div(1024)) - .saturating_mul(MESSAGE_SIZE_FEE_BASE); - let total_factor = EXPONENTIAL_FEE_BASE.saturating_add(message_size_factor); - let previous_factor = bridge.delivery_fee_factor; - bridge.delivery_fee_factor = - bridge.delivery_fee_factor.saturating_mul(total_factor); + // if outbound channel is not congested, do nothing + if !T::LocalXcmChannelManager::is_congested(&T::SiblingBridgeHubLocation::get()) { + return + } + // ok - we need to increase the fee factor, let's do that + let message_size_factor = FixedU128::from_u32(message_size.saturating_div(1024)) + .saturating_mul(MESSAGE_SIZE_FEE_BASE); + let total_factor = EXPONENTIAL_FEE_BASE.saturating_add(message_size_factor); + DeliveryFeeFactor::::mutate(|f| { + let previous_factor = *f; + *f = f.saturating_mul(total_factor); log::info!( target: LOG_TARGET, "Bridge channel is congested. Increased fee factor from {} to {}", previous_factor, - bridge.delivery_fee_factor, + f, ); - - Ok(()) + Self::deposit_event(Event::DeliveryFeeFactorIncreased { new_value: *f }); + *f }); } } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// Delivery fee factor has been decreased. + DeliveryFeeFactorDecreased { + /// New value of the `DeliveryFeeFactor`. + new_value: FixedU128, + }, + /// Delivery fee factor has been increased. + DeliveryFeeFactorIncreased { + /// New value of the `DeliveryFeeFactor`. + new_value: FixedU128, + }, + } } /// We'll be using `SovereignPaidRemoteExporter` to send remote messages over the sibling/child @@ -259,17 +261,25 @@ impl, I: 'static> ExporterFor for Pallet { } // ensure that the message is sent to the expected bridged network and location. - let Some((bridge_hub_location, maybe_payment)) = - T::Bridges::exporter_for(network, remote_location, message) - else { - log::trace!( - target: LOG_TARGET, - "Router with bridged_network_id {:?} does not support bridging to network {:?} and remote_location {:?}!", - T::BridgedNetworkId::get(), - network, - remote_location, - ); - return None + let (bridge_hub_location, maybe_payment) = match T::Bridges::exporter_for( + network, + remote_location, + message, + ) { + Some((bridge_hub_location, maybe_payment)) + if bridge_hub_location.eq(&T::SiblingBridgeHubLocation::get()) => + (bridge_hub_location, maybe_payment), + _ => { + log::trace!( + target: LOG_TARGET, + "Router configured with bridged_network_id {:?} and sibling_bridge_hub_location: {:?} does not support bridging to network {:?} and remote_location {:?}!", + T::BridgedNetworkId::get(), + T::SiblingBridgeHubLocation::get(), + network, + remote_location, + ); + return None + }, }; // take `base_fee` from `T::Brides`, but it has to be the same `T::FeeAsset` @@ -279,8 +289,8 @@ impl, I: 'static> ExporterFor for Pallet { invalid_asset => { log::error!( target: LOG_TARGET, - "Router with bridged_network_id {:?} is configured for `T::FeeAsset` {:?} which is not \ - compatible with {:?} for bridge_hub_location: {:?} for bridging to {:?}/{:?}!", + "Router with bridged_network_id {:?} is configured for `T::FeeAsset` {:?} \ + which is not compatible with {:?} for bridge_hub_location: {:?} for bridging to {:?}/{:?}!", T::BridgedNetworkId::get(), T::FeeAsset::get(), invalid_asset, @@ -300,18 +310,18 @@ impl, I: 'static> ExporterFor for Pallet { let message_size = message.encoded_size(); let message_fee = (message_size as u128).saturating_mul(T::ByteFee::get()); let fee_sum = base_fee.saturating_add(message_fee); - let fee_factor = Self::bridge().delivery_fee_factor; - let fee = fee_factor.saturating_mul_int(fee_sum); + let fee_factor = Self::delivery_fee_factor(); + let fee = fee_factor.saturating_mul_int(fee_sum); let fee = if fee > 0 { Some((T::FeeAsset::get(), fee).into()) } else { None }; log::info!( target: LOG_TARGET, - "Validate send message to {:?} ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}", + "Going to send message to {:?} ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}", (network, remote_location), message_size, fee, - fee_factor + fee_factor, ); Some((bridge_hub_location, fee)) @@ -410,66 +420,57 @@ mod tests { use mock::*; use frame_support::traits::Hooks; + use frame_system::{EventRecord, Phase}; use sp_runtime::traits::One; - fn congested_bridge(delivery_fee_factor: FixedU128) -> BridgeState { - BridgeState { is_congested: true, delivery_fee_factor } - } - - fn uncongested_bridge(delivery_fee_factor: FixedU128) -> BridgeState { - BridgeState { is_congested: false, delivery_fee_factor } - } - #[test] fn initial_fee_factor_is_one() { run_test(|| { - assert_eq!( - Bridge::::get(), - uncongested_bridge(MINIMAL_DELIVERY_FEE_FACTOR), - ); + assert_eq!(DeliveryFeeFactor::::get(), MINIMAL_DELIVERY_FEE_FACTOR); }) } #[test] fn fee_factor_is_not_decreased_from_on_initialize_when_xcm_channel_is_congested() { run_test(|| { - Bridge::::put(uncongested_bridge(FixedU128::from_rational(125, 100))); - TestWithBridgeHubChannel::make_congested(); + DeliveryFeeFactor::::put(FixedU128::from_rational(125, 100)); + TestLocalXcmChannelManager::make_congested(&SiblingBridgeHubLocation::get()); - // it should not decrease, because xcm channel is congested - let old_bridge = XcmBridgeHubRouter::bridge(); + // it should not decrease, because queue is congested + let old_delivery_fee_factor = XcmBridgeHubRouter::delivery_fee_factor(); XcmBridgeHubRouter::on_initialize(One::one()); - assert_eq!(XcmBridgeHubRouter::bridge(), old_bridge); - }) - } - - #[test] - fn fee_factor_is_not_decreased_from_on_initialize_when_bridge_has_reported_congestion() { - run_test(|| { - Bridge::::put(congested_bridge(FixedU128::from_rational(125, 100))); + assert_eq!(XcmBridgeHubRouter::delivery_fee_factor(), old_delivery_fee_factor); - // it should not decrease, because bridge congested - let old_bridge = XcmBridgeHubRouter::bridge(); - XcmBridgeHubRouter::on_initialize(One::one()); - assert_eq!(XcmBridgeHubRouter::bridge(), old_bridge); + assert_eq!(System::events(), vec![]); }) } #[test] fn fee_factor_is_decreased_from_on_initialize_when_xcm_channel_is_uncongested() { run_test(|| { - Bridge::::put(uncongested_bridge(FixedU128::from_rational(125, 100))); + let initial_fee_factor = FixedU128::from_rational(125, 100); + DeliveryFeeFactor::::put(initial_fee_factor); - // it should eventually decreased to one - while XcmBridgeHubRouter::bridge().delivery_fee_factor > MINIMAL_DELIVERY_FEE_FACTOR { + // it shold eventually decreased to one + while XcmBridgeHubRouter::delivery_fee_factor() > MINIMAL_DELIVERY_FEE_FACTOR { XcmBridgeHubRouter::on_initialize(One::one()); } // verify that it doesn't decreases anymore XcmBridgeHubRouter::on_initialize(One::one()); + assert_eq!(XcmBridgeHubRouter::delivery_fee_factor(), MINIMAL_DELIVERY_FEE_FACTOR); + + // check emitted event + let first_system_event = System::events().first().cloned(); assert_eq!( - XcmBridgeHubRouter::bridge(), - uncongested_bridge(MINIMAL_DELIVERY_FEE_FACTOR) + first_system_event, + Some(EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmBridgeHubRouter(Event::DeliveryFeeFactorDecreased { + new_value: initial_fee_factor / EXPONENTIAL_FEE_BASE, + }), + topics: vec![], + }) ); }) } @@ -577,7 +578,7 @@ mod tests { // but when factor is larger than one, it increases the fee, so it becomes: // `(BASE_FEE + BYTE_FEE * msg_size) * F + HRMP_FEE` let factor = FixedU128::from_rational(125, 100); - Bridge::::put(uncongested_bridge(factor)); + DeliveryFeeFactor::::put(factor); let expected_fee = (FixedU128::saturating_from_integer(BASE_FEE + BYTE_FEE * (msg_size as u128)) * factor) @@ -591,45 +592,31 @@ mod tests { } #[test] - fn sent_message_doesnt_increase_factor_if_xcm_channel_is_uncongested() { + fn sent_message_doesnt_increase_factor_if_queue_is_uncongested() { run_test(|| { - let old_bridge = XcmBridgeHubRouter::bridge(); - assert_ok!(send_xcm::( - Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), - vec![ClearOrigin].into(), - ) - .map(drop)); + let old_delivery_fee_factor = XcmBridgeHubRouter::delivery_fee_factor(); + assert_eq!( + send_xcm::( + Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), + vec![ClearOrigin].into(), + ) + .map(drop), + Ok(()), + ); assert!(TestToBridgeHubSender::is_message_sent()); - assert_eq!(old_bridge, XcmBridgeHubRouter::bridge()); - }); - } + assert_eq!(old_delivery_fee_factor, XcmBridgeHubRouter::delivery_fee_factor()); - #[test] - fn sent_message_increases_factor_if_xcm_channel_is_congested() { - run_test(|| { - TestWithBridgeHubChannel::make_congested(); - - let old_bridge = XcmBridgeHubRouter::bridge(); - assert_ok!(send_xcm::( - Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), - vec![ClearOrigin].into(), - ) - .map(drop)); - - assert!(TestToBridgeHubSender::is_message_sent()); - assert!( - old_bridge.delivery_fee_factor < XcmBridgeHubRouter::bridge().delivery_fee_factor - ); + assert_eq!(System::events(), vec![]); }); } #[test] - fn sent_message_increases_factor_if_bridge_has_reported_congestion() { + fn sent_message_increases_factor_if_xcm_channel_is_congested() { run_test(|| { - Bridge::::put(congested_bridge(MINIMAL_DELIVERY_FEE_FACTOR)); + TestLocalXcmChannelManager::make_congested(&SiblingBridgeHubLocation::get()); - let old_bridge = XcmBridgeHubRouter::bridge(); + let old_delivery_fee_factor = XcmBridgeHubRouter::delivery_fee_factor(); assert_ok!(send_xcm::( Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), vec![ClearOrigin].into(), @@ -637,9 +624,20 @@ mod tests { .map(drop)); assert!(TestToBridgeHubSender::is_message_sent()); - assert!( - old_bridge.delivery_fee_factor < XcmBridgeHubRouter::bridge().delivery_fee_factor - ); + assert!(old_delivery_fee_factor < XcmBridgeHubRouter::delivery_fee_factor()); + + // check emitted event + let first_system_event = System::events().first().cloned(); + assert!(matches!( + first_system_event, + Some(EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmBridgeHubRouter( + Event::DeliveryFeeFactorIncreased { .. } + ), + .. + }) + )); }); } diff --git a/bridges/modules/xcm-bridge-hub-router/src/mock.rs b/bridges/modules/xcm-bridge-hub-router/src/mock.rs index 3e2c1bb369cb..f4806eb55b2a 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/mock.rs @@ -24,13 +24,11 @@ use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{Contains, Equals}, }; -use frame_system::EnsureRoot; use sp_runtime::{traits::ConstU128, BuildStorage}; use sp_std::cell::RefCell; use xcm::prelude::*; use xcm_builder::{InspectMessageQueues, NetworkExportTable, NetworkExportTableItem}; -pub type AccountId = u64; type Block = frame_system::mocking::MockBlock; /// HRMP fee. @@ -44,7 +42,7 @@ construct_runtime! { pub enum TestRuntime { System: frame_system::{Pallet, Call, Config, Storage, Event}, - XcmBridgeHubRouter: pallet_xcm_bridge_hub_router::{Pallet, Storage}, + XcmBridgeHubRouter: pallet_xcm_bridge_hub_router::{Pallet, Storage, Event}, } } @@ -72,17 +70,18 @@ impl frame_system::Config for TestRuntime { } impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime { + type RuntimeEvent = RuntimeEvent; type WeightInfo = (); type UniversalLocation = UniversalLocation; + type SiblingBridgeHubLocation = SiblingBridgeHubLocation; type BridgedNetworkId = BridgedNetworkId; type Bridges = NetworkExportTable; type DestinationVersion = LatestOrNoneForLocationVersionChecker>; - type BridgeHubOrigin = EnsureRoot; type ToBridgeHubSender = TestToBridgeHubSender; - type WithBridgeHubChannel = TestWithBridgeHubChannel; + type LocalXcmChannelManager = TestLocalXcmChannelManager; type ByteFee = ConstU128; type FeeAsset = BridgeFeeAsset; @@ -147,17 +146,22 @@ impl InspectMessageQueues for TestToBridgeHubSender { } } -pub struct TestWithBridgeHubChannel; +pub struct TestLocalXcmChannelManager; -impl TestWithBridgeHubChannel { - pub fn make_congested() { - frame_support::storage::unhashed::put(b"TestWithBridgeHubChannel.Congested", &true); +impl TestLocalXcmChannelManager { + pub fn make_congested(with: &Location) { + frame_support::storage::unhashed::put( + &(b"TestLocalXcmChannelManager.Congested", with).encode()[..], + &true, + ); } } -impl XcmChannelStatusProvider for TestWithBridgeHubChannel { - fn is_congested() -> bool { - frame_support::storage::unhashed::get_or_default(b"TestWithBridgeHubChannel.Congested") +impl XcmChannelStatusProvider for TestLocalXcmChannelManager { + fn is_congested(with: &Location) -> bool { + frame_support::storage::unhashed::get_or_default( + &(b"TestLocalXcmChannelManager.Congested", with).encode()[..], + ) } } @@ -169,7 +173,12 @@ pub fn new_test_ext() -> sp_io::TestExternalities { /// Run pallet test. pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(test) + new_test_ext().execute_with(|| { + System::set_block_number(1); + System::reset_events(); + + test() + }) } pub(crate) fn fake_message_hash(message: &Xcm) -> XcmHash { diff --git a/bridges/modules/xcm-bridge-hub-router/src/weights.rs b/bridges/modules/xcm-bridge-hub-router/src/weights.rs index b0c8fc6252cd..d9a0426fecaf 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/weights.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/weights.rs @@ -52,8 +52,6 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn on_initialize_when_non_congested() -> Weight; fn on_initialize_when_congested() -> Weight; - fn report_bridge_status() -> Weight; - fn send_message() -> Weight; } /// Weights for `pallet_xcm_bridge_hub_router` that are generated using one of the Bridge testnets. @@ -61,30 +59,20 @@ pub trait WeightInfo { /// Those weights are test only and must never be used in production. pub struct BridgeWeight(PhantomData); impl WeightInfo for BridgeWeight { - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) + /// Storage: `XcmBridgeHubRouter::DeliveryFeeFactor` (r:1 w:1) /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) + /// Proof: `XcmBridgeHubRouter::DeliveryFeeFactor` (`max_values`: Some(1), `max_size`: Some(16), + /// added: 511, mode: `MaxEncodedLen`) fn on_initialize_when_non_congested() -> Weight { // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `3518` - // Minimum execution time: 11_934 nanoseconds. - Weight::from_parts(12_201_000, 3518) + // Measured: `52` + // Estimated: `3517` + // Minimum execution time: 11_141 nanoseconds. + Weight::from_parts(11_339_000, 3517) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` /// (r:1 w:0) /// @@ -92,117 +80,44 @@ impl WeightInfo for BridgeWeight { /// w:0) fn on_initialize_when_congested() -> Weight { // Proof Size summary in bytes: - // Measured: `94` - // Estimated: `3559` - // Minimum execution time: 9_010 nanoseconds. - Weight::from_parts(9_594_000, 3559) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `1502` - // Minimum execution time: 10_427 nanoseconds. - Weight::from_parts(10_682_000, 1502) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn send_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3517` - // Minimum execution time: 19_709 nanoseconds. - Weight::from_parts(20_110_000, 3517) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Measured: `82` + // Estimated: `3547` + // Minimum execution time: 4_239 nanoseconds. + Weight::from_parts(4_383_000, 3547).saturating_add(T::DbWeight::get().reads(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` /// (r:1 w:0) /// /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 /// w:0) - fn on_initialize_when_non_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `3518` - // Minimum execution time: 11_934 nanoseconds. - Weight::from_parts(12_201_000, 3518) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) + /// Storage: `XcmBridgeHubRouter::DeliveryFeeFactor` (r:1 w:1) /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn on_initialize_when_congested() -> Weight { + /// Proof: `XcmBridgeHubRouter::DeliveryFeeFactor` (`max_values`: Some(1), `max_size`: Some(16), + /// added: 511, mode: `MaxEncodedLen`) + fn on_initialize_when_non_congested() -> Weight { // Proof Size summary in bytes: - // Measured: `94` - // Estimated: `3559` - // Minimum execution time: 9_010 nanoseconds. - Weight::from_parts(9_594_000, 3559) + // Measured: `52` + // Estimated: `3517` + // Minimum execution time: 11_141 nanoseconds. + Weight::from_parts(11_339_000, 3517) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `1502` - // Minimum execution time: 10_427 nanoseconds. - Weight::from_parts(10_682_000, 1502) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` /// (r:1 w:0) /// /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 /// w:0) - fn send_message() -> Weight { + fn on_initialize_when_congested() -> Weight { // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3517` - // Minimum execution time: 19_709 nanoseconds. - Weight::from_parts(20_110_000, 3517) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + // Measured: `82` + // Estimated: `3547` + // Minimum execution time: 4_239 nanoseconds. + Weight::from_parts(4_383_000, 3547).saturating_add(RocksDbWeight::get().reads(1_u64)) } } diff --git a/bridges/modules/xcm-bridge-hub/Cargo.toml b/bridges/modules/xcm-bridge-hub/Cargo.toml index 8fbf61a0a535..fe58b910a94e 100644 --- a/bridges/modules/xcm-bridge-hub/Cargo.toml +++ b/bridges/modules/xcm-bridge-hub/Cargo.toml @@ -20,7 +20,6 @@ bp-messages = { workspace = true } bp-runtime = { workspace = true } bp-xcm-bridge-hub = { workspace = true } pallet-bridge-messages = { workspace = true } -bridge-runtime-common = { workspace = true } # Substrate Dependencies frame-support = { workspace = true } @@ -35,27 +34,31 @@ xcm-builder = { workspace = true } xcm-executor = { workspace = true } [dev-dependencies] -bp-header-chain = { workspace = true, default-features = true } -pallet-balances = { workspace = true, default-features = true } -sp-io = { workspace = true, default-features = true } -bp-runtime = { features = ["test-helpers"], workspace = true } +pallet-balances = { workspace = true } +sp-io = { workspace = true } +bp-runtime = { workspace = true } +bp-header-chain = { workspace = true } pallet-xcm-bridge-hub-router = { workspace = true } +polkadot-parachain-primitives = { workspace = true } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-messages/std", "bp-runtime/std", "bp-xcm-bridge-hub/std", - "bridge-runtime-common/std", "codec/std", "frame-support/std", "frame-system/std", "log/std", + "pallet-balances/std", "pallet-bridge-messages/std", "pallet-xcm-bridge-hub-router/std", + "polkadot-parachain-primitives/std", "scale-info/std", "sp-core/std", + "sp-io/std", "sp-runtime/std", "sp-std/std", "xcm-builder/std", @@ -63,12 +66,12 @@ std = [ "xcm/std", ] runtime-benchmarks = [ - "bridge-runtime-common/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", "pallet-xcm-bridge-hub-router/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", diff --git a/bridges/modules/xcm-bridge-hub/src/dispatcher.rs b/bridges/modules/xcm-bridge-hub/src/dispatcher.rs new file mode 100644 index 000000000000..2412bb0f3bb0 --- /dev/null +++ b/bridges/modules/xcm-bridge-hub/src/dispatcher.rs @@ -0,0 +1,267 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! The code that allows to use the pallet (`pallet-xcm-bridge-hub`) as inbound +//! bridge messages dispatcher. Internally, it just forwards inbound blob to the +//! XCM-level blob dispatcher, which pushes message to some other queue (e.g. +//! to HRMP queue with the sibling target chain). +//! +//! This code is executed at the target bridge hub. + +use crate::{Config, Pallet, LOG_TARGET}; + +use bp_messages::{ + target_chain::{DispatchMessage, MessageDispatch}, + LaneId, +}; +use bp_runtime::messages::MessageDispatchResult; +use bp_xcm_bridge_hub::{LocalXcmChannelManager, XcmAsPlainPayload}; +use codec::{Decode, Encode}; +use frame_support::{weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; +use pallet_bridge_messages::{Config as BridgeMessagesConfig, WeightInfoExt}; +use scale_info::TypeInfo; +use sp_runtime::SaturatedConversion; +use xcm::prelude::*; +use xcm_builder::{DispatchBlob, DispatchBlobError}; + +/// Message dispatch result type for single message. +#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] +pub enum XcmBlobMessageDispatchResult { + /// We've been unable to decode message payload. + InvalidPayload, + /// Message has been dispatched. + Dispatched, + /// Message has **NOT** been dispatched because of given error. + NotDispatched(#[codec(skip)] Option), +} + +/// An easy way to access associated messages pallet weights. +type MessagesPalletWeights = + >::BridgeMessagesPalletInstance>>::WeightInfo; + +impl, I: 'static> MessageDispatch for Pallet +where + T: BridgeMessagesConfig, +{ + type DispatchPayload = XcmAsPlainPayload; + type DispatchLevelResult = XcmBlobMessageDispatchResult; + + fn is_active(lane: LaneId) -> bool { + Pallet::::bridge_by_lane_id(&lane) + .and_then(|(_, bridge)| bridge.bridge_origin_relative_location.try_as().cloned().ok()) + .map(|recipient: Location| !T::LocalXcmChannelManager::is_congested(&recipient)) + .unwrap_or(false) + } + + fn dispatch_weight(message: &mut DispatchMessage) -> Weight { + match message.data.payload { + Ok(ref payload) => { + let payload_size = payload.encoded_size().saturated_into(); + MessagesPalletWeights::::message_dispatch_weight(payload_size) + }, + Err(_) => Weight::zero(), + } + } + + fn dispatch( + message: DispatchMessage, + ) -> MessageDispatchResult { + let payload = match message.data.payload { + Ok(payload) => payload, + Err(e) => { + log::error!( + target: LOG_TARGET, + "dispatch - payload error: {e:?} for lane_id: {} and message_nonce: {:?}", + message.key.lane_id, + message.key.nonce + ); + return MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, + } + }, + }; + let dispatch_level_result = match T::BlobDispatcher::dispatch_blob(payload) { + Ok(_) => { + log::debug!( + target: LOG_TARGET, + "dispatch - `DispatchBlob::dispatch_blob` was ok for lane_id: {} and message_nonce: {:?}", + message.key.lane_id, + message.key.nonce + ); + XcmBlobMessageDispatchResult::Dispatched + }, + Err(e) => { + log::error!( + target: LOG_TARGET, + "dispatch - `DispatchBlob::dispatch_blob` failed with error: {e:?} for lane_id: {} and message_nonce: {:?}", + message.key.lane_id, + message.key.nonce + ); + XcmBlobMessageDispatchResult::NotDispatched(Some(e)) + }, + }; + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf}; + + use bp_messages::{target_chain::DispatchMessageData, MessageKey}; + use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState}; + use frame_support::assert_ok; + use pallet_bridge_messages::InboundLaneStorage; + use xcm_executor::traits::ConvertLocation; + + fn bridge() -> (Box, LaneId) { + let origin = OpenBridgeOrigin::sibling_parachain_origin(); + let with = bridged_asset_hub_universal_location(); + let locations = + XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + (locations, lane_id) + } + + fn run_test_with_opened_bridge(test: impl FnOnce()) { + run_test(|| { + let (bridge, lane_id) = bridge(); + + if !Bridges::::contains_key(bridge.bridge_id()) { + // insert bridge + Bridges::::insert( + bridge.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new( + bridge.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + bridge.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + bridge.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: LocationToAccountId::convert_location( + bridge.bridge_origin_relative_location(), + ) + .expect("valid accountId"), + deposit: 0, + lane_id, + }, + ); + LaneToBridge::::insert(lane_id, bridge.bridge_id()); + + // create lanes + let lanes_manager = LanesManagerOf::::new(); + if lanes_manager.create_inbound_lane(lane_id).is_ok() { + assert_eq!( + 0, + lanes_manager + .active_inbound_lane(lane_id) + .unwrap() + .storage() + .data() + .last_confirmed_nonce + ); + } + if lanes_manager.create_outbound_lane(lane_id).is_ok() { + assert!(lanes_manager + .active_outbound_lane(lane_id) + .unwrap() + .queued_messages() + .is_empty()); + } + } + assert_ok!(XcmOverBridge::do_try_state()); + + test(); + }); + } + + fn invalid_message() -> DispatchMessage> { + DispatchMessage { + key: MessageKey { lane_id: LaneId::new(1, 2), nonce: 1 }, + data: DispatchMessageData { payload: Err(codec::Error::from("test")) }, + } + } + + fn valid_message() -> DispatchMessage> { + DispatchMessage { + key: MessageKey { lane_id: LaneId::new(1, 2), nonce: 1 }, + data: DispatchMessageData { payload: Ok(vec![42]) }, + } + } + + #[test] + fn dispatcher_is_inactive_when_channel_with_target_chain_is_congested() { + run_test_with_opened_bridge(|| { + TestLocalXcmChannelManager::make_congested(); + assert!(!XcmOverBridge::is_active(bridge().1)); + }); + } + + #[test] + fn dispatcher_is_active_when_channel_with_target_chain_is_not_congested() { + run_test_with_opened_bridge(|| { + assert!(XcmOverBridge::is_active(bridge().1)); + }); + } + + #[test] + fn dispatch_weight_is_zero_if_we_have_failed_to_decode_message() { + run_test(|| { + assert_eq!(XcmOverBridge::dispatch_weight(&mut invalid_message()), Weight::zero()); + }); + } + + #[test] + fn dispatch_weight_is_non_zero_if_we_have_decoded_message() { + run_test(|| { + assert_ne!(XcmOverBridge::dispatch_weight(&mut valid_message()), Weight::zero()); + }); + } + + #[test] + fn message_is_not_dispatched_when_we_have_failed_to_decode_message() { + run_test(|| { + assert_eq!( + XcmOverBridge::dispatch(invalid_message()), + MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, + }, + ); + assert!(!TestBlobDispatcher::is_dispatched()); + }); + } + + #[test] + fn message_is_dispatched_when_we_have_decoded_message() { + run_test(|| { + assert_eq!( + XcmOverBridge::dispatch(valid_message()), + MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::Dispatched, + }, + ); + assert!(TestBlobDispatcher::is_dispatched()); + }); + } +} diff --git a/bridges/modules/xcm-bridge-hub/src/exporter.rs b/bridges/modules/xcm-bridge-hub/src/exporter.rs index 5bca3ad8e277..b42ae1e267f4 100644 --- a/bridges/modules/xcm-bridge-hub/src/exporter.rs +++ b/bridges/modules/xcm-bridge-hub/src/exporter.rs @@ -22,14 +22,29 @@ use crate::{Config, Pallet, LOG_TARGET}; -use bp_messages::source_chain::MessagesBridge; -use bp_xcm_bridge_hub::XcmAsPlainPayload; -use bridge_runtime_common::messages_xcm_extension::{LocalXcmQueueManager, SenderAndLane}; -use pallet_bridge_messages::{Config as BridgeMessagesConfig, Pallet as BridgeMessagesPallet}; +use crate::{BridgeOf, Bridges}; + +use bp_messages::{ + source_chain::{MessagesBridge, OnMessagesDelivered}, + LaneId, MessageNonce, +}; +use bp_xcm_bridge_hub::{BridgeId, BridgeState, LocalXcmChannelManager, XcmAsPlainPayload}; +use frame_support::{ensure, traits::Get}; +use pallet_bridge_messages::{ + Config as BridgeMessagesConfig, Error, Pallet as BridgeMessagesPallet, +}; use xcm::prelude::*; use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; use xcm_executor::traits::ExportXcm; +/// Maximal number of messages in the outbound bridge queue. Once we reach this limit, we +/// suspend a bridge. +const OUTBOUND_LANE_CONGESTED_THRESHOLD: MessageNonce = 8_192; + +/// After we have suspended the bridge, we wait until number of messages in the outbound bridge +/// queue drops to this count, before sending resuming the bridge. +const OUTBOUND_LANE_UNCONGESTED_THRESHOLD: MessageNonce = 1_024; + /// An easy way to access `HaulBlobExporter`. pub type PalletAsHaulBlobExporter = HaulBlobExporter< DummyHaulBlob, @@ -45,7 +60,8 @@ where T: BridgeMessagesConfig, { type Ticket = ( - SenderAndLane, + BridgeId, + BridgeOf, as MessagesBridge>::SendMessageArgs, XcmHash, ); @@ -57,12 +73,46 @@ where destination: &mut Option, message: &mut Option>, ) -> Result<(Self::Ticket, Assets), SendError> { - // Find supported lane_id. - let sender_and_lane = Self::lane_for( - universal_source.as_ref().ok_or(SendError::MissingArgument)?, - (&network, destination.as_ref().ok_or(SendError::MissingArgument)?), - ) - .ok_or(SendError::NotApplicable)?; + log::trace!( + target: LOG_TARGET, + "Validate for network: {network:?}, channel: {channel:?}, universal_source: {universal_source:?}, destination: {destination:?}" + ); + + // `HaulBlobExporter` may consume the `universal_source` and `destination` arguments, so + // let's save them before + let bridge_origin_universal_location = + universal_source.clone().take().ok_or(SendError::MissingArgument)?; + // Note: watch out this is `ExportMessage::destination`, which is relative to the `network`, + // which means it does not contain `GlobalConsensus`, We need to find `BridgeId` with + // `Self::bridge_locations` which requires **universal** location for destination. + let bridge_destination_universal_location = { + let dest = destination.clone().take().ok_or(SendError::MissingArgument)?; + match dest.global_consensus() { + Ok(dest_network) => { + log::trace!( + target: LOG_TARGET, + "Destination: {dest:?} is already universal, checking dest_network: {dest_network:?} and network: {network:?} if matches: {:?}", + dest_network == network + ); + ensure!(dest_network == network, SendError::Unroutable); + // ok, `dest` looks like a universal location, so let's use it + dest + }, + Err(_) => { + // `dest` is not a universal location, so we need to prepend it with + // `GlobalConsensus`. + dest.pushed_front_with(GlobalConsensus(network)).map_err(|error_data| { + log::error!( + target: LOG_TARGET, + "Destination: {:?} is not a universal and prepending with {:?} failed!", + error_data.0, + error_data.1, + ); + SendError::Unroutable + })? + }, + } + }; // check if we are able to route the message. We use existing `HaulBlobExporter` for that. // It will make all required changes and will encode message properly, so that the @@ -75,43 +125,215 @@ where message, )?; - let bridge_message = MessagesPallet::::validate_message(sender_and_lane.lane, &blob) + // prepare the origin relative location + let bridge_origin_relative_location = + bridge_origin_universal_location.relative_to(&T::UniversalLocation::get()); + + // then we are able to compute the `BridgeId` and find `LaneId` used to send messages + let locations = Self::bridge_locations( + bridge_origin_relative_location, + bridge_destination_universal_location.into(), + ) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Validate `bridge_locations` with error: {e:?}", + ); + SendError::Unroutable + })?; + let bridge = Self::bridge(locations.bridge_id()).ok_or(SendError::Unroutable)?; + + let bridge_message = MessagesPallet::::validate_message(bridge.lane_id, &blob) .map_err(|e| { - log::debug!( + match e { + Error::LanesManager(ref ei) => + log::error!(target: LOG_TARGET, "LanesManager: {ei:?}"), + Error::MessageRejectedByPallet(ref ei) => + log::error!(target: LOG_TARGET, "MessageRejectedByPallet: {ei:?}"), + Error::ReceptionConfirmation(ref ei) => + log::error!(target: LOG_TARGET, "ReceptionConfirmation: {ei:?}"), + _ => (), + }; + + log::error!( target: LOG_TARGET, - "XCM message {:?} cannot be exported because of bridge error {:?} on bridge {:?}", + "XCM message {:?} cannot be exported because of bridge error: {:?} on bridge {:?} and laneId: {:?}", id, e, - sender_and_lane.lane, + locations, + bridge.lane_id, ); SendError::Transport("BridgeValidateError") })?; - Ok(((sender_and_lane, bridge_message, id), price)) + Ok(((*locations.bridge_id(), bridge, bridge_message, id), price)) } - fn deliver((sender_and_lane, bridge_message, id): Self::Ticket) -> Result { - let lane_id = sender_and_lane.lane; + fn deliver( + (bridge_id, bridge, bridge_message, id): Self::Ticket, + ) -> Result { let artifacts = MessagesPallet::::send_message(bridge_message); log::info!( target: LOG_TARGET, - "XCM message {:?} has been enqueued at bridge {:?} with nonce {}", + "XCM message {:?} has been enqueued at bridge {:?} and lane_id: {:?} with nonce {}", id, - lane_id, + bridge_id, + bridge.lane_id, artifacts.nonce, ); - // notify XCM queue manager about updated lane state - LocalXcmQueueManager::::on_bridge_message_enqueued( - &sender_and_lane, - artifacts.enqueued_messages, - ); + // maybe we need switch to congested state + Self::on_bridge_message_enqueued(bridge_id, bridge, artifacts.enqueued_messages); Ok(id) } } +impl, I: 'static> OnMessagesDelivered for Pallet { + fn on_messages_delivered(lane_id: LaneId, enqueued_messages: MessageNonce) { + Self::on_bridge_messages_delivered(lane_id, enqueued_messages); + } +} + +impl, I: 'static> Pallet { + /// Called when new message is pushed onto outbound bridge queue. + fn on_bridge_message_enqueued( + bridge_id: BridgeId, + bridge: BridgeOf, + enqueued_messages: MessageNonce, + ) { + // if the bridge queue is not congested, we don't want to do anything + let is_congested = enqueued_messages > OUTBOUND_LANE_CONGESTED_THRESHOLD; + if !is_congested { + return + } + + // TODO: https://github.com/paritytech/parity-bridges-common/issues/2006 we either need fishermens + // to watch this rule violation (suspended, but keep sending new messages), or we need a + // hard limit for that like other XCM queues have + + // check if the lane is already suspended. If it is, do nothing. We still accept new + // messages to the suspended bridge, hoping that it'll be actually resumed soon + if bridge.state == BridgeState::Suspended { + return + } + + // else - suspend the bridge + let bridge_origin_relative_location = match bridge.bridge_origin_relative_location.try_as() + { + Ok(bridge_origin_relative_location) => bridge_origin_relative_location, + Err(_) => { + log::debug!( + target: LOG_TARGET, + "Failed to convert the bridge {:?} origin location {:?}", + bridge_id, + bridge.bridge_origin_relative_location, + ); + + return + }, + }; + let suspend_result = + T::LocalXcmChannelManager::suspend_bridge(bridge_origin_relative_location, bridge_id); + match suspend_result { + Ok(_) => { + log::debug!( + target: LOG_TARGET, + "Suspended the bridge {:?}, originated by the {:?}", + bridge_id, + bridge.bridge_origin_relative_location, + ); + }, + Err(e) => { + log::debug!( + target: LOG_TARGET, + "Failed to suspended the bridge {:?}, originated by the {:?}: {:?}", + bridge_id, + bridge.bridge_origin_relative_location, + e, + ); + + return + }, + } + + // and remember that we have suspended the bridge + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Suspended; + }); + } + + /// Must be called whenever we receive a message delivery confirmation. + fn on_bridge_messages_delivered(lane_id: LaneId, enqueued_messages: MessageNonce) { + // if the bridge queue is still congested, we don't want to do anything + let is_congested = enqueued_messages > OUTBOUND_LANE_UNCONGESTED_THRESHOLD; + if is_congested { + return + } + + // if we have not suspended the bridge before (or it is closed), we don't want to do + // anything + let (bridge_id, bridge) = match Self::bridge_by_lane_id(&lane_id) { + Some(bridge) if bridge.1.state == BridgeState::Suspended => bridge, + _ => { + // if there is no bridge or it has been closed, then we don't need to send resume + // signal to the local origin - it has closed bridge itself, so it should have + // alrady pruned everything else + return + }, + }; + + // else - resume the bridge + let bridge_origin_relative_location = (*bridge.bridge_origin_relative_location).try_into(); + let bridge_origin_relative_location = match bridge_origin_relative_location { + Ok(bridge_origin_relative_location) => bridge_origin_relative_location, + Err(e) => { + log::debug!( + target: LOG_TARGET, + "Failed to convert the bridge {:?} location for lane_id: {:?}, error {:?}", + bridge_id, + lane_id, + e, + ); + + return + }, + }; + + let resume_result = + T::LocalXcmChannelManager::resume_bridge(&bridge_origin_relative_location, bridge_id); + match resume_result { + Ok(_) => { + log::debug!( + target: LOG_TARGET, + "Resumed the bridge {:?} and lane_id: {:?}, originated by the {:?}", + bridge_id, + lane_id, + bridge_origin_relative_location, + ); + }, + Err(e) => { + log::debug!( + target: LOG_TARGET, + "Failed to resume the bridge {:?} and lane_id: {:?}, originated by the {:?}: {:?}", + bridge_id, + lane_id, + bridge_origin_relative_location, + e, + ); + + return + }, + } + + // and forget that we have previously suspended the bridge + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Opened; + }); + } +} + /// Dummy implementation of the `HaulBlob` trait that is never called. /// /// We are using `HaulBlobExporter`, which requires `HaulBlob` implementation. It assumes that @@ -130,30 +352,203 @@ impl HaulBlob for DummyHaulBlob { #[cfg(test)] mod tests { use super::*; - use crate::mock::*; + use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf}; + use bp_runtime::RangeInclusiveExt; + use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState}; use frame_support::assert_ok; - use xcm_executor::traits::export_xcm; + use pallet_bridge_messages::InboundLaneStorage; + use xcm_builder::{NetworkExportTable, UnpaidRemoteExporter}; + use xcm_executor::traits::{export_xcm, ConvertLocation}; fn universal_source() -> InteriorLocation { - [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)].into() + SiblingUniversalLocation::get() } fn bridged_relative_destination() -> InteriorLocation { BridgedRelativeDestination::get() } + fn bridged_universal_destination() -> InteriorLocation { + BridgedUniversalDestination::get() + } + + fn open_lane() -> (BridgeLocations, LaneId) { + // open expected outbound lane + let origin = OpenBridgeOrigin::sibling_parachain_origin(); + let with = bridged_asset_hub_universal_location(); + let locations = + XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + + if !Bridges::::contains_key(locations.bridge_id()) { + // insert bridge + Bridges::::insert( + locations.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new(SiblingLocation::get().into()), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: LocationToAccountId::convert_location( + locations.bridge_origin_relative_location(), + ) + .expect("valid accountId"), + deposit: 0, + lane_id, + }, + ); + LaneToBridge::::insert(lane_id, locations.bridge_id()); + + // create lanes + let lanes_manager = LanesManagerOf::::new(); + if lanes_manager.create_inbound_lane(lane_id).is_ok() { + assert_eq!( + 0, + lanes_manager + .active_inbound_lane(lane_id) + .unwrap() + .storage() + .data() + .last_confirmed_nonce + ); + } + if lanes_manager.create_outbound_lane(lane_id).is_ok() { + assert!(lanes_manager + .active_outbound_lane(lane_id) + .unwrap() + .queued_messages() + .is_empty()); + } + } + assert_ok!(XcmOverBridge::do_try_state()); + + (*locations, lane_id) + } + + fn open_lane_and_send_regular_message() -> (BridgeId, LaneId) { + let (locations, lane_id) = open_lane(); + + // now let's try to enqueue message using our `ExportXcm` implementation + export_xcm::( + BridgedRelayNetwork::get(), + 0, + locations.bridge_origin_universal_location().clone(), + locations.bridge_destination_universal_location().clone(), + vec![Instruction::ClearOrigin].into(), + ) + .unwrap(); + + (*locations.bridge_id(), lane_id) + } + #[test] - fn export_works() { + fn exporter_works() { run_test(|| { - assert_ok!(export_xcm::( - BridgedRelayNetwork::get(), - 0, - universal_source(), - bridged_relative_destination(), - vec![Instruction::ClearOrigin].into(), - )); - }) + let (_, lane_id) = open_lane_and_send_regular_message(); + + // double check that the message has been pushed to the expected lane + // (it should already been checked during `send_message` call) + assert!(!LanesManagerOf::::new() + .active_outbound_lane(lane_id) + .unwrap() + .queued_messages() + .is_empty()); + }); + } + + #[test] + fn exporter_does_not_suspend_the_bridge_if_outbound_bridge_queue_is_not_congested() { + run_test(|| { + let (bridge_id, _) = open_lane_and_send_regular_message(); + assert!(!TestLocalXcmChannelManager::is_bridge_suspened()); + assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Opened); + }); + } + + #[test] + fn exporter_does_not_suspend_the_bridge_if_it_is_already_suspended() { + run_test(|| { + let (bridge_id, _) = open_lane_and_send_regular_message(); + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Suspended; + }); + for _ in 1..OUTBOUND_LANE_CONGESTED_THRESHOLD { + open_lane_and_send_regular_message(); + } + + open_lane_and_send_regular_message(); + assert!(!TestLocalXcmChannelManager::is_bridge_suspened()); + }); + } + + #[test] + fn exporter_suspends_the_bridge_if_outbound_bridge_queue_is_congested() { + run_test(|| { + let (bridge_id, _) = open_lane_and_send_regular_message(); + for _ in 1..OUTBOUND_LANE_CONGESTED_THRESHOLD { + open_lane_and_send_regular_message(); + } + + assert!(!TestLocalXcmChannelManager::is_bridge_suspened()); + assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Opened); + + open_lane_and_send_regular_message(); + assert!(TestLocalXcmChannelManager::is_bridge_suspened()); + assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Suspended); + }); + } + + #[test] + fn bridge_is_not_resumed_if_outbound_bridge_queue_is_still_congested() { + run_test(|| { + let (bridge_id, lane_id) = open_lane_and_send_regular_message(); + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Suspended; + }); + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + OUTBOUND_LANE_UNCONGESTED_THRESHOLD + 1, + ); + + assert!(!TestLocalXcmChannelManager::is_bridge_resumed()); + assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Suspended); + }); + } + + #[test] + fn bridge_is_not_resumed_if_it_was_not_suspended_before() { + run_test(|| { + let (bridge_id, lane_id) = open_lane_and_send_regular_message(); + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + OUTBOUND_LANE_UNCONGESTED_THRESHOLD, + ); + + assert!(!TestLocalXcmChannelManager::is_bridge_resumed()); + assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Opened); + }); + } + + #[test] + fn bridge_is_resumed_when_enough_messages_are_delivered() { + run_test(|| { + let (bridge_id, lane_id) = open_lane_and_send_regular_message(); + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Suspended; + }); + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + OUTBOUND_LANE_UNCONGESTED_THRESHOLD, + ); + + assert!(TestLocalXcmChannelManager::is_bridge_resumed()); + assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Opened); + }); } #[test] @@ -186,23 +581,56 @@ mod tests { #[test] fn exporter_computes_correct_lane_id() { run_test(|| { - let expected_lane_id = TEST_LANE_ID; + assert_ne!(bridged_universal_destination(), bridged_relative_destination()); - assert_eq!( - XcmOverBridge::validate( - BridgedRelayNetwork::get(), - 0, - &mut Some(universal_source()), - &mut Some(bridged_relative_destination()), - &mut Some(Vec::new().into()), - ) - .unwrap() - .0 - .0 - .lane, - expected_lane_id, - ); - }) + let locations = BridgeLocations::bridge_locations( + UniversalLocation::get(), + SiblingLocation::get(), + bridged_universal_destination(), + BridgedRelayNetwork::get(), + ) + .unwrap(); + let expected_bridge_id = locations.bridge_id(); + let expected_lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + + if LanesManagerOf::::new() + .create_outbound_lane(expected_lane_id) + .is_ok() + { + Bridges::::insert( + expected_bridge_id, + Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: [0u8; 32].into(), + deposit: 0, + lane_id: expected_lane_id, + }, + ); + } + + let ticket = XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + // Note: The `ExportMessage` expects relative `InteriorLocation` in the + // `BridgedRelayNetwork`. + &mut Some(bridged_relative_destination()), + &mut Some(Vec::new().into()), + ) + .unwrap() + .0; + assert_eq!(&ticket.0, expected_bridge_id); + assert_eq!(ticket.1.lane_id, expected_lane_id); + }); } #[test] @@ -210,30 +638,44 @@ mod tests { run_test(|| { // valid routable destination let dest = Location::new(2, BridgedUniversalDestination::get()); - let expected_lane_id = TEST_LANE_ID; + + // open bridge + let (_, expected_lane_id) = open_lane(); // check before - no messages assert_eq!( pallet_bridge_messages::Pallet::::outbound_lane_data( expected_lane_id ) + .unwrap() .queued_messages() .saturating_len(), 0 ); + // send `ExportMessage(message)` by `UnpaidRemoteExporter`. + TestExportXcmWithXcmOverBridge::set_origin_for_execute(SiblingLocation::get()); + assert_ok!(send_xcm::< + UnpaidRemoteExporter< + NetworkExportTable, + TestExportXcmWithXcmOverBridge, + UniversalLocation, + >, + >(dest.clone(), Xcm::<()>::default())); + // send `ExportMessage(message)` by `pallet_xcm_bridge_hub_router`. TestExportXcmWithXcmOverBridge::set_origin_for_execute(SiblingLocation::get()); - assert_ok!(send_xcm::(dest, Xcm::<()>::default())); + assert_ok!(send_xcm::(dest.clone(), Xcm::<()>::default())); // check after - a message ready to be relayed assert_eq!( pallet_bridge_messages::Pallet::::outbound_lane_data( expected_lane_id ) + .unwrap() .queued_messages() .saturating_len(), - 1 + 2 ); }) } diff --git a/bridges/modules/xcm-bridge-hub/src/lib.rs b/bridges/modules/xcm-bridge-hub/src/lib.rs index 60b988497fc5..02d578386a75 100644 --- a/bridges/modules/xcm-bridge-hub/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub/src/lib.rs @@ -14,19 +14,156 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Module that adds XCM support to bridge pallets. +//! Module that adds XCM support to bridge pallets. The pallet allows to dynamically +//! open and close bridges between local (to this pallet location) and remote XCM +//! destinations. +//! +//! The `pallet_xcm_bridge_hub` pallet is used to manage (open, close) bridges between chains from +//! different consensuses. The new extrinsics `fn open_bridge` and `fn close_bridge` are introduced. +//! Other chains can manage channels with different bridged global consensuses. +//! +//! # Concept of `lane` and `LaneId` +//! +//! There is another `pallet_bridge_messages` pallet that handles inbound/outbound lanes for +//! messages. Each lane is a unique connection between two chains from different consensuses and is +//! identified by `LaneId`. `LaneId` is generated once when a new bridge is requested by `fn +//! open_bridge`. It is generated by `BridgeLocations::calculate_lane_id` based on the following +//! parameters: +//! - Source `bridge_origin_universal_location` (latest XCM) +//! - Destination `bridge_destination_universal_location` (latest XCM) +//! - XCM version (both sides of the bridge must use the same parameters to generate the same +//! `LaneId`) +//! - `bridge_origin_universal_location`, `bridge_destination_universal_location` is converted to +//! the `Versioned*` structs +//! +//! `LaneId` is expected to never change because: +//! - We need the same `LaneId` on both sides of the bridge, as `LaneId` is part of the message key +//! proofs. +//! - Runtime upgrades are entirely asynchronous. +//! - We already have a running production Polkadot/Kusama bridge that uses `LaneId([0, 0, 0, 0])`. +//! +//! `LaneId` is backward compatible, meaning it can be encoded/decoded from the older format `[u8; +//! 4]` used for static lanes, as well as the new format `H256` generated by +//! `BridgeLocations::calculate_lane_id`. +//! +//! # Concept of `bridge` and `BridgeId` +//! +//! The `pallet_xcm_bridge_hub` pallet needs to store some metadata about opened bridges. The bridge +//! (or bridge metadata) is stored under the `BridgeId` key. +//! +//! `BridgeId` is generated from `bridge_origin_relative_location` and +//! `bridge_origin_universal_location` using the `latest` XCM structs. `BridgeId` is not transferred +//! over the bridge; it is only important for local consensus. It essentially serves as an index/key +//! to bridge metadata. All the XCM infrastructure around `XcmExecutor`, `SendXcm`, `ExportXcm` use +//! the `latest` XCM, so `BridgeId` must remain compatible with the `latest` XCM. For example, we +//! have an `ExportXcm` implementation in `exporter.rs` that handles the `ExportMessage` instruction +//! with `universal_source` and `destination` (latest XCM), so we need to create `BridgeId` and the +//! corresponding `LaneId`. +//! +//! # Migrations and State +//! +//! This pallet implements `try_state`, ensuring compatibility and checking everything so we know if +//! any migration is needed. `do_try_state` checks for `BridgeId` compatibility, which is +//! recalculated on runtime upgrade. Upgrading to a new XCM version should not break anything, +//! except removing older XCM versions. In such cases, we need to add migration for `BridgeId` and +//! stored `Versioned*` structs and update `LaneToBridge` mapping, but this won't affect `LaneId` +//! over the bridge. +//! +//! # How to Open a Bridge? +//! +//! The `pallet_xcm_bridge_hub` pallet has the extrinsic `fn open_bridge` and an important +//! configuration `pallet_xcm_bridge_hub::Config::OpenBridgeOrigin`, which translates the call's +//! origin to the XCM `Location` and converts it to the `bridge_origin_universal_location`. With the +//! current setup, this origin/location is expected to be either the relay chain or a sibling +//! parachain as one side of the bridge. Another parameter is +//! `bridge_destination_universal_location`, which is the other side of the bridge from a different +//! global consensus. +//! +//! Every bridge between two XCM locations has a dedicated lane in associated +//! messages pallet. Assuming that this pallet is deployed at the bridge hub +//! parachain and there's a similar pallet at the bridged network, the dynamic +//! bridge lifetime is as follows: +//! +//! 1) the sibling parachain opens a XCMP channel with this bridge hub; +//! +//! 2) the sibling parachain funds its sovereign parachain account at this bridge hub. It shall hold +//! enough funds to pay for the bridge (see `BridgeDeposit`); +//! +//! 3) the sibling parachain opens the bridge by sending XCM `Transact` instruction with the +//! `open_bridge` call. The `BridgeDeposit` amount is reserved on the sovereign account of +//! sibling parachain; +//! +//! 4) at the other side of the bridge, the same thing (1, 2, 3) happens. Parachains that need to +//! connect over the bridge need to coordinate the moment when they start sending messages over +//! the bridge. Otherwise they may lose messages and/or bundled assets; +//! +//! 5) when either side wants to close the bridge, it sends the XCM `Transact` with the +//! `close_bridge` call. The bridge is closed immediately if there are no queued messages. +//! Otherwise, the owner must repeat the `close_bridge` call to prune all queued messages first. +//! +//! The pallet doesn't provide any mechanism for graceful closure, because it always involves +//! some contract between two connected chains and the bridge hub knows nothing about that. It +//! is the task for the connected chains to make sure that all required actions are completed +//! before the closure. In the end, the bridge hub can't even guarantee that all messages that +//! are delivered to the destination, are processed in the way their sender expects. So if we +//! can't guarantee that, we shall not care about more complex procedures and leave it to the +//! participating parties. +//! +//! # Example +//! +//! Example of opening a bridge between some random parachains from Polkadot and Kusama: +//! +//! 0. Let's have: +//! - BridgeHubPolkadot with `UniversalLocation` = `[GlobalConsensus(Polkadot), Parachain(1002)]` +//! - BridgeHubKusama with `UniversalLocation` = `[GlobalConsensus(Kusama), Parachain(1002)]` +//! 1. The Polkadot local sibling parachain `Location::new(1, Parachain(1234))` must send some DOTs +//! to its sovereign account on BridgeHubPolkadot to cover `BridgeDeposit`, fees for `Transact`, +//! and the existential deposit. +//! 2. Send a call to the BridgeHubPolkadot from the local sibling parachain: `Location::new(1, +//! Parachain(1234))` ``` xcm::Transact( origin_kind: OriginKind::Xcm, +//! XcmOverBridgeHubKusama::open_bridge( VersionedInteriorLocation::V4([GlobalConsensus(Kusama), +//! Parachain(4567)].into()), ); ) ``` +//! 3. Check the stored bridge metadata and generated `LaneId`. +//! 4. The Kusama local sibling parachain `Location::new(1, Parachain(4567))` must send some KSMs to +//! its sovereign account +//! on BridgeHubKusama to cover `BridgeDeposit`, fees for `Transact`, and the existential deposit. +//! 5. Send a call to the BridgeHubKusama from the local sibling parachain: `Location::new(1, +//! Parachain(4567))` ``` xcm::Transact( origin_kind: OriginKind::Xcm, +//! XcmOverBridgeHubKusama::open_bridge( +//! VersionedInteriorLocation::V4([GlobalConsensus(Polkadot), Parachain(1234)].into()), ); ) ``` +//! 6. Check the stored bridge metadata and generated `LaneId`. +//! 7. Both `LaneId`s from steps 3 and 6 must be the same (see above _Concept of `lane` and +//! `LaneId`_). +//! 8. Run the bridge messages relayer for `LaneId`. +//! 9. Send messages from both sides. +//! +//! The opening bridge holds the configured `BridgeDeposit` from the origin's sovereign account, but +//! this deposit is returned when the bridge is closed with `fn close_bridge`. #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -use bridge_runtime_common::messages_xcm_extension::XcmBlobHauler; -use pallet_bridge_messages::Config as BridgeMessagesConfig; +use bp_messages::{LaneId, LaneState, MessageNonce}; +use bp_runtime::{AccountIdOf, BalanceOf, RangeInclusiveExt}; +pub use bp_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; +use bp_xcm_bridge_hub::{BridgeLocations, BridgeLocationsError, LocalXcmChannelManager}; +use frame_support::{traits::fungible::MutateHold, DefaultNoBound}; +use frame_system::Config as SystemConfig; +use pallet_bridge_messages::{Config as BridgeMessagesConfig, LanesManagerError}; +use sp_runtime::traits::Zero; +use sp_std::{boxed::Box, vec::Vec}; use xcm::prelude::*; +use xcm_builder::DispatchBlob; +use xcm_executor::traits::ConvertLocation; +pub use bp_xcm_bridge_hub::XcmAsPlainPayload; +pub use dispatcher::XcmBlobMessageDispatchResult; pub use exporter::PalletAsHaulBlobExporter; pub use pallet::*; +mod dispatcher; mod exporter; +pub mod migration; mod mock; /// The target that will be used when publishing logs related to this pallet. @@ -35,15 +172,29 @@ pub const LOG_TARGET: &str = "runtime::bridge-xcm"; #[frame_support::pallet] pub mod pallet { use super::*; - use bridge_runtime_common::messages_xcm_extension::SenderAndLane; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::BlockNumberFor; + use frame_support::{ + pallet_prelude::*, + traits::{tokens::Precision, Contains}, + }; + use frame_system::pallet_prelude::{BlockNumberFor, *}; + + /// The reason for this pallet placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// The funds are held as a deposit for opened bridge. + #[codec(index = 0)] + BridgeDeposit, + } #[pallet::config] #[pallet::disable_frame_system_supertrait_check] pub trait Config: BridgeMessagesConfig { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + /// Runtime's universal location. type UniversalLocation: Get; // TODO: https://github.com/paritytech/parity-bridges-common/issues/1666 remove `ChainId` and @@ -56,63 +207,1513 @@ pub mod pallet { /// `BridgedNetworkId` consensus. type BridgeMessagesPalletInstance: 'static; - /// Price of single message export to the bridged consensus (`Self::BridgedNetworkId`). + /// Price of single message export to the bridged consensus (`Self::BridgedNetwork`). type MessageExportPrice: Get; /// Checks the XCM version for the destination. type DestinationVersion: GetVersion; - /// Get point-to-point links with bridged consensus (`Self::BridgedNetworkId`). - /// (this will be replaced with dynamic on-chain bridges - `Bridges V2`) - type Lanes: Get>; - /// Support for point-to-point links - /// (this will be replaced with dynamic on-chain bridges - `Bridges V2`) - type LanesSupport: XcmBlobHauler; + /// The origin that is allowed to call privileged operations on the pallet, e.g. open/close + /// bridge for location that coresponds to `Self::BridgeOriginAccountIdConverter` and + /// `Self::BridgedNetwork`. + type AdminOrigin: EnsureOrigin<::RuntimeOrigin>; + /// A set of XCM locations within local consensus system that are allowed to open + /// bridges with remote destinations. + type OpenBridgeOrigin: EnsureOrigin< + ::RuntimeOrigin, + Success = Location, + >; + /// A converter between a location and a sovereign account. + type BridgeOriginAccountIdConverter: ConvertLocation>>; + + /// Amount of this chain native tokens that is reserved on the sibling parachain account + /// when bridge open request is registered. + #[pallet::constant] + type BridgeDeposit: Get>>; + /// Currency used to pay for bridge registration. + type Currency: MutateHold< + AccountIdOf>, + Balance = BalanceOf>, + Reason = Self::RuntimeHoldReason, + >; + /// The overarching runtime hold reason. + type RuntimeHoldReason: From>; + /// Do not hold `Self::BridgeDeposit` for the location of `Self::OpenBridgeOrigin`. + /// For example, it is possible to make an exception for a system parachain or relay. + type AllowWithoutBridgeDeposit: Contains; + + /// Local XCM channel manager. + type LocalXcmChannelManager: LocalXcmChannelManager; + /// XCM-level dispatcher for inbound bridge messages. + type BlobDispatcher: DispatchBlob; } + /// An alias for the bridge metadata. + pub type BridgeOf = Bridge>; + /// An alias for this chain. + pub type ThisChainOf = + pallet_bridge_messages::ThisChainOf>::BridgeMessagesPalletInstance>; + /// An alias for the associated lanes manager. + pub type LanesManagerOf = + pallet_bridge_messages::LanesManager>::BridgeMessagesPalletInstance>; + #[pallet::pallet] + #[pallet::storage_version(migration::STORAGE_VERSION)] pub struct Pallet(PhantomData<(T, I)>); #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { fn integrity_test() { assert!( - Self::bridged_network_id().is_some(), + Self::bridged_network_id().is_ok(), "Configured `T::BridgedNetwork`: {:?} does not contain `GlobalConsensus` junction with `NetworkId`", T::BridgedNetwork::get() ) } + + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + Self::do_try_state() + } + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Open a bridge between two locations. + /// + /// The caller must be within the `T::OpenBridgeOrigin` filter (presumably: a sibling + /// parachain or a parent relay chain). The `bridge_destination_universal_location` must be + /// a destination within the consensus of the `T::BridgedNetwork` network. + /// + /// The `BridgeDeposit` amount is reserved on the caller account. This deposit + /// is unreserved after bridge is closed. + /// + /// The states after this call: bridge is `Opened`, outbound lane is `Opened`, inbound lane + /// is `Opened`. + #[pallet::call_index(0)] + #[pallet::weight(Weight::zero())] // TODO:(bridges-v2) - https://github.com/paritytech/parity-bridges-common/issues/3046 - add benchmarks impl + pub fn open_bridge( + origin: OriginFor, + bridge_destination_universal_location: Box, + ) -> DispatchResult { + // check and compute required bridge locations and laneId + let xcm_version = bridge_destination_universal_location.identify_version(); + let locations = + Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?; + let lane_id = locations.calculate_lane_id(xcm_version).map_err(|e| { + log::trace!( + target: LOG_TARGET, + "calculate_lane_id error: {e:?}", + ); + Error::::BridgeLocations(e) + })?; + + Self::do_open_bridge(locations, lane_id, true) + } + + /// Try to close the bridge. + /// + /// Can only be called by the "owner" of this side of the bridge, meaning that the + /// inbound XCM channel with the local origin chain is working. + /// + /// Closed bridge is a bridge without any traces in the runtime storage. So this method + /// first tries to prune all queued messages at the outbound lane. When there are no + /// outbound messages left, outbound and inbound lanes are purged. After that, funds + /// are returned back to the owner of this side of the bridge. + /// + /// The number of messages that we may prune in a single call is limited by the + /// `may_prune_messages` argument. If there are more messages in the queue, the method + /// prunes exactly `may_prune_messages` and exits early. The caller may call it again + /// until outbound queue is depleted and get his funds back. + /// + /// The states after this call: everything is either `Closed`, or purged from the + /// runtime storage. + #[pallet::call_index(1)] + #[pallet::weight(Weight::zero())] // TODO:(bridges-v2) - https://github.com/paritytech/parity-bridges-common/issues/3046 - add benchmarks impl + pub fn close_bridge( + origin: OriginFor, + bridge_destination_universal_location: Box, + may_prune_messages: MessageNonce, + ) -> DispatchResult { + // compute required bridge locations + let locations = + Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?; + + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1760 - may do refund here, if + // bridge/lanes are already closed + for messages that are not pruned + + // update bridge metadata - this also guarantees that the bridge is in the proper state + let bridge = + Bridges::::try_mutate_exists(locations.bridge_id(), |bridge| match bridge { + Some(bridge) => { + bridge.state = BridgeState::Closed; + Ok(bridge.clone()) + }, + None => Err(Error::::UnknownBridge), + })?; + + // close inbound and outbound lanes + let lanes_manager = LanesManagerOf::::new(); + let mut inbound_lane = lanes_manager + .any_state_inbound_lane(bridge.lane_id) + .map_err(Error::::LanesManager)?; + let mut outbound_lane = lanes_manager + .any_state_outbound_lane(bridge.lane_id) + .map_err(Error::::LanesManager)?; + + // now prune queued messages + let mut pruned_messages = 0; + for _ in outbound_lane.queued_messages() { + if pruned_messages == may_prune_messages { + break + } + + outbound_lane.remove_oldest_unpruned_message(); + pruned_messages += 1; + } + + // if there are outbound messages in the queue, just update states and early exit + if !outbound_lane.queued_messages().is_empty() { + // update lanes state. Under normal circumstances, following calls shall never fail + inbound_lane.set_state(LaneState::Closed); + outbound_lane.set_state(LaneState::Closed); + + // write something to log + let enqueued_messages = outbound_lane.queued_messages().saturating_len(); + log::trace!( + target: LOG_TARGET, + "Bridge {:?} between {:?} and {:?} is closing lane_id: {:?}. {} messages remaining", + locations.bridge_id(), + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + bridge.lane_id, + enqueued_messages, + ); + + // deposit the `ClosingBridge` event + Self::deposit_event(Event::::ClosingBridge { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id, + pruned_messages, + enqueued_messages, + }); + + return Ok(()) + } + + // else we have pruned all messages, so lanes and the bridge itself may gone + inbound_lane.purge(); + outbound_lane.purge(); + Bridges::::remove(locations.bridge_id()); + LaneToBridge::::remove(bridge.lane_id); + + // return deposit + let released_deposit = T::Currency::release( + &HoldReason::BridgeDeposit.into(), + &bridge.bridge_owner_account, + bridge.deposit, + Precision::BestEffort, + ) + .map_err(|e| { + // we can't do anything here - looks like funds have been (partially) unreserved + // before by someone else. Let's not fail, though - it'll be worse for the caller + log::error!( + target: LOG_TARGET, + "Failed to unreserve during the bridge {:?} closure with error: {e:?}", + locations.bridge_id(), + ); + e + }) + .ok() + .unwrap_or(BalanceOf::>::zero()); + + // write something to log + log::trace!( + target: LOG_TARGET, + "Bridge {:?} between {:?} and {:?} has closed lane_id: {:?}, the bridge deposit {released_deposit:?} was returned", + locations.bridge_id(), + bridge.lane_id, + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + ); + + // deposit the `BridgePruned` event + Self::deposit_event(Event::::BridgePruned { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id, + bridge_deposit: released_deposit, + pruned_messages, + }); + + Ok(()) + } } impl, I: 'static> Pallet { - /// Returns dedicated/configured lane identifier. - pub(crate) fn lane_for( - source: &InteriorLocation, - dest: (&NetworkId, &InteriorLocation), - ) -> Option { - let source = source.clone().relative_to(&T::UniversalLocation::get()); - - // Check that we have configured a point-to-point lane for 'source' and `dest`. - T::Lanes::get() - .into_iter() - .find_map(|(lane_source, (lane_dest_network, lane_dest))| { - if lane_source.location == source && - &lane_dest_network == dest.0 && - Self::bridged_network_id().as_ref() == Some(dest.0) && - &lane_dest == dest.1 - { - Some(lane_source) - } else { - None - } - }) + pub(crate) fn do_open_bridge( + locations: Box, + lane_id: LaneId, + create_lanes: bool, + ) -> Result<(), DispatchError> { + // reserve balance on the origin's sovereign account (if needed) + let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location( + locations.bridge_origin_relative_location(), + ) + .ok_or(Error::::InvalidBridgeOriginAccount)?; + let deposit = if T::AllowWithoutBridgeDeposit::contains( + locations.bridge_origin_relative_location(), + ) { + BalanceOf::>::zero() + } else { + let deposit = T::BridgeDeposit::get(); + T::Currency::hold( + &HoldReason::BridgeDeposit.into(), + &bridge_owner_account, + deposit, + ) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Failed to hold bridge deposit: {deposit:?} \ + from bridge_owner_account: {bridge_owner_account:?} derived from \ + bridge_origin_relative_location: {:?} with error: {e:?}", + locations.bridge_origin_relative_location(), + ); + Error::::FailedToReserveBridgeDeposit + })?; + deposit + }; + + // save bridge metadata + Bridges::::try_mutate(locations.bridge_id(), |bridge| match bridge { + Some(_) => Err(Error::::BridgeAlreadyExists), + None => { + *bridge = Some(BridgeOf:: { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account, + deposit, + lane_id, + }); + Ok(()) + }, + })?; + // save lane to bridge mapping + LaneToBridge::::try_mutate(lane_id, |bridge| match bridge { + Some(_) => Err(Error::::BridgeAlreadyExists), + None => { + *bridge = Some(*locations.bridge_id()); + Ok(()) + }, + })?; + + if create_lanes { + // create new lanes. Under normal circumstances, following calls shall never fail + let lanes_manager = LanesManagerOf::::new(); + lanes_manager + .create_inbound_lane(lane_id) + .map_err(Error::::LanesManager)?; + lanes_manager + .create_outbound_lane(lane_id) + .map_err(Error::::LanesManager)?; + } + + // write something to log + log::trace!( + target: LOG_TARGET, + "Bridge {:?} between {:?} and {:?} has been opened using lane_id: {lane_id:?}", + locations.bridge_id(), + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + ); + + // deposit `BridgeOpened` event + Self::deposit_event(Event::::BridgeOpened { + bridge_id: *locations.bridge_id(), + bridge_deposit: deposit, + local_endpoint: Box::new(locations.bridge_origin_universal_location().clone()), + remote_endpoint: Box::new( + locations.bridge_destination_universal_location().clone(), + ), + lane_id, + }); + + Ok(()) + } + } + + impl, I: 'static> Pallet { + /// Return bridge endpoint locations and dedicated lane identifier. This method converts + /// runtime `origin` argument to relative `Location` using the `T::OpenBridgeOrigin` + /// converter. + pub fn bridge_locations_from_origin( + origin: OriginFor, + bridge_destination_universal_location: Box, + ) -> Result, sp_runtime::DispatchError> { + Self::bridge_locations( + T::OpenBridgeOrigin::ensure_origin(origin)?, + (*bridge_destination_universal_location) + .try_into() + .map_err(|_| Error::::UnsupportedXcmVersion)?, + ) + } + + /// Return bridge endpoint locations and dedicated **bridge** identifier (`BridgeId`). + pub fn bridge_locations( + bridge_origin_relative_location: Location, + bridge_destination_universal_location: InteriorLocation, + ) -> Result, sp_runtime::DispatchError> { + BridgeLocations::bridge_locations( + T::UniversalLocation::get(), + bridge_origin_relative_location, + bridge_destination_universal_location, + Self::bridged_network_id()?, + ) + .map_err(|e| { + log::trace!( + target: LOG_TARGET, + "bridge_locations error: {e:?}", + ); + Error::::BridgeLocations(e).into() + }) + } + + /// Return bridge metadata by lane_id + pub fn bridge_by_lane_id(lane_id: &LaneId) -> Option<(BridgeId, BridgeOf)> { + LaneToBridge::::get(lane_id) + .and_then(|bridge_id| Self::bridge(bridge_id).map(|bridge| (bridge_id, bridge))) } + } + impl, I: 'static> Pallet { /// Returns some `NetworkId` if contains `GlobalConsensus` junction. - fn bridged_network_id() -> Option { + fn bridged_network_id() -> Result { match T::BridgedNetwork::get().take_first_interior() { - Some(GlobalConsensus(network)) => Some(network), - _ => None, + Some(GlobalConsensus(network)) => Ok(network), + _ => Err(Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeDestination, + ) + .into()), + } + } + } + + #[cfg(any(test, feature = "try-runtime", feature = "std"))] + impl, I: 'static> Pallet { + /// Ensure the correctness of the state of this pallet. + pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { + use sp_std::collections::btree_set::BTreeSet; + + let mut lanes = BTreeSet::new(); + + // check all known bridge configurations + for (bridge_id, bridge) in Bridges::::iter() { + lanes.insert(Self::do_try_state_for_bridge(bridge_id, bridge)?); + } + ensure!( + lanes.len() == Bridges::::iter().count(), + "Invalid `Bridges` configuration, probably two bridges handle the same laneId!" + ); + ensure!( + lanes.len() == LaneToBridge::::iter().count(), + "Invalid `LaneToBridge` configuration, probably missing or not removed laneId!" + ); + + // check connected `pallet_bridge_messages` state. + Self::do_try_state_for_messages() + } + + /// Ensure the correctness of the state of the bridge. + pub fn do_try_state_for_bridge( + bridge_id: BridgeId, + bridge: BridgeOf, + ) -> Result { + log::info!(target: LOG_TARGET, "Checking `do_try_state_for_bridge` for bridge_id: {bridge_id:?} and bridge: {bridge:?}"); + + // check `BridgeId` points to the same `LaneId` and vice versa. + ensure!( + Some(bridge_id) == LaneToBridge::::get(bridge.lane_id), + "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!" + ); + + // check `pallet_bridge_messages` state for that `LaneId`. + let lanes_manager = LanesManagerOf::::new(); + ensure!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).is_ok(), + "Inbound lane not found!", + ); + ensure!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).is_ok(), + "Outbound lane not found!", + ); + + // check that `locations` are convertible to the `latest` XCM. + let bridge_origin_relative_location_as_latest: &Location = + bridge.bridge_origin_relative_location.try_as().map_err(|_| { + "`bridge.bridge_origin_relative_location` cannot be converted to the `latest` XCM, needs migration!" + })?; + let bridge_origin_universal_location_as_latest: &InteriorLocation = bridge.bridge_origin_universal_location + .try_as() + .map_err(|_| "`bridge.bridge_origin_universal_location` cannot be converted to the `latest` XCM, needs migration!")?; + let bridge_destination_universal_location_as_latest: &InteriorLocation = bridge.bridge_destination_universal_location + .try_as() + .map_err(|_| "`bridge.bridge_destination_universal_location` cannot be converted to the `latest` XCM, needs migration!")?; + + // check `BridgeId` does not change + ensure!( + bridge_id == BridgeId::new(bridge_origin_universal_location_as_latest, bridge_destination_universal_location_as_latest), + "`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!" + ); + + // check bridge account owner + ensure!( + T::BridgeOriginAccountIdConverter::convert_location(bridge_origin_relative_location_as_latest) == Some(bridge.bridge_owner_account), + "`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!" + ); + + Ok(bridge.lane_id) + } + + /// Ensure the correctness of the state of the connected `pallet_bridge_messages` instance. + pub fn do_try_state_for_messages() -> Result<(), sp_runtime::TryRuntimeError> { + // check that all `InboundLanes` laneIds have mapping to some bridge. + for lane_id in pallet_bridge_messages::InboundLanes::::iter_keys() { + log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `InboundLanes`'s lane_id: {lane_id:?}..."); + ensure!( + LaneToBridge::::get(lane_id).is_some(), + "Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!" + ); + } + + // check that all `OutboundLanes` laneIds have mapping to some bridge. + for lane_id in pallet_bridge_messages::OutboundLanes::::iter_keys() { + log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `OutboundLanes`'s lane_id: {lane_id:?}..."); + ensure!( + LaneToBridge::::get(lane_id).is_some(), + "Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!" + ); } + + Ok(()) } } + + /// All registered bridges. + #[pallet::storage] + #[pallet::getter(fn bridge)] + pub type Bridges, I: 'static = ()> = + StorageMap<_, Identity, BridgeId, BridgeOf>; + /// All registered `lane_id` and `bridge_id` mappings. + #[pallet::storage] + pub type LaneToBridge, I: 'static = ()> = + StorageMap<_, Identity, LaneId, BridgeId>; + + #[pallet::genesis_config] + #[derive(DefaultNoBound)] + pub struct GenesisConfig, I: 'static = ()> { + /// Opened bridges. + /// + /// Keep in mind that we are **NOT** reserving any amount for the bridges opened at + /// genesis. We are **NOT** opening lanes, used by this bridge. It all must be done using + /// other pallets genesis configuration or some other means. + pub opened_bridges: Vec<(Location, InteriorLocation)>, + /// Dummy marker. + #[serde(skip)] + pub _phantom: sp_std::marker::PhantomData<(T, I)>, + } + + #[pallet::genesis_build] + impl, I: 'static> BuildGenesisConfig for GenesisConfig + where + T: frame_system::Config>>, + { + fn build(&self) { + for (bridge_origin_relative_location, bridge_destination_universal_location) in + &self.opened_bridges + { + let locations = Pallet::::bridge_locations( + bridge_origin_relative_location.clone(), + bridge_destination_universal_location.clone().into(), + ) + .expect("Invalid genesis configuration"); + let lane_id = + locations.calculate_lane_id(xcm::latest::VERSION).expect("Valid locations"); + let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location( + locations.bridge_origin_relative_location(), + ) + .expect("Invalid genesis configuration"); + + Bridges::::insert( + locations.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account, + deposit: Zero::zero(), + lane_id, + }, + ); + LaneToBridge::::insert(lane_id, locations.bridge_id()); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager + .create_inbound_lane(lane_id) + .expect("Invalid genesis configuration"); + lanes_manager + .create_outbound_lane(lane_id) + .expect("Invalid genesis configuration"); + } + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// The bridge between two locations has been opened. + BridgeOpened { + /// Bridge identifier. + bridge_id: BridgeId, + /// Amount of deposit held. + bridge_deposit: BalanceOf>, + + /// Universal location of local bridge endpoint. + local_endpoint: Box, + /// Universal location of remote bridge endpoint. + remote_endpoint: Box, + /// Lane identifier. + lane_id: LaneId, + }, + /// Bridge is going to be closed, but not yet fully pruned from the runtime storage. + ClosingBridge { + /// Bridge identifier. + bridge_id: BridgeId, + /// Lane identifier. + lane_id: LaneId, + /// Number of pruned messages during the close call. + pruned_messages: MessageNonce, + /// Number of enqueued messages that need to be pruned in follow up calls. + enqueued_messages: MessageNonce, + }, + /// Bridge has been closed and pruned from the runtime storage. It now may be reopened + /// again by any participant. + BridgePruned { + /// Bridge identifier. + bridge_id: BridgeId, + /// Lane identifier. + lane_id: LaneId, + /// Amount of deposit released. + bridge_deposit: BalanceOf>, + /// Number of pruned messages during the close call. + pruned_messages: MessageNonce, + }, + } + + #[pallet::error] + pub enum Error { + /// Bridge locations error. + BridgeLocations(BridgeLocationsError), + /// Invalid local bridge origin account. + InvalidBridgeOriginAccount, + /// The bridge is already registered in this pallet. + BridgeAlreadyExists, + /// The local origin already owns a maximal number of bridges. + TooManyBridgesForLocalOrigin, + /// Trying to close already closed bridge. + BridgeAlreadyClosed, + /// Lanes manager error. + LanesManager(LanesManagerError), + /// Trying to access unknown bridge. + UnknownBridge, + /// The bridge origin can't pay the required amount for opening the bridge. + FailedToReserveBridgeDeposit, + /// The version of XCM location argument is unsupported. + UnsupportedXcmVersion, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use mock::*; + + use bp_messages::LaneId; + use frame_support::{assert_err, assert_noop, assert_ok, traits::fungible::Mutate, BoundedVec}; + use frame_system::{EventRecord, Phase}; + use sp_core::H256; + use sp_runtime::TryRuntimeError; + + fn fund_origin_sovereign_account(locations: &BridgeLocations, balance: Balance) -> AccountId { + let bridge_owner_account = + LocationToAccountId::convert_location(locations.bridge_origin_relative_location()) + .unwrap(); + assert_ok!(Balances::mint_into(&bridge_owner_account, balance)); + bridge_owner_account + } + + fn mock_open_bridge_from_with( + origin: RuntimeOrigin, + deposit: Balance, + with: InteriorLocation, + ) -> (BridgeOf, BridgeLocations) { + let locations = + XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + let bridge_owner_account = + fund_origin_sovereign_account(&locations, deposit + ExistentialDeposit::get()); + Balances::hold(&HoldReason::BridgeDeposit.into(), &bridge_owner_account, deposit).unwrap(); + + let bridge = Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account, + deposit, + lane_id, + }; + Bridges::::insert(locations.bridge_id(), bridge.clone()); + LaneToBridge::::insert(bridge.lane_id, locations.bridge_id()); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager.create_inbound_lane(bridge.lane_id).unwrap(); + lanes_manager.create_outbound_lane(bridge.lane_id).unwrap(); + + assert_ok!(XcmOverBridge::do_try_state()); + + (bridge, *locations) + } + + fn mock_open_bridge_from( + origin: RuntimeOrigin, + deposit: Balance, + ) -> (BridgeOf, BridgeLocations) { + mock_open_bridge_from_with(origin, deposit, bridged_asset_hub_universal_location()) + } + + fn enqueue_message(lane: LaneId) { + let lanes_manager = LanesManagerOf::::new(); + lanes_manager + .active_outbound_lane(lane) + .unwrap() + .send_message(BoundedVec::try_from(vec![42]).expect("We craft valid messages")); + } + + #[test] + fn open_bridge_fails_if_origin_is_not_allowed() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::disallowed_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + sp_runtime::DispatchError::BadOrigin, + ); + }) + } + + #[test] + fn open_bridge_fails_if_origin_is_not_relative() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::parent_relay_chain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::sibling_parachain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + }) + } + + #[test] + fn open_bridge_fails_if_destination_is_not_remote() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::parent_relay_chain_origin(), + Box::new( + [GlobalConsensus(RelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)] + .into() + ), + ), + Error::::BridgeLocations(BridgeLocationsError::DestinationIsLocal), + ); + }); + } + + #[test] + fn open_bridge_fails_if_outside_of_bridged_consensus() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::parent_relay_chain_origin(), + Box::new( + [ + GlobalConsensus(NonBridgedRelayNetwork::get()), + Parachain(BRIDGED_ASSET_HUB_ID) + ] + .into() + ), + ), + Error::::BridgeLocations( + BridgeLocationsError::UnreachableDestination + ), + ); + }); + } + + #[test] + fn open_bridge_fails_if_origin_has_no_sovereign_account() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::origin_without_sovereign_account(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::InvalidBridgeOriginAccount, + ); + }); + } + + #[test] + fn open_bridge_fails_if_origin_sovereign_account_has_no_enough_funds() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::sibling_parachain_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::FailedToReserveBridgeDeposit, + ); + }); + } + + #[test] + fn open_bridge_fails_if_it_already_exists() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new(bridged_asset_hub_universal_location().into()), + ) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + fund_origin_sovereign_account( + &locations, + BridgeDeposit::get() + ExistentialDeposit::get(), + ); + + Bridges::::insert( + locations.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: [0u8; 32].into(), + deposit: 0, + lane_id, + }, + ); + + assert_noop!( + XcmOverBridge::open_bridge( + origin, + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::BridgeAlreadyExists, + ); + }) + } + + #[test] + fn open_bridge_fails_if_its_lanes_already_exists() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new(bridged_asset_hub_universal_location().into()), + ) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + fund_origin_sovereign_account( + &locations, + BridgeDeposit::get() + ExistentialDeposit::get(), + ); + + let lanes_manager = LanesManagerOf::::new(); + + lanes_manager.create_inbound_lane(lane_id).unwrap(); + assert_noop!( + XcmOverBridge::open_bridge( + origin.clone(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::LanesManager(LanesManagerError::InboundLaneAlreadyExists), + ); + + lanes_manager.active_inbound_lane(lane_id).unwrap().purge(); + lanes_manager.create_outbound_lane(lane_id).unwrap(); + assert_noop!( + XcmOverBridge::open_bridge( + origin, + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::LanesManager( + LanesManagerError::OutboundLaneAlreadyExists + ), + ); + }) + } + + #[test] + fn open_bridge_works() { + run_test(|| { + // in our test runtime, we expect that bridge may be opened by parent relay chain + // and any sibling parachain + let origins = [ + (OpenBridgeOrigin::parent_relay_chain_origin(), 0), + (OpenBridgeOrigin::sibling_parachain_origin(), BridgeDeposit::get()), + ]; + + // check that every origin may open the bridge + let lanes_manager = LanesManagerOf::::new(); + let existential_deposit = ExistentialDeposit::get(); + for (origin, expected_deposit) in origins { + // reset events + System::set_block_number(1); + System::reset_events(); + + // compute all other locations + let xcm_version = xcm::latest::VERSION; + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new( + VersionedInteriorLocation::from(bridged_asset_hub_universal_location()) + .into_version(xcm_version) + .expect("valid conversion"), + ), + ) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm_version).unwrap(); + + // ensure that there's no bridge and lanes in the storage + assert_eq!(Bridges::::get(locations.bridge_id()), None); + assert_eq!( + lanes_manager.active_inbound_lane(lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.active_outbound_lane(lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + assert_eq!(LaneToBridge::::get(lane_id), None); + + // give enough funds to the sovereign account of the bridge origin + let bridge_owner_account = fund_origin_sovereign_account( + &locations, + expected_deposit + existential_deposit, + ); + assert_eq!( + Balances::free_balance(&bridge_owner_account), + expected_deposit + existential_deposit + ); + assert_eq!(Balances::reserved_balance(&bridge_owner_account), 0); + + // now open the bridge + assert_ok!(XcmOverBridge::open_bridge( + origin, + Box::new(locations.bridge_destination_universal_location().clone().into()), + )); + + // ensure that everything has been set up in the runtime storage + assert_eq!( + Bridges::::get(locations.bridge_id()), + Some(Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into() + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: expected_deposit, + lane_id + }), + ); + assert_eq!( + lanes_manager.active_inbound_lane(lane_id).map(|l| l.state()), + Ok(LaneState::Opened) + ); + assert_eq!( + lanes_manager.active_outbound_lane(lane_id).map(|l| l.state()), + Ok(LaneState::Opened) + ); + assert_eq!( + LaneToBridge::::get(lane_id), + Some(*locations.bridge_id()) + ); + assert_eq!(Balances::free_balance(&bridge_owner_account), existential_deposit); + assert_eq!(Balances::reserved_balance(&bridge_owner_account), expected_deposit); + + // ensure that the proper event is deposited + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::BridgeOpened { + bridge_id: *locations.bridge_id(), + bridge_deposit: expected_deposit, + local_endpoint: Box::new( + locations.bridge_origin_universal_location().clone() + ), + remote_endpoint: Box::new( + locations.bridge_destination_universal_location().clone() + ), + lane_id + }), + topics: vec![], + }), + ); + + // check state + assert_ok!(XcmOverBridge::do_try_state()); + } + }); + } + + #[test] + fn close_bridge_fails_if_origin_is_not_allowed() { + run_test(|| { + assert_noop!( + XcmOverBridge::close_bridge( + OpenBridgeOrigin::disallowed_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + 0, + ), + sp_runtime::DispatchError::BadOrigin, + ); + }) + } + + #[test] + fn close_bridge_fails_if_origin_is_not_relative() { + run_test(|| { + assert_noop!( + XcmOverBridge::close_bridge( + OpenBridgeOrigin::parent_relay_chain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + 0, + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + + assert_noop!( + XcmOverBridge::close_bridge( + OpenBridgeOrigin::sibling_parachain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + 0, + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + }) + } + + #[test] + fn close_bridge_fails_if_its_lanes_are_unknown() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let (bridge, locations) = mock_open_bridge_from(origin.clone(), 0); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().purge(); + assert_noop!( + XcmOverBridge::close_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + 0, + ), + Error::::LanesManager(LanesManagerError::UnknownInboundLane), + ); + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge(); + + let (_, locations) = mock_open_bridge_from(origin.clone(), 0); + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge(); + assert_noop!( + XcmOverBridge::close_bridge( + origin, + Box::new(locations.bridge_destination_universal_location().clone().into()), + 0, + ), + Error::::LanesManager(LanesManagerError::UnknownOutboundLane), + ); + }); + } + + #[test] + fn close_bridge_works() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let expected_deposit = BridgeDeposit::get(); + let (bridge, locations) = mock_open_bridge_from(origin.clone(), expected_deposit); + System::set_block_number(1); + + // remember owner balances + let free_balance = Balances::free_balance(&bridge.bridge_owner_account); + let reserved_balance = Balances::reserved_balance(&bridge.bridge_owner_account); + + // enqueue some messages + for _ in 0..32 { + enqueue_message(bridge.lane_id); + } + + // now call the `close_bridge`, which will only partially prune messages + assert_ok!(XcmOverBridge::close_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + 16, + ),); + + // as a result, the bridge and lanes are switched to the `Closed` state, some messages + // are pruned, but funds are not unreserved + let lanes_manager = LanesManagerOf::::new(); + assert_eq!( + Bridges::::get(locations.bridge_id()).map(|b| b.state), + Some(BridgeState::Closed) + ); + assert_eq!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager + .any_state_outbound_lane(bridge.lane_id) + .unwrap() + .queued_messages() + .checked_len(), + Some(16) + ); + assert_eq!( + LaneToBridge::::get(bridge.lane_id), + Some(*locations.bridge_id()) + ); + assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance); + assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance); + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id, + pruned_messages: 16, + enqueued_messages: 16, + }), + topics: vec![], + }), + ); + + // now call the `close_bridge` again, which will only partially prune messages + assert_ok!(XcmOverBridge::close_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + 8, + ),); + + // nothing is changed (apart from the pruned messages) + assert_eq!( + Bridges::::get(locations.bridge_id()).map(|b| b.state), + Some(BridgeState::Closed) + ); + assert_eq!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager + .any_state_outbound_lane(bridge.lane_id) + .unwrap() + .queued_messages() + .checked_len(), + Some(8) + ); + assert_eq!( + LaneToBridge::::get(bridge.lane_id), + Some(*locations.bridge_id()) + ); + assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance); + assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance); + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id, + pruned_messages: 8, + enqueued_messages: 8, + }), + topics: vec![], + }), + ); + + // now call the `close_bridge` again that will prune all remaining messages and the + // bridge + assert_ok!(XcmOverBridge::close_bridge( + origin, + Box::new(locations.bridge_destination_universal_location().clone().into()), + 9, + ),); + + // there's no traces of bridge in the runtime storage and funds are unreserved + assert_eq!( + Bridges::::get(locations.bridge_id()).map(|b| b.state), + None + ); + assert_eq!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + assert_eq!(LaneToBridge::::get(bridge.lane_id), None); + assert_eq!( + Balances::free_balance(&bridge.bridge_owner_account), + free_balance + reserved_balance + ); + assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), 0); + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::BridgePruned { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id, + bridge_deposit: expected_deposit, + pruned_messages: 8, + }), + topics: vec![], + }), + ); + }); + } + + #[test] + fn do_try_state_works() { + use sp_runtime::Either; + + let bridge_origin_relative_location = SiblingLocation::get(); + let bridge_origin_universal_location = SiblingUniversalLocation::get(); + let bridge_destination_universal_location = BridgedUniversalDestination::get(); + let bridge_owner_account = + LocationToAccountId::convert_location(&bridge_origin_relative_location) + .expect("valid accountId"); + let bridge_owner_account_mismatch = + LocationToAccountId::convert_location(&Location::parent()).expect("valid accountId"); + let bridge_id = BridgeId::new( + &bridge_origin_universal_location, + &bridge_destination_universal_location, + ); + let bridge_id_mismatch = BridgeId::new(&InteriorLocation::Here, &InteriorLocation::Here); + let lane_id = LaneId::from_inner(Either::Left(H256::default())); + let lane_id_mismatch = LaneId::from_inner(Either::Left(H256::from([1u8; 32]))); + + let test_bridge_state = |id, + bridge, + (lane_id, bridge_id), + (inbound_lane_id, outbound_lane_id), + expected_error: Option| { + Bridges::::insert(id, bridge); + LaneToBridge::::insert(lane_id, bridge_id); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager.create_inbound_lane(inbound_lane_id).unwrap(); + lanes_manager.create_outbound_lane(outbound_lane_id).unwrap(); + + let result = XcmOverBridge::do_try_state(); + if let Some(e) = expected_error { + assert_err!(result, e); + } else { + assert_ok!(result); + } + }; + let cleanup = |bridge_id, lane_ids| { + Bridges::::remove(bridge_id); + for lane_id in lane_ids { + LaneToBridge::::remove(lane_id); + let lanes_manager = LanesManagerOf::::new(); + if let Ok(lane) = lanes_manager.any_state_inbound_lane(lane_id) { + lane.purge(); + } + if let Ok(lane) = lanes_manager.any_state_outbound_lane(lane_id) { + lane.purge(); + } + } + assert_ok!(XcmOverBridge::do_try_state()); + }; + + run_test(|| { + // ok state + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id), + (lane_id, lane_id), + None, + ); + cleanup(bridge_id, vec![lane_id]); + + // error - missing `LaneToBridge` mapping + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id_mismatch), + (lane_id, lane_id), + Some(TryRuntimeError::Other( + "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!", + )), + ); + cleanup(bridge_id, vec![lane_id]); + + // error bridge owner account cannot be calculated + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + )), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account_mismatch.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id), + (lane_id, lane_id), + Some(TryRuntimeError::Other("`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!")), + ); + cleanup(bridge_id, vec![lane_id]); + + // error when (bridge_origin_universal_location + bridge_destination_universal_location) + // produces different `BridgeId` + test_bridge_state( + bridge_id_mismatch, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + )), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account_mismatch.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id_mismatch), + (lane_id, lane_id), + Some(TryRuntimeError::Other("`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!")), + ); + cleanup(bridge_id_mismatch, vec![lane_id]); + + // missing inbound lane for a bridge + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id), + (lane_id_mismatch, lane_id), + Some(TryRuntimeError::Other("Inbound lane not found!")), + ); + cleanup(bridge_id, vec![lane_id, lane_id_mismatch]); + + // missing outbound lane for a bridge + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id), + (lane_id, lane_id_mismatch), + Some(TryRuntimeError::Other("Outbound lane not found!")), + ); + cleanup(bridge_id, vec![lane_id, lane_id_mismatch]); + + // missing bridge for inbound lane + let lanes_manager = LanesManagerOf::::new(); + assert!(lanes_manager.create_inbound_lane(lane_id).is_ok()); + assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!")); + cleanup(bridge_id, vec![lane_id]); + + // missing bridge for outbound lane + let lanes_manager = LanesManagerOf::::new(); + assert!(lanes_manager.create_outbound_lane(lane_id).is_ok()); + assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!")); + cleanup(bridge_id, vec![lane_id]); + }); + } + + #[test] + fn ensure_encoding_compatibility() { + use codec::Encode; + + let bridge_destination_universal_location = BridgedUniversalDestination::get(); + let may_prune_messages = 13; + + assert_eq!( + bp_xcm_bridge_hub::XcmBridgeHubCall::open_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ) + } + .encode(), + Call::::open_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ) + } + .encode() + ); + assert_eq!( + bp_xcm_bridge_hub::XcmBridgeHubCall::close_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ), + may_prune_messages, + } + .encode(), + Call::::close_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ), + may_prune_messages, + } + .encode() + ); + } } diff --git a/bridges/modules/xcm-bridge-hub/src/migration.rs b/bridges/modules/xcm-bridge-hub/src/migration.rs new file mode 100644 index 000000000000..c9d8b67176a5 --- /dev/null +++ b/bridges/modules/xcm-bridge-hub/src/migration.rs @@ -0,0 +1,146 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! A module that is responsible for migration of storage. + +use crate::{Config, Pallet, LOG_TARGET}; +use bp_messages::LaneId; +use frame_support::{ + traits::{Get, OnRuntimeUpgrade, StorageVersion}, + weights::Weight, +}; +use xcm::prelude::{InteriorLocation, Location}; + +/// The in-code storage version. +pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + +/// This migration does not modify storage but can be used to open a bridge and link it to the +/// specified LaneId. This is useful when we want to open a bridge and use a custom LaneId instead +/// of the pre-calculated one provided by the `fn open_bridge extrinsic`. +/// Or perhaps if you want to ensure that your runtime (e.g., for testing) always has an open +/// bridge. +pub struct OpenBridgeForLane< + T, + I, + Lane, + CreateLane, + SourceRelativeLocation, + BridgedUniversalLocation, +>( + core::marker::PhantomData<( + T, + I, + Lane, + CreateLane, + SourceRelativeLocation, + BridgedUniversalLocation, + )>, +); +impl< + T: Config, + I: 'static, + Lane: Get, + CreateLane: Get, + SourceRelativeLocation: Get, + BridgedUniversalLocation: Get, + > OnRuntimeUpgrade + for OpenBridgeForLane +{ + fn on_runtime_upgrade() -> Weight { + let bridge_origin_relative_location = SourceRelativeLocation::get(); + let bridge_destination_universal_location = BridgedUniversalLocation::get(); + let lane_id = Lane::get(); + let create_lane = CreateLane::get(); + log::info!( + target: LOG_TARGET, + "OpenBridgeForLane - going to open bridge with lane_id: {lane_id:?} (create_lane: {create_lane:?}) \ + between bridge_origin_relative_location: {bridge_origin_relative_location:?} and \ + bridge_destination_universal_location: {bridge_destination_universal_location:?}", + ); + + let locations = match Pallet::::bridge_locations( + bridge_origin_relative_location.clone(), + bridge_destination_universal_location.clone(), + ) { + Ok(locations) => locations, + Err(e) => { + log::error!( + target: LOG_TARGET, + "OpenBridgeForLane - on_runtime_upgrade failed to construct bridge_locations with error: {e:?}" + ); + return T::DbWeight::get().reads(0) + }, + }; + + // check if already exists + if let Some((bridge_id, bridge)) = Pallet::::bridge_by_lane_id(&lane_id) { + log::info!( + target: LOG_TARGET, + "OpenBridgeForLane - bridge: {bridge:?} with bridge_id: {bridge_id:?} already exist for lane_id: {lane_id:?}!" + ); + if &bridge_id != locations.bridge_id() { + log::warn!( + target: LOG_TARGET, + "OpenBridgeForLane - check you parameters, because a different bridge: {bridge:?} \ + with bridge_id: {bridge_id:?} exist for lane_id: {lane_id:?} for requested \ + bridge_origin_relative_location: {bridge_origin_relative_location:?} and \ + bridge_destination_universal_location: {bridge_destination_universal_location:?} !", + ); + } + + return T::DbWeight::get().reads(2) + } + + if let Err(e) = Pallet::::do_open_bridge(locations, lane_id, create_lane) { + log::error!(target: LOG_TARGET, "OpenBridgeForLane - do_open_bridge failed with error: {e:?}"); + T::DbWeight::get().reads(6) + } else { + log::info!(target: LOG_TARGET, "OpenBridgeForLane - do_open_bridge passed!"); + T::DbWeight::get().reads_writes(6, 4) + } + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: sp_std::vec::Vec) -> Result<(), sp_runtime::DispatchError> { + let bridge_origin_relative_location = SourceRelativeLocation::get(); + let bridge_destination_universal_location = BridgedUniversalLocation::get(); + let lane_id = Lane::get(); + + // check that requested bridge is stored + let Ok(locations) = Pallet::::bridge_locations( + bridge_origin_relative_location.clone(), + bridge_destination_universal_location.clone(), + ) else { + return Err(sp_runtime::DispatchError::Other("Invalid locations!")) + }; + let Some((bridge_id, _)) = Pallet::::bridge_by_lane_id(&lane_id) else { + return Err(sp_runtime::DispatchError::Other("Missing bridge!")) + }; + frame_support::ensure!( + locations.bridge_id() == &bridge_id, + "Bridge is not stored correctly!" + ); + + log::info!( + target: LOG_TARGET, + "OpenBridgeForLane - post_upgrade found opened bridge with lane_id: {lane_id:?} \ + between bridge_origin_relative_location: {bridge_origin_relative_location:?} and \ + bridge_destination_universal_location: {bridge_destination_universal_location:?}", + ); + + Ok(()) + } +} diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs index 0cca32ba9e5f..d32e0989dfd8 100644 --- a/bridges/modules/xcm-bridge-hub/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub/src/mock.rs @@ -23,13 +23,14 @@ use bp_messages::{ ChainWithMessages, LaneId, MessageNonce, }; use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, HashOf}; -use bridge_runtime_common::messages_xcm_extension::{SenderAndLane, XcmBlobHauler}; +use bp_xcm_bridge_hub::{BridgeId, LocalXcmChannelManager}; use codec::Encode; use frame_support::{ assert_ok, derive_impl, parameter_types, - traits::{Everything, NeverEnsureOrigin}, + traits::{EnsureOrigin, Equals, Everything, OriginTrait}, weights::RuntimeDbWeight, }; +use polkadot_parachain_primitives::primitives::Sibling; use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, @@ -39,27 +40,26 @@ use sp_runtime::{ use sp_std::cell::RefCell; use xcm::prelude::*; use xcm_builder::{ - AllowUnpaidExecutionFrom, FixedWeightBounds, InspectMessageQueues, NetworkExportTable, - NetworkExportTableItem, + AllowUnpaidExecutionFrom, DispatchBlob, DispatchBlobError, FixedWeightBounds, + InspectMessageQueues, NetworkExportTable, NetworkExportTableItem, ParentIsPreset, + SiblingParachainConvertsVia, }; use xcm_executor::XcmExecutor; pub type AccountId = AccountId32; pub type Balance = u64; - type Block = frame_system::mocking::MockBlock; pub const SIBLING_ASSET_HUB_ID: u32 = 2001; pub const THIS_BRIDGE_HUB_ID: u32 = 2002; pub const BRIDGED_ASSET_HUB_ID: u32 = 1001; -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]); frame_support::construct_runtime! { pub enum TestRuntime { System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Event}, Messages: pallet_bridge_messages::{Pallet, Call, Event}, - XcmOverBridge: pallet_xcm_bridge_hub::{Pallet}, + XcmOverBridge: pallet_xcm_bridge_hub::{Pallet, Call, HoldReason, Event}, XcmOverBridgeRouter: pallet_xcm_bridge_hub_router, } } @@ -82,25 +82,22 @@ impl pallet_balances::Config for TestRuntime { type AccountStore = System; } -parameter_types! { - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; -} - impl pallet_bridge_messages::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type WeightInfo = TestMessagesWeights; - type ActiveOutboundLanes = ActiveOutboundLanes; + type ThisChain = ThisUnderlyingChain; + type BridgedChain = BridgedUnderlyingChain; + type BridgedHeaderChain = BridgedHeaderChain; + type OutboundPayload = Vec; type InboundPayload = Vec; + type DeliveryPayments = (); type DeliveryConfirmationPayments = (); type OnMessagesDelivered = (); - type MessageDispatch = TestMessageDispatch; - type ThisChain = ThisUnderlyingChain; - type BridgedChain = BridgedUnderlyingChain; - type BridgedHeaderChain = BridgedHeaderChain; + type MessageDispatch = TestMessageDispatch; } pub struct TestMessagesWeights; @@ -127,8 +124,8 @@ impl pallet_bridge_messages::WeightInfo for TestMessagesWeights { fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { Weight::zero() } - fn receive_single_n_bytes_message_proof_with_dispatch(_: u32) -> Weight { - Weight::zero() + fn receive_single_n_bytes_message_proof_with_dispatch(_n: u32) -> Weight { + Weight::from_parts(1, 0) } } @@ -153,6 +150,7 @@ parameter_types! { Parachain(THIS_BRIDGE_HUB_ID), ].into(); pub SiblingLocation: Location = Location::new(1, [Parachain(SIBLING_ASSET_HUB_ID)]); + pub SiblingUniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)].into(); pub const BridgedRelayNetwork: NetworkId = NetworkId::Polkadot; pub BridgedRelayNetworkLocation: Location = (Parent, GlobalConsensus(BridgedRelayNetwork::get())).into(); @@ -161,7 +159,6 @@ parameter_types! { pub const NonBridgedRelayNetwork: NetworkId = NetworkId::Rococo; pub const BridgeDeposit: Balance = 100_000; - pub const Penalty: Balance = 1_000; // configuration for pallet_xcm_bridge_hub_router pub BridgeHubLocation: Location = Here.into(); @@ -178,7 +175,14 @@ parameter_types! { pub UnitWeightCost: Weight = Weight::from_parts(10, 10); } +/// **Universal** `InteriorLocation` of bridged asset hub. +pub fn bridged_asset_hub_universal_location() -> InteriorLocation { + BridgedUniversalDestination::get() +} + impl pallet_xcm_bridge_hub::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; type BridgedNetwork = BridgedRelayNetworkLocation; type BridgeMessagesPalletInstance = (); @@ -186,25 +190,35 @@ impl pallet_xcm_bridge_hub::Config for TestRuntime { type MessageExportPrice = (); type DestinationVersion = AlwaysLatest; - type Lanes = TestLanes; - type LanesSupport = TestXcmBlobHauler; + type AdminOrigin = frame_system::EnsureNever<()>; + type OpenBridgeOrigin = OpenBridgeOrigin; + type BridgeOriginAccountIdConverter = LocationToAccountId; + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type AllowWithoutBridgeDeposit = Equals; + + type LocalXcmChannelManager = TestLocalXcmChannelManager; + + type BlobDispatcher = TestBlobDispatcher; } impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime { + type RuntimeEvent = RuntimeEvent; type WeightInfo = (); type UniversalLocation = UniversalLocation; + type SiblingBridgeHubLocation = BridgeHubLocation; type BridgedNetworkId = BridgedRelayNetwork; type Bridges = NetworkExportTable; type DestinationVersion = AlwaysLatest; - type BridgeHubOrigin = NeverEnsureOrigin; type ToBridgeHubSender = TestExportXcmWithXcmOverBridge; + type LocalXcmChannelManager = TestLocalXcmChannelManager; type ByteFee = ConstU128<0>; type FeeAsset = BridgeFeeAsset; - - type WithBridgeHubChannel = (); } pub struct XcmConfig; @@ -291,29 +305,149 @@ impl TestExportXcmWithXcmOverBridge { } } +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the parent `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, +); + parameter_types! { - pub TestSenderAndLane: SenderAndLane = SenderAndLane { - location: SiblingLocation::get(), - lane: TEST_LANE_ID, - }; - pub TestLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = sp_std::vec![ - (TestSenderAndLane::get(), (BridgedRelayNetwork::get(), BridgedRelativeDestination::get())) - ]; + pub ParentRelayChainLocation: Location = Location { parents: 1, interior: Here }; +} +pub struct OpenBridgeOrigin; + +impl OpenBridgeOrigin { + pub fn parent_relay_chain_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([0u8; 32].into()) + } + + pub fn parent_relay_chain_universal_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([1u8; 32].into()) + } + + pub fn sibling_parachain_origin() -> RuntimeOrigin { + let mut account = [0u8; 32]; + account[..4].copy_from_slice(&SIBLING_ASSET_HUB_ID.encode()[..4]); + RuntimeOrigin::signed(account.into()) + } + + pub fn sibling_parachain_universal_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([2u8; 32].into()) + } + + pub fn origin_without_sovereign_account() -> RuntimeOrigin { + RuntimeOrigin::signed([3u8; 32].into()) + } + + pub fn disallowed_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([42u8; 32].into()) + } } -pub struct TestXcmBlobHauler; -impl XcmBlobHauler for TestXcmBlobHauler { - type Runtime = TestRuntime; - type MessagesInstance = (); - type ToSourceChainSender = (); - type CongestedMessage = (); - type UncongestedMessage = (); +impl EnsureOrigin for OpenBridgeOrigin { + type Success = Location; + + fn try_origin(o: RuntimeOrigin) -> Result { + let signer = o.clone().into_signer(); + if signer == Self::parent_relay_chain_origin().into_signer() { + return Ok(ParentRelayChainLocation::get()) + } else if signer == Self::parent_relay_chain_universal_origin().into_signer() { + return Ok(Location { + parents: 2, + interior: GlobalConsensus(RelayNetwork::get()).into(), + }) + } else if signer == Self::sibling_parachain_universal_origin().into_signer() { + return Ok(Location { + parents: 2, + interior: [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)] + .into(), + }) + } else if signer == Self::origin_without_sovereign_account().into_signer() { + return Ok(Location { + parents: 1, + interior: [Parachain(SIBLING_ASSET_HUB_ID), OnlyChild].into(), + }) + } + + let mut sibling_account = [0u8; 32]; + sibling_account[..4].copy_from_slice(&SIBLING_ASSET_HUB_ID.encode()[..4]); + if signer == Some(sibling_account.into()) { + return Ok(Location { parents: 1, interior: Parachain(SIBLING_ASSET_HUB_ID).into() }) + } + + Err(o) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(Self::parent_relay_chain_origin()) + } +} + +pub struct TestLocalXcmChannelManager; + +impl TestLocalXcmChannelManager { + pub fn make_congested() { + frame_support::storage::unhashed::put(b"TestLocalXcmChannelManager.Congested", &true); + } + + pub fn is_bridge_suspened() -> bool { + frame_support::storage::unhashed::get_or_default(b"TestLocalXcmChannelManager.Suspended") + } + + pub fn is_bridge_resumed() -> bool { + frame_support::storage::unhashed::get_or_default(b"TestLocalXcmChannelManager.Resumed") + } +} + +impl LocalXcmChannelManager for TestLocalXcmChannelManager { + type Error = (); + + fn is_congested(_with: &Location) -> bool { + frame_support::storage::unhashed::get_or_default(b"TestLocalXcmChannelManager.Congested") + } + + fn suspend_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> { + frame_support::storage::unhashed::put(b"TestLocalXcmChannelManager.Suspended", &true); + Ok(()) + } + + fn resume_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> { + frame_support::storage::unhashed::put(b"TestLocalXcmChannelManager.Resumed", &true); + Ok(()) + } +} + +impl pallet_xcm_bridge_hub_router::XcmChannelStatusProvider for TestLocalXcmChannelManager { + fn is_congested(with: &Location) -> bool { + ::is_congested(with) + } +} + +pub struct TestBlobDispatcher; + +impl TestBlobDispatcher { + pub fn is_dispatched() -> bool { + frame_support::storage::unhashed::get_or_default(b"TestBlobDispatcher.Dispatched") + } +} + +impl DispatchBlob for TestBlobDispatcher { + fn dispatch_blob(_blob: Vec) -> Result<(), DispatchBlobError> { + frame_support::storage::unhashed::put(b"TestBlobDispatcher.Dispatched", &true); + Ok(()) + } } pub struct ThisUnderlyingChain; impl Chain for ThisUnderlyingChain { const ID: ChainId = *b"tuch"; + type BlockNumber = u64; type Hash = H256; type Hasher = BlakeTwo256; @@ -335,16 +469,15 @@ impl Chain for ThisUnderlyingChain { } impl ChainWithMessages for ThisUnderlyingChain { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; - + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "WithThisChainBridgeMessages"; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128; } -pub struct BridgedUnderlyingChain; pub type BridgedHeaderHash = H256; pub type BridgedChainHeader = SubstrateHeader; +pub struct BridgedUnderlyingChain; impl Chain for BridgedUnderlyingChain { const ID: ChainId = *b"bgdc"; type BlockNumber = u64; @@ -368,9 +501,18 @@ impl Chain for BridgedUnderlyingChain { } impl ChainWithMessages for BridgedUnderlyingChain { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "WithBridgedChainBridgeMessages"; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128; +} + +pub struct BridgedHeaderChain; +impl bp_header_chain::HeaderChain for BridgedHeaderChain { + fn finalized_header_state_root( + _hash: HashOf, + ) -> Option> { + unreachable!() + } } /// Test message dispatcher. @@ -386,8 +528,9 @@ impl MessageDispatch for TestMessageDispatch { type DispatchPayload = Vec; type DispatchLevelResult = (); - fn is_active() -> bool { - frame_support::storage::unhashed::take::(&(b"inactive").encode()[..]) != Some(false) + fn is_active(lane: LaneId) -> bool { + frame_support::storage::unhashed::take::(&(b"inactive", lane).encode()[..]) != + Some(false) } fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { @@ -401,15 +544,6 @@ impl MessageDispatch for TestMessageDispatch { } } -pub struct BridgedHeaderChain; -impl bp_header_chain::HeaderChain for BridgedHeaderChain { - fn finalized_header_state_root( - _hash: HashOf, - ) -> Option> { - unreachable!() - } -} - /// Run pallet test. pub fn run_test(test: impl FnOnce() -> T) -> T { sp_io::TestExternalities::new( diff --git a/bridges/primitives/header-chain/src/call_info.rs b/bridges/primitives/header-chain/src/call_info.rs new file mode 100644 index 000000000000..acf7447adabc --- /dev/null +++ b/bridges/primitives/header-chain/src/call_info.rs @@ -0,0 +1,94 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Defines structures related to calls of the `pallet-bridge-grandpa` pallet. + +use crate::{justification, InitializationData}; + +use bp_runtime::HeaderOf; +use codec::{Decode, Encode}; +use frame_support::{weights::Weight, RuntimeDebugNoBound}; +use scale_info::TypeInfo; +use sp_consensus_grandpa::SetId; +use sp_runtime::traits::{Header as HeaderT, Zero}; +use sp_std::{boxed::Box, fmt::Debug}; + +/// A minimized version of `pallet-bridge-grandpa::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeGrandpaCall { + /// `pallet-bridge-grandpa::Call::submit_finality_proof` + #[codec(index = 0)] + submit_finality_proof { + /// The header that we are going to finalize. + finality_target: Box
, + /// Finality justification for the `finality_target`. + justification: justification::GrandpaJustification
, + }, + /// `pallet-bridge-grandpa::Call::initialize` + #[codec(index = 1)] + initialize { + /// All data, required to initialize the pallet. + init_data: InitializationData
, + }, + /// `pallet-bridge-grandpa::Call::submit_finality_proof_ex` + #[codec(index = 4)] + submit_finality_proof_ex { + /// The header that we are going to finalize. + finality_target: Box
, + /// Finality justification for the `finality_target`. + justification: justification::GrandpaJustification
, + /// An identifier of the validators set, that have signed the justification. + current_set_id: SetId, + }, +} + +/// The `BridgeGrandpaCall` for a pallet that bridges with given `C`; +pub type BridgeGrandpaCallOf = BridgeGrandpaCall>; + +/// A digest information on the `BridgeGrandpaCall::submit_finality_proof` call. +#[derive(Copy, Clone, PartialEq, RuntimeDebugNoBound)] +pub struct SubmitFinalityProofInfo { + /// Number of the finality target. + pub block_number: N, + /// An identifier of the validators set that has signed the submitted justification. + /// It might be `None` if deprecated version of the `submit_finality_proof` is used. + pub current_set_id: Option, + /// If `true`, then the call proves new **mandatory** header. + pub is_mandatory: bool, + /// If `true`, then the call must be free (assuming that everything else is valid) to + /// be treated as valid. + pub is_free_execution_expected: bool, + /// Extra weight that we assume is included in the call. + /// + /// We have some assumptions about headers and justifications of the bridged chain. + /// We know that if our assumptions are correct, then the call must not have the + /// weight above some limit. The fee paid for weight above that limit, is never refunded. + pub extra_weight: Weight, + /// Extra size (in bytes) that we assume are included in the call. + /// + /// We have some assumptions about headers and justifications of the bridged chain. + /// We know that if our assumptions are correct, then the call must not have the + /// weight above some limit. The fee paid for bytes above that limit, is never refunded. + pub extra_size: u32, +} + +impl SubmitFinalityProofInfo { + /// Returns `true` if call size/weight is below our estimations for regular calls. + pub fn fits_limits(&self) -> bool { + self.extra_weight.is_zero() && self.extra_size.is_zero() + } +} diff --git a/bridges/primitives/header-chain/src/lib.rs b/bridges/primitives/header-chain/src/lib.rs index 26295dee1801..48326bf5c19d 100644 --- a/bridges/primitives/header-chain/src/lib.rs +++ b/bridges/primitives/header-chain/src/lib.rs @@ -38,6 +38,10 @@ use sp_consensus_grandpa::{ use sp_runtime::{traits::Header as HeaderT, Digest, RuntimeDebug, SaturatedConversion}; use sp_std::{boxed::Box, vec::Vec}; +pub use call_info::{BridgeGrandpaCall, BridgeGrandpaCallOf, SubmitFinalityProofInfo}; + +mod call_info; + pub mod justification; pub mod storage_keys; @@ -228,39 +232,6 @@ pub trait FindEquivocations Result, Self::Error>; } -/// A minimized version of `pallet-bridge-grandpa::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeGrandpaCall { - /// `pallet-bridge-grandpa::Call::submit_finality_proof` - #[codec(index = 0)] - submit_finality_proof { - /// The header that we are going to finalize. - finality_target: Box
, - /// Finality justification for the `finality_target`. - justification: justification::GrandpaJustification
, - }, - /// `pallet-bridge-grandpa::Call::initialize` - #[codec(index = 1)] - initialize { - /// All data, required to initialize the pallet. - init_data: InitializationData
, - }, - /// `pallet-bridge-grandpa::Call::submit_finality_proof_ex` - #[codec(index = 4)] - submit_finality_proof_ex { - /// The header that we are going to finalize. - finality_target: Box
, - /// Finality justification for the `finality_target`. - justification: justification::GrandpaJustification
, - /// An identifier of the validators set, that have signed the justification. - current_set_id: SetId, - }, -} - -/// The `BridgeGrandpaCall` used by a chain. -pub type BridgeGrandpaCallOf = BridgeGrandpaCall>; - /// Substrate-based chain that is using direct GRANDPA finality. /// /// Keep in mind that parachains are relying on relay chain GRANDPA, so they should not implement diff --git a/bridges/primitives/messages/Cargo.toml b/bridges/primitives/messages/Cargo.toml index 4a9037342bce..87c8cbe88180 100644 --- a/bridges/primitives/messages/Cargo.toml +++ b/bridges/primitives/messages/Cargo.toml @@ -16,19 +16,19 @@ scale-info = { features = ["bit-vec", "derive"], workspace = true } serde = { features = ["alloc", "derive"], workspace = true } # Bridge dependencies - bp-runtime = { workspace = true } bp-header-chain = { workspace = true } # Substrate Dependencies - frame-support = { workspace = true } sp-core = { workspace = true } sp-std = { workspace = true } +sp-io = { workspace = true } [dev-dependencies] hex = { workspace = true, default-features = true } hex-literal = { workspace = true, default-features = true } +bp-runtime = { workspace = true } [features] default = ["std"] @@ -40,5 +40,6 @@ std = [ "scale-info/std", "serde/std", "sp-core/std", + "sp-io/std", "sp-std/std", ] diff --git a/bridges/primitives/messages/src/call_info.rs b/bridges/primitives/messages/src/call_info.rs new file mode 100644 index 000000000000..c8f06ed8cb7c --- /dev/null +++ b/bridges/primitives/messages/src/call_info.rs @@ -0,0 +1,172 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Defines structures related to calls of the `pallet-bridge-messages` pallet. + +use crate::{source_chain, target_chain, LaneId, MessageNonce, UnrewardedRelayersState}; + +use bp_runtime::{AccountIdOf, HashOf}; +use codec::{Decode, Encode}; +use frame_support::weights::Weight; +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; +use sp_std::ops::RangeInclusive; + +/// The `BridgeMessagesCall` used to bridge with a given chain. +pub type BridgeMessagesCallOf = BridgeMessagesCall< + AccountIdOf, + target_chain::FromBridgedChainMessagesProof>, + source_chain::FromBridgedChainMessagesDeliveryProof>, +>; + +/// A minimized version of `pallet-bridge-messages::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeMessagesCall { + /// `pallet-bridge-messages::Call::receive_messages_proof` + #[codec(index = 2)] + receive_messages_proof { + /// Account id of relayer at the **bridged** chain. + relayer_id_at_bridged_chain: AccountId, + /// Messages proof. + proof: MessagesProof, + /// A number of messages in the proof. + messages_count: u32, + /// Total dispatch weight of messages in the proof. + dispatch_weight: Weight, + }, + /// `pallet-bridge-messages::Call::receive_messages_delivery_proof` + #[codec(index = 3)] + receive_messages_delivery_proof { + /// Messages delivery proof. + proof: MessagesDeliveryProof, + /// "Digest" of unrewarded relayers state at the bridged chain. + relayers_state: UnrewardedRelayersState, + }, +} + +/// Generic info about a messages delivery/confirmation proof. +#[derive(PartialEq, RuntimeDebug)] +pub struct BaseMessagesProofInfo { + /// Message lane, used by the call. + pub lane_id: LaneId, + /// Nonces of messages, included in the call. + /// + /// For delivery transaction, it is nonces of bundled messages. For confirmation + /// transaction, it is nonces that are to be confirmed during the call. + pub bundled_range: RangeInclusive, + /// Nonce of the best message, stored by this chain before the call is dispatched. + /// + /// For delivery transaction, it is the nonce of best delivered message before the call. + /// For confirmation transaction, it is the nonce of best confirmed message before the call. + pub best_stored_nonce: MessageNonce, +} + +impl BaseMessagesProofInfo { + /// Returns true if `bundled_range` continues the `0..=best_stored_nonce` range. + pub fn appends_to_stored_nonce(&self) -> bool { + Some(*self.bundled_range.start()) == self.best_stored_nonce.checked_add(1) + } +} + +/// Occupation state of the unrewarded relayers vector. +#[derive(PartialEq, RuntimeDebug)] +#[cfg_attr(test, derive(Default))] +pub struct UnrewardedRelayerOccupation { + /// The number of remaining unoccupied entries for new relayers. + pub free_relayer_slots: MessageNonce, + /// The number of messages that we are ready to accept. + pub free_message_slots: MessageNonce, +} + +/// Info about a `ReceiveMessagesProof` call which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub struct ReceiveMessagesProofInfo { + /// Base messages proof info + pub base: BaseMessagesProofInfo, + /// State of unrewarded relayers vector. + pub unrewarded_relayers: UnrewardedRelayerOccupation, +} + +impl ReceiveMessagesProofInfo { + /// Returns true if: + /// + /// - either inbound lane is ready to accept bundled messages; + /// + /// - or there are no bundled messages, but the inbound lane is blocked by too many unconfirmed + /// messages and/or unrewarded relayers. + pub fn is_obsolete(&self, is_dispatcher_active: bool) -> bool { + // if dispatcher is inactive, we don't accept any delivery transactions + if !is_dispatcher_active { + return true + } + + // transactions with zero bundled nonces are not allowed, unless they're message + // delivery transactions, which brings reward confirmations required to unblock + // the lane + if self.base.bundled_range.is_empty() { + let empty_transactions_allowed = + // we allow empty transactions when we can't accept delivery from new relayers + self.unrewarded_relayers.free_relayer_slots == 0 || + // or if we can't accept new messages at all + self.unrewarded_relayers.free_message_slots == 0; + + return !empty_transactions_allowed + } + + // otherwise we require bundled messages to continue stored range + !self.base.appends_to_stored_nonce() + } +} + +/// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub struct ReceiveMessagesDeliveryProofInfo(pub BaseMessagesProofInfo); + +impl ReceiveMessagesDeliveryProofInfo { + /// Returns true if outbound lane is ready to accept confirmations of bundled messages. + pub fn is_obsolete(&self) -> bool { + self.0.bundled_range.is_empty() || !self.0.appends_to_stored_nonce() + } +} + +/// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call +/// which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub enum MessagesCallInfo { + /// Messages delivery call info. + ReceiveMessagesProof(ReceiveMessagesProofInfo), + /// Messages delivery confirmation call info. + ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo), +} + +impl MessagesCallInfo { + /// Returns lane, used by the call. + pub fn lane_id(&self) -> LaneId { + match *self { + Self::ReceiveMessagesProof(ref info) => info.base.lane_id, + Self::ReceiveMessagesDeliveryProof(ref info) => info.0.lane_id, + } + } + + /// Returns range of messages, bundled with the call. + pub fn bundled_messages(&self) -> RangeInclusive { + match *self { + Self::ReceiveMessagesProof(ref info) => info.base.bundled_range.clone(), + Self::ReceiveMessagesDeliveryProof(ref info) => info.0.bundled_range.clone(), + } + } +} diff --git a/bridges/primitives/messages/src/lane.rs b/bridges/primitives/messages/src/lane.rs new file mode 100644 index 000000000000..6d4ca402eb34 --- /dev/null +++ b/bridges/primitives/messages/src/lane.rs @@ -0,0 +1,281 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Primitives of messages module, that represents lane id. + +use codec::{Decode, Encode, Error as CodecError, Input, MaxEncodedLen}; +use frame_support::sp_runtime::Either; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_core::{RuntimeDebug, TypeId, H256}; +use sp_io::hashing::blake2_256; + +/// Bridge lane identifier. +/// +/// Lane connects two endpoints at both sides of the bridge. We assume that every endpoint +/// has its own unique identifier. We want lane identifiers to be **the same on the both sides +/// of the bridge** (and naturally unique across global consensus if endpoints have unique +/// identifiers). So lane id is the hash (`blake2_256`) of **ordered** encoded locations +/// concatenation (separated by some binary data). I.e.: +/// +/// ```nocompile +/// let endpoint1 = X2(GlobalConsensus(NetworkId::Rococo), Parachain(42)); +/// let endpoint2 = X2(GlobalConsensus(NetworkId::Wococo), Parachain(777)); +/// +/// let final_lane_key = if endpoint1 < endpoint2 { +/// (endpoint1, VALUES_SEPARATOR, endpoint2) +/// } else { +/// (endpoint2, VALUES_SEPARATOR, endpoint1) +/// }.using_encoded(blake2_256); +/// ``` +/// +/// Note: For backwards compatibility reasons, we also handle the older format `[u8; 4]`. +#[derive( + Clone, + Copy, + Decode, + Encode, + Eq, + Ord, + PartialOrd, + PartialEq, + TypeInfo, + MaxEncodedLen, + Serialize, + Deserialize, +)] +pub struct LaneId(InnerLaneId); + +impl LaneId { + /// Create lane identifier from two locations. + pub fn new(endpoint1: T, endpoint2: T) -> Self { + const VALUES_SEPARATOR: [u8; 31] = *b"bridges-lane-id-value-separator"; + + LaneId(InnerLaneId::Hash( + if endpoint1 < endpoint2 { + (endpoint1, VALUES_SEPARATOR, endpoint2) + } else { + (endpoint2, VALUES_SEPARATOR, endpoint1) + } + .using_encoded(blake2_256) + .into(), + )) + } + + /// Create lane identifier from given hash. + /// + /// There's no `From` implementation for the `LaneId`, because using this conversion + /// in a wrong way (i.e. computing hash of endpoints manually) may lead to issues. So we + /// want the call to be explicit. + pub const fn from_inner(inner: Either) -> Self { + LaneId(match inner { + Either::Left(hash) => InnerLaneId::Hash(hash), + Either::Right(array) => InnerLaneId::Array(array), + }) + } +} + +impl core::fmt::Display for LaneId { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Display::fmt(&self.0, f) + } +} + +impl core::fmt::Debug for LaneId { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.0, f) + } +} + +impl AsRef<[u8]> for LaneId { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl TypeId for LaneId { + const TYPE_ID: [u8; 4] = *b"blan"; +} + +#[derive( + Clone, Copy, Eq, Ord, PartialOrd, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize, +)] +enum InnerLaneId { + /// Old format (for backwards compatibility). + Array([u8; 4]), + /// New format 32-byte hash generated by `blake2_256`. + Hash(H256), +} + +impl Encode for InnerLaneId { + fn encode(&self) -> sp_std::vec::Vec { + match self { + InnerLaneId::Array(array) => array.encode(), + InnerLaneId::Hash(hash) => hash.encode(), + } + } +} + +impl Decode for InnerLaneId { + fn decode(input: &mut I) -> Result { + // check backwards compatibly first + if input.remaining_len() == Ok(Some(4)) { + let array: [u8; 4] = Decode::decode(input)?; + return Ok(InnerLaneId::Array(array)) + } + + // else check new format + H256::decode(input).map(InnerLaneId::Hash) + } +} + +impl core::fmt::Display for InnerLaneId { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + InnerLaneId::Array(array) => write!(f, "Array({:?})", array), + InnerLaneId::Hash(hash) => write!(f, "Hash({:?})", hash), + } + } +} + +impl core::fmt::Debug for InnerLaneId { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + InnerLaneId::Array(array) => array.fmt(fmt), + InnerLaneId::Hash(hash) => hash.fmt(fmt), + } + } +} + +impl AsRef<[u8]> for InnerLaneId { + fn as_ref(&self) -> &[u8] { + match self { + InnerLaneId::Array(array) => array.as_ref(), + InnerLaneId::Hash(hash) => hash.as_ref(), + } + } +} + +/// Lane state. +#[derive(Clone, Copy, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum LaneState { + /// Lane is opened and messages may be sent/received over it. + Opened, + /// Lane is closed and all attempts to send/receive messages to/from this lane + /// will fail. + /// + /// Keep in mind that the lane has two ends and the state of the same lane at + /// its ends may be different. Those who are controlling/serving the lane + /// and/or sending messages over the lane, have to coordinate their actions on + /// both ends to make sure that lane is operating smoothly on both ends. + Closed, +} + +impl LaneState { + /// Returns true if lane state allows sending/receiving messages. + pub fn is_active(&self) -> bool { + matches!(*self, LaneState::Opened) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn lane_id_debug_format_matches_inner_hash_format() { + assert_eq!( + format!("{:?}", LaneId(InnerLaneId::Hash(H256::from([1u8; 32])))), + format!("{:?}", H256::from([1u8; 32])), + ); + assert_eq!( + format!("{:?}", LaneId(InnerLaneId::Array([0, 0, 0, 1]))), + format!("{:?}", [0, 0, 0, 1]), + ); + } + + #[test] + fn lane_id_as_ref_works() { + assert_eq!( + "0101010101010101010101010101010101010101010101010101010101010101", + hex::encode(LaneId(InnerLaneId::Hash(H256::from([1u8; 32]))).as_ref()), + ); + assert_eq!("00000001", hex::encode(LaneId(InnerLaneId::Array([0, 0, 0, 1])).as_ref()),); + } + + #[test] + fn lane_id_encode_decode_works() { + let test_encode_decode = |expected_hex, lane_id: LaneId| { + let enc = lane_id.encode(); + let decoded_lane_id = LaneId::decode(&mut &enc[..]).expect("decodable"); + assert_eq!(lane_id, decoded_lane_id); + + assert_eq!(expected_hex, hex::encode(lane_id.as_ref()),); + assert_eq!(expected_hex, hex::encode(decoded_lane_id.as_ref()),); + + let hex_bytes = hex::decode(expected_hex).expect("valid hex"); + let hex_decoded_lane_id = LaneId::decode(&mut &hex_bytes[..]).expect("decodable"); + assert_eq!(hex_decoded_lane_id, lane_id); + assert_eq!(hex_decoded_lane_id, decoded_lane_id); + }; + + test_encode_decode( + "0101010101010101010101010101010101010101010101010101010101010101", + LaneId(InnerLaneId::Hash(H256::from([1u8; 32]))), + ); + test_encode_decode("00000001", LaneId(InnerLaneId::Array([0, 0, 0, 1]))); + } + + #[test] + fn lane_id_is_generated_using_ordered_endpoints() { + assert_eq!(LaneId::new(1, 2), LaneId::new(2, 1)); + } + + #[test] + fn lane_id_is_different_for_different_endpoints() { + assert_ne!(LaneId::new(1, 2), LaneId::new(1, 3)); + } + + #[test] + fn lane_id_is_different_even_if_arguments_has_partial_matching_encoding() { + /// Some artificial type that generates the same encoding for different values + /// concatenations. I.e. the encoding for `(Either::Two(1, 2), Either::Two(3, 4))` + /// is the same as encoding of `(Either::Three(1, 2, 3), Either::One(4))`. + /// In practice, this type is not useful, because you can't do a proper decoding. + /// But still there may be some collisions even in proper types. + #[derive(Eq, Ord, PartialEq, PartialOrd)] + enum Either { + Three(u64, u64, u64), + Two(u64, u64), + One(u64), + } + + impl codec::Encode for Either { + fn encode(&self) -> Vec { + match *self { + Self::One(a) => a.encode(), + Self::Two(a, b) => (a, b).encode(), + Self::Three(a, b, c) => (a, b, c).encode(), + } + } + } + + assert_ne!( + LaneId::new(Either::Two(1, 2), Either::Two(3, 4)), + LaneId::new(Either::Three(1, 2, 3), Either::One(4)), + ); + } +} diff --git a/bridges/primitives/messages/src/lib.rs b/bridges/primitives/messages/src/lib.rs index 9984f8ac3222..7eb0c5629395 100644 --- a/bridges/primitives/messages/src/lib.rs +++ b/bridges/primitives/messages/src/lib.rs @@ -31,9 +31,17 @@ pub use frame_support::weights::Weight; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use source_chain::RelayersRewards; -use sp_core::{RuntimeDebug, TypeId}; +use sp_core::RuntimeDebug; use sp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*}; +pub use call_info::{ + BaseMessagesProofInfo, BridgeMessagesCall, BridgeMessagesCallOf, MessagesCallInfo, + ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, UnrewardedRelayerOccupation, +}; +pub use lane::{LaneId, LaneState}; + +mod call_info; +mod lane; pub mod source_chain; pub mod storage_keys; pub mod target_chain; @@ -165,46 +173,9 @@ impl OperatingMode for MessagesOperatingMode { } } -/// Lane id which implements `TypeId`. -#[derive( - Clone, - Copy, - Decode, - Default, - Encode, - Eq, - Ord, - PartialOrd, - PartialEq, - TypeInfo, - MaxEncodedLen, - Serialize, - Deserialize, -)] -pub struct LaneId(pub [u8; 4]); - -impl core::fmt::Debug for LaneId { - fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { - self.0.fmt(fmt) - } -} - -impl AsRef<[u8]> for LaneId { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl TypeId for LaneId { - const TYPE_ID: [u8; 4] = *b"blan"; -} - /// Message nonce. Valid messages will never have 0 nonce. pub type MessageNonce = u64; -/// Message id as a tuple. -pub type BridgeMessageId = (LaneId, MessageNonce); - /// Opaque message payload. We only decode this payload when it is dispatched. pub type MessagePayload = Vec; @@ -229,6 +200,11 @@ pub struct Message { /// Inbound lane data. #[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct InboundLaneData { + /// Inbound lane state. + /// + /// If state is `Closed`, then all attempts to deliver messages to this end will fail. + pub state: LaneState, + /// Identifiers of relayers and messages that they have delivered to this lane (ordered by /// message nonce). /// @@ -261,11 +237,20 @@ pub struct InboundLaneData { impl Default for InboundLaneData { fn default() -> Self { - InboundLaneData { relayers: VecDeque::new(), last_confirmed_nonce: 0 } + InboundLaneData { + state: LaneState::Closed, + relayers: VecDeque::new(), + last_confirmed_nonce: 0, + } } } impl InboundLaneData { + /// Returns default inbound lane data with opened state. + pub fn opened() -> Self { + InboundLaneData { state: LaneState::Opened, ..Default::default() } + } + /// Returns approximate size of the struct, given a number of entries in the `relayers` set and /// size of each entry. /// @@ -351,7 +336,7 @@ pub struct UnrewardedRelayer { } /// Received messages with their dispatch result. -#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct ReceivedMessages { /// Id of the lane which is receiving messages. pub lane: LaneId, @@ -464,6 +449,10 @@ impl From<&InboundLaneData> for UnrewardedRelayersState { /// Outbound lane data. #[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] pub struct OutboundLaneData { + /// Lane state. + /// + /// If state is `Closed`, then all attempts to send messages messages at this end will fail. + pub state: LaneState, /// Nonce of the oldest message that we haven't yet pruned. May point to not-yet-generated /// message if all sent messages are already pruned. pub oldest_unpruned_nonce: MessageNonce, @@ -473,9 +462,17 @@ pub struct OutboundLaneData { pub latest_generated_nonce: MessageNonce, } +impl OutboundLaneData { + /// Returns default outbound lane data with opened state. + pub fn opened() -> Self { + OutboundLaneData { state: LaneState::Opened, ..Default::default() } + } +} + impl Default for OutboundLaneData { fn default() -> Self { OutboundLaneData { + state: LaneState::Closed, // it is 1 because we're pruning everything in [oldest_unpruned_nonce; // latest_received_nonce] oldest_unpruned_nonce: 1, @@ -514,32 +511,6 @@ where relayers_rewards } -/// A minimized version of `pallet-bridge-messages::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeMessagesCall { - /// `pallet-bridge-messages::Call::receive_messages_proof` - #[codec(index = 2)] - receive_messages_proof { - /// Account id of relayer at the **bridged** chain. - relayer_id_at_bridged_chain: AccountId, - /// Messages proof. - proof: MessagesProof, - /// A number of messages in the proof. - messages_count: u32, - /// Total dispatch weight of messages in the proof. - dispatch_weight: Weight, - }, - /// `pallet-bridge-messages::Call::receive_messages_delivery_proof` - #[codec(index = 3)] - receive_messages_delivery_proof { - /// Messages delivery proof. - proof: MessagesDeliveryProof, - /// "Digest" of unrewarded relayers state at the bridged chain. - relayers_state: UnrewardedRelayersState, - }, -} - /// Error that happens during message verification. #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] pub enum VerificationError { @@ -569,9 +540,16 @@ pub enum VerificationError { mod tests { use super::*; + #[test] + fn lane_is_closed_by_default() { + assert_eq!(InboundLaneData::<()>::default().state, LaneState::Closed); + assert_eq!(OutboundLaneData::default().state, LaneState::Closed); + } + #[test] fn total_unrewarded_messages_does_not_overflow() { let lane_data = InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: 1, messages: DeliveredMessages::new(0) }, UnrewardedRelayer { @@ -599,6 +577,7 @@ mod tests { for (relayer_entries, messages_count) in test_cases { let expected_size = InboundLaneData::::encoded_size_hint(relayer_entries as _); let actual_size = InboundLaneData { + state: LaneState::Opened, relayers: (1u8..=relayer_entries) .map(|i| UnrewardedRelayer { relayer: i, @@ -626,9 +605,4 @@ mod tests { assert!(delivered_messages.contains_message(150)); assert!(!delivered_messages.contains_message(151)); } - - #[test] - fn lane_id_debug_format_matches_inner_array_format() { - assert_eq!(format!("{:?}", LaneId([0, 0, 0, 0])), format!("{:?}", [0, 0, 0, 0]),); - } } diff --git a/bridges/primitives/messages/src/storage_keys.rs b/bridges/primitives/messages/src/storage_keys.rs index 8eedf8fcc7ac..ff62dab078e7 100644 --- a/bridges/primitives/messages/src/storage_keys.rs +++ b/bridges/primitives/messages/src/storage_keys.rs @@ -72,6 +72,7 @@ pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { #[cfg(test)] mod tests { use super::*; + use frame_support::sp_runtime::Either; use hex_literal::hex; #[test] @@ -91,7 +92,17 @@ mod tests { fn storage_message_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted messages proofs. - let storage_key = message_key("BridgeMessages", &LaneId(*b"test"), 42).0; + let storage_key = message_key("BridgeMessages", &LaneId::new(1, 2), 42).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea70e9bdb8f50c68d12f06eabb57759ee5eb1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea02a00000000000000").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + + // check backwards compatibility + let storage_key = + message_key("BridgeMessages", &LaneId::from_inner(Either::Right(*b"test")), 42).0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(), @@ -104,7 +115,18 @@ mod tests { fn outbound_lane_data_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted outbound lane state proofs. - let storage_key = outbound_lane_data_key("BridgeMessages", &LaneId(*b"test")).0; + let storage_key = outbound_lane_data_key("BridgeMessages", &LaneId::new(1, 2)).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1fd3bef8b00df8ca7b01813b5e2741950db1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea0").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + + // check backwards compatibility + let storage_key = + outbound_lane_data_key("BridgeMessages", &LaneId::from_inner(Either::Right(*b"test"))) + .0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(), @@ -117,7 +139,17 @@ mod tests { fn inbound_lane_data_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted inbound lane state proofs. - let storage_key = inbound_lane_data_key("BridgeMessages", &LaneId(*b"test")).0; + let storage_key = inbound_lane_data_key("BridgeMessages", &LaneId::new(1, 2)).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fabd3bef8b00df8ca7b01813b5e2741950db1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea0").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + + // check backwards compatibility + let storage_key = + inbound_lane_data_key("BridgeMessages", &LaneId::from_inner(Either::Right(*b"test"))).0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(), diff --git a/bridges/primitives/messages/src/target_chain.rs b/bridges/primitives/messages/src/target_chain.rs index 74fecb9d9f0d..67868ff7c7cd 100644 --- a/bridges/primitives/messages/src/target_chain.rs +++ b/bridges/primitives/messages/src/target_chain.rs @@ -23,7 +23,7 @@ use codec::{Decode, Encode, Error as CodecError}; use frame_support::weights::Weight; use scale_info::TypeInfo; use sp_core::RuntimeDebug; -use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, prelude::*}; +use sp_std::{fmt::Debug, marker::PhantomData, prelude::*}; /// Messages proof from bridged chain. /// @@ -59,7 +59,7 @@ impl Size for FromBridgedChainMessagesProof = BTreeMap>; +pub type ProvedMessages = (LaneId, ProvedLaneMessages); /// Proved messages from single lane of the source chain. #[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] @@ -103,7 +103,7 @@ pub trait MessageDispatch { /// /// We check it in the messages delivery transaction prologue. So if it becomes `false` /// after some portion of messages is already dispatched, it doesn't fail the whole transaction. - fn is_active() -> bool; + fn is_active(lane: LaneId) -> bool; /// Estimate dispatch weight. /// @@ -179,7 +179,7 @@ impl MessageDispatch for ForbidInboundMessages bool { + fn is_active(_: LaneId) -> bool { false } diff --git a/bridges/primitives/parachains/src/call_info.rs b/bridges/primitives/parachains/src/call_info.rs new file mode 100644 index 000000000000..fd7cd45a72c8 --- /dev/null +++ b/bridges/primitives/parachains/src/call_info.rs @@ -0,0 +1,59 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Defines structures related to calls of the `pallet-bridge-parachains` pallet. + +use crate::{ParaHash, ParaId, RelayBlockHash, RelayBlockNumber}; + +use bp_polkadot_core::parachains::ParaHeadsProof; +use bp_runtime::HeaderId; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_runtime::RuntimeDebug; +use sp_std::vec::Vec; + +/// A minimized version of `pallet-bridge-parachains::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeParachainCall { + /// `pallet-bridge-parachains::Call::submit_parachain_heads` + #[codec(index = 0)] + submit_parachain_heads { + /// Relay chain block, for which we have submitted the `parachain_heads_proof`. + at_relay_block: (RelayBlockNumber, RelayBlockHash), + /// Parachain identifiers and their head hashes. + parachains: Vec<(ParaId, ParaHash)>, + /// Parachain heads proof. + parachain_heads_proof: ParaHeadsProof, + }, +} + +/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. +/// +/// The pallet supports updating multiple parachain heads at once, +#[derive(PartialEq, RuntimeDebug)] +pub struct SubmitParachainHeadsInfo { + /// Number and hash of the finalized relay block that has been used to prove parachain + /// finality. + pub at_relay_block: HeaderId, + /// Parachain identifier. + pub para_id: ParaId, + /// Hash of the bundled parachain head. + pub para_head_hash: ParaHash, + /// If `true`, then the call must be free (assuming that everything else is valid) to + /// be treated as valid. + pub is_free_execution_expected: bool, +} diff --git a/bridges/primitives/parachains/src/lib.rs b/bridges/primitives/parachains/src/lib.rs index 142c6e9b0892..ec3bf9ca3a0a 100644 --- a/bridges/primitives/parachains/src/lib.rs +++ b/bridges/primitives/parachains/src/lib.rs @@ -20,11 +20,9 @@ #![cfg_attr(not(feature = "std"), no_std)] pub use bp_header_chain::StoredHeaderData; +pub use call_info::{BridgeParachainCall, SubmitParachainHeadsInfo}; -use bp_polkadot_core::{ - parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}, - BlockNumber as RelayBlockNumber, Hash as RelayBlockHash, -}; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaId}; use bp_runtime::{ BlockNumberOf, Chain, HashOf, HeaderOf, Parachain, StorageDoubleMapKeyProvider, StorageMapKeyProvider, @@ -36,6 +34,15 @@ use sp_core::storage::StorageKey; use sp_runtime::{traits::Header as HeaderT, RuntimeDebug}; use sp_std::{marker::PhantomData, prelude::*}; +/// Block hash of the bridged relay chain. +pub type RelayBlockHash = bp_polkadot_core::Hash; +/// Block number of the bridged relay chain. +pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; +/// Hasher of the bridged relay chain. +pub type RelayBlockHasher = bp_polkadot_core::Hasher; + +mod call_info; + /// Best known parachain head hash. #[derive(Clone, Decode, Encode, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] pub struct BestParaHeadHash { @@ -185,19 +192,3 @@ impl ParaStoredHeaderDataBuilder for C { None } } - -/// A minimized version of `pallet-bridge-parachains::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeParachainCall { - /// `pallet-bridge-parachains::Call::submit_parachain_heads` - #[codec(index = 0)] - submit_parachain_heads { - /// Relay chain block, for which we have submitted the `parachain_heads_proof`. - at_relay_block: (RelayBlockNumber, RelayBlockHash), - /// Parachain identifiers and their head hashes. - parachains: Vec<(ParaId, ParaHash)>, - /// Parachain heads proof. - parachain_heads_proof: ParaHeadsProof, - }, -} diff --git a/bridges/primitives/polkadot-core/Cargo.toml b/bridges/primitives/polkadot-core/Cargo.toml index acae2f431bf2..366ee7aa948e 100644 --- a/bridges/primitives/polkadot-core/Cargo.toml +++ b/bridges/primitives/polkadot-core/Cargo.toml @@ -14,7 +14,9 @@ workspace = true codec = { features = ["derive"], workspace = true } parity-util-mem = { optional = true, workspace = true } scale-info = { features = ["derive"], workspace = true } -serde = { optional = true, features = ["derive"], workspace = true, default-features = true } +serde = { optional = true, features = [ + "derive", +], workspace = true, default-features = true } # Bridge Dependencies diff --git a/bridges/primitives/relayers/Cargo.toml b/bridges/primitives/relayers/Cargo.toml index 3448e8a40963..34be38bed4ac 100644 --- a/bridges/primitives/relayers/Cargo.toml +++ b/bridges/primitives/relayers/Cargo.toml @@ -15,13 +15,15 @@ codec = { features = ["bit-vec", "derive"], workspace = true } scale-info = { features = ["bit-vec", "derive"], workspace = true } # Bridge Dependencies - +bp-header-chain = { workspace = true } bp-messages = { workspace = true } +bp-parachains = { workspace = true } bp-runtime = { workspace = true } # Substrate Dependencies - +frame-system = { workspace = true } frame-support = { workspace = true } +pallet-utility = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } @@ -32,10 +34,14 @@ hex-literal = { workspace = true, default-features = true } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-messages/std", + "bp-parachains/std", "bp-runtime/std", "codec/std", "frame-support/std", + "frame-system/std", + "pallet-utility/std", "scale-info/std", "sp-runtime/std", "sp-std/std", diff --git a/bridges/primitives/relayers/src/extension.rs b/bridges/primitives/relayers/src/extension.rs new file mode 100644 index 000000000000..5ab8e6cde96b --- /dev/null +++ b/bridges/primitives/relayers/src/extension.rs @@ -0,0 +1,191 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! All runtime calls, supported by `pallet-bridge-relayers` when it acts as a signed +//! extension. + +use bp_header_chain::SubmitFinalityProofInfo; +use bp_messages::MessagesCallInfo; +use bp_parachains::SubmitParachainHeadsInfo; +use bp_runtime::StaticStrProvider; +use frame_support::{ + dispatch::CallableCallFor, traits::IsSubType, weights::Weight, RuntimeDebugNoBound, +}; +use frame_system::Config as SystemConfig; +use pallet_utility::{Call as UtilityCall, Pallet as UtilityPallet}; +use sp_runtime::{ + traits::Get, + transaction_validity::{TransactionPriority, TransactionValidityError}, + RuntimeDebug, +}; +use sp_std::{fmt::Debug, marker::PhantomData, vec, vec::Vec}; + +/// Type of the call that the signed extension recognizes. +#[derive(PartialEq, RuntimeDebugNoBound)] +pub enum ExtensionCallInfo { + /// Relay chain finality + parachain finality + message delivery/confirmation calls. + AllFinalityAndMsgs( + SubmitFinalityProofInfo, + SubmitParachainHeadsInfo, + MessagesCallInfo, + ), + /// Relay chain finality + message delivery/confirmation calls. + RelayFinalityAndMsgs(SubmitFinalityProofInfo, MessagesCallInfo), + /// Parachain finality + message delivery/confirmation calls. + /// + /// This variant is used only when bridging with parachain. + ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo), + /// Standalone message delivery/confirmation call. + Msgs(MessagesCallInfo), +} + +impl + ExtensionCallInfo +{ + /// Returns true if call is a message delivery call (with optional finality calls). + pub fn is_receive_messages_proof_call(&self) -> bool { + match self.messages_call_info() { + MessagesCallInfo::ReceiveMessagesProof(_) => true, + MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => false, + } + } + + /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. + pub fn submit_finality_proof_info( + &self, + ) -> Option> { + match *self { + Self::AllFinalityAndMsgs(info, _, _) => Some(info), + Self::RelayFinalityAndMsgs(info, _) => Some(info), + _ => None, + } + } + + /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. + pub fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { + match self { + Self::AllFinalityAndMsgs(_, info, _) => Some(info), + Self::ParachainFinalityAndMsgs(info, _) => Some(info), + _ => None, + } + } + + /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. + pub fn messages_call_info(&self) -> &MessagesCallInfo { + match self { + Self::AllFinalityAndMsgs(_, _, info) => info, + Self::RelayFinalityAndMsgs(_, info) => info, + Self::ParachainFinalityAndMsgs(_, info) => info, + Self::Msgs(info) => info, + } + } +} + +/// Extra post-dispatch data, associated with the supported runtime call. +#[derive(Default, RuntimeDebug)] +pub struct ExtensionCallData { + /// Extra weight, consumed by the call. We have some assumptions about normal weight + /// that may be consumed by expected calls. If the actual weight is larger than that, + /// we do not refund relayer for this extra weight. + pub extra_weight: Weight, + /// Extra size, consumed by the call. We have some assumptions about normal size + /// of the encoded call. If the actual size is larger than that, we do not refund relayer + /// for this extra size. + pub extra_size: u32, +} + +/// Signed extension configuration. +/// +/// The single `pallet-bridge-relayers` instance may be shared by multiple messages +/// pallet instances, bridging with different remote networks. We expect every instance +/// of the messages pallet to add a separate signed extension to runtime. So it must +/// have a separate configuration. +pub trait ExtensionConfig { + /// Unique identifier of the signed extension that will use this configuration. + type IdProvider: StaticStrProvider; + /// Runtime that optionally supports batched calls. We assume that batched call + /// succeeds if and only if all of its nested calls succeed. + type Runtime: frame_system::Config; + /// Messages pallet instance. + type BridgeMessagesPalletInstance: 'static; + /// Additional priority that is added to base message delivery transaction priority + /// for every additional bundled message. + type PriorityBoostPerMessage: Get; + /// Type of reward, that the `pallet-bridge-relayers` is using. + type Reward; + /// Block number for the remote **GRANDPA chain**. Mind that this chain is not + /// necessarily the chain that we are bridging with. If we are bridging with + /// parachain, it must be its parent relay chain. If we are bridging with the + /// GRANDPA chain, it must be it. + type RemoteGrandpaChainBlockNumber: Clone + Copy + Debug; + + /// Given runtime call, check if it is supported by the signed extension. Additionally, + /// check if call (or any of batched calls) are obsolete. + fn parse_and_check_for_obsolete_call( + call: &::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + >; + + /// Check if runtime call is already obsolete. + fn check_obsolete_parsed_call( + call: &::RuntimeCall, + ) -> Result<&::RuntimeCall, TransactionValidityError>; + + /// Given runtime call info, check that this call has been successful and has updated + /// runtime storage accordingly. + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &::AccountId, + ) -> bool; +} + +/// Something that can unpack batch calls (all-or-nothing flavor) of given size. +pub trait BatchCallUnpacker { + /// Unpack batch call with no more than `max_packed_calls` calls. + fn unpack(call: &Runtime::RuntimeCall, max_packed_calls: u32) -> Vec<&Runtime::RuntimeCall>; +} + +/// An `BatchCallUnpacker` adapter for runtimes with utility pallet. +pub struct RuntimeWithUtilityPallet(PhantomData); + +impl BatchCallUnpacker for RuntimeWithUtilityPallet +where + Runtime: pallet_utility::Config::RuntimeCall>, + ::RuntimeCall: + IsSubType, Runtime>>, +{ + fn unpack( + call: &::RuntimeCall, + max_packed_calls: u32, + ) -> Vec<&::RuntimeCall> { + match call.is_sub_type() { + Some(UtilityCall::::batch_all { ref calls }) + if calls.len() <= max_packed_calls as usize => + calls.iter().collect(), + Some(_) => vec![], + None => vec![call], + } + } +} + +impl BatchCallUnpacker for () { + fn unpack(call: &Runtime::RuntimeCall, _max_packed_calls: u32) -> Vec<&Runtime::RuntimeCall> { + vec![call] + } +} diff --git a/bridges/primitives/relayers/src/lib.rs b/bridges/primitives/relayers/src/lib.rs index 436f33db4008..1e63c89ecd70 100644 --- a/bridges/primitives/relayers/src/lib.rs +++ b/bridges/primitives/relayers/src/lib.rs @@ -19,6 +19,10 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +pub use extension::{ + BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig, + RuntimeWithUtilityPallet, +}; pub use registration::{ExplicitOrAccountParams, Registration, StakeAndSlash}; use bp_messages::LaneId; @@ -32,6 +36,7 @@ use sp_runtime::{ }; use sp_std::{fmt::Debug, marker::PhantomData}; +mod extension; mod registration; /// The owner of the sovereign account that should pay the rewards. @@ -57,9 +62,12 @@ pub enum RewardsAccountOwner { /// parameters to identify the account that pays a reward to the relayer. #[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] pub struct RewardsAccountParams { - lane_id: LaneId, - bridged_chain_id: ChainId, + // **IMPORTANT NOTE**: the order of fields here matters - we are using + // `into_account_truncating` and lane id is already `32` byte, so if other fields are encoded + // after it, they're simply dropped. So lane id shall be the last field. owner: RewardsAccountOwner, + bridged_chain_id: ChainId, + lane_id: LaneId, } impl RewardsAccountParams { @@ -162,21 +170,21 @@ mod tests { fn different_lanes_are_using_different_accounts() { assert_eq!( PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), + LaneId::new(1, 2), *b"test", RewardsAccountOwner::ThisChain )), - hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") + hex_literal::hex!("627261700074657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc") .into(), ); assert_eq!( PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 1]), + LaneId::new(1, 3), *b"test", RewardsAccountOwner::ThisChain )), - hex_literal::hex!("62726170000000017465737400726577617264732d6163636f756e7400000000") + hex_literal::hex!("627261700074657374a43e8951aa302c133beb5f85821a21645f07b487270ef3") .into(), ); } @@ -185,21 +193,21 @@ mod tests { fn different_directions_are_using_different_accounts() { assert_eq!( PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), + LaneId::new(1, 2), *b"test", RewardsAccountOwner::ThisChain )), - hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") + hex_literal::hex!("627261700074657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc") .into(), ); assert_eq!( PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), + LaneId::new(1, 2), *b"test", RewardsAccountOwner::BridgedChain )), - hex_literal::hex!("62726170000000007465737401726577617264732d6163636f756e7400000000") + hex_literal::hex!("627261700174657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc") .into(), ); } diff --git a/bridges/primitives/runtime/Cargo.toml b/bridges/primitives/runtime/Cargo.toml index 117409b37b94..7528f2e5d6ca 100644 --- a/bridges/primitives/runtime/Cargo.toml +++ b/bridges/primitives/runtime/Cargo.toml @@ -20,7 +20,6 @@ scale-info = { features = ["derive"], workspace = true } serde = { features = ["alloc", "derive"], workspace = true } # Substrate Dependencies - frame-support = { workspace = true } frame-system = { workspace = true } sp-core = { workspace = true } diff --git a/bridges/primitives/runtime/src/storage_proof.rs b/bridges/primitives/runtime/src/storage_proof.rs index 7bfa0d6fde01..8bd9001f2b6c 100644 --- a/bridges/primitives/runtime/src/storage_proof.rs +++ b/bridges/primitives/runtime/src/storage_proof.rs @@ -18,7 +18,7 @@ use frame_support::PalletError; use sp_core::RuntimeDebug; -use sp_std::{default::Default, vec::Vec}; +use sp_std::vec::Vec; use sp_trie::{ accessed_nodes_tracker::AccessedNodesTracker, read_trie_value, LayoutV1, MemoryDB, StorageProof, }; @@ -280,7 +280,7 @@ where /// Return valid storage proof and state root. /// -/// NOTE: This should only be used for **testing**. +/// Note: This should only be used for **testing**. #[cfg(feature = "std")] pub fn craft_valid_storage_proof() -> (sp_core::H256, RawStorageProof) { use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend}; diff --git a/bridges/primitives/xcm-bridge-hub-router/Cargo.toml b/bridges/primitives/xcm-bridge-hub-router/Cargo.toml index c3cf3356184b..ba0c51152bd2 100644 --- a/bridges/primitives/xcm-bridge-hub-router/Cargo.toml +++ b/bridges/primitives/xcm-bridge-hub-router/Cargo.toml @@ -18,6 +18,15 @@ scale-info = { features = ["bit-vec", "derive"], workspace = true } sp-runtime = { workspace = true } sp-core = { workspace = true } +# Polkadot Dependencies +xcm = { workspace = true } + [features] default = ["std"] -std = ["codec/std", "scale-info/std", "sp-core/std", "sp-runtime/std"] +std = [ + "codec/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "xcm/std", +] diff --git a/bridges/primitives/xcm-bridge-hub-router/src/lib.rs b/bridges/primitives/xcm-bridge-hub-router/src/lib.rs index dbedb7a52c7f..89123b51ef2f 100644 --- a/bridges/primitives/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/primitives/xcm-bridge-hub-router/src/lib.rs @@ -22,6 +22,7 @@ use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::H256; use sp_runtime::{FixedU128, RuntimeDebug}; +use xcm::latest::prelude::Location; /// Minimal delivery fee factor. pub const MINIMAL_DELIVERY_FEE_FACTOR: FixedU128 = FixedU128::from_u32(1); @@ -32,11 +33,11 @@ pub const MINIMAL_DELIVERY_FEE_FACTOR: FixedU128 = FixedU128::from_u32(1); /// of the bridge queues. pub trait XcmChannelStatusProvider { /// Returns true if the channel is currently congested. - fn is_congested() -> bool; + fn is_congested(with: &Location) -> bool; } impl XcmChannelStatusProvider for () { - fn is_congested() -> bool { + fn is_congested(_with: &Location) -> bool { false } } diff --git a/bridges/primitives/xcm-bridge-hub/Cargo.toml b/bridges/primitives/xcm-bridge-hub/Cargo.toml index 932e9ade0197..79201a8756f9 100644 --- a/bridges/primitives/xcm-bridge-hub/Cargo.toml +++ b/bridges/primitives/xcm-bridge-hub/Cargo.toml @@ -11,10 +11,34 @@ repository.workspace = true workspace = true [dependencies] +codec = { features = ["derive"], workspace = true } +scale-info = { features = ["derive"], workspace = true } +serde = { features = ["alloc", "derive"], workspace = true } + +# Bridge Dependencies +bp-messages = { workspace = true } +bp-runtime = { workspace = true } # Substrate Dependencies sp-std = { workspace = true } +sp-io = { workspace = true } +sp-core = { workspace = true } +frame-support = { workspace = true } + +# Polkadot Dependencies +xcm = { workspace = true } [features] default = ["std"] -std = ["sp-std/std"] +std = [ + "bp-messages/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "scale-info/std", + "serde/std", + "sp-core/std", + "sp-io/std", + "sp-std/std", + "xcm/std", +] diff --git a/bridges/primitives/xcm-bridge-hub/src/call_info.rs b/bridges/primitives/xcm-bridge-hub/src/call_info.rs new file mode 100644 index 000000000000..fd4fc67822fe --- /dev/null +++ b/bridges/primitives/xcm-bridge-hub/src/call_info.rs @@ -0,0 +1,43 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Defines structures related to calls of the `pallet-xcm-bridge-hub` pallet. + +use bp_messages::MessageNonce; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_std::boxed::Box; +use xcm::prelude::VersionedInteriorLocation; + +/// A minimized version of `pallet_xcm_bridge_hub::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum XcmBridgeHubCall { + /// `pallet_xcm_bridge_hub::Call::open_bridge` + #[codec(index = 0)] + open_bridge { + /// Universal `InteriorLocation` from the bridged consensus. + bridge_destination_universal_location: Box, + }, + /// `pallet_xcm_bridge_hub::Call::close_bridge` + #[codec(index = 1)] + close_bridge { + /// Universal `InteriorLocation` from the bridged consensus. + bridge_destination_universal_location: Box, + /// The number of messages that we may prune in a single call. + may_prune_messages: MessageNonce, + }, +} diff --git a/bridges/primitives/xcm-bridge-hub/src/lib.rs b/bridges/primitives/xcm-bridge-hub/src/lib.rs index 9745011c902d..44a90a57d4fb 100644 --- a/bridges/primitives/xcm-bridge-hub/src/lib.rs +++ b/bridges/primitives/xcm-bridge-hub/src/lib.rs @@ -19,6 +19,678 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +use bp_messages::LaneId; +use bp_runtime::{AccountIdOf, BalanceOf, Chain}; +pub use call_info::XcmBridgeHubCall; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ + ensure, sp_runtime::RuntimeDebug, CloneNoBound, PalletError, PartialEqNoBound, + RuntimeDebugNoBound, +}; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_core::H256; +use sp_io::hashing::blake2_256; +use sp_std::boxed::Box; +use xcm::{ + latest::prelude::*, prelude::XcmVersion, IntoVersion, VersionedInteriorLocation, + VersionedLocation, +}; + +mod call_info; + /// Encoded XCM blob. We expect the bridge messages pallet to use this blob type for both inbound /// and outbound payloads. pub type XcmAsPlainPayload = sp_std::vec::Vec; + +/// Bridge identifier - used **only** for communicating with sibling/parent chains in the same +/// consensus. +/// +/// For example, `SendXcm` implementations (which use the `latest` XCM) can use it to identify a +/// bridge and the corresponding `LaneId` that is used for over-consensus communication between +/// bridge hubs. +/// +/// This identifier is constructed from the `latest` XCM, so it is expected to ensure migration to +/// the `latest` XCM version. This could change the `BridgeId`, but it will not affect the `LaneId`. +/// In other words, `LaneId` will never change, while `BridgeId` could change with (every) XCM +/// upgrade. +#[derive( + Clone, + Copy, + Decode, + Encode, + Eq, + Ord, + PartialOrd, + PartialEq, + RuntimeDebug, + TypeInfo, + MaxEncodedLen, + Serialize, + Deserialize, +)] +pub struct BridgeId(H256); + +impl BridgeId { + /// Create bridge identifier from two universal locations. + /// + /// Note: The `BridgeId` is constructed from `latest` XCM, so if stored, you need to ensure + /// compatibility with newer XCM versions. + pub fn new( + universal_source: &InteriorLocation, + universal_destination: &InteriorLocation, + ) -> Self { + const VALUES_SEPARATOR: [u8; 33] = *b"bridges-bridge-id-value-separator"; + + BridgeId( + (universal_source, VALUES_SEPARATOR, universal_destination) + .using_encoded(blake2_256) + .into(), + ) + } +} + +/// Local XCM channel manager. +pub trait LocalXcmChannelManager { + /// Error that may be returned when suspending/resuming the bridge. + type Error: sp_std::fmt::Debug; + + /// Returns true if the channel with given location is currently congested. + /// + /// The `with` is guaranteed to be in the same consensus. However, it may point to something + /// below the chain level - like the contract or pallet instance, for example. + fn is_congested(with: &Location) -> bool; + + /// Suspend the bridge, opened by given origin. + /// + /// The `local_origin` is guaranteed to be in the same consensus. However, it may point to + /// something below the chain level - like the contract or pallet instance, for example. + fn suspend_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error>; + + /// Resume the previously suspended bridge, opened by given origin. + /// + /// The `local_origin` is guaranteed to be in the same consensus. However, it may point to + /// something below the chain level - like the contract or pallet instance, for example. + fn resume_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error>; +} + +impl LocalXcmChannelManager for () { + type Error = (); + + fn is_congested(_with: &Location) -> bool { + false + } + + fn suspend_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> { + Ok(()) + } + + fn resume_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> { + Ok(()) + } +} + +/// Bridge state. +#[derive(Clone, Copy, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum BridgeState { + /// Bridge is opened. Associated lanes are also opened. + Opened, + /// Bridge is suspended. Associated lanes are opened. + /// + /// We keep accepting messages to the bridge. The only difference with the `Opened` state + /// is that we have sent the "Suspended" message/signal to the local bridge origin. + Suspended, + /// Bridge is closed. Associated lanes are also closed. + /// After all outbound messages will be pruned, the bridge will vanish without any traces. + Closed, +} + +/// Bridge metadata. +#[derive( + CloneNoBound, Decode, Encode, Eq, PartialEqNoBound, TypeInfo, MaxEncodedLen, RuntimeDebugNoBound, +)] +#[scale_info(skip_type_params(ThisChain))] +pub struct Bridge { + /// Relative location of the bridge origin chain. This is expected to be **convertible** to the + /// `latest` XCM, so the check and migration needs to be ensured. + pub bridge_origin_relative_location: Box, + + /// See [`BridgeLocations::bridge_origin_universal_location`]. + /// Stored for `BridgeId` sanity check. + pub bridge_origin_universal_location: Box, + /// See [`BridgeLocations::bridge_destination_universal_location`]. + /// Stored for `BridgeId` sanity check. + pub bridge_destination_universal_location: Box, + + /// Current bridge state. + pub state: BridgeState, + /// Account with the reserved funds. Derived from `self.bridge_origin_relative_location`. + pub bridge_owner_account: AccountIdOf, + /// Reserved amount on the sovereign account of the sibling bridge origin. + pub deposit: BalanceOf, + + /// Mapping to the unique `LaneId`. + pub lane_id: LaneId, +} + +/// Locations of bridge endpoints at both sides of the bridge. +#[derive(Clone, RuntimeDebug, PartialEq, Eq)] +pub struct BridgeLocations { + /// Relative (to this bridge hub) location of this side of the bridge. + bridge_origin_relative_location: Location, + /// Universal (unique) location of this side of the bridge. + bridge_origin_universal_location: InteriorLocation, + /// Universal (unique) location of other side of the bridge. + bridge_destination_universal_location: InteriorLocation, + /// An identifier of the dedicated bridge message lane. + bridge_id: BridgeId, +} + +/// Errors that may happen when we check bridge locations. +#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] +pub enum BridgeLocationsError { + /// Origin or destination locations are not universal. + NonUniversalLocation, + /// Bridge origin location is not supported. + InvalidBridgeOrigin, + /// Bridge destination is not supported (in general). + InvalidBridgeDestination, + /// Destination location is within the same global consensus. + DestinationIsLocal, + /// Destination network is not the network we are bridged with. + UnreachableDestination, + /// Destination location is unsupported. We only support bridges with relay + /// chain or its parachains. + UnsupportedDestinationLocation, + /// The version of XCM location argument is unsupported. + UnsupportedXcmVersion, +} + +impl BridgeLocations { + /// Given XCM locations, generate lane id and universal locations of bridge endpoints. + /// + /// The `here_universal_location` is the universal location of the bridge hub runtime. + /// + /// The `bridge_origin_relative_location` is the relative (to the `here_universal_location`) + /// location of the bridge endpoint at this side of the bridge. It may be the parent relay + /// chain or the sibling parachain. All junctions below parachain level are dropped. + /// + /// The `bridge_destination_universal_location` is the universal location of the bridge + /// destination. It may be the parent relay or the sibling parachain of the **bridged** + /// bridge hub. All junctions below parachain level are dropped. + /// + /// Why we drop all junctions between parachain level - that's because the lane is a bridge + /// between two chains. All routing under this level happens when the message is delivered + /// to the bridge destination. So at bridge level we don't care about low level junctions. + /// + /// Returns error if `bridge_origin_relative_location` is outside of `here_universal_location` + /// local consensus OR if `bridge_destination_universal_location` is not a universal location. + pub fn bridge_locations( + here_universal_location: InteriorLocation, + bridge_origin_relative_location: Location, + bridge_destination_universal_location: InteriorLocation, + expected_remote_network: NetworkId, + ) -> Result, BridgeLocationsError> { + fn strip_low_level_junctions( + location: InteriorLocation, + ) -> Result { + let mut junctions = location.into_iter(); + + let global_consensus = junctions + .next() + .filter(|junction| matches!(junction, GlobalConsensus(_))) + .ok_or(BridgeLocationsError::NonUniversalLocation)?; + + // we only expect `Parachain` junction here. There are other junctions that + // may need to be supported (like `GeneralKey` and `OnlyChild`), but now we + // only support bridges with relay and parachans + // + // if there's something other than parachain, let's strip it + let maybe_parachain = + junctions.next().filter(|junction| matches!(junction, Parachain(_))); + Ok(match maybe_parachain { + Some(parachain) => [global_consensus, parachain].into(), + None => [global_consensus].into(), + }) + } + + // ensure that the `here_universal_location` and `bridge_destination_universal_location` + // are universal locations within different consensus systems + let local_network = here_universal_location + .global_consensus() + .map_err(|_| BridgeLocationsError::NonUniversalLocation)?; + let remote_network = bridge_destination_universal_location + .global_consensus() + .map_err(|_| BridgeLocationsError::NonUniversalLocation)?; + ensure!(local_network != remote_network, BridgeLocationsError::DestinationIsLocal); + ensure!( + remote_network == expected_remote_network, + BridgeLocationsError::UnreachableDestination + ); + + // get universal location of endpoint, located at this side of the bridge + let bridge_origin_universal_location = here_universal_location + .within_global(bridge_origin_relative_location.clone()) + .map_err(|_| BridgeLocationsError::InvalidBridgeOrigin)?; + // strip low-level junctions within universal locations + let bridge_origin_universal_location = + strip_low_level_junctions(bridge_origin_universal_location)?; + let bridge_destination_universal_location = + strip_low_level_junctions(bridge_destination_universal_location)?; + + // we know that the `bridge_destination_universal_location` starts from the + // `GlobalConsensus` and we know that the `bridge_origin_universal_location` + // is also within the `GlobalConsensus`. So we know that the lane id will be + // the same on both ends of the bridge + let bridge_id = BridgeId::new( + &bridge_origin_universal_location, + &bridge_destination_universal_location, + ); + + Ok(Box::new(BridgeLocations { + bridge_origin_relative_location, + bridge_origin_universal_location, + bridge_destination_universal_location, + bridge_id, + })) + } + + /// Getter for `bridge_origin_relative_location` + pub fn bridge_origin_relative_location(&self) -> &Location { + &self.bridge_origin_relative_location + } + + /// Getter for `bridge_origin_universal_location` + pub fn bridge_origin_universal_location(&self) -> &InteriorLocation { + &self.bridge_origin_universal_location + } + + /// Getter for `bridge_destination_universal_location` + pub fn bridge_destination_universal_location(&self) -> &InteriorLocation { + &self.bridge_destination_universal_location + } + + /// Getter for `bridge_id` + pub fn bridge_id(&self) -> &BridgeId { + &self.bridge_id + } + + /// Generates the exact same `LaneId` on the both bridge hubs. + /// + /// Note: Use this **only** when opening a new bridge. + pub fn calculate_lane_id( + &self, + xcm_version: XcmVersion, + ) -> Result { + // a tricky helper struct that adds required `Ord` support for + // `VersionedInteriorLocation` + #[derive(Eq, PartialEq, Ord, PartialOrd)] + struct EncodedVersionedInteriorLocation(sp_std::vec::Vec); + impl Encode for EncodedVersionedInteriorLocation { + fn encode(&self) -> sp_std::vec::Vec { + self.0.clone() + } + } + + let universal_location1 = + VersionedInteriorLocation::from(self.bridge_origin_universal_location.clone()) + .into_version(xcm_version) + .map_err(|_| BridgeLocationsError::UnsupportedXcmVersion); + let universal_location2 = + VersionedInteriorLocation::from(self.bridge_destination_universal_location.clone()) + .into_version(xcm_version) + .map_err(|_| BridgeLocationsError::UnsupportedXcmVersion); + + Ok(LaneId::new( + EncodedVersionedInteriorLocation(universal_location1.encode()), + EncodedVersionedInteriorLocation(universal_location2.encode()), + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const LOCAL_NETWORK: NetworkId = Kusama; + const REMOTE_NETWORK: NetworkId = Polkadot; + const UNREACHABLE_NETWORK: NetworkId = Rococo; + const SIBLING_PARACHAIN: u32 = 1000; + const LOCAL_BRIDGE_HUB: u32 = 1001; + const REMOTE_PARACHAIN: u32 = 2000; + + struct SuccessfulTest { + here_universal_location: InteriorLocation, + bridge_origin_relative_location: Location, + + bridge_origin_universal_location: InteriorLocation, + bridge_destination_universal_location: InteriorLocation, + + expected_remote_network: NetworkId, + } + + fn run_successful_test(test: SuccessfulTest) -> BridgeLocations { + let locations = BridgeLocations::bridge_locations( + test.here_universal_location, + test.bridge_origin_relative_location.clone(), + test.bridge_destination_universal_location.clone(), + test.expected_remote_network, + ); + assert_eq!( + locations, + Ok(Box::new(BridgeLocations { + bridge_origin_relative_location: test.bridge_origin_relative_location, + bridge_origin_universal_location: test.bridge_origin_universal_location.clone(), + bridge_destination_universal_location: test + .bridge_destination_universal_location + .clone(), + bridge_id: BridgeId::new( + &test.bridge_origin_universal_location, + &test.bridge_destination_universal_location, + ), + })), + ); + + *locations.unwrap() + } + + // successful tests that with various origins and destinations + + #[test] + fn at_relay_from_local_relay_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_relay_from_sibling_parachain_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: [Parachain(SIBLING_PARACHAIN)].into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_relay_from_local_relay_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_relay_from_sibling_parachain_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: [Parachain(SIBLING_PARACHAIN)].into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_local_relay_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: Parent.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_sibling_parachain_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_local_relay_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: Parent.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_sibling_parachain_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + // successful tests that show that we are ignoring low-level junctions of bridge origins + + #[test] + fn low_level_junctions_at_bridge_origin_are_stripped() { + let locations1 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + let locations2 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: [PalletInstance(0)].into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + + assert_eq!(locations1.bridge_id, locations2.bridge_id); + } + + #[test] + fn low_level_junctions_at_bridge_destination_are_stripped() { + let locations1 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + let locations2 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + + assert_eq!(locations1.bridge_id, locations2.bridge_id); + } + + #[test] + fn calculate_lane_id_works() { + let from_local_to_remote = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + + let from_remote_to_local = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(REMOTE_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(REMOTE_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + + expected_remote_network: LOCAL_NETWORK, + }); + + assert_ne!( + from_local_to_remote.calculate_lane_id(xcm::latest::VERSION), + from_remote_to_local.calculate_lane_id(xcm::latest::VERSION - 1), + ); + assert_eq!( + from_local_to_remote.calculate_lane_id(xcm::latest::VERSION), + from_remote_to_local.calculate_lane_id(xcm::latest::VERSION), + ); + } + + // negative tests + + #[test] + fn bridge_locations_fails_when_here_is_not_universal_location() { + assert_eq!( + BridgeLocations::bridge_locations( + [Parachain(1000)].into(), + Here.into(), + [GlobalConsensus(REMOTE_NETWORK)].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::NonUniversalLocation), + ); + } + + #[test] + fn bridge_locations_fails_when_computed_destination_is_not_universal_location() { + assert_eq!( + BridgeLocations::bridge_locations( + [GlobalConsensus(LOCAL_NETWORK)].into(), + Here.into(), + [OnlyChild].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::NonUniversalLocation), + ); + } + + #[test] + fn bridge_locations_fails_when_computed_destination_is_local() { + assert_eq!( + BridgeLocations::bridge_locations( + [GlobalConsensus(LOCAL_NETWORK)].into(), + Here.into(), + [GlobalConsensus(LOCAL_NETWORK), OnlyChild].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::DestinationIsLocal), + ); + } + + #[test] + fn bridge_locations_fails_when_computed_destination_is_unreachable() { + assert_eq!( + BridgeLocations::bridge_locations( + [GlobalConsensus(LOCAL_NETWORK)].into(), + Here.into(), + [GlobalConsensus(UNREACHABLE_NETWORK)].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::UnreachableDestination), + ); + } +} diff --git a/bridges/relays/client-substrate/Cargo.toml b/bridges/relays/client-substrate/Cargo.toml index 969cd73d6194..6065c23773e3 100644 --- a/bridges/relays/client-substrate/Cargo.toml +++ b/bridges/relays/client-substrate/Cargo.toml @@ -20,8 +20,12 @@ log = { workspace = true } num-traits = { workspace = true, default-features = true } rand = { workspace = true, default-features = true } serde_json = { workspace = true } -scale-info = { features = ["derive"], workspace = true, default-features = true } -tokio = { features = ["rt-multi-thread"], workspace = true, default-features = true } +scale-info = { features = [ + "derive", +], workspace = true, default-features = true } +tokio = { features = [ + "rt-multi-thread", +], workspace = true, default-features = true } thiserror = { workspace = true } quick_cache = { workspace = true } diff --git a/bridges/relays/lib-substrate-relay/Cargo.toml b/bridges/relays/lib-substrate-relay/Cargo.toml index b0f93e5b5485..89115cfeee92 100644 --- a/bridges/relays/lib-substrate-relay/Cargo.toml +++ b/bridges/relays/lib-substrate-relay/Cargo.toml @@ -22,6 +22,7 @@ num-traits = { workspace = true, default-features = true } rbtag = { workspace = true } structopt = { workspace = true } strum = { features = ["derive"], workspace = true, default-features = true } +rustc-hex = { workspace = true } thiserror = { workspace = true } # Bridge dependencies diff --git a/bridges/relays/lib-substrate-relay/src/cli/bridge.rs b/bridges/relays/lib-substrate-relay/src/cli/bridge.rs index 5631285b3c54..28b0eb0ad526 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/bridge.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/bridge.rs @@ -22,7 +22,7 @@ use crate::{ messages::{MessagesRelayLimits, SubstrateMessageLane}, parachains::SubstrateParachainsPipeline, }; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use relay_substrate_client::{ Chain, ChainWithRuntimeVersion, ChainWithTransactions, Parachain, RelayChain, }; diff --git a/bridges/relays/lib-substrate-relay/src/cli/mod.rs b/bridges/relays/lib-substrate-relay/src/cli/mod.rs index ddb3e416dc32..ef8403ff68ee 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/mod.rs @@ -16,13 +16,14 @@ //! Deal with CLI args of substrate-to-substrate relay. -use codec::{Decode, Encode}; +use bp_messages::LaneId; use rbtag::BuildInfo; +use sp_core::H256; +use sp_runtime::Either; +use std::str::FromStr; use structopt::StructOpt; use strum::{EnumString, VariantNames}; -use bp_messages::LaneId; - pub mod bridge; pub mod chain_schema; pub mod detect_equivocations; @@ -42,45 +43,36 @@ pub type DefaultClient = relay_substrate_client::RpcWithCachingClient; /// Lane id. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct HexLaneId(pub [u8; 4]); +pub struct HexLaneId(Either); impl From for LaneId { fn from(lane_id: HexLaneId) -> LaneId { - LaneId(lane_id.0) + LaneId::from_inner(lane_id.0) } } -impl std::str::FromStr for HexLaneId { - type Err = hex::FromHexError; - - fn from_str(s: &str) -> Result { - let mut lane_id = [0u8; 4]; - hex::decode_to_slice(s, &mut lane_id)?; - Ok(HexLaneId(lane_id)) - } -} - -/// Nicer formatting for raw bytes vectors. -#[derive(Default, Encode, Decode, PartialEq, Eq)] -pub struct HexBytes(pub Vec); - -impl std::str::FromStr for HexBytes { - type Err = hex::FromHexError; +impl FromStr for HexLaneId { + type Err = rustc_hex::FromHexError; fn from_str(s: &str) -> Result { - Ok(Self(hex::decode(s)?)) - } -} - -impl std::fmt::Debug for HexBytes { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "0x{self}") - } -} - -impl std::fmt::Display for HexBytes { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "{}", hex::encode(&self.0)) + // check `H256` variant at first + match H256::from_str(s) { + Ok(hash) => Ok(HexLaneId(Either::Left(hash))), + Err(hash_error) => { + // check backwards compatible + let mut lane_id = [0u8; 4]; + match hex::decode_to_slice(s, &mut lane_id) { + Ok(_) => Ok(HexLaneId(Either::Right(lane_id))), + Err(array_error) => { + log::error!( + target: "bridge", + "Failed to parse `HexLaneId` as hex string: {s:?} - hash_error: {hash_error:?}, array_error: {array_error:?}", + ); + Err(hash_error) + }, + } + }, + } } } @@ -182,15 +174,32 @@ mod tests { use super::*; #[test] - fn hex_bytes_display_matches_from_str_for_clap() { - // given - let hex = HexBytes(vec![1, 2, 3, 4]); - let display = format!("{hex}"); - - // when - let hex2: HexBytes = display.parse().unwrap(); - - // then - assert_eq!(hex.0, hex2.0); + fn hex_lane_id_from_str_works() { + // hash variant + assert!(HexLaneId::from_str( + "101010101010101010101010101010101010101010101010101010101010101" + ) + .is_err()); + assert!(HexLaneId::from_str( + "00101010101010101010101010101010101010101010101010101010101010101" + ) + .is_err()); + assert_eq!( + LaneId::from( + HexLaneId::from_str( + "0101010101010101010101010101010101010101010101010101010101010101" + ) + .unwrap() + ), + LaneId::from_inner(Either::Left(H256::from([1u8; 32]))) + ); + + // array variant + assert!(HexLaneId::from_str("0000001").is_err()); + assert!(HexLaneId::from_str("000000001").is_err()); + assert_eq!( + LaneId::from(HexLaneId::from_str("00000001").unwrap()), + LaneId::from_inner(Either::Right([0, 0, 0, 1])) + ); } } diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs index 338dda3c6330..3786976bed9b 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs @@ -55,7 +55,7 @@ use sp_core::Pair; #[derive(Debug, PartialEq, StructOpt)] pub struct HeadersAndMessagesSharedParams { /// Hex-encoded lane identifiers that should be served by the complex relay. - #[structopt(long, default_value = "00000000")] + #[structopt(long)] pub lane: Vec, /// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) /// are relayed. @@ -359,6 +359,8 @@ mod tests { use crate::{cli::chain_schema::RuntimeVersionType, declare_chain_cli_schema}; use relay_substrate_client::{ChainRuntimeVersion, Parachain, SimpleRuntimeVersion}; + use sp_core::H256; + use sp_runtime::Either; #[test] // We need `#[allow(dead_code)]` because some of the methods generated by the macros @@ -422,7 +424,7 @@ mod tests { "--polkadot-port", "9944", "--lane", - "00000000", + "0000000000000000000000000000000000000000000000000000000000000000", "--prometheus-host", "0.0.0.0", ]); @@ -432,7 +434,7 @@ mod tests { res, BridgeHubKusamaBridgeHubPolkadotHeadersAndMessages { shared: HeadersAndMessagesSharedParams { - lane: vec![HexLaneId([0x00, 0x00, 0x00, 0x00])], + lane: vec![HexLaneId(Either::Left(H256::from([0x00u8; 32])))], only_mandatory_headers: false, only_free_headers: false, prometheus_params: PrometheusParams { diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs index 8104be7af807..e8b797f84fa5 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs @@ -30,8 +30,8 @@ use crate::{ headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, }, }; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::ParaHash; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use relay_substrate_client::{ AccountIdOf, AccountKeyPairOf, Chain, ChainWithRuntimeVersion, ChainWithTransactions, Client, Parachain, diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs index 6c078973fedc..f9884ee197b4 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs @@ -33,8 +33,8 @@ use crate::{ headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, }, }; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::ParaHash; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use relay_substrate_client::{ AccountIdOf, AccountKeyPairOf, Chain, ChainWithRuntimeVersion, ChainWithTransactions, Client, Parachain, diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs index 68bbe71ae599..34d5226e90c5 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs @@ -37,8 +37,8 @@ use relay_utils::UniqueSaturatedInto; /// Messages relaying params. #[derive(StructOpt)] pub struct RelayMessagesParams { - /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. - #[structopt(long, default_value = "00000000")] + /// Hex-encoded lane id that should be served by the relay. + #[structopt(long)] lane: HexLaneId, #[structopt(flatten)] source: SourceConnectionParams, @@ -59,8 +59,8 @@ pub struct RelayMessagesRangeParams { /// This header must be previously proved to the target chain. #[structopt(long)] at_source_block: u128, - /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. - #[structopt(long, default_value = "00000000")] + /// Hex-encoded lane id that should be served by the relay. + #[structopt(long)] lane: HexLaneId, /// Nonce (inclusive) of the first message to relay. #[structopt(long)] @@ -88,8 +88,8 @@ pub struct RelayMessagesDeliveryConfirmationParams { /// delivery proof. This header must be previously proved to the source chain. #[structopt(long)] at_target_block: u128, - /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. - #[structopt(long, default_value = "00000000")] + /// Hex-encoded lane id that should be served by the relay. + #[structopt(long)] lane: HexLaneId, #[structopt(flatten)] source: SourceConnectionParams, diff --git a/bridges/relays/lib-substrate-relay/src/messages/mod.rs b/bridges/relays/lib-substrate-relay/src/messages/mod.rs index e52b70206669..28bc5c7f5e8e 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/mod.rs @@ -644,7 +644,7 @@ where FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: Default::default(), + lane: LaneId::new(1, 2), nonces_start: 1, nonces_end: messages as u64, }, @@ -706,7 +706,7 @@ mod tests { let receive_messages_proof = FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), + lane: LaneId::new(1, 2), nonces_start: 0, nonces_end: 0, }; @@ -761,7 +761,7 @@ mod tests { let receive_messages_delivery_proof = FromBridgedChainMessagesDeliveryProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), + lane: LaneId::new(1, 2), }; let relayers_state = UnrewardedRelayersState { unrewarded_relayer_entries: 0, @@ -838,7 +838,6 @@ mod tests { type ThisChain = ThisUnderlyingChain; type BridgedChain = BridgedUnderlyingChain; type BridgedHeaderChain = BridgedHeaderChain; - type ActiveOutboundLanes = (); type OutboundPayload = Vec; type InboundPayload = Vec; type DeliveryPayments = (); diff --git a/bridges/relays/lib-substrate-relay/src/messages/source.rs b/bridges/relays/lib-substrate-relay/src/messages/source.rs index b75fc86d5eee..2c49df3452ab 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/source.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/source.rs @@ -661,7 +661,7 @@ mod tests { } let maybe_batches = - split_msgs_to_refine::(Default::default(), msgs_to_refine); + split_msgs_to_refine::(LaneId::new(1, 2), msgs_to_refine); match expected_batches { Ok(expected_batches) => { let batches = maybe_batches.unwrap(); diff --git a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs index 4579222a2c68..2ef86f48ecbe 100644 --- a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs +++ b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs @@ -31,11 +31,11 @@ use async_std::{ sync::{Arc, Mutex}, }; use async_trait::async_trait; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::{ParaHash, ParaId}; use bp_runtime::HeaderIdProvider; use futures::{select, FutureExt}; use num_traits::Zero; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use parachains_relay::parachains_loop::{AvailableHeader, SourceClient, TargetClient}; use relay_substrate_client::{ is_ancient_block, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, diff --git a/bridges/relays/lib-substrate-relay/src/parachains/mod.rs b/bridges/relays/lib-substrate-relay/src/parachains/mod.rs index 8b128bb770dd..08d8e5e2a4f5 100644 --- a/bridges/relays/lib-substrate-relay/src/parachains/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/parachains/mod.rs @@ -18,11 +18,9 @@ //! parachain finality proofs synchronization pipelines. use async_trait::async_trait; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; -use pallet_bridge_parachains::{ - Call as BridgeParachainsCall, Config as BridgeParachainsConfig, RelayBlockHash, - RelayBlockHasher, RelayBlockNumber, -}; +use pallet_bridge_parachains::{Call as BridgeParachainsCall, Config as BridgeParachainsConfig}; use parachains_relay::ParachainsPipeline; use relay_substrate_client::{ CallOf, Chain, ChainWithTransactions, HeaderIdOf, Parachain, RelayChain, diff --git a/bridges/relays/messages/src/message_lane_loop.rs b/bridges/relays/messages/src/message_lane_loop.rs index b681d86d2ae8..995499092c3e 100644 --- a/bridges/relays/messages/src/message_lane_loop.rs +++ b/bridges/relays/messages/src/message_lane_loop.rs @@ -276,7 +276,7 @@ pub struct ClientsState { /// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs /// sync loop. pub fn metrics_prefix(lane: &LaneId) -> String { - format!("{}_to_{}_MessageLane_{}", P::SOURCE_NAME, P::TARGET_NAME, hex::encode(lane)) + format!("{}_to_{}_MessageLane_{:?}", P::SOURCE_NAME, P::TARGET_NAME, lane) } /// Run message lane service loop. @@ -957,7 +957,7 @@ pub(crate) mod tests { }; let _ = run( Params { - lane: LaneId([0, 0, 0, 0]), + lane: LaneId::new(1, 2), source_tick: Duration::from_millis(100), target_tick: Duration::from_millis(100), reconnect_delay: Duration::from_millis(0), @@ -1274,4 +1274,12 @@ pub(crate) mod tests { assert!(!result.target_to_source_header_requirements.is_empty()); assert!(!result.source_to_target_header_requirements.is_empty()); } + + #[test] + fn metrics_prefix_is_valid() { + assert!(MessageLaneLoopMetrics::new(Some(&metrics_prefix::( + &LaneId::new(1, 2) + ))) + .is_ok()); + } } diff --git a/bridges/relays/parachains/src/parachains_loop.rs b/bridges/relays/parachains/src/parachains_loop.rs index 0fd1d72c7075..59ca458e6667 100644 --- a/bridges/relays/parachains/src/parachains_loop.rs +++ b/bridges/relays/parachains/src/parachains_loop.rs @@ -496,7 +496,7 @@ where }, (AvailableHeader::Missing, Some(_)) => { // parachain/parathread has been offboarded removed from the system. It needs to - // be propageted to the target client + // be propagated to the target client true }, (AvailableHeader::Missing, None) => { diff --git a/cumulus/pallets/xcmp-queue/src/bridging.rs b/cumulus/pallets/xcmp-queue/src/bridging.rs index eff4a37b0cef..8ed11505a27a 100644 --- a/cumulus/pallets/xcmp-queue/src/bridging.rs +++ b/cumulus/pallets/xcmp-queue/src/bridging.rs @@ -15,48 +15,52 @@ use crate::{pallet, OutboundState}; use cumulus_primitives_core::ParaId; -use frame_support::pallet_prelude::Get; +use xcm::latest::prelude::*; /// Adapter implementation for `bp_xcm_bridge_hub_router::XcmChannelStatusProvider` which checks -/// both `OutboundXcmpStatus` and `InboundXcmpStatus` for defined `ParaId` if any of those is +/// both `OutboundXcmpStatus` and `InboundXcmpStatus` for defined `Location` if any of those is /// suspended. -pub struct InAndOutXcmpChannelStatusProvider( - core::marker::PhantomData<(SiblingBridgeHubParaId, Runtime)>, -); -impl, Runtime: crate::Config> - bp_xcm_bridge_hub_router::XcmChannelStatusProvider - for InAndOutXcmpChannelStatusProvider +pub struct InAndOutXcmpChannelStatusProvider(core::marker::PhantomData); +impl bp_xcm_bridge_hub_router::XcmChannelStatusProvider + for InAndOutXcmpChannelStatusProvider { - fn is_congested() -> bool { + fn is_congested(with: &Location) -> bool { + // handle congestion only for a sibling parachain locations. + let sibling_para_id: ParaId = match with.unpack() { + (_, [Parachain(para_id)]) => (*para_id).into(), + _ => return false, + }; + // if the inbound channel with recipient is suspended, it means that we are unable to - // receive congestion reports from the bridge hub. So we assume the bridge pipeline is - // congested too - if pallet::Pallet::::is_inbound_channel_suspended(SiblingBridgeHubParaId::get()) { + // receive congestion reports from the `with` location. So we assume the pipeline is + // congested too. + if pallet::Pallet::::is_inbound_channel_suspended(sibling_para_id) { return true } // if the outbound channel with recipient is suspended, it means that one of further - // bridge queues (e.g. bridge queue between two bridge hubs) is overloaded, so we shall + // queues (e.g. bridge queue between two bridge hubs) is overloaded, so we shall // take larger fee for our outbound messages - OutXcmpChannelStatusProvider::::is_congested() + OutXcmpChannelStatusProvider::::is_congested(with) } } /// Adapter implementation for `bp_xcm_bridge_hub_router::XcmChannelStatusProvider` which checks /// only `OutboundXcmpStatus` for defined `SiblingParaId` if is suspended. -pub struct OutXcmpChannelStatusProvider( - core::marker::PhantomData<(SiblingBridgeHubParaId, Runtime)>, -); -impl, Runtime: crate::Config> - bp_xcm_bridge_hub_router::XcmChannelStatusProvider - for OutXcmpChannelStatusProvider +pub struct OutXcmpChannelStatusProvider(core::marker::PhantomData); +impl bp_xcm_bridge_hub_router::XcmChannelStatusProvider + for OutXcmpChannelStatusProvider { - fn is_congested() -> bool { - let sibling_bridge_hub_id: ParaId = SiblingBridgeHubParaId::get(); + fn is_congested(with: &Location) -> bool { + // handle congestion only for a sibling parachain locations. + let sibling_para_id: ParaId = match with.unpack() { + (_, [Parachain(para_id)]) => (*para_id).into(), + _ => return false, + }; // let's find the channel's state with the sibling parachain, let Some((outbound_state, queued_pages)) = - pallet::Pallet::::outbound_channel_state(sibling_bridge_hub_id) + pallet::Pallet::::outbound_channel_state(sibling_para_id) else { return false }; diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml index 7bd91ae6774c..51ce5b180056 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml @@ -26,3 +26,6 @@ testnet-parachains-constants = { features = ["rococo"], workspace = true, defaul # Polkadot xcm = { workspace = true } + +# Bridges +bp-bridge-hub-rococo = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs index 1f98d3ba964d..75b61d6a4cd7 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs @@ -24,8 +24,8 @@ use frame_support::traits::OnInitialize; use emulated_integration_tests_common::{ impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain, impl_assets_helpers_for_parachain, impl_assets_helpers_for_system_parachain, - impl_foreign_assets_helpers_for_parachain, impl_xcm_helpers_for_parachain, impls::Parachain, - xcm_emulator::decl_test_parachains, + impl_bridge_helpers_for_chain, impl_foreign_assets_helpers_for_parachain, + impl_xcm_helpers_for_parachain, impls::Parachain, xcm_emulator::decl_test_parachains, }; use rococo_emulated_chain::Rococo; @@ -61,3 +61,9 @@ impl_assets_helpers_for_system_parachain!(AssetHubRococo, Rococo); impl_assets_helpers_for_parachain!(AssetHubRococo); impl_foreign_assets_helpers_for_parachain!(AssetHubRococo, xcm::v4::Location); impl_xcm_helpers_for_parachain!(AssetHubRococo); +impl_bridge_helpers_for_chain!( + AssetHubRococo, + ParaPallet, + PolkadotXcm, + bp_bridge_hub_rococo::RuntimeCall::XcmOverBridgeHubWestend +); diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml index 86d4ce3e7ac8..d32f98321706 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml @@ -26,3 +26,6 @@ testnet-parachains-constants = { features = ["westend"], workspace = true, defau # Polkadot xcm = { workspace = true } + +# Bridges +bp-bridge-hub-westend = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs index 6066adec52c3..c44f4b010c0a 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs @@ -24,8 +24,8 @@ use frame_support::traits::OnInitialize; use emulated_integration_tests_common::{ impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain, impl_assets_helpers_for_parachain, impl_assets_helpers_for_system_parachain, - impl_foreign_assets_helpers_for_parachain, impl_xcm_helpers_for_parachain, impls::Parachain, - xcm_emulator::decl_test_parachains, + impl_bridge_helpers_for_chain, impl_foreign_assets_helpers_for_parachain, + impl_xcm_helpers_for_parachain, impls::Parachain, xcm_emulator::decl_test_parachains, }; use westend_emulated_chain::Westend; @@ -61,3 +61,9 @@ impl_assets_helpers_for_system_parachain!(AssetHubWestend, Westend); impl_assets_helpers_for_parachain!(AssetHubWestend); impl_foreign_assets_helpers_for_parachain!(AssetHubWestend, xcm::v4::Location); impl_xcm_helpers_for_parachain!(AssetHubWestend); +impl_bridge_helpers_for_chain!( + AssetHubWestend, + ParaPallet, + PolkadotXcm, + bp_bridge_hub_westend::RuntimeCall::XcmOverBridgeHubRococo +); diff --git a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml index 7152f1dbc272..981ee5c88b4e 100644 --- a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml @@ -42,5 +42,7 @@ asset-test-utils = { workspace = true, default-features = true } # Bridges bp-messages = { workspace = true, default-features = true } +bp-xcm-bridge-hub = { workspace = true, default-features = true } pallet-bridge-messages = { workspace = true, default-features = true } +pallet-xcm-bridge-hub = { workspace = true, default-features = true } bridge-runtime-common = { workspace = true, default-features = true } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs index 8f2789eb2f3a..559a16379bb4 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs @@ -17,7 +17,8 @@ pub use codec::{Decode, Encode}; pub use paste; pub use crate::{ - xcm_helpers::xcm_transact_unpaid_execution, PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, + xcm_helpers::{xcm_transact_paid_execution, xcm_transact_unpaid_execution}, + PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, }; // Substrate @@ -30,7 +31,6 @@ pub use frame_support::{ pub use pallet_assets; pub use pallet_message_queue; pub use pallet_xcm; -use sp_core::Get; // Polkadot pub use polkadot_runtime_parachains::{ @@ -38,7 +38,9 @@ pub use polkadot_runtime_parachains::{ inclusion::{AggregateMessageOrigin, UmpQueueId}, }; pub use xcm::{ - prelude::{Location, OriginKind, Outcome, VersionedXcm, XcmError, XcmVersion}, + prelude::{ + Asset, InteriorLocation, Location, OriginKind, Outcome, VersionedXcm, XcmError, XcmVersion, + }, DoubleEncoded, }; @@ -51,7 +53,7 @@ pub use cumulus_primitives_core::{ }; pub use parachains_common::{AccountId, Balance}; pub use xcm_emulator::{ - assert_expected_events, bx, helpers::weight_within_threshold, BridgeMessage, + assert_expected_events, bx, helpers::weight_within_threshold, BridgeLaneId, BridgeMessage, BridgeMessageDispatchError, BridgeMessageHandler, Chain, Network, Parachain, RelayChain, TestExt, }; @@ -61,60 +63,60 @@ use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, LaneId, MessageKey, OutboundLaneData, }; -use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; -use pallet_bridge_messages::{Config, OutboundLanes, Pallet}; +pub use bp_xcm_bridge_hub::XcmBridgeHubCall; +use pallet_bridge_messages::{Config as BridgeMessagesConfig, OutboundLanes, Pallet}; pub use pallet_bridge_messages::{ Instance1 as BridgeMessagesInstance1, Instance2 as BridgeMessagesInstance2, Instance3 as BridgeMessagesInstance3, }; +use pallet_xcm_bridge_hub::XcmBlobMessageDispatchResult; pub struct BridgeHubMessageHandler { _marker: std::marker::PhantomData<(S, SI, T, TI)>, } struct LaneIdWrapper(LaneId); - -impl From for u32 { - fn from(lane_id: LaneIdWrapper) -> u32 { - u32::from_be_bytes(lane_id.0 .0) +impl From for BridgeLaneId { + fn from(lane_id: LaneIdWrapper) -> BridgeLaneId { + lane_id.0.encode() } } - -impl From for LaneIdWrapper { - fn from(id: u32) -> LaneIdWrapper { - LaneIdWrapper(LaneId(id.to_be_bytes())) +impl From for LaneIdWrapper { + fn from(id: BridgeLaneId) -> LaneIdWrapper { + LaneIdWrapper(LaneId::decode(&mut &id[..]).expect("decodable")) } } impl BridgeMessageHandler for BridgeHubMessageHandler where - S: Config, + S: BridgeMessagesConfig, SI: 'static, - T: Config, + T: BridgeMessagesConfig, TI: 'static, - >::InboundPayload: From>, - >::MessageDispatch: + >::InboundPayload: From>, + >::MessageDispatch: MessageDispatch, { fn get_source_outbound_messages() -> Vec { // get the source active outbound lanes - let active_lanes = S::ActiveOutboundLanes::get(); + let active_outbound_lanes = OutboundLanes::::iter_keys(); let mut messages: Vec = Default::default(); // collect messages from `OutboundMessages` for each active outbound lane in the source - for lane in active_lanes { - let latest_generated_nonce = OutboundLanes::::get(lane).latest_generated_nonce; - let latest_received_nonce = OutboundLanes::::get(lane).latest_received_nonce; + for lane in active_outbound_lanes { + let latest_generated_nonce = + OutboundLanes::::get(lane).unwrap().latest_generated_nonce; + let latest_received_nonce = + OutboundLanes::::get(lane).unwrap().latest_received_nonce; (latest_received_nonce + 1..=latest_generated_nonce).for_each(|nonce| { - let encoded_payload: Vec = Pallet::::outbound_message_data(*lane, nonce) + let encoded_payload: Vec = Pallet::::outbound_message_data(lane, nonce) .expect("Bridge message does not exist") .into(); let payload = Vec::::decode(&mut &encoded_payload[..]) .expect("Decoding XCM message failed"); - let id: u32 = LaneIdWrapper(*lane).into(); - let message = BridgeMessage { id, nonce, payload }; + let message = BridgeMessage { lane_id: LaneIdWrapper(lane).into(), nonce, payload }; messages.push(message); }); @@ -125,10 +127,10 @@ where fn dispatch_target_inbound_message( message: BridgeMessage, ) -> Result<(), BridgeMessageDispatchError> { - type TargetMessageDispatch = >::MessageDispatch; - type InboundPayload = >::InboundPayload; + type TargetMessageDispatch = >::MessageDispatch; + type InboundPayload = >::InboundPayload; - let lane_id = LaneIdWrapper::from(message.id).0; + let lane_id = LaneIdWrapper::from(message.lane_id).0; let nonce = message.nonce; let payload = Ok(From::from(message.payload)); @@ -151,15 +153,16 @@ where result } - fn notify_source_message_delivery(lane_id: u32) { - let data = OutboundLanes::::get(LaneIdWrapper::from(lane_id).0); + fn notify_source_message_delivery(lane_id: BridgeLaneId) { + let lane_id = LaneIdWrapper::from(lane_id).0; + let data = OutboundLanes::::get(lane_id).unwrap(); let new_data = OutboundLaneData { oldest_unpruned_nonce: data.oldest_unpruned_nonce + 1, latest_received_nonce: data.latest_received_nonce + 1, ..data }; - OutboundLanes::::insert(LaneIdWrapper::from(lane_id).0, new_data); + OutboundLanes::::insert(lane_id, new_data); } } @@ -925,3 +928,49 @@ macro_rules! impl_xcm_helpers_for_parachain { } } } + +#[macro_export] +macro_rules! impl_bridge_helpers_for_chain { + ( $chain:ident, $pallet:ident, $pallet_xcm:ident, $runtime_call_wrapper:path ) => { + $crate::impls::paste::paste! { + impl $chain { + /// Open bridge with `dest`. + pub fn open_bridge( + bridge_location: $crate::impls::Location, + bridge_destination_universal_location: $crate::impls::InteriorLocation, + maybe_paid: Option<($crate::impls::Asset, $crate::impls::AccountId)> + ) { + ::execute_with(|| { + use $crate::impls::{bx, Chain}; + use $crate::impls::XcmBridgeHubCall; + use $crate::impls::Encode; + + // important to use `root` and `OriginKind::Xcm` + let root_origin = ::RuntimeOrigin::root(); + + // construct call + let call: $crate::impls::DoubleEncoded<()> = $runtime_call_wrapper(XcmBridgeHubCall::open_bridge { + bridge_destination_universal_location: bx!( + bridge_destination_universal_location.clone().into() + ) + }).encode().into(); + + let xcm = if let Some((fee_asset, beneficiary)) = maybe_paid { + $crate::impls::xcm_transact_paid_execution(call, $crate::impls::OriginKind::Xcm, fee_asset, beneficiary) + } else { + $crate::impls::xcm_transact_unpaid_execution(call, $crate::impls::OriginKind::Xcm) + }; + + // Send XCM `Transact` with `open_bridge` call + $crate::impls::assert_ok!(]>::$pallet_xcm::send( + root_origin, + bx!(bridge_location.into()), + bx!(xcm), + )); + Self::assert_xcm_pallet_sent(); + }); + } + } + } + } +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml index a5787885329d..86ace7d564e8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml @@ -31,6 +31,7 @@ xcm-executor = { workspace = true } # Bridges pallet-bridge-messages = { workspace = true } +pallet-xcm-bridge-hub = { workspace = true } # Cumulus cumulus-pallet-xcmp-queue = { workspace = true } @@ -38,7 +39,7 @@ emulated-integration-tests-common = { workspace = true } parachains-common = { workspace = true, default-features = true } rococo-system-emulated-network = { workspace = true } rococo-westend-system-emulated-network = { workspace = true } -testnet-parachains-constants = { features = ["rococo"], workspace = true, default-features = true } +testnet-parachains-constants = { features = ["rococo", "westend"], workspace = true, default-features = true } # Snowbridge snowbridge-core = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs index 8a674f89c9ef..6df51c5f7048 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs @@ -25,6 +25,9 @@ fn send_assets_over_bridge(send_fn: F) { AssetHubRococo::force_xcm_version(asset_hub_westend_location(), XCM_VERSION); BridgeHubRococo::force_xcm_version(bridge_hub_westend_location(), XCM_VERSION); + // open bridge + open_bridge_between_asset_hub_rococo_and_asset_hub_westend(); + // send message over bridge send_fn(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs index 6ce8ecef0df3..b540f55642a5 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs @@ -36,7 +36,10 @@ pub(crate) fn bridged_roc_at_ah_westend() -> Location { Location::new(2, [GlobalConsensus(Rococo)]) } -// wWND +// WND and wWND +pub(crate) fn wnd_at_ah_westend() -> Location { + Parent.into() +} pub(crate) fn bridged_wnd_at_ah_rococo() -> Location { Location::new(2, [GlobalConsensus(Westend)]) } @@ -210,3 +213,57 @@ pub(crate) fn assert_bridge_hub_westend_message_received() { ); }) } + +pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() { + use testnet_parachains_constants::{ + rococo::currency::UNITS as ROC, westend::currency::UNITS as WND, + }; + + // open AHR -> AHW + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), ROC * 5); + AssetHubRococo::open_bridge( + AssetHubRococo::sibling_location_of(BridgeHubRococo::para_id()), + [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())].into(), + Some(( + (roc_at_ah_rococo(), ROC * 1).into(), + BridgeHubRococo::sovereign_account_id_of(BridgeHubRococo::sibling_location_of( + AssetHubRococo::para_id(), + )), + )), + ); + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmOverBridgeHubWestend( + pallet_xcm_bridge_hub::Event::BridgeOpened { .. } + ) => {}, + ] + ); + }); + + // open AHW -> AHR + BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), WND * 5); + AssetHubWestend::open_bridge( + AssetHubWestend::sibling_location_of(BridgeHubWestend::para_id()), + [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())].into(), + Some(( + (wnd_at_ah_westend(), WND * 1).into(), + BridgeHubWestend::sovereign_account_id_of(BridgeHubWestend::sibling_location_of( + AssetHubWestend::para_id(), + )), + )), + ); + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + BridgeHubWestend, + vec![ + RuntimeEvent::XcmOverBridgeHubRococo( + pallet_xcm_bridge_hub::Event::BridgeOpened { .. } + ) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs index 3f2038b4bdd1..12f05742a080 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs @@ -79,6 +79,9 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() { // fund sender AssetHubRococo::fund_accounts(vec![(AssetHubRococoSender::get().into(), amount * 10)]); + // open bridge + open_bridge_between_asset_hub_rococo_and_asset_hub_westend(); + // send XCM from AssetHubRococo - fails - destination version not known assert_err!( send_assets_from_asset_hub_rococo( diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml index 1f2d2c8ece24..44121cbfdafb 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml @@ -32,13 +32,14 @@ xcm-executor = { workspace = true } # Bridges pallet-bridge-messages = { workspace = true } +pallet-xcm-bridge-hub = { workspace = true } # Cumulus cumulus-pallet-xcmp-queue = { workspace = true } emulated-integration-tests-common = { workspace = true } parachains-common = { workspace = true, default-features = true } rococo-westend-system-emulated-network = { workspace = true } -testnet-parachains-constants = { workspace = true, features = ["westend"] } +testnet-parachains-constants = { features = ["rococo", "westend"], workspace = true, default-features = true } asset-hub-westend-runtime = { workspace = true } bridge-hub-westend-runtime = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs index fc8b772a9c7e..c3f81175da23 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs @@ -24,6 +24,9 @@ fn send_assets_over_bridge(send_fn: F) { AssetHubWestend::force_xcm_version(asset_hub_rococo_location(), XCM_VERSION); BridgeHubWestend::force_xcm_version(bridge_hub_rococo_location(), XCM_VERSION); + // open bridge + open_bridge_between_asset_hub_rococo_and_asset_hub_westend(); + // send message over bridge send_fn(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs index bf894a3baf58..30cc4de3905e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs @@ -37,7 +37,10 @@ pub(crate) fn bridged_wnd_at_ah_rococo() -> Location { Location::new(2, [GlobalConsensus(Westend)]) } -// wROC +// ROC and wROC +pub(crate) fn roc_at_ah_rococo() -> Location { + Parent.into() +} pub(crate) fn bridged_roc_at_ah_westend() -> Location { Location::new(2, [GlobalConsensus(Rococo)]) } @@ -224,3 +227,57 @@ pub(crate) fn assert_bridge_hub_rococo_message_received() { ); }) } + +pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() { + use testnet_parachains_constants::{ + rococo::currency::UNITS as ROC, westend::currency::UNITS as WND, + }; + + // open AHR -> AHW + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), ROC * 5); + AssetHubRococo::open_bridge( + AssetHubRococo::sibling_location_of(BridgeHubRococo::para_id()), + [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())].into(), + Some(( + (roc_at_ah_rococo(), ROC * 1).into(), + BridgeHubRococo::sovereign_account_id_of(BridgeHubRococo::sibling_location_of( + AssetHubRococo::para_id(), + )), + )), + ); + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmOverBridgeHubWestend( + pallet_xcm_bridge_hub::Event::BridgeOpened { .. } + ) => {}, + ] + ); + }); + + // open AHW -> AHR + BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), WND * 5); + AssetHubWestend::open_bridge( + AssetHubWestend::sibling_location_of(BridgeHubWestend::para_id()), + [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())].into(), + Some(( + (wnd_at_ah_westend(), WND * 1).into(), + BridgeHubWestend::sovereign_account_id_of(BridgeHubWestend::sibling_location_of( + AssetHubWestend::para_id(), + )), + )), + ); + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + BridgeHubWestend, + vec![ + RuntimeEvent::XcmOverBridgeHubRococo( + pallet_xcm_bridge_hub::Event::BridgeOpened { .. } + ) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs index dee411bea8b7..ae05e4223b07 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs @@ -79,6 +79,9 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() { // fund sender AssetHubWestend::fund_accounts(vec![(AssetHubWestendSender::get().into(), amount * 10)]); + // open bridge + open_bridge_between_asset_hub_rococo_and_asset_hub_westend(); + // send XCM from AssetHubWestend - fails - destination version not known assert_err!( send_assets_from_asset_hub_westend( diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 896edd012dd5..2f3fb6b68c4a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -63,8 +63,7 @@ use frame_support::{ ord_parameter_types, parameter_types, traits::{ fungible, fungibles, tokens::imbalance::ResolveAssetTo, AsEnsureOriginWithArg, ConstBool, - ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Equals, InstanceFilter, - TransformOrigin, + ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, BoundedVec, PalletId, @@ -917,29 +916,18 @@ impl pallet_nfts::Config for Runtime { /// consensus with dynamic fees and back-pressure. pub type ToWestendXcmRouterInstance = pallet_xcm_bridge_hub_router::Instance3; impl pallet_xcm_bridge_hub_router::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::pallet_xcm_bridge_hub_router::WeightInfo; type UniversalLocation = xcm_config::UniversalLocation; + type SiblingBridgeHubLocation = xcm_config::bridging::SiblingBridgeHub; type BridgedNetworkId = xcm_config::bridging::to_westend::WestendNetwork; type Bridges = xcm_config::bridging::NetworkExportTable; type DestinationVersion = PolkadotXcm; - #[cfg(not(feature = "runtime-benchmarks"))] - type BridgeHubOrigin = EnsureXcm>; - #[cfg(feature = "runtime-benchmarks")] - type BridgeHubOrigin = EitherOfDiverse< - // for running benchmarks - EnsureRoot, - // for running tests with `--feature runtime-benchmarks` - EnsureXcm>, - >; - type ToBridgeHubSender = XcmpQueue; - type WithBridgeHubChannel = - cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider< - xcm_config::bridging::SiblingBridgeHubParaId, - Runtime, - >; + type LocalXcmChannelManager = + cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider; type ByteFee = xcm_config::bridging::XcmBridgeHubRouterByteFee; type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs index 0a86037391b4..00ecf239428f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_bridge_hub_router` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -52,14 +52,14 @@ impl pallet_xcm_bridge_hub_router::WeightInfo for Weigh /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) - /// Storage: `ToWestendXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToWestendXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) + /// Storage: `ToWestendXcmRouter::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `ToWestendXcmRouter::DeliveryFeeFactor` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) fn on_initialize_when_non_congested() -> Weight { // Proof Size summary in bytes: - // Measured: `154` + // Measured: `153` // Estimated: `5487` - // Minimum execution time: 8_078_000 picoseconds. - Weight::from_parts(8_455_000, 0) + // Minimum execution time: 12_993_000 picoseconds. + Weight::from_parts(13_428_000, 0) .saturating_add(Weight::from_parts(0, 5487)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -72,55 +72,9 @@ impl pallet_xcm_bridge_hub_router::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `144` // Estimated: `5487` - // Minimum execution time: 4_291_000 picoseconds. - Weight::from_parts(4_548_000, 0) + // Minimum execution time: 6_305_000 picoseconds. + Weight::from_parts(6_536_000, 0) .saturating_add(Weight::from_parts(0, 5487)) .saturating_add(T::DbWeight::get().reads(2)) } - /// Storage: `ToWestendXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToWestendXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `150` - // Estimated: `1502` - // Minimum execution time: 9_959_000 picoseconds. - Weight::from_parts(10_372_000, 0) - .saturating_add(Weight::from_parts(0, 1502)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) - /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x3302afcb67e838a3f960251b417b9a4f` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x3302afcb67e838a3f960251b417b9a4f` (r:1 w:0) - /// Storage: UNKNOWN KEY `0x0973fe64c85043ba1c965cbc38eb63c7` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x0973fe64c85043ba1c965cbc38eb63c7` (r:1 w:0) - /// Storage: `ToWestendXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToWestendXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) - /// Proof: `XcmpQueue::DeliveryFeeFactor` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) - /// Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0) - /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) - /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) - /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) - /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) - /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) - /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`) - fn send_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `448` - // Estimated: `6388` - // Minimum execution time: 45_888_000 picoseconds. - Weight::from_parts(47_022_000, 0) - .saturating_add(Weight::from_parts(0, 6388)) - .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(4)) - } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 3d6ae6ddd1d2..7478ba8893c1 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-rococo-dev"), DB CACHE: 1024 @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 34_180_000 picoseconds. - Weight::from_parts(34_745_000, 3593) + // Minimum execution time: 34_364_000 picoseconds. + Weight::from_parts(35_040_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 42_638_000 picoseconds. - Weight::from_parts(43_454_000, 6196) + // Minimum execution time: 42_755_000 picoseconds. + Weight::from_parts(43_650_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,8 +90,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `8799` - // Minimum execution time: 102_916_000 picoseconds. - Weight::from_parts(105_699_000, 8799) + // Minimum execution time: 103_037_000 picoseconds. + Weight::from_parts(105_732_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -99,8 +99,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_805_000 picoseconds. - Weight::from_parts(1_901_000, 0) + // Minimum execution time: 1_095_000 picoseconds. + Weight::from_parts(1_220_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -122,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 108_018_000 picoseconds. - Weight::from_parts(110_310_000, 6196) + // Minimum execution time: 108_117_000 picoseconds. + Weight::from_parts(110_416_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -131,8 +131,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_507_000 picoseconds. - Weight::from_parts(3_724_000, 0) + // Minimum execution time: 2_907_000 picoseconds. + Weight::from_parts(3_050_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -140,8 +140,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 26_269_000 picoseconds. - Weight::from_parts(26_706_000, 3593) + // Minimum execution time: 24_965_000 picoseconds. + Weight::from_parts(25_687_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -165,8 +165,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `6196` - // Minimum execution time: 84_759_000 picoseconds. - Weight::from_parts(86_157_000, 6196) + // Minimum execution time: 83_312_000 picoseconds. + Weight::from_parts(85_463_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -190,8 +190,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 50_876_000 picoseconds. - Weight::from_parts(51_512_000, 3610) + // Minimum execution time: 49_874_000 picoseconds. + Weight::from_parts(51_165_000, 3610) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index bee6bcdf21cf..f6a883c03e9d 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yprdrvc7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -68,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 440_298_000 picoseconds. - Weight::from_parts(446_508_000, 6196) + // Minimum execution time: 99_552_000 picoseconds. + Weight::from_parts(101_720_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -77,8 +77,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_313_000 picoseconds. - Weight::from_parts(3_422_000, 0) + // Minimum execution time: 659_000 picoseconds. + Weight::from_parts(706_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -86,58 +86,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3568` - // Minimum execution time: 9_691_000 picoseconds. - Weight::from_parts(9_948_000, 3568) + // Minimum execution time: 9_665_000 picoseconds. + Weight::from_parts(9_878_000, 3568) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_384_000 picoseconds. - Weight::from_parts(11_085_000, 0) + // Minimum execution time: 6_959_000 picoseconds. + Weight::from_parts(7_111_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_438_000 picoseconds. - Weight::from_parts(3_577_000, 0) + // Minimum execution time: 2_682_000 picoseconds. + Weight::from_parts(2_799_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_126_000 picoseconds. - Weight::from_parts(2_243_000, 0) + // Minimum execution time: 656_000 picoseconds. + Weight::from_parts(683_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_126_000 picoseconds. - Weight::from_parts(2_207_000, 0) + // Minimum execution time: 687_000 picoseconds. + Weight::from_parts(719_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_105_000 picoseconds. - Weight::from_parts(2_193_000, 0) + // Minimum execution time: 588_000 picoseconds. + Weight::from_parts(653_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_999_000 picoseconds. - Weight::from_parts(3_056_000, 0) + // Minimum execution time: 690_000 picoseconds. + Weight::from_parts(714_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_091_000 picoseconds. - Weight::from_parts(2_176_000, 0) + // Minimum execution time: 671_000 picoseconds. + Weight::from_parts(710_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 55_728_000 picoseconds. - Weight::from_parts(56_704_000, 6196) + // Minimum execution time: 67_374_000 picoseconds. + Weight::from_parts(68_899_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -170,8 +170,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `160` // Estimated: `3625` - // Minimum execution time: 12_839_000 picoseconds. - Weight::from_parts(13_457_000, 3625) + // Minimum execution time: 12_896_000 picoseconds. + Weight::from_parts(13_191_000, 3625) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +179,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_116_000 picoseconds. - Weight::from_parts(2_219_000, 0) + // Minimum execution time: 634_000 picoseconds. + Weight::from_parts(677_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -200,8 +200,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 24_891_000 picoseconds. - Weight::from_parts(25_583_000, 3610) + // Minimum execution time: 28_197_000 picoseconds. + Weight::from_parts(28_752_000, 3610) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -211,44 +211,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_968_000 picoseconds. - Weight::from_parts(4_122_000, 0) + // Minimum execution time: 2_678_000 picoseconds. + Weight::from_parts(2_803_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 136_220_000 picoseconds. - Weight::from_parts(137_194_000, 0) + // Minimum execution time: 22_806_000 picoseconds. + Weight::from_parts(23_217_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_343_000 picoseconds. - Weight::from_parts(12_635_000, 0) + // Minimum execution time: 6_221_000 picoseconds. + Weight::from_parts(6_347_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_237_000 picoseconds. - Weight::from_parts(2_315_000, 0) + // Minimum execution time: 653_000 picoseconds. + Weight::from_parts(676_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_094_000 picoseconds. - Weight::from_parts(2_231_000, 0) + // Minimum execution time: 621_000 picoseconds. + Weight::from_parts(678_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_379_000 picoseconds. - Weight::from_parts(2_455_000, 0) + // Minimum execution time: 770_000 picoseconds. + Weight::from_parts(829_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -270,8 +270,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 60_734_000 picoseconds. - Weight::from_parts(61_964_000, 6196) + // Minimum execution time: 71_654_000 picoseconds. + Weight::from_parts(73_329_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -279,8 +279,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_500_000 picoseconds. - Weight::from_parts(5_720_000, 0) + // Minimum execution time: 3_999_000 picoseconds. + Weight::from_parts(4_179_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -302,8 +302,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 55_767_000 picoseconds. - Weight::from_parts(56_790_000, 6196) + // Minimum execution time: 66_722_000 picoseconds. + Weight::from_parts(68_812_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -311,22 +311,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_201_000 picoseconds. - Weight::from_parts(2_291_000, 0) + // Minimum execution time: 718_000 picoseconds. + Weight::from_parts(745_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_164_000 picoseconds. - Weight::from_parts(2_241_000, 0) + // Minimum execution time: 623_000 picoseconds. + Weight::from_parts(682_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_127_000 picoseconds. - Weight::from_parts(2_236_000, 0) + // Minimum execution time: 664_000 picoseconds. + Weight::from_parts(696_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -334,22 +334,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1489` - // Minimum execution time: 4_275_000 picoseconds. - Weight::from_parts(4_381_000, 1489) + // Minimum execution time: 2_495_000 picoseconds. + Weight::from_parts(2_604_000, 1489) .saturating_add(T::DbWeight::get().reads(1)) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_132_000 picoseconds. - Weight::from_parts(2_216_000, 0) + // Minimum execution time: 645_000 picoseconds. + Weight::from_parts(673_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_265_000 picoseconds. - Weight::from_parts(2_332_000, 0) + // Minimum execution time: 643_000 picoseconds. + Weight::from_parts(701_000, 0) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index f263baf4bef8..fab0141e6d41 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -630,17 +630,6 @@ pub mod bridging { /// Allow any asset native to the Westend ecosystem if it comes from Westend Asset Hub. pub type WestendAssetFromAssetHubWestend = matching::RemoteAssetFromLocation, AssetHubWestend>; - - impl Contains for ToWestendXcmRouter { - fn contains(call: &RuntimeCall) -> bool { - matches!( - call, - RuntimeCall::ToWestendXcmRouter( - pallet_xcm_bridge_hub_router::Call::report_bridge_status { .. } - ) - ) - } - } } pub mod to_ethereum { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs index 83f4f9ec3dc5..6b0cf87a6f7a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -27,7 +27,7 @@ use asset_hub_rococo_runtime::{ AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - SessionKeys, ToWestendXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue, + SessionKeys, TrustBackedAssetsInstance, XcmpQueue, }; use asset_test_utils::{ test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys, @@ -1235,94 +1235,6 @@ mod asset_hub_rococo_tests { ) } - #[test] - fn report_bridge_status_from_xcm_bridge_router_for_westend_works() { - asset_test_utils::test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< - Runtime, - AllPalletsWithoutSystem, - XcmConfig, - LocationToAccountId, - ToWestendXcmRouterInstance, - >( - collator_session_keys(), - bridging_to_asset_hub_westend, - || { - vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_rococo::Call::ToWestendXcmRouter( - bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - }, - ) - .encode() - .into(), - }, - ] - .into() - }, - || { - vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_rococo::Call::ToWestendXcmRouter( - bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: false, - }, - ) - .encode() - .into(), - }, - ] - .into() - }, - ) - } - - #[test] - fn test_report_bridge_status_call_compatibility() { - // if this test fails, make sure `bp_asset_hub_rococo` has valid encoding - assert_eq!( - RuntimeCall::ToWestendXcmRouter( - pallet_xcm_bridge_hub_router::Call::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - } - ) - .encode(), - bp_asset_hub_rococo::Call::ToWestendXcmRouter( - bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - } - ) - .encode() - ); - } - - #[test] - fn check_sane_weight_report_bridge_status_for_westend() { - use pallet_xcm_bridge_hub_router::WeightInfo; - let actual = >::WeightInfo::report_bridge_status(); - let max_weight = bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(); - assert!( - actual.all_lte(max_weight), - "max_weight: {:?} should be adjusted to actual {:?}", - max_weight, - actual - ); - } - #[test] fn reserve_transfer_native_asset_to_non_teleport_para_works() { asset_test_utils::test_cases::reserve_transfer_native_asset_to_non_teleport_para_works::< diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index ebbc000d1413..97dbe7c361c1 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -45,8 +45,8 @@ use frame_support::{ traits::{ fungible, fungibles, tokens::{imbalance::ResolveAssetTo, nonfungibles_v2::Inspect}, - AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, Equals, - InstanceFilter, TransformOrigin, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, InstanceFilter, + TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, BoundedVec, PalletId, @@ -57,7 +57,6 @@ use frame_system::{ }; use pallet_asset_conversion_tx_payment::SwapAssetAdapter; use pallet_nfts::{DestroyWitness, PalletFeatures}; -use pallet_xcm::EnsureXcm; use parachains_common::{ impls::DealWithFees, message_queue::*, AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, BlockNumber, CollectionId, Hash, Header, ItemId, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, @@ -908,29 +907,18 @@ impl pallet_nfts::Config for Runtime { /// consensus with dynamic fees and back-pressure. pub type ToRococoXcmRouterInstance = pallet_xcm_bridge_hub_router::Instance1; impl pallet_xcm_bridge_hub_router::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::pallet_xcm_bridge_hub_router::WeightInfo; type UniversalLocation = xcm_config::UniversalLocation; + type SiblingBridgeHubLocation = xcm_config::bridging::SiblingBridgeHub; type BridgedNetworkId = xcm_config::bridging::to_rococo::RococoNetwork; type Bridges = xcm_config::bridging::NetworkExportTable; type DestinationVersion = PolkadotXcm; - #[cfg(not(feature = "runtime-benchmarks"))] - type BridgeHubOrigin = EnsureXcm>; - #[cfg(feature = "runtime-benchmarks")] - type BridgeHubOrigin = frame_support::traits::EitherOfDiverse< - // for running benchmarks - EnsureRoot, - // for running tests with `--feature runtime-benchmarks` - EnsureXcm>, - >; - type ToBridgeHubSender = XcmpQueue; - type WithBridgeHubChannel = - cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider< - xcm_config::bridging::SiblingBridgeHubParaId, - Runtime, - >; + type LocalXcmChannelManager = + cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider; type ByteFee = xcm_config::bridging::XcmBridgeHubRouterByteFee; type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs index 21d15c75af55..c0898012e9f3 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_bridge_hub_router` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -52,14 +52,14 @@ impl pallet_xcm_bridge_hub_router::WeightInfo for Weigh /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) - /// Storage: `ToRococoXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToRococoXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) + /// Storage: `ToRococoXcmRouter::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `ToRococoXcmRouter::DeliveryFeeFactor` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) fn on_initialize_when_non_congested() -> Weight { // Proof Size summary in bytes: - // Measured: `226` + // Measured: `225` // Estimated: `5487` - // Minimum execution time: 8_363_000 picoseconds. - Weight::from_parts(8_620_000, 0) + // Minimum execution time: 13_483_000 picoseconds. + Weight::from_parts(13_862_000, 0) .saturating_add(Weight::from_parts(0, 5487)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -72,55 +72,9 @@ impl pallet_xcm_bridge_hub_router::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `111` // Estimated: `5487` - // Minimum execution time: 3_436_000 picoseconds. - Weight::from_parts(3_586_000, 0) + // Minimum execution time: 5_078_000 picoseconds. + Weight::from_parts(5_233_000, 0) .saturating_add(Weight::from_parts(0, 5487)) .saturating_add(T::DbWeight::get().reads(2)) } - /// Storage: `ToRococoXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToRococoXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `150` - // Estimated: `1502` - // Minimum execution time: 9_706_000 picoseconds. - Weight::from_parts(10_139_000, 0) - .saturating_add(Weight::from_parts(0, 1502)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) - /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x3302afcb67e838a3f960251b417b9a4f` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x3302afcb67e838a3f960251b417b9a4f` (r:1 w:0) - /// Storage: UNKNOWN KEY `0x0973fe64c85043ba1c965cbc38eb63c7` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x0973fe64c85043ba1c965cbc38eb63c7` (r:1 w:0) - /// Storage: `ToRococoXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToRococoXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) - /// Proof: `XcmpQueue::DeliveryFeeFactor` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) - /// Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0) - /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) - /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) - /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) - /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) - /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) - /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`) - fn send_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `520` - // Estimated: `6460` - // Minimum execution time: 46_250_000 picoseconds. - Weight::from_parts(47_801_000, 0) - .saturating_add(Weight::from_parts(0, 6460)) - .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(4)) - } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index f7891aedc496..0aeae3184627 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-westend-dev"), DB CACHE: 1024 @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 32_612_000 picoseconds. - Weight::from_parts(33_359_000, 3593) + // Minimum execution time: 32_651_000 picoseconds. + Weight::from_parts(33_225_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 41_144_000 picoseconds. - Weight::from_parts(41_788_000, 6196) + // Minimum execution time: 41_059_000 picoseconds. + Weight::from_parts(41_730_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,8 +90,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `8799` - // Minimum execution time: 101_340_000 picoseconds. - Weight::from_parts(103_686_000, 8799) + // Minimum execution time: 102_780_000 picoseconds. + Weight::from_parts(105_302_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -99,8 +99,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_682_000 picoseconds. - Weight::from_parts(1_734_000, 0) + // Minimum execution time: 1_124_000 picoseconds. + Weight::from_parts(1_201_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -122,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 107_335_000 picoseconds. - Weight::from_parts(109_665_000, 6196) + // Minimum execution time: 109_024_000 picoseconds. + Weight::from_parts(111_406_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -131,8 +131,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_345_000 picoseconds. - Weight::from_parts(3_548_000, 0) + // Minimum execution time: 2_887_000 picoseconds. + Weight::from_parts(3_081_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -140,8 +140,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 25_560_000 picoseconds. - Weight::from_parts(26_779_000, 3593) + // Minimum execution time: 25_234_000 picoseconds. + Weight::from_parts(25_561_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -165,8 +165,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `6196` - // Minimum execution time: 84_453_000 picoseconds. - Weight::from_parts(86_755_000, 6196) + // Minimum execution time: 83_416_000 picoseconds. + Weight::from_parts(85_683_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -190,8 +190,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 50_463_000 picoseconds. - Weight::from_parts(51_587_000, 3610) + // Minimum execution time: 49_271_000 picoseconds. + Weight::from_parts(51_019_000, 3610) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 127bc173c103..98ecd7bd3092 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -1,24 +1,25 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-vmdtonbz-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -67,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 415_033_000 picoseconds. - Weight::from_parts(429_573_000, 6196) + // Minimum execution time: 100_823_000 picoseconds. + Weight::from_parts(103_071_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -76,8 +77,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_193_000 picoseconds. - Weight::from_parts(3_620_000, 0) + // Minimum execution time: 600_000 picoseconds. + Weight::from_parts(686_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -85,58 +86,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3568` - // Minimum execution time: 8_045_000 picoseconds. - Weight::from_parts(8_402_000, 3568) + // Minimum execution time: 8_226_000 picoseconds. + Weight::from_parts(8_650_000, 3568) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_827_000 picoseconds. - Weight::from_parts(10_454_000, 0) + // Minimum execution time: 7_131_000 picoseconds. + Weight::from_parts(7_600_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_330_000 picoseconds. - Weight::from_parts(3_677_000, 0) + // Minimum execution time: 2_589_000 picoseconds. + Weight::from_parts(2_705_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_947_000 picoseconds. - Weight::from_parts(2_083_000, 0) + // Minimum execution time: 667_000 picoseconds. + Weight::from_parts(744_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_915_000 picoseconds. - Weight::from_parts(1_993_000, 0) + // Minimum execution time: 646_000 picoseconds. + Weight::from_parts(720_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_918_000 picoseconds. - Weight::from_parts(2_048_000, 0) + // Minimum execution time: 633_000 picoseconds. + Weight::from_parts(669_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_683_000 picoseconds. - Weight::from_parts(3_064_000, 0) + // Minimum execution time: 671_000 picoseconds. + Weight::from_parts(726_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_893_000 picoseconds. - Weight::from_parts(2_159_000, 0) + // Minimum execution time: 615_000 picoseconds. + Weight::from_parts(675_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -158,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 53_116_000 picoseconds. - Weight::from_parts(54_154_000, 6196) + // Minimum execution time: 67_236_000 picoseconds. + Weight::from_parts(69_899_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -169,8 +170,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `160` // Estimated: `3625` - // Minimum execution time: 12_381_000 picoseconds. - Weight::from_parts(12_693_000, 3625) + // Minimum execution time: 12_976_000 picoseconds. + Weight::from_parts(13_357_000, 3625) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -178,8 +179,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_933_000 picoseconds. - Weight::from_parts(1_983_000, 0) + // Minimum execution time: 633_000 picoseconds. + Weight::from_parts(685_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -199,8 +200,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 24_251_000 picoseconds. - Weight::from_parts(24_890_000, 3610) + // Minimum execution time: 28_707_000 picoseconds. + Weight::from_parts(31_790_000, 3610) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -210,44 +211,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_850_000 picoseconds. - Weight::from_parts(4_082_000, 0) + // Minimum execution time: 2_670_000 picoseconds. + Weight::from_parts(2_833_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 112_248_000 picoseconds. - Weight::from_parts(124_454_000, 0) + // Minimum execution time: 23_459_000 picoseconds. + Weight::from_parts(23_817_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_457_000 picoseconds. - Weight::from_parts(12_060_000, 0) + // Minimum execution time: 6_197_000 picoseconds. + Weight::from_parts(6_338_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_959_000 picoseconds. - Weight::from_parts(2_076_000, 0) + // Minimum execution time: 671_000 picoseconds. + Weight::from_parts(715_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_920_000 picoseconds. - Weight::from_parts(1_994_000, 0) + // Minimum execution time: 655_000 picoseconds. + Weight::from_parts(694_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_149_000 picoseconds. - Weight::from_parts(2_394_000, 0) + // Minimum execution time: 810_000 picoseconds. + Weight::from_parts(858_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -269,8 +270,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 58_011_000 picoseconds. - Weight::from_parts(59_306_000, 6196) + // Minimum execution time: 73_136_000 picoseconds. + Weight::from_parts(75_314_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -278,8 +279,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_031_000 picoseconds. - Weight::from_parts(5_243_000, 0) + // Minimum execution time: 4_515_000 picoseconds. + Weight::from_parts(4_768_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -301,8 +302,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 53_078_000 picoseconds. - Weight::from_parts(54_345_000, 6196) + // Minimum execution time: 68_072_000 picoseconds. + Weight::from_parts(69_866_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -310,22 +311,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_936_000 picoseconds. - Weight::from_parts(2_002_000, 0) + // Minimum execution time: 696_000 picoseconds. + Weight::from_parts(736_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_855_000 picoseconds. - Weight::from_parts(1_950_000, 0) + // Minimum execution time: 618_000 picoseconds. + Weight::from_parts(681_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_882_000 picoseconds. - Weight::from_parts(1_977_000, 0) + // Minimum execution time: 647_000 picoseconds. + Weight::from_parts(672_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -333,22 +334,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1489` - // Minimum execution time: 3_912_000 picoseconds. - Weight::from_parts(4_167_000, 1489) + // Minimum execution time: 2_496_000 picoseconds. + Weight::from_parts(2_617_000, 1489) .saturating_add(T::DbWeight::get().reads(1)) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_911_000 picoseconds. - Weight::from_parts(1_971_000, 0) + // Minimum execution time: 637_000 picoseconds. + Weight::from_parts(675_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_990_000 picoseconds. - Weight::from_parts(2_076_000, 0) + // Minimum execution time: 607_000 picoseconds. + Weight::from_parts(683_000, 0) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index bc5d07f552b0..cc96bd59cb48 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -651,17 +651,6 @@ pub mod bridging { (StartsWith, StartsWith), AssetHubRococo, >; - - impl Contains for ToRococoXcmRouter { - fn contains(call: &RuntimeCall) -> bool { - matches!( - call, - RuntimeCall::ToRococoXcmRouter( - pallet_xcm_bridge_hub_router::Call::report_bridge_status { .. } - ) - ) - } - } } pub mod to_ethereum { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 1c334d6f84f8..ad3c450eb375 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -28,7 +28,7 @@ use asset_hub_westend_runtime::{ AllPalletsWithoutSystem, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, - ToRococoXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue, + TrustBackedAssetsInstance, XcmpQueue, }; pub use asset_hub_westend_runtime::{AssetConversion, AssetDeposit, CollatorSelection, System}; use asset_test_utils::{ @@ -1242,92 +1242,6 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic ) } -#[test] -fn report_bridge_status_from_xcm_bridge_router_for_rococo_works() { - asset_test_utils::test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< - Runtime, - AllPalletsWithoutSystem, - XcmConfig, - LocationToAccountId, - ToRococoXcmRouterInstance, - >( - collator_session_keys(), - bridging_to_asset_hub_rococo, - || { - vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_westend::Call::ToRococoXcmRouter( - bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - }, - ) - .encode() - .into(), - }, - ] - .into() - }, - || { - vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_westend::Call::ToRococoXcmRouter( - bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: false, - }, - ) - .encode() - .into(), - }, - ] - .into() - }, - ) -} - -#[test] -fn test_report_bridge_status_call_compatibility() { - // if this test fails, make sure `bp_asset_hub_rococo` has valid encoding - assert_eq!( - RuntimeCall::ToRococoXcmRouter(pallet_xcm_bridge_hub_router::Call::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - }) - .encode(), - bp_asset_hub_westend::Call::ToRococoXcmRouter( - bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - } - ) - .encode() - ) -} - -#[test] -fn check_sane_weight_report_bridge_status() { - use pallet_xcm_bridge_hub_router::WeightInfo; - let actual = >::WeightInfo::report_bridge_status(); - let max_weight = bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(); - assert!( - actual.all_lte(max_weight), - "max_weight: {:?} should be adjusted to actual {:?}", - max_weight, - actual - ); -} - #[test] fn change_xcm_bridge_hub_router_byte_fee_by_governance_works() { asset_test_utils::test_cases::change_storage_constant_by_governance_works::< diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs index e0b3f70c7546..d86761174740 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs @@ -542,14 +542,19 @@ pub fn report_bridge_status_from_xcm_bridge_router_works< // execute xcm as XcmpQueue would do let outcome = XcmExecutor::::prepare_and_execute( - local_bridge_hub_location, + local_bridge_hub_location.clone(), xcm, &mut hash, - RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + RuntimeHelper::::xcm_max_weight( + XcmReceivedFrom::Sibling, + ), Weight::zero(), ); assert_ok!(outcome.ensure_complete()); - assert_eq!(is_congested, pallet_xcm_bridge_hub_router::Pallet::::bridge().is_congested); + assert_eq!( + is_congested, + <>::LocalXcmChannelManager as pallet_xcm_bridge_hub_router::XcmChannelStatusProvider>::is_congested(&local_bridge_hub_location) + ); }; report_bridge_status(true); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 6d0fbd7d5c66..9fa1f3b1602c 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -97,7 +97,7 @@ bp-parachains = { workspace = true } bp-polkadot-bulletin = { workspace = true } bp-polkadot-core = { workspace = true } bp-relayers = { workspace = true } -bp-runtime = { features = ["test-helpers"], workspace = true } +bp-runtime = { workspace = true } bp-rococo = { workspace = true } bp-westend = { workspace = true } pallet-bridge-grandpa = { workspace = true } @@ -123,9 +123,8 @@ bridge-hub-common = { workspace = true } [dev-dependencies] bridge-hub-test-utils = { workspace = true, default-features = true } -bridge-runtime-common = { features = [ - "integrity-test", -], workspace = true, default-features = true } +bridge-runtime-common = { features = ["integrity-test"], workspace = true, default-features = true } +pallet-bridge-relayers = { features = ["integrity-test"], workspace = true } sp-keyring = { workspace = true, default-features = true } snowbridge-runtime-test-common = { workspace = true, default-features = true } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs index 83b92918dc4f..00d902486c85 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -20,31 +20,33 @@ //! are reusing Polkadot Bulletin chain primitives everywhere here. use crate::{ - weights, xcm_config::UniversalLocation, BridgeRococoBulletinGrandpa, - BridgeRococoBulletinMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverRococoBulletin, - XcmRouter, + weights, xcm_config::UniversalLocation, AccountId, Balance, Balances, + BridgeRococoBulletinGrandpa, BridgeRococoBulletinMessages, PolkadotXcm, Runtime, RuntimeEvent, + RuntimeHoldReason, XcmOverRococoBulletin, XcmRouter, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, LaneId, -}; -use bridge_runtime_common::{ - extensions::refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter, - RefundableMessagesLane, - }, - messages_xcm_extension::{ - SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, - XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, - }, + target_chain::FromBridgedChainMessagesProof, }; +use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge; -use frame_support::{parameter_types, traits::PalletInfoAccess}; +use frame_support::{ + parameter_types, + traits::{Equals, PalletInfoAccess}, +}; +use frame_system::EnsureRoot; +use pallet_bridge_relayers::extension::{ + BridgeRelayersSignedExtension, WithMessagesExtensionConfig, +}; +use pallet_xcm::EnsureXcm; +use pallet_xcm_bridge_hub::XcmAsPlainPayload; +use polkadot_parachain_primitives::primitives::Sibling; +use testnet_parachains_constants::rococo::currency::UNITS as ROC; use xcm::{ latest::prelude::*, prelude::{InteriorLocation, NetworkId}, }; -use xcm_builder::BridgeBlobDispatcher; +use xcm_builder::{BridgeBlobDispatcher, ParentIsPreset, SiblingParachainConvertsVia}; parameter_types! { /// Interior location (relative to this runtime) of the with-RococoBulletin messages pallet. @@ -58,12 +60,6 @@ parameter_types! { 2, [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get())] ); - /// All active lanes that the current bridge supports. - pub ActiveOutboundLanesToRococoBulletin: &'static [bp_messages::LaneId] - = &[XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN]; - /// Lane identifier, used to connect Rococo People and Rococo Bulletin chain. - pub const RococoPeopleToRococoBulletinMessagesLane: bp_messages::LaneId - = XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN; // see the `FEE_BOOST_PER_RELAY_HEADER` constant get the meaning of this value pub PriorityBoostPerRelayHeader: u64 = 58_014_163_614_163; @@ -75,26 +71,11 @@ parameter_types! { /// meaning of this value. pub PriorityBoostPerMessage: u64 = 182_044_444_444_444; - /// Identifier of the sibling Rococo People parachain. - pub RococoPeopleParaId: cumulus_primitives_core::ParaId = rococo_runtime_constants::system_parachain::PEOPLE_ID.into(); - /// A route (XCM location and bridge lane) that the Rococo People Chain -> Rococo Bulletin Chain - /// message is following. - pub FromRococoPeopleToRococoBulletinRoute: SenderAndLane = SenderAndLane::new( - ParentThen(Parachain(RococoPeopleParaId::get().into()).into()).into(), - XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, - ); - /// All active routes and their destinations. - pub ActiveLanes: alloc::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = alloc::vec![ - ( - FromRococoPeopleToRococoBulletinRoute::get(), - (RococoBulletinGlobalConsensusNetwork::get(), Here) - ) - ]; + /// PeopleRococo location + pub PeopleRococoLocation: Location = Location::new(1, [Parachain(rococo_runtime_constants::system_parachain::PEOPLE_ID)]); - /// XCM message that is never sent. - pub NeverSentMessage: Option> = None; + pub storage BridgeDeposit: Balance = 5 * ROC; } -pub const XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN: LaneId = LaneId([0, 0, 0, 0]); /// Proof of messages, coming from Rococo Bulletin chain. pub type FromRococoBulletinMessagesProof = @@ -110,34 +91,15 @@ type FromRococoBulletinMessageBlobDispatcher = BridgeBlobDispatcher< BridgeRococoToRococoBulletinMessagesPalletInstance, >; -/// Export XCM messages to be relayed to the other side -pub type ToRococoBulletinHaulBlobExporter = XcmOverRococoBulletin; - -pub struct ToRococoBulletinXcmBlobHauler; -impl XcmBlobHauler for ToRococoBulletinXcmBlobHauler { - type Runtime = Runtime; - type MessagesInstance = WithRococoBulletinMessagesInstance; - type ToSourceChainSender = XcmRouter; - type CongestedMessage = NeverSentMessage; - type UncongestedMessage = NeverSentMessage; -} - -/// On messages delivered callback. -type OnMessagesDeliveredFromRococoBulletin = - XcmBlobHaulerAdapter; - /// Signed extension that refunds relayers that are delivering messages from the Rococo Bulletin /// chain. -pub type OnBridgeHubRococoRefundRococoBulletinMessages = RefundSignedExtensionAdapter< - RefundBridgedMessages< +pub type OnBridgeHubRococoRefundRococoBulletinMessages = BridgeRelayersSignedExtension< + Runtime, + WithMessagesExtensionConfig< + StrOnBridgeHubRococoRefundRococoBulletinMessages, Runtime, - RefundableMessagesLane< - WithRococoBulletinMessagesInstance, - RococoPeopleToRococoBulletinMessagesLane, - >, - ActualFeeRefund, + WithRococoBulletinMessagesInstance, PriorityBoostPerMessage, - StrOnBridgeHubRococoRefundRococoBulletinMessages, >, >; bp_runtime::generate_static_str_provider!(OnBridgeHubRococoRefundRococoBulletinMessages); @@ -153,8 +115,6 @@ impl pallet_bridge_messages::Config for Runt type BridgedChain = bp_polkadot_bulletin::PolkadotBulletin; type BridgedHeaderChain = BridgeRococoBulletinGrandpa; - type ActiveOutboundLanes = ActiveOutboundLanesToRococoBulletin; - type OutboundPayload = XcmAsPlainPayload; type InboundPayload = XcmAsPlainPayload; @@ -162,22 +122,38 @@ impl pallet_bridge_messages::Config for Runt type DeliveryConfirmationPayments = (); - type MessageDispatch = - XcmBlobMessageDispatch; - type OnMessagesDelivered = OnMessagesDeliveredFromRococoBulletin; + type MessageDispatch = XcmOverRococoBulletin; + type OnMessagesDelivered = XcmOverRococoBulletin; } /// Add support for the export and dispatch of XCM programs. pub type XcmOverPolkadotBulletinInstance = pallet_xcm_bridge_hub::Instance2; impl pallet_xcm_bridge_hub::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; type BridgedNetwork = RococoBulletinGlobalConsensusNetworkLocation; type BridgeMessagesPalletInstance = WithRococoBulletinMessagesInstance; + type MessageExportPrice = (); type DestinationVersion = XcmVersionOfDestAndRemoteBridge; - type Lanes = ActiveLanes; - type LanesSupport = ToRococoBulletinXcmBlobHauler; + + type AdminOrigin = EnsureRoot; + // Only allow calls from sibling People parachain to directly open the bridge. + type OpenBridgeOrigin = EnsureXcm>; + // Converter aligned with `OpenBridgeOrigin`. + type BridgeOriginAccountIdConverter = + (ParentIsPreset, SiblingParachainConvertsVia); + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + // Do not require deposit from People parachains. + type AllowWithoutBridgeDeposit = Equals; + + type LocalXcmChannelManager = (); + type BlobDispatcher = FromRococoBulletinMessageBlobDispatcher; } #[cfg(test)] @@ -232,13 +208,13 @@ mod tests { // Bulletin chain - it has the same (almost) runtime for Polkadot Bulletin and Rococo // Bulletin, so we have to adhere Polkadot names here - bridge_runtime_common::extensions::priority_calculator::per_relay_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_relay_header::ensure_priority_boost_is_sane::< Runtime, BridgeGrandpaRococoBulletinInstance, PriorityBoostPerRelayHeader, >(FEE_BOOST_PER_RELAY_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_message::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_message::ensure_priority_boost_is_sane::< Runtime, WithRococoBulletinMessagesInstance, PriorityBoostPerMessage, @@ -252,3 +228,73 @@ mod tests { assert_eq!(BridgeRococoToRococoBulletinMessagesPalletInstance::get(), expected,); } } + +#[cfg(feature = "runtime-benchmarks")] +pub(crate) fn open_bridge_for_benchmarks( + with: bp_messages::LaneId, + sibling_para_id: u32, +) -> InteriorLocation { + use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; + use sp_runtime::traits::Zero; + use xcm::VersionedInteriorLocation; + use xcm_executor::traits::ConvertLocation; + + // insert bridge metadata + let lane_id = with; + let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); + let universal_source = [GlobalConsensus(Rococo), Parachain(sibling_para_id)].into(); + let universal_destination = + [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get()), Parachain(2075)].into(); + let bridge_id = BridgeId::new(&universal_source, &universal_destination); + + // insert only bridge metadata, because the benchmarks create lanes + pallet_xcm_bridge_hub::Bridges::::insert( + bridge_id, + Bridge { + bridge_origin_relative_location: alloc::boxed::Box::new( + sibling_parachain.clone().into(), + ), + bridge_origin_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_source.clone()), + ), + bridge_destination_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_destination), + ), + state: BridgeState::Opened, + bridge_owner_account: crate::xcm_config::LocationToAccountId::convert_location( + &sibling_parachain, + ) + .expect("valid AccountId"), + deposit: Balance::zero(), + lane_id, + }, + ); + pallet_xcm_bridge_hub::LaneToBridge::::insert( + lane_id, bridge_id, + ); + + universal_source +} + +/// Contains the migration for the PeopleRococo<>RococoBulletin bridge. +pub mod migration { + use super::*; + use bp_messages::LaneId; + use frame_support::traits::ConstBool; + use sp_runtime::Either; + + parameter_types! { + pub RococoPeopleToRococoBulletinMessagesLane: LaneId = LaneId::from_inner(Either::Right([0, 0, 0, 0])); + pub BulletinRococoLocation: InteriorLocation = [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get())].into(); + } + + /// Ensure that the existing lanes for the People<>Bulletin bridge are correctly configured. + pub type StaticToDynamicLanes = pallet_xcm_bridge_hub::migration::OpenBridgeForLane< + Runtime, + XcmOverPolkadotBulletinInstance, + RococoPeopleToRococoBulletinMessagesLane, + ConstBool, + PeopleRococoLocation, + BulletinRococoLocation, + >; +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index 9880e8a17c2b..fc52413a909f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -20,30 +20,32 @@ use crate::{ bridge_common_config::{BridgeParachainWestendInstance, DeliveryRewardInBalance}, weights, xcm_config::UniversalLocation, - BridgeWestendMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverBridgeHubWestend, XcmRouter, + AccountId, Balance, Balances, BridgeWestendMessages, PolkadotXcm, Runtime, RuntimeEvent, + RuntimeHoldReason, XcmOverBridgeHubWestend, XcmRouter, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, LaneId, -}; -use bridge_runtime_common::{ - extensions::refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter, - RefundableMessagesLane, - }, - messages_xcm_extension::{ - SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, - XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, - }, + target_chain::FromBridgedChainMessagesProof, }; +use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge; +use pallet_xcm_bridge_hub::XcmAsPlainPayload; -use codec::Encode; use frame_support::{parameter_types, traits::PalletInfoAccess}; +use frame_system::EnsureRoot; +use pallet_bridge_relayers::extension::{ + BridgeRelayersSignedExtension, WithMessagesExtensionConfig, +}; +use pallet_xcm::EnsureXcm; +use parachains_common::xcm_config::{ + AllSiblingSystemParachains, ParentRelayOrSiblingParachains, RelayOrOtherSystemParachains, +}; +use polkadot_parachain_primitives::primitives::Sibling; +use testnet_parachains_constants::rococo::currency::UNITS as ROC; use xcm::{ latest::prelude::*, prelude::{InteriorLocation, NetworkId}, }; -use xcm_builder::BridgeBlobDispatcher; +use xcm_builder::{BridgeBlobDispatcher, ParentIsPreset, SiblingParachainConvertsVia}; parameter_types! { pub BridgeRococoToWestendMessagesPalletInstance: InteriorLocation = [PalletInstance(::index() as u8)].into(); @@ -59,26 +61,6 @@ parameter_types! { // see the `FEE_BOOST_PER_MESSAGE` constant to get the meaning of this value pub PriorityBoostPerMessage: u64 = 182_044_444_444_444; - pub AssetHubRococoParaId: cumulus_primitives_core::ParaId = bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID.into(); - pub AssetHubWestendParaId: cumulus_primitives_core::ParaId = bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID.into(); - - // Lanes - pub ActiveOutboundLanesToBridgeHubWestend: &'static [bp_messages::LaneId] = &[XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND]; - pub const AssetHubRococoToAssetHubWestendMessagesLane: bp_messages::LaneId = XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND; - pub FromAssetHubRococoToAssetHubWestendRoute: SenderAndLane = SenderAndLane::new( - ParentThen([Parachain(AssetHubRococoParaId::get().into())].into()).into(), - XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, - ); - pub ActiveLanes: alloc::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = alloc::vec![ - ( - FromAssetHubRococoToAssetHubWestendRoute::get(), - (WestendGlobalConsensusNetwork::get(), [Parachain(AssetHubWestendParaId::get().into())].into()) - ) - ]; - - pub CongestedMessage: Xcm<()> = build_congestion_message(true).into(); - pub UncongestedMessage: Xcm<()> = build_congestion_message(false).into(); - pub BridgeHubWestendLocation: Location = Location::new( 2, [ @@ -86,26 +68,8 @@ parameter_types! { Parachain(::PARACHAIN_ID) ] ); -} -pub const XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND: LaneId = LaneId([0, 0, 0, 2]); - -fn build_congestion_message(is_congested: bool) -> alloc::vec::Vec> { - alloc::vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_rococo::Call::ToWestendXcmRouter( - bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested, - } - ) - .encode() - .into(), - } - ] + + pub storage BridgeDeposit: Balance = 5 * ROC; } /// Proof of messages, coming from Westend. @@ -119,33 +83,14 @@ pub type ToWestendBridgeHubMessagesDeliveryProof = type FromWestendMessageBlobDispatcher = BridgeBlobDispatcher; -/// Export XCM messages to be relayed to the other side -pub type ToBridgeHubWestendHaulBlobExporter = XcmOverBridgeHubWestend; - -pub struct ToBridgeHubWestendXcmBlobHauler; -impl XcmBlobHauler for ToBridgeHubWestendXcmBlobHauler { - type Runtime = Runtime; - type MessagesInstance = WithBridgeHubWestendMessagesInstance; - type ToSourceChainSender = XcmRouter; - type CongestedMessage = CongestedMessage; - type UncongestedMessage = UncongestedMessage; -} - -/// On messages delivered callback. -type OnMessagesDeliveredFromWestend = - XcmBlobHaulerAdapter; - /// Signed extension that refunds relayers that are delivering messages from the Westend parachain. -pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = RefundSignedExtensionAdapter< - RefundBridgedMessages< +pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = BridgeRelayersSignedExtension< + Runtime, + WithMessagesExtensionConfig< + StrOnBridgeHubRococoRefundBridgeHubWestendMessages, Runtime, - RefundableMessagesLane< - WithBridgeHubWestendMessagesInstance, - AssetHubRococoToAssetHubWestendMessagesLane, - >, - ActualFeeRefund, + WithBridgeHubWestendMessagesInstance, PriorityBoostPerMessage, - StrOnBridgeHubRococoRefundBridgeHubWestendMessages, >, >; bp_runtime::generate_static_str_provider!(OnBridgeHubRococoRefundBridgeHubWestendMessages); @@ -164,8 +109,6 @@ impl pallet_bridge_messages::Config for Ru bp_bridge_hub_westend::BridgeHubWestend, >; - type ActiveOutboundLanes = ActiveOutboundLanesToBridgeHubWestend; - type OutboundPayload = XcmAsPlainPayload; type InboundPayload = XcmAsPlainPayload; @@ -177,28 +120,86 @@ impl pallet_bridge_messages::Config for Ru DeliveryRewardInBalance, >; - type MessageDispatch = XcmBlobMessageDispatch< - FromWestendMessageBlobDispatcher, - Self::WeightInfo, - cumulus_pallet_xcmp_queue::bridging::OutXcmpChannelStatusProvider< - AssetHubRococoParaId, - Runtime, - >, - >; - type OnMessagesDelivered = OnMessagesDeliveredFromWestend; + type MessageDispatch = XcmOverBridgeHubWestend; + type OnMessagesDelivered = XcmOverBridgeHubWestend; } /// Add support for the export and dispatch of XCM programs. pub type XcmOverBridgeHubWestendInstance = pallet_xcm_bridge_hub::Instance1; impl pallet_xcm_bridge_hub::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; type BridgedNetwork = WestendGlobalConsensusNetworkLocation; type BridgeMessagesPalletInstance = WithBridgeHubWestendMessagesInstance; + type MessageExportPrice = (); type DestinationVersion = XcmVersionOfDestAndRemoteBridge; - type Lanes = ActiveLanes; - type LanesSupport = ToBridgeHubWestendXcmBlobHauler; + + type AdminOrigin = EnsureRoot; + // Only allow calls from relay chains and sibling parachains to directly open the bridge. + type OpenBridgeOrigin = EnsureXcm; + // Converter aligned with `OpenBridgeOrigin`. + type BridgeOriginAccountIdConverter = + (ParentIsPreset, SiblingParachainConvertsVia); + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + // Do not require deposit from system parachains or relay chain + type AllowWithoutBridgeDeposit = + RelayOrOtherSystemParachains; + + // TODO:(bridges-v2) - add `LocalXcmChannelManager` impl - https://github.com/paritytech/parity-bridges-common/issues/3047 + type LocalXcmChannelManager = (); + type BlobDispatcher = FromWestendMessageBlobDispatcher; +} + +#[cfg(feature = "runtime-benchmarks")] +pub(crate) fn open_bridge_for_benchmarks( + with: bp_messages::LaneId, + sibling_para_id: u32, +) -> InteriorLocation { + use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; + use sp_runtime::traits::Zero; + use xcm::VersionedInteriorLocation; + use xcm_executor::traits::ConvertLocation; + + // insert bridge metadata + let lane_id = with; + let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); + let universal_source = [GlobalConsensus(Rococo), Parachain(sibling_para_id)].into(); + let universal_destination = [GlobalConsensus(Westend), Parachain(2075)].into(); + let bridge_id = BridgeId::new(&universal_source, &universal_destination); + + // insert only bridge metadata, because the benchmarks create lanes + pallet_xcm_bridge_hub::Bridges::::insert( + bridge_id, + Bridge { + bridge_origin_relative_location: alloc::boxed::Box::new( + sibling_parachain.clone().into(), + ), + bridge_origin_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_source.clone()), + ), + bridge_destination_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_destination), + ), + state: BridgeState::Opened, + bridge_owner_account: crate::xcm_config::LocationToAccountId::convert_location( + &sibling_parachain, + ) + .expect("valid AccountId"), + deposit: Balance::zero(), + lane_id, + }, + ); + pallet_xcm_bridge_hub::LaneToBridge::::insert( + lane_id, bridge_id, + ); + + universal_source } #[cfg(test)] @@ -207,14 +208,11 @@ mod tests { use crate::bridge_common_config::BridgeGrandpaWestendInstance; use bridge_runtime_common::{ assert_complete_bridge_types, - extensions::refund_relayer_extension::RefundableParachain, integrity::{ assert_complete_with_parachain_bridge_constants, check_message_lane_weights, AssertChainConstants, AssertCompleteBridgeConstants, }, }; - use parachains_common::Balance; - use testnet_parachains_constants::rococo; /// Every additional message in the message delivery transaction boosts its priority. /// So the priority of transaction with `N+1` messages is larger than priority of @@ -225,12 +223,12 @@ mod tests { /// /// We want this tip to be large enough (delivery transactions with more messages = less /// operational costs and a faster bridge), so this value should be significant. - const FEE_BOOST_PER_MESSAGE: Balance = 2 * rococo::currency::UNITS; + const FEE_BOOST_PER_MESSAGE: Balance = 2 * ROC; // see `FEE_BOOST_PER_MESSAGE` comment - const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * rococo::currency::UNITS; + const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * ROC; // see `FEE_BOOST_PER_MESSAGE` comment - const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * rococo::currency::UNITS; + const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * ROC; #[test] fn ensure_bridge_hub_rococo_message_lane_weights_are_correct() { @@ -268,19 +266,20 @@ mod tests { }, }); - bridge_runtime_common::extensions::priority_calculator::per_relay_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_relay_header::ensure_priority_boost_is_sane::< Runtime, BridgeGrandpaWestendInstance, PriorityBoostPerRelayHeader, >(FEE_BOOST_PER_RELAY_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_parachain_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_parachain_header::ensure_priority_boost_is_sane::< Runtime, - RefundableParachain, + WithBridgeHubWestendMessagesInstance, + bp_bridge_hub_westend::BridgeHubWestend, PriorityBoostPerParachainHeader, >(FEE_BOOST_PER_PARACHAIN_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_message::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_message::ensure_priority_boost_is_sane::< Runtime, WithBridgeHubWestendMessagesInstance, PriorityBoostPerMessage, @@ -294,3 +293,29 @@ mod tests { assert_eq!(BridgeRococoToWestendMessagesPalletInstance::get(), expected,); } } + +/// Contains the migration for the AssetHubRococo<>AssetHubWestend bridge. +pub mod migration { + use super::*; + use bp_messages::LaneId; + use frame_support::traits::ConstBool; + use sp_runtime::Either; + + parameter_types! { + pub AssetHubRococoToAssetHubWestendMessagesLane: LaneId = LaneId::from_inner(Either::Right([0, 0, 0, 2])); + pub AssetHubRococoLocation: Location = Location::new(1, [Parachain(bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID)]); + pub AssetHubWestendUniversalLocation: InteriorLocation = [GlobalConsensus(WestendGlobalConsensusNetwork::get()), Parachain(bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID)].into(); + } + + /// Ensure that the existing lanes for the AHR<>AHW bridge are correctly configured. + pub type StaticToDynamicLanes = pallet_xcm_bridge_hub::migration::OpenBridgeForLane< + Runtime, + XcmOverBridgeHubWestendInstance, + AssetHubRococoToAssetHubWestendMessagesLane, + // the lanes are already created for AHR<>AHW, but we need to link them to the bridge + // structs + ConstBool, + AssetHubRococoLocation, + AssetHubWestendUniversalLocation, + >; +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index c65880771e08..100bff5a0705 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -39,10 +39,7 @@ extern crate alloc; use alloc::{vec, vec::Vec}; use bridge_runtime_common::extensions::{ - check_obsolete_extension::{ - CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, - }, - refund_relayer_extension::RefundableParachain, + CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use snowbridge_beacon_primitives::{Fork, ForkVersions}; @@ -165,10 +162,36 @@ pub type Migrations = ( ConstU32, ConstU32, >, + pallet_bridge_messages::migration::v1::MigrationToV1< + Runtime, + bridge_to_westend_config::WithBridgeHubWestendMessagesInstance, + >, + pallet_bridge_messages::migration::v1::MigrationToV1< + Runtime, + bridge_to_bulletin_config::WithRococoBulletinMessagesInstance, + >, + bridge_to_westend_config::migration::StaticToDynamicLanes, + bridge_to_bulletin_config::migration::StaticToDynamicLanes, + frame_support::migrations::RemoveStorage< + BridgeWestendMessagesPalletName, + OutboundLanesCongestedSignalsKey, + RocksDbWeight, + >, + frame_support::migrations::RemoveStorage< + BridgePolkadotBulletinMessagesPalletName, + OutboundLanesCongestedSignalsKey, + RocksDbWeight, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); +parameter_types! { + pub const BridgeWestendMessagesPalletName: &'static str = "BridgeWestendMessages"; + pub const BridgePolkadotBulletinMessagesPalletName: &'static str = "BridgePolkadotBulletinMessages"; + pub const OutboundLanesCongestedSignalsKey: &'static str = "OutboundLanesCongestedSignals"; +} + /// Migration to initialize storage versions for pallets added after genesis. /// /// Ideally this would be done automatically (see @@ -780,10 +803,8 @@ bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages! { // Parachains CheckAndBoostBridgeParachainsTransactions< Runtime, - RefundableParachain< bridge_common_config::BridgeParachainWestendInstance, - bp_bridge_hub_westend::BridgeHubWestend, - >, + bp_bridge_hub_westend::BridgeHubWestend, bridge_to_westend_config::PriorityBoostPerParachainHeader, xcm_config::TreasuryAccount, >, @@ -1379,11 +1400,41 @@ impl_runtime_apis! { ); BenchmarkError::Stop("XcmVersion was not stored!") })?; + + let sibling_parachain_location = Location::new(1, [Parachain(5678)]); + + // fund SA + use frame_support::traits::fungible::Mutate; + use xcm_executor::traits::ConvertLocation; + frame_support::assert_ok!( + Balances::mint_into( + &xcm_config::LocationToAccountId::convert_location(&sibling_parachain_location).expect("valid AccountId"), + bridge_to_westend_config::BridgeDeposit::get() + .saturating_add(ExistentialDeposit::get()) + .saturating_add(UNITS * 5) + ) + ); + + // open bridge + let origin = RuntimeOrigin::from(pallet_xcm::Origin::Xcm(sibling_parachain_location.clone())); + XcmOverBridgeHubWestend::open_bridge( + origin.clone(), + Box::new(VersionedInteriorLocation::from([GlobalConsensus(NetworkId::Westend), Parachain(8765)])), + ).map_err(|e| { + log::error!( + "Failed to `XcmOverBridgeHubWestend::open_bridge`({:?}, {:?})`, error: {:?}", + origin, + [GlobalConsensus(NetworkId::Westend), Parachain(8765)], + e + ); + BenchmarkError::Stop("Bridge was not opened!") + })?; + Ok( ( - bridge_to_westend_config::FromAssetHubRococoToAssetHubWestendRoute::get().location, + sibling_parachain_location, NetworkId::Westend, - [Parachain(bridge_to_westend_config::AssetHubWestendParaId::get().into())].into() + [Parachain(8765)].into() ) ) } @@ -1435,16 +1486,18 @@ impl_runtime_apis! { use cumulus_primitives_core::XcmpMessageSource; assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into()); + let universal_source = bridge_to_westend_config::open_bridge_for_benchmarks(params.lane, 42); prepare_message_proof_from_parachain::< Runtime, bridge_common_config::BridgeGrandpaWestendInstance, bridge_to_westend_config::WithBridgeHubWestendMessagesInstance, - >(params, generate_xcm_builder_bridge_message_sample([GlobalConsensus(Rococo), Parachain(42)].into())) + >(params, generate_xcm_builder_bridge_message_sample(universal_source)) } fn prepare_message_delivery_proof( params: MessageDeliveryProofParams, ) -> bridge_to_westend_config::ToWestendBridgeHubMessagesDeliveryProof { + let _ = bridge_to_westend_config::open_bridge_for_benchmarks(params.lane, 42); prepare_message_delivery_proof_from_parachain::< Runtime, bridge_common_config::BridgeGrandpaWestendInstance, @@ -1470,16 +1523,18 @@ impl_runtime_apis! { use cumulus_primitives_core::XcmpMessageSource; assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into()); + let universal_source = bridge_to_bulletin_config::open_bridge_for_benchmarks(params.lane, 42); prepare_message_proof_from_grandpa_chain::< Runtime, bridge_common_config::BridgeGrandpaRococoBulletinInstance, bridge_to_bulletin_config::WithRococoBulletinMessagesInstance, - >(params, generate_xcm_builder_bridge_message_sample([GlobalConsensus(Rococo), Parachain(42)].into())) + >(params, generate_xcm_builder_bridge_message_sample(universal_source)) } fn prepare_message_delivery_proof( params: MessageDeliveryProofParams, ) -> bridge_to_bulletin_config::ToRococoBulletinMessagesDeliveryProof { + let _ = bridge_to_bulletin_config::open_bridge_for_benchmarks(params.lane, 42); prepare_message_delivery_proof_from_grandpa_chain::< Runtime, bridge_common_config::BridgeGrandpaRococoBulletinInstance, @@ -1511,8 +1566,8 @@ impl_runtime_apis! { parachain_head_size: u32, proof_params: bp_runtime::UnverifiedStorageProofParams, ) -> ( - pallet_bridge_parachains::RelayBlockNumber, - pallet_bridge_parachains::RelayBlockHash, + bp_parachains::RelayBlockNumber, + bp_parachains::RelayBlockHash, bp_polkadot_core::parachains::ParaHeadsProof, Vec<(bp_polkadot_core::parachains::ParaId, bp_polkadot_core::parachains::ParaHash)>, ) { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa.rs index 4ce57b2e5016..a9cc2411a9c6 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_grandpa` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yaoqqom-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -68,13 +68,13 @@ impl pallet_bridge_grandpa::WeightInfo for WeightInfo pallet_bridge_grandpa::WeightInfo for WeightInfo pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `654` - // Estimated: `52645` - // Minimum execution time: 36_836_000 picoseconds. - Weight::from_parts(37_858_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `933` + // Estimated: `52674` + // Minimum execution time: 61_893_000 picoseconds. + Weight::from_parts(63_358_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -71,21 +75,25 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4076]`. /// The range of component `n` is `[1, 4076]`. fn receive_n_messages_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `654` - // Estimated: `52645` - // Minimum execution time: 36_587_000 picoseconds. - Weight::from_parts(37_516_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 8_655 - .saturating_add(Weight::from_parts(11_649_169, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `933` + // Estimated: `52674` + // Minimum execution time: 61_612_000 picoseconds. + Weight::from_parts(62_758_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 13_521 + .saturating_add(Weight::from_parts(14_530_846, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -93,17 +101,21 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `654` - // Estimated: `52645` - // Minimum execution time: 42_157_000 picoseconds. - Weight::from_parts(43_105_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `933` + // Estimated: `52674` + // Minimum execution time: 66_862_000 picoseconds. + Weight::from_parts(69_531_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -111,21 +123,25 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `654` - // Estimated: `52645` - // Minimum execution time: 35_536_000 picoseconds. - Weight::from_parts(37_452_828, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 3 - .saturating_add(Weight::from_parts(2_269, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `933` + // Estimated: `52674` + // Minimum execution time: 58_971_000 picoseconds. + Weight::from_parts(62_999_984, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 7 + .saturating_add(Weight::from_parts(2_050, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -133,17 +149,21 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundMessages` (r:0 w:1) - /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: - // Measured: `621` - // Estimated: `2543` - // Minimum execution time: 25_800_000 picoseconds. - Weight::from_parts(26_666_000, 0) - .saturating_add(Weight::from_parts(0, 2543)) - .saturating_add(T::DbWeight::get().reads(3)) + // Measured: `900` + // Estimated: `5383` + // Minimum execution time: 43_066_000 picoseconds. + Weight::from_parts(43_878_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -151,17 +171,21 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: - // Measured: `621` - // Estimated: `2543` - // Minimum execution time: 27_262_000 picoseconds. - Weight::from_parts(27_997_000, 0) - .saturating_add(Weight::from_parts(0, 2543)) - .saturating_add(T::DbWeight::get().reads(3)) + // Measured: `900` + // Estimated: `5383` + // Minimum execution time: 44_120_000 picoseconds. + Weight::from_parts(45_914_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -169,17 +193,21 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: - // Measured: `621` - // Estimated: `2543` - // Minimum execution time: 26_992_000 picoseconds. - Weight::from_parts(27_921_000, 0) - .saturating_add(Weight::from_parts(0, 2543)) - .saturating_add(T::DbWeight::get().reads(3)) + // Measured: `900` + // Estimated: `5383` + // Minimum execution time: 44_930_000 picoseconds. + Weight::from_parts(46_111_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -187,7 +215,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) @@ -208,14 +240,14 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof_with_dispatch(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `813` - // Estimated: `52645` - // Minimum execution time: 55_509_000 picoseconds. - Weight::from_parts(59_826_763, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 7 - .saturating_add(Weight::from_parts(7_565, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `1092` + // Estimated: `52674` + // Minimum execution time: 81_911_000 picoseconds. + Weight::from_parts(88_170_136, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 9 + .saturating_add(Weight::from_parts(7_233, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(4)) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs index dc6c917c6d00..b27bbf4ff6c6 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -50,90 +50,98 @@ pub struct WeightInfo(PhantomData); impl pallet_bridge_messages::WeightInfo for WeightInfo { /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `658` - // Estimated: `52645` - // Minimum execution time: 40_198_000 picoseconds. - Weight::from_parts(42_079_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `810` + // Estimated: `52674` + // Minimum execution time: 62_750_000 picoseconds. + Weight::from_parts(65_328_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4076]`. /// The range of component `n` is `[1, 4076]`. fn receive_n_messages_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `658` - // Estimated: `52645` - // Minimum execution time: 39_990_000 picoseconds. - Weight::from_parts(41_381_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 8_459 - .saturating_add(Weight::from_parts(11_710_167, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `810` + // Estimated: `52674` + // Minimum execution time: 62_275_000 picoseconds. + Weight::from_parts(63_714_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 13_139 + .saturating_add(Weight::from_parts(14_630_892, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `658` - // Estimated: `52645` - // Minimum execution time: 45_940_000 picoseconds. - Weight::from_parts(47_753_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `810` + // Estimated: `52674` + // Minimum execution time: 68_950_000 picoseconds. + Weight::from_parts(71_420_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `658` - // Estimated: `52645` - // Minimum execution time: 39_067_000 picoseconds. - Weight::from_parts(41_787_019, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 5 - .saturating_add(Weight::from_parts(2_295, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `810` + // Estimated: `52674` + // Minimum execution time: 60_477_000 picoseconds. + Weight::from_parts(64_935_758, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 8 + .saturating_add(Weight::from_parts(2_008, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) @@ -141,21 +149,25 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:1) - /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: - // Measured: `501` - // Estimated: `3966` - // Minimum execution time: 33_107_000 picoseconds. - Weight::from_parts(34_364_000, 0) - .saturating_add(Weight::from_parts(0, 3966)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `779` + // Estimated: `5383` + // Minimum execution time: 52_939_000 picoseconds. + Weight::from_parts(54_637_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) @@ -163,21 +175,25 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: - // Measured: `501` - // Estimated: `3966` - // Minimum execution time: 34_826_000 picoseconds. - Weight::from_parts(35_563_000, 0) - .saturating_add(Weight::from_parts(0, 3966)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `779` + // Estimated: `5383` + // Minimum execution time: 54_645_000 picoseconds. + Weight::from_parts(57_391_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) @@ -185,31 +201,37 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:2 w:2) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: - // Measured: `501` - // Estimated: `6086` - // Minimum execution time: 38_725_000 picoseconds. - Weight::from_parts(39_727_000, 0) - .saturating_add(Weight::from_parts(0, 6086)) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `779` + // Estimated: `6144` + // Minimum execution time: 59_581_000 picoseconds. + Weight::from_parts(61_657_000, 0) + .saturating_add(Weight::from_parts(0, 6144)) + .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) @@ -222,20 +244,22 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof_with_dispatch(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `789` - // Estimated: `52645` - // Minimum execution time: 56_892_000 picoseconds. - Weight::from_parts(61_941_659, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 8 - .saturating_add(Weight::from_parts(7_580, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `1140` + // Estimated: `52674` + // Minimum execution time: 83_530_000 picoseconds. + Weight::from_parts(91_297_344, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 11 + .saturating_add(Weight::from_parts(7_197, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(4)) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains.rs index 8eb291ea1452..3629d8797bf7 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_parachains` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -65,11 +65,11 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `558` // Estimated: `2543` - // Minimum execution time: 34_889_000 picoseconds. - Weight::from_parts(36_100_759, 0) + // Minimum execution time: 41_810_000 picoseconds. + Weight::from_parts(42_952_442, 0) .saturating_add(Weight::from_parts(0, 2543)) - // Standard Error: 102_466 - .saturating_add(Weight::from_parts(178_820, 0).saturating_mul(p.into())) + // Standard Error: 108_155 + .saturating_add(Weight::from_parts(340_328, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -89,8 +89,8 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `558` // Estimated: `2543` - // Minimum execution time: 36_501_000 picoseconds. - Weight::from_parts(37_266_000, 0) + // Minimum execution time: 43_567_000 picoseconds. + Weight::from_parts(44_746_000, 0) .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -111,8 +111,8 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `558` // Estimated: `2543` - // Minimum execution time: 66_059_000 picoseconds. - Weight::from_parts(67_139_000, 0) + // Minimum execution time: 70_654_000 picoseconds. + Weight::from_parts(72_314_000, 0) .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs index f8bb983e80aa..b7318361c7d9 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_relayers` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -49,15 +49,15 @@ use core::marker::PhantomData; pub struct WeightInfo(PhantomData); impl pallet_bridge_relayers::WeightInfo for WeightInfo { /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn claim_rewards() -> Weight { // Proof Size summary in bytes: - // Measured: `278` + // Measured: `306` // Estimated: `3593` - // Minimum execution time: 44_224_000 picoseconds. - Weight::from_parts(44_905_000, 0) + // Minimum execution time: 53_924_000 picoseconds. + Weight::from_parts(54_736_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -72,8 +72,8 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `131` // Estimated: `4714` - // Minimum execution time: 23_902_000 picoseconds. - Weight::from_parts(24_702_000, 0) + // Minimum execution time: 28_608_000 picoseconds. + Weight::from_parts(29_081_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -86,8 +86,8 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `231` // Estimated: `4714` - // Minimum execution time: 24_469_000 picoseconds. - Weight::from_parts(25_176_000, 0) + // Minimum execution time: 29_738_000 picoseconds. + Weight::from_parts(30_242_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -102,21 +102,21 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `334` // Estimated: `4714` - // Minimum execution time: 27_518_000 picoseconds. - Weight::from_parts(28_068_000, 0) + // Minimum execution time: 33_174_000 picoseconds. + Weight::from_parts(33_992_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) fn register_relayer_reward() -> Weight { // Proof Size summary in bytes: // Measured: `76` - // Estimated: `3538` - // Minimum execution time: 5_484_000 picoseconds. - Weight::from_parts(5_718_000, 0) - .saturating_add(Weight::from_parts(0, 3538)) + // Estimated: `3567` + // Minimum execution time: 7_950_000 picoseconds. + Weight::from_parts(8_123_000, 0) + .saturating_add(Weight::from_parts(0, 3567)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 9c58072d402c..9a9137c18093 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -68,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 60_119_000 picoseconds. - Weight::from_parts(61_871_000, 6196) + // Minimum execution time: 70_133_000 picoseconds. + Weight::from_parts(71_765_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -77,8 +77,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 998_000 picoseconds. - Weight::from_parts(1_038_000, 0) + // Minimum execution time: 959_000 picoseconds. + Weight::from_parts(996_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -86,58 +86,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 6_327_000 picoseconds. - Weight::from_parts(6_520_000, 3497) + // Minimum execution time: 7_537_000 picoseconds. + Weight::from_parts(7_876_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_783_000 picoseconds. - Weight::from_parts(7_117_000, 0) + // Minimum execution time: 7_774_000 picoseconds. + Weight::from_parts(7_895_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_589_000 picoseconds. - Weight::from_parts(1_655_000, 0) + // Minimum execution time: 1_577_000 picoseconds. + Weight::from_parts(1_622_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_013_000 picoseconds. - Weight::from_parts(1_045_000, 0) + // Minimum execution time: 973_000 picoseconds. + Weight::from_parts(1_008_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_005_000 picoseconds. - Weight::from_parts(1_044_000, 0) + // Minimum execution time: 1_027_000 picoseconds. + Weight::from_parts(1_052_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 964_000 picoseconds. - Weight::from_parts(1_011_000, 0) + // Minimum execution time: 953_000 picoseconds. + Weight::from_parts(992_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_005_000 picoseconds. - Weight::from_parts(1_027_000, 0) + // Minimum execution time: 949_000 picoseconds. + Weight::from_parts(1_020_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 980_000 picoseconds. - Weight::from_parts(1_009_000, 0) + // Minimum execution time: 979_000 picoseconds. + Weight::from_parts(1_032_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 56_726_000 picoseconds. - Weight::from_parts(59_300_000, 6196) + // Minimum execution time: 66_663_000 picoseconds. + Weight::from_parts(67_728_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -170,8 +170,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 8_962_000 picoseconds. - Weight::from_parts(9_519_000, 3555) + // Minimum execution time: 11_074_000 picoseconds. + Weight::from_parts(11_439_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +179,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 999_000 picoseconds. - Weight::from_parts(1_035_000, 0) + // Minimum execution time: 943_000 picoseconds. + Weight::from_parts(1_021_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -200,8 +200,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 20_313_000 picoseconds. - Weight::from_parts(21_000_000, 3503) + // Minimum execution time: 25_123_000 picoseconds. + Weight::from_parts(25_687_000, 3503) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -211,44 +211,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_820_000 picoseconds. - Weight::from_parts(2_949_000, 0) + // Minimum execution time: 2_868_000 picoseconds. + Weight::from_parts(3_124_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_293_000 picoseconds. - Weight::from_parts(1_354_000, 0) + // Minimum execution time: 1_378_000 picoseconds. + Weight::from_parts(1_458_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_076_000 picoseconds. - Weight::from_parts(1_114_000, 0) + // Minimum execution time: 1_036_000 picoseconds. + Weight::from_parts(1_105_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_014_000 picoseconds. - Weight::from_parts(1_055_000, 0) + // Minimum execution time: 945_000 picoseconds. + Weight::from_parts(1_021_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 979_000 picoseconds. - Weight::from_parts(1_019_000, 0) + // Minimum execution time: 931_000 picoseconds. + Weight::from_parts(1_006_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_161_000 picoseconds. - Weight::from_parts(1_208_000, 0) + // Minimum execution time: 1_139_000 picoseconds. + Weight::from_parts(1_206_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -270,8 +270,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 62_250_000 picoseconds. - Weight::from_parts(64_477_000, 6196) + // Minimum execution time: 72_884_000 picoseconds. + Weight::from_parts(74_331_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -279,8 +279,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_286_000 picoseconds. - Weight::from_parts(4_476_000, 0) + // Minimum execution time: 4_432_000 picoseconds. + Weight::from_parts(4_542_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -302,8 +302,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 58_253_000 picoseconds. - Weight::from_parts(59_360_000, 6196) + // Minimum execution time: 67_102_000 picoseconds. + Weight::from_parts(68_630_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -311,44 +311,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_026_000 picoseconds. - Weight::from_parts(1_065_000, 0) + // Minimum execution time: 995_000 picoseconds. + Weight::from_parts(1_057_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 993_000 picoseconds. - Weight::from_parts(1_015_000, 0) + // Minimum execution time: 956_000 picoseconds. + Weight::from_parts(1_021_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 966_000 picoseconds. - Weight::from_parts(999_000, 0) + // Minimum execution time: 944_000 picoseconds. + Weight::from_parts(986_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0) // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + // Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) // Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) // Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) // Storage: `BridgeWestendMessages::OutboundLanes` (r:1 w:1) - // Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) - // Storage: `BridgeWestendMessages::OutboundLanesCongestedSignals` (r:1 w:0) - // Proof: `BridgeWestendMessages::OutboundLanesCongestedSignals` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`) + // Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) // Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:1) - // Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + // Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) /// The range of component `x` is `[1, 1000]`. pub fn export_message(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `190` - // Estimated: `6130` - // Minimum execution time: 37_014_000 picoseconds. - Weight::from_parts(38_096_655, 6130) - // Standard Error: 61 - .saturating_add(Weight::from_parts(45_146, 0).saturating_mul(x.into())) + // Measured: `589` + // Estimated: `6529` + // Minimum execution time: 58_111_000 picoseconds. + Weight::from_parts(59_123_071, 6529) + // Standard Error: 167 + .saturating_add(Weight::from_parts(43_658, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -356,14 +356,14 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 996_000 picoseconds. - Weight::from_parts(1_025_000, 0) + // Minimum execution time: 950_000 picoseconds. + Weight::from_parts(1_002_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_001_000 picoseconds. - Weight::from_parts(1_044_000, 0) + // Minimum execution time: 963_000 picoseconds. + Weight::from_parts(1_012_000, 0) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 92368b292121..e9b15024be81 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -17,10 +17,9 @@ use super::{ AccountId, AllPalletsWithSystem, Balances, BaseDeliveryFee, FeeAssetId, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - TransactionByteFee, WeightToFee, XcmpQueue, + TransactionByteFee, WeightToFee, XcmOverBridgeHubWestend, XcmOverRococoBulletin, XcmpQueue, }; -use bp_messages::LaneId; -use bp_relayers::{PayRewardFromAccount, RewardsAccountOwner, RewardsAccountParams}; + use core::marker::PhantomData; use frame_support::{ parameter_types, @@ -39,22 +38,20 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use snowbridge_runtime_common::XcmExportFeeToSibling; -use sp_core::Get; use sp_runtime::traits::AccountIdConversion; use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; use xcm::latest::prelude::*; use xcm_builder::{ - deposit_or_burn_fee, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, - AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, - FrameTransactionalProcessor, FungibleAdapter, HandleFee, IsConcrete, ParentAsSuperuser, - ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, + AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, + DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, + FungibleAdapter, HandleFee, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, }; use xcm_executor::{ - traits::{FeeManager, FeeReason, FeeReason::Export, TransactAsset}, + traits::{FeeManager, FeeReason, FeeReason::Export}, XcmExecutor, }; @@ -207,13 +204,6 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = XcmFeeManagerFromComponentsBridgeHub< WaivedLocations, ( - XcmExportFeeToRelayerRewardAccounts< - Self::AssetTransactor, - crate::bridge_to_westend_config::WestendGlobalConsensusNetwork, - crate::bridge_to_westend_config::AssetHubWestendParaId, - bp_bridge_hub_westend::BridgeHubWestend, - crate::bridge_to_westend_config::AssetHubRococoToAssetHubWestendMessagesLane, - >, XcmExportFeeToSibling< bp_rococo::Balance, AccountId, @@ -226,8 +216,8 @@ impl xcm_executor::Config for XcmConfig { ), >; type MessageExporter = ( - crate::bridge_to_westend_config::ToBridgeHubWestendHaulBlobExporter, - crate::bridge_to_bulletin_config::ToRococoBulletinHaulBlobExporter, + XcmOverBridgeHubWestend, + XcmOverRococoBulletin, crate::bridge_to_ethereum_config::SnowbridgeExporter, ); type UniversalAliases = Nothing; @@ -294,100 +284,6 @@ impl cumulus_pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; } -/// A `HandleFee` implementation that simply deposits the fees for `ExportMessage` XCM instructions -/// into the accounts that are used for paying the relayer rewards. -/// Burns the fees in case of a failure. -pub struct XcmExportFeeToRelayerRewardAccounts< - AssetTransactor, - DestNetwork, - DestParaId, - DestBridgedChain, - BridgeLaneId, ->(PhantomData<(AssetTransactor, DestNetwork, DestParaId, DestBridgedChain, BridgeLaneId)>); - -impl< - AssetTransactor: TransactAsset, - DestNetwork: Get, - DestParaId: Get, - DestBridgedChain: bp_runtime::Chain, - BridgeLaneId: Get, - > HandleFee - for XcmExportFeeToRelayerRewardAccounts< - AssetTransactor, - DestNetwork, - DestParaId, - DestBridgedChain, - BridgeLaneId, - > -{ - fn handle_fee(fee: Assets, maybe_context: Option<&XcmContext>, reason: FeeReason) -> Assets { - if matches!(reason, FeeReason::Export { network: bridged_network, destination } - if bridged_network == DestNetwork::get() && - destination == [Parachain(DestParaId::get().into())]) - { - let bridged_chain_id = DestBridgedChain::ID; - - // We have 2 relayer rewards accounts: - // - the SA of the source parachain on this BH: this pays the relayers for delivering - // Source para -> Target Para message delivery confirmations - // - the SA of the destination parachain on this BH: this pays the relayers for - // delivering Target para -> Source Para messages - // We split the `ExportMessage` fee between these 2 accounts. - let source_para_account = PayRewardFromAccount::< - pallet_balances::Pallet, - AccountId, - >::rewards_account(RewardsAccountParams::new( - BridgeLaneId::get(), - bridged_chain_id, - RewardsAccountOwner::ThisChain, - )); - - let dest_para_account = PayRewardFromAccount::< - pallet_balances::Pallet, - AccountId, - >::rewards_account(RewardsAccountParams::new( - BridgeLaneId::get(), - bridged_chain_id, - RewardsAccountOwner::BridgedChain, - )); - - for asset in fee.into_inner() { - match asset.fun { - Fungible(total_fee) => { - let source_fee = total_fee / 2; - deposit_or_burn_fee::( - Asset { id: asset.id.clone(), fun: Fungible(source_fee) }.into(), - maybe_context, - AccountId32 { network: None, id: source_para_account.clone().into() } - .into(), - ); - - let dest_fee = total_fee - source_fee; - deposit_or_burn_fee::( - Asset { id: asset.id, fun: Fungible(dest_fee) }.into(), - maybe_context, - AccountId32 { network: None, id: dest_para_account.clone().into() } - .into(), - ); - }, - NonFungible(_) => { - deposit_or_burn_fee::( - asset.into(), - maybe_context, - AccountId32 { network: None, id: source_para_account.clone().into() } - .into(), - ); - }, - } - } - - return Assets::new() - } - - fee - } -} - pub struct XcmFeeManagerFromComponentsBridgeHub( PhantomData<(WaivedLocations, HandleFee)>, ); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index e91837af0b21..0daf90872189 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -19,7 +19,7 @@ use bp_polkadot_core::Signature; use bridge_hub_rococo_runtime::{ bridge_common_config, bridge_to_bulletin_config, bridge_to_westend_config, - xcm_config::{RelayNetwork, TokenLocation, XcmConfig}, + xcm_config::{LocationToAccountId, RelayNetwork, TokenLocation, XcmConfig}, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, EthereumGatewayAddress, Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, SignedExtra, TransactionPayment, UncheckedExtrinsic, @@ -149,11 +149,21 @@ mod bridge_hub_westend_tests { use bridge_hub_test_utils::test_cases::from_parachain; use bridge_to_westend_config::{ BridgeHubWestendLocation, WestendGlobalConsensusNetwork, - WithBridgeHubWestendMessagesInstance, XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, + WithBridgeHubWestendMessagesInstance, XcmOverBridgeHubWestendInstance, }; - // Para id of sibling chain used in tests. - pub const SIBLING_PARACHAIN_ID: u32 = 1000; + // Random para id of sibling chain used in tests. + pub const SIBLING_PARACHAIN_ID: u32 = 2053; + // Random para id of sibling chain used in tests. + pub const SIBLING_SYSTEM_PARACHAIN_ID: u32 = 1008; + // Random para id of bridged chain from different global consensus used in tests. + pub const BRIDGED_LOCATION_PARACHAIN_ID: u32 = 1075; + + parameter_types! { + pub SiblingParachainLocation: Location = Location::new(1, [Parachain(SIBLING_PARACHAIN_ID)]); + pub SiblingSystemParachainLocation: Location = Location::new(1, [Parachain(SIBLING_SYSTEM_PARACHAIN_ID)]); + pub BridgedUniversalLocation: InteriorLocation = [GlobalConsensus(WestendGlobalConsensusNetwork::get()), Parachain(BRIDGED_LOCATION_PARACHAIN_ID)].into(); + } // Runtime from tests PoV type RuntimeTestsAdapter = from_parachain::WithRemoteParachainHelperAdapter< @@ -313,14 +323,20 @@ mod bridge_hub_westend_tests { _ => None, } }), - || ExportMessage { network: Westend, destination: [Parachain(bridge_to_westend_config::AssetHubWestendParaId::get().into())].into(), xcm: Xcm(vec![]) }, + || ExportMessage { network: WestendGlobalConsensusNetwork::get(), destination: [Parachain(BRIDGED_LOCATION_PARACHAIN_ID)].into(), xcm: Xcm(vec![]) }, Some((TokenLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` Some((TokenLocation::get(), bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs::get()).into()), || { PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(BridgeHubWestendLocation::get()), XCM_VERSION).expect("version saved!"); - XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubWestendInstance, + LocationToAccountId, + TokenLocation, + >(SiblingParachainLocation::get(), BridgedUniversalLocation::get()).1 }, ) } @@ -354,7 +370,7 @@ mod bridge_hub_westend_tests { _ => None, } }), - || XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, + || (), ) } @@ -368,7 +384,16 @@ mod bridge_hub_westend_tests { bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, SIBLING_PARACHAIN_ID, Rococo, - || XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, + || { + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubWestendInstance, + LocationToAccountId, + TokenLocation, + >(SiblingParachainLocation::get(), BridgedUniversalLocation::get()) + .1 + }, construct_and_apply_extrinsic, ) } @@ -383,7 +408,16 @@ mod bridge_hub_westend_tests { bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, SIBLING_PARACHAIN_ID, Rococo, - || XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, + || { + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubWestendInstance, + LocationToAccountId, + TokenLocation, + >(SiblingParachainLocation::get(), BridgedUniversalLocation::get()) + .1 + }, construct_and_apply_extrinsic, ) } @@ -446,6 +480,25 @@ mod bridge_hub_westend_tests { ), ) } + + #[test] + fn open_and_close_bridge_works() { + let origins = [SiblingParachainLocation::get(), SiblingSystemParachainLocation::get()]; + + for origin in origins { + bridge_hub_test_utils::test_cases::open_and_close_bridge_works::< + Runtime, + XcmOverBridgeHubWestendInstance, + LocationToAccountId, + TokenLocation, + >( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + origin, + BridgedUniversalLocation::get(), + ) + } + } } mod bridge_hub_bulletin_tests { @@ -454,11 +507,17 @@ mod bridge_hub_bulletin_tests { use bridge_hub_test_utils::test_cases::from_grandpa_chain; use bridge_to_bulletin_config::{ RococoBulletinGlobalConsensusNetwork, RococoBulletinGlobalConsensusNetworkLocation, - WithRococoBulletinMessagesInstance, XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + WithRococoBulletinMessagesInstance, XcmOverPolkadotBulletinInstance, }; - // Para id of sibling chain used in tests. - pub const SIBLING_PARACHAIN_ID: u32 = rococo_runtime_constants::system_parachain::PEOPLE_ID; + // Random para id of sibling chain used in tests. + pub const SIBLING_PEOPLE_PARACHAIN_ID: u32 = + rococo_runtime_constants::system_parachain::PEOPLE_ID; + + parameter_types! { + pub SiblingPeopleParachainLocation: Location = Location::new(1, [Parachain(SIBLING_PEOPLE_PARACHAIN_ID)]); + pub BridgedBulletinLocation: InteriorLocation = [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get())].into(); + } // Runtime from tests PoV type RuntimeTestsAdapter = from_grandpa_chain::WithRemoteGrandpaChainHelperAdapter< @@ -505,7 +564,7 @@ mod bridge_hub_bulletin_tests { >( collator_session_keys(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - SIBLING_PARACHAIN_ID, + SIBLING_PEOPLE_PARACHAIN_ID, Box::new(|runtime_event_encoded: Vec| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { Ok(RuntimeEvent::BridgePolkadotBulletinMessages(event)) => Some(event), @@ -522,7 +581,13 @@ mod bridge_hub_bulletin_tests { || { PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(RococoBulletinGlobalConsensusNetworkLocation::get()), XCM_VERSION).expect("version saved!"); - XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN + // we need to create lane between RococoPeople and RococoBulletin + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverPolkadotBulletinInstance, + LocationToAccountId, + TokenLocation, + >(SiblingPeopleParachainLocation::get(), BridgedBulletinLocation::get()).1 }, ) } @@ -543,7 +608,7 @@ mod bridge_hub_bulletin_tests { collator_session_keys(), slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - SIBLING_PARACHAIN_ID, + SIBLING_PEOPLE_PARACHAIN_ID, Box::new(|runtime_event_encoded: Vec| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { Ok(RuntimeEvent::ParachainSystem(event)) => Some(event), @@ -556,7 +621,7 @@ mod bridge_hub_bulletin_tests { _ => None, } }), - || XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + || (), ) } @@ -567,9 +632,18 @@ mod bridge_hub_bulletin_tests { collator_session_keys(), slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - SIBLING_PARACHAIN_ID, + SIBLING_PEOPLE_PARACHAIN_ID, Rococo, - || XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + || { + // we need to create lane between RococoPeople and RococoBulletin + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverPolkadotBulletinInstance, + LocationToAccountId, + TokenLocation, + >(SiblingPeopleParachainLocation::get(), BridgedBulletinLocation::get()) + .1 + }, construct_and_apply_extrinsic, ) } @@ -581,10 +655,38 @@ mod bridge_hub_bulletin_tests { collator_session_keys(), slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - SIBLING_PARACHAIN_ID, + SIBLING_PEOPLE_PARACHAIN_ID, Rococo, - || XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + || { + // we need to create lane between RococoPeople and RococoBulletin + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverPolkadotBulletinInstance, + LocationToAccountId, + TokenLocation, + >(SiblingPeopleParachainLocation::get(), BridgedBulletinLocation::get()) + .1 + }, construct_and_apply_extrinsic, ) } + + #[test] + fn open_and_close_bridge_works() { + let origins = [SiblingPeopleParachainLocation::get()]; + + for origin in origins { + bridge_hub_test_utils::test_cases::open_and_close_bridge_works::< + Runtime, + XcmOverPolkadotBulletinInstance, + LocationToAccountId, + TokenLocation, + >( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + origin, + BridgedBulletinLocation::get(), + ) + } + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index 1c9d8c0207b9..67d4eff0f7fe 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -90,7 +90,7 @@ bp-messages = { workspace = true } bp-parachains = { workspace = true } bp-polkadot-core = { workspace = true } bp-relayers = { workspace = true } -bp-runtime = { features = ["test-helpers"], workspace = true } +bp-runtime = { workspace = true } bp-rococo = { workspace = true } bp-westend = { workspace = true } pallet-bridge-grandpa = { workspace = true } @@ -117,6 +117,7 @@ snowbridge-runtime-common = { workspace = true } [dev-dependencies] bridge-hub-test-utils = { workspace = true, default-features = true } bridge-runtime-common = { features = ["integrity-test"], workspace = true, default-features = true } +pallet-bridge-relayers = { features = ["integrity-test"], workspace = true } sp-keyring = { workspace = true, default-features = true } snowbridge-runtime-test-common = { workspace = true, default-features = true } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index be4d40c2275f..2d9e8f664276 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -18,33 +18,36 @@ use crate::{ bridge_common_config::DeliveryRewardInBalance, weights, xcm_config::UniversalLocation, - BridgeRococoMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverBridgeHubRococo, XcmRouter, + AccountId, Balance, Balances, BridgeRococoMessages, PolkadotXcm, Runtime, RuntimeEvent, + RuntimeHoldReason, XcmOverBridgeHubRococo, XcmRouter, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, LaneId, + target_chain::FromBridgedChainMessagesProof, }; use bp_parachains::SingleParaStoredHeaderDataBuilder; -use bridge_runtime_common::{ - extensions::refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter, - RefundableMessagesLane, - }, - messages_xcm_extension::{ - SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, - XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, - }, -}; -use codec::Encode; +use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge; +use pallet_xcm_bridge_hub::XcmAsPlainPayload; + use frame_support::{ parameter_types, traits::{ConstU32, PalletInfoAccess}, }; +use frame_system::EnsureRoot; +use pallet_bridge_relayers::extension::{ + BridgeRelayersSignedExtension, WithMessagesExtensionConfig, +}; +use pallet_xcm::EnsureXcm; +use parachains_common::xcm_config::{ + AllSiblingSystemParachains, ParentRelayOrSiblingParachains, RelayOrOtherSystemParachains, +}; +use polkadot_parachain_primitives::primitives::Sibling; +use testnet_parachains_constants::westend::currency::UNITS as WND; use xcm::{ latest::prelude::*, prelude::{InteriorLocation, NetworkId}, }; -use xcm_builder::BridgeBlobDispatcher; +use xcm_builder::{BridgeBlobDispatcher, ParentIsPreset, SiblingParachainConvertsVia}; parameter_types! { pub const RelayChainHeadersToKeep: u32 = 1024; @@ -66,26 +69,6 @@ parameter_types! { // see the `FEE_BOOST_PER_MESSAGE` constant to get the meaning of this value pub PriorityBoostPerMessage: u64 = 182_044_444_444_444; - pub AssetHubWestendParaId: cumulus_primitives_core::ParaId = bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID.into(); - pub AssetHubRococoParaId: cumulus_primitives_core::ParaId = bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID.into(); - - // Lanes - pub ActiveOutboundLanesToBridgeHubRococo: &'static [bp_messages::LaneId] = &[XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO]; - pub const AssetHubWestendToAssetHubRococoMessagesLane: bp_messages::LaneId = XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO; - pub FromAssetHubWestendToAssetHubRococoRoute: SenderAndLane = SenderAndLane::new( - ParentThen([Parachain(AssetHubWestendParaId::get().into())].into()).into(), - XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, - ); - pub ActiveLanes: alloc::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = alloc::vec![ - ( - FromAssetHubWestendToAssetHubRococoRoute::get(), - (RococoGlobalConsensusNetwork::get(), [Parachain(AssetHubRococoParaId::get().into())].into()) - ) - ]; - - pub CongestedMessage: Xcm<()> = build_congestion_message(true).into(); - pub UncongestedMessage: Xcm<()> = build_congestion_message(false).into(); - pub BridgeHubRococoLocation: Location = Location::new( 2, [ @@ -93,26 +76,8 @@ parameter_types! { Parachain(::PARACHAIN_ID) ] ); -} -pub const XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 2]); - -fn build_congestion_message(is_congested: bool) -> alloc::vec::Vec> { - alloc::vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_westend::Call::ToRococoXcmRouter( - bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested, - } - ) - .encode() - .into(), - } - ] + + pub storage BridgeDeposit: Balance = 10 * WND; } /// Proof of messages, coming from Rococo. @@ -126,33 +91,14 @@ pub type ToRococoBridgeHubMessagesDeliveryProof = type FromRococoMessageBlobDispatcher = BridgeBlobDispatcher; -/// Export XCM messages to be relayed to the other side -pub type ToBridgeHubRococoHaulBlobExporter = XcmOverBridgeHubRococo; - -pub struct ToBridgeHubRococoXcmBlobHauler; -impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { - type Runtime = Runtime; - type MessagesInstance = WithBridgeHubRococoMessagesInstance; - - type ToSourceChainSender = XcmRouter; - type CongestedMessage = CongestedMessage; - type UncongestedMessage = UncongestedMessage; -} - -/// On messages delivered callback. -type OnMessagesDelivered = XcmBlobHaulerAdapter; - /// Signed extension that refunds relayers that are delivering messages from the Rococo parachain. -pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = RefundSignedExtensionAdapter< - RefundBridgedMessages< +pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = BridgeRelayersSignedExtension< + Runtime, + WithMessagesExtensionConfig< + StrOnBridgeHubWestendRefundBridgeHubRococoMessages, Runtime, - RefundableMessagesLane< - WithBridgeHubRococoMessagesInstance, - AssetHubWestendToAssetHubRococoMessagesLane, - >, - ActualFeeRefund, + WithBridgeHubRococoMessagesInstance, PriorityBoostPerMessage, - StrOnBridgeHubWestendRefundBridgeHubRococoMessages, >, >; bp_runtime::generate_static_str_provider!(OnBridgeHubWestendRefundBridgeHubRococoMessages); @@ -195,8 +141,6 @@ impl pallet_bridge_messages::Config for Run bp_bridge_hub_rococo::BridgeHubRococo, >; - type ActiveOutboundLanes = ActiveOutboundLanesToBridgeHubRococo; - type OutboundPayload = XcmAsPlainPayload; type InboundPayload = XcmAsPlainPayload; @@ -208,27 +152,85 @@ impl pallet_bridge_messages::Config for Run DeliveryRewardInBalance, >; - type MessageDispatch = XcmBlobMessageDispatch< - FromRococoMessageBlobDispatcher, - Self::WeightInfo, - cumulus_pallet_xcmp_queue::bridging::OutXcmpChannelStatusProvider< - AssetHubWestendParaId, - Runtime, - >, - >; - type OnMessagesDelivered = OnMessagesDelivered; + type MessageDispatch = XcmOverBridgeHubRococo; + type OnMessagesDelivered = XcmOverBridgeHubRococo; } /// Add support for the export and dispatch of XCM programs. pub type XcmOverBridgeHubRococoInstance = pallet_xcm_bridge_hub::Instance1; impl pallet_xcm_bridge_hub::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; type BridgedNetwork = RococoGlobalConsensusNetworkLocation; type BridgeMessagesPalletInstance = WithBridgeHubRococoMessagesInstance; + type MessageExportPrice = (); type DestinationVersion = XcmVersionOfDestAndRemoteBridge; - type Lanes = ActiveLanes; - type LanesSupport = ToBridgeHubRococoXcmBlobHauler; + + type AdminOrigin = EnsureRoot; + // Only allow calls from relay chains and sibling parachains to directly open the bridge. + type OpenBridgeOrigin = EnsureXcm; + // Converter aligned with `OpenBridgeOrigin`. + type BridgeOriginAccountIdConverter = + (ParentIsPreset, SiblingParachainConvertsVia); + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + // Do not require deposit from system parachains or relay chain + type AllowWithoutBridgeDeposit = + RelayOrOtherSystemParachains; + + // TODO:(bridges-v2) - add `LocalXcmChannelManager` impl - https://github.com/paritytech/parity-bridges-common/issues/3047 + type LocalXcmChannelManager = (); + type BlobDispatcher = FromRococoMessageBlobDispatcher; +} + +#[cfg(feature = "runtime-benchmarks")] +pub(crate) fn open_bridge_for_benchmarks( + with: bp_messages::LaneId, + sibling_para_id: u32, +) -> InteriorLocation { + use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; + use sp_runtime::traits::Zero; + use xcm::VersionedInteriorLocation; + use xcm_executor::traits::ConvertLocation; + + // insert bridge metadata + let lane_id = with; + let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); + let universal_source = [GlobalConsensus(Westend), Parachain(sibling_para_id)].into(); + let universal_destination = [GlobalConsensus(Rococo), Parachain(2075)].into(); + let bridge_id = BridgeId::new(&universal_source, &universal_destination); + + // insert only bridge metadata, because the benchmarks create lanes + pallet_xcm_bridge_hub::Bridges::::insert( + bridge_id, + Bridge { + bridge_origin_relative_location: alloc::boxed::Box::new( + sibling_parachain.clone().into(), + ), + bridge_origin_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_source.clone()), + ), + bridge_destination_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_destination), + ), + state: BridgeState::Opened, + bridge_owner_account: crate::xcm_config::LocationToAccountId::convert_location( + &sibling_parachain, + ) + .expect("valid AccountId"), + deposit: Balance::zero(), + lane_id, + }, + ); + pallet_xcm_bridge_hub::LaneToBridge::::insert( + lane_id, bridge_id, + ); + + universal_source } #[cfg(test)] @@ -236,14 +238,11 @@ mod tests { use super::*; use bridge_runtime_common::{ assert_complete_bridge_types, - extensions::refund_relayer_extension::RefundableParachain, integrity::{ assert_complete_with_parachain_bridge_constants, check_message_lane_weights, AssertChainConstants, AssertCompleteBridgeConstants, }, }; - use parachains_common::Balance; - use testnet_parachains_constants::westend; /// Every additional message in the message delivery transaction boosts its priority. /// So the priority of transaction with `N+1` messages is larger than priority of @@ -254,12 +253,12 @@ mod tests { /// /// We want this tip to be large enough (delivery transactions with more messages = less /// operational costs and a faster bridge), so this value should be significant. - const FEE_BOOST_PER_MESSAGE: Balance = 2 * westend::currency::UNITS; + const FEE_BOOST_PER_MESSAGE: Balance = 2 * WND; // see `FEE_BOOST_PER_MESSAGE` comment - const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * westend::currency::UNITS; + const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * WND; // see `FEE_BOOST_PER_MESSAGE` comment - const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * westend::currency::UNITS; + const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * WND; #[test] fn ensure_bridge_hub_westend_message_lane_weights_are_correct() { @@ -297,19 +296,20 @@ mod tests { }, }); - bridge_runtime_common::extensions::priority_calculator::per_relay_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_relay_header::ensure_priority_boost_is_sane::< Runtime, BridgeGrandpaRococoInstance, PriorityBoostPerRelayHeader, >(FEE_BOOST_PER_RELAY_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_parachain_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_parachain_header::ensure_priority_boost_is_sane::< Runtime, - RefundableParachain, + WithBridgeHubRococoMessagesInstance, + bp_bridge_hub_rococo::BridgeHubRococo, PriorityBoostPerParachainHeader, >(FEE_BOOST_PER_PARACHAIN_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_message::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_message::ensure_priority_boost_is_sane::< Runtime, WithBridgeHubRococoMessagesInstance, PriorityBoostPerMessage, @@ -323,3 +323,29 @@ mod tests { ); } } + +/// Contains the migration for the AssetHubWestend<>AssetHubRococo bridge. +pub mod migration { + use super::*; + use bp_messages::LaneId; + use frame_support::traits::ConstBool; + use sp_runtime::Either; + + parameter_types! { + pub AssetHubWestendToAssetHubRococoMessagesLane: LaneId = LaneId::from_inner(Either::Right([0, 0, 0, 2])); + pub AssetHubWestendLocation: Location = Location::new(1, [Parachain(bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID)]); + pub AssetHubRococoUniversalLocation: InteriorLocation = [GlobalConsensus(RococoGlobalConsensusNetwork::get()), Parachain(bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID)].into(); + } + + /// Ensure that the existing lanes for the AHW<>AHR bridge are correctly configured. + pub type StaticToDynamicLanes = pallet_xcm_bridge_hub::migration::OpenBridgeForLane< + Runtime, + XcmOverBridgeHubRococoInstance, + AssetHubWestendToAssetHubRococoMessagesLane, + // the lanes are already created for AHR<>AHW, but we need to link them to the bridge + // structs + ConstBool, + AssetHubWestendLocation, + AssetHubRococoUniversalLocation, + >; +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 1c26742fd67f..5717db456a77 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -37,10 +37,7 @@ extern crate alloc; use alloc::{vec, vec::Vec}; use bridge_runtime_common::extensions::{ - check_obsolete_extension::{ - CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, - }, - refund_relayer_extension::RefundableParachain, + CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::ParaId; @@ -84,7 +81,6 @@ use xcm_runtime_apis::{ }; use bp_runtime::HeaderId; - #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -144,10 +140,25 @@ pub type Migrations = ( // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5, + pallet_bridge_messages::migration::v1::MigrationToV1< + Runtime, + bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance, + >, + bridge_to_rococo_config::migration::StaticToDynamicLanes, + frame_support::migrations::RemoveStorage< + BridgeRococoMessagesPalletName, + OutboundLanesCongestedSignalsKey, + RocksDbWeight, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); +parameter_types! { + pub const BridgeRococoMessagesPalletName: &'static str = "BridgeRococoMessages"; + pub const OutboundLanesCongestedSignalsKey: &'static str = "OutboundLanesCongestedSignals"; +} + /// Migration to initialize storage versions for pallets added after genesis. /// /// Ideally this would be done automatically (see @@ -549,10 +560,8 @@ bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages! { // Parachains CheckAndBoostBridgeParachainsTransactions< Runtime, - RefundableParachain< - bridge_to_rococo_config::BridgeParachainRococoInstance, - bp_bridge_hub_rococo::BridgeHubRococo, - >, + bridge_to_rococo_config::BridgeParachainRococoInstance, + bp_bridge_hub_rococo::BridgeHubRococo, bridge_to_rococo_config::PriorityBoostPerParachainHeader, xcm_config::TreasuryAccount, >, @@ -1101,11 +1110,41 @@ impl_runtime_apis! { ); BenchmarkError::Stop("XcmVersion was not stored!") })?; + + let sibling_parachain_location = Location::new(1, [Parachain(5678)]); + + // fund SA + use frame_support::traits::fungible::Mutate; + use xcm_executor::traits::ConvertLocation; + frame_support::assert_ok!( + Balances::mint_into( + &xcm_config::LocationToAccountId::convert_location(&sibling_parachain_location).expect("valid AccountId"), + bridge_to_rococo_config::BridgeDeposit::get() + .saturating_add(ExistentialDeposit::get()) + .saturating_add(UNITS * 5) + ) + ); + + // open bridge + let origin = RuntimeOrigin::from(pallet_xcm::Origin::Xcm(sibling_parachain_location.clone())); + XcmOverBridgeHubRococo::open_bridge( + origin.clone(), + alloc::boxed::Box::new(VersionedInteriorLocation::from([GlobalConsensus(NetworkId::Rococo), Parachain(8765)])), + ).map_err(|e| { + log::error!( + "Failed to `XcmOverBridgeHubRococo::open_bridge`({:?}, {:?})`, error: {:?}", + origin, + [GlobalConsensus(NetworkId::Rococo), Parachain(8765)], + e + ); + BenchmarkError::Stop("Bridge was not opened!") + })?; + Ok( ( - bridge_to_rococo_config::FromAssetHubWestendToAssetHubRococoRoute::get().location, + sibling_parachain_location, NetworkId::Rococo, - [Parachain(bridge_to_rococo_config::AssetHubRococoParaId::get().into())].into() + [Parachain(8765)].into() ) ) } @@ -1154,16 +1193,18 @@ impl_runtime_apis! { use cumulus_primitives_core::XcmpMessageSource; assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into()); + let universal_source = bridge_to_rococo_config::open_bridge_for_benchmarks(params.lane, 42); prepare_message_proof_from_parachain::< Runtime, bridge_to_rococo_config::BridgeGrandpaRococoInstance, bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance, - >(params, generate_xcm_builder_bridge_message_sample([GlobalConsensus(Westend), Parachain(42)].into())) + >(params, generate_xcm_builder_bridge_message_sample(universal_source)) } fn prepare_message_delivery_proof( params: MessageDeliveryProofParams, ) -> bridge_to_rococo_config::ToRococoBridgeHubMessagesDeliveryProof { + let _ = bridge_to_rococo_config::open_bridge_for_benchmarks(params.lane, 42); prepare_message_delivery_proof_from_parachain::< Runtime, bridge_to_rococo_config::BridgeGrandpaRococoInstance, @@ -1195,8 +1236,8 @@ impl_runtime_apis! { parachain_head_size: u32, proof_params: bp_runtime::UnverifiedStorageProofParams, ) -> ( - pallet_bridge_parachains::RelayBlockNumber, - pallet_bridge_parachains::RelayBlockHash, + bp_parachains::RelayBlockNumber, + bp_parachains::RelayBlockHash, bp_polkadot_core::parachains::ParaHeadsProof, Vec<(bp_polkadot_core::parachains::ParaId, bp_polkadot_core::parachains::ParaHash)>, ) { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_grandpa.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_grandpa.rs index fa7efc260489..74bf144ac712 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_grandpa.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_grandpa.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_grandpa` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yaoqqom-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -64,17 +64,15 @@ impl pallet_bridge_grandpa::WeightInfo for WeightInfo Weight { + fn submit_finality_proof(p: u32, _v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `268 + p * (60 ±0)` // Estimated: `51735` - // Minimum execution time: 294_381_000 picoseconds. - Weight::from_parts(21_868_057, 0) + // Minimum execution time: 361_133_000 picoseconds. + Weight::from_parts(406_081_000, 0) .saturating_add(Weight::from_parts(0, 51735)) - // Standard Error: 14_649 - .saturating_add(Weight::from_parts(40_681_012, 0).saturating_mul(p.into())) - // Standard Error: 48_883 - .saturating_add(Weight::from_parts(2_466_672, 0).saturating_mul(v.into())) + // Standard Error: 26_551 + .saturating_add(Weight::from_parts(40_356_046, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -92,8 +90,8 @@ impl pallet_bridge_grandpa::WeightInfo for WeightInfo(PhantomData); impl pallet_bridge_messages::WeightInfo for WeightInfo { /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `522` - // Estimated: `52645` - // Minimum execution time: 40_289_000 picoseconds. - Weight::from_parts(42_150_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `701` + // Estimated: `52674` + // Minimum execution time: 62_015_000 picoseconds. + Weight::from_parts(63_891_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4076]`. fn receive_n_messages_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `522` - // Estimated: `52645` - // Minimum execution time: 40_572_000 picoseconds. - Weight::from_parts(41_033_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 12_000 - .saturating_add(Weight::from_parts(11_710_588, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `701` + // Estimated: `52674` + // Minimum execution time: 62_034_000 picoseconds. + Weight::from_parts(63_355_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 8_231 + .saturating_add(Weight::from_parts(14_096_117, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `522` - // Estimated: `52645` - // Minimum execution time: 46_655_000 picoseconds. - Weight::from_parts(49_576_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `701` + // Estimated: `52674` + // Minimum execution time: 65_063_000 picoseconds. + Weight::from_parts(67_125_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `522` - // Estimated: `52645` - // Minimum execution time: 40_245_000 picoseconds. - Weight::from_parts(43_461_320, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 21 - .saturating_add(Weight::from_parts(2_246, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `701` + // Estimated: `52674` + // Minimum execution time: 58_688_000 picoseconds. + Weight::from_parts(61_404_716, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 7 + .saturating_add(Weight::from_parts(2_249, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) @@ -139,21 +147,25 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:1) - /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: - // Measured: `357` - // Estimated: `3822` - // Minimum execution time: 32_001_000 picoseconds. - Weight::from_parts(32_842_000, 0) - .saturating_add(Weight::from_parts(0, 3822)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `710` + // Estimated: `5383` + // Minimum execution time: 53_123_000 picoseconds. + Weight::from_parts(54_417_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) @@ -161,21 +173,25 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: - // Measured: `357` - // Estimated: `3822` - // Minimum execution time: 33_287_000 picoseconds. - Weight::from_parts(33_769_000, 0) - .saturating_add(Weight::from_parts(0, 3822)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `710` + // Estimated: `5383` + // Minimum execution time: 55_140_000 picoseconds. + Weight::from_parts(56_456_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) @@ -183,31 +199,37 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:2 w:2) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: - // Measured: `357` - // Estimated: `6086` - // Minimum execution time: 37_136_000 picoseconds. - Weight::from_parts(38_294_000, 0) - .saturating_add(Weight::from_parts(0, 6086)) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `710` + // Estimated: `6144` + // Minimum execution time: 60_415_000 picoseconds. + Weight::from_parts(62_057_000, 0) + .saturating_add(Weight::from_parts(0, 6144)) + .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) @@ -220,19 +242,21 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof_with_dispatch(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `653` - // Estimated: `52645` - // Minimum execution time: 55_942_000 picoseconds. - Weight::from_parts(60_615_769, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 14 - .saturating_add(Weight::from_parts(7_225, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `965` + // Estimated: `52674` + // Minimum execution time: 84_340_000 picoseconds. + Weight::from_parts(89_615_003, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 15 + .saturating_add(Weight::from_parts(7_574, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(4)) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_parachains.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_parachains.rs index b4748f141705..87c5057cf9b8 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_parachains.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_parachains.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_parachains` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -61,13 +61,15 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:0 w:1) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// The range of component `p` is `[1, 2]`. - fn submit_parachain_heads_with_n_parachains(_p: u32, ) -> Weight { + fn submit_parachain_heads_with_n_parachains(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `315` // Estimated: `2543` - // Minimum execution time: 34_177_000 picoseconds. - Weight::from_parts(35_662_308, 0) + // Minimum execution time: 39_518_000 picoseconds. + Weight::from_parts(40_461_018, 0) .saturating_add(Weight::from_parts(0, 2543)) + // Standard Error: 98_154 + .saturating_add(Weight::from_parts(479_640, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -87,8 +89,8 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `315` // Estimated: `2543` - // Minimum execution time: 35_975_000 picoseconds. - Weight::from_parts(36_510_000, 0) + // Minimum execution time: 41_243_000 picoseconds. + Weight::from_parts(42_293_000, 0) .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -109,8 +111,8 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `315` // Estimated: `2543` - // Minimum execution time: 62_837_000 picoseconds. - Weight::from_parts(63_562_000, 0) + // Minimum execution time: 70_926_000 picoseconds. + Weight::from_parts(71_681_000, 0) .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs index 60d81dc3082a..74be73df1403 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_relayers` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -49,15 +49,15 @@ use core::marker::PhantomData; pub struct WeightInfo(PhantomData); impl pallet_bridge_relayers::WeightInfo for WeightInfo { /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn claim_rewards() -> Weight { // Proof Size summary in bytes: - // Measured: `207` + // Measured: `272` // Estimated: `3593` - // Minimum execution time: 43_132_000 picoseconds. - Weight::from_parts(43_923_000, 0) + // Minimum execution time: 52_499_000 picoseconds. + Weight::from_parts(53_659_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -70,10 +70,10 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< /// Proof: `Balances::Reserves` (`max_values`: None, `max_size`: Some(1249), added: 3724, mode: `MaxEncodedLen`) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `61` + // Measured: `97` // Estimated: `4714` - // Minimum execution time: 22_765_000 picoseconds. - Weight::from_parts(23_576_000, 0) + // Minimum execution time: 28_706_000 picoseconds. + Weight::from_parts(29_434_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -84,10 +84,10 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< /// Proof: `Balances::Reserves` (`max_values`: None, `max_size`: Some(1249), added: 3724, mode: `MaxEncodedLen`) fn deregister() -> Weight { // Proof Size summary in bytes: - // Measured: `160` + // Measured: `197` // Estimated: `4714` - // Minimum execution time: 24_013_000 picoseconds. - Weight::from_parts(24_460_000, 0) + // Minimum execution time: 29_563_000 picoseconds. + Weight::from_parts(30_222_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -100,23 +100,23 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn slash_and_deregister() -> Weight { // Proof Size summary in bytes: - // Measured: `263` + // Measured: `300` // Estimated: `4714` - // Minimum execution time: 26_946_000 picoseconds. - Weight::from_parts(27_485_000, 0) + // Minimum execution time: 32_618_000 picoseconds. + Weight::from_parts(33_528_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) fn register_relayer_reward() -> Weight { // Proof Size summary in bytes: - // Measured: `6` - // Estimated: `3538` - // Minimum execution time: 4_658_000 picoseconds. - Weight::from_parts(4_902_000, 0) - .saturating_add(Weight::from_parts(0, 3538)) + // Measured: `42` + // Estimated: `3567` + // Minimum execution time: 7_521_000 picoseconds. + Weight::from_parts(7_844_000, 0) + .saturating_add(Weight::from_parts(0, 3567)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index ba434ff29629..16c483a21817 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -68,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 58_505_000 picoseconds. - Weight::from_parts(60_437_000, 6196) + // Minimum execution time: 70_715_000 picoseconds. + Weight::from_parts(72_211_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -77,8 +77,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 510_000 picoseconds. - Weight::from_parts(569_000, 0) + // Minimum execution time: 968_000 picoseconds. + Weight::from_parts(1_022_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -86,58 +86,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 5_597_000 picoseconds. - Weight::from_parts(5_884_000, 3497) + // Minimum execution time: 7_718_000 picoseconds. + Weight::from_parts(7_894_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_320_000 picoseconds. - Weight::from_parts(5_594_000, 0) + // Minimum execution time: 7_662_000 picoseconds. + Weight::from_parts(7_937_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_164_000 picoseconds. - Weight::from_parts(1_227_000, 0) + // Minimum execution time: 1_699_000 picoseconds. + Weight::from_parts(1_783_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 528_000 picoseconds. - Weight::from_parts(586_000, 0) + // Minimum execution time: 977_000 picoseconds. + Weight::from_parts(1_045_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 509_000 picoseconds. - Weight::from_parts(571_000, 0) + // Minimum execution time: 971_000 picoseconds. + Weight::from_parts(1_030_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 511_000 picoseconds. - Weight::from_parts(546_000, 0) + // Minimum execution time: 958_000 picoseconds. + Weight::from_parts(996_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 560_000 picoseconds. - Weight::from_parts(600_000, 0) + // Minimum execution time: 992_000 picoseconds. + Weight::from_parts(1_056_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 514_000 picoseconds. - Weight::from_parts(558_000, 0) + // Minimum execution time: 975_000 picoseconds. + Weight::from_parts(1_026_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 55_871_000 picoseconds. - Weight::from_parts(57_172_000, 6196) + // Minimum execution time: 67_236_000 picoseconds. + Weight::from_parts(68_712_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -170,8 +170,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 8_487_000 picoseconds. - Weight::from_parts(8_800_000, 3555) + // Minimum execution time: 10_890_000 picoseconds. + Weight::from_parts(11_223_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +179,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 528_000 picoseconds. - Weight::from_parts(569_000, 0) + // Minimum execution time: 959_000 picoseconds. + Weight::from_parts(1_018_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -200,8 +200,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 19_803_000 picoseconds. - Weight::from_parts(20_368_000, 3503) + // Minimum execution time: 25_162_000 picoseconds. + Weight::from_parts(25_621_000, 3503) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -211,44 +211,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_185_000 picoseconds. - Weight::from_parts(2_332_000, 0) + // Minimum execution time: 2_949_000 picoseconds. + Weight::from_parts(3_119_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 822_000 picoseconds. - Weight::from_parts(928_000, 0) + // Minimum execution time: 1_329_000 picoseconds. + Weight::from_parts(1_410_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 603_000 picoseconds. - Weight::from_parts(643_000, 0) + // Minimum execution time: 1_063_000 picoseconds. + Weight::from_parts(1_101_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 503_000 picoseconds. - Weight::from_parts(580_000, 0) + // Minimum execution time: 991_000 picoseconds. + Weight::from_parts(1_041_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 534_000 picoseconds. - Weight::from_parts(577_000, 0) + // Minimum execution time: 944_000 picoseconds. + Weight::from_parts(998_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 694_000 picoseconds. - Weight::from_parts(745_000, 0) + // Minimum execution time: 1_100_000 picoseconds. + Weight::from_parts(1_180_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -270,8 +270,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 61_083_000 picoseconds. - Weight::from_parts(62_214_000, 6196) + // Minimum execution time: 71_203_000 picoseconds. + Weight::from_parts(73_644_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -279,8 +279,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_261_000 picoseconds. - Weight::from_parts(3_483_000, 0) + // Minimum execution time: 4_018_000 picoseconds. + Weight::from_parts(4_267_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -302,8 +302,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 56_270_000 picoseconds. - Weight::from_parts(57_443_000, 6196) + // Minimum execution time: 67_893_000 picoseconds. + Weight::from_parts(69_220_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -311,44 +311,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 565_000 picoseconds. - Weight::from_parts(628_000, 0) + // Minimum execution time: 980_000 picoseconds. + Weight::from_parts(1_043_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 496_000 picoseconds. - Weight::from_parts(563_000, 0) + // Minimum execution time: 944_000 picoseconds. + Weight::from_parts(981_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 518_000 picoseconds. - Weight::from_parts(557_000, 0) + // Minimum execution time: 930_000 picoseconds. + Weight::from_parts(962_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0) // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + // Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) // Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) // Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) // Storage: `BridgeRococoMessages::OutboundLanes` (r:1 w:1) - // Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) - // Storage: `BridgeRococoMessages::OutboundLanesCongestedSignals` (r:1 w:0) - // Proof: `BridgeRococoMessages::OutboundLanesCongestedSignals` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`) + // Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) // Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:1) - // Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + // Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) /// The range of component `x` is `[1, 1000]`. pub fn export_message(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `225` - // Estimated: `6165` - // Minimum execution time: 36_288_000 picoseconds. - Weight::from_parts(37_707_751, 6165) - // Standard Error: 124 - .saturating_add(Weight::from_parts(51_290, 0).saturating_mul(x.into())) + // Measured: `552` + // Estimated: `6492` + // Minimum execution time: 56_762_000 picoseconds. + Weight::from_parts(58_320_046, 6492) + // Standard Error: 162 + .saturating_add(Weight::from_parts(51_730, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -356,14 +356,14 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 485_000 picoseconds. - Weight::from_parts(540_000, 0) + // Minimum execution time: 971_000 picoseconds. + Weight::from_parts(1_018_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 542_000 picoseconds. - Weight::from_parts(586_000, 0) + // Minimum execution time: 979_000 picoseconds. + Weight::from_parts(1_026_000, 0) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index 81705ee2dc94..491caa38dc5f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs @@ -17,7 +17,7 @@ use super::{ AccountId, AllPalletsWithSystem, Balances, BaseDeliveryFee, FeeAssetId, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - TransactionByteFee, WeightToFee, XcmpQueue, + TransactionByteFee, WeightToFee, XcmOverBridgeHubRococo, XcmpQueue, }; use frame_support::{ parameter_types, @@ -212,10 +212,8 @@ impl xcm_executor::Config for XcmConfig { SendXcmFeeToAccount, ), >; - type MessageExporter = ( - crate::bridge_to_rococo_config::ToBridgeHubRococoHaulBlobExporter, - crate::bridge_to_ethereum_config::SnowbridgeExporter, - ); + type MessageExporter = + (XcmOverBridgeHubRococo, crate::bridge_to_ethereum_config::SnowbridgeExporter); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs index 46a0fa7a664b..c5f3871c0790 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs @@ -86,11 +86,11 @@ pub fn transfer_token_to_ethereum_fee_not_enough() { collator_session_keys(), BRIDGE_HUB_WESTEND_PARACHAIN_ID, ASSET_HUB_WESTEND_PARACHAIN_ID, - DefaultBridgeHubEthereumBaseFee::get() + 10_000_000_000, + DefaultBridgeHubEthereumBaseFee::get() + 20_000_000_000, H160::random(), H160::random(), // fee not enough - 10_000_000_000, + 20_000_000_000, NotHoldingFees, ) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs index a66c0f84240c..4391b069cf09 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs @@ -21,14 +21,15 @@ use bridge_common_config::{DeliveryRewardInBalance, RequiredStakeForStakeAndSlas use bridge_hub_test_utils::{test_cases::from_parachain, SlotDurations}; use bridge_hub_westend_runtime::{ bridge_common_config, bridge_to_rococo_config, - xcm_config::{RelayNetwork, WestendLocation, XcmConfig}, + bridge_to_rococo_config::RococoGlobalConsensusNetwork, + xcm_config::{LocationToAccountId, RelayNetwork, WestendLocation, XcmConfig}, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, SignedExtra, TransactionPayment, UncheckedExtrinsic, }; use bridge_to_rococo_config::{ BridgeGrandpaRococoInstance, BridgeHubRococoLocation, BridgeParachainRococoInstance, - WithBridgeHubRococoMessagesInstance, XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, + WithBridgeHubRococoMessagesInstance, XcmOverBridgeHubRococoInstance, }; use codec::{Decode, Encode}; use frame_support::{dispatch::GetDispatchInfo, parameter_types, traits::ConstU8}; @@ -42,8 +43,18 @@ use sp_runtime::{ use testnet_parachains_constants::westend::{consensus::*, fee::WeightToFee}; use xcm::latest::prelude::*; -// Para id of sibling chain used in tests. -pub const SIBLING_PARACHAIN_ID: u32 = 1000; +// Random para id of sibling chain used in tests. +pub const SIBLING_PARACHAIN_ID: u32 = 2053; +// Random para id of sibling chain used in tests. +pub const SIBLING_SYSTEM_PARACHAIN_ID: u32 = 1008; +// Random para id of bridged chain from different global consensus used in tests. +pub const BRIDGED_LOCATION_PARACHAIN_ID: u32 = 1075; + +parameter_types! { + pub SiblingParachainLocation: Location = Location::new(1, [Parachain(SIBLING_PARACHAIN_ID)]); + pub SiblingSystemParachainLocation: Location = Location::new(1, [Parachain(SIBLING_SYSTEM_PARACHAIN_ID)]); + pub BridgedUniversalLocation: InteriorLocation = [GlobalConsensus(RococoGlobalConsensusNetwork::get()), Parachain(BRIDGED_LOCATION_PARACHAIN_ID)].into(); +} // Runtime from tests PoV type RuntimeTestsAdapter = from_parachain::WithRemoteParachainHelperAdapter< @@ -211,14 +222,20 @@ fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { _ => None, } }), - || ExportMessage { network: Rococo, destination: [Parachain(bridge_to_rococo_config::AssetHubRococoParaId::get().into())].into(), xcm: Xcm(vec![]) }, + || ExportMessage { network: RococoGlobalConsensusNetwork::get(), destination: [Parachain(BRIDGED_LOCATION_PARACHAIN_ID)].into(), xcm: Xcm(vec![]) }, Some((WestendLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` Some((WestendLocation::get(), bp_bridge_hub_westend::BridgeHubWestendBaseXcmFeeInWnds::get()).into()), || { PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(BridgeHubRococoLocation::get()), XCM_VERSION).expect("version saved!"); - XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubRococoInstance, + LocationToAccountId, + WestendLocation, + >(SiblingParachainLocation::get(), BridgedUniversalLocation::get()).1 }, ) } @@ -251,7 +268,7 @@ fn message_dispatch_routing_works() { _ => None, } }), - || XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, + || (), ) } @@ -264,7 +281,40 @@ fn relayed_incoming_message_works() { bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, SIBLING_PARACHAIN_ID, Westend, - || XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, + || { + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubRococoInstance, + LocationToAccountId, + WestendLocation, + >(SiblingParachainLocation::get(), BridgedUniversalLocation::get()) + .1 + }, + construct_and_apply_extrinsic, + ) +} + +#[test] +fn free_relay_extrinsic_works() { + // from Rococo + from_parachain::free_relay_extrinsic_works::( + collator_session_keys(), + slot_durations(), + bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + SIBLING_PARACHAIN_ID, + Westend, + || { + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubRococoInstance, + LocationToAccountId, + WestendLocation, + >(SiblingParachainLocation::get(), BridgedUniversalLocation::get()) + .1 + }, construct_and_apply_extrinsic, ) } @@ -327,3 +377,22 @@ pub fn can_calculate_fee_for_standalone_message_confirmation_transaction() { ), ) } + +#[test] +fn open_and_close_bridge_works() { + let origins = [SiblingParachainLocation::get(), SiblingSystemParachainLocation::get()]; + + for origin in origins { + bridge_hub_test_utils::test_cases::open_and_close_bridge_works::< + Runtime, + XcmOverBridgeHubRococoInstance, + LocationToAccountId, + WestendLocation, + >( + collator_session_keys(), + bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, + origin, + BridgedUniversalLocation::get(), + ) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml index 3ae43075000b..9cb24a2b2820 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml @@ -12,6 +12,7 @@ scale-info = { features = ["derive"], workspace = true } frame-support = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } +sp-std = { workspace = true } cumulus-primitives-core = { workspace = true } xcm = { workspace = true } pallet-message-queue = { workspace = true } @@ -28,6 +29,7 @@ std = [ "snowbridge-core/std", "sp-core/std", "sp-runtime/std", + "sp-std/std", "xcm/std", ] diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/common/src/lib.rs index aac6eb036526..b806b8cdb22d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/common/src/lib.rs @@ -16,6 +16,7 @@ pub mod digest_item; pub mod message_queue; +pub mod xcm_version; pub use digest_item::CustomDigestItem; pub use message_queue::{AggregateMessageOrigin, BridgeHubMessageRouter}; diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/src/xcm_version.rs b/cumulus/parachains/runtimes/bridge-hubs/common/src/xcm_version.rs new file mode 100644 index 000000000000..72e6c697e44a --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/common/src/xcm_version.rs @@ -0,0 +1,44 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Custom XCM implementation. + +use frame_support::traits::Get; +use xcm::{ + latest::prelude::*, + prelude::{GetVersion, XcmVersion}, +}; + +/// Adapter for the implementation of `GetVersion`, which attempts to find the minimal +/// configured XCM version between the destination `dest` and the bridge hub location provided as +/// `Get`. +pub struct XcmVersionOfDestAndRemoteBridge( + sp_std::marker::PhantomData<(Version, RemoteBridge)>, +); +impl> GetVersion + for XcmVersionOfDestAndRemoteBridge +{ + fn get_version_for(dest: &Location) -> Option { + let dest_version = Version::get_version_for(dest); + let bridge_hub_version = Version::get_version_for(&RemoteBridge::get()); + + match (dest_version, bridge_hub_version) { + (Some(dv), Some(bhv)) => Some(sp_std::cmp::min(dv, bhv)), + (Some(dv), None) => Some(dv), + (None, Some(bhv)) => Some(bhv), + (None, None) => None, + } + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index 44a8646142d6..8c048a0d2dbd 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -41,14 +41,17 @@ xcm-executor = { workspace = true } # Bridges bp-header-chain = { workspace = true } bp-messages = { workspace = true } +bp-parachains = { workspace = true } bp-polkadot-core = { workspace = true } bp-relayers = { workspace = true } bp-runtime = { workspace = true } bp-test-utils = { workspace = true } +bp-xcm-bridge-hub = { workspace = true } pallet-bridge-grandpa = { workspace = true } pallet-bridge-parachains = { workspace = true } pallet-bridge-messages = { features = ["test-helpers"], workspace = true } pallet-bridge-relayers = { workspace = true } +pallet-xcm-bridge-hub = { workspace = true } bridge-runtime-common = { workspace = true } [features] @@ -57,10 +60,12 @@ std = [ "asset-test-utils/std", "bp-header-chain/std", "bp-messages/std", + "bp-parachains/std", "bp-polkadot-core/std", "bp-relayers/std", "bp-runtime/std", "bp-test-utils/std", + "bp-xcm-bridge-hub/std", "bridge-runtime-common/std", "codec/std", "cumulus-pallet-parachain-system/std", @@ -75,6 +80,7 @@ std = [ "pallet-bridge-relayers/std", "pallet-timestamp/std", "pallet-utility/std", + "pallet-xcm-bridge-hub/std", "parachains-common/std", "parachains-runtimes-test-utils/std", "sp-core/std", diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs index 0b3463f0df97..b8d6d87051c7 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -24,6 +24,7 @@ extern crate alloc; pub use bp_test_utils::test_header; pub use parachains_runtimes_test_utils::*; use sp_runtime::Perbill; +pub use test_cases::helpers::ensure_opened_bridge; /// A helper function for comparing the actual value of a fee constant with its estimated value. The /// estimated value can be overestimated (`overestimate_in_percent`), and if the difference to the diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs index f2f0ccecba13..72743eaa41db 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs @@ -26,7 +26,7 @@ use alloc::{boxed::Box, vec}; use bp_header_chain::ChainWithGrandpa; use bp_messages::{LaneId, UnrewardedRelayersState}; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; -use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; +use bp_xcm_bridge_hub::XcmAsPlainPayload; use frame_support::traits::{OnFinalize, OnInitialize}; use frame_system::pallet_prelude::BlockNumberFor; use pallet_bridge_messages::{BridgedChainOf, ThisChainOf}; @@ -447,7 +447,7 @@ where BridgedChainOf, ThisChainOf, >( - LaneId::default(), + LaneId::new(1, 2), vec![Instruction::<()>::ClearOrigin; 1_024].into(), 1, [GlobalConsensus(Polkadot), Parachain(1_000)].into(), @@ -503,7 +503,7 @@ where ThisChainOf, (), >( - LaneId::default(), + LaneId::new(1, 2), 1u32.into(), AccountId32::from(Alice.public()).into(), unrewarded_relayers.clone(), @@ -551,7 +551,7 @@ where BridgedChainOf, ThisChainOf, >( - LaneId::default(), + LaneId::new(1, 2), vec![Instruction::<()>::ClearOrigin; 1_024].into(), 1, [GlobalConsensus(Polkadot), Parachain(1_000)].into(), @@ -603,7 +603,7 @@ where ThisChainOf, (), >( - LaneId::default(), + LaneId::new(1, 2), 1u32.into(), AccountId32::from(Alice.public()).into(), unrewarded_relayers.clone(), diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs index 0988528c3f2a..82edcacdcab5 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs @@ -28,7 +28,7 @@ use bp_messages::{LaneId, UnrewardedRelayersState}; use bp_polkadot_core::parachains::ParaHash; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::{Chain, Parachain}; -use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; +use bp_xcm_bridge_hub::XcmAsPlainPayload; use frame_support::traits::{OnFinalize, OnInitialize}; use frame_system::pallet_prelude::BlockNumberFor; use pallet_bridge_messages::{BridgedChainOf, ThisChainOf}; @@ -552,7 +552,7 @@ where BridgedChainOf, ThisChainOf, >( - LaneId::default(), + LaneId::new(1, 2), vec![Instruction::<()>::ClearOrigin; 1_024].into(), 1, [GlobalConsensus(Polkadot), Parachain(1_000)].into(), @@ -622,7 +622,7 @@ where BridgedChainOf, ThisChainOf, >( - LaneId::default(), + LaneId::new(1, 2), 1, 5, 1_000, @@ -684,7 +684,7 @@ where BridgedChainOf, ThisChainOf, >( - LaneId::default(), + LaneId::new(1, 2), vec![Instruction::<()>::ClearOrigin; 1_024].into(), 1, [GlobalConsensus(Polkadot), Parachain(1_000)].into(), @@ -739,7 +739,7 @@ where BridgedChainOf, ThisChainOf, >( - LaneId::default(), + LaneId::new(1, 2), 1, 5, 1_000, diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs index c4b5e5583baa..c343e9b3e09a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs @@ -23,11 +23,13 @@ use bp_messages::{LaneId, MessageNonce}; use bp_polkadot_core::parachains::{ParaHash, ParaId}; use bp_relayers::RewardsAccountParams; use bp_runtime::Chain; +use bp_xcm_bridge_hub::BridgeLocations; use codec::Decode; use core::marker::PhantomData; use frame_support::{ assert_ok, - traits::{OnFinalize, OnInitialize, PalletInfoAccess}, + dispatch::GetDispatchInfo, + traits::{fungible::Mutate, OnFinalize, OnInitialize, PalletInfoAccess}, }; use frame_system::pallet_prelude::BlockNumberFor; use pallet_bridge_grandpa::{BridgedBlockHash, BridgedHeader}; @@ -40,6 +42,7 @@ use sp_core::Get; use sp_keyring::AccountKeyring::*; use sp_runtime::{traits::TrailingZeroInput, AccountId32}; use xcm::latest::prelude::*; +use xcm_executor::traits::ConvertLocation; /// Verify that the transaction has succeeded. #[impl_trait_for_tuples::impl_for_tuples(30)] @@ -157,8 +160,8 @@ where fn verify_outcome(&self) { assert_eq!( pallet_bridge_messages::InboundLanes::::get(self.lane) - .last_delivered_nonce(), - self.expected_nonce, + .map(|d| d.last_delivered_nonce()), + Some(self.expected_nonce), ); } } @@ -382,3 +385,139 @@ fn execute_and_verify_calls( verifier.verify_outcome(); } } + +/// Helper function to open the bridge/lane for `source` and `destination` while ensuring all +/// required balances are placed into the SA of the source. +pub fn ensure_opened_bridge(source: Location, destination: InteriorLocation) -> (BridgeLocations, LaneId) +where + Runtime: BasicParachainRuntime + BridgeXcmOverBridgeConfig, + XcmOverBridgePalletInstance: 'static, + ::RuntimeCall: GetDispatchInfo + From>, + ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, + ::Balance: From, + LocationToAccountId: ConvertLocation>, +TokenLocation: Get{ + // construct expected bridge configuration + let locations = + pallet_xcm_bridge_hub::Pallet::::bridge_locations( + source.clone().into(), + destination.clone().into(), + ) + .expect("valid bridge locations"); + assert!(pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ) + .is_none()); + + // required balance: ED + fee + BridgeDeposit + let bridge_deposit = + >::BridgeDeposit::get( + ); + // random high enough value for `BuyExecution` fees + let buy_execution_fee_amount = 5_000_000_000_000_u128; + let buy_execution_fee = (TokenLocation::get(), buy_execution_fee_amount).into(); + let balance_needed = ::ExistentialDeposit::get() + + buy_execution_fee_amount.into() + + bridge_deposit.into(); + + // SA of source location needs to have some required balance + let source_account_id = LocationToAccountId::convert_location(&source).expect("valid location"); + let _ = >::mint_into(&source_account_id, balance_needed) + .expect("mint_into passes"); + + // open bridge with `Transact` call + let open_bridge_call = RuntimeCallOf::::from(BridgeXcmOverBridgeCall::< + Runtime, + XcmOverBridgePalletInstance, + >::open_bridge { + bridge_destination_universal_location: Box::new(destination.into()), + }); + + // execute XCM as source origin would do with `Transact -> Origin::Xcm` + assert_ok!(RuntimeHelper::::execute_as_origin_xcm( + open_bridge_call, + source.clone(), + buy_execution_fee + ) + .ensure_complete()); + + let bridge = pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id(), + ) + .expect("opened bridge"); + + // check state + assert_ok!( + pallet_xcm_bridge_hub::Pallet::::do_try_state() + ); + + // return locations + (*locations, bridge.lane_id) +} + +/// Helper function to close the bridge/lane for `source` and `destination`. +pub fn close_bridge(source: Location, destination: InteriorLocation) +where + Runtime: BasicParachainRuntime + BridgeXcmOverBridgeConfig, + XcmOverBridgePalletInstance: 'static, + ::RuntimeCall: GetDispatchInfo + From>, + ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, + ::Balance: From, + LocationToAccountId: ConvertLocation>, +TokenLocation: Get{ + // construct expected bridge configuration + let locations = + pallet_xcm_bridge_hub::Pallet::::bridge_locations( + source.clone().into(), + destination.clone().into(), + ) + .expect("valid bridge locations"); + assert!(pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ) + .is_some()); + + // required balance: ED + fee + BridgeDeposit + let bridge_deposit = + >::BridgeDeposit::get( + ); + // random high enough value for `BuyExecution` fees + let buy_execution_fee_amount = 2_500_000_000_000_u128; + let buy_execution_fee = (TokenLocation::get(), buy_execution_fee_amount).into(); + let balance_needed = ::ExistentialDeposit::get() + + buy_execution_fee_amount.into() + + bridge_deposit.into(); + + // SA of source location needs to have some required balance + let source_account_id = LocationToAccountId::convert_location(&source).expect("valid location"); + let _ = >::mint_into(&source_account_id, balance_needed) + .expect("mint_into passes"); + + // close bridge with `Transact` call + let close_bridge_call = RuntimeCallOf::::from(BridgeXcmOverBridgeCall::< + Runtime, + XcmOverBridgePalletInstance, + >::close_bridge { + bridge_destination_universal_location: Box::new(destination.into()), + may_prune_messages: 16, + }); + + // execute XCM as source origin would do with `Transact -> Origin::Xcm` + assert_ok!(RuntimeHelper::::execute_as_origin_xcm( + close_bridge_call, + source.clone(), + buy_execution_fee + ) + .ensure_complete()); + + // bridge is closed + assert!(pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ) + .is_none()); + + // check state + assert_ok!( + pallet_xcm_bridge_hub::Pallet::::do_try_state() + ); +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs index a36a74dbbbc3..de117982b26f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs @@ -29,17 +29,15 @@ use crate::{test_cases::bridges_prelude::*, test_data}; use asset_test_utils::BasicParachainRuntime; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - LaneId, MessageKey, MessagesOperatingMode, OutboundLaneData, + LaneId, LaneState, MessageKey, MessagesOperatingMode, OutboundLaneData, }; use bp_runtime::BasicOperatingMode; -use bridge_runtime_common::messages_xcm_extension::{ - XcmAsPlainPayload, XcmBlobMessageDispatchResult, -}; +use bp_xcm_bridge_hub::{Bridge, BridgeState, XcmAsPlainPayload}; use codec::Encode; use frame_support::{ assert_ok, dispatch::GetDispatchInfo, - traits::{Get, OnFinalize, OnInitialize, OriginTrait}, + traits::{Contains, Get, OnFinalize, OnInitialize, OriginTrait}, }; use frame_system::pallet_prelude::BlockNumberFor; use parachains_common::AccountId; @@ -51,17 +49,23 @@ use sp_runtime::{traits::Zero, AccountId32}; use xcm::{latest::prelude::*, AlwaysLatest}; use xcm_builder::DispatchBlobError; use xcm_executor::{ - traits::{TransactAsset, WeightBounds}, + traits::{ConvertLocation, TransactAsset, WeightBounds}, XcmExecutor, }; /// Common bridges exports. pub(crate) mod bridges_prelude { + pub use bp_parachains::{RelayBlockHash, RelayBlockNumber}; pub use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig}; - pub use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig}; + pub use pallet_bridge_messages::{ + Call as BridgeMessagesCall, Config as BridgeMessagesConfig, LanesManagerError, + }; pub use pallet_bridge_parachains::{ - Call as BridgeParachainsCall, Config as BridgeParachainsConfig, RelayBlockHash, - RelayBlockNumber, + Call as BridgeParachainsCall, Config as BridgeParachainsConfig, + }; + pub use pallet_xcm_bridge_hub::{ + Call as BridgeXcmOverBridgeCall, Config as BridgeXcmOverBridgeConfig, LanesManagerOf, + XcmBlobMessageDispatchResult, }; } @@ -339,7 +343,12 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< pallet_bridge_messages::OutboundLanes::::try_get( expected_lane_id ), - Err(()) + Ok(OutboundLaneData { + state: LaneState::Opened, + oldest_unpruned_nonce: 1, + latest_received_nonce: 0, + latest_generated_nonce: 0 + }) ); // prepare `ExportMessage` @@ -390,6 +399,7 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< expected_lane_id ), Ok(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 0, latest_generated_nonce: 1, @@ -429,7 +439,7 @@ pub fn message_dispatch_routing_works< unwrap_cumulus_pallet_xcmp_queue_event: Box< dyn Fn(Vec) -> Option>, >, - prepare_configuration: impl Fn() -> LaneId, + prepare_configuration: impl Fn(), ) where Runtime: BasicParachainRuntime + cumulus_pallet_xcmp_queue::Config @@ -457,8 +467,9 @@ pub fn message_dispatch_routing_works< assert_ne!(runtime_para_id, sibling_parachain_id); run_test::(collator_session_key, runtime_para_id, vec![], || { - let expected_lane_id = prepare_configuration(); + prepare_configuration(); + let dummy_lane_id = LaneId::new(1, 2); let mut alice = [0u8; 32]; alice[0] = 1; @@ -475,7 +486,7 @@ pub fn message_dispatch_routing_works< >((RuntimeNetwork::get(), Here)); let result = <>::MessageDispatch>::dispatch( - test_data::dispatch_message(expected_lane_id, 1, bridging_message), + test_data::dispatch_message(dummy_lane_id, 1, bridging_message), ); assert_eq!( format!("{:?}", result.dispatch_level_result), @@ -503,7 +514,7 @@ pub fn message_dispatch_routing_works< let result = <>::MessageDispatch>::dispatch( DispatchMessage { - key: MessageKey { lane_id: expected_lane_id, nonce: 1 }, + key: MessageKey { lane_id: dummy_lane_id, nonce: 1 }, data: DispatchMessageData { payload: Ok(bridging_message.clone()) }, }, ); @@ -535,7 +546,7 @@ pub fn message_dispatch_routing_works< let result = <>::MessageDispatch>::dispatch( DispatchMessage { - key: MessageKey { lane_id: expected_lane_id, nonce: 1 }, + key: MessageKey { lane_id: dummy_lane_id, nonce: 1 }, data: DispatchMessageData { payload: Ok(bridging_message) }, }, ); @@ -641,3 +652,126 @@ where estimated_fee.into() } + +/// Test-case makes sure that `Runtime` can open/close bridges. +pub fn open_and_close_bridge_works( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + source: Location, + destination: InteriorLocation, +) where + Runtime: BasicParachainRuntime + BridgeXcmOverBridgeConfig, + XcmOverBridgePalletInstance: 'static, + ::RuntimeCall: GetDispatchInfo + From>, + ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, + ::Balance: From, + <>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::AccountId: From<::AccountId>, + LocationToAccountId: ConvertLocation>, + TokenLocation: Get, +{ + run_test::(collator_session_key, runtime_para_id, vec![], || { + // construct expected bridge configuration + let locations = pallet_xcm_bridge_hub::Pallet::::bridge_locations( + source.clone().into(), + destination.clone().into(), + ).expect("valid bridge locations"); + let expected_lane_id = + locations.calculate_lane_id(xcm::latest::VERSION).expect("valid laneId"); + let lanes_manager = LanesManagerOf::::new(); + + let expected_deposit = if >::AllowWithoutBridgeDeposit::contains( + locations.bridge_origin_relative_location() + ) { + Zero::zero() + } else { + >::BridgeDeposit::get() + }; + + // check bridge/lane DOES not exist + assert_eq!( + pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ), + None + ); + assert_eq!( + lanes_manager.active_inbound_lane(expected_lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.active_outbound_lane(expected_lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + + // open bridge with Transact call from sibling + assert_eq!( + helpers::ensure_opened_bridge::< + Runtime, + XcmOverBridgePalletInstance, + LocationToAccountId, + TokenLocation, + >(source.clone(), destination.clone()) + .0 + .bridge_id(), + locations.bridge_id() + ); + + // check bridge/lane DOES exist + assert_eq!( + pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ), + Some(Bridge { + bridge_origin_relative_location: Box::new(source.clone().into()), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into() + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into() + ), + state: BridgeState::Opened, + bridge_owner_account: LocationToAccountId::convert_location(&source) + .expect("valid location") + .into(), + deposit: expected_deposit, + lane_id: expected_lane_id + }) + ); + assert_eq!( + lanes_manager.active_inbound_lane(expected_lane_id).map(|lane| lane.state()), + Ok(LaneState::Opened) + ); + assert_eq!( + lanes_manager.active_outbound_lane(expected_lane_id).map(|lane| lane.state()), + Ok(LaneState::Opened) + ); + + // close bridge with Transact call from sibling + helpers::close_bridge::< + Runtime, + XcmOverBridgePalletInstance, + LocationToAccountId, + TokenLocation, + >(source.clone(), destination); + + // check bridge/lane DOES not exist + assert_eq!( + pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ), + None + ); + assert_eq!( + lanes_manager.active_inbound_lane(expected_lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.active_outbound_lane(expected_lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + }); +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs index 5c1dc492a9d2..2940c4e00f42 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs @@ -20,12 +20,12 @@ use crate::test_data::prepare_inbound_xcm; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, MessageNonce, - UnrewardedRelayersState, + target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, LaneState, + MessageNonce, UnrewardedRelayersState, }; use bp_runtime::{AccountIdOf, BlockNumberOf, Chain, HeaderOf, UnverifiedStorageProofParams}; use bp_test_utils::make_default_justification; -use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; +use bp_xcm_bridge_hub::XcmAsPlainPayload; use codec::Encode; use pallet_bridge_grandpa::{BridgedChain, BridgedHeader}; use sp_runtime::traits::Header as HeaderT; @@ -225,6 +225,7 @@ where prepare_message_delivery_storage_proof::( lane_id, InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: relayer_id_at_this_chain, diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs index b99c275bd0df..aefbc0dbd0a7 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs @@ -20,17 +20,17 @@ use super::{from_grandpa_chain::make_complex_bridged_grandpa_header_proof, prepa use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, + target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, LaneState, UnrewardedRelayersState, Weight, }; +use bp_parachains::{RelayBlockHash, RelayBlockNumber}; use bp_runtime::{ AccountIdOf, BlockNumberOf, Chain, HeaderOf, Parachain, UnverifiedStorageProofParams, }; use bp_test_utils::prepare_parachain_heads_proof; -use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; +use bp_xcm_bridge_hub::XcmAsPlainPayload; use codec::Encode; use pallet_bridge_grandpa::BridgedHeader; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockNumber}; use sp_runtime::traits::Header as HeaderT; use xcm::latest::prelude::*; @@ -292,6 +292,7 @@ where prepare_message_delivery_storage_proof::( lane_id, InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: relayer_id_at_this_chain.into(), diff --git a/cumulus/parachains/runtimes/test-utils/src/lib.rs b/cumulus/parachains/runtimes/test-utils/src/lib.rs index 940aa1b734df..fe75b2b6e72f 100644 --- a/cumulus/parachains/runtimes/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/test-utils/src/lib.rs @@ -22,7 +22,7 @@ use cumulus_primitives_core::{ use cumulus_primitives_parachain_inherent::ParachainInherentData; use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; use frame_support::{ - dispatch::{DispatchResult, RawOrigin}, + dispatch::{DispatchResult, GetDispatchInfo, RawOrigin}, inherent::{InherentData, ProvideInherent}, pallet_prelude::Get, traits::{OnFinalize, OnInitialize, OriginTrait, UnfilteredDispatchable}, @@ -450,6 +450,7 @@ impl< require_weight_at_most, call: call.into(), }, + ExpectTransactStatus(MaybeErrorCode::Success), ]); // execute xcm as parent origin @@ -462,6 +463,38 @@ impl< Weight::zero(), ) } + + pub fn execute_as_origin_xcm( + call: Call, + origin: Location, + buy_execution_fee: Asset, + ) -> Outcome { + // prepare `Transact` xcm + let xcm = Xcm(vec![ + WithdrawAsset(buy_execution_fee.clone().into()), + BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited }, + Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: call.get_dispatch_info().weight, + call: call.encode().into(), + }, + ExpectTransactStatus(MaybeErrorCode::Success), + ]); + + // execute xcm as parent origin + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + <::XcmExecutor>::prepare_and_execute( + origin.clone(), + xcm, + &mut hash, + Self::xcm_max_weight(if origin == Location::parent() { + XcmReceivedFrom::Parent + } else { + XcmReceivedFrom::Sibling + }), + Weight::zero(), + ) + } } pub enum XcmReceivedFrom { diff --git a/cumulus/xcm/xcm-emulator/src/lib.rs b/cumulus/xcm/xcm-emulator/src/lib.rs index 8de3660c2236..afed14278d17 100644 --- a/cumulus/xcm/xcm-emulator/src/lib.rs +++ b/cumulus/xcm/xcm-emulator/src/lib.rs @@ -296,9 +296,11 @@ impl Bridge for () { fn init() {} } +pub type BridgeLaneId = Vec; + #[derive(Clone, Default, Debug)] pub struct BridgeMessage { - pub id: u32, + pub lane_id: BridgeLaneId, pub nonce: u64, pub payload: Vec, } @@ -310,7 +312,7 @@ pub trait BridgeMessageHandler { message: BridgeMessage, ) -> Result<(), BridgeMessageDispatchError>; - fn notify_source_message_delivery(lane_id: u32); + fn notify_source_message_delivery(lane_id: BridgeLaneId); } impl BridgeMessageHandler for () { @@ -324,7 +326,7 @@ impl BridgeMessageHandler for () { Err(BridgeMessageDispatchError(Box::new("Not a bridge"))) } - fn notify_source_message_delivery(_lane_id: u32) {} + fn notify_source_message_delivery(_lane_id: BridgeLaneId) {} } #[derive(Debug)] @@ -1079,12 +1081,12 @@ macro_rules! decl_test_networks { }); match dispatch_result { - Err(e) => panic!("Error {:?} processing bridged message: {:?}", e, msg.clone()), + Err(e) => panic!("Error {:?} processing bridged message: {:?}", e, msg), Ok(()) => { <::Source as TestExt>::ext_wrapper(|| { - <::Handler as BridgeMessageHandler>::notify_source_message_delivery(msg.id); + <::Handler as BridgeMessageHandler>::notify_source_message_delivery(msg.lane_id.clone()); }); - $crate::log::debug!(target: concat!("bridge::", stringify!($name)) , "Bridged message processed {:?}", msg.clone()); + $crate::log::debug!(target: concat!("bridge::", stringify!($name)) , "Bridged message processed {:?}", msg); } } } diff --git a/prdoc/pr_4949.prdoc b/prdoc/pr_4949.prdoc new file mode 100644 index 000000000000..4a5c09c6fa8d --- /dev/null +++ b/prdoc/pr_4949.prdoc @@ -0,0 +1,78 @@ +title: "[bridges-v2] Permissionless lanes" + +doc: +- audience: Runtime Dev + description: | + This PR adds support for opening and closing dynamic, also known as permissionless, lanes. + This means that authorized origins (relay chain, sibling parachains) + can open and close bridges with other bridged (substrate-like) consensuses supported by Bridge Hubs. + The Bridge Hubs, particularly the `pallet-xcm-bridge-hub`, introduce new extrinsics `open_bridge` and `close_bridge`, + which can be called using `xcm::Transact`. + +crates: +- name: bridge-runtime-common + bump: major +- name: bp-bridge-hub-rococo + bump: minor +- name: bp-bridge-hub-westend + bump: minor +- name: pallet-bridge-grandpa + bump: major +- name: pallet-bridge-messages + bump: major +- name: pallet-bridge-parachains + bump: major +- name: pallet-bridge-relayers + bump: major +- name: pallet-xcm-bridge-hub + bump: major +- name: pallet-xcm-bridge-hub-router + bump: major +- name: bp-header-chain + bump: patch +- name: bp-messages + bump: major +- name: bp-parachains + bump: major +- name: bp-polkadot-core + bump: none +- name: bp-relayers + bump: major +- name: bp-runtime + bump: minor +- name: bp-xcm-bridge-hub-router + bump: patch +- name: bp-xcm-bridge-hub + bump: major +- name: relay-substrate-client + bump: none +- name: substrate-relay-helper + bump: major +- name: messages-relay + bump: major +- name: parachains-relay + bump: none +- name: cumulus-pallet-xcmp-queue + bump: patch +- name: parachains-relay + bump: none +- name: asset-hub-rococo-runtime + bump: major +- name: asset-hub-westend-runtime + bump: major +- name: bridge-hub-rococo-runtime + bump: major +- name: bridge-hub-westend-runtime + bump: major +- name: emulated-integration-tests-common + bump: minor +- name: asset-test-utils + bump: patch +- name: parachains-runtimes-test-utils + bump: minor +- name: bridge-hub-common + bump: minor +- name: bridge-hub-test-utils + bump: major +- name: xcm-emulator + bump: major From 7d228d9bff23eb442d1e08f6d0a775373a167f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Mon, 2 Sep 2024 18:19:51 +0200 Subject: [PATCH 220/382] Update CODEOWNERS and review-bot for new contracts pallet (#5549) Created a new @paritytech/smart-contracts team that is now referenced in the review bot config and CODEOWNERS file. Also excluded the new pallet in the other review bot rules. --- .github/CODEOWNERS | 4 ++-- .github/review-bot.yml | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 343358221215..d13add97d419 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -64,8 +64,8 @@ /substrate/primitives/merkle-mountain-range/ @acatangiu # Contracts -/substrate/frame/contracts/ @athei @pgherveou @paritytech/docs-audit -/substrate/frame/revive/ @athei @pgherveou @paritytech/docs-audit +/substrate/frame/contracts/ @paritytech/smart-contracts @paritytech/docs-audit +/substrate/frame/revive/ @paritytech/smart-contracts @paritytech/docs-audit # NPoS and election /substrate/frame/election-provider-multi-phase/ @paritytech/staking-core @paritytech/docs-audit diff --git a/.github/review-bot.yml b/.github/review-bot.yml index adbc480c6ba1..c2080142f506 100644 --- a/.github/review-bot.yml +++ b/.github/review-bot.yml @@ -29,7 +29,7 @@ rules: # excluding files from 'Runtime files' and 'CI files' rules exclude: - ^cumulus/parachains/common/src/[^/]+\.rs$ - - ^substrate/frame/(?!.*(nfts/.*|uniques/.*|babe/.*|grandpa/.*|beefy|merkle-mountain-range/.*|contracts/.*|election|nomination-pools/.*|staking/.*|aura/.*)) + - ^substrate/frame/(?!.*(nfts/.*|uniques/.*|babe/.*|grandpa/.*|beefy|merkle-mountain-range/.*|contracts/.*|revive/.*|election|nomination-pools/.*|staking/.*|aura/.*)) - ^\.gitlab-ci\.yml - ^docker/.* - ^\.github/.* @@ -56,7 +56,7 @@ rules: - name: FRAME coders substrate condition: include: - - ^substrate/frame/(?!.*(nfts/.*|uniques/.*|babe/.*|grandpa/.*|beefy|merkle-mountain-range/.*|contracts/.*|election|nomination-pools/.*|staking/.*|aura/.*)) + - ^substrate/frame/(?!.*(nfts/.*|uniques/.*|babe/.*|grandpa/.*|beefy|merkle-mountain-range/.*|contracts/.*|revive/.*|election|nomination-pools/.*|staking/.*|aura/.*)) type: "and" reviewers: - minApprovals: 2 @@ -66,6 +66,17 @@ rules: teams: - frame-coders + # Smart Contracts + - name: Smart Contracts + type: basic + condition: + include: + - ^substrate/frame/contracts/.* + - ^substrate/frame/revive/.* + minApprovals: 2 + teams: + - smart-contracts + # Protection of THIS file - name: Review Bot countAuthor: true From ad2ac0db4585cc710e672fcdc330d1ba08ece61c Mon Sep 17 00:00:00 2001 From: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Date: Mon, 2 Sep 2024 19:25:03 +0300 Subject: [PATCH 221/382] Elastic scaling: introduce new candidate receipt primitive (#5322) closes https://github.com/paritytech/polkadot-sdk/issues/5044 This PR switches the runtime to the new receipts format (vstaging primitives). I've implemented `From` to convert from new primitives to `v7` primitives and used them in the node runtime api client implementation. Until we implement the support in the node, it will continue e to use the v7 primitives but the runtime apis already use the new primitives. An expected downside of RFC103 is decoding V2 receipts shows garbage values if the input is V1: _![ima_9ce77de](https://github.com/user-attachments/assets/71d80e78-e238-4518-8cd1-548ae0d74b70)_ TODO: - [x] fix tests - [x] A few more tests for the new primitives - [x] PRDoc --------- Signed-off-by: Andrei Sandu --- Cargo.lock | 2 + .../src/lib.rs | 22 +- polkadot/node/service/src/fake_runtime_api.rs | 16 +- .../subsystem-types/src/runtime_client.rs | 34 +- .../node/test/client/src/block_builder.rs | 2 +- polkadot/primitives/Cargo.toml | 6 + polkadot/primitives/src/runtime_api.rs | 16 +- polkadot/primitives/src/v8/mod.rs | 5 +- .../primitives/src/vstaging/async_backing.rs | 76 ++ polkadot/primitives/src/vstaging/mod.rs | 908 ++++++++++++++++++ polkadot/primitives/test-helpers/src/lib.rs | 78 +- polkadot/runtime/parachains/Cargo.toml | 2 + polkadot/runtime/parachains/src/builder.rs | 78 +- .../parachains/src/inclusion/benchmarking.rs | 27 +- .../parachains/src/inclusion/migration.rs | 10 +- .../runtime/parachains/src/inclusion/mod.rs | 38 +- .../runtime/parachains/src/inclusion/tests.rs | 120 +-- .../src/paras_inherent/benchmarking.rs | 8 +- .../parachains/src/paras_inherent/mod.rs | 70 +- .../parachains/src/paras_inherent/tests.rs | 225 +++-- .../parachains/src/runtime_api_impl/v10.rs | 20 +- .../src/runtime_api_impl/vstaging.rs | 4 +- polkadot/runtime/rococo/src/lib.rs | 18 +- polkadot/runtime/test-runtime/src/lib.rs | 14 +- polkadot/runtime/westend/src/lib.rs | 18 +- prdoc/pr_5322.prdoc | 30 + 26 files changed, 1535 insertions(+), 312 deletions(-) create mode 100644 polkadot/primitives/src/vstaging/async_backing.rs create mode 100644 prdoc/pr_5322.prdoc diff --git a/Cargo.lock b/Cargo.lock index 7634cbc166a4..2d4485c92211 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14127,6 +14127,7 @@ dependencies = [ "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain-primitives", + "polkadot-primitives-test-helpers", "scale-info", "serde", "sp-api", @@ -14140,6 +14141,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-staking", + "sp-std 14.0.0", ] [[package]] diff --git a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs index c796dc5f7c38..629fa728be37 100644 --- a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs +++ b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs @@ -137,7 +137,11 @@ impl RelayChainInterface for RelayChainInProcessInterface { hash: PHash, para_id: ParaId, ) -> RelayChainResult> { - Ok(self.full_client.runtime_api().candidate_pending_availability(hash, para_id)?) + Ok(self + .full_client + .runtime_api() + .candidate_pending_availability(hash, para_id)? + .map(|receipt| receipt.into())) } async fn session_index_for_child(&self, hash: PHash) -> RelayChainResult { @@ -260,7 +264,13 @@ impl RelayChainInterface for RelayChainInProcessInterface { &self, relay_parent: PHash, ) -> RelayChainResult>> { - Ok(self.full_client.runtime_api().availability_cores(relay_parent)?) + Ok(self + .full_client + .runtime_api() + .availability_cores(relay_parent)? + .into_iter() + .map(|core_state| core_state.into()) + .collect::>()) } async fn candidates_pending_availability( @@ -268,7 +278,13 @@ impl RelayChainInterface for RelayChainInProcessInterface { hash: PHash, para_id: ParaId, ) -> RelayChainResult> { - Ok(self.full_client.runtime_api().candidates_pending_availability(hash, para_id)?) + Ok(self + .full_client + .runtime_api() + .candidates_pending_availability(hash, para_id)? + .into_iter() + .map(|receipt| receipt.into()) + .collect::>()) } } diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index cdef39d5bdf1..1f2efdbbb5b3 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -21,12 +21,16 @@ use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_primitives::{ - runtime_api, slashing, AccountId, AuthorityDiscoveryId, Balance, Block, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreState, - DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, - InboundHrmpMessage, Nonce, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + runtime_api, slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AuthorityDiscoveryId, Balance, Block, BlockNumber, CandidateCommitments, + CandidateHash, DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, + InboundDownwardMessage, InboundHrmpMessage, Nonce, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, SessionIndex, SessionInfo, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use sp_consensus_beefy::ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature}; use sp_consensus_grandpa::AuthorityId as GrandpaId; diff --git a/polkadot/node/subsystem-types/src/runtime_client.rs b/polkadot/node/subsystem-types/src/runtime_client.rs index e5e1e4d24ef9..7938223df23b 100644 --- a/polkadot/node/subsystem-types/src/runtime_client.rs +++ b/polkadot/node/subsystem-types/src/runtime_client.rs @@ -380,7 +380,10 @@ where &self, at: Hash, ) -> Result>, ApiError> { - self.client.runtime_api().availability_cores(at) + self.client + .runtime_api() + .availability_cores(at) + .map(|cores| cores.into_iter().map(|core| core.into()).collect::>()) } async fn persisted_validation_data( @@ -433,7 +436,10 @@ where at: Hash, para_id: Id, ) -> Result>, ApiError> { - self.client.runtime_api().candidate_pending_availability(at, para_id) + self.client + .runtime_api() + .candidate_pending_availability(at, para_id) + .map(|maybe_candidate| maybe_candidate.map(|candidate| candidate.into())) } async fn candidates_pending_availability( @@ -441,11 +447,19 @@ where at: Hash, para_id: Id, ) -> Result>, ApiError> { - self.client.runtime_api().candidates_pending_availability(at, para_id) + self.client + .runtime_api() + .candidates_pending_availability(at, para_id) + .map(|candidates| { + candidates.into_iter().map(|candidate| candidate.into()).collect::>() + }) } async fn candidate_events(&self, at: Hash) -> Result>, ApiError> { - self.client.runtime_api().candidate_events(at) + self.client + .runtime_api() + .candidate_events(at) + .map(|events| events.into_iter().map(|event| event.into()).collect::>()) } async fn dmq_contents( @@ -476,7 +490,10 @@ where &self, at: Hash, ) -> Result>, ApiError> { - self.client.runtime_api().on_chain_votes(at) + self.client + .runtime_api() + .on_chain_votes(at) + .map(|maybe_votes| maybe_votes.map(|votes| votes.into())) } async fn session_executor_params( @@ -588,7 +605,12 @@ where at: Hash, para_id: Id, ) -> Result, ApiError> { - self.client.runtime_api().para_backing_state(at, para_id) + self.client + .runtime_api() + .para_backing_state(at, para_id) + .map(|maybe_backing_state| { + maybe_backing_state.map(|backing_state| backing_state.into()) + }) } async fn async_backing_params( diff --git a/polkadot/node/test/client/src/block_builder.rs b/polkadot/node/test/client/src/block_builder.rs index 71bcdaffac4e..9375aca6ed73 100644 --- a/polkadot/node/test/client/src/block_builder.rs +++ b/polkadot/node/test/client/src/block_builder.rs @@ -16,7 +16,7 @@ use crate::Client; use codec::{Decode, Encode}; -use polkadot_primitives::{Block, InherentData as ParachainsInherentData}; +use polkadot_primitives::{vstaging::InherentData as ParachainsInherentData, Block}; use polkadot_test_runtime::UncheckedExtrinsic; use polkadot_test_service::GetLastTimestamp; use sc_block_builder::{BlockBuilder, BlockBuilderBuilder}; diff --git a/polkadot/primitives/Cargo.toml b/polkadot/primitives/Cargo.toml index 8f7ec314ecff..a8cd6cb5f4e0 100644 --- a/polkadot/primitives/Cargo.toml +++ b/polkadot/primitives/Cargo.toml @@ -28,10 +28,14 @@ sp-consensus-slots = { features = ["serde"], workspace = true } sp-io = { workspace = true } sp-keystore = { optional = true, workspace = true } sp-staking = { features = ["serde"], workspace = true } +sp-std = { workspace = true, optional = true } polkadot-core-primitives = { workspace = true } polkadot-parachain-primitives = { workspace = true } +[dev-dependencies] +polkadot-primitives-test-helpers = { workspace = true } + [features] default = ["std"] std = [ @@ -54,9 +58,11 @@ std = [ "sp-keystore?/std", "sp-runtime/std", "sp-staking/std", + "sp-std/std", ] runtime-benchmarks = [ "polkadot-parachain-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", ] +test = [] diff --git a/polkadot/primitives/src/runtime_api.rs b/polkadot/primitives/src/runtime_api.rs index b4816ad15075..ddebe99e6214 100644 --- a/polkadot/primitives/src/runtime_api.rs +++ b/polkadot/primitives/src/runtime_api.rs @@ -114,11 +114,15 @@ //! separated from the stable primitives. use crate::{ - async_backing, slashing, ApprovalVotingParams, AsyncBackingParams, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, - CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, NodeFeatures, - OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, - SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, ValidatorSignature, + slashing, + vstaging::{ + self, CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + ApprovalVotingParams, AsyncBackingParams, BlockNumber, CandidateCommitments, CandidateHash, + CoreIndex, DisputeState, ExecutorParams, GroupRotationInfo, Hash, NodeFeatures, + OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, SessionIndex, SessionInfo, + ValidatorId, ValidatorIndex, ValidatorSignature, }; use alloc::{ @@ -260,7 +264,7 @@ sp_api::decl_runtime_apis! { /// Returns the state of parachain backing for a given para. #[api_version(7)] - fn para_backing_state(_: ppp::Id) -> Option>; + fn para_backing_state(_: ppp::Id) -> Option>; /// Returns candidate's acceptance limitations for asynchronous backing for a relay parent. #[api_version(7)] diff --git a/polkadot/primitives/src/v8/mod.rs b/polkadot/primitives/src/v8/mod.rs index b6928e372064..a51ee0bd99bf 100644 --- a/polkadot/primitives/src/v8/mod.rs +++ b/polkadot/primitives/src/v8/mod.rs @@ -15,7 +15,6 @@ // along with Polkadot. If not, see . //! `V7` Primitives. - use alloc::{ vec, vec::{IntoIter, Vec}, @@ -1157,7 +1156,7 @@ pub enum OccupiedCoreAssumption { Free, } -/// An even concerning a candidate. +/// An event concerning a candidate. #[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] #[cfg_attr(feature = "std", derive(PartialEq))] pub enum CandidateEvent { @@ -2130,7 +2129,7 @@ impl> Default for SchedulerParams } #[cfg(test)] -mod tests { +pub mod tests { use super::*; use bitvec::bitvec; use sp_core::sr25519; diff --git a/polkadot/primitives/src/vstaging/async_backing.rs b/polkadot/primitives/src/vstaging/async_backing.rs new file mode 100644 index 000000000000..8706214b5a01 --- /dev/null +++ b/polkadot/primitives/src/vstaging/async_backing.rs @@ -0,0 +1,76 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use super::*; + +use alloc::vec::Vec; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; + +/// A candidate pending availability. +#[derive(RuntimeDebug, Clone, PartialEq, Encode, Decode, TypeInfo)] +pub struct CandidatePendingAvailability { + /// The hash of the candidate. + pub candidate_hash: CandidateHash, + /// The candidate's descriptor. + pub descriptor: CandidateDescriptorV2, + /// The commitments of the candidate. + pub commitments: CandidateCommitments, + /// The candidate's relay parent's number. + pub relay_parent_number: N, + /// The maximum Proof-of-Validity size allowed, in bytes. + pub max_pov_size: u32, +} + +impl From> + for crate::v8::async_backing::CandidatePendingAvailability +{ + fn from(value: CandidatePendingAvailability) -> Self { + Self { + candidate_hash: value.candidate_hash, + descriptor: value.descriptor.into(), + commitments: value.commitments, + relay_parent_number: value.relay_parent_number, + max_pov_size: value.max_pov_size, + } + } +} + +/// The per-parachain state of the backing system, including +/// state-machine constraints and candidates pending availability. +#[derive(RuntimeDebug, Clone, PartialEq, Encode, Decode, TypeInfo)] +pub struct BackingState { + /// The state-machine constraints of the parachain. + pub constraints: Constraints, + /// The candidates pending availability. These should be ordered, i.e. they should form + /// a sub-chain, where the first candidate builds on top of the required parent of the + /// constraints and each subsequent builds on top of the previous head-data. + pub pending_availability: Vec>, +} + +impl From> for crate::v8::async_backing::BackingState { + fn from(value: BackingState) -> Self { + Self { + constraints: value.constraints, + pending_availability: value + .pending_availability + .into_iter() + .map(|candidate| candidate.into()) + .collect::>(), + } + } +} diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 1429b0c326ac..57cba85c10d9 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -15,5 +15,913 @@ // along with Polkadot. If not, see . //! Staging Primitives. +use crate::{ValidatorIndex, ValidityAttestation}; // Put any primitives used by staging APIs functions here +use super::{ + async_backing::Constraints, BlakeTwo256, BlockNumber, CandidateCommitments, + CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CoreIndex, GroupIndex, Hash, + HashT, HeadData, Header, Id, Id as ParaId, MultiDisputeStatementSet, ScheduledCore, + UncheckedSignedAvailabilityBitfields, ValidationCodeHash, +}; +use bitvec::prelude::*; +use sp_application_crypto::ByteArray; + +use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; +use sp_runtime::traits::Header as HeaderT; +use sp_staking::SessionIndex; +/// Async backing primitives +pub mod async_backing; + +/// A type representing the version of the candidate descriptor and internal version number. +#[derive(PartialEq, Eq, Encode, Decode, Clone, TypeInfo, RuntimeDebug, Copy)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct InternalVersion(pub u8); + +/// A type representing the version of the candidate descriptor. +#[derive(PartialEq, Eq, Clone, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub enum CandidateDescriptorVersion { + /// The old candidate descriptor version. + V1, + /// The new `CandidateDescriptorV2`. + V2, + /// An unknown version. + Unknown, +} + +/// A unique descriptor of the candidate receipt. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct CandidateDescriptorV2 { + /// The ID of the para this is a candidate for. + para_id: ParaId, + /// The hash of the relay-chain block this is executed in the context of. + relay_parent: H, + /// Version field. The raw value here is not exposed, instead it is used + /// to determine the `CandidateDescriptorVersion`, see `fn version()`. + /// For the current version this field is set to `0` and will be incremented + /// by next versions. + version: InternalVersion, + /// The core index where the candidate is backed. + core_index: u16, + /// The session index of the candidate relay parent. + session_index: SessionIndex, + /// Reserved bytes. + reserved1: [u8; 25], + /// The blake2-256 hash of the persisted validation data. This is extra data derived from + /// relay-chain state which may vary based on bitfields included before the candidate. + /// Thus it cannot be derived entirely from the relay-parent. + persisted_validation_data_hash: Hash, + /// The blake2-256 hash of the PoV. + pov_hash: Hash, + /// The root of a block's erasure encoding Merkle tree. + erasure_root: Hash, + /// Reserved bytes. + reserved2: [u8; 64], + /// Hash of the para header that is being generated by this candidate. + para_head: Hash, + /// The blake2-256 hash of the validation code bytes. + validation_code_hash: ValidationCodeHash, +} + +impl From> for CandidateDescriptor { + fn from(value: CandidateDescriptorV2) -> Self { + Self { + para_id: value.para_id, + relay_parent: value.relay_parent, + collator: value.rebuild_collator_field(), + persisted_validation_data_hash: value.persisted_validation_data_hash, + pov_hash: value.pov_hash, + erasure_root: value.erasure_root, + signature: value.rebuild_signature_field(), + para_head: value.para_head, + validation_code_hash: value.validation_code_hash, + } + } +} + +#[cfg(any(feature = "runtime-benchmarks", feature = "test"))] +impl From> for CandidateDescriptorV2 { + fn from(value: CandidateDescriptor) -> Self { + Decode::decode(&mut value.encode().as_slice()).unwrap() + } +} + +impl CandidateDescriptorV2 { + /// Constructor + pub fn new( + para_id: Id, + relay_parent: H, + core_index: CoreIndex, + session_index: SessionIndex, + persisted_validation_data_hash: Hash, + pov_hash: Hash, + erasure_root: Hash, + para_head: Hash, + validation_code_hash: ValidationCodeHash, + ) -> Self { + Self { + para_id, + relay_parent, + version: InternalVersion(0), + core_index: core_index.0 as u16, + session_index, + reserved1: [0; 25], + persisted_validation_data_hash, + pov_hash, + erasure_root, + reserved2: [0; 64], + para_head, + validation_code_hash, + } + } + + /// Set the PoV size in the descriptor. Only for tests. + #[cfg(feature = "test")] + pub fn set_pov_hash(&mut self, pov_hash: Hash) { + self.pov_hash = pov_hash; + } + + /// Set the version in the descriptor. Only for tests. + #[cfg(feature = "test")] + pub fn set_version(&mut self, version: InternalVersion) { + self.version = version; + } +} + +/// A candidate-receipt at version 2. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct CandidateReceiptV2 { + /// The descriptor of the candidate. + pub descriptor: CandidateDescriptorV2, + /// The hash of the encoded commitments made as a result of candidate execution. + pub commitments_hash: Hash, +} + +/// A candidate-receipt with commitments directly included. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct CommittedCandidateReceiptV2 { + /// The descriptor of the candidate. + pub descriptor: CandidateDescriptorV2, + /// The commitments of the candidate receipt. + pub commitments: CandidateCommitments, +} + +/// An event concerning a candidate. +#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub enum CandidateEvent { + /// This candidate receipt was backed in the most recent block. + /// This includes the core index the candidate is now occupying. + #[codec(index = 0)] + CandidateBacked(CandidateReceiptV2, HeadData, CoreIndex, GroupIndex), + /// This candidate receipt was included and became a parablock at the most recent block. + /// This includes the core index the candidate was occupying as well as the group responsible + /// for backing the candidate. + #[codec(index = 1)] + CandidateIncluded(CandidateReceiptV2, HeadData, CoreIndex, GroupIndex), + /// This candidate receipt was not made available in time and timed out. + /// This includes the core index the candidate was occupying. + #[codec(index = 2)] + CandidateTimedOut(CandidateReceiptV2, HeadData, CoreIndex), +} + +impl From> for super::v8::CandidateEvent { + fn from(value: CandidateEvent) -> Self { + match value { + CandidateEvent::CandidateBacked(receipt, head_data, core_index, group_index) => + super::v8::CandidateEvent::CandidateBacked( + receipt.into(), + head_data, + core_index, + group_index, + ), + CandidateEvent::CandidateIncluded(receipt, head_data, core_index, group_index) => + super::v8::CandidateEvent::CandidateIncluded( + receipt.into(), + head_data, + core_index, + group_index, + ), + CandidateEvent::CandidateTimedOut(receipt, head_data, core_index) => + super::v8::CandidateEvent::CandidateTimedOut(receipt.into(), head_data, core_index), + } + } +} + +impl CandidateReceiptV2 { + /// Get a reference to the candidate descriptor. + pub fn descriptor(&self) -> &CandidateDescriptorV2 { + &self.descriptor + } + + /// Computes the blake2-256 hash of the receipt. + pub fn hash(&self) -> CandidateHash + where + H: Encode, + { + CandidateHash(BlakeTwo256::hash_of(self)) + } +} + +impl CommittedCandidateReceiptV2 { + /// Transforms this into a plain `CandidateReceipt`. + pub fn to_plain(&self) -> CandidateReceiptV2 { + CandidateReceiptV2 { + descriptor: self.descriptor.clone(), + commitments_hash: self.commitments.hash(), + } + } + + /// Computes the hash of the committed candidate receipt. + /// + /// This computes the canonical hash, not the hash of the directly encoded data. + /// Thus this is a shortcut for `candidate.to_plain().hash()`. + pub fn hash(&self) -> CandidateHash + where + H: Encode, + { + self.to_plain().hash() + } + + /// Does this committed candidate receipt corresponds to the given [`CandidateReceiptV2`]? + pub fn corresponds_to(&self, receipt: &CandidateReceiptV2) -> bool + where + H: PartialEq, + { + receipt.descriptor == self.descriptor && receipt.commitments_hash == self.commitments.hash() + } +} + +impl PartialOrd for CommittedCandidateReceiptV2 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for CommittedCandidateReceiptV2 { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.descriptor + .para_id + .cmp(&other.descriptor.para_id) + .then_with(|| self.commitments.head_data.cmp(&other.commitments.head_data)) + } +} + +impl From> for super::v8::CommittedCandidateReceipt { + fn from(value: CommittedCandidateReceiptV2) -> Self { + Self { descriptor: value.descriptor.into(), commitments: value.commitments } + } +} + +impl From> for super::v8::CandidateReceipt { + fn from(value: CandidateReceiptV2) -> Self { + Self { descriptor: value.descriptor.into(), commitments_hash: value.commitments_hash } + } +} + +/// A strictly increasing sequence number, typically this would be the least significant byte of the +/// block number. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub struct CoreSelector(pub u8); + +/// An offset in the relay chain claim queue. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub struct ClaimQueueOffset(pub u8); + +/// Signals that a parachain can send to the relay chain via the UMP queue. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub enum UMPSignal { + /// A message sent by a parachain to select the core the candidate is commited to. + /// Relay chain validators, in particular backers, use the `CoreSelector` and + /// `ClaimQueueOffset` to compute the index of the core the candidate has commited to. + SelectCore(CoreSelector, ClaimQueueOffset), +} +/// Separator between `XCM` and `UMPSignal`. +pub const UMP_SEPARATOR: Vec = vec![]; + +impl CandidateCommitments { + /// Returns the core selector and claim queue offset the candidate has committed to, if any. + pub fn selected_core(&self) -> Option<(CoreSelector, ClaimQueueOffset)> { + // We need at least 2 messages for the separator and core selector + if self.upward_messages.len() < 2 { + return None + } + + let separator_pos = + self.upward_messages.iter().rposition(|message| message == &UMP_SEPARATOR)?; + + // Use first commitment + let message = self.upward_messages.get(separator_pos + 1)?; + + match UMPSignal::decode(&mut message.as_slice()).ok()? { + UMPSignal::SelectCore(core_selector, cq_offset) => Some((core_selector, cq_offset)), + } + } +} + +/// CandidateReceipt construction errors. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub enum CandidateReceiptError { + /// The specified core index is invalid. + InvalidCoreIndex, + /// The core index in commitments doesn't match the one in descriptor + CoreIndexMismatch, + /// The core selector or claim queue offset is invalid. + InvalidSelectedCore, + /// The parachain is not assigned to any core at specified claim queue offset. + NoAssignment, + /// No core was selected. + NoCoreSelected, + /// Unknown version. + UnknownVersion(InternalVersion), +} + +macro_rules! impl_getter { + ($field:ident, $type:ident) => { + /// Returns the value of $field field. + pub fn $field(&self) -> $type { + self.$field + } + }; +} + +impl CandidateDescriptorV2 { + impl_getter!(erasure_root, Hash); + impl_getter!(para_head, Hash); + impl_getter!(relay_parent, H); + impl_getter!(para_id, ParaId); + impl_getter!(persisted_validation_data_hash, Hash); + impl_getter!(pov_hash, Hash); + impl_getter!(validation_code_hash, ValidationCodeHash); + + /// Returns the candidate descriptor version. + /// The candidate is at version 2 if the reserved fields are zeroed out + /// and the internal `version` field is 0. + pub fn version(&self) -> CandidateDescriptorVersion { + if self.reserved2 != [0u8; 64] || self.reserved1 != [0u8; 25] { + return CandidateDescriptorVersion::V1 + } + + match self.version.0 { + 0 => CandidateDescriptorVersion::V2, + _ => CandidateDescriptorVersion::Unknown, + } + } + + fn rebuild_collator_field(&self) -> CollatorId { + let mut collator_id = Vec::with_capacity(32); + let core_index: [u8; 2] = self.core_index.to_ne_bytes(); + let session_index: [u8; 4] = self.session_index.to_ne_bytes(); + + collator_id.push(self.version.0); + collator_id.extend_from_slice(core_index.as_slice()); + collator_id.extend_from_slice(session_index.as_slice()); + collator_id.extend_from_slice(self.reserved1.as_slice()); + + CollatorId::from_slice(&collator_id.as_slice()) + .expect("Slice size is exactly 32 bytes; qed") + } + + /// Returns the collator id if this is a v1 `CandidateDescriptor` + pub fn collator(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + Some(self.rebuild_collator_field()) + } else { + None + } + } + + fn rebuild_signature_field(&self) -> CollatorSignature { + CollatorSignature::from_slice(self.reserved2.as_slice()) + .expect("Slice size is exactly 64 bytes; qed") + } + + /// Returns the collator signature of `V1` candidate descriptors, `None` otherwise. + pub fn signature(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return Some(self.rebuild_signature_field()) + } + + None + } + + /// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise. + pub fn core_index(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return None + } + + Some(CoreIndex(self.core_index as u32)) + } + + /// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise. + pub fn session_index(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return None + } + + Some(self.session_index) + } +} + +impl CommittedCandidateReceiptV2 { + /// Checks if descriptor core index is equal to the commited core index. + /// Input `assigned_cores` must contain the sorted cores assigned to the para at + /// the committed claim queue offset. + pub fn check(&self, assigned_cores: &[CoreIndex]) -> Result<(), CandidateReceiptError> { + // Don't check v1 descriptors. + if self.descriptor.version() == CandidateDescriptorVersion::V1 { + return Ok(()) + } + + if self.descriptor.version() == CandidateDescriptorVersion::Unknown { + return Err(CandidateReceiptError::UnknownVersion(self.descriptor.version)) + } + + if assigned_cores.is_empty() { + return Err(CandidateReceiptError::NoAssignment) + } + + let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32); + + let (core_selector, _cq_offset) = + self.commitments.selected_core().ok_or(CandidateReceiptError::NoCoreSelected)?; + + let core_index = assigned_cores + .get(core_selector.0 as usize % assigned_cores.len()) + .ok_or(CandidateReceiptError::InvalidCoreIndex)?; + + if *core_index != descriptor_core_index { + return Err(CandidateReceiptError::CoreIndexMismatch) + } + + Ok(()) + } +} + +/// A backed (or backable, depending on context) candidate. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct BackedCandidate { + /// The candidate referred to. + candidate: CommittedCandidateReceiptV2, + /// The validity votes themselves, expressed as signatures. + validity_votes: Vec, + /// The indices of the validators within the group, expressed as a bitfield. May be extended + /// beyond the backing group size to contain the assigned core index, if ElasticScalingMVP is + /// enabled. + validator_indices: BitVec, +} + +/// Parachains inherent-data passed into the runtime by a block author +#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug, TypeInfo)] +pub struct InherentData { + /// Signed bitfields by validators about availability. + pub bitfields: UncheckedSignedAvailabilityBitfields, + /// Backed candidates for inclusion in the block. + pub backed_candidates: Vec>, + /// Sets of dispute votes for inclusion, + pub disputes: MultiDisputeStatementSet, + /// The parent block header. Used for checking state proofs. + pub parent_header: HDR, +} + +impl BackedCandidate { + /// Constructor + pub fn new( + candidate: CommittedCandidateReceiptV2, + validity_votes: Vec, + validator_indices: BitVec, + core_index: Option, + ) -> Self { + let mut instance = Self { candidate, validity_votes, validator_indices }; + if let Some(core_index) = core_index { + instance.inject_core_index(core_index); + } + instance + } + + /// Get a reference to the committed candidate receipt of the candidate. + pub fn candidate(&self) -> &CommittedCandidateReceiptV2 { + &self.candidate + } + + /// Get a reference to the descriptor of the candidate. + pub fn descriptor(&self) -> &CandidateDescriptorV2 { + &self.candidate.descriptor + } + + /// Get a mutable reference to the descriptor of the candidate. Only for testing. + #[cfg(feature = "test")] + pub fn descriptor_mut(&mut self) -> &mut CandidateDescriptorV2 { + &mut self.candidate.descriptor + } + + /// Get a reference to the validity votes of the candidate. + pub fn validity_votes(&self) -> &[ValidityAttestation] { + &self.validity_votes + } + + /// Get a mutable reference to validity votes of the para. + pub fn validity_votes_mut(&mut self) -> &mut Vec { + &mut self.validity_votes + } + + /// Compute this candidate's hash. + pub fn hash(&self) -> CandidateHash + where + H: Clone + Encode, + { + self.candidate.to_plain().hash() + } + + /// Get this candidate's receipt. + pub fn receipt(&self) -> CandidateReceiptV2 + where + H: Clone, + { + self.candidate.to_plain() + } + + /// Get a copy of the validator indices and the assumed core index, if any. + pub fn validator_indices_and_core_index( + &self, + core_index_enabled: bool, + ) -> (&BitSlice, Option) { + // This flag tells us if the block producers must enable Elastic Scaling MVP hack. + // It extends `BackedCandidate::validity_indices` to store a 8 bit core index. + if core_index_enabled { + let core_idx_offset = self.validator_indices.len().saturating_sub(8); + if core_idx_offset > 0 { + let (validator_indices_slice, core_idx_slice) = + self.validator_indices.split_at(core_idx_offset); + return ( + validator_indices_slice, + Some(CoreIndex(core_idx_slice.load::() as u32)), + ); + } + } + + (&self.validator_indices, None) + } + + /// Inject a core index in the validator_indices bitvec. + fn inject_core_index(&mut self, core_index: CoreIndex) { + let core_index_to_inject: BitVec = + BitVec::from_vec(vec![core_index.0 as u8]); + self.validator_indices.extend(core_index_to_inject); + } + + /// Update the validator indices and core index in the candidate. + pub fn set_validator_indices_and_core_index( + &mut self, + new_indices: BitVec, + maybe_core_index: Option, + ) { + self.validator_indices = new_indices; + + if let Some(core_index) = maybe_core_index { + self.inject_core_index(core_index); + } + } +} + +/// Scraped runtime backing votes and resolved disputes. +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub struct ScrapedOnChainVotes { + /// The session in which the block was included. + pub session: SessionIndex, + /// Set of backing validators for each candidate, represented by its candidate + /// receipt. + pub backing_validators_per_candidate: + Vec<(CandidateReceiptV2, Vec<(ValidatorIndex, ValidityAttestation)>)>, + /// On-chain-recorded set of disputes. + /// Note that the above `backing_validators` are + /// unrelated to the backers of the disputes candidates. + pub disputes: MultiDisputeStatementSet, +} + +impl From> for super::v8::ScrapedOnChainVotes { + fn from(value: ScrapedOnChainVotes) -> Self { + Self { + session: value.session, + backing_validators_per_candidate: value + .backing_validators_per_candidate + .into_iter() + .map(|(receipt, validators)| (receipt.into(), validators)) + .collect::>(), + disputes: value.disputes, + } + } +} + +/// Information about a core which is currently occupied. +#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub struct OccupiedCore { + // NOTE: this has no ParaId as it can be deduced from the candidate descriptor. + /// If this core is freed by availability, this is the assignment that is next up on this + /// core, if any. None if there is nothing queued for this core. + pub next_up_on_available: Option, + /// The relay-chain block number this began occupying the core at. + pub occupied_since: N, + /// The relay-chain block this will time-out at, if any. + pub time_out_at: N, + /// If this core is freed by being timed-out, this is the assignment that is next up on this + /// core. None if there is nothing queued for this core or there is no possibility of timing + /// out. + pub next_up_on_time_out: Option, + /// A bitfield with 1 bit for each validator in the set. `1` bits mean that the corresponding + /// validators has attested to availability on-chain. A 2/3+ majority of `1` bits means that + /// this will be available. + pub availability: BitVec, + /// The group assigned to distribute availability pieces of this candidate. + pub group_responsible: GroupIndex, + /// The hash of the candidate occupying the core. + pub candidate_hash: CandidateHash, + /// The descriptor of the candidate occupying the core. + pub candidate_descriptor: CandidateDescriptorV2, +} + +/// The state of a particular availability core. +#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub enum CoreState { + /// The core is currently occupied. + #[codec(index = 0)] + Occupied(OccupiedCore), + /// The core is currently free, with a para scheduled and given the opportunity + /// to occupy. + /// + /// If a particular Collator is required to author this block, that is also present in this + /// variant. + #[codec(index = 1)] + Scheduled(ScheduledCore), + /// The core is currently free and there is nothing scheduled. This can be the case for + /// parathread cores when there are no parathread blocks queued. Parachain cores will never be + /// left idle. + #[codec(index = 2)] + Free, +} + +impl From> for super::v8::OccupiedCore { + fn from(value: OccupiedCore) -> Self { + Self { + next_up_on_available: value.next_up_on_available, + occupied_since: value.occupied_since, + time_out_at: value.time_out_at, + next_up_on_time_out: value.next_up_on_time_out, + availability: value.availability, + group_responsible: value.group_responsible, + candidate_hash: value.candidate_hash, + candidate_descriptor: value.candidate_descriptor.into(), + } + } +} + +impl From> for super::v8::CoreState { + fn from(value: CoreState) -> Self { + match value { + CoreState::Free => super::v8::CoreState::Free, + CoreState::Scheduled(core) => super::v8::CoreState::Scheduled(core), + CoreState::Occupied(occupied_core) => + super::v8::CoreState::Occupied(occupied_core.into()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + v8::{ + tests::dummy_committed_candidate_receipt as dummy_old_committed_candidate_receipt, + CommittedCandidateReceipt, Hash, HeadData, ValidationCode, + }, + vstaging::{CandidateDescriptorV2, CommittedCandidateReceiptV2}, + }; + + fn dummy_collator_signature() -> CollatorSignature { + CollatorSignature::from_slice(&mut (0..64).into_iter().collect::>().as_slice()) + .expect("64 bytes; qed") + } + + fn dummy_collator_id() -> CollatorId { + CollatorId::from_slice(&mut (0..32).into_iter().collect::>().as_slice()) + .expect("32 bytes; qed") + } + + pub fn dummy_committed_candidate_receipt_v2() -> CommittedCandidateReceiptV2 { + let zeros = Hash::zero(); + let reserved2 = [0; 64]; + + CommittedCandidateReceiptV2 { + descriptor: CandidateDescriptorV2 { + para_id: 0.into(), + relay_parent: zeros, + version: InternalVersion(0), + core_index: 123, + session_index: 1, + reserved1: Default::default(), + persisted_validation_data_hash: zeros, + pov_hash: zeros, + erasure_root: zeros, + reserved2, + para_head: zeros, + validation_code_hash: ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).hash(), + }, + commitments: CandidateCommitments { + head_data: HeadData(vec![]), + upward_messages: vec![].try_into().expect("empty vec fits within bounds"), + new_validation_code: None, + horizontal_messages: vec![].try_into().expect("empty vec fits within bounds"), + processed_downward_messages: 0, + hrmp_watermark: 0_u32, + }, + } + } + + #[test] + fn is_binary_compatibile() { + let old_ccr = dummy_old_committed_candidate_receipt(); + let new_ccr = dummy_committed_candidate_receipt_v2(); + + assert_eq!(old_ccr.encoded_size(), new_ccr.encoded_size()); + + let encoded_old = old_ccr.encode(); + + // Deserialize from old candidate receipt. + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_old.as_slice()).unwrap(); + + // We get same candidate hash. + assert_eq!(old_ccr.hash(), new_ccr.hash()); + } + + #[test] + fn invalid_version_descriptor() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::V2); + // Put some unknown version. + new_ccr.descriptor.version = InternalVersion(100); + + // Deserialize as V1. + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut new_ccr.encode().as_slice()).unwrap(); + + assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::Unknown); + assert_eq!( + new_ccr.check(&vec![].as_slice()), + Err(CandidateReceiptError::UnknownVersion(InternalVersion(100))) + ) + } + + #[test] + fn test_ump_commitment() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + new_ccr.descriptor.core_index = 123; + new_ccr.descriptor.para_id = ParaId::new(1000); + + // dummy XCM messages + new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]); + new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]); + + // separator + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // CoreIndex commitment + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + assert_eq!(new_ccr.check(&vec![CoreIndex(123)]), Ok(())); + } + + #[test] + fn test_invalid_ump_commitment() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + new_ccr.descriptor.core_index = 0; + new_ccr.descriptor.para_id = ParaId::new(1000); + + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // The check should fail because no `SelectCore` signal was sent. + assert_eq!( + new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), + Err(CandidateReceiptError::NoCoreSelected) + ); + + // Garbage message. + new_ccr.commitments.upward_messages.force_push(vec![0, 13, 200].encode()); + + // No `SelectCore` can be decoded. + assert_eq!(new_ccr.commitments.selected_core(), None); + + // Failure is expected. + assert_eq!( + new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), + Err(CandidateReceiptError::NoCoreSelected) + ); + + new_ccr.commitments.upward_messages.clear(); + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + // Duplicate + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode()); + + // Duplicate doesn't override first signal. + assert_eq!(new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), Ok(())); + } + + #[test] + fn test_version2_receipts_decoded_as_v1() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + new_ccr.descriptor.core_index = 123; + new_ccr.descriptor.para_id = ParaId::new(1000); + + // dummy XCM messages + new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]); + new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]); + + // separator + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // CoreIndex commitment + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + let encoded_ccr = new_ccr.encode(); + let decoded_ccr: CommittedCandidateReceipt = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(decoded_ccr.descriptor.relay_parent, new_ccr.descriptor.relay_parent()); + assert_eq!(decoded_ccr.descriptor.para_id, new_ccr.descriptor.para_id()); + + assert_eq!(new_ccr.hash(), decoded_ccr.hash()); + + // Encode v1 and decode as V2 + let encoded_ccr = new_ccr.encode(); + let v2_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(v2_ccr.descriptor.core_index(), Some(CoreIndex(123))); + assert_eq!(new_ccr.check(&vec![CoreIndex(123)]), Ok(())); + + assert_eq!(new_ccr.hash(), v2_ccr.hash()); + } + + #[test] + fn test_core_select_is_mandatory() { + // Testing edge case when collators provide zeroed signature and collator id. + let mut old_ccr = dummy_old_committed_candidate_receipt(); + old_ccr.descriptor.para_id = ParaId::new(1000); + let encoded_ccr: Vec = old_ccr.encode(); + + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + // Since collator sig and id are zeroed, it means that the descriptor uses format + // version 2. + // We expect the check to fail in such case because there will be no `SelectCore` + // commitment. + assert_eq!(new_ccr.check(&vec![CoreIndex(0)]), Err(CandidateReceiptError::NoCoreSelected)); + + // Adding collator signature should make it decode as v1. + old_ccr.descriptor.signature = dummy_collator_signature(); + old_ccr.descriptor.collator = dummy_collator_id(); + + let old_ccr_hash = old_ccr.hash(); + + let encoded_ccr: Vec = old_ccr.encode(); + + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(new_ccr.descriptor.signature(), Some(old_ccr.descriptor.signature)); + assert_eq!(new_ccr.descriptor.collator(), Some(old_ccr.descriptor.collator)); + + assert_eq!(new_ccr.descriptor.core_index(), None); + assert_eq!(new_ccr.descriptor.para_id(), ParaId::new(1000)); + + assert_eq!(old_ccr_hash, new_ccr.hash()); + } +} diff --git a/polkadot/primitives/test-helpers/src/lib.rs b/polkadot/primitives/test-helpers/src/lib.rs index d43cf3317e57..b0f78717dd97 100644 --- a/polkadot/primitives/test-helpers/src/lib.rs +++ b/polkadot/primitives/test-helpers/src/lib.rs @@ -23,9 +23,10 @@ //! Note that `dummy_` prefixed values are meant to be fillers, that should not matter, and will //! contain randomness based data. use polkadot_primitives::{ + vstaging::{CandidateDescriptorV2, CandidateReceiptV2, CommittedCandidateReceiptV2}, CandidateCommitments, CandidateDescriptor, CandidateReceipt, CollatorId, CollatorSignature, - CommittedCandidateReceipt, Hash, HeadData, Id as ParaId, PersistedValidationData, - ValidationCode, ValidationCodeHash, ValidatorId, + CommittedCandidateReceipt, CoreIndex, Hash, HeadData, Id as ParaId, PersistedValidationData, + SessionIndex, ValidationCode, ValidationCodeHash, ValidatorId, }; pub use rand; use sp_application_crypto::sr25519; @@ -42,6 +43,14 @@ pub fn dummy_candidate_receipt>(relay_parent: H) -> CandidateRece } } +/// Creates a v2 candidate receipt with filler data. +pub fn dummy_candidate_receipt_v2>(relay_parent: H) -> CandidateReceiptV2 { + CandidateReceiptV2:: { + commitments_hash: dummy_candidate_commitments(dummy_head_data()).hash(), + descriptor: dummy_candidate_descriptor_v2(relay_parent), + } +} + /// Creates a committed candidate receipt with filler data. pub fn dummy_committed_candidate_receipt>( relay_parent: H, @@ -52,6 +61,16 @@ pub fn dummy_committed_candidate_receipt>( } } +/// Creates a v2 committed candidate receipt with filler data. +pub fn dummy_committed_candidate_receipt_v2>( + relay_parent: H, +) -> CommittedCandidateReceiptV2 { + CommittedCandidateReceiptV2 { + descriptor: dummy_candidate_descriptor_v2::(relay_parent), + commitments: dummy_candidate_commitments(dummy_head_data()), + } +} + /// Create a candidate receipt with a bogus signature and filler data. Optionally set the commitment /// hash with the `commitments` arg. pub fn dummy_candidate_receipt_bad_sig( @@ -124,6 +143,23 @@ pub fn dummy_candidate_descriptor>(relay_parent: H) -> CandidateD descriptor } +/// Create a v2 candidate descriptor with filler data. +pub fn dummy_candidate_descriptor_v2>(relay_parent: H) -> CandidateDescriptorV2 { + let invalid = Hash::zero(); + let descriptor = make_valid_candidate_descriptor_v2( + 1.into(), + relay_parent, + CoreIndex(1), + 1, + invalid, + invalid, + invalid, + invalid, + invalid, + ); + descriptor +} + /// Create meaningless validation code. pub fn dummy_validation_code() -> ValidationCode { ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]) @@ -134,16 +170,16 @@ pub fn dummy_head_data() -> HeadData { HeadData(vec![]) } -/// Create a meaningless collator id. -pub fn dummy_collator() -> CollatorId { - CollatorId::from(sr25519::Public::default()) -} - /// Create a meaningless validator id. pub fn dummy_validator() -> ValidatorId { ValidatorId::from(sr25519::Public::default()) } +/// Create a meaningless collator id. +pub fn dummy_collator() -> CollatorId { + CollatorId::from(sr25519::Public::default()) +} + /// Create a meaningless collator signature. pub fn dummy_collator_signature() -> CollatorSignature { CollatorSignature::from(sr25519::Signature::default()) @@ -232,6 +268,34 @@ pub fn make_valid_candidate_descriptor>( descriptor } +/// Create a v2 candidate descriptor. +pub fn make_valid_candidate_descriptor_v2>( + para_id: ParaId, + relay_parent: H, + core_index: CoreIndex, + session_index: SessionIndex, + persisted_validation_data_hash: Hash, + pov_hash: Hash, + validation_code_hash: impl Into, + para_head: Hash, + erasure_root: Hash, +) -> CandidateDescriptorV2 { + let validation_code_hash = validation_code_hash.into(); + + let descriptor = CandidateDescriptorV2::new( + para_id, + relay_parent, + core_index, + session_index, + persisted_validation_data_hash, + pov_hash, + erasure_root, + para_head, + validation_code_hash, + ); + + descriptor +} /// After manually modifying the candidate descriptor, resign with a defined collator key. pub fn resign_candidate_descriptor_with_collator>( descriptor: &mut CandidateDescriptor, diff --git a/polkadot/runtime/parachains/Cargo.toml b/polkadot/runtime/parachains/Cargo.toml index cfe373e8cba2..a3eec3f9d961 100644 --- a/polkadot/runtime/parachains/Cargo.toml +++ b/polkadot/runtime/parachains/Cargo.toml @@ -59,6 +59,8 @@ polkadot-runtime-metrics = { workspace = true } polkadot-core-primitives = { workspace = true } [dev-dependencies] +polkadot-primitives = { workspace = true, features = ["test"] } + futures = { workspace = true } hex-literal = { workspace = true, default-features = true } sp-keyring = { workspace = true, default-features = true } diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index 65e56881c315..665737afa6cb 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -30,32 +30,38 @@ use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use polkadot_primitives::{ - node_features::FeatureIndex, AvailabilityBitfield, BackedCandidate, CandidateCommitments, - CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CommittedCandidateReceipt, - CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, GroupIndex, HeadData, - Id as ParaId, IndexedVec, InherentData as ParachainsInherentData, InvalidDisputeStatementKind, + node_features::FeatureIndex, + vstaging::{ + BackedCandidate, CandidateDescriptorV2, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + InherentData as ParachainsInherentData, + }, + AvailabilityBitfield, CandidateCommitments, CandidateDescriptor, CandidateHash, CollatorId, + CollatorSignature, CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, + GroupIndex, HeadData, Id as ParaId, IndexedVec, InvalidDisputeStatementKind, PersistedValidationData, SessionIndex, SigningContext, UncheckedSigned, ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, ValidityAttestation, }; -use sp_core::{sr25519, ByteArray, H256}; +use sp_core::{ByteArray, H256}; use sp_runtime::{ generic::Digest, traits::{Header as HeaderT, One, TrailingZeroInput, Zero}, RuntimeAppPublic, }; - -/// Create a null collator id. -pub fn dummy_collator() -> CollatorId { - CollatorId::from_slice(&vec![0u8; 32]).expect("32 bytes; qed") +fn mock_validation_code() -> ValidationCode { + ValidationCode(vec![1, 2, 3]) } -/// Create a null collator signature. -pub fn dummy_collator_signature() -> CollatorSignature { - CollatorSignature::from_slice(&vec![0u8; 64]).expect("64 bytes; qed") +// Create a dummy collator id suitable to be used in a V1 candidate descriptor. +fn junk_collator() -> CollatorId { + CollatorId::from_slice(&mut (0..32).into_iter().collect::>().as_slice()) + .expect("32 bytes; qed") } -fn mock_validation_code() -> ValidationCode { - ValidationCode(vec![1, 2, 3]) +// Creates a dummy collator signature suitable to be used in a V1 candidate descriptor. +fn junk_collator_signature() -> CollatorSignature { + CollatorSignature::from_slice(&mut (0..64).into_iter().collect::>().as_slice()) + .expect("64 bytes; qed") } /// Grab an account, seeded by a name and index. @@ -136,6 +142,8 @@ pub(crate) struct BenchBuilder { fill_claimqueue: bool, /// Cores which should not be available when being populated with pending candidates. unavailable_cores: Vec, + /// Use v2 candidate descriptor. + candidate_descriptor_v2: bool, _phantom: core::marker::PhantomData, } @@ -167,6 +175,7 @@ impl BenchBuilder { code_upgrade: None, fill_claimqueue: true, unavailable_cores: vec![], + candidate_descriptor_v2: false, _phantom: core::marker::PhantomData::, } } @@ -275,6 +284,12 @@ impl BenchBuilder { self } + /// Toggle usage of v2 candidate descriptors. + pub(crate) fn set_candidate_descriptor_v2(mut self, enable: bool) -> Self { + self.candidate_descriptor_v2 = enable; + self + } + /// Get the maximum number of validators per core. fn max_validators_per_core(&self) -> u32 { self.max_validators_per_core.unwrap_or(Self::fallback_max_validators_per_core()) @@ -310,18 +325,20 @@ impl BenchBuilder { HeadData(vec![0xFF; max_head_size as usize]) } - fn candidate_descriptor_mock() -> CandidateDescriptor { + fn candidate_descriptor_mock() -> CandidateDescriptorV2 { + // Use a v1 descriptor. CandidateDescriptor:: { para_id: 0.into(), relay_parent: Default::default(), - collator: CollatorId::from(sr25519::Public::from_raw([42u8; 32])), + collator: junk_collator(), persisted_validation_data_hash: Default::default(), pov_hash: Default::default(), erasure_root: Default::default(), - signature: CollatorSignature::from(sr25519::Signature::from_raw([42u8; 64])), + signature: junk_collator_signature(), para_head: Default::default(), validation_code_hash: mock_validation_code().hash(), } + .into() } /// Create a mock of `CandidatePendingAvailability`. @@ -632,18 +649,35 @@ impl BenchBuilder { let group_validators = scheduler::Pallet::::group_validators(group_idx).unwrap(); - let candidate = CommittedCandidateReceipt:: { - descriptor: CandidateDescriptor:: { + let descriptor = if self.candidate_descriptor_v2 { + CandidateDescriptorV2::new( + para_id, + relay_parent, + core_idx, + 1, + persisted_validation_data_hash, + pov_hash, + Default::default(), + head_data.hash(), + validation_code_hash, + ) + } else { + CandidateDescriptor:: { para_id, relay_parent, - collator: dummy_collator(), + collator: junk_collator(), persisted_validation_data_hash, pov_hash, erasure_root: Default::default(), - signature: dummy_collator_signature(), + signature: junk_collator_signature(), para_head: head_data.hash(), validation_code_hash, - }, + } + .into() + }; + + let candidate = CommittedCandidateReceipt:: { + descriptor, commitments: CandidateCommitments:: { upward_messages: Default::default(), horizontal_messages: Default::default(), diff --git a/polkadot/runtime/parachains/src/inclusion/benchmarking.rs b/polkadot/runtime/parachains/src/inclusion/benchmarking.rs index 978ef718ea40..cb6329bf88ea 100644 --- a/polkadot/runtime/parachains/src/inclusion/benchmarking.rs +++ b/polkadot/runtime/parachains/src/inclusion/benchmarking.rs @@ -25,10 +25,9 @@ use bitvec::{bitvec, prelude::Lsb0}; use frame_benchmarking::benchmarks; use pallet_message_queue as mq; use polkadot_primitives::{ - CandidateCommitments, CollatorId, CollatorSignature, CommittedCandidateReceipt, HrmpChannelId, - OutboundHrmpMessage, SessionIndex, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateCommitments, + HrmpChannelId, OutboundHrmpMessage, SessionIndex, }; -use sp_core::sr25519; fn create_candidate_commitments( para_id: ParaId, @@ -124,17 +123,17 @@ benchmarks! { let core_index = CoreIndex::from(0); let backing_group = GroupIndex::from(0); - let descriptor = CandidateDescriptor:: { - para_id: para, - relay_parent: Default::default(), - collator: CollatorId::from(sr25519::Public::from_raw([42u8; 32])), - persisted_validation_data_hash: Default::default(), - pov_hash: Default::default(), - erasure_root: Default::default(), - signature: CollatorSignature::from(sr25519::Signature::from_raw([42u8; 64])), - para_head: Default::default(), - validation_code_hash: ValidationCode(vec![1, 2, 3]).hash(), - }; + let descriptor = CandidateDescriptor::::new( + para, + Default::default(), + CoreIndex(0), + 1, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ValidationCode(vec![1, 2, 3]).hash(), + ); let receipt = CommittedCandidateReceipt:: { descriptor, diff --git a/polkadot/runtime/parachains/src/inclusion/migration.rs b/polkadot/runtime/parachains/src/inclusion/migration.rs index 36a810d341c6..2a215d5d595c 100644 --- a/polkadot/runtime/parachains/src/inclusion/migration.rs +++ b/polkadot/runtime/parachains/src/inclusion/migration.rs @@ -20,8 +20,8 @@ pub mod v0 { use frame_support::{storage_alias, Twox64Concat}; use frame_system::pallet_prelude::BlockNumberFor; use polkadot_primitives::{ - AvailabilityBitfield, CandidateCommitments, CandidateDescriptor, CandidateHash, CoreIndex, - GroupIndex, Id as ParaId, ValidatorIndex, + vstaging::CandidateDescriptorV2 as CandidateDescriptor, AvailabilityBitfield, + CandidateCommitments, CandidateHash, CoreIndex, GroupIndex, Id as ParaId, ValidatorIndex, }; use scale_info::TypeInfo; @@ -219,7 +219,7 @@ mod tests { use frame_support::traits::UncheckedOnRuntimeUpgrade; use polkadot_primitives::{AvailabilityBitfield, Id as ParaId}; use polkadot_primitives_test_helpers::{ - dummy_candidate_commitments, dummy_candidate_descriptor, dummy_hash, + dummy_candidate_commitments, dummy_candidate_descriptor_v2, dummy_hash, }; #[test] @@ -235,7 +235,7 @@ mod tests { let mut expected = vec![]; for i in 1..5 { - let descriptor = dummy_candidate_descriptor(dummy_hash()); + let descriptor = dummy_candidate_descriptor_v2(dummy_hash()); v0::PendingAvailability::::insert( ParaId::from(i), v0::CandidatePendingAvailability { @@ -285,7 +285,7 @@ mod tests { ParaId::from(6), v0::CandidatePendingAvailability { core: CoreIndex(6), - descriptor: dummy_candidate_descriptor(dummy_hash()), + descriptor: dummy_candidate_descriptor_v2(dummy_hash()), relay_parent_number: 6, hash: CandidateHash(dummy_hash()), availability_votes: Default::default(), diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index b1d22996fd12..e014529ea11a 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -44,11 +44,15 @@ use frame_support::{ use frame_system::pallet_prelude::*; use pallet_message_queue::OnQueueChanged; use polkadot_primitives::{ - effective_minimum_backing_votes, supermajority_threshold, well_known_keys, BackedCandidate, - CandidateCommitments, CandidateDescriptor, CandidateHash, CandidateReceipt, - CommittedCandidateReceipt, CoreIndex, GroupIndex, Hash, HeadData, Id as ParaId, - SignedAvailabilityBitfields, SigningContext, UpwardMessage, ValidatorId, ValidatorIndex, - ValidityAttestation, + effective_minimum_backing_votes, supermajority_threshold, + vstaging::{ + BackedCandidate, CandidateDescriptorV2 as CandidateDescriptor, + CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + }, + well_known_keys, CandidateCommitments, CandidateHash, CoreIndex, GroupIndex, Hash, HeadData, + Id as ParaId, SignedAvailabilityBitfields, SigningContext, UpwardMessage, ValidatorId, + ValidatorIndex, ValidityAttestation, }; use scale_info::TypeInfo; use sp_runtime::{traits::One, DispatchError, SaturatedConversion, Saturating}; @@ -764,7 +768,7 @@ impl Pallet { let mut backers = bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()]; let signing_context = SigningContext { - parent_hash: backed_candidate.descriptor().relay_parent, + parent_hash: backed_candidate.descriptor().relay_parent(), session_index: shared::CurrentSessionIndex::::get(), }; @@ -880,7 +884,7 @@ impl Pallet { let now = frame_system::Pallet::::block_number(); paras::Pallet::::schedule_code_upgrade( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), new_code, now, &config, @@ -890,19 +894,19 @@ impl Pallet { // enact the messaging facet of the candidate. dmp::Pallet::::prune_dmq( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.processed_downward_messages, ); Self::receive_upward_messages( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.upward_messages.as_slice(), ); hrmp::Pallet::::prune_hrmp( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), BlockNumberFor::::from(commitments.hrmp_watermark), ); hrmp::Pallet::::queue_outbound_hrmp( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.horizontal_messages, ); @@ -914,7 +918,7 @@ impl Pallet { )); paras::Pallet::::note_new_head( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.head_data, relay_parent_number, ); @@ -1250,8 +1254,8 @@ impl CandidateCheckContext { backed_candidate_receipt: &CommittedCandidateReceipt<::Hash>, parent_head_data: HeadData, ) -> Result, Error> { - let para_id = backed_candidate_receipt.descriptor().para_id; - let relay_parent = backed_candidate_receipt.descriptor().relay_parent; + let para_id = backed_candidate_receipt.descriptor.para_id(); + let relay_parent = backed_candidate_receipt.descriptor.relay_parent(); // Check that the relay-parent is one of the allowed relay-parents. let (relay_parent_storage_root, relay_parent_number) = { @@ -1271,7 +1275,7 @@ impl CandidateCheckContext { let expected = persisted_validation_data.hash(); ensure!( - expected == backed_candidate_receipt.descriptor().persisted_validation_data_hash, + expected == backed_candidate_receipt.descriptor.persisted_validation_data_hash(), Error::::ValidationDataHashMismatch, ); } @@ -1280,12 +1284,12 @@ impl CandidateCheckContext { // A candidate for a parachain without current validation code is not scheduled. .ok_or_else(|| Error::::UnscheduledCandidate)?; ensure!( - backed_candidate_receipt.descriptor().validation_code_hash == validation_code_hash, + backed_candidate_receipt.descriptor.validation_code_hash() == validation_code_hash, Error::::InvalidValidationCodeHash, ); ensure!( - backed_candidate_receipt.descriptor().para_head == + backed_candidate_receipt.descriptor.para_head() == backed_candidate_receipt.commitments.head_data.hash(), Error::::ParaHeadMismatch, ); diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 95fd66bf8e4f..59114e28be16 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -26,22 +26,21 @@ use crate::{ shared::AllowedRelayParentsTracker, }; use polkadot_primitives::{ - effective_minimum_backing_votes, AvailabilityBitfield, SignedAvailabilityBitfields, - UncheckedSignedAvailabilityBitfields, + effective_minimum_backing_votes, AvailabilityBitfield, CandidateDescriptor, + SignedAvailabilityBitfields, UncheckedSignedAvailabilityBitfields, }; use assert_matches::assert_matches; use codec::DecodeAll; use frame_support::assert_noop; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CandidateDescriptor, CollatorId, + BlockNumber, CandidateCommitments, CollatorId, CollatorSignature, CompactStatement as Statement, Hash, SignedAvailabilityBitfield, SignedStatement, ValidationCode, ValidatorId, ValidityAttestation, PARACHAIN_KEY_TYPE_ID, }; -use polkadot_primitives_test_helpers::{ - dummy_collator, dummy_collator_signature, dummy_validation_code, -}; +use polkadot_primitives_test_helpers::dummy_validation_code; use sc_keystore::LocalKeystore; +use sp_core::ByteArray; use sp_keyring::Sr25519Keyring; use sp_keystore::{Keystore, KeystorePtr}; use std::sync::Arc; @@ -96,24 +95,6 @@ pub(crate) enum BackingKind { Lacking, } -pub(crate) fn collator_sign_candidate( - collator: Sr25519Keyring, - candidate: &mut CommittedCandidateReceipt, -) { - candidate.descriptor.collator = collator.public().into(); - - let payload = polkadot_primitives::collator_signature_payload( - &candidate.descriptor.relay_parent, - &candidate.descriptor.para_id, - &candidate.descriptor.persisted_validation_data_hash, - &candidate.descriptor.pov_hash, - &candidate.descriptor.validation_code_hash, - ); - - candidate.descriptor.signature = collator.sign(&payload[..]).into(); - assert!(candidate.descriptor().check_collator_signature().is_ok()); -} - pub(crate) fn back_candidate( candidate: CommittedCandidateReceipt, validators: &[Sr25519Keyring], @@ -311,9 +292,16 @@ impl TestCandidateBuilder { validation_code_hash: self.validation_code.hash(), para_head: self.para_head_hash.unwrap_or_else(|| self.head_data.hash()), erasure_root: Default::default(), - signature: dummy_collator_signature(), - collator: dummy_collator(), - }, + signature: CollatorSignature::from_slice( + &mut (0..64).into_iter().collect::>().as_slice(), + ) + .expect("64 bytes; qed"), + collator: CollatorId::from_slice( + &mut (0..32).into_iter().collect::>().as_slice(), + ) + .expect("32 bytes; qed"), + } + .into(), commitments: CandidateCommitments { head_data: self.head_data, new_validation_code: self.new_validation_code, @@ -1244,7 +1232,7 @@ fn candidate_checks() { // Check candidate ordering { - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1253,7 +1241,7 @@ fn candidate_checks() { ..Default::default() } .build(); - let mut candidate_b_1 = TestCandidateBuilder { + let candidate_b_1 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -1265,7 +1253,7 @@ fn candidate_checks() { .build(); // Make candidate b2 a child of b1. - let mut candidate_b_2 = TestCandidateBuilder { + let candidate_b_2 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -1281,10 +1269,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_1); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_2); - let backed_a = back_candidate( candidate_a, &validators, @@ -1356,7 +1340,7 @@ fn candidate_checks() { // candidate does not build on top of the latest unincluded head - let mut candidate_b_3 = TestCandidateBuilder { + let candidate_b_3 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(4), @@ -1371,7 +1355,6 @@ fn candidate_checks() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_3); let backed_b_3 = back_candidate( candidate_b_3, @@ -1396,7 +1379,7 @@ fn candidate_checks() { // candidate not backed. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1405,7 +1388,6 @@ fn candidate_checks() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); // Insufficient backing. let backed = back_candidate( @@ -1459,7 +1441,7 @@ fn candidate_checks() { let wrong_parent_hash = Hash::repeat_byte(222); assert!(System::parent_hash() != wrong_parent_hash); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: wrong_parent_hash, pov_hash: Hash::repeat_byte(1), @@ -1468,7 +1450,7 @@ fn candidate_checks() { } .build(); - let mut candidate_b = TestCandidateBuilder { + let candidate_b = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -1478,10 +1460,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b); - let backed_a = back_candidate( candidate_a, &validators, @@ -1531,10 +1509,9 @@ fn candidate_checks() { .build(); assert_eq!(CollatorId::from(Sr25519Keyring::Two.public()), thread_collator); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate); // change the candidate after signing. - candidate.descriptor.pov_hash = Hash::repeat_byte(2); + candidate.descriptor.set_pov_hash(Hash::repeat_byte(2)); let backed = back_candidate( candidate, @@ -1597,7 +1574,7 @@ fn candidate_checks() { // interfering code upgrade - reject { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1608,8 +1585,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1648,7 +1623,7 @@ fn candidate_checks() { // Bad validation data hash - reject { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1658,8 +1633,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1685,7 +1658,7 @@ fn candidate_checks() { // bad validation code hash { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1696,8 +1669,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1723,7 +1694,7 @@ fn candidate_checks() { // Para head hash in descriptor doesn't match head data { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1734,8 +1705,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1826,7 +1795,7 @@ fn backing_works() { let chain_b_assignment = (chain_b, CoreIndex::from(1)); let thread_a_assignment = (thread_a, CoreIndex::from(2)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1835,9 +1804,8 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - let mut candidate_b = TestCandidateBuilder { + let candidate_b = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -1846,9 +1814,8 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b); - let mut candidate_c = TestCandidateBuilder { + let candidate_c = TestCandidateBuilder { para_id: thread_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -1857,7 +1824,6 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_c); let backed_a = back_candidate( candidate_a.clone(), @@ -1978,7 +1944,7 @@ fn backing_works() { Vec<(ValidatorIndex, ValidityAttestation)>, )>| { candidate_receipts_with_backers.sort_by(|(cr1, _), (cr2, _)| { - cr1.descriptor().para_id.cmp(&cr2.descriptor().para_id) + cr1.descriptor().para_id().cmp(&cr2.descriptor().para_id()) }); candidate_receipts_with_backers }; @@ -2121,7 +2087,7 @@ fn backing_works_with_elastic_scaling_mvp() { let allowed_relay_parents = default_allowed_relay_parent_tracker(); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -2130,9 +2096,8 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - let mut candidate_b_1 = TestCandidateBuilder { + let candidate_b_1 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -2141,10 +2106,9 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b_1); // Make candidate b2 a child of b1. - let mut candidate_b_2 = TestCandidateBuilder { + let candidate_b_2 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -2158,7 +2122,6 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b_2); let backed_a = back_candidate( candidate_a.clone(), @@ -2396,7 +2359,7 @@ fn can_include_candidate_with_ok_code_upgrade() { let allowed_relay_parents = default_allowed_relay_parent_tracker(); let chain_a_assignment = (chain_a, CoreIndex::from(0)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -2406,7 +2369,6 @@ fn can_include_candidate_with_ok_code_upgrade() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let backed_a = back_candidate( candidate_a.clone(), @@ -2556,7 +2518,7 @@ fn check_allowed_relay_parents() { let chain_b_assignment = (chain_b, CoreIndex::from(1)); let thread_a_assignment = (thread_a, CoreIndex::from(2)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: relay_parent_a.1, pov_hash: Hash::repeat_byte(1), @@ -2569,10 +2531,9 @@ fn check_allowed_relay_parents() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let signing_context_a = SigningContext { parent_hash: relay_parent_a.1, session_index: 5 }; - let mut candidate_b = TestCandidateBuilder { + let candidate_b = TestCandidateBuilder { para_id: chain_b, relay_parent: relay_parent_b.1, pov_hash: Hash::repeat_byte(2), @@ -2585,10 +2546,9 @@ fn check_allowed_relay_parents() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b); let signing_context_b = SigningContext { parent_hash: relay_parent_b.1, session_index: 5 }; - let mut candidate_c = TestCandidateBuilder { + let candidate_c = TestCandidateBuilder { para_id: thread_a, relay_parent: relay_parent_c.1, pov_hash: Hash::repeat_byte(3), @@ -2601,7 +2561,6 @@ fn check_allowed_relay_parents() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_c); let signing_context_c = SigningContext { parent_hash: relay_parent_c.1, session_index: 5 }; let backed_a = back_candidate( @@ -2823,7 +2782,7 @@ fn para_upgrade_delay_scheduled_from_inclusion() { let allowed_relay_parents = default_allowed_relay_parent_tracker(); let chain_a_assignment = (chain_a, CoreIndex::from(0)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -2833,7 +2792,6 @@ fn para_upgrade_delay_scheduled_from_inclusion() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let backed_a = back_candidate( candidate_a.clone(), diff --git a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs index 1742e91276d4..fa466de11987 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs @@ -144,8 +144,8 @@ benchmarks! { // Traverse candidates and assert descriptors are as expected for (para_id, backing_validators) in vote.backing_validators_per_candidate.iter().enumerate() { let descriptor = backing_validators.0.descriptor(); - assert_eq!(ParaId::from(para_id), descriptor.para_id); - assert_eq!(header.hash(), descriptor.relay_parent); + assert_eq!(ParaId::from(para_id), descriptor.para_id()); + assert_eq!(header.hash(), descriptor.relay_parent()); assert_eq!(backing_validators.1.len(), votes); } @@ -203,8 +203,8 @@ benchmarks! { for (para_id, backing_validators) in vote.backing_validators_per_candidate.iter().enumerate() { let descriptor = backing_validators.0.descriptor(); - assert_eq!(ParaId::from(para_id), descriptor.para_id); - assert_eq!(header.hash(), descriptor.relay_parent); + assert_eq!(ParaId::from(para_id), descriptor.para_id()); + assert_eq!(header.hash(), descriptor.relay_parent()); assert_eq!( backing_validators.1.len(), votes, diff --git a/polkadot/runtime/parachains/src/paras_inherent/mod.rs b/polkadot/runtime/parachains/src/paras_inherent/mod.rs index bd8d08a842c3..84d8299cd29c 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/mod.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/mod.rs @@ -48,12 +48,17 @@ use frame_support::{ use frame_system::pallet_prelude::*; use pallet_babe::{self, ParentBlockRandomness}; use polkadot_primitives::{ - effective_minimum_backing_votes, node_features::FeatureIndex, BackedCandidate, CandidateHash, - CandidateReceipt, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, - DisputeStatementSet, HeadData, InherentData as ParachainsInherentData, - MultiDisputeStatementSet, ScrapedOnChainVotes, SessionIndex, SignedAvailabilityBitfields, - SigningContext, UncheckedSignedAvailabilityBitfield, UncheckedSignedAvailabilityBitfields, - ValidatorId, ValidatorIndex, ValidityAttestation, PARACHAINS_INHERENT_IDENTIFIER, + effective_minimum_backing_votes, + node_features::FeatureIndex, + vstaging::{ + BackedCandidate, CandidateDescriptorVersion, CandidateReceiptV2 as CandidateReceipt, + InherentData as ParachainsInherentData, ScrapedOnChainVotes, + }, + CandidateHash, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, + DisputeStatementSet, HeadData, MultiDisputeStatementSet, SessionIndex, + SignedAvailabilityBitfields, SigningContext, UncheckedSignedAvailabilityBitfield, + UncheckedSignedAvailabilityBitfields, ValidatorId, ValidatorIndex, ValidityAttestation, + PARACHAINS_INHERENT_IDENTIFIER, }; use rand::{seq::SliceRandom, SeedableRng}; use scale_info::TypeInfo; @@ -594,6 +599,12 @@ impl Pallet { .map(|b| *b) .unwrap_or(false); + let allow_v2_receipts = configuration::ActiveConfig::::get() + .node_features + .get(FeatureIndex::CandidateReceiptV2 as usize) + .map(|b| *b) + .unwrap_or(false); + let mut eligible: BTreeMap> = BTreeMap::new(); let mut total_eligible_cores = 0; @@ -610,6 +621,7 @@ impl Pallet { concluded_invalid_hashes, eligible, core_index_enabled, + allow_v2_receipts, ); let count = count_backed_candidates(&backed_candidates_with_core); @@ -787,7 +799,7 @@ pub(crate) fn apply_weight_limit( let mut current_para_id = None; for candidate in core::mem::take(candidates).into_iter() { - let candidate_para_id = candidate.descriptor().para_id; + let candidate_para_id = candidate.descriptor().para_id(); if Some(candidate_para_id) == current_para_id { let chain = chained_candidates .last_mut() @@ -966,14 +978,15 @@ pub(crate) fn sanitize_bitfields( /// subsequent candidates after the filtered one. /// /// Filter out: -/// 1. any candidates which don't form a chain with the other candidates of the paraid (even if they +/// 1. Candidates that have v2 descriptors if the node `CandidateReceiptV2` feature is not enabled. +/// 2. any candidates which don't form a chain with the other candidates of the paraid (even if they /// do form a chain but are not in the right order). -/// 2. any candidates that have a concluded invalid dispute or who are descendants of a concluded +/// 3. any candidates that have a concluded invalid dispute or who are descendants of a concluded /// invalid candidate. -/// 3. any unscheduled candidates, as well as candidates whose paraid has multiple cores assigned +/// 4. any unscheduled candidates, as well as candidates whose paraid has multiple cores assigned /// but have no injected core index. -/// 4. all backing votes from disabled validators -/// 5. any candidates that end up with less than `effective_minimum_backing_votes` backing votes +/// 5. all backing votes from disabled validators +/// 6. any candidates that end up with less than `effective_minimum_backing_votes` backing votes /// /// Returns the scheduled /// backed candidates which passed filtering, mapped by para id and in the right dependency order. @@ -983,13 +996,28 @@ fn sanitize_backed_candidates( concluded_invalid_with_descendants: BTreeSet, scheduled: BTreeMap>, core_index_enabled: bool, + allow_v2_receipts: bool, ) -> BTreeMap, CoreIndex)>> { // Map the candidates to the right paraids, while making sure that the order between candidates // of the same para is preserved. let mut candidates_per_para: BTreeMap> = BTreeMap::new(); for candidate in backed_candidates { + // Drop any v2 candidate receipts if nodes are not allowed to use them. + // It is mandatory to filter these before calling `filter_unchained_candidates` to ensure + // any v1 descendants of v2 candidates are dropped. + if !allow_v2_receipts && candidate.descriptor().version() == CandidateDescriptorVersion::V2 + { + log::debug!( + target: LOG_TARGET, + "V2 candidate descriptors not allowed. Dropping candidate {:?} for paraid {:?}.", + candidate.candidate().hash(), + candidate.descriptor().para_id() + ); + continue + } + candidates_per_para - .entry(candidate.descriptor().para_id) + .entry(candidate.descriptor().para_id()) .or_default() .push(candidate); } @@ -1008,7 +1036,7 @@ fn sanitize_backed_candidates( target: LOG_TARGET, "Found backed candidate {:?} which was concluded invalid or is a descendant of a concluded invalid candidate, for paraid {:?}.", candidate.candidate().hash(), - candidate.descriptor().para_id + candidate.descriptor().para_id() ); } keep @@ -1189,13 +1217,13 @@ fn filter_backed_statements_from_disabled_validators< // Get relay parent block number of the candidate. We need this to get the group index // assigned to this core at this block number let relay_parent_block_number = - match allowed_relay_parents.acquire_info(bc.descriptor().relay_parent, None) { + match allowed_relay_parents.acquire_info(bc.descriptor().relay_parent(), None) { Some((_, block_num)) => block_num, None => { log::debug!( target: LOG_TARGET, "Relay parent {:?} for candidate is not in the allowed relay parents. Dropping the candidate.", - bc.descriptor().relay_parent + bc.descriptor().relay_parent() ); return false }, @@ -1396,7 +1424,7 @@ fn map_candidates_to_cores block_num, None => { log::debug!( target: LOG_TARGET, "Relay parent {:?} for candidate {:?} is not in the allowed relay parents.", - candidate.descriptor().relay_parent, + candidate.descriptor().relay_parent(), candidate.candidate().hash(), ); return None diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index e34055bfa9f2..ac42ac1611df 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -58,7 +58,9 @@ mod enter { use core::panic; use frame_support::assert_ok; use frame_system::limits; - use polkadot_primitives::{AvailabilityBitfield, SchedulerParams, UncheckedSigned}; + use polkadot_primitives::{ + vstaging::InternalVersion, AvailabilityBitfield, SchedulerParams, UncheckedSigned, + }; use sp_runtime::Perbill; struct TestConfig { @@ -70,6 +72,7 @@ mod enter { fill_claimqueue: bool, elastic_paras: BTreeMap, unavailable_cores: Vec, + v2_descriptor: bool, } fn make_inherent_data( @@ -82,6 +85,7 @@ mod enter { fill_claimqueue, elastic_paras, unavailable_cores, + v2_descriptor, }: TestConfig, ) -> Bench { let extra_cores = elastic_paras @@ -99,7 +103,8 @@ mod enter { .set_backed_and_concluding_paras(backed_and_concluding.clone()) .set_dispute_sessions(&dispute_sessions[..]) .set_fill_claimqueue(fill_claimqueue) - .set_unavailable_cores(unavailable_cores); + .set_unavailable_cores(unavailable_cores) + .set_candidate_descriptor_v2(v2_descriptor); // Setup some assignments as needed: mock_assigner::Pallet::::set_core_count(builder.max_cores()); @@ -145,6 +150,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); // We expect the scenario to have cores 0 & 1 with pending availability. The backed @@ -240,6 +246,7 @@ mod enter { fill_claimqueue: false, elastic_paras: [(2, 3)].into_iter().collect(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -344,6 +351,7 @@ mod enter { fill_claimqueue: true, elastic_paras: [(2, 4)].into_iter().collect(), unavailable_cores: unavailable_cores.clone(), + v2_descriptor: false, }); let mut expected_para_inherent_data = scenario.data.clone(); @@ -600,6 +608,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -673,6 +682,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -744,6 +754,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -831,6 +842,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -918,6 +930,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -977,6 +990,7 @@ mod enter { fill_claimqueue: true, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1063,6 +1077,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1170,6 +1185,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1238,6 +1254,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1304,6 +1321,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1407,6 +1425,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let mut para_inherent_data = scenario.data.clone(); @@ -1440,7 +1459,7 @@ mod enter { // The chained candidates are not picked, instead a single other candidate is picked assert_eq!(backed_candidates.len(), 1); - assert_ne!(backed_candidates[0].descriptor().para_id, ParaId::from(1000)); + assert_ne!(backed_candidates[0].descriptor().para_id(), ParaId::from(1000)); // All bitfields are kept. assert_eq!(bitfields.len(), 150); @@ -1461,9 +1480,9 @@ mod enter { // Only the chained candidates should pass filter. assert_eq!(backed_candidates.len(), 3); // Check the actual candidates - assert_eq!(backed_candidates[0].descriptor().para_id, ParaId::from(1000)); - assert_eq!(backed_candidates[1].descriptor().para_id, ParaId::from(1000)); - assert_eq!(backed_candidates[2].descriptor().para_id, ParaId::from(1000)); + assert_eq!(backed_candidates[0].descriptor().para_id(), ParaId::from(1000)); + assert_eq!(backed_candidates[1].descriptor().para_id(), ParaId::from(1000)); + assert_eq!(backed_candidates[2].descriptor().para_id(), ParaId::from(1000)); // All bitfields are kept. assert_eq!(bitfields.len(), 150); @@ -1496,6 +1515,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1524,6 +1544,76 @@ mod enter { assert_eq!(dispatch_error, Error::::InherentOverweight.into()); }); } + + #[test] + fn v2_descriptors_are_filtered() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 5, + code_upgrade: None, + fill_claimqueue: true, + // 8 cores ! + elastic_paras: [(2, 8)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + }); + + let mut unfiltered_para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (5 validators per core, 10 backed candidates) + assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 50); + // * 10 v2 candidate descriptors. + assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10); + + // Make the last candidate look like v1, by using an unknown version. + unfiltered_para_inherent_data.backed_candidates[9] + .descriptor_mut() + .set_version(InternalVersion(123)); + + let mut inherent_data = InherentData::new(); + inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &unfiltered_para_inherent_data) + .unwrap(); + + // We expect all backed candidates to be filtered out. + let filtered_para_inherend_data = + Pallet::::create_inherent_inner(&inherent_data).unwrap(); + + assert_eq!(filtered_para_inherend_data.backed_candidates.len(), 0); + + let dispatch_error = Pallet::::enter( + frame_system::RawOrigin::None.into(), + unfiltered_para_inherent_data, + ) + .unwrap_err() + .error; + + // We expect `enter` to fail because the inherent data contains backed candidates with + // v2 descriptors. + assert_eq!(dispatch_error, Error::::CandidatesFilteredDuringExecution.into()); + }); + } } fn default_header() -> polkadot_primitives::Header { @@ -1540,9 +1630,7 @@ mod sanitizers { use super::*; use crate::{ - inclusion::tests::{ - back_candidate, collator_sign_candidate, BackingKind, TestCandidateBuilder, - }, + inclusion::tests::{back_candidate, BackingKind, TestCandidateBuilder}, mock::new_test_ext, }; use bitvec::order::Lsb0; @@ -1556,7 +1644,6 @@ mod sanitizers { use crate::mock::Test; use polkadot_primitives::PARACHAIN_KEY_TYPE_ID; use sc_keystore::LocalKeystore; - use sp_keyring::Sr25519Keyring; use sp_keystore::{Keystore, KeystorePtr}; use std::sync::Arc; @@ -1940,7 +2027,7 @@ mod sanitizers { .into_iter() .map(|idx0| { let idx1 = idx0 + 1; - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(idx1), relay_parent, pov_hash: Hash::repeat_byte(idx1 as u8), @@ -1957,8 +2044,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1991,7 +2076,7 @@ mod sanitizers { let mut expected_backed_candidates_with_core = BTreeMap::new(); for candidate in backed_candidates.iter() { - let para_id = candidate.descriptor().para_id; + let para_id = candidate.descriptor().para_id(); expected_backed_candidates_with_core.entry(para_id).or_insert(vec![]).push(( candidate.clone(), @@ -2177,7 +2262,7 @@ mod sanitizers { // Para 1 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -2195,8 +2280,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -2215,7 +2298,7 @@ mod sanitizers { .push((backed, CoreIndex(0))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -2233,8 +2316,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2255,7 +2336,7 @@ mod sanitizers { // Para 2 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2272,8 +2353,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2294,7 +2373,7 @@ mod sanitizers { // Para 3 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(3), relay_parent, pov_hash: Hash::repeat_byte(4 as u8), @@ -2311,8 +2390,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2331,7 +2408,7 @@ mod sanitizers { // Para 4 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(4), relay_parent, pov_hash: Hash::repeat_byte(5 as u8), @@ -2348,8 +2425,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -2366,7 +2441,7 @@ mod sanitizers { .or_insert(vec![]) .push((backed, CoreIndex(5))); - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(4), relay_parent, pov_hash: Hash::repeat_byte(6 as u8), @@ -2384,8 +2459,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2402,7 +2475,7 @@ mod sanitizers { // Para 6. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(6), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2419,8 +2492,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2435,7 +2506,7 @@ mod sanitizers { // Para 7. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(7), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2452,8 +2523,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2468,7 +2537,7 @@ mod sanitizers { // Para 8. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(8), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2485,8 +2554,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2707,7 +2774,7 @@ mod sanitizers { // Para 1 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -2725,8 +2792,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let prev_backed: BackedCandidate = back_candidate( candidate, @@ -2738,7 +2803,7 @@ mod sanitizers { core_index_enabled.then_some(CoreIndex(0 as u32)), ); - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -2756,8 +2821,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2773,7 +2836,7 @@ mod sanitizers { // Para 2. { - let mut candidate_1 = TestCandidateBuilder { + let candidate_1 = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2791,8 +2854,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_1); - let backed_1: BackedCandidate = back_candidate( candidate_1, &validators, @@ -2811,7 +2872,7 @@ mod sanitizers { .push((backed_1, CoreIndex(2))); } - let mut candidate_2 = TestCandidateBuilder { + let candidate_2 = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(4 as u8), @@ -2829,8 +2890,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_2); - let backed_2 = back_candidate( candidate_2.clone(), &validators, @@ -2842,7 +2901,7 @@ mod sanitizers { ); backed_candidates.push(backed_2.clone()); - let mut candidate_3 = TestCandidateBuilder { + let candidate_3 = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(5 as u8), @@ -2860,8 +2919,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_3); - let backed_3 = back_candidate( candidate_3, &validators, @@ -2876,7 +2933,7 @@ mod sanitizers { // Para 3 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(3), relay_parent, pov_hash: Hash::repeat_byte(6 as u8), @@ -2894,8 +2951,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -2914,7 +2969,7 @@ mod sanitizers { .push((backed, CoreIndex(5))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(3), relay_parent, pov_hash: Hash::repeat_byte(6 as u8), @@ -2932,8 +2987,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2954,7 +3007,7 @@ mod sanitizers { // Para 4 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(4), relay_parent, pov_hash: Hash::repeat_byte(8 as u8), @@ -2972,8 +3025,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed: BackedCandidate = back_candidate( candidate.clone(), &validators, @@ -3210,7 +3261,7 @@ mod sanitizers { // Para 1 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -3228,8 +3279,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -3248,7 +3297,7 @@ mod sanitizers { .push((backed, CoreIndex(0))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent: prev_relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -3267,8 +3316,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -3281,7 +3328,7 @@ mod sanitizers { ); backed_candidates.push(backed.clone()); - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -3300,8 +3347,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -3316,7 +3361,7 @@ mod sanitizers { // Para 2 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent: prev_relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -3334,8 +3379,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -3354,7 +3397,7 @@ mod sanitizers { .push((backed, CoreIndex(3))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -3373,8 +3416,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -3393,7 +3434,7 @@ mod sanitizers { .push((backed, CoreIndex(4))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -3412,8 +3453,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -3486,7 +3525,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), BTreeSet::new(), scheduled, - core_index_enabled + core_index_enabled, + false, ), expected_backed_candidates_with_core, ); @@ -3510,7 +3550,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), BTreeSet::new(), scheduled, - core_index_enabled + core_index_enabled, + false, ), expected_backed_candidates_with_core, ); @@ -3535,6 +3576,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ), expected_backed_candidates_with_core ); @@ -3567,6 +3609,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ), expected_backed_candidates_with_core ); @@ -3607,6 +3650,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); if core_index_enabled { @@ -3677,6 +3721,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); if core_index_enabled { @@ -3715,6 +3760,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); assert!(sanitized_backed_candidates.is_empty()); @@ -3751,6 +3797,7 @@ mod sanitizers { set, scheduled, core_index_enabled, + false, ); assert_eq!(sanitized_backed_candidates.len(), backed_candidates.len() / 2); @@ -3773,9 +3820,9 @@ mod sanitizers { let mut invalid_set = std::collections::BTreeSet::new(); for (idx, backed_candidate) in backed_candidates.iter().enumerate() { - if backed_candidate.descriptor().para_id == ParaId::from(1) && idx == 0 { + if backed_candidate.descriptor().para_id() == ParaId::from(1) && idx == 0 { invalid_set.insert(backed_candidate.hash()); - } else if backed_candidate.descriptor().para_id == ParaId::from(3) { + } else if backed_candidate.descriptor().para_id() == ParaId::from(3) { invalid_set.insert(backed_candidate.hash()); } } @@ -3788,6 +3835,7 @@ mod sanitizers { invalid_set, scheduled, true, + false, ); // We'll be left with candidates from paraid 2 and 4. @@ -3811,7 +3859,7 @@ mod sanitizers { let mut invalid_set = std::collections::BTreeSet::new(); for (idx, backed_candidate) in backed_candidates.iter().enumerate() { - if backed_candidate.descriptor().para_id == ParaId::from(1) && idx == 1 { + if backed_candidate.descriptor().para_id() == ParaId::from(1) && idx == 1 { invalid_set.insert(backed_candidate.hash()); } } @@ -3824,6 +3872,7 @@ mod sanitizers { invalid_set, scheduled, true, + false, ); // Only the second candidate of paraid 1 should be removed. diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs index 697890232211..ead825b38f07 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs @@ -27,15 +27,19 @@ use frame_support::traits::{GetStorageVersion, StorageVersion}; use frame_system::pallet_prelude::*; use polkadot_primitives::{ async_backing::{ - AsyncBackingParams, BackingState, CandidatePendingAvailability, Constraints, - InboundHrmpLimitations, OutboundHrmpChannelLimitations, + AsyncBackingParams, Constraints, InboundHrmpLimitations, OutboundHrmpChannelLimitations, }, - slashing, ApprovalVotingParams, AuthorityDiscoveryId, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, GroupIndex, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - NodeFeatures, OccupiedCore, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + slashing, + vstaging::{ + async_backing::{BackingState, CandidatePendingAvailability}, + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + OccupiedCore, ScrapedOnChainVotes, + }, + ApprovalVotingParams, AuthorityDiscoveryId, CandidateHash, CoreIndex, DisputeState, + ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, + PvfCheckStatement, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, + ValidatorIndex, ValidatorSignature, }; use sp_runtime::traits::One; diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs index 4aa381e33b1b..a3440f686e94 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs @@ -21,7 +21,9 @@ use alloc::{ collections::{btree_map::BTreeMap, vec_deque::VecDeque}, vec::Vec, }; -use polkadot_primitives::{CommittedCandidateReceipt, CoreIndex, Id as ParaId}; +use polkadot_primitives::{ + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreIndex, Id as ParaId, +}; use sp_runtime::traits::One; /// Returns the claimqueue from the scheduler diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index dfc41b15bb1d..6b046e190830 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -35,12 +35,16 @@ use frame_support::{ }; use pallet_nis::WithMaximumOf; use polkadot_primitives::{ - slashing, AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateEvent, - CandidateHash, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, - NodeFeatures, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, - SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - PARACHAIN_KEY_TYPE_ID, + slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateHash, CoreIndex, + DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, NodeFeatures, Nonce, OccupiedCoreAssumption, + PersistedValidationData, SessionInfo, Signature, ValidationCode, ValidationCodeHash, + ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, }; use polkadot_runtime_common::{ assigned_slots, auctions, claims, crowdloan, identity_migrator, impl_runtime_weights, @@ -2032,7 +2036,7 @@ sp_api::impl_runtime_apis! { parachains_runtime_api_impl::minimum_backing_votes::() } - fn para_backing_state(para_id: ParaId) -> Option { + fn para_backing_state(para_id: ParaId) -> Option { parachains_runtime_api_impl::backing_state::(para_id) } diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 8e34320d38f2..72d024e9a878 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -59,10 +59,14 @@ use pallet_session::historical as session_historical; use pallet_timestamp::Now; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_primitives::{ - slashing, AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash as HashT, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, + slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AccountIndex, Balance, BlockNumber, CandidateHash, CoreIndex, DisputeState, + ExecutorParams, GroupRotationInfo, Hash as HashT, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, SessionInfo as SessionInfoData, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, }; @@ -978,7 +982,7 @@ sp_api::impl_runtime_apis! { runtime_impl::minimum_backing_votes::() } - fn para_backing_state(para_id: ParaId) -> Option { + fn para_backing_state(para_id: ParaId) -> Option { runtime_impl::backing_state::(para_id) } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index e8fe11615d74..b02c2d8c671e 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -49,12 +49,16 @@ use pallet_identity::legacy::IdentityInfo; use pallet_session::historical as session_historical; use pallet_transaction_payment::{FeeDetails, FungibleAdapter, RuntimeDispatchInfo}; use polkadot_primitives::{ - slashing, AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateEvent, - CandidateHash, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, - NodeFeatures, Nonce, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, - ValidatorIndex, ValidatorSignature, PARACHAIN_KEY_TYPE_ID, + slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateHash, CoreIndex, + DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, NodeFeatures, Nonce, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, SessionInfo, Signature, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, PARACHAIN_KEY_TYPE_ID, }; use polkadot_runtime_common::{ assigned_slots, auctions, crowdloan, @@ -2064,7 +2068,7 @@ sp_api::impl_runtime_apis! { parachains_runtime_api_impl::minimum_backing_votes::() } - fn para_backing_state(para_id: ParaId) -> Option { + fn para_backing_state(para_id: ParaId) -> Option { parachains_runtime_api_impl::backing_state::(para_id) } diff --git a/prdoc/pr_5322.prdoc b/prdoc/pr_5322.prdoc new file mode 100644 index 000000000000..b4cf261f33a4 --- /dev/null +++ b/prdoc/pr_5322.prdoc @@ -0,0 +1,30 @@ +title: Elastic scaling - introduce new candidate receipt primitive + +doc: + - audience: [Runtime Dev, Node Dev] + description: | + Introduces `CandidateDescriptorV2` primitive as described in [RFC 103](https://github.com/polkadot-fellows/RFCs/pull/103). + Updates parachains runtime, Westend, Rococo and test runtimes to use the new primitives. + This change does not implement the functionality of the new candidate receipts. + +crates: +- name: polkadot-primitives + bump: minor +- name: polkadot-primitives-test-helpers + bump: minor +- name: polkadot-runtime-parachains + bump: major +- name: rococo-runtime + bump: major +- name: westend-runtime + bump: major +- name: polkadot-test-runtime + bump: major +- name: polkadot-service + bump: patch +- name: polkadot-node-subsystem-types + bump: patch +- name: polkadot-test-client + bump: major +- name: cumulus-relay-chain-inprocess-interface + bump: patch From 383550737edee0b722af8ddf5a20a9c32aa0547d Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 2 Sep 2024 21:10:24 +0200 Subject: [PATCH 222/382] [pallet-revive] Use address20 for contract's address (#5548) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Theißen Co-authored-by: command-bot <> --- Cargo.lock | 11 +- prdoc/pr_5548.prdoc | 16 + substrate/bin/node/runtime/src/lib.rs | 20 +- substrate/frame/revive/Cargo.toml | 12 +- substrate/frame/revive/fixtures/Cargo.toml | 10 +- .../frame/revive/fixtures/contracts/call.rs | 2 +- .../fixtures/contracts/call_return_code.rs | 2 +- .../contracts/call_runtime_and_call.rs | 2 +- .../contracts/call_with_flags_and_value.rs | 2 +- .../fixtures/contracts/call_with_limit.rs | 2 +- .../fixtures/contracts/caller_contract.rs | 6 +- .../contracts/create_storage_and_call.rs | 2 +- .../create_storage_and_instantiate.rs | 4 +- .../create_transient_storage_and_call.rs | 2 +- .../fixtures/contracts/delegate_call_lib.rs | 4 +- .../contracts/destroy_and_transfer.rs | 6 +- .../contracts/locking_delegate_dependency.rs | 4 +- .../fixtures/contracts/read_only_call.rs | 2 +- .../fixtures/contracts/self_destruct.rs | 6 +- substrate/frame/revive/fixtures/src/lib.rs | 12 +- .../src/parachain/contracts_config.rs | 2 +- substrate/frame/revive/src/address.rs | 145 ++- .../revive/src/benchmarking/call_builder.rs | 10 +- .../frame/revive/src/benchmarking/code.rs | 14 +- .../frame/revive/src/benchmarking/mod.rs | 119 +- substrate/frame/revive/src/debug.rs | 13 +- substrate/frame/revive/src/exec.rs | 896 +++++++------ substrate/frame/revive/src/lib.rs | 127 +- substrate/frame/revive/src/primitives.rs | 22 +- substrate/frame/revive/src/storage.rs | 23 +- substrate/frame/revive/src/storage/meter.rs | 12 +- substrate/frame/revive/src/test_utils.rs | 28 + .../frame/revive/src/test_utils/builder.rs | 47 +- substrate/frame/revive/src/tests.rs | 1146 +++++++++-------- .../frame/revive/src/tests/test_debug.rs | 47 +- substrate/frame/revive/src/wasm/mod.rs | 30 +- substrate/frame/revive/src/wasm/runtime.rs | 45 +- 37 files changed, 1522 insertions(+), 1331 deletions(-) create mode 100644 prdoc/pr_5548.prdoc diff --git a/Cargo.lock b/Cargo.lock index 2d4485c92211..0d41be5c9bd1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11472,6 +11472,7 @@ dependencies = [ "paste", "polkavm 0.10.0", "pretty_assertions", + "rlp", "scale-info", "serde", "sp-api", @@ -11494,6 +11495,8 @@ dependencies = [ "frame-system", "parity-wasm", "polkavm-linker 0.10.0", + "sp-core", + "sp-io", "sp-runtime", "tempfile", "toml 0.8.12", @@ -16924,9 +16927,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.11.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608a5726529f2f0ef81b8fde9873c4bb829d6b5b5ca6be4d97345ddf0749c825" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -16948,9 +16951,9 @@ dependencies = [ [[package]] name = "ruint-macro" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rustc-demangle" diff --git a/prdoc/pr_5548.prdoc b/prdoc/pr_5548.prdoc new file mode 100644 index 000000000000..69e79213fa29 --- /dev/null +++ b/prdoc/pr_5548.prdoc @@ -0,0 +1,16 @@ +title: Use H160 when interfacing with contracts + +doc: + - audience: Runtime Dev + description: | + When interfacing with a contract we now use the native ethereum address + type and map it to AccountId32 when interfacing with the rest + of substrate. + +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-fixtures + bump: major + - name: pallet-revive-mock-network + bump: major diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index ef5c52bf6e6e..7ef0779dd9b8 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -91,7 +91,7 @@ use sp_consensus_beefy::{ mmr::MmrLeafVersion, }; use sp_consensus_grandpa::AuthorityId as GrandpaId; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ create_runtime_str, @@ -1392,7 +1392,7 @@ impl pallet_revive::Config for Runtime { type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_revive::weights::SubstrateWeight; type ChainExtension = (); - type AddressGenerator = pallet_revive::DefaultAddressGenerator; + type AddressMapper = pallet_revive::DefaultAddressMapper; type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; @@ -2988,11 +2988,11 @@ impl_runtime_apis! { } } - impl pallet_revive::ReviveApi for Runtime + impl pallet_revive::ReviveApi for Runtime { fn call( origin: AccountId, - dest: AccountId, + dest: H160, value: Balance, gas_limit: Option, storage_deposit_limit: Option, @@ -3015,10 +3015,10 @@ impl_runtime_apis! { value: Balance, gas_limit: Option, storage_deposit_limit: Option, - code: pallet_revive::Code, + code: pallet_revive::Code, data: Vec, - salt: Vec, - ) -> pallet_revive::ContractInstantiateResult + salt: [u8; 32], + ) -> pallet_revive::ContractInstantiateResult { Revive::bare_instantiate( RuntimeOrigin::signed(origin), @@ -3037,7 +3037,7 @@ impl_runtime_apis! { origin: AccountId, code: Vec, storage_deposit_limit: Option, - ) -> pallet_revive::CodeUploadResult + ) -> pallet_revive::CodeUploadResult { Revive::bare_upload_code( RuntimeOrigin::signed(origin), @@ -3047,8 +3047,8 @@ impl_runtime_apis! { } fn get_storage( - address: AccountId, - key: Vec, + address: H160, + key: [u8; 32], ) -> pallet_revive::GetStorageResult { Revive::get_storage( address, diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml index 747c2283e217..6b7542e89202 100644 --- a/substrate/frame/revive/Cargo.toml +++ b/substrate/frame/revive/Cargo.toml @@ -18,6 +18,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] +environmental = { workspace = true } paste = { workspace = true } polkavm = { version = "0.10.0", default-features = false } bitflags = { workspace = true } @@ -29,9 +30,9 @@ scale-info = { features = ["derive"], workspace = true } log = { workspace = true } serde = { optional = true, features = ["derive"], workspace = true, default-features = true } impl-trait-for-tuples = { workspace = true } +rlp = { workspace = true } -# Substrate Dependencies -environmental = { workspace = true } +# Polkadot SDK Dependencies frame-benchmarking = { optional = true, workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } @@ -54,10 +55,7 @@ pretty_assertions = { workspace = true } wat = { workspace = true } pallet-revive-fixtures = { workspace = true, default-features = true } -# Polkadot Dependencies -xcm-builder = { workspace = true, default-features = true } - -# Substrate Dependencies +# Polkadot SDK Dependencies pallet-balances = { workspace = true, default-features = true } pallet-timestamp = { workspace = true, default-features = true } pallet-message-queue = { workspace = true, default-features = true } @@ -66,6 +64,7 @@ pallet-assets = { workspace = true, default-features = true } pallet-proxy = { workspace = true, default-features = true } sp-keystore = { workspace = true, default-features = true } sp-tracing = { workspace = true, default-features = true } +xcm-builder = { workspace = true, default-features = true } [features] default = ["std"] @@ -86,6 +85,7 @@ std = [ "pallet-timestamp/std", "pallet-utility/std", "polkavm/std", + "rlp/std", "scale-info/std", "serde", "sp-api/std", diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index 9e54acdace70..db284c7cc062 100644 --- a/substrate/frame/revive/fixtures/Cargo.toml +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -12,15 +12,17 @@ workspace = true [dependencies] frame-system = { workspace = true, default-features = true, optional = true } +sp-core = { workspace = true, default-features = true, optional = true } +sp-io = { workspace = true, default-features = true, optional = true } sp-runtime = { workspace = true, default-features = true, optional = true } -anyhow = { workspace = true } +anyhow = { workspace = true, default-features = true, optional = true } [build-dependencies] parity-wasm = { workspace = true } tempfile = { workspace = true } toml = { workspace = true } polkavm-linker = { version = "0.10.0" } -anyhow = { workspace = true } +anyhow = { workspace = true, default-features = true } [features] default = ["std"] @@ -30,7 +32,9 @@ default = ["std"] riscv = [] # only when std is enabled all fixtures are available std = [ - "anyhow/std", + "anyhow", "frame-system", + "sp-core", + "sp-io", "sp-runtime", ] diff --git a/substrate/frame/revive/fixtures/contracts/call.rs b/substrate/frame/revive/fixtures/contracts/call.rs index a75aee65c205..73f427650c20 100644 --- a/substrate/frame/revive/fixtures/contracts/call.rs +++ b/substrate/frame/revive/fixtures/contracts/call.rs @@ -31,7 +31,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( callee_input: [u8; 4], - callee_addr: [u8; 32], + callee_addr: [u8; 20], ); // Call the callee diff --git a/substrate/frame/revive/fixtures/contracts/call_return_code.rs b/substrate/frame/revive/fixtures/contracts/call_return_code.rs index 32654d59ef1f..e8f995cffc7f 100644 --- a/substrate/frame/revive/fixtures/contracts/call_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/call_return_code.rs @@ -33,7 +33,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( 100, - callee_addr: [u8; 32], + callee_addr: [u8; 20], input: [u8], ); diff --git a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs index 1323c8c5d55d..f3d2ece21327 100644 --- a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs @@ -31,7 +31,7 @@ pub extern "C" fn call() { input!( 512, callee_input: [u8; 4], - callee_addr: [u8; 32], + callee_addr: [u8; 20], call: [u8], ); diff --git a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs index a078162d7995..15c1124eeaee 100644 --- a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs +++ b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs @@ -31,7 +31,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( 256, - callee_addr: [u8; 32], + callee_addr: [u8; 20], flags: u32, value: u64, forwarded_input: [u8], diff --git a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs index a5356924f24f..985df6724110 100644 --- a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs +++ b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs @@ -32,7 +32,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( 256, - callee_addr: [u8; 32], + callee_addr: [u8; 20], ref_time: u64, proof_size: u64, forwarded_input: [u8], diff --git a/substrate/frame/revive/fixtures/contracts/caller_contract.rs b/substrate/frame/revive/fixtures/contracts/caller_contract.rs index 2fa11df82d04..dceab813f88f 100644 --- a/substrate/frame/revive/fixtures/contracts/caller_contract.rs +++ b/substrate/frame/revive/fixtures/contracts/caller_contract.rs @@ -33,7 +33,7 @@ pub extern "C" fn call() { // The value to transfer on instantiation and calls. Chosen to be greater than existential // deposit. let value = 32768u64.to_le_bytes(); - let salt = [0u8; 0]; + let salt = [0u8; 32]; // Callee will use the first 4 bytes of the input to return an exit status. let input = [0u8, 1, 34, 51, 68, 85, 102, 119]; @@ -72,7 +72,7 @@ pub extern "C" fn call() { assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); // Deploy the contract successfully. - let mut callee = [0u8; 32]; + let mut callee = [0u8; 20]; let callee = &mut &mut callee[..]; api::instantiate( @@ -87,7 +87,7 @@ pub extern "C" fn call() { &salt, ) .unwrap(); - assert_eq!(callee.len(), 32); + assert_eq!(callee.len(), 20); // Call the new contract and expect it to return failing exit code. let res = api::call( diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs index d0d3651dfe4d..7a0b497079cd 100644 --- a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs @@ -32,7 +32,7 @@ pub extern "C" fn call() { input!( buffer, input: [u8; 4], - callee: [u8; 32], + callee: [u8; 20], deposit_limit: [u8; 8], ); diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs index 918a4abe6b21..53b9afba7783 100644 --- a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs @@ -36,8 +36,8 @@ pub extern "C" fn call() { ); let value = 10_000u64.to_le_bytes(); - let salt = [0u8; 0]; - let mut address = [0u8; 32]; + let salt = [0u8; 32]; + let mut address = [0u8; 20]; let address = &mut &mut address[..]; api::instantiate( diff --git a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs index a2a0e85bcf64..8788542a0c5a 100644 --- a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs @@ -35,7 +35,7 @@ pub extern "C" fn call() { buffer, len: u32, input: [u8; 4], - callee: [u8; 32], + callee: [u8; 20], ); let rounds = len as usize / BUFFER.len(); diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs index 055760729bd2..921543dced06 100644 --- a/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs @@ -44,6 +44,6 @@ pub extern "C" fn call() { assert_eq!(value_transferred, 1337); // Assert that ALICE is the caller of the contract. - output!(caller, [0u8; 32], api::caller,); - assert_eq!(&caller[..], &[1u8; 32]); + output!(caller, [0u8; 20], api::caller,); + assert_eq!(&caller[..], &[1u8; 20]); } diff --git a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs index 8b74493a1e38..b86e761d53e7 100644 --- a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs +++ b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs @@ -30,9 +30,9 @@ pub extern "C" fn deploy() { input!(code_hash: [u8; 32],); let input = [0u8; 0]; - let mut address = [0u8; 32]; + let mut address = [0u8; 20]; let address = &mut &mut address[..]; - let salt = [71u8, 17u8]; + let salt = [47u8; 32]; api::instantiate( code_hash, @@ -54,7 +54,7 @@ pub extern "C" fn deploy() { #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - let mut callee_addr = [0u8; 32]; + let mut callee_addr = [0u8; 20]; let callee_addr = &mut &mut callee_addr[..]; api::get_storage(StorageFlags::empty(), &ADDRESS_KEY, callee_addr).unwrap(); diff --git a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs index 3ed886b49489..2f3e5ae148cb 100644 --- a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs +++ b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs @@ -23,7 +23,7 @@ use common::input; use uapi::{HostFn, HostFnImpl as api}; -const ALICE: [u8; 32] = [1u8; 32]; +const ETH_ALICE: [u8; 20] = [1u8; 20]; /// Load input data and perform the action specified by the input. /// If `delegate_call` is true, then delegate call into the contract. @@ -44,7 +44,7 @@ fn load_input(delegate_call: bool) { }, // 3 = Terminate 3 => { - api::terminate(&ALICE); + api::terminate(Ð_ALICE); }, // Everything else is a noop _ => {}, diff --git a/substrate/frame/revive/fixtures/contracts/read_only_call.rs b/substrate/frame/revive/fixtures/contracts/read_only_call.rs index a62bb205039f..ef8bc95f0098 100644 --- a/substrate/frame/revive/fixtures/contracts/read_only_call.rs +++ b/substrate/frame/revive/fixtures/contracts/read_only_call.rs @@ -31,7 +31,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( 256, - callee_addr: [u8; 32], + callee_addr: [u8; 20], callee_input: [u8], ); diff --git a/substrate/frame/revive/fixtures/contracts/self_destruct.rs b/substrate/frame/revive/fixtures/contracts/self_destruct.rs index 10f33226acf2..b0e004018f10 100644 --- a/substrate/frame/revive/fixtures/contracts/self_destruct.rs +++ b/substrate/frame/revive/fixtures/contracts/self_destruct.rs @@ -21,7 +21,7 @@ use common::{input, output}; use uapi::{HostFn, HostFnImpl as api}; -const DJANGO: [u8; 32] = [4u8; 32]; +const ETH_DJANGO: [u8; 20] = [4u8; 20]; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -36,7 +36,7 @@ pub extern "C" fn call() { input!(input, 4,); if !input.is_empty() { - output!(addr, [0u8; 32], api::address,); + output!(addr, [0u8; 20], api::address,); api::call( uapi::CallFlags::ALLOW_REENTRY, addr, @@ -50,6 +50,6 @@ pub extern "C" fn call() { .unwrap(); } else { // Try to terminate and give balance to django. - api::terminate(&DJANGO); + api::terminate(Ð_DJANGO); } } diff --git a/substrate/frame/revive/fixtures/src/lib.rs b/substrate/frame/revive/fixtures/src/lib.rs index 1b6103f57aee..54e32130635a 100644 --- a/substrate/frame/revive/fixtures/src/lib.rs +++ b/substrate/frame/revive/fixtures/src/lib.rs @@ -21,18 +21,12 @@ extern crate alloc; /// Load a given wasm module and returns a wasm binary contents along with it's hash. #[cfg(feature = "std")] -pub fn compile_module( - fixture_name: &str, -) -> anyhow::Result<(Vec, ::Output)> -where - T: frame_system::Config, -{ - use sp_runtime::traits::Hash; +pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec, sp_core::H256)> { let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); let fixture_path = out_dir.join(format!("{fixture_name}.polkavm")); let binary = std::fs::read(fixture_path)?; - let code_hash = T::Hashing::hash(&binary); - Ok((binary, code_hash)) + let code_hash = sp_io::hashing::keccak_256(&binary); + Ok((binary, sp_core::H256(code_hash))) } /// Fixtures used in runtime benchmarks. diff --git a/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs b/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs index 49f53ae5bc3d..678e7a444900 100644 --- a/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs +++ b/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs @@ -20,7 +20,7 @@ use frame_support::derive_impl; #[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)] impl pallet_revive::Config for Runtime { - type AddressGenerator = pallet_revive::DefaultAddressGenerator; + type AddressMapper = pallet_revive::DefaultAddressMapper; type Currency = Balances; type Time = super::Timestamp; type Xcm = pallet_xcm::Pallet; diff --git a/substrate/frame/revive/src/address.rs b/substrate/frame/revive/src/address.rs index 5758daf7b1ff..f1bd36dcbba8 100644 --- a/substrate/frame/revive/src/address.rs +++ b/substrate/frame/revive/src/address.rs @@ -15,54 +15,115 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Functions that deal with address derivation. +//! Functions that deal contract addresses. -use crate::{CodeHash, Config}; -use codec::{Decode, Encode}; -use sp_runtime::traits::{Hash, TrailingZeroInput}; +use alloc::vec::Vec; +use sp_core::H160; +use sp_io::hashing::keccak_256; +use sp_runtime::AccountId32; -/// Provides the contract address generation method. +/// Map between the native chain account id `T` and an Ethereum [`H160`]. /// -/// See [`DefaultAddressGenerator`] for the default implementation. +/// This trait exists only to emulate specialization for different concrete +/// native account ids. **Not** to make the mapping user configurable. Hence +/// the trait is `Sealed` and only one mandatory implementor [`DefaultAddressMapper`] +/// exists. /// -/// # Note for implementors -/// -/// 1. Make sure that there are no collisions, different inputs never lead to the same output. -/// 2. Make sure that the same inputs lead to the same output. -pub trait AddressGenerator { - /// The address of a contract based on the given instantiate parameters. +/// Please note that we assume that the native account is at least 20 bytes and +/// only implement this type for a `T` where this is the case. Luckily, this is the +/// case for all existing runtimes as of right now. Reasing is that this will allow +/// us to reverse an address -> account_id mapping by just stripping the prefix. +pub trait AddressMapper: private::Sealed { + /// Convert an account id to an ethereum adress. + /// + /// This mapping is **not** required to be reversible. + fn to_address(account_id: &T) -> H160; + + /// Convert an ethereum address to a native account id. /// - /// Changing the formular for an already deployed chain is fine as long as no collisions - /// with the old formular. Changes only affect existing contracts. - fn contract_address( - deploying_address: &T::AccountId, - code_hash: &CodeHash, - input_data: &[u8], - salt: &[u8], - ) -> T::AccountId; + /// This mapping is **required** to be reversible. + fn to_account_id(address: &H160) -> T; + + /// Same as [`Self::to_account_id`] but when we know the address is a contract. + /// + /// This is only the case when we just generated the new address. + fn to_account_id_contract(address: &H160) -> T; } -/// Default address generator. -/// -/// This is the default address generator used by contract instantiation. Its result -/// is only dependent on its inputs. It can therefore be used to reliably predict the -/// address of a contract. This is akin to the formula of eth's CREATE2 opcode. There -/// is no CREATE equivalent because CREATE2 is strictly more powerful. -/// Formula: -/// `hash("contract_addr_v1" ++ deploying_address ++ code_hash ++ input_data ++ salt)` -pub struct DefaultAddressGenerator; - -impl AddressGenerator for DefaultAddressGenerator { - /// Formula: `hash("contract_addr_v1" ++ deploying_address ++ code_hash ++ input_data ++ salt)` - fn contract_address( - deploying_address: &T::AccountId, - code_hash: &CodeHash, - input_data: &[u8], - salt: &[u8], - ) -> T::AccountId { - let entropy = (b"contract_addr_v1", deploying_address, code_hash, input_data, salt) - .using_encoded(T::Hashing::hash); - Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) - .expect("infinite length input; no invalid inputs for type; qed") +mod private { + pub trait Sealed {} + impl Sealed for super::DefaultAddressMapper {} +} + +/// The only implementor for `AddressMapper`. +pub enum DefaultAddressMapper {} + +impl AddressMapper for DefaultAddressMapper { + fn to_address(account_id: &AccountId32) -> H160 { + H160::from_slice(&>::as_ref(&account_id)[..20]) + } + + fn to_account_id(address: &H160) -> AccountId32 { + let mut account_id = AccountId32::new([0xEE; 32]); + >::as_mut(&mut account_id)[..20] + .copy_from_slice(address.as_bytes()); + account_id + } + + fn to_account_id_contract(address: &H160) -> AccountId32 { + Self::to_account_id(address) + } +} + +/// Determine the address of a contract using CREATE semantics. +#[allow(dead_code)] +pub fn create1(deployer: &H160, nonce: u64) -> H160 { + let mut list = rlp::RlpStream::new_list(2); + list.append(&deployer.as_bytes()); + list.append(&nonce); + let hash = keccak_256(&list.out()); + H160::from_slice(&hash[12..]) +} + +/// Determine the address of a contract using the CREATE2 semantics. +pub fn create2(deployer: &H160, code: &[u8], input_data: &[u8], salt: &[u8; 32]) -> H160 { + let init_code_hash = { + let init_code: Vec = code.into_iter().chain(input_data).cloned().collect(); + keccak_256(init_code.as_ref()) + }; + let mut bytes = [0; 85]; + bytes[0] = 0xff; + bytes[1..21].copy_from_slice(deployer.as_bytes()); + bytes[21..53].copy_from_slice(salt); + bytes[53..85].copy_from_slice(&init_code_hash); + let hash = keccak_256(&bytes); + H160::from_slice(&hash[12..]) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::test_utils::ALICE_ADDR; + use sp_core::{hex2array, H160}; + + #[test] + fn create1_works() { + assert_eq!( + create1(&ALICE_ADDR, 1u64), + H160(hex2array!("c851da37e4e8d3a20d8d56be2963934b4ad71c3b")), + ) + } + + #[test] + fn create2_works() { + assert_eq!( + create2( + &ALICE_ADDR, + &hex2array!("600060005560016000"), + &hex2array!("55"), + &hex2array!("1234567890123456789012345678901234567890123456789012345678901234") + ), + H160(hex2array!("7f31e795e5836a19a8f919ab5a9de9a197ecd2b6")), + ) } } diff --git a/substrate/frame/revive/src/benchmarking/call_builder.rs b/substrate/frame/revive/src/benchmarking/call_builder.rs index 654ba3de4f72..c000817a8a39 100644 --- a/substrate/frame/revive/src/benchmarking/call_builder.rs +++ b/substrate/frame/revive/src/benchmarking/call_builder.rs @@ -16,6 +16,7 @@ // limitations under the License. use crate::{ + address::AddressMapper, benchmarking::{default_deposit_limit, Contract, WasmModule}, exec::{ExportedFunction, Ext, Key, Stack}, storage::meter::Meter, @@ -59,7 +60,7 @@ where as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, { /// Setup a new call for the given module. - pub fn new(module: WasmModule) -> Self { + pub fn new(module: WasmModule) -> Self { let contract = Contract::::new(module.clone(), vec![]).unwrap(); let dest = contract.account_id.clone(); let origin = Origin::from_account_id(contract.caller.clone()); @@ -74,7 +75,10 @@ where // Whitelist the contract's contractInfo as it is already accounted for in the call // benchmark benchmarking::add_to_whitelist( - crate::ContractInfoOf::::hashed_key_for(&contract.account_id).into(), + crate::ContractInfoOf::::hashed_key_for(&T::AddressMapper::to_address( + &contract.account_id, + )) + .into(), ); Self { @@ -138,7 +142,7 @@ where /// Build the call stack. pub fn ext(&mut self) -> (StackExt<'_, T>, WasmBlob) { let mut ext = StackExt::bench_new_call( - self.dest.clone(), + T::AddressMapper::to_address(&self.dest), self.origin.clone(), &mut self.gas_meter, &mut self.storage_meter, diff --git a/substrate/frame/revive/src/benchmarking/code.rs b/substrate/frame/revive/src/benchmarking/code.rs index eba4710d8a2c..ede3bb69b11b 100644 --- a/substrate/frame/revive/src/benchmarking/code.rs +++ b/substrate/frame/revive/src/benchmarking/code.rs @@ -24,19 +24,19 @@ //! we define this simple definition of a contract that can be passed to `create_code` that //! compiles it down into a `WasmModule` that can be used as a contract's code. -use crate::Config; use alloc::vec::Vec; use pallet_revive_fixtures::bench as bench_fixtures; -use sp_runtime::traits::Hash; +use sp_core::H256; +use sp_io::hashing::keccak_256; /// A wasm module ready to be put on chain. #[derive(Clone)] -pub struct WasmModule { +pub struct WasmModule { pub code: Vec, - pub hash: ::Output, + pub hash: H256, } -impl WasmModule { +impl WasmModule { /// Return a contract code that does nothing. pub fn dummy() -> Self { Self::new(bench_fixtures::DUMMY.to_vec()) @@ -63,7 +63,7 @@ impl WasmModule { } fn new(code: Vec) -> Self { - let hash = T::Hashing::hash(&code); - Self { code, hash } + let hash = keccak_256(&code); + Self { code, hash: H256(hash) } } } diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index b4bd028d6f03..409d53b8a062 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -65,7 +65,7 @@ const UNBALANCED_TRIE_LAYERS: u32 = 20; struct Contract { caller: T::AccountId, account_id: T::AccountId, - addr: AccountIdLookupOf, + addr: T::AccountId, } impl Contract @@ -74,14 +74,14 @@ where as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, { /// Create new contract and use a default account id as instantiator. - fn new(module: WasmModule, data: Vec) -> Result, &'static str> { + fn new(module: WasmModule, data: Vec) -> Result, &'static str> { Self::with_index(0, module, data) } /// Create new contract and use an account id derived from the supplied index as instantiator. fn with_index( index: u32, - module: WasmModule, + module: WasmModule, data: Vec, ) -> Result, &'static str> { Self::with_caller(account("instantiator", index, 0), module, data) @@ -90,11 +90,11 @@ where /// Create new contract and use the supplied `caller` as instantiator. fn with_caller( caller: T::AccountId, - module: WasmModule, + module: WasmModule, data: Vec, ) -> Result, &'static str> { T::Currency::set_balance(&caller, caller_funding::()); - let salt = vec![0xff]; + let salt = [0xffu8; 32]; let outcome = Contracts::::bare_instantiate( RawOrigin::Signed(caller.clone()).into(), @@ -108,20 +108,17 @@ where CollectEvents::Skip, ); - let addr = outcome.result?.account_id; - let result = Contract { caller, account_id: addr.clone(), addr: T::Lookup::unlookup(addr) }; + let address = outcome.result?.addr; + let account_id = T::AddressMapper::to_account_id_contract(&address); + let result = Contract { caller, account_id: account_id.clone(), addr: account_id }; - ContractInfoOf::::insert(&result.account_id, result.info()?); + ContractInfoOf::::insert(&address, result.info()?); Ok(result) } /// Create a new contract with the supplied storage item count and size each. - fn with_storage( - code: WasmModule, - stor_num: u32, - stor_size: u32, - ) -> Result { + fn with_storage(code: WasmModule, stor_num: u32, stor_size: u32) -> Result { let contract = Contract::::new(code, vec![])?; let storage_items = (0..stor_num) .map(|i| { @@ -143,12 +140,12 @@ where info.write(&Key::Fix(item.0), Some(item.1.clone()), None, false) .map_err(|_| "Failed to write storage to restoration dest")?; } - >::insert(&self.account_id, info); + >::insert(T::AddressMapper::to_address(&self.account_id), info); Ok(()) } /// Create a new contract with the specified unbalanced storage trie. - fn with_unbalanced_storage_trie(code: WasmModule, key: &[u8]) -> Result { + fn with_unbalanced_storage_trie(code: WasmModule, key: &[u8]) -> Result { if (key.len() as u32) < (UNBALANCED_TRIE_LAYERS + 1) / 2 { return Err("Key size too small to create the specified trie"); } @@ -179,7 +176,8 @@ where /// Get the `ContractInfo` of the `addr` or an error if it no longer exists. fn address_info(addr: &T::AccountId) -> Result, &'static str> { - ContractInfoOf::::get(addr).ok_or("Expected contract to exist at this point.") + ContractInfoOf::::get(T::AddressMapper::to_address(addr)) + .ok_or("Expected contract to exist at this point.") } /// Get the `ContractInfo` of this contract or an error if it no longer exists. @@ -193,12 +191,12 @@ where } /// Returns `true` iff all storage entries related to code storage exist. - fn code_exists(hash: &CodeHash) -> bool { + fn code_exists(hash: &sp_core::H256) -> bool { >::contains_key(hash) && >::contains_key(&hash) } /// Returns `true` iff no storage entry related to code storage exist. - fn code_removed(hash: &CodeHash) -> bool { + fn code_removed(hash: &sp_core::H256) -> bool { !>::contains_key(hash) && !>::contains_key(&hash) } } @@ -328,7 +326,7 @@ mod benchmarks { let instance = Contract::::with_caller(whitelisted_caller(), WasmModule::sized(c), vec![])?; let value = Pallet::::min_balance(); - let callee = instance.addr; + let callee = T::AddressMapper::to_address(&instance.addr); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] @@ -351,22 +349,23 @@ mod benchmarks { fn instantiate_with_code( c: Linear<0, { T::MaxCodeLen::get() }>, i: Linear<0, { limits::MEMORY_BYTES }>, - s: Linear<0, { limits::MEMORY_BYTES }>, ) { let input = vec![42u8; i as usize]; - let salt = vec![42u8; s as usize]; + let salt = [42u8; 32]; let value = Pallet::::min_balance(); let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); - let WasmModule { code, hash, .. } = WasmModule::::sized(c); + let WasmModule { code, .. } = WasmModule::sized(c); let origin = RawOrigin::Signed(caller.clone()); - let addr = Contracts::::contract_address(&caller, &hash, &input, &salt); + let deployer = T::AddressMapper::to_address(&caller); + let addr = crate::address::create2(&deployer, &code, &input, &salt); + let account_id = T::AddressMapper::to_account_id_contract(&addr); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] _(origin, value, Weight::MAX, storage_deposit, code, input, salt); let deposit = - T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr); + T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id); // uploading the code reserves some balance in the callers account let code_deposit = T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &caller); @@ -375,27 +374,26 @@ mod benchmarks { caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), ); // contract has the full value - assert_eq!(T::Currency::balance(&addr), value + Pallet::::min_balance()); + assert_eq!(T::Currency::balance(&account_id), value + Pallet::::min_balance()); } // `i`: Size of the input in bytes. // `s`: Size of the salt in bytes. #[benchmark(pov_mode = Measured)] - fn instantiate( - i: Linear<0, { limits::MEMORY_BYTES }>, - s: Linear<0, { limits::MEMORY_BYTES }>, - ) -> Result<(), BenchmarkError> { + fn instantiate(i: Linear<0, { limits::MEMORY_BYTES }>) -> Result<(), BenchmarkError> { let input = vec![42u8; i as usize]; - let salt = vec![42u8; s as usize]; + let salt = [42u8; 32]; let value = Pallet::::min_balance(); let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); let origin = RawOrigin::Signed(caller.clone()); - let WasmModule { code, .. } = WasmModule::::dummy(); + let WasmModule { code, .. } = WasmModule::dummy(); let storage_deposit = default_deposit_limit::(); + let deployer = T::AddressMapper::to_address(&caller); + let addr = crate::address::create2(&deployer, &code, &input, &salt); let hash = Contracts::::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash; - let addr = Contracts::::contract_address(&caller, &hash, &input, &salt); + let account_id = T::AddressMapper::to_account_id_contract(&addr); #[extrinsic_call] _( @@ -409,16 +407,16 @@ mod benchmarks { ); let deposit = - T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr); + T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id); let code_deposit = - T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &caller); + T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &account_id); // value was removed from the caller assert_eq!( - T::Currency::balance(&caller), + T::Currency::total_balance(&caller), caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), ); // contract has the full value - assert_eq!(T::Currency::balance(&addr), value + Pallet::::min_balance()); + assert_eq!(T::Currency::balance(&account_id), value + Pallet::::min_balance()); Ok(()) } @@ -437,7 +435,7 @@ mod benchmarks { Contract::::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; let value = Pallet::::min_balance(); let origin = RawOrigin::Signed(instance.caller.clone()); - let callee = instance.addr.clone(); + let callee = T::AddressMapper::to_address(&instance.addr); let before = T::Currency::balance(&instance.account_id); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] @@ -470,7 +468,7 @@ mod benchmarks { fn upload_code(c: Linear<0, { T::MaxCodeLen::get() }>) { let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); - let WasmModule { code, hash, .. } = WasmModule::::sized(c); + let WasmModule { code, hash, .. } = WasmModule::sized(c); let origin = RawOrigin::Signed(caller.clone()); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] @@ -487,7 +485,7 @@ mod benchmarks { fn remove_code() -> Result<(), BenchmarkError> { let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); - let WasmModule { code, hash, .. } = WasmModule::::dummy(); + let WasmModule { code, hash, .. } = WasmModule::dummy(); let origin = RawOrigin::Signed(caller.clone()); let storage_deposit = default_deposit_limit::(); let uploaded = @@ -508,12 +506,12 @@ mod benchmarks { let instance = >::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; // we just add some bytes so that the code hash is different - let WasmModule { code, .. } = >::dummy_unique(128); + let WasmModule { code, .. } = WasmModule::dummy_unique(128); let origin = RawOrigin::Signed(instance.caller.clone()); let storage_deposit = default_deposit_limit::(); let hash = >::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash; - let callee = instance.addr.clone(); + let callee = T::AddressMapper::to_address(&instance.addr); assert_ne!(instance.info()?.code_hash, hash); #[extrinsic_call] _(RawOrigin::Root, callee, hash); @@ -534,7 +532,7 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn seal_caller() { - let len = ::max_encoded_len() as u32; + let len = H160::len_bytes(); build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); let result; @@ -545,8 +543,8 @@ mod benchmarks { assert_ok!(result); assert_eq!( - &::decode(&mut &memory[4..]).unwrap(), - runtime.ext().caller().account_id().unwrap() + ::decode(&mut &memory[4..]).unwrap(), + T::AddressMapper::to_address(&runtime.ext().caller().account_id().unwrap()) ); } @@ -569,7 +567,7 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn seal_code_hash() { let contract = Contract::::with_index(1, WasmModule::dummy(), vec![]).unwrap(); - let len = as MaxEncodedLen>::max_encoded_len() as u32; + let len = ::max_encoded_len() as u32; build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], contract.account_id.encode(), ]); let result; @@ -580,14 +578,14 @@ mod benchmarks { assert_ok!(result); assert_eq!( - as Decode>::decode(&mut &memory[4..]).unwrap(), + ::decode(&mut &memory[4..]).unwrap(), contract.info().unwrap().code_hash ); } #[benchmark(pov_mode = Measured)] fn seal_own_code_hash() { - let len = as MaxEncodedLen>::max_encoded_len() as u32; + let len = ::max_encoded_len() as u32; build_runtime!(runtime, contract, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); let result; #[block] @@ -597,7 +595,7 @@ mod benchmarks { assert_ok!(result); assert_eq!( - as Decode>::decode(&mut &memory[4..]).unwrap(), + ::decode(&mut &memory[4..]).unwrap(), contract.info().unwrap().code_hash ); } @@ -631,7 +629,7 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn seal_address() { - let len = as MaxEncodedLen>::max_encoded_len() as u32; + let len = H160::len_bytes(); build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); let result; @@ -640,10 +638,7 @@ mod benchmarks { result = runtime.bench_address(memory.as_mut_slice(), 4, 0); } assert_ok!(result); - assert_eq!( - &::decode(&mut &memory[4..]).unwrap(), - runtime.ext().address() - ); + assert_eq!(::decode(&mut &memory[4..]).unwrap(), runtime.ext().address()); } #[benchmark(pov_mode = Measured)] @@ -809,7 +804,7 @@ mod benchmarks { build_runtime!(runtime, memory: [beneficiary.encode(),]); (0..n).for_each(|i| { - let new_code = WasmModule::::dummy_unique(65 + i); + let new_code = WasmModule::dummy_unique(65 + i); Contracts::::bare_upload_code(origin.clone().into(), new_code.code, storage_deposit) .unwrap(); runtime.ext().lock_delegate_dependency(new_code.hash).unwrap(); @@ -1536,10 +1531,8 @@ mod benchmarks { // i: size of input in bytes // s: size of salt in bytes #[benchmark(pov_mode = Measured)] - fn seal_instantiate( - i: Linear<0, { limits::MEMORY_BYTES }>, - s: Linear<0, { limits::MEMORY_BYTES }>, - ) -> Result<(), BenchmarkError> { + fn seal_instantiate(i: Linear<0, { limits::MEMORY_BYTES }>) -> Result<(), BenchmarkError> { + let code = WasmModule::dummy(); let hash = Contract::::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash; let hash_bytes = hash.encode(); let hash_len = hash_bytes.len() as u32; @@ -1561,8 +1554,10 @@ mod benchmarks { let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); let input = vec![42u8; i as _]; - let salt = vec![42u8; s as _]; - let addr = Contracts::::contract_address(&account_id, &hash, &input, &salt); + let salt = [42u8; 32]; + let deployer = T::AddressMapper::to_address(&account_id); + let addr = crate::address::create2(&deployer, &code.code, &input, &salt); + let account_id = T::AddressMapper::to_account_id_contract(&addr); let mut memory = memory!(hash_bytes, deposit_bytes, value_bytes, input, salt,); let mut offset = { @@ -1592,13 +1587,13 @@ mod benchmarks { SENTINEL, // output_ptr 0, // output_len_ptr offset(i), // salt_ptr - s, // salt_len + 32, // salt_len ); } assert_ok!(result); assert!(ContractInfoOf::::get(&addr).is_some()); - assert_eq!(T::Currency::balance(&addr), Pallet::::min_balance() + value); + assert_eq!(T::Currency::balance(&account_id), Pallet::::min_balance() + value); Ok(()) } diff --git a/substrate/frame/revive/src/debug.rs b/substrate/frame/revive/src/debug.rs index 467f4e1ad491..00e893b94f86 100644 --- a/substrate/frame/revive/src/debug.rs +++ b/substrate/frame/revive/src/debug.rs @@ -20,6 +20,7 @@ pub use crate::{ primitives::ExecReturnValue, }; use crate::{Config, LOG_TARGET}; +use sp_core::H160; /// Umbrella trait for all interfaces that serves for debugging. pub trait Debugger: Tracing + CallInterceptor {} @@ -43,7 +44,7 @@ pub trait Tracing { /// * `entry_point` - Describes whether the call is the constructor or a regular call. /// * `input_data` - The raw input data of the call. fn new_call_span( - contract_address: &T::AccountId, + contract_address: &H160, entry_point: ExportedFunction, input_data: &[u8], ) -> Self::CallSpan; @@ -62,11 +63,7 @@ pub trait CallSpan { impl Tracing for () { type CallSpan = (); - fn new_call_span( - contract_address: &T::AccountId, - entry_point: ExportedFunction, - input_data: &[u8], - ) { + fn new_call_span(contract_address: &H160, entry_point: ExportedFunction, input_data: &[u8]) { log::trace!(target: LOG_TARGET, "call {entry_point:?} account: {contract_address:?}, input_data: {input_data:?}") } } @@ -95,7 +92,7 @@ pub trait CallInterceptor { /// is returned. /// * `None` - otherwise, i.e. the call should be executed normally. fn intercept_call( - contract_address: &T::AccountId, + contract_address: &H160, entry_point: ExportedFunction, input_data: &[u8], ) -> Option; @@ -103,7 +100,7 @@ pub trait CallInterceptor { impl CallInterceptor for () { fn intercept_call( - _contract_address: &T::AccountId, + _contract_address: &H160, _entry_point: ExportedFunction, _input_data: &[u8], ) -> Option { diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 9740707ae706..54019a6ba992 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -16,6 +16,7 @@ // limitations under the License. use crate::{ + address::{self, AddressMapper}, debug::{CallInterceptor, CallSpan, Tracing}, gas::GasMeter, limits, @@ -23,8 +24,8 @@ use crate::{ runtime_decl_for_revive_api::{Decode, Encode, RuntimeDebugNoBound, TypeInfo}, storage::{self, meter::Diff, WriteOutcome}, transient_storage::TransientStorage, - BalanceOf, CodeHash, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, DebugBuffer, - Error, Event, Pallet as Contracts, LOG_TARGET, + BalanceOf, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, DebugBuffer, Error, + Event, Pallet as Contracts, LOG_TARGET, }; use alloc::vec::Vec; use core::{fmt::Debug, marker::PhantomData, mem}; @@ -48,7 +49,7 @@ use frame_system::{ use sp_core::{ ecdsa::Public as ECDSAPublic, sr25519::{Public as SR25519Public, Signature as SR25519Signature}, - ConstU32, Get, + ConstU32, Get, H160, H256, }; use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256}; use sp_runtime::{ @@ -184,7 +185,7 @@ pub trait Ext: sealing::Sealed { &mut self, gas_limit: Weight, deposit_limit: BalanceOf, - to: AccountIdOf, + to: &H160, value: BalanceOf, input_data: Vec, allows_reentry: bool, @@ -196,7 +197,7 @@ pub trait Ext: sealing::Sealed { /// Returns the code size of the called contract. fn delegate_call( &mut self, - code: CodeHash, + code: H256, input_data: Vec, ) -> Result; @@ -209,11 +210,11 @@ pub trait Ext: sealing::Sealed { &mut self, gas_limit: Weight, deposit_limit: BalanceOf, - code: CodeHash, + code: H256, value: BalanceOf, input_data: Vec, - salt: &[u8], - ) -> Result<(AccountIdOf, ExecReturnValue), ExecError>; + salt: &[u8; 32], + ) -> Result<(H160, ExecReturnValue), ExecError>; /// Transfer all funds to `beneficiary` and delete the contract. /// @@ -222,10 +223,10 @@ pub trait Ext: sealing::Sealed { /// /// This function will fail if the same contract is present on the contract /// call stack. - fn terminate(&mut self, beneficiary: &AccountIdOf) -> DispatchResult; + fn terminate(&mut self, beneficiary: &H160) -> DispatchResult; /// Transfer some amount of funds into the specified account. - fn transfer(&mut self, to: &AccountIdOf, value: BalanceOf) -> DispatchResult; + fn transfer(&mut self, to: &H160, value: BalanceOf) -> DispatchResult; /// Returns the storage entry of the executing account by the given `key`. /// @@ -273,15 +274,15 @@ pub trait Ext: sealing::Sealed { fn caller(&self) -> Origin; /// Check if a contract lives at the specified `address`. - fn is_contract(&self, address: &AccountIdOf) -> bool; + fn is_contract(&self, address: &H160) -> bool; /// Returns the code hash of the contract for the given `address`. /// /// Returns `None` if the `address` does not belong to a contract. - fn code_hash(&self, address: &AccountIdOf) -> Option>; + fn code_hash(&self, address: &H160) -> Option; /// Returns the code hash of the contract being executed. - fn own_code_hash(&mut self) -> &CodeHash; + fn own_code_hash(&mut self) -> &H256; /// Check if the caller of the current contract is the origin of the whole call stack. /// @@ -293,7 +294,12 @@ pub trait Ext: sealing::Sealed { fn caller_is_root(&self) -> bool; /// Returns a reference to the account id of the current contract. - fn address(&self) -> &AccountIdOf; + fn account_id(&self) -> &AccountIdOf; + + /// Returns a reference to the [`H160`] address of the current contract. + fn address(&self) -> H160 { + ::AddressMapper::to_address(self.account_id()) + } /// Returns the balance of the current contract. /// @@ -368,7 +374,7 @@ pub trait Ext: sealing::Sealed { fn transient_storage(&mut self) -> &mut TransientStorage; /// Sets new code hash for existing contract. - fn set_code_hash(&mut self, hash: CodeHash) -> DispatchResult; + fn set_code_hash(&mut self, hash: H256) -> DispatchResult; /// Returns the number of times the specified contract exists on the call stack. Delegated calls /// Increment the reference count of a of a stored code by one. @@ -377,7 +383,7 @@ pub trait Ext: sealing::Sealed { /// /// [`Error::CodeNotFound`] is returned if no stored code found having the specified /// `code_hash`. - fn increment_refcount(code_hash: CodeHash) -> DispatchResult; + fn increment_refcount(code_hash: H256) -> DispatchResult; /// Decrement the reference count of a stored code by one. /// @@ -385,7 +391,7 @@ pub trait Ext: sealing::Sealed { /// /// A contract whose reference count dropped to zero isn't automatically removed. A /// `remove_code` transaction must be submitted by the original uploader to do so. - fn decrement_refcount(code_hash: CodeHash); + fn decrement_refcount(code_hash: H256); /// Adds a delegate dependency to [`ContractInfo`]'s `delegate_dependencies` field. /// @@ -398,7 +404,7 @@ pub trait Ext: sealing::Sealed { /// - [`Error::MaxDelegateDependenciesReached`] /// - [`Error::CannotAddSelfAsDelegateDependency`] /// - [`Error::DelegateDependencyAlreadyExists`] - fn lock_delegate_dependency(&mut self, code_hash: CodeHash) -> DispatchResult; + fn lock_delegate_dependency(&mut self, code_hash: H256) -> DispatchResult; /// Removes a delegate dependency from [`ContractInfo`]'s `delegate_dependencies` field. /// @@ -408,7 +414,7 @@ pub trait Ext: sealing::Sealed { /// # Errors /// /// - [`Error::DelegateDependencyNotFound`] - fn unlock_delegate_dependency(&mut self, code_hash: &CodeHash) -> DispatchResult; + fn unlock_delegate_dependency(&mut self, code_hash: &H256) -> DispatchResult; /// Returns the number of locked delegate dependencies. /// @@ -447,10 +453,7 @@ pub trait Executable: Sized { /// /// # Note /// Charges size base load weight from the gas meter. - fn from_storage( - code_hash: CodeHash, - gas_meter: &mut GasMeter, - ) -> Result; + fn from_storage(code_hash: H256, gas_meter: &mut GasMeter) -> Result; /// Execute the specified exported function and return the result. /// @@ -471,8 +474,11 @@ pub trait Executable: Sized { /// The code info of the executable. fn code_info(&self) -> &CodeInfo; + /// The raw code of the executable. + fn code(&self) -> &[u8]; + /// The code hash of the executable. - fn code_hash(&self) -> &CodeHash; + fn code_hash(&self) -> &H256; } /// The complete call stack of a contract execution. @@ -519,7 +525,7 @@ pub struct Stack<'a, T: Config, E> { /// For each nested contract call or instantiate one frame is created. It holds specific /// information for the said call and caches the in-storage `ContractInfo` data structure. struct Frame { - /// The account id of the executing contract. + /// The address of the executing contract. account_id: T::AccountId, /// The cached in-storage data of the contract. contract_info: CachedContract, @@ -567,7 +573,7 @@ enum FrameArgs<'a, T: Config, E> { /// The executable whose `deploy` function is run. executable: E, /// A salt used in the contract address derivation of the new contract. - salt: &'a [u8], + salt: &'a [u8; 32], /// The input data is used in the contract address derivation of the new contract. input_data: &'a [u8], }, @@ -668,7 +674,7 @@ impl CachedContract { /// Load the `contract_info` from storage if necessary. fn load(&mut self, account_id: &T::AccountId) { if let CachedContract::Invalidated = self { - let contract = >::get(&account_id); + let contract = >::get(T::AddressMapper::to_address(account_id)); if let Some(contract) = contract { *self = CachedContract::Cached(contract); } @@ -705,7 +711,7 @@ where /// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)> pub fn run_call( origin: Origin, - dest: T::AccountId, + dest: H160, gas_meter: &'a mut GasMeter, storage_meter: &'a mut storage::meter::Meter, value: BalanceOf, @@ -713,7 +719,11 @@ where debug_message: Option<&'a mut DebugBuffer>, ) -> Result { let (mut stack, executable) = Self::new( - FrameArgs::Call { dest, cached_info: None, delegated_call: None }, + FrameArgs::Call { + dest: T::AddressMapper::to_account_id(&dest), + cached_info: None, + delegated_call: None, + }, origin, gas_meter, storage_meter, @@ -740,9 +750,9 @@ where storage_meter: &'a mut storage::meter::Meter, value: BalanceOf, input_data: Vec, - salt: &[u8], + salt: &[u8; 32], debug_message: Option<&'a mut DebugBuffer>, - ) -> Result<(T::AccountId, ExecReturnValue), ExecError> { + ) -> Result<(H160, ExecReturnValue), ExecError> { let (mut stack, executable) = Self::new( FrameArgs::Instantiate { sender: origin.clone(), @@ -756,13 +766,13 @@ where value, debug_message, )?; - let account_id = stack.top_frame().account_id.clone(); - stack.run(executable, input_data).map(|ret| (account_id, ret)) + let address = T::AddressMapper::to_address(&stack.top_frame().account_id); + stack.run(executable, input_data).map(|ret| (address, ret)) } #[cfg(all(feature = "runtime-benchmarks", feature = "riscv"))] pub fn bench_new_call( - dest: T::AccountId, + dest: H160, origin: Origin, gas_meter: &'a mut GasMeter, storage_meter: &'a mut storage::meter::Meter, @@ -770,7 +780,11 @@ where debug_message: Option<&'a mut DebugBuffer>, ) -> (Self, E) { Self::new( - FrameArgs::Call { dest, cached_info: None, delegated_call: None }, + FrameArgs::Call { + dest: T::AddressMapper::to_account_id(&dest), + cached_info: None, + delegated_call: None, + }, origin, gas_meter, storage_meter, @@ -834,7 +848,8 @@ where let contract = if let Some(contract) = cached_info { contract } else { - >::get(&dest).ok_or(>::ContractNotFound)? + >::get(T::AddressMapper::to_address(&dest)) + .ok_or(>::ContractNotFound)? }; let (executable, delegate_caller) = @@ -847,18 +862,20 @@ where (dest, contract, executable, delegate_caller, ExportedFunction::Call) }, FrameArgs::Instantiate { sender, executable, salt, input_data } => { - let account_id = Contracts::::contract_address( - &sender, - &executable.code_hash(), - input_data, - salt, - ); + let deployer = T::AddressMapper::to_address(&sender); + let address = address::create2(&deployer, executable.code(), input_data, salt); let contract = ContractInfo::new( - &account_id, + &address, >::account_nonce(&sender), *executable.code_hash(), )?; - (account_id, contract, executable, None, ExportedFunction::Constructor) + ( + T::AddressMapper::to_account_id_contract(&address), + contract, + executable, + None, + ExportedFunction::Constructor, + ) }, }; @@ -887,7 +904,7 @@ where read_only: bool, ) -> Result { if self.frames.len() as u32 == limits::CALL_STACK_DEPTH { - return Err(Error::::MaxCallDepthReached.into()) + return Err(Error::::MaxCallDepthReached.into()); } // We need to make sure that changes made to the contract info are not discarded. @@ -898,7 +915,10 @@ where if let (CachedContract::Cached(contract), ExportedFunction::Call) = (&frame.contract_info, frame.entry_point) { - >::insert(frame.account_id.clone(), contract.clone()); + >::insert( + T::AddressMapper::to_address(&frame.account_id), + contract.clone(), + ); } let frame = top_frame_mut!(self); @@ -950,11 +970,11 @@ where // Every non delegate call or instantiate also optionally transfers the balance. self.initial_transfer()?; - let contract_address = &top_frame!(self).account_id; + let contract_address = T::AddressMapper::to_address(&top_frame!(self).account_id); - let call_span = T::Debug::new_call_span(contract_address, entry_point, &input_data); + let call_span = T::Debug::new_call_span(&contract_address, entry_point, &input_data); - let output = T::Debug::intercept_call(contract_address, entry_point, &input_data) + let output = T::Debug::intercept_call(&contract_address, entry_point, &input_data) .unwrap_or_else(|| { executable .execute(self, entry_point, input_data) @@ -965,7 +985,7 @@ where // Avoid useless work that would be reverted anyways. if output.did_revert() { - return Ok(output) + return Ok(output); } // Storage limit is normally enforced as late as possible (when the last frame returns) @@ -980,12 +1000,12 @@ where } let frame = self.top_frame(); - let account_id = &frame.account_id.clone(); + let account_id = T::AddressMapper::to_address(&frame.account_id); match (entry_point, delegated_code_hash) { (ExportedFunction::Constructor, _) => { // It is not allowed to terminate a contract inside its constructor. if matches!(frame.contract_info, CachedContract::Terminated) { - return Err(Error::::TerminatedInConstructor.into()) + return Err(Error::::TerminatedInConstructor.into()); } // If a special limit was set for the sub-call, we enforce it here. @@ -995,17 +1015,17 @@ where let contract = frame.contract_info.as_contract(); frame.nested_storage.enforce_subcall_limit(contract)?; - let caller = self.caller().account_id()?.clone(); + let caller = T::AddressMapper::to_address(self.caller().account_id()?); // Deposit an instantiation event. Contracts::::deposit_event(Event::Instantiated { deployer: caller, - contract: account_id.clone(), + contract: account_id, }); }, (ExportedFunction::Call, Some(code_hash)) => { Contracts::::deposit_event(Event::DelegateCalled { - contract: account_id.clone(), + contract: account_id, code_hash, }); }, @@ -1019,7 +1039,7 @@ where let caller = self.caller(); Contracts::::deposit_event(Event::Called { caller: caller.clone(), - contract: account_id.clone(), + contract: account_id, }); }, } @@ -1081,7 +1101,7 @@ where // Only gas counter changes are persisted in case of a failure. if !persist { - return + return; } // Record the storage meter changes of the nested call into the parent meter. @@ -1100,7 +1120,7 @@ where // trigger a rollback. if prev.account_id == *account_id { prev.contract_info = CachedContract::Cached(contract); - return + return; } // Predecessor is a different contract: We persist the info and invalidate the first @@ -1108,7 +1128,7 @@ where // because that case is already handled by the optimization above. Only the first // cache needs to be invalidated because that one will invalidate the next cache // when it is popped from the stack. - >::insert(account_id, contract); + >::insert(T::AddressMapper::to_address(account_id), contract); if let Some(c) = self.frames_mut().skip(1).find(|f| f.account_id == *account_id) { c.contract_info = CachedContract::Invalidated; } @@ -1123,7 +1143,7 @@ where } self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_gas)); if !persist { - return + return; } let mut contract = self.first_frame.contract_info.as_contract(); self.storage_meter.absorb( @@ -1132,7 +1152,10 @@ where contract.as_deref_mut(), ); if let Some(contract) = contract { - >::insert(&self.first_frame.account_id, contract); + >::insert( + T::AddressMapper::to_address(&self.first_frame.account_id), + contract, + ); } } } @@ -1158,7 +1181,7 @@ where // If it is a delegate call, then we've already transferred tokens in the // last non-delegate frame. if frame.delegate_caller.is_some() { - return Ok(()) + return Ok(()); } let value = frame.value_transferred; @@ -1203,7 +1226,7 @@ where } /// Returns whether the specified contract allows to be reentered right now. - fn allows_reentry(&self, id: &AccountIdOf) -> bool { + fn allows_reentry(&self, id: &T::AccountId) -> bool { !self.frames().any(|f| &f.account_id == id && !f.allows_reentry) } } @@ -1219,7 +1242,7 @@ where &mut self, gas_limit: Weight, deposit_limit: BalanceOf, - to: T::AccountId, + dest: &H160, value: BalanceOf, input_data: Vec, allows_reentry: bool, @@ -1230,9 +1253,11 @@ where // is caught by it. self.top_frame_mut().allows_reentry = allows_reentry; + let dest = T::AddressMapper::to_account_id(dest); + let try_call = || { - if !self.allows_reentry(&to) { - return Err(>::ReentranceDenied.into()) + if !self.allows_reentry(&dest) { + return Err(>::ReentranceDenied.into()); } // We ignore instantiate frames in our search for a cached contract. @@ -1240,13 +1265,13 @@ where // constructor: We disallow calling not fully constructed contracts. let cached_info = self .frames() - .find(|f| f.entry_point == ExportedFunction::Call && f.account_id == to) + .find(|f| f.entry_point == ExportedFunction::Call && f.account_id == dest) .and_then(|f| match &f.contract_info { CachedContract::Cached(contract) => Some(contract.clone()), _ => None, }); let executable = self.push_frame( - FrameArgs::Call { dest: to, cached_info, delegated_call: None }, + FrameArgs::Call { dest, cached_info, delegated_call: None }, value, gas_limit, deposit_limit, @@ -1267,7 +1292,7 @@ where fn delegate_call( &mut self, - code_hash: CodeHash, + code_hash: H256, input_data: Vec, ) -> Result { let executable = E::from_storage(code_hash, self.gas_meter_mut())?; @@ -1293,11 +1318,11 @@ where &mut self, gas_limit: Weight, deposit_limit: BalanceOf, - code_hash: CodeHash, + code_hash: H256, value: BalanceOf, input_data: Vec, - salt: &[u8], - ) -> Result<(AccountIdOf, ExecReturnValue), ExecError> { + salt: &[u8; 32], + ) -> Result<(H160, ExecReturnValue), ExecError> { let executable = E::from_storage(code_hash, self.gas_meter_mut())?; let sender = &self.top_frame().account_id; let executable = self.push_frame( @@ -1312,20 +1337,22 @@ where deposit_limit, self.is_read_only(), )?; - let account_id = self.top_frame().account_id.clone(); - self.run(executable, input_data).map(|ret| (account_id, ret)) + let address = T::AddressMapper::to_address(&self.top_frame().account_id); + self.run(executable, input_data).map(|ret| (address, ret)) } - fn terminate(&mut self, beneficiary: &AccountIdOf) -> DispatchResult { + fn terminate(&mut self, beneficiary: &H160) -> DispatchResult { if self.is_recursive() { - return Err(Error::::TerminatedWhileReentrant.into()) + return Err(Error::::TerminatedWhileReentrant.into()); } let frame = self.top_frame_mut(); let info = frame.terminate(); - frame.nested_storage.terminate(&info, beneficiary.clone()); + let beneficiary_account = T::AddressMapper::to_account_id(beneficiary); + frame.nested_storage.terminate(&info, beneficiary_account); info.queue_trie_for_deletion(); - ContractInfoOf::::remove(&frame.account_id); + let account_address = T::AddressMapper::to_address(&frame.account_id); + ContractInfoOf::::remove(&account_address); Self::decrement_refcount(info.code_hash); for (code_hash, deposit) in info.delegate_dependencies() { @@ -1336,14 +1363,19 @@ where } Contracts::::deposit_event(Event::Terminated { - contract: frame.account_id.clone(), - beneficiary: beneficiary.clone(), + contract: account_address, + beneficiary: *beneficiary, }); Ok(()) } - fn transfer(&mut self, to: &T::AccountId, value: BalanceOf) -> DispatchResult { - Self::transfer(Preservation::Preserve, &self.top_frame().account_id, to, value) + fn transfer(&mut self, to: &H160, value: BalanceOf) -> DispatchResult { + Self::transfer( + Preservation::Preserve, + &self.top_frame().account_id, + &T::AddressMapper::to_account_id(to), + value, + ) } fn get_storage(&mut self, key: &Key) -> Option> { @@ -1370,11 +1402,13 @@ where } fn get_transient_storage(&self, key: &Key) -> Option> { - self.transient_storage.read(self.address(), key) + self.transient_storage.read(self.account_id(), key) } fn get_transient_storage_size(&self, key: &Key) -> Option { - self.transient_storage.read(self.address(), key).map(|value| value.len() as _) + self.transient_storage + .read(self.account_id(), key) + .map(|value| value.len() as _) } fn set_transient_storage( @@ -1383,11 +1417,11 @@ where value: Option>, take_old: bool, ) -> Result { - let account_id = self.address().clone(); + let account_id = self.account_id().clone(); self.transient_storage.write(&account_id, key, value, take_old) } - fn address(&self) -> &T::AccountId { + fn account_id(&self) -> &T::AccountId { &self.top_frame().account_id } @@ -1402,15 +1436,15 @@ where } } - fn is_contract(&self, address: &T::AccountId) -> bool { + fn is_contract(&self, address: &H160) -> bool { ContractInfoOf::::contains_key(&address) } - fn code_hash(&self, address: &T::AccountId) -> Option> { + fn code_hash(&self, address: &H160) -> Option { >::get(&address).map(|contract| contract.code_hash) } - fn own_code_hash(&mut self) -> &CodeHash { + fn own_code_hash(&mut self) -> &H256 { &self.top_frame_mut().contract_info().code_hash } @@ -1446,7 +1480,10 @@ where fn deposit_event(&mut self, topics: Vec, data: Vec) { Contracts::::deposit_indexed_event( topics, - Event::ContractEmitted { contract: self.top_frame().account_id.clone(), data }, + Event::ContractEmitted { + contract: T::AddressMapper::to_address(self.account_id()), + data, + }, ); } @@ -1497,7 +1534,7 @@ where } fn call_runtime(&self, call: ::RuntimeCall) -> DispatchResultWithPostInfo { - let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.address().clone()).into(); + let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.account_id().clone()).into(); origin.add_filter(T::CallFilter::contains); call.dispatch(origin) } @@ -1528,7 +1565,7 @@ where &mut self.transient_storage } - fn set_code_hash(&mut self, hash: CodeHash) -> DispatchResult { + fn set_code_hash(&mut self, hash: H256) -> DispatchResult { let frame = top_frame_mut!(self); let info = frame.contract_info(); @@ -1548,14 +1585,14 @@ where Self::increment_refcount(hash)?; Self::decrement_refcount(prev_hash); Contracts::::deposit_event(Event::ContractCodeUpdated { - contract: frame.account_id.clone(), + contract: T::AddressMapper::to_address(&frame.account_id), new_code_hash: hash, old_code_hash: prev_hash, }); Ok(()) } - fn increment_refcount(code_hash: CodeHash) -> DispatchResult { + fn increment_refcount(code_hash: H256) -> DispatchResult { >::mutate(code_hash, |existing| -> Result<(), DispatchError> { if let Some(info) = existing { *info.refcount_mut() = info.refcount().saturating_add(1); @@ -1566,7 +1603,7 @@ where }) } - fn decrement_refcount(code_hash: CodeHash) { + fn decrement_refcount(code_hash: H256) { >::mutate(code_hash, |existing| { if let Some(info) = existing { *info.refcount_mut() = info.refcount().saturating_sub(1); @@ -1574,7 +1611,7 @@ where }); } - fn lock_delegate_dependency(&mut self, code_hash: CodeHash) -> DispatchResult { + fn lock_delegate_dependency(&mut self, code_hash: H256) -> DispatchResult { let frame = self.top_frame_mut(); let info = frame.contract_info.get(&frame.account_id); ensure!(code_hash != info.code_hash, Error::::CannotAddSelfAsDelegateDependency); @@ -1590,7 +1627,7 @@ where Ok(()) } - fn unlock_delegate_dependency(&mut self, code_hash: &CodeHash) -> DispatchResult { + fn unlock_delegate_dependency(&mut self, code_hash: &H256) -> DispatchResult { let frame = self.top_frame_mut(); let info = frame.contract_info.get(&frame.account_id); @@ -1635,10 +1672,9 @@ mod tests { test_utils::{get_balance, place_contract, set_balance}, ExtBuilder, RuntimeCall, RuntimeEvent as MetaEvent, Test, TestFilter, }, - Error, + AddressMapper, Error, }; use assert_matches::assert_matches; - use codec::{Decode, Encode}; use frame_support::{assert_err, assert_ok, parameter_types}; use frame_system::{EventRecord, Phase}; use pallet_revive_uapi::ReturnFlags; @@ -1673,25 +1709,25 @@ mod tests { struct MockExecutable { func: Rc Fn(MockCtx<'a>, &Self) -> ExecResult + 'static>, func_type: ExportedFunction, - code_hash: CodeHash, + code_hash: H256, code_info: CodeInfo, } #[derive(Default, Clone)] pub struct MockLoader { - map: HashMap, MockExecutable>, + map: HashMap, counter: u64, } impl MockLoader { - fn code_hashes() -> Vec> { + fn code_hashes() -> Vec { Loader::get().map.keys().copied().collect() } fn insert( func_type: ExportedFunction, f: impl Fn(MockCtx, &MockExecutable) -> ExecResult + 'static, - ) -> CodeHash { + ) -> H256 { Loader::mutate(|loader| { // Generate code hashes as monotonically increasing values. let hash = ::Hash::from_low_u64_be(loader.counter); @@ -1712,7 +1748,7 @@ mod tests { impl Executable for MockExecutable { fn from_storage( - code_hash: CodeHash, + code_hash: H256, _gas_meter: &mut GasMeter, ) -> Result { Loader::mutate(|loader| { @@ -1746,7 +1782,12 @@ mod tests { } } - fn code_hash(&self) -> &CodeHash { + fn code(&self) -> &[u8] { + // The mock executable doesn't have code", so we return the code hash. + self.code_hash.as_ref() + } + + fn code_hash(&self) -> &H256 { &self.code_hash } @@ -1784,7 +1825,7 @@ mod tests { assert_matches!( MockStack::run_call( Origin::from_account_id(ALICE), - BOB, + BOB_ADDR, &mut gas_meter, &mut storage_meter, value, @@ -1802,24 +1843,19 @@ mod tests { fn transfer_works() { // This test verifies that a contract is able to transfer // some funds to another account. - let origin = ALICE; - let dest = BOB; - ExtBuilder::default().build().execute_with(|| { - set_balance(&origin, 100); - set_balance(&dest, 0); + set_balance(&ALICE, 100); + set_balance(&BOB, 0); - MockStack::transfer(Preservation::Preserve, &origin, &dest, 55).unwrap(); + MockStack::transfer(Preservation::Preserve, &ALICE, &BOB, 55).unwrap(); - assert_eq!(get_balance(&origin), 45); - assert_eq!(get_balance(&dest), 55); + assert_eq!(get_balance(&ALICE), 45); + assert_eq!(get_balance(&BOB), 55); }); } #[test] fn correct_transfer_on_call() { - let origin = ALICE; - let dest = BOB; let value = 55; let success_ch = MockLoader::insert(Call, move |ctx, _| { @@ -1828,15 +1864,15 @@ mod tests { }); ExtBuilder::default().build().execute_with(|| { - place_contract(&dest, success_ch); - set_balance(&origin, 100); - let balance = get_balance(&dest); - let contract_origin = Origin::from_account_id(origin.clone()); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, value).unwrap(); + place_contract(&BOB, success_ch); + set_balance(&ALICE, 100); + let balance = get_balance(&BOB_CONTRACT_ID); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, value).unwrap(); let _ = MockStack::run_call( - contract_origin.clone(), - dest.clone(), + origin.clone(), + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, value, @@ -1845,15 +1881,13 @@ mod tests { ) .unwrap(); - assert_eq!(get_balance(&origin), 100 - value); - assert_eq!(get_balance(&dest), balance + value); + assert_eq!(get_balance(&ALICE), 100 - value); + assert_eq!(get_balance(&BOB_CONTRACT_ID), balance + value); }); } #[test] fn correct_transfer_on_delegate_call() { - let origin = ALICE; - let dest = BOB; let value = 35; let success_ch = MockLoader::insert(Call, move |ctx, _| { @@ -1868,15 +1902,15 @@ mod tests { }); ExtBuilder::default().build().execute_with(|| { - place_contract(&dest, delegate_ch); - set_balance(&origin, 100); - let balance = get_balance(&dest); - let contract_origin = Origin::from_account_id(origin.clone()); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 55).unwrap(); + place_contract(&BOB, delegate_ch); + set_balance(&ALICE, 100); + let balance = get_balance(&BOB_CONTRACT_ID); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 55).unwrap(); let _ = MockStack::run_call( - contract_origin.clone(), - dest.clone(), + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, value, @@ -1885,8 +1919,8 @@ mod tests { ) .unwrap(); - assert_eq!(get_balance(&origin), 100 - value); - assert_eq!(get_balance(&dest), balance + value); + assert_eq!(get_balance(&ALICE), 100 - value); + assert_eq!(get_balance(&BOB_CONTRACT_ID), balance + value); }); } @@ -1894,23 +1928,21 @@ mod tests { fn changes_are_reverted_on_failing_call() { // This test verifies that changes are reverted on a call which fails (or equally, returns // a non-zero status code). - let origin = ALICE; - let dest = BOB; let return_ch = MockLoader::insert(Call, |_, _| { Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: Vec::new() }) }); ExtBuilder::default().build().execute_with(|| { - place_contract(&dest, return_ch); - set_balance(&origin, 100); - let balance = get_balance(&dest); - let contract_origin = Origin::from_account_id(origin.clone()); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 55).unwrap(); + place_contract(&BOB, return_ch); + set_balance(&ALICE, 100); + let balance = get_balance(&BOB); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 55).unwrap(); let output = MockStack::run_call( - contract_origin.clone(), - dest.clone(), + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 55, @@ -1920,8 +1952,8 @@ mod tests { .unwrap(); assert!(output.did_revert()); - assert_eq!(get_balance(&origin), 100); - assert_eq!(get_balance(&dest), balance); + assert_eq!(get_balance(&ALICE), 100); + assert_eq!(get_balance(&BOB), balance); }); } @@ -1947,20 +1979,18 @@ mod tests { fn output_is_returned_on_success() { // Verifies that if a contract returns data with a successful exit status, this data // is returned from the execution context. - let origin = ALICE; - let dest = BOB; let return_ch = MockLoader::insert(Call, |_, _| { Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![1, 2, 3, 4] }) }); ExtBuilder::default().build().execute_with(|| { - let contract_origin = Origin::from_account_id(origin); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); place_contract(&BOB, return_ch); let result = MockStack::run_call( - contract_origin, - dest, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -1978,20 +2008,18 @@ mod tests { fn output_is_returned_on_failure() { // Verifies that if a contract returns data with a failing exit status, this data // is returned from the execution context. - let origin = ALICE; - let dest = BOB; let return_ch = MockLoader::insert(Call, |_, _| { Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![1, 2, 3, 4] }) }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, return_ch); - let contract_origin = Origin::from_account_id(origin); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - dest, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2015,12 +2043,12 @@ mod tests { // This one tests passing the input data into a contract via call. ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, input_data_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2048,13 +2076,10 @@ mod tests { let executable = MockExecutable::from_storage(input_data_ch, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 10_000); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new( - &contract_origin, - deposit_limit::(), - min_balance, - ) - .unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), min_balance) + .unwrap(); let result = MockStack::run_instantiate( ALICE, @@ -2063,7 +2088,7 @@ mod tests { &mut storage_meter, min_balance, vec![1, 2, 3, 4], - &[], + &[0; 32], None, ); assert_matches!(result, Ok(_)); @@ -2083,7 +2108,7 @@ mod tests { let r = ctx.ext.call( Weight::zero(), BalanceOf::::zero(), - BOB, + &BOB_ADDR, 0, vec![], true, @@ -2108,12 +2133,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { set_balance(&BOB, 1); place_contract(&BOB, recurse_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, value).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, value).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, value, @@ -2127,18 +2152,18 @@ mod tests { #[test] fn caller_returns_proper_values() { - let origin = ALICE; - let dest = BOB; - parameter_types! { - static WitnessedCallerBob: Option> = None; - static WitnessedCallerCharlie: Option> = None; + static WitnessedCallerBob: Option = None; + static WitnessedCallerCharlie: Option = None; } let bob_ch = MockLoader::insert(Call, |ctx, _| { // Record the caller for bob. WitnessedCallerBob::mutate(|caller| { - *caller = Some(ctx.ext.caller().account_id().unwrap().clone()) + let origin = ctx.ext.caller(); + *caller = Some(::AddressMapper::to_address( + &origin.account_id().unwrap(), + )); }); // Call into CHARLIE contract. @@ -2146,7 +2171,7 @@ mod tests { ctx.ext.call( Weight::zero(), BalanceOf::::zero(), - CHARLIE, + &CHARLIE_ADDR, 0, vec![], true, @@ -2159,20 +2184,23 @@ mod tests { let charlie_ch = MockLoader::insert(Call, |ctx, _| { // Record the caller for charlie. WitnessedCallerCharlie::mutate(|caller| { - *caller = Some(ctx.ext.caller().account_id().unwrap().clone()) + let origin = ctx.ext.caller(); + *caller = Some(::AddressMapper::to_address( + &origin.account_id().unwrap(), + )); }); exec_success() }); ExtBuilder::default().build().execute_with(|| { - place_contract(&dest, bob_ch); + place_contract(&BOB, bob_ch); place_contract(&CHARLIE, charlie_ch); - let contract_origin = Origin::from_account_id(origin.clone()); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin.clone(), - dest.clone(), + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2183,28 +2211,28 @@ mod tests { assert_matches!(result, Ok(_)); }); - assert_eq!(WitnessedCallerBob::get(), Some(origin)); - assert_eq!(WitnessedCallerCharlie::get(), Some(dest)); + assert_eq!(WitnessedCallerBob::get(), Some(ALICE_ADDR)); + assert_eq!(WitnessedCallerCharlie::get(), Some(BOB_ADDR)); } #[test] fn is_contract_returns_proper_values() { let bob_ch = MockLoader::insert(Call, |ctx, _| { // Verify that BOB is a contract - assert!(ctx.ext.is_contract(&BOB)); + assert!(ctx.ext.is_contract(&BOB_ADDR)); // Verify that ALICE is not a contract - assert!(!ctx.ext.is_contract(&ALICE)); + assert!(!ctx.ext.is_contract(&ALICE_ADDR)); exec_success() }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, bob_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2219,20 +2247,20 @@ mod tests { fn code_hash_returns_proper_values() { let code_bob = MockLoader::insert(Call, |ctx, _| { // ALICE is not a contract and hence they do not have a code_hash - assert!(ctx.ext.code_hash(&ALICE).is_none()); + assert!(ctx.ext.code_hash(&ALICE_ADDR).is_none()); // BOB is a contract and hence it has a code_hash - assert!(ctx.ext.code_hash(&BOB).is_some()); + assert!(ctx.ext.code_hash(&BOB_ADDR).is_some()); exec_success() }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // ALICE (not contract) -> BOB (contract) let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2246,19 +2274,19 @@ mod tests { #[test] fn own_code_hash_returns_proper_values() { let bob_ch = MockLoader::insert(Call, |ctx, _| { - let code_hash = ctx.ext.code_hash(&BOB).unwrap(); + let code_hash = ctx.ext.code_hash(&BOB_ADDR).unwrap(); assert_eq!(*ctx.ext.own_code_hash(), code_hash); exec_success() }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, bob_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // ALICE (not contract) -> BOB (contract) let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2281,19 +2309,26 @@ mod tests { // ALICE is the origin of the call stack assert!(ctx.ext.caller_is_origin()); // BOB calls CHARLIE - ctx.ext - .call(Weight::zero(), BalanceOf::::zero(), CHARLIE, 0, vec![], true, false) + ctx.ext.call( + Weight::zero(), + BalanceOf::::zero(), + &CHARLIE_ADDR, + 0, + vec![], + true, + false, + ) }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // ALICE -> BOB (caller is origin) -> CHARLIE (caller is not origin) let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2314,12 +2349,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); - let contract_origin = Origin::Root; - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::Root; + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // root -> BOB (caller is root) let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2340,12 +2375,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); - let contract_origin = Origin::Root; - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::Root; + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // root -> BOB (caller is root) let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 1, @@ -2368,19 +2403,26 @@ mod tests { // root is the origin of the call stack. assert!(ctx.ext.caller_is_root()); // BOB calls CHARLIE. - ctx.ext - .call(Weight::zero(), BalanceOf::::zero(), CHARLIE, 0, vec![], true, false) + ctx.ext.call( + Weight::zero(), + BalanceOf::::zero(), + &CHARLIE_ADDR, + 0, + vec![], + true, + false, + ) }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::Root; - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::Root; + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // root -> BOB (caller is root) -> CHARLIE (caller is not root) let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2395,14 +2437,14 @@ mod tests { fn address_returns_proper_values() { let bob_ch = MockLoader::insert(Call, |ctx, _| { // Verify that address matches BOB. - assert_eq!(*ctx.ext.address(), BOB); + assert_eq!(ctx.ext.address(), BOB_ADDR); // Call into charlie contract. assert_matches!( ctx.ext.call( Weight::zero(), BalanceOf::::zero(), - CHARLIE, + &CHARLIE_ADDR, 0, vec![], true, @@ -2413,19 +2455,19 @@ mod tests { exec_success() }); let charlie_ch = MockLoader::insert(Call, |ctx, _| { - assert_eq!(*ctx.ext.address(), CHARLIE); + assert_eq!(ctx.ext.address(), CHARLIE_ADDR); exec_success() }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, bob_ch); place_contract(&CHARLIE, charlie_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2444,8 +2486,8 @@ mod tests { ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap(); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); assert_matches!( MockStack::run_instantiate( @@ -2455,7 +2497,7 @@ mod tests { &mut storage_meter, 0, // <- zero value vec![], - &[], + &[0; 32], None, ), Err(_) @@ -2478,10 +2520,9 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 1000); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, min_balance * 100, min_balance) - .unwrap(); + storage::meter::Meter::new(&origin, min_balance * 100, min_balance).unwrap(); let instantiated_contract_address = assert_matches!( MockStack::run_instantiate( @@ -2492,22 +2533,26 @@ mod tests { min_balance, vec![], - &[], + &[0;32], None, ), Ok((address, ref output)) if output.data == vec![80, 65, 83, 83] => address ); + let instantiated_contract_id = + ::AddressMapper::to_account_id_contract( + &instantiated_contract_address, + ); // Check that the newly created account has the expected code hash and // there are instantiation event. assert_eq!( - ContractInfo::::load_code_hash(&instantiated_contract_address).unwrap(), + ContractInfo::::load_code_hash(&instantiated_contract_id).unwrap(), dummy_ch ); assert_eq!( &events(), &[Event::Instantiated { - deployer: ALICE, + deployer: ALICE_ADDR, contract: instantiated_contract_address }] ); @@ -2529,10 +2574,9 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 1000); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, min_balance * 100, min_balance) - .unwrap(); + storage::meter::Meter::new(&origin, min_balance * 100, min_balance).unwrap(); let instantiated_contract_address = assert_matches!( MockStack::run_instantiate( @@ -2543,16 +2587,19 @@ mod tests { min_balance, vec![], - &[], + &[0;32], None, ), Ok((address, ref output)) if output.data == vec![70, 65, 73, 76] => address ); + let instantiated_contract_id = + ::AddressMapper::to_account_id_contract( + &instantiated_contract_address, + ); + // Check that the account has not been created. - assert!( - ContractInfo::::load_code_hash(&instantiated_contract_address).is_none() - ); + assert!(ContractInfo::::load_code_hash(&instantiated_contract_id).is_none()); assert!(events().is_empty()); }); } @@ -2560,7 +2607,7 @@ mod tests { #[test] fn instantiation_from_contract() { let dummy_ch = MockLoader::insert(Call, |_, _| exec_success()); - let instantiated_contract_address = Rc::new(RefCell::new(None::>)); + let instantiated_contract_address = Rc::new(RefCell::new(None::)); let instantiator_ch = MockLoader::insert(Call, { let instantiated_contract_address = Rc::clone(&instantiated_contract_address); move |ctx, _| { @@ -2573,11 +2620,11 @@ mod tests { dummy_ch, ::Currency::minimum_balance(), vec![], - &[48, 49, 50], + &[48; 32], ) .unwrap(); - *instantiated_contract_address.borrow_mut() = address.into(); + *instantiated_contract_address.borrow_mut() = Some(address); Ok(output) } }); @@ -2590,18 +2637,15 @@ mod tests { let min_balance = ::Currency::minimum_balance(); set_balance(&ALICE, min_balance * 100); place_contract(&BOB, instantiator_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new( - &contract_origin, - min_balance * 10, - min_balance * 10, - ) - .unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, min_balance * 10, min_balance * 10) + .unwrap(); assert_matches!( MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, min_balance * 10, @@ -2612,22 +2656,30 @@ mod tests { ); let instantiated_contract_address = - instantiated_contract_address.borrow().as_ref().unwrap().clone(); + *instantiated_contract_address.borrow().as_ref().unwrap(); + + let instantiated_contract_id = + ::AddressMapper::to_account_id_contract( + &instantiated_contract_address, + ); // Check that the newly created account has the expected code hash and // there are instantiation event. assert_eq!( - ContractInfo::::load_code_hash(&instantiated_contract_address).unwrap(), + ContractInfo::::load_code_hash(&instantiated_contract_id).unwrap(), dummy_ch ); assert_eq!( &events(), &[ Event::Instantiated { - deployer: BOB, + deployer: BOB_ADDR, contract: instantiated_contract_address }, - Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB }, + Event::Called { + caller: Origin::from_account_id(ALICE), + contract: BOB_ADDR + }, ] ); }); @@ -2646,7 +2698,7 @@ mod tests { dummy_ch, ::Currency::minimum_balance(), vec![], - &[], + &[0; 32], ), Err(ExecError { error: DispatchError::Other("It's a trap!"), @@ -2664,16 +2716,15 @@ mod tests { .build() .execute_with(|| { set_balance(&ALICE, 1000); - set_balance(&BOB, 100); + set_balance(&BOB_CONTRACT_ID, 100); place_contract(&BOB, instantiator_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = - storage::meter::Meter::new(&contract_origin, 200, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); assert_matches!( MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2687,7 +2738,7 @@ mod tests { // event here. assert_eq!( &events(), - &[Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB },] + &[Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB_ADDR },] ); }); } @@ -2695,7 +2746,7 @@ mod tests { #[test] fn termination_from_instantiate_fails() { let terminate_ch = MockLoader::insert(Constructor, |ctx, _| { - ctx.ext.terminate(&ALICE).unwrap(); + ctx.ext.terminate(&ALICE_ADDR).unwrap(); exec_success() }); @@ -2708,10 +2759,9 @@ mod tests { let executable = MockExecutable::from_storage(terminate_ch, &mut gas_meter).unwrap(); set_balance(&ALICE, 10_000); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 100) - .unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 100).unwrap(); assert_eq!( MockStack::run_instantiate( @@ -2721,7 +2771,7 @@ mod tests { &mut storage_meter, 100, vec![], - &[], + &[0; 32], None, ), Err(Error::::TerminatedInConstructor.into()) @@ -2750,7 +2800,7 @@ mod tests { ctx.ext.call( Weight::zero(), BalanceOf::::zero(), - CHARLIE, + &CHARLIE_ADDR, 0, vec![], true, @@ -2765,7 +2815,15 @@ mod tests { let code_charlie = MockLoader::insert(Call, |ctx, _| { assert!(ctx .ext - .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![99], true, false) + .call( + Weight::zero(), + BalanceOf::::zero(), + &BOB_ADDR, + 0, + vec![99], + true, + false + ) .is_ok()); exec_trapped() }); @@ -2774,12 +2832,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2793,10 +2851,13 @@ mod tests { #[test] fn recursive_call_during_constructor_fails() { let code = MockLoader::insert(Constructor, |ctx, _| { + let account_id = ctx.ext.account_id().clone(); + let addr = ::AddressMapper::to_address(&account_id); + assert_matches!( - ctx.ext.call(Weight::zero(), BalanceOf::::zero(), ctx.ext.address().clone(), 0, vec![], true, false), - Err(ExecError{error, ..}) if error == >::ContractNotFound.into() - ); + ctx.ext.call(Weight::zero(), BalanceOf::::zero(), &addr, 0, vec![], + true, false), Err(ExecError{error, ..}) if error == >::ContractNotFound.into() + ); exec_success() }); @@ -2809,13 +2870,10 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(code, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 10_000); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new( - &contract_origin, - deposit_limit::(), - min_balance, - ) - .unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), min_balance) + .unwrap(); let result = MockStack::run_instantiate( ALICE, @@ -2824,7 +2882,7 @@ mod tests { &mut storage_meter, min_balance, vec![], - &[], + &[0; 32], None, ); assert_matches!(result, Ok(_)); @@ -2847,11 +2905,11 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -2880,11 +2938,11 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -2913,11 +2971,11 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -2933,9 +2991,9 @@ mod tests { fn call_reentry_direct_recursion() { // call the contract passed as input with disabled reentry let code_bob = MockLoader::insert(Call, |ctx, _| { - let dest = Decode::decode(&mut ctx.input_data.as_ref()).unwrap(); + let dest = H160::from_slice(ctx.input_data.as_ref()); ctx.ext - .call(Weight::zero(), BalanceOf::::zero(), dest, 0, vec![], false, false) + .call(Weight::zero(), BalanceOf::::zero(), &dest, 0, vec![], false, false) }); let code_charlie = MockLoader::insert(Call, |_, _| exec_success()); @@ -2943,29 +3001,29 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // Calling another contract should succeed assert_ok!(MockStack::run_call( - contract_origin.clone(), - BOB, + origin.clone(), + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, - CHARLIE.encode(), + CHARLIE_ADDR.as_bytes().to_vec(), None, )); // Calling into oneself fails assert_err!( MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, - BOB.encode(), + BOB_ADDR.as_bytes().to_vec(), None, ) .map_err(|e| e.error), @@ -2981,7 +3039,7 @@ mod tests { ctx.ext.call( Weight::zero(), BalanceOf::::zero(), - CHARLIE, + &CHARLIE_ADDR, 0, vec![], false, @@ -2994,21 +3052,28 @@ mod tests { // call BOB with input set to '1' let code_charlie = MockLoader::insert(Call, |ctx, _| { - ctx.ext - .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![1], true, false) + ctx.ext.call( + Weight::zero(), + BalanceOf::::zero(), + &BOB_ADDR, + 0, + vec![1], + true, + false, + ) }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // BOB -> CHARLIE -> BOB fails as BOB denies reentry. assert_err!( MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -3037,12 +3102,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); System::reset_events(); MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3058,7 +3123,7 @@ mod tests { EventRecord { phase: Phase::Initialization, event: MetaEvent::System(frame_system::Event::Remarked { - sender: BOB, + sender: BOB_CONTRACT_ID, hash: remark_hash }), topics: vec![], @@ -3067,7 +3132,7 @@ mod tests { phase: Phase::Initialization, event: MetaEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: BOB, + contract: BOB_ADDR, }), topics: vec![], }, @@ -3121,12 +3186,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); System::reset_events(); MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3142,7 +3207,7 @@ mod tests { EventRecord { phase: Phase::Initialization, event: MetaEvent::System(frame_system::Event::Remarked { - sender: BOB, + sender: BOB_CONTRACT_ID, hash: remark_hash }), topics: vec![], @@ -3164,7 +3229,7 @@ mod tests { phase: Phase::Initialization, event: MetaEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: BOB, + contract: BOB_ADDR, }), topics: vec![], }, @@ -3185,16 +3250,16 @@ mod tests { fail_code, ctx.ext.minimum_balance() * 100, vec![], - &[], + &[0; 32], ) .ok(); exec_success() }); let succ_succ_code = MockLoader::insert(Constructor, move |ctx, _| { let alice_nonce = System::account_nonce(&ALICE); - assert_eq!(System::account_nonce(ctx.ext.address()), 0); + assert_eq!(System::account_nonce(ctx.ext.account_id()), 0); assert_eq!(ctx.ext.caller().account_id().unwrap(), &ALICE); - let (account_id, _) = ctx + let (addr, _) = ctx .ext .instantiate( Weight::zero(), @@ -3202,29 +3267,23 @@ mod tests { success_code, ctx.ext.minimum_balance() * 100, vec![], - &[], + &[0; 32], ) .unwrap(); + let account_id = ::AddressMapper::to_account_id_contract(&addr); + assert_eq!(System::account_nonce(&ALICE), alice_nonce); - assert_eq!(System::account_nonce(ctx.ext.address()), 1); + assert_eq!(System::account_nonce(ctx.ext.account_id()), 1); assert_eq!(System::account_nonce(&account_id), 0); // a plain call should not influence the account counter ctx.ext - .call( - Weight::zero(), - BalanceOf::::zero(), - account_id.clone(), - 0, - vec![], - false, - false, - ) + .call(Weight::zero(), BalanceOf::::zero(), &addr, 0, vec![], false, false) .unwrap(); assert_eq!(System::account_nonce(ALICE), alice_nonce); - assert_eq!(System::account_nonce(ctx.ext.address()), 1); + assert_eq!(System::account_nonce(ctx.ext.account_id()), 1); assert_eq!(System::account_nonce(&account_id), 0); exec_success() @@ -3246,13 +3305,10 @@ mod tests { MockExecutable::from_storage(succ_succ_code, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 10_000); set_balance(&BOB, min_balance * 10_000); - let contract_origin = Origin::from_account_id(BOB); - let mut storage_meter = storage::meter::Meter::new( - &contract_origin, - deposit_limit::(), - min_balance * 100, - ) - .unwrap(); + let origin = Origin::from_account_id(BOB); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), min_balance * 100) + .unwrap(); // fail should not increment MockStack::run_instantiate( @@ -3262,7 +3318,7 @@ mod tests { &mut storage_meter, min_balance * 100, vec![], - &[], + &[0; 32], None, ) .ok(); @@ -3275,7 +3331,7 @@ mod tests { &mut storage_meter, min_balance * 100, vec![], - &[], + &[0; 32], None, )); assert_eq!(System::account_nonce(&ALICE), 1); @@ -3287,7 +3343,7 @@ mod tests { &mut storage_meter, min_balance * 200, vec![], - &[], + &[0; 32], None, )); assert_eq!(System::account_nonce(&ALICE), 2); @@ -3299,7 +3355,7 @@ mod tests { &mut storage_meter, min_balance * 200, vec![], - &[], + &[0; 32], None, )); assert_eq!(System::account_nonce(&ALICE), 3); @@ -3358,12 +3414,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3469,12 +3525,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3508,12 +3564,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3547,12 +3603,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3600,12 +3656,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3656,12 +3712,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3731,12 +3787,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -3762,7 +3818,7 @@ mod tests { ctx.ext.call( Weight::zero(), BalanceOf::::zero(), - CHARLIE, + &CHARLIE_ADDR, 0, vec![], true, @@ -3788,7 +3844,15 @@ mod tests { let code_charlie = MockLoader::insert(Call, |ctx, _| { assert!(ctx .ext - .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![99], true, false) + .call( + Weight::zero(), + BalanceOf::::zero(), + &BOB_ADDR, + 0, + vec![99], + true, + false + ) .is_ok()); // CHARLIE can not read BOB`s storage. assert_eq!(ctx.ext.get_transient_storage(storage_key_1), None); @@ -3799,12 +3863,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -3838,11 +3902,11 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -3866,7 +3930,7 @@ mod tests { ctx.ext.call( Weight::zero(), BalanceOf::::zero(), - CHARLIE, + &CHARLIE_ADDR, 0, vec![], true, @@ -3888,7 +3952,15 @@ mod tests { let code_charlie = MockLoader::insert(Call, |ctx, _| { assert!(ctx .ext - .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![99], true, false) + .call( + Weight::zero(), + BalanceOf::::zero(), + &BOB_ADDR, + 0, + vec![99], + true, + false + ) .is_ok()); exec_trapped() }); @@ -3897,12 +3969,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -3931,11 +4003,11 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, bob_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 303c649bc8cf..359434b9abd6 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -70,13 +70,14 @@ use frame_system::{ EventRecord, Pallet as System, }; use scale_info::TypeInfo; +use sp_core::{H160, H256}; use sp_runtime::{ - traits::{BadOrigin, Convert, Dispatchable, Saturating, StaticLookup}, + traits::{BadOrigin, Convert, Dispatchable, Saturating}, DispatchError, }; pub use crate::{ - address::{AddressGenerator, DefaultAddressGenerator}, + address::{AddressMapper, DefaultAddressMapper}, debug::Tracing, migration::{MigrateSequence, Migration, NoopMigration}, pallet::*, @@ -86,12 +87,10 @@ pub use weights::WeightInfo; #[cfg(doc)] pub use crate::wasm::SyscallDoc; -type CodeHash = ::Hash; type TrieId = BoundedVec>; type BalanceOf = <::Currency as Inspect<::AccountId>>::Balance; type CodeVec = BoundedVec::MaxCodeLen>; -type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; type EventRecordOf = EventRecord<::RuntimeEvent, ::Hash>; type DebugBuffer = BoundedVec>; @@ -228,9 +227,9 @@ pub mod pallet { #[pallet::constant] type CodeHashLockupDepositPercent: Get; - /// The address generator used to generate the addresses of contracts. + /// Only valid type is [`DefaultAddressMapper`]. #[pallet::no_default_bounds] - type AddressGenerator: AddressGenerator; + type AddressMapper: AddressMapper>; /// The maximum length of a contract code in bytes. /// @@ -376,8 +375,7 @@ pub mod pallet { #[inject_runtime_type] type RuntimeCall = (); - - type AddressGenerator = DefaultAddressGenerator; + type AddressMapper = DefaultAddressMapper; type CallFilter = (); type ChainExtension = (); type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; @@ -401,7 +399,7 @@ pub mod pallet { #[pallet::event] pub enum Event { /// Contract deployed by address at the specified address. - Instantiated { deployer: T::AccountId, contract: T::AccountId }, + Instantiated { deployer: H160, contract: H160 }, /// Contract has been removed. /// @@ -411,34 +409,34 @@ pub mod pallet { /// `seal_terminate`. Terminated { /// The contract that was terminated. - contract: T::AccountId, + contract: H160, /// The account that received the contracts remaining balance - beneficiary: T::AccountId, + beneficiary: H160, }, /// Code with the specified hash has been stored. - CodeStored { code_hash: T::Hash, deposit_held: BalanceOf, uploader: T::AccountId }, + CodeStored { code_hash: H256, deposit_held: BalanceOf, uploader: H160 }, /// A custom event emitted by the contract. ContractEmitted { /// The contract that emitted the event. - contract: T::AccountId, + contract: H160, /// Data supplied by the contract. Metadata generated during contract compilation /// is needed to decode it. data: Vec, }, /// A code with the specified hash was removed. - CodeRemoved { code_hash: T::Hash, deposit_released: BalanceOf, remover: T::AccountId }, + CodeRemoved { code_hash: H256, deposit_released: BalanceOf, remover: H160 }, /// A contract's code was updated. ContractCodeUpdated { /// The contract that has been updated. - contract: T::AccountId, + contract: H160, /// New code hash that was set for the contract. - new_code_hash: T::Hash, + new_code_hash: H256, /// Previous code hash of the contract. - old_code_hash: T::Hash, + old_code_hash: H256, }, /// A contract was called either by a plain account or another contract. @@ -452,7 +450,7 @@ pub mod pallet { /// The caller of the `contract`. caller: Origin, /// The contract that was called. - contract: T::AccountId, + contract: H160, }, /// A contract delegate called a code hash. @@ -465,24 +463,16 @@ pub mod pallet { DelegateCalled { /// The contract that performed the delegate call and hence in whose context /// the `code_hash` is executed. - contract: T::AccountId, + contract: H160, /// The code hash that was delegate called. - code_hash: CodeHash, + code_hash: H256, }, /// Some funds have been transferred and held as storage deposit. - StorageDepositTransferredAndHeld { - from: T::AccountId, - to: T::AccountId, - amount: BalanceOf, - }, + StorageDepositTransferredAndHeld { from: H160, to: H160, amount: BalanceOf }, /// Some storage deposit funds have been transferred and released. - StorageDepositTransferredAndReleased { - from: T::AccountId, - to: T::AccountId, - amount: BalanceOf, - }, + StorageDepositTransferredAndReleased { from: H160, to: H160, amount: BalanceOf }, } #[pallet::error] @@ -592,16 +582,15 @@ pub mod pallet { /// A mapping from a contract's code hash to its code. #[pallet::storage] - pub(crate) type PristineCode = StorageMap<_, Identity, CodeHash, CodeVec>; + pub(crate) type PristineCode = StorageMap<_, Identity, H256, CodeVec>; /// A mapping from a contract's code hash to its code info. #[pallet::storage] - pub(crate) type CodeInfoOf = StorageMap<_, Identity, CodeHash, CodeInfo>; + pub(crate) type CodeInfoOf = StorageMap<_, Identity, H256, CodeInfo>; /// The code associated with a given account. #[pallet::storage] - pub(crate) type ContractInfoOf = - StorageMap<_, Identity, T::AccountId, ContractInfo>; + pub(crate) type ContractInfoOf = StorageMap<_, Identity, H160, ContractInfo>; /// Evicted contracts that await child trie deletion. /// @@ -804,13 +793,12 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::call().saturating_add(*gas_limit))] pub fn call( origin: OriginFor, - dest: AccountIdLookupOf, + dest: H160, #[pallet::compact] value: BalanceOf, gas_limit: Weight, #[pallet::compact] storage_deposit_limit: BalanceOf, data: Vec, ) -> DispatchResultWithPostInfo { - let dest = T::Lookup::lookup(dest)?; let mut output = Self::bare_call( origin, dest, @@ -843,9 +831,9 @@ pub mod pallet { #[pallet::compact] value: BalanceOf, gas_limit: Weight, #[pallet::compact] storage_deposit_limit: BalanceOf, - code_hash: CodeHash, + code_hash: sp_core::H256, data: Vec, - salt: Vec, + salt: [u8; 32], ) -> DispatchResultWithPostInfo { let data_len = data.len() as u32; let salt_len = salt.len() as u32; @@ -887,7 +875,7 @@ pub mod pallet { /// from the caller to pay for the storage consumed. /// * `code`: The contract code to deploy in raw bytes. /// * `data`: The input data to pass to the contract constructor. - /// * `salt`: Used for the address derivation. See [`Pallet::contract_address`]. + /// * `salt`: Used for the address derivation. See [`crate::address::create2`]. /// /// Instantiation is executed as follows: /// @@ -909,7 +897,7 @@ pub mod pallet { #[pallet::compact] storage_deposit_limit: BalanceOf, code: Vec, data: Vec, - salt: Vec, + salt: [u8; 32], ) -> DispatchResultWithPostInfo { let code_len = code.len() as u32; let data_len = data.len() as u32; @@ -967,7 +955,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::remove_code())] pub fn remove_code( origin: OriginFor, - code_hash: CodeHash, + code_hash: sp_core::H256, ) -> DispatchResultWithPostInfo { Migration::::ensure_migrated()?; let origin = ensure_signed(origin)?; @@ -990,12 +978,11 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::set_code())] pub fn set_code( origin: OriginFor, - dest: AccountIdLookupOf, - code_hash: CodeHash, + dest: H160, + code_hash: sp_core::H256, ) -> DispatchResult { Migration::::ensure_migrated()?; ensure_root(origin)?; - let dest = T::Lookup::lookup(dest)?; >::try_mutate(&dest, |contract| { let contract = if let Some(contract) = contract { contract @@ -1005,7 +992,7 @@ pub mod pallet { >>::increment_refcount(code_hash)?; >>::decrement_refcount(contract.code_hash); Self::deposit_event(Event::ContractCodeUpdated { - contract: dest.clone(), + contract: dest, new_code_hash: code_hash, old_code_hash: contract.code_hash, }); @@ -1075,7 +1062,7 @@ impl Pallet { /// collection). pub fn bare_call( origin: OriginFor, - dest: T::AccountId, + dest: H160, value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, @@ -1133,12 +1120,12 @@ impl Pallet { value: BalanceOf, gas_limit: Weight, mut storage_deposit_limit: BalanceOf, - code: Code>, + code: Code, data: Vec, - salt: Vec, + salt: [u8; 32], debug: DebugInfo, collect_events: CollectEvents, - ) -> ContractInstantiateResult, EventRecordOf> { + ) -> ContractInstantiateResult, EventRecordOf> { let mut gas_meter = GasMeter::new(gas_limit); let mut storage_deposit = Default::default(); let mut debug_message = @@ -1187,7 +1174,7 @@ impl Pallet { }; ContractInstantiateResult { result: output - .map(|(account_id, result)| InstantiateReturnValue { result, account_id }) + .map(|(addr, result)| InstantiateReturnValue { result, addr }) .map_err(|e| e.error), gas_consumed: gas_meter.gas_consumed(), gas_required: gas_meter.gas_required(), @@ -1204,7 +1191,7 @@ impl Pallet { origin: OriginFor, code: Vec, storage_deposit_limit: BalanceOf, - ) -> CodeUploadResult, BalanceOf> { + ) -> CodeUploadResult> { Migration::::ensure_migrated()?; let origin = T::UploadOrigin::ensure_origin(origin)?; let (module, deposit) = Self::try_upload_code(origin, code, storage_deposit_limit, None)?; @@ -1212,34 +1199,17 @@ impl Pallet { } /// Query storage of a specified contract under a specified key. - pub fn get_storage(address: T::AccountId, key: Vec) -> GetStorageResult { + pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult { if Migration::::in_progress() { return Err(ContractAccessError::MigrationInProgress) } let contract_info = ContractInfoOf::::get(&address).ok_or(ContractAccessError::DoesntExist)?; - let maybe_value = contract_info.read( - &Key::try_from_var(key) - .map_err(|_| ContractAccessError::KeyDecodingFailed)? - .into(), - ); + let maybe_value = contract_info.read(&Key::from_fixed(key)); Ok(maybe_value) } - /// Determine the address of a contract. - /// - /// This is the address generation function used by contract instantiation. See - /// [`DefaultAddressGenerator`] for the default implementation. - pub fn contract_address( - deploying_address: &T::AccountId, - code_hash: &CodeHash, - input_data: &[u8], - salt: &[u8], - ) -> T::AccountId { - T::AddressGenerator::contract_address(deploying_address, code_hash, input_data, salt) - } - /// Uploads new code and returns the Wasm blob and deposit amount collected. fn try_upload_code( origin: T::AccountId, @@ -1300,11 +1270,10 @@ environmental!(executing_contract: bool); sp_api::decl_runtime_apis! { /// The API used to dry-run contract interactions. #[api_version(1)] - pub trait ReviveApi where + pub trait ReviveApi where AccountId: Codec, Balance: Codec, BlockNumber: Codec, - Hash: Codec, EventRecord: Codec, { /// Perform a call from a specified account to a given contract. @@ -1312,7 +1281,7 @@ sp_api::decl_runtime_apis! { /// See [`crate::Pallet::bare_call`]. fn call( origin: AccountId, - dest: AccountId, + dest: H160, value: Balance, gas_limit: Option, storage_deposit_limit: Option, @@ -1327,10 +1296,10 @@ sp_api::decl_runtime_apis! { value: Balance, gas_limit: Option, storage_deposit_limit: Option, - code: Code, + code: Code, data: Vec, - salt: Vec, - ) -> ContractInstantiateResult; + salt: [u8; 32], + ) -> ContractInstantiateResult; /// Upload new code without instantiating a contract from it. /// @@ -1339,7 +1308,7 @@ sp_api::decl_runtime_apis! { origin: AccountId, code: Vec, storage_deposit_limit: Option, - ) -> CodeUploadResult; + ) -> CodeUploadResult; /// Query a given storage key in a given contract. /// @@ -1347,8 +1316,8 @@ sp_api::decl_runtime_apis! { /// specified account and `Ok(None)` if it doesn't. If the account specified by the address /// doesn't exist, or doesn't have a contract then `Err` is returned. fn get_storage( - address: AccountId, - key: Vec, + address: H160, + key: [u8; 32], ) -> GetStorageResult; } } diff --git a/substrate/frame/revive/src/primitives.rs b/substrate/frame/revive/src/primitives.rs index a4a1133b7104..98e8879457bf 100644 --- a/substrate/frame/revive/src/primitives.rs +++ b/substrate/frame/revive/src/primitives.rs @@ -17,6 +17,7 @@ //! A crate that hosts a common definitions that are relevant for the pallet-revive. +use crate::H160; use alloc::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::weights::Weight; @@ -86,12 +87,11 @@ pub type ContractExecResult = ContractResult, Balance, EventRecord>; /// Result type of a `bare_instantiate` call as well as `ContractsApi::instantiate`. -pub type ContractInstantiateResult = - ContractResult, DispatchError>, Balance, EventRecord>; +pub type ContractInstantiateResult = + ContractResult, Balance, EventRecord>; /// Result type of a `bare_code_upload` call. -pub type CodeUploadResult = - Result, DispatchError>; +pub type CodeUploadResult = Result, DispatchError>; /// Result type of a `get_storage` call. pub type GetStorageResult = Result>, ContractAccessError>; @@ -125,29 +125,29 @@ impl ExecReturnValue { /// The result of a successful contract instantiation. #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct InstantiateReturnValue { +pub struct InstantiateReturnValue { /// The output of the called constructor. pub result: ExecReturnValue, - /// The account id of the new contract. - pub account_id: AccountId, + /// The address of the new contract. + pub addr: H160, } /// The result of successfully uploading a contract. #[derive(Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)] -pub struct CodeUploadReturnValue { +pub struct CodeUploadReturnValue { /// The key under which the new code is stored. - pub code_hash: CodeHash, + pub code_hash: sp_core::H256, /// The deposit that was reserved at the caller. Is zero when the code already existed. pub deposit: Balance, } /// Reference to an existing code hash or a new wasm module. #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum Code { +pub enum Code { /// A wasm module as raw bytes. Upload(Vec), /// The code hash of an on-chain wasm blob. - Existing(Hash), + Existing(sp_core::H256), } /// The amount of balance that was either charged or refunded in order to pay for storage. diff --git a/substrate/frame/revive/src/storage.rs b/substrate/frame/revive/src/storage.rs index 87274ce407fa..91b7b904d2bb 100644 --- a/substrate/frame/revive/src/storage.rs +++ b/substrate/frame/revive/src/storage.rs @@ -20,12 +20,13 @@ pub mod meter; use crate::{ + address::AddressMapper, exec::{AccountIdOf, Key}, limits, storage::meter::Diff, weights::WeightInfo, - BalanceOf, CodeHash, CodeInfo, Config, ContractInfoOf, DeletionQueue, DeletionQueueCounter, - Error, TrieId, SENTINEL, + BalanceOf, CodeInfo, Config, ContractInfoOf, DeletionQueue, DeletionQueueCounter, Error, + TrieId, SENTINEL, }; use alloc::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; @@ -36,7 +37,7 @@ use frame_support::{ CloneNoBound, DefaultNoBound, }; use scale_info::TypeInfo; -use sp_core::{ConstU32, Get}; +use sp_core::{ConstU32, Get, H160}; use sp_io::KillStorageResult; use sp_runtime::{ traits::{Hash, Saturating, Zero}, @@ -44,7 +45,7 @@ use sp_runtime::{ }; type DelegateDependencyMap = - BoundedBTreeMap, BalanceOf, ConstU32<{ limits::DELEGATE_DEPENDENCIES }>>; + BoundedBTreeMap, ConstU32<{ limits::DELEGATE_DEPENDENCIES }>>; /// Information for managing an account and its sub trie abstraction. /// This is the required info to cache for an account. @@ -54,7 +55,7 @@ pub struct ContractInfo { /// Unique ID for the subtree encoded as a bytes vector. pub trie_id: TrieId, /// The code associated with a given account. - pub code_hash: CodeHash, + pub code_hash: sp_core::H256, /// How many bytes of storage are accumulated in this contract's child trie. storage_bytes: u32, /// How many items of storage are accumulated in this contract's child trie. @@ -82,9 +83,9 @@ impl ContractInfo { /// This returns an `Err` if an contract with the supplied `account` already exists /// in storage. pub fn new( - account: &AccountIdOf, + account: &H160, nonce: T::Nonce, - code_hash: CodeHash, + code_hash: sp_core::H256, ) -> Result { if >::contains_key(account) { return Err(Error::::DuplicateContract.into()) @@ -259,7 +260,7 @@ impl ContractInfo { /// the delegate dependency already exists. pub fn lock_delegate_dependency( &mut self, - code_hash: CodeHash, + code_hash: sp_core::H256, amount: BalanceOf, ) -> DispatchResult { self.delegate_dependencies @@ -275,7 +276,7 @@ impl ContractInfo { /// Returns an error if the entry doesn't exist. pub fn unlock_delegate_dependency( &mut self, - code_hash: &CodeHash, + code_hash: &sp_core::H256, ) -> Result, DispatchError> { self.delegate_dependencies .remove(code_hash) @@ -352,8 +353,8 @@ impl ContractInfo { } /// Returns the code hash of the contract specified by `account` ID. - pub fn load_code_hash(account: &AccountIdOf) -> Option> { - >::get(account).map(|i| i.code_hash) + pub fn load_code_hash(account: &AccountIdOf) -> Option { + >::get(&T::AddressMapper::to_address(account)).map(|i| i.code_hash) } } diff --git a/substrate/frame/revive/src/storage/meter.rs b/substrate/frame/revive/src/storage/meter.rs index 8735aa823421..f6ad4c5fc346 100644 --- a/substrate/frame/revive/src/storage/meter.rs +++ b/substrate/frame/revive/src/storage/meter.rs @@ -18,8 +18,8 @@ //! This module contains functions to meter the storage deposit. use crate::{ - storage::ContractInfo, AccountIdOf, BalanceOf, CodeInfo, Config, Error, Event, HoldReason, - Inspect, Origin, Pallet, StorageDeposit as Deposit, System, LOG_TARGET, + address::AddressMapper, storage::ContractInfo, AccountIdOf, BalanceOf, CodeInfo, Config, Error, + Event, HoldReason, Inspect, Origin, Pallet, StorageDeposit as Deposit, System, LOG_TARGET, }; use alloc::vec::Vec; @@ -537,8 +537,8 @@ impl Ext for ReservingExt { )?; Pallet::::deposit_event(Event::StorageDepositTransferredAndHeld { - from: origin.clone(), - to: contract.clone(), + from: T::AddressMapper::to_address(origin), + to: T::AddressMapper::to_address(contract), amount: *amount, }); }, @@ -554,8 +554,8 @@ impl Ext for ReservingExt { )?; Pallet::::deposit_event(Event::StorageDepositTransferredAndReleased { - from: contract.clone(), - to: origin.clone(), + from: T::AddressMapper::to_address(contract), + to: T::AddressMapper::to_address(origin), amount: transferred, }); diff --git a/substrate/frame/revive/src/test_utils.rs b/substrate/frame/revive/src/test_utils.rs index 2bfe754f86cd..671efebdf4bd 100644 --- a/substrate/frame/revive/src/test_utils.rs +++ b/substrate/frame/revive/src/test_utils.rs @@ -24,12 +24,40 @@ pub mod builder; use crate::{BalanceOf, Config}; use frame_support::weights::Weight; +use sp_core::H160; pub use sp_runtime::AccountId32; +const fn ee_suffix(addr: H160) -> AccountId32 { + let mut id = [0u8; 32]; + let mut i = 0; + while i < 20 { + id[i] = addr.0[i]; + i += 1; + } + + let mut j = 20; + while j < 32 { + id[j] = 0xee; + j += 1; + } + + AccountId32::new(id) +} + pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]); +pub const ALICE_ADDR: H160 = H160([1u8; 20]); +pub const ETH_ALICE: AccountId32 = ee_suffix(ALICE_ADDR); + pub const BOB: AccountId32 = AccountId32::new([2u8; 32]); +pub const BOB_ADDR: H160 = H160([2u8; 20]); +pub const BOB_CONTRACT_ID: AccountId32 = ee_suffix(BOB_ADDR); + pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]); +pub const CHARLIE_ADDR: H160 = H160([3u8; 20]); + pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]); +pub const DJANGO_ADDR: H160 = H160([4u8; 20]); +pub const ETH_DJANGO: AccountId32 = ee_suffix(DJANGO_ADDR); pub const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); diff --git a/substrate/frame/revive/src/test_utils/builder.rs b/substrate/frame/revive/src/test_utils/builder.rs index bf8cbcd5a01f..76b4c98d4cb0 100644 --- a/substrate/frame/revive/src/test_utils/builder.rs +++ b/substrate/frame/revive/src/test_utils/builder.rs @@ -17,7 +17,7 @@ use super::{deposit_limit, GAS_LIMIT}; use crate::{ - AccountIdLookupOf, AccountIdOf, BalanceOf, Code, CodeHash, CollectEvents, Config, + address::AddressMapper, AccountIdOf, BalanceOf, Code, CollectEvents, Config, ContractExecResult, ContractInstantiateResult, DebugInfo, EventRecordOf, ExecReturnValue, InstantiateReturnValue, OriginFor, Pallet, Weight, }; @@ -26,6 +26,7 @@ use core::fmt::Debug; use frame_support::pallet_prelude::DispatchResultWithPostInfo; use paste::paste; use scale_info::TypeInfo; +use sp_core::H160; /// Helper macro to generate a builder for contract API calls. macro_rules! builder { @@ -74,6 +75,11 @@ macro_rules! builder { } } +pub struct Contract { + pub account_id: AccountIdOf, + pub addr: H160, +} + builder!( instantiate_with_code( origin: OriginFor, @@ -82,7 +88,7 @@ builder!( storage_deposit_limit: BalanceOf, code: Vec, data: Vec, - salt: Vec, + salt: [u8; 32], ) -> DispatchResultWithPostInfo; /// Create an [`InstantiateWithCodeBuilder`] with default values. @@ -94,7 +100,7 @@ builder!( storage_deposit_limit: deposit_limit::(), code, data: vec![], - salt: vec![], + salt: [0; 32], } } ); @@ -105,13 +111,13 @@ builder!( value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, - code_hash: CodeHash, + code_hash: sp_core::H256, data: Vec, - salt: Vec, + salt: [u8; 32], ) -> DispatchResultWithPostInfo; /// Create an [`InstantiateBuilder`] with default values. - pub fn instantiate(origin: OriginFor, code_hash: CodeHash) -> Self { + pub fn instantiate(origin: OriginFor, code_hash: sp_core::H256) -> Self { Self { origin, value: 0u32.into(), @@ -119,7 +125,7 @@ builder!( storage_deposit_limit: deposit_limit::(), code_hash, data: vec![], - salt: vec![], + salt: [0; 32], } } ); @@ -130,24 +136,27 @@ builder!( value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, - code: Code>, + code: Code, data: Vec, - salt: Vec, + salt: [u8; 32], debug: DebugInfo, collect_events: CollectEvents, - ) -> ContractInstantiateResult, BalanceOf, EventRecordOf>; + ) -> ContractInstantiateResult, EventRecordOf>; /// Build the instantiate call and unwrap the result. - pub fn build_and_unwrap_result(self) -> InstantiateReturnValue> { + pub fn build_and_unwrap_result(self) -> InstantiateReturnValue { self.build().result.unwrap() } /// Build the instantiate call and unwrap the account id. - pub fn build_and_unwrap_account_id(self) -> AccountIdOf { - self.build().result.unwrap().account_id + pub fn build_and_unwrap_contract(self) -> Contract { + let addr = self.build().result.unwrap().addr; + let account_id = T::AddressMapper::to_account_id(&addr); + Contract{ account_id, addr } } - pub fn bare_instantiate(origin: OriginFor, code: Code>) -> Self { + /// Create a [`BareInstantiateBuilder`] with default values. + pub fn bare_instantiate(origin: OriginFor, code: Code) -> Self { Self { origin, value: 0u32.into(), @@ -155,7 +164,7 @@ builder!( storage_deposit_limit: deposit_limit::(), code, data: vec![], - salt: vec![], + salt: [0; 32], debug: DebugInfo::UnsafeDebug, collect_events: CollectEvents::Skip, } @@ -165,7 +174,7 @@ builder!( builder!( call( origin: OriginFor, - dest: AccountIdLookupOf, + dest: H160, value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, @@ -173,7 +182,7 @@ builder!( ) -> DispatchResultWithPostInfo; /// Create a [`CallBuilder`] with default values. - pub fn call(origin: OriginFor, dest: AccountIdLookupOf) -> Self { + pub fn call(origin: OriginFor, dest: H160) -> Self { CallBuilder { origin, dest, @@ -188,7 +197,7 @@ builder!( builder!( bare_call( origin: OriginFor, - dest: AccountIdOf, + dest: H160, value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, @@ -203,7 +212,7 @@ builder!( } /// Create a [`BareCallBuilder`] with default values. - pub fn bare_call(origin: OriginFor, dest: AccountIdOf) -> Self { + pub fn bare_call(origin: OriginFor, dest: H160) -> Self { Self { origin, dest, diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 52ee7b310542..a37e9842a2cb 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -26,6 +26,7 @@ use self::{ }; use crate::{ self as pallet_revive, + address::AddressMapper, chain_extension::{ ChainExtension, Environment, Ext, RegisteredChainExtension, Result as ExtensionResult, RetVal, ReturnFlags, @@ -39,10 +40,12 @@ use crate::{ tests::test_utils::{get_contract, get_contract_checked}, wasm::Memory, weights::WeightInfo, - BalanceOf, Code, CodeHash, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf, - DebugInfo, DefaultAddressGenerator, DeletionQueueCounter, Error, HoldReason, - MigrationInProgress, Origin, Pallet, PristineCode, + BalanceOf, Code, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf, DebugInfo, + DefaultAddressMapper, DeletionQueueCounter, Error, HoldReason, MigrationInProgress, Origin, + Pallet, PristineCode, H160, }; + +use crate::test_utils::builder::Contract; use assert_matches::assert_matches; use codec::{Decode, Encode}; use frame_support::{ @@ -62,12 +65,11 @@ use frame_support::{ use frame_system::{EventRecord, Phase}; use pallet_revive_fixtures::{bench::dummy_unique, compile_module}; use pallet_revive_uapi::ReturnErrorCode as RuntimeReturnCode; -use sp_core::ByteArray; use sp_io::hashing::blake2_256; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::{ testing::H256, - traits::{BlakeTwo256, Convert, Hash, IdentityLookup}, + traits::{BlakeTwo256, Convert, IdentityLookup}, AccountId32, BuildStorage, DispatchError, Perbill, TokenError, }; @@ -102,15 +104,17 @@ macro_rules! assert_refcount { pub mod test_utils { use super::{Contracts, DepositPerByte, DepositPerItem, Test}; use crate::{ - exec::AccountIdOf, BalanceOf, CodeHash, CodeInfo, CodeInfoOf, Config, ContractInfo, - ContractInfoOf, PristineCode, + address::AddressMapper, exec::AccountIdOf, BalanceOf, CodeInfo, CodeInfoOf, Config, + ContractInfo, ContractInfoOf, PristineCode, }; use codec::{Encode, MaxEncodedLen}; use frame_support::traits::fungible::{InspectHold, Mutate}; + use sp_core::H160; - pub fn place_contract(address: &AccountIdOf, code_hash: CodeHash) { + pub fn place_contract(address: &AccountIdOf, code_hash: sp_core::H256) { set_balance(address, Contracts::min_balance() * 10); >::insert(code_hash, CodeInfo::new(address.clone())); + let address = ::AddressMapper::to_address(&address); let contract = >::new(&address, 0, code_hash).unwrap(); >::insert(address, contract); } @@ -126,18 +130,16 @@ pub mod test_utils { ) -> u64 { ::Currency::balance_on_hold(reason.into(), who) } - pub fn get_contract(addr: &AccountIdOf) -> ContractInfo { + pub fn get_contract(addr: &H160) -> ContractInfo { get_contract_checked(addr).unwrap() } - pub fn get_contract_checked(addr: &AccountIdOf) -> Option> { + pub fn get_contract_checked(addr: &H160) -> Option> { ContractInfoOf::::get(addr) } - pub fn get_code_deposit(code_hash: &CodeHash) -> BalanceOf { + pub fn get_code_deposit(code_hash: &sp_core::H256) -> BalanceOf { crate::CodeInfoOf::::get(code_hash).unwrap().deposit() } - pub fn contract_info_storage_deposit( - addr: &::AccountId, - ) -> BalanceOf { + pub fn contract_info_storage_deposit(addr: &H160) -> BalanceOf { let contract_info = self::get_contract(&addr); let info_size = contract_info.encoded_size() as u64; DepositPerByte::get() @@ -152,7 +154,7 @@ pub mod test_utils { DepositPerByte::get().saturating_mul(code_len as u64 + code_info_len) + DepositPerItem::get().saturating_mul(2) } - pub fn ensure_stored(code_hash: CodeHash) -> usize { + pub fn ensure_stored(code_hash: sp_core::H256) -> usize { // Assert that code_info is stored assert!(CodeInfoOf::::contains_key(&code_hash)); // Assert that contract code is stored, and get its size. @@ -163,16 +165,17 @@ pub mod test_utils { mod builder { use super::Test; use crate::{ - test_utils::{builder::*, AccountId32, ALICE}, + test_utils::{builder::*, ALICE}, tests::RuntimeOrigin, - AccountIdLookupOf, Code, CodeHash, + Code, }; + use sp_core::{H160, H256}; - pub fn bare_instantiate(code: Code>) -> BareInstantiateBuilder { + pub fn bare_instantiate(code: Code) -> BareInstantiateBuilder { BareInstantiateBuilder::::bare_instantiate(RuntimeOrigin::signed(ALICE), code) } - pub fn bare_call(dest: AccountId32) -> BareCallBuilder { + pub fn bare_call(dest: H160) -> BareCallBuilder { BareCallBuilder::::bare_call(RuntimeOrigin::signed(ALICE), dest) } @@ -183,11 +186,11 @@ mod builder { ) } - pub fn instantiate(code_hash: CodeHash) -> InstantiateBuilder { + pub fn instantiate(code_hash: H256) -> InstantiateBuilder { InstantiateBuilder::::instantiate(RuntimeOrigin::signed(ALICE), code_hash) } - pub fn call(dest: AccountIdLookupOf) -> CallBuilder { + pub fn call(dest: H160) -> CallBuilder { CallBuilder::::call(RuntimeOrigin::signed(ALICE), dest) } } @@ -483,7 +486,7 @@ impl Config for Test { (TestExtension, DisabledExtension, RevertingExtension, TempStorageExtension); type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; - type AddressGenerator = DefaultAddressGenerator; + type AddressMapper = DefaultAddressMapper; type UnsafeUnstableInterface = UnstableInterface; type UploadOrigin = EnsureAccount; type InstantiateOrigin = EnsureAccount; @@ -495,7 +498,7 @@ impl Config for Test { pub struct ExtBuilder { existential_deposit: u64, storage_version: Option, - code_hashes: Vec>, + code_hashes: Vec, } impl Default for ExtBuilder { @@ -513,7 +516,7 @@ impl ExtBuilder { self.existential_deposit = existential_deposit; self } - pub fn with_code_hashes(mut self, code_hashes: Vec>) -> Self { + pub fn with_code_hashes(mut self, code_hashes: Vec) -> Self { self.code_hashes = code_hashes; self } @@ -601,7 +604,7 @@ mod run_tests { let base_cost = <::WeightInfo as WeightInfo>::call(); assert_eq!( - builder::call(BOB).build(), + builder::call(BOB_ADDR).build(), Err(DispatchErrorWithPostInfo { error: Error::::ContractNotFound.into(), post_info: PostDispatchInfo { @@ -636,7 +639,7 @@ mod run_tests { #[test] fn migration_in_progress_works() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(1).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -655,11 +658,11 @@ mod run_tests { Error::::MigrationInProgress, ); assert_err!( - Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB.clone(), code_hash), + Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB_ADDR, code_hash), Error::::MigrationInProgress, ); assert_err_ignore_postinfo!( - builder::call(BOB).build(), + builder::call(BOB_ADDR).build(), Error::::MigrationInProgress ); assert_err_ignore_postinfo!( @@ -675,7 +678,7 @@ mod run_tests { #[test] fn instantiate_and_call_and_deposit_event() { - let (wasm, code_hash) = compile_module::("event_and_return_on_deploy").unwrap(); + let (wasm, code_hash) = compile_module("event_and_return_on_deploy").unwrap(); ExtBuilder::default().existential_deposit(1).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -694,9 +697,10 @@ mod run_tests { initialize_block(2); // Check at the end to get hash on error easily - let addr = builder::bare_instantiate(Code::Existing(code_hash)) - .value(value) - .build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Existing(code_hash)) + .value(value) + .build_and_unwrap_contract(); assert!(ContractInfoOf::::contains_key(&addr)); assert_eq!( @@ -705,14 +709,14 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: addr.clone() + account: account_id.clone() }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: addr.clone(), + account: account_id.clone(), free_balance: min_balance, }), topics: vec![], @@ -721,7 +725,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: account_id.clone(), amount: min_balance, }), topics: vec![], @@ -730,7 +734,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: account_id.clone(), amount: value, }), topics: vec![], @@ -738,7 +742,7 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::ContractEmitted { - contract: addr.clone(), + contract: addr, data: vec![1, 2, 3, 4] }), topics: vec![], @@ -746,8 +750,8 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Instantiated { - deployer: ALICE, - contract: addr.clone() + deployer: ALICE_ADDR, + contract: addr }), topics: vec![], }, @@ -755,8 +759,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE, - to: addr.clone(), + from: ALICE_ADDR, + to: addr, amount: test_utils::contract_info_storage_deposit(&addr), } ), @@ -769,17 +773,17 @@ mod run_tests { #[test] fn deposit_event_max_value_limit() { - let (wasm, _code_hash) = compile_module::("event_size").unwrap(); + let (wasm, _code_hash) = compile_module("event_size").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(30_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Call contract with allowed storage value. - assert_ok!(builder::call(addr.clone()) + assert_ok!(builder::call(addr) .gas_limit(GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2)) // we are copying a huge buffer, .data(limits::PAYLOAD_BYTES.encode()) .build()); @@ -795,14 +799,14 @@ mod run_tests { // Fail out of fuel (ref_time weight) in the engine. #[test] fn run_out_of_fuel_engine() { - let (wasm, _code_hash) = compile_module::("run_out_of_gas").unwrap(); + let (wasm, _code_hash) = compile_module("run_out_of_gas").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(100 * min_balance) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Call the contract with a fixed gas limit. It must run out of gas because it just // loops forever. @@ -818,19 +822,19 @@ mod run_tests { // Fail out of fuel (ref_time weight) in the host. #[test] fn run_out_of_fuel_host() { - let (code, _hash) = compile_module::("chain_extension").unwrap(); + let (code, _hash) = compile_module("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let gas_limit = Weight::from_parts(u32::MAX as u64, GAS_LIMIT.proof_size()); // Use chain extension to charge more ref_time than it is available. - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(addr) .gas_limit(gas_limit) .data( ExtensionInput { extension_id: 0, func_id: 2, extra: &u32::MAX.encode() } @@ -844,16 +848,17 @@ mod run_tests { #[test] fn gas_syncs_work() { - let (code, _code_hash) = compile_module::("caller_is_origin_n").unwrap(); + let (code, _code_hash) = compile_module("caller_is_origin_n").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_account_id(); + let contract = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); - let result = builder::bare_call(addr.clone()).data(0u32.encode()).build(); + let result = builder::bare_call(contract.addr).data(0u32.encode()).build(); assert_ok!(result.result); let engine_consumed_noop = result.gas_consumed.ref_time(); - let result = builder::bare_call(addr.clone()).data(1u32.encode()).build(); + let result = builder::bare_call(contract.addr).data(1u32.encode()).build(); assert_ok!(result.result); let gas_consumed_once = result.gas_consumed.ref_time(); let host_consumed_once = @@ -861,7 +866,7 @@ mod run_tests { let engine_consumed_once = gas_consumed_once - host_consumed_once - engine_consumed_noop; - let result = builder::bare_call(addr).data(2u32.encode()).build(); + let result = builder::bare_call(contract.addr).data(2u32.encode()).build(); assert_ok!(result.result); let gas_consumed_twice = result.gas_consumed.ref_time(); let host_consumed_twice = host_consumed_once * 2; @@ -878,7 +883,7 @@ mod run_tests { /// Check the `Nonce` storage item for more information. #[test] fn instantiate_unique_trie_id() { - let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); + let (wasm, code_hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(500).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -886,8 +891,8 @@ mod run_tests { .unwrap(); // Instantiate the contract and store its trie id for later comparison. - let addr = - builder::bare_instantiate(Code::Existing(code_hash)).build_and_unwrap_account_id(); + let Contract { addr, .. } = + builder::bare_instantiate(Code::Existing(code_hash)).build_and_unwrap_contract(); let trie_id = get_contract(&addr).trie_id; // Try to instantiate it again without termination should yield an error. @@ -897,7 +902,7 @@ mod run_tests { ); // Terminate the contract. - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(addr).build()); // Re-Instantiate after termination. assert_ok!(builder::instantiate(code_hash).build()); @@ -909,14 +914,14 @@ mod run_tests { #[test] fn storage_work() { - let (code, _code_hash) = compile_module::("storage").unwrap(); + let (code, _code_hash) = compile_module("storage").unwrap(); ExtBuilder::default().build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); - let addr = builder::bare_instantiate(Code::Upload(code)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); builder::bare_call(addr).build_and_unwrap_result(); }); @@ -924,18 +929,18 @@ mod run_tests { #[test] fn storage_max_value_limit() { - let (wasm, _code_hash) = compile_module::("storage_size").unwrap(); + let (wasm, _code_hash) = compile_module("storage_size").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(30_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); get_contract(&addr); // Call contract with allowed storage value. - assert_ok!(builder::call(addr.clone()) + assert_ok!(builder::call(addr) .gas_limit(GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2)) // we are copying a huge buffer .data(limits::PAYLOAD_BYTES.encode()) .build()); @@ -950,14 +955,14 @@ mod run_tests { #[test] fn transient_storage_work() { - let (code, _code_hash) = compile_module::("transient_storage").unwrap(); + let (code, _code_hash) = compile_module("transient_storage").unwrap(); ExtBuilder::default().build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); - let addr = builder::bare_instantiate(Code::Upload(code)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); builder::bare_call(addr).build_and_unwrap_result(); }); @@ -966,28 +971,27 @@ mod run_tests { #[test] fn transient_storage_limit_in_call() { let (wasm_caller, _code_hash_caller) = - compile_module::("create_transient_storage_and_call").unwrap(); - let (wasm_callee, _code_hash_callee) = - compile_module::("set_transient_storage").unwrap(); + compile_module("create_transient_storage_and_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("set_transient_storage").unwrap(); ExtBuilder::default().build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create both contracts: Constructors do nothing. - let addr_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); // Call contracts with storage values within the limit. // Caller and Callee contracts each set a transient storage value of size 100. - assert_ok!(builder::call(addr_caller.clone()) + assert_ok!(builder::call(addr_caller) .data((100u32, 100u32, &addr_callee).encode()) .build(),); // Call a contract with a storage value that is too large. // Limit exceeded in the caller contract. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .data((4u32 * 1024u32, 200u32, &addr_callee).encode()) .build(), >::OutOfTransientStorage, @@ -1006,17 +1010,27 @@ mod run_tests { #[test] fn deploy_and_call_other_contract() { - let (caller_wasm, _caller_code_hash) = compile_module::("caller_contract").unwrap(); - let (callee_wasm, callee_code_hash) = compile_module::("return_with_data").unwrap(); + let (caller_wasm, _caller_code_hash) = compile_module("caller_contract").unwrap(); + let (callee_wasm, callee_code_hash) = compile_module("return_with_data").unwrap(); ExtBuilder::default().existential_deposit(1).build().execute_with(|| { let min_balance = Contracts::min_balance(); // Create let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let caller_addr = builder::bare_instantiate(Code::Upload(caller_wasm)) - .value(100_000) - .build_and_unwrap_account_id(); + let Contract { addr: caller_addr, account_id: caller_account } = + builder::bare_instantiate(Code::Upload(caller_wasm)) + .value(100_000) + .build_and_unwrap_contract(); + + let callee_addr = crate::address::create2( + &caller_addr, + &callee_wasm, + &[0, 1, 34, 51, 68, 85, 102, 119], // hard coded in wasm + &[0u8; 32], + ); + let callee_account = ::AddressMapper::to_account_id(&callee_addr); + Contracts::upload_code( RuntimeOrigin::signed(ALICE), callee_wasm, @@ -1024,21 +1038,12 @@ mod run_tests { ) .unwrap(); - let callee_addr = Contracts::contract_address( - &caller_addr, - &callee_code_hash, - &[0, 1, 34, 51, 68, 85, 102, 119], // hard coded in wasm - &[], - ); - // Drop previous events initialize_block(2); // Call BOB contract, which attempts to instantiate and call the callee contract and // makes various assertions on the results from those calls. - assert_ok!(builder::call(caller_addr.clone()) - .data(callee_code_hash.as_ref().to_vec()) - .build()); + assert_ok!(builder::call(caller_addr).data(callee_code_hash.as_ref().to_vec()).build()); assert_eq!( System::events(), @@ -1046,14 +1051,14 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: callee_addr.clone() + account: callee_account.clone() }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: callee_addr.clone(), + account: callee_account.clone(), free_balance: min_balance, }), topics: vec![], @@ -1062,7 +1067,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: callee_addr.clone(), + to: callee_account.clone(), amount: min_balance, }), topics: vec![], @@ -1070,8 +1075,8 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { - from: caller_addr.clone(), - to: callee_addr.clone(), + from: caller_account.clone(), + to: callee_account.clone(), amount: 32768 // hardcoded in wasm }), topics: vec![], @@ -1079,16 +1084,16 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Instantiated { - deployer: caller_addr.clone(), - contract: callee_addr.clone(), + deployer: caller_addr, + contract: callee_addr, }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { - from: caller_addr.clone(), - to: callee_addr.clone(), + from: caller_account.clone(), + to: callee_account.clone(), amount: 32768, }), topics: vec![], @@ -1096,8 +1101,8 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { - caller: Origin::from_account_id(caller_addr.clone()), - contract: callee_addr.clone(), + caller: Origin::from_account_id(caller_account.clone()), + contract: callee_addr, }), topics: vec![], }, @@ -1105,7 +1110,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: caller_addr.clone(), + contract: caller_addr, }), topics: vec![], }, @@ -1113,8 +1118,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE, - to: callee_addr.clone(), + from: ALICE_ADDR, + to: callee_addr, amount: test_utils::contract_info_storage_deposit(&callee_addr), } ), @@ -1127,20 +1132,21 @@ mod run_tests { #[test] fn delegate_call() { - let (caller_wasm, _caller_code_hash) = compile_module::("delegate_call").unwrap(); - let (callee_wasm, callee_code_hash) = compile_module::("delegate_call_lib").unwrap(); + let (caller_wasm, _caller_code_hash) = compile_module("delegate_call").unwrap(); + let (callee_wasm, callee_code_hash) = compile_module("delegate_call_lib").unwrap(); ExtBuilder::default().existential_deposit(500).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the 'caller' - let caller_addr = builder::bare_instantiate(Code::Upload(caller_wasm)) - .value(300_000) - .build_and_unwrap_account_id(); + let Contract { addr: caller_addr, .. } = + builder::bare_instantiate(Code::Upload(caller_wasm)) + .value(300_000) + .build_and_unwrap_contract(); // Only upload 'callee' code assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), callee_wasm, 100_000,)); - assert_ok!(builder::call(caller_addr.clone()) + assert_ok!(builder::call(caller_addr) .value(1337) .data(callee_code_hash.as_ref().to_vec()) .build()); @@ -1149,29 +1155,33 @@ mod run_tests { #[test] fn transfer_expendable_cannot_kill_account() { - let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + let (wasm, _code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(1_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Check that the BOB contract has been instantiated. get_contract(&addr); - let total_balance = ::Currency::total_balance(&addr); + let account = ::AddressMapper::to_account_id(&addr); + let total_balance = ::Currency::total_balance(&account); assert_eq!( - test_utils::get_balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr), + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &account + ), test_utils::contract_info_storage_deposit(&addr) ); // Some ot the total balance is held, so it can't be transferred. assert_err!( <::Currency as Mutate>::transfer( - &addr, + &account, &ALICE, total_balance, Preservation::Expendable, @@ -1179,33 +1189,34 @@ mod run_tests { TokenError::FundsUnavailable, ); - assert_eq!(::Currency::total_balance(&addr), total_balance); + assert_eq!(::Currency::total_balance(&account), total_balance); }); } #[test] fn cannot_self_destruct_through_draining() { - let (wasm, _code_hash) = compile_module::("drain").unwrap(); + let (wasm, _code_hash) = compile_module("drain").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let value = 1_000; let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(value) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); + let account = ::AddressMapper::to_account_id(&addr); // Check that the BOB contract has been instantiated. get_contract(&addr); // Call BOB which makes it send all funds to the zero address // The contract code asserts that the transfer fails with the correct error code - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(addr).build()); // Make sure the account wasn't remove by sending all free balance away. assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account), value + test_utils::contract_info_storage_deposit(&addr) + min_balance, ); }); @@ -1213,54 +1224,54 @@ mod run_tests { #[test] fn cannot_self_destruct_through_storage_refund_after_price_change() { - let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + let (wasm, _code_hash) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); - - let info_deposit = test_utils::contract_info_storage_deposit(&addr); + let contract = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); + let info_deposit = test_utils::contract_info_storage_deposit(&contract.addr); // Check that the contract has been instantiated and has the minimum balance - assert_eq!(get_contract(&addr).total_deposit(), info_deposit); - assert_eq!(get_contract(&addr).extra_deposit(), 0); + assert_eq!(get_contract(&contract.addr).total_deposit(), info_deposit); + assert_eq!(get_contract(&contract.addr).extra_deposit(), 0); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&contract.account_id), info_deposit + min_balance ); // Create 100 bytes of storage with a price of per byte and a single storage item of // price 2 - assert_ok!(builder::call(addr.clone()).data(100u32.to_le_bytes().to_vec()).build()); - assert_eq!(get_contract(&addr).total_deposit(), info_deposit + 102); + assert_ok!(builder::call(contract.addr).data(100u32.to_le_bytes().to_vec()).build()); + assert_eq!(get_contract(&contract.addr).total_deposit(), info_deposit + 102); // Increase the byte price and trigger a refund. This should not have any influence // because the removal is pro rata and exactly those 100 bytes should have been // removed. DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = 500); - assert_ok!(builder::call(addr.clone()).data(0u32.to_le_bytes().to_vec()).build()); + assert_ok!(builder::call(contract.addr).data(0u32.to_le_bytes().to_vec()).build()); // Make sure the account wasn't removed by the refund assert_eq!( - ::Currency::total_balance(&addr), - get_contract(&addr).total_deposit() + min_balance, + ::Currency::total_balance(&contract.account_id), + get_contract(&contract.addr).total_deposit() + min_balance, ); - assert_eq!(get_contract(&addr).extra_deposit(), 2); + assert_eq!(get_contract(&contract.addr).extra_deposit(), 2); }); } #[test] fn cannot_self_destruct_while_live() { - let (wasm, _code_hash) = compile_module::("self_destruct").unwrap(); + let (wasm, _code_hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(100_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Check that the BOB contract has been instantiated. get_contract(&addr); @@ -1268,7 +1279,7 @@ mod run_tests { // Call BOB with input data, forcing it make a recursive call to itself to // self-destruct, resulting in a trap. assert_err_ignore_postinfo!( - builder::call(addr.clone()).data(vec![0]).build(), + builder::call(addr).data(vec![0]).build(), Error::::ContractTrapped, ); @@ -1279,38 +1290,38 @@ mod run_tests { #[test] fn self_destruct_works() { - let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); + let (wasm, code_hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(1_000).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let _ = ::Currency::set_balance(&DJANGO, 1_000_000); + let _ = ::Currency::set_balance(Ð_DJANGO, 1_000_000); let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let contract = builder::bare_instantiate(Code::Upload(wasm)) .value(100_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Check that the BOB contract has been instantiated. - let _ = get_contract(&addr); + let _ = get_contract(&contract.addr); - let info_deposit = test_utils::contract_info_storage_deposit(&addr); + let info_deposit = test_utils::contract_info_storage_deposit(&contract.addr); // Drop all previous events initialize_block(2); // Call BOB without input data which triggers termination. - assert_matches!(builder::call(addr.clone()).build(), Ok(_)); + assert_matches!(builder::call(contract.addr).build(), Ok(_)); // Check that code is still there but refcount dropped to zero. assert_refcount!(&code_hash, 0); // Check that account is gone - assert!(get_contract_checked(&addr).is_none()); - assert_eq!(::Currency::total_balance(&addr), 0); + assert!(get_contract_checked(&contract.addr).is_none()); + assert_eq!(::Currency::total_balance(&contract.account_id), 0); // Check that the beneficiary (django) got remaining balance. assert_eq!( - ::Currency::free_balance(DJANGO), + ::Currency::free_balance(ETH_DJANGO), 1_000_000 + 100_000 + min_balance ); @@ -1327,8 +1338,8 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Terminated { - contract: addr.clone(), - beneficiary: DJANGO + contract: contract.addr, + beneficiary: DJANGO_ADDR, }), topics: vec![], }, @@ -1336,7 +1347,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: addr.clone(), + contract: contract.addr, }), topics: vec![], }, @@ -1344,8 +1355,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndReleased { - from: addr.clone(), - to: ALICE, + from: contract.addr, + to: ALICE_ADDR, amount: info_deposit, } ), @@ -1354,15 +1365,15 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::KilledAccount { - account: addr.clone() + account: contract.account_id.clone() }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { - from: addr.clone(), - to: DJANGO, + from: contract.account_id.clone(), + to: ETH_DJANGO, amount: 100_000 + min_balance, }), topics: vec![], @@ -1376,30 +1387,30 @@ mod run_tests { // additional funds after it has been drained. #[test] fn destroy_contract_and_transfer_funds() { - let (callee_wasm, callee_code_hash) = compile_module::("self_destruct").unwrap(); - let (caller_wasm, _caller_code_hash) = - compile_module::("destroy_and_transfer").unwrap(); + let (callee_wasm, callee_code_hash) = compile_module("self_destruct").unwrap(); + let (caller_wasm, _caller_code_hash) = compile_module("destroy_and_transfer").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create code hash for bob to instantiate let _ = ::Currency::set_balance(&ALICE, 1_000_000); Contracts::upload_code( RuntimeOrigin::signed(ALICE), - callee_wasm, + callee_wasm.clone(), deposit_limit::(), ) .unwrap(); // This deploys the BOB contract, which in turn deploys the CHARLIE contract during // construction. - let addr_bob = builder::bare_instantiate(Code::Upload(caller_wasm)) - .value(200_000) - .data(callee_code_hash.as_ref().to_vec()) - .build_and_unwrap_account_id(); + let Contract { addr: addr_bob, .. } = + builder::bare_instantiate(Code::Upload(caller_wasm)) + .value(200_000) + .data(callee_code_hash.as_ref().to_vec()) + .build_and_unwrap_contract(); // Check that the CHARLIE contract has been instantiated. - let addr_charlie = - Contracts::contract_address(&addr_bob, &callee_code_hash, &[], &[0x47, 0x11]); + let salt = [47; 32]; // hard coded in fixture. + let addr_charlie = crate::address::create2(&addr_bob, &callee_wasm, &[], &salt); get_contract(&addr_charlie); // Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct. @@ -1412,7 +1423,7 @@ mod run_tests { #[test] fn cannot_self_destruct_in_constructor() { - let (wasm, _) = compile_module::("self_destructing_constructor").unwrap(); + let (wasm, _) = compile_module("self_destructing_constructor").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -1426,15 +1437,15 @@ mod run_tests { #[test] fn crypto_hashes() { - let (wasm, _code_hash) = compile_module::("crypto_hashes").unwrap(); + let (wasm, _code_hash) = compile_module("crypto_hashes").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the CRYPTO_HASHES contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(100_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Perform the call. let input = b"_DEAD_BEEF"; use sp_io::hashing::*; @@ -1456,8 +1467,7 @@ mod run_tests { // We offset data in the contract tables by 1. let mut params = vec![(n + 1) as u8]; params.extend_from_slice(input); - let result = - builder::bare_call(addr.clone()).data(params).build_and_unwrap_result(); + let result = builder::bare_call(addr).data(params).build_and_unwrap_result(); assert!(!result.did_revert()); let expected = hash_fn(input.as_ref()); assert_eq!(&result.data[..*expected_size], &*expected); @@ -1467,54 +1477,54 @@ mod run_tests { #[test] fn transfer_return_code() { - let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let contract = builder::bare_instantiate(Code::Upload(wasm)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Contract has only the minimal balance so any transfer will fail. - ::Currency::set_balance(&addr, min_balance); - let result = builder::bare_call(addr.clone()).build_and_unwrap_result(); + ::Currency::set_balance(&contract.account_id, min_balance); + let result = builder::bare_call(contract.addr).build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); }); } #[test] fn call_return_code() { - let (caller_code, _caller_hash) = compile_module::("call_return_code").unwrap(); - let (callee_code, _callee_hash) = compile_module::("ok_trap_revert").unwrap(); + let (caller_code, _caller_hash) = compile_module("call_return_code").unwrap(); + let (callee_code, _callee_hash) = compile_module("ok_trap_revert").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); - let addr_bob = builder::bare_instantiate(Code::Upload(caller_code)) + let bob = builder::bare_instantiate(Code::Upload(caller_code)) .value(min_balance * 100) .data(vec![0]) - .build_and_unwrap_account_id(); - ::Currency::set_balance(&addr_bob, min_balance); + .build_and_unwrap_contract(); + ::Currency::set_balance(&bob.account_id, min_balance); // Contract calls into Django which is no valid contract - let result = builder::bare_call(addr_bob.clone()) + let result = builder::bare_call(bob.addr) .data(AsRef::<[u8]>::as_ref(&DJANGO).to_vec()) .build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::NotCallable); - let addr_django = builder::bare_instantiate(Code::Upload(callee_code)) + let django = builder::bare_instantiate(Code::Upload(callee_code)) .origin(RuntimeOrigin::signed(CHARLIE)) .value(min_balance * 100) .data(vec![0]) - .build_and_unwrap_account_id(); - ::Currency::set_balance(&addr_django, min_balance); + .build_and_unwrap_contract(); + ::Currency::set_balance(&django.account_id, min_balance); // Contract has only the minimal balance so any transfer will fail. - let result = builder::bare_call(addr_bob.clone()) + let result = builder::bare_call(bob.addr) .data( - AsRef::<[u8]>::as_ref(&addr_django) + AsRef::<[u8]>::as_ref(&django.addr) .iter() .chain(&0u32.to_le_bytes()) .cloned() @@ -1524,10 +1534,10 @@ mod run_tests { assert_return_code!(result, RuntimeReturnCode::TransferFailed); // Contract has enough balance but callee reverts because "1" is passed. - ::Currency::set_balance(&addr_bob, min_balance + 1000); - let result = builder::bare_call(addr_bob.clone()) + ::Currency::set_balance(&bob.account_id, min_balance + 1000); + let result = builder::bare_call(bob.addr) .data( - AsRef::<[u8]>::as_ref(&addr_django) + AsRef::<[u8]>::as_ref(&django.addr) .iter() .chain(&1u32.to_le_bytes()) .cloned() @@ -1537,9 +1547,9 @@ mod run_tests { assert_return_code!(result, RuntimeReturnCode::CalleeReverted); // Contract has enough balance but callee traps because "2" is passed. - let result = builder::bare_call(addr_bob) + let result = builder::bare_call(bob.addr) .data( - AsRef::<[u8]>::as_ref(&addr_django) + AsRef::<[u8]>::as_ref(&django.addr) .iter() .chain(&2u32.to_le_bytes()) .cloned() @@ -1552,9 +1562,8 @@ mod run_tests { #[test] fn instantiate_return_code() { - let (caller_code, _caller_hash) = - compile_module::("instantiate_return_code").unwrap(); - let (callee_code, callee_hash) = compile_module::("ok_trap_revert").unwrap(); + let (caller_code, _caller_hash) = compile_module("instantiate_return_code").unwrap(); + let (callee_code, callee_hash) = compile_module("ok_trap_revert").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); @@ -1565,31 +1574,31 @@ mod run_tests { .value(min_balance * 100) .build()); - let addr = builder::bare_instantiate(Code::Upload(caller_code)) + let contract = builder::bare_instantiate(Code::Upload(caller_code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Contract has only the minimal balance so any transfer will fail. - ::Currency::set_balance(&addr, min_balance); - let result = builder::bare_call(addr.clone()) + ::Currency::set_balance(&contract.account_id, min_balance); + let result = builder::bare_call(contract.addr) .data(callee_hash.clone()) .build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); // Contract has enough balance but the passed code hash is invalid - ::Currency::set_balance(&addr, min_balance + 10_000); + ::Currency::set_balance(&contract.account_id, min_balance + 10_000); let result = - builder::bare_call(addr.clone()).data(vec![0; 33]).build_and_unwrap_result(); + builder::bare_call(contract.addr).data(vec![0; 33]).build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::CodeNotFound); // Contract has enough balance but callee reverts because "1" is passed. - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(contract.addr) .data(callee_hash.iter().chain(&1u32.to_le_bytes()).cloned().collect()) .build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::CalleeReverted); // Contract has enough balance but callee traps because "2" is passed. - let result = builder::bare_call(addr) + let result = builder::bare_call(contract.addr) .data(callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect()) .build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::CalleeTrapped); @@ -1598,16 +1607,16 @@ mod run_tests { #[test] fn disabled_chain_extension_errors_on_call() { - let (code, _hash) = compile_module::("chain_extension").unwrap(); + let (code, _hash) = compile_module("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let contract = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); TestExtension::disable(); assert_err_ignore_postinfo!( - builder::call(addr.clone()).data(vec![7u8; 8]).build(), + builder::call(contract.addr).data(vec![7u8; 8]).build(), Error::::NoChainExtension, ); }); @@ -1615,46 +1624,46 @@ mod run_tests { #[test] fn chain_extension_works() { - let (code, _hash) = compile_module::("chain_extension").unwrap(); + let (code, _hash) = compile_module("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let contract = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // 0 = read input buffer and pass it through as output let input: Vec = ExtensionInput { extension_id: 0, func_id: 0, extra: &[99] }.into(); - let result = builder::bare_call(addr.clone()).data(input.clone()).build(); + let result = builder::bare_call(contract.addr).data(input.clone()).build(); assert_eq!(TestExtension::last_seen_buffer(), input); assert_eq!(result.result.unwrap().data, input); // 1 = treat inputs as integer primitives and store the supplied integers - builder::bare_call(addr.clone()) + builder::bare_call(contract.addr) .data(ExtensionInput { extension_id: 0, func_id: 1, extra: &[] }.into()) .build_and_unwrap_result(); assert_eq!(TestExtension::last_seen_input_len(), 4); // 2 = charge some extra weight (amount supplied in the fifth byte) - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(contract.addr) .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &0u32.encode() }.into()) .build(); assert_ok!(result.result); let gas_consumed = result.gas_consumed; - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(contract.addr) .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &42u32.encode() }.into()) .build(); assert_ok!(result.result); assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 42); - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(contract.addr) .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &95u32.encode() }.into()) .build(); assert_ok!(result.result); assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 95); // 3 = diverging chain extension call that sets flags to 0x1 and returns a fixed buffer - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(contract.addr) .data(ExtensionInput { extension_id: 0, func_id: 3, extra: &[] }.into()) .build_and_unwrap_result(); assert_eq!(result.flags, ReturnFlags::REVERT); @@ -1663,7 +1672,7 @@ mod run_tests { // diverging to second chain extension that sets flags to 0x1 and returns a fixed buffer // We set the MSB part to 1 (instead of 0) which routes the request into the second // extension - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(contract.addr) .data(ExtensionInput { extension_id: 1, func_id: 0, extra: &[] }.into()) .build_and_unwrap_result(); assert_eq!(result.flags, ReturnFlags::REVERT); @@ -1673,7 +1682,7 @@ mod run_tests { // We set the MSB part to 2 (instead of 0) which routes the request into the third // extension assert_err_ignore_postinfo!( - builder::call(addr.clone()) + builder::call(contract.addr) .data(ExtensionInput { extension_id: 2, func_id: 0, extra: &[] }.into()) .build(), Error::::NoChainExtension, @@ -1683,13 +1692,13 @@ mod run_tests { #[test] fn chain_extension_temp_storage_works() { - let (code, _hash) = compile_module::("chain_extension_temp_storage").unwrap(); + let (code, _hash) = compile_module("chain_extension_temp_storage").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let contract = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Call func 0 and func 1 back to back. let stop_recursion = 0u8; @@ -1701,32 +1710,32 @@ mod run_tests { .as_ref(), ); - assert_ok!(builder::bare_call(addr.clone()).data(input.clone()).build().result); + assert_ok!(builder::bare_call(contract.addr).data(input.clone()).build().result); }) } #[test] fn lazy_removal_works() { - let (code, _hash) = compile_module::("self_destruct").unwrap(); + let (code, _hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let contract = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); - let info = get_contract(&addr); + let info = get_contract(&contract.addr); let trie = &info.child_trie_info(); // Put value into the contracts child trie child::put(trie, &[99], &42); // Terminate the contract - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(contract.addr).build()); // Contract info should be gone - assert!(!>::contains_key(&addr)); + assert!(!>::contains_key(&contract.addr)); // But value should be still there as the lazy removal did not run, yet. assert_matches!(child::get(trie, &[99]), Some(42)); @@ -1741,19 +1750,19 @@ mod run_tests { #[test] fn lazy_batch_removal_works() { - let (code, _hash) = compile_module::("self_destruct").unwrap(); + let (code, _hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let mut tries: Vec = vec![]; for i in 0..3u8 { - let addr = builder::bare_instantiate(Code::Upload(code.clone())) + let contract = builder::bare_instantiate(Code::Upload(code.clone())) .value(min_balance * 100) - .salt(vec![i]) - .build_and_unwrap_account_id(); + .salt([i; 32]) + .build_and_unwrap_contract(); - let info = get_contract(&addr); + let info = get_contract(&contract.addr); let trie = &info.child_trie_info(); // Put value into the contracts child trie @@ -1761,9 +1770,9 @@ mod run_tests { // Terminate the contract. Contract info should be gone, but value should be still // there as the lazy removal did not run, yet. - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(contract.addr).build()); - assert!(!>::contains_key(&addr)); + assert!(!>::contains_key(&contract.addr)); assert_matches!(child::get(trie, &[99]), Some(42)); tries.push(trie.clone()) @@ -1781,7 +1790,7 @@ mod run_tests { #[test] fn lazy_removal_partial_remove_works() { - let (code, _hash) = compile_module::("self_destruct").unwrap(); + let (code, _hash) = compile_module("self_destruct").unwrap(); // We create a contract with some extra keys above the weight limit let extra_keys = 7u32; @@ -1797,9 +1806,9 @@ mod run_tests { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let info = get_contract(&addr); @@ -1810,7 +1819,7 @@ mod run_tests { >::insert(&addr, info.clone()); // Terminate the contract - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(addr).build()); // Contract info should be gone assert!(!>::contains_key(&addr)); @@ -1856,14 +1865,14 @@ mod run_tests { #[test] fn lazy_removal_does_no_run_on_low_remaining_weight() { - let (code, _hash) = compile_module::("self_destruct").unwrap(); + let (code, _hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let info = get_contract(&addr); let trie = &info.child_trie_info(); @@ -1872,7 +1881,7 @@ mod run_tests { child::put(trie, &[99], &42); // Terminate the contract - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(addr).build()); // Contract info should be gone assert!(!>::contains_key(&addr)); @@ -1906,7 +1915,7 @@ mod run_tests { #[test] fn lazy_removal_does_not_use_all_weight() { - let (code, _hash) = compile_module::("self_destruct").unwrap(); + let (code, _hash) = compile_module("self_destruct").unwrap(); let mut meter = WeightMeter::with_limit(Weight::from_parts(5_000_000_000, 100 * 1024)); let mut ext = ExtBuilder::default().existential_deposit(50).build(); @@ -1915,9 +1924,9 @@ mod run_tests { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let info = get_contract(&addr); let (weight_per_key, max_keys) = ContractInfo::::deletion_budget(&meter); @@ -1935,7 +1944,7 @@ mod run_tests { >::insert(&addr, info.clone()); // Terminate the contract - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(addr).build()); // Contract info should be gone assert!(!>::contains_key(&addr)); @@ -1970,7 +1979,7 @@ mod run_tests { #[test] fn deletion_queue_ring_buffer_overflow() { - let (code, _hash) = compile_module::("self_destruct").unwrap(); + let (code, _hash) = compile_module("self_destruct").unwrap(); let mut ext = ExtBuilder::default().existential_deposit(50).build(); // setup the deletion queue with custom counters @@ -1989,10 +1998,10 @@ mod run_tests { // add 3 contracts to the deletion queue for i in 0..3u8 { - let addr = builder::bare_instantiate(Code::Upload(code.clone())) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code.clone())) .value(min_balance * 100) - .salt(vec![i]) - .build_and_unwrap_account_id(); + .salt([i; 32]) + .build_and_unwrap_contract(); let info = get_contract(&addr); let trie = &info.child_trie_info(); @@ -2002,7 +2011,7 @@ mod run_tests { // Terminate the contract. Contract info should be gone, but value should be still // there as the lazy removal did not run, yet. - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(addr).build()); assert!(!>::contains_key(&addr)); assert_matches!(child::get(trie, &[99]), Some(42)); @@ -2024,27 +2033,29 @@ mod run_tests { } #[test] fn refcounter() { - let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); + let (wasm, code_hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); // Create two contracts with the same code and check that they do in fact share it. - let addr0 = builder::bare_instantiate(Code::Upload(wasm.clone())) - .value(min_balance * 100) - .salt(vec![0]) - .build_and_unwrap_account_id(); - let addr1 = builder::bare_instantiate(Code::Upload(wasm.clone())) - .value(min_balance * 100) - .salt(vec![1]) - .build_and_unwrap_account_id(); + let Contract { addr: addr0, .. } = + builder::bare_instantiate(Code::Upload(wasm.clone())) + .value(min_balance * 100) + .salt([0; 32]) + .build_and_unwrap_contract(); + let Contract { addr: addr1, .. } = + builder::bare_instantiate(Code::Upload(wasm.clone())) + .value(min_balance * 100) + .salt([1; 32]) + .build_and_unwrap_contract(); assert_refcount!(code_hash, 2); // Sharing should also work with the usual instantiate call - let addr2 = builder::bare_instantiate(Code::Existing(code_hash)) + let Contract { addr: addr2, .. } = builder::bare_instantiate(Code::Existing(code_hash)) .value(min_balance * 100) - .salt(vec![2]) - .build_and_unwrap_account_id(); + .salt([2; 32]) + .build_and_unwrap_contract(); assert_refcount!(code_hash, 3); // Terminating one contract should decrement the refcount @@ -2069,13 +2080,13 @@ mod run_tests { #[test] fn debug_message_works() { - let (wasm, _code_hash) = compile_module::("debug_message_works").unwrap(); + let (wasm, _code_hash) = compile_module("debug_message_works").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(30_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build(); assert_matches!(result.result, Ok(_)); @@ -2085,13 +2096,13 @@ mod run_tests { #[test] fn debug_message_logging_disabled() { - let (wasm, _code_hash) = compile_module::("debug_message_logging_disabled").unwrap(); + let (wasm, _code_hash) = compile_module("debug_message_logging_disabled").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(30_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // the dispatchables always run without debugging assert_ok!(Contracts::call( RuntimeOrigin::signed(ALICE), @@ -2106,13 +2117,13 @@ mod run_tests { #[test] fn debug_message_invalid_utf8() { - let (wasm, _code_hash) = compile_module::("debug_message_invalid_utf8").unwrap(); + let (wasm, _code_hash) = compile_module("debug_message_invalid_utf8").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(30_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build(); assert_ok!(result.result); assert!(result.debug_message.is_empty()); @@ -2121,24 +2132,27 @@ mod run_tests { #[test] fn gas_estimation_for_subcalls() { - let (caller_code, _caller_hash) = compile_module::("call_with_limit").unwrap(); - let (call_runtime_code, _caller_hash) = compile_module::("call_runtime").unwrap(); - let (dummy_code, _callee_hash) = compile_module::("dummy").unwrap(); + let (caller_code, _caller_hash) = compile_module("call_with_limit").unwrap(); + let (call_runtime_code, _caller_hash) = compile_module("call_runtime").unwrap(); + let (dummy_code, _callee_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 2_000 * min_balance); - let addr_caller = builder::bare_instantiate(Code::Upload(caller_code)) - .value(min_balance * 100) - .build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); - let addr_dummy = builder::bare_instantiate(Code::Upload(dummy_code)) - .value(min_balance * 100) - .build_and_unwrap_account_id(); + let Contract { addr: addr_dummy, .. } = + builder::bare_instantiate(Code::Upload(dummy_code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); - let addr_call_runtime = builder::bare_instantiate(Code::Upload(call_runtime_code)) - .value(min_balance * 100) - .build_and_unwrap_account_id(); + let Contract { addr: addr_call_runtime, .. } = + builder::bare_instantiate(Code::Upload(call_runtime_code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); // Run the test for all of those weight limits for the subcall let weights = [ @@ -2174,8 +2188,7 @@ mod run_tests { .collect(); // Call in order to determine the gas that is required for this call - let result_orig = - builder::bare_call(addr_caller.clone()).data(input.clone()).build(); + let result_orig = builder::bare_call(addr_caller).data(input.clone()).build(); assert_ok!(&result_orig.result); // If the out of gas happens in the subcall the caller contract @@ -2190,7 +2203,7 @@ mod run_tests { }; // Make the same call using the estimated gas. Should succeed. - let result = builder::bare_call(addr_caller.clone()) + let result = builder::bare_call(addr_caller) .gas_limit(result_orig.gas_required) .storage_deposit_limit(result_orig.storage_deposit.charge_or_zero()) .data(input.clone()) @@ -2198,7 +2211,7 @@ mod run_tests { assert_ok!(&result.result); // Check that it fails with too little ref_time - let result = builder::bare_call(addr_caller.clone()) + let result = builder::bare_call(addr_caller) .gas_limit(result_orig.gas_required.sub_ref_time(1)) .storage_deposit_limit(result_orig.storage_deposit.charge_or_zero()) .data(input.clone()) @@ -2206,7 +2219,7 @@ mod run_tests { assert_err!(result.result, error); // Check that it fails with too little proof_size - let result = builder::bare_call(addr_caller.clone()) + let result = builder::bare_call(addr_caller) .gas_limit(result_orig.gas_required.sub_proof_size(1)) .storage_deposit_limit(result_orig.storage_deposit.charge_or_zero()) .data(input.clone()) @@ -2219,16 +2232,17 @@ mod run_tests { #[test] fn gas_estimation_call_runtime() { - let (caller_code, _caller_hash) = compile_module::("call_runtime").unwrap(); + let (caller_code, _caller_hash) = compile_module("call_runtime").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); - let addr_caller = builder::bare_instantiate(Code::Upload(caller_code)) - .value(min_balance * 100) - .salt(vec![0]) - .build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .salt([0; 32]) + .build_and_unwrap_contract(); // Call something trivial with a huge gas limit so that we can observe the effects // of pre-charging. This should create a difference between consumed and required. @@ -2236,7 +2250,7 @@ mod run_tests { pre_charge: Weight::from_parts(10_000_000, 1_000), actual_weight: Weight::from_parts(100, 100), }); - let result = builder::bare_call(addr_caller.clone()).data(call.encode()).build(); + let result = builder::bare_call(addr_caller).data(call.encode()).build(); // contract encodes the result of the dispatch runtime let outcome = u32::decode(&mut result.result.unwrap().data.as_ref()).unwrap(); assert_eq!(outcome, 0); @@ -2255,22 +2269,24 @@ mod run_tests { #[test] fn call_runtime_reentrancy_guarded() { - let (caller_code, _caller_hash) = compile_module::("call_runtime").unwrap(); - let (callee_code, _callee_hash) = compile_module::("dummy").unwrap(); + let (caller_code, _caller_hash) = compile_module("call_runtime").unwrap(); + let (callee_code, _callee_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); - let addr_caller = builder::bare_instantiate(Code::Upload(caller_code)) - .value(min_balance * 100) - .salt(vec![0]) - .build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .salt([0; 32]) + .build_and_unwrap_contract(); - let addr_callee = builder::bare_instantiate(Code::Upload(callee_code)) - .value(min_balance * 100) - .salt(vec![1]) - .build_and_unwrap_account_id(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(callee_code)) + .value(min_balance * 100) + .salt([1; 32]) + .build_and_unwrap_contract(); // Call pallet_revive call() dispatchable let call = RuntimeCall::Contracts(crate::Call::call { @@ -2283,9 +2299,8 @@ mod run_tests { // Call runtime to re-enter back to contracts engine by // calling dummy contract - let result = builder::bare_call(addr_caller.clone()) - .data(call.encode()) - .build_and_unwrap_result(); + let result = + builder::bare_call(addr_caller).data(call.encode()).build_and_unwrap_result(); // Call to runtime should fail because of the re-entrancy guard assert_return_code!(result, RuntimeReturnCode::CallRuntimeFailed); }); @@ -2293,15 +2308,15 @@ mod run_tests { #[test] fn ecdsa_recover() { - let (wasm, _code_hash) = compile_module::("ecdsa_recover").unwrap(); + let (wasm, _code_hash) = compile_module("ecdsa_recover").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the ecdsa_recover contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(100_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); #[rustfmt::skip] let signature: [u8; 65] = [ @@ -2326,7 +2341,7 @@ mod run_tests { params.extend_from_slice(&signature); params.extend_from_slice(&message_hash); assert!(params.len() == 65 + 32); - let result = builder::bare_call(addr.clone()).data(params).build_and_unwrap_result(); + let result = builder::bare_call(addr).data(params).build_and_unwrap_result(); assert!(!result.did_revert()); assert_eq!(result.data, EXPECTED_COMPRESSED_PUBLIC_KEY); }) @@ -2334,7 +2349,7 @@ mod run_tests { #[test] fn bare_instantiate_returns_events() { - let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); @@ -2352,7 +2367,7 @@ mod run_tests { #[test] fn bare_instantiate_does_not_return_events() { - let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); @@ -2368,18 +2383,17 @@ mod run_tests { #[test] fn bare_call_returns_events() { - let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); - let result = builder::bare_call(addr.clone()) - .collect_events(CollectEvents::UnsafeCollect) - .build(); + let result = + builder::bare_call(addr).collect_events(CollectEvents::UnsafeCollect).build(); let events = result.events.unwrap(); assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success); @@ -2390,16 +2404,16 @@ mod run_tests { #[test] fn bare_call_does_not_return_events() { - let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); - let result = builder::bare_call(addr.clone()).build(); + let result = builder::bare_call(addr).build(); let events = result.events; assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success); @@ -2410,15 +2424,15 @@ mod run_tests { #[test] fn sr25519_verify() { - let (wasm, _code_hash) = compile_module::("sr25519_verify").unwrap(); + let (wasm, _code_hash) = compile_module("sr25519_verify").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the sr25519_verify contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(100_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let call_with = |message: &[u8; 11]| { // Alice's signature for "hello world" @@ -2442,7 +2456,7 @@ mod run_tests { params.extend_from_slice(&public_key); params.extend_from_slice(message); - builder::bare_call(addr.clone()).data(params).build_and_unwrap_result() + builder::bare_call(addr).data(params).build_and_unwrap_result() }; // verification should succeed for "hello world" @@ -2455,8 +2469,8 @@ mod run_tests { #[test] fn failed_deposit_charge_should_roll_back_call() { - let (wasm_caller, _) = compile_module::("call_runtime_and_call").unwrap(); - let (wasm_callee, _) = compile_module::("store_call").unwrap(); + let (wasm_caller, _) = compile_module("call_runtime_and_call").unwrap(); + let (wasm_callee, _) = compile_module("store_call").unwrap(); const ED: u64 = 200; let execute = || { @@ -2464,15 +2478,16 @@ mod run_tests { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate both contracts. - let addr_caller = builder::bare_instantiate(Code::Upload(wasm_caller.clone())) - .build_and_unwrap_account_id(); - let addr_callee = builder::bare_instantiate(Code::Upload(wasm_callee.clone())) - .build_and_unwrap_account_id(); + let caller = builder::bare_instantiate(Code::Upload(wasm_caller.clone())) + .build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee.clone())) + .build_and_unwrap_contract(); // Give caller proxy access to Alice. assert_ok!(Proxy::add_proxy( RuntimeOrigin::signed(ALICE), - addr_caller.clone(), + caller.account_id.clone(), (), 0 )); @@ -2497,7 +2512,7 @@ mod run_tests { transfer_proxy_call, ); - builder::call(addr_caller).data(data.encode()).build() + builder::call(caller.addr).data(data.encode()).build() }) }; @@ -2511,7 +2526,7 @@ mod run_tests { #[test] fn upload_code_works() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -2532,7 +2547,7 @@ mod run_tests { event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash, deposit_held: deposit_expected, - uploader: ALICE + uploader: ALICE_ADDR }), topics: vec![], },] @@ -2542,7 +2557,7 @@ mod run_tests { #[test] fn upload_code_limit_too_low() { - let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + let (wasm, _code_hash) = compile_module("dummy").unwrap(); let deposit_expected = expected_deposit(wasm.len()); let deposit_insufficient = deposit_expected.saturating_sub(1); @@ -2563,7 +2578,7 @@ mod run_tests { #[test] fn upload_code_not_enough_balance() { - let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + let (wasm, _code_hash) = compile_module("dummy").unwrap(); let deposit_expected = expected_deposit(wasm.len()); let deposit_insufficient = deposit_expected.saturating_sub(1); @@ -2584,7 +2599,7 @@ mod run_tests { #[test] fn remove_code_works() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -2605,7 +2620,7 @@ mod run_tests { event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash, deposit_held: deposit_expected, - uploader: ALICE + uploader: ALICE_ADDR }), topics: vec![], }, @@ -2614,7 +2629,7 @@ mod run_tests { event: RuntimeEvent::Contracts(crate::Event::CodeRemoved { code_hash, deposit_released: deposit_expected, - remover: ALICE + remover: ALICE_ADDR }), topics: vec![], }, @@ -2625,7 +2640,7 @@ mod run_tests { #[test] fn remove_code_wrong_origin() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -2649,7 +2664,7 @@ mod run_tests { event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash, deposit_held: deposit_expected, - uploader: ALICE + uploader: ALICE_ADDR }), topics: vec![], },] @@ -2659,7 +2674,7 @@ mod run_tests { #[test] fn remove_code_in_use() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -2680,7 +2695,7 @@ mod run_tests { #[test] fn remove_code_not_found() { - let (_wasm, code_hash) = compile_module::("dummy").unwrap(); + let (_wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -2699,7 +2714,7 @@ mod run_tests { #[test] fn instantiate_with_zero_balance_works() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); @@ -2708,15 +2723,16 @@ mod run_tests { initialize_block(2); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); // Ensure the contract was stored and get expected deposit amount to be reserved. let deposit_expected = expected_deposit(ensure_stored(code_hash)); // Make sure the account exists even though no free balance was send - assert_eq!(::Currency::free_balance(&addr), min_balance); + assert_eq!(::Currency::free_balance(&account_id), min_balance); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), min_balance + test_utils::contract_info_storage_deposit(&addr) ); @@ -2728,21 +2744,21 @@ mod run_tests { event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash, deposit_held: deposit_expected, - uploader: ALICE + uploader: ALICE_ADDR }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: addr.clone(), + account: account_id.clone(), }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: addr.clone(), + account: account_id.clone(), free_balance: min_balance, }), topics: vec![], @@ -2751,7 +2767,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: account_id, amount: min_balance, }), topics: vec![], @@ -2759,8 +2775,8 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Instantiated { - deployer: ALICE, - contract: addr.clone(), + deployer: ALICE_ADDR, + contract: addr, }), topics: vec![], }, @@ -2768,8 +2784,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE, - to: addr.clone(), + from: ALICE_ADDR, + to: addr, amount: test_utils::contract_info_storage_deposit(&addr), } ), @@ -2782,7 +2798,7 @@ mod run_tests { #[test] fn instantiate_with_below_existential_deposit_works() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); @@ -2792,16 +2808,16 @@ mod run_tests { initialize_block(2); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, account_id } = builder::bare_instantiate(Code::Upload(wasm)) .value(value) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Ensure the contract was stored and get expected deposit amount to be reserved. let deposit_expected = expected_deposit(ensure_stored(code_hash)); // Make sure the account exists even though not enough free balance was send - assert_eq!(::Currency::free_balance(&addr), min_balance + value); + assert_eq!(::Currency::free_balance(&account_id), min_balance + value); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), min_balance + value + test_utils::contract_info_storage_deposit(&addr) ); @@ -2813,21 +2829,21 @@ mod run_tests { event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash, deposit_held: deposit_expected, - uploader: ALICE + uploader: ALICE_ADDR }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: addr.clone() + account: account_id.clone() }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: addr.clone(), + account: account_id.clone(), free_balance: min_balance, }), topics: vec![], @@ -2836,7 +2852,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: account_id.clone(), amount: min_balance, }), topics: vec![], @@ -2845,7 +2861,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: account_id.clone(), amount: 50, }), topics: vec![], @@ -2853,8 +2869,8 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Instantiated { - deployer: ALICE, - contract: addr.clone(), + deployer: ALICE_ADDR, + contract: addr, }), topics: vec![], }, @@ -2862,8 +2878,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE, - to: addr.clone(), + from: ALICE_ADDR, + to: addr, amount: test_utils::contract_info_storage_deposit(&addr), } ), @@ -2876,11 +2892,12 @@ mod run_tests { #[test] fn storage_deposit_works() { - let (wasm, _code_hash) = compile_module::("multi_store").unwrap(); + let (wasm, _code_hash) = compile_module("multi_store").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); let mut deposit = test_utils::contract_info_storage_deposit(&addr); @@ -2888,20 +2905,20 @@ mod run_tests { initialize_block(2); // Create storage - assert_ok!(builder::call(addr.clone()).value(42).data((50u32, 20u32).encode()).build()); + assert_ok!(builder::call(addr).value(42).data((50u32, 20u32).encode()).build()); // 4 is for creating 2 storage items let charged0 = 4 + 50 + 20; deposit += charged0; assert_eq!(get_contract(&addr).total_deposit(), deposit); // Add more storage (but also remove some) - assert_ok!(builder::call(addr.clone()).data((100u32, 10u32).encode()).build()); + assert_ok!(builder::call(addr).data((100u32, 10u32).encode()).build()); let charged1 = 50 - 10; deposit += charged1; assert_eq!(get_contract(&addr).total_deposit(), deposit); // Remove more storage (but also add some) - assert_ok!(builder::call(addr.clone()).data((10u32, 20u32).encode()).build()); + assert_ok!(builder::call(addr).data((10u32, 20u32).encode()).build()); // -1 for numeric instability let refunded0 = 90 - 10 - 1; deposit -= refunded0; @@ -2914,7 +2931,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: account_id.clone(), amount: 42, }), topics: vec![], @@ -2923,7 +2940,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: addr.clone(), + contract: addr, }), topics: vec![], }, @@ -2931,8 +2948,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE, - to: addr.clone(), + from: ALICE_ADDR, + to: addr, amount: charged0, } ), @@ -2942,7 +2959,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: addr.clone(), + contract: addr, }), topics: vec![], }, @@ -2950,8 +2967,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE, - to: addr.clone(), + from: ALICE_ADDR, + to: addr, amount: charged1, } ), @@ -2961,7 +2978,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: addr.clone(), + contract: addr, }), topics: vec![], }, @@ -2969,8 +2986,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndReleased { - from: addr.clone(), - to: ALICE, + from: addr, + to: ALICE_ADDR, amount: refunded0, } ), @@ -2983,24 +3000,24 @@ mod run_tests { #[test] fn storage_deposit_callee_works() { - let (wasm_caller, _code_hash_caller) = compile_module::("call").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); // Create both contracts: Constructors do nothing. - let addr_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, account_id } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); assert_ok!(builder::call(addr_caller).data((100u32, &addr_callee).encode()).build()); let callee = get_contract(&addr_callee); let deposit = DepositPerByte::get() * 100 + DepositPerItem::get() * 1; - assert_eq!(test_utils::get_balance(&addr_callee), min_balance); + assert_eq!(test_utils::get_balance(&account_id), min_balance); assert_eq!( callee.total_deposit(), deposit + test_utils::contract_info_storage_deposit(&addr_callee) @@ -3010,15 +3027,16 @@ mod run_tests { #[test] fn set_code_extrinsic() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); - let (new_wasm, new_code_hash) = compile_module::("crypto_hashes").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); + let (new_wasm, new_code_hash) = compile_module("crypto_hashes").unwrap(); assert_ne!(code_hash, new_code_hash); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); assert_ok!(Contracts::upload_code( RuntimeOrigin::signed(ALICE), @@ -3035,7 +3053,7 @@ mod run_tests { // only root can execute this extrinsic assert_noop!( - Contracts::set_code(RuntimeOrigin::signed(ALICE), addr.clone(), new_code_hash), + Contracts::set_code(RuntimeOrigin::signed(ALICE), addr, new_code_hash), sp_runtime::traits::BadOrigin, ); assert_eq!(get_contract(&addr).code_hash, code_hash); @@ -3045,7 +3063,7 @@ mod run_tests { // contract must exist assert_noop!( - Contracts::set_code(RuntimeOrigin::root(), BOB, new_code_hash), + Contracts::set_code(RuntimeOrigin::root(), BOB_ADDR, new_code_hash), >::ContractNotFound, ); assert_eq!(get_contract(&addr).code_hash, code_hash); @@ -3055,7 +3073,7 @@ mod run_tests { // new code hash must exist assert_noop!( - Contracts::set_code(RuntimeOrigin::root(), addr.clone(), Default::default()), + Contracts::set_code(RuntimeOrigin::root(), addr, Default::default()), >::CodeNotFound, ); assert_eq!(get_contract(&addr).code_hash, code_hash); @@ -3064,7 +3082,7 @@ mod run_tests { assert_eq!(System::events(), vec![]); // successful call - assert_ok!(Contracts::set_code(RuntimeOrigin::root(), addr.clone(), new_code_hash)); + assert_ok!(Contracts::set_code(RuntimeOrigin::root(), addr, new_code_hash)); assert_eq!(get_contract(&addr).code_hash, new_code_hash); assert_refcount!(&code_hash, 0); assert_refcount!(&new_code_hash, 1); @@ -3073,7 +3091,7 @@ mod run_tests { vec![EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(pallet_revive::Event::ContractCodeUpdated { - contract: addr.clone(), + contract: addr, new_code_hash, old_code_hash: code_hash, }), @@ -3085,15 +3103,15 @@ mod run_tests { #[test] fn slash_cannot_kill_account() { - let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + let (wasm, _code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let value = 700; let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, account_id } = builder::bare_instantiate(Code::Upload(wasm)) .value(value) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Drop previous events initialize_block(2); @@ -3101,12 +3119,15 @@ mod run_tests { let info_deposit = test_utils::contract_info_storage_deposit(&addr); assert_eq!( - test_utils::get_balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr), + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &account_id + ), info_deposit ); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), info_deposit + value + min_balance ); @@ -3116,18 +3137,18 @@ mod run_tests { // in staking. let _ = ::Currency::slash( &HoldReason::StorageDepositReserve.into(), - &addr, - ::Currency::total_balance(&addr), + &account_id, + ::Currency::total_balance(&account_id), ); // Slashing only removed the balance held. - assert_eq!(::Currency::total_balance(&addr), value + min_balance); + assert_eq!(::Currency::total_balance(&account_id), value + min_balance); }); } #[test] fn contract_reverted() { - let (wasm, code_hash) = compile_module::("return_with_data").unwrap(); + let (wasm, code_hash) = compile_module("return_with_data").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -3163,21 +3184,21 @@ mod run_tests { .build_and_unwrap_result(); assert_eq!(result.result.flags, flags); assert_eq!(result.result.data, buffer); - assert!(!>::contains_key(result.account_id)); + assert!(!>::contains_key(result.addr)); // Pass empty flags and therefore successfully instantiate the contract for later use. - let addr = builder::bare_instantiate(Code::Existing(code_hash)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Existing(code_hash)) .data(ReturnFlags::empty().bits().encode()) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Calling extrinsic: revert leads to an error assert_err_ignore_postinfo!( - builder::call(addr.clone()).data(input.clone()).build(), + builder::call(addr).data(input.clone()).build(), >::ContractReverted, ); // Calling directly: revert leads to success but the flags indicate the error - let result = builder::bare_call(addr.clone()).data(input).build_and_unwrap_result(); + let result = builder::bare_call(addr).data(input).build_and_unwrap_result(); assert_eq!(result.flags, flags); assert_eq!(result.data, buffer); }); @@ -3185,17 +3206,17 @@ mod run_tests { #[test] fn set_code_hash() { - let (wasm, code_hash) = compile_module::("set_code_hash").unwrap(); - let (new_wasm, new_code_hash) = - compile_module::("new_set_code_hash_contract").unwrap(); + let (wasm, code_hash) = compile_module("set_code_hash").unwrap(); + let (new_wasm, new_code_hash) = compile_module("new_set_code_hash_contract").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the 'caller' - let contract_addr = builder::bare_instantiate(Code::Upload(wasm)) - .value(300_000) - .build_and_unwrap_account_id(); + let Contract { addr: contract_addr, .. } = + builder::bare_instantiate(Code::Upload(wasm)) + .value(300_000) + .build_and_unwrap_contract(); // upload new code assert_ok!(Contracts::upload_code( RuntimeOrigin::signed(ALICE), @@ -3206,14 +3227,14 @@ mod run_tests { System::reset_events(); // First call sets new code_hash and returns 1 - let result = builder::bare_call(contract_addr.clone()) + let result = builder::bare_call(contract_addr) .data(new_code_hash.as_ref().to_vec()) .debug(DebugInfo::UnsafeDebug) .build_and_unwrap_result(); assert_return_code!(result, 1); // Second calls new contract code that returns 2 - let result = builder::bare_call(contract_addr.clone()) + let result = builder::bare_call(contract_addr) .debug(DebugInfo::UnsafeDebug) .build_and_unwrap_result(); assert_return_code!(result, 2); @@ -3225,7 +3246,7 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::ContractCodeUpdated { - contract: contract_addr.clone(), + contract: contract_addr, new_code_hash, old_code_hash: code_hash, }), @@ -3235,7 +3256,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: contract_addr.clone(), + contract: contract_addr, }), topics: vec![], }, @@ -3243,7 +3264,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: contract_addr.clone(), + contract: contract_addr, }), topics: vec![], }, @@ -3254,7 +3275,7 @@ mod run_tests { #[test] fn storage_deposit_limit_is_enforced() { - let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + let (wasm, _code_hash) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); @@ -3270,13 +3291,14 @@ mod run_tests { ); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); let info_deposit = test_utils::contract_info_storage_deposit(&addr); // Check that the BOB contract has been instantiated and has the minimum balance assert_eq!(get_contract(&addr).total_deposit(), info_deposit); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), info_deposit + min_balance ); @@ -3284,7 +3306,7 @@ mod run_tests { // setting insufficient deposit limit, as it requires 3 Balance: // 2 for the item added + 1 for the new storage item. assert_err_ignore_postinfo!( - builder::call(addr.clone()) + builder::call(addr) .storage_deposit_limit(2) .data(1u32.to_le_bytes().to_vec()) .build(), @@ -3294,7 +3316,7 @@ mod run_tests { // Create 1 byte of storage, should cost 3 Balance: // 2 for the item added + 1 for the new storage item. // Should pass as it fallbacks to DefaultDepositLimit. - assert_ok!(builder::call(addr.clone()) + assert_ok!(builder::call(addr) .storage_deposit_limit(3) .data(1u32.to_le_bytes().to_vec()) .build()); @@ -3302,7 +3324,7 @@ mod run_tests { // Use 4 more bytes of the storage for the same item, which requires 4 Balance. // Should fail as DefaultDepositLimit is 3 and hence isn't enough. assert_err_ignore_postinfo!( - builder::call(addr.clone()) + builder::call(addr) .storage_deposit_limit(3) .data(5u32.to_le_bytes().to_vec()) .build(), @@ -3313,21 +3335,20 @@ mod run_tests { #[test] fn deposit_limit_in_nested_calls() { - let (wasm_caller, _code_hash_caller) = - compile_module::("create_storage_and_call").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("create_storage_and_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create both contracts: Constructors do nothing. - let addr_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); // Create 100 bytes of storage with a price of per byte // This is 100 Balance + 2 Balance for the item - assert_ok!(builder::call(addr_callee.clone()) + assert_ok!(builder::call(addr_callee) .storage_deposit_limit(102) .data(100u32.to_le_bytes().to_vec()) .build()); @@ -3338,7 +3359,7 @@ mod run_tests { // This should fail as the specified parent's limit is less than the cost: 13 < // 14. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .storage_deposit_limit(13) .data((100u32, &addr_callee, 0u64).encode()) .build(), @@ -3352,7 +3373,7 @@ mod run_tests { // This should fail as the specified parent's limit is less than the cost: 14 // < 15. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .storage_deposit_limit(14) .data((101u32, &addr_callee, 0u64).encode()) .build(), @@ -3365,7 +3386,7 @@ mod run_tests { // that the nested call should have a deposit limit of at least 2 Balance. The // sub-call should be rolled back, which is covered by the next test case. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .storage_deposit_limit(16) .data((102u32, &addr_callee, 1u64).encode()) .build(), @@ -3376,7 +3397,7 @@ mod run_tests { // caller. Note that if previous sub-call wouldn't roll back, this call would pass // making the test case fail. We don't set a special limit for the nested call here. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .storage_deposit_limit(0) .data((87u32, &addr_callee, 0u64).encode()) .build(), @@ -3388,16 +3409,14 @@ mod run_tests { // Require more than the sender's balance. // We don't set a special limit for the nested call. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) - .data((512u32, &addr_callee, 1u64).encode()) - .build(), + builder::call(addr_caller).data((512u32, &addr_callee, 1u64).encode()).build(), >::StorageDepositLimitExhausted, ); // Same as above but allow for the additional deposit of 1 Balance in parent. // We set the special deposit limit of 1 Balance for the nested call, which isn't // enforced as callee frees up storage. This should pass. - assert_ok!(builder::call(addr_caller.clone()) + assert_ok!(builder::call(addr_caller) .storage_deposit_limit(1) .data((87u32, &addr_callee, 1u64).encode()) .build()); @@ -3407,20 +3426,21 @@ mod run_tests { #[test] fn deposit_limit_in_nested_instantiate() { let (wasm_caller, _code_hash_caller) = - compile_module::("create_storage_and_instantiate").unwrap(); - let (wasm_callee, code_hash_callee) = compile_module::("store_deploy").unwrap(); + compile_module("create_storage_and_instantiate").unwrap(); + let (wasm_callee, code_hash_callee) = compile_module("store_deploy").unwrap(); const ED: u64 = 5; ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let _ = ::Currency::set_balance(&BOB, 1_000_000); // Create caller contract - let addr_caller = builder::bare_instantiate(Code::Upload(wasm_caller)) - .value(10_000u64) // this balance is later passed to the deployed contract - .build_and_unwrap_account_id(); + let Contract { addr: addr_caller, account_id: caller_id } = + builder::bare_instantiate(Code::Upload(wasm_caller)) + .value(10_000u64) // this balance is later passed to the deployed contract + .build_and_unwrap_contract(); // Deploy a contract to get its occupied storage size - let addr = builder::bare_instantiate(Code::Upload(wasm_callee)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm_callee)) .data(vec![0, 0, 0, 0]) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let callee_info_len = ContractInfoOf::::get(&addr).unwrap().encoded_size() as u64; @@ -3436,7 +3456,7 @@ mod run_tests { // Provided the limit is set to be 1 Balance less, // this call should fail on the return from the caller contract. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 1) .data((0u32, &code_hash_callee, 0u64).encode()) @@ -3450,7 +3470,7 @@ mod run_tests { // byte in the constructor. Hence +1 Balance to the limit is needed. This should fail on // the return from constructor. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 2) .data((1u32, &code_hash_callee, 0u64).encode()) @@ -3464,7 +3484,7 @@ mod run_tests { // instantiate. This should fail during the charging for the instantiation in // `RawMeter::charge_instantiate()` assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 2) .data((0u32, &code_hash_callee, callee_info_len + 2 + ED + 1).encode()) @@ -3479,7 +3499,7 @@ mod run_tests { // Now we set enough limit for the parent call, but insufficient limit for child // instantiate. This should fail right after the constructor execution. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 3) // enough parent limit .data((1u32, &code_hash_callee, callee_info_len + 2 + ED + 2).encode()) @@ -3490,7 +3510,7 @@ mod run_tests { assert_eq!(::Currency::free_balance(&BOB), 1_000_000); // Set enough deposit limit for the child instantiate. This should succeed. - let result = builder::bare_call(addr_caller.clone()) + let result = builder::bare_call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 4) .data((1u32, &code_hash_callee, callee_info_len + 2 + ED + 3).encode()) @@ -3499,12 +3519,13 @@ mod run_tests { let returned = result.result.unwrap(); // All balance of the caller except ED has been transferred to the callee. // No deposit has been taken from it. - assert_eq!(::Currency::free_balance(&addr_caller), ED); + assert_eq!(::Currency::free_balance(&caller_id), ED); // Get address of the deployed contract. - let addr_callee = AccountId32::from_slice(&returned.data[0..32]).unwrap(); + let addr_callee = H160::from_slice(&returned.data[0..20]); + let callee_account_id = ::AddressMapper::to_account_id(&addr_callee); // 10_000 should be sent to callee from the caller contract, plus ED to be sent from the // origin. - assert_eq!(::Currency::free_balance(&addr_callee), 10_000 + ED); + assert_eq!(::Currency::free_balance(&callee_account_id), 10_000 + ED); // The origin should be charged with: // - callee instantiation deposit = (callee_info_len + 2) // - callee account ED @@ -3520,7 +3541,7 @@ mod run_tests { #[test] fn deposit_limit_honors_liquidity_restrictions() { - let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + let (wasm, _code_hash) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let bobs_balance = 1_000; let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -3528,13 +3549,14 @@ mod run_tests { let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); let info_deposit = test_utils::contract_info_storage_deposit(&addr); // Check that the contract has been instantiated and has the minimum balance assert_eq!(get_contract(&addr).total_deposit(), info_deposit); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), info_deposit + min_balance ); @@ -3546,7 +3568,7 @@ mod run_tests { ) .unwrap(); assert_err_ignore_postinfo!( - builder::call(addr.clone()) + builder::call(addr) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(10_000) .data(100u32.to_le_bytes().to_vec()) @@ -3559,27 +3581,28 @@ mod run_tests { #[test] fn deposit_limit_honors_existential_deposit() { - let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + let (wasm, _code_hash) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let _ = ::Currency::set_balance(&BOB, 300); let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); let info_deposit = test_utils::contract_info_storage_deposit(&addr); // Check that the contract has been instantiated and has the minimum balance assert_eq!(get_contract(&addr).total_deposit(), info_deposit); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), min_balance + info_deposit ); // check that the deposit can't bring the account below the existential deposit assert_err_ignore_postinfo!( - builder::call(addr.clone()) + builder::call(addr) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(10_000) .data(100u32.to_le_bytes().to_vec()) @@ -3592,14 +3615,15 @@ mod run_tests { #[test] fn deposit_limit_honors_min_leftover() { - let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + let (wasm, _code_hash) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let _ = ::Currency::set_balance(&BOB, 1_000); let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); let info_deposit = test_utils::contract_info_storage_deposit(&addr); @@ -3607,7 +3631,7 @@ mod run_tests { // storage deposit assert_eq!(get_contract(&addr).total_deposit(), info_deposit); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), info_deposit + min_balance ); @@ -3616,7 +3640,7 @@ mod run_tests { // 50 for the storage deposit. Which is not enough to store the 50 bytes // as we also need 2 bytes for the item assert_err_ignore_postinfo!( - builder::call(addr.clone()) + builder::call(addr) .origin(RuntimeOrigin::signed(BOB)) .value(750) .storage_deposit_limit(10_000) @@ -3633,13 +3657,12 @@ mod run_tests { // set hash lock up deposit to 30%, to test deposit calculation. CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30)); - let (wasm_caller, self_code_hash) = - compile_module::("locking_delegate_dependency").unwrap(); + let (wasm_caller, self_code_hash) = compile_module("locking_delegate_dependency").unwrap(); let callee_codes: Vec<_> = (0..limits::DELEGATE_DEPENDENCIES + 1).map(|idx| dummy_unique(idx)).collect(); let callee_hashes: Vec<_> = callee_codes .iter() - .map(|c| ::Hashing::hash(c)) + .map(|c| sp_core::H256(sp_io::hashing::keccak_256(c))) .collect(); // Define inputs with various actions to test locking / unlocking delegate_dependencies. @@ -3652,17 +3675,21 @@ mod run_tests { // Instantiate the caller contract with the given input. let instantiate = |input: &(u32, H256)| { builder::bare_instantiate(Code::Upload(wasm_caller.clone())) + .origin(RuntimeOrigin::signed(ETH_ALICE)) .data(input.encode()) .build() }; // Call contract with the given input. - let call = |addr_caller: &AccountId32, input: &(u32, H256)| { - builder::bare_call(addr_caller.clone()).data(input.encode()).build() + let call = |addr_caller: &H160, input: &(u32, H256)| { + builder::bare_call(*addr_caller) + .origin(RuntimeOrigin::signed(ETH_ALICE)) + .data(input.encode()) + .build() }; const ED: u64 = 2000; ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let _ = Balances::set_balance(&ALICE, 1_000_000); + let _ = Balances::set_balance(Ð_ALICE, 1_000_000); // Instantiate with lock_delegate_dependency should fail since the code is not yet on // chain. @@ -3676,7 +3703,7 @@ mod run_tests { for code in callee_codes.iter() { let CodeUploadReturnValue { deposit: deposit_per_code, .. } = Contracts::bare_upload_code( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(ETH_ALICE), code.clone(), deposit_limit::(), ) @@ -3685,8 +3712,8 @@ mod run_tests { } // Instantiate should now work. - let addr_caller = - instantiate(&lock_delegate_dependency_input).result.unwrap().account_id; + let addr_caller = instantiate(&lock_delegate_dependency_input).result.unwrap().addr; + let caller_account_id = ::AddressMapper::to_account_id(&addr_caller); // There should be a dependency and a deposit. let contract = test_utils::get_contract(&addr_caller); @@ -3699,14 +3726,14 @@ mod run_tests { assert_eq!( test_utils::get_balance_on_hold( &HoldReason::StorageDepositReserve.into(), - &addr_caller + &caller_account_id ), dependency_deposit + contract.storage_base_deposit() ); // Removing the code should fail, since we have added a dependency. assert_err!( - Contracts::remove_code(RuntimeOrigin::signed(ALICE), callee_hashes[0]), + Contracts::remove_code(RuntimeOrigin::signed(ETH_ALICE), callee_hashes[0]), >::CodeInUse ); @@ -3742,7 +3769,7 @@ mod run_tests { assert_eq!( test_utils::get_balance_on_hold( &HoldReason::StorageDepositReserve.into(), - &addr_caller + &caller_account_id ), contract.storage_base_deposit() ); @@ -3755,7 +3782,7 @@ mod run_tests { // Locking a dependency with a storage limit too low should fail. assert_err!( - builder::bare_call(addr_caller.clone()) + builder::bare_call(addr_caller) .storage_deposit_limit(dependency_deposit - 1) .data(lock_delegate_dependency_input.encode()) .build() @@ -3764,14 +3791,14 @@ mod run_tests { ); // Since we unlocked the dependency we should now be able to remove the code. - assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), callee_hashes[0])); + assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ETH_ALICE), callee_hashes[0])); // Calling should fail since the delegated contract is not on chain anymore. assert_err!(call(&addr_caller, &noop_input).result, Error::::ContractTrapped); // Add the dependency back. Contracts::upload_code( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(ETH_ALICE), callee_codes[0].clone(), deposit_limit::(), ) @@ -3779,22 +3806,22 @@ mod run_tests { call(&addr_caller, &lock_delegate_dependency_input).result.unwrap(); // Call terminate should work, and return the deposit. - let balance_before = test_utils::get_balance(&ALICE); + let balance_before = test_utils::get_balance(Ð_ALICE); assert_ok!(call(&addr_caller, &terminate_input).result); assert_eq!( - test_utils::get_balance(&ALICE), + test_utils::get_balance(Ð_ALICE), ED + balance_before + contract.storage_base_deposit() + dependency_deposit ); // Terminate should also remove the dependency, so we can remove the code. - assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), callee_hashes[0])); + assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ETH_ALICE), callee_hashes[0])); }); } #[test] fn native_dependency_deposit_works() { - let (wasm, code_hash) = compile_module::("set_code_hash").unwrap(); - let (dummy_wasm, dummy_code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("set_code_hash").unwrap(); + let (dummy_wasm, dummy_code_hash) = compile_module("dummy").unwrap(); // Set hash lock up deposit to 30%, to test deposit calculation. CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30)); @@ -3830,7 +3857,8 @@ mod run_tests { // Instantiate the set_code_hash contract. let res = builder::bare_instantiate(code).build(); - let addr = res.result.unwrap().account_id; + let addr = res.result.unwrap().addr; + let account_id = ::AddressMapper::to_account_id(&addr); let base_deposit = test_utils::contract_info_storage_deposit(&addr); let upload_deposit = test_utils::get_code_deposit(&code_hash); let extra_deposit = add_upload_deposit.then(|| upload_deposit).unwrap_or_default(); @@ -3846,7 +3874,7 @@ mod run_tests { ); // call set_code_hash - builder::bare_call(addr.clone()) + builder::bare_call(addr) .data(dummy_code_hash.encode()) .build_and_unwrap_result(); @@ -3858,7 +3886,7 @@ mod run_tests { assert_eq!( test_utils::get_balance_on_hold( &HoldReason::StorageDepositReserve.into(), - &addr + &account_id ), deposit ); @@ -3868,7 +3896,7 @@ mod run_tests { #[test] fn root_cannot_upload_code() { - let (wasm, _) = compile_module::("dummy").unwrap(); + let (wasm, _) = compile_module("dummy").unwrap(); ExtBuilder::default().build().execute_with(|| { assert_noop!( @@ -3880,7 +3908,7 @@ mod run_tests { #[test] fn root_cannot_remove_code() { - let (_, code_hash) = compile_module::("dummy").unwrap(); + let (_, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().build().execute_with(|| { assert_noop!( @@ -3892,11 +3920,11 @@ mod run_tests { #[test] fn signed_cannot_set_code() { - let (_, code_hash) = compile_module::("dummy").unwrap(); + let (_, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().build().execute_with(|| { assert_noop!( - Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB, code_hash), + Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB_ADDR, code_hash), DispatchError::BadOrigin, ); }); @@ -3906,7 +3934,7 @@ mod run_tests { fn none_cannot_call_code() { ExtBuilder::default().build().execute_with(|| { assert_err_ignore_postinfo!( - builder::call(BOB).origin(RuntimeOrigin::none()).build(), + builder::call(BOB_ADDR).origin(RuntimeOrigin::none()).build(), DispatchError::BadOrigin, ); }); @@ -3914,21 +3942,22 @@ mod run_tests { #[test] fn root_can_call() { - let (wasm, _) = compile_module::("dummy").unwrap(); + let (wasm, _) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); // Call the contract. - assert_ok!(builder::call(addr.clone()).origin(RuntimeOrigin::root()).build()); + assert_ok!(builder::call(addr).origin(RuntimeOrigin::root()).build()); }); } #[test] fn root_cannot_instantiate_with_code() { - let (wasm, _) = compile_module::("dummy").unwrap(); + let (wasm, _) = compile_module("dummy").unwrap(); ExtBuilder::default().build().execute_with(|| { assert_err_ignore_postinfo!( @@ -3940,7 +3969,7 @@ mod run_tests { #[test] fn root_cannot_instantiate() { - let (_, code_hash) = compile_module::("dummy").unwrap(); + let (_, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().build().execute_with(|| { assert_err_ignore_postinfo!( @@ -3952,7 +3981,7 @@ mod run_tests { #[test] fn only_upload_origin_can_upload() { - let (wasm, _) = compile_module::("dummy").unwrap(); + let (wasm, _) = compile_module("dummy").unwrap(); UploadAccount::set(Some(ALICE)); ExtBuilder::default().build().execute_with(|| { let _ = Balances::set_balance(&ALICE, 1_000_000); @@ -3987,7 +4016,7 @@ mod run_tests { #[test] fn only_instantiation_origin_can_instantiate() { - let (code, code_hash) = compile_module::("dummy").unwrap(); + let (code, code_hash) = compile_module("dummy").unwrap(); InstantiateAccount::set(Some(ALICE)); ExtBuilder::default().build().execute_with(|| { let _ = Balances::set_balance(&ALICE, 1_000_000); @@ -4020,23 +4049,23 @@ mod run_tests { #[test] fn balance_api_returns_free_balance() { - let (wasm, _code_hash) = compile_module::("balance").unwrap(); + let (wasm, _code_hash) = compile_module("balance").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the BOB contract without any extra balance. - let addr = builder::bare_instantiate(Code::Upload(wasm.to_vec())) - .build_and_unwrap_account_id(); + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(wasm.to_vec())).build_and_unwrap_contract(); let value = 0; // Call BOB which makes it call the balance runtime API. // The contract code asserts that the returned balance is 0. - assert_ok!(builder::call(addr.clone()).value(value).build()); + assert_ok!(builder::call(addr).value(value).build()); let value = 1; // Calling with value will trap the contract. assert_err_ignore_postinfo!( - builder::call(addr.clone()).value(value).build(), + builder::call(addr).value(value).build(), >::ContractTrapped ); }); @@ -4044,17 +4073,18 @@ mod run_tests { #[test] fn gas_consumed_is_linear_for_nested_calls() { - let (code, _code_hash) = compile_module::("recurse").unwrap(); + let (code, _code_hash) = compile_module("recurse").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_account_id(); + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); let [gas_0, gas_1, gas_2, gas_max] = { [0u32, 1u32, 2u32, limits::CALL_STACK_DEPTH] .iter() .map(|i| { - let result = builder::bare_call(addr.clone()).data(i.encode()).build(); + let result = builder::bare_call(addr).data(i.encode()).build(); assert_ok!(result.result); result.gas_consumed }) @@ -4070,16 +4100,16 @@ mod run_tests { #[test] fn read_only_call_cannot_store() { - let (wasm_caller, _code_hash_caller) = compile_module::("read_only_call").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("read_only_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create both contracts: Constructors do nothing. - let addr_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); // Read-only call fails when modifying storage. assert_err_ignore_postinfo!( @@ -4091,17 +4121,16 @@ mod run_tests { #[test] fn read_only_call_cannot_transfer() { - let (wasm_caller, _code_hash_caller) = - compile_module::("call_with_flags_and_value").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("dummy").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("call_with_flags_and_value").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create both contracts: Constructors do nothing. - let addr_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); // Read-only call fails when a non-zero value is set. assert_err_ignore_postinfo!( @@ -4118,21 +4147,20 @@ mod run_tests { #[test] fn read_only_subsequent_call_cannot_store() { - let (wasm_read_only_caller, _code_hash_caller) = - compile_module::("read_only_call").unwrap(); - let (wasm_caller, _code_hash_caller) = - compile_module::("call_with_flags_and_value").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); + let (wasm_read_only_caller, _code_hash_caller) = compile_module("read_only_call").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("call_with_flags_and_value").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create contracts: Constructors do nothing. - let addr_caller = builder::bare_instantiate(Code::Upload(wasm_read_only_caller)) - .build_and_unwrap_account_id(); - let addr_subsequent_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_read_only_caller)) + .build_and_unwrap_contract(); + let Contract { addr: addr_subsequent_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); // Subsequent call input. let input = (&addr_callee, pallet_revive_uapi::CallFlags::empty().bits(), 0u64, 100u32); @@ -4149,18 +4177,18 @@ mod run_tests { #[test] fn read_only_call_works() { - let (wasm_caller, _code_hash_caller) = compile_module::("read_only_call").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("dummy").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("read_only_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create both contracts: Constructors do nothing. - let addr_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); - assert_ok!(builder::call(addr_caller.clone()).data(addr_callee.encode()).build()); + assert_ok!(builder::call(addr_caller).data(addr_callee.encode()).build()); }); } } diff --git a/substrate/frame/revive/src/tests/test_debug.rs b/substrate/frame/revive/src/tests/test_debug.rs index 166a0a8606a6..e3571b7f21eb 100644 --- a/substrate/frame/revive/src/tests/test_debug.rs +++ b/substrate/frame/revive/src/tests/test_debug.rs @@ -16,18 +16,19 @@ // limitations under the License. use super::*; + use crate::{ debug::{CallInterceptor, CallSpan, ExecResult, ExportedFunction, Tracing}, primitives::ExecReturnValue, test_utils::*, - AccountIdOf, }; use frame_support::traits::Currency; +use sp_core::H160; use std::cell::RefCell; #[derive(Clone, PartialEq, Eq, Debug)] struct DebugFrame { - contract_account: AccountId32, + contract_account: sp_core::H160, call: ExportedFunction, input: Vec, result: Option>, @@ -35,12 +36,12 @@ struct DebugFrame { thread_local! { static DEBUG_EXECUTION_TRACE: RefCell> = RefCell::new(Vec::new()); - static INTERCEPTED_ADDRESS: RefCell> = RefCell::new(None); + static INTERCEPTED_ADDRESS: RefCell> = RefCell::new(None); } pub struct TestDebug; pub struct TestCallSpan { - contract_account: AccountId32, + contract_account: sp_core::H160, call: ExportedFunction, input: Vec, } @@ -49,20 +50,20 @@ impl Tracing for TestDebug { type CallSpan = TestCallSpan; fn new_call_span( - contract_account: &AccountIdOf, + contract_account: &crate::H160, entry_point: ExportedFunction, input_data: &[u8], ) -> TestCallSpan { DEBUG_EXECUTION_TRACE.with(|d| { d.borrow_mut().push(DebugFrame { - contract_account: contract_account.clone(), + contract_account: *contract_account, call: entry_point, input: input_data.to_vec(), result: None, }) }); TestCallSpan { - contract_account: contract_account.clone(), + contract_account: *contract_account, call: entry_point, input: input_data.to_vec(), } @@ -71,7 +72,7 @@ impl Tracing for TestDebug { impl CallInterceptor for TestDebug { fn intercept_call( - contract_address: &::AccountId, + contract_address: &sp_core::H160, _entry_point: ExportedFunction, _input_data: &[u8], ) -> Option { @@ -106,14 +107,14 @@ mod run_tests { #[test] fn debugging_works() { - let (wasm_caller, _) = compile_module::("call").unwrap(); - let (wasm_callee, _) = compile_module::("store_call").unwrap(); + let (wasm_caller, _) = compile_module("call").unwrap(); + let (wasm_callee, _) = compile_module("store_call").unwrap(); fn current_stack() -> Vec { DEBUG_EXECUTION_TRACE.with(|stack| stack.borrow().clone()) } - fn deploy(wasm: Vec) -> AccountId32 { + fn deploy(wasm: Vec) -> H160 { Contracts::bare_instantiate( RuntimeOrigin::signed(ALICE), 0, @@ -121,27 +122,27 @@ mod run_tests { deposit_limit::(), Code::Upload(wasm), vec![], - vec![], + [0u8; 32], DebugInfo::Skip, CollectEvents::Skip, ) .result .unwrap() - .account_id + .addr } - fn constructor_frame(contract_account: &AccountId32, after: bool) -> DebugFrame { + fn constructor_frame(contract_account: &H160, after: bool) -> DebugFrame { DebugFrame { - contract_account: contract_account.clone(), + contract_account: *contract_account, call: ExportedFunction::Constructor, input: vec![], result: if after { Some(vec![]) } else { None }, } } - fn call_frame(contract_account: &AccountId32, args: Vec, after: bool) -> DebugFrame { + fn call_frame(contract_account: &H160, args: Vec, after: bool) -> DebugFrame { DebugFrame { - contract_account: contract_account.clone(), + contract_account: *contract_account, call: ExportedFunction::Call, input: args, result: if after { Some(vec![]) } else { None }, @@ -171,7 +172,7 @@ mod run_tests { assert_ok!(Contracts::call( RuntimeOrigin::signed(ALICE), - addr_caller.clone(), + addr_caller, 0, GAS_LIMIT, deposit_limit::(), @@ -193,7 +194,7 @@ mod run_tests { #[test] fn call_interception_works() { - let (wasm, _) = compile_module::("dummy").unwrap(); + let (wasm, _) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); @@ -206,18 +207,18 @@ mod run_tests { Code::Upload(wasm), vec![], // some salt to ensure that the address of this contract is unique among all tests - vec![0x41, 0x41, 0x41, 0x41], + [0x41; 32], DebugInfo::Skip, CollectEvents::Skip, ) .result .unwrap() - .account_id; + .addr; // no interception yet assert_ok!(Contracts::call( RuntimeOrigin::signed(ALICE), - account_id.clone(), + account_id, 0, GAS_LIMIT, deposit_limit::(), @@ -225,7 +226,7 @@ mod run_tests { )); // intercept calls to this contract - INTERCEPTED_ADDRESS.with(|i| *i.borrow_mut() = Some(account_id.clone())); + INTERCEPTED_ADDRESS.with(|i| *i.borrow_mut() = Some(account_id)); assert_err_ignore_postinfo!( Contracts::call( diff --git a/substrate/frame/revive/src/wasm/mod.rs b/substrate/frame/revive/src/wasm/mod.rs index 784993ca793d..9024390fd24f 100644 --- a/substrate/frame/revive/src/wasm/mod.rs +++ b/substrate/frame/revive/src/wasm/mod.rs @@ -32,12 +32,13 @@ pub use crate::wasm::runtime::{ReturnData, TrapReason}; pub use crate::wasm::runtime::{ApiVersion, Memory, Runtime, RuntimeCosts}; use crate::{ + address::AddressMapper, exec::{ExecResult, Executable, ExportedFunction, Ext}, gas::{GasMeter, Token}, storage::meter::Diff, weights::WeightInfo, - AccountIdOf, BadOrigin, BalanceOf, CodeHash, CodeInfoOf, CodeVec, Config, Error, Event, - ExecError, HoldReason, Pallet, PristineCode, Weight, API_VERSION, LOG_TARGET, + AccountIdOf, BadOrigin, BalanceOf, CodeInfoOf, CodeVec, Config, Error, Event, ExecError, + HoldReason, Pallet, PristineCode, Weight, API_VERSION, LOG_TARGET, }; use alloc::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; @@ -47,7 +48,7 @@ use frame_support::{ traits::{fungible::MutateHold, tokens::Precision::BestEffort}, }; use sp_core::Get; -use sp_runtime::{traits::Hash, DispatchError}; +use sp_runtime::DispatchError; /// Validated Wasm module ready for execution. /// This data structure is immutable once created and stored. @@ -61,7 +62,7 @@ pub struct WasmBlob { code_info: CodeInfo, // This is for not calculating the hash every time we need it. #[codec(skip)] - code_hash: CodeHash, + code_hash: sp_core::H256, } /// Contract code related data, such as: @@ -143,14 +144,14 @@ impl WasmBlob { api_version: API_VERSION, behaviour_version: Default::default(), }; - let code_hash = T::Hashing::hash(&code); + let code_hash = sp_core::H256(sp_io::hashing::keccak_256(&code)); Ok(WasmBlob { code, code_info, code_hash }) } /// Remove the code from storage and refund the deposit to its owner. /// /// Applies all necessary checks before removing the code. - pub fn remove(origin: &T::AccountId, code_hash: CodeHash) -> DispatchResult { + pub fn remove(origin: &T::AccountId, code_hash: sp_core::H256) -> DispatchResult { >::try_mutate_exists(&code_hash, |existing| { if let Some(code_info) = existing { ensure!(code_info.refcount == 0, >::CodeInUse); @@ -162,7 +163,7 @@ impl WasmBlob { BestEffort, ); let deposit_released = code_info.deposit; - let remover = code_info.owner.clone(); + let remover = T::AddressMapper::to_address(&code_info.owner); *existing = None; >::remove(&code_hash); @@ -201,10 +202,11 @@ impl WasmBlob { self.code_info.refcount = 0; >::insert(code_hash, &self.code); *stored_code_info = Some(self.code_info.clone()); + let uploader = T::AddressMapper::to_address(&self.code_info.owner); >::deposit_event(Event::CodeStored { code_hash, deposit_held: deposit, - uploader: self.code_info.owner.clone(), + uploader, }); Ok(deposit) }, @@ -315,7 +317,7 @@ impl WasmBlob { impl Executable for WasmBlob { fn from_storage( - code_hash: CodeHash, + code_hash: sp_core::H256, gas_meter: &mut GasMeter, ) -> Result { let code_info = >::get(code_hash).ok_or(Error::::CodeNotFound)?; @@ -340,11 +342,15 @@ impl Executable for WasmBlob { prepared_call.call() } - fn code_info(&self) -> &CodeInfo { - &self.code_info + fn code(&self) -> &[u8] { + self.code.as_ref() } - fn code_hash(&self) -> &CodeHash { + fn code_hash(&self) -> &sp_core::H256 { &self.code_hash } + + fn code_info(&self) -> &CodeInfo { + &self.code_info + } } diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index de910e79e73e..70d405da9897 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -18,12 +18,13 @@ //! Environment definition of the wasm smart-contract runtime. use crate::{ + address::AddressMapper, exec::{ExecError, ExecResult, Ext, Key, TopicOf}, gas::{ChargedAmount, Token}, limits, primitives::ExecReturnValue, weights::WeightInfo, - BalanceOf, CodeHash, Config, Error, LOG_TARGET, SENTINEL, + BalanceOf, Config, Error, LOG_TARGET, SENTINEL, }; use alloc::{boxed::Box, vec, vec::Vec}; use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; @@ -34,6 +35,7 @@ use frame_support::{ }; use pallet_revive_proc_macro::define_env; use pallet_revive_uapi::{CallFlags, ReturnErrorCode, ReturnFlags, StorageFlags}; +use sp_core::{H160, H256}; use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256}; use sp_runtime::{traits::Zero, DispatchError, RuntimeDebug}; @@ -939,8 +941,8 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { let call_outcome = match call_type { CallType::Call { callee_ptr, value_ptr, deposit_ptr, weight } => { - let callee: <::T as frame_system::Config>::AccountId = - memory.read_as(callee_ptr)?; + let mut callee = H160::zero(); + memory.read_into_buf(callee_ptr, callee.as_bytes_mut())?; let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { BalanceOf::<::T>::zero() } else { @@ -959,7 +961,7 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { self.ext.call( weight, deposit_limit, - callee, + &callee, value, input_data, flags.contains(CallFlags::ALLOW_REENTRY), @@ -1022,9 +1024,10 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { memory.read_as(deposit_ptr)? }; let value: BalanceOf<::T> = memory.read_as(value_ptr)?; - let code_hash: CodeHash<::T> = memory.read_as(code_hash_ptr)?; + let code_hash: H256 = memory.read_as(code_hash_ptr)?; let input_data = memory.read(input_data_ptr, input_data_len)?; - let salt = memory.read(salt_ptr, salt_len)?; + let mut salt = [0u8; 32]; + memory.read_into_buf(salt_ptr, salt.as_mut_slice())?; let instantiate_outcome = self.ext.instantiate(weight, deposit_limit, code_hash, value, input_data, &salt); if let Ok((address, output)) = &instantiate_outcome { @@ -1054,8 +1057,8 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { let count = self.ext.locked_delegate_dependencies_count() as _; self.charge_gas(RuntimeCosts::Terminate(count))?; - let beneficiary: <::T as frame_system::Config>::AccountId = - memory.read_as(beneficiary_ptr)?; + let mut beneficiary = H160::zero(); + memory.read_into_buf(beneficiary_ptr, beneficiary.as_bytes_mut())?; self.ext.terminate(&beneficiary)?; Err(TrapReason::Termination) } @@ -1161,8 +1164,8 @@ pub mod env { value_ptr: u32, ) -> Result { self.charge_gas(RuntimeCosts::Transfer)?; - let callee: <::T as frame_system::Config>::AccountId = - memory.read_as(account_ptr)?; + let mut callee = H160::zero(); + memory.read_into_buf(account_ptr, callee.as_bytes_mut())?; let value: BalanceOf<::T> = memory.read_as(value_ptr)?; let result = self.ext.transfer(&callee, value); match result { @@ -1311,12 +1314,12 @@ pub mod env { #[api_version(0)] fn caller(&mut self, memory: &mut M, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::Caller)?; - let caller = self.ext.caller().account_id()?.clone(); + let caller = ::AddressMapper::to_address(self.ext.caller().account_id()?); Ok(self.write_sandbox_output( memory, out_ptr, out_len_ptr, - &caller.encode(), + caller.as_bytes(), false, already_charged, )?) @@ -1327,9 +1330,8 @@ pub mod env { #[api_version(0)] fn is_contract(&mut self, memory: &mut M, account_ptr: u32) -> Result { self.charge_gas(RuntimeCosts::IsContract)?; - let address: <::T as frame_system::Config>::AccountId = - memory.read_as(account_ptr)?; - + let mut address = H160::zero(); + memory.read_into_buf(account_ptr, address.as_bytes_mut())?; Ok(self.ext.is_contract(&address) as u32) } @@ -1344,8 +1346,8 @@ pub mod env { out_len_ptr: u32, ) -> Result { self.charge_gas(RuntimeCosts::CodeHash)?; - let address: <::T as frame_system::Config>::AccountId = - memory.read_as(account_ptr)?; + let mut address = H160::zero(); + memory.read_into_buf(account_ptr, address.as_bytes_mut())?; if let Some(value) = self.ext.code_hash(&address) { self.write_sandbox_output( memory, @@ -1408,11 +1410,12 @@ pub mod env { out_len_ptr: u32, ) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::Address)?; + let address = self.ext.address(); Ok(self.write_sandbox_output( memory, out_ptr, out_len_ptr, - &self.ext.address().encode(), + address.as_bytes(), false, already_charged, )?) @@ -1754,7 +1757,7 @@ pub mod env { dispatch_info, RuntimeCosts::CallXcmExecute, |runtime| { - let origin = crate::RawOrigin::Signed(runtime.ext.address().clone()).into(); + let origin = crate::RawOrigin::Signed(runtime.ext.account_id().clone()).into(); let weight_used = <::Xcm>::execute( origin, Box::new(message), @@ -1786,7 +1789,7 @@ pub mod env { let message: VersionedXcm<()> = memory.read_as_unbounded(msg_ptr, msg_len)?; let weight = <::Xcm as SendController<_>>::WeightInfo::send(); self.charge_gas(RuntimeCosts::CallRuntime(weight))?; - let origin = crate::RawOrigin::Signed(self.ext.address().clone()).into(); + let origin = crate::RawOrigin::Signed(self.ext.account_id().clone()).into(); match <::Xcm>::send(origin, dest.into(), message.into()) { Ok(message_id) => { @@ -1872,7 +1875,7 @@ pub mod env { code_hash_ptr: u32, ) -> Result { self.charge_gas(RuntimeCosts::SetCodeHash)?; - let code_hash: CodeHash<::T> = memory.read_as(code_hash_ptr)?; + let code_hash: H256 = memory.read_as(code_hash_ptr)?; match self.ext.set_code_hash(code_hash) { Err(err) => { let code = Self::err_into_return_code(err)?; From c8015b2e79ad8063701d9be026ed77d4a92e50cc Mon Sep 17 00:00:00 2001 From: Clara van Staden Date: Mon, 2 Sep 2024 21:12:10 +0200 Subject: [PATCH 223/382] Snowbridge free consensus updates (#5201) Allow free Snowbridge consensus updates, if the header interval is larger than the configured value (set to 32, so once a epoch). This PR also moves the Rococo Snowbridge pallet config into its own module. Original PR: https://github.com/Snowfork/polkadot-sdk/pull/159 --------- Co-authored-by: Francisco Aguirre --- .../pallets/ethereum-client/src/lib.rs | 53 ++++- .../pallets/ethereum-client/src/mock.rs | 4 + .../pallets/ethereum-client/src/tests.rs | 218 ++++++++++++------ .../pallets/inbound-queue/src/mock.rs | 1 + .../snowbridge/runtime/test-common/src/lib.rs | 73 +++++- .../src/bridge_to_ethereum_config.rs | 201 +++++++++++++++- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 194 +--------------- .../bridge-hub-rococo/tests/tests.rs | 10 +- .../src/bridge_to_ethereum_config.rs | 3 + prdoc/pr_5201.prdoc | 23 ++ 10 files changed, 498 insertions(+), 282 deletions(-) create mode 100644 prdoc/pr_5201.prdoc diff --git a/bridges/snowbridge/pallets/ethereum-client/src/lib.rs b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs index 6894977c21f4..84b1476931c9 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/lib.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs @@ -33,7 +33,10 @@ mod tests; mod benchmarking; use frame_support::{ - dispatch::DispatchResult, pallet_prelude::OptionQuery, traits::Get, transactional, + dispatch::{DispatchResult, PostDispatchInfo}, + pallet_prelude::OptionQuery, + traits::Get, + transactional, }; use frame_system::ensure_signed; use snowbridge_beacon_primitives::{ @@ -82,6 +85,9 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; #[pallet::constant] type ForkVersions: Get; + /// Minimum gap between finalized headers for an update to be free. + #[pallet::constant] + type FreeHeadersInterval: Get; type WeightInfo: WeightInfo; } @@ -204,11 +210,10 @@ pub mod pallet { #[transactional] /// Submits a new finalized beacon header update. The update may contain the next /// sync committee. - pub fn submit(origin: OriginFor, update: Box) -> DispatchResult { + pub fn submit(origin: OriginFor, update: Box) -> DispatchResultWithPostInfo { ensure_signed(origin)?; ensure!(!Self::operating_mode().is_halted(), Error::::Halted); - Self::process_update(&update)?; - Ok(()) + Self::process_update(&update) } /// Halt or resume all pallet operations. May only be called by root. @@ -280,10 +285,9 @@ pub mod pallet { Ok(()) } - pub(crate) fn process_update(update: &Update) -> DispatchResult { + pub(crate) fn process_update(update: &Update) -> DispatchResultWithPostInfo { Self::verify_update(update)?; - Self::apply_update(update)?; - Ok(()) + Self::apply_update(update) } /// References and strictly follows @@ -432,8 +436,9 @@ pub mod pallet { /// Reference and strictly follows DispatchResult { + /// SyncCommitteePrepared type. Stores the provided finalized header. Updates are free + /// if the certain conditions specified in `check_refundable` are met. + fn apply_update(update: &Update) -> DispatchResultWithPostInfo { let latest_finalized_state = FinalizedBeaconState::::get(LatestFinalizedBlockRoot::::get()) .ok_or(Error::::NotBootstrapped)?; @@ -465,11 +470,17 @@ pub mod pallet { }); }; + let pays_fee = Self::check_refundable(update, latest_finalized_state.slot); + let actual_weight = match update.next_sync_committee_update { + None => T::WeightInfo::submit(), + Some(_) => T::WeightInfo::submit_with_sync_committee(), + }; + if update.finalized_header.slot > latest_finalized_state.slot { Self::store_finalized_header(update.finalized_header, update.block_roots_root)?; } - Ok(()) + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee }) } /// Computes the signing root for a given beacon header and domain. The hash tree root @@ -634,11 +645,31 @@ pub mod pallet { config::SLOTS_PER_EPOCH as u64, )); let domain_type = config::DOMAIN_SYNC_COMMITTEE.to_vec(); - // Domains are used for for seeds, for signatures, and for selecting aggregators. + // Domains are used for seeds, for signatures, and for selecting aggregators. let domain = Self::compute_domain(domain_type, fork_version, validators_root)?; // Hash tree root of SigningData - object root + domain let signing_root = Self::compute_signing_root(header, domain)?; Ok(signing_root) } + + /// Updates are free if the update is successful and the interval between the latest + /// finalized header in storage and the newly imported header is large enough. All + /// successful sync committee updates are free. + pub(super) fn check_refundable(update: &Update, latest_slot: u64) -> Pays { + // If the sync committee was successfully updated, the update may be free. + if update.next_sync_committee_update.is_some() { + return Pays::No; + } + + // If the latest finalized header is larger than the minimum slot interval, the header + // import transaction is free. + if update.finalized_header.slot >= + latest_slot.saturating_add(T::FreeHeadersInterval::get() as u64) + { + return Pays::No; + } + + Pays::Yes + } } } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs index 96298d4fa896..be456565d407 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs @@ -10,6 +10,7 @@ use sp_std::default::Default; use std::{fs::File, path::PathBuf}; type Block = frame_system::mocking::MockBlock; +use frame_support::traits::ConstU32; use sp_runtime::BuildStorage; fn load_fixture(basename: String) -> Result @@ -108,9 +109,12 @@ parameter_types! { }; } +pub const FREE_SLOTS_INTERVAL: u32 = config::SLOTS_PER_EPOCH as u32; + impl ethereum_beacon_client::Config for Test { type RuntimeEvent = RuntimeEvent; type ForkVersions = ChainForkVersions; + type FreeHeadersInterval = ConstU32; type WeightInfo = (); } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/tests.rs b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs index c16743b75ea4..82a3b8224470 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/tests.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs @@ -1,21 +1,20 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork use crate::{ - functions::compute_period, sync_committee_sum, verify_merkle_branch, BeaconHeader, - CompactBeaconState, Error, FinalizedBeaconState, LatestFinalizedBlockRoot, NextSyncCommittee, - SyncCommitteePrepared, -}; - -use crate::mock::{ - get_message_verification_payload, load_checkpoint_update_fixture, - load_finalized_header_update_fixture, load_next_finalized_header_update_fixture, - load_next_sync_committee_update_fixture, load_sync_committee_update_fixture, + functions::compute_period, + mock::{ + get_message_verification_payload, load_checkpoint_update_fixture, + load_finalized_header_update_fixture, load_next_finalized_header_update_fixture, + load_next_sync_committee_update_fixture, load_sync_committee_update_fixture, + }, + sync_committee_sum, verify_merkle_branch, BeaconHeader, CompactBeaconState, Error, + FinalizedBeaconState, LatestFinalizedBlockRoot, NextSyncCommittee, SyncCommitteePrepared, }; pub use crate::mock::*; use crate::config::{EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT}; -use frame_support::{assert_err, assert_noop, assert_ok}; +use frame_support::{assert_err, assert_noop, assert_ok, pallet_prelude::Pays}; use hex_literal::hex; use snowbridge_beacon_primitives::{ types::deneb, Fork, ForkVersions, NextSyncCommitteeUpdate, VersionedExecutionPayloadHeader, @@ -129,6 +128,39 @@ pub fn compute_domain_bls() { }); } +#[test] +pub fn may_refund_call_fee() { + let finalized_update = Box::new(load_next_finalized_header_update_fixture()); + let sync_committee_update = Box::new(load_sync_committee_update_fixture()); + new_tester().execute_with(|| { + let free_headers_interval: u64 = crate::mock::FREE_SLOTS_INTERVAL as u64; + // Not free, smaller than the allowed free header interval + assert_eq!( + EthereumBeaconClient::check_refundable( + &finalized_update.clone(), + finalized_update.finalized_header.slot + free_headers_interval + ), + Pays::Yes + ); + // Is free, larger than the minimum interval + assert_eq!( + EthereumBeaconClient::check_refundable( + &finalized_update, + finalized_update.finalized_header.slot - (free_headers_interval + 2) + ), + Pays::No + ); + // Is free, valid sync committee update + assert_eq!( + EthereumBeaconClient::check_refundable( + &sync_committee_update, + finalized_update.finalized_header.slot + ), + Pays::No + ); + }); +} + #[test] pub fn verify_merkle_branch_for_finalized_root() { new_tester().execute_with(|| { @@ -340,7 +372,9 @@ fn submit_update_in_current_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::Yes); let block_root: H256 = update.finalized_header.hash_tree_root().unwrap(); assert!(>::contains_key(block_root)); }); @@ -357,7 +391,9 @@ fn submit_update_with_sync_committee_in_current_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update)); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); assert!(>::exists()); }); } @@ -374,20 +410,21 @@ fn reject_submit_update_in_next_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - sync_committee_update.clone() - )); + let result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); + // check an update in the next period is rejected - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()), - Error::::SyncCommitteeUpdateRequired - ); + let second_result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_err!(second_result, Error::::SyncCommitteeUpdateRequired); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); + // submit update with next sync committee - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - next_sync_committee_update - )); + let third_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_sync_committee_update); + assert_ok!(third_result); + assert_eq!(third_result.unwrap().pays_fee, Pays::No); // check same header in the next period can now be submitted successfully assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); let block_root: H256 = update.finalized_header.clone().hash_tree_root().unwrap(); @@ -407,10 +444,9 @@ fn submit_update_with_invalid_header_proof() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::InvalidHeaderMerkleProof - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::InvalidHeaderMerkleProof); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -426,10 +462,9 @@ fn submit_update_with_invalid_block_roots_proof() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::InvalidBlockRootsRootMerkleProof - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::InvalidBlockRootsRootMerkleProof); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -447,10 +482,9 @@ fn submit_update_with_invalid_next_sync_committee_proof() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::InvalidSyncCommitteeMerkleProof - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::InvalidSyncCommitteeMerkleProof); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -464,14 +498,14 @@ fn submit_update_with_skipped_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - sync_committee_update.clone() - )); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::SkippedSyncCommitteePeriod - ); + let result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); + + let second_result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(second_result, Error::::SkippedSyncCommitteePeriod); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -487,9 +521,16 @@ fn submit_update_with_sync_committee_in_next_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); assert!(>::exists()); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone())); + + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()); + assert_ok!(second_result); + assert_eq!(second_result.unwrap().pays_fee, Pays::No); let last_finalized_state = FinalizedBeaconState::::get(LatestFinalizedBlockRoot::::get()).unwrap(); let last_synced_period = compute_period(last_finalized_state.slot); @@ -505,13 +546,12 @@ fn submit_update_with_sync_committee_invalid_signature_slot() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - // makes a invalid update with signature_slot should be more than attested_slot + // makes an invalid update with signature_slot should be more than attested_slot update.signature_slot = update.attested_header.slot; - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::InvalidUpdateSlot - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::InvalidUpdateSlot); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -525,10 +565,9 @@ fn submit_update_with_skipped_sync_committee_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_update), - Error::::SkippedSyncCommitteePeriod - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_update); + assert_err!(result, Error::::SkippedSyncCommitteePeriod); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -546,10 +585,9 @@ fn submit_irrelevant_update() { update.attested_header.slot = checkpoint.header.slot; update.signature_slot = checkpoint.header.slot + 1; - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::IrrelevantUpdate - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::IrrelevantUpdate); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -558,10 +596,9 @@ fn submit_update_with_missing_bootstrap() { let update = Box::new(load_next_finalized_header_update_fixture()); new_tester().execute_with(|| { - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::NotBootstrapped - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::NotBootstrapped); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -574,7 +611,9 @@ fn submit_update_with_invalid_sync_committee_update() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update)); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); // makes update with invalid next_sync_committee >::mutate(>::get(), |x| { @@ -586,10 +625,9 @@ fn submit_update_with_invalid_sync_committee_update() { let next_sync_committee = NextSyncCommitteeUpdate::default(); next_update.next_sync_committee_update = Some(next_sync_committee); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update), - Error::::InvalidSyncCommitteeUpdate - ); + let second_result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update); + assert_err!(second_result, Error::::InvalidSyncCommitteeUpdate); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -612,12 +650,15 @@ fn submit_finalized_header_update_with_too_large_gap() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); assert!(>::exists()); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()), - Error::::InvalidFinalizedHeaderGap - ); + + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()); + assert_err!(second_result, Error::::InvalidFinalizedHeaderGap); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -637,14 +678,41 @@ fn submit_finalized_header_update_with_gap_at_limit() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); assert!(>::exists()); + + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()); assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()), + second_result, // The test should pass the InvalidFinalizedHeaderGap check, and will fail at the // next check, the merkle proof, because we changed the next_update slots. Error::::InvalidHeaderMerkleProof ); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); + }); +} + +#[test] +fn duplicate_sync_committee_updates_are_not_free() { + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let sync_committee_update = Box::new(load_sync_committee_update_fixture()); + + new_tester().execute_with(|| { + assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); + let result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); + + // Check that if the same update is submitted, the update is not free. + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update); + assert_err!(second_result, Error::::IrrelevantUpdate); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index a031676c6076..871df6d1e51b 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -88,6 +88,7 @@ parameter_types! { impl snowbridge_pallet_ethereum_client::Config for Test { type RuntimeEvent = RuntimeEvent; type ForkVersions = ChainForkVersions; + type FreeHeadersInterval = ConstU32<32>; type WeightInfo = (); } diff --git a/bridges/snowbridge/runtime/test-common/src/lib.rs b/bridges/snowbridge/runtime/test-common/src/lib.rs index 8f36313e360f..b157ad4356bd 100644 --- a/bridges/snowbridge/runtime/test-common/src/lib.rs +++ b/bridges/snowbridge/runtime/test-common/src/lib.rs @@ -12,7 +12,7 @@ use parachains_runtimes_test_utils::{ }; use snowbridge_core::{ChannelId, ParaId}; use snowbridge_pallet_ethereum_client_fixtures::*; -use sp_core::{H160, U256}; +use sp_core::{Get, H160, U256}; use sp_keyring::AccountKeyring::*; use sp_runtime::{traits::Header, AccountId32, DigestItem, SaturatedConversion, Saturating}; use xcm::{ @@ -466,23 +466,37 @@ pub fn ethereum_extrinsic( let initial_checkpoint = make_checkpoint(); let update = make_finalized_header_update(); let sync_committee_update = make_sync_committee_update(); + let mut invalid_update = make_finalized_header_update(); + let mut invalid_sync_committee_update = make_sync_committee_update(); + invalid_update.finalized_header.slot = 4354; + invalid_sync_committee_update.finalized_header.slot = 4354; let alice = Alice; let alice_account = alice.to_account_id(); >::mint_into( - &alice_account.into(), + &alice_account.clone().into(), 10_000_000_000_000_u128.saturated_into::>(), ) .unwrap(); + let balance_before = + >::free_balance(&alice_account.clone().into()); assert_ok!(>::force_checkpoint( RuntimeHelper::::root_origin(), - initial_checkpoint, + initial_checkpoint.clone(), )); + let balance_after_checkpoint = + >::free_balance(&alice_account.clone().into()); let update_call: ::RuntimeCall = snowbridge_pallet_ethereum_client::Call::::submit { - update: Box::new(*update), + update: Box::new(*update.clone()), + } + .into(); + + let invalid_update_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit { + update: Box::new(*invalid_update), } .into(); @@ -492,12 +506,63 @@ pub fn ethereum_extrinsic( } .into(); + let invalid_update_sync_committee_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit { + update: Box::new(*invalid_sync_committee_update), + } + .into(); + + // Finalized header update let update_outcome = construct_and_apply_extrinsic(alice, update_call.into()); assert_ok!(update_outcome); + let balance_after_update = + >::free_balance(&alice_account.clone().into()); + + // Invalid finalized header update + let invalid_update_outcome = + construct_and_apply_extrinsic(alice, invalid_update_call.into()); + assert_err!( + invalid_update_outcome, + snowbridge_pallet_ethereum_client::Error::::InvalidUpdateSlot + ); + let balance_after_invalid_update = + >::free_balance(&alice_account.clone().into()); + // Sync committee update let sync_committee_outcome = construct_and_apply_extrinsic(alice, update_sync_committee_call.into()); assert_ok!(sync_committee_outcome); + let balance_after_sync_com_update = + >::free_balance(&alice_account.clone().into()); + + // Invalid sync committee update + let invalid_sync_committee_outcome = + construct_and_apply_extrinsic(alice, invalid_update_sync_committee_call.into()); + assert_err!( + invalid_sync_committee_outcome, + snowbridge_pallet_ethereum_client::Error::::InvalidUpdateSlot + ); + let balance_after_invalid_sync_com_update = + >::free_balance(&alice_account.clone().into()); + + // Assert paid operations are charged and free operations are free + // Checkpoint is a free operation + assert!(balance_before == balance_after_checkpoint); + let gap = + ::FreeHeadersInterval::get(); + // Large enough header gap is free + if update.finalized_header.slot >= initial_checkpoint.header.slot + gap as u64 { + assert!(balance_after_checkpoint == balance_after_update); + } else { + // Otherwise paid + assert!(balance_after_checkpoint > balance_after_update); + } + // An invalid update is paid + assert!(balance_after_update > balance_after_invalid_update); + // A successful sync committee update is free + assert!(balance_after_invalid_update == balance_after_sync_com_update); + // An invalid sync committee update is paid + assert!(balance_after_sync_com_update > balance_after_invalid_sync_com_update); }); } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 01a762d4b99f..6c0486c62fa6 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -14,9 +14,34 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::{xcm_config::UniversalLocation, Runtime}; -use snowbridge_router_primitives::outbound::EthereumBlobExporter; -use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; +#[cfg(not(feature = "runtime-benchmarks"))] +use crate::XcmRouter; +use crate::{ + xcm_config, xcm_config::UniversalLocation, Balances, EthereumInboundQueue, + EthereumOutboundQueue, EthereumSystem, MessageQueue, Runtime, RuntimeEvent, TransactionByteFee, + TreasuryAccount, +}; +use parachains_common::{AccountId, Balance}; +use snowbridge_beacon_primitives::{Fork, ForkVersions}; +use snowbridge_core::{gwei, meth, AllowSiblingsOnly, PricingParameters, Rewards}; +use snowbridge_router_primitives::{inbound::MessageToXcm, outbound::EthereumBlobExporter}; +use sp_core::H160; +use testnet_parachains_constants::rococo::{ + currency::*, + fee::WeightToFee, + snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, +}; + +#[cfg(feature = "runtime-benchmarks")] +use benchmark_helpers::DoNothingRouter; +use frame_support::{parameter_types, weights::ConstantMultiplier}; +use pallet_xcm::EnsureXcm; +use sp_runtime::{ + traits::{ConstU32, ConstU8, Keccak256}, + FixedU128, +}; + +pub const SLOTS_PER_EPOCH: u32 = snowbridge_pallet_ethereum_client::config::SLOTS_PER_EPOCH as u32; /// Exports message to the Ethereum Gateway contract. pub type SnowbridgeExporter = EthereumBlobExporter< @@ -25,3 +50,173 @@ pub type SnowbridgeExporter = EthereumBlobExporter< snowbridge_pallet_outbound_queue::Pallet, snowbridge_core::AgentIdOf, >; + +// Ethereum Bridge +parameter_types! { + pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39")); +} + +parameter_types! { + pub const CreateAssetCall: [u8;2] = [53, 0]; + pub const CreateAssetDeposit: u128 = (UNITS / 10) + EXISTENTIAL_DEPOSIT; + pub Parameters: PricingParameters = PricingParameters { + exchange_rate: FixedU128::from_rational(1, 400), + fee_per_gas: gwei(20), + rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, + multiplier: FixedU128::from_rational(1, 1), + }; +} + +impl snowbridge_pallet_inbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Verifier = snowbridge_pallet_ethereum_client::Pallet; + type Token = Balances; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmSender = XcmRouter; + #[cfg(feature = "runtime-benchmarks")] + type XcmSender = DoNothingRouter; + type ChannelLookup = EthereumSystem; + type GatewayAddress = EthereumGatewayAddress; + #[cfg(feature = "runtime-benchmarks")] + type Helper = Runtime; + type MessageConverter = MessageToXcm< + CreateAssetCall, + CreateAssetDeposit, + ConstU8, + AccountId, + Balance, + >; + type WeightToFee = WeightToFee; + type LengthToFee = ConstantMultiplier; + type MaxMessageSize = ConstU32<2048>; + type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type AssetTransactor = ::AssetTransactor; +} + +impl snowbridge_pallet_outbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Hashing = Keccak256; + type MessageQueue = MessageQueue; + type Decimals = ConstU8<12>; + type MaxMessagePayloadSize = ConstU32<2048>; + type MaxMessagesPerBlock = ConstU32<32>; + type GasMeter = snowbridge_core::outbound::ConstantGasMeter; + type Balance = Balance; + type WeightToFee = WeightToFee; + type WeightInfo = crate::weights::snowbridge_pallet_outbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type Channels = EthereumSystem; +} + +#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [0, 0, 0, 0], // 0x00000000 + epoch: 0, + }, + altair: Fork { + version: [1, 0, 0, 0], // 0x01000000 + epoch: 0, + }, + bellatrix: Fork { + version: [2, 0, 0, 0], // 0x02000000 + epoch: 0, + }, + capella: Fork { + version: [3, 0, 0, 0], // 0x03000000 + epoch: 0, + }, + deneb: Fork { + version: [4, 0, 0, 0], // 0x04000000 + epoch: 0, + } + }; +} + +#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [144, 0, 0, 111], // 0x90000069 + epoch: 0, + }, + altair: Fork { + version: [144, 0, 0, 112], // 0x90000070 + epoch: 50, + }, + bellatrix: Fork { + version: [144, 0, 0, 113], // 0x90000071 + epoch: 100, + }, + capella: Fork { + version: [144, 0, 0, 114], // 0x90000072 + epoch: 56832, + }, + deneb: Fork { + version: [144, 0, 0, 115], // 0x90000073 + epoch: 132608, + }, + }; +} + +impl snowbridge_pallet_ethereum_client::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ForkVersions = ChainForkVersions; + // Free consensus update every epoch. Works out to be 225 updates per day. + type FreeHeadersInterval = ConstU32; + type WeightInfo = crate::weights::snowbridge_pallet_ethereum_client::WeightInfo; +} + +impl snowbridge_pallet_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OutboundQueue = EthereumOutboundQueue; + type SiblingOrigin = EnsureXcm; + type AgentIdOf = snowbridge_core::AgentIdOf; + type TreasuryAccount = TreasuryAccount; + type Token = Balances; + type WeightInfo = crate::weights::snowbridge_pallet_system::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); + type DefaultPricingParameters = Parameters; + type InboundDeliveryCost = EthereumInboundQueue; +} + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmark_helpers { + use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; + use codec::Encode; + use snowbridge_beacon_primitives::BeaconHeader; + use snowbridge_pallet_inbound_queue::BenchmarkHelper; + use sp_core::H256; + use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}; + + impl BenchmarkHelper for Runtime { + fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) { + EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap(); + } + } + + pub struct DoNothingRouter; + impl SendXcm for DoNothingRouter { + type Ticket = Xcm<()>; + + fn validate( + _dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult { + Ok((xcm.clone().unwrap(), Assets::new())) + } + fn deliver(xcm: Xcm<()>) -> Result { + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + Ok(hash) + } + } + + impl snowbridge_pallet_system::BenchmarkHelper for () { + fn make_xcm_origin(location: Location) -> RuntimeOrigin { + RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) + } + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 100bff5a0705..14409ce4642d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -42,20 +42,13 @@ use bridge_runtime_common::extensions::{ CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use snowbridge_beacon_primitives::{Fork, ForkVersions}; -use snowbridge_core::{ - gwei, meth, - outbound::{Command, Fee}, - AgentId, AllowSiblingsOnly, PricingParameters, Rewards, -}; -use snowbridge_router_primitives::inbound::MessageToXcm; use sp_api::impl_runtime_apis; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{Block as BlockT, Keccak256}, + traits::Block as BlockT, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, + ApplyExtrinsicResult, }; #[cfg(feature = "std")] @@ -76,16 +69,13 @@ use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, }; -use testnet_parachains_constants::rococo::{ - consensus::*, currency::*, fee::WeightToFee, snowbridge::INBOUND_QUEUE_PALLET_INDEX, time::*, -}; +use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use bp_runtime::HeaderId; use bridge_hub_common::{ message_queue::{NarrowOriginToSibling, ParaIdToSibling}, AggregateMessageOrigin, }; -use pallet_xcm::EnsureXcm; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; use xcm::VersionedLocation; @@ -96,7 +86,11 @@ pub use sp_runtime::BuildStorage; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use rococo_runtime_constants::system_parachain::{ASSET_HUB_ID, BRIDGE_HUB_ID}; -use xcm::prelude::*; +use snowbridge_core::{ + outbound::{Command, Fee}, + AgentId, PricingParameters, +}; +use xcm::{latest::prelude::*, prelude::*}; use xcm_runtime_apis::{ dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, fees::Error as XcmPaymentApiError, @@ -111,8 +105,6 @@ use parachains_common::{ #[cfg(feature = "runtime-benchmarks")] use alloc::boxed::Box; -#[cfg(feature = "runtime-benchmarks")] -use benchmark_helpers::DoNothingRouter; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -542,174 +534,6 @@ impl pallet_utility::Config for Runtime { type WeightInfo = weights::pallet_utility::WeightInfo; } -// Ethereum Bridge -parameter_types! { - pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39")); -} - -parameter_types! { - pub const CreateAssetCall: [u8;2] = [53, 0]; - pub const CreateAssetDeposit: u128 = (UNITS / 10) + EXISTENTIAL_DEPOSIT; - pub Parameters: PricingParameters = PricingParameters { - exchange_rate: FixedU128::from_rational(1, 400), - fee_per_gas: gwei(20), - rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, - multiplier: FixedU128::from_rational(1, 1), - }; -} - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmark_helpers { - use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; - use codec::Encode; - use snowbridge_beacon_primitives::BeaconHeader; - use snowbridge_pallet_inbound_queue::BenchmarkHelper; - use sp_core::H256; - use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}; - - impl BenchmarkHelper for Runtime { - fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) { - EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap(); - } - } - - pub struct DoNothingRouter; - impl SendXcm for DoNothingRouter { - type Ticket = Xcm<()>; - - fn validate( - _dest: &mut Option, - xcm: &mut Option>, - ) -> SendResult { - Ok((xcm.clone().unwrap(), Assets::new())) - } - fn deliver(xcm: Xcm<()>) -> Result { - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - Ok(hash) - } - } - - impl snowbridge_pallet_system::BenchmarkHelper for () { - fn make_xcm_origin(location: Location) -> RuntimeOrigin { - RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) - } - } -} - -impl snowbridge_pallet_inbound_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Verifier = snowbridge_pallet_ethereum_client::Pallet; - type Token = Balances; - #[cfg(not(feature = "runtime-benchmarks"))] - type XcmSender = XcmRouter; - #[cfg(feature = "runtime-benchmarks")] - type XcmSender = DoNothingRouter; - type ChannelLookup = EthereumSystem; - type GatewayAddress = EthereumGatewayAddress; - #[cfg(feature = "runtime-benchmarks")] - type Helper = Runtime; - type MessageConverter = MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - ConstU8, - AccountId, - Balance, - >; - type WeightToFee = WeightToFee; - type LengthToFee = ConstantMultiplier; - type MaxMessageSize = ConstU32<2048>; - type WeightInfo = weights::snowbridge_pallet_inbound_queue::WeightInfo; - type PricingParameters = EthereumSystem; - type AssetTransactor = ::AssetTransactor; -} - -impl snowbridge_pallet_outbound_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Hashing = Keccak256; - type MessageQueue = MessageQueue; - type Decimals = ConstU8<12>; - type MaxMessagePayloadSize = ConstU32<2048>; - type MaxMessagesPerBlock = ConstU32<32>; - type GasMeter = snowbridge_core::outbound::ConstantGasMeter; - type Balance = Balance; - type WeightToFee = WeightToFee; - type WeightInfo = weights::snowbridge_pallet_outbound_queue::WeightInfo; - type PricingParameters = EthereumSystem; - type Channels = EthereumSystem; -} - -#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] -parameter_types! { - pub const ChainForkVersions: ForkVersions = ForkVersions { - genesis: Fork { - version: [0, 0, 0, 0], // 0x00000000 - epoch: 0, - }, - altair: Fork { - version: [1, 0, 0, 0], // 0x01000000 - epoch: 0, - }, - bellatrix: Fork { - version: [2, 0, 0, 0], // 0x02000000 - epoch: 0, - }, - capella: Fork { - version: [3, 0, 0, 0], // 0x03000000 - epoch: 0, - }, - deneb: Fork { - version: [4, 0, 0, 0], // 0x04000000 - epoch: 0, - } - }; -} - -#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] -parameter_types! { - pub const ChainForkVersions: ForkVersions = ForkVersions { - genesis: Fork { - version: [144, 0, 0, 111], // 0x90000069 - epoch: 0, - }, - altair: Fork { - version: [144, 0, 0, 112], // 0x90000070 - epoch: 50, - }, - bellatrix: Fork { - version: [144, 0, 0, 113], // 0x90000071 - epoch: 100, - }, - capella: Fork { - version: [144, 0, 0, 114], // 0x90000072 - epoch: 56832, - }, - deneb: Fork { - version: [144, 0, 0, 115], // 0x90000073 - epoch: 132608, - }, - }; -} - -impl snowbridge_pallet_ethereum_client::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ForkVersions = ChainForkVersions; - type WeightInfo = weights::snowbridge_pallet_ethereum_client::WeightInfo; -} - -impl snowbridge_pallet_system::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OutboundQueue = EthereumOutboundQueue; - type SiblingOrigin = EnsureXcm; - type AgentIdOf = snowbridge_core::AgentIdOf; - type TreasuryAccount = TreasuryAccount; - type Token = Balances; - type WeightInfo = weights::snowbridge_pallet_system::WeightInfo; - #[cfg(feature = "runtime-benchmarks")] - type Helper = (); - type DefaultPricingParameters = Parameters; - type InboundDeliveryCost = EthereumInboundQueue; -} - // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 0daf90872189..982c9fec6634 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -18,11 +18,13 @@ use bp_polkadot_core::Signature; use bridge_hub_rococo_runtime::{ - bridge_common_config, bridge_to_bulletin_config, bridge_to_westend_config, + bridge_common_config, bridge_to_bulletin_config, + bridge_to_ethereum_config::EthereumGatewayAddress, + bridge_to_westend_config, xcm_config::{LocationToAccountId, RelayNetwork, TokenLocation, XcmConfig}, - AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, EthereumGatewayAddress, - Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, SessionKeys, SignedExtra, TransactionPayment, UncheckedExtrinsic, + AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit, + ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, + SignedExtra, TransactionPayment, UncheckedExtrinsic, }; use bridge_hub_test_utils::SlotDurations; use codec::{Decode, Encode}; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 7922d3ed02b1..47b6006ed6c1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -42,6 +42,8 @@ use sp_runtime::{ FixedU128, }; +pub const SLOTS_PER_EPOCH: u32 = snowbridge_pallet_ethereum_client::config::SLOTS_PER_EPOCH as u32; + /// Exports message to the Ethereum Gateway contract. pub type SnowbridgeExporter = EthereumBlobExporter< UniversalLocation, @@ -163,6 +165,7 @@ parameter_types! { impl snowbridge_pallet_ethereum_client::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ForkVersions = ChainForkVersions; + type FreeHeadersInterval = ConstU32; type WeightInfo = crate::weights::snowbridge_pallet_ethereum_client::WeightInfo; } diff --git a/prdoc/pr_5201.prdoc b/prdoc/pr_5201.prdoc new file mode 100644 index 000000000000..a0c1bbfd2e41 --- /dev/null +++ b/prdoc/pr_5201.prdoc @@ -0,0 +1,23 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Snowbridge free consensus updates + +doc: + - audience: Runtime Dev + description: | + Allow free consensus updates to the Snowbridge Ethereum client if the headers are more than a certain + number of headers apart. Relayers providing valid consensus updates are refunded for updates. Bridge + users are not affected. + +crates: + - name: snowbridge-pallet-ethereum-client + bump: patch + - name: snowbridge-pallet-inbound-queue + bump: patch + - name: snowbridge-runtime-test-common + bump: patch + - name: bridge-hub-rococo-runtime + bump: major + - name: bridge-hub-westend-runtime + bump: major From f6eeca91b6cf0810ca3d8e7ea23988d9510851ba Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Mon, 2 Sep 2024 21:03:02 +0100 Subject: [PATCH 224/382] [FRAME] MQ processor should be transactional (#5198) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes https://github.com/paritytech/polkadot-sdk/issues/2441 Polkadot address: 12GyGD3QhT4i2JJpNzvMf96sxxBLWymz4RdGCxRH5Rj5agKW --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Bastian Köcher --- prdoc/pr_5198.prdoc | 13 +++ .../message-queue/src/integration_test.rs | 1 + substrate/frame/message-queue/src/lib.rs | 26 +++++- substrate/frame/message-queue/src/mock.rs | 15 ++- substrate/frame/message-queue/src/tests.rs | 93 +++++++++++++++---- 5 files changed, 126 insertions(+), 22 deletions(-) create mode 100644 prdoc/pr_5198.prdoc diff --git a/prdoc/pr_5198.prdoc b/prdoc/pr_5198.prdoc new file mode 100644 index 000000000000..417b0b5a4fd9 --- /dev/null +++ b/prdoc/pr_5198.prdoc @@ -0,0 +1,13 @@ +title: "MQ processor should be transactional" + +doc: + - audience: [Runtime User, Runtime Dev] + description: | + Enforce transactional processing on pallet Message Queue Processor. + + Storage changes that were done while processing a message will now be rolled back + when the processing returns an error. `Ok(false)` will not revert, only `Err(_)`. + +crates: + - name: pallet-message-queue + bump: major \ No newline at end of file diff --git a/substrate/frame/message-queue/src/integration_test.rs b/substrate/frame/message-queue/src/integration_test.rs index 14b8d2217eb2..e4db87d8be7a 100644 --- a/substrate/frame/message-queue/src/integration_test.rs +++ b/substrate/frame/message-queue/src/integration_test.rs @@ -151,6 +151,7 @@ fn stress_test_recursive() { TotalEnqueued::set(TotalEnqueued::get() + enqueued); Enqueued::set(Enqueued::get() + enqueued); Called::set(Called::get() + 1); + Ok(()) })); build_and_execute::(|| { diff --git a/substrate/frame/message-queue/src/lib.rs b/substrate/frame/message-queue/src/lib.rs index 2dbffef7e5a2..48002acb1474 100644 --- a/substrate/frame/message-queue/src/lib.rs +++ b/substrate/frame/message-queue/src/lib.rs @@ -225,7 +225,7 @@ use sp_arithmetic::traits::{BaseArithmetic, Unsigned}; use sp_core::{defer, H256}; use sp_runtime::{ traits::{One, Zero}, - SaturatedConversion, Saturating, + SaturatedConversion, Saturating, TransactionOutcome, }; use sp_weights::WeightMeter; pub use weights::WeightInfo; @@ -1435,6 +1435,8 @@ impl Pallet { /// The base weight of this function needs to be accounted for by the caller. `weight` is the /// remaining weight to process the message. `overweight_limit` is the maximum weight that a /// message can ever consume. Messages above this limit are marked as permanently overweight. + /// This process is also transactional, any form of error that occurs in processing a message + /// causes storage changes to be rolled back. fn process_message_payload( origin: MessageOriginOf, page_index: PageIndex, @@ -1447,7 +1449,27 @@ impl Pallet { use ProcessMessageError::*; let prev_consumed = meter.consumed(); - match T::MessageProcessor::process_message(message, origin.clone(), meter, &mut id) { + let transaction = + storage::with_transaction(|| -> TransactionOutcome> { + let res = + T::MessageProcessor::process_message(message, origin.clone(), meter, &mut id); + match &res { + Ok(_) => TransactionOutcome::Commit(Ok(res)), + Err(_) => TransactionOutcome::Rollback(Ok(res)), + } + }); + + let transaction = match transaction { + Ok(result) => result, + _ => { + defensive!( + "Error occurred processing message, storage changes will be rolled back" + ); + return MessageExecutionStatus::Unprocessable { permanent: true } + }, + }; + + match transaction { Err(Overweight(w)) if w.any_gt(overweight_limit) => { // Permanently overweight. Self::deposit_event(Event::::OverweightEnqueued { diff --git a/substrate/frame/message-queue/src/mock.rs b/substrate/frame/message-queue/src/mock.rs index 26533cc7c330..d3f719c62356 100644 --- a/substrate/frame/message-queue/src/mock.rs +++ b/substrate/frame/message-queue/src/mock.rs @@ -184,8 +184,15 @@ impl ProcessMessage for RecordingMessageProcessor { if meter.try_consume(required).is_ok() { if let Some(p) = message.strip_prefix(&b"callback="[..]) { let s = String::from_utf8(p.to_vec()).expect("Need valid UTF8"); - Callback::get()(&origin, s.parse().expect("Expected an u32")); + if let Err(()) = Callback::get()(&origin, s.parse().expect("Expected an u32")) { + return Err(ProcessMessageError::Corrupt) + } + + if s.contains("000") { + return Ok(false) + } } + let mut m = MessagesProcessed::get(); m.push((message.to_vec(), origin)); MessagesProcessed::set(m); @@ -197,7 +204,7 @@ impl ProcessMessage for RecordingMessageProcessor { } parameter_types! { - pub static Callback: Box = Box::new(|_, _| {}); + pub static Callback: Box Result<(), ()>> = Box::new(|_, _| { Ok(()) }); pub static IgnoreStackOvError: bool = false; } @@ -252,7 +259,9 @@ impl ProcessMessage for CountingMessageProcessor { if meter.try_consume(required).is_ok() { if let Some(p) = message.strip_prefix(&b"callback="[..]) { let s = String::from_utf8(p.to_vec()).expect("Need valid UTF8"); - Callback::get()(&origin, s.parse().expect("Expected an u32")); + if let Err(()) = Callback::get()(&origin, s.parse().expect("Expected an u32")) { + return Err(ProcessMessageError::Corrupt) + } } NumMessagesProcessed::set(NumMessagesProcessed::get() + 1); Ok(true) diff --git a/substrate/frame/message-queue/src/tests.rs b/substrate/frame/message-queue/src/tests.rs index e89fdb8b3208..fac135f135ce 100644 --- a/substrate/frame/message-queue/src/tests.rs +++ b/substrate/frame/message-queue/src/tests.rs @@ -1675,6 +1675,7 @@ fn regression_issue_2319() { build_and_execute::(|| { Callback::set(Box::new(|_, _| { MessageQueue::enqueue_message(mock_helpers::msg("anothermessage"), There); + Ok(()) })); use MessageOrigin::*; @@ -1695,23 +1696,26 @@ fn regression_issue_2319() { #[test] fn recursive_enqueue_works() { build_and_execute::(|| { - Callback::set(Box::new(|o, i| match i { - 0 => { - MessageQueue::enqueue_message(msg(&format!("callback={}", 1)), *o); - }, - 1 => { - for _ in 0..100 { - MessageQueue::enqueue_message(msg(&format!("callback={}", 2)), *o); - } - for i in 0..100 { - MessageQueue::enqueue_message(msg(&format!("callback={}", 3)), i.into()); - } - }, - 2 | 3 => { - MessageQueue::enqueue_message(msg(&format!("callback={}", 4)), *o); - }, - 4 => (), - _ => unreachable!(), + Callback::set(Box::new(|o, i| { + match i { + 0 => { + MessageQueue::enqueue_message(msg(&format!("callback={}", 1)), *o); + }, + 1 => { + for _ in 0..100 { + MessageQueue::enqueue_message(msg(&format!("callback={}", 2)), *o); + } + for i in 0..100 { + MessageQueue::enqueue_message(msg(&format!("callback={}", 3)), i.into()); + } + }, + 2 | 3 => { + MessageQueue::enqueue_message(msg(&format!("callback={}", 4)), *o); + }, + 4 => (), + _ => unreachable!(), + }; + Ok(()) })); MessageQueue::enqueue_message(msg("callback=0"), MessageOrigin::Here); @@ -1735,6 +1739,7 @@ fn recursive_service_is_forbidden() { // This call will fail since it is recursive. But it will not mess up the state. assert_storage_noop!(MessageQueue::service_queues(10.into_weight())); MessageQueue::enqueue_message(msg("m2"), There); + Ok(()) })); for _ in 0..5 { @@ -1778,6 +1783,7 @@ fn recursive_overweight_while_service_is_forbidden() { ), ExecuteOverweightError::RecursiveDisallowed ); + Ok(()) })); MessageQueue::enqueue_message(msg("weight=10"), There); @@ -1800,6 +1806,7 @@ fn recursive_reap_page_is_forbidden() { Callback::set(Box::new(|_, _| { // This call will fail since it is recursive. But it will not mess up the state. assert_noop!(MessageQueue::do_reap_page(&Here, 0), Error::::RecursiveDisallowed); + Ok(()) })); // Create 10 pages more than the stale limit. @@ -1975,3 +1982,55 @@ fn execute_overweight_keeps_stack_ov_message() { System::reset_events(); }); } + +#[test] +fn process_message_error_reverts_storage_changes() { + build_and_execute::(|| { + assert!(!sp_io::storage::exists(b"key"), "Key should not exist"); + + Callback::set(Box::new(|_, _| { + sp_io::storage::set(b"key", b"value"); + Err(()) + })); + + MessageQueue::enqueue_message(msg("callback=0"), MessageOrigin::Here); + MessageQueue::service_queues(10.into_weight()); + + assert!(!sp_io::storage::exists(b"key"), "Key should have been rolled back"); + }); +} + +#[test] +fn process_message_ok_false_keeps_storage_changes() { + build_and_execute::(|| { + assert!(!sp_io::storage::exists(b"key"), "Key should not exist"); + + Callback::set(Box::new(|_, _| { + sp_io::storage::set(b"key", b"value"); + Ok(()) + })); + + // 000 will make it return `Ok(false)` + MessageQueue::enqueue_message(msg("callback=000"), MessageOrigin::Here); + MessageQueue::service_queues(10.into_weight()); + + assert_eq!(sp_io::storage::exists(b"key"), true); + }); +} + +#[test] +fn process_message_ok_true_keeps_storage_changes() { + build_and_execute::(|| { + assert!(!sp_io::storage::exists(b"key"), "Key should not exist"); + + Callback::set(Box::new(|_, _| { + sp_io::storage::set(b"key", b"value"); + Ok(()) + })); + + MessageQueue::enqueue_message(msg("callback=0"), MessageOrigin::Here); + MessageQueue::service_queues(10.into_weight()); + + assert_eq!(sp_io::storage::exists(b"key"), true); + }); +} From db71d274265f2562af6e098b813936b5be569721 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 02:09:45 +0800 Subject: [PATCH 225/382] Fix for compatibility --- bridges/snowbridge/primitives/router/src/outbound/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index 1ad8be72bcbc..ee86feaeabc7 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -11,7 +11,7 @@ use codec::{Decode, Encode}; use frame_support::{ensure, traits::Get}; use snowbridge_core::{ - outbound::{Command, Message, SendMessage}, + outbound::{AgentExecuteCommand, Command, Message, SendMessage}, AgentId, ChannelId, ParaId, TokenId, TokenIdOf, }; use sp_core::{H160, H256}; @@ -294,7 +294,10 @@ where let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; Ok(( - Command::TransferNativeToken { agent_id: self.agent_id, token, recipient, amount }, + Command::AgentExecute { + agent_id: self.agent_id, + command: AgentExecuteCommand::TransferToken { token, recipient, amount }, + }, *topic_id, )) } From 07f8e82d307c9159d082662dbd4cd7c855983f34 Mon Sep 17 00:00:00 2001 From: Vincent Geddes <117534+vgeddes@users.noreply.github.com> Date: Tue, 3 Sep 2024 01:21:55 +0200 Subject: [PATCH 226/382] PNA system pallet cleanups (#173) --- bridges/snowbridge/pallets/system/src/lib.rs | 45 +++++++++++-------- bridges/snowbridge/primitives/core/src/lib.rs | 2 +- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 30aba4588ba5..46a94782666c 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -35,8 +35,15 @@ //! //! Typically, Polkadot governance will use the `force_transfer_native_from_agent` and //! `force_update_channel` and extrinsics to manage agents and channels for system parachains. +//! +//! ## Polkadot-native tokens on Ethereum +//! +//! Tokens deposited on AssetHub pallet can be bridged to Ethereum as wrapped ERC20 tokens. As a prerequisite, +//! the token should be registered first. +//! +//! * [`Call:register_token`]: Register a token location as a wrapped ERC20 contract on Ethereum. +//! #![cfg_attr(not(feature = "std"), no_std)] - #[cfg(test)] mod mock; @@ -214,7 +221,7 @@ pub mod pallet { PricingParametersChanged { params: PricingParametersOf, }, - /// Register token + /// Register Polkadot-native token as a wrapped ERC20 token on Ethereum RegisterToken { asset_id: VersionedLocation, token_id: H256, @@ -252,14 +259,16 @@ pub mod pallet { pub type PricingParameters = StorageValue<_, PricingParametersOf, ValueQuery, T::DefaultPricingParameters>; + /// Lookup table for foreign to native token ID #[pallet::storage] #[pallet::getter(fn tokens)] - pub type Tokens = + pub type ForeignToNativeId = StorageMap<_, Twox64Concat, TokenId, xcm::v4::Location, OptionQuery>; + /// Lookup table for native to foreign token ID #[pallet::storage] #[pallet::getter(fn location_tokens)] - pub type LocationToToken = + pub type NativeToForeignId = StorageMap<_, Twox64Concat, xcm::v4::Location, TokenId, OptionQuery>; #[pallet::genesis_config] @@ -594,10 +603,9 @@ pub mod pallet { Ok(()) } - /// Sends a message to the Gateway contract to register a new - /// token that represents `asset`. + /// Registers a Polkadot-native token as a wrapped ERC20 token on Ethereum. /// - /// - `origin`: Must be `MultiLocation` from sibling parachain + /// - `origin`: Must be root #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::register_token())] pub fn register_token( @@ -607,12 +615,12 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; - let asset_loc: Location = + let location: Location = (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; let pays_fee = PaysFee::::No; - Self::do_register_token(asset_loc, metadata, pays_fee)?; + Self::do_register_token(location, metadata, pays_fee)?; Ok(()) } @@ -707,15 +715,16 @@ pub mod pallet { } pub(crate) fn do_register_token( - asset_loc: Location, + location: Location, metadata: AssetMetadata, pays_fee: PaysFee, ) -> Result<(), DispatchError> { // Record the token id or fail if it has already been created - let token_id = TokenIdOf::convert_location(&asset_loc) + let token_id = TokenIdOf::convert_location(&location) .ok_or(Error::::LocationConversionFailed)?; - Tokens::::insert(token_id, asset_loc.clone()); - LocationToToken::::insert(asset_loc.clone(), token_id); + + ForeignToNativeId::::insert(token_id, location.clone()); + NativeToForeignId::::insert(location.clone(), token_id); let command = Command::RegisterForeignToken { token_id, @@ -725,7 +734,7 @@ pub mod pallet { }; Self::send(SECONDARY_GOVERNANCE_CHANNEL, command, pays_fee)?; - Self::deposit_event(Event::::RegisterToken { asset_id: asset_loc.into(), token_id }); + Self::deposit_event(Event::::RegisterToken { asset_id: location.into(), token_id }); Ok(()) } @@ -752,11 +761,11 @@ pub mod pallet { } impl MaybeEquivalence for Pallet { - fn convert(id: &TokenId) -> Option { - Tokens::::get(id) + fn convert(foreign_id: &TokenId) -> Option { + ForeignToNativeId::::get(id) } - fn convert_back(loc: &Location) -> Option { - LocationToToken::::get(loc) + fn convert_back(location: &Location) -> Option { + NativeToForeignId::::get(location) } } } diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index b4e9c9b3c857..0afba79fd765 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -168,7 +168,7 @@ impl DescribeLocation for DescribeHere { pub type AgentIdOf = HashedDescription)>; -#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct AssetMetadata { pub name: BoundedVec>, pub symbol: BoundedVec>, From 7566c01e46480fc91096590a91df6b43c0bb56a5 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 08:20:22 +0800 Subject: [PATCH 227/382] Fix building error --- bridges/snowbridge/pallets/system/src/lib.rs | 7 +++---- bridges/snowbridge/primitives/core/src/lib.rs | 2 +- .../bridge-hub-rococo/src/bridge_to_ethereum_config.rs | 2 ++ .../runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 4 ---- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 46a94782666c..0814a0e35f0c 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -38,11 +38,10 @@ //! //! ## Polkadot-native tokens on Ethereum //! -//! Tokens deposited on AssetHub pallet can be bridged to Ethereum as wrapped ERC20 tokens. As a prerequisite, -//! the token should be registered first. +//! Tokens deposited on AssetHub pallet can be bridged to Ethereum as wrapped ERC20 tokens. As a +//! prerequisite, the token should be registered first. //! //! * [`Call:register_token`]: Register a token location as a wrapped ERC20 contract on Ethereum. -//! #![cfg_attr(not(feature = "std"), no_std)] #[cfg(test)] mod mock; @@ -762,7 +761,7 @@ pub mod pallet { impl MaybeEquivalence for Pallet { fn convert(foreign_id: &TokenId) -> Option { - ForeignToNativeId::::get(id) + ForeignToNativeId::::get(foreign_id) } fn convert_back(location: &Location) -> Option { NativeToForeignId::::get(location) diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index 0afba79fd765..f2e4f9131b50 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -168,7 +168,7 @@ impl DescribeLocation for DescribeHere { pub type AgentIdOf = HashedDescription)>; -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct AssetMetadata { pub name: BoundedVec>, pub symbol: BoundedVec>, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 4e585c99b89e..26857d262c20 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -166,6 +166,8 @@ parameter_types! { }; } +pub const SLOTS_PER_EPOCH: u32 = snowbridge_pallet_ethereum_client::config::SLOTS_PER_EPOCH as u32; + impl snowbridge_pallet_ethereum_client::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ForkVersions = ChainForkVersions; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index b28113e76b3d..14409ce4642d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -42,10 +42,6 @@ use bridge_runtime_common::extensions::{ CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use snowbridge_core::{ - outbound::{Command, Fee}, - AgentId, PricingParameters, -}; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ From ae0f67468d8b55fc0eb623ceaa3b99426b237b67 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 08:37:47 +0800 Subject: [PATCH 228/382] Fix test --- .../bridge-hub-westend/src/tests/snowbridge.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index e0dd2a42c340..996ab2657329 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -46,12 +46,6 @@ pub enum ControlCall { CreateAgent, #[codec(index = 4)] CreateChannel { mode: OperatingMode }, - #[codec(index = 11)] - ForceRegisterToken { - location: Box, - asset: Box, - metadata: AssetMetadata, - }, } #[allow(clippy::large_enum_variant)] @@ -473,7 +467,7 @@ fn transfer_ah_token() { BridgeHubWestend::execute_with(|| { type Runtime = ::Runtime; - snowbridge_pallet_system::Tokens::::insert( + snowbridge_pallet_system::ForeignToNativeId::::insert( token_id, asset_id_after_reanchored.clone(), ); @@ -643,7 +637,7 @@ fn transfer_penpal_native_token() { BridgeHubWestend::execute_with(|| { type Runtime = ::Runtime; - snowbridge_pallet_system::Tokens::::insert( + snowbridge_pallet_system::ForeignToNativeId::::insert( token_id, penpal_asset_location_after_reanchored.clone(), ); @@ -813,7 +807,7 @@ fn transfer_penpal_asset() { BridgeHubWestend::execute_with(|| { type Runtime = ::Runtime; - snowbridge_pallet_system::Tokens::::insert( + snowbridge_pallet_system::ForeignToNativeId::::insert( token_id, penpal_asset_location_after_reanchored.clone(), ); From 26263acee7bb734ec80538bffc67a69d906f6c3f Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 08:49:38 +0800 Subject: [PATCH 229/382] Fix test --- .../primitives/router/src/outbound/tests.rs | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index 75dce864493a..6e4fd5946340 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -424,11 +424,13 @@ fn xcm_converter_convert_success() { .into(); let mut converter = XcmConverter::::new(&message, network, Default::default()); - let expected_payload = Command::TransferNativeToken { + let expected_payload = Command::AgentExecute { agent_id: Default::default(), - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -459,11 +461,13 @@ fn xcm_converter_convert_without_buy_execution_yields_success() { .into(); let mut converter = XcmConverter::::new(&message, network, Default::default()); - let expected_payload = Command::TransferNativeToken { + let expected_payload = Command::AgentExecute { agent_id: Default::default(), - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -496,11 +500,13 @@ fn xcm_converter_convert_with_wildcard_all_asset_filter_succeeds() { .into(); let mut converter = XcmConverter::::new(&message, network, Default::default()); - let expected_payload = Command::TransferNativeToken { + let expected_payload = Command::AgentExecute { agent_id: Default::default(), - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -533,11 +539,13 @@ fn xcm_converter_convert_with_fees_less_than_reserve_yields_success() { .into(); let mut converter = XcmConverter::::new(&message, network, Default::default()); - let expected_payload = Command::TransferNativeToken { + let expected_payload = Command::AgentExecute { agent_id: Default::default(), - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); From 7ee483249a8986acefbc2b69ee8862ebd7120dc8 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 10:04:05 +0800 Subject: [PATCH 230/382] Fix doc --- bridges/snowbridge/pallets/system/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 0814a0e35f0c..145c8c612909 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -41,7 +41,8 @@ //! Tokens deposited on AssetHub pallet can be bridged to Ethereum as wrapped ERC20 tokens. As a //! prerequisite, the token should be registered first. //! -//! * [`Call:register_token`]: Register a token location as a wrapped ERC20 contract on Ethereum. +//! * [`Call::register_token`]: Register a token location as a wrapped ERC20 contract on Ethereum. + #![cfg_attr(not(feature = "std"), no_std)] #[cfg(test)] mod mock; @@ -603,8 +604,13 @@ pub mod pallet { } /// Registers a Polkadot-native token as a wrapped ERC20 token on Ethereum. + /// Privileged. Can only be called by root. + /// + /// Fee required: No /// /// - `origin`: Must be root + /// - `location`: Location of the asset + /// - `metadata`: Metadata(name,symbol,decimals) of the asset #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::register_token())] pub fn register_token( From 46cb3528cd8cd1394af2335a6907d7ab8647717a Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 10:20:23 +0800 Subject: [PATCH 231/382] SetAppendix with fee not trapped --- .../snowbridge/primitives/router/src/inbound/mod.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index fd3f71390bbe..d8292488f14f 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -433,15 +433,16 @@ impl< let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); instructions.extend(vec![ + // `SetAppendix` ensures that `fees` are not trapped in any case + SetAppendix(Xcm(vec![DepositAsset { + assets: AllCounted(1).into(), + beneficiary: Location::new(1, [Parachain(dest_para_id)]), + }])), // Perform a reserve withdraw to send to destination chain. Leave half of the // asset_hub_fee for the delivery cost InitiateReserveWithdraw { assets: Definite( - vec![ - asset.clone(), - (Location::parent(), dest_para_fee + asset_hub_fee / 2).into(), - ] - .into(), + vec![asset.clone(), (Location::parent(), dest_para_fee).into()].into(), ), reserve: Location::new(1, [Parachain(dest_para_id)]), xcm: vec![ From 3c71db3efb7ed62dc0db950d3f090686803b10c5 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Tue, 3 Sep 2024 09:27:30 +0100 Subject: [PATCH 232/382] Remove noise from the template list (#5437) Co-authored-by: Javier Viola <363911+pepoviola@users.noreply.github.com> Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Co-authored-by: command-bot <> --- docs/sdk/src/polkadot_sdk/templates.rs | 44 ++++++++++---------------- docs/sdk/src/reference_docs/cli.rs | 2 +- 2 files changed, 17 insertions(+), 29 deletions(-) diff --git a/docs/sdk/src/polkadot_sdk/templates.rs b/docs/sdk/src/polkadot_sdk/templates.rs index 03a7aa98198f..ab742ad5c3a2 100644 --- a/docs/sdk/src/polkadot_sdk/templates.rs +++ b/docs/sdk/src/polkadot_sdk/templates.rs @@ -7,34 +7,22 @@ //! //! ## Internal //! -//! The following [templates](https://github.com/paritytech/polkadot-sdk/blob/master/templates) are -//! maintained as a part of the `polkadot-sdk` repository: -//! -//! - `minimal_template_node`/[`minimal_template_runtime`]: A minimal template that contains the -//! least amount of features to be a functioning blockchain. Suitable for learning, development -//! and testing. This template is not meant to be used in production. -//! - `solochain_template_node`/[`solochain_template_runtime`]: Formerly known as -//! "substrate-node-template", is a white-labeled substrate-based blockchain (aka. solochain) that -//! contains moderate features, such as a basic consensus engine and some FRAME pallets. This -//! template can act as a good starting point for those who want to launch a solochain. -//! - `parachain_template_node`/[`parachain_template_runtime`]: A parachain template ready to be -//! connected to a test relay-chain. -//! -//! These templates are always kept up to date, and are mirrored to external repositories for easy -//! forking: -//! -//! - -//! - -//! - -//! -//! ## External Templates -//! -//! Noteworthy templates outside of this repository. -//! -//! - [`extended-parachain-template`](https://github.com/paritytech/extended-parachain-template): A -//! parachain template that contains more built-in functionality such as assets and NFTs. -//! - [`frontier-parachain-template`](https://github.com/paritytech/frontier-parachain-template): A -//! parachain template for launching EVM-compatible parachains. +//! The following templates are maintained as a part of the `polkadot-sdk` repository: +//! +//! - [`minimal-template`](https://github.com/paritytech/polkadot-sdk-minimal-template): A minimal +//! template that contains the least amount of features to be a functioning blockchain. Suitable +//! for learning and testing. +//! - [`solochain-template`](https://github.com/paritytech/polkadot-sdk-solochain-template): +//! Formerly known as "substrate-node-template", is a white-labeled substrate-based blockchain +//! (aka. solochain) that contains moderate features, such as a basic consensus engine and some +//! FRAME pallets. This template can act as a good starting point for those who want to launch a +//! solochain. +//! - [`parachain-template`](https://github.com/paritytech/polkadot-sdk-parachain-template): +//! A parachain template ready to be connected to a relay-chain, such as [Paseo](https://github.com/paseo-network/.github) +//! , Kusama or Polkadot. +//! +//! Note that these templates are mirrored automatically from [this](https://github.com/paritytech/polkadot-sdk/blob/master/templates) +//! directory of polkadot-sdk, therefore any changes to them should be made as a PR to this repo. //! //! ## OpenZeppelin //! diff --git a/docs/sdk/src/reference_docs/cli.rs b/docs/sdk/src/reference_docs/cli.rs index b9cdbd60e959..6f393f267b0d 100644 --- a/docs/sdk/src/reference_docs/cli.rs +++ b/docs/sdk/src/reference_docs/cli.rs @@ -1,7 +1,7 @@ //! # Substrate CLI //! //! Let's see some examples of typical CLI arguments used when setting up and running a -//! Substrate-based blockchain. We use the [`substrate-node-template`](https://github.com/substrate-developer-hub/substrate-node-template) +//! Substrate-based blockchain. We use the [`solochain-template`](https://github.com/paritytech/polkadot-sdk-solochain-template) //! on these examples. //! //! #### Checking the available CLI arguments From 020cda338b0c3a5c46d488d4bbf8efc339f05d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Tue, 3 Sep 2024 12:26:00 +0200 Subject: [PATCH 233/382] revive: Make `salt` salt optional to allow for CREATE1 semantics (#5556) Before we only supported CREATE2 semantics for contract address derivations. In order to be compatible we also want to allow CREATE1 semantics. We accomplish this to make the salt an `Option` in all places where it is used. Supplying `None` will use CREATE1 semantics by just using the deployers account nonce. ## Todo - [x] Add new tests specific for CREATE1 --- prdoc/pr_5556.prdoc | 11 ++++ substrate/bin/node/runtime/src/lib.rs | 2 +- substrate/frame/revive/src/address.rs | 1 - .../frame/revive/src/benchmarking/mod.rs | 10 ++- substrate/frame/revive/src/exec.rs | 45 +++++++------ substrate/frame/revive/src/lib.rs | 24 +++---- .../frame/revive/src/test_utils/builder.rs | 12 ++-- substrate/frame/revive/src/tests.rs | 63 +++++++++++++++---- .../frame/revive/src/tests/test_debug.rs | 4 +- substrate/frame/revive/src/wasm/runtime.rs | 32 ++++++---- 10 files changed, 133 insertions(+), 71 deletions(-) create mode 100644 prdoc/pr_5556.prdoc diff --git a/prdoc/pr_5556.prdoc b/prdoc/pr_5556.prdoc new file mode 100644 index 000000000000..4865ec1e338d --- /dev/null +++ b/prdoc/pr_5556.prdoc @@ -0,0 +1,11 @@ +title: Make salt optional + +doc: + - audience: Runtime Dev + description: | + By making salt optional we allow clients to use CREATE1 semantics + when deploying a new contract. + +crates: + - name: pallet-revive + bump: major diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 7ef0779dd9b8..31584427b3b6 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -3017,7 +3017,7 @@ impl_runtime_apis! { storage_deposit_limit: Option, code: pallet_revive::Code, data: Vec, - salt: [u8; 32], + salt: Option<[u8; 32]>, ) -> pallet_revive::ContractInstantiateResult { Revive::bare_instantiate( diff --git a/substrate/frame/revive/src/address.rs b/substrate/frame/revive/src/address.rs index f1bd36dcbba8..c51940ba771e 100644 --- a/substrate/frame/revive/src/address.rs +++ b/substrate/frame/revive/src/address.rs @@ -76,7 +76,6 @@ impl AddressMapper for DefaultAddressMapper { } /// Determine the address of a contract using CREATE semantics. -#[allow(dead_code)] pub fn create1(deployer: &H160, nonce: u64) -> H160 { let mut list = rlp::RlpStream::new_list(2); list.append(&deployer.as_bytes()); diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index 409d53b8a062..3ffd53e3561c 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -94,7 +94,7 @@ where data: Vec, ) -> Result, &'static str> { T::Currency::set_balance(&caller, caller_funding::()); - let salt = [0xffu8; 32]; + let salt = Some([0xffu8; 32]); let outcome = Contracts::::bare_instantiate( RawOrigin::Signed(caller.clone()).into(), @@ -344,7 +344,6 @@ mod benchmarks { // `c`: Size of the code in bytes. // `i`: Size of the input in bytes. - // `s`: Size of the salt in bytes. #[benchmark(pov_mode = Measured)] fn instantiate_with_code( c: Linear<0, { T::MaxCodeLen::get() }>, @@ -362,7 +361,7 @@ mod benchmarks { let account_id = T::AddressMapper::to_account_id_contract(&addr); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] - _(origin, value, Weight::MAX, storage_deposit, code, input, salt); + _(origin, value, Weight::MAX, storage_deposit, code, input, Some(salt)); let deposit = T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id); @@ -378,7 +377,7 @@ mod benchmarks { } // `i`: Size of the input in bytes. - // `s`: Size of the salt in bytes. + // `s`: Size of e salt in bytes. #[benchmark(pov_mode = Measured)] fn instantiate(i: Linear<0, { limits::MEMORY_BYTES }>) -> Result<(), BenchmarkError> { let input = vec![42u8; i as usize]; @@ -403,7 +402,7 @@ mod benchmarks { storage_deposit, hash, input, - salt, + Some(salt), ); let deposit = @@ -1529,7 +1528,6 @@ mod benchmarks { // t: value to transfer // i: size of input in bytes - // s: size of salt in bytes #[benchmark(pov_mode = Measured)] fn seal_instantiate(i: Linear<0, { limits::MEMORY_BYTES }>) -> Result<(), BenchmarkError> { let code = WasmModule::dummy(); diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 54019a6ba992..649479f7790f 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -54,7 +54,7 @@ use sp_core::{ use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256}; use sp_runtime::{ traits::{BadOrigin, Convert, Dispatchable, Zero}, - DispatchError, + DispatchError, SaturatedConversion, }; pub type AccountIdOf = ::AccountId; @@ -213,7 +213,7 @@ pub trait Ext: sealing::Sealed { code: H256, value: BalanceOf, input_data: Vec, - salt: &[u8; 32], + salt: Option<&[u8; 32]>, ) -> Result<(H160, ExecReturnValue), ExecError>; /// Transfer all funds to `beneficiary` and delete the contract. @@ -573,7 +573,7 @@ enum FrameArgs<'a, T: Config, E> { /// The executable whose `deploy` function is run. executable: E, /// A salt used in the contract address derivation of the new contract. - salt: &'a [u8; 32], + salt: Option<&'a [u8; 32]>, /// The input data is used in the contract address derivation of the new contract. input_data: &'a [u8], }, @@ -750,7 +750,7 @@ where storage_meter: &'a mut storage::meter::Meter, value: BalanceOf, input_data: Vec, - salt: &[u8; 32], + salt: Option<&[u8; 32]>, debug_message: Option<&'a mut DebugBuffer>, ) -> Result<(H160, ExecReturnValue), ExecError> { let (mut stack, executable) = Self::new( @@ -863,7 +863,12 @@ where }, FrameArgs::Instantiate { sender, executable, salt, input_data } => { let deployer = T::AddressMapper::to_address(&sender); - let address = address::create2(&deployer, executable.code(), input_data, salt); + let account_nonce = >::account_nonce(&sender); + let address = if let Some(salt) = salt { + address::create2(&deployer, executable.code(), input_data, salt) + } else { + address::create1(&deployer, account_nonce.saturated_into()) + }; let contract = ContractInfo::new( &address, >::account_nonce(&sender), @@ -1321,7 +1326,7 @@ where code_hash: H256, value: BalanceOf, input_data: Vec, - salt: &[u8; 32], + salt: Option<&[u8; 32]>, ) -> Result<(H160, ExecReturnValue), ExecError> { let executable = E::from_storage(code_hash, self.gas_meter_mut())?; let sender = &self.top_frame().account_id; @@ -2088,7 +2093,7 @@ mod tests { &mut storage_meter, min_balance, vec![1, 2, 3, 4], - &[0; 32], + Some(&[0; 32]), None, ); assert_matches!(result, Ok(_)); @@ -2497,7 +2502,7 @@ mod tests { &mut storage_meter, 0, // <- zero value vec![], - &[0; 32], + Some(&[0; 32]), None, ), Err(_) @@ -2533,7 +2538,7 @@ mod tests { min_balance, vec![], - &[0;32], + Some(&[0 ;32]), None, ), Ok((address, ref output)) if output.data == vec![80, 65, 83, 83] => address @@ -2587,7 +2592,7 @@ mod tests { min_balance, vec![], - &[0;32], + Some(&[0; 32]), None, ), Ok((address, ref output)) if output.data == vec![70, 65, 73, 76] => address @@ -2620,7 +2625,7 @@ mod tests { dummy_ch, ::Currency::minimum_balance(), vec![], - &[48; 32], + Some(&[48; 32]), ) .unwrap(); @@ -2698,7 +2703,7 @@ mod tests { dummy_ch, ::Currency::minimum_balance(), vec![], - &[0; 32], + Some(&[0; 32]), ), Err(ExecError { error: DispatchError::Other("It's a trap!"), @@ -2771,7 +2776,7 @@ mod tests { &mut storage_meter, 100, vec![], - &[0; 32], + Some(&[0; 32]), None, ), Err(Error::::TerminatedInConstructor.into()) @@ -2882,7 +2887,7 @@ mod tests { &mut storage_meter, min_balance, vec![], - &[0; 32], + Some(&[0; 32]), None, ); assert_matches!(result, Ok(_)); @@ -3250,7 +3255,7 @@ mod tests { fail_code, ctx.ext.minimum_balance() * 100, vec![], - &[0; 32], + Some(&[0; 32]), ) .ok(); exec_success() @@ -3267,7 +3272,7 @@ mod tests { success_code, ctx.ext.minimum_balance() * 100, vec![], - &[0; 32], + Some(&[0; 32]), ) .unwrap(); @@ -3318,7 +3323,7 @@ mod tests { &mut storage_meter, min_balance * 100, vec![], - &[0; 32], + Some(&[0; 32]), None, ) .ok(); @@ -3331,7 +3336,7 @@ mod tests { &mut storage_meter, min_balance * 100, vec![], - &[0; 32], + Some(&[0; 32]), None, )); assert_eq!(System::account_nonce(&ALICE), 1); @@ -3343,7 +3348,7 @@ mod tests { &mut storage_meter, min_balance * 200, vec![], - &[0; 32], + Some(&[0; 32]), None, )); assert_eq!(System::account_nonce(&ALICE), 2); @@ -3355,7 +3360,7 @@ mod tests { &mut storage_meter, min_balance * 200, vec![], - &[0; 32], + Some(&[0; 32]), None, )); assert_eq!(System::account_nonce(&ALICE), 3); diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 359434b9abd6..9a99b01776c1 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -824,7 +824,7 @@ pub mod pallet { /// must be supplied. #[pallet::call_index(1)] #[pallet::weight( - T::WeightInfo::instantiate(data.len() as u32, salt.len() as u32).saturating_add(*gas_limit) + T::WeightInfo::instantiate(data.len() as u32, 32).saturating_add(*gas_limit) )] pub fn instantiate( origin: OriginFor, @@ -833,10 +833,9 @@ pub mod pallet { #[pallet::compact] storage_deposit_limit: BalanceOf, code_hash: sp_core::H256, data: Vec, - salt: [u8; 32], + salt: Option<[u8; 32]>, ) -> DispatchResultWithPostInfo { let data_len = data.len() as u32; - let salt_len = salt.len() as u32; let mut output = Self::bare_instantiate( origin, value, @@ -856,7 +855,7 @@ pub mod pallet { dispatch_result( output.result.map(|result| result.result), output.gas_consumed, - T::WeightInfo::instantiate(data_len, salt_len), + T::WeightInfo::instantiate(data_len, 32), ) } @@ -875,7 +874,9 @@ pub mod pallet { /// from the caller to pay for the storage consumed. /// * `code`: The contract code to deploy in raw bytes. /// * `data`: The input data to pass to the contract constructor. - /// * `salt`: Used for the address derivation. See [`crate::address::create2`]. + /// * `salt`: Used for the address derivation. If `Some` is supplied then `CREATE2` + /// semantics are used. If `None` then `CRATE1` is used. + /// /// /// Instantiation is executed as follows: /// @@ -887,7 +888,7 @@ pub mod pallet { /// - The `deploy` function is executed in the context of the newly-created account. #[pallet::call_index(2)] #[pallet::weight( - T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32, salt.len() as u32) + T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32, 32) .saturating_add(*gas_limit) )] pub fn instantiate_with_code( @@ -897,11 +898,10 @@ pub mod pallet { #[pallet::compact] storage_deposit_limit: BalanceOf, code: Vec, data: Vec, - salt: [u8; 32], + salt: Option<[u8; 32]>, ) -> DispatchResultWithPostInfo { let code_len = code.len() as u32; let data_len = data.len() as u32; - let salt_len = salt.len() as u32; let mut output = Self::bare_instantiate( origin, value, @@ -921,7 +921,7 @@ pub mod pallet { dispatch_result( output.result.map(|result| result.result), output.gas_consumed, - T::WeightInfo::instantiate_with_code(code_len, data_len, salt_len), + T::WeightInfo::instantiate_with_code(code_len, data_len, 32), ) } @@ -1122,7 +1122,7 @@ impl Pallet { mut storage_deposit_limit: BalanceOf, code: Code, data: Vec, - salt: [u8; 32], + salt: Option<[u8; 32]>, debug: DebugInfo, collect_events: CollectEvents, ) -> ContractInstantiateResult, EventRecordOf> { @@ -1158,7 +1158,7 @@ impl Pallet { &mut storage_meter, value, data, - &salt, + salt.as_ref(), debug_message.as_mut(), ); storage_deposit = storage_meter @@ -1298,7 +1298,7 @@ sp_api::decl_runtime_apis! { storage_deposit_limit: Option, code: Code, data: Vec, - salt: [u8; 32], + salt: Option<[u8; 32]>, ) -> ContractInstantiateResult; /// Upload new code without instantiating a contract from it. diff --git a/substrate/frame/revive/src/test_utils/builder.rs b/substrate/frame/revive/src/test_utils/builder.rs index 76b4c98d4cb0..b17067769c05 100644 --- a/substrate/frame/revive/src/test_utils/builder.rs +++ b/substrate/frame/revive/src/test_utils/builder.rs @@ -88,7 +88,7 @@ builder!( storage_deposit_limit: BalanceOf, code: Vec, data: Vec, - salt: [u8; 32], + salt: Option<[u8; 32]>, ) -> DispatchResultWithPostInfo; /// Create an [`InstantiateWithCodeBuilder`] with default values. @@ -100,7 +100,7 @@ builder!( storage_deposit_limit: deposit_limit::(), code, data: vec![], - salt: [0; 32], + salt: Some([0; 32]), } } ); @@ -113,7 +113,7 @@ builder!( storage_deposit_limit: BalanceOf, code_hash: sp_core::H256, data: Vec, - salt: [u8; 32], + salt: Option<[u8; 32]>, ) -> DispatchResultWithPostInfo; /// Create an [`InstantiateBuilder`] with default values. @@ -125,7 +125,7 @@ builder!( storage_deposit_limit: deposit_limit::(), code_hash, data: vec![], - salt: [0; 32], + salt: Some([0; 32]), } } ); @@ -138,7 +138,7 @@ builder!( storage_deposit_limit: BalanceOf, code: Code, data: Vec, - salt: [u8; 32], + salt: Option<[u8; 32]>, debug: DebugInfo, collect_events: CollectEvents, ) -> ContractInstantiateResult, EventRecordOf>; @@ -164,7 +164,7 @@ builder!( storage_deposit_limit: deposit_limit::(), code, data: vec![], - salt: [0; 32], + salt: Some([0; 32]), debug: DebugInfo::UnsafeDebug, collect_events: CollectEvents::Skip, } diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index a37e9842a2cb..447d55f0dd8d 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -26,7 +26,7 @@ use self::{ }; use crate::{ self as pallet_revive, - address::AddressMapper, + address::{create1, create2, AddressMapper}, chain_extension::{ ChainExtension, Environment, Ext, RegisteredChainExtension, Result as ExtensionResult, RetVal, ReturnFlags, @@ -771,6 +771,47 @@ mod run_tests { }); } + #[test] + fn create1_address_from_extrinsic() { + let (wasm, code_hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(1).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + wasm.clone(), + deposit_limit::(), + )); + + assert_eq!(System::account_nonce(&ALICE), 0); + + for nonce in 0..3 { + let Contract { addr, .. } = builder::bare_instantiate(Code::Existing(code_hash)) + .salt(None) + .build_and_unwrap_contract(); + assert!(ContractInfoOf::::contains_key(&addr)); + assert_eq!( + addr, + create1(&::AddressMapper::to_address(&ALICE), nonce) + ); + } + assert_eq!(System::account_nonce(&ALICE), 3); + + for nonce in 3..6 { + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm.clone())) + .salt(None) + .build_and_unwrap_contract(); + assert!(ContractInfoOf::::contains_key(&addr)); + assert_eq!( + addr, + create1(&::AddressMapper::to_address(&ALICE), nonce) + ); + } + assert_eq!(System::account_nonce(&ALICE), 6); + }); + } + #[test] fn deposit_event_max_value_limit() { let (wasm, _code_hash) = compile_module("event_size").unwrap(); @@ -1023,7 +1064,7 @@ mod run_tests { .value(100_000) .build_and_unwrap_contract(); - let callee_addr = crate::address::create2( + let callee_addr = create2( &caller_addr, &callee_wasm, &[0, 1, 34, 51, 68, 85, 102, 119], // hard coded in wasm @@ -1410,7 +1451,7 @@ mod run_tests { // Check that the CHARLIE contract has been instantiated. let salt = [47; 32]; // hard coded in fixture. - let addr_charlie = crate::address::create2(&addr_bob, &callee_wasm, &[], &salt); + let addr_charlie = create2(&addr_bob, &callee_wasm, &[], &salt); get_contract(&addr_charlie); // Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct. @@ -1759,7 +1800,7 @@ mod run_tests { for i in 0..3u8 { let contract = builder::bare_instantiate(Code::Upload(code.clone())) .value(min_balance * 100) - .salt([i; 32]) + .salt(Some([i; 32])) .build_and_unwrap_contract(); let info = get_contract(&contract.addr); @@ -2000,7 +2041,7 @@ mod run_tests { for i in 0..3u8 { let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code.clone())) .value(min_balance * 100) - .salt([i; 32]) + .salt(Some([i; 32])) .build_and_unwrap_contract(); let info = get_contract(&addr); @@ -2042,19 +2083,19 @@ mod run_tests { let Contract { addr: addr0, .. } = builder::bare_instantiate(Code::Upload(wasm.clone())) .value(min_balance * 100) - .salt([0; 32]) + .salt(Some([0; 32])) .build_and_unwrap_contract(); let Contract { addr: addr1, .. } = builder::bare_instantiate(Code::Upload(wasm.clone())) .value(min_balance * 100) - .salt([1; 32]) + .salt(Some([1; 32])) .build_and_unwrap_contract(); assert_refcount!(code_hash, 2); // Sharing should also work with the usual instantiate call let Contract { addr: addr2, .. } = builder::bare_instantiate(Code::Existing(code_hash)) .value(min_balance * 100) - .salt([2; 32]) + .salt(Some([2; 32])) .build_and_unwrap_contract(); assert_refcount!(code_hash, 3); @@ -2241,7 +2282,7 @@ mod run_tests { let Contract { addr: addr_caller, .. } = builder::bare_instantiate(Code::Upload(caller_code)) .value(min_balance * 100) - .salt([0; 32]) + .salt(Some([0; 32])) .build_and_unwrap_contract(); // Call something trivial with a huge gas limit so that we can observe the effects @@ -2279,13 +2320,13 @@ mod run_tests { let Contract { addr: addr_caller, .. } = builder::bare_instantiate(Code::Upload(caller_code)) .value(min_balance * 100) - .salt([0; 32]) + .salt(Some([0; 32])) .build_and_unwrap_contract(); let Contract { addr: addr_callee, .. } = builder::bare_instantiate(Code::Upload(callee_code)) .value(min_balance * 100) - .salt([1; 32]) + .salt(Some([1; 32])) .build_and_unwrap_contract(); // Call pallet_revive call() dispatchable diff --git a/substrate/frame/revive/src/tests/test_debug.rs b/substrate/frame/revive/src/tests/test_debug.rs index e3571b7f21eb..7885d681e482 100644 --- a/substrate/frame/revive/src/tests/test_debug.rs +++ b/substrate/frame/revive/src/tests/test_debug.rs @@ -122,7 +122,7 @@ mod run_tests { deposit_limit::(), Code::Upload(wasm), vec![], - [0u8; 32], + Some([0u8; 32]), DebugInfo::Skip, CollectEvents::Skip, ) @@ -207,7 +207,7 @@ mod run_tests { Code::Upload(wasm), vec![], // some salt to ensure that the address of this contract is unique among all tests - [0x41; 32], + Some([0x41; 32]), DebugInfo::Skip, CollectEvents::Skip, ) diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 70d405da9897..533baf8d2c82 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -327,8 +327,8 @@ pub enum RuntimeCosts { CallTransferSurcharge, /// Weight per byte that is cloned by supplying the `CLONE_INPUT` flag. CallInputCloned(u32), - /// Weight of calling `seal_instantiate` for the given input length and salt. - Instantiate { input_data_len: u32, salt_len: u32 }, + /// Weight of calling `seal_instantiate` for the given input lenth. + Instantiate { input_data_len: u32 }, /// Weight of calling `seal_hash_sha_256` for the given input size. HashSha256(u32), /// Weight of calling `seal_hash_keccak_256` for the given input size. @@ -453,8 +453,7 @@ impl Token for RuntimeCosts { DelegateCallBase => T::WeightInfo::seal_delegate_call(), CallTransferSurcharge => cost_args!(seal_call, 1, 0), CallInputCloned(len) => cost_args!(seal_call, 0, len), - Instantiate { input_data_len, salt_len } => - T::WeightInfo::seal_instantiate(input_data_len, salt_len), + Instantiate { input_data_len } => T::WeightInfo::seal_instantiate(input_data_len, 32), HashSha256(len) => T::WeightInfo::seal_hash_sha2_256(len), HashKeccak256(len) => T::WeightInfo::seal_hash_keccak_256(len), HashBlake256(len) => T::WeightInfo::seal_hash_blake2_256(len), @@ -1015,9 +1014,8 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { output_ptr: u32, output_len_ptr: u32, salt_ptr: u32, - salt_len: u32, ) -> Result { - self.charge_gas(RuntimeCosts::Instantiate { input_data_len, salt_len })?; + self.charge_gas(RuntimeCosts::Instantiate { input_data_len })?; let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { BalanceOf::<::T>::zero() } else { @@ -1026,10 +1024,21 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { let value: BalanceOf<::T> = memory.read_as(value_ptr)?; let code_hash: H256 = memory.read_as(code_hash_ptr)?; let input_data = memory.read(input_data_ptr, input_data_len)?; - let mut salt = [0u8; 32]; - memory.read_into_buf(salt_ptr, salt.as_mut_slice())?; - let instantiate_outcome = - self.ext.instantiate(weight, deposit_limit, code_hash, value, input_data, &salt); + let salt = if salt_ptr == SENTINEL { + None + } else { + let mut salt = [0u8; 32]; + memory.read_into_buf(salt_ptr, salt.as_mut_slice())?; + Some(salt) + }; + let instantiate_outcome = self.ext.instantiate( + weight, + deposit_limit, + code_hash, + value, + input_data, + salt.as_ref(), + ); if let Ok((address, output)) = &instantiate_outcome { if !output.flags.contains(ReturnFlags::REVERT) { self.write_sandbox_output( @@ -1253,7 +1262,7 @@ pub mod env { output_ptr: u32, output_len_ptr: u32, salt_ptr: u32, - salt_len: u32, + _salt_len: u32, ) -> Result { self.instantiate( memory, @@ -1268,7 +1277,6 @@ pub mod env { output_ptr, output_len_ptr, salt_ptr, - salt_len, ) } From 09331f268b0547a76972a904e3e8b5d02d2d5f8b Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 3 Sep 2024 12:55:46 +0200 Subject: [PATCH 234/382] [CI] Also backport to stable2409 (#5561) Also make the backport bot use the new release. --- .github/workflows/command-backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/command-backport.yml b/.github/workflows/command-backport.yml index 72da40f19839..1ad68d96a63f 100644 --- a/.github/workflows/command-backport.yml +++ b/.github/workflows/command-backport.yml @@ -31,7 +31,7 @@ jobs: uses: korthout/backport-action@v3 id: backport with: - target_branches: stable2407 + target_branches: stable2407 stable2409 merge_commits: skip github_token: ${{ secrets.GITHUB_TOKEN }} pull_description: | From d96c452b542992f8925ce4077d8bbbbae6b21329 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Tue, 3 Sep 2024 13:03:08 +0200 Subject: [PATCH 235/382] semver fixes --- prdoc/pr_5546.prdoc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc index 70cf730f840d..16e2bd993463 100644 --- a/prdoc/pr_5546.prdoc +++ b/prdoc/pr_5546.prdoc @@ -10,12 +10,14 @@ crates: bump: patch - name: snowbridge-pallet-inbound-queue bump: patch - - name: snowbridge-pallet-system + - name: snowbridge-pallet-outbound-queue bump: patch + - name: snowbridge-pallet-system + bump: major - name: snowbridge-core - bump: patch + bump: major - name: snowbridge-router-primitives - bump: patch + bump: major - name: bridge-hub-westend-runtime bump: patch - name: bridge-hub-rococo-runtime @@ -28,3 +30,4 @@ crates: bump: patch - name: emulated-integration-tests-common bump: patch + From d7b575338b2c647e04fc48bfbe00ea8f492fb580 Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Tue, 3 Sep 2024 12:27:17 +0100 Subject: [PATCH 236/382] GHA Migration - build misc (#5346) Closes https://github.com/paritytech/ci_cd/issues/1017 - Enabled subsystem-benchmarks 100% + adjusted them to publish as separate job so it can use environment for scope secrets - Added -v1.* triggers for GHA checks - Removed remove subkey & polkavm from gitlab --------- Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> --- .github/workflows/build-misc.yml | 98 +++++++++++++++++++ .github/workflows/check-runtime-migration.yml | 2 +- .github/workflows/checks.yml | 2 +- .../publish-subsystem-benchmarks.yml | 55 ----------- .github/workflows/subsystem-benchmarks.yml | 94 ++++++++++++++---- .gitlab/pipeline/build.yml | 69 +------------ .gitlab/pipeline/publish.yml | 77 --------------- .gitlab/pipeline/test.yml | 61 ++---------- 8 files changed, 184 insertions(+), 274 deletions(-) create mode 100644 .github/workflows/build-misc.yml delete mode 100644 .github/workflows/publish-subsystem-benchmarks.yml diff --git a/.github/workflows/build-misc.yml b/.github/workflows/build-misc.yml new file mode 100644 index 000000000000..c85549b37999 --- /dev/null +++ b/.github/workflows/build-misc.yml @@ -0,0 +1,98 @@ +name: Build Misc + +on: + push: + branches: + - master + pull_request: + types: [ opened, synchronize, reopened, ready_for_review ] + merge_group: + + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + set-image: + # GitHub Actions allows using 'env' in a container context. + # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 + # This workaround sets the container image for each job using 'set-image' job output. + runs-on: ubuntu-latest + outputs: + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT + - id: set_runner + run: | + # Run merge queues on persistent runners + if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT + else + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + fi + + build-runtimes-polkavm: + timeout-minutes: 20 + needs: [ set-image ] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check Rust + run: | + rustup show + rustup +nightly show + + - name: Build + env: + SUBSTRATE_RUNTIME_TARGET: riscv + run: | + forklift cargo check -p minimal-template-runtime + forklift cargo check -p westend-runtime + forklift cargo check -p rococo-runtime + forklift cargo check -p polkadot-test-runtime + + build-subkey: + timeout-minutes: 20 + needs: [ set-image ] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check Rust + run: | + rustup show + rustup +nightly show + + - name: Build + env: + SKIP_WASM_BUILD: 1 + run: | + cd ./substrate/bin/utils/subkey + forklift cargo build --locked --release + + confirm-required-build-misc-jobs-passed: + runs-on: ubuntu-latest + name: All build misc jobs passed + # If any new job gets added, be sure to add it to this array + needs: + [ + build-runtimes-polkavm, + build-subkey + ] + steps: + - run: echo '### Good job! All the build misc tests passed 🚀' >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/check-runtime-migration.yml b/.github/workflows/check-runtime-migration.yml index b93a07bf61f1..5fb9dca38d17 100644 --- a/.github/workflows/check-runtime-migration.yml +++ b/.github/workflows/check-runtime-migration.yml @@ -46,7 +46,7 @@ jobs: # We need to set this to rather long to allow the snapshot to be created, but the average time # should be much lower. timeout-minutes: 60 - needs: [set-image] + needs: [ set-image ] container: image: ${{ needs.set-image.outputs.IMAGE }} strategy: diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index ad9d0d1a959d..9aebd83282e3 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -12,7 +12,7 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true -permissions: {} +permissions: { } jobs: # temporary disabled because currently doesn't work in merge queue diff --git a/.github/workflows/publish-subsystem-benchmarks.yml b/.github/workflows/publish-subsystem-benchmarks.yml deleted file mode 100644 index e5b9db0836f3..000000000000 --- a/.github/workflows/publish-subsystem-benchmarks.yml +++ /dev/null @@ -1,55 +0,0 @@ -# The actions takes json file as input and runs github-action-benchmark for it. - -on: - workflow_dispatch: - inputs: - benchmark-data-dir-path: - description: "Path to the benchmark data directory" - required: true - type: string - output-file-path: - description: "Path to the benchmark data file" - required: true - type: string - -jobs: - subsystem-benchmarks: - runs-on: ubuntu-latest - environment: subsystem-benchmarks - steps: - - name: Validate inputs - run: | - echo "${{ github.event.inputs.benchmark-data-dir-path }}" | grep -P '^[a-z\-]' - echo "${{ github.event.inputs.output-file-path }}" | grep -P '^[a-z\-]+\.json' - - - name: Checkout Sources - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: "gh-pages" - - - name: Copy bench results - id: step_one - run: | - cp bench/gitlab/${{ github.event.inputs.output-file-path }} ${{ github.event.inputs.output-file-path }} - - - name: Switch branch - id: step_two - run: | - git checkout master -- - - - uses: actions/create-github-app-token@v1 - id: app-token - with: - app-id: ${{ secrets.POLKADOTSDK_GHPAGES_APP_ID }} - private-key: ${{ secrets.POLKADOTSDK_GHPAGES_APP_KEY }} - - - name: Store benchmark result - uses: benchmark-action/github-action-benchmark@v1 - with: - tool: "customSmallerIsBetter" - name: ${{ github.event.inputs.benchmark-data-dir-path }} - output-file-path: ${{ github.event.inputs.output-file-path }} - benchmark-data-dir-path: "bench/${{ github.event.inputs.benchmark-data-dir-path }}" - github-token: ${{ steps.app-token.outputs.token }} - auto-push: true diff --git a/.github/workflows/subsystem-benchmarks.yml b/.github/workflows/subsystem-benchmarks.yml index 7c19b420a6ac..c33c782a731c 100644 --- a/.github/workflows/subsystem-benchmarks.yml +++ b/.github/workflows/subsystem-benchmarks.yml @@ -3,7 +3,7 @@ on: branches: - master pull_request: - types: [ opened, synchronize, reopened, closed, labeled ] + types: [ opened, synchronize, reopened, ready_for_review ] merge_group: concurrency: @@ -12,12 +12,9 @@ concurrency: permissions: contents: read - pull-requests: write jobs: set-image: - # TODO: remove once migration is complete or this workflow is fully stable - if: contains(github.event.label.name, 'GHA-migration') # GitHub Actions allows using 'env' in a container context. # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 # This workaround sets the container image for each job using 'set-image' job output. @@ -31,13 +28,11 @@ jobs: run: cat .github/env >> $GITHUB_OUTPUT build: + timeout-minutes: 80 needs: [ set-image ] runs-on: arc-runners-polkadot-sdk-benchmark container: image: ${{ needs.set-image.outputs.IMAGE }} - env: - BENCH_DIR: ./charts/bench/${{ matrix.features.bench }} - BENCH_FILE_NAME: ${{ matrix.features.bench }} strategy: fail-fast: false matrix: @@ -57,26 +52,85 @@ jobs: rustup +nightly show - name: Run Benchmarks - continue-on-error: true id: run-benchmarks run: | - cargo bench -p ${{ matrix.features.name }} --bench ${{ matrix.features.bench }} --features subsystem-benchmarks || echo "Benchmarks failed" + forklift cargo bench -p ${{ matrix.features.name }} --bench ${{ matrix.features.bench }} --features subsystem-benchmarks || echo "Benchmarks failed" ls -lsa ./charts - mkdir -p $BENCH_DIR || echo "Directory exists" - cp charts/${BENCH_FILE_NAME}.json $BENCH_DIR - ls -lsa $BENCH_DIR + mkdir ./artifacts + cp ./charts/${{ matrix.features.bench }}.json ./artifacts/${{ matrix.features.bench }}.json + - name: Upload artifacts + uses: actions/upload-artifact@v4.3.6 + with: + name: ${{matrix.features.bench}} + path: ./artifacts + + publish-benchmarks: + timeout-minutes: 60 + needs: [ build ] + if: github.ref == 'refs/heads/master' + environment: subsystem-benchmarks + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download artifacts + uses: actions/download-artifact@v4.1.8 + with: + path: ./artifacts + + - name: Setup git + run: | # Fixes "detected dubious ownership" error in the ci git config --global --add safe.directory '*' + ls -lsR ./artifacts + + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ secrets.POLKADOTSDK_GHPAGES_APP_ID }} + private-key: ${{ secrets.POLKADOTSDK_GHPAGES_APP_KEY }} - - name: Publish result to GH Pages - if: ${{ steps.run-benchmarks.outcome == 'success' }} + - name: Publish ${{ env.BENCH_NAME }} uses: benchmark-action/github-action-benchmark@v1 + env: + BENCH_NAME: availability-recovery-regression-bench with: tool: "customSmallerIsBetter" - name: ${{ env.BENCH_FILE_NAME }} - output-file-path: ${{ env.BENCH_DIR }}/${{ env.BENCH_FILE_NAME }}.json - benchmark-data-dir-path: ${{ env.BENCH_DIR }} - github-token: ${{ secrets.GITHUB_TOKEN }} - comment-on-alert: ${{ github.event_name == 'pull_request' }} # will comment on PRs if regression is detected - auto-push: false # TODO: enable when gitlab part is removed ${{ github.ref == 'refs/heads/master' }} + name: ${{ env.BENCH_NAME }} + output-file-path: ./artifacts/${{ env.BENCH_NAME }}/${{ env.BENCH_NAME }}.json + benchmark-data-dir-path: ./artifacts/${{ env.BENCH_NAME }} + github-token: ${{ steps.app-token.outputs.token }} + - name: Publish ${{ env.BENCH_NAME }} + uses: benchmark-action/github-action-benchmark@v1 + env: + BENCH_NAME: availability-distribution-regression-bench + with: + tool: "customSmallerIsBetter" + name: ${{ env.BENCH_NAME }} + output-file-path: ./artifacts/${{ env.BENCH_NAME }}/${{ env.BENCH_NAME }}.json + benchmark-data-dir-path: ./artifacts/${{ env.BENCH_NAME }} + github-token: ${{ steps.app-token.outputs.token }} + + - name: Publish ${{ env.BENCH_NAME }} + uses: benchmark-action/github-action-benchmark@v1 + env: + BENCH_NAME: approval-voting-regression-bench + with: + tool: "customSmallerIsBetter" + name: ${{ env.BENCH_NAME }} + output-file-path: ./artifacts/${{ env.BENCH_NAME }}/${{ env.BENCH_NAME }}.json + benchmark-data-dir-path: ./artifacts/${{ env.BENCH_NAME }} + github-token: ${{ steps.app-token.outputs.token }} + + - name: Publish ${{ env.BENCH_NAME }} + uses: benchmark-action/github-action-benchmark@v1 + env: + BENCH_NAME: statement-distribution-regression-bench + with: + tool: "customSmallerIsBetter" + name: ${{ env.BENCH_NAME }} + output-file-path: ./artifacts/${{ env.BENCH_NAME }}/${{ env.BENCH_NAME }}.json + benchmark-data-dir-path: ./artifacts/${{ env.BENCH_NAME }} + github-token: ${{ steps.app-token.outputs.token }} diff --git a/.gitlab/pipeline/build.yml b/.gitlab/pipeline/build.yml index 8658e92efc8f..a5de2173a71f 100644 --- a/.gitlab/pipeline/build.yml +++ b/.gitlab/pipeline/build.yml @@ -313,7 +313,7 @@ build-linux-substrate: # tldr: we need to checkout the branch HEAD explicitly because of our dynamic versioning approach while building the substrate binary # see https://github.com/paritytech/ci_cd/issues/682#issuecomment-1340953589 - git checkout -B "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA" - - !reference [.forklift-cache, before_script] + - !reference [ .forklift-cache, before_script ] script: - time WASM_BUILD_NO_COLOR=1 cargo build --locked --release -p staging-node-cli - mv $CARGO_TARGET_DIR/release/substrate-node ./artifacts/substrate/substrate @@ -329,73 +329,6 @@ build-linux-substrate: # - printf '\n# building node-template\n\n' # - ./scripts/ci/node-template-release.sh ./artifacts/substrate/substrate-node-template.tar.gz -build-runtimes-polkavm: - stage: build - extends: - - .docker-env - - .common-refs - - .run-immediately - script: - - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p minimal-template-runtime - - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p westend-runtime - - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p rococo-runtime - - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p polkadot-test-runtime - -.build-subkey: - stage: build - extends: - - .docker-env - - .common-refs - - .run-immediately - # - .collect-artifact - variables: - # this variable gets overridden by "rusty-cachier environment inject", use the value as default - CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" - before_script: - - mkdir -p ./artifacts/subkey - - !reference [.forklift-cache, before_script] - script: - - cd ./substrate/bin/utils/subkey - - time SKIP_WASM_BUILD=1 cargo build --locked --release - # - cd - - # - mv $CARGO_TARGET_DIR/release/subkey ./artifacts/subkey/. - # - echo -n "Subkey version = " - # - ./artifacts/subkey/subkey --version | - # sed -n -E 's/^subkey ([0-9.]+.*)/\1/p' | - # tee ./artifacts/subkey/VERSION; - # - sha256sum ./artifacts/subkey/subkey | tee ./artifacts/subkey/subkey.sha256 - # - cp -r ./scripts/ci/docker/subkey.Dockerfile ./artifacts/subkey/ - -build-subkey-linux: - extends: .build-subkey - # DAG - needs: - - job: build-malus - artifacts: false -# tbd -# build-subkey-macos: -# extends: .build-subkey -# # duplicating before_script & script sections from .build-subkey hidden job -# # to overwrite rusty-cachier integration as it doesn't work on macos -# before_script: -# # skip timestamp script, the osx bash doesn't support printf %()T -# - !reference [.job-switcher, before_script] -# - mkdir -p ./artifacts/subkey -# script: -# - cd ./bin/utils/subkey -# - SKIP_WASM_BUILD=1 time cargo build --locked --release -# - cd - -# - mv ./target/release/subkey ./artifacts/subkey/. -# - echo -n "Subkey version = " -# - ./artifacts/subkey/subkey --version | -# sed -n -E 's/^subkey ([0-9.]+.*)/\1/p' | -# tee ./artifacts/subkey/VERSION; -# - sha256sum ./artifacts/subkey/subkey | tee ./artifacts/subkey/subkey.sha256 -# - cp -r ./scripts/ci/docker/subkey.Dockerfile ./artifacts/subkey/ -# after_script: [""] -# tags: -# - osx - # bridges # we need some non-binary artifacts in our bridges+zombienet image diff --git a/.gitlab/pipeline/publish.yml b/.gitlab/pipeline/publish.yml index 44cd1933a9cf..5ad9ae9bfb36 100644 --- a/.gitlab/pipeline/publish.yml +++ b/.gitlab/pipeline/publish.yml @@ -62,83 +62,6 @@ publish-rustdoc: after_script: - rm -rf .git/ ./* -publish-subsystem-benchmarks: - stage: publish - variables: - CI_IMAGE: "paritytech/tools:latest" - extends: - - .kubernetes-env - - .publish-gh-pages-refs - needs: - - job: subsystem-benchmark-availability-recovery - artifacts: true - - job: subsystem-benchmark-availability-distribution - artifacts: true - - job: subsystem-benchmark-approval-voting - artifacts: true - - job: subsystem-benchmark-statement-distribution - artifacts: true - - job: publish-rustdoc - artifacts: false - script: - # setup ssh - - eval $(ssh-agent) - - ssh-add - <<< ${GITHUB_SSH_PRIV_KEY} - - mkdir ~/.ssh && touch ~/.ssh/known_hosts - - ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts - # Set git config - - rm -rf .git/config - - git config user.email "devops-team@parity.io" - - git config user.name "${GITHUB_USER}" - - git config remote.origin.url "git@github.com:/paritytech/${CI_PROJECT_NAME}.git" - - git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" - - git fetch origin gh-pages - # Push result to github - - git checkout gh-pages --force - - mkdir -p bench/gitlab/ || echo "Directory exists" - - rm -rf bench/gitlab/*.json || echo "No json files" - - cp -r charts/*.json bench/gitlab/ - - git add bench/gitlab/ - - git commit -m "Add json files with benchmark results for ${CI_COMMIT_REF_NAME}" - - git push origin gh-pages - # artificial sleep to publish gh-pages - - sleep 300 - allow_failure: true - after_script: - - rm -rf .git/ ./* - -trigger_workflow: - stage: deploy - extends: - - .kubernetes-env - - .publish-gh-pages-refs - needs: - - job: publish-subsystem-benchmarks - artifacts: false - - job: subsystem-benchmark-availability-recovery - artifacts: true - - job: subsystem-benchmark-availability-distribution - artifacts: true - - job: subsystem-benchmark-approval-voting - artifacts: true - - job: subsystem-benchmark-statement-distribution - artifacts: true - script: - - echo "Triggering workflow" - - > - for benchmark in $(ls charts/*.json); do - export benchmark_name=$(basename $benchmark); - echo "Benchmark: $benchmark_name"; - export benchmark_dir=$(echo $benchmark_name | sed 's/\.json//'); - curl -q -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token $GITHUB_TOKEN" \ - https://api.github.com/repos/paritytech/${CI_PROJECT_NAME}/actions/workflows/publish-subsystem-benchmarks.yml/dispatches \ - -d "{\"ref\":\"refs/heads/master\",\"inputs\":{\"benchmark-data-dir-path\":\"$benchmark_dir\",\"output-file-path\":\"$benchmark_name\"}}"; - sleep 300; - done - allow_failure: true - # note: images are used not only in zombienet but also in rococo, wococo and versi .build-push-image: image: $BUILDAH_IMAGE diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index 85f6e8dc780b..83af0e6b7b20 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -35,7 +35,7 @@ codecov-start: - .pipeline-stopper-artifacts - .run-immediately script: - - !reference [.codecov-check, script] + - !reference [ .codecov-check, script ] - > if [ "$CI_COMMIT_REF_NAME" != "master" ]; then codecovcli -v create-commit -t ${CODECOV_TOKEN} -r paritytech/polkadot-sdk --commit-sha ${CI_COMMIT_SHA} --fail-on-error --pr ${CI_COMMIT_REF_NAME} --git-service github; @@ -57,7 +57,7 @@ codecov-finish: needs: - test-linux-stable-codecov script: - - !reference [.codecov-check, script] + - !reference [ .codecov-check, script ] - codecovcli -v create-report-results -t ${CODECOV_TOKEN} -r paritytech/polkadot-sdk --commit-sha ${CI_COMMIT_SHA} --git-service github - codecovcli -v get-report-results -t ${CODECOV_TOKEN} -r paritytech/polkadot-sdk --commit-sha ${CI_COMMIT_SHA} --git-service github - codecovcli -v send-notifications -t ${CODECOV_TOKEN} -r paritytech/polkadot-sdk --commit-sha ${CI_COMMIT_SHA} --git-service github @@ -83,7 +83,7 @@ test-linux-stable-codecov: parallel: 2 script: # tools - - !reference [.codecov-check, script] + - !reference [ .codecov-check, script ] - rustup component add llvm-tools-preview - mkdir -p target/coverage/result/ # Place real test call here @@ -316,7 +316,7 @@ cargo-check-benches: git merge --verbose --no-edit FETCH_HEAD; fi fi' - - !reference [.forklift-cache, before_script] + - !reference [ .forklift-cache, before_script ] parallel: 2 script: - mkdir -p ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA @@ -360,7 +360,7 @@ node-bench-regression-guard: artifacts: true variables: CI_IMAGE: "paritytech/node-bench-regression-guard:latest" - before_script: [""] + before_script: [ "" ] script: - if [ $(ls -la artifacts/benches/ | grep master | wc -l) == 0 ]; then echo "Couldn't find master artifacts"; @@ -371,7 +371,7 @@ node-bench-regression-guard: - echo "In case of this job failure, check your pipeline's cargo-check-benches" - "node-bench-regression-guard --reference artifacts/benches/master-* --compare-with artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - after_script: [""] + after_script: [ "" ] # if this fails run `bot update-ui` in the Pull Request or "./scripts/update-ui-tests.sh" locally # see ./docs/contributor/CONTRIBUTING.md#ui-tests @@ -533,9 +533,9 @@ cargo-check-each-crate-macos: # - .collect-artifacts before_script: # skip timestamp script, the osx bash doesn't support printf %()T - - !reference [.job-switcher, before_script] - - !reference [.rust-info-script, script] - - !reference [.pipeline-stopper-vars, script] + - !reference [ .job-switcher, before_script ] + - !reference [ .rust-info-script, script ] + - !reference [ .pipeline-stopper-vars, script ] variables: SKIP_WASM_BUILD: 1 script: @@ -580,46 +580,3 @@ cargo-hfuzz: - cargo hfuzz build - for target in $(cargo read-manifest | jq -r '.targets | .[] | .name'); do cargo hfuzz run "$target" || { printf "fuzzing failure for %s\n" "$target"; exit 1; }; done - -.subsystem-benchmark-template: - stage: test - artifacts: - name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" - when: always - expire_in: 1 hour - paths: - - charts/ - extends: - - .docker-env - - .common-refs - - .run-immediately - tags: - - benchmark - -subsystem-benchmark-availability-recovery: - extends: - - .subsystem-benchmark-template - script: - - cargo bench -p polkadot-availability-recovery --bench availability-recovery-regression-bench --features subsystem-benchmarks - allow_failure: true - -subsystem-benchmark-availability-distribution: - extends: - - .subsystem-benchmark-template - script: - - cargo bench -p polkadot-availability-distribution --bench availability-distribution-regression-bench --features subsystem-benchmarks - allow_failure: true - -subsystem-benchmark-approval-voting: - extends: - - .subsystem-benchmark-template - script: - - cargo bench -p polkadot-node-core-approval-voting --bench approval-voting-regression-bench --features subsystem-benchmarks - allow_failure: true - -subsystem-benchmark-statement-distribution: - extends: - - .subsystem-benchmark-template - script: - - cargo bench -p polkadot-statement-distribution --bench statement-distribution-regression-bench --features subsystem-benchmarks - allow_failure: true From c80a5cb6e6c0b32febef41699f71b192b6214d77 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 21:21:00 +0800 Subject: [PATCH 237/382] EnsureRootOrSigned --- bridges/snowbridge/pallets/system/src/lib.rs | 46 ++++++++++++++++++- bridges/snowbridge/pallets/system/src/mock.rs | 2 + .../src/bridge_to_ethereum_config.rs | 2 + .../src/bridge_to_ethereum_config.rs | 2 + 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 145c8c612909..c2c68c111de1 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -59,6 +59,7 @@ pub mod weights; pub use weights::*; use frame_support::{ + dispatch::RawOrigin, pallet_prelude::*, traits::{ fungible::{Inspect, Mutate}, @@ -135,6 +136,39 @@ where No, } +pub struct EnsureRootOrSigned(core::marker::PhantomData); +impl EnsureOrigin<::RuntimeOrigin> for EnsureRootOrSigned { + type Success = (bool, Option>); + fn try_origin(o: T::RuntimeOrigin) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Root => Ok((true, None)), + RawOrigin::Signed(t) => Ok((false, Some(t))), + r => Err(T::RuntimeOrigin::from(r)), + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(RawOrigin::Root.into()) + } +} + +pub struct EnsureRootOnly(core::marker::PhantomData); +impl EnsureOrigin<::RuntimeOrigin> for EnsureRootOnly { + type Success = (bool, Option>); + fn try_origin(o: T::RuntimeOrigin) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Root => Ok((true, None)), + r => Err(T::RuntimeOrigin::from(r)), + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(RawOrigin::Root.into()) + } +} + #[frame_support::pallet] pub mod pallet { use snowbridge_core::StaticLookup; @@ -176,6 +210,11 @@ pub mod pallet { #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; + + type RegisterTokenOrigin: EnsureOrigin< + Self::RuntimeOrigin, + Success = (bool, Option>), + >; } #[pallet::event] @@ -618,12 +657,15 @@ pub mod pallet { location: Box, metadata: AssetMetadata, ) -> DispatchResult { - ensure_root(origin)?; + let (is_sudo, who) = T::RegisterTokenOrigin::ensure_origin(origin)?; let location: Location = (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; - let pays_fee = PaysFee::::No; + let mut pays_fee = PaysFee::::No; + if !is_sudo && who.is_some() { + pays_fee = PaysFee::::Yes(who.unwrap()); + } Self::do_register_token(location, metadata, pays_fee)?; diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index 98bd3da9ab27..a3946497b8cf 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -22,6 +22,7 @@ use xcm::prelude::*; #[cfg(feature = "runtime-benchmarks")] use crate::BenchmarkHelper; +use crate::EnsureRootOrSigned; type Block = frame_system::mocking::MockBlock; type Balance = u128; @@ -210,6 +211,7 @@ impl crate::Config for Test { type InboundDeliveryCost = InboundDeliveryCost; #[cfg(feature = "runtime-benchmarks")] type Helper = (); + type RegisterTokenOrigin = EnsureRootOrSigned; } // Build genesis storage according to the mock runtime. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 26857d262c20..49d436df07ee 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -37,6 +37,7 @@ use crate::xcm_config::RelayNetwork; use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; use pallet_xcm::EnsureXcm; +use snowbridge_pallet_system::EnsureRootOnly; use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, @@ -188,6 +189,7 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type RegisterTokenOrigin = EnsureRootOnly; } #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 621d39af5af0..e11b55da619f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -38,6 +38,7 @@ use crate::xcm_config::RelayNetwork; use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; use pallet_xcm::EnsureXcm; +use snowbridge_pallet_system::EnsureRootOnly; use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, @@ -188,6 +189,7 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type RegisterTokenOrigin = EnsureRootOnly; } #[cfg(feature = "runtime-benchmarks")] From 9caa782ea7c1304fb803b6ccbff74099d840ca3f Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 21:25:56 +0800 Subject: [PATCH 238/382] Update bridges/snowbridge/primitives/router/src/inbound/mod.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index d8292488f14f..1f66d1a2e42a 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -382,6 +382,9 @@ impl< ) } + /// Constructs an XCM message destined for AssetHub that withdraws assets from the sovereign account + /// of the Gateway contract and either deposits those assets into a recipient account or + /// forwards the assets to another parachain. fn convert_send_native_token( message_id: H256, chain_id: u64, From 92df2b5b3b9459ac960648b99f27c7a1a9c3d026 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 21:26:08 +0800 Subject: [PATCH 239/382] Update bridges/snowbridge/primitives/core/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/primitives/core/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index f2e4f9131b50..e7043310f478 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -177,6 +177,7 @@ pub struct AssetMetadata { pub type TokenId = H256; +/// Convert a token location to a stable ID that can be used on the Ethereum side pub type TokenIdOf = HashedDescription>; pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); From c699503046edbce76211c8e1ae93eb09456f4481 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 21:44:34 +0800 Subject: [PATCH 240/382] Revert "EnsureRootOrSigned" This reverts commit c80a5cb6e6c0b32febef41699f71b192b6214d77. --- bridges/snowbridge/pallets/system/src/lib.rs | 46 +------------------ bridges/snowbridge/pallets/system/src/mock.rs | 2 - .../src/bridge_to_ethereum_config.rs | 2 - .../src/bridge_to_ethereum_config.rs | 2 - 4 files changed, 2 insertions(+), 50 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index c2c68c111de1..145c8c612909 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -59,7 +59,6 @@ pub mod weights; pub use weights::*; use frame_support::{ - dispatch::RawOrigin, pallet_prelude::*, traits::{ fungible::{Inspect, Mutate}, @@ -136,39 +135,6 @@ where No, } -pub struct EnsureRootOrSigned(core::marker::PhantomData); -impl EnsureOrigin<::RuntimeOrigin> for EnsureRootOrSigned { - type Success = (bool, Option>); - fn try_origin(o: T::RuntimeOrigin) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Root => Ok((true, None)), - RawOrigin::Signed(t) => Ok((false, Some(t))), - r => Err(T::RuntimeOrigin::from(r)), - }) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - Ok(RawOrigin::Root.into()) - } -} - -pub struct EnsureRootOnly(core::marker::PhantomData); -impl EnsureOrigin<::RuntimeOrigin> for EnsureRootOnly { - type Success = (bool, Option>); - fn try_origin(o: T::RuntimeOrigin) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Root => Ok((true, None)), - r => Err(T::RuntimeOrigin::from(r)), - }) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - Ok(RawOrigin::Root.into()) - } -} - #[frame_support::pallet] pub mod pallet { use snowbridge_core::StaticLookup; @@ -210,11 +176,6 @@ pub mod pallet { #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; - - type RegisterTokenOrigin: EnsureOrigin< - Self::RuntimeOrigin, - Success = (bool, Option>), - >; } #[pallet::event] @@ -657,15 +618,12 @@ pub mod pallet { location: Box, metadata: AssetMetadata, ) -> DispatchResult { - let (is_sudo, who) = T::RegisterTokenOrigin::ensure_origin(origin)?; + ensure_root(origin)?; let location: Location = (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; - let mut pays_fee = PaysFee::::No; - if !is_sudo && who.is_some() { - pays_fee = PaysFee::::Yes(who.unwrap()); - } + let pays_fee = PaysFee::::No; Self::do_register_token(location, metadata, pays_fee)?; diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index a3946497b8cf..98bd3da9ab27 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -22,7 +22,6 @@ use xcm::prelude::*; #[cfg(feature = "runtime-benchmarks")] use crate::BenchmarkHelper; -use crate::EnsureRootOrSigned; type Block = frame_system::mocking::MockBlock; type Balance = u128; @@ -211,7 +210,6 @@ impl crate::Config for Test { type InboundDeliveryCost = InboundDeliveryCost; #[cfg(feature = "runtime-benchmarks")] type Helper = (); - type RegisterTokenOrigin = EnsureRootOrSigned; } // Build genesis storage according to the mock runtime. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 49d436df07ee..26857d262c20 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -37,7 +37,6 @@ use crate::xcm_config::RelayNetwork; use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; use pallet_xcm::EnsureXcm; -use snowbridge_pallet_system::EnsureRootOnly; use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, @@ -189,7 +188,6 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; - type RegisterTokenOrigin = EnsureRootOnly; } #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index e11b55da619f..621d39af5af0 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -38,7 +38,6 @@ use crate::xcm_config::RelayNetwork; use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; use pallet_xcm::EnsureXcm; -use snowbridge_pallet_system::EnsureRootOnly; use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, @@ -189,7 +188,6 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; - type RegisterTokenOrigin = EnsureRootOnly; } #[cfg(feature = "runtime-benchmarks")] From bce32ea0d4838c69c93eed1225c12f0481e1313a Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 22:10:29 +0800 Subject: [PATCH 241/382] Update bridges/snowbridge/primitives/core/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/primitives/core/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index e7043310f478..c31124edf51d 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -168,6 +168,7 @@ impl DescribeLocation for DescribeHere { pub type AgentIdOf = HashedDescription)>; +/// Metadata to include in the instantiated ERC20 token contract #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct AssetMetadata { pub name: BoundedVec>, From e5cb15978bc60ca4016f87599b8446b01df1605f Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 22:20:27 +0800 Subject: [PATCH 242/382] Use reference instead --- bridges/snowbridge/pallets/system/src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 145c8c612909..6b8aa7cdaa7e 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -625,7 +625,7 @@ pub mod pallet { let pays_fee = PaysFee::::No; - Self::do_register_token(location, metadata, pays_fee)?; + Self::do_register_token(&location, metadata, pays_fee)?; Ok(()) } @@ -720,7 +720,7 @@ pub mod pallet { } pub(crate) fn do_register_token( - location: Location, + location: &Location, metadata: AssetMetadata, pays_fee: PaysFee, ) -> Result<(), DispatchError> { @@ -739,7 +739,10 @@ pub mod pallet { }; Self::send(SECONDARY_GOVERNANCE_CHANNEL, command, pays_fee)?; - Self::deposit_event(Event::::RegisterToken { asset_id: location.into(), token_id }); + Self::deposit_event(Event::::RegisterToken { + asset_id: location.clone().into(), + token_id, + }); Ok(()) } From ee7c1e338b42c690b4cf2bf8ab87f69908caaf99 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 22:21:15 +0800 Subject: [PATCH 243/382] Update bridges/snowbridge/pallets/system/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/pallets/system/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 6b8aa7cdaa7e..16e0c48bbf7d 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -610,7 +610,7 @@ pub mod pallet { /// /// - `origin`: Must be root /// - `location`: Location of the asset - /// - `metadata`: Metadata(name,symbol,decimals) of the asset + /// - `metadata`: Metadata to include in the instantiated ERC20 contract on Ethereum #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::register_token())] pub fn register_token( From 51f38b3b99d589e44890cc7335205c4229ee826e Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 22:22:40 +0800 Subject: [PATCH 244/382] Update bridges/snowbridge/pallets/system/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/pallets/system/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 16e0c48bbf7d..dd23f660edb9 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -261,7 +261,6 @@ pub mod pallet { /// Lookup table for foreign to native token ID #[pallet::storage] - #[pallet::getter(fn tokens)] pub type ForeignToNativeId = StorageMap<_, Twox64Concat, TokenId, xcm::v4::Location, OptionQuery>; From cfb926980b56a60e7854fdce5416c0b490036795 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 22:22:53 +0800 Subject: [PATCH 245/382] Update bridges/snowbridge/pallets/system/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/pallets/system/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index dd23f660edb9..c643bc491584 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -42,7 +42,6 @@ //! prerequisite, the token should be registered first. //! //! * [`Call::register_token`]: Register a token location as a wrapped ERC20 contract on Ethereum. - #![cfg_attr(not(feature = "std"), no_std)] #[cfg(test)] mod mock; From 263b622703a04ea0a4cd06efe1e966c2e0ab33e8 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 22:23:03 +0800 Subject: [PATCH 246/382] Update bridges/snowbridge/pallets/system/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/pallets/system/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index c643bc491584..a0d90dce78ff 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -222,8 +222,10 @@ pub mod pallet { }, /// Register Polkadot-native token as a wrapped ERC20 token on Ethereum RegisterToken { - asset_id: VersionedLocation, - token_id: H256, + /// Location of Polkadot-native token + location: VersionedLocation, + /// ID of Polkadot-native token on Ethereum + foreign_token_id: H256, }, } From 0306436196eab3f1702840641c813f3c02320e18 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 22:23:13 +0800 Subject: [PATCH 247/382] Update bridges/snowbridge/pallets/system/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/pallets/system/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index a0d90dce78ff..0d9c0bf89eee 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -267,7 +267,6 @@ pub mod pallet { /// Lookup table for native to foreign token ID #[pallet::storage] - #[pallet::getter(fn location_tokens)] pub type NativeToForeignId = StorageMap<_, Twox64Concat, xcm::v4::Location, TokenId, OptionQuery>; From f339995cdbb59419f3a0c23e7dc7df6f370aec7d Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 22:42:42 +0800 Subject: [PATCH 248/382] Add comment for the xcm format --- bridges/snowbridge/primitives/router/src/outbound/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index ee86feaeabc7..d3b6c116dd7a 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -318,6 +318,13 @@ where } } + /// Convert the xcm for Polkadot-native token from AH into the Command + /// To match transfers of Polkadot-native tokens, we expect an input of the form: + /// # ReserveAssetDeposited + /// # ClearOrigin + /// # BuyExecution + /// # DepositAsset + /// # SetTopic fn send_native_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { use XcmConverterError::*; From 91707b7441464de1ec1a037c0eb0a92e66bf0914 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 22:43:02 +0800 Subject: [PATCH 249/382] Fix building --- bridges/snowbridge/pallets/system/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 0d9c0bf89eee..051e15c387ad 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -224,7 +224,7 @@ pub mod pallet { RegisterToken { /// Location of Polkadot-native token location: VersionedLocation, - /// ID of Polkadot-native token on Ethereum + /// ID of Polkadot-native token on Ethereum foreign_token_id: H256, }, } @@ -739,8 +739,8 @@ pub mod pallet { Self::send(SECONDARY_GOVERNANCE_CHANNEL, command, pays_fee)?; Self::deposit_event(Event::::RegisterToken { - asset_id: location.clone().into(), - token_id, + location: location.clone().into(), + foreign_token_id: token_id, }); Ok(()) From 2bbee55050f95fd8cd8689b5976436ee7aa855c6 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 22:49:23 +0800 Subject: [PATCH 250/382] Fix format --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 1f66d1a2e42a..3e1ddb1ffc6a 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -382,8 +382,8 @@ impl< ) } - /// Constructs an XCM message destined for AssetHub that withdraws assets from the sovereign account - /// of the Gateway contract and either deposits those assets into a recipient account or + /// Constructs an XCM message destined for AssetHub that withdraws assets from the sovereign + /// account of the Gateway contract and either deposits those assets into a recipient account or /// forwards the assets to another parachain. fn convert_send_native_token( message_id: H256, From 325df541392fe04f95803a8cb5364cdbda741c16 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:17:36 +0300 Subject: [PATCH 251/382] chainHead/fix: Report bestBlock events only for newBlock reports (#5527) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The https://github.com/paritytech/polkadot-sdk/issues/5512 has surfaced that we reported a `BestBlock` event for a block not previously reported via `NewBlock`. This is because of a race between: - the stream of events that announces new blocks - `self.client.info().best_block` It is possible that `client.info()` contains newer information than the information polled from the block stream (that may be lagging). To mitigate this, instead of relying on the client's info use the last finalized block to emit a new event. There are two cases when a new best block event is emitted: - The best block is in the pruned list and is reported immediately - The best block is not a descendant of the last finalized block Closes: https://github.com/paritytech/polkadot-sdk/issues/5512 Thanks @jsdw and @josepot for helping debug this 🙏 cc @paritytech/subxt-team --------- Signed-off-by: Alexandru Vasile Co-authored-by: Sebastian Kunert Co-authored-by: command-bot <> --- prdoc/pr_5527.prdoc | 17 ++ .../src/chain_head/chain_head_follow.rs | 99 ++++---- .../rpc-spec-v2/src/chain_head/test_utils.rs | 9 +- .../rpc-spec-v2/src/chain_head/tests.rs | 221 +++++++++++++++++- 4 files changed, 287 insertions(+), 59 deletions(-) create mode 100644 prdoc/pr_5527.prdoc diff --git a/prdoc/pr_5527.prdoc b/prdoc/pr_5527.prdoc new file mode 100644 index 000000000000..38eb75affe44 --- /dev/null +++ b/prdoc/pr_5527.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Report BestBlock events only for newBlock reports + +doc: + - audience: Node Dev + description: | + This PR ensures that the chainHead_v1_follow method of the RPC-v2 API is always + reporting a `BestBlock` event after a `NewBlock`. + There was a race condition in the chainHead follow logic which led to the `BestBlock` + event to be emitted without an associated `NewBlock` event. + +crates: + - name: sc-rpc-spec-v2 + bump: minor + diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs index 1d28d2071248..ebb72ed3d156 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs @@ -428,9 +428,12 @@ where /// Generates new block events from the given finalized hashes. /// /// It may be possible that the `Finalized` event fired before the `NewBlock` - /// event. In that case, for each finalized hash that was not reported yet - /// generate the `NewBlock` event. For the final finalized hash we must also - /// generate one `BestBlock` event. + /// event. Only in that case we generate: + /// - `NewBlock` event for all finalized hashes. + /// - `BestBlock` event for the last finalized hash. + /// + /// This function returns an empty list if all finalized hashes were already reported + /// and are pinned. fn generate_finalized_events( &mut self, finalized_block_hashes: &[Block::Hash], @@ -454,34 +457,33 @@ where } // Generate `NewBlock` events for all blocks beside the last block in the list - if i + 1 != finalized_block_hashes.len() { + let is_last = i + 1 == finalized_block_hashes.len(); + if !is_last { // Generate only the `NewBlock` event for this block. events.extend(self.generate_import_events(*hash, *parent, false)); - } else { + continue; + } + + if let Some(best_block_hash) = self.current_best_block { + let ancestor = + sp_blockchain::lowest_common_ancestor(&*self.client, *hash, best_block_hash)?; + // If we end up here and the `best_block` is a descendent of the finalized block // (last block in the list), it means that there were skipped notifications. - // Otherwise `pin_block` would had returned `true`. + // Otherwise `pin_block` would had returned `false`. // // When the node falls out of sync and then syncs up to the tip of the chain, it can // happen that we skip notifications. Then it is better to terminate the connection // instead of trying to send notifications for all missed blocks. - if let Some(best_block_hash) = self.current_best_block { - let ancestor = sp_blockchain::lowest_common_ancestor( - &*self.client, - *hash, - best_block_hash, - )?; - - if ancestor.hash == *hash { - return Err(SubscriptionManagementError::Custom( - "A descendent of the finalized block was already reported".into(), - )) - } + if ancestor.hash == *hash { + return Err(SubscriptionManagementError::Custom( + "A descendent of the finalized block was already reported".into(), + )) } - - // Let's generate the `NewBlock` and `NewBestBlock` events for the block. - events.extend(self.generate_import_events(*hash, *parent, true)) } + + // Let's generate the `NewBlock` and `NewBestBlock` events for the block. + events.extend(self.generate_import_events(*hash, *parent, true)) } Ok(events) @@ -549,39 +551,32 @@ where }); if let Some(current_best_block) = self.current_best_block { - // The best reported block is in the pruned list. Report a new best block. + // We need to generate a new best block if the best block is in the pruned list. let is_in_pruned_list = pruned_block_hashes.iter().any(|hash| *hash == current_best_block); - // The block is not the last finalized block. - // - // It can be either: - // - a descendant of the last finalized block - // - a block on a fork that will be pruned in the future. - // - // In those cases, we emit a new best block. - let is_not_last_finalized = current_best_block != last_finalized; - - if is_in_pruned_list || is_not_last_finalized { - // We need to generate a best block event. - let best_block_hash = self.client.info().best_hash; - - // Defensive check against state missmatch. - if best_block_hash == current_best_block { - // The client doest not have any new information about the best block. - // The information from `.info()` is updated from the DB as the last - // step of the finalization and it should be up to date. - // If the info is outdated, there is nothing the RPC can do for now. - debug!( - target: LOG_TARGET, - "[follow][id={:?}] Client does not contain different best block", - self.sub_id, - ); - } else { - // The RPC needs to also submit a new best block changed before the - // finalized event. - self.current_best_block = Some(best_block_hash); - events - .push(FollowEvent::BestBlockChanged(BestBlockChanged { best_block_hash })); + if is_in_pruned_list { + self.current_best_block = Some(last_finalized); + events.push(FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: last_finalized, + })); + } else { + // The pruning logic ensures that when the finalized block is announced, + // all blocks on forks that have the common ancestor lower or equal + // to the finalized block are reported. + // + // However, we double check if the best block is a descendant of the last finalized + // block to ensure we don't miss any events. + let ancestor = sp_blockchain::lowest_common_ancestor( + &*self.client, + last_finalized, + current_best_block, + )?; + let is_descendant = ancestor.hash == last_finalized; + if !is_descendant { + self.current_best_block = Some(last_finalized); + events.push(FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: last_finalized, + })); } } } diff --git a/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs b/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs index ab5be1f24e5d..073ee34a79f3 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs @@ -69,7 +69,7 @@ impl ChainHeadMockClient { } } - pub async fn trigger_finality_stream(&self, header: Header) { + pub async fn trigger_finality_stream(&self, header: Header, stale_heads: Vec) { // Ensure the client called the `finality_notification_stream`. while self.finality_sinks.lock().is_empty() { tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; @@ -77,11 +77,8 @@ impl ChainHeadMockClient { // Build the notification. let (sink, _stream) = tracing_unbounded("test_sink", 100_000); - let summary = FinalizeSummary { - header: header.clone(), - finalized: vec![header.hash()], - stale_heads: vec![], - }; + let summary = + FinalizeSummary { header: header.clone(), finalized: vec![header.hash()], stale_heads }; let notification = FinalityNotification::from_summary(summary, sink); for sink in self.finality_sinks.lock().iter_mut() { diff --git a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs index 38f091471f87..a638a9c7ec54 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -2743,7 +2743,7 @@ async fn follow_finalized_before_new_block() { // expect for the `chainHead` to generate `NewBlock`, `BestBlock` and `Finalized` events. // Trigger the Finalized notification before the NewBlock one. - run_with_timeout(client_mock.trigger_finality_stream(block_1.header.clone())).await; + run_with_timeout(client_mock.trigger_finality_stream(block_1.header.clone(), vec![])).await; // Initialized must always be reported first. let finalized_hash = client.info().finalized_hash; @@ -3833,3 +3833,222 @@ async fn follow_unique_pruned_blocks() { }); assert_eq!(event, expected); } + +#[tokio::test] +async fn follow_report_best_block_of_a_known_block() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let client = Arc::new(builder.build()); + + let client_mock = Arc::new(ChainHeadMockClient::new(client.clone())); + + let api = ChainHead::new( + client_mock.clone(), + backend, + Arc::new(TaskExecutor::default()), + ChainHeadConfig { + global_max_pinned_blocks: MAX_PINNED_BLOCKS, + subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), + subscription_max_ongoing_operations: MAX_OPERATIONS, + operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_lagging_distance: MAX_LAGGING_DISTANCE, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + }, + ) + .into_rpc(); + + let finalized_hash = client.info().finalized_hash; + let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap(); + // Initialized must always be reported first. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Initialized(Initialized { + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], + finalized_block_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + + // Block tree: + // + // finalized -> block 1 -> block 2 + // ^^^ best block reported + // + // -> block 1 -> block 2_f -> block 3 (best) + // ^^^ finalized + + let block_1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_1_hash = block_1.hash(); + client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); + let block_2_f = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_2_f_hash = block_2_f.hash(); + client.import(BlockOrigin::Own, block_2_f.clone()).await.unwrap(); + + // Import block 2 as best on the fork. + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(1) + .build() + .unwrap(); + // This push is required as otherwise block 3 has the same hash as block 2 and won't get + // imported + block_builder + .push_transfer(Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Ferdie.into(), + amount: 41, + nonce: 0, + }) + .unwrap(); + let block_2 = block_builder.build().unwrap().block; + let block_2_hash = block_2.header.hash(); + client.import_as_best(BlockOrigin::Own, block_2.clone()).await.unwrap(); + + run_with_timeout(client_mock.trigger_import_stream(block_1.header.clone())).await; + run_with_timeout(client_mock.trigger_import_stream(block_2_f.header.clone())).await; + run_with_timeout(client_mock.trigger_import_stream(block_2.header.clone())).await; + + // Check block 1. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_1_hash), + parent_block_hash: format!("{:?}", finalized_hash), + new_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_1_hash), + }); + assert_eq!(event, expected); + + // Check block 2. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_2_f_hash), + parent_block_hash: format!("{:?}", block_1_hash), + new_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_2_f_hash), + }); + assert_eq!(event, expected); + + // Check block 2, that we imported as custom best. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_2_hash), + parent_block_hash: format!("{:?}", block_1_hash), + new_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_2_hash), + }); + assert_eq!(event, expected); + + // Craft block 3 and import it later to simulate a race condition. + let block_3 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_2_f_hash) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_3_hash = block_3.hash(); + + // Set best block info to block 3, that is not announced yet. + // + // This simulates the following edge-case: + // - The client imports a new block as best block. + // - The finality stream is triggered before the block is announced. + // + // This generated in the past a `BestBlock` event for the block that was not announced + // by `NewBlock` events. + // + // This happened because the chainHead was using the `client.info()` without verifying + // if the block was announced or not. This was fixed by using the latest finalized + // block instead as fallback. For more info see: https://github.com/paritytech/polkadot-sdk/issues/5512. + client_mock.set_best_block(block_3_hash, 3); + + // Finalize the block 2 from the fork. + client.finalize_block(block_2_f_hash, None).unwrap(); + run_with_timeout( + client_mock.trigger_finality_stream(block_2_f.header.clone(), vec![block_2_hash]), + ) + .await; + + // Block 2f is now the best block, not the block 3 that is not announced yet. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_2_f_hash), + }); + assert_eq!(event, expected); + // Block 2 must be reported as pruned, even if it was the previous best. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Finalized(Finalized { + finalized_block_hashes: vec![ + // Note: the client mock is only reporting one block at a time. + // format!("{:?}", block_1_hash), + format!("{:?}", block_2_f_hash), + ], + pruned_block_hashes: vec![format!("{:?}", block_2_hash)], + }); + assert_eq!(event, expected); + + // Block 3 is now imported as best. + client.import_as_best(BlockOrigin::Own, block_3.clone()).await.unwrap(); + run_with_timeout(client_mock.trigger_import_stream(block_3.header.clone())).await; + + // Check block 3. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_3_hash), + parent_block_hash: format!("{:?}", block_2_f_hash), + new_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_3_hash), + }); + assert_eq!(event, expected); + + // Pruned hash can be unpinned. + let sub_id = sub.subscription_id(); + let sub_id = serde_json::to_string(&sub_id).unwrap(); + let hash = format!("{:?}", block_2_hash); + let _res: () = api.call("chainHead_v1_unpin", rpc_params![&sub_id, &hash]).await.unwrap(); + + // Finalize the block 3. + client.finalize_block(block_3_hash, None).unwrap(); + run_with_timeout(client_mock.trigger_finality_stream(block_3.header.clone(), vec![])).await; + + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Finalized(Finalized { + finalized_block_hashes: vec![format!("{:?}", block_3_hash)], + pruned_block_hashes: vec![], + }); + assert_eq!(event, expected); +} From 4d2f7932925915fa377eaf0a8aaaf312fcb7db90 Mon Sep 17 00:00:00 2001 From: Iulian Barbu <14218860+iulianbarbu@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:27:25 +0300 Subject: [PATCH 252/382] cumulus/client: added external rpc connection retry logic (#5515) # Description Adds retry logic that makes the RPC relay chain interface more reliable for the cases of a collator connecting to external RPC servers. Closes #5514 Closes #4278 Final solution still debated on #5514 , what this PR addresses might change (e.g. #4278 might require a more advanced approach). ## Integration Users that start collators should barely observe differences based on this logic, since the retry logic applies only in case the collators fail to connect to the RPC servers. In practice I assume the RPC servers are already live before starting collators, and the issue isn't visible. ## Review Notes The added retry logic is for retrying the connection to the RPC servers (which can be multiple). It is at the level of the cumulus/client/relay-chain-rpc-interface module, but more specifically relevant to the RPC clients logic (`ClientManager`). The retry logic is not configurable, it tries to connect to the RPC client for 5 times, with an exponential backoff in between each iteration starting with 1 second wait time and ending with 16 seconds. The same logic is applied in case an existing connection to an RPC is dropped. There is a `ReconnectingWebsocketWorker` who ensures there is connectivity to at least on RPC node, and the retry logic makes this stronger by insisting on trying connections to the RPC servers list for 5 times. ## Testing - This was tested manually by starting zombienet natively based on [006-rpc_collator_builds_blocks.toml](https://github.com/paritytech/polkadot-sdk/blob/master/cumulus/zombienet/tests/0006-rpc_collator_builds_blocks.toml) and observing collators don't fail anymore: ```bash zombienet -l text --dir zbn-run -f --provider native spawn polkadot-sdk/cumulus/zombienet/tests/0006-rpc_collator_builds_blocks.toml ``` - Added a unit test that exercises the retry logic for a client connection to a server that comes online in 10 seconds. The retry logic can wait for as long as 30 seconds, but thought that it is too much for a unit test. Just being conscious of CI time if it runs this test, but I am happy to see suggestions around it too. I am not that sure either it runs in CI, haven't figured it out entirely yet. The test can be considered an integration test too, but it exercises crate internal implementation, not the public API. Collators example logs after the change: ``` 2024-08-29 14:28:11.730 INFO tokio-runtime-worker reconnecting-websocket-client: [Parachain] Trying to connect to next external relaychain node. current_iteration=0 index=2 url="ws://127.0.0.1:37427/" 2024-08-29 14:28:12.737 INFO tokio-runtime-worker reconnecting-websocket-client: [Parachain] Trying to connect to next external relaychain node. current_iteration=1 index=0 url="ws://127.0.0.1:43617/" 2024-08-29 14:28:12.739 INFO tokio-runtime-worker reconnecting-websocket-client: [Parachain] Trying to connect to next external relaychain node. current_iteration=1 index=1 url="ws://127.0.0.1:37965/" 2024-08-29 14:28:12.755 INFO tokio-runtime-worker reconnecting-websocket-client: [Parachain] Trying to connect to next external relaychain node. current_iteration=1 index=2 url="ws://127.0.0.1:37427/" 2024-08-29 14:28:14.758 INFO tokio-runtime-worker reconnecting-websocket-client: [Parachain] Trying to connect to next external relaychain node. current_iteration=2 index=0 url="ws://127.0.0.1:43617/" 2024-08-29 14:28:14.759 INFO tokio-runtime-worker reconnecting-websocket-client: [Parachain] Trying to connect to next external relaychain node. current_iteration=2 index=1 url="ws://127.0.0.1:37965/" 2024-08-29 14:28:14.760 INFO tokio-runtime-worker reconnecting-websocket-client: [Parachain] Trying to connect to next external relaychain node. current_iteration=2 index=2 url="ws://127.0.0.1:37427/" 2024-08-29 14:28:18.766 INFO tokio-runtime-worker reconnecting-websocket-client: [Parachain] Trying to connect to next external relaychain node. current_iteration=3 index=0 url="ws://127.0.0.1:43617/" 2024-08-29 14:28:18.768 INFO tokio-runtime-worker reconnecting-websocket-client: [Parachain] Trying to connect to next external relaychain node. current_iteration=3 index=1 url="ws://127.0.0.1:37965/" 2024-08-29 14:28:18.768 INFO tokio-runtime-worker reconnecting-websocket-client: [Parachain] Trying to connect to next external relaychain node. current_iteration=3 index=2 url="ws://127.0.0.1:37427/" 2024-08-29 14:28:26.770 INFO tokio-runtime-worker reconnecting-websocket-client: [Parachain] Trying to connect to next external relaychain node. current_iteration=4 index=0 url="ws://127.0.0.1:43617/" ``` --------- Signed-off-by: Iulian Barbu Co-authored-by: Sebastian Kunert --- Cargo.lock | 1 + .../relay-chain-rpc-interface/Cargo.toml | 3 + .../src/reconnecting_ws_client.rs | 72 +++++++++++++++++-- prdoc/pr_5515.prdoc | 15 ++++ 4 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 prdoc/pr_5515.prdoc diff --git a/Cargo.lock b/Cargo.lock index 0d41be5c9bd1..e37b79cc58e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4457,6 +4457,7 @@ dependencies = [ "parity-scale-codec", "pin-project", "polkadot-overseer", + "portpicker", "rand", "sc-client-api", "sc-rpc-api", diff --git a/cumulus/client/relay-chain-rpc-interface/Cargo.toml b/cumulus/client/relay-chain-rpc-interface/Cargo.toml index 6c0730a56a26..c2deddc5341d 100644 --- a/cumulus/client/relay-chain-rpc-interface/Cargo.toml +++ b/cumulus/client/relay-chain-rpc-interface/Cargo.toml @@ -9,6 +9,9 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [lints] workspace = true +[dev-dependencies] +portpicker = "0.1.1" + [dependencies] polkadot-overseer = { workspace = true, default-features = true } diff --git a/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs b/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs index 48d35dd3a55e..dc0e9d697b46 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs @@ -34,7 +34,7 @@ use jsonrpsee::{ use sc_rpc_api::chain::ChainApiClient; use schnellru::{ByLength, LruMap}; use sp_runtime::generic::SignedBlock; -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; use tokio::sync::mpsc::{ channel as tokio_channel, Receiver as TokioReceiver, Sender as TokioSender, }; @@ -43,6 +43,9 @@ use url::Url; use crate::rpc_client::{distribute_header, RpcDispatcherMessage}; const LOG_TARGET: &str = "reconnecting-websocket-client"; +const DEFAULT_EXTERNAL_RPC_CONN_RETRIES: usize = 5; +const DEFAULT_SLEEP_TIME_MS_BETWEEN_RETRIES: u64 = 1000; +const DEFAULT_SLEEP_EXP_BACKOFF_BETWEEN_RETRIES: i32 = 2; /// Worker that should be used in combination with [`RelayChainRpcClient`]. /// @@ -93,16 +96,45 @@ struct RelayChainSubscriptions { best_subscription: Subscription, } -/// Try to find a new RPC server to connect to. +/// Try to find a new RPC server to connect to. Uses a naive retry +/// logic that does an exponential backoff in between iterations +/// through all URLs from the list. It uses a constant to tell how +/// many iterations of connection attempts to all URLs we allow. We +/// return early when a connection is made. async fn connect_next_available_rpc_server( urls: &Vec, starting_position: usize, ) -> Result<(usize, Arc), ()> { tracing::debug!(target: LOG_TARGET, starting_position, "Connecting to RPC server."); - for (counter, url) in urls.iter().cycle().skip(starting_position).take(urls.len()).enumerate() { + + let mut prev_iteration: u32 = 0; + for (counter, url) in urls + .iter() + .cycle() + .skip(starting_position) + .take(urls.len() * DEFAULT_EXTERNAL_RPC_CONN_RETRIES) + .enumerate() + { + // If we reached the end of the urls list, backoff before retrying + // connections to the entire list once more. + let Ok(current_iteration) = (counter / urls.len()).try_into() else { + tracing::error!(target: LOG_TARGET, "Too many connection attempts to the RPC servers, aborting..."); + break; + }; + if current_iteration > prev_iteration { + // Safe conversion given we convert positive i32s which are lower than u64::MAX. + tokio::time::sleep(Duration::from_millis( + DEFAULT_SLEEP_TIME_MS_BETWEEN_RETRIES * + DEFAULT_SLEEP_EXP_BACKOFF_BETWEEN_RETRIES.pow(prev_iteration) as u64, + )) + .await; + prev_iteration = current_iteration; + } + let index = (starting_position + counter) % urls.len(); tracing::info!( target: LOG_TARGET, + attempt = current_iteration, index, url, "Trying to connect to next external relaychain node.", @@ -112,6 +144,8 @@ async fn connect_next_available_rpc_server( Err(err) => tracing::debug!(target: LOG_TARGET, url, ?err, "Unable to connect."), }; } + + tracing::error!(target: LOG_TARGET, "Retrying to connect to any external relaychain node failed."); Err(()) } @@ -431,9 +465,14 @@ impl ReconnectingWebsocketWorker { #[cfg(test)] mod test { - use super::url_to_string_with_port; + use std::time::Duration; + + use super::{url_to_string_with_port, ClientManager}; + use jsonrpsee::Methods; use url::Url; + const SERVER_STARTUP_DELAY_SECONDS: u64 = 10; + #[test] fn url_to_string_works() { let url = Url::parse("wss://something/path").unwrap(); @@ -460,4 +499,29 @@ mod test { url_to_string_with_port(url) ); } + + #[tokio::test] + // Testing the retry logic at full means increasing CI with half a minute according + // to the current logic, so lets test it best effort. + async fn client_manager_retry_logic() { + let port = portpicker::pick_unused_port().unwrap(); + let server = jsonrpsee::server::Server::builder() + .build(format!("0.0.0.0:{}", port)) + .await + .unwrap(); + + // Start the server. + let server = tokio::spawn(async { + tokio::time::sleep(Duration::from_secs(SERVER_STARTUP_DELAY_SECONDS)).await; + server.start(Methods::default()) + }); + + // Start the client. Not exitting right away with an error means it + // is handling gracefully received connections refused while the server + // is starting. + let res = ClientManager::new(vec![format!("ws://127.0.0.1:{}", port)]).await; + assert!(res.is_ok()); + + server.await.unwrap(); + } } diff --git a/prdoc/pr_5515.prdoc b/prdoc/pr_5515.prdoc new file mode 100644 index 000000000000..60f43b922c7f --- /dev/null +++ b/prdoc/pr_5515.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add retry logic in relay chain rpc interface + +doc: + - audience: [ Node Dev, Node Operator ] + description: | + Added a basic retry logic for collators connecting to external RPC servers. The collator + will try for 5 times to connect to each RPC server from the provided list. In between + each iteration will wait a duration which will increase exponentailly by a factor of two. + The maximum time a collator can spend in the retry logic is 1 + 2 + 4 + 8 + 16 = 31 seconds. +crates: + - name: cumulus-relay-chain-rpc-interface + bump: minor From 89b41c57d5e070681b23db9fafcbfcceb92f39d9 Mon Sep 17 00:00:00 2001 From: bader y Date: Wed, 4 Sep 2024 06:30:57 -0400 Subject: [PATCH 253/382] Add parachain related parameters to `chain-spec-builder` (#4889) When using with `polkadot-parachain`, you usually need to specify the `relay_chain` and `para_id` fields in the chain spec. With this PR it can be achieved by specifying newly added `--para-id` and `--relay-chain` command line args, e.g: ``` chain-spec-builder create -r _runtime.wasm --para-id 100 --relay-chain xxx default ``` This was implemented by simple _json_ blobs merging. Additionally unit tests covering basic functionality were added. Also adds a fix for not overwriting the chain spec with the default config each time, swallowing not standard fields is also fixed. Fixes: #4873 --------- Co-authored-by: Sebastian Kunert Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> --- Cargo.lock | 2 + .../src/reference_docs/chain_spec_genesis.rs | 6 +- .../tests/chain_spec_builder_tests.rs | 61 ++++- prdoc/pr_4889.prdoc | 15 ++ .../bin/utils/chain-spec-builder/Cargo.toml | 4 + .../bin/utils/chain-spec-builder/bin/main.rs | 108 +------- .../bin/utils/chain-spec-builder/src/lib.rs | 245 ++++++++++++++++-- .../tests/expected/add_code_substitute.json | 42 +++ .../tests/expected/convert_to_raw.json | 38 +++ .../tests/expected/create_default.json | 37 +++ .../tests/expected/create_parachain.json | 39 +++ .../tests/expected/create_raw_storage.json | 38 +++ .../tests/expected/create_with_full.json | 58 +++++ .../expected/create_with_named_preset.json | 38 +++ .../tests/expected/create_with_params.json | 37 +++ .../tests/expected/create_with_patch.json | 43 +++ .../tests/expected/update_code.json | 40 +++ .../tests/expected/update_code_raw.json | 38 +++ .../input/chain_spec_conversion_test.json | 40 +++ .../tests/input/chain_spec_plain.json | 40 +++ .../tests/input/chain_spec_raw.json | 38 +++ .../tests/input/code_040506.blob | 1 + .../chain-spec-builder/tests/input/full.json | 40 +++ .../chain-spec-builder/tests/input/patch.json | 25 ++ .../utils/chain-spec-builder/tests/test.rs | 194 ++++++++++++++ 25 files changed, 1137 insertions(+), 130 deletions(-) create mode 100644 prdoc/pr_4889.prdoc create mode 100644 substrate/bin/utils/chain-spec-builder/tests/expected/add_code_substitute.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/expected/convert_to_raw.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/expected/create_default.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/expected/create_parachain.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/expected/create_raw_storage.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/expected/create_with_full.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/expected/create_with_named_preset.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/expected/create_with_params.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/expected/create_with_patch.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/expected/update_code.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/expected/update_code_raw.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_conversion_test.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_plain.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_raw.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/input/code_040506.blob create mode 100644 substrate/bin/utils/chain-spec-builder/tests/input/full.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/input/patch.json create mode 100644 substrate/bin/utils/chain-spec-builder/tests/test.rs diff --git a/Cargo.lock b/Cargo.lock index e37b79cc58e9..83c40e9a4c7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21195,8 +21195,10 @@ dependencies = [ "clap 4.5.11", "log", "sc-chain-spec", + "serde", "serde_json", "sp-tracing 16.0.0", + "substrate-test-runtime", ] [[package]] diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index 39e5993d020f..a2e22d1ed1eb 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -1,4 +1,4 @@ -//! # What is chain-spec. +//! # What is a chain specification //! //! A chain specification file defines the set of properties that are required to run the node as //! part of the chain. The chain specification consists of two main parts: @@ -165,8 +165,10 @@ #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", list_presets)] //! ## Displaying preset with given name #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", get_preset)] -//! ## Building chain-spec using given preset +//! ## Building a solo chain-spec (the default) using given preset #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", generate_chain_spec)] +//! ## Building a parachain chain-spec using given preset +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", generate_para_chain_spec)] //! //! [`RuntimeGenesisConfig`]: //! chain_spec_guide_runtime::runtime::RuntimeGenesisConfig diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs index d08f547bba61..cc273685fcb4 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs @@ -104,7 +104,66 @@ fn generate_chain_spec() { "bootNodes": [], "telemetryEndpoints": null, "protocolId": null, - "properties": null, + "properties": { "tokenDecimals": 12, "tokenSymbol": "UNIT" }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x123", + "patch": { + "bar": { + "initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL" + }, + "foo": { + "someEnum": { + "Data2": { + "values": "0x0c10" + } + }, + "someInteger": 200 + } + } + } + } + }); + assert_eq!(output, expected_output, "Output did not match expected"); +} + +#[test] +#[docify::export] +fn generate_para_chain_spec() { + let output = Command::new(get_chain_spec_builder_path()) + .arg("-c") + .arg("/dev/stdout") + .arg("create") + .arg("-c") + .arg("polkadot") + .arg("-p") + .arg("1000") + .arg("-r") + .arg(WASM_FILE_PATH) + .arg("named-preset") + .arg("preset_2") + .output() + .expect("Failed to execute command"); + + let mut output: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); + + //remove code field for better readability + if let Some(code) = output["genesis"]["runtimeGenesis"].as_object_mut().unwrap().get_mut("code") + { + *code = Value::String("0x123".to_string()); + } + + let expected_output = json!({ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "relay_chain": "polkadot", + "para_id": 1000, + "properties": { "tokenDecimals": 12, "tokenSymbol": "UNIT" }, "codeSubstitutes": {}, "genesis": { "runtimeGenesis": { diff --git a/prdoc/pr_4889.prdoc b/prdoc/pr_4889.prdoc new file mode 100644 index 000000000000..dfcdcd4f15bc --- /dev/null +++ b/prdoc/pr_4889.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add CLI options for parachain chain specifications + fix bug for swallowing custom fields + +doc: + - audience: Node Operator + description: | + Parachain ID and relay chain can be specified via the CLI arguments for when creating a chain spec. + A bug that also swallowed custom fields outside of the default config has also been fixed. + + +crates: + - name: staging-chain-spec-builder + bump: major diff --git a/substrate/bin/utils/chain-spec-builder/Cargo.toml b/substrate/bin/utils/chain-spec-builder/Cargo.toml index 070cf1309175..f2fe8cb7e166 100644 --- a/substrate/bin/utils/chain-spec-builder/Cargo.toml +++ b/substrate/bin/utils/chain-spec-builder/Cargo.toml @@ -28,4 +28,8 @@ clap = { features = ["derive"], workspace = true } log = { workspace = true, default-features = true } sc-chain-spec = { features = ["clap"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } +serde = { workspace = true, default-features = true } sp-tracing = { workspace = true, default-features = true } + +[dev-dependencies] +substrate-test-runtime = { workspace = true } diff --git a/substrate/bin/utils/chain-spec-builder/bin/main.rs b/substrate/bin/utils/chain-spec-builder/bin/main.rs index 39fa054b4806..9898a582dd14 100644 --- a/substrate/bin/utils/chain-spec-builder/bin/main.rs +++ b/substrate/bin/utils/chain-spec-builder/bin/main.rs @@ -16,19 +16,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use chain_spec_builder::{ - generate_chain_spec_for_runtime, AddCodeSubstituteCmd, ChainSpecBuilder, ChainSpecBuilderCmd, - ConvertToRawCmd, DisplayPresetCmd, ListPresetsCmd, UpdateCodeCmd, VerifyCmd, -}; +use chain_spec_builder::ChainSpecBuilder; use clap::Parser; -use sc_chain_spec::{ - set_code_substitute_in_json_chain_spec, update_code_in_json_chain_spec, GenericChainSpec, - GenesisConfigBuilderRuntimeCaller, -}; use staging_chain_spec_builder as chain_spec_builder; -use std::fs; - -type ChainSpec = GenericChainSpec<(), ()>; //avoid error message escaping fn main() { @@ -42,99 +32,5 @@ fn inner_main() -> Result<(), String> { sp_tracing::try_init_simple(); let builder = ChainSpecBuilder::parse(); - let chain_spec_path = builder.chain_spec_path.to_path_buf(); - - match builder.command { - ChainSpecBuilderCmd::Create(cmd) => { - let chain_spec_json = generate_chain_spec_for_runtime(&cmd)?; - fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; - }, - ChainSpecBuilderCmd::UpdateCode(UpdateCodeCmd { - ref input_chain_spec, - ref runtime_wasm_path, - }) => { - let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?; - - let mut chain_spec_json = - serde_json::from_str::(&chain_spec.as_json(false)?) - .map_err(|e| format!("Conversion to json failed: {e}"))?; - update_code_in_json_chain_spec( - &mut chain_spec_json, - &fs::read(runtime_wasm_path.as_path()) - .map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..], - ); - - let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json) - .map_err(|e| format!("to pretty failed: {e}"))?; - fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; - }, - ChainSpecBuilderCmd::AddCodeSubstitute(AddCodeSubstituteCmd { - ref input_chain_spec, - ref runtime_wasm_path, - block_height, - }) => { - let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?; - - let mut chain_spec_json = - serde_json::from_str::(&chain_spec.as_json(false)?) - .map_err(|e| format!("Conversion to json failed: {e}"))?; - - set_code_substitute_in_json_chain_spec( - &mut chain_spec_json, - &fs::read(runtime_wasm_path.as_path()) - .map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..], - block_height, - ); - let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json) - .map_err(|e| format!("to pretty failed: {e}"))?; - fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; - }, - ChainSpecBuilderCmd::ConvertToRaw(ConvertToRawCmd { ref input_chain_spec }) => { - let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?; - - let chain_spec_json = - serde_json::from_str::(&chain_spec.as_json(true)?) - .map_err(|e| format!("Conversion to json failed: {e}"))?; - - let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json) - .map_err(|e| format!("Conversion to pretty failed: {e}"))?; - fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; - }, - ChainSpecBuilderCmd::Verify(VerifyCmd { ref input_chain_spec }) => { - let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?; - let _ = serde_json::from_str::(&chain_spec.as_json(true)?) - .map_err(|e| format!("Conversion to json failed: {e}"))?; - }, - ChainSpecBuilderCmd::ListPresets(ListPresetsCmd { runtime_wasm_path }) => { - let code = fs::read(runtime_wasm_path.as_path()) - .map_err(|e| format!("wasm blob shall be readable {e}"))?; - let caller: GenesisConfigBuilderRuntimeCaller = - GenesisConfigBuilderRuntimeCaller::new(&code[..]); - let presets = caller - .preset_names() - .map_err(|e| format!("getting default config from runtime should work: {e}"))?; - let presets: Vec = presets - .into_iter() - .map(|preset| { - String::from( - TryInto::<&str>::try_into(&preset) - .unwrap_or_else(|_| "cannot display preset id") - .to_string(), - ) - }) - .collect(); - println!("{}", serde_json::json!({"presets":presets}).to_string()); - }, - ChainSpecBuilderCmd::DisplayPreset(DisplayPresetCmd { runtime_wasm_path, preset_name }) => { - let code = fs::read(runtime_wasm_path.as_path()) - .map_err(|e| format!("wasm blob shall be readable {e}"))?; - let caller: GenesisConfigBuilderRuntimeCaller = - GenesisConfigBuilderRuntimeCaller::new(&code[..]); - let preset = caller - .get_named_preset(preset_name.as_ref()) - .map_err(|e| format!("getting default config from runtime should work: {e}"))?; - println!("{preset}"); - }, - }; - Ok(()) + builder.run() } diff --git a/substrate/bin/utils/chain-spec-builder/src/lib.rs b/substrate/bin/utils/chain-spec-builder/src/lib.rs index 6c679f109a00..629edcf68568 100644 --- a/substrate/bin/utils/chain-spec-builder/src/lib.rs +++ b/substrate/bin/utils/chain-spec-builder/src/lib.rs @@ -117,11 +117,18 @@ //! [sp-genesis-builder-list]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.preset_names //! [sp-genesis-builder-get-preset]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset -use std::{fs, path::PathBuf}; - use clap::{Parser, Subcommand}; -use sc_chain_spec::{ChainType, GenericChainSpec, GenesisConfigBuilderRuntimeCaller}; +use sc_chain_spec::{ + json_patch, set_code_substitute_in_json_chain_spec, update_code_in_json_chain_spec, ChainType, + GenericChainSpec, GenesisConfigBuilderRuntimeCaller, +}; +use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::{ + borrow::Cow, + fs, + path::{Path, PathBuf}, +}; /// A utility to easily create a chain spec definition. #[derive(Debug, Parser)] @@ -158,9 +165,15 @@ pub struct CreateCmd { /// The chain type. #[arg(value_enum, short = 't', default_value = "live")] chain_type: ChainType, + /// The para ID for your chain. + #[arg(long, value_enum, short = 'p', requires = "relay_chain")] + pub para_id: Option, + /// The relay chain you wish to connect to. + #[arg(long, value_enum, short = 'c', requires = "para_id")] + pub relay_chain: Option, /// The path to runtime wasm blob. - #[arg(long, short)] - runtime_wasm_path: PathBuf, + #[arg(long, short, alias = "runtime-wasm-path")] + runtime: PathBuf, /// Export chainspec as raw storage. #[arg(long, short = 's')] raw_storage: bool, @@ -170,6 +183,10 @@ pub struct CreateCmd { verify: bool, #[command(subcommand)] action: GenesisBuildAction, + + /// Allows to provide the runtime code blob, instead of reading it from the provided file path. + #[clap(skip)] + code: Option>, } #[derive(Subcommand, Debug, Clone)] @@ -220,7 +237,8 @@ pub struct UpdateCodeCmd { /// Please note that the file will not be updated in-place. pub input_chain_spec: PathBuf, /// The path to new runtime wasm blob to be stored into chain-spec. - pub runtime_wasm_path: PathBuf, + #[arg(alias = "runtime-wasm-path")] + pub runtime: PathBuf, } /// Add a code substitute in the chain spec. @@ -237,7 +255,8 @@ pub struct AddCodeSubstituteCmd { /// Chain spec to be updated. pub input_chain_spec: PathBuf, /// New runtime wasm blob that should replace the existing code. - pub runtime_wasm_path: PathBuf, + #[arg(alias = "runtime-wasm-path")] + pub runtime: PathBuf, /// The block height at which the code should be substituted. pub block_height: u64, } @@ -253,16 +272,16 @@ pub struct ConvertToRawCmd { #[derive(Parser, Debug, Clone)] pub struct ListPresetsCmd { /// The path to runtime wasm blob. - #[arg(long, short)] - pub runtime_wasm_path: PathBuf, + #[arg(long, short, alias = "runtime-wasm-path")] + pub runtime: PathBuf, } /// Displays given preset #[derive(Parser, Debug, Clone)] pub struct DisplayPresetCmd { /// The path to runtime wasm blob. - #[arg(long, short)] - pub runtime_wasm_path: PathBuf, + #[arg(long, short, alias = "runtime-wasm-path")] + pub runtime: PathBuf, /// Preset to be displayed. If none is given default will be displayed. #[arg(long, short)] pub preset_name: Option, @@ -279,18 +298,146 @@ pub struct VerifyCmd { pub input_chain_spec: PathBuf, } -/// Processes `CreateCmd` and returns JSON version of `ChainSpec`. -pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result { - let code = fs::read(cmd.runtime_wasm_path.as_path()) - .map_err(|e| format!("wasm blob shall be readable {e}"))?; +#[derive(Deserialize, Serialize, Clone)] +pub struct ParachainExtension { + /// The relay chain of the Parachain. + pub relay_chain: String, + /// The id of the Parachain. + pub para_id: u32, +} - let chain_type = &cmd.chain_type; +type ChainSpec = GenericChainSpec<()>; - let builder = GenericChainSpec::<()>::builder(&code[..], Default::default()) - .with_name(&cmd.chain_name[..]) - .with_id(&cmd.chain_id[..]) - .with_chain_type(chain_type.clone()); +impl ChainSpecBuilder { + /// Executes the internal command. + pub fn run(self) -> Result<(), String> { + let chain_spec_path = self.chain_spec_path.to_path_buf(); + + match self.command { + ChainSpecBuilderCmd::Create(cmd) => { + let chain_spec_json = generate_chain_spec_for_runtime(&cmd)?; + fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; + }, + ChainSpecBuilderCmd::UpdateCode(UpdateCodeCmd { + ref input_chain_spec, + ref runtime, + }) => { + let mut chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?; + + update_code_in_json_chain_spec( + &mut chain_spec_json, + &fs::read(runtime.as_path()) + .map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..], + ); + + let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json) + .map_err(|e| format!("to pretty failed: {e}"))?; + fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; + }, + ChainSpecBuilderCmd::AddCodeSubstitute(AddCodeSubstituteCmd { + ref input_chain_spec, + ref runtime, + block_height, + }) => { + let mut chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?; + + set_code_substitute_in_json_chain_spec( + &mut chain_spec_json, + &fs::read(runtime.as_path()) + .map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..], + block_height, + ); + let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json) + .map_err(|e| format!("to pretty failed: {e}"))?; + fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; + }, + ChainSpecBuilderCmd::ConvertToRaw(ConvertToRawCmd { ref input_chain_spec }) => { + let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?; + + let mut genesis_json = + serde_json::from_str::(&chain_spec.as_json(true)?) + .map_err(|e| format!("Conversion to json failed: {e}"))?; + + // We want to extract only raw genesis ("genesis::raw" key), and apply it as a patch + // for the original json file. However, the file also contains original plain + // genesis ("genesis::runtimeGenesis") so set it to null so the patch will erase it. + genesis_json.as_object_mut().map(|map| { + map.retain(|key, _| key == "genesis"); + map.get_mut("genesis").map(|genesis| { + genesis.as_object_mut().map(|genesis_map| { + genesis_map + .insert("runtimeGenesis".to_string(), serde_json::Value::Null); + }); + }); + }); + + let mut org_chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?; + json_patch::merge(&mut org_chain_spec_json, genesis_json); + + let chain_spec_json = serde_json::to_string_pretty(&org_chain_spec_json) + .map_err(|e| format!("Conversion to pretty failed: {e}"))?; + fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; + }, + ChainSpecBuilderCmd::Verify(VerifyCmd { ref input_chain_spec }) => { + let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?; + let _ = serde_json::from_str::(&chain_spec.as_json(true)?) + .map_err(|e| format!("Conversion to json failed: {e}"))?; + }, + ChainSpecBuilderCmd::ListPresets(ListPresetsCmd { runtime }) => { + let code = fs::read(runtime.as_path()) + .map_err(|e| format!("wasm blob shall be readable {e}"))?; + let caller: GenesisConfigBuilderRuntimeCaller = + GenesisConfigBuilderRuntimeCaller::new(&code[..]); + let presets = caller + .preset_names() + .map_err(|e| format!("getting default config from runtime should work: {e}"))?; + let presets: Vec = presets + .into_iter() + .map(|preset| { + String::from( + TryInto::<&str>::try_into(&preset) + .unwrap_or_else(|_| "cannot display preset id") + .to_string(), + ) + }) + .collect(); + println!("{}", serde_json::json!({"presets":presets}).to_string()); + }, + ChainSpecBuilderCmd::DisplayPreset(DisplayPresetCmd { runtime, preset_name }) => { + let code = fs::read(runtime.as_path()) + .map_err(|e| format!("wasm blob shall be readable {e}"))?; + let caller: GenesisConfigBuilderRuntimeCaller = + GenesisConfigBuilderRuntimeCaller::new(&code[..]); + let preset = caller + .get_named_preset(preset_name.as_ref()) + .map_err(|e| format!("getting default config from runtime should work: {e}"))?; + println!("{preset}"); + }, + } + Ok(()) + } + + /// Sets the code used by [`CreateCmd`] + /// + /// The file pointed by `CreateCmd::runtime` field will not be read. Provided blob will used + /// instead for chain spec generation. + pub fn set_create_cmd_runtime_code(&mut self, code: Cow<'static, [u8]>) { + match &mut self.command { + ChainSpecBuilderCmd::Create(cmd) => { + cmd.code = Some(code); + }, + _ => { + panic!("Overwriting code blob is only supported for CreateCmd"); + }, + }; + } +} +fn process_action( + cmd: &CreateCmd, + code: &[u8], + builder: sc_chain_spec::ChainSpecBuilder, +) -> Result { let builder = match cmd.action { GenesisBuildAction::NamedPreset(NamedPresetCmd { ref preset_name }) => builder.with_genesis_config_preset_name(&preset_name), @@ -310,7 +457,7 @@ pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result { let caller: GenesisConfigBuilderRuntimeCaller = - GenesisConfigBuilderRuntimeCaller::new(&code[..]); + GenesisConfigBuilderRuntimeCaller::new(&code); let default_config = caller .get_default_config() .map_err(|e| format!("getting default config from runtime should work: {e}"))?; @@ -330,3 +477,59 @@ pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result chain_spec.as_json(false), } } + +impl CreateCmd { + /// Returns the associated runtime code. + /// + /// If the code blob was previously set, returns it. Otherwise reads the file. + fn get_runtime_code(&self) -> Result, String> { + Ok(if let Some(code) = self.code.clone() { + code + } else { + fs::read(self.runtime.as_path()) + .map_err(|e| format!("wasm blob shall be readable {e}"))? + .into() + }) + } +} + +/// Processes `CreateCmd` and returns string represenataion of JSON version of `ChainSpec`. +pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result { + let code = cmd.get_runtime_code()?; + + let chain_type = &cmd.chain_type; + + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "UNIT".into()); + properties.insert("tokenDecimals".into(), 12.into()); + + let builder = ChainSpec::builder(&code[..], Default::default()) + .with_name(&cmd.chain_name[..]) + .with_id(&cmd.chain_id[..]) + .with_properties(properties) + .with_chain_type(chain_type.clone()); + + let chain_spec_json_string = process_action(&cmd, &code[..], builder)?; + + if let (Some(para_id), Some(ref relay_chain)) = (cmd.para_id, &cmd.relay_chain) { + let parachain_properties = serde_json::json!({ + "relay_chain": relay_chain, + "para_id": para_id, + }); + let mut chain_spec_json_blob = serde_json::from_str(chain_spec_json_string.as_str()) + .map_err(|e| format!("deserialization a json failed {e}"))?; + json_patch::merge(&mut chain_spec_json_blob, parachain_properties); + Ok(serde_json::to_string_pretty(&chain_spec_json_blob) + .map_err(|e| format!("to pretty failed: {e}"))?) + } else { + Ok(chain_spec_json_string) + } +} + +/// Extract any chain spec and convert it to JSON +fn extract_chain_spec_json(input_chain_spec: &Path) -> Result { + let chain_spec = &fs::read(input_chain_spec) + .map_err(|e| format!("Provided chain spec could not be read: {e}"))?; + + serde_json::from_slice(&chain_spec).map_err(|e| format!("Conversion to json failed: {e}")) +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/add_code_substitute.json b/substrate/bin/utils/chain-spec-builder/tests/expected/add_code_substitute.json new file mode 100644 index 000000000000..b957b09f5646 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/add_code_substitute.json @@ -0,0 +1,42 @@ +{ + "bootNodes": [], + "chainType": "Live", + "codeSubstitutes": { + "100": "0x040506" + }, + "custom_field": "custom_value", + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + }, + "id": "custom", + "name": "Custom", + "para_id": 10101, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "protocolId": null, + "relay_chain": "rococo-local", + "telemetryEndpoints": null +} \ No newline at end of file diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/convert_to_raw.json b/substrate/bin/utils/chain-spec-builder/tests/expected/convert_to_raw.json new file mode 100644 index 000000000000..5b1b4e2f04cf --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/convert_to_raw.json @@ -0,0 +1,38 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "relay_chain": "rococo-local", + "para_id": 10101, + "custom_field": "custom_value", + "codeSubstitutes": {}, + "genesis": { + "raw": { + "childrenDefault": {}, + "top": { + "0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x00", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000", + "0x3a636f6465": "0x010203", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x0000000000000000" + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_default.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_default.json new file mode 100644 index 000000000000..ac67aef93345 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_default.json @@ -0,0 +1,37 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_parachain.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_parachain.json new file mode 100644 index 000000000000..7106b4b50dc5 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_parachain.json @@ -0,0 +1,39 @@ +{ + "name": "test_chain", + "id": "100", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "relay_chain": "rococo-local", + "para_id": 10101, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_raw_storage.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_raw_storage.json new file mode 100644 index 000000000000..0501d6cbe45b --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_raw_storage.json @@ -0,0 +1,38 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92c2a60ec6dd16cd8ab911865ecf7555b186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e": "0x00000000000000000000000001000000000000000080e03779c311000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000", + "0x3a636f6465": "0x010203", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00806d8176de1800" + }, + "childrenDefault": {} + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_full.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_full.json new file mode 100644 index 000000000000..6d127b6c0aca --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_full.json @@ -0,0 +1,58 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 2, + 4 + ] + } + }, + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 2000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 2000000000000000 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 5000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_named_preset.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_named_preset.json new file mode 100644 index 000000000000..2bf84281c59e --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_named_preset.json @@ -0,0 +1,38 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "patch": { + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL" + ] + } + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_params.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_params.json new file mode 100644 index 000000000000..5aedd5b5c18b --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_params.json @@ -0,0 +1,37 @@ +{ + "name": "test_chain", + "id": "100", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_patch.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_patch.json new file mode 100644 index 000000000000..f98be3d7cfbe --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_patch.json @@ -0,0 +1,43 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "patch": { + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1000000000000000 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 5000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ] + } + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/update_code.json b/substrate/bin/utils/chain-spec-builder/tests/expected/update_code.json new file mode 100644 index 000000000000..dde561a594ff --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/update_code.json @@ -0,0 +1,40 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "relay_chain": "rococo-local", + "para_id": 10101, + "custom_field": "custom_value", + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x040506", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/update_code_raw.json b/substrate/bin/utils/chain-spec-builder/tests/expected/update_code_raw.json new file mode 100644 index 000000000000..d8c558a0ccbb --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/update_code_raw.json @@ -0,0 +1,38 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92c2a60ec6dd16cd8ab911865ecf7555b186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e": "0x00000000000000000000000001000000000000000080e03779c311000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000", + "0x3a636f6465": "0x040506", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00806d8176de1800" + }, + "childrenDefault": {} + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_conversion_test.json b/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_conversion_test.json new file mode 100644 index 000000000000..6a390c0d38b1 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_conversion_test.json @@ -0,0 +1,40 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "relay_chain": "rococo-local", + "para_id": 10101, + "custom_field": "custom_value", + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x0061736d0100000001b4033b60017f0060017f017f60037f7f7f017f60027f7f017f60027f7f0060037f7f7f0060047f7f7f7f0060057f7f7f7f7f0060000060047f7f7f7f017f60067f7f7f7f7f7f0060087f7f7f7f7f7f7f7f006000017f60047f7e7e7e0060027e7e0060017e0060027e7e017e60017e017e60037e7e7f017e60017f017e60017e017f60027f7e017f60037f7f7e017e60037f7e7f017f60037f7e7e0060047e7e7e7f017e60037e7e7e0060027e7f017f60047f7f7e7e0060037e7f7f0060047e7e7f7f017f60067f7f7f7f7f7f017f60057f7f7f7f7f017f600f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f017f60077f7f7f7f7f7f7f0060047f7c7f7f017f60037f7c7f017f60037e7f7f017f60077f7f7f7f7f7f7f017f60097f7f7f7f7f7f7e7e7e0060067f7f7f7e7e7f0060097f7f7f7e7e7f7f7f7f0060027e7f0060027c7f017f60067f7f7e7e7f7f0060027f7e00600a7f7f7f7f7f7f7f7f7f7f006000017e60067f7f7e7f7f7f0060057f7f7f7e7f0060047f7f7f7e0060027f7f017e60057f7f7e7f7f0060037f7f7e0060027f7e017e600b7f7f7f7f7f7f7f7f7f7f7f0060037f7f7c0060057f7e7e7e7e0060047f7e7e7f0002800d2a03656e76066d656d6f727902001203656e761c6578745f73746f726167655f617070656e645f76657273696f6e5f31000e03656e761b6578745f73746f726167655f636c6561725f76657273696f6e5f31000f03656e76226578745f73746f726167655f636c6561725f7072656669785f76657273696f6e5f32001003656e76286578745f73746f726167655f636f6d6d69745f7472616e73616374696f6e5f76657273696f6e5f31000803656e76196578745f73746f726167655f6765745f76657273696f6e5f31001103656e761e6578745f73746f726167655f6e6578745f6b65795f76657273696f6e5f31001103656e761a6578745f73746f726167655f726561645f76657273696f6e5f31001203656e762a6578745f73746f726167655f726f6c6c6261636b5f7472616e73616374696f6e5f76657273696f6e5f31000803656e761a6578745f73746f726167655f726f6f745f76657273696f6e5f32001303656e76196578745f73746f726167655f7365745f76657273696f6e5f31000e03656e76276578745f73746f726167655f73746172745f7472616e73616374696f6e5f76657273696f6e5f31000803656e76206578745f68617368696e675f626c616b65325f3132385f76657273696f6e5f31001403656e76206578745f68617368696e675f626c616b65325f3235365f76657273696f6e5f31001403656e761e6578745f68617368696e675f74776f785f3132385f76657273696f6e5f31001403656e761d6578745f68617368696e675f74776f785f36345f76657273696f6e5f31001403656e76226578745f6f6666636861696e5f696e6465785f636c6561725f76657273696f6e5f31000f03656e76206578745f6f6666636861696e5f696e6465785f7365745f76657273696f6e5f31000e03656e76236578745f63727970746f5f65636473615f67656e65726174655f76657273696f6e5f31001503656e76266578745f63727970746f5f65636473615f7075626c69635f6b6579735f76657273696f6e5f31001303656e761f6578745f63727970746f5f65636473615f7369676e5f76657273696f6e5f31001603656e76216578745f63727970746f5f65636473615f7665726966795f76657273696f6e5f32001703656e76256578745f63727970746f5f656432353531395f67656e65726174655f76657273696f6e5f31001503656e76286578745f63727970746f5f656432353531395f7075626c69635f6b6579735f76657273696f6e5f31001303656e76216578745f63727970746f5f656432353531395f7369676e5f76657273696f6e5f31001603656e76236578745f63727970746f5f656432353531395f7665726966795f76657273696f6e5f31001703656e76256578745f63727970746f5f737232353531395f67656e65726174655f76657273696f6e5f31001503656e76286578745f63727970746f5f737232353531395f7075626c69635f6b6579735f76657273696f6e5f31001303656e76216578745f63727970746f5f737232353531395f7369676e5f76657273696f6e5f31001603656e76236578745f63727970746f5f737232353531395f7665726966795f76657273696f6e5f32001703656e76296578745f6f6666636861696e5f7375626d69745f7472616e73616374696f6e5f76657273696f6e5f31001103656e76256578745f7472616e73616374696f6e5f696e6465785f696e6465785f76657273696f6e5f31000503656e76196578745f6c6f6767696e675f6c6f675f76657273696f6e5f31001803656e761f6578745f6c6f6767696e675f6d61785f6c6576656c5f76657273696f6e5f31000c03656e761c6578745f616c6c6f6361746f725f667265655f76657273696f6e5f31000003656e761e6578745f616c6c6f6361746f725f6d616c6c6f635f76657273696f6e5f31000103656e76286578745f64656661756c745f6368696c645f73746f726167655f726561645f76657273696f6e5f31001903656e76276578745f64656661756c745f6368696c645f73746f726167655f7365745f76657273696f6e5f31001a03656e762a6578745f747269655f626c616b65325f3235365f6f7264657265645f726f6f745f76657273696f6e5f32001b03656e761c6578745f6d6973635f7072696e745f6865785f76657273696f6e5f31000f03656e761d6578745f6d6973635f7072696e745f757466385f76657273696f6e5f31000f03656e76226578745f6d6973635f72756e74696d655f76657273696f6e5f76657273696f6e5f31001103e40de20d0403000203080506040400000303040402031c0a00030406060105060a060605060605010606010508061d1e0300050302031f20020302210302030303030304030302222323240303070100000405050507220503250303030300030303000007020320020203030607030505050707260103030607270005050405040505050404040405040505040404040304040500040404040404040406050504040404000004040428050404040404040404010505040504000029050505010404040400060406040404050505050000000003030c00070004040404000000040400030100010004040400000000060404050505060604000304000703000604050707060404000000030605040205030004040506002a2b2c07040404040404040404060405050504040407060000000703220303031c03050500000000000000030007040303030300030303030203020000030303020c03030301010203020c0604000504030401060405030003020303030400000604050000000000000000000404040604040505050004040006040505000604040505050000040404040000000000000000000000000000060606060000000604000000000604060405050505000000000400000005000404040000000000000006040505050604042d2d2d040003030300040506010300040504070805050a080406080505050504060504070905040709050407090104020405070c040404040400010b0a05040405040404040000000405040404060606060606060606060606060404040505050505000000040404040004000003030300000304000004040500000000000000000000000607060405060707000403030300030003000605020605040000000404040604040505050400060000000000060400030000040607060707050505060505050505050505050505050505050506050505220a220a040000040404000000000307060505070605050505050506070706070704040a0a040606222206222206060705070707070700000a0a06060605040404040404040404040404040404040505050505050509030304040404040404040404040404040404040404040404040404050404040403030004092d09090209090909090300060000000000000000000000000000000000000000000005050004040404040400040404050407030305050404040400000000070404050504040404040404040404000000000000000004040400000304000000061400000000030404010506000705050504050400050400000406050f2d03030303030303000000030506040404060a0004030000000001050600040400000004040504050404042d042a040404040400030f000400070602030204050606050505000404040004040400040004040000000003030a000003030305031313130405030505050504040000030404000000000000000000000004040003050304030403060501000a0304050303040504030504040404050000000c0000000000000000080404040000040004040404040405000303030303030303030303030303030303030303030303000000000000070707070700070303030303040300040404040502040004030303030303030303030000000000000403030b0b2e2e06070506020404040406010911040000040400062f0000000804050800030003040500000000000500030504030404040004000000040000010500000000300008060506050508040308040000000401040000000404000000000000030d03000000050304040f050404040404040404000606040a030302053131313105320101070502030103010106070305000000000301090203090401040404040401040404040a200a0a0a220606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606040305050505050505053333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333330400000000000000000000000334353130360100000503040401040104000000040404040404040404030303030b0404040404000537040a07040404050607070707060404000400000000000000040404060404040606050707050506060203030400040406020400040f050001040008000506000004000600070406050504040403030000000306050505040206020404030000000000000000000000040138063535051c1c03030303030303030000000703060300010406151c3300040103332d040305050304000704050304040303040404090400030403050303030300000800080306050500000304393a39023902020202023a3a390239020407017001c404c4040619037f01418080c0000b7f004198a4c6000b7f0041a0a4c6000b078b0c33195f5f696e6469726563745f66756e6374696f6e5f7461626c6501000c436f72655f76657273696f6e00de0b12436f72655f657865637574655f626c6f636b00df0b15436f72655f696e697469616c697a655f626c6f636b00e00b114d657461646174615f6d6574616461746100e10b1c4d657461646174615f6d657461646174615f61745f76657273696f6e00e20b1a4d657461646174615f6d657461646174615f76657273696f6e7300e30b2b5461676765645472616e73616374696f6e51756575655f76616c69646174655f7472616e73616374696f6e00e40b1c426c6f636b4275696c6465725f6170706c795f65787472696e73696300e50b1b426c6f636b4275696c6465725f66696e616c697a655f626c6f636b00e60b20426c6f636b4275696c6465725f696e686572656e745f65787472696e7369637300e70b1c426c6f636b4275696c6465725f636865636b5f696e686572656e747300e80b1d4163636f756e744e6f6e63654170695f6163636f756e745f6e6f6e636500e90b12546573744150495f62616c616e63655f6f6600ea0b19546573744150495f62656e63686d61726b5f6164645f6f6e6500eb0b20546573744150495f62656e63686d61726b5f766563746f725f6164645f6f6e6500ec0b22546573744150495f66756e6374696f6e5f7369676e61747572655f6368616e67656400ed0b10546573744150495f7573655f7472696500ee0b1f546573744150495f62656e63686d61726b5f696e6469726563745f63616c6c00ef0b1d546573744150495f62656e63686d61726b5f6469726563745f63616c6c00f00b19546573744150495f7665635f776974685f636170616369747900f10b18546573744150495f6765745f626c6f636b5f6e756d62657200f20b1b546573744150495f746573745f656432353531395f63727970746f00f30b1b546573744150495f746573745f737232353531395f63727970746f00f40b19546573744150495f746573745f65636473615f63727970746f00f50b14546573744150495f746573745f73746f7261676500f60b14546573744150495f746573745f7769746e65737300f70b1f546573744150495f746573745f6d756c7469706c655f617267756d656e747300f80b14546573744150495f646f5f74726163655f6c6f6700f90b16546573744150495f7665726966795f6564323535313900fa0b17546573744150495f77726974655f6b65795f76616c756500fb0b15417572614170695f736c6f745f6475726174696f6e00fc0b13417572614170695f617574686f72697469657300fd0b15426162654170695f636f6e66696775726174696f6e00fe0b1b426162654170695f63757272656e745f65706f63685f737461727400ff0b15426162654170695f63757272656e745f65706f636800800c12426162654170695f6e6578745f65706f636800810c35426162654170695f7375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e73696300820c24426162654170695f67656e65726174655f6b65795f6f776e6572736869705f70726f6f6600830c214f6666636861696e576f726b65724170695f6f6666636861696e5f776f726b657200840c2153657373696f6e4b6579735f67656e65726174655f73657373696f6e5f6b65797300850c1f53657373696f6e4b6579735f6465636f64655f73657373696f6e5f6b65797300860c1e4772616e6470614170695f6772616e6470615f617574686f72697469657300870c194772616e6470614170695f63757272656e745f7365745f696400880c384772616e6470614170695f7375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e73696300890c274772616e6470614170695f67656e65726174655f6b65795f6f776e6572736869705f70726f6f66008a0c1a47656e657369734275696c6465725f6275696c645f7374617465008b0c1947656e657369734275696c6465725f6765745f707265736574008c0c1b47656e657369734275696c6465725f7072657365745f6e616d6573008d0c0a5f5f646174615f656e6403010b5f5f686561705f62617365030209e208010041010bc3042b2c2d2a800134393a3335364041424344464c48494a4b3d3e4d4e4f509301568a018b018f0167655a75687466860185018301840163b905f501f401f901f801fd01da02e302900282029102840288028a028302fa049d029e029f02a00255a302a202e502af02bb02e102d502d902db02e802e7026182018101f10264eb0271ec02ed02ef02f402f502ee02fe02fa02970384039603f802fc02fb029203f702f902930395039403a403a503a303a603b903cb03e402cd03c003ce03c703c103df03c803cc03d803f003cf03d003d103da03d903ea03f203ee03ef03f703fa03f803f603f903e002fb0389048a048b048d048c04900491048f049204950496049704980499049a049b049c049d049e049f04a604a704a804a904aa04ab04ac04ad04ae04af04b004b104b704b804c004c104c204c304c404c504a004a104a204a304a404a504be04bf04b604b204fe04f104e80483058605ff04a7038405ee04e704f204f5049d05f304f404f604f704f804f904e9049b019c01960595059405980593059905ae05a305a205b105b205b005b305b705e308e605dd05fb05e105fa05e005fe05d106dd06e206e903db0ca307aa0aa407a707850d830db403e106b602a90ad70cc508e306ff068c0987079b039a03dd03a0039f03ae078d09d303900c940ca607ff02db07d907d707da078003d807d2078709d5079709e602850582058005e607a708820df6099202ef069908ea068509f0079d08f0069f08ee06a208e706e506a508e906b508a908ad08aa08900999098e09dd0a9809c308de03a203a103c708c908ca08b109cb08c20ae901cf08d108de08df08d508db08b805c208c608fd08f206f106f004a308eb06ab09a0098607a209af089e09ad09b0099d099209b80c8a0988098b098609c10996099b09ab08ba0cd50bb90c8f0994099f099c09a109aa099109ac099509a40aa50aa30ab807b707c108ac0cad0cab0cda0cd50c8705b405e606930ceb038105e707fc03e406d60ce907a107e006a105e307a507a207c408ec06e003d90cc903970c960cca03c603920c950ca4088805910ce8068f0cf406e807f409c808f509f709cc09ed0cc10dc709b409b509e30cec0ce60cea0ce80cc509be09d407d909da09d209d609d309d509d409c809f30cf40cc609d709d809e40c9b0de70ceb0ce90ccd099c0d9d0dc70ddb09c209c60ddc09b209c409c309f109e50793099a0a7f9a09f00c9302f2078c02ba058702e202fa09ae0ab30ab50aaf0ab20af302910de00abc09df0af2028909bd09bb09d006c009a608bf09cf06b70cb00ad107f601ae02ed06b103f506f306e009fb08d80cec03fa08d203f701a00db502900d9a0cd607c10cc00cc40cbe0cbf0cfe09ee0cad02a807ac02940d8b088a0889088c088e088d08930dd307b70db80dbe0d70b90dba0dbf0db50dbb0dbc0dbd0dae0db00db10db30db40db20dc20daf0db60d58c40dcb0dcc0dd20dd50dcd0dca0dd30dd40dce0dd60dcf0dd00dc80dd10dc90dd70dc30de50de60dec0def0df30dee0ded0dea0df80deb0df90d0afd9e49e20d0d002000200110b780808000000b12002000418080c08000200110d9808080000b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0b4b01017f02402000280200200028020822036b20024f0d0020002003200210af80808000200028020821030b200028020420036a2001200210848e8080001a2000200320026a36020841000bf20201027f23808080800041106b220224808080800002400240024002402001418001490d002002410036020c2001418010490d0102402001418080044f0d0020022001413f71418001723a000e20022001410c7641e001723a000c20022001410676413f71418001723a000d410321010c030b20022001413f71418001723a000f20022001410676413f71418001723a000e20022001410c76413f71418001723a000d2002200141127641077141f001723a000c410421010c020b0240200028020822032000280200470d002000200310b180808000200028020821030b200028020420036a20013a00002000200028020841016a3602080c020b20022001413f71418001723a000d2002200141067641c001723a000c410221010b02402000280200200028020822036b20014f0d0020002003200110af80808000200028020821030b200028020420036a2002410c6a200110848e8080001a2000200320016a3602080b200241106a24808080800041000b4e01017f23808080800041206b2200248080808000200041146a42003702002000410136020c2000419c81c080003602082000419880c08000360210200041086a41a481c0800010f680808000000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a10b080808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b860201017f024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d0020020d02410121010c030b20032802002103200241002802c8a3c68000118180808000002201450d0320012003200410848e8080001a200341002802c0a3c68000118080808000000c020b20020d00410121010c010b41002d00fca3c680001a200241002802c8a3c68000118180808000002201450d010b20002001360204200041086a2002360200200041003602000f0b20004101360204200041086a2002360200200041013602000f0b20004100360204200041086a2002360200200041013602000f0b20004100360204200041013602000be00101037f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014108200141084b1b2201417f73411f7621040240024020030d00200241003602180c010b2002200336021c20024101360218200220002802043602140b200241086a20042001200241146a10b080808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000b0d002001200010a980808000000b02000b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0b2100200128021441b481c080004105200141186a28020028020c118280808000000b2100200128021441b981c08000410b200141186a28020028020c118280808000000bc60101017f23808080800041306b22022480808080002002200036020c024041002d00fda3c680000d002002411c6a420137020020024102360214200241e881c08000360210200241858080800036022c2002200241286a36021820022002410c6a360228200241106a410041e882c0800010f780808000000b2002411c6a420137020020024102360214200241e881c08000360210200241858080800036022c2002200241286a36021820022002410c6a360228200241106a41f882c0800010f680808000000bb80301077f23808080800041106b220224808080800002400240024002400240024020012802042203450d00200128020021042003410371210502400240200341044f0d0041002103410021060c010b2004411c6a21072003417c712108410021034100210603402007280200200741786a280200200741706a280200200741686a28020020036a6a6a6a2103200741206a21072008200641046a2206470d000b0b02402005450d00200641037420046a41046a21070340200728020020036a2103200741086a21072005417f6a22050d000b0b02402001410c6a280200450d0020034100480d012003411049200428020445710d01200341017421030b20030d010b41012107410021030c010b2003417f4c0d0141002d00fca3c680001a200341002802c8a3c68000118180808000002207450d020b2002410036020820022007360204200220033602002002418883c08000200110d980808000450d0241a083c0800041332002410f6a41d483c0800041d484c08000108981808000000b10ae80808000000b4101200310b280808000000b20002002290200370200200041086a200241086a280200360200200241106a2480808080000b4b01017f02402000280200200028020822036b20024f0d0020002003200210af80808000200028020821030b200028020420036a2001200210848e8080001a2000200320026a36020841000bf20201027f23808080800041106b220224808080800002400240024002402001418001490d002002410036020c2001418010490d0102402001418080044f0d0020022001413f71418001723a000e20022001410c7641e001723a000c20022001410676413f71418001723a000d410321010c030b20022001413f71418001723a000f20022001410676413f71418001723a000e20022001410c76413f71418001723a000d2002200141127641077141f001723a000c410421010c020b0240200028020822032000280200470d002000200310b180808000200028020821030b200028020420036a20013a00002000200028020841016a3602080c020b20022001413f71418001723a000d2002200141067641c001723a000c410221010b02402000280200200028020822036b20014f0d0020002003200110af80808000200028020821030b200028020420036a2002410c6a200110848e8080001a2000200320016a3602080b200241106a24808080800041000b892e07027e017f087e017f0a7e017f157e200020002903102204200129002022057c200041306a220629030022077c2208200129002822097c200820028542ebfa86dabfb5f6c11f85422089220a42abf0d3f4afeebcb73c7c220b200785422889220c7c220d200129006022027c2000290318220e200129003022087c200041386a220f29030022107c2211200129003822127c201120038542f9c2f89b91a3b3f0db0085422089220342f1edf4f8a5a7fda7a57f7c221120108542288922137c2214200385423089221520117c221620138542018922177c2218200129006822037c201820002903082219200129001022117c200041286a221a290300221b7c221c200129001822137c201c429fd8f9d9c291da829b7f85422089221c42bbceaaa6d8d0ebb3bb7f7c221d201b85422889221e7c221f201c85423089222085422089222120002903002222200129000022187c200029032022237c22242001290008221c7c200029034020248542d1859aeffacf9487d100854220892224428892f39dffccf984ea007c222520238542288922267c2227202485423089222820257c22257c2229201785422889222a7c222b200129004822177c201f200129005022247c200d200a85423089220d200b7c221f200c85420189220b7c220c2001290058220a7c200c202885422089220c20167c2216200b85422889220b7c2228200c85423089222c20167c2216200b85420189222d7c222e2001290078220b7c202e20142001290070220c7c202520268542018922147c2225200b7c2025200d85422089220d2020201d7c221d7c222020148542288922147c2225200d85423089222685422089222e20272001290040220d7c201d201e85420189221d7c221e20177c201e2015854220892215201f7c221e201d85422889221d7c221f2015854230892215201e7c221e7c2227202d85422889222d7c222f200a7c202520037c202b202185423089222120297c2225202a8542018922297c222a20087c202a201585422089221520167c221620298542288922297c222a201585423089221520167c221620298542018922297c222b20127c202b202820057c201e201d85420189221d7c221e200d7c201e202185422089221e202620207c22207c2221201d85422889221d7c2226201e85423089221e854220892228201f200c7c202020148542018922147c221f20247c201f202c85422089221f20257c222020148542288922147c2225201f85423089221f20207c22207c222b20298542288922297c222c20097c202620187c202f202e85423089222620277c2227202d85420189222d7c222e20117c202e201f85422089221f20167c2216202d85422889222d7c222e201f85423089221f20167c2216202d85420189222d7c222f20117c202f202a20097c202020148542018922147c222020137c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222a2025201c7c201e201d85420189221d7c221e20027c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202d85422889222d7c222f20127c2026200b7c202c2028854230892226202b7c222820298542018922297c222b20037c202b201585422089221520167c221620298542288922297c222b201585423089221520167c221620298542018922297c222c201c7c202c202e20027c201e201d85420189221d7c221e20187c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222c2025200a7c202020148542018922147c2220200d7c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20037c202620137c202f202a85423089222620277c2227202d85420189222a7c222d20087c202d201f85422089221f20167c2216202a85422889222a7c222d201f85423089221f20167c2216202a85420189222a7c222f20027c202f202b20177c202020148542018922147c222020057c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222b202520247c201e201d85420189221d7c221e200c7c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f20057c2026200a7c202e202c85423089222620287c222820298542018922297c222c200c7c202c201585422089221520167c221620298542288922297c222c201585423089221520167c221620298542018922297c222e20187c202e202d20137c201e201d85420189221d7c221e201c7c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222d202520127c202020148542018922147c222020177c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20117c202620097c202f202b85423089222620277c2227202a85420189222a7c222b20247c202b201f85422089221f20167c2216202a85422889222a7c222b201f85423089221f20167c2216202a85420189222a7c222f20057c202f202c200b7c202020148542018922147c2220200d7c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222c202520117c201e201d85420189221d7c221e20087c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f20087c202620247c202e202d85423089222620287c222820298542018922297c222d200b7c202d201585422089221520167c221620298542288922297c222d201585423089221520167c221620298542018922297c222e200d7c202e202b20097c201e201d85420189221d7c221e20127c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222b202520177c202020148542018922147c222020187c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20187c2026200a7c202f202c85423089222620277c2227202a85420189222a7c222c20027c202c201f85422089221f20167c2216202a85422889222a7c222c201f85423089221f20167c2216202a85420189222a7c222f200a7c202f202d20137c202020148542018922147c222020037c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222d2025200c7c201e201d85420189221d7c221e201c7c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f200b7c2026200d7c202e202b85423089222620287c222820298542018922297c222b20137c202b201585422089221520167c221620298542288922297c222b201585423089221520167c221620298542018922297c222e200c7c202e202c20087c201e201d85420189221d7c221e20247c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222c202520117c202020148542018922147c222020027c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e200c7c202620127c202f202d85423089222620277c2227202a85420189222a7c222d20097c202d201f85422089221f20167c2216202a85422889222a7c222d201f85423089221f20167c2216202a85420189222a7c222f20037c202f202b201c7c202020148542018922147c222020177c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222b202520057c201e201d85420189221d7c221e20037c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f20177c202620057c202e202c85423089222620287c222820298542018922297c222c20247c202c201585422089221520167c221620298542288922297c222c201585423089221520167c221620298542018922297c222e20117c202e202d201c7c201e201d85420189221d7c221e200b7c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222d202520027c202020148542018922147c222020097c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20027c202620087c202f202b85423089222620277c2227202a85420189222a7c222b20137c202b201f85422089221f20167c2216202a85422889222a7c222b201f85423089221f20167c2216202a85420189222a7c222f201c7c202f202c200d7c202020148542018922147c2220200a7c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222c202520187c201e201d85420189221d7c221e20127c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f200d7c202620137c202e202d85423089222620287c222820298542018922297c222d20177c202d201585422089221520167c221620298542288922297c222d201585423089221520167c221620298542018922297c222e20087c202e202b20127c201e201d85420189221d7c221e200c7c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222b202520037c202020148542018922147c2220200a7c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e200a7c2026200b7c202f202c85423089222620277c2227202a85420189222a7c222c20057c202c201f85422089221f20167c2216202a85422889222a7c222c201f85423089221f20167c2216202a85420189222a7c222f20137c202f202d20117c202020148542018922147c222020247c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222d202520097c201e201d85420189221d7c221e20187c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f201c7c202620187c202e202b85423089222620287c222820298542018922297c222b200d7c202b201585422089221520167c221620298542288922297c222b201585423089221520167c221620298542018922297c222e20057c202e202c200c7c201e201d85420189221d7c221e20177c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222c202520087c202020148542018922147c2220200b7c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20127c202620037c202f202d85423089222620277c2227202a85420189222a7c222d20127c202d201f85422089221f20167c2216202a85422889222a7c222d201f85423089221f20167c2216202a85420189222a7c222f20087c202f202b20247c202020148542018922147c222020097c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222b202520027c201e201d85420189221d7c221e20117c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f20137c2026201c7c202e202c85423089222620287c222820298542018922297c222c20097c202c201585422089221520167c221620298542288922297c222c201585423089221520167c221620298542018922297c222e20027c202e202d200d7c201e201d85420189221d7c221e20057c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222d202520247c202020148542018922147c222020117c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20057c202620177c202f202b85423089222620277c2227202a85420189222a7c222b200c7c202b201f85422089221f20167c2216202a85422889222a7c222b201f85423089221f20167c2216202a85420189222a7c222f20097c202f202c20037c202020148542018922147c222020187c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222c2025200b7c201e201d85420189221d7c221e200a7c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f20027c202620087c202e202d85423089222620287c222820298542018922297c222d20127c202d201585422089221520167c221620298542288922297c222d201585423089221520167c221620298542018922297c222e20037c202e202b20117c201e201d85420189221d7c221e20137c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222b202520187c202020148542018922147c2220201c7c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20177c202620247c202f202c85423089222620277c2227202a85420189222a7c222c200a7c202c201f85422089221f20167c2216202a85422889222a7c222c201f85423089221f20167c2216202a85420189222a7c222f200b7c202f202d200c7c202020148542018922147c2220200b7c2020202685422089220b201e20217c221e7c222020148542288922147c2221200b85423089220b8542208922262025200d7c201e201d85420189221d7c221e20177c201e201585422089221720277c2215201d85422889221d7c221e201785423089221720157c22157c2225202a8542288922277c222a200a7c202120037c202e202b85423089220320287c220a20298542018922217c222820087c2028201785422089220820167c221720218542288922167c2221200885423089220820177c221720168542018922167c222820127c2028202c20057c2015201d8542018922057c2212200d7c20122003854220892212200b20207c22037c220b20058542288922057c220d2012854230892212854220892215201e200c7c200320148542018922037c220c20247c200c201f854220892224200a7c220a20038542288922037c220c2024854230892224200a7c220a7c221420168542288922167c221d200485200c201c7c2012200b7c221220058542018922057c221c20027c201c2008854220892202202a202685423089220820257c221c7c220b20058542288922057c220c2002854230892202200b7c220b853703102000200e2013202120097c200a20038542018922097c22037c2003200885422089220820127c221220098542288922097c2203852011200d20187c201c20278542018922137c22187c2018202485422089221120177c221820138542288922137c221c201185423089221120187c2218853703182000201c2019852003200885423089220820127c2212853703082000200c202285201d201585423089220320147c221c85370300201a201b200b20058542018985200385370300200f2010201c2016854201898520028537030020062007201820138542018985200885370300200020232012200985420189852011853703200b8b0601097f0240024002400240024020020d00410021060c010b200120026a210720052d008001210841002106200121094100210a03400240024020092c0000220b4100480d002005200b41ff0171220b6a2d0000220c41ff01470d012000200a3602042000200b3602000f0b2000200a3602042000418280c4003602000f0b02400240200620044b0d00200320066a210d2006450d01024002402006410371220e0d002003210b0c010b2003210b0340200b200b2d0000413a6c200c6a220c3a0000200b41016a210b200c410876210c200e417f6a220e0d000b0b20064104490d010340200b200b2d0000413a6c200c6a220c3a0000200b41016a220e200e2d0000413a6c200c4108766a220c3a0000200b41026a220e200e2d0000413a6c200c4108766a220c3a0000200b41036a220e200e2d0000413a6c200c4108766a220c3a0000200c410876210c200b41046a220b200d470d000c020b0b20062004418c88c08000109581808000000b0240200c450d00200620044f0d04200d200c3a0000200641016a21060b200a41016a210a200941016a22092007470d000b20062004200620044b1b210e200841ff0171210c0240034020012d0000200c470d01200320066a410020062004491b210b200e2006460d03200141016a2101200b41003a0000200641016a21062002417f6a22020d000b0b200620044b0d0320064102490d00200320066a200641017622046b210a4100210b024020044101460d002006417f6a210c200441feffffff077121014100210b03402003200c6a220d2d00002109200d2003200b6a220e2d00003a0000200e20093a0000200a2004200b417e736a6a220d2d00002109200d200e41016a220e2d00003a0000200e20093a0000200c417e6a210c2001200b41026a220b470d000b0b2006410271450d002003200b6a220c2d0000210e200c200a2004200b417f736a6a220b2d00003a0000200b200e3a00000b2000418380c400360200200020063602040f0b2000200b3602042000418080c4003602000f0b2000428080c4003702000f0b2006200441fc87c08000109581808000000b02000b21002001280214419c88c08000410b200141186a28020028020c118280808000000bd20101037f200128020421020240024002402001280208220320012802002204460d0041002d00fca3c680001a410c41002802c8a3c68000118180808000002201450d0220014101360208200120043602042001200236020041908ac0800021040c010b024020030d00418489c08000210441002101419c88c0800021020c010b41a489c08000210402402002410171450d00200221010c010b20024101722101419489c0800021040b2000200136020c2000200336020820002002360204200020043602000f0b4104410c10b280808000000b22002000410036020c20002003360208200020023602042000418489c080003602000b7801017f024002400240024020030d00410121040c010b2003417f4c0d0141002d00fca3c680001a200341002802c8a3c68000118180808000002204450d020b20042002200310848e80800021022000200336020820002002360204200020033602000f0b10ae80808000000b4101200310b280808000000b040041000b02000b6b01017f024020012802002204410171450d002000200120042004417e712002200310c5808080000f0b20042004280208220141016a36020802402001417f4c0d002000200436020c2000200336020820002002360204200041908ac080003602000f0b10d180808000000be00101017f41002d00fca3c680001a02400240410c41002802c8a3c68000118180808000002206450d0020064102360208200620033602002006200420036b20056a360204200120062001280200220320032002461b360200024020032002470d002000200636020c2000200536020820002004360204200041908ac080003602000f0b20032003280208220241016a3602082002417f4c0d012000200336020c2000200536020820002004360204200041908ac08000360200200641002802c0a3c68000118080808000000f0b4104410c10b280808000000b10d180808000000b4d00024020012802002201410171450d002001417e712002200310fe8d808000210120002003360208200020013602042000200220036a20016b3602000f0b200020012002200310c7808080000bdb0201037f23808080800041106b220424808080800041012105200141002001280208220620064101461b3602080240024002400240024020064101470d002001280204210620012802002105200141002802c0a3c6800011808080800000200020052002200310fe8d808000360204200020063602000c010b02402003450d002003417f4c0d0241002d00fca3c680001a200341002802c8a3c68000118180808000002205450d030b20052002200310848e8080002102200120012802082206417f6a360208024020064101470d00200141046a280200417f4c0d04200128020041002802c0a3c6800011808080800000200141002802c0a3c68000118080808000000b20002002360204200020033602000b20002003360208200441106a2480808080000f0b10ae80808000000b4101200310b280808000000b41b489c08000412b2004410f6a41e089c0800041808ac08000108981808000000bea0101017f23808080800041106b2203248080808000024002400240024020002802002200410171450d00200120026a2000417e7122006b417f4c0d02200041002802c0a3c68000118080808000000c010b200020002802082202417f6a36020820024101470d00200041046a280200417f4c0d02200028020041002802c0a3c6800011808080800000200041002802c0a3c68000118080808000000b200341106a2480808080000f0b41b489c08000412b2003410f6a41e089c0800041f089c08000108981808000000b41b489c08000412b2003410f6a41e089c0800041808ac08000108981808000000b6801017f024020012802002204410171450d0020002001200420042002200310c5808080000f0b20042004280208220141016a36020802402001417f4c0d002000200436020c2000200336020820002002360204200041908ac080003602000f0b10d180808000000b4a00024020012802002201410171450d0020012002200310fe8d808000210120002003360208200020013602042000200220036a20016b3602000f0b200020012002200310c7808080000be50101017f23808080800041106b2203248080808000024002400240024020002802002200410171450d00200120026a20006b417f4c0d02200041002802c0a3c68000118080808000000c010b200020002802082202417f6a36020820024101470d00200041046a280200417f4c0d02200028020041002802c0a3c6800011808080800000200041002802c0a3c68000118080808000000b200341106a2480808080000f0b41b489c08000412b2003410f6a41e089c0800041f089c08000108981808000000b41b489c08000412b2003410f6a41e089c0800041808ac08000108981808000000b2301017f410121010240200028020022004101710d00200028020841014621010b20010b4901017f200128020022012001280208220441016a36020802402004417f4a0d0010d180808000000b2000200136020c2000200336020820002002360204200041908ac080003602000b1300200020012802002002200310c7808080000b0d0020002802002802084101460b960101027f23808080800041106b22032480808080002000280200220020002802082204417f6a3602080240024020044101470d00200041046a280200417f4c0d01200028020041002802c0a3c6800011808080800000200041002802c0a3c68000118080808000000b200341106a2480808080000f0b41b489c08000412b2003410f6a41e089c0800041808ac08000108981808000000b170041a08ac08000410541808bc0800010f880808000000bd10805017f027e017f017e027f23808080800041e0006b22042480808080002004200336023c02400240024002402003417e6a41234f0d0020020d01200041003a00010c020b200441cc006a420137020020044101360244200441cc8bc08000360240200441858080800036025c2004200441d8006a36024820042004413c6a360258200441c0006a41c48cc0800010f680808000000b0240024002400240024020012d000041556a0e03010200020b20024101460d03200141016a21012003ad2105024002400240200241104b0d0020034111490d010b024002402003410a4b0d002002417f6a21024200210603402002450d07200441286a20062006423f872005420010878e80800020012d000041506a220720034f0d08200429033020042903282208423f87520d04200141016a21012002417f6a21022007ad2206420055200820067d220620085373450d000c020b0b2002417f6a21024200210603402002450d06200441186a20062006423f872005420010878e8080002004290318210820042903202106024020012d0000220941506a2207410a490d00417f2009412072220741a97f6a220920092007419f7f6a491b220720034f0d080b20062008423f87520d03200141016a21012002417f6a21022007ad2206420055200820067d220620085373450d000b0b200041033a00010c060b02402003410a4b0d002002417f6a210242002106034020012d000041506a220720034f0d06200141016a2101200620057e2007ad7d21062002417f6a22020d000c050b0b2002417f6a2107420021060340024020012d0000220941506a2202410a490d00417f2009412072220241a97f6a220920092002419f7f6a491b220220034f0d060b200141016a2101200620057e2002ad7d21062007417f6a22070d000c040b0b200041033a00010c040b2002417f6a2202450d02200141016a21010b2003ad210802400240200341104b0d0020024110490d010b2003410b49210a42002106024003402002450d03200441086a20062006423f872008420010878e80800020012d0000220941506a2107200429030821052004290310210602400240200a0d002007410a490d01417f2009412072220741a97f6a220920092007419f7f6a491b21070b200720034f0d050b20062005423f87520d01200141016a21012002417f6a21022007ad2206420053200520067c220620055373450d000b200041023a00010c040b200041023a00010c030b02402003410a4b0d0042002106034020012d000041506a220720034f0d03200141016a2101200620087e2007ad7c21062002417f6a22020d000c020b0b420021060340024020012d0000220941506a2207410a490d00417f2009412072220741a97f6a220920092007419f7f6a491b220720034f0d030b200141016a2101200620087e2007ad7c21062002417f6a22020d000b0b20002006370308410021010c020b41012101200041013a00010c010b410121010b200020013a0000200441e0006a2480808080000bc50605027f017e017f017e027f02402002280200220341134d0d00024002400240200042808084fea6dee111540d002002200341706a2204360200200120046a2000200042808084fea6dee11180220542808084fea6dee1117e7d2200428080e983b1de1680a741017441da8dc080006a2f00003b0000200320016a2206417c6a200042e40080220742e40082a741017441da8dc080006a2f00003b00002006417a6a20004290ce008042e40082a741017441da8dc080006a2f00003b0000200641786a200042c0843d8042e40082a741017441da8dc080006a2f00003b0000200641766a20004280c2d72f80a741e4007041017441da8dc080006a2f00003b0000200641746a20004280c8afa02580a741e4007041017441da8dc080006a2f00003b0000200641726a20004280a094a58d1d80a741ffff037141e4007041017441da8dc080006a2f00003b00002000200742e4007e7da721060c010b024020004280c2d72f5a0d0020032104200021050c020b2001200341786a22046a200020004280c2d72f8022054280c2d72f7e7da7220641c0843d6e41017441da8dc080006a2f00003b0000200320016a2208417c6a200641e4006e220941e4007041017441da8dc080006a2f00003b00002008417a6a20064190ce006e41ffff037141e4007041017441da8dc080006a2f00003b00002006200941e4006c6b21060b200320016a417e6a200641017441da8dc080006a2f00003b00000b024002402005a722084190ce004f0d0020042103200821060c010b20012004417c6a22036a200820084190ce006e22064190ce006c6b220841ffff037141e4006e220941017441da8dc080006a2f00003b0000200420016a417e6a2008200941e4006c6b41ffff037141017441da8dc080006a2f00003b00000b02400240200641ffff0371220441e4004f0d00200621040c010b20012003417e6a22036a2006200441e4006e220441e4006c6b41ffff037141017441da8dc080006a2f00003b00000b0240200441ffff0371410a490d0020012003417e6a22036a200441ffff037141017441da8dc080006a2f00003b0000200220033602000f0b20012003417f6a22036a200441306a3a0000200220033602000f0b41a28fc08000411c41c08fc0800010f880808000000bd10403017f027e017f2380808080004190016b22042480808080002004412736028c0102400240200142808020540d00200441306a2000420042f3b2d8c19e9ebdcc957f420010878e808000200441206a2000420042d2e1aadaeda7c987f600420010878e808000200441d0006a2001420042f3b2d8c19e9ebdcc957f420010878e808000200441c0006a2001420042d2e1aadaeda7c987f600420010878e808000200441c0006a41086a290300200441206a41086a290300200441306a41086a290300220520042903207c2201200554ad7c220620042903407c2205200654ad7c2005200441d0006a41086a290300200120042903507c200154ad7c7c2201200554ad7c2206423e8821052001423e8820064202868421010c010b20004213882001422d868442bda282a38eab04802101420021050b200441106a20012005428080e0b0b79fb79cf500420010878e808000200429031020007c200441e5006a2004418c016a10d380808000200428028c012107024020012005844200510d00200441e5006a41146a41302007416c6a108a8e8080001a2004411436028c01200420014213882005422d8684220542bda282a38eab048022002001428080e0b0b79fb79cf500420010878e808000200429030020017c200441e5006a2004418c016a10d380808000200428028c012107200542bda282a38eab04540d00200441e6006a41302007417f6a108a8e8080001a20042000a74130723a0065410021070b2003200241d08fc080004100200441e5006a20076a412720076b10db80808000210720044190016a24808080800020070ba60101037f2380808080004180016b220224808080800020002d00002103410021000340200220006a41ff006a20034101714130723a00002000417f6a2100200341ff017122044101762103200441024f0d000b024020004180016a22034180014d0d00200341800141c88dc08000109481808000000b2001410141d88dc080004102200220006a4180016a410020006b10db80808000210020024180016a24808080800020000b02000bb10701017f23808080800041106b2203248080808000024002400240024002400240024002400240024020010e2805080808080808080801030808020808080808080808080808080808080808080808060808080807000b200141dc00460d030c070b20004180043b010a20004200370102200041dce8013b01000c070b20004180043b010a20004200370102200041dce4013b01000c060b20004180043b010a20004200370102200041dcdc013b01000c050b20004180043b010a20004200370102200041dcb8013b01000c040b20004180043b010a20004200370102200041dce0003b01000c030b20024180800471450d0120004180043b010a20004200370102200041dcc4003b01000c020b200241800271450d0020004180043b010a20004200370102200041dcce003b01000c010b024002400240024002402002410171450d00200110f3808080000d010b2001109a81808000450d012000200136020420004180013a00000c040b200341066a41026a41003a0000200341003b0106200341fd003a000f20032001410f714190bdc080006a2d00003a000e20032001410476410f714190bdc080006a2d00003a000d20032001410876410f714190bdc080006a2d00003a000c20032001410c76410f714190bdc080006a2d00003a000b20032001411076410f714190bdc080006a2d00003a000a20032001411476410f714190bdc080006a2d00003a0009200141017267410276417e6a2201410b4f0d01200341066a20016a220241002f008fbec080003b0000200241026a41002d0091bec080003a00002000410a3a000b200020013a000a20002003290106370000200041086a200341066a41086a2f01003b00000c030b200341066a41026a41003a0000200341003b0106200341fd003a000f20032001410f714190bdc080006a2d00003a000e20032001410476410f714190bdc080006a2d00003a000d20032001410876410f714190bdc080006a2d00003a000c20032001410c76410f714190bdc080006a2d00003a000b20032001411076410f714190bdc080006a2d00003a000a20032001411476410f714190bdc080006a2d00003a0009200141017267410276417e6a2201410b4f0d01200341066a20016a220241002f008fbec080003b0000200241026a41002d0091bec080003a00002000410a3a000b200020013a000a20002003290106370000200041086a200341066a41086a2f01003b00000c020b2001410a4194bec08000109481808000000b2001410a4194bec08000109481808000000b200341106a2480808080000b17002001280214200141186a280200200010d9808080000be105010b7f23808080800041306b2203248080808000200341246a2001360200200341033a002c2003412036021c410021042003410036022820032000360220200341003602142003410036020c02400240024002400240200228021022050d002002410c6a2802002200450d012002280208220120004103746a21062000417f6a41ffffffff017141016a2104200228020021004100210703400240200041046a2802002208450d00200328022020002802002008200328022428020c118280808000000d040b20012802002003410c6a200141046a280200118380808000000d03200741016a2107200041086a2100200141086a22012006470d000c020b0b200241146a2802002201450d00200141057421092001417f6a41ffffff3f7141016a21042002280208210a20022802002100410021074100210b03400240200041046a2802002201450d00200328022020002802002001200328022428020c118280808000000d030b2003200520076a220141106a28020036021c20032001411c6a2d00003a002c2003200141186a2802003602282001410c6a28020021064100210c41002108024002400240200141086a2802000e03010002010b2006410374210d41002108200a200d6a220d280204419d80808000470d01200d28020028020021060b410121080b200320063602102003200836020c200141046a280200210802400240024020012802000e03010002010b20084103742106200a20066a2206280204419d80808000470d01200628020028020021080b4101210c0b200320083602182003200c360214200a200141146a2802004103746a22012802002003410c6a200141046a280200118380808000000d02200b41016a210b200041086a21002009200741206a2207470d000b0b200420022802044f0d012003280220200228020020044103746a22012802002001280204200328022428020c11828080800000450d010b410121010c010b410021010b200341306a24808080800020010b17002001280214200141186a280200200010d9808080000bad0601077f0240024020010d00200541016a2106200028021c2107412d21080c010b412b418080c400200028021c220741017122011b2108200120056a21060b0240024020074104710d00410021020c010b0240024020034110490d002002200310fd8080800021010c010b024020030d00410021010c010b2003410371210902400240200341044f0d00410021014100210a0c010b2003417c71210b410021014100210a034020012002200a6a220c2c000041bf7f4a6a200c41016a2c000041bf7f4a6a200c41026a2c000041bf7f4a6a200c41036a2c000041bf7f4a6a2101200b200a41046a220a470d000b0b2009450d002002200a6a210c03402001200c2c000041bf7f4a6a2101200c41016a210c2009417f6a22090d000b0b200120066a21060b0240024020002802000d00410121012000280214220c2000280218220a20082002200310dc808080000d01200c20042005200a28020c118280808000000f0b02402000280204220920064b0d00410121012000280214220c2000280218220a20082002200310dc808080000d01200c20042005200a28020c118280808000000f0b02402007410871450d002000280210210b2000413036021020002d0020210741012101200041013a00202000280214220c2000280218220a20082002200310dc808080000d01200920066b41016a2101024003402001417f6a2201450d01200c4130200a28021011838080800000450d000b41010f0b41012101200c20042005200a28020c118280808000000d01200020073a00202000200b360210410021010c010b200920066b210602400240024020002d002022010e0402000100020b20062101410021060c010b20064101762101200641016a41017621060b200141016a2101200041186a280200210c200028021021092000280214210a024003402001417f6a2201450d01200a2009200c28021011838080800000450d000b41010f0b41012101200a200c20082002200310dc808080000d00200a20042005200c28020c118280808000000d00410021010340024020062001470d0020062006490f0b200141016a2101200a2009200c28021011838080800000450d000b2001417f6a2006490f0b20010b4a01017f0240024002402002418080c400460d0041012105200020022001280210118380808000000d010b20030d01410021050b20050f0b200020032004200128020c118280808000000bd20701087f0240200028020022032000280208220472450d0002402004450d00200120026a21052000410c6a28020041016a2106410021072001210802400340200821042006417f6a2206450d0120042005460d020240024020042c00002209417f4c0d00200441016a2108200941ff017121090c010b20042d0001413f71210a2009411f71210802402009415f4b0d002008410674200a722109200441026a21080c010b200a41067420042d0002413f7172210a0240200941704f0d00200a2008410c74722109200441036a21080c010b200a41067420042d0003413f71722008411274418080f00071722209418080c400460d03200441046a21080b200720046b20086a21072009418080c400470d000c020b0b20042005460d00024020042c00002208417f4a0d0020084160490d0020084170490d0020042d0002413f7141067420042d0001413f71410c747220042d0003413f7172200841ff0171411274418080f0007172418080c400460d010b024002402007450d00024020072002490d004100210420072002460d010c020b41002104200120076a2c00004140480d010b200121040b2007200220041b21022004200120041b21010b024020030d00200028021420012002200041186a28020028020c118280808000000f0b200028020421050240024020024110490d002001200210fd8080800021040c010b024020020d00410021040c010b2002410371210602400240200241044f0d0041002104410021090c010b2002417c712107410021044100210903402004200120096a22082c000041bf7f4a6a200841016a2c000041bf7f4a6a200841026a2c000041bf7f4a6a200841036a2c000041bf7f4a6a21042007200941046a2209470d000b0b2006450d00200120096a21080340200420082c000041bf7f4a6a2104200841016a21082006417f6a22060d000b0b02400240200520044d0d00200520046b21074100210402400240024020002d00200e0402000102020b20072104410021070c010b20074101762104200741016a41017621070b200441016a2104200041186a2802002108200028021021062000280214210903402004417f6a2204450d0220092006200828021011838080800000450d000b41010f0b200028021420012002200041186a28020028020c118280808000000f0b410121040240200920012002200828020c118280808000000d004100210402400340024020072004470d00200721040c020b200441016a210420092006200828021011838080800000450d000b2004417f6a21040b200420074921040b20040f0b200028021420012002200041186a28020028020c118280808000000b9d05010a7f23808080800041106b2202248080808000024002400240024002402000280200450d00200028020421032002410c6a2001410c6a280200220436020020022001280208220536020820022001280204220636020420022001280200220136020020002d002021072000280210210820002d001c4108710d0120062101200821092007210a0c020b20002802142000280218200110df8080800021050c030b200028021420012006200041186a28020028020c118280808000000d014101210a200041013a002041302109200041303602104100210120024100360204200241e88fc080003602004100200320066b2206200620034b1b21030b02402004450d002004410c6c21040340024002400240024020052f01000e03000201000b200541046a28020021060c020b200541086a28020021060c010b0240200541026a2f0100220b41e807490d0041044105200b4190ce00491b21060c010b41012106200b410a490d0041024103200b41e400491b21060b2005410c6a2105200620016a2101200441746a22040d000b0b024002400240200320014d0d00200320016b2104024002400240200a41ff017122050e0402000100020b20042105410021040c010b20044101762105200441016a41017621040b200541016a2105200041186a28020021012000280214210603402005417f6a2205450d0220062009200128021011838080800000450d000c040b0b20002802142000280218200210df8080800021050c010b20062001200210df808080000d014100210502400340024020042005470d00200421050c020b200541016a210520062009200128021011838080800000450d000b2005417f6a21050b200520044921050b200020073a0020200020083602100c010b410121050b200241106a24808080800020050b9f0501087f23808080800041106b22032480808080000240024020022802042204450d0041012105200020022802002004200128020c118280808000000d010b02402002410c6a2802002205450d00200228020822062005410c6c6a2107200341086a41046a21080340024002400240024020062f01000e03000201000b024002402006280204220241c100490d002001410c6a280200210503400240200041d890c0800041c000200511828080800000450d00410121050c090b200241406a220241c0004b0d000c020b0b2002450d032001410c6a28020021050b200041d890c080002002200511828080800000450d02410121050c050b20002006280204200641086a2802002001410c6a28020011828080800000450d01410121050c040b20062f01022102200841003a00002003410036020802400240024002400240024020062f01000e03020100020b200641086a21050c020b024020062f0102220541e807490d004104410520054190ce00491b21090c030b410121092005410a490d0241024103200541e400491b21090c020b200641046a21050b02402005280200220941064f0d0020090d01410021090c020b20094105419891c08000109581808000000b200341086a20096a21040240024020094101710d00200221050c010b2004417f6a22042002200241ffff0371410a6e2205410a6c6b4130723a00000b20094101460d002004417e6a210203402002200541ffff03712204410a6e220a410a704130723a0000200241016a2005200a410a6c6b4130723a0000200441e4006e21052002200341086a4621042002417e6a21022004450d000b0b2000200341086a20092001410c6a28020011828080800000450d00410121050c030b2006410c6a22062007470d000b0b410021050b200341106a24808080800020050b820201017f23808080800041106b220f248080808000200028021420012002200041186a28020028020c118280808000002102200f41003a000d200f20023a000c200f2000360208200f41086a2003200420052006108c81808000200720082009200a108c81808000200b200c200d200e108c818080002101200f2d000c210202400240200f2d000d0d00200241ff017141004721000c010b41012100200241ff01710d000240200128020022002d001c4104710d0020002802144187a5c080004102200028021828020c1182808080000021000c010b20002802144186a5c080004101200028021828020c1182808080000021000b200f41106a24808080800020000b2d00024020002d00000d00200141a891c08000410510dd808080000f0b200141ad91c08000410410dd808080000bd107030d7f017e017f23808080800041206b22032480808080004101210402400240200228021422054122200241186a28020022062802102207118380808000000d000240024020010d0041002102410021010c010b200020016a210841002102200021094100210a024002400340024002402009220b2c0000220c417f4c0d00200b41016a2109200c41ff0171210d0c010b200b2d0001413f71210e200c411f71210f0240200c415f4b0d00200f410674200e72210d200b41026a21090c010b200e410674200b2d0002413f7172210e200b41036a21090240200c41704f0d00200e200f410c7472210d0c010b200e41067420092d0000413f7172200f411274418080f0007172220d418080c400460d03200b41046a21090b200341046a200d4181800410d7808080000240024020032d0004418001460d0020032d000f20032d000e6b41ff01714101460d00200a2002490d0302402002450d00024020022001490d0020022001460d010c050b200020026a2c00004140480d040b0240200a450d000240200a2001490d00200a2001460d010c050b2000200a6a2c000041bf7f4c0d040b024002402005200020026a200a20026b200628020c118280808000000d00200341106a41086a220f200341046a41086a28020036020020032003290204221037031002402010a741ff0171418001470d00418001210e034002400240200e41ff0171418001460d0020032d001a220c20032d001b4f0d052003200c41016a3a001a200c410a4f0d07200341106a200c6a2d000021020c010b4100210e200f410036020020032802142102200342003703100b20052002200711838080800000450d000c020b0b20032d001a2202410a2002410a4b1b210c20032d001b220e2002200e20024b1b2111034020112002460d022003200241016a220e3a001a200c2002460d04200341106a20026a210f200e21022005200f2d0000200711838080800000450d000b0b410121040c070b410121020240200d418001490d0041022102200d418010490d0041034104200d41808004491b21020b2002200a6a21020b200a200b6b20096a210a20092008470d010c030b0b200c410a41a4bec0800010f980808000000b200020012002200a41c491c08000109781808000000b024020020d00410021020c010b0240200120024b0d0020012002470d03200120026b210c20012102200c21010c010b200020026a2c000041bf7f4c0d02200120026b21010b2005200020026a2001200628020c118280808000000d002005412220071183808080000021040b200341206a24808080800020040f0b200020012002200141b491c08000109781808000000be40201077f23808080800041106b22022480808080004101210302400240200128021422044127200141186a2802002802102205118380808000000d002002200028020041810210d7808080000240024020022d0000418001470d00200241086a21064180012107034002400240200741ff0171418001460d0020022d000a220020022d000b4f0d042002200041016a3a000a2000410a4f0d06200220006a2d000021010c010b410021072006410036020020022802042101200242003703000b20042001200511838080800000450d000c030b0b20022d000a2201410a2001410a4b1b210020022d000b22072001200720014b1b2108034020082001460d012002200141016a22073a000a20002001460d03200220016a210620072101200420062d0000200511838080800000450d000c020b0b2004412720051183808080000021030b200241106a24808080800020030f0b2000410a41a4bec0800010f980808000000bbb0201017f23808080800041106b220224808080800020002802002100024002402001280200200128020872450d002002410036020c02400240024002402000418001490d002000418010490d012000418080044f0d0220022000413f71418001723a000e20022000410c7641e001723a000c20022000410676413f71418001723a000d410321000c030b200220003a000c410121000c020b20022000413f71418001723a000d2002200041067641c001723a000c410221000c010b20022000413f71418001723a000f2002200041127641f001723a000c20022000410676413f71418001723a000e20022000410c76413f71418001723a000d410421000b20012002410c6a200010dd8080800021010c010b20012802142000200141186a2802002802101183808080000021010b200241106a24808080800020010b180020002802002001200028020428020c118380808000000bf80202027f017e2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120002903004101200110fe8080800021000c020b20002903002104410021000340200220006a41ff006a413041d7002004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002903002104410021000340200220006a41ff006a413041372004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b140020012000280200200028020410dd808080000b2000200042d7e8a9deef8fddde6a370308200042f0c6e287c69783ad393703000bb30301057f23808080800041c0006b22022480808080004101210302402001280214220441d491c08000410c200141186a280200220528020c2206118280808000000d00200028020c2101200241106a410c6a42033702002002413c6a418580808000360200200241286a410c6a41858080800036020020024103360214200241f49fc0800036021020022001410c6a3602382002200141086a360230200241a28080800036022c200220013602282002200241286a36021820042005200241106a10d9808080000d000240024020002802082201450d00200441e091c0800041022006118280808000000d02200241286a41106a200141106a290200370300200241286a41086a200141086a2902003703002002200129020037032820042005200241286a10d9808080000d020c010b2002200028020022012000280204410c6a28020011848080800000200229030042c1f7f9e8cc93b2d14185200241086a29030042e4dec78590d085de7d858450450d00200441e091c0800041022006118280808000000d012004200128020020012802042006118280808000000d010b410021030b200241c0006a24808080800020030b8a0401077f0240024002400240200141800a4f0d00200141057621020240024020002802a0012203450d00200341284b0d032002417f6a2104200341027420006a417c6a2105200320026a41027420006a417c6a21060340200420036a41274b0d02200620052802003602002006417c6a21062005417c6a21052003417f6a22030d000b0b2001411f712104024020014120490d002000410020024101200241014b1b410274108a8e8080001a0b20002802a00120026a2105024020040d00200020053602a00120000f0b2005417f6a220341274b0d0320052107200020034102746a2802002206410020016b2208762203450d040240200541274b0d00200020054102746a2003360200200541016a21070c050b2005412841d892c0800010f980808000000b200220036a417f6a412841d892c0800010f980808000000b418293c08000411d41d892c0800010f880808000000b2003417f6a412841d892c0800010f980808000000b2003412841d892c0800010f980808000000b02400240200241016a220120054f0d002008411f712108200541027420006a41786a210303402005417e6a41284f0d02200341046a200620047420032802002206200876723602002003417c6a210320012005417f6a2205490d000b0b200020024102746a22032003280200200474360200200020073602a00120000f0b417f412841d892c0800010f980808000000bb806030b7f027e017f23808080800041a0016b22032480808080002003410041a001108a8e80800021040240024002400240024020002802a00122052002490d00200541294f0d01200120024102746a21060240024002402005450d00200541016a21072005410274210241002108410021090340200420084102746a210a03402008210b200a210320012006460d09200341046a210a200b41016a21082001280200210c200141046a220d2101200c450d000b200cad210e4200210f2002210c200b21012000210a0340200141284f0d042003200f20033502007c200a350200200e7e7c220f3e0200200f422088210f200341046a2103200141016a2101200a41046a210a200c417c6a220c0d000b200521030240200fa72201450d00200b20056a220341284f0d03200420034102746a2001360200200721030b20092003200b6a2203200920034b1b2109200d21010c000b0b4100210941002103034020012006460d07200341016a21032001280200210a200141046a22082101200a450d0020092003417f6a2201200920014b1b2109200821010c000b0b2003412841d892c0800010f980808000000b2001412841d892c0800010f980808000000b200541294f0d0120024102742107200241016a2110200020054102746a210d4100210b2000210a4100210903402004200b4102746a21080340200b210c20082103200a200d460d05200341046a2108200c41016a210b200a2802002106200a41046a2205210a2006450d000b2006ad210e4200210f20072106200c210a2001210802400340200a41284f0d012003200f20033502007c2008350200200e7e7c220f3e0200200f422088210f200341046a2103200a41016a210a200841046a21082006417c6a22060d000b200221030240200fa7220a450d00200c20026a220341284f0d05200420034102746a200a360200201021030b20092003200c6a2203200920034b1b21092005210a0c010b0b200a412841d892c0800010f980808000000b2005412841d892c08000109581808000000b2005412841d892c08000109581808000000b2003412841d892c0800010f980808000000b2000200441a00110848e808000220320093602a001200441a0016a24808080800020030bc203000240024002402002450d0020012d000041304d0d01200641034d0d02200541023b01000240024002400240200341107441107522064101480d0020052001360204200341ffff0371220320024f0d01200541023b0118200541023b010c20052003360208200541206a200220036b22023602002005411c6a200120036a360200200541146a4101360200200541106a41c895c0800036020041032101200420024d0d03200420026b21040c020b200541023b0118200541003b010c20054102360208200541c995c08000360204200541206a20023602002005411c6a2001360200200541106a410020066b220336020041032101200420024d0d02200420026b220220034d0d02200220066a21040c010b200541003b010c20052002360208200541106a200320026b360200024020040d00410221010c020b200541023b0118200541206a41013602002005411c6a41c895c080003602000b200541003b0124200541286a2004360200410421010b20002001360204200020053602000f0b41b294c08000412141d494c0800010f880808000000b41e494c08000411f418495c0800010f880808000000b419495c08000412241b895c0800010f880808000000b970809017f017e017f017e017f037e027f017e017f23808080800041f0086b22042480808080002001bd21050240024020012001610d00410221060c010b200542ffffffffffffff0783220742808080808080800884200542018642feffffffffffff0f832005423488a741ff0f7122081b2209420183210a4103210602400240024041014102410420054280808080808080f8ff0083220b50220c1b200b4280808080808080f8ff00511b41034104200c1b2007501b417f6a0e0403000102030b410421060c020b200841cd776a210d200a5021064201210e0c010b428080808080808020200942018620094280808080808080085122061b21094202420120061b210e41cb7741cc7720061b20086a210d200a5021060b2004200d3b01e8082004200e3703e008200442013703d808200420093703d008200420063a00ea080240024002400240024002402006417e6a41ff01712206410320064103491b2208450d0041f095c0800041ef95c0800020021b41ef95c080002005423f87a7220f417f4a1b210c410121064101200f4180017141077620021b21022008417f6a0e03010203010b2004410336029808200441f195c0800036029408200441023b0190084101210620044190086a210d4100210241ef95c08000210c0c040b2004410336029808200441f495c0800036029408200441023b01900820044190086a210d0c030b41022106200441023b0190082003450d01200441a0086a2003360200200441003b019c082004410236029808200441c995c080003602940820044190086a210d0c020b024041744105200d41107441107522064100481b20066c220641c0fd004f0d0020044190086a200441d0086a200441106a200641047641156a220d410020036b4180807e200341808002491b2206109e8180800020064110744110752106024002402004280290080d00200441c0086a200441d0086a200441106a200d20061092818080000c010b200441c0086a41086a20044190086a41086a28020036020020042004290290083703c0080b024020042e01c808220d20064c0d00200441086a20042802c00820042802c408200d200320044190086a410410ec80808000200428020c21062004280208210d0c030b41022106200441023b019008024020030d00410121062004410136029808200441f795c080003602940820044190086a210d0c030b200441a0086a2003360200200441003b019c082004410236029808200441c995c080003602940820044190086a210d0c020b41fb95c08000412541a096c0800010f880808000000b410121062004410136029808200441f795c080003602940820044190086a210d0b200441cc086a20063602002004200d3602c808200420023602c4082004200c3602c0082000200441c0086a10de808080002106200441f0086a24808080800020060bac0608017f017e017f017e017f037e027f017e2380808080004180016b22042480808080002001bd21050240024020012001610d00410221060c010b200542ffffffffffffff0783220742808080808080800884200542018642feffffffffffff0f832005423488a741ff0f7122081b2209420183210a4103210602400240024041014102410420054280808080808080f8ff0083220b50220c1b200b4280808080808080f8ff00511b41034104200c1b2007501b417f6a0e0403000102030b410421060c020b200841cd776a210d200a5021064201210e0c010b428080808080808020200942018620094280808080808080085122061b21094202420120061b210e41cb7741cc7720061b20086a210d200a5021060b2004200d3b01782004200e3703702004420137036820042009370360200420063a007a02400240024002402006417e6a41ff01712206410320064103491b220c450d0041f095c0800041ef95c080002005423f87a72208417f4a22061b41ef95c0800041ef95c0800020061b20021b210d41012106410120084180017141077620021b21020240200c417f6a0e03020300020b200441206a200441e0006a2004410f6a4111109d818080000240024020042802200d00200441d0006a200441e0006a2004410f6a41111091818080000c010b200441d0006a41086a200441206a41086a280200360200200420042902203703500b20042004280250200428025420042f01582003200441206a410410ec80808000200428020421062004280200210c0c030b20044103360228200441f195c08000360224200441023b012041012106200441206a210c4100210241ef95c08000210d0c020b20044103360228200441f495c08000360224200441023b0120200441206a210c0c010b41022106200441023b012002402003450d00200441306a4101360200200441003b012c20044102360228200441c995c08000360224200441206a210c0c010b4101210620044101360228200441f795c08000360224200441206a210c0b200441dc006a20063602002004200c360258200420023602542004200d3602502000200441d0006a10de80808000210620044180016a24808080800020060bf10709017f017e017f017e017f037e027f017e017f2380808080004190016b22032480808080002001bd21040240024020012001610d00410221050c010b200442ffffffffffffff0783220642808080808080800884200442018642feffffffffffff0f832004423488a741ff0f7122071b220842018321094103210502400240024041014102410420044280808080808080f8ff0083220a50220b1b200a4280808080808080f8ff00511b41034104200b1b2006501b417f6a0e0403000102030b410421050c020b200741cd776a210c20095021054201210d0c010b428080808080808020200842018620084280808080808080085122051b21084202420120051b210d41cb7741cc7720051b20076a210c20095021050b2003200c3b0188012003200d370380012003420137037820032008370370200320053a008a01024002400240024002400240024002402005417e6a41ff01712205410320054103491b220b450d0041f095c0800041ef95c0800020021b41ef95c080002004423f87a72207417f4a1b210c41012105410120074180017141077620021b21020240200b417f6a0e03020300020b200341186a200341f0006a200341076a4111109d818080000240024020032802180d00200341e0006a200341f0006a200341076a41111091818080000c010b200341e0006a41086a200341186a41086a280200360200200320032902183703600b20032802642207450d032003280260220e2d000041304d0d0420032e0168210b200341013602202003200e36021c200341023b011841012105200741014b0d050c060b20034103360220200341f195c0800036021c200341023b0118410121054100210241ef95c08000210c0c060b20034103360220200341f495c0800036021c200341023b01180c050b20034103360220200341f895c0800036021c200341023b01180c040b41b294c08000412141cc95c0800010f880808000000b41e494c08000411f41dc95c0800010f880808000000b200341386a2007417f6a360200200341346a200e41016a3602002003412c6a4101360200200341286a41c895c08000360200200341023b0130200341023b0124410321050b02400240200b4101480d00200341186a2005410c6c6a22074101360208200741ec95c08000360204200741023b0100200b417f6a21070c010b200341186a2005410c6c6a22074102360208200741ed95c08000360204200741023b01004101200b6b21070b2005410c6c200341186a6a220b410e6a20073b0100200b410c6a41013b0100200541026a21050b200341ec006a2005360200200320023602642003200c3602602003200341186a3602682000200341e0006a10de80808000210520034190016a24808080800020050b950103017f017e027c200128021c410171210202402001280208450d00200120002b030020022001410c6a28020010ed808080000f0b20002903002203bf21040240200342ffffffffffffffffff0083bf2205440080e03779c34143660d002005440000000000000000622005442d431cebe2361a3f63710d00200120042002410110ee808080000f0b20012004200210ef808080000b4502017f017c200128021c410171210220002b0300210302402001280208450d002001200320022001410c6a28020010ed808080000f0b200120032002410010ee808080000bb00c03097f017e017f024020040d002000200336023820002001360230200041003a000e20004181023b010c20002002360208200042003703002000413c6a4100360200200041346a20023602000f0b41012105410021060240024002400240024002400240024002400240024020044101470d0041002107410121080c010b41012109410121054100210a4101210b410021060340200b210c2006200a6a220b20044f0d0202400240200320096a2d000041ff017122092003200b6a2d0000220b4f0d00200c20066a41016a220b200a6b2105410021060c010b02402009200b460d0041012105200c41016a210b41002106200c210a0c010b4100200641016a220b200b20054622091b2106200b410020091b200c6a210b0b200b20066a22092004490d000b4101210941012108410021074101210b410021060340200b210c200620076a220b20044f0d0302400240200320096a2d000041ff017122092003200b6a2d0000220b4d0d00200c20066a41016a220b20076b2108410021060c010b02402009200b460d0041012108200c41016a210b41002106200c21070c010b4100200641016a220b200b20084622091b2106200b410020091b200c6a210b0b200b20066a22092004490d000b200a21060b200420062007200620074b220b1b220d490d0220052008200b1b220b200d6a2206200b490d03200620044b0d040240024020032003200b6a200d10888e808000450d002004410371210c024002402004417f6a41034f0d004100210b4200210e0c010b2004417c7121094100210b4200210e034042012003200b6a220641036a310000864201200641026a310000864201200641016a310000864201200631000086200e84848484210e2009200b41046a220b470d000b0b2004200d6b21090240200c450d002003200b6a210603404201200631000086200e84210e200641016a2106200c417f6a220c0d000b0b200d2009200d20094b1b41016a210b417f210a200d2105417f21060c010b41012107410021064101210941002105024003402009220c20066a220820044f0d01200420066b200c417f736a220920044f0d082006417f7320046a20056b220a20044f0d0902400240200320096a2d000041ff017122092003200a6a2d0000220a4f0d00200841016a220920056b2107410021060c010b02402009200a460d00200c41016a21094100210641012107200c21050c010b4100200641016a22092009200746220a1b210620094100200a1b200c6a21090b2007200b470d000b0b41012107410021064101210941002108024003402009220c20066a220f20044f0d01200420066b200c417f736a220920044f0d0a2006417f7320046a20086b220a20044f0d0b02400240200320096a2d000041ff017122092003200a6a2d0000220a4d0d00200f41016a220920086b2107410021060c010b02402009200a460d00200c41016a21094100210641012107200c21080c010b4100200641016a22092009200746220a1b210620094100200a1b200c6a21090b2007200b470d000b0b200420052008200520084b1b6b210502400240200b0d004200210e4100210b4100210a0c010b200b41037121094100210a02400240200b41044f0d004200210e4100210c0c010b200b417c7121074100210c4200210e034042012003200c6a220641036a310000864201200641026a310000864201200641016a310000864201200631000086200e84848484210e2007200c41046a220c470d000b0b2009450d002003200c6a210603404201200631000086200e84210e200641016a21062009417f6a22090d000b0b200421060b2000200336023820002001360230200020063602282000200a360224200020023602202000410036021c2000200b360218200020053602142000200d3602102000200e370308200041013602002000413c6a2004360200200041346a20023602000f0b200b200441c497c0800010f980808000000b200b200441c497c0800010f980808000000b200d200441a497c08000109581808000000b200b200641b497c08000109681808000000b2006200441b497c08000109581808000000b2009200441d497c0800010f980808000000b200a200441e497c0800010f980808000000b2009200441d497c0800010f980808000000b200a200441e497c0800010f980808000000bf00201057f2000410b742101410021024121210341212104024002400340200341017620026a2203410274419499c080006a280200410b7422052001460d0120032004200520014b1b2204200341016a200220052001491b22026b2103200420024b0d000c020b0b200341016a21020b0240024002400240200241204b0d0020024102742203419499c080006a280200411576210120024120470d01411f210241d70521050c020b2002412141f498c0800010f980808000000b2003419899c080006a2802004115762105024020020d00410021020c020b2002417f6a21020b2002410274419499c080006a28020041ffffff007121020b0240024020052001417f736a450d00200020026b2104200141d705200141d7054b1b21032005417f6a210541002102034020032001460d022002200141989ac080006a2d00006a220220044b0d012005200141016a2201470d000b200521010b20014101710f0b200341d705418499c0800010f980808000000b02000b02000b4c01017f23808080800041206b2202248080808000200241013b011c20022001360218200220003602142002418ca0c08000360210200241f09fc0800036020c2002410c6a109384808000000b7d01017f23808080800041306b2203248080808000200341106a200041106a290200370300200341086a200041086a29020037030020032000290200370300200320013a002d200341003a002c200320023602282003418ca0c08000360220200341f09fc0800036021c200320033602242003411c6a109384808000000b5401017f23808080800041206b22032480808080002003410c6a420037020020034101360204200341f09fc080003602082003200136021c200320003602182003200341186a3602002003200210f680808000000b870101017f23808080800041306b22032480808080002003200136020420032000360200200341086a410c6a4202370200200341206a410c6a4185808080003602002003410236020c200341d0a0c0800036020820034185808080003602242003200341206a360210200320033602282003200341046a360220200341086a200210f680808000000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a41e0a0c080002005410c6a41e0a0c080002003200410fb80808000000bd90301017f23808080800041f0006b22072480808080002007200236020c2007200136020820072004360214200720033602100240024002400240200041ff01710e03000102000b200741f0a0c08000360218410221020c020b200741f2a0c08000360218410221020c010b200741f4a0c08000360218410721020b2007200236021c024020052802000d00200741cc006a41a380808000360200200741386a410c6a41a380808000360200200741d8006a410c6a42033702002007410336025c200741aca1c08000360258200741a28080800036023c2007200741386a3602602007200741106a3602482007200741086a3602402007200741186a360238200741d8006a200610f680808000000b200741206a41106a200541106a290200370300200741206a41086a200541086a29020037030020072005290200370320200741d8006a410c6a4204370200200741d4006a41a380808000360200200741cc006a41a380808000360200200741386a410c6a41a4808080003602002007410436025c200741e0a1c08000360258200741a28080800036023c2007200741386a3602602007200741106a3602502007200741086a3602482007200741206a3602402007200741186a360238200741d8006a200610f680808000000bf90503057f027e017f02402002450d004100200241796a2203200320024b1b2104200141036a417c7120016b21054100210303400240024002400240200120036a2d0000220641187441187522074100480d00200520036b4103710d01200320044f0d020340200120036a220641046a280200200628020072418081828478710d03200341086a22032004490d000c030b0b428080808080202108428080808010210902400240024002400240024002400240024002400240024020064180a2c080006a2d0000417e6a0e030001020a0b200341016a22062002490d0242002108420021090c090b42002108200341016a220a2002490d02420021090c080b42002108200341016a220a2002490d02420021090c070b4280808080802021084280808080102109200120066a2c000041bf7f4a0d060c070b2001200a6a2c0000210a024002400240200641a07e6a0e0e0002020202020202020202020201020b200a41607141a07f460d040c030b200a419f7f4a0d020c030b02402007411f6a41ff0171410c490d002007417e71416e470d02200a4140480d030c020b200a4140480d020c010b2001200a6a2c0000210a0240024002400240200641907e6a0e050100000002000b2007410f6a41ff017141024b0d03200a41404e0d030c020b200a41f0006a41ff017141304f0d020c010b200a418f7f4a0d010b0240200341026a22062002490d00420021090c050b200120066a2c000041bf7f4a0d0242002109200341036a220620024f0d04200120066a2c000041bf7f4c0d05428080808080e00021080c030b4280808080802021080c020b42002109200341026a220620024f0d02200120066a2c000041bf7f4c0d030b428080808080c00021080b42808080801021090b200020082003ad84200984370204200041013602000f0b200641016a21030c020b200341016a21030c010b200320024f0d000340200120036a2c00004100480d012002200341016a2203470d000c030b0b20032002490d000b0b20002001360204200041086a2002360200200041003602000b800701097f024002402001200041036a417c71220220006b2203490d00200120036b22044104490d002004410371210541002106410021010240200220004622070d00410021010240024020022000417f736a41034f0d00410021080c010b4100210803402001200020086a22092c000041bf7f4a6a200941016a2c000041bf7f4a6a200941026a2c000041bf7f4a6a200941036a2c000041bf7f4a6a2101200841046a22080d000b0b20070d00200020026b2102200020086a21090340200120092c000041bf7f4a6a2101200941016a2109200241016a22020d000b0b200020036a210802402005450d0020082004417c716a22092c000041bf7f4a210620054101460d00200620092c000141bf7f4a6a210620054102460d00200620092c000241bf7f4a6a21060b20044102762103200620016a21020340200821062003450d02200341c001200341c001491b220441037121052004410274210702400240200441fc0171220a0d00410021090c010b2006200a4102746a21004100210920062101034020012802002208417f7341077620084106767241818284087120096a200141046a2802002209417f734107762009410676724181828408716a200141086a2802002209417f734107762009410676724181828408716a2001410c6a2802002209417f734107762009410676724181828408716a2109200141106a22012000470d000b0b200320046b2103200620076a2108200941087641ff81fc0771200941ff81fc07716a418180046c41107620026a21022005450d000b2006200a4102746a22092802002201417f734107762001410676724181828408712101024020054101460d0020092802042208417f7341077620084106767241818284087120016a210120054102460d0020092802082209417f7341077620094106767241818284087120016a21010b200141087641ff811c71200141ff81fc07716a418180046c41107620026a21020c010b024020010d0041000f0b2001410371210802400240200141044f0d0041002102410021090c010b2001417c712103410021024100210903402002200020096a22012c000041bf7f4a6a200141016a2c000041bf7f4a6a200141026a2c000041bf7f4a6a200141036a2c000041bf7f4a6a21022003200941046a2209470d000b0b2008450d00200020096a21010340200220012c000041bf7f4a6a2102200141016a21012008417f6a22080d000b0b20020be90203027f017e037f23808080800041306b2203248080808000412721040240024020004290ce005a0d00200021050c010b412721040340200341096a20046a2206417c6a200020004290ce008022054290ce007e7da7220741ffff037141e4006e220841017441da8dc080006a2f00003b00002006417e6a2007200841e4006c6b41ffff037141017441da8dc080006a2f00003b00002004417c6a2104200042ffc1d72f5621062005210020060d000b0b02402005a7220641e3004d0d00200341096a2004417e6a22046a2005a72206200641ffff037141e4006e220641e4006c6b41ffff037141017441da8dc080006a2f00003b00000b024002402006410a490d00200341096a2004417e6a22046a200641017441da8dc080006a2f00003b00000c010b200341096a2004417f6a22046a200641306a3a00000b200220014180a4c080004100200341096a20046a412720046b10db808080002104200341306a24808080800020040b110020003100004101200110fe808080000b110020003502004101200110fe808080000b2301027e200029030022022002423f8722038520037d2002427f55200110fe808080000b110020002903004101200110fe808080000b02000b850601047f2380808080004180016b22022480808080000240024002400240024002400240200128021c22034110710d00024020034120710d004101210320003502004101200110fe80808000450d020c030b20002802002103410021040340200220046a41ff006a413041372003410f712205410a491b20056a3a00002004417f6a210420034110492105200341047621032005450d000b20044180016a22034180014b0d03410121032001410141c48dc080004102200220046a4180016a410020046b10db80808000450d010c020b20002802002103410021040340200220046a41ff006a413041d7002003410f712205410a491b20056a3a00002004417f6a210420034110492105200341047621032005450d000b20044180016a22034180014b0d03410121032001410141c48dc080004102200220046a4180016a410020046b10db808080000d010b2002410c6a4200370200410121032002410136020420024184a4c0800036020020024180a4c080003602082001280214200141186a280200200210d9808080000d0002400240200128021c22034110710d0020034120710d0120003502044101200110fe8080800021030c020b20002802042103410021040340200220046a41ff006a413041d7002003410f712205410a491b20056a3a00002004417f6a210420034110492105200341047621032005450d000b20044180016a22034180014b0d042001410141c48dc080004102200220046a4180016a410020046b10db8080800021030c010b20002802042103410021040340200220046a41ff006a413041372003410f712205410a491b20056a3a00002004417f6a210420034110492105200341047621032005450d000b20044180016a22034180014b0d042001410141c48dc080004102200220046a4180016a410020046b10db8080800021030b20024180016a24808080800020030f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b21002001280214418ca4c08000410b200141186a28020028020c118280808000000b210020012802144197a4c08000410e200141186a28020028020c118280808000000b5e01017f23808080800041306b2201248080808000200141186a420137020020014101360210200141b8a4c0800036020c200141a9808080003602282001200141246a36021420012001412f6a3602242001410c6a200010f680808000000b5e01017f23808080800041306b2201248080808000200141186a420137020020014101360210200141dca4c0800036020c200141aa808080003602282001200141246a36021420012001412f6a3602242001410c6a200010f680808000000b990101017f23808080800041c0006b22052480808080002005200136020c200520003602082005200336021420052002360210200541186a410c6a4202370200200541306a410c6a41a3808080003602002005410236021c200541e8a4c08000360218200541a2808080003602342005200541306a3602202005200541106a3602382005200541086a360230200541186a200410f680808000000bc404010b7f2000280204210320002802002104200028020821054100210641002107410021084100210902400340200941ff01710d0102400240200820024b0d000340200120086a210a02400240024002400240200220086b220b4108490d00200a41036a417c712200200a460d012000200a6b2200450d014100210c0340200a200c6a2d0000410a460d052000200c41016a220c470d000b2000200b41786a220d4b0d030c020b024020022008470d00200221080c060b4100210c0340200a200c6a2d0000410a460d04200b200c41016a220c470d000b200221080c050b200b41786a210d410021000b0340200a20006a220c41046a2802002209418a94a8d0007341fffdfb776a2009417f7371200c280200220c418a94a8d0007341fffdfb776a200c417f737172418081828478710d01200041086a2200200d4d0d000b0b02402000200b470d00200221080c030b03400240200a20006a2d0000410a470d002000210c0c020b200b200041016a2200470d000b200221080c020b200c20086a220041016a21080240200020024f0d00200120006a2d0000410a470d00410021092008210d200821000c030b200820024d0d000b0b410121092007210d2002210020072002460d020b0240024020052d0000450d00200441f8a4c080004104200328020c118280808000000d010b200120076a210c200020076b210a4100210b024020002007460d00200a200c6a417f6a2d0000410a46210b0b2005200b3a0000200d21072004200c200a200328020c11828080800000450d010b0b410121060b20060b5801027f20002802042102200028020021030240200028020822002d0000450d00200341f8a4c080004104200228020c11828080800000450d0041010f0b20002001410a463a0000200320012002280210118380808000000be40302057f017e23808080800041c0006b220524808080800041012106024020002d00040d0020002d0005210702402000280200220828021c22094104710d0041012106200828021441ffa4c0800041fca4c08000200741ff017122071b4102410320071b200841186a28020028020c118280808000000d0141012106200828021420012002200828021828020c118280808000000d0141012106200828021441e4a4c080004102200828021828020c118280808000000d0120032008200428020c1183808080000021060c010b0240200741ff01710d004101210620082802144181a5c080004103200841186a28020028020c118280808000000d01200828021c21090b41012106200541013a001b200541346a41d08fc080003602002005200829021437020c20052005411b6a360214200520082902083702242008290200210a200520093602382005200828021036022c200520082d00203a003c2005200a37021c20052005410c6a3602302005410c6a20012002108a818080000d002005410c6a41e4a4c080004102108a818080000d0020032005411c6a200428020c118380808000000d0020052802304184a5c080004102200528023428020c1182808080000021060b200041013a0005200020063a0004200541c0006a24808080800020000bf70202057f017e23808080800041c0006b22032480808080002000280200210441012105024020002d00080d0002402000280204220628021c22074104710d0041012105200628021441ffa4c080004189a5c0800020041b4102410120041b200641186a28020028020c118280808000000d0120012006200228020c1183808080000021050c010b024020040d00410121052006280214418aa5c080004102200641186a28020028020c118280808000000d01200628021c21070b41012105200341013a001b200341346a41d08fc080003602002003200629021437020c20032003411b6a3602142003200629020837022420062902002108200320073602382003200628021036022c200320062d00203a003c2003200837021c20032003410c6a36023020012003411c6a200228020c118380808000000d0020032802304184a5c080004102200328023428020c1182808080000021050b200020053a00082000200441016a360200200341c0006a24808080800020000bf90202047f017e23808080800041c0006b220324808080800041012104024020002d00040d0020002d00052104024002402000280200220528021c22064104710d00200441ff0171450d0141012104200528021441ffa4c080004102200541186a28020028020c11828080800000450d010c020b0240200441ff01710d00410121042005280214418ea5c080004101200541186a28020028020c118280808000000d02200528021c21060b41012104200341013a001b200341346a41d08fc080003602002003200529021437020c20032003411b6a3602142003200529020837022420052902002107200320063602382003200528021036022c200320052d00203a003c2003200737021c20032003410c6a36023020012003411c6a200228020c118380808000000d0120032802304184a5c080004102200328023428020c1182808080000021040c010b20012005200228020c1183808080000021040b200041013a0005200020043a0004200341c0006a24808080800020000b120020004194a5c08000200110d9808080000ba00705027f017e027f017e017f024020014107712202450d000240024020002802a001220341294f0d00024020030d00200041003602a0010c030b200241027441aca5c080006a35020021042003417f6a41ffffffff0371220241016a220541037121060240200241034f0d0042002107200021020c020b200541fcffffff07712105420021072000210203402002200235020020047e20077c22073e0200200241046a2208200835020020047e20074220887c22073e0200200241086a2208200835020020047e20074220887c22073e02002002410c6a2208200835020020047e20074220887c22073e020020074220882107200241106a21022005417c6a22050d000c020b0b2003412841d892c08000109581808000000b02402006450d0003402002200235020020047e20077c22073e0200200241046a2102200742208821072006417f6a22060d000b0b024002402007a72202450d00200341274b0d01200020034102746a2002360200200341016a21030b200020033602a0010c010b4128412841d892c0800010f980808000000b024002402001410871450d0002400240024020002802a001220341294f0d00024020030d00410021030c030b2003417f6a41ffffffff0371220241016a220541037121060240200241034f0d0042002104200021020c020b200541fcffffff0771210542002104200021020340200220023502004280c2d72f7e20047c22043e0200200241046a220820083502004280c2d72f7e20044220887c22043e0200200241086a220820083502004280c2d72f7e20044220887c22043e02002002410c6a220820083502004280c2d72f7e20044220887c22043e020020044220882104200241106a21022005417c6a22050d000c020b0b2003412841d892c08000109581808000000b02402006450d000340200220023502004280c2d72f7e20047c22043e0200200241046a2102200442208821042006417f6a22060d000b0b2004a72202450d00200341274b0d02200020034102746a2002360200200341016a21030b200020033602a0010b02402001411071450d00200041fca5c08000410210eb808080001a0b02402001412071450d0020004184a6c08000410410eb808080001a0b0240200141c00071450d0020004194a6c08000410710eb808080001a0b0240200141800171450d00200041b0a6c08000410e10eb808080001a0b0240200141800271450d00200041e8a6c08000411b10eb808080001a0b20000f0b4128412841d892c0800010f980808000000bc53103017f047e1c7f23808080800041a00a6b2204248080808000024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240200129030022054200510d00200129030822064200510d01200129031022074200510d02200520077c22082005540d0320052006540d04200341104d0d0520012c001a210920012f01182101200420053e0200200441014102200542808080801054220a1b3602a001200441002005422088a7200a1b360204200441086a4100419801108a8e8080001a200420063e02a401200441014102200642808080801054220a1b3602c402200441002006422088a7200a1b3602a801200441a4016a41086a4100419801108a8e8080001a200420073e02c802200441014102200742808080801054220a1b3602e803200441002007422088a7200a1b3602cc02200441c8026a41086a4100419801108a8e8080001a200441f0036a4100419c01108a8e8080001a200441013602ec032004410136028c052001ad4230864230872008427f7c797d42c29ac1e8047e4280a1cda0b4027c422088a7220a411074411075210b024002402001411074411075220c4100480d002004200110ea808080001a200441a4016a200110ea808080001a200441c8026a200110ea808080001a0c010b200441ec036a4100200c6b41107441107510ea808080001a0b02400240200b417f4a0d0020044100200b6b41107441107522011090818080001a200441a4016a20011090818080001a200441c8026a20011090818080001a0c010b200441ec036a200a41ffff03711090818080001a0b20042802a001210d200441fc086a200441a00110848e8080001a2004200d36029c0a200d20042802e803220e200d200e4b1b220f41294f0d060240200f0d004100210f0c090b200f41017121100240200f4101470d0041002111410021120c080b200f417e71211341002111200441fc086a2101200441c8026a210a410021120340200120012802002214200a2802006a220c20114101716a2215360200200141046a221120112802002216200a41046a2802006a2211200c2014492015200c49726a220c3602002011201649200c201149722111200a41086a210a200141086a21012013201241026a2212470d000c080b0b41d8a8c08000411c41f4a8c0800010f880808000000b4184a9c08000411d41a4a9c0800010f880808000000b41b4a9c08000411c41d0a9c0800010f880808000000b4198abc08000413641d0abc0800010f880808000000b41d0aac0800041374188abc0800010f880808000000b41e0a9c08000412d4190aac0800010f880808000000b200f412841d892c08000109581808000000b02402010450d00200441fc086a201241027422016a220a200a280200220a200441c8026a20016a2802006a220120116a220c3602002001200a49200c2001497221110b2011410171450d00200f41274b0d01200441fc086a200f4102746a4101360200200f41016a210f0b2004200f36029c0a200428028c052212200f2012200f4b1b220141294f0d01200141027421010240024003402001450d01417f2001417c6a2201200441fc086a6a280200220a2001200441ec036a6a280200220c47200a200c4b1b220a450d000c020b0b417f410020011b210a0b0240200a2009480d000240200d0d004100210d0c050b200d417f6a41ffffffff0371220141016a220c410371210a0240200141034f0d0020042101420021050c040b200c41fcffffff0771210c2004210142002105034020012001350200420a7e20057c22053e0200200141046a22112011350200420a7e20054220887c22053e0200200141086a22112011350200420a7e20054220887c22053e02002001410c6a22112011350200420a7e20054220887c22053e020020054220882105200141106a2101200c417c6a220c0d000c040b0b200b41016a210b0c0b0b4128412841d892c0800010f980808000000b2001412841d892c08000109581808000000b0240200a450d00034020012001350200420a7e20057c22053e0200200141046a210120054220882105200a417f6a220a0d000b0b2005a72201450d00200d41274b0d012004200d4102746a2001360200200d41016a210d0b2004200d3602a00120042802c402221441294f0d0141002115410021012014450d032014417f6a41ffffffff0371220141016a220c410371210a0240200141034f0d00200441a4016a2101420021050c030b200c41fcffffff0771210c200441a4016a210142002105034020012001350200420a7e20057c22053e0200200141046a22112011350200420a7e20054220887c22053e0200200141086a22112011350200420a7e20054220887c22053e02002001410c6a22112011350200420a7e20054220887c22053e020020054220882105200141106a2101200c417c6a220c0d000c030b0b4128412841d892c0800010f980808000000b2014412841d892c08000109581808000000b0240200a450d00034020012001350200420a7e20057c22053e0200200141046a210120054220882105200a417f6a220a0d000b0b02402005a722010d00201421010c010b201441274b0d01200441a4016a20144102746a2001360200201441016a21010b200420013602c402200e450d02200e417f6a41ffffffff0371220141016a220c410371210a0240200141034f0d00200441c8026a2101420021050c020b200c41fcffffff0771210c200441c8026a210142002105034020012001350200420a7e20057c22053e0200200141046a22112011350200420a7e20054220887c22053e0200200141086a22112011350200420a7e20054220887c22053e02002001410c6a22112011350200420a7e20054220887c22053e020020054220882105200141106a2101200c417c6a220c0d000c020b0b4128412841d892c0800010f980808000000b0240200a450d00034020012001350200420a7e20057c22053e0200200141046a210120054220882105200a417f6a220a0d000b0b02402005a722010d002004200e3602e8030c020b200e41274b0d02200441c8026a200e4102746a2001360200200e41016a21150b200420153602e8030b20044190056a200441ec036a41a00110848e8080001a200420123602b00620044190056a410110ea808080002117200428028c052101200441b4066a200441ec036a41a00110848e8080001a200420013602d407200441b4066a410210ea808080002118200428028c052101200441d8076a200441ec036a41a00110848e8080001a200420013602f808200441d8076a410310ea8080800021190240024020042802a001221220042802f808221a2012201a4b1b221041284b0d0020044190056a417c6a210e200441b4066a417c6a210d200441d8076a417c6a210f200428028c05211b20042802b006211c20042802d407211d4100211e0340201e211f201041027421010240024003402001450d01417f200f20016a280200220a2001417c6a220120046a280200220c47200a200c4b1b220a450d000c020b0b417f410020011b210a0b410021200240024002400240024002400240024002400240024002400240024002400240024002400240024002400240200a41014b0d0002402010450d00410121112010410171212141002112024020104101460d002010417e712113410021124101211120042101200441d8076a210a0340200120012802002214200a280200417f736a220c20114101716a2215360200200141046a221120112802002216200a41046a280200417f736a2211200c2014492015200c49726a220c3602002011201649200c201149722111200a41086a210a200141086a21012013201241026a2212470d000b0b02402021450d002004201241027422016a220a200a280200220a201920016a280200417f736a220120116a220c3602002001200a49200c2001497221110b2011410171450d020b200420103602a00141082120201021120b2012201d2012201d4b1b221341294f0d01201341027421010240024003402001450d01417f200d20016a280200220a2001417c6a220120046a280200220c47200a200c4b1b220a450d000c020b0b417f410020011b210a0b02400240200a41014d0d00201221130c010b02402013450d00410121112013410171212141002112024020134101460d002013417e712110410021124101211120042101200441b4066a210a0340200120012802002214200a280200417f736a220c20114101716a2215360200200141046a221120112802002216200a41046a280200417f736a2211200c2014492015200c49726a220c3602002011201649200c201149722111200a41086a210a200141086a21012010201241026a2212470d000b0b02402021450d002004201241027422016a220a200a280200220a201820016a280200417f736a220120116a220c3602002001200a49200c2001497221110b2011410171450d040b200420133602a001202041047221200b2013201c2013201c4b1b222141294f0d03202141027421010240024003402001450d01417f200e20016a280200220a2001417c6a220120046a280200220c47200a200c4b1b220a450d000c020b0b417f410020011b210a0b02400240200a41014d0d00201321210c010b02402021450d00410121112021410171211041002112024020214101460d002021417e71211341002112410121112004210120044190056a210a0340200120012802002214200a280200417f736a220c20114101716a2215360200200141046a221120112802002216200a41046a280200417f736a2211200c2014492015200c49726a220c3602002011201649200c201149722111200a41086a210a200141086a21012013201241026a2212470d000b0b02402010450d002004201241027422016a220a200a280200220a201720016a280200417f736a220120116a220c3602002001200a49200c2001497221110b2011410171450d060b200420213602a001202041026a21200b2021201b2021201b4b1b221041294f0d05201041027421010240024003402001450d01417f2001417c6a2201200441ec036a6a280200220a200120046a280200220c47200a200c4b1b220a450d000c020b0b417f410020011b210a0b02400240200a41014d0d00202121100c010b02402010450d00410121112010410171212141002112024020104101460d002010417e712113410021124101211120042101200441ec036a210a0340200120012802002214200a280200417f736a220c20114101716a2215360200200141046a221120112802002216200a41046a280200417f736a2211200c2014492015200c49726a220c3602002011201649200c201149722111200a41086a210a200141086a21012013201241026a2212470d000b0b02402021450d002004201241027422016a220a200a280200220a200441ec036a20016a280200417f736a220120116a220c3602002001200a49200c2001497221110b2011410171450d080b200420103602a001202041016a21200b201f2003460d0b2002201f6a202041306a3a0000201020042802c4022222201020224b1b220141294f0d07201f41016a211e200141027421010240024003402001450d01417f2001417c6a2201200441a4016a6a280200220a200120046a280200220c47200a200c4b1b2213450d000c020b0b417f410020011b21130b200441fc086a200441a00110848e8080001a2004201036029c0a201020042802e8032223201020234b1b222041294f0d080240024020200d00410021200c010b202041017121244100211141002112024020204101460d002020417e71212141002111200441fc086a2101200441c8026a210a410021120340200120012802002214200a2802006a220c20114101716a2215360200200141046a221120112802002216200a41046a2802006a2211200c2014492015200c49726a220c3602002011201649200c201149722111200a41086a210a200141086a21012021201241026a2212470d000b0b02402024450d00200441fc086a201241027422016a220a200a280200220a200441c8026a20016a2802006a220120116a220c3602002001200a49200c2001497221110b2011410171450d00202041274b0d0a200441fc086a20204102746a4101360200202041016a21200b2004202036029c0a201b2020201b20204b1b220141294f0d0a200141027421010240024003402001450d01417f2001417c6a2201200441fc086a6a280200220a2001200441ec036a6a280200220c47200a200c4b1b220a450d000c020b0b417f410020011b210a0b024002400240201320094822010d00200a20094e0d010b200a20094e0d1c20010d010c1b0b41002114410021122010450d0f2010417f6a41ffffffff0371220141016a220c410371210a0240200141034f0d0020042101420021050c0f0b200c41fcffffff0771210c2004210142002105034020012001350200420a7e20057c22053e0200200141046a22112011350200420a7e20054220887c22053e0200200141086a22112011350200420a7e20054220887c22053e02002001410c6a22112011350200420a7e20054220887c22053e020020054220882105200141106a2101200c417c6a220c0d000c0f0b0b2004410110ea808080001a20042802a0012201200428028c05220a2001200a4b1b220141294f0d0c200141027421012004417c6a2111200441ec036a417c6a21120240024003402001450d01201120016a210a201220016a210c2001417c6a2101417f200c280200220c200a280200220a47200c200a4b1b220a450d000c020b0b417f410020011b210a0b200a4102490d190c1a0b41e892c08000411a41d892c0800010f880808000000b2013412841d892c08000109581808000000b41e892c08000411a41d892c0800010f880808000000b2021412841d892c08000109581808000000b41e892c08000411a41d892c0800010f880808000000b2010412841d892c08000109581808000000b41e892c08000411a41d892c0800010f880808000000b2001412841d892c08000109581808000000b2020412841d892c08000109581808000000b4128412841d892c0800010f980808000000b2001412841d892c08000109581808000000b2003200341a0aac0800010f980808000000b2001412841d892c08000109581808000000b0240200a450d00034020012001350200420a7e20057c22053e0200200141046a210120054220882105200a417f6a220a0d000b0b02402005a722010d00201021120c010b201041274b0d01200420104102746a2001360200201041016a21120b200420123602a0012022450d022022417f6a41ffffffff0371220141016a220c410371210a0240200141034f0d00200441a4016a2101420021050c020b200c41fcffffff0771210c200441a4016a210142002105034020012001350200420a7e20057c22053e0200200141046a22112011350200420a7e20054220887c22053e0200200141086a22112011350200420a7e20054220887c22053e02002001410c6a22112011350200420a7e20054220887c22053e020020054220882105200141106a2101200c417c6a220c0d000c020b0b4128412841d892c0800010f980808000000b0240200a450d00034020012001350200420a7e20057c22053e0200200141046a210120054220882105200a417f6a220a0d000b0b02402005a722010d00202221140c010b202241274b0d01200441a4016a20224102746a2001360200202241016a21140b200420143602c402024020230d00410021230c030b2023417f6a41ffffffff0371220141016a220c410371210a0240200141034f0d00200441c8026a2101420021050c020b200c41fcffffff0771210c200441c8026a210142002105034020012001350200420a7e20057c22053e0200200141046a22112011350200420a7e20054220887c22053e0200200141086a22112011350200420a7e20054220887c22053e02002001410c6a22112011350200420a7e20054220887c22053e020020054220882105200141106a2101200c417c6a220c0d000c020b0b4128412841d892c0800010f980808000000b0240200a450d00034020012001350200420a7e20057c22053e0200200141046a210120054220882105200a417f6a220a0d000b0b2005a72201450d00202341274b0d03200441c8026a20234102746a2001360200202341016a21230b200420233602e8032012201a2012201a4b1b221041284d0d000b0b2010412841d892c08000109581808000000b4128412841d892c0800010f980808000000b4128412841d892c0800010f980808000000b2002201e6a2112201f2101417f210a024003402001417f460d01200a41016a210a200220016a210c2001417f6a22112101200c2d00004139460d000b200220116a220c41016a220120012d000041016a3a0000201141026a201e4f0d01200c41026a4130200a108a8e8080001a0c010b02400240201e0d00413121010c010b200241313a00000240201f0d00413021010c010b41302101200241016a4130201f108a8e8080001a0b0240201e20034f0d00201220013a0000200b41016a210b201f41026a211e0c010b201e200341b0aac0800010f980808000000b0240201e20034b0d002000200b3b01082000201e36020420002002360200200441a00a6a2480808080000f0b201e200341c0aac08000109581808000000b872a03017f037e1a7f23808080800041c0066b22052480808080000240024002400240024002400240024002400240024002400240024002400240024002400240200129030022064200510d00200129030822074200510d01200129031022084200510d02200620087c2006540d0320062007540d0420012f01182101200520063e020c20054101410220064280808080105422091b3602ac01200541002006422088a720091b360210200541146a4100419801108a8e8080001a200541b4016a4100419c01108a8e8080001a200541013602b001200541013602d0022001ad4230864230872006427f7c797d42c29ac1e8047e4280a1cda0b4027c422088a72209411074411075210a024002402001411074411075220b4100480d002005410c6a200110ea808080001a0c010b200541b0016a4100200b6b41107441107510ea808080001a0b02400240200a417f4a0d002005410c6a4100200a6b4110744110751090818080001a0c010b200541b0016a200941ffff03711090818080001a0b20052802d002210c2005419c056a200541b0016a41a00110848e8080001a2005200c3602bc062003210d02402003410a490d0002400240200c41284d0d00200c21010c010b2005419c056a41786a210e2003210d200c2101034002402001450d002001417f6a41ffffffff0371220941016a220b410171210f200141027421010240024020090d002005419c056a20016a2101420021060c010b200b41feffffff07712109200e20016a2101420021060340200141046a220b2006422086200b350200842206428094ebdc038022073e0200200120062007428094ebdc037e7d4220862001350200842206428094ebdc038022073e020020062007428094ebdc037e7d2106200141786a21012009417e6a22090d000b200141086a21010b200f450d002001417c6a22012006422086200135020084428094ebdc03803e02000b200d41776a220d41094d0d0220052802bc0622014129490d000b0b2001412841d892c08000109581808000000b200d41027441d4a5c080006a2802002209450d0520052802bc06220141294f0d060240024020010d00410021010c010b2001417f6a41ffffffff0371220b41016a220f410171210d200141027421012009ad210602400240200b0d002005419c056a20016a2101420021070c010b200f41feffffff0771210920012005419c056a6a41786a2101420021070340200141046a220b2007422086200b35020084220720068022083e020020012007200820067e7d422086200135020084220720068022083e02002007200820067e7d2107200141786a21012009417e6a22090d000b200141086a21010b0240200d450d002001417c6a220120074220862001350200842006803e02000b20052802bc0621010b200120052802ac012210200120104b1b221141294f0d07024020110d00410021110c0a0b20114101712112024020114101470d004100210d4100210f0c090b2011417e7121134100210d2005419c056a21012005410c6a21094100210f034020012001280200220e20092802006a220b200d4101716a2214360200200141046a220d200d2802002215200941046a2802006a220d200b200e492014200b49726a220b360200200d201549200b200d4972210d200941086a2109200141086a21012013200f41026a220f470d000c090b0b41d8a8c08000411c41e0abc0800010f880808000000b4184a9c08000411d41f0abc0800010f880808000000b41b4a9c08000411c4180acc0800010f880808000000b4198abc08000413641f0acc0800010f880808000000b41d0aac08000413741e0acc0800010f880808000000b419f93c08000411b41d892c0800010f880808000000b2001412841d892c08000109581808000000b2011412841d892c08000109581808000000b02402012450d002005419c056a200f41027422016a2209200928020022092005410c6a20016a2802006a2201200d6a220b3602002001200949200b20014972210d0b200d410171450d00201141274b0d012005419c056a20114102746a4101360200201141016a21110b200520113602bc062011200c2011200c4b1b220141294f0d01200141027421010240024003402001450d01417f2001417c6a2201200541b0016a6a280200220920012005419c056a6a280200220b472009200b4b1b2209450d000c020b0b417f410020011b21090b0240200941014b0d00200a41016a210a0c050b024020100d00410021100c040b2010417f6a41ffffffff0371220141016a220b41037121090240200141034f0d002005410c6a2101420021060c030b200b41fcffffff0771210b2005410c6a210142002106034020012001350200420a7e20067c22063e0200200141046a220d200d350200420a7e20064220887c22063e0200200141086a220d200d350200420a7e20064220887c22063e02002001410c6a220d200d350200420a7e20064220887c22063e020020064220882106200141106a2101200b417c6a220b0d000c030b0b4128412841d892c0800010f980808000000b2001412841d892c08000109581808000000b02402009450d00034020012001350200420a7e20067c22063e0200200141046a2101200642208821062009417f6a22090d000b0b2006a72201450d00201041274b0d022005410c6a20104102746a2001360200201041016a21100b200520103602ac010b4100210e02400240200a411074411075220120044110744110752209480d00200a20046b4110744110752003200120096b2003491b220d0d014100210e0b4100210d0c020b200541d4026a200541b0016a41a00110848e8080001a2005200c3602f403200541d4026a410110ea80808000211620052802d0022101200541f8036a200541b0016a41a00110848e8080001a2005200136029805200541f8036a410210ea80808000211720052802d00221012005419c056a200541b0016a41a00110848e8080001a200520013602bc06200541b0016a417c6a2113200541d4026a417c6a2115200541f8036a417c6a21142005419c056a417c6a210e2005419c056a410310ea80808000211820052802d002210c20052802f4032119200528029805211a20052802bc06211b20052802ac0121104100211c02400340201c211d0240024002400240024002400240024002400240024002400240201041294f0d00201d41016a211c2010410274210b410021010240024002400340200b2001460d012005410c6a20016a2109200141046a21012009280200450d000b2010201b2010201b4b1b221e41294f0d04201e41027421010240024003402001450d01417f200e20016a28020022092001417c6a22012005410c6a6a280200220b472009200b4b1b2209450d000c020b0b417f410020011b21090b4100211f0240200941024f0d000240201e450d004101210f201e410171211f410021100240201e4101460d00201e417e712120410021104101210f2005410c6a21012005419c056a210903402001200128020022112009280200417f736a220b200f4101716a2212360200200141046a220f200f2802002221200941046a280200417f736a220f200b2011492012200b49726a220b360200200f202149200b200f4972210f200941086a2109200141086a21012020201041026a2210470d000b0b0240201f450d002005410c6a201041027422016a220920092802002209201820016a280200417f736a2201200f6a220b3602002001200949200b20014972210f0b200f410171450d080b2005201e3602ac014108211f201e21100b2010201a2010201a4b1b222041294f0d072020410274210103402001450d02417f201420016a28020022092001417c6a22012005410c6a6a280200220b472009200b4b1b2209450d000c030b0b200d20034b0d04200d201d460d132002201d6a4130200d201d6b108a8e8080001a0c130b417f410020011b21090b02400240200941014d0d00201021200c010b02402020450d004101210f2020410171212241002110024020204101460d002020417e71211e410021104101210f2005410c6a2101200541f8036a210903402001200128020022112009280200417f736a220b200f4101716a2212360200200141046a220f200f2802002221200941046a280200417f736a220f200b2011492012200b49726a220b360200200f202149200b200f4972210f200941086a2109200141086a2101201e201041026a2210470d000b0b02402022450d002005410c6a201041027422016a220920092802002209201720016a280200417f736a2201200f6a220b3602002001200949200b20014972210f0b200f410171450d070b200520203602ac01201f410472211f0b20202019202020194b1b221e41294f0d06201e41027421010240024003402001450d01417f201520016a28020022092001417c6a22012005410c6a6a280200220b472009200b4b1b2209450d000c020b0b417f410020011b21090b02400240200941014d0d002020211e0c010b0240201e450d004101210f201e4101712122410021100240201e4101460d00201e417e712120410021104101210f2005410c6a2101200541d4026a210903402001200128020022112009280200417f736a220b200f4101716a2212360200200141046a220f200f2802002221200941046a280200417f736a220f200b2011492012200b49726a220b360200200f202149200b200f4972210f200941086a2109200141086a21012020201041026a2210470d000b0b02402022450d002005410c6a201041027422016a220920092802002209201620016a280200417f736a2201200f6a220b3602002001200949200b20014972210f0b200f410171450d090b2005201e3602ac01201f41026a211f0b201e200c201e200c4b1b221041294f0d08201041027421010240024003402001450d01417f201320016a28020022092001417c6a22012005410c6a6a280200220b472009200b4b1b2209450d000c020b0b417f410020011b21090b02400240200941014d0d00201e21100c010b02402010450d004101210f2010410171212241002111024020104101460d002010417e71211e410021114101210f2005410c6a2101200541b0016a210903402001200128020022122009280200417f736a220b200f4101716a2221360200200141046a220f200f2802002220200941046a280200417f736a220f200b2012492021200b49726a220b360200200f202049200b200f4972210f200941086a2109200141086a2101201e201141026a2211470d000b0b02402022450d002005410c6a201141027422016a220920092802002209200541b0016a20016a280200417f736a2201200f6a220b3602002001200949200b20014972210f0b200f410171450d0b0b200520103602ac01201f41016a211f0b0240201d2003460d002002201d6a201f41306a3a0000201041294f0d0b024020100d00410021100c0e0b2010417f6a41ffffffff0371220141016a220b41037121090240200141034f0d002005410c6a2101420021060c0d0b200b41fcffffff0771210b2005410c6a210142002106034020012001350200420a7e20067c22063e0200200141046a220f200f350200420a7e20064220887c22063e0200200141086a220f200f350200420a7e20064220887c22063e02002001410c6a220f200f350200420a7e20064220887c22063e020020064220882106200141106a2101200b417c6a220b0d000c0d0b0b2003200341c0acc0800010f980808000000b2010412841d892c08000109581808000000b201e412841d892c08000109581808000000b200d200341d0acc08000109581808000000b41e892c08000411a41d892c0800010f880808000000b2020412841d892c08000109581808000000b41e892c08000411a41d892c0800010f880808000000b201e412841d892c08000109581808000000b41e892c08000411a41d892c0800010f880808000000b2010412841d892c08000109581808000000b41e892c08000411a41d892c0800010f880808000000b2010412841d892c08000109581808000000b02402009450d00034020012001350200420a7e20067c22063e0200200141046a2101200642208821062009417f6a22090d000b0b2006a72201450d00201041274b0d022005410c6a20104102746a2001360200201041016a21100b200520103602ac01201c200d470d000b4101210e0c020b4128412841d892c0800010f980808000000b4128412841d892c0800010f980808000000b0240024002400240024002400240200c41294f0d000240200c0d004100210c0c030b200c417f6a41ffffffff0371220141016a220b41037121090240200141034f0d00200541b0016a2101420021060c020b200b41fcffffff0771210b200541b0016a21014200210603402001200135020042057e20067c22063e0200200141046a220f200f35020042057e20064220887c22063e0200200141086a220f200f35020042057e20064220887c22063e02002001410c6a220f200f35020042057e20064220887c22063e020020064220882106200141106a2101200b417c6a220b0d000c020b0b200c412841d892c08000109581808000000b02402009450d0003402001200135020042057e20067c22063e0200200141046a2101200642208821062009417f6a22090d000b0b2006a72201450d00200c41274b0d01200541b0016a200c4102746a2001360200200c41016a210c0b2005200c3602d0022010200c2010200c4b1b220141294f0d0120014102742101024002400240024003402001450d01417f2001417c6a2201200541b0016a6a280200220920012005410c6a6a280200220b472009200b4b1b2209450d000b200941ff01714101460d010c070b200e20014571450d06200d417f6a220120034f0d01200220016a2d0000410171450d060b200d20034b0d042002200d6a210f410021012002210902400340200d2001460d01200141016a21012009417f6a2209200d6a220b2d00004139460d000b200b200b2d000041016a3a0000200d20016b41016a200d4f0d06200b41016a41302001417f6a108a8e8080001a0c060b02400240200d0d00413121010c010b200241313a000041302101200d4101460d0041302101200241016a4130200d417f6a108a8e8080001a0b200a411074418080046a411075220a20044110744110754a0d010c050b200120034190acc0800010f980808000000b200d20034f0d03200f20013a0000200d41016a210d0c030b4128412841d892c0800010f980808000000b2001412841d892c08000109581808000000b200d200341a0acc08000109581808000000b200d20034b0d010b2000200a3b01082000200d36020420002002360200200541c0066a2480808080000f0b200d200341b0acc08000109581808000000b0d0020002802001a037f0c000b0b870101017f23808080800041306b22032480808080002003200036020020032001360204200341086a410c6a4202370200200341206a410c6a4185808080003602002003410236020c200341b4adc0800036020820034185808080003602242003200341206a3602102003200341046a36022820032003360220200341086a200210f680808000000b870101017f23808080800041306b22032480808080002003200036020020032001360204200341086a410c6a4202370200200341206a410c6a4185808080003602002003410236020c200341d4adc0800036020820034185808080003602242003200341206a3602102003200341046a36022820032003360220200341086a200210f680808000000b870101017f23808080800041306b22032480808080002003200036020020032001360204200341086a410c6a4202370200200341206a410c6a4185808080003602002003410236020c20034188aec0800036020820034185808080003602242003200341206a3602102003200341046a36022820032003360220200341086a200210f680808000000b130020002001200220032004109881808000000bf70901057f23808080800041f0006b22052480808080002005200336020c20052002360208024002402001418102490d004180022106024020002c00800241bf7f4a0d0041ff01210620002c00ff0141bf7f4a0d0041fe01210620002c00fe0141bf7f4a0d0041fd0121060b2005200636021420052000360210410521064198aec0800021070c010b2005200136021420052000360210410021064180adc0800021070b2005200636021c2005200736021802400240024002400240200220014b22060d00200320014b0d00200220034b0d01024002402002450d00200220014f0d00200020026a2c00004140480d010b200321020b20052002360220200121030240200220014f0d0041002002417d6a2203200320024b1b2203200241016a22064b0d03024020032006460d00200020066a200020036a22086b21060240200020026a22092c000041bf7f4c0d002006417f6a21070c010b20032002460d0002402009417f6a22022c000041bf7f4c0d002006417e6a21070c010b20082002460d0002402009417e6a22022c000041bf7f4c0d002006417d6a21070c010b20082002460d0002402009417d6a22022c000041bf7f4c0d002006417c6a21070c010b20082002460d002006417b6a21070b200720036a21030b02402003450d0002400240200120034b0d0020012003460d010c070b200020036a2c000041bf7f4c0d060b200120036b21010b2001450d030240024002400240200020036a22012c00002202417f4a0d0020012d0001413f7121002002411f7121062002415f4b0d01200641067420007221010c020b2005200241ff0171360224410121020c020b200041067420012d0002413f717221000240200241704f0d0020002006410c747221010c010b200041067420012d0003413f71722006411274418080f00071722201418080c400460d050b20052001360224410121022001418001490d00410221022001418010490d0041034104200141808004491b21020b200520033602282005200220036a36022c200541306a410c6a4205370200200541ec006a41a280808000360200200541e4006a41a280808000360200200541dc006a41ac80808000360200200541c8006a410c6a41ad8080800036020020054105360234200541a0afc08000360230200541858080800036024c2005200541c8006a3602382005200541186a3602682005200541106a3602602005200541286a3602582005200541246a3602502005200541206a360248200541306a200410f680808000000b20052002200320061b360228200541306a410c6a4203370200200541dc006a41a280808000360200200541c8006a410c6a41a28080800036020020054103360234200541e0afc08000360230200541858080800036024c2005200541c8006a3602382005200541186a3602582005200541106a3602502005200541286a360248200541306a200410f680808000000b200541e4006a41a280808000360200200541dc006a41a280808000360200200541c8006a410c6a418580808000360200200541306a410c6a420437020020054104360234200541c0aec08000360230200541858080800036024c2005200541c8006a3602382005200541186a3602602005200541106a36025820052005410c6a3602502005200541086a360248200541306a200410f680808000000b2003200641e8b0c08000109681808000000b200410a081808000000b20002001200320012004109781808000000b8a0301077f41012107024002402002450d00200120024101746a210820004180fe037141087621094100210a200041ff0171210b0340200141026a210c200a20012d000122026a210d024020012d000022012009460d00200120094b0d02200d210a200c2101200c2008460d020c010b024002400240200a200d4b0d00200d20044b0d012003200a6a210103402002450d032002417f6a210220012d0000210a200141016a2101200a200b470d000b410021070c050b200a200d4184b2c08000109681808000000b200d20044184b2c08000109581808000000b200d210a200c2101200c2008470d000b0b2006450d00200520066a210b200041ffff0371210a200520064100476a21024101210703400240024020052d00002201411874411875220d4100480d00200221050c010b02402002200b460d00200241016a2105200d41ff007141087420022d00007221010c010b41f4b1c0800010a081808000000b200a20016b220a4100480d012007410173210720052005200b4722016a210220010d000b0b20074101710ba10201017f0240200041204f0d0041000f0b4101210102400240200041ff00490d00200041808004490d0102400240200041808008490d000240200041d0b8736a41d0ba2b4f0d0041000f0b0240200041b5d9736a41054f0d0041000f0b0240200041e28b746a41e20b4f0d0041000f0b02402000419fa8746a419f184f0d0041000f0b0240200041dee2746a410e4f0d0041000f0b02402000417e71419ef00a470d0041000f0b200041607141e0cd0a470d0141000f0b20004194b2c08000412c41ecb2c0800041c40141b0b4c0800041c2031099818080000f0b41002101200041c691756a4106490d002000418080bc7f6a41f083744921010b20010f0b200041f2b7c08000412841c2b8c08000419f0241e1bac0800041af021099818080000bed0201037f2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000be40201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d012000ad4101200110fe8080800021000c020b410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000bb01107017f037e027f127e027f027e037f23808080800041306b22042480808080000240024002400240024002400240024002400240024002400240200129030022054200510d00200129030822064200510d01200129031022074200510d02200520077c22072005540d0320052006540d04200341104d0d052007428080808080808080205a0d06200420012f011822013b01082004200520067d22063703002001200141606a200120074280808080105422081b220941706a20092007422086200720081b220742808080808080c0005422081b220941786a20092007421086200720081b2207428080808080808080015422081b2209417c6a20092007420886200720081b2207428080808080808080105422081b2209417e6a20092007420486200720081b2207428080808080808080c0005422081b2007420286200720081b220a427f556b22086b4110744110752209417f4c0d07200420062009ad220786220b200788220c370310200c2006520d08200420013b010820042005370300200420052007423f832206862207200688220637031020062005520d0941a07f20086b41107441107541d0006c41b0a7056a41ce106d220141d1004f0d0a200141047422014188c0c080006a290300220642ffffffff0f8322052007422088220d7e220c422088220e2006422088220f200d7e7c200f200742ffffffff0f8322077e220642208822107c2111200c42ffffffff0f83200520077e4220887c200642ffffffff0f837c4280808080087c422088211242014100200820014190c0c080006a2f01006a6b413f71ad2207862213427f7c21142005200b42208822067e220c42ffffffff0f832005200b42ffffffff0f83220b7e4220887c200f200b7e220b42ffffffff0f837c4280808080087c4220882115200f20067e2106200b422088210b200c422088210c20014192c0c080006a2f010021010240200f200a200a427f85423f8886220a42208822167e2217200520167e221842208822197c200f200a42ffffffff0f83220a7e221a422088221b7c201842ffffffff0f832005200a7e4220887c201a42ffffffff0f837c4280808080087c422088221a7c42017c2218200788a722094190ce00490d00200941c0843d490d0c024020094180c2d72f490d00410841092009418094ebdc034922081b211c4180c2d72f418094ebdc0320081b21080c0e0b4106410720094180ade2044922081b211c41c0843d4180ade20420081b21080c0d0b0240200941e400490d0041024103200941e8074922081b211c41e40041e80720081b21080c0d0b410a4101200941094b221c1b21080c0c0b41accbc08000411c41c8cbc0800010f880808000000b41d8cbc08000411d41f8cbc0800010f880808000000b4188ccc08000411c41a4ccc0800010f880808000000b41d4cec080004136418ccfc0800010f880808000000b418ccec08000413741c4cec0800010f880808000000b41b4ccc08000412d41e4ccc0800010f880808000000b41f4ccc08000412d41a4cdc0800010f880808000000b41b4bec08000411d41c8bfc0800010f880808000000b200441003602184100200441106a2004200441186a41d8bfc0800010fa80808000000b200441003602184100200441106a2004200441186a41d8bfc0800010fa80808000000b200141d100419ccbc0800010f980808000000b41044105200941a08d064922081b211c4190ce0041a08d0620081b21080b201120127c211120182014832105201c20016b41016a211d2018200c20067c200b7c20157c221e7d221f42017c220c201483210a41002101024002400240024002400240024002400340200920086e212020032001460d02200220016a2221202041306a22223a000002400240200c2009202020086c6b2209ad200786220620057c220b560d00201c2001470d01200141016a21014201210603402006210b200a210c200120034f0d06200220016a2005420a7e2205200788a741306a22083a0000200141016a2101200b420a7e2106200c420a7e220a20052014832205580d000b2006201820117d7e220720067c2112200a20057d20135422090d07200720067d22142005560d030c070b200c200b7d220c2008ad2007862207542108201820117d220a42017c2115200a427f7c2213200b580d05200c2007540d05200520077c220b200e7c20107c20127c200f200d20167d7e7c20197d201b7d201a7d210c2019201b7c201a7c20177c210a42002011200620057c7c7d210d4202201e200b20067c7c7d2118034002402006200b7c22142013540d00200d200a7c2006200c7c5a0d00200620057c210b410021080c070b20212022417f6a22223a0000200520077c21052018200a7c210f0240201420135a0d00200b20077c210b200c20077c210c200a20077d210a200f20075a0d010b0b200f2007542108200620057c210b0c050b200141016a21012008410a4921202008410a6e21082020450d000b41d0cdc08000411941b4cdc0800010f880808000000b200220016a417f6a2120200c420a7e201320057c7d210f201320147d2118201420057d210d4200210703400240200520137c22062014540d00200d20077c201820057c5a0d00410021090c050b20202008417f6a22083a0000200f20077c220c2013542109200620145a0d05200720137d210720062105200c2013540d050c000b0b2003200341eccdc0800010f980808000000b2001200341fccdc0800010f980808000000b02402015200b580d0020080d000240200b20077c22052015540d002015200b7d200520157d540d010b200041003602000c040b02400240200b4202540d00200b201f427d7c580d010b200041003602000c040b2000201d3b01082000200141016a3602040c020b200521060b024020122006580d0020090d000240200620137c22052012540d00201220067d200520127d540d010b200041003602000c020b02400240200b42147e2006560d002006200b42587e200a7c580d010b200041003602000c020b2000201d3b0108200020013602040b200020023602000b200441306a2480808080000b9e0906017e017f047e027f017e057f0240024002400240024002400240200129030022054200510d002005428080808080808080205a0d012003450d0241a07f20012f0118220141606a200120054280808080105422061b220141706a20012005422086200520061b220542808080808080c0005422061b220141786a20012005421086200520061b2205428080808080808080015422061b2201417c6a20012005420886200520061b2205428080808080808080105422061b2201417e6a20012005420486200520061b2205428080808080808080c0005422061b2005420286200520061b2205427f556b22066b41107441107541d0006c41b0a7056a41ce106d220141d1004f0d03200141047422014188c0c080006a290300220742ffffffff0f83220820052005427f85423f8886220542208822097e220a4220882007422088220720097e7c2007200542ffffffff0f8322057e22074220887c200a42ffffffff0f83200820057e4220887c200742ffffffff0f837c4280808080087c4220887c22054140200620014190c0c080006a2f01006a6b220b413f71ad220888a7210c20014192c0c080006a2f01002101024020054201200886220d427f7c22098322074200520d002003410a4b0d0720034102744190d0c080006a280200200c4b0d070b0240200c4190ce00490d00200c41c0843d490d050240200c4180c2d72f490d0041084109200c418094ebdc034922061b210e4180c2d72f418094ebdc0320061b21060c070b41064107200c4180ade2044922061b210e41c0843d4180ade20420061b21060c060b0240200c41e400490d0041024103200c41e8074922061b210e41e40041e80720061b21060c060b410a4101200c41094b220e1b21060c050b41accbc08000411c41c0cfc0800010f880808000000b41d0cfc08000412441f4cfc0800010f880808000000b419ccfc0800041214184d0c0800010f880808000000b200141d100419ccbc0800010f980808000000b41044105200c41a08d064922061b210e4190ce0041a08d0620061b21060b02400240024002400240200e20016b411074418080046a411075220f200441107441107522014c0d00200b41ffff03712110200f20046b4110744110752003200f20016b2003491b2211417f6a2112410021010340200c20066e210b20032001460d03200c200b20066c6b210c200220016a200b41306a3a000020122001460d04200e2001460d02200141016a21012006410a49210b2006410a6e2106200b450d000b41d0cdc08000411941bcd0c0800010f880808000000b2000200220034100200f20042005420a802006ad200886200d109f818080000f0b200141016a21012010417f6a413f71ad210a42012105034002402005200a88500d00200041003602000f0b200120034f0d03200220016a2007420a7e2207200888a741306a3a00002005420a7e2105200720098321072011200141016a2201470d000b2000200220032011200f20042007200d2005109f818080000f0b2003200341ccd0c0800010f980808000000b2000200220032011200f2004200cad20088620077c2006ad200886200d109f818080000f0b2001200341dcd0c0800010f980808000000b200041003602000bae0301047f024002400240024002400240024020072008580d00200720087d2008580d01024002400240200720067d2006580d00200720064201867d20084201865a0d010b20062008560d010c080b200320024b0d030c060b2007200620087d22087d2008560d06200320024b0d03200120036a21094100210a2001210b024003402003200a460d01200a41016a210a200b417f6a220b20036a220c2d00004139460d000b200c200c2d000041016a3a00002003200a6b41016a20034f0d05200c41016a4130200a417f6a108a8e8080001a0c050b0240024020030d004131210a0c010b200141313a00004130210a20034101460d004130210a200141016a41302003417f6a108a8e8080001a0b2004411074418080046a4110752104200320024f0d04200420054110744110754c0d042009200a3a0000200341016a21030c040b200041003602000f0b200041003602000f0b20032002418cd1c08000109581808000000b2003200241ecd0c08000109581808000000b200320024d0d002003200241fcd0c08000109581808000000b200020043b010820002003360204200020013602000f0b200041003602000b1300419cd1c08000412b200010f880808000000b6c01017f23808080800041306b22032480808080002003200136020c200320003602082003411c6a420137020020034101360214200341c8d1c08000360210200341a28080800036022c2003200341286a3602182003200341086a360228200341106a200210f680808000000b870101017f23808080800041306b22032480808080002003200136020420032000360200200341086a410c6a4202370200200341206a410c6a4185808080003602002003410336020c20034194d2c0800036020820034185808080003602242003200341206a360210200320033602282003200341046a360220200341086a200210f680808000000b840602087e0a7f200020013502102202421a8820013502147c220342198820013502187c2204421a88200135021c7c220542198820013502207c2206421a8820013502247c220742198842137e2001350200220842ffffff1f837c2209a741ffffff1f71220a41136a411a762009421a882008421a8820013502047c220842ffffff0f837ca7220b6a411976200842198820013502087c2208a741ffffff1f71220c6a411a762008421a88200135020c7c2208a741ffffff0f71220d6a4119762008421988200242ffffff1f837c2202a741ffffff1f71220e6a411a762002421a88200342ffffff0f837ca7220f6a4119762004a741ffffff1f7122106a411a762005a741ffffff0f7122116a4119762006a741ffffff1f7122126a411a762007a741ffffff0f7122136a41197641136c200a6a22013a0000200020014110763a0002200020014108763a000120002001411a76200b6a220a410e763a00052000200a4106763a00042000200a4102742001411876410371723a00032000200a411976200c6a2201410d763a0008200020014105763a000720002001410374200a418080800e71411676723a000620002001411a76200d6a220a410b763a000b2000200a4103763a000a2000200a4105742001411576411f71723a00092000200a411976200e6a22014112763a000f20002001410a763a000e200020014102763a000d20002001411a76200f6a220b3a001020002001410674200a411376413f71723a000c2000200b4110763a00122000200b4108763a00112000200b41197620106a2201410f763a0015200020014107763a001420002001410174200b411876410171723a001320002001411a7620116a220a410d763a00182000200a4105763a00172000200a4103742001411776410771723a00162000200a41197620126a2201410c763a001b200020014104763a001a20002001410474200a411576410f71723a001920002001411a7620136a220a410a763a001e2000200a4102763a001d2000200a418080f00f714112763a001f2000200a4106742001411476413f71723a001c0b832404017f067e0f7f027e23808080800041f0076b2203248080808000200341a0076a200210a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f7136029007200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f7136028007200320032903d8072006421a887c2206a741ffffff0f7136029407200320032903b8072009421a887c2209a741ffffff0f71360284072003200642198820032903e0077c2206a741ffffff1f713602980720032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e028c0720032004a741ffffff1f713602880720032006421a8820032903e8077c2204a741ffffff0f7136029c072003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02fc0620032004a741ffffff1f713602f806200341086a200341f8066a200210a681808000200341a0076a200341086a10a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f7136029007200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f7136028007200320032903d8072006421a887c2206a741ffffff0f7136029407200320032903b8072009421a887c2209a741ffffff0f71360284072003200642198820032903e0077c2206a741ffffff1f713602980720032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e028c0720032004a741ffffff1f713602880720032006421a8820032903e8077c2204a741ffffff0f7136029c072003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02fc0620032004a741ffffff1f713602f806200341306a200341f8066a200210a681808000200341d8006a2001200341086a10a68180800020034180016a2001200341306a10a681808000200341a0076a20034180016a10a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f713602c001200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f713602b001200320032903d8072006421a887c2206a741ffffff0f713602c401200320032903b8072009421a887c2209a741ffffff0f713602b4012003200642198820032903e0077c2206a741ffffff1f713602c80120032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e02bc0120032004a741ffffff1f713602b80120032006421a8820032903e8077c2204a741ffffff0f713602cc012003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02ac0120032004a741ffffff1f713602a801200341a0076a200341a8016a10a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f7136029007200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f7136028007200320032903d8072006421a887c2206a741ffffff0f7136029407200320032903b8072009421a887c2209a741ffffff0f71360284072003200642198820032903e0077c2206a741ffffff1f713602980720032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e028c0720032004a741ffffff1f713602880720032006421a8820032903e8077c2204a741ffffff0f7136029c072003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02fc0620032004a741ffffff1f713602f806200341a0076a200341f8066a10a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f713602e801200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f713602d801200320032903d8072006421a887c2206a741ffffff0f713602ec01200320032903b8072009421a887c2209a741ffffff0f713602dc012003200642198820032903e0077c2206a741ffffff1f713602f00120032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e02e40120032004a741ffffff1f713602e00120032006421a8820032903e8077c2204a741ffffff0f713602f4012003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02d40120032004a741ffffff1f713602d001200341f8016a20034180016a200341d0016a10a681808000200341a0026a200341a8016a200341f8016a10a681808000200341a0076a200341a0026a10a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f713602e002200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f713602d002200320032903d8072006421a887c2206a741ffffff0f713602e402200320032903b8072009421a887c2209a741ffffff0f713602d4022003200642198820032903e0077c2206a741ffffff1f713602e80220032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e02dc0220032004a741ffffff1f713602d80220032006421a8820032903e8077c2204a741ffffff0f713602ec022003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02cc0220032004a741ffffff1f713602c802200341f0026a200341f8016a200341c8026a10a68180800020034198036a200341f0026a410510a781808000200341c0036a20034198036a200341f0026a10a681808000200341e8036a200341c0036a410a10a78180800020034190046a200341e8036a200341c0036a10a681808000200341b8046a20034190046a411410a781808000200341e0046a200341b8046a20034190046a10a68180800020034188056a200341e0046a410a10a781808000200341b0056a20034188056a200341c0036a10a681808000200341d8056a200341b0056a413210a78180800020034180066a200341d8056a200341b0056a10a681808000200341a8066a20034180066a41e40010a781808000200341d0066a200341a8066a20034180066a10a681808000200341f8066a200341d0066a413210a781808000200341a0076a200341f8066a200341b0056a10a681808000200341f8066a41206a200341a0076a41206a290200370300200341f8066a41186a200341a0076a41186a290200370300200341f8066a41106a200341a0076a41106a290200370300200341f8066a41086a200341a0076a41086a290200370300200320032902a0073703f806200341a0076a200341f8066a410210a781808000200341d0066a20034180016a200341a0076a10a681808000200341a8066a200341d8006a200341d0066a10a681808000200341a0076a200341a8066a10a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f7136029007200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f7136028007200320032903d8072006421a887c2206a741ffffff0f7136029407200320032903b8072009421a887c2209a741ffffff0f71360284072003200642198820032903e0077c2206a741ffffff1f713602980720032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e028c0720032004a741ffffff1f713602880720032006421a8820032903e8077c2204a741ffffff0f7136029c072003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02fc0620032004a741ffffff1f713602f806200341d0066a2002200341f8066a10a681808000200341f8066a200341d0066a10a381808000200341a0076a200110a381808000410021024101210a0340200341f8066a20026a2d0000200341a0076a20026a2d00004610ad8d808000200a71210a200241016a22024120470d000b200a10ad8d808000210b200341f0ffffff0320012802106bad2204421a8841f0ffffff0120012802146bad7c220542198841f0ffffff0320012802186bad7c2206a741ffffff1f71220c3602b807200341d0fdffff0320012802006bad2207421a8841f0ffffff0120012802046bad7c220842198841f0ffffff0320012802086bad7c2209a741ffffff1f71220d3602a80720032006421a8841f0ffffff01200128021c6bad7c2206a741ffffff0f71220e3602bc0720032009421a8841f0ffffff01200128020c6bad7c2209a741ffffff0f71220f3602ac072003200642198841f0ffffff0320012802206bad7c2206a741ffffff1f7122103602c00720032009421988200442ffffff1f837c2204a741ffffff1f7122113602b00720032004421a88200542ffffff0f837ca722123602b40720032006421a8841f0ffffff0120012802246bad7c2204a741ffffff0f7122013602c4072003200442198842137e200742ffffff1f837c2204a741ffffff1f7122133602a00720032004421a88200842ffffff0f837ca722143602a40720034180066a200341d0066a10a381808000200341f8066a200341a0076a10a381808000410021024101210a034020034180066a20026a2d0000200341f8066a20026a2d00004610ad8d808000200a71210a200241016a22024120470d000b200a10ad8d8080002115200320013602c407200320103602c0072003200e3602bc072003200c3602b807200320123602b407200320113602b0072003200f3602ac072003200d3602a807200320143602a407200320133602a007200341f8066a200341a0076a41d4d2c0800010a681808000200341d8056a200341d0066a10a38180800020034180066a200341f8066a10a381808000410021024101210a0340200341d8056a20026a2d000020034180066a20026a2d00004610ad8d808000200a71210a200241016a22024120470d000b200a10ad8d8080002102200341a0076a41d4d2c08000200341a8066a10a681808000200220157210ad8d8080002102200341b0066a220a20032802a807200a2802002201734100200241ff01716b2202712001732201360200200341b8066a220c20032802b007200c280200220d73200271200d73220d360200200341c0066a220e20032802b807200e280200220f73200271200f73220f360200200320032802ac0720032802b40622107320027120107322103602b406200320032802a40720032802ac0622117320027120117322113602ac06200320032802a00720032802a80622127320027120127322123602a806200320032802b40720032802bc0622137320027120137322133602bc06200320032802bc0720032802c40622147320027120147322143602c406200341c8066a221620032802c00720162802002217732002712017732217360200200320032802c40720032802cc0622187320027120187322183602cc06200341f8066a200341a8066a10a381808000201641f0ffffff03200d6bad2204421a8841f0ffffff0120136bad7c220542198841f0ffffff03200f6bad7c2206421a8841f0ffffff0120146bad7c220742198841f0ffffff0320176bad7c2208a741ffffff1f71201773410020032d00f80641017110ad8d80800041ff01716b220271201773360200200e2006a741ffffff1f71200f73200271200f73360200200c200442ffffff1f8341d0fdffff0320126bad2204421a8841f0ffffff0120116bad7c220642198841f0ffffff0320016bad7c2209421a8841f0ffffff0120106bad7c22194219887c221aa741ffffff1f71200d73200271200d73360200200a2009a741ffffff1f7120017320027120017336020020032008421a8841f0ffffff0120186bad7c2208a741ffffff0f712018732002712018733602cc0620032007a741ffffff0f712014732002712014733602c40620032013200542ffffff0f83201a421a887ca7732002712013733602bc0620032019a741ffffff0f712010732002712010733602b4062003200842198842137e200442ffffff1f837c2204a741ffffff1f712012732002712012733602a806200320112004421a88200642ffffff0f837ca7732002712011733602ac0620002015200b7210ad8d8080003a0000200041246a20162902003702002000411c6a200e290200370200200041146a200c2902003702002000410c6a200a290200370200200020032902a806370204200341f0076a2480808080000bcd0412017f017e017f017e017f017e017f017e017f017e017f017e017f017e017f017e017f0c7e2000200128020c2202ad220320012802002204410174ad22057e20012802042206410174ad220720012802082208ad22097e7c2001280220220a41136cad220b2001280214220c410174ad220d7e7c2001280224220e41136cad220f20012802102210ad22117e200128021c221241136cad221320012802182201ad22147e7c4201867c3703182000200141136cad2215200d7e20052006ad22167e7c200b2002410174ad22177e7c200f20097e201320117e7c4201867c3703082000201420177e2010410174ad2218200cad22197e7c2012ad221a2008410174ad221b7e7c200aad221c20077e7c200ead221d20057e7c37034820002019201b7e201720117e7c201420077e7c201a20057e7c201c200f7e4201867c3703382000201120077e201b20037e7c201920057e7c200b2012410174ad221e7e7c2014200f7e4201867c3703282000201720077e200920097e7c201120057e7c200b2001410174ad7e7c200f200d7e2013201a7e7c4201867c3703202000200920057e200720167e7c201520147e7c200b20187e7c200f20177e2013200d7e7c4201867c3703102000201520187e2004ad220920097e7c200b201b7e7c201320177e200c41136cad20197e7c200f20077e7c4201867c37030020002014201b7e201120117e7c200d20177e7c201e20077e7c201c20057e7c201d200f7e4201867c3703402000201720037e2011201b7e7c200d20077e7c201420057e7c200b201c7e7c201e200f7e4201867c3703300bc7081a017f017e017f017e017f017e017f017e017f017e017f017e017f017e017f027e017f017e017f017e017f027e017f027e017f147e2000200128020c2203410174ad2204200228020c2205ad22067e20012802042207410174ad220820022802142209ad220a7e7c2001280214220b410174ad220c2002280204220dad220e7e7c200128021c220f410174ad22102002280224221141136cad22127e7c2001350200221320022802182214ad22157e7c20012802242216410174ad2217200228021c221841136cad22197e7c2001350208221a2002280210221bad221c7e7c2001350210221d2002280208221ead221f7e7c20013502182220200235020022217e7c200135022022222002280220220241136cad22237e7c2003ad2224201f7e2007ad2225201c7e7c200fad222620237e7c2016ad2227201441136cad22287e7c2013200a7e7c2021200bad22297e7c201a20067e7c201d200e7e7c202020127e7c202220197e7c2004200e7e200820067e7c200c20127e7c201020197e7c2013201c7e7c2017200941136cad222a7e7c201a201f7e7c201d20217e7c202020237e7c202220287e7c222b421a887c222c4219887c222da741ffffff1f713602182000200420127e2008200e7e7c200c20197e7c2010202a7e7c2013201f7e7c2017200541136cad222e7e7c201a20217e7c201d20237e7c202020287e7c2022201b41136cad222f7e7c202920287e202420237e7c2026202f7e7c2027201e41136cad22307e7c2013200e7e7c202120257e7c201a20127e7c201d20197e7c2020202a7e7c2022202e7e7c200420197e200820127e7c200c202a7e7c2010202e7e7c2017200d41136cad7e7c202120137e7c201a20237e7c201d20287e7c2020202f7e7c202220307e7c2230421a887c22314219887c2232a741ffffff1f7136020820002024201c7e202520157e7c2029201f7e7c202720237e7c20132018ad222e7e7c202120267e7c201a200a7e7c201d20067e7c2020200e7e7c202220127e7c202d421a887c222da741ffffff0f7136021c2000202920237e2025201f7e7c202620287e7c2027202f7e7c201320067e7c202120247e7c201a200e7e7c201d20127e7c202020197e7c2022202a7e7c2032421a887c2223a741ffffff0f7136020c20002004200a7e2008202e7e7c200c20067e7c2010200e7e7c20132002ad22197e7c201720127e7c201a20157e7c201d201c7e7c2020201f7e7c202220217e7c202d4219887c2212a741ffffff1f7136022020002023421988202b42ffffff1f837c2223421a88202c42ffffff0f837c3e021420002023a741ffffff1f713602102000202420157e202520197e7c2029201c7e7c2026201f7e7c20132011ad7e7c202120277e7c201a202e7e7c201d200a7e7c202020067e7c2022200e7e7c2012421a887c2213a741ffffff0f713602242000201342198842137e203042ffffff1f837c2213421a88203142ffffff0f837c3e020420002013a741ffffff1f713602000be20502017f067e2380808080004180016b2203248080808000200341306a200110a58180800020032003290360200329035820032903502204421a887c22054219887c2206a741ffffff1f7136022020032003290340200329033820032903302207421a887c22084219887c2209a741ffffff1f71360210200320032903682006421a887c2206a741ffffff0f71360224200320032903482009421a887c2209a741ffffff0f713602142003200642198820032903707c2206a741ffffff1f7136022820032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e021c20032004a741ffffff1f7136021820032006421a8820032903787c2204a741ffffff0f7136022c2003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e020c20032004a741ffffff1f71360208024020024102490d002002417f6a21020340200341306a200341086a10a58180800020032003290360200329035820032903502204421a887c22054219887c2206a741ffffff1f7136022020032003290340200329033820032903302207421a887c22084219887c2209a741ffffff1f71360210200320032903682006421a887c2206a741ffffff0f71360224200320032903482009421a887c2209a741ffffff0f713602142003200642198820032903707c2206a741ffffff1f7136022820032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e021c20032004a741ffffff1f7136021820032006421a8820032903787c2204a741ffffff0f7136022c2003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e020c20032004a741ffffff1f713602082002417f6a22020d000b0b20002003290208370200200041206a200341086a41206a290200370200200041186a200341086a41186a290200370200200041106a200341086a41106a290200370200200041086a200341086a41086a29020037020020034180016a2480808080000be103020e7f067e20022802242103200128022421042002280220210520012802202106200228020c2107200128020c2108200228021c2109200128021c210a2002280208210b2001280208210c2002280204210d2001280204210e2002280200210f200128020021102000200128021020022802106b41f0ffffff036aad2211421a88200128021420022802146b41f0ffffff016aad7c2212421988200128021820022802186b41f0ffffff036aad7c2213a741ffffff1f7136021820002010200f6b41d0fdffff036aad2214421a88200e200d6b41f0ffffff016aad7c2215421988200c200b6b41f0ffffff036aad7c2216a741ffffff1f7136020820002013421a88200a20096b41f0ffffff016aad7c2213a741ffffff0f7136021c20002016421a88200820076b41f0ffffff016aad7c2216a741ffffff0f7136020c20002013421988200620056b41f0ffffff036aad7c2213a741ffffff1f7136022020002016421988201142ffffff1f837c2211421a88201242ffffff0f837c3e021420002011a741ffffff1f7136021020002013421a88200420036b41f0ffffff016aad7c2211a741ffffff0f713602242000201142198842137e201442ffffff1f837c2211421a88201542ffffff0f837c3e020420002011a741ffffff1f713602000beb0201067e200041f0ffffff0320012802106bad2202421a8841f0ffffff0120012802146bad7c220342198841f0ffffff0320012802186bad7c2204a741ffffff1f71360218200041d0fdffff0320012802006bad2205421a8841f0ffffff0120012802046bad7c220642198841f0ffffff0320012802086bad7c2207a741ffffff1f7136020820002004421a8841f0ffffff01200128021c6bad7c2204a741ffffff0f7136021c20002007421a8841f0ffffff01200128020c6bad7c2207a741ffffff0f7136020c2000200442198841f0ffffff0320012802206bad7c2204a741ffffff1f7136022020002007421988200242ffffff1f837c2202421a88200342ffffff0f837c3e021420002002a741ffffff1f7136021020002004421a8841f0ffffff0120012802246bad7c2202a741ffffff0f713602242000200242198842137e200542ffffff1f837c2202421a88200642ffffff0f837c3e020420002002a741ffffff1f713602000be00302187e017f20013100052102200131000421032001310015210420013100142105200131000821062001310007210720013100062108200131000b2109200131000a210a2001310009210b200131000f210c200131000e210d200131000d210e200131000c210f200131001821102001310017211120013100162112200131001b2113200131001a211420013100192115200131001f2116200131001e2117200131001d2118200131001c21192001280000211a20002001280010220141ffffff0f713602142000201a41ffffff1f7136020020002016421286428080f00f8320184202862019420688842017420a8684843e022420002019421486428080c01f8320144204862015420488842013420c8684843e022020002015421586428080800f8320114205862012420388842010420d8684843e021c2000200e420286200f42068884200d420a8684200c421286843e02102000200f421386428080e00f83200a420386200b420588842009420b8684843e020c2000200b421586428080801f8320074205862008420388842006420d8684843e020820002012421786428080801c8320054207862001411976ad842004420f8684843e021820002008421686428080800e832003420686201a411a76ad842002420e8684843e02040bfe0c0a017f027e017f017e017f017e017f027e017f157e23808080800041f0006b2202248080808000200241046a200110ae8180800020022002350214220342ecf3b78a037e20022802082201ad220442e7e2e4b3017e20022802042205ad220642eecaf5ff017e7c200228020c2207ad2208428c93f0fb007e7c20022802102209ad220a4283e685d3017e7c200342edf3b78a017e7c220b7d2002280218220c20056aad220d42eecaf5ff017e7c200228021c220520016aad220e42e6e2a4b4017e7c2002280220220120076aad220f428b93f0fb027e7c20022802242207ad221042ffffffff017e22112001ad221242ffffffff017e22137c22142005ad221542ffff3f7e7c22167d200720096aad22174282e685d3037e7c200642ff037e42ffffffff0183221842d2b1cc047e200442edf3b78a017e20064283e685d3017e7c22197c201842eda7d7e7017e200642edf3b78a017e221a7c421d887c221b429bfcd192017e42ffffffff0183221c4214867c200842e7e2e4b3017e200442eecaf5ff017e7c200a428c93f0fb007e7c20034283e685d3017e7c200cad221d42ffffffff017e221e7d221f201a7d200d42ecf3b78a037e7c201c42cd027e7c20044283e685d3017e2006428c93f0fb007e7c200842edf3b78a017e7c222020184296eb9cef017e7c201c42d2b1cc047e7c201c42eda7d7e7017e201b7c421d887c221b429bfcd192017e42ffffffff0183221a42c5faceef017e7c2004428c93f0fb007e200642e7e2e4b3017e7c20084283e685d3017e7c200a42edf3b78a017e7c2221201842c5faceef017e7c201c4296eb9cef017e7c201a42d2b1cc047e7c201a42eda7d7e7017e201b7c421d887c2204429bfcd192017e42ffffffff018322064296eb9cef017e7c200b201842cd027e7c201c42c5faceef017e7c201a4296eb9cef017e7c200642d2b1cc047e7c200642eda7d7e7017e20047c421d887c2204429bfcd192017e42ffffffff0183221c42d2b1cc047e7c201c42eda7d7e7017e20047c421d887c220b429bfcd192017e42ffffffff0183220442cd027e7c200d4282e685d3037e20197d200e42ecf3b78a037e7c200a42e7e2e4b3017e200842eecaf5ff017e7c2003428c93f0fb007e7c201542ffffffff017e2215201e7c22197d221b7c201a42cd027e7c200642c5faceef017e7c201c4296eb9cef017e7c200442d2b1cc047e7c200442eda7d7e7017e200b7c421d887c220b429bfcd192017e42ffffffff0183220842c5faceef017e7c200d428b93f0fb027e20207d200e4282e685d3037e7c200f42ecf3b78a037e7c200342e7e2e4b3017e200a42eecaf5ff017e7c201320197c7d22137c200642cd027e7c201c42c5faceef017e7c20044296eb9cef017e7c200842d2b1cc047e7c200842eda7d7e7017e200b7c421d887c220b429bfcd192017e42ffffffff0183220a4296eb9cef017e7c201842148620217d200342eecaf5ff017e7c200d42e6e2a4b4017e7c200e428b93f0fb027e7c200f4282e685d3037e7c2014201d42ffff3f7e7c20157c220d7d201742ecf3b78a037e7c201c42cd027e7c200442c5faceef017e7c20084296eb9cef017e7c200a42d2b1cc047e7c200a42eda7d7e7017e200b7c421d887c220b429bfcd192017e42ffffffff0183221842d2b1cc047e7c201842eda7d7e7017e200b7c421d887c220ba741ffffffff017136024c200220034282e685d3037e201f7d200e42eecaf5ff017e7c200f42e6e2a4b4017e7c2011201242ffff3f7e7c220e7d2017428b93f0fb027e7c201a4214867c200842cd027e7c200a42c5faceef017e7c20184296eb9cef017e7c200b421d887c221aa741ffffffff017136025020022003428b93f0fb027e201b201042ffff3f7e220b7c7d200f42eecaf5ff017e7c201742e6e2a4b4017e7c20064214867c200a42cd027e7c201842c5faceef017e7c201a421d887c2206a741ffffffff01713602542002200342e6e2a4b4017e20137d201742eecaf5ff017e7c201c4214867c201842cd027e7c2006421d887c2203a741ffffffff017136025820022004421486200d7c2003421d887c2203a741ffffffff017136025c2002200842148620167c2003421d887c2203a741ffffffff01713602602002200a421486200e7c2003421d887c2203a741ffffffff017136026420022018421486200b7c2003421d887c2203421d883e026c20022003a741ffffffff0171360268200241286a200241cc006a41fcd2c0800010ad818080002000200241286a10ac81808000200241f0006a2480808080000b950301077f2000200128022022023a001d2000200128020022033a0000200020024110763a001f200020024108763a001e2000200128021c22044115763a001c20002004410d763a001b200020044105763a001a2000200128021822024112763a001820002002410a763a0017200020024102763a0016200020012802142205410f763a0014200020054107763a00132000200128021022064114763a001120002006410c763a0010200020064104763a000f2000200128020c22074111763a000d200020074109763a000c200020074101763a000b200020012802082208410e763a0009200020084106763a00082000200128020422014113763a000620002001410b763a0005200020014103763a0004200020034110763a0002200020034108763a0001200020044103742002411a76723a0019200020024106742005411776723a0015200020054101742006411c76723a0012200020064104742007411976723a000e200020074107742008411676723a000a200020084102742001411b76723a0007200020014105742003411876723a00030bcd04010e7f23808080800041106b2203200128022020022802206b200128021c200228021c6b200128021820022802186b200128021420022802146b200128021020022802106b200128020c200228020c6b200128020820022802086b200128020020022802006b2204411f7520012802046a20022802046b2201411f756a2205411f756a2206411f756a2207411f756a2208411f756a2209411f756a220a411f756a220b411f75220236020c200328020c210c2003200236020c200328020c210d2003200236020c200328020c210e2003200236020c200328020c210f2003200236020c200328020c21102003200236020c200328020c1a2003200236020c200328020c1a2003200236020c200328020c1a2003200236020c200328020c21032000200c41eda7d7e70171200441ffffffff01716a2202411d76200141ffffffff01716a200d41d2b1cc04716a2201411d76200541ffffffff01716a200e4196eb9cef01716a2204411d76200641ffffffff01716a200f41c5faceef01716a2205411d76200741ffffffff01716a201041cd02716a2206411d76200841ffffffff01716a2207411d76200941ffffffff01716a2208411d76200a41ffffffff01716a220941ffffffff017136021c2000200841ffffffff01713602182000200741ffffffff01713602142000200641ffffffff01713602102000200541ffffffff017136020c2000200441ffffffff01713602082000200141ffffffff01713602042000200241ffffffff017136020020002009411d76200b6a2003418080c000716a41ffffffff01713602200bd50301187f20012f0004210220012d0006210320012d0018210420012d0016210520012d0017210620012f0008210720012d0007210820012f000c210920012d000b210a20012d000a210b20012f0010210c20012d000f210d20012d000e210e20012d0014210f20012d0015211020012d0013211120012d0012211220012d001c211320012d0019211420012d001a211520012d001b211620012f0000211720012d0002211820012d00032119200020012f001d20012d001f41107472360220200020172018411074722019411874220141808080f80171723602002000201341157420154110742016411874722014410874221372410b767236021c2000200f2010410874221072410f7420114118742012411074220f724111767241ffffffff01713602142000200c200f72410c74200d411874200e411074220c724114767241ffffffff017136021020002009200c72410974200a411874200b4110742209724117767241ffffffff017136020c2000200720097241067420084118742207411a767241ffffffff0171360208200020132004724112742005411074200641187472201072410e767241ffffffff0171360218200020022003411074722007724103742001411d767241ffffffff01713602040bcf08011b7f23808080800041a0026b22032480808080002001412c6a2802002104200141306a2802002105200141346a2802002106200141386a28020021072001413c6a2802002108200141c0006a2802002109200141c4006a280200210a200141c8006a280200210b200141cc006a280200210c2001280204210d2001280208210e200128020c210f200128021021102001280214211120012802182112200128021c211320012802202114200128022421152003200128020020012802286a36020820032015200c6a36022c20032014200b6a36022820032013200a6a3602242003201220096a3602202003201120086a36021c2003201020076a3602182003200f20066a3602142003200e20056a3602102003200d20046a36020c200341306a200141286a200110a881808000200341d8006a200341086a200210a68180800020034180016a200341306a200241286a10a681808000200341a8016a200141f8006a200241f8006a10a681808000200341d0016a200141d0006a200241d0006a10a681808000200320032802d00141017422013602f801200320032802d40141017422023602fc01200320032802d801410174220436028002200320032802dc01410174220536028402200320032802e001410174220636028802200320032802e401410174220736028c02200320032802e801410174220836029002200320032802ec01410174220936029402200320032802f001410174220a36029802200320032802f401410174220b36029c022000200341d8006a20034180016a10a881808000200328028001210c2003280258210d200328028401210e200328025c210f200328028801211020032802602111200328028c012112200328026421132003280290012114200328026821152003280294012116200328026c2117200328029801211820032802702119200328029c01211a2003280274211b20032802a001211c2003280278211d200041cc006a20032802a401200328027c6a360200200041c8006a201c201d6a360200200041c4006a201a201b6a360200200041c0006a201820196a3602002000413c6a201620176a360200200041386a201420156a360200200041346a201220136a360200200041306a201020116a3602002000412c6a200e200f6a3602002000200c200d6a36022820032802a801210c20032802ac01210d20032802b001210e20032802b401210f20032802b801211020032802bc01211120032802c001211220032802c401211320032802c8012114200041f4006a20032802cc01200b6a360200200041f0006a2014200a6a360200200041ec006a201320096a360200200041e8006a201220086a360200200041e4006a201120076a360200200041e0006a201020066a360200200041dc006a200f20056a360200200041d8006a200e20046a360200200041d4006a200d20026a3602002000200c20016a360250200041f8006a200341f8016a200341a8016a10a881808000200341a0026a2480808080000ba40401137f23808080800041c0026b22032480808080002002412c6a2802002104200241306a2802002105200241346a2802002106200241386a28020021072002413c6a2802002108200241c0006a2802002109200241c4006a280200210a200241c8006a280200210b2002280200210c2002280228210d2002280204210e2002280208210f200228020c2110200228021021112002280214211220022802182113200228021c21142002280220211520032002280224200241cc006a2802006a3602c40120032015200b6a3602c00120032014200a6a3602bc012003201320096a3602b8012003201220086a3602b4012003201120076a3602b0012003201020066a3602ac012003200f20056a3602a8012003200e20046a3602a4012003200c200d6a3602a001200341a0016a41286a200241286a200210a881808000200341a0016a41f0006a200241f0006a290200370200200341a0016a41e8006a200241e8006a290200370200200341a0016a41e0006a200241e0006a290200370200200341a0016a41d8006a200241d8006a290200370200200320022902503702f001200341a0016a41f8006a200241f8006a41a0d3c0800010a68180800020032001200341a0016a10af8180800020002003200341f8006a220210a681808000200041286a200341286a2201200341d0006a220410a681808000200041d0006a2004200210a681808000200041f8006a2003200110a681808000200341c0026a2480808080000bae0201047f23808080800041f0026b2202248080808000200241cc016a200110b681808000200241086a200241d8016a290200370300200241106a200241e0016a290200370300200241186a200241e8016a290200370300200241206a200241f0016a290200370300200220022902d00137030041002101024020022d00cd0120022d00cc01417f7341017110ad8d8080007210ad8d80800041ff01710d00200241cc016a200210b78180800020022d00ce01210320022d00cd01210420022d00cc012105200241286a200241cc016a41046a41a00110848e8080001a200320042005417f7341017110ad8d8080007210ad8d8080007210ad8d80800041ff01710d00200041046a200241286a41a00110848e8080001a410121010b20002001360200200241f0026a2480808080000bb1160a067f017e017f017e017f017e017f027e0d7f017e23808080800041e0046b2202248080808000200241086a41206a2203200141206a290200370300200241086a41186a2204200141186a290200370300200241086a41106a2205200141106a290200370300200241086a41086a2206200141086a29020037030020022001290200370308200241306a41206a2207200141c8006a2902002208370300200241306a41186a2209200141c0006a290200220a370300200241306a41106a220b200141386a290200220c370300200241306a41086a220d200141306a290200220e37030020022001290228220f370330200141d4006a2802002110200141d8006a2802002111200141dc006a2802002112200141e0006a2802002113200141e4006a2802002114200141e8006a2802002115200141ec006a2802002116200141f0006a28020021172001280250211820022802342119200228023c211a2002280244211b200228024c211c20022002280254200141f4006a2802006a36028c04200220172008a76a360288042002201c20166a3602840420022015200aa76a360280042002201b20146a3602fc0320022013200ca76a3602f8032002201a20126a3602f40320022011200ea76a3602f0032002201920106a3602ec0320022018200fa76a3602e80320024190046a200141d0006a2214200241306a10a881808000200241d8006a200241e8036a20024190046a10a68180800020024180016a200241086a200241306a10a68180800020024190046a20024180016a10a581808000200220022903c00420022903b80420022903b0042208421a887c220a4219887c220ca741ffffff1f7136028004200220022903a004200229039804200229039004220e421a887c220f4219887c221da741ffffff1f713602f003200220022903c804200c421a887c220ca741ffffff0f7136028404200220022903a804201d421a887c221da741ffffff0f713602f4032002200c42198820022903d0047c220ca741ffffff1f71360288042002201d421988200842ffffff1f837c2208421a88200a42ffffff0f837c3e02fc0320022008a741ffffff1f713602f8032002200c421a8820022903d8047c2208a741ffffff0f7136028c042002200842198842137e200e42ffffff1f837c2208421a88200f42ffffff0f837c3e02ec0320022008a741ffffff1f713602e803200241c0036a200241d8006a200241e8036a10a68180800020024190046a41acd2c08000200241c0036a10a481808000200241a8016a41206a200241b4046a290200370300200241a8016a41186a200241ac046a290200370300200241a8016a41106a200241a4046a290200370300200241a8016a41086a2002419c046a29020037030020022002290294043703a801200241d0016a200241a8016a200241d8006a10a681808000200241f8016a200241a8016a20024180016a10a68180800020024190046a200241f8016a200141f8006a220110a681808000200241a0026a200241d0016a20024190046a10a681808000200241c8026a41206a2210200241f8016a41206a290200370300200241c8026a41186a2211200241f8016a41186a290200370300200241c8026a41106a2212200241f8016a41106a290200370300200241c8026a41086a2213200241f8016a41086a290200370300200220022902f8013703c802200241f0026a200241086a4198d4c0800010a68180800020024198036a200241306a4198d4c0800010a681808000200241c0036a200241d0016a41c0d4c0800010a68180800020024190046a2001200241a0026a10a681808000200241e8036a20024190046a10a38180800020022d00e80341017110ad8d8080002101200620022802a00320062802002215734100200141ff01716b220171201573360200200520022802a8032005280200220673200171200673360200200220022802980320022802082205732001712005733602082002200228029c03200228020c22057320017120057336020c200220022802a4032002280214220573200171200573360214200220022802ac03200228021c22057320017120057336021c200420022802b0032004280200220573200171200573360200200220022802b4032002280224220473200171200473360224200320022802b8032003280200220473200171200473360200200220022802bc03200228022c22037320017120037336022c200220022802f0022002280230220373200171200373360230200220022802f4022002280234220373200171200373360234200d20022802f802200d280200220373200171200373360200200220022802fc02200228023c22037320017120037336023c200b200228028003200b28020022037320017120037336020020022002280284032002280244220373200171200373360244200920022802880320092802002203732001712003733602002002200228028c03200228024c22037320017120037336024c2007200228029003200728020022037320017120037336020020022002280294032002280254220373200171200373360254200220022802c00320022802c8022203732001712003733602c802200220022802c40320022802cc022203732001712003733602cc02201320022802c8032013280200220373200171200373360200200220022802cc0320022802d4022203732001712003733602d402201220022802d0032012280200220373200171200373360200200220022802d40320022802dc022203732001712003733602dc02201120022802d8032011280200220373200171200373360200200220022802dc0320022802e4022203732001712003733602e402201020022802e0032010280200220373200171200373360200200220022802e40320022802ec022203732001712003733602ec02200241e8036a200241086a200241a0026a10a68180800020024190046a200241e8036a10a38180800020022d00900441017110ad8d808000210120024190046a200241306a10a981808000200d200228029804200d2802002203734100200141ff01716b220171200373360200200b20022802a004200b280200220d73200171200d73360200200920022802a8042009280200220b73200171200b7336020020022002280290042002280230220973200171200973360230200220022802940420022802342209732001712009733602342002200228029c04200228023c22097320017120097336023c200220022802a4042002280244220973200171200973360244200220022802ac04200228024c22097320017120097336024c200720022802b0042007280200220973200171200973360200200220022802b404200228025422077320017120077336025420024190046a2014200241306a10a881808000200241e8036a200241c8026a20024190046a10a68180800020024190046a200241e8036a10a38180800020022d00900441017110ad8d808000210120024190046a200241e8036a10a981808000200220022802900420022802e8032207734100200141ff01716b2201712007733602e803200220022802940420022802ec032207732001712007733602ec03200220022802980420022802f0032207732001712007733602f0032002200228029c0420022802f4032207732001712007733602f403200220022802a00420022802f8032207732001712007733602f803200220022802a40420022802fc032207732001712007733602fc03200220022802a80420022802800422077320017120077336028004200220022802ac0420022802840422077320017120077336028404200220022802b00420022802880422077320017120077336028804200220022802b404200228028c0422077320017120077336028c042000200241e8036a10a381808000200241e0046a2480808080000bee1803097f067e0e7f2380808080004180066b2202248080808000200241086a41206a22034100290290d4c08000370300200241086a41186a22044100290288d4c08000370300200241086a41106a22054100290280d4c08000370300200241086a41086a220641002902f8d3c08000370300200241002902f0d3c08000370308200241306a41206a220741002902e8d3c08000370300200241306a41186a220841002902e0d3c08000370300200241306a41106a220941002902d8d3c08000370300200241306a41086a220a41002902d0d3c08000370300200241002902c8d3c0800037033020024190046a200110a581808000200220022903c00420022903b80420022903b004220b421a887c220c4219887c220da741ffffff1f7136028803200220022903a004200229039804200229039004220e421a887c220f4219887c2210a741ffffff1f713602f802200220022903c804200d421a887c220da741ffffff0f7136028c03200220022903a8042010421a887c2210a741ffffff0f713602fc022002200d42198820022903d0047c220da741ffffff1f713602900320022010421988200b42ffffff1f837c220b421a88200c42ffffff0f837c3e0284032002200ba741ffffff1f71360280032002200d421a8820022903d8047c220ba741ffffff0f71360294032002200b42198842137e200e42ffffff1f837c220b421a88200f42ffffff0f837c3e02f4022002200ba741ffffff1f713602f002200241d8006a4198d4c08000200241f0026a10a681808000200a280200210a2009280200210920082802002108200728020021072002280258211120022802302112200228025c2113200228023421142002280260211520022802642116200228023c211720022802682118200228026c21192002280244211a2002280270211b2002280274211c200228024c211d2002280278211e20022002280254200228027c6a3602b40420022007201e6a3602b0042002201d201c6a3602ac0420022008201b6a3602a8042002201a20196a3602a4042002200920186a3602a0042002201720166a36029c042002200a20156a360298042002201420136a360294042002201220116a3602900420024180016a20024190046a41e8d4c0800010a681808000200241f0026a4190d5c08000200241d8006a10a681808000200241d8056a200241086a200241f0026a10a8818080002002200228027c41b39ba00a6a3602b4042002200228027841ffed8a176a3602b004200220022802744198e3b90e6a3602ac042002200228027041bbf9801d6a3602a8042002200228026c4198d1e70b6a3602a4042002200228026841a980076a3602a00420022002280264418e94a8036a36029c042002200228026041bdddd5186a360298042002200228025c4184e5cd066a360294042002200228025841a3f1e51a6a36029004200241a8016a200241d8056a20024190046a10a68180800020024190046a20024180016a200241a8016a10a48180800020022d0090042111200241d0016a41206a2207200241b4046a290200370300200241d0016a41186a2208200241ac046a290200370300200241d0016a41106a2209200241a4046a290200370300200241d0016a41086a220a2002419c046a29020037030020022002290294043703d001200241f8016a200241d0016a200110a68180800020024190046a200241f8016a10a38180800020022d00900441017110ad8d808000417f7341017110ad8d808000210120024190046a200241f8016a10a981808000200220022802900420022802f8012212734100200141ff01716b2201712012733602f801200220022802940420022802fc012212732001712012733602fc012002200228029804200228028002221273200171201273360280022002200228029c0420022802840222127320017120127336028402200220022802a00420022802880222127320017120127336028802200220022802a404200228028c0222127320017120127336028c02200220022802a80420022802900222127320017120127336029002200220022802ac0420022802940222127320017120127336029402200220022802b00420022802980222127320017120127336029802200220022802b404200228029c0222127320017120127336029c022011417f73410171221110ad8d8080002101200a200228028002200a2802002212734100200141ff01716b2201712012733602002009200228028802200928020022127320017120127336020020082002280290022008280200221273200171201273360200200220022802f80120022802d0012212732001712012733602d001200220022802fc0120022802d4012212732001712012733602d401200220022802840220022802dc012212732001712012733602dc012002200228028c0220022802e4012212732001712012733602e401200220022802940220022802ec012212732001712012733602ec01200720022802980220072802002212732001712012733602002002200228029c0220022802f4012212732001712012733602f401201110ad8d80800021012006200228026020062802002211734100200141ff01716b2201712011733602002005200228026820052802002206732001712006733602002004200228027020042802002205732001712005733602002002200228025820022802082204732001712004733602082002200228025c200228020c22047320017120047336020c2002200228026420022802142204732001712004733602142002200228026c200228021c22047320017120047336021c2002200228027420022802242204732001712004733602242003200228027820032802002204732001712004733602002002200228027c200228022c22037320017120037336022c20024190046a200241d8006a200241306a10a881808000200241f0026a200241086a20024190046a10a681808000200241d8056a200241f0026a41b8d5c0800010a681808000200241a0026a200241d8056a200241a8016a10a88180800020024190046a200241d0016a10a581808000200220022903c00420022903b80420022903b004220b421a887c220c4219887c220da741ffffff1f713602e002200220022903a004200229039804200229039004220e421a887c220f4219887c2210a741ffffff1f713602d002200220022903c804200d421a887c220da741ffffff0f713602e402200220022903a8042010421a887c2210a741ffffff0f713602d4022002200d42198820022903d0047c220da741ffffff1f713602e80220022010421988200b42ffffff1f837c220b421a88200c42ffffff0f837c3e02dc022002200ba741ffffff1f713602d8022002200d421a8820022903d8047c220ba741ffffff0f713602ec022002200b42198842137e200e42ffffff1f837c220b421a88200f42ffffff0f837c3e02cc022002200ba741ffffff1f713602c802200220022802f4014101743602fc05200220072802004101743602f805200220022802ec014101743602f405200220082802004101743602f005200220022802e4014101743602ec05200220092802004101743602e805200220022802dc014101743602e4052002200a2802004101743602e005200220022802d4014101743602dc05200220022802d0014101743602d805200241b0056a200241d8056a200241a8016a10a681808000200241f0026a200241a0026a41e0d5c0800010a68180800020024190046a41286a220141c8d3c08000200241c8026a10a881808000200241ac056a20022802ec02360200200241a4056a20022902e4023702002002419c056a20022902dc0237020020024194056a20022902d4023702002002418c056a20022902cc0237020020024190046a41086a200241b0056a41086a29020037030020024190046a41106a200241b0056a41106a29020037030020024190046a41186a200241b0056a41186a29020037030020024190046a41206a200241b0056a41206a290200370300200220022902b00537039004200220022802c80241016a3602880520024180056a200241f0026a41206a290200370300200241f8046a200241f0026a41186a290200370300200241f0046a200241f0026a41106a290200370300200241e8046a200241f0026a41086a290200370300200220022902f0023703e004200241f0026a20024190046a20024190046a41f8006a220710a681808000200241f0026a41286a200120024190046a41d0006a220810a681808000200241f0026a41d0006a2008200710a681808000200241f0026a41f8006a20024190046a200110a6818080002000200241f0026a41a00110848e8080001a20024180066a2480808080000bf90101017f23808080800041d0036b2202248080808000200241186a200141186a290000370300200241106a200141106a290000370300200241086a200141086a29000037030020022001290000370300200241206a200210aa81808000200241c8006a200241206a10b381808000200241e8016a41186a200141386a290000370300200241e8016a41106a200141306a290000370300200241e8016a41086a200141286a290000370300200220012900203703e80120024188026a200241e8016a10aa81808000200241b0026a20024188026a10b3818080002000200241c8006a200241b0026a10b081808000200241d0036a2480808080000bbf0201037f23808080800041e0016b220224808080800020022000200141286a220310a681808000200241286a200041286a2204200110a681808000200241d0006a2000200110a681808000200241f8006a2004200310a681808000200241a0016a200210a381808000200241c0016a200241286a10a38180800041002101410121000340200241a0016a20016a2d0000200241c0016a20016a2d00004610ad8d8080002000712100200141016a22014120470d000b200010ad8d8080002103200241a0016a200241d0006a10a381808000200241c0016a200241f8006a10a38180800041002101410121000340200241a0016a20016a2d0000200241c0016a20016a2d00004610ad8d8080002000712100200141016a22014120470d000b200010ad8d80800020037210ad8d8080002101200241e0016a24808080800020010bf80101037f23808080800041f0006b2202248080808000200241086a200110aa81808000200241306a200241086a10a38180800041002103410121040340200241306a20036a2d0000200120036a2d00004610ad8d8080002004712104200341016a22034120470d000b200410ad8d8080002103200241d0006a200241086a10a38180800020022d005041017110ad8d8080002104200041246a200241286a2902003702002000411c6a200241206a290200370200200041146a200241186a2902003702002000410c6a200241106a29020037020020002002290208370204200020043a0001200020033a0000200241f0006a2480808080000bd51503067f067e0e7f23808080800041a0066b220224808080800041002103200241206a220441002902a8d6c08000370300200241186a220541002902a0d6c08000370300200241106a22064100290298d6c08000370300200241086a22074100290290d6c0800037030020024100290288d6c0800037030020024180056a200110a581808000200220022903b00520022903a80520022903a0052208421a887c22094219887c220aa741ffffff1f713602402002200229039005200229038805200229038005220b421a887c220c4219887c220da741ffffff1f71360230200220022903b805200a421a887c220aa741ffffff0f713602442002200229039805200d421a887c220da741ffffff0f713602342002200a42198820022903c0057c220aa741ffffff1f713602482002200d421988200842ffffff1f837c2208421a88200942ffffff0f837c3e023c20022008a741ffffff1f713602382002200a421a8820022903c8057c2208a741ffffff0f7136024c2002200842198842137e200b42ffffff1f837c2208421a88200c42ffffff0f837c3e022c20022008a741ffffff1f71360228200241d0006a2002200241286a10a881808000200728020021072006280200210620052802002105200428020021042002280200210e2002280228210f20022802042110200228022c211120022802302112200228020c2113200228023421142002280238211520022802142116200228023c211720022802402118200228021c21192002280244211a2002280248211b2002200228024c20022802246a36029c012002201b20046a360298012002201a20196a360294012002201820056a360290012002201720166a36028c012002201520066a360288012002201420136a360284012002201220076a360280012002201120106a36027c2002200f200e6a36027820024180056a200241f8006a10a581808000200220022903b00520022903a80520022903a0052208421a887c22094219887c220aa741ffffff1f713602b8012002200229039005200229038805200229038005220b421a887c220c4219887c220da741ffffff1f713602a801200220022903b805200a421a887c220aa741ffffff0f713602bc012002200229039805200d421a887c220da741ffffff0f713602ac012002200a42198820022903c0057c220aa741ffffff1f713602c0012002200d421988200842ffffff1f837c2208421a88200942ffffff0f837c3e02b40120022008a741ffffff1f713602b0012002200a421a8820022903c8057c2208a741ffffff0f713602c4012002200842198842137e200b42ffffff1f837c2208421a88200c42ffffff0f837c3e02a40120022008a741ffffff1f713602a001200241b8036a41b0d6c0800010a98180800020024180056a200241d0006a10a581808000200220022903b00520022903a80520022903a0052208421a887c22094219887c220aa741ffffff1f713602f8032002200229039005200229038805200229038005220b421a887c220c4219887c220da741ffffff1f713602e803200220022903b805200a421a887c220aa741ffffff0f713602fc032002200229039805200d421a887c220da741ffffff0f713602ec032002200a42198820022903c0057c220aa741ffffff1f71360280042002200d421988200842ffffff1f837c2208421a88200942ffffff0f837c3e02f40320022008a741ffffff1f713602f0032002200a421a8820022903c8057c2208a741ffffff0f71360284042002200842198842137e200b42ffffff1f837c2208421a88200c42ffffff0f837c3e02e40320022008a741ffffff1f713602e00320024180056a200241b8036a200241e0036a10a681808000200241c8016a20024180056a200241a0016a10a881808000200241e0036a200241c8016a200241a0016a10a68180800020024180056a41acd2c08000200241e0036a10a48180800020022d0080052104200241f0016a41206a200241a4056a290200370300200241f0016a41186a2002419c056a290200370300200241f0016a41106a20024194056a290200370300200241f0016a41086a2002418c056a29020037030020022002290284053703f00120024198026a200241f0016a200241f8006a10a68180800020024180056a20024198026a200241c8016a10a681808000200241c0026a200241f0016a20024180056a10a681808000200220012802244101743602a405200220012802204101743602a0052002200128021c41017436029c052002200128021841017436029805200220012802144101743602940520022001280210410174360290052002200128020c41017436028c05200220012802084101743602880520022001280204410174360284052002200128020041017436028005200241e8026a20024180056a20024198026a10a68180800020024180056a200241e8026a10a38180800020022d00800541017110ad8d808000210120024180056a200241e8026a10a981808000200220022802800520022802e8022205734100200141ff01716b2201712005733602e802200220022802840520022802ec022205732001712005733602ec02200220022802880520022802f0022205732001712005733602f0022002200228028c0520022802f4022205732001712005733602f402200220022802900520022802f8022205732001712005733602f802200220022802940520022802fc022205732001712005733602fc022002200228029805200228028003220573200171200573360280032002200228029c0520022802840322057320017120057336028403200220022802a00520022802880322057320017120057336028803200220022802a405200228028c0322057320017120057336028c0320024190036a200241d0006a200241c0026a10a681808000200241b8036a200241e8026a20024190036a10a68180800020024180056a200241b8036a10a38180800020022d00800541017110ad8d8080002105200241e0036a41186a4200370300200241e0036a41106a4200370300200241e0036a41086a4200370300200242003703e00320024180056a20024190036a10a38180800041012101034020024180056a20036a2d0000200241e0036a20036a2d00004610ad8d8080002001712101200341016a22034120470d000b200110ad8d808000210320024180056a41206a200241e8026a41206a29020037030020024180056a41186a200241e8026a41186a29020037030020024180056a41106a200241e8026a41106a29020037030020024180056a41086a200241e8026a41086a290200370300200241b0056a20024190036a41086a290200370300200241b8056a20024190036a41106a290200370300200241c0056a20024190036a41186a290200370300200241c8056a20024190036a41206a290200370300200220022902e8023703800520022002290290033703a805200241f0056a200241206a290300370300200241e8056a200241186a290300370300200241e0056a200241106a290300370300200241d8056a200241086a29030037030020024198066a200241b8036a41206a29020037030020024190066a200241b8036a41186a29020037030020024188066a200241b8036a41106a29020037030020024180066a200241b8036a41086a290200370300200220022903003703d005200220022902b8033703f805200241e0036a20024180056a41a00110848e8080001a200020033a0002200020053a0001200020043a0000200041046a200241e0036a41a00110848e8080001a200241a0066a2480808080000bde0101047f23808080800041106b220324808080800002402001450d00200020014104746a21040340200028020021052003200028020422013602082003200341086a36020c2003410c6a200210cb8180800002402002280200200228020822066b20014f0d0020022006200110b182808000200228020821060b200228020420066a2005200110848e8080001a2002200620016a3602082003200041086a36020c2003410c6a200210cb8180800020032000410c6a36020c2003410c6a200210cb81808000200041106a22002004470d000b0b200341106a2480808080000b9506010e7f20002802042101024020002802082202450d0041002103034002402001200341386c6a2204280200450d00200428020441002802c0a3c68000118080808000000b0240200428020c450d00200441106a28020041002802c0a3c68000118080808000000b0240024002400240024020042d00240e050001040402040b2004412c6a21050240200441306a2802002206450d00200528020021072006410171210841002109024020064101460d002006417e71210a4100210920072106034002402006280200450d00200641046a28020041002802c0a3c68000118080808000000b0240200641206a280200450d00200641246a28020041002802c0a3c68000118080808000000b200641c0006a2106200a200941026a2209470d000b0b2008450d00200720094105746a2206280200450d00200628020441002802c0a3c68000118080808000000b20042802280d020c030b2004412c6a21050240200441306a280200220b450d002005280200210c4100210703400240200c200741246c6a22082802082206450d002008280204210d2006410171210e41002109024020064101460d002006417e71210a41002109200d2106034002402006280200450d00200641046a28020041002802c0a3c68000118080808000000b0240200641206a280200450d00200641246a28020041002802c0a3c68000118080808000000b200641c0006a2106200a200941026a2209470d000b0b200e450d00200d20094105746a2206280200450d00200628020441002802c0a3c68000118080808000000b02402008280200450d00200828020441002802c0a3c68000118080808000000b0240200828020c450d002008410c6a28020441002802c0a3c68000118080808000000b200741016a2207200b470d000b0b20042802280d010c020b200441286a280200450d012004412c6a21050b200528020041002802c0a3c68000118080808000000b02402004280218450d002004411c6a28020041002802c0a3c68000118080808000000b200341016a22032002470d000b0b02402000280200450d00200141002802c0a3c68000118080808000000b0bb703010a7f23808080800041e0016b2202248080808000200128020c2203200128020422046b220541077621062001280208210720012802002108024002400240024020032004470d002002200336020c200220073602082002200836020041002101410421090c010b2005418099b3e67c4b0d01200641d0006c220a417f4c0d01200128021021054100210141002d00fca3c680001a200a41002802c8a3c68000118180808000002209450d022002200336020c20022007360208200220043602042002200836020020024184016a220a41086a210b200921070240034020042802702208418080808078460d01200241106a200441f00010848e8080001a200b200441fc006a280200360200200a2004290274370200200220083602800120024190016a200241106a200510e381808000200720024190016a41d00010848e80800041d0006a2107200141016a210120044180016a2208210420082003470d000b200321040c010b20044180016a21040b20022004360204200210ca81808000200020013602082000200936020420002006360200200241e0016a2480808080000f0b10ae80808000000b4104200a10b280808000000bc204020b7f027e23808080800041c0006b220224808080800020012802082103200128020c220421052001280200220621070240200128020422082004460d002001280210210920062107024003402008220a280200220b418080808078460d01200a28020c210c200a290218210d200a290210210e200a2802042108200a28020821052002200936023c20022008200541386c6a3602382002200b360234200220083602302002200836022c200241086a2002412c6a10bc8180800020072002290308370200200741106a200e370200200741186a200d3702002002200c41ffffffff0171360214200741086a200241086a41086a290300370200200741206a2107200a41206a22082004470d000b0b200a41206a21050b20014284808080c00037020020014280808080c000370208024020042005460d00200420056b41057621044100210c034002402005200c4105746a220b2802082208450d00200b28020441306a210a03400240200a41706a280200450d00200a41746a28020041002802c0a3c68000118080808000000b0240200a417c6a280200450d00200a28020041002802c0a3c68000118080808000000b200a41386a210a2008417f6a22080d000b0b0240200b280200450d00200b28020441002802c0a3c68000118080808000000b0240200b28020c450d00200b410c6a28020441002802c0a3c68000118080808000000b200c41016a220c2004470d000b0b200020063602042000200341ffffff3f713602002000200720066b410576360208200241c0006a2480808080000b8505020d7f017e23808080800041e0006b2202248080808000200128020c2203200128020422046b220541386e21062001280208210720012802002108024002400240024020032004470d00410021094104210a0c010b200541a8e3f1b87c4b0d01200641246c2209417f4c0d012001280210210b4100210c41002d00fca3c680001a200941002802c8a3c6800011818080800000220a450d02200541486a210d200a210541002109024003402004200c6a2201412c6a280200220e418080808078460d01200141306a290300210f200241286a200141286a280200360200200241206a200141206a290300370300200241186a200141186a290300370300200241106a200141106a290300370300200241086a200141086a2903003703002002200f3703302002200e36022c20022001290300370300200241386a2002200b10e281808000200541206a200241386a41206a280200360200200541186a200241386a41186a290300370200200541106a200241386a41106a290300370200200541086a200241386a41086a29030037020020052002290338370200200541246a2105200941016a21092004200c41386a220c6a2003470d000c020b0b200141386a2003460d00200141e8006a2101200d200c6b41386e210503400240200141706a280200450d00200141746a28020041002802c0a3c68000118080808000000b02402001417c6a280200450d00200128020041002802c0a3c68000118080808000000b200141386a21012005417f6a22050d000b0b02402007450d00200841002802c0a3c68000118080808000000b200020093602082000200a36020420002006360200200241e0006a2480808080000f0b10ae80808000000b4104200910b280808000000bac0201087f23808080800041306b2202248080808000200128020c2203200128020422046b220541e8006e2106200128021021072001280208210820012802002109024002400240024020032004470d00410421010c010b200541d0b6dbed7e4b0d01200641386c2205417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002201450d020b200241046a41086a220541003602002002200136020820022006360204200220073602202002200336021c2002200836021820022004360214200220093602102002200136022c2002410036022820022005360224200241106a200241246a10c881808000200041086a200528020036020020002002290204370200200241306a2480808080000f0b10ae80808000000b4104200510b280808000000bb703010a7f23808080800041d0016b2202248080808000200128020c2203200128020422046b220541f8006e21062001280208210720012802002108024002400240024020032004470d002002200336020c200220073602082002200836020041002101410421090c010b200541f8c2878f7e4b0d01200641c4006c220a417f4c0d01200128021021054100210141002d00fca3c680001a200a41002802c8a3c68000118180808000002209450d022002200336020c200220073602082002200436020420022008360200200241f8006a220a41086a210b200921070240034020042802642208418080808078460d01200241106a200441e40010848e8080001a200b200441f0006a290300370300200a20042903683703002002200836027420024188016a200241106a200510cf81808000200720024188016a41c40010848e80800041c4006a2107200141016a2101200441f8006a2208210420082003470d000b200321040c010b200441f8006a21040b20022004360204200210c981808000200020013602082000200936020420002006360200200241d0016a2480808080000f0b10ae80808000000b4104200a10b280808000000bab0201087f23808080800041306b2202248080808000200128020c2203200128020422046b220541386e2106200128021021072001280208210820012802002109024002400240024020032004470d00410421010c010b200541a8e3f1b87c4b0d01200641246c2205417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002201450d020b200241046a41086a220541003602002002200136020820022006360204200220073602202002200336021c2002200836021820022004360214200220093602102002200136022c2002410036022820022005360224200241106a200241246a10c581808000200041086a200528020036020020002002290204370200200241306a2480808080000f0b10ae80808000000b4104200510b280808000000b850703077f017e027f2380808080004190016b2202248080808000200241386a200110db81808000024002400240024002402002280258418080808078460d00200228023c210320022802382104200241e8006a41206a200241e0006a290300370300200241e8006a41186a2205200241386a41206a290300370300200241e8006a41106a200241386a41186a290300370300200241f0006a200241386a41106a290300370300200220022903403703682001280224200241e8006a10dd82808000210620052802002205418080808078470d010b2000410036020820004280808080c000370200200110df818080000c010b200128022041016a2207417f20071b22074104200741044b1b220741d5aad52a4b0d01200741186c2208417f4c0d0120024184016a290200210941002d00fca3c680001a200841002802c8a3c6800011818080800000220a450d02200a2006360214200a200937020c200a2005360208200a2003360204200a20043602002002410136020c2002200a36020820022007360204200241106a41206a200141206a290200370300200241106a41186a200141186a290200370300200241106a41106a200141106a290200370300200241106a41086a200141086a29020037030020022001290200370310200241386a200241106a10db8180800002402002280258418080808078460d00200241386a41086a2101412c2104410121030340200228023c210620022802382107200241e8006a41206a200141206a290300370300200241e8006a41186a2205200141186a290300370300200241e8006a41106a200141106a290300370300200241e8006a41086a200141086a290300370300200220012903003703682002280234200241e8006a10dd8280800021082005280200220b418080808078460d012002290284012109024020032002280204470d00200241046a2003200228023041016a2205417f20051b10c3818080002002280208210a0b200a20046a22052008360200200541786a2009370200200541746a200b360200200541706a20063602002005416c6a20073602002002200341016a220336020c200441186a2104200241386a200241106a10db818080002002280258418080808078470d000b0b200241106a10df8180800020002002290204370200200041086a200241046a41086a2802003602000b20024190016a2480808080000f0b10ae80808000000b4104200810b280808000000bd10c01077f2000200110d781808000024002400240024002400240024002400240200128020022004188808080786a410f2000418780808078481b41786a0e0701020304050607000b200110b9818080000240200141146a2802002202450d00200141106a280200210341002104034002402003200441d0006c6a22052802302200418080808078460d00200541306a21060240200541386a2802002207450d00200541346a280200210003400240200041186a2802002208418080808078460d002008450d002000411c6a28020041002802c0a3c68000118080808000000b02402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041386a21002007417f6a22070d000b200628020021000b2000450d00200628020441002802c0a3c68000118080808000000b0240200541206a2802002207450d002005411c6a2802002100034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041246a21002007417f6a22070d000b0b02402005280218450d00200541186a28020441002802c0a3c68000118080808000000b02402005280224450d00200541246a28020441002802c0a3c68000118080808000000b200441016a22042002470d000b0b0240200128020c450d00200128021041002802c0a3c68000118080808000000b02402001280218450d002001411c6a28020041002802c0a3c68000118080808000000b0240200141c0006a2802002204450d002001413c6a28020021064100210503400240200620054105746a22082802082207450d0020082802042100034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041246a21002007417f6a22070d000b0b02402008280200450d00200828020441002802c0a3c68000118080808000000b0240200828020c450d002008410c6a28020441002802c0a3c68000118080808000000b200541016a22052004470d000b0b02402001280238450d00200128023c41002802c0a3c68000118080808000000b200141d4006a10de818080000f0b2001280204450d06200141086a28020041002802c0a3c68000118080808000000f0b2001280204450d05200141086a28020041002802c0a3c68000118080808000000f0b2001280204450d04200141086a28020041002802c0a3c68000118080808000000f0b2001280204450d03200141086a28020041002802c0a3c68000118080808000000f0b2001280204450d02200141086a28020041002802c0a3c68000118080808000000f0b2001280204450d01200141086a28020041002802c0a3c68000118080808000000f0b200141046a10b9818080000240200141186a2802002202450d00200141146a280200210341002104034002402003200441c4006c6a22052802242200418080808078460d00200541246a210602402005412c6a2802002207450d00200541286a280200210003400240200041186a2802002208418080808078460d002008450d002000411c6a28020041002802c0a3c68000118080808000000b02402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041386a21002007417f6a22070d000b200628020021000b2000450d00200628020441002802c0a3c68000118080808000000b0240200541206a2802002207450d002005411c6a2802002100034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041246a21002007417f6a22070d000b0b02402005280218450d00200541186a28020441002802c0a3c68000118080808000000b200441016a22042002470d000b0b0240200141106a280200450d00200128021441002802c0a3c68000118080808000000b2001411c6a280200450d00200141206a28020041002802c0a3c68000118080808000000b0ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241186c2104200241d6aad52a4941027421050240024020010d00200341003602180c010b200341043602182003200141186c36021c200320002802043602140b200341086a20052004200341146a10c281808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bb21208067f017e027f017e017f017e057f017e23808080800041c0006b220324808080800020002802002104024020002802042205450d0002400240200541037122060d00200521070c010b2005210703402007417f6a2107200420042f018e024102746a4190026a28020021042006417f6a22060d000b0b20054104490d000340200420042f018e024102746a4190026a280200220420042f018e024102746a4190026a280200220420042f018e024102746a4190026a280200220420042f018e024102746a4190026a28020021042007417c6a22070d000b0b200341086a41206a200141206a290200370300200341086a41186a200141186a290200370300200341086a41106a200141106a290200370300200341086a41086a200141086a29020037030020032001290200370308200341146a21080340200329030821092003280224210120032802102106200328022c210a0240024003402003418180808078360210200341086a2105024002402006418180808078460d00200121072006210b2009210c0c010b2001200a460d022003200141186a22073602242001280208210b2001290200210c200121050b0240200b418080808078460d00200528020c210d2005290210210e200c422088a72105200ca7210f02402007200a470d0020034180808080783602100c040b2003200741186a22013602242007290200210920072802082106200729020c210c200841086a200741146a2802003602002008200c370200200320063602102006418080808078460d0320052009422088a7470d03200f2009a7200510888e8080000d03200b450d01200d41002802c0a3c68000118080808000000c010b0b20032009370308200a2007460d00200a20076b220441186e22014101712105410021060240200441686a4118490d00200741246a2104200141feffffff007121014100210603400240200441646a280200450d00200441686a28020041002802c0a3c68000118080808000000b02402004417c6a280200450d00200428020041002802c0a3c68000118080808000000b200441306a21042001200641026a2206470d000b0b2005450d002007200641186c6a2204280208450d00200441086a28020441002802c0a3c68000118080808000000b0240200341286a280200450d00200328022041002802c0a3c68000118080808000000b02402000280204220b450d00200028020021060340024002400240024020062f018e022207450d0020064190026a220520074102746a28020022042f018e02220141054f0d0320052007417f6a220d4102746a28020022052f018e022207410520016b220f490d0120052007200f6b22083b018e02200441053b018e02200441b0016a2200200f4103746a2000200141037410fe8d8080001a2004200f4104746a2004200141047410fe8d8080001a2007200841016a220a6b2207410420016b470d022000200541b0016a2202200a4103746a2007410374221010848e808000210020042005200a4104746a2007410474221110848e8080002107200341086a41086a200520084104746a221241086a290200220c3703002006200d4103746a41b0016a2213290200210e201229020021092013200220084103746a290200370200200320093703082006200d4104746a2206290200211420062009370200200641086a220629020021092006200c370200200020106a200e370200200720116a22062014370200200641086a2009370200200b4101460d0320074190026a2206200f410274220f6a2006200141027441046a10fe8d8080001a20062005200a4102746a4190026a200f10848e8080001a200728029002220641003b018c02200620073602880220074194026a280200220641013b018c02200620073602880220074198026a280200220641023b018c0220062007360288022007419c026a280200220641033b018c022006200736028802200741a0026a280200220641043b018c022006200736028802200741a4026a280200220641053b018c0220062007360288020c030b41f8d8c0800041194194d9c0800010f880808000000b41c0d8c08000412741e8d8c0800010f880808000000b4188d8c08000412841b0d8c0800010f880808000000b20042106200b417f6a220b0d000b0b200341c0006a2480808080000f0b20032009370308024002400240024002400240024020042f018e022207410b490d004100210a0240024003402004280288022204450d01200a41016a210a20042f018e02410b4f0d000c020b0b200028020421062000280200210741002d00fca3c680001a41c00241002802c8a3c68000118180808000002204450d022004200736029002200441003b018e02200441003602880220002004360200200741003b018c0220072004360288022000200641016a220a3602040b41002d00fca3c680001a41900241002802c8a3c68000118180808000002206450d02200641003b018e022006410036028802200a417f6a2201450d04034041002d00fca3c680001a41c00241002802c8a3c68000118180808000002207450d042007200636029002200741003b018e022007410036028802200641003b018c022006200736028802200721062001417f6a2201450d050c000b0b2004200741016a3b018e02200420074103746a220641b0016a200f360200200641b4016a2005360200200420074104746a2207200b3602002007200d3602042007200e3702080c040b410441c00210b280808000000b410441900210b280808000000b410441c00210b280808000000b20042f018e022207410b4f0d012004200741016a22013b018e02200420074103746a221041b0016a200f360200201041b4016a2005360200200420074104746a2207200b3602002007200d3602042007200e370208200420014102746a4190026a2006360200200620013b018c022006200436028802200a450d0002400240200a41037122060d00200a21070c010b200a210703402007417f6a2107200420042f018e024102746a4190026a28020021042006417f6a22060d000b0b200a4104490d000340200420042f018e024102746a4190026a280200220420042f018e024102746a4190026a280200220420042f018e024102746a4190026a280200220420042f018e024102746a4190026a28020021042007417c6a22070d000b0b2002200228020041016a3602000c010b0b41d8d6c08000412041f8d7c0800010f880808000000bc804020d7f037e23808080800041c0006b22022480808080002001280204210320012802002104200028020821052000280200210602400240024020002802042207200028020c2208460d00200028021021092001280208200341246c6a2101200820076b41486a210a200241086a41206a210b200241086a41086a210c4100210d03402007200d6a2200412c6a280200220e418080808078460d02200041306a290300210f200241086a41286a200041286a280200360200200b200041206a290300370300200241086a41186a200041186a290300370300200241086a41106a200041106a290300370300200c200041086a2903003703002002200029030022103703082002200f3703382002200e3602342009200c10dd828080002100200b290200210f2002280234210e20022903382111200141086a200b41086a2802003602002001200f370200200141206a2000360200200141186a2010370200200141106a20113702002001410c6a200e41ffffffff0171360200200141246a2101200341016a21032007200d41386a220d6a2008470d000b0b200420033602000c010b20042003360200200041386a2008460d00200041e8006a2100200a200d6b41386e210103400240200041706a280200450d00200041746a28020041002802c0a3c68000118080808000000b02402000417c6a280200450d00200028020041002802c0a3c68000118080808000000b200041386a21002001417f6a22010d000b0b02402005450d00200641002802c0a3c68000118080808000000b200241c0006a2480808080000bab02010d7f23808080800041206b220224808080800020012802042103200128020021042000280208210520002802002106024020002802042207200028020c2208460d002000280210210920012802082003410c6c6a2100200241086a210a2002410472220b41186a210c200b41106a210d034020072802002201450d01200b2007290204370200200c2007411c6a280200360200200d200741146a290200370200200b41086a2007410c6a290200370200200220013602002002280204210e200041086a2009200a10dd82808000360200200041046a200e360200200020013602002000410c6a2100200341016a2103200741206a22072008470d000b0b2004200336020002402005450d00200641002802c0a3c68000118080808000000b200241206a2480808080000b870301107f23808080800041c0006b220224808080800020012802042103200128020021042000280208210520002802002106024020002802042207200028020c2208460d0020002802102109200128020820034104746a2100200241086a41206a210a200241086a41086a210b200241086a410472220141306a210c200141286a210d200141186a210e03402007280200220f450d0120012007290204370200200c200741346a280200360200200d2007412c6a290200370200200141206a200741246a290200370200200e2007411c6a290200370200200141106a200741146a290200370200200141086a2007410c6a2902003702002002200f360208200228020c21102009200b10dd8280800021112000410c6a2009200a10dd82808000360200200041086a2011360200200041046a20103602002000200f360200200041106a2100200341016a2103200741386a22072008470d000b0b2004200336020002402005450d00200641002802c0a3c68000118080808000000b200241c0006a2480808080000b9a0605097f027e087f027e017f23808080800041c0006b2202248080808000200128020421032001280200210420002802082105200028020021060240024020002802042207200028020c2208460d00200028021021092001280208200341386c6a2100200241086a2101024003402007280200220a4102460d012001200741d0006a28020036020020022007290248370300200741106a290200210b2007290208210c20072d0060210d200728025c210e2007280258210f2007280254211020072802442111200728024021122007280218211302400240200a0d002002200c370328200220133602382002200b3703302009200241286a10dd82808000210a41808080807821140c010b200741306a2902002115200729022821162007280238210a200728021c211720072802042114200220072802203602202002201736021c200220133602182002200b37031020022015370330200220163703282002200a3602382009200241106a10dd8280800021172009200241286a10dd828080002113200ca7210a0b20002002290300370200200041346a200d3a0000200041306a20113602002000412c6a2012360200200041286a2013360200200041246a2017360200200041206a200c422088a73602002000411c6a200a360200200041186a2014360200200041146a200e360200200041106a200f3602002000410c6a201041ffffffff0171360200200041086a2001280200360200200041386a2100200341016a2103200741e8006a22072008470d000b200420033602000c020b200741e8006a21070b20042003360200200820076b41e8006e210020082007460d00034002402007280200450d00200741046a280200450d00200741086a28020041002802c0a3c68000118080808000000b0240200741c8006a280200450d00200741cc006a28020041002802c0a3c68000118080808000000b0240200741d4006a280200450d00200741d8006a28020041002802c0a3c68000118080808000000b200741e8006a21072000417f6a22000d000b0b02402005450d00200641002802c0a3c68000118080808000000b200241c0006a2480808080000be00301077f200028020c2201200028020422026b41f8006e2103024020012002460d0041002104034002402002200441f8006c6a22052802502201418080808078460d000240200541d0006a22062802082207450d0020062802042101034002402001280200450d00200141046a280200450d00200141086a28020041002802c0a3c68000118080808000000b0240200141c8006a280200450d00200141cc006a28020041002802c0a3c68000118080808000000b0240200141d4006a280200450d00200141d8006a28020041002802c0a3c68000118080808000000b200141e8006a21012007417f6a22070d000b200628020021010b2001450d00200628020441002802c0a3c68000118080808000000b0240200541ec006a2802002207450d00200541e8006a28020041306a210103400240200141706a280200450d00200141746a28020041002802c0a3c68000118080808000000b02402001417c6a280200450d00200128020041002802c0a3c68000118080808000000b200141386a21012007417f6a22070d000b0b0240200541e4006a2201280200450d00200128020441002802c0a3c68000118080808000000b200441016a22042003470d000b0b02402000280208450d00200028020041002802c0a3c68000118080808000000b0b820401077f0240200028020c220120002802042202460d00200120026b41077621034100210403400240200220044107746a22052802502201418080808078460d000240200541d0006a22062802082207450d0020062802042101034002402001280200450d00200141046a280200450d00200141086a28020041002802c0a3c68000118080808000000b0240200141c8006a280200450d00200141cc006a28020041002802c0a3c68000118080808000000b0240200141d4006a280200450d00200141d8006a28020041002802c0a3c68000118080808000000b200141e8006a21012007417f6a22070d000b200628020021010b2001450d00200628020441002802c0a3c68000118080808000000b0240200541ec006a2802002207450d00200541e8006a28020041306a210103400240200141706a280200450d00200141746a28020041002802c0a3c68000118080808000000b02402001417c6a280200450d00200128020041002802c0a3c68000118080808000000b200141386a21012007417f6a22070d000b0b0240200541e4006a2201280200450d00200128020441002802c0a3c68000118080808000000b02402005280270450d00200541f0006a28020441002802c0a3c68000118080808000000b200441016a22042003470d000b0b02402000280208450d00200028020041002802c0a3c68000118080808000000b0b950301037f0240200028020022022802002200413f4b0d00200041027421020240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20023a00000f0b0240200041ffff004b0d002000410274410172210202402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20023b00000f0b0240200041ffffffff034b0d002000410274410272210202402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20023600000f0b02402001280200220320012802082200470d0020012000410110b18280800020012802002103200128020821000b2001280204220420006a41033a00002001200041016a2200360208200228020021020240200320006b41034b0d0020012000410410b18280800020012802042104200128020821000b2001200041046a360208200420006a20023600000bd80301057f23808080800041106b22022480808080002000280218210320022000411c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a3602082002200041206a36020c2002410c6a200110cb81808000200028020421032002200028020822043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a360208200041106a28020021042002200041146a28020022003602082002200241086a36020c2002410c6a200110cb8180800002402000450d00200420004103746a21060340200428020021032002200428020422003602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20004f0d0020012005200010b182808000200128020821050b200128020420056a2003200010848e8080001a2001200520006a360208200441086a22042006470d000b0b200241106a2480808080000bdf0601067f23808080800041106b2202248080808000200028022c21032002200041306a28020022043602082002200241086a36020c2002410c6a200110cb81808000024020012802002205200128020822066b20044f0d0020012006200410b18280800020012802002105200128020821060b2001280204220720066a2003200410848e8080001a2001200620046a220436020820002d00342106024020052004470d0020012005410110b18280800020012802042107200128020821040b200720046a20063a00002001200441016a2204360208024002402000280218418080808078470d00024020012802002004470d0020012004410110b18280800020012802042107200128020821040b2000411c6a21062001200441016a360208200720046a41003a00000c010b024020012802002004470d0020012004410110b18280800020012802042107200128020821040b2001200441016a360208200720046a41013a0000200028021c21062002200041206a28020022073602082002200241086a36020c2002410c6a200110cb8180800002402007450d00034020062d000021050240200128020020012802082204470d0020012004410110b182808000200128020821040b200641016a21062001200441016a360208200128020420046a20053a00002007417f6a22070d000b0b2002200041246a36020c2002410c6a200110cb81808000200041286a21060b2002200636020c2002410c6a200110cb81808000200028020421072002200028020822043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822066b20044f0d0020012006200410b182808000200128020821060b200128020420066a2007200410848e8080001a2001200620046a360208200041106a28020021042002200041146a28020022063602082002200241086a36020c2002410c6a200110cb8180800002402006450d00200420064103746a21000340200428020021052002200428020422063602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822076b20064f0d0020012007200610b182808000200128020821070b200128020420076a2005200610848e8080001a2001200720066a360208200441086a22042000470d000b0b200241106a2480808080000bcf0701117f23808080800041b0016b220624808080800020062004370310200620033703082006200536021841002107200641206a41206a41003602002006420037023420064200370228200642808080808001370220200620012802003602800120062001280204220536027c2006200536027820062005200128020841f8006c6a360284012006200641206a36028801200641c4006a200641f8006a10be81808000200641206a200210dd8280800021082002411c6a28020021092002280218210a20022d0024210b024002400240200241206a280200220c0d004104210d0c010b4100210741002d00fca3c680001a200c410474220241002802c8a3c6800011818080800000220d450d012009200c41386c6a210e200641f8006a41206a210f200641f8006a41086a2110200641f8006a410472220541306a2111200541286a2112200541186a2113200d210120092102034020022802002214450d01200520022902043702002011200241346a28020036020020122002412c6a290200370200200541206a200241246a29020037020020132002411c6a290200370200200541106a200241146a290200370200200541086a2002410c6a29020037020020062014360278200628027c2115200641206a201010dd8280800021162001410c6a200641206a200f10dd82808000360200200141086a2016360200200141046a201536020020012014360200200141106a2101200741016a2107200241386a2202200e470d000b0b0240200a450d00200941002802c0a3c68000118080808000000b200641206a200641086a10dd828080002101200641d0006a41206a200641206a41206a2802002205360200200641d0006a41186a2214200641206a41186a2902002204370300200641d0006a41106a200641206a41106a290200370300200641d0006a41086a200641206a41086a290200370300200620062902203703502006200541002004a722021b360298012006200641d0006a411c6a28020022053602940120062002360290012006410036028c0120062002410047221536028801200620053602840120062002360280012006410036027c200620153602782000200641f8006a10c882808000200641dc006a10d38280800002402006280250450d00200628025441002802c0a3c68000118080808000000b201410d482808000200041146a200641c4006a41086a2802003602002000200629024437020c2000200136022c200041286a200b3a0000200041246a2008360200200041206a20073602002000411c6a200d3602002000200c360218200641b0016a2480808080000f0b4104200210b280808000000b900503057f017e067f23808080800041c0006b2203248080808000200141cc006a2802002104200128024821054180808080782106024020012802502207418080808078460d00200141dc006a2902002108200141d4006a2802002109200141d8006a28020021062003200236023820032009200641e8006c6a360234200320073602302003200936022c20032009360228200341146a200341286a10bd81808000200341086a200837030020032003290218370300200328021421060b4100210702400240200128021022090d004100210a0c010b200129030021082003200141086a29030037033020032008370328200320093602382002200341286a10dd82808000210b4101210a0b02400240200141286a28020022090d000c010b200129031821082003200141206a29030037033020032008370328200320093602382002200341286a10dd82808000210c410121070b200141ec006a280200210d200141e8006a28020021092001280264210e200320023602382003200e3602302003200936022c2003200936022820032009200d41386c6a360234200341146a200341286a10bf8180800002400240200141c0006a28020022090d00410021020c010b200129033021082003200141386a29030037033020032008370328200320093602382002200341286a10dd828080002109410121020b2000200536023820002006360224200020073602082000200b3602042000200a36020020002003290214370218200020023602102000413c6a2004360200200041286a20032903003702002000410c6a200c360200200020012d00703a0040200041146a2009360200200041306a200341086a290300370200200041206a200341146a41086a280200360200200341c0006a2480808080000b850301037f23808080800041106b2202248080808000200028020421032002200028020822043602082002200241086a36020c2002410c6a200110cb8180800002402004450d00200441386c210403402003200110d681808000200341386a2103200441486a22040d000b0b200041106a28020021032002200041146a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402004450d00200441c4006c210403402003200110d181808000200341c4006a2103200441bc7f6a22040d000b0b2002200041246a36020c2002410c6a200110cb81808000200041286a2d000021040240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a20043a00002001200341016a3602082000411c6a28020021032002200041206a28020022043602082002200241086a36020c2002410c6a200110cb8180800020032004200110b88180800020022000412c6a36020c2002410c6a200110cb81808000200241106a2480808080000bb00701057f23808080800041106b22022480808080002000280238210320022000413c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b2001280204220620056a2003200410848e8080001a2001200520046a2204360208024002402000280224418080808078470d00024020012802002004470d0020012004410110b18280800020012802042106200128020821040b2001200441016a360208200620046a41003a00000c010b200041246a2105024020012802002004470d0020012004410110b18280800020012802042106200128020821040b2001200441016a360208200620046a41013a00002005200110d2818080000b0240024020002802000d000240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41003a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a00002002200041046a36020c2002410c6a200110cb818080000b0240024020002802080d000240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41003a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a000020022000410c6a36020c2002410c6a200110cb818080000b2000411c6a28020021042002200041206a28020022053602082002200241086a36020c2002410c6a200110cb8180800002402005450d00200541246c210503402004200110cc81808000200441246a21042005415c6a22050d000b0b0240024020002802100d000240200128020020012802082205470d0020012005410110b182808000200128020821050b2001200541016a2204360208200128020420056a41003a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a00002002200041146a36020c2002410c6a200110cb81808000200128020821040b20002d00402105024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a20053a0000200241106a2480808080000be80101047f23808080800041106b2202248080808000200028020c21032002200041106a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a360208200028020421042002200028020822003602082002200241086a36020c2002410c6a200110cb8180800002402000450d00200041386c210003402004200110cd81808000200441386a2104200041486a22000d000b0b200241106a2480808080000ba80201047f23808080800041106b22022480808080002000280208210320022000410c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a22043602080240024020002802000d00024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41003a00000c010b024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a00002002200041046a360204200241046a200110cb818080000b200241106a2480808080000bdb0501057f23808080800041106b220224808080800002400240200028020c22030d000240200128020020012802082204470d0020012004410110b182808000200128020821040b200128020420046a41003a0000200441016a21040c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b200128020420046a41013a00002001200441016a3602082002200041106a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a200520046a21040b200120043602082002200041146a36020c2002410c6a200110cb8180800002400240200028021822030d000240200128020020012802082204470d0020012004410110b182808000200128020821040b200128020420046a41003a0000200441016a21040c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b200128020420046a41013a00002001200441016a36020820022000411c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a200520046a21040b20012004360208200028020421042002200028020822003602082002200241086a36020c2002410c6a200110cb8180800002402000450d00200420004103746a21060340200428020021032002200428020422003602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20004f0d0020012005200010b182808000200128020821050b200128020420056a2003200010848e8080001a2001200520006a360208200441086a22042006470d000b0b200241106a2480808080000beb0301057f23808080800041106b22022480808080002000280218210320022000411c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a360208200028020421042002200028020822053602082002200241086a36020c2002410c6a200110cb8180800002402005450d002005410574210503402004200110d481808000200441206a2104200541606a22050d000b0b20002d002021050240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a20053a0000200041106a28020021042002200041146a28020022053602082002200241086a36020c2002410c6a200110cb8180800002402005450d00200420054103746a21060340200428020021032002200428020422053602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822006b20054f0d0020012000200510b182808000200128020821000b200128020420006a2003200510848e8080001a2001200020056a360208200441086a22042006470d000b0b200241106a2480808080000bb70c01067f23808080800041106b22022480808080002002200041346a36020c2002410c6a200110cb81808000200028020421032002200028020822043602082002200241086a36020c2002410c6a200110cb8180800002402004450d00200320044103746a21050340200328020021062002200328020422043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822076b20044f0d0020012007200410b182808000200128020821070b200128020420076a2006200410848e8080001a2001200720046a360208200341086a22032005470d000b0b200041106a28020021032002200041146a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402004450d002004410474210403402003200110d381808000200341106a2103200441706a22040d000b0b02400240024002400240024002400240024020002d00240e080001020304050607000b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41003a00002001200341016a3602082000412c6a28020021032002200041306a28020022043602082002200241086a36020c2002410c6a200110cb818080002004450d072004410574210403402003200110d481808000200341206a2103200441606a22040d000c080b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41013a00002001200341016a3602082000412c6a28020021032002200041306a28020022043602082002200241086a36020c2002410c6a200110cb818080002004450d06200441246c210403402003200110d581808000200341246a21032004415c6a22040d000c070b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41023a00002002200041286a36020c2002410c6a200110cb818080000c050b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41033a00002001200341016a2203360208200041286a28020021040240200128020020036b41034b0d0020012003410410b182808000200128020821030b2001200341046a360208200128020420036a200436000020022000412c6a36020c2002410c6a200110cb818080000c040b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41043a00002001200341016a3602082000412c6a28020021032002200041306a28020022043602082002200241086a36020c2002410c6a200110cb818080002004450d032004410274210403402002200336020c2002410c6a200110cb81808000200341046a21032004417c6a22040d000c040b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41053a00002001200341016a2203360208200041256a2d00002104024020012802002003470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a20043a00000c020b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41063a00002002200041286a36020c2002410c6a200110cb818080000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41073a00002002200041286a36020c2002410c6a200110cb8180800020022000412c6a36020c2002410c6a200110cb818080000b2000411c6a28020021032002200041206a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402004450d00200320044103746a21050340200328020021062002200328020422043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822076b20044f0d0020012007200410b182808000200128020821070b200128020420076a2006200410848e8080001a2001200720046a360208200341086a22032005470d000b0b200241106a2480808080000bf40b01037f23808080800041206b220224808080800002400240417f200110d881808000220341046a220420042003491b2203417f4c0d0041002d00fca3c680001a200341002802c8a3c68000118180808000002204450d01200220043602102002200336020c200420012802603600004104210420024104360214024002400240024002400240024002400240200128020022034188808080786a410f2003418780808078481b41786a0e080001020304050607000b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a41083a00002002200341016a360214200141086a280200210420022001410c6a28020022013602182002200241186a36021c2002411c6a2002410c6a10cb818080000240200228020c200228021422036b20014f0d002002410c6a2003200110b182808000200228021421030b200228021020036a2004200110848e8080001a2002200320016a3602140c070b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a41093a00002002200341016a360214200141086a280200210420022001410c6a28020022013602182002200241186a36021c2002411c6a2002410c6a10cb818080000240200228020c200228021422036b20014f0d002002410c6a2003200110b182808000200228021421030b200228021020036a2004200110848e8080001a2002200320016a3602140c060b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a410a3a00002002200341016a360214200141086a280200210420022001410c6a28020022013602182002200241186a36021c2002411c6a2002410c6a10cb818080000240200228020c200228021422036b20014f0d002002410c6a2003200110b182808000200228021421030b200228021020036a2004200110848e8080001a2002200320016a3602140c050b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a410b3a00002002200341016a360214200141086a280200210420022001410c6a28020022013602182002200241186a36021c2002411c6a2002410c6a10cb818080000240200228020c200228021422036b20014f0d002002410c6a2003200110b182808000200228021421030b200228021020036a2004200110848e8080001a2002200320016a3602140c040b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a410c3a00002002200341016a360214200141086a280200210420022001410c6a28020022013602182002200241186a36021c2002411c6a2002410c6a10cb818080000240200228020c200228021422036b20014f0d002002410c6a2003200110b182808000200228021421030b200228021020036a2004200110848e8080001a2002200320016a3602140c030b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a410d3a00002002200341016a360214200141086a280200210420022001410c6a28020022013602182002200241186a36021c2002411c6a2002410c6a10cb818080000240200228020c200228021422036b20014f0d002002410c6a2003200110b182808000200228021421030b200228021020036a2004200110848e8080001a2002200320016a3602140c020b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a410e3a00002002200341016a360214200141046a2002410c6a10d0818080000c010b0240200228020c4104470d002002410c6a4104410110b182808000200228021421040b200228021020046a410f3a00002002200441016a36021420012002410c6a10e5818080000b2000200229020c370200200041086a2002410c6a41086a280200360200200241206a2480808080000f0b10ae80808000000b4101200310b280808000000b840301057f024002400240024002400240024002400240200028020022014188808080786a410f2001418780808078481b41786a0e080001020304050607000b2000410c6a28020041046a21000c070b2000410c6a28020041046a21000c060b2000410c6a28020041046a21000c050b2000410c6a28020041046a21000c040b2000410c6a28020041046a21000c030b2000410c6a28020041046a21000c020b200041186a21022000410c6a2103410621010240200041286a280200220441c000490d0041072101200441808001490d004109410a2004418080808004491b21010b2002280200210420032802002103200041246a2802002105410121020240200041306a280200220041c000490d0041022102200041808001490d00410441052000418080808004491b21020b417f417f417f200441c4006c200341386c41047222006a41046a220320032000491b220020054104742001726a220120012000491b220020026a220120012000491b21000c010b200010e48180800021000b200041016a0b931003137f017e047f23808080800041206b2203248080808000024002400240024020014115490d0041002d00fca3c680001a0240200141017641186c41002802c8a3c68000118180808000002204450d0041002d00fca3c680001a41800141002802c8a3c68000118180808000002205450d04200041686a2106200041346a210741002108410021094110210a0240034020002008220b41186c220c6a210d0240024002402001200b6b220e4102490d000240200d41186a280200220f200d280200200d411c6a2802002210200d41046a280200221120102011491b10888e8080002212201020116b20121b4100480d0041022112200e4102460d022007200c6a21114102211203402011417c6a2802002213200f20112802002214201020142010491b10888e808000220f201420106b200f1b4100480d03201141186a2111201421102013210f200e201241016a2212460d020c000b0b410221120240200e4102460d002007200c6a21114102211203402011417c6a2802002213200f20112802002214201020142010491b10888e808000220f201420106b200f1b417f4a0d01201141186a2111201421102013210f200e201241016a2212470d000b200e21120b024002402012200b6a22082012490d00200820014b0d0120124102490d042012410176210e200c201241186c6a2115200621142000210f0340200f200c6a221041086a221329020021162013201420156a221141086a221729020037020020172016370200201141146a2802002113201141106a221728020021182017201041106a221929020037020020102902002116201020112902003702002011201637020020192018360200201041146a2013360200201441686a2114200f41186a210f200e417f6a220e0d000c050b0b200b200841c8dbc08000109681808000000b2008200141c8dbc08000109581808000000b200e21120b2012200b6a21080b024002402008200b490d00200820014d0d010b41b8dcc08000412c41e4dcc0800010f880808000000b024002400240200820014f0d002012410a490d010b2008200b6b21100c010b200b410a6a2210200120102001491b2208200b490d02200d2008200b6b221020124101201241014b1b10da818080000b024002402009200a470d0041002d00fca3c680001a200941047441002802c8a3c68000118180808000002211450d012009410174210a20112005200941037410848e8080002111200541002802c0a3c6800011808080800000201121050b200520094103746a2211200b36020420112010360200200941016a22152109024020154102490d0003400240024002400240200520152213417f6a22154103746a2209280200221020092802046a2001460d00201341037420056a221441706a280200221220104d0d0041022109201341024d0d0520052013417d6a22184103746a2802002211201220106a4d0d0141032109201341034d0d05201441606a280200201120126a4d0d01201321090c050b20134103490d0120052013417d6a22184103746a28020021110b20112010490d010b2013417e6a21180b024002400240024002400240201320184d0d002013201841016a22104d0d01200520104103746a22192802042019280200221a6a2211200520184103746a220d280204220c490d02201120014b0d032000200c41186c6a2209200d280200221741186c22126a2110201141186c211402402011200c6b220f20176b221120174f0d0020042010201141186c221210848e808000220e20126a211220174101480d0520114101480d05200620146a2111034020112012201241686a2214280200201041686a220f280200201441046a2802002214200f41046a280200220f2014200f491b10888e808000220b2014200f6b200b1b2214411f75220f417f7341186c6a22122010200f41186c6a22102014417f4a1b2214290200370200201141106a201441106a290200370200201141086a201441086a290200370200201020094d0d06201141686a21112012200e4d0d060c000b0b20042009201210848e808000221120126a21120240201741014e0d00201121110c060b0240200f20174a0d00201121110c060b200020146a210e20112111034020092011201020102802002011280200201041046a2802002214201141046a280200220f2014200f491b10888e808000220b2014200f6b200b1b220b417f4a220f1b2214290200370200200941106a201441106a290200370200200941086a201441086a290200370200200941186a21092011200f41186c6a221120124f0d062010200b411f7641186c6a2210200e490d000c060b0b200341146a42003702002003410136020c200341ccdac08000360208200341d4dac08000360210200341086a41d8dbc0800010f680808000000b200341146a42003702002003410136020c200341ccdac08000360208200341d4dac08000360210200341086a41e8dbc0800010f680808000000b200c201141f8dbc08000109681808000000b2011200141f8dbc08000109581808000000b20102109200e21110b20092011201220116b10848e8080001a2019200c3602042019201a20176a360200200d200d41086a20132018417f736a41037410fe8d8080001a41012109201541014b0d000b0b200820014f0d050c010b0b41a8dcc0800010a081808000000b200b200841f4dcc08000109681808000000b4188dcc0800010a081808000000b200141014d0d0120002001410110da818080000c010b200541002802c0a3c6800011808080800000200441002802c0a3c68000118080808000000b200341206a2480808080000f0b4198dcc0800010a081808000000bba0303097f017e017f23808080800041106b220324808080800002402002417f6a20014f0d000240200220014f0d00200241186c20006a41506a2104034002402000200241186c6a22052802002206200541686a2207280200200541046a2802002208200741046a280200220920082009491b10888e808000220a200820096b200a1b417f4a0d0020052007290200370200200341086a220b200541106a2209290200370300200541086a2205290200210c2005200741086a2902003702002009200741106a2902003702002003200c370300024020024101460d004101210d200421050340200541186a2107200620052802002008200541046a280200220920082009491b10888e808000220a200820096b200a1b417f4a0d0120072005290200370200200741106a200541106a290200370200200741086a200541086a290200370200200541686a21052002200d41016a220d470d000b200021070b200720083602042007200636020020072003290300370208200741106a200b2903003702000b200441186a2104200241016a22022001470d000b0b200341106a2480808080000f0b4184ddc08000412e41b4ddc0800010f880808000000bef0602077f017e0240200128022022020d00200128020021022001410036020002402002450d000240200128020422020d0020012802082102200128020c2203450d0002400240200341077122040d00200321050c010b2003210503402005417f6a210520022802980421022004417f6a22040d000b0b20034108490d000340200228029804280298042802980428029804280298042802980428029804280298042102200541786a22050d000b0b03402002280290042105200241002802c0a3c68000118080808000002005210220050d000b0b20004180808080783602200f0b20012002417f6a360220200128020421020240024002400240024020012802002205450d002002450d010b2005450d022001410c6a2802002104200141086a28020021030c010b200141086a280200210202402001410c6a2802002203450d0002400240200341077122040d00200321050c010b2003210503402005417f6a210520022802980421022004417f6a22040d000b0b20034108490d000340200228029804280298042802980428029804280298042802980428029804280298042102200541786a22050d000b0b20014200370208200120023602042001410136020041002104410021030b0240200420022f0196044f0d00200221050c020b024003402002280290042205450d0120022f0194042104200241002802c0a3c6800011808080800000200341016a210320052102200420052f019604490d030c000b0b200241002802c0a3c680001180808080000041a8dac0800010a081808000000b41c8dec0800010a081808000000b200441016a21060240024020030d00200521020c010b200520064102746a4198046a2802002102410021062003417f6a2207450d002003417e6a2108024020074107712203450d0003402007417f6a210720022802980421022003417f6a22030d000b0b20084107490d000340200228029804280298042802980428029804280298042802980428029804280298042102200741786a22070d000b0b2001200636020c2001410036020820012002360204200520044103746a290200210920002005200441286c6a220241d8006a290300370308200041106a200241e0006a290300370300200041186a200241e8006a290300370300200041206a200241f0006a290300370300200041286a200241f8006a290300370300200020093703000be50101047f23808080800041106b2203248080808000200320013602082003200341086a36020c2003410c6a200210cb8180800002402001450d0020002001410c6c6a21040340200028020021052003200028020422013602082003200341086a36020c2003410c6a200210cb8180800002402002280200200228020822066b20014f0d0020022006200110b182808000200228020821060b200228020420066a2005200110848e8080001a2002200620016a3602082003200041086a36020c2003410c6a200210cb818080002000410c6a22002004470d000b0b200341106a2480808080000bfb0201057f23808080800041106b22022480808080002000280218210320022000411c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a36020820002802042000280208200110dc818080002002200041206a36020c2002410c6a200110cb81808000200041106a28020021042002200041146a28020022003602082002200241086a36020c2002410c6a200110cb8180800002402000450d00200420004103746a21060340200428020021032002200428020422003602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20004f0d0020012005200010b182808000200128020821050b200128020420056a2003200010848e8080001a2001200520006a360208200441086a22042006470d000b0b200241106a2480808080000ba70501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802900221012005417f6a22050d000b0b20024108490d000340200128029002280290022802900228029002280290022802900228029002280290022101200041786a22000d000b0b410021050b024002400240200520012f018e02490d0003402001280288022200450d0220012f018c022105200141002802c0a3c6800011808080800000200441016a210420002101200520002f018e024f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a4190026a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802900221002004417f6a22040d000b0b20074107490d010340200028029002280290022802900228029002280290022802900228029002280290022100200641786a22060d000c020b0b200141002802c0a3c680001180808080000041a8dac0800010a081808000000b0240200120054104746a2201280200450d00200128020441002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802900221002005417f6a22050d000b0b20024108490d000340200028029002280290022802900228029002280290022802900228029002280290022100200141786a22010d000b0b03402000280288022101200041002802c0a3c68000118080808000002001210020010d000b0b0ba00601097f2000280200210102400240024020002802202202450d0020002802042103034020002002417f6a220236022002400240024002400240024002402001450d0020030d0020002802082104200028020c2205450d03200541077122060d01200521030c020b2001450d04200028020c210620002802082105200321040c030b2005210303402003417f6a210320042802980421042006417f6a22060d000b0b20054108490d000340200428029804280298042802980428029804280298042802980428029804280298042104200341786a22030d000b0b2000420037020820002004360204410121012000410136020041002106410021050b02400240200620042f019604490d0003402004280290042203450d0220042f0194042106200441002802c0a3c6800011808080800000200541016a210520032104200620032f0196044f0d000b200321040b200641016a2107024020050d00200421030c030b200420074102746a4198046a2802002103410021072005417f6a2208450d022005417e6a2109024020084107712205450d0003402008417f6a210820032802980421032005417f6a22050d000b0b20094107490d020340200328029804280298042802980428029804280298042802980428029804280298042103200841786a22080d000c030b0b200441002802c0a3c680001180808080000041a8dac0800010a081808000000b41c8dec0800010a081808000000b2000200736020c200041003602082000200336020402402004200641286c6a41f0006a2204280200450d00200428020441002802c0a3c68000118080808000000b20020d000b200041003602000c010b200041003602002001450d01200028020422030d0020002802082103200028020c2205450d0002400240200541077122060d00200521040c010b2005210403402004417f6a210420032802980421032006417f6a22060d000b0b20054108490d000340200328029804280298042802980428029804280298042802980428029804280298042103200441786a22040d000b0b03402003280290042104200341002802c0a3c68000118080808000002004210320040d000b0b0be70801097f23808080800041d0016b2209248080808000200920043703102009200337030820092005360218200941206a41206a220a41003602002009420037023420094200370228200942808080808001370220200920012802003602b4012009200128020422053602b001200920053602ac012009200520012802084107746a3602b8012009200941206a3602bc01200941c4006a200941ac016a10ba8180800020022d006c210b200941206a200210dd82808000210c200941206a200241186a10dd82808000210d200941206a200241306a10dd82808000210e200941206a200241c8006a10dd82808000210f200241e4006a280200210120022802602110024002400240200241e8006a28020022020d00410421050c010b41002d00fca3c680001a2002410474221141002802c8a3c68000118180808000002205450d010b200941a0016a41086a22114100360200200920053602a401200920023602a00120092001200241386c6a3602b801200920103602b401200920013602b001200920013602ac012009200941206a3602bc0120092005360280012009410036027c20092011360278200941ac016a200941f8006a10c781808000200941d0006a41086a22012011280200360200200920092902a001370350200941206a200941086a10dd828080002105200920062802003602b4012009200628020422023602b001200920023602ac012009200220062802084105746a3602b8012009200941206a3602bc01200941e0006a200941ac016a10bb81808000200941206a200710dd828080002106200941206a200741186a10dd828080002111200941206a200741306a10dd828080002107200941ec006a2008200941206a10e181808000200941f8006a41206a200a2802002208360200200941f8006a41186a220a200941206a41186a2902002204370300200941f8006a41106a200941206a41106a290200370300200941f8006a41086a200941206a41086a290200370300200920092902203703782009200841002004a722021b3602cc01200920094194016a28020022083602c801200920023602c401200941003602c0012009200241004722103602bc01200920083602b801200920023602b401200941003602b001200920103602ac012000200941ac016a10c88280800020094184016a10d38280800002402009280278450d00200928027c41002802c0a3c68000118080808000000b200a10d482808000200041146a200941c4006a41086a2802003602002000200929024437020c20002009290350370218200041206a200128020036020020002005360244200041346a200b3a0000200041306a200f3602002000412c6a200e360200200041286a200d360200200041246a200c36020020002009290260370238200041c0006a200941e0006a41086a280200360200200041d0006a2007360200200041cc006a2011360200200020063602482000200929026c370254200041dc006a200941ec006a41086a280200360200200941d0016a2480808080000f0b4104201110b280808000000bb80301037f23808080800041d0006b22032480808080002001280204210420012802082105200128020021012003200236024420032005410020011b3602402003200436023c20032001360238200341003602342003200141004722023602302003200436022c200320013602282003410036022420032002360220200341086a200341206a10c081808000200328020c2101024002400240200328021022020d0002402003280208450d00200141002802c0a3c68000118080808000000b41002101410021020c010b2003200341cf006a36022020012002200341206a10d9818080002003280208210541002d00fca3c680001a41900241002802c8a3c68000118180808000002204450d01200441003b018e02200441003602880220034100360218200320043602142003410036021c20032001200241186c6a360244200320053602402003200136023c200320013602382003418180808078360228200341146a200341206a2003411c6a10c4818080002003280214210120032802182104200328021c21020b200020023602082000200436020420002001360200200341d0006a2480808080000f0b410441900210b280808000000bcd0201087f23808080800041306b2203248080808000200141246a280200210420012802202105200128020421062001280200210702400240200141286a28020022080d00410421090c010b41002d00fca3c680001a2008410c6c220a41002802c8a3c680001181808080000022090d004104200a10b280808000000b200341046a41086a220a41003602002003200936020820032008360204200320023602202003200420084105746a36021c2003200536021820032004360214200320043602102003200936022c200341003602282003200a360224200341106a200341246a10c681808000200041086a200a2802003602002000200329020437020020002002200141086a10dd828080003602202000411c6a200636020020002007360218200041106a200141306a2903003702002000200128022c41ffffffff017136020c200341306a2480808080000bb30503057f017e067f23808080800041c0006b2203248080808000200141cc006a2802002104200128024821054180808080782106024020012802502207418080808078460d00200141dc006a2902002108200141d4006a2802002109200141d8006a28020021062003200236023820032009200641e8006c6a360234200320073602302003200936022c20032009360228200341146a200341286a10bd81808000200341086a200837030020032003290218370300200328021421060b4100210702400240200128021022090d004100210a0c010b200129030021082003200141086a29030037033020032008370328200320093602382002200341286a10dd82808000210b4101210a0b02400240200141286a28020022090d000c010b200129031821082003200141206a29030037033020032008370328200320093602382002200341286a10dd82808000210c410121070b200141ec006a280200210d200141e8006a28020021092001280264210e200320023602382003200e3602302003200936022c2003200936022820032009200d41386c6a360234200341146a200341286a10bf8180800002400240200141c0006a28020022090d00410021020c010b200129033021082003200141386a29030037033020032008370328200320093602382002200341286a10dd828080002109410121020b2000200536024420002006360230200020073602082000200b3602042000200a36020020002003290214370218200041c8006a2004360200200041346a20032903003702002000410c6a200c360200200041286a200141f4006a2902003702002000200128027041ffffffff01713602242000413c6a200341086a290300370200200041206a200341146a41086a280200360200200020012d007c3a004c200041146a200936020020002002360210200341c0006a2480808080000bfa04010d7f410221010240200041246a280200220241c000490d0041032101200241808001490d00410541062002418080808004491b21010b41012102410121030240200041286a280200220441c000490d0041022103200441808001490d00410441052004418080808004491b21030b02402000412c6a280200220441c000490d0041022102200441808001490d00410441052004418080808004491b21020b41012104410121050240200041306a280200220641c000490d0041022105200641808001490d00410441052006418080808004491b21050b02402000280244220641c000490d0041022104200641808001490d00410441052006418080808004491b21040b410121064101210702402000280248220841c000490d0041022107200841808001490d00410441052008418080808004491b21070b200041146a2108200041206a2109200041c0006a210a0240200041cc006a280200220b41c000490d0041022106200b41808001490d0041044105200b418080808004491b21060b2008280200210b2000280208210c20092802002109200a280200210a410121080240200041d0006a280200220d41c000490d0041022108200d41808001490d0041044105200d418080808004491b21080b417f417f417f417f417f417f200c41386c410472220c200b41d0006c6a41046a220b200b200c491b220b417f200320016a20026a20056a220120094104746a41046a220220022001491b6a22012001200b491b220120046a220220022001491b2201200a4105746a41046a220220022001491b2201200620076a20086a6a220220022001491b2201200041dc006a280200220041047420004103746a6a41046a220020002001491b0bec0a01097f23808080800041106b2202248080808000200028020421032002200028020822043602082002200241086a36020c2002410c6a200110cb8180800002402004450d00200441386c210403402003200110d681808000200341386a2103200441486a22040d000b0b200041106a28020021032002200041146a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402004450d00200441d0006c210403402003200110e681808000200341d0006a2103200441b07f6a22040d000b0b200041346a2d000021040240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a20043a00002002200041246a36020c2002410c6a200110cb818080002002200041286a36020c2002410c6a200110cb8180800020022000412c6a36020c2002410c6a200110cb818080002002200041306a36020c2002410c6a200110cb818080002000411c6a28020021032002200041206a28020022043602082002200241086a36020c2002410c6a200110cb8180800020032004200110b8818080002002200041c4006a36020c2002410c6a200110cb818080002000413c6a28020021032002200041c0006a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402004450d002004410574210403402003200110e781808000200341206a2103200441606a22040d000b0b2002200041c8006a36020c2002410c6a200110cb818080002002200041cc006a36020c2002410c6a200110cb818080002002200041d0006a36020c2002410c6a200110cb818080002002200041dc006a28020022053602082002200241086a36020c2002410c6a200110cb81808000024002402005450d0020002802542206450d004100210320064100472107200041d8006a280200210003400240024002402007450d002003450d010b20070d0141e8dec0800010a081808000000b410121072006210302402000450d0020002104024020004107712206450d0003402004417f6a210420032802900221032006417f6a22060d000b0b20004108490d000340200328029002280290022802900228029002280290022802900228029002280290022103200441786a22040d000b0b41002106410021000b0240200020032f018e02490d0003402003280288022204450d04200641016a210620032f018c02210020042103200020042f018e024f0d000b0b200041016a21080240024020060d00200321040c010b200320084102746a4190026a2802002104410021082006417f6a2209450d002006417e6a210a024020094107712206450d0003402009417f6a210920042802900221042006417f6a22060d000b0b200a4107490d000340200428029002280290022802900228029002280290022802900228029002280290022104200941786a22090d000b0b200320004103746a220641b0016a28020021092002200641b4016a28020022063602082002200241086a36020c2002410c6a200110cb81808000200320004104746a210302402001280200200128020822006b20064f0d0020012000200610b182808000200128020821000b200128020420006a2009200610848e8080001a2001200020066a36020820022003410c6a36020c2002410c6a200110cb81808000200328020421062002200328020822033602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822006b20034f0d0020012000200310b182808000200128020821000b200128020420006a2006200310848e8080001a2001200020036a3602084100210620082100200421032005417f6a22050d000b0b200241106a2480808080000f0b41d8dec0800010a081808000000be70801057f23808080800041106b2202248080808000200028024421032002200041c8006a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a2204360208024002402000280230418080808078470d00024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41003a00000c010b200041306a2105024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a00002005200110d2818080000b0240024020002802000d000240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41003a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a00002002200041046a36020c2002410c6a200110cb818080000b0240024020002802080d000240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41003a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a000020022000410c6a36020c2002410c6a200110cb818080000b2000411c6a28020021042002200041206a28020022053602082002200241086a36020c2002410c6a200110cb8180800002402005450d00200541246c210503402004200110cc81808000200441246a21042005415c6a22050d000b0b0240024020002802100d000240200128020020012802082205470d0020012005410110b182808000200128020821050b2001200541016a2204360208200128020420056a41003a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a00002002200041146a36020c2002410c6a200110cb81808000200128020821040b20002d004c2105024020012802002004470d0020012004410110b182808000200128020821040b200128020420046a20053a00002001200441016a360208200041286a280200210420022000412c6a28020022053602082002200241086a36020c2002410c6a200110cb8180800002402005450d00200420054103746a21060340200428020021032002200428020422053602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822006b20054f0d0020012000200510b182808000200128020821000b200128020420006a2003200510848e8080001a2001200020056a360208200441086a22042006470d000b0b200241106a2480808080000ba80301057f23808080800041106b22022480808080002000280218210320022000411c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a360208200028020421042002200028020822053602082002200241086a36020c2002410c6a200110cb8180800002402005450d00200541246c210503402004200110dd81808000200441246a21042005415c6a22050d000b0b200041106a28020021042002200041146a28020022053602082002200241086a36020c2002410c6a200110cb8180800002402005450d00200420054103746a21060340200428020021032002200428020422053602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822006b20054f0d0020012000200510b182808000200128020821000b200128020420006a2003200510848e8080001a2001200020056a360208200441086a22042006470d000b0b200241106a2480808080000bc00302057f017e0240024002400240024020012d00000d004100210241002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0241ffdfc080002101034002402001417f6a2d0000412072220441506a220541ff0171410a490d00200441997f6a41ff017141f9014d0d05200441a97f6a21050b024020012d0000412072220641506a220441ff0171410a490d00200641997f6a41ff017141f9014d0d06200641a97f6a21040b200320026a20042005410474723a0000200141026a2101200241016a2205210220054120470d000b200020032f00003b0001200041106a200329000f370000200041036a200341026a2d00003a0000200041186a200341176a290000370000200041206a2003411f6a2d00003a000020032900032107200328000b2101200341002802c0a3c68000118080808000002000410c6a2001360000200041046a20073700000c010b20002001290001370001200041196a200141196a290000370000200041116a200141116a290000370000200041096a200141096a2900003700000b200041013a00000f0b4101412010b280808000000b418087c0800010a081808000000b419087c0800010a081808000000bf20305027f017e027f017e017f23808080800041c0006b2201248080808000200141246a41bee0c08000410441c2e0c08000411d41fcdfc08000410010d882808000200141086a410036020020014280808080c00037030020012802242102200129022821032001410c6a41086a2204410036020020014280808080c00037020c2001410c6a410010eb818080002001280210200428020041246c6a220541003a00202005410836021c200541dfe0c080003602182005420437021020054200370208200542808080808001370200200141306a41086a200428020041016a22053602002001200129020c2206370330024020052006a7470d00200141306a200510eb81808000200128023821050b2001280234200541246c6a220541013a00202005410736021c200541e7e0c08000360218200542043702102005420037020820054280808080800137020002402002418080808078470d0041f8dec08000411141ecdfc0800010a181808000000b20012802342105200128023021042001280238210720002001290300370350200042808080808001370244200020023602382000200536020820002004360204200041013a0000200041cc006a41003602002000413c6a20033702002000200741016a36020c200041d8006a200141086a280200360200200141c0006a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a10ea81808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000be00101037f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014108200141084b1b2201417f73411f7621040240024020030d00200241003602180c010b2002200336021c20024101360218200220002802043602140b200241086a20042001200241146a10ec81808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10ec81808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a10ec81808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10ec81808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10ec81808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a10ec81808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a10ec81808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bba0504057f017e027f017e23808080800041c0006b2201248080808000200141246a41a0e2c08000410441a4e2c08000411741a0e2c08000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702302001428080808080013702382001410c6a200141306a10fe81808000200141086a200141206a28020036020020012001290218370300200128020c2102200128021021032001280214210420012802242105200129022821062001410c6a41086a2207410036020020014280808080c00037020c2001410c6a410010ef818080002001280210200728020041246c6a220841003a00202008410336021c2008419ae2c080003602182008420437021020084200370208200842808080808001370200200141306a41086a200728020041016a22083602002001200129020c2209370330024020082009a7470d00200141306a200810ef81808000200128023821080b2001280234200841246c6a220841013a00202008410236021c2008419de2c0800036021820084204370210200842003702082008428080808080013702002001280238210720012802342108200120012802303602142001200836020c20012008200741246c6a41246a36021820012008360210200141306a2001410c6a10808280800002402005418080808078470d0041eee0c08000411141e0e1c0800010a181808000000b200120023602142001200336020c200120033602102001200320044105746a360218200041c4006a2001410c6a10fe81808000200141176a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290300370350200041d8006a200141086a2802003602002001200129023037000f2000200129000c370001200041086a200141136a290000370000200141c0006a2480808080000bbf0604057f017e037f017e23808080800041d0006b2201248080808000200141306a41bbe2c08000410d41a4e2c08000411741a0e2c08000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a10fe81808000200141086a41086a2001412c6a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a2207410036020020014280808080c000370218200141186a410010ef81808000200128021c200728020041246c6a220841003a00202008410636021c200841c8e2c080003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810ef81808000200128024821080b2001280244200841246c6a220841013a00202008410b36021c200841cee2c0800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810ef81808000200128022021080b200128021c200841246c6a220841023a00202008410936021c200841d9e2c08000360218200842043702102008420037020820084280808080800137020020012802202107200128021c2108200120012802183602202001200836021820012008200741246c6a41246a3602242001200836021c200141c0006a200141186a10808280800002402005418080808078470d0041eee0c08000411141e0e1c0800010a181808000000b20012002360220200120033602182001200336021c2001200320044105746a360224200041c4006a200141186a10fe81808000200141236a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a2001411f6a290000370000200141d0006a2480808080000b9f0704057f017e037f017e23808080800041d0006b2201248080808000200141286a41e2e2c08000410c41a4e2c08000411741a0e2c08000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10fe81808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a22074100360200200142808080808001370210200141106a410010ee818080002001280214200728020041386c6a2208420437022c20084206370224200841f6e1c080003602202008410636021c200841f0e1c08000360218200841ae808080003602102008428a96cd9bb2e69e8d723703082008429b99b4f08dc8ccdea77f370300200141c0006a41086a2209200728020041016a220836020020012001290210220a37034002402008200aa7470d00200141c0006a200810ee81808000200128024821080b2001280244200841386c6a2208420437022c2008420d37022420084181e2c080003602202008410536021c200841fce1c08000360218200841af80808000360210200842a3e3bbd692f2b9f9c000370308200842b79f92f0eda6f987be7f3703002007200928020041016a220836020020012001290340220a37031002402008200aa7470d00200141106a200810ee81808000200128021821080b2001280214200841386c6a2208420437022c2008420437022420084196e2c080003602202008410836021c2008418ee2c08000360218200841b080808000360210200842d58ec2d7bbaa93feab7f370308200842b3e2d6a8e7e4a79f77370300200128021821072001280214210820012001280210360248200120083602402001200836024420012008200741386c6a41386a36024c200141346a200141c0006a10ff8180800002402005418080808078470d0041eee0c08000411141e0e1c0800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10fe818080002001411b6a200141346a41086a2802003600002000413c6a200637020020002005360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000bba0504057f017e027f017e23808080800041c0006b2201248080808000200141246a41f0e2c08000410d41fde2c08000412341f0e2c08000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702302001428080808080013702382001410c6a200141306a10fe81808000200141086a200141206a28020036020020012001290218370300200128020c2102200128021021032001280214210420012802242105200129022821062001410c6a41086a2207410036020020014280808080c00037020c2001410c6a410010ef818080002001280210200728020041246c6a220841003a00202008410436021c200841a0e3c080003602182008420437021020084200370208200842808080808001370200200141306a41086a200728020041016a22083602002001200129020c2209370330024020082009a7470d00200141306a200810ef81808000200128023821080b2001280234200841246c6a220841013a00202008410836021c200841a4e3c0800036021820084204370210200842003702082008428080808080013702002001280238210720012802342108200120012802303602142001200836020c20012008200741246c6a41246a36021820012008360210200141306a2001410c6a10808280800002402005418080808078470d0041eee0c08000411141e0e1c0800010a181808000000b200120023602142001200336020c200120033602102001200320044105746a360218200041c4006a2001410c6a10fe81808000200141176a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290300370350200041d8006a200141086a2802003602002001200129023037000f2000200129000c370001200041086a200141136a290000370000200141c0006a2480808080000b140020012000280204200028020810dd808080000b2100200128021441ace3c080004105200141186a28020028020c118280808000000bc40403027f017e047f2380808080004190016b220024808080800041002101200041086a41b1e3c08000411341002802b8a1c680001185808080000002402000280208450d00200041186a41086a200041086a41086a290200220237030020002000290208370318200028021c210302402002a7220441034b0d0002400240417f4100280284a4c680002201410147200141014b1b2201417f460d00200141ff01710d010b2000413c6a41e4e5c08000410241b1e3c08000411310fc818080002000412c6a410c6a41b180808000360200200041b28080800036023020002000418f016a36023420002000413c6a36022c4100280290a1c680002101410028028ca1c6800021054100280280a4c68000210620004180016a4202370200200041f8006a4102360200200041f0006a4110360200200041ec006a41e6e5c08000360200200041e0006a41f6e5c08000ad4280808080900d84370200200041c8006a410c6a41dfe6c08000ad4280808080800484370200200041fc006a2000412c6a360200200041d4e5c08000360274200041013602682000410036025c2000410036025020004281808080c003370248200541ecf2c08000200641024622061b200041c8006a200141d4f2c0800020061b28021011848080800000200028023c450d00200028024041002802c0a3c68000118080808000000b200041246a20032004200028021828020c11858080800000410021010c010b20032800002101200041246a20032004200028021828020c118580808000000b20004190016a24808080800020010b890301047f23808080800041d0006b2201248080808000024002400240024010fa8180800022020e020001020b0240417f4100280284a4c680002202410247200241024b1b2202417f460d00200241ff01710d030b4100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141c4006a4200370200200141c0006a41ace3c080003602002001413c6a4101360200200141346a4125360200200141306a41a8e4c08000360200200141246a41cde4c08000ad4280808080e00d84370200200141186a41a8e4c08000ad4280808080d00484370200200141a0e4c08000360238200141003602202001410036021420014281808080800937020c2001410236022c200341ecf2c08000200441024622041b2001410c6a200241d4f2c0800020041b280210118480808000000c020b41b1e3c08000411341002802a0a1c68000118480808000000c010b20012002417f6a36020c41b1e3c0800041132001410c6a410441002802e0a1c68000118680808000000b200141d0006a2480808080000beb0601057f23808080800041106b220524808080800041012106024002400240200441017420026a2207450d002007417f4c0d0241002d00fca3c680001a200741002802c8a3c68000118180808000002206450d010b4100210820054100360208200520063602042005200736020002402002450d00200120026a210741002108034002400240024020012c00002202417f4c0d00200141016a2101200241ff017121020c010b20012d0001413f7121062002411f712109024002402002415f4b0d0020094106742006722102200141026a21010c010b200641067420012d0002413f717221060240200241704f0d0020062009410c74722102200141036a21010c010b200641067420012d0003413f71722009411274418080f00071722202418080c400460d04200141046a21010b2002418001490d002005410036020c024002402002418010490d0002402002418080044f0d0020052002413f71418001723a000e20052002410c7641e001723a000c20052002410676413f71418001723a000d410321020c020b20052002413f71418001723a000f2005200241127641f001723a000c20052002410676413f71418001723a000e20052002410c76413f71418001723a000d410421020c010b20052002413f71418001723a000d2005200241067641c001723a000c410221020b0240200528020020086b20024f0d0020052008200210f381808000200528020821080b200528020420086a2005410c6a200210848e8080001a200820026a21080c010b024020082005280200470d002005200810ed81808000200528020821080b200528020420086a20023a0000200528020841016a21080b2005200836020820012007470d000b0b02402004450d000340413041d70020032d0000220141a001491b20014104766a2102024020082005280200470d002005200810ed81808000200528020821080b200528020420086a20023a00002005200528020841016a2208360208024020082005280200470d002005200810ed81808000200528020821080b200341016a2103200528020420086a413041d7002001410f712208410a491b20086a3a00002005200528020841016a22083602082004417f6a22040d000b0b20002005290200370200200041086a200541086a280200360200200541106a2480808080000f0b4101200710b280808000000b10ae80808000000b130020004200370200200041086a42043702000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710f18180800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710f08180800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710f28180800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000b9c07010b7f23808080800041306b22022480808080002001280208210302400240024002400240024002400240024002400240200128020022040d0020030d01200242808080801037020c410021050c080b200128020420046b210520030d01200521060c020b2001410c6a28020020036b21060c010b20052001410c6a28020020036b6a22062005490d010b0240024020060d00410121070c010b2006417f4c0d0641002d00fca3c680001a200641002802c8a3c68000118180808000002207450d070b4100210520024100360214200220073602102002200636020c200128020c21082001280204210102400240024020040d002003450d07200820036b21090c010b200120046b2105024020030d00200521090c010b2005200820036b6a22092005490d010b410021050240200620094f0d002002410c6a4100200910f38180800020022802102107200228021421050b2004450d0420042001460d04200120046b2206410371210a2004417f7320016a41034f0d02410021010c030b200241246a42003702002002410136021c20024190e7c0800036021820024198e7c08000360220200241186a41a0e9c0800010f680808000000b200241186a410c6a42003702002002410136021c20024190e7c0800036021820024198e7c08000360220200241186a419ce8c0800010f680808000000b200720056a210b2006417c71210c410021010340200b20016a2206200420016a22092d00003a0000200641016a200941016a2d00003a0000200641026a200941026a2d00003a0000200641036a200941036a2d00003a0000200c200141046a2201470d000b200520016a21050b200a450d00200420016a21010340200720056a20012d00003a0000200141016a2101200541016a2105200a417f6a220a0d000b0b2003450d0020032008460d00200820036b2201410371210a024002402003417f7320086a41034f0d00410021010c010b200720056a21042001417c71210b410021010340200420016a2206200320016a22092d00003a0000200641016a200941016a2d00003a0000200641026a200941026a2d00003a0000200641036a200941036a2d00003a0000200b200141046a2201470d000b200520016a21050b200a450d00200320016a21010340200720056a20012d00003a0000200141016a2101200541016a2105200a417f6a220a0d000b0b2000200229020c370200200041086a2005360200200241306a2480808080000f0b10ae80808000000b4101200610b280808000000bf20503037f017e037f23808080800041c0006b220124808080800020014106360210200141b0e9c0800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241b0e9c08000360200200241046a410636020041b0e9c08000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d022003429b99b4f08dc8ccdea77f370308200341ae8080800036021820034101360204200341b6e9c08000360200200341106a428a96cd9bb2e69e8d7237030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a108f828080002001290230210420012802382105200141186a41086a2206410036020020014280808080c000370218200141186a4100109582808000200128021c200628020041246c6a220341003a00202003410436021c200341b7e9c080003602182003420437021020034200370208200342808080808001370200200141306a41086a2207200628020041016a360200200120012902183703302001410c6a200141306a41bbe9c080004104109a828080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a108d82808000200141236a2007280200360000200041cc006a2005360200200020043702442000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b6e00200042d7c9cb8fc1cf97db3e37030820004280808080c00037033820004280808080c000370350200041b580808000360218200041063a0000200041106a42e88488d0c0e3aebc13370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b02000bd70809017e027f027e017f077e017f037e017f037e200141386a29030021022001290330502103200141c0006a2104427f200141286a2903002205200129031822065022071b2108200141d0006a290300210920014190026a290300210a2001290348210b02400240427f200141206a290300220c20071b220d200129038802220e580d002008200a580d00200d200b580d004100210f20082009560d010b200d200884420052210f0b20042903002110200141a0026a2107427f200220031b21022001290310211120012903082112410121040240024020012802002213450d002012500d012011500d010b20134520124200200d200b7d22142014200d561b5820114200200820097d220d200d2008561b587172410173200f7221040b427f201020031b21122007290300210d2001290398022108024002402002200b580d0020122009560d010b200220128442005220047221040b2008200b200e7c58200d2009200a7c58722006a7410047200c2008562005200d56727172200472210320014190016a290300211120014188016a29030050210720014198016a2104427f20014180016a290300220c200141f0006a290300221050220f1b210b200141a8016a2903002102200141a0016a290300210902400240427f200141f8006a2903002214200f1b2212200e580d00200b200a580d0020122009580d00200b2002560d010b2012200b8442005220037221030b20042903002115427f201120071b2111200141e8006a2903002106200141e0006a290300210541012104024002402001280258220f450d002005500d012006500d010b200f4520054200201220097d221620162012561b5820064200200b20027d22122012200b561b58717241017320037221040b427f201520071b210b0240024020112009580d00200b2002560d010b2011200b8442005220047221040b20082009200e7c58200d2002200a7c58722010a74100472014200856200c200d567271722004722103200141e8016a2903002111200141e0016a290300502107200141f0016a2104427f200141d8016a2903002210200141c8016a290300221450220f1b210b20014180026a2903002102200141f8016a290300210902400240427f200141d0016a2903002215200f1b2212200e580d00200b200a580d0020122009580d00200b2002560d010b2012200b8442005220037221030b2004290300210c427f201120071b2111200141c0016a2903002106200141b8016a2903002105410121040240024020012802b001220f450d002005500d012006500d010b200f4520054200201220097d221620162012561b5820064200200b20027d22122012200b561b58717241017320037221040b427f200c20071b210b0240024020112009580d00200b2002560d010b2011200b8442005220047221040b024020082009200e7c580d00200d2002200a7c580d002014a74520152008582010200d587172450d0020040d002000200141a80210848e8080001a0f0b20004202370300200041013a00080bfe0606017f027e017f017e037f027e23808080800041b0026b2202248080808000200241086a200141a80210848e8080001a200241a8026a290300210320022903a0022104024020022903205022050d00200241306a2903002206200320062003561b2103200241286a2903002206200420062004561b21040b0240200241f8006a2903005022070d0020024188016a2903002206200320062003561b210320024180016a2903002206200420062004561b21040b200141ac026a210820012802a80221010240200241d0016a2903005022090d00200241e0016a2903002206200320062003561b2103200241d8016a2903002206200420062004561b21040b20083502002106200220033703a802200220043703a00202402001450d0020032003428094ebdc0380220a428094ebdc037e7d20067e2203428094ebdc0380220b200a20067e7c2003200b428094ebdc037e7d4280cab5ee0156ad7c210b20042004428094ebdc03802203428094ebdc037e7d20067e2204428094ebdc0380220a200320067e7c2004200a428094ebdc037e7d4280cab5ee0156ad7c21060240200229030850450d00024002402005450d004200210a0c010b42004200200241306a2903002203200b7d220420042003561b2203200241d8006a2903007d220420042003561b210442004200200241286a290300220320067d220a200a2003561b220320022903507d220a200a2003561b21034201210a0b20022004370318200220033703102002200a3703080b024020022903604200520d004200210a024020070d004200420020024188016a2903002203200b7d220420042003561b2203200241b0016a2903007d220420042003561b21044200420020024180016a290300220320067d220a200a2003561b2203200241a8016a2903007d220a200a2003561b21034201210a0b200241f0006a2004370300200241e8006a20033703002002200a3703600b20022903b8014200520d00024002402009450d00420021060c010b42004200200241e0016a2903002203200b7d220420042003561b220320024188026a2903007d220420042003561b210442004200200241d8016a290300220320067d220620062003561b220320024180026a2903007d220620062003561b2103420121060b200241c8016a2004370300200241c0016a2003370300200220063703b8010b2000200241086a108582808000200241b0026a2480808080000bc40403057f017e037f23808080800041d0006b2201248080808000200141286a41afebc08000410b41baebc08000411441c0e9c08000410010d882808000200141206a42043702002001420037021820014280808080800137021020014288808080800137023420014280808080800137023c200141106a200141346a108f82808000200141086a2202200141106a41146a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c21062001280228210720014100360218200142808080808001370210200141106a41001096828080002001280214200128021841386c6a2208420437022c2008421537022420084182eec080003602202008410336021c200841ffedc08000360218200841b680808000360210200842a5968aeabda0f18d34370308200842a2c6d1e2c3c0eaf01f370300200128021821092001280214210820012001280210360218200120083602102001200836021420012008200941386c6a41386a36021c200141c4006a200141106a108e8280800002402007418080808078470d0041e3ecc08000411141d4edc0800010a181808000000b200141346a410b6a200141c4006a41086a2802003600002000200336024420002007360238200041003a000020002001290300370350200041cc006a2005360200200041c8006a20043602002000413c6a20063702002001200129024437003720002001290034370001200041d8006a2002280200360200200041086a2001413b6a290000370000200141d0006a2480808080000b2100200128021441a0ebc08000410f200141186a28020028020c118280808000000bd20403027f017e047f410121014101210202402000290348220342c000540d0041022102200342808001540d00410421022003428080808004540d004109200379a74103766b21020b0240200041d0006a290300220342c000540d0041022101200342808001540d00410421012003428080808004540d004109200379a74103766b21010b410121044101210502402000290300500d004102210502402000290308220342c000540d0041032105200342808001540d00410521052003428080808004540d00410a200379a74103766b21050b410121060240200041106a290300220342c000540d0041022106200342808001540d00410421062003428080808004540d004109200379a74103766b21060b200520066a21050b02402000290318500d00410221040240200041206a290300220342c000540d0041032104200342808001540d00410521042003428080808004540d00410a200379a74103766b21040b410121060240200041286a290300220342c000540d0041022106200342808001540d00410421062003428080808004540d004109200379a74103766b21060b200420066a21040b02400240200029033050450d00410121000c010b410221060240200041386a290300220342c000540d0041032106200342808001540d00410521062003428080808004540d00410a200379a74103766b21060b410121070240200041c0006a290300220342c000540d0041022107200342808001540d00410421072003428080808004540d004109200379a74103766b21070b200620076a21000b200120026a20056a20046a20006a0bbf0804057f017e037f017e23808080800041e0006b2201248080808000200141306a41ceebc08000410f41baebc08000411441c0e9c08000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a108f82808000200141086a41086a200141186a41146a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a22074100360200200142808080808001370218200141186a4100109682808000200128021c200728020041386c6a2208420437022c20084206370224200841a5eec080003602202008410e36021c20084197eec08000360218200841ae808080003602102008428a96cd9bb2e69e8d723703082008429b99b4f08dc8ccdea77f370300200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a2008109682808000200128024821080b2001280244200841386c6a2208420437022c2008420e370224200841b8eec080003602202008410d36021c200841abeec08000360218200841b7808080003602102008428caaf9b7c8a1dba0c900370308200842e3cce7c1b8e587e3ba7f3703002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a2008109682808000200128022021080b200128021c200841386c6a2208420437022c2008420e370224200841b8eec080003602202008410936021c200841c6eec08000360218200841b7808080003602102008428caaf9b7c8a1dba0c900370308200842e3cce7c1b8e587e3ba7f370300200141c0006a41086a200141186a41086a28020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a2008109682808000200128024821080b2001280244200841386c6a2208420437022c2008420e370224200841b8eec080003602202008410836021c200841cfeec08000360218200841b7808080003602102008428caaf9b7c8a1dba0c900370308200842e3cce7c1b8e587e3ba7f370300200128024821072001280244210820012001280240360220200120083602182001200836021c20012008200741386c6a41386a360224200141d4006a200141186a108e8280800002402005418080808078470d0041e3ecc08000411141d4edc0800010a181808000000b200141cb006a200141d4006a41086a2802003600002000200236024420002005360238200041003a000020002001290308370350200041cc006a2004360200200041c8006a20033602002000413c6a20063702002001200129025437004320002001290040370001200041d8006a200141086a41086a280200360200200041086a200141c7006a290000370000200141e0006a2480808080000bd40203027f017e027f41012101410121020240200029038802220342c000540d0041022102200342808001540d00410421022003428080808004540d004109200379a74103766b21020b024020004190026a290300220342c000540d0041022101200342808001540d00410421012003428080808004540d004109200379a74103766b21010b41012104410121050240200029039802220342c000540d0041022105200342808001540d00410421052003428080808004540d004109200379a74103766b21050b0240200041a0026a290300220342c000540d0041022104200342808001540d00410421042003428080808004540d004109200379a74103766b21040b417f200120026a20056a20046a2201417f417f20001089828080002202200041d8006a1089828080006a220420042002491b2202200041b0016a1089828080006a220020002002491b6a220020002001491b0b940704057f017e037f017e23808080800041e0006b2201248080808000200141306a41ddebc08000410c41baebc08000411441c0e9c08000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a108f82808000200141086a41086a200141186a41146a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a22074100360200200142808080808001370218200141186a4100109682808000200128021c200728020041386c6a2208420437022c20084206370224200841a5eec080003602202008410a36021c200841d7eec08000360218200841ae808080003602102008428a96cd9bb2e69e8d723703082008429b99b4f08dc8ccdea77f370300200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a2008109682808000200128024821080b2001280244200841386c6a2208420437022c20084206370224200841a5eec080003602202008410936021c200841e1eec08000360218200841ae808080003602102008428a96cd9bb2e69e8d723703082008429b99b4f08dc8ccdea77f3703002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a2008109682808000200128022021080b200128021c200841386c6a2208420437022c20084221370224200841f3eec080003602202008410936021c200841eaeec08000360218200841b8808080003602102008429de3c1c199ed89c7f200370308200842b6a3f7ac9ac487f47237030020012802202107200128021c210820012001280218360220200120083602182001200836021c20012008200741386c6a41386a360224200141d4006a200141186a108e8280800002402005418080808078470d0041e3ecc08000411141d4edc0800010a181808000000b200141cb006a200141d4006a41086a2802003600002000200236024420002005360238200041003a000020002001290308370350200041cc006a2004360200200041c8006a20033602002000413c6a20063702002001200129025437004320002001290040370001200041d8006a200141086a41086a280200360200200041086a200141c7006a290000370000200141e0006a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710988280800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710998280800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710978280800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000be50604047f017e027f017e23808080800041d0006b2201248080808000200141306a41ecebc08000411041fcebc08000411741ecebc08000410010d8828080002001412c6a410036020020014280808080c00037022441002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d00200242d7c9cb8fc1cf97db3e370308200241b5808080003602182002410136020420024193ecc08000360200200241106a42e88488d0c0e3aebc13370300200141086a41086a200141246a220341086a280200360200200120032902003703082001280230210420012902342105200141186a41086a22064100360200200142808080808001370218200141186a4100109682808000200128021c200628020041386c6a2203420437022c20034201370224200341ededc080003602202003410636021c200341eeedc08000360218200341b580808000360210200342e88488d0c0e3aebc13370308200342d7c9cb8fc1cf97db3e370300200141c0006a41086a2207200628020041016a2203360200200120012902182208370340024020032008a7470d00200141c0006a2003109682808000200128024821030b2001280244200341386c6a2203420437022c20034201370224200341ededc080003602202003410b36021c200341f4edc08000360218200341b580808000360210200342e88488d0c0e3aebc13370308200342d7c9cb8fc1cf97db3e3703002006200728020041016a2203360200200120012903402208370318024020032008a7470d00200141186a2003109682808000200128022021030b200128021c200341386c6a2203420437022c20034201370224200341ededc080003602202003410936021c200341e4edc08000360218200341b580808000360210200342e88488d0c0e3aebc13370308200342d7c9cb8fc1cf97db3e3703002004418080808078460d012001280220210320012802182106200128021c210720004101360244200020043602382000200736020820002006360204200041003a000020002001290308370350200041cc006a4101360200200041c8006a20023602002000413c6a20053702002000200341016a36020c200041d8006a200141106a280200360200200141d0006a2480808080000f0b4108412010b280808000000b41e3ecc08000411141d4edc0800010a181808000000be90604047f017e027f017e23808080800041d0006b2201248080808000200141306a41ecebc08000411041fcebc08000411741ecebc08000410010d8828080002001412c6a410036020020014280808080c00037022441002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d0020024296a2989994a79f81d800370308200241bb808080003602182002410136020420024193ecc08000360200200241106a4295f8a9f7f1dbffef28370300200141086a41086a200141246a220341086a280200360200200120032902003703082001280230210420012902342105200141186a41086a22064100360200200142808080808001370218200141186a4100109682808000200128021c200628020041386c6a2203420437022c20034201370224200341ededc080003602202003410636021c200341eeedc08000360218200341bb8080800036021020034295f8a9f7f1dbffef2837030820034296a2989994a79f81d800370300200141c0006a41086a2207200628020041016a2203360200200120012902182208370340024020032008a7470d00200141c0006a2003109682808000200128024821030b2001280244200341386c6a2203420437022c20034201370224200341ededc080003602202003410b36021c200341f4edc08000360218200341bb8080800036021020034295f8a9f7f1dbffef2837030820034296a2989994a79f81d8003703002006200728020041016a2203360200200120012903402208370318024020032008a7470d00200141186a2003109682808000200128022021030b200128021c200341386c6a2203420437022c20034201370224200341ededc080003602202003410936021c200341e4edc08000360218200341bb8080800036021020034295f8a9f7f1dbffef2837030820034296a2989994a79f81d8003703002004418080808078460d012001280220210320012802182106200128021c210720004101360244200020043602382000200736020820002006360204200041003a000020002001290308370350200041cc006a4101360200200041c8006a20023602002000413c6a20053702002000200341016a36020c200041d8006a200141106a280200360200200141d0006a2480808080000f0b4108412010b280808000000b41e3ecc08000411141d4edc0800010a181808000000b9e0405027f017e017f017e027f23808080800041c0006b2201248080808000200141286a4194ecc0800041054199ecc08000410c41ecebc08000410010d882808000200141086a410036020020014280808080c00037030020012802282102200129022c21032001410036021820014280808080c000370210200141346a200141106a41a5ecc08000410e109b828080000240200128023c22042001280234470d00200141346a2004109582808000200128023c21040b2001280238200441246c6a220441013a00202004410c36021c200441b3ecc080003602182004420437021020044200370208200442808080808001370200200141106a41086a200141346a41086a28020041016a2204360200200120012902342205370310024020042005a7470d00200141106a2004109582808000200128021821040b2001280214200441246c6a220441023a00202004410e36021c200441bfecc08000360218200442043702102004420037020820044280808080800137020002402002418080808078470d0041e3ecc08000411141d4edc0800010a181808000000b200128021421042001280210210620012802182107200042808080808001370244200020023602382000200436020820002006360204200041013a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200741016a36020c200041d8006a200141086a280200360200200141c0006a2480808080000bb80405027f017e027f017e017f23808080800041c0006b2201248080808000200141246a41cdecc0800041164199ecc08000410c41ecebc08000410010d882808000200141086a410036020020014280808080c00037030020012802242102200129022821032001410c6a41086a2204410036020020014280808080800137020c2001410c6a41001096828080002001280210200428020041386c6a2205420437022c20054213370224200541a3efc080003602202005410c36021c20054197efc08000360218200541bc80808000360210200542b2b8a880edaaf0a1c000370308200542e1ffbd85d68ecce4b97f370300200141306a41086a200428020041016a22053602002001200129020c2206370330024020052006a7470d00200141306a2005109682808000200128023821050b2001280234200541386c6a2205420437022c20054219370224200541bfefc080003602202005410936021c200541b6efc08000360218200541bd80808000360210200542efc9c9edb5e7b3a6c700370308200542acf6debeefe0d9c8d30037030002402002418080808078470d0041e3ecc08000411141d4edc0800010a181808000000b200128023821052001280230210420012802342107200042808080808001370244200020023602382000200736020820002004360204200041003a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200541016a36020c200041d8006a200141086a280200360200200141c0006a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a109482808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a109482808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a109482808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a109482808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a109482808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a41001096828080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641ae808080003602102006428a96cd9bb2e69e8d723703082006429b99b4f08dc8ccdea77f37030020042802042107200428020821080240200128020822062001280200470d0020012006109582808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbd0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a41001096828080002004280208200428020c220541386c6a2206420437022c2006420337022420064194efc0800036022020064100360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e37030020042802042107200428020821080240200128020822062001280200470d0020012006109582808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000be708012d7e0240200141184b0d000240411820016b41037441e8efc080006a41a8f1c08000460d00410020014103746b210120002903c0012102200029039801210320002903702104200029034821052000290320210620002903b80121072000290390012108200029036821092000290340210a2000290318210b20002903b001210c200029038801210d2000290360210e2000290338210f2000290310211020002903a8012111200029038001211220002903582113200029033021142000290308211520002903a00121162000290378211720002903502118200029032821192000290300211a03402017201685201885201985201a85221b200d200c85200e85200f85201085221c42018985221d201485211e201b4201892008200785200985200a85200b85221f85221b2002852120201d2011854202892221201c2003200285200485200585200685222242018985221c200a8542378922232012201185201385201485201585220a201f42018985221f201085423e892224427f8583852102200a42018920228522102017854229892222201b2004854227892225427f85832023852111201d201385420a892226201c2007854238892227201f200d85420f892228427f858385210d202620102019854224892229427f8583201b200685421b89222a852117201f200f85420689222b201d201585420189222c427f858320102016854212892216852104201b200385420889222d201c200985421989222e427f8583202b852113201b200585421489221b201c200b85421c89220b427f8583201f200c85423d89220f852105201d201285422d89221d200b200f427f858385210a200f201d427f85832010201885420389221585210f201d2015427f8583201b8521142015201b427f8583200b8521192010201a85221d2020420e89221b427f8583201c200885421589221c85210b201b201c427f8583201f200e85422b89221f852110201e422c892206201c201f427f8583852115200141a8f1c080006a290300201f2006427f858385201d85211a2029202a427f8583202785221c21032006201d427f8583201b85221d210620242021427f8583202285221b2107202a2027427f8583202885221f2108202c2016427f8583202d852227210920212022427f85832025852221210c2016202d427f8583202e852222210e20282026427f85832029852226211220252023427f858320248522232116202c202e202b427f85838522242118200141086a22010d000b200020233703a001200020173703782000202437035020002019370328200020113703a8012000202637038001200020133703582000201437033020002015370308200020213703b0012000200d37038801200020223703602000200f370338200020103703102000201b3703b8012000201f37039001200020273703682000200a3703402000200b370318200020023703c0012000201c3703980120002004370370200020053703482000201d3703202000201a3703000b0f0b4181f2c0800041c10041c4f2c0800010f880808000000b02000b040041000b02000b02000bc00202057f057e23808080800041d0006b220524808080800002402003450d00200541186a420037020020054101360210200541b8f3c0800036020c200541d4f2c080003602142005410c6a41a4f4c0800010f680808000000b4100280290a1c680002103410028028ca1c6800021064100280280a4c6800021072002280210220828020821092000290200210a2002290208210b2008290200210c2002290200210d2000290208210e200541c8006a2000290210370200200541c0006a200e370200200541306a200d370200200541246a200c370200200541186a200b3702002005200a3702382005200136022c2005410036022020054100360214200520093602102005410136020c200641ecf2c08000200741024622021b2005410c6a200341d4f2c0800020021b28021011848080800000200541d0006a2480808080000bf60201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120003100004101200110fe8080800021000c020b20002d00002103410021000340200220006a41ff006a413041d7002003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002d00002103410021000340200220006a41ff006a413041372003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b02000b4601017f23808080800041106b22042480808080002004200136020c200420003602084100200441086a41b4f4c080002004410c6a41b4f4c080002002200310fb80808000000ba70401037f23808080800041206b22022480808080002002200141087122033a000702400240024020030d00200020013a00ca0120002d00c9012104200020002d00c801220341016a3a00c9010240200341c7014b0d00200020036a220320032d00002004733a0000200020002d00c80141016a22033a00c80102400240200341ff0171220341a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002103200041003b01c8010c010b200341c7014b0d010b200020036a220320032d00002001733a0000200020002d00c80141016a22033a00c801024002400240200341ff017141a601470d00200020002d00a60120002d00c901733a00a60141840121030c010b2001412471450d01200341ff01712203450d01200341c7014b0d04200020036a220320032d000020002d00c901733a000020002d00c80141016a41ff0171220341c8014f0d05200020036a220320032d00004104733a000041800121030b200020002d00a7012003733a00a70120004118109c82808000200041003b01c8010b200241206a2480808080000f0b200341c80141d4f5c0800010f980808000000b20024200370214200241b4f4c080003602102002410136020c200241b4f6c08000360208200241076a41bcf6c08000200241086a41c0f6c0800010a482808000000b200341c80141b4f5c0800010f980808000000b200341c80141c4f5c0800010f980808000000b890d01027f23808080800041a0036b2203248080808000200341002f00a4f5c080003b0104200341002800a0f5c08000360200200341126a410041b601108a8e8080001a2003410e6a41002800aef5c08000360100200341002900a6f5c0800037010620034118109c82808000200341d0016a200341c80110848e8080001a200341003a009a03200341003b019803200341d0016a411210a582808000024020032d009803220441c7014b0d00200341d0016a20046a220420042d000041cd00733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d000041e500733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d000041f200733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d000041ec00733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d000041e900733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d000041ee00733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d00004120733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d000041f600733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d00004131733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d0000412e733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d00004130733a0000200320032d00980341016a22043a0098030240200441ff017141a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c82808000200341003b0198030b2003200341d0016a41d00110848e808000220341a0f7c0800041072001200210a7828080002000200341d00110848e8080001a200341a0036a2480808080000f0b200441c80141d4f5c0800010f980808000000b8c0901027f23808080800041f0006b22052480808080002000411210a582808000024002400240024002402002450d0020002d00c80121060340200641ff0171220641c7014b0d02200020066a220620062d000020012d0000733a0000200020002d00c80141016a22063a00c8010240200641ff017141a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010b200141016a21012002417f6a22020d000b0b200541123a000720002d00ca014112470d0120002d00c801220641c7014b0d02200020066a220620062d00002004733a0000200020002d00c80141016a22063a00c80102400240200641ff0171220641a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010c010b200641c7014b0d030b200020066a220620062d00002004410876733a0000200020002d00c80141016a22063a00c80102400240200641ff0171220641a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010c010b200641c7014b0d030b200020066a220620062d00002004411076733a0000200020002d00c80141016a22063a00c80102400240200641ff0171220641a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010c010b200641c7014b0d030b200020066a220620062d00002004411876733a0000200020002d00c80141016a22063a00c8010240200641ff017141a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c82808000200041003b01c8010b2000410210a58280800002402004450d0020002d00c80121060340200641ff0171220641c7014b0d05200020066a220620062d000020032d0000733a0000200020002d00c80141016a22063a00c8010240200641ff017141a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010b200341016a21032004417f6a22040d000b0b200541f0006a2480808080000f0b200641c80141d4f5c0800010f980808000000b2005412c6a41c280808000360200200541c2808080003602242005200041ca016a22003602202005200541076a360228200541ec006a41033a0000200541e8006a4104360200200541e0006a42a080808010370200200541d8006a41023602002005410236021c200541023602142005410236020c20054180f7c0800036020820054102360250200541033a004c200541043602482005422037024020054102360238200541023602302005200541306a3602182005200541206a3602102000200541076a200541086a4190f7c0800010a482808000000b200641c80141d4f5c0800010f980808000000b200641c80141d4f5c0800010f980808000000b910901027f23808080800041f0006b22052480808080002000411210a582808000024002400240024002402002450d0020002d00c80121060340200641ff0171220641c7014b0d02200020066a220620062d000020012d0000733a0000200020002d00c80141016a22063a00c8010240200641ff017141a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010b200141016a21012002417f6a22020d000b0b200541123a000720002d00ca014112470d0120002d00c801220641c7014b0d02200020066a220620062d00002004733a0000200020002d00c80141016a22063a00c80102400240200641ff0171220641a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010c010b200641c7014b0d030b200020066a220620062d00002004410876733a0000200020002d00c80141016a22063a00c80102400240200641ff0171220641a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010c010b200641c7014b0d030b200020066a220620062d00002004411076733a0000200020002d00c80141016a22063a00c80102400240200641ff0171220641a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010c010b200641c7014b0d030b200020066a220620062d00002004411876733a0000200020002d00c80141016a22063a00c8010240200641ff017141a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c82808000200041003b01c8010b2000410710a58280800002402004450d0020002d00c80121060340200641ff0171220641c8014f0d05200020066a22062d00002101200641003a0000200320013a0000200020002d00c80141016a22063a00c8010240200641ff017141a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010b200341016a21032004417f6a22040d000b0b200541f0006a2480808080000f0b200641c80141d4f5c0800010f980808000000b2005412c6a41c280808000360200200541c2808080003602242005200041ca016a22003602202005200541076a360228200541ec006a41033a0000200541e8006a4104360200200541e0006a42a080808010370200200541d8006a41023602002005410236021c200541023602142005410236020c20054180f7c0800036020820054102360250200541033a004c200541043602482005422037024020054102360238200541023602302005200541306a3602182005200541206a3602102000200541076a200541086a4190f7c0800010a482808000000b200641c80141d4f5c0800010f980808000000b200641c80141e4f5c0800010f980808000000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a10a982808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10a982808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000be10405027f017e037f017e017f23808080800041d0006b2201248080808000200141306a41a8f7c08000410741aff7c08000411641a8f7c08000410010d882808000200141086a41086a410036020020014280808080c0003703082001280230210220012902342103200141186a41086a2204410036020020014280808080c000370218200141186a410010aa82808000200141c0006a41086a2004280200220541016a2206360200200128021c200541246c6a220541003a00202005410336021c200541c5f7c080003602182005420437021020054200370208200542808080808001370200200120012902182207370340024020062007a7470d00200141c0006a200610aa82808000200128024821060b2004200641016a22083602002001280244200641246c6a220541013a00202005410436021c200541c8f7c080003602182005420437021020054200370208200542808080808001370200200120012903402207370318024020082007a72206470d00200141186a200810aa8280800020012802182106200128022021080b200128021c2204200841246c6a220541023a00202005410336021c200541ccf7c08000360218200542043702102005420037020820054280808080800137020002402002418080808078470d0041fcf7c08000411141f0f8c0800010a181808000000b200042808080808001370244200020023602382000200436020820002006360204200041013a000020002001290308370350200041cc006a41003602002000413c6a20033702002000200841016a36020c200041d8006a200141106a280200360200200141d0006a2480808080000bf20203027f017e047f23808080800041306b2201248080808000200141246a41cff7c08000410a41aff7c08000411641a8f7c08000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010ab82808000200128021022052001280214220641386c6a220742e987d8bed5a3d0fbfb00370308200742e2e68fceaa92ce9c7b3703002007420437022c2007420437022420074180f9c0800036022020074100360218200741c58080800036021002402004418080808078470d0041fcf7c08000411141f0f8c0800010a181808000000b200128020c2107200042808080808001370244200020043602382000200536020820002007360204200041003a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200641016a36020c200041d8006a2002280200360200200141306a2480808080000be70305027f017e027f017e027f23808080800041c0006b2201248080808000200141246a41d9f7c08000411341aff7c08000411641a8f7c08000410010d882808000200141086a410036020020014280808080c00037030020012802242102200129022821032001410c6a41086a2204410036020020014280808080c00037020c2001410c6a410010aa82808000200141306a41086a2004280200220441016a22053602002001280210200441246c6a220441003a00202004410836021c200441ecf7c0800036021820044204370210200442003702082004428080808080013702002001200129020c2206370330024020052006a72207470d00200141306a200510aa8280800020012802302107200128023821050b20012802342208200541246c6a220441013a00202004410836021c200441f4f7c08000360218200442043702102004420037020820044280808080800137020002402002418080808078470d0041fcf7c08000411141f0f8c0800010a181808000000b200042808080808001370244200020023602382000200836020820002007360204200041013a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200541016a36020c200041d8006a200141086a280200360200200141c0006a2480808080000bed0201037f2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000b860201017f024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d0020020d02410121010c030b20032802002103200241002802c8a3c68000118180808000002201450d0320012003200410848e8080001a200341002802c0a3c68000118080808000000c020b20020d00410121010c010b41002d00fca3c680001a200241002802c8a3c68000118180808000002201450d010b20002001360204200041086a2002360200200041003602000f0b20004101360204200041086a2002360200200041013602000f0b20004100360204200041086a2002360200200041013602000f0b20004100360204200041013602000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a10b082808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bc10601067f23808080800041306b220224808080800002400240024002400240024002402001280208220320012802102204460d0002400240024002400240200441016a2205450d0020032005490d0120012802042106200120053602100240024002400240200620046a2d000022074103710e0400020103000b200741027621040c070b200241096a20073a0000200241013a0008200220013602042002410036021c200241046a2002411c6a410410b3828080000d07200228021c220541808004490d07200541027621040c050b200241096a20073a0000200241013a000820022001360204200241003b011c200241046a2002411c6a410210b3828080000d0620022f011c2205418002490d06200541027621040c040b20074104490d020c050b417f20054190fbc08000109681808000000b200520034190fbc08000109581808000000b200320056b4104490d02200441056a21042005417b4b0d04200420034b0d0520012004360210200620056a2800002204418080808004490d020b20012802082103200128021021050b2002200536020020032005490d042001200320056b22033602082001200128020420056a220536020420014100360210200420034d0d01200041003602000c060b200041003602000c050b20032004460d03024020040d00200042003702082000419c88c080003602042000418489c080003602000c050b200241046a2001410c6a20052003200128020028020011868080800000200241046a41086a22052004360200200020022902043702002001200128020820046b3602082001200128020420046a360204200041086a20052902003702000c040b200520044190fbc08000109681808000000b200420034190fbc08000109581808000000b2002411c6a410c6a41c680808000360200200241046a410c6a420237020020024102360208200241acf9c08000360204200241c6808080003602202002200336022c20022002411c6a36020c20022002412c6a3602242002200236021c200241046a4198fac0800010f680808000000b200020012902003702002001419c88c080003602042001418489c08000360200200041086a200141086a2201290200370200200142003702000b200241306a2480808080000ba10201037f20002d00042103200041003a0004024002400240024002400240024020030d0041012103200028020022002802082204200028021022056b2002490d02200520026a22032005490d03200320044b0d042001200028020420056a200210848e8080001a200020033602100c010b2001200041056a2d00003a000041012103200028020022002802082204200028021022056b2002417f6a2202490d01200520026a22032005490d04200320044b0d05200141016a200028020420056a200210848e8080001a200020033602100b410021030b20030f0b200520034190fbc08000109681808000000b200320044190fbc08000109581808000000b200520034190fbc08000109681808000000b200320044190fbc08000109581808000000bc40301047f23808080800041106b2203248080808000024002400240024002400240200241046a2204450d002004417f4c0d0241002d00fca3c680001a200441002802c8a3c68000118180808000002205450d03200320053602082003200436020402402002413f4b0d00200520024102743a0000410121060c060b200241ffff004b0d0141022106200520024102744101723b00000c050b2003410036020c2003428080808010370204200341046a4100410110b1828080002003280208210520032802042104200328020c21060c030b41002106200241ffffffff034b0d0220052002410274410272360000410421060c030b10ae80808000000b4101200410b280808000000b200520066a41033a00002003200641016a220636020c0240200420066b41034b0d00200341046a2006410410b1828080002003280204210420032802082105200328020c21060b200520066a2002360000200641046a21060b2003200636020c0240200420066b20024f0d00200341046a2006200210b18280800020032802082105200328020c21060b200520066a2001200210848e8080001a200041086a200620026a36020020002003290204370200200341106a2480808080000b2100200128021441a0fbc08000410b200141186a28020028020c118280808000000b840303027f017e047f23808080800041306b2201248080808000200141246a41bbfbc08000410441acfbc08000410f41acfbc08000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010b782808000200128021022052001280214220641386c6a220742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db003703002007420437022c20074208370224200741c0fcc0800036022020074100360218200741c78080800036021002402004418080808078470d0041bffbc08000411141b0fcc0800010a181808000000b200128020c210720014288808080800137020c200142808080808001370214200041c4006a2001410c6a10b8828080002000413c6a2003370200200020043602382000200641016a36020c2000200536020820002007360204200041003a000020002001290300370350200041d8006a2002280200360200200141306a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10ba82808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710b98280800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10ba82808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000b7600200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c00037035020004120360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bda0303017f017e047f0240024020004280808080105a0d0020012102200021030c010b200141786a220220004280c2d72f8022034280bea8d00f7e20007ca722044190ce006e22054190ce0070220641ffff037141e4006e22074101744188d0c180006a2f00003b00002001417c6a200420054190ce006c6b220441ffff037141e4006e22054101744188d0c180006a2f00003b00002001417a6a2006200741e4006c6b41ffff03714101744188d0c180006a2f00003b00002001417e6a2004200541e4006c6b41ffff03714101744188d0c180006a2f00003b00000b024002402003a722014190ce004f0d00200121040c010b2002417c6a21020340200220014190ce006e220441f0b17f6c20016a220541e4006e22064101744188d0c180006a2f00003b0000200241026a2005200641e4006c6b4101744188d0c180006a2f00003b00002002417c6a2102200141ffc1d72f4b21052004210120050d000b200241046a21020b02400240200441e3004b0d00200421010c010b2002417e6a22022004200441ffff037141e4006e220141e4006c6b41ffff03714101744188d0c180006a2f00003b00000b0240200141094b0d002002417f6a200141306a3a00000f0b2002417e6a20014101744188d0c180006a2f00003b00000bb51509017f027e037f017e027f037e017f027e017f23808080800041a0026b22022480808080002000bd220342ffffffffffffff078321042003423488a7210541002106024020034200590d002001412d3a0000410121060b200541ff0f712105024002400240024002400240024020044200520d002005450d010b20044200522005410249722107200442808080808080800884200420051b22034202862104200342018321080240200541cb776a41cc7720051b2205417f4a0d0020024190026a41a8a7c1800020054185a2536c4114762005417f476b220920056a220a41047422056b290300220342002004420284220b420010878e80800020024180026a41b0a7c1800020056b290300220c4200200b420010878e808000200241f0016a20024190026a41086a290300220b2002290380027c220d20024180026a41086a290300200d200b54ad7c2009200a41b1d9b51f6c4113766b413c6a41ff0071220510fc8d808000200241b0016a2003420020042007ad427f857c220b420010878e808000200241a0016a200c4200200b420010878e80800020024190016a200241b0016a41086a290300220b20022903a0017c220d200241a0016a41086a290300200d200b54ad7c200510fc8d808000200241e0016a200342002004420010878e808000200241d0016a200c42002004420010878e808000200241c0016a200241e0016a41086a290300220320022903d0017c220c200241d0016a41086a290300200c200354ad7c200510fc8d80800020022903c001210b200229039001210d20022903f001210c024020094102490d002009413e4b0d032004427f2009ad86427f858350450d030c040b200c20087d210c200720085071210e410121090c040b20024180016a200541c1e8046c411276200541034b6b220a410474220941c8fcc080006a290300220c420020044202842203420010878e808000200241f0006a200941d0fcc080006a290300220b42002003420010878e808000200241e0006a20024180016a41086a290300220d20022903707c220f200241f0006a41086a290300200f200d54ad7c200a20056b200a41cfa6ca006c4113766a413d6a41ff0071220510fc8d808000200241206a200c420020042007ad2210427f857c220d420010878e808000200241106a200b4200200d420010878e8080002002200241206a41086a290300220d20022903107c220f200241106a41086a290300200f200d54ad7c200510fc8d808000200241d0006a200c42002004420010878e808000200241c0006a200b42002004420010878e808000200241306a200241d0006a41086a290300220c20022903407c220b200241c0006a41086a290300200b200c54ad7c200510fc8d8080002002290330210b2002290300210d2002290360210c200a41164f0d0102402004420580a7417b6c41002004a76b470d00417f210503402004a72107200541016a210520044205802204a7417b6c410020076b460d000b2005200a4f0d030c020b02402008500d00417f210503402003a72107200541016a210520034205802203a7417b6c410020076b460d000b200c2005200a4fad7d210c0c020b2010427f8520047c2104417f210503402004a72107200541016a210520044205802204a7417b6c410020076b460d000b2005200a490d014101210e410021090c030b200120066a220541002f00d0d1c180003b0000200541026a41002d00d2d1c180003a00002003423f88a741036a21050c040b4100210702400240200c42e400802203200d42e40080220f560d0041002105200d210f200c2103200b21040c010b200b42e400802204a7419c7f6c200ba76a41314b2107410221050b02402003420a802203200f420a80220c580d000340200541016a21052004220b420a8021042003420a802203200c220f420a80220c560d000b2004a741766c200ba76a41044b21070b2004200f5120077221070c020b4100210e410121090b4100210702400240200c420a802204200d420a80220f560d0041002105200d2103200b210c0c010b41002105410021070340200e200f2203a741766c4100200da76b4671210e200541016a21052009200741ff017145712109200b420a80220ca741766c200ba76a2107200c210b2003210d2004420a8022042003420a80220f560d000b0b024002400240200e450d002003420a80220ba741766c41002003a76b460d010b200c21040c010b0340200ba72111200541016a21052009200741ff017145712109200c420a802204a741766c200ca76a2107200b2103200b420a80220d210b2004210c200da741766c410020116b460d000b0b2008420052200e417f7372200420035171410441052004420183501b2007200741ff01714105461b200720091b41ff017141044b7221070b2005200a6a210941112105024020042007ad7c220442ffff83fea6dee111560d0041102105200442ffff99a6eaafe301560d00410f2105200442ffffe883b1de16560d00410e2105200442ffbfcaf384a302560d00410d2105200442ff9f94a58d1d560d00410c2105200442ffcfdbc3f402560d00410b2105200442ffc7afa025560d00410a2105200442ff93ebdc03560d0041092105200442ffc1d72f560d0041082105200442fface204560d0041072105200442bf843d560d00410621052004429f8d06560d00410521052004428fce00560d0041042105200442e707560d0041032105200442e300560d004102410120044209561b21050b200520096a2107024002400240024002400240024002400240024020094100480d0020074111480d010b2007417f6a22094110490d01200741046a4105490d02200620016a221141016a210e20054101470d05200e41e5003a000020112004a741306a3a00002001200641027222066a210e20094100480d03200921050c040b20042001200520066a6a220e10bc828080000240200520074e0d00200e41302009108a8e8080001a0b2001200720066a22056a41aee0003b0000200541026a21050c080b200420012005200641016a22096a22056a10bc82808000200120066a200120096a200710fe8d8080001a2001200720066a6a412e3a00000c070b200120066a220e41b0dc003b0000410220076b210902402007417f4a0d00200e41026a413020094103200941034a1b417e6a108a8e8080001a0b20042001200520066a20096a22056a10bc828080000c060b200e412d3a0000410120076b2105200e41016a210e0b200541e3004a0d010240200541094a0d00200e200541306a3a00002009411f7641016a20066a21050c050b200e20054101744188d0c180006a2f00003b00002009411f7641027220066a21050c040b2004200520066a220520016a41016a220610bc828080002011200e2d00003a0000200e412e3a0000200641e5003a00002001200541026a22066a210e20094100480d01200921050c020b200e200541e4006e220741306a3a0000200e2005200741e4006c6b4101744188d0c180006a2f00003b00012009411f7641036a20066a21050c020b200e412d3a0000410120076b2105200e41016a210e0b0240200541e3004a0d000240200541094a0d00200e200541306a3a00002009411f7641016a20066a21050c020b200e20054101744188d0c180006a2f00003b00002009411f7641027220066a21050c010b200e200541e4006e220741306a3a0000200e2005200741e4006c6b4101744188d0c180006a2f00003b00012009411f7641036a20066a21050b200241a0026a24808080800020050b8414030b7f027e057f0240024002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022062f01e2012207410b490d004101210841042107200128020822094105490d03200921072009417b6a0e020302010b2006200128020822094104746a210a2001280204210b200941016a220120074d0d03200a2002370300200a20033703080c040b200941796a210941002108410621070c010b4100210841052107410021090b2001280204210a41002d00fca3c680001a41e80141002802c8a3c6800011818080800000220c450d05200c41003b01e201200c41003602b001200c20062f01e201220d2007417f736a22013b01e2012001410c4f0d06200d200741016a220e6b2001470d07200641b4016a220d20074102746a280200210f200620074104746a22102903002111201041086a2903002112200c2006200e4104746a200141047410848e808000221041b4016a200d200e4102746a200141027410848e8080001a200620073b01e2012006201020081b221320094104746a210120132f01e201220720094b0d0220012002370300200120033703080c030b200620014104746a200a200720096b220841047410fe8d8080001a200a2003370308200a2002370300200641b4016a220a20014102746a200a20094102746a200841027410fe8d8080001a0b200620094102746a41b4016a20043602002006200741016a3b01e2010c020b2013200941016a220e4104746a2001200720096b220d41047410fe8d8080001a2001200337030820012002370300201341b4016a2201200e4102746a200120094102746a200d41027410fe8d8080001a0b200a410020081b210b201320094102746a41b4016a20043602002013200741016a3b01e20102400240024020062802b00122010d00410021100c010b410021100340200621072012210320112102200f2114200c211520012106200a2010470d0720072f01e001210102400240024020062f01e2012210410b490d004101210e200141054f0d012001210d410421010c020b200141016a2107201041016a2104200620014104746a210a0240024020012010490d00200a2002370300200a2003370308200620014102746a41b4016a20143602000c010b200620074104746a200a201020016b220841047410fe8d8080001a200a2003370308200a2002370300200641b4016a220a2007410274220e6a200a2001410274220d6a220a2008410274220810fe8d8080001a200a2014360200200d200641e8016a220a6a41086a200a200e6a200810fe8d8080001a0b200620043b01e201200620074102746a41e8016a20153602002007201041026a22084f0d040240201020016b220e41016a410371220a450d00200620014102746a41ec016a210103402001280200220420073b01e001200420063602b001200141046a2101200741016a2107200a417f6a220a0d000b0b200e4103490d04200741027420066a41f4016a21010340200141746a280200220a20073b01e001200a20063602b001200141786a280200220a200741016a3b01e001200a20063602b0012001417c6a280200220a200741026a3b01e001200a20063602b0012001280200220a200741036a3b01e001200a20063602b001200141106a21012008200741046a2207470d000c050b0b2001210d024002402001417b6a0e020201000b200141796a210d4100210e410621010c010b4100210e410521014100210d0b41002d00fca3c680001a41980241002802c8a3c6800011818080800000220c450d08200c41003b01e201200c41003602b001200c20062f01e20122042001417f736a22073b01e2012007410c4f0d092004200141016a22086b2007470d0a200641b4016a221620014102746a280200210f200620014104746a22042903002111200441086a2903002112200c200620084104746a200741047410848e808000220441b4016a2016200841027422176a200741027410848e8080001a200620013b01e20120042f01e201220741016a21082007410c4f0d0b201020016b22012008470d0c200a41016a2110200441e8016a200620176a41e8016a200141027410848e80800021084100210102400340200820014102746a280200220a20013b01e001200a20043602b001200120074f0d01200120012007496a220120074d0d000b0b20062004200e1b2207200d4104746a210402400240200d41016a220120072f01e201220a4d0d0020042002370300200420033703080c010b200720014104746a2004200a200d6b220841047410fe8d8080001a2004200337030820042002370300200741b4016a220420014102746a2004200d4102746a200841027410fe8d8080001a0b200a41016a21082007200d4102746a221641b4016a2014360200200741e8016a21040240200d41026a2214200a41026a220e4f0d00200420144102746a200420014102746a200a200d6b41027410fe8d8080001a0b200420014102746a2015360200200720083b01e20102402001200e4f0d000240200a200d6b220d41016a4103712204450d00201641ec016a210a0340200a280200220820013b01e001200820073602b001200a41046a210a200141016a21012004417f6a22040d000b0b200d4103490d00200720014102746a41f4016a210a0340200a41746a280200220420013b01e001200420073602b001200a41786a2802002204200141016a3b01e001200420073602b001200a417c6a2802002204200141026a3b01e001200420073602b001200a2802002204200141036a3b01e001200420073602b001200a41106a210a200e200141046a2201470d000b0b2010210a20062802b00122010d000b0b20052802002207280200220a450d0b2007280204210441002d00fca3c680001a41980241002802c8a3c68000118180808000002201450d0c2001200a3602e801200141003b01e201200141003602b00120072001360200200a41003b01e001200a20013602b0012007200441016a36020420042010470d0d20012f01e2012207410b4f0d0e2001200741016a220a3b01e201200120074104746a2204201237030820042011370300200141e8016a200a4102746a200c360200200120074102746a41b4016a200f360200200c200a3b01e001200c20013602b0010b201321060b200020093602082000200b360204200020063602000f0b410841e80110b280808000000b2001410b4190d5c18000109581808000000b41d8d4c1800041284180d5c1800010f880808000000b41b0d5c18000413541e8d5c1800010f880808000000b410841980210b280808000000b2007410b4190d5c18000109581808000000b41d8d4c1800041284180d5c1800010f880808000000b2008410c41a0d5c18000109581808000000b41d8d4c1800041284180d5c1800010f880808000000b41d8d2c1800010a081808000000b410841980210b280808000000b4188d4c18000413041b8d4c1800010f880808000000b41e8d2c18000412041c8d4c1800010f880808000000bf51f011a7f23808080800041c0016b220524808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022062f01ee042207410b490d0041012108410421092001280208220a4105490d03200a2109200a417b6a0e020302010b200641046a22092001280208220a4102746a210b2001280204210c02400240200a41016a220120074d0d00200b20023602000c010b200920014102746a200b2007200a6b220941027410fe8d8080001a200b2002360200200641306a220b200141346c6a200b200a41346c6a200941346c10fe8d8080001a0b2006200a41346c6a220141e0006a200341306a280200360200200141d8006a200341286a290200370200200141d0006a200341206a290200370200200141c8006a200341186a290200370200200141c0006a200341106a290200370200200141386a200341086a290200370200200141306a20032902003702002006200741016a3b01ee040c030b200a41796a210a41002108410621090c010b41002108410521094100210a0b2001280204210b41002d00fca3c680001a41f00441002802c8a3c68000118180808000002207450d03200741003b01ee0420074100360200200720062f01ee04220d2009417f736a220e3b01ee0420054198016a200641306a220f200941346c6a2201410c6a290200370300200541a0016a200141146a290200370300200541a8016a2001411c6a290200370300200541b0016a200141246a290200370300200541b8016a2001412c6a2902003703002005200129020437039001200e410c4f0d04200d200941016a22106b200e470d0520012802002111200641046a220120094102746a2802002112200741046a200120104102746a200e41027410848e8080001a200741306a200f201041346c6a200e41346c10848e8080001a200620093b01ee04200541e0006a41086a20054190016a41086a290300370300200541e0006a41106a20054190016a41106a290300370300200541e0006a41186a20054190016a41186a290300370300200541e0006a41206a20054190016a41206a290300370300200541e0006a41286a20054190016a41286a29030037030020052005290390013703602006200720081b221341046a200a4102746a21010240024020132f01ee042209200a4b0d00200120023602000c010b200141046a20012009200a6b220e41027410fe8d8080001a200120023602002013200a41346c6a220141e4006a200141306a200e41346c10fe8d8080001a0b200b410020081b210c2013200a41346c6a220141e0006a200341306a280200360200200141d8006a200341286a290200370200200141d0006a200341206a290200370200200141c8006a200341186a290200370200200141c0006a200341106a290200370200200141386a200341086a290200370200200141306a2003290200370200200541086a2203200541e0006a41086a290300370300200541106a2201200541e0006a41106a290300370300200541186a220e200541e0006a41186a290300370300200541206a2202200541e0006a41206a290300370300200541286a2208200541e0006a41286a2903003703002013200941016a3b01ee04200520052903603703002011418080808078470d01201321060b2000200a3602082000200c360204200020063602000c010b200541306a41286a2008290300370300200541306a41206a2002290300370300200541306a41186a200e290300370300200541306a41106a2001290300370300200541306a41086a200329030037030020052005290300370330024002400240200628020022030d00410021140c010b20054190016a41086a210820054190016a41106a211020054190016a41186a210d20054190016a41206a210f20054190016a41286a21154100211420072116201221172011211803402003210e200b2014470d0720062f01ec042106024002400240200e2f01ee042214410b490d0041012119200641054f0d0120062109410421060c020b200e41046a220b200641027422096a2101200641016a2103201441016a21070240024020062014490d0020012017360200200e200641346c6a220141306a2018360200200141346a20052903303702002001413c6a200541386a290300370200200141c4006a200541c0006a290300370200200141cc006a200541c8006a290300370200200141d4006a200541d0006a290300370200200141dc006a200541d8006a2903003702000c010b200b200341027422026a2001201420066b220b410274220810fe8d8080001a20012017360200200e41306a2201200341346c6a2001200641346c6a2201200b41346c10fe8d8080001a20012018360200200120052903303702042001410c6a200541306a41086a290300370200200141146a200541c0006a2903003702002001411c6a200541c8006a290300370200200141246a200541d0006a2903003702002001412c6a200541d8006a2903003702002009200e41f0046a22016a41086a200120026a200810fe8d8080001a0b200e20073b01ee04200e20034102746a41f0046a20163602002003201441026a220b4f0d040240201420066b220941016a4103712201450d00200e20064102746a41f4046a210603402006280200220720033b01ec042007200e360200200641046a2106200341016a21032001417f6a22010d000b0b20094103490d042003410274200e6a41fc046a21060340200641746a280200220120033b01ec042001200e360200200641786a2802002201200341016a3b01ec042001200e3602002006417c6a2802002201200341026a3b01ec042001200e36020020062802002201200341036a3b01ec042001200e360200200641106a2106200b200341046a2203470d000c050b0b20062109024002402006417b6a0e020201000b200641796a210941002119410621060c010b4100211941052106410021090b41002d00fca3c680001a41a00541002802c8a3c68000118180808000002207450d08200741003b01ee04200741003602002007200e2f01ee0422112006417f736a22013b01ee042008200e41306a221a200641346c6a2203410c6a2902003703002010200341146a290200370300200d2003411c6a290200370300200f200341246a29020037030020152003412c6a29020037030020052003290204370390012001410c4f0d092011200641016a22026b2001470d0a20032802002111200e41046a220320064102746a2802002112200741046a20032002410274221b6a200141027410848e8080001a200741306a201a200241346c6a200141346c10848e8080001a200e20063b01ee04200541e0006a41086a22022008290300370300200541e0006a41106a221a2010290300370300200541e0006a41186a221c200d290300370300200541e0006a41206a221d200f290300370300200541e0006a41286a221e2015290300370300200520052903900137036020072f01ee04220341016a21012003410c4f0d0b201420066b22062001470d0c200b41016a2114200741f0046a200e201b6a41f0046a200641027410848e808000210b4100210602400340200b20064102746a280200220120063b01ec0420012007360200200620034f0d01200620062003496a220620034d0d000b0b2015201e290300370300200f201d290300370300200d201c2903003703002010201a290300370300200820022903003703002005200529036037039001200e200720191b220141046a22022009410274221e6a210302400240200941016a220620012f01ee04220b4d0d00200320173602000c010b200220064102746a2003200b20096b220241027410fe8d8080001a20032017360200200141306a2203200641346c6a2003200941346c6a200241346c10fe8d8080001a0b200b41016a21192001200941346c6a220341306a2018360200200341346a20052903303702002003413c6a200541306a41086a2218290300370200200341c4006a200541306a41106a2217290300370200200341cc006a200541306a41186a221a290300370200200341d4006a200541306a41206a221b290300370200200341dc006a200541306a41286a221c290300370200200141f0046a21030240200941026a221d200b41026a22024f0d002003201d4102746a200320064102746a200b20096b41027410fe8d8080001a0b200320064102746a2016360200200120193b01ee040240200620024f0d000240200b20096b221941016a410371220b450d002001201e6a41f4046a210303402003280200220920063b01ec0420092001360200200341046a2103200641016a2106200b417f6a220b0d000b0b20194103490d00200120064102746a41fc046a21030340200341746a280200220b20063b01ec04200b2001360200200341786a280200220b200641016a3b01ec04200b20013602002003417c6a280200220b200641026a3b01ec04200b20013602002003280200220b200641036a3b01ec04200b2001360200200341106a21032002200641046a2206470d000b0b200541286a22062015290300370300200541206a2203200f290300370300200541186a2201200d290300370300200541106a220b2010290300370300200541086a2209200829030037030020052005290390013703002011418080808078460d02201c2006290300370300201b2003290300370300201a20012903003703002017200b290300370300201820092903003703002005200529030037033020072116201221172014210b200e210620112118200e28020022030d000b0b200428020022032802002201450d0b2003280204210b41002d00fca3c680001a41a00541002802c8a3c68000118180808000002206450d0c200620013602f004200641003b01ee042006410036020020032006360200200141003b01ec04200120063602002003200b41016a360204200b2014470d0d20062f01ee042201410b4f0d0e2006200141016a220b3b01ee042006200141346c6a220341346a20052903303702002003413c6a200541386a290300370200200341c4006a200541c0006a290300370200200341cc006a200541c8006a290300370200200341d4006a200541d0006a290300370200200341dc006a200541d8006a290300370200200341306a2011360200200620014102746a41046a2012360200200641f0046a200b4102746a20073602002007200b3b01ec04200720063602000b2000200a3602082000200c360204200020133602000b200541c0016a2480808080000f0b410441f00410b280808000000b200e410b4190d5c18000109581808000000b41d8d4c1800041284180d5c1800010f880808000000b41b0d5c18000413541e8d5c1800010f880808000000b410441a00510b280808000000b2001410b4190d5c18000109581808000000b41d8d4c1800041284180d5c1800010f880808000000b2001410c41a0d5c18000109581808000000b41d8d4c1800041284180d5c1800010f880808000000b41d8d2c1800010a081808000000b410441a00510b280808000000b4188d4c18000413041b8d4c1800010f880808000000b41e8d2c18000412041c8d4c1800010f880808000000bc909030f7f017e057f4100210202400240024002400240024002402001280200220341024622040d00024020012d00490d004100200120041b210520012802302106024002400240024020030d002001410e6a2d00000d032001410c6a2d00002107200141346a2802002204210802400240024020012802042203450d0002400240200420034b0d0020042003460d010c030b200620036a2c00004140480d020b200420036b21080b2008450d0302400240200620036a22092c00002208417f4a0d0020092d0001413f71210a2008411f71210b0240200841604f0d00200b410674200a7221080c020b200a41067420092d0002413f7172210a0240200841704f0d00200a200b410c747221080c020b200a41067420092d0003413f7172200b411274418080f000717221080c010b200841ff017121080b200741ff01710d0a2008418080c400460d014101210702402008418001490d00410221072008418010490d0041034104200841808004491b21070b2001200720036a22033602042003450d0902400240200420034b0d0020042003470d010c0a0b200620036a2c000041bf7f4a0d090b410121070b200120074101733a000c200620042003200441b0d8c18000109781808000000b200141013a000c0c020b02402001411c6a28020022082001413c6a280200220c417f6a220d6a2203200141346a28020022094f0d002001280238210a200c200141186a280200220e6b210f200141106a280200211020012903082111200141246a2802002212417f46211320122114034002400240024002402011200620036a31000088a74101710d0020012008200c6a220836021c0c010b201020102014201020144b1b20131b2215200c2015200c4b1b210b200620086a21162015210302400240024003400240200b2003470d004100201420131b2107201021030340024020072003490d0020012008200c6a220336021c2012417f460d15200141003602240c150b2003417f6a2203200c4f0d05200320086a220420094f0d03200a20036a2d0000200620046a2d0000460d000b20012008200e6a220836021c200f21032013450d060c070b200820036a20094f0d02201620036a2104200a20036a2107200341016a210320072d000020042d0000460d000b200820106b20036a21080c030b200420094190d8c1800010f980808000000b2009201520086a2203200920034b1b200941a0d8c1800010f980808000000b2003200c4180d8c1800010f980808000000b4100210320130d010b20012003360224200321140b2008200d6a22032009490d000b0b2001200936021c0c020b200120074101733a000c20032108200741ff01710d080b200141013a000e0b200141013a00490240024020052d0048450d0020052802442103200528024021040c010b2005280244220320052802402204460d010b200320046b2103200620046a21020c070b200141023602000b024020012802500d000c060b200141d4006a2203280200210220034100360200200141d8006a28020021030c050b200420036b21040b024020040d00410021040c020b41012107200620036a2c00002204417f4a0d0020044160491a0b200741017321040b200120043a000c200321080b2005280240210120052003360240200820016b2103200620016a21020b20002003360204200020023602000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710cb8280800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041306b2202248080808000200128020c2203200128020422046b220541386e2106200128021021072001280208210820012802002109024002400240024020032004470d00410421010c010b200541c8ffffff7d4b0d0120064105742205417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002201450d020b200241046a41086a220541003602002002200136020820022006360204200220073602202002200336021c2002200836021820022004360214200220093602102002200136022c2002410036022820022005360224200241106a200241246a10ce82808000200041086a200528020036020020002002290204370200200241306a2480808080000f0b10ae80808000000b4104200510b280808000000bbd05030a7f027e027f23808080800041c0006b220224808080800020012802082103200128020c220421052001280200220621070240200128020422082004460d0020012802102105200621070240034020082209280200220a418080808078460d01200928020c210b2009290218210c2009290210210d20092d0020210e200928020421082009280208210f2002200536023c20022008200f41386c6a3602382002200a360234200220083602302002200836022c200241086a2002412c6a10c282808000200241086a41206a2208200e3a000020072002290308370200200741106a200d370200200741186a200c3702002002200b41ffffffff0171360214200741086a200241086a41086a290300370200200741206a2008280200360200200741246a2107200941246a22082004470d000b0b200941246a21050b20014284808080c00037020020014280808080c000370208200420056b41246e2101024020042005460d004100210e034002402005200e41246c6a220b280208220a450d00200b2802042104200a410171210f410021080240200a4101460d00200441e4006a2109200a417e71210a4100210803400240200941446a280200450d00200941486a28020041002802c0a3c68000118080808000000b02402009417c6a280200450d00200928020041002802c0a3c68000118080808000000b200941f0006a2109200a200841026a2208470d000b0b200f450d002004200841386c6a2209280228450d00200941286a28020441002802c0a3c68000118080808000000b0240200b280200450d00200b28020441002802c0a3c68000118080808000000b0240200b28020c450d00200b410c6a28020441002802c0a3c68000118080808000000b200e41016a220e2001470d000b0b200020063602042000200720066b41246e3602082000200341246c41246e360200200241c0006a2480808080000b9d03010e7f02400240024002400240200128020822020d00410421030c010b200241ffffff1f4b0d0220024105742204417f4c0d022001280204210141002d00fca3c680001a200441002802c8a3c68000118180808000002203450d014100210520022106034020042005460d012001411c6a2802002107200141106a2802002108200128020421092001280218210a2001280214210b200128020c210c024002402001280208220d0d004104210e4100210f0c010b200d41ffffffff004b0d04200d410374220f417f4c0d0441002d00fca3c680001a200f41002802c8a3c6800011818080800000220e450d050b200141206a2101200e2009200f10848e8080002109200320056a220f200d360200200f411c6a2007360200200f41186a200a360200200f41146a200b360200200f41106a2008360200200f410c6a200c360200200f41086a200d360200200f41046a2009360200200541206a21052006417f6a22060d000b0b2000200236020820002003360204200020023602000f0b4104200410b280808000000b10ae80808000000b4104200f10b280808000000b860601177f0240024002400240024002400240200128020822020d00410421030c010b200241e3f1b81c4b0d04200241246c2204417f4c0d042001280204210541002d00fca3c680001a200441002802c8a3c68000118180808000002203450d012005200241246c6a21062002210741002108034020052006460d012005411c6a28020021092005280218210a4104210b4104210c02402005280208220d450d00200d41ffffff1f4b0d06200d410574220e417f4c0d062005280204210141002d00fca3c680001a200e41002802c8a3c6800011818080800000220c450d044100210f200d21100340200e200f460d012001411c6a2802002111200141106a2802002112200128020421132001280218211420012802142115200128020c211602400240200128020822170d0041042118410021040c010b201741ffffffff004b0d0820174103742204417f4c0d0841002d00fca3c680001a200441002802c8a3c68000118180808000002218450d070b200141206a210120182013200410848e8080002113200c200f6a220420173602002004411c6a2011360200200441186a2014360200200441146a2015360200200441106a20123602002004410c6a2016360200200441086a2017360200200441046a2013360200200f41206a210f2010417f6a22100d000b0b200541106a280200211720052d0020210f02400240200541146a28020022040d00410021010c010b200441ffffffff004b0d0620044103742201417f4c0d0641002d00fca3c680001a200141002802c8a3c6800011818080800000220b450d070b200541246a2105200b2017200110848e80800021172003200841246c6a2201200f3a00202001200936021c2001200a36021820012004360214200120173602102001200436020c2001200d3602082001200c3602042001200d360200200841016a21082007417f6a22070d000b0b2000200236020820002003360204200020023602000f0b4104200410b280808000000b4104200e10b280808000000b4104200410b280808000000b10ae80808000000b4104200110b280808000000b880301067f2380808080004190016b2202248080808000200241186a200110c082808000024002400240200228021822030d002000410036020820004280808080c0003702000c010b200228021c210441002d00fca3c680001a412041002802c8a3c68000118180808000002205450d0120052003360200200520043602042002410136022c2002200536022820024104360224200241306a200141e00010848e8080001a200241106a200241306a10c082808000024020022802102204450d0020022802142106410c2103410121010340024020012002280224470d00200241246a2001410241012002280284011b41012002280280011b10cd82808000200228022821050b200520036a220720063602002007417c6a20043602002002200141016a220136022c200341086a2103200241086a200241306a10c082808000200228020c2106200228020822040d000b0b20002002290224370200200041086a200241246a41086a2802003602000b20024190016a2480808080000f0b4104412010b280808000000bcc0401077f2380808080004190016b2202248080808000200241106a200141086a10c0828080000240024002400240024020022802102203450d002002280214210420012802042205450d0220012802002106200541047421050340024020062802042004470d0020032006280200200410888e808000450d030b200641106a2106200541706a22050d000c030b0b2000410036020820004280808080c0003702000c020b2006410c6a2802002104200641086a28020021030b41002d00fca3c680001a412041002802c8a3c68000118180808000002207450d01200720043602042007200336020020024101360224200220073602202002410436021c200241286a200141e80010848e8080001a200241086a200241286a41086a220810c082808000024020022802082203450d00200228020c21044101210103400240200228022c2205450d00200228022821062005410474210502400340024020062802042004470d0020032006280200200410888e808000450d020b200641106a2106200541706a22050d000c020b0b2006410c6a2802002104200641086a28020021030b02402001200228021c470d002002411c6a2001410241012002280284011b41012002280280011b10cd82808000200228022021070b200720014103746a22062004360204200620033602002002200141016a22013602242002200810c08280800020022802042104200228020022030d000b0b2000200229021c370200200041086a2002411c6a41086a2802003602000b20024190016a2480808080000f0b4104412010b280808000000bbf0801117f23808080800041d0006b22022480808080002002200110d08280800002400240024002400240024020022802002203450d0020032802002103200241186a200228020410cf828080002002200336024c2002280218418080808078470d010b2000410036020820004280808080c0003702000c010b2001280220220441016a2203417f20031b22034104200341044b1b22034192c9a4124b0d03200341386c2205417f4c0d0341002d00fca3c680001a200541002802c8a3c68000118180808000002206450d0220062002290218370200200641306a200241186a41306a2207290200370200200641286a200241186a41286a2208290200370200200641206a200241186a41206a2209290200370200200641186a200241186a41186a220a290200370200200641106a200241186a41106a220b290200370200200641086a200241186a41086a220c29020037020020024101360214200220063602102002200336020c02402004450d00200128020c210d20012802082103200128020421052001280200210e4101210f0340024002400240200e450d002005450d010b200e0d0141f8dac1800010a081808000000b4101210e0240200d450d00200d21050240200d4107712201450d0003402005417f6a210520032802f00421032001417f6a22010d000b0b200d4108490d00034020032802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042103200541786a22050d000b0b4100210d20032105410021030b0240200d20052f01ee04490d00034020052802002201450d05200341016a210320052f01ec04210d20012105200d20012f01ee044f0d000b0b200d41016a21100240024020030d00200521010c010b200520104102746a41f0046a2802002101410021102003417f6a2211450d002003417e6a2112024020114107712203450d0003402011417f6a211120012802f00421012003417f6a22030d000b0b20124107490d00034020012802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042101201141786a22110d000b0b2005200d4102746a41046a2802002103200241186a2005200d41346c6a41306a10cf828080002002200336024c2002280218418080808078460d012004417f6a21040240200f200228020c470d002002410c6a200f200441016a2203417f20031b10cc82808000200228021021060b2006200f41386c6a22032002290218370200200341306a2007290200370200200341286a2008290200370200200341206a2009290200370200200341186a200a290200370200200341106a200b290200370200200341086a200c2902003702002002200f41016a220f36021441002103200121052010210d20040d000b0b2000200229020c370200200041086a2002410c6a41086a2802003602000b200241d0006a2480808080000f0b41e8dac1800010a081808000000b4104200510b280808000000b10ae80808000000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bee0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b22014104742104200141808080c0004941037421050240024020030d00200241003602180c010b200241083602182002200341047436021c200220002802043602140b200241086a20052004200241146a10c982808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10c982808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941027421050240024020010d00200341003602180c010b200341043602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10c982808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bf00101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b2202410374210420024180808080014941027421050240024020010d00200341003602180c010b200341043602182003200141037436021c200320002802043602140b200341086a20052004200341146a10c982808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b9505020c7f037e23808080800041c0006b22022480808080002001280204210320012802002104200028020821052000280200210602400240024020002802042207200028020c2208460d00200241086a412c6a21092000280210210a200128020820034105746a21014100210b03402007200b6a220041286a280200220c418080808078460d02200241086a41206a220d200041206a290300370300200241086a41186a200041186a290300220e370300200241086a41106a200041106a290300370300200241086a41086a200041086a2903003703002002200029030037030820092000412c6a290200370200200941086a200041346a2802003602002002200c360230200a200241086a10dd828080002100200d290300210f2002280230210c20022902342110200141146a2000360200200141186a200f3702002001410c6a200e370200200141046a20103702002001200c41ffffffff0171360200200141206a2101200341016a21032007200b41386a220b6a2008470d000b0b200420033602000c010b20042003360200200041386a2008460d00200820076b200b6b220341486a220c41386e4101712109410021010240200341907f6a4138490d002000419c016a2100200c41f0006e41017421034100210103400240200041446a280200450d00200041486a28020041002802c0a3c68000118080808000000b02402000417c6a280200450d00200028020041002802c0a3c68000118080808000000b200041f0006a21002003200141026a2201470d000b0b2009450d002007200141386c6a200b6a220041e0006a280200450d00200041e4006a28020041002802c0a3c68000118080808000000b02402005450d00200641002802c0a3c68000118080808000000b200241c0006a2480808080000ba90703097f017e047f23808080800041106b220224808080800020012802042103410421040240024002400240024002400240200128020822050d0041002106410421070c010b200541ffffffff004b0d0420054103742206417f4c0d0441002d00fca3c680001a200641002802c8a3c68000118180808000002207450d010b20072003200610848e80800021080240200141146a2802002209450d00200941ffffff3f4b0d042009410474220a417f4c0d04200141106a28020021034100210641002d00fca3c680001a200a41002802c8a3c68000118180808000002204450d02200921070340200a2006460d012003290208210b200420066a220c2003290200370200200c41086a200b370200200641106a2106200341106a21032007417f6a22070d000b0b0240024002400240024002400240024002400240024020012d00240e080001020304050607000b2002200141286a10c482808000200228020821072002280204210c2002280200210a4100210d0c080b2002200141286a10c582808000200228020821072002280204210c2002280200210a4101210d0c070b200141286a280200210a4102210d0c060b2001412c6a280200210c200141286a280200210a4103210d0c040b2001412c6a28020021034104210d02400240200141306a28020022070d00410021064104210c0c010b200741ffffffff014b0d0a20074102742206417f4c0d0a41002d00fca3c680001a200641002802c8a3c6800011818080800000220c450d090b200c2003200610848e8080001a2007210a0c040b200141256a2d0000210e4105210d0c040b200141286a280200210a4106210d0c020b2001412c6a280200210c200141286a280200210a4107210d0b0b0b2001411c6a280200210f02400240200141206a28020022060d0041042101410021030c010b200641ffffffff004b0d0420064103742203417f4c0d0441002d00fca3c680001a200341002802c8a3c68000118180808000002201450d050b2001200f200310848e8080002103200041206a20063602002000411c6a200336020020002006360218200041306a20073602002000412c6a200c360200200041286a200a360200200041256a200e3a00002000200d3a0024200041146a2009360200200041106a20043602002000200936020c200020053602082000200836020420002005360200200241106a2480808080000f0b4104200610b280808000000b4104200a10b280808000000b4104200610b280808000000b10ae80808000000b4104200310b280808000000bbb0401077f0240024002400240200128022022020d00410021020c010b20012002417f6a3602202001280204210202400240024020012802002203450d002002450d010b2003450d032001410c6a2802002104200141086a28020021050c010b200141086a280200210202402001410c6a2802002205450d0002400240200541077122040d00200521030c010b2005210303402003417f6a210320022802f00421022004417f6a22040d000b0b20054108490d00034020022802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042102200341786a22030d000b0b20014200370208200120023602042001410136020041002104410021050b02400240200420022f01ee044f0d00200221030c010b034020022802002203450d04200541016a210520022f01ec04210420032102200420032f01ee044f0d000b0b200441016a21060240024020050d00200321020c010b200320064102746a41f0046a2802002102410021062005417f6a2207450d002005417e6a2108024020074107712205450d0003402007417f6a210720022802f00421022005417f6a22050d000b0b20084107490d00034020022802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042102200741786a22070d000b0b2001200636020c20014100360208200120023602042003200441346c6a41306a2105200320044102746a41046a21020b20002005360204200020023602000f0b41f8dac1800010a081808000000b41e8dac1800010a081808000000bd009030e7f017e087f23808080800041c0006b22052480808080002005200120022003200410f28080800020052802302106200541346a2802002107024002400240024020052802000d00200221032005410e6a2d00000d032006200528020422086a21092005410c6a2d00002104024020080d00024020070d0020022103200441ff0171450d0503402004410173220441ff01710d000b200221030c050b024020092c0000220a4100480d00200441ff0171450d0403402004410173220441ff01710d000c050b0b0240200a4160490d000240200a4170490d0020092d0002413f7141067420092d0001413f71410c74722107200441ff0171450d0403402004410173220441ff01710d000c050b0b200441ff0171450d0403402004410173220441ff01710d000c050b0b200441ff0171450d0303402004410173220441ff01710d000c040b0b02400240200820074f0d0020092c0000220a4140480d01200441017321030240200a4100480d00200441ff0171450d050340200341ff017121042003410173210320040d000c060b0b0240200a4160490d000240200a4170490d0020092d0002413f7141067420092d0001413f71410c74722107200441ff0171450d050340200341ff017121042003410173210320040d000c060b0b200441ff0171450d050340200341ff017121042003410173210320040d000c060b0b200441ff0171450d040340200341ff017121042003410173210320040d000c050b0b20082007470d0020022103200441ff0171450d0403402004410173220441ff01710d000b200221030c040b20062007200820074188dbc18000109781808000000b200221032005411c6a28020022092007460d022005413c6a280200220b200541186a280200220c6b210d200b417f6a210e200541246a280200210f2005280238210a200620096a2110200541106a280200221120096b211220052903082113034002402009200e6a22032007490d00200921030c040b410020126b21142009200b6a21152009200c6a2116200f2117200f2118200921040340024020092004460d00200921030c050b0240024002402013200620036a31000088a74101710d004100210320152104200f417f460d020c010b201120112018201120184b1b200f417f4622191b221a200b201a200b4b1b211b201a210402400240024003400240201b2004470d004100201820191b2108201121040340024020082004490d002017410020191b210f2010200b6a21102012200b6b21122015210920152007470d0a200221030c0d0b2004417f6a2204200b4f0d03200420096a220320074f0d04200a20046a2d0000200620036a2d0000460d000b200d2103201621042019450d050c060b200920046a20074f0d03201020046a2103200a20046a2108200441016a210420082d000020032d0000460d000b201420046a2104410021032019450d030c040b2004200b41b4d9c1800010f980808000000b2003200741c4d9c1800010f980808000000b2007201a20096a2204200720044b1b200741d4d9c1800010f980808000000b20032117200321180b2004200e6a22032007490d000b0b200921030c020b20022103200720092d0003413f7172200a411274418080f0007172418080c400460d010b200821030b2000200220036b3602042000200120036a360200200541c0006a2480808080000bda0501097f23808080800041306b220424808080800002400240024002400240200128020022050d0041002d00fca3c680001a41f00441002802c8a3c680001181808080000022060d01410441f00410b280808000000b2001280204210703402005417c6a210820052f01ee042209410274210a41002106417f210b024003400240200a2006470d002009210b0c020b200520066a210c200641046a2106200b41016a210b200841346a2108417f200c41046a280200220c200247200c20024b1b220c4101460d000b200c41ff0171450d040b02402007450d002007417f6a21072005200b4102746a41f0046a28020021050c010b0b200441003602102004200536020c20042002360208200420013602042004200b360214200441206a200b3602002004200429020c370318200441246a200441186a20022003200441046a10bf8280800020042802042206200628020841016a3602080c010b200641013b01ee04200641003602002006200236020420062003290200370230200641386a200341086a290200370200200641c0006a200341106a290200370200200641c8006a200341186a290200370200200641d0006a200341206a290200370200200641d8006a200341286a290200370200200641e0006a200341306a2802003602002001428080808010370204200120063602000b20004180808080783602000c010b20002008290200370200200041306a200841306a2206280200360200200041286a200841286a220b290200370200200041206a200841206a2202290200370200200041186a200841186a220c290200370200200041106a200841106a2205290200370200200041086a200841086a220a29020037020020082003290200370200200a200341086a2902003702002005200341106a290200370200200c200341186a2902003702002002200341206a290200370200200b200341286a2902003702002006200341306a2802003602000b200441306a2480808080000bff0401057f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002002210420012105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802e80121012005417f6a22050d000b0b20024108490d00034020012802e8012802e8012802e8012802e8012802e8012802e8012802e8012802e8012101200041786a22000d000b0b410021050b024002400240200420012f01e201490d00034020012802b0012200450d0220012f01e0012104200141002802c0a3c6800011808080800000200541016a210520002101200420002f01e2014f0d000b200021010b200441016a2102024020050d00200121000c020b200120024102746a41e8016a2802002100410021022005417f6a2201450d012005417e6a2104024020014107712205450d0003402001417f6a210120002802e80121002005417f6a22050d000b0b20044107490d01034020002802e8012802e8012802e8012802e8012802e8012802e8012802e8012802e8012100200141786a22010d000c020b0b200141002802c0a3c680001180808080000041fcd6c1800010a081808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802e80121002005417f6a22050d000b0b20024108490d00034020002802e8012802e8012802e8012802e8012802e8012802e8012802e8012802e8012100200141786a22010d000b0b034020002802b0012101200041002802c0a3c68000118080808000002001210020010d000b0b0bc50a010c7f024020002802002201450d00200028020421020240024020002802082203450d00410021040340024002402004450d002002210520012106200421010c010b4100210502402002450d0020022100024020024107712204450d0003402000417f6a210020012802f00421012004417f6a22040d000b0b20024108490d00034020012802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042101200041786a22000d000b0b410021060b024002400240200520012f01ee04490d00034020012802002200450d0220012f01ec042105200141002802c0a3c6800011808080800000200641016a210620002101200520002f01ee044f0d000b200021010b200541016a2102024020060d00200121040c020b200120024102746a41f0046a2802002104410021022006417f6a2200450d012006417e6a2107024020004107712206450d0003402000417f6a210020042802f00421042006417f6a22060d000b0b20074107490d01034020042802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042104200041786a22000d000c020b0b200141002802c0a3c680001180808080000041fcd6c1800010a081808000000b02402001200541346c6a41306a2208280200450d00200828020441002802c0a3c68000118080808000000b0240200828020c450d00200841106a28020041002802c0a3c68000118080808000000b0240024002400240024020082d00240e050001040402040b2008412c6a28020021090240200841306a2802002201450d002001410171210641002100024020014101460d002001417e7121054100210020092101034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b0240200141206a280200450d00200141246a28020041002802c0a3c68000118080808000000b200141c0006a21012005200041026a2200470d000b0b2006450d00200920004105746a2201280200450d00200128020441002802c0a3c68000118080808000000b20082802280d020c030b2008412c6a28020021090240200841306a280200220a450d0041002107034002402009200741246c6a22062802082201450d002006280204210b2001410171210c41002100024020014101460d002001417e71210541002100200b2101034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b0240200141206a280200450d00200141246a28020041002802c0a3c68000118080808000000b200141c0006a21012005200041026a2200470d000b0b200c450d00200b20004105746a2201280200450d00200128020441002802c0a3c68000118080808000000b02402006280200450d00200628020441002802c0a3c68000118080808000000b0240200628020c450d002006410c6a28020441002802c0a3c68000118080808000000b200741016a2207200a470d000b0b20082802280d010c020b200841286a280200450d012008412c6a28020021090b200941002802c0a3c68000118080808000000b02402008280218450d002008411c6a28020041002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121040c010b02400240200241077122000d0020012104200221010c010b200121042002210103402001417f6a210120042802f00421042000417f6a22000d000b0b20024108490d00034020042802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042104200141786a22010d000b0b034020042802002101200441002802c0a3c68000118080808000002001210420010d000b0b0b02000bd00201027f23808080800041f0006b2205248080808000200541106a200320044198dbc18000410210f280808000200520023602682005200136026420054101360260200541013b0158200520043602544100210120054100360250200541046a200541106a10c682808000024002400240200528020c22060d000c010b2005280208220120064103746a21024100210320012104034002402004280200200441046a28020010d7828080000d00410121010c020b200341016a2103200441086a22042002470d000b024020052802042204418080808078470d00200621030c020b200020063602082000200136020420002004360200200541f0006a2480808080000f0b2005280204450d00200528020841002802c0a3c68000118080808000000b2005200336021420052001360210419adbc180004132200541106a41ccdbc1800041c0dcc18000108981808000000ba90301057f23808080800041106b2202248080808000024002400240024020014104490d00200041036a417c71220320006b220420014d0d010b2000417f6a21042001210303402003450d02200420036a21052003417f6a210320052c0000417f4a0d000b410021050c020b410021052000280000418081828478710d0102404104200420032000461b22032001417c6a22044f0d000340200020036a280200418081828478710d03200341046a22032004490d000b0b200020046a280000418081828478710d010b200241086a2000200141a0ddc18000410210d18280800041002105200228020c2204450d0020022802082203450d0041012106024020032d0000220541df00460d00410121062005419f7f6a41ff0171411a490d00200541bf7f6a41ff0171411a4921060b200341016a21032004417f6a21040240034020042200450d012000417f6a210420032d00002105200341016a22012103200541506a41ff0171410a490d0020012103200541ff017141df00460d00200121032005415f7141bf7f6a41ff0171411a490d000b0b20062000457121050b200241106a24808080800020050be10201017f2380808080004180016b2207248080808000200741186a41086a200320044198dbc18000410210f280808000200720023602782007200136027420074101360270200741013b01682007200436026441002101200741003602602007200636021c200720053602182007410c6a200741186a10c782808000024002400240200728021422060d000c010b2007280210220120064103746a21024100210320012104034002402004280200200441046a28020010d7828080000d00410121010c020b200341016a2103200441086a22042002470d000b0240200728020c2204418080808078470d00200621030c020b20002006360208200020013602042000200436020020074180016a2480808080000f0b200728020c450d00200728021041002802c0a3c68000118080808000000b2007200336021c20072001360218419adbc180004132200741186a41ccdbc1800041d0dcc18000108981808000000bac0201027f23808080800041106b22022480808080000240024020002802000d00200128021441a2ddc18000410f200141186a28020028020c1182808080000021010c010b2002200041046a360204200128021441b1ddc180004111200141186a28020028020c118280808000002100200241003a000d200220003a000c20022001360208200241086a41c2ddc180004107200241046a41ccddc18000108c81808000210320022d000c2100024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b140020002802002000280204200110e2808080000bf40201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000b8505020a7f027e23808080800041c0006b2204248080808000200128020821050240024002400240200128020c22060d00410021060c010b200141106a28020021070340200641b0016a210820062f01e2012209410474210a4100210b417f210c0240024003400240200a200b470d002009210c0c020b2006200b6a210d200b41106a210b200c41016a210c200841046a2108417f200d290300220e200285200d41086a290300220f20038584420052200e200256200f200356200f2003511b1b220d4101460d000b200d41ff0171450d010b2007450d022007417f6a21072006200c4102746a41e8016a28020021060c010b0b200828020021054100210b0c010b20042001410c6a3602242004200c3602202004410036021c20042006360218200420023e0208200420024220883e020c200420033e0210200420034220883e02140240024020060d0041002d00fca3c680001a41e80141002802c8a3c6800011818080800000220b450d03200b41013b01e201200b41003602b001200b20053602b401200b2004290308370300200b200441106a290300370308200141106a4280808080103702002001200b36020c0c010b200441286a41086a200441186a220b41086a2802003602002004200b290200370328200441346a200441286a2004290308200441086a41086a2903002005200441246a10be828080002004280224220b200b28020841016a3602080b02402001280208220b2001280200470d002001200b10ca828080002001280208210b0b2001280204200b4104746a220b2003370308200b20023703004101210b2001200128020841016a3602080b200020053602042000200b3a0000200441c0006a2480808080000f0b410841e80110b280808000000bf409030c7f017e017f23808080800041c0016b2202248080808000200241086a20002001290300200141086a29030010dc82808000200228020c21030240024020022d0008450d00200241c8006a200128021011808080800000200220024184016a290200370218200220022802800141ffffffff017136021420024190016a2802002104200228028c0121050240024020024194016a28020022060d0041002107410421080c010b4100210741002d00fca3c680001a2006410474220141002802c8a3c68000118180808000002208450d02200420064105746a21092008210a2004210103402001280200220b450d012001280204210c024002402001280218220d0d004100210d0c010b2001290208210e2002200141106a2902003703b0012002200e3703a8012002200d3602b8012000200241a8016a10dd82808000210f4101210d0b200a200d360200200a410c6a200c360200200a41086a200b360200200a41046a200f360200200a41106a210a200741016a2107200141206a22012009470d000b0b02402005450d00200441002802c0a3c68000118080808000000b200241286a2007360200200241246a200836020020022006360220200241386a200241c8006a200010de82808000200241306a2002419c016a290200370200200220022802980141ffffffff017136022c200241c8006a200041186a2003200241146a10d28280800020022802482201418080808078460d0002402001450d00200228024c41002802c0a3c68000118080808000000b02402002280254450d00200241d8006a28020041002802c0a3c68000118080808000000b0240024002400240024020022d006c0e050001040402040b200241f4006a280200210d0240200241f8006a2802002201450d002001410171210b4100210a024020014101460d002001417e712107200d21014100210a034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b0240200141206a280200450d00200141246a28020041002802c0a3c68000118080808000000b200141c0006a21012007200a41026a220a470d000b0b200b450d00200d200a4105746a2201280200450d00200128020441002802c0a3c68000118080808000000b20022802700d020c030b200241f4006a280200210d0240200241f8006a2802002209450d004100210c03400240200d200c41246c6a220b2802082201450d00200b280204210f200141017121004100210a024020014101460d002001417e7121074100210a200f2101034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b0240200141206a280200450d00200141246a28020041002802c0a3c68000118080808000000b200141c0006a21012007200a41026a220a470d000b0b2000450d00200f200a4105746a2201280200450d00200128020441002802c0a3c68000118080808000000b0240200b280200450d00200b28020441002802c0a3c68000118080808000000b0240200b28020c450d00200b410c6a28020441002802c0a3c68000118080808000000b200c41016a220c2009470d000b0b20022802700d010c020b200241f0006a280200450d01200241f4006a280200210d0b200d41002802c0a3c68000118080808000000b2002280260450d00200241e4006a28020041002802c0a3c68000118080808000000b200241c0016a24808080800020030f0b4104200110b280808000000bab0603027f017e077f23808080800041d0006b2203248080808000024002400240024002400240024002400240024020012d00000e080001020304050607000b200041046a2002200141046a10df82808000410021010c070b20032002360248200320012802043602402003200141086a280200220436023c20032004360238200320042001410c6a28020041246c6a360244200041046a200341386a10c382808000410121010c060b200141186a2802002104200129030821052003200141106a290300370340200320053703382003200436024820002002200341386a10dd82808000360204410221010c050b200141206a2802002104200041086a2002200141086a10dd8280800036020020002004360204410321010c040b200141086a280200210620012802042107024002402001410c6a28020022080d00410021094104210a0c010b41002d00fca3c680001a2008410274220141002802c8a3c6800011818080800000220a450d052006200841186c6a210b200841037441786a41037641016a2109200a21042006210103402001280210210c200129030021052003200141086a290300370340200320053703382003200c36024820042002200341386a10dd82808000360200200441046a2104200141186a2201200b470d000b0b02402007450d00200641002802c0a3c68000118080808000000b200020083602042000410c6a2009360200200041086a200a360200410421010c030b200020012d00013a0001410521010c020b200141186a2802002104200129030821052003200141106a290300370340200320053703382003200436024820002002200341386a10dd82808000360204410621010c010b200341086a41286a200141306a290300370300200341086a41206a200141286a290300370300200341086a41186a2204200141206a290300370300200341086a41106a200141186a290300370300200341086a41086a200141106a290300370300200320012903083703082002200141086a10dd828080002101200041086a2002200410dd8280800036020020002001360204410721010b200020013a0000200341d0006a2480808080000f0b4104200110b280808000000b950201057f23808080800041306b220324808080800020022802042104200228020021050240024002400240200228020822020d00410421060c010b200241ffffff1f4b0d0120024105742207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b200341046a41086a2207410036020020032006360208200320023602042003200136022020032004200241386c6a36021c2003200536021820032004360214200320043602102003200636022c2003410036022820032007360224200341106a200341246a10ce82808000200041086a200728020036020020002003290204370200200341306a2480808080000f0b10ae80808000000b4104200710b280808000000b420020004280808080c00037033820004280808080c000370350200041053b0100200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b430020004280808080c00037033820004280808080c00037035020004185063b0100200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b430020004280808080c00037033820004280808080c00037035020004185083b0100200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b430020004280808080c00037033820004280808080c000370350200041850a3b0100200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b430020004280808080c00037033820004280808080c000370350200041850c3b0100200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b430020004280808080c00037033820004280808080c000370350200041850e3b0100200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b550020004280808080c00037033820004280808080c0003703502000410036020c200042808080808001370204200041043a0000200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bf60201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120003100004101200110fe8080800021000c020b20002d00002103410021000340200220006a41ff006a413041d7002003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002d00002103410021000340200220006a41ff006a413041372003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b02000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a41dcddc180002005410c6a41dcddc180002003200410fb80808000000bd90201047f23808080800041c0006b220224808080800020012c001f417f4a10ad8d8080002103200241186a200141186a290000370300200241106a200141106a290000370300200241086a200141086a29000037030020022001290000370300200241206a200210ab8180800041002104410121050340200220046a2d0000200241206a20046a2d00004610ad8d8080002005712105200441016a22044120470d000b4100210402400240200510ad8d80800020037110ad8d8080002205417f7341017110ad8d80800041ff01710d00200220053a0000200541ff01714101470d0120002001290000370001200041196a200141186a290000370000200041116a200141106a290000370000200041096a200141086a290000370000410121040b200020043a0000200241c0006a2480808080000f0b200241003602204100200241ecddc18000200241206a41c8dec1800010e982808000000b140020002802002000280204200110e2808080000b1900200120002802002200280200200028020410dd808080000b140020012000280200200028020410dd808080000b1200200041d8dec18000200110d9808080000b02000b800902017f017c23808080800041306b2202248080808000024002400240024002400240024002400240024002400240024002400240024002400240024020002d00000e12000102030405060708090a0b0c0d0e0f1011000b200220002d00013a00082002411c6a42013702002002410236021420024190dfc18000360210200241ce8080800036022c200141186a28020021002002200241286a3602182002200241086a36022820012802142000200241106a10d98080800021010c110b200220002903083703082002411c6a420137020020024102360214200241acdfc18000360210200241cf8080800036022c200141186a28020021002002200241286a3602182002200241086a36022820012802142000200241106a10d98080800021010c100b200220002903083703082002411c6a420137020020024102360214200241acdfc18000360210200241d08080800036022c200141186a28020021002002200241286a3602182002200241086a36022820012802142000200241106a10d98080800021010c0f0b20002b030821032002411c6a420137020020024102360214200241ccdfc18000360210200241d18080800036020c20022003390328200141186a28020021002002200241086a3602182002200241286a36020820012802142000200241106a10d98080800021010c0e0b200220002802043602082002411c6a420137020020024102360214200241e8dfc18000360210200241d28080800036022c200141186a28020021002002200241286a3602182002200241086a36022820012802142000200241106a10d98080800021010c0d0b200220002902043702082002411c6a42013702002002410136021420024180e0c18000360210200241d38080800036022c200141186a28020021002002200241286a3602182002200241086a36022820012802142000200241106a10d98080800021010c0c0b200128021441fcdec18000410a200141186a28020028020c1182808080000021010c0b0b20012802144188e0c18000410a200141186a28020028020c1182808080000021010c0a0b20012802144192e0c18000410c200141186a28020028020c1182808080000021010c090b2001280214419ee0c18000410e200141186a28020028020c1182808080000021010c080b200128021441ace0c180004108200141186a28020028020c1182808080000021010c070b200128021441b4e0c180004103200141186a28020028020c1182808080000021010c060b200128021441b7e0c180004104200141186a28020028020c1182808080000021010c050b200128021441bbe0c18000410c200141186a28020028020c1182808080000021010c040b200128021441c7e0c18000410f200141186a28020028020c1182808080000021010c030b200128021441d6e0c18000410d200141186a28020028020c1182808080000021010c020b200128021441e3e0c18000410e200141186a28020028020c1182808080000021010c010b20012802142000280204200041086a280200200141186a28020028020c1182808080000021010b200241306a24808080800020010baf0201027f23808080800041306b220224808080800002400240200029030042ffffffffffffffffff0083bf44000000000000f07f630d002002411c6a420137020020024101360214200241a8e2c18000360210200241d48080800036022c20022000360228200141186a28020021002002200241286a36021820012802142000200241106a10d98080800021030c010b200241003a000c200220013602082002411c6a42013702004101210320024101360214200241a8e2c18000360210200241d48080800036022c200220003602282002200241286a360218200241086a41d8dec18000200241106a10d9808080000d00024020022d000c0d00200128021441b0e2c180004102200141186a28020028020c118280808000000d010b410021030b200241306a24808080800020030b2300200128021420002802002000280204200141186a28020028020c118280808000000bbc0401067f23808080800041306b220224808080800020002802002103024002400240024002400240200028020422040e03030201000b4101210020012802142205419ce2c180004107200141186a280200220628020c2207118280808000000d0420022003360214200241246a42013702002002410236021c200241ece1c18000360218200241d5808080003602082002200241046a3602202002200241146a36020420052006200241186a10d9808080000d03200341086a2101200441037441786a2103034020022001360214200541a3e2c1800041022007118280808000000d042002410236021c200241ece1c1800036021820024201370224200241d5808080003602082002200241046a3602202002200241146a36020420052006200241186a10d9808080000d04200141086a210141002100200341786a22030d000c050b0b200241046a410c6a41d680808000360200200241186a410c6a42023702002002410336021c20024184e2c18000360218200241d680808000360208200220033602042002200341086a36020c200141186a28020021002002200241046a36022020012802142000200241186a10d98080800021000c030b200241246a42013702002002410236021c200241ece1c18000360218200241d68080800036020820022003360204200141186a28020021002002200241046a36022020012802142000200241186a10d98080800021000c020b41f1e0c18000410e41dce1c1800010f880808000000b410121000b200241306a24808080800020000bc90301047f0240024002400240024020024108490d00200141036a417c7122032001460d01200320016b2204450d01200120036b21054101210620012103034020032d0000412e460d05200341016a2103200541016a22050d000b2004200241786a22064b0d030c020b024020020d00410021060c040b20012d0000412e4622060d0320024101460d0320012d0001412e4622060d0320024102460d0320012d0002412e4622060d0320024103460d0320012d0003412e4622060d0320024104460d0320012d0004412e4622060d0320024105460d0320012d0005412e4622060d0320024106460d0320012d0006412e4621060c030b200241786a2106410021040b0340200120046a220341046a280200220541aedcb8f1027341fffdfb776a2005417f73712003280200220341aedcb8f1027341fffdfb776a2003417f737172418081828478710d01200441086a220420064d0d000b0b4100210620042002460d00200120046a21032004417f7320026a210503402005210420032d0000412e4622060d01200341016a21032004417f6a210520040d000b0b2000200620002d0004410047723a00042000280200220328021420012002200341186a28020028020c118280808000000b330020002001412e4620002d0004410047723a0004200028020022002802142001200041186a280200280210118380808000000bfa0103017f017c017e23808080800041106b22032480808080000240024002400240024020002802000e0400010203000b20002b03082104200341033a00002003200439030820032001200210838380800021020c030b20002903082105200341013a00002003200537030820032001200210838380800021020c020b20002903082105200341023a00002003200537030820032001200210838380800021020c010b200341086a4106360200200341b5e2c18000360204200341113a000020032001200210838380800021022000280204450d00200041086a28020041002802c0a3c68000118080808000000b200341106a24808080800020020b02000b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0b2100200128021441fcf8c180004105200141186a28020028020c118280808000000b140020002802042000280208200110e2808080000bf20201027f23808080800041106b220224808080800002400240024002402001418001490d002002410036020c2001418010490d0102402001418080044f0d0020022001413f71418001723a000e20022001410c7641e001723a000c20022001410676413f71418001723a000d410321010c030b20022001413f71418001723a000f20022001410676413f71418001723a000e20022001410c76413f71418001723a000d2002200141127641077141f001723a000c410421010c020b0240200028020822032000280200470d0020002003109083808000200028020821030b200028020420036a20013a00002000200028020841016a3602080c020b20022001413f71418001723a000d2002200141067641c001723a000c410221010b02402000280200200028020822036b20014f0d00200020032001109183808000200028020821030b200028020420036a2002410c6a200110848e8080001a2000200320016a3602080b200241106a24808080800041000b4b01017f02402000280200200028020822036b20024f0d00200020032002109183808000200028020821030b200028020420036a2001200210848e8080001a2000200320026a36020841000b4201017f41002d00fca3c680001a0240411441002802c8a3c680001181808080000022000d004104411410b280808000000b2000420037020c2000410136020020000b810700024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020002802000e19000102030405060708090a0b0c0d0e0f101112131415161718000b20012802142000280204200041086a280200200141186a28020028020c118280808000000f0b200041046a20011098838080001a000b200128021441d4f9c180004118200141186a28020028020c118280808000000f0b200128021441ecf9c18000411b200141186a28020028020c118280808000000f0b20012802144187fac18000411a200141186a28020028020c118280808000000f0b200128021441a1fac180004119200141186a28020028020c118280808000000f0b200128021441bafac18000410c200141186a28020028020c118280808000000f0b200128021441c6fac180004113200141186a28020028020c118280808000000f0b200128021441d9fac180004113200141186a28020028020c118280808000000f0b200128021441ecfac18000410e200141186a28020028020c118280808000000f0b200128021441fafac18000410e200141186a28020028020c118280808000000f0b20012802144188fbc18000410c200141186a28020028020c118280808000000f0b20012802144194fbc18000410e200141186a28020028020c118280808000000f0b200128021441a2fbc18000410e200141186a28020028020c118280808000000f0b200128021441b0fbc180004113200141186a28020028020c118280808000000f0b200128021441c3fbc18000411a200141186a28020028020c118280808000000f0b200128021441ddfbc18000413e200141186a28020028020c118280808000000f0b2001280214419bfcc180004114200141186a28020028020c118280808000000f0b200128021441affcc180004134200141186a28020028020c118280808000000f0b200128021441e3fcc18000412c200141186a28020028020c118280808000000f0b2001280214418ffdc180004124200141186a28020028020c118280808000000f0b200128021441b3fdc18000410e200141186a28020028020c118280808000000f0b200128021441c1fdc180004113200141186a28020028020c118280808000000f0b200128021441d4fdc18000411c200141186a28020028020c118280808000000f0b200128021441f0fdc180004118200141186a28020028020c118280808000000bc80101017f23808080800041306b2202248080808000024002402000280200220028020c0d002000200110fe8280800021000c010b2002412c6a418580808000360200200241186a410c6a4185808080003602002002410c6a4203370200200241033602042002419cfec1800036020020022000410c6a360220200241db8080800036021c200220003602182002200041106a360228200141186a28020021002002200241186a36020820012802142000200210d98080800021000b200241306a24808080800020000bfb0201017f23808080800041f0006b220224808080800020002802002100200241003602482002428080808010370240200241cc006a41186a41e8f5c18000360200200241033a006c2002412036025c20024100360268200241003602542002410036024c2002200241c0006a36026002402000200241cc006a10fe828080000d00200241306a41086a200241c0006a41086a2802003602002002412c6a418580808000360200200241186a410c6a4185808080003602002002410c6a420337020020022002290240370330200241dc8080800036021c20024104360204200241d0fec180003602002002200041106a36022820022000410c6a360220200141186a28020021002002200241306a3602182002200241186a36020820012802142000200210d980808000210002402002280230450d00200228023441002802c0a3c68000118080808000000b200241f0006a24808080800020000f0b4180f6c180004137200241186a41b8f6c1800041b8f7c18000108981808000000b850201037f23808080800041106b22012480808080002000410c6a28020021020240024002400240024002400240024020002802040e020001020b20020d01410121034100210041c8f7c1800021020c030b2002450d010b200141046a200010b8808080000c020b2000280200220028020021020240200028020422000d0041012103410021000c010b2000417f4c0d0241002d00fca3c680001a200041002802c8a3c68000118180808000002203450d030b20032002200010848e80800021022001200036020c20012002360208200120003602040b200141046a1082838080002100200141106a24808080800020000f0b10ae80808000000b4101200010b280808000000bdd1303157f017e027f23808080800041c0006b2201248080808000200120002802042202200028020822034188fec18000410910f28080800002400240024002400240024020012802000d002001410e6a2d00000d032001410d6a2d00002104200141086a2802002205450d0120012802302106024002402005200141346a2802002207490d0020052007460d010c070b200620056a2c00004140480d060b0240200620056a2208417f6a2d00002209411874411875220a417f4a0d00024002402008417e6a2d00002209411874411875220b41bf7f4c0d002009411f7121090c010b024002402008417d6a2d00002209411874411875220c41bf7f4c0d002009410f7121090c010b2008417c6a2d0000410771410674200c413f717221090b2009410674200b413f717221090b2009410674200a413f717221090b200441ff01710d022009418080c400460d03417f210402402009418001490d00417e21042009418010490d00417d417c200941808004491b21040b0240200420056a22050d00410021050c030b0240024020052007490d0020052007470d070c010b200620056a2c000041bf7f4c0d060b200620056a2204417f6a2c0000417f4a0d022004417e6a2c000041bf7f4a1a0c020b200141206a280200220d2001413c6a280200220e6b2205200141346a280200220a4f0d02200141246a280200210f2001280230210b200141146a2802002210200e2010200e4b1b211120012802382212417f6a2113200141286a2802002114200141186a28020021152001290308211603400240024002402016200b20056a220c31000088a74101710d00200e21042005210d200f417f470d010c020b02400240024002400240024020102010201420102014491b200f417f4622171b2204417f6a2206200e4f0d00201320046a2109410020046b2106200420056a417f6a210403402006450d022004200a4f0d03200641016a2106200b20046a210720092d000021082004417f6a21042009417f6a2109200820072d0000460d000b200d20106b20066b210d200e210420170d070c060b20040d020b200e201420171b22042010200420104b1b210720102104034020072004460d0920112004460d03200520046a200a4f0d04200c20046a2106201220046a2109200441016a210420092d000020062d0000460d000b200d20156b210d201521042017450d040c050b2004200a41ecf8c1800010f980808000000b2006200e41dcf8c1800010f980808000000b2011200e41bcf8c1800010f980808000000b200a200520106a2204200a20044b1b200a41ccf8c1800010f980808000000b200421140b200d200e6b2205200a490d000c030b0b41002105200441ff0171450d010b200220056a210b200541096a220e20036b211241002104417720056b22112109200e21070240024002400240024002400340200520046a210a2003210602402011200446220c0d000240200a41096a2003490d00201220046a0d03200320096a21060c010b200b20046a41096a2c000041bf7f4c0d02200320076b21060b200b20046a2108024002402006450d00200841096a2d000041506a41ff0171410a490d010b200541096a2206200220046a22126a210d200620046a2114200620036b20046a2117200321070240200c0d000240024020142003490d002017450d010c0a0b200d2c000041bf7f4c0d090b200320096a21070b4101210620074108490d05200d29000042a0c6bde3d6ae9bb720520d05200441116a2109200320046b416f6a2111201241116a210641002112410020056b210f416f20056b2115200a20036b41116a2118200a41116a2213210a034020032107024002400240200520096a2210450d00024020102003490d0020052011470d02200f20116a21070c010b200620056a2c000041bf7f4c0d012003200a6b21070b02402007450d00200620056a2d000041506a41ff0171410a490d020b4101210620102003490d08200e20144b0d090240200e450d000240200e2003490d00200e2003460d010c0b0b2002200e6a2c00004140480d0a0b0240200c0d00024020142003490d0020170d0b0c010b200d2c000041bf7f4c0d0a0b2004450d080240024002402002200e6a22072d000041556a0e03010002000b2004210a0c080b20044101460d082004417f6a210a200741016a21070c070b2004210a20044101470d060c070b200220032010200341e880c28000109781808000000b200641016a2106200941016a2109201241016a21122011417f6a2111200a41016a210a0c000b0b2009417f6a2109200441016a2104200741016a21070c000b0b20022003200a41096a200341c880c28000109781808000000b02400240200a4109490d00410021090340200a450d0220072d000041506a220c41094b0d032009ad420a7e2216422088a74100470d03200741016a2107200a417f6a210a2016a7220e200c6a2209200e4f0d000c030b0b41002109034020072d000041506a220c41094b0d02200741016a2107200c2009410a6c6a2109200a417f6a220a0d000b0b0240201320104b0d00024020152004460d00024020132003490d002018450d010c020b200841116a2c00004140480d010b02402010450d0020052011470d010b2012450d02024002400240200841116a22042d000041556a0e03000201020b4101210620124101460d042012417f6a2112200841126a21040c010b4101210620124101460d030b0240024020124109490d004100210803402012450d024101210620042d000041506a220741094b0d052008ad420a7e2216422088a74100470d05200441016a21042012417f6a21122016a7220a20076a2208200a490d050c000b0b4100210841012106034020042d000041506a220741094b0d04200441016a210420072008410a6c6a21082012417f6a22120d000b0b4100210620032005490d06024002402005450d00200320054d0d00200b2c000041bf7f4c0d010b20002005360208200521030c070b4181f9c18000413041b4f9c1800010f880808000000b2002200320132010418881c28000109781808000000b410121060c040b0c030b20022003200e201441f880c28000109781808000000b200220032014200341d880c28000109781808000000b410121060b02400240024002402000280200220420034b0d00200221070c010b0240024020030d00410121070c010b200341002802c8a3c68000118180808000002207450d02200720022004200320042003491b10848e8080001a0b200241002802c0a3c68000118080808000000b41002d00fca3c680001a411441002802c8a3c68000118180808000002204450d0120042003360208200420073602042004410036020020044100200820061b36021020044100200920061b36020c200141c0006a24808080800020040f0b4101200310b280808000000b4104411410b280808000000b200620074100200541c4f9c18000109781808000000bb20101017f23808080800041c0006b22032480808080002003200236020420032001360200200341086a410c6a4202370200200341206a410c6a41dd80808000360200200341306a41086a200041086a2903003703002003410236020c2003418cffc18000360208200341de80808000360224200320002903003703302003200341206a360210200320033602282003200341306a360220200341086a1081838080002100200341c0006a24808080800020000bf70204017f017c017e027f23808080800041c0006b2202248080808000024002400240024020002d0000417d6a0e050100000002000b200241286a41086a200041086a29030037030020022000290300370328200241286a200110f08280800021000c020b0240024020002b03082203bd22044280808080808080f8ff00834280808080808080f8ff00510d00200241286a21002003200241286a10bd8280800021050c010b41a887c2800041ab87c280002004427f5522051b41af87c28000200442ffffffffffffff07835022061b21004103410420051b410320061b21050b2002410c6a42013702002002200536022420022000360220200241df8080800036021c20024102360204200241d0ffc18000360200200141186a28020021002002200241206a3602182002200241186a36020820012802142000200210d98080800021000c010b200128021441e0ffc180004104200141186a28020028020c1182808080000021000b200241c0006a24808080800020000bb20101017f23808080800041c0006b22032480808080002003200236020420032001360200200341086a410c6a4202370200200341206a410c6a41dd80808000360200200341306a41086a200041086a2903003703002003410236020c200341acffc18000360208200341de80808000360224200320002903003703302003200341206a360210200320033602282003200341306a360220200341086a1081838080002100200341c0006a24808080800020000b4201017f41002d00fca3c680001a0240411441002802c8a3c680001181808080000022000d004104411410b280808000000b2000420037020c2000410d36020020000bbd0401057f024002400240024002400240200320024b0d00410121044100210520034101480d04200120036a21060240200341034b0d000340200620014d0d062006417f6a22062d0000410a470d000c050b0b02402006417c6a2800002207417f732007418a94a8d0007341fffdfb776a7141808182847871450d000340200620014d0d062006417f6a22062d0000410a470d000c050b0b200320064103716b210720034109490d010340200722064108480d03200120066a220841786a2802002207417f732007418a94a8d0007341fffdfb776a71418081828478710d03200641786a21072008417c6a2802002208417f732008418a94a8d0007341fffdfb776a7141808182847871450d000c030b0b20032002418884c28000109581808000000b200120076a21060340200620014d0d032006417f6a22062d0000410a470d000c020b0b200120066a21060340200620014d0d022006417f6a22062d0000410a470d000b0b200620016b220641016a2105200620024f0d010b0240200120056a20014d0d0020054103712104024002402005417f6a41034f0d00410021060c010b2005417c712102410021060340200620012d0000410a466a20012d0001410a466a20012d0002410a466a20012d0003410a466a2106200141046a21012002417c6a22020d000b0b02402004450d000340200620012d0000410a466a2106200141016a21012004417f6a22040d000b0b200641016a21040b200020043602002000200320056b3602040f0b20052002419884c28000109581808000000b880501077f02402000280208220220002802042203460d000240024002400240024002400240200220034f0d002000280200220420026a2d000022054122460d07200541dc00460d0702402005411f4b0d0020010d080b2000200241016a2206360208200320066b2107024020010d0020074101480d070240200420036a2208200420066a22056b41034b0d002007210120052102034020022d000022034122460d08200341dc00460d08200241016a21022001417f6a22010d000c090b0b200721032005210220052800002201417f73418081828478712204200141a2c48891027341fffdfb776a71450d030c040b200441016a210541002007417c7122046b210103402001450d02200520026a2103200241046a2102200141046a210120032800002203417f73200341a2c48891027341fffdfb776a200341e0bffffe7d6a72200341dcb8f1e2057341fffdfb776a7271418081828478712203450d000b200020036841037620026a417d6a3602080f0b2002200341a884c2800010f980808000000b2000200420066a36020820001089838080000f0b20072103200521022004200141dcb8f1e2057341fffdfb776a710d002005417c7141046a22022008417c6a22044b0d01034020022802002203417f73418081828478712201200341a2c48891027341fffdfb776a710d022001200341dcb8f1e2057341fffdfb776a710d02200241046a220220044d0d000c020b0b034020022d000022014122460d02200141dc00460d02200241016a21022003417f6a22030d000c030b0b200220084f0d01200820026b2101034020022d000022034122460d01200341dc00460d01200241016a21022001417f6a22010d000c020b0b200220056b21070b2000200720066a3602080b0b5301047f024020002802082201200028020422024f0d00200028020021030340200320016a2d000022044122460d01200441dc00460d0120044120490d012000200141016a220136020820022001470d000b0b0bcc1501087f23808080800041e0006b2203248080808000200128020821042001410110888380800002400240024002402001280208220520012802042206460d000340024020052006490d002005200641b884c2800010f980808000000b02400240024002400240024002400240024002400240024002400240024002402001280200220720056a2d0000220841dc00460d0020084122460d012001200541016a2202360208200341106a20072006200210878380800041002d00fca3c680001a2003280214210120032802102105411441002802c8a3c68000118180808000002202450d032002200536020c2002411036020020002002360204200220013602100c120b024020052004490d00200720046a210902402002280200200228020822086b200520046b22044f0d00200220082004109183808000200228020821080b200228020420086a2009200410848e8080001a2001200541016a22093602082002200820046a220836020802400240024002400240024002400240024002400240200920064f0d002001200541026a2204360208200720096a2d0000415e6a0e5402010101010101010101010101040101010101010101010101010101010101010101010101010101010101010101010101010101010101010101030101010101050101010601010101010101070101010801090a010b200341c0006a20072006200910878380800041002d00fca3c680001a2003280244210120032802402105411441002802c8a3c68000118180808000002202450d0e2002200536020c20024104360200200220013602100c160b200341286a20072006200410878380800041002d00fca3c680001a200328022c210120032802282105411441002802c8a3c68000118180808000002202450d0e2002200536020c2002410c360200200220013602100c150b024020082002280200470d0020022008109083808000200228020821080b200228020420086a41223a00002002200228020841016a3602080c170b024020082002280200470d0020022008109083808000200228020821080b200228020420086a41dc003a00002002200228020841016a3602080c160b024020082002280200470d0020022008109083808000200228020821080b200228020420086a412f3a00002002200228020841016a3602080c150b024020082002280200470d0020022008109083808000200228020821080b200228020420086a41083a00002002200228020841016a3602080c140b024020082002280200470d0020022008109083808000200228020821080b200228020420086a410c3a00002002200228020841016a3602080c130b024020082002280200470d0020022008109083808000200228020821080b200228020420086a410a3a00002002200228020841016a3602080c120b024020082002280200470d0020022008109083808000200228020821080b200228020420086a410d3a00002002200228020841016a3602080c110b024020082002280200470d0020022008109083808000200228020821080b200228020420086a41093a00002002200228020841016a3602080c100b200341cc006a2001108b8380800020032f014c0d020240024002400240024020032f014e22054180f8037122044180b003460d0020044180b803460d0420054180b0bf7f73418090bc7f4f0d01419885c2800010a081808000000b2001280208220420012802042208490d02200341386a20012802002008200410878380800041002d00fca3c680001a200328023c210120032802382105411441002802c8a3c680001181808080000022020d014104411410b280808000000b200341003602542005418001490d09024020054180104f0d0020032005413f71418001723a00552003200541067641c001723a0054410221050c120b20032005413f71418001723a005620032005410c7641e001723a005420032005410676413f71418001723a0055410321050c110b2002200536020c20024104360200200220013602100c0e0b2001200441016a360208200128020020046a2d000041dc00460d0c200341173602542001200341d4006a108c8380800021020c0d0b200341306a20012802002001280204200128020810878380800041002d00fca3c680001a2003280234210120032802302105411441002802c8a3c68000118180808000002202450d072002200536020c20024114360200200220013602100c0c0b2004200541e884c28000109681808000000b20022802082208450d0820052004490d06200720046a210a0240200228020020086b200520046b22094f0d00200220082009109183808000200228020821080b200228020420086a200a200910848e8080001a410121042001200541016a22053602082002200820096a2201360208200341d4006a2002280204200110fc80808000024020032802540d00200020032902583702040c120b200341206a20072006200510878380800041002d00fca3c680001a2003280224210120032802202105411441002802c8a3c68000118180808000002202450d072002200536020c2002410f36020020022001360210200020023602040c100b200328025021020c090b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200320053a0054410121050c070b4104411410b280808000000b2004200541d884c28000109681808000000b4104411410b280808000000b0240024020052004490d002001200541016a2202360208200341d4006a200720046a200520046b10fc80808000024020032802540d0020002003290258370204410021040c0b0b200341186a20072006200210878380800041002d00fca3c680001a200328021c210120032802182105411441002802c8a3c68000118180808000002202450d012002200536020c2002410f36020020022001360210200020023602040c090b2004200541c884c28000109681808000000b4104411410b280808000000b200341d4006a2001108d83808000024020032d00540d0020032d005521082001200441026a3602080240200841f500470d00200341d4006a2001108b8380800020032f01540d01024020032f015622044180c0006a41ffff03714180f8034f0d00200341143602542001200341d4006a108c8380800021020c030b024020054180d0006a41ffff0371410a7420044180c8006a41ffff0371722208418080046a2205418080c400460d0020054180b00373418080bc7f6a41ff8fbc7f4b0d040b2003410f3602542001200341d4006a108c8380800021020c020b200341173602542001200341d4006a108c8380800021020c010b200328025821020b200020023602040c050b20032004413f71418001723a005720032008410676413f71418001723a005620032005410c76413f71418001723a00552003200541127641077141f001723a0054410421050b02402002280200200228020822046b20054f0d00200220042005109183808000200228020821040b200228020420046a200341d4006a200510848e8080001a2002200420056a360208200128020821040b200141011088838080002001280208220520012802042206470d000b0b200341086a20012802002005200510878380800041002d00fca3c680001a200328020c210120032802082105411441002802c8a3c68000118180808000002202450d022002200536020c2002410436020020002002360204200220013602100b410221040b20002004360200200341e0006a2480808080000f0b4104411410b280808000000bd00401097f23808080800041106b2202248080808000024002400240024002400240024002402001280208220341046a2204200128020422054b0d00200520034d0d03200128020021062001200341016a2207360208200620036a2d000041a885c280006a2d0000220841ff01470d01200721040c040b20012005360208200220012802002005200510878380800041002d00fca3c680001a2002280204210520022802002103411441002802c8a3c68000118180808000002201450d012001200336020c2001410436020020002001360204200120053602100c040b02404100200520036b2209200920054b1b22094101470d00200721030c020b2001200341026a220a3602080240200620076a2d000041a885c280006a2d0000220741ff01470d00200a21040c030b024020094102470d00200a21030c020b2001200341036a220336020802402006200a6a2d000041a885c280006a2d0000220a41ff01470d00200321040c030b20094103460d0120012004360208200620036a2d000041a885c280006a2d0000220141ff01460d022000200741047420084108746a200a6a41047420016a3b0102410021010c040b4104411410b280808000000b20032005418885c2800010f980808000000b200241086a20062005200410878380800041002d00fca3c680001a200228020c210520022802082103411441002802c8a3c68000118180808000002201450d022001200336020c2001410c36020020002001360204200120053602100b410121010b200020013b0100200241106a2480808080000f0b4104411410b280808000000ba00101037f23808080800041106b2202248080808000200241086a20002802002000280204200028020810878380800041002d00fca3c680001a200228020c2103200228020821040240411441002802c8a3c680001181808080000022000d004104411410b280808000000b2000200436020c2000200129020037020020002003360210200041086a200141086a280200360200200241106a24808080800020000bc60101037f23808080800041106b22022480808080000240024002402001280208220320012802042204490d00200241086a20012802002004200310878380800041002d00fca3c680001a200228020c210320022802082104411441002802c8a3c68000118180808000002201450d022001200436020c200141043602002000200136020420012003360210410121010c010b2000200128020020036a2d00003a0001410021010b200020013a0000200241106a2480808080000f0b4104411410b280808000000bef0501057f23808080800041306b2201248080808000200041011088838080000240024002402000280208220220002802042203460d000340024020022003490d002002200341f884c2800010f980808000000b0240024002400240024002402000280200220420026a2d0000220541dc00460d0020054122460d01200141106a20042003200210878380800041002d00fca3c680001a2001280214210220012802102103411441002802c8a3c68000118180808000002200450d022000200336020c20004110360200200020023602100c080b2000200241016a2205360208024002400240200520034f0d002000200241026a2202360208200420056a2d0000415e6a0e54070101010101010101010101010701010101010101010101010101010101010101010101010101010101010101010101010101010101010101010701010101010701010107010101010101010701010107010702010b200141206a20042003200510878380800041002d00fca3c680001a2001280224210220012802202103411441002802c8a3c68000118180808000002200450d042000200336020c20004104360200200020023602100c090b200141186a20042003200210878380800041002d00fca3c680001a200128021c210220012802182103411441002802c8a3c68000118180808000002200450d042000200336020c2000410c360200200020023602100c080b200141286a2000108b8380800020012f0128450d04200128022c21000c070b2000200241016a360208410021000c060b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200041011088838080002000280208220220002802042203470d000b0b200141086a20002802002002200210878380800041002d00fca3c680001a200128020c210220012802082103411441002802c8a3c68000118180808000002200450d012000200336020c20004104360200200020023602100b200141306a24808080800020000f0b4104411410b280808000000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000be00101037f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014108200141084b1b2201417f73411f7621040240024020030d00200241003602180c010b2002200336021c20024101360218200220002802043602140b200241086a20042001200241146a108f83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a108f83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b1200200041b487c28000200110d9808080000b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0bf20201027f23808080800041106b220224808080800002400240024002402001418001490d002002410036020c2001418010490d0102402001418080044f0d0020022001413f71418001723a000e20022001410c7641e001723a000c20022001410676413f71418001723a000d410321010c030b20022001413f71418001723a000f20022001410676413f71418001723a000e20022001410c76413f71418001723a000d2002200141127641077141f001723a000c410421010c020b0240200028020822032000280200470d0020002003109083808000200028020821030b200028020420036a20013a00002000200028020841016a3602080c020b20022001413f71418001723a000d2002200141067641c001723a000c410221010b02402000280200200028020822036b20014f0d00200020032001109183808000200028020821030b200028020420036a2002410c6a200110848e8080001a2000200320016a3602080b200241106a24808080800041000b4b01017f02402000280200200028020822036b20024f0d00200020032002109183808000200028020821030b200028020420036a2001200210848e8080001a2000200320026a36020841000b140020012000280200200028020410dd808080000b180020002802002001200028020428020c118380808000000b170041cc87c28000412841d888c2800010f880808000000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a41002007109e8380800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bfc0203027f017e047f23808080800041306b2201248080808000200141246a41e888c28000410641ee88c28000412341e888c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a4100109d838080002001280210200128021441386c6a2205420437022c2005420d370224200541c38ac2800036022020054100360218200541e980808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db0037030002402004418080808078470d00419a89c280004111418c8ac2800010a181808000000b20012802142105200128020c210620012802102107200042808080808001370244200020043602382000200736020820002006360204200041003a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200541016a36020c200041d8006a2002280200360200200141306a2480808080000bfc0203027f017e047f23808080800041306b2201248080808000200141246a419189c28000410941ee88c28000412341e888c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a4100109d838080002001280210200128021441386c6a2205420437022c20054210370224200541d98ac2800036022020054100360218200541ea80808000360210200542f0a3fcacaeba9c956f370308200542e3beec81d3e996af917f37030002402004418080808078470d00419a89c280004111418c8ac2800010a181808000000b20012802142105200128020c210620012802102107200042808080808001370244200020043602382000200736020820002006360204200041003a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200541016a36020c200041d8006a2002280200360200200141306a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a109c83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a109c83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bf60203027f017e057f23808080800041306b2201248080808000200141246a419c8ac28000410641a28ac280004121419c8ac28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a4100109d8380800020012802102205200128021441386c22066a2207429ae5abc8c1f8c7ab997f370308200742cfdf8096d7fe8c90c100370300200128020c21082007420437022c2007420d370224200741c38ac2800036022020074100360218200741eb8080800036021002402004418080808078470d00419a89c280004111418c8ac2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000bf40203027f017e057f23808080800041306b2201248080808000200141246a41d08ac28000410941a28ac280004121419c8ac28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a4100109d8380800020012802102205200128021441386c22066a220742c4decdf7cda6c89430370308200742aad9ebaed398dbfa41370300200128020c21082007420437022c20074210370224200741d98ac2800036022020074100360218200741ec8080800036021002402004418080808078470d00419a89c280004111418c8ac2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000bf50203027f017e057f23808080800041306b2201248080808000200141246a419c8ac28000410641e98ac280004123419c8ac28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a4100109d8380800020012802102205200128021441386c22066a220742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db00370300200128020c21082007420437022c2007420d370224200741c38ac2800036022020074100360218200741e98080800036021002402004418080808078470d00419a89c280004111418c8ac2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000bf50203027f017e057f23808080800041306b2201248080808000200141246a41d08ac28000410941e98ac280004123419c8ac28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a4100109d8380800020012802102205200128021441386c22066a220742f0a3fcacaeba9c956f370308200742e3beec81d3e996af917f370300200128020c21082007420437022c20074210370224200741d98ac2800036022020074100360218200741ea8080800036021002402004418080808078470d00419a89c280004111418c8ac2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a2001109983808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004121360220200041c880808000360218200041033a0000200141106a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a2001109983808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004120360220200041c880808000360218200041033a0000200141106a2480808080000bb00101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a2001109983808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c000370350200041c000360220200041c880808000360218200041033a0000200141106a2480808080000bb00101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a2001109983808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c000370350200041c100360220200041c880808000360218200041033a0000200141106a2480808080000bbf0604057f017e037f017e23808080800041d0006b2201248080808000200141306a418c8bc28000410f419b8bc28000410d418c8bc28000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a10a883808000200141086a41086a2001412c6a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a2207410036020020014280808080c000370218200141186a410010ad83808000200128021c200728020041246c6a220841003a00202008410936021c200841a88bc280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810ad83808000200128024821080b2001280244200841246c6a220841013a00202008410836021c200841b18bc2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810ad83808000200128022021080b200128021c200841246c6a220841023a00202008410e36021c200841b98bc28000360218200842043702102008420037020820084280808080800137020020012802202107200128021c2108200120012802183602202001200836021820012008200741246c6a41246a3602242001200836021c200141c0006a200141186a10a98380800002402005418080808078470d0041e88bc28000411141dc8cc2800010a181808000000b20012002360220200120033602182001200336021c2001200320044105746a360224200041c4006a200141186a10a883808000200141236a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a2001411f6a290000370000200141d0006a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710ae8380800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710af8380800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710b08380800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10ab83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a10ab83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10ab83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a10ab83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10ab83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bcc0403057f017e037f23808080800041d0006b2201248080808000200141246a41e18bc28000410741c88bc28000411941c88bc28000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702402001428080808080013702482001410c6a200141c0006a10a883808000200141086a2202200141206a28020036020020012001290218370300200128020c2103200128021021042001280214210520012902282106200128022421072001410036021420014280808080800137020c2001410c6a410010ac838080002001280210200128021441386c6a2208420437022c20084203370224200841ec8cc2800036022020084100360218200841b580808000360210200842e88488d0c0e3aebc13370308200842d7c9cb8fc1cf97db3e37030020012802142109200128021021082001200128020c360248200120083602402001200836024420012008200941386c6a41386a36024c200141306a200141c0006a10aa8380800002402007418080808078470d0041e88bc28000411141dc8cc2800010a181808000000b200120033602142001200436020c200120043602102001200420054105746a360218200041c4006a2001410c6a10a883808000200141176a200141306a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129033037000f2000200129000c370001200041086a2001410c6a41076a290000370000200141d0006a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710b78380800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710b88380800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bca0403057f017e037f23808080800041d0006b2201248080808000200141246a41808ec28000410641868ec28000412741808ec28000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702402001428080808080013702482001410c6a200141c0006a10b283808000200141086a2202200141206a28020036020020012001290218370300200128020c2103200128021021042001280214210520012902282106200128022421072001410036021420014280808080800137020c2001410c6a410010b6838080002001280210200128021441386c6a2208420437022c2008420f370224200841f08dc2800036022020084100360218200841ed80808000360210200842a4d2928ecac0faa432370308200842c4ccab9c81ebb4b8db0037030020012802142109200128021021082001200128020c360248200120083602402001200836024420012008200941386c6a41386a36024c200141306a200141c0006a10b38380800002402007418080808078470d0041ef8cc28000411141e08dc2800010a181808000000b200120033602142001200436020c200120043602102001200420054105746a360218200041c4006a2001410c6a10b283808000200141176a200141306a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129033037000f2000200129000c370001200041086a200141136a290000370000200141d0006a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10b583808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10b583808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10b583808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110b283808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004120360220200041c880808000360218200041033a0000200141106a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a10ba83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10ba83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10ba83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a10ba83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10ba83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b6e00200042e5f0b3f4e8a9b1b12a37030820004280808080c00037033820004280808080c000370350200041ee80808000360218200041023a0000200041106a4281ebc5ecd497b09a0a370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c880808000360218200041023a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710be8380800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710bf8380800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710bd8380800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bc90103057f027e017f20012802082102200128020022032104024020012802042205200128020c2206460d0020032104034002402005290300220742efb7e9de94adbae42685200541086a290300220842c785eb8b8b8eddc0618584500d0020052802102109200420083703082004200737030020042009360210200441186a21040b200541186a22052006470d000b0b200142888080808001370200200142808080808001370208200020033602042000200420036b41186e3602082000200241186c41186e3602000b920a04027f017e037f017e23808080800041d0006b2201248080808000200141286a41cc90c28000411141dd90c28000411141cc90c28000410010d882808000200141086a410036020020014280808080c00037030020012802282102200129022c2103200141106a41086a22044100360200200142808080808001370210200141106a410010bc838080002001280214200428020041386c6a2205420437022c200542033702242005419596c280003602202005410d36021c2005418896c28000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c370300200141c0006a41086a2206200428020041016a2205360200200120012902102207370340024020052007a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c200542033702242005419596c280003602202005410c36021c2005419896c28000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c3703002004200628020041016a2205360200200120012903402207370310024020052007a7470d00200141106a200510bc83808000200128021821050b2001280214200541386c6a2205420437022c2005420a370224200541d695c280003602202005410136021c200541d595c28000360218200541f080808000360210200542e0df9add94b69bd2ff00370308200542bcf58bd0d59aa7c9db00370300200141c0006a41086a2204200141106a41086a220628020041016a2205360200200120012903102207370340024020052007a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c20054227370224200541af96c280003602202005410b36021c200541a496c28000360218200541f180808000360210200542c6be9491c9dc9cca59370308200542c7e49fb5d2d4f6ebb47f3703002006200428020041016a2205360200200120012903402207370310024020052007a7470d00200141106a200510bc83808000200128021821050b2001280214200541386c6a2205420437022c2005420a370224200541e096c280003602202005410a36021c200541d696c28000360218200541f280808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200141c0006a41086a200141106a41086a28020041016a2205360200200120012903102207370340024020052007a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c2005420c370224200541ed95c280003602202005410d36021c200541e095c28000360218200541f380808000360210200542edb3cdfa95de98ad10370308200542cde181a596cd91e354370300200128024821042001280244210520012001280240360218200120053602102001200536021420012005200441386c6a41386a36021c200141346a200141106a10c48380800002402002418080808078470d0041d293c28000411141c494c2800010a181808000000b200042808080808001370244200041cc006a4100360200200141cb006a200141346a41086a2802003600002000413c6a200337020020002002360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437004320002001290040370001200041086a200141c7006a290000370000200141d0006a2480808080000bc40504027f017e037f017e23808080800041d0006b2201248080808000200141306a41ee90c28000410c41dd90c28000411141cc90c28000410010d882808000200141086a41086a410036020020014280808080c0003703082001280230210220012902342103200141186a41086a2204410036020020014280808080c000370218200141186a410010bb83808000200128021c200428020041246c6a220541003a00202005410c36021c200541fa90c280003602182005420437021020054200370208200542808080808001370200200141c0006a41086a2206200428020041016a2205360200200120012902182207370340024020052007a7470d00200141c0006a200510bb83808000200128024821050b2001280244200541246c6a220541013a00202005411d36021c2005418691c2800036021820054204370210200542003702082005428080808080013702002004200628020041016a2205360200200120012903402207370318024020052007a7470d00200141186a200510bb83808000200128022021050b200128021c200541246c6a220541023a00202005411b36021c200541a391c28000360218200542043702102005420037020820054280808080800137020020012802202104200128021c2105200120012802183602202001200536021820012005200441246c6a41246a3602242001200536021c200141c0006a200141186a10c28380800002402002418080808078470d0041d293c28000411141c494c2800010a181808000000b200042808080808001370244200041cc006a4100360200200141236a200141c0006a41086a2802003600002000413c6a200337020020002002360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a2001411f6a290000370000200141d0006a2480808080000b880504027f017e027f017e23808080800041d0006b2201248080808000200141286a41d091c28000411641dd90c28000411141cc90c28000410010d882808000200141086a410036020020014280808080c00037030020012802282102200129022c2103200141106a41086a22044100360200200142808080808001370210200141106a410010bc838080002001280214200428020041386c6a2205420437022c2005420a370224200541d695c280003602202005410136021c200541d595c28000360218200541f080808000360210200542e0df9add94b69bd2ff00370308200542bcf58bd0d59aa7c9db00370300200141c0006a41086a200428020041016a2205360200200120012902102206370340024020052006a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c2005420c370224200541ed95c280003602202005410d36021c200541e095c28000360218200541f380808000360210200542edb3cdfa95de98ad10370308200542cde181a596cd91e354370300200128024821042001280244210520012001280240360218200120053602102001200536021420012005200441386c6a41386a36021c200141346a200141106a10c48380800002402002418080808078470d0041d293c28000411141c494c2800010a181808000000b200042808080808001370244200041cc006a4100360200200141cb006a200141346a41086a2802003600002000413c6a200337020020002002360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437004320002001290040370001200041086a200141c7006a290000370000200141d0006a2480808080000bd00303027f017e037f23808080800041d0006b2201248080808000200141286a418392c28000411741dd90c28000411141cc90c28000410010d882808000200141086a2202410036020020014280808080c000370300200129022c21032001280228210420014100360218200142808080808001370210200141106a410010bc838080002001280214200128021841386c6a2205420437022c20054207370224200541ea96c2800036022020054100360218200541f480808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c500370300200128021821062001280214210520012001280210360218200120053602102001200536021420012005200641386c6a41386a36021c200141346a200141106a10c48380800002402004418080808078470d0041d293c28000411141c494c2800010a181808000000b200042808080808001370244200041cc006a4100360200200141cc006a200141346a41086a2802003600002000413c6a200337020020002004360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437004420002001290041370001200041086a200141c8006a290000370000200141d0006a2480808080000b930a04027f017e037f017e23808080800041d0006b2201248080808000200141286a419a92c28000410541dd90c28000411141cc90c28000410010d882808000200141086a410036020020014280808080c00037030020012802282102200129022c2103200141106a41086a22044100360200200142808080808001370210200141106a410010bc838080002001280214200428020041386c6a2205420437022c200542033702242005419596c280003602202005410b36021c200541f196c28000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c370300200141c0006a41086a2206200428020041016a2205360200200120012902102207370340024020052007a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c20054204370224200541fc94c280003602202005410a36021c200541fc96c28000360218200541f580808000360210200542b8b6d386cdcbfab1a07f370308200542e3a4fae3cee3d18d723703002004200628020041016a2205360200200120012903402207370310024020052007a7470d00200141106a200510bc83808000200128021821050b2001280214200541386c6a2205420437022c200542033702242005419596c280003602202005410836021c2005418697c28000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c370300200141c0006a41086a2204200141106a41086a220628020041016a2205360200200120012903102207370340024020052007a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c20054227370224200541af96c280003602202005410b36021c200541a496c28000360218200541f180808000360210200542c6be9491c9dc9cca59370308200542c7e49fb5d2d4f6ebb47f3703002006200428020041016a2205360200200120012903402207370310024020052007a7470d00200141106a200510bc83808000200128021821050b2001280214200541386c6a2205420437022c2005420a370224200541e096c280003602202005410a36021c200541d696c28000360218200541f280808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200141c0006a41086a200141106a41086a28020041016a2205360200200120012903102207370340024020052007a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c200542163702242005419497c280003602202005410636021c2005418e97c28000360218200541f680808000360210200542f2b3a2a5fbe78f9ac600370308200542b7e2f1ac9bf5d4dc827f370300200128024821042001280244210520012001280240360218200120053602102001200536021420012005200441386c6a41386a36021c200141346a200141106a10c48380800002402002418080808078470d0041d293c28000411141c494c2800010a181808000000b200042808080808001370244200041cc006a4100360200200141cb006a200141346a41086a2802003600002000413c6a200337020020002002360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437004320002001290040370001200041086a200141c7006a290000370000200141d0006a2480808080000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242a5e9e3ab9e929adc2c370318200242e9b494c69bdbc4d608370308200242ead283debcdebd93d800370300200241f780808000360210200241206a4293888c8f89fdc6ec9e7f370300200241286a41ef808080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a10c583808000200142888080808001370210200142808080808001370218200041c4006a200141106a10c3838080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bca0403057f017e037f23808080800041d0006b2201248080808000200141286a41b693c28000410641bc93c28000411641a092c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10c383808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c21062001280228210720014100360218200142808080808001370210200141106a410010bc838080002001280214200128021841386c6a2208420437022c2008420f370224200841f995c2800036022020084100360218200841f880808000360210200842a4d2928ecac0faa432370308200842c4ccab9c81ebb4b8db00370300200128021821092001280214210820012001280210360248200120083602402001200836024420012008200941386c6a41386a36024c200141346a200141c0006a10c48380800002402007418080808078470d0041d293c28000411141c494c2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10c3838080002001411b6a200141346a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242a5e9e3ab9e929adc2c37031820024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200241ef80808000360210200241206a4293888c8f89fdc6ec9e7f370300200241286a41ef808080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a10c583808000200142888080808001370210200142808080808001370218200041c4006a200141106a10c3838080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110c383808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004120360220200041c880808000360218200041033a0000200141106a2480808080000b9f0704057f017e037f017e23808080800041d0006b2201248080808000200141286a41a092c28000411041b092c28000411a41a092c28000410010d882808000200141106a41106a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10c383808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a22074100360200200142808080808001370210200141106a410010bc838080002001280214200728020041386c6a2208420437022c20084215370224200841e394c280003602202008410f36021c200841d494c28000360218200841b580808000360210200842e88488d0c0e3aebc13370308200842d7c9cb8fc1cf97db3e370300200141c0006a41086a2209200728020041016a220836020020012001290210220a37034002402008200aa7470d00200141c0006a200810bc83808000200128024821080b2001280244200841386c6a2208420437022c20084204370224200841fc94c280003602202008410436021c200841f894c28000360218200841f580808000360210200842b8b6d386cdcbfab1a07f370308200842e3a4fae3cee3d18d723703002007200928020041016a220836020020012001290340220a37031002402008200aa7470d00200141106a200810bc83808000200128021821080b2001280214200841386c6a2208420437022c2008420c3702242008418d95c280003602202008410d36021c2008418095c28000360218200841f980808000360210200842cd86adf4d1ba8a89763703082008428ad5e2d7bdbcbdc342370300200128021821072001280214210820012001280210360248200120083602402001200836024420012008200741386c6a41386a36024c200141346a200141c0006a10c48380800002402005418080808078470d0041d293c28000411141c494c2800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10c3838080002001411b6a200141346a41086a2802003600002000413c6a200637020020002005360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000b810604057f017e027f017e23808080800041d0006b2201248080808000200141286a41ca92c28000411741b092c28000411a41a092c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10c383808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a22074100360200200142808080808001370210200141106a410010bc838080002001280214200728020041386c6a2208420437022c20084215370224200841e394c280003602202008410f36021c200841d494c28000360218200841b580808000360210200842e88488d0c0e3aebc13370308200842d7c9cb8fc1cf97db3e370300200141c0006a41086a200728020041016a2208360200200120012902102209370340024020082009a7470d00200141c0006a200810bc83808000200128024821080b2001280244200841386c6a2208420437022c20084204370224200841fc94c280003602202008410436021c200841f894c28000360218200841f580808000360210200842b8b6d386cdcbfab1a07f370308200842e3a4fae3cee3d18d72370300200128024821072001280244210820012001280240360248200120083602402001200836024420012008200741386c6a41386a36024c200141346a200141c0006a10c48380800002402005418080808078470d0041d293c28000411141c494c2800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10c3838080002001411b6a200141346a41086a2802003600002000413c6a200637020020002005360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000b9c0704057f017e037f017e23808080800041d0006b2201248080808000200141286a41e192c28000411541b092c28000411a41a092c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10c383808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a22074100360200200142808080808001370210200141106a410010bc838080002001280214200728020041386c6a2208420437022c20084215370224200841e394c280003602202008410f36021c200841d494c28000360218200841b580808000360210200842e88488d0c0e3aebc13370308200842d7c9cb8fc1cf97db3e370300200141c0006a41086a2209200728020041016a220836020020012001290210220a37034002402008200aa7470d00200141c0006a200810bc83808000200128024821080b2001280244200841386c6a2208420437022c20084204370224200841fc94c280003602202008410436021c200841f894c28000360218200841f580808000360210200842b8b6d386cdcbfab1a07f370308200842e3a4fae3cee3d18d723703002007200928020041016a220836020020012001290340220a37031002402008200aa7470d00200141106a200810bc83808000200128021821080b2001280214200841386c6a2208420437022c2008420c3702242008418d95c280003602202008410d36021c2008418095c28000360218200841f980808000360210200842cd86adf4d1ba8a89763703082008428ad5e2d7bdbcbdc342370300200128021821072001280214210820012001280210360248200120083602402001200836024420012008200741386c6a41386a36024c200141346a200141c0006a10c48380800002402005418080808078470d0041d293c28000411141c494c2800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10c3838080002001411b6a200141346a41086a2802003600002000413c6a200637020020002005360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000baa0403057f017e027f23808080800041d0006b2201248080808000200141286a41f692c28000410941b092c28000411a41a092c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10c383808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c2106200128022821072001410036021820014280808080c000370210200141c0006a200141106a41ff92c28000410710d483808000200141106a200141c0006a418693c28000410e10d683808000200141346a200141106a419493c28000410c10d7838080002001200128023436021820012001280238220836021020012008200128023c41246c6a36021c20012008360214200141c0006a200141106a10c28380800002402007418080808078470d0041d293c28000411141c494c2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10c3838080002001411b6a200141c0006a41086a2802003600002000413c6a200637020020002007360238200041013a000020002001290300370350200041d8006a20022802003602002001200129024037001320002001290010370001200041086a200141106a41076a290000370000200141d0006a2480808080000bf80303057f017e027f23808080800041d0006b2201248080808000200141286a41a093c28000411441b092c28000411a41a092c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10c383808000200141086a2202200141106a41146a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c2106200128022821072001410036021820014280808080c000370210200141346a200141106a41b493c28000410210d5838080002001200128023436021820012001280238220836021020012008200128023c41246c6a36021c20012008360214200141c0006a200141106a10c28380800002402007418080808078470d0041d293c28000411141c494c2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10c3838080002001411b6a200141c0006a41086a2802003600002000413c6a200637020020002007360238200041013a000020002001290300370350200041d8006a20022802003602002001200129024037001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010bc838080002004280208200428020c220541386c6a2206420437022c200642103702242006419995c2800036022020064100360218200641fa80808000360210200642e1eab2d29ed8a59afd003703082006428b91acbbbb91f6d30b37030020042802042107200428020821080240200128020822062001280200470d002001200610bb83808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bf20303037f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010bc8380800020042802182005280200220641386c6a2205420437022c2005420a370224200541d695c280003602202005410136021c200541d595c28000360218200541f080808000360210200542e0df9add94b69bd2ff00370308200542bcf58bd0d59aa7c9db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610bc83808000200428021021060b200428020c200641386c6a2205420437022c2005420c370224200541ed95c280003602202005410d36021c200541e095c28000360218200541f380808000360210200542edb3cdfa95de98ad10370308200542cde181a596cd91e35437030020042802082108200428020c21090240200128020822052001280200470d002001200510bb83808000200128020821050b2001280204200541246c6a220541013a00202005200336021c200520023602182005410036021420054280808080c00037020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010bc838080002004280208200428020c220541386c6a2206420437022c20064217370224200641a995c2800036022020064100360218200641fb80808000360210200642beb6e3c9cf8cb1be2137030820064290e384e9e5949acd8f7f37030020042802042107200428020821080240200128020822062001280200470d002001200610bb83808000200128020821060b2001280204200641246c6a220641023a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbd0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010bc838080002004280208200428020c220541386c6a2206420437022c20064215370224200641c095c2800036022020064100360218200641fc80808000360210200642a8f8d28adee2a2ca163703082006429e8f89a5d7f0a0bf7d37030020042802042107200428020821080240200128020822062001280200470d002001200610bb83808000200128020821060b2001280204200641246c6a220641033a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110c383808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004120360220200041c880808000360218200041033a0000200141106a2480808080000b7700200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c000360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b7600200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c00037035020004120360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10db83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bf50203027f017e057f23808080800041306b2201248080808000200141246a41ac98c28000410641b298c28000411941ac98c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010dc8380800020012802102205200128021441386c22066a220742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db00370300200128020c21082007420437022c2007420f370224200741cb98c2800036022020074100360218200741fd8080800036021002402004418080808078470d0041aa97c280004111419c98c2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000bf50203027f017e057f23808080800041306b2201248080808000200141246a41da98c28000410941b298c28000411941ac98c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010dc8380800020012802102205200128021441386c22066a220742f0a3fcacaeba9c956f370308200742e3beec81d3e996af917f370300200128020c21082007420437022c20074212370224200741e398c2800036022020074100360218200741fe8080800036021002402004418080808078470d0041aa97c280004111419c98c2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000bf20203027f017e057f23808080800041306b2201248080808000200141246a41f899c28000410441fc99c28000411241f899c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010e283808000200128021022052001280214220641386c6a22074293888c8f89fdc6ec9e7f370308200742a5e9e3ab9e929adc2c370300200128020c21082007420437022c200742033702242007418e9ac2800036022020074100360218200741ef8080800036021002402004418080808078470d0041f598c28000411141e899c2800010a181808000000b20002001290300370350200042808080808001370244200020043602382000200536020820002008360204200041003a0000200041cc006a41003602002000413c6a20033702002000200641016a36020c200041d8006a2002280200360200200141306a2480808080000bf20203027f017e057f23808080800041306b2201248080808000200141246a41919ac28000410c41fc99c28000411241f899c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010e283808000200128021022052001280214220641386c6a22074293888c8f89fdc6ec9e7f370308200742a5e9e3ab9e929adc2c370300200128020c21082007420437022c200742033702242007418e9ac2800036022020074100360218200741ef8080800036021002402004418080808078470d0041f598c28000411141e899c2800010a181808000000b20002001290300370350200042808080808001370244200020043602382000200536020820002008360204200041003a0000200041cc006a41003602002000413c6a20033702002000200641016a36020c200041d8006a2002280200360200200141306a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10e183808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10e383808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10e383808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10e383808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a10e383808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bf10803067f057e027f23808080800041a0056b220324808080800020034200370340200342f9c2f89b91a3b3f0db00370338200342ebfa86dabfb5f6c11f3703302003429fd8f9d9c291da829b7f370328200342d1859aeffacf9487d100370320200342f1edf4f8a5a7fda7a57f370318200342abf0d3f4afeebcb73c370310200342bbceaaa6d8d0ebb3bb7f370308200342c892f795ffccf984ea00370300200341cf006a410041f900108a8e8080002104200341c8016a41073a0000200341cb006a41002800a39ac28000360000200341002800a09ac2800036024802400240200241fa00490d002004200141f90010848e8080001a20034280013703402003200341c8006a22054200420010bb80808000200141f9006a2201200241877f6a2202410776200241ff00712202456b22064107746a2107200241800120021b210402402006450d00200641077421020340200320032903404280017c370340200320014200420010bb8080800020014180016a2101200241807f6a22020d000b0b20052007200410848e8080001a0c010b20042001200210848e8080001a200241076a21040b200320043a00c80120034190026a200341d00110848e8080001a200320032903d002200341d8036a2d00002201ad7c3703d002200341d8026a210202402001418001460d00200220016a410041800120016b108a8e8080001a0b200341003a00d80320034190026a2002427f420010bb80808000200341a0046a41086a220120034190026a41086a290300370300200341a0046a41106a220220034190026a41106a290300370300200341a0046a41186a220420034190026a41186a290300370300200341a0046a41206a220620032903b002370300200341a0046a41286a220520034190026a41286a290300370300200341a0046a41306a220720034190026a41306a290300370300200341a0046a41386a220820034190026a41386a29030037030020032003290390023703a004200341e0036a41106a20022903002209370300200341e0036a41186a2004290300220a370300200341e0036a41206a2006290300220b370300200341e0036a41286a2005290300220c370300200341e0036a41306a2007290300220d370300200341d0016a41086a22022001290300370300200341d0016a41106a22042009370300200341d0016a41186a2206200a370300200341d0016a41206a2205200b370300200341d0016a41286a2207200c370300200341d0016a41306a220e200d370300200341d0016a41386a220f2008290300370300200320032903a0043703d00141002d00fca3c680001a024041c00041002802c8a3c680001181808080000022010d00410141c00010b280808000000b200120032903d001370000200041c00036020820002001360204200041c000360200200141386a200f290300370000200141306a200e290300370000200141286a2007290300370000200141206a2005290300370000200141186a2006290300370000200141106a2004290300370000200141086a2002290300370000200341a0056a2480808080000bca0403057f017e037f23808080800041d0006b2201248080808000200141286a41b69ac28000410941a79ac28000410f41a09ac28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10f483808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c21062001280228210720014100360218200142808080808001370210200141106a410010e4838080002001280214200128021841386c6a2208420437022c20084207370224200841809cc2800036022020084100360218200841ff80808000360210200842d886fac2c186f9c46f3703082008429cfcf2b9cfebc9bfa37f370300200128021821092001280214210820012001280210360248200120083602402001200836024420012008200941386c6a41386a36024c200141346a200141c0006a10f58380800002402007418080808078470d0041fd9ac28000411141f09bc2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10f4838080002001411b6a200141346a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110f483808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004104360220200041c880808000360218200041033a0000200141106a2480808080000bcd0403057f017e037f23808080800041d0006b2201248080808000200141286a41c09ac28000410e41ce9ac28000410741c09ac28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10f483808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c21062001280228210720014100360218200142808080808001370210200141106a410010e4838080002001280214200128021841386c6a2208420437022c20084207370224200841879cc28000360220200841003602182008418081808000360210200842b891b68c98adebcf61370308200842e7b0a091f3ed9c85c500370300200128021821092001280214210820012001280210360248200120083602402001200836024420012008200941386c6a41386a36024c200141346a200141c0006a10f58380800002402007418080808078470d0041fd9ac28000411141f09bc2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10f4838080002001411b6a200141346a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437001320002001290010370001200041086a200141106a41076a290000370000200141d0006a2480808080000bb50303057f017e017f23808080800041c0006b2201248080808000200141246a41d59ac28000410441ce9ac28000410741c09ac28000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702302001428080808080013702382001410c6a200141306a10f483808000200141086a2202200141206a28020036020020012001290218370300200128020c21032001280210210420012802142105200129022821062001280224210720014284808080c00037020c20014280808080c000370214200141306a2001410c6a10f38380800002402007418080808078470d0041fd9ac28000411141f09bc2800010a181808000000b200120033602142001200436020c200120043602102001200420054105746a360218200041c4006a2001410c6a10f483808000200141176a200141306a41086a2802003600002000413c6a200637020020002007360238200041013a000020002001290300370350200041d8006a20022802003602002001200129023037000f2000200129000c370001200041086a2001410c6a41076a290000370000200141c0006a2480808080000b950301037f0240200028020022022802002200413f4b0d00200041027421020240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20023a00000f0b0240200041ffff004b0d002000410274410172210202402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20023b00000f0b0240200041ffffffff034b0d002000410274410272210202402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20023600000f0b02402001280200220320012802082200470d0020012000410110b18280800020012802002103200128020821000b2001280204220420006a41033a00002001200041016a2200360208200228020021020240200320006b41034b0d0020012000410410b18280800020012802042104200128020821000b2001200041046a360208200420006a20023600000b7600200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c00037035020004120360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b7700200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c000360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b880504027f017e027f017e23808080800041d0006b2201248080808000200141286a41dc9ac28000410c41e89ac28000411541dc9ac28000410010d882808000200141086a410036020020014280808080c00037030020012802282102200129022c2103200141106a41086a22044100360200200142808080808001370210200141106a410010e4838080002001280214200428020041386c6a2205420437022c2005420c370224200541989cc280003602202005410a36021c2005418e9cc280003602182005418181808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200141c0006a41086a200428020041016a2205360200200120012902102206370340024020052006a7470d00200141c0006a200510e483808000200128024821050b2001280244200541386c6a2205420437022c20054208370224200541a99cc280003602202005410536021c200541a49cc280003602182005418281808000360210200542f0a3fcacaeba9c956f370308200542e3beec81d3e996af917f370300200128024821042001280244210520012001280240360218200120053602102001200536021420012005200441386c6a41386a36021c200141346a200141106a10f58380800002402002418080808078470d0041fd9ac28000411141f09bc2800010a181808000000b200042808080808001370244200041cc006a4100360200200141cb006a200141346a41086a2802003600002000413c6a200337020020002002360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437004320002001290040370001200041086a200141c7006a290000370000200141d0006a2480808080000b810201037f23808080800041206b22032480808080000240024002400240200241046a22040d00410121050c010b2004417f4c0d0141002d00fca3c680001a200441002802c8a3c68000118180808000002205450d020b20034100360214200320053602102003200436020c200320023602182003200341186a36021c2003411c6a2003410c6a10ed838080000240200328020c200328021422046b20024f0d002003410c6a2004200210b182808000200328021421040b200328021020046a2001200210848e8080001a200041086a200420026a3602002000200329020c370200200341206a2480808080000f0b10ae80808000000b4101200410b280808000000b6f00200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c880808000360218200041023a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710e78380800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710e68380800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710e58380800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000b6f002000428d9abfc787899e9d7f37030820004280808080c00037033820004280808080c0003703502000418381808000360218200041023a0000200041106a42aeabccc7cea8c39bcc00370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000be20101017f41002d00fca3c680001a0240413041002802c8a3c680001181808080000022010d004108413010b280808000000b200142e7b0a091f3ed9c85c500370318200142a8c7daf8defb9a8fc400370308200142a7b98ffce8cbcbd38b7f370300200141848180800036021020004280808080c00037033820004280808080c0003703502000410236020c2000200136020820004102360204200041043a0000200141206a42b891b68c98adebcf61370300200141286a418581808000360200200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c880808000360218200041023a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b950501077f23808080800041306b22012480808080002001410836020c200141b49dc2800036020841002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241b49dc28000360200200241046a410836020041b49dc28000410810d782808000450d0141002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d02200342a7b98ffce8cbcbd38b7f370308200341bd9dc28000360220200341848180800036021820034101360204200341bc9dc28000360200200341306a42b891b68c98adebcf61370300200341286a42e7b0a091f3ed9c85c500370300200341106a42a8c7daf8defb9a8fc400370300200341386a418581808000360200200341246a410136020020014100360218200142808080808001370210200141106a410010fe8380800020012802142204200128021841386c22056a220642bcc8e6aaf4e393df6c3703082006429de7d5c2a3c6909862370300200128021021072006410036023020064280808080c00037032820064100360220200641003602182006418681808000360210200041cc006a4102360200200041c8006a2003360200200041023602442000413c6a2002ad4280808080108437020020004101360238200041d8006a410036020020004280808080c0003703502000200541386a41386e36020c2000200436020820002007360204200041003a0000200141306a2480808080000f0b4104410810b280808000000b200241002802c0a3c68000118080808000002001411c6a42013702002001410236021420014180ddc18000360210200141b48080800036022c2001200141286a3602182001200141086a360228200141106a4190ddc1800010f680808000000b410841c00010b280808000000b7600200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c00037035020004108360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b800303027f017e057f23808080800041306b2201248080808000200141246a41c09dc28000410c41cc9dc28000410c41c09dc28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010fe8380800020012802102205200128021441386c22066a2207428485acdad4b59be240370308200742f4d89fc7ac8bcaf8db00370300200128020c21082007420437022c20074225370224200741dc9dc280003602202007410436021c200741d89dc28000360218200741878180800036021002402004418080808078470d0041b19cc28000411141a49dc2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000bcc0505027f017e037f017e017f23808080800041d0006b2201248080808000200141306a41819ec28000411441cc9dc28000410c41c09dc28000410010d882808000200141086a41086a410036020020014280808080c0003703082001280230210220012902342103200141186a41086a22044100360200200142808080808001370218200141186a410010fe83808000200128021c2004280200220541386c6a2206420437022c20064204370224200641999ec280003602202006410436021c200641959ec28000360218200641888180800036021020064298848fa1dab08ba174370308200642febac4ad81b6fafcb37f370300200141c0006a41086a200541016a2205360200200120012902182207370340024020052007a7470d00200141c0006a200510fe83808000200128024821050b2001280244200541386c6a2206420437022c20064204370224200641999ec280003602202006410b36021c2006419d9ec28000360218200641888180800036021020064298848fa1dab08ba174370308200642febac4ad81b6fafcb37f3703002004200541016a2206360200200120012903402207370318024020062007a72205470d00200141186a200610fe8380800020012802182105200128022021060b200128021c2204200641386c22086a220642aeb899d38addfa91213703082006429cc4c6fe96f1ecf5e0003703002006420437022c2006420c370224200641c09dc280003602202006410636021c200641a89ec28000360218200641898180800036021002402002418080808078470d0041b19cc28000411141a49dc2800010a181808000000b200042808080808001370244200020023602382000200436020820002005360204200041003a000020002001290308370350200041cc006a41003602002000413c6a2003370200200041d8006a200141106a2802003602002000200841386a41386e36020c200141d0006a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10fd83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000b8f04010a7f23808080800041c0006b220324808080800002400240024002402001280204220441216e2205200220052002491b22050d00410121060c010b200541bef0831f4b0d01200541216c2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b4100210720034100360214200320063602102003200536020c0240024002402002450d0020012802002105034020044121490d0220012004415f6a22043602042001200541216a2208360200200341186a41086a2209200541086a290000370300200341186a41106a220a200541106a290000370300200341186a41186a220b200541186a290000370300200341186a41206a220c200541206a2d00003a00002003200529000037031802402007200328020c470d002003410c6a200710838480800020032802102106200328021421070b2006200741216c6a22052003290318370000200541206a200c2d00003a0000200541186a200b290300370000200541106a200a290300370000200541086a20092903003700002003200741016a2207360214200821052002417f6a22020d000b0b2000200329020c370200200041086a2003410c6a41086a2802003602000c010b2000418080808078360200200328020c450d00200641002802c0a3c68000118080808000000b200341c0006a2480808080000f0b10ae80808000000b4101200710b280808000000beb0301097f23808080800041306b22032480808080000240024002400240200128020422044105762205200220052002491b22050d00410121060c010b200541ffffff1f4b0d0120054105742207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b410021072003410036020c20032006360208200320053602040240024002402002450d0020012802002105034020044120490d022001200441606a22043602042001200541206a2208360200200341106a41086a2209200541086a290000370300200341106a41106a220a200541106a290000370300200341106a41186a220b200541186a29000037030020032005290000370310024020072003280204470d00200341046a200710848480800020032802082106200328020c21070b200620074105746a22052003290310370000200541186a200b290300370000200541106a200a290300370000200541086a20092903003700002003200741016a220736020c200821052002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c010b20004180808080783602002003280204450d00200641002802c0a3c68000118080808000000b200341306a2480808080000f0b10ae80808000000b4101200710b280808000000beb0301097f23808080800041306b22032480808080000240024002400240200128020422044105762205200220052002491b22050d00410121060c010b200541ffffff1f4b0d0120054105742207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b410021072003410036020c20032006360208200320053602040240024002402002450d0020012802002105034020044120490d022001200441606a22043602042001200541206a2208360200200341106a41086a2209200541086a290000370300200341106a41106a220a200541106a290000370300200341106a41186a220b200541186a29000037030020032005290000370310024020072003280204470d00200341046a200710848480800020032802082106200328020c21070b200620074105746a22052003290310370000200541186a200b290300370000200541106a200a290300370000200541086a20092903003700002003200741016a220736020c200821052002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c010b20004180808080783602002003280204450d00200641002802c0a3c68000118080808000000b200341306a2480808080000f0b10ae80808000000b4101200710b280808000000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bea0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141bff0831f492104200141216c21050240024020030d00200241003602180c010b200241013602182002200341216c36021c200220002802043602140b200241086a20042005200241146a108284808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bea0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b22014180808020492104200141057421050240024020030d00200241003602180c010b200241013602182002200341057436021c200220002802043602140b200241086a20042005200241146a108284808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000ba90301057f23808080800041206b22022480808080000240024002402001422088a722030d002000410036020820004280808080103702000c010b20022003417f6a220436021820022001a7220541016a3602140240024002400240024020052d000022064103710e0400030102000b200641027621030c030b20034104490d0420022003417c6a3602182002200541046a360214200541036a2d000041187420052f000141087472200672220341808004490d04200341027621030c020b200641044f0d0320034105490d0320022003417b6a3602182002200541056a360214200528000122034180808080044f0d010c030b2004450d0220022003417e6a3602182002200541026a36021420052d000141087420067241ffff03712203418002490d02200341027621030b200241086a200241146a20031081848080002002280208418080808078460d0120002002290208370200200041086a200241086a41086a280200360200200541002802c0a3c68000118080808000000b200241206a2480808080000f0b41ae9ec28000412e2002411f6a41dc9ec2800041dc9fc28000108981808000000ba90301057f23808080800041206b22022480808080000240024002402001422088a722030d002000410036020820004280808080103702000c010b20022003417f6a220436021820022001a7220541016a3602140240024002400240024020052d000022064103710e0400030102000b200641027621030c030b20034104490d0420022003417c6a3602182002200541046a360214200541036a2d000041187420052f000141087472200672220341808004490d04200341027621030c020b200641044f0d0320034105490d0320022003417b6a3602182002200541056a360214200528000122034180808080044f0d010c030b2004450d0220022003417e6a3602182002200541026a36021420052d000141087420067241ffff03712203418002490d02200341027621030b200241086a200241146a200310ff838080002002280208418080808078460d0120002002290208370200200041086a200241086a41086a280200360200200541002802c0a3c68000118080808000000b200241206a2480808080000f0b41ae9ec28000412e2002411f6a41dc9ec2800041dc9fc28000108981808000000ba90301057f23808080800041206b22022480808080000240024002402001422088a722030d002000410036020820004280808080103702000c010b20022003417f6a220436021820022001a7220541016a3602140240024002400240024020052d000022064103710e0400030102000b200641027621030c030b20034104490d0420022003417c6a3602182002200541046a360214200541036a2d000041187420052f000141087472200672220341808004490d04200341027621030c020b200641044f0d0320034105490d0320022003417b6a3602182002200541056a360214200528000122034180808080044f0d010c030b2004450d0220022003417e6a3602182002200541026a36021420052d000141087420067241ffff03712203418002490d02200341027621030b200241086a200241146a20031080848080002002280208418080808078460d0120002002290208370200200041086a200241086a41086a280200360200200541002802c0a3c68000118080808000000b200241206a2480808080000f0b41ae9ec28000412e2002411f6a41dc9ec2800041dc9fc28000108981808000000ba80301057f23808080800041206b22022480808080002001280204210302400240024002400240024020012802082201410c6c41046a22040d00410121050c010b2004417f4c0d0141002d00fca3c680001a200441002802c8a3c68000118180808000002205450d020b20024100360214200220053602102002200436020c200220013602182002200241186a36021c2002411c6a2002410c6a10b5848080002001450d022001410c6c2106200341086a210403402004417c6a28020021032002200428020022013602182002200241186a36021c2002411c6a2002410c6a10b5848080000240200228020c200228021422056b20014f0d002002410c6a2005200110b182808000200228021421050b200228021020056a2003200110848e8080001a2002200520016a22013602142004410c6a2104200641746a22060d000c040b0b10ae80808000000b4101200410b280808000000b200228021421010b200228020c2105200228021021042000410c6a2001360200200041086a2004360200200020053602042000410136020020002001ad4220862004ad84370310200241206a2480808080000b02000b2100200128021441ec9fc280004105200141186a28020028020c118280808000000b0f002000280200200110e9808080000b120020014198a2c28000410210dd808080000b02000bd30101027f2001410c6a2802002102024002400240024002400240024020012802040e020001020b20020d01410121034100210141b8a1c2800021020c030b2002450d010b2000200110b8808080000f0b2001280200220128020021020240200128020422010d0041012103410021010c010b2001417f4c0d0141002d00fca3c680001a200141002802c8a3c68000118180808000002203450d020b20032002200110848e80800021022000200136020820002002360204200020013602000f0b10ae80808000000b4101200110b280808000000b02000b22002000410036020c2000200336020820002002360204200041b8a1c280003602000b040041000b21002001280214419aa2c280004105200141186a28020028020c118280808000000b8a0101017f23808080800041306b2201248080808000200120003602002001411c6a420137020020014101360214200141a0a2c280003602102001418c8180800036022c2001200141286a36021820012001360228200141046a200141106a108e84808000410141a8a2c2800041072001280208200128020c41002802d0a2c680001187808080000000000bb20201067f41022102024002402001280208220320012802102204470d000c010b024002400240024002400240200441016a2205450d0020032005490d01200128020421062001200536021002400240200620046a2d00000e020001080b200320056b4104490d07200441056a21072005417b4b0d0341002102200720034b0d040c060b200320056b4104490d06200441056a21072005417b4b0d0441012102200720034d0d05200720034190fbc08000109581808000000b417f20054190fbc08000109681808000000b200520034190fbc08000109581808000000b200520074190fbc08000109681808000000b200720034190fbc08000109581808000000b200520074190fbc08000109681808000000b20012007360210200620056a28000021070b20002007360204200020023602000b40002001ad4220862000ad84200235020842208620022802042201ad8410808080800002402002280200450d00200141002802c0a3c68000118080808000000b0b12002001ad4220862000ad841081808080000b9d0302047f027e23808080800041206b22052480808080004100210641002d00fca3c680001a024002404105410120031b220741002802c8a3c68000118180808000002208450d002002ad42208621090240024020030d00428080808010210a0c010b200820043600014280808080d000210a410121060b200820063a00000240024020092001ad84200a2008ad841082808080002209422088a722020d0041b8a1c28000210341b8a1c280002106410021010c010b41a489c08000210602402009a72203410171450d00200321010c010b20034101722101419489c0800021060b2005410036021c2005200136021820052002360214200520033602102005200636020c20052005410c6a1094848080002005280204210620052802002103200541186a20052802102005280214200528020c28020c1185808080000020034102460d01200841002802c0a3c68000118080808000002000200636020420002003360200200541206a2480808080000f0b4101200710b280808000000b41c8a1c28000412e2005410c6a41f8a1c280004188a2c28000108981808000000b08001083808080000b800303017f017e027f23808080800041306b22032480808080000240024002402002ad4220862001ad841084808080002204422088a72202450d00410021052003410036021c2003200236021420032004a722023602102003410136021c200320022002410172200241017122011b360218200341a489c08000419489c0800020011b36020c200341186a21060240024020022d00000e020001040b41002101410121050c030b200341206a2003410c6a10b28280800020032802202201450d01200341086a2003412c6a28020036020020032003290224370300410121050c020b2003420037021820032002360214200341b8a1c28000360210200341b8a1c2800036020c200341186a2106410021050b0b200620032802102003280214200328020c28020c11858080800000024020050d0041c8a1c28000412e2003410c6a41f8a1c280004188a2c28000108981808000000b20002001360200200020032903003702042000410c6a200341086a280200360200200341306a2480808080000b9e0203017f017e027f23808080800041206b2203248080808000024002402002ad4220862001ad841085808080002204422088a722050d0041b8a1c28000210241b8a1c280002101410021060c010b41a489c08000210102402004a72202410171450d00200221060c010b20024101722106419489c0800021010b2003410036021c2003200636021820032005360214200320023602102003200136020c20032003410c6a10bc84808000200341186a20032802102003280214200328020c28020c1185808080000002402003280200418180808078470d0041c8a1c28000412e2003410c6a41f8a1c280004188a2c28000108981808000000b20002003290200370200200041086a200341086a280200360200200341206a2480808080000ba30202017f017e23808080800041206b2206248080808000024002402002ad4220862001ad842004ad4220862003ad8420051086808080002207422088a722020d0041b8a1c28000210441b8a1c280002103410021010c010b41a489c08000210302402007a72204410171450d00200421010c010b20044101722101419489c0800021030b2006410036021c2006200136021820062002360214200620043602102006200336020c20062006410c6a10ba848080002006280204210320062802002104200641186a20062802102006280214200628020c28020c11858080800000024020044102470d0041c8a1c28000412e2006410c6a41f8a1c280004188a2c28000108981808000000b2000200336020420002004360200200641206a2480808080000b08001087808080000b4101017e024020011088808080002202422088a722010d002000410036020820004280808080103702000f0b20002001360208200020023e0204200020013602000b1c002001ad4220862000ad842003ad4220862002ad841089808080000b0800108a808080000b380020002002ad4220862001ad84108b80808000220229000837000820002002290000370000200241002802c0a3c68000118080808000000b5e0020002002ad4220862001ad84108c808080002202290000370000200041186a200241186a290000370000200041106a200241106a290000370000200041086a200241086a290000370000200241002802c0a3c68000118080808000000b380020002002ad4220862001ad84108d80808000220229000837000820002002290000370000200241002802c0a3c68000118080808000000b2e0020002002ad4220862001ad84108e808080002202290000370000200241002802c0a3c68000118080808000000b12002001ad4220862000ad84108f808080000b1c002001ad4220862000ad842003ad4220862002ad841090808080000bf70101037f23808080800041106b220324808080800020032001360200200341046a200210b9848080002003280204210420002003200335020c42208620032802082205ad841091808080002201290000370000200041206a200141206a2d00003a0000200041186a200141186a290000370000200041106a200141106a290000370000200041086a200141086a290000370000200141002802c0a3c680001180808080000002402004450d00200541002802c0a3c68000118080808000000b024020022802002200418080808078460d002000450d00200228020441002802c0a3c68000118080808000000b200341106a2480808080000b3a01017f23808080800041106b22022480808080002002200136020c20002002410c6a109280808000108684808000200241106a2480808080000bac0202017f017e23808080800041f0006b22052480808080002005200136020002400240200520022004ad4220862003ad841093808080002206422088a722010d002005420037020c200541b8a1c28000360208200541b8a1c280003602040c010b2005200136021c200520063e021820052001360214200541046a200541146a10bf808080000b200541d8006a41086a2201200541046a41086a2902003703002005200529020437035820054100360268200541146a200541d8006a10bb84808000200541e4006a200528025c2001280200200528025828020c11858080800000024020052d00144102470d0041c8a1c28000412e200541d8006a41f8a1c280004188a2c28000108981808000000b2000200541146a41c20010848e8080001a200541f0006a2480808080000b190020002002ad4220862001ad8420031094808080004101460be70101037f23808080800041106b220324808080800020032001360200200341046a200210b9848080002003280204210420002003200335020c42208620032802082205ad841095808080002201290000370000200041186a200141186a290000370000200041106a200141106a290000370000200041086a200141086a290000370000200141002802c0a3c680001180808080000002402004450d00200541002802c0a3c68000118080808000000b024020022802002200418080808078460d002000450d00200228020441002802c0a3c68000118080808000000b200341106a2480808080000b3a01017f23808080800041106b22022480808080002002200136020c20002002410c6a109680808000108784808000200241106a2480808080000bac0202017f017e23808080800041f0006b22052480808080002005200136020002400240200520022004ad4220862003ad841097808080002206422088a722010d002005420037020c200541b8a1c28000360208200541b8a1c280003602040c010b2005200136021c200520063e021820052001360214200541046a200541146a10bf808080000b200541d8006a41086a2201200541046a41086a2902003703002005200529020437035820054100360268200541146a200541d8006a10bd84808000200541e4006a200528025c2001280200200528025828020c11858080800000024020052d00144102470d0041c8a1c28000412e200541d8006a41f8a1c280004188a2c28000108981808000000b2000200541146a41c10010848e8080001a200541f0006a2480808080000b190020002002ad4220862001ad8420031098808080004101460be70101037f23808080800041106b220324808080800020032001360200200341046a200210b9848080002003280204210420002003200335020c42208620032802082205ad841099808080002201290000370000200041186a200141186a290000370000200041106a200141106a290000370000200041086a200141086a290000370000200141002802c0a3c680001180808080000002402004450d00200541002802c0a3c68000118080808000000b024020022802002200418080808078460d002000450d00200228020441002802c0a3c68000118080808000000b200341106a2480808080000b3a01017f23808080800041106b22022480808080002002200136020c20002002410c6a109a80808000108584808000200241106a2480808080000bac0202017f017e23808080800041f0006b22052480808080002005200136020002400240200520022004ad4220862003ad84109b808080002206422088a722010d002005420037020c200541b8a1c28000360208200541b8a1c280003602040c010b2005200136021c200520063e021820052001360214200541046a200541146a10bf808080000b200541d8006a41086a2201200541046a41086a2902003703002005200529020437035820054100360268200541146a200541d8006a10bd84808000200541e4006a200528025c2001280200200528025828020c11858080800000024020052d00144102470d0041c8a1c28000412e200541d8006a41f8a1c280004188a2c28000108981808000000b2000200541146a41c10010848e8080001a200541f0006a2480808080000b190020002002ad4220862001ad842003109c808080004101460bdd0203027f017e047f23808080800041306b220124808080800002400240200035020842208620002802042202ad84109d808080002203422088a722040d0020014200370210200141b8a1c2800036020c200141b8a1c280003602080c010b20012004360220200120033e021c20012004360218200141086a200141186a10bf808080000b200141186a41086a200141086a41086a2902002203370300200120012902083703184100210420014100360228200128021c210502402003a72206450d0020052d0000210720014101360228200741ff01712207450d004101410220074101461b21040b200141246a20052006200128021828020c1185808080000002402006450d0020044102460d0002402000280200450d00200241002802c0a3c68000118080808000000b200141306a24808080800020044100470f0b41c8a1c28000412e200141186a41f8a1c280004188a2c28000108981808000000bd60301077f23808080800041106b22022480808080000240024002400240024002402001280208220320012802102204470d00410121050c010b200441016a2206450d0120032006490d02200128020421072001200636021002400240024002400240200720046a2d000022084103710e0400020301000b20084102762108410021050c040b4101210520084104490d020c030b200241096a20083a000041012105200241013a000820022001360204200241003b010c0240200241046a2002410c6a410210b4848080000d0020022f010c2201410276210820014180024921050c030b0c020b200241096a20083a000041012105200241013a0008200220013602042002410036020c0240200241046a2002410c6a410410b4848080000d00200228020c220141027621082001418080044921050c020b0c010b200320066b4104490d00200441056a21052006417b4b0d03200520034b0d0420012005360210200720066a28000022084180808080044921050b2000200836020420002005360200200241106a2480808080000f0b417f20064190fbc08000109681808000000b200620034190fbc08000109581808000000b200620054190fbc08000109681808000000b200520034190fbc08000109581808000000bad0201037f20002d00042103200041003a0004024002400240024002400240024002402002450d00200341ff01710d010b41012103200028020022002802082204200028021022056b2002490d02200520026a22032005490d03200320044b0d042001200028020420056a200210848e8080001a200020033602100c010b2001200041056a2d00003a000041012103200028020022002802082204200028021022056b2002417f6a2202490d01200520026a22032005490d04200320044b0d05200141016a200028020420056a200210848e8080001a200020033602100b410021030b20030f0b200520034190fbc08000109681808000000b200320044190fbc08000109581808000000b200520034190fbc08000109681808000000b200320044190fbc08000109581808000000b950301037f0240200028020022022802002200413f4b0d00200041027421020240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20023a00000f0b0240200041ffff004b0d002000410274410172210202402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20023b00000f0b0240200041ffffffff034b0d002000410274410272210202402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20023600000f0b02402001280200220320012802082200470d0020012000410110b18280800020012802002103200128020821000b2001280204220420006a41033a00002001200041016a2200360208200228020021020240200320006b41034b0d0020012000410410b18280800020012802042104200128020821000b2001200041046a360208200420006a20023600000b0e00200020012002109e808080000b25002000417f6a41ff01712002ad4220862001ad842004ad4220862003ad84109f808080000b5601027f23808080800041106b2200248080808000024010a080808000220141ff01714106490d0041f19fc2800041342000410f6a41a8a0c2800041a8a1c28000108981808000000b200041106a24808080800020010bbb0301057f23808080800041206b2202248080808000024002400240024002402001280200418080808078460d00024002402001280208220341056a2204450d002004417f4c0d044100210541002d00fca3c680001a200441002802c8a3c68000118180808000002206450d05200220063602102002200436020c0c010b20024100360214200242808080801037020c2002410c6a4100410110b18280800020022802102106200228021421050b200620056a41013a00002002200541016a36021420012802042104200220033602182002200241186a36021c2002411c6a2002410c6a10b5848080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a2002200120036a3602140c010b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d03200220013602102002410136020c200141003a0000200241013602140b2000200229020c370200200041086a2002410c6a41086a280200360200200241206a2480808080000f0b10ae80808000000b4101200410b280808000000b4101410110b280808000000bec0101067f410221020240024002400240024002402001280208220320012802102204470d000c010b200441016a2205450d0120032005490d02200128020421062001200536021002400240200620046a2d00000e020001020b410021020c010b200320056b4104490d00200441056a21072005417b4b0d03200720034b0d0420012007360210200620056a2800002107410121020b20002007360204200020023602000f0b417f20054190fbc08000109681808000000b200520034190fbc08000109581808000000b200520074190fbc08000109681808000000b200720034190fbc08000109581808000000bef0101057f41022102024002400240024002402001280208220320012802102204460d00200441016a2205450d0120032005490d02200128020421062001200536021002400240200620046a2d00000e020001020b200041003a00000f0b200320056b41c100490d00200441c2006a2104200541be7f4b0d03200420034b0d042001200436021041012102200041016a200620056a41c10010848e8080001a0b200020023a00000f0b417f20054190fbc08000109681808000000b200520034190fbc08000109581808000000b200520044190fbc08000109681808000000b200420034190fbc08000109581808000000ba80301067f23808080800041106b2202248080808000024002400240024002400240024002402001280208220320012802102204460d00200441016a2205450d02200520034b0d032001280204210320012005360210024002400240200320046a2d00000e020102000b20004181808080783602000c030b20004180808080783602000c020b200241086a200110b38480800020022802080d0020012802082205200128021022036b200228020c2204490d000240024020040d00410121060c010b2004417f4c0d05200441002802c8a3c68000118180808000002206450d06200641002004108a8e8080001a0b200320046a22072003490d06200720054b0d072006200128020420036a200410848e8080002103200020043602002001200736021020002004ad4220862003ad843702040c010b20004181808080783602000b200241106a2480808080000f0b417f20054190fbc08000109681808000000b200520034190fbc08000109581808000000b10ae80808000000b4101200410b280808000000b200320074190fbc08000109681808000000b200720054190fbc08000109581808000000bda0201057f41022102024002400240024002402001280208220320012802102204460d00200441016a2205450d0120032005490d02200128020421062001200536021002400240200620046a2d00000e020001020b200041003a00000f0b200320056b41c000490d00200441c1006a2104200541bf7f4b0d03200420034b0d04200120043602102000200620056a2201290000370001200041096a200141086a290000370000200041116a200141106a290000370000200041196a200141186a290000370000200041216a200141206a290000370000200041296a200141286a290000370000200041316a200141306a290000370000200041396a200141386a290000370000410121020b200020023a00000f0b417f20054190fbc08000109681808000000b200520034190fbc08000109581808000000b200520044190fbc08000109681808000000b200420034190fbc08000109581808000000b0a00200010a1808080000b0a00200010a2808080000bb60202017f017e23808080800041306b2208248080808000024002402002ad4220862001ad842004ad4220862003ad842006ad4220862005ad84200710a3808080002209422088a722060d0020084200370210200841b8a1c2800036020c200841b8a1c280003602080c010b20082006360220200820093e021c20082006360218200841086a200841186a10bf808080000b200841186a41086a2205200841086a41086a29020037030020082008290208370318200841003602282008200841186a10ba848080002008280204210420082802002106200841246a200828021c2005280200200828021828020c11858080800000024020064102470d0041c8a1c28000412e200841186a41f8a1c280004188a2c28000108981808000000b2000200436020420002006360200200841306a2480808080000b26002001ad4220862000ad842003ad4220862002ad842005ad4220862004ad8410a4808080000b9a0303017f017e037f23808080800041206b2203248080808000200341086a20011088848080002000200329031820032903102204200328020822051b200210a5808080002202290000370000200041186a200241186a290000370000200041106a200241106a290000370000200041086a200241086a290000370000200241002802c0a3c680001180808080000002402005450d00200328020c450d002004a741002802c0a3c68000118080808000000b20012802042106024020012802082200450d002000410171210741002102024020004101460d002000417e7121052006210041002102034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002005200241026a2202470d000b0b2007450d0020062002410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b02402001280200450d00200641002802c0a3c68000118080808000000b200341206a2480808080000b12002001ad4220862000ad8410a6808080000b12002001ad4220862000ad8410a7808080000bad0202017f017e23808080800041c0006b2203248080808000024002402002ad4220862001ad8410a8808080002204422088a722020d0020034200370214200341b8a1c28000360210200341b8a1c2800036020c0c010b20032002360230200320043e022c200320023602282003410c6a200341286a10bf808080000b200341286a41086a22022003410c6a41086a2902003703002003200329020c370328200341003602382003411c6a200341286a10bc84808000200341346a200328022c2002280200200328022828020c118580808000000240200328021c418180808078470d0041c8a1c28000412e200341286a41f8a1c280004188a2c28000108981808000000b2000200329021c370200200041086a2003411c6a41086a280200360200200341c0006a2480808080000b8103010c7f2380808080004180026b220224808080800020012802082203410774220441f8006e2105200128020022062107024020012802042208200128020c2209460d00200241f4016a220a41086a210b200621070340200120084180016a220c3602042008280270220d418080808078460d0120024180016a200841f00010848e8080001a200b200841fc006a280200360200200a20082902743702002002200d3602f001200241086a20024180016a10d0848080002007200241086a41f80010848e80800041f8006a2107200c2108200c2009470d000b0b200110cb8480800020062108024002402003450d00200621082004200541f8006c220c460d00024020040d00410821080c010b200c41002802c8a3c68000118180808000002208450d0120082006200c10848e8080001a200641002802c0a3c68000118080808000000b20002008360204200020053602002000200720066b41f8006e360208200110cc8480800020024180026a2480808080000f0b4108200c10b280808000000be70304077f017e057f017e200128020c220221032001280200220421050240200128020422062002460d002004210503402006220741386a21060240200728022c2208418080808078470d00200621030c020b200741106a29030021092007280224220a210302402007280228220b450d00200a200b410574220b6a210c200a210303402003280200450d01200341206a2103200b41606a220b0d000b200c21030b2007280220210b2007280218210c2007280204210d2007280200210e2007290330210f200520072903083703082005200f3703302005200836022c2005200a3602242005200c3602182005200d3602042005200e360200200541106a20093703002005200b41ffffff3f7136022020052003200a6b410576360228200541386a210520062002470d000b200221030b20012802082107200142888080808001370200200142808080808001370208200220036b41386e210b024020022003460d00200341306a210303400240200341706a280200450d00200341746a28020041002802c0a3c68000118080808000000b02402003417c6a280200450d00200328020041002802c0a3c68000118080808000000b200341386a2103200b417f6a220b0d000b0b200020043602042000200520046b41386e3602082000200741386c41386e3602000be704020a7f017e23808080800041c0006b220224808080800020012802082103200128020c220421052001280200220621070240200128020422082004460d002002411c6a210920062107024003402008220a280200220b418080808078460d01200241086a2205200a41146a2802003602002002200a29020c370300200a290218210c2002200a2802042208200a28020841386c6a36023c2002200b3602382002200836023420022008360230200241106a200241306a10c784808000200241106a41186a2208200c370300200941086a200528020036020020092002290300370200200741186a2008290300370200200741106a200241106a41106a290300370200200741086a200241106a41086a29030037020020072002290310370200200741206a2107200a41206a22082004470d000b0b200a41206a21050b20014284808080c00037020020014280808080c000370208024020042005460d00200420056b41057621044100210b034002402005200b4105746a22092802082208450d00200928020441306a210a03400240200a41706a280200450d00200a41746a28020041002802c0a3c68000118080808000000b0240200a417c6a280200450d00200a28020041002802c0a3c68000118080808000000b200a41386a210a2008417f6a22080d000b0b02402009280200450d00200928020441002802c0a3c68000118080808000000b0240200928020c450d002009410c6a28020441002802c0a3c68000118080808000000b200b41016a220b2004470d000b0b200020063602042000200341ffffff3f713602002000200720066b410576360208200241c0006a2480808080000bc503010b7f23808080800041e0016b220224808080800020012802082103200128020c220421052001280200220621070240200128020422082004460d00200241146a2109200241106a210a4100210502400340200620056a2107200820056a220b280200220c4102460d012009200b41046a41e40010848e8080001a2002200c3602102002200736020c20022006360208200241f8006a200a10d1848080002007200241f8006a41e80010848e8080001a200541e8006a2105200b41e8006a2004470d000b200620056a21070b200b41e8006a21050b200142888080808001370200200142808080808001370208200420056b41e8006e210b024020042005460d00034002402005280200450d00200541046a280200450d00200541086a28020041002802c0a3c68000118080808000000b0240200541c8006a280200450d00200541cc006a28020041002802c0a3c68000118080808000000b0240200541d4006a280200450d00200541d8006a28020041002802c0a3c68000118080808000000b200541e8006a2105200b417f6a220b0d000b0b200020063602042000200720066b41e8006e3602082000200341e8006c41e8006e360200200241e0016a2480808080000be00301067f024020002802082201450d00200028020421024100210303400240200220034107746a22042802502200418080808078460d00200441d0006a21050240200441d8006a2802002206450d00200441d4006a2802002100034002402000280200450d00200041046a280200450d00200041086a28020041002802c0a3c68000118080808000000b0240200041c8006a280200450d00200041cc006a28020041002802c0a3c68000118080808000000b0240200041d4006a280200450d00200041d8006a28020041002802c0a3c68000118080808000000b200041e8006a21002006417f6a22060d000b200528020021000b2000450d00200528020441002802c0a3c68000118080808000000b0240200441ec006a2802002206450d00200441e8006a28020041306a210003400240200041706a280200450d00200041746a28020041002802c0a3c68000118080808000000b02402000417c6a280200450d00200028020041002802c0a3c68000118080808000000b200041386a21002006417f6a22060d000b0b02402004280264450d00200441e4006a28020441002802c0a3c68000118080808000000b02402004280270450d00200441f0006a28020441002802c0a3c68000118080808000000b200341016a22032001470d000b0b0bfe0301067f200028020c210120004280808080800137020820002802042102200042888080808001370200024020012002460d00200120026b41077621034100210403400240200220044107746a22052802502200418080808078460d000240200541d0006a22062802082201450d0020062802042100034002402000280200450d00200041046a280200450d00200041086a28020041002802c0a3c68000118080808000000b0240200041c8006a280200450d00200041cc006a28020041002802c0a3c68000118080808000000b0240200041d4006a280200450d00200041d8006a28020041002802c0a3c68000118080808000000b200041e8006a21002001417f6a22010d000b200628020021000b2000450d00200628020441002802c0a3c68000118080808000000b0240200541ec006a2802002201450d00200541e8006a28020041306a210003400240200041706a280200450d00200041746a28020041002802c0a3c68000118080808000000b02402000417c6a280200450d00200028020041002802c0a3c68000118080808000000b200041386a21002001417f6a22010d000b0b0240200541e4006a2200280200450d00200028020441002802c0a3c68000118080808000000b02402005280270450d00200541f0006a28020441002802c0a3c68000118080808000000b200441016a22042003470d000b0b0b820401077f0240200028020c220120002802042202460d00200120026b41077621034100210403400240200220044107746a22052802502201418080808078460d000240200541d0006a22062802082207450d0020062802042101034002402001280200450d00200141046a280200450d00200141086a28020041002802c0a3c68000118080808000000b0240200141c8006a280200450d00200141cc006a28020041002802c0a3c68000118080808000000b0240200141d4006a280200450d00200141d8006a28020041002802c0a3c68000118080808000000b200141e8006a21012007417f6a22070d000b200628020021010b2001450d00200628020441002802c0a3c68000118080808000000b0240200541ec006a2802002207450d00200541e8006a28020041306a210103400240200141706a280200450d00200141746a28020041002802c0a3c68000118080808000000b02402001417c6a280200450d00200128020041002802c0a3c68000118080808000000b200141386a21012007417f6a22070d000b0b0240200541e4006a2201280200450d00200128020441002802c0a3c68000118080808000000b02402005280270450d00200541f0006a28020441002802c0a3c68000118080808000000b200441016a22042003470d000b0b02402000280208450d00200028020041002802c0a3c68000118080808000000b0baa070b057f017e017f027e017f027e0f7f017e017f017e037f23808080800041c0006b2202248080808000200141cc006a2802002103200128024821044180808080782105024020012802502206418080808078460d00200141dc006a29020021072002200141d4006a2802002208200141d8006a28020041e8006c6a36023c200220063602382002200836023420022008360230200241186a200241306a10c984808000200241106a20073703002002200229021c370308200228021821050b200141206a2903002109200141086a290300210a200141286a280200210b2001290318210c2001290300210d2001280264210e2001280210210f200141e8006a280200221021110240200141ec006a2802002206450d002010200641386c22126a2111200241306a41046a2108200241186a41046a21134100211402400340201020146a2206412c6a22152802002216418080808078460d01200641306a22172903002107200641046a22182802002119200641186a221a280200211b200641086a221c290300211d200641106a221e290300211f20062802002120200241186a41086a200641246a222129020037030020022006411c6a2222290200370318200841086a201341086a28020036020020082013290200370200201e201f370300201c201d370300201a201b3602002018201936020020062020360200202220022902303702002021200241306a41086a29020037020020152016360200201720073702002012201441386a2214470d000c020b0b0240201241486a2014460d00200641e8006a2108201220146b41486a41386e211403400240200841706a280200450d00200841746a28020041002802c0a3c68000118080808000000b02402008417c6a280200450d00200828020041002802c0a3c68000118080808000000b200841386a21082014417f6a22140d000b0b200621110b2000200d3703002000200c37031820002005360250200020043602482000200f3602102000200a370308200041206a200937030020002001290330370330200041e8006a201036020020002001290370370370200041cc006a2003360200200041d4006a2002290308370200200041286a200b360200200020012d007c3a007c200041386a200141386a290300370300200041ec006a201120106b41386e3602002000200e41386c41386e360264200041f8006a200141f8006a280200360200200041dc006a200241086a41086a290300370200200041c0006a200141c0006a280200360200200241c0006a2480808080000b870c03067f107e137f2380808080004190046b22032480808080000240024002400240200241726a0e020102000b2000418780808078360200200141e8016a10ca84808000024020012802e801450d00200141ec016a28020041002802c0a3c68000118080808000000b0240200141d8016a280200450d00200141dc016a28020041002802c0a3c68000118080808000000b200141f8016a28020021040240200141fc016a2802002205450d004100210603400240200420064105746a22072802082208450d00200728020441306a210203400240200241706a280200450d00200241746a28020041002802c0a3c68000118080808000000b02402002417c6a280200450d00200228020041002802c0a3c68000118080808000000b200241386a21022008417f6a22080d000b0b02402007280200450d00200728020441002802c0a3c68000118080808000000b0240200728020c450d002007410c6a28020441002802c0a3c68000118080808000000b200641016a22062005470d000b0b20012802f401450d02200441002802c0a3c68000118080808000000c020b2003200141800210848e80800022024194036a200210cf84808000200241868080807836029003200241edcad18b063602f003200020024190036a41e40010848e8080001a0c010b200141c8016a2903002109200141b0016a290300210a20014198016a290300210b20014180016a290300210c200141d0006a290300210d200141386a290300210e200141206a290300210f200141086a290300211020012903c001211120012903a80121122001290390012113200129037821142001290348211520012903302116200129031821172001290300211820012802fc01211920012802f801211a20012802f401211b20012d00e401211c20012802e001211d20012802dc01211e20012802d801211f20012802d001212020012802b801212120012802a001212220012802880121232001280258212420012802402125200128022821262001280210212720012802e8012128200320012802ec01220720012802f001220841077422046a222936029802200320283602940220032007360290022003200736028c022007210202402008450d00200341086a210541002108200341fc006a222a41086a212b024002400340200720086a220241f0006a2802002201418080808078460d012005200241f00010848e808000210620032007360200202b200241fc006a280200360200202a200241f4006a290200370200200320013602782003200236020420034190036a200610cd84808000200220034190036a41800110848e8080001a200420084180016a2208470d000b200720086a21020c010b20024180016a21290b20032029360290020b2003418c026a10cb848080002003202841ffffff0f713602800220032007360284022003200220076b410776360288022003418c026a10cc84808000201e21020240201d450d00201e201d41386c6a2107201d41386c2108201e210203402002280200450d01200241386a2102200841486a22080d000b200721020b200341d0006a2009370300200341386a200a370300200341206a200b370300200341e4006a201e360200200341d8006a2020360200200341c0006a2021360200200341286a2022360200200341e8006a2002201e6b41386e360200200320113703482003201237033020032013370318200320143703002003201c3a006c200320233602102003200c3703082003201f41386c41386e3602602003201b3602b0022003201a3602ac022003201a3602a8022003201a20194105746a3602b4022003419c026a200341a8026a10c884808000200341b8026a41386a200d370300200341b8026a41206a200e370300200341b8026a41c0006a2024360200200341b8026a41286a2025360200200320153703e802200320163703d0022003200f3703c002200320173703b802200320263602c8022003410036028c03200341003602840320034190036a20034180026a20032018201020272003419c026a200341b8026a20034184036a10e081808000200341edcad18b063602f003200020034190036a41e40010848e8080001a0b20034190046a2480808080000be40405027f017e037f017e037f23808080800041d0006b2202248080808000200220012802e8013602202002200141ec016a280200220336021c2002200336021820022003200141f0016a2802004107746a3602242002410c6a200241186a10c684808000200141e8006a2903002104200141e4016a2d00002105200141d8016a2802002106200141f0006a280200210720012903602108200141dc016a280200220921030240200141e0016a280200220a450d002009200a41386c220a6a210b2009210303402003280200450d01200341386a2103200a41486a220a0d000b200b21030b200241c4006a2009360200200241c8006a200320096b41386e36020020022008370328200220053a004c20022007360238200220043703302002200641386c41386e36024020002002410c6a200241286a2001290300200141086a290300200128021010ce81808000200141f8016a28020021050240200141fc016a2802002206450d004100210003400240200520004105746a2209280208220a450d00200928020441306a210303400240200341706a280200450d00200341746a28020041002802c0a3c68000118080808000000b02402003417c6a280200450d00200328020041002802c0a3c68000118080808000000b200341386a2103200a417f6a220a0d000b0b02402009280200450d00200928020441002802c0a3c68000118080808000000b0240200928020c450d002009410c6a28020441002802c0a3c68000118080808000000b200041016a22002006470d000b0b024020012802f401450d00200541002802c0a3c68000118080808000000b200241d0006a2480808080000bb2070b057f017e017f027e017f027e0f7f017e017f017e037f23808080800041c0006b2202248080808000200141cc006a2802002103200128024821044180808080782105024020012802502206418080808078460d00200141dc006a29020021072002200141d4006a2802002208200141d8006a28020041e8006c6a36023c200220063602382002200836023420022008360230200241186a200241306a10c984808000200241106a20073703002002200229021c370308200228021821050b200141206a2903002109200141086a290300210a200141286a280200210b2001290318210c2001290300210d2001280264210e2001280210210f200141e8006a280200221021110240200141ec006a2802002206450d002010200641386c22126a2111200241306a41046a2108200241186a41046a21134100211402400340201020146a2206412c6a22152802002216418080808078460d01200641306a22172903002107200641046a22182802002119200641186a221a280200211b200641086a221c290300211d200641106a221e290300211f20062802002120200241186a41086a200641246a222129020037030020022006411c6a2222290200370318200841086a201341086a28020036020020082013290200370200201e201f370300201c201d370300201a201b3602002018201936020020062020360200202220022902303702002021200241306a41086a29020037020020152016360200201720073702002012201441386a2214470d000c020b0b0240201241486a2014460d00200641e8006a2108201220146b41486a41386e211403400240200841706a280200450d00200841746a28020041002802c0a3c68000118080808000000b02402008417c6a280200450d00200828020041002802c0a3c68000118080808000000b200841386a21082014417f6a22140d000b0b200621110b2000200c3703182000200d37030020002005360250200020043602482000200f36021020002001290330370330200041206a20093703002000200a370308200041e8006a2010360200200041cc006a2003360200200041d4006a2002290308370200200020012d007c3a0070200041286a200b360200200041386a200141386a290300370300200041ec006a201120106b41386e3602002000200e41386c41386e360264200041dc006a200241086a41086a290300370200200041c0006a200141c0006a28020036020002402001280270450d00200141f4006a28020041002802c0a3c68000118080808000000b200241c0006a2480808080000bb80205067f017e027f017e027f200141c4006a280200210220012d00602103200128024021040240024020012802000d00200141186a280200210520012802082106410021070c010b200141306a2903002108200141086a2802002106200141386a2802002109200141206a280200210a2001290328210b200128021c210c200128021821052001280204210d410121070b2000200b370328200020033a006020002004360240200020093602382000200a3602202000200c36021c20002005360218200020063602082000200d36020420002007360200200041306a20083703002000200129034837034820002001290254370254200041c4006a2002360200200020012903103703102000200128020c36020c200041d0006a200141d0006a280200360200200041dc006a200141dc006a2802003602000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c200642123702242006418ba5c2800036022020064100360218200641bc81808000360210200642c9a784c5fedbb0fa25370308200642efa8a2ace59194fdb37f37030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c20064202370224200641e0a4c2800036022020064100360218200641c880808000360210200642e6ed8d82cc91adcb05370308200642dbd791d5c2919eaecd0037030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641073a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bdc0303037f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010e18480800020042802182005280200220641386c6a2205420437022c20054211370224200541b0a4c2800036022020054100360218200541bd81808000360210200542d886fac2c186f9c46f3703082005429cfcf2b9cfebc9bfa37f370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610e184808000200428021021060b200428020c200641386c6a2205420437022c20054207370224200541c1a4c2800036022020054100360218200541be81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c50037030020042802082108200428020c21090240200128020822052001280200470d002001200510e084808000200128020821050b2001280204200541246c6a220541053a00202005200336021c200520023602182005410036021420054280808080c00037020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000bbd0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c2006420b370224200641e7a4c2800036022020064100360218200641bf81808000360210200642878ff9fb82dfe1a82a370308200642949ecee4d5a9a0866437030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641033a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c20064207370224200641c1a4c2800036022020064100360218200641be81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c2006420a370224200641f2a4c2800036022020064100360218200641c0818080003602102006428d999faad5ccf2dbca00370308200642a7cde9f3c9fe9ebfe30037030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641073a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbd0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c200642123702242006419da5c2800036022020064100360218200641c18180800036021020064283e8c09cb8d3f4c01f370308200642dcffb3b186d2b1f50637030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bdc0303037f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010e18480800020042802182005280200220641386c6a2205420437022c20054211370224200541b0a4c2800036022020054100360218200541bd81808000360210200542d886fac2c186f9c46f3703082005429cfcf2b9cfebc9bfa37f370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610e184808000200428021021060b200428020c200641386c6a2205420437022c20054207370224200541c1a4c2800036022020054100360218200541be81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c50037030020042802082108200428020c21090240200128020822052001280200470d002001200510e084808000200128020821050b2001280204200541246c6a220541063a00202005200336021c200520023602182005410036021420054280808080c00037020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c2006420f370224200641fca4c2800036022020064100360218200641c281808000360210200642f68af3a9c7b285af3c37030820064296b2f4978ff1ee81bf7f37030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641083a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c20064202370224200641e0a4c2800036022020064100360218200641c880808000360210200642e6ed8d82cc91adcb05370308200642dbd791d5c2919eaecd0037030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641023a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c20064212370224200641baa6c2800036022020064100360218200641c381808000360210200642c69182ab97bfe490ac7f37030820064286a3ebac9a8ef1cfee0037030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641093a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bdc0303037f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010e18480800020042802182005280200220641386c6a2205420437022c20054211370224200541b0a4c2800036022020054100360218200541bd81808000360210200542d886fac2c186f9c46f3703082005429cfcf2b9cfebc9bfa37f370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610e184808000200428021021060b200428020c200641386c6a2205420437022c20054207370224200541c1a4c2800036022020054100360218200541be81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c50037030020042802082108200428020c21090240200128020822052001280200470d002001200510e084808000200128020821050b2001280204200541246c6a220541043a00202005200336021c200520023602182005410036021420054280808080c00037020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141146c2104200141e7cc99334941027421050240024020030d00200241003602180c010b200241043602182002200341146c36021c200220002802043602140b200241086a20052004200241146a10de84808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a10de84808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10de84808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bf00101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b2202410c6c2104200241abd5aad5004941027421050240024020010d00200341003602180c010b2003410436021820032001410c6c36021c200320002802043602140b200341086a20052004200341146a10de84808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a10de84808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10de84808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10de84808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b810201037f23808080800041206b22032480808080000240024002400240200241046a22040d00410121050c010b2004417f4c0d0141002d00fca3c680001a200441002802c8a3c68000118180808000002205450d020b20034100360214200320053602102003200436020c200320023602182003200341186a36021c2003411c6a2003410c6a10fb848080000240200328020c200328021422046b20024f0d002003410c6a2004200210b182808000200328021421040b200328021020046a2001200210848e8080001a200041086a200420026a3602002000200329020c370200200341206a2480808080000f0b10ae80808000000b4101200410b280808000000b70002000428ca3c7fa85a49cf2a17f37030820004280808080c00037033820004280808080c000370350200041c481808000360218200041023a0000200041106a42e0f4e1e1b7dafaaf8d7f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c880808000360218200041023a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042e7b0a091f3ed9c85c50037030820004280808080c00037033820004280808080c000370350200041be81808000360218200041023a0000200041106a42b891b68c98adebcf61370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710e38480800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710e48480800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710e58480800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bf80701047f23808080800041206b220224808080800020024100360214200242808080801037020c02400240024002400240024020012802000e050001020304000b2001410c6a280200210320012802082104200128020421052002410c6a4100410110b1828080002002280210200228021422016a41063a00002002200141016a22013602140240200228020c20016b41034b0d002002410c6a2001410410b182808000200228021421010b200228021020016a20052800003600002002200141046a360214200220033602182002200241186a36021c2002411c6a2002410c6a10fb848080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a200120036a21010c040b2001410c6a280200210320012802082104200128020421052002410c6a4100410110b1828080002002280210200228021422016a41043a00002002200141016a22013602140240200228020c20016b41034b0d002002410c6a2001410410b182808000200228021421010b200228021020016a20052800003600002002200141046a360214200220033602182002200241186a36021c2002411c6a2002410c6a10fb848080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a200120036a21010c030b2001410c6a280200210320012802082104200128020421052002410c6a4100410110b1828080002002280210200228021422016a41053a00002002200141016a22013602140240200228020c20016b41034b0d002002410c6a2001410410b182808000200228021421010b200228021020016a20052800003600002002200141046a360214200220033602182002200241186a36021c2002411c6a2002410c6a10fb848080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a200120036a21010c020b200141086a2802002103200128020421042002410c6a4100410110b1828080002002280210200228021422016a41003a00002002200141016a360214200220033602182002200241186a36021c2002411c6a2002410c6a10fb848080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a200120036a21010c010b2002410c6a4100410110b1828080002002280210200228021422016a41083a0000200141016a21010b2000200229020c370200200041086a2001360200200241206a2480808080000bbc0402037f017e23808080800041c0006b2201248080808000200141186a41cca6c28000410a41d6a6c28000411b10d682808000200141146a410036020020014280808080c00037020c2001410036023820014280808080c000370230200141246a200141306a41f1a6c28000410a10d984808000200141306a200141246a41fba6c28000410910dd84808000200141246a200141306a4184a7c28000410410d484808000200141306a200141246a4188a7c28000410510d6848080000240200128023822022001280230470d00200141306a200210e084808000200128023821020b2001280234200241246c6a220241083a00202002411936021c2002418da7c2800036021820024204370210200242003702082002428080808080013702002001280238210320012802342102200120012802303602382001200236023020012002200341246c6a41246a36023c20012002360234200141246a200141306a10ea84808000024020012802182202418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001411c6a2902002104200142888080808001370230200142808080808001370238200041c4006a200141306a10ec848080002001413b6a200141246a41086a2802003600002000413c6a200437020020002002360238200041013a000020002001410c6a2202290200370250200041d8006a200241086a2802003602002001200129022437003320002001290030370001200041086a200141376a290000370000200141c0006a2480808080000bdd0701047f23808080800041206b2202248080808000024002400240024002400240024020002d0000220320012d00002204470d000240024002400240024020030e04000102030b0b20030d0a20002800012001280001460d0341a6a7c2800041144100280280a3c68000118480808000000c080b20034101470d09024020002800012001280001460d0041a6a7c2800041144100280280a3c68000118480808000000c070b0240200041106a2802002203200141106a280200470d002000410c6a2802002001410c6a280200200310888e808000450d0a0b41a6a7c2800041144100280280a3c68000118480808000000c060b20034102470d08024020002800012001280001460d0041a6a7c2800041144100280280a3c68000118480808000000c050b0240200041106a2802002203200141106a280200470d002000410c6a2802002001410c6a280200200310888e808000450d090b41a6a7c2800041144100280280a3c68000118480808000000c040b20034103470d07200041086a280200210302402000410c6a28020022052001410c6a280200470d002003200141086a280200200510888e808000450d080b41a6a7c2800041144100280280a3c68000118480808000000c020b0240200041106a2802002203200141106a280200470d002000410c6a2802002001410c6a280200200310888e808000450d070b41a6a7c2800041144100280280a3c68000118480808000000c040b41a6a7c2800041144100280280a3c6800011848080800000024020030e050403020005040b2000410c6a2802002105200041086a28020021030b200241186a200536020020022003360214410321030c030b2002200041016a36021420022000410c6a290200370218410221030c020b410121032002200041016a36021420022000410c6a2902003702180c010b2002200041016a36021420022000410c6a290200370218410021030b20022003360210200241046a200241106a10ed8480800020022802082203200228020c41002802f8a2c680001184808080000002402002280204450d00200341002802c0a3c68000118080808000000b0240024002400240024020040e050001020304000b2002200141016a36021420022001410c6a2902003702180c030b2002200141016a36021420022001410c6a2902003702180c020b2002200141016a36021420022001410c6a2902003702180c010b2002200141086a2902003702140b20022004360210200241046a200241106a10ed8480800020022802082203200228020c41002802f8a2c68000118480808000002002280204450d00200341002802c0a3c68000118080808000000b200241206a2480808080000bd40403057f017e037f23808080800041d0006b2201248080808000200141286a41baa7c28000410641d6a6c28000411b41cca6c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10ec84808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c21062001280228210720014100360218200142808080808001370210200141106a410010e1848080002001280214200128021841386c6a2208420437022c2008420f370224200841cca4c280003602202008410436021c200841c8a4c28000360218200841c581808000360210200842dfbeb7dcc08ee1ef70370308200842d1c6e9d6b09296e819370300200128021821092001280214210820012001280210360248200120083602402001200836024420012008200941386c6a41386a36024c200141346a200141c0006a10eb8480800002402007418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10ec848080002001411b6a200141346a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000b7600200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c00037035020004104360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b1a002001280214200141186a280200200028020010d9808080000bbd0201027f23808080800041106b22022480808080002002410036020c02400240024002402001418001490d002001418010490d012001418080044f0d0220022001413f71418001723a000e20022001410c7641e001723a000c20022001410676413f71418001723a000d410321010c030b200220013a000c410121010c020b20022001413f71418001723a000d2002200141067641c001723a000c410221010c010b20022001413f71418001723a000f20022001410676413f71418001723a000e20022001410c76413f71418001723a000d2002200141127641077141f001723a000c410421010b02402000280200200028020822036b20014f0d00200020032001109c85808000200028020821030b200028020420036a2002410c6a200110848e8080001a2000200320016a360208200241106a24808080800041000b1200200041c0a7c28000200110d9808080000b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0b02000b040041010bda0101017f23808080800041306b2202248080808000200242808080801037020020024100360208200241186a420137020020024101360210200241f0a7c2800036020c200241c68180800036022820022001412c6a36022c2002200241246a36021420022002412c6a360224200241c0a7c280002002410c6a10d9808080001a2001280220200141246a280200200141286a2802002002280204200228020841002802d0a2c680001187808080000002402002280200450d00200228020441002802c0a3c68000118080808000000b200241306a2480808080000b02000b7c01017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110c182808000200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004185043b0100200141106a2480808080000b950301037f0240200028020022022802002200413f4b0d00200041027421020240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20023a00000f0b0240200041ffff004b0d002000410274410172210202402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20023b00000f0b0240200041ffffffff034b0d002000410274410272210202402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20023600000f0b02402001280200220320012802082200470d0020012000410110b18280800020012802002103200128020821000b2001280204220420006a41033a00002001200041016a2200360208200228020021020240200320006b41034b0d0020012000410410b18280800020012802042104200128020821000b2001200041046a360208200420006a20023600000b7601017f20014180feff077141087621020240024020014101710d002002411874411875410274220241d0afc280006a2101200241a4afc280006a21020c010b200241187441187541027422024188b0c280006a2101200241fcafc280006a21020b20002001280200360204200020022802003602000ba90303027f027e037f200141106a2103200241146a2802002104200229030021052001290300210602402001280210200141186a28020022076b200241186a28020022084f0d0020032007200810e284808000200128021821070b200141146a2802002007410c6c6a20042008410c6c10848e8080001a2001200720086a3602182002410036021820002003290300370310200041186a200341086a2802003602002001411c6a2103200241206a28020021090240200128021c200141246a28020022076b200241246a28020022084f0d0020032007200810e284808000200128022421070b200141206a2802002007410c6c6a20092008410c6c10848e8080001a2001200720086a360224200241003602242000427f200620057c220520052006541b3703002000200329020037021c200041246a200341086a280200360200200020012d002841004720022d0028410047713a00282000200129030822062002290308220520062005541b37030802402002280210450d00200441002802c0a3c68000118080808000000b0240200228021c450d00200941002802c0a3c68000118080808000000b0be60d04057f017e037f017e23808080800041d0006b2201248080808000200141306a4181adc2800041124193adc2800041204188a8c28000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a10ec84808000200141086a41086a2001412c6a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a2207410036020020014280808080c000370218200141186a410010e084808000200128021c200728020041246c6a220841003a00202008410436021c200841b3adc280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841013a00202008410736021c200841b7adc2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841023a00202008410636021c200841beadc280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841033a00202008410536021c200841c4adc2800036021820084204370210200842003702082008428080808080013702002009200728020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841043a00202008410836021c200841c9adc280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841053a00202008411136021c200841d1adc2800036021820084204370210200842003702082008428080808080013702002009200728020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841063a00202008411136021c200841e2adc280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a36020020012001290318370340200141186a200141c0006a41f3adc28000410610d3848080000240200128022022082001280218470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841083a00202008410c36021c200841f9adc2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841093a00202008411336021c20084185aec280003602182008420437021020084200370208200842808080808001370200200141186a41086a200141c0006a41086a28020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a2208410a3a00202008410936021c20084198aec28000360218200842043702102008420037020820084280808080800137020020012802202107200128021c2108200120012802183602202001200836021820012008200741246c6a41246a3602242001200836021c200141c0006a200141186a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b20012002360220200120033602182001200336021c2001200320044105746a360224200041c4006a200141186a10ec84808000200141236a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a2001411f6a290000370000200141d0006a2480808080000be70504057f017e037f017e23808080800041d0006b2201248080808000200141286a41a1aec2800041124193adc2800041204188a8c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10ec84808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a2207410036020020014280808080c000370210200141106a410010e0848080002001280214200728020041246c6a220841003a00202008410c36021c200841f5acc280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290210220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841013a00202008411336021c200841b3aec2800036021820084204370210200842003702082008428080808080013702002007200928020041016a36020020012001290340370310200141346a200141106a41f3adc28000410610db848080002001200128023436021820012001280238220836021020012008200128023c41246c6a36021c20012008360214200141c0006a200141106a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10ec848080002001411b6a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290300370350200041d8006a200141086a2802003602002001200129024037001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000b950403057f017e027f23808080800041d0006b2201248080808000200141106a41186a41c6aec2800041184193adc2800041204188a8c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10ec84808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c2106200128022821072001410036021820014280808080c000370210200141c0006a200141106a41deaec28000410710d284808000200141346a200141c0006a41e5aec28000410710d8848080002001200128023436021820012001280238220836021020012008200128023c41246c6a36021c20012008360214200141c0006a200141106a10ea8480800002402007418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10ec848080002001411b6a200141c0006a41086a2802003600002000413c6a200637020020002007360238200041013a000020002001290300370350200041d8006a20022802003602002001200129024037001320002001290010370001200041086a200141106a41076a290000370000200141d0006a2480808080000bbf0604057f017e037f017e23808080800041d0006b2201248080808000200141306a41ecaec2800041114193adc2800041204188a8c28000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a10ec84808000200141086a41086a2001412c6a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a2207410036020020014280808080c000370218200141186a410010e084808000200128021c200728020041246c6a220841003a00202008410736021c200841fdaec280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841013a00202008410536021c20084184afc2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841023a00202008410836021c20084189afc28000360218200842043702102008420037020820084280808080800137020020012802202107200128021c2108200120012802183602202001200836021820012008200741246c6a41246a3602242001200836021c200141c0006a200141186a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b20012002360220200120033602182001200336021c2001200320044105746a360224200041c4006a200141186a10ec84808000200141236a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a2001411f6a290000370000200141d0006a2480808080000be70904057f017e037f017e23808080800041d0006b2201248080808000200141286a4191afc2800041104193adc2800041204188a8c28000410010d882808000200141106a41106a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10ec84808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a22074100360200200142808080808001370210200141106a410010e1848080002001280214200728020041386c6a2208420437022c20084213370224200841b7a5c280003602202008410836021c200841afa5c28000360218200841ef8080800036021020084293888c8f89fdc6ec9e7f370308200842a5e9e3ab9e929adc2c370300200141c0006a41086a2209200728020041016a220836020020012001290210220a37034002402008200aa7470d00200141c0006a200810e184808000200128024821080b2001280244200841386c6a2208420437022c20084213370224200841d2a5c280003602202008410836021c200841caa5c28000360218200841cf81808000360210200842aac2d79da4bfd9a04e370308200842e6afce95f6a1ffa6c3003703002007200928020041016a220836020020012001290340220a37031002402008200aa7470d00200141106a200810e184808000200128021821080b2001280214200841386c6a2208420437022c20084213370224200841d2a5c280003602202008410836021c200841e5a5c28000360218200841cf81808000360210200842aac2d79da4bfd9a04e370308200842e6afce95f6a1ffa6c300370300200141c0006a41086a2207200141106a41086a220928020041016a220836020020012001290310220a37034002402008200aa7470d00200141c0006a200810e184808000200128024821080b2001280244200841386c6a2208420437022c20084214370224200841f6a5c280003602202008410936021c200841eda5c28000360218200841ef8080800036021020084293888c8f89fdc6ec9e7f370308200842a5e9e3ab9e929adc2c3703002009200728020041016a220836020020012001290340220a37031002402008200aa7470d00200141106a200810e184808000200128021821080b2001280214200841386c6a2208420437022c2008420437022420084193a6c280003602202008410936021c2008418aa6c28000360218200841888180800036021020084298848fa1dab08ba174370308200842febac4ad81b6fafcb37f370300200128021821072001280214210820012001280210360248200120083602402001200836024420012008200741386c6a41386a36024c200141346a200141c0006a10eb8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10ec848080002001411b6a200141346a41086a2802003600002000413c6a200637020020002005360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000b820604057f017e027f017e23808080800041d0006b2201248080808000200141286a41abb0c28000410b41a1b0c28000410a4194b0c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10ec84808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a22074100360200200142808080808001370210200141106a410010e1848080002001280214200728020041386c6a2208420437022c20084202370224200841e0a4c280003602202008410536021c200841dba4c28000360218200841c880808000360210200842e6ed8d82cc91adcb05370308200842dbd791d5c2919eaecd00370300200141c0006a41086a200728020041016a2208360200200120012902102209370340024020082009a7470d00200141c0006a200810e184808000200128024821080b2001280244200841386c6a2208420437022c2008422337022420084197a6c280003602202008410536021c200841e2a4c28000360218200841bd81808000360210200842d886fac2c186f9c46f3703082008429cfcf2b9cfebc9bfa37f370300200128024821072001280244210820012001280240360248200120083602402001200836024420012008200741386c6a41386a36024c200141346a200141c0006a10eb8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10ec848080002001411b6a200141346a41086a2802003600002000413c6a200637020020002005360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000bbd0504057f017e027f017e23808080800041c0006b2201248080808000200141246a41b6b0c28000411241a1b0c28000410a4194b0c28000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702302001428080808080013702382001410c6a200141306a10ec84808000200141086a200141206a28020036020020012001290218370300200128020c2102200128021021032001280214210420012802242105200129022821062001410c6a41086a2207410036020020014280808080c00037020c2001410c6a410010e0848080002001280210200728020041246c6a220841003a00202008410c36021c200841c8b0c280003602182008420437021020084200370208200842808080808001370200200141306a41086a200728020041016a22083602002001200129020c2209370330024020082009a7470d00200141306a200810e084808000200128023821080b2001280234200841246c6a220841013a00202008410736021c200841d4b0c2800036021820084204370210200842003702082008428080808080013702002001280238210720012802342108200120012802303602142001200836020c20012008200741246c6a41246a36021820012008360210200141306a2001410c6a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b200120023602142001200336020c200120033602102001200320044105746a360218200041c4006a2001410c6a10ec84808000200141176a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290300370350200041d8006a200141086a2802003602002001200129023037000f2000200129000c370001200041086a2001410c6a41076a290000370000200141c0006a2480808080000bb40e04057f017e037f017e23808080800041d0006b2201248080808000200141306a4194b0c28000410d41a1b0c28000410a4194b0c28000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a10ec84808000200141086a41086a2001412c6a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a2207410036020020014280808080c000370218200141186a410010e084808000200128021c200728020041246c6a220841003a00202008410536021c200841dbb0c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841013a00202008410c36021c200841e0b0c2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841023a00202008410936021c200841ecb0c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a36020020012001290318370340200141186a200141c0006a41f5b0c28000410610d5848080000240200128022022082001280218470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841043a00202008411136021c200841fbb0c2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841053a00202008410b36021c2008418cb1c280003602182008420437021020084200370208200842808080808001370200200141186a41086a2207200141c0006a41086a220928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841063a00202008411036021c20084197b1c2800036021820084204370210200842003702082008428080808080013702002009200728020041016a36020020012001290318370340200141186a200141c0006a41a7b1c28000410510d784808000200141c0006a200141186a41acb1c28000410a10da84808000200141186a200141c0006a41b6b1c28000410d10dc848080000240200128022022082001280218470d00200141186a200810e084808000200128022021080b200128021c200841246c6a2208410a3a00202008410936021c200841c3b1c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a2208410b3a00202008410a36021c200841ccb1c2800036021820084204370210200842003702082008428080808080013702002009200728020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a2208410c3a00202008410b36021c200841d6b1c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a200141186a41086a28020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a2208410d3a00202008410e36021c200841e1b1c2800036021820084204370210200842003702082008428080808080013702002001280248210720012802442108200120012802403602202001200836021820012008200741246c6a41246a3602242001200836021c200141c0006a200141186a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b20012002360220200120033602182001200336021c2001200320044105746a360224200041c4006a200141186a10ec84808000200141236a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a2001411f6a290000370000200141d0006a2480808080000bd10d04057f017e037f017e23808080800041d0006b2201248080808000200141306a41efb1c28000410a41a1b0c28000410a4194b0c28000410010d882808000200141186a41106a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a10ec84808000200141086a41086a2001412c6a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a2207410036020020014280808080c000370218200141186a410010e084808000200128021c200728020041246c6a220841003a00202008411036021c200841f9b1c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841013a00202008410c36021c20084189b2c2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841023a00202008410c36021c20084195b2c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841033a00202008410c36021c200841a1b2c2800036021820084204370210200842003702082008428080808080013702002009200728020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841043a00202008410c36021c200841adb2c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841053a00202008410636021c200841b9b2c2800036021820084204370210200842003702082008428080808080013702002009200728020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841063a00202008410b36021c200841bfb2c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841073a00202008411036021c200841cab2c2800036021820084204370210200842003702082008428080808080013702002009200728020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841083a00202008410d36021c200841dab2c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a200141186a41086a28020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841093a00202008410736021c200841e7b2c2800036021820084204370210200842003702082008428080808080013702002001280248210720012802442108200120012802403602202001200836021820012008200741246c6a41246a3602242001200836021c200141c0006a200141186a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b20012002360220200120033602182001200336021c2001200320044105746a360224200041c4006a200141186a10ec84808000200141236a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a200141186a41076a290000370000200141d0006a2480808080000bba0504057f017e027f017e23808080800041c0006b2201248080808000200141246a41eeb2c28000411641a1b0c28000410a4194b0c28000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702302001428080808080013702382001410c6a200141306a10ec84808000200141086a200141206a28020036020020012001290218370300200128020c2102200128021021032001280214210420012802242105200129022821062001410c6a41086a2207410036020020014280808080c00037020c2001410c6a410010e0848080002001280210200728020041246c6a220841003a00202008410d36021c20084184b3c280003602182008420437021020084200370208200842808080808001370200200141306a41086a200728020041016a22083602002001200129020c2209370330024020082009a7470d00200141306a200810e084808000200128023821080b2001280234200841246c6a220841013a00202008410d36021c20084191b3c2800036021820084204370210200842003702082008428080808080013702002001280238210720012802342108200120012802303602142001200836020c20012008200741246c6a41246a36021820012008360210200141306a2001410c6a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b200120023602142001200336020c200120033602102001200320044105746a360218200041c4006a2001410c6a10ec84808000200141176a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290300370350200041d8006a200141086a2802003602002001200129023037000f2000200129000c370001200041086a200141136a290000370000200141c0006a2480808080000bcd0403057f017e037f23808080800041d0006b2201248080808000200141286a419eb3c28000410b41a1b0c28000410a4194b0c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10ec84808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c21062001280228210720014100360218200142808080808001370210200141106a410010e1848080002001280214200128021841386c6a2208420437022c20084207370224200841c1a4c2800036022020084100360218200841be81808000360210200842b891b68c98adebcf61370308200842e7b0a091f3ed9c85c500370300200128021821092001280214210820012001280210360248200120083602402001200836024420012008200941386c6a41386a36024c200141346a200141c0006a10eb8480800002402007418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10ec84808000200141106a410b6a200141346a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000bc611050a7f017e027f017e027f024002400240024002400240024002400240024002400240024002400240024002400240200128020022042f018a012205410b490d004101210641042105200128020822074105490d03200721052007417b6a0e020302010b200441046a21082001280204210902402001280208220741016a220120054b0d0020082001410c6c6a20082007410c6c6a200520076b410c6c10fe8d8080001a0b20082007410c6c6a22012002290200370200200141086a200241086a2802003602002004200541016a3b018a010c030b200741796a210741002106410621050c010b4100210641052105410021070b2001280204210a41002d00fca3c680001a418c0141002802c8a3c68000118180808000002208450d01200841003b018a0120084100360200200820042f018a01220b2005417f736a22013b018a012001410c4f0d02200b200541016a220c6b2001470d03200441046a220b2005410c6c6a220d290204210e200d280200210d200841046a200b200c410c6c6a2001410c6c10848e8080001a200420053b018a012004200820061b220f41046a21010240200f2f018a01220520074d0d0020012007410c6c6a220b410c6a200b200520076b410c6c10fe8d8080001a0b200a410020061b210920012007410c6c6a22012002290200370200200141086a200241086a280200360200200f200541016a3b018a010240200d418080808078460d00410021010240034020082110200e2111200d2112200422022802002204450d01200a2001470d0720022f018801210102400240024020042f018a012206410b490d004101210c200141054f0d012001210b410421010c020b200441046a220b2001410c6c6a2105200141016a2102200641016a21080240024020012006490d0020052011370204200520123602000c010b200b2002410c6c6a2005200620016b220b410c6c10fe8d8080001a200520113702042005201236020020014102742004418c016a22056a41086a200520024102746a200b41027410fe8d8080001a0b200420083b018a01200420024102746a418c016a20103602002002200641026a220b4f0d040240200620016b220641016a4103712205450d00200420014102746a4190016a210103402001280200220820023b01880120082004360200200141046a2101200241016a21022005417f6a22050d000b0b20064103490d04200241027420046a4198016a21010340200141746a280200220520023b01880120052004360200200141786a2802002205200241016a3b018801200520043602002001417c6a2802002205200241026a3b0188012005200436020020012802002205200241036a3b01880120052004360200200141106a2101200b200241046a2202470d000c050b0b2001210b024002402001417b6a0e020201000b200141796a210b4100210c410621010c010b4100210c410521014100210b0b41002d00fca3c680001a41bc0141002802c8a3c68000118180808000002208450d08200841003b018a0120084100360200200820042f018a01220d2001417f736a22023b018a012002410c4f0d09200d200141016a22056b2002470d0a200441046a22132001410c6c6a220d290204210e200d280200210d200841046a20132005410c6c6a2002410c6c10848e8080001a200420013b018a0120082f018a01220241016a21132002410c4f0d0b200620016b22012013470d0c200a41016a210a2008418c016a200420054102746a418c016a200141027410848e80800021064100210102400340200620014102746a280200220520013b01880120052008360200200120024f0d01200120012002496a220120024d0d000b0b20042008200c1b220541046a21060240200b41016a220120052f018a0122024b0d0020062001410c6c6a2006200b410c6c6a2002200b6b410c6c10fe8d8080001a0b200241016a21132006200b410c6c6a22062011370204200620123602002005418c016a21060240200b41026a2212200241026a220c4f0d00200620124102746a200620014102746a2002200b6b41027410fe8d8080001a0b200620014102746a2010360200200520133b018a0102402001200c4f0d0002402002200b6b221041016a4103712206450d002005200b4102746a4190016a210203402002280200220b20013b018801200b2005360200200241046a2102200141016a21012006417f6a22060d000b0b20104103490d00200520014102746a4198016a21020340200241746a280200220620013b01880120062005360200200241786a2802002206200141016a3b018801200620053602002002417c6a2802002206200141026a3b0188012006200536020020022802002206200141036a3b01880120062005360200200241106a2102200c200141046a2201470d000b0b200a2101200d418080808078470d000c020b0b200328020022052802002208450d0b2005280204210641002d00fca3c680001a41bc0141002802c8a3c68000118180808000002202450d0c2002200836028c01200241003b018a012002410036020020052002360200200841003b018801200820023602002005200641016a36020420062001470d0d20022f018a012201410b4f0d0e2002200141016a22053b018a0120022001410c6c6a220141086a2011370200200141046a20123602002002418c016a20054102746a2010360200201020053b018801201020023602000b200f21040b2000200736020820002009360204200020043602000f0b4104418c0110b280808000000b2001410b41e8b6c28000109581808000000b41b0b6c28000412841d8b6c2800010f880808000000b4188b7c28000413541c0b7c2800010f880808000000b410441bc0110b280808000000b2002410b41e8b6c28000109581808000000b41b0b6c28000412841d8b6c2800010f880808000000b2013410c41f8b6c28000109581808000000b41b0b6c28000412841d8b6c2800010f880808000000b41b0b4c2800010a081808000000b410441bc0110b280808000000b41e0b5c2800041304190b6c2800010f880808000000b41c0b4c28000412041a0b6c2800010f880808000000b8d17050c7f017e027f017e027f2380808080004180076b220524808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022062f01aa142207410b490d004101210841042107200128020822094105490d03200921072009417b6a0e020302010b200641a4136a220820012802082209410c6c6a210a2001280204210b02400240200941016a220120074d0d00200a2002290200370200200a41086a200241086a2802003602000c010b20082001410c6c6a200a200720096b2208410c6c10fe8d8080001a200a41086a200241086a280200360200200a20022902003702002006200141e0016c6a2006200941e0016c6a200841e0016c10fe8d8080001a0b2006200941e0016c6a200341e00110848e8080001a2006200741016a3b01aa140c030b200941796a210941002108410621070c010b4100210841052107410021090b2001280204210a41002d00fca3c680001a41ac1441002802c8a3c6800011818080800000220c450d03200c41003b01aa14200c41003602a013200c20062f01aa14220d2007417f736a22013b01aa14200641a4136a220e2007410c6c6a220f2802002110200f2902042111200541a0056a2006200741e0016c6a41e00110848e8080001a2001410c4f0d04200d200741016a220f6b2001470d05200c41a4136a200e200f410c6c6a2001410c6c10848e8080001a200c2006200f41e0016c6a200141e0016c10848e808000210f200620073b01aa14200541c0036a200541a0056a41e00110848e8080001a2006200f20081b221241a4136a2009410c6c6a21010240024020122f01aa14220720094b0d0020012002290200370200200141086a200241086a2802003602000c010b2001410c6a2001200720096b220d410c6c10fe8d8080001a200141086a200241086a280200360200200120022902003702002012200941e0016c6a220141e0016a2001200d41e0016c10fe8d8080001a0b200a410020081b210b2012200941e0016c6a200341e00110848e8080001a2012200741016a3b01aa142005200541c0036a41e00110848e808000210d2010418080808078470d01201221060b200020093602082000200b360204200020063602000c010b200d41e0016a200d41e00110848e8080001a0240024020062802a01322010d004100210e0c010b4100210e200f21132011211420102115034020012108200a200e470d0620062f01a81421060240024002400240024020082f01aa14220e410b490d004101210f200641054f0d0120062103410421060c020b200841a4136a220a2006410c6c6a2107200641016a2101200e41016a2102024002402006200e490d0020072014370204200720153602002008200641e0016c6a200d41e0016a41e00110848e8080001a0c010b200a2001410c6c6a2007200e20066b220a410c6c10fe8d8080001a20072014370204200720153602002008200141e0016c6a2008200641e0016c6a2207200a41e0016c10fe8d8080001a2007200d41e0016a41e00110848e8080001a2006410274200841ac146a22076a41086a200720014102746a200a41027410fe8d8080001a0b200820023b01aa14200820014102746a41ac146a20133602002001200e41026a220a4f0d020240200e20066b220341016a4103712207450d00200820064102746a41b0146a210603402006280200220220013b01a814200220083602a013200641046a2106200141016a21012007417f6a22070d000b0b20034103490d02200141027420086a41b8146a21060340200641746a280200220720013b01a814200720083602a013200641786a2802002207200141016a3b01a814200720083602a0132006417c6a2802002207200141026a3b01a814200720083602a01320062802002207200141036a3b01a814200720083602a013200641106a2106200a200141046a2201470d000c030b0b20062103024002402006417b6a0e020201000b200641796a21034100210f410621060c010b4100210f41052106410021030b41002d00fca3c680001a41dc1441002802c8a3c6800011818080800000220c450d09200c41003b01aa14200c41003602a013200c20082f01aa1422022006417f736a22013b01aa14200841a4136a22162006410c6c6a2207280200211020072902042111200d41a0056a2008200641e0016c6a41e00110848e8080001a2001410c4f0d0a2002200641016a22076b2001470d0b200c41a4136a20162007410c6c6a2001410c6c10848e8080001a200c2008200741e0016c6a200141e0016c10848e8080002102200820063b01aa14200d41c0036a200d41a0056a41e00110848e8080001a20022f01aa14220141016a21162001410c4f0d0c200e20066b22062016470d0d200a41016a210e200241ac146a200820074102746a41ac146a200641027410848e808000210a4100210602400340200a20064102746a280200220720063b01a814200720023602a013200620014f0d01200620062001496a220620014d0d000b0b200d41a0056a200d41c0036a41e00110848e8080001a20082002200f1b220141a4136a220f2003410c6c6a210a02400240200341016a220620012f01aa1422074d0d00200a2014370204200a20153602000c010b200f2006410c6c6a200a200720036b220f410c6c10fe8d8080001a200a2014370204200a20153602002001200641e0016c6a2001200341e0016c6a200f41e0016c10fe8d8080001a0b200741016a21152001200341e0016c6a200d41e0016a41e00110848e8080001a200141ac146a210a0240200341026a2216200741026a220f4f0d00200a20164102746a200a20064102746a200720036b41027410fe8d8080001a0b200a20064102746a2013360200200120153b01aa1402402006200f4f0d000240200720036b221341016a410371220a450d00200120034102746a41b0146a210703402007280200220320063b01a814200320013602a013200741046a2107200641016a2106200a417f6a220a0d000b0b20134103490d00200120064102746a41b8146a21070340200741746a280200220a20063b01a814200a20013602a013200741786a280200220a200641016a3b01a814200a20013602a0132007417c6a280200220a200641026a3b01a814200a20013602a0132007280200220a200641036a3b01a814200a20013602a013200741106a2107200f200641046a2206470d000b0b200d200d41a0056a41e00110848e80800021062010418080808078470d010b200020093602082000200b360204200020123602000c030b200641e0016a200641e00110848e8080001a20022113200e210a20082106201121142010211520082802a01322010d000b0b200428020022012802002207450d0a2001280204210241002d00fca3c680001a41dc1441002802c8a3c68000118180808000002206450d0b200620073602ac14200641003b01aa14200641003602a01320012006360200200741003b01a814200720063602a0132001200241016a3602042002200e470d0c20062f01aa142201410b4f0d0d2006200141016a22073b01aa1420062001410c6c6a220241a8136a2011370200200241a4136a20103602002006200141e0016c6a200d41e0016a41e00110848e8080001a200641ac146a20074102746a200c360200200020093602082000200b36020420002012360200200c20073b01a814200c20063602a0130b20054180076a2480808080000f0b410441ac1410b280808000000b2001410b41e8b6c28000109581808000000b41b0b6c28000412841d8b6c2800010f880808000000b4188b7c28000413541c0b7c2800010f880808000000b410441dc1410b280808000000b2001410b41e8b6c28000109581808000000b41b0b6c28000412841d8b6c2800010f880808000000b2016410c41f8b6c28000109581808000000b41b0b6c28000412841d8b6c2800010f880808000000b41b0b4c2800010a081808000000b410441dc1410b280808000000b41e0b5c2800041304190b6c2800010f880808000000b41c0b4c28000412041a0b6c2800010f880808000000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bee0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b2201410274210420014180808080024941027421050240024020030d00200241003602180c010b200241043602182002200341027436021c200220002802043602140b200241086a20052004200241146a108b85808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a108b85808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bf20301027f23808080800041c0006b22042480808080000240024020010d00410021010c010b41012101200241c000490d0041022101200241808001490d00410441052002418080808004491b21010b200420033602182004200441186a36021c2004410c6a2004411c6a10928580800002402000280200220228020822052001490d0020024100360208200228020421032004413c6a2004280210220020042802146a360200200441386a200428020c360200200441346a2000360200200420023602242004200336021c20042000360230200420013602282004200520016b36022c2004200320016a3602202004411c6a10918580800020042802202102200441d0b7c28000360220200428021c2103200441d0b7c2800036021c200428022c210102400240024020022003470d002001450d022004280224220041086a21032004280228220520002802082202460d012000280204220020026a200020056a200110fe8d8080001a0c010b2001450d012004280224220041086a21032004280228220520002802082202460d002000280204220020026a200020056a200110fe8d8080001a0b2003200220016a3602000b02402004280238450d00200428023041002802c0a3c68000118080808000000b200441c0006a2480808080000f0b2001200541dcbcc28000109581808000000be60701097f410341022001280200418080808078461b21052000280200210602400240024020020d00200041086a2802002006200641054b22071b22020d010b200041086a2102024002402000200641054b22074103746a28020022082006410520071b460d002002200020071b21022000280204200041046a20071b21060c010b2000109a8580800020002802082108200028020421060b20062008412c6c6a2206200129020037020c200620053602082006410036022820064280808080c000370220200641146a200141086a2802003602002002200228020041016a3602000c010b024002402002412c6c200041046a2202280200200220071b6a41546a220628020822084101460d00410221070c010b200641186a2207280200210920074100360200200641146a280200210a2006280210210b20064280808080103702102006410c6a280200210c2006280204210d200628020021070b02400240024002402008417e6a2208410220084102491b0e020103000b200641106a21080c010b2006410c6a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200620053602082006200129020037020c200641146a200141086a28020036020020074102460d0002400240200028020820002802002201200141054b22011b220641014d0d0002402006412c6c2002280200200220011b6a41a87f6a220128020841014b0d0041002106410021022001280200450d02410121022001280204220541c000490d0241022102200541808001490d02410441052005418080808004491b21020c020b200b450d02200a41002802c0a3c68000118080808000000c020b4198bbc2800041c00041d8bbc2800010a181808000000b02402007450d0041012106200d41c000490d0041022106200d41808001490d0041044105200d418080808004491b21060b2001200d360204200120073602002009200220066b200620026b20062002491b2207410020076b20022006491b200c6a220620092006491b210602402001280210450d00200141146a28020041002802c0a3c68000118080808000000b2001200b360210200141186a2006360200200141146a200a3602000b0240024020034101470d00200041086a28020020002802002201200141054b22011b2206450d012006412c6c2000280204200041046a20011b6a220141746a210002402001417c6a2802002201450d00200141027420002802046a417c6a2802002004460d010b024020012000280200470d0020002001108c85808000200028020821010b200028020420014102746a20043602002000200028020841016a3602080b0f0b41fcb8c2800041fc004188bbc2800010a181808000000b8c07010c7f23808080800041b0026b2205248080808000200128020421060240024002400240024002400240200128020822070d00410121080c010b2007417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002208450d020b20082006200710848e808000210902402000280200220a0d004100210a0c030b2000280204210b024002400340200a41a4136a2108200a2f01aa14220c410c6c210d417f210e0240024003400240200d0d00200c210e0c020b200841086a210f200841046a2110200d41746a210d200e41016a210e2008410c6a2108417f200920102802002007200f280200220f2007200f491b10888e80800022102007200f6b20101b220f410047200f4100481b220f4101460d000b200f41ff0171450d010b200b450d02200b417f6a210b200a200e4102746a41ac146a280200210a0c010b0b2007450d01200941002802c0a3c68000118080808000000c010b2007418080808078470d032009210a2000210e0b200a200e41e0016c6a21080c030b10ae80808000000b4101200710b280808000000b2005200e360224200541003602202005200a36021c2005200036021820052007360214200520093602102005200736020c200541003602280240200a0d0041002d00fca3c680001a41ac1441002802c8a3c68000118180808000002208450d02200841013b01aa14200841003602a013200820073602a413200841ac136a2007360200200841a8136a20093602002008200541286a41e00110848e80800021072000428080808010370204200020073602000c010b20054188026a41086a2005411c6a220841086a2802003602002005200829020037038802200520073602ac02200520093602a802200520073602a40220054198026a20054188026a200541a4026a200541286a200541186a108a8580800020052802182208200828020841016a36020820052802980220052802a00241e0016c6a21080b02400240200041146a280200200028020c2207200741054b22071b220d450d00200041106a220f280200210e200541286a41086a200141086a28020036020020052001290200370328200d410c6c200e200f20071b6a41746a200541286a10978580800041017321070c010b024020012802000d00410021070c010b41002107200641002802c0a3c68000118080808000000b20082002200720032004108f85808000200541b0026a2480808080000f0b410441ac1410b280808000000bf608010c7f200041e8bbc28000360204200041e8bbc280003602000240200028021022010d00024020002802082202280200200228020822036b200041206a2802002204200041186a28020022056b22064f0d00200220032006108d85808000200228020821030b024020052004460d00200228020421072005417f7320046a2108024020064103712206450d000340200720036a20052d00003a0000200341016a2103200541016a21052006417f6a22060d000b0b024020084103490d00200720036a2109410021070340200920076a2208200520076a22062d00003a0000200841016a200641016a2d00003a0000200841026a200641026a2d00003a0000200841036a200641036a2d00003a0000200741046a2107200641046a2004470d000b200320076a21030b200020043602180b200220033602080f0b024002400240200028020c220a200028020822032802082205470d00200041206a2802002109200041186a28020021070c010b200a20056b2106200328020420056a2105200041186a2802002107200041206a2802002109034020072009460d02200520072d00003a00002000200741016a22073602182003200328020841016a360208200541016a21052006417f6a22060d000b0b0240024020092007470d0020092107200921060c010b02402003280200200a20016a22066b200920076b22054f0d00200320062005108d858080000b200328020422062005200a6a22046a2006200a6a200110fe8d8080001a2000200436020c0240200328020822062004470d002004210a200721060c010b200328020420066a2105200a20096a20066b210820072106034020062009460d02200520062d00003a00002000200641016a22063602182003200328020841016a360208200541016a21052008200741016a2207470d000b2004210a0b0240024020092006470d00410121020c010b02400240200920076b2208417f4c0d004100210541002d00fca3c680001a200841002802c8a3c68000118180808000002202450d01200920066b2208410371210b02402006417f7320096a4103490d002008417c71210c410021050340200220056a2208200620056a22042d00003a0000200841016a200441016a2d00003a0000200841026a200441026a2d00003a0000200841036a200441036a2d00003a0000200c200541046a2205470d000b200620056a21060b0240200b450d000340200220056a20062d00003a0000200541016a2105200641016a2106200b417f6a220b0d000b0b200020093602182005450d0202402003280200200a20016a22066b20054f0d00200320062005108d858080000b20032802042208200a20056a22066a2008200a6a200110fe8d8080001a2000200636020c200328020822082006460d02200328020420086a21002005200a20086b6a210841002106034020052006460d03200020066a200220066a2d00003a00002003200328020841016a3602082008200641016a2206460d030c000b0b10ae80808000000b4101200810b280808000000b20092007460d00200241002802c0a3c68000118080808000000b0bcb0301047f23808080800041106b220224808080800041012103024020012802002204280200220141c000490d0041022103200141808001490d00410441052001418080808004491b21030b41002d00fca3c680001a024002400240200341002802c8a3c68000118180808000002201450d002002410036020c2002200136020820022003360204024020042802002205413f4b0d00200120054102743a0000410121030c030b0240200541ffff004b0d0020054102744101722104410021050240200341014b0d00200241046a4100410210b18280800020022802082101200228020c21050b200120056a20043b0000200541026a21030c030b200541ffffffff034b0d0120054102744102722104410021050240200341034b0d00200241046a4100410410b18280800020022802082101200228020c21050b200120056a2004360000200541046a21030c020b4101200310b280808000000b200141033a0000410121052002410136020c200428020021040240200341044b0d00200241046a4101410410b18280800020022802082101200228020c21050b200120056a2004360000200541046a21030b20002002290204370200200041086a2003360200200241106a2480808080000b9a0201027f23808080800041106b2202248080808000200220002802002200360204200128021441ecbcc280004106200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a41f2bcc280004104200041046a41f8bcc28000108c818080004188bdc280004105200241046a4190bdc28000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010bae0101017f23808080800041306b2202248080808000200028020021002002410c6a4202370200200241186a410c6a41d08180800036020020022000280200220036022820024103360204200241f0bfc08000360200200241d18180800036021c200220006836022c200141186a28020021002002200241186a36020820022002412c6a3602202002200241286a36021820012802142000200210d9808080002101200241306a24808080800020010bed0201037f2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000b02000bed04020c7f017e23808080800041d0006b22022480808080000240024002400240200028020022030d00200141046a2104410021030c010b200141046a210420012802082105200128020421062000280204210702400340200341046a210820032f018a012209410c6c210a417f210b0240024003400240200a0d002009210b0c020b200841086a210c200841046a210d200a41746a210a200b41016a210b2008410c6a2108417f2006200d2802002005200c280200220c2005200c491b10888e808000220d2005200c6b200d1b220c410047200c4100481b220c4101460d000b200c41ff0171450d010b2007450d022007417f6a21072003200b4102746a418c016a28020021030c010b0b410121082001280200450d02200641002802c0a3c68000118080808000000c020b200bad422086210e0b410121082001280200220a418080808078460d002002200e3702202002200336021c200220003602182002200a36020c200220042902003702100240024020030d0041002d00fca3c680001a418c0141002802c8a3c68000118180808000002208450d0320084100360200200841013b018a01200241146a280200210a2008200229020c3702042008410c6a200a3602002000428080808010370204200020083602000c010b200241286a41086a2002411c6a220841086a28020036020020022008290200370328200241c0006a41086a2002410c6a41086a2802003602002002200229020c370340200241346a200241286a200241c0006a200241186a10898580800020022802182208200828020841016a3602080b410021080b200241d0006a24808080800020080f0b4104418c0110b280808000000b02000ba90201027f23808080800041106b22022480808080000240024020002802000d00200128021441a0bdc280004110200141186a28020028020c1182808080000021010c010b20022000360204200128021441b0bdc280004108200141186a28020028020c118280808000002100200241003a000d200220003a000c20022001360208200241086a41b8bdc280004106200241046a41c0bdc28000108c81808000210320022d000c2100024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010bbd0402077f017e23808080800041106b2201248080808000024002400240024002400240200041086a28020020002802002202200241054b1b220341016a2204450d004100417f2004417f6a677620044102491b41016a2204450d00200320044b0d0120024105200241054b22051b21062000280204200041046a220720051b2105024020044106490d0020062004460d060240024002402004ad422c7e2208422088a70d002008a7220741fcffffff074b0d0020024106490d012006ad422c7e2208422088a70d002008a7220641fcffffff074b0d00200741002802c8a3c68000118180808000002202450d02200220052006200720062007491b10848e8080001a200541002802c0a3c68000118080808000000c080b41f8bec280004111418cbfc2800010f880808000000b41002d00fca3c680001a200741002802c8a3c680001181808080000022020d050b4104200710b280808000000b200241064f0d020c050b41f8bec280004111419cbfc2800010a181808000000b41acbfc28000412041ccbfc2800010f880808000000b200720052003412c6c10848e8080001a200020033602002006ad422c7e2208a7210002402008422088a70d00200041fdffffff074f0d00200541002802c0a3c68000118080808000000c030b2001200036020c2001410036020841acbec28000412b200141086a41d8bec2800041e8bec28000108981808000000b200220052003412c6c10848e8080001a0b2000200336020820002002360204200020043602000b200141106a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a109b85808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b4b01017f02402000280200200028020822036b20024f0d00200020032002109c85808000200028020821030b200028020420036a2001200210848e8080001a2000200320026a36020841000b860201017f024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d0020020d02410121010c030b20032802002103200241002802c8a3c68000118180808000002201450d0320012003200410848e8080001a200341002802c0a3c68000118080808000000c020b20020d00410121010c010b41002d00fca3c680001a200241002802c8a3c68000118180808000002201450d010b20002001360204200041086a2002360200200041003602000f0b20004101360204200041086a2002360200200041013602000f0b20004100360204200041086a2002360200200041013602000f0b20004100360204200041013602000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a109e85808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000be50201057f23808080800041106b220224808080800020012802042103024002400240024002402001280208220141176a22040d002002410036020c20024280808080103702040c010b2004417f4c0d024100210541002d00fca3c680001a200441002802c8a3c68000118180808000002206450d032002410036020c200220063602082002200436020420014169490d010b200241046a41004117109f8580800020022802082106200228020c21050b200620056a220441002900dcbfc280003700002004410f6a41002900ebbfc28000370000200441086a41002900e4bfc280003700002002200541176a220436020c0240200228020420046b20014f0d00200241046a20042001109f8580800020022802082106200228020c21040b200620046a2003200110848e8080001a200041086a200420016a36020020002002290204370200200241106a2480808080000f0b10ae80808000000b4101200410b280808000000b800303027f017e057f23808080800041306b2201248080808000200141246a4192c2c28000410c41fcc1c28000411641fcc1c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010a98580800020012802102205200128021441386c22066a220742d1beacaad083ca85ad7f370308200742fbd1a5f0d984c58f51370300200128020c21082007420437022c20074211370224200741a8c2c280003602202007410a36021c2007419ec2c28000360218200741d88180800036021002402004418080808078470d0041f3bfc28000411141e4c0c2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000b6f00200042e7b0a091f3ed9c85c50037030820004280808080c00037033820004280808080c000370350200041d981808000360218200041023a0000200041106a42b891b68c98adebcf61370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c880808000360218200041023a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710ac8580800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710ab8580800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000ba707010d7f200141186a28020021022001410d6a2d00002103200141096a2d00002104200141146a28020021052001280210210620012d000c21072001280204210820012d000821092001280200210a0340024002400240024002400240024002400240024002400240024002400240024002400240200a417e6a0e020102000b0240200941ff0171220b4102460d002001200b454101743a000841002109200b450d002004210c0c030b0240200a450d002008450d0002402008418002490d002001200841817e6a220836020441ff01210c410221090c040b200141003602042008417f6a210c41022109410021080c030b41022109200141023602000b200741ff0171210a410221070240200a4102460d0041002107200141003a000c200a0d030b200141033602000b2006450d072005450d07200120052005200220052002491b220a6b220536021420012006200a6a220b360210024002400240200a0e020001020b4100410041c8c4c2800010f980808000000b4101410141d8c4c2800010f980808000000b20062d000041047420062d000172210c4103210a200b21060b2000280208220b2000280200470d0d200a4103470d024100210d4103210a2006450d0c2005450d0c2002450d01200520026e220d2005200d20026c6b4100476a210d0c0c0b410021072000280208220b2000280200460d024102210a2003210c0c0c0b41c0c3c28000411941acc3c2800010f880808000000b2006450d06200a4102470d024102210a200741ff01714102470d014100210d410221070c050b2003210c2006450d060b200741ff0171410047210d4102210a0c030b200741ff0171220d4102460d012009410171200d4100476a210d0c020b0f0b410221074100210d0240200941ff0171220e4102470d00410221090c010b200e410047210d0b4100210e024002402005450d002002450d01200520026e220e2005200e20026c6b4100476a210e0b417f200d200e6a220e200e200d491b210d0c040b41c0c3c28000411941acc3c2800010f880808000000b200a4102470d014102210a410021064100210d200741ff01714102460d020b41002106200741ff0171410047210d4102210a0c010b0240200741ff0171220d4102460d00410021062009410171200d4100476a210d0c010b410021060240200941ff0171220d4102470d00410221094100210d0c010b41002106200d410047210d0b2000200b200d41016a220d417f200d1b10aa858080000b2000200b41016a3602082000280204200b6a200c3a00000c000b0ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000be00101037f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014108200141084b1b2201417f73411f7621040240024020030d00200241003602180c010b2002200336021c20024101360218200220002802043602140b200241086a20042001200241146a10a785808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10a785808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a10a785808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10a785808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10a785808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b950301037f0240200028020022022802002200413f4b0d00200041027421020240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20023a00000f0b0240200041ffff004b0d002000410274410172210202402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20023b00000f0b0240200041ffffffff034b0d002000410274410272210202402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20023600000f0b02402001280200220320012802082200470d0020012000410110b18280800020012802002103200128020821000b2001280204220420006a41033a00002001200041016a2200360208200228020021020240200320006b41034b0d0020012000410410b18280800020012802042104200128020821000b2001200041046a360208200420006a20023600000bfe0501077f23808080800041c0006b220124808080800020014108360224200141d9c3c2800036022041002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241d9c3c28000360200200241046a410836020041d9c3c28000410810d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342e7b0a091f3ed9c85c500370308200341d98180800036021820034101360204200341e1c3c28000360200200341106a42b891b68c98adebcf6137030020014101360210200120033602082001200341206a3602142001200336020c200141306a200141086a10a48580800020012802382104200128023421052001280230210620014100360210200142808080808001370208200141086a410010a985808000200128020c200128021041386c6a2203410036023020034280808080c0003703282003410036022020034100360218200341da81808000360210200342aac2d79da4bfd9a04e370308200342e6afce95f6a1ffa6c30037030020012802102107200128020c210320012001280208360238200120033602302001200336023420012003200741386c6a41386a36023c200141206a200141306a10a58580800020012006360210200120053602082001200520044105746a3602142001200536020c200041c4006a200141086a10a485808000200141136a200141206a41086a2802003600002000413c6a2002ad4280808080108437020020004101360238200041003a0000200041d8006a410036020020004280808080c0003703502001200129032037000b20002001290008370001200041086a2001410f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141146a42013702002001410236020c20014180ddc18000360208200141b4808080003602342001200141306a3602102001200141206a360230200141086a4190ddc1800010f680808000000b4108412010b280808000000b9d0401057f23808080800041306b2204248080808000200241017121050240024020032802002206418080808078470d002002413e2002413e491b210602402005450d0020012d000021070b200441246a4102360200200441206a2002417e71360200200420073a0019200441013a00142004410136020c2004200220066b360210200420053a00182004200120056a36021c200441ff00200641c000722002413e4b1b3a001520002004410c6a10a6858080002004200341086a28020022023602282004200441286a36022c2004412c6a200010ad858080002003280204210502402000280200200028020822036b20024f0d0020002003200210aa85808000200028020821030b200028020420036a2005200210848e8080001a2000200320026a3602080c010b2002411e2002411e491b210702402005450d0020012d000021080b200441246a4102360200200441206a2002417e71360200200420083a0019200441013a00142004410136020c2004200220076b360210200420053a00182004200120056a36021c2004413f20074120722002411e4b1b3a001520002004410c6a10a6858080002003280204210502402000280200200028020822026b200328020822034f0d0020002002200310aa85808000200028020821020b200028020420026a2005200310848e8080001a2000200220036a3602082006450d00200541002802c0a3c68000118080808000000b200441306a2480808080000b6f00200042f9f59bcab3e385eb9e7f37030820004280808080c00037033820004280808080c000370350200041db81808000360218200041023a0000200041106a42e4a581ceab969ef15c370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000be10101017f41002d00fca3c680001a0240413041002802c8a3c680001181808080000022010d004108413010b280808000000b200142d7c9cb8fc1cf97db3e370318200142a8c7daf8defb9a8fc400370308200142a7b98ffce8cbcbd38b7f370300200141dc8180800036021020004280808080c00037033820004280808080c0003703502000410236020c2000200136020820004102360204200041043a0000200141206a42e88488d0c0e3aebc13370300200141286a41b580808000360200200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b7600200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c00037035020004108360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bcd0401077f23808080800041306b22012480808080002001410336020c200141dcc6c2800036020841002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241dcc6c28000360200200241046a410336020041dcc6c28000410310d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342cec69c9ba88e87eade00370308200341dd8180800036021820034101360204200341dfc6c28000360200200341106a428e89b0b185a4c6d5fa0037030020014100360218200142808080808001370210200141106a410010b68580800020012802142204200128021841386c22056a2206428e89b0b185a4c6d5fa00370308200642cec69c9ba88e87eade00370300200128021021072006410036023020064280808080c0003703282006410036022020064100360218200641dd81808000360210200041cc006a4101360200200041c8006a2003360200200041013602442000413c6a2002ad4280808080108437020020004101360238200041d8006a410036020020004280808080c0003703502000200541386a41386e36020c2000200436020820002007360204200041003a0000200141306a2480808080000f0b4104410810b280808000000b200241002802c0a3c68000118080808000002001411c6a42013702002001410236021420014180ddc18000360210200141b48080800036022c2001200141286a3602182001200141086a360228200141106a4190ddc1800010f680808000000b4108412010b280808000000bdf0b05027f017e037f017e017f23808080800041d0006b2201248080808000200141306a41e0c6c28000410e41eec6c28000410a41e0c6c28000410010d882808000200141086a41086a410036020020014280808080c0003703082001280230210220012902342103200141186a41086a22044100360200200142808080808001370218200141186a410010b685808000200128021c2004280200220541386c6a2206420437022c2006420d37022420064181c7c280003602202006410936021c200641f8c6c28000360218200641bd80808000360210200642efc9c9edb5e7b3a6c700370308200642acf6debeefe0d9c8d300370300200141c0006a41086a200541016a2205360200200120012902182207370340024020052007a7470d00200141c0006a200510b685808000200128024821050b2001280244200541386c6a2206420437022c2006420d37022420064181c7c280003602202006410936021c2006418ec7c28000360218200641bd80808000360210200642efc9c9edb5e7b3a6c700370308200642acf6debeefe0d9c8d3003703002004200541016a2205360200200120012903402207370318024020052007a7470d00200141186a200510b685808000200128022021050b200128021c200541386c6a2206420437022c20064203370224200641a8c7c280003602202006411136021c20064197c7c28000360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e370300200141c0006a41086a200541016a2205360200200120012903182207370340024020052007a7470d00200141c0006a200510b685808000200128024821050b2001280244200541386c6a2206420437022c20064203370224200641a8c7c280003602202006410c36021c200641abc7c28000360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e370300200141186a41086a200541016a2205360200200120012903402207370318024020052007a7470d00200141186a200510b685808000200128022021050b200128021c200541386c6a2206420437022c20064203370224200641a8c7c280003602202006410c36021c200641b7c7c28000360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e370300200141c0006a41086a200541016a2205360200200120012903182207370340024020052007a7470d00200141c0006a200510b685808000200128024821050b2001280244200541386c6a2206420437022c20064207370224200641c7c7c280003602202006410436021c200641c3c7c28000360218200641de81808000360210200642abd8aafbe989a3f863370308200642ccc2e78eb1d4fbf0bc7f370300200141186a41086a200541016a2205360200200120012903402207370318024020052007a7470d00200141186a200510b685808000200128022021050b200128021c200541386c6a2206420437022c20064203370224200641a8c7c280003602202006411336021c200641cec7c28000360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e370300200141c8006a200541016a2205360200200120012903182207370340024020052007a72204470d00200141c0006a200510b68580800020012802402104200128024821050b20012802442208200541386c6a220642e6ed8d82cc91adcb05370308200642dbd791d5c2919eaecd003703002006420437022c20064202370224200641eec7c280003602202006410d36021c200641e1c7c28000360218200641c88080800036021002402002418080808078470d0041d8c5c28000411141ccc6c2800010a181808000000b200042808080808001370244200020023602382000200836020820002004360204200041003a000020002001290308370350200041cc006a41003602002000413c6a2003370200200041d8006a200141106a2802003602002000200541386c41386a41386e36020c200141d0006a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10b585808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000b6f00200042a5e9e3ab9e929adc2c37030820004280808080c00037033820004280808080c000370350200041ef80808000360218200041063a0000200041106a4293888c8f89fdc6ec9e7f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b960101017f23808080800041306b2202248080808000200241206a410c6a41cf80808000360200200241086a410c6a42023702002002410336020c20024190c8c28000360208200241cf80808000360224200220003602202002200041086a360228200141186a28020021002002200241206a36021020012802142000200241086a10d9808080002100200241306a24808080800020000bab0405027f017e027f017e027f23808080800041c0006b2201248080808000200141246a41a8c8c28000410641aec8c28000411541f0c7c28000410010d882808000200141086a410036020020014280808080c00037030020012802242102200129022821032001410c6a41086a2204410036020020014280808080800137020c2001410c6a410010bb85808000200141306a41086a2004280200220441016a22053602002001280210200441386c6a220442899ac8f29d8ce69ac300370308200442e78dcee4d0becc97573703002004420437022c20044203370224200441e8c9c280003602202004410836021c200441e0c9c28000360218200441df818080003602102001200129020c2206370330024020052006a72207470d00200141306a200510bb8580800020012802302107200128023821050b20012802342208200541386c6a220442899ac8f29d8ce69ac300370308200442e78dcee4d0becc97573703002004420437022c20044203370224200441e8c9c280003602202004410a36021c200441ebc9c28000360218200441df8180800036021002402002418080808078470d0041dcc8c28000411141d0c9c2800010a181808000000b200042808080808001370244200020023602382000200836020820002007360204200041003a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200541016a36020c200041d8006a200141086a280200360200200141c0006a2480808080000bab0405027f017e027f017e027f23808080800041c0006b2201248080808000200141246a41c3c8c28000410f41d2c8c28000410a41f0c7c28000410010d882808000200141086a410036020020014280808080c00037030020012802242102200129022821032001410c6a41086a2204410036020020014280808080800137020c2001410c6a410010bb85808000200141306a41086a2004280200220441016a22053602002001280210200441386c6a22044293888c8f89fdc6ec9e7f370308200442a5e9e3ab9e929adc2c3703002004420437022c20044203370224200441e8c9c280003602202004410436021c200441f5c9c28000360218200441ef808080003602102001200129020c2206370330024020052006a72207470d00200141306a200510bb8580800020012802302107200128023821050b20012802342208200541386c6a22044293888c8f89fdc6ec9e7f370308200442a5e9e3ab9e929adc2c3703002004420437022c20044203370224200441e8c9c280003602202004410536021c200441f9c9c28000360218200441ef8080800036021002402002418080808078470d0041dcc8c28000411141d0c9c2800010a181808000000b200042808080808001370244200020023602382000200836020820002007360204200041003a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200541016a36020c200041d8006a200141086a280200360200200141c0006a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10bc85808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000ba02001177f23808080800041c0016b220524808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022062f0196042207410b490d0041012108410421092001280208220a4105490d03200a2109200a417b6a0e020302010b20062001280208220a4105746a21092001280204210b02400240200a41016a220120074d0d0020092002290000370000200941186a200241186a290000370000200941106a200241106a290000370000200941086a200241086a2900003700000c010b200620014105746a20092007200a6b220c41057410fe8d8080001a200941186a200241186a290000370000200941106a200241106a290000370000200941086a200241086a29000037000020092002290000370000200641e0026a220220014104746a2002200a4104746a200c41047410fe8d8080001a0b2006200a4104746a220241e8026a200341086a290200370200200241e0026a20032902003702002006200741016a3b0196040c030b200a41796a210a41002108410621090c010b41002108410521094100210a0b2001280204210741002d00fca3c680001a41980441002802c8a3c6800011818080800000220d450d03200d41003b019604200d41003602900420054190016a41086a200620094105746a220c41086a29000037030020054190016a41106a200c41106a29000037030020054190016a41186a200c41186a290000370300200541c0006a41086a200641e0026a220e20094104746a220f410c6a280200360200200d20062f01960422102009417f736a22013b0196042005200c290000370390012005200f2902043703402001410c4f0d042010200941016a220c6b2001470d05200f2802002111200d2006200c4105746a200141057410848e808000220f41e0026a200e200c4104746a200141047410848e8080001a200620093b019604200541f0006a41086a20054190016a41086a290300370300200541f0006a41106a20054190016a41106a290300370300200541f0006a41186a20054190016a41186a290300370300200541b0016a41086a200541c0006a41086a2802003602002005200529039001370370200520052903403703b0012006200f20081b2212200a4105746a21010240024020122f0196042209200a4b0d0020012002290000370000200141186a200241186a290000370000200141106a200241106a290000370000200141086a200241086a2900003700000c010b2012200a41016a220c4105746a20012009200a6b221041057410fe8d8080001a200141186a200241186a290000370000200141106a200241106a290000370000200141086a200241086a29000037000020012002290000370000201241e0026a2202200c4104746a2002200a4104746a201041047410fe8d8080001a0b2007410020081b210b2012200a4104746a220241e8026a200341086a290200370200200241e0026a2003290200370200200541106a41086a2202200541f0006a41086a290300370300200541106a41106a2201200541f0006a41106a290300370300200541106a41186a2203200541f0006a41186a290300370300200541e0006a41086a220c200541b0016a41086a2802003602002012200941016a3b01960420052005290370370310200520052903b0013703602011418080808078470d01201221060b2000200a3602082000200b360204200020063602000c010b200541c0006a41186a2003290300370300200541c0006a41106a2001290300370300200541c0006a41086a200229030037030020052005290310370340200541306a41086a200c2802003602002005200529036037033002400240024020062802900422020d00410021130c010b41002113200f21142011211503402002210c20072013470d0720062f0194042106024002400240200c2f0196042213410b490d0041012108200641054f0d0120062103410421060c020b200641016a2102201341016a2109200c20064105746a21010240024020062013490d0020012005290340370000200141186a200541c0006a41186a290300370000200141106a200541c0006a41106a290300370000200141086a200541c0006a41086a290300370000200c20064104746a220141e0026a2015360200200141e4026a2005290330370200200141ec026a200541306a41086a2802003602000c010b200c20024105746a2001201320066b220741057410fe8d8080001a200141186a200541c0006a41186a290300370000200141106a200541c0006a41106a290300370000200141086a200541c0006a41086a29030037000020012005290340370000200c41e0026a220120024104746a200120064104746a2201200741047410fe8d8080001a20012015360200200120052903303702042001410c6a200541306a41086a2802003602002006410274200c4198046a22016a41086a200120024102746a200741027410fe8d8080001a0b200c20093b019604200c20024102746a4198046a20143602002002201341026a22074f0d040240201320066b220341016a4103712201450d00200c20064102746a419c046a210603402006280200220920023b0194042009200c36029004200641046a2106200241016a21022001417f6a22010d000b0b20034103490d042002410274200c6a41a4046a21060340200641746a280200220120023b0194042001200c36029004200641786a2802002201200241016a3b0194042001200c360290042006417c6a2802002201200241026a3b0194042001200c3602900420062802002201200241036a3b0194042001200c36029004200641106a21062007200241046a2202470d000c050b0b20062103024002402006417b6a0e020201000b200641796a210341002108410621060c010b4100210841052106410021030b41002d00fca3c680001a41c80441002802c8a3c6800011818080800000220d450d08200d41003b019604200d41003602900420054190016a41086a220f200c20064105746a220141086a29000037030020054190016a41106a2210200141106a29000037030020054190016a41186a220e200141186a290000370300200541b0016a41086a2216200c41e0026a221720064104746a2209410c6a280200360200200d200c2f01960422112006417f736a22023b0196042005200129000037039001200520092902043703b0012002410c4f0d092011200641016a22016b2002470d0a20092802002111200d200c20014105746a200241057410848e808000220941e0026a201720014104746a200241047410848e8080001a200c20063b019604200541f0006a41086a2218200f290300370300200541f0006a41106a22192010290300370300200541f0006a41186a221a200e290300370300200541e0006a41086a221b20162802003602002005200529039001370370200520052903b00137036020092f019604220241016a21172002410c4f0d0b201320066b22062017470d0c200741016a211320094198046a200c20014102746a4198046a200641027410848e80800021074100210602400340200720064102746a280200220120063b0194042001200936029004200620024f0d01200620062002496a220620024d0d000b0b200e201a29030037030020102019290300370300200f20182903003703002016201b2802003602002005200529037037039001200520052903603703b001200c200920081b220220034105746a210102400240200341016a220620022f01960422074d0d0020012005290340370000200141186a200541c0006a41186a290300370000200141106a200541c0006a41106a290300370000200141086a200541c0006a41086a2903003700000c010b200220064105746a2001200720036b220841057410fe8d8080001a200141186a200541c0006a41186a290300370000200141106a200541c0006a41106a290300370000200141086a200541c0006a41086a29030037000020012005290340370000200241e0026a220120064104746a200120034104746a200841047410fe8d8080001a0b200741016a2117200220034104746a220141e0026a2015360200200141e4026a2005290330370200200141ec026a200541306a41086a221528020036020020024198046a21010240200341026a2218200741026a22084f0d00200120184102746a200120064102746a200720036b41027410fe8d8080001a0b200120064102746a2014360200200220173b0196040240200620084f0d000240200720036b221441016a4103712207450d00200220034102746a419c046a210103402001280200220320063b0194042003200236029004200141046a2101200641016a21062007417f6a22070d000b0b20144103490d00200220064102746a41a4046a21010340200141746a280200220720063b0194042007200236029004200141786a2802002207200641016a3b01940420072002360290042001417c6a2802002207200641026a3b019404200720023602900420012802002207200641036a3b0194042007200236029004200141106a21012008200641046a2206470d000b0b200541106a41186a2206200e290300370300200541106a41106a22022010290300370300200541106a41086a2201200f290300370300200541086a220720162802003602002005200529039001370310200520052903b0013703002011418080808078460d02200541c0006a41186a2006290300370300200541c0006a41106a2002290300370300200541c0006a41086a20012903003703002015200728020036020020052005290310370340200520052903003703302009211420132107200c210620112115200c2802900422020d000b0b200428020022022802002201450d0b2002280204210941002d00fca3c680001a41c80441002802c8a3c68000118180808000002206450d0c2006200136029804200641003b019604200641003602900420022006360200200141003b01940420012006360290042002200941016a36020420092013470d0d20062f0196042202410b4f0d0e2006200241016a22093b019604200620024105746a22012005290340370200200141086a200541c0006a41086a290300370200200141186a200541c0006a41186a290300370200200141106a200541c0006a41106a290300370200200620024104746a220241e0026a2011360200200241e4026a2005290330370200200241ec026a200541306a41086a28020036020020064198046a20094102746a200d360200200d20093b019404200d2006360290040b2000200a3602082000200b360204200020123602000b200541c0016a2480808080000f0b410441980410b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b41dccdc2800041354194cec2800010f880808000000b410441c80410b280808000000b2002410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b2017410c41cccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b4184cbc2800010a081808000000b410441c80410b280808000000b41b4ccc28000413041e4ccc2800010f880808000000b4194cbc28000412041f4ccc2800010f880808000000ba61601137f23808080800041d0026b220424808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022052f01f6062206410b490d004101210741042106200128020822084105490d03200821062008417b6a0e020302010b2001280204210902402001280208220841016a220120064b0d002005200141d0006c6a2005200841d0006c6a200620086b41d0006c10fe8d8080001a0b2005200841d0006c6a200241d00010848e8080001a2005200641016a3b01f6060c030b200841796a210841002107410621060c010b4100210741052106410021080b2001280204210a41002d00fca3c680001a41f80641002802c8a3c6800011818080800000220b450d03200b41003b01f606200b41003602f006200b20052f01f606220c2006417f736a22013b01f60620044180026a2005200641d0006c6a220d41cc0010848e8080001a200441e2006a200d41cf006a2d00003a00002004200d2f004d3b01602001410c4f0d04200c200641016a220e6b2001470d05200d2d004c210f200b2005200e41d0006c6a200141d0006c10848e808000210d200520063b01f606200441b0016a20044180026a41cc0010848e8080001a200441fc016a41026a2206200441e0006a41026a2d00003a0000200420042f01603b01fc0102402005200d20071b22102f01f606220120084d0d002010200841d0006c6a220c41d0006a200c200120086b41d0006c10fe8d8080001a0b200a410020071b21092010200841d0006c6a200241d00010848e8080001a2010200141016a3b01f606200441106a200441b0016a41cc0010848e8080001a200441ac016a41026a20062d00003a0000200420042f01fc013b01ac01200f41ff01714102470d01201021050b2000200836020820002009360204200020053602000c010b200441e0006a200441106a41cc0010848e8080001a200441dc006a41026a2211200441ac016a41026a22122d00003a0000200420042f01ac013b015c0240024020052802f00622010d00410021130c010b200441fc016a41026a210e41002113200d2114200f2115034020012107200a2013470d0620052f01f40621050240024002400240024020072f01f6062213410b490d0041012116200541054f0d012005210d410421050c020b200541016a21010240200520134f22020d002007200141d0006c6a2007200541d0006c6a201320056b41d0006c10fe8d8080001a0b201341016a210d2007200541d0006c6a200441e0006a41cc0010848e808000220620153a004c200620042f015c3b004d200641cf006a200441dc006a41026a2d00003a0000200741f8066a2106201341026a210a024020020d00200541027420066a41086a200620014102746a201320056b41027410fe8d8080001a0b2007200d3b01f606200620014102746a20143602002001200a4f0d020240201320056b220d41016a4103712206450d00200720054102746a41fc066a210503402005280200220220013b01f406200220073602f006200541046a2105200141016a21012006417f6a22060d000b0b200d4103490d02200141027420076a4184076a21050340200541746a280200220620013b01f406200620073602f006200541786a2802002206200141016a3b01f406200620073602f0062005417c6a2802002206200141026a3b01f406200620073602f00620052802002206200141036a3b01f406200620073602f006200541106a2105200a200141046a2201470d000c030b0b2005210d024002402005417b6a0e020201000b200541796a210d41002116410621050c010b41002116410521054100210d0b41002d00fca3c680001a41a80741002802c8a3c6800011818080800000220b450d09200b41003b01f606200b41003602f006200b20072f01f60622022005417f736a22013b01f60620044180026a2007200541d0006c6a220641cc0010848e8080001a200e200641cf006a2d00003a0000200420062f004d3b01fc012001410c4f0d0a2002200541016a220c6b2001470d0b20062d004c210f200b2007200c41d0006c6a200141d0006c10848e8080002102200720053b01f606200441b0016a20044180026a41cc0010848e8080001a2012200e2d00003a0000200420042f01fc013b01ac0120022f01f606220141016a21062001410c4f0d0c201320056b22052006470d0d200a41016a2113200241f8066a2007200c4102746a41f8066a200541027410848e808000210a4100210502400340200a20054102746a280200220620053b01f406200620023602f006200520014f0d01200520052001496a220520014d0d000b0b20044180026a200441b0016a41cc0010848e8080001a200e20122d00003a0000200420042f01ac013b01fc010240200d41016a22052007200220161b22062f01f60622014b0d002006200541d0006c6a2006200d41d0006c6a2001200d6b41d0006c10fe8d8080001a0b200141016a21162006200d41d0006c6a200441e0006a41cc0010848e808000220a20153a004c200a20042f015c3b004d200a41cf006a20112d00003a0000200641f8066a210a0240200d41026a2215200141026a220c4f0d00200a20154102746a200a20054102746a2001200d6b41027410fe8d8080001a0b200a20054102746a2014360200200620163b01f60602402005200c4f0d0002402001200d6b221641016a410371220a450d002006200d4102746a41fc066a210103402001280200220d20053b01f406200d20063602f006200141046a2101200541016a2105200a417f6a220a0d000b0b20164103490d00200620054102746a4184076a21010340200141746a280200220a20053b01f406200a20063602f006200141786a280200220a200541016a3b01f406200a20063602f0062001417c6a280200220a200541026a3b01f406200a20063602f0062001280200220a200541036a3b01f406200a20063602f006200141106a2101200c200541046a2205470d000b0b200441106a20044180026a41cc0010848e8080001a2004410c6a41026a2205200e2d00003a0000200420042f01fc013b010c200f41ff01714102470d010b2000200836020820002009360204200020103602000c030b200441e0006a200441106a41cc0010848e8080001a201120052d00003a0000200420042f010c3b015c200221142013210a20072105200f211520072802f00622010d000b0b200328020022012802002206450d0a2001280204210241002d00fca3c680001a41a80741002802c8a3c68000118180808000002205450d0b200520063602f806200541003b01f606200541003602f00620012005360200200641003b01f406200620053602f0062001200241016a36020420022013470d0c20052f01f6062201410b4f0d0d2005200141016a22063b01f6062005200141d0006c6a200441e0006a41cc0010848e8080002201200f3a004c200120042f015c3b004d200141cf006a200441dc006a41026a2d00003a0000200541f8066a20064102746a200b360200200020083602082000200936020420002010360200200b20063b01f406200b20053602f0060b200441d0026a2480808080000f0b410441f80610b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b41dccdc2800041354194cec2800010f880808000000b410441a80710b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b2006410c41cccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b4184cbc2800010a081808000000b410441a80710b280808000000b41b4ccc28000413041e4ccc2800010f880808000000b4194cbc28000412041f4ccc2800010f880808000000b9718050b7f017e037f017e037f23808080800041c0006b220524808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022062f01ba022207410b490d004101210841042107200128020822094105490d03200921072009417b6a0e020302010b200641b4016a220820012802082209410c6c6a210a2001280204210b02400240200941016a220120074d0d00200a2002290200370200200a41086a200241086a2802003602000c010b20082001410c6c6a200a200720096b2208410c6c10fe8d8080001a200a41086a200241086a280200360200200a2002290200370200200620014104746a200620094104746a200841047410fe8d8080001a0b200620094104746a220141086a200341086a290200370200200120032902003702002006200741016a3b01ba020c030b200941796a210941002108410621070c010b4100210841052107410021090b2001280204210a41002d00fca3c680001a41bc0241002802c8a3c6800011818080800000220c450d03200c41003b01ba02200c41003602b001200c20062f01ba02220d2007417f736a22013b01ba02200541306a41086a200620074104746a220e41086a2902003703002005200e2902003703302001410c4f0d04200d200741016a220e6b2001470d05200641b4016a220d2007410c6c6a220f2902042110200f280200210f200c41b4016a200d200e410c6c6a2001410c6c10848e8080001a200c2006200e4104746a200141047410848e808000210e200620073b01ba02200541206a41086a200541306a41086a290300370300200520052903303703202006200e20081b221141b4016a220d2009410c6c6a21010240024020112f01ba02220720094b0d0020012002290200370200200141086a200241086a2802003602000c010b200d200941016a2212410c6c6a2001200720096b220d410c6c10fe8d8080001a200141086a200241086a28020036020020012002290200370200201120124104746a201120094104746a200d41047410fe8d8080001a0b200a410020081b210b201120094104746a220141086a200341086a29020037020020012003290200370200200541086a2201200541206a41086a2903003703002011200741016a3b01ba0220052005290320370300200f418080808078470d01201121060b200020093602082000200b360204200020063602000c010b200541106a41086a2001290300370300200520052903003703100240024020062802b00122010d00410021120c010b41002112200e211320102114200f2115034020012108200a2012470d0620062f01b80221060240024002400240024020082f01ba022212410b490d004101210e200641054f0d0120062102410421060c020b200841b4016a220a2006410c6c6a2107200641016a2101201241016a21030240024020062012490d002007201437020420072015360200200820064104746a22072005290310370200200741086a200541106a41086a2903003702000c010b200a2001410c6c6a2007201220066b220a410c6c10fe8d8080001a2007201437020420072015360200200820014104746a200820064104746a2207200a41047410fe8d8080001a200741086a200541106a41086a290300370200200720052903103702002006410274200841bc026a22076a41086a200720014102746a200a41027410fe8d8080001a0b200820033b01ba02200820014102746a41bc026a20133602002001201241026a220a4f0d020240201220066b220241016a4103712207450d00200820064102746a41c0026a210603402006280200220320013b01b802200320083602b001200641046a2106200141016a21012007417f6a22070d000b0b20024103490d02200141027420086a41c8026a21060340200641746a280200220720013b01b802200720083602b001200641786a2802002207200141016a3b01b802200720083602b0012006417c6a2802002207200141026a3b01b802200720083602b00120062802002207200141036a3b01b802200720083602b001200641106a2106200a200141046a2201470d000c030b0b20062102024002402006417b6a0e020201000b200641796a21024100210e410621060c010b4100210e41052106410021020b41002d00fca3c680001a41ec0241002802c8a3c6800011818080800000220c450d09200c41003b01ba02200c41003602b001200c20082f01ba0222032006417f736a22013b01ba02200541306a41086a220d200820064104746a220741086a290200370300200520072902003703302001410c4f0d0a2003200641016a22076b2001470d0b200841b4016a22032006410c6c6a220f2902042110200f280200210f200c41b4016a20032007410c6c6a2001410c6c10848e8080001a200c200820074104746a200141047410848e8080002103200820063b01ba02200541206a41086a2216200d2903003703002005200529033037032020032f01ba02220141016a21172001410c4f0d0c201220066b22062017470d0d200a41016a2112200341bc026a200820074102746a41bc026a200641027410848e808000210a4100210602400340200a20064102746a280200220720063b01b802200720033602b001200620014f0d01200620062001496a220620014d0d000b0b200d20162903003703002005200529032037033020082003200e1b220141b4016a220e2002410c6c6a210a02400240200241016a220620012f01ba0222074d0d00200a2014370204200a20153602000c010b200e2006410c6c6a200a200720026b220e410c6c10fe8d8080001a200a2014370204200a2015360200200120064104746a200120024104746a200e41047410fe8d8080001a0b200741016a2115200120024104746a220a2005290310370200200a41086a200541106a41086a2217290300370200200141bc026a210a0240200241026a2216200741026a220e4f0d00200a20164102746a200a20064102746a200720026b41027410fe8d8080001a0b200a20064102746a2013360200200120153b01ba0202402006200e4f0d000240200720026b221341016a410371220a450d00200120024102746a41c0026a210703402007280200220220063b01b802200220013602b001200741046a2107200641016a2106200a417f6a220a0d000b0b20134103490d00200120064102746a41c8026a21070340200741746a280200220a20063b01b802200a20013602b001200741786a280200220a200641016a3b01b802200a20013602b0012007417c6a280200220a200641026a3b01b802200a20013602b0012007280200220a200641036a3b01b802200a20013602b001200741106a2107200e200641046a2206470d000b0b200541086a2206200d29030037030020052005290330370300200f418080808078470d010b200020093602082000200b360204200020113602000c030b2017200629030037030020052005290300370310200321132012210a2008210620102114200f211520082802b00122010d000b0b200428020022012802002207450d0a2001280204210341002d00fca3c680001a41ec0241002802c8a3c68000118180808000002206450d0b200620073602bc02200641003b01ba02200641003602b00120012006360200200741003b01b802200720063602b0012001200341016a36020420032012470d0c20062f01ba022201410b4f0d0d2006200141016a22073b01ba0220062001410c6c6a220341b8016a2010370200200341b4016a200f360200200620014104746a220141086a200541106a41086a29030037020020012005290310370200200641bc026a20074102746a200c360200200020093602082000200b36020420002011360200200c20073b01b802200c20063602b0010b200541c0006a2480808080000f0b410441bc0210b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b41dccdc2800041354194cec2800010f880808000000b410441ec0210b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b2017410c41cccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b4184cbc2800010a081808000000b410441ec0210b280808000000b41b4ccc28000413041e4ccc2800010f880808000000b4194cbc28000412041f4ccc2800010f880808000000b9718050b7f017e037f017e037f23808080800041c0006b220524808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022062f01ba022207410b490d004101210841042107200128020822094105490d03200921072009417b6a0e020302010b200641b4016a220820012802082209410c6c6a210a2001280204210b02400240200941016a220120074d0d00200a2002290200370200200a41086a200241086a2802003602000c010b20082001410c6c6a200a200720096b2208410c6c10fe8d8080001a200a41086a200241086a280200360200200a2002290200370200200620014104746a200620094104746a200841047410fe8d8080001a0b200620094104746a220141086a200341086a290200370200200120032902003702002006200741016a3b01ba020c030b200941796a210941002108410621070c010b4100210841052107410021090b2001280204210a41002d00fca3c680001a41bc0241002802c8a3c6800011818080800000220c450d03200c41003b01ba02200c41003602b001200c20062f01ba02220d2007417f736a22013b01ba02200541306a41086a200620074104746a220e41086a2902003703002005200e2902003703302001410c4f0d04200d200741016a220e6b2001470d05200641b4016a220d2007410c6c6a220f2902042110200f280200210f200c41b4016a200d200e410c6c6a2001410c6c10848e8080001a200c2006200e4104746a200141047410848e808000210e200620073b01ba02200541206a41086a200541306a41086a290300370300200520052903303703202006200e20081b221141b4016a220d2009410c6c6a21010240024020112f01ba02220720094b0d0020012002290200370200200141086a200241086a2802003602000c010b200d200941016a2212410c6c6a2001200720096b220d410c6c10fe8d8080001a200141086a200241086a28020036020020012002290200370200201120124104746a201120094104746a200d41047410fe8d8080001a0b200a410020081b210b201120094104746a220141086a200341086a29020037020020012003290200370200200541086a2201200541206a41086a2903003703002011200741016a3b01ba0220052005290320370300200f418080808078470d01201121060b200020093602082000200b360204200020063602000c010b200541106a41086a2001290300370300200520052903003703100240024020062802b00122010d00410021120c010b41002112200e211320102114200f2115034020012108200a2012470d0620062f01b80221060240024002400240024020082f01ba022212410b490d004101210e200641054f0d0120062102410421060c020b200841b4016a220a2006410c6c6a2107200641016a2101201241016a21030240024020062012490d002007201437020420072015360200200820064104746a22072005290310370200200741086a200541106a41086a2903003702000c010b200a2001410c6c6a2007201220066b220a410c6c10fe8d8080001a2007201437020420072015360200200820014104746a200820064104746a2207200a41047410fe8d8080001a200741086a200541106a41086a290300370200200720052903103702002006410274200841bc026a22076a41086a200720014102746a200a41027410fe8d8080001a0b200820033b01ba02200820014102746a41bc026a20133602002001201241026a220a4f0d020240201220066b220241016a4103712207450d00200820064102746a41c0026a210603402006280200220320013b01b802200320083602b001200641046a2106200141016a21012007417f6a22070d000b0b20024103490d02200141027420086a41c8026a21060340200641746a280200220720013b01b802200720083602b001200641786a2802002207200141016a3b01b802200720083602b0012006417c6a2802002207200141026a3b01b802200720083602b00120062802002207200141036a3b01b802200720083602b001200641106a2106200a200141046a2201470d000c030b0b20062102024002402006417b6a0e020201000b200641796a21024100210e410621060c010b4100210e41052106410021020b41002d00fca3c680001a41ec0241002802c8a3c6800011818080800000220c450d09200c41003b01ba02200c41003602b001200c20082f01ba0222032006417f736a22013b01ba02200541306a41086a220d200820064104746a220741086a290200370300200520072902003703302001410c4f0d0a2003200641016a22076b2001470d0b200841b4016a22032006410c6c6a220f2902042110200f280200210f200c41b4016a20032007410c6c6a2001410c6c10848e8080001a200c200820074104746a200141047410848e8080002103200820063b01ba02200541206a41086a2216200d2903003703002005200529033037032020032f01ba02220141016a21172001410c4f0d0c201220066b22062017470d0d200a41016a2112200341bc026a200820074102746a41bc026a200641027410848e808000210a4100210602400340200a20064102746a280200220720063b01b802200720033602b001200620014f0d01200620062001496a220620014d0d000b0b200d20162903003703002005200529032037033020082003200e1b220141b4016a220e2002410c6c6a210a02400240200241016a220620012f01ba0222074d0d00200a2014370204200a20153602000c010b200e2006410c6c6a200a200720026b220e410c6c10fe8d8080001a200a2014370204200a2015360200200120064104746a200120024104746a200e41047410fe8d8080001a0b200741016a2115200120024104746a220a2005290310370200200a41086a200541106a41086a2217290300370200200141bc026a210a0240200241026a2216200741026a220e4f0d00200a20164102746a200a20064102746a200720026b41027410fe8d8080001a0b200a20064102746a2013360200200120153b01ba0202402006200e4f0d000240200720026b221341016a410371220a450d00200120024102746a41c0026a210703402007280200220220063b01b802200220013602b001200741046a2107200641016a2106200a417f6a220a0d000b0b20134103490d00200120064102746a41c8026a21070340200741746a280200220a20063b01b802200a20013602b001200741786a280200220a200641016a3b01b802200a20013602b0012007417c6a280200220a200641026a3b01b802200a20013602b0012007280200220a200641036a3b01b802200a20013602b001200741106a2107200e200641046a2206470d000b0b200541086a2206200d29030037030020052005290330370300200f418080808078470d010b200020093602082000200b360204200020113602000c030b2017200629030037030020052005290300370310200321132012210a2008210620102114200f211520082802b00122010d000b0b200428020022012802002207450d0a2001280204210341002d00fca3c680001a41ec0241002802c8a3c68000118180808000002206450d0b200620073602bc02200641003b01ba02200641003602b00120012006360200200741003b01b802200720063602b0012001200341016a36020420032012470d0c20062f01ba022201410b4f0d0d2006200141016a22073b01ba0220062001410c6c6a220341b8016a2010370200200341b4016a200f360200200620014104746a220141086a200541106a41086a29030037020020012005290310370200200641bc026a20074102746a200c360200200020093602082000200b36020420002011360200200c20073b01b802200c20063602b0010b200541c0006a2480808080000f0b410441bc0210b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b41dccdc2800041354194cec2800010f880808000000b410441ec0210b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b2017410c41cccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b4184cbc2800010a081808000000b410441ec0210b280808000000b41b4ccc28000413041e4ccc2800010f880808000000b4194cbc28000412041f4ccc2800010f880808000000b8d11030b7f037e047f23808080800041206b220324808080800020002802002104024020002802042205450d0002400240200541037122060d00200521070c010b2005210703402007417f6a2107200420042f01e2014102746a41e4016a28020021042006417f6a22060d000b0b20054104490d000340200420042f01e2014102746a41e4016a280200220420042f01e2014102746a41e4016a280200220420042f01e2014102746a41e4016a280200220420042f01e2014102746a41e4016a28020021042007417c6a22070d000b0b20012802202108200128021c2109200128021821062001280214210a2001280210210b200128020c210c2001280208210d2001290200210e0340200bad422086200cad84210f02400240034002400240200d418180808078460d0020062107200d2105200e21100c010b20062008460d02200641146a2107200629020c210f20062802082105200629020021100b02402005418080808078460d00200fa72111024020072008470d00418080808078210d200821060c040b200741146a21062007280210210b200728020c210c2007290200210e02402007280208220d418080808078470d00418080808078210d0c040b2010200e520d03200729020c210f2010210e2005450d01201141002802c0a3c68000118080808000002010210e0c010b0b20082007460d00200820076b220441146e220141017121054100210602402004416c6a4114490d00200741206a2104200141feffffff007121014100210603400240200441686a280200450d002004416c6a28020041002802c0a3c68000118080808000000b02402004417c6a280200450d00200428020041002802c0a3c68000118080808000000b200441286a21042001200641026a2206470d000b0b2005450d002007200641146c6a2204280208450d00200441086a28020441002802c0a3c68000118080808000000b02402009450d00200a41002802c0a3c68000118080808000000b02402000280204220d450d00200028020021060340024002400240024020062f01e2012207450d00200641e4016a220520074102746a28020022042f01e201220141054f0d0320052007417f6a22084102746a28020022052f01e2012207410520016b2212490d012005200720126b22133b01e201200441053b01e201200420124103746a2004200141037410fe8d8080001a200441dc006a220b2012410c6c6a200b2001410c6c10fe8d8080001a2007201341016a22116b220c410420016b470d022004200520114103746a200c410374220010848e8080002107200b200541dc006a22022011410c6c6a200c410c6c220c10848e808000210b200341086a20022013410c6c6a220241086a28020022143602002002290200210e200620084103746a220229000021102002200520134103746a2900003700002003200e37030020062008410c6c6a220641dc006a2213290200210f2013200e370200200641e4006a2206280200211320062014360200200720006a2010370000200b200c6a220641086a20133602002006200f370200200d4101460d03200741e4016a2206201241027422126a2006200141027441046a10fe8d8080001a2006200520114102746a41e4016a201210848e8080001a20072802e401220641003b01e00120062007360258200741e8016a280200220641013b01e00120062007360258200741ec016a280200220641023b01e00120062007360258200741f0016a280200220641033b01e00120062007360258200741f4016a280200220641043b01e00120062007360258200741f8016a280200220641053b01e001200620073602580c030b41dccec28000411941f8cec2800010f880808000000b41a4cec28000412741cccec2800010f880808000000b4184cdc28000412841accdc2800010f880808000000b20042106200d417f6a220d0d000b0b200341206a2480808080000f0b200f422088a72114024002400240024002400240024020042f01e2012207410b490d004100211302400240034020042802582204450d01201341016a211320042f01e201410b4f0d000c020b0b200028020421012000280200210741002d00fca3c680001a41940241002802c8a3c68000118180808000002204450d02200420073602e401200441003b01e2012004410036025820002004360200200741003b01e001200720043602582000200141016a22133602040b41002d00fca3c680001a41e40141002802c8a3c68000118180808000002201450d02200141003b01e201200141003602582013417f6a2212450d04034041002d00fca3c680001a41940241002802c8a3c68000118180808000002207450d04200720013602e401200741003b01e20120074100360258200141003b01e00120012007360258200721012012417f6a2212450d050c000b0b2004200741016a3b01e201200420074103746a201037000020042007410c6c6a220741e4006a2014360200200741e0006a2011360200200741dc006a20053602000c040b410441940210b280808000000b410441e40110b280808000000b410441940210b280808000000b20042f01e2012207410b4f0d012004200741016a22123b01e201200420074103746a201037000020042007410c6c6a220741e0006a2011360200200741dc006a2005360200200741e4006a2014360200200420124102746a41e4016a2001360200200120123b01e001200120043602582013450d0002400240201341037122010d00201321070c010b2013210703402007417f6a2107200420042f01e2014102746a41e4016a28020021042001417f6a22010d000b0b20134104490d000340200420042f01e2014102746a41e4016a280200220420042f01e2014102746a41e4016a280200220420042f01e2014102746a41e4016a280200220420042f01e2014102746a41e4016a28020021042007417c6a22070d000b0b2002200228020041016a3602000c010b0b4194cbc28000412041f4ccc2800010f880808000000bc513020f7f057e23808080800041c0016b220324808080800020002802002104024020002802042205450d0002400240200541037122060d00200521070c010b2005210703402007417f6a2107200420042f01e6024102746a41e8026a28020021042006417f6a22060d000b0b20054104490d000340200420042f01e6024102746a41e8026a280200220420042f01e6024102746a41e8026a280200220420042f01e6024102746a41e8026a280200220420042f01e6024102746a41e8026a28020021042007417c6a22070d000b0b200341306a200141306a280200360200200341286a200141286a290200370300200341206a200141206a290200370300200341186a200141186a290200370300200341106a200141106a290200370300200341086a200141086a2902003703002003200129020037030020034198016a41016a2108200341116a21090240034020032802042106200328020c210a0240024020032d001022074102460d0020082009290000370000200841186a200941186a290000370000200841106a200941106a290000370000200841086a200941086a29000037000020070d010c030b2006200a460d0220082006290000370000200841086a200641086a290000370000200841106a200641106a290000370000200841186a200641186a2900003700002003200641206a22063602040b200341f8006a41186a220b200841186a290000370300200341f8006a41106a220c200841106a290000370300200341f8006a41086a220d200841086a29000037030020032008290000370378024002402006200a460d0020092006290000370000200941086a2201200641086a220e290000370000200941106a2205200641106a220f290000370000200941186a2210200641186a22112900003700002003200641206a220736020402400240200341f8006a2009412010888e8080000d00200d200e290000370300200c200f290000370300200b2011290000370300200320062900003703782007200a460d0102400340200920072900003700002001200741086a2900003700002005200741106a2900003700002010200741186a290000370000200341f8006a2009412010888e8080000d01200d2001290000370300200c2005290000370300200b201029000037030020032009290000370378200741206a2207200a460d030c000b0b2003200741206a3602040b200341013a00100c020b2003200a3602040b2009200329039801370000200941086a20034198016a41086a290300370000200941106a20034198016a41106a290300370000200941186a20034198016a41186a290300370000200341003a00100b200341386a41186a2210200b290300370300200341386a41106a220a200c290300370300200341386a41086a220b200d29030037030020032003290378370338024002400240024002400240024020042f01e6022207410b490d004100210502400240034020042802e0022204450d01200541016a210520042f01e602410b4f0d000c020b0b200028020421062000280200210741002d00fca3c680001a41980341002802c8a3c68000118180808000002204450d02200420073602e802200441003b01e602200441003602e00220002004360200200741003b01e402200720043602e0022000200641016a22053602040b41002d00fca3c680001a41e80241002802c8a3c68000118180808000002206450d02200641003b01e602200641003602e0022005417f6a2201450d04034041002d00fca3c680001a41980341002802c8a3c68000118180808000002207450d04200720063602e802200741003b01e602200741003602e002200641003b01e402200620073602e002200721062001417f6a2201450d050c000b0b2004200741016a3b01e602200420074105746a22072003290338370000200741186a2010290300370000200741106a200a290300370000200741086a200b2903003700000c040b410441980310b280808000000b410441e80210b280808000000b410441980310b280808000000b20042f01e6022207410b4f0d012004200741016a22013b01e602200420074105746a22072003290338370000200741186a2010290300370000200741106a200a290300370000200741086a200b290300370000200420014102746a41e8026a2006360200200620013b01e402200620043602e0022005450d0002400240200541037122060d00200521070c010b2005210703402007417f6a2107200420042f01e6024102746a41e8026a28020021042006417f6a22060d000b0b20054104490d000340200420042f01e6024102746a41e8026a280200220420042f01e6024102746a41e8026a280200220420042f01e6024102746a41e8026a280200220420042f01e6024102746a41e8026a28020021042007417c6a22070d000b0b2002200228020041016a3602000c010b0b4194cbc28000412041f4ccc2800010f880808000000b02402003280208450d00200328020041002802c0a3c68000118080808000000b024020002802042205450d00200028020021060340024002400240024020062f01e6022204450d00200641e8026a220920044102746a28020022072f01e602220141054f0d0320092004417f6a220b4102746a28020022092f01e6022204410520016b2210490d012009200420106b22083b01e602200741053b01e602200720104105746a2007200141057410fe8d8080001a2004200841016a220a6b2204410420016b470d0220072009200a4105746a2004410574220c10848e80800021042006200b4105746a22062900002112200920084105746a220841086a2900002113200841106a2900002114200841186a290000211520062008290000370000200641186a2208290000211620082015370000200641106a2208290000211520082014370000200641086a22062900002114200620133700002004200c6a220641186a2016370000200641106a2015370000200641086a20143700002006201237000020054101460d05200441e8026a2206201041027422086a2006200141027441046a10fe8d8080001a20062009200a4102746a41e8026a200810848e8080001a20042802e802220641003b01e402200620043602e002200441ec026a280200220641013b01e402200620043602e002200441f0026a280200220641023b01e402200620043602e002200441f4026a280200220641033b01e402200620043602e002200441f8026a280200220641043b01e402200620043602e002200441fc026a280200220641053b01e402200620043602e0020c030b41dccec28000411941f8cec2800010f880808000000b41a4cec28000412741cccec2800010f880808000000b4184cdc28000412841accdc2800010f880808000000b200721062005417f6a22050d000b0b200341c0016a2480808080000b9e0f03087f017e027f23808080800041c0006b220324808080800020002802002104024020002802042205450d0002400240200541037122060d00200521070c010b2005210703402007417f6a2107200420042f018a014102746a418c016a28020021042006417f6a22060d000b0b20054104490d000340200420042f018a014102746a418c016a280200220420042f018a014102746a418c016a280200220420042f018a014102746a418c016a280200220420042f018a014102746a418c016a28020021042007417c6a22070d000b0b200341086a41186a2208200141186a280200360200200341086a41106a2209200141106a290200370300200341086a41086a200141086a29020037030020032001290200370308200341246a200341086a10b38980800002402003280224418080808078460d00200341246a41086a210a0240034002400240024002400240024020042f018a012207410b490d004100210502400240034020042802002204450d01200541016a210520042f018a01410b4f0d000c020b0b200028020421062000280200210741002d00fca3c680001a41bc0141002802c8a3c68000118180808000002204450d022004200736028c01200441003b018a012004410036020020002004360200200741003b018801200720043602002000200641016a22053602040b41002d00fca3c680001a418c0141002802c8a3c68000118180808000002206450d02200641003b018a01200641003602002005417f6a2201450d04034041002d00fca3c680001a41bc0141002802c8a3c68000118180808000002207450d042007200636028c01200741003b018a0120074100360200200641003b01880120062007360200200721062001417f6a2201450d050c000b0b2004200741016a3b018a012003290224210b20042007410c6c6a2207410c6a200a280200360200200741046a200b3702000c040b410441bc0110b280808000000b4104418c0110b280808000000b410441bc0110b280808000000b20042f018a012207410b4f0d022004200741016a22013b018a0120042007410c6c6a2207410c6a200a280200360200200741046a2003290224370200200420014102746a418c016a2006360200200620013b018801200620043602002005450d0002400240200541037122060d00200521070c010b2005210703402007417f6a2107200420042f018a014102746a418c016a28020021042006417f6a22060d000b0b20054104490d000340200420042f018a014102746a418c016a280200220420042f018a014102746a418c016a280200220420042f018a014102746a418c016a280200220420042f018a014102746a418c016a28020021042007417c6a22070d000b0b2002200228020041016a360200200341246a200341086a10b3898080002003280224418080808078470d000c020b0b4194cbc28000412041f4ccc2800010f880808000000b02402008280200220420092802002201460d00200420016b2204410c6e22064101712105410021070240200441746a410c490d00200641feffffff017121062001210441002107034002402004280200450d00200441046a28020041002802c0a3c68000118080808000000b02402004410c6a280200450d00200441106a28020041002802c0a3c68000118080808000000b200441186a21042006200741026a2207470d000b0b2005450d0020012007410c6c6a2204280200450d00200428020441002802c0a3c68000118080808000000b02402003411c6a280200450d00200328021441002802c0a3c68000118080808000000b024020032802082204418280808078480d002004450d00200328020c41002802c0a3c68000118080808000000b024020002802042201450d00200028020021070340024002400240024020072f018a012205450d002007418c016a220220054102746a28020022042f018a01220641054f0d0320022005417f6a220c4102746a28020022022f018a012200410520066b2205490d012002200020056b220a3b018a01200441053b018a01200441046a22082005410c6c6a20082006410c6c10fe8d8080001a2000200a41016a22096b2200410420066b470d022008200241046a220d2009410c6c6a2000410c6c220010848e8080002108200d200a410c6c6a220a41086a280200210d2007200c410c6c6a220741046a220c290200210b200c200a2902003702002007410c6a2207280200210a2007200d360200200820006a220741086a200a3602002007200b37020020014101460d052004418c016a2207200541027422056a2007200641027441046a10fe8d8080001a2007200220094102746a418c016a200510848e8080001a200428028c01220741003b0188012007200436020020044190016a280200220741013b0188012007200436020020044194016a280200220741023b0188012007200436020020044198016a280200220741033b018801200720043602002004419c016a280200220741043b01880120072004360200200441a0016a280200220741053b018801200720043602000c030b41dccec28000411941f8cec2800010f880808000000b41a4cec28000412741cccec2800010f880808000000b4184cdc28000412841accdc2800010f880808000000b200421072001417f6a22010d000b0b200341c0006a2480808080000be805010d7f200341246a280200200341c8006a2802002204200441284b22041b21052003280220200341206a20041b2106024002400240200341cc006a2d000041ff01712207450d00200341cd006a2d000041ff01712108034020012f01f606220941d0006c210a41002104417f210b4100210c024002400340200b210d200a2004460d010240417f2003200120046a220b412010888e808000220e410047200e4100481b220e0d00417f2006200b41206a220e280200200e200b41c8006a280200220f41284b22101b2005200b41246a280200200f20101b220e2005200e491b10888e808000220f2005200e6b200f1b220e410047200e4100481b220e0d0002402007200b41cc006a2d0000220f4f0d00200c21090c030b4101210e2007200f470d0002402008200b41cd006a2d000041ff0171220e4f0d00200c21090c030b2008200e47210e0b200c41016a210c200441d0006a2104200d41016a210b200e4101460d000b200e41ff0171450d01200d41016a21090b2002450d032002417f6a2102200120094102746a41f8066a28020021010c010b0b41002104200b21090c020b034020012f01f606220f41d0006c210c41002104417f2109024002400240024003400240200c2004470d00200f21090c030b0240417f2003200120046a220b412010888e808000220e410047200e4100481b220e0d00417f2006200b41206a220e280200200e200b41c8006a280200220d41284b220a1b2005200b41246a280200200d200a1b220e2005200e491b10888e808000220d2005200e6b200d1b220e410047200e4100481b220e450d020b200441d0006a2104200941016a2109200e4101460d000b200e41ff01710d01410021040c060b200b41cc006a2d0000450d01200941016a21090b20020d010c030b200941016a2109410021040c030b2002417f6a2102200120094102746a41f8066a28020021010c000b0b41012104410021020b20002001360204200020043602002000410c6a2009360200200041086a20023602000ba90402097f017e23808080800041306b22032480808080000240024002400240024002402001280204220441286e2205200220052002491b22050d00410821060c010b200541b3e6cc194b0d01200541286c2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b410021072003410036020c200320063602082003200536020402402002450d00200128020041206a2105034002402004411f4b0d00200328020421080c050b200120053602002001200441606a22063602042003280204210820064108490d042001200641786a22043602042001200541086a360200200341106a41086a2209200541606a220641086a290000370300200341106a41106a220a200641106a290000370300200341106a41186a220b200641186a290000370300200320062900003703102005290000210c024020072008470d00200341046a2007109a86808000200328020c21070b2003280208200741286c6a220620032903103703002006200c370320200641086a2009290300370300200641106a200a290300370300200641186a200b2903003703002003200741016a220736020c200541286a21052002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c030b10ae80808000000b4108200710b280808000000b20004180808080783602002008450d00200328020841002802c0a3c68000118080808000000b200341306a2480808080000beb0301097f23808080800041306b22032480808080000240024002400240200128020422044105762205200220052002491b22050d00410121060c010b200541ffffff1f4b0d0120054105742207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b410021072003410036020c20032006360208200320053602040240024002402002450d0020012802002105034020044120490d022001200441606a22043602042001200541206a2208360200200341106a41086a2209200541086a290000370300200341106a41106a220a200541106a290000370300200341106a41186a220b200541186a29000037030020032005290000370310024020072003280204470d00200341046a200710a18680800020032802082106200328020c21070b200620074105746a22052003290310370000200541186a200b290300370000200541106a200a290300370000200541086a20092903003700002003200741016a220736020c200821052002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c010b20004180808080783602002003280204450d00200641002802c0a3c68000118080808000000b200341306a2480808080000f0b10ae80808000000b4101200710b280808000000b980501067f23808080800041c0006b2203248080808000410421040240024002400240200128020041046a28020041146e2205200220052002491b2205450d00200541e6cc99334b0d02200541146c2206417f4c0d0241002d00fca3c680001a200641002802c8a3c68000118180808000002204450d010b20012001280204220741016a2206360204200341003602102003200436020c2003200536020802400240200620012802084d0d0020004180808080783602000c010b024002402002450d00200341146a41017221040340200341146a2001109f8d80800020032d001422064105460d02200341286a410f6a22072004410f6a280000360000200341286a41086a2208200441086a290000370300200320042900003703280240200328021022052003280208470d00200341086a200510a586808000200328021021050b200328020c200541146c6a22052003290328370001200520063a0000200541096a2008290300370000200541106a20072800003600002003200328021041016a3602102002417f6a22020d000b2001280204417f6a21070b2001200736020420002003290208370200200041086a200341086a41086a2802003602000c040b200041808080807836020020032802102201450d00200328020c220621054100210403402006200441146c6a21020240024002400240024020052d00000e0400010102040b200541086a21020c020b200241086a21020c010b200241046a21020b2002280200450d00200228020441002802c0a3c68000118080808000000b200441016a2104200541146a21052001417f6a22010d000b0b2003280208450d02200328020c41002802c0a3c68000118080808000000c020b4104200610b280808000000b10ae80808000000b200341c0006a2480808080000ba204010a7f23808080800041306b2203248080808000410121040240024002402001280200220541046a2802004105762206200220062002491b2206450d00200641ffffff1f4b0d0120064105742207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002204450d020b20012001280204220841016a22073602042003410036020c200320043602082003200636020402400240200720012802084b0d00024002402002450d00200541046a220928020022064120490d0103402009200641606a36020020052005280200220641206a360200200341106a41086a220a200641086a290000370300200341106a41106a220b200641106a290000370300200341106a41186a220c200641186a290000370300200320062900003703100240200328020c22062003280204470d00200341046a200610a186808000200328020c21060b2003280208220420064105746a22072003290310370000200741086a200a290300370000200741106a200b290300370000200741186a200c2903003700002003200641016a36020c2002417f6a2202450d01200928020022064120490d020c000b0b2001200836020420002003290204370200200041086a200341046a41086a2802003602000c020b200328020421060b20004180808080783602002006450d00200441002802c0a3c68000118080808000000b200341306a2480808080000f0b10ae80808000000b4101200710b280808000000bda0401067f23808080800041c0006b220324808080800002400240024002400240200128020441146e2204200220042002491b22040d00410421050c010b200441e6cc99334b0d02200441146c2206417f4c0d0241002d00fca3c680001a200641002802c8a3c68000118180808000002205450d010b200341003602102003200536020c20032004360208024002402002450d00200341146a41017221050340200341146a2001109e8d80800020032d001422064105460d02200341286a410f6a22072005410f6a280000360000200341286a41086a2208200541086a290000370300200320052900003703280240200328021022042003280208470d00200341086a200410a586808000200328021021040b200328020c200441146c6a22042003290328370001200420063a0000200441096a2008290300370000200441106a20072800003600002003200328021041016a3602102002417f6a22020d000b0b20002003290208370200200041086a200341086a41086a2802003602000c030b2000418080808078360200024020032802102206450d00200328020c220721044100210503402007200541146c6a21020240024002400240024020042d00000e0400010102040b200441086a21020c020b200241086a21020c010b200241046a21020b2002280200450d00200228020441002802c0a3c68000118080808000000b200541016a2105200441146a21042006417f6a22060d000b0b2003280208450d02200328020c41002802c0a3c68000118080808000000c020b4104200610b280808000000b10ae80808000000b200341c0006a2480808080000beb0301097f23808080800041306b22032480808080000240024002400240200128020422044105762205200220052002491b22050d00410121060c010b200541ffffff1f4b0d0120054105742207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b410021072003410036020c20032006360208200320053602040240024002402002450d0020012802002105034020044120490d022001200441606a22043602042001200541206a2208360200200341106a41086a2209200541086a290000370300200341106a41106a220a200541106a290000370300200341106a41186a220b200541186a29000037030020032005290000370310024020072003280204470d00200341046a200710a18680800020032802082106200328020c21070b200620074105746a22052003290310370000200541186a200b290300370000200541106a200a290300370000200541086a20092903003700002003200741016a220736020c200821052002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c010b20004180808080783602002003280204450d00200641002802c0a3c68000118080808000000b200341306a2480808080000f0b10ae80808000000b4101200710b280808000000bb80302067f017e23808080800041106b2203248080808000024002400240024002400240200128020422044104762205200220052002491b22060d00410821070c010b200641ffffff3f4b0d0120064104742205417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002207450d020b410021052003410036020c200320073602082003200636020402402002450d002001280200210603400240200441074b0d00200328020421080c050b2001200441786a22043602042001200641086a22073602002003280204210820044104490d042006290000210920012004417c6a22043602042001200741046a220636020020072800002107024020052008470d00200341046a200510a286808000200328020c21050b200328020820054104746a22052007360208200520093703002003200328020c41016a220536020c2002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c030b10ae80808000000b4108200510b280808000000b20004180808080783602002008450d00200328020841002802c0a3c68000118080808000000b200341106a2480808080000bd70301057f23808080800041e0036b22032480808080000240024002400240200128020041046a28020041e8016e2204200220042002491b22040d00410821050c010b200441cbfbb4044b0d01200441e8016c2206417f4c0d0141002d00fca3c680001a200641002802c8a3c68000118180808000002205450d020b20012001280204220741016a22063602042003410036020c200320053602082003200436020402400240200620012802084b0d0002402002450d00200341106a41047221060340200341106a200110f58c80800020032802102205450d02200341fc016a200641e40110848e8080001a0240200328020c22042003280204470d00200341046a2004109786808000200328020c21040b2003280208200441e8016c6a22042005360200200441046a200341fc016a41e40110848e8080001a2003200328020c41016a36020c2002417f6a22020d000b2001280204417f6a21070b2001200736020420002003290204370200200041086a200341046a41086a2802003602000c010b2000418080808078360200200341046a108f878080002003280204450d00200328020841002802c0a3c68000118080808000000b200341e0036a2480808080000f0b10ae80808000000b4108200610b280808000000b9f0602077f027e23808080800041206b22032480808080004104210402400240024002400240024002400240200128020041046a280200410c6e2205200220052002491b2205450d00200541aad5aad5004b0d022005410c6c2206417f4c0d0241002d00fca3c680001a200641002802c8a3c68000118180808000002204450d010b20012001280204220741016a22063602042003410036021c2003200436021820032005360214200620012802084b0d032002450d050340200341086a200110bc8a80800020032802080d042001280200220641046a22072802002204200328020c2205490d040240024020050d00410121080c010b2005417f4c0d03200541002802c8a3c68000118180808000002208450d04200841002005108a8e8080001a200728020021040b0240200420054f0d00200841002802c0a3c68000118080808000000c050b200820062802002209200510848e80800021082007200420056b3602002006200920056a3602002005ad210a2008ad210b0240200328021c22042003280214470d00200341146a2004109986808000200328021c21040b20032802182004410c6c6a2204200a422086200b84370204200420053602002003200328021c41016a36021c2002417f6a2202450d050c000b0b4104200610b280808000000b10ae80808000000b4101200510b280808000000b20004180808080783602000240200328021c2205450d00200328021821022005410171210641002101024020054101460d002005417e7121042002210541002101034002402005280200450d00200541046a28020041002802c0a3c68000118080808000000b02402005410c6a280200450d00200541106a28020041002802c0a3c68000118080808000000b200541186a21052004200141026a2201470d000b0b2006450d0020022001410c6c6a2205280200450d00200528020441002802c0a3c68000118080808000000b2003280214450d02200328021841002802c0a3c68000118080808000000c020b2001280204417f6a21070b2001200736020420002003290214370200200041086a200341146a41086a2802003602000b200341206a2480808080000bb104010e7f23808080800041106b220324808080800002400240024002400240024020012802042204410c6e2205200220052002491b22050d00410421060c010b200541aad5aad5004b0d012005410c6c2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b410021072003410036020c200320063602082003200536020402402002450d002001280200210503400240200441074b0d00200328020421080c050b2001200441786a22043602042001200541086a22063602002003280204210820044104490d04200541076a2d00002109200541066a2d0000210a200541056a2d0000210b200541036a2d0000210c20052d0004210d20052d0002210e20052d0001210f20052d0000211020012004417c6a22043602042001200641046a220536020020062800002106024020072008470d00200341046a2007109986808000200328020c21070b20032802082007410c6c6a2207200e3a00022007200f3a0001200720103a000020072006360208200741036a200c3a00002007200d3a0004200741056a200b3a0000200741066a200a3a0000200741076a20093a00002003200328020c41016a220736020c2002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c030b10ae80808000000b4104200710b280808000000b20004180808080783602002008450d00200328020841002802c0a3c68000118080808000000b200341106a2480808080000bf90401067f23808080800041c0006b220324808080800041042104024002400240200128020041046a28020041186e2205200220052002491b2205450d00200541d5aad52a4b0d01200541186c2206417f4c0d0141002d00fca3c680001a200641002802c8a3c68000118180808000002204450d020b20012001280204220741016a22063602042003410036020c2003200436020820032005360204024002400240200620012802084d0d0020004180808080783602000c010b024002402002450d00200341146a21040340200341106a200110fd8680800020032802102206418080808078460d02200341286a41106a2207200441106a280200360200200341286a41086a2208200441086a290200370300200320042902003703280240200328020c22052003280204470d00200341046a200510a686808000200328020c21050b2003280208200541186c6a22052003290328370204200520063602002005410c6a2008290300370200200541146a20072802003602002003200328020c41016a36020c2002417f6a22020d000b2001280204417f6a21070b2001200736020420002003290204370200200041086a200341046a41086a2802003602000c020b2000418080808078360200200328020c2204450d0020032802082105034002402005280200450d00200541046a28020041002802c0a3c68000118080808000000b02402005410c6a280200450d00200541106a28020041002802c0a3c68000118080808000000b200541186a21052004417f6a22040d000b0b2003280204450d00200328020841002802c0a3c68000118080808000000b200341c0006a2480808080000f0b10ae80808000000b4104200610b280808000000be10101037f0240024002402001280200220341046a28020022012002490d000240024020020d00410121040c010b2002417f4c0d02200241002802c8a3c68000118180808000002204450d03200441002002108a8e8080001a200341046a28020021010b024020012002490d00200420032802002205200210848e8080002104200341046a200120026b3602002003200520026a3602002000200236020020002002ad4220862004ad843702040f0b200441002802c0a3c68000118080808000000b20004180808080783602000f0b10ae80808000000b4101200210b280808000000bfb0101047f02400240200241ffffffff014b0d002001280200220341046a280200220420024103742201490d0002400240024002402002450d00200241ffffffff004b0d052001417f4c0d05200141002802c8a3c680001181808080000022050d014108200110b280808000000b410821050c010b200541002001108a8e8080002106200341046a28020022042001490d010b200520032802002206200110848e8080002105200341046a200420016b3602002003200620016a3602002000200236020020002002ad4220862005ad843702040f0b200641002802c0a3c68000118080808000000b20004180808080783602000f0b10ae80808000000bf90101047f23808080800041106b220324808080800002402001450d002001410c6c21040340024002402000280200418080808078470d00200341046a200041046a280200200041086a28020010b4828080000c010b200341046a2000280204200041086a28020010e6848080000b2003280208210502402002280200200228020822016b200328020c22064f0d0020022001200610b182808000200228020821010b200228020420016a2005200610848e8080001a2002200120066a36020802402003280204450d00200541002802c0a3c68000118080808000000b2000410c6a2100200441746a22040d000b0b200341106a2480808080000b8a0202057f017e23808080800041106b220324808080800002402001450d0020014103742104200228020821010340200341046a10b989808000200328020821050240200228020020016b200328020c22064f0d0020022001200610b182808000200228020821010b2002280204220720016a2005200610848e8080001a2002200120066a220636020802402003280204450d00200541002802c0a3c68000118080808000000b200029030021080240200228020020066b41074b0d0020022006410810b18280800020022802042107200228020821060b200041086a21002002200641086a2201360208200720066a2008370000200441786a22040d000b0b200341106a2480808080000bfd0101047f23808080800041106b220324808080800002402001450d00200141047421042000410c6a21010340200141786a280200210520032001417c6a28020022003602082003200341086a36020c2003410c6a200210c08a80800002402002280200200228020822066b20004f0d0020022006200010b182808000200228020821060b200228020420066a2005200010848e8080001a2002200620006a22003602080240200228020020006b41034b0d0020022000410410b182808000200228020821000b2002200041046a360208200228020420006a2001280200360000200141106a2101200441706a22040d000b0b200341106a2480808080000bab0201037f23808080800041206b22042480808080000240024002400240200141046a22050d00410121060c010b2005417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002206450d020b20044100360214200420063602102004200536020c200420013602182004200441186a36021c2004411c6a2004410c6a10c08a8080000240200428020c200428021422056b20014f0d002004410c6a2005200110b182808000200428021421050b200428021020056a2000200110848e8080001a200428020c21002002200320042802102206200520016a41002802e0a1c680001186808080000002402000450d00200641002802c0a3c68000118080808000000b200441206a2480808080000f0b10ae80808000000b4101200510b280808000000b810201037f23808080800041206b22032480808080000240024002400240200241046a22040d00410121050c010b2004417f4c0d0141002d00fca3c680001a200441002802c8a3c68000118180808000002205450d020b20034100360214200320053602102003200436020c200320023602182003200341186a36021c2003411c6a2003410c6a10c08a8080000240200328020c200328021422046b20024f0d002003410c6a2004200210b182808000200328021421040b200328021020046a2001200210848e8080001a200041086a200420026a3602002000200329020c370200200341206a2480808080000f0b10ae80808000000b4101200410b280808000000bf60101047f23808080800041206b2203248080808000024002402002417f4c0d0041002d00fca3c680001a20024103742204410472220541002802c8a3c68000118180808000002206450d0120034100360214200320063602102003200536020c200320023602182003200341186a36021c2003411c6a2003410c6a10c08a8080000240200328020c200328021422026b20044f0d002003410c6a2002200410b182808000200328021421020b200328021020026a2001200410848e8080001a200041086a200220046a3602002000200329020c370200200341206a2480808080000f0b10ae80808000000b4101200510b280808000000b860201047f23808080800041206b220324808080800002400240024002402002410274220441046a22050d00410121060c010b2005417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002206450d020b20034100360214200320063602102003200536020c200320023602182003200341186a36021c2003411c6a2003410c6a10c08a8080000240200328020c200328021422056b20044f0d002003410c6a2005200410b182808000200328021421050b200328021020056a2001200410848e8080001a200041086a200520046a3602002000200329020c370200200341206a2480808080000f0b10ae80808000000b4101200510b280808000000bc80401077f23808080800041c0006b2207248080808000200120042005200610e38d80800021060240024002400240024020032d00004102470d00200341086a28020021052002280200220841d4006a2802002109200328020421042008280250210a200128022c220841017621030240024020084101710d00200128020420012802282208200841284b22081b220b2003490d042001280200200120081b21084100210b0c010b200128020420012802282208200841284b1b220b2003490d04200b20034d0d05200741296a20012802002001200841284b1b220820036a2d000041f001713a00004101210b0b2007200b3a002820072003360224200720083602202007200a200741206a20042005200928021c1187808080000020022802002102200128020021082001280204210920012802282103200741206a41186a200741186a220a290000370300200741206a41106a200741106a220b290000370300200741206a41086a200741086a220c29000037030020072007290000370320200220082001200341284b220d1b20092003200d1b20042005200741206a10fd858080002001200610e08d808000200041003a0000200041196a200a290000370000200041116a200b290000370000200041096a200c290000370000200020072900003700010c010b200020022802002003200110ff858080002001200610e08d8080000b200741c0006a2480808080000f0b2003200b41ac95c68000109581808000000b2003200b41bc95c68000109581808000000b2003200b41cc95c6800010f980808000000beb0401097f23808080800041c0006b22062480808080002001280200220728020020032004200510e38d80800021050240024002400240024020022d00004102470d00200241086a280200210420012802042208280200220141d4006a2802002109200228020421032001280250210a2007280200220228022c220b410176210102400240200b4101710d0020022802042002280228220b200b41284b220b1b220c2001490d0420022802002002200b1b21024100210b0c010b20022802042002280228220b200b41284b1b220c2001490d04200c20014d0d05200641296a20022802002002200b41284b1b220220016a2d000041f001713a00004101210b0b2006200b3a002820062001360224200620023602202006200a200641206a20032004200928021c118780808000002008280200210b2007280200220228020021082002280204210920022802282101200641206a41186a200641186a220a290000370300200641206a41106a200641106a220c290000370300200641206a41086a200641086a220d29000037030020062006290000370320200b20082002200141284b220e1b20092001200e1b20032004200641206a10fc858080002007280200200510e08d808000200041003a0000200041196a200a290000370000200041116a200c290000370000200041096a200d290000370000200020062900003700010c010b20002001280204280200200220072802001080868080002007280200200510e08d8080000b200641c0006a2480808080000f0b2001200c41ac95c68000109581808000000b2001200c41bc95c68000109581808000000b2001200c41cc95c6800010f980808000000bc80401077f23808080800041c0006b2207248080808000200120042005200610e38d80800021060240024002400240024020032d00004102470d00200341086a28020021052002280200220841d4006a2802002109200328020421042008280250210a200128022c220841017621030240024020084101710d00200128020420012802282208200841284b22081b220b2003490d042001280200200120081b21084100210b0c010b200128020420012802282208200841284b1b220b2003490d04200b20034d0d05200741296a20012802002001200841284b1b220820036a2d000041f001713a00004101210b0b2007200b3a002820072003360224200720083602202007200a200741206a20042005200928021c1187808080000020022802002102200128020021082001280204210920012802282103200741206a41186a200741186a220a290000370300200741206a41106a200741106a220b290000370300200741206a41086a200741086a220c29000037030020072007290000370320200220082001200341284b220d1b20092003200d1b20042005200741206a10fc858080002001200610e08d808000200041003a0000200041196a200a290000370000200041116a200b290000370000200041096a200c290000370000200020072900003700010c010b20002002280200200320011080868080002001200610e08d8080000b200741c0006a2480808080000f0b2003200b41ac95c68000109581808000000b2003200b41bc95c68000109581808000000b2003200b41cc95c6800010f980808000000beb0401097f23808080800041c0006b22062480808080002001280200220728020020032004200510e38d80800021050240024002400240024020022d00004102470d00200241086a280200210420012802042208280200220141d4006a2802002109200228020421032001280250210a2007280200220228022c220b410176210102400240200b4101710d0020022802042002280228220b200b41284b220b1b220c2001490d0420022802002002200b1b21024100210b0c010b20022802042002280228220b200b41284b1b220c2001490d04200c20014d0d05200641296a20022802002002200b41284b1b220220016a2d000041f001713a00004101210b0b2006200b3a002820062001360224200620023602202006200a200641206a20032004200928021c118780808000002008280200210b2007280200220228020021082002280204210920022802282101200641206a41186a200641186a220a290000370300200641206a41106a200641106a220c290000370300200641206a41086a200641086a220d29000037030020062006290000370320200b20082002200141284b220e1b20092001200e1b20032004200641206a10fd858080002007280200200510e08d808000200041003a0000200041196a200a290000370000200041116a200c290000370000200041096a200d290000370000200020062900003700010c010b200020012802042802002002200728020010ff858080002007280200200510e08d8080000b200641c0006a2480808080000f0b2001200c41ac95c68000109581808000000b2001200c41bc95c68000109581808000000b2001200c41cc95c6800010f980808000000ba00301047f23808080800041f0086b2202248080808000200241e8016a2001280200220128020022032001280204220110c28c8080000240024002400240024020022802e80122044105470d002002410c6a410c6a200241e8016a410c6a290200370200200220022902ec013702102002410536020c0c010b200241e0086a41086a2205200241e8016a410c6a290200370300200220022902ec013703e00820024184076a41146a200241e8016a41146a41c80110848e8080001a20024184076a410c6a20052903003702002002200436028407200220022903e008370288072002410c6a20024184076a2003200110f48d808000200228020c22014105470d010b200241106a10de858080000c010b20024184076a41046a2002410c6a41046a41d80110848e8080001a2002200136028407200241e8016a20024184076a10b68a80800020022802e80122014108470d0120022802ec0110df858080000b41c0d3c2800041c2004184d4c2800010a181808000000b200041046a200241e8016a41046a41980510848e8080001a20002001360200200241f0086a2480808080000bc50101027f024002400240024020002802002201418080808078732202410220024104491b0e03030301000b0240024002402000280204220028020041fcffffff076a2202410320024105491b0e0404040102000b2000280204450d03200041086a28020041002802c0a3c68000118080808000000c030b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b200010de858080000c010b2001450d01200028020421000b200041002802c0a3c68000118080808000000b0b8b0101017f0240024002400240200028020041fcffffff076a2201410320014105491b0e0403030102000b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b2000280204450d01200041086a28020041002802c0a3c68000118080808000000c010b200010de858080000b200041002802c0a3c68000118080808000000b860101037f20012802042102200128020022012802042103200128020022012001280200220441016a36020002402004417f4a0d0000000b2000200336020820002001360204200041073602002000200229000037000c200041246a200241186a2900003700002000411c6a200241106a290000370000200041146a200241086a2900003700000ba00301047f23808080800041f0086b2202248080808000200241e8016a2001280200220128020022032001280204220110c28c8080000240024002400240024020022802e80122044105470d002002410c6a410c6a200241e8016a410c6a290200370200200220022902ec013702102002410536020c0c010b200241e0086a41086a2205200241e8016a410c6a290200370300200220022902ec013703e00820024184076a41146a200241e8016a41146a41c80110848e8080001a20024184076a410c6a20052903003702002002200436028407200220022903e008370288072002410c6a20024184076a2003200110f48d808000200228020c22014105470d010b200241106a10de858080000c010b20024184076a41046a2002410c6a41046a41d80110848e8080001a2002200136028407200241e8016a20024184076a10b78a80800020022802e80122014108470d0120022802ec0110df858080000b41c0d3c2800041c2004184d4c2800010a181808000000b200041046a200241e8016a41046a41980510848e8080001a20002001360200200241f0086a2480808080000be80201047f23808080800041106b220224808080800020012802082103200128020421040240024002400240024020012802000d00024020034100480d00200341f5ffffff074f0d0202402003410b6a417c7122050d00410421010c050b41002d00fca3c680001a200541002802c8a3c680001181808080000022010d044104200510b280808000000b41fc9bc68000412b2002410f6a41a89cc6800041b89cc68000108981808000000b20034120470d0120002004290000370001200041196a200441186a290000370000200041116a200441106a290000370000200041096a200441086a290000370000410121010c030b41e484c08000412b2002410f6a419085c08000419086c08000108981808000000b412020034190d1c2800010a281808000000b2001428180808010370200200141086a2004200310848e8080001a200041086a200336020020002001360204410021010b200020013a0000200241106a2480808080000b810401027f0240024002400240024020002d00002201417c6a41ff01712202410420024104491b0e0404010203000b0240200041dc006a2802004129490d00200041346a28020041002802c0a3c68000118080808000000b200028022c41002802c0a3c680001180808080000020014103460d0302400240024020010e020106000b2000280224220220022802002202417f6a36020020024101470d05200041246a21000c010b2000280204220220022802002202417f6a36020020024101470d04200041046a21000b200010e28a8080000c030b0240200041dc006a2802004129490d00200041346a28020041002802c0a3c68000118080808000000b02400240024020002d00040e020105000b200041286a2200280200220220022802002202417f6a36020020024101470d040c010b200041086a2200280200220220022802002202417f6a36020020024101470d030b200010e28a8080000f0b200041d4006a2802004129490d012000412c6a28020041002802c0a3c68000118080808000000f0b200028023041002802c0a3c680001180808080000020002d000422024103460d0002400240024020020e020103000b200041286a2200280200220220022802002202417f6a36020020024101470d020c010b200041086a2200280200220220022802002202417f6a36020020024101470d010b200010e28a8080000f0b0b6801017f024002400240024020002d00000e020103000b2000280224220120012802002201417f6a36020020014101470d02200041246a21000c010b2000280204220120012802002201417f6a36020020014101470d01200041046a21000b200010e28a8080000b0b7101017f024020002d000022014103460d0002400240024020010e020103000b2000280224220120012802002201417f6a36020020014101470d02200041246a21000c010b2000280204220120012802002201417f6a36020020014101470d01200041046a21000b200010e28a8080000b0b02000bdc0101017f0240024002400240024020002d00000e03000201040b20012d00000d03200041086a2802002202200141086a280200470d03200028020441086a200128020441086a200210888e808000450f0b20012d00004102460d010c020b20012d00004101470d01200041016a200141016a412010888e808000450f0b0240024020002d0001450d0020012d000141ff01710d010b200041286a2802002202200141286a280200470d01200028022441086a200128022441086a200210888e808000450f0b200041026a200141026a412010888e808000450f0b41000ba97407047f027e037f017e027f017e1b7f23808080800041d00f6b22052480808080002005200436020c200541e00d6a2002200310c28c80800002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020052802e00d22064105470d00200541106a410c6a200541e00d6a410c6a290200370200200520052902e40d3702140c010b200541a0036a2207200541e00d6a410c6a290200370300200520052902e40d37039803200541800c6a41146a200541e00d6a41146a41c80110848e8080001a200541800c6a410c6a2007290300370200200520063602800c20052005290398033702840c200541106a200541800c6a2002200310f48d808000200528021022034105470d010b200541e00d6a41086a22032005411c6a290200370300200541e00d6a41186a2202200141086a290000370300200541e00d6a41206a2204200141106a290000370300200541e00d6a41286a2206200141186a290000370300200520052902143703e00d200520012900003703f00d41002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b2005280228210220052802242106200528022021082005290218210920052802142107200541f0016a2005412c6a41a80110848e8080001a20052902d801210a20052802d401210b4104210c0240024002400240024002400240024020030e052d000301022d0b200541003602880e200541e00d6a200720072009a76a10f58d808000200541800c6a41186a200541e00d6a41186a290200370300200541800c6a41106a200541e00d6a41106a290200370300200541800c6a41086a200541e00d6a41086a290200370300200520052902e00d3703800c20052802800e210d20052902840e210e024020080d00024020024100480d00200241f5ffffff074f0d0a02402002410b6a417c7122010d004104210f0c2d0b41002d00fca3c680001a200141002802c8a3c6800011818080800000220f0d2c4104200110b280808000000b41fc9bc68000412b200541e00d6a41a89cc6800041b89cc68000108981808000000b20024120470d09200541106a41026a200641026a2d00003a0000200541e80d6a200641136a290000370300200541ed0d6a200641186a290000370000200520062f00003b01102005200629000b3703e00d200628000721022006280003210f410121100c2b0b200520023602f40d200520063602f00d200520083602ec0d200520093702e40d200520073602e00d200541e00d6a41186a200541f0016a41a80110848e8080002103200520013602d0074102210220052005410c6a3602d4072005200541e00d6a3602cc070240024020074102470d000c010b200541c00f6a41086a200541e00d6a41086a280200360200200520052902e00d3703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200410e98580800020052d00800c22024102460d0a200541fe0b6a20052d00830c3a0000200541f8086a41086a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c210620052802ec0d2108200221020b4102210420054184046a41026a200541fc0b6a41026a2d00003a0000200541e8036a41086a200541f8086a41086a290300370300200541e8036a41106a200541f8086a41106a290300370300200541e8036a41186a200541f8086a41186a280200360200200520052f01fc0b3b018404200520052903f8083703e80320084102470d020c030b20052902e401211120052802e001210f200520023602e80d200520063602e40d200520083602e00d200541ec0d6a200541f0016a41a80110848e80800021032005200a3702980f2005200b3602940f200520013602d0074102210220052005410c6a3602d4072005200541e00d6a3602cc070240024020084102470d000c010b200541c00f6a41086a200541e00d6a41086a280200360200200520052902e00d3703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200410e98580800020052d00800c22024102460d19200541fe0b6a20052d00830c3a0000200541f8086a41086a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2106200221020b4102210420054194086a41026a200541fc0b6a41026a2d00003a0000200541f8076a41086a200541f8086a41086a290300370300200541f8076a41106a200541f8086a41106a290300370300200541f8076a41186a200541f8086a41186a280200360200200520052f01fc0b3b019408200520052903f8083703f80720052802ec0d4102470d030c040b200520023602e403200520063602e003200520083602dc03200541003602880e200541e00d6a200720072009a76a10f58d808000200541106a41086a2203200541e00d6a41106a290200370300200541106a41106a2202200541e00d6a41186a290200370300200541106a41186a2204200541800e6a290200370300200520052902e80d37031020052802e40d211220052802e00d211320052802880e210d200541800c6a41186a200141186a290000370300200541800c6a41106a200141106a290000370300200541800c6a41086a200141086a290000370300200520012900003703800c200541e00d6a200541800c6a200541dc036a200528020c10e985808000024020052d00e00d22104102460d00200541d4036a41026a20052d00e30d3a0000200541b8036a41086a200541f40d6a290200370300200541b8036a41106a200541fc0d6a29020037030020054198036a41086a200329030037030020054198036a41106a200229030037030020054198036a41186a2004290300370300200520052f00e10d3b01d403200520052902ec0d3703b80320052005290310370398032009422088a7211420052802e40d210f20052802e80d21024106210c0c2a0b20052802e40d2101200041083a000020002001360204200d4129490d2a201341002802c0a3c68000118080808000000c2a0b200541c00f6a41086a200541ec0d6a220441086a280200360200200520042902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c22044102460d07200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2108200421040b41022107200541a4046a41026a200541fc0b6a41026a2d00003a000020054188046a41086a200541f8086a41086a29030037030020054188046a41106a200541f8086a41106a29030037030020054188046a41186a200541f8086a41186a280200360200200520052f01fc0b3b01a404200520052903f808370388040240024020052802f80d4102470d000c010b200541c00f6a41086a200341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c22034102460d08200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c210d200321070b41022103200541c4046a41026a200541fc0b6a41026a2d00003a0000200541a8046a41086a200541f8086a41086a290300370300200541a8046a41106a200541f8086a41106a290300370300200541a8046a41186a200541f8086a41186a280200360200200520052f01fc0b3b01c404200520052903f8083703a8040240024020052802840e4102470d000c010b200541c00f6a41086a200541840e6a220341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c22014102460d09200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2113200121030b200541e4046a41026a200541fc0b6a41026a2d00003a0000200541c8046a41086a200541f8086a41086a290300370300200541c8046a41106a200541f8086a41106a290300370300200541c8046a41186a200541f8086a41186a280200360200200520052f01fc0b3b01e404200520052903f8083703c804200541800c6a200541cc076a410410ea8580800020052d00800c22014103460d0920054184056a41026a20052d00830c3a0000200541e8046a41086a200541800c6a41106a290200370300200541e8046a41106a200541800c6a41186a290200370300200541e8046a41186a200541a00c6a220f280200360200200520052f00810c3b018405200520052902880c3703e80420052802840c210c200541800c6a200541cc076a410510ea8580800020052d00800c22144103460d0a200541a4056a41026a20052d00830c3a000020054188056a41086a200541800c6a41106a221229020037030020054188056a41106a200541800c6a41186a221029020037030020054188056a41186a200f280200360200200520052f00810c3b01a405200520052902880c3703880520052802840c210f200541800c6a200541cc076a410610ea8580800020052d00800c22154103460d0b200541c4056a41026a20052d00830c3a0000200541a8056a41086a2012290200370300200541a8056a41106a2010290200370300200541a8056a41186a200541a00c6a2212280200360200200520052f00810c3b01c405200520052902880c3703a80520052802840c2116200541800c6a200541cc076a410710ea8580800020052d00800c22174103460d0c200541e4056a41026a20052d00830c3a0000200541c8056a41086a200541800c6a41106a2210290200370300200541c8056a41106a200541800c6a41186a2218290200370300200541c8056a41186a2012280200360200200520052f00810c3b01e405200520052902880c3703c80520052802840c2119200541800c6a200541cc076a410810ea8580800020052d00800c221a4103460d0d20054184066a41026a20052d00830c3a0000200541e8056a41086a2010290200370300200541e8056a41106a2018290200370300200541e8056a41186a200541a00c6a2212280200360200200520052f00810c3b018406200520052902880c3703e80520052802840c2118200541800c6a200541cc076a410910ea8580800020052d00800c221b4103460d0e200541a4066a41026a20052d00830c3a000020054188066a41086a200541800c6a41106a221029020037030020054188066a41106a200541800c6a41186a221c29020037030020054188066a41186a2012280200360200200520052f00810c3b01a406200520052902880c3703880620052802840c211d200541800c6a200541cc076a410a10ea8580800020052d00800c221e4103460d0f200541c4066a41026a20052d00830c3a0000200541a8066a41086a2010290200370300200541a8066a41106a201c290200370300200541a8066a41186a200541a00c6a2212280200360200200520052f00810c3b01c406200520052902880c3703a80620052802840c211c200541800c6a200541cc076a410b10ea8580800020052d00800c221f4103460d10200541e4066a41026a20052d00830c3a0000200541c8066a41086a200541800c6a41106a2210290200370300200541c8066a41106a200541800c6a41186a2220290200370300200541c8066a41186a2012280200360200200520052f00810c3b01e406200520052902880c3703c80620052802840c2121200541800c6a200541cc076a410c10ea8580800020052d00800c22224103460d1120054184076a41026a20052d00830c3a0000200541e8066a41086a2010290200370300200541e8066a41106a2020290200370300200541e8066a41186a200541a00c6a2212280200360200200520052f00810c3b018407200520052902880c3703e80620052802840c2120200541800c6a200541cc076a410d10ea8580800020052d00800c22234103460d12200541a4076a41026a20052d00830c3a000020054188076a41086a200541800c6a41106a221029020037030020054188076a41106a200541800c6a41186a222429020037030020054188076a41186a2012280200360200200520052f00810c3b01a407200520052902880c3703880720052802840c2125200541800c6a200541cc076a410e10ea8580800020052d00800c22264103460d13200541c8076a41026a20052d00830c3a0000200541a8076a41086a2010290200370300200541a8076a41106a2024290200370300200541a8076a41186a200541a00c6a2212280200360200200520052f00810c3b01c807200520052902880c3703a80720052802840c2124200541800c6a200541cc076a410f10ea8580800002400240024020052d00800c22274103460d00200541f4076a41026a222820052d00830c3a0000200541d8076a41086a2229200541800c6a41106a290200370300200541d8076a41106a222a200541800c6a41186a290200370300200541d8076a41186a222b2012280200360200200520052f00810c3b01f407200520052902880c3703d80720052802840c212c41002d00fca3c680001a41c00441002802c8a3c68000118180808000002212450d17201220023a0000201220052f0184043b000120122006360204201220052903e803370208201220043a0024201220052f01a4043b002541032110201241036a20054184046a41026a2d00003a0000201241106a200541e8036a41086a290300370200201241186a200541e8036a41106a290300370200201241206a200541e8036a41186a280200360200201241276a200541a4046a41026a2d00003a000020122008360228201220073a00482012200d36024c201220052903880437022c201241346a20054188046a41086a2903003702002012413c6a20054188046a41106a290300370200201241c4006a20054188046a41186a280200360200201220052f01c4043b0049201241cb006a200541c4046a41026a2d00003a0000201220052903a804370250201241d8006a200541a8046a41086a290300370200201241e0006a200541a8046a41106a290300370200201241e8006a200541a8046a41186a280200360200201220033a006c20122013360270201220013a009001201220052f01e4043b006d201241ef006a200541e4046a41026a2d00003a00002012418c016a200541c8046a41186a28020036020020124184016a200541c8046a41106a290300370200201241fc006a200541c8046a41086a290300370200201220052903c804370274201220052f0184053b00910120124193016a20054184056a41026a2d00003a00002012200c36029401201241b0016a200541e8046a41186a280200360200201241a8016a200541e8046a41106a290300370200201241a0016a200541e8046a41086a290300370200201220052903e80437029801201220143a00b401201241b7016a200541a4056a41026a2d00003a0000201220052f01a4053b00b5012012200f3602b801201241d4016a20054188056a41186a280200360200201241cc016a20054188056a41106a290300370200201241c4016a20054188056a41086a29030037020020122005290388053702bc01201220153a00d801201241db016a200541c4056a41026a2d00003a0000201220052f01c4053b00d901201220163602dc01201241f8016a200541a8056a41186a280200360200201241f0016a200541a8056a41106a290300370200201241e8016a200541a8056a41086a290300370200201220052903a8053702e001201220173a00fc01201241ff016a200541e4056a41026a2d00003a0000201220052f01e4053b00fd0120122019360280022012419c026a200541c8056a41186a28020036020020124194026a200541c8056a41106a2903003702002012418c026a200541c8056a41086a290300370200201220052903c805370284022012201a3a00a002201241a3026a20054184066a41026a2d00003a0000201220052f0184063b00a102201220183602a402201241c0026a200541e8056a41186a280200360200201241b8026a200541e8056a41106a290300370200201241b0026a200541e8056a41086a290300370200201220052903e8053702a8022012201b3a00c402201241c7026a200541a4066a41026a2d00003a0000201220052f01a4063b00c5022012201d3602c802201241e4026a20054188066a41186a280200360200201241dc026a20054188066a41106a290300370200201241d4026a20054188066a41086a29030037020020122005290388063702cc022012201e3a00e802201241eb026a200541c4066a41026a2d00003a0000201220052f01c4063b00e9022012201c3602ec0220124188036a200541a8066a41186a28020036020020124180036a200541a8066a41106a290300370200201241f8026a200541a8066a41086a290300370200201220052903a8063702f0022012201f3a008c032012418f036a200541e4066a41026a2d00003a0000201220052f01e4063b008d032012202136029003201241ac036a200541c8066a41186a280200360200201241a4036a200541c8066a41106a2903003702002012419c036a200541c8066a41086a290300370200201220052903c80637029403201220223a00b003201241b3036a20054184076a41026a2d00003a0000201220052f0184073b00b103201220203602b403201241d0036a200541e8066a41186a280200360200201241c8036a200541e8066a41106a290300370200201241c0036a200541e8066a41086a290300370200201220052903e8063702b803201220233a00d403201241d7036a200541a4076a41026a2d00003a0000201220052f01a4073b00d503201220253602d803201241f4036a20054188076a41186a280200360200201241ec036a20054188076a41106a290300370200201241e4036a20054188076a41086a29030037020020122005290388073702dc03201220263a00f803201241fb036a200541c8076a41026a2d00003a0000201220052f01c8073b00f903201220243602fc0320124198046a200541a8076a41186a28020036020020124190046a200541a8076a41106a29030037020020124188046a200541a8076a41086a290300370200201220052903a80737028004201220273a009c042012419f046a20282d00003a0000201220052f01f4073b009d042012202c3602a004201241bc046a202b280200360200201241b4046a202a290300370200201241ac046a2029290300370200201220052903d8073702a404200b4102470d010c020b20052802840c2101200041083a0000200020013602040c2a0b2005200a3702fc082005200b3602f808200541800c6a200541f8086a10e285808000200541da076a20052d00830c3a0000200541186a200541940c6a290200370300200541206a2005419c0c6a290200370300200520052f00810c3b01d8072005200529028c0c37031020052d00800c211020052802840c210f20052802880c210220052802a40c211420052802a80c21130b200541d4036a41026a200541d8076a41026a2d00003a0000200541b8036a41086a200541106a41086a290300370300200541b8036a41106a200541106a41106a290300370300200520052f01d8073b01d403200520052903103703b8034107210c0c270b200541c00f6a41086a200341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c22034102460d15200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2108200321040b41022103200541b4086a41026a200541fc0b6a41026a2d00003a000020054198086a41086a200541f8086a41086a29030037030020054198086a41106a200541f8086a41106a29030037030020054198086a41186a200541f8086a41186a280200360200200520052f01fc0b3b01b408200520052903f808370398080240024020052802f80d4102470d000c010b200541c00f6a41086a200541e00d6a41186a220341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c22034102460d16200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2110200321030b4102210d200541d4086a41026a200541fc0b6a41026a2d00003a0000200541b8086a41086a200541f8086a41086a290300370300200541b8086a41106a200541f8086a41106a290300370300200541b8086a41186a200541f8086a41186a280200360200200520052f01fc0b3b01d408200520052903f8083703b8080240024020052802840e4102470d000c010b200541c00f6a41086a200541840e6a220d41086a2802003602002005200d2902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c220d4102460d17200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c210b200d210d0b41022114200541f4086a41026a200541fc0b6a41026a2d00003a0000200541d8086a41086a200541f8086a41086a290300370300200541d8086a41106a200541f8086a41106a290300370300200541d8086a41186a200541f8086a41186a280200360200200520052f01fc0b3b01f408200520052903f8083703d8080240024020052802900e4102470d000c010b200541c00f6a41086a200541900e6a221341086a280200360200200520132902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c22134102460d18200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2101201321140b200541b4096a41026a200541fc0b6a41026a2d00003a000020054198096a41086a200541f8086a41086a29030037030020054198096a41106a200541f8086a41106a29030037030020054198096a41186a200541f8086a41186a280200360200200520052f01fc0b3b01b409200520052903f80837039809200541800c6a200541cc076a410510eb8580800020052d00800c22154103460d18200541d4096a41026a20052d00830c3a0000200541b8096a41086a200541800c6a41106a290200370300200541b8096a41106a200541800c6a41186a290200370300200541b8096a41186a200541a00c6a2213280200360200200520052f00810c3b01d409200520052902880c3703b80920052802840c2116200541800c6a200541cc076a410610eb8580800020052d00800c22174103460d19200541f4096a41026a20052d00830c3a0000200541d8096a41086a200541800c6a41106a2212290200370300200541d8096a41106a200541800c6a41186a220c290200370300200541d8096a41186a2013280200360200200520052f00810c3b01f409200520052902880c3703d80920052802840c2118200541800c6a200541cc076a410710eb8580800020052d00800c22194103460d1a200541940a6a41026a20052d00830c3a0000200541f8096a41086a2012290200370300200541f8096a41106a200c290200370300200541f8096a41186a200541a00c6a2213280200360200200520052f00810c3b01940a200520052902880c3703f80920052802840c211a200541800c6a200541cc076a410810eb8580800020052d00800c221b4103460d1b200541b40a6a41026a20052d00830c3a0000200541980a6a41086a200541800c6a41106a2212290200370300200541980a6a41106a200541800c6a41186a220c290200370300200541980a6a41186a2013280200360200200520052f00810c3b01b40a200520052902880c3703980a20052802840c211c200541800c6a200541cc076a410910eb8580800020052d00800c221d4103460d1c200541d40a6a41026a20052d00830c3a0000200541b80a6a41086a2012290200370300200541b80a6a41106a200c290200370300200541b80a6a41186a200541a00c6a2213280200360200200520052f00810c3b01d40a200520052902880c3703b80a20052802840c211e200541800c6a200541cc076a410a10eb8580800020052d00800c221f4103460d1d200541f40a6a41026a20052d00830c3a0000200541d80a6a41086a200541800c6a41106a2212290200370300200541d80a6a41106a200541800c6a41186a220c290200370300200541d80a6a41186a2013280200360200200520052f00810c3b01f40a200520052902880c3703d80a20052802840c2120200541800c6a200541cc076a410b10eb8580800020052d00800c22214103460d1e200541940b6a41026a20052d00830c3a0000200541f80a6a41086a2012290200370300200541f80a6a41106a200c290200370300200541f80a6a41186a200541a00c6a2213280200360200200520052f00810c3b01940b200520052902880c3703f80a20052802840c2122200541800c6a200541cc076a410c10eb8580800020052d00800c22234103460d1f200541b40b6a41026a20052d00830c3a0000200541980b6a41086a200541800c6a41106a2212290200370300200541980b6a41106a200541800c6a41186a220c290200370300200541980b6a41186a2013280200360200200520052f00810c3b01b40b200520052902880c3703980b20052802840c2124200541800c6a200541cc076a410d10eb8580800020052d00800c22254103460d20200541d40b6a41026a20052d00830c3a0000200541b80b6a41086a2012290200370300200541b80b6a41106a200c290200370300200541b80b6a41186a200541a00c6a2213280200360200200520052f00810c3b01d40b200520052902880c3703b80b20052802840c2126200541800c6a200541cc076a410e10eb8580800020052d00800c22274103460d21200541f80b6a41026a20052d00830c3a0000200541d80b6a41086a200541800c6a41106a2212290200370300200541d80b6a41106a200541800c6a41186a220c290200370300200541d80b6a41186a2013280200360200200520052f00810c3b01f80b200520052902880c3703d80b20052802840c2128200541800c6a200541cc076a410f10eb8580800002400240024020052d00800c22294103460d00200541f4076a41026a20052d00830c3a0000200541d8076a41086a2012290200370300200541d8076a41106a200c290200370300200541d8076a41186a200541800c6a41206a280200360200200520052f00810c3b01f407200520052902880c3703d80720052802840c212a41002d00fca3c680001a41c00441002802c8a3c68000118180808000002213450d252009422088a72112201320023a0000201320052f0194083b000120132006360204201320052903f807370208201320043a0024201320052f01b4083b00254103210c201341036a20054194086a41026a2d00003a0000201341106a200541f8076a41086a290300370200201341186a200541f8076a41106a290300370200201341206a200541f8076a41186a280200360200201341276a200541b4086a41026a2d00003a000020132008360228201320033a00482013201036024c201320052903980837022c201341346a20054198086a41086a2903003702002013413c6a20054198086a41106a290300370200201341c4006a20054198086a41186a280200360200201320052f01d4083b0049201341cb006a200541d4086a41026a2d00003a0000201320052903b808370250201341d8006a200541b8086a41086a290300370200201341e0006a200541b8086a41106a290300370200201341e8006a200541b8086a41186a2802003602002013200d3a006c2013200b360270201320143a009001201320052f01f4083b006d201341ef006a200541f4086a41026a2d00003a00002013418c016a200541d8086a41186a28020036020020134184016a200541d8086a41106a290300370200201341fc006a200541d8086a41086a290300370200201320052903d808370274201320052f01b4093b00910120134193016a200541b4096a41026a2d00003a00002013200136029401201341b0016a20054198096a41186a280200360200201341a8016a20054198096a41106a290300370200201341a0016a20054198096a41086a290300370200201320052903980937029801201320153a00b401201341b7016a200541d4096a41026a2d00003a0000201320052f01d4093b00b501201320163602b801201341d4016a200541b8096a41186a280200360200201341cc016a200541b8096a41106a290300370200201341c4016a200541b8096a41086a290300370200201320052903b8093702bc01201320173a00d801201341db016a200541f4096a41026a2d00003a0000201320052f01f4093b00d901201320183602dc01201341f8016a200541d8096a41186a280200360200201341f0016a200541d8096a41106a290300370200201341e8016a200541d8096a41086a290300370200201320052903d8093702e001201320193a00fc01201341ff016a200541940a6a41026a2d00003a0000201320052f01940a3b00fd012013201a360280022013419c026a200541f8096a41186a28020036020020134194026a200541f8096a41106a2903003702002013418c026a200541f8096a41086a290300370200201320052903f809370284022013201b3a00a002201341a3026a200541b40a6a41026a2d00003a0000201320052f01b40a3b00a1022013201c3602a402201341c0026a200541980a6a41186a280200360200201341b8026a200541980a6a41106a290300370200201341b0026a200541980a6a41086a290300370200201320052903980a3702a8022013201d3a00c402201341c7026a200541d40a6a41026a2d00003a0000201320052f01d40a3b00c5022013201e3602c802201341e4026a200541b80a6a41186a280200360200201341dc026a200541b80a6a41106a290300370200201341d4026a200541b80a6a41086a290300370200201320052903b80a3702cc022013201f3a00e802201341eb026a200541f40a6a41026a2d00003a0000201320052f01f40a3b00e902201320203602ec0220134188036a200541d80a6a41186a28020036020020134180036a200541d80a6a41106a290300370200201341f8026a200541d80a6a41086a290300370200201320052903d80a3702f002201320213a008c032013418f036a200541940b6a41026a2d00003a0000201320052f01940b3b008d032013202236029003201341ac036a200541f80a6a41186a280200360200201341a4036a200541f80a6a41106a2903003702002013419c036a200541f80a6a41086a290300370200201320052903f80a37029403201320233a00b003201341b3036a200541b40b6a41026a2d00003a0000201320052f01b40b3b00b103201320243602b403201341d0036a200541980b6a41186a280200360200201341c8036a200541980b6a41106a290300370200201341c0036a200541980b6a41086a290300370200201320052903980b3702b803201320253a00d403201341d7036a200541d40b6a41026a2d00003a0000201320052f01d40b3b00d503201320263602d803201341f4036a200541b80b6a41186a280200360200201341ec036a200541b80b6a41106a290300370200201341e4036a200541b80b6a41086a290300370200201320052903b80b3702dc03201320273a00f803201341fb036a200541f80b6a41026a2d00003a0000201320052f01f80b3b00f903201320283602fc0320134198046a200541d80b6a41186a28020036020020134190046a200541d80b6a41106a29030037020020134188046a200541d80b6a41086a290300370200201320052903d80b37028004201320293a009c042013419f046a200541f4076a41026a2d00003a0000201320052f01f4073b009d042013202a3602a004201341bc046a200541d8076a41186a280200360200201341b4046a200541d8076a41106a290300370200201341ac046a200541d8076a41086a290300370200201320052903d8073702a404200541003602a80c200541800c6a200720072009a76a10f58d808000200541106a41186a2201200541800c6a41186a2202290200370300200541106a41106a2203200541800c6a41106a290200370300200541106a41086a2204200541800c6a41086a290200370300200520052902800c37031020052802a00c210d20052902a40c210e200f4102470d010c020b20052802840c2101200041083a0000200020013602040c280b200520113702dc072005200f3602d807200541800c6a200541d8076a10e285808000200541c00f6a41026a20052d00830c3a0000200541fc0b6a41026a200541870c6a2d00003a0000200541f8086a41086a2002290200370300200541f8086a41106a200541a00c6a290200370300200520052f00810c3b01c00f200520052f00850c3b01fc0b200520052902900c3703f80820052d00800c210c20052d00840c211020052802880c210f200528028c0c210220052802a80c21140b20054198036a41186a200129030037030020054198036a41106a200329030037030020054198036a41086a2004290300370300200541d8036a41026a200541c00f6a41026a2d00003a0000200541d4036a41026a200541fc0b6a41026a2d00003a0000200541b8036a41106a200541f8086a41106a290300370300200541b8036a41086a200541f8086a41086a2903003703002005200529031037039803200520052f01c00f3b01d803200520052f01fc0b3b01d403200520052903f8083703b8030c250b200120052903e00d370200200141286a2006290300370200200141206a2004290300370200200141186a2002290300370200200141106a200541e00d6a41106a290300370200200141086a2003290300370200200041083a0000200020013602040c250b41e484c08000412b200541e00d6a419085c08000419086c08000108981808000000b412020024190d1c2800010a281808000000b20052802840c2101200041083a0000200020013602040c220b20052802840c2101200041083a0000200020013602040c210b20052802840c2101200041083a0000200020013602040c200b20052802840c2101200041083a0000200020013602040c1f0b20052802840c2101200041083a0000200020013602040c1e0b20052802840c2101200041083a0000200020013602040c1d0b20052802840c2101200041083a0000200020013602040c1c0b20052802840c2101200041083a0000200020013602040c1b0b20052802840c2101200041083a0000200020013602040c1a0b20052802840c2101200041083a0000200020013602040c190b20052802840c2101200041083a0000200020013602040c180b20052802840c2101200041083a0000200020013602040c170b20052802840c2101200041083a0000200020013602040c160b20052802840c2101200041083a0000200020013602040c150b20052802840c2101200041083a0000200020013602040c140b410441c00410b280808000000b20052802840c2101200041083a0000200020013602040c120b20052802840c2101200041083a0000200020013602040c110b20052802840c2101200041083a0000200020013602040c100b20052802840c2101200041083a0000200020013602040c0f0b20052802840c2101200041083a0000200020013602040c0e0b20052802840c2101200041083a0000200020013602040c0d0b20052802840c2101200041083a0000200020013602040c0c0b20052802840c2101200041083a0000200020013602040c0b0b20052802840c2101200041083a0000200020013602040c0a0b20052802840c2101200041083a0000200020013602040c090b20052802840c2101200041083a0000200020013602040c080b20052802840c2101200041083a0000200020013602040c070b20052802840c2101200041083a0000200020013602040c060b20052802840c2101200041083a0000200020013602040c050b20052802840c2101200041083a0000200020013602040c040b410441c00410b280808000000b200f428180808010370200200f41086a2006200210848e8080001a410021100b2009422088a7211220054198036a41186a200541800c6a41186a29030037030020054198036a41106a200541800c6a41106a29030037030020054198036a41086a200541800c6a41086a290300370300200541d4036a41026a200541106a41026a2d00003a0000200541b8036a41086a200541e00d6a41086a290300370300200541b8036a41106a200541e00d6a41106a290300370300200520052903800c37039803200520052f01103b01d403200520052903e00d3703b8034105210c0b200020052f01d8033b0001200020052f01d4033b0005200020052903b8033702102000200529039803370234200041036a200541d8036a41026a2d00003a0000200041076a200541d4036a41026a2d00003a0000200041186a200541b8036a41086a290300370200200041206a200541b8036a41106a2903003702002000413c6a20054198036a41086a290300370200200041c4006a20054198036a41106a290300370200200041cc006a20054198036a41186a2903003702002000200e3702582000200d3602542000200f3602082000200236020c200020143602282000201336022c20002012360230200020103a00042000200c3a00000b200541d00f6a2480808080000ba30801057f23808080800041e0016b2204248080808000200241086a28020021052002280204210602400240024002400240024020022802000d00024002400240024020050d00410121020c010b20054120460d012005417f4c0d0441002d00fca3c680001a200541002802c8a3c68000118180808000002202450d050b20022006200510848e808000210341002d00fca3c680001a413041002802c8a3c680001181808080000022020d014104413010b280808000000b2004411c6a41026a200641026a2d00003a0000200441086a2006410f6a290000370300200441106a200641176a290000370300200441186a2006411f6a2d00003a0000200420062f00003b011c2004200629000737030020062800032102410121060c050b2002200536020c2002200336020820022005360204200241888080807836020020022001290000370010200241186a200141086a290000370000200241206a200141106a290000370000200241286a200141186a2900003700000c030b20044180016a41186a200141186a29000037030020044180016a41106a200141106a29000037030020044180016a41086a200141086a2900003703002004200129000037038001200441206a20044180016a20062005200310e885808000024020042d002022054108460d00200441dc016a41026a20042d00233a0000200420042f00213b01dc012004280224210720044180016a200441286a41d80010848e8080001a02400240200341186a2802002201450d0020032001417f6a36021841002106200341146a22012001280200220141016a22024100200328020c220820022008491b6b360200200341106a28020020014102746a2802002202200328020822014f0d01200328020420024107746a220141046a200120012d00004108461b10e385808000200120053a0004200141083a000020012007360208200120042f01dc013b0005200141076a200441de016a2d00003a00002001410c6a20044180016a41d80010848e8080001a0c060b0240200328020822012003280200470d0020032001109e86808000200328020821010b200328020420014107746a220120053a0004200141083a0000200120042f01dc013b000520012007360208200141076a200441de016a2d00003a00002001410c6a20044180016a41d80010848e8080001a20032003280208220241016a360208410021060c050b2002200141d4d7c2800010f980808000000b200428022421020c020b10ae80808000000b4101200510b280808000000b200041023a0000200020023602040c010b200020042f011c3b00012000200429030037020820002002360204200020063a0000200041036a2004411e6a2d00003a0000200041106a200441086a290300370200200041186a200441106a290300370200200041206a200441186a2802003602000b200441e0016a2480808080000bd40201017f23808080800041d0006b220324808080800002402002410f4b0d000240024020012802002002410c6c6a22022802004102470d00200041023a00000c010b200341086a200241086a28020036020020032002290200370300200341306a41086a2001280204220241086a290000370300200341306a41106a200241106a290000370300200341306a41186a200241186a290000370300200320022900003703302003410c6a200341306a2003200128020828020010e985808000024020032d000c4102460d002000200329020c370200200041206a2003410c6a41206a280200360200200041186a2003410c6a41186a290200370200200041106a2003410c6a41106a290200370200200041086a2003410c6a41086a2902003702000c010b20002003280210360204200041033a00000b200341d0006a2480808080000f0b2002411041b0d1c2800010f980808000000bd40201017f23808080800041d0006b220324808080800002402002410f4b0d000240024020012802002002410c6c6a22022802004102470d00200041023a00000c010b200341086a200241086a28020036020020032002290200370300200341306a41086a2001280204220241086a290000370300200341306a41106a200241106a290000370300200341306a41186a200241186a290000370300200320022900003703302003410c6a200341306a2003200128020828020010e985808000024020032d000c4102460d002000200329020c370200200041206a2003410c6a41206a280200360200200041186a2003410c6a41186a290200370200200041106a2003410c6a41106a290200370200200041086a2003410c6a41086a2902003702000c010b20002003280210360204200041033a00000b200341d0006a2480808080000f0b2002411041a0d1c2800010f980808000000ba97407047f027e037f017e027f017e1b7f23808080800041d00f6b22052480808080002005200436020c200541e00d6a2002200310c28c80800002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020052802e00d22064105470d00200541106a410c6a200541e00d6a410c6a290200370200200520052902e40d3702140c010b200541a0036a2207200541e00d6a410c6a290200370300200520052902e40d37039803200541800c6a41146a200541e00d6a41146a41c80110848e8080001a200541800c6a410c6a2007290300370200200520063602800c20052005290398033702840c200541106a200541800c6a2002200310f48d808000200528021022034105470d010b200541e00d6a41086a22032005411c6a290200370300200541e00d6a41186a2202200141086a290000370300200541e00d6a41206a2204200141106a290000370300200541e00d6a41286a2206200141186a290000370300200520052902143703e00d200520012900003703f00d41002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b2005280228210220052802242106200528022021082005290218210920052802142107200541f0016a2005412c6a41a80110848e8080001a20052902d801210a20052802d401210b4104210c0240024002400240024002400240024020030e052d000301022d0b200541003602880e200541e00d6a200720072009a76a10f58d808000200541800c6a41186a200541e00d6a41186a290200370300200541800c6a41106a200541e00d6a41106a290200370300200541800c6a41086a200541e00d6a41086a290200370300200520052902e00d3703800c20052802800e210d20052902840e210e024020080d00024020024100480d00200241f5ffffff074f0d0a02402002410b6a417c7122010d004104210f0c2d0b41002d00fca3c680001a200141002802c8a3c6800011818080800000220f0d2c4104200110b280808000000b41fc9bc68000412b200541e00d6a41a89cc6800041b89cc68000108981808000000b20024120470d09200541106a41026a200641026a2d00003a0000200541e80d6a200641136a290000370300200541ed0d6a200641186a290000370000200520062f00003b01102005200629000b3703e00d200628000721022006280003210f410121100c2b0b200520023602f40d200520063602f00d200520083602ec0d200520093702e40d200520073602e00d200541e00d6a41186a200541f0016a41a80110848e8080002103200520013602d0074102210220052005410c6a3602d4072005200541e00d6a3602cc070240024020074102470d000c010b200541c00f6a41086a200541e00d6a41086a280200360200200520052902e00d3703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200410ed8580800020052d00800c22024102460d0a200541fe0b6a20052d00830c3a0000200541f8086a41086a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c210620052802ec0d2108200221020b4102210420054184046a41026a200541fc0b6a41026a2d00003a0000200541e8036a41086a200541f8086a41086a290300370300200541e8036a41106a200541f8086a41106a290300370300200541e8036a41186a200541f8086a41186a280200360200200520052f01fc0b3b018404200520052903f8083703e80320084102470d020c030b20052902e401211120052802e001210f200520023602e80d200520063602e40d200520083602e00d200541ec0d6a200541f0016a41a80110848e80800021032005200a3702980f2005200b3602940f200520013602d0074102210220052005410c6a3602d4072005200541e00d6a3602cc070240024020084102470d000c010b200541c00f6a41086a200541e00d6a41086a280200360200200520052902e00d3703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200410ed8580800020052d00800c22024102460d19200541fe0b6a20052d00830c3a0000200541f8086a41086a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2106200221020b4102210420054194086a41026a200541fc0b6a41026a2d00003a0000200541f8076a41086a200541f8086a41086a290300370300200541f8076a41106a200541f8086a41106a290300370300200541f8076a41186a200541f8086a41186a280200360200200520052f01fc0b3b019408200520052903f8083703f80720052802ec0d4102470d030c040b200520023602e403200520063602e003200520083602dc03200541003602880e200541e00d6a200720072009a76a10f58d808000200541106a41086a2203200541e00d6a41106a290200370300200541106a41106a2202200541e00d6a41186a290200370300200541106a41186a2204200541800e6a290200370300200520052902e80d37031020052802e40d211220052802e00d211320052802880e210d200541800c6a41186a200141186a290000370300200541800c6a41106a200141106a290000370300200541800c6a41086a200141086a290000370300200520012900003703800c200541e00d6a200541800c6a200541dc036a200528020c10ed85808000024020052d00e00d22104102460d00200541d4036a41026a20052d00e30d3a0000200541b8036a41086a200541f40d6a290200370300200541b8036a41106a200541fc0d6a29020037030020054198036a41086a200329030037030020054198036a41106a200229030037030020054198036a41186a2004290300370300200520052f00e10d3b01d403200520052902ec0d3703b80320052005290310370398032009422088a7211420052802e40d210f20052802e80d21024106210c0c2a0b20052802e40d2101200041083a000020002001360204200d4129490d2a201341002802c0a3c68000118080808000000c2a0b200541c00f6a41086a200541ec0d6a220441086a280200360200200520042902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c22044102460d07200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2108200421040b41022107200541a4046a41026a200541fc0b6a41026a2d00003a000020054188046a41086a200541f8086a41086a29030037030020054188046a41106a200541f8086a41106a29030037030020054188046a41186a200541f8086a41186a280200360200200520052f01fc0b3b01a404200520052903f808370388040240024020052802f80d4102470d000c010b200541c00f6a41086a200341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c22034102460d08200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c210d200321070b41022103200541c4046a41026a200541fc0b6a41026a2d00003a0000200541a8046a41086a200541f8086a41086a290300370300200541a8046a41106a200541f8086a41106a290300370300200541a8046a41186a200541f8086a41186a280200360200200520052f01fc0b3b01c404200520052903f8083703a8040240024020052802840e4102470d000c010b200541c00f6a41086a200541840e6a220341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c22014102460d09200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2113200121030b200541e4046a41026a200541fc0b6a41026a2d00003a0000200541c8046a41086a200541f8086a41086a290300370300200541c8046a41106a200541f8086a41106a290300370300200541c8046a41186a200541f8086a41186a280200360200200520052f01fc0b3b01e404200520052903f8083703c804200541800c6a200541cc076a410410ee8580800020052d00800c22014103460d0920054184056a41026a20052d00830c3a0000200541e8046a41086a200541800c6a41106a290200370300200541e8046a41106a200541800c6a41186a290200370300200541e8046a41186a200541a00c6a220f280200360200200520052f00810c3b018405200520052902880c3703e80420052802840c210c200541800c6a200541cc076a410510ee8580800020052d00800c22144103460d0a200541a4056a41026a20052d00830c3a000020054188056a41086a200541800c6a41106a221229020037030020054188056a41106a200541800c6a41186a221029020037030020054188056a41186a200f280200360200200520052f00810c3b01a405200520052902880c3703880520052802840c210f200541800c6a200541cc076a410610ee8580800020052d00800c22154103460d0b200541c4056a41026a20052d00830c3a0000200541a8056a41086a2012290200370300200541a8056a41106a2010290200370300200541a8056a41186a200541a00c6a2212280200360200200520052f00810c3b01c405200520052902880c3703a80520052802840c2116200541800c6a200541cc076a410710ee8580800020052d00800c22174103460d0c200541e4056a41026a20052d00830c3a0000200541c8056a41086a200541800c6a41106a2210290200370300200541c8056a41106a200541800c6a41186a2218290200370300200541c8056a41186a2012280200360200200520052f00810c3b01e405200520052902880c3703c80520052802840c2119200541800c6a200541cc076a410810ee8580800020052d00800c221a4103460d0d20054184066a41026a20052d00830c3a0000200541e8056a41086a2010290200370300200541e8056a41106a2018290200370300200541e8056a41186a200541a00c6a2212280200360200200520052f00810c3b018406200520052902880c3703e80520052802840c2118200541800c6a200541cc076a410910ee8580800020052d00800c221b4103460d0e200541a4066a41026a20052d00830c3a000020054188066a41086a200541800c6a41106a221029020037030020054188066a41106a200541800c6a41186a221c29020037030020054188066a41186a2012280200360200200520052f00810c3b01a406200520052902880c3703880620052802840c211d200541800c6a200541cc076a410a10ee8580800020052d00800c221e4103460d0f200541c4066a41026a20052d00830c3a0000200541a8066a41086a2010290200370300200541a8066a41106a201c290200370300200541a8066a41186a200541a00c6a2212280200360200200520052f00810c3b01c406200520052902880c3703a80620052802840c211c200541800c6a200541cc076a410b10ee8580800020052d00800c221f4103460d10200541e4066a41026a20052d00830c3a0000200541c8066a41086a200541800c6a41106a2210290200370300200541c8066a41106a200541800c6a41186a2220290200370300200541c8066a41186a2012280200360200200520052f00810c3b01e406200520052902880c3703c80620052802840c2121200541800c6a200541cc076a410c10ee8580800020052d00800c22224103460d1120054184076a41026a20052d00830c3a0000200541e8066a41086a2010290200370300200541e8066a41106a2020290200370300200541e8066a41186a200541a00c6a2212280200360200200520052f00810c3b018407200520052902880c3703e80620052802840c2120200541800c6a200541cc076a410d10ee8580800020052d00800c22234103460d12200541a4076a41026a20052d00830c3a000020054188076a41086a200541800c6a41106a221029020037030020054188076a41106a200541800c6a41186a222429020037030020054188076a41186a2012280200360200200520052f00810c3b01a407200520052902880c3703880720052802840c2125200541800c6a200541cc076a410e10ee8580800020052d00800c22264103460d13200541c8076a41026a20052d00830c3a0000200541a8076a41086a2010290200370300200541a8076a41106a2024290200370300200541a8076a41186a200541a00c6a2212280200360200200520052f00810c3b01c807200520052902880c3703a80720052802840c2124200541800c6a200541cc076a410f10ee8580800002400240024020052d00800c22274103460d00200541f4076a41026a222820052d00830c3a0000200541d8076a41086a2229200541800c6a41106a290200370300200541d8076a41106a222a200541800c6a41186a290200370300200541d8076a41186a222b2012280200360200200520052f00810c3b01f407200520052902880c3703d80720052802840c212c41002d00fca3c680001a41c00441002802c8a3c68000118180808000002212450d17201220023a0000201220052f0184043b000120122006360204201220052903e803370208201220043a0024201220052f01a4043b002541032110201241036a20054184046a41026a2d00003a0000201241106a200541e8036a41086a290300370200201241186a200541e8036a41106a290300370200201241206a200541e8036a41186a280200360200201241276a200541a4046a41026a2d00003a000020122008360228201220073a00482012200d36024c201220052903880437022c201241346a20054188046a41086a2903003702002012413c6a20054188046a41106a290300370200201241c4006a20054188046a41186a280200360200201220052f01c4043b0049201241cb006a200541c4046a41026a2d00003a0000201220052903a804370250201241d8006a200541a8046a41086a290300370200201241e0006a200541a8046a41106a290300370200201241e8006a200541a8046a41186a280200360200201220033a006c20122013360270201220013a009001201220052f01e4043b006d201241ef006a200541e4046a41026a2d00003a00002012418c016a200541c8046a41186a28020036020020124184016a200541c8046a41106a290300370200201241fc006a200541c8046a41086a290300370200201220052903c804370274201220052f0184053b00910120124193016a20054184056a41026a2d00003a00002012200c36029401201241b0016a200541e8046a41186a280200360200201241a8016a200541e8046a41106a290300370200201241a0016a200541e8046a41086a290300370200201220052903e80437029801201220143a00b401201241b7016a200541a4056a41026a2d00003a0000201220052f01a4053b00b5012012200f3602b801201241d4016a20054188056a41186a280200360200201241cc016a20054188056a41106a290300370200201241c4016a20054188056a41086a29030037020020122005290388053702bc01201220153a00d801201241db016a200541c4056a41026a2d00003a0000201220052f01c4053b00d901201220163602dc01201241f8016a200541a8056a41186a280200360200201241f0016a200541a8056a41106a290300370200201241e8016a200541a8056a41086a290300370200201220052903a8053702e001201220173a00fc01201241ff016a200541e4056a41026a2d00003a0000201220052f01e4053b00fd0120122019360280022012419c026a200541c8056a41186a28020036020020124194026a200541c8056a41106a2903003702002012418c026a200541c8056a41086a290300370200201220052903c805370284022012201a3a00a002201241a3026a20054184066a41026a2d00003a0000201220052f0184063b00a102201220183602a402201241c0026a200541e8056a41186a280200360200201241b8026a200541e8056a41106a290300370200201241b0026a200541e8056a41086a290300370200201220052903e8053702a8022012201b3a00c402201241c7026a200541a4066a41026a2d00003a0000201220052f01a4063b00c5022012201d3602c802201241e4026a20054188066a41186a280200360200201241dc026a20054188066a41106a290300370200201241d4026a20054188066a41086a29030037020020122005290388063702cc022012201e3a00e802201241eb026a200541c4066a41026a2d00003a0000201220052f01c4063b00e9022012201c3602ec0220124188036a200541a8066a41186a28020036020020124180036a200541a8066a41106a290300370200201241f8026a200541a8066a41086a290300370200201220052903a8063702f0022012201f3a008c032012418f036a200541e4066a41026a2d00003a0000201220052f01e4063b008d032012202136029003201241ac036a200541c8066a41186a280200360200201241a4036a200541c8066a41106a2903003702002012419c036a200541c8066a41086a290300370200201220052903c80637029403201220223a00b003201241b3036a20054184076a41026a2d00003a0000201220052f0184073b00b103201220203602b403201241d0036a200541e8066a41186a280200360200201241c8036a200541e8066a41106a290300370200201241c0036a200541e8066a41086a290300370200201220052903e8063702b803201220233a00d403201241d7036a200541a4076a41026a2d00003a0000201220052f01a4073b00d503201220253602d803201241f4036a20054188076a41186a280200360200201241ec036a20054188076a41106a290300370200201241e4036a20054188076a41086a29030037020020122005290388073702dc03201220263a00f803201241fb036a200541c8076a41026a2d00003a0000201220052f01c8073b00f903201220243602fc0320124198046a200541a8076a41186a28020036020020124190046a200541a8076a41106a29030037020020124188046a200541a8076a41086a290300370200201220052903a80737028004201220273a009c042012419f046a20282d00003a0000201220052f01f4073b009d042012202c3602a004201241bc046a202b280200360200201241b4046a202a290300370200201241ac046a2029290300370200201220052903d8073702a404200b4102470d010c020b20052802840c2101200041083a0000200020013602040c2a0b2005200a3702fc082005200b3602f808200541800c6a200541f8086a10e285808000200541da076a20052d00830c3a0000200541186a200541940c6a290200370300200541206a2005419c0c6a290200370300200520052f00810c3b01d8072005200529028c0c37031020052d00800c211020052802840c210f20052802880c210220052802a40c211420052802a80c21130b200541d4036a41026a200541d8076a41026a2d00003a0000200541b8036a41086a200541106a41086a290300370300200541b8036a41106a200541106a41106a290300370300200520052f01d8073b01d403200520052903103703b8034107210c0c270b200541c00f6a41086a200341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c22034102460d15200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2108200321040b41022103200541b4086a41026a200541fc0b6a41026a2d00003a000020054198086a41086a200541f8086a41086a29030037030020054198086a41106a200541f8086a41106a29030037030020054198086a41186a200541f8086a41186a280200360200200520052f01fc0b3b01b408200520052903f808370398080240024020052802f80d4102470d000c010b200541c00f6a41086a200541e00d6a41186a220341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c22034102460d16200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2110200321030b4102210d200541d4086a41026a200541fc0b6a41026a2d00003a0000200541b8086a41086a200541f8086a41086a290300370300200541b8086a41106a200541f8086a41106a290300370300200541b8086a41186a200541f8086a41186a280200360200200520052f01fc0b3b01d408200520052903f8083703b8080240024020052802840e4102470d000c010b200541c00f6a41086a200541840e6a220d41086a2802003602002005200d2902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c220d4102460d17200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c210b200d210d0b41022114200541f4086a41026a200541fc0b6a41026a2d00003a0000200541d8086a41086a200541f8086a41086a290300370300200541d8086a41106a200541f8086a41106a290300370300200541d8086a41186a200541f8086a41186a280200360200200520052f01fc0b3b01f408200520052903f8083703d8080240024020052802900e4102470d000c010b200541c00f6a41086a200541900e6a221341086a280200360200200520132902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c22134102460d18200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2101201321140b200541b4096a41026a200541fc0b6a41026a2d00003a000020054198096a41086a200541f8086a41086a29030037030020054198096a41106a200541f8086a41106a29030037030020054198096a41186a200541f8086a41186a280200360200200520052f01fc0b3b01b409200520052903f80837039809200541800c6a200541cc076a410510ef8580800020052d00800c22154103460d18200541d4096a41026a20052d00830c3a0000200541b8096a41086a200541800c6a41106a290200370300200541b8096a41106a200541800c6a41186a290200370300200541b8096a41186a200541a00c6a2213280200360200200520052f00810c3b01d409200520052902880c3703b80920052802840c2116200541800c6a200541cc076a410610ef8580800020052d00800c22174103460d19200541f4096a41026a20052d00830c3a0000200541d8096a41086a200541800c6a41106a2212290200370300200541d8096a41106a200541800c6a41186a220c290200370300200541d8096a41186a2013280200360200200520052f00810c3b01f409200520052902880c3703d80920052802840c2118200541800c6a200541cc076a410710ef8580800020052d00800c22194103460d1a200541940a6a41026a20052d00830c3a0000200541f8096a41086a2012290200370300200541f8096a41106a200c290200370300200541f8096a41186a200541a00c6a2213280200360200200520052f00810c3b01940a200520052902880c3703f80920052802840c211a200541800c6a200541cc076a410810ef8580800020052d00800c221b4103460d1b200541b40a6a41026a20052d00830c3a0000200541980a6a41086a200541800c6a41106a2212290200370300200541980a6a41106a200541800c6a41186a220c290200370300200541980a6a41186a2013280200360200200520052f00810c3b01b40a200520052902880c3703980a20052802840c211c200541800c6a200541cc076a410910ef8580800020052d00800c221d4103460d1c200541d40a6a41026a20052d00830c3a0000200541b80a6a41086a2012290200370300200541b80a6a41106a200c290200370300200541b80a6a41186a200541a00c6a2213280200360200200520052f00810c3b01d40a200520052902880c3703b80a20052802840c211e200541800c6a200541cc076a410a10ef8580800020052d00800c221f4103460d1d200541f40a6a41026a20052d00830c3a0000200541d80a6a41086a200541800c6a41106a2212290200370300200541d80a6a41106a200541800c6a41186a220c290200370300200541d80a6a41186a2013280200360200200520052f00810c3b01f40a200520052902880c3703d80a20052802840c2120200541800c6a200541cc076a410b10ef8580800020052d00800c22214103460d1e200541940b6a41026a20052d00830c3a0000200541f80a6a41086a2012290200370300200541f80a6a41106a200c290200370300200541f80a6a41186a200541a00c6a2213280200360200200520052f00810c3b01940b200520052902880c3703f80a20052802840c2122200541800c6a200541cc076a410c10ef8580800020052d00800c22234103460d1f200541b40b6a41026a20052d00830c3a0000200541980b6a41086a200541800c6a41106a2212290200370300200541980b6a41106a200541800c6a41186a220c290200370300200541980b6a41186a2013280200360200200520052f00810c3b01b40b200520052902880c3703980b20052802840c2124200541800c6a200541cc076a410d10ef8580800020052d00800c22254103460d20200541d40b6a41026a20052d00830c3a0000200541b80b6a41086a2012290200370300200541b80b6a41106a200c290200370300200541b80b6a41186a200541a00c6a2213280200360200200520052f00810c3b01d40b200520052902880c3703b80b20052802840c2126200541800c6a200541cc076a410e10ef8580800020052d00800c22274103460d21200541f80b6a41026a20052d00830c3a0000200541d80b6a41086a200541800c6a41106a2212290200370300200541d80b6a41106a200541800c6a41186a220c290200370300200541d80b6a41186a2013280200360200200520052f00810c3b01f80b200520052902880c3703d80b20052802840c2128200541800c6a200541cc076a410f10ef8580800002400240024020052d00800c22294103460d00200541f4076a41026a20052d00830c3a0000200541d8076a41086a2012290200370300200541d8076a41106a200c290200370300200541d8076a41186a200541800c6a41206a280200360200200520052f00810c3b01f407200520052902880c3703d80720052802840c212a41002d00fca3c680001a41c00441002802c8a3c68000118180808000002213450d252009422088a72112201320023a0000201320052f0194083b000120132006360204201320052903f807370208201320043a0024201320052f01b4083b00254103210c201341036a20054194086a41026a2d00003a0000201341106a200541f8076a41086a290300370200201341186a200541f8076a41106a290300370200201341206a200541f8076a41186a280200360200201341276a200541b4086a41026a2d00003a000020132008360228201320033a00482013201036024c201320052903980837022c201341346a20054198086a41086a2903003702002013413c6a20054198086a41106a290300370200201341c4006a20054198086a41186a280200360200201320052f01d4083b0049201341cb006a200541d4086a41026a2d00003a0000201320052903b808370250201341d8006a200541b8086a41086a290300370200201341e0006a200541b8086a41106a290300370200201341e8006a200541b8086a41186a2802003602002013200d3a006c2013200b360270201320143a009001201320052f01f4083b006d201341ef006a200541f4086a41026a2d00003a00002013418c016a200541d8086a41186a28020036020020134184016a200541d8086a41106a290300370200201341fc006a200541d8086a41086a290300370200201320052903d808370274201320052f01b4093b00910120134193016a200541b4096a41026a2d00003a00002013200136029401201341b0016a20054198096a41186a280200360200201341a8016a20054198096a41106a290300370200201341a0016a20054198096a41086a290300370200201320052903980937029801201320153a00b401201341b7016a200541d4096a41026a2d00003a0000201320052f01d4093b00b501201320163602b801201341d4016a200541b8096a41186a280200360200201341cc016a200541b8096a41106a290300370200201341c4016a200541b8096a41086a290300370200201320052903b8093702bc01201320173a00d801201341db016a200541f4096a41026a2d00003a0000201320052f01f4093b00d901201320183602dc01201341f8016a200541d8096a41186a280200360200201341f0016a200541d8096a41106a290300370200201341e8016a200541d8096a41086a290300370200201320052903d8093702e001201320193a00fc01201341ff016a200541940a6a41026a2d00003a0000201320052f01940a3b00fd012013201a360280022013419c026a200541f8096a41186a28020036020020134194026a200541f8096a41106a2903003702002013418c026a200541f8096a41086a290300370200201320052903f809370284022013201b3a00a002201341a3026a200541b40a6a41026a2d00003a0000201320052f01b40a3b00a1022013201c3602a402201341c0026a200541980a6a41186a280200360200201341b8026a200541980a6a41106a290300370200201341b0026a200541980a6a41086a290300370200201320052903980a3702a8022013201d3a00c402201341c7026a200541d40a6a41026a2d00003a0000201320052f01d40a3b00c5022013201e3602c802201341e4026a200541b80a6a41186a280200360200201341dc026a200541b80a6a41106a290300370200201341d4026a200541b80a6a41086a290300370200201320052903b80a3702cc022013201f3a00e802201341eb026a200541f40a6a41026a2d00003a0000201320052f01f40a3b00e902201320203602ec0220134188036a200541d80a6a41186a28020036020020134180036a200541d80a6a41106a290300370200201341f8026a200541d80a6a41086a290300370200201320052903d80a3702f002201320213a008c032013418f036a200541940b6a41026a2d00003a0000201320052f01940b3b008d032013202236029003201341ac036a200541f80a6a41186a280200360200201341a4036a200541f80a6a41106a2903003702002013419c036a200541f80a6a41086a290300370200201320052903f80a37029403201320233a00b003201341b3036a200541b40b6a41026a2d00003a0000201320052f01b40b3b00b103201320243602b403201341d0036a200541980b6a41186a280200360200201341c8036a200541980b6a41106a290300370200201341c0036a200541980b6a41086a290300370200201320052903980b3702b803201320253a00d403201341d7036a200541d40b6a41026a2d00003a0000201320052f01d40b3b00d503201320263602d803201341f4036a200541b80b6a41186a280200360200201341ec036a200541b80b6a41106a290300370200201341e4036a200541b80b6a41086a290300370200201320052903b80b3702dc03201320273a00f803201341fb036a200541f80b6a41026a2d00003a0000201320052f01f80b3b00f903201320283602fc0320134198046a200541d80b6a41186a28020036020020134190046a200541d80b6a41106a29030037020020134188046a200541d80b6a41086a290300370200201320052903d80b37028004201320293a009c042013419f046a200541f4076a41026a2d00003a0000201320052f01f4073b009d042013202a3602a004201341bc046a200541d8076a41186a280200360200201341b4046a200541d8076a41106a290300370200201341ac046a200541d8076a41086a290300370200201320052903d8073702a404200541003602a80c200541800c6a200720072009a76a10f58d808000200541106a41186a2201200541800c6a41186a2202290200370300200541106a41106a2203200541800c6a41106a290200370300200541106a41086a2204200541800c6a41086a290200370300200520052902800c37031020052802a00c210d20052902a40c210e200f4102470d010c020b20052802840c2101200041083a0000200020013602040c280b200520113702dc072005200f3602d807200541800c6a200541d8076a10e285808000200541c00f6a41026a20052d00830c3a0000200541fc0b6a41026a200541870c6a2d00003a0000200541f8086a41086a2002290200370300200541f8086a41106a200541a00c6a290200370300200520052f00810c3b01c00f200520052f00850c3b01fc0b200520052902900c3703f80820052d00800c210c20052d00840c211020052802880c210f200528028c0c210220052802a80c21140b20054198036a41186a200129030037030020054198036a41106a200329030037030020054198036a41086a2004290300370300200541d8036a41026a200541c00f6a41026a2d00003a0000200541d4036a41026a200541fc0b6a41026a2d00003a0000200541b8036a41106a200541f8086a41106a290300370300200541b8036a41086a200541f8086a41086a2903003703002005200529031037039803200520052f01c00f3b01d803200520052f01fc0b3b01d403200520052903f8083703b8030c250b200120052903e00d370200200141286a2006290300370200200141206a2004290300370200200141186a2002290300370200200141106a200541e00d6a41106a290300370200200141086a2003290300370200200041083a0000200020013602040c250b41e484c08000412b200541e00d6a419085c08000419086c08000108981808000000b412020024190d1c2800010a281808000000b20052802840c2101200041083a0000200020013602040c220b20052802840c2101200041083a0000200020013602040c210b20052802840c2101200041083a0000200020013602040c200b20052802840c2101200041083a0000200020013602040c1f0b20052802840c2101200041083a0000200020013602040c1e0b20052802840c2101200041083a0000200020013602040c1d0b20052802840c2101200041083a0000200020013602040c1c0b20052802840c2101200041083a0000200020013602040c1b0b20052802840c2101200041083a0000200020013602040c1a0b20052802840c2101200041083a0000200020013602040c190b20052802840c2101200041083a0000200020013602040c180b20052802840c2101200041083a0000200020013602040c170b20052802840c2101200041083a0000200020013602040c160b20052802840c2101200041083a0000200020013602040c150b20052802840c2101200041083a0000200020013602040c140b410441c00410b280808000000b20052802840c2101200041083a0000200020013602040c120b20052802840c2101200041083a0000200020013602040c110b20052802840c2101200041083a0000200020013602040c100b20052802840c2101200041083a0000200020013602040c0f0b20052802840c2101200041083a0000200020013602040c0e0b20052802840c2101200041083a0000200020013602040c0d0b20052802840c2101200041083a0000200020013602040c0c0b20052802840c2101200041083a0000200020013602040c0b0b20052802840c2101200041083a0000200020013602040c0a0b20052802840c2101200041083a0000200020013602040c090b20052802840c2101200041083a0000200020013602040c080b20052802840c2101200041083a0000200020013602040c070b20052802840c2101200041083a0000200020013602040c060b20052802840c2101200041083a0000200020013602040c050b20052802840c2101200041083a0000200020013602040c040b410441c00410b280808000000b200f428180808010370200200f41086a2006200210848e8080001a410021100b2009422088a7211220054198036a41186a200541800c6a41186a29030037030020054198036a41106a200541800c6a41106a29030037030020054198036a41086a200541800c6a41086a290300370300200541d4036a41026a200541106a41026a2d00003a0000200541b8036a41086a200541e00d6a41086a290300370300200541b8036a41106a200541e00d6a41106a290300370300200520052903800c37039803200520052f01103b01d403200520052903e00d3703b8034105210c0b200020052f01d8033b0001200020052f01d4033b0005200020052903b8033702102000200529039803370234200041036a200541d8036a41026a2d00003a0000200041076a200541d4036a41026a2d00003a0000200041186a200541b8036a41086a290300370200200041206a200541b8036a41106a2903003702002000413c6a20054198036a41086a290300370200200041c4006a20054198036a41106a290300370200200041cc006a20054198036a41186a2903003702002000200e3702582000200d3602542000200f3602082000200236020c200020143602282000201336022c20002012360230200020103a00042000200c3a00000b200541d00f6a2480808080000ba30801057f23808080800041e0016b2204248080808000200241086a28020021052002280204210602400240024002400240024020022802000d00024002400240024020050d00410121020c010b20054120460d012005417f4c0d0441002d00fca3c680001a200541002802c8a3c68000118180808000002202450d050b20022006200510848e808000210341002d00fca3c680001a413041002802c8a3c680001181808080000022020d014104413010b280808000000b2004411c6a41026a200641026a2d00003a0000200441086a2006410f6a290000370300200441106a200641176a290000370300200441186a2006411f6a2d00003a0000200420062f00003b011c2004200629000737030020062800032102410121060c050b2002200536020c2002200336020820022005360204200241888080807836020020022001290000370010200241186a200141086a290000370000200241206a200141106a290000370000200241286a200141186a2900003700000c030b20044180016a41186a200141186a29000037030020044180016a41106a200141106a29000037030020044180016a41086a200141086a2900003703002004200129000037038001200441206a20044180016a20062005200310ec85808000024020042d002022054108460d00200441dc016a41026a20042d00233a0000200420042f00213b01dc012004280224210720044180016a200441286a41d80010848e8080001a02400240200341186a2802002201450d0020032001417f6a36021841002106200341146a22012001280200220141016a22024100200328020c220820022008491b6b360200200341106a28020020014102746a2802002202200328020822014f0d01200328020420024107746a220141046a200120012d00004108461b10e385808000200120053a0004200141083a000020012007360208200120042f01dc013b0005200141076a200441de016a2d00003a00002001410c6a20044180016a41d80010848e8080001a0c060b0240200328020822012003280200470d0020032001109e86808000200328020821010b200328020420014107746a220120053a0004200141083a0000200120042f01dc013b000520012007360208200141076a200441de016a2d00003a00002001410c6a20044180016a41d80010848e8080001a20032003280208220241016a360208410021060c050b2002200141d4d7c2800010f980808000000b200428022421020c020b10ae80808000000b4101200510b280808000000b200041023a0000200020023602040c010b200020042f011c3b00012000200429030037020820002002360204200020063a0000200041036a2004411e6a2d00003a0000200041106a200441086a290300370200200041186a200441106a290300370200200041206a200441186a2802003602000b200441e0016a2480808080000bd40201017f23808080800041d0006b220324808080800002402002410f4b0d000240024020012802002002410c6c6a22022802004102470d00200041023a00000c010b200341086a200241086a28020036020020032002290200370300200341306a41086a2001280204220241086a290000370300200341306a41106a200241106a290000370300200341306a41186a200241186a290000370300200320022900003703302003410c6a200341306a2003200128020828020010ed85808000024020032d000c4102460d002000200329020c370200200041206a2003410c6a41206a280200360200200041186a2003410c6a41186a290200370200200041106a2003410c6a41106a290200370200200041086a2003410c6a41086a2902003702000c010b20002003280210360204200041033a00000b200341d0006a2480808080000f0b2002411041b0d1c2800010f980808000000bd40201017f23808080800041d0006b220324808080800002402002410f4b0d000240024020012802002002410c6c6a22022802004102470d00200041023a00000c010b200341086a200241086a28020036020020032002290200370300200341306a41086a2001280204220241086a290000370300200341306a41106a200241106a290000370300200341306a41186a200241186a290000370300200320022900003703302003410c6a200341306a2003200128020828020010ed85808000024020032d000c4102460d002000200329020c370200200041206a2003410c6a41206a280200360200200041186a2003410c6a41186a290200370200200041106a2003410c6a41106a290200370200200041086a2003410c6a41086a2902003702000c010b20002003280210360204200041033a00000b200341d0006a2480808080000f0b2002411041a0d1c2800010f980808000000bb09e0101617f23808080800041800c6b2203248080808000024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020012802002204417e6a2205410420054106491b0e06050001020304050b200341980b6a200141046a10fa8d808000200141386a21050240024020012802342202450d002005280200210520022002280200220441016a360200410021012004417f4a0d010c1f0b200341026a200541026a2d00003a0000200341c00a6a200141cb006a290000370300200341c50a6a200141d0006a290000370000200320052f00003b0100200320012900433703b80a200128003f2105200128003b2102410121010b200020032902980b370230200020013a0004200041056a20032f01003b0000200041d8006a200341980b6a41286a290200370200200041d0006a200341980b6a41206a290200370200200041c8006a200341980b6a41186a290200370200200041c0006a200341980b6a41106a290200370200200041386a200341980b6a41086a290200370200200041076a200341026a2d00003a00002000410c6a2005360200200041086a2002360200200041053a0000200041106a20032903b80a370200200041186a200341b80a6a41086a290300370200200041206a200341b80a6a41106a290300370200200041286a200341b80a6a41186a2903003702000c1c0b2003200141286a10fa8d8080000240024020012d00040d00200341ba0a6a200141076a2d00003a0000200341980b6a41086a200141146a290200370300200341a80b6a2001411c6a290200370300200341b00b6a200141246a2d00003a00002003200141056a2f00003b01b80a20032001410c6a2902003703980b200141086a2802002101410121020c010b200341b80a6a200141086a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002201450d0020022001417f6a360218200241146a22012001280200220141016a22054100200228020c220420052004491b6b360200200241106a28020020014102746a2802002201200228020822054f0d0b200228020420014107746a220241046a200220022d00004108461b10e385808000200241083a0000200241016a200341980b6a41e30010848e8080001a0c010b0240200228020822012002280200470d0020022001109e86808000200228020821010b200228020420014107746a220141083a0000200141016a200341980b6a41e30010848e8080001a20022002280208220141016a3602080b410021020b20002003290200370228200020023a0004200041056a20032f01b80a3b0000200041086a2001360200200041d0006a200341286a290200370200200041c8006a200341206a290200370200200041c0006a200341186a290200370200200041386a200341106a290200370200200041306a200341086a290200370200200041076a200341ba0a6a2d00003a0000200041063a0000200041246a200341980b6a41186a2802003602002000411c6a200341980b6a41106a290300370200200041146a200341980b6a41086a2903003702002000410c6a20032903980b3702000c1b0b4102210641022107024020012d003022044102460d000240024020040d00200341026a200141336a2d00003a0000200341a00b6a200141c0006a290200370300200341a80b6a200141c8006a290200370300200341b00b6a200141d0006a2d00003a00002003200141316a2f00003b01002003200141386a2902003703980b200141346a2802002105410121070c010b200341b80a6a200141346a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002205450d0020022005417f6a360218200241146a22052005280200220541016a22044100200228020c220820042008491b6b360200200241106a28020020054102746a2802002205200228020822044f0d0c200228020420054107746a220441046a200420042d00004108461b10e385808000200441083a0000200441016a200341980b6a41e30010848e8080001a0c010b0240200228020822052002280200470d0020022005109e86808000200228020821050b200228020420054107746a220541083a0000200541016a200341980b6a41e30010848e8080001a20022002280208220541016a3602080b410021070b200341f0006a41026a200341026a2d00003a0000200341e0056a41086a200341980b6a41086a290300370300200341e0056a41106a200341980b6a41106a290300370300200341e0056a41186a200341980b6a41186a280200360200200320032f01003b0170200320032903980b3703e0050b0240200141d4006a2d000022084102460d000240024020080d00200341026a200141d7006a2d00003a0000200341a00b6a200141e4006a290200370300200341a80b6a200141ec006a290200370300200341b00b6a200141f4006a2d00003a00002003200141d5006a2f00003b01002003200141dc006a2902003703980b200141d8006a2802002104410121060c010b200341b80a6a200141d8006a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002204450d0020022004417f6a360218200241146a22042004280200220441016a22084100200228020c220920082009491b6b360200200241106a28020020044102746a2802002204200228020822084f0d0d200228020420044107746a220841046a200820082d00004108461b10e385808000200841083a0000200841016a200341980b6a41e30010848e8080001a0c010b0240200228020822042002280200470d0020022004109e86808000200228020821040b200228020420044107746a220441083a0000200441016a200341980b6a41e30010848e8080001a20022002280208220441016a3602080b410021060b200341f4006a41026a200341026a2d00003a000020034188066a41086a200341980b6a41086a29030037030020034188066a41106a200341980b6a41106a29030037030020034188066a41186a200341980b6a41186a280200360200200320032f01003b0174200320032903980b370388060b4102210a4102210b0240200141f8006a2d000022094102460d000240024020090d00200341026a200141fb006a2d00003a0000200341a00b6a20014188016a290200370300200341a80b6a20014190016a290200370300200341b00b6a20014198016a2d00003a00002003200141f9006a2f00003b0100200320014180016a2902003703980b200141fc006a28020021084101210b0c010b200341b80a6a200141fc006a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002208450d0020022008417f6a360218200241146a22082008280200220841016a22094100200228020c220c2009200c491b6b360200200241106a28020020084102746a2802002208200228020822094f0d0e200228020420084107746a220941046a200920092d00004108461b10e385808000200941083a0000200941016a200341980b6a41e30010848e8080001a0c010b0240200228020822082002280200470d0020022008109e86808000200228020821080b200228020420084107746a220841083a0000200841016a200341980b6a41e30010848e8080001a20022002280208220841016a3602080b4100210b0b200341f8006a41026a200341026a2d00003a0000200341b0066a41086a200341980b6a41086a290300370300200341b0066a41106a200341980b6a41106a290300370300200341b0066a41186a200341980b6a41186a280200360200200320032f01003b0178200320032903980b3703b0060b02402001419c016a2d0000220c4102460d0002400240200c0d00200341026a2001419f016a2d00003a0000200341a00b6a200141ac016a290200370300200341a80b6a200141b4016a290200370300200341b00b6a200141bc016a2d00003a000020032001419d016a2f00003b01002003200141a4016a2902003703980b200141a0016a28020021094101210a0c010b200341b80a6a200141a0016a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002209450d0020022009417f6a360218200241146a22092009280200220941016a220c4100200228020c220d200c200d491b6b360200200241106a28020020094102746a28020022092002280208220c4f0d0f200228020420094107746a220c41046a200c200c2d00004108461b10e385808000200c41083a0000200c41016a200341980b6a41e30010848e8080001a0c010b0240200228020822092002280200470d0020022009109e86808000200228020821090b200228020420094107746a220941083a0000200941016a200341980b6a41e30010848e8080001a20022002280208220941016a3602080b4100210a0b200341fc006a41026a200341026a2d00003a0000200341d8066a41086a200341980b6a41086a290300370300200341d8066a41106a200341980b6a41106a290300370300200341d8066a41186a200341980b6a41186a280200360200200320032f01003b017c200320032903980b3703d8060b4102210e4102210f0240200141c0016a2d0000220d4102460d0002400240200d0d00200341026a200141c3016a2d00003a0000200341a00b6a200141d0016a290200370300200341a80b6a200141d8016a290200370300200341b00b6a200141e0016a2d00003a00002003200141c1016a2f00003b01002003200141c8016a2902003703980b200141c4016a280200210c4101210f0c010b200341b80a6a200141c4016a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200220c450d002002200c417f6a360218200241146a220c200c280200220c41016a220d4100200228020c2210200d2010491b6b360200200241106a280200200c4102746a280200220c2002280208220d4f0d102002280204200c4107746a220d41046a200d200d2d00004108461b10e385808000200d41083a0000200d41016a200341980b6a41e30010848e8080001a0c010b02402002280208220c2002280200470d002002200c109e868080002002280208210c0b2002280204200c4107746a220c41083a0000200c41016a200341980b6a41e30010848e8080001a20022002280208220c41016a3602080b4100210f0b20034180016a41026a200341026a2d00003a000020034180076a41086a200341980b6a41086a29030037030020034180076a41106a200341980b6a41106a29030037030020034180076a41186a200341980b6a41186a280200360200200320032f01003b018001200320032903980b370380070b0240200141e4016a2d000022104102460d000240024020100d00200341026a200141e7016a2d00003a0000200341a00b6a200141f4016a290200370300200341a80b6a200141fc016a290200370300200341b00b6a20014184026a2d00003a00002003200141e5016a2f00003b01002003200141ec016a2902003703980b200141e8016a280200210d4101210e0c010b200341b80a6a200141e8016a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200220d450d002002200d417f6a360218200241146a220d200d280200220d41016a22104100200228020c221120102011491b6b360200200241106a280200200d4102746a280200220d200228020822104f0d112002280204200d4107746a221041046a201020102d00004108461b10e385808000201041083a0000201041016a200341980b6a41e30010848e8080001a0c010b02402002280208220d2002280200470d002002200d109e868080002002280208210d0b2002280204200d4107746a220d41083a0000200d41016a200341980b6a41e30010848e8080001a20022002280208220d41016a3602080b4100210e0b20034184016a41026a200341026a2d00003a0000200341a8076a41086a200341980b6a41086a290300370300200341a8076a41106a200341980b6a41106a290300370300200341a8076a41186a200341980b6a41186a280200360200200320032f01003b018401200320032903980b3703a8070b4102211241022113024020014188026a2d000022114102460d000240024020110d00200341026a2001418b026a2d00003a0000200341a00b6a20014198026a290200370300200341a80b6a200141a0026a290200370300200341b00b6a200141a8026a2d00003a0000200320014189026a2f00003b0100200320014190026a2902003703980b2001418c026a2802002110410121130c010b200341b80a6a2001418c026a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002210450d0020022010417f6a360218200241146a22102010280200221041016a22114100200228020c221420112014491b6b360200200241106a28020020104102746a2802002210200228020822114f0d12200228020420104107746a221141046a201120112d00004108461b10e385808000201141083a0000201141016a200341980b6a41e30010848e8080001a0c010b0240200228020822102002280200470d0020022010109e86808000200228020821100b200228020420104107746a221041083a0000201041016a200341980b6a41e30010848e8080001a20022002280208221041016a3602080b410021130b20034188016a41026a200341026a2d00003a0000200341d0076a41086a200341980b6a41086a290300370300200341d0076a41106a200341980b6a41106a290300370300200341d0076a41186a200341980b6a41186a280200360200200320032f01003b018801200320032903980b3703d0070b0240200141ac026a2d000022144102460d000240024020140d00200341026a200141af026a2d00003a0000200341a00b6a200141bc026a290200370300200341a80b6a200141c4026a290200370300200341b00b6a200141cc026a2d00003a00002003200141ad026a2f00003b01002003200141b4026a2902003703980b200141b0026a2802002111410121120c010b200341b80a6a200141b0026a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002211450d0020022011417f6a360218200241146a22112011280200221141016a22144100200228020c221520142015491b6b360200200241106a28020020114102746a2802002211200228020822144f0d13200228020420114107746a221441046a201420142d00004108461b10e385808000201441083a0000201441016a200341980b6a41e30010848e8080001a0c010b0240200228020822112002280200470d0020022011109e86808000200228020821110b200228020420114107746a221141083a0000201141016a200341980b6a41e30010848e8080001a20022002280208221141016a3602080b410021120b2003418c016a41026a200341026a2d00003a0000200341f8076a41086a200341980b6a41086a290300370300200341f8076a41106a200341980b6a41106a290300370300200341f8076a41186a200341980b6a41186a280200360200200320032f01003b018c01200320032903980b3703f8070b41022116410221170240200141d0026a2d000022154102460d000240024020150d00200341026a200141d3026a2d00003a0000200341a00b6a200141e0026a290200370300200341a80b6a200141e8026a290200370300200341b00b6a200141f0026a2d00003a00002003200141d1026a2f00003b01002003200141d8026a2902003703980b200141d4026a2802002114410121170c010b200341b80a6a200141d4026a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002214450d0020022014417f6a360218200241146a22142014280200221441016a22154100200228020c221820152018491b6b360200200241106a28020020144102746a2802002214200228020822154f0d14200228020420144107746a221541046a201520152d00004108461b10e385808000201541083a0000201541016a200341980b6a41e30010848e8080001a0c010b0240200228020822142002280200470d0020022014109e86808000200228020821140b200228020420144107746a221441083a0000201441016a200341980b6a41e30010848e8080001a20022002280208221441016a3602080b410021170b20034190016a41026a200341026a2d00003a0000200341a0086a41086a200341980b6a41086a290300370300200341a0086a41106a200341980b6a41106a290300370300200341a0086a41186a200341980b6a41186a280200360200200320032f01003b019001200320032903980b3703a0080b0240200141f4026a2d000022184102460d000240024020180d00200341026a200141f7026a2d00003a0000200341a00b6a20014184036a290200370300200341a80b6a2001418c036a290200370300200341b00b6a20014194036a2d00003a00002003200141f5026a2f00003b01002003200141fc026a2902003703980b200141f8026a2802002115410121160c010b200341b80a6a200141f8026a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002215450d0020022015417f6a360218200241146a22152015280200221541016a22184100200228020c221920182019491b6b360200200241106a28020020154102746a2802002215200228020822184f0d15200228020420154107746a221841046a201820182d00004108461b10e385808000201841083a0000201841016a200341980b6a41e30010848e8080001a0c010b0240200228020822152002280200470d0020022015109e86808000200228020821150b200228020420154107746a221541083a0000201541016a200341980b6a41e30010848e8080001a20022002280208221541016a3602080b410021160b20034194016a41026a200341026a2d00003a0000200341c8086a41086a200341980b6a41086a290300370300200341c8086a41106a200341980b6a41106a290300370300200341c8086a41186a200341980b6a41186a280200360200200320032f01003b019401200320032903980b3703c8080b4102211a4102211b024020014198036a2d000022194102460d000240024020190d00200341026a2001419b036a2d00003a0000200341a00b6a200141a8036a290200370300200341a80b6a200141b0036a290200370300200341b00b6a200141b8036a2d00003a0000200320014199036a2f00003b01002003200141a0036a2902003703980b2001419c036a28020021184101211b0c010b200341b80a6a2001419c036a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002218450d0020022018417f6a360218200241146a22182018280200221841016a22194100200228020c221c2019201c491b6b360200200241106a28020020184102746a2802002218200228020822194f0d16200228020420184107746a221941046a201920192d00004108461b10e385808000201941083a0000201941016a200341980b6a41e30010848e8080001a0c010b0240200228020822182002280200470d0020022018109e86808000200228020821180b200228020420184107746a221841083a0000201841016a200341980b6a41e30010848e8080001a20022002280208221841016a3602080b4100211b0b20034198016a41026a200341026a2d00003a0000200341f0086a41086a200341980b6a41086a290300370300200341f0086a41106a200341980b6a41106a290300370300200341f0086a41186a200341980b6a41186a280200360200200320032f01003b019801200320032903980b3703f0080b0240200141bc036a2d0000221c4102460d0002400240201c0d00200341026a200141bf036a2d00003a0000200341a00b6a200141cc036a290200370300200341a80b6a200141d4036a290200370300200341b00b6a200141dc036a2d00003a00002003200141bd036a2f00003b01002003200141c4036a2902003703980b200141c0036a28020021194101211a0c010b200341b80a6a200141c0036a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002219450d0020022019417f6a360218200241146a22192019280200221941016a221c4100200228020c221d201c201d491b6b360200200241106a28020020194102746a28020022192002280208221c4f0d17200228020420194107746a221c41046a201c201c2d00004108461b10e385808000201c41083a0000201c41016a200341980b6a41e30010848e8080001a0c010b0240200228020822192002280200470d0020022019109e86808000200228020821190b200228020420194107746a221941083a0000201941016a200341980b6a41e30010848e8080001a20022002280208221941016a3602080b4100211a0b2003419c016a41026a200341026a2d00003a000020034198096a41086a200341980b6a41086a29030037030020034198096a41106a200341980b6a41106a29030037030020034198096a41186a200341980b6a41186a280200360200200320032f01003b019c01200320032903980b370398090b4102211e4102211f0240200141e0036a2d0000221d4102460d0002400240201d0d00200341026a200141e3036a2d00003a0000200341a00b6a200141f0036a290200370300200341a80b6a200141f8036a290200370300200341b00b6a20014180046a2d00003a00002003200141e1036a2f00003b01002003200141e8036a2902003703980b200141e4036a280200211c4101211f0c010b200341b80a6a200141e4036a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200221c450d002002201c417f6a360218200241146a221c201c280200221c41016a221d4100200228020c2220201d2020491b6b360200200241106a280200201c4102746a280200221c2002280208221d4f0d182002280204201c4107746a221d41046a201d201d2d00004108461b10e385808000201d41083a0000201d41016a200341980b6a41e30010848e8080001a0c010b02402002280208221c2002280200470d002002201c109e868080002002280208211c0b2002280204201c4107746a221c41083a0000201c41016a200341980b6a41e30010848e8080001a20022002280208221c41016a3602080b4100211f0b200341a0016a41026a200341026a2d00003a0000200341c0096a41086a200341980b6a41086a290300370300200341c0096a41106a200341980b6a41106a290300370300200341c0096a41186a200341980b6a41186a280200360200200320032f01003b01a001200320032903980b3703c0090b024020014184046a2d000022204102460d000240024020200d00200341026a20014187046a2d00003a0000200341a00b6a20014194046a290200370300200341a80b6a2001419c046a290200370300200341b00b6a200141a4046a2d00003a0000200320014185046a2f00003b010020032001418c046a2902003703980b20014188046a280200211d4101211e0c010b200341b80a6a20014188046a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200221d450d002002201d417f6a360218200241146a221d201d280200221d41016a22204100200228020c222120202021491b6b360200200241106a280200201d4102746a280200221d200228020822204f0d192002280204201d4107746a222041046a202020202d00004108461b10e385808000202041083a0000202041016a200341980b6a41e30010848e8080001a0c010b02402002280208221d2002280200470d002002201d109e868080002002280208211d0b2002280204201d4107746a221d41083a0000201d41016a200341980b6a41e30010848e8080001a20022002280208221d41016a3602080b4100211e0b200341a4016a41026a200341026a2d00003a0000200341e8096a41086a200341980b6a41086a290300370300200341e8096a41106a200341980b6a41106a290300370300200341e8096a41186a200341980b6a41186a280200360200200320032f01003b01a401200320032903980b3703e8090b41022122410221230240200141a8046a2d000022214102460d000240024020210d00200341026a200141ab046a2d00003a0000200341a00b6a200141b8046a290200370300200341a80b6a200141c0046a290200370300200341b00b6a200141c8046a2d00003a00002003200141a9046a2f00003b01002003200141b0046a2902003703980b200141ac046a2802002120410121230c010b200341b80a6a200141ac046a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002220450d0020022020417f6a360218200241146a22202020280200222041016a22214100200228020c222320212023491b6b360200200241106a28020020204102746a2802002220200228020822214f0d1a200228020420204107746a222141046a202120212d00004108461b10e385808000202141083a0000202141016a200341980b6a41e30010848e8080001a0c010b0240200228020822202002280200470d0020022020109e86808000200228020821200b200228020420204107746a222041083a0000202041016a200341980b6a41e30010848e8080001a20022002280208222041016a3602080b410021230b200341a8016a41026a200341026a2d00003a0000200341900a6a41086a200341980b6a41086a290300370300200341900a6a41106a200341980b6a41106a290300370300200341900a6a41186a200341980b6a41186a280200360200200320032f01003b01a801200320032903980b3703900a0b0240200141cc046a2d000022244102460d000240024020240d00200341ba056a200141cf046a2d00003a0000200341a00b6a200141dc046a290200370300200341a80b6a200141e4046a290200370300200341b00b6a200141ec046a2d00003a00002003200141cd046a2f00003b01b8052003200141d4046a2902003703980b200141d0046a2802002121410121220c010b200341b80a6a200141d0046a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002221450d0020022021417f6a360218200241146a22212021280200222141016a22224100200228020c222420222024491b6b360200200241106a28020020214102746a2802002221200228020822224f0d1b200228020420214107746a220241046a200220022d00004108461b10e385808000200241083a0000200241016a200341980b6a41e30010848e8080001a0c010b0240200228020822212002280200470d0020022021109e86808000200228020821210b200228020420214107746a222141083a0000202141016a200341980b6a41e30010848e8080001a20022002280208222141016a3602080b410021220b200341ac016a41026a200341b8056a41026a2d00003a0000200341086a200341980b6a41086a290300370300200341106a200341980b6a41106a290300370300200341186a200341980b6a41186a280200360200200320032f01b8053b01ac01200320032903980b3703000b200341ec006a41026a2224200341f0006a41026a2d00003a0000200341980b6a41086a2225200341e0056a41086a290300370300200341980b6a41106a2226200341e0056a41106a290300370300200341980b6a41186a2227200341e0056a41186a280200360200200341e8006a41026a2228200341f4006a41026a2d00003a0000200341b80a6a41186a222920034188066a41186a280200360200200341b80a6a41106a222a20034188066a41106a290300370300200341b80a6a41086a222b20034188066a41086a290300370300200320032f01703b016c200320032903e0053703980b200320032f01743b016820032003290388063703b80a200341e4006a41026a222c200341f8006a41026a2d00003a0000200341b8056a41086a222d200341b0066a41086a290300370300200341b8056a41106a222e200341b0066a41106a290300370300200341b8056a41186a222f200341b0066a41186a280200360200200341e0006a41026a2230200341fc006a41026a2d00003a000020034190056a41086a2231200341d8066a41086a29030037030020034190056a41106a2232200341d8066a41106a29030037030020034190056a41186a2233200341d8066a41186a280200360200200320032f01783b0164200320032903b0063703b805200320032f017c3b0160200320032903d80637039005200341dc006a41026a223420034180016a41026a2d00003a0000200341e8046a41186a223520034180076a41186a280200360200200341e8046a41106a223620034180076a41106a290300370300200341e8046a41086a223720034180076a41086a290300370300200341d8006a41026a223820034184016a41026a2d00003a0000200341c0046a41186a2239200341a8076a41186a280200360200200341c0046a41106a223a200341a8076a41106a290300370300200341c0046a41086a223b200341a8076a41086a290300370300200320032f0180013b015c20032003290380073703e804200320032f0184013b0158200320032903a8073703c004200341d4006a41026a223c20034188016a41026a2d00003a0000200320032f0188013b015420034198046a41186a223d200341d0076a41186a28020036020020034198046a41106a223e200341d0076a41106a29030037030020034198046a41086a223f200341d0076a41086a290300370300200320032903d00737039804200341d0006a41026a22402003418c016a41026a2d00003a0000200320032f018c013b0150200341f0036a41186a2241200341f8076a41186a280200360200200341f0036a41106a2242200341f8076a41106a290300370300200341f0036a41086a2243200341f8076a41086a290300370300200320032903f8073703f003200341cc006a41026a224420034190016a41026a2d00003a0000200320032f0190013b014c200341c8036a41186a2245200341a0086a41186a280200360200200341c8036a41106a2246200341a0086a41106a290300370300200341c8036a41086a2247200341a0086a41086a290300370300200320032903a0083703c803200341c8006a41026a224820034194016a41026a2d00003a0000200320032f0194013b0148200341a0036a41186a2249200341c8086a41186a280200360200200341a0036a41106a224a200341c8086a41106a290300370300200341a0036a41086a224b200341c8086a41086a290300370300200320032903c8083703a003200341c4006a41026a224c20034198016a41026a2d00003a0000200320032f0198013b0144200341f8026a41186a224d200341f0086a41186a280200360200200341f8026a41106a224e200341f0086a41106a290300370300200341f8026a41086a224f200341f0086a41086a290300370300200320032903f0083703f802200341c0006a41026a22502003419c016a41026a2d00003a0000200320032f019c013b0140200341d0026a41186a225120034198096a41186a280200360200200341d0026a41106a225220034198096a41106a290300370300200341d0026a41086a225320034198096a41086a29030037030020032003290398093703d0022003413c6a41026a2254200341a0016a41026a2d00003a0000200320032f01a0013b013c200341a8026a41186a2255200341c0096a41186a280200360200200341a8026a41106a2256200341c0096a41106a290300370300200341a8026a41086a2257200341c0096a41086a290300370300200320032903c0093703a802200341386a41026a2258200341a4016a41026a2d00003a0000200320032f01a4013b013820034180026a41186a2259200341e8096a41186a28020036020020034180026a41106a225a200341e8096a41106a29030037030020034180026a41086a225b200341e8096a41086a290300370300200320032903e80937038002200341346a41026a225c200341a8016a41026a2d00003a0000200320032f01a8013b0134200341d8016a41186a225d200341900a6a41186a280200360200200341d8016a41106a225e200341900a6a41106a290300370300200341d8016a41086a225f200341900a6a41086a290300370300200320032903900a3703d801200341306a41026a2260200341ac016a41026a2d00003a0000200320032f01ac013b0130200341b0016a41186a2261200341186a280200360200200341b0016a41106a2262200341106a290300370300200341b0016a41086a2263200341086a290300370300200320032903003703b00141002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d18200220073a0000200220032f016c3b000120022005360204200220032903980b370008200220063a0024200220032f01683b002541032107200241036a20242d00003a0000200241106a2025290300370000200241186a2026290300370000200241206a2027280200360000200241276a20282d00003a0000200220043602282002200b3a00482002200836024c200220032903b80a37002c200241346a202b2903003700002002413c6a202a290300370000200241c4006a2029280200360000200220032f01643b0049200241cb006a202c2d00003a0000200220032903b805370050200241d8006a202d290300370000200241e0006a202e290300370000200241e8006a202f2802003600002002200a3a006c200220093602702002200f3a009001200220032f01603b006d200241ef006a20302d00003a00002002418c016a203328020036000020024184016a2032290300370000200241fc006a20312903003700002002200329039005370074200220032f015c3b00910120024193016a20342d00003a00002002200c36029401200241b0016a2035280200360000200241a8016a2036290300370000200241a0016a2037290300370000200220032903e804370098012002200e3a00b401200241b7016a20382d00003a0000200220032f01583b00b5012002200d3602b801200241d4016a2039280200360000200241cc016a203a290300370000200241c4016a203b290300370000200220032903c0043700bc01200220133a00d801200241db016a203c2d00003a0000200220032f01543b00d901200220103602dc01200241f8016a203d280200360000200241f0016a203e290300370000200241e8016a203f29030037000020022003290398043700e001200220123a00fc01200241ff016a20402d00003a0000200220032f01503b00fd0120022011360280022002419c026a204128020036000020024194026a20422903003700002002418c026a2043290300370000200220032903f00337008402200220173a00a002200241a3026a20442d00003a0000200220032f014c3b00a102200220143602a402200241c0026a2045280200360000200241b8026a2046290300370000200241b0026a2047290300370000200220032903c8033700a802200220163a00c402200241c7026a20482d00003a0000200220032f01483b00c502200220153602c802200241e4026a2049280200360000200241dc026a204a290300370000200241d4026a204b290300370000200220032903a0033700cc022002201b3a00e802200241eb026a204c2d00003a0000200220032f01443b00e902200220183602ec0220024188036a204d28020036000020024180036a204e290300370000200241f8026a204f290300370000200220032903f8023700f0022002201a3a008c032002418f036a20502d00003a0000200220032f01403b008d032002201936029003200241ac036a2051280200360000200241a4036a20522903003700002002419c036a2053290300370000200220032903d002370094032002201f3a00b003200241b3036a20542d00003a0000200220032f013c3b00b1032002201c3602b403200241d0036a2055280200360000200241c8036a2056290300370000200241c0036a2057290300370000200220032903a8023700b8032002201e3a00d403200241d7036a20582d00003a0000200220032f01383b00d5032002201d3602d803200241f4036a2059280200360000200241ec036a205a290300370000200241e4036a205b29030037000020022003290380023700dc03200220233a00f803200241fb036a205c2d00003a0000200220032f01343b00f903200220203602fc0320024198046a205d28020036000020024190046a205e29030037000020024188046a205f290300370000200220032903d80137008004200220223a009c042002419f046a20602d00003a0000200220032f01303b009d04200220213602a004200241bc046a2061280200360000200241b4046a2062290300370000200241ac046a2063290300370000200220032903b0013700a40420012802040d030c040b4102210541022108024020012d002c4102460d00200341980b6a2001412c6a200210f18580800020034190066a200341a10b6a29000037030020034198066a200341a90b6a290000370300200341a0066a200341b10b6a290000370300200341a7066a200341b80b6a280000360000200320032900990b3703880620032d00980b21080b0240200141d0006a22092d00004102460d00200341980b6a2009200210f185808000200341b8066a200341a10b6a290000370300200341c0066a200341a90b6a290000370300200341c8066a200341b10b6a290000370300200341cf066a200341b80b6a280000360000200320032900990b3703b00620032d00980b21050b410221094102210c0240200141f4006a220d2d00004102460d00200341980b6a200d200210f185808000200341e0066a200341a10b6a290000370300200341e8066a200341a90b6a290000370300200341f0066a200341b10b6a290000370300200341f7066a200341b80b6a280000360000200320032900990b3703d80620032d00980b210c0b024020014198016a220d2d00004102460d00200341980b6a200d200210f18580800020034188076a200341a10b6a29000037030020034190076a200341a90b6a29000037030020034198076a200341b10b6a2900003703002003419f076a200341b80b6a280000360000200320032900990b3703800720032d00980b21090b4102210d410221100240200141bc016a22112d00004102460d00200341980b6a2011200210f185808000200341b0076a200341a10b6a290000370300200341b8076a200341a90b6a290000370300200341c0076a200341b10b6a290000370300200341c7076a200341b80b6a280000360000200320032900990b3703a80720032d00980b21100b0240200141e0016a22112d00004102460d00200341980b6a2011200210f185808000200341d8076a200341a10b6a290000370300200341e0076a200341a90b6a290000370300200341e8076a200341b10b6a290000370300200341ef076a200341b80b6a280000360000200320032900990b3703d00720032d00980b210d0b4102211141022114024020014184026a22152d00004102460d00200341980b6a2015200210f18580800020034180086a200341a10b6a29000037030020034188086a200341a90b6a29000037030020034190086a200341b10b6a29000037030020034197086a200341b80b6a280000360000200320032900990b3703f80720032d00980b21140b0240200141a8026a22152d00004102460d00200341980b6a2015200210f185808000200341a8086a200341a10b6a290000370300200341b0086a200341a90b6a290000370300200341b8086a200341b10b6a290000370300200341bf086a200341b80b6a280000360000200320032900990b3703a00820032d00980b21110b41022115410221180240200141cc026a22192d00004102460d00200341980b6a2019200210f185808000200341d0086a200341a10b6a290000370300200341d8086a200341a90b6a290000370300200341e0086a200341b10b6a290000370300200341e7086a200341b80b6a280000360000200320032900990b3703c80820032d00980b21180b0240200141f0026a22192d00004102460d00200341980b6a2019200210f185808000200341f8086a200341a10b6a29000037030020034180096a200341a90b6a29000037030020034188096a200341b10b6a2900003703002003418f096a200341b80b6a280000360000200320032900990b3703f00820032d00980b21150b410221194102211c024020014194036a221d2d00004102460d00200341980b6a201d200210f185808000200341a0096a200341a10b6a290000370300200341a8096a200341a90b6a290000370300200341b0096a200341b10b6a290000370300200341b7096a200341b80b6a280000360000200320032900990b3703980920032d00980b211c0b0240200141b8036a221d2d00004102460d00200341980b6a201d200210f185808000200341c8096a200341a10b6a290000370300200341d0096a200341a90b6a290000370300200341d8096a200341b10b6a290000370300200341df096a200341b80b6a280000360000200320032900990b3703c00920032d00980b21190b4102211d410221200240200141dc036a22212d00004102460d00200341980b6a2021200210f185808000200341f0096a200341a10b6a290000370300200341f8096a200341a90b6a290000370300200341800a6a200341b10b6a290000370300200341870a6a200341b80b6a280000360000200320032900990b3703e80920032d00980b21200b024020014180046a22212d00004102460d00200341980b6a2021200210f185808000200341980a6a200341a10b6a290000370300200341a00a6a200341a90b6a290000370300200341a80a6a200341b10b6a290000370300200341af0a6a200341b80b6a280000360000200320032900990b3703900a20032d00980b211d0b41022121410221070240200141a4046a22062d00004102460d00200341980b6a2006200210f185808000200341086a200341a10b6a290000370300200341106a200341a90b6a290000370300200341186a200341b10b6a2900003703002003411f6a200341b80b6a280000360000200320032900990b37030020032d00980b21070b0240200141c8046a22062d00004102460d00200341980b6a2006200210f185808000200341c00a6a200341a10b6a290000370300200341c80a6a200341a90b6a290000370300200341d00a6a200341b10b6a290000370300200341d70a6a200341b80b6a280000360000200320032900990b3703b80a20032d00980b21210b200341980b6a411f6a220620034188066a411f6a280000360000200341980b6a41186a220b20034188066a41186a290300370300200341980b6a41106a220a20034188066a41106a290300370300200341980b6a41086a220f20034188066a41086a290300370300200341e0056a41086a220e200341b0066a41086a290300370300200341e0056a41106a2213200341b0066a41106a290300370300200341e0056a41186a2212200341b0066a41186a290300370300200341e0056a411f6a2217200341b0066a411f6a28000036000020032003290388063703980b200320032903b0063703e005200341b8056a411f6a2216200341d8066a411f6a280000360000200341b8056a41186a221b200341d8066a41186a290300370300200341b8056a41106a221a200341d8066a41106a290300370300200341b8056a41086a221f200341d8066a41086a29030037030020034190056a41086a221e20034180076a41086a29030037030020034190056a41106a222320034180076a41106a29030037030020034190056a41186a222220034180076a41186a29030037030020034190056a411f6a222420034180076a411f6a280000360000200320032903d8063703b805200320032903800737039005200341e8046a411f6a2225200341a8076a411f6a280000360000200341e8046a41186a2226200341a8076a41186a290300370300200341e8046a41106a2227200341a8076a41106a290300370300200341e8046a41086a2228200341a8076a41086a290300370300200341c0046a411f6a2229200341d0076a411f6a280000360000200341c0046a41186a222a200341d0076a41186a290300370300200341c0046a41106a222b200341d0076a41106a290300370300200341c0046a41086a222c200341d0076a41086a290300370300200320032903a8073703e804200320032903d0073703c00420034198046a411f6a222d200341f8076a411f6a28000036000020034198046a41186a222e200341f8076a41186a29030037030020034198046a41106a222f200341f8076a41106a29030037030020034198046a41086a2230200341f8076a41086a290300370300200320032903f80737039804200341f0036a411f6a2231200341a0086a411f6a280000360000200341f0036a41186a2232200341a0086a41186a290300370300200341f0036a41106a2233200341a0086a41106a290300370300200341f0036a41086a2234200341a0086a41086a290300370300200320032903a0083703f003200341c8036a411f6a2235200341c8086a411f6a280000360000200341c8036a41186a2236200341c8086a41186a290300370300200341c8036a41106a2237200341c8086a41106a290300370300200341c8036a41086a2238200341c8086a41086a290300370300200320032903c8083703c803200341a0036a411f6a2239200341f0086a411f6a280000360000200341a0036a41186a223a200341f0086a41186a290300370300200341a0036a41106a223b200341f0086a41106a290300370300200341a0036a41086a223c200341f0086a41086a290300370300200320032903f0083703a003200341f8026a411f6a223d20034198096a411f6a280000360000200341f8026a41186a223e20034198096a41186a290300370300200341f8026a41106a223f20034198096a41106a290300370300200341f8026a41086a224020034198096a41086a29030037030020032003290398093703f802200341d0026a411f6a2241200341c0096a411f6a280000360000200341d0026a41186a2242200341c0096a41186a290300370300200341d0026a41106a2243200341c0096a41106a290300370300200341d0026a41086a2244200341c0096a41086a290300370300200320032903c0093703d002200341a8026a411f6a2245200341e8096a411f6a280000360000200341a8026a41186a2246200341e8096a41186a290300370300200341a8026a41106a2247200341e8096a41106a290300370300200341a8026a41086a2248200341e8096a41086a290300370300200320032903e8093703a80220034180026a411f6a2249200341900a6a411f6a28000036000020034180026a41186a224a200341900a6a41186a29030037030020034180026a41106a224b200341900a6a41106a29030037030020034180026a41086a224c200341900a6a41086a290300370300200320032903900a37038002200341d8016a411f6a224d2003411f6a280000360000200341d8016a41186a224e200341186a290300370300200341d8016a41106a224f200341106a290300370300200341d8016a41086a2250200341086a290300370300200320032903003703d801200341b0016a411f6a2251200341b80a6a411f6a280000360000200341b0016a41186a2252200341b80a6a41186a290300370300200341b0016a41106a2253200341b80a6a41106a290300370300200341b0016a41086a2254200341b80a6a41086a290300370300200320032903b80a3703b00141002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d18200220083a0000200220032903980b370001200220053a0024200220032903e005370025200241096a200f290300370000200241116a200a290300370000200241196a200b290300370000200241206a20062800003600002002412d6a200e290300370000200241356a20132903003700002002413d6a2012290300370000200241c4006a20172800003600002002200c3a0048200220093a006c200220032903b805370049200241d1006a201f290300370000200241d9006a201a290300370000200241e1006a201b290300370000200241e8006a2016280000360000200220032903900537006d200241f5006a201e290300370000200241fd006a202329030037000020024185016a20222903003700002002418c016a2024280000360000200220103a0090012002200d3a00b401200220032903e8043700910120024199016a2028290300370000200241a1016a2027290300370000200241a9016a2026290300370000200241b0016a2025280000360000200220032903c0043700b501200241bd016a202c290300370000200241c5016a202b290300370000200241cd016a202a290300370000200241d4016a2029280000360000200220143a00d801200241f8016a202d280000360000200241f1016a202e290300370000200241e9016a202f290300370000200241e1016a203029030037000020022003290398043700d901200220113a00fc012002419c026a203128000036000020024195026a20322903003700002002418d026a203329030037000020024185026a2034290300370000200220032903f0033700fd01200220183a00a002200241c0026a2035280000360000200241b9026a2036290300370000200241b1026a2037290300370000200241a9026a2038290300370000200220032903c8033700a102200220153a00c402200241e4026a2039280000360000200241dd026a203a290300370000200241d5026a203b290300370000200241cd026a203c290300370000200220032903a0033700c5022002201c3a00e80220024188036a203d28000036000020024181036a203e290300370000200241f9026a203f290300370000200241f1026a2040290300370000200220032903f8023700e902200220193a008c03200241ac036a2041280000360000200241a5036a20422903003700002002419d036a204329030037000020024195036a2044290300370000200220032903d00237008d03200220203a00b003200241d0036a2045280000360000200241c9036a2046290300370000200241c1036a2047290300370000200241b9036a2048290300370000200220032903a8023700b1032002201d3a00d403200241f4036a2049280000360000200241ed036a204a290300370000200241e5036a204b290300370000200241dd036a204c29030037000020022003290380023700d503200220073a00f80320024198046a204d28000036000020024191046a204e29030037000020024189046a204f29030037000020024181046a2050290300370000200220032903d8013700f903200220213a009c04200241bc046a2051280000360000200241b5046a2052290300370000200241ad046a2053290300370000200241a5046a2054290300370000200220032903b00137009d04200341980b6a200141ec046a10fa8d80800020040d04410321010c050b200341a40b6a42013702002003410136029c0b200341fcd1c280003602980b200341e0818080003602bc0a200341c4d2c280003602b80a2003200341b80a6a3602a00b200341980b6a41ccd2c2800010f680808000000b200041043a00000c170b2001410c6a210402400240200141086a2802002205450d002004280200210420052005280200220141016a360200410021072001417f4c0d190c010b200341026a200441026a2d00003a0000200341980b6a41086a2001411f6a290000370300200341a50b6a200141246a290000370000200320042f00003b0100200320012900173703980b20012800132104200128000f2105410121070b200341900a6a41026a200341026a2d00003a0000200341b80a6a41086a200341980b6a41086a290300370300200341b80a6a41106a200341980b6a41106a290300370300200341b80a6a41186a200341980b6a41186a290300370300200320032f01003b01900a200320032903980b3703b80a0b200020073a000420002002360230200041073a0000200041056a20032f01900a3b00002000410c6a2004360200200041086a2005360200200041106a20032903b80a370000200041076a200341920a6a2d00003a0000200041186a200341b80a6a41086a290300370000200041206a200341b80a6a41106a290300370000200041286a200341b80a6a41186a2903003700000c150b200141086a21040240024020012802042205450d002004280200210420052005280200220841016a360200410021012008417f4c0d170c010b200341900a6a41026a200441026a2d00003a0000200341b80a6a41086a2001411b6a290000370300200341c50a6a200141206a290000370000200320042f00003b01900a200320012900133703b80a200128000f2104200128000b2105410121010b200341e8096a41026a200341900a6a41026a2d00003a0000200341086a200341b80a6a41086a290300370300200341106a200341b80a6a41106a290300370300200341186a200341b80a6a41186a290300370300200320032f01900a3b01e809200320032903b80a3703000b200020032902980b3702302000200236022c200020013a0000200020032f01e8093b0001200041d8006a200341c00b6a290200370200200041d0006a200341980b6a41206a290200370200200041c8006a200341980b6a41186a290200370200200041c0006a200341980b6a41106a290200370200200041386a200341980b6a41086a290200370200200041036a200341ea096a2d00003a000020002004360208200020053602042000200329030037000c200041146a200341086a2903003700002000411c6a200341106a290300370000200041246a200341186a2903003700000c130b2001200541d4d7c2800010f980808000000b2005200441d4d7c2800010f980808000000b2004200841d4d7c2800010f980808000000b2008200941d4d7c2800010f980808000000b2009200c41d4d7c2800010f980808000000b200c200d41d4d7c2800010f980808000000b200d201041d4d7c2800010f980808000000b2010201141d4d7c2800010f980808000000b2011201441d4d7c2800010f980808000000b2014201541d4d7c2800010f980808000000b2015201841d4d7c2800010f980808000000b2018201941d4d7c2800010f980808000000b2019201c41d4d7c2800010f980808000000b201c201d41d4d7c2800010f980808000000b201d202041d4d7c2800010f980808000000b2020202141d4d7c2800010f980808000000b2021202241d4d7c2800010f980808000000b410441c00410b280808000000b410441c00410b280808000000b200341800c6a2480808080000f0b00000bb50301037f23808080800041d0016b220324808080800002400240024020012d00000d0020002001290001370001200041196a200141196a290000370000200041116a200141116a290000370000200041096a200141096a290000370000410121020c010b2003410c6a2001280204200210f085808000200341f0006a2003410c6a41e00010848e8080001a02400240200241186a2802002201450d0020022001417f6a360218200241146a22012001280200220141016a22044100200228020c220520042005491b6b360200200241106a28020020014102746a2802002201200228020822044f0d03200228020420014107746a220241046a200220022d00004108461b10e385808000200241083a0000200241016a200341ed006a41e30010848e8080001a0c010b0240200228020822012002280200470d0020022001109e86808000200228020821010b200228020420014107746a220141083a0000200141016a200341ed006a41e30010848e8080001a20022002280208220141016a3602080b20002001360204410021020b200020023a0000200341d0016a2480808080000f0b2001200441d4d7c2800010f980808000000bb09e0101617f23808080800041800c6b2203248080808000024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020012802002204417e6a2205410420054106491b0e06050001020304050b200341980b6a200141046a10fa8d808000200141386a21050240024020012802342202450d002005280200210520022002280200220441016a360200410021012004417f4a0d010c1f0b200341026a200541026a2d00003a0000200341c00a6a200141cb006a290000370300200341c50a6a200141d0006a290000370000200320052f00003b0100200320012900433703b80a200128003f2105200128003b2102410121010b200020032902980b370230200020013a0004200041056a20032f01003b0000200041d8006a200341980b6a41286a290200370200200041d0006a200341980b6a41206a290200370200200041c8006a200341980b6a41186a290200370200200041c0006a200341980b6a41106a290200370200200041386a200341980b6a41086a290200370200200041076a200341026a2d00003a00002000410c6a2005360200200041086a2002360200200041053a0000200041106a20032903b80a370200200041186a200341b80a6a41086a290300370200200041206a200341b80a6a41106a290300370200200041286a200341b80a6a41186a2903003702000c1c0b2003200141286a10fa8d8080000240024020012d00040d00200341ba0a6a200141076a2d00003a0000200341980b6a41086a200141146a290200370300200341a80b6a2001411c6a290200370300200341b00b6a200141246a2d00003a00002003200141056a2f00003b01b80a20032001410c6a2902003703980b200141086a2802002101410121020c010b200341b80a6a200141086a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002201450d0020022001417f6a360218200241146a22012001280200220141016a22054100200228020c220420052004491b6b360200200241106a28020020014102746a2802002201200228020822054f0d0b200228020420014107746a220241046a200220022d00004108461b10e385808000200241083a0000200241016a200341980b6a41e30010848e8080001a0c010b0240200228020822012002280200470d0020022001109e86808000200228020821010b200228020420014107746a220141083a0000200141016a200341980b6a41e30010848e8080001a20022002280208220141016a3602080b410021020b20002003290200370228200020023a0004200041056a20032f01b80a3b0000200041086a2001360200200041d0006a200341286a290200370200200041c8006a200341206a290200370200200041c0006a200341186a290200370200200041386a200341106a290200370200200041306a200341086a290200370200200041076a200341ba0a6a2d00003a0000200041063a0000200041246a200341980b6a41186a2802003602002000411c6a200341980b6a41106a290300370200200041146a200341980b6a41086a2903003702002000410c6a20032903980b3702000c1b0b4102210641022107024020012d003022044102460d000240024020040d00200341026a200141336a2d00003a0000200341a00b6a200141c0006a290200370300200341a80b6a200141c8006a290200370300200341b00b6a200141d0006a2d00003a00002003200141316a2f00003b01002003200141386a2902003703980b200141346a2802002105410121070c010b200341b80a6a200141346a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002205450d0020022005417f6a360218200241146a22052005280200220541016a22044100200228020c220820042008491b6b360200200241106a28020020054102746a2802002205200228020822044f0d0c200228020420054107746a220441046a200420042d00004108461b10e385808000200441083a0000200441016a200341980b6a41e30010848e8080001a0c010b0240200228020822052002280200470d0020022005109e86808000200228020821050b200228020420054107746a220541083a0000200541016a200341980b6a41e30010848e8080001a20022002280208220541016a3602080b410021070b200341f0006a41026a200341026a2d00003a0000200341e0056a41086a200341980b6a41086a290300370300200341e0056a41106a200341980b6a41106a290300370300200341e0056a41186a200341980b6a41186a280200360200200320032f01003b0170200320032903980b3703e0050b0240200141d4006a2d000022084102460d000240024020080d00200341026a200141d7006a2d00003a0000200341a00b6a200141e4006a290200370300200341a80b6a200141ec006a290200370300200341b00b6a200141f4006a2d00003a00002003200141d5006a2f00003b01002003200141dc006a2902003703980b200141d8006a2802002104410121060c010b200341b80a6a200141d8006a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002204450d0020022004417f6a360218200241146a22042004280200220441016a22084100200228020c220920082009491b6b360200200241106a28020020044102746a2802002204200228020822084f0d0d200228020420044107746a220841046a200820082d00004108461b10e385808000200841083a0000200841016a200341980b6a41e30010848e8080001a0c010b0240200228020822042002280200470d0020022004109e86808000200228020821040b200228020420044107746a220441083a0000200441016a200341980b6a41e30010848e8080001a20022002280208220441016a3602080b410021060b200341f4006a41026a200341026a2d00003a000020034188066a41086a200341980b6a41086a29030037030020034188066a41106a200341980b6a41106a29030037030020034188066a41186a200341980b6a41186a280200360200200320032f01003b0174200320032903980b370388060b4102210a4102210b0240200141f8006a2d000022094102460d000240024020090d00200341026a200141fb006a2d00003a0000200341a00b6a20014188016a290200370300200341a80b6a20014190016a290200370300200341b00b6a20014198016a2d00003a00002003200141f9006a2f00003b0100200320014180016a2902003703980b200141fc006a28020021084101210b0c010b200341b80a6a200141fc006a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002208450d0020022008417f6a360218200241146a22082008280200220841016a22094100200228020c220c2009200c491b6b360200200241106a28020020084102746a2802002208200228020822094f0d0e200228020420084107746a220941046a200920092d00004108461b10e385808000200941083a0000200941016a200341980b6a41e30010848e8080001a0c010b0240200228020822082002280200470d0020022008109e86808000200228020821080b200228020420084107746a220841083a0000200841016a200341980b6a41e30010848e8080001a20022002280208220841016a3602080b4100210b0b200341f8006a41026a200341026a2d00003a0000200341b0066a41086a200341980b6a41086a290300370300200341b0066a41106a200341980b6a41106a290300370300200341b0066a41186a200341980b6a41186a280200360200200320032f01003b0178200320032903980b3703b0060b02402001419c016a2d0000220c4102460d0002400240200c0d00200341026a2001419f016a2d00003a0000200341a00b6a200141ac016a290200370300200341a80b6a200141b4016a290200370300200341b00b6a200141bc016a2d00003a000020032001419d016a2f00003b01002003200141a4016a2902003703980b200141a0016a28020021094101210a0c010b200341b80a6a200141a0016a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002209450d0020022009417f6a360218200241146a22092009280200220941016a220c4100200228020c220d200c200d491b6b360200200241106a28020020094102746a28020022092002280208220c4f0d0f200228020420094107746a220c41046a200c200c2d00004108461b10e385808000200c41083a0000200c41016a200341980b6a41e30010848e8080001a0c010b0240200228020822092002280200470d0020022009109e86808000200228020821090b200228020420094107746a220941083a0000200941016a200341980b6a41e30010848e8080001a20022002280208220941016a3602080b4100210a0b200341fc006a41026a200341026a2d00003a0000200341d8066a41086a200341980b6a41086a290300370300200341d8066a41106a200341980b6a41106a290300370300200341d8066a41186a200341980b6a41186a280200360200200320032f01003b017c200320032903980b3703d8060b4102210e4102210f0240200141c0016a2d0000220d4102460d0002400240200d0d00200341026a200141c3016a2d00003a0000200341a00b6a200141d0016a290200370300200341a80b6a200141d8016a290200370300200341b00b6a200141e0016a2d00003a00002003200141c1016a2f00003b01002003200141c8016a2902003703980b200141c4016a280200210c4101210f0c010b200341b80a6a200141c4016a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200220c450d002002200c417f6a360218200241146a220c200c280200220c41016a220d4100200228020c2210200d2010491b6b360200200241106a280200200c4102746a280200220c2002280208220d4f0d102002280204200c4107746a220d41046a200d200d2d00004108461b10e385808000200d41083a0000200d41016a200341980b6a41e30010848e8080001a0c010b02402002280208220c2002280200470d002002200c109e868080002002280208210c0b2002280204200c4107746a220c41083a0000200c41016a200341980b6a41e30010848e8080001a20022002280208220c41016a3602080b4100210f0b20034180016a41026a200341026a2d00003a000020034180076a41086a200341980b6a41086a29030037030020034180076a41106a200341980b6a41106a29030037030020034180076a41186a200341980b6a41186a280200360200200320032f01003b018001200320032903980b370380070b0240200141e4016a2d000022104102460d000240024020100d00200341026a200141e7016a2d00003a0000200341a00b6a200141f4016a290200370300200341a80b6a200141fc016a290200370300200341b00b6a20014184026a2d00003a00002003200141e5016a2f00003b01002003200141ec016a2902003703980b200141e8016a280200210d4101210e0c010b200341b80a6a200141e8016a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200220d450d002002200d417f6a360218200241146a220d200d280200220d41016a22104100200228020c221120102011491b6b360200200241106a280200200d4102746a280200220d200228020822104f0d112002280204200d4107746a221041046a201020102d00004108461b10e385808000201041083a0000201041016a200341980b6a41e30010848e8080001a0c010b02402002280208220d2002280200470d002002200d109e868080002002280208210d0b2002280204200d4107746a220d41083a0000200d41016a200341980b6a41e30010848e8080001a20022002280208220d41016a3602080b4100210e0b20034184016a41026a200341026a2d00003a0000200341a8076a41086a200341980b6a41086a290300370300200341a8076a41106a200341980b6a41106a290300370300200341a8076a41186a200341980b6a41186a280200360200200320032f01003b018401200320032903980b3703a8070b4102211241022113024020014188026a2d000022114102460d000240024020110d00200341026a2001418b026a2d00003a0000200341a00b6a20014198026a290200370300200341a80b6a200141a0026a290200370300200341b00b6a200141a8026a2d00003a0000200320014189026a2f00003b0100200320014190026a2902003703980b2001418c026a2802002110410121130c010b200341b80a6a2001418c026a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002210450d0020022010417f6a360218200241146a22102010280200221041016a22114100200228020c221420112014491b6b360200200241106a28020020104102746a2802002210200228020822114f0d12200228020420104107746a221141046a201120112d00004108461b10e385808000201141083a0000201141016a200341980b6a41e30010848e8080001a0c010b0240200228020822102002280200470d0020022010109e86808000200228020821100b200228020420104107746a221041083a0000201041016a200341980b6a41e30010848e8080001a20022002280208221041016a3602080b410021130b20034188016a41026a200341026a2d00003a0000200341d0076a41086a200341980b6a41086a290300370300200341d0076a41106a200341980b6a41106a290300370300200341d0076a41186a200341980b6a41186a280200360200200320032f01003b018801200320032903980b3703d0070b0240200141ac026a2d000022144102460d000240024020140d00200341026a200141af026a2d00003a0000200341a00b6a200141bc026a290200370300200341a80b6a200141c4026a290200370300200341b00b6a200141cc026a2d00003a00002003200141ad026a2f00003b01002003200141b4026a2902003703980b200141b0026a2802002111410121120c010b200341b80a6a200141b0026a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002211450d0020022011417f6a360218200241146a22112011280200221141016a22144100200228020c221520142015491b6b360200200241106a28020020114102746a2802002211200228020822144f0d13200228020420114107746a221441046a201420142d00004108461b10e385808000201441083a0000201441016a200341980b6a41e30010848e8080001a0c010b0240200228020822112002280200470d0020022011109e86808000200228020821110b200228020420114107746a221141083a0000201141016a200341980b6a41e30010848e8080001a20022002280208221141016a3602080b410021120b2003418c016a41026a200341026a2d00003a0000200341f8076a41086a200341980b6a41086a290300370300200341f8076a41106a200341980b6a41106a290300370300200341f8076a41186a200341980b6a41186a280200360200200320032f01003b018c01200320032903980b3703f8070b41022116410221170240200141d0026a2d000022154102460d000240024020150d00200341026a200141d3026a2d00003a0000200341a00b6a200141e0026a290200370300200341a80b6a200141e8026a290200370300200341b00b6a200141f0026a2d00003a00002003200141d1026a2f00003b01002003200141d8026a2902003703980b200141d4026a2802002114410121170c010b200341b80a6a200141d4026a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002214450d0020022014417f6a360218200241146a22142014280200221441016a22154100200228020c221820152018491b6b360200200241106a28020020144102746a2802002214200228020822154f0d14200228020420144107746a221541046a201520152d00004108461b10e385808000201541083a0000201541016a200341980b6a41e30010848e8080001a0c010b0240200228020822142002280200470d0020022014109e86808000200228020821140b200228020420144107746a221441083a0000201441016a200341980b6a41e30010848e8080001a20022002280208221441016a3602080b410021170b20034190016a41026a200341026a2d00003a0000200341a0086a41086a200341980b6a41086a290300370300200341a0086a41106a200341980b6a41106a290300370300200341a0086a41186a200341980b6a41186a280200360200200320032f01003b019001200320032903980b3703a0080b0240200141f4026a2d000022184102460d000240024020180d00200341026a200141f7026a2d00003a0000200341a00b6a20014184036a290200370300200341a80b6a2001418c036a290200370300200341b00b6a20014194036a2d00003a00002003200141f5026a2f00003b01002003200141fc026a2902003703980b200141f8026a2802002115410121160c010b200341b80a6a200141f8026a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002215450d0020022015417f6a360218200241146a22152015280200221541016a22184100200228020c221920182019491b6b360200200241106a28020020154102746a2802002215200228020822184f0d15200228020420154107746a221841046a201820182d00004108461b10e385808000201841083a0000201841016a200341980b6a41e30010848e8080001a0c010b0240200228020822152002280200470d0020022015109e86808000200228020821150b200228020420154107746a221541083a0000201541016a200341980b6a41e30010848e8080001a20022002280208221541016a3602080b410021160b20034194016a41026a200341026a2d00003a0000200341c8086a41086a200341980b6a41086a290300370300200341c8086a41106a200341980b6a41106a290300370300200341c8086a41186a200341980b6a41186a280200360200200320032f01003b019401200320032903980b3703c8080b4102211a4102211b024020014198036a2d000022194102460d000240024020190d00200341026a2001419b036a2d00003a0000200341a00b6a200141a8036a290200370300200341a80b6a200141b0036a290200370300200341b00b6a200141b8036a2d00003a0000200320014199036a2f00003b01002003200141a0036a2902003703980b2001419c036a28020021184101211b0c010b200341b80a6a2001419c036a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002218450d0020022018417f6a360218200241146a22182018280200221841016a22194100200228020c221c2019201c491b6b360200200241106a28020020184102746a2802002218200228020822194f0d16200228020420184107746a221941046a201920192d00004108461b10e385808000201941083a0000201941016a200341980b6a41e30010848e8080001a0c010b0240200228020822182002280200470d0020022018109e86808000200228020821180b200228020420184107746a221841083a0000201841016a200341980b6a41e30010848e8080001a20022002280208221841016a3602080b4100211b0b20034198016a41026a200341026a2d00003a0000200341f0086a41086a200341980b6a41086a290300370300200341f0086a41106a200341980b6a41106a290300370300200341f0086a41186a200341980b6a41186a280200360200200320032f01003b019801200320032903980b3703f0080b0240200141bc036a2d0000221c4102460d0002400240201c0d00200341026a200141bf036a2d00003a0000200341a00b6a200141cc036a290200370300200341a80b6a200141d4036a290200370300200341b00b6a200141dc036a2d00003a00002003200141bd036a2f00003b01002003200141c4036a2902003703980b200141c0036a28020021194101211a0c010b200341b80a6a200141c0036a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002219450d0020022019417f6a360218200241146a22192019280200221941016a221c4100200228020c221d201c201d491b6b360200200241106a28020020194102746a28020022192002280208221c4f0d17200228020420194107746a221c41046a201c201c2d00004108461b10e385808000201c41083a0000201c41016a200341980b6a41e30010848e8080001a0c010b0240200228020822192002280200470d0020022019109e86808000200228020821190b200228020420194107746a221941083a0000201941016a200341980b6a41e30010848e8080001a20022002280208221941016a3602080b4100211a0b2003419c016a41026a200341026a2d00003a000020034198096a41086a200341980b6a41086a29030037030020034198096a41106a200341980b6a41106a29030037030020034198096a41186a200341980b6a41186a280200360200200320032f01003b019c01200320032903980b370398090b4102211e4102211f0240200141e0036a2d0000221d4102460d0002400240201d0d00200341026a200141e3036a2d00003a0000200341a00b6a200141f0036a290200370300200341a80b6a200141f8036a290200370300200341b00b6a20014180046a2d00003a00002003200141e1036a2f00003b01002003200141e8036a2902003703980b200141e4036a280200211c4101211f0c010b200341b80a6a200141e4036a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200221c450d002002201c417f6a360218200241146a221c201c280200221c41016a221d4100200228020c2220201d2020491b6b360200200241106a280200201c4102746a280200221c2002280208221d4f0d182002280204201c4107746a221d41046a201d201d2d00004108461b10e385808000201d41083a0000201d41016a200341980b6a41e30010848e8080001a0c010b02402002280208221c2002280200470d002002201c109e868080002002280208211c0b2002280204201c4107746a221c41083a0000201c41016a200341980b6a41e30010848e8080001a20022002280208221c41016a3602080b4100211f0b200341a0016a41026a200341026a2d00003a0000200341c0096a41086a200341980b6a41086a290300370300200341c0096a41106a200341980b6a41106a290300370300200341c0096a41186a200341980b6a41186a280200360200200320032f01003b01a001200320032903980b3703c0090b024020014184046a2d000022204102460d000240024020200d00200341026a20014187046a2d00003a0000200341a00b6a20014194046a290200370300200341a80b6a2001419c046a290200370300200341b00b6a200141a4046a2d00003a0000200320014185046a2f00003b010020032001418c046a2902003703980b20014188046a280200211d4101211e0c010b200341b80a6a20014188046a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200221d450d002002201d417f6a360218200241146a221d201d280200221d41016a22204100200228020c222120202021491b6b360200200241106a280200201d4102746a280200221d200228020822204f0d192002280204201d4107746a222041046a202020202d00004108461b10e385808000202041083a0000202041016a200341980b6a41e30010848e8080001a0c010b02402002280208221d2002280200470d002002201d109e868080002002280208211d0b2002280204201d4107746a221d41083a0000201d41016a200341980b6a41e30010848e8080001a20022002280208221d41016a3602080b4100211e0b200341a4016a41026a200341026a2d00003a0000200341e8096a41086a200341980b6a41086a290300370300200341e8096a41106a200341980b6a41106a290300370300200341e8096a41186a200341980b6a41186a280200360200200320032f01003b01a401200320032903980b3703e8090b41022122410221230240200141a8046a2d000022214102460d000240024020210d00200341026a200141ab046a2d00003a0000200341a00b6a200141b8046a290200370300200341a80b6a200141c0046a290200370300200341b00b6a200141c8046a2d00003a00002003200141a9046a2f00003b01002003200141b0046a2902003703980b200141ac046a2802002120410121230c010b200341b80a6a200141ac046a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002220450d0020022020417f6a360218200241146a22202020280200222041016a22214100200228020c222320212023491b6b360200200241106a28020020204102746a2802002220200228020822214f0d1a200228020420204107746a222141046a202120212d00004108461b10e385808000202141083a0000202141016a200341980b6a41e30010848e8080001a0c010b0240200228020822202002280200470d0020022020109e86808000200228020821200b200228020420204107746a222041083a0000202041016a200341980b6a41e30010848e8080001a20022002280208222041016a3602080b410021230b200341a8016a41026a200341026a2d00003a0000200341900a6a41086a200341980b6a41086a290300370300200341900a6a41106a200341980b6a41106a290300370300200341900a6a41186a200341980b6a41186a280200360200200320032f01003b01a801200320032903980b3703900a0b0240200141cc046a2d000022244102460d000240024020240d00200341ba056a200141cf046a2d00003a0000200341a00b6a200141dc046a290200370300200341a80b6a200141e4046a290200370300200341b00b6a200141ec046a2d00003a00002003200141cd046a2f00003b01b8052003200141d4046a2902003703980b200141d0046a2802002121410121220c010b200341b80a6a200141d0046a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002221450d0020022021417f6a360218200241146a22212021280200222141016a22224100200228020c222420222024491b6b360200200241106a28020020214102746a2802002221200228020822224f0d1b200228020420214107746a220241046a200220022d00004108461b10e385808000200241083a0000200241016a200341980b6a41e30010848e8080001a0c010b0240200228020822212002280200470d0020022021109e86808000200228020821210b200228020420214107746a222141083a0000202141016a200341980b6a41e30010848e8080001a20022002280208222141016a3602080b410021220b200341ac016a41026a200341b8056a41026a2d00003a0000200341086a200341980b6a41086a290300370300200341106a200341980b6a41106a290300370300200341186a200341980b6a41186a280200360200200320032f01b8053b01ac01200320032903980b3703000b200341ec006a41026a2224200341f0006a41026a2d00003a0000200341980b6a41086a2225200341e0056a41086a290300370300200341980b6a41106a2226200341e0056a41106a290300370300200341980b6a41186a2227200341e0056a41186a280200360200200341e8006a41026a2228200341f4006a41026a2d00003a0000200341b80a6a41186a222920034188066a41186a280200360200200341b80a6a41106a222a20034188066a41106a290300370300200341b80a6a41086a222b20034188066a41086a290300370300200320032f01703b016c200320032903e0053703980b200320032f01743b016820032003290388063703b80a200341e4006a41026a222c200341f8006a41026a2d00003a0000200341b8056a41086a222d200341b0066a41086a290300370300200341b8056a41106a222e200341b0066a41106a290300370300200341b8056a41186a222f200341b0066a41186a280200360200200341e0006a41026a2230200341fc006a41026a2d00003a000020034190056a41086a2231200341d8066a41086a29030037030020034190056a41106a2232200341d8066a41106a29030037030020034190056a41186a2233200341d8066a41186a280200360200200320032f01783b0164200320032903b0063703b805200320032f017c3b0160200320032903d80637039005200341dc006a41026a223420034180016a41026a2d00003a0000200341e8046a41186a223520034180076a41186a280200360200200341e8046a41106a223620034180076a41106a290300370300200341e8046a41086a223720034180076a41086a290300370300200341d8006a41026a223820034184016a41026a2d00003a0000200341c0046a41186a2239200341a8076a41186a280200360200200341c0046a41106a223a200341a8076a41106a290300370300200341c0046a41086a223b200341a8076a41086a290300370300200320032f0180013b015c20032003290380073703e804200320032f0184013b0158200320032903a8073703c004200341d4006a41026a223c20034188016a41026a2d00003a0000200320032f0188013b015420034198046a41186a223d200341d0076a41186a28020036020020034198046a41106a223e200341d0076a41106a29030037030020034198046a41086a223f200341d0076a41086a290300370300200320032903d00737039804200341d0006a41026a22402003418c016a41026a2d00003a0000200320032f018c013b0150200341f0036a41186a2241200341f8076a41186a280200360200200341f0036a41106a2242200341f8076a41106a290300370300200341f0036a41086a2243200341f8076a41086a290300370300200320032903f8073703f003200341cc006a41026a224420034190016a41026a2d00003a0000200320032f0190013b014c200341c8036a41186a2245200341a0086a41186a280200360200200341c8036a41106a2246200341a0086a41106a290300370300200341c8036a41086a2247200341a0086a41086a290300370300200320032903a0083703c803200341c8006a41026a224820034194016a41026a2d00003a0000200320032f0194013b0148200341a0036a41186a2249200341c8086a41186a280200360200200341a0036a41106a224a200341c8086a41106a290300370300200341a0036a41086a224b200341c8086a41086a290300370300200320032903c8083703a003200341c4006a41026a224c20034198016a41026a2d00003a0000200320032f0198013b0144200341f8026a41186a224d200341f0086a41186a280200360200200341f8026a41106a224e200341f0086a41106a290300370300200341f8026a41086a224f200341f0086a41086a290300370300200320032903f0083703f802200341c0006a41026a22502003419c016a41026a2d00003a0000200320032f019c013b0140200341d0026a41186a225120034198096a41186a280200360200200341d0026a41106a225220034198096a41106a290300370300200341d0026a41086a225320034198096a41086a29030037030020032003290398093703d0022003413c6a41026a2254200341a0016a41026a2d00003a0000200320032f01a0013b013c200341a8026a41186a2255200341c0096a41186a280200360200200341a8026a41106a2256200341c0096a41106a290300370300200341a8026a41086a2257200341c0096a41086a290300370300200320032903c0093703a802200341386a41026a2258200341a4016a41026a2d00003a0000200320032f01a4013b013820034180026a41186a2259200341e8096a41186a28020036020020034180026a41106a225a200341e8096a41106a29030037030020034180026a41086a225b200341e8096a41086a290300370300200320032903e80937038002200341346a41026a225c200341a8016a41026a2d00003a0000200320032f01a8013b0134200341d8016a41186a225d200341900a6a41186a280200360200200341d8016a41106a225e200341900a6a41106a290300370300200341d8016a41086a225f200341900a6a41086a290300370300200320032903900a3703d801200341306a41026a2260200341ac016a41026a2d00003a0000200320032f01ac013b0130200341b0016a41186a2261200341186a280200360200200341b0016a41106a2262200341106a290300370300200341b0016a41086a2263200341086a290300370300200320032903003703b00141002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d18200220073a0000200220032f016c3b000120022005360204200220032903980b370008200220063a0024200220032f01683b002541032107200241036a20242d00003a0000200241106a2025290300370000200241186a2026290300370000200241206a2027280200360000200241276a20282d00003a0000200220043602282002200b3a00482002200836024c200220032903b80a37002c200241346a202b2903003700002002413c6a202a290300370000200241c4006a2029280200360000200220032f01643b0049200241cb006a202c2d00003a0000200220032903b805370050200241d8006a202d290300370000200241e0006a202e290300370000200241e8006a202f2802003600002002200a3a006c200220093602702002200f3a009001200220032f01603b006d200241ef006a20302d00003a00002002418c016a203328020036000020024184016a2032290300370000200241fc006a20312903003700002002200329039005370074200220032f015c3b00910120024193016a20342d00003a00002002200c36029401200241b0016a2035280200360000200241a8016a2036290300370000200241a0016a2037290300370000200220032903e804370098012002200e3a00b401200241b7016a20382d00003a0000200220032f01583b00b5012002200d3602b801200241d4016a2039280200360000200241cc016a203a290300370000200241c4016a203b290300370000200220032903c0043700bc01200220133a00d801200241db016a203c2d00003a0000200220032f01543b00d901200220103602dc01200241f8016a203d280200360000200241f0016a203e290300370000200241e8016a203f29030037000020022003290398043700e001200220123a00fc01200241ff016a20402d00003a0000200220032f01503b00fd0120022011360280022002419c026a204128020036000020024194026a20422903003700002002418c026a2043290300370000200220032903f00337008402200220173a00a002200241a3026a20442d00003a0000200220032f014c3b00a102200220143602a402200241c0026a2045280200360000200241b8026a2046290300370000200241b0026a2047290300370000200220032903c8033700a802200220163a00c402200241c7026a20482d00003a0000200220032f01483b00c502200220153602c802200241e4026a2049280200360000200241dc026a204a290300370000200241d4026a204b290300370000200220032903a0033700cc022002201b3a00e802200241eb026a204c2d00003a0000200220032f01443b00e902200220183602ec0220024188036a204d28020036000020024180036a204e290300370000200241f8026a204f290300370000200220032903f8023700f0022002201a3a008c032002418f036a20502d00003a0000200220032f01403b008d032002201936029003200241ac036a2051280200360000200241a4036a20522903003700002002419c036a2053290300370000200220032903d002370094032002201f3a00b003200241b3036a20542d00003a0000200220032f013c3b00b1032002201c3602b403200241d0036a2055280200360000200241c8036a2056290300370000200241c0036a2057290300370000200220032903a8023700b8032002201e3a00d403200241d7036a20582d00003a0000200220032f01383b00d5032002201d3602d803200241f4036a2059280200360000200241ec036a205a290300370000200241e4036a205b29030037000020022003290380023700dc03200220233a00f803200241fb036a205c2d00003a0000200220032f01343b00f903200220203602fc0320024198046a205d28020036000020024190046a205e29030037000020024188046a205f290300370000200220032903d80137008004200220223a009c042002419f046a20602d00003a0000200220032f01303b009d04200220213602a004200241bc046a2061280200360000200241b4046a2062290300370000200241ac046a2063290300370000200220032903b0013700a40420012802040d030c040b4102210541022108024020012d002c4102460d00200341980b6a2001412c6a200210f38580800020034190066a200341a10b6a29000037030020034198066a200341a90b6a290000370300200341a0066a200341b10b6a290000370300200341a7066a200341b80b6a280000360000200320032900990b3703880620032d00980b21080b0240200141d0006a22092d00004102460d00200341980b6a2009200210f385808000200341b8066a200341a10b6a290000370300200341c0066a200341a90b6a290000370300200341c8066a200341b10b6a290000370300200341cf066a200341b80b6a280000360000200320032900990b3703b00620032d00980b21050b410221094102210c0240200141f4006a220d2d00004102460d00200341980b6a200d200210f385808000200341e0066a200341a10b6a290000370300200341e8066a200341a90b6a290000370300200341f0066a200341b10b6a290000370300200341f7066a200341b80b6a280000360000200320032900990b3703d80620032d00980b210c0b024020014198016a220d2d00004102460d00200341980b6a200d200210f38580800020034188076a200341a10b6a29000037030020034190076a200341a90b6a29000037030020034198076a200341b10b6a2900003703002003419f076a200341b80b6a280000360000200320032900990b3703800720032d00980b21090b4102210d410221100240200141bc016a22112d00004102460d00200341980b6a2011200210f385808000200341b0076a200341a10b6a290000370300200341b8076a200341a90b6a290000370300200341c0076a200341b10b6a290000370300200341c7076a200341b80b6a280000360000200320032900990b3703a80720032d00980b21100b0240200141e0016a22112d00004102460d00200341980b6a2011200210f385808000200341d8076a200341a10b6a290000370300200341e0076a200341a90b6a290000370300200341e8076a200341b10b6a290000370300200341ef076a200341b80b6a280000360000200320032900990b3703d00720032d00980b210d0b4102211141022114024020014184026a22152d00004102460d00200341980b6a2015200210f38580800020034180086a200341a10b6a29000037030020034188086a200341a90b6a29000037030020034190086a200341b10b6a29000037030020034197086a200341b80b6a280000360000200320032900990b3703f80720032d00980b21140b0240200141a8026a22152d00004102460d00200341980b6a2015200210f385808000200341a8086a200341a10b6a290000370300200341b0086a200341a90b6a290000370300200341b8086a200341b10b6a290000370300200341bf086a200341b80b6a280000360000200320032900990b3703a00820032d00980b21110b41022115410221180240200141cc026a22192d00004102460d00200341980b6a2019200210f385808000200341d0086a200341a10b6a290000370300200341d8086a200341a90b6a290000370300200341e0086a200341b10b6a290000370300200341e7086a200341b80b6a280000360000200320032900990b3703c80820032d00980b21180b0240200141f0026a22192d00004102460d00200341980b6a2019200210f385808000200341f8086a200341a10b6a29000037030020034180096a200341a90b6a29000037030020034188096a200341b10b6a2900003703002003418f096a200341b80b6a280000360000200320032900990b3703f00820032d00980b21150b410221194102211c024020014194036a221d2d00004102460d00200341980b6a201d200210f385808000200341a0096a200341a10b6a290000370300200341a8096a200341a90b6a290000370300200341b0096a200341b10b6a290000370300200341b7096a200341b80b6a280000360000200320032900990b3703980920032d00980b211c0b0240200141b8036a221d2d00004102460d00200341980b6a201d200210f385808000200341c8096a200341a10b6a290000370300200341d0096a200341a90b6a290000370300200341d8096a200341b10b6a290000370300200341df096a200341b80b6a280000360000200320032900990b3703c00920032d00980b21190b4102211d410221200240200141dc036a22212d00004102460d00200341980b6a2021200210f385808000200341f0096a200341a10b6a290000370300200341f8096a200341a90b6a290000370300200341800a6a200341b10b6a290000370300200341870a6a200341b80b6a280000360000200320032900990b3703e80920032d00980b21200b024020014180046a22212d00004102460d00200341980b6a2021200210f385808000200341980a6a200341a10b6a290000370300200341a00a6a200341a90b6a290000370300200341a80a6a200341b10b6a290000370300200341af0a6a200341b80b6a280000360000200320032900990b3703900a20032d00980b211d0b41022121410221070240200141a4046a22062d00004102460d00200341980b6a2006200210f385808000200341086a200341a10b6a290000370300200341106a200341a90b6a290000370300200341186a200341b10b6a2900003703002003411f6a200341b80b6a280000360000200320032900990b37030020032d00980b21070b0240200141c8046a22062d00004102460d00200341980b6a2006200210f385808000200341c00a6a200341a10b6a290000370300200341c80a6a200341a90b6a290000370300200341d00a6a200341b10b6a290000370300200341d70a6a200341b80b6a280000360000200320032900990b3703b80a20032d00980b21210b200341980b6a411f6a220620034188066a411f6a280000360000200341980b6a41186a220b20034188066a41186a290300370300200341980b6a41106a220a20034188066a41106a290300370300200341980b6a41086a220f20034188066a41086a290300370300200341e0056a41086a220e200341b0066a41086a290300370300200341e0056a41106a2213200341b0066a41106a290300370300200341e0056a41186a2212200341b0066a41186a290300370300200341e0056a411f6a2217200341b0066a411f6a28000036000020032003290388063703980b200320032903b0063703e005200341b8056a411f6a2216200341d8066a411f6a280000360000200341b8056a41186a221b200341d8066a41186a290300370300200341b8056a41106a221a200341d8066a41106a290300370300200341b8056a41086a221f200341d8066a41086a29030037030020034190056a41086a221e20034180076a41086a29030037030020034190056a41106a222320034180076a41106a29030037030020034190056a41186a222220034180076a41186a29030037030020034190056a411f6a222420034180076a411f6a280000360000200320032903d8063703b805200320032903800737039005200341e8046a411f6a2225200341a8076a411f6a280000360000200341e8046a41186a2226200341a8076a41186a290300370300200341e8046a41106a2227200341a8076a41106a290300370300200341e8046a41086a2228200341a8076a41086a290300370300200341c0046a411f6a2229200341d0076a411f6a280000360000200341c0046a41186a222a200341d0076a41186a290300370300200341c0046a41106a222b200341d0076a41106a290300370300200341c0046a41086a222c200341d0076a41086a290300370300200320032903a8073703e804200320032903d0073703c00420034198046a411f6a222d200341f8076a411f6a28000036000020034198046a41186a222e200341f8076a41186a29030037030020034198046a41106a222f200341f8076a41106a29030037030020034198046a41086a2230200341f8076a41086a290300370300200320032903f80737039804200341f0036a411f6a2231200341a0086a411f6a280000360000200341f0036a41186a2232200341a0086a41186a290300370300200341f0036a41106a2233200341a0086a41106a290300370300200341f0036a41086a2234200341a0086a41086a290300370300200320032903a0083703f003200341c8036a411f6a2235200341c8086a411f6a280000360000200341c8036a41186a2236200341c8086a41186a290300370300200341c8036a41106a2237200341c8086a41106a290300370300200341c8036a41086a2238200341c8086a41086a290300370300200320032903c8083703c803200341a0036a411f6a2239200341f0086a411f6a280000360000200341a0036a41186a223a200341f0086a41186a290300370300200341a0036a41106a223b200341f0086a41106a290300370300200341a0036a41086a223c200341f0086a41086a290300370300200320032903f0083703a003200341f8026a411f6a223d20034198096a411f6a280000360000200341f8026a41186a223e20034198096a41186a290300370300200341f8026a41106a223f20034198096a41106a290300370300200341f8026a41086a224020034198096a41086a29030037030020032003290398093703f802200341d0026a411f6a2241200341c0096a411f6a280000360000200341d0026a41186a2242200341c0096a41186a290300370300200341d0026a41106a2243200341c0096a41106a290300370300200341d0026a41086a2244200341c0096a41086a290300370300200320032903c0093703d002200341a8026a411f6a2245200341e8096a411f6a280000360000200341a8026a41186a2246200341e8096a41186a290300370300200341a8026a41106a2247200341e8096a41106a290300370300200341a8026a41086a2248200341e8096a41086a290300370300200320032903e8093703a80220034180026a411f6a2249200341900a6a411f6a28000036000020034180026a41186a224a200341900a6a41186a29030037030020034180026a41106a224b200341900a6a41106a29030037030020034180026a41086a224c200341900a6a41086a290300370300200320032903900a37038002200341d8016a411f6a224d2003411f6a280000360000200341d8016a41186a224e200341186a290300370300200341d8016a41106a224f200341106a290300370300200341d8016a41086a2250200341086a290300370300200320032903003703d801200341b0016a411f6a2251200341b80a6a411f6a280000360000200341b0016a41186a2252200341b80a6a41186a290300370300200341b0016a41106a2253200341b80a6a41106a290300370300200341b0016a41086a2254200341b80a6a41086a290300370300200320032903b80a3703b00141002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d18200220083a0000200220032903980b370001200220053a0024200220032903e005370025200241096a200f290300370000200241116a200a290300370000200241196a200b290300370000200241206a20062800003600002002412d6a200e290300370000200241356a20132903003700002002413d6a2012290300370000200241c4006a20172800003600002002200c3a0048200220093a006c200220032903b805370049200241d1006a201f290300370000200241d9006a201a290300370000200241e1006a201b290300370000200241e8006a2016280000360000200220032903900537006d200241f5006a201e290300370000200241fd006a202329030037000020024185016a20222903003700002002418c016a2024280000360000200220103a0090012002200d3a00b401200220032903e8043700910120024199016a2028290300370000200241a1016a2027290300370000200241a9016a2026290300370000200241b0016a2025280000360000200220032903c0043700b501200241bd016a202c290300370000200241c5016a202b290300370000200241cd016a202a290300370000200241d4016a2029280000360000200220143a00d801200241f8016a202d280000360000200241f1016a202e290300370000200241e9016a202f290300370000200241e1016a203029030037000020022003290398043700d901200220113a00fc012002419c026a203128000036000020024195026a20322903003700002002418d026a203329030037000020024185026a2034290300370000200220032903f0033700fd01200220183a00a002200241c0026a2035280000360000200241b9026a2036290300370000200241b1026a2037290300370000200241a9026a2038290300370000200220032903c8033700a102200220153a00c402200241e4026a2039280000360000200241dd026a203a290300370000200241d5026a203b290300370000200241cd026a203c290300370000200220032903a0033700c5022002201c3a00e80220024188036a203d28000036000020024181036a203e290300370000200241f9026a203f290300370000200241f1026a2040290300370000200220032903f8023700e902200220193a008c03200241ac036a2041280000360000200241a5036a20422903003700002002419d036a204329030037000020024195036a2044290300370000200220032903d00237008d03200220203a00b003200241d0036a2045280000360000200241c9036a2046290300370000200241c1036a2047290300370000200241b9036a2048290300370000200220032903a8023700b1032002201d3a00d403200241f4036a2049280000360000200241ed036a204a290300370000200241e5036a204b290300370000200241dd036a204c29030037000020022003290380023700d503200220073a00f80320024198046a204d28000036000020024191046a204e29030037000020024189046a204f29030037000020024181046a2050290300370000200220032903d8013700f903200220213a009c04200241bc046a2051280000360000200241b5046a2052290300370000200241ad046a2053290300370000200241a5046a2054290300370000200220032903b00137009d04200341980b6a200141ec046a10fa8d80800020040d04410321010c050b200341a40b6a42013702002003410136029c0b200341fcd1c280003602980b200341e0818080003602bc0a200341c4d2c280003602b80a2003200341b80a6a3602a00b200341980b6a41ccd2c2800010f680808000000b200041043a00000c170b2001410c6a210402400240200141086a2802002205450d002004280200210420052005280200220141016a360200410021072001417f4c0d190c010b200341026a200441026a2d00003a0000200341980b6a41086a2001411f6a290000370300200341a50b6a200141246a290000370000200320042f00003b0100200320012900173703980b20012800132104200128000f2105410121070b200341900a6a41026a200341026a2d00003a0000200341b80a6a41086a200341980b6a41086a290300370300200341b80a6a41106a200341980b6a41106a290300370300200341b80a6a41186a200341980b6a41186a290300370300200320032f01003b01900a200320032903980b3703b80a0b200020073a000420002002360230200041073a0000200041056a20032f01900a3b00002000410c6a2004360200200041086a2005360200200041106a20032903b80a370000200041076a200341920a6a2d00003a0000200041186a200341b80a6a41086a290300370000200041206a200341b80a6a41106a290300370000200041286a200341b80a6a41186a2903003700000c150b200141086a21040240024020012802042205450d002004280200210420052005280200220841016a360200410021012008417f4c0d170c010b200341900a6a41026a200441026a2d00003a0000200341b80a6a41086a2001411b6a290000370300200341c50a6a200141206a290000370000200320042f00003b01900a200320012900133703b80a200128000f2104200128000b2105410121010b200341e8096a41026a200341900a6a41026a2d00003a0000200341086a200341b80a6a41086a290300370300200341106a200341b80a6a41106a290300370300200341186a200341b80a6a41186a290300370300200320032f01900a3b01e809200320032903b80a3703000b200020032902980b3702302000200236022c200020013a0000200020032f01e8093b0001200041d8006a200341c00b6a290200370200200041d0006a200341980b6a41206a290200370200200041c8006a200341980b6a41186a290200370200200041c0006a200341980b6a41106a290200370200200041386a200341980b6a41086a290200370200200041036a200341ea096a2d00003a000020002004360208200020053602042000200329030037000c200041146a200341086a2903003700002000411c6a200341106a290300370000200041246a200341186a2903003700000c130b2001200541d4d7c2800010f980808000000b2005200441d4d7c2800010f980808000000b2004200841d4d7c2800010f980808000000b2008200941d4d7c2800010f980808000000b2009200c41d4d7c2800010f980808000000b200c200d41d4d7c2800010f980808000000b200d201041d4d7c2800010f980808000000b2010201141d4d7c2800010f980808000000b2011201441d4d7c2800010f980808000000b2014201541d4d7c2800010f980808000000b2015201841d4d7c2800010f980808000000b2018201941d4d7c2800010f980808000000b2019201c41d4d7c2800010f980808000000b201c201d41d4d7c2800010f980808000000b201d202041d4d7c2800010f980808000000b2020202141d4d7c2800010f980808000000b2021202241d4d7c2800010f980808000000b410441c00410b280808000000b410441c00410b280808000000b200341800c6a2480808080000f0b00000bb50301037f23808080800041d0016b220324808080800002400240024020012d00000d0020002001290001370001200041196a200141196a290000370000200041116a200141116a290000370000200041096a200141096a290000370000410121020c010b2003410c6a2001280204200210f285808000200341f0006a2003410c6a41e00010848e8080001a02400240200241186a2802002201450d0020022001417f6a360218200241146a22012001280200220141016a22044100200228020c220520042005491b6b360200200241106a28020020014102746a2802002201200228020822044f0d03200228020420014107746a220241046a200220022d00004108461b10e385808000200241083a0000200241016a200341ed006a41e30010848e8080001a0c010b0240200228020822012002280200470d0020022001109e86808000200228020821010b200228020420014107746a220141083a0000200141016a200341ed006a41e30010848e8080001a20022002280208220141016a3602080b20002001360204410021020b200020023a0000200341d0016a2480808080000f0b2001200441d4d7c2800010f980808000000bb60301017f2380808080004180016b22042480808080000240024002400240024020012d00000e03010200020b200441003a005420042002360250200441023a002c2004200141286a2802003602342004200128022441086a360230200441046a20032004412c6a20024100200410da8580800020042d00040d03200441f0006a22022004411d6a290000370300200441e8006a2203200441156a290000370300200441d8006a41086a2004410d6a29000037030020042004290005370358024020012d00010d00200141013a0001200141026a20042903583700002001411a6a2002290300370000200141126a20032903003700002001410a6a200441e0006a2903003700000b200141026a210241202101410121030c020b200128020441086a2102200141086a2802002101410021030c010b41012103200141016a2102412021010b20002001360208200020023602042000200336020020044180016a2480808080000f0b200441386a420137020020044101360230200441fcd1c2800036022c200441e08180800036027c20044180d3c280003602782004200441f8006a3602342004412c6a4188d3c2800010f680808000000bb80301017f2380808080004180016b22052480808080000240024002400240024020012d00000e03010200020b200541003a005420052002360250200541023a002c2005200141286a2802003602342005200128022441086a360230200541046a200320042005412c6a20024100200510db8580800020052d00040d03200541f0006a22022005411d6a290000370300200541e8006a2204200541156a290000370300200541d8006a41086a2005410d6a29000037030020052005290005370358024020012d00010d00200141013a0001200141026a20052903583700002001411a6a2002290300370000200141126a20042903003700002001410a6a200541e0006a2903003700000b200141026a210241202101410121040c020b200128020441086a2102200141086a2802002101410021040c010b41012104200141016a2102412021010b20002001360208200020023602042000200436020020054180016a2480808080000f0b200541386a420137020020054101360230200541fcd1c2800036022c200541e08180800036027c20054180d3c280003602782005200541f8006a3602342005412c6a4188d3c2800010f680808000000bb80301017f2380808080004180016b22052480808080000240024002400240024020012d00000e03010200020b200541003a005420052002360250200541023a002c2005200141286a2802003602342005200128022441086a360230200541046a200320042005412c6a20024100200510d98580800020052d00040d03200541f0006a22022005411d6a290000370300200541e8006a2204200541156a290000370300200541d8006a41086a2005410d6a29000037030020052005290005370358024020012d00010d00200141013a0001200141026a20052903583700002001411a6a2002290300370000200141126a20042903003700002001410a6a200541e0006a2903003700000b200141026a210241202101410121040c020b200128020441086a2102200141086a2802002101410021040c010b41012104200141016a2102412021010b20002001360208200020023602042000200436020020054180016a2480808080000f0b200541386a420137020020054101360230200541fcd1c2800036022c200541e08180800036027c20054180d3c280003602782005200541f8006a3602342005412c6a4188d3c2800010f680808000000bb60301017f2380808080004180016b22042480808080000240024002400240024020012d00000e03010200020b200441003a005420042002360250200441023a002c2004200141286a2802003602342004200128022441086a360230200441046a20032004412c6a20024100200410dc8580800020042d00040d03200441f0006a22022004411d6a290000370300200441e8006a2203200441156a290000370300200441d8006a41086a2004410d6a29000037030020042004290005370358024020012d00010d00200141013a0001200141026a20042903583700002001411a6a2002290300370000200141126a20032903003700002001410a6a200441e0006a2903003700000b200141026a210241202101410121030c020b200128020441086a2102200141086a2802002101410021030c010b41012103200141016a2102412021010b20002001360208200020023602042000200336020020044180016a2480808080000f0b200441386a420137020020044101360230200441fcd1c2800036022c200441e08180800036027c20044180d3c280003602782004200441f8006a3602342004412c6a4188d3c2800010f680808000000be31303057f047e077f23808080800041d0016b220524808080800020052003360218200520023602140240024002400240200028026822060d00410021000c010b200041ec006a280200220728021421002005200541146a360258200541086a20062001200541d8006a4198d3c28000200011878080800000200528020c210802402005280208450d000240024002400240200828020041fcffffff076a2200410320004105491b0e0403030102000b2008280204450d02200841086a28020041002802c0a3c68000118080808000000c020b2008280204450d01200841086a28020041002802c0a3c68000118080808000000c010b200810de858080000b200841002802c0a3c68000118080808000002004280200450d030c020b20054280808080c00037021c20054100360224024020042802002209450d00200541286a41286a2004412c6a290200370300200541286a41206a200441246a290200370300200541286a41186a2004411c6a290200370300200541286a41106a200441146a290200370300200541286a41086a2004410c6a29020037030020052004290204370328024002400240024002400240024020082802002203417e6a2200410420004106491b0e06060206000103060b2008280204450d05200841086a2203280200450d05200841106a21000c040b2003450d042008280204450d04200841046a21032008410c6a21000c030b20082802340d010c030b2008410c6a2100200841046a21030c010b200841346a21032008413c6a21000b20054190016a41186a200041186a290000220a37030020054190016a41106a200041106a290000220b37030020054190016a41086a200041086a290000220c37030020052000290000220d37039001200541d8006a41186a200a370300200541d8006a41106a200b370300200541d8006a41086a200c3703002005200d370358200528022821014101210002400240024002400240200528022c20052802502202200241284b22021b220e450d00200e417f4c0d0141002d00fca3c680001a200e41002802c8a3c68000118180808000002200450d020b20002001200541286a20021b200e10848e808000210f200341046a2802002101200328020022022002280200220041016a3602002000417f4c0d022005200136029401200520023602900103402002280204210003402000417f460d012000417f4c0d052002200041016a2002280204220320032000461b360204200320004721012003210020010d000b0b2005280294012103200528029001220020002802002200417f6a360200024020004101470d0020054190016a10f18d8080000b024020052802242200200528021c470d002005411c6a2000109f86808000200528022421000b2005280220200041386c6a220041023a000c2000200e3602082000200f3602042000200e3602002000200529035837000d2000200336023420002002360230200041156a200541e0006a2903003700002000411d6a200541e8006a290300370000200041256a200541f0006a2903003700002005200528022441016a3602240c040b10ae80808000000b4101200e10b280808000000b00000b10f08d808000000b20052802282103200528022c2102200528025021004100211020054190016a41286a2201410036020020054190016a2003200541286a200041284b220e1b2203200320022000200e1b6a10f686808000200541d8006a41286a2001280200360200200541d8006a41206a20054190016a41206a290200370300200541d8006a41186a20054190016a41186a290200370300200541d8006a41106a20054190016a41106a290200370300200541d8006a41086a20054190016a41086a290200370300200520052902900137035820052005280254360284012008280200210320052005411c6a3602c00141002111024002400240024002402003417e6a2203410420034106491b0e06040403000104040b200841306a21120c010b2008412c6a21120b41022111410021000c010b200841046a2100410121110b2012415c6a21132005200541c0016a3602c8012005200541d8006a3602c40103400240024002400240024020110e03010200010b20004110200041104b1b210e2012200041246c22026a2103201320026a2102200041087441807e6a21010340200e2000460d03200241246a210220014180026a2101200041016a210020032d00002108200341246a210320084102460d000b20014180fe0371410172210f200221140c030b200f41807e71410272210f0c020b200f41807e71210f0240201041ff01710d0041012110200021140c020b200f410272210f0c010b200f41807e71410272210f200e21000b0240200f41ff01714102460d0020142d0000450d01201428020421032005200541c4016a3602cc01200fad220a42ff01834202510d0120052003ad422086200a42ffff03838437039001200541cc016a20054190016a10bc8c8080000c010b0b02402005280280014129490d00200528025841002802c0a3c68000118080808000000b024020052802504129490d00200528022841002802c0a3c68000118080808000000b2005280220220f2005280224220341386c6a2108200528021c2114200f2100024002402003450d00200541d8006a41086a210320054188016a210e200f21000240034020002802002202418080808078460d01200028020421012000290204210a200e200041346a280200360200200541d8006a41286a2000412c6a290200370300200541d8006a41206a200041246a290200370300200541d8006a41186a2000411c6a290200370300200541d8006a41106a200041146a29020037030020032000410c6a29020037030020052000290204370358200541286a41286a200341286a280200360200200541286a41206a200341206a290200370300200541286a41186a200341186a290200370300200541286a41106a200341106a290200370300200541286a41086a200341086a290200370300200520032902003703282006200aa7200a422088a7200541286a20072802101186808080000002402002450d00200141002802c0a3c68000118080808000000b200041386a22002008470d000c030b0b200041386a21000b200820006b41386e210320082000460d00034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a2d00004102490d00200041306a2802002202417f460d00200220022802042201417f6a36020420014101470d00200041346a280200410b6a4104490d00200241002802c0a3c68000118080808000000b200041386a21002003417f6a22030d000b0b2014450d00200f41002802c0a3c68000118080808000000b200941004721000b20000d012004280200450d010b2004412c6a2802004129490d00200428020441002802c0a3c68000118080808000000b200541d0016a2480808080000be31303057f047e077f23808080800041d0016b220524808080800020052003360218200520023602140240024002400240200028026822060d00410021000c010b200041ec006a280200220728021421002005200541146a360258200541086a20062001200541d8006a41acd3c28000200011878080800000200528020c210802402005280208450d000240024002400240200828020041fcffffff076a2200410320004105491b0e0403030102000b2008280204450d02200841086a28020041002802c0a3c68000118080808000000c020b2008280204450d01200841086a28020041002802c0a3c68000118080808000000c010b200810de858080000b200841002802c0a3c68000118080808000002004280200450d030c020b20054280808080c00037021c20054100360224024020042802002209450d00200541286a41286a2004412c6a290200370300200541286a41206a200441246a290200370300200541286a41186a2004411c6a290200370300200541286a41106a200441146a290200370300200541286a41086a2004410c6a29020037030020052004290204370328024002400240024002400240024020082802002203417e6a2200410420004106491b0e06060206000103060b2008280204450d05200841086a2203280200450d05200841106a21000c040b2003450d042008280204450d04200841046a21032008410c6a21000c030b20082802340d010c030b2008410c6a2100200841046a21030c010b200841346a21032008413c6a21000b20054190016a41186a200041186a290000220a37030020054190016a41106a200041106a290000220b37030020054190016a41086a200041086a290000220c37030020052000290000220d37039001200541d8006a41186a200a370300200541d8006a41106a200b370300200541d8006a41086a200c3703002005200d370358200528022821014101210002400240024002400240200528022c20052802502202200241284b22021b220e450d00200e417f4c0d0141002d00fca3c680001a200e41002802c8a3c68000118180808000002200450d020b20002001200541286a20021b200e10848e808000210f200341046a2802002101200328020022022002280200220041016a3602002000417f4c0d022005200136029401200520023602900103402002280204210003402000417f460d012000417f4c0d052002200041016a2002280204220320032000461b360204200320004721012003210020010d000b0b2005280294012103200528029001220020002802002200417f6a360200024020004101470d0020054190016a10f18d8080000b024020052802242200200528021c470d002005411c6a2000109f86808000200528022421000b2005280220200041386c6a220041023a000c2000200e3602082000200f3602042000200e3602002000200529035837000d2000200336023420002002360230200041156a200541e0006a2903003700002000411d6a200541e8006a290300370000200041256a200541f0006a2903003700002005200528022441016a3602240c040b10ae80808000000b4101200e10b280808000000b00000b10f08d808000000b20052802282103200528022c2102200528025021004100211020054190016a41286a2201410036020020054190016a2003200541286a200041284b220e1b2203200320022000200e1b6a10f686808000200541d8006a41286a2001280200360200200541d8006a41206a20054190016a41206a290200370300200541d8006a41186a20054190016a41186a290200370300200541d8006a41106a20054190016a41106a290200370300200541d8006a41086a20054190016a41086a290200370300200520052902900137035820052005280254360284012008280200210320052005411c6a3602c00141002111024002400240024002402003417e6a2203410420034106491b0e06040403000104040b200841306a21120c010b2008412c6a21120b41022111410021000c010b200841046a2100410121110b2012415c6a21132005200541c0016a3602c8012005200541d8006a3602c40103400240024002400240024020110e03010200010b20004110200041104b1b210e2012200041246c22026a2103201320026a2102200041087441807e6a21010340200e2000460d03200241246a210220014180026a2101200041016a210020032d00002108200341246a210320084102460d000b20014180fe0371410172210f200221140c030b200f41807e71410272210f0c020b200f41807e71210f0240201041ff01710d0041012110200021140c020b200f410272210f0c010b200f41807e71410272210f200e21000b0240200f41ff01714102460d0020142d0000450d01201428020421032005200541c4016a3602cc01200fad220a42ff01834202510d0120052003ad422086200a42ffff03838437039001200541cc016a20054190016a10bd8c8080000c010b0b02402005280280014129490d00200528025841002802c0a3c68000118080808000000b024020052802504129490d00200528022841002802c0a3c68000118080808000000b2005280220220f2005280224220341386c6a2108200528021c2114200f2100024002402003450d00200541d8006a41086a210320054188016a210e200f21000240034020002802002202418080808078460d01200028020421012000290204210a200e200041346a280200360200200541d8006a41286a2000412c6a290200370300200541d8006a41206a200041246a290200370300200541d8006a41186a2000411c6a290200370300200541d8006a41106a200041146a29020037030020032000410c6a29020037030020052000290204370358200541286a41286a200341286a280200360200200541286a41206a200341206a290200370300200541286a41186a200341186a290200370300200541286a41106a200341106a290200370300200541286a41086a200341086a290200370300200520032902003703282006200aa7200a422088a7200541286a20072802101186808080000002402002450d00200141002802c0a3c68000118080808000000b200041386a22002008470d000c030b0b200041386a21000b200820006b41386e210320082000460d00034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a2d00004102490d00200041306a2802002202417f460d00200220022802042201417f6a36020420014101470d00200041346a280200410b6a4104490d00200241002802c0a3c68000118080808000000b200041386a21002003417f6a22030d000b0b2014450d00200f41002802c0a3c68000118080808000000b200941004721000b20000d012004280200450d010b2004412c6a2802004129490d00200428020441002802c0a3c68000118080808000000b200541d0016a2480808080000ba00301047f23808080800041f0086b2202248080808000200241e8016a2001280200220128020022032001280204220110c28c8080000240024002400240024020022802e80122044105470d002002410c6a410c6a200241e8016a410c6a290200370200200220022902ec013702102002410536020c0c010b200241e0086a41086a2205200241e8016a410c6a290200370300200220022902ec013703e00820024184076a41146a200241e8016a41146a41c80110848e8080001a20024184076a410c6a20052903003702002002200436028407200220022903e008370288072002410c6a20024184076a2003200110f48d808000200228020c22014105470d010b200241106a10de858080000c010b20024184076a41046a2002410c6a41046a41d80110848e8080001a2002200136028407200241e8016a20024184076a10b78a80800020022802e80122014108470d0120022802ec0110df858080000b41c0d3c2800041c2004184d4c2800010a181808000000b200041046a200241e8016a41046a41980510848e8080001a20002001360200200241f0086a2480808080000ba00301047f23808080800041f0086b2202248080808000200241e8016a2001280200220128020022032001280204220110c28c8080000240024002400240024020022802e80122044105470d002002410c6a410c6a200241e8016a410c6a290200370200200220022902ec013702102002410536020c0c010b200241e0086a41086a2205200241e8016a410c6a290200370300200220022902ec013703e00820024184076a41146a200241e8016a41146a41c80110848e8080001a20024184076a410c6a20052903003702002002200436028407200220022903e008370288072002410c6a20024184076a2003200110f48d808000200228020c22014105470d010b200241106a10de858080000c010b20024184076a41046a2002410c6a41046a41d80110848e8080001a2002200136028407200241e8016a20024184076a10b68a80800020022802e80122014108470d0120022802ec0110df858080000b41c0d3c2800041c2004184d4c2800010a181808000000b200041046a200241e8016a41046a41980510848e8080001a20002001360200200241f0086a2480808080000b9a0801047f23808080800041f0006b220624808080800002400240024020002802682207450d0002400240024020044100480d00200441f5ffffff074f0d0102402004410b6a417c7122080d00410421090c030b41002d00fca3c680001a200841002802c8a3c680001181808080000022090d024104200810b280808000000b41fc9bc68000412b200641186a41a89cc6800041b89cc68000108981808000000b41e484c08000412b200641186a419085c08000419086c08000108981808000000b2009428180808010370200200941086a2003200410848e8080001a2006200436021420062009360210200041ec006a2802002103200641186a41186a200541186a290000370300200641186a41106a200541106a290000370300200641186a41086a200541086a290000370300200620052900003703182006200536026c200328021421042006200641106a360268200641086a2007200641186a200641e8006a4194d4c28000200411878080800000200628020c220428020021000240024002400240024020062802080d00024002402000417e6a2209410420094106491b0e06060006030104060b20042802342200450d05200441346a21090c040b2000450d0420042802042200450d04200441046a21090c030b0240024002400240200041fcffffff076a2205410320054105491b0e0403030102000b2004280204450d02200441086a28020041002802c0a3c68000118080808000000c020b2004280204450d01200441086a28020041002802c0a3c68000118080808000000c010b200410de858080000b200441002802c0a3c68000118080808000000c030b2004280204450d02200441086a220928020022000d010c020b200441046a2109200428020421000b200941046a280200210920002000280200220441016a3602002004417f4c0d02200641c8006a41086a200541086a290000370300200641c8006a41106a200541106a290000370300200641c8006a41186a200541186a290000370300200620052900003703482006200936026c2006200036026803402000280204210503402005417f460d012005417f4c0d052000200541016a2000280204220420042005461b360204200420054721092004210520090d000b0b200628026c21052006280268220420042802002204417f6a360200024020044101470d00200641e8006a10f18d8080000b200641c0006a2005360200200641216a200641d0006a290300370000200641296a200641d8006a290300370000200641316a200641e0006a290300370000200620062903483700192006200036023c200641023a0018200720012002200641186a2003280210118680808000000b2006280210220520052802002205417f6a36020020054101470d00200641106a10e28a8080000b200641f0006a2480808080000f0b00000b10f08d808000000b9a0801047f23808080800041f0006b220624808080800002400240024020002802682207450d0002400240024020044100480d00200441f5ffffff074f0d0102402004410b6a417c7122080d00410421090c030b41002d00fca3c680001a200841002802c8a3c680001181808080000022090d024104200810b280808000000b41fc9bc68000412b200641186a41a89cc6800041b89cc68000108981808000000b41e484c08000412b200641186a419085c08000419086c08000108981808000000b2009428180808010370200200941086a2003200410848e8080001a2006200436021420062009360210200041ec006a2802002103200641186a41186a200541186a290000370300200641186a41106a200541106a290000370300200641186a41086a200541086a290000370300200620052900003703182006200536026c200328021421042006200641106a360268200641086a2007200641186a200641e8006a4194d4c28000200411878080800000200628020c220428020021000240024002400240024020062802080d00024002402000417e6a2209410420094106491b0e06060006030104060b20042802342200450d05200441346a21090c040b2000450d0420042802042200450d04200441046a21090c030b0240024002400240200041fcffffff076a2205410320054105491b0e0403030102000b2004280204450d02200441086a28020041002802c0a3c68000118080808000000c020b2004280204450d01200441086a28020041002802c0a3c68000118080808000000c010b200410de858080000b200441002802c0a3c68000118080808000000c030b2004280204450d02200441086a220928020022000d010c020b200441046a2109200428020421000b200941046a280200210920002000280200220441016a3602002004417f4c0d02200641c8006a41086a200541086a290000370300200641c8006a41106a200541106a290000370300200641c8006a41186a200541186a290000370300200620052900003703482006200936026c2006200036026803402000280204210503402005417f460d012005417f4c0d052000200541016a2000280204220420042005461b360204200420054721092004210520090d000b0b200628026c21052006280268220420042802002204417f6a360200024020044101470d00200641e8006a10f18d8080000b200641c0006a2005360200200641216a200641d0006a290300370000200641296a200641d8006a290300370000200641316a200641e0006a290300370000200620062903483700192006200036023c200641023a0018200720012002200641186a2003280210118680808000000b2006280210220520052802002205417f6a36020020054101470d00200641106a10e28a8080000b200641f0006a2480808080000f0b00000b10f08d808000000b870101037f2001280200220241046a2802002103200228020022022002280200220441016a36020002402004417f4a0d0000000b20002003360208200020023602042000410736020020002001280204220129000037000c200041146a200141086a2900003700002000411c6a200141106a290000370000200041246a200141186a2900003700000bc32103097f017e027f23808080800041b0046b22042480808080002004200336021420042001360210024002400240024002400240024002400240024020022d00000d00200228020421020240200141286a28020022052001411c6a22062802002203470d00200610a889808000200128021c2103200128022821050b200141206a280200200141246a28020020056a22054100200320052003491b6b4102746a20023602002001200128022841016a360228200141186a280200220320024d0d02200441186a200141146a28020020024107746a220141800110848e8080001a200141043a0004200141083a00000240024020042d00184108470d0020044198016a200441186a41046a41e00010848e8080001a02400240200428021028026822070d00410021080c010b2004280214220128020021032001280204210520012802282102200441d8036a41286a22064100360200200441d8036a20032001200241284b22091b220320032005200220091b6a10f686808000200441f0026a41286a2006280200360200200441f0026a41206a200441d8036a41206a290200370300200441f0026a41186a200441d8036a41186a290200370300200441f0026a41106a200441d8036a41106a290200370300200441f0026a41086a200441d8036a41086a290200370300200420042902d8033703f0022004200128022c220a36029c030240024002400240024020042d009801417c6a41ff01712201410420014104491b0e050400010402040b200441c8016a21010c020b20044198016a41286a21010c010b200441c8016a21010b200141086a2802002001412c6a2802002202200241284b22031b21022001280204200141046a20031b21052001280200220341017621010240024020034101710d00410021060240200120024b0d0020012103410021010c020b2001200241d492c68000109481808000000b200120024f0d0841012106200141016a2103200520016a2d0000410f7121010b200441e0036a200220036b360200200420013a00d903200420063a00d8032004200520036a3602dc03200441f0026a200441d8036a10e28d808000200428029c03210a0b200441f8016a41086a200441fc026a290200370300200441f8016a41106a20044184036a290200370300200441f8016a41186a2004418c036a290200370300200441f8016a41206a20044194036a280200360200200420042902f4023703f80120042802f002210b2004280298032106410121080b200441a8026a41086a20044198016a41096a290000370300200441a8026a41106a20044198016a41116a290000370300200441a8026a41186a20044198016a41196a290000370300200441a8026a411f6a20044198016a41206a29000037000020042004290099013703a80220042d009801210320042802c001210c20042802c401210520042802c80121022004200441106a3602ac032004200441146a3602a803200441cc016a210102400240024002400240024002400240024002402003417c6a41ff01712209410420094104491b0e050001020304000b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d0f200141003a0000200441013602a402200420013602a0022004410136029c020c080b200441fc026a200141086a29020037020020044184036a200141106a2902003702002004418c036a200141186a290200370200200441f0026a41246a200141206a2902003702002004419c036a2203200141286a280200360200200420023602f002200420012902003702f402200441d8036a41206a200441cb026a280000360200200441d8036a41186a200441c3026a290000370300200441d8036a41106a200441bb026a290000370300200441d8036a41086a200441b3026a290000370300200420042900ab023703d80320042005360280042004200c3602fc03200420023602ac04200420042802f80220032802002201200141284b22011b3602a804200420042802f402200441f0026a41046a20011b3602a40420044194046a200441d8036a200441a4046a200441a8036a10f78580800020042802ac042203410176210220042802a804210120042802a40421090240024020034101710d004100210c0240200220014b0d0020022105410021020c020b2002200141d492c68000109481808000000b200220014f0d104101210c200241016a2105200920026a2d0000410f7121020b200420023a00bd032004200c3a00bc03200441003602b8032004200120056b3602b4032004200920056a3602b0032004419c026a200441b0036a200141017420036b20044194046a10d08c808000024002400240024020042d00d8030e020103000b20042802fc03220120012802002201417f6a36020020014101470d02200441d8036a41246a21010c010b20042802dc03220120012802002201417f6a36020020014101470d01200441d8036a41047221010b200110e28a8080000b200428029c034129490d0720042802f40241002802c0a3c68000118080808000000c070b20044184036a200141086a2902003702002004418c036a200141106a29020037020020044194036a200141186a2902003702002004419c036a200141206a280200360200200420023602f802200420053602f4022004200c3602f002200420012902003702fc02200441d8036a41206a200441cb026a280000360200200441d8036a41186a200441c3026a290000370300200441d8036a41106a200441bb026a290000370300200441d8036a41086a200441b3026a290000370300200420042900ab023703d803200441086a200441f0026a41046a41c0d1c280001096878080002004200c3602ac04200420042903083702a40420044194046a200441a4046a10dc8d808000200441003a0080042004200441a4046a3602fc03200441b0036a200441a8036a200441d8036a200441a4046a4100200410dc858080002004419c026a20044194046a20042802a80441017420042802ac046b200441b0036a10cb8c808000000b200441f0026a41206a200441cb026a280000360200200441f0026a41186a200441c3026a290000370300200441f0026a41106a200441bb026a290000370300200441f0026a41086a200441b3026a290000370300200420042900ab02220d3703f00220042005360298032004200c36029403200da741ff01714103470d01410221000c020b200441fc026a200141086a29020037020020044184036a200141106a2902003702002004418c036a200141186a29020037020020044194036a200141206a2902003702002004419c036a2209200141286a280200360200200420023602f002200420012902003702f402200441d8036a41096a20044198016a410172220141086a290000370000200441d8036a41116a200141106a290000370000200441d8036a41196a200141186a290000370000200441d8036a41206a2001411f6a2900003700002004200c36028004200420012900003700d903200420033a00d8032004200236029004200420042802f80220092802002201200141284b22091b220136028c04200420042802f402200441f0026a41046a20091b220936028804200341ff01714103470d024102210e0c030b200441d8036a200441f0026a4100200441a8036a10f78580800020042902dc03210d20042802d80321000b2004200d3702b403200420003602b0032004419c026a200441d8036a200441b0036a10ca8c808000000b200441b0036a200441d8036a20044188046a200441a8036a10f78580800020042902b403210d20042802b003210e200428028c042101200428028804210920042802900421020b200241017621030240024020024101710d004100210f0240200320014b0d002003210c410021030c020b2003200141d492c68000109481808000000b200320014f0d0a4101210f200341016a210c200920036a2d0000410f7121030b200420033a00a1042004200f3a00a0042004410036029c0420042001200c6b3602980420042009200c6a36029404200441c0036a200441a8036a360200200441003602b803200420053602b0032004200541c0046a3602b4032004200441f0026a3602bc032004200d3702a8042004200e3602a4042004419c026a20044194046a200141017420026b200441b0036a200441a4046a10ce8c808000024020042d00d80322014103460d0002400240024020010e020103000b20042802fc03220120012802002201417f6a36020020014101470d02200441fc036a21010c010b20042802dc03220120012802002201417f6a36020020014101470d01200441dc036a21010b200110e28a8080000b200541002802c0a3c6800011808080800000200428029c034129490d0020042802f40241002802c0a3c68000118080808000000b0240024020042802a4022201411f4b0d0020042802a0022102200441f0026a20016a4100412020016b108a8e8080001a200441f0026a2002200110848e8080001a20002001360204200041206a200441f0026a41186a290000370000200041186a200441f0026a41106a290000370000200041106a200441f8026a290000370000200020042900f002370008410121030c010b2004280210220241d4006a28020021092002280250210c2004280214220228022c220541017621030240024020054101710d00200228020420022802282205200541284b22051b220e2003490d0c2002280200200220051b2102410021050c010b200228020420022802282205200541284b1b220e2003490d0c200e20034d0d0d200441f9026a20022802002002200541284b1b220220036a2d000041f001713a0000410121050b200420053a00f802200420033602f402200420023602f002200441d0026a200c200441f0026a20042802a00222022001200928021c1187808080000020042802102103200441f0026a41106a200441f8016a41086a290300370200200441f0026a41186a200441f8016a41106a290300370200200441f0026a41206a200441f8016a41186a29030037020020044198036a200441f8016a41206a2802003602002004200b3602f402200420083602f002200420042903f8013702f8022004200a3602a0032004200636029c03200441d8036a41186a200441d0026a41186a2205290000370300200441d8036a41106a200441d0026a41106a2209290000370300200441d8036a41086a200441d0026a41086a220a290000370300200420042900d0023703d8032003200441d8036a20022001200441f0026a10f985808000200041196a2005290000370000200041116a2009290000370000200041096a200a290000370000200020042900d002370001410021030b200020033a00000240200428029c02450d00200241002802c0a3c68000118080808000000b02402007450d002001411f4b0d0020064129490d00200b41002802c0a3c68000118080808000000b20042d00184108470d010c030b20002004290278370001200041003a0000200041196a20044190016a290200370000200041116a20044188016a290200370000200041096a20044180016a2902003700000b200441186a10e3858080000c010b200041003a000020002002290001370001200041196a200241196a290000370000200041116a200241116a290000370000200041096a200241096a2900003700000b200441b0046a2480808080000f0b2002200341e4d7c2800010f980808000000b2001200241e492c6800010f980808000000b4101410110b280808000000b2002200141e492c6800010f980808000000b2003200141e492c6800010f980808000000b2003200e41ac95c68000109581808000000b2003200e41bc95c68000109581808000000b2003200e41cc95c6800010f980808000000bc32103097f017e027f23808080800041b0046b22042480808080002004200336021420042001360210024002400240024002400240024002400240024020022d00000d00200228020421020240200141286a28020022052001411c6a22062802002203470d00200610a889808000200128021c2103200128022821050b200141206a280200200141246a28020020056a22054100200320052003491b6b4102746a20023602002001200128022841016a360228200141186a280200220320024d0d02200441186a200141146a28020020024107746a220141800110848e8080001a200141043a0004200141083a00000240024020042d00184108470d0020044198016a200441186a41046a41e00010848e8080001a02400240200428021028026822070d00410021080c010b2004280214220128020021032001280204210520012802282102200441d8036a41286a22064100360200200441d8036a20032001200241284b22091b220320032005200220091b6a10f686808000200441f0026a41286a2006280200360200200441f0026a41206a200441d8036a41206a290200370300200441f0026a41186a200441d8036a41186a290200370300200441f0026a41106a200441d8036a41106a290200370300200441f0026a41086a200441d8036a41086a290200370300200420042902d8033703f0022004200128022c220a36029c030240024002400240024020042d009801417c6a41ff01712201410420014104491b0e050400010402040b200441c8016a21010c020b20044198016a41286a21010c010b200441c8016a21010b200141086a2802002001412c6a2802002202200241284b22031b21022001280204200141046a20031b21052001280200220341017621010240024020034101710d00410021060240200120024b0d0020012103410021010c020b2001200241d492c68000109481808000000b200120024f0d0841012106200141016a2103200520016a2d0000410f7121010b200441e0036a200220036b360200200420013a00d903200420063a00d8032004200520036a3602dc03200441f0026a200441d8036a10e28d808000200428029c03210a0b200441f8016a41086a200441fc026a290200370300200441f8016a41106a20044184036a290200370300200441f8016a41186a2004418c036a290200370300200441f8016a41206a20044194036a280200360200200420042902f4023703f80120042802f002210b2004280298032106410121080b200441a8026a41086a20044198016a41096a290000370300200441a8026a41106a20044198016a41116a290000370300200441a8026a41186a20044198016a41196a290000370300200441a8026a411f6a20044198016a41206a29000037000020042004290099013703a80220042d009801210320042802c001210c20042802c401210520042802c80121022004200441106a3602ac032004200441146a3602a803200441cc016a210102400240024002400240024002400240024002402003417c6a41ff01712209410420094104491b0e050001020304000b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d0f200141003a0000200441013602a402200420013602a0022004410136029c020c080b200441fc026a200141086a29020037020020044184036a200141106a2902003702002004418c036a200141186a290200370200200441f0026a41246a200141206a2902003702002004419c036a2203200141286a280200360200200420023602f002200420012902003702f402200441d8036a41206a200441cb026a280000360200200441d8036a41186a200441c3026a290000370300200441d8036a41106a200441bb026a290000370300200441d8036a41086a200441b3026a290000370300200420042900ab023703d80320042005360280042004200c3602fc03200420023602ac04200420042802f80220032802002201200141284b22011b3602a804200420042802f402200441f0026a41046a20011b3602a40420044194046a200441d8036a200441a4046a200441a8036a10f48580800020042802ac042203410176210220042802a804210120042802a40421090240024020034101710d004100210c0240200220014b0d0020022105410021020c020b2002200141d492c68000109481808000000b200220014f0d104101210c200241016a2105200920026a2d0000410f7121020b200420023a00bd032004200c3a00bc03200441003602b8032004200120056b3602b4032004200920056a3602b0032004419c026a200441b0036a200141017420036b20044194046a10d08c808000024002400240024020042d00d8030e020103000b20042802fc03220120012802002201417f6a36020020014101470d02200441d8036a41246a21010c010b20042802dc03220120012802002201417f6a36020020014101470d01200441d8036a41047221010b200110e28a8080000b200428029c034129490d0720042802f40241002802c0a3c68000118080808000000c070b20044184036a200141086a2902003702002004418c036a200141106a29020037020020044194036a200141186a2902003702002004419c036a200141206a280200360200200420023602f802200420053602f4022004200c3602f002200420012902003702fc02200441d8036a41206a200441cb026a280000360200200441d8036a41186a200441c3026a290000370300200441d8036a41106a200441bb026a290000370300200441d8036a41086a200441b3026a290000370300200420042900ab023703d803200441086a200441f0026a41046a41c0d1c280001096878080002004200c3602ac04200420042903083702a40420044194046a200441a4046a10dc8d808000200441003a0080042004200441a4046a3602fc03200441b0036a200441a8036a200441d8036a200441a4046a4100200410da858080002004419c026a20044194046a20042802a80441017420042802ac046b200441b0036a10cb8c808000000b200441f0026a41206a200441cb026a280000360200200441f0026a41186a200441c3026a290000370300200441f0026a41106a200441bb026a290000370300200441f0026a41086a200441b3026a290000370300200420042900ab02220d3703f00220042005360298032004200c36029403200da741ff01714103470d01410221000c020b200441fc026a200141086a29020037020020044184036a200141106a2902003702002004418c036a200141186a29020037020020044194036a200141206a2902003702002004419c036a2209200141286a280200360200200420023602f002200420012902003702f402200441d8036a41096a20044198016a410172220141086a290000370000200441d8036a41116a200141106a290000370000200441d8036a41196a200141186a290000370000200441d8036a41206a2001411f6a2900003700002004200c36028004200420012900003700d903200420033a00d8032004200236029004200420042802f80220092802002201200141284b22091b220136028c04200420042802f402200441f0026a41046a20091b220936028804200341ff01714103470d024102210e0c030b200441d8036a200441f0026a4100200441a8036a10f48580800020042902dc03210d20042802d80321000b2004200d3702b403200420003602b0032004419c026a200441d8036a200441b0036a10ca8c808000000b200441b0036a200441d8036a20044188046a200441a8036a10f48580800020042902b403210d20042802b003210e200428028c042101200428028804210920042802900421020b200241017621030240024020024101710d004100210f0240200320014b0d002003210c410021030c020b2003200141d492c68000109481808000000b200320014f0d0a4101210f200341016a210c200920036a2d0000410f7121030b200420033a00a1042004200f3a00a0042004410036029c0420042001200c6b3602980420042009200c6a36029404200441c0036a200441a8036a360200200441003602b803200420053602b0032004200541c0046a3602b4032004200441f0026a3602bc032004200d3702a8042004200e3602a4042004419c026a20044194046a200141017420026b200441b0036a200441a4046a10cf8c808000024020042d00d80322014103460d0002400240024020010e020103000b20042802fc03220120012802002201417f6a36020020014101470d02200441fc036a21010c010b20042802dc03220120012802002201417f6a36020020014101470d01200441dc036a21010b200110e28a8080000b200541002802c0a3c6800011808080800000200428029c034129490d0020042802f40241002802c0a3c68000118080808000000b0240024020042802a4022201411f4b0d0020042802a0022102200441f0026a20016a4100412020016b108a8e8080001a200441f0026a2002200110848e8080001a20002001360204200041206a200441f0026a41186a290000370000200041186a200441f0026a41106a290000370000200041106a200441f8026a290000370000200020042900f002370008410121030c010b2004280210220241d4006a28020021092002280250210c2004280214220228022c220541017621030240024020054101710d00200228020420022802282205200541284b22051b220e2003490d0c2002280200200220051b2102410021050c010b200228020420022802282205200541284b1b220e2003490d0c200e20034d0d0d200441f9026a20022802002002200541284b1b220220036a2d000041f001713a0000410121050b200420053a00f802200420033602f402200420023602f002200441d0026a200c200441f0026a20042802a00222022001200928021c1187808080000020042802102103200441f0026a41106a200441f8016a41086a290300370200200441f0026a41186a200441f8016a41106a290300370200200441f0026a41206a200441f8016a41186a29030037020020044198036a200441f8016a41206a2802003602002004200b3602f402200420083602f002200420042903f8013702f8022004200a3602a0032004200636029c03200441d8036a41186a200441d0026a41186a2205290000370300200441d8036a41106a200441d0026a41106a2209290000370300200441d8036a41086a200441d0026a41086a220a290000370300200420042900d0023703d8032003200441d8036a20022001200441f0026a10f885808000200041196a2005290000370000200041116a2009290000370000200041096a200a290000370000200020042900d002370001410021030b200020033a00000240200428029c02450d00200241002802c0a3c68000118080808000000b02402007450d002001411f4b0d0020064129490d00200b41002802c0a3c68000118080808000000b20042d00184108470d010c030b20002004290278370001200041003a0000200041196a20044190016a290200370000200041116a20044188016a290200370000200041096a20044180016a2902003700000b200441186a10e3858080000c010b200041003a000020002002290001370001200041196a200241196a290000370000200041116a200241116a290000370000200041096a200241096a2900003700000b200441b0046a2480808080000f0b2002200341e4d7c2800010f980808000000b2001200241e492c6800010f980808000000b4101410110b280808000000b2002200141e492c6800010f980808000000b2003200141e492c6800010f980808000000b2003200e41ac95c68000109581808000000b2003200e41bc95c68000109581808000000b2003200e41cc95c6800010f980808000000b807703067f027e087f23808080800041f0066b2207248080808000200741086a41086a200341086a28020036020020072003290200370308024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020022d0000417c6a41ff01712208410420084104491b0e050001020304000b200728021022084101762203200728020c22014b0d04200728020821024100210920074188066a41286a220a410036020020074188066a200220036a200220016a10f58d808000200741186a41286a200a280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a29020037030020072007290288063703182008410171210a4105210341002106200521084100210b0c310b200741f0036a41286a200241d8006a290200370300200741f0036a41206a200241d0006a290200370300200741f0036a41186a200241c8006a290200370300200741f0036a41106a200241c0006a290200370300200741f0036a41086a2208200241386a290200370300200720022902303703f003200741a8026a41286a2002412c6a280200360200200741a8026a41206a200241246a290200370300200741a8026a41186a2002411c6a290200370300200741a8026a41106a200241146a290200370300200741a8026a41086a2002410c6a290200370300200720022902043703a802200720072802f0033602dc0220072008280200200741f0036a412c6a2802002208200841284b22081b3602d802200720072802f403200741f0036a410472220920081b3602d402200241046a2108200741086a200741d4026a10db8d808000220a20072802d802220b41017420072802dc02220c6b2202460d210c220b200741f0036a41286a200241d0006a29020037030020074190046a200241c8006a29020037030020074188046a200241c0006a29020037030020074180046a200241386a290200370300200741f0036a41086a200241306a290200220d37030020072002290228220e3703f003200241086a280200210f20022d000421102007200e3e02b0022007200da72007419c046a2802002208200841284b22081b3602ac02200720072802f403200741f0036a41047220081b3602a802200241046a210a200741086a200741a8026a10db8d808000210820072802ac02220c410174210920072802b002210b024002402008450d0020082009200b6b470d012003200328020820086a36020820074188066a2001200a2003200420052006108286808000200728028806210420072d008c0622024102460d1e20072802b0022208410176220120072802ac0222054b0d0b20072802a802210341002106200741b0066a220a410036020020074188066a200320016a200320056a10f58d808000200741206a20074188066a41106a290200370300200741186a41106a20074188066a41186a290200370300200741186a41186a20074188066a41206a290200370300200741186a41206a200a28020036020020072007290290063703182008410171210f200728028c06210a2007280288062108200245210b410621030c200b2009200b460d20200b4101762208200c4f0d0520072802a80220086a2d0000210c41002d00fca3c680001a41c00441002802c8a3c68000118180808000002208450d04200841023a0000200841023a009c04200841023a00f803200841023a00d403200841023a00b003200841023a008c03200841023a00e802200841023a00c402200841023a00a002200841023a00fc01200841023a00d801200841023a00b401200841023a009001200841023a006c200841023a0048200841023a00240240024020072802ac02220941017420072802b00222116b4101470d00200741c8046a41026a200241056a220a41026a2d00003a000020074188066a41086a2002410c6a220241086a29020037030020074188066a41106a200241106a29020037030020074188066a41186a200241186a2802003602002007200a2f00003b01c80420072002290200370388060c010b201141016a2210410176220f20094b0d0720072802a802210220074188066a41286a2211410036020020074188066a2002200f6a200220096a10f58d808000200741c8046a41286a2011280200360200200741c8046a41206a20074188066a41206a290200370300200741c8046a41186a20074188066a41186a290200370300200741c8046a41106a20074188066a41106a290200370300200741c8046a41086a20074188066a41086a29020037030020072007290288063703c804200741ac046a200a41086a290000370000200741b4046a200a41106a290000370000200741bc046a200a41186a290000370000200741c4046a200a41206a2800003600002007200a2900003700a4042010410171210a02400240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a220941002001411c6a280200220f2009200f491b6b360200200141206a28020020024102746a280200220f200141186a28020022024f0d0a200141146a280200200f4107746a220241046a200220022d00004108461b10e385808000200241063a0004200241083a00002002200a36022c200220072900a1043700052002410d6a200741a1046a41086a290000370000200241156a200741a1046a41106a2900003700002002411d6a200741a1046a41186a290000370000200241246a200741c0046a290000370000200220072903c804370230200241386a200741c8046a41086a290300370200200241c0006a200741c8046a41106a290300370200200241c8006a200741c8046a41186a290300370200200241d0006a200741e8046a290300370200200241d8006a200741f0046a2802003602000c010b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200141146a28020020024107746a220241063a0004200241083a0000200220072900a1043700052002200a36022c200220072903c8043702302002410d6a200741a1046a41086a290000370000200241156a200741a1046a41106a2900003700002002411d6a200741a1046a41186a290000370000200241246a200741c0046a290000370000200241386a200741c8046a41086a290300370200200241c0006a200741c8046a41106a290300370200200241c8006a200741c8046a41186a290300370200200241d0006a200741e8046a290300370200200241d8006a200741f0046a28020036020020012001280218220f41016a3602180b410021100b2008200c410f71200c410476200b4101711b41246c6a220220103a0000200220072f01c8043b00012002200f3602042002200729038806370208200241036a200741c8046a41026a2d00003a0000200241106a20074188066a41086a290300370200200241186a20074188066a41106a290300370200200241206a20074188066a41186a280200360200200741033a00cc04200720083602f804200741073a00c80420074188066a2001200741c8046a20032004200520061081868080002007280288064102460d1c200741c8006a41086a200741a4066a290200370300200741c8006a41106a20074188066a41246a280200360200200741c0006a200741e8066a280200360200200741186a41206a200741e0066a290200370300200741186a41186a200741d8066a290200370300200741186a41106a200741d0066a290200370300200741186a41086a200741c8066a2902003703002007200729029c06370348200720072902c006370318200728028c06220341087621022007280298062105200728029406210420072f019206210c20072d009106210920072d009006210620072802b006210120072802b406210f20072802b806210820072802bc06210a0c1e0b200b20086a2209410176220b200c4b0d0720072802a802210220074188066a41286a220f410036020020074188066a2002200b6a2002200c6a10f58d808000200741fc056a200f280200360200200741f4056a20074188066a41206a290200370200200741ec056a20074188066a41186a290200370200200741e4056a20074188066a41106a290200370200200741dc056a20074188066a41086a2202290200370200200741d4056a200729028806370200200741b4056a200a41086a290200370200200741bc056a200a41106a290200370200200741c4056a200a41186a290200370200200741cc056a200a41206a2802003602002003200328020820086a360208200720094101713602d0052007200a2902003702ac05200741063a00a80520074188066a2001200741a8056a20032004200520061081868080002007280288064102460d1b200728028c062103200741c8046a200241dc0010848e8080001a20074188066a200741a8026a200810da8d80800002400240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a220441002001411c6a280200220520042005491b6b360200200141206a28020020024102746a2802002204200141186a28020022024f0d0a200141146a28020020044107746a220241046a200220022d00004108461b10e38580800020022003360204200241083a0000200241086a200741c8046a41dc0010848e8080001a0c010b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200141146a28020020024107746a22022003360204200241083a0000200241086a200741c8046a41dc0010848e8080001a20012001280218220441016a3602180b200741206a2007419c066a290200370300200741286a200741a4066a290200370300200741306a200741ac066a290200370300200741386a200741b4066a2802003602002007200729029406370318200728028806210f200728028c062108200728029006210a41002106410621030c1d0b2002280230210a20074198046a2002412c6a28020036020020074190046a200241246a29020037030020074188046a2002411c6a29020037030020074180046a200241146a290200370300200741f0036a41086a2002410c6a290200370300200720022902043703f003200728020c220b41017420072802102208460d1920084101762202200b4f0d09200728020820026a2d000021022003200328020841016a360208200a2002410f71200241047620084101711b41246c6a22022d00002108200241023a000002400240024020084102460d00200741e0006a41096a200241096a290000370000200741e0006a41116a200241116a290000370000200741e0006a41196a200241196a290000370000200741e0006a41206a200241206a280000360000200720083a00602007200229000137006120074188066a2001200741e0006a2003200420052006108286808000200728028806210320072d008c0622044102460d0220022003360204200241003a000020040d0120074193066a200741f0036a41086a2903003700002007419b066a200741f0036a41106a290300370000200741a3066a200741f0036a41186a290300370000200741ab066a200741f0036a41206a290300370000200741b3066a20074198046a280200360000200720072903f00337008b0620002007290088063700052000410d6a20074188066a41086a290000370000200041156a20074188066a41106a2900003700002000411d6a20074188066a41186a290000370000200041256a20074188066a41206a2900003700002000412c6a200741af066a2900003700002000200a360234200041073a0004200041013602000c320b2003280208220641017622082003280204220b4b0d0c2003280200210320074188066a41286a220c410036020020074188066a200320086a2003200b6a10f58d808000200741c8046a41286a200c280200360200200741c8046a41206a20074188066a41206a290200370300200741c8046a41186a20074188066a41186a290200370300200741c8046a41106a20074188066a41106a290200370300200741c8046a41086a20074188066a41086a29020037030020072007290288063703c8042006410171210b02400240200141286a2802002203450d0020012003417f6a360228200141246a22032003280200220341016a220841002001411c6a280200220620082006491b6b360200200141206a28020020034102746a2802002208200141186a28020022034f0d0f200141146a28020020084107746a220341046a200320032d00004108461b10e3858080002003200b360234200320053600302003200436002c200320053600102003200436000c200341003b0008200341053a0004200341083a0000200320072903c804370238200341c0006a200741c8046a41086a290300370200200341c8006a200741d8046a290300370200200341d0006a200741e0046a290300370200200341d8006a200741e8046a290300370200200341e0006a200741f0046a2802003602000c010b0240200141186a28020022032001280210470d00200141106a2003109e86808000200128021821030b200141146a28020020034107746a2203200b360234200320053600302003200436002c200320053600102003200436000c200341003b0008200341053a0004200341083a0000200320072903c804370238200341c0006a200741c8046a41086a290300370200200341c8006a200741d8046a290300370200200341d0006a200741e0046a290300370200200341d8006a200741e8046a290300370200200341e0006a200741f0046a28020036020020012001280218220841016a3602180b20022008360204200241003a00000b200741d0006a20074184046a290200370300200741d8006a2007418c046a280200360200200720072902fc033703484100210b2007280298042108200728029404210f200728029004210120072802f803210520072802f403210420072f01f203210c20072d00f103210920072d00f0032106410721030c2f0b2000410236020020002003360204024020072d00f00322004103460d0002400240024020000e020103000b200728029404220020002802002200417f6a36020020004101470d0220074194046a21000c010b20072802f403220020002802002200417f6a36020020004101470d01200741f0036a41047221000b200010e28a8080000b200a41002802c0a3c68000118080808000000c2f0b200741c8046a41286a200241d8006a290200370300200741c8046a41206a200241d0006a290200370300200741c8046a41186a200241c8006a290200370300200741c8046a41106a200241c0006a290200370300200741c8046a41086a220a200241386a290200370300200720022902303703c804200228022c2108200741a8026a41286a200241286a280200360200200741a8026a41206a200241206a290200370300200741a8026a41186a200241186a290200370300200741a8026a41106a200241106a290200370300200741a8026a41086a200241086a290200370300200720022902003703a802200720072802c80436028c012007200a280200200741f4046a2802002202200241284b22021b36028801200720072802cc04200741c8046a41047220021b360284010240024002400240024002400240024002400240200741086a20074184016a10db8d808000220a2007280288012202410174200728028c01220f6b220b470d00200a200728020c41017420072802106b460d010b200a200b490d012007280210200a6a220b4101762202200728020c220c4f0d02200728020820026a2d000021022003200a20032802086a41016a36020820082002410f712002410476200b4101711b41246c6a22022d0000210a200241023a0000200a4102460d0320074184026a41096a200241096a29000037000020074184026a41116a200241116a29000037000020074184026a41196a200241196a29000037000020074184026a41206a200241206a2800003600002007200a3a008402200720022900013700850220074188066a200120074184026a2003200420052006108286808000200728028806210320072d008c0622044102460d0520022003360204200241003a000020040d04200041346a20074184016a10d98d80800020002008360230200041013602002000412c6a200741d0026a280200360200200041246a200741c8026a2903003702002000411c6a200741c0026a290300370200200041146a200741b8026a2903003702002000410c6a200741b0026a290300370200200020072903a8023702040c060b20072005360298042007200436029404200720053602f803200720043602f4034100210b200741003b01f003024020072d00a8024103460d00200741a8026a200741f0036a10e785808000210b0b200f410176220920024b0d13200728028401210a20074188066a41286a220c410036020020074188066a200a20096a200a20026a10f58d808000200741186a41286a200c280200360200200741186a41206a20074188066a41206a2202290200370300200741186a41186a20074188066a41186a2210290200370300200741186a41106a20074188066a41106a2211290200370300200741186a41086a20074188066a41086a221229020037030020072007290288063703182003280204210a2003280200210920032802082103200728028c012113200728028801211420072f00f103211520072d00f3032116200c200741a8026a41286a2802003602002002200741a8026a41206a2903003703002010200741a8026a41186a2903003703002011200741a8026a41106a2903003703002012200741a8026a41086a290300370300200720072903a802370388062003201441017420136b6a220341017621020240024020034101710d002002200a4b0d16200741003a00dc02200720023602d802200720093602d4020c010b2002200a4b0d162002200a4f0d17200720093602d402200720023602d802200741dd026a200920026a2d000041f001713a0000200741013a00dc020b2004411076210c20044108762109200f410171210a201520164110747221022001200620074188066a200741d4026a1083868080004100210320042106200521112005210f0c070b200f200a41016a22096a220c410176220b20024b0d16200728028401210320074188066a41286a2206410036020020074188066a2003200b6a200320026a10f58d80800020074190016a41286a200628020036020020074190016a41206a20074188066a41206a29020037030020074190016a41186a20074188066a41186a29020037030020074190016a41106a20074188066a41106a29020037030020074190016a41086a20074188066a41086a290200370300200741c0016a41086a200741a8026a41086a290300370300200741c0016a41106a200741a8026a41106a290300370300200741c0016a41186a200741a8026a41186a290300370300200741c0016a41206a200741a8026a41206a290300370300200741c0016a41286a200741a8026a41286a280200360200200720072902880637039001200720072903a8023703c001200728028c01200a6a220641017622022007280288012203490d0520022003418cd5c2800010f980808000000b2002200c418cd5c2800010f980808000000b20032802082206410176220a2003280204220b4b0d152003280200210320074188066a41286a220c410036020020074188066a2003200a6a2003200b6a10f58d808000200741f0036a41286a200c280200360200200741f0036a41206a20074188066a41206a290200370300200741f0036a41186a20074188066a41186a290200370300200741f0036a41106a20074188066a41106a290200370300200741f0036a41086a20074188066a41086a29020037030020072007290288063703f0032006410171210b02400240200141286a2802002203450d0020012003417f6a360228200141246a22032003280200220341016a220a41002001411c6a2802002206200a2006491b6b360200200141206a28020020034102746a280200220a200141186a28020022034f0d18200141146a280200200a4107746a220341046a200320032d00004108461b10e3858080002003200b360234200320053600302003200436002c200320053600102003200436000c200341003b0008200341053a0004200341083a0000200320072903f003370238200341c0006a200741f0036a41086a290300370200200341c8006a20074180046a290300370200200341d0006a20074188046a290300370200200341d8006a20074190046a290300370200200341e0006a20074198046a2802003602000c010b0240200141186a28020022032001280210470d00200141106a2003109e86808000200128021821030b200141146a28020020034107746a2203200b360234200320053600302003200436002c200320053600102003200436000c200341003b0008200341053a0004200341083a0000200320072903f003370238200341c0006a200741f0036a41086a290300370200200341c8006a20074180046a290300370200200341d0006a20074188046a290300370200200341d8006a20074190046a290300370200200341e0006a20074198046a28020036020020012001280218220a41016a3602180b2002200a360204200241003a00000b200728028c012201410176220320072802880122044b0d1620072802840121024100210b20074188066a41286a2205410036020020074188066a200220036a200220046a10f58d808000200741186a41286a2005280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a290200370300200720072902880637031820072d00a802210320072d00ac02210620072d00ad02210920072f01ae02210c20072802b002211120072802b402210520072f00a902210220072d00ab022104200741c8006a41106a200741a8026a41206a280200360200200741c8006a41086a200741a8026a41186a290300370300200720072903b8023703482001410171210a2002200441107472210220072802d002210f20072802cc0221040c030b2000410236020020002003360204200741a8026a10e585808000200841002802c0a3c68000118080808000000b20072802f4044129490d3020072802cc0441002802c0a3c68000118080808000000c300b20072802840120026a2d0000210341002d00fca3c680001a41c00441002802c8a3c68000118180808000002210450d14200c410171210c201041023a009c04201041023a00f803201041023a00d403201041023a00b003201041023a008c03201041023a00e802201041023a00c402201041023a00a002201041023a00fc01201041023a00d801201041023a00b401201041023a009001201041023a006c201041023a0048201041023a0024201041023a0000200741b3066a200741c0016a41286a280200360000200741ab066a200741c0016a41206a290300370000200741a3066a200741c0016a41186a2903003700002007419b066a200741c0016a41106a29030037000020074193066a200741c8016a290300370000200720072903c00137008b06200141106a210f02400240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a220b4100200128021c2211200b2011491b6b360200200141206a28020020024102746a280200220b200141186a221128020022024f0d17200141146a280200200b4107746a220241046a200220022d00004108461b10e385808000200241083a00002002200c360234200220083602302002200729008806370001200241096a20074188066a41086a290000370000200241116a20074188066a41106a290000370000200241196a20074188066a41186a290000370000200241216a20074188066a41206a290000370000200241286a200741af066a2900003700002002200729039001370238200241c0006a20074190016a41086a290300370200200241c8006a20074190016a41106a290300370200200241d0006a20074190016a41186a290300370200200241d8006a20074190016a41206a290300370200200241e0006a20074190016a41286a2802003602000c010b0240200141186a221128020022022001280210470d00200f2002109e86808000201128020021020b200141146a28020020024107746a220241083a000020022007290088063700012002200c360234200220083602302002200729039001370238200241096a20074188066a41086a290000370000200241116a20074188066a41106a290000370000200241196a20074188066a41186a290000370000200241216a20074188066a41206a290000370000200241286a200741af066a290000370000200241c0006a20074190016a41086a290300370200200241c8006a20074190016a41106a290300370200200241d0006a20074190016a41186a290300370200200241d8006a20074190016a41206a290300370200200241e0006a20074190016a41286a28020036020020012001280218220b41016a3602180b20102003410f71200341047620064101711b41246c6a2203200b36020441002102200341003a00000240200728020c22034101742007280210220b200a6a2208470d0020074188066a20074184016a200a10da8d808000200741c8006a41106a200741f0016a41106a280200360200200741c8006a41086a200741f0016a41086a290200370300200741186a41086a20074194066a290200370300200741186a41106a2007419c066a290200370300200741306a200741a4066a290200370300200741386a20074188066a41246a290200370300200741c0006a200741b4066a280200360200200720072902f0013703482007200729028c063703182004411076210c20044108762109200728028806210a20042106200521112005210f20102108410021034100210b0c010b2008410176220620034f0d18200b20096a2209410176220c20034b0d162007280208220220066a2d0000210b20074188066a41286a2206410036020020074188066a2002200c6a200220036a10f58d808000200741f0036a41286a2006280200360200200741f0036a41206a20074188066a41206a290200370300200741f0036a41186a20074188066a41186a290200370300200741f0036a41106a20074188066a41106a290200370300200741f0036a41086a20074188066a41086a29020037030020072007290288063703f003200941017121060240024020012802282202450d0020012002417f6a360228200141246a22022002280200220241016a22034100200128021c220c2003200c491b6b360200200141206a28020020024102746a2802002203200128021822024f0d19200141146a28020020034107746a220241046a200220022d00004108461b10e3858080002002410b6a41003a0000200241003b0009200220053602102002200436020c200241003a0008200241053a0004200241083a000020022006360234200220053602302002200436022c200220072902f001370218200241206a200741f0016a41086a290200370200200241286a200741f0016a41106a280200360200200220072903f003370238200241c0006a200741f0036a41086a290300370200200241c8006a200741f0036a41106a290300370200200241d0006a20074188046a290300370200200241d8006a200741f0036a41206a290300370200200241e0006a200741f0036a41286a2802003602000c010b024020112802002202200f280200470d00200f2002109e86808000201128020021020b200141146a28020020024107746a220241003b0009200220053602102002200436020c200241003a0008200241053a0004200241083a0000200220072902f00137021820022006360234200220053602302002200436022c200220072903f0033702382002410b6a41003a0000200241206a200741f0016a41086a290200370200200241286a200741f0016a41106a280200360200200241c0006a200741f0036a41086a290300370200200241c8006a200741f0036a41106a290300370200200241d0006a20074188046a290300370200200241d8006a200741f0036a41206a290300370200200241e0006a200741f0036a41286a28020036020020012001280218220341016a3602180b2010200b410f71200b41047620084101711b41246c6a220220033602044100210b200241003a000020074188066a20074184016a200a10da8d808000200741206a20074194066a290200370300200741286a2007419c066a290200370300200741306a200741a4066a290200370300200741386a20074188066a41246a290200370300200741c0006a200741b4066a2802003602002007200729028c06370318200728028806210a41032103201021080b024020072802f4044129490d0020072802cc0441002802c0a3c68000118080808000000b20042101201121040c2d0b2003200141e491c68000109481808000000b410441c00410b280808000000b2008200c418cd5c2800010f980808000000b200f200941e491c68000109481808000000b200f200241d4d7c2800010f980808000000b200b200c41e491c68000109481808000000b2004200241d4d7c2800010f980808000000b2001200541e491c68000109481808000000b2002200b418cd5c2800010f980808000000b2008200b41e491c68000109481808000000b2008200341d4d7c2800010f980808000000b2009200241e491c68000109481808000000b2002200a41f492c68000109581808000000b2002200a418493c68000109581808000000b2002200a419493c6800010f980808000000b200b200241e491c68000109481808000000b200a200b41e491c68000109481808000000b200a200341d4d7c2800010f980808000000b2003200441e491c68000109481808000000b410441c00410b280808000000b200b200241d4d7c2800010f980808000000b200c200341e491c68000109481808000000b2003200241d4d7c2800010f980808000000b20062003418cd5c2800010f980808000000b200720053602b006200720043602ac0620072005360290062007200436028c064100210b200741003b018806024020072d00f0034103460d00200741f0036a20074188066a10e785808000210b0b2003280208220c410176210802400240024002400240200c4101710d0020082003280204220c4b0d02200741003a00d004200720083602cc04200720032802003602c8040c010b20082003280204220c4b0d022008200c4f0d032007200328020022033602c804200720083602cc04200741d1046a200320086a2d000041f001713a0000200741013a00d0040b20012006200241046a200741c8046a1083868080004100210941072103410021062004210f200521080c170b2008200c41f492c68000109581808000000b2008200c418493c68000109581808000000b2008200c419493c6800010f980808000000b200728028c0621040b2000410236020020002004360204200728029c044129490d1320072802f40341002802c0a3c68000118080808000000c130b4100210b0b200728029c044129490d1020072802f40341002802c0a3c68000118080808000000c100b419cd5c28000412a41c8d5c2800010f880808000000b200a200728020c41017420072802106b470d00200741d0046a2005360200200720043602cc04200741003a00c804200741a8026a200741c8046a10e785808000210c2003280208200a6a220b41017621022003280204210a2003280200210302400240200b4101710d002002200a4b0d05200741003a0090062007200236028c0620072003360288060c010b2002200a4b0d052002200a4f0d0620072003360288062007200236028c0620074191066a200320026a2d000041f001713a0000200741013a0090060b20012006200820074188066a108386808000200741d4046a2102200c0d0120072802f003210a20072802f403210120072802f8032108200728029c0421034100210b20074188066a41286a2206410036020020074188066a20012009200341284b220c1b2201200120082003200c1b6a10f686808000200741186a41286a2006280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a29020037030020072007290288063703180c020b200a2002490d07200c410176220a200b4b0d0520072802d402210220074188066a41286a2209410036020020074188066a2002200a6a2002200b6a10f58d808000200741c8046a41286a220a2009280200360200200741c8046a41206a220b20074188066a41206a290200370300200741c8046a41186a220920074188066a41186a290200370300200741c8046a41106a220f20074188066a41106a290200370300200741c8046a41086a221020074188066a41086a29020037030020072007290288063703c80441002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d06200241023a009c04200241023a00f803200241023a00d403200241023a00b003200241023a008c03200241023a00e802200241023a00c402200241023a00a002200241023a00fc01200241023a00d801200241023a00b401200241023a009001200241023a006c200241023a0048200241023a0024200241023a000020074190036a41086a200841086a29020037030020074190036a41106a200841106a29020037030020074190036a41186a200841186a29020037030020074190036a41206a200841206a29020037030020074190036a41286a200841286a2802003602002007200829020037039003200741c4036a20072903c804370200200741cc036a2010290300370200200741d4036a200f290300370200200741dc036a2009290300370200200741e4036a200b290300370200200741ec036a200a2802003602002007200c4101713602c003200720023602bc0320074188066a200120074190036a20032004200520061081868080002007280288064102460d08200741c8006a41086a200741a4066a290200370300200741c8006a41106a200741ac066a280200360200200741c0006a200741e8066a280200360200200741386a200741e0066a290200370300200741306a200741d8066a290200370300200741186a41106a200741d0066a290200370300200741186a41086a200741c8066a2902003703002007200729029c06370348200720072902c006370318200728028c06220341087621022007280298062105200728029406210420072f019206210c20072d009106210920072d009006210620072802b006210120072802b406210f20072802b806210820072802bc06210a4100210b0c0c0b20072802f003210a20072802f403210120072802f8032108200728029c04210320074188066a41286a220b410036020020074188066a20012009200341284b22061b220120012008200320061b6a10f686808000200741186a41286a200b280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a29020037030020072007290288063703184101210b0b200741c8006a41106a200241106a280200360200200741c8006a41086a200241086a2902003703002007200229020037034841052103410021060c0a0b2002200a41f492c68000109581808000000b2002200a418493c68000109581808000000b2002200a419493c6800010f980808000000b200a200b41e491c68000109481808000000b410441c00410b280808000000b41002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d01200241023a0000200241023a009c04200241023a00f803200241023a00d403200241023a00b003200241023a008c03200241023a00e802200241023a00c402200241023a00a002200241023a00fc01200241023a00d801200241023a00b401200241023a009001200241023a006c200241023a0048200241023a0024024020072802dc02200a6a220c410176220f20072802d802220b490d00200f200b418cd5c2800010f980808000000b200c41016a22114101762210200b4b0d0220072802d4022209200f6a2d0000210f20074188066a41286a2212410036020020074188066a200920106a2009200b6a10f58d808000200741c8046a41286a2012280200360200200741c8046a41206a20074188066a41206a290200370300200741c8046a41186a20074188066a41186a290200370300200741c8046a41106a20074188066a41106a290200370300200741c8046a41086a20074188066a41086a29020037030020072007290288063703c804200741ec026a200841086a290000370000200741f4026a200841106a290000370000200741fc026a200841186a29000037000020074184036a200841206a2900003700002007418c036a200841286a280000360000200720082900003700e4022011410171210902400240200141286a2802002208450d0020012008417f6a360228200141246a22082008280200220841016a220b41002001411c6a2802002210200b2010491b6b360200200141206a28020020084102746a280200220b200141186a28020022084f0d05200141146a280200200b4107746a220841046a200820082d00004108461b10e385808000200841053a0004200841083a000020082009360234200820072900e1023700052008410d6a200741e1026a41086a290000370000200841156a200741e1026a41106a2900003700002008411d6a200741e1026a41186a290000370000200841256a200741e1026a41206a2900003700002008412c6a20074188036a290000370000200820072903c804370238200841c0006a200741c8046a41086a290300370200200841c8006a200741c8046a41106a290300370200200841d0006a200741c8046a41186a290300370200200841d8006a200741c8046a41206a290300370200200841e0006a200741f0046a2802003602000c010b0240200141186a28020022082001280210470d00200141106a2008109e86808000200128021821080b200141146a28020020084107746a220841053a0004200841083a0000200820072900e10237000520082009360234200820072903c8043702382008410d6a200741e1026a41086a290000370000200841156a200741e1026a41106a2900003700002008411d6a200741e1026a41186a290000370000200841256a200741e1026a41206a2900003700002008412c6a20074188036a290000370000200841c0006a200741c8046a41086a290300370200200841c8006a200741c8046a41106a290300370200200841d0006a200741c8046a41186a290300370200200841d8006a200741c8046a41206a290300370200200841e0006a200741f0046a28020036020020012001280218220b41016a3602180b2002200f410f71200f410476200c4101711b41246c6a2208200b3602044100210b200841003a0000200741f8046a200741086a200a10da8d808000200720023602f404200741033a00c80420074188066a2001200741c8046a20032004200520061081868080002007280288064102460d00200741c8006a41086a200741a4066a290200370300200741c8006a41106a20074188066a41246a280200360200200741c0006a200741e8066a280200360200200741386a200741e0066a290200370300200741306a200741d8066a290200370300200741186a41106a200741d0066a290200370300200741186a41086a200741c8066a2902003703002007200729029c06370348200720072902c006370318200728028c06220341087621022007280298062105200728029406210420072f019206210c20072d009106210920072d009006210620072802b006210120072802b406210f20072802b806210820072802bc06210a0c040b200728028c0621022000410236020020002002360204200728029c044129490d0520072802f40341002802c0a3c68000118080808000000c050b410441c00410b280808000000b2010200b41e491c68000109481808000000b200b200841d4d7c2800010f980808000000b200728029c044129490d0020072802f40341002802c0a3c68000118080808000000b200020023b0005200020053602102000200436020c2000200c3b010a200020093a0009200020063a0008200020033a00042000200b360200200020072903483702142000200a360234200020083602302000200f36022c20002001360228200041076a20024110763a00002000411c6a200741c8006a41086a290300370200200041246a200741c8006a41106a280200360200200041e0006a200741c0006a280200360200200041d8006a200741386a290300370200200041d0006a200741306a290300370200200041c8006a200741186a41106a290300370200200041c0006a200741186a41086a290300370200200020072903183702380b200741f0066a2480808080000bb11503057f017e157f23808080800041b0036b22072480808080002007200536020420072004360200024002400240024002400240024002400240024002400240024020022d00000d00200228020421020c010b200741206a200241196a290000370300200741186a200241116a290000370300200741106a200241096a290000370300200720022900013703082003280208220541017621020240024020054101710d002002200328020422054b0d05200741003a00ec01200720023602e801200720032802003602e4010c010b2002200328020422054b0d05200220054f0d062007200328020022053602e401200720023602e801200741ed016a200520026a2d000041f001713a0000200741013a00ec010b200741cc026a2001200741086a200741e4016a10878680800020072802d002210220072802cc020d010b0240200141286a28020022052001411c6a22082802002204470d00200810a889808000200128021c2104200128022821050b200141206a280200200141246a28020020056a22054100200420052004491b6b4102746a20023602002001200128022841016a360228200141186a280200220420024d0d05200141146a28020020024107746a22022d00002104200741296a200241016a41df0010848e8080001a200241043a0004200241083a0000200228026021092007280200210520072802042108024020044108470d00200741e4016a2007412c6a41dc0010848e8080001a200720033602c802200720093602c002200720013602c402200741cc026a2001200741e4016a200320052008200610818680800020072802cc0222034102460d022003410147210a20072802ac03210b20072902a403210c20072802a0032104200728029c03210520072802980321062007280294032108200728029003210d200728028c03210e200728028803210f2007280284032110200728028003211120072802fc02211220072802f802211320072802f402211420072802f002211520072802ec02211620072802e802211720072802e402211820072802e002211920072802dc02211a20072802d802211b20072802d402211c20072802d0022102410821034100211d0c090b2003280208211d2003280204210b2003280200210a200741e4016a410172200741296a41df0010848e8080001a200741e0016a200241fc006a280000360200200741d8016a200241f4006a290000370300200741c8016a41086a200241ec006a290000370300200720013602c402200720033602c802200720043a00e401200720022900643703c801200741cc026a2001200741e4016a200320052008200610818680800020072802cc0222034102460d0120072902a803210c20072802a403211e20072802a0032104200728029c03210520072802980321062007280294032108200728029003210d200728028c03210e200728028803210f2007280284032110200728028003211120072802fc02211220072802f802211320072802f402211420072802f002211520072802ec02211620072802e802211720072802e402211820072802e002211a20072802dc02211b20072802d802211c20072802d402211f20072802d0022102024020034101460d00201d410176210302400240201d4101710d0002402003200b4b0d00410021190c020b2003200b41f492c68000109581808000000b2003200b4b0d082003200b4f0d09200a20036a2d00004170712120410121190b200c422088a7210b4100211d200741cc026a41286a22214100360200200741cc026a200a200a20036a10f58d80800020074184026a220a41286a2021280200360200200a41206a200741cc026a41206a290200370200200a41186a200741cc026a41186a290200370200200a41106a200741cc026a41106a29020037020041082103200a41086a200741cc026a41086a290200370200200a20072902cc02370200200741b1026a20203a0000200741b0026a20193a0000200741f0016a200741c8016a41086a290300370200200741f8016a200741c8016a41106a29030037020020074180026a200741c8016a41186a280200360200200720093602e401200720072903c8013702e801200141dc006a200741e4016a10958d8080001a200c422086201ead84210c4101210a201a2119201b211a201c211b201f211c0c090b200741a8016a41186a200741c8016a41186a280200360200200741a8016a41106a200741c8016a41106a290300370300200741a8016a41086a200741c8016a41086a290300370300200720072903c8013703a801200241ff01712103200241807e71211d4100210a201821192017211820162117201521162014211520132114201221132011211220102111200f2110200e210f200d210e2008210d200621082005210620042105201e21042009210b201f21020c080b200041023a000420002002360200200420042802002201417f6a36020020014101470d08200710e28a8080000c080b20072802d0022101200041023a0004200020013602000c070b2002200541f492c68000109581808000000b20022005418493c68000109581808000000b20022005419493c6800010f980808000000b2002200441e4d7c2800010f980808000000b2003200b418493c68000109581808000000b2003200b419493c6800010f980808000000b20074188016a41186a200741a8016a41186a28020036020020074188016a41106a200741a8016a41106a29030037030020074188016a41086a200741a8016a41086a290300370300200720072903a801370388012003201d7221090240024020012802282203450d0020012003417f6a36022820012001280224220341016a221d4100200128021c221e201d201e491b6b360224200128022020034102746a28020022032001280218221d4f0d03200128021420034107746a220141046a200120012d00004108461b10e3858080002001200b3602602001200c37025820012004360254200120053602502001200636024c200120083602482001200d3602442001200e3602402001200f36023c2001201036023820012011360234200120123602302001201336022c2001201436022820012015360224200120163602202001201736021c20012018360218200120193602142001201a3602102001201b36020c2001201c3602082001200236020420012009360200200141fc006a200741a0016a280200360200200141f4006a20074198016a290300370200200141ec006a20074188016a41086a29030037020020012007290388013702640c010b0240200128021822032001280210470d00200141106a2003109e86808000200128021821030b200128021420034107746a2203200b3602602003200c37025820032004360254200320053602502003200636024c200320083602482003200d3602442003200e3602402003200f36023c2003201036023820032011360234200320123602302003201336022c2003201436022820032015360224200320163602202003201736021c20032018360218200320193602142003201a3602102003201b36020c2003201c3602082003200236020420032009360200200341fc006a200741a0016a280200360200200341f4006a20074198016a290300370200200341ec006a20074190016a290300370200200320072903880137026420012001280218220341016a3602180b2000200a3a0004200020033602000b200741b0036a2480808080000f0b2003201d41d4d7c2800010f980808000000b840502047f047e23808080800041b0016b2204248080808000024020022d000022054103460d0002400240024002402005417f6a0e020100040b20022d00010d010c030b200241016a21050c010b200241026a21050b200441086a41186a200541186a290000370300200441086a41106a200541106a290000370300200441086a41086a200541086a29000037030020042005290000370308200328020421062003280200210520044184016a41286a2207410036020020044184016a2005200520066a10f686808000200441d8006a41286a20072802002205360200200441d8006a41206a20044184016a41206a2902002208370300200441d8006a41186a20044184016a41186a2902002209370300200441d8006a41106a20044184016a41106a290200220a370300200441d8006a41086a20044184016a41086a290200220b370300200441086a41286a200b370300200441386a200a370300200441c0006a2009370300200441c8006a2008370300200441d0006a20053602002004200429028401220837035820042008370328200441d4006a20032f01083b0100200041dc006a200441086a10958d8080001a0b024020012d000022034103460d0002400240024020030e020103000b2001280224220320032802002203417f6a36020020034101470d02200141246a21030c010b2001280204220320032802002203417f6a36020020034101470d01200141046a21030b200310e28a8080000b20012002290200370200200141286a200241286a280200360200200141206a200241206a290200370200200141186a200241186a290200370200200141106a200241106a290200370200200141086a200241086a290200370200200441b0016a2480808080000bab7803097f027e067f23808080800041f0066b2207248080808000200741086a41086a200341086a28020036020020072003290200370308024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020022d0000417c6a41ff01712208410420084104491b0e050001020304000b200728021022084101762203200728020c22014b0d04200728020821024100210920074188066a41286a220a410036020020074188066a200220036a200220016a10f58d808000200741186a41286a200a280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a29020037030020072007290288063703182008410171210a200541204b410174210b4105210c2004210d200521084100210e0c310b200741f0036a41286a200241d8006a290200370300200741f0036a41206a200241d0006a290200370300200741f0036a41186a200241c8006a290200370300200741f0036a41106a200241c0006a290200370300200741f0036a41086a2208200241386a290200370300200720022902303703f003200741a8026a41286a2002412c6a280200360200200741a8026a41206a200241246a290200370300200741a8026a41186a2002411c6a290200370300200741a8026a41106a200241146a290200370300200741a8026a41086a2002410c6a290200370300200720022902043703a802200720072802f0033602dc0220072008280200200741f0036a412c6a2802002208200841284b22081b3602d802200720072802f403200741f0036a410472220f20081b3602d402200241046a2108200741086a200741d4026a10db8d808000220a20072802d802220e41017420072802dc02220c6b2202460d210c220b200741f0036a41286a200241d0006a29020037030020074190046a200241c8006a29020037030020074188046a200241c0006a29020037030020074180046a200241386a290200370300200741f0036a41086a200241306a29020022103703002007200229022822113703f003200241086a280200210d20022d0004210f200720113e02b00220072010a72007419c046a2802002208200841284b22081b3602ac02200720072802f403200741f0036a41047220081b3602a802200241046a210a200741086a200741a8026a10db8d808000210820072802ac02220c410174210b20072802b002210e024002402008450d002008200b200e6b470d012003200328020820086a36020820074188066a2001200a2003200420052006108586808000200728028806210420072d008c0622024102460d1e20072802b0022208410176220520072802ac0222014b0d0b20072802a80221034100210b200741b0066a220a410036020020074188066a200320056a200320016a10f58d808000200741206a20074188066a41106a290200370300200741186a41106a20074188066a41186a290200370300200741186a41186a20074188066a41206a290200370300200741186a41206a200a28020036020020072007290290063703182008410171210d200728028c06210a2007280288062108200245210e4106210c0c200b200b200e460d20200e4101762208200c4f0d0520072802a80220086a2d0000210c41002d00fca3c680001a41c00441002802c8a3c68000118180808000002208450d04200841023a0000200841023a009c04200841023a00f803200841023a00d403200841023a00b003200841023a008c03200841023a00e802200841023a00c402200841023a00a002200841023a00fc01200841023a00d801200841023a00b401200841023a009001200841023a006c200841023a0048200841023a00240240024020072802ac02220b41017420072802b00222096b4101470d00200741c8046a41026a200241056a220a41026a2d00003a000020074188066a41086a2002410c6a220241086a29020037030020074188066a41106a200241106a29020037030020074188066a41186a200241186a2802003602002007200a2f00003b01c80420072002290200370388060c010b200941016a220f410176220d200b4b0d0720072802a802210220074188066a41286a2209410036020020074188066a2002200d6a2002200b6a10f58d808000200741c8046a41286a2009280200360200200741c8046a41206a20074188066a41206a290200370300200741c8046a41186a20074188066a41186a290200370300200741c8046a41106a20074188066a41106a290200370300200741c8046a41086a20074188066a41086a29020037030020072007290288063703c804200741ac046a200a41086a290000370000200741b4046a200a41106a290000370000200741bc046a200a41186a290000370000200741c4046a200a41206a2800003600002007200a2900003700a404200f410171210a02400240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a220b41002001411c6a280200220d200b200d491b6b360200200141206a28020020024102746a280200220d200141186a28020022024f0d0a200141146a280200200d4107746a220241046a200220022d00004108461b10e385808000200241063a0004200241083a00002002200a36022c200220072900a1043700052002410d6a200741a1046a41086a290000370000200241156a200741a1046a41106a2900003700002002411d6a200741a1046a41186a290000370000200241246a200741c0046a290000370000200220072903c804370230200241386a200741c8046a41086a290300370200200241c0006a200741c8046a41106a290300370200200241c8006a200741c8046a41186a290300370200200241d0006a200741e8046a290300370200200241d8006a200741f0046a2802003602000c010b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200141146a28020020024107746a220241063a0004200241083a0000200220072900a1043700052002200a36022c200220072903c8043702302002410d6a200741a1046a41086a290000370000200241156a200741a1046a41106a2900003700002002411d6a200741a1046a41186a290000370000200241246a200741c0046a290000370000200241386a200741c8046a41086a290300370200200241c0006a200741c8046a41106a290300370200200241c8006a200741c8046a41186a290300370200200241d0006a200741e8046a290300370200200241d8006a200741f0046a28020036020020012001280218220d41016a3602180b4100210f0b2008200c410f71200c410476200e4101711b41246c6a2202200f3a0000200220072f01c8043b00012002200d3602042002200729038806370208200241036a200741c8046a41026a2d00003a0000200241106a20074188066a41086a290300370200200241186a20074188066a41106a290300370200200241206a20074188066a41186a280200360200200741033a00cc04200720083602f804200741073a00c80420074188066a2001200741c8046a20032004200520061084868080002007280288064102460d1c200741c8006a41086a200741a4066a290200370300200741c8006a41106a20074188066a41246a280200360200200741c0006a200741e8066a280200360200200741186a41206a200741e0066a290200370300200741186a41186a200741d8066a290200370300200741186a41106a200741d0066a290200370300200741186a41086a200741c8066a2902003703002007200729029c06370348200720072902c006370318200728028c06220c41087621022007280298062105200728029406210420072f019206210f20072d009106210920072d009006210b20072802b006210320072802b406210d20072802b806210820072802bc06210a0c1e0b200e20086a220b410176220e200c4b0d0720072802a802210220074188066a41286a220d410036020020074188066a2002200e6a2002200c6a10f58d808000200741fc056a200d280200360200200741f4056a20074188066a41206a290200370200200741ec056a20074188066a41186a290200370200200741e4056a20074188066a41106a290200370200200741dc056a20074188066a41086a2202290200370200200741d4056a200729028806370200200741b4056a200a41086a290200370200200741bc056a200a41106a290200370200200741c4056a200a41186a290200370200200741cc056a200a41206a2802003602002003200328020820086a3602082007200b4101713602d0052007200a2902003702ac05200741063a00a80520074188066a2001200741a8056a20032004200520061084868080002007280288064102460d1b200728028c062103200741c8046a200241dc0010848e8080001a20074188066a200741a8026a200810da8d80800002400240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a220441002001411c6a280200220520042005491b6b360200200141206a28020020024102746a2802002204200141186a28020022024f0d0a200141146a28020020044107746a220241046a200220022d00004108461b10e38580800020022003360204200241083a0000200241086a200741c8046a41dc0010848e8080001a0c010b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200141146a28020020024107746a22022003360204200241083a0000200241086a200741c8046a41dc0010848e8080001a20012001280218220441016a3602180b200741206a2007419c066a290200370300200741286a200741a4066a290200370300200741306a200741ac066a290200370300200741386a200741b4066a2802003602002007200729029406370318200728028806210d200728028c062108200728029006210a4100210b4106210c0c1d0b2002280230210a20074198046a2002412c6a280200360200200741f0036a41206a200241246a29020037030020074188046a2002411c6a29020037030020074180046a200241146a290200370300200741f0036a41086a2002410c6a290200370300200720022902043703f003200728020c220e41017420072802102208460d1920084101762202200e4f0d09200728020820026a2d000021022003200328020841016a360208200a2002410f71200241047620084101711b41246c6a22022d00002108200241023a000002400240024020084102460d00200741e0006a41096a200241096a290000370000200741e0006a41116a200241116a290000370000200741e0006a41196a200241196a290000370000200741e0006a41206a200241206a280000360000200720083a00602007200229000137006120074188066a2001200741e0006a2003200420052006108586808000200728028806210320072d008c0622044102460d0220022003360204200241003a000020040d0120074193066a200741f0036a41086a2903003700002007419b066a200741f0036a41106a290300370000200741a3066a200741f0036a41186a290300370000200741ab066a200741f0036a41206a290300370000200741b3066a20074198046a280200360000200720072903f00337008b0620002007290088063700052000410d6a20074188066a41086a290000370000200041156a20074188066a41106a2900003700002000411d6a20074188066a41186a290000370000200041256a20074188066a41206a2900003700002000412c6a200741af066a2900003700002000200a360234200041073a0004200041013602000c320b2003280208220641017622082003280204220e4b0d0c200541204b410174210b2003280200210320074188066a41286a220c410036020020074188066a200320086a2003200e6a10f58d808000200741c8046a41286a200c280200360200200741c8046a41206a20074188066a41206a290200370300200741c8046a41186a20074188066a41186a290200370300200741c8046a41106a20074188066a41106a290200370300200741c8046a41086a20074188066a41086a29020037030020072007290288063703c8042006410171210e02400240200141286a2802002203450d0020012003417f6a360228200141246a22032003280200220341016a220841002001411c6a280200220620082006491b6b360200200141206a28020020034102746a2802002208200141186a28020022034f0d0f200141146a28020020084107746a220341046a200320032d00004108461b10e3858080002003200e360234200320053600302003200436002c200320053600102003200436000c200341003a00092003200b3a0008200341053a0004200341083a0000200320072903c804370238200341c0006a200741c8046a41086a290300370200200341c8006a200741d8046a290300370200200341d0006a200741e0046a290300370200200341d8006a200741c8046a41206a290300370200200341e0006a200741f0046a2802003602000c010b0240200141186a28020022032001280210470d00200141106a2003109e86808000200128021821030b200141146a28020020034107746a2203200e360234200320053600302003200436002c200320053600102003200436000c200341003a00092003200b3a0008200341053a0004200341083a0000200320072903c804370238200341c0006a200741c8046a41086a290300370200200341c8006a200741d8046a290300370200200341d0006a200741e0046a290300370200200341d8006a200741e8046a290300370200200341e0006a200741f0046a28020036020020012001280218220841016a3602180b20022008360204200241003a00000b200741d0006a20074184046a290200370300200741d8006a2007418c046a280200360200200720072902fc033703484100210e2007280298042108200728029404210d200728029004210320072802f803210520072802f403210420072f01f203210f20072d00f103210920072d00f003210b4107210c0c2f0b2000410236020020002003360204024020072d00f00322004103460d0002400240024020000e020103000b200728029404220020002802002200417f6a36020020004101470d0220074194046a21000c010b20072802f403220020002802002200417f6a36020020004101470d01200741f0036a41047221000b200010e28a8080000b200a41002802c0a3c68000118080808000000c2f0b200741c8046a41286a200241d8006a290200370300200741c8046a41206a200241d0006a290200370300200741c8046a41186a200241c8006a290200370300200741c8046a41106a200241c0006a290200370300200741c8046a41086a220a200241386a290200370300200720022902303703c804200228022c2108200741a8026a41286a200241286a280200360200200741a8026a41206a200241206a290200370300200741a8026a41186a200241186a290200370300200741a8026a41106a200241106a290200370300200741a8026a41086a200241086a290200370300200720022902003703a802200720072802c80436028c012007200a280200200741f4046a2802002202200241284b22021b36028801200720072802cc04200741c8046a41047220021b360284010240024002400240024002400240024002400240200741086a20074184016a10db8d808000220a2007280288012202410174200728028c01220b6b220e470d00200a200728020c41017420072802106b460d010b200a200e490d012007280210200a6a220e4101762202200728020c220c4f0d02200728020820026a2d000021022003200a20032802086a41016a36020820082002410f712002410476200e4101711b41246c6a22022d0000210a200241023a0000200a4102460d0320074184026a41096a200241096a29000037000020074184026a41116a200241116a29000037000020074184026a41196a200241196a29000037000020074184026a41206a200241206a2800003600002007200a3a008402200720022900013700850220074188066a200120074184026a2003200420052006108586808000200728028806210320072d008c0622044102460d0520022003360204200241003a000020040d04200041346a20074184016a10d98d80800020002008360230200041013602002000412c6a200741d0026a280200360200200041246a200741c8026a2903003702002000411c6a200741c0026a290300370200200041146a200741b8026a2903003702002000410c6a200741b0026a290300370200200020072903a8023702040c060b20072005360298042007200436029404200720053602f803200720043602f4034100210e200741003a00f1032007200541204b410174220c3a00f003024020072d00a8024103460d00200741a8026a200741f0036a10e785808000210e0b200b410176220f20024b0d13200728028401210a20074188066a41286a220d410036020020074188066a200a200f6a200a20026a10f58d808000200741186a41286a200d280200360200200741186a41206a20074188066a41206a2202290200370300200741186a41186a20074188066a41186a2209290200370300200741186a41106a20074188066a41106a2212290200370300200741186a41086a20074188066a41086a221329020037030020072007290288063703182003280204210a2003280200210f20032802082103200728028c012114200728028801211520072f00f103211620072d00f3032117200d200741a8026a41286a2802003602002002200741a8026a41206a2903003703002009200741a8026a41186a2903003703002012200741a8026a41106a2903003703002013200741a8026a41086a290300370300200720072903a802370388062003201541017420146b6a220341017621020240024020034101710d002002200a4b0d16200741003a00dc02200720023602d8022007200f3602d4020c010b2002200a4b0d162002200a4f0d172007200f3602d402200720023602d802200741dd026a200f20026a2d000041f001713a0000200741013a00dc020b2004411076210f20044108762109200b410171210a201620174110747221022001200620074188066a200741d4026a1083868080002004210b200521012005210d0c070b200b200a41016a220d6a220c410176220e20024b0d16200728028401210320074188066a41286a2206410036020020074188066a2003200e6a200320026a10f58d80800020074190016a41286a200628020036020020074190016a41206a20074188066a41206a29020037030020074190016a41186a20074188066a41186a29020037030020074190016a41106a20074188066a41106a29020037030020074190016a41086a20074188066a41086a290200370300200741c0016a41086a200741a8026a41086a290300370300200741c0016a41106a200741a8026a41106a290300370300200741c0016a41186a200741a8026a41186a290300370300200741c0016a41206a200741a8026a41206a290300370300200741c0016a41286a200741a8026a41286a280200360200200720072902880637039001200720072903a8023703c001200728028c01200a6a220b41017622022007280288012203490d0520022003418cd5c2800010f980808000000b2002200c418cd5c2800010f980808000000b20032802082206410176220a2003280204220e4b0d15200541204b410174210b2003280200210320074188066a41286a220c410036020020074188066a2003200a6a2003200e6a10f58d808000200741f0036a41286a200c280200360200200741f0036a41206a20074188066a41206a290200370300200741f0036a41186a20074188066a41186a290200370300200741f0036a41106a20074188066a41106a290200370300200741f0036a41086a20074188066a41086a29020037030020072007290288063703f0032006410171210e02400240200141286a2802002203450d0020012003417f6a360228200141246a22032003280200220341016a220a41002001411c6a2802002206200a2006491b6b360200200141206a28020020034102746a280200220a200141186a28020022034f0d18200141146a280200200a4107746a220341046a200320032d00004108461b10e3858080002003200e360234200320053600302003200436002c200320053600102003200436000c200341003a00092003200b3a0008200341053a0004200341083a0000200320072903f003370238200341c0006a200741f0036a41086a290300370200200341c8006a20074180046a290300370200200341d0006a20074188046a290300370200200341d8006a200741f0036a41206a290300370200200341e0006a20074198046a2802003602000c010b0240200141186a28020022032001280210470d00200141106a2003109e86808000200128021821030b200141146a28020020034107746a2203200e360234200320053600302003200436002c200320053600102003200436000c200341003a00092003200b3a0008200341053a0004200341083a0000200320072903f003370238200341c0006a200741f0036a41086a290300370200200341c8006a20074180046a290300370200200341d0006a20074188046a290300370200200341d8006a20074190046a290300370200200341e0006a20074198046a28020036020020012001280218220a41016a3602180b2002200a360204200241003a00000b200728028c01220a410176220320072802880122044b0d1620072802840121024100210e20074188066a41286a2205410036020020074188066a200220036a200220046a10f58d808000200741186a41286a2005280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a290200370300200720072902880637031820072d00a802210c20072d00ac02210b20072d00ad02210920072f01ae02210f20072802b002210120072802b402210520072f00a902210220072d00ab022103200741c8006a41106a200741a8026a41206a280200360200200741c8006a41086a200741a8026a41186a290300370300200720072903b802370348200a410171210a2002200341107472210220072802d002210d20072802cc0221040c030b2000410236020020002003360204200741a8026a10e585808000200841002802c0a3c68000118080808000000b20072802f4044129490d3020072802cc0441002802c0a3c68000118080808000000c300b20072802840120026a2d0000210e41002d00fca3c680001a41c00441002802c8a3c68000118180808000002203450d14200c410171210c200341023a009c04200341023a00f803200341023a00d403200341023a00b003200341023a008c03200341023a00e802200341023a00c402200341023a00a002200341023a00fc01200341023a00d801200341023a00b401200341023a009001200341023a006c200341023a0048200341023a0024200341023a0000200741b3066a200741c0016a41286a280200360000200741ab066a200741c0016a41206a290300370000200741a3066a200741c0016a41186a2903003700002007419b066a200741c0016a41106a29030037000020074193066a200741c8016a290300370000200720072903c00137008b06200141106a210f02400240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a22064100200128021c220920062009491b6b360200200141206a28020020024102746a2802002206200141186a220928020022024f0d17200141146a28020020064107746a220241046a200220022d00004108461b10e385808000200241083a00002002200c360234200220083602302002200729008806370001200241096a20074188066a41086a290000370000200241116a20074188066a41106a290000370000200241196a20074188066a41186a290000370000200241216a20074188066a41206a290000370000200241286a200741af066a2900003700002002200729039001370238200241c0006a20074190016a41086a290300370200200241c8006a20074190016a41106a290300370200200241d0006a20074190016a41186a290300370200200241d8006a20074190016a41206a290300370200200241e0006a20074190016a41286a2802003602000c010b0240200141186a220928020022022001280210470d00200f2002109e86808000200928020021020b200141146a28020020024107746a220241083a000020022007290088063700012002200c360234200220083602302002200729039001370238200241096a20074188066a41086a290000370000200241116a20074188066a41106a290000370000200241196a20074188066a41186a290000370000200241216a20074188066a41206a290000370000200241286a200741af066a290000370000200241c0006a20074190016a41086a290300370200200241c8006a20074190016a41106a290300370200200241d0006a20074190016a41186a290300370200200241d8006a20074190016a41206a290300370200200241e0006a20074190016a41286a28020036020020012001280218220641016a3602180b2003200e410f71200e410476200b4101711b41246c6a2208200636020441002102200841003a0000200541204b410174210c0240200728020c220841017420072802102206200a6a220e470d0020074188066a20074184016a200a10da8d808000200741c8006a41106a200741f0016a41106a280200360200200741c8006a41086a200741f0016a41086a290200370300200741186a41086a20074194066a290200370300200741186a41106a2007419c066a290200370300200741306a200741a4066a290200370300200741186a41206a20074188066a41246a290200370300200741c0006a200741b4066a280200360200200720072902f0013703482007200729028c063703182004411076210f20044108762109200728028806210a2004210b200521012005210d200321084100210e0c010b200e410176220b20084f0d182006200d6a2212410176220d20084b0d1620072802082202200b6a2d0000210620074188066a41286a220b410036020020074188066a2002200d6a200220086a10f58d808000200741f0036a41286a200b280200360200200741f0036a41206a20074188066a41206a290200370300200741f0036a41186a20074188066a41186a290200370300200741f0036a41106a20074188066a41106a290200370300200741f0036a41086a20074188066a41086a29020037030020072007290288063703f0032012410171210b0240024020012802282202450d0020012002417f6a360228200141246a22022002280200220241016a22084100200128021c220d2008200d491b6b360200200141206a28020020024102746a2802002208200128021822024f0d19200141146a28020020084107746a220241046a200220022d00004108461b10e3858080002002410b6a41003a0000200241003b0009200220053602102002200436020c2002200c3a0008200241053a0004200241083a00002002200b360234200220053602302002200436022c200220072902f001370218200241206a200741f0016a41086a290200370200200241286a200741f0016a41106a280200360200200220072903f003370238200241c0006a200741f0036a41086a290300370200200241c8006a200741f0036a41106a290300370200200241d0006a20074188046a290300370200200241d8006a200741f0036a41206a290300370200200241e0006a200741f0036a41286a2802003602000c010b024020092802002202200f280200470d00200f2002109e86808000200928020021020b200141146a28020020024107746a220241003b0009200220053602102002200436020c2002200c3a0008200241053a0004200241083a0000200220072902f0013702182002200b360234200220053602302002200436022c200220072903f0033702382002410b6a41003a0000200241206a200741f0016a41086a290200370200200241286a200741f0016a41106a280200360200200241c0006a200741f0036a41086a290300370200200241c8006a200741f0036a41106a290300370200200241d0006a20074188046a290300370200200241d8006a200741f0036a41206a290300370200200241e0006a200741f0036a41286a28020036020020012001280218220841016a3602180b20032006410f712006410476200e4101711b41246c6a220220083602044100210e200241003a000020074188066a20074184016a200a10da8d808000200741206a20074194066a290200370300200741286a2007419c066a290200370300200741306a200741a4066a290200370300200741386a20074188066a41246a290200370300200741c0006a200741b4066a2802003602002007200729028c06370318200728028806210a4103210c200321080b024020072802f4044129490d0020072802cc0441002802c0a3c68000118080808000000b20042103200121040c2d0b2003200141e491c68000109481808000000b410441c00410b280808000000b2008200c418cd5c2800010f980808000000b200d200b41e491c68000109481808000000b200d200241d4d7c2800010f980808000000b200e200c41e491c68000109481808000000b2004200241d4d7c2800010f980808000000b2005200141e491c68000109481808000000b2002200e418cd5c2800010f980808000000b2008200e41e491c68000109481808000000b2008200341d4d7c2800010f980808000000b200f200241e491c68000109481808000000b2002200a41f492c68000109581808000000b2002200a418493c68000109581808000000b2002200a419493c6800010f980808000000b200e200241e491c68000109481808000000b200a200e41e491c68000109481808000000b200a200341d4d7c2800010f980808000000b2003200441e491c68000109481808000000b410441c00410b280808000000b2006200241d4d7c2800010f980808000000b200d200841e491c68000109481808000000b2008200241d4d7c2800010f980808000000b200b2008418cd5c2800010f980808000000b200720053602b006200720043602ac0620072005360290062007200436028c064100210e200741003a0089062007200541204b410174220b3a008806024020072d00f0034103460d00200741f0036a20074188066a10e785808000210e0b2003280208220c410176210802400240024002400240200c4101710d0020082003280204220c4b0d02200741003a00d004200720083602cc04200720032802003602c8040c010b20082003280204220c4b0d022008200c4f0d032007200328020022033602c804200720083602cc04200741d1046a200320086a2d000041f001713a0000200741013a00d0040b20012006200241046a200741c8046a108386808000410021094107210c2004210d200521080c170b2008200c41f492c68000109581808000000b2008200c418493c68000109581808000000b2008200c419493c6800010f980808000000b200728028c0621040b2000410236020020002004360204200728029c044129490d1320072802f40341002802c0a3c68000118080808000000c130b4100210e0b200728029c044129490d1020072802f40341002802c0a3c68000118080808000000c100b419cd5c28000412a41c8d5c2800010f880808000000b200a200728020c41017420072802106b470d0002400240200541204b0d00200720043602cc04200741d0046a21024100210b0c010b200720043602ec04200741003a00c904200741f0046a21024102210b2004210d0b200220053602002007200b3a00c804200741a8026a200741c8046a10e785808000210e2003280208200a6a220a4101762102200328020421052003280200210302400240200a4101710d00200220054b0d05200741003a0090062007200236028c0620072003360288060c010b200220054b0d05200220054f0d0620072003360288062007200236028c0620074191066a200320026a2d000041f001713a0000200741013a0090060b20012006200820074188066a108386808000200741d4046a2102200e0d0120072802f003210a20072802f403210520072802f8032101200728029c0421034100210e20074188066a41286a2208410036020020074188066a2005200f200341284b22061b220520052001200320061b6a10f686808000200741186a41286a2008280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a29020037030020072007290288063703180c020b200a2002490d07200c410176220a200e4b0d0520072802d402210220074188066a41286a220b410036020020074188066a2002200a6a2002200e6a10f58d808000200741c8046a41286a220a200b280200360200200741c8046a41206a220e20074188066a41206a290200370300200741c8046a41186a220b20074188066a41186a290200370300200741c8046a41106a220d20074188066a41106a290200370300200741c8046a41086a220f20074188066a41086a29020037030020072007290288063703c80441002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d06200241023a009c04200241023a00f803200241023a00d403200241023a00b003200241023a008c03200241023a00e802200241023a00c402200241023a00a002200241023a00fc01200241023a00d801200241023a00b401200241023a009001200241023a006c200241023a0048200241023a0024200241023a000020074190036a41086a200841086a29020037030020074190036a41106a200841106a29020037030020074190036a41186a200841186a29020037030020074190036a41206a200841206a29020037030020074190036a41286a200841286a2802003602002007200829020037039003200741c4036a20072903c804370200200741cc036a200f290300370200200741d4036a200d290300370200200741dc036a200b290300370200200741e4036a200e290300370200200741ec036a200a2802003602002007200c4101713602c003200720023602bc0320074188066a200120074190036a20032004200520061084868080002007280288064102460d08200741c8006a41086a200741a4066a290200370300200741c8006a41106a200741ac066a280200360200200741c0006a200741e8066a280200360200200741386a200741e0066a290200370300200741306a200741d8066a290200370300200741186a41106a200741d0066a290200370300200741186a41086a200741c8066a2902003703002007200729029c06370348200720072902c006370318200728028c06220c41087621022007280298062105200728029406210420072f019206210f20072d009106210920072d009006210b20072802b006210320072802b406210d20072802b806210820072802bc06210a4100210e0c0c0b20072802f003210a20072802f403210520072802f8032101200728029c04210320074188066a41286a2208410036020020074188066a2005200f200341284b220e1b2205200520012003200e1b6a10f686808000200741186a41286a2008280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a29020037030020072007290288063703184101210e0b200741c8006a41086a200241086a290200370300200741c8006a41106a200241106a2802003602002007200229020037034820072802d004210520072802f00421084105210c410021090c0a0b2002200541f492c68000109581808000000b20022005418493c68000109581808000000b20022005419493c6800010f980808000000b200a200e41e491c68000109481808000000b410441c00410b280808000000b41002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d01200241023a0000200241023a009c04200241023a00f803200241023a00d403200241023a00b003200241023a008c03200241023a00e802200241023a00c402200241023a00a002200241023a00fc01200241023a00d801200241023a00b401200241023a009001200241023a006c200241023a0048200241023a0024024020072802dc02200a6a220c410176220d20072802d802220e490d00200d200e418cd5c2800010f980808000000b200c41016a2209410176220f200e4b0d0220072802d402220b200d6a2d0000210d20074188066a41286a2212410036020020074188066a200b200f6a200b200e6a10f58d808000200741c8046a41286a2012280200360200200741c8046a41206a20074188066a41206a290200370300200741c8046a41186a20074188066a41186a290200370300200741c8046a41106a20074188066a41106a290200370300200741c8046a41086a20074188066a41086a29020037030020072007290288063703c804200741ec026a200841086a290000370000200741f4026a200841106a290000370000200741fc026a200841186a29000037000020074184036a200841206a2900003700002007418c036a200841286a280000360000200720082900003700e4022009410171210b02400240200141286a2802002208450d0020012008417f6a360228200141246a22082008280200220841016a220e41002001411c6a280200220f200e200f491b6b360200200141206a28020020084102746a280200220e200141186a28020022084f0d05200141146a280200200e4107746a220841046a200820082d00004108461b10e385808000200841053a0004200841083a00002008200b360234200820072900e1023700052008410d6a200741e1026a41086a290000370000200841156a200741e1026a41106a2900003700002008411d6a200741e1026a41186a290000370000200841256a200741e1026a41206a2900003700002008412c6a20074188036a290000370000200820072903c804370238200841c0006a200741c8046a41086a290300370200200841c8006a200741c8046a41106a290300370200200841d0006a200741c8046a41186a290300370200200841d8006a200741c8046a41206a290300370200200841e0006a200741f0046a2802003602000c010b0240200141186a28020022082001280210470d00200141106a2008109e86808000200128021821080b200141146a28020020084107746a220841053a0004200841083a0000200820072900e1023700052008200b360234200820072903c8043702382008410d6a200741e1026a41086a290000370000200841156a200741e1026a41106a2900003700002008411d6a200741e1026a41186a290000370000200841256a200741e1026a41206a2900003700002008412c6a20074188036a290000370000200841c0006a200741c8046a41086a290300370200200841c8006a200741c8046a41106a290300370200200841d0006a200741c8046a41186a290300370200200841d8006a200741c8046a41206a290300370200200841e0006a200741f0046a28020036020020012001280218220e41016a3602180b2002200d410f71200d410476200c4101711b41246c6a2208200e3602044100210e200841003a0000200741f8046a200741086a200a10da8d808000200720023602f404200741033a00c80420074188066a2001200741c8046a20032004200520061084868080002007280288064102460d00200741c8006a41086a200741a4066a290200370300200741c8006a41106a20074188066a41246a280200360200200741c0006a200741e8066a280200360200200741386a200741e0066a290200370300200741306a200741d8066a290200370300200741186a41106a200741d0066a290200370300200741186a41086a200741c8066a2902003703002007200729029c06370348200720072902c006370318200728028c06220c41087621022007280298062105200728029406210420072f019206210f20072d009106210920072d009006210b20072802b006210320072802b406210d20072802b806210820072802bc06210a0c040b200728028c0621022000410236020020002002360204200728029c044129490d0520072802f40341002802c0a3c68000118080808000000c050b410441c00410b280808000000b200f200e41e491c68000109481808000000b200e200841d4d7c2800010f980808000000b200728029c044129490d0020072802f40341002802c0a3c68000118080808000000b200020023b0005200020053602102000200436020c2000200f3b010a200020093a00092000200b3a00082000200c3a00042000200e360200200020072903483702142000200a360234200020083602302000200d36022c20002003360228200041076a20024110763a00002000411c6a200741c8006a41086a290300370200200041246a200741c8006a41106a280200360200200041e0006a200741c0006a280200360200200041d8006a200741386a290300370200200041d0006a200741306a290300370200200041c8006a200741186a41106a290300370200200041c0006a200741186a41086a290300370200200020072903183702380b200741f0066a2480808080000bb11503057f017e157f23808080800041b0036b22072480808080002007200536020420072004360200024002400240024002400240024002400240024002400240024020022d00000d00200228020421020c010b200741206a200241196a290000370300200741186a200241116a290000370300200741106a200241096a290000370300200720022900013703082003280208220541017621020240024020054101710d002002200328020422054b0d05200741003a00ec01200720023602e801200720032802003602e4010c010b2002200328020422054b0d05200220054f0d062007200328020022053602e401200720023602e801200741ed016a200520026a2d000041f001713a0000200741013a00ec010b200741cc026a2001200741086a200741e4016a10868680800020072802d002210220072802cc020d010b0240200141286a28020022052001411c6a22082802002204470d00200810a889808000200128021c2104200128022821050b200141206a280200200141246a28020020056a22054100200420052004491b6b4102746a20023602002001200128022841016a360228200141186a280200220420024d0d05200141146a28020020024107746a22022d00002104200741296a200241016a41df0010848e8080001a200241043a0004200241083a0000200228026021092007280200210520072802042108024020044108470d00200741e4016a2007412c6a41dc0010848e8080001a200720033602c802200720093602c002200720013602c402200741cc026a2001200741e4016a200320052008200610848680800020072802cc0222034102460d022003410147210a20072802ac03210b20072902a403210c20072802a0032104200728029c03210520072802980321062007280294032108200728029003210d200728028c03210e200728028803210f2007280284032110200728028003211120072802fc02211220072802f802211320072802f402211420072802f002211520072802ec02211620072802e802211720072802e402211820072802e002211920072802dc02211a20072802d802211b20072802d402211c20072802d0022102410821034100211d0c090b2003280208211d2003280204210b2003280200210a200741e4016a410172200741296a41df0010848e8080001a200741e0016a200241fc006a280000360200200741d8016a200241f4006a290000370300200741c8016a41086a200241ec006a290000370300200720013602c402200720033602c802200720043a00e401200720022900643703c801200741cc026a2001200741e4016a200320052008200610848680800020072802cc0222034102460d0120072902a803210c20072802a403211e20072802a0032104200728029c03210520072802980321062007280294032108200728029003210d200728028c03210e200728028803210f2007280284032110200728028003211120072802fc02211220072802f802211320072802f402211420072802f002211520072802ec02211620072802e802211720072802e402211820072802e002211a20072802dc02211b20072802d802211c20072802d402211f20072802d0022102024020034101460d00201d410176210302400240201d4101710d0002402003200b4b0d00410021190c020b2003200b41f492c68000109581808000000b2003200b4b0d082003200b4f0d09200a20036a2d00004170712120410121190b200c422088a7210b4100211d200741cc026a41286a22214100360200200741cc026a200a200a20036a10f58d80800020074184026a220a41286a2021280200360200200a41206a200741cc026a41206a290200370200200a41186a200741cc026a41186a290200370200200a41106a200741cc026a41106a29020037020041082103200a41086a200741cc026a41086a290200370200200a20072902cc02370200200741b1026a20203a0000200741b0026a20193a0000200741f0016a200741c8016a41086a290300370200200741f8016a200741c8016a41106a29030037020020074180026a200741c8016a41186a280200360200200720093602e401200720072903c8013702e801200141dc006a200741e4016a10958d8080001a200c422086201ead84210c4101210a201a2119201b211a201c211b201f211c0c090b200741a8016a41186a200741c8016a41186a280200360200200741a8016a41106a200741c8016a41106a290300370300200741a8016a41086a200741c8016a41086a290300370300200720072903c8013703a801200241ff01712103200241807e71211d4100210a201821192017211820162117201521162014211520132114201221132011211220102111200f2110200e210f200d210e2008210d200621082005210620042105201e21042009210b201f21020c080b200041023a000420002002360200200420042802002201417f6a36020020014101470d08200710e28a8080000c080b20072802d0022101200041023a0004200020013602000c070b2002200541f492c68000109581808000000b20022005418493c68000109581808000000b20022005419493c6800010f980808000000b2002200441e4d7c2800010f980808000000b2003200b418493c68000109581808000000b2003200b419493c6800010f980808000000b20074188016a41186a200741a8016a41186a28020036020020074188016a41106a200741a8016a41106a29030037030020074188016a41086a200741a8016a41086a290300370300200720072903a801370388012003201d7221090240024020012802282203450d0020012003417f6a36022820012001280224220341016a221d4100200128021c221e201d201e491b6b360224200128022020034102746a28020022032001280218221d4f0d03200128021420034107746a220141046a200120012d00004108461b10e3858080002001200b3602602001200c37025820012004360254200120053602502001200636024c200120083602482001200d3602442001200e3602402001200f36023c2001201036023820012011360234200120123602302001201336022c2001201436022820012015360224200120163602202001201736021c20012018360218200120193602142001201a3602102001201b36020c2001201c3602082001200236020420012009360200200141fc006a200741a0016a280200360200200141f4006a20074198016a290300370200200141ec006a20074188016a41086a29030037020020012007290388013702640c010b0240200128021822032001280210470d00200141106a2003109e86808000200128021821030b200128021420034107746a2203200b3602602003200c37025820032004360254200320053602502003200636024c200320083602482003200d3602442003200e3602402003200f36023c2003201036023820032011360234200320123602302003201336022c2003201436022820032015360224200320163602202003201736021c20032018360218200320193602142003201a3602102003201b36020c2003201c3602082003200236020420032009360200200341fc006a200741a0016a280200360200200341f4006a20074198016a290300370200200341ec006a20074190016a290300370200200320072903880137026420012001280218220341016a3602180b2000200a3a0004200020033602000b200741b0036a2480808080000f0b2003201d41d4d7c2800010f980808000000bc90a01057f2380808080004180026b2204248080808000024002400240024002400240024002400240024020012802682205450d0020052002200141ec006a2802002802181183808080000022050d010b200441e0006a200128025020022003200141d4006a280200280214118680808000000240024020042802602203418080808078470d0020044180016a41186a2205200241186a29000037030020044180016a41106a2203200241106a29000037030020044180016a41086a2206200241086a290000370300200420022900003703800141002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b20042802682106200428026421052001280200450d032001280204450d0241d8d5c28000108781808000000b200141858080807836020020012004290380013702042001410c6a2006290300370200200141146a20032903003702002001411c6a200529030037020020004101360200200020013602040c070b02402001280200450d0002402001280204450d0041e8d5c28000108781808000000b2001417f360204200141086a28020021032001410c6a2802002106200441a0016a200241186a29000037020020044180016a41186a200241106a29000037020020044180016a41106a200241086a29000037020020042005360284012004418180808078360280012004200229000037028801200320044180016a200628020c11848080800000200141003602040b20042005200141106a10f2858080000c020b2001417f360204200141086a28020021072001410c6a280200210820044180016a410c6a200636020020044180016a41086a2005360200200441a8016a200241186a290000370200200441a0016a200241106a29000037020020044180016a41186a200241086a2900003702002004428280808088808080807f370280012004200229000037029001200720044180016a200828020c11848080800000200141003602040b200441e0006a41186a200241186a290000370300200441e0006a41106a200241106a290000370300200441e0006a41086a200241086a2900003703002004200229000037036020044180016a200441e0006a20052006200141106a10e88580800020042d00800122064108460d01200420042d0083013a0003200420042f0081013b00012004280284012107200441086a20044180016a41086a41d80010848e8080001a20042007360204200420063a00002003450d00200541002802c0a3c68000118080808000000b20044180016a200441e00010848e8080001a200441f8016a200241186a290000370200200441f0016a200241106a290000370200200441e8016a200241086a290000370200200420022900003702e001200141286a2802002202450d0120012002417f6a360228200141246a22022002280200220241016a220541002001411c6a280200220320052003491b6b360200200141206a28020020024102746a2802002202200141186a28020022054f0d04200141146a28020020024107746a220141046a200120012d00004108461b10e385808000200120044180016a41800110848e8080001a0c020b200428028401210120004101360200200020013602042003450d02200541002802c0a3c68000118080808000000c020b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200141146a28020020024107746a20044180016a41800110848e8080001a20012001280218220241016a3602180b20004100360200200020023602040b20044180026a2480808080000f0b2002200541d4d7c2800010f980808000000bc90a01057f2380808080004180026b2204248080808000024002400240024002400240024002400240024020012802682205450d0020052002200141ec006a2802002802181183808080000022050d010b200441e0006a200128025020022003200141d4006a280200280214118680808000000240024020042802602203418080808078470d0020044180016a41186a2205200241186a29000037030020044180016a41106a2203200241106a29000037030020044180016a41086a2206200241086a290000370300200420022900003703800141002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b20042802682106200428026421052001280200450d032001280204450d0241d8d5c28000108781808000000b200141858080807836020020012004290380013702042001410c6a2006290300370200200141146a20032903003702002001411c6a200529030037020020004101360200200020013602040c070b02402001280200450d0002402001280204450d0041e8d5c28000108781808000000b2001417f360204200141086a28020021032001410c6a2802002106200441a0016a200241186a29000037020020044180016a41186a200241106a29000037020020044180016a41106a200241086a29000037020020042005360284012004418180808078360280012004200229000037028801200320044180016a200628020c11848080800000200141003602040b20042005200141106a10f0858080000c020b2001417f360204200141086a28020021072001410c6a280200210820044180016a410c6a200636020020044180016a41086a2005360200200441a8016a200241186a290000370200200441a0016a200241106a29000037020020044180016a41186a200241086a2900003702002004428280808088808080807f370280012004200229000037029001200720044180016a200828020c11848080800000200141003602040b200441e0006a41186a200241186a290000370300200441e0006a41106a200241106a290000370300200441e0006a41086a200241086a2900003703002004200229000037036020044180016a200441e0006a20052006200141106a10ec8580800020042d00800122064108460d01200420042d0083013a0003200420042f0081013b00012004280284012107200441086a20044180016a41086a41d80010848e8080001a20042007360204200420063a00002003450d00200541002802c0a3c68000118080808000000b20044180016a200441e00010848e8080001a200441f8016a200241186a290000370200200441f0016a200241106a290000370200200441e8016a200241086a290000370200200420022900003702e001200141286a2802002202450d0120012002417f6a360228200141246a22022002280200220241016a220541002001411c6a280200220320052003491b6b360200200141206a28020020024102746a2802002202200141186a28020022054f0d04200141146a28020020024107746a220141046a200120012d00004108461b10e385808000200120044180016a41800110848e8080001a0c020b200428028401210120004101360200200020013602042003450d02200541002802c0a3c68000118080808000000c020b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200141146a28020020024107746a20044180016a41800110848e8080001a20012001280218220241016a3602180b20004100360200200020023602040b20044180026a2480808080000f0b2002200541d4d7c2800010f980808000000b99950105107f017e017f047e397f23808080800041c0086b2205248080808000024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020022d00002206417c6a41ff01712207410420074104491b417e6a0e03020108000b2000200241e00010848e8080001a0c200b200241056a210820022d000421092002280230220a2d00004102460d0141002106410121070c020b200541c8036a41286a200241d0006a290200370300200541e8036a200241c8006a290200370300200541c8036a41186a200241c0006a290200370300200541c8036a41106a200241386a290200370300200541c8036a41086a2207200241306a290200370300200520022902283703c803200541fe036a200241076a2d00003a000020054198016a41086a200241146a29020037030020054198016a41106a2002411c6a29020037030020054198016a41186a200241246a2802003602002005200241056a2f00003b01fc0320052002410c6a29020037039801200241086a280200210b20022d000421062003280200210a2003280204210820032802082109024002400240024002400240024002400240024020040d002007280200200541f4036a2802002204200441284b1b2207417f6a210c2007450d11200741017420096a20052802c803417f736a2209410176210720052802cc03200541c8036a410472200441284b1b200c6a2d0000410f71210c024020094101710d00200720084d0d022007200841f492c68000109581808000000b200720084b0d1220072008490d0220072008419493c6800010f980808000000b2009410176210702400240200941017122040d00200720084d0d012007200841f492c68000109581808000000b200720084b0d13200720084f0d14200a20076a2d000041707121090b2005420037028002200541003602d804200520073602d4042005200a3602d004200541d8016a200541d0046a4100200510e38d8080001a2004450d06200941f00171210820052d0084024101710d04200541d8016a41046a2104200541d8016a41044128200528028002220d41284b22091b6a280200220c200d412820091b460d022004200541d8016a41286a20091b210420052802d801200541d8016a20091b21090c030b410021042005410036028004200c410474210e20054180046a41046a210f410121080c070b200a20076a22042d0000210941002108200541d8016a41286a220d4100360200200541d8016a200a200410f686808000200541d0046a41286a2210200d280200220d360200200541d0046a41206a2211200541d8016a41206a290200370300200541d0046a41186a2212200541d8016a41186a290200370300200541d0046a41106a2213200541d8016a41106a290200370300200541d0046a41086a2214200541d8016a41086a290200370300200520052902d80122153703d004200541d0046a41047221042009417071200c72211602400240200541d0046a41044128200d41284b22091b6a280200220c200d412820091b460d002004201020091b21042015a7200541d0046a20091b21090c010b200541d0046a10f88680800020052802d404210c20052802d00421090b2009200c6a20163a00002004200428020041016a360200200541b0066a41086a20142903002215370300200541b0066a41106a20132903002217370300200541b0066a41186a20122903002218370300200541b0066a41206a20112903002219370300200541b0066a41286a20102802002204360200200520052903d004221a3703b0062005418c046a201537020020054194046a20173702002005419c046a2018370200200541a4046a2019370200200541ac046a200436020020054101360280042005201a3702840420054180046a41046a210f0c050b200541d8016a10f78d80800020052802dc01210c20052802d80121090b2009200c6a20083a00002004200428020041016a3602000c010b20052802dc012005280280022204200441284b22041b2209450d0f200920052802d801200541d8016a20041b6a417f6a220420042d00002008410476723a00000b200520052802840241016a360284020b200520052802c8033602d8042005200541d0036a280200200541f4036a2802002204200441284b22041b3602d404200520052802cc03200541c8036a41047220041b3602d004200541d8016a200541d0046a4100200510e38d8080001a200528028402220841017621040240024020084101710d0020052802dc012005280280022209200941284b22091b220c2004490d1020052802d801200541d8016a20091b21090c010b20052802dc012005280280022209200941284b1b220c2004490d10200c20044d0d1120052802d801200541d8016a200941284b1b220920046a2d0000417071210e0b200541d0046a41286a220c4100360200200541d0046a2009200920046a10f686808000200541b0066a41286a2204200c280200360200200541b0066a41206a2209200541d0046a41206a290200370300200541b0066a41186a220c200541d0046a41186a290200370300200541b0066a41106a220d200541d0046a41106a290200370300200541b0066a41086a2210200541d0046a41086a290200370300200520052902d0043703b00602402005280280024129490d0020052802d80141002802c0a3c68000118080808000000b2005418c046a201029030037020020054194046a200d2903003702002005419c046a200c290300370200200541a4046a2009290300370200200541ac046a20042802003602002005410136028004200520052903b0063702840420054184046a210f0b20052802880420052802ac042204200441284b22041b2109200528028404200f20041b21040b2009200720041b211b2008410171211c2004200a20041b211d024020064101710d000240200141286a28020022042001411c6a220a2802002207470d00200a10a889808000200128021c2107200128022821040b200141206a280200200141246a28020020046a22044100200720042007491b6b4102746a200b3602002001200128022841016a360228200141186a2802002207200b4b0d1c200b200741e4d7c2800010f980808000000b200541b0046a41026a200541fc036a41026a2d00003a0000200541bf046a20054198016a41086a290300370000200541c7046a200541a8016a290300370000200541cf046a200541b0016a2d00003a0000200520052f01fc033b01b0042005200b3600b30420052005290398013700b7042005200e3a00e1012005201c3a00e0012005201b3602dc012005201d3602d801200541d0046a2001200541b0046a200541d8016a10878680800020052802d404210b20052802d004450d1a200041083a00002000200b3602040240200528028004450d00200f2802284129490d0020052802840441002802c0a3c68000118080808000000b200541f4036a2802004129490d0620052802cc0341002802c0a3c68000118080808000000c060b4101210641022107200a2d00244102470d004102210641032107200a2d00484102470d000240200a2d006c4102460d0041032106410421070c010b0240200a2d0090014102460d0041042106410521070c010b0240200a2d00b4014102460d0041052106410621070c010b0240200a2d00d8014102460d0041062106410721070c010b0240200a2d00fc014102460d0041072106410821070c010b0240200a2d00a0024102460d0041082106410921070c010b0240200a2d00c4024102460d0041092106410a21070c010b0240200a2d00e8024102460d00410a2106410b21070c010b0240200a2d008c034102460d00410b2106410c21070c010b0240200a2d00b0034102460d00410c2106410d21070c010b0240200a2d00d4034102460d00410d2106410e21070c010b0240200a2d00f8034102460d00410e2106410f21070c010b200a2d009c044102460d01410f2106411021070b200741246c21070340200741246a220441e404460d02200a20076a210b20042107200b2d00004102460d000c180b0b200941ff01714103460d15200020082900003700052000412c6a200841276a280000360000200041256a200841206a2900003700002000411d6a200841186a290000370000200041156a200841106a2900003700002000410d6a200841086a290000370000200542003702dc01200541a0d1c280003602d801200041306a200541d8016a10d98d808000200020093a0004200041053a00000c010b200941ff01714103470d15200520063a00d004200541d8016a41286a22074100360200200541d8016a200541d0046a200541d0046a41016a10f58d808000200541086a41286a220b2007280200360200200541086a41206a2208200541d8016a41206a290200370300200541086a41186a2209200541d8016a41186a290200370300200541086a41106a220c200541d8016a41106a290200370300200541086a41086a220d200541d8016a41086a290200370300200520052902d801370308200a200641246c6a22072d00002104200741023a000020044102460d0b2005413d6a2007290001370000200541e4006a2005290308370200200541386a41246a200741206a280000360000200541d5006a200741196a290000370000200541cd006a200741116a290000370000200541c5006a200741096a290000370000200541ec006a200d290300370200200541f4006a200c290300370200200541fc006a200929030037020020054184016a20082903003702002005418c016a200b28020036020020054101360260200541063a0038200520043a003c20002001200541386a200341001088868080000b200a41002802c0a3c68000118080808000000c170b200541a0016a200241c4006a290200370300200541a8016a200241cc006a290200370300200541b0016a200241d4006a29020037030020052002413c6a2902003703980141012107200241016a2108200241306a2109200241dc006a2802002110200241386a2802002111200241346a2802002113200228023021124100210c024002400240200228022c220a2d0000220d4102470d004101210c41022107200a2d0024220d4102470d004102210c41032107200a2d0048220d4102470d000240200a2d006c220d4102460d004103210c410421070c010b0240200a2d009001220d4102460d004104210c410521070c010b0240200a2d00b401220d4102460d004105210c410621070c010b0240200a2d00d801220d4102460d004106210c410721070c010b0240200a2d00fc01220d4102460d004107210c410821070c010b0240200a2d00a002220d4102460d004108210c410921070c010b0240200a2d00c402220d4102460d004109210c410a21070c010b0240200a2d00e802220d4102460d00410a210c410b21070c010b0240200a2d008c03220d4102460d00410b210c410c21070c010b0240200a2d00b003220d4102460d00410c210c410d21070c010b0240200a2d00d403220d4102460d00410d210c410e21070c010b0240200a2d00f803220d4102460d00410e210c410f21070c010b200a2d009c04220d4102460d01410f210c411021070b200741246c21070340200741246a220441e404460d02200a20076a210b20042107200b2d00004102460d000c140b0b200641ff01714103460d1120002008290000370005200020092902003702302000412c6a200841276a280000360000200041256a200841206a2900003700002000411d6a200841186a290000370000200041156a200841106a2900003700002000410d6a200841086a290000370000200041386a200941086a290200370200200041c0006a200941106a290200370200200041c8006a200941186a290200370200200041d0006a200941206a290200370200200041d8006a200941286a290200370200200020063a0004200041053a00000c100b200641ff01714103470d11200a200c41246c6a220741023a0000200d4102460d0a2007280204210b200541bc086a41026a200741036a2d00003a0000200541c0016a200741106a290200370300200541b8016a41106a200741186a290200370300200541b8016a41186a200741206a280200360200200520072f00013b01bc08200520072902083703b801200328020820112010201041284b1b41017420126b6a220841017621072003280204210420032802002103024002400240024020084101710d00200720044d0d012007200441f492c68000109581808000000b200720044b0d0e20072004490d0120072004419493c6800010f980808000000b410021042005410036028004200c4104742109410121060c010b200320076a22042d0000210841002106200541d8016a41286a22094100360200200541d8016a2003200410f686808000200541d0046a41286a221420092802002209360200200541d0046a41206a221e200541d8016a41206a290200370300200541d0046a41186a221f200541d8016a41186a290200370300200541d0046a41106a2220200541d8016a41106a290200370300200541d0046a41086a2221200541d8016a41086a290200370300200520052902d80122153703d004200541d0046a41047221042008417071200c72212202400240200541d0046a41044128200941284b22081b6a28020022162009412820081b460d002004201420081b21042015a7200541d0046a20081b21080c010b200541d0046a10f88680800020052802d404211620052802d00421080b200820166a20223a00002004200428020041016a360200200541ac046a20142802002208360200200541a4046a201e2903003702002005419c046a201f29030037020020054194046a20202903003702002005418c046a2021290300370200200520052903d00422153702840420054101360280042015a720054180046a41046a200841284b22091b2104200528028804200820091b21080b2008200720041b21082004200320041b21070240200d4101710d000240200141286a28020022032001411c6a220d2802002204470d00200d10a889808000200128021c2104200128022821030b200141206a280200200141246a28020020036a22034100200420032004491b6b4102746a200b3602002001200128022841016a360228200141186a2802002204200b4d0d0d200541d8016a200141146a280200200b4107746a220441800110848e8080001a200441043a0004200441083a00000c0f0b200541d8026a41026a200541bc086a41026a2d00003a0000200541e7026a200541b8016a41086a290300370000200541ef026a200541b8016a41106a290300370000200541f7026a200541d0016a2d00003a0000200520052f01bc083b01d8022005200b3600db02200520052903b8013700df02200520093a00d904200520063a00d804200520083602d404200520073602d004200541b0066a2001200541d8026a200541d0046a10878680800020052802b406210420052802b006450d0d200041083a0000200020043602040240200528028004450d0020052802ac044129490d0020052802840441002802c0a3c68000118080808000000b200a41002802c0a3c680001180808080000020104129490d00201341002802c0a3c68000118080808000000b20022d0000417c6a41ff01712207410420074104491b417e6a4103490d170c160b200c200741f8d5c2800010f980808000000b20072008418493c68000109581808000000b20072008418493c68000109581808000000b20072008419493c6800010f980808000000b419c94c68000413a41d894c6800010a181808000000b2004200c41ac95c68000109581808000000b2004200c41bc95c68000109581808000000b2004200c41cc95c6800010f980808000000b41c8d6c28000412441ecd6c2800010a181808000000b41c8d6c280004124418cd7c2800010a181808000000b20072004418493c68000109581808000000b200b200441e4d7c2800010f980808000000b200541d8016a200141106a20041089868080000b0240024020052d00d80122044108470d00200541f8026a41086a200541e5016a290000370300200541f8026a41106a200541ed016a290000370300200541f8026a41186a200541f5016a290000370300200541f8026a41206a200541fd016a2900003703002005419f036a20054184026a280000360000200541b8056a41086a20054194026a290200370300200541b8056a41106a2005419c026a290200370300200541b8056a41186a200541a4026a290200370300200541b8056a41206a200541ac026a290200370300200541e0056a200541b4026a2902003703002005200541dd016a2900003703f80220052005418c026a2902003703b80520054188026a280200210b20052d00dc0121040c010b200541b0076a41086a220d200541e1016a290000370300200541b0076a41106a2214200541e9016a290000370300200541b0076a41186a2216200541f1016a290000370300200541b0076a41206a221e200541f9016a290000370300200541b0076a41276a221f200541d8016a41286a280000360000200541b0066a41086a2220200541d8016a41386a290200370300200541b0066a41106a2221200541d8016a41c0006a290200370300200541b0066a41186a2222200541d8016a41c8006a290200370300200541b0066a41206a2223200541a8026a290200370300200541b0066a41286a2224200541b0026a290200370300200520052900d9013703b00720052005290288023703b006200528028402210b200541a8036a41186a2225200541d0026a290200370300200541a8036a41106a2226200541c8026a290200370300200541a8036a41086a2227200541c0026a290200370300200520052902b8023703a803200541b8056a41286a22034100360200200541b8056a2007200720086a10f686808000200541c8036a41286a22072003280200360200200541c8036a41206a2208200541b8056a41206a220f290200370300200541c8036a41186a2228200541b8056a41186a2229290200370300200541c8036a41106a222a200541b8056a41106a222b290200370300200541c8036a41086a222c200541b8056a41086a222d290200370300200541d0046a41086a2027290300370300200541d0046a41106a2026290300370300200541d0046a41186a2025290300370300200520052902b8053703c803200520052903a8033703d004200541d0046a41c8006a2007280200360200200541d0046a41c0006a2008290300370300200541d0046a41386a202829030037030020054180056a202a290300370300200541d0046a41286a202c290300370300200520052903c8033703f0042005419d056a20093a00002005419c056a20063a0000200141dc006a200541d0046a10958d8080001a200541f8026a41086a200d290300370300200541f8026a41106a2014290300370300200541f8026a41186a2016290300370300200541f8026a41206a201e290300370300200541f8026a41276a201f280000360000202d2020290300370300202b202129030037030020292022290300370300200f202329030037030020032024290300370300200520052903b0073703f802200520052903b0063703b8050b02400240024002402004417c6a41ff01712207410420074104491b417f6a0e0401000002000b419cd7c28000412841c4d7c2800010f880808000000b200541b0066a41286a200541b8056a41286a290300370300200541b0066a41206a200541b8056a41206a290300370300200541b0066a41186a200541b8056a41186a290300370300200541b0066a41106a200541b8056a41106a290300370300200541b0066a41086a2204200541b8056a41086a290300370300200520052903b8053703b006200541d0046a41086a22012011360200200541e4046a20054198016a41086a290300370200200541ec046a20054198016a41106a290300370200200541f4046a20054198016a41186a290300370200200520133602d404200520123602d00420052005290398013702dc04200520103602fc04200541c8036a41086a22074101360200200541013602c8032005200c3a00b0072005200541b0076a3602cc03200541d0046a200541c8036a10e78d80800020072004280200200541dc066a22042802002203200341284b22031b360200200520052802b406200541b0066a41047220031b3602cc03200520052802b0063602c803200541d0046a200541c8036a10e78d808000200041d8006a200541d0046a41286a290200370200200041d0006a200541d0046a41206a290200370200200041c8006a200541d0046a41186a290200370200200041c0006a200541d0046a41106a290200370200200041386a2001290200370200200020052902d004370230200541c8036a410b6a200541f8026a410b6a290000370000200541c8036a41136a200541f8026a41136a290000370000200541c8036a411b6a200541f8026a411b6a290000370000200541c8036a41236a200541f8026a41236a290000370000200520052900fb023700cb03200041053a00002000200b36022c200020052900c803370001200041096a2007290000370000200041116a200541c8036a41106a290000370000200041196a200541c8036a41186a290000370000200041216a200541c8036a41206a290000370000200041286a200541ef036a28000036000020042802004129490d0120052802b40641002802c0a3c68000118080808000000c010b200020043a0000200020052903f802370001200541b0066a41286a200541b8056a41286a290300370300200541b0066a41206a200541b8056a41206a290300370300200541b0066a41186a200541b8056a41186a290300370300200541b0066a41106a200541b8056a41106a290300370300200541b0066a41086a2207200541b8056a41086a290300370300200041096a200541f8026a41086a290300370000200041116a200541f8026a41106a290300370000200041196a200541f8026a41186a290300370000200041216a200541f8026a41206a290300370000200041286a2005419f036a280000360000200520052903b8053703b006200541d0046a41086a22042011360200200541e4046a20054198016a41086a290300370200200541ec046a20054198016a41106a290300370200200541f4046a20054198016a41186a290300370200200520133602d404200520123602d00420052005290398013702dc04200520103602fc04200541c8036a41086a22014101360200200541013602c8032005200c3a00b0072005200541b0076a3602cc03200541d0046a200541c8036a10e78d80800020012007280200200541dc066a22072802002203200341284b22031b360200200520052802b406200541b0066a41047220031b3602cc03200520052802b0063602c803200541d0046a200541c8036a10e78d808000200041d8006a200541d0046a41286a290200370200200041d0006a200541d0046a41206a290200370200200041c8006a200541d0046a41186a290200370200200041c0006a200541d0046a41106a290200370200200041386a2004290200370200200020052902d0043702302000200b36022c20072802004129490d0020052802b40641002802c0a3c68000118080808000000b200528028004450d0020052802ac044129490d0020052802840441002802c0a3c68000118080808000000b200a41002802c0a3c68000118080808000000c060b4188d6c28000412f41fcd6c2800010f880808000000b2000200929020037023020002008290000370001200041d8006a200941286a290200370200200041d0006a200941206a290200370200200041c8006a200941186a290200370200200041c0006a200941106a290200370200200041386a200941086a290200370200200041096a200841086a290000370000200041116a200841106a290000370000200041196a200841186a290000370000200041216a200841206a290000370000200041286a200841276a2800003600002000200a36022c200020063a00000c040b4188d6c28000412f41b8d6c2800010f880808000000b2000200a360230200020093a0004200041073a0000200020082900003700052000412c6a200841276a280000360000200041256a200841206a2900003700002000411d6a200841186a290000370000200041156a200841106a2900003700002000410d6a200841086a2900003700000c020b0240200141286a28020022042001411c6a220a2802002207470d00200a10a889808000200128021c2107200128022821040b200141206a280200200141246a28020020046a22044100200720042007491b6b4102746a200b3602002001200128022841016a360228200141186a2802002207200b4b0d00200b200741e4d7c2800010f980808000000b200141146a280200200b4107746a22072d0060212e20072d005c210b20072d0058210a20072d0054210920072d0050210620072d004c210c20072d0048211020072d0044211120072d0040211220072d003c211420072d0038211620072d0034211e20072d0030212020072d002c212120072d0028212220072d0024212420072d0020212520072d001c212620072d0018212f20072d0014212320072d0010211f20072d000c211320072d0008210d20072d0004210820072d00002104200741043a0004200741083a0000200741e1006a2130200741dd006a2131200741d9006a2132200741d5006a2133200741d1006a2134200741cd006a2135200741c9006a2136200741c5006a2137200741c1006a21382007413d6a2139200741396a213a200741356a213b200741316a213c2007412d6a213d200741296a213e200741256a213f200741216a21402007411d6a212d200741196a212c200741156a212b200741116a212a2007410d6a2129200741096a2128200741056a21270240024020044108460d00200541b8056a41026a200741036a2d00003a0000200541b0076a41026a202741026a2d00003a0000200541b8016a41026a202841026a2d00003a0000200541bc086a41026a202941026a2d00003a0000200541b8086a41026a202a41026a2d00003a0000200520072f00013b01b805200520272f00003b01b007200520282f00003b01b801200520292f00003b01bc082005202a2f00003b01b808200541b4086a41026a202b41026a2d00003a0000200541b0086a41026a202c41026a2d00003a0000200541ac086a41026a202d41026a2d00003a0000200541a8086a41026a204041026a2d00003a0000200541a4086a41026a203f41026a2d00003a00002005202b2f00003b01b4082005202c2f00003b01b0082005202d2f00003b01ac08200520402f00003b01a8082005203f2f00003b01a408200541a0086a41026a203e41026a2d00003a00002005203e2f00003b01a0082005419c086a41026a203d41026a2d00003a00002005203d2f00003b019c0820054198086a41026a203c41026a2d00003a00002005203c2f00003b01980820054194086a41026a203b41026a2d00003a00002005203b2f00003b01940820054190086a41026a203a41026a2d00003a00002005203a2f00003b0190082005418c086a41026a203941026a2d00003a0000200520392f00003b018c0820054188086a41026a203841026a2d00003a0000200520382f00003b01880820054184086a41026a203741026a2d00003a0000200520372f00003b01840820054180086a41026a203641026a2d00003a0000200520362f00003b018008200541fc076a41026a203541026a2d00003a0000200520352f00003b01fc07200541f8076a41026a203441026a2d00003a0000200520342f00003b01f807200541f4076a41026a203341026a2d00003a0000200520332f00003b01f407200541f0076a41026a203241026a2d00003a0000200520322f00003b01f007200541ec076a41026a203141026a2d00003a0000200520312f00003b01ec07200541b0066a41026a203041026a2d00003a0000200520302f00003b01b006200541f0016a200741fc006a280000360200200541e8016a200741f4006a290000370300200541d8016a41086a200741ec006a290000370300200520072900643703d801200a2128200421272009210a20062109200c21062010210c20112110201221112014211220162114201e21162020211e2021212020222121202421222025212420262125202f21260c010b200541b8056a41026a202741026a2d00003a0000200541b0076a41026a202841026a2d00003a0000200541b8016a41026a202941026a2d00003a0000200541bc086a41026a202a41026a2d00003a0000200541b8086a41026a202b41026a2d00003a0000200520272f00003b01b805200520282f00003b01b007200520292f00003b01b8012005202a2f00003b01bc082005202b2f00003b01b808200541b4086a41026a202c41026a2d00003a0000200541b0086a41026a202d41026a2d00003a0000200541ac086a41026a204041026a2d00003a0000200541a8086a41026a203f41026a2d00003a0000200541a4086a41026a203e41026a2d00003a00002005202c2f00003b01b4082005202d2f00003b01b008200520402f00003b01ac082005203f2f00003b01a8082005203e2f00003b01a408200541a0086a41026a203d41026a2d00003a00002005203d2f00003b01a0082005419c086a41026a203c41026a2d00003a00002005203c2f00003b019c0820054198086a41026a203b41026a2d00003a00002005203b2f00003b01980820054194086a41026a203a41026a2d00003a00002005203a2f00003b01940820054190086a41026a203941026a2d00003a0000200520392f00003b0190082005418c086a41026a203841026a2d00003a0000200520382f00003b018c0820054188086a41026a203741026a2d00003a0000200520372f00003b01880820054184086a41026a203641026a2d00003a0000200520362f00003b01840820054180086a41026a203541026a2d00003a0000200520352f00003b018008200541fc076a41026a203441026a2d00003a0000200520342f00003b01fc07200541f8076a41026a203341026a2d00003a0000200520332f00003b01f807200541f4076a41026a203241026a2d00003a0000200520322f00003b01f407200541f0076a41026a203141026a2d00003a0000200520312f00003b01f007200541ec076a41026a203041026a2d00003a0000200520302f00003b01ec07200b2128202e210b20082127200d21082013210d201f21132023211f202f21230b200541d7046a200541b0076a41026a2d00003a0000200541db046a200541b8016a41026a2d00003a0000200541df046a200541bc086a41026a2d00003a0000200520052f01b8053b00d104200520083a00d404200520052f01b0073b00d5042005200d3a00d804200520052f01b8013b00d904200520133a00dc04200520052f01bc083b00dd042005200541b8056a41026a2d00003a00d304200520273a00d004200541e3046a200541b8086a41026a2d00003a0000200541e7046a200541b4086a41026a2d00003a0000200541eb046a200541b0086a41026a2d00003a0000200541ef046a200541ac086a41026a2d00003a00002005201f3a00e004200520233a00e404200520263a00e804200520253a00ec04200520052f01b8083b00e104200520052f01b4083b00e504200520052f01b0083b00e904200520052f01ac083b00ed04200541f3046a200541a8086a41026a2d00003a0000200541f7046a200541a4086a41026a2d00003a0000200541fb046a200541a0086a41026a2d00003a0000200541ff046a2005419c086a41026a2d00003a0000200520243a00f004200520223a00f404200520213a00f804200520203a00fc04200520052f01a8083b00f104200520052f01a4083b00f504200520052f01a0083b00f904200520052f019c083b00fd042005201e3a00800520054183056a20054198086a41026a2d00003a0000200520052f0198083b008105200520163a00840520054187056a20054194086a41026a2d00003a0000200520052f0194083b008505200520143a0088052005418b056a20054190086a41026a2d00003a0000200520052f0190083b008905200520123a008c052005418f056a2005418c086a41026a2d00003a0000200520052f018c083b008d05200520113a00900520054193056a20054188086a41026a2d00003a0000200520052f0188083b009105200520103a00940520054197056a20054184086a41026a2d00003a0000200520052f0184083b0095052005200c3a0098052005419b056a20054180086a41026a2d00003a0000200520052f0180083b009905200520063a009c052005419f056a200541fc076a41026a2d00003a0000200520052f01fc073b009d05200520093a00a005200541a3056a200541f8076a41026a2d00003a0000200520052f01f8073b00a1052005200a3a00a405200541a7056a200541f4076a41026a2d00003a0000200520052f01f4073b00a505200520283a00a805200541ab056a200541f0076a41026a2d00003a0000200520052f01f0073b00a9052005200b3a00ac05200541af056a200541ec076a41026a2d00003a0000200520052f01ec073b00ad05200541b4056a41026a200541b0066a41026a2d00003a0000200520052f01b0063b01b4054108212a200541f8026a41086a200541d8016a41086a290300370300200541f8026a41106a200541d8016a41106a290300370300200541f8026a41186a200541d8016a41186a280200360200200520052903d8013703f802200541d0046a41046a210702400240024002400240024002402027417c6a41ff01712229410420294104491b417f6a0e020102000b200541ad056a2107200541a9056a2103200541a5056a2129200541a1056a212b2005419d056a212c20054199056a212d20054195056a214020054191056a213f2005418d056a213e20054189056a213d20054185056a213c20054181056a213b200541fd046a213a200541f9046a2139200541f5046a2138200541f1046a2137200541ed046a2136200541e9046a2135200541e5046a2134200541e1046a2133200541dd046a2132200541d9046a2131200541d5046a2130200541d0046a410172211d20044108470d02200541e2076a2104200541e6076a210e200541e9076a211b200541ec076a211c200541f0076a212f200541f4076a2141200541f8076a2142200541fc076a214320054180086a214420054184086a214520054188086a21462005418c086a214720054190086a214820054194086a214920054198086a214a2005419c086a214b200541a0086a214c200541a4086a214d200541a8086a214e200541ac086a214f200541b0086a2150200541b4086a2151200541b8086a2152200541bc086a21530c030b200541b0066a41286a20054180056a220b41286a290200370300200541b0066a41206a200b41206a290200370300200541b0066a41186a200b41186a290200370300200541b0066a41106a200b41106a290200370300200541b0066a41086a200b41086a290200370300200541e0066a41086a200741086a290200370300200541e0066a41106a200741106a290200370300200541e0066a41186a200741186a290200370300200541e0066a41206a220a200741206a290200370300200541e0066a41286a200741286a2802003602002005200b2902003703b006200520072902003703e006024020044108460d00200541ac076a41026a2207200541b4056a41026a2d00003a000020054190076a41086a2204200541f8026a41086a29030037030020054190076a41106a220b200541f8026a41106a29030037030020054190076a41186a2203200541f8026a41186a280200360200200520052f01b4053b01ac07200520052903f80237039007200541b8056a41286a22084100360200200541b8056a201d201d201b6a10f686808000200541b0076a41286a22092008280200360200200541b0076a41206a2208200541b8056a41206a290200370300200541b0076a41186a2206200541b8056a41186a290200370300200541b0076a41106a220c200541b8056a41106a290200370300200541b0076a41086a220d200541b8056a41086a290200370300200520052902b8053703b0072005202e3a00d801200520052f01ac073b00d901200520072d00003a00db01200541f4016a2003280200360200200541ec016a200b290300370200200541e4016a200429030037020020052005290390073702dc01200541a0026a200928020036020020054198026a200829030037020020054190026a200629030037020020054188026a200c290300370200200541d8016a41286a200d290300370200200520052903b0073702f801200541a5026a200e3a0000200541a4026a201c3a0000200141dc006a200541d8016a10958d8080001a0b200541d8016a41286a2207200541c8036a41286a290300370300200541d8016a41206a2204200541c8036a41206a290300370300200541d8016a41186a220b200541c8036a41186a290300370300200541d8016a41106a2201200541c8036a41106a290300370300200541d8016a41086a2203200541c8036a41086a290300370300200520052903c8033703d801200541b8056a41086a2208200541b0066a41086a280200200541dc066a22092802002206200641284b22061b360200200520052802b406200541b0066a41047220061b3602bc05200520052802b0063602b805200541d8016a200541b8056a10e78d808000200041d8006a2007290300370200200041d0006a2004290300370200200041c8006a200b290300370200200041c0006a2001290300370200200041386a2003290300370200200020052903d801370230200541c3056a200541e0066a41086a290300370000200541cb056a200541e0066a41106a290300370000200541d3056a200541e0066a41186a290300370000200541db056a200a290300370000200541e3056a200541e0066a41286a280200360000200520052903e0063700bb05200041053a0000200020052900b805370001200041096a2008290000370000200041116a200541b8056a41106a290000370000200041196a200541b8056a41186a290000370000200041216a200541b8056a41206a290000370000200041286a200541df056a29000037000020092802004129490d0320052802b40641002802c0a3c68000118080808000000c030b200541b8056a41286a200541d0046a41286a220b41286a290200370300200541b8056a41206a200b41206a290200370300200541b8056a41186a200b41186a290200370300200541b8056a41106a200b41106a290200370300200541b8056a41086a200b41086a290200370300200541e8056a41086a200741086a290200370300200541e8056a41106a200741106a290200370300200541e8056a41186a200741186a290200370300200541e8056a41206a220a200741206a2802003602002005200b2902003703b805200520072902003703e805024020044108460d00200541ac066a41026a2207200541b4056a41026a2d00003a000020054190066a41086a2204200541f8026a41086a29030037030020054190066a41106a220b200541f8026a41106a29030037030020054190066a41186a2208200541f8026a41186a280200360200200520052f01b4053b01ac06200520052903f80237039006200541b0066a41286a22094100360200200541b0066a201d201d201b6a10f686808000200541b0076a41286a22062009280200360200200541b0076a41206a2209200541b0066a41206a290200370300200541b0076a41186a220c200541b0066a41186a290200370300200541b0076a41106a220d200541b0066a41106a290200370300200541b0076a41086a2210200541b0066a41086a290200370300200520052902b0063703b0072005202e3a00d801200520052f01ac063b00d901200520072d00003a00db01200541f4016a2008280200360200200541ec016a200b290300370200200541e4016a200429030037020020052005290390063702dc01200541a0026a200628020036020020054198026a200929030037020020054190026a200c29030037020020054188026a200d290300370200200541d8016a41286a2010290300370200200520052903b0073702f801200541a5026a200e3a0000200541a4026a201c3a0000200141dc006a200541d8016a10958d8080001a0b200541b0066a41286a2207200541c8036a41286a290300370300200541b0066a41206a2204200541c8036a41206a290300370300200541b0066a41186a220b200541c8036a41186a290300370300200541b0066a41106a2208200541c8036a41106a290300370300200541b0066a41086a2209200541c8036a41086a290300370300200520052903c8033703b006200541d8016a41086a200541b8056a41086a280200200541e4056a2206280200220c200c41284b220c1b360200200520052802bc05200541b8056a410472200c1b3602dc01200520052802b8053602d801200541b0066a200541d8016a10e78d808000200541a8026a2007290300370200200541a0026a200429030037020020054198026a200b29030037020020054190026a200829030037020020054188026a2009290300370200200541e4016a200541e8056a41086a290300370200200541ec016a200541e8056a41106a290300370200200541f4016a200541e8056a41186a290300370200200541fc016a200a280200360200200520052903b00637028002200520052903e8053702dc01200541063a00d80120002001200541d8016a2003410110888680800020062802004129490d0220052802bc0541002802c0a3c68000118080808000000c020b200541e2076a41026a200541b4056a41026a2d00003a0000200541b8056a41086a200541f8026a41086a290300370300200541b8056a41106a200541f8026a41106a290300370300200541b8056a41186a200541f8026a41186a280200360200200520052f01b4053b01e207200520052903f8023703b805200541e6076a2104200541e9076a210e200541ec076a211b200541f0076a211c200541f4076a212f200541f8076a2141200541fc076a214220054180086a214320054184086a214420054188086a21452005418c086a214620054190086a214720054194086a214820054198086a21492005419c086a214a200541a0086a214b200541a4086a214c200541a8086a214d200541ac086a214e200541b0086a214f200541b4086a2150200541b8086a2151200541bc086a2152200541b8016a21532027212a20082127200d21082013210d201f21132023211f202621232025212620242125202221242021212220202121201e21202016211e20142116201221142011211220102111200c21102006210c20092106200a21092028210a200b2128202e210b0b2053201d2f00003b0000205220302f00003b0000205120312f00003b0000205020322f00003b0000204f20332f00003b0000205341026a201d41026a2d00003a0000205241026a203041026a2d00003a0000205141026a203141026a2d00003a0000205041026a203241026a2d00003a0000204f41026a203341026a2d00003a0000204e41026a203441026a2d00003a0000204e20342f00003b0000204d20352f00003b0000204d41026a203541026a2d00003a0000204c20362f00003b0000204c41026a203641026a2d00003a0000204b20372f00003b0000204b41026a203741026a2d00003a0000204a41026a203841026a2d00003a0000204a20382f00003b0000204941026a203941026a2d00003a0000204920392f00003b0000204841026a203a41026a2d00003a00002048203a2f00003b0000204741026a203b41026a2d00003a00002047203b2f00003b0000204641026a203c41026a2d00003a00002046203c2f00003b0000204541026a203d41026a2d00003a00002045203d2f00003b0000204441026a203e41026a2d00003a00002044203e2f00003b0000204341026a203f41026a2d00003a00002043203f2f00003b0000204241026a204041026a2d00003a0000204220402f00003b0000204141026a202d41026a2d00003a00002041202d2f00003b0000202f41026a202c41026a2d00003a0000202f202c2f00003b0000201c41026a202b41026a2d00003a0000201c202b2f00003b0000201b41026a202941026a2d00003a0000201b20292f00003b0000200e41026a200341026a2d00003a0000200e20032f00003b0000200441026a200741026a2d00003a0000200420072f00003b0000200541d8016a41286a200541c8036a41286a290300370300200541d8016a41206a200541c8036a41206a290300370300200541d8016a41186a200541c8036a41186a290300370300200541d8016a41106a200541c8036a41106a290300370300200541d8016a41086a200541c8036a41086a290300370300200520052903c8033703d801024002400240200141286a2802002207450d0020012007417f6a360228200141246a22072007280200220741016a220441002001411c6a280200220320042003491b6b360200200141206a28020020074102746a2802002204200141186a28020022074f0d02200128021420044107746a220741046a200720072d00004108461b10e3858080002007202a3a0000200720273a0004200720083a00082007200d3a000c200720052f00b8013b0001200741036a200541b8016a41026a2d00003a0000200720052f00bc083b0005200741076a200541bc086a41026a2d00003a0000200720052f00b8083b00092007410b6a200541b8086a41026a2d00003a0000200720052f00b4083b000d2007410f6a200541b4086a41026a2d00003a0000200720133a00102007201f3a0014200720233a0018200720263a001c200720052f00b0083b0011200741136a200541b0086a41026a2d00003a0000200720052f00ac083b0015200741176a200541ac086a41026a2d00003a0000200720052f00a8083b00192007411b6a200541a8086a41026a2d00003a0000200720052f00a4083b001d2007411f6a200541a4086a41026a2d00003a0000200720253a0020200720243a0024200720223a0028200720213a002c200720052f00a0083b0021200741236a200541a0086a41026a2d00003a0000200720052f009c083b0025200741276a2005419c086a41026a2d00003a0000200720052f0098083b00292007412b6a20054198086a41026a2d00003a0000200720052f0094083b002d2007412f6a20054194086a41026a2d00003a0000200720203a0030200741336a20054190086a41026a2d00003a0000200720052f0090083b00312007201e3a0034200741376a2005418c086a41026a2d00003a0000200720052f008c083b0035200720163a00382007413b6a20054188086a41026a2d00003a0000200720052f0088083b0039200720143a003c2007413f6a20054184086a41026a2d00003a0000200720052f0084083b003d200720123a0040200741c3006a20054180086a41026a2d00003a0000200720052f0080083b0041200720113a0044200741c7006a200541fc076a41026a2d00003a0000200720052f00fc073b0045200720103a0048200741cb006a200541f8076a41026a2d00003a0000200720052f00f8073b00492007200c3a004c200741cf006a200541f4076a41026a2d00003a0000200720052f00f4073b004d200720063a0050200741d3006a200541f0076a41026a2d00003a0000200720052f00f0073b0051200720093a0054200741d7006a200541ec076a41026a2d00003a0000200720052f00ec073b00552007200a3a0058200741db006a200541e9076a41026a2d00003a0000200720052f00e9073b0059200720283a005c200741df006a200541e6076a41026a2d00003a0000200720052f00e6073b005d2007200b3a0060200741e3006a200541e2076a41026a2d00003a0000200720052f01e2073b0061200741fc006a200541d0056a280200360000200741f4006a200541c8056a290300370000200741ec006a200541b8056a41086a290300370000200720052903b8053700640c010b0240200141186a28020022072001280210470d00200141106a2007109e86808000200128021821070b200128021420074107746a2207202a3a0000200720052f00b8013b0001200720273a0004200720052f00bc083b0005200720083a0008200720052f00b8083b00092007200d3a000c200720052f00b4083b000d200741036a200541b8016a41026a2d00003a0000200741076a200541bc086a41026a2d00003a00002007410b6a200541b8086a41026a2d00003a00002007410f6a200541b4086a41026a2d00003a0000200720133a00102007201f3a0014200720233a0018200720263a001c200720052f00b0083b0011200741136a200541b0086a41026a2d00003a0000200720052f00ac083b0015200741176a200541ac086a41026a2d00003a0000200720052f00a8083b00192007411b6a200541a8086a41026a2d00003a0000200720052f00a4083b001d2007411f6a200541a4086a41026a2d00003a0000200720253a0020200741236a200541a0086a41026a2d00003a0000200720052f00a0083b0021200720243a0024200741276a2005419c086a41026a2d00003a0000200720052f009c083b0025200720223a00282007412b6a20054198086a41026a2d00003a0000200720052f0098083b0029200720213a002c2007412f6a20054194086a41026a2d00003a0000200720052f0094083b002d200720203a0030200741336a20054190086a41026a2d00003a0000200720052f0090083b00312007201e3a0034200741376a2005418c086a41026a2d00003a0000200720052f008c083b0035200720163a00382007413b6a20054188086a41026a2d00003a0000200720052f0088083b0039200720143a003c2007413f6a20054184086a41026a2d00003a0000200720052f0084083b003d200720123a0040200741c3006a20054180086a41026a2d00003a0000200720052f0080083b0041200720113a0044200741c7006a200541fc076a41026a2d00003a0000200720052f00fc073b0045200720103a0048200741cb006a200541f8076a41026a2d00003a0000200720052f00f8073b00492007200c3a004c200741cf006a200541f4076a41026a2d00003a0000200720052f00f4073b004d200720063a0050200741d3006a200541f0076a41026a2d00003a0000200720052f00f0073b0051200720093a0054200741d7006a200541ec076a41026a2d00003a0000200720052f00ec073b00552007200a3a0058200741db006a200541e9076a41026a2d00003a0000200720052f00e9073b0059200720283a005c200741df006a200541e6076a41026a2d00003a0000200720052f00e6073b005d2007200b3a0060200741e3006a200541e2076a41026a2d00003a0000200720052f01e2073b0061200741fc006a200541d0056a280200360000200741f4006a200541c8056a290300370000200741ec006a200541c0056a290300370000200720052903b80537006420012001280218220441016a3602180b200020052903d801370228200020052f00b0073b0005200020052902b00637020c200041d0006a20054180026a290300370200200041c8006a200541f8016a290300370200200041c0006a200541d8016a41186a290300370200200041386a200541d8016a41106a290300370200200041306a200541d8016a41086a290300370200200041076a200541b0076a41026a2d00003a0000200041146a200541b0066a41086a2902003702002000411c6a200541b0066a41106a290200370200200041246a200541b0066a41186a280200360200200041063a0000200020052f00d8013b0001200041036a200541d8016a41026a2d00003a000020002004360208200041003a00040c020b2004200741d4d7c2800010f980808000000b20052d00d004417c6a41ff01712207410420074104491b417f6a4102490d00200541d0046a10e3858080000b200528028004450d00200f2802284129490d0020052802840441002802c0a3c68000118080808000000b20022d0000417c6a41ff01712207410420074104491b417e6a4103490d010b200210e3858080000b200541c0086a2480808080000bb20101027f0240200141186a2802002203200128020c2204470d002001410c6a10a889808000200128020c2104200128021821030b200141106a280200200141146a28020020036a22034100200420032004491b6b4102746a20023602002001200128021841016a36021802402001280208220420024d0d002000200128020420024107746a220141800110848e8080001a200141043a0004200141083a00000f0b2002200441e4d7c2800010f980808000000b99950105107f017e017f047e397f23808080800041c0086b2205248080808000024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020022d00002206417c6a41ff01712207410420074104491b417e6a0e03020108000b2000200241e00010848e8080001a0c200b200241056a210820022d000421092002280230220a2d00004102460d0141002106410121070c020b200541c8036a41286a200241d0006a290200370300200541e8036a200241c8006a290200370300200541c8036a41186a200241c0006a290200370300200541c8036a41106a200241386a290200370300200541c8036a41086a2207200241306a290200370300200520022902283703c803200541fe036a200241076a2d00003a000020054198016a41086a200241146a29020037030020054198016a41106a2002411c6a29020037030020054198016a41186a200241246a2802003602002005200241056a2f00003b01fc0320052002410c6a29020037039801200241086a280200210b20022d000421062003280200210a2003280204210820032802082109024002400240024002400240024002400240024020040d002007280200200541f4036a2802002204200441284b1b2207417f6a210c2007450d11200741017420096a20052802c803417f736a2209410176210720052802cc03200541c8036a410472200441284b1b200c6a2d0000410f71210c024020094101710d00200720084d0d022007200841f492c68000109581808000000b200720084b0d1220072008490d0220072008419493c6800010f980808000000b2009410176210702400240200941017122040d00200720084d0d012007200841f492c68000109581808000000b200720084b0d13200720084f0d14200a20076a2d000041707121090b2005420037028002200541003602d804200520073602d4042005200a3602d004200541d8016a200541d0046a4100200510e38d8080001a2004450d06200941f00171210820052d0084024101710d04200541d8016a41046a2104200541d8016a41044128200528028002220d41284b22091b6a280200220c200d412820091b460d022004200541d8016a41286a20091b210420052802d801200541d8016a20091b21090c030b410021042005410036028004200c410474210e20054180046a41046a210f410121080c070b200a20076a22042d0000210941002108200541d8016a41286a220d4100360200200541d8016a200a200410f686808000200541d0046a41286a2210200d280200220d360200200541d0046a41206a2211200541d8016a41206a290200370300200541d0046a41186a2212200541d8016a41186a290200370300200541d0046a41106a2213200541d8016a41106a290200370300200541d0046a41086a2214200541d8016a41086a290200370300200520052902d80122153703d004200541d0046a41047221042009417071200c72211602400240200541d0046a41044128200d41284b22091b6a280200220c200d412820091b460d002004201020091b21042015a7200541d0046a20091b21090c010b200541d0046a10f88680800020052802d404210c20052802d00421090b2009200c6a20163a00002004200428020041016a360200200541b0066a41086a20142903002215370300200541b0066a41106a20132903002217370300200541b0066a41186a20122903002218370300200541b0066a41206a20112903002219370300200541b0066a41286a20102802002204360200200520052903d004221a3703b0062005418c046a201537020020054194046a20173702002005419c046a2018370200200541a4046a2019370200200541ac046a200436020020054101360280042005201a3702840420054180046a41046a210f0c050b200541d8016a10f78d80800020052802dc01210c20052802d80121090b2009200c6a20083a00002004200428020041016a3602000c010b20052802dc012005280280022204200441284b22041b2209450d0f200920052802d801200541d8016a20041b6a417f6a220420042d00002008410476723a00000b200520052802840241016a360284020b200520052802c8033602d8042005200541d0036a280200200541f4036a2802002204200441284b22041b3602d404200520052802cc03200541c8036a41047220041b3602d004200541d8016a200541d0046a4100200510e38d8080001a200528028402220841017621040240024020084101710d0020052802dc012005280280022209200941284b22091b220c2004490d1020052802d801200541d8016a20091b21090c010b20052802dc012005280280022209200941284b1b220c2004490d10200c20044d0d1120052802d801200541d8016a200941284b1b220920046a2d0000417071210e0b200541d0046a41286a220c4100360200200541d0046a2009200920046a10f686808000200541b0066a41286a2204200c280200360200200541b0066a41206a2209200541d0046a41206a290200370300200541b0066a41186a220c200541d0046a41186a290200370300200541b0066a41106a220d200541d0046a41106a290200370300200541b0066a41086a2210200541d0046a41086a290200370300200520052902d0043703b00602402005280280024129490d0020052802d80141002802c0a3c68000118080808000000b2005418c046a201029030037020020054194046a200d2903003702002005419c046a200c290300370200200541a4046a2009290300370200200541ac046a20042802003602002005410136028004200520052903b0063702840420054184046a210f0b20052802880420052802ac042204200441284b22041b2109200528028404200f20041b21040b2009200720041b211b2008410171211c2004200a20041b211d024020064101710d000240200141286a28020022042001411c6a220a2802002207470d00200a10a889808000200128021c2107200128022821040b200141206a280200200141246a28020020046a22044100200720042007491b6b4102746a200b3602002001200128022841016a360228200141186a2802002207200b4b0d1c200b200741e4d7c2800010f980808000000b200541b0046a41026a200541fc036a41026a2d00003a0000200541bf046a20054198016a41086a290300370000200541c7046a200541a8016a290300370000200541cf046a200541b0016a2d00003a0000200520052f01fc033b01b0042005200b3600b30420052005290398013700b7042005200e3a00e1012005201c3a00e0012005201b3602dc012005201d3602d801200541d0046a2001200541b0046a200541d8016a10868680800020052802d404210b20052802d004450d1a200041083a00002000200b3602040240200528028004450d00200f2802284129490d0020052802840441002802c0a3c68000118080808000000b200541f4036a2802004129490d0620052802cc0341002802c0a3c68000118080808000000c060b4101210641022107200a2d00244102470d004102210641032107200a2d00484102470d000240200a2d006c4102460d0041032106410421070c010b0240200a2d0090014102460d0041042106410521070c010b0240200a2d00b4014102460d0041052106410621070c010b0240200a2d00d8014102460d0041062106410721070c010b0240200a2d00fc014102460d0041072106410821070c010b0240200a2d00a0024102460d0041082106410921070c010b0240200a2d00c4024102460d0041092106410a21070c010b0240200a2d00e8024102460d00410a2106410b21070c010b0240200a2d008c034102460d00410b2106410c21070c010b0240200a2d00b0034102460d00410c2106410d21070c010b0240200a2d00d4034102460d00410d2106410e21070c010b0240200a2d00f8034102460d00410e2106410f21070c010b200a2d009c044102460d01410f2106411021070b200741246c21070340200741246a220441e404460d02200a20076a210b20042107200b2d00004102460d000c180b0b200941ff01714103460d15200020082900003700052000412c6a200841276a280000360000200041256a200841206a2900003700002000411d6a200841186a290000370000200041156a200841106a2900003700002000410d6a200841086a290000370000200542003702dc01200541a0d1c280003602d801200041306a200541d8016a10d98d808000200020093a0004200041053a00000c010b200941ff01714103470d15200520063a00d004200541d8016a41286a22074100360200200541d8016a200541d0046a200541d0046a41016a10f58d808000200541086a41286a220b2007280200360200200541086a41206a2208200541d8016a41206a290200370300200541086a41186a2209200541d8016a41186a290200370300200541086a41106a220c200541d8016a41106a290200370300200541086a41086a220d200541d8016a41086a290200370300200520052902d801370308200a200641246c6a22072d00002104200741023a000020044102460d0b2005413d6a2007290001370000200541e4006a2005290308370200200541386a41246a200741206a280000360000200541d5006a200741196a290000370000200541cd006a200741116a290000370000200541c5006a200741096a290000370000200541ec006a200d290300370200200541f4006a200c290300370200200541fc006a200929030037020020054184016a20082903003702002005418c016a200b28020036020020054101360260200541063a0038200520043a003c20002001200541386a20034100108a868080000b200a41002802c0a3c68000118080808000000c170b200541a0016a200241c4006a290200370300200541a8016a200241cc006a290200370300200541b0016a200241d4006a29020037030020052002413c6a2902003703980141012107200241016a2108200241306a2109200241dc006a2802002110200241386a2802002111200241346a2802002113200228023021124100210c024002400240200228022c220a2d0000220d4102470d004101210c41022107200a2d0024220d4102470d004102210c41032107200a2d0048220d4102470d000240200a2d006c220d4102460d004103210c410421070c010b0240200a2d009001220d4102460d004104210c410521070c010b0240200a2d00b401220d4102460d004105210c410621070c010b0240200a2d00d801220d4102460d004106210c410721070c010b0240200a2d00fc01220d4102460d004107210c410821070c010b0240200a2d00a002220d4102460d004108210c410921070c010b0240200a2d00c402220d4102460d004109210c410a21070c010b0240200a2d00e802220d4102460d00410a210c410b21070c010b0240200a2d008c03220d4102460d00410b210c410c21070c010b0240200a2d00b003220d4102460d00410c210c410d21070c010b0240200a2d00d403220d4102460d00410d210c410e21070c010b0240200a2d00f803220d4102460d00410e210c410f21070c010b200a2d009c04220d4102460d01410f210c411021070b200741246c21070340200741246a220441e404460d02200a20076a210b20042107200b2d00004102460d000c140b0b200641ff01714103460d1120002008290000370005200020092902003702302000412c6a200841276a280000360000200041256a200841206a2900003700002000411d6a200841186a290000370000200041156a200841106a2900003700002000410d6a200841086a290000370000200041386a200941086a290200370200200041c0006a200941106a290200370200200041c8006a200941186a290200370200200041d0006a200941206a290200370200200041d8006a200941286a290200370200200020063a0004200041053a00000c100b200641ff01714103470d11200a200c41246c6a220741023a0000200d4102460d0a2007280204210b200541bc086a41026a200741036a2d00003a0000200541c0016a200741106a290200370300200541b8016a41106a200741186a290200370300200541b8016a41186a200741206a280200360200200520072f00013b01bc08200520072902083703b801200328020820112010201041284b1b41017420126b6a220841017621072003280204210420032802002103024002400240024020084101710d00200720044d0d012007200441f492c68000109581808000000b200720044b0d0e20072004490d0120072004419493c6800010f980808000000b410021042005410036028004200c4104742109410121060c010b200320076a22042d0000210841002106200541d8016a41286a22094100360200200541d8016a2003200410f686808000200541d0046a41286a221420092802002209360200200541d0046a41206a221e200541d8016a41206a290200370300200541d0046a41186a221f200541d8016a41186a290200370300200541d0046a41106a2220200541d8016a41106a290200370300200541d0046a41086a2221200541d8016a41086a290200370300200520052902d80122153703d004200541d0046a41047221042008417071200c72212202400240200541d0046a41044128200941284b22081b6a28020022162009412820081b460d002004201420081b21042015a7200541d0046a20081b21080c010b200541d0046a10f88680800020052802d404211620052802d00421080b200820166a20223a00002004200428020041016a360200200541ac046a20142802002208360200200541a4046a201e2903003702002005419c046a201f29030037020020054194046a20202903003702002005418c046a2021290300370200200520052903d00422153702840420054101360280042015a720054180046a41046a200841284b22091b2104200528028804200820091b21080b2008200720041b21082004200320041b21070240200d4101710d000240200141286a28020022032001411c6a220d2802002204470d00200d10a889808000200128021c2104200128022821030b200141206a280200200141246a28020020036a22034100200420032004491b6b4102746a200b3602002001200128022841016a360228200141186a2802002204200b4d0d0d200541d8016a200141146a280200200b4107746a220441800110848e8080001a200441043a0004200441083a00000c0f0b200541d8026a41026a200541bc086a41026a2d00003a0000200541e7026a200541b8016a41086a290300370000200541ef026a200541b8016a41106a290300370000200541f7026a200541d0016a2d00003a0000200520052f01bc083b01d8022005200b3600db02200520052903b8013700df02200520093a00d904200520063a00d804200520083602d404200520073602d004200541b0066a2001200541d8026a200541d0046a10868680800020052802b406210420052802b006450d0d200041083a0000200020043602040240200528028004450d0020052802ac044129490d0020052802840441002802c0a3c68000118080808000000b200a41002802c0a3c680001180808080000020104129490d00201341002802c0a3c68000118080808000000b20022d0000417c6a41ff01712207410420074104491b417e6a4103490d170c160b200c200741f8d5c2800010f980808000000b20072008418493c68000109581808000000b20072008418493c68000109581808000000b20072008419493c6800010f980808000000b419c94c68000413a41d894c6800010a181808000000b2004200c41ac95c68000109581808000000b2004200c41bc95c68000109581808000000b2004200c41cc95c6800010f980808000000b41c8d6c28000412441ecd6c2800010a181808000000b41c8d6c280004124418cd7c2800010a181808000000b20072004418493c68000109581808000000b200b200441e4d7c2800010f980808000000b200541d8016a200141106a20041089868080000b0240024020052d00d80122044108470d00200541f8026a41086a200541e5016a290000370300200541f8026a41106a200541ed016a290000370300200541f8026a41186a200541f5016a290000370300200541f8026a41206a200541fd016a2900003703002005419f036a20054184026a280000360000200541b8056a41086a20054194026a290200370300200541b8056a41106a2005419c026a290200370300200541b8056a41186a200541a4026a290200370300200541b8056a41206a200541ac026a290200370300200541e0056a200541b4026a2902003703002005200541dd016a2900003703f80220052005418c026a2902003703b80520054188026a280200210b20052d00dc0121040c010b200541b0076a41086a220d200541e1016a290000370300200541b0076a41106a2214200541e9016a290000370300200541b0076a41186a2216200541f1016a290000370300200541b0076a41206a221e200541f9016a290000370300200541b0076a41276a221f200541d8016a41286a280000360000200541b0066a41086a2220200541d8016a41386a290200370300200541b0066a41106a2221200541d8016a41c0006a290200370300200541b0066a41186a2222200541d8016a41c8006a290200370300200541b0066a41206a2223200541a8026a290200370300200541b0066a41286a2224200541b0026a290200370300200520052900d9013703b00720052005290288023703b006200528028402210b200541a8036a41186a2225200541d0026a290200370300200541a8036a41106a2226200541c8026a290200370300200541a8036a41086a2227200541c0026a290200370300200520052902b8023703a803200541b8056a41286a22034100360200200541b8056a2007200720086a10f686808000200541c8036a41286a22072003280200360200200541c8036a41206a2208200541b8056a41206a220f290200370300200541c8036a41186a2228200541b8056a41186a2229290200370300200541c8036a41106a222a200541b8056a41106a222b290200370300200541c8036a41086a222c200541b8056a41086a222d290200370300200541d0046a41086a2027290300370300200541d0046a41106a2026290300370300200541d0046a41186a2025290300370300200520052902b8053703c803200520052903a8033703d004200541d0046a41c8006a2007280200360200200541d0046a41c0006a2008290300370300200541d0046a41386a202829030037030020054180056a202a290300370300200541d0046a41286a202c290300370300200520052903c8033703f0042005419d056a20093a00002005419c056a20063a0000200141dc006a200541d0046a10958d8080001a200541f8026a41086a200d290300370300200541f8026a41106a2014290300370300200541f8026a41186a2016290300370300200541f8026a41206a201e290300370300200541f8026a41276a201f280000360000202d2020290300370300202b202129030037030020292022290300370300200f202329030037030020032024290300370300200520052903b0073703f802200520052903b0063703b8050b02400240024002402004417c6a41ff01712207410420074104491b417f6a0e0401000002000b419cd7c28000412841c4d7c2800010f880808000000b200541b0066a41286a200541b8056a41286a290300370300200541b0066a41206a200541b8056a41206a290300370300200541b0066a41186a200541b8056a41186a290300370300200541b0066a41106a200541b8056a41106a290300370300200541b0066a41086a2204200541b8056a41086a290300370300200520052903b8053703b006200541d0046a41086a22012011360200200541e4046a20054198016a41086a290300370200200541ec046a20054198016a41106a290300370200200541f4046a20054198016a41186a290300370200200520133602d404200520123602d00420052005290398013702dc04200520103602fc04200541c8036a41086a22074101360200200541013602c8032005200c3a00b0072005200541b0076a3602cc03200541d0046a200541c8036a10e78d80800020072004280200200541dc066a22042802002203200341284b22031b360200200520052802b406200541b0066a41047220031b3602cc03200520052802b0063602c803200541d0046a200541c8036a10e78d808000200041d8006a200541d0046a41286a290200370200200041d0006a200541d0046a41206a290200370200200041c8006a200541d0046a41186a290200370200200041c0006a200541d0046a41106a290200370200200041386a2001290200370200200020052902d004370230200541c8036a410b6a200541f8026a410b6a290000370000200541c8036a41136a200541f8026a41136a290000370000200541c8036a411b6a200541f8026a411b6a290000370000200541c8036a41236a200541f8026a41236a290000370000200520052900fb023700cb03200041053a00002000200b36022c200020052900c803370001200041096a2007290000370000200041116a200541c8036a41106a290000370000200041196a200541c8036a41186a290000370000200041216a200541c8036a41206a290000370000200041286a200541ef036a28000036000020042802004129490d0120052802b40641002802c0a3c68000118080808000000c010b200020043a0000200020052903f802370001200541b0066a41286a200541b8056a41286a290300370300200541b0066a41206a200541b8056a41206a290300370300200541b0066a41186a200541b8056a41186a290300370300200541b0066a41106a200541b8056a41106a290300370300200541b0066a41086a2207200541b8056a41086a290300370300200041096a200541f8026a41086a290300370000200041116a200541f8026a41106a290300370000200041196a200541f8026a41186a290300370000200041216a200541f8026a41206a290300370000200041286a2005419f036a280000360000200520052903b8053703b006200541d0046a41086a22042011360200200541e4046a20054198016a41086a290300370200200541ec046a20054198016a41106a290300370200200541f4046a20054198016a41186a290300370200200520133602d404200520123602d00420052005290398013702dc04200520103602fc04200541c8036a41086a22014101360200200541013602c8032005200c3a00b0072005200541b0076a3602cc03200541d0046a200541c8036a10e78d80800020012007280200200541dc066a22072802002203200341284b22031b360200200520052802b406200541b0066a41047220031b3602cc03200520052802b0063602c803200541d0046a200541c8036a10e78d808000200041d8006a200541d0046a41286a290200370200200041d0006a200541d0046a41206a290200370200200041c8006a200541d0046a41186a290200370200200041c0006a200541d0046a41106a290200370200200041386a2004290200370200200020052902d0043702302000200b36022c20072802004129490d0020052802b40641002802c0a3c68000118080808000000b200528028004450d0020052802ac044129490d0020052802840441002802c0a3c68000118080808000000b200a41002802c0a3c68000118080808000000c060b4188d6c28000412f41fcd6c2800010f880808000000b2000200929020037023020002008290000370001200041d8006a200941286a290200370200200041d0006a200941206a290200370200200041c8006a200941186a290200370200200041c0006a200941106a290200370200200041386a200941086a290200370200200041096a200841086a290000370000200041116a200841106a290000370000200041196a200841186a290000370000200041216a200841206a290000370000200041286a200841276a2800003600002000200a36022c200020063a00000c040b4188d6c28000412f41b8d6c2800010f880808000000b2000200a360230200020093a0004200041073a0000200020082900003700052000412c6a200841276a280000360000200041256a200841206a2900003700002000411d6a200841186a290000370000200041156a200841106a2900003700002000410d6a200841086a2900003700000c020b0240200141286a28020022042001411c6a220a2802002207470d00200a10a889808000200128021c2107200128022821040b200141206a280200200141246a28020020046a22044100200720042007491b6b4102746a200b3602002001200128022841016a360228200141186a2802002207200b4b0d00200b200741e4d7c2800010f980808000000b200141146a280200200b4107746a22072d0060212e20072d005c210b20072d0058210a20072d0054210920072d0050210620072d004c210c20072d0048211020072d0044211120072d0040211220072d003c211420072d0038211620072d0034211e20072d0030212020072d002c212120072d0028212220072d0024212420072d0020212520072d001c212620072d0018212f20072d0014212320072d0010211f20072d000c211320072d0008210d20072d0004210820072d00002104200741043a0004200741083a0000200741e1006a2130200741dd006a2131200741d9006a2132200741d5006a2133200741d1006a2134200741cd006a2135200741c9006a2136200741c5006a2137200741c1006a21382007413d6a2139200741396a213a200741356a213b200741316a213c2007412d6a213d200741296a213e200741256a213f200741216a21402007411d6a212d200741196a212c200741156a212b200741116a212a2007410d6a2129200741096a2128200741056a21270240024020044108460d00200541b8056a41026a200741036a2d00003a0000200541b0076a41026a202741026a2d00003a0000200541b8016a41026a202841026a2d00003a0000200541bc086a41026a202941026a2d00003a0000200541b8086a41026a202a41026a2d00003a0000200520072f00013b01b805200520272f00003b01b007200520282f00003b01b801200520292f00003b01bc082005202a2f00003b01b808200541b4086a41026a202b41026a2d00003a0000200541b0086a41026a202c41026a2d00003a0000200541ac086a41026a202d41026a2d00003a0000200541a8086a41026a204041026a2d00003a0000200541a4086a41026a203f41026a2d00003a00002005202b2f00003b01b4082005202c2f00003b01b0082005202d2f00003b01ac08200520402f00003b01a8082005203f2f00003b01a408200541a0086a41026a203e41026a2d00003a00002005203e2f00003b01a0082005419c086a41026a203d41026a2d00003a00002005203d2f00003b019c0820054198086a41026a203c41026a2d00003a00002005203c2f00003b01980820054194086a41026a203b41026a2d00003a00002005203b2f00003b01940820054190086a41026a203a41026a2d00003a00002005203a2f00003b0190082005418c086a41026a203941026a2d00003a0000200520392f00003b018c0820054188086a41026a203841026a2d00003a0000200520382f00003b01880820054184086a41026a203741026a2d00003a0000200520372f00003b01840820054180086a41026a203641026a2d00003a0000200520362f00003b018008200541fc076a41026a203541026a2d00003a0000200520352f00003b01fc07200541f8076a41026a203441026a2d00003a0000200520342f00003b01f807200541f4076a41026a203341026a2d00003a0000200520332f00003b01f407200541f0076a41026a203241026a2d00003a0000200520322f00003b01f007200541ec076a41026a203141026a2d00003a0000200520312f00003b01ec07200541b0066a41026a203041026a2d00003a0000200520302f00003b01b006200541f0016a200741fc006a280000360200200541e8016a200741f4006a290000370300200541d8016a41086a200741ec006a290000370300200520072900643703d801200a2128200421272009210a20062109200c21062010210c20112110201221112014211220162114201e21162020211e2021212020222121202421222025212420262125202f21260c010b200541b8056a41026a202741026a2d00003a0000200541b0076a41026a202841026a2d00003a0000200541b8016a41026a202941026a2d00003a0000200541bc086a41026a202a41026a2d00003a0000200541b8086a41026a202b41026a2d00003a0000200520272f00003b01b805200520282f00003b01b007200520292f00003b01b8012005202a2f00003b01bc082005202b2f00003b01b808200541b4086a41026a202c41026a2d00003a0000200541b0086a41026a202d41026a2d00003a0000200541ac086a41026a204041026a2d00003a0000200541a8086a41026a203f41026a2d00003a0000200541a4086a41026a203e41026a2d00003a00002005202c2f00003b01b4082005202d2f00003b01b008200520402f00003b01ac082005203f2f00003b01a8082005203e2f00003b01a408200541a0086a41026a203d41026a2d00003a00002005203d2f00003b01a0082005419c086a41026a203c41026a2d00003a00002005203c2f00003b019c0820054198086a41026a203b41026a2d00003a00002005203b2f00003b01980820054194086a41026a203a41026a2d00003a00002005203a2f00003b01940820054190086a41026a203941026a2d00003a0000200520392f00003b0190082005418c086a41026a203841026a2d00003a0000200520382f00003b018c0820054188086a41026a203741026a2d00003a0000200520372f00003b01880820054184086a41026a203641026a2d00003a0000200520362f00003b01840820054180086a41026a203541026a2d00003a0000200520352f00003b018008200541fc076a41026a203441026a2d00003a0000200520342f00003b01fc07200541f8076a41026a203341026a2d00003a0000200520332f00003b01f807200541f4076a41026a203241026a2d00003a0000200520322f00003b01f407200541f0076a41026a203141026a2d00003a0000200520312f00003b01f007200541ec076a41026a203041026a2d00003a0000200520302f00003b01ec07200b2128202e210b20082127200d21082013210d201f21132023211f202f21230b200541d7046a200541b0076a41026a2d00003a0000200541db046a200541b8016a41026a2d00003a0000200541df046a200541bc086a41026a2d00003a0000200520052f01b8053b00d104200520083a00d404200520052f01b0073b00d5042005200d3a00d804200520052f01b8013b00d904200520133a00dc04200520052f01bc083b00dd042005200541b8056a41026a2d00003a00d304200520273a00d004200541e3046a200541b8086a41026a2d00003a0000200541e7046a200541b4086a41026a2d00003a0000200541eb046a200541b0086a41026a2d00003a0000200541ef046a200541ac086a41026a2d00003a00002005201f3a00e004200520233a00e404200520263a00e804200520253a00ec04200520052f01b8083b00e104200520052f01b4083b00e504200520052f01b0083b00e904200520052f01ac083b00ed04200541f3046a200541a8086a41026a2d00003a0000200541f7046a200541a4086a41026a2d00003a0000200541fb046a200541a0086a41026a2d00003a0000200541ff046a2005419c086a41026a2d00003a0000200520243a00f004200520223a00f404200520213a00f804200520203a00fc04200520052f01a8083b00f104200520052f01a4083b00f504200520052f01a0083b00f904200520052f019c083b00fd042005201e3a00800520054183056a20054198086a41026a2d00003a0000200520052f0198083b008105200520163a00840520054187056a20054194086a41026a2d00003a0000200520052f0194083b008505200520143a0088052005418b056a20054190086a41026a2d00003a0000200520052f0190083b008905200520123a008c052005418f056a2005418c086a41026a2d00003a0000200520052f018c083b008d05200520113a00900520054193056a20054188086a41026a2d00003a0000200520052f0188083b009105200520103a00940520054197056a20054184086a41026a2d00003a0000200520052f0184083b0095052005200c3a0098052005419b056a20054180086a41026a2d00003a0000200520052f0180083b009905200520063a009c052005419f056a200541fc076a41026a2d00003a0000200520052f01fc073b009d05200520093a00a005200541a3056a200541f8076a41026a2d00003a0000200520052f01f8073b00a1052005200a3a00a405200541a7056a200541f4076a41026a2d00003a0000200520052f01f4073b00a505200520283a00a805200541ab056a200541f0076a41026a2d00003a0000200520052f01f0073b00a9052005200b3a00ac05200541af056a200541ec076a41026a2d00003a0000200520052f01ec073b00ad05200541b4056a41026a200541b0066a41026a2d00003a0000200520052f01b0063b01b4054108212a200541f8026a41086a200541d8016a41086a290300370300200541f8026a41106a200541d8016a41106a290300370300200541f8026a41186a200541d8016a41186a280200360200200520052903d8013703f802200541d0046a41046a210702400240024002400240024002402027417c6a41ff01712229410420294104491b417f6a0e020102000b200541ad056a2107200541a9056a2103200541a5056a2129200541a1056a212b2005419d056a212c20054199056a212d20054195056a214020054191056a213f2005418d056a213e20054189056a213d20054185056a213c20054181056a213b200541fd046a213a200541f9046a2139200541f5046a2138200541f1046a2137200541ed046a2136200541e9046a2135200541e5046a2134200541e1046a2133200541dd046a2132200541d9046a2131200541d5046a2130200541d0046a410172211d20044108470d02200541e2076a2104200541e6076a210e200541e9076a211b200541ec076a211c200541f0076a212f200541f4076a2141200541f8076a2142200541fc076a214320054180086a214420054184086a214520054188086a21462005418c086a214720054190086a214820054194086a214920054198086a214a2005419c086a214b200541a0086a214c200541a4086a214d200541a8086a214e200541ac086a214f200541b0086a2150200541b4086a2151200541b8086a2152200541bc086a21530c030b200541b0066a41286a20054180056a220b41286a290200370300200541b0066a41206a200b41206a290200370300200541b0066a41186a200b41186a290200370300200541b0066a41106a200b41106a290200370300200541b0066a41086a200b41086a290200370300200541e0066a41086a200741086a290200370300200541e0066a41106a200741106a290200370300200541e0066a41186a200741186a290200370300200541e0066a41206a220a200741206a290200370300200541e0066a41286a200741286a2802003602002005200b2902003703b006200520072902003703e006024020044108460d00200541ac076a41026a2207200541b4056a41026a2d00003a000020054190076a41086a2204200541f8026a41086a29030037030020054190076a41106a220b200541f8026a41106a29030037030020054190076a41186a2203200541f8026a41186a280200360200200520052f01b4053b01ac07200520052903f80237039007200541b8056a41286a22084100360200200541b8056a201d201d201b6a10f686808000200541b0076a41286a22092008280200360200200541b0076a41206a2208200541b8056a41206a290200370300200541b0076a41186a2206200541b8056a41186a290200370300200541b0076a41106a220c200541b8056a41106a290200370300200541b0076a41086a220d200541b8056a41086a290200370300200520052902b8053703b0072005202e3a00d801200520052f01ac073b00d901200520072d00003a00db01200541f4016a2003280200360200200541ec016a200b290300370200200541e4016a200429030037020020052005290390073702dc01200541a0026a200928020036020020054198026a200829030037020020054190026a200629030037020020054188026a200c290300370200200541d8016a41286a200d290300370200200520052903b0073702f801200541a5026a200e3a0000200541a4026a201c3a0000200141dc006a200541d8016a10958d8080001a0b200541d8016a41286a2207200541c8036a41286a290300370300200541d8016a41206a2204200541c8036a41206a290300370300200541d8016a41186a220b200541c8036a41186a290300370300200541d8016a41106a2201200541c8036a41106a290300370300200541d8016a41086a2203200541c8036a41086a290300370300200520052903c8033703d801200541b8056a41086a2208200541b0066a41086a280200200541dc066a22092802002206200641284b22061b360200200520052802b406200541b0066a41047220061b3602bc05200520052802b0063602b805200541d8016a200541b8056a10e78d808000200041d8006a2007290300370200200041d0006a2004290300370200200041c8006a200b290300370200200041c0006a2001290300370200200041386a2003290300370200200020052903d801370230200541c3056a200541e0066a41086a290300370000200541cb056a200541e0066a41106a290300370000200541d3056a200541e0066a41186a290300370000200541db056a200a290300370000200541e3056a200541e0066a41286a280200360000200520052903e0063700bb05200041053a0000200020052900b805370001200041096a2008290000370000200041116a200541b8056a41106a290000370000200041196a200541b8056a41186a290000370000200041216a200541b8056a41206a290000370000200041286a200541df056a29000037000020092802004129490d0320052802b40641002802c0a3c68000118080808000000c030b200541b8056a41286a200541d0046a41286a220b41286a290200370300200541b8056a41206a200b41206a290200370300200541b8056a41186a200b41186a290200370300200541b8056a41106a200b41106a290200370300200541b8056a41086a200b41086a290200370300200541e8056a41086a200741086a290200370300200541e8056a41106a200741106a290200370300200541e8056a41186a200741186a290200370300200541e8056a41206a220a200741206a2802003602002005200b2902003703b805200520072902003703e805024020044108460d00200541ac066a41026a2207200541b4056a41026a2d00003a000020054190066a41086a2204200541f8026a41086a29030037030020054190066a41106a220b200541f8026a41106a29030037030020054190066a41186a2208200541f8026a41186a280200360200200520052f01b4053b01ac06200520052903f80237039006200541b0066a41286a22094100360200200541b0066a201d201d201b6a10f686808000200541b0076a41286a22062009280200360200200541b0076a41206a2209200541b0066a41206a290200370300200541b0076a41186a220c200541b0066a41186a290200370300200541b0076a41106a220d200541b0066a41106a290200370300200541b0076a41086a2210200541b0066a41086a290200370300200520052902b0063703b0072005202e3a00d801200520052f01ac063b00d901200520072d00003a00db01200541f4016a2008280200360200200541ec016a200b290300370200200541e4016a200429030037020020052005290390063702dc01200541a0026a200628020036020020054198026a200929030037020020054190026a200c29030037020020054188026a200d290300370200200541d8016a41286a2010290300370200200520052903b0073702f801200541a5026a200e3a0000200541a4026a201c3a0000200141dc006a200541d8016a10958d8080001a0b200541b0066a41286a2207200541c8036a41286a290300370300200541b0066a41206a2204200541c8036a41206a290300370300200541b0066a41186a220b200541c8036a41186a290300370300200541b0066a41106a2208200541c8036a41106a290300370300200541b0066a41086a2209200541c8036a41086a290300370300200520052903c8033703b006200541d8016a41086a200541b8056a41086a280200200541e4056a2206280200220c200c41284b220c1b360200200520052802bc05200541b8056a410472200c1b3602dc01200520052802b8053602d801200541b0066a200541d8016a10e78d808000200541a8026a2007290300370200200541a0026a200429030037020020054198026a200b29030037020020054190026a200829030037020020054188026a2009290300370200200541e4016a200541e8056a41086a290300370200200541ec016a200541e8056a41106a290300370200200541f4016a200541e8056a41186a290300370200200541fc016a200a280200360200200520052903b00637028002200520052903e8053702dc01200541063a00d80120002001200541d8016a20034101108a8680800020062802004129490d0220052802bc0541002802c0a3c68000118080808000000c020b200541e2076a41026a200541b4056a41026a2d00003a0000200541b8056a41086a200541f8026a41086a290300370300200541b8056a41106a200541f8026a41106a290300370300200541b8056a41186a200541f8026a41186a280200360200200520052f01b4053b01e207200520052903f8023703b805200541e6076a2104200541e9076a210e200541ec076a211b200541f0076a211c200541f4076a212f200541f8076a2141200541fc076a214220054180086a214320054184086a214420054188086a21452005418c086a214620054190086a214720054194086a214820054198086a21492005419c086a214a200541a0086a214b200541a4086a214c200541a8086a214d200541ac086a214e200541b0086a214f200541b4086a2150200541b8086a2151200541bc086a2152200541b8016a21532027212a20082127200d21082013210d201f21132023211f202621232025212620242125202221242021212220202121201e21202016211e20142116201221142011211220102111200c21102006210c20092106200a21092028210a200b2128202e210b0b2053201d2f00003b0000205220302f00003b0000205120312f00003b0000205020322f00003b0000204f20332f00003b0000205341026a201d41026a2d00003a0000205241026a203041026a2d00003a0000205141026a203141026a2d00003a0000205041026a203241026a2d00003a0000204f41026a203341026a2d00003a0000204e41026a203441026a2d00003a0000204e20342f00003b0000204d20352f00003b0000204d41026a203541026a2d00003a0000204c20362f00003b0000204c41026a203641026a2d00003a0000204b20372f00003b0000204b41026a203741026a2d00003a0000204a41026a203841026a2d00003a0000204a20382f00003b0000204941026a203941026a2d00003a0000204920392f00003b0000204841026a203a41026a2d00003a00002048203a2f00003b0000204741026a203b41026a2d00003a00002047203b2f00003b0000204641026a203c41026a2d00003a00002046203c2f00003b0000204541026a203d41026a2d00003a00002045203d2f00003b0000204441026a203e41026a2d00003a00002044203e2f00003b0000204341026a203f41026a2d00003a00002043203f2f00003b0000204241026a204041026a2d00003a0000204220402f00003b0000204141026a202d41026a2d00003a00002041202d2f00003b0000202f41026a202c41026a2d00003a0000202f202c2f00003b0000201c41026a202b41026a2d00003a0000201c202b2f00003b0000201b41026a202941026a2d00003a0000201b20292f00003b0000200e41026a200341026a2d00003a0000200e20032f00003b0000200441026a200741026a2d00003a0000200420072f00003b0000200541d8016a41286a200541c8036a41286a290300370300200541d8016a41206a200541c8036a41206a290300370300200541d8016a41186a200541c8036a41186a290300370300200541d8016a41106a200541c8036a41106a290300370300200541d8016a41086a200541c8036a41086a290300370300200520052903c8033703d801024002400240200141286a2802002207450d0020012007417f6a360228200141246a22072007280200220741016a220441002001411c6a280200220320042003491b6b360200200141206a28020020074102746a2802002204200141186a28020022074f0d02200128021420044107746a220741046a200720072d00004108461b10e3858080002007202a3a0000200720273a0004200720083a00082007200d3a000c200720052f00b8013b0001200741036a200541b8016a41026a2d00003a0000200720052f00bc083b0005200741076a200541bc086a41026a2d00003a0000200720052f00b8083b00092007410b6a200541b8086a41026a2d00003a0000200720052f00b4083b000d2007410f6a200541b4086a41026a2d00003a0000200720133a00102007201f3a0014200720233a0018200720263a001c200720052f00b0083b0011200741136a200541b0086a41026a2d00003a0000200720052f00ac083b0015200741176a200541ac086a41026a2d00003a0000200720052f00a8083b00192007411b6a200541a8086a41026a2d00003a0000200720052f00a4083b001d2007411f6a200541a4086a41026a2d00003a0000200720253a0020200720243a0024200720223a0028200720213a002c200720052f00a0083b0021200741236a200541a0086a41026a2d00003a0000200720052f009c083b0025200741276a2005419c086a41026a2d00003a0000200720052f0098083b00292007412b6a20054198086a41026a2d00003a0000200720052f0094083b002d2007412f6a20054194086a41026a2d00003a0000200720203a0030200741336a20054190086a41026a2d00003a0000200720052f0090083b00312007201e3a0034200741376a2005418c086a41026a2d00003a0000200720052f008c083b0035200720163a00382007413b6a20054188086a41026a2d00003a0000200720052f0088083b0039200720143a003c2007413f6a20054184086a41026a2d00003a0000200720052f0084083b003d200720123a0040200741c3006a20054180086a41026a2d00003a0000200720052f0080083b0041200720113a0044200741c7006a200541fc076a41026a2d00003a0000200720052f00fc073b0045200720103a0048200741cb006a200541f8076a41026a2d00003a0000200720052f00f8073b00492007200c3a004c200741cf006a200541f4076a41026a2d00003a0000200720052f00f4073b004d200720063a0050200741d3006a200541f0076a41026a2d00003a0000200720052f00f0073b0051200720093a0054200741d7006a200541ec076a41026a2d00003a0000200720052f00ec073b00552007200a3a0058200741db006a200541e9076a41026a2d00003a0000200720052f00e9073b0059200720283a005c200741df006a200541e6076a41026a2d00003a0000200720052f00e6073b005d2007200b3a0060200741e3006a200541e2076a41026a2d00003a0000200720052f01e2073b0061200741fc006a200541d0056a280200360000200741f4006a200541c8056a290300370000200741ec006a200541b8056a41086a290300370000200720052903b8053700640c010b0240200141186a28020022072001280210470d00200141106a2007109e86808000200128021821070b200128021420074107746a2207202a3a0000200720052f00b8013b0001200720273a0004200720052f00bc083b0005200720083a0008200720052f00b8083b00092007200d3a000c200720052f00b4083b000d200741036a200541b8016a41026a2d00003a0000200741076a200541bc086a41026a2d00003a00002007410b6a200541b8086a41026a2d00003a00002007410f6a200541b4086a41026a2d00003a0000200720133a00102007201f3a0014200720233a0018200720263a001c200720052f00b0083b0011200741136a200541b0086a41026a2d00003a0000200720052f00ac083b0015200741176a200541ac086a41026a2d00003a0000200720052f00a8083b00192007411b6a200541a8086a41026a2d00003a0000200720052f00a4083b001d2007411f6a200541a4086a41026a2d00003a0000200720253a0020200741236a200541a0086a41026a2d00003a0000200720052f00a0083b0021200720243a0024200741276a2005419c086a41026a2d00003a0000200720052f009c083b0025200720223a00282007412b6a20054198086a41026a2d00003a0000200720052f0098083b0029200720213a002c2007412f6a20054194086a41026a2d00003a0000200720052f0094083b002d200720203a0030200741336a20054190086a41026a2d00003a0000200720052f0090083b00312007201e3a0034200741376a2005418c086a41026a2d00003a0000200720052f008c083b0035200720163a00382007413b6a20054188086a41026a2d00003a0000200720052f0088083b0039200720143a003c2007413f6a20054184086a41026a2d00003a0000200720052f0084083b003d200720123a0040200741c3006a20054180086a41026a2d00003a0000200720052f0080083b0041200720113a0044200741c7006a200541fc076a41026a2d00003a0000200720052f00fc073b0045200720103a0048200741cb006a200541f8076a41026a2d00003a0000200720052f00f8073b00492007200c3a004c200741cf006a200541f4076a41026a2d00003a0000200720052f00f4073b004d200720063a0050200741d3006a200541f0076a41026a2d00003a0000200720052f00f0073b0051200720093a0054200741d7006a200541ec076a41026a2d00003a0000200720052f00ec073b00552007200a3a0058200741db006a200541e9076a41026a2d00003a0000200720052f00e9073b0059200720283a005c200741df006a200541e6076a41026a2d00003a0000200720052f00e6073b005d2007200b3a0060200741e3006a200541e2076a41026a2d00003a0000200720052f01e2073b0061200741fc006a200541d0056a280200360000200741f4006a200541c8056a290300370000200741ec006a200541c0056a290300370000200720052903b80537006420012001280218220441016a3602180b200020052903d801370228200020052f00b0073b0005200020052902b00637020c200041d0006a20054180026a290300370200200041c8006a200541f8016a290300370200200041c0006a200541d8016a41186a290300370200200041386a200541d8016a41106a290300370200200041306a200541d8016a41086a290300370200200041076a200541b0076a41026a2d00003a0000200041146a200541b0066a41086a2902003702002000411c6a200541b0066a41106a290200370200200041246a200541b0066a41186a280200360200200041063a0000200020052f00d8013b0001200041036a200541d8016a41026a2d00003a000020002004360208200041003a00040c020b2004200741d4d7c2800010f980808000000b20052d00d004417c6a41ff01712207410420074104491b417f6a4102490d00200541d0046a10e3858080000b200528028004450d00200f2802284129490d0020052802840441002802c0a3c68000118080808000000b20022d0000417c6a41ff01712207410420074104491b417e6a4103490d010b200210e3858080000b200541c0086a2480808080000bab1903057f017e177f2380808080004190046b2205248080808000024002400240024002400240024002400240024002400240024020022d00000d00200228020421020240200141286a28020022062001411c6a22072802002208470d00200710a889808000200128021c2108200128022821060b200141206a280200200141246a28020020066a22064100200820062008491b6b4102746a20023602002001200128022841016a360228200141186a280200220820024b0d012002200841e4d7c2800010f980808000000b200541a0016a200241196a29000037030020054198016a200241116a29000037030020054190016a200241096a29000037030020052002290001370388012003280208220841017621020240024020084101710d002002200328020422084b0d04200541003a00b003200520023602ac03200520032802003602a8030c010b2002200328020422084b0d04200220084f0d052005200328020022083602a803200520023602ac03200541b1036a200820026a2d000041f001713a0000200541013a00b0030b200541c4026a200120054188016a200541a8036a10868680800020052802c802210220052802c4020d010240200141286a28020022062001411c6a22072802002208470d00200710a889808000200128021c2108200128022821060b200141206a280200200141246a28020020066a22064100200820062008491b6b4102746a20023602002001200128022841016a360228200141186a280200220820024d0d090b200141146a28020020024107746a22022d00002108200541296a200241016a41df0010848e8080001a200241043a0004200241083a0000200541086a41086a200241ec006a290200370300200541186a200241f4006a290200370300200541206a200241fc006a2802003602002005200229026437030820022802602102200541a9016a200541296a41df0010848e8080001a02400240024002400240024020084108470d00200541a8036a200541a9016a41036a41dc0010848e8080001a20052001360288042005200336028c042005200236028404200541c4026a20042001200541a8036a2003108c8680800020052802c40222034103460d0520052802a4032109200529029c03210a20052802980321062005280294032107200528029003210b200528028c03210c200528028803210d200528028403210e200528028003210f20052802fc02211020052802f802211120052802f402211220052802f002211320052802ec02211420052802e802211520052802e402211620052802e002211720052802dc02211820052802d802211920052802d402211a20052802d002211b20052802cc02211c20052802c8022108410121044108211d20030e03030201030b20032802082109200328020421192003280200211d200541a8036a410172200541296a41df0010848e8080001a20052001360288042005200336028c04200520083a00a803200541c4026a20042001200541a8036a2003108c8680800020052802c40222034103460d0420052902a003210a200528029c03211e20052802980321062005280294032107200528029003210b200528028c03210c200528028803210d200528028403210e200528028003210f20052802fc02211020052802f802211120052802f402211220052802f002211320052802ec02211420052802e802211520052802e402211620052802e002211720052802dc02211820052802d802211a20052802d402211b20052802d002211c20052802cc02211f20052802c802210802400240024020030e03000102000b200941017621040240024020094101710d000240200420194b0d00410021190c020b2004201941f492c68000109581808000000b200420194b0d0c200420194f0d0d201d20046a2d00004170712120410121190b200a422088a7210941002103200541c4026a41286a22214100360200200541c4026a201d201d20046a10f58d808000200541c8036a220441286a2021280200360200200441206a200541c4026a41206a290200370200200441186a200541c4026a41186a290200370200200441106a200541c4026a41106a2902003702004108211d200441086a200541c4026a41086a290200370200200420052902c402370200200541f5036a20203a0000200541f4036a20193a0000200541b4036a200541086a41086a290300370200200541bc036a200541086a41106a290300370200200541c4036a200541086a41186a280200360200200520023602a803200520052903083702ac03200141dc006a200541a8036a10958d8080001a200a422086201ead84210a41012104201a2119201b211a201c211b201f211c0c040b200541a8026a41186a200541086a41186a280200360200200541a8026a41106a200541086a41106a290300370300200541a8026a41086a200541086a41086a290300370300200520052903083703a802200841ff0171211d200841807e71210341002104201821192017211820162117201521162014211520132114201221132011211220102111200f2110200e210f200d210e200c210d200b210c2007210b20062107201e210620022109201f21080c030b200941017621030240024020094101710d000240200320194b0d00410021040c020b2003201941f492c68000109581808000000b200320194b0d0c200320194f0d0d201d20036a2d00004170712108410121040b200541c4026a41286a22064100360200200541c4026a201d201d20036a10f58d808000200541c8036a220341286a2006280200360200200341206a200541c4026a41206a290200370200200341186a200541c4026a41186a290200370200200341106a200541c4026a41106a290200370200200341086a200541c4026a41086a290200370200200320052902c402370200200541f5036a20083a0000200541f4036a20043a0000200541b4036a200541086a41086a290300370200200541bc036a200541086a41106a290300370200200541c4036a200541086a41186a280200360200200520023602a803200520052903083702ac03200141dc006a200541a8036a10958d8080001a0b410221040c020b41002103410021040b20054188026a41186a200541a8026a41186a28020036020020054188026a41106a200541a8026a41106a29030037030020054188026a41086a200541a8026a41086a290300370300200520052903a80237038802201d20037221030240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a221d41002001411c6a280200221e201d201e491b6b360200200141206a28020020024102746a2802002202200141186a280200221d4f0d0c200128021420024107746a220141046a200120012d00004108461b10e385808000200120093602602001200a37025820012006360254200120073602502001200b36024c2001200c3602482001200d3602442001200e3602402001200f36023c2001201036023820012011360234200120123602302001201336022c2001201436022820012015360224200120163602202001201736021c20012018360218200120193602142001201a3602102001201b36020c2001201c3602082001200836020420012003360200200141fc006a200541a0026a280200360200200141f4006a20054198026a290300370200200141ec006a20054188026a41086a29030037020020012005290388023702640c010b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200128021420024107746a220220093602602002200a37025820022006360254200220073602502002200b36024c2002200c3602482002200d3602442002200e3602402002200f36023c2002201036023820022011360234200220123602302002201336022c2002201436022820022015360224200220163602202002201736021c20022018360218200220193602142002201a3602102002201b36020c2002201c3602082002200836020420022003360200200241fc006a200541a0026a280200360200200241f4006a20054198026a290300370200200241ec006a20054190026a290300370200200220052903880237026420012001280218220241016a3602180b200020043a0004200020023602000c0b0b20052802c80221020b200041033a0004200020023602000c090b2002200841f492c68000109581808000000b20022008418493c68000109581808000000b20022008419493c6800010f980808000000b20042019418493c68000109581808000000b20042019419493c6800010f980808000000b20032019418493c68000109581808000000b20032019419493c6800010f980808000000b2002200841e4d7c2800010f980808000000b2002201d41d4d7c2800010f980808000000b20054190046a2480808080000be54502117f017e23808080800041a0046b2205248080808000200541086a200441086a28020022063602002005200429020037030020052802042107200541e4006a200341e00010848e8080001a41012108200520062007410174220946220a3a00c4014101210b4102210c024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020052d0064220d417c6a41ff0171220e4104200e4104491b0e051400010203140b200541c0036a41286a200541bc016a290200370300200541c0036a41206a200541b4016a290200370300200541c0036a41186a200541ac016a290200370300200541c0036a41106a200541a4016a290200370300200541c0036a41086a220f2005419c016a290200370300200541b0026a41086a200541f8006a290200370300200541b0026a41106a220b20054180016a290200370300200541b0026a41186a220e20054188016a28020036020020052005290294013703c0032005200541f0006a2902003703b002200541e4006a41086a2802002110200541e9006a2f00002103200541eb006a2d0000210720052d00682111200541e4006a412c6a2802002112200541e4006a41286a2802002113200520052802c003221436028002200520052802c403200541c0036a4104722215200541c0036a412c6a280200220c41284b22081b3602f8012005200f280200200c20081b22083602fc014101210c2003200741107472210f02400240200841017420146b2203200920066b470d00200541f8016a200510db8d8080002003460d0120052802c00321140b200541106a41286a201541286a280200360200200541106a41206a201541206a290200370300200541106a41186a201541186a290200370300200541106a41106a201541106a290200370300200541106a41086a201541086a290200370300200541c0006a41086a200541b0026a41086a290300370300200541c0006a41106a200b290300370300200541c0006a41186a200e280200360200200520052903b00237034020052015290200370310410521150c130b200428020421142004280200210c20042802082104200528028002211520052802fc012103200541e0026a41106a200541b8026a290300370200200541e0026a41186a200541b0026a41106a29030037020020054180036a200541b0026a41186a280200360200200520103602e402200520113a00e002200520052903b0023702e802200520123602880320052013360284032005200f3b00e1022005200f4110763a00e3022004200341017420156b6a221541017621040240024020154101710d00200420144b0d06200541003a00d001200520043602cc012005200c3602c8010c010b200420144b0d06200420144f0d072005200c3602c801200520043602cc01200541d1016a200c20046a2d000041f001713a0000200541013a00d0010b20022001200541e0026a200541c8016a1083868080004102210c024020052802ec0341294f0d00410521150c130b20052802c40341002802c0a3c6800011808080800000410521150c120b200541b0026a41286a200541b4016a290200370300200541b0026a41206a200541ac016a290200370300200541b0026a41186a200541a4016a290200370300200541b0026a41106a2005419c016a290200370300200541b0026a41086a220f20054194016a290200370300200541c8016a41086a200541f0006a290200370300200541c8016a41106a2214200541e4006a41146a290200370300200541c8016a41186a2211200541e4006a411c6a290200370300200541c8016a41206a2210200541e4006a41246a2802003602002005200529028c013703b002200520052902683703c801200520052802b0023602c8032005200f280200200541dc026a2212280200220f200f41284b220f1b3602c403200520052802b402200541b0026a410472200f1b3602c0034101210c0240200541c0036a200510db8d808000220f20052802c40341017420052802c8036b460d00200541106a41086a200541b0026a41146a290200370300200541106a41106a200541b0026a411c6a290200370300200541106a41186a200541b0026a41246a290200370300200541106a41206a2012280200360200200541c0006a41086a2014290300370300200541c0006a41106a2011290300370300200541c0006a41186a2010280200360200200520052902bc02370310200520052903d00137034020052f00c90120052d00cb0141107472210f4106211520052802cc01211020052d00c801211120052802b802211420052802b402211220052802b00221130c120b200541f8016a41086a200441086a2214280200221136020020142011200f6a360200200520042902003703f801200541c0036a2002200541c8016a20042001108b8680800020052802c00321100240024002400240024020052d00c4030e0400010204010b200541106a41086a200541c4026a290200370300200541206a200541cc026a290200370300200541286a200541d4026a290200370300200541306a200541dc026a280200360200200520052902bc02370310410021114106211520052802b802211420052802b402211220052802b00221130c150b200541c0036a41086a201036020020054190046a200541b0026a41286a29030037020020054188046a200541b0026a41206a29030037020020054180046a200541b0026a41186a290300370200200541f8036a200541b0026a41106a290300370200200541f0036a200541b0026a41086a290300370200200520052903b0023702e8034100210c200541003a00c403200541063a00c003200541e0026a2002200541c0036a200541f8016a4100108a8680800020052d00e00222154108460d01200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541106a41206a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c140b4102210c024020052802dc0241294f0d000c140b20052802b40241002802c0a3c68000118080808000000c130b20052802e402210420004103360200200020043602040c140b200041033602002000201036020420052802dc024129490d1320052802b40241002802c0a3c68000118080808000000c130b024002400240024002400240200a0d002005280294012114200541d8026a20054190016a280200360200200541d0026a200541e4006a41246a290200370300200541c8026a20054180016a290200370300200541c0026a200541f8006a290200370300200541b8026a200541f0006a290200370300200520052902683703b0022006410176220f20074f0d0120142005280200200f6a2d0000220f410f71200f41047620064101711b41246c6a220f2d00002111200f41023a000020114102460d02200541c8016a41096a200f41096a290000370000200541c8016a41116a200f41116a290000370000200541c8016a41196a200f41196a290000370000200541c8016a41206a200f41206a280000360000200541f8016a41086a200441086a2210280200220c360200200f29000121162010200c41016a360200200520113a00c801200520163700c901200520042902003703f801200541c0036a2002200541c8016a20042001108b8680800020052802c003210420052d00c4032211417e6a0e020407030b41032111200528029401211420052d00684103470d0441012108410721154101210b4101210c0c160b200f2007418cd5c2800010f980808000000b200541c8006a200541b0026a41106a290300370300200541c0006a41106a200541b0026a41186a290300370300200541c0006a41186a200541d0026a280200360200200520052903b80237034020052f00b10220052d00b30241107472210f4101210c0c110b200f2004360204200f41003a0000200541c0006a41086a200541b0026a41106a290300370300200541c0006a41106a200541b0026a41186a290300370300200541c0006a41186a200541b0026a41206a280200360200200520052903b80237034020052f00b10220052d00b30241107472210f201145210c0c100b200541ec036a200541b0026a41286a280200360200200541c0036a41246a200541b0026a41206a290300370200200541c0036a411c6a200541b0026a41186a290300370200200541c0036a41146a200541b0026a41106a290300370200200541cc036a200541b0026a41086a290300370200200520052903b0023702c403200520143602f003200541073a00c0034100210c200541e0026a2002200541c0036a200541f8016a4100108a86808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541e0026a41146a290200370300200541c0006a41106a200541e0026a411c6a290200370300200541c0006a41186a200541e0026a41246a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c110b20052802e402210420004103360200200020043602040c130b200541b0026a41286a200541e8006a220f41286a280200360200200541b0026a41206a200f41206a290200370300200541b0026a41186a200f41186a290200370300200541b0026a41106a200f41106a290200370300200541b0026a41086a200f41086a2902003703002005200f2902003703b00220042802082211410176210f0240024020114101710d00200f200428020422114b0d07200541003a00c8032005200f3602c403200520042802003602c0030c010b200f200428020422114b0d07200f20114f0d082005200428020022113602c0032005200f3602c403200541c9036a2011200f6a2d000041f001713a0000200541013a00c8030b20022001200541b0026a200541c0036a108386808000200541033a00c403200520143602f003200541073a00c003200541c8016a41086a200441086a280200360200200520042902003703c8014100210b200541e0026a2002200541c0036a200541c8016a4100108a86808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541386a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f410121084100210c0c120b20052802e402210420004103360200200020043602040c120b20054194016a210f0240200a0d00200541b0026a41286a200f41286a290200370300200541b0026a41206a200f41206a290200370300200541b0026a41186a200f41186a290200370300200541b0026a41106a200f41106a290200370300200541b0026a41086a2214200f41086a2902003703002005200f2902003703b0022005280290012112200541c8016a41286a200341286a280200360200200541c8016a41206a200341206a290200370300200541c8016a41186a200341186a290200370300200541c8016a41106a200341106a290200370300200541c8016a41086a200341086a290200370300200520032902003703c801200520052802b0023602c80320052014280200200541dc026a280200220f200f41284b22141b3602c403200520052802b402200541b0026a410472220f20141b3602c003200541c0036a200510db8d808000221420052802c40341017420052802c8036b2211470d0c2014200528020441017420052802086b470d0c41032115024020052d00c8014103470d00200541106a41086a200f41086a290200370300200541106a41106a200f41106a290200370300200541106a41186a200f41186a290200370300200541106a41206a200f41206a290200370300200541106a41286a200f41286a2802003602002005200f2902003703104101210c20052802b00221140c0e0b200541c0036a41286a200541c8016a41286a280200360200200541c0036a41206a200541c8016a41206a290300370300200541c0036a41186a200541c8016a41186a290300370300200541c0036a41106a200541c8016a41106a290300370300200541c0036a41086a200541c8016a41086a290300370300200520052903c8013703c003200541a0026a41086a200441086a220f28020020146a360200200520042902003703a002200541e0026a200541a0026a10dd8d80800020022001200541c0036a200541e0026a10838680800020054198046a200541b0026a41286a29030037020020054190046a200541b0026a41206a29030037020020054188046a200541b0026a41186a29030037020020054180046a200541b0026a41106a290300370200200541f8036a200541b0026a41086a290300370200200520052903b0023702f003200520123602ec03200541033a00c003200541f8016a41086a200f280200360200200520042902003703f8014100210c200541e0026a2002200541c0036a200541f8016a4100108a86808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541106a41206a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c0e0b200020052802e402360204200041033602000c120b41032115200d41ff01714103460d0a20054198046a200f41286a29020037020020054190046a200f41206a29020037020020054188046a200f41186a29020037020020054180046a200f41106a290200370200200541f8036a200f41086a2902003702002005200f2902003702f003200528029001211120042802082214410176210f0240024020144101710d00200f200428020422144b0d09200541003a00e8022005200f3602e402200520042802003602e0020c010b200f200428020422144b0d09200f20144f0d0a2005200428020022143602e0022005200f3602e402200541e9026a2014200f6a2d000041f001713a0000200541013a00e8020b200220012003200541e0026a108386808000200520113602ec03200541033a00c003200541b0026a41086a200441086a280200360200200520042902003703b00241002108200541e0026a2002200541c0036a200541b0026a4100108a86808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541386a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f4101210b4100210c0c110b20052802e402210420004103360200200020043602040c110b2000410336020020002004360204200541b0026a10e585808000201441002802c0a3c68000118080808000000c100b2004201441f492c68000109581808000000b20042014418493c68000109581808000000b20042014419493c6800010f980808000000b200f201141f492c68000109581808000000b200f2011418493c68000109581808000000b200f2011419493c6800010f980808000000b200f201441f492c68000109581808000000b200f2014418493c68000109581808000000b200f2014419493c6800010f980808000000b200541186a200541a0016a290200370300200541206a200541a8016a290200370300200541286a200541b0016a290200370300200541306a200541b8016a290200370300200541386a200541c0016a280200360200200520054198016a29020037031041012108200528029001211220052802940121144101210b4101210c0c050b0240024002400240024002400240024020142011490d00200528020820146a221041017622112005280204220c4f0d012012200528020020116a2d00002211410f71201141047620104101711b41246c6a22112d00002110201141023a000020104102460d02200541f8016a41096a201141096a290000370000200541f8016a41116a201141116a290000370000200541f8016a41196a201141196a290000370000200541f8016a41206a201141206a280000360000200541a0026a41086a200441086a220c280200221536020020112900012116200c201420156a41016a360200200520103a00f801200520163700f901200520042902003703a002200541c0036a2002200541f8016a20042001108b8680800020052802c003210420052d00c403220c417e6a0e020304060b200541e2006a20052d00cb013a0000200541106a41086a200f41086a290200370300200541106a41106a200f41106a290200370300200541106a41186a200f41186a290200370300200541106a41206a200f41206a290200370300200541106a41286a200f41286a280200360200200520052f00c9013b01602005200f29020037031020052802b002211420052d00c8012115200541c0006a41086a200541dc016a290200370300200541c0006a41106a200541e4016a290200370300200541c0006a41186a200541ec016a280200360200200520052902d40137034020052f00cd0120052d00cf0141107472210f0c060b2011200c418cd5c2800010f980808000000b200541e0006a41026a20052d00cb013a0000200541106a41086a200f41086a290200370300200541106a41106a200f41106a290200370300200541106a41186a200f41186a290200370300200541106a41206a200f41206a290200370300200541106a41286a200f41286a280200360200200520052f00c9013b01602005200f29020037031020052802b002211420052d00c8012115200541c0006a41086a200541dc016a290200370300200541c0006a41106a200541e4016a290200370300200541c0006a41186a200541c8016a41246a280200360200200520052902d40137034020052f00cd0120052d00cf0141107472210f0c040b20054198046a200541b0026a41286a29030037030020054190046a200541b0026a41206a29030037030020054188046a200541b0026a41186a29030037030020054180046a200541b0026a41106a290300370300200541f8036a200541b0026a41086a290300370300200541c0036a41086a200541c8016a41086a290300370300200541c0036a41106a200541c8016a41106a290300370300200541c0036a41186a200541c8016a41186a290300370300200541c0036a41206a200541c8016a41206a290300370300200541c0036a41286a200541c8016a41286a280200360200200520052903b0023703f003200520052903c8013703c003200520123602ec034100210c200541e0026a2002200541c0036a200541a0026a4100108a8680800020052d00e00222154108460d01200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c040b2000410336020020002004360204024020052d00c8014103460d00200541c8016a10e4858080000b201241002802c0a3c680001180808080000020052802dc024129490d0820052802b40241002802c0a3c68000118080808000000c080b20052802e402210420004103360200200020043602040c070b20112004360204201141003a0000200541e2006a20052d00cb013a0000200541c0006a41086a200541dc016a290200370300200541c0006a41106a200541e4016a290200370300200541c0006a41186a200541ec016a280200360200200520052f00c9013b0160200520052902d40137034020052802f001211320052802d001211020052d00cc01211120052d00c801211520052802b002211420052f00cd01210420052d00cf012103200541106a41286a200f41286a280200360200200541106a41206a200f41206a290200370300200541106a41186a200f41186a290200370300200541106a41106a200f41106a290200370300200541106a41086a200f41086a2902003703002005200f2902003703102004200341107472210f200c45210c0c010b4101210c20052802f001211320052802d001211020052d00cc0121110b4101210b410021080c030b4107211520052802d802211220052802d402211320052802b402211020052d00b00221110b4100210b410121080c010b410121084101210b0b2000200f3b0009200020153a00042000200c360200200020052f01603b00052000201036020c200020113a0008200020052903403702102000201336022c20002012360230200020143602342000410b6a200f4110763a0000200041076a200541e2006a2d00003a0000200041186a200541c0006a41086a290300370200200041206a200541c0006a41106a290300370200200041286a200541c0006a41186a280200360200200041e0006a200541106a41286a280200360200200041d8006a200541106a41206a290300370200200041d0006a200541106a41186a290300370200200041c8006a200541106a41106a290300370200200041c0006a200541106a41086a29030037020020002005290310370238024002400240024020052d00642204417c6a41ff01712200410420004104491b417d6a0e020001040b200b450d0320052d006822004103460d03024020000e020004020b200541ec006a2200280200220420042802002204417f6a36020020044101460d020c030b2008450d0220044103460d020240024020040e020104000b200528028801220020002802002200417f6a36020020004101470d0320054188016a10e28a8080000c030b2005280268220020002802002200417f6a36020020004101470d02200541e8006a10e28a8080000c020b2005418c016a2200280200220420042802002204417f6a36020020044101470d010b200010e28a8080000b200541a0046a2480808080000bab1903057f017e177f2380808080004190046b2205248080808000024002400240024002400240024002400240024002400240024020022d00000d00200228020421020240200141286a28020022062001411c6a22072802002208470d00200710a889808000200128021c2108200128022821060b200141206a280200200141246a28020020066a22064100200820062008491b6b4102746a20023602002001200128022841016a360228200141186a280200220820024b0d012002200841e4d7c2800010f980808000000b200541a0016a200241196a29000037030020054198016a200241116a29000037030020054190016a200241096a29000037030020052002290001370388012003280208220841017621020240024020084101710d002002200328020422084b0d04200541003a00b003200520023602ac03200520032802003602a8030c010b2002200328020422084b0d04200220084f0d052005200328020022083602a803200520023602ac03200541b1036a200820026a2d000041f001713a0000200541013a00b0030b200541c4026a200120054188016a200541a8036a10878680800020052802c802210220052802c4020d010240200141286a28020022062001411c6a22072802002208470d00200710a889808000200128021c2108200128022821060b200141206a280200200141246a28020020066a22064100200820062008491b6b4102746a20023602002001200128022841016a360228200141186a280200220820024d0d090b200141146a28020020024107746a22022d00002108200541296a200241016a41df0010848e8080001a200241043a0004200241083a0000200541086a41086a200241ec006a290200370300200541186a200241f4006a290200370300200541206a200241fc006a2802003602002005200229026437030820022802602102200541a9016a200541296a41df0010848e8080001a02400240024002400240024020084108470d00200541a8036a200541a9016a41036a41dc0010848e8080001a20052001360288042005200336028c042005200236028404200541c4026a20042001200541a8036a2003108e8680800020052802c40222034103460d0520052802a4032109200529029c03210a20052802980321062005280294032107200528029003210b200528028c03210c200528028803210d200528028403210e200528028003210f20052802fc02211020052802f802211120052802f402211220052802f002211320052802ec02211420052802e802211520052802e402211620052802e002211720052802dc02211820052802d802211920052802d402211a20052802d002211b20052802cc02211c20052802c8022108410121044108211d20030e03030201030b20032802082109200328020421192003280200211d200541a8036a410172200541296a41df0010848e8080001a20052001360288042005200336028c04200520083a00a803200541c4026a20042001200541a8036a2003108e8680800020052802c40222034103460d0420052902a003210a200528029c03211e20052802980321062005280294032107200528029003210b200528028c03210c200528028803210d200528028403210e200528028003210f20052802fc02211020052802f802211120052802f402211220052802f002211320052802ec02211420052802e802211520052802e402211620052802e002211720052802dc02211820052802d802211a20052802d402211b20052802d002211c20052802cc02211f20052802c802210802400240024020030e03000102000b200941017621040240024020094101710d000240200420194b0d00410021190c020b2004201941f492c68000109581808000000b200420194b0d0c200420194f0d0d201d20046a2d00004170712120410121190b200a422088a7210941002103200541c4026a41286a22214100360200200541c4026a201d201d20046a10f58d808000200541c8036a220441286a2021280200360200200441206a200541c4026a41206a290200370200200441186a200541c4026a41186a290200370200200441106a200541c4026a41106a2902003702004108211d200441086a200541c4026a41086a290200370200200420052902c402370200200541f5036a20203a0000200541f4036a20193a0000200541b4036a200541086a41086a290300370200200541bc036a200541086a41106a290300370200200541c4036a200541086a41186a280200360200200520023602a803200520052903083702ac03200141dc006a200541a8036a10958d8080001a200a422086201ead84210a41012104201a2119201b211a201c211b201f211c0c040b200541a8026a41186a200541086a41186a280200360200200541a8026a41106a200541086a41106a290300370300200541a8026a41086a200541086a41086a290300370300200520052903083703a802200841ff0171211d200841807e71210341002104201821192017211820162117201521162014211520132114201221132011211220102111200f2110200e210f200d210e200c210d200b210c2007210b20062107201e210620022109201f21080c030b200941017621030240024020094101710d000240200320194b0d00410021040c020b2003201941f492c68000109581808000000b200320194b0d0c200320194f0d0d201d20036a2d00004170712108410121040b200541c4026a41286a22064100360200200541c4026a201d201d20036a10f58d808000200541c8036a220341286a2006280200360200200341206a200541c4026a41206a290200370200200341186a200541c4026a41186a290200370200200341106a200541c4026a41106a290200370200200341086a200541c4026a41086a290200370200200320052902c402370200200541f5036a20083a0000200541f4036a20043a0000200541b4036a200541086a41086a290300370200200541bc036a200541086a41106a290300370200200541c4036a200541086a41186a280200360200200520023602a803200520052903083702ac03200141dc006a200541a8036a10958d8080001a0b410221040c020b41002103410021040b20054188026a41186a200541a8026a41186a28020036020020054188026a41106a200541a8026a41106a29030037030020054188026a41086a200541a8026a41086a290300370300200520052903a80237038802201d20037221030240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a221d41002001411c6a280200221e201d201e491b6b360200200141206a28020020024102746a2802002202200141186a280200221d4f0d0c200128021420024107746a220141046a200120012d00004108461b10e385808000200120093602602001200a37025820012006360254200120073602502001200b36024c2001200c3602482001200d3602442001200e3602402001200f36023c2001201036023820012011360234200120123602302001201336022c2001201436022820012015360224200120163602202001201736021c20012018360218200120193602142001201a3602102001201b36020c2001201c3602082001200836020420012003360200200141fc006a200541a0026a280200360200200141f4006a20054198026a290300370200200141ec006a20054188026a41086a29030037020020012005290388023702640c010b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200128021420024107746a220220093602602002200a37025820022006360254200220073602502002200b36024c2002200c3602482002200d3602442002200e3602402002200f36023c2002201036023820022011360234200220123602302002201336022c2002201436022820022015360224200220163602202002201736021c20022018360218200220193602142002201a3602102002201b36020c2002201c3602082002200836020420022003360200200241fc006a200541a0026a280200360200200241f4006a20054198026a290300370200200241ec006a20054190026a290300370200200220052903880237026420012001280218220241016a3602180b200020043a0004200020023602000c0b0b20052802c80221020b200041033a0004200020023602000c090b2002200841f492c68000109581808000000b20022008418493c68000109581808000000b20022008419493c6800010f980808000000b20042019418493c68000109581808000000b20042019419493c6800010f980808000000b20032019418493c68000109581808000000b20032019419493c6800010f980808000000b2002200841e4d7c2800010f980808000000b2002201d41d4d7c2800010f980808000000b20054190046a2480808080000be54502117f017e23808080800041a0046b2205248080808000200541086a200441086a28020022063602002005200429020037030020052802042107200541e4006a200341e00010848e8080001a41012108200520062007410174220946220a3a00c4014101210b4102210c024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020052d0064220d417c6a41ff0171220e4104200e4104491b0e051400010203140b200541c0036a41286a200541bc016a290200370300200541c0036a41206a200541b4016a290200370300200541c0036a41186a200541ac016a290200370300200541c0036a41106a200541a4016a290200370300200541c0036a41086a220f2005419c016a290200370300200541b0026a41086a200541f8006a290200370300200541b0026a41106a220b20054180016a290200370300200541b0026a41186a220e20054188016a28020036020020052005290294013703c0032005200541f0006a2902003703b002200541e4006a41086a2802002110200541e9006a2f00002103200541eb006a2d0000210720052d00682111200541e4006a412c6a2802002112200541e4006a41286a2802002113200520052802c003221436028002200520052802c403200541c0036a4104722215200541c0036a412c6a280200220c41284b22081b3602f8012005200f280200200c20081b22083602fc014101210c2003200741107472210f02400240200841017420146b2203200920066b470d00200541f8016a200510db8d8080002003460d0120052802c00321140b200541106a41286a201541286a280200360200200541106a41206a201541206a290200370300200541106a41186a201541186a290200370300200541106a41106a201541106a290200370300200541106a41086a201541086a290200370300200541c0006a41086a200541b0026a41086a290300370300200541c0006a41106a200b290300370300200541c0006a41186a200e280200360200200520052903b00237034020052015290200370310410521150c130b200428020421142004280200210c20042802082104200528028002211520052802fc012103200541e0026a41106a200541b8026a290300370200200541e0026a41186a200541b0026a41106a29030037020020054180036a200541b0026a41186a280200360200200520103602e402200520113a00e002200520052903b0023702e802200520123602880320052013360284032005200f3b00e1022005200f4110763a00e3022004200341017420156b6a221541017621040240024020154101710d00200420144b0d06200541003a00d001200520043602cc012005200c3602c8010c010b200420144b0d06200420144f0d072005200c3602c801200520043602cc01200541d1016a200c20046a2d000041f001713a0000200541013a00d0010b20022001200541e0026a200541c8016a1083868080004102210c024020052802ec0341294f0d00410521150c130b20052802c40341002802c0a3c6800011808080800000410521150c120b200541b0026a41286a200541b4016a290200370300200541b0026a41206a200541ac016a290200370300200541b0026a41186a200541a4016a290200370300200541b0026a41106a2005419c016a290200370300200541b0026a41086a220f20054194016a290200370300200541c8016a41086a200541f0006a290200370300200541c8016a41106a2214200541e4006a41146a290200370300200541c8016a41186a2211200541e4006a411c6a290200370300200541c8016a41206a2210200541e4006a41246a2802003602002005200529028c013703b002200520052902683703c801200520052802b0023602c8032005200f280200200541dc026a2212280200220f200f41284b220f1b3602c403200520052802b402200541b0026a410472200f1b3602c0034101210c0240200541c0036a200510db8d808000220f20052802c40341017420052802c8036b460d00200541106a41086a200541b0026a41146a290200370300200541106a41106a200541b0026a411c6a290200370300200541106a41186a200541b0026a41246a290200370300200541106a41206a2012280200360200200541c0006a41086a2014290300370300200541c0006a41106a2011290300370300200541c0006a41186a2010280200360200200520052902bc02370310200520052903d00137034020052f00c90120052d00cb0141107472210f4106211520052802cc01211020052d00c801211120052802b802211420052802b402211220052802b00221130c120b200541f8016a41086a200441086a2214280200221136020020142011200f6a360200200520042902003703f801200541c0036a2002200541c8016a20042001108d8680800020052802c00321100240024002400240024020052d00c4030e0400010204010b200541106a41086a200541c4026a290200370300200541206a200541cc026a290200370300200541286a200541d4026a290200370300200541306a200541dc026a280200360200200520052902bc02370310410021114106211520052802b802211420052802b402211220052802b00221130c150b200541c0036a41086a201036020020054190046a200541b0026a41286a29030037020020054188046a200541b0026a41206a29030037020020054180046a200541b0026a41186a290300370200200541f8036a200541b0026a41106a290300370200200541f0036a200541b0026a41086a290300370200200520052903b0023702e8034100210c200541003a00c403200541063a00c003200541e0026a2002200541c0036a200541f8016a410010888680800020052d00e00222154108460d01200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541106a41206a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c140b4102210c024020052802dc0241294f0d000c140b20052802b40241002802c0a3c68000118080808000000c130b20052802e402210420004103360200200020043602040c140b200041033602002000201036020420052802dc024129490d1320052802b40241002802c0a3c68000118080808000000c130b024002400240024002400240200a0d002005280294012114200541d8026a20054190016a280200360200200541d0026a200541e4006a41246a290200370300200541c8026a20054180016a290200370300200541c0026a200541f8006a290200370300200541b8026a200541f0006a290200370300200520052902683703b0022006410176220f20074f0d0120142005280200200f6a2d0000220f410f71200f41047620064101711b41246c6a220f2d00002111200f41023a000020114102460d02200541c8016a41096a200f41096a290000370000200541c8016a41116a200f41116a290000370000200541c8016a41196a200f41196a290000370000200541c8016a41206a200f41206a280000360000200541f8016a41086a200441086a2210280200220c360200200f29000121162010200c41016a360200200520113a00c801200520163700c901200520042902003703f801200541c0036a2002200541c8016a20042001108d8680800020052802c003210420052d00c4032211417e6a0e020407030b41032111200528029401211420052d00684103470d0441012108410721154101210b4101210c0c160b200f2007418cd5c2800010f980808000000b200541c8006a200541b0026a41106a290300370300200541c0006a41106a200541b0026a41186a290300370300200541c0006a41186a200541d0026a280200360200200520052903b80237034020052f00b10220052d00b30241107472210f4101210c0c110b200f2004360204200f41003a0000200541c0006a41086a200541b0026a41106a290300370300200541c0006a41106a200541b0026a41186a290300370300200541c0006a41186a200541b0026a41206a280200360200200520052903b80237034020052f00b10220052d00b30241107472210f201145210c0c100b200541ec036a200541b0026a41286a280200360200200541c0036a41246a200541b0026a41206a290300370200200541c0036a411c6a200541b0026a41186a290300370200200541c0036a41146a200541b0026a41106a290300370200200541cc036a200541b0026a41086a290300370200200520052903b0023702c403200520143602f003200541073a00c0034100210c200541e0026a2002200541c0036a200541f8016a4100108886808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541e0026a41146a290200370300200541c0006a41106a200541e0026a411c6a290200370300200541c0006a41186a200541e0026a41246a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c110b20052802e402210420004103360200200020043602040c130b200541b0026a41286a200541e8006a220f41286a280200360200200541b0026a41206a200f41206a290200370300200541b0026a41186a200f41186a290200370300200541b0026a41106a200f41106a290200370300200541b0026a41086a200f41086a2902003703002005200f2902003703b00220042802082211410176210f0240024020114101710d00200f200428020422114b0d07200541003a00c8032005200f3602c403200520042802003602c0030c010b200f200428020422114b0d07200f20114f0d082005200428020022113602c0032005200f3602c403200541c9036a2011200f6a2d000041f001713a0000200541013a00c8030b20022001200541b0026a200541c0036a108386808000200541033a00c403200520143602f003200541073a00c003200541c8016a41086a200441086a280200360200200520042902003703c8014100210b200541e0026a2002200541c0036a200541c8016a4100108886808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541386a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f410121084100210c0c120b20052802e402210420004103360200200020043602040c120b20054194016a210f0240200a0d00200541b0026a41286a200f41286a290200370300200541b0026a41206a200f41206a290200370300200541b0026a41186a200f41186a290200370300200541b0026a41106a200f41106a290200370300200541b0026a41086a2214200f41086a2902003703002005200f2902003703b0022005280290012112200541c8016a41286a200341286a280200360200200541c8016a41206a200341206a290200370300200541c8016a41186a200341186a290200370300200541c8016a41106a200341106a290200370300200541c8016a41086a200341086a290200370300200520032902003703c801200520052802b0023602c80320052014280200200541dc026a280200220f200f41284b22141b3602c403200520052802b402200541b0026a410472220f20141b3602c003200541c0036a200510db8d808000221420052802c40341017420052802c8036b2211470d0c2014200528020441017420052802086b470d0c41032115024020052d00c8014103470d00200541106a41086a200f41086a290200370300200541106a41106a200f41106a290200370300200541106a41186a200f41186a290200370300200541106a41206a200f41206a290200370300200541106a41286a200f41286a2802003602002005200f2902003703104101210c20052802b00221140c0e0b200541c0036a41286a200541c8016a41286a280200360200200541c0036a41206a200541c8016a41206a290300370300200541c0036a41186a200541c8016a41186a290300370300200541c0036a41106a200541c8016a41106a290300370300200541c0036a41086a200541c8016a41086a290300370300200520052903c8013703c003200541a0026a41086a200441086a220f28020020146a360200200520042902003703a002200541e0026a200541a0026a10dd8d80800020022001200541c0036a200541e0026a10838680800020054198046a200541b0026a41286a29030037020020054190046a200541b0026a41206a29030037020020054188046a200541b0026a41186a29030037020020054180046a200541b0026a41106a290300370200200541f8036a200541b0026a41086a290300370200200520052903b0023702f003200520123602ec03200541033a00c003200541f8016a41086a200f280200360200200520042902003703f8014100210c200541e0026a2002200541c0036a200541f8016a4100108886808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541106a41206a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c0e0b200020052802e402360204200041033602000c120b41032115200d41ff01714103460d0a20054198046a200f41286a29020037020020054190046a200f41206a29020037020020054188046a200f41186a29020037020020054180046a200f41106a290200370200200541f8036a200f41086a2902003702002005200f2902003702f003200528029001211120042802082214410176210f0240024020144101710d00200f200428020422144b0d09200541003a00e8022005200f3602e402200520042802003602e0020c010b200f200428020422144b0d09200f20144f0d0a2005200428020022143602e0022005200f3602e402200541e9026a2014200f6a2d000041f001713a0000200541013a00e8020b200220012003200541e0026a108386808000200520113602ec03200541033a00c003200541b0026a41086a200441086a280200360200200520042902003703b00241002108200541e0026a2002200541c0036a200541b0026a4100108886808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541386a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f4101210b4100210c0c110b20052802e402210420004103360200200020043602040c110b2000410336020020002004360204200541b0026a10e585808000201441002802c0a3c68000118080808000000c100b2004201441f492c68000109581808000000b20042014418493c68000109581808000000b20042014419493c6800010f980808000000b200f201141f492c68000109581808000000b200f2011418493c68000109581808000000b200f2011419493c6800010f980808000000b200f201441f492c68000109581808000000b200f2014418493c68000109581808000000b200f2014419493c6800010f980808000000b200541186a200541a0016a290200370300200541206a200541a8016a290200370300200541286a200541b0016a290200370300200541306a200541b8016a290200370300200541386a200541c0016a280200360200200520054198016a29020037031041012108200528029001211220052802940121144101210b4101210c0c050b0240024002400240024002400240024020142011490d00200528020820146a221041017622112005280204220c4f0d012012200528020020116a2d00002211410f71201141047620104101711b41246c6a22112d00002110201141023a000020104102460d02200541f8016a41096a201141096a290000370000200541f8016a41116a201141116a290000370000200541f8016a41196a201141196a290000370000200541f8016a41206a201141206a280000360000200541a0026a41086a200441086a220c280200221536020020112900012116200c201420156a41016a360200200520103a00f801200520163700f901200520042902003703a002200541c0036a2002200541f8016a20042001108d8680800020052802c003210420052d00c403220c417e6a0e020304060b200541e2006a20052d00cb013a0000200541106a41086a200f41086a290200370300200541106a41106a200f41106a290200370300200541106a41186a200f41186a290200370300200541106a41206a200f41206a290200370300200541106a41286a200f41286a280200360200200520052f00c9013b01602005200f29020037031020052802b002211420052d00c8012115200541c0006a41086a200541dc016a290200370300200541c0006a41106a200541e4016a290200370300200541c0006a41186a200541ec016a280200360200200520052902d40137034020052f00cd0120052d00cf0141107472210f0c060b2011200c418cd5c2800010f980808000000b200541e0006a41026a20052d00cb013a0000200541106a41086a200f41086a290200370300200541106a41106a200f41106a290200370300200541106a41186a200f41186a290200370300200541106a41206a200f41206a290200370300200541106a41286a200f41286a280200360200200520052f00c9013b01602005200f29020037031020052802b002211420052d00c8012115200541c0006a41086a200541dc016a290200370300200541c0006a41106a200541e4016a290200370300200541c0006a41186a200541c8016a41246a280200360200200520052902d40137034020052f00cd0120052d00cf0141107472210f0c040b20054198046a200541b0026a41286a29030037030020054190046a200541b0026a41206a29030037030020054188046a200541b0026a41186a29030037030020054180046a200541b0026a41106a290300370300200541f8036a200541b0026a41086a290300370300200541c0036a41086a200541c8016a41086a290300370300200541c0036a41106a200541c8016a41106a290300370300200541c0036a41186a200541c8016a41186a290300370300200541c0036a41206a200541c8016a41206a290300370300200541c0036a41286a200541c8016a41286a280200360200200520052903b0023703f003200520052903c8013703c003200520123602ec034100210c200541e0026a2002200541c0036a200541a0026a410010888680800020052d00e00222154108460d01200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c040b2000410336020020002004360204024020052d00c8014103460d00200541c8016a10e4858080000b201241002802c0a3c680001180808080000020052802dc024129490d0820052802b40241002802c0a3c68000118080808000000c080b20052802e402210420004103360200200020043602040c070b20112004360204201141003a0000200541e2006a20052d00cb013a0000200541c0006a41086a200541dc016a290200370300200541c0006a41106a200541e4016a290200370300200541c0006a41186a200541ec016a280200360200200520052f00c9013b0160200520052902d40137034020052802f001211320052802d001211020052d00cc01211120052d00c801211520052802b002211420052f00cd01210420052d00cf012103200541106a41286a200f41286a280200360200200541106a41206a200f41206a290200370300200541106a41186a200f41186a290200370300200541106a41106a200f41106a290200370300200541106a41086a200f41086a2902003703002005200f2902003703102004200341107472210f200c45210c0c010b4101210c20052802f001211320052802d001211020052d00cc0121110b4101210b410021080c030b4107211520052802d802211220052802d402211320052802b402211020052d00b00221110b4100210b410121080c010b410121084101210b0b2000200f3b0009200020153a00042000200c360200200020052f01603b00052000201036020c200020113a0008200020052903403702102000201336022c20002012360230200020143602342000410b6a200f4110763a0000200041076a200541e2006a2d00003a0000200041186a200541c0006a41086a290300370200200041206a200541c0006a41106a290300370200200041286a200541c0006a41186a280200360200200041e0006a200541106a41286a280200360200200041d8006a200541106a41206a290300370200200041d0006a200541106a41186a290300370200200041c8006a200541106a41106a290300370200200041c0006a200541106a41086a29030037020020002005290310370238024002400240024020052d00642204417c6a41ff01712200410420004104491b417d6a0e020001040b200b450d0320052d006822004103460d03024020000e020004020b200541ec006a2200280200220420042802002204417f6a36020020044101460d020c030b2008450d0220044103460d020240024020040e020104000b200528028801220020002802002200417f6a36020020004101470d0320054188016a10e28a8080000c030b2005280268220020002802002200417f6a36020020004101470d02200541e8006a10e28a8080000c020b2005418c016a2200280200220420042802002204417f6a36020020044101470d010b200010e28a8080000b200541a0046a2480808080000bdb2802087f037e2380808080004180056b2201248080808000200028025c21022000410036025c200041e4006a22032802002104200341003602002001200036020c200041e0006a280200210320012004410020021b3602d003200120033602cc03200120023602c803200141003602c4032001200241004722043602c003200120033602bc03200120023602b803200141003602b403200120043602b003200141106a200141b0036a10e28c808000024020012d005c22034102460d002001419d026a210520014190016a41206a2102200141dd006a2106034020014190016a200141106a41cc0010848e8080001a200541026a200641026a2d00003a0000200520062f00003b0000200141106a41186a20014190016a41186a290200370300200141106a41106a20014190016a41106a290200370300200141106a41086a20014190016a41086a290200370300200141f0016a41086a200241086a290200370300200141f0016a41106a200241106a290200370300200141f0016a41186a200241186a290200370300200141f0016a41206a200241206a290200370300200141f0016a41286a2204200241286a2802003602002001200129029001370310200120022902003703f001200120033a009c022000280250210720002802542108200120012d009d023a00ad02200120033a00ac02200120012802f40120042802002203200341284b22031b3602a802200120012802f001200141f0016a20031b3602a4022007200141106a200141a4026a200828022411858080800000024020042802004129490d0020012802f00141002802c0a3c68000118080808000000b200141106a200141b0036a10e28c80800020012d005c22034102470d000b0b200141b0036a10a48d8080000240024020002d002c0d00200041306a28020021020240200041286a28020022042000411c6a22052802002203470d00200510a889808000200028021c2103200028022821040b200041206a280200200041246a28020020046a22044100200320042003491b6b4102746a20023602002000200028022841016a3602280240024002400240024002400240024002400240200041186a280200220320024d0d00200041146a28020020024107746a22022d00002103200141106a200241016a41ff0010848e8080001a200241043a0004200241083a000002400240024002400240024020034108470d0020014190016a200141136a41e00010848e8080001a4100210220012d0090012104200128020c280268450d052004417c6a41ff01712203410420034104491b0e050501020503050b200128020c22022802582204200129006f370000200441186a20014187016a290000370000200441106a200141ff006a290000370000200441086a200141f7006a290000370000200141ef006a210402400240200241286a2802002200450d0020022000417f6a360228200241246a22002000280200220041016a220541002002411c6a280200220620052006491b6b360200200241206a28020020004102746a2802002200200241186a28020022054f0d08200241146a28020020004107746a220241046a200220022d00004108461b10e385808000200220033a0000200241016a200141106a41df0010848e8080001a200241f8006a200441186a290000370000200241f0006a200441106a290000370000200241e8006a200441086a290000370000200220042900003700600c010b0240200241186a28020022002002280210470d00200241106a2000109e86808000200228021821000b200241146a28020020004107746a220020033a0000200041016a200141106a41df0010848e8080001a200041f8006a200441186a290000370000200041f0006a200441106a290000370000200041e8006a200441086a2900003700002000200429000037006020022002280218220041016a3602180b200128020c220241003a002c200241306a20003602000c0f0b200141c0016a21020c020b200141b8016a21020c010b200141c0016a21020b200120022802003602b8032001200241086a2802002002412c6a2802002203200341284b22031b3602b40320012002280204200241046a20031b3602b003200141f0016a41046a200141b0036a10e48d8080004101210220012d00900121040b200120023602f001200142003702cc02200141e0026a411f6a20014190016a41206a290000370000200141e0026a41186a20014190016a41196a290000370300200141e0026a41106a20014190016a41116a290000370300200141e0026a41086a20014190016a41096a29000037030020012001290091013703e00220012802b801210620012802bc01210020012802c001210320012001410c6a3602ac032001200141a4026a3602a803200141c4016a2102024002400240024002400240024002402004417c6a41ff01712205410420054104491b0e050001020304000b41002d00fca3c680001a410141002802c8a3c68000118180808000002202450d09200241003a0000200141013602dc02200120023602d802200141013602d4020c100b200141bc036a200241086a290200370200200141c4036a200241106a290200370200200141cc036a200241186a290200370200200141b0036a41246a200241206a290200370200200141dc036a2204200241286a280200360200200120033602b003200120022902003702b403200141e0036a41206a20014183036a280000360200200141e0036a41186a200141fb026a290000370300200141e0036a41106a200141f3026a290000370300200141e0036a41086a200141eb026a290000370300200120012900e3023703e00320012000360288042001200636028404200120033602a404200120012802b80320042802002202200241284b22041b22023602a004200120012802b403200141b0036a41046a20041b220536029c04024002400240024020012d00e0030e03010200020b200141003a00f804200120003602d8042001200641086a3602d404200141023a00d00420012001419c046a3602f404200141a8046a200141a4026a2001410c6a200141d0046a2001419c046a4100200110db8580800020012d00a8040d0c200141e8046a2202200141c1046a290000370300200141e0046a2203200141b9046a290000370300200141d0046a41086a200141b1046a290000370300200120012900a9043703d004024020012d00e1030d00200141fa036a2002290300370100200141f2036a2003290300370100200141ea036a200141d8046a290300370100200120012903d0043701e203200141013a00e1030b200141e0036a4102722104412021004101210620012802a0042102200128029c04210520012802a40421030c020b20012802e40341086a2104200141e0036a41086a2802002100410021060c010b41012106200141e0036a4101722104412021000b200120003602b004200120043602ac04200120063602a804200341017621040240024020034101710d00410021060240200420024b0d0020042100410021040c020b2004200241d492c68000109481808000000b200420024f0d0b41012106200441016a2100200520046a2d0000410f7121040b200120043a00dd04200120063a00dc04200141003602d8042001200220006b3602d4042001200520006a3602d004200141d4026a200141d0046a200241017420036b200141a8046a10d08c808000024002400240024020012d00e0030e020103000b200128028404220220022802002202417f6a36020020024101470d02200141e0036a41246a21020c010b20012802e403220220022802002202417f6a36020020024101470d01200141e0036a41047221020b200210e28a8080000b20012802dc034129490d0f20012802b40341002802c0a3c68000118080808000000c0f0b200141c4036a200241086a290200370200200141cc036a200241106a290200370200200141d4036a200241186a290200370200200141dc036a200241206a280200360200200120033602b803200120003602b403200120063602b003200120022902003702bc03200141d0046a41206a20014183036a280000360200200141d0046a41186a200141fb026a290000370300200141d0046a41106a200141f3026a290000370300200141d0046a41086a200141eb026a290000370300200120012900e3023703d0042001200141b0036a41046a41c0d1c28000109687808000200120063602a4042001200129030037029c04200141a8046a2001419c046a10dc8d808000200141003a00f80420012001419c046a3602f404200141e0036a200141a4026a2001410c6a200141d0046a2001419c046a4100200110db85808000200141d4026a200141a8046a20012802a00441017420012802a4046b200141e0036a10cb8c808000000b200141b0036a41206a20014183036a280000360200200141b0036a41186a200141fb026a290000370300200141b0036a41106a200141f3026a290000370300200141b0036a41086a200141eb026a290000370300200120012900e30222093703b003200120003602d803200120063602d4032009a741ff01714103470d01410221020c020b200141bc036a200241086a290200370200200141c4036a200241106a290200370200200141cc036a200241186a290200370200200141d4036a200241206a290200370200200141dc036a2205200241286a280200360200200120033602b003200120022902003702b403200141e0036a41096a20014190016a410172220241086a290000370000200141e0036a41116a200241106a290000370000200141e0036a41196a200241186a290000370000200141e0036a41206a2002411f6a2900003700002001200636028804200120022900003700e103200120043a00e0032001200336029804200120012802b80320052802002202200241284b22051b220236029404200120012802b403200141b0036a41046a20051b220736029004200441ff01714103470d02410221080c0b0b200141d0046a200141b0036a4100200141a4026a2001410c6a10f58580800020012902d404210920012802d00421020b200120093702e403200120023602e003200141d4026a200141d0046a200141e0036a10ca8c808000000b200141e0036a4101722105428080808080042109410121080240200441ff01710e03070800080b200141003a00f804200120063602d804200120012802840441086a3602d404200141023a00d004200120014190046a3602f404200141a8046a200141a4026a2001410c6a200141d0046a20014190046a4100200110db8580800020012d00a8040d05200141e8046a2202200141c1046a290000370300200141e0046a2203200141b9046a290000370300200141d0046a41086a200141b1046a290000370300200120012900a9043703d004024020012d00e1030d00200141fa036a2002290300370100200141f2036a2003290300370100200141ea036a200141d8046a290300370100200120012903d0043701e203200141013a00e1030b200141e0036a41027221052001280294042102200128029004210720012802980421030c070b2002200341e4d7c2800010f980808000000b2000200541d4d7c2800010f980808000000b4101410110b280808000000b200141dc046a4201370200200141013602d404200141fcd1c280003602d004200141e0818080003602940420014180d3c2800036029004200120014190046a3602d804200141d0046a4188d3c2800010f680808000000b2004200241e492c6800010f980808000000b200141dc046a4201370200200141013602d404200141fcd1c280003602d004200141e0818080003602a00420014180d3c2800036029c0420012001419c046a3602d804200141d0046a4188d3c2800010f680808000000b20012802e40341086a2105200141e0036a41086a3502004220862109410021080b20092005ad8421090b200341017621040240024020034101710d00410021060240200420024b0d0020042105410021040c020b2004200241d492c68000109481808000000b200420024f0d0341012106200441016a2105200720046a2d0000410f7121040b200120043a00b504200120063a00b404200141003602b0042001200220056b3602ac042001200720056a3602a804200141e0046a200141a8036a360200200141003602d804200120003602d0042001200041c0046a3602d4042001200141b0036a3602dc04200120093702a0042001200836029c04200141d4026a200141a8046a200241017420036b200141d0046a2001419c046a10cd8c808000024020012d00e00322024103460d0002400240024020020e020103000b200128028404220220022802002202417f6a36020020024101470d0220014184046a21020c010b20012802e403220220022802002202417f6a36020020024101470d01200141e4036a21020b200210e28a8080000b200041002802c0a3c680001180808080000020012802dc034129490d0020012802b40341002802c0a3c68000118080808000000b200128020c220241d4006a280200210320022802502102200141d0046a41086a41002802e0efc08000360200200141002902d8efc080003703d004200141b0036a2002200141d0046a20012802d802220420012802dc022200200328021c11878080800000200128020c2203280258220220012900b003370000200241086a200141b0036a41086a2205290000370000200241106a200141b0036a41106a2206290000370000200241186a200141b0036a41186a220729000037000020052003280258220241086a2900003703002006200241106a2900003703002007200241186a290000370300200120022900003703b0032003200141b0036a20042000200141f0016a10f885808000200128020c2202280258220341186a2900002109200341106a290000210a200341086a290000210b2002412d6a2003290000370000200241013a002c200241356a200b3700002002413d6a200a370000200241c5006a2009370000024020012802d402450d00200441002802c0a3c68000118080808000000b20012802cc024129490d0020012802a40241002802c0a3c68000118080808000000b20014180056a2480808080000f0b2004200241e492c6800010f980808000000bdb2802087f037e2380808080004180056b2201248080808000200028025c21022000410036025c200041e4006a22032802002104200341003602002001200036020c200041e0006a280200210320012004410020021b3602d003200120033602cc03200120023602c803200141003602c4032001200241004722043602c003200120033602bc03200120023602b803200141003602b403200120043602b003200141106a200141b0036a10e28c808000024020012d005c22034102460d002001419d026a210520014190016a41206a2102200141dd006a2106034020014190016a200141106a41cc0010848e8080001a200541026a200641026a2d00003a0000200520062f00003b0000200141106a41186a20014190016a41186a290200370300200141106a41106a20014190016a41106a290200370300200141106a41086a20014190016a41086a290200370300200141f0016a41086a200241086a290200370300200141f0016a41106a200241106a290200370300200141f0016a41186a200241186a290200370300200141f0016a41206a200241206a290200370300200141f0016a41286a2204200241286a2802003602002001200129029001370310200120022902003703f001200120033a009c022000280250210720002802542108200120012d009d023a00ad02200120033a00ac02200120012802f40120042802002203200341284b22031b3602a802200120012802f001200141f0016a20031b3602a4022007200141106a200141a4026a200828022411858080800000024020042802004129490d0020012802f00141002802c0a3c68000118080808000000b200141106a200141b0036a10e28c80800020012d005c22034102470d000b0b200141b0036a10a48d8080000240024020002d002c0d00200041306a28020021020240200041286a28020022042000411c6a22052802002203470d00200510a889808000200028021c2103200028022821040b200041206a280200200041246a28020020046a22044100200320042003491b6b4102746a20023602002000200028022841016a3602280240024002400240024002400240024002400240200041186a280200220320024d0d00200041146a28020020024107746a22022d00002103200141106a200241016a41ff0010848e8080001a200241043a0004200241083a000002400240024002400240024020034108470d0020014190016a200141136a41e00010848e8080001a4100210220012d0090012104200128020c280268450d052004417c6a41ff01712203410420034104491b0e050501020503050b200128020c22022802582204200129006f370000200441186a20014187016a290000370000200441106a200141ff006a290000370000200441086a200141f7006a290000370000200141ef006a210402400240200241286a2802002200450d0020022000417f6a360228200241246a22002000280200220041016a220541002002411c6a280200220620052006491b6b360200200241206a28020020004102746a2802002200200241186a28020022054f0d08200241146a28020020004107746a220241046a200220022d00004108461b10e385808000200220033a0000200241016a200141106a41df0010848e8080001a200241f8006a200441186a290000370000200241f0006a200441106a290000370000200241e8006a200441086a290000370000200220042900003700600c010b0240200241186a28020022002002280210470d00200241106a2000109e86808000200228021821000b200241146a28020020004107746a220020033a0000200041016a200141106a41df0010848e8080001a200041f8006a200441186a290000370000200041f0006a200441106a290000370000200041e8006a200441086a2900003700002000200429000037006020022002280218220041016a3602180b200128020c220241003a002c200241306a20003602000c0f0b200141c0016a21020c020b200141b8016a21020c010b200141c0016a21020b200120022802003602b8032001200241086a2802002002412c6a2802002203200341284b22031b3602b40320012002280204200241046a20031b3602b003200141f0016a41046a200141b0036a10e48d8080004101210220012d00900121040b200120023602f001200142003702cc02200141e0026a411f6a20014190016a41206a290000370000200141e0026a41186a20014190016a41196a290000370300200141e0026a41106a20014190016a41116a290000370300200141e0026a41086a20014190016a41096a29000037030020012001290091013703e00220012802b801210620012802bc01210020012802c001210320012001410c6a3602ac032001200141a4026a3602a803200141c4016a2102024002400240024002400240024002402004417c6a41ff01712205410420054104491b0e050001020304000b41002d00fca3c680001a410141002802c8a3c68000118180808000002202450d09200241003a0000200141013602dc02200120023602d802200141013602d4020c100b200141bc036a200241086a290200370200200141c4036a200241106a290200370200200141cc036a200241186a290200370200200141b0036a41246a200241206a290200370200200141dc036a2204200241286a280200360200200120033602b003200120022902003702b403200141e0036a41206a20014183036a280000360200200141e0036a41186a200141fb026a290000370300200141e0036a41106a200141f3026a290000370300200141e0036a41086a200141eb026a290000370300200120012900e3023703e00320012000360288042001200636028404200120033602a404200120012802b80320042802002202200241284b22041b22023602a004200120012802b403200141b0036a41046a20041b220536029c04024002400240024020012d00e0030e03010200020b200141003a00f804200120003602d8042001200641086a3602d404200141023a00d00420012001419c046a3602f404200141a8046a200141a4026a2001410c6a200141d0046a2001419c046a4100200110d98580800020012d00a8040d0c200141e8046a2202200141c1046a290000370300200141e0046a2203200141b9046a290000370300200141d0046a41086a200141b1046a290000370300200120012900a9043703d004024020012d00e1030d00200141fa036a2002290300370100200141f2036a2003290300370100200141ea036a200141d8046a290300370100200120012903d0043701e203200141013a00e1030b200141e0036a4102722104412021004101210620012802a0042102200128029c04210520012802a40421030c020b20012802e40341086a2104200141e0036a41086a2802002100410021060c010b41012106200141e0036a4101722104412021000b200120003602b004200120043602ac04200120063602a804200341017621040240024020034101710d00410021060240200420024b0d0020042100410021040c020b2004200241d492c68000109481808000000b200420024f0d0b41012106200441016a2100200520046a2d0000410f7121040b200120043a00dd04200120063a00dc04200141003602d8042001200220006b3602d4042001200520006a3602d004200141d4026a200141d0046a200241017420036b200141a8046a10d08c808000024002400240024020012d00e0030e020103000b200128028404220220022802002202417f6a36020020024101470d02200141e0036a41246a21020c010b20012802e403220220022802002202417f6a36020020024101470d01200141e0036a41047221020b200210e28a8080000b20012802dc034129490d0f20012802b40341002802c0a3c68000118080808000000c0f0b200141c4036a200241086a290200370200200141cc036a200241106a290200370200200141d4036a200241186a290200370200200141dc036a200241206a280200360200200120033602b803200120003602b403200120063602b003200120022902003702bc03200141d0046a41206a20014183036a280000360200200141d0046a41186a200141fb026a290000370300200141d0046a41106a200141f3026a290000370300200141d0046a41086a200141eb026a290000370300200120012900e3023703d0042001200141b0036a41046a41c0d1c28000109687808000200120063602a4042001200129030037029c04200141a8046a2001419c046a10dc8d808000200141003a00f80420012001419c046a3602f404200141e0036a200141a4026a2001410c6a200141d0046a2001419c046a4100200110d985808000200141d4026a200141a8046a20012802a00441017420012802a4046b200141e0036a10cb8c808000000b200141b0036a41206a20014183036a280000360200200141b0036a41186a200141fb026a290000370300200141b0036a41106a200141f3026a290000370300200141b0036a41086a200141eb026a290000370300200120012900e30222093703b003200120003602d803200120063602d4032009a741ff01714103470d01410221020c020b200141bc036a200241086a290200370200200141c4036a200241106a290200370200200141cc036a200241186a290200370200200141d4036a200241206a290200370200200141dc036a2205200241286a280200360200200120033602b003200120022902003702b403200141e0036a41096a20014190016a410172220241086a290000370000200141e0036a41116a200241106a290000370000200141e0036a41196a200241186a290000370000200141e0036a41206a2002411f6a2900003700002001200636028804200120022900003700e103200120043a00e0032001200336029804200120012802b80320052802002202200241284b22051b220236029404200120012802b403200141b0036a41046a20051b220736029004200441ff01714103470d02410221080c0b0b200141d0046a200141b0036a4100200141a4026a2001410c6a10f68580800020012902d404210920012802d00421020b200120093702e403200120023602e003200141d4026a200141d0046a200141e0036a10ca8c808000000b200141e0036a4101722105428080808080042109410121080240200441ff01710e03070800080b200141003a00f804200120063602d804200120012802840441086a3602d404200141023a00d004200120014190046a3602f404200141a8046a200141a4026a2001410c6a200141d0046a20014190046a4100200110d98580800020012d00a8040d05200141e8046a2202200141c1046a290000370300200141e0046a2203200141b9046a290000370300200141d0046a41086a200141b1046a290000370300200120012900a9043703d004024020012d00e1030d00200141fa036a2002290300370100200141f2036a2003290300370100200141ea036a200141d8046a290300370100200120012903d0043701e203200141013a00e1030b200141e0036a41027221052001280294042102200128029004210720012802980421030c070b2002200341e4d7c2800010f980808000000b2000200541d4d7c2800010f980808000000b4101410110b280808000000b200141dc046a4201370200200141013602d404200141fcd1c280003602d004200141e0818080003602940420014180d3c2800036029004200120014190046a3602d804200141d0046a4188d3c2800010f680808000000b2004200241e492c6800010f980808000000b200141dc046a4201370200200141013602d404200141fcd1c280003602d004200141e0818080003602a00420014180d3c2800036029c0420012001419c046a3602d804200141d0046a4188d3c2800010f680808000000b20012802e40341086a2105200141e0036a41086a3502004220862109410021080b20092005ad8421090b200341017621040240024020034101710d00410021060240200420024b0d0020042105410021040c020b2004200241d492c68000109481808000000b200420024f0d0341012106200441016a2105200720046a2d0000410f7121040b200120043a00b504200120063a00b404200141003602b0042001200220056b3602ac042001200720056a3602a804200141e0046a200141a8036a360200200141003602d804200120003602d0042001200041c0046a3602d4042001200141b0036a3602dc04200120093702a0042001200836029c04200141d4026a200141a8046a200241017420036b200141d0046a2001419c046a10cc8c808000024020012d00e00322024103460d0002400240024020020e020103000b200128028404220220022802002202417f6a36020020024101470d0220014184046a21020c010b20012802e403220220022802002202417f6a36020020024101470d01200141e4036a21020b200210e28a8080000b200041002802c0a3c680001180808080000020012802dc034129490d0020012802b40341002802c0a3c68000118080808000000b200128020c220241d4006a280200210320022802502102200141d0046a41086a41002802e0efc08000360200200141002902d8efc080003703d004200141b0036a2002200141d0046a20012802d802220420012802dc022200200328021c11878080800000200128020c2203280258220220012900b003370000200241086a200141b0036a41086a2205290000370000200241106a200141b0036a41106a2206290000370000200241186a200141b0036a41186a220729000037000020052003280258220241086a2900003703002006200241106a2900003703002007200241186a290000370300200120022900003703b0032003200141b0036a20042000200141f0016a10f985808000200128020c2202280258220341186a2900002109200341106a290000210a200341086a290000210b2002412d6a2003290000370000200241013a002c200241356a200b3700002002413d6a200a370000200241c5006a2009370000024020012802d402450d00200441002802c0a3c68000118080808000000b20012802cc024129490d0020012802a40241002802c0a3c68000118080808000000b20014180056a2480808080000f0b2004200241e492c6800010f980808000000ba90601037f2380808080004190016b2206248080808000200641033a000c02400240024020054100480d00200541f5ffffff074f0d0102402005410b6a417c7122070d00410421080c030b41002d00fca3c680001a200741002802c8a3c680001181808080000022080d024104200710b280808000000b41fc9bc68000412b200641e0006a41a89cc6800041b89cc68000108981808000000b41e484c08000412b200641e0006a419085c08000419086c08000108981808000000b2008428180808010370200200841086a2004200510848e8080001a0240024020012d002c0d00200141306a2802002104410021070c010b200641d6006a2001412f6a2d00003a0000200641386a41086a2001413c6a290200370300200641c8006a200141c4006a290200370300200641d0006a200141cc006a2d00003a000020062001412d6a2f00003b01542006200141346a290200370338200141306a2802002104410121070b200641e0006a41106a200641386a41086a290300370200200641e0006a41186a200641386a41106a290300370200200641e0006a41206a200641386a41186a280200360200200620073a0060200620062f01543b006120062004360264200620062903383702682006200641d4006a41026a2d00003a00632006410036028c0120062003360288012006200236028401200641d8006a2001200641e0006a20064184016a200820052006410c6a108286808000200628025821050240024020062d005c4102460d00200141003a002c2000200629020c370200200141306a2005360200200041086a2006410c6a41086a290200370200200041106a2006410c6a41106a290200370200200041186a2006410c6a41186a290200370200200041206a2006410c6a41206a290200370200200041286a2006410c6a41286a2802003602000c010b200041043a00002000200536020420062d000c22014103460d000240024020010e020102000b2006280230220120012802002201417f6a36020020014101470d01200641306a10e28a8080000c010b2006280210220120012802002201417f6a36020020014101470d00200641106a10e28a8080000b20064190016a2480808080000ba90601037f2380808080004190016b2206248080808000200641033a000c02400240024020054100480d00200541f5ffffff074f0d0102402005410b6a417c7122070d00410421080c030b41002d00fca3c680001a200741002802c8a3c680001181808080000022080d024104200710b280808000000b41fc9bc68000412b200641e0006a41a89cc6800041b89cc68000108981808000000b41e484c08000412b200641e0006a419085c08000419086c08000108981808000000b2008428180808010370200200841086a2004200510848e8080001a0240024020012d002c0d00200141306a2802002104410021070c010b200641d6006a2001412f6a2d00003a0000200641386a41086a2001413c6a290200370300200641c8006a200141c4006a290200370300200641d0006a200141cc006a2d00003a000020062001412d6a2f00003b01542006200141346a290200370338200141306a2802002104410121070b200641e0006a41106a200641386a41086a290300370200200641e0006a41186a200641386a41106a290300370200200641e0006a41206a200641386a41186a280200360200200620073a0060200620062f01543b006120062004360264200620062903383702682006200641d4006a41026a2d00003a00632006410036028c0120062003360288012006200236028401200641d8006a2001200641e0006a20064184016a200820052006410c6a108586808000200628025821050240024020062d005c4102460d00200141003a002c2000200629020c370200200141306a2005360200200041086a2006410c6a41086a290200370200200041106a2006410c6a41106a290200370200200041186a2006410c6a41186a290200370200200041206a2006410c6a41206a290200370200200041286a2006410c6a41286a2802003602000c010b200041043a00002000200536020420062d000c22014103460d000240024020010e020102000b2006280230220120012802002201417f6a36020020014101470d01200641306a10e28a8080000c010b2006280210220120012802002201417f6a36020020014101470d00200641106a10e28a8080000b20064190016a2480808080000ba60601037f2380808080004190016b22042480808080000240024020012d002c0d00200141306a2802002105410021060c010b2004412a6a2001412f6a2d00003a0000200441106a2001413c6a290200370300200441186a200141c4006a290200370300200441206a200141cc006a2d00003a000020042001412d6a2f00003b01282004200141346a290200370308200141306a2802002105410121060b20044100360234200420033602302004200236022c200441033a0038200441ec006a41106a2203200441086a41086a290300370200200441ec006a41186a2202200441086a41106a2903003702002004418c016a200441086a41186a280200360200200420063a006c200420042f01283b006d20042004412a6a2d00003a006f2004200536027020042004290308370274200441e4006a2001200441ec006a2004412c6a200441386a108d86808000200428026421050240024002400240024020042d0068417e6a0e020100020b200041043a00002000200536020420042d003822014103460d030240024020010e020105000b200428025c220120012802002201417f6a36020020014101470d04200441dc006a10e28a8080000c040b200428023c220120012802002201417f6a36020020014101470d032004413c6a10e28a8080000c030b200441ec006a41badbc5800041014100280298a3c6800011858080800000200141013a002c2001412d6a200429006c370000200141356a200441ec006a41086a22052900003700002001413d6a2003290000370000200141c5006a2002290000370000200441ec006a41badbc5800041014100280298a3c680001185808080000020012802582201200429006c370000200141186a2002290000370000200141106a2003290000370000200141086a20052900003700000c010b200141003a002c200141306a20053602000b20002004290238370200200041286a200441386a41286a280200360200200041206a200441386a41206a290200370200200041186a200441386a41186a290200370200200041106a200441386a41106a290200370200200041086a200441386a41086a2902003702000b20044190016a2480808080000ba60601037f2380808080004190016b22042480808080000240024020012d002c0d00200141306a2802002105410021060c010b2004412a6a2001412f6a2d00003a0000200441106a2001413c6a290200370300200441186a200141c4006a290200370300200441206a200141cc006a2d00003a000020042001412d6a2f00003b01282004200141346a290200370308200141306a2802002105410121060b20044100360234200420033602302004200236022c200441033a0038200441ec006a41106a2203200441086a41086a290300370200200441ec006a41186a2202200441086a41106a2903003702002004418c016a200441086a41186a280200360200200420063a006c200420042f01283b006d20042004412a6a2d00003a006f2004200536027020042004290308370274200441e4006a2001200441ec006a2004412c6a200441386a108b86808000200428026421050240024002400240024020042d0068417e6a0e020100020b200041043a00002000200536020420042d003822014103460d030240024020010e020105000b200428025c220120012802002201417f6a36020020014101470d04200441dc006a10e28a8080000c040b200428023c220120012802002201417f6a36020020014101470d032004413c6a10e28a8080000c030b200441ec006a41badbc5800041014100280298a3c6800011858080800000200141013a002c2001412d6a200429006c370000200141356a200441ec006a41086a22052900003700002001413d6a2003290000370000200141c5006a2002290000370000200441ec006a41badbc5800041014100280298a3c680001185808080000020012802582201200429006c370000200141186a2002290000370000200141106a2003290000370000200141086a20052900003700000c010b200141003a002c200141306a20053602000b20002004290238370200200041286a200441386a41286a280200360200200041206a200441386a41206a290200370200200041186a200441386a41186a290200370200200041106a200441386a41106a290200370200200041086a200441386a41086a2902003702000b20044190016a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000b8f0100024002400240024020010d00410121020c010b2001417f4c0d0102402002450d00200141002802c8a3c68000118180808000002202450d03200241002001108a8e8080001a0c010b41002d00fca3c680001a200141002802c8a3c68000118180808000002202450d020b20002002360204200020013602000f0b10ae80808000000b4101200110b280808000000bef0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141e8016c2104200141ccfbb4044941037421050240024020030d00200241003602180c010b200241083602182002200341e8016c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141306c2104200141abd5aa154941027421050240024020030d00200241003602180c010b200241043602182002200341306c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bee0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b2201410c6c2104200141abd5aad5004941027421050240024020030d00200241003602180c010b2002410436021820022003410c6c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141286c2104200141b4e6cc194941037421050240024020030d00200241003602180c010b200241083602182002200341286c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bee0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b2201410274210420014180808080024941027421050240024020030d00200241003602180c010b200241043602182002200341027436021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000be00101037f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014108200141084b1b2201417f73411f7621040240024020030d00200241003602180c010b2002200336021c20024101360218200220002802043602140b200241086a20042001200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141e8006c2104200141b2a7ec094941037421050240024020030d00200241003602180c010b200241083602182002200341e8006c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b22014107742104200141808080084941027421050240024020030d00200241003602180c010b200241043602182002200341077436021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941027421050240024020030d00200241003602180c010b200241043602182002200341386c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bea0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b22014180808020492104200141057421050240024020030d00200241003602180c010b200241013602182002200341057436021c200220002802043602140b200241086a20042005200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bee0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b22014104742104200141808080c0004941037421050240024020030d00200241003602180c010b200241083602182002200341047436021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bee0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b22014104742104200141808080c0004941027421050240024020030d00200241003602180c010b20022000280204360214200241043602182002200341047436021c0b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141146c2104200141e7cc99334941027421050240024020030d00200241003602180c010b200241043602182002200341146c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141186c2104200141d6aad52a4941027421050240024020030d00200241003602180c010b200241043602182002200341186c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241146c2104200241e7cc99334941027421050240024020010d00200341003602180c010b200341043602182003200141146c36021c200320002802043602140b200341086a20052004200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bf00101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024104742104200241808080c0004941027421050240024020010d00200341003602180c010b20032000280204360214200341043602182003200141047436021c0b200341086a20052004200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bf00101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b2202410c6c2104200241abd5aad5004941027421050240024020010d00200341003602180c010b2003410436021820032001410c6c36021c200320002802043602140b200341086a20052004200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b8d07010b7f02402000280200220428020020042802082200470d0020042000410110ab86808000200428020821000b200428020420006a41223a00002004200041016a22053602082002417f6a21062003417f732107200220036a2108410021092002210a02400340410021000240024002400240024002400240024002400240024003400240200a20006a220b2008470d00024020032009460d0002402009450d00200320094d0d04200220096a2c000041bf7f4c0d04200320096b21030b200220096a21000240200428020020056b20034f0d0020042005200310ab86808000200428020821050b200428020420056a2000200310848e8080001a2004200520036a22053602080b024020042802002005470d0020042005410110ab86808000200428020821050b200428020420056a41223a00002004200541016a36020841000f0b200041016a2100200b2d0000220c41a881c280006a2d0000220b450d000b02402009200920006a220d417f6a220e4f0d0002402009450d000240200320094b0d0020032009460d010c0f0b200220096a2c00004140480d0e0b02400240200e2003490d00200d20076a0d0f0c010b200620096a20006a2c000041bf7f4c0d0e0b0240200428020020056b2000417f6a220e4f0d0020042005200e10ab86808000200428020821050b200428020420056a200220096a200e10848e8080001a2004200520006a417f6a22053602080b200a20006a210a41b3d9c280002100200b41a47f6a0e1a080a0a0a0a0a010a0a0a060a0a0a0a0a0a0a050a0a0a040a0302070b2002200320092003418cd9c28000109781808000000b41b5d9c2800021000c060b200c410f71419881c280006a2d0000210b200c410476419881c280006a2d0000210c0240200428020020056b41054b0d0020042005410610ab86808000200428020821050b200428020420056a2200200b3a00052000200c3a0004200041dceac18103360000200541066a21050c060b41bdd9c2800021000c040b41bbd9c2800021000c030b41b9d9c2800021000c020b41b7d9c2800021000c010b200b4122470d0241b1d9c2800021000b0240200428020020056b41014b0d0020042005410210ab86808000200428020821050b200428020420056a20002f00003b0000200541026a21050b20042005360208200d21090c010b0b41f4d7c28000412841fcd8c2800010f880808000000b200220032009200920006a417f6a419cd9c28000109781808000000b830b010a7f0240024002400240024002400240024020002d00000e06000102050304000b024020012802002202280200200228020822006b41034b0d0020022000410410ab86808000200228020821000b200228020420006a41eeeab1e3063600002002200041046a3602080c050b20012802002102024020002d00010d0002402002280200200228020822006b41044b0d0020022000410510ab86808000200228020821000b2002200041056a360208200228020420006a220241002800acd9c28000360000200241046a41002d00b0d9c280003a000041000f0b02402002280200200228020822006b41034b0d0020022000410410ab86808000200228020821000b200228020420006a41f4e4d5ab063600002002200041046a3602080c040b200041086a2802002103024020012802002202280200200228020822046b2000410c6a28020022004f0d0020022004200010ab86808000200228020821040b200228020420046a2003200010848e8080001a2002200420006a3602080c030b2001200041046a10b0868080000f0b2000410c6a280200210402402001280200220528020020052802082202470d0020052002410110ab86808000200528020821020b200528020420026a41fb003a00002005200241016a22023602084180022106024020040d00024020052802002002470d0020052002410110ab86808000200528020821020b200528020420026a41fd003a00002005200241016a360208410021060b4100210220044100200028020422031b210720034100472108200041086a2802002104034002400240024002402007450d000240024020020d0020080d010b20080d0441f887c6800010a081808000000b41012108200321022004450d0220042100024020044107712203450d0003402000417f6a210020022802bc0221022003417f6a22030d000b0b200441084f0d010c020b0240200641ff01710d004100210020064180fe0371450d070240200528020020052802082202470d0020052002410110ab86808000200528020821020b200528020420026a41fd003a00002005200241016a36020841000f0b41f4d7c280004128419cdcc2800010f880808000000b034020022802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022102200041786a22000d000b0b41002104410021030b024002400240200420022f01ba02490d00034020022802b0012200450d02200341016a210320022f01b802210420002102200420002f01ba024f0d000b0b200441016a2109024020030d00200221000c020b200220094102746a41bc026a2802002100410021092003417f6a220a450d012003417e6a210b0240200a4107712203450d000340200a417f6a210a20002802bc0221002003417f6a22030d000b0b200b4107490d01034020002802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022100200a41786a220a0d000c020b0b41e887c6800010a081808000000b0240200641ff01710d002004410474210320022004410c6c6a220441bc016a280200210a200441b8016a2802002104024020064180fe0371418002460d00024020052802002005280208220b470d002005200b410110ab868080002005280208210b0b2005280204200b6a412c3a00002005200b41016a3602080b200220036a210b200120022004200a10ae868080001a0240200528020020052802082202470d0020052002410110ab86808000200528020821020b2007417f6a2107200641ff817c71418004722106200528020420026a413a3a00002005200241016a360208410021032009210420002102200b200110af868080002200450d010c040b0b41f4d7c280004128418cdcc2800010f880808000000b20012002200041086a2802002000410c6a28020010ae868080001a0b410021000b20000bfd0201047f200128020821022001280204210102402000280200220328020020032802082204470d0020032004410110ab86808000200328020821040b200328020420046a41db003a00002003200441016a22043602080240024002400240024002402002450d0020024104742105418002210203402002220441ff01710d02024020044180fe0371418002460d000240200328020020032802082202470d0020032002410110ab86808000200328020821020b200328020420026a412c3a00002003200241016a3602080b2001200010af8680800022020d06200141106a2101200441ff817c71418004722102200541706a22050d000b200441ff01710d02200328020020032802082204470d040c030b20032802002004460d020c030b41f4d7c28000412841acdcc2800010f880808000000b41f4d7c28000412841bcdcc2800010f880808000000b20032004410110ab86808000200328020821040b200328020420046a41dd003a00002003200441016a360208410021020b20020bc70602077f037e2380808080004180016b2202248080808000024002400240024002400240024010fa81808000220341fe014b0d002002200341016a3602284100210441b1e3c080004113200241286a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22053602002001280228210620012d00042107024020050d00200341086a28020022082003410c6a28020022052802001180808080000002402005280204450d00200841002802c0a3c68000118080808000000b200341046a22052005280200417f6a220536020020050d00200341002802c0a3c68000118080808000000b0240200741ff0171450d00410221010c050b200241186a2001412c6a220141186a290000370300200241106a200141106a290000370300200241086a200141086a2900003703002002200129000037030020024200200629030022092009428080e983b1de16541b370320200241286a2002200241206a10a38c808000200241286a41106a290300210a200241286a41086a2903002109024020022802280d002002290320220b2009560d02200b20095a0d0320022009200b7d370328200241286a108d8a8080000c030b200228022c22014108762104200141ff0171410e470d040c030b200041093b0100200128020022012001280200417f6a220036020020000d05200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d05200141002802c0a3c68000118080808000000c050b2002200b20097d370328200241286a108f8a8080000b200241d0006a200241186a290300370300200241c8006a200241106a290300370300200241286a41186a200241086a2903003703002002200229030037033820022002290320370330200241033a0028200241286a108e8a808000410021040b2004410874410e72210141b0a1c6800021030c010b2004410874200141ff017172210141d0a1c6800021030b2003280200118880808000002000200a37020c2000200937020420002001360200200241ff006a10fb818080000b20024180016a2480808080000bdd0502067f027e23808080800041e0006b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36022041b1e3c080004113200241206a410441002802e0a1c680001186808080000041002802e8a1c68000118880808000002001280228210420012d00042105200128020022032003280200417f6a2206360200024020060d00200341086a28020022072003410c6a28020022062802001180808080000002402006280204450d00200741002802c0a3c68000118080808000000b200341046a22062006280200417f6a220636020020060d00200341002802c0a3c68000118080808000000b0240200541ff01714101460d0041002101410221030c020b200241186a200141056a220341186a290000370300200241106a200341106a290000370300200241086a200341086a29000037030020022003290000370300200241206a41186a2001412c6a220141186a290000370300200241206a41106a200141106a290000370300200241206a41086a200141086a2900003703002002200129000037032041002101200241c0006a2002200241206a20042903004100109d8c808000200241c0006a41106a2903002108200241c0006a41086a290300210902402002280240450d00200228024422034108762101200341ff0171410e470d020b2001410874410e72210141b0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b2001410874200341ff017172210141d0a1c6800021030b2003280200118880808000002000200837020c2000200937020420002001360200200241df006a10fb818080000b200241e0006a2480808080000be70501057f23808080800041106b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff01710d010240200541086a2802002203450d002003417f6a41ffffffff01712106200541046a2802002204210102402003410171450d0020042802042004280208200441106a280200200441146a28020041002802e0a1c6800011868080800000200441186a21010b2006450d002004200341186c6a210303402001280204200141086a280200200141106a280200200141146a28020041002802e0a1c68000118680808000002001411c6a280200200141206a280200200141286a2802002001412c6a28020041002802e0a1c6800011868080800000200141306a22012003470d000b0b4100210141002802b0a1c6800011888080800000200041003a001820004200370308200042023703000c020b200041003a001820004200370300200041093b0120200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b41002802d0a1c680001188808080000020004200370300200041003a0018200041086a4200370300410221010b200020013a00202002410f6a10fb818080000b200241106a2480808080000bb30301047f23808080800041106b22022480808080000240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a220436020020012d00042101024020040d00200341086a28020022052003410c6a28020022042802001180808080000002402004280204450d00200541002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b41b0a1c6800041d0a1c68000200141ff017141014622011b280200118880808000002000410e410220011b3a00002002410f6a10fb818080000c010b200041093b0100200128020022012001280200417f6a220036020020000d00200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d00200141002802c0a3c68000118080808000000b200241106a2480808080000bd00602077f027e23808080800041d0026b22022480808080000240024010fa81808000220341fe014b0d002002200341016a36022041b1e3c080004113200241206a410441002802e0a1c680001186808080000041002802e8a1c680001188808080000020012d00102104200128020821052001280204210320012802002106200128020c22012001280200417f6a2207360200024020070d00200141086a28020022082001410c6a28020022072802001180808080000002402007280204450d00200841002802c0a3c68000118080808000000b200141046a22072007280200417f6a220736020020070d00200141002802c0a3c68000118080808000000b4102210102400240200441ff01710d00200241206a2003200510978a808000024020022d00202201410e470d004100210141e7adc4800041052003200541002802e0a1c6800011868080800000200241043a0020200241206a10f588808000200241163a0020200241023a0028200241206a108e8a80800002402006450d00200341002802c0a3c68000118080808000000b200241206a10e189808000200241c0026a290300210920022903b802210a41002802b0a1c68000118880808000002000420137030820004202370300411021030c020b200241176a200241306a280000360000200241106a200241296a290000370300200220022900213703080b02402006450d00200341002802c0a3c68000118080808000000b20002002290308370021200041306a200241176a28000036000041082103200041296a200241086a41086a29030037000041002802d0a1c680001188808080000042002109200042003703004201210a0b200020036a200a370300200020013a002020002009370318200241cf026a10fb818080000c010b200041003a001820004200370300200041093b0120200128020c22002000280200417f6a2203360200024020030d00200041086a28020022042000410c6a28020022032802001180808080000002402003280204450d00200441002802c0a3c68000118080808000000b200041046a22032003280200417f6a220336020020030d00200041002802c0a3c68000118080808000000b2001280200450d00200128020441002802c0a3c68000118080808000000b200241d0026a2480808080000bf10603037f027e027f23808080800041d0006b22022480808080000240024002400240024002400240024010fa81808000220341fe014b0d002002200341016a36022041b1e3c080004113200241206a410441002802e0a1c680001186808080000041002802e8a1c680001188808080000020012d00382104200129033021052001290328210620012d00042103200128020022012001280200417f6a2207360200024020070d00200141086a28020022082001410c6a28020022072802001180808080000002402007280204450d00200841002802c0a3c68000118080808000000b200141046a22072007280200417f6a220736020020070d00200141002802c0a3c68000118080808000000b200341ff01710d0102400240200650450d002005500d05200441ff01710d010c050b2005500d040b200242919fd78da1a1ad84ff003703282002429ceccef7a6c0dedd2037032020024290f1f7a1ea9083df363703382002428b87a199bee8b0f0ac7f37033041002d00fca3c680001a411241002802c8a3c68000118180808000002201450d0220012006370001200141013a000020012005370009200120043a0011200241206a41202001411241002802e0a1c6800011868080800000200141002802c0a3c68000118080808000002002410e3a000c0c040b200041093b0100200128020022012001280200417f6a220036020020000d06200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d06200141002802c0a3c68000118080808000000c060b200241023a000c0c030b4101411210b280808000000b2002410c6a410410d28880800020022d000c410e470d010b200241206a41106a2002410c6a41106a280200360200200241206a41086a2002410c6a41086a2902003703002002200229020c37032041b0a1c6800021010c010b200241206a41106a2002410c6a41106a280200360200200241206a41086a2002410c6a41086a2902003703002002200229020c37032041d0a1c6800021010b20002002290320370200200041106a200241206a41106a280200360200200041086a200241206a41086a290300370200200128020011888080800000200241cf006a10fb818080000b200241d0006a2480808080000bc20503057f047e017f23808080800041a0016b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36022841b1e3c080004113200241286a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff0171450d014102210141d0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b200241086a41186a2201200541186a2900002207370300200241086a41106a2203200541106a2900002208370300200241086a41086a2204200541086a290000220937030020022005290000220a370308200241c1006a22052007370000200241396a22062008370000200241316a220b20093700002002200a370029200241003a0028200241ff006a10ac8a808000200241286a200241ff006a4120109388808000200241c9006a20012903003700002005200329030037000020062004290300370000200b2002290308370000200241d1006a41003a0000200241063a0030200241163a0028200241286a108e8a808000410e210141b0a1c6800021030b200328020011888080800000200020013a00002002419f016a10fb818080000b200241a0016a2480808080000bae0401057f23808080800041306b22022480808080000240024002400240024010fa81808000220341fe014b0d002002200341016a36020c41b1e3c0800041132002410c6a410441002802e0a1c680001186808080000041002802e8a1c68000118880808000002001280228210420012d00042103200128020022012001280200417f6a2205360200024020050d00200141086a28020022062001410c6a28020022052802001180808080000002402005280204450d00200641002802c0a3c68000118080808000000b200141046a22052005280200417f6a220536020020050d00200141002802c0a3c68000118080808000000b200341ff01714101470d022002410c6a2004280204200428020822014100280298a3c680001185808080000020024184dec28000411010d08880800020022802000d014188dfc2800010a081808000000b200041093b0100200128020022012001280200417f6a220036020020000d03200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d03200141002802c0a3c68000118080808000000c030b200228020420012002410c6a41002802d0a3c6800011858080800000410e210141b0a1c6800021030c010b4102210141d0a1c6800021030b200328020011888080800000200020013a00002002412f6a10fb818080000b200241306a2480808080000b8e0601057f23808080800041106b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff01710d010240200541086a2802002204450d002004417f6a41ffffffff03712106200541046a28020022052101024020044103712203450d002005210103402001280204200141086a28020041002802a0a1c68000118480808000002001410c6a21012003417f6a22030d000b0b20064103490d0020052004410c6c6a210303402001280204200141086a28020041002802a0a1c6800011848080800000200141106a280200200141146a28020041002802a0a1c68000118480808000002001411c6a280200200141206a28020041002802a0a1c6800011848080800000200141286a2802002001412c6a28020041002802a0a1c6800011848080800000200141306a22012003470d000b0b4100210141002802b0a1c6800011888080800000200041003a001820004200370308200042023703000c020b200041003a001820004200370300200041093b0120200128020022012001280200417f6a220336020020030d02200141086a28020022002001410c6a28020022032802001180808080000002402003280204450d00200041002802c0a3c68000118080808000000b200141046a22032003280200417f6a220336020020030d02200141002802c0a3c68000118080808000000c020b41002802d0a1c680001188808080000020004200370300200041003a0018200041086a4200370300410221010b200020013a00202002410f6a10fb818080000b200241106a2480808080000b9c0502057f017e23808080800041106b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff01714101470d01410021010240200541086a2802002206450d0020064105742104200541046a2802002103410021010340200141016a2205417f20051b2001200310a08c8080001b2101200341206a2103200441606a22040d000b02400240200120064b0d002001ad428094ebdc037e2006ad8022074280808080105a0d002007a721010c010b418094ebdc0321010b200141ffd193ad034b21010b41002802b0a1c6800011888080800000200041003a001820004200370308200042023703000c020b200041003a001820004200370300200041093b0120200128020022012001280200417f6a220336020020030d02200141086a28020022042001410c6a28020022032802001180808080000002402003280204450d00200441002802c0a3c68000118080808000000b200141046a22032003280200417f6a220336020020030d02200141002802c0a3c68000118080808000000c020b41002802d0a1c680001188808080000020004200370300200041003a0018200041086a4200370300410221010b200020013a00202002410f6a10fb818080000b200241106a2480808080000bf30402057f017e23808080800041206b22022480808080000240024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff0171450d0141002802d0a1c680001188808080000041022101420021070c020b200041003a001820004200370300200041093b0120200128020022002000280200417f6a220136020020010d02200041086a28020022032000410c6a28020022012802001180808080000002402001280204450d00200341002802c0a3c68000118080808000000b200041046a22012001280200417f6a220136020020010d02200041002802c0a3c68000118080808000000c020b4100210141002d00fca3c680001a410841002802c8a3c68000118180808000002203450d022003200529030037000041bfd9c28000410a2003410841002802e0a1c6800011868080800000200341002802c0a3c6800011808080800000200241043a0008200241086a10f58880800041002802b0a1c6800011888080800000420221070b200020013a0020200041003a001820004200370308200020073703002002411f6a10fb818080000b200241206a2480808080000f0b4101410810b280808000000bfc0304027f027e017f017e23808080800041c0006b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36021041b1e3c080004113200241106a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200241306a41086a200141086a28020036020020022001290200370330200241106a200241306a109c8a80800042022104200229031022054202510d01200220022800213602082002200241246a28000036000b20022d0020210120022802282103200228022c2106200229031821072000411c6a200228000b3600002000200228020836001941002802b0a1c6800011888080800000200020073703100c020b200041003a001820004200370300200041093b01202001280200450d02200128020441002802c0a3c68000118080808000000c020b2002200241216a2800003602082002200241246a28000036000b200241206a2d00002101200241286a2802002103200229031821042000412c6a200228000b3600002000200228020836002941002802d0a1c680001188808080000020002003360230200020013a00282004422088a721062004a7210342002104410021010b2000200636022420002003360220200020013a001820002005370308200020043703002002413f6a10fb818080000b200241c0006a2480808080000be10502067f027e23808080800041e0006b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36022041b1e3c080004113200241206a410441002802e0a1c680001186808080000041002802e8a1c68000118880808000002001280228210420012d00042105200128020022032003280200417f6a2206360200024020060d00200341086a28020022072003410c6a28020022062802001180808080000002402006280204450d00200741002802c0a3c68000118080808000000b200341046a22062006280200417f6a220636020020060d00200341002802c0a3c68000118080808000000b0240200541ff01714101460d0041002101410221030c020b200241186a200141056a220341186a290000370300200241106a200341106a290000370300200241086a200341086a29000037030020022003290000370300200241206a41186a2001412c6a220141186a290000370300200241206a41106a200141106a290000370300200241206a41086a200141086a29000037030020022001290000370320200241c0006a2002200241206a20042903004102109d8c808000200241c0006a41106a2903002108200241c0006a41086a29030021090240024020022802400d00410021010c010b200228024422034108762101200341ff0171410e470d020b2001410874410e72210141b0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b2001410874200341ff017172210141d0a1c6800021030b2003280200118880808000002000200837020c2000200937020420002001360200200241df006a10fb818080000b200241e0006a2480808080000b880502067f017e23808080800041a0016b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36024841b1e3c080004113200241c8006a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042106024020040d00200341086a28020022072003410c6a28020022042802001180808080000002402004280204450d00200741002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200641ff01714101460d014102210141d0a1c680002103420021080c020b200041003a001820004200370300200041093b0120200128020022002000280200417f6a220136020020010d02200041086a28020022032000410c6a28020022012802001180808080000002402001280204450d00200341002802c0a3c68000118080808000000b200041046a22012001280200417f6a220136020020010d02200041002802c0a3c68000118080808000000c020b200241196a200141056a220141186a290000370000200241116a200141106a290000370000200241096a200141086a2900003700002002200129000037000141002101200241216a200528020420052802084100280298a3c6800011858080800000200241053a0000200241c8006a41086a200241c80010848e8080001a200241163a0048200241c8006a108e8a8080004202210841b0a1c6800021030b200328020011888080800000200020013a0020200041003a001820004200370308200020083703002002419f016a10fb818080000b200241a0016a2480808080000bf50301067f23808080800041106b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a2204360200200128022c21052001280228210620012d00042101024020040d00200341086a28020022072003410c6a28020022042802001180808080000002402004280204450d00200741002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff01714101460d014102210141d0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b200628020420062802082005280204200528020841002802b8a3c6800011868080800000410e210141b0a1c6800021030b200328020011888080800000200020013a00002002410f6a10fb818080000b200241106a2480808080000be40301057f23808080800041106b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff01714101460d014102210141d0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b2005280204200528020841002802b0a3c6800011848080800000410e210141b0a1c6800021030b200328020011888080800000200020013a00002002410f6a10fb818080000b200241106a2480808080000b980502077f027e23808080800041c0006b22022480808080000240024002400240024010fa81808000220341fe014b0d002002200341016a36020041b1e3c0800041132002410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128022c21042001280228210520012d00042106200128020022032003280200417f6a2207360200024020070d00200341086a28020022082003410c6a28020022072802001180808080000002402007280204450d00200841002802c0a3c68000118080808000000b200341046a22072007280200417f6a220736020020070d00200341002802c0a3c68000118080808000000b200641ff01714101470d01200241186a200141056a220141186a290000370300200241106a200141106a290000370300200241086a200141086a2900003703002002200129000037030041002101200241206a2002200429030020052d000041017441004100109e8c808000200241206a41106a2903002109200241206a41086a290300210a02402002280220450d00200228022422034108762101200341ff0171410e470d030b2001410874410e72210141b0a1c6800021030c030b200041093b0100200128020022012001280200417f6a220036020020000d03200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d03200141002802c0a3c68000118080808000000c030b41002101410221030b2001410874200341ff017172210141d0a1c6800021030b2003280200118880808000002000200937020c2000200a370204200020013602002002413f6a10fb818080000b200241c0006a2480808080000ba40401067f23808080800041306b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042106024020040d00200341086a28020022072003410c6a28020022042802001180808080000002402004280204450d00200741002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200641ff01710d01200241086a41186a2001412c6a220141186a290000370300200241086a41106a200141106a290000370300200241086a41086a200141086a29000037030020022001290000370308200241086a2005290300109f8c8080001a41002802b0a1c6800011888080800000410e21010c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b41002802d0a1c6800011888080800000410221010b200020013a00002002412f6a10fb818080000b200241306a2480808080000ba10501067f23808080800041106b220224808080800002400240024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a2204360200200128022c21052001280228210620012d00042101024020040d00200341086a28020022072003410c6a28020022042802001180808080000002402004280204450d00200741002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff01710d0141002101200220062802042204200628020822034101200528020041002802a8a1c680001187808080000002402002280200450d002003450d002003417f4c0d0541002d00fca3c680001a200341002802c8a3c68000118180808000002206450d0620062004200310848e80800041002802c0a3c68000118080808000000b41002802b0a1c6800011888080800000200041003a001820004200370308200042023703000c020b200041003a001820004200370300200041093b0120200128020022002000280200417f6a220136020020010d02200041086a28020022032000410c6a28020022012802001180808080000002402001280204450d00200341002802c0a3c68000118080808000000b200041046a22012001280200417f6a220136020020010d02200041002802c0a3c68000118080808000000c020b41002802d0a1c680001188808080000020004200370300200041003a0018200041086a4200370300410221010b200020013a00202002410f6a10fb818080000b200241106a2480808080000f0b10ae80808000000b4101200310b280808000000bcf0502077f027e23808080800041b0026b22022480808080000240024010fa81808000220341fe014b0d002002200341016a36020041b1e3c0800041132002410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020c22032003280200417f6a220436020020012d00102105200128020821062001280204210720012802002108024020040d00200341086a28020022042003410c6a28020022012802001180808080000002402001280204450d00200441002802c0a3c68000118080808000000b200341046a22012001280200417f6a220136020020010d00200341002802c0a3c68000118080808000000b02400240200541ff01710d004100210141e7adc4800041052007200641002802e0a1c6800011868080800000200241043a0000200210f588808000200241163a0000200241023a00082002108e8a80800002402008450d00200741002802c0a3c68000118080808000000b200210e189808000200241a0026a2903002109200229039802210a41002802b0a1c68000118880808000002000420137030820004202370300411021030c010b02402008450d00200741002802c0a3c68000118080808000000b41002802d0a1c68000118880808000004200210920004200370300410221014201210a410821030b200020036a200a370300200020013a002020002009370318200241af026a10fb818080000c010b200041003a001820004200370300200041093b0120200128020c22002000280200417f6a2203360200024020030d00200041086a28020022042000410c6a28020022032802001180808080000002402003280204450d00200441002802c0a3c68000118080808000000b200041046a22032003280200417f6a220336020020030d00200041002802c0a3c68000118080808000000b2001280200450d00200128020441002802c0a3c68000118080808000000b200241b0026a2480808080000bc20503057f047e017f23808080800041a0016b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36022841b1e3c080004113200241286a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff0171450d014102210141d0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b200241086a41186a2201200541186a2900002207370300200241086a41106a2203200541106a2900002208370300200241086a41086a2204200541086a290000220937030020022005290000220a370308200241c1006a22052007370000200241396a22062008370000200241316a220b20093700002002200a370029200241013a0028200241ff006a10ac8a808000200241286a200241ff006a4120109388808000200241c9006a20012903003700002005200329030037000020062004290300370000200b2002290308370000200241d1006a41013a0000200241063a0030200241163a0028200241286a108e8a808000410e210141b0a1c6800021030b200328020011888080800000200020013a00002002419f016a10fb818080000b200241a0016a2480808080000bfb0602067f027e2380808080004180016b220224808080800002400240024002400240024010fa81808000220341fe014b0d002002200341016a36023841b1e3c080004113200241386a410441002802e0a1c680001186808080000041002802e8a1c68000118880808000002001280228210420012d00042105200128020022032003280200417f6a2206360200024020060d00200341086a28020022072003410c6a28020022062802001180808080000002402006280204450d00200741002802c0a3c68000118080808000000b200341046a22062006280200417f6a220636020020060d00200341002802c0a3c68000118080808000000b0240200541ff01714101460d0041002101410221030c040b2001412c6a2103200241186a200141056a220141186a290000370300200241106a200141106a290000370300200241086a200141086a2900003703002002200129000037030020042d00002101200241386a200210fa878080004200200241e0006a2903002208200241d8006a2903007d220920092008561b210820014101742105200241386a41186a290300210920010d012009500d02200241386a200210fa878080002002280268450d02200228026c4102490d010c020b200041093b0100200128020022012001280200417f6a220036020020000d04200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d04200141002802c0a3c68000118080808000000c040b2008428080e983b1de162008428080e983b1de16561b21080b200241386a41186a200341186a290000370300200241386a41106a200341106a290000370300200241386a41086a200341086a29000037030020022003290000370338200241206a2002200241386a4200200920087d220820082009561b2005109d8c808000200241206a41106a2903002109200241206a41086a29030021080240024020022802200d00410021010c010b200228022422034108762101200341ff0171410e470d010b2001410874410e72210141b0a1c6800021030c010b2001410874200341ff017172210141d0a1c6800021030b2003280200118880808000002000200937020c2000200837020420002001360200200241ff006a10fb818080000b20024180016a2480808080000bb20802067f027e23808080800041a0016b2202248080808000024002400240024002400240024010fa81808000220341fe014b0d002002200341016a36024041b1e3c080004113200241c0006a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128022c21042001280228210520012d00042103200128020022012001280200417f6a2206360200024020060d00200141086a28020022072001410c6a28020022062802001180808080000002402006280204450d00200741002802c0a3c68000118080808000000b200141046a22062006280200417f6a220636020020060d00200141002802c0a3c68000118080808000000b200341ff01710d010240200529030050450d00410c21010c030b20024298d5afd2c6aeacae2f370348200242c2cdc8b0c7b9e78f857f370340200242e4c5bdb4b2e9a5a6807f370358200242d790d7a3fef9fda0c800370350200241186a200241c0006a10e6888080002002290320420020022802181b2108200529030021090240024020042d00000d00427f200820097c220920092008541b21090c010b4200200820097d220920092008561b21090b20024298d5afd2c6aeacae2f370348200242c2cdc8b0c7b9e78f857f370340200242a2bba4efe3ffa586553703582002429c9a9bbf88a5a0fc937f370350200241086a200241c0006a10e68880800002402002280208450d00410b210120022903102009560d030b20024298d5afd2c6aeacae2f370348200242c2cdc8b0c7b9e78f857f370340200242e4c5bdb4b2e9a5a6807f370358200242d790d7a3fef9fda0c8003703502002200937039001200241c0006a412020024190016a410841002802e0a1c68000118680808000002002200937035020022008370348200241153a0040200241c0006a108e8a8080002002410e3a002c0c030b200041093b0100200128020022012001280200417f6a220036020020000d05200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d05200141002802c0a3c68000118080808000000c050b200241023a002c0c020b2002412c6a200110f88880800020022d002c410e470d010b200241c0006a41106a2002412c6a41106a280200360200200241c0006a41086a2002412c6a41086a2902003703002002200229022c37034041b0a1c6800021010c010b200241c0006a41106a2002412c6a41106a280200360200200241c0006a41086a2002412c6a41086a2902003703002002200229022c37034041d0a1c6800021010b20002002290340370200200041106a200241c0006a41106a280200360200200041086a200241c0006a41086a2903003702002001280200118880808000002002419f016a10fb818080000b200241a0016a2480808080000bdc0502077f027e23808080800041e0006b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a3602204100210441b1e3c080004113200241206a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22053602002001280228210620012d00042107024020050d00200341086a28020022082003410c6a28020022052802001180808080000002402005280204450d00200841002802c0a3c68000118080808000000b200341046a22052005280200417f6a220536020020050d00200341002802c0a3c68000118080808000000b0240200741ff0171450d00410221010c020b200241186a2001412c6a220341186a290000370300200241106a200341106a290000370300200241086a200341086a29000037030020022003290000370300200241206a41186a200141cc006a220141186a290000370300200241206a41106a200141106a290000370300200241206a41086a200141086a2900003703002002200129000037032041002104200241c0006a2002200241206a20062903004100109d8c808000200241c0006a41106a2903002109200241c0006a41086a290300210a02402002280240450d00200228024422014108762104200141ff0171410e470d020b2004410874410e72210141b0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b2004410874200141ff017172210141d0a1c6800021030b2003280200118880808000002000200937020c2000200a37020420002001360200200241df006a10fb818080000b200241e0006a2480808080000bc30202047f017e23808080800041106b22022480808080000240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020c21030240024020012802002204418080808078470d002003280204200328020841002802a0a1c68000118480808000000c010b200128020421052003280204200328020820012902042206a72006422088a741002802e0a1c68000118680808000002004450d00200541002802c0a3c68000118080808000000b41002802b0a1c68000118880808000002000410e3a00002002410f6a10fb818080000c010b200041093b010020012802002200418080808078460d002000450d00200128020441002802c0a3c68000118080808000000b200241106a2480808080000bac0100024002400240024002400240200241746a0e12010404040404040404040404040404020400040b200141d5d9c28000411d10888e8080000d03200041013a00010c020b200141c9d9c28000410c10888e808000450d030c020b200141f2d9c28000411b10888e8080000d01200041023a00010b200041003a00000f0b2000200120024190dac28000410310e88a808000360204200041013a00000f0b200041003a0001200041003a00000bc80501087f23808080800041106b220224808080800002400240024002400240024002400240200141146a2802002203200141106a28020022044f0d00200128020c21050340200520036a2d0000220641776a220741194b0d06024041012007744193808004710d0020074119470d07200241046a200110ee8a80800020022d00040d0320022d00050e03040506040b2001200341016a220336021420042003470d000b0b2002410536020420002001200241046a10c78a8080003602040c050b200020022802083602040c040b41002105200041003a00010c040b200041013a0001410021050c030b200041023a0001410021050c020b0240200641fb00460d002002410a36020420002001200241046a10c78a8080003602040c010b200120012d0018417f6a22073a00180240200741ff0171450d002001200341016a360214200241046a200110ee8a80800002400240024020022d00040d0020022d00052107200110d78a80800022030d01024002400240024020070e03000102000b200110f08a80800022030d04410021080c020b200110f08a80800022030d03410121080c010b200110f08a80800022030d02410221080b41012105200120012d001841016a3a0018024020012802142203200128021022094f0d00200128020c21060340200620036a2d0000220441776a220741174b0d044101200774419380800471450d042001200341016a220336021420092003470d000b0b2002410336020420002001200241046a10d88a8080003602040c050b200228020821030b2000200336020441012105200120012d001841016a3a00180c030b0240200441fd00470d00200020083a00012001200341016a360214410021050c030b2002410a36020420002001200241046a10d88a8080003602040c010b2002411836020420002001200241046a10c78a8080003602040b410121050b200020053a0000200241106a2480808080000bb70301047f23808080800041106b220224808080800041052103024002400240024002400240024002400240200128020022040e03000201000b417f2001410c6a28020041286c220341246a220520052003410472491b41016a2203450d022003417f4a0d0110ae80808000000b411321030b41002d00fca3c680001a200341002802c8a3c68000118180808000002205450d052002200536020820022003360204024020040e03000304000b410021030c010b2002410036020c2002428080808010370204200241046a4100410110b18280800020022802082105200228020c21030b200520036a41013a00002002200341016a36020c200141046a200241046a10af8c8080000c020b200541023a0000410121032002410136020c2001280204210102402002280204417f6a41034b0d00200241046a4101410410b182808000200228020c21030b200228020820036a20013600002002200341046a36020c0c010b200541033a00002002410136020c200141086a200241046a10b08c8080000b20002002290204370200200041086a200241046a41086a280200360200200241106a2480808080000f0b4101200310b280808000000bb90703047f017e037f23808080800041206b220224808080800002400240417f200141d8006a280200220341286c220441cd006a220520052004410472491b2204417f4c0d0041002d00fca3c680001a200441002802c8a3c68000118180808000002205450d01200220053602102002200436020c200520012903183700002002410836021420012903202106410821040240200228020c4178714108470d002002410c6a4108410810b182808000200228021421040b200228021020046a20063700002002200441086a2204360214200129032821060240200228020c20046b41074b0d002002410c6a2004410810b182808000200228021421040b200228021020046a20063700002002200441086a360214200141d4006a2802002104200220033602182002200241186a36021c2002411c6a2002410c6a10c08a8080002002280214210502402003450d002004200341286c6a210703400240200228020c220820056b411f4b0d002002410c6a2005412010b182808000200228020c2108200228021421050b2002280210220920056a22032004290000370000200341186a200441186a290000370000200341106a200441106a290000370000200341086a200441086a2900003700002002200541206a2205360214200441206a29030021060240200820056b41074b0d002002410c6a2005410810b18280800020022802102109200228021421050b200920056a20063700002002200541086a2205360214200441286a22042007470d000b0b200141306a21040240200228020c220820056b411f4b0d002002410c6a2005412010b182808000200228020c2108200228021421050b2002280210220920056a22032004290000370000200341086a200441086a290000370000200341106a200441106a290000370000200341186a200441186a2900003700002002200541206a2204360214200129030021060240200820046b41074b0d002002410c6a2004410810b18280800020022802102109200228020c2108200228021421040b200920046a20063700002002200441086a2204360214200129030821060240200820046b41074b0d002002410c6a2004410810b18280800020022802102109200228021421040b200920046a20063700002002200441086a220436021420012d001021050240200228020c2004470d002002410c6a2004410110b182808000200228021421040b200228021020046a20053a0000200041086a200441016a3602002000200229020c370200200241206a2480808080000f0b10ae80808000000b4101200410b280808000000bf40603047f017e037f23808080800041206b220224808080800002400240417f200141c8006a280200220341286c220441c5006a220520052004410472491b2204417f4c0d0041002d00fca3c680001a200441002802c8a3c68000118180808000002205450d01200220053602102002200436020c200520012903003700002002410836021420012903082106410821040240200228020c4178714108470d002002410c6a4108410810b182808000200228021421040b200228021020046a20063700002002200441086a2204360214200129031021060240200228020c220520046b41074b0d002002410c6a2004410810b182808000200228020c2105200228021421040b2002280210220720046a20063700002002200441086a2204360214200141186a29030021060240200520046b41074b0d002002410c6a2004410810b18280800020022802102107200228021421040b200720046a20063700002002200441086a360214200141c4006a2802002104200220033602182002200241186a36021c2002411c6a2002410c6a10c08a8080002002280214210502402003450d002004200341286c6a210803400240200228020c220720056b411f4b0d002002410c6a2005412010b182808000200228020c2107200228021421050b2002280210220920056a22032004290000370000200341186a200441186a290000370000200341106a200441106a290000370000200341086a200441086a2900003700002002200541206a2205360214200441206a29030021060240200720056b41074b0d002002410c6a2005410810b18280800020022802102109200228021421050b200920056a20063700002002200541086a2205360214200441286a22042008470d000b0b200141206a21040240200228020c20056b411f4b0d002002410c6a2005412010b182808000200228021421050b2002280210220720056a22032004290000370000200341086a200441086a290000370000200341106a200441106a290000370000200341186a200441186a2900003700002002200541206a220436021420012d004c21050240200228020c2004470d002002410c6a2004410110b18280800020022802102107200228021421040b200720046a20053a0000200041086a200441016a3602002000200229020c370200200241206a2480808080000f0b10ae80808000000b4101200410b280808000000b2100200128021441e691c28000411d200141186a28020028020c118280808000000b2100200128021441be91c280004112200141186a28020028020c118280808000000b02000be80802077f027e23808080800041d0006b220224808080800002400240024002400240200128020822030d002002420437020c200220033602080c010b2001280204210441002d00fca3c680001a2003410474220541002802c8a3c68000118180808000002201450d032004200341286c6a2106200241003602102002200136020c20022003360208034041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002203450d002002200336021c2002410236021820024100360220200241286a200441002f0194a1c6800010c887808000200228022c210502400240200228023022010d00410121070c010b2001417f4c0d0641002d00fca3c680001a200141002802c8a3c68000118180808000002207450d020b20072005200110848e808000210702402002280228450d00200541002802c0a3c68000118080808000000b200320022f00283b0001200341033a00002003200736020820032001360204200341036a200241286a41026a22082d00003a00002003200136020c20024101360220411421030240200429032022094290ce005a0d002009210a0c030b411421030340200241286a20036a2201417c6a200920094290ce0080220a4290ce007e7da7220541ffff037141e4006e220741017441b6dac280006a2f00003b00002001417e6a2005200741e4006c6b41ffff037141017441b6dac280006a2f00003b00002003417c6a2103200942ffc1d72f562101200a210920010d000c030b0b4104412010b280808000000b4101200110b280808000000b0240200aa7220541e3004d0d00200241286a2003417e6a22036a200aa72201200141ffff037141e4006e220541e4006c6b41ffff037141017441b6dac280006a2f00003b00000b024002402005410a490d00200241286a2003417e6a22016a200541017441b6dac280006a2f00003b00000c010b200241286a2003417f6a22016a200541306a3a00000b411420016b210341012105024020014114460d002003417f4c0d0341002d00fca3c680001a200341002802c8a3c68000118180808000002205450d040b2005200241286a20016a200310848e80800021050240200228022022012002280218470d00200241186a200110a386808000200228022021010b200228021c20014104746a2201200336020c2001200536020820012003360204200141023a00002002200228022041016a36022020022802182101200229021c21090240200228021022032002280208470d00200241086a200310a386808000200228021021030b200228020c20034104746a220320022f00283b0001200341043a00002003200937020820032001360204200341036a20082d00003a00002002200228021041016a360210200441286a22042006470d000b0b200241336a200241086a41086a280200360000200041043a00002002200229030837002b20002002290028370001200041086a2002412f6a290000370000200241d0006a2480808080000f0b10ae80808000000b4101200310b280808000000b4104200510b280808000000ba50702047f027e23808080800041106b22042480808080000240024020002d00000d0020002802042105024020002d00014101460d0002402005280200220628020020062802082207470d0020062007410110ab86808000200628020821070b200628020420076a412c3a00002006200741016a3602080b200041023a0001200520052001200210ae868080001a02402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a413a3a00002000200641016a36020802402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41fb003a00002000200641016a36020820032903082108200329030021092005200541a8dac28000410110ae868080001a02402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a413a3a00002000200641016a36020802402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41db003a00002000200641016a3602082004200536020c2004418002360208200441086a200910d486808000200441086a200810d4868080002004280208220041ff01710d01024020004180fe0371450d000240200428020c280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41dd003a00002000200641016a3602080b20032d0010210602402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a412c3a00002000200341016a3602082005200541a9dac28000410d10ae868080001a02402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a413a3a00002000200341016a360208024002400240024020060e03000102000b2005200541c9d9c28000410c10ae868080001a0c020b2005200541d5d9c28000411d10ae868080001a0c010b2005200541f2d9c28000411b10ae868080001a0b02402005280200220528020020052802082200470d0020052000410110ab86808000200528020821000b200528020420006a41fd003a00002005200041016a360208200441106a24808080800041000f0b41f4d7c280004128418cdcc2800010f880808000000b41f4d7c28000412841bcdcc2800010f880808000000b980402057f017e23808080800041306b22022480808080000240024020002d00000d0020002802042103024020002d00014101460d0002402003280200220428020020042802082205470d0020042005410110ab86808000200428020821050b200428020420056a412c3a00002004200541016a3602080b200041023a00012003280200210641142100024020014290ce005a0d00200121070c020b411421000340200241086a20006a2203417c6a200120014290ce008022074290ce007e7da7220441ffff037141e4006e220541017441b6dac280006a2f00003b00002003417e6a2004200541e4006c6b41ffff037141017441b6dac280006a2f00003b00002000417c6a2100200142ffc1d72f5621032007210120030d000c020b0b41f4d7c28000412841acdcc2800010f880808000000b02402007a7220341e3004d0d00200241086a2000417e6a22006a2007a72203200341ffff037141e4006e220341e4006c6b41ffff037141017441b6dac280006a2f00003b00000b024002402003410a490d00200241086a2000417e6a22046a200341017441b6dac280006a2f00003b00000c010b200241086a2000417f6a22046a200341306a3a00000b02402006280200200628020822006b411420046b22034f0d0020062000200310ab86808000200628020821000b200628020420006a200241086a20046a200310848e8080001a2006200020036a360208200241306a2480808080000bda0101037f024020002d00000d0020002802042104024020002d00014101460d0002402004280200220528020020052802082206470d0020052006410110ab86808000200528020821060b200528020420066a412c3a00002005200641016a3602080b200041023a0001200420002001200210ae868080001a02402004280200220028020020002802082205470d0020002005410110ab86808000200028020821050b200028020420056a413a3a00002000200541016a3602082003200410a48c8080000f0b41f4d7c280004128418cdcc2800010f880808000000be50701047f23808080800041106b22042480808080000240024020002d00000d0020002802042105024020002d00014101460d0002402005280200220628020020062802082207470d0020062007410110ab86808000200628020821070b200628020420076a412c3a00002006200741016a3602080b200041023a0001200520052001200210ae868080001a200328020821062003280204210202402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a413a3a00002000200341016a36020802402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a41db003a00002000200341016a220336020802400240024020060d00024020002802002003470d0020002003410110ab86808000200028020821030b200028020420036a41dd003a00002000200341016a3602080c010b024020002802002003470d0020002003410110ab86808000200028020821030b200028020420036a41db003a00002000200341016a3602082004200536020c20044180043602082002200510ae8980800022000d01200441086a200229032010d4868080002004280208220041ff01710d03024020004180fe0371450d000240200428020c280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a41dd003a00002000200341016a3602080b024020064101460d002002200641286c6a2101200241286a2103034002402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a412c3a00002000200641016a36020802402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41db003a00002000200641016a3602082004200536020c20044180043602082003200510ae8980800022000d03200441086a200341206a29030010d4868080002004280208220041ff01710d05024020004180fe0371450d000240200428020c280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41dd003a00002000200641016a3602080b200341286a22032001470d000b0b02402005280200220528020020052802082200470d0020052000410110ab86808000200528020821000b200528020420006a41dd003a00002005200041016a3602080b410021000b200441106a24808080800020000f0b41f4d7c280004128418cdcc2800010f880808000000b41f4d7c28000412841bcdcc2800010f880808000000be80101047f024020002d00000d00200128020821032001280204210420002802042101024020002d00014101460d0002402001280200220528020020052802082206470d0020052006410110ab86808000200528020821060b200528020420066a412c3a00002005200641016a3602080b200041023a0001200120002004200310ae868080001a02402001280200220028020020002802082205470d0020002005410110ab86808000200028020821050b200028020420056a413a3a00002000200541016a3602082002200110af868080000f0b41f4d7c280004128418cdcc2800010f880808000000bca0501047f23808080800041106b2204248080808000024020002d00000d0020002802042105024020002d00014101460d0002402005280200220628020020062802082207470d0020062007410110ab86808000200628020821070b200628020420076a412c3a00002006200741016a3602080b200041023a0001200520042001200210ae868080001a200328020821022003280204210602402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a413a3a00002000200341016a36020802402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a41db003a00002000200341016a22033602080240024020020d00200041086a2106200041046a210520002802002003470d0120002003410110ab86808000200028020821030c010b200441046a200641002f0194a1c6800010c8878080002005200420042802082200200428020c10ae868080001a02402004280204450d00200041002802c0a3c68000118080808000000b024020024101460d00200641206a2106200241057441606a2102034002402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a412c3a00002000200341016a360208200441046a200641002f0194a1c6800010c8878080002005200420042802082200200428020c10ae868080001a02402004280204450d00200041002802c0a3c68000118080808000000b200641206a2106200241606a22020d000b0b02402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200041086a2106200041046a21050b200528020020036a41dd003a00002006200341016a360200200441106a24808080800041000f0b41f4d7c280004128418cdcc2800010f880808000000bd70801047f23808080800041206b22042480808080000240024020002d00000d0020002802042105024020002d00014101460d0002402005280200220628020020062802082207470d0020062007410110ab86808000200628020821070b200628020420076a412c3a00002006200741016a3602080b200041023a0001200520042001200210ae868080001a200328020821012003280204210702402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a413a3a00002000200341016a36020802402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200041086a2106200028020420036a41db003a00002000200341016a2203360208200041046a21020240024020010d0020002802002003470d0120002003410110ab86808000200028020821030c010b024020002802002003470d0020002003410110ab86808000200028020821030b200228020020036a41db003a00002006200341016a3602002004200536020c2004418004360208200441146a200741002f0194a1c6800010c8878080002005200420042802182200200428021c10ae868080001a02402004280214450d00200041002802c0a3c68000118080808000000b200441086a200729032010d4868080002004280208220041ff01710d02024020004180fe0371450d000240200428020c280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a41dd003a00002000200341016a3602080b024020014101460d002007200141286c6a2102200741286a2103034002402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a412c3a00002000200641016a36020802402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41db003a00002000200641016a3602082004200536020c2004418004360208200441146a200341002f0194a1c6800010c8878080002005200420042802182200200428021c10ae868080001a02402004280214450d00200041002802c0a3c68000118080808000000b200441086a200341206a29030010d4868080002004280208220041ff01710d04024020004180fe0371450d000240200428020c280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41dd003a00002000200641016a3602080b200341286a22032002470d000b0b02402005280200220528020020052802082203470d0020052003410110ab86808000200528020821030b200541086a2106200541046a21020b200228020020036a41dd003a00002006200341016a360200200441206a24808080800041000f0b41f4d7c280004128418cdcc2800010f880808000000b41f4d7c28000412841bcdcc2800010f880808000000bce0201037f024020002d00000d0020002802042104024020002d00014101460d0002402004280200220528020020052802082206470d0020052006410110ab86808000200528020821060b200528020420066a412c3a00002005200641016a3602080b200041023a0001200420002001200210ae868080001a02402004280200220028020020002802082205470d0020002005410110ab86808000200028020821050b200028020420056a413a3a00002000200541016a36020802402004280200220028020020002802082204470d0020002004410110ab86808000200028020821040b200028020420046a41fb003a00002000200441016a2204360208024020002802002004470d0020002004410110ab86808000200028020821040b200028020420046a41fd003a00002000200441016a36020841000f0b41f4d7c280004128418cdcc2800010f880808000000bda0101037f024020002d00000d0020002802042104024020002d00014101460d0002402004280200220528020020052802082206470d0020052006410110ab86808000200528020821060b200528020420066a412c3a00002005200641016a3602080b200041023a0001200420002001200210ae868080001a02402004280200220028020020002802082205470d0020002005410110ab86808000200028020821050b200028020420056a413a3a00002000200541016a3602082003200410b9878080000f0b41f4d7c280004128418cdcc2800010f880808000000bda0101037f024020002d00000d0020002802042104024020002d00014101460d0002402004280200220528020020052802082206470d0020052006410110ab86808000200528020821060b200528020420066a412c3a00002005200641016a3602080b200041023a0001200420002001200210ae868080001a02402004280200220028020020002802082205470d0020002005410110ab86808000200028020821050b200028020420056a413a3a00002000200541016a3602082003200410be888080000f0b41f4d7c280004128418cdcc2800010f880808000000b2100200128021441fedbc28000410c200141186a28020028020c118280808000000ba40a01097f200028020c2201200028020422026b41e8016e2103024020012002460d004100210403400240024002400240024002402002200441e8016c6a2201280200220541736a2206410220064104491b0e03010203000b20012d00084106470d042001410c6a280200450d04200141106a28020021070c030b0240024002400240024002400240024020012d0008417f6a0e0a010b0203040506070b0b000b2001410c6a280200450d0a200141106a28020021070c090b2001410c6a280200450d09200141106a28020021070c080b2001410c6a280200450d08200141106a28020021070c070b2001410c6a280200450d07200141106a28020021070c060b0240200141146a2802002205450d00200141106a2802002106034002402006280200450d00200641046a28020041002802c0a3c68000118080808000000b02402006410c6a280200450d00200641106a28020041002802c0a3c68000118080808000000b200641186a21062005417f6a22050d000b0b200128020c450d06200128021021070c050b0240200141146a2802002206450d00200141106a28020021072006410171210841002105024020064101460d002006417e7121094100210520072106034002402006280200450d00200641046a28020041002802c0a3c68000118080808000000b02402006410c6a280200450d00200641106a28020041002802c0a3c68000118080808000000b200641186a21062009200541026a2205470d000b0b2008450d0020072005410c6c6a2206280200450d00200628020441002802c0a3c68000118080808000000b200128020c450d05200128021021070c040b200141106a280200450d04200141146a28020021070c030b2001410c6a280200450d03200141106a28020021070c020b200141186a2d0000417d6a41ff017141014b0d020240200128020822074198016a2802002209450d004100210620074194016a2802002208210103402008200641146c6a21050240024002400240024020012d00000e0400010102040b200141086a21050c020b200541086a21050c010b200541046a21050b2005280200450d00200528020441002802c0a3c68000118080808000000b200641016a2106200141146a21012009417f6a22090d000b0b0240200728029001450d0020072802940141002802c0a3c68000118080808000000b024020074190026a2802002209450d00410021062007418c026a2802002208210103402008200641146c6a21050240024002400240024020012d00000e0400010102040b200141086a21050c020b200541086a21050c010b200541046a21050b2005280200450d00200528020441002802c0a3c68000118080808000000b200641016a2106200141146a21012009417f6a22090d000b0b200728028802450d01200728028c0241002802c0a3c68000118080808000000c010b0240024002400240024002402005417e6a0e06000102000003070b200141046a21010c030b02402001280210450d00200141146a28020041002802c0a3c68000118080808000000b20012802042206418080808078460d05200141046a21010c030b02402001280204450d00200141086a28020041002802c0a3c68000118080808000000b200141106a21010c010b0240024020012d00040e0400000001050b2001410c6a21010c010b200141086a21010b200128020021060b2006450d01200128020421070b200741002802c0a3c68000118080808000000b200441016a22042003470d000b0b02402000280208450d00200028020041002802c0a3c68000118080808000000b0bab0202017f017e23808080800041106b22042480808080002004200128020020022003109188808000024002400240024002402004280200450d00200441086a28020021010240200428020422030d002001ad422086210541808080807821010c020b0240024020010d00410121020c010b2001417f4c0d0341002d00fca3c680001a200141002802c8a3c68000118180808000002202450d040b2001ad42208620022003200110848e808000ad8421050c010b200420012802042002200310ec8780800020042802002201418180808078460d03200429020421050b2000200537020420002001360200200441106a2480808080000f0b10ae80808000000b4101200110b280808000000b41dcdcc2800041302004410f6a41ccdcc2800041f4ddc28000108981808000000b6f00200042a5e9e3ab9e929adc2c37030820004280808080c00037033820004280808080c000370350200041ef80808000360218200041023a0000200041106a4293888c8f89fdc6ec9e7f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000be10101017f41002d00fca3c680001a0240413041002802c8a3c680001181808080000022010d004108413010b280808000000b200142e7b0a091f3ed9c85c500370318200142b891b68c98adebcf61370308200142e7b0a091f3ed9c85c500370300200141ea8180800036021020004280808080c00037033820004280808080c0003703502000410236020c2000200136020820004102360204200041043a0000200141206a42b891b68c98adebcf61370300200141286a41ea81808000360200200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c880808000360218200041023a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000be10101017f41002d00fca3c680001a0240413041002802c8a3c680001181808080000022010d004108413010b280808000000b200142cbc4c4bdb7cedec904370318200142b891b68c98adebcf61370308200142e7b0a091f3ed9c85c500370300200141ea8180800036021020004280808080c00037033820004280808080c0003703502000410236020c2000200136020820004102360204200041043a0000200141206a42f3d1f7c4e49ca3ecb07f370300200141286a41eb81808000360200200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b7000200042b7a18ef9ac95b6cbeb0037030820004280808080c00037033820004280808080c000370350200041ec81808000360218200041023a0000200041106a42dda1fc828d83b3faab7f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b70002000429ca0fdd992b0ed90df0037030820004280808080c00037033820004280808080c000370350200041ed81808000360218200041023a0000200041106a42bbf8f5b8b9e2c39ac400370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6e00200042d7c9cb8fc1cf97db3e37030820004280808080c00037033820004280808080c000370350200041b580808000360218200041023a0000200041106a42e88488d0c0e3aebc13370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f002000428291fa9e999aa9d24637030820004280808080c00037033820004280808080c000370350200041ee81808000360218200041023a0000200041106a42bc90c1fdf28f8bb0ff00370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6e0020004280c6969198fdafd00537030820004280808080c00037033820004280808080c000370350200041ef81808000360218200041023a0000200041106a42eaacebad91f388e55c370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042c4ccab9c81ebb4b8db0037030820004280808080c00037033820004280808080c000370350200041f081808000360218200041023a0000200041106a42a4d2928ecac0faa432370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6e0020004285cbd9e891b2d0c16137030820004280808080c00037033820004280808080c000370350200041f181808000360218200041023a0000200041106a4282d2add5d4f1deea6a370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042d7c9c3bdedbdd399957f37030820004280808080c00037033820004280808080c000370350200041f281808000360218200041023a0000200041106a42a6f8d4ee83cba9e440370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042ee93e0d6a2949dcf3137030820004280808080c00037033820004280808080c000370350200041f381808000360218200041023a0000200041106a42c49beac9c5ace288fd00370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6e00200042e1e0d7feab89e9c84837030820004280808080c00037033820004280808080c000370350200041f481808000360218200041023a0000200041106a42d391b3b4c8e6f89148370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042c4ccab9c81ebb4b8db0037030820004280808080c00037033820004280808080c000370350200041f081808000360218200041023a0000200041106a42a4d2928ecac0faa432370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042c194a6a793ccc3a85737030820004280808080c00037033820004280808080c000370350200041f581808000360218200041023a0000200041106a42ab8bffbed784ffa5937f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042ef8683cfe1dddaca6337030820004280808080c00037033820004280808080c000370350200041f681808000360218200041023a0000200041106a4284b2a2d692ae8580b57f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b7000200042c9fea184df91c8afd40037030820004280808080c00037033820004280808080c000370350200041f781808000360218200041023a0000200041106a42e2a4dca09a8089e2927f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6e00200042e5f0b3f4e8a9b1b12a37030820004280808080c00037033820004280808080c000370350200041f881808000360218200041023a0000200041106a4281ebc5ecd497b09a0a370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6e00200042a9c4d590f68dedef5837030820004280808080c00037033820004280808080c000370350200041f981808000360218200041023a0000200041106a42dbbc81a3c9e8f4e22a370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b7000200042acf6debeefe0d9c8d30037030820004280808080c00037033820004280808080c000370350200041bd80808000360218200041023a0000200041106a42efc9c9edb5e7b3a6c700370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042e7b0a091f3ed9c85c50037030820004280808080c00037033820004280808080c000370350200041ea81808000360218200041023a0000200041106a42b891b68c98adebcf61370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000be10301077f23808080800041106b2203248080808000200041286a2104024002400240200028022822054128200541284b22061b22072000280204200520061b22066b200220016b22084f0d00200620086a22052006490d014100417f2005417f6a677620054102491b41016a2205450d01200341086a2000200510f786808000024020032802082205418180808078460d002005450d022005200328020c10b280808000000b200428020022054128200541284b1b21070b200041046a22092004200541284b22061b21080240024020004104412820061b6a280200220520074f0d002000280200200020061b2106034020012002460d02200620056a20012d00003a0000200141016a21012007200541016a2205470d000b200721050b2008200536020020012002460d02034020012d00002108024002402000410441282000280228220741284b22051b6a28020022062007412820051b460d002009200420051b21072000280200200020051b21050c010b200010f8868080002000280204210620002802002105200921070b200520066a20083a00002007200728020041016a360200200141016a22012002470d000c030b0b200820053602000c010b4198dfc28000411141d8e2c2800010f880808000000b200341106a2480808080000bc70301067f23808080800041106b220324808080800002400240024002400240200128020420012802282204200441284b22051b220620024b0d002004412820051b21072001280200200120051b2108024020024129490d00418180808078210520072002460d040240200241004e0d00410021050c060b02400240024020044129490d002007417f4a0d0120072102410021050c080b41002d00fca3c680001a200241002802c8a3c680001181808080000022040d01410121050c070b0240200241002802c8a3c680001181808080000022040d00410121050c070b200420082007200220072002491b10848e8080001a200841002802c0a3c68000118080808000000c040b20042008200610848e8080001a0c030b418180808078210520044129490d0320012008200610848e80800020063602282007417f4c0d01200841002802c0a3c68000118080808000000c030b41f8e2c2800041204198e3c2800010f880808000000b2003200736020c20034100360208418ce2c28000412b200341086a41b8e2c2800041c8e2c28000108981808000000b2001200236022820012006360204200120043602000b0b2000200236020420002005360200200341106a2480808080000bb90101027f23808080800041106b220124808080800002400240200028020420002802282202200241284b1b41016a2202450d004100417f2002417f6a677620024102491b41016a2202450d00200141086a2000200210f786808000024020012802082200418180808078460d002000450d022000200128020c10b280808000000b200141106a2480808080000f0b4198dfc28000411141e8e2c2800010a181808000000b4198dfc28000411141d8e2c2800010f880808000000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710a78680800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710ac8680800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710a98680800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bdc03010d7f23808080800041106b2202248080808000024002400240024002402001280200220328020422044108490d002003280200220541076a2d00002106200541066a2d00002107200541056a2d00002108200541036a2d0000210920052d0004210a20052d0002210b20052d0001210c20052d0000210d2003200541086a3602002003200441786a360204200241086a200110bc8a808000024020022802080d002001280200220141046a2802002203200228020c2205490d000240024020050d00410121040c010b2005417f4c0d05200541002802c8a3c68000118180808000002204450d06200441002005108a8e8080001a200141046a28020021030b200320054f0d02200441002802c0a3c68000118080808000000b20004180808080783602080c020b20004180808080783602080c010b20042001280200220e200510848e80800021042000200b3a00022000200c3a00012000200d3a0000200141046a200320056b3602002001200e20056a36020020002005360208200041036a20093a000020002005ad4220862004ad8437020c2000200a3a0004200041056a20083a0000200041066a20073a0000200041076a20063a00000b200241106a2480808080000f0b10ae80808000000b4101200510b280808000000b980401077f23808080800041106b2202248080808000200241086a200110bc8a808000024002400240024002400240024020022802080d002001280200220341046a2802002204200228020c2205490d000240024020050d00410121060c010b2005417f4c0d03200541002802c8a3c68000118180808000002206450d04200641002005108a8e8080001a200341046a28020021040b200420054f0d01200641002802c0a3c68000118080808000000b20004180808080783602000c050b200620032802002207200510848e8080002106200341046a200420056b3602002003200720056a3602002002200110bc8a808000024020022802000d002001280200220341046a280200220420022802042201490d000240024020010d00410121070c010b2001417f4c0d02200141002802c8a3c68000118180808000002207450d04200741002001108a8e8080001a200341046a28020021040b200420014f0d04200741002802c0a3c68000118080808000000b20004180808080783602002005450d04200641002802c0a3c68000118080808000000c040b10ae80808000000b4101200510b280808000000b4101200110b280808000000b200720032802002208200110848e8080002107200341046a200420016b3602002003200820016a3602002000200136020c20002005360208200020063602042000200536020020002001ad4220862007ad843702100b200241106a2480808080000b840201047f23808080800041106b2202248080808000200028020421032002200028020822043602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a360208200041106a28020021052002200041146a28020022003602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822046b20004f0d0020012004200010b182808000200128020821040b200128020420046a2005200010848e8080001a2001200420006a360208200241106a2480808080000b02000bb70202067f017e23808080800041106b2202248080808000200028020421032002200028020822003602082002200241086a36020c2002410c6a200110c08a80800002402000450d002003200041286c6a210420012802082100034002402001280200220520006b411f4b0d0020012000412010b18280800020012802002105200128020821000b2001280204220620006a22072003290000370000200741186a200341186a290000370000200741106a200341106a290000370000200741086a200341086a2900003700002001200041206a2207360208200341206a29030021080240200520076b41074b0d0020012007410810b18280800020012802042106200128020821070b2001200741086a2200360208200620076a2008370000200341286a22032004470d000b0b200241106a2480808080000b850202067f017e23808080800041106b2202248080808000200028020421032002200028020822003602082002200241086a36020c2002410c6a200110c08a80800002402000450d00200320004104746a210420012802082100034002402001280200220520006b41074b0d0020012000410810b18280800020012802002105200128020821000b2001200041086a22063602082001280204220720006a2003290300370000200341086a29030021080240200520066b41074b0d0020012006410810b18280800020012802042107200128020821060b2001200641086a2200360208200720066a2008370000200341106a22032004470d000b0b200241106a2480808080000b880203037f017e047f23808080800041106b2202248080808000200028020421032002200028020822003602082002200241086a36020c2002410c6a200110c08a80800002402000450d00200320004104746a21042001280208210003402003290300210502402001280200220620006b41074b0d0020012000410810b18280800020012802002106200128020821000b2001200041086a22073602082001280204220820006a2005370000200328020821090240200620076b41034b0d0020012007410410b18280800020012802042108200128020821070b2001200741046a2200360208200820076a2009360000200341106a22032004470d000b0b200241106a2480808080000b4901017f02402000280200200028020822036b20024f0d0020002003200210ab86808000200028020821030b200028020420036a2001200210848e8080001a2000200320026a3602080bc90103057f027e017f20012802082102200128020022032104024020012802042205200128020c2206460d0020032104034002402005290300220742efb7e9de94adbae42685200541086a290300220842c785eb8b8b8eddc0618584500d0020052802102109200420083703082004200737030020042009360210200441186a21040b200541186a22052006470d000b0b200142888080808001370200200142808080808001370208200020033602042000200420036b41186e3602082000200241186c41186e3602000bc70201067f23808080800041106b220524808080800020032802042106200328020021070240200128020022012802082203200320026a22084f0d00200321080240200128020020036b20024f0d0020012003200210ab86808000200128020821080b2001280204220920086a210a024020024102490d00200a41002002417f6a2202108a8e8080001a2009200820026a22086a210a0b200a41003a0000200841016a21080b20012008360208024020082003490d00200541086a20072006200128020420036a200820036b200410bc80808000200528020c21080240024020052802082202418380c400470d0002402001280208200820036a2203490d00200120033602080b2000418380c400360200200020083602040c010b20002008360204200020023602000b200541106a2480808080000f0b200320084190e1c28000109481808000000bdb0101047f23808080800041106b22022480808080002000280208210320002802042100410121042001280214418fa5c080004101200141186a28020028020c118280808000002105200241003a0009200220053a00082002200136020402402003450d0003402002200036020c200241046a2002410c6a41d0cfc48000108e818080001a200041016a21002003417f6a22030d000b20022d000821050b0240200541ff01710d00200228020422002802144190a5c080004101200041186a28020028020c1182808080000021040b200241106a24808080800020040ba90201027f23808080800041106b22022480808080000240024020002802000d00200128021441a9dfc280004110200141186a28020028020c1182808080000021010c010b20022000360204200128021441b9dfc280004108200141186a28020028020c118280808000002100200241003a000d200220003a000c20022001360208200241086a41c1dfc280004106200241046a41c8dfc28000108c81808000210320022d000c2100024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010bcc0501087f23808080800041206b22032480808080002003200136021c2003410036021820032002360214200341086a200341146a10bc8a808000024002400240024002400240024020032802080d002003280214220441046a2802002205200328020c2201490d000240024020010d00410121060c010b2001417f4c0d03200141002802c8a3c68000118180808000002206450d04200641002001108a8e8080001a200441046a28020021050b024020052001490d00200620042802002207200110848e8080002108200441046a200520016b3602002004200720016a3602002003200341146a10bc8a8080000240024020032802000d002003280214220541046a280200220720032802042204490d000240024020040d00410121090c010b2004417f4c0d06200441002802c8a3c68000118180808000002209450d08200941002004108a8e8080001a200541046a28020021070b200720044f0d01200941002802c0a3c68000118080808000000b20010d010c020b20092005280200220a200410848e8080002109200541046a200720046b3602002005200a20046a3602000240200328021422052802042207450d0020052007417f6a36020420052005280200220741016a3602004101410220072d000022054101461b410020051b22054102470d030b02402004450d00200941002802c0a3c68000118080808000000b2001450d010b200641002802c0a3c68000118080808000000b20004180808080783602000c050b2002280204450d03200041808080807836020002402001450d00200841002802c0a3c68000118080808000000b2004450d04200941002802c0a3c68000118080808000000c040b10ae80808000000b4101200110b280808000000b4101200410b280808000000b200020053a001820002004360214200020093602102000200436020c2000200136020820002008360204200020013602000b200341206a2480808080000bb20501087f23808080800041206b22032480808080002003200136021c2003410036021820032002360214200341086a200341146a10bc8a8080000240024020032802080d002003280214220441046a2802002205200328020c2201490d00024002400240024002400240024020010d00410121060c010b2001417f4c0d01200141002802c8a3c68000118180808000002206450d02200641002001108a8e8080001a200441046a28020021050b20052001490d04200620042802002207200110848e8080002108200441046a200520016b3602002004200720016a3602002003200341146a10bc8a8080000240024020032802000d002003280214220541046a280200220720032802042204490d000240024020040d00410121090c010b2004417f4c0d03200441002802c8a3c68000118180808000002209450d05200941002004108a8e8080001a200541046a28020021070b200720044f0d01200941002802c0a3c68000118080808000000b20010d050c060b20092005280200220a200410848e8080002109200541046a200720046b3602002005200a20046a3602002003280214220528020422074104490d0320052007417c6a36020420052005280200220641046a360200024020022802040d002000200628000036021820002004360214200020093602102000200436020c2000200136020820002008360204200020013602000c070b200041808080807836020002402001450d00200841002802c0a3c68000118080808000000b2004450d06200941002802c0a3c68000118080808000000c060b10ae80808000000b4101200110b280808000000b4101200410b280808000000b02402004450d00200941002802c0a3c68000118080808000000b2001450d010b200641002802c0a3c68000118080808000000b20004180808080783602000b200341206a2480808080000baf0201087f02400240024002400240200128020822020d00410421030c010b200241aad5aad5004b0d022002410c6c2204417f4c0d022001280204210541002d00fca3c680001a200441002802c8a3c68000118180808000002203450d014100210620022107034020042006460d012005280204210802400240200528020822010d00410121090c010b2001417f4c0d0441002d00fca3c680001a200141002802c8a3c68000118180808000002209450d050b2005410c6a210520092008200110848e8080002109200320066a22082001360200200841086a2001360200200841046a20093602002006410c6a21062007417f6a22070d000b0b2000200236020820002003360204200020023602000f0b4104200410b280808000000b10ae80808000000b4101200110b280808000000b840101027f200128020421020240024002400240200128020822010d00410121030c010b2001417f4c0d0141002d00fca3c680001a200141002802c8a3c68000118180808000002203450d020b20032002200110848e80800021022000200136020820002002360204200020013602000f0b10ae80808000000b4101200110b280808000000baa03010a7f024002400240024002400240200128020822020d00410421030c010b200241d5aad52a4b0d03200241186c2204417f4c0d032001280204210141002d00fca3c680001a200441002802c8a3c68000118180808000002203450d014100210520022106034020042005460d0120012802042107410121084101210902402001280208220a450d00200a417f4c0d0541002d00fca3c680001a200a41002802c8a3c68000118180808000002209450d040b20092007200a10848e808000210b200141106a28020021090240200141146a2802002207450d002007417f4c0d0541002d00fca3c680001a200741002802c8a3c68000118180808000002208450d060b200141186a210120082009200710848e8080002108200320056a2209200a360200200941146a2007360200200941106a20083602002009410c6a2007360200200941086a200a360200200941046a200b360200200541186a21052006417f6a22060d000b0b2000200236020820002003360204200020023602000f0b4104200410b280808000000b4101200a10b280808000000b10ae80808000000b4101200710b280808000000bfe06010f7f02400240024002400240024002400240200128020822020d00410421030c010b200241e6cc99334b0d05200241146c2204417f4c0d052001280204210541002d00fca3c680001a200441002802c8a3c68000118180808000002203450d014100210620022107034020042006460d014104210802400240024002400240200520066a22012d00000e050001020304000b200141046a2d00002109200141036a2d0000210a200141026a2d0000210b4101210c200141016a2d0000210d2001410c6a28020021080240200141106a280200220e450d00200e417f4c0d0b41002d00fca3c680001a200e41002802c8a3c6800011818080800000220c450d080b200c2008200e10848e8080001a41002108200e210f0c030b200141046a2d00002109200141036a2d0000210a200141026a2d0000210b41012108200141016a2d0000210d2001410c6a280200210f4101210c0240200141106a280200220e450d00200e417f4c0d0a41002d00fca3c680001a200e41002802c8a3c6800011818080800000220c450d080b200c200f200e10848e8080001a200e210f0c020b200141046a2d00002109200141036a2d0000210a200141026a2d0000210b4101210c200141016a2d0000210d2001410c6a28020021080240200141106a280200220e450d00200e417f4c0d0941002d00fca3c680001a200e41002802c8a3c6800011818080800000220c450d080b200c2008200e10848e8080001a41022108200e210f0c010b2001410f6a2d000021082001410d6a2f000021102001410c6a220e2d00002109200141086a280200210102400240200e280200220c0d004101210e0c010b200c417f4c0d0841002d00fca3c680001a200c41002802c8a3c6800011818080800000220e450d090b20102008411074722110200e2001200c10848e8080001a410321080b200320066a220120083a0000200141076a20104110763a0000200141056a20103b0000200141036a200a3a0000200141026a200b3a0000200141016a200d3a0000200141106a200f3602002001410c6a200c360200200141086a200e360200200141046a20093a0000200641146a21062007417f6a22070d000b0b2000200236020820002003360204200020023602000f0b4104200410b280808000000b4101200e10b280808000000b4101200e10b280808000000b4101200e10b280808000000b10ae80808000000b4101200c10b280808000000bee0601047f024002402000280200220141054b0d002001450d0102400240024002402000410c6a280200417e6a2202410220024102491b0e020103000b200041146a21020c010b200041106a21020b2002280200450d00200228020441002802c0a3c68000118080808000000b0240200041246a280200450d00200041286a28020041002802c0a3c68000118080808000000b20014101460d010240024002400240200041386a280200417e6a2202410220024102491b0e020003010b2000413c6a21020c010b200041c0006a21020b2002280200450d00200228020441002802c0a3c68000118080808000000b0240200041d0006a280200450d00200041d4006a28020041002802c0a3c68000118080808000000b20014102460d010240024002400240200041e4006a280200417e6a2202410220024102491b0e020003010b200041e8006a21020c010b200041ec006a21020b2002280200450d00200228020441002802c0a3c68000118080808000000b0240200041fc006a280200450d0020004180016a28020041002802c0a3c68000118080808000000b20014103460d01024002400240024020004190016a280200417e6a2202410220024102491b0e020003010b20004194016a21020c010b20004198016a21020b2002280200450d00200228020441002802c0a3c68000118080808000000b0240200041a8016a280200450d00200041ac016a28020041002802c0a3c68000118080808000000b20014104460d010240024002400240200041bc016a280200417e6a2201410220014102491b0e020003010b200041c0016a21010c010b200041c4016a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200041d4016a280200450d01200041d8016a28020041002802c0a3c68000118080808000000f0b200028020421030240200041086a2802002202450d00200341246a21004100210103400240024002400240200041646a280200417e6a2204410220044102491b0e020103000b20032001412c6c6a41106a21040c010b200041686a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b02402000417c6a280200450d00200028020041002802c0a3c68000118080808000000b200141016a21012000412c6a21002002417f6a22020d000b0b200341002802c0a3c68000118080808000000b0be70901087f024020002802082201450d00200028020421024100210303400240024002400240024002402002200341e8016c6a2200280200220441736a2205410220054104491b0e03010203000b20002d00084106470d042000410c6a280200450d04200041106a28020021060c030b0240024002400240024002400240024020002d0008417f6a0e0a010b0203040506070b0b000b2000410c6a280200450d0a200041106a28020021060c090b2000410c6a280200450d09200041106a28020021060c080b2000410c6a280200450d08200041106a28020021060c070b2000410c6a280200450d07200041106a28020021060c060b0240200041146a2802002204450d00200041106a2802002105034002402005280200450d00200541046a28020041002802c0a3c68000118080808000000b02402005410c6a280200450d00200541106a28020041002802c0a3c68000118080808000000b200541186a21052004417f6a22040d000b0b200028020c450d06200028021021060c050b0240200041146a2802002205450d00200041106a28020021062005410171210741002104024020054101460d002005417e7121084100210420062105034002402005280200450d00200541046a28020041002802c0a3c68000118080808000000b02402005410c6a280200450d00200541106a28020041002802c0a3c68000118080808000000b200541186a21052008200441026a2204470d000b0b2007450d0020062004410c6c6a2205280200450d00200528020441002802c0a3c68000118080808000000b200028020c450d05200028021021060c040b200041106a280200450d04200041146a28020021060c030b2000410c6a280200450d03200041106a28020021060c020b200041186a2d0000417d6a41ff017141014b0d020240200028020822064198016a2802002208450d004100210520064194016a2802002207210003402007200541146c6a2104024002400240024020002d00000e0400000001030b200441086a21040c010b200041046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200541016a2105200041146a21002008417f6a22080d000b0b024020064190016a280200450d0020062802940141002802c0a3c68000118080808000000b024020064190026a2802002208450d00410021052006418c026a2802002207210003402007200541146c6a2104024002400240024020002d00000e0400000001030b200441086a21040c010b200041046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200541016a2105200041146a21002008417f6a22080d000b0b20064188026a280200450d01200628028c0241002802c0a3c68000118080808000000c010b0240024002400240024002402004417e6a0e06000102000003070b200041046a21000c030b02402000280210450d00200041146a28020041002802c0a3c68000118080808000000b20002802042205418080808078460d05200041046a21000c030b02402000280204450d00200041086a28020041002802c0a3c68000118080808000000b200041106a21000c010b0240024020002d00040e0400000001050b2000410c6a21000c010b200041086a21000b200028020021050b2005450d01200028020421060b200641002802c0a3c68000118080808000000b200341016a22032001470d000b0b0bb70401047f024020002802082201450d0020002802042100034002400240024002400240200041046a200020002d00004108461b22022d00002203417c6a41ff01712204410420044104491b0e0404010203000b0240200241dc006a2802004129490d00200241346a28020041002802c0a3c68000118080808000000b200228022c41002802c0a3c680001180808080000020034103460d030240024020030e020105000b2002280224220420042802002204417f6a36020020044101470d04200241246a10e28a8080000c040b2002280204220420042802002204417f6a36020020044101470d03200241046a10e28a8080000c030b0240200241dc006a2802004129490d00200241346a28020041002802c0a3c68000118080808000000b02400240024020022d00040e020105000b200241286a2202280200220420042802002204417f6a36020020044101460d010c040b200241086a2202280200220420042802002204417f6a36020020044101470d030b200210e28a8080000c020b200241d4006a2802004129490d012002412c6a28020041002802c0a3c68000118080808000000c010b200228023041002802c0a3c680001180808080000020022d000422044103460d0002400240024020040e020103000b200241286a2202280200220420042802002204417f6a36020020044101460d010c020b200241086a2202280200220420042802002204417f6a36020020044101470d010b200210e28a8080000b20004180016a21002001417f6a22010d000b0b0bd80201047f23808080800041306b2201248080808000024020002802082202450d002000280204210003400240024002400240024020002d00000e050404010203000b02400240200041046a28020022030d0041002103410021040c010b200120033602242001410036022020012003360214200141003602102001200041086a2802002203360228200120033602182000410c6a2802002104410121030b2001200436022c2001200336021c2001200336020c2001410c6a10aa8d8080000c030b200041046a280200450d02200041086a28020041002802c0a3c68000118080808000000c020b200041046a280200450d01200041086a28020041002802c0a3c68000118080808000000c010b200041046a22031091878080002003280200450d00200041086a28020041002802c0a3c68000118080808000000b200041106a21002002417f6a22020d000b0b200141306a2480808080000b980201057f23808080800041106b2205248080808000024020012802082206200620026a22074f0d00200621070240200128020020066b20024f0d0020012006200210ab86808000200128020821070b2001280204220820076a2109024020024102490d00200941002002417f6a2202108a8e8080001a2008200720026a22076a21090b200941003a0000200741016a21070b20012007360208024020072006490d00200541086a20032802002003280204200128020420066a200720066b200410a888808000200528020c21020240200528020822030d002007200220066a2206490d00200120063602080b2000200336020020002002360204200541106a2480808080000f0b2006200741fce1c28000109481808000000b5601017f23808080800041106b2202248080808000200241086a200110bd8a8080000240024020022802080d0020002001200228020c10ce858080000c010b20004180808080783602000b200241106a2480808080000b5601017f23808080800041106b2202248080808000200241086a200110bc8a8080000240024020022802080d0020002001200228020c10c7858080000c010b20004180808080783602000b200241106a2480808080000bbe0101077f41002103024002402001410f712204410f470d00410121032002280208220541016a200228020422064b0d0020022802002107410f2108200521010340200120064f0d022002200141016a3602080240200720016a2d0000220941ff01470d00200841ff016a210841012103200141026a2109200141016a2101200920064d0d010c020b0b200920086a2104410021030b20002004360204200020033602000f0b20052006200520064b1b200641ecc1c2800010f980808000000b2c01017f2000200128020420012802282203200341284b22031b36020420002001280200200120031b3602000b810701097f23808080800041106b22022480808080000240024002400240024002402001280208220341016a2204200128020422054b22060d000240200320054f0d0020012004360208024002400240024002400240024002400240024002402001280200220720036a2d00002208450d0020084106764102730e0403020a01030b200041003a00000c100b2008413f712208413f470d030240200341026a20054b0d00413f2108200421030340200320054f0d0d2001200341016a2209360208200720036a2d0000220a41ff01470d04200841ff016a2108200341026a210a20092103200a20054d0d000b0b200041053a00000c0f0b2008413f712208413f470d040240200341026a20054b0d00413f2108200421030340200320054f0d0d2001200341016a2209360208200720036a2d0000220a41ff01470d05200841ff016a2108200341026a210a20092103200a20054d0d000b0b200041053a00000c0e0b2008413f712208413f470d050240200341026a20054b0d00413f2108200421030340200320054f0d0d2001200341016a2209360208200720036a2d0000220a41ff01470d06200841ff016a2108200341026a210a20092103200a20054d0d000b0b200041053a00000c0d0b200a20086a21080b20002008360204200041023a00000c0b0b200a20086a21080b2000200836020420004181023b01000c090b200a20086a21080b20002008360204200041013b01000c070b0240024002400240200841e001714120460d00200841f001714110460d01200041053a00000c0a0b2008411f712208411f470d020240200341026a20054b0d00411f2108200421030340200320054f0d0a2001200341016a2209360208200720036a2d0000220a41ff01470d03200841ff016a2108200341026a210a20092103200a20054d0d000b0b200041053a00000c090b200241086a20082001109587808000024020022802080d00200228020c2101200041033a0000200020013602040c090b200041053a00000c080b200a20086a21080b20002008360204200041043a00000c060b2003200541ecc1c2800010f980808000000b200041053a00000c040b2004200520061b200541ecc1c2800010f980808000000b2004200520061b200541ecc1c2800010f980808000000b2004200520061b200541ecc1c2800010f980808000000b2004200520061b200541ecc1c2800010f980808000000b200241106a2480808080000bf70701047f02400240024002400240024020002d00000e050004010203000b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41003a00000c040b41ff0020002802042202413e2002413e491b220041c000722002413e4b1b2103200220006b2102410121040340200441ff0171210041002104200321050240024020000e03000100010b2002450d05024002402002418002490d00200241817e6a210241ff0121050c010b2002417f6a2105410021020b410221040b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20053a00000c000b0b411f20002802042202410e2002410e491b22004110722002410e4b1b2103200220006b2102410121040340200441ff0171210041002104200321050240024020000e03000100010b2002450d04024002402002418002490d00200241817e6a210241ff0121050c010b2002417f6a2105410021020b410221040b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20053a00000c000b0b413f20002802042202411e2002411e491b22004120722002411e4b1b2103200220006b2102410121040340200441ff0171210041002104200321050240024020000e03000100010b2002450d03024002402002418002490d00200241817e6a210241ff0121050c010b2002417f6a2105410021020b410221040b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20053a00000c000b0b200028020422042004413e2004413e491b22056b2102024020002d0001450d00417f20054140722004413e4b1b2103410121040340200441ff0171210041002104200321050240024020000e03000100010b2002450d03024002402002418002490d00200241817e6a210241ff0121050c010b2002417f6a2105410021020b410221040b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20053a00000c000b0b41bf7f200541807f722004413e4b1b2103410121040340200441ff0171210041002104200321050240024020000e03000100010b2002450d02024002402002418002490d00200241817e6a210241ff0121050c010b2002417f6a2105410021020b410221040b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20053a00000c000b0b0ba707010d7f200141186a28020021022001410d6a2d00002103200141096a2d00002104200141146a28020021052001280210210620012d000c21072001280204210820012d000821092001280200210a0340024002400240024002400240024002400240024002400240024002400240024002400240200a417e6a0e020102000b0240200941ff0171220b4102460d002001200b454101743a000841002109200b450d002004210c0c030b0240200a450d002008450d0002402008418002490d002001200841817e6a220836020441ff01210c410221090c040b200141003602042008417f6a210c41022109410021080c030b41022109200141023602000b200741ff0171210a410221070240200a4102460d0041002107200141003a000c200a0d030b200141033602000b2006450d072005450d07200120052005200220052002491b220a6b220536021420012006200a6a220b360210024002400240200a0e020001020b4100410041f4d3c4800010f980808000000b410141014184d4c4800010f980808000000b20062d000041047420062d000172210c4103210a200b21060b2000280208220b2000280200470d0d200a4103470d024100210d4103210a2006450d0c2005450d0c2002450d01200520026e220d2005200d20026c6b4100476a210d0c0c0b410021072000280208220b2000280200460d024102210a2003210c0c0c0b41b0e4c280004119419ce4c2800010f880808000000b2006450d06200a4102470d024102210a200741ff01714102470d014100210d410221070c050b2003210c2006450d060b200741ff0171410047210d4102210a0c030b200741ff0171220d4102460d012009410171200d4100476a210d0c020b0f0b410221074100210d0240200941ff0171220e4102470d00410221090c010b200e410047210d0b4100210e024002402005450d002002450d01200520026e220e2005200e20026c6b4100476a210e0b417f200d200e6a220e200e200d491b210d0c040b41b0e4c280004119419ce4c2800010f880808000000b200a4102470d014102210a410021064100210d200741ff01714102460d020b41002106200741ff0171410047210d4102210a0c010b0240200741ff0171220d4102460d00410021062009410171200d4100476a210d0c010b410021060240200941ff0171220d4102470d00410221094100210d0c010b41002106200d410047210d0b2000200b200d41016a220d417f200d1b10ab868080000b2000200b41016a3602082000280204200b6a200c3a00000c000b0bff0101077f20012802002102200128020421032001280208210402400240024020012d000c450d002001410d6a2d00002105200141003a000c2005410f7121060c010b200420034f0d012001200441016a2205360208200220046a2d00002106200521040b0240200028020822072000280200470d0020002007410110ab868080000b2000200741016a22053602082000280204220820076a20063a0000200420034f0d0003402001200441016a2207360208200220046a2d00002104024020052000280200470d0020002005410110ab86808000200028020421080b200820056a20043a00002000200541016a22053602082007210420032007470d000b0b0bca04020c7f027e23808080800041206b2202248080808000200241146a200110818a808000024002400240024002402002280214418080808078470d002000410036020820004280808080c0003702000c010b41002d00fca3c680001a413041002802c8a3c68000118180808000002203450d0120032002290214370200200341086a200241146a41086a280200360200200241013602102002200336020c20024104360208024020012802082204200128020422054d0d00200128020c210620012802002107200420056b2108411021054100210903402002200710bc8a8080000240024020022802000d002007280200220a41046a220b280200220420022802042201490d000240024020010d004101210c0c010b2001417f4c0d07200141002802c8a3c6800011818080800000220c450d08200c41002001108a8e8080001a200b28020021040b200420014f0d01200c41002802c0a3c68000118080808000000b200641013a00000c020b200c200a280200220d200110848e808000210c200b200420016b360200200a200d20016a3602002001ad210e200cad210f0240200941016a22042002280208470d00200241086a2004410110ad86808000200228020c21030b200320056a220a200e422086200f84370200200a417c6a20013602002002200941026a3602102005410c6a21052004210920082004470d000b0b20002002290208370200200041086a200241086a41086a2802003602000b200241206a2480808080000f0b4104413010b280808000000b10ae80808000000b4101200110b280808000000bae09010c7f2380808080004190016b2202248080808000200241186a200110e18c8080000240024002400240024020022802182203418080808078460d002002280228210420022802242105200228021c210602402001412c6a2802002207200141286a28020022084d0d0002402003450d00200641002802c0a3c68000118080808000000b2005450d01200441002802c0a3c68000118080808000000c010b200228022c21092002280220210a20012008417f6a36022820012001280224220841046a3602242002200936022c20022004360228200220053602242002200a3602202002200636021c200220073602342002200836023020022003360218200241f0006a2001413c6a200241186a10de8a8080002002280278418080808078470d010b2000410036020820004280808080c000370200200110ab8d8080000c010b20012802202203410020012802282205200128022c22046b41016a20052004491b220520032005491b41016a2203417f20031b22034104200341044b1b220341e6cc99334b0d01200341146c2205417f4c0d0141002d00fca3c680001a200541002802c8a3c6800011818080800000220b450d02200b2002290270370200200b41106a200241f0006a41106a280200360200200b41086a200241f0006a41086a290200370200200241013602142002200b3602102002200336020c200241186a41386a200141386a290200370300200241186a41306a200141306a290200370300200241186a41286a200141286a290200370300200241186a41206a200141206a290200370300200241186a41186a200141186a290200370300200241186a41106a200141106a290200370300200241186a41086a200141086a29020037030020022001290200370318200241f0006a200241186a10e18c808000024020022802702205418080808078460d00200241d4006a210c411421034101210103402002280280012106200228027c210420022802742107024020022802442208200228024022094d0d0002402005450d00200741002802c0a3c68000118080808000000b2004450d02200641002802c0a3c68000118080808000000c020b200228028401210a2002280278210d20022009417f6a3602402002200228023c220941046a36023c2002200a3602840120022006360280012002200436027c2002200d360278200220073602742002200836028c01200220093602880120022005360270200241dc006a200c200241f0006a10de8a8080002002280264418080808078460d0102402001200228020c470d002002410c6a200120022802382205410020022802402204200228024422066b41016a20042006491b220420052004491b41016a2205417f20051b10a8868080002002280210210b0b200b20036a2205200229025c370200200541106a200241dc006a41106a280200360200200541086a200241dc006a41086a2902003702002002200141016a2201360214200341146a2103200241f0006a200241186a10e18c80800020022802702205418080808078470d000b0b200241186a10ab8d808000200041086a2002410c6a41086a2802003602002000200229020c3702000b20024190016a2480808080000f0b10ae80808000000b4104200510b280808000000b8d0e010f7f23808080800041206b2202248080808000024002400240024002400240024020012802004102460d00200241086a200110e08c808000024020022802082203450d00200228020c220441086a28020020042802002205200541054b22051b2206450d03200328020821072003280204210802402006412c6c2004280204200441046a20051b6a41546a2204280208220341014b0d0020042802042106200428021c2105024020042802002209450d0020052006460d010b2002200441106a36021c2002411c6a200920062005108e858080002004200536020420044101360200200428020821030b410021050240024002402003417e6a2203410220034102491b0e03000501000b2004410c6a21040c010b200441106a21040b20042802082106200428020421050c020b200141023602000b20012802242204450d022004200141286a280200460d022001200441186a3602244100200441106a280200200428020c418080808078461b2105200441146a280200210620042802082107200428020421080b20012802242103024020012802004102470d0020030d03410021040c040b200128022021042003450d03417f2004200141286a28020020036b41186e6a220320032004491b21040c030b41fcb8c2800041fc0041f8bac2800010a181808000000b2000410036020820004280808080c0003702000c020b200141286a28020020036b41186e21040b0240200441016a2204417f20041b22044104200441044b1b220341ffffff3f4b0d0020034104742209417f4c0d0041002d00fca3c680001a0240200941002802c8a3c68000118180808000002204450d002004200636020c2004200536020820042007360204200420083602002002410136021820022004360214200220033602102001280228210a200128022421092001280220210b200128020c2103200128020821052001280204210420012802002107024002400340024002400240024002400240024002400240024020074102460d00200b0d014100210b0b2009450d012009200a460d014100200941106a280200200928020c418080808078461b2108200941146a280200210c2009280208210d2009280204210e41022107200941186a22012109200228021822062002280210460d050c080b024002402007450d002004450d010b20070d0441d4fec5800010a081808000000b41012107200521042003450d0220032101024020034107712205450d0003402001417f6a210120042802ac1421042005417f6a22050d000b0b200341084f0d010c020b20002002290210370200200041086a200241106a41086a2802003602000c0c0b034020042802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142104200141786a22010d000b0b41002103410021050b0240200320042f01aa14490d00034020042802a0132201450d07200541016a210520042f01a814210320012104200320012f01aa144f0d000b0b200341016a210f0240024020050d00200421010c010b2004200f4102746a41ac146a28020021014100210f2005417f6a2206450d002005417e6a2108024020064107712205450d0003402006417f6a210620012802ac1421012005417f6a22050d000b0b20084107490d00034020012802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142101200641786a22060d000b0b2004200341e0016c6a220541086a28020020052802002206200641054b22061b2208450d0620042003410c6c6a220441ac136a280200210d200441a8136a280200210e02402008412c6c2005280204200541046a20061b6a41546a2204280208220341014b0d0020042802042106200428021c2105024020042802002208450d0020052006460d010b2002200441106a36021c2002411c6a200820062005108e858080002004200536020420044101360200200428020821030b410021054100210802400240024002402003417e6a2203410220034102491b0e03000301000b2004410c6a21040c010b200441106a21040b2004280208210c200428020421080b200b417f6a210b0240200228021822062002280210460d0020012104200f21030c040b20074102470d01410221074100210520012104200f210320092101410021102009450d020b200a20016b41186e211041022107200121090c010b41002105024020090d0041002109200f210320012104200b21100c010b417f200b200a20096b41186e6a22042004200b491b2110200f2103200121040b200241106a2006201041016a2201417f20011b10aa868080000b200228021420064104746a2201200c36020c200120083602082001200d3602042001200e3602002002200641016a3602180c000b0b419cd0c2800010a081808000000b41fcb8c2800041fc0041f8bac2800010a181808000000b4104200910b280808000000b10ae80808000000b200241206a2480808080000bb00703077f027e057f23808080800041306b2202248080808000200141086a280200220320012802042204200320044b1b210520012802002106200128020c2107024002400240024003400240024020042005460d002001200441016a2204360204200241186a200610fc8680800020022802202208418080808078470d01200741013a00000b2000410036020820004280808080c0003702000c020b2008418180808078460d000b200229031821092002290224210a41002d00fca3c680001a41d00041002802c8a3c6800011818080800000220b450d01200b200a37020c200b2008360208200b2009370200200241013602142002200b3602102002410436020c0240200420034f0d00200420036b210c41202103410121080340024002402006280200220428020422014108490d002004280200220529000021092004200141786a3602042004200541086a360200200628020022042802042205450d0020042005417f6a220d36020420042004280200220141016a3602000240024002400240024020012d0000220e4103710e0400030102000b200e41027621040c030b20054104490d0320042005417c6a3602042004200141046a360200200141036a2d000041187420012f000141087472200e72220441808004490d03200441027621040c020b200e41044f0d0220054105490d0220042005417b6a3602042004200141056a360200200128000122044180808080044f0d010c020b200d450d0120042005417e6a3602042004200141026a36020020012d0001410874200e7241ffff03712204418002490d01200441027621040b2006280200220541046a220e28020022012004490d000240024020040d004101210d0c010b2004417f4c0d07200441002802c8a3c6800011818080800000220d450d08200d41002004108a8e8080001a200e28020021010b200120044f0d01200d41002802c0a3c68000118080808000000b200741013a00000c020b200d2005280200220f200410848e808000210d200e200120046b3602002005200f20046a3602002004ad422086200dad84210a02402008200228020c470d002002410c6a2008410110a8868080002002280210210b0b200b20036a2201200a3702002001417c6a2004360200200141746a20093702002002200841016a2208360214200341146a2103200c20086a4101470d000b0b2000200229020c370200200041086a2002410c6a41086a2802003602000b200241306a2480808080000f0b410441d00010b280808000000b10ae80808000000b4101200410b280808000000bc60a010b7f23808080800041206b2202248080808000200241086a200110e08c80800002400240024020022802082203450d00200228020c220441086a28020020042802002205200541054b22051b2206450d01200328020821072003280204210802402006412c6c2004280204200441046a20051b6a41546a2204280208220341014b0d0020042802042106200428021c2105024020042802002209450d0020052006460d010b2002200441106a36021c2002411c6a200920062005108e858080002004200536020420044101360200200428020821030b4100210502400240024002402003417e6a2203410220034102491b0e03000301000b2004410c6a21040c010b200441106a21040b20042802082106200428020421050b0240200128022041016a2204417f20041b22044104200441044b1b220341ffffff3f4b0d0020034104742209417f4c0d0041002d00fca3c680001a0240200941002802c8a3c68000118180808000002204450d002004200636020c20042005360208200420073602042004200836020020024101360218200220043602142002200336021002400240024020012802202207450d00200128020021082001280204210420012802082105200128020c210303400240024002402008450d002004450d010b20080d0141d4fec5800010a081808000000b410121082005210402402003450d0020032101024020034107712205450d0003402001417f6a210120042802ac1421042005417f6a22050d000b0b20034108490d00034020042802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142104200141786a22010d000b0b41002103410021050b0240200320042f01aa14490d00034020042802a0132201450d04200541016a210520042f01a814210320012104200320012f01aa144f0d000b0b200341016a21090240024020050d00200421010c010b200420094102746a41ac146a2802002101410021092005417f6a2206450d002005417e6a210a024020064107712205450d0003402006417f6a210620012802ac1421012005417f6a22050d000b0b200a4107490d00034020012802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142101200641786a22060d000b0b2004200341e0016c6a220541086a28020020052802002206200641054b22061b220a450d0320042003410c6c6a220441ac136a280200210b200441a8136a280200210c0240200a412c6c2005280204200541046a20061b6a41546a2204280208220341014b0d0020042802042106200428021c210502402004280200220a450d0020052006460d010b2002200441106a36021c2002411c6a200a20062005108e858080002004200536020420044101360200200428020821030b4100210502400240024002402003417e6a2203410220034102491b0e03000301000b2004410c6a21040c010b200441106a21040b20042802082106200428020421050b2007417f6a21070240200228021822032002280210470d00200241106a2003200741016a2204417f20041b10aa868080000b200228021420034104746a2204200636020c200420053602082004200b3602042004200c3602002002200341016a36021841002105200921032001210420070d000b0b20002002290210370200200041086a200241106a41086a2802003602000c060b419cd0c2800010a081808000000b41fcb8c2800041fc0041f8bac2800010a181808000000b4104200910b280808000000b10ae80808000000b2000410036020820004280808080c0003702000c010b41fcb8c2800041fc0041f8bac2800010a181808000000b200241206a2480808080000bd20201047f23808080800041c0006b2202248080808000200241206a41086a200141086a29020037030020022001290200370320200241086a200241206a109b87808000024002400240200228021022030d0020004100360208200041003602002002280208450d01200228020c41002802c0a3c68000118080808000000c010b200228020c220120032002413f6a10d88b8080002002280208210441002d00fca3c680001a418c0141002802c8a3c68000118180808000002205450d0120054100360200200541003b018a0120024100360218200220053602142002410036021c200220012003410c6c6a36023820022004360234200220013602302002200136022c2002418180808078360220200241146a200241206a2002411c6a10c3858080002000200228021c360208200020022902143702000b200241c0006a2480808080000f0b4104418c0110b280808000000b7c01017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110c182808000200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c000370350200041850c3b0100200141106a2480808080000bed0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242bea4ae99af91eca2827f370318200242dac1a9c58db4fcb8cb00370308200242f1acbf83d7fffdaeb77f370300200241fd81808000360210200241206a42a1efe9e2fe938eee74370300200241286a41fe818080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000beb0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242d7c9cb8fc1cf97db3e37031820024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200241ef80808000360210200241206a42e88488d0c0e3aebc13370300200241286a41b5808080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242a5e9e3ab9e929adc2c370318200242949384fce98ae9ac977f370308200242c4a8f7cba2aea4ad35370300200241ff81808000360210200241206a4293888c8f89fdc6ec9e7f370300200241286a41ef808080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000beb0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242b7a0aca7a89ca5e845370318200242d4a5e8fca9e787a92e370308200242cce9ab8ffbfcead3703703002002418082808000360210200241206a42f58896d99bcda087da00370300200241286a4181828080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242a5e9e3ab9e929adc2c37031820024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200241ef80808000360210200241206a4293888c8f89fdc6ec9e7f370300200241286a41ef808080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110fa86808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004120360220200041c880808000360218200041033a0000200141106a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110fa86808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004108360220200041c880808000360218200041033a0000200141106a2480808080000b8f0503017f017e057f23808080800041106b22022480808080002000290300210302402001280200200128020822046b41074b0d0020012004410810b182808000200128020821040b2001200441086a360208200128020420046a2003370000200041146a28020021052002200041186a28020022043602082002200241086a36020c2002410c6a200110c08a80800002402004450d002004410c6c2106200541086a210503402005417c6a28020021072002200528020022043602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822086b20044f0d0020012008200410b182808000200128020821080b200128020420086a2007200410848e8080001a2001200820046a3602082005410c6a2105200641746a22060d000b0b200041206a28020021052002200041246a28020022043602082002200241086a36020c2002410c6a200110c08a808000024002402004450d002004410c6c2106200541086a210503402005417c6a28020021072002200528020022043602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822086b20044f0d0020012008200410b182808000200128020821080b200128020420086a2007200410848e8080001a2001200820046a22043602082005410c6a2105200641746a22060d000c020b0b200128020821040b200029030821030240200128020020046b41074b0d0020012004410810b182808000200128020821040b200128020420046a20033700002001200441086a220436020820002d00282105024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a20053a0000200241106a2480808080000bd70801047f0240024020002d00000d000240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a22033602082001280204220420026a41003a00000240024002400240024002400240024002400240024020002d00010e0b000102030405060708090a000b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41003a00000c0b0b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41013a00000c0a0b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41023a00000c090b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41033a00000c080b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41043a00000c070b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41053a00000c060b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41063a00000c050b200041026a2d0000210502400240200128020022022003460d00200321000c010b20012003410110b1828080002001280200210220012802042104200128020821000b2001200041016a2203360208200420006a41073a0000024020022003470d0020012002410110b18280800020012802042104200128020821030b200420036a20053a00000c040b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41083a00000c030b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41093a00000c020b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a410a3a00000c010b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a22033602082001280204220420026a41013a000002400240024020002d00010e03000102000b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41003a00000c020b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41013a00000c010b200041026a2d0000210502400240200128020022022003460d00200321000c010b20012003410110b1828080002001280200210220012802042104200128020821000b2001200041016a2203360208200420006a41023a0000024020022003470d0020012002410110b18280800020012802042104200128020821030b200420036a20053a00000b2001200341016a3602080bb92a03047f017e017f23808080800041f0016b220224808080800002400240024002400240024002400240024002400240024020012d0028417f6a0e0b000102030405060708090a000b200141306a28020021032001412c6a2802002104200128020021010240024010fa81808000220541fe014d0d0042002106410921050c010b2002200541016a3602484100210541b1e3c080004113200241c8006a410441002802e0a1c680001186808080000041002802e8a1c680001188808080000041002802b0a1c6800011888080800000200241c8006a10fb81808000420221060b02402004450d00200341002802c0a3c68000118080808000000b20012001280200417f6a2204360200024020040d00200141086a28020022032001410c6a28020022042802001180808080000002402004280204450d00200341002802c0a3c68000118080808000000b200141046a22042004280200417f6a220436020020040d00200141002802c0a3c68000118080808000000b2000420037030820002006370300200041216a41003a0000200041206a20053a0000200041186a41003a00000c0a0b200141306a2903002106200241b8016a41206a200141206a290300370300200241b8016a41186a2204200141186a290300370300200241b8016a41106a2205200141106a290300370300200241b8016a41086a2203200141086a290300370300200220012903003703b8012002200637038001200220024180016a3602e001200241c8006a200241b8016a10bb8680800002400240200229034822064202520d002004200241c8006a41206a2903003703002005200241c8006a41186a2903003703002003200241c8006a41106a290300370300200220022903503703b8010c010b200020022903703703282004200241c8006a41206a2903003703002005200241c8006a41186a2903003703002003200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b8010b200041086a220120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c090b20024180016a41086a2204200141346a28020036020020022001412c6a29020022063703800120024180016a412c6a200141206a290200370200200241a4016a200141186a2902003702002002419c016a200141106a29020037020020024194016a200141086a2902003702002002200129020037028c01200241b8016a41306a20024180016a41306a280200360200200241b8016a41286a20024180016a41286a290300370300200241b8016a41206a20024180016a41206a290300370300200241b8016a41186a220120024180016a41186a290300370300200241b8016a41106a220520024180016a41106a290300370300200241b8016a41086a22032004290300370300200220063703b801200241c8006a200241b8016a10b58680800002400240200229034822064202520d002001200241c8006a41206a2903003703002005200241c8006a41186a2903003703002003200241c8006a41106a290300370300200220022903503703b8010c010b200020022903703703282001200241c8006a41206a2903003703002005200241c8006a41186a2903003703002003200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b8010b200041086a220120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c080b20024180016a41086a2204200141346a28020036020020022001412c6a29020022063703800120024180016a412c6a200141206a290200370200200241a4016a200141186a2902003702002002419c016a200141106a29020037020020024194016a200141086a2902003702002002200129020037028c01200241b8016a41306a20024180016a41306a280200360200200241b8016a41286a20024180016a41286a290300370300200241b8016a41206a20024180016a41206a290300370300200241b8016a41186a220120024180016a41186a290300370300200241b8016a41106a220520024180016a41106a290300370300200241b8016a41086a22032004290300370300200220063703b801200241c8006a200241b8016a10c48680800002400240200229034822064202520d002001200241c8006a41206a2903003703002005200241c8006a41186a2903003703002003200241c8006a41106a290300370300200220022903503703b8010c010b200020022903703703282001200241c8006a41206a2903003703002005200241c8006a41186a2903003703002003200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b8010b200041086a220120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c070b200241086a41086a2204200141346a28020036020020022001412c6a290200370308200241b8016a41206a200141206a290300370300200241b8016a41186a200141186a290300370300200241b8016a41106a200141106a290300370300200241b8016a41086a200141086a290300370300200220012903003703b8012002200241086a3602e001200241c8006a200241b8016a10b386808000024020042802002204450d00200228020c2101034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b02402001410c6a280200450d00200141106a28020041002802c0a3c68000118080808000000b200141186a21012004417f6a22040d000b0b02402002280208450d00200228020c41002802c0a3c68000118080808000000b02400240200229034822064202520d00200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200220022903503703b801200041086a21010c010b20002002290370370328200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b801200041086a21010b200120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c060b200241186a41086a2204200141346a28020036020020022001412c6a290200370318200241b8016a41206a200141206a290300370300200241b8016a41186a200141186a290300370300200241b8016a41106a200141106a290300370300200241b8016a41086a200141086a290300370300200220012903003703b8012002200241186a3602e001200241c8006a200241b8016a10b986808000024020042802002201450d00200228021c21072001410171210341002104024020014101460d002001417e7121052007210141002104034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b02402001410c6a280200450d00200141106a28020041002802c0a3c68000118080808000000b200141186a21012005200441026a2204470d000b0b2003450d0020072004410c6c6a2201280200450d00200128020441002802c0a3c68000118080808000000b02402002280218450d00200228021c41002802c0a3c68000118080808000000b02400240200229034822064202520d00200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200220022903503703b801200041086a21010c010b20002002290370370328200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b801200041086a21010b200120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c050b200241286a41086a200141386a2802003602002002200141306a29020037032820022001412c6a28020036028001200241b8016a41206a200141206a290300370300200241b8016a41186a200141186a290300370300200241b8016a41106a200141106a290300370300200241b8016a41086a200141086a290300370300200220012903003703b801200220024180016a3602e4012002200241286a3602e001200241c8006a200241b8016a10c38680800002402002280228450d00200228022c41002802c0a3c68000118080808000000b02400240200229034822064202520d00200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200220022903503703b801200041086a21010c010b20002002290370370328200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b801200041086a21010b200120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c040b200241386a41086a200141346a28020036020020022001412c6a290200370338200241b8016a41206a200141206a290300370300200241b8016a41186a200141186a290300370300200241b8016a41106a200141106a290300370300200241b8016a41086a200141086a290300370300200220012903003703b8012002200241386a3602e001200241c8006a200241b8016a10be8680800002402002280238450d00200228023c41002802c0a3c68000118080808000000b02400240200229034822064202520d00200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200220022903503703b801200041086a21010c010b20002002290370370328200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b801200041086a21010b200120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c030b200241b8016a41186a200141c1006a290000370300200241b8016a41106a200141396a290000370300200241b8016a41086a200141316a2900003703002002200141296a2900003703b801200241c8006a41206a200141206a290300370300200241c8006a41186a200141186a290300370300200241c8006a41106a200141106a290300370300200241c8006a41086a200141086a290300370300200220012903003703482002200241b8016a36027020024180016a200241c8006a10c5868080000240024020022d008001410e470d0020004200370308200041206a2101420221060c010b2000200229028001370220200041286a20024180016a41086a290200370200200241ca006a20024193016a2d000022013a0000200220022f00910122043b014820022d0090012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021060b20002006370300200141003a00000c020b200241b8016a41186a200141c1006a290000370300200241b8016a41106a200141396a290000370300200241b8016a41086a200141316a2900003703002002200141296a2900003703b801200241c8006a41206a200141206a290300370300200241c8006a41186a200141186a290300370300200241c8006a41106a200141106a290300370300200241c8006a41086a200141086a290300370300200220012903003703482002200241b8016a36027020024180016a200241c8006a10b7868080000240024020022d008001410e470d0020004200370308200041206a2101420221060c010b2000200229028001370220200041286a20024180016a41086a290200370200200241ca006a20024193016a2d000022013a0000200220022f00910122043b014820022d0090012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021060b20002006370300200141003a00000c010b20012802002104200241b8016a41086a200141346a28020036020020022001412c6a2902003703b801200241c8006a200241b8016a10bc8680800020042004280200417f6a2201360200024020010d00200441086a28020022052004410c6a28020022012802001180808080000002402001280204450d00200541002802c0a3c68000118080808000000b200441046a22012001280200417f6a220136020020010d00200441002802c0a3c68000118080808000000b02400240200229034822064202520d00200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200220022903503703b801200041086a21010c010b20002002290370370328200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b801200041086a21010b200120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000b200241f0016a2480808080000b850702087f017e23808080800041306b2201248080808000200142919fd78da1a1ad84ff003703182001429ceccef7a6c0dedd20370310200142958ed1e593cab9fca47f370328200142e6d0c3af83b9abdfff0037032020014100360204200141106a4120200141046a410441002802e0a1c6800011868080800000024002400240024002400240200041206a2802002202450d002000411c6a2802002103200142919fd78da1a1ad84ff003703182001429ceccef7a6c0dedd2037031020014282fceae49dd9e2861d370328200142de8c84a1ecd0a6d30c370320200141046a200141106a10e288808000024020012802042204418080808078460d0020012802082105200128020c0d022004450d00200541002802c0a3c68000118080808000000b200241b3e6cc194b0d03200241286c2206417f4c0d034100210541002d00fca3c680001a200641002802c8a3c68000118180808000002207450d04200221080240034020062005460d0120032903202109200720056a22042003290300370300200441186a200341186a290300370300200441106a200341106a290300370300200441086a200341086a290300370300200441206a2009370300200541286a2105200341286a21032008417f6a22080d000b0b2002410a4b0d022001200236021020012002ad4220862007ad84370214200141106a10f188808000200141106a10f0888080002001280210450d00200128021441002802c0a3c68000118080808000000b200142919fd78da1a1ad84ff003703182001429ceccef7a6c0dedd20370310200142c2a5b2f6d3b4fb986f370328200142dcd7ddd8f18e8ca1e30037032041002d00fca3c680001a411141002802c8a3c68000118180808000002203450d042003200029030037000020032000290308370008200320002d00103a0010200141106a41202003411141002802e0a1c6800011868080800000200341002802c0a3c6800011808080800000200141306a2480808080000f0b02402004450d00200541002802c0a3c68000118080808000000b2001411c6a420037020020014101360214200141e4ecc28000360210200141e4e7c28000360218200141106a41ececc2800010f680808000000b200741002802c0a3c680001180808080000041dcebc2800041c400200141106a41a0ecc2800041b0ecc28000108981808000000b10ae80808000000b4108200610b280808000000b4101411110b280808000000bf10501047f2380808080004190016b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd20370300200141cc006a2001411041002802c0a1c680001185808080000002400240200128024c2202418080808078460d0020012802502103024020012802544110490d0020012003411010888e808000210402402002450d00200341002802c0a3c68000118080808000000b20040d010c020b2002450d00200341002802c0a3c68000118080808000000b41002102200141003b011e02400240417f4100280284a4c680002203410347200341034b1b2203450d00200341ff017141ff01470d010b200141286a410c6a418282808000360200200141838280800036022c20014104360224200141f5a6c4800036022020012001411e6a3602302001200141206a3602284100280290a1c680002102410028028ca1c6800021034100280280a4c68000210420014184016a4202370200200141fc006a4103360200200141f4006a4116360200200141f0006a41c1e5c28000360200200141e4006a41e8e4c28000ad4280808080900b84370200200141cc006a410c6a41d7e5c28000ad4280808080b0028437020020014180016a200141286a360200200141f0e6c280003602782001410336026c200141003602602001410036025420014281808080800e37024c200341ecf2c08000200441024622041b200141cc006a200241d4f2c0800020041b2802101184808080000020012f011e21020b200141cc006a41f5a6c48000410441002802a0a3c6800011858080800000200141cc006a41106a220341bccdc48000411541002802a0a3c6800011858080800000200141286a41186a200141cc006a41186a290000370300200141286a41106a2003290000370300200141286a41086a200141cc006a41086a2900003703002001200129004c370328200120023b014c200141286a4120200141cc006a410241002802e0a1c68000118680808000000b200042003703082000420037030020014190016a2480808080000b930201027f23808080800041106b2202248080808000200220003602002002200128021441fc94c38000410e200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a2002418c95c38000108d81808000210120022d000c210002400240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010bf51a03017f017e027f23808080800041c0016b2202248080808000024002400240024002400240024002400240024020012d0028417f6a0e09000102030405060708000b200241e4006a200141d0006a290200370200200241dc006a200141c8006a290200370200200241d4006a200141c0006a2902003702002002200141386a29020037024c200141306a2903002103200241206a41086a200141086a290300370300200241206a41106a200141106a290300370300200241206a41186a200141186a290300370300200241206a41206a200141206a29030037030020022001290300370320200220033703102002200241106a36024820024190016a200241206a10b2868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c080b200241e4006a200141d0006a290200370200200241dc006a200141c8006a290200370200200241d4006a200141c0006a290200370200200241f4006a200141e0006a290200370200200241fc006a200141e8006a29020037020020024184016a200141f0006a2902003702002002200141386a29020037024c2002200141d8006a29020037026c200141306a2903002103200241206a41086a200141086a290300370300200241206a41106a200141106a290300370300200241206a41186a200141186a290300370300200241206a41206a200141206a29030037030020022001290300370320200220033703102002200241106a36024820024190016a200241206a10c8868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c070b200241e4006a200141d0006a290200370200200241dc006a200141c8006a290200370200200241d4006a200141c0006a2902003702002002200141386a29020037024c200141306a2903002103200241206a41086a200141086a290300370300200241206a41106a200141106a290300370300200241206a41186a200141186a290300370300200241206a41206a200141206a29030037030020022001290300370320200220033703102002200241106a36024820024190016a200241206a10bd868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c060b200241e4006a200141c1006a290000370200200241dc006a200141396a290000370200200241d4006a200141316a2900003702002002200141296a29000037024c200141c9006a2d00002104200241206a41086a200141086a290300370300200241206a41106a200141106a290300370300200241206a41186a200141186a290300370300200241206a41206a200141206a29030037030020022001290300370320200220043a00102002200241106a36024820024190016a200241206a10c6868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c050b200241e4006a200141d0006a290200370200200241dc006a200141c8006a290200370200200241d4006a200141c0006a2902003702002002200141386a29020037024c200141306a2903002103200241206a41086a200141086a290300370300200241206a41106a200141106a290300370300200241206a41186a200141186a290300370300200241206a41206a200141206a29030037030020022001290300370320200220033703102002200241106a36024820024190016a200241206a10c2868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c040b200241086a200141346a28020036020020022001412c6a29020037030020024190016a41206a200141206a29030037030020024190016a41186a200141186a29030037030020024190016a41106a200141106a29030037030020024190016a41086a200141086a2903003703002002200129030037039001200220023602b801200241206a20024190016a10ba8680800002402002280200450d00200228020441002802c0a3c68000118080808000000b02400240200229032022034202520d0020024190016a41186a200241c0006a29030037030020024190016a41106a200241206a41186a29030037030020024190016a41086a200241206a41106a2903003703002002200229032837039001200041086a21010c010b2000200229034837032820024190016a41186a200241c0006a29030037030020024190016a41106a200241206a41186a29030037030020024190016a41086a200241206a41106a290300370300200041306a200241206a41306a2903003703002002200229032837039001200041086a21010b200120022903900137030020002003370300200141186a20024190016a41186a290300370300200141106a20024190016a41106a290300370300200141086a20024190016a41086a2903003703000c030b200241e4006a200141d0006a290200370200200241dc006a200141c8006a290200370200200241d4006a200141c0006a2902003702002002200141386a29020037024c200141306a2903002103200241206a41086a200141086a290300370300200241206a41106a200141106a290300370300200241206a41186a200141186a290300370300200241206a41206a200141206a29030037030020022001290300370320200220033703102002200241106a36024820024190016a200241206a10b1868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c020b200141306a2903002103200141296a2d00002104200241206a41206a200141206a290300370300200241206a41186a200141186a290300370300200241206a41106a200141106a290300370300200241206a41086a200141086a29030037030020022001290300370320200220043a001f2002200337031020022002411f6a36024c2002200241106a36024820024190016a200241206a10c7868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c010b200141296a2d00002104200141306a2903002103200241206a41206a200141206a290300370300200241206a41186a200141186a290300370300200241206a41106a200141106a290300370300200241206a41086a200141086a2903003703002002200129030037032020022003370310200220043a001f2002200241106a36024c20022002411f6a36024820024190016a200241206a10c1868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000b200241c0016a2480808080000b910602027f027e23808080800041e0036b220124808080800041002d00fca3c680001a024041980241002802c8a3c680001181808080000022020d00410841980210b280808000000b20002903202103200141f0016a41186a200041c0006a290000370300200141f0016a41106a200041386a290000370300200141f0016a41086a200041306a290000370300200141f0016a41306a200041d8006a290000370300200141f0016a41386a200041e0006a290000370300200141f0016a41c0006a200041e8006a290000370300200141f0016a41d0006a200041f8006a290000370300200141f0016a41d8006a20004180016a290000370300200141f0016a41e0006a20004188016a290000370300200120002900283703f0012001200041d0006a290000370398022001200041f0006a2900003703b802200041c8006a2903002104200141f0016a41e8006a20004190016a108d878080002001200437039002200141e8026a41186a200041b8016a290000370300200141e8026a41106a200041b0016a290000370300200141e8026a41086a200041a8016a290000370300200141e8026a41306a200041d0016a290000370300200141e8026a41386a200041d8016a290000370300200141e8026a41c0006a200041e0016a290000370300200141e8026a41d0006a200041f0016a290000370300200141e8026a41d8006a200041f8016a290000370300200141e8026a41e0006a20004180026a290000370300200120002900a0013703e8022001200041c8016a290000370390032001200041e8016a2900003703b003200041c0016a2903002104200141e8026a41e8006a20004188026a108d878080002001200437038803200141f8006a200141f0016a41f80010848e8080001a2001200141e8026a41f80010848e8080002101200241186a200041186a290300370300200241106a200041106a290300370300200241086a200041086a2903003703002002200029030037030020022003370320200241286a200141f8006a41f80010848e8080001a200241a0016a200141f80010848e8080001a200141e0036a2480808080000b840903017f037e047f23808080800041b0016b2201248080808000200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142aac0b0e5d1eaef8d63370378200142b8e2b0fbfb91a8ed827f370370200141206a200141e0006a10e688808000024002400240024002402001290328420020012802201b42017c22024200510d00200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142dca0bb8e8acbf9b3da00370378200142e78ec688edebaee7ba7f3703702001200242004206420010878e808000200141106a200141e0006a10e78880800020012903084200520d01200129030022032001290318420020012802101b7c22042003540d01200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd2037036020014292d189e4a1e58a9fcc00370378200142aa9f83c8cbf687edfa0037037020014190016a200141e0006a10e288808000200128029001210520012802940121062001280298012107200142919fd78da1a1ad84ff00370398012001429ceccef7a6c0dedd2037039001200142c2a6e5f5c0d0a4be463703a801200142fccce3cbd7d3cfff023703a001200141e0006a20014190016a10e5888080000240024020012d00600d00200141a8016a4200370300200141a0016a420037030020014198016a420037030020014200370390010c010b200141a8016a200141f9006a290000370300200141a0016a200141f1006a29000037030020014198016a200141e9006a29000037030020012001290061370390010b200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142addc97bf9599fde7e3003703782001429ad7aad8b5ececacd100370370200141306a200141e0006a10e88880800020012d00404103470d02200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142c2a5b2f6d3b4fb986f370378200142dcd7ddd8f18e8ca1e300370370200141c8006a200141e0006a10e88880800020012d00584103460d03200141e0006a41106a200141c8006a41106a290300370300200141e0006a41086a200141c8006a41086a290300370300200120012903483703600c040b41ece8c2800041ef0041dce9c2800010a181808000000b41c18fc2800041fa0041bc90c2800010a181808000000b200141e0006a41106a200141306a41106a290300370300200141e0006a41086a200141306a41086a290300370300200120012903303703600c010b41ece9c2800041c80041b4eac2800010a181808000000b200042063703282000200437032020002002370318200020012903900137003020002001290360370300200041d8006a4100200720054180808080784622081b360200200041d4006a4108200620081b36020020004100200520081b360250200041386a20014190016a41086a290300370000200041c0006a20014190016a41106a290300370000200041c8006a200141a8016a290300370000200041086a200141e0006a41086a290300370300200041106a200141e0006a41106a290300370300200141b0016a2480808080000bfb0705017f037e017f017e037f23808080800041b0016b2201248080808000200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142aac0b0e5d1eaef8d63370378200142b8e2b0fbfb91a8ed827f370370200141386a200141e0006a10e6888080002001290340210220012903382103200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142aac0b0e5d1eaef8d63370378200142b8e2b0fbfb91a8ed827f370370200141286a200141e0006a10e6888080002001290330210420012802282105200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142dca0bb8e8acbf9b3da00370378200142e78ec688edebaee7ba7f370370200141086a2004420020051b42004206420010878e808000200141186a200141e0006a10e7888080000240024020012903104200520d00200129030822042001290320420020012802181b7c22062004540d00200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd2037036020014282fceae49dd9e2861d370378200142de8c84a1ecd0a6d30c37037020014190016a200141e0006a10e288808000200128029001210520012802940121072001280298012108200142919fd78da1a1ad84ff00370398012001429ceccef7a6c0dedd2037039001200142c6e4a9b1aaa1afebf2003703a801200142fa82b1828b81b8f31e3703a001200141e0006a20014190016a10e5888080000240024020012d00600d00200141a8016a4200370300200141a0016a420037030020014198016a420037030020014200370390010c010b200141a8016a200141f9006a290000370300200141a0016a200141f1006a29000037030020014198016a200141e9006a29000037030020012001290061370390010b200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142c2a5b2f6d3b4fb986f370378200142dcd7ddd8f18e8ca1e300370370200141c8006a200141e0006a10e88880800020012d00584103460d0120002001290348370300200041106a200141c8006a41106a290300370300200041086a200141c8006a41086a290300370300200041d8006a4100200820054180808080784622091b360200200041d4006a4108200720091b36020020004100200520091b36025020004206370328200020063703202000200242002003a71b3703182000200129039001370030200041386a20014190016a41086a290300370000200041c0006a20014190016a41106a290300370000200041c8006a200141a8016a290300370000200141b0016a2480808080000f0b41c18fc2800041fa0041bc90c2800010a181808000000b41ece9c2800041c80041c4eac2800010a181808000000bf61704017f017e017f017e2380808080004190026b22042480808080000240024002400240024002402000280208450d00200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442e9c5fea9cdfbc4d26d3703b80120044286aaece2939beae4653703b001200441286a200441a0016a10e7888080002004290330210520042802282106200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442dca0bb8e8acbf9b3da003703b801200442e78ec688edebaee7ba7f3703b001200441186a200441a0016a10e788808000200442002005420020061b22052004290320420020042802181b7d220720072005561b4206802205370338200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442aac0b0e5d1eaef8d633703b801200442b8e2b0fbfb91a8ed827f3703b001200441086a200441a0016a10e68880800020024101470d04427f200429031042017c22072007501b420120042802081b2005520d010c040b417f4100280284a4c680002202410247200241024b1b2202450d01200241ff017141ff01460d010c020b200420033602a001200441386a200441a0016a10f788808000200429033821050c020b4100280290a1c680002102410028028ca1c6800021034100280280a4c680002106200441d8016a4200370200200441d4016a41e4e7c28000360200200441d0016a4101360200200441c8016a410d360200200441c4016a41c6e8c28000360200200441b8016a41e8e4c28000ad4280808080900b84370200200441ac016a41f8eac28000ad4280808080b00184370200200441f0eac280003602cc01200441003602b401200441003602a80120044281808080b0ce003702a001200441023602c001200341ecf2c08000200641024622061b200441a0016a200241d4f2c0800020061b280210118480808000000b02402001280200450d00200128020441002802c0a3c68000118080808000000b2000280200450d01200028020441002802c0a3c68000118080808000000c010b200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442aac0b0e5d1eaef8d633703b801200442b8e2b0fbfb91a8ed827f3703b001200420053703f001200441a0016a4120200441f0016a410841002802e0a1c6800011868080800000200441a0016a41086a200041086a280200360200200420002902003703a001200441a0016a10f3888080000240200429033842017c220550450d004183ebc2800041c90041ccebc2800010a181808000000b200442919fd78da1a1ad84ff003703f8012004429ceccef7a6c0dedd203703f001200442c2a6e5f5c0d0a4be4637038802200442fccce3cbd7d3cfff0237038002200441a0016a200441f0016a10e5888080000240024020042d00a0010d00200441d8006a4200370300200441d0006a4200370300200441c8006a4200370300200442003703400c010b200441d8006a200441b9016a290000370300200441d0006a200441b1016a290000370300200441c8006a200441a9016a290000370300200420042900a1013703400b200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442958ed1e593cab9fca47f3703b801200442e6d0c3af83b9abdfff003703b0012004200441a0016a412010d0888080002004280204210020042802002102200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442958ed1e593cab9fca47f3703b801200442e6d0c3af83b9abdfff003703b001200441003602f001200441a0016a4120200441f0016a410441002802e0a1c6800011868080800000200441003602bc01200441003602ac01200442013702a00120042000410020021b22003602a801200441f0016a200441c0006a2005200441a0016a4101200041016a2200417f20001b41087410908a80800041002d00fca3c680001a200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442c2a6e5f5c0d0a4be463703b801200442fccce3cbd7d3cfff023703b0010240024002400240412041002802c8a3c68000118180808000002200450d00200020042900f001370000200041186a200441f0016a41186a290000370000200041106a200441f0016a41106a290000370000200041086a200441f0016a41086a290000370000200441a0016a41202000412041002802e0a1c6800011868080800000200041002802c0a3c680001180808080000041002d00fca3c680001a200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442c6e4a9b1aaa1afebf2003703b801200442fa82b1828b81b8f31e3703b001412041002802c8a3c68000118180808000002200450d0120002004290340370000200041186a200441c0006a41186a290300370000200041106a200441c0006a41106a290300370000200041086a200441c0006a41086a290300370000200441a0016a41202000412041002802e0a1c6800011868080800000200041002802c0a3c6800011808080800000200110f08880800010f688808000200442919fd78da1a1ad84ff003703f8012004429ceccef7a6c0dedd203703f001200442c2a6e5f5c0d0a4be4637038802200442fccce3cbd7d3cfff0237038002200441a0016a200441f0016a10e5888080000240024020042d00a0010d0020044184016a4200370200200441fc006a4200370200200441f4006a42003702002004420037026c0c010b20044184016a200441b9016a290000370200200441fc006a200441b1016a290000370200200441f4006a200441a9016a290000370200200420042900a10137026c0b200441e0006a41086a2200200141086a280200360200200420012902002205370360200441cc016a20044188016a280200360200200441c4016a20044180016a290300370200200441bc016a200441f8006a290300370200200441b4016a200441f0006a290300370200200441ac016a2000290300370200200420053702a401200441003602a001200441f0016a41086a200441a0016a10cc86808000200441f4016a41c5003a000020044181848592043602f001200441f0016a10f588808000024020042802a0010d0020042802a401450d00200441a8016a28020041002802c0a3c68000118080808000000b200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442addc97bf9599fde7e3003703b8012004429ad7aad8b5ececacd1003703b001200441f0016a200441a0016a10e888808000024020042d00800222014103460d0020042903f801210520042903f0012107200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442c2a5b2f6d3b4fb986f3703b801200442dcd7ddd8f18e8ca1e3003703b00141002d00fca3c680001a411141002802c8a3c68000118180808000002200450d032000200737000020002005370008200020013a0010200441a0016a41202000411141002802e0a1c6800011868080800000200041002802c0a3c68000118080808000000b200441f0016a10f48880800020042d00800222014103460d0420042903f801210520042903f0012107200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442addc97bf9599fde7e3003703b8012004429ad7aad8b5ececacd1003703b00141002d00fca3c680001a411141002802c8a3c68000118180808000002200450d032000200737000020002005370008200020013a0010200441a0016a41202000411141002802e0a1c6800011868080800000200041002802c0a3c6800011808080800000200441b8016a20013a0000200441b0016a2005370300200441b9016a200428008102360000200441bc016a20044184026a280000360000200420073703a801200441023602a00120044194016a200441a0016a10cc8680800020044190016a41c5003a0000200441818485920436028c012004418c016a10f58880800020042802a0010d0420042802a401450d0420042802a80141002802c0a3c68000118080808000000c040b4101412010b280808000000b4101412010b280808000000b4101411110b280808000000b4101411110b280808000000b20044190026a2480808080000bae0302027f037e23808080800041e0006b2201248080808000410021020240024020004201510d00200142919fd78da1a1ad84ff003703482001429ceccef7a6c0dedd20370340200142e9c5fea9cdfbc4d26d37035820014286aaece2939beae465370350200141306a200141c0006a10e7888080002001290338210320012903302104200142919fd78da1a1ad84ff003703482001429ceccef7a6c0dedd20370340200142aac0b0e5d1eaef8d63370358200142b8e2b0fbfb91a8ed827f370350200141206a200141c0006a10e6888080002001290328210020012802202102200142919fd78da1a1ad84ff003703482001429ceccef7a6c0dedd20370340200142dca0bb8e8acbf9b3da00370358200142e78ec688edebaee7ba7f37035020012000420020021b42004206420010878e808000200141106a200141c0006a10e78880800020012903084200520d01200129030022002001290318420020012802101b7c22052000540d014200200342002004a71b220020057d220320032000561b42055621020b200141e0006a24808080800020020f0b41c18fc2800041fa0041bc90c2800010a181808000000ba42901047f23808080800041206b22012480808080002001410036021020014280808080800137020841002d00fca3c680001a024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240410841002802c8a3c68000118180808000002202450d00200241fcecc280003602002002411536020441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d01200342003700000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410136025c2004200236025820044288808080103703502004200336024c2004428a8080808001370244200441a8c0c38000360240200441ef8080800036021820044100360200200442a5e9e3ab9e929adc2c370308200441106a4293888c8f89fdc6ec9e7f3703002001200128021041016a36021041002d00fca3c680001a410841002802c8a3c68000118180808000002204450d022004411b36020420044191edc280003602002001410136021c2001200436021820014101360214200141146a200141086a10fc8880800041002d00fca3c680001a411041002802c8a3c68000118180808000002202450d03200241acedc28000360200200241eaedc280003602082002413e3602042002410c6a412436020041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d04200342003700000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410236025c2004200236025820044288808080203703502004200336024c2004428b8080808001370244200441fabdc38000360240200441f580808000360218200442e3a4fae3cee3d18d7237030820044100360200200441106a42b8b6d386cdcbfab1a07f37030041002d00fca3c680001a2001200128021041016a360210410841002802c8a3c68000118180808000002202450d052002418eeec280003602002002411536020441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d06200342003700000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410136025c2004200236025820044288808080103703502004200336024c2004428b8080808001370244200441b4bec38000360240200441f580808000360218200442e3a4fae3cee3d18d7237030820044100360200200441106a42b8b6d386cdcbfab1a07f37030041002d00fca3c680001a2001200128021041016a36021041d00041002802c8a3c68000118180808000002204450d07200441a6f1c28000360248200441e5f0c28000360240200441a0f0c28000360238200441dbefc280003602302004419defc28000360228200441dceec28000360220200441e4e7c28000360218200441d1eec28000360210200441e4e7c280003602082004412e360204200441a3eec28000360200200441cc006a41c300360200200441c4006a41c1003602002004413c6a41c500360200200441346a41c5003602002004412c6a413e360200200441246a41c1003602002004411c6a4100360200200441146a410b3602002004410c6a410036020041002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0820034200370000200341186a4200370000200341106a4200370000200341086a42003700000240200128021022022001280208470d00200141086a2002109d86808000200128021021020b200128020c200241e8006c6a220241013a00602002410a36025c20022004360258200242a0808080a0013703502002200336024c2002428a808080800437024420024197c1c38000360240200241f081808000360218200242c4ccab9c81ebb4b8db0037030820024100360200200241106a42a4d2928ecac0faa43237030041002d00fca3c680001a2001200128021041016a360210410841002802c8a3c68000118180808000002202450d09200241e9f1c28000360200200241d80036020441002d00fca3c680001a410141002802c8a3c68000118180808000002203450d0a200341003a00000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441003a00602004410136025c2004200236025820044281808080103703502004200336024c200442988080801037024420044181c0c380003602402004418482808000360218200442c7facddcf584b3fea87f37030820044100360200200441106a42a8dcd6c1f2afcba52b37030041002d00fca3c680001a2001200128021041016a360210410841002802c8a3c68000118180808000002203450d0b200341c1f2c280003602002003411736020441002d00fca3c680001a412041002802c8a3c68000118180808000002202450d0c20024200370000200241186a4200370000200241106a4200370000200241086a42003700000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410136025c20042003360258200442a0808080103703502004200236024c2004428e808080800437024420044190bfc38000360240200441f08180800036021820044100360200200442c4ccab9c81ebb4b8db00370308200441106a42a4d2928ecac0faa4323703002001200128021041016a36021041002d00fca3c680001a410841002802c8a3c68000118180808000002204450d0d20044118360204200441d8f2c280003602002001410136021c2001200436021820014101360214200141146a200141086a10818980800041002d00fca3c680001a41c80041002802c8a3c68000118180808000002204450d0e200441f0f2c28000360200200441aff5c28000360240200441edf4c28000360238200441b2f4c28000360230200441e4e7c280003602282004418df4c28000360220200441cdf3c280003602182004418ff3c28000360210200441e4e7c280003602082004411f360204200441c4006a41073602002004413c6a41c200360200200441346a413b3602002004412c6a4100360200200441246a41253602002004411c6a41c000360200200441146a413e3602002004410c6a410036020041002d00fca3c680001a410441002802c8a3c68000118180808000002203450d0f200341003600000240200128021022022001280208470d00200141086a2002109d86808000200128021021020b200128020c200241e8006c6a220241013a00602002410936025c200220043602582002428480808090013703502002200336024c2002428c808080c000370244200241e1bfc38000360240200241b58080800036021820024100360200200242d7c9cb8fc1cf97db3e370308200241106a42e88488d0c0e3aebc133703002001200128021041016a36021041002d00fca3c680001a410841002802c8a3c68000118180808000002204450d10200441c500360204200441b6f5c280003602002001410136021c2001200436021820014101360214200141146a200141086a109e8880800041002d00fca3c680001a411041002802c8a3c68000118180808000002202450d11200241fbf5c28000360200200241bbf6c28000360208200241c0003602042002410c6a41c70036020041002d00fca3c680001a410141002802c8a3c68000118180808000002203450d12200341003a00000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441003a00602004410236025c2004200236025820044281808080203703502004200336024c2004428b80808010370244200441a9bec380003602402004418582808000360218200442c3d5bbd5b981e38c0337030820044100360200200441106a42c98d9ad7c1aad5e25937030041002d00fca3c680001a2001200128021041016a360210412041002802c8a3c68000118180808000002202450d1320024182f7c280003602002002418df8c28000360218200241e4e7c28000360210200241c7f7c28000360208200241c5003602042002411c6a41d200360200200241146a41003602002002410c6a41c60036020041002d00fca3c680001a410141002802c8a3c68000118180808000002203450d14200341003a00000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410436025c2004200236025820044281808080c0003703502004200336024c2004429380808010370244200441ecc0c3800036024020044186828080003602182004428cc882a2e2dcdde2f00037030820044100360200200441106a42aa8be08180efaa931e37030041002d00fca3c680001a2001200128021041016a360210412841002802c8a3c68000118180808000002204450d15200441dff8c28000360200200441e3fac280003602202004418dfac28000360218200441bbf9c28000360210200441b6f9c28000360208200441d700360204200441246a41d6003602002004411c6a41d600360200200441146a41d2003602002004410c6a410536020041002d00fca3c680001a411041002802c8a3c68000118180808000002203450d1620034200370000200341086a42003700000240200128021022022001280208470d00200141086a2002109d86808000200128021021020b200128020c200241e8006c6a220241013a00602002410536025c2002200436025820024290808080d0003703502002200336024c2002428a8080808002370244200241bdc0c380003602402002418782808000360218200242bcf58bd0d59aa7c9db0037030820024100360200200241106a42e0df9add94b69bd2ff0037030041002d00fca3c680001a2001200128021041016a360210412841002802c8a3c68000118180808000002204450d17200441b9fbc28000360200200441f8fcc28000360220200441b4fcc28000360218200441effbc28000360210200441e4e7c2800036020820044136360204200441246a412c3602002004411c6a41c400360200200441146a41c5003602002004410c6a410036020041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d18200342003700000240200128021022022001280208470d00200141086a2002109d86808000200128021021020b200128020c200241e8006c6a220241013a00602002410536025c2002200436025820024288808080d0003703502002200336024c200242888080808001370244200241c7c0c38000360240200241ef80808000360218200242a5e9e3ab9e929adc2c37030820024100360200200241106a4293888c8f89fdc6ec9e7f37030041002d00fca3c680001a2001200128021041016a360210411041002802c8a3c68000118180808000002202450d19200241a4fdc28000360200200241fcfdc28000360208200241d8003602042002410c6a410936020041002d00fca3c680001a410141002802c8a3c68000118180808000002203450d1a200341003a00000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441003a00602004410236025c2004200236025820044281808080203703502004200336024c2004428b80808010370244200441b2c0c38000360240200441f680808000360218200442b7e2f1ac9bf5d4dc827f37030820044100360200200441106a42f2b3a2a5fbe78f9ac60037030041002d00fca3c680001a2001200128021041016a360210411041002802c8a3c68000118180808000002202450d1b20024185fec28000360200200241d0fec28000360208200241cb003602042002410c6a413a36020041002d00fca3c680001a410141002802c8a3c68000118180808000002203450d1c200341003a00000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441003a00602004410236025c2004200236025820044281808080203703502004200336024c2004428f808080103702442004419abec38000360240200441f68080800036021820044100360200200442b7e2f1ac9bf5d4dc827f370308200441106a42f2b3a2a5fbe78f9ac6003703002001200128021041016a36021041002d00fca3c680001a41c00041002802c8a3c68000118180808000002204450d1d200441a182c38000360238200441d781c380003602302004418981c38000360228200441bc80c38000360220200441f0ffc28000360218200441e4e7c28000360210200441d4ffc28000360208200441ca003602042004418affc280003602002004413c6a412c360200200441346a41ca003602002004412c6a41ce00360200200441246a41cd003602002004411c6a41cc00360200200441146a41003602002004410c6a411c3602002001410836021c2001200436021820014108360214200141146a200141086a108289808000200041086a200141086a41086a28020036020020002001290208370200200041106a4104360200200041f5a6c4800036020c200141206a2480808080000f0b4104410810b280808000000b4101410810b280808000000b4104410810b280808000000b4104411010b280808000000b4101410810b280808000000b4104410810b280808000000b4101410810b280808000000b410441d00010b280808000000b4101412010b280808000000b4104410810b280808000000b4101410110b280808000000b4104410810b280808000000b4101412010b280808000000b4104410810b280808000000b410441c80010b280808000000b4101410410b280808000000b4104410810b280808000000b4104411010b280808000000b4101410110b280808000000b4104412010b280808000000b4101410110b280808000000b4104412810b280808000000b4101411010b280808000000b4104412810b280808000000b4101410810b280808000000b4104411010b280808000000b4101410110b280808000000b4104411010b280808000000b4101410110b280808000000b410441c00010b280808000000ba80901097f41002d00fca3c680001a02400240024002400240024002400240024041e00141002802c8a3c68000118180808000002201450d0041002d00fca3c680001a410841002802c8a3c68000118180808000002202450d012002420637000041002d00fca3c680001a411841002802c8a3c68000118180808000002203450d02200341cd82c38000360200200341ce83c380003602102003418883c380003602082003413b360204200341146a41c8003602002003410c6a41c60036020041002d00fca3c680001a410841002802c8a3c68000118180808000002204450d0320044290ce0037000041002d00fca3c680001a412841002802c8a3c68000118180808000002205450d04200541a384c38000360200200541ab86c38000360220200541e985c38000360218200541a885c38000360210200541e484c38000360208200541c100360204200541246a41283602002005411c6a41c200360200200541146a41c1003602002005410c6a41c40036020041002d00fca3c680001a410441002802c8a3c68000118180808000002206450d052006410a36000041002d00fca3c680001a410841002802c8a3c68000118180808000002207450d06200741e486c380003602002007412236020441002d00fca3c680001a410441002802c8a3c68000118180808000002208450d07200841e40036000041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d08200941353602042009419487c38000360200200141b8016a42e88488d0c0e3aebc13370300200141b0016a42d7c9cb8fc1cf97db3e37030020014180016a42e88488d0c0e3aebc13370300200141f8006a42d7c9cb8fc1cf97db3e370300200141c8006a4293888c8f89fdc6ec9e7f370300200141c0006a42a5e9e3ab9e929adc2c370300200141106a4293888c8f89fdc6ec9e7f370300200142a5e9e3ab9e929adc2c370308200141dc016a4101360200200141d8016a2009360200200141d0016a428480808010370200200141cc016a2008360200200141c8016a4104360200200141c0016a41b580808000360200200141ac016a410d360200200141c987c380003602a801200141a4016a4101360200200141a0016a200736020020014198016a42848080801037020020014194016a200636020020014190016a410436020020014188016a41b580808000360200200141f4006a410e3602002001418687c38000360270200141ec006a4105360200200141e8006a2005360200200141e0006a4288808080d000370200200141dc006a2004360200200141d8006a4108360200200141d0006a41ef808080003602002001413c6a4111360200200141d386c38000360238200141033602342001200336023020014288808080303702282001200236022420014108360220200141ef808080003602182001410d3602042001419684c380003602002000410436020820002001360204200041043602000f0b410841e00110b280808000000b4101410810b280808000000b4104411810b280808000000b4101410810b280808000000b4104412810b280808000000b4101410410b280808000000b4104410810b280808000000b4101410410b280808000000b4104410810b280808000000b860503057f017e027f23808080800041c0006b2201248080808000200141186a41d687c38000410441d7e5c28000411341e4e7c28000410010d882808000200141106a42043702002001420037020820014280808080800137020041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241da87c3800036020020014101360238200120023602302001200241206a36023c200120023602342001200141306a10fa8680800041002d00fca3c680001a20012802002103200128020421022001280208210420012802182105200129021c2106410841002802c8a3c68000118180808000002207450d01200741002903a088c380003702002001410036020820014280808080c000370200200141306a200141a888c38000411310be8b8080002001200141306a41bb88c38000411c10c18b808000200141246a200141d788c38000411210c48b8080002001200128022436020820012001280228220836020020012008200128022c41246c6a36020c20012008360204200141306a200110fb868080002005418080808078460d022001200336020820012002360200200120023602042001200220044105746a36020c200041c4006a200110fa868080002001410b6a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a0000200041d8006a4101360200200041d4006a2007360200200041013602502001200129023037000320002001290000370001200041086a200141076a290000370000200141c0006a2480808080000f0b4108412010b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000be20a03067f017e037f23808080800041c0006b2201248080808000200141206a41e988c38000410541d7e5c28000411341e4e7c28000410010d882808000200141186a42043702002001420037021020014280808080800137020841002d00fca3c680001a0240024002400240024002400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241da87c3800036020020014101360238200120023602302001200241206a36023c20012002360234200141086a200141306a10fa8680800041002d00fca3c680001a20012802082103200128020c2104200128021021052001280220210620012902242107410841002802c8a3c68000118180808000002208450d012008410029039089c3800037020041002d00fca3c680001a41002802c8a3c6800021022001410036021020014280808080c00037020841082002118180808000002209450d02200941002903c8e2c48000370200200141086a410010a086808000200128020c200141086a41086a220a28020041246c6a220241003a00202002411836021c2002419889c3800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200a28020041016a3602002001200129020837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d03200941002903f0e3c480003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241013a00202002411836021c200241b089c3800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0420094100290398e3c480003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241023a00202002411636021c200241c889c3800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d05200941002903f0e1c480003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241033a00202002411436021c200241de89c38000360218200241013602142002200936021020024280808080103702082002428080808080013702002001280238210920012802342102200120012802303602102001200236020820012002200941246c6a41246a3602142001200236020c200141306a200141086a10fb868080002006418080808078460d0620012003360210200120043602082001200436020c2001200420054105746a360214200041c4006a200141086a10fa86808000200141136a200141306a41086a2802003600002000413c6a200737020020002006360238200041013a0000200041d8006a4101360200200041d4006a2008360200200041013602502001200129023037000b20002001290008370001200041086a2001410f6a290000370000200141c0006a2480808080000f0b4108412010b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000bd90201037f23808080800041106b220224808080800002402001280200220328020020032802082204470d0020032004410110ab86808000200328020821040b200328020420046a41fb003a00002003200441016a3602082002200136020c20024180023b010802400240200241086a41f289c38000410b200041186a10d68680800022030d00024020022d0008450d0041002d00fca3c680001a411441002802c8a3c68000118180808000002203450d022003420037020c2003410d3602000c010b200241086a41fd89c38000410b200010d38680800022030d0002402002280208220341ff01710d0020034180fe0371450d000240200228020c280200220328020020032802082204470d0020032004410110ab86808000200328020821040b200328020420046a41fd003a00002003200441016a3602080b410021030b200241106a24808080800020030f0b4104411410b280808000000b8a0c02077f027e23808080800041a0026b220224808080800002400240024002400240024002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020020042d00000e03010504020b200041053a00100c050b2001200128020441016a22033602040240200320012802084b0d0041002d00fca3c680001a41980241002802c8a3c68000118180808000002205450d02200241086a2001108f8d80800002402002280290022203418080808078460d002005200241086a41880210848e80800022064194026a200241086a4194026a280200360200200620022902940237028c02200620033602880220012001280204417f6a3602040240200128020022012802042203450d0020012003417f6a3602042001200128020041016a3602000b024020064198016a2802002207450d0020064194016a280200220821014100210303402008200341146c6a21040240024002400240024020012d00000e0400010102040b200141086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200341016a2103200141146a21012007417f6a22070d000b0b0240200628029001450d0020062802940141002802c0a3c68000118080808000000b024020064190026a2802002207450d00200628028c02220821014100210303402008200341146c6a21040240024002400240024020012d00000e0400010102040b200141086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200341016a2103200141146a21012007417f6a22070d000b0b200628028802450d002006418c026a28020041002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000b200041053a00100c040b200041053a00100c030b410841980210b280808000000b0240200128020022012802042203450d0020012003417f6a36020420012001280200220441016a36020020042d00004101470d0020034109490d002001200341776a22073602042001200441096a36020020074108490d002004290001210920012003416f6a22073602042001200441116a3602002007450d002004290009210a20012003416e6a3602042001200441126a36020020042d0011220141034f0d00200020013a00102000200a370308200020093703000c020b200041053a00100c010b2001200128020441016a22033602040240200320012802084b0d0041002d00fca3c680001a41980241002802c8a3c68000118180808000002205450d02200241086a2001108f8d80800002402002280290022203418080808078460d002005200241086a41880210848e80800022064194026a200241086a4194026a280200360200200620022902940237028c02200620033602880220012001280204417f6a3602040240200128020022012802042203450d0020012003417f6a3602042001200128020041016a3602000b024020064198016a2802002207450d0020064194016a280200220821014100210303402008200341146c6a21040240024002400240024020012d00000e0400010102040b200141086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200341016a2103200141146a21012007417f6a22070d000b0b0240200628029001450d0020062802940141002802c0a3c68000118080808000000b024020064190026a2802002207450d00200628028c02220821014100210303402008200341146c6a21040240024002400240024020012d00000e0400010102040b200141086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200341016a2103200141146a21012007417f6a22070d000b0b200628028802450d002006418c026a28020041002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000b200041053a00100b200241a0026a2480808080000f0b410841980210b280808000000bb60702047f017e23808080800041106b2202248080808000024002400240024020002d0010417d6a41ff0171220341016a410320034102491b417f6a0e03000102030b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41003a00002001200341016a22033602082000280200210002402001280200220420036b411f4b0d0020012003412010b18280800020012802002104200128020821030b200128020420036a22052000290000370000200541186a200041186a290000370000200541106a200041106a290000370000200541086a200041086a2900003700002001200341206a2203360208200029032021060240200420036b41074b0d0020012003410810b182808000200128020821030b2001200341086a360208200128020420036a2006370000200041286a200110dd8c808000200041a0016a200110dd8c808000200241046a1095888080002002280208210502402001280200200128020822006b200228020c22034f0d0020012000200310b182808000200128020821000b200128020420006a2005200310848e8080001a2001200020036a3602082002280204450d02200541002802c0a3c68000118080808000000c020b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41013a00002001200341016a22033602082000280200210002402001280200220420036b411f4b0d0020012003412010b18280800020012802002104200128020821030b200128020420036a22052000290000370000200541186a200041186a290000370000200541106a200041106a290000370000200541086a200041086a2900003700002001200341206a2203360208200029032021060240200420036b41074b0d0020012003410810b182808000200128020821030b2001200341086a360208200128020420036a2006370000200041286a200110dd8c808000200041a0016a200110dd8c808000200241046a1095888080002002280208210502402001280200200128020822006b200228020c22034f0d0020012000200310b182808000200128020821000b200128020420006a2005200310848e8080001a2001200020036a3602082002280204450d01200541002802c0a3c68000118080808000000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41023a00002000200110b08c8080000b200241106a2480808080000b830402037f017e4101210141002102024002400240024020002d0010417d6a41ff0171220341016a410320034102491b417f6a0e03000102030b410121034101210102402000280200220241c8006a290300220442c000540d0041022101200442808001540d00410421012004428080808004540d004109200479a74103766b21010b20024198016a28020021000240200241c0016a290300220442c000540d0041022103200442808001540d00410421032004428080808004540d004109200479a74103766b21030b417f417f417f200041146c20016a41046a220020002001491b220041286a220120012000491b2200417f20024190026a28020041146c20036a41046a220220022003491b6a220220022000491b21020c020b02402000280200220241c8006a290300220442c000540d0041022101200442808001540d00410421012004428080808004540d004109200479a74103766b21010b20024198016a2802002103410121000240200241c0016a290300220442c000540d0041022100200442808001540d00410421002004428080808004540d004109200479a74103766b21000b417f417f417f200341146c20016a41046a220320032001491b220341286a220120012003491b2203417f20024190026a28020041146c20006a41046a220220022000491b6a220220022003491b21020c010b411221020b200241016a0b9c0601077f23808080800041d0006b2203248080808000200220016b220441286e210502400240024020022001460d0002400240200441d8ffffff794b0d0020054105742202417f4c0d004100210641002d00fca3c680001a200241002802c8a3c68000118180808000002207450d012005410371210802402005417f6a4103490d00200541fcffff3f712109410021062007210220012104034020022004290000370000200241186a200441186a290000370000200241106a200441106a290000370000200241086a200441086a290000370000200241386a200441c0006a290000370000200241306a200441386a290000370000200241286a200441306a290000370000200241206a200441286a290000370000200241d8006a200441e8006a290000370000200241d0006a200441e0006a290000370000200241c8006a200441d8006a290000370000200241c0006a200441d0006a290000370000200241e0006a200441f8006a290000370000200241e8006a20044180016a290000370000200241f0006a20044188016a290000370000200241f8006a20044190016a29000037000020024180016a2102200441a0016a21042009200641046a2206470d000b0b02402008450d002001200641286c6a2102200720064105746a2104034020042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a290000370000200241286a2102200441206a21042008417f6a22080d000b0b20072005200341cf006a10dc8b80800041002d00fca3c680001a41e80241002802c8a3c68000118180808000002202450d04200241003b01e602200241003602e002200341003602102003200236020c20034100360214200341023a00282003200736021c20032007360218200320053602202003200720054105746a3602242003410c6a200341186a200341146a10c285808000200020032802143602082000200329020c3702000c030b10ae80808000000b4101200210b280808000000b20004100360208200041003602000b200341d0006a2480808080000f0b410441e80210b280808000000bbe0905017f017e017f017e017f23808080800041c0026b2204248080808000024002400240024020014180016a2d00004102460d00200441c8006a200141f0006a290000370300200441c0006a200141e8006a290000370300200441386a200141e0006a29000037030020042001290058370330200141f8006a290300210520044180026a200441306a10fa8780800041012106024002400240024020042802b40220042802b80272450d0020042903800222072005510d014103410220072005561b21060b410021080c010b2004200542017c22053703800220044188016a41386a20044180026a41386a29030037030020044188016a41306a20044180026a41306a29030037030020044188016a41286a20044180026a41286a29030037030020044188016a41206a20044180026a41206a29030037030020044188016a41186a20044180026a41186a29030037030020044188016a41106a20044180026a41106a29030037030020044188016a41086a20044180026a41086a2903003703002004200537038801200441306a20044188016a10fe8780800002402002200310d388808000220841ff01714102460d0020084180feff077141087621060c010b200441306a20012002200310dd89808000220841ff01714102460d0120084180feff077141087621060b200020083a000820004203370300200041096a20063b00000c030b200441e0016a41186a200441306a41186a290300370300200441e0016a41106a200441306a41106a290300370300200441e0016a41086a200441306a41086a290300370300200420042903303703e001410121060c010b02402002200310d388808000220341ff01714102460d00200020033a000820004203370300200041096a20034108763b00000c020b4100210602400240024002400240200128020041736a2203410220034104491b0e03000102050b200141086a108a8a80800022034180feff077141087621080c020b4100210341002108200141186a2d00004104470d02200128020810b087808000000b200110ea8780800022034180feff077141087621080b200341ff01714102460d010b200020033a000820004203370300200041096a20083b00000c010b200441186a200441e0016a41086a290300370000200441206a200441e0016a41106a290300370000200441286a200441e0016a41186a290300370000200420063a000f200420042903e00137001020044188016a200141d80010848e8080001a20044180026a2004410f6a10f289808000200441d0006a20044188016a20044180026a10f38980800020044188016a41086a200441d0006a41086a2203200441d0006a20042903504202511b220141086a29030037030020044188016a41106a200141106a29030037030020044188016a41186a200141186a2903003703002004200129030037038801200220044188016a10cd88808000200041306a200441d0006a41306a290300370300200041286a200441d0006a41286a290300370300200041206a200441d0006a41206a290300370300200041186a200441d0006a41186a290300370300200041106a200441d0006a41106a290300370300200041086a2003290300370300200020042903503703000c010b200110bf878080000b200441c0026a2480808080000bef0901057f0240024002400240024002402000280200220141736a2202410220024104491b0e03010203000b20002d00084106470d042000410c6a280200450d04200041106a28020021030c030b0240024002400240024002400240024020002d0008417f6a0e0a010b0203040506070b0b000b2000410c6a280200450d0a200041106a28020021030c090b2000410c6a280200450d09200041106a28020021030c080b2000410c6a280200450d08200041106a28020021030c070b2000410c6a280200450d07200041106a28020021030c060b200041106a28020021030240200041146a2802002201450d0020032102034002402002280200450d00200241046a28020041002802c0a3c68000118080808000000b02402002410c6a280200450d00200241106a28020041002802c0a3c68000118080808000000b200241186a21022001417f6a22010d000b0b200028020c450d060c050b200041106a28020021030240200041146a2802002202450d002002410171210441002101024020024101460d002002417e7121052003210241002101034002402002280200450d00200241046a28020041002802c0a3c68000118080808000000b02402002410c6a280200450d00200241106a28020041002802c0a3c68000118080808000000b200241186a21022005200141026a2201470d000b0b2004450d0020032001410c6c6a2202280200450d00200228020441002802c0a3c68000118080808000000b200028020c0d040c050b200041106a280200450d04200041146a28020021030c030b2000410c6a280200450d03200041106a28020021030c020b200041186a2d0000417d6a41ff017141014b0d020240200028020822034198016a2802002205450d0020034194016a280200220421004100210203402004200241146c6a21010240024002400240024020002d00000e0400010102040b200041086a21010c020b200141086a21010c010b200141046a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200241016a2102200041146a21002005417f6a22050d000b0b0240200328029001450d0020032802940141002802c0a3c68000118080808000000b024020034190026a2802002205450d002003418c026a280200220421004100210203402004200241146c6a21010240024002400240024020002d00000e0400010102040b200041086a21010c020b200141086a21010c010b200141046a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200241016a2102200041146a21002005417f6a22050d000b0b200328028802450d01200328028c0241002802c0a3c68000118080808000000c010b024002400240024002400240024002402001417e6a0e06000102030405090b200041046a21000c050b02402000280210450d00200041146a28020041002802c0a3c68000118080808000000b20002802042202418080808078460d07200041046a21000c050b02402000280204450d00200041086a28020041002802c0a3c68000118080808000000b200041106a21000c030b200041046a21000c020b200041046a21000c010b024002400240024020002d00040e0400010203070b2000410c6a21000c030b2000410c6a21000c020b2000410c6a21000c010b200041086a21000b200028020021020b2002450d01200028020421030b200341002802c0a3c68000118080808000000f0b0bca0704067f017e017f017e2380808080004180016b22052480808080000240024020014180016a2d00004102460d002000200141f8006a200141d8006a20012003200410da888080000c010b200541d0006a20012003200410d688808000024020052802602203418080808078460d00200541086a2206200541d0006a41086a22072903003703002005200529035037030020052802642108200528026c21092005280270210a2005290378210b200528026821042005280274210c200541d0006a2002200110f989808000024020052802602201418080808078460d00200541106a41086a2007290300220d370300200541206a411c6a200541d0006a411c6a290200370200200541206a41246a200541d0006a41246a290200370200200541206a412c6a200541d0006a412c6a280200360200200541206a41086a200d370300200520052902643702342005200529035037032020052001360230200720062903003703002005200b3703782005200c3602742005200a3602702005200936026c200520043602682005200836026420052003360260200520052903003703502000200541d0006a200541206a10fd848080000c020b200520052d005222013a0012200520052f015022023b0110200041026a20013a0000200020023b0100200041808080807836021002402004450d002004410171210241002100024020044101460d002004417e7121042008210141002100034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b02402001410c6a280200450d00200141106a28020041002802c0a3c68000118080808000000b200141186a21012004200041026a2200470d000b0b2002450d0020082000410c6c6a2201280200450d00200128020441002802c0a3c68000118080808000000b02402003450d00200841002802c0a3c68000118080808000000b0240200c450d00200c4101712103410021000240200c4101460d00200c417e712104200a210141002100034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b02402001410c6a280200450d00200141106a28020041002802c0a3c68000118080808000000b200141186a21012004200041026a2200470d000b0b2003450d00200a2000410c6c6a2201280200450d00200128020441002802c0a3c68000118080808000000b2009450d01200a41002802c0a3c68000118080808000000c010b200520052d005222013a0012200520052f015022043b0110200041026a20013a0000200020043b010020004180808080783602100b20054180016a2480808080000bcb0601057f23808080800041106b22032480808080000240024002402001280204220441feffffff074b0d00200141086a2802000d02200320012802001180808080000002402001280204450d0041908cc38000108781808000000b2001417f36020420012802080d01200141013602082001410c6a2003290200370200200141146a200341086a290200370200410021040c020b41a08cc38000108881808000000b0240200141186a2802002205450d00200141146a28020021040340200428020022062006280200417f6a2207360200024020070d00200641046a22072007280200417f6a220736020020070d00200641002802c0a3c68000118080808000000b200441046a21042005417f6a22050d000b0b02402001280210450d00200128021441002802c0a3c68000118080808000000b200141013602082001410c6a2003290200370200200141146a200341086a2902003702002001200128020441016a2204360204200441ffffffff07490d0041808cc38000108881808000000b2001200441016a36020402400240024002402001410c6a280200220441feffffff074b0d0002400240200141186a280200450d002000200210ab878080000c010b20040d042001417f36020c2002280250280200210641002d00fca3c680001a411041002802c8a3c68000118180808000002204450d022004200636020c2004410036020820044281808080103702000240200128021822062001280210470d00200141106a2006109b86808000200128021821060b200141146a28020020064102746a20043602002001200128021841016a3602182001200128020c41016a36020c2000200210ab87808000200128020c0d032001417f36020c02400240200128021822040d00410021040c010b20012004417f6a2204360218200128021420044102746a28020022042004280200417f6a2206360200024020060d00200441046a22062006280200417f6a220636020020060d00200441002802c0a3c68000118080808000000b200128020c41016a21040b2001200436020c0b20012001280204417f6a360204200341106a2480808080000f0b41888bc38000108881808000000b4104411010b280808000000b41ec8fc38000108781808000000b41f88ac38000108781808000000bb60e02087f017e2380808080004190016b22032480808080000240024002402001280204220441feffffff074b0d00200141086a2802000d02200341d0006a20012802001180808080000002402001280204450d0041908cc38000108781808000000b2001417f36020420012802080d01200141013602082001410c6a2003290250370200200141146a200341d8006a290200370200410021040c020b41a08cc38000108881808000000b0240200141186a2802002205450d00200141146a28020021040340200428020022062006280200417f6a2207360200024020070d00200641046a22072007280200417f6a220736020020070d00200641002802c0a3c68000118080808000000b200441046a21042005417f6a22050d000b0b02402001280210450d00200128021441002802c0a3c68000118080808000000b200141013602082001410c6a2003290250370200200141146a200341d8006a2902003702002001200128020441016a2204360204200441ffffffff07490d0041808cc38000108881808000000b2001200441016a36020402400240024002402001410c6a280200220441feffffff074b0d000240024002400240200141186a280200450d00024020022d00382206417d6a41ff0171220441016a410320044102491b22044103460d002004417f6a0e020202020b200341186a41286a22042002290328370300200341186a41306a2205200241306a290300370300200341d0006a413c6a2002413c6a2800003600002003200228003936008901200341186a41086a2207200241086a290300370300200341186a41106a2208200241106a290300370300200341186a41186a2209200241186a290300370300200341186a41206a220a200241206a29030037030020032002290300370318200341d0006a41086a2007290300370300200341d0006a41106a2008290300370300200341d0006a41186a2009290300370300200341d0006a41206a200a290300370300200341d0006a41286a2004290300370300200341d0006a41306a200529030037030020032003290318370350200320063a008801200341046a200341d0006a10b6868080000240024020032d0004410e470d0020004200370308200041206a21044202210b0c010b20002003290204370220200041286a200341046a41086a290200370200200341d2006a200341176a2d000022043a0000200320032f001522063b015020032d00142102200041336a20043a0000200041316a20063b0000200041306a20023a0000200041186a21044200210b0b2000200b370300200441003a00000c030b20040d062001417f36020c2002280240280200210641002d00fca3c680001a411041002802c8a3c68000118180808000002204450d042004200636020c2004410036020820044281808080103702000240200128021822062001280210470d00200141106a2006109b86808000200128021821060b200141146a28020020064102746a20043602002001200128021841016a3602182001200128020c41016a36020c20022d00382206417d6a41ff0171220441016a410320044102491b22044103460d012004417f6a0e020000000b00000b200341186a41286a22042002290328370300200341186a41306a2205200241306a290300370300200341d0006a413c6a2002413c6a2800003600002003200228003936008901200341186a41086a2207200241086a290300370300200341186a41106a2208200241106a290300370300200341186a41186a2209200241186a290300370300200341186a41206a220a200241206a29030037030020032002290300370318200341d0006a41086a2007290300370300200341d0006a41106a2008290300370300200341d0006a41186a2009290300370300200341d0006a41206a200a290300370300200341d0006a41286a2004290300370300200341d0006a41306a200529030037030020032003290318370350200320063a008801200341046a200341d0006a10b6868080000240024020032d0004410e470d0020004200370308200041206a21064202210b0c010b20002003290204370220200041286a200341046a41086a290200370200200341d2006a200341176a2d000022043a0000200320032f001522063b015020032d00142102200041336a20043a0000200041316a20063b0000200041306a20023a0000200041186a21064200210b0b2000200b37030041002104200641003a0000200128020c0d032001417f36020c024020012802182206450d0020012006417f6a2204360218200128021420044102746a28020022042004280200417f6a2206360200024020060d00200441046a22062006280200417f6a220636020020060d00200441002802c0a3c68000118080808000000b200128020c41016a21040b2001200436020c0b20012001280204417f6a36020420034190016a2480808080000f0b41888bc38000108881808000000b4104411010b280808000000b41ec8fc38000108781808000000b41f88ac38000108781808000000bcc0601057f23808080800041106b22032480808080000240024002402001280204220441feffffff074b0d00200141086a2802000d02200320012802001180808080000002402001280204450d0041908cc38000108781808000000b2001417f36020420012802080d01200141013602082001410c6a2003290200370200200141146a200341086a290200370200410021040c020b41a08cc38000108881808000000b0240200141186a2802002205450d00200141146a28020021040340200428020022062006280200417f6a2207360200024020070d00200641046a22072007280200417f6a220736020020070d00200641002802c0a3c68000118080808000000b200441046a21042005417f6a22050d000b0b02402001280210450d00200128021441002802c0a3c68000118080808000000b200141013602082001410c6a2003290200370200200141146a200341086a2902003702002001200128020441016a2204360204200441ffffffff07490d0041808cc38000108881808000000b2001200441016a36020402400240024002402001410c6a280200220441feffffff074b0d0002400240200141186a280200450d002000200210c4878080000c010b20040d042001417f36020c200228028001280200210641002d00fca3c680001a411041002802c8a3c68000118180808000002204450d022004200636020c2004410036020820044281808080103702000240200128021822062001280210470d00200141106a2006109b86808000200128021821060b200141146a28020020064102746a20043602002001200128021841016a3602182001200128020c41016a36020c2000200210c487808000200128020c0d032001417f36020c02400240200128021822040d00410021040c010b20012004417f6a2204360218200128021420044102746a28020022042004280200417f6a2206360200024020060d00200441046a22062006280200417f6a220636020020060d00200441002802c0a3c68000118080808000000b200128020c41016a21040b2001200436020c0b20012001280204417f6a360204200341106a2480808080000f0b41888bc38000108881808000000b4104411010b280808000000b41ec8fc38000108781808000000b41f88ac38000108781808000000bf21e02057f017e23808080800041a0016b220224808080800002400240024002400240024002400240024002400240024002402001280200417f6a0e0c000102030405060708090a0b000b2001280258210141092103024010fa81808000220441fe014b0d002002200441016a36027041b1e3c080004113200241f0006a410441002802e0a1c680001186808080000041002802e8a1c680001188808080000041002802b0a1c6800011888080800000200241d8006a10fb81808000410e21030b20012001280200417f6a2205360200024020050d00200141086a28020022062001410c6a28020022052802001180808080000002402005280204450d00200641002802c0a3c68000118080808000000b200141046a22052005280200417f6a220536020020050d00200141002802c0a3c68000118080808000000b02400240200441fe014b0d0020004200370308200041206a2101420221070c010b200020033a0020200041216a41003a0000200041186a2101420021070b20002007370300200141003a00000c0b0b200141086a28020021032001280204210420024190016a200141f8006a29030037030020024188016a200141f0006a29030037030020024180016a200141e8006a290300370300200241f0006a41086a200141e0006a29030037030020022001290358370370200241d8006a200241f0006a10b48680800002402004450d00200341002802c0a3c68000118080808000000b0240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241e0006a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c0a0b200241086a41086a200141186a2802003602002002200129021037030820012802582104200241d8006a41086a2001410c6a280200360200200220012902043703582002200241086a360264200241f0006a200241d8006a10c98680800002402002280208450d00200228020c41002802c0a3c68000118080808000000b20042004280200417f6a2201360200024020010d00200441086a28020022032004410c6a28020022012802001180808080000002402001280204450d00200341002802c0a3c68000118080808000000b200441046a22012001280200417f6a220136020020010d00200441002802c0a3c68000118080808000000b0240024020022d0070410e470d0020004200370308200041206a2101420221070c010b20002002290270370220200041286a200241f8006a290200370200200241da006a20024183016a2d000022013a0000200220022f00810122043b015820022d0080012103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c090b200241186a41086a2001410c6a280200360200200241286a41086a200141186a280200360200200220012902043703182002200129021037032820024190016a200141f8006a290300370300200241f0006a41186a200141f0006a29030037030020024180016a200141e8006a290300370300200241f0006a41086a200141e0006a290300370300200220012903583703702002200241286a36029c012002200241186a36029801200241d8006a200241f0006a10bf8680800002402002280228450d00200228022c41002802c0a3c68000118080808000000b02402002280218450d00200228021c41002802c0a3c68000118080808000000b0240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241e0006a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c080b200241386a41086a2001410c6a2802003602002002200129020437033820024190016a200141f8006a29030037030020024188016a200141f0006a29030037030020024180016a200141e8006a290300370300200241f0006a41086a200141e0006a290300370300200220012903583703702002200241386a36029801200241d8006a200241f0006a10c08680800002402002280238450d00200228023c41002802c0a3c68000118080808000000b0240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241e0006a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c070b200241c8006a41086a2001410c6a2802003602002002200129020437034820024190016a200141f8006a29030037030020024188016a200141f0006a29030037030020024180016a200141e8006a290300370300200241f0006a41086a200141e0006a290300370300200220012903583703702002200241c8006a36029801200241d8006a200241f0006a10b88680800002402002280248450d00200228024c41002802c0a3c68000118080808000000b0240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241e0006a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c060b200241f0006a41206a200141f8006a290200370300200241f0006a41186a200141f0006a29020037030020024180016a200141e8006a290200370300200241f0006a41086a200141e0006a29020037030020022001290258370370200241d8006a200241f0006a200141046a10b6888080000240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241d8006a41086a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c050b2001280258210141092103024010fa81808000220441fe014b0d002002200441016a36027041b1e3c080004113200241f0006a410441002802e0a1c680001186808080000041002802e8a1c680001188808080000041002802b0a1c6800011888080800000200241d8006a10fb81808000410e21030b20012001280200417f6a2205360200024020050d00200141086a28020022062001410c6a28020022052802001180808080000002402005280204450d00200641002802c0a3c68000118080808000000b200141046a22052005280200417f6a220536020020050d00200141002802c0a3c68000118080808000000b02400240200441fe014b0d0020004200370308200041206a2101420221070c010b200020033a0020200041216a41003a0000200041186a2101420021070b20002007370300200141003a00000c040b2001280258210141092103024010fa81808000220441fe014b0d002002200441016a36027041b1e3c080004113200241f0006a410441002802e0a1c680001186808080000041002802e8a1c680001188808080000041002802b0a1c6800011888080800000200241d8006a10fb81808000410e21030b20012001280200417f6a2205360200024020050d00200141086a28020022062001410c6a28020022052802001180808080000002402005280204450d00200641002802c0a3c68000118080808000000b200141046a22052005280200417f6a220536020020050d00200141002802c0a3c68000118080808000000b02400240200441fe014b0d0020004200370308200041206a2101420221070c010b200020033a0020200041216a41003a0000200041186a2101420021070b20002007370300200141003a00000c030b200241f0006a41206a200141f8006a290300370300200241f0006a41186a200141f0006a29030037030020024180016a200141e8006a290300370300200241f0006a41086a200141e0006a29030037030020022001290358370370200241d8006a200241f0006a10b4868080000240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241d8006a41086a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c020b20012802042104200241f0006a41206a200141f8006a290200370300200241f0006a41186a200141f0006a29020037030020024180016a200141e8006a290200370300200241f0006a41086a200141e0006a29020037030020022001290258370370200241d8006a200241f0006a200410b7888080000240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241d8006a41086a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c010b20012802042104200241f0006a41206a200141f8006a290200370300200241f0006a41186a200141f0006a29020037030020024180016a200141e8006a290200370300200241f0006a41086a200141e0006a29020037030020022001290258370370200241d8006a200241f0006a200410b9888080000240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241d8006a41086a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000b200241a0016a2480808080000bcb0601057f23808080800041106b22032480808080000240024002402001280204220441feffffff074b0d00200141086a2802000d02200320012802001180808080000002402001280204450d0041908cc38000108781808000000b2001417f36020420012802080d01200141013602082001410c6a2003290200370200200141146a200341086a290200370200410021040c020b41a08cc38000108881808000000b0240200141186a2802002205450d00200141146a28020021040340200428020022062006280200417f6a2207360200024020070d00200641046a22072007280200417f6a220736020020070d00200641002802c0a3c68000118080808000000b200441046a21042005417f6a22050d000b0b02402001280210450d00200128021441002802c0a3c68000118080808000000b200141013602082001410c6a2003290200370200200141146a200341086a2902003702002001200128020441016a2204360204200441ffffffff07490d0041808cc38000108881808000000b2001200441016a36020402400240024002402001410c6a280200220441feffffff074b0d0002400240200141186a280200450d002000200210af878080000c010b20040d042001417f36020c2002280278280200210641002d00fca3c680001a411041002802c8a3c68000118180808000002204450d022004200636020c2004410036020820044281808080103702000240200128021822062001280210470d00200141106a2006109b86808000200128021821060b200141146a28020020064102746a20043602002001200128021841016a3602182001200128020c41016a36020c2000200210af87808000200128020c0d032001417f36020c02400240200128021822040d00410021040c010b20012004417f6a2204360218200128021420044102746a28020022042004280200417f6a2206360200024020060d00200441046a22062006280200417f6a220636020020060d00200441002802c0a3c68000118080808000000b200128020c41016a21040b2001200436020c0b20012001280204417f6a360204200341106a2480808080000f0b41888bc38000108881808000000b4104411010b280808000000b41ec8fc38000108781808000000b41f88ac38000108781808000000bd11d03057f047e067f2380808080004180026b220224808080800020012802002103200241386a4208370300200241306a4200370300200242808080801037032820024100360220200242808080808001370318200241023a0010200242043703082002420137030041002d00fca3c680001a02400240024002400240024041800141002802c8a3c68000118180808000002204450d00200241003602f801200220043602f40120024180013602f0012002200241f0016a3602d00102402002200241d0016a10fb898080002205450d0020022802f001450d0620022802f40141002802c0a3c68000118080808000000c060b20022802f401210520022802f0012206418080808078460d0502402003418180808078470d00200020022802f80136020820002005360204200020063602000c050b024002402003418080808078470d00200241d4006a41086a200141086a2802002203360200200241003602542002200128020422013602580c010b200241d4006a2001280204200128020810fc8080800020022802540d03200241dc006a2802002103200228025821010b0240024002400240024002400240024002400240024002400240024002402003417a6a0e020100110b200141b395c38000410710888e8080000d1041002d00fca3c680001a41c00041002802c8a3c68000118180808000002201450d0d200141386a4100290087a3c28000370000200141306a41002900ffa2c28000370000200141286a41002900f7a2c28000370000200141002900efa2c28000370020200141002900cfa2c28000370000200141086a41002900d7a2c28000370000200141106a41002900dfa2c28000370000200141186a41002900e7a2c28000370000200241003602742002410036026c41002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0c200342e2c2b18be6edd8b2f300370000200241083602a801200220033602a401200241083602a001200241003602b401200241003602ac0141002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0b200342e2c2b18be6edd8b2f300370000200241083602cc01200220033602c801200241083602c40141002d00fca3c680001a024041d00041002802c8a3c68000118180808000002203450d00200141086a2900002107200141106a2900002108200141186a29000021092001290000210a20034280809aa6eaafe301370320200341186a2009370000200341106a2008370000200341086a20073700002003200a370000200141206a220441086a2900002107200441106a2900002108200441186a290000210920032004290000370028200341c8006a4280809aa6eaafe301370300200341c0006a2009370000200341386a2008370000200341306a2007370000200141002802c0a3c6800011808080800000200241023602ec01200220033602e801200241023602e401200241f0016a200241e4016a10d28680800020022d00f0014106460d10200241d0016a41086a200241f0016a41086a290200370300200220022902f0013703d001200241f0016a200241ac016a200241c4016a200241d0016a10968d808000024020022d00f0014106460d00200241f0016a10c7878080000b024020022802e401450d0020022802e80141002802c0a3c68000118080808000000b200241dc016a200241b4016a280200360200200220022902ac013702d401200241053a00d001200241f0016a200241ec006a200241a0016a200241d0016a10968d808000024020022d00f0014106460d00200241f0016a10c7878080000b41002d00fca3c680001a410d41002802c8a3c68000118180808000002201450d08200141056a41002900d796c38000370000200141002900d296c380003700002002410d36028c0120022001360288012002410d36028401200241003602a801200241003602a00141002d00fca3c680001a410b41002802c8a3c68000118180808000002201450d07200141076a41002800f989c38000360000200141002900f289c380003700002002410b3602b401200220013602b0012002410b3602ac0141002d00fca3c680001a412041002802c8a3c68000118180808000002201450d06200241c4016a41afa2c2800041002f0194a1c6800010c8878080004101210b20022802c801210c41012104024020022802cc012203450d002003417f4c0d0641002d00fca3c680001a200341002802c8a3c68000118180808000002204450d050b2004200c200310848e808000210d200241e4016a418fa3c2800041002f0194a1c6800010c88780800020022802e801210e024020022802ec012204450d002004417f4c0d0641002d00fca3c680001a200441002802c8a3c6800011818080800000220b450d040b200b200e200410848e808000210b2001411c6a2004360200200141186a200b360200200141146a2004360200200141033a00102001200336020c2001200d36020820012003360204200141033a0000200241fc016a4102360200200241f8016a2001360200200241023602f401200241043a00f001200241d0016a200241a0016a200241ac016a200241f0016a10968d808000024020022d00d0014106460d00200241d0016a10c7878080000b024020022802e401450d00200e41002802c0a3c68000118080808000000b024020022802c401450d00200c41002802c0a3c68000118080808000000b200241f0016a410c6a200241a0016a41086a280200360200200220022902a0013702f401200241053a00f001200241d0016a200241ec006a20024184016a200241f0016a10968d808000024020022d00d0014106460d00200241d0016a10c7878080000b200241c4006a410c6a200241ec006a41086a2802003602002002200229026c370248200241053a00440c020b410841d00010b280808000000b200141ba95c38000410610888e8080000d0f200241003602cc01200241003602c40141002d00fca3c680001a410341002802c8a3c68000118180808000002201450d09200141026a41002d00c295c380003a0000200141002f00c095c380003b0000200241033602ec01200220013602e801200241033602e40141002d00fca3c680001a410341002802c8a3c68000118180808000002201450d08200141026a41002d00c595c380003a0000200141002f00c395c380003b0000200241033602fc01200220013602f801200241033602f401200241033a00f001200241d0016a200241c4016a200241e4016a200241f0016a10968d808000024020022d00d0014106460d00200241d0016a10c7878080000b200241d0006a200241cc016a280200360200200220022902c401370248200241053a00440b41002d00fca3c680001a41800141002802c8a3c68000118180808000002201450d06200220013602f40120024180013602f0012002200241f0016a3602e401200141fb003a0000200241013602f80141800221010240200241c4006a410c6a28020022030d0041012101024020022802f0014101470d00200241f0016a4101410110ab8680800020022802f80121010b20022802f40120016a41fd003a00002002200141016a3602f801410021010b200220013602d00141002101200341002002280248220c1b210e200c410047210f2002200241e4016a3602d401200241c4006a41086a2802002104024003400240024002400240200e450d0002400240200f450d002001450d010b200f0d0441f887c6800010a081808000000b4101210f200c21012004450d022004210302402004410771220c450d0003402003417f6a210320012802bc022101200c417f6a220c0d000b0b200441084f0d010c020b024020022802d001220141ff01710d00024020014180fe0371450d00024020022802d401280200220128020020012802082203470d0020012003410110ab86808000200128020821030b200128020420036a41fd003a00002001200341016a3602080b20022802f401210320022802f0012201418080808078460d05200020022802f8013602082000200336020420002001360200200241c4006a10c7878080000c150b41e493c38000412841ec94c3800010f880808000000b034020012802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022101200341786a22030d000b0b410021044100210c0b024002400240200420012f01ba02490d00034020012802b0012203450d02200c41016a210c20012f01b802210420032101200420032f01ba024f0d000b0b2001210b2004220d41016a21040240200c0d00200b21010c020b200b20044102746a41bc026a280200210141002104200c417f6a2203450d01200c417e6a211002402003410771220c450d0003402003417f6a210320012802bc022101200c417f6a220c0d000b0b20104107490d01034020012802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022101200341786a22030d000c020b0b41e887c6800010a081808000000b200e417f6a210e4100210c200241d0016a200b200d410c6c6a41b4016a200b200d4104746a10d7868080002203450d000b20022802f001450d0020022802f40141002802c0a3c68000118080808000000b200220033602f00141b08cc38000412f200241f0016a41e08cc3800041e096c38000108981808000000b4101200410b280808000000b4101200310b280808000000b10ae80808000000b4104412010b280808000000b4101410b10b280808000000b4101410d10b280808000000b410141800110b280808000000b4101410310b280808000000b4101410310b280808000000b4101410810b280808000000b4101410810b280808000000b410141c00010b280808000000b410141800110b280808000000b200220022802f4013602d00141c695c38000412b200241d0016a41e08cc3800041f096c38000108981808000000b20004180808080783602000b2006450d00200541002802c0a3c68000118080808000000b02402002280218450d00200228021c41002802c0a3c68000118080808000000b02402002280228450d00200228022c41002802c0a3c68000118080808000000b02402002280234450d00200228023841002802c0a3c68000118080808000000b20024180026a2480808080000f0b200220053602f00141b08cc38000412f200241f0016a41e08cc3800041e08dc38000108981808000000ba60201027f23808080800041306b22012480808080000240024002400240024020002d00000e050404010203000b02400240200028020422020d0041002100410021020c010b200120023602242001410036022020012002360214200141003602102001200041086a2802002202360228200120023602182000410c6a2802002102410121000b2001200236022c2001200036021c2001200036020c2001410c6a10aa8d8080000c030b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b2000280204450d01200041086a28020041002802c0a3c68000118080808000000c010b200041046a1091878080002000280204450d00200041086a28020041002802c0a3c68000118080808000000b200141306a2480808080000b840501047f23808080800041c0006b220324808080800041002d00fca3c680001a41002802c8a3c680002104024002400240024002400240200241ffff00712205413f4b0d004101210641012004118180808000002204450d02200420023a00000c010b4102210641022004118180808000002204450d02200420024106742005410876723a00012004200241fc017141027641c000723a00000b200320063602182003200436021420032006360210200341106a2006412010ab8680800020032802142206200328021822046a22022001290000370000200241086a200141086a290000370000200241106a200141106a290000370000200241186a200141186a2900003700002003200441206a22013602182003411c6a2006200110e8838080002003280224220241014d0d022003280220210202402003280210220420016b41014b0d00200341106a2001410210ab868080002003280214210620032802102104200328021821010b200620016a20022f00003b00002003410036023020034280808080103702282003200341286a360234200320063602382003200141026a220536023c200341086a200341346a200141036a41017620056a200341386a41f091c3800010a9898080002003280208210102402004450d00200641002802c0a3c68000118080808000000b20010d0320002003290228370200200041086a200341286a41086a2802003602000240200328021c450d00200241002802c0a3c68000118080808000000b200341c0006a2480808080000f0b4101410110b280808000000b4101410210b280808000000b4102200241e091c38000109581808000000b41b4a1c38000412b200341386a41e0a1c3800041f0a1c38000108981808000000bc10501067f23808080800041b0016b22022480808080002001280204210320012802082104200241003602980120022004360294012002200336029001200241c0006a20024190016a10c58a808000024002400240024020022802742204418080808078470d0020022002280240360280012002419c016a4201370200410121052002410136029401200241848ec380003602900120024188828080003602ac012002200241a8016a36029801200220024180016a3602a80120024184016a20024190016a10b88080800020022802880121060240200228028c012204450d002004417f4c0d0341002d00fca3c680001a200441002802c8a3c68000118180808000002205450d040b20052006200410848e80800021070240200228028401450d00200641002802c0a3c68000118080808000000b024020022802800122052802000d00200541086a280200450d00200528020441002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000002000200436020820002007360204200020043602000c010b200241146a200241c0006a41146a2902003702002002411c6a2205200241c0006a411c6a290200370200200241246a200241c0006a41246a2902003702002002412c6a200241c0006a412c6a2902003702002002200229024c37020c20022002290378370338200220043602342002200228024836020820022002290340370300200210f889808000200041818080807836020002402002280218450d00200528020041002802c0a3c68000118080808000000b02402002280228450d002002412c6a28020041002802c0a3c68000118080808000000b2002280234450d00200228023841002802c0a3c68000118080808000000b02402001280200450d00200341002802c0a3c68000118080808000000b200241b0016a2480808080000f0b10ae80808000000b4101200410b280808000000b4100200042919fd78da1a1ad84ff003700082000429ceccef7a6c0dedd20370000200042dca0bb8e8acbf9b3da00370018200042e78ec688edebaee7ba7f3700100b4000200042919fd78da1a1ad84ff003700082000429ceccef7a6c0dedd20370000200042aac0b0e5d1eaef8d63370018200042b8e2b0fbfb91a8ed827f3700100bae0301047f23808080800041106b2202248080808000200041c0006a210302402001280200200128020822046b411f4b0d0020012004412010b182808000200128020821040b200128020420046a22052003290000370000200541186a200341186a290000370000200541106a200341106a290000370000200541086a200341086a2900003700002001200441206a22033602080240200128020020036b413f4b0d002001200341c00010b182808000200128020821030b2001200341c0006a360208200128020420036a22032000290000370000200341086a200041086a290000370000200341106a200041106a290000370000200341186a200041186a290000370000200341206a200041206a290000370000200341286a200041286a290000370000200341306a200041306a290000370000200341386a200041386a2900003700002002200041e0006a36020c2002410c6a200110c18a808000200041e8006a2d000021030240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20033a0000200241106a2480808080000b890301047f23808080800041206b2204248080808000024002402001417f4c0d0041002d00fca3c680001a2001410574410472220541002802c8a3c68000118180808000002206450d0120044100360214200420063602102004200536020c200420013602182004200441186a36021c2004411c6a2004410c6a10c08a8080000240024020010d0020042802142101200428021021070c010b200141057421062004280214210103400240200428020c20016b411f4b0d002004410c6a2001412010b182808000200428021421010b2004280210220720016a22052000290000370000200541086a200041086a290000370000200541106a200041106a290000370000200541186a200041186a2900003700002004200141206a2201360214200041206a2100200641606a22060d000b0b200428020c2100200220032007200141002802e0a1c680001186808080000002402000450d00200741002802c0a3c68000118080808000000b200441206a2480808080000f0b10ae80808000000b4101200510b280808000000baf0302057f017e23808080800041206b2203248080808000024002402002417f4c0d0041002d00fca3c680001a200241286c410472220441002802c8a3c68000118180808000002205450d0120034100360214200320053602102003200436020c200320023602182003200341186a36021c2003411c6a2003410c6a10c08a80800002402002450d002001200241286c6a21062003280214210203400240200328020c220520026b411f4b0d002003410c6a2002412010b182808000200328020c2105200328021421020b2003280210220720026a22042001290000370000200441186a200141186a290000370000200441106a200141106a290000370000200441086a200141086a2900003700002003200241206a2202360214200141206a29030021080240200520026b41074b0d002003410c6a2002410810b18280800020032802102107200328021421020b200720026a20083700002003200241086a2202360214200141286a22012006470d000b0b2000200329020c370200200041086a2003410c6a41086a280200360200200341206a2480808080000f0b10ae80808000000b4101200410b280808000000bc01404017f017e057f037e23808080800041900d6b2201248080808000200142919fd78da1a1ad84ff003703b80b2001429ceccef7a6c0dedd203703b00b200142d8d2dfcce2f8e593413703c80b200142faa5fa8ea9819ff1bd7f3703c00b200141a0086a200141b00b6a10e08880800002400240024020012903a00822024204510d00200141b00b6a412041002802a0a1c6800011848080800000200141a0056a200141a0086a41086a41f00010848e8080001a200141286a200141a0056a41f00010848e8080001a20024203510d00200120023703980120014198016a41086a200141286a41f00010848e8080001a20024201510d0020014188026a2802002103200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a00820014282fceae49dd9e2861d3703b808200142de8c84a1ecd0a6d30c3703b008200141a0056a200141a0086a10e2888080000240024020012802a0052204418080808078470d00410121050c010b20012802a4052106410021070240200320012802a8054f0d00200142919fd78da1a1ad84ff003703a8052001429ceccef7a6c0dedd203703a005200142c6e4a9b1aaa1afebf2003703b805200142fa82b1828b81b8f31e3703b0052006200341286c6a2103200141a0086a200141a0056a10e5888080000240024020012d00a0080d00200141b8056a4200370300200141b0056a4200370300200141a8056a4200370300200142003703a0050c010b200141b8056a200141b9086a290000370300200141b0056a200141b1086a290000370300200141a8056a200141a9086a290000370300200120012900a1083703a0050b200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a008200142e9c5fea9cdfbc4d26d3703b80820014286aaece2939beae4653703b008200141186a200141a0086a10e7888080002001290320210220012802182105200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a008200142aac0b0e5d1eaef8d633703b808200142b8e2b0fbfb91a8ed827f3703b008200141086a200141a0086a10e688808000200129031021082001280208210720012002420020051b3703e00320012008420020071b3703b00b200141a0086a41ad8ec28000410410a682808000200141a0086a41b18ec28000410b200141e0036a410810a782808000200141a0086a41bc8ec28000410d200141b00b6a410810a782808000200141a0086a41c98ec280004110200141a0056a412010a78280800020014190026a200141a0086a41d00110848e8080001a200141b00b6a41186a200341186a2900002202370300200141b00b6a41106a200341106a2900002208370300200141b00b6a41086a200341086a290000220937030020012003290000220a3703b00b200141a0056a41186a22032002370300200141a0056a41106a2008370300200141a0056a41086a20093703002001200a3703a005200141a0086a200141a0056a10b18180800002400240024020012802a008450d0020014189046a200141a0086a410d6a29000037000020014190046a200141b4086a290000370000200120012900a5083700810420012d00a408210520014198046a200141bc086a41880110848e8080001a200141e0036a41096a200141a0056a41096a290000370000200141e0036a41116a200141a0056a41116a290000370000200141e0036a41186a2003290000370000200120012900a1053700e103200120053a008004200120012d00a0053a00e00320012f01d803210320012d00da032105200141b00b6a20014190026a41c80110848e8080001a200120053a00fa0c200120033b01f80c200141a0086a200141e0016a200141e0036a200141b00b6a10c48a80800020012802a008450d010b410021070c010b200141a0056a200141a4086a41800310848e8080001a41012107200141a0086a41016a200141a0056a41a08ec38000411310c38a808000200141b80b6a200141a0086a41096a290000370300200141c00b6a200141a0086a41116a290000370300200141c80b6a200141b9086a290000370300200120012900a1083703b00b0b41002105034020014190026a20056a220341003a0000200341016a41003a0000200341026a41003a0000200341036a41003a0000200341046a41003a0000200541056a220541c801470d000b0b02402004450d00200641002802c0a3c68000118080808000000b20074521052007450d002001290398014200520d00200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a008200142958ed1e593cab9fca47f3703b808200142e6d0c3af83b9abdfff003703b0082001200141a0086a412010d08880800020012001280204410020012802001b3602880d20014190026a200141880d6a10f98780800002400240200128029802220341ff014b0d0002402003200128029002470d0020014190026a200310a18680800020012802980221030b20012802940220034105746a220520012903b00b370000200541086a200141b00b6a41086a290300370000200541106a200141b00b6a41106a290300370000200541186a200141b00b6a41186a2903003700002001200341016a36029802200141880d6a20014190026a1080888080000c010b200120012802880d41016a36028c0d41002d00fca3c680001a412041002802c8a3c68000118180808000002203450d03200320012903b00b370000200341186a200141b00b6a41186a290300370000200341106a200141b00b6a41106a290300370000200341086a200141b00b6a41086a290300370000200141013602a805200120033602a405200141013602a0052001418c0d6a200141a0056a108188808000200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a008200142958ed1e593cab9fca47f3703b808200142e6d0c3af83b9abdfff003703b0082001200128028c0d3602e003200141a0086a4120200141e0036a410441002802e0a1c68000118680808000000b0240200128029002450d0020012802940241002802c0a3c68000118080808000000b410021050b200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a0082001428fa194fdfdd0d0e2c5003703b808200142d0effddeadf1b688773703b008410121074100210441002d00fca3c680001a4101412120051b220641002802c8a3c68000118180808000002203450d02024020050d00200320012903b00b370001200341196a200141c80b6a290300370000200341116a200141c00b6a290300370000200341096a200141b80b6a29030037000041212107410121040b200320043a0000200141a0086a41202003200741002802e0a1c6800011868080800000200341002802c0a3c68000118080808000000b200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a008200142bdff9adbcf84adb29e7f3703b80820014283c69cb2f58af8c40f3703b008200141a0086a412041002802a0a1c6800011848080800000200141900d6a2480808080000f0b4101412010b280808000000b4101200610b280808000000bbb0d04037f017e037f057e23808080800041b0026b2202248080808000200242919fd78da1a1ad84ff003703282002429ceccef7a6c0dedd20370320200242d8d2dfcce2f8e59341370338200242faa5fa8ea9819ff1bd7f37033020024190016a200241206a10e08880800002402002290390014204520d0020024190026a10ee8880800020022802940221030240024020022802980222040d00420321050c010b200441146c210620032107024002400340024020072d00000d00200741016a280000210820022007410c6a29020037027c200841c28289aa04470d0020024190016a200241fc006a10ae8c80800020022903900122054203520d020b200741146a21072006416c6a22060d000b420321050c010b2002290398012109200241206a200241a0016a41d80010848e8080001a200229038002210a20022903f801210b0b200321074100210603402003200641146c6a21080240024002400240024020072d00000e0400010102040b200741086a21080c020b200841086a21080c010b200841046a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200641016a2106200741146a21072004417f6a22040d000b0b0240200228029002450d00200341002802c0a3c68000118080808000000b024020054203510d00200242919fd78da1a1ad84ff00370398012002429ceccef7a6c0dedd2037039001200242dca0bb8e8acbf9b3da003703a801200242e78ec688edebaee7ba7f3703a0012009200b20054201511b210c200241106a20024190016a10e788808000024002402002280210450d00200229031850450d010b200242919fd78da1a1ad84ff00370398012002429ceccef7a6c0dedd2037039001200242dca0bb8e8acbf9b3da003703a801200242e78ec688edebaee7ba7f3703a0012002200c3703900220024190016a412020024190026a410841002802e0a1c6800011868080800000200242919fd78da1a1ad84ff00370398012002429ceccef7a6c0dedd203703900120024282fceae49dd9e2861d3703a801200242de8c84a1ecd0a6d30c3703a00120024190026a20024190016a10e288808000200228029402210820022802980221042002280290022107200242919fd78da1a1ad84ff00370398022002429ceccef7a6c0dedd2037039002200242c6e4a9b1aaa1afebf2003703a802200242fa82b1828b81b8f31e3703a0024100200420074180808080784622061b21044108200820061b21084100200720061b210720024190016a20024190026a10e5888080000240024020022d0090010d00200241a8026a4200370300200241a0026a420037030020024190026a41086a420037030020024200370390020c010b200241a8026a200241a9016a290000370300200241a0026a200241a1016a29000037030020024190026a41086a20024199016a2900003703002002200229009101370390020b2002419c016a200436020020024190016a41086a200836020020024190016a41106a20022903900237030020024190016a41186a20024190026a41086a290300370300200241b0016a20024190026a41106a290300370300200241b8016a20024190026a41186a29030037030020022007360294012002410036029001200241fc006a41086a20024190016a10cc8680800020024180016a41c5003a0000200241818485920436027c200241fc006a10f5888080002002280290010d00200228029401450d0020022802980141002802c0a3c68000118080808000000b200242919fd78da1a1ad84ff00370398012002429ceccef7a6c0dedd2037039001200242e9c5fea9cdfbc4d26d3703a80120024286aaece2939beae4653703a001200220024190016a10e788808000200228020021072002290308210d200242919fd78da1a1ad84ff00370398012002429ceccef7a6c0dedd2037039001200242bdff9adbcf84adb29e7f3703a80120024283c69cb2f58af8c40f3703a00120024200200c200d42017c420120071b7d220d200d200c561b42ffffffff0f833703900220024190016a412020024190026a410841002802e0a1c6800011868080800000200242919fd78da1a1ad84ff00370398012002429ceccef7a6c0dedd2037039001200242e9c5fea9cdfbc4d26d3703a80120024286aaece2939beae4653703a0012002200c3703900220024190016a412020024190026a410841002802e0a1c68000118680808000000b20022009370398012002200537039001200241a0016a200241206a41d80010848e8080001a2002200a370380022002200b3703f80120024190016a10f288808000200110b88a8080000b2000420037030820004200370300200241b0026a2480808080000b21002001280214418c8ec380004114200141186a28020028020c118280808000000b040041000bb80201027f23808080800041206b2202248080808000200028020021002002200128021441e4e7c280004100200141186a28020028020c118280808000003a00142002200136021041012101200241013a00152002410036020c200220003602182002200041046a36021c2002410c6a200241186a41b88ec38000108d818080002002411c6a41c88ec38000108d81808000210020022d0014210302400240200028020022000d00200341ff017141004721010c010b200341ff01710d0020022802102103024020004101470d0020022d001541ff0171450d0020032d001c4104710d00410121012003280214418ca5c080004101200341186a28020028020c118280808000000d010b2003280214418da5c080004101200341186a28020028020c1182808080000021010b200241206a24808080800020010b2100200128021441e6acc28000410f200141186a28020028020c118280808000000bff0101027f23808080800041106b2202248080808000200220002802003602042001280214419c95c380004111200141186a28020028020c118280808000002100200241003a000d200220003a000c20022001360208200241086a41ad95c380004106200241046a418c95c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b040041000b1200200141b38ec38000410210dd808080000b02000b02000b43000240200028020022002802000d00200041086a280200450d00200028020441002802c0a3c68000118080808000000b200041002802c0a3c68000118080808000000bdd0401027f23808080800041106b22022480808080000240024002400240024002400240024002400240024020002f01000e09000102030405060708000b200128021441d88ec380004109200141186a28020028020c1182808080000021010c090b200128021441e18ec380004109200141186a28020028020c1182808080000021010c080b2002200041026a3602002002200128021441ea8ec380004118200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241848fc38000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c080b41012101200041ff01710d072002280208210020034101470d0620022d000d41ff0171450d0620002d001c4104710d06410121012000280214418ca5c080004101200041186a28020028020c11828080800000450d060c070b200128021441948fc38000410f200141186a28020028020c1182808080000021010c060b200128021441a38fc38000410d200141186a28020028020c1182808080000021010c050b200128021441b08fc38000410d200141186a28020028020c1182808080000021010c040b200128021441bd8fc38000410b200141186a28020028020c1182808080000021010c030b200128021441c88fc380004110200141186a28020028020c1182808080000021010c020b200128021441d88fc380004112200141186a28020028020c1182808080000021010c010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010b960301047f23808080800041d0076b22032480808080002003200136028004200341003602fc03200320023602f8030240024020022802042201450d0020022001417f6a36020420022002280200220141016a36020020012d0000220141024b0d00200341e8056a200341f8036a10f58c80800020032802e805450d0020032802e805210420034184046a200341e8056a41047241e40110848e8080001a024020032802f8032205280204220641204f0d00200341e8056a10bf878080000c010b2005200641606a36020420052005280200220641206a36020020034194026a20034184046a41e40110848e8080001a2004450d00200341047220034194026a41e40110848e8080001a20034181026a200641186a290000370000200341f9016a200641106a290000370000200341f1016a200641086a290000370000200320062900003700e901200320013a00e80120032004360200024020022802040d002000200341900210848e8080001a0c020b20004100360200200310bf878080000c010b200041003602000b200341d0076a2480808080000baa03010a7f23808080800041d0006b22042480808080004100210541002106024002402001280200450d0020012802040d012001417f360204200141086a21060b02400240024002402001280210450d0020012802140d012001417f360214200141186a21050b200141246a21074100210820060d01410021090c020b41dc90c38000108781808000000b2006280204210a200628020021090b2007280200210b200128022821072001280220210c0240024020050d000c010b2005280204210d200528020021080b200441c0006a200d360200200441386a200a3602002004410c6a41106a200741086a2900003702002004410c6a41186a200741106a2900003702002004412c6a200741186a2900003702002004200b3602102004200c36020c2004200836023c20042009360234200420072900003702142004410036024c200420033602482004200236024420002004410c6a20022003200441c4006a10c68c80800002402005450d002001200128021441016a3602140b02402006450d002001200128020441016a3602040b200441d0006a2480808080000f0b41ec90c38000108781808000000b840f020a7f027e23808080800041e0006b2202248080808000200241286a200110f48a808000200228022c21030240024002400240024002400240024020022802282204418080808078460d0020022802302101200241f091c380003602502002200136024c20022003360248410021052002410036023020024280808080103702282002200241286a36025c200241d4006a200241dc006a2001200241c8006a41f091c3800010858780800002402002280254418380c400460d0002402002280228450d00200228022c41002802c0a3c68000118080808000000b410021050c050b20022802282206418080808078460d04200228022c210741012105200228023022084102490d0320072d0000220141c000490d0102402001411874411875417f4a0d00410421050c040b4102210920072d0001220a413f714108742001410274200a4106767241ff01717221010c020b200041013a0000200020033602040c060b410121090b2008200941226a470d000240200141feff0071412e470d00410721050c010b200241c8006a20072009412072220510e883808000024002400240024002402002280250220a41014d0d00200228024c210a0240024002400240200820056b4102470d00200720056a2f0000200a2f0000470d00200720096a22092f0003210520092f0001210820092d0000210b2002413f6a2009411c6a280000360000200241386a200941156a290000370300200241306a2009410d6a2900003703002002200929000537032802402002280248450d00200a41002802c0a3c68000118080808000000b02402006450d00200741002802c0a3c68000118080808000000b2001413a490d08200141c00f4a0d02200141b7034a0d01200141416a0ece010808080808080807080808070707080807070807070707070707070707070707070707070707070707070807070707080707080707070807070707070707070807070707080707070707070707070707070707070707070707070707070707070707070707070707070707070708070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070807070807070707070707070707070708030b02402002280248450d00200a41002802c0a3c68000118080808000000b410321050c080b0240200141d5084a0d00024020014194064a0d00200141b803460d082001419a05470d070c080b2001419506460d07200141e307470d060c070b0240200141d0756a0e0a07060606060606060607000b200141d608460d06200141ec0b470d050c060b0240200141de364a0d00024020014196114a0d000240200141bf706a0e30080707070707070707070707070707070707070707070807080707070707070707070707080707070707070707070708000b200141d46f6a220741144b0d064101200774418180c10071450d060c070b0240200141a51f4a0d002001419711460d07200141ce11460d07200141851a470d060c070b0240200141f1284a0d00200141a61f460d07200141e222470d060c070b200141f228460d06200141ce2f470d050c060b0240200141cecd004a0d000240200141a1c5004a0d000240200141a1496a0e0708070707070708000b2001418a39460d07200141df39470d060c070b200141deba7f6a220741144d0d030c040b0240200141fade004a0d000240200141ddd9004a0d00200141cfcd00460d07200141b9ce00470d060c070b200141ded900460d06200141acdc00470d050c060b0240200141bbe6004a0d00200141fbde00460d062001419fdf00470d050c060b200141bce600460d05200141e9f200460d050c040b200141a403470d030c040b4102200a41ac93c38000109581808000000b4101200774418180c800710d020b200141f0c600460d01200141cfcc00460d010b02400240200141c4094a0d00200141a87f6a0e320202020102020101010102020202010101010101010101010101010101010101010101010101010102020101010101010202010b200141bb766a4102490d01200141fc756a4102490d01200141e26e6a4102490d010b200141bca77f6a417d4b0d00200141002f0194a1c68000460d00410221050c030b200241086a41176a2201200241286a41176a280000360000200241086a41106a2207200241286a41106a290300370300200241086a41086a200241286a41086a290300220c37030020022002290328220d370308200041046a20053b0100200041026a20083b01002000200b3a0001200041066a200d3701002000410e6a200c370100200041166a20072903003701002000411d6a2001280000360000410021010c030b2006450d00200741002802c0a3c68000118080808000000b0b200220013b015e200220053b015c200241346a4201370200410121012002410136022c200241bc93c3800036022820024189828080003602582002200241d4006a3602302002200241dc006a360254200241c8006a200241286a10b8808080002000200241c8006a10ea8a8080003602040b200020013a00002004450d00200341002802c0a3c68000118080808000000b200241e0006a2480808080000bc708020c7f027e2380808080004190026b220224808080800002400240200128020422034120490d002001200341606a220436020420012001280200220541206a2206360200200241c8006a41086a200541086a290000370300200241c8006a41106a200541106a290000370300200241c8006a41186a200541186a29000037030020022005290000370348200441c000490d002001200341a07f6a3602042001200541e0006a360200200241e8006a41086a200641086a2201290000370300200241e8006a41106a200641106a2205290000370300200241e8006a41186a200641186a2203290000370300200241e8006a41206a2204200641206a290000370300200241e8006a41286a200641286a290000370300200241e8006a41306a200641306a290000370300200241e8006a41386a200641386a29000037030020022006290000370368200241a8016a41186a2003290000370300200241a8016a41106a2005290000370300200241a8016a41086a2001290000370300200220062900003703a801200241ef016a200241a8016a10ea8280800020022d00ef01450d00200241ee016a2201200241ef016a41036a22052d00003a0000200241d0016a41086a2206200241ef016a410d6a2203290000370300200241d0016a410f6a2207200241ef016a41146a2208290000370000200241c8016a41046a2209200241ef016a41206a2d00003a0000200220022f00f0013b01ec012002200241ef016a41056a220a2900003703d0012002200241ef016a411c6a220b2800003602c801200241ef016a41046a220c2d0000210d200241ef016a200410ea8280800020022d00ef01450d00200241086a220420032900003703002002410f6a22032008290000370000200220022f00f0013b0118200220052d00003a001a2002200a290000370300200220022f01ec013b0140200220012d00003a0042200b350000210e200241ef016a41206a310000210f200c2d00002101200241286a410f6a22052007290000370000200241286a41086a22072006290300370300200220022903d001370328200241206a41046a220620092d00003a0000200220022802c801360220200041d9006a200241e0006a290300370000200041d1006a200241d8006a290300370000200041c9006a200241c8006a41086a290300370000200041c1006a2002290348370000200041046a200d3a0000200020022f01403b0001200041036a20022d00423a0000200041056a20022903283700002000410d6a2007290300370000200041146a2005290000370000200041206a20062d00003a00002000411c6a2002280220360000200041236a20022d001a3a0000200041216a20022f01183b0000200041246a20013a0000200041346a20032900003700002000412d6a2004290300370000200041256a2002290300370000200041c0006a200f3c00002000413c6a200e3e0000200041003a00000c010b200041013a00000b20024190026a2480808080000bf80501087f2380808080004180016b220224808080800041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002203450d0020032000290040370000200341186a2204200041d8006a290000370000200341106a2205200041d0006a290000370000200341086a2206200041c8006a29000037000002402001280200200128020822076b411f4b0d0020012007412010b182808000200128020821070b2001280204220820076a22092003290000370000200941086a2006290000370000200941106a2005290000370000200941186a20042900003700002001200741206a2209360208200341002802c0a3c6800011808080800000200241086a200041086a290000370300200241106a200041106a290000370300200241186a200041186a290000370300200241206a22032000290020370300200241286a2207200041286a290000370300200241306a2204200041306a290000370300200241386a2205200041386a2900003703002002200029000037030041002d00fca3c680001a41c00041002802c8a3c68000118180808000002200450d0120002002290300370000200041386a2005290300370000200041306a2004290300370000200041286a2007290300370000200041206a2003290300370000200041186a2207200241186a290300370000200041106a2204200241106a290300370000200041086a2205200241086a2903003700000240200128020020096b413f4b0d002001200941c00010b18280800020012802042108200128020821090b200820096a22032000290000370000200341386a200041386a290000370000200341306a200041306a290000370000200341286a200041286a290000370000200341206a200041206a290000370000200341186a2007290000370000200341106a2004290000370000200341086a20052900003700002001200941c0006a360208200041002802c0a3c680001180808080000020024180016a2480808080000f0b4101412010b280808000000b410141c00010b280808000000ba60401067f23808080800041d0006b2204248080808000200141246a28020028020c210520012802202106200441186a41086a2207200341086a280200360200200420032902003703182004410c6a20062002200441186a20051186808080000002400240024002400240200428020c2205418080808078470d00200441186a41186a2203200241186a290000370300200441186a41106a2205200241106a2900003703002007200241086a2900003703002004200229000037031841002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b20042802142106200428021021072001280210450d022001280214450d0141c493c38000108781808000000b2001418580808078360200200120042903183702042001410c6a200441206a290300370200200141146a20052903003702002001411c6a20032903003702002000418080808078360200200020013602040c020b2001417f360214200141186a28020021082001411c6a2802002109200441c4006a200241186a2900003702002004413c6a200241106a290000370200200441186a411c6a200241086a290000370200200420063602202004200736021c20044180808080783602182004200229000037022c200420032902003702242008200441186a200928020c118480808000002001200128021441016a3602140b2000200636020820002007360204200020053602000b200441d0006a2480808080000b9c0e05047f027e017f017e027f23808080800041d0056b2206248080808000200341086a280200210720032802042108024002400240024002400240024002400240024002400240024002400240200328020022090d0020070d01410121030c020b0240024020070d00410121030c010b2007417f4c0d0741002d00fca3c680001a200741002802c8a3c68000118180808000002203450d080b20032008200710848e8080001a41002104200721080c030b20074120460d012007417f4c0d0541002d00fca3c680001a200741002802c8a3c68000118180808000002203450d070b20032008200710848e808000210841002d00fca3c680001a413041002802c8a3c680001181808080000022030d024104413010b280808000000b200841026a2d000021072008410f6a290000210a200841176a290000210b20082f000021032008280003210c2008290007210d20064188026a411f6a2008411f6a2d00003a000020064188026a41176a200b37000020064188026a410f6a200a37000020064188026a41026a20073a00002006200d37008f022006200c36008b02200620033b018802200141246a280200210720012802202103200641306a41086a200441086a280200360200200620042902003703302006410c6a200320064188026a200641306a200728020c11868080800000200628020c2207418080808078460d08200641f0036a41086a20064188026a41086a290100370300200641f0036a41106a20064188026a41106a290100370300200641f0036a41186a20064188026a41186a29010037030020062006290188023703f0032006280214210820062802102103410121040b200641156a200641f0036a41086a220e2903003700002006411d6a20064180046a220c290300370000200641256a20064188046a290300370000200620043a000c200620062903f00337000d200641f0036a2003200810c28c8080002006410d6a210420062802f003220f4105470d0120064188026a41086a200c2802003602002006200e2902003703880220062802f403210802402007450d00200341002802c0a3c68000118080808000000b200641f0036a41186a22032002200420091b220741186a290000370300200641f0036a41106a2202200741106a290000370300200641f0036a41086a2201200741086a290000370300200620072900003703f00341002d00fca3c680001a413041002802c8a3c680001181808080000022070d054104413010b280808000000b2003200736020c2003200836020820032007360204200341888080807836020020032002290000370010200341186a200241086a290000370000200341206a200241106a290000370000200341286a200241186a2900003700000c070b200641e0036a41086a2202200c280200360200200620062902f8033703e00320062802f403210c20064194026a20064184046a41c80110848e8080001a20064188026a41086a2002280200360200200620062903e00337038802200641306a20064188026a41d40110848e8080001a20090d042005450d042001280210450d04024020012802140d002001417f360214200141186a28020021022001411c6a2802002109200641fc036a2008360200200641f0036a41086a200336020020064198046a200441186a29000037020020064190046a200441106a290000370200200641f0036a41186a200441086a2900003702002006428280808088808080807f3702f00320062004290000370280042002200641f0036a200928020c118480808000002001200128021441016a3602140c050b41d493c38000108781808000000b10ae80808000000b4101200710b280808000000b4101200710b280808000000b200720083602002007200629038802370204200720062903f0033702102007410c6a20064188026a41086a280200360200200741186a2001290300370200200741206a2002290300370200200741286a200329030037020020004105360200200020073602040c030b200041086a200641306a41d40110848e8080001a200020073602dc01200020083602e401200020033602e0012000200c3602042000200f3602002000200629000c3700e801200041f0016a2006410c6a41086a290000370000200041f8016a2006411c6a29000037000020004180026a200641246a29000037000020004188026a2006412c6a2d00003a00000c020b20042d000821082004280204210741002d00fca3c680001a413041002802c8a3c6800011818080800000210302400240024020070d00200841ff0171450d010b418580808078210720030d014104413010b280808000000b418480808078210720030d004104413010b280808000000b2003200736020020032006290188023700042003410c6a20064190026a290100370000200341146a20064198026a2901003700002003411c6a200641a0026a2901003700000b20004105360200200020033602040b200641d0056a2480808080000bb00101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110fa86808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c000370350200041c000360220200041c880808000360218200041033a0000200141106a2480808080000b2a00200041003b01102000420037030820004280c2d72f42e097e1c29b0120012d00104103491b3703000bec0c01027f23808080800041106b22022480808080000240024002400240024002400240024002400240024002400240024002400240024002400240024020002d00000e0e000102030405060708090a0b0c0d000b2002200041046a36020020022001280214418ea2c380004105200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a20024194a2c38000108d81808000210120022d000c21000240200128020022010d00200041ff017141004721030c130b41012103200041ff01710d122002280208210020014101470d1120022d000d41ff0171450d1120002d001c4104710d11410121032000280214418ca5c080004101200041186a28020028020c11828080800000450d110c120b200128021441a4a2c38000410c200141186a28020028020c1182808080000021030c110b200128021441b0a2c380004109200141186a28020028020c1182808080000021030c100b2002200041046a3602002002200128021441b9a2c380004106200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241c0a2c38000108d81808000210120022d000c21000240200128020022010d00200041ff017141004721030c100b41012103200041ff01710d0f2002280208210020014101470d0d20022d000d41ff0171450d0d20002d001c4104710d0d410121032000280214418ca5c080004101200041186a28020028020c11828080800000450d0d0c0f0b200128021441d0a2c380004111200141186a28020028020c1182808080000021030c0e0b200128021441e1a2c38000410b200141186a28020028020c1182808080000021030c0d0b200128021441eca2c380004110200141186a28020028020c1182808080000021030c0c0b410121032002200041016a3602002002200128021441fca2c380004105200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a20024184a3c38000108d81808000210120022d000c21000240200128020022010d00200041ff017141004721030c0c0b200041ff01710d0b2002280208210020014101470d0820022d000d41ff0171450d0820002d001c4104710d08410121032000280214418ca5c080004101200041186a28020028020c11828080800000450d080c0b0b410121032002200041016a360200200220012802144194a3c38000410a200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241a0a3c38000108d81808000210120022d000c21000240200128020022010d00200041ff017141004721030c0b0b200041ff01710d0a2002280208210020014101470d0620022d000d41ff0171450d0620002d001c4104710d06410121032000280214418ca5c080004101200041186a28020028020c11828080800000450d060c0a0b410121032002200041016a3602002002200128021441b0a3c38000410d200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241c0a3c38000108d81808000210120022d000c21000240200128020022010d00200041ff017141004721030c0a0b200041ff01710d092002280208210020014101470d0420022d000d41ff0171450d0420002d001c4104710d04410121032000280214418ca5c080004101200041186a28020028020c11828080800000450d040c090b200128021441d0a3c380004109200141186a28020028020c1182808080000021030c080b200128021441d9a3c38000410a200141186a28020028020c1182808080000021030c070b200128021441e3a3c38000410b200141186a28020028020c1182808080000021030c060b200128021441eea3c38000410e200141186a28020028020c1182808080000021030c050b2000280214418da5c080004101200041186a28020028020c1182808080000021030c040b2000280214418da5c080004101200041186a28020028020c1182808080000021030c030b2000280214418da5c080004101200041186a28020028020c1182808080000021030c020b2000280214418da5c080004101200041186a28020028020c1182808080000021030c010b2000280214418da5c080004101200041186a28020028020c1182808080000021030b200241106a24808080800020030b840601067f23808080800041306b220124808080800020014106360204200141a599c3800036020041002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a599c38000360200200241046a410636020041a599c38000410610d782808000450d0141002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d02200342d1c5efcdcd82cde1ff00370308200341ac99c38000360220200341938280800036021820034101360204200341ab99c38000360200200341306a42c0969aec91e181fcf200370300200341286a42d7a7fbff94a5afcaf800370300200341106a429ccab49c93e2e495cf00370300200341386a419482808000360200200341246a4101360200200141023602142001200336020c2001200341c0006a36021820012003360210200141246a2001410c6a10fa86808000200128022c210420012802282103200128022421052001410036021420014280808080c00037020c200141246a2001410c6a41ad99c38000410210a08b8080002001200141246a41af99c38000410310ce8b8080002001200128020036021420012001280204220636020c20012006200128020841246c6a36021820012006360210200141246a2001410c6a10fb86808000200120053602142001200336020c2001200320044105746a36021820012003360210200041c4006a2001410c6a10fa86808000200141176a200141246a41086a2802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129022437000f2000200129000c370001200041086a200141136a290000370000200141306a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141186a42013702002001410236021020014180ddc1800036020c200141b4808080003602282001200141246a360214200120013602242001410c6a4190ddc1800010f680808000000b410841c00010b280808000000b800601067f23808080800041306b220124808080800020014106360204200141a599c3800036020041002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a599c38000360200200241046a410636020041a599c38000410610d782808000450d0141002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d022003429198b9e48fc6c3ad1d370308200341ac99c38000360220200341958280800036021820034101360204200341ab99c38000360200200341306a42ab8ad7e1bb97ae9f51370300200341286a42f2f9a5a49996c5e032370300200341106a42f69a89928aa399ea00370300200341386a419682808000360200200341246a4101360200200141023602142001200336020c2001200341c0006a36021820012003360210200141246a2001410c6a10fa86808000200128022c210420012802282103200128022421052001410036021420014280808080c00037020c200141246a2001410c6a41ad99c380004102109a8b8080002001200141246a41af99c38000410310fc8a8080002001200128020036021420012001280204220636020c20012006200128020841246c6a36021820012006360210200141246a2001410c6a10fb86808000200120053602142001200336020c2001200320044105746a36021820012003360210200041c4006a2001410c6a10fa86808000200141176a200141246a41086a2802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129022437000f2000200129000c370001200041086a200141136a290000370000200141306a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141186a42013702002001410236021020014180ddc1800036020c200141b4808080003602282001200141246a360214200120013602242001410c6a4190ddc1800010f680808000000b410841c00010b280808000000b840601067f23808080800041306b220124808080800020014106360204200141a599c3800036020041002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a599c38000360200200241046a410636020041a599c38000410610d782808000450d0141002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d02200342d1c5efcdcd82cde1ff00370308200341ac99c38000360220200341938280800036021820034101360204200341ab99c38000360200200341306a42efc9c9edb5e7b3a6c700370300200341286a42acf6debeefe0d9c8d300370300200341106a429ccab49c93e2e495cf00370300200341386a41bd80808000360200200341246a4101360200200141023602142001200336020c2001200341c0006a36021820012003360210200141246a2001410c6a10fa86808000200128022c210420012802282103200128022421052001410036021420014280808080c00037020c200141246a2001410c6a41ad99c38000410210a08b8080002001200141246a41af99c38000410310898b8080002001200128020036021420012001280204220636020c20012006200128020841246c6a36021820012006360210200141246a2001410c6a10fb86808000200120053602142001200336020c2001200320044105746a36021820012003360210200041c4006a2001410c6a10fa86808000200141176a200141246a41086a2802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129022437000f2000200129000c370001200041086a200141136a290000370000200141306a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141186a42013702002001410236021020014180ddc1800036020c200141b4808080003602282001200141246a360214200120013602242001410c6a4190ddc1800010f680808000000b410841c00010b280808000000b800601067f23808080800041306b220124808080800020014106360204200141a599c3800036020041002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a599c38000360200200241046a410636020041a599c38000410610d782808000450d0141002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d02200342d9adfaebb1c192ed50370308200341ac99c38000360220200341978280800036021820034101360204200341ab99c38000360200200341306a42ab8ad7e1bb97ae9f51370300200341286a42f2f9a5a49996c5e032370300200341106a42fb8387e2d6839cd949370300200341386a419682808000360200200341246a4101360200200141023602142001200336020c2001200341c0006a36021820012003360210200141246a2001410c6a10fa86808000200128022c210420012802282103200128022421052001410036021420014280808080c00037020c200141246a2001410c6a41ad99c380004102108a8b8080002001200141246a41af99c38000410310fc8a8080002001200128020036021420012001280204220636020c20012006200128020841246c6a36021820012006360210200141246a2001410c6a10fb86808000200120053602142001200336020c2001200320044105746a36021820012003360210200041c4006a2001410c6a10fa86808000200141176a200141246a41086a2802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129022437000f2000200129000c370001200041086a200141136a290000370000200141306a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141186a42013702002001410236021020014180ddc1800036020c200141b4808080003602282001200141246a360214200120013602242001410c6a4190ddc1800010f680808000000b410841c00010b280808000000bac0401097f23808080800041306b220124808080800020012000200010eb8780800002400240024020012802102202418080808078460d002001280224210320012802202104200128021c210520012802142106024020012802182200450d002000410171210741002108024020004101460d002000417e7121092006210041002108034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002009200841026a2208470d000b0b2007450d0020062008410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b02402002450d00200641002802c0a3c68000118080808000000b02402003450d002003410171210241002108024020034101460d002003417e7121092004210041002108034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002009200841026a2208470d000b0b2002450d0020042008410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b2005450d01200441002802c0a3c68000118080808000000c010b20012d000041ff017122004102460d0020012f000141087421080c010b41002108410221000b200141306a24808080800020082000720bfb0801087f23808080800041e0006b22032480808080002003200236020c02404100280284a4c680004105470d00200341988280800036025820032003410c6a3602544100280290a1c680002102410028028ca1c6800021044100280280a4c680002105200341c8006a4201370200200341c0006a4101360200200341386a4115360200200341346a41a4a9c38000360200200341286a41c0a7c38000ad4280808080b00e843702002003411c6a41b9a9c38000ad4280808080d00684370200200341c4006a200341d4006a3602002003419ca9c3800036023c20034105360230200341003602242003410036021820034281808080901c370210200441ecf2c08000200541024622051b200341106a200241d4f2c0800020051b28021011848080800000200328020c21020b02400240024002400240024020022802002202410c4b0d004101200274418831710d010b2000418080808078360210200041003b01000c010b41002d00fca3c680001a410c41002802c8a3c68000118180808000002204450d014101210202400240024002400240024002400240024002400240200328020c2205280200417f6a0e0c080600010203090409050505080b417f200541186a28020041046a220241012005410c6a28020041056a2005280204418080808078461b6a220620062002491b21020c060b417f200541186a2802002005410c6a28020041046a22026a41046a220620062002491b21020c050b2005410c6a28020041046a21020c040b2005410c6a28020041046a21020c030b410921020c040b410521020c030b2005410c6a28020041046a21020b4101210641002107200241016a2202450d022002417f4a0d0110ae80808000000b41d10021020b41002d00fca3c680001a200241002802c8a3c68000118180808000002206450d03200221070b2003200636021420032007360210200341003602182005200341106a10ba8880800020032802102102200341106a2003280214220520032802184100280298a3c680001185808080000002402002450d00200541002802c0a3c68000118080808000000b2003410036025c200342808080801037025441002d00fca3c680001a412041002802c8a3c68000118180808000002202450d0320022003290010370000200241186a2207200341106a41186a290000370000200241106a2208200341106a41106a290000370000200241086a2209200341106a41086a290000370000200341d4006a4100412010b1828080002003280258200341d4006a41086a2206280200220a6a22052002290000370000200541086a2009290000370000200541106a2008290000370000200541186a20072900003700002006200a41206a360200200241002802c0a3c6800011808080800000200441086a20062802003602002004200329025437020020004280808080c000370310200041013a0028200041013602242000200436022020004280808080103703182000427f370308200042003703000b200341e0006a2480808080000f0b4104410c10b280808000000b4101200210b280808000000b4101412010b280808000000bfd0201017f23808080800041c0006b22042480808080000240024020012d00780d0020012d00790d01200441386a41a08ac480003602002004412c6a4100360200200442003702242004410036021c20044200370214200420013602342004200141386a36023c200441086a200441146a2002200310dd87808000024002402004280208418180808078460d0020002004290208370200200041086a200441086a41086a2802003602000c010b0240024002400240200428020c220128020041fcffffff076a2203410320034105491b0e0403030102000b2001280204450d02200141086a28020041002802c0a3c68000118080808000000c020b2001280204450d01200141086a28020041002802c0a3c68000118080808000000c010b200110ed878080000b200141002802c0a3c680001180808080000020004181808080783602000b200441c0006a2480808080000f0b41b299c38000410f41b89dc3800010f880808000000b41b299c38000410f41b49ac3800010f880808000000bc50101027f024002400240024020002802002201418080808078732202410220024104491b0e03030301000b0240024002402000280204220028020041fcffffff076a2202410320024105491b0e0404040102000b2000280204450d03200041086a28020041002802c0a3c68000118080808000000c030b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b200010ed878080000c010b2001450d01200028020421000b200041002802c0a3c68000118080808000000b0b170041b299c38000410f41b49ac3800010f880808000000b970601087f23808080800041106b2202248080808000024002400240024020002802000e03000102000b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41003a00002001200341016a2204360208200028020421050240200128020020046b41034b0d0020012004410410b182808000200128020821040b2001200441046a2203360208200128020420046a20053600000c020b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a2203360208200128020420046a41013a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a2203360208200128020420046a41023a00000b0240024020002d00084116470d00024020012802002003470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41003a0000200041106a200110a08a8080000c010b200041086a2104024020012802002003470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41033a00002004200110a88c8080000b200041dc006a28020021032002200041e0006a28020022003602082002200241086a36020c2002410c6a200110c08a808000024002402000450d0020004105742106034041002d00fca3c680001a412041002802c8a3c68000118180808000002200450d0220002003290000370000200041186a2207200341186a290000370000200041106a2208200341106a290000370000200041086a2209200341086a29000037000002402001280200200128020822056b411f4b0d0020012005412010b182808000200128020821050b200341206a2103200128020420056a22042000290000370000200441086a2009290000370000200441106a2008290000370000200441186a20072900003700002001200541206a360208200041002802c0a3c6800011808080800000200641606a22060d000b0b200241106a2480808080000f0b4101412010b280808000000bad0b04067f017e027f017e23808080800041d0006b2201248080808000200141286a419f9bc38000410b41aa9bc38000410c418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242a5e9e3ab9e929adc2c370308200241bb9bc38000360220200241ef8080800036021820024105360204200241b69bc38000360200200241306a42e1be86f0e3e6afc202370300200241286a42c4daf0e7d4cf86cca87f370300200241106a4293888c8f89fdc6ec9e7f370300200241386a419982808000360200200241246a410b36020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a200141106a410c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c2002420537022420024185e4c480003602202002410536021c20024180e4c48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420837022420024193e4c480003602202002410936021c2002419be4c48000360218200241b580808000360210200242e88488d0c0e3aebc13370308200242d7c9cb8fc1cf97db3e3703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c2002420837022420024193e4c480003602202002410936021c2002418ae4c48000360218200241b580808000360210200242e88488d0c0e3aebc13370308200242d7c9cb8fc1cf97db3e370300200141c0006a41086a2208200141106a41086a220928020041016a220236020020012001290310220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420837022420024193e4c480003602202002410b36021c200241b3e4c48000360218200241b580808000360210200242e88488d0c0e3aebc13370308200242d7c9cb8fc1cf97db3e3703002009200828020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c2002420b370224200241a8e4c480003602202002410436021c200241a4e4c480003602182002419982808000360210200242e1be86f0e3e6afc202370308200242c4daf0e7d4cf86cca87f370300200128021821082001280214210220012001280210360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000beb0804067f017e027f017e23808080800041d0006b2201248080808000200141286a41c69bc38000410b41aa9bc38000410c418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d002002428efca59582e3caf618370308200241ab99c380003602202002419a8280800036021820024101360204200241ac99c38000360200200241306a42ab8bffbed784ffa5937f370300200241286a42c194a6a793ccc3a857370300200241106a42988ac9c0f3f1b2b0b57f370300200241386a41f581808000360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a200141106a410c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024205370224200241d5e4c480003602202002410536021c200241d0e4c480003602182002419b82808000360210200242dd9fc4e2a4bae882d400370308200242fb9bf4d1a5ccd7cbed00370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024201370224200241c3e4c480003602202002410536021c200241bee4c480003602182002419a82808000360210200242988ac9c0f3f1b2b0b57f3703082002428efca59582e3caf6183703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024206370224200241cae4c480003602202002410636021c200241c4e4c480003602182002419c82808000360210200242b6e5c682fa82cdd4dc00370308200242b49ee2c5eaeedcf10c370300200128021821082001280214210220012001280210360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bdd0604067f017e017f017e23808080800041d0006b2201248080808000200141106a41186a41d19bc38000411841aa9bc38000410c418097c38000410010d882808000200141206a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241ab99c3800036020020014101360248200120023602402001200241206a36024c20012002360244200141106a200141c0006a10fa86808000200141086a200141106a410c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024207370224200241e3e4c480003602202002410936021c200241dae4c48000360218200241f581808000360210200242ab8bffbed784ffa5937f370308200242c194a6a793ccc3a857370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024204370224200241f7e4c480003602202002410d36021c200241eae4c48000360218200241888180800036021020024298848fa1dab08ba174370308200242febac4ad81b6fafcb37f370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000be80501047f23808080800041e0006b2202248080808000200241346a41f9a6c48000410641002802a0a3c6800011858080800000200241346a41106a220341899fc38000410d41002802a0a3c6800011858080800000200241086a41186a200241346a41186a290000370300200241086a41106a2003290000370300200241086a41086a200241346a41086a2900003703002002200229003437030820022001360254200241d8006a200241d4006a410441002802a8a3c68000118580808000002002200241d4006a41046a3602402002200241d8006a41086a3602382002200241d4006a36023c2002200241d8006a360234200241286a200241346a108182808000024002400240024002402002280230220341206a22040d002002410036023c20024280808080103702340c010b2004417f4c0d024100210541002d00fca3c680001a200441002802c8a3c68000118180808000002201450d032002410036023c200220013602382002200436023420034160490d010b200241346a4100412010ab8680800020022802382101200228023c21050b200120056a22042002290308370000200441186a200241086a41186a290300370000200441106a200241086a41106a290300370000200441086a200241086a41086a2903003700002002200541206a220436023c200228022c21050240200228023420046b20034f0d00200241346a2004200310ab8680800020022802382101200228023c21040b200120046a2005200310848e8080001a200420036a21032002280234210402402002280228450d00200541002802c0a3c68000118080808000000b200241346a2001200310e988808000024002402002280234418080808078470d002000410036020820004280808080103702000c010b20002002290234370200200041086a200241346a41086a2802003602000b02402004450d00200141002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200410b280808000000bb108050b7f017e037f027e017f2380808080004190016b2202248080808000200241086a41f9a6c48000410641002802a0a3c6800011858080800000200241086a41106a220341ea9ec38000410741002802a0a3c6800011858080800000200241d0006a41186a200241086a41186a290000370300200241d0006a41106a2003290000370300200241d0006a41086a200241086a41086a2900003703002002200229000837035041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002204450d0020042001290000370000200441186a200141186a290000370000200441106a200141106a290000370000200441086a200141086a290000370000200241086a200441204100280290a3c68000118580808000002002200441206a36028c01200220043602880120022003360284012002200241086a36028001200241f4006a20024180016a108182808000200441002802c0a3c6800011808080800000024002400240200228027c220141206a22030d002002410036021020024280808080103702080c010b2003417f4c0d034100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d04200241003602102002200436020c2002200336020820014160490d010b200241086a4100412010ab86808000200228020c2104200228021021050b200420056a22032002290350370000200341186a200241d0006a41186a290300370000200341106a200241d0006a41106a290300370000200341086a200241d0006a41086a2903003700002002200541206a2203360210200228027821050240200228020820036b20014f0d00200241086a2003200110ab86808000200228020c2104200228021021030b200420036a2005200110848e8080001a200320016a21012002280208210302402002280274450d00200541002802c0a3c68000118080808000000b200241086a2004200110e4888080000240024020022903084200520d00410521010c010b200241d0006a41086a2205200241306a2206290300370300200241d0006a41106a2207200241386a22082903003703002002200241286a22092903003703500240200241c4006a220a280200220b0d00410521010c010b200241086a41106a220c290300210d200241c0006a220e280200210f200241c8006a2210290300211120022903102112200241206a22132013290300370300200c200d3703002009200229035037030020102011370300200a200b3602002006200529030037030020082007290300370300200e200f41016a2205417f20051b3602002002201237031020024201370308200241086a41086a2004200110f587808000410e21010b200020013a000002402003450d00200441002802c0a3c68000118080808000000b20024190016a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200310b280808000000b980301047f23808080800041106b220324808080800041002d00fca3c680001a0240413c41002802c8a3c68000118180808000002204450d00200320043602082003413c360204200420002903003700002003410836020c200028023021054108210402402003280204417c714108470d00200341046a4108410410b182808000200328020c21040b200328020820046a20053600002003200441046a220436020c200028023421050240200328020420046b41034b0d00200341046a2004410410b182808000200328020c21040b200041086a2106200328020820046a20053600002003200441046a220436020c200028023821000240200328020420046b41034b0d00200341046a2004410410b182808000200328020c21040b200328020820046a20003600002003200441046a36020c2006200341046a10848d808000200328020421002001200220032802082204200328020c41002802e0a1c680001186808080000002402000450d00200441002802c0a3c68000118080808000000b200341106a2480808080000f0b4101413c10b280808000000bc20802067f037e2380808080004190016b2202248080808000200241086a41f9a6c48000410641002802a0a3c6800011858080800000200241086a41106a220341ea9ec38000410741002802a0a3c6800011858080800000200241d0006a41186a200241086a41186a290000370300200241d0006a41106a2003290000370300200241d0006a41086a200241086a41086a2900003703002002200229000837035041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002204450d0020042001290000370000200441186a200141186a290000370000200441106a200141106a290000370000200441086a200141086a290000370000200241086a200441204100280290a3c68000118580808000002002200441206a36028c01200220043602880120022003360284012002200241086a36028001200241f4006a20024180016a108182808000200441002802c0a3c6800011808080800000024002400240200228027c220141206a22030d002002410036021020024280808080103702080c010b2003417f4c0d034100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d04200241003602102002200436020c2002200336020820014160490d010b200241086a4100412010ab86808000200228020c2104200228021021050b200420056a22032002290350370000200341186a200241d0006a41186a290300370000200341106a200241d0006a41106a290300370000200341086a200241d0006a41086a2903003700002002200541206a2203360210200228027821050240200228020820036b20014f0d00200241086a2003200110ab86808000200228020c2104200228021021030b200420036a2005200110848e8080001a200320016a21012002280208210602402002280274450d00200541002802c0a3c68000118080808000000b200241086a2004200110e4888080000240024020022903084200520d00410521030c010b200241d8006a200241306a290300370300200241d0006a41106a200241386a2903003703002002200241286a2903003703500240200241c4006a28020022050d00410521030c010b41062103200241c0006a2802002207410f4b0d00200241086a41106a2903002108200241c8006a29030021092002290310210a200241206a200241206a290300370300200241086a41106a2008370300200241286a2002290350370300200241c8006a2009370300200241c4006a2005360200200241c0006a200741016a360200200241306a200241d0006a41086a290300370300200241386a200241d0006a41106a2903003703002002200a37031020024201370308200241086a41086a2004200110f587808000410e21030b200020033a000002402006450d00200441002802c0a3c68000118080808000000b20024190016a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200310b280808000000be60e01127f23808080800041e0016b220324808080800020034190016a41f9a6c48000410641002802a0a3c680001185808080000020034190016a41106a220441ea9ec38000410741002802a0a3c6800011858080800000200341086a41186a20034190016a41186a290000370300200341086a41106a2004290000370300200341086a41086a20034190016a41086a290000370300200320032900900137030841002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002205450d0020052001290000370000200541186a200141186a290000370000200541106a200141106a290000370000200541086a200141086a29000037000020034190016a200541204100280290a3c68000118580808000002003200541206a36026c2003200536026820032004360264200320034190016a360260200341d4006a200341e0006a108182808000200541002802c0a3c6800011808080800000024002400240200328025c220141206a22040d0020034100360298012003428080808010370290010c010b2004417f4c0d034100210641002d00fca3c680001a200441002802c8a3c68000118180808000002205450d0420034100360298012003200536029401200320043602900120014160490d010b20034190016a4100412010ab86808000200328029401210520032802980121060b200520066a22042003290308370000200441186a200341086a41186a290300370000200441106a200341086a41106a290300370000200441086a200341086a41086a2903003700002003200641206a22043602980120032802582106024020032802900120046b20014f0d0020034190016a2004200110ab86808000200328029401210520032802980121040b200520046a2006200110848e8080001a200420016a2104200328029001210702402003280254450d00200641002802c0a3c68000118080808000000b200341086a2005200410e488808000200328020821012003420037030802400240024002400240024020014101470d00200328024c210820032802482109200328024421062003280240210a200341e0006a41286a220b200341086a41086a220141286a220c290300370300200341e0006a41206a220d200141206a220e290300370300200341e0006a41186a220f200141186a2210290300370300200341e0006a41106a2211200141106a2212290300370300200341e0006a41086a200141086a2903003703002003200129030037036002400240024020060e020001020b0240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b4100280290a1c680002106410028028ca1c6800021134100280280a4c680002114200341c8016a4200370200200341c4016a418097c38000360200200341c0016a4101360200200341b8016a410f360200200341b4016a41a89cc38000360200200341a8016a41c49ac38000ad4280808080b00b843702002003419c016a41aa9bc38000ad4280808080c00184370200200341f09cc380003602bc01200341013602b001200341003602a40120034100360298012003428180808090bd0137029001201341ecf2c08000201441024622141b20034190016a200641d4f2c0800020141b280210118480808000000b200a200972450d03200a0d02410121060b20012003290360370300200c200b290300370300200e200d2903003703002010200f29030037030020122011290300370300200141086a200341e0006a41086a2903003703002003200836024c200320093602482003200a3602402003420137030820032006417f6a36024420012005200410f587808000410121010c040b0240417f4100280284a4c680002201410147200141014b1b2201417f460d00200141ff01710d030b4100280290a1c680002101410028028ca1c6800021064100280280a4c680002102200341c8016a4200370200200341c4016a418097c38000360200200341c0016a4101360200200341b8016a410f360200200341b4016a41a89cc38000360200200341a8016a41c49ac38000ad4280808080b00b843702002003419c016a41aa9bc38000ad4280808080c00184370200200341b09dc380003602bc01200341013602b001200341003602a401200341003602980120034281808080b0c00137029001200641ecf2c08000200241024622021b20034190016a200141d4f2c0800020021b280210118480808000000c020b410421040c030b20034199016a2002290000370000200341b1016a200241186a290000370000200341a9016a200241106a290000370000200341a1016a200241086a290000370000200341043a009801200341163a00900120034190016a108e8a8080000b410021012005200441002802a0a1c68000118480808000000b410e21040b200020013a0001200020043a000020002003290190013701022000410a6a20034198016a290100370100200041126a200341a0016a2f01003b010002402007450d00200541002802c0a3c68000118080808000000b200341e0016a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200410b280808000000b800801047f2380808080004190016b2202248080808000200241086a41f9a6c48000410641002802a0a3c6800011858080800000200241086a41106a220341ea9ec38000410741002802a0a3c6800011858080800000200241d0006a41186a200241086a41186a290000370300200241d0006a41106a2003290000370300200241d0006a41086a200241086a41086a2900003703002002200229000837035041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002204450d0020042001290000370000200441186a200141186a290000370000200441106a200141106a290000370000200441086a200141086a290000370000200241086a200441204100280290a3c68000118580808000002002200441206a36028c01200220043602880120022003360284012002200241086a36028001200241f4006a20024180016a108182808000200441002802c0a3c6800011808080800000024002400240200228027c220141206a22030d002002410036021020024280808080103702080c010b2003417f4c0d034100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d04200241003602102002200436020c2002200336020820014160490d010b200241086a4100412010ab86808000200228020c2104200228021021050b200420056a22032002290350370000200341186a200241d0006a41186a290300370000200341106a200241d0006a41106a290300370000200341086a200241d0006a41086a2903003700002002200541206a2203360210200228027821050240200228020820036b20014f0d00200241086a2003200110ab86808000200228020c2104200228021021030b200420036a2005200110848e8080001a200320016a21012002280208210302402002280274450d00200541002802c0a3c68000118080808000000b200241086a2004200110e4888080000240024020022903084200520d002000420037030820004200370300200041106a428080808080808080807f370300200041186a4200370300200041206a4200370300200041286a4200370300200041306a4200370300200041386a41003602000c010b20002002290310370300200041386a200241c8006a290300370300200041306a200241086a41386a290300370300200041286a200241086a41306a290300370300200041206a200241086a41286a290300370300200041186a200241086a41206a290300370300200041106a200241086a41186a290300370300200041086a200241086a41106a2903003703000b02402003450d00200441002802c0a3c68000118080808000000b20024190016a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200310b280808000000beb0501047f23808080800041e0006b2202248080808000200241346a41f5a6c48000410441002802a0a3c6800011858080800000200241346a41106a220341f89ec38000411141002802a0a3c6800011858080800000200241086a41186a200241346a41186a290000370300200241086a41106a2003290000370300200241086a41086a200241346a41086a2900003703002002200229003437030820022001280200360254200241d8006a200241d4006a410441002802a8a3c68000118580808000002002200241d4006a41046a3602402002200241d8006a41086a3602382002200241d4006a36023c2002200241d8006a360234200241286a200241346a108182808000024002400240024002402002280230220341206a22040d002002410036023c20024280808080103702340c010b2004417f4c0d024100210541002d00fca3c680001a200441002802c8a3c68000118180808000002201450d032002410036023c200220013602382002200436023420034160490d010b200241346a4100412010ab8680800020022802382101200228023c21050b200120056a22042002290308370000200441186a200241086a41186a290300370000200441106a200241086a41106a290300370000200441086a200241086a41086a2903003700002002200541206a220436023c200228022c21050240200228023420046b20034f0d00200241346a2004200310ab8680800020022802382101200228023c21040b200120046a2005200310848e8080001a200420036a21032002280234210402402002280228450d00200541002802c0a3c68000118080808000000b200241346a2001200310e188808000024002402002280234418080808078470d002000410036020820004280808080103702000c010b20002002290234370200200041086a200241346a41086a2802003602000b02402004450d00200141002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200410b280808000000b800801047f2380808080004190016b2202248080808000200241086a41f9a6c48000410641002802a0a3c6800011858080800000200241086a41106a220341ea9ec38000410741002802a0a3c6800011858080800000200241d0006a41186a200241086a41186a290000370300200241d0006a41106a2003290000370300200241d0006a41086a200241086a41086a2900003703002002200229000837035041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002204450d0020042001290000370000200441186a200141186a290000370000200441106a200141106a290000370000200441086a200141086a290000370000200241086a200441204100280290a3c68000118580808000002002200441206a36028c01200220043602880120022003360284012002200241086a36028001200241f4006a20024180016a108182808000200441002802c0a3c6800011808080800000024002400240200228027c220141206a22030d002002410036021020024280808080103702080c010b2003417f4c0d034100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d04200241003602102002200436020c2002200336020820014160490d010b200241086a4100412010ab86808000200228020c2104200228021021050b200420056a22032002290350370000200341186a200241d0006a41186a290300370000200341106a200241d0006a41106a290300370000200341086a200241d0006a41086a2903003700002002200541206a2203360210200228027821050240200228020820036b20014f0d00200241086a2003200110ab86808000200228020c2104200228021021030b200420036a2005200110848e8080001a200320016a21012002280208210302402002280274450d00200541002802c0a3c68000118080808000000b200241086a2004200110e4888080000240024020022903084200520d002000420037030820004200370300200041106a428080808080808080807f370300200041186a4200370300200041206a4200370300200041286a4200370300200041306a4200370300200041386a41003602000c010b20002002290310370300200041386a200241c8006a290300370300200041306a200241086a41386a290300370300200041286a200241086a41306a290300370300200041206a200241086a41286a290300370300200041186a200241086a41206a290300370300200041106a200241086a41186a290300370300200041086a200241086a41106a2903003703000b02402003450d00200441002802c0a3c68000118080808000000b20024190016a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200310b280808000000b920601057f23808080800041f0006b22022480808080002002410c6a41f9a6c48000410641002802a0a3c68000118580808000002002410c6a41106a220341d49ec38000410941002802a0a3c6800011858080800000200241306a41186a2002410c6a41186a290000370300200241306a41106a2003290000370300200241306a41086a2002410c6a41086a2900003703002002200229000c37033020022001370360200241e8006a200241e0006a410841002802a8a3c68000118580808000002002200241e0006a41086a3602182002200241e8006a41086a3602102002200241e0006a3602142002200241e8006a36020c200241d4006a2002410c6a10818280800002400240024002400240200228025c220441206a22050d0020024100360214200242808080801037020c0c010b2005417f4c0d024100210641002d00fca3c680001a200541002802c8a3c68000118180808000002203450d0320024100360214200220033602102002200536020c20044160490d010b2002410c6a4100412010ab8680800020022802102103200228021421060b200320066a22052002290330370000200541186a200241306a41186a290300370000200541106a200241306a41106a290300370000200541086a200241306a41086a2903003700002002200641206a2205360214200228025821060240200228020c20056b20044f0d002002410c6a2005200410ab8680800020022802102103200228021421050b200320056a2006200410848e8080001a200520046a2104200228020c210502402002280254450d00200641002802c0a3c68000118080808000000b2002410c6a2003200410dd888080000240024020022d000c0d0020004200370000200041186a4200370000200041106a4200370000200041086a42003700000c010b2000200229000d370000200041186a200241256a290000370000200041106a2002411d6a290000370000200041086a200241156a2900003700000b02402005450d00200341002802c0a3c68000118080808000000b200241f0006a2480808080000f0b10ae80808000000b4101200510b280808000000bee0501057f23808080800041e0006b2202248080808000200241346a41f5a6c48000410441002802a0a3c6800011858080800000200241346a41106a220341f89ec38000411141002802a0a3c6800011858080800000200241086a41186a200241346a41186a290000370300200241086a41106a2003290000370300200241086a41086a200241346a41086a2900003703002002200229003437030820022001280200360254200241d8006a200241d4006a410441002802a8a3c68000118580808000002002200241d4006a41046a3602402002200241d8006a41086a3602382002200241d4006a36023c2002200241d8006a360234200241286a200241346a108182808000024002400240024002402002280230220341206a22040d002002410036023c20024280808080103702340c010b2004417f4c0d024100210541002d00fca3c680001a200441002802c8a3c68000118180808000002201450d032002410036023c200220013602382002200436023420034160490d010b200241346a4100412010ab8680800020022802382101200228023c21050b200120056a22042002290308370000200441186a200241086a41186a290300370000200441106a200241086a41106a290300370000200441086a200241086a41086a2903003700002002200541206a220436023c200228022c21050240200228023420046b20034f0d00200241346a2004200310ab8680800020022802382101200228023c21040b200120046a2005200310848e8080001a200420036a21032002280234210602402002280228450d00200541002802c0a3c68000118080808000000b200241346a2001200310e1888080000240024020022802342204418080808078470d0020004201370204410021040c010b2001200341002802a0a1c6800011848080800000200020022902383702040b2000200436020002402006450d00200141002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200410b280808000000b910601067f23808080800041e0006b2202248080808000200241306a41f9a6c48000410641002802a0a3c6800011858080800000200241306a41106a220341d49ec38000410941002802a0a3c6800011858080800000200241186a200241306a41186a290000370300200241106a2003290000370300200241086a200241306a41086a2900003703002002200229003037030020022000370350200241d8006a200241d0006a410841002802a8a3c68000118580808000002002200241d0006a41086a36023c2002200241d8006a41086a3602342002200241d0006a3602382002200241d8006a360230200241246a200241306a108182808000024002400240024002400240200228022c220441206a22030d002002410036023820024280808080103702300c010b2003417f4c0d024100210541002d00fca3c680001a200341002802c8a3c68000118180808000002206450d0320024100360238200220063602342002200336023020044160490d010b200241306a4100412010ab8680800020022802342106200228023821050b200620056a22032002290300370000200341186a200241186a290300370000200341106a200241106a290300370000200341086a200241086a2903003700002002200541206a2205360238200228022821030240200228023020056b20044f0d00200241306a2005200410ab8680800020022802342106200228023821050b200620056a2003200410848e8080001a2002280230210702402002280224450d00200341002802c0a3c68000118080808000000b41002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0220032001290000370000200341186a200141186a290000370000200341106a200141106a290000370000200341086a200141086a2900003700002006200520046a2003412041002802e0a1c6800011868080800000200341002802c0a3c680001180808080000002402007450d00200641002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200310b280808000000b4101412010b280808000000bd90801077f23808080800041e0006b2202248080808000200241306a41f9a6c48000410641002802a0a3c6800011858080800000200241306a41106a220341ea9ec38000410741002802a0a3c6800011858080800000200241186a200241306a41186a290000370300200241106a2003290000370300200241086a200241306a41086a2900003703002002200229003037030041002d00fca3c680001a0240024002400240412041002802c8a3c68000118180808000002204450d0020042000290000370000200441186a200041186a290000370000200441106a200041106a290000370000200441086a200041086a290000370000200241306a200441204100280290a3c68000118580808000002002200441206a36025c20022004360258200220033602542002200241306a360250200241246a200241d0006a108182808000200441002802c0a3c6800011808080800000024002400240200228022c220041206a22030d002002410036023820024280808080103702300c010b2003417f4c0d034100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d0420024100360238200220043602342002200336023020004160490d010b200241306a4100412010ab8680800020022802342104200228023821050b200420056a22032002290300370000200341186a200241186a290300370000200341106a200241106a290300370000200341086a200241086a2903003700002002200541206a2203360238200228022821050240200228023020036b20004f0d00200241306a2003200010ab8680800020022802342104200228023821030b200420036a2005200010848e8080001a2002280230210602402002280224450d00200541002802c0a3c68000118080808000000b41002d00fca3c680001a413c41002802c8a3c68000118180808000002205450d03200220053602342002413c3602302005200129030037000020024108360238200128023021074108210502402002280230417c714108470d00200241306a4108410410b182808000200228023821050b200228023420056a20073600002002200541046a2205360238200128023421070240200228023020056b41034b0d00200241306a2005410410b182808000200228023821050b200320006a2103200141086a2108200228023420056a20073600002002200541046a2200360238200128023821010240200228023020006b41034b0d00200241306a2000410410b182808000200228023821000b200228023420006a20013600002002200041046a3602382008200241306a10848d808000200228023021012004200320022802342200200228023841002802e0a1c680001186808080000002402001450d00200041002802c0a3c68000118080808000000b02402006450d00200441002802c0a3c68000118080808000000b200241e0006a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200310b280808000000b4101413c10b280808000000bc70501047f23808080800041e0006b2202248080808000200241346a41f9a6c48000410641002802a0a3c6800011858080800000200241346a41106a220341899fc38000410d41002802a0a3c6800011858080800000200241086a41186a200241346a41186a290000370300200241086a41106a2003290000370300200241086a41086a200241346a41086a2900003703002002200229003437030820022000360254200241d8006a200241d4006a410441002802a8a3c68000118580808000002002200241d4006a41046a3602402002200241d8006a41086a3602382002200241d4006a36023c2002200241d8006a360234200241286a200241346a108182808000024002400240024002402002280230220341206a22040d002002410036023c20024280808080103702340c010b2004417f4c0d024100210541002d00fca3c680001a200441002802c8a3c68000118180808000002200450d032002410036023c200220003602382002200436023420034160490d010b200241346a4100412010ab8680800020022802382100200228023c21050b200020056a22042002290308370000200441186a200241086a41186a290300370000200441106a200241086a41106a290300370000200441086a200241086a41086a2903003700002002200541206a220436023c200228022c21050240200228023420046b20034f0d00200241346a2004200310ab8680800020022802382100200228023c21040b200020046a2005200310848e8080001a200420036a21032002280234210402402002280228450d00200541002802c0a3c68000118080808000000b2001280204220520012802082000200310d58580800002402004450d00200041002802c0a3c68000118080808000000b02402001280200450d00200541002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200410b280808000000b810801087f23808080800041e0006b2202248080808000200241346a41f5a6c48000410441002802a0a3c6800011858080800000200241346a41106a220341f89ec38000411141002802a0a3c6800011858080800000200241086a41186a200241346a41186a290000370300200241086a41106a2003290000370300200241086a41086a200241346a41086a2900003703002002200229003437030820022000280200360254200241d8006a200241d4006a410441002802a8a3c68000118580808000002002200241d4006a41046a3602402002200241d8006a41086a3602382002200241d4006a36023c2002200241d8006a360234200241286a200241346a1081828080000240024002400240024002402002280230220041206a22030d002002410036023c20024280808080103702340c010b2003417f4c0d024100210441002d00fca3c680001a200341002802c8a3c68000118180808000002205450d032002410036023c200220053602382002200336023420004160490d010b200241346a4100412010ab8680800020022802382105200228023c21040b200520046a22032002290308370000200341186a200241086a41186a290300370000200341106a200241086a41106a290300370000200341086a200241086a41086a2903003700002002200441206a220436023c200228022c21030240200228023420046b20004f0d00200241346a2004200010ab8680800020022802382105200228023c21040b200520046a2003200010848e8080001a2002280234210602402002280228450d00200341002802c0a3c68000118080808000000b20012802082203417f4c0d0041002d00fca3c680001a2003410574410472220741002802c8a3c68000118180808000002208450d02200420006a21092002410036023c200220083602382002200736023420012802042100200220033602282002200241286a360208200241086a200241346a10c08a8080000240024020030d00200228023c2103200228023821070c010b20034105742101200228023c210303400240200228023420036b411f4b0d00200241346a2003412010b182808000200228023c21030b2002280238220720036a22042000290000370000200441086a200041086a290000370000200441106a200041106a290000370000200441186a200041186a2900003700002002200341206a220336023c200041206a2100200141606a22010d000b0b20022802342100200520092007200341002802e0a1c680001186808080000002402000450d00200741002802c0a3c68000118080808000000b02402006450d00200541002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200310b280808000000b4101200710b280808000000ba20801097f23808080800041e0006b2202248080808000200241346a41f5a6c48000410441002802a0a3c6800011858080800000200241346a41106a220341f89ec38000411141002802a0a3c6800011858080800000200241086a41186a200241346a41186a290000370300200241086a41106a2003290000370300200241086a41086a200241346a41086a2900003703002002200229003437030820022000280200360254200241d8006a200241d4006a410441002802a8a3c68000118580808000002002200241d4006a41046a3602402002200241d8006a41086a3602382002200241d4006a36023c2002200241d8006a360234200241286a200241346a1081828080000240024002400240024002402002280230220041206a22030d002002410036023c20024280808080103702340c010b2003417f4c0d024100210441002d00fca3c680001a200341002802c8a3c68000118180808000002205450d032002410036023c200220053602382002200336023420004160490d010b200241346a4100412010ab8680800020022802382105200228023c21040b200520046a22032002290308370000200341186a200241086a41186a290300370000200341106a200241086a41106a290300370000200341086a200241086a41086a2903003700002002200441206a220436023c200228022c21030240200228023420046b20004f0d00200241346a2004200010ab8680800020022802382105200228023c21040b200520046a2003200010848e8080001a2002280234210602402002280228450d00200341002802c0a3c68000118080808000000b20012802082203417f4c0d0041002d00fca3c680001a2003410574410472220741002802c8a3c68000118180808000002208450d02200420006a21092002410036023c20022008360238200220073602342001280204210a200220033602282002200241286a360208200241086a200241346a10c08a8080000240024020030d00200228023c2103200228023821080c010b20034105742107200228023c2103200a210003400240200228023420036b411f4b0d00200241346a2003412010b182808000200228023c21030b2002280238220820036a22042000290000370000200441086a200041086a290000370000200441106a200041106a290000370000200441186a200041186a2900003700002002200341206a220336023c200041206a2100200741606a22070d000b0b20022802342100200520092008200341002802e0a1c680001186808080000002402000450d00200841002802c0a3c68000118080808000000b02402006450d00200541002802c0a3c68000118080808000000b02402001280200450d00200a41002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200310b280808000000b4101200710b280808000000bc60906017f037e037f017e017f027e2380808080004190016b2202248080808000200141106a29030021032001290308210420013502002105200241286a41f9a6c48000410641002802a0a3c6800011858080800000200241286a41106a220641ea9ec38000410741002802a0a3c6800011858080800000200241186a200241286a41186a290000370300200241106a2006290000370300200241086a200241286a41086a2900003703002002200229002837030041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002207450d0020072000290000370000200741186a200041186a290000370000200741106a200041106a290000370000200741086a200041086a290000370000200241286a200741204100280290a3c68000118580808000002002200741206a36028c01200220073602880120022006360284012002200241286a36028001200241f4006a20024180016a108182808000200741002802c0a3c6800011808080800000024002400240200228027c220041206a22060d002002410036023020024280808080103702280c010b2006417f4c0d034100210841002d00fca3c680001a200641002802c8a3c68000118180808000002207450d04200241003602302002200736022c2002200636022820004160490d010b200241286a4100412010ab86808000200228022c2107200228023021080b200720086a22062002290300370000200641186a200241186a290300370000200641106a200241106a290300370000200641086a200241086a2903003700002002200841206a2206360230200228027821080240200228022820066b20004f0d00200241286a2006200010ab86808000200228022c2107200228023021060b200720066a2008200010848e8080001a200620006a21002002280228210602402002280274450d00200841002802c0a3c68000118080808000000b200241286a2007200010e488808000420021090240024020022903284200520d00200241206a4100360200200242003703180c010b200241086a200241d0006a290300370300200241106a200241d8006a290300370300200241186a200241e0006a290300370300200241206a200241e8006a2802003602002002200241286a41206a290300370300200241ec006a280200210a200229033021090b4200210b0240024020054200520d00200241386a4200370300200241306a420037030020024200370328428080808080808080807f21030c010b200241386a200141286a290300370300200241306a200141206a290300370300200220012903183703282004210b0b200241106a200241286a41106a22012903002205370300200241086a200241286a41086a2208290300220437030020022002290328220c370300200241286a41186a20033703002001200b370300200241286a41206a200c370300200241d0006a2004370300200241d8006a2005370300200241e0006a200241186a290300370300200241e8006a200241206a280200360200200241ec006a200a360200200220093703302002420137032820082007200010f58780800002402006450d00200741002802c0a3c68000118080808000000b20024190016a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200610b280808000000bf70a03057f047e037f2380808080004190016b2201248080808000200141086a41f9a6c48000410641002802a0a3c6800011858080800000200141086a41106a220241ea9ec38000410741002802a0a3c6800011858080800000200141d0006a41186a200141086a41186a290000370300200141d0006a41106a2002290000370300200141d0006a41086a200141086a41086a2900003703002001200129000837035041002d00fca3c680001a02400240024002400240412041002802c8a3c68000118180808000002203450d0020032000290000370000200341186a200041186a290000370000200341106a200041106a290000370000200341086a200041086a290000370000200141086a200341204100280290a3c68000118580808000002001200341206a36028c01200120033602880120012002360284012001200141086a36028001200141f4006a20014180016a108182808000200341002802c0a3c6800011808080800000024002400240200128027c220041206a22020d002001410036021020014280808080103702080c010b2002417f4c0d034100210441002d00fca3c680001a200241002802c8a3c68000118180808000002203450d04200141003602102001200336020c2001200236020820004160490d010b200141086a4100412010ab86808000200128020c2103200128021021040b200320046a22022001290350370000200241186a200141d0006a41186a290300370000200241106a200141d0006a41106a290300370000200241086a200141d0006a41086a2903003700002001200441206a2202360210200128027821040240200128020820026b20004f0d00200141086a2002200010ab86808000200128020c2103200128021021020b200320026a2004200010848e8080001a200220006a21002001280208210502402001280274450d00200441002802c0a3c68000118080808000000b200141086a2003200010e48880800042002106024020012903084200520d00200141e0006a4200370300200141d8006a420037030020014200370350428080808080808080807f210742002108420021090c040b200141d8006a200141306a290300370300200141d0006a41106a200141386a2903003703002001200141286a290300370350200141206a2903002107200141086a41106a2903002108200141c4006a2902002106200141cc006a280200210420012903102109200141c0006a2802002202450d032002417f6a21020c040b4101412010b280808000000b10ae80808000000b4101200210b280808000000b410021020240417f4100280284a4c68000220a410147200a41014b1b220a417f460d00200a41ff01710d010b410021024100280290a1c68000210a410028028ca1c68000210b4100280280a4c68000210c200141c0006a42003702002001413c6a418097c38000360200200141386a4101360200200141306a410f3602002001412c6a41a89cc38000360200200141206a41c49ac38000ad4280808080b00b84370200200141146a41aa9bc38000ad4280808080c00184370200200141a09cc38000360234200141013602282001410036021c2001410036021020014281808080d0ce01370208200b41ecf2c08000200c410246220c1b200141086a200a41d4f2c08000200c1b280210118480808000000b200141206a2007370300200141086a41106a2008370300200141286a2001290350370300200141cc006a2004360200200141c4006a2006370200200141c0006a2002360200200141306a200141d0006a41086a290300370300200141386a200141d0006a41106a2903003703002001200937031020014201370308200141086a41086a2003200010f58780800002402005450d00200341002802c0a3c68000118080808000000b20014190016a2480808080000bfc0903057f037e027f2380808080004190016b2202248080808000200241c0006a41f9a6c48000410641002802a0a3c6800011858080800000200241c0006a41106a220341ea9ec38000410741002802a0a3c6800011858080800000200241186a200241c0006a41186a290000370300200241106a2003290000370300200241086a200241c0006a41086a2900003703002002200229004037030041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002204450d0020042000290000370000200441186a200041186a290000370000200441106a200041106a290000370000200441086a200041086a290000370000200241c0006a200441204100280290a3c68000118580808000002002200441206a36023c20022004360238200220033602342002200241c0006a360230200241246a200241306a108182808000200441002802c0a3c6800011808080800000024002400240200228022c220041206a22030d002002410036024820024280808080103702400c010b2003417f4c0d034100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d0420024100360248200220043602442002200336024020004160490d010b200241c0006a4100412010ab8680800020022802442104200228024821050b200420056a22032002290300370000200341186a200241186a290300370000200341106a200241106a290300370000200341086a200241086a2903003700002002200541206a2203360248200228022821050240200228024020036b20004f0d00200241c0006a2003200010ab8680800020022802442104200228024821030b200420036a2005200010848e8080001a200320006a21002002280240210602402002280224450d00200541002802c0a3c68000118080808000000b200241c0006a2004200010e488808000420021070240024020022903404200520d0041002103200241186a4100360200200241106a4200370300200241086a420037030020024200370300428080808080808080807f210842002109410021050c010b200241086a200241e8006a290300370300200241106a200241f0006a290300370300200241186a200241f8006a2802003602002002200241e0006a290300370300200241c0006a41186a2903002108200241c0006a41106a290300210720024184016a280200210a20024180016a2802002103200241fc006a2802002105200229034821090b024002402005200372220b0d00200241c9006a2001290000370000200241e1006a200141186a290000370000200241d9006a200141106a290000370000200241d1006a200141086a290000370000200241033a0048200241163a0040200241c0006a108e8a808000410121010c010b200541016a2205417f20051b21010b200b4100472105200241c0006a41186a2008370300200241c0006a41106a2007370300200241e0006a200229030037030020024184016a200a36020020024180016a2003360200200241fc006a2001360200200241e8006a200241086a290300370300200241f0006a200241106a290300370300200241f8006a200241186a2802003602002002200937034820024201370340200241c0006a41086a2004200010f58780800002402006450d00200441002802c0a3c68000118080808000000b20024190016a24808080800020050f0b4101412010b280808000000b10ae80808000000b4101200310b280808000000b960501057f23808080800041e0006b2201248080808000200141306a41f9a6c48000410641002802a0a3c6800011858080800000200141306a41106a220241d49ec38000410941002802a0a3c6800011858080800000200141186a200141306a41186a290000370300200141106a2002290000370300200141086a200141306a41086a2900003703002001200129003037030020012000370350200141d8006a200141d0006a410841002802a8a3c68000118580808000002001200141d0006a41086a36023c2001200141d8006a41086a3602342001200141d0006a3602382001200141d8006a360230200141246a200141306a10818280800002400240024002400240200128022c220341206a22040d002001410036023820014280808080103702300c010b2004417f4c0d024100210541002d00fca3c680001a200441002802c8a3c68000118180808000002202450d0320014100360238200120023602342001200436023020034160490d010b200141306a4100412010ab8680800020012802342102200128023821050b200220056a22042001290300370000200441186a200141186a290300370000200441106a200141106a290300370000200441086a200141086a2903003700002001200541206a2204360238200128022821050240200128023020046b20034f0d00200141306a2004200310ab8680800020012802342102200128023821040b200220046a2005200310848e8080001a200420036a21032001280230210402402001280224450d00200541002802c0a3c68000118080808000000b2002200341002802a0a1c680001184808080000002402004450d00200241002802c0a3c68000118080808000000b200141e0006a2480808080000f0b10ae80808000000b4101200410b280808000000bf60501047f23808080800041e0006b2201248080808000200141306a41f9a6c48000410641002802a0a3c6800011858080800000200141306a41106a220241ea9ec38000410741002802a0a3c6800011858080800000200141186a200141306a41186a290000370300200141106a2002290000370300200141086a200141306a41086a2900003703002001200129003037030041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002203450d0020032000290000370000200341186a200041186a290000370000200341106a200041106a290000370000200341086a200041086a290000370000200141306a200341204100280290a3c68000118580808000002001200341206a36025c20012003360258200120023602542001200141306a360250200141246a200141d0006a108182808000200341002802c0a3c6800011808080800000024002400240200128022c220041206a22020d002001410036023820014280808080103702300c010b2002417f4c0d034100210441002d00fca3c680001a200241002802c8a3c68000118180808000002203450d0420014100360238200120033602342001200236023020004160490d010b200141306a4100412010ab8680800020012802342103200128023821040b200320046a22022001290300370000200241186a200141186a290300370000200241106a200141106a290300370000200241086a200141086a2903003700002001200441206a2202360238200128022821040240200128023020026b20004f0d00200141306a2002200010ab8680800020012802342103200128023821020b200320026a2004200010848e8080001a200220006a21002001280230210202402001280224450d00200441002802c0a3c68000118080808000000b2003200041002802a0a1c680001184808080000002402002450d00200341002802c0a3c68000118080808000000b200141e0006a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200210b280808000000b170041b299c38000410f41b89dc3800010f880808000000b170041b299c38000410f41c89dc3800010f880808000000b170041b299c38000410f41d89dc3800010f880808000000b170041b299c38000410f41e89dc3800010f880808000000b170041b299c38000410f41f89dc3800010f880808000000b170041b299c38000410f41889ec3800010f880808000000b170041b299c38000410f41989ec3800010f880808000000b170041b299c38000410f41a89ec3800010f880808000000bae08020a7f017e23808080800041306b2203248080808000024020002802402204418080808078460d0002400240200041cc006a28020022050d0041002105410021060c010b200320053602242003410036022020032005360214200341003602102003200041d0006a280200220536022820032005360218200041d4006a2802002106410121050b2003200636022c2003200536021c2003200536020c2003410c6a10928d8080002004450d00200041c4006a28020041002802c0a3c68000118080808000000b2000418080808078360240024002400240024002400240024020002802200d00200228020021052002280208210620004100360220200041286a2204200429030042017c37030020002802300d014100210420004100360230200041386a22072007290300410020062005418080808078461bad7c3703000240024020002d00d8020d000c010b024020002802a4012208450d00200041a8016a28020021090340200841a07e6a210a200841a4136a210420082f01aa14220b410c6c2105417f2107024002400340024020050d00200b21070c020b200441086a2106200441046a210c200541746a2105200741016a2107200a41e0016a210a2004410c6a2104417f41b99ec38000200c28020020062802002206411020064110491b10888e808000220c411020066b200c1b220641004720064100481b22064101460d000b200641ff0171450d010b2009450d022009417f6a2109200820074102746a41ac146a28020021080c010b0b200a41086a280200200a2802002204200441054b22041b2205450d0402402005412c6c200a41046a2205280200200520041b6a41546a2204280208220541014b0d0020042802042107200428021c210602402004280200220a450d0020062007460d010b2003200441106a36020c2003410c6a200a20072006108e858080002004200636020420044101360200200428020821050b02400240024002402005417e6a2205410220054102491b0e03020100020b200441106a21060c020b20002802000d07200041003602002000200029030842017c37030820002802100d08200041003602100c020b2004410c6a21060b20002802000d07200641086a350200210d41002105200041003602002000200029030842017c37030820002802100d0820004100360210200041186a22042004290300200d7c37030041012104410121070240200641086a2802004104490d00200641046a2802002800002105410021070b2007450d010b41012104417f21050b200041a4016a2001200220042005109085808000200341306a2480808080000f0b41ecb8c28000108781808000000b41dcb8c28000108781808000000b41fcb8c2800041fc0041f8bac2800010a181808000000b41ccb8c28000108781808000000b41bcb8c28000108781808000000b41ccb8c28000108781808000000b41bcb8c28000108781808000000bd51501137f23808080800041f0036b2204248080808000024002400240024002402001280240418080808078460d00200041186a20014190016a290000370000200041106a20014188016a290000370000200041086a20014180016a2900003700002000200141f8006a290000370000410121050c010b41002d00fca3c680001a20012802a4012106200141a0016a28020021072001419c016a2802002108200141ac016a2802002109200141a8016a280200210a2001280298012105410141002802c8a3c6800011818080800000220b450d01200b41003a000020044180016a41186a220c419380c6800041014100280298a3c680001185808080000020044194016a410036020020044201370288012004200b360284012004410136028001200441003602c00120044280808080c0003702b8014104210b0240024020050d004100210d0c010b4100210d2007450d00200441ac026a210e200441c4026a210f200441a0026a41017221104100210d4100210b034002400240200b450d002008211120052112200b21050c010b4100211102402008450d002008210b024020084107712212450d000340200b417f6a210b20052802ac0921052012417f6a22120d000b0b20084108490d00034020052802ac092802ac092802ac092802ac092802ac092802ac092802ac092802ac092105200b41786a220b0d000b0b410021120b0240201120052f01aa09490d00034020052802a008220b450d06201241016a211220052f01a8092111200b21052011200b2f01aa094f0d000b0b201141016a210802400240024020120d002005210b0c010b200520084102746a41ac096a280200210b410021082012417f6a2213450d002012417e6a2114024020134107712212450d0003402013417f6a2113200b2802ac09210b2012417f6a22120d000b0b024020144107490d000340200b2802ac092802ac092802ac092802ac092802ac092802ac092802ac092802ac09210b201341786a22130d000b0b2005450d010b2005201141e0006c6a22112802082112201128020021052004201128020422133602e001200420053602dc01200441003602d801200420133602d001200420053602cc01200441003602c80120042012410020051b3602e4012004200541004722053602d401200420053602c401200441a0026a2002201141d4006a2205200441c4016a200310888d808000200441286a41186a2212201041186a290000370300200441286a41106a2213201041106a290000370300200441286a41086a220d201041086a290000370300200441e8016a41086a200f41086a290200370300200441e8016a41106a200f41106a290200370300200441e8016a41186a200f41186a290200370300200441e8016a41206a200f41206a290200370300200441e8016a41286a200f41286a290200370300200441e8016a41306a200f41306a290200370300200420102900003703282004200f2902003703e80120042d00a0022111200441fc026a200510a08580800020044180016a200441e8016a10ac8d8080000240024020110d00200441a0026a41086a2214200441fc026a41086a280200360200200420042902fc023703a00241002d00fca3c680001a2004410036029003200442808080801037028803412041002802c8a3c68000118180808000002205450d0820052004290328370000200541186a22152012290300370000200541106a22162013290300370000200541086a2213200d29030037000020044188036a4100412010b182808000200428028c0320044188036a41086a2212280200220d6a22112005290000370000201141086a2013290000370000201141106a2016290000370000201141186a20152900003700002012200d41206a360200200541002802c0a3c6800011808080800000200e41086a2012280200360200200e200429028803370200024020042802c001220520042802b801470d00200441b8016a200510a68680800020042802c00121050b20042802bc01200541186c6a221120042903a002370200201141086a2014290300370200201141106a200441a0026a41106a290300370200200541016a210d0c010b024020042802c001220520042802b801470d00200441b8016a200510a68680800020042802c00121050b20042802bc01200541186c6a220520042902fc02370200200541808080807836020c200541086a200441fc026a41086a28020036020020042802c00141016a210d0b2004200d3602c001410021052007417f6a22070d010b0b20042802bc01210b0b2004200b3602e403200441c0036a41286a200b200d41186c6a36020020042009410020061b3602e0032004200a3602dc03200420063602d803200441003602d4032004200641004722053602d0032004200a3602cc03200420063602c803200441003602c403200420053602c003200441a0026a2002200441c0036a200310868d808000200441286a41086a200441a0026a41086a290200370300200441286a41106a200441a0026a41106a290200370300200441286a41186a200441a0026a41186a29020037030020044188036a41086a200441a0026a41286a29020037030020044188036a41106a200441a0026a41306a29020037030020044188036a41186a200441a0026a41386a29020037030020044188036a41206a200441a0026a41c0006a29020037030020044188036a41286a200441a0026a41c8006a29020037030020044188036a41306a200441a0026a41d0006a290200370300200420042902a002370328200420042902c0023703880320044180016a20044188036a10ac8d808000200441286a41d0006a20044180016a41306a290200370300200441286a41c8006a20044180016a41286a290200370300200441286a41c0006a20044180016a41206a290200370300200441286a41386a200c290200370300200441286a41306a20044180016a41106a290200370300200441286a41286a20044180016a41086a2902003703002004200429028001370348024020042802c001220b450d0020042802bc012105034002402005280200450d00200541046a28020041002802c0a3c68000118080808000000b02402005410c6a2802002211418080808078460d002011450d00200541106a28020041002802c0a3c68000118080808000000b200541186a2105200b417f6a220b0d000b0b200141c0006a2113200441286a41206a2105024020042802b801450d0020042802bc0141002802c0a3c68000118080808000000b200441086a41086a220b200441286a41086a290300370300200441086a41106a2211200441286a41106a290300370300200441086a41186a2212200441286a41186a29030037030020042004290328370308200441a0026a41306a200541306a290200370300200441a0026a41286a200541286a290200370300200441a0026a41206a200541206a290200370300200441a0026a41186a200541186a290200370300200441a0026a41106a200541106a290200370300200441a0026a41086a200541086a290200370300200420052902003703a002200441e0026a200b290300370300200441e8026a2011290300370300200441f0026a2012290300370300200420042903083703d8022013200441a0026a41d80010848e8080001a200041186a2012290300370000200041106a2011290300370000200041086a200b29030037000020002004290308370000410021050b200020053a0020200441f0036a2480808080000f0b4101410110b280808000000b419cd0c2800010a081808000000b4101412010b280808000000bd905020a7f017e23808080800041106b2204248080808000024002400240024002400240024020012802a40122050d00410021060c010b200141a8016a28020021070340200541a07e6a2108200541a4136a210620052f01aa142209410c6c210a417f210b0240024003400240200a0d002009210b0c020b200641086a210c200641046a210d200a41746a210a200b41016a210b200841e0016a21082006410c6a2106417f2002200d2802002003200c280200220c2003200c491b10888e808000220d2003200c6b200d1b220c410047200c4100481b220c4101460d000b200c41ff0171450d010b024020070d00410021060c030b2007417f6a21072005200b4102746a41ac146a28020021050c010b0b200841086a28020020082802002206200641054b22061b220a450d010240200a412c6c200841046a220a280200200a20061b6a41546a2206280208220a41014b0d002006280204210c200628021c210302402006280200220b450d002003200c460d010b2004200641106a36020c2004410c6a200b200c2003108e8580800020062003360204200641013602002006280208210a0b02400240024002400240200a417e6a220a4102200a4102491b0e03000301000b2006410c6a21060c010b200641106a21060b20012802000d04200641086a350200210e200141003602002001200129030842017c37030820012802100d0520014100360210200141186a220a200a290300200e7c370300200641086a280200210a200641046a28020021060c010b20012802000d0541002106200141003602002001200129030842017c37030820012802100d06200141003602100b20002006360204200041086a200a360200410121060b20002006360200200441106a2480808080000f0b41fcb8c2800041fc0041f8bac2800010a181808000000b41ccb8c28000108781808000000b41bcb8c28000108781808000000b41ccb8c28000108781808000000b41bcb8c28000108781808000000bab0301037f23808080800041206b2203248080808000410121040240200028020c220541c000490d0041022104200541808001490d00410441052005418080808004491b21040b41002d00fca3c680001a0240200441002802c8a3c68000118180808000002205450d00200341003602102003200536020c2003200436020820032000410c6a360214200341146a200341086a10c08a808000024002402000280200418080808078470d00200341146a2000280204200041086a28020010b4828080000c010b200341146a2000280204200028020810e6848080000b2003280218210502402003280208200328021022006b200328021c22044f0d00200341086a2000200410b182808000200328021021000b200328020c20006a2005200410848e8080001a2003200020046a220036021002402003280214450d00200541002802c0a3c6800011808080800000200328021021000b2003280208210420012002200328020c2205200041002802e0a1c680001186808080000002402004450d00200541002802c0a3c68000118080808000000b200341206a2480808080000f0b4101200410b280808000000bba0301087f23808080800041106b220324808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002204450d002003410036020c200320043602082003410136020441002d00fca3c680001a412041002802c8a3c68000118180808000002204450d0120042000290001370000200441186a2205200041196a290000370000200441106a2206200041116a290000370000200441086a2207200041096a290000370000200341046a4100412010b18280800020032802082208200328020c22096a220a2004290000370000200a41086a2007290000370000200a41106a2006290000370000200a41186a20052900003700002003200941206a220a36020c200441002802c0a3c680001180808080000020002d00002100024020032802042204200a470d00200341046a200a410110b1828080002003280208210820032802042104200328020c210a0b2008200a6a20003a0000200120022008200a41016a41002802e0a1c680001186808080000002402004450d00200841002802c0a3c68000118080808000000b200341106a2480808080000f0b4101410110b280808000000b4101412010b280808000000bc70201057f23808080800041106b2203248080808000410521044100210502400240200028020022060e03010000010b41012105410121040b41002d00fca3c680001a0240200441002802c8a3c68000118180808000002207450d002003200736020820032004360204410121040240024002400240024020060e03000201000b200741003a0000410121042003410136020c200028020421064100210002402005450d00200341046a4101410410b182808000200328020445210020032802082107200328020c21040b200720046a2006360000200120022007200441046a41002802e0a1c68000118680808000002000450d020c030b410221040b200720043a0000200120022007410141002802e0a1c68000118680808000000b200741002802c0a3c68000118080808000000b200341106a2480808080000f0b4101200410b280808000000bca0101057f23808080800041206b220124808080800041002102200141003602102001428080808010370208200141146a10958880800041012103200128021821040240200128021c2205450d00200141086a4100200510b182808000200128020c2103200128021021020b200320026a2004200510848e8080001a2001200220056a36021002402001280214450d00200441002802c0a3c68000118080808000000b20002001290208370200200041086a200141086a41086a280200360200200141206a2480808080000b900201047f23808080800041106b2202248080808000200128020041027441f8adc380006a28020021030240024020012d00084116470d00200141106a10a18a80800021040c010b200141086a10a98c80800021040b02400240417f417f200420036a41016a220420042003491b2203200141e0006a2802004105746a41046a220420042003491b2203417f4c0d0041002d00fca3c680001a200341002802c8a3c68000118180808000002204450d01200241046a41086a2205410036020020022004360208200220033602042001200241046a10ef87808000200041086a200528020036020020002002290204370200200241106a2480808080000f0b10ae80808000000b4101200310b280808000000bcb0401097f23808080800041206b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341053a000041002d00fca3c680001a2002410036021c2002428080808010370214412041002802c8a3c68000118180808000002204450d0120044200370000200441186a22054200370000200441106a22064200370000200441086a22074200370000200241146a4100412010b1828080002002280218200241146a41086a220828020022096a220a2004290000370000200a41086a2007290000370000200a41106a2006290000370000200a41186a20052900003700002008200941206a360200200441002802c0a3c6800011808080800000200241086a41086a220a2008280200360200200220022902143703080240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442c194a6a793ccc3a857370328200442a5e9e3ab9e929adc2c37031020044109360244200441d49ec38000360240200441f581808000360238200441ef808080003602202004410136020c2004200336020820044281808080103703002004200229030837034820042000290200370254200441306a42ab8bffbed784ffa5937f370300200441186a4293888c8f89fdc6ec9e7f370300200441d0006a200a280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241206a2480808080000f0b4101410110b280808000000b4101412010b280808000000bdd0301057f23808080800041306b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341023a00002002410036022020024280808080800137021841002d00fca3c680001a410441002802c8a3c68000118180808000002204450d01200241246a41086a220541003602002002200436022820024104360224200241186a200241246a108187808000200241086a41086a22062005280200360200200220022902243703080240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442a09acee28b90aca44d370328200442c4ccab9c81ebb4b8db0037031020044108360244200441dd9ec380003602402004419d82808000360238200441f0818080003602202004410136020c2004200336020820044281808080103703002004200229030837034820042000290200370254200441306a42f68ebee6f7f7fefdac7f370300200441186a42a4d2928ecac0faa432370300200441d0006a2006280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241306a2480808080000f0b4101410110b280808000000b4101410410b280808000000be90503067f017e027f23808080800041d0006b2201248080808000200141286a419b9fc38000410a41a59fc380004120418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d0020024285cbd9e891b2d0c161370308200241c59fc38000360220200241f18180800036021820024101360204200241ab99c38000360200200241106a4282d2add5d4f1deea6a370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c48000360220200241003602182002419e82808000360210200242cdec93ccd5b69cacdf00370308200242be8af3e3f0cdac8456370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bd70301057f23808080800041206b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341023a000041002d00fca3c680001a410441002802c8a3c68000118180808000002204450d012002410c6a41086a22054100360200200220043602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000200241086a220620052802003602002002200229020c3703000240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442fb92fda592a6fcd31c370328200442c4ccab9c81ebb4b8db0037031020044105360244200441e59ec380003602402004419f82808000360238200441f0818080003602202004410136020c2004200336020820044281808080103703002004200229030037034820042000290200370254200441306a42b584d6d28c82acc02f370300200441186a42a4d2928ecac0faa432370300200441d0006a2006280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241206a2480808080000f0b4101410110b280808000000b4101410410b280808000000b850601057f23808080800041e0006b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341023a0000200241206a428080808080808080807f370300200241286a4200370300200241306a4200370300200241386a4200370300200241c0006a4200370300200241c8006a4100360200200242003703182002420037031041002d00fca3c680001a413c41002802c8a3c68000118180808000002204450d01200220043602582002413c360254200442003700002002410836025c200228024021054108210402402002280254417c714108470d00200241d4006a4108410410b182808000200228025c21040b200228025820046a20053600002002200441046a220436025c200228024421050240200228025420046b41034b0d00200241d4006a2004410410b182808000200228025c21040b200241106a41086a2106200228025820046a20053600002002200441046a220436025c200228024821050240200228025420046b41034b0d00200241d4006a2004410410b182808000200228025c21040b200228025820046a2005360000200241d4006a41086a2205200441046a3602002006200241d4006a10848d808000200241086a22062005280200360200200220022902543703000240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442f8b3f08d85bca5b6e300370328200442c4ccab9c81ebb4b8db0037031020044107360244200441ea9ec38000360240200441a082808000360238200441f0818080003602202004410136020c2004200336020820044281808080103703002004200229030037034820042000290200370254200441306a4291ba8cfeb2d6b48568370300200441186a42a4d2928ecac0faa432370300200441d0006a2006280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241e0006a2480808080000f0b4101410110b280808000000b4101413c10b280808000000bd90301057f23808080800041206b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341023a000041002d00fca3c680001a410441002802c8a3c68000118180808000002204450d012002410c6a41086a22054100360200200220043602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000200241086a220620052802003602002002200229020c3703000240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442fa89d3f6b7fdfb8f9a7f370328200442c4ccab9c81ebb4b8db0037031020044107360244200441f19ec38000360240200441a182808000360238200441f0818080003602202004410136020c2004200336020820044281808080103703002004200229030037034820042000290200370254200441306a42fd8281afbeafaef5807f370300200441186a42a4d2928ecac0faa432370300200441d0006a2006280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241206a2480808080000f0b4101410110b280808000000b4101410410b280808000000be90503067f017e027f23808080800041d0006b2201248080808000200141286a419b9fc38000410a41a59fc380004120418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242ef8683cfe1dddaca63370308200241c59fc38000360220200241f68180800036021820024101360204200241ab99c38000360200200241106a4284b2a2d692ae8580b57f370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c4800036022020024100360218200241a282808000360210200242afc2c78afea4cdde6c370308200242c9aaa6b382c5ade30a370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bd80301057f23808080800041206b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341053a000041002d00fca3c680001a410441002802c8a3c68000118180808000002204450d012002410c6a41086a22054100360200200220043602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000200241086a220620052802003602002002200229020c3703000240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442d6e1dd9492e0e8e8c900370328200442d7c9cb8fc1cf97db3e37031020044111360244200441f89ec38000360240200441a382808000360238200441b5808080003602202004410136020c2004200336020820044281808080103703002004200229030037034820042000290200370254200441306a42efbbaa8aaae9fdc9ab7f370300200441186a42e88488d0c0e3aebc13370300200441d0006a2006280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241206a2480808080000f0b4101410110b280808000000b4101410410b280808000000beb0503067f017e027f23808080800041d0006b2201248080808000200141286a419b9fc38000410a41a59fc380004120418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242c4ccab9c81ebb4b8db00370308200241c59fc38000360220200241f08180800036021820024101360204200241ab99c38000360200200241106a42a4d2928ecac0faa432370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c4800036022020024100360218200241a4828080003602102002429bc594f9e4cbe78bb87f37030820024299f3bffc9a86c097ea00370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b950301037f41002d00fca3c680001a02400240410141002802c8a3c68000118180808000002202450d00200241023a000041002d00fca3c680001a412841002802c8a3c68000118180808000002203450d012003420037001820034200370000200341206a428080808080808080807f370000200341106a4200370000200341086a42003700000240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442c4daf0e7d4cf86cca87f370328200442c4ccab9c81ebb4b8db00370310200441283602502004200336024c200442878080808005370244200441ea9ec380003602402004419982808000360238200441f0818080003602202004410136020c20042002360208200442818080801037030020042000290200370254200441013a0060200441306a42e1be86f0e3e6afc202370300200441186a42a4d2928ecac0faa432370300200441dc006a200041086a2802003602002001200128020841016a3602080f0b4101410110b280808000000b4101412810b280808000000be70301057f23808080800041206b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341023a000041002d00fca3c680001a410441002802c8a3c68000118180808000002204450d012002410c6a41086a22054100360200200220043602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000410841002002410c6a10d385808000200241086a220620052802003602002002200229020c3703000240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a2204428f9acc9be4ab9dde977f370328200442c4ccab9c81ebb4b8db0037031020044105360244200441969fc38000360240200441a582808000360238200441f0818080003602202004410136020c2004200336020820044281808080103703002004200229030037034820042000290200370254200441306a42ba98e4d187d5e0d005370300200441186a42a4d2928ecac0faa432370300200441d0006a2006280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241206a2480808080000f0b4101410110b280808000000b4101410410b280808000000beb0503067f017e027f23808080800041d0006b2201248080808000200141286a419b9fc38000410a41a59fc380004120418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d002002428291fa9e999aa9d246370308200241c59fc38000360220200241ee8180800036021820024101360204200241ab99c38000360200200241106a42bc90c1fdf28f8bb0ff00370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c4800036022020024100360218200241a6828080003602102002429bc4fd96a6cbbfd7ed003703082002429db1fdedf2d2e1d1f500370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bea0503067f017e027f23808080800041d0006b2201248080808000200141286a419b9fc38000410a41a59fc380004120418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d002002429ca0fdd992b0ed90df00370308200241c59fc38000360220200241ed8180800036021820024101360204200241ab99c38000360200200241106a42bbf8f5b8b9e2c39ac400370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c4800036022020024100360218200241a782808000360210200242eab19fb291b0f9fb0c370308200242d39ec9badc8ccad845370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bca0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41c69fc38000411141d79fc380004114418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242c194a6a793ccc3a857370308200241ec9fc38000360220200241f58180800036021820024101360204200241eb9fc38000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a42ab8bffbed784ffa5937f370300200241386a41ef80808000360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024205370224200241f6a4c580003602202002410636021c200241f0a4c58000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002421237022420024187a5c580003602202002410c36021c200241fba4c58000360218200241a882808000360210200242ef88c9d3f691f5f30b370308200242e0c4f8e5b8f790b077370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bde0503057f017e027f23808080800041d0006b2201248080808000200141286a41ed9fc38000410c41d79fc380004114418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242c194a6a793ccc3a857370308200241ec9fc38000360220200241f58180800036021820024101360204200241eb9fc38000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a42ab8bffbed784ffa5937f370300200241386a41ef80808000360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2203200141106a410c6a220241086a28020036020020012002290200370300200128021021042001280214210220012802182105200129022c2106200128022821072001410036021820014280808080c000370210200141c0006a200141106a41f99fc38000410710b88b808000200141346a200141c0006a4180a0c38000410910bf8b8080002001200128023436021820012001280238220836021020012008200128023c41246c6a36021c20012008360214200141c0006a200141106a10fb868080002007418080808078460d012001200436021820012002360210200120023602142001200220054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141c0006a41086a2802003600002000413c6a200637020020002007360238200041013a000020002001290300370350200041d8006a20032802003602002001200129024037001320002001290010370001200041086a200141106a41076a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b2100200128021441d4abc380004114200141186a28020028020c118280808000000b2100200128021441e8abc38000410f200141186a28020028020c118280808000000b920601087f024002402002450d00200120026a2106200341016a210741002108200121090240034002400240200820044b0d0020092d0000210a02402008450d000240024020084101710d002003210b0c010b200320032d0000410874200a72220a200a413a6e220a413a6c6b3a00002007210b0b20084101460d00200320086a210c0340200b200b2d0000410874200a6a220a200a413a6e220a413a6c6b3a0000200b41016a220d200d2d0000410874200a6a220a200a413a6e220a413a6c6b3a0000200b41026a220b200c470d000b0b200a450d0120082004200820044b1b210c034020042008460d040240200c2008460d00200320086a200a200a413a6e220b413a6c6b3a0000200841016a2108200a413a49210d200b210a200d0d030c010b0b200c200441a4a1c3800010f980808000000b200820044194a1c38000109581808000000b200941016a22092006470d000b20082004200820044b1b210a0240034020012d00000d0120042008460d020240200a2008460d00200141016a2101200320086a41003a0000200841016a21082002417f6a22020d010c020b0b200a20044184a1c3800010f980808000000b0240200820044b0d002008450d02200320086a21044100210a024003402003200a6a220d2d0000220b413a4f0d01200d2005200b6a4180016a2d00003a00002008200a41016a220a470d000b4100210a20084102490d042004200841017622016b21024100210a4100210b024020014101460d002008417f6a210d200141feffffff077121064100210b03402003200d6a220c2d00002109200c2003200b6a22042d00003a0000200420093a000020022001200b417e736a6a220c2d00002109200c200441016a22042d00003a0000200420093a0000200d417e6a210d2006200b41026a220b470d000b0b2008410271450d042003200b6a220d2d00002104200d20022001200b417f736a6a220b2d00003a0000200b20043a00000c040b200b413a41f4a0c3800010f980808000000b2008200441e4a0c38000109581808000000b4101210a0c010b410021084100210a0b200020083602042000200a3602000b02000b02000b8d0201037f23808080800041106b2202248080808000410121032001280214418fa5c080004101200141186a28020028020c118280808000002104200241003a0009200220043a0008200220013602042002200036020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041016a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041026a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041036a36020c200241046a2002410c6a41d0cfc48000108e818080001a024020022d00080d00200228020422002802144190a5c080004101200041186a28020028020c1182808080000021030b200241106a24808080800020030ba90901037f23808080800041106b2202248080808000410121032001280214418fa5c080004101200141186a28020028020c118280808000002104200241003a0009200220043a0008200220013602042002200036020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041016a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041026a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041036a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041046a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041056a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041066a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041076a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041086a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041096a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000410a6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000410b6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000410c6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000410d6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000410e6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000410f6a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041106a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041116a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041126a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041136a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041146a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041156a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041166a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041176a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041186a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041196a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000411a6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000411b6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000411c6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000411d6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000411e6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000411f6a36020c200241046a2002410c6a41d0cfc48000108e818080001a024020022d00080d00200228020422002802144190a5c080004101200041186a28020028020c1182808080000021030b200241106a24808080800020030b210020012802144180a2c38000410e200141186a28020028020c118280808000000bf20302037f037e2380808080004180086b22032480808080002003200136028c022003410036028802200320023602840202400240200228020422014108490d002002200141786a220436020420022002280200220541086a3602002004450d00200529000021062002200141776a3602042002200541096a36020002400240024020052d00080e020001030b20034180066a20034184026a10f988808000420021072003290380064200520d0220034188046a20034188066a41f80110848e8080001a0c010b20034180066a20034184026a10f9888080002003290380064200520d0120034188046a20034188066a41f80110848e8080001a420121070b20034190026a20034188046a41f80110848e8080001a200320034184026a10bc8a80800020032802000d0020034180066a20034184026a200328020410d0858080002003280280062201418080808078460d0020033502880621082003280284062105200341086a20034190026a41f80110848e8080001a024020022802040d0020002007370300200041086a200341086a41f80110848e8080001a20002008370390022000200536028c02200020013602880220002006370380020c020b200042023703002001450d01200541002802c0a3c68000118080808000000c010b200042023703000b20034180086a2480808080000b8d0101027f23808080800041306b2202248080808000200241146a42013702002002410236020c2002418ca4c38000360208200241e0818080003602242002411f36022c2002419ca4c38000360228200141186a28020021032002200241206a3602102002200241286a36022020012802142003200241086a10d9808080002101200241306a24808080800020010b8f0302047f017e23808080800041106b22012480808080004102210202400240024020002d0000220341726a0e020201000b20034102744184aec380006a28020021020c010b41014102200041026a2d000022024102491b4102410120024107461b20002d00011b41026a21020b41002d00fca3c680001a0240200241002802c8a3c68000118180808000002204450d002001200436020820012002360204024002402003410f460d00200441003a0000410121022001410136020c02402003410e470d0041012102024020012802044101470d00200141046a4101410110b182808000200128020c21020b200128020820026a41003a00002001200241016a36020c0c020b024020012802044101470d00200141046a4101410110b182808000200128020c21020b200128020820026a41013a00002001200241016a36020c2000200141046a10cc888080000c010b200441013a00002001410136020c200041016a200141046a10aa878080000b20012902082105200141106a24808080800020050f0b4101200210b280808000000bce0203037f017e017f23808080800041206b220124808080800041002d00fca3c680001a0240410141002802c8a3c68000118180808000002202450d002001200236020c200141013602080240024020002802002203418180808078470d00200241003a000042808080801021040c010b200241013a000020014101360210024002402003418080808078470d00200141146a2000280204200041086a28020010b4828080000c010b200141146a2000280204200028020810e6848080000b41012100200128021821050240200128021c2203450d00200141086a4101200310b182808000200128020c2102200128021021000b200220006a2005200310848e8080001a200020036a210002402001280214450d00200541002802c0a3c68000118080808000000b2000ad42208621040b200141206a24808080800020042002ad840f0b4101410110b280808000000b8a0302057f017e23808080800041106b2201248080808000024002400240024002400240024020002802102202418080808078470d004101410220002d000122034102491b4102410120034107461b20002d00001b41026a21030c010b417f417f200041246a280200410c6c417f200041186a280200410c6c2203410c6a22042004200341046a491b22036a41046a220420042003491b220341096a220420042003491b41016a2203450d012003417f4c0d040b4100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d0420012004360208200120033602042002418080808078470d01200441013a00002001410136020c2000200141046a10aa878080000c020b2001410036020c2001428080808010370204200141046a4100410110b18280800020012802082104200128020c21050b200420056a41003a00002001200541016a36020c2000200141046a10a9878080000b20012902082106200141106a24808080800020060f0b10ae80808000000b4101200310b280808000000b970501047f23808080800041d0066b220224808080800041002d00fca3c680001a0240024041800341002802c8a3c68000118180808000002203450d0020024198046a41086a2001280228220441086a29000037030020024198046a41106a200441106a29000037030020024198046a41186a200441186a2900003703002002200429000037039804200241bc046a41086a4120360200200220043602c004200241003602bc042002418c026a200120024198046a200241bc046a41d8efc08000410110e28780800002400240200228028c0222014105460d002002280290022105200241086a2002418c026a41086a41810210848e8080001a200241c8046a200241086a41e00110848e8080001a200241a8066a41206a20024188026a2d00003a0000200241a8066a41186a20024180026a290200370300200241a8066a41106a200241086a41f0016a290200370300200241a8066a41086a200241f0016a290200370300200220022902e8013703a80641002d00fca3c680001a41f00141002802c8a3c68000118180808000002204450d032004200536020c200420013602082004428180808010370200200441106a200241c8046a41e00110848e8080001a200320043602082003410036020020004200370234200041013602082000200336020420004108360200200320022903a80637000c200341146a200241a8066a41086a2903003700002003411c6a200241a8066a41106a290300370000200341246a200241c0066a2903003700002003412c6a200241c8066a2d00003a00000c010b2002280290022104200041808080807836020020002004360204200341002802c0a3c68000118080808000000b200241d0066a2480808080000f0b410441800310b280808000000b410441f00110b280808000000bfc3303127f017e017f23808080800041e0026b22032480808080000240024020012802082204450d00200141106a2105200141346a21062001410c6a2107200341086a41e8016a2108200341086a41086a2109200341086a41046a210a03402001280204220b2004417f6a220c41306c6a210403402004280208220d41ec016a280200210e200d41e8016a280200210f024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020042802000e050300010210030b200d280208417e6a0e03040506030b2004280204210c200d280208220b417d6a22100e020908020b200d2802080e050c0c090a0b0c0b200441014103200b200c41306c6a220d280208280208417e6a4103491b360200200128020c210c2001280210210e20012802342104200341086a41286a220f4100360200200341086a200c2007200441284b220b1b220c200c200e2004200b1b6a10f686808000200341a0026a41286a200f280200360200200341a0026a41206a200341086a41206a290200370300200341a0026a41186a200341086a41186a290200370300200341a0026a41106a200341086a41106a290200370300200341a0026a41086a2009290200370300200320032902083703a002200320012802383602cc02200341086a200d280208220441086a200441e8016a280200200441ec016a28020010f48d8080002003280208417f6a0e040d100f0e100b418ca6c3800041e60041f4a6c3800010f880808000000b200d41106a280200220c200d410c6a280200220b490d11200c200e4b0d12200f200b6a2111200c200b6b210b200d41146a2802002210410176210c0240024020104101710d00410021120240200c200b4b0d00200c21104100210c0c020b200c200b41d492c68000109481808000000b200c200b4f0d1441012112200c41016a21102011200c6a2d0000410f71210c0b2003200c3a0009200320123a00082003200b20106b3602102003201120106a36020c2007200341086a10e28d80800020042d000c4101710d02200341a0026a41186a4200370300200341a0026a41106a4200370300200341a0026a41086a4200370300200342003703a0020c030b2001280234210d024002402001280238220c4101710d000240024020052006200d41284b220e1b220c280200220f200d4128200e1b460d0020072802002007200e1b210d0c010b200710f78d8080002005280200210f2007280200210d2005210c0b200d200f6a41003a0000200c200c28020041016a3602002001280238210c0c010b2005280200200d200d41284b1b450d140b2001200c41016a3602384103210c2004280208210e02400240024002400240024020042802000e0400010203490b41014103200e280208417e6a4103491b210c0c480b41024103200e280208417d6a4102491b210c4100210d0c470b2004280204210f200e280208417d6a0e020102460b4104210c0c450b200f410e4b0d440c430b200f410f490d420c430b200d41106a280200220c200d410c6a280200220b490d13200c200e4b0d14200f200b6a210f200c200b6b210c200d41146a280200220e410176210d02400240200e4101710d004100210b0240200d200c4b0d00200d210e4100210d0c020b200d200c41d492c68000109481808000000b200d200c4f0d164101210b200d41016a210e200f200d6a2d0000410f71210d0b2003200d3a00092003200b3a00082003200c200e6b3602102003200f200e6a36020c2007200341086a10e28d8080002001280234210d024002402001280238220c4101710d000240024020052006200d41284b220e1b220c280200220f200d4128200e1b460d0020072802002007200e1b210d0c010b200710f78d8080002005280200210f2007280200210d2005210c0b200d200f6a41003a0000200c200c28020041016a3602002001280238210c0c010b2005280200200d200d41284b1b450d170b2001200c41016a3602384103210c2004280208210e02400240024002400240024020042802000e0400010203460b41014103200e280208417e6a4103491b210c0c450b41024103200e280208417d6a4102491b210c4100210d0c440b2004280204210f200e280208417d6a0e020102430b4104210c0c420b200f410e4b0d410c400b200f410f490d3f0c400b200341a0026a41186a200441256a290000370300200341a0026a41106a2004411d6a290000370300200341a0026a41086a200441156a29000037030020032004410d6a2900003703a0020b200d41206a280200210c200d411c6a280200210b02400240200d41186a2802000d00200c200b490d174100210d200c200e4d0d01200c200e41e49dc68000109581808000000b200c200b490d174101210d200c200e4b0d180b2003200d360294022003200c200b6b36029c022003200f200b6a360298022001280238220c410176210d02400240200c4101710d0020052802002006280200220c200c41284b220c1b220e200d490d1a20072802002007200c1b210c4100210e0c010b20052802002006280200220c200c41284b220e1b220c200d490d1a200c200d4d0d1b200320072802002007200e1b220c200d6a2d000041f001713a00dd024101210e0b2003200e3a00dc022003200d3602d8022003200c3602d402200341086a2002200341a0026a20034194026a200341d4026a410110e287808000024020032802084105460d0041002d00fca3c680001a41f00141002802c8a3c6800011818080800000220d450d1c200d428180808010370200200d41086a200341086a41e80110848e8080001a0240200128020822042001280200470d0020012004109886808000200128020821040b2001280204200441306c6a2204200d360208200441003602002004200829020037020c200441146a200841086a2902003702002004411c6a200841106a290200370200200441246a200841186a2902003702002004412c6a200841206a2d00003a0000200128020841016a210d0c3d0b4103210a2004280208210d02400240024002400240024020042802000e0400010203400b41014103200d280208417e6a4103491b210a0c3f0b41024103200d280208417d6a4102491b210a410021010c3e0b2004280204210c200d280208417d6a0e0201023d0b4104210a0c3c0b200c410e4b0d3b0c3a0b200c410f490d390c3a0b4106210b0b200c410f4b0d1a024002400240200d200b4102746a200c410c6c6a410c6a220d2802004102460d0002400240024020012802382210450d0020052006200628020041284b220b1b2802002211450d21200141104134200b1b6a2011417f6a2211360200200128020c22122007200b1b20116a2d0000211120012010417f6a220b3602380240200b410171450d00201141f00171211302400240200520062006280200221441284b22101b220b28020022112014412820101b460d002012200720101b21100c010b200710f78d80800020052802002111200728020021102005210b0b201020116a20133a0000200b200b28020041016a3602002001280238210b0b200b4101710d010b200c410474211102400240200520062006280200221241284b220b1b220c280200221020124128200b1b460d0020072802002007200b1b210b0c010b200710f78d808000200528020021102007280200210b2005210c0b200b20106a20113a0000200c200c28020041016a3602000c010b20052802002006280200220b200b41284b220b1b2210450d20201020072802002007200b1b6a417f6a220b200b2d0000200c723a00000b2001200128023841016a221036023820042d000c4101710d01200341a0026a41186a4200370300200341a0026a41106a4200370300200341a0026a41086a4200370300200342003703a0020c020b4103210e0240024020100e0200013a0b200c410e4b0d390c380b200c410f490d370c380b200341a0026a41186a200441256a290000370300200341a0026a41106a2004411d6a290000370300200341a0026a41086a200441156a29000037030020032004410d6a2900003703a0020b200d41086a280200210c200d280204210b02400240200d2802000d00200c200b490d1f4100210d200c200e4d0d01200c200e41e49dc68000109581808000000b200c200b490d1f4101210d200c200e4b0d200b2003200d360294022003200c200b6b36029c022003200f200b6a360298022010410176210d0240024020104101710d0020052802002006280200220c200c41284b220c1b220e200d490d2220072802002007200c1b210c4100210e0c010b20052802002006280200220c200c41284b220e1b220c200d490d22200c200d4d0d23200320072802002007200e1b220c200d6a2d000041f001713a00dd024101210e0b2003200e3a00dc022003200d3602d8022003200c3602d402200341086a2002200341a0026a20034194026a200341d4026a410110e287808000024020032802084105460d0041002d00fca3c680001a41f00141002802c8a3c6800011818080800000220d450d24200d428180808010370200200d41086a200341086a41e80110848e8080001a0240200128020822042001280200470d0020012004109886808000200128020821040b2001280204200441306c6a2204200d360208200441003602002004200829020037020c200441146a200841086a2902003702002004411c6a200841106a290200370200200441246a200841186a2902003702002004412c6a200841206a2d00003a0000200128020841016a210d0c3b0b4103210a2004280208210d02400240024002400240024020042802000e04000102033a0b41014103200d280208417e6a4103491b210a0c390b41024103200d280208417d6a4102491b210a410021010c380b2004280204210c200d280208417d6a0e020102370b4104210a0c360b200c410e4b0d350c340b200c410f490d330c340b2007200d41106a280200200d410c6a2802006b410174200d41146a2802006b10e08d8080000c020b2001280238220d450d0120052006200628020041284b22041b280200220c450d2220014110413420041b6a200c417f6a220c360200200128020c220e200720041b200c6a2d0000210c2001200d417f6a22043602382004410171450d01200c41f00171210f02400240200520062006280200220b41284b220d1b2204280200220c200b4128200d1b460d00200e2007200d1b210d0c010b200710f78d8080002005280200210c2007280200210d200521040b200d200c6a200f3a00002004200428020041016a3602000c010b2007200d41106a280200200d410c6a2802006b410174200d41146a2802006b41016a10e08d8080000b20012802082204450d3e4103210c20012802042004417f6a41306c6a2204280208210e02400240024002400240024020042802000e0400010203340b41014103200e280208417e6a4103491b210c0c330b41024103200e280208417d6a4102491b210c4100210d0c320b2004280204210f200e280208417d6a0e020102310b4104210c0c300b200f410e4b0d2f0c2e0b200f410f490d2d0c2e0b2001200c360208200341a0026a41086a220d200b200c41306c6a2204410c6a290200370300200341a0026a41106a220c200441146a290200370300200341a0026a41186a220e2004411c6a290200370300200341a0026a41206a220f200441246a290200370300200341a0026a41286a220b2004412c6a280200360200200320042902043703a002200428020022044105460d2b200a20032903a002370200200a41286a200b280200360200200a41206a200f290300370200200a41186a200e290300370200200a41106a200c290300370200200a41086a200d290300370200200320043602082003280210220420042802002204417f6a360200024020044101470d00200910e18a8080000b20012802082204450d3d4103210c20012802042004417f6a41306c6a2204280208210e02400240024002400240024020042802000e0400010203300b41014103200e280208417e6a4103491b210c0c2f0b41024103200e280208417d6a4102491b210c4100210d0c2e0b2004280204210f200e280208417d6a0e0201022d0b4104210c0c2c0b200f410e4b0d2b0c2a0b200f410f490d290c2a0b2003280214220e410176210d200329021c2115200328021821042003280210210c200328020c210f02400240200e4101710d004100210b0240200d200c4b0d00200d210e4100210d0c020b200d200c41d492c68000109481808000000b200d200c4f0d204101210b200d41016a210e200f200d6a2d0000410f71210d0b2003200d3a00d5022003200b3a00d4022003200c200e6b3602dc022003200f200e6a3602d802200341a0026a200341d4026a10e28d8080000c040b2003280214220e410176210d20032902dc01211520032802d80121042003280210210c200328020c210f02400240200e4101710d004100210b0240200d200c4b0d00200d210e4100210d0c020b200d200c41d492c68000109481808000000b200d200c4f0d204101210b200d41016a210e200f200d6a2d0000410f71210d0b2003200d3a00d5022003200b3a00d4022003200c200e6b3602dc022003200f200e6a3602d802200341a0026a200341d4026a10e28d80800020044102460d010c030b20032802cc0122044102470d010b20032802c8024129490d2420032802a00241002802c0a3c68000118080808000000c240b20032902d00121150b20032802cc02220c410176210d02400240200c410171220b0d0020032802a40220032802c802220e200e41284b220e1b220f200d490d1e20032802a002200341a0026a200e1b210f0c010b20032802a40220032802c802220e200e41284b220f1b220e200d490d1e200e200d4d0d1f20032802a002200341a0026a200f1b220f200d6a2d000041707121160b4101210e0240200c4102490d0041002d00fca3c680001a200d41002802c8a3c6800011818080800000220e450d200b200e200f200d10848e808000210e024020032802c8024129490d0020032802a00241002802c0a3c68000118080808000000b20044103460d2220044102460d2e0240200b450d0041002d00fca3c680001a413041002802c8a3c68000118180808000002201450d21200120163a00102001200d36020c2001200e3602082001200d36020420014186808080783602002000200136020420004180808080783602000c390b2015422088a7210a2015a7210102400240024020040d002003200a4100109686808000200328020021042003280204220c2001200a10848e8080001a0c010b200341003a00a8022003200d3602a4022003200e3602a002200a4120470d23200341086a41186a200141186a290000370300200341086a41106a200141106a290000370300200341086a41086a200141086a29000037030020032001290000370308200341d4026a2002200341086a200341a0026a10e18780800020032802d4022204418080808078460d0120032802dc02210a20032802d802210c0b2000200a3602142000200c3602102000200436020c2000200d3602082000200e3602042000200d3602000c390b200020032802d8023602042000418080808078360200200c4102490d38200e41002802c0a3c68000118080808000000c380b200b200c41849ec68000109681808000000b200c200e41849ec68000109581808000000b200c200b41e492c6800010f980808000000b419c94c68000413a41d894c6800010a181808000000b200b200c41849ec68000109681808000000b200c200e41849ec68000109581808000000b200d200c41e492c6800010f980808000000b419c94c68000413a41d894c6800010a181808000000b200b200c41e49dc68000109681808000000b200b200c41f49dc68000109681808000000b200c200e41f49dc68000109581808000000b200d200e41ac95c68000109581808000000b200d200c41bc95c68000109581808000000b200d200c41cc95c6800010f980808000000b410441f00110b280808000000b200c411041fca5c3800010f980808000000b41e894c680004122418c95c6800010a181808000000b419c94c68000413a41d894c6800010a181808000000b200b200c41e49dc68000109681808000000b200b200c41f49dc68000109681808000000b200c200e41f49dc68000109581808000000b200d200e41ac95c68000109581808000000b200d200c41bc95c68000109581808000000b200d200c41cc95c6800010f980808000000b410441f00110b280808000000b41e894c680004122418c95c6800010a181808000000b200d200c41e492c6800010f980808000000b200d200c41e492c6800010f980808000000b200d200f41ac95c68000109581808000000b200d200e41bc95c68000109581808000000b200d200e41cc95c6800010f980808000000b4101200d10b280808000000b4104413010b280808000000b4120200a419ca5c3800010a281808000000b200128020822040d130c140b200f41016a210d4102210c0b2004200d3602042004200c3602002001280208210d0c0f0b41aca5c3800041cd004184a7c3800010a181808000000b200f41016a210d4102210c0b2004200d3602042004200c3602002001280208210d0c0c0b200c41016a21014102210a0b200328020c210d200420013602042004200a3602000c040b200c41016a210d4102210e0b2004200d3602042004200e3602002001280208210d0c080b200c41016a21014102210a0b200328020c210d200420013602042004200a3602000b2000200d36020420004180808080783602000c090b2001200d3602080c040b200f41016a210d4102210c0b2004200d3602042004200c3602002001280208210d0c020b200f41016a210d4102210c0b2004200d3602042004200c3602002001280208210d0b2001280204220b200d417f6a220c41306c6a2104200d0d000b0b0b20004181808080783602000b200341e0026a2480808080000b930201027f23808080800041106b220224808080800020022000360200200220012802144194a7c38000410e200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241a4a7c38000108d81808000210120022d000c210002400240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010baf0301027f23808080800041206b220324808080800002400240024010fa81808000220441ff01490d00200041093b0100024002400240024020022d00000e0400010203060b200241086a21020c040b200241086a21020c030b200241086a21020c020b200241046a21020c010b2003200441016a36020841b1e3c080004113200341086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200341086a41106a200241106a280200360200200341086a41086a200241086a29020037030020032002290200370308200341086a10f58880800041002802b0a1c68000118880808000002000410e3a0000200341086a10fb818080000c010b2002280200450d00200228020441002802c0a3c68000118080808000000b200128020022022002280200417f6a2201360200024020010d00200241086a28020022002002410c6a28020022012802001180808080000002402001280204450d00200041002802c0a3c68000118080808000000b200241046a22012001280200417f6a220136020020010d00200241002802c0a3c68000118080808000000b200341206a2480808080000bdf0201027f23808080800041206b22032480808080000240024010fa81808000220441fe014b0d002003200441016a36020c41b1e3c0800041132003410c6a410441002802e0a1c680001186808080000041002802e8a1c68000118880808000002003410c6a2002410010b8888080002000200329020c370200200041086a2003410c6a41086a290200370200200041106a2003410c6a41106a28020036020041b0a1c6800041d0a1c6800020032d000c410e461b280200118880808000002003410c6a10fb818080000c010b200041093b01000b200128020022002000280200417f6a2201360200024020010d00200041086a28020022022000410c6a28020022012802001180808080000002402001280204450d00200241002802c0a3c68000118080808000000b200041046a22012001280200417f6a220136020020010d00200041002802c0a3c68000118080808000000b200341206a2480808080000bc60301087f23808080800041306b220324808080800020032001360200024002400240024002402001450d00200341246a21044101210541002106410021070340200341046a2005200741002802c0a1c680001185808080000020032802042208418080808078460d02200341186a20032802082209200328020c220741002802b8a1c680001185808080000002402003280218220a450d002004200328021c2003280220200a28020c118580808000000b02402006450d00200541002802c0a3c68000118080808000000b20092105200821062001417f6a22010d000b20020d022000410e3a00002008450d04200941002802c0a3c68000118080808000000c040b20020d012000410e3a00000c030b2002450d012000410e3a00002006450d02200541002802c0a3c68000118080808000000c020b200341246a42003702002003410136021c200341b8a7c380003602182003418097c38000360220200341186a41b4a8c3800010f680808000000b200341246a42013702002003410236021c200341e8a8c3800036021820034185808080003602142003200341106a36022020032003360210200341186a41f8a8c3800010f680808000000b200341306a2480808080000bdf0201027f23808080800041206b22032480808080000240024010fa81808000220441fe014b0d002003200441016a36020c41b1e3c0800041132003410c6a410441002802e0a1c680001186808080000041002802e8a1c68000118880808000002003410c6a2002410110b8888080002000200329020c370200200041086a2003410c6a41086a290200370200200041106a2003410c6a41106a28020036020041b0a1c6800041d0a1c6800020032d000c410e461b280200118880808000002003410c6a10fb818080000c010b200041093b01000b200128020022002000280200417f6a2201360200024020010d00200041086a28020022022000410c6a28020022012802001180808080000002402001280204450d00200241002802c0a3c68000118080808000000b200041046a22012001280200417f6a220136020020010d00200041002802c0a3c68000118080808000000b200341206a2480808080000bb70e02047f017e23808080800041106b220224808080800002400240024002400240024002400240024002400240024002402000280200417f6a0e0c000102030405060708090a0b000b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41003a0000200041086a200110df898080000c0b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41013a00002001200341016a360208200041086a280200210420022000410c6a28020022003602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822036b20004f0d0020012003200010b182808000200128020821030b200128020420036a2004200010848e8080001a2001200320006a3602080c0a0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41023a00002001200341016a360208200041146a28020021052002200041186a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822046b20034f0d0020012004200310b182808000200128020821040b200128020420046a2005200310848e8080001a2001200420036a360208200041046a200110c98c8080000c090b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41033a00002001200341016a360208200041086a280200210520022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822046b20034f0d0020012004200310b182808000200128020821040b200128020420046a2005200310848e8080001a2001200420036a360208200041146a28020021042002200041186a28020022003602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822036b20004f0d0020012003200010b182808000200128020821030b200128020420036a2004200010848e8080001a2001200320006a3602080c080b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41043a00002001200341016a360208200041086a280200210420022000410c6a28020022003602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822036b20004f0d0020012003200010b182808000200128020821030b200128020420036a2004200010848e8080001a2001200320006a3602080c070b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41053a00002001200341016a360208200041086a280200210420022000410c6a28020022003602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822036b20004f0d0020012003200010b182808000200128020821030b200128020420036a2004200010848e8080001a2001200320006a3602080c060b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41063a0000200041046a200110898d8080000c050b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41073a00002001200341016a2203360208200029030821060240200128020020036b41074b0d0020012003410810b182808000200128020821030b2001200341086a360208200128020420036a20063700000c040b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41083a00000c030b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41093a00002001200341016a2203360208200028020421000240200128020020036b41034b0d0020012003410410b182808000200128020821030b2001200341046a360208200128020420036a20003600000c020b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a410a3a00002001200341016a2203360208200028020421000240200128020020036b41034b0d0020012003410410b182808000200128020821030b2001200341046a360208200128020420036a20003600000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a410b3a00002001200341016a2203360208200028020421000240200128020020036b41034b0d0020012003410410b182808000200128020821030b2001200341046a360208200128020420036a20003600000b200241106a2480808080000bf50102017f057e23808080800041b0026b2202248080808000024002402001280200410a460d004200210342e40021040c010b20013502042104200241086a10e1898080002004200241a8026a29030022032003428094ebdc03802203428094ebdc037e7d7e2205428094ebdc03802206200320047e7c20052006428094ebdc037e7d4280cab5ee0156ad7c2103200420022903a00222052005428094ebdc03802205428094ebdc037e7d7e2206428094ebdc03802207200520047e7c20062007428094ebdc037e7d4280cab5ee0156ad7c21040b200041003b01102000200337030820002004370300200241b0026a2480808080000b8e0301047f23808080800041306b22012480808080002001410036020c20014280808080800137020441002d00fca3c680001a0240410441002802c8a3c680001181808080000022020d004101410410b280808000000b2001411c6a41086a22034100360200200120023602202001410436021c200141003602282001200141286a36022c2001412c6a2001411c6a10c08a808000200141106a41086a220420032802003602002001200129021c370310200141046a4100109d868080002001280208200141046a41086a220328020041e8006c6a2202410b36024420024185bec38000360240200241a9828080003602182002410036020020022001290310370348200241013a00602002410036025c20024280808080c000370254200241d0006a200428020036020020024287bba5b8adf5dafa5b370308200241106a429595f4d085b899a1603703002003200328020041016a2202360200200041086a200236020020002001290204370200200041106a410d360200200041e8a6c4800036020c200141306a2480808080000bf20501047f2380808080004190016b2201248080808000200142f0fe93e98686d7ab8c7f37030820014280eee1b0e3b7afe9987f370300200141cc006a2001411041002802c0a1c680001185808080000002400240200128024c2202418080808078460d0020012802502103024020012802544110490d0020012003411010888e808000210402402002450d00200341002802c0a3c68000118080808000000b20040d010c020b2002450d00200341002802c0a3c68000118080808000000b41002102200141003b011e02400240417f4100280284a4c680002203410347200341034b1b2203450d00200341ff017141ff01470d010b200141286a410c6a41aa82808000360200200141838280800036022c2001410d360224200141e8a6c4800036022020012001411e6a3602302001200141206a3602284100280290a1c680002102410028028ca1c6800021034100280280a4c68000210420014184016a4202370200200141fc006a4103360200200141f4006a4116360200200141f0006a418cabc38000360200200141e4006a41c0a7c38000ad4280808080b00e84370200200141cc006a410c6a41b9a9c38000ad4280808080d0068437020020014180016a200141286a360200200141f4aac380003602782001410336026c200141003602602001410036025420014281808080e00437024c200341ecf2c08000200441024622041b200141cc006a200241d4f2c0800020041b2802101184808080000020012f011e21020b200141cc006a41e8a6c48000410d41002802a0a3c6800011858080800000200141cc006a41106a220341bccdc48000411541002802a0a3c6800011858080800000200141286a41186a200141cc006a41186a290000370300200141286a41106a2003290000370300200141286a41086a200141cc006a41086a2900003703002001200129004c370328200120023b014c200141286a4120200141cc006a410241002802e0a1c68000118680808000000b200042003703082000420037030020014190016a2480808080000beb0101037f23808080800041106b220224808080800002402001280200220328020020032802082204470d0020032004410110ab86808000200328020821040b200328020420046a41fb003a00002003200441016a3602082002200136020c20024180023b01080240200241086a41c0abc38000410b200010d88680800022030d002002280208220441ff01710d0020044180fe0371450d000240200228020c280200220428020020042802082201470d0020042001410110ab86808000200428020821010b200428020420016a41fd003a00002004200141016a3602080b200241106a24808080800020030b840d03017f0a7e047f23808080800041c0006b22022480808080000240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002402001280200417f6a0e0c000102030405060708090a0b000b200241186a200141206a2903002203370300200241106a200141186a2903002204370300200241086a200141106a2903002205370300200241206a200141286a2903002206370300200241286a200141306a2903002207370300200241306a200141386a2903002208370300200241386a200141c0006a290300220937030020022001290308220a370300200141c8006a290300210b200141d0006a290300210c200041c0006a2009370300200041386a2008370300200041306a2007370300200041286a2006370300200041206a2003370300200041186a2004370300200041106a20053703002000200a370308200041d0006a200c370300200041c8006a200b370300200041013602000c160b200141086a280200210d024002402001410c6a28020022010d004101210e0c010b2001417f4c0d0e41002d00fca3c680001a200141002802c8a3c6800011818080800000220e450d0f0b200e200d200110848e808000210d2000410c6a2001360200200041086a200d36020020002001360204200041023602000c150b200141146a280200210e02400240200141186a280200220d0d004101210f0c010b200d417f4c0d0d41002d00fca3c680001a200d41002802c8a3c6800011818080800000220f450d0f0b200f200e200d10848e808000210f418080808078210e2001280204418080808078460d0b200141086a28020021102001410c6a280200220e0d09410121010c0a0b200141086a28020021104101210f4101210e02402001410c6a280200220d450d00200d417f4c0d0c41002d00fca3c680001a200d41002802c8a3c6800011818080800000220e450d0f0b200e2010200d10848e808000210e2000410c6a200d360200200041086a200e3602002000200d360204200141146a280200210d0240200141186a2802002201450d002001417f4c0d0c41002d00fca3c680001a200141002802c8a3c6800011818080800000220f450d100b200f200d200110848e808000210d200041186a2001360200200041146a200d36020020002001360210200041043602000c130b200141086a280200210d024002402001410c6a28020022010d004101210e0c010b2001417f4c0d0b41002d00fca3c680001a200141002802c8a3c6800011818080800000220e450d100b200e200d200110848e808000210d2000410c6a2001360200200041086a200d36020020002001360204200041053602000c120b200141086a280200210d024002402001410c6a28020022010d004101210e0c010b2001417f4c0d0a41002d00fca3c680001a200141002802c8a3c6800011818080800000220e450d100b200e200d200110848e808000210d2000410c6a2001360200200041086a200d36020020002001360204200041063602000c110b02400240024002400240024020012d00040e050001020304000b200141056a280000210d200241086a2001410c6a108b87808000200241003a00002002200d3600010c040b200141056a280000210d200241086a2001410c6a108b87808000200241013a00002002200d3600010c030b200141056a280000210d200241086a2001410c6a108b87808000200241023a00002002200d3600010c020b200241046a200141086a108b87808000200241033a00000c010b200241043a00000b2000200229020037020420004107360200200041146a200241106a2802003602002000410c6a200241086a2902003702000c100b20004108360200200020012903083703080c0f0b200041093602000c0e0b2000410a360200200020012802043602040c0d0b2000410b360200200020012802043602040c0c0b2000410c360200200020012802043602040c0b0b200e417f4c0d0241002d00fca3c680001a200e41002802c8a3c68000118180808000002201450d090b200ead42208620012010200e10848e808000ad8421030b2000200d3602102000200e36020420004103360200200041186a200d360200200041146a200f360200200041086a20033703000c080b10ae80808000000b4101200110b280808000000b4101200d10b280808000000b4101200d10b280808000000b4101200110b280808000000b4101200110b280808000000b4101200110b280808000000b4101200e10b280808000000b200241c0006a2480808080000b8b1004057f017e037f087e23808080800041c0026b220224808080800002400240024002400240024002400240024002400240024002400240024002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020020042d00000e0c0102030405060708090a0b0d0c0b200041003602000c0f0b024002402001280200220328020422044120490d002003200441606a220536020420032003280200220141206a220636020020024180026a41086a200141086a29000037030020024180026a41106a200141106a29000037030020024180026a41186a200141186a290000370300200220012900003703800220054120490d002003200441406a22053602042003200141c0006a360200200241a0026a41086a200641086a290000370300200241a0026a41106a200641106a290000370300200241a0026a41186a200641186a290000370300200220062900003703a00220054108490d002003200441b87f6a22063602042003200141c8006a36020020064108490d00200129004021072003200441b07f6a3602042003200141d0006a360200200241c0016a41206a220320022903a002370300200241c0016a41086a220420024180026a41086a290300370300200241c0016a41106a220620024180026a41106a290300370300200241c0016a41186a220520024180026a41186a290300370300200241c0016a41286a2208200241a0026a41086a290300370300200241c0016a41306a2209200241a0026a41106a290300370300200241c0016a41386a220a200241a0026a41186a29030037030020022002290380023703c0012001290048210b20024180016a41386a2201200a29030037030020024180016a41306a2009290300220c37030020024180016a41286a2008290300220d37030020024180016a41206a2003290300220e37030020024180016a41186a2005290300220f37030020024180016a41106a2006290300221037030020024180016a41086a20042903002211370300200220022903c001221237038001200241f0006a200c370200200241e8006a200d370200200241e0006a200e370200200241d8006a200f370200200241d0006a2010370200200241c8006a2011370200200241f8006a200129030037020020022012370240200041046a2002413c6a41c40010848e8080001a2000200b37035020002007370348410121030c010b410021030b200020033602000c0e0b200241086a200110bc8a808000024020022802080d002002413c6a2001200228020c10d085808000200228023c418080808078460d002000200229023c3702042000410c6a200241c4006a280200360200200041023602000c0e0b200041003602000c0d0b200241106a200110bc8a80800020022802100d0a2002413c6a2001200228021410d085808000200228023c2203418080808078460d0a20022802442106200228024021042002413c6a200110c78c8080000240200228023c418180808078460d002000200229023c3702042000410c6a200241c4006a280200360200200020063602182000200436021420002003360210200041033602000c0d0b200041003602002003450d0c200441002802c0a3c68000118080808000000c0c0b200241206a200110bc8a80800020022802200d0a2002413c6a2001200228022410d085808000200228023c2203418080808078460d0a2002280244210620022802402104200241186a200110bc8a808000024020022802180d002002413c6a2001200228021c10d085808000200228023c418080808078460d002000200229023c370210200041186a200241c4006a2802003602002000200636020c2000200436020820002003360204200041043602000c0c0b200041003602002003450d0b200441002802c0a3c68000118080808000000c0b0b200241286a200110bc8a808000024020022802280d002002413c6a2001200228022c10d085808000200228023c418080808078460d002000200229023c3702042000410c6a200241c4006a280200360200200041053602000c0b0b200041003602000c0a0b200241306a200110bc8a808000024020022802300d002002413c6a2001200228023410d085808000200228023c418080808078460d002000200229023c3702042000410c6a200241c4006a280200360200200041063602000c0a0b200041003602000c090b2002413c6a2001109f8d808000024020022d003c4105460d002000200229023c370204200041146a200241cc006a2802003602002000410c6a200241c4006a290200370200200041073602000c090b200041003602000c080b02402001280200220328020422014108490d00200041083602002003200141786a36020420032003280200220141086a360200200020012900003703080c080b200041003602000c070b200041093602000c060b02402001280200220328020422014104490d0020032001417c6a36020420032003280200220141046a36020020012800002203418094ebdc034b0d00200020033602042000410a3602000c060b200041003602000c050b02402001280200220328020422014104490d002000410b36020020032001417c6a36020420032003280200220141046a360200200020012800003602040c050b200041003602000c040b200041003602000c030b02402001280200220328020422014104490d002000410c36020020032001417c6a36020420032003280200220141046a360200200020012800003602040c030b200041003602000c020b200041003602000c010b200041003602000b200241c0026a2480808080000bc60603057f017e027f23808080800041c0006b2201248080808000200141186a41f7abc38000410441b9a9c380004135418097c38000410010d882808000200141106a42043702002001420037020820014280808080800137020041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241ab99c3800036020020014101360238200120023602302001200241206a36023c200120023602342001200141306a10fa8680800041002d00fca3c680001a20012802002103200128020421022001280208210420012802182105200129021c2106410841002802c8a3c68000118180808000002207450d01200741002903c0acc380003702002001410036020820014280808080c000370200200141306a200141c8acc38000410a10b28b8080002001200141306a41d2acc38000410c10fe8a808000200141306a200141deacc38000410e10938b8080002001200141306a41ecacc38000411210b38b808000200141306a200141feacc38000411410a28b8080002001200141306a4192adc38000410c10a68b808000200141306a2001419eadc38000411710ac8b8080002001200141306a41b5adc38000411210a98b808000200141306a200141c7adc38000411510a18b8080002001200141306a41dcadc38000410a109d8b808000200141306a200141e6adc38000410410828b808000200141246a200141306a41eaadc38000410e10908b8080002001200128022436020820012001280228220836020020012008200128022c41246c6a36020c20012008360204200141306a200110fb868080002005418080808078460d022001200336020820012002360200200120023602042001200220044105746a36020c200041c4006a200110fa868080002001410b6a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a0000200041d8006a4101360200200041d4006a2007360200200041013602502001200129023037000320002001290000370001200041086a200141076a290000370000200141c0006a2480808080000f0b4108412010b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000beb0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242d0ffe6c89b859fc430370318200242ecb2f697e9f8a9f860370308200242ef899cf7a6a3ca9dd100370300200241b382808000360210200241206a42919fe7f19ec9d1a177370300200241286a41b4828080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bcb0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41e1bdc38000410941cfbdc38000411041a0b5c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242c194a6a793ccc3a857370308200241e0bdc38000360220200241f58180800036021820024101360204200241dfbdc38000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a42ab8bffbed784ffa5937f370300200241386a41ef80808000360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024201370224200241a1a4c580003602202002410b36021c20024196a4c58000360218200241f581808000360210200242ab8bffbed784ffa5937f370308200242c194a6a793ccc3a857370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420137022420024195a4c580003602202002410d36021c20024188a4c58000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242f099b79de1dbade83c370318200242a6829fd5dedbeecd9a7f370308200242f993ea84fdedefb2d000370300200241b582808000360210200241206a4297b1e6929387a1b844370300200241286a41b6828080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242a5e9e3ab9e929adc2c370318200242e9b494c69bdbc4d608370308200242ead283debcdebd93d800370300200241f780808000360210200241206a4293888c8f89fdc6ec9e7f370300200241286a41ef808080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242d0ffe6c89b859fc430370318200242c6bb82f1efc886d58a7f370308200242c6bdb6a4b5ec93c2d300370300200241b782808000360210200241206a42919fe7f19ec9d1a177370300200241286a41b4828080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bcb0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41c8bdc38000410741cfbdc38000411041a0b5c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242c194a6a793ccc3a857370308200241e0bdc38000360220200241f58180800036021820024101360204200241dfbdc38000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a42ab8bffbed784ffa5937f370300200241386a41ef80808000360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024201370224200241a1a4c580003602202002410b36021c20024196a4c58000360218200241f581808000360210200242ab8bffbed784ffa5937f370308200242c194a6a793ccc3a857370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420137022420024195a4c580003602202002410d36021c20024188a4c58000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bd20301027f23808080800041206b220124808080800041002d00fca3c680001a024041e00041002802c8a3c680001181808080000022020d00410841e00010b280808000000b200242ccf3faf4aeedbdfafb00370348200242fc86a1bbccd1ddf472370330200242e1a1e0ae85b5ea80fd00370318200242dca9a2e881dac9f914370308200242b4fd99f0e7c3fbd60e370300200241b882808000360210200241d0006a42cda992f8a2a6d9f2897f370300200241386a428bebb7a9dff4a9fe6a370300200241206a42b285eeed9386eed0d700370300200241d8006a41b982808000360200200241c0006a41ba82808000360200200241286a41bb828080003602002001200241e0006a36021c200141043602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000b9f0503067f017e027f23808080800041d0006b2201248080808000200141286a41d8aec38000410a41a0b5c38000412541a0b5c38000410010d882808000200141206a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241c5b5c3800036020020014101360248200120023602402001200241206a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024208370224200241f8e3c4800036022020024100360218200241bc82808000360210200242899ac8f29d8ce69ac300370308200242e78dcee4d0becc9757370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bab0503067f017e027f23808080800041d0006b2201248080808000200141286a41bcaec3800041114198b0c38000411d41a0b5c38000410010d882808000200141206a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241c5b5c3800036020020014101360248200120023602402001200241206a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024204370224200241e3a6c580003602202002410436021c200241dfa6c58000360218200241bd82808000360210200242cf94e6f2afe8d5d9aa7f370308200242c1b18585f195bbb0d600370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b890403057f017e017f23808080800041d0006b2201248080808000200141286a41cdaec38000410b41ceb1c38000412641a0b5c38000410010d882808000200141206a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241c5b5c3800036020020014101360248200120023602402001200241206a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210220012802182105200129022c210620012802282107200142808080808001370248200142888080808001370240200141346a200141c0006a10f9868080002007418080808078460d012001200436021820012002360210200120023602142001200220054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bdb0901027f0240024002400240024002400240024002400240024002400240024020002d00000e0e000102030405060708090a0b0c0d000b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41003a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41013a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41023a00000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41033a00002001200241016a2202360208200041106a2d00002103024020012802002002470d0020012002410110b182808000200128020821020b200128020420026a20033a00002001200241016a22023602080240200128020020026b41034b0d0020012002410410b182808000200128020821020b200128020420026a200028020c3600002001200241046a3602080f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41043a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41053a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41063a00000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41073a00002001200241016a220236020820002d00012100024020012802002002470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a20003a00000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41083a00002001200241016a220236020820002d00012100024020012802002002470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a20003a00000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41093a00002001200241016a220236020820002d00012100024020012802002002470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a20003a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a410a3a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a410b3a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a410c3a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a410d3a00000bde0803017f047e017f23808080800041b0016b220224808080800020002903082203210420002903002205210602402001290300500d00200141106a2903002204200320042003541b210420012903082206200520062005541b21060b0240024020032004520d0020052006510d010b200320047d2103200520067d210520002d00102101200241086a10ce88808000200241086a210002400240024020010e03020001020b200241186a21000c010b200241286a21000b200042002000290300220420057d220520052004561b370300200042002000290308220520037d220320032005561b37030820024190016a200241086a41286a290300370300200241e0006a41286a200241086a41206a290300370300200241e0006a41206a200241086a41186a290300370300200241e0006a41186a200241086a41106a290300370300200241e0006a41106a200241086a41086a29030037030020022002290308370368200242013703602002200241e0006a41086a36023c200242fc90b9e5d09296e777370348200242a6d4e6f1a4dd959860370340200242f89aef8eef91e1ce967f370358200242b4d6d6dfccc6b592c3003703502002413c6a200241c0006a412010f98c8080000b02404100280284a4c680004105470d00200241086a10ce88808000200241be828080003602442002200241086a3602404100280290a1c680002100410028028ca1c6800021014100280280a4c68000210720024198016a420137020020024190016a410136020020024188016a410f36020020024184016a41bfb1c38000360200200241f8006a41d0b0c38000ad4280808080f00d84370200200241ec006a41ceb1c38000ad4280808080e0048437020020024194016a200241c0006a360200200241c8b0c3800036028c012002410536028001200241003602742002410036026820024281808080c021370260200141ecf2c08000200741024622071b200241e0006a200041d4f2c0800020071b280210118480808000000b02404100280284a4c680004105470d00200242fc90b9e5d09296e777370368200242a6d4e6f1a4dd95986037036020024295f38cfcb699a3c4ef00370378200242a8db95cdaa86daa7193703702002200241e0006a412010d088808000200241bf8280800036020c20022002280204410020022802001b3602402002200241c0006a3602084100280290a1c680002100410028028ca1c6800021014100280280a4c68000210720024198016a420137020020024190016a410136020020024188016a410f36020020024184016a41bfb1c38000360200200241f8006a41d0b0c38000ad4280808080f00d84370200200241ec006a41ceb1c38000ad4280808080e0048437020020024194016a200241086a36020020024188b2c3800036028c012002410536028001200241003602742002410036026820024281808080a022370260200141ecf2c08000200741024622071b200241e0006a200041d4f2c0800020071b280210118480808000000b200241b0016a2480808080000bb50603017f017e057f23808080800041b0016b2201248080808000200142fc90b9e5d09296e777370308200142a6d4e6f1a4dd959860370300200142f89aef8eef91e1ce967f370318200142b4d6d6dfccc6b592c300370310200141206a2001412041002802b8a1c68000118580808000000240024002402001280220450d00200141306a41086a200141206a41086a29020022023703002001200129022037033020012002a72203360248200120012802342204360244200141e8006a200141c4006a10f18c8080002001290368500d0102400240417f4100280284a4c680002205410147200541014b1b2205417f460d00200541ff01710d010b200141dc006a4194afc3800041022001412010dc8a808000200141cc006a410c6a41c082808000360200200141c1828080003602502001200141af016a3602542001200141dc006a36024c4100280290a1c680002105410028028ca1c6800021064100280280a4c680002107200141a0016a420237020020014198016a410236020020014190016a41103602002001418c016a4184bac3800036020020014180016a4194bac38000ad4280808080900d84370200200141e8006a410c6a41fdbac38000ad42808080808004843702002001419c016a200141cc006a360200200141f4b9c380003602940120014101360288012001410036027c2001410036027020014281808080c003370268200641ecf2c08000200741024622071b200141e8006a200541d4f2c0800020071b28021011848080800000200128025c450d00200128026041002802c0a3c68000118080808000000b2001413c6a20042003200128023028020c118580808000000b20004200370300200041286a4200370300200041206a4200370300200041186a4200370300200041106a4200370300200041086a42003703000c010b20002001290370370300200041286a20014198016a290300370300200041206a200141e8006a41286a290300370300200041186a200141e8006a41206a290300370300200041106a200141e8006a41186a290300370300200041086a200141e8006a41106a2903003703002001413c6a20042003200128023028020c118580808000000b200141b0016a2480808080000b7401017f23808080800041106b22022480808080002002200041206a36020c200141d3cdc38000411041e3cdc380004106200041eccdc3800041fccdc38000410b200041106a41eccdc380004187cec3800041092002410c6a4190cec3800010e0808080002100200241106a24808080800020000bbf0403027f017e027f2380808080004190016b220324808080800041002104200341086a2001200241002802b8a1c68000118580808000000240024020032802080d000c010b200341186a41086a200341086a41086a290200220537030020032003290208370318200328021c2106024002402005a722074104490d0020062800002102410121040c010b02400240417f4100280284a4c680002204410147200441014b1b2204417f460d00200441ff01710d010b2003413c6a4194afc3800041022001200210dc8a8080002003412c6a410c6a41c082808000360200200341c18280800036023020032003418f016a36023420032003413c6a36022c4100280290a1c680002104410028028ca1c6800021024100280280a4c68000210120034180016a4202370200200341f8006a4102360200200341f0006a4110360200200341ec006a4184bac38000360200200341e0006a4194bac38000ad4280808080900d84370200200341c8006a410c6a41fdbac38000ad4280808080800484370200200341fc006a2003412c6a360200200341f4b9c38000360274200341013602682003410036025c2003410036025020034281808080c003370248200241ecf2c08000200141024622011b200341c8006a200441d4f2c0800020011b28021011848080800000200328023c450d00200328024041002802c0a3c68000118080808000000b41002102410021040b200341246a20062007200328021828020c118580808000000b200020023602042000200436020020034190016a2480808080000bed0201037f2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000bc00301077f23808080800041206b220224808080800041002d00fca3c680001a0240410141002802c8a3c68000118180808000002203450d0020032001417f6a3a0000200241013602102002200336020c20024101360208200241086a4101410310ab86808000200228020c2203200228021022046a220541003b0000200541026a41003a000020022802082105024002400240200441036a22044104470d00200341036a2d0000210420032d0002210620032d0001210720032d000021082005450d01200341002802c0a3c68000118080808000000c010b2005418080808078470d01200341187621042003411076210620034180fe03714108762107200321080b200041033a00002000410d6a20073a00002000410c6a20083a0000200041106a41013a00002000410e6a20063a00002000410f6a20043a0000200041086a200141187441808080786a411875410274220341b0cec380006a2802003602002000200341a0cec380006a280200360204200241206a2480808080000f0b2002200436021c200220033602182002200536021441e0b3c3800041cb00200241146a4188b5c3800041d0b3c38000108981808000000b4101410110b280808000000bfc0e04037f037e017f017e23808080800041f0036b2202248080808000200241306a20002d00102203200110d7888080000240024020022d0030450d0020022f0132210120022d003121000c010b20022802342104200210ce88808000200241306a10e1898080002000290308210520002903002106024002400240024020030e03000102000b427f427f200520024180016a2903007c220720072005541b22052001ad7c220720072005541b2107427f200620022903787c220520052006541b2105200241306a21080c020b427f427f2005200241d8016a2903007c220720072005541b22052001ad7c220720072005541b2107427f2006200241d0016a2903007c220520052006541b210520024188016a21080c010b427f427f2005200241b0026a2903007c220720072005541b22052001ad7c220720072005541b2107427f2006200241a8026a2903007c220520052006541b2105200241e0016a21080b0240024002400240200829031822094200520d002008290330500d010b2002210102400240024020030e03020001020b200241106a21010c010b200241206a21010b02402001290300220620057c22052006540d002001290308220620077c22072006540d0020012007370308200120053703000c020b410021000240417f4100280284a4c680002201410447200141044b1b2201417f460d00200141ff01710d030b410021004100280290a1c680002101410028028ca1c6800021034100280280a4c680002108200241d8036a4200370200200241d4036a41a0b5c38000360200200241d0036a4101360200200241c8036a410f360200200241c4036a41bfb1c38000360200200241b8036a41d0b0c38000ad4280808080f00d84370200200241ac036a41ceb1c38000ad4280808080e00484370200200241bcb7c380003602cc03200241043602c003200241003602b403200241003602a80320024281808080c0133702a003200341ecf2c08000200841024622081b200241a0036a200141d4f2c0800020081b280210118480808000000c020b2002210102400240024020030e03020001020b200241106a21010c010b200241206a21010b2001427f2001290308220620077c220720072006541b3703082001427f2001290300220620057c220520052006541b3703000b2002210102400240024020030e03020001020b200241106a21010c010b200241206a21010b20012903082106200129030021050240024002402009500d002005200841206a290300560d012006200841286a290300560d010b0240427f427f2002290300220720022903107c220920092007541b220720022903207c220920092007541b20022903c802560d00427f427f20022903082207200241186a2903007c220920092007541b2207200241286a2903007c220920092007541b200241d0026a290300580d020b2008290330500d0102402005200841386a290300560d002006200841c0006a290300580d020b410021000240417f4100280284a4c680002201410447200141044b1b2201417f460d00200141ff01710d030b200241a0036a410c6a4200370200200241013602a40320024184b7c380003602a003200241a0b5c380003602a8032002418c036a410c6a41263602002002418cb7c3800036029c03200241ceb1c38000360294032002410f36029003200241bfb1c3800036028c0341002100200241a0036a41042002418c036a4100200210a182808000410621010c030b410021000240417f4100280284a4c680002201410447200141044b1b2201417f460d00200141ff01710d020b410021004100280290a1c680002101410028028ca1c6800021034100280280a4c680002108200241d8036a4200370200200241d4036a41a0b5c38000360200200241d0036a4101360200200241c8036a410f360200200241c4036a41bfb1c38000360200200241b8036a41d0b0c38000ad4280808080f00d84370200200241ac036a41ceb1c38000ad4280808080e00484370200200241dcb6c380003602cc03200241043602c003200241003602b403200241003602a80320024281808080a0153702a003200341ecf2c08000200841024622081b200241a0036a200141d4f2c0800020081b28021011848080800000410621010c020b200241d8026a41286a200241286a290300370300200241d8026a41206a200241206a290300370300200241d8026a41186a200241186a290300370300200241d8026a41106a200241106a290300370300200241d8026a41086a200241086a290300370300200220022903003703d802200010d888808000220041ff01714102470d00200242fc90b9e5d09296e7773703a803200242a6d4e6f1a4dd9598603703a00320024295f38cfcb699a3c4ef003703b803200242a8db95cdaa86daa7193703b0032002200436028c03200241a0036a41202002418c036a410441002802e0a1c6800011868080800000200242fc90b9e5d09296e7773703a803200242a6d4e6f1a4dd9598603703a003200242f89aef8eef91e1ce967f3703b803200242b4d6d6dfccc6b592c3003703b003200241d8026a200241a0036a4120108b8d808000410221000c010b410621010b200241f0036a2480808080002001410874200041ff0171720bc40702067f027e23808080800041c0016b2202248080808000024002400240024020012d000822030d00410021010c010b200241fc006a200141096a10e881808000024020022d007c0d0041002104410121030c020b200241c7006a41196a200241fc006a41196a290000370000200241c7006a41116a200241fc006a41116a290000370000200241c7006a41096a200241fc006a41096a2900003700002002200229007d370048410121010b200220013a004702400240417f4100280284a4c680002204410447200441044b1b2204450d00200441ff017141ff01460d00200241306a200241d3006a290000370300200241386a200241db006a2900003703002002413d6a200241e0006a2900003700002002200229004b37032820022d004a210520022d0049210420022d004821030c010b0240024020030d0020024180808080783602700c010b200241f0006a4194afc380004102200241c8006a10db8a8080000b200241c28280800036026c2002200241f0006a3602684100280290a1c680002101410028028ca1c6800021044100280280a4c680002103200241b4016a4201370200200241ac016a4101360200200241a4016a4116360200200241a0016a4196afc3800036020020024194016a41acafc38000ad4280808080c00d84370200200241fc006a410c6a4198b0c38000ad4280808080d00384370200200241b0016a200241e8006a3602002002418cafc380003602a8012002410436029c012002410036029001200241003602840120024281808080e01237027c200441ecf2c08000200341024622031b200241fc006a200141d4f2c0800020031b28021011848080800000024020022802702201418080808078460d002001450d00200228027441002802c0a3c68000118080808000000b200241306a200241c7006a410c6a290000370300200241386a200241db006a2900003703002002413d6a200241e0006a2900003700002002200229004b37032820022d0048210320022d0049210420022d004a210520022d004722014102460d010b200241086a41156a2206200241286a41156a290000370000200241086a41106a2207200241286a41106a290300370300200241086a41086a200241286a41086a2903002208370300200220022903282209370308200020053a0003200020043a0002200020033a0001200020013a0000200020093700042000410c6a2008370000200041146a2007290300370000200041196a20062900003700000c010b200020033a0001200041023a0000200041036a20053a0000200041026a20043a00000b200241c0016a2480808080000bc70201027f23808080800041106b2202248080808000024002402000280200418080808078470d00200128021441c6c8c380004104200141186a28020028020c1182808080000021010c010b200220003602002002200128021441cac8c380004104200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241d0c8c38000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010bb00701037f23808080800041a0026b2204248080808000200441013a00880120044204370380012004420037037820044280808080c0003703702004427f37036820044200370360200441013a0028200442043703202004420037031820044280808080c0003703102004427f37030820044200370300200441c0016a200441e0006a200410fd84808000200441e0006a20022d0010200310d78880800002400240024020042d00600d0041062103200210d888808000220241ff01714102470d01200441013a0058200442043703502004420037034820044280808080c0003703402004427f37033820044200370330200441e0006a200441c0016a200441306a10fd84808000200441013a00b801200442043703b001200442003703a80120044280808080c0003703a0012004427f370398012004420037039001200441c0016a200441e0006a20044190016a10fd84808000200441013a0098022004420437039002200442003703880220044280808080c000370380022004427f3703f801200442003703f0012000200441c0016a200441f0016a10fd848080000c020b20042f0162210320042d006121020b2000418080808078360210200041026a20034108763a000020002003410874200241ff0171723b0100200441d4016a28020021050240200441d8016a2802002200450d002000410171210641002103024020004101460d002000417e7121022005210041002103034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002002200341026a2203470d000b0b2006450d0020052003410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b024020042802d001450d00200541002802c0a3c68000118080808000000b200441e0016a28020021050240200441e4016a2802002200450d002000410171210641002103024020004101460d002000417e7121022005210041002103034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002002200341026a2203470d000b0b2006450d0020052003410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b20042802dc01450d00200541002802c0a3c68000118080808000000b200441a0026a2480808080000bfe0401047f2380808080004180016b22032480808080002003418080c0023602142003428080f0818080802837020c200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd95986037033020034295f38cfcb699a3c4ef00370348200342a8db95cdaa86daa7193703402003200341306a412010d0888080002003417f2003280204410020032802001b220420026a220220022004491b2204360218200341146a2105200341106a21062003410c6a2102024002400240200141ff01710e03020001020b200621020c010b200521020b02400240200420022802004b0d0020002004360204410021020c010b02400240417f4100280284a4c680002202410447200241044b1b2202450d00200241ff017141ff01470d010b2003410c6a2102024002400240200141ff01710e03020001020b200621020c010b200521020b2003411c6a410c6a41c38280800036020020034185808080003602202003200236022c20032003412c6a3602242003200341186a36021c4100280290a1c680002102410028028ca1c6800021044100280280a4c680002101200341e8006a4202370200200341e0006a4102360200200341d8006a410f360200200341d4006a41bfb1c38000360200200341c8006a41d0b0c38000ad4280808080f00d84370200200341306a410c6a41ceb1c38000ad4280808080e00484370200200341e4006a2003411c6a360200200341e8b5c3800036025c20034104360250200341003602442003410036023820034281808080f009370230200441ecf2c08000200141024622011b200341306a200241d4f2c0800020011b280210118480808000000b200041800c3b0001410121020b200020023a000020034180016a2480808080000bec0302037f027e23808080800041d0026b2201248080808000200141086a10e189808000200141086a210202400240024020002d00100e03020001020b200141e0006a21020c010b200141b8016a21020b41022103024020022802004101470d00200229031021040240200029030020022903082205560d0020002903082004580d010b200120043703b802200120053703b00202400240417f4100280284a4c680002202410447200241044b1b2202450d00200241ff017141ff01470d010b200141c0026a410c6a41c482808000360200200141c4828080003602c402200120003602c0022001200141b0026a3602c8024100280290a1c680002102410028028ca1c6800021004100280280a4c680002103200141c0006a4202370200200141386a4102360200200141306a410f3602002001412c6a41bfb1c38000360200200141206a41d0b0c38000ad4280808080f00d84370200200141086a410c6a41ceb1c38000ad4280808080e004843702002001413c6a200141c0026a360200200141a8b6c38000360234200141043602282001410036021c2001410036021020014281808080e006370208200041ecf2c08000200341024622031b200141086a200241d4f2c0800020031b280210118480808000000b410021030b200141d0026a248080808000200341800c720b9d0b010b7f23808080800041106b22012480808080002001410036020c20014280808080800137020441002d00fca3c680001a0240024002400240413841002802c8a3c68000118180808000002202450d002002410a360204200241d8aec38000360200200241286a2203429ccab49c93e2e495cf00370300200241206a220442d1c5efcdcd82cde1ff00370300200241106a220542dca9a2e881dac9f914370300200241086a220642b4fd99f0e7c3fbd60e370300200241306a2207419382808000360200200241186a220841b882808000360200200141046a4100410110a78680800020012802082209200128020c220a41386c6a220b2002290300370300200b41086a2006290300370300200b41106a2005290300370300200b41186a2008290300370300200b41206a2004290300370300200b41286a2003290300370300200b41306a20072903003703002001200a41016a220336020c200241002802c0a3c680001180808080000041002d00fca3c680001a413841002802c8a3c68000118180808000002202450d01200242d1c5efcdcd82cde1ff00370320200242e1a1e0ae85b5ea80fd003703082002419382808000360230200241bb828080003602182002410b360204200241cdaec38000360200200241286a429ccab49c93e2e495cf00370300200241106a42b285eeed9386eed0d700370300024020012802042003470d00200141046a2003410110a78680800020012802082109200128020c21030b2009200341386c6a220b2002290300370300200b41306a200241306a290300370300200b41286a200241286a290300370300200b41206a200241206a290300370300200b41186a200241186a290300370300200b41106a200241106a290300370300200b41086a200241086a2903003703002001200341016a220336020c200241002802c0a3c680001180808080000041002d00fca3c680001a413841002802c8a3c68000118180808000002202450d02200242d1c5efcdcd82cde1ff00370320200242fc86a1bbccd1ddf4723703082002419382808000360230200241ba8280800036021820024112360204200241b9d0c38000360200200241286a429ccab49c93e2e495cf00370300200241106a428bebb7a9dff4a9fe6a370300024020012802042003470d00200141046a2003410110a78680800020012802082109200128020c21030b2009200341386c6a220b2002290300370300200b41306a200241306a290300370300200b41286a200241286a290300370300200b41206a200241206a290300370300200b41186a200241186a290300370300200b41106a200241106a290300370300200b41086a200241086a2903003703002001200341016a220336020c200241002802c0a3c680001180808080000041002d00fca3c680001a413841002802c8a3c68000118180808000002202450d032002428cc882a2e2dcdde2f000370320200242ccf3faf4aeedbdfafb003703082002418682808000360230200241b98280800036021820024111360204200241bcaec38000360200200241286a42aa8be08180efaa931e370300200241106a42cda992f8a2a6d9f2897f370300024020012802042003470d00200141046a2003410110a78680800020012802082109200128020c21030b2009200341386c6a220b2002290300370300200b41306a200241306a290300370300200b41286a200241286a290300370300200b41206a200241206a290300370300200b41186a200241186a290300370300200b41106a200241106a290300370300200b41086a200241086a290300370300200141046a41086a220b200341016a360200200241002802c0a3c6800011808080800000200041086a200b28020036020020002001290204370200200141106a2480808080000f0b4108413810b280808000000b4108413810b280808000000b4108413810b280808000000b4108413810b280808000000b961303027f027e017f2380808080004180036b220624808080800041012107200641013a00d001200642043703c801200642003703c00120064280808080c0003703b8012006427f3703b001200642003703a80120012903002108200641b8026a200210fa87808000024002400240024020062802ec0220062802f00272450d004103210720062903b80222092008560d0041002d00fca3c680001a0240024002400240410c41002802c8a3c68000118180808000002201450d0041002d00fca3c680001a412841002802c8a3c68000118180808000002207450d0120072002290000370000200741186a200241186a290000370000200741106a200241106a290000370000200741086a200241086a290000370000200720083700202001412836020820012007360204200141283602000240024020092008540d0041042107410021020c010b41002d00fca3c680001a410c41002802c8a3c68000118180808000002207450d0341002d00fca3c680001a412841002802c8a3c6800011818080800000220a450d04200a2002290000370000200a41186a200241186a290000370000200a41106a200241106a290000370000200a41086a200241086a290000370000200a2008427f7c370020200741283602082007200a36020420074128360200410121020b200641013a00302006410136022c2006200136022820064101360224200620023602202006200736021c200620023602182006427f37031020064200370308200641b8026a200641a8016a200641086a10fd84808000200641a8016a20042d0010200510d7888080000240024020062d00a8010d0041062102200410d888808000220741ff01714102470d01200641013a0060200642043703582006420037035020064280808080c0003703482006427f37034020064200370338200641e8006a200641b8026a200641386a10fd848080004100280284a4c680004105460d070c080b20062f01aa01210220062d00a90121070b2000418080808078360210200041026a20024108763a000020002002410874200741ff0171723b0100200641cc026a28020021010240200641d0026a2802002200450d002000410171210441002102024020004101460d002000417e7121072001210041002102034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002007200241026a2202470d000b0b2004450d0020012002410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b024020062802c802450d00200141002802c0a3c68000118080808000000b200641d8026a28020021010240200641dc026a2802002200450d002000410171210441002102024020004101460d002000417e7121072001210041002102034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002007200241026a2202470d000b0b2004450d0020012002410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b20062802d402450d07200141002802c0a3c68000118080808000000c070b4104410c10b280808000000b4101412810b280808000000b4104410c10b280808000000b4101412810b280808000000b2000418080808078360210200041026a41003a0000200020074108743b01000c020b4100280290a1c680002102410028028ca1c6800021074100280280a4c680002101200641f0026a4200370200200641ec026a41e8d1c38000360200200641e8026a4101360200200641e0026a4116360200200641dc026a41e88bc48000360200200641d0026a41f48ac48000ad4280808080900c84370200200641c4026a41fe8bc48000ad4280808080e00284370200200641e08bc480003602e402200641053602d802200641003602cc02200641003602c0022006428180808090243702b802200741ecf2c08000200141024622011b200641b8026a200241d4f2c0800020011b280210118480808000000b024002400240200328020041736a220241034b0d0020024102460d00200641013a00d00141002102200641003602cc0120064280808080c0003702c401200642043702bc012006427f3703b001200642003703a8010c010b200641a8016a200310d18c80800020062802b8012202418080808078460d010b20064198016a41086a200641a8016a41086a2903002208370300200641d8016a411c6a200641a8016a411c6a290200370200200641d8016a41246a200641a8016a41246a290200370200200641d8016a412c6a200641a8016a412c6a280200360200200641d8016a41086a2008370300200620062903a801220837039801200620062902bc013702ec01200620083703d801200620023602e801200641b8026a200641e8006a200641d8016a10fd84808000200641013a00b002200642043703a802200642003703a00220064280808080c000370398022006427f3703900220064200370388022000200641b8026a20064188026a10fd848080000c010b200620062d00aa0122023a009a01200620062f01a80122073b019801200041026a20023a0000200020073b01002000418080808078360210200641fc006a2802002101024020064180016a2802002200450d002000410171210441002102024020004101460d002000417e7121072001210041002102034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002007200241026a2202470d000b0b2004450d0020012002410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b02402006280278450d00200141002802c0a3c68000118080808000000b20064188016a280200210102402006418c016a2802002200450d002000410171210441002102024020004101460d002000417e7121072001210041002102034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002007200241026a2202470d000b0b2004450d0020012002410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b200628028401450d00200141002802c0a3c68000118080808000000b20064180036a2480808080000b140020002802003502004101200110fe808080000bc00301077f23808080800041206b220224808080800041002d00fca3c680001a0240410141002802c8a3c68000118180808000002203450d0020032001417f6a3a0000200241013602102002200336020c20024101360208200241086a4101410310ab86808000200228020c2203200228021022046a220541003b0000200541026a41003a000020022802082105024002400240200441036a22044104470d00200341036a2d0000210420032d0002210620032d0001210720032d000021082005450d01200341002802c0a3c68000118080808000000c010b2005418080808078470d01200341187621042003411076210620034180fe03714108762107200321080b200041033a00002000410d6a20073a00002000410c6a20083a0000200041106a41003a00002000410e6a20063a00002000410f6a20043a0000200041086a200141187441808080786a411875410274220341c0cec380006a2802003602002000200341e4cec380006a280200360204200241206a2480808080000f0b2002200436021c200220033602182002200536021441e0b3c3800041cb00200241146a4188b5c3800041a0b8c38000108981808000000b4101410110b280808000000be90403017f017e037f2380808080004190016b2203248080808000200341086a2001200241002802b8a1c68000118580808000000240024020032802080d00200041003a00000c010b200341186a41086a200341086a41086a290200220437030020032003290208370318200328021c2105024002402004a722064120490d00200041013a000020002005290000370001200041196a200541186a290000370000200041116a200541106a290000370000200041096a200541086a2900003700000c010b02400240417f4100280284a4c680002207410147200741014b1b2207417f460d00200741ff01710d010b2003413c6a4194afc3800041022001200210dc8a8080002003412c6a410c6a41c082808000360200200341c18280800036023020032003418f016a36023420032003413c6a36022c4100280290a1c680002102410028028ca1c6800021014100280280a4c68000210720034180016a4202370200200341f8006a4102360200200341f0006a4110360200200341ec006a4184bac38000360200200341e0006a4194bac38000ad4280808080900d84370200200341c8006a410c6a41fdbac38000ad4280808080800484370200200341fc006a2003412c6a360200200341f4b9c38000360274200341013602682003410036025c2003410036025020034281808080c003370248200141ecf2c08000200741024622071b200341c8006a200241d4f2c0800020071b28021011848080800000200328023c450d00200328024041002802c0a3c68000118080808000000b200041003a00000b200341246a20052006200328021828020c118580808000000b20034190016a2480808080000b2100200128021441c4cac380004105200141186a28020028020c118280808000000b140020012000280204200028020810dd808080000bb60503017f017e047f23808080800041b0026b2202248080808000200241086a2001412041002802b8a1c68000118580808000000240024020022802080d00200042043703000c010b200241186a41086a200241086a41086a29020022033703002002200229020837031820022003a722043602a0012002200228021c220536029c01024002402004450d0020022004417f6a3602a0012002200541016a36029c01420321030240024020052d00000e020100020b200241b0016a2002419c016a10ae8c80800020022903b00122034203510d01200241286a200241b8016a41f00010848e8080001a0b200041086a200241286a41f00010848e8080001a200020033703000c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b200241a4016a4194afc3800041022001412010dc8a808000200241286a410c6a41c082808000360200200241c18280800036022c2002200241af026a3602302002200241a4016a3602284100280290a1c680002101410028028ca1c6800021064100280280a4c680002107200241e8016a4202370200200241e0016a4102360200200241d8016a4110360200200241d4016a4184bac38000360200200241c8016a4194bac38000ad4280808080900d84370200200241b0016a410c6a41fdbac38000ad4280808080800484370200200241e4016a200241286a360200200241f4b9c380003602dc01200241013602d001200241003602c401200241003602b80120024281808080c0033702b001200641ecf2c08000200741024622071b200241b0016a200141d4f2c0800020071b2802101184808080000020022802a401450d0020022802a80141002802c0a3c68000118080808000000b200042043703000b200241246a20052004200228021828020c118580808000000b200241b0026a2480808080000be60603017f017e047f2380808080004190016b220324808080800020032001200241002802b8a1c68000118580808000000240024020032802000d0020004180808080783602000c010b200341106a41086a200341086a29020022043703002003200329020037031020032004a72205360228200320032802142206360224024002402005450d0020032005417f6a22073602282003200641016a36022402400240024002400240024020062d000022084103710e0400030102000b200841027621080c040b20054104490d0420032005417c6a3602282003200641046a360224200641036a2d000041187420062f000141087472200872220741027621082007418080044921070c020b200841044f0d0320054105490d0320032005417b6a3602282003200641056a360224200628000122084180808080044921070c010b2007450d0220032005417e6a3602282003200641026a36022420062d000141087420087241ffff03712207410276210820074180024921070b20070d0120084180024b0d010b200341c8006a200341246a200810c68580800020032802482208418080808078460d002000200329024c370204200020083602000c010b02400240417f4100280284a4c680002208410147200841014b1b2208417f460d00200841ff01710d010b2003413c6a4194afc3800041022001200210dc8a8080002003412c6a410c6a41c082808000360200200341c18280800036023020032003418f016a36023420032003413c6a36022c4100280290a1c680002102410028028ca1c6800021014100280280a4c68000210820034180016a4202370200200341f8006a4102360200200341f0006a4110360200200341ec006a4184bac38000360200200341e0006a4194bac38000ad4280808080900d84370200200341c8006a410c6a41fdbac38000ad4280808080800484370200200341fc006a2003412c6a360200200341f4b9c38000360274200341013602682003410036025c2003410036025020034281808080c003370248200141ecf2c08000200841024622081b200341c8006a200241d4f2c0800020081b28021011848080800000200328023c450d00200328024041002802c0a3c68000118080808000000b20004180808080783602000b2003411c6a20062005200328021028020c118580808000000b20034190016a2480808080000b9c0903017f017e057f2380808080004190016b220224808080800020022001412041002802b8a1c68000118580808000000240024020022802000d0020004180808080783602000c010b200241106a41086a200241086a29020022033703002002200229020037031020022003a72204360228200220022802142205360224024002402004450d0020022004417f6a22063602282002200541016a3602240240024002400240024020052d000022074103710e0403020001030b20044104490d0420022004417c6a3602282002200541046a360224200541036a2d000041187420052f000141087472200772220741808004490d04200741027621070c030b200741044f0d0320044105490d0320022004417b6a3602282002200541056a360224200528000122074180808080044f0d020c030b2006450d0220022004417e6a3602282002200541026a36022420052d000141087420077241ffff0371220741ff014d0d02200741027621070c010b200741027621070b200241c8006a200241246a200710c58580800020022802482207418080808078460d000240200229024c2203422088a7410b490d000240417f4100280284a4c680002201410247200141024b1b2201417f460d00200141ff01710d010b200241e0818080003602402002410636023020024198b5c3800036022c20022002412c6a36023c4100280290a1c680002101410028028ca1c6800021064100280280a4c68000210820024180016a4201370200200241f8006a4102360200200241f0006a4107360200200241ec006a419cc2c38000360200200241e0006a41a3c2c38000ad4280808080b00e84370200200241d4006a41afc1c38000ad4280808080d00484370200200241fc006a2002413c6a3602002002418cc2c38000360274200241023602682002410036025c20024100360250200242818080809016370248200641ecf2c08000200841024622081b200241c8006a200141d4f2c0800020081b280210118480808000000b20002003370204200020073602000c010b02400240417f4100280284a4c680002207410147200741014b1b2207417f460d00200741ff01710d010b2002413c6a4194afc3800041022001412010dc8a8080002002412c6a410c6a41c082808000360200200241c18280800036023020022002418f016a36023420022002413c6a36022c4100280290a1c680002101410028028ca1c6800021074100280280a4c68000210620024180016a4202370200200241f8006a4102360200200241f0006a4110360200200241ec006a4184bac38000360200200241e0006a4194bac38000ad4280808080900d84370200200241c8006a410c6a41fdbac38000ad4280808080800484370200200241fc006a2002412c6a360200200241f4b9c38000360274200241013602682002410036025c2002410036025020024281808080c003370248200741ecf2c08000200641024622061b200241c8006a200141d4f2c0800020061b28021011848080800000200228023c450d00200228024041002802c0a3c68000118080808000000b20004180808080783602000b2002411c6a20052004200228021028020c118580808000000b20024190016a2480808080000b140020012000280200200028020410dd808080000bbb0503017f017e037f2380808080004190016b2203248080808000200341086a2001200241002802b8a1c68000118580808000000240024020032802080d00200042003703000c010b200341186a41086a200341086a41086a290200220437030020032003290208370318200328021c2105024002402004a722064108490d002006417c7122074108460d002007410c460d0020074110460d002006416c6a4128490d0020052900002104200528000821022000200528000c36023c2000200236023820002004370308200042013703002000200529002c37031020002005280010360240200020052900243703302000200529001c37032820002005290014370320200041186a200541346a2900003703000c010b02400240417f4100280284a4c680002207410147200741014b1b2207417f460d00200741ff01710d010b2003413c6a4194afc3800041022001200210dc8a8080002003412c6a410c6a41c082808000360200200341c18280800036023020032003418f016a36023420032003413c6a36022c4100280290a1c680002102410028028ca1c6800021014100280280a4c68000210720034180016a4202370200200341f8006a4102360200200341f0006a4110360200200341ec006a4184bac38000360200200341e0006a4194bac38000ad4280808080900d84370200200341c8006a410c6a41fdbac38000ad4280808080800484370200200341fc006a2003412c6a360200200341f4b9c38000360274200341013602682003410036025c2003410036025020034281808080c003370248200141ecf2c08000200741024622071b200341c8006a200241d4f2c0800020071b28021011848080800000200328023c450d00200328024041002802c0a3c68000118080808000000b200042003703000b200341246a20052006200328021828020c118580808000000b20034190016a2480808080000be90403017f017e047f2380808080004190016b2202248080808000200241086a2001412041002802b8a1c68000118580808000000240024020022802080d00200041003a00000c010b200241186a41086a200241086a41086a290200220337030020022002290208370318200228021c2104024002402003a722054120490d00200041013a000020002004290000370001200041196a200441186a290000370000200041116a200441106a290000370000200041096a200441086a2900003700000c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b2002413c6a4194afc3800041022001412010dc8a8080002002412c6a410c6a41c082808000360200200241c18280800036023020022002418f016a36023420022002413c6a36022c4100280290a1c680002101410028028ca1c6800021064100280280a4c68000210720024180016a4202370200200241f8006a4102360200200241f0006a4110360200200241ec006a4184bac38000360200200241e0006a4194bac38000ad4280808080900d84370200200241c8006a410c6a41fdbac38000ad4280808080800484370200200241fc006a2002412c6a360200200241f4b9c38000360274200241013602682002410036025c2002410036025020024281808080c003370248200641ecf2c08000200741024622071b200241c8006a200141d4f2c0800020071b28021011848080800000200228023c450d00200228024041002802c0a3c68000118080808000000b200041003a00000b200241246a20042005200228021828020c118580808000000b20024190016a2480808080000bc30405017f017e027f017e027f2380808080004190016b2202248080808000200241086a2001412041002802b8a1c68000118580808000000240024020022802080d00420021030c010b200241186a41086a200241086a41086a290200220337030020022002290208370318200228021c2104024002402003a722054108490d0020042900002106420121030c010b02400240417f4100280284a4c680002207410147200741014b1b2207417f460d00200741ff01710d010b2002413c6a4194afc3800041022001412010dc8a8080002002412c6a410c6a41c082808000360200200241c18280800036023020022002418f016a36023420022002413c6a36022c4100280290a1c680002101410028028ca1c6800021074100280280a4c68000210820024180016a4202370200200241f8006a4102360200200241f0006a4110360200200241ec006a4184bac38000360200200241e0006a4194bac38000ad4280808080900d84370200200241c8006a410c6a41fdbac38000ad4280808080800484370200200241fc006a2002412c6a360200200241f4b9c38000360274200241013602682002410036025c2002410036025020024281808080c003370248200741ecf2c08000200841024622081b200241c8006a200141d4f2c0800020081b28021011848080800000200228023c450d00200228024041002802c0a3c68000118080808000000b42002106420021030b200241246a20042005200228021828020c118580808000000b200020063703082000200337030020024190016a2480808080000bbf0405017f017e027f017e027f2380808080004190016b2202248080808000200241086a2001412041002802b8a1c68000118580808000000240024020022802080d00420021030c010b200241186a41086a200241086a41086a290200220337030020022002290208370318200228021c2104024002402003a722054108490d0020042900002106420121030c010b02400240417f4100280284a4c680002207410147200741014b1b2207417f460d00200741ff01710d010b2002413c6a4194afc3800041022001412010dc8a8080002002412c6a410c6a41c082808000360200200241c18280800036023020022002418f016a36023420022002413c6a36022c4100280290a1c680002101410028028ca1c6800021074100280280a4c68000210820024180016a4202370200200241f8006a4102360200200241f0006a4110360200200241ec006a4184bac38000360200200241e0006a4194bac38000ad4280808080900d84370200200241c8006a410c6a41fdbac38000ad4280808080800484370200200241fc006a2002412c6a360200200241f4b9c38000360274200241013602682002410036025c2002410036025020024281808080c003370248200741ecf2c08000200841024622081b200241c8006a200141d4f2c0800020081b28021011848080800000200228023c450d00200228024041002802c0a3c68000118080808000000b420021030b200241246a20042005200228021828020c118580808000000b200020063703082000200337030020024190016a2480808080000bec0405017f017e037f017e017f2380808080004190016b2202248080808000200241086a2001412041002802b8a1c68000118580808000000240024020022802080d00200041033a00100c010b200241186a41086a200241086a41086a290200220337030020022002290208370318200228021c2104024002402003a722054108490d0020054178714108460d0020054110460d0020042d0010220641024b0d002004290000210320042900082107200020063a001020002007370308200020033703000c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b2002413c6a4194afc3800041022001412010dc8a8080002002412c6a410c6a41c082808000360200200241c18280800036023020022002418f016a36023420022002413c6a36022c4100280290a1c680002101410028028ca1c6800021064100280280a4c68000210820024180016a4202370200200241f8006a4102360200200241f0006a4110360200200241ec006a4184bac38000360200200241e0006a4194bac38000ad4280808080900d84370200200241c8006a410c6a41fdbac38000ad4280808080800484370200200241fc006a2002412c6a360200200241f4b9c38000360274200241013602682002410036025c2002410036025020024281808080c003370248200641ecf2c08000200841024622081b200241c8006a200141d4f2c0800020081b28021011848080800000200228023c450d00200228024041002802c0a3c68000118080808000000b200041033a00100b200241246a20042005200228021828020c118580808000000b20024190016a2480808080000baa0703027f017e047f2380808080004190016b2203248080808000200341086a2001200241002802b8a1c68000118580808000000240024020032802080d0041808080807821040c010b200341186a41086a200341086a41086a290200220537030020032003290208370318200328021c2106024002402005a72207450d002007417f6a21080240024002400240024020062d000022044103710e0400030201000b200641016a2109200441027621040c030b20074105490d03200441034b0d0320062800012204418080808004490d03200641056a21092007417b6a21080c020b20074104490d02200641036a2d000041187420062f000141087472200472220441808004490d0220044102762104200641046a21092007417c6a21080c010b2008450d0120062d000141087420047241ffff03712204418002490d01200641026a21092007417e6a2108200441027621040b20082004490d0002400240024020040d00410121080c010b2004417f4c0d03200441002802c8a3c68000118180808000002208450d01200841002004108a8e8080001a0b20082009200410848e8080002108200341246a20062007200328021828020c118580808000002001200241002802a0a1c68000118480808000002004ad4220862008ad8421050c030b4101200410b280808000000b02400240417f4100280284a4c680002204410147200441014b1b2204417f460d00200441ff01710d010b2003413c6a4194afc3800041022001200210dc8a8080002003412c6a410c6a41c082808000360200200341c18280800036023020032003418f016a36023420032003413c6a36022c4100280290a1c680002104410028028ca1c6800021024100280280a4c68000210120034180016a4202370200200341f8006a4102360200200341f0006a4110360200200341ec006a4184bac38000360200200341e0006a4194bac38000ad4280808080900d84370200200341c8006a410c6a41fdbac38000ad4280808080800484370200200341fc006a2003412c6a360200200341f4b9c38000360274200341013602682003410036025c2003410036025020034281808080c003370248200241ecf2c08000200141024622011b200341c8006a200441d4f2c0800020011b28021011848080800000200328023c450d00200328024041002802c0a3c68000118080808000000b200341246a20062007200328021828020c1185808080000041808080807821040c010b10ae80808000000b200020053702042000200436020020034190016a2480808080000ba90903027f017e067f23808080800041b0016b2201248080808000200142fc90b9e5d09296e777370308200142a6d4e6f1a4dd959860370300200142a0b9ab8f9ae5999778370318200142f999a7c78cd1d1cdb17f370310200141286a2001412041002802b8a1c6800011858080800000024002400240024020012802280d0041818080807821020c010b200141386a41086a200141286a41086a290200220337030020012001290228370338200128023c21040240024002402003a722050d000c010b2005417f6a210602400240024002400240024020042d000022024103710e0400010402000b200441016a2107200241027621080c040b20060d010c040b20054105490d03200241034b0d0320042800012208418080808004490d03200441056a21072005417b6a21060c020b20042d000141087420027241ffff03712202418002490d02200441026a21072005417e6a2106200241027621080c010b20054104490d01200441036a2d000041187420042f000141087472200272220241808004490d0120024102762108200441046a21072005417c6a21060b2006450d002006417f6a21090240024002400240024020072d000022024103710e0400010203000b200741016a2107200241027621020c030b2009450d0320072d000141087420027241ffff03712202418002490d03200741026a21072006417e6a2109200241027621020c020b20064104490d02200741036a2d000041187420072f000141087472200272220241808004490d0220024102762102200741046a21072006417c6a21090c010b20064105490d01200241034b0d0120072800012202418080808004490d01200741056a21072006417b6a21090b20092002490d000240024020020d00410121060c010b2002417f4c0d04200241002802c8a3c68000118180808000002206450d05200641002002108a8e8080001a0b2002ad42208620062007200210848e808000ad8421030c010b02400240417f4100280284a4c680002202410147200241014b1b2202417f460d00200241ff01710d010b200141dc006a4194afc3800041022001412010dc8a808000200141cc006a410c6a41c082808000360200200141c1828080003602502001200141af016a3602542001200141dc006a36024c4100280290a1c680002102410028028ca1c6800021064100280280a4c680002107200141a0016a420237020020014198016a410236020020014190016a41103602002001418c016a4184bac3800036020020014180016a4194bac38000ad4280808080900d84370200200141e8006a410c6a41fdbac38000ad42808080808004843702002001419c016a200141cc006a360200200141f4b9c380003602940120014101360288012001410036027c2001410036027020014281808080c003370268200641ecf2c08000200741024622071b200141e8006a200241d4f2c0800020071b28021011848080800000200128025c450d00200128026041002802c0a3c68000118080808000000b41818080807821020b200141c4006a20042005200128023828020c118580808000000b2000200836020c2000200337020420002002360200200141b0016a2480808080000f0b10ae80808000000b4101200210b280808000000bb90703017f017e057f23808080800041b0016b2201248080808000200142f0fe93e98686d7ab8c7f37030820014280eee1b0e3b7afe9987f37030020014282fceae49dd9e2861d370318200142de8c84a1ecd0a6d30c370310200141206a2001412041002802b8a1c68000118580808000000240024002402001280220450d00200141306a41086a200141206a41086a29020022023703002001200129022037033020012002a72203360248200120012802342204360244024002402003450d0020012003417f6a22053602482001200441016a3602440240024002400240024020042d000022064103710e0400030102000b200641027621060c030b20034104490d0320012003417c6a3602482001200441046a360244200441036a2d000041187420042f000141087472200672220641808004490d03200641027621060c020b200641044f0d0220034105490d0220012003417b6a3602482001200441056a360244200428000122064180808080044f0d010c020b2005450d0120012003417e6a3602482001200441026a36024420042d000141087420067241ffff03712206418002490d01200641027621060b200141e8006a200141c4006a200610ca8580800020012802682206418080808078460d00200129026c21020c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b200141dc006a4194afc3800041022001412010dc8a808000200141cc006a410c6a41c082808000360200200141c1828080003602502001200141af016a3602542001200141dc006a36024c4100280290a1c680002106410028028ca1c6800021054100280280a4c680002107200141a0016a420237020020014198016a410236020020014190016a41103602002001418c016a4184bac3800036020020014180016a4194bac38000ad4280808080900d84370200200141e8006a410c6a41fdbac38000ad42808080808004843702002001419c016a200141cc006a360200200141f4b9c380003602940120014101360288012001410036027c2001410036027020014281808080c003370268200541ecf2c08000200741024622071b200141e8006a200641d4f2c0800020071b28021011848080800000200128025c450d00200128026041002802c0a3c68000118080808000000b41808080807821060b2001413c6a20042003200128023028020c118580808000002006418080808078470d010b2000410036020820004280808080103702000c010b20002002370204200020063602000b200141b0016a2480808080000ba20503027f017e047f23808080800041b0016b2201248080808000200142fc90b9e5d09296e777370308200142a6d4e6f1a4dd95986037030020014293bb8a9cbb9ab6b31a370318200142ffabedd185d3d8d216370310200141286a2001412041002802b8a1c68000118580808000000240024020012802280d00410321020c010b200141386a41086a200141286a41086a290200220337030020012001290228370338200128023c21040240024002402003a72205450d000240024020042d000022020e03000401020b2005417b6a417b4b0d0120042800012106410021020c030b410221020c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b200141dc006a4194afc3800041022001412010dc8a808000200141cc006a410c6a41c082808000360200200141c1828080003602502001200141af016a3602542001200141dc006a36024c4100280290a1c680002106410028028ca1c6800021024100280280a4c680002107200141a0016a420237020020014198016a410236020020014190016a41103602002001418c016a4184bac3800036020020014180016a4194bac38000ad4280808080900d84370200200141e8006a410c6a41fdbac38000ad42808080808004843702002001419c016a200141cc006a360200200141f4b9c380003602940120014101360288012001410036027c2001410036027020014281808080c003370268200241ecf2c08000200741024622071b200141e8006a200641d4f2c0800020071b28021011848080800000200128025c450d00200128026041002802c0a3c68000118080808000000b410321020b0b200141c4006a20042005200128023828020c118580808000000b2000200636020420002002360200200141b0016a2480808080000b910503027f017e057f23808080800041b0016b2200248080808000200042fc90b9e5d09296e777370308200042a6d4e6f1a4dd959860370300200042beee8ae9ce8fa2b1977f37031820004295d4f9afa2868fb86437031041002101200041286a2000412041002802b8a1c680001185808080000002402000280228450d00200041386a41086a200041286a41086a290200220237030020002000290228370338200028023c2103024002402002a72201450d0020032d000041ff01712204450d004101410220044101461b21040c010b410021040b02402004410220011b22044102470d000240417f4100280284a4c680002205410147200541014b1b2205417f460d00200541ff01710d010b200041dc006a4194afc3800041022000412010dc8a808000200041cc006a410c6a41c082808000360200200041c1828080003602502000200041af016a3602542000200041dc006a36024c4100280290a1c680002105410028028ca1c6800021064100280280a4c680002107200041a0016a420237020020004198016a410236020020004190016a41103602002000418c016a4184bac3800036020020004180016a4194bac38000ad4280808080900d84370200200041e8006a410c6a41fdbac38000ad42808080808004843702002000419c016a200041cc006a360200200041f4b9c380003602940120004101360288012000410036027c2000410036027020004281808080c003370268200641ecf2c08000200741024622071b200041e8006a200541d4f2c0800020071b28021011848080800000200028025c450d00200028026041002802c0a3c68000118080808000000b200041c4006a20032001200028023828020c11858080800000200441017121010b200041b0016a24808080800020010bb80703017f017e057f23808080800041b0016b2201248080808000200142fc90b9e5d09296e777370308200142a6d4e6f1a4dd959860370300200142f4fa97f89782c7a62d37031820014299cfe7ffe3b8eac708370310200141206a2001412041002802b8a1c68000118580808000000240024002402001280220450d00200141306a41086a200141206a41086a29020022023703002001200129022037033020012002a72203360248200120012802342204360244024002402003450d0020012003417f6a22053602482001200441016a3602440240024002400240024020042d000022064103710e0400030102000b200641027621060c030b20034104490d0320012003417c6a3602482001200441046a360244200441036a2d000041187420042f000141087472200672220641808004490d03200641027621060c020b200641044f0d0220034105490d0220012003417b6a3602482001200441056a360244200428000122064180808080044f0d010c020b2005450d0120012003417e6a3602482001200441026a36024420042d000141087420067241ffff03712206418002490d01200641027621060b200141e8006a200141c4006a200610c98580800020012802682206418080808078460d00200129026c21020c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b200141dc006a4194afc3800041022001412010dc8a808000200141cc006a410c6a41c082808000360200200141c1828080003602502001200141af016a3602542001200141dc006a36024c4100280290a1c680002106410028028ca1c6800021054100280280a4c680002107200141a0016a420237020020014198016a410236020020014190016a41103602002001418c016a4184bac3800036020020014180016a4194bac38000ad4280808080900d84370200200141e8006a410c6a41fdbac38000ad42808080808004843702002001419c016a200141cc006a360200200141f4b9c380003602940120014101360288012001410036027c2001410036027020014281808080c003370268200541ecf2c08000200741024622071b200141e8006a200641d4f2c0800020071b28021011848080800000200128025c450d00200128026041002802c0a3c68000118080808000000b41808080807821060b2001413c6a20042003200128023028020c118580808000002006418080808078470d010b2000410036020820004280808080c0003702000c010b20002002370204200020063602000b200141b0016a2480808080000ba80603027f017e047f23808080800041d0016b2201248080808000200142fc90b9e5d09296e777370328200142a6d4e6f1a4dd959860370320200142f7b6fccfd083b2cf4d370338200142afd2c6ffdbc495bc08370330200141c8006a200141206a412041002802b8a1c68000118580808000000240024020012802480d00410221020c010b200141d8006a41086a200141c8006a41086a290200220337030020012001290248370358200128025c2104024002402003a722054120490d000240024020054120460d0020042d002041ff01712202450d004101410220024101461b21020c010b410021020b20054120460d0020024102460d00200141186a200441186a290000370300200141106a200441106a290000370300200141086a200441086a290000370300200120042900003703000c010b02400240417f4100280284a4c680002202410147200241014b1b2202417f460d00200241ff01710d010b200141fc006a4194afc380004102200141206a412010dc8a808000200141ec006a410c6a41c082808000360200200141c1828080003602702001200141cf016a3602742001200141fc006a36026c4100280290a1c680002102410028028ca1c6800021064100280280a4c680002107200141c0016a4202370200200141b8016a4102360200200141b0016a4110360200200141ac016a4184bac38000360200200141a0016a4194bac38000ad4280808080900d8437020020014188016a410c6a41fdbac38000ad4280808080800484370200200141bc016a200141ec006a360200200141f4b9c380003602b401200141013602a8012001410036029c01200141003602900120014281808080c00337028801200641ecf2c08000200741024622071b20014188016a200241d4f2c0800020071b28021011848080800000200128027c450d0020012802800141002802c0a3c68000118080808000000b410221020b200141e4006a20042005200128025828020c118580808000000b200020023a000020002001290300370001200041096a200141086a290300370000200041116a200141106a290300370000200041196a200141186a290300370000200141d0016a2480808080000b900201037f23808080800041306b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd2037030020014292d189e4a1e58a9fcc00370318200142aa9f83c8cbf687edfa003703100240024020002802082202417f4c0d0041002d00fca3c680001a200241286c410472220241002802c8a3c68000118180808000002203450d0120012003360228200120023602242001410036022c2000200141246a108087808000200128022421002001412020012802282202200128022c41002802e0a1c680001186808080000002402000450d00200241002802c0a3c68000118080808000000b200141306a2480808080000f0b10ae80808000000b4101200210b280808000000b8e0201037f23808080800041306b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd2037030020014282fceae49dd9e2861d370318200142de8c84a1ecd0a6d30c3703100240024020002802082202417f4c0d0041002d00fca3c680001a200241286c410472220241002802c8a3c68000118180808000002203450d0120012003360228200120023602242001410036022c2000200141246a108087808000200128022421002001412020012802282202200128022c41002802e0a1c680001186808080000002402000450d00200241002802c0a3c68000118080808000000b200141306a2480808080000f0b10ae80808000000b4101200210b280808000000b860203017f017e027f23808080800041306b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd20370300200142d8d2dfcce2f8e59341370318200142faa5fa8ea9819ff1bd7f3703102000290300210241002d00fca3c680001a02404101410e20024203511b220341002802c8a3c68000118180808000002204450d0020012004360228200120033602242001410036022c2000200141246a10b28c808000200128022421002001412020012802282203200128022c41002802e0a1c680001186808080000002402000450d00200341002802c0a3c68000118080808000000b200141306a2480808080000f0b4101200310b280808000000bae0201037f23808080800041306b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd2037030020014282fceae49dd9e2861d370318200142de8c84a1ecd0a6d30c3703100240024020002802082202417f4c0d0041002d00fca3c680001a200241286c410472220241002802c8a3c68000118180808000002203450d0120012003360228200120023602242001410036022c2000200141246a108087808000200128022421022001412020012802282203200128022c41002802e0a1c680001186808080000002402002450d00200341002802c0a3c68000118080808000000b02402000280200450d00200028020441002802c0a3c68000118080808000000b200141306a2480808080000f0b10ae80808000000b4101200210b280808000000b840604027f017e047f017e23808080800041b0016b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd2037030020014290f1f7a1ea9083df363703182001428b87a199bee8b0f0ac7f370310200141286a2001412041002802b8a1c68000118580808000000240024020012802280d00410321020c010b200141386a41086a200141286a41086a290200220337030020012001290228370338200128023c2104024002402003a72205450d0020054109490d0020042d000041ff01714101470d00200541776a4108490d0020054111460d0020042d001122024103490d010b02400240417f4100280284a4c680002202410147200241014b1b2202417f460d00200241ff01710d010b200141dc006a4194afc3800041022001412010dc8a808000200141cc006a410c6a41c082808000360200200141c1828080003602502001200141af016a3602542001200141dc006a36024c4100280290a1c680002102410028028ca1c6800021064100280280a4c680002107200141a0016a420237020020014198016a410236020020014190016a41103602002001418c016a4184bac3800036020020014180016a4194bac38000ad4280808080900d84370200200141e8006a410c6a41fdbac38000ad42808080808004843702002001419c016a200141cc006a360200200141f4b9c380003602940120014101360288012001410036027c2001410036027020014281808080c003370268200641ecf2c08000200741024622071b200141e8006a200241d4f2c0800020071b28021011848080800000200128025c450d00200128026041002802c0a3c68000118080808000000b200141c4006a20042005200128023828020c11858080800000410321020c010b2004290001210820042900092103200141c4006a20042005200128023828020c118580808000002001412041002802a0a1c68000118480808000000b200020033703082000200837030020002001280068360011200020023a0010200041146a200141eb006a280000360000200141b0016a2480808080000be10201027f23808080800041c0006b2201248080808000200142fc90b9e5d09296e777370308200142a6d4e6f1a4dd959860370300200142f4fa97f89782c7a62d37031820014299cfe7ffe3b8eac7083703100240024002400240024020002d000022020e050001020304000b2001200041016a36023420012000410c6a2902003702380c030b2001200041016a36023420012000410c6a2902003702380c020b2001200041016a36023420012000410c6a2902003702380c010b2001200041086a2902003702340b20012002360230200141246a200141306a10ed8480800020014120200141246a4100280298a1c680001185808080000002400240024002400240024020002d00000e0400010203050b200041086a21000c030b200041086a21000c020b200041086a21000c010b200041046a21000b2000280200450d00200028020441002802c0a3c68000118080808000000b200141c0006a2480808080000b9c0706017f027e027f017e037f017e23808080800041c0016b2200248080808000200042919fd78da1a1ad84ff003703182000429ceccef7a6c0dedd20370310200042a4feeaa7f9c4ff8f0e370328200042e99dbcf9dba59e96b37f370320200041306a200041106a412041002802b8a1c68000118580808000000240024020002802300d00420021010c010b200041c0006a41086a200041306a41086a29020022023703002000200029023037034020002802442103024002402002a722044108490d0020044178714108460d0020032900082105420121010c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b200041e4006a4194afc380004102200041106a412010dc8a808000200041d4006a410c6a41c082808000360200200041c1828080003602582000200041bf016a36025c2000200041e4006a3602544100280290a1c680002106410028028ca1c6800021074100280280a4c680002108200041a8016a4202370200200041a0016a410236020020004198016a411036020020004194016a4184bac3800036020020004188016a4194bac38000ad4280808080900d84370200200041f0006a410c6a41fdbac38000ad4280808080800484370200200041a4016a200041d4006a360200200041f4b9c3800036029c01200041013602900120004100360284012000410036027820004281808080c003370270200741ecf2c08000200841024622081b200041f0006a200641d4f2c0800020081b280210118480808000002000280264450d00200028026841002802c0a3c68000118080808000000b420021010b200041cc006a20032004200028024028020c118580808000000b200042fc90b9e5d09296e777370378200042a6d4e6f1a4dd959860370370200042d3d8c5d2a9b9d2c1ac7f3703880120004282ca868eabf3add0cf00370380012000200041f0006a10e6888080002000290308210220002903002109200042919fd78da1a1ad84ff003703782000429ceccef7a6c0dedd20370370200042a4feeaa7f9c4ff8f0e37038801200042e99dbcf9dba59e96b37f3703800141002d00fca3c680001a0240411041002802c8a3c680001181808080000022040d004101411010b280808000000b2004420020052001501b3700002004200242002009a71b370008200041f0006a41202004411041002802e0a1c6800011868080800000200441002802c0a3c6800011808080800000200041c0016a2480808080000bee0e05017f017e047f017e017f23808080800041c0016b2202248080808000200242919fd78da1a1ad84ff003703082002429ceccef7a6c0dedd2037030020024282ea96a7a8d58af34c370318200242b7f092fcc2eaf1f65f370310200241286a2002412041002802b8a1c68000118580808000000240024002400240024002402002280228450d00200241386a41086a200241286a41086a29020022033703002002200229022837033820022003a7220436024c2002200228023c2205360248024002402004450d0020022004417f6a220636024c2002200541016a36024802400240024002400240024020052d000022074103710e0400030102000b200741027621070c040b20044104490d0420022004417c6a36024c2002200541046a360248200541036a2d000041187420052f000141087472200772220641027621072006418080044921060c020b200741044f0d0320044105490d0320022004417b6a36024c2002200541056a360248200528000122074180808080044921060c010b2006450d0220022004417e6a36024c2002200541026a36024820052d000141087420077241ffff03712206410276210720064180024921060b20060d01200741e4004b0d010b200241f0006a200241c8006a200710cb8580800020022802702207418080808078460d00200229027421080c010b02400240417f4100280284a4c680002207410147200741014b1b2207417f460d00200741ff01710d010b200241e4006a4194afc3800041022002412010dc8a808000200241d0006a410c6a41c082808000360200200241c1828080003602542002200241bf016a3602582002200241e4006a3602504100280290a1c680002107410028028ca1c6800021064100280280a4c680002109200241a8016a4202370200200241a0016a410236020020024198016a411036020020024194016a4184bac3800036020020024188016a4194bac38000ad4280808080900d84370200200241f0006a410c6a41fdbac38000ad4280808080800484370200200241a4016a200241d0006a360200200241f4b9c3800036029c01200241013602900120024100360284012002410036027820024281808080c003370270200641ecf2c08000200941024622091b200241f0006a200741d4f2c0800020091b280210118480808000002002280264450d00200228026841002802c0a3c68000118080808000000b41808080807821070b200241c4006a20052004200228023828020c118580808000002007418080808078460d002002200736023820002903002103200128020021042002200837023c20032004ad5a0d010c040b41002107200241003602402002428080808080013703382000290300220320012802002204ad540d030c010b0240024002402008422088a7220541e3004b0d00200541e300470d020c010b2008a72207200741106a200541047441706a10fe8d8080001a200228023821070b41e3002105200241e3003602400b20052007470d010b200241386a200710a286808000200228024021050b200228023c20054104746a22072004360208200720033703002002200228024041016a3602400c010b0240417f4100280284a4c680002207410247200741024b1b2207417f460d00200741ff01710d010b2002410c6a41858080800036020020022001360208200241cf80808000360204200220003602004100280290a1c680002107410028028ca1c6800021044100280280a4c680002105200241a8016a4202370200200241a0016a410336020020024198016a410d36020020024194016a41b5b3c3800036020020024188016a41dcb2c38000ad4280808080900b84370200200241f0006a410c6a41c2b3c38000ad4280808080b00184370200200241a4016a2002360200200241c4b2c3800036029c0120024100360284012002410036027820024281808080c0d1003702702002410236029001200441ecf2c08000200541024622051b200241f0006a200741d4f2c0800020051b280210118480808000000b200241d0006a41086a200241386a41086a28020036020020022002290338220337035002400240024002402003a72207418080808078470d00200242919fd78da1a1ad84ff003703782002429ceccef7a6c0dedd2037037020024282ea96a7a8d58af34c37038801200242b7f092fcc2eaf1f65f37038001200241f0006a412041002802a0a1c68000118480808000000c010b200242919fd78da1a1ad84ff003703782002429ceccef7a6c0dedd2037037020024282ea96a7a8d58af34c37038801200242b7f092fcc2eaf1f65f3703800120022802582204417f4c0d0141002d00fca3c680001a2004410474410472220441002802c8a3c68000118180808000002205450d02200220053602042002200436020020024100360208200241d0006a200210828780800020022802002104200241f0006a412020022802042205200228020841002802e0a1c680001186808080000002402004450d00200541002802c0a3c68000118080808000000b2007450d00200228025441002802c0a3c68000118080808000000b200241c0016a2480808080000f0b10ae80808000000b4101200410b280808000000bc00301077f23808080800041206b220224808080800041002d00fca3c680001a0240410141002802c8a3c68000118180808000002203450d0020032001417f6a3a0000200241013602102002200336020c20024101360208200241086a4101410310ab86808000200228020c2203200228021022046a220541003b0000200541026a41003a000020022802082105024002400240200441036a22044104470d00200341036a2d0000210420032d0002210620032d0001210720032d000021082005450d01200341002802c0a3c68000118080808000000c010b2005418080808078470d01200341187621042003411076210620034180fe03714108762107200321080b200041033a00002000410d6a20073a00002000410c6a20083a0000200041106a41033a00002000410e6a20063a00002000410f6a20043a0000200041086a200141187441808080786a41187541027422034188cfc380006a2802003602002000200341b8cfc380006a280200360204200241206a2480808080000f0b2002200436021c200220033602182002200536021441e0b3c3800041cb00200241146a4188b5c3800041fcbbc38000108981808000000b4101410110b280808000000bc30b06057f017e037f017e017f017e23808080800041c0016b22022480808080000240024002402001280200220128020422034108490d002001200341786a220436020420012001280200220541086a220636020020044120490d01200529000021072001200341586a22083602042001200541286a2204360200200241086a200641086a290000370300200241106a200641106a290000370300200241186a200641186a29000037030020022006290000370300024020084120490d002001200341b87f6a22063602042001200541c8006a360200200241a0016a41086a200441086a290000370300200241a0016a41106a200441106a290000370300200241a0016a41186a200441186a290000370300200220042900003703a00120064108490d002001200341b07f6a22043602042001200541d0006a220636020020024180016a41086a2208200241a0016a41086a29030037030020024180016a41106a2209200241a0016a41106a29030037030020024180016a41186a220a200241a0016a41186a290300370300200220022903a00137038001200441c000490d002005290048210b2001200341f07e6a220c360204200120054190016a2204360200200241206a41086a200641086a290000370300200241206a41106a200641106a290000370300200241206a41186a200641186a290000370300200241206a41206a200641206a290000370300200241206a41286a200641286a290000370300200241206a41306a200641306a290000370300200241206a41386a200641386a29000037030020022006290000370320200241e0006a41186a200a290300370300200241e0006a41106a2009290300370300200241e0006a41086a20082903003703002002200229038001370360200c4120490d002001200341d07e6a22063602042001200541b0016a360200200241a0016a41086a2208200441086a290000370300200241a0016a41106a2209200441106a290000370300200241a0016a41186a220a200441186a290000370300200220042900003703a00120064108490d002001200341c87e6a22043602042001200541b8016a220636020020024180016a41086a200829030037030020024180016a41106a200929030037030020024180016a41186a200a290300370300200220022903a00137038001200441c000490d0020052900b001210d2001200341887e6a3602042001200541f8016a360200200041c0016a2006290000370000200041c8016a200641086a290000370000200041d0016a200641106a290000370000200041d8016a200641186a290000370000200041e0016a200641206a290000370000200041e8016a200641286a290000370000200041f0016a200641306a290000370000200041f8016a200641386a290000370000200041b0016a20024180016a41186a290300370300200041a8016a20024180016a41106a290300370300200041a0016a20024180016a41086a29030037030020004198016a200229038001370300200041206a200241186a290300370000200041186a200241106a290300370000200041106a200241086a29030037000020002002290300370008200041306a2002290360370300200041386a200241e0006a41086a290300370300200041c0006a200241e0006a41106a290300370300200041c8006a200241e0006a41186a290300370300200041d8006a2002290320370300200041e0006a200241206a41086a290300370300200041e8006a200241206a41106a290300370300200041f0006a200241206a41186a290300370300200041f8006a200241206a41206a29030037030020004180016a200241206a41286a29030037030020004188016a200241206a41306a29030037030020004190016a200241206a41386a290300370300200041b8016a200d370300200041d0006a200b370300200041286a2007370300200042003703000c030b200042013703000c020b200042013703000c010b200042013703000b200241c0016a2480808080000bdd0a04067f017e027f017e23808080800041d0006b2201248080808000200141286a41eabdc38000410c41cfbdc38000411041a0b5c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041e00041002802c8a3c68000118180808000002202450d00200242c4a8f7cba2aea4ad35370308200241f9bdc38000360240200241f8bdc38000360220200241ff8180800036021820024102360204200241f6bdc38000360200200241d0006a42919fe7f19ec9d1a177370300200241c8006a42d0ffe6c89b859fc430370300200241306a42ecb2f697e9f8a9f860370300200241286a42ef899cf7a6a3ca9dd100370300200241106a42949384fce98ae9ac977f370300200241d8006a41b482808000360200200241c4006a4101360200200241386a41b382808000360200200241246a410136020020014103360248200120023602402001200241e0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a200141106a410c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024203370224200241bcf2c480003602202002410c36021c200241bba4c58000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024202370224200241d0fbc480003602202002410836021c200241b3a4c58000360218200241ff81808000360210200242949384fce98ae9ac977f370308200242c4a8f7cba2aea4ad353703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024206370224200241a7a4c580003602202002410536021c200241a2a4c58000360218200241c58280800036021020024285f5e9f2d6c0a4cd48370308200242ece297b9e9e4c9bdda00370300200141c0006a41086a200141106a41086a28020041016a220236020020012001290310220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024206370224200241a7a4c580003602202002410636021c200241ada4c58000360218200241c58280800036021020024285f5e9f2d6c0a4cd48370308200242ece297b9e9e4c9bdda00370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841e00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bde0a04067f017e027f017e23808080800041d0006b2201248080808000200141286a41eabdc38000410c41cfbdc38000411041a0b5c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041e00041002802c8a3c68000118180808000002202450d00200242c4a8f7cba2aea4ad35370308200241f9bdc38000360240200241f8bdc38000360220200241ff8180800036021820024102360204200241f6bdc38000360200200241d0006a42919fe7f19ec9d1a177370300200241c8006a42d0ffe6c89b859fc430370300200241306a42c6bb82f1efc886d58a7f370300200241286a42c6bdb6a4b5ec93c2d300370300200241106a42949384fce98ae9ac977f370300200241d8006a41b482808000360200200241c4006a4101360200200241386a41b782808000360200200241246a410136020020014103360248200120023602402001200241e0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a200141106a410c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024203370224200241bcf2c480003602202002410c36021c200241bba4c58000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024202370224200241d0fbc480003602202002410836021c200241b3a4c58000360218200241ff81808000360210200242949384fce98ae9ac977f370308200242c4a8f7cba2aea4ad353703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024206370224200241a7a4c580003602202002410536021c200241a2a4c58000360218200241c682808000360210200242c5bcc8a2879389a83a3703082002428f8de586a6e995cafa00370300200141c0006a41086a200141106a41086a28020041016a220236020020012001290310220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024206370224200241a7a4c580003602202002410636021c200241ada4c58000360218200241c682808000360210200242c5bcc8a2879389a83a3703082002428f8de586a6e995cafa00370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841e00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000be30201047f23808080800041206b220224808080800041002d00fca3c680001a0240410441002802c8a3c68000118180808000002203450d002002410c6a41086a22044100360200200220033602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000200241086a220520042802003602002002200229020c3703000240200128020822032001280200470d0020012003109d86808000200128020821030b2001280204200341e8006c6a220342a19eece481d0928e393703082003410b36024420034185bec38000360240200341c782808000360218200341003602002003200229030037034820032000290200370254200341013a0060200341106a42cdba88b6a38cd989b57f370300200341d0006a2005280200360200200341dc006a200041086a2802003602002001200128020841016a360208200241206a2480808080000f0b4101410410b280808000000be90503067f017e027f23808080800041d0006b2201248080808000200141286a41a1c1c38000410e41afc1c38000412541a0b5c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242e5f0b3f4e8a9b1b12a370308200241f9bdc38000360220200241f88180800036021820024101360204200241c5b5c38000360200200241106a4281ebc5ecd497b09a0a370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c4800036022020024100360218200241c882808000360210200242c6be9491c9dc9cca59370308200242c7e49fb5d2d4f6ebb47f370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bd60301087f23808080800041206b22022480808080002002410036021c200242808080801037021441002d00fca3c680001a0240412041002802c8a3c68000118180808000002203450d0020034200370000200341186a22044200370000200341106a22054200370000200341086a22064200370000200241146a4100412010b1828080002002280218200241146a41086a220728020022086a22092003290000370000200941086a2006290000370000200941106a2005290000370000200941186a20042900003700002007200841206a360200200341002802c0a3c6800011808080800000200241086a41086a22092007280200360200200220022902143703080240200128020822032001280200470d0020012003109d86808000200128020821030b2001280204200341e8006c6a220342c194a6a793ccc3a8573703082003410a36024420034186bfc38000360240200341f581808000360218200341003602002003200229030837034820032000290200370254200341013a0060200341106a42ab8bffbed784ffa5937f370300200341d0006a2009280200360200200341dc006a200041086a2802003602002001200128020841016a360208200241206a2480808080000f0b4101412010b280808000000be30201047f23808080800041206b220224808080800041002d00fca3c680001a0240410441002802c8a3c68000118180808000002203450d002002410c6a41086a22044100360200200220033602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000200241086a220520042802003602002002200229020c3703000240200128020822032001280200470d0020012003109d86808000200128020821030b2001280204200341e8006c6a2203428aaac8d3f1d38bddba7f37030820034106360244200341bbbfc38000360240200341c982808000360218200341003602002003200229030037034820032000290200370254200341013a0060200341106a42c6a4f3f9b3bbafd83e370300200341d0006a2005280200360200200341dc006a200041086a2802003602002001200128020841016a360208200241206a2480808080000f0b4101410410b280808000000be40201047f23808080800041206b220224808080800041002d00fca3c680001a0240410441002802c8a3c68000118180808000002203450d002002410c6a41086a22044100360200200220033602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10fb84808000200241086a220520042802003602002002200229020c3703000240200128020822032001280200470d0020012003109d86808000200128020821030b2001280204200341e8006c6a220342fddcc0b9c5fc86d6a17f37030820034106360244200341edbfc38000360240200341ca82808000360218200341003602002003200229030037034820032000290200370254200341013a0060200341106a4296b787c192cbb889d000370300200341d0006a2005280200360200200341dc006a200041086a2802003602002001200128020841016a360208200241206a2480808080000f0b4101410410b280808000000be30201047f23808080800041206b220224808080800041002d00fca3c680001a0240410441002802c8a3c68000118180808000002203450d002002410c6a41086a22044100360200200220033602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000200241086a220520042802003602002002200229020c3703000240200128020822032001280200470d0020012003109d86808000200128020821030b2001280204200341e8006c6a220342a19eece481d0928e393703082003410f36024420034199c0c38000360240200341c782808000360218200341003602002003200229030037034820032000290200370254200341013a0060200341106a42cdba88b6a38cd989b57f370300200341d0006a2005280200360200200341dc006a200041086a2802003602002001200128020841016a360208200241206a2480808080000f0b4101410410b280808000000be80201047f23808080800041306b22022480808080002002410036022020024280808080800137021841002d00fca3c680001a0240410441002802c8a3c68000118180808000002203450d00200241246a41086a220441003602002002200336022820024104360224200241186a200241246a108287808000200241086a41086a22052004280200360200200220022902243703080240200128020822032001280200470d0020012003109d86808000200128020821030b2001280204200341e8006c6a22034293bfa0a5e3cecac4ec003703082003410d360244200341dfc0c38000360240200341cb82808000360218200341003602002003200229030837034820032000290200370254200341013a0060200341106a429698dbdf9eeb9da04b370300200341d0006a2005280200360200200341dc006a200041086a2802003602002001200128020841016a360208200241306a2480808080000f0b4101410410b280808000000ba50301077f23808080800041106b220224808080800041002d00fca3c680001a02400240411841002802c8a3c68000118180808000002203450d00200320012802002204290000370000200341106a200441106a290000370000200341086a200441086a29000037000020022003360208200241183602042002411836020c2001280204210141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0120032001290000370000200341186a2205200141186a290000370000200341106a2206200141106a290000370000200341086a2207200141086a290000370000200241046a4118412010b18280800020022802082204200228020c22086a22012003290000370000200141086a2007290000370000200141106a2006290000370000200141186a200529000037000020022802042101200341002802c0a3c680001180808080000020002004200841206a4100280298a3c680001185808080000002402001450d00200441002802c0a3c68000118080808000000b200241106a2480808080000f0b4101411810b280808000000b4101412010b280808000000bd90201037f23808080800041206b2203248080808000024002402002417f4c0d0041002d00fca3c680001a2002410574410472220441002802c8a3c68000118180808000002205450d0120034100360214200320053602102003200436020c200320023602182003200341186a36021c2003411c6a2003410c6a10c08a80800002402002450d00200241057421052003280214210203400240200328020c20026b411f4b0d002003410c6a2002412010b182808000200328021421020b200328021020026a22042001290000370000200441086a200141086a290000370000200441106a200141106a290000370000200441186a200141186a2900003700002003200241206a2202360214200141206a2101200541606a22050d000b0b2000200329020c370200200041086a2003410c6a41086a280200360200200341206a2480808080000f0b10ae80808000000b4101200410b280808000000be90503067f017e027f23808080800041d0006b2201248080808000200141286a41a1c1c38000410e41afc1c38000412541a0b5c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242d7c9c3bdedbdd399957f370308200241f9bdc38000360220200241f28180800036021820024101360204200241c5b5c38000360200200241106a42a6f8d4ee83cba9e440370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c4800036022020024100360218200241cc82808000360210200242dfbcf99189a39d8f56370308200242b2f3abcf8fb9a3b15e370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b040041000bed0201027f23808080800041206b2202248080808000200028020028020021002002200128021441a0b5c380004100200141186a28020028020c118280808000003a000c2002200136020841012103200241013a000d20024100360204200220003602102002200041086a2201360214200220013602182002200136021c200241046a200241106a41b4c4c38000108d81808000200241146a41b4c4c38000108d81808000200241186a41c4c4c38000108d818080002002411c6a41d4c4c38000108d81808000210120022d000c210002400240200128020022010d00200041ff017141004721030c010b200041ff01710d0020022802082100024020014101470d0020022d000d41ff0171450d0020002d001c4104710d00410121032000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021030b200241206a24808080800020030be30101047f23808080800041106b22022480808080002000280200220041046a280200210320002802002100410121042001280214418fa5c080004101200141186a28020028020c118280808000002105200241003a0009200220053a00082002200136020402402003450d0003402002200036020c200241046a2002410c6a41d0cfc48000108e818080001a200041016a21002003417f6a22030d000b20022d000821050b0240200541ff01710d00200228020422002802144190a5c080004101200041186a28020028020c1182808080000021040b200241106a24808080800020040bf60201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120003100004101200110fe8080800021000c020b20002d00002103410021000340200220006a41ff006a413041d7002003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002d00002103410021000340200220006a41ff006a413041372003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b12002000280200280200200110ab888080000bf40201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000b9a0201027f23808080800041106b2202248080808000200220002802002200360204200128021441acc6c380004106200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a41b2c6c380004104200041046a41b8c6c38000108c8180800041c8c6c380004105200241046a41d0c6c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b140020002802002000280204200110e2808080000b3c00200128021420002802002d000041027422004190d0c380006a280200200041e8cfc380006a280200200141186a28020028020c118280808000000b9a0201027f23808080800041106b220224808080800020022000280200220041046a360204200128021441e0c6c380004109200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a41e9c6c38000410b200041b8c6c38000108c8180800041f4c6c380004109200241046a4180c7c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b1900200028020022002802002000280204200110e2808080000b0f002000280200200110ac888080000bf80202027f017e2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120002903004101200110fe8080800021000c020b20002903002104410021000340200220006a41ff006a413041d7002004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002903002104410021000340200220006a41ff006a413041372004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b2100200128021441c4cdc38000410f200141186a28020028020c118280808000000b1f002000280200220041046a280200200041086a280200200110e2808080000b9a0201027f23808080800041106b220224808080800020022000280200220041086a360204200128021441e0c8c380004106200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a41e6c8c380004108200041f0c8c38000108c818080004180c9c38000410a200241046a41e4c3c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010bab0201027f23808080800041106b220224808080800020002802002100200128021441bcaec380004111200141186a28020028020c118280808000002103200241003a000d200220033a000c2002200136020841012101200241086a4196c3c380004108200041226a41a0c3c38000108c8180800041b0c3c380004104200041b4c3c38000108c8180800041c4c3c38000410d200041016a41d4c3c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010bf80201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120003301004101200110fe8080800021000c020b20002f01002103410021000340200220006a41ff006a413041d7002003410f712204410a491b20046a3a00002000417f6a2100200341ffff037122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002f01002103410021000340200220006a41ff006a413041372003410f712204410a491b20046a3a00002000417f6a2100200341ffff037122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b370020012802144196c9c38000418ac9c3800020002802002d000022001b4107410c20001b200141186a28020028020c118280808000000b7901017f23808080800041106b220224808080800020022000280200220036020c200141d9c5c38000410b41e4c5c3800041052000410c6a41ecc5c3800041fcc5c380004105200041086a4184c6c380004194c6c3800041072002410c6a419cc6c3800010e0808080002100200241106a24808080800020000b9d0101017f23808080800041306b220224808080800020002802002100200241086a410c6a4202370200200241206a410c6a41cf808080003602002002410336020c20024190c8c280003602082002200041086a360228200241cf8080800036022420022000360220200141186a28020021002002200241206a36021020012802142000200241086a10d9808080002101200241306a24808080800020010bef0201037f2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120003100004101200110fe8080800021000c020b20002d00002103410021000340200220006a41ff006a413041d7002003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002d00002103410021000340200220006a41ff006a413041372003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000bf10202027f017e2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120002903004101200110fe8080800021000c020b20002903002104410021000340200220006a41ff006a413041d7002004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002903002104410021000340200220006a41ff006a413041372004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b02000b02000b02000b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0b02000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a41e4c3c380002005410c6a41e4c3c380002003200410fb80808000000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a41f4c3c380002005410c6a41f4c3c380002003200410fb80808000000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a4184c4c380002005410c6a4184c4c380002003200410fb80808000000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a4194c4c380002005410c6a4194c4c380002003200410fb80808000000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a41a4c4c380002005410c6a41a4c4c380002003200410fb80808000000b9b0101057f200020002802002201109b868080000240200028020822022001200028020c22036b4d0d002000280200210402400240200120026b2205200320056b22034d0d00200420016b20034f0d010b20002802042201200420056b22034102746a200120024102746a200541027410fe8d8080001a200020033602080f0b2000280204220020014102746a2000200341027410848e8080001a0b0b800302037f027e23808080800041306b2205248080808000200541106a41086a2001280200220141086a220628020036020020064100360200200520012902003703102001428080808010370200200541086a200541106a200220032004109287808000200528020c21040240024002400240200528020822030d00200528021021022005411c6a200528021422072005280218220610fc808080000240200528021c0d002006ad2108200721060c030b200529022021082002418080808078470d01200721020c020b2005280210450d02200528021441002802c0a3c68000118080808000000c020b200520083702282005200236021c20052006ad4220862007ad84370220419dc9c38000412b2005411c6a41c8c9c3800041b4cac38000108981808000000b2006ad4220862002ad8421092008a7210202402001280200450d00200128020441002802c0a3c68000118080808000000b20012002360208200120093702000b2000200336020020002004360204200541306a2480808080000b950201027f23808080800041106b220224808080800020022000410c6a36020420012802144190c7c38000410d200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a419dc7c38000410520004188b5c38000108c8180800041fcc5c380004105200241046a41a4c7c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010bdd0401027f23808080800041106b22022480808080000240024002400240024002400240024002400240024020002f01000e09000102030405060708000b200128021441b4c7c380004109200141186a28020028020c1182808080000021010c090b200128021441bdc7c380004109200141186a28020028020c1182808080000021010c080b2002200041026a3602002002200128021441c6c7c380004118200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241e0c7c38000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c080b41012101200041ff01710d072002280208210020034101470d0620022d000d41ff0171450d0620002d001c4104710d06410121012000280214418ca5c080004101200041186a28020028020c11828080800000450d060c070b200128021441f0c7c38000410f200141186a28020028020c1182808080000021010c060b200128021441ffc7c38000410d200141186a28020028020c1182808080000021010c050b2001280214418cc8c38000410d200141186a28020028020c1182808080000021010c040b20012802144199c8c38000410b200141186a28020028020c1182808080000021010c030b200128021441a4c8c380004110200141186a28020028020c1182808080000021010c020b200128021441b4c8c380004112200141186a28020028020c1182808080000021010c010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010b950201027f23808080800041106b22022480808080002002200041086a360204200128021441e0c8c380004106200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a41e6c8c380004108200041f0c8c38000108c818080004180c9c38000410a200241046a41e4c3c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b3400200128021441d1cac3800041c9cac3800020002d000022001b4107410820001b200141186a28020028020c118280808000000bc40501057f23808080800041c0006b220224808080800041002d00fca3c680001a41002802c8a3c68000210302400240024002400240024041002f0194a1c68000220441ffff00712205413f4b0d004101210641012003118180808000002203450d02200320043a00000c010b4102210641022003118180808000002203450d02200320044106742005410876723a00012003200441fc017141027641c000723a00000b200220063602182002200336021420022006360210200241106a2006412010ab8680800020022802142206200228021822046a22032000290000370000200341086a200041086a290000370000200341106a200041106a290000370000200341186a200041186a2900003700002002200441206a22003602182002411c6a2006200010e8838080002002280224220341014d0d022002280220210302402002280210220420006b41014b0d00200241106a2000410210ab868080002002280214210620022802102104200228021821000b200620006a20032f00003b00002002410036023020024280808080103702282002200241286a360234200220063602382002200041026a220536023c200241086a200241346a200041036a41017620056a200241386a41d4cbc3800010a9898080002002280208210002402004450d00200641002802c0a3c68000118080808000000b20000d0320022802302104200228022c2100200228022821050240200228021c450d00200341002802c0a3c68000118080808000000b4100210602402001200141046a2000200410ae86808000450d0010fd8280800021060b02402005450d00200041002802c0a3c68000118080808000000b200241c0006a24808080800020060f0b4101410110b280808000000b4101410210b280808000000b4102200341c4cbc38000109581808000000b41b4a1c38000412b200241386a41e0a1c3800041f0a1c38000108981808000000b9a0f020a7f027e23808080800041e0006b2202248080808000200241286a200110f48a808000200228022c2103024002400240024002400240024020022802282204418080808078460d0020022802302101200241d4cbc38000360254200220013602502002200336024c410021052002410036023020024280808080103702282002200241286a360248200241d8006a200241c8006a2001200241cc006a41d4cbc38000108587808000024020022802582201418380c400460d002001411076210102402002280228450d00200228022c41002802c0a3c68000118080808000000b410021050c050b200228022c2106024020022802282207418080808078470d00200641107621010c050b41012105200228023022084102490d0320062d0000220141c000490d0102402001411874411875417f4a0d00410421050c040b4102210920062d0001220a413f714108742001410274200a4106767241ff01717221010c020b200041013a0000200020033602040c050b410121090b2008200941226a470d000240200141feff0071412e470d00410721050c010b200241cc006a20062009412072220510e883808000024002400240024002402002280254220a41014d0d002002280250210a0240024002400240200820056b4102470d00200620056a2f0000200a2f0000470d00200620096a22092f0003210520092f0001210820092d0000210b2002413f6a2009411c6a280000360000200241386a200941156a290000370300200241306a2009410d6a290000370300200220092900053703280240200228024c450d00200a41002802c0a3c68000118080808000000b02402007450d00200641002802c0a3c68000118080808000000b2001413a490d08200141c00f4a0d02200141b7034a0d01200141416a0ece010808080808080807080808070707080807070807070707070707070707070707070707070707070707070807070707080707080707070807070707070707070807070707080707070707070707070707070707070707070707070707070707070707070707070707070707070708070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070807070807070707070707070707070708030b0240200228024c450d00200a41002802c0a3c68000118080808000000b410321050c080b0240200141d5084a0d00024020014194064a0d00200141b803460d082001419a05470d070c080b2001419506460d07200141e307470d060c070b0240200141d0756a0e0a07060606060606060607000b200141d608460d06200141ec0b470d050c060b0240200141de364a0d00024020014196114a0d000240200141bf706a0e30080707070707070707070707070707070707070707070807080707070707070707070707080707070707070707070708000b200141d46f6a220641144b0d064101200674418180c10071450d060c070b0240200141a51f4a0d002001419711460d07200141ce11460d07200141851a470d060c070b0240200141f1284a0d00200141a61f460d07200141e222470d060c070b200141f228460d06200141ce2f470d050c060b0240200141cecd004a0d000240200141a1c5004a0d000240200141a1496a0e0708070707070708000b2001418a39460d07200141df39470d060c070b200141deba7f6a220641144d0d030c040b0240200141fade004a0d000240200141ddd9004a0d00200141cfcd00460d07200141b9ce00470d060c070b200141ded900460d06200141acdc00470d050c060b0240200141bbe6004a0d00200141fbde00460d062001419fdf00470d050c060b200141bce600460d05200141e9f200460d050c040b200141a403470d030c040b4102200a4190cdc38000109581808000000b4101200674418180c800710d020b200141f0c600460d01200141cfcc00460d010b02400240200141c4094a0d00200141a87f6a0e320202020102020101010102020202010101010101010101010101010101010101010101010101010102020101010101010202010b200141bb766a4102490d01200141fc756a4102490d01200141e26e6a4102490d010b200141bca77f6a417d4b0d00200141002f0194a1c68000460d00410221050c020b200241086a41176a2201200241286a41176a280000360000200241086a41106a2206200241286a41106a290300370300200241086a41086a200241286a41086a290300220c37030020022002290328220d370308200041046a20053b0100200041026a20083b01002000200b3a0001200041066a200d3701002000410e6a200c370100200041166a20062903003701002000411d6a2001280000360000410021010c020b024020070d000c010b200641002802c0a3c68000118080808000000b200220013b014a200220053b0148200241346a4201370200410121012002410136022c200241d8cac38000360228200241cd8280800036025c2002200241d8006a3602302002200241c8006a360258200241cc006a200241286a10b8808080002000200241cc006a10ea8a8080003602040b200020013a00002004450d00200341002802c0a3c68000118080808000000b200241e0006a2480808080000bc30201027f23808080800041106b22022480808080000240024020002d00000d00200128021441a0cdc38000410c200141186a28020028020c1182808080000021030c010b410121032002200041016a3602002002200128021441accdc380004106200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241b4cdc38000108d81808000210120022d000c21000240200128020022010d00200041ff017141004721030c010b200041ff01710d0020022802082100024020014101470d0020022d000d41ff0171450d0020002d001c4104710d00410121032000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021030b200241106a24808080800020030bba0203027f017e017f23808080800041d0006b2201248080808000200141286a41b9d0c38000411241fe8bc48000411641e8d1c38000410010d882808000200141086a2202410036020020014280808080c000370300200129022c210320012802282104200142808080808001370218200142888080808001370210200141346a200141106a10f98680800002402004418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a4100360200200141cc006a200141346a41086a2802003600002000413c6a200337020020002004360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437004420002001290041370001200041086a200141c8006a290000370000200141d0006a2480808080000b2000200042bae6c989f69dacb01a3703082000428e8397fa82e98fd06e3703000bca0204067f017e027f017e200141186a2802002102200141106a2802002103200128020821042001280204210520012802002106024003402001418180808078360200024002400240024020064180808080786a0e020200010b20032002460d0120012003410c6a220736021020032802002206418080808078460d0120032902042208422088a721042008a72105200721030b2005210720062109024020032002460d0020012003410c6a220a36021020032802002206418080808078470d020b2001200b37020420014180808080783602000c030b20004180808080783602000f0b20012003290204220b370204200120063602002004200b422088a7470d012007200ba72205200410888e8080000d01200a21032009450d00200741002802c0a3c6800011808080800000200a21030c000b0b2000200436020820002007360204200020093602000b1400200041ecd4c38000360204200020013602000b1400200041ecd4c38000360204200020013602000bef0201027f23808080800041c0006b2203248080808000200320023a000c200320013602082003410036021820034280808080103702102003411c6a200341086a10ec8a8080000240024020032d001c0d002003411c6a41027221020240034020032d001d410171450d010240200328021822042003280210470d00200341106a200410a186808000200328021821040b200328021420044105746a22012002290000370000200141086a200241086a290000370000200141106a200241106a290000370000200141186a200241186a2900003700002003200441016a3602182003411c6a200341086a10ec8a80800020032d001c450d000b2000200328022036020420004180808080783602002003280210450d02200328021441002802c0a3c68000118080808000000c020b20002003290210370200200041086a200341106a41086a2802003602000c010b2000200328022036020420004180808080783602000b200341c0006a2480808080000bdd0702047f017e23808080800041306b2203248080808000024002400240024002402000280200220441736a2205410220054104491b0e0400010203000b41002105024002400240024002400240024002400240024020002d0008417f6a0e0b0001020304050607090908090b200041146a28020041046a21050c080b410821050c070b200041146a28020041046a21050c060b200041146a28020041046a21050c050b200041146a28020041186c41047221050c040b200041146a280200410c6c41046a21050c030b417f200041186a280200220541086a22042004200541046a491b21050c020b200041146a28020041046a21050c010b200041146a28020041046a21050b200541016a21050c030b200041086a10bc8780800021050c020b410021050240024002400240024002400240024002402004417f6a0e0c000102030405080608070707000b41d00021050c070b2000410c6a28020041046a21050c060b417f200041186a28020041046a220541012000410c6a28020041056a2000280204418080808078461b6a220420042005491b21050c050b417f200041186a2802002000410c6a28020041046a22056a41046a220420042005491b21050c040b2000410c6a28020041046a21050c030b2000410c6a28020041046a21050c020b410821050c010b410421050b200541016a21050c010b200041086a10a78c80800021050b41012104200541016a21064102210502402000290358220742c000540d0041032105200742808001540d00410521052007428080808004540d00410a200779a74103766b21050b024002400240200620056a4121410120002d0088011b6a2205450d002005417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002204450d020b20004188016a210620034100360214200320043602102003200536020c20002003410c6a10b8898080002003200041d8006a36022c2003412c6a2003410c6a10c18a808000200041e0006a2d000021050240200328020c20032802142200470d002003410c6a2000410110b182808000200328021421000b200328021020006a20053a00002003200041016a36021420062003410c6a10b18c80800020032802102100200328020c210502400240200328021422044180024b0d00200120002004200241002802c8a2c680001189808080000021040c010b2003410c6a200020044100280298a3c680001185808080000020012003410c6a4120200241002802c8a2c680001189808080000021040b02402005450d00200041002802c0a3c68000118080808000000b200341306a24808080800020040f0b10ae80808000000b4101200510b280808000000bcd0201017f0240024002400240200028020041736a2202410220024104491b0e0400010203000b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41003a0000200041086a200110a78a8080000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41013a0000200041086a200110bb878080000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41023a00002000200110ba888080000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41033a0000200041086a200110a68c8080000bca0101057f23808080800041206b220124808080800041002102200141003602102001428080808010370208200141146a10b98980800041012103200128021821040240200128021c2205450d00200141086a4100200510b182808000200128020c2103200128021021020b200320026a2004200510848e8080001a2001200220056a36021002402001280214450d00200441002802c0a3c68000118080808000000b20002001290208370200200041086a200141086a41086a280200360200200141206a2480808080000b840201037f23808080800041106b22022480808080000240024020012802082203410c6c20034103746a41066a2203417f4c0d0041002d00fca3c680001a200341002802c8a3c68000118180808000002204450d012002200436020820022003360204200420012d000c3a00002002410136020c20012d000d210441012103024020022802044101470d00200241046a4101410110b182808000200228020c21030b200228020820036a20043a0000200241046a41086a2204200341016a3602002001200241046a10d48c808000200041086a200428020036020020002002290204370200200241106a2480808080000f0b10ae80808000000b4101200310b280808000000b2100200128021441f4dec180004108200141186a28020028020c118280808000000b210020012802144194d5c380004111200141186a28020028020c118280808000000b2100200128021441a5d5c38000410a200141186a28020028020c118280808000000b2100200128021441b2e2c180004103200141186a28020028020c118280808000000b2100200128021441d0a7c48000411b200141186a28020028020c118280808000000b2100200128021441f0dec180004104200141186a28020028020c118280808000000b2100200128021441d895c48000410f200141186a28020028020c118280808000000b12002001418a84c48000410210dd808080000b040041010b040041010b02000bb90101037f23808080800041306b220124808080800002400240200028020c22020d0041002102410021030c010b200120023602242001410036022020012002360214200141003602102001200041106a280200220236022820012002360218200041146a2802002103410121020b2001200336022c2001200236021c2001200236020c2001410c6a10928d80800002402000280200450d00200028020441002802c0a3c68000118080808000000b200141306a2480808080000b02000b2d002000410c6a10a58d80800002402000280200450d00200028020441002802c0a3c68000118080808000000b0bef0901057f0240024002400240024002402000280200220141736a2202410220024104491b0e03010203000b20002d00084106470d042000410c6a280200450d04200041106a28020021030c030b0240024002400240024002400240024020002d0008417f6a0e0a010b0203040506070b0b000b2000410c6a280200450d0a200041106a28020021030c090b2000410c6a280200450d09200041106a28020021030c080b2000410c6a280200450d08200041106a28020021030c070b2000410c6a280200450d07200041106a28020021030c060b200041106a28020021030240200041146a2802002201450d0020032102034002402002280200450d00200241046a28020041002802c0a3c68000118080808000000b02402002410c6a280200450d00200241106a28020041002802c0a3c68000118080808000000b200241186a21022001417f6a22010d000b0b200028020c450d060c050b200041106a28020021030240200041146a2802002202450d002002410171210441002101024020024101460d002002417e7121052003210241002101034002402002280200450d00200241046a28020041002802c0a3c68000118080808000000b02402002410c6a280200450d00200241106a28020041002802c0a3c68000118080808000000b200241186a21022005200141026a2201470d000b0b2004450d0020032001410c6c6a2202280200450d00200228020441002802c0a3c68000118080808000000b200028020c0d040c050b200041106a280200450d04200041146a28020021030c030b2000410c6a280200450d03200041106a28020021030c020b200041186a2d0000417d6a41ff017141014b0d020240200028020822034198016a2802002205450d0020034194016a280200220421004100210203402004200241146c6a21010240024002400240024020002d00000e0400010102040b200041086a21010c020b200141086a21010c010b200141046a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200241016a2102200041146a21002005417f6a22050d000b0b0240200328029001450d0020032802940141002802c0a3c68000118080808000000b024020034190026a2802002205450d002003418c026a280200220421004100210203402004200241146c6a21010240024002400240024020002d00000e0400010102040b200041086a21010c020b200141086a21010c010b200141046a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200241016a2102200041146a21002005417f6a22050d000b0b200328028802450d01200328028c0241002802c0a3c68000118080808000000c010b024002400240024002400240024002402001417e6a0e06000102030405090b200041046a21000c050b02402000280210450d00200041146a28020041002802c0a3c68000118080808000000b20002802042202418080808078460d07200041046a21000c050b02402000280204450d00200041086a28020041002802c0a3c68000118080808000000b200041106a21000c030b200041046a21000c020b200041046a21000c010b024002400240024020002d00040e0400010203070b2000410c6a21000c030b2000410c6a21000c020b2000410c6a21000c010b200041086a21000b200028020021020b2002450d01200028020421030b200341002802c0a3c68000118080808000000f0b0bc50101027f024002400240024020002802002201418080808078732202410220024104491b0e03030301000b0240024002402000280204220028020041fcffffff076a2202410320024105491b0e0404040102000b2000280204450d03200041086a28020041002802c0a3c68000118080808000000c030b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b200010ca898080000c010b2001450d01200028020421000b200041002802c0a3c68000118080808000000b0b29000240200041808080807872418080808078460d00200141002802c0a3c68000118080808000000b0b910401027f23808080800041106b22022480808080000240024002402000280210418080808078460d00200220003602002002200128021441d089c480004102200128021828020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241d489c48000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c030b41012101200041ff01710d022002280208210020034101470d0120022d000d41ff0171450d0120002d001c4104710d01410121012000280214418ca5c080004101200041186a28020028020c11828080800000450d010c020b200220003602002002200128021441e489c480004103200128021828020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241e889c48000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c020b41012101200041ff01710d0120022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d020b2000280214418da5c080004101200041186a28020028020c1182808080000021010c010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010b1900200120002802002200280200200028020410dd808080000bf60b01097f23808080800041e0016b2208248080808000200841356a2002290000370000200841dc006a41ecd4c38000360200200841086a41286a4100360200200841086a41206a4204370200200841086a41186a4200370200200841f4006a2007360200200841ec006a4100360200200841cd006a200241186a290000370000200841c5006a200241106a2900003700002008413d6a200241086a290000370000200820023602602008200136025820084280808080c0003702182008200636027020084100360264200841013a0034200820053602142008410036020c2008200436021020082004410047360208200841b0016a41286a200341286a280200360200200841b0016a41206a200341206a290200370300200841b0016a41186a200341186a290200370300200841b0016a41106a200341106a290200370300200841b0016a41086a2204200341086a290200370300200820032902003703b001200841f8006a200841b0016a109d87808000200828027c210920082802800121032008200841df016a3602b00120092003200841b0016a10d68b808000200841086a41106a210a2008280278210b024002402003450d00200920034104746a210c200841a8016a210d200841b0016a410172210620084184016a41086a210720084184016a41046a210e20084184016a410172210520092103034020032802002201450d012003280204210f02400240024020032802082210450d00200841b0016a200841086a2001200f2010200328020c10918680800020082d00b00122014104460d02200520062f00003b000020072004290200370200200541026a200641026a2d00003a0000200741086a200441086a290200370200200741106a200441106a290200370200200741186a200441186a290200370200200741206a200441206a2802003602000c010b200841b0016a200841086a2001200f10938680800020082d00b00122014104460d01200520062f00003b000020072004290200370200200541026a200641026a2d00003a0000200741086a200441086a290200370200200741106a200441106a290200370200200741186a200441186a290200370200200741206a200441206a2802003602000b200820082802b401220f36028801200820013a008401024020014103460d0002400240024020010e020103000b20082802a80122012001280200220f417f6a360200200d2101200f4101460d010c020b200f200f2802002210417f6a360200200e210120104101470d010b200110e28a8080000b200341106a2203200c470d010c020b0b20082802b4012103200041013a0000200020033602040240200b450d00200941002802c0a3c68000118080808000000b200841086a109086808000200a10908780800002402008280218450d00200828021c41002802c0a3c68000118080808000000b02402008280224450d00200828022841002802c0a3c68000118080808000000b02400240200828026422030d0041002103410021040c010b200820033602c801200841003602c401200820033602b801200841003602b4012008200841e8006a28020022033602cc01200820033602bc0141012103200828026c21040b200820043602d001200820033602c001200820033602b001200841b0016a10a48d8080000c010b0240200b450d00200941002802c0a3c68000118080808000000b200841086a109086808000200a10908780800002402008280218450d00200828021c41002802c0a3c68000118080808000000b02402008280224450d00200828022841002802c0a3c68000118080808000000b4100210341002104024020082802642207450d00200820073602c801200841003602c401200820073602b801200841003602b4012008200841e8006a28020022033602cc01200820033602bc0141012103200828026c21040b200820043602d001200820033602c001200820033602b001200841b0016a10a48d808000200041003a0000200041196a200241186a290000370000200041116a200241106a290000370000200041096a200241086a290000370000200020022900003700010b200841e0016a2480808080000bf60b01097f23808080800041e0016b2208248080808000200841356a2002290000370000200841dc006a41ecd4c38000360200200841086a41286a4100360200200841086a41206a4204370200200841086a41186a4200370200200841f4006a2007360200200841ec006a4100360200200841cd006a200241186a290000370000200841c5006a200241106a2900003700002008413d6a200241086a290000370000200820023602602008200136025820084280808080c0003702182008200636027020084100360264200841013a0034200820053602142008410036020c2008200436021020082004410047360208200841b0016a41286a200341286a280200360200200841b0016a41206a200341206a290200370300200841b0016a41186a200341186a290200370300200841b0016a41106a200341106a290200370300200841b0016a41086a2204200341086a290200370300200820032902003703b001200841f8006a200841b0016a109d87808000200828027c210920082802800121032008200841df016a3602b00120092003200841b0016a10d68b808000200841086a41106a210a2008280278210b024002402003450d00200920034104746a210c200841a8016a210d200841b0016a410172210620084184016a41086a210720084184016a41046a210e20084184016a410172210520092103034020032802002201450d012003280204210f02400240024020032802082210450d00200841b0016a200841086a2001200f2010200328020c10928680800020082d00b00122014104460d02200520062f00003b000020072004290200370200200541026a200641026a2d00003a0000200741086a200441086a290200370200200741106a200441106a290200370200200741186a200441186a290200370200200741206a200441206a2802003602000c010b200841b0016a200841086a2001200f10948680800020082d00b00122014104460d01200520062f00003b000020072004290200370200200541026a200641026a2d00003a0000200741086a200441086a290200370200200741106a200441106a290200370200200741186a200441186a290200370200200741206a200441206a2802003602000b200820082802b401220f36028801200820013a008401024020014103460d0002400240024020010e020103000b20082802a80122012001280200220f417f6a360200200d2101200f4101460d010c020b200f200f2802002210417f6a360200200e210120104101470d010b200110e28a8080000b200341106a2203200c470d010c020b0b20082802b4012103200041013a0000200020033602040240200b450d00200941002802c0a3c68000118080808000000b200841086a108f86808000200a10908780800002402008280218450d00200828021c41002802c0a3c68000118080808000000b02402008280224450d00200828022841002802c0a3c68000118080808000000b02400240200828026422030d0041002103410021040c010b200820033602c801200841003602c401200820033602b801200841003602b4012008200841e8006a28020022033602cc01200820033602bc0141012103200828026c21040b200820043602d001200820033602c001200820033602b001200841b0016a10a48d8080000c010b0240200b450d00200941002802c0a3c68000118080808000000b200841086a108f86808000200a10908780800002402008280218450d00200828021c41002802c0a3c68000118080808000000b02402008280224450d00200828022841002802c0a3c68000118080808000000b4100210341002104024020082802642207450d00200820073602c801200841003602c401200820073602b801200841003602b4012008200841e8006a28020022033602cc01200820033602bc0141012103200828026c21040b200820043602d001200820033602c001200820033602b001200841b0016a10a48d808000200041003a0000200041196a200241186a290000370000200041116a200241106a290000370000200041096a200241086a290000370000200020022900003700010b200841e0016a2480808080000bcc0c01067f2380808080004190026b220a248080808000200a41186a41186a200441186a220b290000370300200a41186a41106a200441106a220c290000370300200a41186a41086a200441086a220d290000370300200a2004290000370318200a410c6a41086a2002360200200a2001360210200a200336020c200a418c016a41f889c48000360200200a41e0006a4100360200200a41386a41206a4204370200200a41386a41186a4200370200200a41a4016a2009360200200a419c016a4100360200200a41fd006a200b290000370000200a41f5006a200c290000370000200a41ed006a200d290000370000200a41e5006a2004290000370000200a4280808080c000370248200a20083602a001200a410036029401200a41013a0064200a200a41186a36029001200a200a410c6a36028801200a2007360244200a410036023c200a2006360240200a2006410047360238200a41e0016a41206a200541206a280200360200200a41e0016a41186a200541186a290200370300200a41e0016a41106a200541106a290200370300200a41e0016a41086a2206200541086a290200370300200a20052902003703e001200a41a8016a200a41e0016a109f87808000200a2802ac01210b200a2802b0012104200a200a418f026a3602e001200b2004200a41e0016a10d68b808000200a41386a41106a210c200a2802a801210d024002402004450d00200b20044104746a2107200a41d8016a210e200a41e0016a4101722101200a41b4016a41086a2105200a41b4016a41046a210f200a41b4016a4101722103200b2104034020042802002202450d0120042802042109024002400240200428020822080d00200a41e0016a200a41386a20022009109486808000200a2d00e00122024104460d01200320012f00003b000020052006290200370200200341026a200141026a2d00003a0000200541086a200641086a290200370200200541106a200641106a290200370200200541186a200641186a290200370200200541206a200641206a2802003602000c020b200a41e0016a200a41386a200220092008200428020c109286808000200a2d00e00122024104460d00200320012f00003b000020052006290200370200200341026a200141026a2d00003a0000200541086a200641086a290200370200200541106a200641106a290200370200200541186a200641186a290200370200200541206a200641206a2802003602000c010b200a2802e4012104200041013a0000200020043602040240200d450d00200b41002802c0a3c68000118080808000000b200a41386a108f86808000200c1090878080000240200a280248450d00200a28024c41002802c0a3c68000118080808000000b0240200a280254450d00200a28025841002802c0a3c68000118080808000000b02400240200a2802940122040d0041002104410021060c010b200a20043602f801200a41003602f401200a20043602e801200a41003602e401200a200a4198016a28020022043602fc01200a20043602ec0141012104200a28029c0121060b200a200636028002200a20043602f001200a20043602e001200a41e0016a10a48d8080000c030b200a200a2802e40122093602b801200a20023a00b401024020024103460d0002400240024020020e020103000b200a2802d801220220022802002209417f6a360200200e210220094101460d010c020b200920092802002208417f6a360200200f210220084101470d010b200210e28a8080000b200441106a22042007470d000b0b0240200d450d00200b41002802c0a3c68000118080808000000b200a41386a108f86808000200c1090878080000240200a280248450d00200a28024c41002802c0a3c68000118080808000000b0240200a280254450d00200a28025841002802c0a3c68000118080808000000b41002104410021060240200a280294012205450d00200a20053602f801200a41003602f401200a20053602e801200a41003602e401200a200a4198016a28020022043602fc01200a20043602ec0141012104200a28029c0121060b200a200636028002200a20043602f001200a20043602e001200a41e0016a10a48d808000200041196a200a41306a290300370000200041116a200a41286a290300370000200041096a200a41206a2903003700002000200a290318370001200041003a00000b200a4190026a2480808080000bcc0c01067f2380808080004190026b220a248080808000200a41186a41186a200441186a220b290000370300200a41186a41106a200441106a220c290000370300200a41186a41086a200441086a220d290000370300200a2004290000370318200a410c6a41086a2002360200200a2001360210200a200336020c200a418c016a41f889c48000360200200a41e0006a4100360200200a41386a41206a4204370200200a41386a41186a4200370200200a41a4016a2009360200200a419c016a4100360200200a41fd006a200b290000370000200a41f5006a200c290000370000200a41ed006a200d290000370000200a41e5006a2004290000370000200a4280808080c000370248200a20083602a001200a410036029401200a41013a0064200a200a41186a36029001200a200a410c6a36028801200a2007360244200a410036023c200a2006360240200a2006410047360238200a41e0016a41206a200541206a280200360200200a41e0016a41186a200541186a290200370300200a41e0016a41106a200541106a290200370300200a41e0016a41086a2206200541086a290200370300200a20052902003703e001200a41a8016a200a41e0016a109f87808000200a2802ac01210b200a2802b0012104200a200a418f026a3602e001200b2004200a41e0016a10d68b808000200a41386a41106a210c200a2802a801210d024002402004450d00200b20044104746a2107200a41d8016a210e200a41e0016a4101722101200a41b4016a41086a2105200a41b4016a41046a210f200a41b4016a4101722103200b2104034020042802002202450d0120042802042109024002400240200428020822080d00200a41e0016a200a41386a20022009109386808000200a2d00e00122024104460d01200320012f00003b000020052006290200370200200341026a200141026a2d00003a0000200541086a200641086a290200370200200541106a200641106a290200370200200541186a200641186a290200370200200541206a200641206a2802003602000c020b200a41e0016a200a41386a200220092008200428020c109186808000200a2d00e00122024104460d00200320012f00003b000020052006290200370200200341026a200141026a2d00003a0000200541086a200641086a290200370200200541106a200641106a290200370200200541186a200641186a290200370200200541206a200641206a2802003602000c010b200a2802e4012104200041013a0000200020043602040240200d450d00200b41002802c0a3c68000118080808000000b200a41386a109086808000200c1090878080000240200a280248450d00200a28024c41002802c0a3c68000118080808000000b0240200a280254450d00200a28025841002802c0a3c68000118080808000000b02400240200a2802940122040d0041002104410021060c010b200a20043602f801200a41003602f401200a20043602e801200a41003602e401200a200a4198016a28020022043602fc01200a20043602ec0141012104200a28029c0121060b200a200636028002200a20043602f001200a20043602e001200a41e0016a10a48d8080000c030b200a200a2802e40122093602b801200a20023a00b401024020024103460d0002400240024020020e020103000b200a2802d801220220022802002209417f6a360200200e210220094101460d010c020b200920092802002208417f6a360200200f210220084101470d010b200210e28a8080000b200441106a22042007470d000b0b0240200d450d00200b41002802c0a3c68000118080808000000b200a41386a109086808000200c1090878080000240200a280248450d00200a28024c41002802c0a3c68000118080808000000b0240200a280254450d00200a28025841002802c0a3c68000118080808000000b41002104410021060240200a280294012205450d00200a20053602f801200a41003602f401200a20053602e801200a41003602e401200a200a4198016a28020022043602fc01200a20043602ec0141012104200a28029c0121060b200a200636028002200a20043602f001200a20043602e001200a41e0016a10a48d808000200041196a200a41306a290300370000200041116a200a41286a290300370000200041096a200a41206a2903003700002000200a290318370001200041003a00000b200a4190026a2480808080000b9b0301077f23808080800041306b220424808080800020032802002105200128020421060240024002400240024020032802042207200141086a28020022086a22090d004101210a0c010b2009417f4c0d01200941002802c8a3c6800011818080800000220a450d02200a41002009108a8e8080001a0b20092008490d02200a2006200810848e808000220a20086a2005200710848e8080001a20012802002101200420032f01083b0114200420093602102004200a36020c200441186a200128020420022004410c6a10e48c8080000240024002402004280218418080808078470d00200441246a2001280200200210e58c8080002004280224418180808078460d0120002004290224370200200041086a200441246a41086a2802003602000c020b20002004290218370200200041086a200441186a41086a2802003602000c010b20004180808080783602000b02402009450d00200a41002802c0a3c68000118080808000000b200441306a2480808080000f0b10ae80808000000b4101200910b280808000000b2008200941c8c5c28000109581808000000b9a0201077f23808080800041106b220524808080800020022802002106200128020421070240024002400240024020022802042208200141086a28020022096a220a0d004101210b0c010b200a417f4c0d01200a41002802c8a3c6800011818080800000220b450d02200b4100200a108a8e8080001a0b200a2009490d02200b2007200910848e808000220b20096a2006200810848e8080001a20012802002101200520022f01083b010c2005200a3602082005200b36020420002001280204200541046a2003200410e78c8080000240200a450d00200b41002802c0a3c68000118080808000000b200541106a2480808080000f0b10ae80808000000b4101200a10b280808000000b2009200a41c8c5c28000109581808000000b960201077f23808080800041106b220324808080800020022802002104200028020421050240024002400240024020022802042206200041086a28020022076a22080d00410121090c010b2008417f4c0d01200841002802c8a3c68000118180808000002209450d02200941002008108a8e8080001a0b20082007490d0220092005200710848e808000220920076a2004200610848e8080001a20002802002100200320022f01083b010c200320083602082003200936020420002802042001200341046a10e98c80800002402008450d00200941002802c0a3c68000118080808000000b200341106a2480808080000f0b10ae80808000000b4101200810b280808000000b2007200841c8c5c28000109581808000000b980201077f23808080800041106b220424808080800020022802002105200028020421060240024002400240024020022802042207200041086a28020022086a22090d004101210a0c010b2009417f4c0d01200941002802c8a3c6800011818080800000220a450d02200a41002009108a8e8080001a0b20092008490d02200a2006200810848e808000220a20086a2005200710848e8080001a20002802002100200420022f01083b010c200420093602082004200a36020420002802042001200441046a200310eb8c80800002402009450d00200a41002802c0a3c68000118080808000000b200441106a2480808080000f0b10ae80808000000b4101200910b280808000000b2008200941c8c5c28000109581808000000bb20301077f23808080800041306b220324808080800020022802002104200028020421050240024002400240024020022802042206200041086a28020022076a22080d00410121090c010b2008417f4c0d01200841002802c8a3c68000118180808000002209450d02200941002008108a8e8080001a0b20082007490d0220092005200710848e808000220920076a2004200610848e8080001a20002802002107200320022f01083b0114200320083602102003200936020c200341186a200728020420012003410c6a10e48c808000024002400240024020032802182200418080808078470d00200341246a2007280200200110e58c80800020032802242200418180808078470d0141808080807821020c030b200328021c21020c010b41808080807821022000418080808078460d01200328022821020b024020000d00410021020c010b200241002802c0a3c6800011808080800000200021020b02402008450d00200941002802c0a3c68000118080808000000b200341306a2480808080002002418080808078470f0b10ae80808000000b4101200810b280808000000b2007200841c8c5c28000109581808000000b1400200041b48ac48000360204200020013602000b1400200041b48ac48000360204200020013602000b1400200041f889c48000360204200020013602000b1400200041f889c48000360204200020013602000b1a0020022001200041dc8ac480002003280228118680808000000b070020002802000bb106010a7f23808080800041d0006b220424808080800002404100280284a4c680004105470d004100280290a1c680002105410028028ca1c6800021064100280280a4c680002107200441c0006a42003702002004413c6a41e8d1c38000360200200441386a4101360200200441306a41163602002004412c6a41e88bc48000360200200441206a41f48ac48000ad4280808080900c84370200200441146a41fe8bc48000ad4280808080e00284370200200441e08bc48000360234200441053602282004410036021c20044100360210200442818080809024370208200641ecf2c08000200741024622071b200441086a200541d4f2c0800020071b280210118480808000000b4102210702400240200128020041736a220541034b0d0020054102460d000c010b200441086a200110d18c808000024020042802182208418080808078460d00200428022c21092004280228210a2004280224210b200428021c210c024020042802202201450d002001410171210d41002105024020014101460d002001417e712106200c210141002105034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b02402001410c6a280200450d00200141106a28020041002802c0a3c68000118080808000000b200141186a21012006200541026a2205470d000b0b200d450d00200c2005410c6c6a2201280200450d00200128020441002802c0a3c68000118080808000000b02402008450d00200c41002802c0a3c68000118080808000000b02402009450d002009410171210841002105024020094101460d002009417e712106200a210141002105034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b02402001410c6a280200450d00200141106a28020041002802c0a3c68000118080808000000b200141186a21012006200541026a2205470d000b0b2008450d00200a2005410c6c6a2201280200450d00200128020441002802c0a3c68000118080808000000b0240200b0d000c020b200a41002802c0a3c68000118080808000000c010b20042f0009210120042d000821070b200441d0006a2480808080002001410874200741ff0171720b0700200042017c0b8c0302057f017e024020012802002202200128020822036b411f4b0d0020012003412010b18280800020012802002102200128020821030b2001200341206a22043602082001280204220520036a22032000290000370000200341086a200041086a290000370000200341106a200041106a290000370000200341186a200041186a290000370000200041206a21030240200220046b411f4b0d0020012004412010b1828080002001280200210220012802042105200128020821040b200520046a220620032900003700002001200441206a2204360208200641186a200341186a290000370000200641106a200341106a290000370000200641086a200341086a290000370000200029034021070240200220046b41074b0d0020012004410810b18280800020012802042105200128020821040b200520046a20073700002001200441086a2203360208200029034821070240200128020020036b41074b0d0020012003410810b182808000200128020821030b2001200341086a360208200128020420036a20073700000bcc0704027f017e037f017e23808080800041d0006b2201248080808000200141286a41e795c48000410c41fe8bc48000411641e8d1c38000410010d882808000200141086a410036020020014280808080c00037030020012802282102200129022c2103200141106a41086a22044100360200200142808080808001370210200141106a410010a4868080002001280214200428020041386c6a2205420437022c20054209370224200541edfbc480003602202005410436021c200541daa3c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200141c0006a41086a2206200428020041016a2205360200200120012902102207370340024020052007a7470d00200141c0006a200510a486808000200128024821050b2001280244200541386c6a2205420437022c20054209370224200541edfbc480003602202005410236021c200541d8a3c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db003703002004200628020041016a2205360200200120012903402207370310024020052007a7470d00200141106a200510a486808000200128021821050b2001280214200541386c6a2205420437022c20054207370224200541c7fbc480003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c370300200141c0006a41086a200141106a41086a28020041016a2205360200200120012903102207370340024020052007a7470d00200141c0006a200510a486808000200128024821050b2001280244200541386c6a2205420437022c2005420537022420054185e4c480003602202005410536021c20054180e4c48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c370300200128024821042001280244210520012001280240360218200120053602102001200536021420012005200441386c6a41386a36021c200141346a200141106a10f98680800002402002418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a4100360200200141cb006a200141346a41086a2802003600002000413c6a200337020020002002360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437004320002001290040370001200041086a200141c7006a290000370000200141d0006a2480808080000ba60501057f23808080800041e0056b220124808080800020014180036a41086a22024200370300200141086a41106a20014190036a41086a290300370300200141e0026a41086a22034200370300200141d0026a41086a22044200370300200141b8026a41086a22054200370300200141b8026a41106a41003602002001420037030820014200370380032001200129039003370310200142003703e002200142003703d002200142003703b80220014201370338200141c8006a200229030037030020012001290380033703402001420037036020014200370358200142d0a38733370350200141f0006a200141b8036a41086a290300370300200120012903b803370368200142003703b801200142003703b001200142d0a387333703a801200141c8016a200141f0026a41086a290300370300200120012903f0023703c001200142003703d001200141e0016a2003290300370300200120012903e0023703d801200142003703e801200141f8016a2004290300370300200120012903d0023703f0012001420037039802200142d8a698d801370390022001420037038802200142d0a3873337038002200141a8026a2005290300370300200120012903b8023703a0022001428180808080a0f8fa053703b0022001428080808080808080c0003703a0012001428090cad2c60e3703980120014201370390012001427f3703880120014280c0a8ca9a3a3703800120014201370378200142ffffffffffffffffbf7f37033020014280b0def7d32b37032820014201370320200141b8036a200141086a108682808000024020012903b8034202520d00200120012d00c0033a00900341aeeac0800041e00020014190036a41c0e9c080004190ebc08000108981808000000b2000200141b8036a41a80210848e8080001a200141e0056a2480808080000bf40701087f23808080800041b0016b2202248080808000024002400240024002400240024020012802002203418080808078460d00200128020421044101210541012106024020012802082207450d002007417f4c0d0441002d00fca3c680001a200741002802c8a3c68000118180808000002206450d050b20062004200710848e8080002106200220073602082002200636020420022007360200200241e4006a41e5c8c9a90320024100280290a2c68000118580808000002007450d0141002d00fca3c680001a200741002802c8a3c680001181808080000022050d014101200710b280808000000b2002418080808078360200200241e4006a41e5c8c9a90320024100280290a2c6800011858080800000200241808080807836020020024184016a41f3e4c9a903200241002802b0a2c680001185808080000020024180808080783602a4010c010b20052004200710848e808000210620022007360208200220063602042002200736020020024184016a41f3e4c9a903200241002802b0a2c68000118580808000000240024020070d00410121060c010b41002d00fca3c680001a200741002802c8a3c68000118180808000002206450d040b20062004200710848e8080002104200220073602ac01200220043602a801200220073602a4010b200241c0006a220441e5c6919b07200241a4016a41002802f0a1c6800011858080800000200241086a2205200241e4006a41086a290000370300200241106a2208200241e4006a41106a290000370300200241186a2209200241e4006a41186a290000370300200241286a20024184016a41086a290000370300200241306a20024184016a41106a290000370300200241386a20024184016a41186a29000037030020022002290064370300200220022900840137032041002d00fca3c680001a41e10041002802c8a3c68000118180808000002207450d03200720022903003700002007200241206a220629000037002020072004290000370040200741186a2009290300370000200741106a2008290300370000200741086a2005290300370000200741286a200641086a290000370000200741306a200641106a290000370000200741386a200641186a290000370000200741c8006a200441086a290000370000200741d0006a200441106a290000370000200741d8006a200441186a290000370000200741e0006a200441206a2d00003a0000200041e10036020820002007360204200041e10036020002402003418080808078460d002003450d00200128020441002802c0a3c68000118080808000000b200241b0016a2480808080000f0b10ae80808000000b4101200710b280808000000b4101200710b280808000000b410141e10010b280808000000b920501037f23808080800041106b22022480808080002002410036020c20024280808080c00037020441002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002203450d0020032001290000370000200341186a200141186a290000370000200341106a200141106a290000370000200341086a200141086a290000370000200241046a410010a3868080002002280208200228020c4104746a220442a0808080d08c999935370208200420033602042004412036020041002d00fca3c680001a2002200228020c41016a36020c412041002802c8a3c68000118180808000002203450d0120032001290020370000200341186a200141386a290000370000200341106a200141306a290000370000200341086a200141286a2900003700000240200228020c22042002280204470d00200241046a200410a386808000200228020c21040b200228020820044104746a220442a0808080b0ce9c993537020820042003360204200441203602002002200228020c41016a36020c41002d00fca3c680001a412141002802c8a3c68000118180808000002203450d0220032001290040370000200341206a200141e0006a2d00003a0000200341186a200141d8006a290000370000200341106a200141d0006a290000370000200341086a200141c8006a2900003700000240200228020c22012002280204470d00200241046a200110a386808000200228020c21010b200228020820014104746a220142a1808080d0ec98b2f300370208200120033602042001412136020020002002290204370200200041086a200241046a41086a28020041016a360200200241106a2480808080000f0b4101412010b280808000000b4101412010b280808000000b4101412110b280808000000bbe9701012b7f2380808080004190016b220124808080800041002d00fca3c680001a0240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024041800441002802c8a3c68000118180808000002202450d00200141106a109e8a8080002001109f8a808000200141386a10b587808000200141286a10b687808000200141d0006a10bc88808000200141f8006a10a18c808000200141e8006a10a28c808000200241386a4289a2e6a5b198dead63370300200242d5d1b5deb9b78d97cf00370330200241206a42e89d8d84e9a7e0ebbf7f370300200242ffa1f591d896faeca07f370318200242829cf890def98fed23370308200242daf597b8b3adb2fc44370300200241b8016a42bba2f9a9eaf2f897ff00370300200241b0016a4288bfcff6aea5a9cbf90037030020024188016a42c2a7b4b19ace9c92d9003703002002429691e8a3b686eae562370380012002410636024c200241f9a6c48000360248200241ec82808000360240200241ed82808000360228200241ee8280800036021020024280808080c000370370200241cc016a4104360200200241c8016a41f5a6c48000360200200241c0016a41ef82808000360200200241a8016a410036020020024190016a41f082808000360200200241003a007c2002410036027820022001290310370350200241d8006a200141106a41086a290300370300200241e0006a200141106a41106a28020036020020022001290300370264200241ec006a200141086a280200360200200241f8016a410036020020024190026a41f182808000360200200241a8026a4100360200200241c0026a4100360200200241c8026a41e8a6c48000360200200241cc026a410d360200200241f0016a4280808080c000370300200242f681cbfbfc8791b01c3703800220024188026a42f8978ab68af4bdd3937f370300200241fc016a41013a0000200241e0016a200141386a41106a280200360200200241d8016a200141386a41086a290300370300200241d0016a2001290338370300200241e4016a2001290328370200200241ec016a200141286a41086a280200360200200241b8036a42f2a19699e4e1bdb1ed00370300200241b0036a4288d7a582bd91bff5867f370300200241a0036a42e5c18a8c83d9a2ecc20037030020024198036a42b4ff9ef1bdc3bdda2837030020024188036a42c9e8d484edccb5e26c370300200242a79fcbb9e09aeaff7c37038003200241fc026a41023a0000200241f4026a4204370200200241ec026a4200370200200241e4026a42808080808001370200200241cc036a4108360200200241c8036a41e0a6c48000360200200241c0036a41f282808000360200200241a8036a41f38280800036020020024190036a41f482808000360200200241e0026a200141d0006a41106a280200360200200241d8026a200141d0006a41086a290300370300200241d0026a2001290350370300200241e0036a200141f8006a41106a280200360200200241d8036a200141f8006a41086a290300370300200241d0036a2001290378370300200241ec036a200141e8006a41086a280200360200200241e4036a2001290368370200200241fc036a41033a0000200241f8036a4100360200200241f0036a4280808080c000370300200141f8006a10d988808000200128027c2203210402402001280280012205450d002003200541386c22056a21062003210403402004280200450d01200441386a2104200541486a22050d000b200621040b2001280278210741002d00fca3c680001a41800341002802c8a3c68000118180808000002206450d0141002d00fca3c680001a41a80141002802c8a3c68000118180808000002208450d0241002d00fca3c680001a410841002802c8a3c68000118180808000002209450d032009419c84c480003602002009412436020441002d00fca3c680001a412041002802c8a3c68000118180808000002205450d04200541d5d7c3800036020020054292a2a3bece87f785bf7f370308200541f58280800036021820054105360204200541106a42fbf4c799cbf4c181c70037030041002d00fca3c680001a410841002802c8a3c6800011818080800000220a450d05200a41c784c48000360200200a411936020441002d00fca3c680001a412041002802c8a3c6800011818080800000220b450d06200b41f9d3c38000360200200b42f68d80b7cfa6d3bb4e370308200b41f682808000360218200b4106360204200b41106a42dc8ec6fd9fd6fcdeb77f37030041002d00fca3c680001a410841002802c8a3c6800011818080800000220c450d07200c41d000360204200c41ed84c4800036020020084180016a42d39e8a9dc98694cb37370300200841f8006a42c4b088d2f7cca0855f370300200841c8006a429ccab49c93e2e495cf00370300200841c0006a42d1c5efcdcd82cde1ff00370300200841106a42fbe0bedd81cff9c317370300200842cec9d2b3fca8a6f5bc7f370308200841a4016a4101360200200841a0016a200c36020020084198016a42818080801037030020084194016a200b36020020084190016a410136020020084188016a41f782808000360200200841f4006a4110360200200841bd85c48000360270200841ec006a4101360200200841e8006a200a360200200841e0006a428180808010370300200841dc006a2005360200200841d8006a4101360200200841d0006a4193828080003602002008413c6a410d360200200841e084c4800036023820084101360234200820093602302008428080808010370328200842808080808001370320200841f88280800036021820084107360204200841c084c4800036020041002d00fca3c680001a410841002802c8a3c6800011818080800000220d450d08200d41cd85c48000360200200d41c80036020441002d00fca3c680001a41a80141002802c8a3c6800011818080800000220e450d0941002d00fca3c680001a410841002802c8a3c6800011818080800000220a450d0a200a419986c48000360200200a412336020441002d00fca3c680001a412041002802c8a3c68000118180808000002209450d0b200941c084c48000360200200942d7c9cb8fc1cf97db3e370308200941b58080800036021820094107360204200941106a42e88488d0c0e3aebc1337030041002d00fca3c680001a412041002802c8a3c68000118180808000002205450d0c200541c486c48000360200200541ae87c48000360218200541ed86c48000360210200541e8d1c38000360208200541293602042005411c6a41dd00360200200541146a41c1003602002005410c6a410036020041002d00fca3c680001a411841002802c8a3c6800011818080800000220b450d0d200b41c788c48000360210200b41e8d1c38000360208200b4129360204200b419e88c48000360200200b41146a4130360200200b410c6a4100360200200e4180016a42ded2f2adffb8f5dd2b370300200e41f8006a42a3ac8ca0a39ceca97a370300200e41c8006a42c89a9d928994f7ea8d7f370300200e41c0006a42fb82e7fdcaf6be9b18370300200e41106a42a8c786dcd8d2a98d17370300200e42cecfe0e5d1c6dbf757370308200e41a4016a4103360200200e41a0016a200b360200200e4198016a428080808030370300200e4190016a42808080808001370300200e4188016a41f982808000360200200e41f4006a4111360200200e41f788c48000360270200e41ec006a4104360200200e41e8006a2005360200200e41e0006a4281808080c000370300200e41dc006a2009360200200e41d8006a4101360200200e41d0006a41fa82808000360200200e413c6a4113360200200e418b88c48000360238200e4101360234200e200a360230200e428080808010370328200e42808080808001370320200e41fb82808000360218200e4108360204200e41bc86c4800036020041002d00fca3c680001a410841002802c8a3c6800011818080800000220f450d0e200f418889c48000360200200f41c00036020441002d00fca3c680001a413841002802c8a3c68000118180808000002210450d0f41002d00fca3c680001a41e00041002802c8a3c6800011818080800000220b450d10200b41fbf4c38000360200200b42b09d9bccd2bafbc85c370308200b4183f5c38000360240200b4181f5c38000360220200b41fc82808000360218200b4106360204200b41d0006a42ab8bffbed784ffa5937f370300200b41c8006a42c194a6a793ccc3a857370300200b41306a42dda1fc828d83b3faab7f370300200b41286a42b7a18ef9ac95b6cbeb00370300200b41106a42909fd09cacfeedb5967f370300200b41d8006a41f581808000360200200b41c4006a410a360200200b41386a41ec81808000360200200b41246a410236020041002d00fca3c680001a41c80041002802c8a3c68000118180808000002205450d1120054194f8c38000360240200541c3f7c38000360238200541e8d1c38000360230200541a4f7c38000360228200541d1f6c3800036022020054180f6c38000360218200541a7f5c38000360210200541e8d1c380003602082005411a3602042005418df5c38000360200200541c4006a41293602002005413c6a41d100360200200541346a41003602002005412c6a411f360200200541246a41d3003602002005411c6a41d100360200200541146a41d9003602002005410c6a4100360200201041bdf8c38000360200201041106a42fbbf92b8c0c3b2e0f200370300201042e7b7af9aebaca1f35b37030820104109360234201020053602302010428380808090013703282010200b36022420104103360220201041fd828080003602182010411436020441002d00fca3c680001a410841002802c8a3c68000118180808000002211450d12201141d1f8c38000360200201141d30036020441002d00fca3c680001a41e00141002802c8a3c68000118180808000002209450d1341002d00fca3c680001a412041002802c8a3c6800011818080800000220a450d14200a41afd5c38000360200200a42b7a18ef9ac95b6cbeb00370308200a41ec81808000360218200a4109360204200a41106a42dda1fc828d83b3faab7f37030041002d00fca3c680001a412041002802c8a3c6800011818080800000220b450d15200b41b8d5c38000360200200b41a1d6c38000360218200b41d3d5c38000360210200b41e8d1c38000360208200b411b360204200b411c6a4113360200200b41146a41ce00360200200b410c6a410036020041002d00fca3c680001a410841002802c8a3c68000118180808000002212450d16201241c3d6c380003602002012411a36020441002d00fca3c680001a412041002802c8a3c6800011818080800000220c450d17200c41ebd6c38000360200200c429cc4c6fe96f1ecf5e000370308200c418981808000360218200c4108360204200c41106a42aeb899d38addfa912137030041002d00fca3c680001a410841002802c8a3c68000118180808000002213450d18201341f3d6c38000360200201341cf0036020441002d00fca3c680001a41c00041002802c8a3c68000118180808000002205450d19200541d5d7c3800036020020054292a2a3bece87f785bf7f370308200541dad7c38000360220200541f58280800036021820054105360204200541306a42aeb899d38addfa9121370300200541286a429cc4c6fe96f1ecf5e000370300200541106a42fbf4c799cbf4c181c700370300200541386a418981808000360200200541246a410436020041002d00fca3c680001a410841002802c8a3c68000118180808000002214450d1a201441d500360204201441ded7c38000360200200941b8016a42a1f9febbd8b1ab95b07f370300200941b0016a42d8bcf6a5ac90bd85c70037030020094180016a429acfecb0d2a8f28be700370300200941f8006a428bdb9ef4d7aab8cbec00370300200941c8006a42dc8ec6fd9fd6fcdeb77f370300200941c0006a42f68d80b7cfa6d3bb4e370300200941106a42d881e89685dfc6955a370300200942f68096ca91b986f9c000370308200941dc016a4101360200200941d8016a2014360200200941d0016a428280808010370300200941cc016a2005360200200941c8016a4102360200200941c0016a41fe82808000360200200941ac016a410f360200200941b3d8c380003602a801200941a4016a4101360200200941a0016a201336020020094198016a42818080801037030020094194016a200c36020020094190016a410136020020094188016a41ff82808000360200200941f4006a4113360200200941c2d7c38000360270200941ec006a4101360200200941e8006a2012360200200941e0006a428080808010370300200941d8006a42808080808001370300200941d0006a4180838080003602002009413c6a410e360200200941ddd6c38000360238200941043602342009200b36023020094281808080c0003703282009200a3602242009410136022020094181838080003602182009410f360204200941b4d6c3800036020041002d00fca3c680001a410841002802c8a3c68000118180808000002215450d1b201541c2d8c38000360200201541dc0036020441002d00fca3c680001a413841002802c8a3c68000118180808000002216450d1c41002d00fca3c680001a412041002802c8a3c68000118180808000002205450d1d2005419783c48000360200200542c4ccab9c81ebb4b8db00370308200541f08180800036021820054107360204200541106a42a4d2928ecac0faa43237030041002d00fca3c680001a410841002802c8a3c6800011818080800000220b450d1e200b4130360204200b419e83c48000360200201641ce83c48000360200201641106a4293888c8f89fdc6ec9e7f370300201642a5e9e3ab9e929adc2c370308201641013602342016200b36023020164281808080103703282016200536022420164101360220201641ef808080003602182016410d36020441002d00fca3c680001a410841002802c8a3c68000118180808000002217450d1f201741db83c480003602002017412036020441002d00fca3c680001a41f00741002802c8a3c68000118180808000002205450d2041002d00fca3c680001a412041002802c8a3c68000118180808000002218450d21201841feebc38000360200201842c4ccab9c81ebb4b8db00370308201841f08180800036021820184102360204201841106a42a4d2928ecac0faa43237030041002d00fca3c680001a410841002802c8a3c68000118180808000002219450d22201941f395c480003602002019412c36020441002d00fca3c680001a412041002802c8a3c6800011818080800000221a450d23201a41a996c48000360200201a42a5e9e3ab9e929adc2c370308201a418283808000360218201a4103360204201a41106a4293888c8f89fdc6ec9e7f37030041002d00fca3c680001a410841002802c8a3c6800011818080800000221b450d24201b41ac96c48000360200201b41ce0036020441002d00fca3c680001a412041002802c8a3c6800011818080800000221c450d25201c418b97c48000360200201c42e8e094efaab6e5ba5b370308201c418383808000360218201c4103360204201c41106a4299b9c2f4a1b0a7a4f10037030041002d00fca3c680001a411041002802c8a3c6800011818080800000221d450d26201d418e97c48000360200201d41e397c48000360208201d41d500360204201d410c6a410836020041002d00fca3c680001a410841002802c8a3c6800011818080800000221e450d27201e418398c48000360200201e411336020441002d00fca3c680001a410841002802c8a3c6800011818080800000221f450d28201f41b098c48000360200201f411436020441002d00fca3c680001a410841002802c8a3c68000118180808000002220450d29202041cc98c48000360200202041c00036020441002d00fca3c680001a410841002802c8a3c68000118180808000002221450d2a202141a399c480003602002021411b36020441002d00fca3c680001a412041002802c8a3c68000118180808000002222450d2b202241d399c48000360200202242d7c9cb8fc1cf97db3e370308202241b58080800036021820224104360204202241106a42e88488d0c0e3aebc1337030041002d00fca3c680001a410841002802c8a3c68000118180808000002223450d2c202341d799c480003602002023412636020441002d00fca3c680001a410841002802c8a3c68000118180808000002224450d2d2024418e9ac480003602002024412636020441002d00fca3c680001a411841002802c8a3c68000118180808000002213450d2e201341c49ac48000360200201341f59ac48000360210201341e8d1c3800036020820134131360204201341146a41ce003602002013410c6a410036020041002d00fca3c680001a411841002802c8a3c68000118180808000002214450d2f201441d69bc48000360200201441879cc48000360210201441e8d1c3800036020820144131360204201441146a413b3602002014410c6a410036020041002d00fca3c680001a411841002802c8a3c68000118180808000002225450d30202541d59cc48000360200202541849dc48000360210202541e8d1c380003602082025412f360204202541146a41393602002025410c6a410036020041002d00fca3c680001a410841002802c8a3c68000118180808000002226450d31202641ce9dc480003602002026412336020441002d00fca3c680001a41c00041002802c8a3c68000118180808000002212450d32201241fd9dc48000360200201242a4d981999395fed957370308201241829ec48000360220201241848380800036021820124105360204201241306a42ab8bffbed784ffa5937f370300201241286a42c194a6a793ccc3a857370300201241106a4290bdcef9d19f84fee800370300201241386a41f581808000360200201241246a410436020041002d00fca3c680001a410841002802c8a3c68000118180808000002227450d33202741869ec480003602002027411136020441002d00fca3c680001a41e00041002802c8a3c6800011818080800000220b450d34200b41dad7c38000360200200b42e7b0a091f3ed9c85c500370308200b41a89ec48000360240200b41a39ec48000360220200b41ea81808000360218200b4104360204200b41d0006a42e88488d0c0e3aebc13370300200b41c8006a42d7c9cb8fc1cf97db3e370300200b41306a42b891b68c98adebcf61370300200b41286a42e7b0a091f3ed9c85c500370300200b41106a42b891b68c98adebcf61370300200b41d8006a41b580808000360200200b41c4006a4103360200200b41386a41ea81808000360200200b41246a410536020041002d00fca3c680001a411041002802c8a3c68000118180808000002228450d35202841ab9ec48000360200202841ed9ec48000360208202841c2003602042028410c6a410b36020041002d00fca3c680001a410841002802c8a3c68000118180808000002229450d362029418f9fc480003602002029411e36020441002d00fca3c680001a41e00041002802c8a3c6800011818080800000220a450d37200a41b99fc48000360200200a42e3beec81d3e996af917f370308200a41c29fc48000360240200a41bc9fc48000360220200a418583808000360218200a4103360204200a41d0006a42b891b68c98adebcf61370300200a41c8006a42e7b0a091f3ed9c85c500370300200a41306a42a4d2928ecac0faa432370300200a41286a42c4ccab9c81ebb4b8db00370300200a41106a42f0a3fcacaeba9c956f370300200a41d8006a41ea81808000360200200a41c4006a4107360200200a41386a41f081808000360200200a41246a410636020041002d00fca3c680001a410841002802c8a3c6800011818080800000222a450d38202a41c99fc48000360200202a413536020441002d00fca3c680001a41e00041002802c8a3c6800011818080800000220c450d39200c418ca0c48000360200200c42e7b0a091f3ed9c85c500370308200c4194a0c48000360240200c418fa0c48000360220200c41ea81808000360218200c4103360204200c41d0006a4298848fa1dab08ba174370300200c41c8006a42febac4ad81b6fafcb37f370300200c41306a42b891b68c98adebcf61370300200c41286a42e7b0a091f3ed9c85c500370300200c41106a42b891b68c98adebcf61370300200c41d8006a418881808000360200200c41c4006a4105360200200c41386a41ea81808000360200200c41246a410536020041002d00fca3c680001a410841002802c8a3c6800011818080800000222b450d3a202b41d800360204202b4199a0c48000360200200541c8076a429ccab49c93e2e495cf00370300200541c0076a42d1c5efcdcd82cde1ff0037030020054190076a4298848fa1dab08ba17437030020054188076a42febac4ad81b6fafcb37f370300200541d8066a429ccab49c93e2e495cf00370300200541d0066a42d1c5efcdcd82cde1ff00370300200541a0066a429ccab49c93e2e495cf0037030020054198066a42d1c5efcdcd82cde1ff00370300200541e8056a429ccab49c93e2e495cf00370300200541e0056a42d1c5efcdcd82cde1ff00370300200541b0056a429ccab49c93e2e495cf00370300200541a8056a42d1c5efcdcd82cde1ff00370300200541f8046a42dfc4809d8ac5e4f1de00370300200541f0046a42e5e882a9be85fc80fa00370300200541c0046a4294ed85e8ed84e1d30b370300200541b8046a42d8e4bea39ae2d6da0637030020054188046a4295d2cbc8a2888c9a7d37030020054180046a42f69cccefd7e0e1c38b7f370300200541d0036a4293888c8f89fdc6ec9e7f370300200541c8036a42a5e9e3ab9e929adc2c37030020054198036a42b891b68c98adebcf6137030020054190036a42e7b0a091f3ed9c85c500370300200541e0026a4293888c8f89fdc6ec9e7f370300200541d8026a42a5e9e3ab9e929adc2c370300200541a8026a4293888c8f89fdc6ec9e7f370300200541a0026a42a5e9e3ab9e929adc2c370300200541f0016a4293888c8f89fdc6ec9e7f370300200541e8016a42a5e9e3ab9e929adc2c370300200541b8016a4293888c8f89fdc6ec9e7f370300200541b0016a42a5e9e3ab9e929adc2c37030020054180016a42fd87d58eecf6d4b521370300200541f8006a42c5ccf6ddf0d2bef89a7f370300200541c8006a4293888c8f89fdc6ec9e7f370300200541c0006a42a5e9e3ab9e929adc2c370300200541106a4293888c8f89fdc6ec9e7f370300200542a5e9e3ab9e929adc2c370308200541ec076a4101360200200541e8076a202b360200200541e0076a428380808010370300200541dc076a200c360200200541d8076a4103360200200541d0076a419382808000360200200541bc076a410f360200200541f1a0c480003602b807200541b4076a4101360200200541b0076a202a360200200541a8076a428380808010370300200541a4076a200a360200200541a0076a410336020020054198076a41888180800036020020054184076a410e360200200541fe9fc4800036028007200541fc066a4101360200200541f8066a2029360200200541f0066a428080808010370300200541e8066a42808080808001370300200541e0066a419382808000360200200541cc066a410c360200200541ad9fc480003602c806200541c4066a4102360200200541c0066a2028360200200541b8066a428380808020370300200541b4066a200b360200200541b0066a4103360200200541a8066a41938280800036020020054194066a4117360200200541f89ec48000360290062005418c066a410136020020054188066a202736020020054180066a428280808010370300200541fc056a2012360200200541f8056a4102360200200541f0056a419382808000360200200541dc056a410c360200200541979ec480003602d805200541d4056a4101360200200541d0056a2026360200200541c8056a428080808010370300200541c0056a42808080808001370300200541b8056a419382808000360200200541a4056a410c360200200541f19dc480003602a0052005419c056a410336020020054198056a202536020020054190056a42808080803037030020054188056a4280808080800137030020054180056a418683808000360200200541ec046a4111360200200541bd9dc480003602e804200541e4046a4103360200200541e0046a2014360200200541d8046a428080808030370300200541d0046a42808080808001370300200541c8046a418783808000360200200541b4046a4113360200200541c29cc480003602b004200541ac046a4103360200200541a8046a2013360200200541a0046a42808080803037030020054198046a4280808080800137030020054190046a418883808000360200200541fc036a4113360200200541c39bc480003602f803200541f4036a4101360200200541f0036a2024360200200541e8036a428080808010370300200541e0036a42808080808001370300200541d8036a41ef80808000360200200541c4036a4110360200200541b49ac480003602c003200541bc036a4101360200200541b8036a2023360200200541b0036a428180808010370300200541ac036a2022360200200541a8036a4101360200200541a0036a41ea818080003602002005418c036a4111360200200541fd99c480003602880320054184036a410136020020054180036a2021360200200541f8026a428080808010370300200541f0026a42808080808001370300200541e8026a41ef80808000360200200541d4026a4115360200200541be99c480003602d002200541cc026a4101360200200541c8026a2020360200200541c0026a428080808010370300200541b8026a42808080808001370300200541b0026a41ef808080003602002005419c026a41173602002005418c99c480003602980220054194026a410136020020054190026a201f36020020054188026a42808080801037030020054180026a42808080808001370300200541f8016a41ef80808000360200200541e4016a4108360200200541c498c480003602e001200541dc016a4101360200200541d8016a201e360200200541d0016a428080808010370300200541c8016a42808080808001370300200541c0016a41ef80808000360200200541ac016a411a3602002005419698c480003602a801200541a4016a4102360200200541a0016a201d36020020054198016a42818080802037030020054194016a201c36020020054190016a410136020020054188016a418383808000360200200541f4006a4118360200200541eb97c48000360270200541ec006a4101360200200541e8006a201b360200200541e0006a428180808010370300200541dc006a201a360200200541d8006a4101360200200541d0006a41ef808080003602002005413c6a4111360200200541fa96c48000360238200541013602342005201936023020054281808080103703282005201836022420054101360220200541ef808080003602182005410a3602042005419f96c4800036020041002d00fca3c680001a41f00041002802c8a3c68000118180808000002214450d3b41002d00fca3c680001a411841002802c8a3c6800011818080800000220b450d3c200b41aad9c38000360200200b41ced9c38000360210200b41e8d1c38000360208200b4124360204200b41146a41c900360200200b410c6a410036020041002d00fca3c680001a410841002802c8a3c6800011818080800000220a450d3d200a4127360204200a41a4dac3800036020020144197dac38000360200201441c8006a42c4e2cedafbc28595bc7f370300201441c0006a42d8b6abf18ab7a1e965370300201441106a42b683bfa183f992fe847f370300201442de88dfa5f1c5f7b6a27f370308201441ec006a4101360200201441e8006a200a360200201441e0006a428080808010370300201441d8006a42808080808001370300201441d0006a4189838080003602002014413c6a410b360200201441cbdac38000360238201441033602342014200b36023020144280808080303703282014428080808080013703202014418a838080003602182014410d36020441002d00fca3c680001a410841002802c8a3c68000118180808000002228450d3e202841d6dac380003602002028412e36020441002d00fca3c680001a41d00241002802c8a3c6800011818080800000220b450d3f41002d00fca3c680001a410841002802c8a3c68000118180808000002218450d402018418bdbc380003602002018412336020441002d00fca3c680001a410841002802c8a3c6800011818080800000221a450d41201a41bbdbc38000360200201a413136020441002d00fca3c680001a410841002802c8a3c6800011818080800000221c450d42201c41ffdbc38000360200201c413136020441002d00fca3c680001a411041002802c8a3c68000118180808000002225450d43202541bddcc38000360200202541fddcc38000360208202541c0003602042025410c6a411736020041002d00fca3c680001a41c00041002802c8a3c68000118180808000002212450d442012419eddc38000360200201242e3a4fae3cee3d18d72370308201241a2ddc38000360220201241f58080800036021820124104360204201241306a42e9b494c69bdbc4d608370300201241286a42ead283debcdebd93d800370300201241106a42b8b6d386cdcbfab1a07f370300201241386a41f780808000360200201241246a410c36020041002d00fca3c680001a41d80041002802c8a3c6800011818080800000220a450d45200a41c0e2c38000360250200a41fee1c38000360248200a41c2e1c38000360240200a41ffe0c38000360238200a41bde0c38000360230200a41fbdfc38000360228200a41b7dfc38000360220200a41f4dec38000360218200a41b3dec38000360210200a41f0ddc38000360208200a41c200360204200a41aeddc38000360200200a41d4006a4134360200200a41cc006a41c200360200200a41c4006a413c360200200a413c6a41c300360200200a41346a41c200360200200a412c6a41c200360200200a41246a41c400360200200a411c6a41c300360200200a41146a41c100360200200a410c6a41c30036020041002d00fca3c680001a41c00041002802c8a3c68000118180808000002213450d4620134190e3c380003602002013428ae790e9b7c48cad987f370308201341a2e3c380003602202013418b8380800036021820134112360204201341306a42b889cc8cd692ff80fa00370300201341286a4288f7e1d594faa5a120370300201341106a42ade681e7ba94a3bd8d7f370300201341386a418c83808000360200201341246a410f36020041002d00fca3c680001a41c00041002802c8a3c6800011818080800000220c450d47200c41fbe6c38000360238200c41bae6c38000360230200c41f8e5c38000360228200c41b3e5c38000360220200c41f2e4c38000360218200c41b3e4c38000360210200c41f5e3c38000360208200c41c400360204200c41b1e3c38000360200200c413c6a41c100360200200c41346a41c100360200200c412c6a41c200360200200c41246a41c500360200200c411c6a41c100360200200c41146a413f360200200c410c6a413e360200200b41a8026a42a0a58eade3c7d8c3f600370300200b41a0026a42bbbde2dfeee883e643370300200b41f0016a42bcded3878bc0cf8f7c370300200b41e8016a42b7bec490f9e7d7cda97f370300200b41b8016a4285b396c8e397b6a6f000370300200b41b0016a42aca08db5a793aef98d7f370300200b4180016a4285b396c8e397b6a6f000370300200b41f8006a42aca08db5a793aef98d7f370300200b41c8006a42b8b6d386cdcbfab1a07f370300200b41c0006a42e3a4fae3cee3d18d72370300200b41106a42e1ca8ae9eabca78cc200370300200b4290c489c6869cfac00d370308200b41cc026a4108360200200b41c8026a200c360200200b41c0026a42828080808001370300200b41bc026a2013360200200b41b8026a4102360200200b41b0026a418d83808000360200200b419c026a412d360200200b41bce7c3800036029802200b4194026a410b360200200b4190026a200a360200200b4188026a4282808080b001370300200b4184026a2012360200200b4180026a4102360200200b41f8016a418e83808000360200200b41e4016a411c360200200b41f4e2c380003602e001200b41dc016a4102360200200b41d8016a2025360200200b41d0016a428080808020370300200b41c8016a42808080808001370300200b41c0016a418f83808000360200200b41ac016a410a360200200b4194ddc380003602a801200b41a4016a4101360200200b41a0016a201c360200200b4198016a428080808010370300200b4190016a42808080808001370300200b4188016a418f83808000360200200b41f4006a410d360200200b41b0dcc38000360270200b41ec006a4101360200200b41e8006a201a360200200b41e0006a428080808010370300200b41d8006a42808080808001370300200b41d0006a41f580808000360200200b413c6a4113360200200b41ecdbc38000360238200b4101360234200b2018360230200b428080808010370328200b42808080808001370320200b419083808000360218200b410d360204200b41aedbc3800036020041002d00fca3c680001a410841002802c8a3c68000118180808000002219450d48201941e9e7c380003602002019412e36020441002d00fca3c680001a413841002802c8a3c6800011818080800000221a450d4941002d00fca3c680001a412041002802c8a3c6800011818080800000220a450d4a200a41f9d3c38000360200200a42f68d80b7cfa6d3bb4e370308200a41f682808000360218200a4106360204200a41106a42dc8ec6fd9fd6fcdeb77f37030041002d00fca3c680001a410841002802c8a3c6800011818080800000220c450d4b200c4132360204200c41ffd3c38000360200201a41b1d4c38000360200201a41106a429ccab49c93e2e495cf00370300201a42d1c5efcdcd82cde1ff00370308201a4101360234201a200c360230201a428180808010370328201a200a360224201a4101360220201a419382808000360218201a410f36020441002d00fca3c680001a410841002802c8a3c6800011818080800000221b450d4c201b41c0d4c38000360200201b411936020441002d00fca3c680001a41f00041002802c8a3c68000118180808000002213450d4d41002d00fca3c680001a412041002802c8a3c68000118180808000002212450d4e201241cbd0c38000360200201242f99f94a5fdd3dbbaf900370308201241918380800036021820124104360204201241106a42f6d183c8fca4ffd45a37030041002d00fca3c680001a413841002802c8a3c6800011818080800000220a450d4f200a41cfd0c38000360200200a4194d2c38000360230200a41e8d1c38000360228200a41e8d1c38000360220200a41e8d1c38000360218200a41d6d1c38000360210200a4194d1c38000360208200a41c500360204200a41346a4134360200200a412c6a4100360200200a41246a412c360200200a411c6a4100360200200a41146a410f360200200a410c6a41c20036020041002d00fca3c680001a412041002802c8a3c68000118180808000002225450d50202541ddd2c38000360200202542e7b0a091f3ed9c85c500370308202541ea8180800036021820254107360204202541106a42b891b68c98adebcf6137030041002d00fca3c680001a411841002802c8a3c6800011818080800000220c450d51200c418ad3c38000360210200c41e8d1c38000360208200c4126360204200c41e4d2c38000360200200c41146a4137360200200c410c6a4100360200201341c8006a42bfd1fbe392d3878718370300201341c0006a42c6b49cae9188d5c4a37f370300201341106a42b891b68c98adebcf61370300201342e7b0a091f3ed9c85c500370308201341ec006a4103360200201341e8006a200c360200201341e0006a428180808030370300201341dc006a2025360200201341d8006a4101360200201341d0006a4192838080003602002013413c6a4113360200201341c1d3c38000360238201341073602342013200a36023020134281808080f0003703282013201236022420134101360220201341ea8180800036021820134115360204201341c8d2c3800036020041002d00fca3c680001a410841002802c8a3c6800011818080800000221e450d52201e41d4d3c38000360200201e411a36020441002d00fca3c680001a41e00141002802c8a3c6800011818080800000220a450d5341002d00fca3c680001a413041002802c8a3c68000118180808000002225450d54202541baf9c38000360200202541ecfbc380003602282025419dfbc38000360220202541d0fac38000360218202541e8d1c3800036021020254189fac38000360208202541cf003602042025412c6a4130360200202541246a41cf003602002025411c6a41cd00360200202541146a41003602002025410c6a41c70036020041002d00fca3c680001a41c00041002802c8a3c68000118180808000002218450d5520184190e3c38000360200201842c1d4a9c8c2d78cfcc000370308201841a2e3c38000360220201841938380800036021820184112360204201841306a4299f0ab899787d3ad3a370300201841286a42d4b0f086cab3d2eb14370300201841106a42e3f39ebbdcd7db8c79370300201841386a419483808000360200201841246a410f36020041002d00fca3c680001a41c00041002802c8a3c68000118180808000002212450d56201241b1e3c38000360200201241fbe6c38000360238201241bae6c38000360230201241f8e5c38000360228201241b3e5c38000360220201241f2e4c38000360218201241b3e4c38000360210201241f5e3c38000360208201241c4003602042012413c6a41c100360200201241346a41c1003602002012412c6a41c200360200201241246a41c5003602002012411c6a41c100360200201241146a413f3602002012410c6a413e36020041002d00fca3c680001a41c00041002802c8a3c6800011818080800000221c450d57201c41affcc38000360200201c42a5e9e3ab9e929adc2c370308201c41a2ddc38000360220201c41ef80808000360218201c4106360204201c41306a42949384fce98ae9ac977f370300201c41286a42c4a8f7cba2aea4ad35370300201c41106a4293888c8f89fdc6ec9e7f370300201c41386a41ff81808000360200201c41246a410c36020041002d00fca3c680001a41d80041002802c8a3c6800011818080800000220c450d58200c41fffec38000360250200c41bcfec38000360248200c41f7fdc38000360240200c41ffe0c38000360238200c41b8fdc38000360230200c41f4fcc38000360228200c41b7dfc38000360220200c41f4dec38000360218200c41b3dec38000360210200c41b5fcc38000360208200c41c200360204200c41aeddc38000360200200c41d4006a411e360200200c41cc006a41c300360200200c41c4006a41c500360200200c413c6a41c300360200200c41346a413f360200200c412c6a41c400360200200c41246a41c400360200200c411c6a41c300360200200c41146a41c100360200200c410c6a413f36020041002d00fca3c680001a410841002802c8a3c68000118180808000002222450d59202241263602042022419dffc38000360200200a41b8016a4293888c8f89fdc6ec9e7f370300200a41b0016a42a5e9e3ab9e929adc2c370300200a4180016a42d38accc2f68caeecba7f370300200a41f8006a42bfcdc4dc94d7acde3b370300200a41c8006a42a0a58eade3c7d8c3f600370300200a41c0006a42bbbde2dfeee883e643370300200a41106a42d592f683e7e68799fc00370300200a4287bf8ac8928ddfce54370308200a41dc016a4101360200200a41d8016a2022360200200a41d0016a428080808010370300200a41c8016a42808080808001370300200a41c0016a41ef80808000360200200a41ac016a410e360200200a41c3ffc380003602a801200a41a4016a410b360200200a41a0016a200c360200200a4198016a4282808080b001370300200a4194016a201c360200200a4190016a4102360200200a4188016a419583808000360200200a41f4006a411c360200200a41f4e2c38000360270200a41ec006a4108360200200a41e8006a2012360200200a41e0006a42828080808001370300200a41dc006a2018360200200a41d8006a4102360200200a41d0006a418d83808000360200200a413c6a412d360200200a41bce7c38000360238200a4106360234200a2025360230200a4280808080e000370328200a42808080808001370320200a419683808000360218200a4113360204200a419cfcc3800036020041002d00fca3c680001a41c80041002802c8a3c68000118180808000002225450d5a202541d1ffc38000360200202541ce82c48000360240202541e8d1c380003602382025419582c48000360230202541c981c480003602282025418681c48000360220202541c180c48000360218202541e8d1c380003602102025419180c48000360208202541c000360204202541c4006a413f3602002025413c6a4100360200202541346a41393602002025412c6a41cc00360200202541246a41c3003602002025411c6a41c500360200202541146a41003602002025410c6a413036020041002d00fca3c680001a41a80141002802c8a3c6800011818080800000220c450d5b41002d00fca3c680001a412041002802c8a3c68000118180808000002222450d5c2022419ee8c38000360200202242e7b0a091f3ed9c85c500370308202241ea8180800036021820224104360204202241106a42b891b68c98adebcf6137030041002d00fca3c680001a41c80041002802c8a3c68000118180808000002218450d5d201841a2e8c38000360200201841dcebc3800036024020184185ebc38000360238201841e8d1c38000360230201841d8eac3800036022820184182eac3800036022020184184e9c38000360218201841e8d1c38000360210201841fbe8c38000360208201841d900360204201841c4006a41173602002018413c6a41d700360200201841346a41003602002018412c6a412d360200201841246a41d6003602002018411c6a41fe00360200201841146a41003602002018410c6a410936020041002d00fca3c680001a412041002802c8a3c6800011818080800000221d450d5e201d41feebc38000360200201d42b9bfe7eaeaf488d21c370308201d419783808000360218201d4102360204201d41106a42a9a98a9dddd4dcd0dc0037030041002d00fca3c680001a41f00041002802c8a3c68000118180808000002212450d5f20124190f2c38000360268201241b8f1c38000360260201241dff0c380003602582012418cf0c38000360250201241b3efc38000360248201241dfeec3800036024020124197eec38000360238201241e8d1c38000360230201241ffedc38000360228201241adedc38000360220201241deecc38000360218201241e8d1c38000360210201241d8ecc38000360208201241d80036020420124180ecc38000360200201241ec006a4121360200201241e4006a41d800360200201241dc006a41d900360200201241d4006a41d300360200201241cc006a41d900360200201241c4006a41d4003602002012413c6a41c800360200201241346a41003602002012412c6a4118360200201241246a41d2003602002012411c6a41cf00360200201241146a41003602002012410c6a410636020041002d00fca3c680001a412041002802c8a3c6800011818080800000221c450d60201c41e7f3c38000360218201c418ff3c38000360210201c41e8d1c38000360208201c41d400360204201c41bbf2c38000360200201c411c6a41c000360200201c41146a41d800360200201c410c6a4100360200200c4180016a42cdbdd1fc838da48215370300200c41f8006a4288bed2e1b0b8c0e336370300200c41c8006a42f6d183c8fca4ffd45a370300200c41c0006a42f99f94a5fdd3dbbaf900370300200c41106a42cfe5aeebd69bcbd576370300200c42eccdbcbecbac99cabc7f370308200c41a4016a4104360200200c41a0016a201c360200200c4198016a4280808080c000370300200c4190016a42808080808001370300200c4188016a419883808000360200200c41f4006a410c360200200c41a7f4c38000360270200c41ec006a410e360200200c41e8006a2012360200200c41e0006a4281808080e001370300200c41dc006a201d360200200c41d8006a4101360200200c41d0006a419183808000360200200c413c6a410a360200200c41b1f2c38000360238200c4109360234200c2018360230200c42818080809001370328200c2022360224200c4101360220200c419983808000360218200c410b360204200c41f3ebc3800036020041002d00fca3c680001a410841002802c8a3c68000118180808000002212450d612012413a360204201241b3f4c38000360200200641fc026a410e360200200641f8026a41edf4c38000360200200641f4026a4101360200200641f0026a2012360200200641e8026a428380808010370200200641e4026a200c360200200641dc026a428a80808030370200200641d8026a418d83c48000360200200641d4026a4109360200200641d0026a2025360200200641c8026a42848080809001370200200641c4026a200a360200200641bc026a428b808080c000370200200641b8026a41eed3c38000360200200641b4026a4101360200200641b0026a201e360200200641a8026a428280808010370200200641a4026a20133602002006419c026a42918080802037020020064198026a41d9d4c3800036020020064194026a410136020020064190026a201b36020020064188026a42818080801037020020064184026a201a360200200641fc016a428780808010370200200641f8016a4197e8c38000360200200641f4016a4101360200200641f0016a2019360200200641e8016a428680808010370200200641e4016a200b360200200641dc016a4287808080e000370200200641d8016a4184dbc38000360200200641d4016a4101360200200641d0016a2028360200200641c8016a428280808010370200200641c4016a2014360200200641bc016a428780808020370200200641b8016a4180a1c48000360200200641b0016a4204370200200641a8016a4212370200200641a4016a20053602002006419c016a428f808080a00237020020064198016a41fb83c4800036020020064194016a410136020020064190016a201736020020064188016a42818080801037020020064184016a2016360200200641fc006a428c80808010370200200641f8006a419ed9c38000360200200641f4006a4101360200200641f0006a2015360200200641e8006a428480808010370200200641e4006a2009360200200641dc006a4296808080c000370200200641d8006a41a4f9c38000360200200641d4006a4101360200200641d0006a2011360200200641c8006a428180808010370200200641c4006a20103602002006413c6a428880808010370200200641386a41c889c48000360200200641346a4101360200200641306a200f360200200641286a428380808010370200200641246a200e360200200642848080803037021c2006419586c48000360218200641013602142006200d36021020064283808080103702082006200836020420064103360200200041f0016a4104360200200041ec016a2002360200200041043602e801200041f0006a41ec8180800036020020004188016a41f081808000360200200041a0016a419a83808000360200200041b8016a418583808000360200200041d0016a419b83808000360200200041d8016a200741386c41386e360200200041dc016a2003360200200041e0016a200420036b41386e360200200041e4016a41043a0000200041c0016a42dba4b9c0a2bed19501370300200041c8016a4283af8bf0ede78391a67f370300200041a8016a42e3beec81d3e996af917f370300200041b0016a42f0a3fcacaeba9c956f37030020004190016a42dbe2e9e8becbf3fb2f37030020004198016a42fec1aad493d7e4ae3e370300200041f8006a42c4ccab9c81ebb4b8db0037030020004180016a42a4d2928ecac0faa432370300200042b7a18ef9ac95b6cbeb00370360200041e8006a42dda1fc828d83b3faab7f3703002000419c838080003602102000410c3602f401200041f8016a2006360200200041fc016a410c360200200042f9ffdfe3cfb190868f7f37030820004287f1a7a98f94a1fd58370300200041286a419a83808000360200200041c0006a419a82808000360200200041d8006a419d83808000360200200041c8006a42d8a6d184eacbe6c8937f370300200041d0006a428fa8ddeba8afb0e82c370300200041306a428efca59582e3caf618370300200041386a42988ac9c0f3f1b2b0b57f370300200042dbe2e9e8becbf3fb2f370318200041206a42fec1aad493d7e4ae3e370300200041e7016a200141fa006a2d00003a0000200041e5016a20012f00783b000020014190016a2480808080000f0b410841800410b280808000000b410441800310b280808000000b410841a80110b280808000000b4104410810b280808000000b4108412010b280808000000b4104410810b280808000000b4108412010b280808000000b4104410810b280808000000b4104410810b280808000000b410841a80110b280808000000b4104410810b280808000000b4108412010b280808000000b4104412010b280808000000b4104411810b280808000000b4104410810b280808000000b4108413810b280808000000b410841e00010b280808000000b410441c80010b280808000000b4104410810b280808000000b410841e00110b280808000000b4108412010b280808000000b4104412010b280808000000b4104410810b280808000000b4108412010b280808000000b4104410810b280808000000b410841c00010b280808000000b4104410810b280808000000b4104410810b280808000000b4108413810b280808000000b4108412010b280808000000b4104410810b280808000000b4104410810b280808000000b410841f00710b280808000000b4108412010b280808000000b4104410810b280808000000b4108412010b280808000000b4104410810b280808000000b4108412010b280808000000b4104411010b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4108412010b280808000000b4104410810b280808000000b4104410810b280808000000b4104411810b280808000000b4104411810b280808000000b4104411810b280808000000b4104410810b280808000000b410841c00010b280808000000b4104410810b280808000000b410841e00010b280808000000b4104411010b280808000000b4104410810b280808000000b410841e00010b280808000000b4104410810b280808000000b410841e00010b280808000000b4104410810b280808000000b410841f00010b280808000000b4104411810b280808000000b4104410810b280808000000b4104410810b280808000000b410841d00210b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104411010b280808000000b410841c00010b280808000000b410441d80010b280808000000b410841c00010b280808000000b410441c00010b280808000000b4104410810b280808000000b4108413810b280808000000b4108412010b280808000000b4104410810b280808000000b4104410810b280808000000b410841f00010b280808000000b4108412010b280808000000b4104413810b280808000000b4108412010b280808000000b4104411810b280808000000b4104410810b280808000000b410841e00110b280808000000b4104413010b280808000000b410841c00010b280808000000b410441c00010b280808000000b410841c00010b280808000000b410441d80010b280808000000b4104410810b280808000000b410441c80010b280808000000b410841a80110b280808000000b4108412010b280808000000b410441c80010b280808000000b4108412010b280808000000b410441f00010b280808000000b4104412010b280808000000b4104410810b280808000000be00903027f017e027f23808080800041d0036b22042480808080000240200241c0016a2d000022054102460d00200441e0006a200241b0016a290300370300200441d8006a200241a8016a290300370300200441d0006a200241a0016a290300370300200441086a41086a200241e0006a290300370300200441086a41106a200241e8006a290300370300200441086a41186a200241f0006a290300370300200441086a41206a200241f8006a290300370300200441306a20024180016a290300370300200441386a20024188016a290300370300200441c0006a20024190016a29030037030020042002290358370308200420024198016a290300370348200441f0016a41206a200241e1016a2d00003a0000200441f0016a41186a200241d9016a290000370300200441f0016a41106a200241d1016a290000370300200441f0016a41086a200241c9016a2900003703002004200241c1016a2900003703f001200241b8016a290300210620044198026a200441086a41e00010848e8080001a0b02400240024002400240200228020041736a2207410220074104491b0e0400010203000b200441f8026a41086a200241086a10a28a8080002004410d3602f8020c030b0240200241186a2d00002207417d6a41ff0171220841016a410320084102491b22084103460d00024002402008417f6a0e020001000b200228020810b087808000000b200228020810b087808000000b200441f8026a41186a20073a0000200441f8026a41106a200241106a2903003703002004410e3602f80220042002290308370380030c020b200441f8026a200210bf888080000c010b200441f8026a41086a200241086a10aa8c808000200441103602f8020b200441086a41d8006a20044198026a41e00010848e8080001a200441c8016a20053a0000200441c0016a2006370300200441c9016a20042903f001370000200441d1016a200441f0016a41086a290300370000200441d9016a200441f0016a41106a290300370000200441e1016a200441f0016a41186a290300370000200441e8016a2004418f026a290000370000200441086a200441f8026a41d80010848e8080001a20044198026a2001200441086a200310818d80800002404100280284a4c680004105470d00200441f8026a410c6a419e838080003602002004419f838080003602fc02200420023602f802200420044198026a360280034100280290a1c680002103410028028ca1c6800021014100280280a4c680002105200441c0006a4202370200200441386a4102360200200441306a41163602002004412c6a41e88bc48000360200200441086a41186a41f48ac48000ad4280808080900c84370200200441086a410c6a41fe8bc48000ad4280808080e002843702002004413c6a200441f8026a360200200441f4a1c48000360234200441053602282004410036021c2004410036021020044281808080e03e370208200141ecf2c08000200541024622051b200441086a200341d4f2c0800020051b280210118480808000000b2000200429039802370300200041286a20044198026a41286a290300370300200041206a20044198026a41206a290300370300200041186a20044198026a41186a290300370300200041106a20044198026a41106a290300370300200041086a20044198026a41086a290300370300200210c989808000200441d0036a2480808080000be41d03087f017e027f23808080800041b0026b220024808080800041002d00fca3c680001a024002400240024002400240024002400240024002400240024002400240024002400240411641002802c8a3c68000118180808000002201450d002001410e6a41002900a28cc48000370000200141086a410029009c8cc48000370000200141002900948cc4800037000041002d00fca3c680001a410a41002802c8a3c68000118180808000002202450d01200241086a41002f00b28cc480003b0000200241002900aa8cc4800037000041002d00fca3c680001a411641002802c8a3c68000118180808000002203450d022003410e6a41002900c28cc48000370000200341086a41002900bc8cc48000370000200341002900b48cc4800037000041002d00fca3c680001a410a41002802c8a3c68000118180808000002204450d03200441086a41002f00d28cc480003b0000200441002900ca8cc4800037000041002d00fca3c680001a413041002802c8a3c68000118180808000002205450d0441002d00fca3c680001a411641002802c8a3c68000118180808000002206450d05200620012900003700002006410e6a2001410e6a290000370000200641086a200141086a29000037000041002d00fca3c680001a410a41002802c8a3c68000118180808000002207450d0620072002290000370000200741086a200241086a2f00003b0000200541163602002005410a3602142005200736021020054296808080a0013702082005200636020441002d00fca3c680001a411641002802c8a3c68000118180808000002206450d07200620032900003700002006410e6a2003410e6a290000370000200641086a200341086a29000037000041002d00fca3c680001a410a41002802c8a3c68000118180808000002207450d0820072004290000370000200741086a200441086a2f00003b00002005412c6a410a360200200541286a2007360200200541206a4296808080a0013702002005411c6a200636020020054116360218200141002802c0a3c6800011808080800000200241002802c0a3c6800011808080800000200341002802c0a3c6800011808080800000200441002802c0a3c680001180808080000041002d00fca3c680001a410141002802c8a3c68000118180808000002201450d09200141003a0000200041186a419380c6800041014100280298a3c6800011858080800000200041146a4100360200200042013702082000200136020420004101360200200041386a41186a22014200370300200041386a41106a22034200370300200041c0006a2202420037030020004200370338200041386a41d48cc4800041014100280298a3c6800011858080800000200041b0016a41b48ac48000360200200041dc006a41286a4100360200200041dc006a41206a4204370200200041dc006a41186a4200370200200041c0016a4200370200200041a1016a200129030037000020004199016a200329030037000020004191016a200229030037000020004189016a200029033837000020004280808080c00037026c200041003602b801200041013a0088012000200041386a3602b401200020003602ac01200041003602642000420037025c200041f8016a200041dc006a2005280204200528020820052802102005280214109286808000200041dc006a41106a2103024020002d00f80122014104460d002000419c026a2104200041f8016a41046a2102024020014103460d0002400240024020010e020103000b200028029c02220120012802002206417f6a3602002004210120064101460d010c020b20002802fc01220120012802002206417f6a3602002002210120064101470d010b200110e28a8080000b200041f8016a200041dc006a200528021c20052802202005280228200528022c10928680800020002d00f80122014104460d00024020014103460d0002400240024020010e020003010b20002802fc01220120012802002201417f6a36020020014101460d010c020b200028029c02220120012802002201417f6a3602002004210220014101470d010b200210e28a8080000b200041dc006a108f8680800020031090878080000240200028026c450d00200028027041002802c0a3c68000118080808000000b02402000280278450d00200028027c41002802c0a3c68000118080808000000b4100210141002103024020002802b8012202450d0020002002360290022000410036028c022000200236028002200041003602fc012000200041bc016a28020022013602940220002001360284024101210120002802c00121030b20002003360298022000200136028802200020013602f801200041f8016a10a48d808000200041f0016a41d88cc48000360200200041cc016a41186a4100360200200042003702dc01200041003602d401200042003702cc012000200041386a3602f401200020003602ec01200041dc006a200041cc016a10b388808000200028025c2201418080808078470d0e42e60021082000280260220328020041fcffffff076a2201410320014105491b0e040f0f0c0b0d0b024002400240024020002802fc01220128020041fcffffff076a2202410320024105491b0e0403030102000b2001280204450d02200141086a28020041002802c0a3c68000118080808000000c020b2001280204450d01200141086a28020041002802c0a3c68000118080808000000c010b200110ca898080000b200141002802c0a3c6800011808080800000200041dc006a108f8680800020031090878080000240200028026c450d00200028027041002802c0a3c68000118080808000000b02402000280278450d00200028027c41002802c0a3c68000118080808000000b410021014100210341002102024020002802b8012204450d0020002004360290022000410036028c022000200436028002200041003602fc012000200041bc016a28020022033602940220002003360284024101210320002802c00121020b20002002360298022000200336028802200020033602f801200041f8016a10a48d808000410021030240200028020c2202450d00200020023602742000410036027020002002360264200041003602602000200041106a28020022013602782000200136026841012101200028021421030b2000200336027c2000200136026c2000200136025c200041dc006a10928d80800002402000280200450d00200028020441002802c0a3c68000118080808000000b02402005280200450d00200528020441002802c0a3c68000118080808000000b0240200528020c450d00200528021041002802c0a3c68000118080808000000b02402005280218450d00200528021c41002802c0a3c68000118080808000000b42e50021082005280224450d100c0f0b4101411610b280808000000b4101410a10b280808000000b4101411610b280808000000b4101410a10b280808000000b4104413010b280808000000b4101411610b280808000000b4101410a10b280808000000b4101411610b280808000000b4101410a10b280808000000b4101410110b280808000000b200310ca898080000c030b2003280204450d02200341086a28020041002802c0a3c68000118080808000000c020b2003280204450d01200341086a28020041002802c0a3c68000118080808000000c010b20004180026a2202200041dc006a41106a290200370300200041f8016a41106a2204200041dc006a41186a290200370300200041f8016a41186a200041dc006a41206a290200370300200041f8016a41206a2206200041dc006a41286a290200370300200041f8016a41286a2207200041dc006a41306a290200370300200041f8016a41306a2209200041dc006a41386a280200360200200020002902643703f8012000280260210a41002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d032003200a36020420032001360200200320002903f801370208200341106a2002290300370200200341186a2004290300370200200341206a200041f8016a41186a290300370200200341286a2006290300370200200341306a2007290300370200200341386a20092802003602002003200041cc016a36023c200041dc006a2003200041cc016a10b488808000420021080240200028025c2202418180808078460d00410021040340200028026021010240024002402002418080808078470d0041002102024002400240200128020041fcffffff076a2206410320064105491b0e0404040102000b2001280204450d0341002102200141086a28020041002802c0a3c68000118080808000000c030b2001280204450d0241002102200141086a28020041002802c0a3c68000118080808000000c020b200110ca898080000c010b200028026c21062000280268210702402002450d00200141002802c0a3c68000118080808000000b41012102200621012007450d010b200141002802c0a3c68000118080808000000b200041dc006a2003200328023c10b488808000200220046a2104200028025c2202418180808078470d000b2004ad21080b024020032802082202450d00200328020441086a210103402001280200220420042802002204417f6a360200024020044101470d00200110e18a8080000b200141306a21012002417f6a22020d000b0b02402003280200450d00200328020441002802c0a3c68000118080808000000b200341346a2802004129490d00200328020c41002802c0a3c68000118080808000000b41002101200341002802c0a3c6800011808080800000410021030240200028020c2202450d00200020023602742000410036027020002002360264200041003602602000200041106a28020022013602782000200136026841012101200028021421030b2000200336027c2000200136026c2000200136025c200041dc006a10928d80800002402000280200450d00200028020441002802c0a3c68000118080808000000b02402005280200450d00200528020441002802c0a3c68000118080808000000b0240200528020c450d00200528021041002802c0a3c68000118080808000000b02402005280218450d00200528021c41002802c0a3c68000118080808000000b2005280224450d010b200528022841002802c0a3c68000118080808000000b200541002802c0a3c6800011808080800000200041b0026a24808080800020080f0b410441c00010b280808000000be207010a7f2380808080004180026b2201248080808000200141808080807836022c200141b8016a41086a22022001412c6a41086a22032802003602002001200129022c3703b801410021042001410c6a41e5c8c9a903200141b8016a4100280290a2c6800011858080800000200220032802003602002001200129022c3703b801200141386a41e5c8c9a903200141b8016a4100280290a2c6800011858080800000200141d8006a41e5c8c9a9032001412c6a4100280290a2c6800011858080800000200141b8016a41e5c8c9a9034100280298a2c680001184808080000020012802c0012205410574210620012802b80141ffffff3f71210720012802bc01210202400240024002400240034020062004460d01200220046a2103200441206a210420032001410c6a412010888e8080000d000b2005410574210641002104034020062004460d02200220046a2103200441206a21042003200141386a412010888e8080000d000b2005410574210641002104034020062004460d03200220046a2103200441206a21042003200141d8006a412010888e8080000d000b200141b8016a41e5c8c9a9032001410c6a41948ec48000410741002802a0a2c680001187808080000020012d00b801450d03200141f8006a41386a2204200141f1016a290000370300200141f8006a41306a2203200141e9016a290000370300200141f8006a41286a2206200141e1016a290000370300200141f8006a41206a2205200141d9016a290000370300200141f8006a41186a2208200141d1016a290000370300200141f8006a41106a2209200141c9016a290000370300200141f8006a41086a220a200141c1016a290000370300200120012900b901370378200141f8006a41948ec4800041072001410c6a41002802a8a2c6800011898080800000450d04200020012903783700002000200129000c370040200041386a2004290300370000200041306a2003290300370000200041286a2006290300370000200041206a2005290300370000200041186a2008290300370000200041106a2009290300370000200041086a200a290300370000200041c8006a2001410c6a41086a290000370000200041d0006a2001410c6a41106a290000370000200041d8006a2001410c6a41186a29000037000002402007450d00200241002802c0a3c68000118080808000000b20014180026a2480808080000f0b41ec8cc48000412841948dc4800010f880808000000b41a48dc48000412841cc8dc4800010f880808000000b41dc8dc48000412841848ec4800010f880808000000b419b8ec48000412641c48ec4800010a181808000000b41d48ec480004138418c8fc4800010f880808000000be207010a7f2380808080004180026b2201248080808000200141808080807836022c200141b8016a41086a22022001412c6a41086a22032802003602002001200129022c3703b801410021042001410c6a41f3e4c9a903200141b8016a41002802b0a2c6800011858080800000200220032802003602002001200129022c3703b801200141386a41f3e4c9a903200141b8016a41002802b0a2c6800011858080800000200141d8006a41f3e4c9a9032001412c6a41002802b0a2c6800011858080800000200141b8016a41f3e4c9a90341002802b8a2c680001184808080000020012802c0012205410574210620012802b80141ffffff3f71210720012802bc01210202400240024002400240034020062004460d01200220046a2103200441206a210420032001410c6a412010888e8080000d000b2005410574210641002104034020062004460d02200220046a2103200441206a21042003200141386a412010888e8080000d000b2005410574210641002104034020062004460d03200220046a2103200441206a21042003200141d8006a412010888e8080000d000b200141b8016a41f3e4c9a9032001410c6a41cc8fc48000410741002802c0a2c680001187808080000020012d00b801450d03200141f8006a41386a2204200141f1016a290000370300200141f8006a41306a2203200141e9016a290000370300200141f8006a41286a2206200141e1016a290000370300200141f8006a41206a2205200141d9016a290000370300200141f8006a41186a2208200141d1016a290000370300200141f8006a41106a2209200141c9016a290000370300200141f8006a41086a220a200141c1016a290000370300200120012900b901370378200141f8006a41cc8fc4800041072001410c6a41002802c8a2c6800011898080800000450d04200020012903783700002000200129000c370040200041386a2004290300370000200041306a2003290300370000200041286a2006290300370000200041206a2005290300370000200041186a2008290300370000200041106a2009290300370000200041086a200a290300370000200041c8006a2001410c6a41086a290000370000200041d0006a2001410c6a41106a290000370000200041d8006a2001410c6a41186a29000037000002402007450d00200241002802c0a3c68000118080808000000b20014180026a2480808080000f0b41ec8cc480004128419c8fc4800010f880808000000b41a48dc48000412841ac8fc4800010f880808000000b41dc8dc48000412841bc8fc4800010f880808000000b41d38fc48000412641fc8fc4800010a181808000000b418c90c48000413841c490c4800010f880808000000bff0501077f2380808080004180026b22012480808080002001418080808078360228200141b8016a41086a2202200141286a41086a2203280200360200200120012902283703b80141002104200141076a41e5c6919b07200141b8016a41002802f0a1c680001185808080000020022003280200360200200120012902283703b801200141356a41e5c6919b07200141b8016a41002802f0a1c6800011858080800000200141d6006a41e5c6919b07200141286a41002802f0a1c6800011858080800000200141b8016a41e5c6919b0741002802f8a1c680001184808080000020012802c001220541216c210620012802b80141216c210720012802bc01210202400240024002400240034020062004460d01200220046a2103200441216a21042003200141076a412110888e8080000d000b200541216c210641002104034020062004460d02200220046a2103200441216a21042003200141356a412110888e8080000d000b200541216c210641002104034020062004460d03200220046a2103200441216a21042003200141d6006a412110888e8080000d000b200141b8016a41e5c6919b07200141076a418491c4800041054100280280a2c680001187808080000020012d00b801450d03200141f7006a200141b9016a41c10010848e8080001a200141f7006a418491c480004105200141076a4100280288a2c6800011898080800000450d042000200141f7006a41c10010848e808000220441e1006a200141276a2d00003a0000200441d9006a2001411f6a290000370000200441d1006a200141176a290000370000200441c9006a2001410f6a29000037000020042001290007370041024020074121490d00200241002802c0a3c68000118080808000000b20014180026a2480808080000f0b41ec8cc48000412841d490c4800010f880808000000b41a48dc48000412841e490c4800010f880808000000b41dc8dc48000412841f490c4800010f880808000000b418991c48000412441b091c4800010a181808000000b41c091c48000413641f891c4800010f880808000000be50601037f23808080800041e0006b2200248080808000418c92c48000410d418892c48000410441002802e0a1c68000118680808000002000410036022c200041206a418c92c48000410d2000412c6a4104410041002802c8a1c68000118a8080800000200020002802242201360234200020002802202202360230024002400240024002400240024002402002450d0020014104470d00200028022c210220002000412c6a36023c200241f4cacda307470d0120004100360238200041186a418c92c48000410d200041386a4104410441002802c8a1c68000118a80808000002000200028021c220136024020002000280218220236023c2002450d0220010d022000200041386a36024420002802380d0341f092c48000410b41fb92c480004113418892c48000410441002802e8a2c68000118a80808000002000410036022c200041106a41f092c48000410b41fb92c4800041132000412c6a4104410041002802e0a2c68000118b80808000002000200028021422013602342000200028021022023602302002450d0420014104470d04200028022c210220002000412c6a36023c200241f4cacda307470d0520004100360238200041086a41f092c48000410b41fb92c480004113200041386a4104410841002802e0a2c68000118b80808000002000200028020c220136024020002000280208220236023c2002450d0620010d062000200041386a36024420002802380d07200041e0006a2480808080000f0b200041003602484100200041306a419c92c48000200041c8006a41a492c4800010a489808000000b2000410036024841002000413c6a41b492c48000200041c8006a41b892c4800010a589808000000b2000410036024841002000413c6a41ec8ac48000200041c8006a41c892c4800010a489808000000b200041003602484100200041c4006a41dc92c48000200041c8006a41e092c4800010a589808000000b200041003602484100200041306a419c92c48000200041c8006a419093c4800010a489808000000b2000410036024841002000413c6a41b492c48000200041c8006a41a093c4800010a589808000000b2000410036024841002000413c6a41ec8ac48000200041c8006a41b093c4800010a489808000000b200041003602484100200041c4006a41dc92c48000200041c8006a41c093c4800010a589808000000b991701077f23808080800041f0056b2202248080808000200241e0026a41086a2203200041086a280200360200200220002902003703e00220024184026a200241e0026a109a8d808000200241e0026a10a68d808000200241e0026a41306a20024184026a41306a290200370300200241e0026a41286a20024184026a41286a290200370300200241e0026a41206a20024184026a41206a290200370300200241e0026a41186a20024184026a41186a290200370300200241e0026a41106a20024184026a41106a290200370300200320024184026a41086a290200370300200241a0036a200141086a290000370300200241a8036a200141106a290000370300200241b0036a200141186a29000037030020022002290284023703e0022002200129000037039803200241b8036a41b89ec3800041014100280298a3c68000118580808000002002200241e0026a41f80010848e8080002202428080808080808080807f37027c200241003b0178200241d4046a41003a0000200241d0046a41003602002002418c046a420037020020024180046a4200370300200241b4056a4100360200200241a8056a41003a0000200241a4056a4100360200200241e0046a4200370300200241003a00b80520024280808080c0003702ac05200241003602d804200241003602f8032002420037039803200241003602900320024200370388032002410036028003200242003703f802200241003602f002200242003703e802200241003602e00220024180808080783602a003200241003b01cc05200220023602c8052002200241e0026a3602c40520024184026a200241c4056a41d093c48000410610df8680800002400240024002402002280284022200418080808078460d0002402000450d0020022802880241002802c0a3c68000118080808000000b20024184026a200241e0026a20024101109088808000200241d0056a41186a220320024184026a41186a290000370300200241d0056a41106a220420024184026a41106a290000370300200241d0056a41086a220520024184026a41086a29000037030020022002290084023703d00541002d00fca3c680001a2002410036028c022002428080808010370284020240412041002802c8a3c68000118180808000002200450d00200020022903d005370000200041186a22062003290300370000200041106a22072004290300370000200041086a2208200529030037000020024184026a4100412010b1828080002002280288022204200228028c0222056a22032000290000370000200341086a2008290000370000200341106a2007290000370000200341186a2006290000370000200041002802c0a3c680001180808080000020022802840221000240024020050d0020042001412010888e808000450d010b02402000450d00200441002802c0a3c68000118080808000000b41d693c4800041ce0041a494c4800010f880808000000b02402000450d00200441002802c0a3c68000118080808000000b41002d00fca3c680001a410141002802c8a3c68000118180808000002200450d02200041003a0000200241013602d805200220003602d405200241013602d00541002d00fca3c680001a410141002802c8a3c68000118180808000002200450d03200041013a00002002410136028c0220022000360288022002410136028402200241e0026a200241d0056a20024184026a108f8880800020024184026a200241e0026a20024101109088808000200241d0056a41186a220320024184026a41186a290000370300200241d0056a41106a220420024184026a41106a290000370300200241d0056a41086a220520024184026a41086a29000037030020022002290084023703d00541002d00fca3c680001a2002410036028c02200242808080801037028402412041002802c8a3c68000118180808000002200450d04200020022903d005370000200041186a22062003290300370000200041106a22072004290300370000200041086a2204200529030037000020024184026a4100412010b1828080002002280288022205200228028c0222086a22032000290000370000200341086a2004290000370000200341106a2007290000370000200341186a2006290000370000200041002802c0a3c68000118080808000002002280284022100024020080d0020052001412010888e8080000d0002402000450d00200541002802c0a3c68000118080808000000b41b494c4800041ce00418495c4800010f880808000000b20024184046a210102402000450d00200541002802c0a3c68000118080808000000b200241d8046a2103200241f8036a2104200110a38d80800002400240200228029004220141054b0d002001450d01200241e0026a41b4016a21000340200010a68d8080002000410c6a21002001417f6a22010d000c020b0b200241e0026a41b4016a2802002105024020024198046a2802002201450d00200521000340200010a68d8080002000410c6a21002001417f6a22010d000b0b200541002802c0a3c68000118080808000000b200410a28d808000200310a18d8080000240024020022802e404220141054b0d002001450d01200241e0026a4188026a21000340200010a78d8080002000410c6a21002001417f6a22010d000c020b0b200241e0026a4188026a28020021030240200241ec046a2802002201450d00200321000340200010a78d8080002000410c6a21002001417f6a22010d000b0b200341002802c0a3c68000118080808000000b024020022802b4052203450d0020022802b00521052003410171210641002101024020034101460d00200541146a21002003417e7121044100210103400240200041706a2000416c6a22032003280200418080808078461b2203280200450d00200328020441002802c0a3c68000118080808000000b0240200041046a20002000280200418080808078461b2203280200450d00200328020441002802c0a3c68000118080808000000b200041286a21002004200141026a2201470d000b0b2006450d002005200141146c6a220020002802004180808080784622014102746a280200450d00200041046a200020011b28020441002802c0a3c68000118080808000000b024020022802ac05450d0020022802b00541002802c0a3c68000118080808000000b024020022802a003418080808078460d0002400240200241ac036a28020022000d0041002100410021010c010b2002200036029c0220024100360298022002200036028c0220024100360288022002200241b0036a28020022003602a0022002200036029002200241b4036a2802002101410121000b200220013602a4022002200036029402200220003602840220024184026a10928d80800020022802a003450d00200241a4036a28020041002802c0a3c68000118080808000000b2002410c6a10a58d80800002402002280200450d00200228020441002802c0a3c68000118080808000000b02402002280280012200418080808078460d0002402000450d0020024184016a28020041002802c0a3c68000118080808000000b0240200241c8016a2802002200418080808078460d002000450d00200241cc016a28020041002802c0a3c68000118080808000000b0240200241d4016a2802002200418080808078460d002000450d00200241d8016a28020041002802c0a3c68000118080808000000b024020024194016a2802002201450d0020024190016a28020041086a210003402000280200220320032802002203417f6a360200024020034101470d00200010e18a8080000b200041306a21002001417f6a22010d000b0b0240200228028c01450d0020022802900141002802c0a3c68000118080808000000b200241c0016a2802004129490d0020024198016a28020041002802c0a3c68000118080808000000b200241f0056a2480808080000f0b4101412010b280808000000b41808080807820022802880210cb89808000419495c48000413241c895c4800010f880808000000b4101410110b280808000000b4101410110b280808000000b4101412010b280808000000b850201057f23808080800041306b220324808080800020032000280208220436020c2003200028020422053602082003200128020822063602142003200128020422073602100240024020042006470d0020052007200410888e8080000d00200320043602082003200236021020042002470d0102402001280200450d00200741002802c0a3c68000118080808000000b02402000280200450d00200541002802c0a3c68000118080808000000b200341306a2480808080000f0b200341003602184100200341086a200341106a200341186a419ca2c4800010a689808000000b200341003602184100200341086a200341106a200341186a41aca2c4800010a789808000000bce0e020c7f037e23808080800041e0016b220024808080800002404100280284a4c680004105470d004100280290a1c680002101410028028ca1c6800021024100280280a4c680002103200041c0016a4200370200200041bc016a41e8d1c38000360200200041b8016a4101360200200041b0016a4104360200200041ac016a418892c48000360200200041a0016a41f48ac48000ad4280808080900c8437020020004194016a41fe8bc48000ad4280808080e00284370200200041cca2c480003602b401200041053602a8012000410036029c01200041003602900120004281808080e0ca0037028801200241ecf2c08000200341024622031b20004188016a200141d4f2c0800020031b280210118480808000000b2000410f360210200041d4a2c4800036020c02400240024002400240024041002802f8a3c680000d0002400240024041002d00e8a3c680000e03030102000b41e0a3c6800010c58d80800041ff01710e03020001000b41002802e0a3c680002103024002404100280294a4c680004102460d0041988dc68000210241e48dc6800021010c010b41002802f4a3c68000210241002802f0a3c68000210141002802eca3c68000450d002002280208417f6a41787120016a41086a21010b20012003200228021411838080800000450d010b41002802e0a3c68000220141206a28020022020d0141e3a2c4800041224188a3c4800010a181808000000b41002d0090a4c680000d044100280284a4c680004105470d0441002802e0a3c6800021012000410536021420002001290214370218410028028ca1c6800041ecf2c080004100280280a4c6800041024622021b2204200041146a4100280290a1c6800041d4f2c0800020021b220528020c11838080800000450d0441002802e0a3c68000220241206a2802002203450d01200028021c21062000280218210720002802142108200241286a2802002109200241246a280200210a200228021c210b20004100360248200020093602442000200a3602402000200b3602382000200336023c200041cc006a410c6a4200370200200041a8a3c4800036024c200041e8d1c380003602542000410136025020034101460d02200041346a41c0a3c4800036020020004101360274200020093602702000200a36026c200020033602682000200b360264200041b0a3c4800036022820002002411c6a36028401200041306a200041f8006a3602002000200041e4006a36022c2000200041cc006a3602242000200041386a3602202000200041206a36027c20002000410c6a36027820004102360280012001290200210c20004188016a41386a420137020020004188016a41306a4101360200200041b0016a2006360200200041ac016a2007360200200041a0016a200141386a3502004220862001350234220d8437020020004188016a410c6a200141306a350200422086200135022c220e84370200200041bc016a200041d0016a3602002000418c89c680003602b401200020083602a801200041a0838080003602d401200041013a00dc01200041024101200d501b36029c01200041024101200e501b360290012000200041d8016a3602d0012000200041fc006a3602d8012000200c37028801200420004188016a2005280210118480808000000c040b200141286a2802002103200141246a2802002104200128021c2105200041003602482000200336024420002004360240200020053602382000200236023c200041d8006a4200370200200041a8a3c4800036024c200041e8d1c380003602542000410136025020024101460d02200041346a41c0a3c4800036020020004101360274200020033602702000200436026c2000200236026820002005360264200041b0a3c4800036022820002001411c6a36021c200041306a200041d8016a3602002000200041e4006a36022c2000200041cc006a3602242000200041386a3602202000200041206a36021420002000410c6a3602d801200041023602182000200136029c0120004201370388014100280294a4c6800021012000200041146a36029801024020014102470d0041002802f4a3c68000210241002802f0a3c680002101024041002802eca3c68000450d002002280208417f6a41787120016a41086a21010b200120004188016a200228022811838080800000450d00200120004188016a200228022c118480808000000b41002d0090a4c680000d034100280284a4c680004105470d0341002802e0a3c6800021012000410536027c2000200129021437028001410028028ca1c6800041ecf2c080004100280280a4c6800041024622021b2203200041fc006a4100280290a1c6800041d4f2c0800020021b220228020c11838080800000450d0320004188016a41086a200041fc006a41086a2802003602002000200029027c3703880120012003200220004188016a200041146a10c08d8080000c030b41e3a2c4800041224188a3c4800010a181808000000b41e3a2c4800041224188a3c4800010a181808000000b41e3a2c4800041224188a3c4800010a181808000000b200041e0016a2480808080000ba80605027f027e087f017e017f23808080800041d0006b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd20370300200142c2a5b2f6d3b4fb986f370318200142dcd7ddd8f18e8ca1e300370310200141306a200110e88880800020012d004021022001290330210320012903382104200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd2037030020014282fceae49dd9e2861d370318200142de8c84a1ecd0a6d30c370310200141306a200110e288808000024002400240024020012802302205418080808078470d00410021064108210741082108410021050c010b200128023421080240200128023822060d0041002106410821070c010b200641b3e6cc194b0d01200641286c2209417f4c0d014100210a41002d00fca3c680001a200941002802c8a3c68000118180808000002207450d022006210b2008210c03402009200a460d01200c290320210d2007200a6a220e200c290300370300200e41186a200c41186a290300370300200e41106a200c41106a290300370300200e41086a200c41086a290300370300200e41206a200d370300200a41286a210a200c41286a210c200b417f6a220b0d000b0b200142919fd78da1a1ad84ff003703382001429ceccef7a6c0dedd20370330200142c6e4a9b1aaa1afebf200370348200142fa82b1828b81b8f31e37034042032003200241ff0171410346220c1b210d420a2004200c1b210341012002200c1b210e200041206a210c2001200141306a10e5888080000240024020012d00000d00200c4200370300200c41186a4200370300200c41106a4200370300200c41086a42003703000c010b200c2001290001370000200c41186a200141196a290000370000200c41106a200141116a290000370000200c41086a200141096a2900003700000b2000200e3a004c200020063602402000200d37031020004206370308200042e807370300200041c8006a2006360200200041c4006a2007360200200041186a200337030002402005450d00200841002802c0a3c68000118080808000000b200141d0006a2480808080000f0b10ae80808000000b4108200910b280808000000b940301047f02402001280200450d00200128020441002802c0a3c68000118080808000000b20004194016a2802002102024020004198016a2802002203450d00200221014100210403402002200441146c6a21050240024002400240024020012d00000e0400010102040b200141086a21050c020b200541086a21050c010b200541046a21050b2005280200450d00200528020441002802c0a3c68000118080808000000b200441016a2104200141146a21012003417f6a22030d000b0b0240200028029001450d00200241002802c0a3c68000118080808000000b2000418c026a2802002102024020004190026a2802002203450d00200221014100210403402002200441146c6a21050240024002400240024020012d00000e0400010102040b200141086a21050c020b200541086a21050c010b200541046a21050b2005280200450d00200528020441002802c0a3c68000118080808000000b200441016a2104200141146a21012003417f6a22030d000b0b0240200028028802450d00200241002802c0a3c68000118080808000000b41000bad0201037f2380808080004180026b220124808080800041002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200242f3deb5abf6ebdab2f90037000041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0120032000290320370000200141c8016a41023a00002001410836021420012003360210200141083602182001200236021c20014108360220200142838080808001370308200141f0016a200141086a10f78c808000200141f0016a41002802d8a3c6800011818080800000450d024184a4c48000412b200141ff016a41b0a4c4800041c0a4c48000108981808000000b4101410810b280808000000b4101410810b280808000000b200141086a10c98980800020014180026a2480808080000b040041010bc80301057f23808080800041206b22022480808080000240024020012d00000d00410221030c010b200241186a200141196a290000370300200241106a200141116a290000370300200241086a200141096a29000037030020022001290001370300410121030b41002d00fca3c680001a02400240411041002802c8a3c68000118180808000002201450d0020014281808080103702002001410c6a41c8a6c480003602002001410136020841002d00fca3c680001a411041002802c8a3c68000118180808000002204450d01200441b0a6c4800036020c20044101360208200442818080801037020020012001280200417f6a2205360200024020050d00200128020822062001410c6a28020022052802001180808080000002402005280204450d00200641002802c0a3c68000118080808000000b20012001280204417f6a220536020420050d00200141002802c0a3c68000118080808000000b200020033a000420002004360200200020022903003700052000410d6a200241086a290300370000200041156a200241106a2903003700002000411d6a200241186a290300370000200241206a2480808080000f0b4104411010b280808000000b4104411010b280808000000ba90a01077f23808080800041f0026b22032480808080000240024020022d0004450d002002280200220441086a28020020012004410c6a280200280214118380808000000d00200041206a410610dc88808000200041003a00182000420037030020042004280200417f6a2202360200024020020d00200428020822002004410c6a28020022022802001180808080000002402002280204450d00200041002802c0a3c68000118080808000000b200441046a22022002280200417f6a220236020020020d00200441002802c0a3c68000118080808000000b200110c9898080000c010b200128020021042003410c6a200141046a220541d40010848e8080001a0240024002400240200441736a2201410220014104491b0e0400010203000b200341a8016a200341306a290200370300200341a0016a200341286a29020037030020034198016a200341206a29020037030020034190016a200341186a290200370300200341e0006a41086a200241086a290200370300200341e0006a41106a200241106a290200370300200341e0006a41186a200241186a290200370300200341e0006a41206a200241206a290200370300200320032902103703880120032002290200370360200341003602e802200341003602e0022003200341e0026a3602ec02200341d8016a200341e0006a41d00010848e8080001a2003200341ec026a3602a802200041f0a0c68000200341d8016a10c187808000200341e0026a10a98d8080000c030b200341e0006a41286a22012003290210370300200341e0006a41386a2204200341206a290200370300200341e0006a41306a2205200341186a290200370300200341e0006a41086a2206200241086a290200370300200341e0006a41106a2207200241106a290200370300200341e0006a41186a2208200241186a290200370300200341e0006a41206a2209200241206a29020037030020032002290200370360200341003602e802200341003602e0022003200341e0026a3602ec02200341d8016a41386a2004290300370300200341d8016a41306a2005290300370300200341d8016a41286a2001290300370300200341d8016a41206a2009290300370300200341d8016a41186a2008290300370300200341d8016a41106a2007290300370300200341d8016a41086a2006290300370300200320032903603703d8012003200341ec026a36029802200041f0a0c68000200341d8016a10c287808000200341e0026a10a98d8080000c020b200341d8016a410472200541d40010848e8080001a200341d0026a200241206a290200370300200341c8026a200241186a290200370300200341c0026a200241106a290200370300200341b8026a200241086a290200370300200320022902003703b0022003410036026820034100360260200320043602d8012003200341e0026a3602d8022003200341e0006a3602e002200041f0a0c68000200341d8016a10c387808000200341e0006a10a98d8080000c010b20034188016a2003410c6a41046a41d00010848e8080001a200341e0006a41206a200241206a290200370300200341e0006a41186a200241186a290200370300200341e0006a41106a200241106a290200370300200341e0006a41086a200241086a29020037030020032002290200370360200341003602e802200341003602e0022003200341e0026a3602ec02200341d8016a200341e0006a41f80010848e8080001a2003200341ec026a3602d002200041f0a0c68000200341d8016a10c587808000200341e0026a10a98d8080000b200341f0026a2480808080000bcc0303027f017e027f23808080800041d0006b22012480808080002001412c6a41eba7c48000410b41fe8bc48000411641e8d1c38000410010d882808000200141086a41086a2202410036020020014280808080c00037030820012902302103200128022c21042001410036021c20014280808080c000370214200141c4006a200141146a41f9a6c48000410610c98b808000200141146a200141c4006a41f5a6c48000410410948b808000200141c4006a200141146a41e8a6c48000410d10c88b808000200141386a200141c4006a41e0a6c48000410810818b8080002001200128023836021c2001200128023c220536021420012005200128024041246c6a36022020012005360218200141c4006a200141146a10fb8680800002402004418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a4100360200200141146a410b6a200141c4006a41086a2802003600002000413c6a200337020020002004360238200041013a000020002001290308370350200041d8006a20022802003602002001200129024437001720002001290014370001200041086a2001411b6a290000370000200141d0006a2480808080000bbd0203027f017e017f23808080800041d0006b2201248080808000200141286a418ea8c48000410741fe8bc48000411641e8d1c38000410010d882808000200141086a2202410036020020014280808080c000370300200129022c210320012802282104200142808080808001370218200142888080808001370210200141346a200141106a10f98680800002402004418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a4100360200200141cc006a200141346a41086a2802003600002000413c6a200337020020002004360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437004420002001290041370001200041086a200141c1006a41076a290000370000200141d0006a2480808080000b970303027f017e027f23808080800041d0006b22012480808080002001412c6a4182a8c48000410c41fe8bc48000411641e8d1c38000410010d882808000200141086a41086a2202410036020020014280808080c00037030820012902302103200128022c21042001410036021c20014280808080c000370214200141c4006a200141146a41f9a6c480004106109c8b808000200141386a200141c4006a41e0a6c48000410810a58b8080002001200128023836021c2001200128023c220536021420012005200128024041246c6a36022020012005360218200141c4006a200141146a10fb8680800002402004418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a41003602002001411f6a200141c4006a41086a2802003600002000413c6a200337020020002004360238200041013a000020002001290308370350200041d8006a20022802003602002001200129024437001720002001290014370001200041086a2001411b6a290000370000200141d0006a2480808080000baf0303027f017e027f23808080800041d0006b22012480808080002001412c6a41f6a7c48000410c41fe8bc48000411641e8d1c38000410010d882808000200141086a41086a2202410036020020014280808080c00037030820012902302103200128022c21042001410036021c20014280808080c000370214200141c4006a200141146a41f9a6c48000410610c38b808000200141146a200141c4006a41f5a6c48000410410d28b808000200141386a200141146a41e0a6c48000410810b68b8080002001200128023836021c2001200128023c220536021420012005200128024041246c6a36022020012005360218200141c4006a200141146a10fb8680800002402004418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a41003602002001411f6a200141c4006a41086a2802003600002000413c6a200337020020002004360238200041013a000020002001290308370350200041d8006a20022802003602002001200129024437001720002001290014370001200041086a2001411b6a290000370000200141d0006a2480808080000baf0201057f23808080800041206b2201248080808000200041c0006a108c8a808000200010ac878080002000412c6a28020021020240024002400240200041306a28020022030d0041012104410021050c010b200341ffffff1f4b0d0120034105742205417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002204450d020b20042002200510848e8080002105200142f0fe93e98686d7ab8c7f37030820014280eee1b0e3b7afe9987f37030020014282fceae49dd9e2861d370318200142de8c84a1ecd0a6d30c370310200520032001412010cd8780800002402003450d00200541002802c0a3c68000118080808000000b200041346a10988c80800010928a808000200141206a2480808080000f0b10ae80808000000b4101200510b280808000000bc30301027f23808080800041d0006b2203248080808000024002400240024002400240200228020041736a2204410220044104491b0e03010203000b200041808080807836021020004181023b01000c030b20002001200241086a10b98a8080000c020b0240200241186a2d00004104470d00200141ff017141014d0d030240417f4100280284a4c680002202410247200241024b1b2202417f460d00200241ff01710d010b4100280290a1c680002102410028028ca1c6800021044100280280a4c680002101200341c4006a4200370200200341c0006a41e4e7c280003602002003413c6a4101360200200341346a410d360200200341306a41c6e8c28000360200200341246a41e4e7c28000ad4280808080a00c84370200200341186a41d3e8c28000ad4280808080900384370200200341dce7c28000360238200341003602202003410036021420034281808080b01a37020c2003410236022c200441ecf2c08000200141024622011b2003410c6a200241d4f2c0800020011b280210118480808000000b2000418080808078360210200041003b01000c010b20002001200210eb878080000b200341d0006a2480808080000f0b200228020810b087808000000bb80203027f017e017f23808080800041c0006b2201248080808000200141286a41ffa6c48000411141fe8bc48000411641e8d1c38000410010d882808000200141086a2202410036020020014280808080c000370300200129022c21032001280228210420014284808080c00037021020014280808080c000370218200141346a200141106a10fb8680800002402004418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a41003602002001411b6a200141346a41086a2802003600002000413c6a200337020020002004360238200041013a000020002001290300370350200041d8006a20022802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141c0006a2480808080000bc20301037f23808080800041106b220224808080800002402001280200220328020020032802082204470d0020032004410110ab86808000200328020821040b200328020420046a41fb003a00002003200441016a3602082002200136020c20024180023b0108024002400240200241086a4190a7c480004106200041c0006a10da8680800022030d00024020022d0008450d0041002d00fca3c680001a411441002802c8a3c68000118180808000002203450d022003420037020c2003410d3602000c010b200241086a4196a7c480004104200010db8680800022030d00024020022d0008450d0041002d00fca3c680001a411441002802c8a3c68000118180808000002203450d032003420037020c2003410d3602000c010b200241086a419aa7c48000410d200041286a10dc8680800022030d00024020022d0008450d0010868380800021030c010b200241086a41a7a7c480004108200041346a10d58680800022030d0002402002280208220341ff01710d0020034180fe0371450d00200228020c28020041b8d0c3800041011083878080000b410021030b200241106a24808080800020030f0b4104411410b280808000000b4104411410b280808000000bbf010002400240024002400240024002402002417c6a0e0a00050205030505050501050b200128000041e2c289ab06470d04200041013a00010c030b2001419aa7c48000410d10888e8080000d03200041023a00010c020b20014190a7c48000410610888e808000450d030c020b200129000042e2c2b18be6edd8b2f300520d01200041033a00010b200041003a00000f0b20002001200241b0a7c48000410410e58a808000360204200041013a00000f0b200041003a0001200041003a00000bb20602037f047e2380808080004180026b22022480808080000240024002400240024002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020020042d00000e0401020305040b200041003602000c050b200241e0006a200110a68a8080000240024020022d0060450d00200241b0016a41206a200241e0006a41206a2903002205370300200241b0016a41186a200241e0006a41186a2903002206370300200241b0016a41106a200241e0006a41106a2903002207370300200241b0016a41086a200241e0006a41086a2903002208370300200241086a410c6a2008370200200241086a41146a2007370200200241086a411c6a2006370200200241086a41246a20053702002002200229036022053703b0012002200537020c200020022902083702042000410c6a200241086a41086a290200370200200041146a200241086a41106a2902003702002000411c6a200241086a41186a290200370200200041246a200241086a41206a2902003702002000412c6a200241306a280200360200410d21030c010b410021030b200020033602000c040b200241e0006a200110ba87808000024020022d00704105460d00200241b0016a41106a200241e0006a41106a2903002205370300200241b0016a41086a200241e0006a41086a2903002206370300200241086a410c6a2006370200200241086a41146a20053702002002200229036022053703b0012002200537020c200020022902083702042000410c6a200241086a41086a290200370200200041146a200241086a41106a2902003702002000411c6a200241206a2802003602002000410e3602000c040b200041003602000c030b200241086a200110c08880800002402002280208450d002000200241086a41d80010848e8080001a0c030b200041003602000c020b200041003602000c010b200241e0006a200110a58c808000024020022d0060450d00200241b0016a200241e0006a41d00010848e8080001a200241086a41046a200241b0016a41d00010848e8080001a200041046a200241086a41d40010848e8080001a200041103602000c010b200041003602000b20024180026a2480808080000b2100200128021441d895c48000410f200141186a28020028020c118280808000000be702020c7f017e23808080800041106b220224808080800020002802082203415c6a21042000280204210520002d0001210620002d0000210703400240024002400240024020070e03010200010b20054110200541104b1b21082003200541246c22096a2100200420096a2109200541087441807e6a210a034020082005460d03200941246a2109200a4180026a210a200541016a210520002d0000210b200041246a2100200b4102460d000b200a4180fe0371410172210c2009210d0c030b200c41807e71410272210c0c020b200c41807e71210c0240200641ff01710d00410121062005210d0c020b200c410272210c0c010b200c41807e71410272210c200821050b0240200c41ff01714102460d00200d2d0000450d01200d280204210020022001360204200cad220e42ff01834202510d0120022000ad422086200e42ffff038384370308200241046a200241086a10bd8c8080000c010b0b200241106a2480808080000be702020c7f017e23808080800041106b220224808080800020002802082203415c6a21042000280204210520002d0001210620002d0000210703400240024002400240024020070e03010200010b20054110200541104b1b21082003200541246c22096a2100200420096a2109200541087441807e6a210a034020082005460d03200941246a2109200a4180026a210a200541016a210520002d0000210b200041246a2100200b4102460d000b200a4180fe0371410172210c2009210d0c030b200c41807e71410272210c0c020b200c41807e71210c0240200641ff01710d00410121062005210d0c020b200c410272210c0c010b200c41807e71410272210c200821050b0240200c41ff01714102460d00200d2d0000450d01200d280204210020022001360204200cad220e42ff01834202510d0120022000ad422086200e42ffff038384370308200241046a200241086a10bc8c8080000c010b0b200241106a2480808080000bcc0101067f23808080800041206b2202248080808000200141086a280200220320012802042204200320044b1b210520012802002103200128020c2106024002400240034020052004460d022001200441016a2204360204200241086a200310bc8a80800020022802080d01200241146a2003200228020c10d08580800020022802142207418080808078460d012007418180808078460d000b20002002290218370204200020073602000c020b200641013a00000b20004180808080783602000b200241206a2480808080000bef0601107f2380808080004190016b220124808080800002402000280200220220002802042203460d0020002802102104200028020822054110200541104b1b2106200028020c200541246c6a2107200141ec006a41086a2108200141ec006a41017221090240034020002002410c6a220a3602004102210b024020022802004102460d00200141ec006a200210838a8080004102210b200141c4006a41026a200941026a2d00003a0000200141286a41086a2202200841086a290200370300200141286a41106a220c200841106a290200370300200141286a41186a220d200841186a280200360200200120092f00003b0144200120082902003703282001280270210e024020012d006c220f417e6a0e020301000b200141246a41026a200141c4006a41026a2d00003a0000200141086a41086a2002290300370300200141086a41106a200c290300370300200141086a41186a200d280200360200200120012f01443b012420012001290328370308200f210b0b200141e8006a41026a2202200141246a41026a2d00003a0000200141c8006a41086a220c200141086a41086a290300370300200141c8006a41106a220d200141086a41106a290300370300200141c8006a41186a220f200141086a41186a280200360200200120012f01243b01682001200129030837034802400240024020062005460d0020072d00000e03020102010b2006411041fcc8c4800010f980808000000b200741046a280200221010848a808000201041002802c0a3c68000118080808000000b2007200b3a0000200741016a20012f01683b0000200741046a200e360200200741086a2001290348370200200741036a20022d00003a0000200741106a200c290300370200200741186a200d290300370200200741206a200f2802003602002000200541016a2205360208200741246a2107200a2102200a2003470d000c020b0b024020042802002207450d000240024002400240200728020041fcffffff076a2202410320024105491b0e0403030102000b2007280204450d02200741086a28020041002802c0a3c68000118080808000000c020b2007280204450d01200741086a28020041002802c0a3c68000118080808000000c010b200710858a8080000b200741002802c0a3c68000118080808000000b2004200e3602002000200541016a3602080b20014190016a2480808080000baa0801057f23808080800041800e6b22022480808080000240024002400240024002400240024002400240024002400240024020012802000d0020012802042103200128020822040d01410121010c020b200241e0016a200128020422042001280208220110c28c80800020022802e00122034105470d05200241046a410c6a200241e0016a410c6a290200370200200220022902e4013702080c060b20044120460d012004417f4c0d0741002d00fca3c680001a200441002802c8a3c68000118180808000002201450d080b20012003200410848e808000210341002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b200020032f00003b00012000200328000336020420002003290007370008200041036a200341026a2d00003a0000200041106a2003410f6a290000370000200041186a200341176a290000370000200041206a2003411f6a2d00003a0000410021010c010b200142003702102001200436020c2001200336020820012004360204200141888080807836020020002001360204200141186a4200370200200141206a4200370200200141286a4200370200410221010b200020013a00000c070b200241f00d6a41086a2205200241e0016a410c6a290200370300200220022902e4013703f00d200241940c6a41146a200241e0016a41146a41c80110848e8080001a200241940c6a410c6a2005290300370200200220033602940c200220022903f00d3702980c200241046a200241940c6a2004200110f48d80800020022802044105470d010b200241e0016a41186a22044200370300200241e0016a41206a22034200370300200241e0016a41286a22054200370300200241e0016a41086a2206200241106a290200370300200220022902083703e001200242003703f00141002d00fca3c680001a413041002802c8a3c68000118180808000002201450d03200120022903e001370200200141286a2005290300370200200141206a2003290300370200200141186a2004290300370200200141106a200241e0016a41106a290300370200200141086a2006290300370200200041023a0000200020013602040c050b200241940c6a200241046a41dc0110848e8080001a200241e0016a200241940c6a10b68a808000024020022802e00122044108460d0020022802e401210320024180076a200241e0016a41086a41940510848e8080001a41002d00fca3c680001a419c0541002802c8a3c68000118180808000002201450d042001200336020420012004360200200141086a20024180076a41940510848e8080001a20002001360204200041013a00000c050b20022802e4012101200041023a0000200020013602040c040b10ae80808000000b4101200410b280808000000b4104413010b280808000000b4104419c0510b280808000000b200241800e6a2480808080000be11101027f0240024002400240024002402000280200417e6a2201410420014106491b0e050501020304000b2000280204220120012802002201417f6a36020020014101470d04200041046a10e28a8080000c040b02402000412c6a2802004129490d00200028020441002802c0a3c68000118080808000000b20002802342201450d03200120012802002202417f6a36020020024101470d03200041346a10e28a8080000f0b0240200041d0006a2802004129490d00200028022841002802c0a3c68000118080808000000b20002d0004450d02200041086a280200220010848a808000200041002802c0a3c68000118080808000000f0b024020002d003022014102460d002001450d00200041346a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041d4006a2d000022014102460d002001450d00200041d8006a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041f8006a2d000022014102460d002001450d00200041fc006a280200220110848a808000200141002802c0a3c68000118080808000000b02402000419c016a2d000022014102460d002001450d00200041a0016a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041c0016a2d000022014102460d002001450d00200041c4016a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041e4016a2d000022014102460d002001450d00200041e8016a280200220110848a808000200141002802c0a3c68000118080808000000b024020004188026a2d000022014102460d002001450d002000418c026a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041ac026a2d000022014102460d002001450d00200041b0026a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041d0026a2d000022014102460d002001450d00200041d4026a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041f4026a2d000022014102460d002001450d00200041f8026a280200220110848a808000200141002802c0a3c68000118080808000000b024020004198036a2d000022014102460d002001450d002000419c036a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041bc036a2d000022014102460d002001450d00200041c0036a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041e0036a2d000022014102460d002001450d00200041e4036a280200220110848a808000200141002802c0a3c68000118080808000000b024020004184046a2d000022014102460d002001450d0020004188046a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041a8046a2d000022014102460d002001450d00200041ac046a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041cc046a2d000022014102460d002001450d00200041d0046a280200220110848a808000200141002802c0a3c68000118080808000000b2000280204450d01200041086a22012802002200450d01200020002802002202417f6a36020020024101470d01200110e28a8080000f0b024020004194056a2802004129490d0020002802ec0441002802c0a3c68000118080808000000b024020002d002c22014102460d002001450d00200041306a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041d0006a2d000022014102460d002001450d00200041d4006a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041f4006a2d000022014102460d002001450d00200041f8006a280200220110848a808000200141002802c0a3c68000118080808000000b024020004198016a2d000022014102460d002001450d002000419c016a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041bc016a2d000022014102460d002001450d00200041c0016a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041e0016a2d000022014102460d002001450d00200041e4016a280200220110848a808000200141002802c0a3c68000118080808000000b024020004184026a2d000022014102460d002001450d0020004188026a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041a8026a2d000022014102460d002001450d00200041ac026a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041cc026a2d000022014102460d002001450d00200041d0026a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041f0026a2d000022014102460d002001450d00200041f4026a280200220110848a808000200141002802c0a3c68000118080808000000b024020004194036a2d000022014102460d002001450d0020004198036a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041b8036a2d000022014102460d002001450d00200041bc036a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041dc036a2d000022014102460d002001450d00200041e0036a280200220110848a808000200141002802c0a3c68000118080808000000b024020004180046a2d000022014102460d002001450d0020004184046a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041a4046a2d000022014102460d002001450d00200041a8046a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041c8046a2d000022014102460d002001450d00200041cc046a280200220110848a808000200141002802c0a3c68000118080808000000b2000280200450d0020002802042201450d00200120012802002202417f6a36020020024101470d00200041046a10e28a8080000f0b0bc50101027f024002400240024020002802002201418080808078732202410220024104491b0e03030301000b0240024002402000280204220028020041fcffffff076a2202410320024105491b0e0404040102000b2000280204450d03200041086a28020041002802c0a3c68000118080808000000c030b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b200010858a8080000c010b2001450d01200028020421000b200041002802c0a3c68000118080808000000b0bef0601107f2380808080004190016b220124808080800002402000280200220220002802042203460d0020002802102104200028020822054110200541104b1b2106200028020c200541246c6a2107200141ec006a41086a2108200141ec006a41017221090240034020002002410c6a220a3602004102210b024020022802004102460d00200141ec006a200210878a8080004102210b200141c4006a41026a200941026a2d00003a0000200141286a41086a2202200841086a290200370300200141286a41106a220c200841106a290200370300200141286a41186a220d200841186a280200360200200120092f00003b0144200120082902003703282001280270210e024020012d006c220f417e6a0e020301000b200141246a41026a200141c4006a41026a2d00003a0000200141086a41086a2002290300370300200141086a41106a200c290300370300200141086a41186a200d280200360200200120012f01443b012420012001290328370308200f210b0b200141e8006a41026a2202200141246a41026a2d00003a0000200141c8006a41086a220c200141086a41086a290300370300200141c8006a41106a220d200141086a41106a290300370300200141c8006a41186a220f200141086a41186a280200360200200120012f01243b01682001200129030837034802400240024020062005460d0020072d00000e03020102010b2006411041ecc8c4800010f980808000000b200741046a280200221010848a808000201041002802c0a3c68000118080808000000b2007200b3a0000200741016a20012f01683b0000200741046a200e360200200741086a2001290348370200200741036a20022d00003a0000200741106a200c290300370200200741186a200d290300370200200741206a200f2802003602002000200541016a2205360208200741246a2107200a2102200a2003470d000c020b0b024020042802002207450d000240024002400240200728020041fcffffff076a2202410320024105491b0e0403030102000b2007280204450d02200741086a28020041002802c0a3c68000118080808000000c020b2007280204450d01200741086a28020041002802c0a3c68000118080808000000c010b200710858a8080000b200741002802c0a3c68000118080808000000b2004200e3602002000200541016a3602080b20014190016a2480808080000baa0801057f23808080800041800e6b22022480808080000240024002400240024002400240024002400240024002400240024020012802000d0020012802042103200128020822040d01410121010c020b200241e0016a200128020422042001280208220110c28c80800020022802e00122034105470d05200241046a410c6a200241e0016a410c6a290200370200200220022902e4013702080c060b20044120460d012004417f4c0d0741002d00fca3c680001a200441002802c8a3c68000118180808000002201450d080b20012003200410848e808000210341002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b200020032f00003b00012000200328000336020420002003290007370008200041036a200341026a2d00003a0000200041106a2003410f6a290000370000200041186a200341176a290000370000200041206a2003411f6a2d00003a0000410021010c010b200142003702102001200436020c2001200336020820012004360204200141888080807836020020002001360204200141186a4200370200200141206a4200370200200141286a4200370200410221010b200020013a00000c070b200241f00d6a41086a2205200241e0016a410c6a290200370300200220022902e4013703f00d200241940c6a41146a200241e0016a41146a41c80110848e8080001a200241940c6a410c6a2005290300370200200220033602940c200220022903f00d3702980c200241046a200241940c6a2004200110f48d80800020022802044105470d010b200241e0016a41186a22044200370300200241e0016a41206a22034200370300200241e0016a41286a22054200370300200241e0016a41086a2206200241106a290200370300200220022902083703e001200242003703f00141002d00fca3c680001a413041002802c8a3c68000118180808000002201450d03200120022903e001370200200141286a2005290300370200200141206a2003290300370200200141186a2004290300370200200141106a200241e0016a41106a290300370200200141086a2006290300370200200041023a0000200020013602040c050b200241940c6a200241046a41dc0110848e8080001a200241e0016a200241940c6a10b78a808000024020022802e00122044108460d0020022802e401210320024180076a200241e0016a41086a41940510848e8080001a41002d00fca3c680001a419c0541002802c8a3c68000118180808000002201450d042001200336020420012004360200200141086a20024180076a41940510848e8080001a20002001360204200041013a00000c050b20022802e4012101200041023a0000200020013602040c040b10ae80808000000b4101200410b280808000000b4104413010b280808000000b4104419c0510b280808000000b200241800e6a2480808080000bef0601107f2380808080004190016b220124808080800002402000280200220220002802042203460d0020002802102104200028020822054110200541104b1b2106200028020c200541246c6a2107200141ec006a41086a2108200141ec006a41017221090240034020002002410c6a220a3602004102210b024020022802004102460d00200141ec006a200210878a8080004102210b200141c4006a41026a200941026a2d00003a0000200141286a41086a2202200841086a290200370300200141286a41106a220c200841106a290200370300200141286a41186a220d200841186a280200360200200120092f00003b0144200120082902003703282001280270210e024020012d006c220f417e6a0e020301000b200141246a41026a200141c4006a41026a2d00003a0000200141086a41086a2002290300370300200141086a41106a200c290300370300200141086a41186a200d280200360200200120012f01443b012420012001290328370308200f210b0b200141e8006a41026a2202200141246a41026a2d00003a0000200141c8006a41086a220c200141086a41086a290300370300200141c8006a41106a220d200141086a41106a290300370300200141c8006a41186a220f200141086a41186a280200360200200120012f01243b01682001200129030837034802400240024020062005460d0020072d00000e03020102010b2006411041fcc8c4800010f980808000000b200741046a280200221010848a808000201041002802c0a3c68000118080808000000b2007200b3a0000200741016a20012f01683b0000200741046a200e360200200741086a2001290348370200200741036a20022d00003a0000200741106a200c290300370200200741186a200d290300370200200741206a200f2802003602002000200541016a2205360208200741246a2107200a2102200a2003470d000c020b0b024020042802002207450d000240024002400240200728020041fcffffff076a2202410320024105491b0e0403030102000b2007280204450d02200741086a28020041002802c0a3c68000118080808000000c020b2007280204450d01200741086a28020041002802c0a3c68000118080808000000c010b200710858a8080000b200741002802c0a3c68000118080808000000b2004200e3602002000200541016a3602080b20014190016a2480808080000bef0601107f2380808080004190016b220124808080800002402000280200220220002802042203460d0020002802102104200028020822054110200541104b1b2106200028020c200541246c6a2107200141ec006a41086a2108200141ec006a41017221090240034020002002410c6a220a3602004102210b024020022802004102460d00200141ec006a200210838a8080004102210b200141c4006a41026a200941026a2d00003a0000200141286a41086a2202200841086a290200370300200141286a41106a220c200841106a290200370300200141286a41186a220d200841186a280200360200200120092f00003b0144200120082902003703282001280270210e024020012d006c220f417e6a0e020301000b200141246a41026a200141c4006a41026a2d00003a0000200141086a41086a2002290300370300200141086a41106a200c290300370300200141086a41186a200d280200360200200120012f01443b012420012001290328370308200f210b0b200141e8006a41026a2202200141246a41026a2d00003a0000200141c8006a41086a220c200141086a41086a290300370300200141c8006a41106a220d200141086a41106a290300370300200141c8006a41186a220f200141086a41186a280200360200200120012f01243b01682001200129030837034802400240024020062005460d0020072d00000e03020102010b2006411041ecc8c4800010f980808000000b200741046a280200221010848a808000201041002802c0a3c68000118080808000000b2007200b3a0000200741016a20012f01683b0000200741046a200e360200200741086a2001290348370200200741036a20022d00003a0000200741106a200c290300370200200741186a200d290300370200200741206a200f2802003602002000200541016a2205360208200741246a2107200a2102200a2003470d000c020b0b024020042802002207450d000240024002400240200728020041fcffffff076a2202410320024105491b0e0403030102000b2007280204450d02200741086a28020041002802c0a3c68000118080808000000c020b2007280204450d01200741086a28020041002802c0a3c68000118080808000000c010b200710858a8080000b200741002802c0a3c68000118080808000000b2004200e3602002000200541016a3602080b20014190016a2480808080000bab0201027f23808080800041306b22012480808080004100210202400240024020002d0000410b470d002001410c6a200041086a2802002000410c6a280200108b8a80800020012d000c0d0041002d00fca3c680001a410c41002802c8a3c68000118180808000002202450d0241002d00fca3c680001a412041002802c8a3c68000118180808000002200450d012000200129000d370000200041186a200141256a290000370000200041106a2001411d6a290000370000200041086a200141156a290000370000200241203602002002412036020820022000360204200041002802c0a3c6800011808080800000200241002802c0a3c6800011808080800000410221020b200141306a24808080800020020f0b4101412010b280808000000b4104410c10b280808000000b950301027f23808080800041e0006b22032480808080002003412b6a10ef88808000024002400240024020032d002b22044102460d002003410a6a410a6a2003412b6a410a6a2900003700002003410a6a41126a2003412b6a41126a2900003700002003410a6a41196a2003412b6a41196a2900003700002003200329002d37000c200320032d002c3a000b2003412b6a200120024100280298a3c680001185808080000002402003412b6a2003410b6a412010888e808000450d00200041046a410910dc888080000c030b2004450d01200341cc006a2001200210978a80800020032d004c2202410e460d01200041056a200329004d370000200041146a200341dc006a2800003600002000410d6a200341d5006a290000370000200020023a00040c020b200041046a410810dc88808000200041013a00000c020b2000200329002b370001200041003a0000200041196a200341c3006a290000370000200041116a2003413b6a290000370000200041096a200341336a2900003700000c010b200041013a00000b200341e0006a2480808080000b980601027f23808080800041306b2201248080808000200141106a41186a42c58a95aad4a8d1a2c500370300200141106a41106a42c58a95aad4a8d1a2c500370300200141106a41086a42c58a95aad4a8d1a2c500370300200142c58a95aad4a8d1a2c5003703104200200141106a10fd87808000200142fc90b9e5d09296e777370318200142a6d4e6f1a4dd959860370310200142bb88f596f8cbf6cf4c3703282001428a85cd9fb3e4b2ae6d37032041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d00200242c58a95aad4a8d1a2c500370000200241186a42c58a95aad4a8d1a2c500370000200241106a42c58a95aad4a8d1a2c500370000200241086a42c58a95aad4a8d1a2c500370000200141106a41202002412041002802e0a1c6800011868080800000200241002802c0a3c6800011808080800000200142003702082001418097c380003602042001418080808078360200200142fc90b9e5d09296e777370318200142a6d4e6f1a4dd959860370310200142a0b9ab8f9ae5999778370328200142f999a7c78cd1d1cdb17f3703202001200141106a4120109288808000024020012802002202418080808078460d002002450d00200128020441002802c0a3c68000118080808000000b200142fc90b9e5d09296e777370318200142a6d4e6f1a4dd959860370310200142a2f5bea594dedcdb10370328200142d6888295b2b493ecbf7f370320200141013a0000200141106a41202001410141002802e0a1c6800011868080800000200142fc90b9e5d09296e777370318200142a6d4e6f1a4dd959860370310200142d2daa4a6928283fa39370328200142a7fbb3c3b2f09acd28370320200141013a0000200141106a41202001410141002802e0a1c680001186808080000041002d00fca3c680001a410441002802c8a3c68000118180808000002202450d012002410036000041e6aac4800041102002410441002802e0a1c6800011868080800000200241002802c0a3c6800011808080800000200141306a2480808080000f0b4101412010b280808000000b4101410410b280808000000b9d0202017f037e23808080800041f0006b2201248080808000024020002903002202500d0020014298d5afd2c6aeacae2f370318200142c2cdc8b0c7b9e78f857f370310200142e4c5bdb4b2e9a5a6807f370328200142d790d7a3fef9fda0c8003703202001200141106a10e688808000200129030821032001280200210020014298d5afd2c6aeacae2f370318200142c2cdc8b0c7b9e78f857f370310200142e4c5bdb4b2e9a5a6807f370328200142d790d7a3fef9fda0c800370320200142002003420020001b220320027d220420042003561b370368200141106a4120200141e8006a410841002802e0a1c6800011868080800000200141103a001020012002370318200141106a108e8a8080000b200141f0006a2480808080000baa0401047f23808080800041c0016b2201248080808000200142fc90b9e5d09296e777370328200142a6d4e6f1a4dd959860370320200142d3d8c5d2a9b9d2c1ac7f37033820014282ca868eabf3add0cf00370330200141106a200141206a10e68880800002402001280210450d002001290318500d00200141086a10ec88808000200128020c210220012802082103200142fc90b9e5d09296e777370328200142a6d4e6f1a4dd959860370320200142b7aeb183f6f8ab9cd0003703382001428ab0f6f7cbd3f9e2d8003703302001200141206a412010d0888080002001280204410020012802001b41016a2204450d00200142fc90b9e5d09296e777370328200142a6d4e6f1a4dd959860370320200142b7aeb183f6f8ab9cd0003703382001428ab0f6f7cbd3f9e2d8003703302001200436029001200141206a412020014190016a410441002802e0a1c68000118680808000002001200236022420014102200320034103461b360220200141286a200041d00010848e8080001a20014100360280012001428080808010370378200142fc90b9e5d09296e77737039801200142a6d4e6f1a4dd95986037039001200142bc8986ab88c2dce4573703a80120014280a9fbf0e5a2c1b3e5003703a001200141b4016a200141206a10968880800020014190016a4120200141b4016a4100280298a1c68000118580808000002001280278450d00200128027c41002802c0a3c68000118080808000000b200141c0016a2480808080000b9d0202017f037e23808080800041f0006b2201248080808000024020002903002202500d0020014298d5afd2c6aeacae2f370318200142c2cdc8b0c7b9e78f857f370310200142e4c5bdb4b2e9a5a6807f370328200142d790d7a3fef9fda0c8003703202001200141106a10e688808000200129030821032001280200210020014298d5afd2c6aeacae2f370318200142c2cdc8b0c7b9e78f857f370310200142e4c5bdb4b2e9a5a6807f370328200142d790d7a3fef9fda0c8003703202001427f2003420020001b220320027c220420042003541b370368200141106a4120200141e8006a410841002802e0a1c68000118680808000002001410f3a001020012002370318200141106a108e8a8080000b200141f0006a2480808080000bda0e01097f23808080800041c0006b220624808080800002400240024002400240200541057441286a412820041b2204417f4c0d004100210741002d00fca3c680001a200441002802c8a3c68000118180808000002205450d012006410036020c200620053602082006200436020402402004411f4b0d00200641046a4100412010ab868080002006280204210420062802082105200628020c21070b200520076a22082001290000370000200841186a200141186a290000370000200841106a200141106a290000370000200841086a200141086a2900003700002006200741206a220136020c0240200420016b41074b0d00200641046a2001410810ab8680800020062802082105200628020c21010b200520016a20023700002006200141086a220936020c2003280228210a2003280224210b2003280220210c2003280218210520032802102104200328020c2101200328021c210d2003280214210802402003280200450d002003280208210e2003280204210303402003200e2003200e4b1b210702400240034002402001450d00024020042005460d00200641106a41186a200441186a290000370300200641106a41106a200441106a290000370300200641106a41086a200441086a29000037030020062004290000370310200441206a21040c040b2008450d00200141002802c0a3c68000118080808000000b024020072003470d00200721030c020b2006200336023c200641306a2006413c6a10fc87808000024020062802302201418080808078460d00200341016a21032006280234220420062802384105746a210520012108200421010c010b0b200341016a21030b200d450d07200c200a460d06200641106a41186a200c41186a290000370300200641106a41106a200c41106a290000370300200641106a41086a200c41086a2900003703002006200c290000370310200c41206a210c410021010b0240200628020420096b411f4b0d00200641046a2009412010ab86808000200628020c21090b200628020820096a22072006290310370000200741086a200641106a41086a290300370000200741106a200641106a41106a290300370000200741186a200641106a41186a2903003700002006200941206a220936020c0c000b0b0240200d0d002001450d05024020042005460d000340200641106a41186a2207200441186a290000370300200641106a41106a220c200441106a290000370300200641106a41086a220e200441086a290000370300200620042900003703100240200628020420096b41204f0d00200641046a2009412010ab86808000200628020c21090b200628020820096a22032006290310370000200341086a200e290300370000200341106a200c290300370000200341186a20072903003700002006200941206a220936020c200441206a22042005470d000b0b2008450d05200141002802c0a3c68000118080808000000c050b2008450d0203400240024002402001450d0020042005470d01200141002802c0a3c68000118080808000000b200c200a460d06200641106a41186a200c41186a290000370300200641106a41106a200c41106a290000370300200641106a41086a200c41086a2900003703002006200c290000370310200c41206a210c410021010c010b200641106a41186a200441186a290000370300200641106a41106a200441106a290000370300200641106a41086a200441086a29000037030020062004290000370310200441206a21040b0240200628020420096b411f4b0d00200641046a2009412010ab86808000200628020c21090b200628020820096a22032006290310370000200341086a200641106a41086a290300370000200341106a200641106a41106a290300370000200341186a200641106a41186a2903003700002006200941206a220936020c0c000b0b10ae80808000000b4101200410b280808000000b0240024020010d00410121030c010b410021030b03400240024002400240024020030e020001010b20042005470d010c030b200c200a460d04200641106a41186a200c41186a290000370300200641106a41106a200c41106a290000370300200641106a41086a200c41086a2900003703002006200c290000370310200c41206a210c410021010c010b200641106a41186a200441186a290000370300200641106a41106a200441106a290000370300200641106a41086a200441086a29000037030020062004290000370310200441206a21040b0240200628020420096b411f4b0d00200641046a2009412010ab86808000200628020c21090b200628020820096a22032006290310370000200341086a200641106a41086a290300370000200341106a200641106a41106a290300370000200341186a200641106a41186a2903003700002006200941206a220936020c2001450d00410021030c010b410121030c000b0b200b450d00200d41002802c0a3c68000118080808000000b20002006280208220320094100280298a3c680001185808080000002402006280204450d00200341002802c0a3c68000118080808000000b200641c0006a2480808080000b840a01047f23808080800041e0006b220124808080800002400240417f4100280284a4c680002202410447200241044b1b2202417f460d00200241ff01710d010b200141e08180800036021020014106360218200141f9a6c480003602142001200141146a36020c4100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141d4006a4201370200200141cc006a4101360200200141c4006a4116360200200141c0006a41bcaac48000360200200141346a4195a8c48000ad4280808080b00b84370200200141286a41d2aac48000ad4280808080c00284370200200141d0006a2001410c6a360200200141b4aac480003602482001410436023c200141003602302001410036022420014281808080c02037021c200341ecf2c08000200441024622041b2001411c6a200241d4f2c0800020041b280210118480808000000b02400240417f4100280284a4c680002202410447200241044b1b2202417f460d00200241ff01710d010b200141e08180800036021020014104360218200141f5a6c480003602142001200141146a36020c4100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141d4006a4201370200200141cc006a4101360200200141c4006a4116360200200141c0006a41c1e5c28000360200200141346a41e8e4c28000ad4280808080900b84370200200141286a41d7e5c28000ad4280808080b00284370200200141d0006a2001410c6a360200200141e0e4c280003602482001410436023c200141003602302001410036022420014281808080800e37021c200341ecf2c08000200441024622041b2001411c6a200241d4f2c0800020041b280210118480808000000b02400240417f4100280284a4c680002202410447200241044b1b2202417f460d00200241ff01710d010b200141e0818080003602102001410d360218200141e8a6c480003602142001200141146a36020c4100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141d4006a4201370200200141cc006a4101360200200141c4006a4116360200200141c0006a418cabc38000360200200141346a41c0a7c38000ad4280808080b00e84370200200141286a41b9a9c38000ad4280808080d00684370200200141d0006a2001410c6a360200200141b8abc380003602482001410436023c200141003602302001410036022420014281808080e00437021c200341ecf2c08000200441024622041b2001411c6a200241d4f2c0800020041b280210118480808000000b02400240417f4100280284a4c680002202410447200241044b1b2202417f460d00200241ff01710d010b200141e08180800036021020014108360218200141e0a6c480003602142001200141146a36020c4100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141d4006a4201370200200141cc006a4101360200200141c4006a4116360200200141c0006a41d4d8c58000360200200141346a41b8d6c58000ad4280808080d00b84370200200141286a41ead8c58000ad4280808080f00284370200200141d0006a2001410c6a360200200141ccd8c580003602482001410436023c200141003602302001410036022420014281808080a01837021c200341ecf2c08000200441024622041b2001411c6a200241d4f2c0800020041b280210118480808000000b2000420037030820004200370300200141e0006a2480808080000bcd0401077f23808080800041c0006b2200248080808000200041206a41f9a6c48000410641002802a0a3c6800011858080800000200041206a41106a220141bccdc48000411541002802a0a3c6800011858080800000200041186a2202200041206a41186a2203290000370300200041106a22042001290000370300200041086a2205200041206a41086a220629000037030020002000290020370300200041003b012020004120200041206a410241002802e0a1c6800011868080800000200041206a41f5a6c48000410441002802a0a3c6800011858080800000200141bccdc48000411541002802a0a3c680001185808080000020022003290000370300200420012900003703002005200629000037030020002000290020370300200041003b012020004120200041206a410241002802e0a1c6800011868080800000200041206a41e8a6c48000410d41002802a0a3c6800011858080800000200141bccdc48000411541002802a0a3c680001185808080000020022003290000370300200420012900003703002005200629000037030020002000290020370300200041003b012020004120200041206a410241002802e0a1c6800011868080800000200041206a41e0a6c48000410841002802a0a3c6800011858080800000200141bccdc48000411541002802a0a3c680001185808080000020022003290000370300200420012900003703002005200629000037030020002000290020370300200041013b012020004120200041206a410241002802e0a1c6800011868080800000200041c0006a2480808080000b8e1008017f087e017f037e017f027e037f027e23808080800041e0016b2204248080808000200441086a200110fa8780800042002105200441186a290300210620032903002107200441306a2903002108200441286a29030021092004290310210a02400240200441206a290300220b4200520d0020094200520d0020084200520d00200a2006428080808080808080807f858450450d00428080808080808080807f210c4100210d4201210e4200210f42002110410021110c010b200b42ffffe883b1de1656210d4200210e410121110240024020094200510d00200a21052006210c2009210f0c010b4200210e2008420052211142002109200a21052006210c4200210f0b200821100b0240200b200f2007200f2007541b22127c2213200b5a0d0002400240417f4100280284a4c680002203410147200341014b1b2203450d00200341ff017141ff01470d010b200441e081808000360274200441b4f9c580003602704100280290a1c680002103410028028ca1c6800021144100280280a4c680002115200441d0016a4201370200200441c8016a4101360200200441c0016a4112360200200441bc016a41bcf9c58000360200200441b0016a41cef9c58000ad4280808080c00c84370200200441a4016a41b2fac58000ad4280808080b00384370200200441cc016a200441f0006a360200200441b4f8c580003602c401200441013602b801200441003602ac01200441003602a00120044281808080b01b37029801201441ecf2c08000201541024622151b20044198016a200341d4f2c0800020151b280210118480808000000b427f200b20127c22132013200b541b21130b200f2007582010507121030240200d417f73201342ffffe883b1de165671450d00200220021084888080001a0b02400240024002400240024002400240201141004722142003710d0002402014200372450d0020030d040c020b20044198016a200210f68780800020042d0098012214410e460d01200435019a012004419e016a33010042208684210b0c020b20021083888080000c020b20044198016a200210fa8780800020042802c8010d0102400240417f4100280284a4c680002214410147201441014b1b2214450d00201441ff017141ff01470d010b4100280290a1c680002114410028028ca1c6800021154100280280a4c680002116200441d0016a4200370200200441cc016a419cadc48000360200200441c8016a4101360200200441c0016a4111360200200441bc016a41c1c7c48000360200200441b0016a41e4c6c48000ad4280808080d00b84370200200441a4016a41d2c7c48000ad4280808080f00284370200200441dcc6c480003602c401200441013602b801200441003602ac01200441003602a00120044281808080d0800137029801201541ecf2c08000201641024622161b20044198016a201441d4f2c0800020161b280210118480808000000b20044198016a200210f68780800020042d0098012214410e460d01200435019a012004419e016a33010042208684210b0b20042802a801210220042902a001211320042d00990121010c010b200d2013428080e983b1de165471450d01200441c8006a2002200210f78780800020042d00482214410e460d01024002402011450d002003450d01200441dc006a200210f68780800020042d005c410e460d01200441f0006a41106a200441dc006a41106a280200360200200441f0006a41086a200441dc006a41086a2902003703002004200429025c3703700240417f4100280284a4c680002202410147200241014b1b2202450d00200241ff017141ff01470d020b20044188016a410c6a41ca83808000360200200441e08180800036028c012004418c98c38000360288012004200441f0006a360290014100280290a1c680002102410028028ca1c6800021014100280280a4c680002103200441d0016a4202370200200441c8016a4102360200200441c0016a4112360200200441bc016a419498c38000360200200441b0016a41a698c38000ad4280808080c00c8437020020044198016a410c6a418a99c38000ad4280808080b00384370200200441cc016a20044188016a3602002004418497c380003602c401200441013602b801200441003602ac01200441003602a00120044281808080e02437029801200141ecf2c08000200341024622031b20044198016a200241d4f2c0800020031b280210118480808000000c010b20030d0020021083888080000b200435014a200441ce006a33010042208684210b200428025821022004290250211320042d004921010b20004202370300200041186a2002360200200041106a20133703002000200b4210862001ad42ff0183420886842014ad843703080c010b0240024002400240201342ffffe883b1de16560d00200f2007580d010b200f20127d21094200211742012118201342808097fccea1697c42818097fccea1695a0d012013210b0c020b420021182013420052ad211720082110200a21052006210c0c010b2013210b200f2007580d020b20044198016a200110fa878080002013422088a721022013a721030240024020042802cc010d0020044198016a200110fa8780800020042802d0010d0020011086888080000c010b200441a8016a200c370300200420053703a001200420103703c001200420093703b8012004200b3703b0012004201837039801200120044198016a1082888080000b200020123703202000200236021c2000200336021820002017370310200020133703082000200e3703000b200441e0016a2480808080000f0b41e9c7c4800041dd0041c8c8c4800010f880808000000baa0f08017f077e017f037e017f027e057f017e2380808080004190026b2203248080808000200341086a200110fa8780800042002104200341186a2903002105200341306a2903002106200341286a29030021072003290310210802400240200341206a29030022094200520d0020074200520d0020064200520d0020082005428080808080808080807f858450450d00428080808080808080807f210a4100210b4201210c4200210d4200210e4100210f0c010b200942ffffe883b1de1656210b4200210c4101210f0240024020074200510d00200821042005210a2007210d0c010b4200210c2006420052210f42002107200821042005210a4200210d0b2006210e0b0240024042002009200228020429030022107d221120112009561b200241086a280200290300580d00200341c8016a410310f888808000200921100c010b2003410e3a00c8010b200341e0006a41106a200341c8016a41106a280200360200200341e0006a41086a200341c8016a41086a290200370300200320032902c801370360200d200e845021120240200b417f73201042ffffe883b1de165671450d002002280200221320131084888080001a0b02400240024002400240024002400240200f41004722132012710d0002402013201272450d0020120d04200228020021140c020b200341c8016a2002280200221410f68780800020032d00c8012213410e460d0120033501ca01200341ce016a3301004220868421090c020b20022802001083888080000c020b200341c8016a201410fa8780800020032802f8010d0102400240417f4100280284a4c680002213410147201341014b1b2213450d00201341ff017141ff01470d010b4100280290a1c680002113410028028ca1c6800021154100280280a4c68000211620034180026a4200370200200341fc016a419cadc48000360200200341f8016a4101360200200341f0016a4111360200200341ec016a41c1c7c48000360200200341e0016a41e4c6c48000ad4280808080d00b84370200200341d4016a41d2c7c48000ad4280808080f00284370200200341dcc6c480003602f401200341013602e801200341003602dc01200341003602d00120034281808080d080013702c801201541ecf2c08000201641024622161b200341c8016a201341d4f2c0800020161b280210118480808000000b200341c8016a201410f68780800020032d00c8012213410e460d0120033501ca01200341ce016a3301004220868421090b20032802d801210220032902d001211020032d00c90121010c010b200b2010428080e983b1de165471450d01200341f8006a20022802002202200210f78780800020032d00782213410e460d0102400240200f450d002012450d012003418c016a200210f68780800020032d008c01410e460d01200341a0016a41106a2003418c016a41106a280200360200200341a0016a41086a2003418c016a41086a2902003703002003200329028c013703a0010240417f4100280284a4c680002202410147200241014b1b2202450d00200241ff017141ff01470d020b200341b8016a410c6a41ca83808000360200200341e0818080003602bc012003418c98c380003602b8012003200341a0016a3602c0014100280290a1c680002102410028028ca1c6800021014100280280a4c68000211220034180026a4202370200200341f8016a4102360200200341f0016a4112360200200341ec016a419498c38000360200200341e0016a41a698c38000ad4280808080c00c84370200200341c8016a410c6a418a99c38000ad4280808080b00384370200200341fc016a200341b8016a3602002003418497c380003602f401200341013602e801200341003602dc01200341003602d00120034281808080e0243702c801200141ecf2c08000201241024622121b200341c8016a200241d4f2c0800020121b280210118480808000000c010b20120d0020021083888080000b200335017a200341fe006a3301004220868421092003280288012102200329028001211020032d007921010b20004202370300200041186a2002360200200041106a2010370300200020094210862001ad42ff0183420886842013ad843703080c010b0240024002400240201042ffffe883b1de16560d0042002111200d4200510d010b4200211742012111201042808097fccea1697c42818097fccea1695a0d01201021090c020b2010420052ad21172006210e2007210d200821042005210a0c010b20102109200d4200510d020b200341c8006a41106a200341e0006a41106a280200360200200341c8006a41086a200341e0006a41086a29030037030020032003290360370348200341c8016a200110fa878080002010422088a721022010a721120240024020032802fc010d00200341c8016a200110fa878080002003280280020d0020011086888080000c010b200341c8016a41106a200a370300200320043703d0012003200e3703f0012003200d3703e801200320093703e001200320113703c8012001200341c8016a1082888080000b2000200236021c2000201236021820002017370310200020103703082000200c37030020002003290348370320200041286a200341d0006a290300370300200041306a200341d8006a2802003602000b20034190026a2480808080000f0b41e9c7c4800041dd0041c8c8c4800010f880808000000b9e0d06017f087e017f027e057f037e23808080800041e0016b2204248080808000200441086a200110fa8780800042002105200441186a290300210620032903002107200441306a2903002108200441286a29030021092004290310210a024002400240200441206a290300220b4200520d0020094200520d0020084200520d00200a2006428080808080808080807f858450450d00428080808080808080807f210c4100210d4200210e4200210f410021030c010b200b42ffffe883b1de1656210d024020094200510d004101211042012105410121032008210f200a210e2006210c410121110c020b2008420052210342012105200a210e2006210c2008210f0b42002109200f4200522111410021100b02402007428080e983b1de16540d00200d0d00200220021084888080001a0b024002400240024002400240024002402003450d002011450d010b0240024020030d0020110d010b2011450d040c020b20044198016a200210f68780800020042d0098012212410e460d01200435019a012004419e016a3301004220868421070c020b20021083888080000c020b20044198016a200210fa8780800020042802c8010d0102400240417f4100280284a4c680002212410147201241014b1b2212450d00201241ff017141ff01470d010b4100280290a1c680002112410028028ca1c6800021134100280280a4c680002114200441d0016a4200370200200441cc016a419cadc48000360200200441c8016a4101360200200441c0016a4111360200200441bc016a41c1c7c48000360200200441b0016a41e4c6c48000ad4280808080d00b84370200200441a4016a41d2c7c48000ad4280808080f00284370200200441dcc6c480003602c401200441013602b801200441003602ac01200441003602a00120044281808080d0800137029801201341ecf2c08000201441024622141b20044198016a201241d4f2c0800020141b280210118480808000000b20044198016a200210f68780800020042d0098012212410e460d01200435019a012004419e016a3301004220868421070b20042802a801210220042902a001210920042d00990121010c010b2007428080e983b1de1654200d71450d01200441c8006a2002200210f78780800020042d00482212410e460d01024002402003450d0020110d01200441dc006a200210f68780800020042d005c410e460d01200441f0006a41106a200441dc006a41106a280200360200200441f0006a41086a200441dc006a41086a2902003703002004200429025c3703700240417f4100280284a4c680002202410147200241014b1b2202450d00200241ff017141ff01470d020b20044188016a410c6a41ca83808000360200200441e08180800036028c012004418c98c38000360288012004200441f0006a360290014100280290a1c680002102410028028ca1c6800021014100280280a4c680002103200441d0016a4202370200200441c8016a4102360200200441c0016a4112360200200441bc016a419498c38000360200200441b0016a41a698c38000ad4280808080c00c8437020020044198016a410c6a418a99c38000ad4280808080b00384370200200441cc016a20044188016a3602002004418497c380003602c401200441013602b801200441003602ac01200441003602a00120044281808080e02437029801200141ecf2c08000200341024622031b20044198016a200241d4f2c0800020031b280210118480808000000c010b2011450d0020021083888080000b200435014a200441ce006a330100422086842107200428025821022004290250210920042d004921010b20004202370300200041186a2002360200200041106a2009370300200020074210862001ad42ff0183420886842012ad843703080c010b02400240200742ffffe883b1de1656201072450d00420021152007211642012117200742808097fccea1697c42818097fccea169542010720d0141e9c7c4800041dd0041c8c8c4800010f880808000000b420021172007420052ad21152008210f200b2116200a210e2006210c0b2005420185210820044198016a200110fa878080002007422088a721022007a721030240024020042802cc010d0020044198016a200110fa8780800020042802d0010d0020011086888080000c010b200441a8016a200c3703002004200e3703a0012004200f3703c001200420093703b801200420163703b0012004201737039801200120044198016a1082888080000b2000200b3703202000200236021c200020033602182000201537031020002007370308200020083703000b200441e0016a2480808080000ba20602017f017e23808080800041d0006b220324808080800020034102360208200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd95986037033020034293bb8a9cbb9ab6b31a370348200342ffabedd185d3d8d216370340200341086a200341306a41201094888080002003410036023041e6aac480004110200341306a410441002802e0a1c680001186808080000020032001360234200341f0acc48000360230200341086a200341306a1083898080004188adc480004113200341086a412041002802e0a1c6800011868080800000200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd959860370330200342d3d8c5d2a9b9d2c1ac7f37034820034282ca868eabf3add0cf00370340200320002903002204370328200341306a4120200341286a410841002802e0a1c6800011868080800000200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd959860370330200342f4fa97f89782c7a62d37034820034299cfe7ffe3b8eac70837034020022802042002280208200341306a4120108a8d80800041002d00fca3c680001a200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd959860370330200342bb88f596f8cbf6cf4c3703482003428a85cd9fb3e4b2ae6d3703400240412041002802c8a3c680001181808080000022020d004101412010b280808000000b20022001290000370000200241186a200141186a290000370000200241106a200141106a290000370000200241086a200141086a290000370000200341306a41202002412041002802e0a1c6800011868080800000200241002802c0a3c68000118080808000002004427f7c200110fd87808000200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd959860370330200342beee8ae9ce8fa2b1977f37034820034295d4f9afa2868fb864370340200341306a412041002802a0a1c6800011848080800000200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd959860370330200342f89aef8eef91e1ce967f370348200342b4d6d6dfccc6b592c300370340200341306a412041002802a0a1c6800011848080800000200341d0006a2480808080000baa04010a7f23808080800041d0006b220324808080800041002104200341046a200120024100280288a3c68000118580808000000240024020032802042202418080808078460d00200328020821012003200328020c36024c20032001360248200341106a200341c8006a4100200310df8c8080000240024020032802102205418180808078470d00410021060c010b200341176a2d000041187420032f001541087472210420032d0014210620032802382107200328022c2108200328022821092003280220210a200328021c210b2003280218210c0b02402002450d00200141002802c0a3c68000118080808000000b2005418180808078460d00200620047221014101210202400240200c0d0020070d01410221020b2000200210dc888080000240200541808080807872418080808078460d00200141002802c0a3c68000118080808000000b0240200b41808080807872418080808078460d00200a41002802c0a3c68000118080808000000b200941808080807872418080808078460d02200841002802c0a3c68000118080808000000c020b2000410e3a00000240200541808080807872418080808078460d00200141002802c0a3c68000118080808000000b0240200b41808080807872418080808078460d00200a41002802c0a3c68000118080808000000b200941808080807872418080808078460d01200841002802c0a3c68000118080808000000c010b2000410310dc888080000b200341d0006a2480808080000b800401027f23808080800041d0006b2200248080808000200042fc90b9e5d09296e777370338200042a6d4e6f1a4dd959860370330200042bc8986ab88c2dce45737034820004280a9fbf0e5a2c1b3e500370340200041306a412041002802a0a1c6800011848080800000200042fc90b9e5d09296e777370338200042a6d4e6f1a4dd959860370330200042b7aeb183f6f8ab9cd0003703482000428ab0f6f7cbd3f9e2d800370340200041306a412041002802a0a1c6800011848080800000200041306a41f9a6c48000410641002802a0a3c6800011858080800000200041306a41106a220141c99ec38000410b41002802a0a3c6800011858080800000200041106a41186a200041306a41186a290000370300200041106a41106a2001290000370300200041106a41086a200041306a41086a29000037030020002000290030370310200041086a200041106a41204101417f41002802a8a1c6800011878080800000024002402000280208450d0041002d00fca3c680001a412041002802c8a3c68000118180808000002201450d0120012000290310370000200141186a200041106a41186a290300370000200141106a200041106a41106a290300370000200141086a200041106a41086a290300370000200141002802c0a3c68000118080808000000b200041d0006a2480808080000f0b4101412010b280808000000bc10904017f017e027f037e23808080800041a0036b2202248080808000200041086a20002000290300220342025122041b210502400240200020044103746a290300500d00200541106a29030022062001290308220720062007541b2106200529030822072001290300220820072008541b21070c010b20012903082106200129030021070b200241206a10e189808000200241206a210402400240024020012d00100e03020001020b200241f8006a21040c010b200241d0016a21040b2001427f200720042903487c220820082007541b3703002001427f2006200441d0006a2903007c220720072006541b37030841012104024020012d00110d0020052d001841004721040b200120043a00110240024020034202520d0020024183036a200141106a290000370000200241fb026a200141086a290000370000200220012900003700f302410021010c010b2002200036029c0302404100280284a4c680004105470d00200242fc90b9e5d09296e777370328200242a6d4e6f1a4dd959860370320200242d3d8c5d2a9b9d2c1ac7f37033820024282ca868eabf3add0cf00370330200241106a200241206a10e688808000200241ec026a410c6a41cb83808000360200200241cc838080003602f00220022002290318420020022802101b3703d00220022002419c036a3602f4022002200241d0026a3602ec024100280290a1c680002100410028028ca1c6800021044100280280a4c680002105200241d8006a4202370200200241d0006a4102360200200241c8006a410f360200200241c4006a41ccadc48000360200200241386a4195a8c48000ad4280808080b00b84370200200241206a410c6a41dbadc48000ad4280808080c00184370200200241d4006a200241ec026a360200200241bcadc4800036024c20024105360240200241003602342002410036022820024281808080e0fd01370220200441ecf2c08000200541024622051b200241206a200041d4f2c0800020051b28021011848080800000200228029c0321000b200241ff026a200041306a280000360000200241f7026a200041286a2900003700002002418b036a200141086a29000037000020024193036a200141106a290000370000200220002900203700ef022002200129000037008303410121010b200241296a20022900ec02370000200241d8006a20022903d002370300200241316a200241ec026a41086a290000370000200241396a200241ec026a41106a290000370000200241c1006a20024184036a290000370000200241c9006a200241ec026a41206a290000370000200241d0006a20024193036a290000370000200241e0006a200241d0026a41086a290300370300200241e8006a200241d0026a41106a290300370300200220013a0028200241163a0020200241206a108e8a808000200241086a41e6aac48000411010d0888080002002200228020c41016a410120022802081b220136022041e6aac480004110200241206a410441002802e0a1c6800011868080800000200220013602f002200241003602ec02200242fc90b9e5d09296e777370328200242a6d4e6f1a4dd95986037032020024293bb8a9cbb9ab6b31a370338200242ffabedd185d3d8d216370330200241ec026a200241206a4120109488808000200241a0036a2480808080000bf10202027f017e2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120002903004101200110fe8080800021000c020b20002903002104410021000340200220006a41ff006a413041d7002004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002903002104410021000340200220006a41ff006a413041372004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b9a0201037f23808080800041306b2200248080808000200041e6aac48000411010d088808000200028020421010240200028020022024101470d0041e6aac48000411041002802a0a1c68000118480808000000b200042fc90b9e5d09296e777370318200042a6d4e6f1a4dd959860370310200042bad08eede089ffa812370328200042bd81f785e387e6aa817f37032020002001410020021b360208200041106a4120200041086a410441002802e0a1c680001186808080000020004101360208200042fc90b9e5d09296e777370318200042a6d4e6f1a4dd95986037031020004293bb8a9cbb9ab6b31a370328200042ffabedd185d3d8d216370320200041086a200041106a4120109488808000200041306a2480808080000bad0302037f027e23808080800041d0026b2202248080808000200241206a2001280204220320012802082204108b8a8080000240024020022d00200d002001280200210141e7adc4800041052003200441002802e0a1c6800011868080800000200241043a0020200241206a10f588808000200241163a0020200241023a0028200241206a108e8a80800002402001450d00200341002802c0a3c68000118080808000000b200242fc90b9e5d09296e777370328200242a6d4e6f1a4dd959860370320200242f7b6fccfd083b2cf4d370338200242afd2c6ffdbc495bc08370330200241206a412041002802a0a1c6800011848080800000200241206a10e189808000200241c0026a290300210520022903b8022106200041013a00182000200537031020002006370308200042013703000c010b200241136a200241346a28020022043600002002410b6a2002412c6a2902002205370000200220022902242206370003200041186a2004360000200041106a200537000020002006370008200042023703002001280200450d00200341002802c0a3c68000118080808000000b200241d0026a2480808080000b931a03047f027e027f23808080800041a00d6b220124808080800002400240417f4100280284a4c680002202410447200241044b1b2202450d00200241ff017141ff01470d010b200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d009200142d3d8c5d2a9b9d2c1ac7f3703e80920014282ca868eabf3add0cf003703e009200141a8016a200141d0096a10e688808000200120012903b001420020012802a8011b37039802200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d009200142bad08eede089ffa8123703e809200142bd81f785e387e6aa817f3703e009200141a0016a200141d0096a412010d088808000200120012802a401410020012802a0011b3602a002200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d00920014295f38cfcb699a3c4ef003703e809200142a8db95cdaa86daa7193703e00920014198016a200141d0096a412010d0888080002001200128029c0141002001280298011b3602a402200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d00920014295f38cfcb699a3c4ef003703e809200142a8db95cdaa86daa7193703e00920014190016a200141d0096a412010d08880800041e400210241e4002103024020012802940141002001280290011b2204418080f0014b0d00200441e4006c418080f0016e21030b200120033a00a902200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d00920014295f38cfcb699a3c4ef003703e809200142a8db95cdaa86daa7193703e00920014188016a200141d0096a412010d0888080000240200128028c0141002001280288011b2203418080c0024b0d00200341e4006c418080c0026e21020b200120023a00aa02200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d00920014295f38cfcb699a3c4ef003703e809200142a8db95cdaa86daa7193703e00920014180016a200141d0096a412010d08880800041e4002102024020012802840141002001280280011b2203418080c0024b0d00200341e4006c418080c0026e21020b200120023a00ab02200141b0026a10ce888080002001200141b0026a3602ac02200141e8026a10ce8880800020012903e802210520014198036a10e1898080000240024020014198036a41206a290300427f20012802b0031b2206500d0020052006560d00200141f0006a2005420042e400420010878e808000200141e0006a2001290370200141f0006a41086a2903002006420010898e80800020012903602205428002544100200141e0006a41086a290300501b450d002005a721020c010b41e40021020b200120023a00e702200141c8056a10ce888080002001200141d8056a3602c40520014180066a10ce888080002001290390062105200141b0066a10e18980800002400240200141a8076a290300427f200141a0076a2802001b2206500d0020052006560d00200141d0006a2005420042e400420010878e808000200141c0006a2001290350200141d0006a41086a2903002006420010898e80800020012903402205428002544100200141c0006a41086a290300501b450d002005a721020c010b41e40021020b200120023a00ff05200141e8086a10ce88808000200120014188096a3602e408200141a0096a10ce8880800020012903c0092105200141d0096a10e18980800002400240200141a00b6a290300427f200141980b6a2802001b2206500d0020052006560d00200141306a2005420042e400420010878e808000200141206a2001290330200141306a41086a2903002006420010898e80800020012903202205428002544100200141206a41086a290300501b450d002005a721020c010b41e40021020b20014194026a41cd838080003602002001418c026a41ce8380800036020020014184026a41cd83808000360200200141fc016a41ce83808000360200200141f4016a41cd83808000360200200141b8016a41346a41ce83808000360200200141e4016a41cd83808000360200200141b8016a41246a41cd83808000360200200141d4016a41cd83808000360200200141cc016a418580808000360200200141b8016a410c6a418580808000360200200141cc838080003602bc01200120023a009f0920012001419f096a360290022001200141e4086a360288022001200141ff056a360280022001200141c4056a3602f8012001200141e7026a3602f0012001200141ac026a3602e8012001200141ab026a3602e0012001200141aa026a3602d8012001200141a9026a3602d0012001200141a4026a3602c8012001200141a0026a3602c001200120014198026a3602b8014100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141900d6a420c370200200141880d6a410d360200200141800d6a410f360200200141d80c6a41246a41ccadc48000360200200141f00c6a4195a8c48000ad4280808080b00b84370200200141d80c6a410c6a41dbadc48000ad4280808080c00184370200200141d80c6a41346a200141b8016a360200200141dcaec480003602840d200141043602f80c200141003602ec0c200141003602e00c2001428180808090e0013702d80c200341ecf2c08000200441024622041b200141d80c6a200241d4f2c0800020041b280210118480808000000b200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d00920014293bb8a9cbb9ab6b31a3703e809200142ffabedd185d3d8d2163703e009200141d0096a412041002802a0a1c6800011848080800000200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d00920014295f38cfcb699a3c4ef003703e809200142a8db95cdaa86daa7193703e009200141d0096a412041002802a0a1c68000118480808000004188adc48000411341002802a0a1c6800011848080800000200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d009200142beee8ae9ce8fa2b1977f3703e80920014295d4f9afa2868fb8643703e009200141d0096a412041002802a0a1c6800011848080800000200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d009200142d3d8c5d2a9b9d2c1ac7f3703e80920014282ca868eabf3add0cf003703e009200141106a200141d0096a10e6888080002001290318210520012802102107200142fc90b9e5d09296e7773703b806200142a6d4e6f1a4dd9598603703b006200142bb88f596f8cbf6cf4c3703c8062001428a85cd9fb3e4b2ae6d3703c006200141d0096a200141b0066a412010dd888080000240024020012d00d0090d00200141980c6a4200370300200141900c6a4200370300200141880c6a4200370300200142003703800c0c010b200141980c6a200141e9096a290000370300200141900c6a200141e1096a290000370300200141880c6a200141d9096a290000370300200120012900d1093703800c0b200141a00c6a10ee88808000200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d009200142bad08eede089ffa8123703e809200142bd81f785e387e6aa817f3703e009200141086a200141d0096a412010d088808000200128020c2104024002400240024002400240200128020822024101470d00200141d0096a412041002802a0a1c68000118480808000000c010b2002450d010b2004450d000240200441aad5aad5004b0d002004410c6c2202417f4a0d020b10ae80808000000b41042108410021040c010b4100210341002d00fca3c680001a200241002802c8a3c68000118180808000002208450d01200821020340200141d0096a200310f387808000200241086a200141d0096a41086a280200360200200220012903d0093702002002410c6a21022004200341016a2203470d000b0b200120043602b40c200120083602b00c200120043602ac0c200141b80c6a200141ac0c6a410041002802f0a2c680001185808080000002402005420020071b2205420c540d00200542757c1085888080000b200141d0096a410041002802d8a1c6800011848080800000024020012802d8094120490d00200020012802d4092202290000370028200041c0006a200241186a290000370000200041386a200241106a290000370000200041306a200241086a290000370000024020012802d009450d00200241002802c0a3c68000118080808000000b200020012903800c37000020002005370320200020012900b80c370048200041186a200141800c6a41186a290300370000200041106a200141800c6a41106a290300370000200041086a200141800c6a41086a290300370000200041d0006a200141b80c6a41086a290000370000200041d8006a200141b80c6a41106a290000370000200041e0006a200141b80c6a41186a290000370000200020012902a00c370268200041f0006a200141a00c6a41086a280200360200200141a00d6a2480808080000f0b41c4afc48000412c2001419f0d6a41f0afc480004180b0c48000108981808000000b4104200210b280808000000b862901067f23808080800041e0006b22012480808080002001410036021420014280808080800137020c41002d00fca3c680001a0240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240410841002802c8a3c68000118180808000002202450d002002413a36020420024190b0c48000360200200141013602202001200236021c20014101360218200141186a2001410c6a109b8880800041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d01200341cab0c480003602002003412e36020441002d00fca3c680001a410141002802c8a3c68000118180808000002204450d02200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241003a00602002410136025c2002200336025820024281808080103703502002200436024c2002428e80808010370244200241f8bec38000360240200241b580808000360218200242d7c9cb8fc1cf97db3e37030820024100360200200241106a42e88488d0c0e3aebc1337030041002d00fca3c680001a2001200128021441016a360214410841002802c8a3c68000118180808000002203450d03200341f8b0c480003602002003412936020441002d00fca3c680001a410141002802c8a3c68000118180808000002204450d04200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241013a00602002410136025c2002200336025820024281808080103703502002200436024c2002429080808010370244200241cfc0c380003602402002418881808000360218200242febac4ad81b6fafcb37f37030820024100360200200241106a4298848fa1dab08ba17437030041002d00fca3c680001a2001200128021441016a360214410841002802c8a3c68000118180808000002203450d0520034122360204200341a1b1c48000360200200141c0006a4200370300200141386a4200370300200141306a4200370300200141186a41106a4200370300200141186a41086a420037030020014200370318200141c8006a200141186a108d8d808000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a2202410b360244200241d6bfc38000360240200241cf838080003602182002410036020020022001290348370348200241013a00602002410136025c2002200336025820024101360254200241d0006a200141c8006a41086a280200360200200242e6bc8a93f7f6b7ce33370308200241106a42c4f9f3c5a8b6cbf1663703002001200128021441016a36021441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d06200341c3b1c48000360200200341d00036020441002d00fca3c680001a410141002802c8a3c68000118180808000002204450d07200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241003a00602002410136025c2002200336025820024281808080103703502002200436024c20024290808080103702442002419ebfc38000360240200241b58080800036021820024100360200200242d7c9cb8fc1cf97db3e370308200241106a42e88488d0c0e3aebc133703002001200128021441016a36021441002d00fca3c680001a410841002802c8a3c68000118180808000002202450d082002412636020420024193b2c48000360200200141013602202001200236021c20014101360218200141186a2001410c6a10978880800041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d09200341cf00360204200341b9b2c4800036020041002d00fca3c680001a410141002802c8a3c68000118180808000002204450d0a200441053a000041002d00fca3c680001a410441002802c8a3c68000118180808000002202450d0b200141186a41086a220541003602002001200236021c20014104360218200141003602582001200141d8006a36025c200141dc006a200141186a10c08a808000200141c8006a41086a2206200528020036020020012001290218370348024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a2202410d360244200241899fc38000360240200241ea81808000360238200241b5808080003602202002410136020c20022004360208200242818080801037030020022001290348370348200241013a00602002410136025c2002200336025820024101360254200241d0006a2006280200360200200242e7b0a091f3ed9c85c500370328200242d7c9cb8fc1cf97db3e370310200241306a42b891b68c98adebcf61370300200241186a42e88488d0c0e3aebc133703002001200128021441016a36021441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0c20034188b3c48000360200200341c20036020441002d00fca3c680001a410841002802c8a3c68000118180808000002204450d0d20044200370000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241013a00602002410136025c2002200336025820024288808080103703502002200436024c200242868080808001370244200241bfbec38000360240200241ef8080800036021820024100360200200242a5e9e3ab9e929adc2c370308200241106a4293888c8f89fdc6ec9e7f3703002001200128021441016a36021441002d00fca3c680001a410841002802c8a3c68000118180808000002202450d0e2002411c360204200241cab3c48000360200200141013602202001200236021c20014101360218200141186a2001410c6a10fe8880800041002d00fca3c680001a410841002802c8a3c68000118180808000002202450d0f2002413c360204200241e6b3c48000360200200141013602202001200236021c20014101360218200141186a2001410c6a10808980800041002d00fca3c680001a413841002802c8a3c68000118180808000002202450d1020024190b6c48000360230200241c5b5c480003602282002419cadc4800036022020024191b5c48000360218200241cab4c480003602102002419cadc4800036020820024128360204200241a2b4c48000360200200241346a413f3602002002412c6a41cb00360200200241246a41003602002002411c6a4134360200200241146a41c7003602002002410c6a4100360200200141073602202001200236021c20014107360218200141186a2001410c6a10ff8880800041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d11200341cfb6c480003602002003412e36020441002d00fca3c680001a410441002802c8a3c68000118180808000002204450d1220044100360000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241013a00602002410136025c2002200336025820024284808080103703502002200436024c2002428a808080c00037024420024190bec38000360240200241b580808000360218200242d7c9cb8fc1cf97db3e37030820024100360200200241106a42e88488d0c0e3aebc1337030041002d00fca3c680001a2001200128021441016a36021441d00041002802c8a3c68000118180808000002202450d13200241f2bac480003602482002419fbac48000360240200241c9b9c480003602382002419cadc4800036023020024190b9c48000360228200241bfb8c48000360220200241ebb7c480003602182002419cadc48000360210200241c6b7c48000360208200241c900360204200241fdb6c48000360200200241cc006a41c000360200200241c4006a41d3003602002002413c6a41d600360200200241346a41003602002002412c6a4139360200200241246a41d1003602002002411c6a41d400360200200241146a41003602002002410c6a412536020041002d00fca3c680001a410141002802c8a3c68000118180808000002204450d14200441023a000041002d00fca3c680001a410441002802c8a3c68000118180808000002203450d15200141186a41086a220541003602002001200336021c20014104360218200141003602582001200141d8006a36025c200141dc006a200141186a10c08a808000200141c8006a41086a2206200528020036020020012001290218370348024020012802142203200128020c470d002001410c6a2003109d86808000200128021421030b2001280210200341e8006c6a2203410b360244200341c99ec38000360240200341a782808000360238200341f5818080003602202003410136020c20032004360208200342818080801037030020032001290348370348200341013a00602003410a36025c200320023602582003410a360254200341d0006a2006280200360200200342d39ec9badc8ccad845370328200342c194a6a793ccc3a857370310200341306a42eab19fb291b0f9fb0c370300200341186a42ab8bffbed784ffa5937f3703002001200128021441016a36021441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d16200341b2bbc48000360200200341d50036020441002d00fca3c680001a410141002802c8a3c68000118180808000002204450d17200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241003a00602002410136025c2002200336025820024281808080103703502002200436024c2002429280808010370244200241c5bec38000360240200241d083808000360218200242dfcb98f48cd0bd951f37030820024100360200200241106a42acf0e291d3fdc7d4d20037030041002d00fca3c680001a2001200128021441016a360214410841002802c8a3c68000118180808000002203450d1820034187bcc48000360200200341d30036020441002d00fca3c680001a410141002802c8a3c68000118180808000002204450d19200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241013a00602002410136025c2002200336025820024281808080103703502002200436024c2002429580808010370244200241c1bfc380003602402002418881808000360218200242febac4ad81b6fafcb37f37030820024100360200200241106a4298848fa1dab08ba17437030041002d00fca3c680001a2001200128021441016a360214411041002802c8a3c68000118180808000002203450d1a200341dabcc48000360200200341b1bdc48000360208200341d7003602042003410c6a411236020041002d00fca3c680001a410141002802c8a3c68000118180808000002204450d1b200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241013a00602002410236025c2002200336025820024281808080203703502002200436024c2002429880808010370244200241ffc0c380003602402002418881808000360218200242febac4ad81b6fafcb37f37030820024100360200200241106a4298848fa1dab08ba17437030041002d00fca3c680001a2001200128021441016a360214410841002802c8a3c68000118180808000002203450d1c200341c3bdc480003602002003412236020441002d00fca3c680001a410141002802c8a3c68000118180808000002204450d1d200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241003a00602002410136025c2002200336025820024281808080103703502002200436024c2002428e80808010370244200241f3bfc380003602402002419b82808000360218200242fb9bf4d1a5ccd7cbed0037030820024100360200200241106a42dd9fc4e2a4bae882d40037030041002d00fca3c680001a2001200128021441016a360214410841002802c8a3c68000118180808000002203450d1e2003412e360204200341e5bdc48000360200200141023a0018200141c8006a200141186a10b38c808000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a22024111360244200241d7bec38000360240200241d1838080003602182002410036020020022001290348370348200241003a00602002410136025c2002200336025820024101360254200241d0006a200141c8006a41086a280200360200200242fc969fcca28189f9e200370308200241106a42bc9ecabcb9f0b1b0363703002001410c6a41086a2202200228020041016a2202360200200041086a20023602002000200129020c370200200041106a4106360200200041f9a6c4800036020c200141e0006a2480808080000f0b4104410810b280808000000b4104410810b280808000000b4101410110b280808000000b4104410810b280808000000b4101410110b280808000000b4104410810b280808000000b4104410810b280808000000b4101410110b280808000000b4104410810b280808000000b4104410810b280808000000b4101410110b280808000000b4101410410b280808000000b4104410810b280808000000b4101410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104413810b280808000000b4104410810b280808000000b4101410410b280808000000b410441d00010b280808000000b4101410110b280808000000b4101410410b280808000000b4104410810b280808000000b4101410110b280808000000b4104410810b280808000000b4101410110b280808000000b4104411010b280808000000b4101410110b280808000000b4104410810b280808000000b4101410110b280808000000b4104410810b280808000000bb60f010c7f23808080800041d0026b220124808080800041002d00fca3c680001a0240024002400240024002400240024002400240024041d00241002802c8a3c68000118180808000002202450d00200141186a10e189808000200141c4026a200141186a10d48b80800041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d012003413436020420034193bec48000360200200141086a410c6a200141c4026a41086a280200360200200120012902c40237020c41002d00fca3c680001a410c41002802c8a3c68000118180808000002204450d022004428080f081808080283700002004418080c00236000841002d00fca3c680001a410841002802c8a3c68000118180808000002205450d03200541d3bec480003602002005412a36020441002d00fca3c680001a410841002802c8a3c68000118180808000002206450d042006420a37000041002d00fca3c680001a410841002802c8a3c68000118180808000002207450d0520074188bfc48000360200200741d50036020441002d00fca3c680001a411041002802c8a3c68000118180808000002208450d0620084200370000200841086a420037000041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d07200941c200360204200941ebbfc480003602002001412c6a4100360200200141286a418097c38000360200200141386a4200370200200142003702402001428080808080808080807f3702202001418097c3800036021c200141003a004c2001410036024820014280808080c0003702302001418080808078360218200141c4026a200141186a108e8d80800002402001280218220a418080808078460d00200a450d00200128021c41002802c0a3c68000118080808000000b02402001280224220a418080808078460d00200a450d00200128022841002802c0a3c68000118080808000000b02402001280230220a418080808078460d00200a450d00200128023441002802c0a3c68000118080808000000b41002d00fca3c680001a410841002802c8a3c6800011818080800000220b450d08200b4121360204200b41b5c0c48000360200200141246a200141c4026a41086a280200360200200120012902c40237021c41002d00fca3c680001a410241002802c8a3c6800011818080800000220c450d09200c41003b000041002d00fca3c680001a412841002802c8a3c6800011818080800000220a450d0a200a41a1c2c48000360220200a41d5c1c48000360218200a4187c1c48000360210200a419cadc48000360208200a412a360204200a41ddc0c48000360200200a41246a411c360200200a411c6a41cc00360200200a41146a41ce00360200200a410c6a4100360200200241106a428f96cddec79feed66b370300200242c4c5d9da83adf3df4a370308200241f0016a42fbe0bedd81cff9c317370300200241e8016a42cec9d2b3fca8a6f5bc7f370300200241b8016a42e6d58f99bfb09cd660370300200241b0016a428d9fb78edfb6baf67d37030020024180016a4293888c8f89fdc6ec9e7f370300200241f8006a42a5e9e3ab9e929adc2c370300200241c8006a42f6ef8697d7dfa3a3b07f370300200241c0006a42c0f88c9ce3f4f7fcb27f370300200241d2838080003602182002410c360204200241c7bec480003602002002200129020837021c200241246a200141086a41086a290200370200200241f8016a41f882808000360200200241e4016a4107360200200241d6c0c480003602e001200241dc016a4101360200200241d8016a2009360200200241d0016a429080808010370200200241cc016a2008360200200241c8016a4110360200200241c0016a41d383808000360200200241ac016a4108360200200241adc0c480003602a801200241a4016a4101360200200241a0016a200736020020024198016a42888080801037020020024194016a200636020020024190016a410836020020024188016a41ef80808000360200200241f4006a410e360200200241ddbfc48000360270200241ec006a4101360200200241e8006a2005360200200241e0006a428c80808010370200200241dc006a2004360200200241d8006a410c360200200241d0006a41d4838080003602002002413c6a410b360200200241fdbec4800036023820024101360234200220033602302002410136022c2002418c026a410136020020024190026a200b36020020024194026a4101360200200241bdc2c48000360298022002419c026a410a360200200241b0026a41d583808000360200200241b8026a4102360200200241bc026a200c360200200241c8026a200a360200200241cc026a4105360200200241c0026a4282808080d000370200200241a0026a42d7b9acfdf187c880f100370300200241a8026a42a695d4f0d8d192864537030020024184026a200141186a41086a290200370200200241fc016a2001290218370200200041063602082000200236020420004106360200200141d0026a2480808080000f0b410841d00210b280808000000b4104410810b280808000000b4101410c10b280808000000b4104410810b280808000000b4101410810b280808000000b4104410810b280808000000b4101411010b280808000000b4104410810b280808000000b4104410810b280808000000b4101410210b280808000000b4104412810b280808000000bfa0a01067f024002400240024002400240024002400240024020002d00000e0700010203040506070b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41003a0000200041086a200110ef8c8080000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41013a0000200041046a200110cc88808000200041186a200110ef8c8080000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41023a00000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41033a00002001200241016a2202360208200041016a21000240200128020020026b411f4b0d0020012002412010b182808000200128020821020b2001200241206a360208200128020420026a22012000290000370000200141086a200041086a290000370000200141106a200041106a290000370000200141186a200041186a2900003700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41043a00002001200241016a2202360208200041016a21000240200128020020026b411f4b0d0020012002412010b182808000200128020821020b2001200241206a360208200128020420026a22012000290000370000200141086a200041086a290000370000200141106a200041106a290000370000200141186a200041186a2900003700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41053a00002001200241016a2203360208200041016a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441086a200241086a290000370000200441106a200241106a290000370000200441186a200241186a2900003700002001200341206a220336020841002d00fca3c680001a412041002802c8a3c68000118180808000002202450d0220022000290021370000200241186a200041396a290000370000200241106a200041316a290000370000200241086a200041296a2900003700000240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22002002290000370000200041086a200241086a290000370000200041106a200241106a290000370000200041186a200241186a2900003700002001200341206a360208200241002802c0a3c68000118080808000000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41063a00002001200241016a220336020841002d00fca3c680001a412041002802c8a3c68000118180808000002202450d0220022000290001370000200241186a2205200041196a290000370000200241106a2206200041116a290000370000200241086a2207200041096a2900003700000240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441086a2007290000370000200441106a2006290000370000200441186a20052900003700002001200341206a2203360208200241002802c0a3c680001180808080000020002d00212100024020012802002003470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a20003a00000b0f0b4101412010b280808000000b4101412010b280808000000bdc0203017f017e027f410021010240024002400240024020002d00000e0700010402020203040b4103210102402000290308220242c000540d0041042101200242808001540d00410621012002428080808004540d00410b200279a74103766b21010b410121030240200041106a290300220242c000540d0041022103200242808001540d00410421032002428080808004540d004109200279a74103766b21030b200320016a21010c030b20002d000441027441e4c9c480006a21034103210102402000290318220242c000540d0041042101200242808001540d00410621012002428080808004540d00410b200279a74103766b21010b20032802002104410121030240200041206a290300220242c000540d0041022103200242808001540d00410421032002428080808004540d004109200279a74103766b21030b200120046a20036a21010c020b412021010c010b410121010b200141016a0bc70801037f02400240024002400240024002400240024002400240024002400240024002400240024020012d0000417f6a0e0b000102030405060708090a000b200141086a28020021024101210302402001410c6a2802002201450d002001417f4c0d0b41002d00fca3c680001a200141002802c8a3c68000118180808000002203450d0c0b20032002200110848e80800021032000410c6a2001360200200041086a200336020020002001360204200041013a00000f0b20002001290308370308200041023a00000f0b200141086a2802002103024002402001410c6a28020022010d00410121020c010b2001417f4c0d0941002d00fca3c680001a200141002802c8a3c68000118180808000002202450d0b0b20022003200110848e80800021032000410c6a2001360200200041086a200336020020002001360204200041033a00000f0b200141086a2802002103024002402001410c6a28020022010d00410121020c010b2001417f4c0d0841002d00fca3c680001a200141002802c8a3c68000118180808000002202450d0b0b20022003200110848e80800021032000410c6a2001360200200041086a200336020020002001360204200041043a00000f0b200041046a200141046a108c87808000200041053a00000f0b200041046a200141046a108a87808000200041063a00000f0b2001410c6a280200210202400240200141106a28020022030d00410121040c010b2003417f4c0d0541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d090b20042002200310848e8080002102200041106a20033602002000410c6a20023602002000200336020820002001280204360204200041073a00000f0b200141086a2802002103024002402001410c6a28020022010d00410121020c010b2001417f4c0d0441002d00fca3c680001a200141002802c8a3c68000118180808000002202450d090b20022003200110848e80800021032000410c6a2001360200200041086a200336020020002001360204200041083a00000f0b20002001290001370001200041196a200141196a290000370000200041116a200141116a290000370000200041096a200141096a290000370000200041093a00000f0b20002001290001370001200041196a200141196a290000370000200041116a200141116a290000370000200041096a200141096a2900003700002000410a3a00000f0b200141086a2802002103024002402001410c6a28020022010d00410121020c010b2001417f4c0d0141002d00fca3c680001a200141002802c8a3c68000118180808000002202450d070b20022003200110848e80800021032000410c6a2001360200200041086a2003360200200020013602042000410b3a00000f0b10ae80808000000b4101200110b280808000000b4101200110b280808000000b4101200110b280808000000b4101200310b280808000000b4101200110b280808000000b4101200110b280808000000bae0603057f017e027f23808080800041c0006b2201248080808000200141186a41c7c2c48000410441d2aac480004114419cadc48000410010d882808000200141106a42043702002001420037020820014280808080800137020041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241cbc2c4800036020020014101360238200120023602302001200241206a36023c200120023602342001200141306a10fa8680800041002d00fca3c680001a20012802002103200128020421022001280208210420012802182105200129021c2106410841002802c8a3c68000118180808000002207450d0120074100290390c3c480003702002001410036020820014280808080c000370200200141306a200141f0a8c48000410610b18b8080002001200141306a41f6a8c48000410e10988b808000200141306a20014184a9c48000410810868b8080002001200141306a418ca9c48000411710918b808000200141306a200141a3a9c48000410b10838b8080002001200141306a41aea9c48000410c109f8b808000200141306a200141baa9c48000410b10af8b8080002001200141306a41c5a9c48000411110c68b808000200141306a200141d6a9c48000411110d38b8080002001200141306a41e7a9c48000412010a48b808000200141246a20014187aac480004118108d8b8080002001200128022436020820012001280228220836020020012008200128022c41246c6a36020c20012008360204200141306a200110fb868080002005418080808078460d022001200336020820012002360200200120023602042001200220044105746a36020c200041c4006a200110fa868080002001410b6a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a0000200041d8006a4101360200200041d4006a2007360200200041013602502001200129023037000320002001290000370001200041086a200141076a290000370000200141c0006a2480808080000f0b4108412010b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000bb11303067f017e037f23808080800041c0006b2201248080808000200141206a4198c3c48000410541d2aac480004114419cadc48000410010d882808000200141186a42043702002001420037021020014280808080800137020841002d00fca3c680001a024002400240024002400240024002400240024002400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241cbc2c4800036020020014101360238200120023602302001200241206a36023c20012002360234200141086a200141306a10fa8680800041002d00fca3c680001a20012802082103200128020c2104200128021021052001280220210620012902242107410841002802c8a3c68000118180808000002208450d01200841002903b8c3c4800037020041002d00fca3c680001a41002802c8a3c6800021022001410036021020014280808080c00037020841102002118180808000002209450d02200941086a41002902d8f3c48000370200200941002902d0f3c48000370200200141086a410010a086808000200128020c200141086a41086a220a28020041246c6a220241003a00202002410f36021c200241c0c3c4800036021820024102360214200220093602102002428080808020370208200242808080808001370200200141306a41086a200a28020041016a3602002001200129020837033041002d00fca3c680001a411041002802c8a3c68000118180808000002209450d03200941086a41002902b8f4c48000370200200941002902b0f4c480003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241013a00202002411a36021c200241cfc3c4800036021820024102360214200220093602102002428080808020370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a411841002802c8a3c68000118180808000002209450d04200941106a41002902b8f6c48000370200200941086a41002902b0f6c48000370200200941002902a8f6c480003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241023a00202002411d36021c200241e9c3c4800036021820024103360214200220093602102002428080808030370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0520094100290380f5c480003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241033a00202002411336021c20024186c4c4800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d06200941002903b0f8c480003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241043a00202002410f36021c20024199c4c4800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d07200941002903f8f6c480003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241053a00202002410c36021c200241a8c4c4800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d08200941002903d8f7c480003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241063a00202002411b36021c200241b4c4c4800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d09200941002903a0f5c480003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241073a00202002411136021c200241cfc4c4800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0a200941002903e0f8c480003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241083a00202002410c36021c200241e0c4c480003602182002410136021420022009360210200242808080801037020820024280808080800137020020012802102109200128020c2102200120012802083602102001200236020820012002200941246c6a41246a3602142001200236020c200141306a200141086a10fb868080002006418080808078460d0b20012003360210200120043602082001200436020c2001200420054105746a360214200041c4006a200141086a10fa86808000200141136a200141306a41086a2802003600002000413c6a200737020020002006360238200041013a0000200041d8006a4101360200200041d4006a2008360200200041013602502001200129023037000b20002001290008370001200041086a2001410f6a290000370000200141c0006a2480808080000f0b4108412010b280808000000b4104410810b280808000000b4104411010b280808000000b4104411010b280808000000b4104411810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000b8a0703067f017e027f23808080800041c0006b2201248080808000200141186a41ecc4c48000410541d2aac480004114419cadc48000410010d882808000200141106a42043702002001420037020820014280808080800137020041002d00fca3c680001a0240024002400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241cbc2c4800036020020014101360238200120023602302001200241206a36023c200120023602342001200141306a10fa8680800041002d00fca3c680001a20012802002103200128020421042001280208210520012802182106200129021c2107410841002802c8a3c68000118180808000002208450d0120084100290390c5c480003702002001410036020820014280808080c000370200200141306a20014198c5c48000411010b48b8080002001200141306a41a8c5c48000410f10fd8a80800041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d02200941002903a0f9c480003702000240200128020822022001280200470d002001200210a086808000200128020821020b2001280204200241246c6a220241023a00202002410b36021c200241b7c5c4800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a2209200141086a28020041016a360200200120012902003703302001200141306a41c2c5c48000410a10a88b808000200141306a200141ccc5c48000410d10cb8b8080002001200141306a41d9c5c48000410810878b808000200141246a200141e1c5c48000411110ff8a8080002001200128022436020820012001280228220236020020012002200128022c41246c6a36020c20012002360204200141306a200110fb868080002006418080808078460d032001200336020820012004360200200120043602042001200420054105746a36020c200041c4006a200110fa868080002001410b6a20092802003600002000413c6a200737020020002006360238200041013a0000200041d8006a4101360200200041d4006a2008360200200041013602502001200129023037000320002001290000370001200041086a200141076a290000370000200141c0006a2480808080000f0b4108412010b280808000000b4104410810b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000be60d03037f017e017f23808080800041f0006b2202248080808000024002400240024002400240024002400240024002400240024002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020020042d00000e0c01020304050607080d090a0b0d0b200041003a00000c0d0b2002200110bc8a8080000240024020022802000d00200241d4006a2001200228020410d0858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241cf006a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a290000370000410121030c010b410021030b200020033a00000c0c0b02402001280200220328020422014108490d00200041023a00002003200141786a36020420032003280200220141086a360200200020012900003703080c0c0b200041003a00000c0b0b200241086a200110bc8a808000024020022802080d00200241d4006a2001200228020c10d0858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241cf006a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a290000370000200041033a00000c0b0b200041003a00000c0a0b200241106a200110bc8a808000024020022802100d00200241d4006a2001200228021410d0858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241cf006a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a290000370000200041043a00000c0a0b200041003a00000c090b200241186a200110bc8a808000024020022802180d00200241d4006a2001200228021c10cf858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241cf006a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a290000370000200041053a00000c090b200041003a00000c080b200241206a200110bc8a808000024020022802200d00200241d4006a2001200228022410cd858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241cf006a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a290000370000200041063a00000c080b200041003a00000c070b200241286a200110bc8a80800020022802280d04200241c4006a2001200228022c10d08580800020022802442203418080808078460d042002280248210402402001280200220128020422064104490d002000200228024c3602102000200436020c20002003360208200041073a000020012006417c6a36020420012001280200220341046a360200200020032800003602040c070b200041003a00002003450d06200441002802c0a3c68000118080808000000c060b200241306a200110bc8a808000024020022802300d00200241d4006a2001200228023410d0858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241cf006a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a290000370000200041083a00000c060b200041003a00000c050b4100210302402001280200220128020422044120490d002001200441606a36020420012001280200220441206a3602002000200429000037000141092103200041096a200441086a290000370000200041116a200441106a290000370000200041196a200441186a2900003700000b200020033a00000c040b4100210302402001280200220128020422044120490d002001200441606a36020420012001280200220341206a36020020002003290000370001200041096a200341086a290000370000200041116a200341106a290000370000200041196a200341186a290000370000410a21030b200020033a00000c030b200241386a200110bc8a808000024020022802380d00200241d4006a2001200228023c10d0858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241c4006a410b6a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a2900003700002000410b3a00000c030b200041003a00000c020b200041003a00000c010b200041003a00000b200241f0006a2480808080000bbe1203037f017e037f23808080800041106b22022480808080000240024002400240024002400240024002400240024002400240024020002d0000417f6a0e0b000102030405060708090a0b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41003a0000200041086a280200210420022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822006b20034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a3602080c0a0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41013a00002001200341016a2203360208200029030821050240200128020020036b41074b0d0020012003410810b182808000200128020821030b2001200341086a360208200128020420036a20053700000c090b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41023a0000200041086a280200210420022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822006b20034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a3602080c080b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41033a0000200041086a280200210420022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822006b20034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a3602080c070b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41043a0000200041086a280200210320022000410c6a28020022003602082002200241086a36020c2002410c6a200110c08a8080002000450d06200041186c210003402003200110fe86808000200341186a2103200041686a22000d000c070b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41053a0000200041086a280200210420022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a8080002003450d052003410c6c2106200441086a210003402000417c6a28020021072002200028020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822046b20034f0d0020012004200310b182808000200128020821040b200128020420046a2007200310848e8080001a2001200420036a3602082000410c6a2100200641746a22060d000c060b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41063a00002000410c6a28020021062002200041106a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822046b20034f0d0020012004200310b182808000200128020821040b200128020420046a2006200310848e8080001a2001200420036a2203360208200028020421000240200128020020036b41034b0d0020012003410410b182808000200128020821030b2001200341046a360208200128020420036a20003600000c040b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41073a0000200041086a280200210420022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822006b20034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a3602080c030b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41093a00002001200341016a220436020841002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0320032000290001370000200341186a2206200041196a290000370000200341106a2207200041116a290000370000200341086a2208200041096a2900003700000240200128020020046b411f4b0d0020012004412010b182808000200128020821040b200128020420046a22002003290000370000200041086a2008290000370000200041106a2007290000370000200041186a20062900003700002001200441206a360208200341002802c0a3c68000118080808000000c020b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a410a3a00002001200341016a220436020841002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0320032000290001370000200341186a2206200041196a290000370000200341106a2207200041116a290000370000200341086a2208200041096a2900003700000240200128020020046b411f4b0d0020012004412010b182808000200128020821040b200128020420046a22002003290000370000200041086a2008290000370000200041106a2007290000370000200041186a20062900003700002001200441206a360208200341002802c0a3c68000118080808000000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a410b3a0000200041086a280200210420022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822006b20034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a3602080b200241106a2480808080000f0b4101412010b280808000000b4101412010b280808000000bd30202017f027e23808080800041d0006b2201248080808000024020002903002202500d0020014298d5afd2c6aeacae2f370328200142c2cdc8b0c7b9e78f857f370320200142e4c5bdb4b2e9a5a6807f370338200142d790d7a3fef9fda0c800370330200141106a200141206a10e688808000200129031821032001280210210020014298d5afd2c6aeacae2f370328200142c2cdc8b0c7b9e78f857f370320200142e4c5bdb4b2e9a5a6807f370338200142d790d7a3fef9fda0c8003703302001200141206a10e68880800020014298d5afd2c6aeacae2f370328200142c2cdc8b0c7b9e78f857f370320200142e4c5bdb4b2e9a5a6807f370338200142d790d7a3fef9fda0c800370330200142002003420020001b220320027d220220022003561b370348200141206a4120200141c8006a410841002802e0a1c68000118680808000000b200141d0006a2480808080000bcd0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41f2c5c48000410841fac5c480004123419cadc48000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242d1c5efcdcd82cde1ff003703082002419fc6c480003602202002419382808000360218200241023602042002419dc6c48000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a429ccab49c93e2e495cf00370300200241386a41ef80808000360200200241246a410736020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024202370224200241d0fbc480003602202002410236021c200241cefbc4800036021820024193828080003602102002429ccab49c93e2e495cf00370308200242d1c5efcdcd82cde1ff00370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024207370224200241c7fbc480003602202002410636021c200241c1fbc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bcb0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41f2c5c48000410841fac5c480004123419cadc48000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d002002429fb2eafebdc29397f7003703082002419fc6c48000360220200241d683808000360218200241023602042002419dc6c48000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a42e0f4d5c484838cd222370300200241386a41ef80808000360200200241246a410736020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024202370224200241d0fbc480003602202002410236021c200241cefbc48000360218200241d683808000360210200242e0f4d5c484838cd2223703082002429fb2eafebdc29397f700370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024207370224200241c7fbc480003602202002410636021c200241c1fbc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b4000200042fc90b9e5d09296e777370008200042a6d4e6f1a4dd959860370000200042d3d8c5d2a9b9d2c1ac7f37001820004282ca868eabf3add0cf003700100b3e00200042fc90b9e5d09296e777370008200042a6d4e6f1a4dd959860370000200042f7b6fccfd083b2cf4d370018200042afd2c6ffdbc495bc083700100b820702047f057e23808080800041c0016b2201248080808000200142fc90b9e5d09296e777370338200142a6d4e6f1a4dd959860370330200141fc006a200141306a411041002802c0a1c680001185808080000002400240200128027c2202418080808078460d00200128028001210302402001280284014110490d00200141306a2003411010888e808000210402402002450d00200341002802c0a3c68000118080808000000b20040d010c020b2002450d00200341002802c0a3c68000118080808000000b41002102200141003b014e02400240417f4100280284a4c680002203410347200341034b1b2203450d00200341ff017141ff01470d010b200141d8006a410c6a41d783808000360200200141838280800036025c20014106360254200141f9a6c480003602502001200141ce006a3602602001200141d0006a3602584100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141b4016a4202370200200141ac016a4103360200200141a4016a4116360200200141a0016a41bcaac4800036020020014194016a4195a8c48000ad4280808080b00b84370200200141fc006a410c6a41d2aac48000ad4280808080c00284370200200141b0016a200141d8006a360200200141fcabc480003602a8012001410336029c012001410036029001200141003602840120014281808080c02037027c200341ecf2c08000200441024622041b200141fc006a200241d4f2c0800020041b2802101184808080000020012f014e21020b200141fc006a41f9a6c48000410641002802a0a3c6800011858080800000200141fc006a41106a220341bccdc48000411541002802a0a3c6800011858080800000200141d8006a41186a200141fc006a41186a290000370300200141d8006a41106a2003290000370300200141d8006a41086a200141fc006a41086a2900003703002001200129007c370358200120023b017c200141d8006a4120200141fc006a410241002802e0a1c68000118680808000000b200141206a10ad878080002001290320210520012903282106200141106a10bd888080002001290318210720012903102108200110998c808000200129030021092000427f427f200620077c220720072006541b220620012903087c220720072006541b3703082000427f2009427f200520087c220620062005541b22057c220620062005541b370300200141c0016a2480808080000b930201027f23808080800041106b22022480808080002002200036020020022001280214419cc9c48000410e200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241acc9c48000108d81808000210120022d000c210002400240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010b100020004200370308200042003703000b2100200128021441d8c8c480004114200141186a28020028020c118280808000000bcb0601017f0240024020002d00000e03010001000b2000280204220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00240e03010001000b200041286a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00480e03010001000b200041cc006a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d006c0e03010001000b200041f0006a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d0090010e03010001000b20004194016a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00b4010e03010001000b200041b8016a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00d8010e03010001000b200041dc016a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00fc010e03010001000b20004180026a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00a0020e03010001000b200041a4026a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00c4020e03010001000b200041c8026a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00e8020e03010001000b200041ec026a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d008c030e03010001000b20004190036a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00b0030e03010001000b200041b4036a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00d4030e03010001000b200041d8036a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00f8030e03010001000b200041fc036a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d009c040e03010001000b200041a0046a280200220010848a808000200041002802c0a3c68000118080808000000b0b02000b02000b890501037f23808080800041a0016b22032480808080002003200136023c20034100360238200320023602342003200341346a10bc8a80800002400240024020032802000d00200328020421042003200328023841016a22013602382001200328023c4b0d00200341003a007f200320043602980120034100360294012003200341346a360290012003200341ff006a36029c0120034180016a20034190016a10a087808000024020032d007f450d0020034180016a10a68d8080000c010b200341f0006a41086a220520034180016a41086a280200360200200320032903800137037020032003280238417f6a36023802402003280234220128020422044120490d002001200441606a36020420012001280200220441206a360200200341c0006a41086a22012005280200360200200341c0006a41146a200441086a290000370200200341c0006a411c6a200441106a290000370200200341c0006a41246a200441186a2900003702002003200429000037024c200341086a41086a2001290300370300200341086a41106a2201200341c0006a41106a290300370300200341086a41186a2204200341c0006a41186a290300370300200341086a41206a200341c0006a41206a290300370300200341086a41286a2205200341c0006a41286a2802003602002003200329037037030820022802040d02200020032903083702042000412c6a2005280200360200200041246a200341286a2903003702002000411c6a2004290300370200200041146a20012903003702002000410c6a200341106a290300370200200041003602000c030b200341f0006a10a68d8080000b200041013602000c010b200341086a10a68d808000200041013602000b200341a0016a2480808080000b210020012802144198c3c480004105200141186a28020028020c118280808000000b8f1401047f23808080800041900a6b220224808080800002400240024002400240024002400240024002400240024020012802000e050100020304010b20024180056a200141046a10e48d808000024020012802100d000240200141186a28020022034100480d00200341f5ffffff074f0d06200141146a280200210402402003410b6a417c7122050d00410421010c0b0b41002d00fca3c680001a200541002802c8a3c680001181808080000022010d0a4104200510b280808000000b41fc9bc68000412b200241106a41a89cc6800041b89cc68000108981808000000b200141186a28020022034120470d052002411c6a200141146a280200220141086a290000370200200241106a41146a200141106a2900003702002002412c6a200141186a29000037020020022001290000370214410021010c090b200041023602000c090b20024180056a200141046a10e48d808000200241106a200141106a10838a808000024020022d001022014102460d00200020022f00113b00052000200229021837020c200041076a20022d00133a0000200041146a200241106a41106a2902003702002000411c6a200241106a41186a290200370200200041246a200241106a41206a28020036020020022802142103200041d0006a200241a8056a290200370200200041c8006a20024180056a41206a290200370200200041c0006a20024180056a41186a290200370200200041386a20024180056a41106a290200370200200041306a20024188056a290200370200200020022902800537022820002003360208200020013a0004200041043602000c090b20022802142101200041083602002000200136020420022802a8054129490d0820022802800541002802c0a3c68000118080808000000c080b200241023a00ac04200241023a008804200241023a00e403200241023a00c003200241023a009c03200241023a00f802200241023a00d402200241023a00b002200241023a008c02200241023a00e801200241023a00c401200241023a00a001200241023a007c200241023a0058200241023a0034200241023a001041002104200241003602d00420024100360288052002200141c4016a2205360284052002200141046a360280052002200241d0046a360290052002200241106a36028c0520024180056a10828a808000024020022802d0042203450d002000410836020020002003360204200241106a10b18a8080000c080b20024180056a200241106a41c00410848e8080001a0240200528020022034102460d000240024020030d002002200141c8016a2802002203200141cc016a280200220410e98d8080002002280200210120022802042105200241d8046a200320044100280298a3c6800011858080800000200220053602d4040c010b200141cc016a28020022034120470d05200241dc046a200141c8016a280200220141086a290000370200200241e4046a200141106a290000370200200241ec046a200141186a290000370200200220012900003702d404410021010b200241e8096a41086a200241d0046a41086a290200370300200241e8096a41106a200241d0046a41106a290200370300200241e8096a41186a200241d0046a41186a290200370300200241e8096a41206a200241d0046a41206a290200370300200220013602d004200220022902d0043703e809410121040b200041306a20024180056a41c00410848e8080001a200041286a200241e8096a41206a290300370200200041206a200241e8096a41186a290300370200200041186a200241e8096a41106a290300370200200041106a200241f0096a290300370200200020022903e80937020820002004360204200041053602000c070b200241023a00ac04200241023a008804200241023a00e403200241023a00c003200241023a009c03200241023a00f802200241023a00d402200241023a00b002200241023a008c02200241023a00e801200241023a00c401200241023a00a001200241023a007c200241023a0058200241023a0034200241023a001041002104200241003602d00420024100360288052002200141d0016a360284052002200141106a360280052002200241d0046a360290052002200241106a36028c0520024180056a10898a808000024020022802d0042203450d002000410836020020002003360204200241106a10b18a8080000c070b200241d0046a200141046a10e48d80800020024180056a200241106a41c00410848e8080001a024020012802d00122034102460d000240024020030d00200241086a200141d4016a2802002203200141d8016a280200220410e98d80800020022802082101200228020c2105200241f0096a200320044100280298a3c6800011858080800000200220053602ec090c010b200141d8016a28020022034120470d05200241f4096a200141d4016a280200220141086a290000370200200241fc096a200141106a290000370200200241840a6a200141186a290000370200200220012900003702ec09410021010b200241c0096a41086a200241e8096a41086a290200370300200241c0096a41106a200241e8096a41106a290200370300200241c0096a41186a200241e8096a41186a290200370300200241c0096a41206a200241e8096a41206a290200370300200220013602e809200220022902e8093703c009410121040b200020022902d0043702ec0420004194056a200241f8046a2902003702002000418c056a200241d0046a41206a29020037020020004184056a200241d0046a41186a290200370200200041fc046a200241d0046a41106a290200370200200041f4046a200241d0046a41086a2902003702002000412c6a20024180056a41c00410848e8080001a200041246a200241c0096a41206a2903003702002000411c6a200241c0096a41186a290300370200200041146a200241c0096a41106a2903003702002000410c6a200241c0096a41086a290300370200200020022903c009370204200020043602000c060b41e484c08000412b200241106a419085c08000419086c08000108981808000000b41202003418cc9c4800010a281808000000b41202003418cc9c4800010a281808000000b41202003418cc9c4800010a281808000000b2001428180808010370200200141086a2004200310848e8080001a200241106a41086a200420034100280298a3c6800011858080800000200220033602140b20002002290280053702042000410c6a20024180056a41086a290200370200200041146a20024180056a41106a2902003702002000411c6a20024180056a41186a290200370200200041246a20024180056a41206a2902003702002000412c6a200241a8056a29020037020020022001360210200020022902103702342000413c6a200241106a41086a290200370200200041c4006a200241106a41106a290200370200200041cc006a200241106a41186a290200370200200041d4006a200241106a41206a290200370200200041033602000b200241900a6a2480808080000b8f1401047f23808080800041900a6b220224808080800002400240024002400240024002400240024002400240024020012802000e050100020304010b20024180056a200141046a10e48d808000024020012802100d000240200141186a28020022034100480d00200341f5ffffff074f0d06200141146a280200210402402003410b6a417c7122050d00410421010c0b0b41002d00fca3c680001a200541002802c8a3c680001181808080000022010d0a4104200510b280808000000b41fc9bc68000412b200241106a41a89cc6800041b89cc68000108981808000000b200141186a28020022034120470d052002411c6a200141146a280200220141086a290000370200200241106a41146a200141106a2900003702002002412c6a200141186a29000037020020022001290000370214410021010c090b200041023602000c090b20024180056a200141046a10e48d808000200241106a200141106a10878a808000024020022d001022014102460d00200020022f00113b00052000200229021837020c200041076a20022d00133a0000200041146a200241106a41106a2902003702002000411c6a200241106a41186a290200370200200041246a200241106a41206a28020036020020022802142103200041d0006a200241a8056a290200370200200041c8006a20024180056a41206a290200370200200041c0006a20024180056a41186a290200370200200041386a20024180056a41106a290200370200200041306a20024188056a290200370200200020022902800537022820002003360208200020013a0004200041043602000c090b20022802142101200041083602002000200136020420022802a8054129490d0820022802800541002802c0a3c68000118080808000000c080b200241023a00ac04200241023a008804200241023a00e403200241023a00c003200241023a009c03200241023a00f802200241023a00d402200241023a00b002200241023a008c02200241023a00e801200241023a00c401200241023a00a001200241023a007c200241023a0058200241023a0034200241023a001041002104200241003602d00420024100360288052002200141c4016a2205360284052002200141046a360280052002200241d0046a360290052002200241106a36028c0520024180056a10888a808000024020022802d0042203450d002000410836020020002003360204200241106a10b18a8080000c080b20024180056a200241106a41c00410848e8080001a0240200528020022034102460d000240024020030d002002200141c8016a2802002203200141cc016a280200220410e98d8080002002280200210120022802042105200241d8046a200320044100280298a3c6800011858080800000200220053602d4040c010b200141cc016a28020022034120470d05200241dc046a200141c8016a280200220141086a290000370200200241e4046a200141106a290000370200200241ec046a200141186a290000370200200220012900003702d404410021010b200241e8096a41086a200241d0046a41086a290200370300200241e8096a41106a200241d0046a41106a290200370300200241e8096a41186a200241d0046a41186a290200370300200241e8096a41206a200241d0046a41206a290200370300200220013602d004200220022902d0043703e809410121040b200041306a20024180056a41c00410848e8080001a200041286a200241e8096a41206a290300370200200041206a200241e8096a41186a290300370200200041186a200241e8096a41106a290300370200200041106a200241f0096a290300370200200020022903e80937020820002004360204200041053602000c070b200241023a00ac04200241023a008804200241023a00e403200241023a00c003200241023a009c03200241023a00f802200241023a00d402200241023a00b002200241023a008c02200241023a00e801200241023a00c401200241023a00a001200241023a007c200241023a0058200241023a0034200241023a001041002104200241003602d00420024100360288052002200141d0016a360284052002200141106a360280052002200241d0046a360290052002200241106a36028c0520024180056a10868a808000024020022802d0042203450d002000410836020020002003360204200241106a10b18a8080000c070b200241d0046a200141046a10e48d80800020024180056a200241106a41c00410848e8080001a024020012802d00122034102460d000240024020030d00200241086a200141d4016a2802002203200141d8016a280200220410e98d80800020022802082101200228020c2105200241f0096a200320044100280298a3c6800011858080800000200220053602ec090c010b200141d8016a28020022034120470d05200241f4096a200141d4016a280200220141086a290000370200200241fc096a200141106a290000370200200241840a6a200141186a290000370200200220012900003702ec09410021010b200241c0096a41086a200241e8096a41086a290200370300200241c0096a41106a200241e8096a41106a290200370300200241c0096a41186a200241e8096a41186a290200370300200241c0096a41206a200241e8096a41206a290200370300200220013602e809200220022902e8093703c009410121040b200020022902d0043702ec0420004194056a200241f8046a2902003702002000418c056a200241d0046a41206a29020037020020004184056a200241d0046a41186a290200370200200041fc046a200241d0046a41106a290200370200200041f4046a200241d0046a41086a2902003702002000412c6a20024180056a41c00410848e8080001a200041246a200241c0096a41206a2903003702002000411c6a200241c0096a41186a290300370200200041146a200241c0096a41106a2903003702002000410c6a200241c0096a41086a290300370200200020022903c009370204200020043602000c060b41e484c08000412b200241106a419085c08000419086c08000108981808000000b41202003418cc9c4800010a281808000000b41202003418cc9c4800010a281808000000b41202003418cc9c4800010a281808000000b2001428180808010370200200141086a2004200310848e8080001a200241106a41086a200420034100280298a3c6800011858080800000200220033602140b20002002290280053702042000410c6a20024180056a41086a290200370200200041146a20024180056a41106a2902003702002000411c6a20024180056a41186a290200370200200041246a20024180056a41206a2902003702002000412c6a200241a8056a29020037020020022001360210200020022902103702342000413c6a200241106a41086a290200370200200041c4006a200241106a41106a290200370200200041cc006a200241106a41186a290200370200200041d4006a200241106a41206a290200370200200041033602000b200241900a6a2480808080000bcd03010a7f23808080800041c0006b2201248080808000024002400240200010b487808000450d00200142919fd78da1a1ad84ff003703282001429ceccef7a6c0dedd2037032020014282fceae49dd9e2861d370338200142de8c84a1ecd0a6d30c370330200141146a200141206a10e2888080000240024020012802142202418080808078470d00410821034100210241002104410821050c010b200128021821030240200128021c22040d0041082105410021040c010b200441b3e6cc194b0d02200441286c2206417f4c0d024100210741002d00fca3c680001a200641002802c8a3c68000118180808000002205450d032004210820032109034020062007460d0120092903202100200520076a220a2009290300370300200a41186a200941186a290300370300200a41106a200941106a290300370300200a41086a200941086a290300370300200a41206a2000370300200741286a2107200941286a21092008417f6a22080d000b0b200120043602102001200536020c20012004360208200120043602282001200336022420012002360220200141206a200141086a4100200910b3878080000b200141c0006a2480808080000f0b10ae80808000000b4108200610b280808000000bcf0201027f23808080800041306b2203248080808000024002400240024020022d0000410b470d002003410c6a200241086a2802002002410c6a280200108b8a80800020032d000c0d0041002d00fca3c680001a410c41002802c8a3c68000118180808000002204450d0241002d00fca3c680001a412041002802c8a3c68000118180808000002202450d032002200329000d370000200241186a200341256a290000370000200241106a2003411d6a290000370000200241086a200341156a290000370000200041013a00282000410136022420002004360220200042808080801037031820004280808080c0003703102000427f370308200042e4003703002004412036020820042002360204200441203602000c010b2000418080808078360210200041003b01000b200341306a2480808080000f0b4104410c10b280808000000b4101412010b280808000000bb40202017f027e4101210242cd0b21034288da9fed002104024002400240024002400240024002400240024020012d0000417f6a0e0b0009010102030405060607000b2001410c6a3502004283037e42d0b8c5007c21040c070b42db8b04210342f88ef9fab10221040c070b2001410c6a35020042d2eb84307e4280e585017c2104420021030c060b2001410c6a35020042d0a2fa2f7e4298e383017c2104420021030c050b41012102200128020441016a2201417f20011bad220442c6007e4286017c2103200442d1a6983c7e42e0d1fe017c21040c040b2001410c6a35020042ef0a7e42efb29c017c21040c020b4200210342f889a43421040c020b42db8b04210342f8b999efc70221040c010b41002102420021030b200041003a0011200020023a001020002003370308200020043703000b840301077f4101210202400240024002402001280208220341016a2204200128020422054d0d000c010b200320054f0d012001200436020802400240024002400240024002402001280200220620036a2d000022074103710e0400010203000b20074102762108410021020c060b200341026a220320054b0d05200120033602082004417f470d02417f200341dcc1c28000109681808000000b200341046a220320054b0d04200120033602082004417d490d022004200341dcc1c28000109681808000000b20074104490d020c030b200620046a2d000041087420077241ffff03712201410276210820014180024921020c020b200620046a220141026a2d000041187420012f000041087472200772220141027621082001418080044921020c010b200341056a220320054b0d00200120033602082004417c4f0d02200620046a28000022084180808080044921020b20002008360204200020023602000f0b2003200541ecc1c2800010f980808000000b2004200341dcc1c28000109681808000000bae0201057f024002402001280200220128020422020d00410121030c010b20012002417f6a22043602044101210320012001280200220541016a36020002400240024002400240024020052d000022064103710e0400010402000b20064102762104410021030c050b20040d010c040b20064104490d020c030b20012002417e6a3602042001200541026a36020020052d000141087420067241ffff03712201410276210420014180024921030c020b20024104490d0120012002417c6a3602042001200541046a360200200541036a2d000041187420052f000141087472200672220141027621042001418080044921030c010b20024105490d0020012002417b6a3602042001200541056a360200200528000122044180808080044921030b20002004360204200020033602000ba90201057f02400240200128020422020d00410121030c010b20012002417f6a22043602044101210320012001280200220541016a36020002400240024002400240024020052d000022064103710e0400010402000b20064102762104410021030c050b20040d010c040b20064104490d020c030b20012002417e6a3602042001200541026a36020020052d000141087420067241ffff03712201410276210420014180024921030c020b20024104490d0120012002417c6a3602042001200541046a360200200541036a2d000041187420052f000141087472200672220141027621042001418080044921030c010b20024105490d0020012002417b6a3602042001200541056a360200200528000122044180808080044921030b20002004360204200020033602000bf40302067f017e024002400240200128020022012802042202450d0020012002417f6a22033602044101210420012001280200220541016a2206360200024002400240024020052d000022074103710e0400010203000b2007410276ad2108410021040c050b2003450d0320012002417e6a3602042001200541026a36020020052d000141087420077241ffff037122014180024921042001410276ad21080c040b20024104490d0320012002417c6a3602042001200541046a360200200541036a2d000041187420052f0001410874722007722201418080044921042001410276ad21080c030b024002400240200741027622030e050102020200020b20024109490d022001200241776a3602042001200541096a36020020052900012208428080808080808080015421040c040b20024105490d0120012002417b6a3602042001200541056a360200200535000122084280808080045421040c030b200741134b0d00200341046a21072002417e6a2104420021084100210203402004417f460d01200120043602042001200641016a22053602002004417f6a210420063100002002410374413871ad86200884210820052106200241016a220241ff01712007490d000b410021042008427f412820034103746b413871ad88560d020b410121040b0b2000200837030820002004ad3703000bef0302067f017e02400240024020012802042202450d0020012002417f6a22033602044101210420012001280200220541016a2206360200024002400240024020052d000022074103710e0400010203000b2007410276ad2108410021040c050b2003450d0320012002417e6a3602042001200541026a36020020052d000141087420077241ffff037122014180024921042001410276ad21080c040b20024104490d0320012002417c6a3602042001200541046a360200200541036a2d000041187420052f0001410874722007722201418080044921042001410276ad21080c030b024002400240200741027622030e050102020200020b20024109490d022001200241776a3602042001200541096a36020020052900012208428080808080808080015421040c040b20024105490d0120012002417b6a3602042001200541056a360200200535000122084280808080045421040c030b200741134b0d00200341046a21072002417e6a2104420021084100210203402004417f460d01200120043602042001200641016a22053602002004417f6a210420063100002002410374413871ad86200884210820052106200241016a220241ff01712007490d000b410021042008427f412820034103746b413871ad88560d020b410121040b0b2000200837030820002004ad3703000b950301037f0240200028020022022802002200413f4b0d00200041027421020240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20023a00000f0b0240200041ffff004b0d002000410274410172210202402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20023b00000f0b0240200041ffffffff034b0d002000410274410272210202402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20023600000f0b02402001280200220320012802082200470d0020012000410110b18280800020012802002103200128020821000b2001280204220420006a41033a00002001200041016a2200360208200228020021020240200320006b41034b0d0020012000410410b18280800020012802042104200128020821000b2001200041046a360208200420006a20023600000bb80404017f017e047f017e23808080800041206b22022480808080000240024020002802002903002203423f560d002003a741027421040240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20043a00000c010b0240200342ffff00560d002003a7410274410172210402402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20043b00000c010b0240200342ffffffff03560d002003a7410274410272210402402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20043600000c010b4113200379a741037622054102746b21060240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a22003602082001280204220720046a20063a0000200541786a21050340200321080240024020012802002000460d00200021040c010b20012000410110b18280800020012802042107200128020821040b2001200441016a2200360208200720046a2008a73a000020084208882103200541016a22050d000b200220033703002008428002540d0020024200370214200241b0cbc480003602102002410136020c200241f0cbc480003602084100200241f8cbc48000200241086a4180ccc4800010a389808000000b200241206a2480808080000ba80101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110fa86808000200041106a4293888c8f89fdc6ec9e7f370300200042a5e9e3ab9e929adc2c370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c000370350200041ef80808000360218200041063a0000200141106a2480808080000bbc0201017f23808080800041f0016b220424808080800020044197ccc48000410910a682808000200441b0cbc4800041002002200310a782808000200441a0ccc4800041062001412010a782808000200441a6ccc480004107200141c0016a412010a782808000200441d0016a41186a22014200370300200441d0016a41106a22034200370300200441d0016a41086a22024200370300200442003703d001200441b0cbc480004100200441d0016a412010a882808000200041186a2001290300370000200041106a2003290300370000200041086a2002290300370000200020042903d001370000410021010340200420016a220041003a0000200041016a41003a0000200041026a41003a0000200041036a41003a0000200041046a41003a0000200141056a220141c801470d000b200441f0016a2480808080000ba40802017f047e23808080800041c0076b2204248080808000200341e0cfc4800041092002412010a78280800020044180036a200341d00110848e8080001a200441d8066a4200370300200441d0066a4200370300200441c8066a4200370300200441a0066a41206a4200370300200441b8066a4200370300200441b0066a4200370300200441a8066a4200370300200442003703a00620044180036a4190ccc480004107200441a0066a41c00010a882808000200441c0016a200441a0066a10b4818080002004200441c0016a10b281808000200441206a200441c0016a41a00110848e8080001a41002102034020044180036a20026a220341003a0000200341016a41003a0000200341026a41003a0000200341036a41003a0000200341046a41003a0000200241056a220241c801470d000b20044180066a41186a2203200141186a29000037030020044180066a41106a200141106a29000037030020044180066a41086a200141086a290000370300200420012900003703800620044180036a20044180066a10b18180800002400240200428028003450d00200441e9016a2004418d036a290000370000200441c0016a41306a20044194036a29000037000020042004290085033700e10120042d0084032102200441a0066a2004419c036a41880110848e8080001a200441c0016a41096a20044180066a41096a290000370000200441c0016a41116a20044180066a41116a290000370000200441c0016a41186a200329000037000020042004290081063700c101200420023a00e001200420042d0080063a00c001200441c0016a41386a200441a0066a41880110848e8080001a20044180036a41206a420037030020044180036a41186a420037030020044180036a41106a420037030020044180036a41086a420037030020044180036a41306a41002902c4c9c48000220537030020044180036a41386a41002902ccc9c480002206370300200441c0036a41002902d4c9c480002207370300200441c8036a41002902dcc9c480002208370300200441d8036a2005370300200441e0036a2006370300200441e8036a2007370300200441f0036a20083703002004420037038003200441002902bcc9c4800022053703a803200420053703d00320044198046a420037030020044190046a420037030020044180036a4188016a420037030020044180046a4200370300200442003703f803200441a0066a20044180036a41a00110848e8080001a0240200441c0016a41206a200441a0066a10b58180800041ff01710d0020044180036a41c0016a200441c0016a41c00110848e8080001a20044180036a200441c00110848e8080001a200041046a20044180036a41800310848e8080001a200041003602000c020b20004101360200200041013a00040c010b20004101360200200041013a00040b200441c0076a2480808080000be80503067f017e077f2380808080004180016b2202248080808000200241206a2203200141086a28020036020020024180013a002420024100360214200242808080801037020c20022001290200370218200241c0006a2002410c6a200220022002200210c68a808000024002400240024020022802742204418080808078460d00200241286a41106a2205200241c0006a41106a290300370300200241286a41086a200241c0006a41086a2903003703002002200229034037032820022802582106200228025c21072002290360210820022802682109200228026c210a2002280270210b2002280278210c200228027c210d02400240200328020022012002410c6a41106a280200220e4f0d002002280218210f0340200f20016a2d000041776a220341174b0d024101200374419380800471450d02200e200141016a2201470d000b2002200e3602200b200020022903283703002000200d36023c2000200c360238200020043602342000200b3602302000200a36022c20002009360228200020083703202000200736021c20002006360218200041106a2005290300370300200041086a200241286a41086a290300370300200228020c450d03200228021041002802c0a3c68000118080808000000c030b200220013602202002200f200e200e200141016a2201200e2001491b10878380800041002d00fca3c680001a200228020421032002280200210e411441002802c8a3c68000118180808000002201450d032001200e36020c200141163602002000418080808078360234200020013602002001200336021002402006450d00200741002802c0a3c68000118080808000000b02402009450d00200a41002802c0a3c68000118080808000000b2004450d01200c41002802c0a3c68000118080808000000c010b2000418080808078360234200020022802403602000b200228020c450d00200228021041002802c0a3c68000118080808000000b20024180016a2480808080000f0b4104411410b280808000000b9d3004077f017e0b7f017e23808080800041c0026b2206248080808000200128020c21070240024002400240024002400240024002400240024002400240024002400240024002400240200141146a2802002208200141106a28020022094f0d002001410c6a210a0340200720086a2d0000220b41776a220c41174b0d024101200c74419380800471450d022001200841016a220836021420092008470d000b200921080b200641a0016a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a20062802a401210c20062802a0012101411441002802c8a3c68000118180808000002208450d012008200136020c200841053602002000418080808078360234200020083602002008200c3602100c110b024002400240200b41db00460d00200b41fb00460d012001200641bf026a4184d5c4800010d58a80800021080c100b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d10200120083602140240200820094f0d000340200720086a2d0000220b41776a220c41174b0d0d4101200c74419380800471450d0d2001200841016a220836021420092008470d000b200921080b200641e8006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628026c210c20062802682109411441002802c8a3c68000118180808000002208450d012008200936020c200841023602002008200c3602104200210d418080808078210e0c0c0b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d0920012008360214024020082009490d00418080808078210f4180808080782110418080808078210e0c040b20064188026a4104722111418080808078210f4180808080782110418080808078210e410021124100210b0340200a28020021070240024002400240024002400240024002400240024002400240024002400240024002400240024003400240200720086a2d0000220c41776a0e24000003030003030303030303030303030303030303030300030303030303030303030304020b2001200841016a220836021420092008470d000b200921080c180b200c41fd00460d050b200b4101710d012008210b0c0a0b0240200b4101710d002008210b0c0b0b2001200841016a220b360214200b20094f0d0103402007200b6a2d0000220c41776a220841174b0d0a4101200874419380800471450d0a2001200b41016a220b3602142009200b470d000b200921080c020b41012112200641f8006a200720092009200841016a220820092008491b1087838080004100210c41002d00fca3c680001a200628027c210920062802782107411441002802c8a3c68000118180808000002208450d032008200736020c20084108360200200820093602100c150b200841016a21080b4101211220064198016a200720092009200841016a220820092008491b1087838080004100210c41002d00fca3c680001a200628029c0121092006280298012107411441002802c8a3c68000118180808000002208450d022008200736020c20084105360200200820093602100c130b024020124101710d004100210c41f8d7c48000410610e38a8080002108410121120c130b200f418080808078460d0402402010418080808078470d004182d8c48000410d10e38a80800021080c040b200e418080808078460d02200641d8016a41106a200641f0016a41106a280200360200200641d8016a41086a200641f0016a41086a290200370300200620062902f0013703d801201321080c160b4104411410b280808000000b4104411410b280808000000b419ccfc48000410810e38a80800021082010450d00201441002802c0a3c68000118080808000000b20104180808080784621124101210c0240200f0d004100210f0c0f0b201541002802c0a3c68000118080808000000c0e0b4100210c41fed7c48000410410e38a8080002108418080808078210f410121120c0d0b200c4122460d01200c41fd00470d004101211220064190016a200720092009200b41016a220820092008491b1087838080004100210c41002d00fca3c680001a20062802940121092006280290012107411441002802c8a3c68000118180808000002208450d032008200736020c20084115360200200820093602100c0c0b4101211220064180016a200720092009200b41016a220820092008491b1087838080004100210c41002d00fca3c680001a20062802840121092006280280012107411441002802c8a3c68000118180808000002208450d012008200736020c20084111360200200820093602100c0b0b200141003602082001200b41016a36021420064188026a200a2001108a83808000200628028c022108024002402006280288024102460d00200641b4026a200820062802900210fc8980800020062d00b402450d0120062802b80221080b4100210c410121120c0b0b02400240024002400240024020062d00b5020e0400010203000b20124101710d034100210c0240200110d78a8080002208450d00410121120c100b410121122001200820082008200810f68a80800022080d0f0c090b0240200f418080808078460d004100210c4101211241fed7c48000410410e78a80800021080c0f0b0240200110d78a80800022080d0020064188026a2001200820082008200810f78a808000200628028802211320062802a002220f418080808078470d07201321080b4100210c41012112418080808078210f0c0e0b02402010418080808078460d004100210c410121124182d8c48000410d10e78a80800021080c0e0b200110d78a80800022080d0620064188026a2001200820082008200810f58a808000200628028c0221082006280288022210418080808078460d062006280290022116200821140c070b0240200e418080808078460d00410121124100210c419ccfc48000410810e78a80800021080c0f0b0240200110d78a8080002208450d00410121124100210c0c100b20064188026a2001200820082008200810f88a808000200628028c022117200628028802220e418080808078460d0120062802900221180c060b4100210c4101211241f8d7c48000410610e78a80800021080c0b0b410121124100210c201721080c0d0b4104411410b280808000000b4104411410b280808000000b200641f0016a41106a201141106a280200360200200641f0016a41086a201141086a290200370300200620112902003703f00120062802a402211520062903a80221190c010b4100210c4101211241808080807821100c060b4101210b20012802142208200128021022094f0d030c000b0b4104411410b280808000000b4104411410b280808000000b200a28020021070b4101211220064188016a200720092009200841016a220820092008491b1087838080004100210c41002d00fca3c680001a200628028c0121092006280288012107411441002802c8a3c68000118180808000002208450d012008200736020c20084103360200200820093602100b200e418080808078470d010c020b4104411410b280808000000b200e450d00201741002802c0a3c68000118080808000000b02402012450d00201041ffffffff0771450d00201441002802c0a3c68000118080808000000b0240200c0d00200f41808080807872418080808078460d00201541002802c0a3c68000118080808000000b418080808078210e0b200120012d001841016a3a0018200110d98a808000210c02400240200e418080808078460d00200c0d01200641a8016a41106a200641d8016a41106a280200360200200641a8016a41086a200641d8016a41086a290300370300200620062903d8013703a8012018ad422086210d0c060b200c450d060240200c2802000d00200c41086a280200450d00200c28020441002802c0a3c68000118080808000000b200c41002802c0a3c68000118080808000000c060b0240200f450d00201541002802c0a3c68000118080808000000b02402010450d00201441002802c0a3c68000118080808000000b0240200e450d00201741002802c0a3c68000118080808000000b200c21080c050b200641f0006a200720092009200820092008491b10878380800041002d00fca3c680001a2006280274210c20062802702101411441002802c8a3c680001181808080000022080d064104411410b280808000000b0240200b41dd00460d004200210d418080808078210e2001200820082008200810f68a80800022080d01200128020c2109024002400240024002400240024002400240200128021422082001280210220c4f0d0003400240200920086a2d0000220741776a0e24000005050005050505050505050505050505050505050500050505050505050505050503040b2001200841016a2208360214200c2008470d000b200c21080b200641106a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a2006280214210c20062802102109411441002802c8a3c68000118180808000002208450d032008200936020c200841023602002008200c3602100c090b2001200841016a220836021402402008200c4f0d000340200920086a2d0000220b41776a220741174b0d084101200774419380800471450d082001200841016a2208360214200c2008470d000b200c21080b200641d8006a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a200628025c210c20062802582109411441002802c8a3c68000118180808000002208450d042008200936020c200841053602002008200c3602100c080b200741dd00460d040b200641086a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a200628020c210c20062802082109411441002802c8a3c68000118180808000002208450d012008200936020c200841073602002008200c3602100c060b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b410141f0d7c4800041c8cec4800010e68a80800021080c020b024002400240024002400240024002400240200b41dd00470d00200641e0006a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a2006280264210c20062802602109411441002802c8a3c68000118180808000002208450d012008200936020c200841153602002008200c3602100c0a0b20064188026a2001200820082008200810f78a808000200628028802210820062802a002220f418080808078460d0920064180026a20064188026a41146a280200360200200641f8016a20064194026a2902003703002006200629028c023703f00120062802a4022115200128020c210702400240024002402001280214220c200128021022094f0d0020062903a8022119034002402007200c6a2d0000220b41776a0e24000005050005050505050505050505050505050505050500050505050505050505050503040b2001200c41016a220c3602142009200c470d000b2009210c0b200641206a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a2006280224210c20062802202109411441002802c8a3c68000118180808000002208450d042008200936020c200841023602002008200c3602100c090b2001200c41016a220c3602140240200c20094f0d0003402007200c6a2d0000220a41776a220b41174b0d094101200b74419380800471450d092001200c41016a220c3602142009200c470d000b2009210c0b200641c8006a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a200628024c210c20062802482109411441002802c8a3c68000118180808000002208450d052008200936020c200841053602002008200c3602100c080b200b41dd00460d050b200641186a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a200628021c210c20062802182109411441002802c8a3c68000118180808000002208450d022008200936020c200841073602002008200c3602100c060b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b410241f0d7c4800041c8cec4800010e68a80800021080c010b0240024002400240024002400240200a41dd00470d00200641d0006a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a2006280254210c20062802502109411441002802c8a3c68000118180808000002208450d012008200936020c200841153602002008200c3602100c070b20064188026a2001200820082008200810f58a80800002402006280288022210418080808078470d00200628028c0221080c070b200628028c022114200128020c2107024002400240024002402001280214220c200128021022094f0d002006280290022116034002402007200c6a2d0000220b41776a0e24000005050005050505050505050505050505050505050500050505050505050505050503040b2001200c41016a220c3602142009200c470d000b2009210c0b200641306a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a2006280234210c20062802302109411441002802c8a3c68000118180808000002208450d052008200936020c200841023602002008200c3602100c090b2001200c41016a220c3602140240200c20094f0d0003402007200c6a2d0000220a41776a220b41174b0d094101200b74419380800471450d092001200c41016a220c3602142009200c470d000b2009210c0b200641386a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a200628023c210c20062802382109411441002802c8a3c68000118180808000002208450d062008200936020c200841053602002008200c3602100c080b200b41dd00460d010b200641286a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a200628022c210c20062802282109411441002802c8a3c68000118180808000002208450d032008200936020c200841073602002008200c3602100c060b410341f0d7c4800041c8cec4800010e68a80800021080c050b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b0240200a41dd00470d00200641c0006a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a2006280244210c20062802402109411441002802c8a3c68000118180808000002208450d032008200936020c200841153602002008200c3602100c010b20064188026a2001200820082008200810f88a8080000240200628028802220c418080808078470d00200628028c0221080c010b200641c0016a41086a200641f0016a41086a290300370300200641c0016a41106a200641f0016a41106a280200360200200620062903f0013703c001200635029002422086210d200628028c022117200c210e0c050b2010450d00201441002802c0a3c68000118080808000000b200f450d02201541002802c0a3c68000118080808000000c020b4104411410b280808000000b4200210d418080808078210e410041f0d7c4800041c8cec4800010e68a80800021080b0b200120012d001841016a3a0018200110da8a808000210c02400240200e418080808078460d00200c0d01200641a8016a41106a200641c0016a41106a280200360200200641a8016a41086a200641c0016a41086a290300370300200620062903c0013703a8010c020b200c450d020240200c2802000d00200c41086a280200450d00200c28020441002802c0a3c68000118080808000000b200c41002802c0a3c68000118080808000000c020b0240200f450d00201541002802c0a3c68000118080808000000b02402010450d00201441002802c0a3c68000118080808000000b0240200e450d00201741002802c0a3c68000118080808000000b200c21080c010b200020062903a801370204200020163602302000201436022c20002010360228200020193703202000201536021c2000200f3602182000200e36023420002008360200200041146a200641b8016a2802003602002000410c6a200641b0016a2903003702002000200d2017ad843703380c030b2008200110d68a80800021082000418080808078360234200020083602000c020b2006200720092009200820092008491b10878380800041002d00fca3c680001a2006280204210c20062802002101411441002802c8a3c680001181808080000022080d004104411410b280808000000b2008200136020c200841183602002000418080808078360234200020083602002008200c3602100b200641c0026a2480808080000bb50101037f23808080800041106b2202248080808000200241086a200028020c200041106a28020022032003200041146a28020041016a220020032000491b10878380800041002d00fca3c680001a200228020c2103200228020821040240411441002802c8a3c680001181808080000022000d004104411410b280808000000b2000200436020c2000200129020037020020002003360210200041086a200141086a280200360200200241106a24808080800020000b950501077f23808080800041106b220224808080800041002103024002400240200041146a2802002204200041106a28020022054f0d000240200028020c220620046a2d0000220741e500460d00200741c500460d002007412e470d012000200441016a22083602140240200128020822072001280200470d0020012007109c86808000200128020821070b200128020420076a412e3a00002001200128020841016a2207360208024002400240200820054f0d00200441026a2104200620086a2d0000220841506a41ff017141094b0d0120002004360214024020072001280200470d0020012007109c86808000200128020821070b200128020420076a20083a00002001200128020841016a2207360208200420054f0d040340200620046a2d0000220841506a41ff017141094b0d032000200441016a2204360214024020072001280200470d0020012007109c86808000200128020821070b200128020420076a20083a00002001200128020841016a220736020820052004470d000c050b0b200241086a200620052005200441026a220020052000491b10878380800041002d00fca3c680001a200228020c210020022802082101411441002802c8a3c68000118180808000002203450d042003200136020c20034105360200200320003602100c030b2002200620052005200420052004491b10878380800041002d00fca3c680001a2002280204210020022802002101411441002802c8a3c68000118180808000002203450d042003200136020c2003410d360200200320003602100c020b200841207241e500470d0120002008200110c98a80800021030c010b20002007200110c98a80800021030b200241106a24808080800020030f0b4104411410b280808000000b4104411410b280808000000b970501067f23808080800041106b2203248080808000200041146a22042004280200220541016a2204360200024002402001418001490d002001413f7141807f7221062001410676414072210702402002280200200228020822086b41014b0d0020022008410210ab86808000200228020821080b2002200841026a2201360208200228020420086a220820063a0001200820073a00000c010b0240200228020822082002280200470d0020022008109c86808000200228020821080b200228020420086a20013a00002002200228020841016a22013602080b02402004200041106a2802004f0d000240024002400240200028020c20046a2d0000220441556a0e03000401040b2000200541026a36021420012002280200470d020c010b2000200541026a36021420012002280200470d010b20022001109c86808000200228020821010b200228020420016a20043a00002002200228020841016a3602080b200341086a2000200210ca8a808000024002400240024020032d00080d0020032d000941506a41ff0171410a4f0d014100210520002802142201200028021022064f0d02200028020c21070340200720016a2d0000220841506a41ff017141094b0d032000200141016a22013602140240200228020822042002280200470d0020022004109c86808000200228020821040b200228020420046a20083a00002002200228020841016a36020820062001470d000c030b0b200328020c21050c010b2003200028020c2000280210200028021410878380800041002d00fca3c680001a2003280204210220032802002100411441002802c8a3c68000118180808000002205450d012005200036020c2005410d360200200520023602100b200341106a24808080800020050f0b4104411410b280808000000b850301047f23808080800041106b2203248080808000024002400240200141146a2802002204200141106a2802002205490d00200341086a200128020c2005200410878380800041002d00fca3c680001a200328020c210220032802082104411441002802c8a3c68000118180808000002201450d022001200436020c200141053602002000200136020420012002360210410121010c010b2001200441016a36021402400240200128020c20046a2c00002201417f4a0d002001413f7141807f722105200141c00171410676414072210602402002280200200228020822046b41014b0d0020022004410210ab86808000200228020821040b2002200441026a360208200228020420046a220220053a0001200220063a00000c010b0240200228020822042002280200470d0020022004109c86808000200228020821040b200228020420046a20013a00002002200228020841016a3602080b200020013a0001410021010b200020013a0000200341106a2480808080000f0b4104411410b280808000000bd204010a7f23808080800041106b2205248080808000200141146a22062006280200220741016a220836020002400240024002402008200141106a28020022094f0d00200128020c20086a210a200720096b41016a210b41002106024003400240200a20066a2d0000220c41506a220d41ff0171220e410a490d00024020060d00200720066a41016a21080c040b200420066b21060240200c41207241e500460d002000200120022003200610cc8a8080000c050b2000200120022003200610cd8a8080000c040b024020034298b3e6cc99b3e6cc19580d0020034299b3e6cc99b3e6cc19520d02200e41054b0d020b2001200720066a41026a3602142003420a7e200dad42ff01837c2103200b200641016a22066a0d000b2000200120022003200820046a20096b10cc8a8080000c020b2000200120022003200420066b10ce8a8080000c010b2009200841016a220620092006491b2106024020082009490d00200541086a200128020c2009200610878380800041002d00fca3c680001a200528020c210d20052802082101411441002802c8a3c68000118180808000002206450d022006200136020c2006410536020020002006360204200041013602002006200d3602100c010b2005200128020c2009200610878380800041002d00fca3c680001a2005280204210d20052802002101411441002802c8a3c68000118180808000002206450d022006200136020c2006410d36020020002006360204200041013602002006200d3602100b200541106a2480808080000f0b4104411410b280808000000b4104411410b280808000000bd70304017f017c017f017c23808080800041106b22052480808080002003ba21060240024002400240024002400240024020042004411f7522077320076b220741b502490d0003402006440000000000000000610d072004417f4a0d02200644a0c8eb85f3cce17fa32106200441b4026a22042004411f7522077320076b220741b4024b0d000b0b200741037441c0e2c180006a2b030021082004417f4a0d0120062008a321060c050b2005200128020c200141106a280200200141146a28020010878380800041002d00fca3c680001a2005280204210720052802002101411441002802c8a3c68000118180808000002204450d022004200136020c2004410e36020020002004360204200420073602100c010b20062008a222069944000000000000f07f620d03200541086a200128020c200141106a280200200141146a28020010878380800041002d00fca3c680001a200528020c210720052802082101411441002802c8a3c68000118180808000002204450d022004200136020c2004410e36020020002004360204200420073602100b410121040c030b4104411410b280808000000b4104411410b280808000000b2000200620069a20021b390308410021040b20002004360200200541106a2480808080000bed0401077f23808080800041106b220524808080800041012106200141146a22072007280200220741016a220836020002402008200141106a28020022094f0d004101210602400240200128020c20086a2d000041556a0e03010200020b410021060b2001200741026a22083602140b02400240024002400240024002400240200820094f0d002001200841016a2207360214200128020c220a20086a2d000041506a41ff01712208410a4f0d010240200720094f0d000340200a20076a2d000041506a41ff0171220b410a4f0d012001200741016a22073602140240200841cb99b3e6004c0d00200841cc99b3e600470d07200b41074b0d070b2008410a6c200b6a210820092007470d000b0b20060d02200420086b2207411f75418080808078732007200841004a2007200448731b21070c030b200541086a200128020c2009200810878380800041002d00fca3c680001a200528020c210820052802082101411441002802c8a3c68000118180808000002207450d042007200136020c200741053602002000200736020420004101360200200720083602100c060b2005200a2009200710878380800041002d00fca3c680001a2005280204210820052802002101411441002802c8a3c68000118180808000002207450d042007200136020c2007410d3602002000200736020420004101360200200720083602100c050b200420086a2207411f7541808080807873200720084100482007200448731b21070b2000200120022003200710cc8a8080000c030b200020012002200350200610d38a8080000c020b4104411410b280808000000b4104411410b280808000000b200541106a2480808080000b850101047f02400240200141146a2802002205200141106a28020022064f0d00200128020c210702400340200720056a2d0000220841506a41ff017141094b0d012001200541016a220536021420062005470d000c020b0b200841207241e500460d010b2000200120022003200410cc8a8080000f0b2000200120022003200410cd8a8080000bd40804067f017e017f017e23808080800041306b220324808080800002400240024002400240024002400240024002400240024002400240200141146a2802002204200141106a28020022054f0d002001200441016a2206360214200128020c220720046a2d000022084130470d040240200620054f0d00200720066a2d0000220641506a41ff0171410a490d042006412e460d02200641c500460d03200641e500460d030b4200428080808080808080807f20021b21090c0c0b200341186a200128020c2005200410878380800041002d00fca3c680001a200328021c210620032802182105411441002802c8a3c68000118180808000002201450d042001200536020c200141053602002000200136020420004104360200200120063602100c0c0b200341206a200120024200410010cb8a8080002003280220450d0920002003280224360204200041043602000c0b0b200341206a200120024200410010cd8a8080002003280220450d0820002003280224360204200041043602000c0a0b200341086a200720052005200441026a220120052001491b10878380800041002d00fca3c680001a200328020c210620032802082105411441002802c8a3c68000118180808000002201450d022001200536020c2001410d3602002000200136020420004104360200200120063602100c090b02402008414f6a41ff01714109490d00200341106a20072005200610878380800041002d00fca3c680001a2003280214210620032802102105411441002802c8a3c68000118180808000002201450d032001200536020c2001410d3602002000200136020420004104360200200120063602100c090b200841506aad42ff01832109200620054f0d0403400240200720066a2d0000220a41506a220441ff01712208410a490d0002400240200a412e460d00200a41c500460d01200a41e500460d010c080b41002106200341206a200120022009410010cb8a8080002003280220450d0620002003280224360204200041043602000c0b0b41002106200341206a200120022009410010cd8a8080002003280220450d0520002003280224360204200041043602000c0a0b0240024020094299b3e6cc99b3e6cc19540d0020094299b3e6cc99b3e6cc19520d01200841054b0d010b2001200641016a22063602142009420a7e2004ad42ff01837c210920052006470d010c060b0b200341206a20012002200910d08a808000024020032802200d00200020032b0328390308200041003602000c090b20002003280224360204200041043602000c080b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b2003290328210b0c010b4101210602402002450d002009210b0c010b0240420020097d220b4200590d00410221060c010b2009babd428080808080808080807f85210b410021060b2000200b370308200020063602000c020b20032903282109410021020b20002009370308200020023602000b200341306a2480808080000bc30101057f4100210402400240200141106a2802002205200141146a28020022064d0d00200641016a2107200520066b2108200128020c20066a21054100210403400240200520046a2d0000220641506a41ff0171410a490d002006412e460d030240200641c500460d00200641e500470d030b2000200120022003200410cd8a8080000f0b2001200720046a3602142008200441016a2204470d000b200821040b2000200120022003200410cc8a8080000f0b2000200120022003200410cb8a8080000bd30501077f23808080800041206b2201248080808000200028020c21020240024002400240024002400240200041146a2802002203200041106a28020022044f0d002000200341016a22053602140240200220036a2d000022064130470d00200520044f0d05200220056a2d000041506a41ff0171410a490d030c050b2006414f6a41ff017141084d0d01200521030b200141186a20022004200310878380800041002d00fca3c680001a200128021c210020012802182105411441002802c8a3c68000118180808000002203450d022003200536020c2003410d360200200320003602100c040b200520044f0d020340200220056a2d000041506a41ff017141094b0d032000200541016a220536021420042005470d000b410021030c030b200141086a200220042004200341026a220020042000491b10878380800041002d00fca3c680001a200128020c2100200128020821050240411441002802c8a3c68000118180808000002203450d002003200536020c2003410d360200200320003602100c030b4104411410b280808000000b4104411410b280808000000b41002103200520044f0d00024002400240200220056a2d0000220641e500460d00200641c500460d002006412e470d032000200541016a2206360214200620044f0d01200220066a2d000041506a41ff017141094b0d01200541026a2105034020042005460d03200220056a2106200541016a2207210520062d0000220641506a41ff0171410a490d000b20002007417f6a360214200641207241e500470d030b200010d28a80800021030c020b200141106a200220042004200541026a220020042000491b10878380800041002d00fca3c680001a2001280214210020012802102105411441002802c8a3c68000118180808000002203450d022003200536020c2003410d360200200320003602100c010b200020043602140b200141206a24808080800020030f0b4104411410b280808000000bc00201057f23808080800041106b2201248080808000200041146a22022002280200220241016a2203360200200028020c210402402003200041106a28020022054f0d000240200420036a2d000041556a0e03000100010b2000200241026a22033602140b0240024002400240200320054f0d002000200341016a2202360214200420036a2d000041506a41ff017141094d0d01200221030b200141086a20042005200310878380800041002d00fca3c680001a200128020c210220012802082100411441002802c8a3c68000118180808000002203450d022003200036020c2003410d360200200320023602100c010b41002103200220054f0d000340200420026a2d000041506a41ff017141094b0d012000200241016a220236021420052002470d000b0b200141106a24808080800020030f0b4104411410b280808000000ba40201027f23808080800041106b220524808080800002400240024002402004450d002003450d010b200141146a2802002204200141106a28020022034f0d01200128020c21060340200620046a2d000041506a41ff0171410a4f0d022001200441016a220436021420032004470d000c020b0b200541086a200128020c200141106a280200200141146a28020010878380800041002d00fca3c680001a200528020c2101200528020821030240411441002802c8a3c68000118180808000002204450d002004200336020c2004410e3602002000200436020420042001360210410121040c020b4104411410b280808000000b200044000000000000000044000000000000008020021b390308410021040b20002004360200200541106a2480808080000b910802067f027e23808080800041c0006b220324808080800041002d00fca3c680001a02400240024002400240411041002802c8a3c68000118180808000002204450d002003410036022c2003200436022820034110360224024020020d002004412d3a00002003200328022c41016a36022c0b200341306a2001200341246a10ca8a80800020032d00300d0102400240024020032d003122044130470d000240200141146a2802002204200141106a28020022054f0d00200128020c220620046a2d000041506a41ff0171410a490d020b2001200341246a10c88a80800021010c070b2004414f6a41ff017141084b0d010240200141146a2802002204200141106a28020022074f0d00200128020c21080340200820046a2d0000220641506a41ff017141094b0d012001200441016a22043602140240200328022c22052003280224470d00200341246a2005109c86808000200328022c21050b200328022820056a20063a00002003200328022c41016a36022c20072004470d000b0b2001200341246a10c88a80800021010c060b200341106a200620052005200441016a220120052001491b10878380800041002d00fca3c680001a2003280214210420032802102105411441002802c8a3c68000118180808000002201450d032001200536020c2001410d360200200120043602100c050b200341186a200128020c200141106a280200200141146a28020010878380800041002d00fca3c680001a200328021c210420032802182105411441002802c8a3c68000118180808000002201450d032001200536020c2001410d360200200120043602100c040b4101411010b280808000000b200328023421010c020b4104411410b280808000000b4104411410b280808000000b02400240024002400240024020010d00024020020d00200341306a2003280228200328022c410a10d28080800020032d00300d0520002003290338370308200041023602000c040b200328022c2204450d040240200328022822012d000041556a0e03000302030b2004417f6a2204450d04200141016a21010c020b20004104360200200020013602040c020b20044101460d020b0240024020044111490d004200210903402004450d02200320094200420a420010878e80800020012d000041506a220241094b0d0420032903084200520d04200141016a21012004417f6a21042003290300220a2002ad7c2209200a5a0d000c040b0b42002109034020012d000041506a220241094b0d03200141016a21012009420a7e2002ad7c21092004417f6a22040d000b0b20002009370308200041013602000b2003280224450d01200328022841002802c0a3c68000118080808000000c010b20004103360200200020032902243702042000410c6a2003412c6a2802003602000b200341c0006a2480808080000bc30e02087f017e2380808080004180016b2203248080808000200028020c2104024002400240024002400240024002400240024002400240200041146a2802002205200041106a28020022064f0d00024002400240024002400240024002400240024002400240200420056a2d0000220741a57f6a0e21040b0b0b0b0b0b0b0b0b0b030b0b0b0b0b0b0b010b0b0b0b0b020b0b0b0b0b0b05000b2007415e6a0e0c090a0a0a0a0a0a0a0a0a0a080a0b2000200541016a2207360214200720064f0d132000200541026a22083602140240200420076a2d000041f500470d00200820062007200620074b1b2207460d142000200541036a22093602140240200420086a2d000041ec00460d00200921080c010b20092007460d142000200541046a2208360214200420096a2d000041ec00460d050b200341186a20042006200810878380800041002d00fca3c680001a200328021c210520032802182104411441002802c8a3c68000118180808000002200450d0d2000200436020c20004109360200200020053602100c140b2000200541016a2207360214200720064f0d112000200541026a22083602140240200420076a2d000041f200470d00200820062007200620074b1b2207460d122000200541036a22093602140240200420086a2d000041f500460d00200921080c010b20092007460d122000200541046a2208360214200420096a2d000041e500460d050b200341286a20042006200810878380800041002d00fca3c680001a200328022c210520032802282104411441002802c8a3c68000118180808000002200450d0d2000200436020c20004109360200200020053602100c130b2000200541016a2207360214200720064f0d0f2000200541026a22083602140240200420076a2d000041e100470d00200820062007200620074b1b2207460d102000200541036a22093602140240200420086a2d000041ec00460d00200921080c010b20092007460d102000200541046a220a3602140240200420096a2d000041f300460d00200a21080c010b200a2007460d102000200541056a22083602142004200a6a2d000041e500460d050b200341386a20042006200810878380800041002d00fca3c680001a200328023c210520032802382104411441002802c8a3c68000118180808000002200450d0d2000200436020c20004109360200200020053602100c120b2003410a3a0070200341f0006a20012002108383808000200010d68a80800021000c110b2003410b3a0070200341f0006a20012002108383808000200010d68a80800021000c100b200341073a0070200341f0006a20012002108383808000200010d68a80800021000c0f0b20034180023b0170200341f0006a20012002108383808000200010d68a80800021000c0e0b200341003b0170200341f0006a20012002108383808000200010d68a80800021000c0d0b2000200541016a360214200341f0006a2000410010d48a80800020032802704104460d04200341c0006a41086a200341f0006a41086a29030037030020032003290370370340200341c0006a2001200210f682808000200010d68a80800021000c0c0b200041003602082000200541016a360214200341e4006a2000410c6a2000108a83808000024020032802644102460d002003290268210b200341053a00702003200b370274200341f0006a20012002108383808000200010d68a80800021000c0c0b200328026821000c0b0b200741506a41ff0171410a490d010b200341086a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200328020c210420032802082106411441002802c8a3c68000118180808000002205450d052005200636020c2005410a360200200520043602102005200010d68a80800021000c090b200341f0006a2000410110d48a80800020032802704104460d00200341d0006a41086a200341f0006a41086a29030037030020032003290370370350200341d0006a2001200210f682808000200010d68a80800021000c080b200328027421000c070b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200341306a20042006200710878380800041002d00fca3c680001a20032802342105200328023021040240411441002802c8a3c68000118180808000002200450d002000200436020c20004105360200200020053602100c030b4104411410b280808000000b200341206a20042006200710878380800041002d00fca3c680001a20032802242105200328022021040240411441002802c8a3c68000118180808000002200450d002000200436020c20004105360200200020053602100c020b4104411410b280808000000b200341106a20042006200710878380800041002d00fca3c680001a2003280214210520032802102104411441002802c8a3c68000118180808000002200450d012000200436020c20004105360200200020053602100b20034180016a24808080800020000f0b4104411410b280808000000be80101047f23808080800041206b220224808080800002400240200028020c450d00200021010c010b200241106a41086a2203200041086a28020036020020022000290200370310200241086a200128020c200141106a280200200141146a28020010878380800041002d00fca3c680001a200228020c2104200228020821050240411441002802c8a3c680001181808080000022010d004104411410b280808000000b200120022903103702002001200536020c20012004360210200141086a2003280200360200200041002802c0a3c68000118080808000000b200241206a24808080800020010b990301047f23808080800041106b2201248080808000200028020c2102024002400240024002400240200041146a2802002203200041106a28020022044f0d0003400240200220036a2d000041776a0e320000040400040404040404040404040404040404040404000404040404040404040404040404040404040404040404040403040b2000200341016a220336021420042003470d000b200421030b200141086a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a200128020c210020012802082104411441002802c8a3c68000118180808000002203450d032003200436020c20034103360200200320003602100c020b2000200341016a360214410021030c010b2001200220042004200341016a220320042003491b10878380800041002d00fca3c680001a2001280204210020012802002104411441002802c8a3c68000118180808000002203450d022003200436020c20034106360200200320003602100b200141106a24808080800020030f0b4104411410b280808000000b4104411410b280808000000ba60101037f23808080800041106b2202248080808000200241086a200028020c200041106a280200200041146a28020010878380800041002d00fca3c680001a200228020c2103200228020821040240411441002802c8a3c680001181808080000022000d004104411410b280808000000b2000200436020c2000200129020037020020002003360210200041086a200141086a280200360200200241106a24808080800020000b900401057f23808080800041206b2201248080808000200028020c2102024002400240024002400240024002400240200041146a2802002203200041106a28020022044f0d0003400240200220036a2d0000220541776a0e24000004040004040404040404040404040404040404040400040404040404040404040406030b2000200341016a220336021420042003470d000b200421030b200141106a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a2001280214210420012802102100411441002802c8a3c68000118180808000002203450d052003200036020c20034103360200200320043602100c040b200541fd00460d010b200141086a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a200128020c210420012802082100411441002802c8a3c68000118180808000002203450d042003200036020c20034116360200200320043602100c020b2000200341016a360214410021030c010b200141186a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a200128021c210420012802182100411441002802c8a3c68000118180808000002203450d032003200036020c20034115360200200320043602100b200141206a24808080800020030f0b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000bde0501067f23808080800041206b2201248080808000200028020c2102024002400240024002400240024002400240024002400240200041146a2802002203200041106a28020022044f0d0003400240200220036a2d0000220541776a0e24000004040004040404040404040404040404040404040400040404040404040404040406030b2000200341016a220336021420042003470d000b200421030b200141086a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a200128020c210420012802082100411441002802c8a3c68000118180808000002203450d042003200036020c20034102360200200320043602100c080b200541dd00460d010b2001200220042004200341016a220320042003491b10878380800041002d00fca3c680001a2001280204210420012802002100411441002802c8a3c68000118180808000002203450d032003200036020c20034116360200200320043602100c060b2000200341016a360214410021030c050b2000200341016a2203360214200320044f0d030340200220036a2d0000220641776a220541174b0d034101200574419380800471450d032000200341016a220336021420042003470d000b200421030c030b4104411410b280808000000b4104411410b280808000000b200641dd00470d00200141186a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a200128021c210420012802182100411441002802c8a3c68000118180808000002203450d032003200036020c20034115360200200320043602100c010b200141106a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a2001280214210420012802102100411441002802c8a3c68000118180808000002203450d012003200036020c20034116360200200320043602100b200141206a24808080800020030f0b4104411410b280808000000b4104411410b280808000000bf20601057f23808080800041106b220424808080800002400240024002400240200241c0006a22050d0020044201370204200420053602000c010b2005417f4c0d034100210641002d00fca3c680001a200541002802c8a3c68000118180808000002207450d022004410036020820042007360204200420053602002002450d010b200120026a210541002106034002400240024020012c00002202417f4c0d00200141016a2101200241ff017121020c010b20012d0001413f7121072002411f712108024002402002415f4b0d0020084106742007722102200141026a21010c010b200741067420012d0002413f717221070240200241704f0d0020072008410c74722102200141036a21010c010b200741067420012d0003413f71722008411274418080f00071722202418080c400460d04200141046a21010b2002418001490d002004410036020c024002402002418010490d0002402002418080044f0d0020042002413f71418001723a000e20042002410c7641e001723a000c20042002410676413f71418001723a000d410321020c020b20042002413f71418001723a000f2004200241127641f001723a000c20042002410676413f71418001723a000e20042002410c76413f71418001723a000d410421020c010b20042002413f71418001723a000d2004200241067641c001723a000c410221020b0240200428020020066b20024f0d0020042006200210ab86808000200428020821060b200428020420066a2004410c6a200210848e8080001a200620026a21060c010b024020062004280200470d0020042006109c86808000200428020821060b200428020420066a20023a0000200428020841016a21060b2004200636020820012005470d000b0b410021010340413041d700200320016a2d0000220241a001491b20024104766a2105024020062004280200470d0020042006109c86808000200428020821060b200428020420066a20053a00002004200428020841016a2206360208024020062004280200470d0020042006109c86808000200428020821060b200428020420066a413041d7002002410f712206410a491b20066a3a00002004200428020841016a2206360208200141016a22014120470d000b20002004290200370200200041086a200441086a280200360200200441106a2480808080000f0b4101200510b280808000000b10ae80808000000beb0601057f23808080800041106b220524808080800041012106024002400240200441017420026a2207450d002007417f4c0d0241002d00fca3c680001a200741002802c8a3c68000118180808000002206450d010b4100210820054100360208200520063602042005200736020002402002450d00200120026a210741002108034002400240024020012c00002202417f4c0d00200141016a2101200241ff017121020c010b20012d0001413f7121062002411f712109024002402002415f4b0d0020094106742006722102200141026a21010c010b200641067420012d0002413f717221060240200241704f0d0020062009410c74722102200141036a21010c010b200641067420012d0003413f71722009411274418080f00071722202418080c400460d04200141046a21010b2002418001490d002005410036020c024002402002418010490d0002402002418080044f0d0020052002413f71418001723a000e20052002410c7641e001723a000c20052002410676413f71418001723a000d410321020c020b20052002413f71418001723a000f2005200241127641f001723a000c20052002410676413f71418001723a000e20052002410c76413f71418001723a000d410421020c010b20052002413f71418001723a000d2005200241067641c001723a000c410221020b0240200528020020086b20024f0d0020052008200210ab86808000200528020821080b200528020420086a2005410c6a200210848e8080001a200820026a21080c010b024020082005280200470d0020052008109c86808000200528020821080b200528020420086a20023a0000200528020841016a21080b2005200836020820012007470d000b0b02402004450d000340413041d70020032d0000220141a001491b20014104766a2102024020082005280200470d0020052008109c86808000200528020821080b200528020420086a20023a00002005200528020841016a2208360208024020082005280200470d0020052008109c86808000200528020821080b200341016a2103200528020420086a413041d7002001410f712208410a491b20086a3a00002005200528020841016a22083602082004417f6a22040d000b0b20002005290200370200200041086a200541086a280200360200200541106a2480808080000f0b4101200710b280808000000b10ae80808000000b3c00200128021420002802002d00004102742200419cd8c480006a28020020004190d8c480006a280200200141186a28020028020c118280808000000be10101037f024002400240024002402002411c6a2802000e020002010b410041004194d7c4800010f980808000000b20022802182203280204220420032802002203490d0220042001280200220128020822054d0d012004200541b4d7c48000109581808000000b4101410141a4d7c4800010f980808000000b200128020421012000200420036b3602042000200120036a3602002000200229020c370208200041106a200241146a28020036020002402002280200450d00200228020441002802c0a3c68000118080808000000b0f0b2003200441b4d7c48000109681808000000b02000b02000b6301017f02402000280200220041e4016a280200450d00200041e8016a28020041002802c0a3c68000118080808000000b02402000417f460d00200020002802042201417f6a36020420014101470d00200041002802c0a3c68000118080808000000b0b4c01027f024020002802002201417f460d0020002802042102200120012802042200417f6a36020420004101470d002002410b6a4104490d00200141002802c0a3c68000118080808000000b0b7801017f23808080800041306b22022480808080002002200136020c200220003602082002411c6a420137020020024102360214200241fccfc48000360210200241e08180800036022c2002200241286a3602182002200241086a360228200241106a10e48a8080002101200241306a24808080800020010b850201037f23808080800041106b22012480808080002000410c6a28020021020240024002400240024002400240024020002802040e020001020b20020d01410121034100210041b0cbc4800021020c030b2002450d010b200141046a200010b8808080000c020b2000280200220028020021020240200028020422000d0041012103410021000c010b2000417f4c0d0241002d00fca3c680001a200041002802c8a3c68000118180808000002203450d030b20032002200010848e80800021022001200036020c20012002360208200120003602040b200141046a1082838080002100200141106a24808080800020000f0b10ae80808000000b4101200010b280808000000bfa0101017f23808080800041c0006b22042480808080002004200136020c200420003602080240024020030d002004411c6a420137020020044102360214200441b4d0c48000360210200441e08180800036022c2004200441286a3602182004200441086a360228200441106a10e48a80800021030c010b200441286a410c6a41dc83808000360200200441106a410c6a420237020020044102360214200441d0d0c48000360210200441e08180800036022c2004200336023c200420023602382004200441286a3602182004200441386a3602302004200441086a360228200441106a10e48a80800021030b200441c0006a24808080800020030b9f0101017f23808080800041c0006b220324808080800020032002360214200320013602102003200036020c200341186a410c6a4202370200200341306a410c6a41dd838080003602002003410236021c200341fcd0c4800036021820034185808080003602342003200341306a3602202003200341106a36023820032003410c6a360230200341186a10e48a8080002102200341c0006a24808080800020020b7801017f23808080800041306b22022480808080002002200136020c200220003602082002411c6a420137020020024102360214200241a0d1c48000360210200241e08180800036022c2002200241286a3602182002200241086a360228200241106a10e48a8080002101200241306a24808080800020010bfa0101017f23808080800041c0006b22042480808080002004200136020c200420003602080240024020030d002004411c6a420137020020044102360214200441dcd1c48000360210200441e08180800036022c2004200441286a3602182004200441086a360228200441106a10e48a80800021030c010b200441286a410c6a41dc83808000360200200441106a410c6a420237020020044102360214200441ecd1c48000360210200441e08180800036022c2004200336023c200420023602382004200441286a3602182004200441386a3602302004200441086a360228200441106a10e48a80800021030b200441c0006a24808080800020030bfb0c02067f017e23808080800041d0006b22022480808080002001280200220328020c2104024002400240024002400240024002400240024002400240200341146a2802002205200341106a28020022064f0d0003400240200420056a2d0000220741776a0e24000004040004040404040404040404040404040404040400040404040404040404040406030b2003200541016a220536021420062005470d000b200621050b200241186a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228021c210620022802182103411441002802c8a3c68000118180808000002205450d052005200336020c200541023602002000200536020820004202370300200520063602100c0a0b200741dd00460d010b20012d0004450d020c060b200042003703000c070b20012d00040d042003200541016a22053602140240200520064f0d000340200420056a2d0000220741776a220141174b0d074101200174419380800471450d072003200541016a220536021420062005470d000b200621050b200241206a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280224210620022802202103411441002802c8a3c68000118180808000002205450d032005200336020c200541053602002000200536020820004202370300200520063602100c060b2002200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280204210620022802002103411441002802c8a3c68000118180808000002205450d012005200336020c200541073602002000200536020820004202370300200520063602100c050b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200141003a00040b0240024002400240200741dd00470d00200241086a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228020c210620022802082103411441002802c8a3c68000118180808000002205450d012005200336020c200541153602002000200536020820004202370300200520063602100c040b024002400240024002400240200520064f0d0003400240200420056a2d0000220741776a0e2500000404000404040404040404040404040404040404040004040404040404040404040403040b2003200541016a220536021420062005470d000b200621050b200241106a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280214210620022802102103411441002802c8a3c68000118180808000002205450d062005200336020c20054105360200200520063602100c040b2003200541016a36021441002105200241286a2003410010cf8a808000200228022822064104460d02200229033021080240024002400240024020060e0400040102000b200241033a003820022008370340200241386a200241cf006a418c84c4800010838380800021040c020b02402008427f570d00410021050c030b200241023a003820022008370340200241386a200241cf006a41accdc4800010858380800021040c010b20022802302106200228022c21052002410b3a0038200241386a200241cf006a418c84c480001083838080002104200541808080807872418080808078460d00200641002802c0a3c68000118080808000000b410121050b20050d010c060b0240200741506a41ff0171410a490d002003200241cf006a41accdc4800010d58a808000200310d68a80800021050c030b200241286a2003410110cf8a808000200228022822054104460d0141002106200229033021080240024002400240024020050e0400040102000b200241033a003820022008370340200241386a200241cf006a418c84c4800010838380800021040c020b02402008427f570d00410021060c030b200241023a003820022008370340200241386a200241cf006a41accdc4800010858380800021040c010b20022802302106200228022c21052002410b3a0038200241386a200241cf006a418c84c480001083838080002104200541808080807872418080808078460d00200641002802c0a3c68000118080808000000b410121060b2006450d050b2004200310d68a80800021050c010b200228022c21050b20004202370300200020053602080c030b4104411410b280808000000b4104411410b280808000000b20002008370308200042013703000b200241d0006a2480808080000bcc0101047f23808080800041106b2201248080808000200028020421020240024002400240200028020822030d00410121040c010b2003417f4c0d0141002d00fca3c680001a200341002802c8a3c68000118180808000002204450d020b20042002200310848e80800021042001200336020c2001200436020820012003360204200141046a108283808000210302402000280200450d00200241002802c0a3c68000118080808000000b200141106a24808080800020030f0b10ae80808000000b4101200310b280808000000bfc1003067f017e037f23808080800041f0016b22022480808080002001280200220328020c2104024002400240024002400240024002400240024002400240200341146a2802002205200341106a28020022064f0d0003400240200420056a2d0000220741776a0e24000004040004040404040404040404040404040404040400040404040404040404040406030b2003200541016a220536021420062005470d000b200621050b200241306a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280234210620022802302103411441002802c8a3c68000118180808000002205450d052005200336020c200541023602002000200536020820004202370300200520063602100c0a0b200741dd00460d010b20012d0004450d020c060b200042003703000c070b20012d00040d042003200541016a22053602140240200520064f0d000340200420056a2d0000220741776a220141174b0d074101200174419380800471450d072003200541016a220536021420062005470d000b200621050b200241386a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228023c210620022802382103411441002802c8a3c68000118180808000002205450d032005200336020c200541053602002000200536020820004202370300200520063602100c060b200241086a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228020c210620022802082103411441002802c8a3c68000118180808000002205450d012005200336020c200541073602002000200536020820004202370300200520063602100c050b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200141003a00040b0240024002400240024002400240024002400240200741dd00470d00200241106a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280214210620022802102103411441002802c8a3c68000118180808000002205450d012005200336020c200541153602002000200536020820004202370300200520063602100c0a0b02400240200520064f0d000340200420056a2d0000220141776a220741174b0d024101200774419380800471450d022003200541016a220536021420062005470d000b200621050b200241286a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228022c210620022802282103411441002802c8a3c68000118180808000002205450d022005200336020c20054105360200200520063602100c090b0240200141db00460d002003200241ef016a41d4cdc4800010d58a80800021050c080b200320032d0018417f6a22073a0018200541016a2105200741ff0171450d062003200536021420022003360280010240200520064f0d000340200420056a2d0000220141776a220741174b0d054101200774419380800471450d052003200541016a220536021420062005470d000b200621050b200241206a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280224210620022802202104411441002802c8a3c68000118180808000002205450d022005200436020c20054102360200200520063602100c040b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b0240200141dd00470d004100200241ef016a41d4cdc4800010e68a80800021050c010b200241003a008401200241c8016a200310af89808000024020022d00c801450d0020022802cc0121050c010b200241a8016a41106a200241df016a290000220837030020024188016a41086a2205200241d7016a29000037030020024188016a41106a2206200837030020024188016a41186a2204200241e7016a2f00003b01002002200241cf016a2900003703880120022800cb01210920022f00c901210a200241c8016a20024180016a10e98a8080000240024020022903c80122084202560d0002402008a70e03000102000b4101200241ef016a41d4cdc4800010e68a80800021050c020b200241e0006a41086a2005290300370300200241e0006a41106a2006290300370300200241e0006a41186a20042f01003b0100200220022903880137036020022903d00121084101210b410021010c020b20022802d00121050b4100210b410121010b41012104200320032d001841016a3a001841002107200310da8a8080002106024020010d00024020060d00200241c0006a41186a200241e0006a41186a2f01003b0100200241c0006a41106a200241e0006a41106a290300370300200241c0006a41086a200241e0006a41086a290300370300200220022903603703400b20064521040240200b0d00024020052802000d00200541086a280200450d00200528020441002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000b20042107200621050b02402006450d002004450d00024020062802000d00200641086a280200450d00200628020441002802c0a3c68000118080808000000b200641002802c0a3c68000118080808000000b2007450d012000200229034037010e200041266a200241d8006a2f01003b01002000411e6a200241d0006a290300370100200041166a200241c8006a290300370100200020083703282000200936010a2000200a3b0108200042013703000c030b200241186a200420062006200520062005491b10878380800041002d00fca3c680001a200228021c2106200228021821030240411441002802c8a3c68000118180808000002205450d002005200336020c20054118360200200520063602100c020b4104411410b280808000000b2005200310d68a80800021050b20004202370300200020053602080b200241f0016a2480808080000ba80701067f23808080800041d0006b22022480808080002001280200220328020c21040240024002400240024002400240024002400240024002400240200341146a2802002205200341106a28020022064f0d0003400240200420056a2d0000220741776a0e24000004040004040404040404040404040404040404040400040404040404040404040406030b2003200541016a220536021420062005470d000b200621050b200241186a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228021c210620022802182103411441002802c8a3c68000118180808000002205450d052005200336020c2005410236020020002005360204200041013a0000200520063602100c0a0b200741dd00460d010b20012d0004450d020c060b200041003b01000c070b20012d00040d042003200541016a22053602140240200520064f0d000340200420056a2d0000220741776a220141174b0d074101200174419380800471450d072003200541016a220536021420062005470d000b200621050b200241206a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280224210620022802202103411441002802c8a3c68000118180808000002205450d032005200336020c2005410536020020002005360204200041013a0000200520063602100c060b200241086a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228020c210620022802082103411441002802c8a3c68000118180808000002205450d012005200336020c2005410736020020002005360204200041013a0000200520063602100c050b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200141003a00040b0240200741dd00470d00200241106a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280214210620022802102103411441002802c8a3c68000118180808000002205450d022005200336020c2005411536020020002005360204200041013a0000200520063602100c010b2002412c6a200310de87808000024020022d002c0d0020004180023b0100200041026a200229002d3700002000411a6a200241c5006a290000370000200041126a2002413d6a2900003700002000410a6a200241356a2900003700000c010b20002002280230360204200041013a00000b200241d0006a2480808080000f0b4104411410b280808000000b8f0f03067f017e017f23808080800041c0016b22022480808080002001280200220328020c2104024002400240024002400240024002400240024002400240200341146a2802002205200341106a28020022064f0d0003400240200420056a2d0000220741776a0e24000004040004040404040404040404040404040404040400040404040404040404040406030b2003200541016a220536021420062005470d000b200621050b200241206a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280224210620022802202103411441002802c8a3c68000118180808000002205450d052005200336020c200541023602002000200536020820004202370300200520063602100c0a0b200741dd00460d010b20012d0004450d020c060b200042003703000c070b20012d00040d042003200541016a22053602140240200520064f0d000340200420056a2d0000220741776a220141174b0d074101200174419380800471450d072003200541016a220536021420062005470d000b200621050b200241286a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228022c210620022802282103411441002802c8a3c68000118180808000002205450d032005200336020c200541053602002000200536020820004202370300200520063602100c060b2002200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280204210620022802002103411441002802c8a3c68000118180808000002205450d012005200336020c200541073602002000200536020820004202370300200520063602100c050b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200141003a00040b024002400240024002400240024002400240200741dd00470d00200241086a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228020c210620022802082103411441002802c8a3c68000118180808000002205450d012005200336020c200541153602002000200536020820004202370300200520063602100c090b02400240200520064f0d000340200420056a2d0000220141776a220741174b0d024101200774419380800471450d022003200541016a220536021420062005470d000b200621050b200241186a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228021c210620022802182103411441002802c8a3c68000118180808000002205450d022005200336020c20054105360200200520063602100c080b0240200141db00460d002003200241bf016a41e4cdc4800010d58a80800021070c070b200320032d0018417f6a22073a0018200541016a2105200741ff0171450d0520032005360214200241013a00742002200336027020024198016a200241f0006a10ec8a80800020022d0098010d02024020022d0099014101710d004100200241bf016a41e4cdc4800010e68a80800021070c040b200241f8006a41186a2205200241b2016a290100370300200241f8006a41106a2206200241aa016a290100370300200241f8006a41086a2204200241a2016a2901003703002002200229019a0137037820024198016a200241f0006a10e98a8080000240024020022903980122084202560d0002402008a70e03000102000b4101200241bf016a41e4cdc4800010e68a80800021070c050b200241d0006a41086a2004290300370300200241d0006a41106a2006290300370300200241d0006a41186a20052903003703002002200229037837035020022903a001210841012109410021010c050b20022802a00121070c030b4104411410b280808000000b4104411410b280808000000b200228029c0121070b41002109410121010b41012106200320032d001841016a3a001841002104200310da8a8080002105024020010d00024020050d00200241306a41186a200241d0006a41186a290300370300200241306a41106a200241d0006a41106a290300370300200241306a41086a200241d0006a41086a290300370300200220022903503703300b2005452106024020090d00024020072802000d00200741086a280200450d00200728020441002802c0a3c68000118080808000000b200741002802c0a3c68000118080808000000b20062104200521070b02402005450d002006450d00024020052802000d00200541086a280200450d00200528020441002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000b2004450d0120002002290330370308200041206a200241306a41186a290300370300200041186a200241306a41106a290300370300200041106a200241386a29030037030020002008370328200042013703000c030b200241106a200420062006200520062005491b10878380800041002d00fca3c680001a20022802142106200228021021030240411441002802c8a3c68000118180808000002205450d002005200336020c20054118360200200520063602100c020b4104411410b280808000000b2007200310d68a80800021050b20004202370300200020053602080b200241c0016a2480808080000bc10301077f23808080800041206b2202248080808000200128020c2103024002400240024002400240200141146a2802002204200141106a28020022054f0d002001410c6a21060340200320046a2d0000220741776a220841174b0d024101200874419380800471450d022001200441016a220436021420052004470d000b200521040b2002200320052005200441016a220420052004491b10878380800041002d00fca3c680001a2002280204210820022802002101411441002802c8a3c68000118180808000002204450d012004200136020c2004410536020020002004360204200041013a0000200420083602100c040b024020074122460d002001200241146a41d4d4c4800010d58a80800021040c020b200141003602082001200441016a360214200241146a20062001108a838080000240024020022802144102460d002002410c6a2002280218200228021c10ca8680800020022d000c450d01200228021021040c030b20002002280218360204200041013a00000c040b200020022d000d3a0001410021040c020b4104411410b280808000000b20002004200110d68a808000360204410121040b200020043a00000b200241206a2480808080000b860703067f027e027f23808080800041306b2202248080808000200128020c2103024002400240200141146a2802002204200141106a28020022054f0d000340200320046a2d0000220641776a220741174b0d024101200774419380800471450d022001200441016a220436021420052004470d000b200521040b410121072002200320052005200441016a220420052004491b10878380800041002d00fca3c680001a20022802042101200228020021050240411441002802c8a3c68000118180808000002204450d002004200536020c2004410536020020002004360204200420013602100c020b4104411410b280808000000b024002400240024002400240200641db00470d00200120012d0018417f6a22073a0018200441016a21040240200741ff01710d00200241086a200320052005200420052004491b10878380800041002d00fca3c680001a200228020c210720022802082101411441002802c8a3c68000118180808000002204450d022004200136020c2004411836020020002004360204200420073602100c060b20012004360214200241013a001420022001360210200241186a200241106a10e98a808000024002400240200229031822084202510d000240200850450d0041002002412f6a41f4cdc4800010e68a80800021050c020b20022903202108200241186a200241106a10e98a8080000240200229031822094202560d0002402009a70e03000102000b41012002412f6a41f4cdc4800010e68a80800021050c020b2002290320210941012106410021070c020b200228022021050b41002106410121070b41012103200120012d001841016a3a0018200110da8a80800021042005210a20070d022004210a20040d024101210b0c030b20012002412f6a41f4cdc4800010d58a808000210a0c030b4104411410b280808000000b410021032007210b0b024020072006720d00024020052802000d00200541086a280200450d00200528020441002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000b02402004410047200b71450d00024020042802000d00200441086a280200450d00200428020441002802c0a3c68000118080808000000b200441002802c0a3c68000118080808000000b2003450d0020002008370308200041106a2009370300410021070c020b2000200a200110d68a8080003602040b410121070b20002007360200200241306a2480808080000b900501087f23808080800041206b2201248080808000200028020c21020240024002400240200041146a2802002203200041106a28020022044f0d00410020046b2105200341046a21030340200220036a2206417c6a2d0000220741776a220841174b0d024101200874419380800471450d0220002003417d6a3602142005200341016a22036a4104470d000b200421030b2001200220042004200341016a220320042003491b10878380800041002d00fca3c680001a20012802042103200128020021000240411441002802c8a3c68000118180808000002208450d002008200036020c20084105360200200820033602100c020b4104411410b280808000000b024002400240200741ee00470d0020002003417d6a2208360214200820044f0d0220002003417e6a2205360214024002402006417d6a2d000041f500460d00200521030c010b200520042008200420084b1b2208460d0320002003417f6a220536021402402006417e6a2d000041ec00460d00200521030c010b20052008460d0320002003360214410021082006417f6a2d000041ec00460d040b200141106a20022004200310878380800041002d00fca3c680001a2001280214210320012802102100411441002802c8a3c68000118180808000002208450d012008200036020c20084109360200200820033602100c030b20002001411f6a41e4d4c4800010d58a808000200010d68a80800021080c020b4104411410b280808000000b200141086a20022004200810878380800041002d00fca3c680001a200128020c210320012802082100411441002802c8a3c68000118180808000002208450d012008200036020c20084105360200200820033602100b200141206a24808080800020080f0b4104411410b280808000000bc60702067f017e23808080800041e0006b2202248080808000200128020c21030240024002400240200141146a2802002204200141106a28020022054f0d000340200320046a2d0000220641776a220741174b0d024101200774419380800471450d022001200441016a220436021420052004470d000b200521040b2002200320052005200441016a220420052004491b10878380800041002d00fca3c680001a20022802042107200228020021010240411441002802c8a3c68000118180808000002204450d002004200136020c20044105360200200020043602042000418080808078360200200420073602100c020b4104411410b280808000000b0240024002400240200641db00470d00200120012d0018417f6a22073a0018200441016a2104200741ff0171450d0320012004360214200241013a0018200220013602142002410036022420024280808080800137021c200241286a41086a2104024002400340200241286a200241146a10ed8a8080000240200229032822084201510d0020084202520d02200228023021070240200228021c450d00200228022041002802c0a3c68000118080808000000b41808080807821040c030b024020022802242205200228021c470d002002411c6a2005109a86808000200228022421050b2002280220200541286c6a22072004290300370300200741086a200441086a290300370300200741106a200441106a290300370300200741186a200441186a290300370300200741206a200441206a2903003703002002200541016a3602240c000b0b200228021c210420022802202107200228022421030b200120012d001841016a3a0018200110da8a8080002105024002402004418080808078460d002005450d0120040d03200521070c040b2005450d03024020052802000d00200541086a280200450d00200528020441002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000c030b2000200336020820002007360204200020043602000c040b2001200241df006a4194d4c4800010d58a80800021070c010b200741002802c0a3c6800011808080800000200521070b2007200110d68a80800021042000418080808078360200200020043602040c010b200241086a200320052005200420052004491b10878380800041002d00fca3c680001a200228020c210720022802082101411441002802c8a3c68000118180808000002204450d012004200136020c20044118360200200020043602042000418080808078360200200420073602100b200241e0006a2480808080000f0b4104411410b280808000000bc60702067f017e23808080800041e0006b2202248080808000200128020c21030240024002400240200141146a2802002204200141106a28020022054f0d000340200320046a2d0000220641776a220741174b0d024101200774419380800471450d022001200441016a220436021420052004470d000b200521040b2002200320052005200441016a220420052004491b10878380800041002d00fca3c680001a20022802042107200228020021010240411441002802c8a3c68000118180808000002204450d002004200136020c20044105360200200020043602042000418080808078360200200420073602100c020b4104411410b280808000000b0240024002400240200641db00470d00200120012d0018417f6a22073a0018200441016a2104200741ff0171450d0320012004360214200241013a0018200220013602142002410036022420024280808080800137021c200241286a41086a2104024002400340200241286a200241146a10eb8a8080000240200229032822084201510d0020084202520d02200228023021070240200228021c450d00200228022041002802c0a3c68000118080808000000b41808080807821040c030b024020022802242205200228021c470d002002411c6a2005109a86808000200228022421050b2002280220200541286c6a22072004290300370300200741086a200441086a290300370300200741106a200441106a290300370300200741186a200441186a290300370300200741206a200441206a2903003703002002200541016a3602240c000b0b200228021c210420022802202107200228022421030b200120012d001841016a3a0018200110da8a8080002105024002402004418080808078460d002005450d0120040d03200521070c040b2005450d03024020052802000d00200541086a280200450d00200528020441002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000c030b2000200336020820002007360204200020043602000c040b2001200241df006a41a4d4c4800010d58a80800021070c010b200741002802c0a3c6800011808080800000200521070b2007200110d68a80800021042000418080808078360200200020043602040c010b200241086a200320052005200420052004491b10878380800041002d00fca3c680001a200228020c210720022802082101411441002802c8a3c68000118180808000002204450d012004200136020c20044118360200200020043602042000418080808078360200200420073602100b200241e0006a2480808080000f0b4104411410b280808000000bb10501067f23808080800041306b2202248080808000200128020c21030240024002400240200141146a2802002204200141106a28020022054f0d000340200320046a2d0000220641776a220741174b0d024101200774419380800471450d022001200441016a220436021420052004470d000b200521040b200241086a200320052005200441016a220420052004491b10878380800041002d00fca3c680001a200228020c2107200228020821010240411441002802c8a3c68000118180808000002204450d002004200136020c20044105360200200020043602042000418080808078360200200420073602100c020b4104411410b280808000000b02400240200641db00470d00200120012d0018417f6a22073a0018200441016a21040240200741ff01710d00200241106a200320052005200420052004491b10878380800041002d00fca3c680001a2002280214210720022802102101411441002802c8a3c68000118180808000002204450d042004200136020c20044118360200200020043602042000418080808078360200200420073602100c030b200120043602142002411c6a2001410110b689808000200120012d001841016a3a0018200110da8a808000210402400240200228021c2207418080808078460d002004450d012007450d03200228022041002802c0a3c68000118080808000000c030b2002280220210702402004450d00024020042802000d00200441086a280200450d00200428020441002802c0a3c68000118080808000000b200441002802c0a3c68000118080808000000b200721040c020b20002002290220370204200020073602000c020b20012002412f6a41b4d4c4800010d58a80800021040b2004200110d68a80800021042000418080808078360200200020043602040b200241306a2480808080000f0b4104411410b280808000000b850401067f23808080800041206b2202248080808000200128020c210302400240024002400240200141146a2802002204200141106a28020022054f0d002001410c6a2106034002400240200320046a2d000041776a220741194b0d0041012007744193808004710d0120074119460d040b2001200241146a41c4d4c4800010d58a808000200110d68a80800021042000418080808078360200200020043602040c040b2001200441016a220436021420052004470d000b200521040b200241086a200320052005200441016a220420052004491b10878380800041002d00fca3c680001a200228020c210720022802082101411441002802c8a3c68000118180808000002204450d022004200136020c20044105360200200020043602042000418080808078360200200420073602100c010b20014100360208410121072001200441016a360214200241146a20062001108a838080000240024020022802144102460d0020022802182101200228021c2204450d012004417f4c0d0441002d00fca3c680001a200441002802c8a3c680001181808080000022070d014101200410b280808000000b2000200228021836020420004180808080783602000c010b20072001200410848e80800021072000200436020820002007360204200020043602000b200241206a2480808080000f0b4104411410b280808000000b10ae80808000000bbe13010a7f23808080800041e0006b2206248080808000200128020c2107024002400240200141146a2802002208200141106a28020022094f0d002001410c6a210a0340200720086a2d0000220b41776a220c41174b0d024101200c74419380800471450d022001200841016a220836021420092008470d000b200921080b200641086a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628020c210c200628020821010240411441002802c8a3c68000118180808000002208450d002008200136020c200841053602002000200836020420004180808080783602002008200c3602100c020b4104411410b280808000000b02400240024002400240024002400240024002400240200b41db00460d00200b41fb00460d012001200641df006a41f4d4c4800010d58a80800021080c070b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d08200120083602140240200820094f0d000340200720086a2d0000220b41776a220c41174b0d064101200c74419380800471450d062001200841016a220836021420092008470d000b200921080b200641186a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628021c210c20062802182109411441002802c8a3c68000118180808000002208450d012008200936020c200841023602002008200c360210418080808078210c0c050b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d02200120083602140240024002400240024020082009490d00418080808078210d0c010b418080808078210d4100210e0340200a280200210702400240024002400240024002400240024002400240024002400240024003400240200720086a2d0000220c41776a0e24000003030003030303030303030303030303030303030300030303030303030303030304020b2001200841016a220836021420092008470d000b200921080c100b200c41fd00460d050b200e4101710d012008210b0c070b0240200e4101710d002008210b0c080b2001200841016a220b360214200b20094f0d0103402007200b6a2d0000220c41776a220841174b0d074101200874419380800471450d072001200b41016a220b3602142009200b470d000b200921080c020b200641286a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628022c210c20062802282109411441002802c8a3c68000118180808000002208450d032008200936020c200841083602002008200c3602100c0d0b200841016a21080b200641c8006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628024c210c20062802482109411441002802c8a3c68000118180808000002208450d022008200936020c200841053602002008200c3602100c0b0b200d210c200f2108200d418080808078470d0e41d8cec48000410b10e38a8080002108418080808078210c0c0e0b4104411410b280808000000b4104411410b280808000000b200c4122460d01200c41fd00470d00200641c0006a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a2006280244210c20062802402109411441002802c8a3c68000118180808000002208450d032008200936020c200841153602002008200c3602100c070b200641306a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a2006280234210c20062802302109411441002802c8a3c68000118180808000002208450d012008200936020c200841113602002008200c3602100c060b200141003602082001200b41016a360214200641d0006a200a2001108a838080002006280254210820062802504102460d05024002402006280258220c410b470d00200841c0abc38000410b10888e808000450d010b2008200c41ccabc38000410110e58a80800021080c060b0240200d418080808078460d0041d8cec48000410b10e78a80800021080c070b0240200110d78a8080002208450d00418080808078210c0c0a0b200641d0006a200110f38a808000418080808078210c2006280254210f2006280250220d418080808078470d02200f21080c090b4104411410b280808000000b4104411410b280808000000b2006280258210b4101210e2001280214220820012802102209490d000b200a28020021070b200641386a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628023c210c20062802382109411441002802c8a3c68000118180808000002208450d022008200936020c200841033602002008200c3602100b418080808078210c200d418080808078460d030b418080808078210c0240200d450d00200f41002802c0a3c68000118080808000000b0c020b4104411410b280808000000b4104411410b280808000000b200120012d001841016a3a0018200110d98a808000210902400240200c418080808078460d002009450d06200c0d01200921080c050b2009450d04024020092802000d00200941086a280200450d00200928020441002802c0a3c68000118080808000000b200941002802c0a3c68000118080808000000c040b200841002802c0a3c6800011808080800000200921080c030b200641206a200720092009200820092008491b10878380800041002d00fca3c680001a2006280224210c20062802202101411441002802c8a3c680001181808080000022080d054104411410b280808000000b0240200b41dd00470d00418080808078210c410041c8cfc4800041c8cec4800010e68a80800021080c010b200641d0006a200110f38a808000418080808078210c024020062802502209418080808078470d00200628025421080c010b2006280258210b200628025421082009210c0b200120012d001841016a3a0018200110da8a80800021090240200c418080808078460d002009450d020240200c450d00200841002802c0a3c68000118080808000000b200921080c010b2009450d00024020092802000d00200941086a280200450d00200928020441002802c0a3c68000118080808000000b200941002802c0a3c68000118080808000000b2008200110d68a80800021082000418080808078360200200020083602040c030b2000200b360208200020083602042000200c3602000c020b200641106a200720092009200820092008491b10878380800041002d00fca3c680001a2006280214210c20062802102101411441002802c8a3c680001181808080000022080d004104411410b280808000000b2008200136020c200841183602002000200836020420004180808080783602002008200c3602100b200641e0006a2480808080000b8b0901077f23808080800041c0006b2205248080808000200028020c21060240024002400240200041146a2802002207200041106a28020022084f0d002000410c6a21090340200620076a2d0000220a41776a220b41174b0d024101200b74419380800471450d022000200741016a220736021420082007470d000b200821070b200541086a200620082008200741016a220720082007491b10878380800041002d00fca3c680001a200528020c210b200528020821000240411441002802c8a3c68000118180808000002207450d002007200036020c200741053602002007200b3602100c020b4104411410b280808000000b024002400240024002400240200a41db00460d00200a41fb00460d0120002005413f6a41b4d5c4800010d58a808000210b0c020b200020002d0018220a417f6a220b3a0018200741016a2107200b41ff0171450d042000200a3a00182000200736021441002107200010da8a808000220b0d010c050b200020002d0018417f6a220b3a0018200741016a2107200b41ff0171450d022000200736021402400240024002400240200720084f0d000340200620076a2d0000220a41776a220b41194b0d0402404101200b744193808004710d00200b4119470d05200041003602082000200741016a360214200541306a20092000108a8380800020052802304102460d0320052802342005280238419cadc48000410010e58a808000210b0c060b2000200741016a220736021420082007470d000b200821070b200541286a200620082008200741016a220720082007491b10878380800041002d00fca3c680001a200528022c210720052802282108411441002802c8a3c6800011818080800000220b450d01200b200836020c200b4103360200200b20073602100c030b2005280234210b0c020b4104411410b280808000000b0240200a41fd00470d004100210b0c010b200541206a200620082008200741016a220720082007491b10878380800041002d00fca3c680001a2005280224210720052802202108411441002802c8a3c6800011818080800000220b450d02200b200836020c200b4111360200200b20073602100b200020002d001841016a3a0018200010d98a80800021080240200b0d00410021072008210b20080d010c050b2008450d00024020082802000d00200841086a280200450d00200828020441002802c0a3c68000118080808000000b200841002802c0a3c68000118080808000000b200b200010d68a80800021070c030b4104411410b280808000000b200541186a200620082008200720082007491b10878380800041002d00fca3c680001a200528021c210b200528021821000240411441002802c8a3c68000118180808000002207450d002007200036020c200741183602002007200b3602100c020b4104411410b280808000000b200541106a200620082008200720082007491b10878380800041002d00fca3c680001a2005280214210b20052802102100411441002802c8a3c68000118180808000002207450d012007200036020c200741183602002007200b3602100b200541c0006a24808080800020070f0b4104411410b280808000000bd91e030d7f017e017f23808080800041f0016b2206248080808000200128020c2107024002400240024002400240024002400240024002400240024002400240024002400240200141146a2802002208200141106a28020022094f0d002001410c6a210a0340200720086a2d0000220b41776a220c41174b0d024101200c74419380800471450d022001200841016a220836021420092008470d000b200921080b200641e8006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628026c210c20062802682101411441002802c8a3c68000118180808000002208450d012008200136020c200841053602002000418080808078360218200020083602002008200c3602100c100b024002400240200b41db00460d00200b41fb00460d012001200641ef016a41c4d5c4800010d58a80800021080c110b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d0e200120083602140240200820094f0d000340200720086a2d0000220b41776a220c41174b0d0b4101200c74419380800471450d0b2001200841016a220836021420092008470d000b200921080b200641306a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a2006280234210c20062802302109411441002802c8a3c68000118180808000002208450d012008200936020c200841023602002008200c360210418080808078210b0c0a0b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d0720012008360214024020082009490d00418080808078210d0c040b200641e1016a210e200641d0016a410472210f418080808078210d410321104100210b0340200a28020021070240024002400240024002400240024002400240024002400240024002400240024003400240200720086a2d0000220c41776a0e24000003030003030303030303030303030303030303030300030303030303030303030304020b2001200841016a220836021420092008470d000b200921080c150b200c41fd00460d050b200b4101710d012008210b0c070b0240200b4101710d002008210b0c080b2001200841016a220b360214200b20094f0d0103402007200b6a2d0000220c41776a220841174b0d074101200874419380800471450d072001200b41016a220b3602142009200b470d000b200921080c020b200641c0006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a2006280244210c20062802402109411441002802c8a3c68000118180808000002208450d032008200936020c200841083602002008200c3602100c120b200841016a21080b200641e0006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a2006280264210c20062802602109411441002802c8a3c68000118180808000002208450d022008200936020c200841053602002008200c3602100c100b0240200d418080808078460d000240201041ff01714103470d0041e3cec48000410b10e38a8080002108200d450d07201141002802c0a3c68000118080808000000c070b200641a8016a41086a200641c0016a41086a280200360200200620062902c0013703a801200620062800b8013602a0012006200641b8016a41036a2800003600a3012012ad4220862011ad842113201421080c110b41d8cec48000410b10e38a80800021080c050b4104411410b280808000000b4104411410b280808000000b200c4122460d01200c41fd00470d00200641d8006a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a200628025c210c20062802582109411441002802c8a3c68000118180808000002208450d042008200936020c200841153602002008200c3602100c0c0b200641c8006a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a200628024c210c20062802482109411441002802c8a3c68000118180808000002208450d022008200936020c200841113602002008200c3602100c0b0b200141003602082001200b41016a360214200641d0016a200a2001108a8380800020062802d401210820062802d0014102460d0a02400240024020062802d801220c410b470d00200841f289c38000410b10888e808000450d01200841fd89c38000410b10888e808000450d020b2008200c41888ac38000410210e58a80800021080c0c0b0240200d418080808078460d0041d8cec48000410b10e78a80800021080c0c0b200110d78a80800022080d01200641d0016a200110f28a80800020062802d4012111024020062802d001220d418080808078470d00201121080c020b20062802d80121120c050b0240201041ff01714103460d0041e3cec48000410b10e78a80800021080c0b0b200110d78a80800022080d0a200641d0016a2001200820082008200810f98a80800020062802d001211420062d00e00122104103470d03201421080c0a0b418080808078210d0c0a0b4104411410b280808000000b4104411410b280808000000b200641c0016a41086a200f41086a2802003602002006200f2902003703c0012006200e2800003602b8012006200e41036a2800003600bb010b4101210b20012802142208200128021022094f0d030c000b0b4104411410b280808000000b4104411410b280808000000b200a28020021070b200641d0006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a2006280254210c20062802502109411441002802c8a3c68000118180808000002208450d022008200936020c200841033602002008200c3602100b0240200d41808080807872418080808078460d00201141002802c0a3c68000118080808000000b418080808078210d0b200120012d001841016a3a0018418080808078210c2013a72109200110d98a808000210702400240200d418080808078460d0020070d01200641f8006a41086a200641a8016a41086a280200360200200620062903a801370378200620062802a001360270200620062800a301360073200d210c0c070b2007450d06024020072802000d00200741086a280200450d00200728020441002802c0a3c68000118080808000000b200741002802c0a3c68000118080808000000c060b418080808078210c0240200d0d00200721080c060b200941002802c0a3c6800011808080800000200721080c050b4104411410b280808000000b200641386a200720092009200820092008491b10878380800041002d00fca3c680001a200628023c210c20062802382101411441002802c8a3c680001181808080000022080d064104411410b280808000000b0240200b41dd00470d00418080808078210b41004194cfc4800041c8cec4800010e68a80800021080c010b200641d0016a200110f28a808000418080808078210b024020062802d001220a418080808078470d0020062802d40121080c010b20062802d401210d200128020c21090240024002400240024002400240024002400240200128021422082001280210220c4f0d0020063502d801211303400240200920086a2d0000220741776a0e24000005050005050505050505050505050505050505050500050505050505050505050503040b2001200841016a2208360214200c2008470d000b200c21080b200641186a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a200628021c210c20062802182109411441002802c8a3c68000118180808000002208450d042008200936020c200841023602002008200c3602100c080b2001200841016a220836021402402008200c4f0d000340200920086a2d0000220b41776a220741174b0d084101200774419380800471450d082001200841016a2208360214200c2008470d000b200c21080b200641206a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a2006280224210c20062802202109411441002802c8a3c68000118180808000002208450d052008200936020c200841053602002008200c3602100c070b200741dd00460d010b200641106a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a2006280214210c20062802102109411441002802c8a3c68000118180808000002208450d022008200936020c200841073602002008200c3602100c050b41014194cfc4800041c8cec4800010e68a80800021080c040b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b0240200b41dd00470d00200641286a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a200628022c210c20062802282109411441002802c8a3c68000118180808000002208450d052008200936020c200841153602002008200c3602100c010b200641d0016a2001200820082008200810f98a80800020062802d001210820062d00e00122104103460d0020064198016a200641dc016a280200360200200620062902d40137039001200620062800e101360288012006200641e4016a28000036008b012013422086200dad842113200a210b0c020b418080808078210b200a450d00200d41002802c0a3c68000118080808000000b0b200120012d001841016a3a0018418080808078210c2013a72109200110da8a808000210702400240200b418080808078460d0020070d01200641f8006a41086a20064190016a41086a280200360200200620062903900137037820062006280288013602702006200628008b01360073200b210c0c020b2007450d01024020072802000d00200741086a280200450d00200728020441002802c0a3c68000118080808000000b200741002802c0a3c68000118080808000000c010b418080808078210c0240200b450d00200941002802c0a3c68000118080808000000b200721080b200c418080808078460d0320002006290378370204200020103a0010200020062802703600112000200936021c2000200c36021820002008360200200041146a2006280073360000200020134220883e02202000410c6a20064180016a2802003602000c040b4104411410b280808000000b200641086a200720092009200820092008491b10878380800041002d00fca3c680001a200628020c210c20062802082101411441002802c8a3c680001181808080000022080d004104411410b280808000000b2008200136020c200841183602002000418080808078360218200020083602002008200c3602100c010b2008200110d68a80800021082000418080808078360218200020083602000b200641f0016a2480808080000bbc13010a7f23808080800041e0006b2206248080808000200128020c2107024002400240200141146a2802002208200141106a28020022094f0d002001410c6a210a0340200720086a2d0000220b41776a220c41174b0d024101200c74419380800471450d022001200841016a220836021420092008470d000b200921080b200641086a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628020c210c200628020821010240411441002802c8a3c68000118180808000002208450d002008200136020c200841053602002000200836020420004180808080783602002008200c3602100c020b4104411410b280808000000b02400240024002400240024002400240024002400240200b41db00460d00200b41fb00460d012001200641df006a41a4d5c4800010d58a80800021080c070b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d08200120083602140240200820094f0d000340200720086a2d0000220b41776a220c41174b0d064101200c74419380800471450d062001200841016a220836021420092008470d000b200921080b200641186a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628021c210c20062802182109411441002802c8a3c68000118180808000002208450d012008200936020c200841023602002008200c360210418080808078210c0c050b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d02200120083602140240024002400240024020082009490d00418080808078210d0c010b418080808078210d4100210e0340200a280200210702400240024002400240024002400240024002400240024002400240024003400240200720086a2d0000220c41776a0e24000003030003030303030303030303030303030303030300030303030303030303030304020b2001200841016a220836021420092008470d000b200921080c100b200c41fd00460d050b200e4101710d012008210b0c070b0240200e4101710d002008210b0c080b2001200841016a220b360214200b20094f0d0103402007200b6a2d0000220c41776a220841174b0d074101200874419380800471450d072001200b41016a220b3602142009200b470d000b200921080c020b200641286a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628022c210c20062802282109411441002802c8a3c68000118180808000002208450d032008200936020c200841083602002008200c3602100c0d0b200841016a21080b200641c8006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628024c210c20062802482109411441002802c8a3c68000118180808000002208450d022008200936020c200841053602002008200c3602100c0b0b200d210c200f2108200d418080808078470d0e419ccfc48000410810e38a8080002108418080808078210c0c0e0b4104411410b280808000000b4104411410b280808000000b200c4122460d01200c41fd00470d00200641c0006a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a2006280244210c20062802402109411441002802c8a3c68000118180808000002208450d032008200936020c200841153602002008200c3602100c070b200641306a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a2006280234210c20062802302109411441002802c8a3c68000118180808000002208450d012008200936020c200841113602002008200c3602100c060b200141003602082001200b41016a360214200641d0006a200a2001108a838080002006280254210820062802504102460d05024002402006280258220c4108470d00200829000041002900f3ebc58000510d010b2008200c41fcebc58000410110e58a80800021080c060b0240200d418080808078460d00419ccfc48000410810e78a80800021080c070b0240200110d78a8080002208450d00418080808078210c0c0a0b200641d0006a200110f18a808000418080808078210c2006280254210f2006280250220d418080808078470d02200f21080c090b4104411410b280808000000b4104411410b280808000000b2006280258210b4101210e2001280214220820012802102209490d000b200a28020021070b200641386a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628023c210c20062802382109411441002802c8a3c68000118180808000002208450d022008200936020c200841033602002008200c3602100b418080808078210c200d418080808078460d030b418080808078210c0240200d450d00200f41002802c0a3c68000118080808000000b0c020b4104411410b280808000000b4104411410b280808000000b200120012d001841016a3a0018200110d98a808000210902400240200c418080808078460d002009450d06200c0d01200921080c050b2009450d04024020092802000d00200941086a280200450d00200928020441002802c0a3c68000118080808000000b200941002802c0a3c68000118080808000000c040b200841002802c0a3c6800011808080800000200921080c030b200641206a200720092009200820092008491b10878380800041002d00fca3c680001a2006280224210c20062802202101411441002802c8a3c680001181808080000022080d054104411410b280808000000b0240200b41dd00470d00418080808078210c410041c8cfc4800041c8cec4800010e68a80800021080c010b200641d0006a200110f18a808000418080808078210c024020062802502209418080808078470d00200628025421080c010b2006280258210b200628025421082009210c0b200120012d001841016a3a0018200110da8a80800021090240200c418080808078460d002009450d020240200c450d00200841002802c0a3c68000118080808000000b200921080c010b2009450d00024020092802000d00200941086a280200450d00200928020441002802c0a3c68000118080808000000b200941002802c0a3c68000118080808000000b2008200110d68a80800021082000418080808078360200200020083602040c030b2000200b360208200020083602042000200c3602000c020b200641106a200720092009200820092008491b10878380800041002d00fca3c680001a2006280214210c20062802102101411441002802c8a3c680001181808080000022080d004104411410b280808000000b2008200136020c200841183602002000200836020420004180808080783602002008200c3602100b200641e0006a2480808080000be13205077f017e017f027e057f2380808080004180026b2206248080808000200128020c210702400240024002400240024002400240024002400240024002400240024002400240024002400240200141146a2802002208200141106a28020022094f0d002001410c6a210a0340200720086a2d0000220b41776a220c41174b0d024101200c74419380800471450d022001200841016a220836021420092008470d000b200921080b200641d0016a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a20062802d401210c20062802d0012101411441002802c8a3c68000118180808000002208450d012008200136020c20084105360200200041033a0010200020083602002008200c3602100c120b024002400240200b41db00460d00200b41fb00460d012001200641ff016a4194d5c4800010d58a80800021080c110b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d11200120083602140240200820094f0d000340200720086a2d0000220b41776a220c41174b0d0f4101200c74419380800471450d0f2001200841016a220836021420092008470d000b200921080b200641286a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628022c210c20062802282109411441002802c8a3c68000118180808000002208450d012008200936020c200841023602002008200c3602104103210c0c0e0b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d0b20012008360214200820094f0d084200210d4103210e4100210b0340200a280200210702400240024002400240024002400240024002400240024002400240024002400240024003400240200720086a2d0000220c41776a0e24000003030003030303030303030303030303030303030300030303030303030303030304020b2001200841016a220836021420092008470d000b200921080c1b0b200c41fd00460d050b200b4101710d012008210b0c080b0240200b4101710d002008210b0c090b2001200841016a220b360214200b20094f0d0103402007200b6a2d0000220c41776a220841174b0d084101200874419380800471450d082001200b41016a220b3602142009200b470d000b200921080c020b200641386a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628023c210c20062802382109411441002802c8a3c68000118180808000002208450d032008200936020c200841083602002008200c3602104103210c0c180b200841016a21080b200641c8016a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a20062802cc01210c20062802c8012109411441002802c8a3c68000118180808000002208450d022008200936020c200841053602002008200c3602104103210c0c160b200da7450d024103210c0240200e41ff01714103470d004185cec48000410d10e38a80800021080c160b200f422088a72109200fa72108200e210c0c150b4104411410b280808000000b4104411410b280808000000b4103210c4184cec48000410110e38a80800021080c120b200c4122460d01200c41fd00470d00200641c0016a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a20062802c401210c20062802c0012109411441002802c8a3c68000118180808000002208450d032008200936020c200841153602002008200c3602104103210c0c110b200641c0006a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a2006280244210c20062802402109411441002802c8a3c68000118180808000002208450d012008200936020c200841113602002008200c3602104103210c0c100b200141003602082001200b41016a360214200641e0016a200a2001108a8380800020062802e4012108024020062802e0014102460d000240024002400240024020062802e801417f6a0e0d00090909090909090909090901090b20082d000041e300460d010c080b200841a9dac28000410d10888e8080000d07200e41ff01714103460d014103210c4185cec48000410d10e78a80800021080c130b200da74101460d05200110d78a80800022080d02200641e0016a200110ef8a80800020062802e0010d0120062903f001211020062903e801210f4201210d0c070b200110d78a80800022080d01200641e0016a200110cb8680800020062d00e0010d0020062d00e101210e0c060b20062802e40121080b4103210c0c0f0b4104411410b280808000000b4104411410b280808000000b4103210c4184cec48000410110e78a80800021080c0c0b4103210c200110d78a80800022080d0820014100360208200128020c210920012802142208200128021022074f0d07410021110340410020076b2112200841026a2108024002400240024002400240024002400240024002400240024002400240024002400240024002400240034002400240200920086a2213417e6a2d0000220b41776a0e2501010808010808080808080808080808080808080808080108060808080808080808080809000b200b41a57f6a0e21060707070707070707070704070707070707070207070707070307070707070706070b20012008417f6a3602142012200841016a22086a4102470d000b200721080c1d0b20012008417f6a220b3602140240200b2007490d002008417f6a210b0c1c0b2001200836021402402013417f6a2d000041f500470d0020082007200b2007200b4b1b220b460d1c2001200841016a2212360214024020132d000041ec00460d00200841016a21080c010b2012200b460d1c2001200841026a360214201341016a2d000041ec00460d08200841026a21080b20064198016a20092007200810878380800041002d00fca3c680001a200628029c0121092006280298012107411441002802c8a3c68000118180808000002208450d0c2008200736020c20084109360200200820093602100c200b20012008417f6a220b3602140240200b2007490d002008417f6a210b0c1a0b2001200836021402402013417f6a2d000041f200470d0020082007200b2007200b4b1b220b460d1a2001200841016a2212360214024020132d000041f500460d00200841016a21080c010b2012200b460d1a2001200841026a360214201341016a2d000041e500460d07200841026a21080b200641a8016a20092007200810878380800041002d00fca3c680001a20062802ac01210920062802a8012107411441002802c8a3c68000118180808000002208450d0a2008200736020c20084109360200200820093602100c1f0b20012008417f6a220b3602140240200b2007490d002008417f6a210b0c180b2001200836021402402013417f6a2d000041e100470d0020082007200b2007200b4b1b220b460d182001200841016a2212360214024020132d000041ec00460d00200841016a21080c010b2012200b460d182001200841026a22123602140240201341016a2d000041f300460d00200841026a21080c010b2012200b460d182001200841036a360214201341026a2d000041e500460d06200841036a21080b200641b8016a20092007200810878380800041002d00fca3c680001a20062802bc01210920062802b8012107411441002802c8a3c68000118180808000002208450d082008200736020c20084109360200200820093602100c1e0b20012008417f6a360214200a108e8380800022080d1a0c040b02402001280200200128020822086b201141017122094f0d0020012008200910ab86808000200128020821080b02402009450d00200128020420086a20143a0000200841016a21080b200120083602082001200128021441016a360214410021150c040b200b41506a41ff0171410a490d0120064188016a2009200720072008417f6a220820072008491b10878380800041002d00fca3c680001a200628028c0121092006280288012107411441002802c8a3c68000118180808000002208450d042008200736020c2008410a360200200820093602100c1b0b20012008417f6a3602140b200110d18a80800022080d160b4101211502402011410171450d002014210b0c010b20012802082208450d0d20012008417f6a2208360208200128020420086a2d0000210b0b02402001280214221320012802102207490d00200b21140c060b20012802042111200128020c210920012802082112200b21140340201321080240024002400240034002400240200920086a2d0000220b41776a0e2401010e0e010e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e010e0e0e0e0e0e0e0e0e0e0e03000b200b41dd00460d03200b41fd00460d040c0d0b2001200841016a220836021420072008470d000b200721130c0a0b2015410171450d0b2001200841016a22083602140c0b0b201441ff017141db00470d090c010b201441ff017141fb00470d080b2001200841016a22133602142012450d0d20012012417f6a2212360208201120126a2d0000211441012115201320074f0d050c000b0b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200841016a21130b41022109024002400240201441ff0171220841db00460d00200841fb00470d01410321090b200641f8006a200a28020020072007201341016a220820072008491b10878380800041002d00fca3c680001a200628027c21072006280278210b411441002802c8a3c68000118180808000002208450d012008200b36020c20082009360200200820073602100c130b419ccac480004128418ccdc4800010f880808000000b4104411410b280808000000b2015410171450d004107210b201441ff0171220a41db00460d02200a41fb00460d01419ccac480004128419ccdc4800010f880808000000b201441ff017141fb00470d020240200820074f0d00034002400240200920086a2d000041776a220b41194b0d004101200b744193808004710d01200b4119470d002001200841016a360214200a108e8380800022080d10200128020c21090240024002400240024020012802142208200128021022074f0d0003400240200920086a2d000041776a0e320000030300030303030303030303030303030303030303000303030303030303030303030303030303030303030303030304030b2001200841016a220836021420072008470d000b200721080b200641e8006a200920072007200841016a220820072008491b10878380800041002d00fca3c680001a200628026c21092006280268210c411441002802c8a3c68000118180808000002208450d022008200c36020c4103210c20084103360200200820093602100c170b200641e0006a200920072007200841016a220820072008491b10878380800041002d00fca3c680001a2006280264210920062802602107411441002802c8a3c68000118180808000002208450d022008200736020c20084106360200200820093602100c160b2001200841016a22083602140c080b4104411410b280808000000b4104411410b280808000000b200641d0006a200920072007200841016a220820072008491b10878380800041002d00fca3c680001a20062802542109200628025021070240411441002802c8a3c68000118180808000002208450d002008200736020c20084111360200200820093602100c130b4104411410b280808000000b2001200841016a220836021420072008470d000b200721080b200641d8006a200920072007200841016a220820072008491b10878380800041002d00fca3c680001a200628025c21092006280258210c0240411441002802c8a3c68000118180808000002208450d002008200c36020c4103210c20084103360200200820093602100c100b4104411410b280808000000b4108210b0b200641c8006a200920072007200841016a220820072008491b10878380800041002d00fca3c680001a200628024c2109200628024821070240411441002802c8a3c68000118180808000002208450d002008200736020c2008200b360200200820093602100c0e0b4104411410b280808000000b4101211120082007490d000c080b0b4101210b20012802142208200128021022094f0d080c000b0b4104411410b280808000000b4104411410b280808000000b200641b0016a20092007200b10878380800041002d00fca3c680001a20062802b401210920062802b00121070240411441002802c8a3c68000118180808000002208450d002008200736020c20084105360200200820093602100c070b4104411410b280808000000b200641a0016a20092007200b10878380800041002d00fca3c680001a20062802a401210920062802a00121070240411441002802c8a3c68000118180808000002208450d002008200736020c20084105360200200820093602100c060b4104411410b280808000000b20064190016a20092007200b10878380800041002d00fca3c680001a200628029401210920062802900121070240411441002802c8a3c68000118180808000002208450d002008200736020c20084105360200200820093602100c050b4104411410b280808000000b200641f0006a200920072007200841016a220820072008491b10878380800041002d00fca3c680001a20062802742109200628027021070240411441002802c8a3c68000118180808000002208450d002008200736020c20084105360200200820093602100c040b4104411410b280808000000b0c020b200a28020021070b20064180016a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a2006280284012109200628028001210c411441002802c8a3c68000118180808000002208450d012008200c36020c4103210c20084103360200200820093602100b200120012d001841016a3a0018200110d98a808000210b0240200c41ff01714103460d0020082107200b2108200b0d060c050b200b450d050240200b2802000d00200b41086a280200450d00200b28020441002802c0a3c68000118080808000000b200b41002802c0a3c68000118080808000000c050b4104411410b280808000000b200641306a200720092009200820092008491b10878380800041002d00fca3c680001a2006280234210c20062802302101411441002802c8a3c680001181808080000022080d054104411410b280808000000b0240200b41dd00470d004103210c410041c0cec4800041c8cec4800010e68a80800021080c010b200641e0016a200110ef8a808000024020062802e0010d00200128020c210b0240024002400240024002400240024002402001280214220c200128021022074f0d00200641f0016a290300211020062802ec01210920062802e801210803400240200b200c6a2d0000220a41776a0e24000005050005050505050505050505050505050505050500050505050505050505050503040b2001200c41016a220c3602142007200c470d000b2007210c0b200641106a200b20072007200c41016a220820072008491b10878380800041002d00fca3c680001a2006280214210c20062802102109411441002802c8a3c68000118180808000002208450d032008200936020c200841023602002008200c3602104103210c0c090b2001200c41016a220c3602140240200c20074f0d000340200b200c6a2d0000221341776a220a41174b0d084101200a74419380800471450d082001200c41016a220c3602142007200c470d000b2007210c0b200641186a200b20072007200c41016a220820072008491b10878380800041002d00fca3c680001a200628021c210c20062802182109411441002802c8a3c68000118180808000002208450d042008200936020c200841053602002008200c3602104103210c0c080b200a41dd00460d040b200641086a200b20072007200c41016a220820072008491b10878380800041002d00fca3c680001a200628020c210c20062802082109411441002802c8a3c68000118180808000002208450d012008200936020c200841073602002008200c3602104103210c0c060b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b4103210c410141c0cec4800041c8cec4800010e68a80800021080c020b02400240201341dd00470d00200641206a200b20072007200c41016a220820072008491b10878380800041002d00fca3c680001a2006280224210c20062802202109411441002802c8a3c68000118180808000002208450d012008200936020c200841153602002008200c3602104103210c0c030b200641e0016a200110cb8680800020062d00e0010d0120062d00e101210c0c020b4104411410b280808000000b20062802e40121084103210c0b200120012d001841016a3a0018200110da8a808000210b0240200c41ff01714103460d0020082107200b2108200b450d010c020b200b450d010240200b2802000d00200b41086a280200450d00200b28020441002802c0a3c68000118080808000000b200b41002802c0a3c68000118080808000000c010b200020062800d9013600112000200c3a0010200020103703082000200936020420002007360200200041146a200641dc016a2800003600000c030b2008200110d68a8080002108200041033a0010200020083602000c020b2006200720092009200820092008491b10878380800041002d00fca3c680001a2006280204210c20062802002101411441002802c8a3c680001181808080000022080d004104411410b280808000000b2008200136020c20084118360200200041033a0010200020083602002008200c3602100b20064180026a2480808080000be01b010d7f2380808080004190016b2207248080808000024002400240024002400240024002400240024002400240024002400240024020010e020102000b200041146a2108200141146c2209416c6a41146e210a200041106a280200210b2000410c6a280200210c2000280200210d4100210e200041046a280200220f21100340410021110240200f2008200e41146c6a221241046a2802002213200f2013491b2213450d0020122802002112410021110340200d20116a2d0000201220116a2d0000470d012013201141016a2211470d000b201321110b2011201020112010491b2110200e41016a220e200a470d000b20040d03201020024b0d024100210d0c040b0240200328020822112003280200470d002003201110a885808000200328020821110b200328020420116a41003a00002003200328020841016a3602080c0d0b200041106a28020021112000410c6a280200211320054101470d030240201120064f0d002007410c6a20113602002007201336020820074180808080783602040c0c0b200741106a201320114100280298a3c680001185808080000041002d00fca3c680001a412041002802c8a3c68000118180808000002211450d0420112007290010370000201141186a200741106a41186a290000370000201141106a200741106a41106a290000370000201141086a200741106a41086a2900003700002007412036020c20072011360208200741203602040c0b0b2010200f4b0d04200741003602702007428080808010370268200020012010200741e8006a41002005200610fa8a808000200728026c21132007280268210202400240200728027022114120490d00200741106a201320114100280298a3c6800011858080800000200741203602502007200741d0006a36025c200741dc006a200310c08a80800002402003280200200328020822116b411f4b0d0020032011412010b182808000200328020821110b2003201141206a360208200328020420116a22112007290010370000201141086a200741106a41086a290000370000201141106a200741106a41106a290000370000201141186a200741106a41186a2900003700000c010b2007201136025c2007200741dc006a360210200741106a200310c08a80800002402003280200200328020822126b20114f0d0020032012201110b182808000200328020821120b200328020420126a2013201110848e8080001a2003201220116a3602080b2002450d0b201341002802c0a3c68000118080808000000c0b0b4100210a201020024d0d002010200f4b0d04200d20026a210d201020026b210a201021020b200741c8006a4200370300200741c0006a4200370300200741386a4200370300200741306a4200370300200741286a4200370300200741106a41106a4200370300200741186a4200370300200742003703102002200f46210841002111034020112110200820014b0d0741002113024020082001460d002009200841146c22116b2112200020116a211141002113034020022011280204220e4f0d08201128020020026a2d0000201041ff0171470d01201141146a2111201341016a21132012416c6a22120d000b0b200741106a20104102746a2013360200201320086a2108201041016a22114110470d000b02402002200f470d000240024020054101470d00200b20064f0d010b2007200b3602582007200c360254418080808078211120074180808080783602500c090b200741e8006a200c200b4100280298a3c680001185808080000041002d00fca3c680001a412041002802c8a3c68000118180808000002211450d0520112007290068370000201141186a200741e8006a41186a290000370000201141106a200741e8006a41106a290000370000201141086a200741e8006a41086a290000370000200741203602582007201136025420074120360250200741e8006a200741d0006a108b878080002007280270210b200728026c210c200728026821110c080b418180808078211120074181808080783602500c070b2007410c6a20113602002007201336020820074180808080783602040c070b4101412010b280808000000b2010200f41c4d6c48000109581808000000b2010200f41d4d6c48000109581808000000b4101412010b280808000000b2002200e4184d7c4800010f980808000000b2008200141f4d6c48000109481808000000b02400240024002400240200d450d00024002400240024002404101410241042011418080808078461b2011418180808078461b417f6a0e0400010203000b413e210e200a413e200a413e491b211241bf012108418001210f0c030b413e210e200a413e200a413e491b211241ff01210841c001210f0c020b411e210e200a411e200a411e491b2112413f21084120210f0c010b410e210e200a410e200a410e491b2112411f21084110210f0b0240200a4101712213450d00200d2d000021100b20074180016a4102360200200741fc006a200a417e71360200200720103a0075200741013a0070200741013602682007200a20126b36026c200720133a00742007200d20136a36027820072008200f201272200e200a491b3a00712003200741e8006a1099878080002007280228410047410674200728022c4100474107747220072802204100474104742007280224410047410574722007280214410047410174200728021041004772200728021841004741027472200728021c41004741037472727221122007280234410047410174200728023041004772200728023841004741027472200728023c41004741037472200728024041004741047472200728024441004741057472200728024841004741067472200728024c41004741077472210d02402003280200200328020822136b41014b0d0020032013410210ab86808000200328020821130b200328020420136a200d410874201241ff0171723b00002003201341026a221336020802402011418180808078460d0002402011418080808078470d002007200b36028c0120072007418c016a36025c200741dc006a200310c08a80800002402003280200200328020822116b200b4f0d0020032011200b10ab86808000200328020821110b200328020420116a200c200b10848e8080001a20032011200b6a3602080c010b0240200328020020136b200b4f0d0020032013200b10ab86808000200328020821130b200328020420136a200c200b10848e8080001a20032013200b6a3602082011450d00200c41002802c0a3c68000118080808000000b200241016a2110200728025022084181808080784721112005450d014100211303400240200741106a20136a2802002212450d00201220116a22022012490d05200220014b0d0620074100360264200742808080801037025c2000201141146c6a20122010200741dc006a20044101200610fa8a80800020072802602112200728025c210d02400240200728026422114120490d00200741e8006a201220114100280298a3c68000118580808000002007412036028801200720074188016a36028c012007418c016a200310c08a80800002402003280200200328020822116b411f4b0d0020032011412010b182808000200328020821110b2003201141206a360208200328020420116a22112007290068370000201141086a200741e8006a41086a290000370000201141106a200741e8006a41106a290000370000201141186a200741e8006a41186a2900003700000c010b2007201136028c0120072007418c016a360268200741e8006a200310c08a808000024020032802002003280208220e6b20114f0d002003200e201110b1828080002003280208210e0b2003280204200e6a2012201110848e8080001a2003200e20116a3602080b0240200d450d00201241002802c0a3c68000118080808000000b200221110b201341046a221341c000470d000c030b0b200741f4006a42003702002007410136026c200741f4d2c480003602682007200741dc006a360270200741e8006a41e4d3c4800010f680808000000b4100211303400240200741106a20136a2802002212450d00201220116a22022012490d03200220014b0d0420074100360264200742808080801037025c2000201141146c6a20122010200741dc006a20044100200610fa8a80800020072802602112200728025c210d02400240200728026422114120490d00200741e8006a201220114100280298a3c68000118580808000002007412036028801200720074188016a36028c012007418c016a200310c08a80800002402003280200200328020822116b411f4b0d0020032011412010b182808000200328020821110b2003201141206a360208200328020420116a22112007290068370000201141086a200741e8006a41086a290000370000201141106a200741e8006a41106a290000370000201141186a200741e8006a41186a2900003700000c010b2007201136028c0120072007418c016a360268200741e8006a200310c08a808000024020032802002003280208220e6b20114f0d002003200e201110b1828080002003280208210e0b2003280204200e6a2012201110848e8080001a2003200e20116a3602080b0240200d450d00201241002802c0a3c68000118080808000000b200221110b201341046a221341c000470d000b0b2008418280808078480d032008450d03200728025441002802c0a3c68000118080808000000c030b2011200241e4d6c48000109681808000000b2002200141e4d6c48000109581808000000b0240200041046a28020022112002490d002003200028020020026a201120026b200741046a10af858080000c010b2002201141b4d6c48000109481808000000b20074190016a2480808080000bc50401057f23808080800041f0006b220424808080800020044100360214200442808080801037020c41002d00fca3c680001a0240410441002802c8a3c68000118180808000002205450d00200541003602002004410036025c20044200370254200442818080802037024c20042005360248200441003602442004410036023c200442003702342004410036022c2004420037022420042004410c6a360260200441186a200441246a109c878080002004410036026c2004428080808010370264200428021c2206200428022022074100200441e4006a20012002200310fa8a80800020042802642101200020042802682208200428026c4100280298a3c680001185808080000002402007450d002007410171210041002102024020074101460d00200641206a21032007417e7121074100210203400240200341686a280200450d002003416c6a28020041002802c0a3c68000118080808000000b02402003417c6a280200450d00200328020041002802c0a3c68000118080808000000b200341286a21032007200241026a2202470d000b0b2000450d002006200241146c6a2203280208450d00200341086a28020441002802c0a3c68000118080808000000b02402004280218450d00200641002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000240200428020c450d00200428021041002802c0a3c68000118080808000000b02402001450d00200841002802c0a3c68000118080808000000b200441f0006a2480808080000f0b4104410410b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c00037032820064100360220200641003602182006419682808000360210200642ab8ad7e1bb97ae9f51370308200642f2f9a5a49996c5e03237030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bb30403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420d370224200541b4fbc480003602202005410e36021c200541a6fbc480003602182005419482808000360210200542c0969aec91e181fcf200370308200542d7a7fbff94a5afcaf800370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420c37022420054194fbc480003602202005410d36021c20054187fbc48000360218200541ed83808000360210200542e7dca5b4fdd9bda7a17f370308200542accee9bcd783d4ea3937030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a4100290380f9c480003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541013a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410436021c200641a4e4c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903e0a7c580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054207370224200541e3e4c480003602202005410936021c200541dae4c48000360218200541f581808000360210200542ab8bffbed784ffa5937f370308200542c194a6a793ccc3a857370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c20054204370224200541f7e4c480003602202005410d36021c200541eae4c48000360218200541888180800036021020054298848fa1dab08ba174370308200542febac4ad81b6fafcb37f37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903f0f9c480003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541063a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bf50403047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c200542133702242005418993c580003602202005410936021c2005418093c58000360218200541ee83808000360210200542e5fbf9f4fcf79cdd3a3703082005429bb0d3a2d7cce1b225370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c2005420a370224200541d192c580003602202005410536021c200541b393c58000360218200541bc82808000360210200542899ac8f29d8ce69ac300370308200542e78dcee4d0becc975737030020042802082109200428020c210a41002d00fca3c680001a0240412841002802c8a3c68000118180808000002206450d00200641206a41002902908bc58000370200200641186a41002902888bc58000370200200641106a41002902808bc58000370200200641086a41002902f88ac58000370200200641002902f08ac580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541093a00202005200336021c2005200236021820054105360214200520063602102005410536020c2005200741016a3602082005200a3602042005200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104412810b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642ed00370224200641b2b1c5800036022020064100360218200641f482808000360210200642c9e8d484edccb5e26c370308200642a79fcbb9e09aeaff7c37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641033a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000baa0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064203370224200641e1f2c480003602202006410536021c200641f0adc58000360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e37030041002d00fca3c680001a20042802042107200428020821080240411841002802c8a3c68000118180808000002209450d00200941106a41002902e8adc58000370200200941086a41002902e0adc58000370200200941002902d8adc580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a2206410a3a00202006200336021c2006200236021820064103360214200620093602102006410336020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104411810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420d370224200641cdf2c480003602202006410536021c200641c8f2c48000360218200641ef83808000360210200642ce9de6a5b39faacabf7f3703082006428f8dbaa0f1e8bfc67537030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d0020094100290380ebc480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641043a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903d0a3c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a2205410b3a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bd40403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054214370224200541df92c580003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410836021c200541b893c58000360218200541bc82808000360210200542899ac8f29d8ce69ac300370308200542e78dcee4d0becc975737030020042802082108200428020c210941002d00fca3c680001a0240411841002802c8a3c6800011818080800000220a450d00200a41106a41002902d48fc58000370200200a41086a41002902cc8fc58000370200200a41002902c48fc580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541083a00202005200336021c20052002360218200541033602142005200a3602102005410336020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104411810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410436021c200641acf2c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903b8e7c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641023a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410636021c200541a0fbc48000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c20054207370224200541e3e4c480003602202005410436021c20054183fbc48000360218200541f581808000360210200542ab8bffbed784ffa5937f370308200542c194a6a793ccc3a85737030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903e8fac480003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541053a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903d0a2c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541053a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bc00201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641bd80808000360210200642efc9c9edb5e7b3a6c700370308200642acf6debeefe0d9c8d30037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c00037032820064100360220200641003602182006419782808000360210200642fb8387e2d6839cd949370308200642d9adfaebb1c192ed5037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903f09ac580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541073a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903909ac580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541143a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000b900301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410436021c200641acf2c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a2004280204210720042802082108024041c80041002802c8a3c68000118180808000002206450d00200641e4f1c4800041c80010848e80800021090240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a2206410b3a00202006200336021c2006200236021820064109360214200620093602102006410936020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b410441c80010b280808000000bc30403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410736021c200541f0fac48000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240411041002802c8a3c6800011818080800000220a450d00200a41086a4100290298a1c58000370200200a4100290290a1c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541013a00202005200336021c20052002360218200541023602142005200a3602102005410236020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104411010b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420c370224200641f7fac480003602202006410336021c200641f392c58000360218200641f081808000360210200642a4d2928ecac0faa432370308200642c4ccab9c81ebb4b8db0037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903b09ac580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a2206410e3a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000baa0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064203370224200641e1f2c480003602202006410536021c200641f0adc58000360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e37030041002d00fca3c680001a20042802042107200428020821080240411841002802c8a3c68000118180808000002209450d00200941106a41002902e4aec58000370200200941086a41002902dcaec58000370200200941002902d4aec580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a2206410b3a00202006200336021c2006200236021820064103360214200620093602102006410336020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104411810b280808000000bbc0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410436021c200641acf2c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240412041002802c8a3c68000118180808000002209450d00200941186a41002902dce6c48000370200200941106a41002902d4e6c48000370200200941086a41002902cce6c48000370200200941002902c4e6c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641033a00202006200336021c2006200236021820064104360214200620093602102006410436020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104412010b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054207370224200541b0f2c480003602202005410336021c200541c0a8c58000360218200541ea81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c500370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420f370224200541c3a8c580003602202005410536021c200541cc92c580003602182005419183808000360210200542f6d183c8fca4ffd45a370308200542f99f94a5fdd3dbbaf90037030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903b8a8c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541023a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642e900370224200641d7afc5800036022020064100360218200641f082808000360210200642c2a7b4b19ace9c92d9003703082006429691e8a3b686eae56237030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bb90403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054214370224200541df92c580003602202005410436021c200541db92c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c20054204370224200541f7e4c480003602202005410a36021c200541f692c58000360218200541888180800036021020054298848fa1dab08ba174370308200542febac4ad81b6fafcb37f37030020042802082108200428020c210941002d00fca3c680001a024041f80041002802c8a3c68000118180808000002205450d00200541cc83c5800041f80010848e808000210a0240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541043a00202005200336021c200520023602182005410f3602142005200a3602102005410f36020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b410441f80010b280808000000b980503047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054214370224200541df92c580003602202005410436021c200541db92c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c2005420a370224200541d192c580003602202005410536021c200541cc92c58000360218200541bc82808000360210200542899ac8f29d8ce69ac300370308200542e78dcee4d0becc975737030020042802082109200428020c210a41002d00fca3c680001a0240413841002802c8a3c68000118180808000002205450d00200541306a41002902c492c58000370200200541286a41002902bc92c58000370200200541206a41002902b492c58000370200200541186a41002902ac92c58000370200200541106a41002902a492c58000370200200541086a410029029c92c580003702002005410029029492c580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c2006200236021820064107360214200620053602102006410736020c2006200741016a3602082006200a3602042006200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104413810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420a370224200641d192c580003602202006410636021c200641c1fbc48000360218200641ef8080800036021020064293888c8f89fdc6ec9e7f370308200642a5e9e3ab9e929adc2c37030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903989fc580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a2206410f3a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064203370224200641bcf2c480003602202006410536021c200641b7f2c48000360218200641ef8080800036021020064293888c8f89fdc6ec9e7f370308200642a5e9e3ab9e929adc2c37030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d0020094100290380e8c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903c89dc580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541043a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c00037032820064100360220200641003602182006419582808000360210200642f69a89928aa399ea003703082006429198b9e48fc6c3ad1d37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bd40503047f017e027f23808080800041206b2204248080808000200441106a41086a22054100360200200442808080808001370210200441106a410010a48680800020042802142005280200220641386c6a2207420437022c20074214370224200741df92c580003602202007410636021c200741ad93c58000360218200741f081808000360210200742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db00370300200441086a200641016a2206360200200420042902102208370300024020062008a7470d002004200610a486808000200428020821060b2004280204200641386c6a2207420437022c20074214370224200741df92c580003602202007410436021c200741db92c58000360218200741f081808000360210200742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db003703002005200641016a2206360200200420042903002208370310024020062008a7470d00200441106a200610a486808000200428021821060b2004280214200641386c6a2207420437022c2007420a370224200741d192c580003602202007410536021c200741cc92c58000360218200741bc82808000360210200742899ac8f29d8ce69ac300370308200742e78dcee4d0becc9757370300200428021021092004280214210a41002d00fca3c680001a0240411041002802c8a3c68000118180808000002205450d00200541086a41002902e48ec58000370200200541002902dc8ec580003702000240200128020822072001280200470d002001200710a086808000200128020821070b2001280204200741246c6a220741023a00202007200336021c2007200236021820074102360214200720053602102007410236020c2007200641016a3602082007200a3602042007200936020020002001290200370200200141086a2207200728020041016a2207360200200041086a2007360200200441206a2480808080000f0b4104411010b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006421c370224200641f5b2c5800036022020064100360218200641ed82808000360210200642e89d8d84e9a7e0ebbf7f370308200642ffa1f591d896faeca07f37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420737022420064185adc580003602202006410536021c20064180adc58000360218200641f083808000360210200642fef18cfcdbfae085d000370308200642c1e482bcf69e92d65b37030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903f8acc580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641093a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641fb82808000360210200642a8c786dcd8d2a98d17370308200642cecfe0e5d1c6dbf75737030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064208370224200641eef2c480003602202006410436021c200641eaf2c48000360218200641f183808000360210200642aac2d79da4bfd9a04e370308200642e6afce95f6a1ffa6c30037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d0020094100290398e5c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641053a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bc00201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c000370328200641003602202006410036021820064193828080003602102006429ccab49c93e2e495cf00370308200642d1c5efcdcd82cde1ff0037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bd20101027f41002d00fca3c680001a0240410841002802c8a3c68000118180808000002204450d00200441002903c0acc580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541083a00202005200336021c200520023602182005410136021420052004360210200542808080801037020820054280808080800137020020002001290200370200200141086a2201200128020041016a2201360200200041086a20013602000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410336021c200641c0a8c58000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903d0a9c580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641043a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a4100290398a0c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541123a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000b820401067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641e3e4c480003602202006410936021c200641dae4c48000360218200641f581808000360210200642ab8bffbed784ffa5937f370308200642c194a6a793ccc3a85737030041002d00fca3c680001a2004280204210720042802082108024041c00041002802c8a3c68000118180808000002206450d00200641386a41002902a8eec48000370200200641306a41002902a0eec48000370200200641286a4100290298eec48000370200200641206a4100290290eec48000370200200641186a4100290288eec48000370200200641106a4100290280eec48000370200200641086a41002902f8edc48000370200200641002902f0edc480003702000240200128020822092001280200470d002001200910a086808000200128020821090b2001280204200941246c6a2209410a3a00202009200336021c2009200236021820094108360214200920063602102009410836020c2009200541016a360208200920083602042009200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b410441c00010b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006421f37022420064191b3c5800036022020064100360218200641f382808000360210200642e5c18a8c83d9a2ecc200370308200642b4ff9ef1bdc3bdda2837030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641033a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410436021c200641a4e4c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903f8a9c580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641053a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000b980503047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420a370224200541d192c580003602202005410536021c200541cc92c58000360218200541bc82808000360210200542899ac8f29d8ce69ac300370308200542e78dcee4d0becc9757370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c20054204370224200541f7e4c480003602202005410a36021c200541f692c58000360218200541888180800036021020054298848fa1dab08ba174370308200542febac4ad81b6fafcb37f37030020042802082109200428020c210a41002d00fca3c680001a0240413841002802c8a3c68000118180808000002205450d00200541306a41002902dc87c58000370200200541286a41002902d487c58000370200200541206a41002902cc87c58000370200200541186a41002902c487c58000370200200541106a41002902bc87c58000370200200541086a41002902b487c58000370200200541002902ac87c580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a2206410a3a00202006200336021c2006200236021820064107360214200620053602102006410736020c2006200741016a3602082006200a3602042006200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104413810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420c370224200641f7fac480003602202006410736021c200641f0fac48000360218200641f081808000360210200642a4d2928ecac0faa432370308200642c4ccab9c81ebb4b8db0037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903c8f9c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641033a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064213370224200641f0abc580003602202006410836021c200641e8abc58000360218200641ef8080800036021020064293888c8f89fdc6ec9e7f370308200642a5e9e3ab9e929adc2c37030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903e0abc580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641073a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641f283808000360210200642f9beb892cc84b284c800370308200642cc9a879ae783ad825437030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bc30503047f017e027f23808080800041206b2204248080808000200441106a41086a22054100360200200442808080808001370210200441106a410010a48680800020042802142005280200220641386c6a2207420437022c2007420c370224200741f7fac480003602202007410436021c200741daa3c58000360218200741f081808000360210200742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db00370300200441086a200641016a2206360200200420042902102208370300024020062008a7470d002004200610a486808000200428020821060b2004280204200641386c6a2207420437022c2007420c370224200741f7fac480003602202007410236021c200741d8a3c58000360218200741f081808000360210200742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db003703002005200641016a2206360200200420042903002208370310024020062008a7470d00200441106a200610a486808000200428021821060b2004280214200641386c6a2207420437022c2007420a370224200741d192c580003602202007410636021c200741c1fbc48000360218200741ef8080800036021020074293888c8f89fdc6ec9e7f370308200742a5e9e3ab9e929adc2c370300200428021021092004280214210a41002d00fca3c680001a0240410841002802c8a3c68000118180808000002205450d00200541002903f09fc580003702000240200128020822072001280200470d002001200710a086808000200128020821070b2001280204200741246c6a220741023a00202007200336021c2007200236021820074101360214200720053602102007410136020c2007200641016a3602082007200a3602042007200936020020002001290200370200200141086a2207200728020041016a2207360200200041086a2007360200200441206a2480808080000f0b4104410810b280808000000b9b0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006421f370224200641ffaac580003602202006410336021c200641fcaac58000360218200641c481808000360210200642e0f4e1e1b7dafaaf8d7f3703082006428ca3c7fa85a49cf2a17f37030041002d00fca3c680001a20042802042107200428020821080240411041002802c8a3c68000118180808000002209450d00200941086a41002902f4aac58000370200200941002902ecaac580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641063a00202006200336021c2006200236021820064102360214200620093602102006410236020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104411010b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420a370224200541d192c580003602202005410336021c20054185a4c58000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410336021c20054182a4c58000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903d09fc580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541153a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903c09ec580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a2205410a3a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000be40403047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054203370224200541c5f2c480003602202005410636021c200541bff2c48000360218200541ea81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c500370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c20054203370224200541e1f2c480003602202005410736021c200541daf2c48000360218200541b580808000360210200542e88488d0c0e3aebc13370308200542d7c9cb8fc1cf97db3e37030020042802082109200428020c210a41002d00fca3c680001a0240412041002802c8a3c68000118180808000002206450d00200641186a4100290284eac48000370200200641106a41002902fce9c48000370200200641086a41002902f4e9c48000370200200641002902ece9c480003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541063a00202005200336021c2005200236021820054104360214200520063602102005410436020c2005200741016a3602082005200a3602042005200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104412010b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410736021c200541f0fac48000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410c36021c200541dea3c58000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903f099c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541003a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bab0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410636021c200641e4f2c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240411841002802c8a3c68000118180808000002209450d00200941106a41002902d8eac48000370200200941086a41002902d0eac48000370200200941002902c8eac480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c2006200236021820064103360214200620093602102006410336020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104411810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420c370224200641a8a7c580003602202006410836021c200641a0a7c58000360218200641f383808000360210200642a5c7d7fac9c29fd91637030820064289c487f88aeba5a3be7f37030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d0020094100290398a7c580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054207370224200541b0f2c480003602202005410336021c200541c0a8c58000360218200541ea81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c500370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c20054207370224200541b0f2c480003602202005410536021c200541cc92c58000360218200541ea81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c50037030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a4100290388a9c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541033a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420c37022420064194fbc480003602202006410d36021c20064187fbc48000360218200641ed83808000360210200642e7dca5b4fdd9bda7a17f370308200642accee9bcd783d4ea3937030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903c0fac480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903e8a1c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541083a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006421f370224200641d6b2c5800036022020064100360218200641f282808000360210200642f2a19699e4e1bdb1ed0037030820064288d7a582bd91bff5867f37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641033a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410436021c20054193fcc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a410029038099c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541033a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642e200370224200641fda5c5800036022020064100360218200641f4838080003602102006429dfad7afa1e4f28416370308200642ffc593f1fa9c9cbbed0037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903b899c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a2205410d3a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420a370224200641d192c580003602202006410636021c200641c1fbc48000360218200641ef8080800036021020064293888c8f89fdc6ec9e7f370308200642a5e9e3ab9e929adc2c37030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903c89bc580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641103a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903a0a3c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a2205410c3a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bc00201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c000370328200641003602202006410036021820064193828080003602102006429ccab49c93e2e495cf00370308200642d1c5efcdcd82cde1ff0037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c00037032820064100360220200641003602182006418c83808000360210200642b889cc8cd692ff80fa0037030820064288f7e1d594faa5a12037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000be70403047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005422437022420054189e1c480003602202005411236021c200541f7e0c48000360218200541f583808000360210200542ade681e7ba94a3bd8d7f3703082005428ae790e9b7c48cad987f370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c20054210370224200541e7e0c480003602202005410f36021c200541d8e0c48000360218200541f683808000360210200542d5a5dfc0a4ecbbc59f7f37030820054299adc2c4dc87d5fcfe0037030020042802082109200428020c210a41002d00fca3c680001a0240412041002802c8a3c68000118180808000002206450d00200641186a4100290298dec48000370200200641106a4100290290dec48000370200200641086a4100290288dec4800037020020064100290280dec480003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541003a00202005200336021c2005200236021820054104360214200520063602102005410436020c2005200741016a3602082005200a3602042005200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104412010b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642e40037022420064199a5c5800036022020064100360218200641f783808000360210200642dfac899ed482c1d91e370308200642c0df82eab881b3cb5e37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641948380800036021020064299f0ab899787d3ad3a370308200642d4b0f086cab3d2eb1437030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bad0503047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005422437022420054189e1c480003602202005411236021c200541f7e0c48000360218200541f583808000360210200542ade681e7ba94a3bd8d7f3703082005428ae790e9b7c48cad987f370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c20054210370224200541e7e0c480003602202005410f36021c200541d8e0c48000360218200541f683808000360210200542d5a5dfc0a4ecbbc59f7f37030820054299adc2c4dc87d5fcfe0037030020042802082109200428020c210a41002d00fca3c680001a024041c00041002802c8a3c68000118180808000002205450d00200541386a41002902f8ddc48000370200200541306a41002902f0ddc48000370200200541286a41002902e8ddc48000370200200541206a41002902e0ddc48000370200200541186a41002902d8ddc48000370200200541106a41002902d0ddc48000370200200541086a41002902c8ddc48000370200200541002902c0ddc480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c2006200236021820064108360214200620053602102006410836020c2006200741016a3602082006200a3602042006200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b410441c00010b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903889dc580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541113a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006421c3702242006419fb2c5800036022020064100360218200641ec8280800036021020064289a2e6a5b198dead63370308200642d5d1b5deb9b78d97cf0037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbc0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064214370224200641b3e1c480003602202006410636021c200641ade1c480003602182006418482808000360210200642a8dcd6c1f2afcba52b370308200642c7facddcf584b3fea87f37030041002d00fca3c680001a20042802042107200428020821080240412041002802c8a3c68000118180808000002209450d00200941186a41002902d0e0c48000370200200941106a41002902c8e0c48000370200200941086a41002902c0e0c48000370200200941002902b8e0c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641023a00202006200336021c2006200236021820064104360214200620093602102006410436020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104412010b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903909ec580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541093a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410636021c200641e4f2c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d0020094100290390e7c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641073a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a4100290388a2c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541133a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642f200370224200641c0b0c5800036022020064100360218200641f182808000360210200642f8978ab68af4bdd3937f370308200642f681cbfbfc8791b01c37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641023a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642eb00370224200641ecaec5800036022020064100360218200641ee82808000360210200642829cf890def98fed23370308200642daf597b8b3adb2fc4437030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000b870503047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054214370224200541df92c580003602202005410436021c200541db92c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c2005420a370224200541d192c580003602202005410536021c200541cc92c58000360218200541bc82808000360210200542899ac8f29d8ce69ac300370308200542e78dcee4d0becc975737030020042802082109200428020c210a41002d00fca3c680001a0240413041002802c8a3c68000118180808000002206450d00200641286a41002902f089c58000370200200641206a41002902e889c58000370200200641186a41002902e089c58000370200200641106a41002902d889c58000370200200641086a41002902d089c58000370200200641002902c889c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541033a00202005200336021c2005200236021820054106360214200520063602102005410636020c2005200741016a3602082005200a3602042005200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104413010b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420c370224200641f7fac480003602202006410736021c200641f0fac48000360218200641f081808000360210200642a4d2928ecac0faa432370308200642c4ccab9c81ebb4b8db0037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d0020094100290390fac480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641043a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641f081808000360210200642a4d2928ecac0faa432370308200642c4ccab9c81ebb4b8db0037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000b810401067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642113702242006419c93c580003602202006410336021c200641f392c58000360218200641a9828080003602102006429595f4d085b899a16037030820064287bba5b8adf5dafa5b37030041002d00fca3c680001a2004280204210720042802082108024041c00041002802c8a3c68000118180808000002206450d00200641386a41002902e88dc58000370200200641306a41002902e08dc58000370200200641286a41002902d88dc58000370200200641206a41002902d08dc58000370200200641186a41002902c88dc58000370200200641106a41002902c08dc58000370200200641086a41002902b88dc58000370200200641002902b08dc580003702000240200128020822092001280200470d002001200910a086808000200128020821090b2001280204200941246c6a220941063a00202009200336021c2009200236021820094108360214200920063602102009410836020c2009200541016a360208200920083602042009200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b410441c00010b280808000000bc00201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c00037032820064100360220200641003602182006419482808000360210200642c0969aec91e181fcf200370308200642d7a7fbff94a5afcaf80037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bd40403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054214370224200541df92c580003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240411841002802c8a3c6800011818080800000220a450d00200a41106a410029029cfdc48000370200200a41086a4100290294fdc48000370200200a410029028cfdc480003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541053a00202005200336021c20052002360218200541033602142005200a3602102005410336020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104411810b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641f883808000360210200642dfda92949ba784d566370308200642dc85e1fdf99cd0816b37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000beb0603047f017e027f23808080800041206b2204248080808000200441106a41086a22054100360200200442808080808001370210200441106a410010a48680800020042802142005280200220641386c6a2207420437022c2007420c370224200741f7fac480003602202007410436021c200741daa3c58000360218200741f081808000360210200742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db00370300200441086a200641016a2206360200200420042902102208370300024020062008a7470d002004200610a486808000200428020821060b2004280204200641386c6a2207420437022c2007420c370224200741f7fac480003602202007410236021c200741d8a3c58000360218200741f081808000360210200742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db003703002005200641016a2206360200200420042903002208370310024020062008a7470d00200441106a200610a486808000200428021821060b2004280214200641386c6a2207420437022c2007420a370224200741d192c580003602202007410636021c200741c1fbc48000360218200741ef8080800036021020074293888c8f89fdc6ec9e7f370308200742a5e9e3ab9e929adc2c370300200441086a200641016a2206360200200420042903102208370300024020062008a7470d002004200610a486808000200428020821060b2004280204200641386c6a2207420437022c20074206370224200741fca3c580003602202007411236021c200741eaa3c58000360218200741f983808000360210200742dcb08885f1e3a29706370308200742db8cf7c6bbd3898c8c7f370300200428020021092004280204210a41002d00fca3c680001a0240411041002802c8a3c68000118180808000002205450d00200541086a41002902e49cc58000370200200541002902dc9cc580003702000240200128020822072001280200470d002001200710a086808000200128020821070b2001280204200741246c6a220741063a00202007200336021c2007200236021820074102360214200720053602102007410236020c2007200641016a3602082007200a3602042007200936020020002001290200370200200141086a2207200728020041016a2207360200200041086a2007360200200441206a2480808080000f0b4104411010b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006421b370224200641bbb2c5800036022020064100360218200641ef82808000360210200642bba2f9a9eaf2f897ff0037030820064288bfcff6aea5a9cbf90037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbc0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641e3e4c480003602202006410936021c200641dae4c48000360218200641f581808000360210200642ab8bffbed784ffa5937f370308200642c194a6a793ccc3a85737030041002d00fca3c680001a20042802042107200428020821080240412041002802c8a3c68000118180808000002209450d00200941186a41002902c8eec48000370200200941106a41002902c0eec48000370200200941086a41002902b8eec48000370200200941002902b0eec480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641093a00202006200336021c2006200236021820064104360214200620093602102006410436020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104412010b280808000000b940201047f23808080800041106b220224808080800002400240024002402001108b8280800022030d00410121040c010b2003417f4c0d0141002d00fca3c680001a200341002802c8a3c68000118180808000002204450d020b200241086a220541003602002002200436020420022003360200200220014188026a36020c2002410c6a200210c18a808000200220014190026a36020c2002410c6a200210c18a808000200220014198026a36020c2002410c6a200210c18a8080002002200141a0026a36020c2002410c6a200210c18a8080002001200210f28c808000200041086a200528020036020020002002290200370200200241106a2480808080000f0b10ae80808000000b4101200310b280808000000bae0101017f23808080800041306b2202248080808000200028020021002002410c6a4202370200200241186a410c6a41d08180800036020020022000280200220036022820024103360204200241f0bfc08000360200200241d18180800036021c200220006836022c200141186a28020021002002200241186a36020820022002412c6a3602202002200241286a36021820012802142000200210d9808080002101200241306a24808080800020010bb30f03117f017e067f23808080800041206b2203248080808000024002400240024020014115490d0041002d00fca3c680001a0240200141037441f0ffffff077141002802c8a3c68000118180808000002204450d0041002d00fca3c680001a41800141002802c8a3c68000118180808000002205450d04200041706a2106200041246a210741002108410021094110210a0240034020002008220b410474220c6a210d0240024002402001200b6b220e4102490d000240200d41106a280200220f200d280200200d41146a2802002210200d41046a280200221120102011491b10888e8080002212201020116b20121b4100480d0041022112200e4102460d022007200c6a21114102211203402011417c6a2802002213200f2011280200220c2010200c2010491b10888e808000220f200c20106b200f1b4100480d03201141106a2111200c21102013210f200e201241016a2212460d020c000b0b410221120240200e4102460d002007200c6a21114102211203402011417c6a2802002213200f2011280200220c2010200c2010491b10888e808000220f200c20106b200f1b417f4a0d01201141106a2111200c21102013210f200e201241016a2212470d000b200e21120b024002402012200b6a22082012490d00200820014b0d0120124102490d042012410176210c200620084104746a2110200d21110340201129020021142011201029020037020020102014370200201141086a220f2902002114200f201041086a220e290200370200200e2014370200201041706a2110201141106a2111200c417f6a220c0d000c050b0b200b200841c0b4c58000109681808000000b2008200141c0b4c58000109581808000000b200e21120b2012200b6a21080b024002402008200b490d00200820014d0d010b41b0b5c58000412c41dcb5c5800010f880808000000b024002400240200820014f0d002012410a490d010b2008200b6b21100c010b200b410a6a2210200120102001491b2208200b490d02200d2008200b6b221020124101201241014b1b10d78b8080000b024002402009200a470d0041002d00fca3c680001a200941047441002802c8a3c68000118180808000002211450d012009410174210a20112005200941037410848e8080002111200541002802c0a3c6800011808080800000201121050b200520094103746a2211200b36020420112010360200200941016a22152109024020154102490d0003400240024002400240200520152213417f6a22154103746a2209280200221020092802046a2001460d00201341037420056a220c41706a280200221220104d0d0041022109201341024d0d0520052013417d6a22164103746a2802002211201220106a4d0d0141032109201341034d0d05200c41606a280200201120126a4d0d01201321090c050b20134103490d0120052013417d6a22164103746a28020021110b20112010490d010b2013417e6a21160b024002400240024002400240201320164d0d002013201641016a22104d0d01200520104103746a2217280204201728020022186a2211200520164103746a2219280204221a490d02201120014b0d032000201a4104746a22092019280200220d41047422126a21102011410474210c02402011201a6b220f200d6b2211200d4f0d00200420102011410474221210848e808000220e20126a2112200d4101480d0520114101480d052006200c6a2111034020112012201241706a220c280200201041706a220f280200200c41046a280200220c200f41046a280200220f200c200f491b10888e808000220b200c200f6b200b1b220c411f75220f417f734104746a22122010200f4104746a2210200c417f4a1b220c290200370200201141086a200c41086a290200370200201020094d0d06201141706a21112012200e4d0d060c000b0b20042009201210848e808000221120126a21120240200d41014e0d00201121110c060b0240200f200d4a0d00201121110c060b2000200c6a210e20112111034020092011201020102802002011280200201041046a280200220c201141046a280200220f200c200f491b10888e808000220b200c200f6b200b1b220b417f4a220c1b220f290200370200200941086a200f41086a290200370200200941106a21092011200c4104746a221120124f0d062010200b411b764110716a2210200e490d000c060b0b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41d0b4c5800010f680808000000b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41e0b4c5800010f680808000000b201a201141f0b4c58000109681808000000b2011200141f0b4c58000109581808000000b20102109200e21110b20092011201220116b10848e8080001a2017201a36020420172018200d6a3602002019201941086a20132016417f736a41037410fe8d8080001a41012109201541014b0d000b0b200820014f0d050c010b0b41a0b5c5800010a081808000000b200b200841ecb5c58000109681808000000b4180b5c5800010a081808000000b200141014d0d0120002001410110d78b8080000c010b200541002802c0a3c6800011808080800000200441002802c0a3c68000118080808000000b200341206a2480808080000f0b4190b5c5800010a081808000000bd60203077f017e017f02402002417f6a20014f0d000240200220014f0d00200241047420006a41606a210303400240200020024104746a22042802002205200441706a2206280200200441046a2802002207200641046a280200220820072008491b10888e8080002209200720086b20091b417f4a0d0020042006290200370200200441086a2204290200210a2004200641086a290200370200024020024101460d004101210b200321040340200441106a2106200520042802002007200441046a280200220820072008491b10888e8080002209200720086b20091b417f4a0d0120062004290200370200200641086a200441086a290200370200200441706a21042002200b41016a220b470d000b200021060b2006200a37020820062007360204200620053602000b200341106a2103200241016a22022001470d000b0b0f0b41fcb5c58000412e41acb6c5800010f880808000000bbe0f03117f017e067f23808080800041206b2203248080808000024002400240024020014115490d0041002d00fca3c680001a02402001410176410c6c41002802c8a3c68000118180808000002204450d0041002d00fca3c680001a41800141002802c8a3c68000118180808000002205450d04200041746a2106200041206a210741002108410021094110210a0240034020002008220b410c6c220c6a210d0240024002402001200b6b220e4102490d000240200d41106a280200220f200d41046a280200200d41146a2802002210200d41086a280200221120102011491b10888e8080002212201020116b20121b4100480d0041022112200e4102460d022007200c6a21114102211203402011417c6a2802002213200f2011280200220c2010200c2010491b10888e808000220f200c20106b200f1b4100480d032011410c6a2111200c21102013210f200e201241016a2212460d020c000b0b410221120240200e4102460d002007200c6a21114102211203402011417c6a2802002213200f2011280200220c2010200c2010491b10888e808000220f200c20106b200f1b417f4a0d012011410c6a2111200c21102013210f200e201241016a2212470d000b200e21120b024002402012200b6a22082012490d00200820014b0d0120124102490d042012410176210c20062008410c6c6a2110200d211103402011280200210f201120102802003602002010200f360200201141046a220f2902002114200f201041046a220e290200370200200e2014370200201041746a21102011410c6a2111200c417f6a220c0d000c050b0b200b200841c0b4c58000109681808000000b2008200141c0b4c58000109581808000000b200e21120b2012200b6a21080b024002402008200b490d00200820014d0d010b41b0b5c58000412c41dcb5c5800010f880808000000b024002400240200820014f0d002012410a490d010b2008200b6b21100c010b200b410a6a2210200120102001491b2208200b490d02200d2008200b6b221020124101201241014b1b10d98b8080000b024002402009200a470d0041002d00fca3c680001a200941047441002802c8a3c68000118180808000002211450d012009410174210a20112005200941037410848e8080002111200541002802c0a3c6800011808080800000201121050b200520094103746a2211200b36020420112010360200200941016a22152109024020154102490d0003400240024002400240200520152213417f6a22154103746a2209280200221020092802046a2001460d00201341037420056a220c41706a280200221220104d0d0041022109201341024d0d0520052013417d6a22164103746a2802002211201220106a4d0d0141032109201341034d0d05200c41606a280200201120126a4d0d01201321090c050b20134103490d0120052013417d6a22164103746a28020021110b20112010490d010b2013417e6a21160b024002400240024002400240201320164d0d002013201641016a22104d0d01200520104103746a2217280204201728020022186a2211200520164103746a2219280204221a490d02201120014b0d032000201a410c6c6a22092019280200220d410c6c22126a21102011410c6c210c02402011201a6b220f200d6b2211200d4f0d00200420102011410c6c221210848e808000220e20126a2112200d4101480d0520114101480d052006200c6a2111034020112012201241746a220c41046a280200201041746a220f41046a280200200c41086a280200220c200f41086a280200220f200c200f491b10888e808000220b200c200f6b200b1b220c411f75220f417f73410c6c6a22122010200f410c6c6a2210200c417f4a1b220c290200370200201141086a200c41086a280200360200201020094d0d06201141746a21112012200e4d0d060c000b0b20042009201210848e808000221120126a21120240200d41014e0d00201121110c060b0240200f200d4a0d00201121110c060b2000200c6a210e201121110340200920112010201041046a280200201141046a280200201041086a280200220c201141086a280200220f200c200f491b10888e808000220b200c200f6b200b1b220b417f4a220c1b220f290200370200200941086a200f41086a2802003602002009410c6a21092011200c410c6c6a221120124f0d062010200b411f76410c6c6a2210200e490d000c060b0b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41d0b4c5800010f680808000000b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41e0b4c5800010f680808000000b201a201141f0b4c58000109681808000000b2011200141f0b4c58000109581808000000b20102109200e21110b20092011201220116b10848e8080001a2017201a36020420172018200d6a3602002019201941086a20132016417f736a41037410fe8d8080001a41012109201541014b0d000b0b200820014f0d050c010b0b41a0b5c5800010a081808000000b200b200841ecb5c58000109681808000000b4180b5c5800010a081808000000b200141014d0d0120002001410110d98b8080000c010b200541002802c0a3c6800011808080800000200441002802c0a3c68000118080808000000b200341206a2480808080000f0b4190b5c5800010a081808000000bd602010a7f02402002417f6a20014f0d000240200220014f0d002002410c6c20006a41686a21030340024020002002410c6c6a220441046a2802002205200441746a220641046a280200200441086a22072802002208200641086a2209280200220a2008200a491b10888e808000220b2008200a6b200b1b417f4a0d002004280200210c2004200629020037020020072009280200360200024020024101460d00410121072003210403402004410c6a21062005200441046a2802002008200441086a2209280200220a2008200a491b10888e808000220b2008200a6b200b1b417f4a0d0120062004290200370200200641086a2009280200360200200441746a21042002200741016a2207470d000b200021060b20062008360208200620053602042006200c3602000b2003410c6a2103200241016a22022001470d000b0b0f0b41fcb5c58000412e41acb6c5800010f880808000000bb70e03137f017e047f23808080800041206b2203248080808000024002400240024020014115490d0041002d00fca3c680001a0240200141017641146c41002802c8a3c68000118180808000002204450d0041002d00fca3c680001a41800141002802c8a3c68000118180808000002205450d042000416c6a2106200041146a210741002108410021094110210a0240034020002008220b41146c220c6a210d0240024002402001200b6b220e4102490d000240200d41146a200d410810888e8080004100480d004102210f200e4102460d022007200c6a21104102210f0340201041146a22112010410810888e8080004100480d0320112110200e200f41016a220f460d020c000b0b4102210f0240200e4102460d002007200c6a21104102210f0340201041146a22112010410810888e808000417f4a0d0120112110200e200f41016a220f470d000b200e210f0b02400240200f200b6a2208200f490d00200820014b0d01200f4102490d04200f4101762112200c200f41146c6a2113200621112000211403402014200c6a221041086a221529020021162015201120136a220e41086a221729020037020020172016370200201029020021162010200e290200370200200e2016370200200e41106a220e2802002115200e201041106a2210280200360200201020153602002011416c6a2111201441146a21142012417f6a22120d000c050b0b200b200841c0b4c58000109681808000000b2008200141c0b4c58000109581808000000b200e210f0b200f200b6a21080b024002402008200b490d00200820014d0d010b41b0b5c58000412c41dcb5c5800010f880808000000b024002400240200820014f0d00200f410a490d010b2008200b6b21100c010b200b410a6a2210200120102001491b2208200b490d02200d2008200b6b2210200f4101200f41014b1b10db8b8080000b024002402009200a470d0041002d00fca3c680001a200941047441002802c8a3c6800011818080800000220e450d012009410174210a200e2005200941037410848e808000210e200541002802c0a3c6800011808080800000200e21050b200520094103746a220e200b360204200e2010360200200941016a22182109024020184102490d0003400240024002400240200520182217417f6a22184103746a2209280200221020092802046a2001460d00201741037420056a221141706a280200220f20104d0d0041022109201741024d0d0520052017417d6a220c4103746a280200220e200f20106a4d0d0141032109201741034d0d05201141606a280200200e200f6a4d0d01201721090c050b20174103490d0120052017417d6a220c4103746a280200210e0b200e2010490d010b2017417e6a210c0b0240024002400240024002402017200c4d0d002017200c41016a22104d0d01200520104103746a2213280204201328020022196a220e2005200c4103746a221a280204220d490d02200e20014b0d032000200d41146c6a2209201a280200220b41146c220f6a2110200e41146c21140240200e200d6b2212200b6b2211200b4f0d0020042010201141146c220e10848e8080002212200e6a210e200b4101480d0520114101480d05200620146a210f0340200f200e200e416c6a2010416c6a410810888e8080002211411f752214417f7341146c6a220e2010201441146c6a22102011417f4a1b2211290200370200200f41106a201141106a280200360200200f41086a201141086a290200370200201020094d0d06200f416c6a210f200e20124d0d060c000b0b20042009200f10848e8080002211200f6a210e0240200b41014e0d002011210f0c060b02402012200b4a0d002011210f0c060b200020146a21152011210f03402009200f20102010200f410810888e8080002212417f4a22141b2211290200370200200941106a201141106a280200360200200941086a201141086a290200370200200941146a2109200f201441146c6a220f200e4f0d0620102012411f7641146c6a22102015490d000c060b0b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41d0b4c5800010f680808000000b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41e0b4c5800010f680808000000b200d200e41f0b4c58000109681808000000b200e200141f0b4c58000109581808000000b201021092012210f0b2009200f200e200f6b10848e8080001a2013200d36020420132019200b6a360200201a201a41086a2017200c417f736a41037410fe8d8080001a41012109201841014b0d000b0b200820014f0d050c010b0b41a0b5c5800010a081808000000b200b200841ecb5c58000109681808000000b4180b5c5800010a081808000000b200141014d0d0120002001410110db8b8080000c010b200541002802c0a3c6800011808080800000200441002802c0a3c68000118080808000000b200341206a2480808080000f0b4190b5c5800010a081808000000b8a0303047f017e037f23808080800041206b220324808080800002402002417f6a20014f0d000240200220014f0d00200241146c20006a41586a2104034002402000200241146c6a22052005416c6a2206410810888e808000417f4a0d002005290200210720052006290200370200200341086a41106a2208200541106a2209280200360200200341086a41086a220a200541086a22052902003703002005200641086a2902003702002009200641106a28020036020020032007370308024020024101460d0041012109200421050340200541146a2106200341086a2005410810888e808000417f4a0d0120062005290200370200200641106a200541106a280200360200200641086a200541086a2902003702002005416c6a21052002200941016a2209470d000b200021060b20062003290308370200200641106a2008280200360200200641086a200a2903003702000b200441146a2104200241016a22022001470d000b0b200341206a2480808080000f0b41fcb5c58000412e41acb6c5800010f880808000000baf1501197f23808080800041206b2203248080808000024002400240024020014115490d0041002d00fca3c680001a0240200141047441e0ffffff077141002802c8a3c68000118180808000002204450d0041002d00fca3c680001a41800141002802c8a3c68000118180808000002205450d04200041606a2106200041206a210741002108410021094110210a0240034020002008220b410574220c6a210d0240024002402001200b6b220e4102490d000240200d41206a200d412010888e8080004100480d004102210f200e4102460d022007200c6a21104102210f0340201041206a22112010412010888e8080004100480d0320112110200e200f41016a220f460d020c000b0b4102210f0240200e4102460d002007200c6a21104102210f0340201041206a22112010412010888e808000417f4a0d0120112110200e200f41016a220f470d000b200e210f0b02400240200f200b6a2208200f490d00200820014b0d01200f4102490d04200f4101762112200c200f4105746a2113200621112000211403402014200c6a221028000021152010201120136a220e280000360000200e2015360000200e41056a2d00002115200e41046a22162d000021172016201041046a22182f00003b0000201041076a2d00002116201041066a22192d0000211a2019200e41066a221b2f00003b0000201820173a0000201041056a20153a0000201b201a3a0000200e41076a20163a0000201041086a22152d000021162015200e41086a22172d00003a0000201720163a0000201041096a22152d000021162015200e41096a22172d00003a0000201720163a00002010410a6a22152d000021162015200e410a6a22172d00003a0000201720163a00002010410b6a22152d000021162015200e410b6a22172d00003a0000201720163a00002010410c6a22152d000021162015200e410c6a22172d00003a0000201720163a00002010410d6a22152d000021162015200e410d6a22172d00003a0000201720163a00002010410e6a22152d000021162015200e410e6a22172d00003a0000201720163a00002010410f6a22152d000021162015200e410f6a22172d00003a0000201720163a0000201041106a22152d000021162015200e41106a22172d00003a0000201720163a0000201041116a22152d000021162015200e41116a22172d00003a0000201720163a0000201041126a22152d000021162015200e41126a22172d00003a0000201720163a0000201041136a22152d000021162015200e41136a22172d00003a0000201720163a0000201041146a22152d000021162015200e41146a22172d00003a0000201720163a0000201041156a22152d000021162015200e41156a22172d00003a0000201720163a0000201041166a22152d000021162015200e41166a22172d00003a0000201720163a0000201041176a22152d000021162015200e41176a22172d00003a0000201720163a0000201041186a22152d000021162015200e41186a22172d00003a0000201720163a0000201041196a22152d000021162015200e41196a22172d00003a0000201720163a00002010411a6a22152d000021162015200e411a6a22172d00003a0000201720163a00002010411b6a22152d000021162015200e411b6a22172d00003a0000201720163a00002010411c6a22152d000021162015200e411c6a22172d00003a0000201720163a00002010411d6a22152d000021162015200e411d6a22172d00003a0000201720163a00002010411e6a22152d000021162015200e411e6a22172d00003a0000201720163a00002010411f6a22102d000021152010200e411f6a220e2d00003a0000200e20153a0000201141606a2111201441206a21142012417f6a22120d000c050b0b200b200841c0b4c58000109681808000000b2008200141c0b4c58000109581808000000b200e210f0b200f200b6a21080b024002402008200b490d00200820014d0d010b41b0b5c58000412c41dcb5c5800010f880808000000b024002400240200820014f0d00200f410a490d010b2008200b6b21100c010b200b410a6a2210200120102001491b2208200b490d02200d2008200b6b2210200f4101200f41014b1b10dd8b8080000b024002402009200a470d0041002d00fca3c680001a200941047441002802c8a3c6800011818080800000220e450d012009410174210a200e2005200941037410848e808000210e200541002802c0a3c6800011808080800000200e21050b200520094103746a220e200b360204200e2010360200200941016a220b21090240200b4102490d00034002400240024002402005200b2216417f6a220b4103746a220e2802002210200e2802046a2001460d00201641037420056a221141706a280200220f20104d0d0041022109201641024d0d0520052016417d6a22184103746a280200220e200f20106a4d0d0141032109201641034d0d05201141606a280200200e200f6a4d0d01201621090c050b20164103490d0120052016417d6a22184103746a280200210e0b200e2010490d010b2016417e6a21180b024002400240024002400240201620184d0d002016201841016a22104d0d01200520104103746a22192802042019280200220c6a2209200520184103746a221b280204221a490d02200920014b0d032000201a4105746a220e201b2802002217410574220f6a21102009410574211102402009201a6b221420176b220920174f0d00200420102009410574220f10848e8080002212200f6a210f20174101480d0520094101480d05200620116a210903402009200f200f41606a201041606a412010888e8080002211411f752214417f734105746a220f201020144105746a22102011417f4a1b2211290000370000200941186a201141186a290000370000200941106a201141106a290000370000200941086a201141086a2900003700002010200e4d0d06200941606a2109200f20124d0d060c000b0b2004200e200f10848e8080002209200f6a210f0240201741014e0d00200921090c060b0240201420174a0d00200921090c060b200020116a2115200921090340200e2009201020102009412010888e8080002212417f4a22141b2211290000370000200e41186a201141186a290000370000200e41106a201141106a290000370000200e41086a201141086a290000370000200e41206a210e200920144105746a2209200f4f0d0620102012411a764120716a22102015490d000c060b0b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41d0b4c5800010f680808000000b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41e0b4c5800010f680808000000b201a200941f0b4c58000109681808000000b2009200141f0b4c58000109581808000000b2010210e201221090b200e2009200f20096b10848e8080001a2019201a3602042019200c20176a360200201b201b41086a20162018417f736a41037410fe8d8080001a41012109200b41014b0d000b0b200820014f0d050c010b0b41a0b5c5800010a081808000000b200b200841ecb5c58000109681808000000b4180b5c5800010a081808000000b200141014d0d0120002001410110dd8b8080000c010b200541002802c0a3c6800011808080800000200441002802c0a3c68000118080808000000b200341206a2480808080000f0b4190b5c5800010a081808000000bbf0303047f017e057f23808080800041206b220324808080800002402002417f6a20014f0d000240200220014f0d00200241057420006a41406a210403400240200020024105746a2205200541606a2206412010888e808000417f4a0d002005290000210720052006290000370000200341186a2208200541186a2209290000370300200341106a220a200541106a220b290000370300200341086a220c200541086a22052900003703002005200641086a290000370000200b200641106a2900003700002009200641186a29000037000020032007370300024020024101460d0041012109200421050340200541206a210620032005412010888e808000417f4a0d0120062005290000370000200641186a200541186a290000370000200641106a200541106a290000370000200641086a200541086a290000370000200541606a21052002200941016a2209470d000b200021060b20062003290300370000200641186a2008290300370000200641106a200a290300370000200641086a200c2903003700000b200441206a2104200241016a22022001470d000b0b200341206a2480808080000f0b41fcb5c58000412e41acb6c5800010f880808000000b920402027f017e23808080800041d0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d00200241386a410029028cb8c58000370300200241306a4100290284b8c58000370300200241286a41002902fcb7c58000370300200241206a41002902f4b7c58000370300200241186a41002902ecb7c58000370300200241106a41002902e4b7c58000370300200241002902dcb7c58000370308200241c0006a200241086a108e8d8080002002350248210420022802442103024020022802082201418080808078460d002001450d00200228020c41002802c0a3c68000118080808000000b024020022802142201418080808078460d002001450d00200241186a28020041002802c0a3c68000118080808000000b024020022802202201418080808078460d002001450d00200241246a28020041002802c0a3c68000118080808000000b200241d0006a24808080800020044220862003ad840f0b200241146a42003702002002410136020c200241f0b8c580003602082002200241cc006a360210200241086a41dcb9c5800010f680808000000bd50401027f23808080800041d0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020420022000360200200241c8016a418002200210988d808000024020022802c002418080808078460d00200241086a200241c8016a41880110848e8080001a02404100280284a4c680004105470d00200241a8016a410136020020024194016a410c6a4101360200200241fa838080003602b001200241013602980120024198a1c48000360294012002200241086a3602ac01200241033a00e401200241043602e001200242203702d801200241023602d001200241023602c8012002200241c8016a3602a4012002200241ac016a36029c01200241b4016a410c6a4116360200200241a0a1c480003602c401200241fe8bc480003602bc01200241163602b801200241e88bc480003602b40120024194016a4105200241b4016a4100200210a1828080000b200241c8016a200241086a41880110848e8080001a200241c8016a10fa8c808000200241d0026a24808080800042010f0b200241146a42013702002002410136020c20024198bac58000360208200241fb8380800036029801200220024194016a3602102002200241b4016a36029401200241086a41dcb9c5800010f680808000000bd50801057f23808080800041a0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800020022001360204200220003602002002428080808080203702f001200220023602ec01200241f0006a200241ec016a10dc8c8080000240024020022802d8012200418080808078460d00200241086a200241f0006a41e80010848e8080001a20022802e001210320022802dc0121012002280204450d0102402003450d00200121044100210503402001200541146c6a21060240024002400240024020042d00000e0400010102040b200441086a21060c020b200641086a21060c010b200641046a21060b2006280200450d00200628020441002802c0a3c68000118080808000000b200541016a2105200441146a21042003417f6a22030d000b0b2000450d00200141002802c0a3c68000118080808000000b200241fc006a420137020020024101360274200241d0bac58000360270200241fb8380800036020c2002200241086a3602782002200241ec016a360208200241f0006a41dcb9c5800010f680808000000b20022802e4012104200241f0006a200241086a41e80010848e8080001a200220043602e401200220033602e001200220013602dc01200220003602d8014100280284a4c6800021012002200241f0006a3602e801200241f0006a2103024020014105470d0020024180026a4101360200200241ec016a410c6a4101360200200241fc8380800036028802200241013602f001200241c4a1c480003602ec012002200241e8016a36028402200241033a0024200241043602202002422037021820024102360210200241023602082002200241086a3602fc01200220024184026a3602f4012002418c026a410c6a4116360200200241cca1c4800036029c02200241fe8bc48000360294022002411636029002200241e88bc4800036028c02200241ec016a41052002418c026a4100200310a18280800020022802e80121030b200310fb8c8080002106024020022802e0012204450d0020022802dc01220521034100210103402005200141146c6a21000240024002400240024020032d00000e0400010102040b200341086a21000c020b200041086a21000c010b200041046a21000b2000280200450d00200028020441002802c0a3c68000118080808000000b200141016a2101200341146a21032004417f6a22040d000b0b024020022802d801450d0020022802dc0141002802c0a3c68000118080808000000b41002d00fca3c680001a0240410141002802c8a3c68000118180808000002203450d00200320063a0000200241a0026a2480808080002003ad428080808010840f0b4101410110b280808000000bf60202027f017e23808080800041f0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d00200241f0006a10e489808000200241106a200241f0006a10cf84808000200241868080807836020c200241edcad18b0636026c20022002410c6a10c181808000200241f0006a20022802042201200228020810f183808000200235027821042002280274210302402002280200450d00200141002802c0a3c68000118080808000000b200241f0026a24808080800020044220862003ad840f0b200241fc006a420037020020024101360274200241b4bbc5800036027020022002410c6a360278200241f0006a41dcb9c5800010f680808000000bcb0302027f017e23808080800041f0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020014104490d0020014104470d00200041ccb3c5800020011b2800002101200241f0006a10e4898080002002410c6a200241f0006a200110ce8480800002400240200228020c418780808078470d0020024180808080783602000c010b200241f0006a2002410c6a41e40010848e8080001a2002200241f0006a10c1818080000b200241f0006a200210b48c8080002002350278210420022802742101024020022802002203418080808078460d002003450d00200228020441002802c0a3c68000118080808000000b200241f0026a24808080800020044220862001ad840f0b200241fc006a420137020020024101360274200241ecbbc58000360270200241fb8380800036021020022002410c6a3602782002200236020c200241f0006a41dcb9c5800010f680808000000bcb0202027f017e23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d012003428e808080f001370200200241046a2003410210d88580800020022902082104200341002802c0a3c6800011808080800000200241206a24808080800020040f0b200241106a420037020020024101360208200241d8bcc5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4104410810b280808000000ba90703027f017e027f23808080800041b0046b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020c20022000360208200241106a418002200241086a10dc8780800002402002280210450d0020022d00f8012103200241a0026a200241106a41e80110848e8080001a20024188046a41186a20024191026a29000037030020024198046a20024189026a29000037030020024190046a20024181026a290000370300200220022900f90137038804200241106a2003200241a0026a20024188046a10e589808000200241106a10b2888080002104024020022802202203418080808078460d000240200241106a41186a2802002200450d00200228022421052000410171210641002101024020004101460d002000417e7121002005210341002101034002402003280200450d00200341046a28020041002802c0a3c68000118080808000000b02402003410c6a280200450d00200341106a28020041002802c0a3c68000118080808000000b200341186a21032000200141026a2201470d000b0b02402006450d0020052001410c6c6a2203280200450d00200328020441002802c0a3c68000118080808000000b200228022021030b02402003450d00200228022441002802c0a3c68000118080808000000b0240200241346a2802002203450d00200241306a28020021052003410171210641002101024020034101460d002003417e7121002005210341002101034002402003280200450d00200341046a28020041002802c0a3c68000118080808000000b02402003410c6a280200450d00200341106a28020041002802c0a3c68000118080808000000b200341186a21032000200141026a2201470d000b0b2006450d0020052001410c6c6a2203280200450d00200328020441002802c0a3c68000118080808000000b200228022c450d00200228023041002802c0a3c68000118080808000000b200241b0046a24808080800020040f0b200241ac026a4201370200200241013602a40220024194bdc580003602a002200241fb8380800036028c04200220024188046a3602a8022002200241af046a36028804200241a0026a41dcb9c5800010f680808000000bbf0302027f017e23808080800041f0036b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020c200220003602082002428080808080203702e8032002200241086a3602e403200241f8016a200241e4036a10f58c8080000240024020022802f8012203450d00200241106a410472200241f8016a410472220141e40110848e808000210020022003360210200228020c450d01200241106a10f68c8080000b20024184026a4201370200200241013602fc01200241c8bdc580003602f801200241fb838080003602142002200241106a360280022002200241e4036a360210200241f8016a41dcb9c5800010f680808000000b2001200041e40110848e8080001a200220033602f801200241106a200241f8016a10fc8c808000200241106a10b0888080002104200241f0036a24808080800020040bfe0503027f017e047f2380808080004190016b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d0002404100280284a4c680004105470d002002410c6a42003702002002410136020420024184a2c48000360200200241e8d1c38000360208200241fc006a410c6a41163602002002418ca2c4800036028c01200241fe8bc48000360284012002411636028001200241e88bc4800036027c20024105200241fc006a4100200310a1828080000b200210ff8c8080004101210302402002290320220442c000540d0041022103200442808001540d00410421032004428080808004540d004109200479a74103766b21030b0240417f200241f0006a28020041146c20036a41046a220120012003491b2203417f4c0d0041002d00fca3c680001a0240200341002802c8a3c68000118180808000002201450d00200241003602840120022001360280012002200336027c2002200241fc006a10dd8c80800020023502840121042002280280012105024020022802702206450d00200241ec006a280200220721034100210103402007200141146c6a21080240024002400240024020032d00000e0400010102040b200341086a21080c020b200841086a21080c010b200841046a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200141016a2101200341146a21032006417f6a22060d000b0b02402002280268450d00200228026c41002802c0a3c68000118080808000000b20024190016a24808080800020044220862005ad840f0b4101200310b280808000000b10ae80808000000b2002410c6a420037020020024101360204200241b4bec580003602002002200241fc006a360208200241dcb9c5800010f680808000000bfb0402027f017e23808080800041e0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800020022001360214200220003602102002428080808080203702442002200241106a360240200241086a200241c0006a10bc8a8080000240024020022802080d00200228020c21012002200228024441016a2203360244200320022802484b0d00200241003a004f200220013602202002410036021c2002200241c0006a3602182002200241cf006a360224200241d0006a200241186a10d28c808000024020022d004f450d00200241d0006a10d38c8080000c010b200241306a41086a2203200241d0006a41086a280200360200200220022903503703302002280214450d01200241306a10d38c8080000b200241246a42013702002002410136021c200241ecbec58000360218200241fb838080003602542002200241d0006a3602202002200241c0006a360250200241186a41dcb9c5800010f680808000000b200241186a41086a20032802003602002002200229033037031820024100360258200242808080808001370250200241186a10d38c808000200241186a41084100108c8d80800020023502202104200228021c2103200241d0006a108f8780800002402002280250450d00200228025441002802c0a3c68000118080808000000b200241e0006a24808080800020044220862003ad840b9f0502047f017e23808080800041d0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136021c20022000360218200241206a418002200241186a10998d8080000240200228029801418080808078460d00200241b8016a200241206a41880110848e8080001a200241c0026a41086a200241b0016a280200360200200220022903a8013703c002200241013b01142002410036021020024100360208200241c0026a10d38c808000200241a4026a28020021040240200241a8026a2802002205450d00200421034100210103402004200141146c6a21000240024002400240024020032d00000e0400010102040b200341086a21000c020b200041086a21000c010b200041046a21000b2000280200450d00200028020441002802c0a3c68000118080808000000b200141016a2101200341146a21032005417f6a22050d000b0b024020022802a002450d00200441002802c0a3c68000118080808000000b200241b0026a108f87808000024020022802b002450d00200241b4026a28020041002802c0a3c68000118080808000000b200241206a200241086a10ba8980800020022902242106200241086a10d38c808000200241d0026a24808080800020060f0b200241c4016a4201370200200241013602bc01200241a0bfc580003602b801200241fb8380800036020c2002200241086a3602c0012002200241c0026a360208200241b8016a41dcb9c5800010f680808000000bae0302027f017e23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020014120490d0020014120470d00200241086a41186a200041ccb3c5800020011b220141186a290000370300200241086a41106a200141106a290000370300200241086a41086a200141086a29000037030020022001290000370308200241286a200241086a10f8878080002002290328210441002d00fca3c680001a410841002802c8a3c68000118180808000002201450d0120012004370000200241f0006a2480808080002001ad42808080808001840f0b200241346a42013702002002410136022c200241d4bfc58000360228200241fb8380800036020c2002200241086a3602302002200241ef006a360208200241286a41dcb9c5800010f680808000000b4101410810b280808000000bb40302027f017e23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020014120490d0020014120470d00200241086a41186a200041ccb3c5800020011b220141186a290000370300200241086a41106a200141106a290000370300200241086a41086a200141086a29000037030020022001290000370308200241286a200241086a10fa87808000200241286a41186a290300210441002d00fca3c680001a410841002802c8a3c68000118180808000002201450d0120012004370000200241f0006a2480808080002001ad42808080808001840f0b200241346a42013702002002410136022c20024184c0c58000360228200241fb8380800036020c2002200241086a3602302002200241ef006a360208200241286a41dcb9c5800010f680808000000b4101410810b280808000000bd90202027f017e23808080800041306b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020014108490d0020014108470d00200041ccb3c5800020011b290000210441002d00fca3c680001a410841002802c8a3c68000118180808000002201450d012001200442017c370000200241306a2480808080002001ad42808080808001840f0b200241186a420137020020024101360210200241bcc0c5800036020c200241fb838080003602282002200241246a36021420022002412f6a3602242002410c6a41dcb9c5800010f680808000000b4101410810b280808000000bcd0602077f017e23808080800041c0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000200220013602142002200036021020024280808080802037021c2002200241106a360218200241086a200241186a10bc8a8080000240024020022802080d00200241306a200241186a200228020c10d18580800020022802302204418080808078460d00200228023421052002280214450d012004450d00200541002802c0a3c68000118080808000000b200241246a42013702002002410136021c200241fcc0c58000360218200241fb838080003602342002200241306a36022020022002413f6a360230200241186a41dcb9c5800010f680808000000b0240024002400240200228023822060d00410821070c010b200641ffffffff004b0d0120064103742201417f4c0d0141002d00fca3c680001a200141002802c8a3c68000118180808000002207450d0220072005200110848e808000220021030240200141786a220841037641016a4107712201450d00200141037421012000210303402003200329030042017c370300200341086a2103200141786a22010d000b0b20084138490d00200020064103746a210003402003200329030042017c370300200341086a2201200129030042017c370300200341106a2201200129030042017c370300200341186a2201200129030042017c370300200341206a2201200129030042017c370300200341286a2201200129030042017c370300200341306a2201200129030042017c370300200341386a2201200129030042017c370300200341c0006a22032000470d000b0b02402004450d00200541002802c0a3c68000118080808000000b200241186a2007200610d78580800020023502202109200228021c210302402006450d00200741002802c0a3c68000118080808000000b200241c0006a24808080800020094220862003ad840f0b10ae80808000000b4108200110b280808000000ba50201027f23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0120034201370000200241206a2480808080002003ad42808080808001840f0b200241106a420037020020024101360208200241f4c1c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4101410810b280808000000baf0202027f017e23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0010e689808000210441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0120032004370000200241206a2480808080002003ad42808080808001840f0b200241106a420037020020024101360208200241d8c2c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4101410810b280808000000bcc0202027f027e23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0042002104420021050340200510de8980800020047c2104200542017c220542e807520d000b41002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0120032004370000200241206a2480808080002003ad42808080808001840f0b200241106a420037020020024101360208200241ccc3c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4101410810b280808000000bcc0202027f027e23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0042002104420021050340200510de8980800020047c2104200542017c220542e807520d000b41002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0120032004370000200241206a2480808080002003ad42808080808001840f0b200241106a420037020020024101360208200241bcc4c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4101410810b280808000000bb30301027f23808080800041306b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800002400240024020014104490d0020014104470d0002400240200041ccb3c5800020011b28000022010d002002410c6a4101410010d68580800020022802142101200228021021030c010b2001417f4c0d0241002d00fca3c680001a200141002802c8a3c68000118180808000002200450d032002410c6a2000410010d6858080002002280214210120022802102103200041002802c0a3c68000118080808000000b200241306a2480808080002001ad4220862003ad840f0b200241186a420137020020024101360210200241f4c4c5800036020c200241fb838080003602282002200241246a36021420022002412f6a3602242002410c6a41dcb9c5800010f680808000000b10ae80808000000b4101200110b280808000000bd80202027f027e23808080800041c0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d002002411c6a10ab8a808000200241086a2002411c6a10e688808000200229031021042002290308210541002d00fca3c680001a410841002802c8a3c68000118180808000002203450d012003200442002005a71b370000200241c0006a2480808080002003ad42808080808001840f0b200241286a420037020020024101360220200241e0c5c5800036021c20022002413c6a3602242002411c6a41dcb9c5800010f680808000000b4101410810b280808000000b860401027f23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d002002410c6a10e78980800041002d00fca3c680001a41e00041002802c8a3c68000118180808000002203450d012003200229000c3700002003200229004c370040200341386a2002410c6a41386a290000370000200341306a2002410c6a41306a290000370000200341286a2002410c6a41286a290000370000200341206a2002410c6a41206a290000370000200341186a2002410c6a41186a290000370000200341106a2002410c6a41106a290000370000200341086a2002410c6a41086a290000370000200341c8006a2002410c6a41c8006a290000370000200341d0006a2002410c6a41d0006a290000370000200341d8006a2002410c6a41d8006a290000370000200241f0006a2480808080002003ad4280808080800c840f0b200241186a420037020020024101360210200241d0c6c5800036020c2002200241ec006a3602142002410c6a41dcb9c5800010f680808000000b410141e00010b280808000000b860401027f23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d002002410c6a10e88980800041002d00fca3c680001a41e00041002802c8a3c68000118180808000002203450d012003200229000c3700002003200229004c370040200341386a2002410c6a41386a290000370000200341306a2002410c6a41306a290000370000200341286a2002410c6a41286a290000370000200341206a2002410c6a41206a290000370000200341186a2002410c6a41186a290000370000200341106a2002410c6a41106a290000370000200341086a2002410c6a41086a290000370000200341c8006a2002410c6a41c8006a290000370000200341d0006a2002410c6a41d0006a290000370000200341d8006a2002410c6a41d8006a290000370000200241f0006a2480808080002003ad4280808080800c840f0b200241186a420037020020024101360210200241c0c7c5800036020c2002200241ec006a3602142002410c6a41dcb9c5800010f680808000000b410141e00010b280808000000b9c0301027f23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d00200241086a10e98980800041002d00fca3c680001a41e20041002802c8a3c68000118180808000002203450d012003200241086a41c10010848e808000220341e1006a200241086a41e1006a2d00003a0000200341d9006a200241086a41d9006a290000370000200341d1006a200241086a41d1006a290000370000200341c9006a200241086a41c9006a29000037000020032002290049370041200241f0006a2480808080002003ad4280808080a00c840f0b200241146a42003702002002410136020c200241acc8c580003602082002200241ec006a360210200241086a41dcb9c5800010f680808000000b410141e20010b280808000000bec0101027f23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d0010ea89808000200241206a24808080800042010f0b200241106a42003702002002410136020820024194c9c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000bab0301027f23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020420022000360200200241086a418002200210b48a808000024020022802080d00200241386a41086a200241146a280200360200200241c8006a41086a200241086a41186a290200370300200241c8006a41106a200241286a290200370300200241c8006a41186a200241306a2902003703002002200229020c3703382002200241086a41106a290200370348200241386a200241c8006a10eb89808000200241f0006a24808080800042010f0b200241d4006a42013702002002410136024c200241c8c9c58000360248200241fb8380800036023c2002200241386a3602502002200241ef006a360238200241c8006a41dcb9c5800010f680808000000b8b0301027f23808080800041d0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800020022001360208200220003602042002410c6a418002200241046a1089878080000240200228020c418080808078460d00200241c0006a41086a2002410c6a41086a280200360200200241286a41086a200241206a2802003602002002200229020c37034020022002290218370328200241c0006a200241286a200228022410ec89808000200241d0006a24808080800042010f0b200241346a42013702002002410136022c20024184cac58000360228200241fb838080003602442002200241c0006a3602302002200241cf006a360240200241286a41dcb9c5800010f680808000000bec0101027f23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d0010ed89808000200241206a24808080800042010f0b200241106a420037020020024101360208200241eccac5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b820a01167f23808080800041d0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200036020c2002200136021020024280808080802037027820022002410c6a36027402400240200141c000490d00200241e0016a41086a200041086a290000370300200241e0016a41106a200041106a290000370300200241e0016a41186a200041186a290000370300200241e0016a41206a200041206a290000370300200241e0016a41286a200041286a290000370300200241e0016a41306a200041306a290000370300200241e0016a41386a200041386a2900003703002002200041c0006a220336020c200220002900003703e0012002200141406a220436021020044120490d00200241a0026a41086a200341086a290000370300200241a0026a41106a200341106a290000370300200241a0026a41186a200341186a2900003703002002200141a07f6a3602102002200041e0006a36020c200220032900003703a0022002200241f4006a10bc8a80800020022802000d00200241c4026a200241f4006a200228020410d08580800020022802c4022201418080808078460d0020024180016a41086a2203200241e0016a41086a220429030037030020024180016a41106a2205200241e0016a41106a220629030037030020024180016a41186a2207200241e0016a41186a220829030037030020024180016a41206a2209200241e0016a41206a220a29030037030020024180016a41286a220b200241e0016a41286a220c29030037030020024180016a41306a220d200241e0016a41306a220e29030037030020024180016a41386a220f200241e0016a41386a2210290300370300200220022903e0013703800120022802cc02211120022802c8022100200241d8016a2212200241a0026a41186a2213290300370300200241d0016a2214200241a0026a41106a2215290300370300200241c8016a2216200241a0026a41086a2217290300370300200220022903a0023703c001200241146a20024180016a41e00010848e8080001a2002280210450d012001450d00200041002802c0a3c68000118080808000000b2002418c016a42013702002002410136028401200241a0cbc5800036028001200241fb838080003602182002200241146a360288012002200241e0016a36021420024180016a41dcb9c5800010f680808000000b20024180016a200241146a41e00010848e8080001a200420032902003703002006200529020037030020082007290200370300200a2009290200370300200c200b290200370300200e200d2902003703002010200f29020037030020172016290200370300201520142902003703002013201229020037030020022002290280013703e001200220022902c0013703a002200241e0016a20002011200241a0026a41002802a8a2c6800011898080800000210302402001450d00200041002802c0a3c68000118080808000000b41002d00fca3c680001a0240410141002802c8a3c68000118180808000002200450d00200020033a0000200241d0026a2480808080002000ad428080808010840f0b4101410110b280808000000be90301047f23808080800041d0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020c20022000360208200241106a418002200241086a1088878080000240024020022802102203418080808078460d0020022d00282101200228021c2100200228021422042002280218200241206a2802002205200241246a28020041002802e0a1c680001186808080000020010d0102402000450d00200541002802c0a3c68000118080808000000b02402003450d00200441002802c0a3c68000118080808000000b200241d0006a24808080800042010f0b200241386a420137020020024101360230200241d4cbc5800036022c200241fb838080003602482002200241c4006a3602342002200241cf006a3602442002412c6a41dcb9c5800010f680808000000b2002411c6a420037020020024101360214200241eca3c48000360210200241e8d1c38000360218200241106a41f4a3c4800010f680808000000ba60201027f23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d01200342e807370000200241206a2480808080002003ad42808080808001840f0b200241106a420037020020024101360208200241bcccc5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4101410810b280808000000bc60202037f017e23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d00200241046a10eb8880800020022802042103200241046a20022802082204200228020c108489808000200235020c2105200228020821010240200341ffffff3f71450d00200441002802c0a3c68000118080808000000b200241206a24808080800020054220862001ad840f0b200241106a420037020020024101360208200241a4cdc5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000bb70202027f017e23808080800041e0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d00200210ee89808000200241d4006a200210ce86808000200235025c21042002280258210302402002280240450d00200241c4006a28020041002802c0a3c68000118080808000000b200241e0006a24808080800020044220862003ad840f0b2002410c6a4200370200200241013602042002418ccec580003602002002200241d4006a360208200241dcb9c5800010f680808000000bbe0302027f027e23808080800041e0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800002400240024020010d002002413c6a10cb87808000200241286a2002413c6a10e688808000200241086a2002290330420020022802281b42004206420010878e8080002002413c6a10ca87808000200241186a2002413c6a10e78880800020022903104200520d01200229030822042002290320420020022802181b7c22052004540d0141002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0220032005370000200241e0006a2480808080002003ad42808080808001840f0b200241c8006a420037020020024101360240200241fccec5800036023c2002200241dc006a3602442002413c6a41dcb9c5800010f680808000000b41c18fc2800041fa0041bc90c2800010a181808000000b4101410810b280808000000bb70202027f017e23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d00200210b287808000200241e4006a200210cd86808000200235026c21042002280268210302402002280250450d00200241d4006a28020041002802c0a3c68000118080808000000b200241f0006a24808080800020044220862003ad840f0b2002410c6a420037020020024101360204200241e4cfc580003602002002200241e4006a360208200241dcb9c5800010f680808000000bb70202027f017e23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d00200210b187808000200241e4006a200210cd86808000200235026c21042002280268210302402002280250450d00200241d4006a28020041002802c0a3c68000118080808000000b200241f0006a24808080800020044220862003ad840f0b2002410c6a420037020020024101360204200241ccd0c580003602002002200241e4006a360208200241dcb9c5800010f680808000000bc40301027f23808080800041e0046b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020c20022000360208200241106a418002200241086a10978d80800002400240200228029802418080808078460d00200241b8026a200241106a41980210848e8080001a200241d8046a200241b0026a280200360200200220022903a8023703d004200241b8026a200241d0046a10ef89808000210141002d00fca3c680001a410141002802c8a3c68000118180808000002203450d01200320013a0000200241e0046a2480808080002003ad428080808010840f0b200241c4026a4201370200200241013602bc02200241a0d1c580003602b802200241fb838080003602d4042002200241d0046a3602c0022002200241df046a3602d004200241b8026a41dcb9c5800010f680808000000b4101410110b280808000000be40202027f017e23808080800041306b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020014108490d0020014128470d002002418080808078360208200241146a200241086a10b48c808000200235021c210420022802182103024020022802082201418080808078460d002001450d00200228020c41002802c0a3c68000118080808000000b200241306a24808080800020044220862003ad840f0b200241206a420137020020024101360218200241e4d1c58000360214200241fb8380800036020c2002200241086a36021c20022002412f6a360208200241146a41dcb9c5800010f680808000000b9a0601057f2380808080004180026b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020c200220003602082002428080808080203702f8012002200241086a3602f401200241f8006a200241f4016a10dc8c8080000240024020022802e0012200418080808078460d00200241106a200241f8006a41e80010848e8080001a20022802e801210320022802e4012101200228020c450d0102402003450d00200121044100210503402001200541146c6a21060240024002400240024020042d00000e0400010102040b200441086a21060c020b200641086a21060c010b200641046a21060b2006280200450d00200628020441002802c0a3c68000118080808000000b200541016a2105200441146a21042003417f6a22030d000b0b2000450d00200141002802c0a3c68000118080808000000b20024184016a42013702002002410136027c20024198d2c58000360278200241fb838080003602142002200241106a360280012002200241f4016a360210200241f8006a41dcb9c5800010f680808000000b20022802ec012104200241f8006a200241106a41e80010848e8080001a200220043602ec01200220033602e801200220013602e401200220003602e001200241f8006a10f089808000024020022802e8012204450d0020022802e401220521034100210103402005200141146c6a21000240024002400240024020032d00000e0400010102040b200341086a21000c020b200041086a21000c010b200041046a21000b2000280200450d00200028020441002802c0a3c68000118080808000000b200141016a2101200341146a21032004417f6a22040d000b0b024020022802e001450d0020022802e40141002802c0a3c68000118080808000000b20024180026a24808080800042010be80402037f017e23808080800041c0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2104024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800020022001360214200220043602102002428080808080203702342002200241106a36023002402001450d0020022001417f6a22013602142002200041016a360210024002400240024020002d00000e020100040b200241086a200241306a10bc8a80800020022802080d03200241186a200241306a200228020c10d08580800020022802182201418080808078460d03200228021c21002002280214450d012001450d03200041002802c0a3c68000118080808000000c030b20010d022002418080808078360218200241306a200241186a10e2898080000c010b2001418180808078460d012002418080808078360218200241306a200241186a10e2898080002001450d00200041002802c0a3c68000118080808000000b200241186a20022802342200200228023810d68580800020023502202105200228021c210102402002280230450d00200041002802c0a3c68000118080808000000b200241c0006a24808080800020054220862001ad840f0b200241246a42013702002002410136021c200241d4d2c58000360218200241fb838080003602342002200241306a36022020022002413f6a360230200241186a41dcb9c5800010f680808000000ba10803027f017e037f23808080800041f0016b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000200220013602202002200036021c20024280808080802037028c0120022002411c6a36028801200241086a20024188016a10bc8a8080000240024020022802080d00200241246a20024188016a200228020c10d08580800020022802242200418080808078460d00200228022821032002280220450d012000450d00200341002802c0a3c68000118080808000000b20024194016a42013702002002410136028c012002418cd3c5800036028801200241fb838080003602282002200241246a360290012002200241106a36022420024188016a41dcb9c5800010f680808000000b024002400240200228022c220141406a4121490d0020014120490d0020014160714120470d010b20024180808080783602100c010b20024188016a41e0006a200341e0006a2d00003a000020024188016a41d8006a200341d8006a29000037030020024188016a41d0006a200341d0006a29000037030020024188016a41c8006a200341c8006a29000037030020024188016a41086a200341086a29000037030020024188016a41106a200341106a29000037030020024188016a41186a200341186a290000370300200220032900403703c801200220032900003703880120024188016a41386a200341386a29000037030020024188016a41306a200341306a29000037030020024188016a41286a200341286a290000370300200220032900203703a801200241246a20024188016a41e10010848e8080001a200241106a200241246a10e3898080000b02402000450d00200341002802c0a3c68000118080808000000b20024188016a200241106a10b58c8080002002350290012104200228028c012105024020022802102203418080808078460d00024020022802182200450d00200228021421062000410171210741002101024020004101460d002000417e7121002006210341002101034002402003280200450d00200341046a28020041002802c0a3c68000118080808000000b0240200341106a280200450d00200341146a28020041002802c0a3c68000118080808000000b200341206a21032000200141026a2201470d000b0b02402007450d00200620014104746a2203280200450d00200328020441002802c0a3c68000118080808000000b200228021021030b2003450d00200228021441002802c0a3c68000118080808000000b200241f0016a24808080800020044220862005ad840bfe0102027f017e23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800002402001450d00200241106a420037020020024101360208200241fcd3c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b200241046a4108410010ce8780800020022902082104200241206a24808080800020040ba50201027f23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0120034200370000200241206a2480808080002003ad42808080808001840f0b200241106a420037020020024101360208200241e8d4c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4101410810b280808000000b9d0301027f23808080800041d0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020c20022000360208200241106a418002200241086a10ae888080000240024020022903104202510d000240200228029802450d002002419c026a28020041002802c0a3c68000118080808000000b41002d00fca3c680001a410141002802c8a3c68000118180808000002203450d01200341003a0000200241d0026a2480808080002003ad428080808010840f0b200241b8026a4201370200200241013602b002200241a0d1c580003602ac02200241fb838080003602c8022002200241c4026a3602b4022002200241cf026a3602c402200241ac026a41dcb9c5800010f680808000000b4101410110b280808000000be40202027f017e23808080800041306b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020014108490d0020014128470d002002418080808078360208200241146a200241086a10b48c808000200235021c210420022802182103024020022802082201418080808078460d002001450d00200228020c41002802c0a3c68000118080808000000b200241306a24808080800020044220862003ad840f0b200241206a420137020020024101360218200241e4d1c58000360214200241fb8380800036020c2002200241086a36021c20022002412f6a360208200241146a41dcb9c5800010f680808000000bf20302027f017e23808080800041c0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000200220013602142002200036021020024280808080802037021c2002200241106a360218200241086a200241186a10bc8a8080000240024020022802080d00200241306a200241186a200228020c10d08580800020022802302203418080808078460d00200228023421012002280214450d012003450d00200141002802c0a3c68000118080808000000b200241246a42013702002002410136021c20024198d5c58000360218200241fb838080003602342002200241306a36022020022002413f6a360230200241186a41dcb9c5800010f680808000000b200220022802383602202002200136021c20022003360218200241306a200241186a10c987808000200241306a10b1888080002104024020022802302203418280808078480d002003450d00200228023441002802c0a3c68000118080808000000b200241c0006a24808080800020040b900502037f017e23808080800041c0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2104024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800020022001360214200220043602102002428080808080203702342002200241106a36023002402001450d0020022001417f6a22013602142002200041016a360210024002400240024020002d00000e020100040b200241086a200241306a10bc8a80800020022802080d03200241186a200241306a200228020c10d08580800020022802182201418080808078460d032001418180808078460d03200228021c21002002280214450d012001450d03200041002802c0a3c68000118080808000000c030b20010d0241818080807821010c010b2001418280808078460d01200228022021030b200220033602202002200036021c20022001360218200241306a200241186a10c687808000024020022802182201418280808078480d002001450d00200228021c41002802c0a3c68000118080808000000b200241186a200241306a10b68c80800020023502202105200228021c2101024020022802302200418080808078460d002000450d00200228023441002802c0a3c68000118080808000000b200241c0006a24808080800020054220862001ad840f0b200241246a42013702002002410136021c200241c8d5c58000360218200241fb838080003602342002200241306a36022020022002413f6a360230200241186a41dcb9c5800010f680808000000bc60403027f017e017f23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800002400240024020010d0041002d00fca3c680001a411841002802c8a3c68000118180808000002203450d012003428680808080808080807f370208200341d0a4c480003602042003418080808078360200200341146a4107360200200341106a41d6a4c4800036020041002d00fca3c680001a411c41002802c8a3c68000118180808000002201450d0220024100360208200220013602042002411c360200200241023602182002200241186a36021c2002411c6a200210c08a80800020034102200210d2858080002002350208210420022802042101024020032802002205418080808078460d002005450d00200328020441002802c0a3c68000118080808000000b0240200328020c2205418080808078460d002005450d00200341106a28020041002802c0a3c68000118080808000000b200341002802c0a3c6800011808080800000200241206a24808080800020044220862001ad840f0b2002410c6a420037020020024101360204200241b0d6c5800036020020022002411c6a360208200241dcb9c5800010f680808000000b4104411810b280808000000b4101411c10b280808000000bcf0101027e42891c210242f8eb8717210302400240024002400240024002400240024020012d0000417f6a0e09080001020304050607080b42b430210242c0abe81721030c070b429885991221030c060b42a893cc1621030c050b42e8e89b0821030c040b2001410c6a350200220342ab147e42de077c21022003429bf1d4067e42e0b9f4077c21030c030b42a8afc00921030c020b4200210242d0bb920321030c010b42c091e20e42d88cf00920012d00011b2103420021020b200041003b011020002002370308200020033703000b970601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342acf6debeefe0d9c8d300370308200341bd8080800036021820034101360204200341abd8c58000360200200341106a42efc9c9edb5e7b3a6c70037030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410898b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b950601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342dc85e1fdf99cd0816b370308200341f88380800036021820034101360204200341abd8c58000360200200341106a42dfda92949ba784d56637030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410d08b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b950601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342d4b0f086cab3d2eb14370308200341948380800036021820034101360204200341abd8c58000360200200341106a4299f0ab899787d3ad3a37030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410c08b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b960601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342e7b0a091f3ed9c85c500370308200341ea8180800036021820034101360204200341abd8c58000360200200341106a42b891b68c98adebcf6137030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410928b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b950601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342cecfe0e5d1c6dbf757370308200341fb8280800036021820034101360204200341abd8c58000360200200341106a42a8c786dcd8d2a98d1737030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c580004104109e8b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b960601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342c4ccab9c81ebb4b8db00370308200341f08180800036021820034101360204200341abd8c58000360200200341106a42a4d2928ecac0faa43237030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410cc8b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b960601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342cc9a879ae783ad8254370308200341f28380800036021820034101360204200341abd8c58000360200200341106a42f9beb892cc84b284c80037030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410aa8b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b960601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0220034288f7e1d594faa5a1203703082003418c8380800036021820034101360204200341abd8c58000360200200341106a42b889cc8cd692ff80fa0037030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410bd8b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b970601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342d1c5efcdcd82cde1ff00370308200341938280800036021820034101360204200341abd8c58000360200200341106a429ccab49c93e2e495cf0037030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410bc8b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000bfe0503037f017e047f2380808080004190016b22012480808080002000280208220241286c2103200028020421000240024020020d00420021040c010b2002410371210502400240200241044f0d0041002106420021040c010b20004198016a21072002417c712108410021064200210403402007290300200741586a290300200741b07f6a290300200741887f6a29030020047c7c7c7c2104200741a0016a21072008200641046a2206470d000b0b2005450d00200641286c20006a41206a21070340200729030020047c2104200741286a21072005417f6a22050d000b0b200020036a210520014298d5afd2c6aeacae2f370358200142c2cdc8b0c7b9e78f857f370350200142e4c5bdb4b2e9a5a6807f370368200142d790d7a3fef9fda0c80037036020012004370310200141d0006a4120200141106a410841002802e0a1c68000118680808000002000210702400340024020030d00200141046a2000200510bd87808000200128020c2002470d0202402002450d00200241286c2103200141e0006a2107200141d0006a41206a220541086a21060340200041206a2903002104200020001084888080001a200141106a200010fa87808000200141d0006a200010fa87808000024002402001280284010d00200141d0006a200010fa878080002001280288010d0020001086888080000c010b200542003703002007428080808080808080807f370300200642003703002001420037035820012004370368200142013703502000200141d0006a1082888080000b200041286a2100200341586a22030d000b0b200141046a10a88d80800020014190016a2480808080000f0b200341586a210320072903202104200741286a21072004428080e983b1de165a0d000b200141dc006a42003702002001410136025420014188dac58000360250200141a8d9c58000360258200141d0006a4190dac5800010f680808000000b200141dc006a420037020020014101360254200141a0d9c58000360250200141a8d9c58000360258200141d0006a41a8d9c5800010f680808000000bd80501047f2380808080004180016b220124808080800020014298d5afd2c6aeacae2f370308200142c2cdc8b0c7b9e78f857f3703002001413c6a2001411041002802c0a1c680001185808080000002400240200128023c2202418080808078460d0020012802402103024020012802444110490d0020012003411010888e808000210402402002450d00200341002802c0a3c68000118080808000000b20040d010c020b2002450d00200341002802c0a3c68000118080808000000b02400240417f4100280284a4c680002202410347200241034b1b2202450d00200241ff017141ff01470d010b200141186a410c6a41fd83808000360200200141b8dbc58000360220200141838280800036021c20014108360214200141e0a6c480003602102001200141106a3602184100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141f4006a4202370200200141ec006a4102360200200141e4006a4116360200200141e0006a41d4d8c58000360200200141d4006a41b8d6c58000ad4280808080d00b843702002001413c6a410c6a41ead8c58000ad4280808080f00284370200200141f0006a200141186a360200200141a8dbc580003602682001410336025c200141003602502001410036024420014281808080a01837023c200341ecf2c08000200441024622041b2001413c6a200241d4f2c0800020041b280210118480808000000b2001413c6a41e0a6c48000410841002802a0a3c68000118580808000002001413c6a41106a220241bccdc48000411541002802a0a3c6800011858080800000200141186a41186a2001413c6a41186a290000370300200141186a41106a2002290000370300200141186a41086a2001413c6a41086a2900003703002001200129003c370318200141013b013c200141186a41202001413c6a410241002802e0a1c68000118680808000000b200042003703082000420037030020014180016a2480808080000b930201027f23808080800041106b2202248080808000200220003602002002200128021441dcf6c58000410e200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241ecf6c58000108d81808000210120022d000c210002400240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010ba70302027f037e23808080800041c0006b22052480808080002005200110fa87808000200541186a220629030021072005200110fa8780800042004200200541286a2903002208200541206a2903007d220920092008561b20041b2109200629030021080240024002400240200341ff01710e03010002030b2008500d022005200110fa8780800020052802344101470d020c010b2008500d012005200110fa878080002005280230450d01200528023441024f0d010b2009428080e983b1de162009428080e983b1de16561b21090b024002400240200720024200200820097d220920092008561b220820022008541b2208540d0020052001200720087d2208109c8c80800020052802000d0102402005290308500d00200541106a2903002209500d0020052009370300200510a88a8080000b20004200200720087d220820082007561b370308410021010c020b200041073b0104410121010c010b200541086a290300210820052802042101200041106a200541106a290300370300200041086a200837030020002001360204410121010b20002001360200200541c0006a2480808080000bd80703017f027e027f23808080800041e0016b22032480808080002003200237030020034190016a200110fa87808000420021040240200341a8016a2903002202500d0020034190016a200110fa878080004200428080e983b1de1620032802c40141014b1b420020032802c0011b21040b20034200200220047d220420042002561b370308200110a08c8080001a20034190016a41086a200341086a36020020032001360290012003200336029401200341d8006a200120034190016a10948a8080000240024002400240200329035822024202510d00200329037021052003290368210402402002500d0020032903602102200341b8016a200141186a290000370300200341b0016a200141106a29000037030020034190016a41186a200141086a2900003703002003200237039801200341003a009001200320012900003703a00120034190016a108e8a8080000b0240024020044200520d00200341286a410f6a220120034188016a280000360000200341286a41086a220620034181016a290000370300200341106a41086a2006290300370300200341106a410f6a20012800003600002003200329007937031020032d007821010c010b200341b8016a200141186a290000370300200341b0016a200141106a29000037030020034190016a41186a200141086a2900003703002003200537039801200341013a009001200320012900003703a00120034190016a108e8a808000200341286a41086a220620034181016a290000370300200341286a410f6a220720034188016a2800003600002003200329007937032820032d0078210120044202510d02200341106a410f6a2007280000360000200341106a41086a2006290300370300200320032903283703100b200341c0006a410f6a2206200341106a410f6a280000360000200341c0006a41086a2207200341106a41086a29030037030020032003290310370340200141ff0171410e470d022000200437030820004100360200200041106a20053703000c030b200341286a41086a200341e9006a290000370300200341376a200341f0006a2800003600002003200341e1006a29000037032820032d006021010b200341106a410f6a2206200341286a410f6a280000360000200341106a41086a2207200341286a41086a290300370300200320032903282202370310200041146a20062800003600002000410d6a2007290300370000200041056a2002370000200020013a0004200041013602000c010b200020013a000420004101360200200041056a2003290340370000200041146a20062800003600002000410d6a20072903003700000b200341e0016a2480808080000bf60704037f027e017f037e23808080800041e0006b220524808080800002400240024002402003500d0020054298d5afd2c6aeacae2f370318200542c2cdc8b0c7b9e78f857f370310200542e4c5bdb4b2e9a5a6807f370328200542d790d7a3fef9fda0c8003703202005200541106a10e688808000410821062005290308420020052802001b2003540d01200541106a200110fa8780800041072106200541286a220729030022082003540d01200541386a22062903002109200541306a220a290300210b200541106a200110fa8780800042002006290300220c200a2903007d220d200d200c561b210c02402007290300220d500d00200541106a200110fa87808000200c200c428080e983b1de16200c428080e983b1de16561b200528024441014b1b200c20052802401b210c0b41870a21064200200d200c7d220c200c200d561b2003540d010240200820037d220c42ffffe883b1de16560d00200541106a200110fa878080002005280240450d00418702210620052802444102490d020b41870a2106427f200c200b7c220d200d200c541b2009540d010240200441ff0171450d004187102106200c428080e983b1de16540d020b200541106a200210fa87808000024002400240200541286a290300220c20037c220d200c540d00200d428080e983b1de16540d01200541306a290300220c200d7c200c5a0d030b41012101410821020c010b41022101410721020b200041066a2005290110370100200041166a200541206a2f01003b01002000410e6a200541186a290100370100200041056a20013a0000200020023a00040c020b0240024020012002412010888e808000450d00200541106a2001200320044100109b8c80800020052802100d01200541106a200210fa878080000240427f200541106a41186a2206290300220c20037c220d200d200c541b220d200c510d00200d428080e983b1de16540d00200541106a2002200d109c8c80800020052802100d002005290318500d00200541206a290300220c500d002005200c370310200541106a10a88a8080000b200541386a200141186a290000370300200541306a200141106a2900003703002006200141086a290000370300200541c8006a200241086a290000370300200541d0006a200241106a290000370300200541d8006a200241186a29000037030020052003370318200541023a00102005200129000037032020052002290000370340200541106a108e8a8080000b20002003370308410021010c030b200541106a41106a2903002103200541106a41086a290300210c20002005280214360204200041106a2003370300200041086a200c3703000c010b200020063602040b410121010b20002001360200200541e0006a2480808080000be20602017f027e2380808080004190016b2206248080808000200641306a200110fa8780800042004200200641d8006a2903002207200641d0006a2903007d220820082007561b20051b2108200641c8006a29030021070240024002400240200341ff01710e03010002030b2007500d02200641306a200110fa8780800020062802644101470d020c010b2007500d01200641306a200110fa878080002006280260450d01200628026441024f0d010b2008428080e983b1de162008428080e983b1de16561b21080b0240024002404200200720087d220820082007561b220720025a0d0020040d00200041073b01040c010b20064298d5afd2c6aeacae2f370338200642c2cdc8b0c7b9e78f857f370330200642e4c5bdb4b2e9a5a6807f370348200642d790d7a3fef9fda0c800370340200641206a200641306a10e68880800002402006290328420020062802201b2007200220072002541b2207540d00200641306a2001200720032005109b8c808000200641306a41086a2903002107024020062802300d0020064298d5afd2c6aeacae2f370338200642c2cdc8b0c7b9e78f857f370330200642e4c5bdb4b2e9a5a6807f370348200642d790d7a3fef9fda0c800370340200641106a200641306a10e688808000200629031821082006280210210520064298d5afd2c6aeacae2f370338200642c2cdc8b0c7b9e78f857f370330200642e4c5bdb4b2e9a5a6807f370348200642d790d7a3fef9fda0c8003703402006200641306a10e68880800020064298d5afd2c6aeacae2f370338200642c2cdc8b0c7b9e78f857f370330200642e4c5bdb4b2e9a5a6807f370348200642d790d7a3fef9fda0c800370340200642002008420020051b220820077d220220022008561b3703880141002105200641306a412020064188016a410841002802e0a1c6800011868080800000200641d8006a200141186a290000370300200641306a41206a200141106a290000370300200641306a41186a200141086a290000370300200620073703382006410b3a003020062001290000370340200641306a108e8a808000200020073703080c030b200641306a41106a290300210820002006280234360204200041106a2008370300200041086a20073703000c010b20004188023b01040b410121050b2000200536020020064190016a2480808080000b890402017f037e2380808080004180016b22022480808080002002200137030002400240200150450d00420021010c010b200241306a200010fa87808000200241d0006a290300200241c8006a29030084500d00200010a08c8080001a200241086a20002000200210938a8080000240200229030822014202510d0020022903282103200229032021042002290318210502402001500d0020022903102101200241d8006a200041186a290000370300200241d0006a200041106a290000370300200241306a41186a200041086a29000037030020022001370338200241003a003020022000290000370340200241306a108e8a8080000b024020054200510d00200241d8006a200041186a290000370300200241d0006a200041106a290000370300200241306a41186a200041086a2900003703002002200437033820022000290000370340200241013a0030200241306a108e8a8080002005a74101470d00200442ffffe883b1de16200442ffffe883b1de16541b2201500d0020022001370330200241306a10a88a8080000b200241d8006a200041186a290000370300200241d0006a200041106a290000370300200241306a41186a200041086a290000370300200241053a00302002200029000037034020022003370338200241306a108e8a808000200229030020037d21010c010b200229030021010b20024180016a24808080800020010bf10803017f057e027f23808080800041c0016b220124808080800020012000360208200141206a200010fa878080000240200141306a29030022024200530d00200141c8006a2903002103200141386a2903002104200129032821050240200141c0006a2903002206500d0020034200520d00200141206a200010fa87808000024020012802540d0002400240417f4100280284a4c680002207410247200741024b1b2207450d00200741ff017141ff01470d010b200141fe838080003602742001200141086a3602704100280290a1c680002100410028028ca1c6800021074100280280a4c680002108200141d8006a4201370200200141d0006a4102360200200141c8006a4111360200200141c4006a4198dcc58000360200200141386a41b8d6c58000ad4280808080d00b843702002001412c6a41ead8c58000ad4280808080f00284370200200141d4006a200141f0006a36020020014188dcc5800036024c20014102360240200141003602342001410036022820014281808080c0eb00370220200741ecf2c08000200841024622081b200141206a200041d4f2c0800020081b28021011848080800000200128020821000b200020001084888080001a2004428080e983b1de162004428080e983b1de16561b2104200128020821000b2001410c6a200010f487808000024020012d000c410e460d00200141f0006a41106a2001410c6a41106a280200360200200141f0006a41086a2001410c6a41086a2902003703002001200129020c3703700240417f4100280284a4c680002200410147200041014b1b2200450d00200041ff017141ff01470d010b200141b0016a410c6a41ca83808000360200200141e0818080003602b4012001418c98c380003602b0012001200141f0006a3602b8014100280290a1c680002100410028028ca1c6800021074100280280a4c680002108200141d8006a4202370200200141d0006a4102360200200141c8006a4112360200200141c4006a419498c38000360200200141386a41a698c38000ad4280808080c00c84370200200141206a410c6a418a99c38000ad4280808080b00384370200200141d4006a200141b0016a3602002001418497c3800036024c20014101360240200141003602342001410036022820014281808080e024370220200741ecf2c08000200841024622081b200141206a200041d4f2c0800020081b280210118480808000000b200128020821000b200141f0006a200010fa87808000200141206a200010fa878080000240024020012802540d00200141206a200010fa8780800020012802580d0020001086888080000c010b200141306a2002428080808080808080807f8437030020012005370328200120033703482001200637034020012004370338200142013703202000200141206a1082888080000b200141396a2001280208220041186a290000370000200141316a200041106a290000370000200141296a200041086a2900003700002001410e3a002020012000290000370021200141206a108e8a8080000b200141c0016a2480808080002002427f550ba50e01047f23808080800041206b22012480808080002001410036021020014280808080800137020841002d00fca3c680001a024002400240024002400240024002400240410841002802c8a3c68000118180808000002202450d00200241a9dcc580003602002002412636020441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d01200342003700000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410136025c2004200236025820044288808080103703502004200336024c2004428d8080808001370244200441aebfc38000360240200441ef80808000360218200442a5e9e3ab9e929adc2c37030820044100360200200441106a4293888c8f89fdc6ec9e7f37030041002d00fca3c680001a2001200128021041016a360210410841002802c8a3c68000118180808000002202450d02200241cfdcc58000360200200241c20036020441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d03200342003700000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410136025c2004200236025820044288808080103703502004200336024c200442908080808001370244200441e8bec38000360240200441ef8080800036021820044100360200200442a5e9e3ab9e929adc2c370308200441106a4293888c8f89fdc6ec9e7f3703002001200128021041016a36021041002d00fca3c680001a41c00141002802c8a3c68000118180808000002204450d04200441fee1c580003602b801200441b4e1c580003602b001200441d9e0c580003602a80120044185e0c580003602a001200441a8d9c58000360298012004419fdfc58000360290012004419cdfc5800036028801200441e8dfc5800036028001200441eaddc58000360278200441ddddc58000360270200441a8d9c58000360268200441d3ddc58000360260200441a8d9c58000360258200441a3dfc58000360250200441a8d9c580003602482004419fdfc580003602402004419cdfc5800036023820044196dec58000360230200441eaddc58000360228200441ddddc58000360220200441a8d9c58000360218200441d3ddc58000360210200441a8d9c58000360208200441c20036020420044191ddc58000360200200441bc016a41d000360200200441b4016a41ca00360200200441ac016a41db00360200200441a4016a41d4003602002004419c016a410036020020044194016a41043602002004418c016a410336020020044184016a411d360200200441fc006a412c360200200441f4006a410d360200200441ec006a4100360200200441e4006a410a360200200441dc006a4100360200200441d4006a41c500360200200441cc006a4100360200200441c4006a41043602002004413c6a4103360200200441346a4186013602002004412c6a412c360200200441246a410d3602002004411c6a4100360200200441146a410a3602002004410c6a41003602002001411836021c2001200436021820014118360214200141146a200141086a10a08880800041002d00fca3c680001a412041002802c8a3c68000118180808000002204450d05200441c5e3c58000360218200441a8d9c58000360210200441fce2c580003602082004412e360204200441cee2c580003602002004411c6a41eb00360200200441146a41003602002004410c6a41c9003602002001410436021c2001200436021820014104360214200141146a200141086a109a8880800041002d00fca3c680001a411841002802c8a3c68000118180808000002204450d06200441d9e4c58000360210200441a8d9c5800036020820044129360204200441b0e4c58000360200200441146a41ec003602002004410c6a41003602002001410336021c2001200436021820014103360214200141146a200141086a10988880800041002d00fca3c680001a410841002802c8a3c68000118180808000002204450d072004411b360204200441c5e5c580003602002001410136021c2001200436021820014101360214200141146a200141086a10a18880800041002d00fca3c680001a410841002802c8a3c68000118180808000002204450d0820044122360204200441e0e5c580003602002001410136021c2001200436021820014101360214200141146a200141086a109c88808000200041086a200141086a41086a28020036020020002001290208370200200041106a4108360200200041e0a6c4800036020c200141206a2480808080000f0b4104410810b280808000000b4101410810b280808000000b4104410810b280808000000b4101410810b280808000000b410441c00110b280808000000b4104412010b280808000000b4104411810b280808000000b4104410810b280808000000b4104410810b280808000000bad0a01097f41002d00fca3c680001a02400240024002400240024002400240024041e00141002802c8a3c68000118180808000002201450d0041002d00fca3c680001a410841002802c8a3c68000118180808000002202450d012002428080e983b1de1637000041002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d0220034182e6c58000360200200341f9e8c58000360238200341a8d9c58000360230200341d6e8c5800036022820034180e8c58000360220200341a8e7c58000360218200341d2e6c58000360210200341a8d9c58000360208200341d0003602042003413c6a413c360200200341346a41003602002003412c6a4123360200200341246a41d6003602002003411c6a41d800360200200341146a41d6003602002003410c6a410036020041002d00fca3c680001a410441002802c8a3c68000118180808000002204450d032004413236000041002d00fca3c680001a412041002802c8a3c68000118180808000002205450d04200541c7e9c58000360200200541c5e3c58000360218200541a8d9c5800036021020054184eac580003602082005413d3602042005411c6a41eb00360200200541146a41003602002005410c6a413736020041002d00fca3c680001a410441002802c8a3c68000118180808000002206450d052006413236000041002d00fca3c680001a411841002802c8a3c68000118180808000002207450d06200741c3eac58000360200200741d9e4c58000360210200741a8d9c58000360208200741c300360204200741146a41ec003602002007410c6a410036020041002d00fca3c680001a410441002802c8a3c68000118180808000002208450d072008410036000041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d08200941d80036020420094191ebc58000360200200141b8016a42e88488d0c0e3aebc13370300200141b0016a42d7c9cb8fc1cf97db3e37030020014180016a42e88488d0c0e3aebc13370300200141f8006a42d7c9cb8fc1cf97db3e370300200141c8006a42e88488d0c0e3aebc13370300200141c0006a42d7c9cb8fc1cf97db3e370300200141106a4293888c8f89fdc6ec9e7f370300200142a5e9e3ab9e929adc2c370308200141dc016a4101360200200141d8016a2009360200200141d0016a428480808010370200200141cc016a2008360200200141c8016a4104360200200141c0016a41b580808000360200200141ac016a410a360200200141e9ebc580003602a801200141a4016a4103360200200141a0016a200736020020014198016a42848080803037020020014194016a200636020020014190016a410436020020014188016a41b580808000360200200141f4006a410b36020020014186ebc58000360270200141ec006a4104360200200141e8006a2005360200200141e0006a4284808080c000370200200141dc006a2004360200200141d8006a4104360200200141d0006a41b5808080003602002001413c6a4108360200200141bbeac5800036023820014108360234200120033602302001428880808080013702282001200236022420014108360220200141ef8080800036021820014112360204200141b5e9c580003602002000410436020820002001360204200041043602000f0b410841e00110b280808000000b4101410810b280808000000b410441c00010b280808000000b4101410410b280808000000b4104412010b280808000000b4101410410b280808000000b4104411810b280808000000b4101410410b280808000000b4104410810b280808000000bad0302017f047e2380808080004180016b2203248080808000200110a08c8080001a200341086a20012001200210958a80800002400240200329030822044202510d0020032903282105200329032021062003290318210702402004500d0020032903102104200341d8006a200141186a290000370300200341d0006a200141106a290000370300200341306a41186a200141086a29000037030020032004370338200341003a003020032001290000370340200341306a108e8a8080000b024020074200510d00200341d8006a200141186a290000370300200341d0006a200141106a290000370300200341306a41186a200141086a2900003703002003200637033820032001290000370340200341013a0030200341306a108e8a8080002007a74101470d00200642ffffe883b1de16200642ffffe883b1de16541b2204500d0020032004370330200341306a10a88a8080000b20002005370308410021010c010b200341146a290200210420032802102101200041106a2003411c6a290200370300200041086a200437030020002001360204410121010b2000200136020020034180016a2480808080000beb0101037f23808080800041106b220224808080800002402001280200220328020020032802082204470d0020032004410110ab86808000200328020821040b200328020420046a41fb003a00002003200441016a3602082002200136020c20024180023b01080240200241086a41f3ebc580004108200010d98680800022030d002002280208220441ff01710d0020044180fe0371450d000240200228020c280200220428020020042802082201470d0020042001410110ab86808000200428020821010b200428020420016a41fd003a00002004200141016a3602080b200241106a24808080800020030b951103057f017e017f23808080800041c0016b22022480808080000240024002400240024002400240024002400240024002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020020042d00000e0b010a02030405060a0708090a0b200041003a00000c0b0b02402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241a0016a41086a2203200441086a290000370300200241a0016a41106a2205200441106a290000370300200241a0016a41186a2206200441186a290000370300200220042900003703a001200241086a200110be8a8080002002290308a70d0020022903102107200020022903a001370010200041286a2006290300370000200041206a2005290300370000200041186a200329030037000020002007370308200041013a00000c0b0b200041003a00000c0a0b2001280200220328020422044120490d082003200441606a36020420032003280200220441206a36020020024180016a41086a200441086a29000037030020024180016a41106a200441106a29000037030020024180016a41186a200441186a290000370300200220042900003703800102402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241a0016a41086a200441086a290000370300200241a0016a41106a200441106a290000370300200241a0016a41186a200441186a290000370300200220042900003703a001200241186a200110be8a8080002002290318a70d00200229032021072000200229038001370010200020022903a001370030200041286a20024180016a41186a290300370000200041206a20024180016a41106a290300370000200041186a20024180016a41086a290300370000200041386a200241a0016a41086a290300370000200041c0006a200241a0016a41106a290300370000200041c8006a200241a0016a41186a29030037000020002007370308200041023a00000c0a0b200041003a00000c090b02402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241a0016a41086a2203200441086a290000370300200241a0016a41106a2205200441106a290000370300200241a0016a41186a2206200441186a290000370300200220042900003703a001200241286a200110be8a8080002002290328a70d0020022903302107200020022903a001370010200041286a2006290300370000200041206a2005290300370000200041186a200329030037000020002007370308200041033a00000c090b200041003a00000c080b02402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241a0016a41086a200441086a290000370300200241a0016a41106a200441106a290000370300200241a0016a41186a200441186a290000370300200220042900003703a001200128020022032802042201450d0020032001417f6a36020420032003280200220141016a3602004101410220012d000022034101461b410020031b22034102460d00200020022903a001370001200041196a200241b8016a290300370000200041116a200241b0016a290300370000200041096a200241a8016a290300370000200020033a0021200041043a00000c080b200041003a00000c070b02402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241a0016a41086a2205200441086a290000370300200241a0016a41106a2206200441106a290000370300200241a0016a41186a2208200441186a290000370300200220042900003703a0012001280200220328020422014108490d00200020022903a0013700102003200141786a36020420032003280200220141086a360200200041186a2005290300370000200041206a2006290300370000200041286a200829030037000020002001290000370308200041053a00000c070b200041003a00000c060b200241386a200110bc8a808000024020022802380d00200241f4006a2001200228023c10c8858080002002280274418080808078460d0020024180016a41086a200241f4006a41086a2802002203360200200241ab016a200336000020022002290274220737038001200220073700a301200020022900a001370001200041086a200241a7016a290000370000200041063a00000c060b200041003a00000c050b02402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241a0016a41086a2203200441086a290000370300200241a0016a41106a2205200441106a290000370300200241a0016a41186a2206200441186a290000370300200220042900003703a001200241c0006a200110be8a8080002002290340a70d0020022903482107200020022903a001370010200041286a2006290300370000200041206a2005290300370000200041186a200329030037000020002007370308200041073a00000c050b200041003a00000c040b0240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a3602004101410220042d000022034101461b410020031b22034102460d00200241d0006a200110be8a8080002002290350a70d0020022903582107200020033a0001200041083a0000200020073703080c040b200041003a00000c030b200241e0006a200110be8a80800002402002290360a70d00200128020022032802042201450d002002290368210720032001417f6a36020420032003280200220141016a3602004101410220012d000022034101461b410020031b22034102460d00200020033a0001200041093a0000200020073703080c030b200041003a00000c020b200041003a00000c010b200041003a00000b200241c0016a2480808080000ba81002047f017e23808080800041106b2202248080808000024002400240024002400240024002400240024020002d0000417f6a0e09000102030405060708090b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41003a00002001200341016a2204360208200041106a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b2001200441206a360208200128020420046a22042003290000370000200441086a200341086a290000370000200441106a200341106a290000370000200441186a200341186a2900003700002002200041086a36020c2002410c6a200110c18a8080000c080b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41023a00002001200341016a2204360208200041106a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b200128020420046a22052003290000370000200541186a200341186a290000370000200541106a200341106a290000370000200541086a200341086a2900003700002001200441206a2204360208200041306a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b2001200441206a360208200128020420046a22042003290000370000200441086a200341086a290000370000200441106a200341106a290000370000200441186a200341186a2900003700002002200041086a36020c2002410c6a200110c18a8080000c070b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41033a00002001200341016a2204360208200041106a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b2001200441206a360208200128020420046a22042003290000370000200441086a200341086a290000370000200441106a200341106a290000370000200441186a200341186a2900003700002002200041086a36020c2002410c6a200110c18a8080000c060b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41043a00002001200341016a2204360208200041016a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b200128020420046a22052003290000370000200541186a200341186a290000370000200541106a200341106a290000370000200541086a200341086a2900003700002001200441206a220336020820002d00212100024020012802002003470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a20003a00000c050b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41053a00002001200341016a2204360208200041106a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b200128020420046a22052003290000370000200541186a200341186a290000370000200541106a200341106a290000370000200541086a200341086a2900003700002001200441206a2203360208200029030821060240200128020020036b41074b0d0020012003410810b182808000200128020821030b2001200341086a360208200128020420036a20063700000c040b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41063a00002001200341016a360208200041086a280200210320022000410c6a28020022003602082002200241086a36020c2002410c6a200110c08a8080002000450d032000410574210520012802082104034002400240200128020020046b411f4d0d00200421000c010b20012004412010b182808000200128020821000b2001200041206a2204360208200128020420006a22002003290000370000200041086a200341086a290000370000200041106a200341106a290000370000200041186a200341186a290000370000200341206a2103200541606a22050d000c040b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41083a00002001200341016a2204360208200041106a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b2001200441206a360208200128020420046a22042003290000370000200441086a200341086a290000370000200441106a200341106a290000370000200441186a200341186a2900003700002002200041086a36020c2002410c6a200110c18a8080000c020b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41093a00002001200341016a220336020820002d00012104024020012802002003470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a20043a00002002200041086a36020c2002410c6a200110c18a8080000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a410a3a00002002200041086a36020c2002410c6a200110c18a80800020002d000121000240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a20003a00000b200241106a2480808080000bc30302017f017e41002101024002400240024002400240024002400240024020002d0000417f6a0e09000102030405060807090b412121012000290308220242c000540d0841222101200242808001540d08412421012002428080808004540d084129200279a74103766b21010c080b41c10021012000290308220242c000540d0741c2002101200242808001540d0741c40021012002428080808004540d0741c900200279a74103766b21010c070b412121012000290308220242c000540d0641222101200242808001540d06412421012002428080808004540d064129200279a74103766b21010c060b412121010c050b412821010c040b2000410c6a28020041057441047221010c030b412121012000290308220242c000540d0241222101200242808001540d02412421012002428080808004540d024129200279a74103766b21010c020b410221012000290308220242c000540d0141032101200242808001540d01410521012002428080808004540d01410a200279a74103766b21010c010b410221012000290308220242c000540d0041032101200242808001540d00410521012002428080808004540d00410a200279a74103766b21010b200141016a0b802b02037f017e0240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020002d00000e16000102030405060708090a0b0c0d0e0f101112131415160b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41003a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41013a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41023a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2203360208200041306a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41033a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41043a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41053a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41063a00002001200241016a2203360208200041026a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2203360208200041226a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029034821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b200128020420026a20053700002001200241086a220236020820002d00012100024020012802002002470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a20003a00000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41073a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41083a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41093a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a410a3a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a410b3a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a410c3a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a410d3a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a410e3a00002001200241016a2202360208200041016a21000240200128020020026b411f4b0d0020012002412010b182808000200128020821020b2001200241206a360208200128020420026a22012000290000370000200141086a200041086a290000370000200141106a200041106a290000370000200141186a200041186a2900003700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a410f3a00002001200241016a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41103a00002001200241016a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41113a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41123a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41133a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41143a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41153a00002001200241016a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b200128020420026a20053700002001200241086a2202360208200029031021050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000b0b140020002d000041027441a4fcc580006a2802000b970601037f0240024002400240024002400240024002400240024020012d0000417f6a0e09000102030405060708000b2000200129031037031020002001290308370308200041286a200141286a290300370300200041206a200141206a290300370300200041186a200141186a290300370300200041013a00000f0b2000200129031037031020002001290330370330200041286a200141286a290300370300200041206a200141206a290300370300200041186a200141186a290300370300200041386a200141386a290300370300200041c0006a200141c0006a290300370300200041c8006a200141c8006a29030037030020002001290308370308200041023a00000f0b2000200129031037031020002001290308370308200041286a200141286a290300370300200041206a200141206a290300370300200041186a200141186a290300370300200041033a00000f0b20002001290001370001200020012d00213a0021200041196a200141196a290000370000200041116a200141116a290000370000200041096a200141096a290000370000200041043a00000f0b2000200129031037031020002001290308370308200041286a200141286a290300370300200041206a200141206a290300370300200041186a200141186a290300370300200041053a00000f0b200141086a2802002102024002402001410c6a28020022010d0041012103410021040c010b200141ffffff1f4b0d0420014105742204417f4c0d0441002d00fca3c680001a200441002802c8a3c68000118180808000002203450d050b20032002200410848e80800021042000410c6a2001360200200041086a200436020020002001360204200041063a00000f0b2000200129031037031020002001290308370308200041286a200141286a290300370300200041206a200141206a290300370300200041186a200141186a290300370300200041073a00000f0b20002001290308370308200020012d00013a0001200041083a00000f0b20002001290308370308200020012d00013a0001200041093a00000f0b10ae80808000000b4101200410b280808000000ba60603057f017e027f23808080800041c0006b2201248080808000200141186a4184ecc58000410441ead8c58000411741a8d9c58000410010d882808000200141106a42043702002001420037020820014280808080800137020041002d00fca3c680001a02400240024041c00041002802c8a3c68000118180808000002202450d0020024188ecc580003602202002410036021820024101360204200241abd8c58000360200200241386a4100360200200241246a410136020020014102360238200120023602302001200241c0006a36023c200120023602342001200141306a10fa8680800041002d00fca3c680001a20012802002103200128020421022001280208210420012802182105200129021c2106410841002802c8a3c68000118180808000002207450d01200741002903d0ecc580003702002001410036020820014280808080c000370200200141306a20014195d7c58000411410968b8080002001200141306a41a9d7c58000410e109b8b808000200141306a200141b7d7c58000411310ca8b8080002001200141306a41cad7c58000410c10958b808000200141306a200141d6d7c58000410f10cf8b8080002001200141306a41e5d7c58000411010cd8b808000200141306a200141f5d7c58000411110858b8080002001200141306a4186d8c58000411b10808b808000200141246a200141a1d8c58000410410a78b8080002001200128022436020820012001280228220836020020012008200128022c41246c6a36020c20012008360204200141306a200110fb868080002005418080808078460d022001200336020820012002360200200120023602042001200220044105746a36020c200041c4006a200110fa868080002001410b6a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a0000200041d8006a4101360200200041d4006a2007360200200041013602502001200129023037000320002001290000370001200041086a200141076a290000370000200141c0006a2480808080000f0b410841c00010b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000bff1703067f017e037f23808080800041c0006b2201248080808000200141206a41d8ecc58000410541ead8c58000411741a8d9c58000410010d882808000200141186a42043702002001420037021020014280808080800137020841002d00fca3c680001a02400240024002400240024002400240024002400240024002400240024041c00041002802c8a3c68000118180808000002202450d0020024188ecc580003602202002410036021820024101360204200241abd8c58000360200200241386a4100360200200241246a410136020020014102360238200120023602302001200241c0006a36023c20012002360234200141086a200141306a10fa8680800041002d00fca3c680001a20012802082103200128020c2104200128021021052001280220210620012902242107410841002802c8a3c68000118180808000002208450d0120084100290380edc5800037020041002d00fca3c680001a41002802c8a3c6800021022001410036021020014280808080c00037020841082002118180808000002209450d02200941002903e893c58000370200200141086a410010a086808000200128020c200141086a41086a220a28020041246c6a220241003a00202002410e36021c20024188edc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200a28020041016a3602002001200129020837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d03200941002903d097c580003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241013a00202002411536021c20024196edc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d04200941002903e896c580003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241023a00202002411336021c200241abedc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d052009410029038896c580003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241033a00202002411236021c200241b5e9c5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d06200941002903c894c580003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241043a00202002410d36021c200241beedc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d072009410029039098c580003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241053a00202002411736021c200241cbedc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d08200941002903f894c580003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241063a00202002410b36021c200241e2edc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d09200941002903c096c580003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241073a00202002410f36021c200241ededc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0a200941002903c095c580003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241083a00202002410c36021c200241fcedc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0b2009410029039894c580003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241093a00202002410e36021c20024188eec5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0c200941002903d898c580003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a2202410a3a00202002411336021c20024196eec5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0d2009410029039097c580003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a2202410b3a00202002410936021c200241a9eec58000360218200241013602142002200936021020024280808080103702082002428080808080013702002001280238210920012802342102200120012802303602102001200236020820012002200941246c6a41246a3602142001200236020c200141306a200141086a10fb868080002006418080808078460d0e20012003360210200120043602082001200436020c2001200420054105746a360214200041c4006a200141086a10fa86808000200141086a410b6a200141306a41086a2802003600002000413c6a200737020020002006360238200041013a0000200041d8006a4101360200200041d4006a2008360200200041013602502001200129023037000b20002001290008370001200041086a2001410f6a290000370000200141c0006a2480808080000f0b410841c00010b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000bba0803057f017e027f23808080800041c0006b2201248080808000200141186a41b2eec58000410541ead8c58000411741a8d9c58000410010d882808000200141106a42043702002001420037020820014280808080800137020041002d00fca3c680001a02400240024041c00041002802c8a3c68000118180808000002202450d0020024188ecc580003602202002410036021820024101360204200241abd8c58000360200200241386a4100360200200241246a410136020020014102360238200120023602302001200241c0006a36023c200120023602342001200141306a10fa8680800041002d00fca3c680001a20012802002103200128020421022001280208210420012802182105200129021c2106410841002802c8a3c68000118180808000002207450d01200741002903d8eec580003702002001410036020820014280808080c000370200200141306a200141e0eec58000410710b08b8080002001200141306a41e7eec580004108108e8b808000200141306a200141efeec58000410810ab8b8080002001200141306a41f7eec58000410a10b78b808000200141306a20014181efc58000410810998b8080002001200141306a4189efc58000410a10888b808000200141306a20014193efc58000411210d18b8080002001200141306a41a5efc580004107108b8b808000200141306a200141acefc58000410810b58b8080002001200141306a41b4efc58000410710c58b808000200141306a200141bbefc58000410610ae8b8080002001200141306a41c1efc58000410610848b808000200141306a200141c7efc58000410910bb8b8080002001200141306a41d0efc58000410810b98b808000200141306a200141d8efc580004108108f8b8080002001200141306a41e0efc58000410610978b808000200141306a200141e6efc58000410910ba8b8080002001200141306a41efefc58000410610c28b808000200141306a200141f5efc58000410810a38b8080002001200141306a41fdefc58000410610c78b808000200141306a20014183f0c580004106108c8b808000200141246a200141306a4189f0c58000411310ad8b8080002001200128022436020820012001280228220836020020012008200128022c41246c6a36020c20012008360204200141306a200110fb868080002005418080808078460d022001200336020820012002360200200120023602042001200220044105746a36020c200041c4006a200110fa868080002001410b6a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a0000200041d8006a4101360200200041d4006a2007360200200041013602502001200129023037000320002001290000370001200041086a200141076a290000370000200141c0006a2480808080000f0b410841c00010b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000bfa0302047f017e23808080800041f0006b220224808080800002400240024002400240024020012802042203450d0020012003417f6a36020420012001280200220441016a36020020042d0000417f6a0e03010203040b200042033703000c040b024020034105490d0020012003417b6a22053602042001200441056a36020020054108490d00200428000121052001200341736a36020420012004410d6a360200200429000521062002410f6a200110df8780800020022d000f0d00200041086a200241106a41e00010848e8080001a2000200536027020002006370368200042003703000c040b200042033703000c030b024020034105490d0020012003417b6a22053602042001200441056a36020020054108490d0020002004280001360210200042013703002001200341736a36020420012004410d6a360200200020042900053703080c030b200042033703000c020b024020034105490d0020012003417b6a22053602042001200441056a36020020054108490d00200428000121052001200341736a36020420012004410d6a360200200429000521062002410f6a200110df8780800020022d000f0d00200041086a200241106a41e00010848e8080001a2000200536027020002006370368200042023703000c020b200042033703000c010b200042033703000b200241f0006a2480808080000bb10302077f017e23808080800041106b2202248080808000200028020421032002200028020822043602082002200241086a36020c2002410c6a200110c08a8080000240024020040d00200128020821040c010b2003200441286c6a210520012802082104034002402001280200220620046b411f4b0d0020012004412010b18280800020012802002106200128020821040b2001280204220720046a22082003290000370000200841186a200341186a290000370000200841106a200341106a290000370000200841086a200341086a2900003700002001200441206a2208360208200341206a29030021090240200620086b41074b0d0020012008410810b18280800020012802042107200128020821080b2001200841086a2204360208200720086a2009370000200341286a22032005470d000b0b0240200128020020046b411f4b0d0020012004412010b182808000200128020821040b2001200441206a360208200128020420046a2203200029000c370000200341086a200041146a290000370000200341106a2000411c6a290000370000200341186a200041246a290000370000200241106a2480808080000b9c0202047f017e02402001280200220220012802082203470d0020012003410110b18280800020012802002102200128020821030b2001200341016a22043602082001280204220520036a41013a0000200029030021060240200220046b41074b0d0020012004410810b1828080002001280204210520012802002102200128020821040b2001200441086a2203360208200520046a2006370000200029030821060240200220036b41074b0d0020012003410810b18280800020012802042105200128020821030b200520036a20063700002001200341086a220436020820002d00102103024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a20033a00000b8b0201047f024020002d00000d000240200128020020012802082200470d0020012000410110b182808000200128020821000b200128020420006a41003a00002001200041016a3602080f0b02402001280200220220012802082203470d0020012003410110b18280800020012802002102200128020821030b2001200341016a22043602082001280204220520036a41013a0000200041016a21000240200220046b411f4b0d0020012004412010b18280800020012802042105200128020821040b200520046a22032000290000370000200341186a200041186a290000370000200341106a200041106a290000370000200341086a200041086a2900003700002001200441206a3602080b900602017e037f0240200029030022024203520d000240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41003a00000f0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a2204360208200128020420036a41013a00000240024002402002a70e03000102000b024020012802002004470d0020012004410110b182808000200128020821040b200128020420046a41013a00002001200441016a2204360208200041f0006a28020021030240200128020020046b41034b0d0020012004410410b182808000200128020821040b200041086a2105200128020420046a20033600002001200441046a2204360208200041e8006a29030021020240200128020020046b41074b0d0020012004410810b182808000200128020821040b2001200441086a360208200128020420046a20023700002005200110e0878080000f0b024020012802002004470d0020012004410110b182808000200128020821040b200128020420046a41023a00002001200441016a2204360208200041106a2802002103200029030821020240200128020020046b41034b0d0020012004410410b182808000200128020821040b200128020420046a20033600002001200441046a22003602080240200128020020006b41074b0d0020012000410810b182808000200128020821000b2001200041086a360208200128020420006a20023700000f0b024020012802002004470d0020012004410110b182808000200128020821040b200128020420046a41033a00002001200441016a2204360208200041f0006a28020021030240200128020020046b41034b0d0020012004410410b182808000200128020821040b200041086a2105200128020420046a20033600002001200441046a2204360208200041e8006a29030021020240200128020020046b41074b0d0020012004410810b182808000200128020821040b2001200441086a360208200128020420046a20023700002005200110e0878080000bfb0301087f23808080800041106b220224808080800020012d0000210341002d00fca3c680001a41002802c8a3c6800021040240024002400240024020034102460d0041022004118180808000002204450d02200441013a000020022004360208200241023602042002410136020c41002d00fca3c680001a412041002802c8a3c68000118180808000002204450d0320042001290001370000200441186a2205200141196a290000370000200441106a2206200141116a290000370000200441086a2207200141096a290000370000200241046a4101412010b18280800020022802082208200228020c22096a22012004290000370000200141086a2007290000370000200141106a2006290000370000200141186a20052900003700002002200941206a220136020c20022802042105200441002802c0a3c6800011808080800000024020052001470d00200241046a2005410110b18280800020022802082108200228020c21010b200820016a20033a00002002200141016a36020c0c010b41012004118180808000002201450d03200141003a000020022001360208200241013602042002410136020c0b20002002290204370200200041086a200241046a41086a280200360200200241106a2480808080000f0b4101410210b280808000000b4101412010b280808000000b4101410110b280808000000bbb0301057f23808080800041206b2202248080808000024002400240024002402001280200418080808078460d00024002402001280208220341056a2204450d002004417f4c0d044100210541002d00fca3c680001a200441002802c8a3c68000118180808000002206450d05200220063602102002200436020c0c010b20024100360214200242808080801037020c2002410c6a4100410110b18280800020022802102106200228021421050b200620056a41013a00002002200541016a36021420012802042104200220033602182002200241186a36021c2002411c6a2002410c6a10c08a8080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a2002200120036a3602140c010b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d03200220013602102002410136020c200141003a0000200241013602140b2000200229020c370200200041086a2002410c6a41086a280200360200200241206a2480808080000f0b10ae80808000000b4101200410b280808000000b4101410110b280808000000bbf0201047f23808080800041206b2202248080808000024002400240024002402001280200418080808078460d0020012802082203417f4c0d0241002d00fca3c680001a2003410474410572220441002802c8a3c68000118180808000002205450d03200220053602102002200436020c200541013a00002002410136021420012802042101200220033602182002200241186a36021c2002411c6a2002410c6a10c08a808000200120032002410c6a10d4858080000c010b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d03200220013602102002410136020c200141003a0000200241013602140b2000200229020c370200200041086a2002410c6a41086a280200360200200241206a2480808080000f0b10ae80808000000b4101200410b280808000000b4101410110b280808000000bbb0301057f23808080800041206b2202248080808000024002400240024002402001280200418080808078460d00024002402001280208220341056a2204450d002004417f4c0d044100210541002d00fca3c680001a200441002802c8a3c68000118180808000002206450d05200220063602102002200436020c0c010b20024100360214200242808080801037020c2002410c6a4100410110b18280800020022802102106200228021421050b200620056a41013a00002002200541016a36021420012802042104200220033602182002200241186a36021c2002411c6a2002410c6a10c08a8080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a2002200120036a3602140c010b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d03200220013602102002410136020c200141003a0000200241013602140b2000200229020c370200200041086a2002410c6a41086a280200360200200241206a2480808080000f0b10ae80808000000b4101200410b280808000000b4101410110b280808000000b21002001280214419cf0c580004114200141186a28020028020c118280808000000bc80201027f23808080800041106b220224808080800002400240200028020022002802000d00200128021441acd8c580004104200141186a28020028020c1182808080000021010c010b2002200041046a3602002002200128021441b0d8c580004104200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241d0f0c58000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010bc80201027f23808080800041106b220224808080800002400240200028020022032d00000d00200128021441acd8c580004104200141186a28020028020c1182808080000021000c010b410121002002200341016a3602002002200128021441b0d8c580004104200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241b0f0c58000108d81808000210120022d000c21030240200128020022010d00200341ff017141004721000c010b200341ff01710d0020022802082103024020014101470d0020022d000d41ff0171450d0020032d001c4104710d00410121002003280214418ca5c080004101200341186a28020028020c118280808000000d010b2003280214418da5c080004101200341186a28020028020c1182808080000021000b200241106a24808080800020000bc50201027f23808080800041106b220224808080800002400240200028020022002802000d00200128021441acd8c580004104200141186a28020028020c1182808080000021010c010b200220003602002002200128021441b0d8c580004104200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241c0f0c58000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010bd90703027f017e017f23808080800041a0016b22082480808080002008412c6a2006360200200841246a2004360200200841086a41086a200141086a28020022093602002008411c6a200241086a28020036020020082001290200220a37030820082007360230200820053602282008200336022020082002290200370214200828020c2101024002400240024002400240024002400240024002400240200aa70d00024020072802002202450d0020072802042107200841f0006a20043602002008200336026c20084184808080783602682002200841e8006a200728020c118480808000000b20090d01410121070c020b20094120470d07200841386a41186a2209200141186a290000370300200841386a41106a2202200141106a290000370300200841386a41086a220b200141086a29000037030020082001290000370338200841dc006a2005200841386a200841146a200628020c11868080800000200828025c2206418080808078460d022008280264210120082802602109024020072802002202450d002007280204210720084194016a200841d0006a2903003702002008418c016a200841386a41106a29030037020020084184016a200841c0006a290300370200200841e8006a41106a20043602002008200829033837027c20082003360274200820013602702008200936026c20084180808080783602682002200841e8006a200728020c118480808000000b20010d03410121070c040b2009417f4c0d0741002d00fca3c680001a200941002802c8a3c68000118180808000002207450d080b20072001200910848e80800021012000200936020820002001360204200020093602000c040b200841e8006a41186a2009290300370300200841e8006a41106a2002290300370300200841e8006a41086a200b2903003703002008200829033837036841002d00fca3c680001a413041002802c8a3c680001181808080000022010d024104413010b280808000000b2001417f4c0d0441002d00fca3c680001a200141002802c8a3c68000118180808000002207450d060b20072009200110848e80800021072000200136020820002007360204200020013602002006450d01200941002802c0a3c68000118080808000000c010b2001418580808078360200200120082903683702042001410c6a200841f0006a290300370200200141146a200841f8006a2903003702002001411c6a20084180016a2903003702002000418080808078360200200020013602040b200841a0016a2480808080000f0b4120200941a4f2c5800010a281808000000b10ae80808000000b4101200910b280808000000b4101200110b280808000000b8b0d03017f017e077f23808080800041a0016b22022480808080002001290200210320002802002204280200220128020021052001280204210620012802282100200241d8006a41286a22074100360200200241d8006a20052001200041284b22081b220520052006200020081b6a10f686808000200241086a41286a2007280200360200200241086a41206a200241d8006a41206a290200370300200241086a41186a200241d8006a41186a290200370300200241086a41106a200241d8006a41106a290200370300200241086a41086a200241d8006a41086a290200370300200220022902583703082002200128022c22013602340240200342ff0183500d002003420888a7210002400240024020014101710d00200241086a41047221012000410474210602400240200241086a410441282002280230220741284b22001b6a28020022052007412820001b460d002001200241086a41286a20001b21012002280208200241086a20001b21000c010b200241086a10f78d808000200228020c2105200228020821000b200020056a20063a00002001200128020041016a3602000c010b200228020c20022802302201200141284b22011b2205450d0120052002280208200241086a20011b6a417f6a220120012d00002000723a00000b2002200228023441016a3602340c010b419c94c68000413a41d894c6800010a181808000000b024002400240024002402003422088a722072802002200417e6a2201410420014106491b0e06040200040104040b200741286a21010c020b200741ec046a21010c010b200741046a21010b200241086a200110e18d80800020072802002200417e6a21010b02400240024002400240024002400240024002402001410420014106491b0e06050105030200050b2007410c6a2101200741046a21000c030b2007280234450d03200741346a21002007413c6a21010c020b2000450d022007280204450d02200741046a21002007410c6a21010c010b2007280204450d01200741086a2200280200450d01200741106a21010b200241386a41186a200141186a290200370300200241386a41106a200141106a290200370300200241386a41086a200141086a290200370300200220012902003703382002280208210620042802042802002108410121010240200228020c20022802302205200541284b22051b2209450d002009417f4c0d0241002d00fca3c680001a200941002802c8a3c68000118180808000002201450d030b20012006200241086a20051b200910848e808000210a200041046a2802002106200028020022052005280200220141016a3602002001417f4c0d032002200636025c2002200536025803402005280204210103402001417f460d012001417f4c0d062005200141016a2005280204220020002001461b360204200020014721062000210120060d000b0b200228025c21002002280258220120012802002201417f6a360200024020014101470d00200241d8006a10f18d8080000b0240200828020822012008280200470d0020082001109f86808000200828020821010b2008280204200141386c6a220141023a000c200120093602082001200a360204200120093602002001200229033837000d200120022f00583b002d2001200036023420012005360230200141156a200241c0006a2903003700002001411d6a200241c8006a290300370000200141256a200241d0006a2903003700002001412f6a200241d8006a41026a2d00003a00002008200828020841016a3602080b20042802042802002101200241d8006a41286a200241086a41286a290300370300200241d8006a41206a200241086a41206a290300370300200241d8006a41186a200241086a41186a290300370300200241d8006a41106a200241086a41106a290300370300200241d8006a41086a200241086a41086a29030037030020022002290308370358200220013602880141002105024002400240024002402007280200417e6a2206410420064106491b0e06040400010204040b200741046a2100410121050c030b200741306a21010c010b2007412c6a21010b41022105410021000b200220024188016a360290012002200241d8006a36028c012002200136029c012002200036029801200241003a009501200220053a00940120024194016a2002418c016a10808a80800002402002280280014129490d00200228025841002802c0a3c68000118080808000000b200241a0016a2480808080000f0b10ae80808000000b4101200910b280808000000b00000b10f08d808000000b8b0d03017f017e077f23808080800041a0016b22022480808080002001290200210320002802002204280200220128020021052001280204210620012802282100200241d8006a41286a22074100360200200241d8006a20052001200041284b22081b220520052006200020081b6a10f686808000200241086a41286a2007280200360200200241086a41206a200241d8006a41206a290200370300200241086a41186a200241d8006a41186a290200370300200241086a41106a200241d8006a41106a290200370300200241086a41086a200241d8006a41086a290200370300200220022902583703082002200128022c22013602340240200342ff0183500d002003420888a7210002400240024020014101710d00200241086a41047221012000410474210602400240200241086a410441282002280230220741284b22001b6a28020022052007412820001b460d002001200241086a41286a20001b21012002280208200241086a20001b21000c010b200241086a10f78d808000200228020c2105200228020821000b200020056a20063a00002001200128020041016a3602000c010b200228020c20022802302201200141284b22011b2205450d0120052002280208200241086a20011b6a417f6a220120012d00002000723a00000b2002200228023441016a3602340c010b419c94c68000413a41d894c6800010a181808000000b024002400240024002402003422088a722072802002200417e6a2201410420014106491b0e06040200040104040b200741286a21010c020b200741ec046a21010c010b200741046a21010b200241086a200110e18d80800020072802002200417e6a21010b02400240024002400240024002400240024002402001410420014106491b0e06050105030200050b2007410c6a2101200741046a21000c030b2007280234450d03200741346a21002007413c6a21010c020b2000450d022007280204450d02200741046a21002007410c6a21010c010b2007280204450d01200741086a2200280200450d01200741106a21010b200241386a41186a200141186a290200370300200241386a41106a200141106a290200370300200241386a41086a200141086a290200370300200220012902003703382002280208210620042802042802002108410121010240200228020c20022802302205200541284b22051b2209450d002009417f4c0d0241002d00fca3c680001a200941002802c8a3c68000118180808000002201450d030b20012006200241086a20051b200910848e808000210a200041046a2802002106200028020022052005280200220141016a3602002001417f4c0d032002200636025c2002200536025803402005280204210103402001417f460d012001417f4c0d062005200141016a2005280204220020002001461b360204200020014721062000210120060d000b0b200228025c21002002280258220120012802002201417f6a360200024020014101470d00200241d8006a10f18d8080000b0240200828020822012008280200470d0020082001109f86808000200828020821010b2008280204200141386c6a220141023a000c200120093602082001200a360204200120093602002001200229033837000d200120022f00583b002d2001200036023420012005360230200141156a200241c0006a2903003700002001411d6a200241c8006a290300370000200141256a200241d0006a2903003700002001412f6a200241d8006a41026a2d00003a00002008200828020841016a3602080b20042802042802002101200241d8006a41286a200241086a41286a290300370300200241d8006a41206a200241086a41206a290300370300200241d8006a41186a200241086a41186a290300370300200241d8006a41106a200241086a41106a290300370300200241d8006a41086a200241086a41086a29030037030020022002290308370358200220013602880141002105024002400240024002402007280200417e6a2206410420064106491b0e06040400010204040b200741046a2100410121050c030b200741306a21010c010b2007412c6a21010b41022105410021000b200220024188016a360290012002200241d8006a36028c012002200136029c012002200036029801200241003a009501200220053a00940120024194016a2002418c016a10ff8980800002402002280280014129490d00200228025841002802c0a3c68000118080808000000b200241a0016a2480808080000f0b10ae80808000000b4101200910b280808000000b00000b10f08d808000000b0c002000200110bf8c8080000bc90801087f2380808080004190076b2202248080808000200128020421032001280200280200220428020421052004280200210620012802082207280208200128020c2802006a2208410176210420072802042109200728020021070240024002400240024002400240024020084101710d00200420094b0d02200241003a00b805200220043602b405200220073602b0050c010b200420094b0d02200420094f0d03200220073602b005200220043602b405200241b9056a200720046a2d000041f001713a0000200241013a00b8050b200241c4036a20062003200241b0056a200528020c11868080800000024002400240024020022802c4032204418080808078470d00200241b0056a41086a2204200341086a290000370300200241b0056a41106a2207200341106a290000370300200241b0056a41186a2209200341186a290000370300200220032900003703b0052001280210280200210341002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b200241b0056a20022802c803220720022802cc03220110c28c80800020022802b00522094105470d01200241e8016a410c6a200241b0056a410c6a290200370200200220022902b4053702ec010c020b200120022903b005370204200141858080807841848080807820031b3602002001410c6a2004290300370200200141146a20072903003702002001411c6a200929030037020020004108360200200020013602040c070b200241a8056a2208200241b0056a410c6a290200370300200220022902b4053703a005200241c4036a41146a200241b0056a41146a41c80110848e8080001a200241c4036a410c6a2008290300370200200220093602c403200220022903a0053702c803200241e8016a200241c4036a2007200110f48d80800020022802e8014105470d050b200241b0056a41086a2209200241f4016a290200370300200241b0056a41186a2208200341086a290000370300200241b0056a41206a2205200341106a290000370300200241b0056a41286a2206200341186a290000370300200220022902ec013703b005200220032900003703c00541002d00fca3c680001a413041002802c8a3c68000118180808000002201450d03200120022903b005370200200141286a2006290300370200200141206a2005290300370200200141186a2008290300370200200141106a200241b0056a41106a290300370200200141086a200929030037020020004108360200200020013602042004450d05200741002802c0a3c68000118080808000000c050b2004200941f492c68000109581808000000b20042009418493c68000109581808000000b20042009419493c6800010f980808000000b4104413010b280808000000b2002410c6a200241e8016a41dc0110848e8080001a20002002410c6a10b68a8080002004450d00200741002802c0a3c68000118080808000000b20024190076a2480808080000bff0401067f23808080800041306b22022480808080002001280204210320012802002204280200210520042802042104200241106a41086a22062001280208220141086a28020036020020022001290200370310200241046a20052003200241106a200428020c1186808080000002400240024002400240024020022802042205418080808078470d00200241106a41186a2201200341186a290000370300200241106a41106a2204200341106a2900003703002006200341086a2900003703002002200329000037031041002d00fca3c680001a413041002802c8a3c680001181808080000022030d014104413010b280808000000b200228020c2201417f4c0d02200141f5ffffff074f0d0320022802082106024002402001410b6a417c7122070d00410421040c010b41002d00fca3c680001a200741002802c8a3c68000118180808000002204450d050b2004428180808010370200200441086a2006200110848e8080001a02402005450d00200641002802c0a3c68000118080808000000b2000200136020820002004360204200041073602002000200329000037000c200041246a200341186a2900003700002000411c6a200341106a290000370000200041146a200341086a2900003700000c010b2003418580808078360200200320022903103702042003410c6a200241106a41086a290300370200200341146a20042903003702002003411c6a200129030037020020004108360200200020033602040b200241306a2480808080000f0b41bc96c68000412b200241106a41e896c6800041e897c68000108981808000000b41e484c08000412b200241106a419085c08000419086c08000108981808000000b4104200710b280808000000b02000be309020b7f017e23808080800041f0016b22032480808080002003410036022c2003200236022820032001360224200341306a200341246a10978780800002400240024002400240024002400240024020032d003022044105460d00200328023421052004417d6a210620040e050102030203010b2000428580808090808080807f3702000c070b200041003602000c060b20032d00312107200328022c210820054101712209450d04200820024f0d01200120086a2d00004110490d042000428580808080808080807f3702000c050b200328022c210420054101712208450d02200420024f0d01200120046a2d00004110490d022000428580808080808080807f3702000c040b2008200241d4f7c5800010f980808000000b2004200241f4f7c5800010f980808000000b024002400240024002402004200541016a4101766a2202200328022822014b0d002003200236022c20064102490d02200341186a200341246a10bb8a80800020032802180d0141002105200328022c2206200328021c6a220a20032802284d0d032000428580808090808080807f3702000c060b2000428580808090808080807f3702000c050b2000428580808090808080807f3702000c040b4101210520022106200241206a220a20014b0d010b2000200a36021820002006360214200020053602102000200836020c2000200236020820002004360204200041013602000c020b2000428580808090808080807f3702000c010b024002402008200541016a4101766a22052003280228220a4b0d00200541026a220b200a4d0d012000428580808090808080807f3702000c020b2000428580808090808080807f3702000c010b2003200b36022c0240024002402005417d4b0d00200b20024b0d01200120056a2f0000220c450d024102210d02400240024002402004410147200772410171450d000240024020064102490d00200341106a200341246a10bb8a80800020032802100d034100210d200328022c220b20032802146a220220032802284d0d012000428580808090808080807f3702000c090b4101210d200541226a2202200a4b0d030b2003200236022c2002ad422086200bad84210e0b200341023602e401200341023602d801200341023602cc01200341023602c001200341023602b401200341023602a8012003410236029c0120034102360290012003410236028401200341023602782003410236026c2003410236026020034102360254200341023602482003410236023c2003410236023041002101200341306a210203400240200c200141ffff037176410171450d00200341086a200341246a10bb8a80800020032802080d04200328022c2206200328020c220a6a220420032802284b0d04200241086a2004360200200241046a20063602002002200a4120473602002003200436022c0b2002410c6a2102200141016a22014110470d000b2000411c6a200341306a41c00110848e8080001a2000200e3702142000200d3602102000200936020c2000200536020820002008360204200041043602000c060b2000428580808090808080807f3702000c050b2000428580808090808080807f3702000c040b2000428580808090808080807f3702000c030b2005200b41e4f7c58000109681808000000b200b200241e4f7c58000109581808000000b2000428580808090808080807f3702000b200341f0016a2480808080000ba00701027f2380808080004180016b220b248080808000200b2008360214200b200736021002400240024020012802002208450d00200b2008360218200b2001280204220736021c02402009450d00200b41c8006a41086a2004360200200b200336024c200b4184808080783602482009200b41c8006a200a28020c118480808000000b20082008280200220941016a3602002009417f4c0d0120002007360204200020083602002000200141086a2201290200370208200041206a200141186a290200370200200041186a200141106a290200370200200041106a200141086a290200370200200b280218220020002802002200417f6a36020020004101470d02200b41186a10e28a8080000c020b200b41186a41186a2001411c6a2208290000370300200b41186a41106a200141146a2207290000370300200b41186a41086a2001410c6a220c290000370300200b2001290004370318200b41c8006a41186a2008290000370300200b41c8006a41106a2007290000370300200b41c8006a41086a200c290000370300200b2001290004370348200b2002360244200b200b41186a360240200b200b41106a36023c200b41086a2005200b41c8006a200b413c6a41b4f2c58000200628021411878080800000200b28020c21010240200b280208450d0020004100360200200020013602040c020b024002400240024002400240024020012802002207417e6a2208410420084106491b0e06050005020103050b20012802342208450d04200141346a21070c050b2007450d0320012802042208450d03200141046a21070c040b20012802040d010c020b200141046a2107200128020421080c020b200141086a220728020022080d010b41c8f2c5800041ec0041b4f3c5800010a181808000000b200741046a280200210120082008280200220741016a3602002007417f4c0d0002402009450d00200b41c8006a41106a2004360200200b41f4006a200b41306a290300370200200b41ec006a200b41186a41106a290300370200200b41e4006a200b41186a41086a290300370200200b200b29031837025c200b2003360254200b2001360250200b418080808078360248200b200841086a36024c2009200b41c8006a200a28020c118480808000000b2000200b2903183700082000200136020420002008360200200041206a200b41186a41186a290300370000200041186a200b41186a41106a290300370000200041106a200b41206a2903003700000c010b00000b200b4180016a2480808080000bff0401067f23808080800041306b22022480808080002001280204210320012802002204280200210520042802042104200241106a41086a22062001280208220141086a28020036020020022001290200370310200241046a20052003200241106a200428020c1186808080000002400240024002400240024020022802042205418080808078470d00200241106a41186a2201200341186a290000370300200241106a41106a2204200341106a2900003703002006200341086a2900003703002002200329000037031041002d00fca3c680001a413041002802c8a3c680001181808080000022030d014104413010b280808000000b200228020c2201417f4c0d02200141f5ffffff074f0d0320022802082106024002402001410b6a417c7122070d00410421040c010b41002d00fca3c680001a200741002802c8a3c68000118180808000002204450d050b2004428180808010370200200441086a2006200110848e8080001a02402005450d00200641002802c0a3c68000118080808000000b2000200136020820002004360204200041073602002000200329000037000c200041246a200341186a2900003700002000411c6a200341106a290000370000200041146a200341086a2900003700000c010b2003418580808078360200200320022903103702042003410c6a200241106a41086a290300370200200341146a20042903003702002003411c6a200129030037020020004108360200200020033602040b200241306a2480808080000f0b41bc96c68000412b200241106a41e896c6800041e897c68000108981808000000b41e484c08000412b200241106a419085c08000419086c08000108981808000000b4104200710b280808000000bc91e04047f017e0a7f027e23808080800041b0026b2206248080808000200641d0006a41086a200141086a22072802003602002006200129020037035020022802002108200228020421092006200336025c200641e0006a41086a2007280200220236020020062001290200220a370360200641f0006a41186a220b200341206a290000370300200641f0006a41106a220c200341186a290000370300200641f0006a41086a220d200341106a290000370300200620032900083703704100210e20064100360290012006280264220f4101742107200641e0016a41086a2110200aa72111200528021421120240024003402006200e36029401200641e0016a41186a200b290300370300200641e0016a41106a200c2903003703002010200d290300370300200620062903703703e001200620064194016a3602c801200620064190016a3602c4012006200641d0006a3602c0012006200641f0006a3602bc012006200641dc006a3602b801200641086a2004200641e0016a200641b8016a41b8f4c58000201211878080800000200628020c210320062802080d01200e41016a210e0240200628025c22012802302213450d00200141346a2802002101201041186a200b290300370000201041106a200c290300370000201041086a200d29030037000020102006290370370000200620033602e40120064181808080783602e0012013200641e0016a200128020c118480808000000b03400240024002400240024002400240024002400240024002400240024002402003280200417e6a2201410420014106491b0e06000203040501000b200628025c22032802302202450d07200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c070b200641ec016a4201370200200641013602e401200641f8f4c580003602e001200641e0818080003602bc01200641c4f6c580003602b8012006200641b8016a3602e801200641e0016a41ccf6c5800010f680808000000b0240200641e0006a200341046a10df8d8080000d00200628025c22032802302202450d06200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c060b0240024020032802342202450d002003280238210f20022002280200220141016a3602002001417f4c0d0b200641b0016a200341d4006a290000370300200641a8016a200341cc006a290000370300200641a0016a200341c4006a29000037030020062003413c6a290000370398010c010b200641a0016a200341c4006a290000370300200641a8016a200341cc006a290000370300200641b0016a200341d4006a28000036020020062003413c6a290000370398012003280038210f0b200628025c22032802002101200328020421072006290350210a20064188026a200f3602002006418c026a200629039801370200200641e0016a41346a20064198016a41086a2903003702002006419c026a20064198016a41106a290300370200200641a4026a20064198016a41186a290300370200200641e0016a41206a2007360200200641e0016a41186a2005360200200641e0016a41106a200936020020062002360284022006200341306a3602ac02200620013602fc01200620043602f401200620083602ec0141002102200641003a00e8012006200a3702e001200641b8016a20064184026a200641e0016a2008200920042005200120072003280230200341346a28020010c38c80800020062802b8012203450d03200641306a41086a200641b8016a41106a290200370300200641306a41106a200641b8016a41186a290200370300200641306a41186a200641b8016a41206a290200370300200620062902c00137033020062802bc01210f0c070b0240200641e0006a200341286a10de8d8080000d00200628025c22032802302202450d05200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c050b20062002200341d4006a28020022016a2202360268200620012006280290016a36029001200341046a21030c0a0b02400240024020072002460d0020024101762201200f4f0d012003201120016a2d00002201410f71200141047620024101711b41246c6a41306a22032d00004102470d02200628025c22032802302202450d06200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c060b02402003280204450d0002400240200341086a2802002202450d00200328020c210120022002280200220741016a3602002007417f4c0d0c200641b8016a41186a200341286a290000370300200641b8016a41106a200341206a290000370300200641c0016a200341186a2900003703002006200341106a2900003703b8010c010b200328000c2101200641b8016a41186a200341286a280000360200200641b8016a41106a200341206a290000370300200641b8016a41086a200341186a2900003703002006200341106a2900003703b8010b2006418c026a20062903b801370200200641a4026a200641b8016a41186a220f2903003702002006419c026a200641b8016a41106a2211290300370200200641e0016a41346a200641b8016a41086a29030037020020064188026a2001360200200641e0016a41186a2005360200200641e0016a41106a2009360200200641e0016a41206a200628025c220328020422013602002006200236028402200620043602f401200620083602ec0141002102200641003a00e801200620062903503702e0012006200341306a3602ac022006200328020022073602fc01200641b8016a20064184026a200641e0016a2008200920042005200720012003280230200341346a28020010c38c80800020062802b8012203450d04200641306a41086a2011290200370300200641306a41106a200f290200370300200641306a41186a200641b8016a41206a290200370300200620062902c00137033020062802bc01210f0c080b200628025c22032802302202450d06200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c060b2001200f41a8f4c5800010f980808000000b2006200241016a2202360268200620062802900141016a360290010c090b0240200641e0006a200341ec046a10de8d8080000d00200628025c22032802302202450d03200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c030b0240200720026b20034198056a2802002201460d00200120026a22144101762213200f4f0d0202402003201120136a2d00002213410f71201341047620144101711b41246c6a412c6a22032d00004102470d00200628025c22032802302202450d04200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c040b2006200141016a220120026a2202360268200620062802900120016a360290010c090b02402003280200450d000240024020032802042202450d002003280208210120022002280200220741016a3602002007417f4c0d09200641d0016a200341246a290000370300200641c8016a2003411c6a290000370300200641c0016a200341146a29000037030020062003410c6a2900003703b8010c010b20032800082101200641d0016a200341246a280000360200200641c8016a2003411c6a290000370300200641c0016a200341146a29000037030020062003410c6a2900003703b8010b2006418c026a20062903b801370200200641a4026a200641b8016a41186a220f2903003702002006419c026a200641b8016a41106a2211290300370200200641e0016a41346a200641b8016a41086a29030037020020064188026a2001360200200641e0016a41186a2005360200200641e0016a41106a2009360200200641e0016a41206a200628025c220328020422013602002006200236028402200620043602f401200620083602ec0141002102200641003a00e801200620062903503702e0012006200341306a3602ac022006200328020022073602fc01200641b8016a20064184026a200641e0016a2008200920042005200720012003280230200341346a28020010c38c80800020062802b8012203450d01200641306a41086a2011290200370300200641306a41106a200f290200370300200641306a41186a200641b8016a41206a290200370300200620062902c00137033020062802bc01210f0c050b200628025c22032802302202450d03200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c030b4101210220062802bc0121030c030b2013200f41a8f4c5800010f980808000000b41002102410021030c020b41002103410021020b20020d06200641106a41186a200641306a41186a290300370300200641106a41106a200641306a41106a290300370300200641106a41086a200641306a41086a29030037030020062006290330370310024020030d0041002102410021030c010b20032003280200220241016a3602002002417f4c0d01200641f9016a200641286a290300370000200641f1016a200641206a290300370000200641e9016a200641186a290300370000200620062903103700e1012006200f3602bc01200620033602b80103402003280204210203402002417f460d012002417f4c0d042003200241016a2003280204220120012002461b360204200120024721072001210220070d000b0b20062802bc01210220062802b801220120012802002201417f6a360200024020014101470d00200641b8016a10f18d8080000b20064188026a20023602002006200336028402410221020b200620023a00e001200420082009200641e0016a200528021011868080800000200041086a200f36020020002003360204200041003602000c060b00000b10f08d808000000b024020032d00000d00200341096a290000210a200341116a290000211520032900012116200b200341196a290000370300200c2015370300200d200a370300200620163703700c020b200328020421030c000b0b0b20004101360200200020033602040b200641b0026a2480808080000b832a05237f017e017f017e017f23808080800041d0076b2205248080808000200128022821062001410036022802400240024002400240024020060d00200541086a41306a2207200141306a290200370300200541086a41286a200141286a290200370300200541086a41206a2208200141206a290200370300200541086a41186a2206200141186a290200370300200541086a41106a2209200141106a290200370300200541086a41086a220a200141086a29020037030020052001290200370308200541c0006a41086a200441086a2201280200220b36020020052004290200370340200541d0006a41186a220c2008290300370300200541d0006a41106a220d2006290300370300200541d0006a41086a220e20092903003703002005200a2903003703502001280200210f200541d0006a4107722110200541f0056a41186a2111200541f0056a410c6a211220054190016a2113200541f4006a41046a210620054184046a41046a210920054184046a41146a2114200541f0056a41146a2115200541f0056a41046a2116200541f0056a41106a211720042802042118200428020021192005413c6a280200211a2007280200211b200528020c211c2005280208211d4100211e4100210a02400240024002400340200a200f6a220841017621010240024020084101710d00200120184b0d06200541003a00f805200520013602f405200520193602f0050c010b200120184b0d04200120184f0d03200541013a00f805200520193602f005200520013602f4052005201920016a2d000041f001713a00f9050b20054184046a201d200541d0006a200541f0056a201c28020c118680808000000240200528028404221f418080808078470d00200541f0056a41186a2208200541d0006a41186a290300370300200541f0056a41106a220b200541d0006a41106a290300370300200541f0056a41086a2204200541d0006a41086a290300370300200520052903503703f00541002d00fca3c680001a413041002802c8a3c680001181808080000022010d0a4104413010b280808000000b200528028c04210120052802880421200240201b450d0020172005290350370000201741186a200c290300370000201741106a200d290300370000201741086a200e290300370000200520013602fc05200520203602f8052005428280808088808080807f3702f005201b200541f0056a201a28020c118480808000000b200541f0056a2020200110c28c808000200541e0056a41086a2221201641086a2222290200370300200520162902003703e005024020052802f00522044105460d00201e41016a211e200528024422234101742124200528024021252020210803402014201541c80110848e8080001a200941086a2021290300370200200920052903e0053702002005200436028404200541f4006a20054184046a2008200110f48d808000200528027422264105460d08200541f8036a41086a2227200641086a280200360200200520062902003703f80320052802840121042005280288012108200528028c012101200541d0026a201341a80110848e8080001a20052902bc02212820052802b802212902400240024002400240024020260e050001020304000b0240201b450d00200528023c2101200541f8056a2003360200200520023602f40520054186808080783602f005201b200541f0056a200128020c118480808000000b20004180808080783602000c0e0b0240024020052802fc034101742005280280046b2024200b6b220b470d00200541f8036a200541c0006a10db8d808000200b460d010b0240201b450d00200528023c2101200541f8056a2003360200200520023602f40520054186808080783602f005201b200541f0056a200128020c118480808000000b20004180808080783602000c0e0b20054194066a201c3602002005418c066a200336020020054184066a41003a000020054180066a201836020020052007360298062005201d360290062005200236028806200520193602fc05200520013602f805200520083602f405200520043602f00520054184046a200541f0056a200541fc056a20022003201d201c200710bb8c8080000240200528028404418080808078460d002000200529028404370200200041086a20054184046a41086a2802003602000c0e0b200020052802880436020420004181808080783602000c0d0b20052802fc03212920052802800421270240200541c0006a200541f8036a10db8d8080002226202941017420276b460d000240201b450d00200528023c2101200541f8056a2003360200200520023602f40520054186808080783602f005201b200541f0056a200128020c118480808000000b20004180808080783602000c0d0b2005200b20266a220b3602480c020b200541f0056a41086a2027280200360200200520052903f8033703f00520052001360284062005200836028006200520043602fc052011200541d0026a41a80110848e8080001a0240024002402024200b460d00200b410176220120234f0d01200541f0056a202520016a2d00002201410f712001410476200b4101711b410c6c6a220828020022044102470d020240201b450d00200528023c21012005418c046a20033602002005200236028804200541868080807836028404201b20054184046a200128020c118480808000000b20004180808080783602000c0e0b024020294102460d00200541a8046a201c360200200541a0046a200336020020054198046a41003a000020054194046a2018360200200520073602ac042005201d3602a4042005200236029c04200520193602900420052028370288042005202936028404200541f4006a20054184046a20054190046a20022003201d201c200710bb8c80800002402005280274418080808078460d0020002005290274370200200041086a200541f4006a41086a2802003602000c0f0b2000200528027836020420004181808080783602000c0e0b0240201b450d00200528023c21012005418c046a20033602002005200236028804200541868080807836028404201b20054184046a200128020c118480808000000b20004180808080783602000c0d0b2001202341a8f4c5800010f980808000000b410121262005200b41016a220b36024820082802082101200828020421080c010b20052902c802212a20052802c402212b200528028004212620052802fc032127200520013602f805200520083602f405200520043602f0052012200541d0026a41a80110848e8080001a200520283702a807200520293602a4070240200541c0006a200541f8036a10db8d8080002201202741017420266b460d000240201b450d00200528023c21012005418c046a20033602002005200236028804200541868080807836028404201b20054184046a200128020c118480808000000b20004180808080783602000c0b0b024002402024200b6b2001460d00200b20016a2204410176220820234f0d010240200541f0056a202520086a2d00002208410f71200841047620044101711b410c6c6a220828020022044102470d000240201b450d00200528023c21012005418c046a20033602002005200236028804200541868080807836028404201b20054184046a200128020c118480808000000b20004180808080783602000c0d0b2005200b200141016a22266a220b36024820082802082101200828020421080c020b0240202b4102460d00200541a8046a201c360200200541a0046a200336020020054198046a41003a000020054194046a2018360200200520073602ac042005201d3602a4042005200236029c0420052019360290042005202a370288042005202b36028404200541f4006a20054184046a20054190046a20022003201d201c200710bb8c80800002402005280274418080808078460d0020002005290274370200200041086a200541f4006a41086a2802003602000c0d0b2000200528027836020420004181808080783602000c0c0b0240201b450d00200528023c21012005418c046a20033602002005200236028804200541868080807836028404201b20054184046a200128020c118480808000000b20004180808080783602000c0b0b2008202341a8f4c5800010f980808000000b2026200a6a210a024020040d0020014120470d04200841026a2d000021012008410f6a2900002128200841176a290000212a2008411f6a2d0000210420082f000021262008280003212120102008290007370000201041186a20043a0000201041106a202a370000201041086a202837000020052021360053200520013a0052200520263b0150201f450d03202041002802c0a3c68000118080808000000c030b200541f0056a2008200110c28c80800020212022290200370300200520162902003703e00520052802f00522044105470d000b0b0b200620052903e005370200200641086a200541e0056a41086a2903003702000c050b2005200141001096868080002005280200210b20052802042008200110848e808000210441002d00fca3c680001a0240413041002802c8a3c68000118180808000002208450d002008200b36020420084188808080783602002008200136020c2008200436020820002008360204200041818080807836020020082005290350370010200841186a200541d8006a290300370000200841206a200541e0006a290300370000200841286a200541d0006a41186a2903003700000c060b4104413010b280808000000b20012018419493c6800010f980808000000b20012018418493c68000109581808000000b2001201841f492c68000109581808000000b2001412c6a280200210a200541f0056a41306a200141306a2902002228370300200541f0056a41286a200141286a290200370300200541f0056a41206a200141206a290200370300200541f0056a41186a200141186a290200370300200541f0056a41106a200141106a290200370300200541f0056a41086a200141086a290200370300200520012902003703f0052005200336025420052002360250410121160240024002400240024002400240024002400240024002400240024002400240024002402028a72221450d000240202120022003200541a4066a2802002802101182808080000041ff01710e03010002010b410021160b200620022003200a28020c1182808080000022260d010b20054184046a2004200541d0006a200541f0056a2006200a10c58c80800020052802880421082005280284040d012005418c046a28020021090c090b418080808078210920262d00000e030c02010c0b20004181808080783602000c030b20262802242208417f460d01202641016a21292008280200210103402001450d022001417f4c0d042008200141016a2008280200220b200b20014622091b360200200b21012009450d000b201620262802282209412149720d0702402021450d00200541a4066a280200210120054184046a41106a2003360200200541b0046a202941186a290000370200200541a8046a202941106a290000370200200541a0046a202941086a29000037020020052002360290042005200936028c042005200841086a360288042005418080808078360284042005202929000037029804202120054184046a200128020c118480808000000b20052009360288042005200836028404200841086a21010c080b200541a0046a202641196a29000037020020054198046a202641116a29000037020020054190046a202641096a29000037020020054100360284042005202629000137028804200541003a001020052004290200370208200541f4006a20054184046a200541086a200220032006200a20052802f00520052802f4052021200541a4066a28020010c38c808000024020052802742208450d00200541d8026a220b200541f4006a41106a290200370300200541d0026a41106a2204200541f4006a41186a290200370300200541d0026a41186a222620054194016a2902003703002005200529027c3703d0022005280278210920082008280200220141016a3602002001417f4c0d042005419d046a202629030037000020054195046a20042903003700002005418d046a200b290300370000200520052903d00237008504200520093602782005200836027403402008280204210103402001417f460d012001417f4c0d072008200141016a2008280204220b200b2001461b360204200b2001472104200b210120040d000b0b200528027821012005280274220b200b280200220b417f6a3602000240200b4101470d00200541f4006a10f18d8080000b200541ac046a2001360200200520083602a804200541023a00840420062002200320054184046a200a280210118680808000000c070b200528027821012000418180808078360200200020013602040c100b20054184046a2004200541d0006a200541f0056a2006200a10c58c808000200528028804210802402005280284040d002005418c046a28020021090c050b20004181808080783602000b200020083602040c0e0b10f28d808000000b00000b10f08d808000000b20080d0041808080807821090c030b20052009360288042005200836028404200841086a210120090d004101210b410021090c010b2009417f4c0d0241002d00fca3c680001a200941002802c8a3c6800011818080800000220b450d030b200b2001200910848e808000210120082008280200220b417f6a3602000240200b4101470d0020054184046a10e28a8080000b2009ad4220862001ad8421280b20002028370204200020093602000c060b10ae80808000000b4101200910b280808000000b200541f0056a41086a2208200641086a290200370300200541f0056a41186a220b200541d0006a41086a290300370300200541f0056a41206a2204200541d0006a41106a290300370300200541f0056a41286a2209200541d0006a41186a2903003703002005200529035037038006200520062902003703f00541002d00fca3c680001a413041002802c8a3c68000118180808000002201450d01200120052903f005370200200141286a2009290300370200200141206a2004290300370200200141186a200b290300370200200141106a200541f0056a41106a290300370200200141086a20082903003702002000418180808078360200200020013602040b201f450d02202041002802c0a3c68000118080808000000c020b4104413010b280808000000b200120052903f0053702042001418580808078418480808078201e1b3602002001410c6a2004290300370200200141146a200b2903003702002001411c6a20082903003702002000418180808078360200200020013602040b200541d0076a2480808080000bcf0101037f23808080800041206b220224808080800002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020002400240024020042d00000e020102000b20004181808080783602000c030b20004180808080783602000c020b200241086a200110bc8a80800020022802080d00200241146a2001200228020c10d08580800020022802142203418080808078460d0020002002290218370204200020033602000c010b20004181808080783602000b200241206a2480808080000bd80101027f23808080800041106b22022480808080000240024020002903004200520d000240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41003a00000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41013a00002002200041086a360208200241086a200110c18a8080002002200041106a36020c2002410c6a200110c18a8080000b200241106a2480808080000b9e0201037f23808080800041106b2202248080808000024002402000280200418080808078470d000240200128020020012802082200470d0020012000410110b182808000200128020821000b200128020420006a41003a0000200041016a21000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41013a0000200028020421042002200028020822003602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822036b20004f0d0020012003200010b182808000200128020821030b200128020420036a2004200010848e8080001a200320006a21000b20012000360208200241106a2480808080000b4d01017f23808080800041206b2203248080808000200341106a420037020020034101360208200341bcf7c5800036020420032003411c6a36020c200341046a41c4f7c5800010f680808000000b4d01017f23808080800041206b2204248080808000200441106a420037020020044101360208200441bcf7c5800036020420042004411c6a36020c200441046a4184f8c5800010f680808000000bcd0e01117f23808080800041f0006b22052480808080000240024002400240024002400240024020042802002206417f6a0e020200010b200241017641046a2207417f4c0d0241002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a22094100360200200520083602442005200736024020052002360210200541013b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c060b200241017641046a2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a2209410036020020052008360244200520073602402005200236021020054181023b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c050b200241017641046a2207417f4c0d0041002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a220941003602002005200836024420052007360240200541033a000c200520023602102005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c040b10ae80808000000b4101200710b280808000000b4101200710b280808000000b4101200710b280808000000b2005280208220a21020240200a2005280200470d002005200a109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024020022005280200470d0020052002109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024002400240024020060e03000103000b200428020421012005200441086a280200220236020c20052005410c6a360240200541c0006a200510c08a80800002402005280200200528020822046b20024f0d0020052004200210ab86808000200528020821040b200528020420046a2001200210848e8080001a200420026a21020c010b200428020421010240200528020020026b200441086a28020022044f0d0020052002200410ab86808000200528020821020b200528020420026a2001200410848e8080001a200220046a21020b200520023602080b02400240024002400240200328020022022003280204220b470d0041002106410021030c010b2003280210210c20032802082104200328020c220841046a210d2005410c6a41086a21092005410c6a410172210e41002106200541c0006a410172220f411f6a2110200f41186a2111200f41106a21124101210102400240034020022d00002103200241023a000020034103460d0241002107024020034102460d00200f20022900013700002010200241206a2800003600002011200241196a2900003700002012200241116a290000370000200f41086a200241096a29000037000020082802042113200828020821142005200828020036023c20052014200828022c2215201541284b22151b36023820052013200d20151b360234200520043a0069200541013a0068200520033a0040200c41046a28020021032005200541346a3602642005410c6a200c2802002003200541c0006a200541346a4101200410d98580800020052d000c22034103460d022005280210211302400240024020030e03000103010b200541203602342005200541346a360240200541c0006a200510c08a80800002402005280200200528020822076b411f4b0d0020052007412010b182808000200528020821070b200528020420076a220320092900003700072003200e2f00003b0000200341026a200e41026a2d00003a0000200320133600032003410f6a200941086a290000370000200341176a200941106a2900003700002003411f6a200941186a2d00003a00002005200741206a3602080c010b200541c0006a41186a200941186a290000370300200541c0006a41106a200941106a290000370300200541c0006a41086a200941086a29000037030020052009290000370340201341214f0d062005201336026c2005200541ec006a360234200541346a200510c08a80800002402005280200200528020822036b20134f0d0020052003201310b182808000200528020821030b200528020420036a200541c0006a201310848e8080001a2005200320136a3602080b200121070b200441016a21042001410174210120072006722106200241246a2202200b470d000b20064180fe037141087621030c020b20064180fe037141087621030c010b20064180fe037141087621030b200a41026a2102200a417d4b0d012002200528020822044b0d022005280204200a6a220220033a0001200220063a000020002005290300370200200041086a200541086a280200360200200541f0006a2480808080000f0b2013412041a4f8c58000109581808000000b200a20024194f8c58000109681808000000b200220044194f8c58000109581808000000bcd0e01117f23808080800041f0006b22052480808080000240024002400240024002400240024020042802002206417f6a0e020200010b200241017641046a2207417f4c0d0241002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a22094100360200200520083602442005200736024020052002360210200541013b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c060b200241017641046a2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a2209410036020020052008360244200520073602402005200236021020054181023b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c050b200241017641046a2207417f4c0d0041002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a220941003602002005200836024420052007360240200541033a000c200520023602102005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c040b10ae80808000000b4101200710b280808000000b4101200710b280808000000b4101200710b280808000000b2005280208220a21020240200a2005280200470d002005200a109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024020022005280200470d0020052002109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024002400240024020060e03000103000b200428020421012005200441086a280200220236020c20052005410c6a360240200541c0006a200510c08a80800002402005280200200528020822046b20024f0d0020052004200210ab86808000200528020821040b200528020420046a2001200210848e8080001a200420026a21020c010b200428020421010240200528020020026b200441086a28020022044f0d0020052002200410ab86808000200528020821020b200528020420026a2001200410848e8080001a200220046a21020b200520023602080b02400240024002400240200328020022022003280204220b470d0041002106410021030c010b2003280210210c20032802082104200328020c220841046a210d2005410c6a41086a21092005410c6a410172210e41002106200541c0006a410172220f411f6a2110200f41186a2111200f41106a21124101210102400240034020022d00002103200241023a000020034103460d0241002107024020034102460d00200f20022900013700002010200241206a2800003600002011200241196a2900003700002012200241116a290000370000200f41086a200241096a29000037000020082802042113200828020821142005200828020036023c20052014200828022c2215201541284b22151b36023820052013200d20151b360234200520043a0069200541013a0068200520033a0040200c41046a28020021032005200541346a3602642005410c6a200c2802002003200541c0006a200541346a4101200410db8580800020052d000c22034103460d022005280210211302400240024020030e03000103010b200541203602342005200541346a360240200541c0006a200510c08a80800002402005280200200528020822076b411f4b0d0020052007412010b182808000200528020821070b200528020420076a220320092900003700072003200e2f00003b0000200341026a200e41026a2d00003a0000200320133600032003410f6a200941086a290000370000200341176a200941106a2900003700002003411f6a200941186a2d00003a00002005200741206a3602080c010b200541c0006a41186a200941186a290000370300200541c0006a41106a200941106a290000370300200541c0006a41086a200941086a29000037030020052009290000370340201341214f0d062005201336026c2005200541ec006a360234200541346a200510c08a80800002402005280200200528020822036b20134f0d0020052003201310b182808000200528020821030b200528020420036a200541c0006a201310848e8080001a2005200320136a3602080b200121070b200441016a21042001410174210120072006722106200241246a2202200b470d000b20064180fe037141087621030c020b20064180fe037141087621030c010b20064180fe037141087621030b200a41026a2102200a417d4b0d012002200528020822044b0d022005280204200a6a220220033a0001200220063a000020002005290300370200200041086a200541086a280200360200200541f0006a2480808080000f0b2013412041a4f8c58000109581808000000b200a20024194f8c58000109681808000000b200220044194f8c58000109581808000000bbe0e01117f23808080800041f0006b22052480808080000240024002400240024002400240024020042802002206417f6a0e020200010b200241017641046a2207417f4c0d0241002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a22094100360200200520083602442005200736024020052002360210200541013b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c060b200241017641046a2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a2209410036020020052008360244200520073602402005200236021020054181023b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c050b200241017641046a2207417f4c0d0041002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a220941003602002005200836024420052007360240200541033a000c200520023602102005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c040b10ae80808000000b4101200710b280808000000b4101200710b280808000000b4101200710b280808000000b2005280208220a21020240200a2005280200470d002005200a109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024020022005280200470d0020052002109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024002400240024020060e03000103000b200428020421012005200441086a280200220236020c20052005410c6a360240200541c0006a200510c08a80800002402005280200200528020822046b20024f0d0020052004200210ab86808000200528020821040b200528020420046a2001200210848e8080001a200420026a21020c010b200428020421010240200528020020026b200441086a28020022044f0d0020052002200410ab86808000200528020821020b200528020420026a2001200410848e8080001a200220046a21020b200520023602080b02400240024002400240200328020022022003280204220b470d0041002106410021030c010b2003280210210c20032802082104200328020c220841046a210d2005410c6a41086a21092005410c6a410172210e41002106200541c0006a410172220f411f6a2110200f41186a2111200f41106a21124101210102400240034020022d00002103200241023a000020034103460d0241002107024020034102460d00200f20022900013700002010200241206a2800003600002011200241196a2900003700002012200241116a290000370000200f41086a200241096a29000037000020082802042113200828020821142005200828020036023c20052014200828022c2215201541284b22151b36023820052013200d20151b360234200520043a0069200541013a0068200520033a00402005200541346a3602642005410c6a200c200541c0006a200541346a4101200410dc8580800020052d000c22034103460d022005280210211302400240024020030e03000103010b200541203602342005200541346a360240200541c0006a200510c08a80800002402005280200200528020822076b411f4b0d0020052007412010b182808000200528020821070b200528020420076a220320092900003700072003200e2f00003b0000200341026a200e41026a2d00003a0000200320133600032003410f6a200941086a290000370000200341176a200941106a2900003700002003411f6a200941186a2d00003a00002005200741206a3602080c010b200541c0006a41186a200941186a290000370300200541c0006a41106a200941106a290000370300200541c0006a41086a200941086a29000037030020052009290000370340201341214f0d062005201336026c2005200541ec006a360234200541346a200510c08a80800002402005280200200528020822036b20134f0d0020052003201310b182808000200528020821030b200528020420036a200541c0006a201310848e8080001a2005200320136a3602080b200121070b200441016a21042001410174210120072006722106200241246a2202200b470d000b20064180fe037141087621030c020b20064180fe037141087621030c010b20064180fe037141087621030b200a41026a2102200a417d4b0d012002200528020822044b0d022005280204200a6a220220033a0001200220063a000020002005290300370200200041086a200541086a280200360200200541f0006a2480808080000f0b2013412041a4f8c58000109581808000000b200a20024194f8c58000109681808000000b200220044194f8c58000109581808000000bbe0e01117f23808080800041f0006b22052480808080000240024002400240024002400240024020042802002206417f6a0e020200010b200241017641046a2207417f4c0d0241002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a22094100360200200520083602442005200736024020052002360210200541013b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c060b200241017641046a2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a2209410036020020052008360244200520073602402005200236021020054181023b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c050b200241017641046a2207417f4c0d0041002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a220941003602002005200836024420052007360240200541033a000c200520023602102005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c040b10ae80808000000b4101200710b280808000000b4101200710b280808000000b4101200710b280808000000b2005280208220a21020240200a2005280200470d002005200a109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024020022005280200470d0020052002109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024002400240024020060e03000103000b200428020421012005200441086a280200220236020c20052005410c6a360240200541c0006a200510c08a80800002402005280200200528020822046b20024f0d0020052004200210ab86808000200528020821040b200528020420046a2001200210848e8080001a200420026a21020c010b200428020421010240200528020020026b200441086a28020022044f0d0020052002200410ab86808000200528020821020b200528020420026a2001200410848e8080001a200220046a21020b200520023602080b02400240024002400240200328020022022003280204220b470d0041002106410021030c010b2003280210210c20032802082104200328020c220841046a210d2005410c6a41086a21092005410c6a410172210e41002106200541c0006a410172220f411f6a2110200f41186a2111200f41106a21124101210102400240034020022d00002103200241023a000020034103460d0241002107024020034102460d00200f20022900013700002010200241206a2800003600002011200241196a2900003700002012200241116a290000370000200f41086a200241096a29000037000020082802042113200828020821142005200828020036023c20052014200828022c2215201541284b22151b36023820052013200d20151b360234200520043a0069200541013a0068200520033a00402005200541346a3602642005410c6a200c200541c0006a200541346a4101200410da8580800020052d000c22034103460d022005280210211302400240024020030e03000103010b200541203602342005200541346a360240200541c0006a200510c08a80800002402005280200200528020822076b411f4b0d0020052007412010b182808000200528020821070b200528020420076a220320092900003700072003200e2f00003b0000200341026a200e41026a2d00003a0000200320133600032003410f6a200941086a290000370000200341176a200941106a2900003700002003411f6a200941186a2d00003a00002005200741206a3602080c010b200541c0006a41186a200941186a290000370300200541c0006a41106a200941106a290000370300200541c0006a41086a200941086a29000037030020052009290000370340201341214f0d062005201336026c2005200541ec006a360234200541346a200510c08a80800002402005280200200528020822036b20134f0d0020052003201310b182808000200528020821030b200528020420036a200541c0006a201310848e8080001a2005200320136a3602080b200121070b200441016a21042001410174210120072006722106200241246a2202200b470d000b20064180fe037141087621030c020b20064180fe037141087621030c010b20064180fe037141087621030b200a41026a2102200a417d4b0d012002200528020822044b0d022005280204200a6a220220033a0001200220063a000020002005290300370200200041086a200541086a280200360200200541f0006a2480808080000f0b2013412041a4f8c58000109581808000000b200a20024194f8c58000109681808000000b200220044194f8c58000109581808000000bc30402047f017e23808080800041206b2204248080808000200241017641046a21050240024002400240024020032802000d002005417f4c0d0241002d00fca3c680001a200541002802c8a3c68000118180808000002206450d03200441146a41086a220741003602002004200636021820042005360214200441023a0000200420023602042004200441146a109887808000200441146a2001109a87808000200441086a2202200728020036020020042004290214370300200328020421012004200328020822053602102004200441106a360214200441146a200410c08a80800002402004280200200228020022036b20054f0d0020042003200510ab86808000200428020821030b200428020420036a2001200510848e8080001a2004200320056a3602080c010b2005417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002206450d03200441146a41086a220741003602002004200636021820042005360214200441043a0000200420023602042004200441146a109887808000200441146a2001109a87808000200441086a200728020022053602002004200429021422083703002003280204210202402008a720056b200328020822034f0d0020042005200310ab86808000200428020821050b200428020420056a2002200310848e8080001a2004200520036a3602080b20002004290300370200200041086a200441086a280200360200200441206a2480808080000f0b10ae80808000000b4101200510b280808000000b4101200510b280808000000b930302037f017e23808080800041d0006b22022480808080002002200136020002404100280284a4c680004105470d002002419882808000360208200220023602044100280290a1c680002101410028028ca1c6800021034100280280a4c680002104200241c4006a42013702002002413c6a4101360200200241346a4115360200200241306a41dffbc58000360200200241246a41ecfac58000ad4280808080b00e84370200200241186a41f4fbc58000ad4280808080d00584370200200241c0006a200241046a360200200241e4fac580003602382002410536022c200241003602202002410036021420024281808080c01e37020c200341ecf2c08000200441024622041b2002410c6a200141d4f2c0800020041b28021011848080800000200228020021010b4101210342002105024002400240200128020041786a0e020001020b200129030821050c010b41002103420021050b200020033a0028200042043703202000420037031820004280808080c0003703102000427f37030820002005370300200241d0006a2480808080000be70201047f23808080800041c0006b2202248080808000200241186a41086a200141086a290200370300200220012902003703182002200241186a109e8780800020022802042101024002400240200228020822030d0002402002280200450d00200141002802c0a3c68000118080808000000b41002101410021040c010b20022002413f6a36021820012003200241186a10da8b8080002002280200210541002d00fca3c680001a41e40141002802c8a3c68000118180808000002204450d01200441003b01e20120044100360258200241003602102002200436020c2002410036021420022001200341146c6a36023820022005360234200220013602302002200136022c20024181808080783602202002410c6a200241186a200241146a10c18580800020002002280210360204200228020c2101200228021421040b2000200436020820002001360200200241c0006a2480808080000f0b410441e40110b280808000000ba90501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802e40121012005417f6a22050d000b0b20024108490d00034020012802e4012802e4012802e4012802e4012802e4012802e4012802e4012802e4012101200041786a22000d000b0b410021050b024002400240200520012f01e201490d00034020012802582200450d0220012f01e0012105200141002802c0a3c6800011808080800000200441016a210420002101200520002f01e2014f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a41e4016a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802e40121002004417f6a22040d000b0b20074107490d01034020002802e4012802e4012802e4012802e4012802e4012802e4012802e4012802e4012100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b024020012005410c6c6a41dc006a2201280200450d00200128020441002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802e40121002005417f6a22050d000b0b20024108490d00034020002802e4012802e4012802e4012802e4012802e4012802e4012802e4012802e4012100200141786a22010d000b0b034020002802582101200041002802c0a3c68000118080808000002001210020010d000b0b0bcd0501097f23808080800041106b22022480808080002002200028020822033602082002200241086a36020c2002410c6a200110c08a808000024002402003450d0020002802002204450d0041002105200441004721062000280204210703400240024002402006450d002005450d010b20060d0141f887c6800010a081808000000b410121062004210502402007450d0020072100024020074107712204450d0003402000417f6a210020052802e40121052004417f6a22040d000b0b20074108490d00034020052802e4012802e4012802e4012802e4012802e4012802e4012802e4012802e4012105200041786a22000d000b0b41002104410021070b0240200720052f01e201490d00034020052802582200450d04200441016a210420052f01e001210720002105200720002f01e2014f0d000b0b200741016a21080240024020040d00200521000c010b200520084102746a41e4016a2802002100410021082004417f6a2209450d002004417e6a210a024020094107712204450d0003402009417f6a210920002802e40121002004417f6a22040d000b0b200a4107490d00034020002802e4012802e4012802e4012802e4012802e4012802e4012802e4012802e4012100200941786a22090d000b0b200520074103746a210420052007410c6c6a41dc006a210702402001280200200128020822056b41074b0d0020012005410810b182808000200128020821050b200128020420056a20042900003700002001200541086a360208200728020421042002200728020822053602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822076b20054f0d0020012007200510b182808000200128020821070b200128020420076a2004200510848e8080001a2001200720056a3602084100210420082107200021052003417f6a22030d000b0b200241106a2480808080000f0b41e887c6800010a081808000000b0a00200010d68c8080000b850b04067f017e027f017e23808080800041d0006b2201248080808000200141286a419bfdc58000410641aafdc58000411b41fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242a5e9e3ab9e929adc2c370308200241cbfdc58000360220200241ef8080800036021820024106360204200241c5fdc58000360200200241106a4293888c8f89fdc6ec9e7f370300200241386a4100360200200241246a410436020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c2002420c370224200241dad9c480003602202002410b36021c200241e6d9c48000360218200241f581808000360210200242ab8bffbed784ffa5937f370308200242c194a6a793ccc3a857370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420637022420024186dac480003602202002410636021c20024180dac48000360218200241bc82808000360210200242899ac8f29d8ce69ac300370308200242e78dcee4d0becc97573703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c2002420c370224200241dad9c480003602202002410a36021c200241d0d9c48000360218200241f581808000360210200242ab8bffbed784ffa5937f370308200242c194a6a793ccc3a857370300200141c0006a41086a2208200141106a41086a220928020041016a220236020020012001290310220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420c370224200241dad9c480003602202002410f36021c200241f1d9c48000360218200241f581808000360210200242ab8bffbed784ffa5937f370308200242c194a6a793ccc3a8573703002009200828020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c2002420637022420024192dac480003602202002410636021c2002418cdac48000360218200241ca8280800036021020024296b787c192cbb889d000370308200242fddcc0b9c5fc86d6a17f370300200128021821082001280214210220012001280210360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b0a00200010f1878080000b0a00200010d98c8080000b8f0a04067f017e027f017e23808080800041d0006b2201248080808000200141286a41b087c68000411141c187c68000411241fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242f68d80b7cfa6d3bb4e370308200241d387c680003602202002418083808000360218200241063602042002419bfdc58000360200200241306a42e9b494c69bdbc4d608370300200241286a42ead283debcdebd93d800370300200241106a42dc8ec6fd9fd6fcdeb77f370300200241386a41f780808000360200200241246a410236020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024202370224200241d0fbc480003602202002410836021c200241c7a4c58000360218200241f780808000360210200242e9b494c69bdbc4d608370308200242ead283debcdebd93d800370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024204370224200241dfa4c580003602202002410436021c200241dba4c58000360218200241f580808000360210200242b8b6d386cdcbfab1a07f370308200242e3a4fae3cee3d18d723703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024206370224200241b2d9c480003602202002410c36021c200241cfa4c580003602182002418083808000360210200242dc8ec6fd9fd6fcdeb77f370308200242f68d80b7cfa6d3bb4e370300200141c0006a41086a200141106a41086a28020041016a220236020020012001290310220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024206370224200241b2d9c480003602202002410d36021c200241e3a4c580003602182002418083808000360210200242dc8ec6fd9fd6fcdeb77f370308200242f68d80b7cfa6d3bb4e370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bcd0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41fcfcc5800041054181fdc58000411a41fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242f68d80b7cfa6d3bb4e370308200241a1fdc580003602202002418083808000360218200241063602042002419bfdc58000360200200241306a42dda1fc828d83b3faab7f370300200241286a42b7a18ef9ac95b6cbeb00370300200241106a42dc8ec6fd9fd6fcdeb77f370300200241386a41ec81808000360200200241246a410936020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024206370224200241b2d9c480003602202002410636021c200241acd9c480003602182002418083808000360210200242dc8ec6fd9fd6fcdeb77f370308200242f68d80b7cfa6d3bb4e370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420e370224200241c2d9c480003602202002410a36021c200241b8d9c48000360218200241ff828080003602102002429acfecb0d2a8f28be7003703082002428bdb9ef4d7aab8cbec00370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000ba60703067f017e027f23808080800041d0006b2201248080808000200141286a41c0ffc58000411241d2ffc58000412810d682808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041800141002802c8a3c68000118180808000002202450d00200242c4ccab9c81ebb4b8db003703082002418e80c680003602602002418580c680003602402002418180c68000360220200241f08180800036021820024107360204200241faffc58000360200200241f0006a4283af8bf0ede78391a67f370300200241e8006a42dba4b9c0a2bed19501370300200241d0006a42f0a3fcacaeba9c956f370300200241c8006a42e3beec81d3e996af917f370300200241306a42fec1aad493d7e4ae3e370300200241286a42dbe2e9e8becbf3fb2f370300200241106a42a4d2928ecac0faa432370300200241f8006a419b83808000360200200241e4006a4105360200200241d8006a418583808000360200200241c4006a4109360200200241386a419a83808000360200200241246a41043602002001410436024820012002360240200120024180016a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202410036023020024280808080c0003703282002410036022020024100360218200241ea81808000360210200242b891b68c98adebcf61370308200242e7b0a091f3ed9c85c500370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141106a41076a290000370000200141d0006a2480808080000f0b410841800110b280808000000b41a8d8c480004111419cd9c4800010a181808000000be40502037f027e2380808080004180016b2202248080808000024002400240024002402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241106a41086a200441086a290000370300200241106a41106a200441106a290000370300200241106a41186a200441186a290000370300200220042900003703102002200110be8a8080002002290300a70d012001280200220328020422044120490d02200229030821052003200441606a36020420032003280200220441206a360200200241306a41086a200441086a290000370300200241306a41106a200441106a290000370300200241306a41186a200441186a290000370300200220042900003703302001280200220328020422044120490d032003200441606a36020420032003280200220441206a360200200241d0006a41086a200441086a290000370300200241d0006a41106a200441106a290000370300200241d0006a41186a200441186a29000037030020022004290000370350200241f4006a2001109487808000024020022802742201418080808078460d00200229027821062000200229031037000020002002290330370028200041186a200241106a41186a290300370000200041106a200241106a41106a290300370000200041086a200241106a41086a290300370000200041306a200241306a41086a290300370000200041386a200241306a41106a290300370000200041c0006a200241306a41186a29030037000020002002290350370048200041d0006a200241d0006a41086a290300370000200041d8006a200241d0006a41106a290300370000200041e0006a200241d0006a41186a2903003700002000200637026c20002001360268200020053703200c050b20004180808080783602680c040b20004180808080783602680c030b20004180808080783602680c020b20004180808080783602680c010b20004180808080783602680b20024180016a2480808080000bde0601077f23808080800041106b220224808080800041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002203450d0020032000290000370000200341186a2204200041186a290000370000200341106a2205200041106a290000370000200341086a2206200041086a29000037000002402001280200200128020822076b411f4b0d0020012007412010b182808000200128020821070b200128020420076a22082003290000370000200841086a2006290000370000200841106a2005290000370000200841186a20042900003700002001200741206a360208200341002802c0a3c68000118080808000002002200041206a360204200241046a200110c18a80800041002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0120032000290028370000200341186a2204200041c0006a290000370000200341106a2205200041386a290000370000200341086a2206200041306a29000037000002402001280200200128020822076b411f4b0d0020012007412010b182808000200128020821070b200128020420076a22082003290000370000200841086a2006290000370000200841106a2005290000370000200841186a20042900003700002001200741206a360208200341002802c0a3c680001180808080000041002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0220032000290048370000200341186a2204200041e0006a290000370000200341106a2205200041d8006a290000370000200341086a2206200041d0006a29000037000002402001280200200128020822076b411f4b0d0020012007412010b182808000200128020821070b200128020420076a22082003290000370000200841086a2006290000370000200841106a2005290000370000200841186a20042900003700002001200741206a360208200341002802c0a3c6800011808080800000200041ec006a28020021032002200041f0006a28020022003602082002200241086a36020c2002410c6a200110c08a80800002402000450d00200041146c210003402003200110de8c808000200341146a21032000416c6a22000d000b0b200241106a2480808080000f0b4101412010b280808000000b4101412010b280808000000b4101412010b280808000000b9b0201037f23808080800041206b22022480808080000240024002400240024020002d000022030e050001020304000b2002200041016a36021420022000410c6a2902003702180c030b2002200041016a36021420022000410c6a2902003702180c020b2002200041016a36021420022000410c6a2902003702180c010b2002200041086a2902003702140b20022003360210200241046a200241106a10ed848080002002280208210402402001280200200128020822006b200228020c22034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a36020802402002280204450d00200441002802c0a3c68000118080808000000b200241206a2480808080000be707010e7f23808080800041206b2204248080808000200441086a200110bd8a80800002400240024020042802080d0020012802042205200428020c2206490d000240024002400240024002400240024002400240024020060d00410121070c010b2006417f4c0d01200641002802c8a3c68000118180808000002207450d02200741002006108a8e8080001a0b200720012802002208200610848e80800021072001200520066b3602042001200820066a3602002004200110bd8a80800020042802000d072001280204220820042802042205490d070240024020050d00410121090c010b2005417f4c0d01200541002802c8a3c68000118180808000002209450d0c200941002005108a8e8080001a0b20092001280200220a200510848e808000210b2001200820056b22083602042001200a20056a220936020020084104490d0520012008417c6a220a3602042001200941046a360200200a4104490d042009280000210c2001200841786a220a3602042001200941086a360200200a4104490d032009280004210d2001200841746a36020420012009410c6a3602002009280008210e200441146a20011093878080002004280214220a418080808078460d02200428021c210f200428021821104101211102400240024020024101460d00200f410c6c21032010417c6a21020340024020030d0041002102410121110c030b200341746a2103200241046a21082002410c6a22092102200829020042dfd5adc696f381b09b7f520d000b200928020021030b024020034103490d00200128020422024104490d0220012002417c6a36020420012001280200220241046a360200200228000021110b4100210220034104490d0020012802042203450d0120012003417f6a36020420012001280200220341016a36020020032d000021020b200020023a0034200020113602302000200e36022c2000200d3602282000200c3602242000200f3602202000201036021c2000200a360218200020053602142000200b3602102000200536020c2000200636020820002007360204200020063602000c0b0b2000418180808078360200200a41808080807872418080808078460d06201041002802c0a3c68000118080808000000c060b10ae80808000000b4101200610b280808000000b20004181808080783602000c030b20004181808080783602000c020b20004181808080783602000c010b20004181808080783602000b2005450d01200b41002802c0a3c68000118080808000000c010b20004181808080783602000b2006450d01200741002802c0a3c68000118080808000000c010b20004181808080783602000b200441206a2480808080000f0b4101200510b280808000000bbb0401077f0240024002400240200128022022020d00410021020c010b20012002417f6a3602202001280204210202400240024020012802002203450d002002450d010b2003450d032001410c6a2802002104200141086a28020021050c010b200141086a280200210202402001410c6a2802002205450d0002400240200541077122040d00200521030c010b2005210303402003417f6a210320022802ac1421022004417f6a22040d000b0b20054108490d00034020022802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142102200341786a22030d000b0b20014200370208200120023602042001410136020041002104410021050b02400240200420022f01aa144f0d00200221030c010b034020022802a0132203450d04200541016a210520022f01a814210420032102200420032f01aa144f0d000b0b200441016a21060240024020050d00200321020c010b200320064102746a41ac146a2802002102410021062005417f6a2207450d002005417e6a2108024020074107712205450d0003402007417f6a210720022802ac1421022005417f6a22050d000b0b20084107490d00034020022802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142102200741786a22070d000b0b2001200636020c20014100360208200120023602042003200441e0016c6a210520032004410c6c6a41a4136a21020b20002005360204200020023602000f0b41d4fec5800010a081808000000b419cd0c2800010a081808000000bc10601077f0240200128022022020d00200128020021022001410036020002402002450d000240200128020422020d0020012802082102200128020c2203450d0002400240200341077122040d00200321050c010b2003210503402005417f6a210520022802900221022004417f6a22040d000b0b20034108490d000340200228029002280290022802900228029002280290022802900228029002280290022102200541786a22050d000b0b034020022802002105200241002802c0a3c68000118080808000002005210220050d000b0b20004180808080783602000f0b20012002417f6a360220200128020421020240024002400240024020012802002205450d002002450d010b2005450d022001410c6a2802002104200141086a28020021030c010b200141086a280200210202402001410c6a2802002203450d0002400240200341077122040d00200321050c010b2003210503402005417f6a210520022802900221022004417f6a22040d000b0b20034108490d000340200228029002280290022802900228029002280290022802900228029002280290022102200541786a22050d000b0b20014200370208200120023602042001410136020041002104410021030b0240200420022f018e024f0d00200221050c020b0240034020022802002205450d0120022f018c022104200241002802c0a3c6800011808080800000200341016a210320052102200420052f018e02490d030c000b0b200241002802c0a3c6800011808080800000418cd0c2800010a081808000000b41d887c6800010a081808000000b200441016a21060240024020030d00200521020c010b200520064102746a4190026a2802002102410021062003417f6a2207450d002003417e6a2108024020074107712203450d0003402007417f6a210720022802900221022003417f6a22030d000b0b20084107490d000340200228029002280290022802900228029002280290022802900228029002280290022102200741786a22070d000b0b2001200636020c2001410036020820012002360204200020052004410c6c6a220241046a290200370200200041086a2002410c6a280200360200200020024188016a29020037020c200041146a20024190016a2802003602000b900601077f0240200128022022020d00200128020021022001410036020002402002450d000240200128020422020d0020012802082102200128020c2203450d0002400240200341077122040d00200321050c010b2003210503402005417f6a210520022802f80621022004417f6a22040d000b0b20034108490d00034020022802f8062802f8062802f8062802f8062802f8062802f8062802f8062802f8062102200541786a22050d000b0b034020022802f0062105200241002802c0a3c68000118080808000002005210220050d000b0b200041023a004c0f0b20012002417f6a360220200128020421020240024002400240024020012802002205450d002002450d010b2005450d022001410c6a2802002104200141086a28020021030c010b200141086a280200210202402001410c6a2802002203450d0002400240200341077122040d00200321050c010b2003210503402005417f6a210520022802f80621022004417f6a22040d000b0b20034108490d00034020022802f8062802f8062802f8062802f8062802f8062802f8062802f8062802f8062102200541786a22050d000b0b20014200370208200120023602042001410136020041002104410021030b0240200420022f01f6064f0d00200221050c020b0240034020022802f0062205450d0120022f01f4062104200241002802c0a3c6800011808080800000200341016a210320052102200420052f01f606490d030c000b0b200241002802c0a3c6800011808080800000418cd0c2800010a081808000000b41d887c6800010a081808000000b200441016a21060240024020030d00200521020c010b200520064102746a41f8066a2802002102410021062003417f6a2207450d002003417e6a2108024020074107712203450d0003402007417f6a210720022802f80621022003417f6a22030d000b0b20084107490d00034020022802f8062802f8062802f8062802f8062802f8062802f8062802f8062802f8062102200741786a22070d000b0b2001200636020c200141003602082001200236020420002005200441d0006c6a41d00010848e8080001a0bb40101017f23808080800041206b2204248080808000200441086a20012802042002200310e48c8080000240024002402004280208418080808078470d00200441146a2001280200200210e58c8080002004280214418180808078460d0120002004290214370200200041086a200441146a41086a2802003602000c020b20002004290208370200200041086a200441086a41086a2802003602000c010b20004180808080783602000b200441206a2480808080000bd807010a7f23808080800041106b220424808080800002400240024002400240024002400240024002402002200141186a412010888e808000450d00200328020021050240024002402003280204220641216a22070d0020044201370208200420073602040c010b2007417f4c0d084100210841002d00fca3c680001a200741002802c8a3c68000118180808000002209450d092004410036020c20042009360208200420073602042006415f490d010b200441046a4100200610ab868080002004280204210720042802082109200428020c21080b200920086a2005200610848e8080001a2004200820066a220636020c024020032d0008450d00200341096a2d00002103024020062007470d00200441046a2007109c8680800020042802082109200428020c21060b200920066a20033a00002004200428020c41016a220636020c200428020421070b0240200720066b411f4b0d00200441046a2006412010ab8680800020042802042107200428020c21060b2004280208220a20066a22032002290000370000200341086a200241086a290000370000200341106a200241106a290000370000200341186a200241186a290000370000200128020c220b450d02200641206a2106200141106a280200210c0340200b41746a2108200b41b4016a2102200b2f01ba02220d410c6c2101417f210902400340024020010d00200d21090c020b200241086a2103200241046a2105200141746a2101200841106a2108200941016a21092002410c6a2102417f200a200528020020062003280200220320062003491b10888e8080002205200620036b20051b220341004720034100481b22034101460d000b200341ff0171450d030b200c450d03200c417f6a210c200b20094102746a41bc026a280200210b0c000b0b2001280204210602400240200128020822020d00410121010c010b2002417f4c0d0641002d00fca3c680001a200241002802c8a3c68000118180808000002201450d080b20012006200210848e80800021012000200236020820002001360204200020023602000c040b200841086a28020041004a0d010b20004180808080783602000c010b2008280200210102400240200841046a28020022020d00410121060c010b2002417f4c0d0341002d00fca3c680001a200241002802c8a3c68000118180808000002206450d060b20062001200210848e80800021012000200236020820002001360204200020023602000b2007450d00200a41002802c0a3c68000118080808000000b200441106a2480808080000f0b10ae80808000000b4101200710b280808000000b4101200210b280808000000b4101200210b280808000000bd70301087f0240024002400240024002402002200141186a412010888e808000450d0041808080807821030240200128020c22040d000c030b200141106a28020021050340200441dc026a210620042f01960422074105742101417f21082004210902400340024020010d00200721080c020b20022009412010888e808000210a200141606a2101200641106a2106200841016a2108200941206a2109417f200a410047200a4100481b220a4101460d000b200a41ff0171450d030b024020050d000c040b2005417f6a2105200420084102746a4198046a28020021040c000b0b200128020421090240200128020822030d004101210141012009200310848e8080001a0c020b2003417f4c0d0241002d00fca3c680001a200341002802c8a3c68000118180808000002201450d0320012009200310848e8080001a0c010b410121010240200628020041014e0d000c010b200641786a280200210902402006417c6a2802002203450d002003417f4c0d0241002d00fca3c680001a200341002802c8a3c68000118180808000002201450d040b20012009200310848e8080001a0b2000200336020820002001360204200020033602000f0b10ae80808000000b4101200310b280808000000b4101200310b280808000000b15002000200128020420022003200410e78c8080000bec0301037f23808080800041306b220524808080800002400240024002400240024002402004450d002004417f4c0d0241002d00fca3c680001a200441002802c8a3c68000118180808000002206450d0320062003200410848e80800021060240024020012802082004460d00200641002802c0a3c68000118080808000000c010b20062001280204200410888e8080002107200641002802c0a3c68000118080808000002007450d020b200541046a200320044100280298a3c680001185808080000041002d00fca3c680001a200441002802c8a3c680001181808080000022060d054101200410b280808000000b20012802080d030b20002001290018370000200041186a200141306a290000370000200041106a200141286a290000370000200041086a200141206a2900003700000c040b10ae80808000000b4101200410b280808000000b200541046a200341004100280298a3c6800011858080800000410121060b20062003200410848e80800021032005200436022c20052003360228200520043602242001200541046a2002200541246a10eb8c808000200041186a200541046a41186a290000370000200041106a200541046a41106a290000370000200041086a200541046a41086a290000370000200020052900043700000b200541306a2480808080000b110020002802042001200210e98c8080000b890904087f017e027f017e23808080800041e0006b220324808080800002402001200041186a412010888e808000450d002002280200210402400240024002400240024002402002280204220541216a22060d002003420137022c200320063602280c010b2006417f4c0d024100210741002d00fca3c680001a200641002802c8a3c68000118180808000002208450d03200341003602302003200836022c200320063602282005415f490d010b200341286a4100200510ab8680800020032802282106200328022c2108200328023021070b200820076a2004200510848e8080001a2003200720056a2205360230024020022d0008450d00200241096a2d00002102024020052006470d00200341286a2006109c86808000200328022c2108200328023021050b200820056a20023a00002003200328023041016a2205360230200328022821060b0240200620056b411f4b0d00200341286a2005412010ab8680800020032802282106200328023021050b200328022c220920056a22022001290000370000200241086a200141086a290000370000200241106a200141106a290000370000200241186a200141186a290000370000200541206a210202400240200028020c220a0d002002ad4220862009ad84210b4100210a0c010b200041106a280200210c02400340200a41b4016a2101200a2f01ba02220d410c6c2105417f2107024002400340024020050d00200d21070c020b200141086a2108200141046a2104200541746a2105200741016a21072001410c6a2101417f2009200428020020022008280200220820022008491b10888e8080002204200220086b20041b220841004720084100481b22084101460d000b200841ff0171450d010b200c450d02200c417f6a210c200a20074102746a41bc026a280200210a0c010b0b2003200c36022c2003200a3602282003290328210b2006450d05200941002802c0a3c68000118080808000000c050b200320073602302003410036022c2002ad4220862009ad84210b200329022c210e0b2000410c6a21072006418080808078460d032003200e3702202003200a36021c200320073602182003200b3702102003200636020c200342808080807037023020034280808080103702280240200a0d0041002d00fca3c680001a41bc0241002802c8a3c68000118180808000002201450d03200141013b01ba02200141003602b0012001200329020c3702b4012003410c6a41086a280200210520012003290228370200200141086a200341286a41086a290200370200200141bc016a2005360200200041106a4280808080103702002000200136020c0c050b200341386a41086a2003411c6a220141086a28020036020020032001290200370338200341d0006a41086a2003410c6a41086a2802003602002003200329020c370350200341c4006a200341386a200341d0006a200341286a200341186a10c08580800020032802182201200128020841016a3602080c040b10ae80808000000b4101200610b280808000000b410441bc0210b280808000000b200ba720074104746a2201200128020c417f6a36020c0b200341e0006a2480808080000b1300200028020420012002200310eb8c8080000bc20a04097f017e027f017e23808080800041e0006b22042480808080002003280204210502400240024002400240024002400240200328020822062000280208470d0020052000280204200610888e808000450d010b200228020021070240024002402002280204220641216a22080d002004420137022c200420083602280c010b2008417f4c0d044100210941002d00fca3c680001a200841002802c8a3c6800011818080800000220a450d05200441003602302004200a36022c200420083602282006415f490d010b200441286a4100200610ab8680800020042802282108200428022c210a200428023021090b200a20096a2007200610848e8080001a2004200920066a2206360230024020022d0008450d00200241096a2d00002102024020062008470d00200441286a2008109c86808000200428022c210a200428023021060b200a20066a20023a00002004200428023041016a2206360230200428022821080b0240200820066b411f4b0d00200441286a2006412010ab8680800020042802282108200428023021060b200428022c220b20066a22022001290000370000200241086a200141086a290000370000200241106a200141106a290000370000200241186a200141186a290000370000200641206a210202400240200028020c220c0d002002ad422086200bad84210d4100210c0c010b200041106a280200210e02400340200c41b4016a2106200c2f01ba02220f410c6c2101417f2109024002400340024020010d00200f21090c020b200641086a210a200641046a2107200141746a2101200941016a21092006410c6a2106417f200b20072802002002200a280200220a2002200a491b10888e80800022072002200a6b20071b220a410047200a4100481b220a4101460d000b200a41ff0171450d010b200e450d02200e417f6a210e200c20094102746a41bc026a280200210c0c010b0b2004200e36022c2004200c3602282004290328210d2008450d07200b41002802c0a3c68000118080808000000c070b200420093602302004410036022c2002ad422086200bad84210d200429022c21100b2000410c6a21092008418080808078460d05200420103702202004200c36021c200420093602182004200d3702102004200836020c200441286a41086a200341086a2802003602002004410136023420042003290200370328200c0d0141002d00fca3c680001a41bc0241002802c8a3c68000118180808000002206450d04200641013b01ba02200641003602b0012006200429020c3702b4012004410c6a41086a280200210120062004290328370200200641086a200441286a41086a290300370200200641bc016a2001360200200041106a4280808080103702002000200636020c0c060b2003280200450d05200541002802c0a3c68000118080808000000c050b200441386a41086a2004411c6a220641086a28020036020020042006290200370338200441d0006a41086a2004410c6a41086a2802003602002004200429020c370350200441c4006a200441386a200441d0006a200441286a200441186a10c08580800020042802182206200628020841016a3602080c040b10ae80808000000b4101200810b280808000000b410441bc0210b280808000000b0240200da720094104746a220628020c220141004a0d0002402006280200450d00200628020441002802c0a3c6800011808080800000200628020c21010b200620032902003702002006200141016a36020c200641086a200341086a2802003602000c010b2006200141016a36020c2003280200450d00200541002802c0a3c68000118080808000000b200441e0006a2480808080000bcb0101017f23808080800041206b2203248080808000200341086a20002802042001200210e48c808000024002400240024020032802082202418080808078470d00200341146a2000280200200110e58c80800020032802142202418180808078470d0141808080807821000c030b200328020c21000c010b41808080807821002002418080808078460d01200328021821000b024020020d00410021000c010b200041002802c0a3c6800011808080800000200221000b200341206a2480808080002000418080808078470bbf0101027f23808080800041306b2202248080808000410021030240200041c0016a2d00004102460d002002200041b8016a36022c20004198016a21030b200241186a410c6a4184848080003602002002410c6a420237020020022000360220200241858480800036021c2002200336022820024103360204200241fcfec58000360200200141186a28020021002002200241286a3602182002200241186a36020820012802142000200210d9808080002100200241306a24808080800020000bc00201027f23808080800041106b22022480808080000240024020002802000d00200128021441bcffc580004104200141186a28020028020c1182808080000021010c010b2002200036020020022001280214418888c680004104200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a2002418c88c68000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010bce0101037f23808080800041106b220224808080800020022000360208200241086a200110c18a8080002002200041086a36020c2002410c6a200110c18a80800020002d001021030240200128020020012802082204470d0020012004410110b182808000200128020821040b200128020420046a20033a00002001200441016a220436020820002d00112100024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a20003a0000200241106a2480808080000b9c0804067f017e027f017e23808080800041d0006b2201248080808000200141286a4194ffc58000411041a4ffc58000411741fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d002002429b99b4f08dc8ccdea77f370308200241ae8080800036021820024101360204200241bbffc58000360200200241106a428a96cd9bb2e69e8d7237030020014101360248200120023602402001200241206a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024201370224200241dbfbc480003602202002410636021c200241e7fbc48000360218200241ae808080003602102002428a96cd9bb2e69e8d723703082002429b99b4f08dc8ccdea77f370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024201370224200241dbfbc480003602202002410b36021c200241dcfbc48000360218200241ae808080003602102002428a96cd9bb2e69e8d723703082002429b99b4f08dc8ccdea77f3703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024201370224200241dbfbc480003602202002410936021c200241d2fbc48000360218200241ae808080003602102002428a96cd9bb2e69e8d723703082002429b99b4f08dc8ccdea77f370300200128021821082001280214210220012001280210360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b930202017f067e23808080800041e0006b2202248080808000200241d0006a200110bf8a80800042012103024020022802500d0020022903582104200241c0006a200110bf8a8080002002290340a70d0020022903482105200241306a200110bf8a80800020022802300d0020022903382106200241206a200110bf8a8080002002290320a70d0020022903282107200241106a200110bf8a80800020022802100d00200229031821082002200110bf8a8080002002290300a70d002002290308210320002004370308200041306a2003370300200041286a2008370300200041206a2007370300200041186a2006370300200041106a2005370300420021030b20002003370300200241e0006a2480808080000ba80201017f23808080800041106b22022480808080002002200041c8006a36020c2002410c6a200110c18a8080002002200041d0006a36020c2002410c6a200110c18a8080002000200110c88c808000200041186a200110c88c808000200041306a200110c88c8080002002200041a0016a36020c2002410c6a200110c18a8080002002200041a8016a36020c2002410c6a200110c18a808000200041d8006a200110c88c808000200041f0006a200110c88c80800020004188016a200110c88c8080002002200041f8016a36020c2002410c6a200110c18a808000200220004180026a36020c2002410c6a200110c18a808000200041b0016a200110c88c808000200041c8016a200110c88c808000200041e0016a200110c88c808000200241106a2480808080000bd10101017f23808080800041106b220424808080800002400240024002402002200141d8006a412010888e808000450d00200441046a2001200210e58c8080002004280204418180808078460d0120002004290204370200200041086a200441046a41086a2802003602000c020b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d02200041013602082000200136020420004101360200200141003a00000c010b20004180808080783602000b200441106a2480808080000f0b4101410110b280808000000bda0101027f23808080800041106b2203248080808000024002400240024002402001200041d8006a412010888e808000450d00200341046a2000200110e58c80800041808080807821000240200328020422044180808080786a0e020304000b2004450d0220032802082101200421000c010b41002d00fca3c680001a41012100410141002802c8a3c68000118180808000002201450d03200141003a00000b200141002802c0a3c68000118080808000000c010b200421000b200341106a2480808080002000418080808078470f0b4101410110b280808000000bcf0802087f017e23808080800041f0036b2202248080808000200241106a200110bc8a808000024002400240024020022802100d002001280200220341046a22042802002205450d012002280214210620042005417f6a36020020032003280200220441016a36020002400240024020042d0000220341ff00714104470d002003411874411875417f4c0d01410221040c020b200041003602000c050b024002402001280200220328020422074120490d002003200741606a220836020420032003280200220441206a220936020020024190036a41086a200441086a29000037030020024190036a41106a200441106a29000037030020024190036a41186a200441186a2900003703002002200429000037039003200841c000490d002003200741a07f6a3602042003200441e0006a360200200241b0036a41086a200941086a290000370300200241b0036a41106a200941106a290000370300200241b0036a41186a200941186a290000370300200241b0036a41206a200941206a290000370300200241b0036a41286a200941286a290000370300200241b0036a41306a200941306a290000370300200241b0036a41386a200941386a290000370300200220092900003703b0032002200110be8a80800020022802000d00200128020022032802042204450d002002290308210a20032004417f6a36020420032003280200220441016a3602004101410220042d000022034101461b410020031b22044102470d010b200041003602000c050b200241b0026a20024190036a41186a290300370300200241a8026a20024190036a41106a290300370300200241a0026a20024190036a41086a290300370300200241d8016a41086a200241b0036a41086a290300370300200241d8016a41106a200241b0036a41106a290300370300200241d8016a41186a200241b0036a41186a290300370300200241d8016a41206a200241b0036a41206a290300370300200241d8016a41286a200241b0036a41286a290300370300200241d8016a41306a200241b0036a41306a290300370300200241d8016a41386a200241b0036a41386a290300370300200220022903900337039802200220022903b0033703d801200241f8006a200241d8016a41e00010848e8080001a0b200241186a200241f8006a41e00010848e8080001a200241d8016a200110fd8980800020022802d8012203450d02200241f8006a410472200241d8016a41047241d40010848e8080001a20022003360278024041002005200128020041046a2802006b2201200120054b1b2006470d00200241d8016a41d8006a200241186a41e00010848e8080001a200041003a00c101200241d8016a200241f8006a41d80010848e8080001a2000200241d8016a41b80110848e808000220120043a00c0012001200a3703b8010c040b20004100360200200241f8006a10f68c8080000c030b200041003602000c020b200041003602000c010b200041003602000b200241f0036a2480808080000bef0901057f0240024002400240024002402000280200220141736a2202410220024104491b0e03010203000b20002d00084106470d042000410c6a280200450d04200041106a28020021030c030b0240024002400240024002400240024020002d0008417f6a0e0a010b0203040506070b0b000b2000410c6a280200450d0a200041106a28020021030c090b2000410c6a280200450d09200041106a28020021030c080b2000410c6a280200450d08200041106a28020021030c070b2000410c6a280200450d07200041106a28020021030c060b200041106a28020021030240200041146a2802002201450d0020032102034002402002280200450d00200241046a28020041002802c0a3c68000118080808000000b02402002410c6a280200450d00200241106a28020041002802c0a3c68000118080808000000b200241186a21022001417f6a22010d000b0b200028020c450d060c050b200041106a28020021030240200041146a2802002202450d002002410171210441002101024020024101460d002002417e7121052003210241002101034002402002280200450d00200241046a28020041002802c0a3c68000118080808000000b02402002410c6a280200450d00200241106a28020041002802c0a3c68000118080808000000b200241186a21022005200141026a2201470d000b0b2004450d0020032001410c6c6a2202280200450d00200228020441002802c0a3c68000118080808000000b200028020c0d040c050b200041106a280200450d04200041146a28020021030c030b2000410c6a280200450d03200041106a28020021030c020b200041186a2d0000417d6a41ff017141014b0d020240200028020822034198016a2802002205450d0020034194016a280200220421004100210203402004200241146c6a21010240024002400240024020002d00000e0400010102040b200041086a21010c020b200141086a21010c010b200141046a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200241016a2102200041146a21002005417f6a22050d000b0b0240200328029001450d0020032802940141002802c0a3c68000118080808000000b024020034190026a2802002205450d002003418c026a280200220421004100210203402004200241146c6a21010240024002400240024020002d00000e0400010102040b200041086a21010c020b200141086a21010c010b200141046a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200241016a2102200041146a21002005417f6a22050d000b0b200328028802450d01200328028c0241002802c0a3c68000118080808000000c010b024002400240024002400240024002402001417e6a0e06000102030405090b200041046a21000c050b02402000280210450d00200041146a28020041002802c0a3c68000118080808000000b20002802042202418080808078460d07200041046a21000c050b02402000280204450d00200041086a28020041002802c0a3c68000118080808000000b200041106a21000c030b200041046a21000c020b200041046a21000c010b024002400240024020002d00040e0400010203070b2000410c6a21000c030b2000410c6a21000c020b2000410c6a21000c010b200041086a21000b200028020021020b2002450d01200028020421030b200341002802c0a3c68000118080808000000f0b0b890401047f23808080800041206b220224808080800041002d00fca3c680001a02400240024041e80141002802c8a3c68000118180808000002203450d002002410036020820022003360204200241e80136020002400240200141c0016a2d00004102470d00200341043a00002002200228020841016a3602080c010b20034184013a00002002200228020841016a360208200141d8006a200210cc878080000b2001200210b88980800020022002280208220136020c41012104410121030240200141c000490d0041022103200141808001490d00410441052001418080808004491b21030b0240200320016a2201450d002001417f4c0d0241002d00fca3c680001a200141002802c8a3c68000118180808000002204450d030b20024100360218200220043602142002200136021020022002410c6a36021c2002411c6a200241106a10c08a808000200228020421042002280200210502402002280210200228021822016b200228020822034f0d00200241106a2001200310ab86808000200228021821010b200228021420016a2004200310848e8080001a2002200120036a36021802402005450d00200441002802c0a3c68000118080808000000b20002002290210370200200041086a200241106a41086a280200360200200241206a2480808080000f0b410141e80110b280808000000b10ae80808000000b4101200110b280808000000b880703017f057e017f23808080800041f0036b2201248080808000200141c0006a10ce88808000200141f0006a10e18980800002402001290388032202427f427f2001290340220320012903507c220420042003541b220320012903607c220420042003541b2204580d0020014190036a2903002203427f427f20012903482205200141d8006a2903007c220620062005541b2205200141e8006a2903007c220620062005541b2205580d00200141306a20004200200220047d220420042002561b22024200200320057d220420042003561b220341a8c6c480002000a741037141027441a8c6c480006a2207200741b8c6c48000461b2207280200118d8080800000200141206a200042002002200129033022047d220520052002561b42002003200129033822057d220620062003561b41a8c6c48000200741046a2207200741b8c6c48000461b2207280200118d8080800000200141106a200042002002427f200420012903207c220620062004541b22047d220620062002561b42002003427f200520012903287c220620062005541b22057d220620062003561b41a8c6c48000200741046a2207200741b8c6c48000461b2207280200118d80808000002001200042002002427f200420012903107c220620062004541b22047d220620062002561b42002003427f200520012903187c220220022005541b22027d220520052003561b41a8c6c48000200741046a2207200741b8c6c48000461b280200118d8080800000200129030821002001290300210320014198036a10ce8880800020014198036a41286a2207427f20072903002205427f200220007c220020002002541b7c220020002005541b220037030020014198036a41206a2207427f20072903002202427f200420037c220320032004541b7c220320032002541b2202370300200141a0016a2000370300200141f0006a41286a2002370300200141f0006a41206a20014198036a41186a290300370300200141f0006a41186a20014198036a41106a290300370300200141f0006a41106a20014198036a41086a2903003703002001200129039803370378200142013703702001200141f0006a41086a3602cc03200142fc90b9e5d09296e7773703d803200142a6d4e6f1a4dd9598603703d003200142f89aef8eef91e1ce967f3703e803200142b4d6d6dfccc6b592c3003703e003200141cc036a200141d0036a412010f98c8080000b200141f0036a2480808080000bb30503037f017e067f23808080800041106b22032480808080004101210441012105024020002802002200290300220642c000540d0041022105200642808001540d00410421052006428080808004540d004109200679a74103766b21050b02402000290308220642c000540d0041022104200642808001540d00410421042006428080808004540d004109200679a74103766b21040b410121074101210802402000290310220642c000540d0041022108200642808001540d00410421082006428080808004540d004109200679a74103766b21080b0240200041186a2209290300220642c000540d0041022107200642808001540d00410421072006428080808004540d004109200679a74103766b21070b4101210a4101210b02402000290320220642c000540d004102210b200642808001540d004104210b2006428080808004540d004109200679a74103766b210b0b0240200041286a220c290300220642c000540d004102210a200642808001540d004104210a2006428080808004540d004109200679a74103766b210a0b41002d00fca3c680001a0240200420056a20086a20076a200b6a200a6a220441002802c8a3c68000118180808000002207450d002003200736020420032004360200200341003602082003200036020c2003410c6a200310c18a8080002003200041086a36020c2003410c6a200310c18a8080002003200041106a36020c2003410c6a200310c18a8080002003200936020c2003410c6a200310c18a8080002003200041206a36020c2003410c6a200310c18a8080002003200c36020c2003410c6a200310c18a808000200328020021002001200220032802042204200328020841002802e0a1c680001186808080000002402000450d00200441002802c0a3c68000118080808000000b200341106a2480808080000f0b4101200410b280808000000bf70f03017f017e067f23808080800041a0036b2201248080808000200010fb8c8080001a024002400240024020002903202202500d0020014198016a2002427f7c10fb8780800020014198016a2000412010888e8080000d00200141106a200041f80010848e8080001a20002802800121032001200029037822023e02900120012002422088a72200360288012001200036028c0120012000200341e8016c6a22043602940102402003450d0020014198016a410472210502400240034020002802002206450d012005200041046a41e40110848e8080001a200120063602980120014188036a20014198016a10fc8c80800020012d008803410f460d05200041e8016a22002004470d000c020b0b200041e8016a21040b2001200436028c010b20014188016a10de8680800010ed888080000d032003450d020240417f4100280284a4c680002200410147200041014b1b2200417f460d00200041ff01710d030b20014188036a410c6a418382808000360200200141d484c6800036029003200141e08180800036028c03200141f883c68000360288034100280290a1c680002100410028028ca1c6800021064100280280a4c680002104200141d0016a4202370200200141c8016a4102360200200141c0016a4112360200200141bc016a418084c68000360200200141b0016a41d080c68000ad4280808080e00b8437020020014198016a410c6a419284c68000ad4280808080f00184370200200141cc016a20014188036a360200200141a484c680003602c401200141013602b801200141003602ac01200141003602a00120014281808080a0d20037029801200641ecf2c08000200441024622041b20014198016a200041d4f2c0800020041b280210118480808000000c020b200141a4016a42003702002001410136029c01200141f884c6800036029801200141fcfcc580003602a00120014198016a418085c6800010f680808000000b200141086a20012f00890320012d008b034110747210fc84808000200120012903083702800320014180036a10fd8c808000000b10fe8c8080000b109b8a8080002001290330220210f88c808000200210cf8780800020014198016a109d8a8080002001200141106a41f0006a280200220336028003200120014198016a41f0006a28020022003602880102400240024020032000470d002003450d0220014198016a41ec006a2802002107200141106a41ec006a2802002108410021000340200820006a2206200720006a220410ef8480800020062d0000220520042d0000470d020240024002400240024020050e0400010203040b20050d03200641016a280000200441016a280000470d06200641106a2802002205200441106a280200470d062006410c6a2802002004410c6a280200200510888e8080000d060c030b20054101470d02200641016a280000200441016a280000470d05200641106a2802002205200441106a280200470d052006410c6a2802002004410c6a280200200510888e8080000d050c020b20054102470d01200641016a280000200441016a280000470d04200641106a2802002205200441106a280200470d042006410c6a2802002004410c6a280200200510888e808000450d010c040b20054103470d002006410c6a28020022052004410c6a280200470d03200641086a280200200441086a280200200510888e8080000d030b200041146a21002003417f6a22030d000c030b0b2001420037029403200141fcfcc58000360290032001410136028c03200141c880c6800036028803410020014180036a20014188016a20014188036a41b081c6800010a789808000000b20014194036a42003702002001410136028c03200141e482c6800036028803200141fcfcc580003602900320014188036a41ec82c6800010f680808000000b0240200141106a41286a220020014198016a41286a2206412010888e808000450d0041f8a7c28000410e4100280280a3c68000118480808000002000412041002802f8a2c68000118480808000002006412041002802f8a2c68000118480808000000b024020002006412010888e808000450d0020014194036a42003702002001410136028c03200141e881c6800036028803200141fcfcc580003602900320014188036a41f081c6800010f680808000000b0240200141106a41c8006a20014198016a41c8006a412010888e8080000d0002402001280288022205450d00200128028402220321004100210603402003200641146c6a21040240024002400240024020002d00000e0400010102040b200041086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200641016a2106200041146a21002005417f6a22050d000b0b0240200128028002450d0020012802840241002802c0a3c68000118080808000000b02402001280280012205450d00200128027c220321004100210603402003200641146c6a21040240024002400240024020002d00000e0400010102040b200041086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200641016a2106200041146a21002005417f6a22050d000b0b02402001280278450d00200128027c41002802c0a3c68000118080808000000b200141a0036a2480808080000f0b20014194036a42003702002001410136028c03200141a482c6800036028803200141fcfcc580003602900320014188036a41ac82c6800010f680808000000bc30b020a7f077e23808080800041c0036b220124808080800020002802702102200028026c21032001410036024820014280808080c0003702400240024002402002450d00200241146c21040340024020032d00000d00200341046a2d00002105200341036a2d00002106200341026a2d0000210741012102200341016a2d000021082003410c6a28020021090240200341106a280200220a450d00200a417f4c0d0541002d00fca3c680001a200a41002802c8a3c68000118180808000002202450d040b20022009200a10848e80800021090240200128024822022001280240470d00200141c0006a200210df84808000200128024821020b2001280244200241146c6a220220083a00012002200a3602102002200936020c2002200a360208200241003a0000200241046a20053a0000200241036a20063a0000200241026a20073a00002001200128024841016a3602480b200341146a21032004416c6a22040d000b0b200141306a41086a200141c0006a41086a2802003602002001200129024037033010988a808000200141c0006a10ea888080000240024020012802402203418180808078460d00200128024821020240200341808080807872418080808078460d00200128024441002802c0a3c68000118080808000000b20020d004200210b4200210c0c010b200141206a10ad8a8080002001290320210b2001290328210c200141106a10918a8080002001290310210d2001290318210e200142003702f0022001418097c380003602ec0220014180808080783602e802200142fc90b9e5d09296e777370348200142a6d4e6f1a4dd959860370340200142a0b9ab8f9ae5999778370358200142f999a7c78cd1d1cdb17f370350200141e8026a200141c0006a4120109288808000200c200e7c220e200c542103200b200d7c220c200b542102024020012802e8022204418080808078460d002004450d0020012802ec0241002802c0a3c68000118080808000000b427f200e20031b210b427f200c20021b210c0b200041206a2000200141306a10968a8080002001200029032010d0878080002001290308210d2001290300210e200141c0006a10e189808000200141d0026a290300210f20012903c8022110200141e8026a10ce88808000200141e8026a41286a2203427f20032903002211427f200f427f200b200d7c220d200d200b541b220b7c220d200d200b541b7c220b200b2011541b220b370300200141e8026a41206a2203427f2003290300220d427f2010427f200c200e7c220e200e200c541b220c7c220e200e200c541b7c220c200c200d541b220c370300200141f0006a200b370300200141c0006a41286a200c370300200141c0006a41206a200141e8026a41186a290300370300200141c0006a41186a200141e8026a41106a290300370300200141c0006a41106a200141e8026a41086a290300370300200120012903e802370348200142013703402001200141c0006a41086a36029c03200142fc90b9e5d09296e7773703a803200142a6d4e6f1a4dd9598603703a003200142f89aef8eef91e1ce967f3703b803200142b4d6d6dfccc6b592c3003703b0032001419c036a200141a0036a412010f98c808000200142003702e802200142fc90b9e5d09296e777370348200142a6d4e6f1a4dd95986037034020014293bb8a9cbb9ab6b31a370358200142ffabedd185d3d8d216370350200141e8026a200141c0006a412010948880800002402001280238220a450d002001280234220521034100210203402005200241146c6a21040240024002400240024020032d00000e0400010102040b200341086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200241016a2102200341146a2103200a417f6a220a0d000b0b02402001280230450d00200128023441002802c0a3c68000118080808000000b200141c0036a24808080800041000f0b4101200a10b280808000000b10ae80808000000b970802027f017e23808080800041b0056b2202248080808000200241146a200110f78c808000200228021c2103200241f0026a200141e80110848e8080001a200241c8016a200241f0026a200210808d808000024002400240024020022802c8012201450d00200220022d00ce013a00f204200220022f01cc013b01f004200241206a410772200241c8016a41077241a10110848e8080001a200220022f01f0043b0124200220022d00f2043a00262002200136022002400240024002400240200141736a2201410220014104491b0e0400010203000b200241d8046a200241286a10ba8a8080000c030b200241d8046a200241286a10e4878080000c020b200241d8046a200241206a10bb888080000c010b200241d8046a200241286a108e8c8080000b10ed88808000450d010c020b200220022d00ce0122013a00f204200220022f01cc0122033b01f004200041036a20013a0000200020033b00012000410f3a00002002280214450d02200228021841002802c0a3c68000118080808000000c020b10fe8c8080000b200241f0026a41086a200241146a41086a280200360200200220022902143703f002200241086a41e6aac48000411010d088808000200228020c410020022802081b200241f0026a10ff87808000200241f0026a200241206a41a80110848e8080001a200241c8016a200241f0026a200241d8046a200310be878080000240024020022903c80122044203510d00200241f0046a41136a200241c8016a41136a290000370000200241f0046a411b6a200241c8016a411b6a290000370000200241f0046a41236a200241c8016a41236a290000370000200241f0046a412b6a200241c8016a412b6a290000370000200241f0046a41306a200241c8016a41306a290000370000200241f0046a410a6a200241c8016a410a6a2d000022013a0000200220022f01d00122033b01a805200220022900d3013700fb04200220013a00aa05200220033b01f804200220043703f004024020044202510d0020022d00e80441ff01714102460d020b200241f0026a41106a200241d8046a41106a290300370300200241f0026a41086a2201200241d8046a41086a290300370300200220022903d8043703f002200241f0046a200241f0026a10998a808000410e2103024020022903f0044202510d00200120024199056a290000370300200241ff026a200241a0056a28000036000020022002290091053703f00220022d00900521030b200020033a0000200020022903f002370001200041096a2001290300370000200041106a200241ff026a2800003600000c020b200220022f01d00122013b01a8052002200241d2016a2d000022033a00aa05200041036a20033a0000200020013b00012000410f3a00000c010b2000410f3b0100200041026a41083a00000b200241b0056a2480808080000b5c01017f23808080800041206b22012480808080002001410c6a420137020020014101360204200141a085c68000360200200141e08180800036021c200120003602182001200141186a3602082001419085c6800010f680808000000bcf0303017f027e017f2380808080004190036b2200248080808000200042fc90b9e5d09296e777370348200042a6d4e6f1a4dd959860370340200042beee8ae9ce8fa2b1977f37035820004295d4f9afa2868fb864370350200041013a0010200041c0006a4120200041106a410141002802e0a1c6800011868080800000200042fc90b9e5d09296e777370348200042a6d4e6f1a4dd959860370340200042d3d8c5d2a9b9d2c1ac7f37035820004282ca868eabf3add0cf003703502000200041c0006a10e688808000200041106a10ce88808000200041c0006a10e189808000024020002903d802427f427f2000290310220120002903207c220220022001541b220120002903307c220220022001541b580d00200041e0026a290300427f427f20002903182201200041286a2903007c220220022001541b2201200041386a2903007c220220022001541b580d00200041c8006a220310ce8880800020004201370340200020033602ec02200042fc90b9e5d09296e7773703f802200042a6d4e6f1a4dd9598603703f002200042f89aef8eef91e1ce967f37038803200042b4d6d6dfccc6b592c30037038003200041ec026a200041f0026a412010f98c8080000b20004190036a2480808080000ba90102017f017e23808080800041306b2201248080808000024010ed888080000d0010fe8c8080000b109b8a808000200142fc90b9e5d09296e777370318200142a6d4e6f1a4dd959860370310200142d3d8c5d2a9b9d2c1ac7f37032820014282ca868eabf3add0cf003703202001200141106a10e6888080002001290308420020012802001b220210f88c808000200210cf878080002000109d8a808000200141306a2480808080000bed0a020a7f017e23808080800041c0066b220324808080800041022104024002400240200141c0016a22052d00004102470d00200341306a200141d80010848e8080001a0c010b200341e8016a41186a200141b0016a290000370300200341e8016a41106a200141a8016a290000370300200341e8016a41086a200141a0016a290000370300200320014198016a2900003703e801200341e0016a20014190016a290000370300200341d8016a20014188016a290000370300200341a8016a41286a20014180016a290000370300200341a8016a41206a200141f8006a290000370300200341a8016a41186a200141f0006a290000370300200341a8016a41106a200141e8006a290000370300200341a8016a41086a200141e0006a290000370300200320012900583703a801200341e8046a41286a2204200141e0016a290300370300200341e8046a41206a2206200141d8016a290300370300200341e8046a41186a2207200141d0016a290300370300200341e8046a41106a2208200141c8016a290300370300200341e8046a41086a220920052903003703002003200141b8016a2903003703e8042003419e056a200341e8046a10d4888080000240024020032d009e0522054102470d00200320032f009f053b01e4042003200341a1056a2d00003a00e604200110f68c8080000c010b200341c0036a41086a200341aa056a290000370300200341c0036a41106a200341b2056a290000370300200341d5036a220a200341b7056a290000370000200341e2036a220b200341a1056a2d00003a0000200320032900a2053703c003200320032f009f053b01e0032003200141066a2d00003a00e604200320012f01043b01e4042001280200210c200341bf056a200141076a41d10010848e8080001a200341b8066a2004290300370000200341b0066a2006290300370000200341a8066a2007290300370000200341a0066a200829030037000020034198066a2009290300370000200320032903e80437009006200341e3036a200341bf056a41810110848e8080001a200c450d00200320032d00e6043a00ba03200320032f01e4043b01b80320034188026a410772200341e3036a41810110848e8080001a20034193036a200b2d00003a00002003419c036a200341c0036a41086a290300370200200341a4036a200341c0036a41106a290300370200200341a9036a200a290000370000200320032f01e0033b009103200320032903c00337029403200320032f01b8033b018c02200320032d00ba033a008e02200320053a0090032003200c36028802024020034188026a200341a8016a200341e8016a10b7898080000d002000410036020020004180083b010420034188026a10f68c8080000c030b200341306a20034188026a41d80010848e8080001a200341086a41086a200341f1026a290000370300200341086a41106a200341f9026a290000370300200341086a41186a20034181036a290000370300200341276a20034188036a29000037000020034190016a200341e8016a41086a29030037030020034198016a200341e8016a41106a290300370300200341a0016a200341e8016a41186a290300370300200320032900e902370308200320032903e8013703880120032d00e802210420032903e002210d0c010b200320032d00e60422013a00ba03200320032f01e40422043b01b803200041066a20013a0000200020043b0104200041003602000c010b2000200341306a41f80010848e808000220120043a0080012001200d370378200120032903083700810120014189016a200341106a29030037000020014191016a200341186a29030037000020014199016a200341206a290300370000200141a0016a200341276a2900003700000b200341c0066a2480808080000b9f0601057f23808080800041e0046b2204248080808000200442fc90b9e5d09296e7773703f802200442a6d4e6f1a4dd9598603703f002200442d3d8c5d2a9b9d2c1ac7f3703880320044282ca868eabf3add0cf0037038003200441086a200441f0026a10e6888080002004200429031042017c420120042802081b3703c801200441003602f80220044280808080c0003702f002200441c8016a2003200441f0026a10968a808000024020042802f8022205450d0020042802f402220621034100210703402006200741146c6a21080240024002400240024020032d00000e0400010102040b200341086a21080c020b200841086a21080c010b200841046a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200741016a2107200341146a21032005417f6a22050d000b0b024020042802f002450d0020042802f40241002802c0a3c68000118080808000000b200441f0026a200210f78c80800020042802f8022107024020042802f002450d0020042802f40241002802c0a3c68000118080808000000b200441f0026a200241e80110848e8080001a200441c8016a200441f0026a200310808d8080000240024020042802c8012203450d00200420042d00ce013a00c201200420042f01cc013b01c001200441186a410772200441c8016a41077241a10110848e8080001a200420042f01c0013b011c200420042d00c2013a001e2004200336021802400240024002400240200341736a2203410220034104491b0e0400010203000b200441f0026a200441206a10ba8a8080000c030b200441f0026a200441206a10e4878080000c020b200441f0026a200441186a10bb888080000c010b200441f0026a200441206a108e8c8080000b024020042d0080034102470d00200041808080807836021020004180123b0100200441186a10f68c8080000c020b2000200441186a2001200441f0026a200710c087808000200441186a10f68c8080000c010b200420042d00ce0122033a00c201200420042f01cc0122073b01c001200041026a20033a0000200020073b010020004180808080783602100b200441e0046a2480808080000bc40904067f017e027f017e23808080800041d0006b2201248080808000200141286a41a885c68000410b41b385c68000411641fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d00200242a5e9e3ab9e929adc2c370308200241ef8080800036021820024107360204200241c985c68000360200200241106a4293888c8f89fdc6ec9e7f370300200120023602402001200241206a36024c2001200236024420014101360248200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024207370224200241c7fbc480003602202002410436021c20024193fcc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024207370224200241c7fbc480003602202002410836021c2002418bfcc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c3703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024207370224200241c7fbc480003602202002410636021c20024185fcc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200141c0006a41086a200141106a41086a28020041016a220236020020012001290310220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420a370224200241fbfbc480003602202002410536021c200241f6fbc480003602182002418684808000360210200242dcf29fddf2bfe9f9f300370308200242a7a6d9c6f090c49632370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000ba00804067f017e027f017e23808080800041d0006b2201248080808000200141286a41d085c68000410b41b385c68000411641fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d00200242a5e9e3ab9e929adc2c370308200241ef8080800036021820024107360204200241c985c68000360200200241106a4293888c8f89fdc6ec9e7f370300200120023602402001200241206a36024c2001200236024420014101360248200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c2002420e37022420024197fcc480003602202002410236021c200241cefbc480003602182002418784808000360210200242a8c7daf8defb9a8fc400370308200242a7b98ffce8cbcbd38b7f370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024207370224200241c7fbc480003602202002410636021c200241c1fbc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c3703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024207370224200241acfcc480003602202002410736021c200241a5fcc48000360218200241888480800036021020024282b0a8d2a7f3c7e316370308200242e59ffc9df1de8b92867f370300200128021821082001280214210220012001280210360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141106a41076a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bbe0203017e047f017e20002903102102024020012802002203200128020822046b41074b0d0020012004410810b18280800020012802002103200128020821040b2001200441086a22053602082001280204220620046a2002370000200029031821020240200320056b41074b0d0020012005410810b1828080002001280200210320012802042106200128020821050b2001200541086a2204360208200620056a2002370000200029032021020240200320046b41074b0d0020012004410810b18280800020012802042106200128020821040b200620046a20023700002001200441086a2205360208200041086a2903002102200029030021070240200128020020056b410f4b0d0020012005411010b182808000200128020821050b2001200541106a360208200128020420056a22012002370008200120073700000bcd0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41db85c68000410b41b385c68000411641fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242a7b98ffce8cbcbd38b7f370308200241c985c68000360220200241878480800036021820024111360204200241e685c68000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a42a8c7daf8defb9a8fc400370300200241386a41ef80808000360200200241246a410736020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024211370224200241b3fcc480003602202002410236021c200241cefbc480003602182002418784808000360210200242a8c7daf8defb9a8fc400370308200242a7b98ffce8cbcbd38b7f370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024207370224200241c7fbc480003602202002410636021c200241c1fbc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b950901027f23808080800041c0016b220424808080800041002d00fca3c680001a024002400240410141002802c8a3c68000118180808000002205450d00200541003a0000200441246a419380c6800041014100280298a3c6800011858080800000200441206a410036020020044201370214200420053602102004410136020c200141386a210520012d00790d0120012d00780d022004200136024420042004410c6a3602480240024020030d00200441f0006a41186a200541186a290000370300200441f0006a41106a200541106a290000370300200441f0006a41086a200541086a2900003703002004200529000037037020044190016a41286a200241286a28020036020020044190016a41206a200241206a29020037030020044190016a41186a200241186a29020037030020044190016a41106a200241106a29020037030020044190016a41086a200241086a2902003703002004200229020037039001200441cc006a200441c4006a200441f0006a20044190016a4100419c87c680004100418087c6800010ce898080000c010b200441f0006a41186a200541186a290000370300200441f0006a41106a200541106a290000370300200441f0006a41086a200541086a2900003703002004200529000037037020044190016a41286a200241286a28020036020020044190016a41206a200241206a29020037030020044190016a41186a200241186a29020037030020044190016a41106a200241106a29020037030020044190016a41086a200241086a2902003703002004200229020037039001200441cc006a200441c4006a200441f0006a20044190016a4100419c87c680004100418087c6800010cf898080000b0240024020042d004c0d00200441a8016a200441e5006a290000370300200441a0016a200441dd006a29000037030020044198016a200441d5006a2900003703002004200429004d370390010c010b20044190016a41086a200541086a29000037030020044190016a41106a200541106a29000037030020044190016a41186a200541186a290000370300200420052900003703900102400240024002402004280250220528020041fcffffff076a2202410320024105491b0e0403030102000b2005280204450d02200541086a28020041002802c0a3c68000118080808000000c020b2005280204450d01200541086a28020041002802c0a3c68000118080808000000c010b200510878d8080000b200541002802c0a3c68000118080808000000b2000200429039001370000200041186a20044190016a41186a290300370000200041106a20044190016a41106a290300370000200041086a20044190016a41086a290300370000200041d0006a2004410c6a41306a290200370200200041c8006a2004410c6a41286a290200370200200041c0006a2004412c6a290200370200200041386a2004410c6a41186a290200370200200041306a2004410c6a41106a290200370200200041286a2004410c6a41086a2902003702002000200429020c370220200441c0016a2480808080000f0b4101410110b280808000000b200141fa006a200510ee87808000000b200141f9006a108888808000000bc50101027f024002400240024020002802002201418080808078732202410220024104491b0e03030301000b0240024002402000280204220028020041fcffffff076a2202410320024105491b0e0404040102000b2000280204450d03200041086a28020041002802c0a3c68000118080808000000c030b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b200010878d8080000c010b2001450d01200028020421000b200041002802c0a3c68000118080808000000b0bde0f03047f017e017f2380808080004180026b2205248080808000200541086a41014101412110fb8a80800041002d00fca3c680001a024002400240410141002802c8a3c68000118180808000002206450d00200641003a0000200541c0006a419380c6800041014100280298a3c68000118580808000002005413c6a4100360200200542013702302005200636022c20054101360228200541b0016a200210a085808000200141f9006a2106200141386a210720012d00780d0120062d00000d0220052802b801210820052802b4012106200541f4016a41a08ac48000360200200541e8016a4100360200200520073602f801200520013602f001200542003702e001200541003602d801200542003702d0012005418c016a200541d0016a2006200810dd878080000240024002400240200528028c012207418180808078460d002007418080808078460d012005290290012209422088a722084120460d024120200841f086c6800010a281808000000b0240024002400240200528029001220728020041fcffffff076a2208410320084105491b0e0403030102000b2007280204450d02200741086a28020041002802c0a3c68000118080808000000c020b2007280204450d01200741086a28020041002802c0a3c68000118080808000000c010b200710878d8080000b200741002802c0a3c6800011808080800000024020052802b001450d00200641002802c0a3c68000118080808000000b200541e0006a41086a200541086a41086a290000370300200541e0006a41106a200541086a41106a290000370300200541e0006a41186a200541086a41186a290000370300200520052900083703600c020b024020052802b001450d00200641002802c0a3c68000118080808000000b200541e0006a41086a200541086a41086a290000370300200541e0006a41106a200541086a41106a290000370300200541e0006a41186a200541086a41186a290000370300200520052900083703600c010b200528029001210a200541e0006a41186a2009a7220841186a290000370300200541e0006a41106a200841106a290000370300200541e0006a41086a200841086a2900003703002005200829000037036002402007450d00200a41002802c0a3c68000118080808000000b20052802b001450d00200641002802c0a3c68000118080808000000b20052001360284012005200541286a3602880120022802082101200228020421020240024020040d00200541b0016a41186a200541e0006a41186a290300370300200541b0016a41106a200541e0006a41106a290300370300200541b0016a41086a200541e0006a41086a290300370300200520052903603703b001200541d0016a41206a200341206a280200360200200541d0016a41186a200341186a290200370300200541d0016a41106a200341106a290200370300200541d0016a41086a200341086a290200370300200520032902003703d0012005418c016a2002200120054184016a200541b0016a200541d0016a4100419c87c680004100418087c6800010d1898080000c010b200541b0016a41186a200541e0006a41186a290300370300200541b0016a41106a200541e0006a41106a290300370300200541b0016a41086a200541e0006a41086a290300370300200520052903603703b001200541d0016a41206a200341206a280200360200200541d0016a41186a200341186a290200370300200541d0016a41106a200341106a290200370300200541d0016a41086a200341086a290200370300200520032902003703d0012005418c016a2002200120054184016a200541b0016a200541d0016a4100419c87c680004100418087c6800010d0898080000b0240024020052d008c010d00200541e8016a200541a5016a290000370300200541e0016a2005419d016a290000370300200541d8016a20054195016a2900003703002005200529008d013703d0010c010b200541d0016a41086a200541e0006a41086a290300370300200541d0016a41106a200541e0006a41106a290300370300200541d0016a41186a200541e0006a41186a290300370300200520052903603703d0010240024002400240200528029001220128020041fcffffff076a2203410320034105491b0e0403030102000b2001280204450d02200141086a28020041002802c0a3c68000118080808000000c020b2001280204450d01200141086a28020041002802c0a3c68000118080808000000c010b200110878d8080000b200141002802c0a3c68000118080808000000b200541b0016a41086a2201200541d0016a41086a290300370300200541b0016a41106a2203200541d0016a41106a290300370300200541b0016a41186a2202200541d0016a41186a290300370300200520052903d0013703b001200541b0016a200541086a412010888e8080002106200041196a2002290300370000200041116a2003290300370000200041096a2001290300370000200020052903b001370001200020052902283702242000412c6a200541286a41086a290200370200200041346a200541286a41106a2902003702002000413c6a200541286a41186a290200370200200041c4006a200541286a41206a290200370200200041cc006a200541d0006a290200370200200041d4006a200541d8006a29020037020020002006453a000020054180026a2480808080000f0b4101410110b280808000000b20062007108788808000000b200141fa006a200710ee87808000000b9b0201037f23808080800041206b22022480808080000240024002400240024020002d000022030e050001020304000b2002200041016a36021420022000410c6a2902003702180c030b2002200041016a36021420022000410c6a2902003702180c020b2002200041016a36021420022000410c6a2902003702180c010b2002200041086a2902003702140b20022003360210200241046a200241106a10ed848080002002280208210402402001280200200128020822006b200228020c22034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a36020802402002280204450d00200441002802c0a3c68000118080808000000b200241206a2480808080000b9f0201037f23808080800041206b22042480808080000240024002400240200141146c41046a22050d00410121060c010b2005417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002206450d020b20044100360214200420063602102004200536020c200420013602182004200441186a36021c2004411c6a2004410c6a10c08a80800002402001450d00200141146c2101034020002004410c6a10de8c808000200041146a21002001416c6a22010d000b0b200428020c21002002200320042802102201200428021441002802e0a1c680001186808080000002402000450d00200141002802c0a3c68000118080808000000b200441206a2480808080000f0b10ae80808000000b4101200510b280808000000bae0503037f017e067f23808080800041106b2203248080808000410121044101210502402000290300220642c000540d0041022105200642808001540d00410421052006428080808004540d004109200679a74103766b21050b02402000290308220642c000540d0041022104200642808001540d00410421042006428080808004540d004109200679a74103766b21040b410121074101210802402000290310220642c000540d0041022108200642808001540d00410421082006428080808004540d004109200679a74103766b21080b0240200041186a2209290300220642c000540d0041022107200642808001540d00410421072006428080808004540d004109200679a74103766b21070b4101210a4101210b02402000290320220642c000540d004102210b200642808001540d004104210b2006428080808004540d004109200679a74103766b210b0b0240200041286a220c290300220642c000540d004102210a200642808001540d004104210a2006428080808004540d004109200679a74103766b210a0b41002d00fca3c680001a0240200420056a20086a20076a200b6a200a6a220441002802c8a3c68000118180808000002207450d002003200736020420032004360200200341003602082003200036020c2003410c6a200310c18a8080002003200041086a36020c2003410c6a200310c18a8080002003200041106a36020c2003410c6a200310c18a8080002003200936020c2003410c6a200310c18a8080002003200041206a36020c2003410c6a200310c18a8080002003200c36020c2003410c6a200310c18a808000200328020021002001200220032802042204200328020841002802e0a1c680001186808080000002402000450d00200441002802c0a3c68000118080808000000b200341106a2480808080000f0b4101200410b280808000000be40201047f23808080800041206b2203248080808000024002402002417f4c0d0041002d00fca3c680001a200241e8016c410472220441002802c8a3c68000118180808000002205450d012003410036020c2003200536020820032004360204200320023602102003200341106a360214200341146a200341046a10c08a80800002402002450d00200241e8016c2106200328020c21020340200341146a200110f78c808000200328021821050240200328020420026b200328021c22044f0d00200341046a2002200410b182808000200328020c21020b200328020820026a2005200410848e8080001a2003200220046a220236020c02402003280214450d00200541002802c0a3c68000118080808000000b200141e8016a2101200641987e6a22060d000b0b20002003290204370200200041086a200341046a41086a280200360200200341206a2480808080000f0b10ae80808000000b4101200410b280808000000b870503037f017e067f23808080800041106b2202248080808000410121034101210402402001290300220542c000540d0041022104200542808001540d00410421042005428080808004540d004109200579a74103766b21040b02402001290308220542c000540d0041022103200542808001540d00410421032005428080808004540d004109200579a74103766b21030b410121064101210702402001290310220542c000540d0041022107200542808001540d00410421072005428080808004540d004109200579a74103766b21070b0240200141186a2208290300220542c000540d0041022106200542808001540d00410421062005428080808004540d004109200579a74103766b21060b410121094101210a02402001290320220542c000540d004102210a200542808001540d004104210a2005428080808004540d004109200579a74103766b210a0b0240200141286a220b290300220542c000540d0041022109200542808001540d00410421092005428080808004540d004109200579a74103766b21090b41002d00fca3c680001a0240200320046a20076a20066a200a6a20096a220341002802c8a3c680001181808080000022060d004101200310b280808000000b200241086a2207410036020020022006360204200220033602002002200136020c2002410c6a200210c18a8080002002200141086a36020c2002410c6a200210c18a8080002002200141106a36020c2002410c6a200210c18a8080002002200836020c2002410c6a200210c18a8080002002200141206a36020c2002410c6a200210c18a8080002002200b36020c2002410c6a200210c18a808000200041086a200728020036020020002002290200370200200241106a2480808080000bd40801077f23808080800041206b220224808080800002400240417f200141206a2802002203410c6c220441156a22052005200441046a491b2205417f4c0d0041002d00fca3c680001a200541002802c8a3c68000118180808000002204450d012002410036020c2002200436020820022005360204024002402001280200418080808078470d00200241106a2001280204200141086a28020010b4828080000c010b200241106a2001280204200128020810e6848080000b410021062002280214210702402005200228021822084f0d00200241046a4100200810b18280800020022802082104200228020c21060b200420066a2007200810848e8080001a2002200620086a220536020c02402002280210450d00200741002802c0a3c68000118080808000000b200141146a2802002106200141106a280200210802400240200128020c418080808078470d00200241106a2008200610b4828080000c010b200241106a2008200610e6848080000b200228021421080240200228020420056b200228021822064f0d00200241046a2005200610b18280800020022802082104200228020c21050b200420056a2008200610848e8080001a2002200520066a220536020c02402002280210450d00200841002802c0a3c68000118080808000000b200128022421060240200228020420056b41034b0d00200241046a2005410410b18280800020022802082104200228020c21050b200420056a20063600002002200541046a220436020c200128022821050240200228020420046b41034b0d00200241046a2004410410b182808000200228020c21040b200228020820046a20053600002002200441046a220436020c200128022c21050240200228020420046b41034b0d00200241046a2004410410b182808000200228020c21040b200228020820046a20053600002002200441046a36020c2001411c6a28020021052002200336021c20022002411c6a360210200241106a200241046a10c08a808000200228020c210402402003450d0020052003410c6c6a2107034002402002280204220320046b41074b0d00200241046a2004410810b18280800020022802042103200228020c21040b2002280208220620046a20052902003700002002200441086a220436020c200541086a28020021080240200320046b41034b0d00200241046a2004410410b18280800020022802082106200228020c21040b200620046a20083600002002200441046a220436020c2005410c6a22052007470d000b0b200128023021050240200228020420046b41034b0d00200241046a2004410410b182808000200228020c21040b200228020820046a20053600002002200441046a220436020c20012d00342105024020022802042004470d00200241046a2004410110b182808000200228020c21040b200228020820046a20053a0000200041086a200441016a36020020002002290204370200200241206a2480808080000f0b10ae80808000000b4101200510b280808000000b811004047f027e057f027e2380808080004190026b2202248080808000024002402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241206a41086a200441086a290000370300200241206a41106a200441106a290000370300200241206a41186a200441186a29000037030020022004290000370320024002402001280200220328020422044108490d002003200441786a36020420032003280200220441086a3602002001280200220328020422054120490d01200429000021062003200541606a36020420032003280200220441206a360200200241a0016a41086a200441086a290000370300200241a0016a41106a200441106a290000370300200241a0016a41186a200441186a290000370300200220042900003703a001200241106a200110be8a8080002002290310a70d012001280200220328020422044120490d01200229031821072003200441606a36020420032003280200220441206a360200200241c0016a41086a200441086a290000370300200241c0016a41106a200441106a290000370300200241c0016a41186a200441186a290000370300200220042900003703c0012001280200220328020422044120490d012003200441606a36020420032003280200220441206a360200200241e0016a41086a200441086a290000370300200241e0016a41106a200441106a290000370300200241e0016a41186a200441186a290000370300200220042900003703e00120024184026a20011094878080002002280284022208418080808078460d0120024180016a41086a200241a0016a41086a220929030037030020024180016a41106a200241a0016a41106a220a29030037030020024180016a41186a200241a0016a41186a220b290300370300200241e0006a41086a200241c0016a41086a290300370300200241e0006a41106a200241c0016a41106a290300370300200241e0006a41186a200241c0016a41186a290300370300200220022903a00137038001200220022903c001370360200228028c022105200228028802210c200241c0006a41186a200241e0016a41186a290300370300200241c0006a41106a200241e0016a41106a290300370300200241c0006a41086a200241e0016a41086a290300370300200220022903e00137034002402001280200220328020422044120490d002003200441606a36020420032003280200220441206a3602002009200441086a290000370300200a200441106a290000370300200b200441186a290000370300200220042900003703a0012002200110be8a8080002002290300a70d002001280200220328020422044120490d002002290308210d2003200441606a36020420032003280200220441206a360200200241c0016a41086a200441086a290000370300200241c0016a41106a200441106a290000370300200241c0016a41186a200441186a290000370300200220042900003703c0012001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241e0016a41086a200441086a290000370300200241e0016a41106a200441106a290000370300200241e0016a41186a200441186a290000370300200220042900003703e00120024184026a20011094878080002002280284022201418080808078460d00200229028802210e200020022903a0013700a001200020022903c0013700c801200041b8016a200241a0016a41186a290300370000200041b0016a200241a0016a41106a290300370000200041a8016a200241a0016a41086a290300370000200041d0016a200241c0016a41086a290300370000200041d8016a200241c0016a41106a290300370000200041e0016a200241c0016a41186a290300370000200020022903e0013700e801200041f0016a200241e0016a41086a290300370000200041f8016a200241e0016a41106a29030037000020004180026a200241e0016a41186a2903003700002000200e37028c0220002001360288022000200d3703c00120002002290320370000200041086a200241206a41086a290300370000200041106a200241206a41106a290300370000200041186a200241206a41186a2903003700002000200229038001370328200041306a20024180016a41086a290300370300200041386a20024180016a41106a290300370300200041c0006a20024180016a41186a29030037030020002007370348200041e8006a200241e0006a41186a290300370300200041e0006a200241e0006a41106a290300370300200041d8006a200241e0006a41086a2903003703002000200229036037035020004188016a200241c0006a41186a29030037030020004180016a200241c0006a41106a290300370300200041f8006a200241c0006a41086a2903003703002000200229034037037020002005360298012000200c360294012000200836029001200020063703200c040b20004180808080783602880202402005450d00200c2101410021030340200c200341146c6a21040240024002400240024020012d00000e0400010102040b200141086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200341016a2103200141146a21012005417f6a22050d000b0b2008450d03200c41002802c0a3c68000118080808000000c030b2000418080808078360288020c020b2000418080808078360288020c010b2000418080808078360288020b20024190026a2480808080000b21002001280214419c88c68000410f200141186a28020028020c118280808000000b180020002802002001200028020428020c118380808000000bc80601097f2000280200210102400240024020002802202202450d0020002802042103034020002002417f6a220236022002400240024002400240024002402001450d0020030d0020002802082104200028020c2205450d03200541077122060d01200521030c020b2001450d04200028020c210620002802082105200321040c030b2005210303402003417f6a210320042802bc0221042006417f6a22060d000b0b20054108490d00034020042802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022104200341786a22030d000b0b2000420037020820002004360204410121012000410136020041002106410021050b02400240200620042f01ba02490d00034020042802b0012203450d0220042f01b8022106200441002802c0a3c6800011808080800000200541016a210520032104200620032f01ba024f0d000b200321040b200641016a2107024020050d00200421030c030b200420074102746a41bc026a2802002103410021072005417f6a2208450d022005417e6a2109024020084107712205450d0003402008417f6a210820032802bc0221032005417f6a22050d000b0b20094107490d02034020032802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022103200841786a22080d000c030b0b200441002802c0a3c6800011808080800000418cd0c2800010a081808000000b41d887c6800010a081808000000b2000200736020c2000410036020820002003360204024020042006410c6c6a41b4016a2205280200450d00200528020441002802c0a3c68000118080808000000b0240200420064104746a2204280200450d00200428020441002802c0a3c68000118080808000000b20020d000b200041003602000c010b200041003602002001450d01200028020422030d0020002802082103200028020c2205450d0002400240200541077122060d00200521040c010b2005210403402004417f6a210420032802bc0221032006417f6a22060d000b0b20054108490d00034020032802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022103200441786a22040d000b0b034020032802b0012104200341002802c0a3c68000118080808000002004210320040d000b0b0b02000b02000bba0402037f017e23808080800041a0026b22022480808080000240024002400240024002400240200028020022030d002002410c6a200141cc0010848e8080001a2002410a6a200141cf006a2d00003a0000200220012f004d3b010820012d004c2101410021040c010b200241d0016a20032000280204200110c48580800020022802d001450d01200241d8016a290200210520022802d40121042002410c6a200141cc0010848e8080001a2002410a6a200141cf006a2d00003a0000200220012f004d3b010820012d004c21010b41012103200141ff01714102460d03200220003602642002200537025c20022004360258200241e8006a2002410c6a41cc0010848e8080002103200241b7016a200241086a41026a2d00003a0000200220013a00b401200220022f01083b00b50120040d0141002d00fca3c680001a41f80641002802c8a3c68000118180808000002201450d04200141013b01f606200141003602f0062001200341d00010fe8d80800021012000428080808010370204200020013602000c020b41012103200141c8006a2802004129490d02200128022041002802c0a3c68000118080808000000c020b200241b8016a41086a200241d8006a41086a280200360200200220022902583703b801200241d0016a200341d00010848e8080001a200241c4016a200241b8016a200241d0016a200241e4006a10be8580800020022802642201200128020841016a3602080b410021030b200241a0026a24808080800020030f0b410441f80610b280808000000bfe0504027f017e097f017e23808080800041d0006b22042480808080000240024002400240200128020022050d002002290204210620022802002107410021050c010b20022802082108200228020421092001280204210a02400340200541b4016a210720052f01ba02220b410c6c210c417f210d0240024003400240200c0d00200b210d0c020b200741086a210e200741046a210f200c41746a210c200d41016a210d2007410c6a2107417f2009200f2802002008200e280200220e2008200e491b10888e808000220f2008200e6b200f1b220e410047200e4100481b220e4101460d000b200e41ff0171450d010b200a450d02200a417f6a210a2005200d4102746a41bc026a28020021050c010b0b2004200a36024420042005360240200429034021062002280200450d02200941002802c0a3c68000118080808000000c020b2004200d360248200441003602442002280200210720022902042106200429024421100b02402007418080808078470d002001210d0c010b200420103702202004200536021c20042001360218200420063702102004200736020c02400240024020050d0041002d00fca3c680001a41bc0241002802c8a3c68000118180808000002207450d02200741013b01ba02200741003602b0012007200429020c3702b4012004410c6a41086a280200210c20072003290200370200200741086a200341086a290200370200200741bc016a200c3602002001428080808010370204200120073602000c010b200441286a41086a2004411c6a220741086a28020036020020042007290200370328200441c0006a41086a2004410c6a41086a2802003602002004200429020c370340200441346a200441286a200441c0006a2003200441186a10bf8580800020042802182207200728020841016a3602080b200041063a00000c020b410441bc0210b280808000000b20002006a7200d4104746a220729020037020020072003290200370200200041086a200741086a22072902003702002007200341086a2902003702000b200441d0006a2480808080000bc20803097f017e017f23808080800041c0046b2203248080808000200320013602940220034100360290022003200236028c0220034198026a2003418c026a108f8d80800002400240024020032802a0042204418080808078460d0020032802ac04210120032802a804210520032802a404210620032802b003210720032802ac03210820032802a803210920032003418c026a10bc8a808000024020032802000d00200341b4046a2003418c026a200328020410d08580800020032802b404220a418080808078470d020b02402007450d00200821024100210103402008200141146c6a210b0240024002400240024020022d00000e0400010102040b200241086a210b0c020b200b41086a210b0c010b200b41046a210b0b200b280200450d00200b28020441002802c0a3c68000118080808000000b200141016a2101200241146a21022007417f6a22070d000b0b02402009450d00200841002802c0a3c68000118080808000000b02402005450d00200621024100210103402006200141146c6a21070240024002400240024020022d00000e0400010102040b200241086a21070c020b200741086a21070c010b200741046a21070b2007280200450d00200728020441002802c0a3c68000118080808000000b200141016a2101200241146a21022005417f6a22050d000b0b2004450d00200641002802c0a3c68000118080808000000b2000418080808078360288020c010b20033502bc04210c20032802b804210d200341f8006a20034198026a41900110848e8080001a2003410c6a200341b4036a41ec0010848e8080001a02402002280204450d0020004180808080783602880202402007450d00200821024100210103402008200141146c6a210b0240024002400240024020022d00000e0400010102040b200241086a210b0c020b200b41086a210b0c010b200b41046a210b0b200b280200450d00200b28020441002802c0a3c68000118080808000000b200141016a2101200241146a21022007417f6a22070d000b0b02402009450d00200841002802c0a3c68000118080808000000b02402005450d00200621024100210103402006200141146c6a21070240024002400240024020022d00000e0400010102040b200241086a21070c020b200741086a21070c010b200741046a21070b2007280200450d00200728020441002802c0a3c68000118080808000000b200141016a2101200241146a21022005417f6a22050d000b0b02402004450d00200641002802c0a3c68000118080808000000b200a450d01200d41002802c0a3c68000118080808000000c010b2000200341f8006a41900110848e8080002202200736029801200220083602940120022009360290012002419c016a2003410c6a41ec0010848e8080001a2002200c3703a0022002200d36029c022002200a36029802200220013602940220022005360290022002200636028c0220022004360288020b200341c0046a2480808080000b810c03027f017e037f2380808080004190036b2203248080808000200320013602bc01200341003602b801200320023602b401024002400240200228020422014120490d002002200141606a36020420022002280200220141206a360200200341a0026a41086a200141086a290000370300200341a0026a41106a200141106a290000370300200341a0026a41186a200141186a290000370300200320012900003703a002200341186a200341b4016a10be8a8080002003290318a70d0020032802b401220128020422044120490d00200329032021052001200441606a36020420012001280200220441206a360200200341c0026a41086a200441086a290000370300200341c0026a41106a200441106a290000370300200341c0026a41186a200441186a290000370300200320042900003703c00220032802b401220128020422044120490d002001200441606a36020420012001280200220441206a360200200341e0026a41086a200441086a290000370300200341e0026a41106a200441106a290000370300200341e0026a41186a200441186a290000370300200320042900003703e002200341106a200341b4016a10bc8a80800020032802100d0020034184036a200341b4016a200328021410c7858080002003280284032206418080808078460d0020034180026a41086a200341a0026a41086a29030037030020034180026a41106a200341a0026a41106a29030037030020034180026a41186a200341a0026a41186a290300370300200341e0016a41086a200341c0026a41086a290300370300200341e0016a41106a200341c0026a41106a290300370300200341e0016a41186a200341c0026a41186a290300370300200320032903a00237038002200320032903c0023703e001200328028c0321042003280288032107200341c0016a41186a200341e0026a41186a290300370300200341c0016a41106a200341e0026a41106a290300370300200341c0016a41086a200341e0026a41086a290300370300200320032903e0023703c001200341086a200341b4016a10bc8a808000024020032802080d00200341e0026a200341b4016a200328020c10cc8580800020032802e0022201418080808078470d020b02402004450d00200721024100210103402007200141146c6a21080240024002400240024020022d00000e0400010102040b200241086a21080c020b200841086a21080c010b200841046a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200141016a2101200241146a21022004417f6a22040d000b0b2006450d00200741002802c0a3c68000118080808000000b20004180808080783602780c010b200341286a41086a20034180026a41086a290300370300200341286a41106a20034180026a41106a290300370300200341286a41186a20034180026a41186a290300370300200341d8006a200341e0016a41086a290300370300200341e0006a200341e0016a41106a290300370300200341e8006a200341e0016a41186a290300370300200320032903800237032820032005370348200320032903e00137035020032902e402210520034188016a200341c0016a41186a29030037030020034180016a200341c0016a41106a290300370300200341f8006a200341c0016a41086a290300370300200320063602900120032007360294012003200436029801200320032903c001370370200320013602a001200320053702a40102402002280204450d00200041808080807836027802402004450d00200721024100210103402007200141146c6a21000240024002400240024020022d00000e0400010102040b200241086a21000c020b200041086a21000c010b200041046a21000b2000280200450d00200028020441002802c0a3c68000118080808000000b200141016a2101200241146a21022004417f6a22040d000b0b200341a0016a210202402006450d00200741002802c0a3c68000118080808000000b2002108f8780800020032802a001450d0120032802a40141002802c0a3c68000118080808000000c010b2000200341286a41880110848e8080001a0b20034190036a2480808080000be61003027f017e047f23808080800041d0046b2203248080808000200320013602d401200341003602d001200320023602cc0102400240200228020422014120490d002002200141606a36020420022002280200220141206a360200200341e0036a41086a200141086a290000370300200341e0036a41106a200141106a290000370300200341e0036a41186a200141186a290000370300200320012900003703e003200341206a200341cc016a10be8a8080002003290320a70d0020032802cc01220128020422044120490d00200329032821052001200441606a36020420012001280200220441206a36020020034180046a41086a200441086a29000037030020034180046a41106a200441106a29000037030020034180046a41186a200441186a290000370300200320042900003703800420032802cc01220128020422044120490d002001200441606a36020420012001280200220441206a360200200341a0046a41086a200441086a290000370300200341a0046a41106a200441106a290000370300200341a0046a41186a200441186a290000370300200320042900003703a004200341186a200341cc016a10bc8a80800020032802180d00200341c4046a200341cc016a200328021c10c78580800020032802c4042206418080808078460d00200341c0036a41086a200341e0036a41086a290300370300200341c0036a41106a200341e0036a41106a290300370300200341c0036a41186a200341e0036a41186a290300370300200341a0036a41086a20034180046a41086a290300370300200341a0036a41106a20034180046a41106a290300370300200341a0036a41186a20034180046a41186a290300370300200320032903e0033703c00320032003290380043703a00320032802cc04210420032802c804210720034180036a41186a200341a0046a41186a29030037030020034180036a41106a200341a0046a41106a29030037030020034180036a41086a200341a0046a41086a290300370300200320032903a00437038003200341106a200341cc016a10bc8a8080000240024020032802100d00200341a0046a200341cc016a200328021410cc8580800020032802a0042201418080808078470d010b02402004450d00200721024100210103402007200141146c6a21080240024002400240024020022d00000e0400010102040b200241086a21080c020b200841086a21080c010b200841046a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200141016a2101200241146a21022004417f6a22040d000b0b2006450d01200741002802c0a3c68000118080808000000c010b200341f8016a41086a200341c0036a41086a290300370300200341f8016a41106a200341c0036a41106a290300370300200341f8016a41186a200341c0036a41186a290300370300200341a8026a200341a0036a41086a290300370300200341b0026a200341a0036a41106a290300370300200341b8026a200341a0036a41186a290300370300200320032903c0033703f8012003200537039802200320032903a0033703a00220032902a4042105200341d8026a20034180036a41186a290300370300200341d0026a20034180036a41106a290300370300200341c8026a20034180036a41086a290300370300200320063602e002200320073602e402200320043602e80220032003290380033703c002200320013602f002200320053702f402200341086a200341cc016a10bc8a8080000240024020032802080d00200328020c2109200320032802d00141016a22083602d001200820032802d4014b0d00200341003a00c003200320093602a804200341003602a4042003200341cc016a3602a0042003200341c0036a3602ac0420034180046a200341a0046a10d28c80800020032d00c003450d0120034180046a10d38c808000200320032802d001417f6a3602d0010b02402004450d00200721024100210103402007200141146c6a21080240024002400240024020022d00000e0400010102040b200241086a21080c020b200841086a21080c010b200841046a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200141016a2101200241146a21022004417f6a22040d000b0b200341f0026a210202402006450d00200741002802c0a3c68000118080808000000b2002108f8780800020032802f002450d0120032802f40241002802c0a3c68000118080808000000c010b200341e0036a41086a20034180046a41086a2802002204360200200341ec016a2004360200200320032902800422053703e003200320053702e401200341306a200341f8016a41f80010848e8080001a200341d8016a41086a2204200341f4026a220841086a280200360200200341bc016a200341e8016a290300370200200341c4016a200341f0016a280200360200200341b4016a20042903003702002003200829020022053703d801200320053702ac01200320013602a80102402002280204450d0020004180808080783602782003419c016a28020021080240200341a0016a2802002204450d00200821024100210103402008200141146c6a21000240024002400240024020022d00000e0400010102040b200241086a21000c020b200041086a21000c010b200041046a21000b2000280200450d00200028020441002802c0a3c68000118080808000000b200141016a2101200241146a21022004417f6a22040d000b0b200341306a41f8006a21020240200328029801450d00200841002802c0a3c68000118080808000000b2002108f87808000024020032802a801450d0020032802ac0141002802c0a3c68000118080808000000b200341b8016a10d38c8080000c020b2000200341306a41980110848e8080001a0c010b20004180808080783602780b200341d0046a2480808080000bd60f02137f037e2380808080004180026b220224808080800041002d00fca3c680001a024002400240024002400240410141002802c8a3c68000118180808000002203450d00200341003a0000200241186a2204419380c6800041014100280298a3c6800011858080800000200241146a4100360200200242013702082002200336020420024101360200024020012802002203450d0020012802082205450d00200241ac016a210620024188016a41146a21072002418c016a2108200241d8006a41106a21092002410c6a210a2001280204210b410021010340024002402001450d00200b210c2003210d200121030c010b4100210c0240200b450d00200b21010240200b410771220d450d0003402001417f6a2101200328028c012103200d417f6a220d0d000b0b200b4108490d000340200328028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012103200141786a22010d000b0b4100210d0b0240200c20032f018a01490d00034020032802002201450d05200d41016a210d20032f018801210c20012103200c20012f018a014f0d000b0b200c41016a210b02400240200d0d00200321010c010b2003200b4102746a418c016a28020021014100210b200d417f6a220e450d00200d417e6a210f0240200e410771220d450d000340200e417f6a210e200128028c012101200d417f6a220d0d000b0b200f4107490d000340200128028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012101200e41786a220e0d000b0b2003200c410c6c6a220c41086a2802002103024002400240200c410c6a2802002210450d002010417f4c0d0741002d00fca3c680001a201041002802c8a3c6800011818080800000220c450d0a200c2003201010848e808000210c2002280204210d0240024020102002280208220e460d00200c41002802c0a3c68000118080808000000c010b200c200d201010888e808000210f200c41002802c0a3c6800011808080800000200f450d030b200241386a200320104100280298a3c680001185808080000041002d00fca3c680001a201041002802c8a3c68000118180808000002211450d0820112003201010848e80800021032010200e470d012003200d201010888e8080000d01200341002802c0a3c68000118080808000000c020b2002280208450d01200241386a200341004100280298a3c6800011858080800000410121110b200241d8006a41186a200241386a41186a290200370300200241d8006a41106a200241386a41106a290200370300200241d8006a41086a200241386a41086a29020037030020022002290238370358024002400240200228020c220f0d004100210f200241386a21030c010b200228021021120340200f41dc026a210c200f2f01960422134105742114417f210d4100210302400340024020142003470d002013210d0c020b200f20036a210e200341206a2103200d41016a210d200c41106a210c417f200241d8006a200e412010888e808000220e410047200e4100481b220e4101460d000b200e41ff0171450d030b02402012450d002012417f6a2112200f200d4102746a4198046a280200210f0c010b0b200dad4220862115200241d8006a21030b200720092902002216370200200241f8006a41086a200941086a2902002217370300200741086a201737020020022002280264360298012002200a36028801200220153702b0012002200f3602ac012002201637037820022003280208360294012002200329020037028c01200241013602c401200220103602c001200220113602bc01200220103602b8010240200f0d0041002d00fca3c680001a41980441002802c8a3c68000118180808000002203450d09200841086a2900002115200841106a2900002116200841186a290000211720032008290000370000200341186a2017370000200341106a2016370000200341086a2015370000200341013b0196042003410036029004200320022902b8013702e002200341e8026a200241b8016a41086a29020037020020024280808080103702102002200336020c0c020b200241c8016a41086a200641086a280200360200200220062902003703c801200241e0016a41186a200841186a290000370300200241e0016a41106a200841106a290000370300200241e0016a41086a200841086a290000370300200220082900003703e001200241d4016a200241c8016a200241e0016a200241b8016a20024188016a10bd858080002002280288012203200328020841016a3602080c010b0240200c280200220341004a0d000240200c41746a220d280200450d00200c41786a28020041002802c0a3c6800011808080800000200c28020021030b200d2010360200200c200341016a360200200c417c6a2010360200200c41786a20113602000c010b200c200341016a3602002010450d00201141002802c0a3c68000118080808000000b410021032005417f6a22050d000b0b20002002290200370200200041306a200241306a290200370200200041286a200241286a290200370200200041206a200241206a290200370200200041186a2004290200370200200041106a200241106a290200370200200041086a200241086a29020037020020024180026a2480808080000f0b4101410110b280808000000b41e887c6800010a081808000000b10ae80808000000b4101201010b280808000000b410441980410b280808000000b4101201010b280808000000bea05010a7f23808080800041106b220324808080800002400240024002402001200041186a412010888e8080000d00410121010c010b200228020021040240024002402002280204220541216a22060d0020034201370208200320063602040c010b2006417f4c0d034100210741002d00fca3c680001a200641002802c8a3c68000118180808000002208450d042003410036020c20032008360208200320063602042005415f490d010b200341046a4100200510ab868080002003280204210620032802082108200328020c21070b200820076a2004200510848e8080001a2003200720056a220536020c024020022d0008450d00200241096a2d00002102024020052006470d00200341046a2006109c8680800020032802082108200328020c21050b200820056a20023a00002003200328020c41016a220536020c200328020421060b0240200620056b411f4b0d00200341046a2005412010ab8680800020032802042106200328020c21050b2003280208220920056a22022001290000370000200241086a200141086a290000370000200241106a200141106a290000370000200241186a200141186a29000037000002400240200028020c220a0d00410021010c010b200541206a2102200041106a280200210b0340200a417c6a2107200a41b4016a2101200a2f01ba02220c410c6c2105417f2108024002400340024020050d00200c21080c020b200141086a2100200141046a2104200541746a2105200841016a2108200741106a21072001410c6a2101417f2009200428020020022000280200220020022000491b10888e8080002204200220006b20041b220041004720004100481b22004101460d000b200041ff0171450d010b0240200b0d00410021010c030b200b417f6a210b200a20084102746a41bc026a280200210a0c010b0b200728020041004a21010b2006450d00200941002802c0a3c68000118080808000000b200341106a24808080800020010f0b10ae80808000000b4101200610b280808000000b1000200020012002200310e48c8080000b0e00200020012002109b8d8080000bac0901097f23808080800041206b220224808080800002400240024002400240024002400240024002400240024020012802042203450d0020012003417f6a36020420012001280200220441016a36020020042d00000e09010000000203040005000b200041053a00000c090b2002200110bd8a80800020022802000d042001280204220420022802042203490d0402400240024020030d00410121050c010b2003417f4c0d0b200341002802c8a3c68000118180808000002205450d01200541002003108a8e8080001a0b200520012802002206200310848e80800021052001200420036b3602042001200620036a36020020002003360204200041033a000020002003ad4220862005ad843702080c090b4101200310b280808000000b20034105490d0420012003417b6a3602042001200441056a360200200441046a2d00002105200441036a2d00002106200441026a2d0000210720042d00012104200241086a200110bd8a80800020022802080d0420012802042208200228020c2203490d0441012109024002402003450d002003417f4c0d0a200341002802c8a3c68000118180808000002209450d01200941002003108a8e8080001a0b20092001280200220a200310848e8080002109200041036a20063a0000200041026a20073a0000200020043a00012001200820036b3602042001200a20036a36020020002003360208200041013a0000200041046a20053a000020002003ad4220862009ad8437020c0c080b4101200310b280808000000b20034105490d0420012003417b6a3602042001200441056a360200200441046a2d00002105200441036a2d00002106200441026a2d0000210720042d00012104200241106a200110bd8a80800020022802100d042001280204220920022802142203490d0402400240024020030d00410121080c010b2003417f4c0d09200341002802c8a3c68000118180808000002208450d01200841002003108a8e8080001a0b20082001280200220a200310848e8080002108200041036a20063a0000200041026a20073a0000200020043a00012001200920036b3602042001200a20036a36020020002003360208200041023a0000200041046a20053a000020002003ad4220862008ad8437020c0c070b4101200310b280808000000b20034105490d0420012003417b6a3602042001200441056a360200200441046a2d00002105200441036a2d00002106200441026a2d0000210720042d00012104200241186a200110bd8a80800020022802180d0420012802042209200228021c2203490d0402400240024020030d00410121080c010b2003417f4c0d08200341002802c8a3c68000118180808000002208450d01200841002003108a8e8080001a0b20082001280200220a200310848e8080002108200041036a20063a0000200041026a20073a0000200020043a00012001200920036b3602042001200a20036a36020020002003360208200041003a0000200041046a20053a000020002003ad4220862008ad8437020c0c060b4101200310b280808000000b200041043a00000c040b200041053a00000c030b200041053a00000c020b200041053a00000c010b200041053a00000b200241206a2480808080000f0b10ae80808000000bc30602087f017e23808080800041306b220224808080800002400240024002400240024002400240024002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020020042d00000e09010000000203040005000b200041053a00000c090b2002200110bc8a80800020022802000d04200241246a2001200228020410d08580800020022802242203418080808078460d042000200229022837020820002003360204200041033a00000c080b2001280200220328020422054104490d042003280200220441036a2d0000210620042d0002210720042d0001210820042d000021092003200441046a36020020032005417c6a360204200241086a200110bc8a80800020022802080d04200241246a2001200228020c10d08580800020022802242203418080808078460d042002290228210a200041026a20083a0000200020093a00012000200a37020c20002003360208200041013a0000200041036a20073a0000200041046a20063a00000c070b2001280200220328020422054104490d042003280200220441036a2d0000210620042d0002210720042d0001210820042d000021092003200441046a36020020032005417c6a360204200241106a200110bc8a80800020022802100d04200241246a2001200228021410d08580800020022802242203418080808078460d042002290228210a200041026a20083a0000200020093a00012000200a37020c20002003360208200041023a0000200041036a20073a0000200041046a20063a00000c060b2001280200220328020422054104490d042003280200220441036a2d0000210620042d0002210720042d0001210820042d000021092003200441046a36020020032005417c6a360204200241186a200110bc8a80800020022802180d04200241246a2001200228021c10d08580800020022802242203418080808078460d042002290228210a200041026a20083a0000200020093a00012000200a37020c20002003360208200041003a0000200041036a20073a0000200041046a20063a00000c050b200041043a00000c040b200041053a00000c030b200041053a00000c020b200041053a00000c010b200041053a00000b200241306a2480808080000b21002001280214419c88c68000410f200141186a28020028020c118280808000000bd10a01077f024020002802002201450d0020002802042102024002400240200028020822030d00200121000c010b410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802e40c21012005417f6a22050d000b0b20024108490d00034020012802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2101200041786a22000d000b0b410021050b024002400240200520012f01e20c490d0003402001280288022200450d0220012f01e00c2105200141002802c0a3c6800011808080800000200441016a210420002101200520002f01e20c4f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a41e40c6a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802e40c21002004417f6a22040d000b0b20074107490d01034020002802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b02402001200541186c6a2204280200450d00200428020441002802c0a3c68000118080808000000b0240200428020c450d00200441106a28020041002802c0a3c68000118080808000000b2001200541fc006c6a2205418c026a21010240024020054184036a280200220541054b0d002005450d010240200128020c2204418080808078460d002004450d00200141106a28020041002802c0a3c68000118080808000000b02402001280200450d00200128020441002802c0a3c68000118080808000000b20054101460d010240200141246a2802002204418080808078460d002004450d00200141286a28020041002802c0a3c68000118080808000000b02402001280218450d002001411c6a28020041002802c0a3c68000118080808000000b20054102460d0102402001413c6a2802002204418080808078460d002004450d00200141c0006a28020041002802c0a3c68000118080808000000b02402001280230450d00200141346a28020041002802c0a3c68000118080808000000b20054103460d010240200141d4006a2802002204418080808078460d002004450d00200141d8006a28020041002802c0a3c68000118080808000000b02402001280248450d00200141cc006a28020041002802c0a3c68000118080808000000b20054104460d010240200141ec006a2802002205418080808078460d002005450d00200141f0006a28020041002802c0a3c68000118080808000000b2001280260450d01200141e4006a28020041002802c0a3c68000118080808000000c010b20012802002106024020012802042205450d0020062101034002402001410c6a2802002204418080808078460d002004450d00200141106a28020041002802c0a3c68000118080808000000b02402001280200450d00200141046a28020041002802c0a3c68000118080808000000b200141186a21012005417f6a22050d000b0b200641002802c0a3c68000118080808000000b410021012003417f6a22030d000b20000d01410021000b2002450d0002400240200241077122050d00200221010c010b2002210103402001417f6a210120002802e40c21002005417f6a22050d000b0b20024108490d00034020002802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2100200141786a22010d000b0b03402000280288022101200041002802c0a3c68000118080808000002001210020010d000b0b0ba511010e7f024020002802002201450d0020002802042102024002400240200028020822030d00200121040c010b410021040340024002402004450d002001210520022106200421010c010b4100210502402002450d0020022100024020024107712206450d0003402000417f6a210020012802ac0921012006417f6a22060d000b0b20024108490d00034020012802ac092802ac092802ac092802ac092802ac092802ac092802ac092802ac092101200041786a22000d000b0b410021060b024002400240200620012f01aa09490d00034020012802a0082200450d0220012f01a8092106200141002802c0a3c6800011808080800000200541016a210520002101200620002f01aa094f0d000b200021010b200641016a2102024020050d00200121040c020b200120024102746a41ac096a2802002104410021022005417f6a2200450d012005417e6a2107024020004107712205450d0003402000417f6a210020042802ac0921042005417f6a22050d000b0b20074107490d01034020042802ac092802ac092802ac092802ac092802ac092802ac092802ac092802ac092104200041786a22000d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b024020012006410c6c6a41a4086a2200280200450d00200028020441002802c0a3c68000118080808000000b2001200641e0006c6a220810a38d80800002400240200828020c220941054b0d002009450d01200841106a210a4100210b03400240200a200b410c6c6a22002802002201450d002000280204210c024002402000280208220d450d00410021000340024002402000450d0020012105200c2106200021010c010b410021050240200c450d00200c21000240200c4107712206450d0003402000417f6a2100200128028c0121012006417f6a22060d000b0b200c4108490d000340200128028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012101200041786a22000d000b0b410021060b024002400240200620012f018a01490d00034020012802002200450d0220012f0188012106200141002802c0a3c6800011808080800000200541016a210520002101200620002f018a014f0d000b200021010b200641016a210c024020050d00200121000c020b2001200c4102746a418c016a28020021004100210c2005417f6a2207450d012005417e6a210e024020074107712205450d0003402007417f6a2107200028028c0121002005417f6a22050d000b0b200e4107490d010340200028028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012100200741786a22070d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b024020012006410c6c6a41046a2201280200450d00200128020441002802c0a3c68000118080808000000b41002101200d417f6a220d0d000c020b0b0240200c0d00200121000c010b02400240200c41077122060d0020012100200c21010c010b20012100200c210103402001417f6a2101200028028c0121002006417f6a22060d000b0b200c4108490d000340200028028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012100200141786a22010d000b0b034020002802002101200041002802c0a3c68000118080808000002001210020010d000b0b200b41016a220b2009470d000c020b0b200828021021090240200841146a280200220a450d004100210b034002402009200b410c6c6a22002802002201450d002000280204210c024002402000280208220d450d00410021000340024002402000450d0020012105200c2106200021010c010b410021050240200c450d00200c21000240200c4107712206450d0003402000417f6a2100200128028c0121012006417f6a22060d000b0b200c4108490d000340200128028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012101200041786a22000d000b0b410021060b024002400240200620012f018a01490d00034020012802002200450d0220012f0188012106200141002802c0a3c6800011808080800000200541016a210520002101200620002f018a014f0d000b200021010b200641016a210c024020050d00200121000c020b2001200c4102746a418c016a28020021004100210c2005417f6a2207450d012005417e6a210e024020074107712205450d0003402007417f6a2107200028028c0121002005417f6a22050d000b0b200e4107490d010340200028028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012100200741786a22070d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b024020012006410c6c6a41046a2201280200450d00200128020441002802c0a3c68000118080808000000b41002101200d417f6a220d0d000c020b0b0240200c0d00200121000c010b02400240200c41077122060d0020012100200c21010c010b20012100200c210103402001417f6a2101200028028c0121002006417f6a22060d000b0b200c4108490d000340200028028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012100200141786a22010d000b0b034020002802002101200041002802c0a3c68000118080808000002001210020010d000b0b200b41016a220b200a470d000b0b200941002802c0a3c68000118080808000000b02402008280254450d00200841d8006a28020041002802c0a3c68000118080808000000b410021012003417f6a22030d000b20040d01410021040b2002450d0002400240200241077122000d00200221010c010b2002210103402001417f6a210120042802ac0921042000417f6a22000d000b0b20024108490d00034020042802ac092802ac092802ac092802ac092802ac092802ac092802ac092802ac092104200141786a22010d000b0b034020042802a0082101200441002802c0a3c68000118080808000002001210420010d000b0b0bba0501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802ac1421012005417f6a22050d000b0b20024108490d00034020012802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142101200041786a22000d000b0b410021050b024002400240200520012f01aa14490d00034020012802a0132200450d0220012f01a8142105200141002802c0a3c6800011808080800000200441016a210420002101200520002f01aa144f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a41ac146a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802ac1421002004417f6a22040d000b0b20074107490d01034020002802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b024020012005410c6c6a41a4136a2204280200450d00200428020441002802c0a3c68000118080808000000b2001200541e0016c6a108e87808000410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802ac1421002005417f6a22050d000b0b20024108490d00034020002802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142100200141786a22010d000b0b034020002802a0132101200041002802c0a3c68000118080808000002001210020010d000b0b0ba60601097f2000280200210102400240024020002802202202450d0020002802042103034020002002417f6a220236022002400240024002400240024002402001450d0020030d0020002802082104200028020c2205450d03200541077122060d01200521030c020b2001450d04200028020c210620002802082105200321040c030b2005210303402003417f6a210320042802f80621042006417f6a22060d000b0b20054108490d00034020042802f8062802f8062802f8062802f8062802f8062802f8062802f8062802f8062104200341786a22030d000b0b2000420037020820002004360204410121012000410136020041002106410021050b02400240200620042f01f606490d00034020042802f0062203450d0220042f01f4062106200441002802c0a3c6800011808080800000200541016a210520032104200620032f01f6064f0d000b200321040b200641016a2107024020050d00200421030c030b200420074102746a41f8066a2802002103410021072005417f6a2208450d022005417e6a2109024020084107712205450d0003402008417f6a210820032802f80621032005417f6a22050d000b0b20094107490d02034020032802f8062802f8062802f8062802f8062802f8062802f8062802f8062802f8062103200841786a22080d000c030b0b200441002802c0a3c6800011808080800000418cd0c2800010a081808000000b41d887c6800010a081808000000b2000200736020c200041003602082000200336020402402004200641d0006c6a220441c8006a2802004129490d00200441206a28020041002802c0a3c68000118080808000000b20020d000b200041003602000c010b200041003602002001450d01200028020422030d0020002802082103200028020c2205450d0002400240200541077122060d00200521040c010b2005210403402004417f6a210420032802f80621032006417f6a22060d000b0b20054108490d00034020032802f8062802f8062802f8062802f8062802f8062802f8062802f8062802f8062103200441786a22040d000b0b034020032802f0062104200341002802c0a3c68000118080808000002004210320040d000b0b0bab0501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802980421012005417f6a22050d000b0b20024108490d000340200128029804280298042802980428029804280298042802980428029804280298042101200041786a22000d000b0b410021050b024002400240200520012f019604490d0003402001280290042200450d0220012f0194042105200141002802c0a3c6800011808080800000200441016a210420002101200520002f0196044f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a4198046a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802980421002004417f6a22040d000b0b20074107490d010340200028029804280298042802980428029804280298042802980428029804280298042100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b0240200120054104746a41e0026a2201280200450d00200128020441002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802980421002005417f6a22050d000b0b20024108490d000340200028029804280298042802980428029804280298042802980428029804280298042100200141786a22010d000b0b03402000280290042101200041002802c0a3c68000118080808000002001210020010d000b0b0ba80501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a2100200128028c0121012005417f6a22050d000b0b20024108490d000340200128028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012101200041786a22000d000b0b410021050b024002400240200520012f018a01490d00034020012802002200450d0220012f0188012105200141002802c0a3c6800011808080800000200441016a210420002101200520002f018a014f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a418c016a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a2106200028028c0121002004417f6a22040d000b0b20074107490d010340200028028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b024020012005410c6c6a41046a2201280200450d00200128020441002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a2101200028028c0121002005417f6a22050d000b0b20024108490d000340200028028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012100200141786a22010d000b0b034020002802002101200041002802c0a3c68000118080808000002001210020010d000b0b0bca0501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802900221012005417f6a22050d000b0b20024108490d000340200128029002280290022802900228029002280290022802900228029002280290022101200041786a22000d000b0b410021050b024002400240200520012f018e02490d0003402001280288022200450d0220012f018c022105200141002802c0a3c6800011808080800000200441016a210420002101200520002f018e024f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a4190026a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802900221002004417f6a22040d000b0b20074107490d010340200028029002280290022802900228029002280290022802900228029002280290022100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b02402001200541186c6a2201280200450d00200128020441002802c0a3c68000118080808000000b0240200128020c450d00200141106a28020041002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802900221002005417f6a22050d000b0b20024108490d000340200028029002280290022802900228029002280290022802900228029002280290022100200141786a22010d000b0b03402000280288022101200041002802c0a3c68000118080808000002001210020010d000b0b0bfb0401057f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d0020012104200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802e80221012005417f6a22050d000b0b20024108490d00034020012802e8022802e8022802e8022802e8022802e8022802e8022802e8022802e8022101200041786a22000d000b0b410021020b024002400240200220012f01e602490d00034020012802e0022200450d0220012f01e4022102200141002802c0a3c6800011808080800000200441016a210420002101200220002f01e6024f0d000b200021010b200241016a2102024020040d00200121000c020b200120024102746a41e8026a2802002100410021022004417f6a2201450d012004417e6a2105024020014107712204450d0003402001417f6a210120002802e80221002004417f6a22040d000b0b20054107490d01034020002802e8022802e8022802e8022802e8022802e8022802e8022802e8022802e8022100200141786a22010d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122040d0020012100200221010c010b200121002002210103402001417f6a210120002802e80221002004417f6a22040d000b0b20024108490d00034020002802e8022802e8022802e8022802e8022802e8022802e8022802e8022802e8022100200141786a22010d000b0b034020002802e0022101200041002802c0a3c68000118080808000002001210020010d000b0b0bc40501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802900221012005417f6a22050d000b0b20024108490d000340200128029002280290022802900228029002280290022802900228029002280290022101200041786a22000d000b0b410021050b024002400240200520012f018e02490d0003402001280288022200450d0220012f018c022105200141002802c0a3c6800011808080800000200441016a210420002101200520002f018e024f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a4190026a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802900221002004417f6a22040d000b0b20074107490d010340200028029002280290022802900228029002280290022802900228029002280290022100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b200120054103746a220141b0016a2802002205200141b4016a28020022012802001180808080000002402001280204450d00200541002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802900221002005417f6a22050d000b0b20024108490d000340200028029002280290022802900228029002280290022802900228029002280290022100200141786a22010d000b0b03402000280288022101200041002802c0a3c68000118080808000002001210020010d000b0b0bca08010a7f23808080800041306b22012480808080002000280200210202400240024020002802202203450d0020002802042104034020002003417f6a220336022002400240024002400240024002402002450d0020040d0020002802082105200028020c2206450d03200641077122070d01200621040c020b2002450d04200028020c210720002802082106200421050c030b2006210403402004417f6a210420052802bc0221052007417f6a22070d000b0b20064108490d00034020052802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022105200441786a22040d000b0b2000420037020820002005360204410121022000410136020041002107410021060b02400240200720052f01ba02490d00034020052802b0012204450d0220052f01b8022107200541002802c0a3c6800011808080800000200641016a210620042105200720042f01ba024f0d000b200421050b200741016a2108024020060d00200521040c030b200520084102746a41bc026a2802002104410021082006417f6a2209450d022006417e6a210a024020094107712206450d0003402009417f6a210920042802bc0221042006417f6a22060d000b0b200a4107490d02034020042802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022104200941786a22090d000c030b0b200541002802c0a3c6800011808080800000418cd0c2800010a081808000000b41d887c6800010a081808000000b2000200836020c2000410036020820002004360204024020052007410c6c6a41b4016a2206280200450d00200628020441002802c0a3c68000118080808000000b02400240024002400240200520074104746a22052d00000e050404010203000b02400240200528020422070d0041002105410021060c010b2005410c6a28020021062001200541086a28020022053602282001200736022420014100360220200120053602182001200736021420014100360210410121050b2001200636022c2001200536021c2001200536020c2001410c6a10aa8d8080000c030b2005280204450d02200541086a28020041002802c0a3c68000118080808000000c020b2005280204450d01200541086a28020041002802c0a3c68000118080808000000c010b200541046a1091878080002005280204450d00200541086a28020041002802c0a3c68000118080808000000b20030d000b200041003602000c010b200041003602002002450d01200028020422040d0020002802082104200028020c2206450d0002400240200641077122070d00200621050c010b2006210503402005417f6a210520042802bc0221042007417f6a22070d000b0b20064108490d00034020042802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022104200541786a22050d000b0b034020042802b0012105200441002802c0a3c68000118080808000002005210420050d000b0b200141306a2480808080000bc50601097f2000280200210102400240024020002802202202450d0020002802042103034020002002417f6a220236022002400240024002400240024002402001450d0020030d0020002802082104200028020c2205450d03200541077122060d01200521030c020b2001450d04200028020c210620002802082105200321040c030b2005210303402003417f6a210320042802900221042006417f6a22060d000b0b20054108490d000340200428029002280290022802900228029002280290022802900228029002280290022104200341786a22030d000b0b2000420037020820002004360204410121012000410136020041002106410021050b02400240200620042f018e02490d00034020042802002203450d0220042f018c022106200441002802c0a3c6800011808080800000200541016a210520032104200620032f018e024f0d000b200321040b200641016a2107024020050d00200421030c030b200420074102746a4190026a2802002103410021072005417f6a2208450d022005417e6a2109024020084107712205450d0003402008417f6a210820032802900221032005417f6a22050d000b0b20094107490d020340200328029002280290022802900228029002280290022802900228029002280290022103200841786a22080d000c030b0b200441002802c0a3c6800011808080800000418cd0c2800010a081808000000b41d887c6800010a081808000000b2000200736020c2000410036020820002003360204024020042006410c6c6a220441046a2206280200450d00200628020441002802c0a3c68000118080808000000b024020044188016a2204280200450d00200428020441002802c0a3c68000118080808000000b20020d000b200041003602000c010b200041003602002001450d01200028020422030d0020002802082103200028020c2205450d0002400240200541077122060d00200521040c010b2005210403402004417f6a210420032802900221032006417f6a22060d000b0b20054108490d000340200328029002280290022802900228029002280290022802900228029002280290022103200441786a22040d000b0b034020032802002104200341002802c0a3c68000118080808000002004210320040d000b0b0be10e04147f017e047f017e2380808080004180016b2202248080808000200128020c21032001410036020c200141146a2204280200210520044100360200200141106a280200210620022005410020031b2204360254200220063602502002200336024c20024100360248200220034100472207360244200220063602402002200336023c200241003602382002200736023402400240024002402004450d002000410c6a2108200241046a41106a2109200241046a410c6a210a41002104034020022005417f6a22053602540240024002402007450d002004450d010b20070d0141d887c6800010a081808000000b0240024020060d00200321040c010b024002402006410771220b0d00200321042006210c0c010b200321042006210c0340200c417f6a210c20042802bc022104200b417f6a220b0d000b0b20064108490d00034020042802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022104200c41786a220c0d000b0b2002420037023c20022004360238410121072002410136023441002106410021030b024002400240200620042f01ba02490d00034020042802b001220c450d0220042f01b8022106200441002802c0a3c6800011808080800000200341016a2103200c21042006200c2f01ba024f0d000b200c21040b2004210b2006220d41016a2106024020030d00200b21040c020b200b20064102746a41bc026a2802002104410021062003417f6a220c450d012003417e6a210e0240200c4107712203450d000340200c417f6a210c20042802bc0221042003417f6a22030d000b0b200e4107490d01034020042802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022104200c41786a220c0d000c020b0b200441002802c0a3c6800011808080800000418cd0c2800010a081808000000b200220063602402002410036023c20022004360238200b200d410c6c6a220341b4016a280200220f418080808078460d04200b200d4104746a220c28020c2110200c2802082111200c2802042112200c2802002113200341bc016a280200210b200341b8016a280200211402400240024002400240200828020022150d00200bad4220862014ad842116410021150c010b2000280210211702400340201541b4016a210c20152f01ba022218410c6c2103417f210e41002119024002400340024020030d002018210e0c020b200c41086a210d200c41046a211a200341746a2103201941106a2119200e41016a210e200c410c6a210c417f2014201a280200200b200d280200220d200b200d491b10888e808000221a200b200d6b201a1b220d410047200d4100481b220d4101460d000b200d41ff0171450d010b2017450d022017417f6a21172015200e4102746a41bc026a28020021150c010b0b2002201736022420022015360220200229032021160240200f450d00201441002802c0a3c68000118080808000000b2016a720196a220c417c6a2203280200220b417f4a0d020240200c41706a220d280200450d00200c41746a28020041002802c0a3c68000118080808000002003280200210b0b200d2013360200200c41786a2011360200200c41746a20123602002003200b20106a3602000c040b2002200e36022820024100360224200bad4220862014ad8421162002290224211b0b2002201b3702182002201536021420022008360210200220163702082002200f3602042002201036022c200220113602282002201236022420022013360220024020150d0041002d00fca3c680001a41bc0241002802c8a3c6800011818080800000220c450d02200c41013b01ba02200c41003602b001200c20022902043702b401200241046a41086a2802002103200c2002290220370200200c41086a200241206a41086a290200370200200c41bc016a200336020020004280808080103702102000200c36020c0c030b200241d8006a41086a200941086a28020036020020022009290200370358200241f0006a41086a200241046a41086a28020036020020022002290204370370200241e4006a200241d8006a200241f0006a200241206a200a10c0858080002002280210220c200c28020841016a3602080c020b2003200b20106a3602002013450d01201241002802c0a3c68000118080808000000c010b410441bc0210b280808000000b4100210320050d000b41002103200241003602342007450d032004450d010c020b200241003602342003450d020b024020060d00200321040c010b024002402006410771220b0d00200321042006210c0c010b200321042006210c0340200c417f6a210c20042802bc022104200b417f6a220b0d000b0b20064108490d00034020042802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022104200c41786a220c0d000b0b034020042802b001210c200441002802c0a3c6800011808080800000200c2104200c0d000b0b200241346a10928d808000200241003602542002410036024420024100360234200241346a10928d80800002402001280200450d00200128020441002802c0a3c68000118080808000000b20024180016a2480808080000b1901017f23808080800041106b220120003a000f20012d000f0b3c01017f23808080800041106b22032480808080002003200239030820002001200341086a41ac88c6800010af8d808000200341106a2480808080000bd90402017f017e23808080800041c0006b22042480808080002004200336020c2004200236020802400240024002400240024020002d0004450d00200041003a000420012802102203200128020422024f0d04200128020020034103746a220128020021030240200128020422024107470d00200341d489c68000410710888e808000450d020b20002802002101200441106a410c6a4202370200200441286a410c6a41928480800036020020044102360214200441dc89c68000360210200441938480800036022c2004200236023c20042003360238200141186a28020021032004200441286a3602182004200441086a3602302004200441386a36022820012802142003200441106a10d9808080000d020c030b20012802102202200128020422034f0d0420002802002103200441106a410c6a4202370200200128020020024103746a2902002105200441286a410c6a41928480800036020020044102360214200441c489c6800036021020042005370238200441938480800036022c200341186a28020021012004200441286a3602182004200441086a3602302004200441386a36022820032802142001200441106a10d9808080000d010c020b200028020021012004411c6a4201370200200441013602142004418c89c68000360210200441928480800036022c200141186a28020021032004200441286a3602182004200441086a36022820012802142003200441106a10d980808000450d010b200041013a00050b200441c0006a2480808080000f0b2003200241e88ec6800010f980808000000b2002200341e88ec6800010f980808000000b3c01017f23808080800041106b22032480808080002003200237030820002001200341086a41bc88c6800010af8d808000200341106a2480808080000b3c01017f23808080800041106b22032480808080002003200237030820002001200341086a41cc88c6800010af8d808000200341106a2480808080000b3c01017f23808080800041106b2203248080808000200320023a000f200020012003410f6a41dc88c6800010af8d808000200341106a2480808080000b4001017f23808080800041106b2204248080808000200420033703082004200237030020002001200441ec88c6800010af8d808000200441106a2480808080000b4001017f23808080800041106b2204248080808000200420033703082004200237030020002001200441fc88c6800010af8d808000200441106a2480808080000b2d00024020002d00000d00200141a891c08000410510dd808080000f0b200141ad91c08000410410dd808080000b140020002802002000280204200110e2808080000b180020002802002001200028020428020c118380808000000b140020012000280200200028020410dd808080000b810302027f027e2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d01200029030022042004423f8722058520057d2004427f55200110fe8080800021000c020b20002903002104410021000340200220006a41ff006a413041d7002004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002903002104410021000340200220006a41ff006a413041372004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000bf10202027f017e2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120002903004101200110fe8080800021000c020b20002903002104410021000340200220006a41ff006a413041d7002004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002903002104410021000340200220006a41ff006a413041372004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000bfb0404027f027e037f017e2380808080004180016b22022480808080000240024002400240024002400240200128021c22034110710d0020034120710d014200200029030022047d2004200041086a290300220542005322001b420020052004420052ad7c7d200520001b2005427f55200110d48080800021000c050b200041086a290300210420002903002105418001210020024180016a21034100210603402000450d042003417f6a413041d7002005a72207410f712208410a491b20086a3a00002005421054410020045022081b0d022003417e6a2203413041d700200741ff0171220741a001491b20074104766a3a00002004423886210920054280025421072000417e6a210020044208882104200920054208888421052007410020081b0d030c000b0b200041086a290300210420002903002105418001210020024180016a21034100210602400240024003402000450d022003417f6a413041372005a72207410f712208410a491b20086a3a000002402005421054410020045022081b0d002003417e6a220341304137200741ff0171220741a001491b20074104766a3a00002004423886210920054280025421072000417e6a210020044208882104200920054208888421052007410020081b0d020c010b0b2000417f6a21000b20004180014b0d01200021060b2001410141c48dc080004102200220066a41800120066b10db8080800021000c040b200041800141c88dc08000109481808000000b2000417f6a21000b20004180014b0d02200021060b2001410141c48dc080004102200220066a41800120066b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000bd80404027f027e037f017e2380808080004180016b22022480808080000240024002400240024002400240200128021c22034110710d0020034120710d012000290300200041086a2903004101200110d48080800021000c050b200041086a290300210420002903002105418001210020024180016a21034100210603402000450d042003417f6a413041d7002005a72207410f712208410a491b20086a3a00002005421054410020045022081b0d022003417e6a2203413041d700200741ff0171220741a001491b20074104766a3a00002004423886210920054280025421072000417e6a210020044208882104200920054208888421052007410020081b0d030c000b0b200041086a290300210420002903002105418001210020024180016a21034100210602400240024003402000450d022003417f6a413041372005a72207410f712208410a491b20086a3a000002402005421054410020045022081b0d002003417e6a220341304137200741ff0171220741a001491b20074104766a3a00002004423886210920054280025421072000417e6a210020044208882104200920054208888421052007410020081b0d020c010b0b2000417f6a21000b20004180014b0d01200021060b2001410141c48dc080004102200220066a41800120066b10db8080800021000c040b200041800141c88dc08000109481808000000b2000417f6a21000b20004180014b0d02200021060b2001410141c48dc080004102200220066a41800120066b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b02000b02000b02000b900202017f057e23808080800041e0006b22052480808080002005410c6a41386a42013702002005410c6a41306a4101360200200541346a20032802083602002005418c89c68000360238200541c0006a200541d0006a3602002005200329020037022c200041306a3502002106200041386a350200210720002902002108200035022c21092000350234210a200541a083808000360254200541013a005c200520043602582005200541d8006a360250200541246a200a200742208684370200200541186a2009200642208684370200200541024101200a501b3602202005410241012009501b3602142005200837020c20012005410c6a200228021011848080800000200541e0006a2480808080000bc50101047f23808080800041106b220224808080800041002103200241003a000d200220002d00043a000c200220013602080240200028020022012802042204450d0020012802002100200128020828020821052004410c6c210303400240200028020022012802082005470d00200041046a2802002204450d0020042001200241086a419489c68000200041086a28020028020c118680808000000b2000410c6a2100200341746a22030d000b20022d000d41004721030b200241106a24808080800020030bef0101017f23808080800041306b22042480808080002004200336020c20042002360208024020012802102203200128020422024f0d00024002400240200128020020034103746a22032802044107470d00200328020041d489c68000410710888e808000450d010b20002001200441086a41ec89c6800010af8d8080000c010b2004411c6a4201370200200441013602142004418c89c68000360210200441938480800036022c2004200441286a3602182004200441086a36022820002001200441106a41fc89c6800010af8d8080000b200441306a2480808080000f0b2003200241e88ec6800010f980808000000b8b0201067f2380808080004180016b2202248080808000200128020421032001280200210420002802002802002100200128021c2205210602402005410471450d002005410872210620040d0020014281808080a0013702000b2001200641047236021c410021060340200220066a41ff006a413041d7002000410f712207410a491b20076a3a00002006417f6a210620004110492107200041047621002007450d000b024020064180016a22004180014d0d00200041800141c88dc08000109481808000000b2001410141c48dc080004102200220066a4180016a410020066b10db8080800021002001200536021c200120033602042001200436020020024180016a24808080800020000b02000bfc0201047f23808080800041206b220124808080800041012102200020002d00092203410120031b3a0009024002400240024020030d004100210302404100280294a4c680004102470d002000280200210441002802f4a3c68000210241002802f0a3c680002103024041002802eca3c68000450d002002280208417f6a41787120036a41086a21030b41022003200420022802101183808080000041ff0171220341004720034102461b21030b200020033a00084100280288a4c680002103034020002003360204200120033602002001200036020420032000460d04410020004100280288a4c680002202200220034622041b360288a4c68000200221032004450d000b200041023a00090c010b20034102470d010b4102410120002d000822034102461b410020031b21020b200141206a24808080800020020f0b200142003702142001418c8ac680003602102001410136020c200141888cc680003602084101200141046a2001200141086a41f88cc6800010d88d808000000b1c00200041024101200141ff017122014102461b410020011b3a00080b1a0020022001200041888dc680002003280228118680808000000b040041000b2a0020002001360204200020024293c3cef2b89ddac72285200342ebaebfe0a6e7f0cf028584503602000b060042adbd030b02000b02000b040041060b040041010b070020012903000b02000b0900200041023602000b040041000b02000b02000b040041000b02000b02000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a41e48dc680002005410c6a41f48dc680002003200410fb80808000000bd40101057f23808080800041306b22022480808080000240200128020822034101762204200128020422054b0d00200128020021012002412c6a22064100360200200241046a200120046a200120056a10f58d8080002000412c6a2006280200360200200041246a200241246a2902003702002000411c6a2002411c6a290200370200200041146a200241146a2902003702002000410c6a2002410c6a2902003702002000200229020437020420002003410171360200200241306a2480808080000f0b2004200541e491c68000109481808000000bb20601067f23808080800041306b220324808080800020012802002104024002400240024002400240024002400240024002400240024020012802042205410174200128020822016b20024d0d0020014101762106200120026a2207410176210820074101710d0320082006490d06200820054b0d07200420066a2105200820066b22024129490d0141002d00fca3c680001a200241002802c8a3c68000118180808000002204450d0820042005200210848e8080002105200041086a2002360200200020053602040c020b2001410176220220054b0d04200341286a220641003602002003200420026a200420056a10f58d8080002000412c6a2006280200360200200041246a200341206a2902003702002000411c6a200341186a290200370200200041146a200341106a2902003702002000410c6a200341086a29020037020020002003290200370204200020014101713602000c030b200041046a2005200210848e8080001a0b200020014101713602002000412c6a20023602000c010b200841016a22072006490d05200820054f0d06200420066a210402400240200720066b22054129490d002005417f4c0d0941002d00fca3c680001a200541002802c8a3c68000118180808000002206450d0a20062004200510848e8080002104200341086a2005360200200320043602040c010b200341046a2004200510848e8080001a0b2003412c6a22042005360200200320014101713602002003200241017110e88d8080001a024020034108412c200428020041284b1b6a22012802002202450d0020012002417f6a3602000b20002003290200370200200041286a200341286a290200370200200041206a200341206a290200370200200041186a200341186a290200370200200041106a200341106a290200370200200041086a200341086a2902003702000b200341306a2480808080000f0b2002200541e491c68000109481808000000b2006200841f491c68000109681808000000b2008200541f491c68000109581808000000b4101200210b280808000000b20062007418492c68000109681808000000b20072005418492c68000109581808000000b10ae80808000000b4101200510b280808000000bc204010a7f0240024002400240024002400240024020002802082202410171220320012802082204410171460d0002402000280204220541017420026b22032001280204220641017420046b220720032007491b22080d0041000f0b200128020021092000280200210a410021030340200220036a2200410176220120054f0d03200420036a220b410176220720064f0d04200a20016a2d00002201410f71200141047620004101711b200920076a2d00002201410f712001410476200b4101711b470d022008200341016a2203470d000b20080f0b200441017621042002410176210b200028020421024100210502402003450d00200b20024f0d042004200128020422034f0d0541002103200128020020046a2d00002000280200200b6a2d000073410f710d0141012105200441016a2104200b41016a210b0b2002200b490d05200128020422032004490d06200128020020046a21012000280200200b6a2100410221072002200b6b220b200320046b2203200b2003491b220421030340024020030d00200441017420056a0f0b2003417f6a21032007417e6a210720012d0000210b20002d00002102200041016a2100200141016a21012002200b460d000b200b20027341104920076b20056a21030b20030f0b2001200541e890c6800010f980808000000b2007200641e890c6800010f980808000000b200b2002419492c6800010f980808000000b2004200341a492c6800010f980808000000b200b200241c492c68000109481808000000b2004200341b492c68000109481808000000bb10101047f200128020822024101762103200128020421042001280200210502400240024020024101710d00410021020240200320044b0d0020032101410021030c020b2003200441d492c68000109481808000000b200320044f0d0141012102200341016a2101200520036a2d0000410f7121030b200020033a000d200020023a000c200041003602082000200420016b3602042000200520016a3602000f0b2003200441e492c6800010f980808000000bbf0101027f20012802082202410176210302400240024020024101710d002003200128020422024b0d01200041003a000820002003360204200020012802003602000f0b2003200128020422024b0d010240200320024f0d0020012802002101200041013a00082000200336020420002001360200200041096a200120036a2d000041f001713a00000f0b20032002419493c6800010f980808000000b2003200241f492c68000109581808000000b20032002418493c68000109581808000000bda02010b7f23808080800041106b2202248080808000410021030240024020002802042204410174200028020822056b200128022c2206490d002001280228210702402006410171450d002000280200210820012802002001200741284b22001b21092001280204200720001b210a410021010340200620014622030d020240200520016a2207410176220020044f0d00200a2001410176220b4d0d042001410171210c200141016a2101200820006a2d00002200410f71200041047620074101711b2009200b6a2d00002200410f712000410476200c1b460d010c030b0b2000200441e890c6800010f980808000000b2001280204210b2001280200210c2002410036020c2002200c2001200741284b22031b3602042002200b200720031b22013602082000200241046a10db8d80800020014101744621030b200241106a24808080800020030f0b200b200a418c94c6800010f980808000000be902010c7f23808080800041106b22022480808080004100210302400240024020002802042204410174200028020822056b2206200128022c470d002001280204210702402006410171450d00200128020020012001280228220841284b22091b210a2007200820091b210b2000280200210c410021010340200141016a220020064b22030d02200520016a2208410176220720044f0d03200b200141017622094d0d042001410171210d20002101200a20096a2d00002200410f712000410476200d1b200c20076a2d00002200410f71200041047620084101711b460d000c020b0b2001280200210920012802282108410021032002410036020c200220092001200841284b220d1b360204200220072008200d1b220136020820062001410174470d002000200241046a10db8d80800020064621030b200241106a24808080800020030f0b2007200441e890c6800010f980808000000b2009200b418c94c6800010f980808000000b8d0301087f024002402001450d000240200028022c220220014d0d00200041046a2103200041286a21040240200220016b22054101762202200541017122066a2207200028020420002802282201200141284b1b22014f0d0020012002417f736a210802402001200220056a6b410171450d00200741016a210720032004200428020041284b1b22022802002209450d0020022009417f6a3602000b20082006460d00200120076b21010340024020032004200428020041284b1b22022802002207450d0020022007417f6a3602000b024020032004200428020041284b1b22022802002207450d0020022007417f6a3602000b2001417e6a22010d000b0b2000200536022c2006450d01200328020020042802002204200441284b22011b2204417f6a210320040d0220034100419c95c6800010f980808000000b0240200041044128200028022841284b22041b6a280200450d00200041046a200041286a20041b41003602000b2000410036022c0b0f0b2000280200200020011b20036a220420042d000041f001713a00000be805010c7f0240200128022c2202450d0002400240200028022c22034101710d00200128020420012802282203200341284b22031b2204450d012001280200200120031b2103200041046a2105200041286a2106034020032d00002107024002402000410441282000280228220841284b22011b6a28020022092008412820011b460d002005200620011b21082000280200200020011b21010c010b200010f78d8080002000280204210920002802002101200521080b200120096a20073a00002008200828020041016a360200200341016a21032004417f6a22040d000c020b0b024002400240200028020420002802282208200841284b1b2209200341017622044d0d00200128020420012802282209200941284b1b2206450d01200041046a2105200041286a210a200320026a410171210b20002802002000200841284b1b20046a220320012802002001200941284b1b220c2d000041047620032d000041f00171723a000002402006417f6a220d450d0041002101034020062001460d04200c20016a22032d00002104200341016a2d00002107024002402000410441282000280228220841284b22031b6a28020022092008412820031b460d002005200a20031b21082000280200200020031b21030c010b200010f78d8080002000280204210920002802002103200521080b200320096a20044104742007410476410f71723a00002008200828020041016a360200200d200141016a2201470d000b0b200b450d03200c200d6a2d00004104742108024002402000410441282000280228220941284b22011b6a28020022032009412820011b460d002005200a20011b21052000280200200020011b21010c010b200010f78d80800020002802042103200028020021010b200120036a20083a00002005200528020041016a3602000c030b2004200941dc95c6800010f980808000000b4100410041ec95c6800010f980808000000b2006200641fc95c6800010f980808000000b2000200028022c20026a36022c0b0be808010b7f23808080800041106b2202248080808000200141086a2802002103200128020421040240024002400240024002400240024020012d00004101460d00200028022c21050c010b20012d0001410f7121060240024020002d002c4101710d00200041046a210120064104742107024002402000410441282000280228220841284b22061b6a28020022052008412820061b460d002001200041286a20061b21012000280200200020061b21060c010b200010f78d80800020002802042105200028020021060b200620056a20073a00002001200128020041016a3602000c010b200028020420002802282201200141284b22011b2205450d0220052000280200200020011b6a417f6a220120012d00002006723a00000b2000200028022c41016a220536022c0b200041046a2109200041286a210a024002402000280204220720002802282206200641284b22081b22014101742005470d0002402006412820081b20016b20034f0d00200120036a22062001490d044100417f2006417f6a677620064102491b41016a2206450d04200241086a2000200610f68d808000024020022802082206418180808078460d002006450d052006200228020c10b280808000000b20092802002107200a28020021060b20072006200641284b22051b22062001490d042000280200200020051b20016a220520036a2005200620016b10fe8d8080001a20052004200310848e8080001a200041044128200028022841284b1b6a200620036a3602000c010b2003450d002001417f6a21052001450d0420002802002000200641284b1b20056a220120012d000041f001713a0000200028020420002802282201200141284b22011b220620054d0d052000280200200020011b20056a220120012d000020042d000041f00171410476723a000002402003417f6a220b450d0041002101034020032001460d08200420016a22062d00002108200641016a2d0000210c024002402000410441282000280228220541284b22061b6a28020022072005412820061b460d002009200a20061b21052000280200200020061b21060c010b200010f78d8080002000280204210720002802002106200921050b200620076a2008410474200c410476410f71723a00002005200528020041016a360200200b200141016a2201470d000b0b2004200b6a2d00004104742105024002402000410441282000280228220741284b22011b6a28020022062007412820011b460d002009200a20011b21092000280200200020011b21010c010b200010f78d80800020002802042106200028020021010b200120066a20053a00002009200928020041016a3602000b2000200028022c20034101746a36022c200241106a2480808080000f0b419c94c68000413a41d894c6800010a181808000000b418ca0c68000411141a0a0c6800010f880808000000b41f88ec68000411e41f48fc6800010f880808000000b20054100418c96c6800010f980808000000b20052006419c96c6800010f980808000000b2003200341ac96c6800010f980808000000bec0301067f23808080800041106b2204248080808000024002400240024020010d00410021010c010b20012802082205410176210620012802042107200128020021080240024020054101710d00410021090240200620074b0d0020062101410021060c020b2006200741d492c68000109481808000000b200620074f0d0241012109200641016a2101200820066a2d0000410f7121060b2004410c6a200720016b360200200420063a0005200420093a00042004200820016a3602082000200441046a10e28d808000200741017420056b21010b02402002450d000240024020002d002c4101710d00200041046a210720034104742105024002402000410441282000280228220841284b22061b6a28020022022008412820061b460d002007200041286a20061b21072000280200200020061b21060c010b200010f78d80800020002802042102200028020021060b200620026a20053a00002007200728020041016a3602000c010b200028020420002802282207200741284b22071b2206450d0320062000280200200020071b6a417f6a220720072d00002003723a00000b2000200028022c41016a36022c200141016a21010b200441106a24808080800020010f0b2006200741e492c6800010f980808000000b419c94c68000413a41d894c6800010a181808000000bf303010a7f23808080800041306b220224808080800020024200370228200241286a210302400240024020012802042204410174220520012802082206460d00200520066b22054101200541014b1b2105200241046a2107200128020021084100210103402006410176220920044f0d02200820096a2d00002209410f71200941047620064101711b21090240024020014101710d002009410474210a024002402002410441282002280228220941284b22011b6a280200220b2009412820011b460d002007200320011b21092002280200200220011b21010c010b200210f78d8080002002280204210b20022802002101200721090b2001200b6a200a3a00002009200928020041016a3602000c010b200228020420022802282201200141284b22011b220b450d04200b2002280200200220011b6a417f6a220120012d00002009723a00000b2002200228022c41016a220136022c200641016a21062005417f6a22050d000b0b20002002290200370200200041286a2003290200370200200041206a200241206a290200370200200041186a200241186a290200370200200041106a200241106a290200370200200041086a200241086a290200370200200241306a2480808080000f0b2009200441e890c6800010f980808000000b419c94c68000413a41d894c6800010a181808000000b02000b2100200128021441f897c68000410b200141186a28020028020c118280808000000be70201087f20002001280200220220002802006a41017110e88d8080001a200141086a28020021030240024020020d00410021020c010b024002402003450d00200041086a2802002000412c6a2802002202200241284b22041b2202417f6a21052002450d012000280204200041046a20041b20056a220220022d000020012802042d0000410f71723a0000410121020c020b4100410041e498c6800010f980808000000b2005200241f498c6800010f980808000000b0240200320024d0d00200320026b2105200041086a21062000412c6a2104200041046a2107200128020420026a2101034020012d0000210802400240200620042004280200220941284b22031b220028020022022009412820031b460d002007280200200720031b21030c010b200710f78d8080002006280200210220072802002103200621000b200320026a20083a00002000200028020041016a360200200141016a21012005417f6a22050d000b0b0bc00501057f200028020021022000200136020002400240024002400240024002400240024002400240200220014b0d0041002103200220014f0d04200041086a2104200041046a210220004108412c2000412c6a2205280200220341284b22011b6a28020022062003412820011b460d012004200520011b21032000280204200220011b21010c020b200041046a21020240200041086a28020022032000412c6a2802002201200141284b1b2204417f6a2205450d004101210103402000280208200028022c2203200341284b22061b22032001417f6a4d0d06200320014d0d072002280200200220061b20016a2203417f6a220620062d000041047420032d0000410476410f71723a00002004200141016a2201470d000b20002802082103200028022c21010b20032001200141284b1b220020054d0d0620022802002002200141284b1b20056a220120012d00004104743a00000c020b200210f78d8080002004280200210620022802002101200421030b200120066a41003a00002003200328020041016a36020002402004280200220020052802002201200141284b1b22034102490d002003417f6a21010340200428020020052802002200200041284b22031b22002001417f6a22064d0d07200020014d0d082002280200200220031b20016a22002000417f6a2d000041047420002d0000410476410f71723a0000200141014b21002006210120000d000b20042802002100200528020021010b20002001200141284b22031b450d072002280200200220031b220120012d00004104763a00000b410121030b20030f0b2001417f6a200341889ac6800010f980808000000b2001200341989ac6800010f980808000000b2005200041f899c6800010f980808000000b2001417f6a200041a89ac6800010f980808000000b2001200041b89ac6800010f980808000000b4100410041e899c6800010f980808000000be40101037f23808080800041106b220324808080800002400240024020024100480d00200241f5ffffff074f0d0102402002410b6a417c7122040d00410421050c030b41002d00fca3c680001a200441002802c8a3c680001181808080000022050d024104200410b280808000000b41fc9bc68000412b2003410f6a41a89cc6800041b89cc68000108981808000000b41e484c08000412b2003410f6a419085c08000419086c08000108981808000000b2005428180808010370200200541086a2001200210848e8080001a2000200236020420002005360200200341106a2480808080000bae0101017f23808080800041306b2202248080808000200028020021002002410c6a4202370200200241186a410c6a41d08180800036020020022000280200220036022820024103360204200241f0bfc08000360200200241d18180800036021c200220006836022c200141186a28020021002002200241186a36020820022002412c6a3602202002200241286a36021820012802142000200210d9808080002101200241306a24808080800020010b9a0201027f23808080800041106b2202248080808000200220002802002200360204200128021441c89cc680004106200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a41ce9cc680004104200041046a41d49cc68000108c8180800041e49cc680004105200241046a41ec9cc68000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b140020012000280200200028020410dd808080000bed0201037f2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000b02000b02000b6001017f23808080800041206b22002480808080002000410c6a420137020020004101360204200041e49bc68000360200200041bb8480800036021c200041dc9ac680003602182000200041186a360208200041d49bc6800010f680808000000b4c01027f024020002802002201417f460d0020002802042102200120012802042200417f6a36020420004101470d002002410b6a4104490d00200141002802c0a3c68000118080808000000b0b6001017f23808080800041206b22002480808080002000410c6a420137020020004101360204200041e49bc68000360200200041bb8480800036021c200041dc9ac680003602182000200041186a360208200041ec9bc6800010f680808000000b2100200128021441fc9cc68000410b200141186a28020028020c118280808000000b920f03067f017e017f23808080800041c0016b220424808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020012802000e050200010304020b200141086a280200220520012802042206490d04200520034b0d05200141186a2802002107200141146a28020021082001410c6a28020021090240024020012802100d0020072008490d0841002101200720034d0d012007200341949ec68000109581808000000b20072008490d0841012101200720034b0d090b200020013602102000200220066a36020420004101360200200041186a200720086b360200200041146a200220086a3602002000410c6a2009360200200041086a200520066b3602000c160b200141086a280200220520012802042206490d08200520034b0d09200141186a2802002107200141146a28020021082001410c6a28020021090240024020012802100d0020072008490d0c41002101200720034d0d012007200341e49dc68000109581808000000b20072008490d0c41012101200720034b0d0d0b200020013602102000200220066a36020420004102360200200041186a200720086b360200200041146a200220086a3602002000410c6a2009360200200041086a200520066b3602000c150b200041003602000c140b200441023602b401200441023602a8012004410236029c0120044102360290012004410236028401200441023602782004410236026c2004410236026020044102360254200441023602482004410236023c2004410236023020044102360224200441023602182004410236020c20044102360200410021070340410221080240200120076a220541106a28020022094102460d00200541186a2802002106200541146a28020021050240024020090d0020062005490d0f41002108200620034d0d012006200341e49dc68000109581808000000b20062005490d0f41012108200620034b0d100b200620056bad422086200220056aad84210a0b200420076a22052008360200200541046a200a3702002007410c6a220741c001460d130c000b0b200441023602b401200441023602a8012004410236029c0120044102360290012004410236028401200441023602782004410236026c2004410236026020044102360254200441023602482004410236023c2004410236023020044102360224200441023602182004410236020c20044102360200410021070340410221080240200120076a2205411c6a28020022094102460d00200541246a2802002106200541206a28020021050240024020090d0020062005490d1141002108200620034d0d012006200341e49dc68000109581808000000b20062005490d1141012108200620034b0d120b200620056bad422086200220056aad84210a0b200420076a22052008360200200541046a200a3702002007410c6a220741c001460d110c000b0b2006200541849ec68000109681808000000b2005200341849ec68000109581808000000b2008200741949ec68000109681808000000b2008200741a49ec68000109681808000000b2007200341a49ec68000109581808000000b2006200541849ec68000109681808000000b2005200341849ec68000109581808000000b2008200741e49dc68000109681808000000b2008200741f49dc68000109681808000000b2007200341f49dc68000109581808000000b2005200641e49dc68000109681808000000b2005200641f49dc68000109681808000000b2006200341f49dc68000109581808000000b2005200641e49dc68000109681808000000b2005200641f49dc68000109681808000000b2006200341f49dc68000109581808000000b02400240024002400240200141086a280200220720012802042208490d00200720034b0d01410221052001410c6a280200210902402001280210220b4102460d00200141186a2802002106200141146a280200210102400240200b0d0020062001490d0541002105200620034d0d012006200341949ec68000109581808000000b20062001490d0541012105200620034b0d060b200620016bad422086200220016aad84210a0b2000200220086a3602042000410c6a2009360200200041086a200720086b360200200041106a200441c00110848e8080001a200041d4016a200a370200200020053602d001200041043602000c060b2008200741849ec68000109681808000000b2007200341849ec68000109581808000000b2001200641949ec68000109681808000000b2001200641a49ec68000109681808000000b2006200341a49ec68000109581808000000b410221070240024002400240200128020422054102460d002001410c6a2802002108200141086a28020021010240024020050d0020082001490d0341002107200820034d0d012008200341949ec68000109581808000000b20082001490d0341012107200820034b0d040b200820016bad422086200220016aad84210a0b200041046a200441c00110848e8080001a200041c8016a200a370200200020073602c401200041033602000c030b2001200841949ec68000109681808000000b2001200841a49ec68000109681808000000b2008200341a49ec68000109581808000000b200441c0016a2480808080000be10301077f23808080800041106b2203248080808000200041286a2104024002400240200028022822054128200541284b22061b22072000280204200520061b22066b200220016b22084f0d00200620086a22052006490d014100417f2005417f6a677620054102491b41016a2205450d01200341086a2000200510f68d808000024020032802082205418180808078460d002005450d022005200328020c10b280808000000b200428020022054128200541284b1b21070b200041046a22092004200541284b22061b21080240024020004104412820061b6a280200220520074f0d002000280200200020061b2106034020012002460d02200620056a20012d00003a0000200141016a21012007200541016a2205470d000b200721050b2008200536020020012002460d02034020012d00002108024002402000410441282000280228220741284b22051b6a28020022062007412820051b460d002009200420051b21072000280200200020051b21050c010b200010f78d8080002000280204210620002802002105200921070b200520066a20083a00002007200728020041016a360200200141016a22012002470d000c030b0b200820053602000c010b418ca0c68000411141a0a0c6800010f880808000000b200341106a2480808080000bc70301067f23808080800041106b220324808080800002400240024002400240200128020420012802282204200441284b22051b220620024b0d002004412820051b21072001280200200120051b2108024020024129490d00418180808078210520072002460d040240200241004e0d00410021050c060b02400240024020044129490d002007417f4a0d0120072102410021050c080b41002d00fca3c680001a200241002802c8a3c680001181808080000022040d01410121050c070b0240200241002802c8a3c680001181808080000022040d00410121050c070b200420082007200220072002491b10848e8080001a200841002802c0a3c68000118080808000000c040b20042008200610848e8080001a0c030b418180808078210520044129490d0320012008200610848e80800020063602282007417f4c0d01200841002802c0a3c68000118080808000000c030b41c0a0c68000412041e0a0c6800010f880808000000b2003200736020c2003410036020841e49ec68000412b200341086a41909fc6800041fc9fc68000108981808000000b2001200236022820012006360204200120043602000b0b2000200236020420002005360200200341106a2480808080000bb90101027f23808080800041106b220124808080800002400240200028020420002802282202200241284b1b41016a2202450d004100417f2002417f6a677620024102491b41016a2202450d00200141086a2000200210f68d808000024020012802082200418180808078460d002000450d022000200128020c10b280808000000b200141106a2480808080000f0b418ca0c68000411141b0a0c6800010a181808000000b418ca0c68000411141a0a0c6800010f880808000000b02000ba90201027f23808080800041106b22022480808080000240024020002802000d00200128021441b49ec680004110200141186a28020028020c1182808080000021010c010b20022000360204200128021441c49ec680004108200141186a28020028020c118280808000002100200241003a000d200220003a000c20022001360208200241086a41cc9ec680004106200241046a41d49ec68000108c81808000210320022d000c2100024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b9d0501087f23808080800041306b22022480808080000240024020012d002c4101710d0020012802002103200128020421042001280228210541002106200241046a41286a22074100360200200241046a20032001200541284b22081b220120012004200520081b6a10f58d8080002000412c6a2007280200360200200041246a200241246a2902003702002000411c6a2002411c6a290200370200200041146a200241146a2902003702002000410c6a2002410c6a290200370200200020022902043702040c010b02400240200128020420012802282206200641284b22061b2207450d00200241046a41046a2108200241046a41286a210920022001280200200120061b22032d00004104763a00042007417f6a2105200241046a410172210441002101034020052001460d01200420016a200320016a22062d0000410474200641016a2d0000410476410f71723a0000200141016a22014127470d000b2002412836022c20074129490d01200741586a2104200341286a210103402001417f6a2d000041047420012d0000410476410f7172210702400240200241046a41044128200228022c220541284b22061b6a28020022032005412820061b460d002008200920061b21052002280204200241046a20061b21060c010b200241046a10f78d8080002002280208210320022802042106200821050b200620036a20073a00002005200528020041016a360200200141016a21012004417f6a22040d000c020b0b2002200736022c0b200020022902043702042000412c6a2002412c6a280200360200200041246a200241246a2902003702002000411c6a2002411c6a290200370200200041146a200241146a2902003702002000410c6a2002410c6a290200370200410121060b20002006360200200241306a2480808080000b980605017f017e017f057e017f23808080800041206b2205248080808000024002400240024002402003500d002004500d010b420021062001200354200220045420022004511b0d012002500d01200541106a20032004200479a7200279a76b220741ff007110858e80800042012007413f71ad862108200541186a29030021092005290310210a4200210603400240200220097d2001200a54ad7d220b4200530d00200820068421062001200a7d2201200354200b200454200b2004511b0d04200b21020b200a4201882009423f8684210a20084201882108200942018821090c000b0b0240024002402002500d00024020022003540d0020022003510d022002200382210b2002200380210c024020034280808080105a0d00200b4220862001422088842209200380220a4220862009200382422086200142ffffffff0f83842201200380842106200a422088200c84210c200120038221014200210b0c070b2001200354200b200454200b2004511b0d032004423f8620034201888421092003423f86210a428080808080808080807f210242002104024003400240200b20097d2001200a54ad7d22084200530d002001200a7d2101200220048421042008500d022008210b0b200a4201882009423f8684210a20024201882102200942018821090c000b0b200120038020048421064200210b200120038221010c060b200520032004413f200379a72207200279a7220d6b41c0006a2007200d461b220710858e80800042012007413f71ad86210b200541086a29030021092005290300210a42002104024003400240200220097d2001200a54ad7d22084200530d002001200a7d2101200b20048421042008500d02200821020b200a4201882009423f8684210a200b420188210b200942018821090c000b0b200120038020048421064200210b200120038221010c040b200120038021064200210b200120038221010c030b200120028021064200210b4201210c200120028221010c030b420021060c020b2002210b0b4200210c0b2000200137031020002006370300200041186a200b3703002000200c370308200541206a2480808080000b4901017f23808080800041106b2204248080808000200420012002200310868e808000200429030021022000200441086a29030037030820002002370300200441106a2480808080000b6e01067e2000200342ffffffff0f832205200142ffffffff0f8322067e22072003422088220820067e22062005200142208822097e7c22054220867c220a3703002000200820097e2005200654ad4220862005422088847c200a200754ad7c200420017e200320027e7c7c3703080b0e0020002001200210818e8080000b4b01017f23808080800041206b22052480808080002005200120022003200410fb8d808000200529030021042000200541086a29030037030820002004370300200541206a2480808080000bc10201087f02400240200241104f0d00200021030c010b2000410020006b41037122046a210502402004450d0020002103200121060340200320062d00003a0000200641016a2106200341016a22032005490d000b0b2005200220046b2207417c7122086a210302400240200120046a2209410371450d0020084101480d012009410374220641187121022009417c71220a41046a2101410020066b4118712104200a28020021060340200520062002762001280200220620047472360200200141046a2101200541046a22052003490d000c020b0b20084101480d0020092101034020052001280200360200200141046a2101200541046a22052003490d000b0b20074103712102200920086a21010b02402002450d00200320026a21050340200320012d00003a0000200141016a2101200341016a22032005490d000b0b20000bac0501087f0240024002400240200020016b20024f0d00200120026a2103200020026a21040240200241104f0d00200021050c030b2004417c7121054100200441037122066b210702402006450d00200120026a417f6a210803402004417f6a220420082d00003a00002008417f6a210820052004490d000b0b2005200220066b2209417c7122066b21040240200320076a2207410371450d0020064101480d022007410374220841187121022007417c71220a417c6a2101410020086b4118712103200a280200210803402005417c6a2205200820037420012802002208200276723602002001417c6a210120042005490d000c030b0b20064101480d01200920016a417c6a210103402005417c6a220520012802003602002001417c6a210120042005490d000c020b0b02400240200241104f0d00200021040c010b2000410020006b41037122036a210502402003450d0020002104200121080340200420082d00003a0000200841016a2108200441016a22042005490d000b0b2005200220036b2209417c7122066a210402400240200120036a2207410371450d0020064101480d012007410374220841187121022007417c71220a41046a2101410020086b4118712103200a28020021080340200520082002762001280200220820037472360200200141046a2101200541046a22052004490d000c020b0b20064101480d0020072101034020052001280200360200200141046a2101200541046a22052004490d000b0b20094103712102200720066a21010b2002450d02200420026a21050340200420012d00003a0000200141016a2101200441016a22042005490d000c030b0b20094103712201450d012007410020066b6a2103200420016b21050b2003417f6a210103402004417f6a220420012d00003a00002001417f6a210120052004490d000b0b20000bb50101037f02400240200241104f0d00200021030c010b2000410020006b41037122046a210502402004450d00200021030340200320013a0000200341016a22032005490d000b0b2005200220046b2204417c7122026a2103024020024101480d00200141ff017141818284086c2102034020052002360200200541046a22052003490d000b0b200441037121020b02402002450d00200320026a21050340200320013a0000200341016a22032005490d000b0b20000b4a01037f4100210302402002450d000240034020002d0000220420012d00002205470d01200041016a2100200141016a21012002417f6a2202450d020c000b0b200420056b21030b20030b0e0020002001200210808e8080000b5701017e02400240200341c000710d002003450d0120022003413f71ad2204862001410020036b413f71ad88842102200120048621010c010b20012003413f71ad862102420021010b20002001370300200020023703080b5701017e02400240200341c000710d002003450d012002410020036b413f71ad8620012003413f71ad220488842101200220048821020c010b20022003413f71ad882101420021020b20002001370300200020023703080b4b01017f23808080800041106b22052480808080002005200120022003200410fd8d808000200529030021042000200541086a29030037030820002004370300200541106a2480808080000b0e0020002001200210838e8080000b4b01017f23808080800041106b22052480808080002005200120022003200410ff8d808000200529030021042000200541086a29030037030820002004370300200541106a2480808080000b0e0020002001200210828e8080000b0bb4a4060300418080c0000bf0a006010000000c000000040000000200000003000000040000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f7261775f7665632e72736361706163697479206f766572666c6f770000890010001100000018001000710000003a020000050000004572726f724c61796f75744572726f726d656d6f727920616c6c6f636174696f6e206f6620206279746573206661696c65640000c400100015000000d90010000d0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f616c6c6f632e727300f80010006f000000a20100000d000000f80010006f000000a00100000d000000060000000c000000040000000700000008000000040000006120666f726d617474696e6720747261697420696d706c656d656e746174696f6e2072657475726e656420616e206572726f72000900000000000000010000000a0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f666d742e7273000000e40110006d000000640200002000000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c7565000900000000000000010000000b0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f73796e632e72730000a00210006e00000075010000320000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f61727261792d62797465732d362e322e322f7372632f6c69622e72730000200310005e0000000804000022000000200310005e000000080400004f0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f627335382d302e352e312f7372632f6465636f64652e72730000a00310005a000000c00100000b000000a00310005a000000ac010000200000004c61796f75744572726f722f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f62797465732d312e362e302f7372632f62797465732e72730000000c0000000d0000000e0000000f000000100000001100000012000000130000001400000015000000120000001600000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c75650017000000000000000100000018000000270410005a0000002b04000032000000270410005a0000003904000049000000190000001a0000001b0000001c00000061626f72742f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f62797465732d312e362e302f7372632f6c69622e727300000025051000580000006e0000000900000066726f6d5f7374725f72616469785f696e743a206d757374206c696520696e207468652072616e676520605b322c2033365d60202d20666f756e6420900510003c0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6e756d2f6d6f642e7273d405100070000000a9050000050000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f666d742f6e756d2e7273307800005406100070000000690000001700000030623030303130323033303430353036303730383039313031313132313331343135313631373138313932303231323232333234323532363237323832393330333133323333333433353336333733383339343034313432343334343435343634373438343935303531353235333534353535363537353835393630363136323633363436353636363736383639373037313732373337343735373637373738373938303831383238333834383538363837383838393930393139323933393439353936393739383939617373657274696f6e206661696c65643a202a63757272203e20313900005406100070000000ef010000050000001e0000000c000000040000001f00000020000000210000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f666d742f6d6f642e727330303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030e807100070000000f20500001f00000066616c736574727565000000e807100070000000350900001a000000e8071000700000002e0900002200000070616e69636b6564206174203a0a2f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6e756d2f6269676e756d2e7273000000e208100073000000ac01000001000000617373657274696f6e206661696c65643a206e6f626f72726f77617373657274696f6e206661696c65643a20646967697473203c203430617373657274696f6e206661696c65643a206f74686572203e20302f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6e756d2f666c74326465632f6d6f642e7273617373657274696f6e206661696c65643a20216275662e69735f656d707479282900ba09100078000000bc00000005000000617373657274696f6e206661696c65643a206275665b305d203e206227302700ba09100078000000bd00000005000000617373657274696f6e206661696c65643a2070617274732e6c656e2829203e3d20340000ba09100078000000be000000050000002e302e00ba091000780000000b01000005000000ba091000780000000c0100000500000065652d2d2b4e614e696e6630306530617373657274696f6e206661696c65643a206275662e6c656e2829203e3d206d61786c656eba091000780000007f0200000d0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f7061747465726e2e7273300b1000740000004605000012000000300b1000740000004605000028000000300b1000740000003906000015000000300b1000740000006706000015000000300b10007400000068060000150000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f756e69636f64652f756e69636f64655f646174612e7273000000f40b10007d0000005000000028000000f40b10007d0000005c000000160000000003000083042000910560005d13a0001217201f0c20601fef2ca02b2a30202c6fa6e02c02a8602d1efb602e00fe20369eff6036fd01e136010a2137240de137ab0e61392f18a139301c6148f31ea14c40346150f06aa1514f6f21529dbca15200cf615365d1a15300da215400e0e155aee26157ece42159d0e8a1592000ee59f0017f5a00700007002d0101010201020101480b30151001650702060202010423011e1b5b0b3a09090118040109010301052b033c082a180120370101010408040103070a021d013a0101010204080109010a021a010202390104020402020303011e0203010b0239010405010204011402160601013a0101020104080107030a021e013b0101010c01090128010301370101030503010407020b021d013a01020102010301050207020b021c02390201010204080109010a021d0148010401020301010801510102070c08620102090b0749021b0101010101370e01050102050b0124090166040106010202021902040310040d01020206010f01000300031d021e021e02400201070801020b09012d030101750222017603040209010603db0202013a010107010101010208060a0201301f310430070101050128090c0220040202010338010102030101033a0802029803010d0107040106010302c6400001c32100038d016020000669020004010a200250020001030104011902050197021a120d012608190b2e0330010204020227014306020202020c0108012f01330101030202050201012a020801ee010201040100010010101000020001e201950500030102050428030401a502000400025003460b31047b01360f290102020a033104020207013d03240501083e010c0234090a0402015f0302010102060102019d010308150239020101010116010e070305c308020301011701510102060101020101020102eb010204060201021b025508020101026a0101010206010165030204010500090102f5010a0201010401900402020401200a280602040801090602032e0d010200070106010152160207010201027a06030101020107010148020301010100020b023405050101010001060f00053b0700013f0451010002002e0217000101030405080802071e0494030037043208010e011605010f000701110207010201056401a00700013d04000400076d07006080f000003a000000f00f100000000000f00f100001000000f00f10000100000025000000000000000100000026000000696e646578206f7574206f6620626f756e64733a20746865206c656e20697320206275742074686520696e6465782069732000001c101000200000003c10100012000000270000000400000004000000280000003d3d213d6d617463686573617373657274696f6e20606c6566742020726967687460206661696c65640a20206c6566743a200a2072696768743a20007b101000100000008b10100017000000a21010000900000020726967687460206661696c65643a200a20206c6566743a200000007b10100010000000c410100010000000d410100009000000a210100009000000010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002020202020202020202020202020202020202020202020202020202020203030303030303030303030303030303040404040400000000000000000000002e2e00000012100002000000426f72726f774572726f72426f72726f774d75744572726f72616c726561647920626f72726f7765643a20002512100012000000616c7265616479206d757461626c7920626f72726f7765643a200000401210001a0000003a2000000012100000000000641210000200000020202020207b202c20207b0a2c0a7d207d28280a2c290a5b5d0000002b0000000c000000040000001f0000002000000021000000010000000a00000064000000e803000010270000a086010040420f008096980000e1f50500ca9a3b0200000014000000c8000000d0070000204e0000400d030080841e00002d310100c2eb0b009435770000c16ff28623000000000081efac855b416d2dee0400000000000000000000011f6abf64ed386eed97a7daf4f93fe9034f180000000000000000000000000000000000013e952e0999df03fd38150f2fe47423ecf5cfd308dc04c4dab0cdbc197f33a603261fe94e0200000000000000000000000000000000000000000000000000000000000000000000017c2e985b87d3be729fd9d8872f1512c650de6b706e4acf0fd895d56e71b226b066c6ad2436151d5ad3423c0e54ff63c07355cc17eff965f228bc55f7c7dc80dced6ef4ceefdc5ff75305002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6e756d2f666c74326465632f73747261746567792f647261676f6e2e7273617373657274696f6e206661696c65643a20642e6d616e74203e2030d4131000840000007500000005000000617373657274696f6e206661696c65643a20642e6d696e7573203e2030000000d4131000840000007600000005000000617373657274696f6e206661696c65643a20642e706c7573203e2030d4131000840000007700000005000000617373657274696f6e206661696c65643a206275662e6c656e2829203e3d204d41585f5349475f444947495453000000d4131000840000007a00000005000000d413100084000000c100000009000000d413100084000000fa0000000d000000d4131000840000000101000036000000617373657274696f6e206661696c65643a20642e6d616e742e636865636b65645f73756228642e6d696e7573292e69735f736f6d65282900d4131000840000007900000005000000617373657274696f6e206661696c65643a20642e6d616e742e636865636b65645f61646428642e706c7573292e69735f736f6d6528290000d4131000840000007800000005000000d4131000840000000a01000005000000d4131000840000000b01000005000000d4131000840000000c01000005000000d4131000840000007101000024000000d4131000840000007601000057000000d4131000840000008301000036000000d413100084000000650100000d000000d4131000840000004b01000022000000d4131000840000000e01000005000000d4131000840000000d0100000500000072616e676520737461727420696e64657820206f7574206f662072616e676520666f7220736c696365206f66206c656e677468208016100012000000921610002200000072616e676520656e6420696e64657820c4161000100000009216100022000000736c69636520696e64657820737461727473206174202062757420656e64732061742000e416100016000000fa1610000d0000005b2e2e2e5d626567696e203c3d20656e642028203c3d2029207768656e20736c6963696e672060601d1710000e0000002b171000040000002f171000100000003f171000010000006279746520696e64657820206973206e6f742061206368617220626f756e646172793b20697420697320696e7369646520202862797465732029206f66206000601710000b0000006b17100026000000911710000800000099171000060000003f17100001000000206973206f7574206f6620626f756e6473206f6620600000601710000b000000c8171000160000003f171000010000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f6d6f642e7273f8171000700000000c0100002c0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f756e69636f64652f7072696e7461626c652e72730000781810007a0000001a00000036000000781810007a0000000a0000002b000000000601010301040205070702080809020a050b020e041001110212051311140115021702190d1c051d081f0124016a046b02af03b102bc02cf02d102d40cd509d602d702da01e005e102e704e802ee20f004f802fa03fb010c273b3e4e4f8f9e9e9f7b8b9396a2b2ba86b1060709363d3e56f3d0d1041418363756577faaaeafbd35e01287898e9e040d0e11122931343a4546494a4e4f64655cb6b71b1c07080a0b141736393aa8a9d8d909379091a8070a3b3e66698f92116f5fbfeeef5a62f4fcff53549a9b2e2f2728559da0a1a3a4a7a8adbabcc4060b0c151d3a3f4551a6a7cccda007191a22253e3fe7ecefffc5c604202325262833383a484a4c50535556585a5c5e606365666b73787d7f8aa4aaafb0c0d0aeaf6e6fbe935e227b0503042d036603012f2e80821d03310f1c0424091e052b0544040e2a80aa06240424042808340b4e43813709160a08183b45390363080930160521031b05014038044b052f040a070907402027040c0936033a051a07040c07504937330d33072e080a8126524b2b082a161a261c1417094e042409440d19070a0648082709750b423e2a063b050a0651060105100305808b621e48080a80a65e22450b0a060d133a060a362c041780b93c64530c48090a46451b4808530d49070a80f6460a1d03474937030e080a0639070a813619073b031c56010f320d839b66750b80c48a4c630d843010168faa8247a1b98239072a045c06260a460a28051382b05b654b0439071140050b020e97f80884d62a09a2e781330f011d060e0408818c89046b050d0309071092604709743c80f60a73087015467a140c140c570919808781470385420f1584501f060680d52b053e2101702d031a040281401f113a050181d02a82e680f7294c040a04028311444c3d80c23c06010455051b3402810e2c04640c560a80ae381d0d2c040907020e06809a83d80411030d0377045f060c04010f0c0438080a062808224e81540c1d03090736080e040907090780cb250a840600010305050606020706080709110a1c0b190c1a0d100e0c0f0410031212130916011704180119031a071b011c021f1620032b032d0b2e01300331023201a702a902aa04ab08fa02fb05fd02fe03ff09ad78798b8da23057588b8c901cdd0e0f4b4cfbfc2e2f3f5c5d5fe2848d8e9192a9b1babbc5c6c9cadee4e5ff00041112293134373a3b3d494a5d848e92a9b1b4babbc6cacecfe4e500040d0e11122931343a3b4546494a5e646584919b9dc9cecf0d11293a3b4549575b5c5e5f64658d91a9b4babbc5c9dfe4e5f00d11454964658084b2bcbebfd5d7f0f183858ba4a6bebfc5c7cfdadb4898bdcdc6cecf494e4f57595e5f898e8fb1b6b7bfc1c6c7d71116175b5cf6f7feff806d71dedf0e1f6e6f1c1d5f7d7eaeaf7fbbbc16171e1f46474e4f585a5c5e7e7fb5c5d4d5dcf0f1f572738f747596262e2fa7afb7bfc7cfd7df9a409798308f1fd2d4ceff4e4f5a5b07080f10272feeef6e6f373d3f42459091536775c8c9d0d1d8d9e7feff00205f2282df048244081b04061181ac0e80ab051f09811b03190801042f043404070301070607110a500f1207550703041c0a090308030703020303030c0405030b06010e15054e071b0757070206170c500443032d03010411060f0c3a041d255f206d046a2580c80582b0031a0682fd03590716091809140c140c6a060a061a0659072b05460a2c040c040103310b2c041a060b0380ac060a062f314d0380a4083c030f033c0738082b0582ff1118082f112d03210f210f808c048297190b158894052f053b07020e180980be22740c80d61a0c0580ff0580df0cf29d033709815c1480b80880cb050a183b030a06380846080c06740b1e035a0459098083181c0a16094c04808a06aba40c170431a10481da26070c050580a61081f50701202a064c04808d0480be031b030f0d303132333435363738396162636465662f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6573636170652e72735c757b0000a01e10006f000000380000000b000000a01e10006f0000006600000023000000617373657274696f6e206661696c65643a206564656c7461203e3d20302f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6e756d2f6469795f666c6f61742e727300511f1000760000004c00000009000000511f1000760000004e00000009000000202831203c3c2029e81f100000000000e81f100007000000ef1f100001000000df451a3d03cf1ae6c1fbccfe00000000cac69ac717fe70abdcfbd4fe000000004fdcbcbefcb177fff6fbdcfe000000000cd66b41ef9156be11fce4fe000000003cfc7f90ad1fd08d2cfcecfe00000000839a5531285c51d346fcf4fe00000000b5c9a6ad8fac719d61fcfcfe00000000cb8bee2377229cea7bfc04ff000000006d5378409149ccae96fc0cff0000000057ceb65d79123c82b1fc14ff000000003756fb4d369410c2cbfc1cff000000004f9848386fea9690e6fc24ff00000000c73a8225cb8574d700fd2cff00000000f497bf97cdcf86a01bfd34ff00000000e5ac2a17980a34ef35fd3cff000000008eb2352afb6738b250fd44ff000000003b3fc6d2dfd4c8846bfd4cff00000000bacdd31a2744ddc585fd54ff0000000096c925bbce9f6b93a0fd5cff0000000084a5627d246cacdbbafd64ff00000000f6da5f0d5866aba3d5fd6cff0000000026f1c3de93f8e2f3effd74ff00000000b880ffaaa8adb5b50afe7cff000000008b4a7c6c055f628725fe84ff000000005330c13460ffbcc93ffe8cff000000005526ba918c854e965afe94ff00000000bd7e29702477f9df74fe9cff000000008fb8e5b89fbddfa68ffea4ff00000000947d7488cf5fa9f8a9feacff00000000cf9ba88f937044b9c4feb4ff000000006b150fbff8f0088adffebcff00000000b63131655525b0cdf9fec4ff00000000ac7f7bd0c6e23f9914ffccff00000000063b2b2ac4105ce42effd4ff00000000d3927369992424aa49ffdcff000000000eca0083f2b587fd63ffe4ff00000000eb1a11926408e5bc7effecff00000000cc88506f09ccbc8c99fff4ff000000002c6519e25817b7d1b3fffcff00000000000000000000409cceff0400000000000000000010a5d4e8e8ff0c0000000000000062acc5eb78ad0300140000000000840994f878393f811e001c0000000000b31507c97bce97c03800240000000000705cea7bce327e8f53002c00000000006880e9aba438d2d56d0034000000000045229a1726274f9f88003c000000000027fbc4d431a263eda200440000000000a8adc88c3865deb0bd004c0000000000db65ab1a8e08c783d8005400000000009a1d7142f91d5dc4f2005c000000000058e71ba62c694d920d01640000000000ea8d701a64ee01da27016c00000000004a77ef9a99a36da24201740000000000856b7db47b7809f25c017c00000000007718dd79a1e454b47701840000000000c2c59b5b92865b8692018c00000000003d5d96c8c55335c8ac01940000000000b3a097fa5cb42a95c7019c0000000000e35fa099bd9f46dee101a40000000000258c39db34c29ba5fc01ac00000000005c9f98a3729ac6f61602b40000000000cebee95453bfdcb73102bc0000000000e24122f217f3fc884c02c40000000000a5785cd39bce20cc6602cc0000000000df53217bf35a16988102d400000000003a301f97dcb5a0e29b02dc000000000096b3e35c53d1d9a8b602e400000000003c44a7a4d97c9bfbd002ec00000000001044a4a74c4c76bbeb02f400000000001a9c40b6ef8eab8b0603fc00000000002c8457a610ef1fd02003040100000000293191e9e5a4109b3b030c01000000009d0c9ca1fb9b10e7550314010000000029f43b62d92028ac70031c010000000085cfa77a5e4b44808b032401000000002dddac0340e421bfa5032c01000000008fff445e2f9c678ec00334010000000041b88c9c9d1733d4da033c0100000000a91be3b492db199ef503440100000000d977dfba6ebf96eb0f044c01000000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6e756d2f666c74326465632f73747261746567792f67726973752e72730018251000830000007d00000015000000617373657274696f6e206661696c65643a20642e6d616e74203e20301825100083000000a900000005000000617373657274696f6e206661696c65643a20642e6d696e7573203e20300000001825100083000000aa00000005000000617373657274696f6e206661696c65643a20642e706c7573203e20301825100083000000ab00000005000000617373657274696f6e206661696c65643a206275662e6c656e2829203e3d204d41585f5349475f4449474954530000001825100083000000ae00000005000000617373657274696f6e206661696c65643a20642e6d616e74202b20642e706c7573203c202831203c3c203631290000001825100083000000af0000000500000018251000830000000a01000011000000000000000000000000000000617474656d707420746f20646976696465206279207a65726f00000018251000830000000d0100000900000018251000830000004001000009000000617373657274696f6e206661696c65643a20642e6d616e742e636865636b65645f73756228642e6d696e7573292e69735f736f6d652829001825100083000000ad00000005000000617373657274696f6e206661696c65643a20642e6d616e742e636865636b65645f61646428642e706c7573292e69735f736f6d65282900001825100083000000ac00000005000000617373657274696f6e206661696c65643a20216275662e69735f656d70747928290000001825100083000000dc01000005000000617373657274696f6e206661696c65643a20642e6d616e74203c202831203c3c203631291825100083000000dd010000050000001825100083000000de01000005000000010000000a00000064000000e803000010270000a086010040420f008096980000e1f50500ca9a3b182510008300000033020000110000001825100083000000360200000900000018251000830000006c020000090000001825100083000000e30200004e0000001825100083000000ef0200004a0000001825100083000000cc0200004a00000063616c6c656420604f7074696f6e3a3a756e77726170282960206f6e206120604e6f6e65602076616c7565000820100000000000736f7572636520736c696365206c656e67746820282920646f6573206e6f74206d617463682064657374696e6174696f6e20736c696365206c656e677468202829000000d028100015000000e52810002b000000102910000100000001000000000000000000000000000000000000000000000000000000000000000000000000000000b0a00e02d2c986019d188f007f693500600cbd00a7d7fb019e4c80026965e1011dfc0400920cae00edd3f51cd21893009635e71d45bdf31d4d0100000000000000000000000000000000100059f1b20209e5a6017add2a021d14d4005280030030d1f3007779400331e39c01ff6dc501671b900001000000000000000000000000000000000000000000000000000000000000000000000000000000ecffff03ffffff01ffffff03ffffff01ffffff03ffffff01ffffff03ffffff01ffffff03ffffff01b0a00e02d2c986019d188f007f693500600cbd00a7d7fb019e4c80026965e1011dfc0400920cae00ea405d00a06a3f0039d357020bd2ba0058bc740240d80100ffc83d01d8429401fffa5c0024b2e10176c15f00657002014ffca102f16ac6018406b200e4df7000dfee550232f31a003e2b8b02ca410a00a37859038472d300bd6e15030e0a6a0029c0010098e87901bb3ca0039871ce01ffb6e202b30d4801204ded0091aa560135263303f080650128794a03eb4e9b00a99769029b294800c266af03cda265011b2e7b0112a8fd01d2af9702c2db60003876be02fdd1f50198647e02e781150134b8f203c6a4dd0001000000000000000000000000000000000000000000000000000000000000000000000000000000a37859038472d300bd6e15030e0a6a0029c0010098e87901bb3ca0039871ce01ffb6e202b30d4801617373657274696f6e206661696c65643a20696478203c2043415041434954592f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e6f64652e7273782b100080000000b302000009000000617373657274696f6e206661696c65643a207372632e6c656e2829203d3d206473742e6c656e2829782b1000800000002f07000005000000617373657274696f6e206661696c65643a206f6c645f6c6566745f6c656e203e3d20636f756e7400782b100080000000dd0500000d000000617373657274696f6e206661696c65643a206c656e203e2030000000782b10008000000065010000090000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e617669676174652e7273a42c1000840000005902000030000000496e646578206f7574206f6620626f756e647300382d1000130000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f736c6963652f736f72742e727300542d1000730000003b0400000e000000542d100073000000480400001c000000542d100073000000490400001d000000542d1000730000004a04000025000000542d1000730000008e04000040000000542d100073000000b40400004e000000542d100073000000c204000056000000617373657274696f6e206661696c65643a20656e64203e3d20737461727420262620656e64203c3d206c656e542d1000730000002d05000005000000542d1000730000003e05000029000000617373657274696f6e206661696c65643a206f666673657420213d2030202626206f6666736574203c3d206c656e0000542d1000730000009b000000050000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e617669676174652e7273c42e100084000000c700000027000000c42e100084000000170200002f000000c42e100084000000a20000002400000050617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273000000892f100060000000b20000001e0000003078323465386438623732633063336135663432646465323966346535303235326664663132656465353832643632663564386631633234616237633062373066364d6f64656672616d655f6d657461646174615f686173685f657874656e73696f6e44697361626c6564456e61626c656450617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273007f30100060000000b20000001e000000776569676874576569676874636c6173734469737061746368436c617373706179735f666565506179735965734e6f00506179736672616d655f737570706f72743a3a64697370617463684469737061746368436c6173734e6f726d616c4f7065726174696f6e616c4d616e6461746f72794469737061746368496e666f000042616c616e63655374617475736672616d655f737570706f72743a3a7472616974733a3a746f6b656e733a3a6d6973634672656552657365727665644572726f723a7472616e73616374696f6e5f6c6576656c3a57652061726520756e646572666c6f77696e6720776974682063616c63756c6174696e67207472616e73616374696f6e616c206c6576656c732e204e6f742067726561742c20627574206c65742773206e6f742070616e69632e2e2ec43110005c0000006672616d655f737570706f72743a3a73746f726167653a3a7472616e73616374696f6e616c2f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f737570706f72742f7372632f73746f726167652f7472616e73616374696f6e616c2e7273436f727275707465642073746174652061742060603a200000bb32100014000000cf32100003000000307872756e74696d653a3a73746f726167652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f737570706f72742f7372632f73746f726167652f756e6861736865642e72736672616d655f737570706f72743a3a73746f726167653a3a756e6861736865646361706163697479206f766572666c6f777f331000110000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f7665632f737065635f66726f6d5f697465725f6e65737465642e72730098331000830000003b000000120000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f7665632f6d6f642e72730000002c34100071000000a00b00000d0000004f7074696f6e544e6f6e65536f6d65003900000001000000010000003a0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f73797374656d2f7372632f6c696d6974732e72734275696c6465722066696e6973686564207769746820606275696c645f6f725f70616e6963603b205468652070616e69632069732065787065637465642069662072756e74696d65207765696768747320617265206e6f7420636f72726563740000d03410005e000000b4010000160000003c7761736d3a73747269707065643e426c6f636b4c656e6774686672616d655f73797374656d3a3a6c696d69747357656967687473506572436c617373426c6f636b576569676874730000005065724469737061746368436c6173736672616d655f737570706f72743a3a64697370617463685450686173656672616d655f73797374656d4170706c7945787472696e73696346696e616c697a6174696f6e496e697469616c697a6174696f6e4c61737452756e74696d6555706772616465496e666f50617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e72737436100060000000b20000001e0000006d616e6461746f7279546e6f726d616c6f7065726174696f6e616c6d61785065724469737061746368436c6173733c7533323e626173655f65787472696e7369635765696768746d61785f65787472696e7369634f7074696f6e3c5765696768743e6d61785f746f74616c7265736572766564626173655f626c6f636b6d61785f626c6f636b7065725f636c6173735065724469737061746368436c6173733c57656967687473506572436c6173733e753332737065635f76657273696f6e636f6465633a3a436f6d706163743c7533323e737065635f6e616d6573705f72756e74696d653a3a52756e74696d65537472696e67d8371000000000000000000000000000010000000000000082800000000000008a8000000000008000800080000000808b800000000000000100008000000000818000800000008009800000000000808a00000000000000880000000000000009800080000000000a000080000000008b800080000000008b0000000000008089800000000000800380000000000080028000000000008080000000000000800a800000000000000a0000800000008081800080000000808080000000000080010000800000000008800080000000802f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f6b656363616b2d302e312e342f7372632f6c69622e72734120726f756e645f636f756e742067726561746572207468616e204b454343414b5f465f524f554e445f434f554e54206973206e6f7420737570706f72746564210000a838100059000000eb000000090000003e00000000000000010000003f00000040000000410000006b65792d76616c756520737570706f7274206973206578706572696d656e74616c20616e64206d75737420626520656e61626c6564207573696e672074686520606b766020666561747572656c3910004c0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f6c6f672d302e342e32322f7372632f5f5f707269766174655f6170692e7273000000c0391000610000002d00000009000000430000000400000004000000440000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f6d65726c696e2d332e302e302f7372632f7374726f62652e727301a8010001605354524f424576312e302e320000443a10005c0000005e00000009000000443a10005c0000005f00000009000000443a10005c000000680000000d000000443a10005c0000007c00000015000000596f75207573656420746865205420666c61672c207768696368207468697320696d706c656d656e746174696f6e20646f65736e277420737570706f72740000f43a10003e00000000000000443a10005c0000009100000009000000596f7520747269656420746f20636f6e74696e7565206f702020627574206368616e67656420666c61677320746f2000503b100019000000693b100016000000443a10005c000000880000000d000000646f6d2d73657000526561736f6e7370616c6c65745f62616c616e6365733a3a74797065734665654d697363416c6c4578747261466c61677341646a7573746d656e74446972656374696f6e496e637265617365446563726561736550617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e72730000000d3c100060000000b20000001e0000007531323863616e6e6f7420616476616e63652070617374206072656d61696e696e67603a20203c3d20000000843c100021000000a53c1000040000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f62797465732d312e362e302f7372632f62797465732e72730000bc3c10005a0000003b020000090000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7061726974792d7363616c652d636f6465632d332e362e31322f7372632f636f6465632e7273283d100068000000ad01000029000000436f646563206572726f72007072696d69746976655f74797065734832353650617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273d03d100060000000b20000001e0000005b75383b2033325d010000000000000000000000000000209a99999999999999999999999999991915ae47e17a14ae47e17a14ae47e17a14de24068195438b6ce7fba9f1d24d621096d40968226c787aa52c431cebe2361aab436e861bf0f96184f068e388b5f8142236583849f3c7b4368dedb5a0f7c6106a238dc00e52a6875748afbc9af2d71a884fd766a541b89fdf398c30e28e791507a6121f51012de6b294d626e80b2e11a40951cb8168aed6b7babdd7d9df7c1bea3aa7a234edf1de5f956479e17ffd15bbc885e8f6f0277f1911ea2d81999711f80dd640beb40c65c281764968c2251c9371de33989070ea019b2ba1869b841643c17e29e0a6f3219b1556e79eaf03123735310fcdd785692bbc89d897b2d21cf9905a3fd7df37218996d44646f50e17fa7348cc45e65fe7a0ab43d2d15d72125d860d7a3c3d66a534acd2b64fc9831db19ed79463971e515d2342920ca19c17c14b79dd82df7eda7d4f9b0e0ab4e31268ac5b62d198642a96e55e171020391e53f0e281a7e0b6ee4451b21240b32d18a9264fce524d92586aa78ea899c2571341a47eb0b77b5027aad87ddaf5d0f21e345065c05fc9a652bb13cbaec440c21890a6ea994cd4eb0ec90f3cf2369ace13800a11c3ad5379b141196050bef6b01f670874028bdc2dc16747b3a6fe5e5a1952a029356fb02434869fc2ebfe4b4814db19ee90f2591d909e7f688965d639105f29b0b41dc3fb4c9732a7a8d523f619b2ba595db135963dac5b1fba77e9c4142862e17d275eab9756494cfb92879d100d9d68c9d8c9abf2f00e7af8b7a5951a3e17ba3a7aa1bc5b5a722e2d93844415cb45fb2ec81acaafae8e8b8a429d0311450992b1a6f7dcb24ae478aa9dfb381b04a141c1eb927df56e832d55b12fc71503b46767897564c4589c577727266c11d2eca5d8db886d6df4c625f20b3de01bdb23eb461607be8ac3381e28a3fd4c1649b655d2116cfe6e9c604b534f31d7110e8aefb64f1397b16067458518828b1ca5a1bff8720fac271ab96a37ad01d6161e4e9960c27256b9e160552c24ce44129516c2cd031e57f535cebb136de33a1dabab010b0318ac2a2bd82f768a4f62175689346f02e0bcbb5513f3c46e0cb51289a8edb1d0ccc792ef1eb8d44a7aee1d07ba578e400ad3dbf24b93106ffbf11706c8df7100d5a87cf56f0fda58fc2713d60c66e933bba7fabb4cb2298e60a61e11d7848729fc5295c9a38e540b1a85180eacd0d2bac9a8aa0783d8766fae9d13e3ac1a1e5edcdadda5d1c057b2b0621f4f8a484b4bb0487e51419aac8ec01b19d9a1d3d5d5596dcbdacde156a53316147b81dc77117b573ce2d7e7abeac211102acf6059825ef2c63626a6acaa04b619bba580476818f56bc551eb56559d911496840006ed792a23d1a722dfdd7d7410560734a3e18fddd1810cd13196fc531a456cf6e81a73e4a7343da7f444fd0f159e56f853e2281d535d97525d6a97d91062578db903db61eb2ef2509510bff51ae845a4c7cf484ebc585bdadda6659115206b836cd9d37163ade2e1171f1e4111cd119fad28861c9f480403f364639b1b0bdb18be536bb0e5069d358f1de91516a21547cb0f89f3ea6b4a9172e420ab1137bc71784cdbb84446aa1b846d01451c5f63c1c6d615c70305554903be9a9d1619e9cd6b45de383637770769feae1712c1411646a263c1565858720e97b1f21cce67abd1811c01df7913f571128e2817a5ec5541ce16347f61dc90c10ed886126e4756357d24206502c7e768e48ca41d253978f7301d80ea016cb9201dd7b61784fa2cf9f3b099bb3423614d17acf81239f74728534e5c5f54386815f2ac5a1e2e2cd3b9750b7d7f436053445b8a48185823dcc7f7d53099cf19a9367c3b6d1326d2f9728c89b48eb28f0ef1f92b151fb8412e8fa3072a7228a60bf4c7bcdd18fa9abea54f39bbc1861ed65c0697e413f6f7300919c25e9cd730f0fad624d41ff85f5a071468e549798d262fdf83761960e6e1051020516ec70a52bfe5cf5e141a8581d10c80daf1056f0e9984d94b10f5d468821400c44fd6e4e3f4a0f5121a2b77ed01aa9969d911b71cf7b3f7db14bcc58a018814eead7492b0c55cf9af102c09de68a6ed7c4954ea806f9428b31a24d4e453b857ca3a10559abf76205c1583761d4360793b6273aaaeff5e8016119ebdc8d166f52b9db810b132cb33571b7f646d4152c4bc7d600df48ea25cdf15ccb68a67db69fdcae63dc3d84e7d7f11df8a7772c50f2fabd72f058ee42eff1b80d5925b0473f288ac8c6a3e1dbf651666444249d028f5d3563d55984affea11a3a003424d4188b95795bbf31032ab1ce9e60268d7cd39617977fcc2405bef1654520220797161e72df9c968cd15591286509d998eb568a57c5b767415565b1dd2a64ae13e912051fd15c5f6dd447c170e1fa21aff404da7ca443792b1d0c9124acb69f764ceae0b116e58504fb40f1e3b3ceec550d88b3ca7f179733f900c18c9c9f137da7909ca85f4c7c232403d13db42e9bff6c2a8a96fba0c9eb766c81ee39bbacc2bcf53212695707e2c52a018824995708972a91ab8dd2665f074b3139d75881a0f8475f78c2f3e08e787851f175ea07b7236915f0a269806ec9f3719dfe419965bf84019d5844605f07f2c144cea47abafc600e1103705d18c99231047dd3f454ca467cee724d5b4478fd21906b1cc9dd6e952d81fb7ddc39f72a81438270a4b45eedb79192c7e6919c2861059d8a911a2e35f298f46300f8f36711a7a13bba7811cb3baa56bf3d8d85e27152fa995ec9ae3286251898fade04bec101775efe0f7380e9de80e4caf9aac131b792a591a932dd8b05372d625e256a9152e5547480fbe798ddcc1deb7814554117cbb0bda7e968f15949c978ccf08ba1b972fd614ff11a67776b0dfd6726d2e16798cde43ffa751f991f3b278f5bdbe118eadfdd2fe3f1cc21cecb75a2263641cd88a64423233b00117f05f15b5b5b61646a2839b8ec25901ac59e6dd90c42b12a303395f1704f6ceacc2a3fc1ad4121d839c2d4cac695e72bd9b1cca484342179ce38ad6895418f5fde2160807699b12c605abbd0f548dee2f6bf10cd874c51d056b22fe7276d7be8c22c170462ad11704bc4ecb28c512ffd64e678d6bbb0d13a0f97d78743b51cb247ed87b125f7c1e4d61fef929c90d09b731adfc417f63180a81cb9421d4d7a0c52724ca34cc821377ce7854cfb9bf676f0c6d4321ad371ff9712ddda594cc1f59708acf4d57f918c7f4bd7d51ddd67f7af3a13f3eacfa130bee2fc9e82ebeffc3b89c32fd79f71fd624f3a020bf316636fa16c2fdc79219781d5c1a1acc27b85efbab01cb6c751460e47c7bae09539318c9bc67a2f05d1099a094c5b042eb1ef474943f6ae72f1ae1e67604270289e55c2add32881ff314e7eb2b9d85cea0b7b0eeb028a07fc210d8dfdf616f4a0159b44a4e7433ccd01aad4ce6e725d5cde029a23e908fd67315f1d651865177714deeb4cbd972782911e857e9d6e8bee87bb054ac8f848d751b201321df5332bafc59dd890c6aa4f7158042e7184328c863ae4a6e70eee99211666ad827380d0d0617114a1a17431e1ceb21adec2ca43d6b12746e7b129c7e16564e57bdf01cfe88db5c58fc41e3fe11234a2562b49496415f618d603605cb1ce9d41de829aaab677fe73d4df8d0081787dd1720bb2156b932b964d7f9736d12a5958c662b6923c2eac13af2c2ec7b1d1dded61e89ba82cebb34625b025796171818df4b076235a5fcf6b4e201acde1259f36479d89c883b94f187373613311ee1f583c7464a6dfcdc5a06c6914227181a2b03069f6e573017af9ed1a79b521390ded13ccb7d251a2518311ca692ea1e40e5a7303cfe1d48b7795ae384a8bb18005186c0c9314bd3c5c7ae829d53c913cdb4a3cd42e9115209a617d1c885a81fa4901c3e0221db7407b8df403a9e5319500d4acb01b415f705601967fbe44214a70a08099b29def837b37a52fc833510d7dd0ca89142308e59b82ab79339ef19134b0a200e028d3ee1f9eef84261bf140f3c08803e9b3d65e7c758fa9b1a9910e42c0d0064f8c86ea50c8e90f9908e1aea23a499e9f9d38bb7a3714061da3e15bb1c50e1ba94a93cf982f4991a15ff102b61b39bc4ba75c78ed120c35dbb311b891a29166a95c4d20b0ee768b162c115a17bba118877d0db6f3e1f87278267119b925d1c40bf802ce663983e3fd0d81b4975e44933cc33bd51b64665ff0c4716d45d506e8fd68fcaa75e0551cc70d21153c9b3e34b571944d9fd6e4eade7831ca93af68209794703e19725a58aeccf16bafbc468d4606ccf807984ea6ef03f122af9070e87347ae59af5d3104b1a331d2294390b6c902e51e22a43da08155c17b5a9c7d5bca68bda8155cfe1d310b012870fd9222e71df909c55e5025381e61d6c0c144f8b5a4cda16de1dcfa89aeb178aa3a9a5a27ba3ae787eb1a520e22213a905a9a26a5fd27d2797b5a29a369e1e54d12082887fdb971facf74e15927e1877a780ce06667c794c23c6d8dd749813f10b01e40a702d8fad6ba32796545a1f5ad60050a259240cbeefb51f7810151915459ad981141d70fef2f7b2f9d91014776a7b149b4317c0fe5bc6282e7b0d10f24392edc405f2ccca2c0a0e7d2baf19c29c0ebed0375b0a6fbda171ca228c14cee33ecb73f948088c97b427d51b7010b09f6478ec5b0edaac25540c55f94c1ac07f5060f0af3e7bbdb7a9d610610a1533664080f3bfcb95972ceede731ad5105270cd665266acef5847b064b990ee1adb59a4b80e852326476cf3b6faa68b1549aeb693d8d0821e6c23295f95853c1175b08a1ff41a9efdac38a8feee08941bf759d5b229afb197bd938698250710162c7b77f5ba258eac97dc9e131e6ca61113c558222b097d7abf2dfeb8c9793d1c766aad4eefa0fd61cc57cb60a1949716c5eebd0b591afee7091309e74ddd12123ab1fc455b5d63a6dc840ed8affbea1cc88d306baf4a1c85b0d03e13f3622217d4d726bcf26ee3d026dacb75c2e88112868ca4c6ea179fb4d72946899da79c1d6b705005efdf182a46ee04a11786b01789f3d99d25b3e0546b8b9d4d799ef3127452f6626febcd8778452f7c2897521e5da85e82bf220bd3c66abfc986124218e4b94b68cc1b3c0f9f88ff3ad20e68136d2979407a2c601898da989183e40c1f24219433c856b34613e2130e361dd718b64d4329a0788f38dcb4dca4914adf138aaf6ba866277f5a602161a182aacb1fa2bfefb9eb8532154db44db49bbb6f194e998c6189d18eaa3d90a4f6e26259140ce1d61aa1a7d8eecad9b62b4f824710459b245e9b72277e11f68adfb1030c1a04491d1849f585fe0df83b195b69d614d0a04a13d45d9ecba4f92f147c87ab104d01115253c963df3a5ce6b9f90bac1a7167da740fa11c192fb01efbfa6f5615c152482ad980b0ad25c04b2f2ff3111134510daa8e34e71509cd12b27eeb4f1bc40d71ee3e5d1fab6d0a0f283289d9159da48d8b651719bc57080c2028d47a11943a7c123cf2f42c590de0ccd9b9f71b439596dbfcf4c3f0e03db370e1c75f1603111216975d365a1acbf5268139e61104e81cf024fc569090de220b358fa31cd0ece38c1d30dfd9a64b82a25d3fe916da23833db1597fe1eba2ce4eb13254125c39382fb5c2cb6879d17de44e84531de32d60bf5d35d65394a76450720376171c8be665b12a78a976ecb6a68ecfc412fa44d76fb5aa260ff1138bd77db2071e626adfbf2a22523f27436fac642806184e887f99884edb651f9cf289502038134a0dcc28744ac56f6593ea0fb433c01e3ba40987f6a16a59840f2273f6c2991896b6076cf8e7eead36d9b4f59135ae1356570ce0f33f7e4924f5ba2283227d1f45acd64cf6ff64d4e99095e868e83019d189783df8ff8343ee7344ed5320271474a19397c6cc9ccff18f03f10f4d1f105202b925a447617f1cb305e87faecb190f35c7b7e9d24dcc165cd1ecfff1a214d990d25f210f0b3d12b0da23335b8210c1e75099684bab6150b32a06852b6a1a67b94014baa2224e405c556b6abc2115539400dd94e84e0bcd4944bceec9e71051ed00c887da171248a9d3c64a760c1bdabd00a06c4846db6c87dc6bd591a315af64cd4cbd0605498a9fe3efdda74f11b13ae27ac80a08a843ff38e62fa6b21bf42ee8fb39a2395369ff931ef38428165df2ec2ffbb4c77587ff0fb2f503ba112eea47e69121d9223fff7fb622d35c1cf254068541817ab565ffff91e8a8b016f5433837010162c4b73233db86ed2612ee9ff3f10168363a5984eb91a4150b1d8b19f6279bb95efbe069bc7450113c17d67a5e86e2fa7e2fe787635d407496125691fdd6d0f797e571d93862cd86bd1dabdaca780d937984c17a2de83dd2ca1756156f2d714261d09ac88a8631a80813222218af4e6a684d91daaa3d4f40741ee8b479f23e8853a4daae88643f005d18875d6128ff6cdce9ae586d50cc997d13a495680d65ae60a9e48d481a7a5c2f1f8344ed3db7beb3ba8371a0ae61b0f218369d8a312c32f62e36c1e6bee759f513f0617782131dbde4899bd7973ff6ee1f5a4e2c35a97dca83a1afdfdf32f88b1915a556f720fea19ce7f2b24cc2f96f14aa1d12f9b3311b4ab9288f709b945910dd95b6c1ecb55e43f50de580c5ed281a4ade5e01575ee535c4a41d67048bed14d5b11801ac7eb7c4691d7e52d008be1022b65a9b799725a10f2f30b7b3a7c91a815e154961acb74dd958f3f8c21f6e159b4b44078123c6d7ade0f59335e624112bacd33e9b053d5949345686223d6e1bbc89dccb159efde06dc3110582caf11563a1e36f1118feb3246941379b3b8e11d19bd27fb559638607753525c5c5161c0ee30e339114e9d1d290f750379e78160b1c3f8fda76ba74750dc6402c18fa1178c631e59024f7edbb48a367e059c31c2d055bb7401d2c8bc9d3b51f4dae021724047c5fcd7d566fd40f2be6708b6812066dc69848c9f07eedb2113d4e12741d9fbd9ee006a1c09857c2a7fda40e9017e6ca4b4dd2800047799becca50a5d912a24479481dce00d88ec5ad448108291e82d02d6d17d833133fd1579d9ad32018cea624247946f6a865a7ac4a15764d137da43aa08e3dbd746fa57a778856e21e645095e63e31645d8cb7fbc50612b518b7a6aaebcb8db64a702c96d16b0ec41357a4aa12131624111a47f0e81217a01fdfe9ee0edc4483da146cf35342df4c198021bfd87c9d02e243232943687f3d143381327afd7d684e361c54cfb9323110b8ce509095c9404abdc6b94b2951e819c60ba7a677d4330831d2c76f87dab9146b09ec1ec67629a08d0ed3bfd2ae9410dfdbac64a35742004917b8ff1d7e871a19e323eab5df01cda0126099b1313915aeb51c88914cce704d75e6ad278efa10e25594a6b5ade31aafbb70490c7d2a1be8774385c457e97bf2628d073d97bb1587f935046a7987c98eb50a0664df621171c2bc06108fa575e48877d66c65d11b2735ca6ba6a5b7f7e9d392abf01d41161fc4a1bc1e1ec65fee0f0f568db1cd1165d302616463a3ff16b3b189484f7c1c51dc9b4d501ce932df288ed406d9c9160e7d497173e3208fb220d87605143b127c2e0f8285059b7eeacd59f13b532b1dcabea5019e37afcbeed747f42fdc5517a19884344bf95809bfac6cc38c16ab120000000000000000000000000000001000000000000000000000000000000014000000000000000000000000000000190000000000000000000000000000401f0000000000000000000000000000881300000000000000000000000000006a180000000000000000000000000080841e00000000000000000000000000d012130000000000000000000000000084d7170000000000000000000000000065cd1d000000000000000000000000205fa012000000000000000000000000e8764817000000000000000000000000a2941a1d000000000000000000000040e59c30120000000000000000000000901ec4bc1600000000000000000000003426f56b1c0000000000000000000080e03779c31100000000000000000000a0d88557341600000000000000000000c84e676dc11b000000000000000000003d9160e45811000000000000000000408cb5781daf1500000000000000000050efe2d6e41a1b00000000000000000092d54d06cff010000000000000000080f64ae1c7022d15000000000000000020b49dd97943781a0000000000000000949002282c2a8b100000000000000000b9340332b7f4ad140000000000000040e70184fee471d91900000000000000883081121f2fe7271000000000000000aa7c21d7e6fae0311400000000000080d4dbe98ca039593e19000000000000a0c95224b00888ef8d1f00000000000004beb3166e05b5b5b81300000000000085ad609cc94622e3a618000000000040e6d878037cd8ea9bd01e0000000000e88f872b824dc7726142130000000000e27369b6e22079cff912180000000080dad003641b695743b8171e00000000908862821eb1a1162ad3ce1200000000b42afb22661d4a9cf48782170000000061f5b9abbfa45cc3f129631d000000a05c3954cbf7e6191a37fa5d12000000c8b34729beb560a0e0c478f516000000baa099b32de378c818f6d6b21c00004074044090fc8d4b7dcf59c6ef11000050910550b47b719e5c43f0b76b160000a4f50664a1da0dc63354eca5061c0080865984dea4a8c85ba0b4b32784110020e86f2516ced2ba72c8a1a031e5150028e2cbae9b8187698f3aca087e5e1b00596d3f4d01b1f4a199647ec50e1b1140af488fa041dd710ac0fddd76d2611510db1ab30892540e0d307d951447ba1aeac8f06f45dbf428083e6edd6c6cb41024fbeccb161232338acdc9148887e114ed39e87e9c96febfec40fc196ae9191a342451cf211efff793a83d50e2315010416d2543aae5fef5b8124de45a3e641492c8eed3149f7e336757609df14d7d19b67aea08da465e00416db8046ea1dc1fb28c924548ec3aa04844f3c2e4e4e913de2ff7565aa749c85a15b0f31d5ee418d6fbb4ec30115c7ab11a9c70a5751d1f651df193be8a79ecae90616687697213bf64ed386eed97a7daf4f93fe9034f18efbd28c7c9e87d511172f88fe3c4621eb576791c7eb1eed24a47fb390ebbfd1262d497a3dd5daa871d197ac8d129bd177bc97d0c55f594e9649f983a4674ac1ded9dce275519fd119f639fe4abc88b126845c271aa5f7cd6863cc7ddd6ba2e17c2d6320e95771b8ca80b39958c69fa1c39c6df28bd2a915749a743ddf7811c12c8b717736c7575ad1b9194d475a2a316baa5dd8fc7d2d29862b5b949138b4c1c9487eab9bcc3839f5d11140eecd6af11792965e8abb46407b5159911a7cc1b16d7737ee2d6e13d49225bffd5d0bfa21b66088f4d26adc66df598bf85e2b7451180caf2e06f5838c9327f2f27db259715207d2fd98b6e867bff5efbf051effc1a34aebd67170534ad5f1b9d369315de10c119ad415d06819837624404f89a151532601892f447a17ec57a5505b6015b1a1f3c4fdbf8cc246fbb6c55c311e17810270b23123700ee4aeac72a3456199714f0cdabd64480a9dde47935c1abdfbc19b6602b062bf0890a2f6cc158cb0b1610e438b6c7356c2ccd3ac7f12ebe8e1b141dc7a339438777800939aeba6d722219e4b80c08146995e04bc75929090f6b1f8ef30785ac615d6c8f1cd8b965e9a21372f049a617ba7447b3234e28bfa38b188f6cdc8f9de85119a0ac61f2ae8cae1ed9c3e9796231d30fe40b7d57ed172d13cf346418bbfdc713dd4e5cade85df81703427dde29fdb9589462b3d86275f61d42490e2b3a3e74b79c1d70c75d09ba1292dbd1b5c84d51e503254c39b58b6817775246e33aa1a5de442e9f87a2ae421d8af30bcec484270beb7cc39425ad49126df08e01f665f1cd255cf4f96e18dc1688acf28173bf6d412f7371b88a1e931cd5ab3731a897e488fde746b316f3db11ca96853d92bd1debfca11860dcef52167dfce6ccf62ce5257cca1e78d3abe71bce5d10401a3caf978d3e132b64cb7011427514d0200b9bfd300ed8353dfecc1592921904e9cd013dbd114e83cc3d401b9bfb8fa2b120214616cb10d29f26081182fa330bde68a9d7dbfd94c647304a1523f9008e15c393cd523d3ab859bc9c1ab69bc078ed597cc053662413b8f5a110a3c2f0d668709bb0e87fed172673ca144cf3ac0c834cc2dce2dfe89def0ffd190f18ece7d16ff9c9ed8bb1c2f5293e10131ee761c6cb773ce9ee5d3373b44d1498e560fab7be958ba36a350090216119fe1ef9f8652e7b6e4cc54200f469b91f5fb39bbbfffc0cc54fbb298038e2d31337a082aa3f3c50b6232a34a0c6dac818444823954f4be4a3ac3441487811fb1e2b0d36bd11af6ee6ebc0282debea5c137590832cd65a0ae026f172f8a52534189374a4b78bf10c9870ad8f760f2f411edcc8c652f716085f66cc19aa69bde812137b7827b51ccaf67f3fa014c4eca217d7995671e2a37cf45f4fc819f5a78b1d2620d6866de6cdf89b311d30f948771230a88be8086001f7027e247c371b15173c92ae220bb8c1b4839d2d5b0562da1c651badf50613f9507282fc58437d08123f6218b3c85737e50ea33b2f949c8a16cf7adedfba2d859ed28b0a3bb9432d1cc10cebcb943c13a36397e6c4534a9c11f1cfe5feb90bd88b3c3d20b6e85c0316ee439f7ea80eceae8b4ca8e32234841b758a234f29c9404dd72f49ce95a03211126deca273fb9020cd7bdb41bb487f155688a78b503ab568c05a5212ea1adf1a36b5485772447141b878734bd270cb1083e21aed8e95cd51e65650de064dfe14249b61a8f2fa40e69f6ce49548e03d1af7003da9d79ce8efe3c3ae5d2dac661034418c930dc4e2ebdc741ab53857801481516ff81075db26141261e2066da019f192459b2a2949984cab7c4d24440410adf7164275735bbe1fd6db602d55051498b59c925250f2ada7cb12b978aa0619ffe2433767e46e99917e57e71655481fdf6d8a82c04ee5ff1aaf96502e358d1357092da370a2debfe15abce479827018ad4bf8cb0c4bd62f9a71eb5d18a38c1e4c2f7bffe7eee55d0027b33aefe517131ffb59ffa16a5f75c0f05f096bdfdd17e779307f4a45b792f0ecb7cb4557d51d304c7e8f4e8bb25b16f4529f8b56a5123cdf5d33222e9ff21bb127872eac4e170b5735c0aaf946ef629df1283a57221d675621b80a5c8cd55d0297598476351201ac29660d73ef4af5c2fc6f25d4c2160117b4bfd04fab9db2f3fbcb2e89731c608ed077e2118ba24f787d3fbd35c811f9b1c4155bd62d8b63d65c8f2c433a1677de35dbf14bf96dfc0b34b3f7d3c81b0aab012977cfbbc47d8700d07a845d11cd1542f354c3ea355da9008499e5b415409b12302a746583b4d300e5ff1e221b08a10b5e9a681fd2508420ef5f53f5104a898ef5c042a70665a5e8ea37a832159d2bf23271135148becea2e545527f1a425bd7bf26ac32ed36c185af6b938f101232cd6f30577fa88431679b4678b314977ec08bfc2c9fd2e5fd40425856e0191e4f58d71d7ca3a3af9e6829f7352c10e6622e4d255b8c8c5bc6c2f3744337149ffb79a0ee71af6ff277b33052144519877a98486a4e9b0bef55e0bc6659961f944c5f6d02114167b5350c36e0f7bd13ba1fb708435511c122438f43d875ad18a8e7e4ca93aa5571eb1373544ed3d81ec910cf5e9c8ad52673ecc7f410844713fbd4827643ed8af08fe7f931156519183a8a235494a8adec7361787e5abe1f1e643696b45c89ec73e83c0b8ff8d6d312fdc3bbe1b3abe790220cceb2b6cc8817fdb42adaa09621352b8f815fe4ff6a1d1eb15a8824fe34017bf9b0bbeedf6212655d71aaad3d82c1d9379d6aea97fb16bfb40d1519cde231d0854405e57dba1cf79028ad2fc02d1fa2d34a23af8ef41135b572983b30f9a68a881dec5ab2711682628f7e4a7cb750adea24a7f11e0e1c919d198faead7252ac12770857d38811f604e0321a590f6757d794ca2c08eb15330698bf602fd3402d0d3afd37ca651be003bf779cfd83483c4844fe629e1f11d8c4ae9503fda45a4b5ad5bdfb8567150e761a7b443c4e31deb04aad7a67c11ac989f0ccaae5d0de8aae4eacace0b8103bac2c80151f85962d5a62d7d718e7144ad737e0da6626fcb8f03acd0ddf201a8ee622cc4800989d73d644a0688b541032a02bff5a00fe84100c56c842ae69143e88f6be71803da6148f6b7ad31984194e2ab42e8ee0cccfd97206594820e51f709a30dd580ce021c807a4372d34ef130dc17c146f0f582aba098d853801eb1850f19bd94a13eeb4284cf0a686c1251fd27601c80ecc1471992f5628f498771386d4017a12ff59cd7fbb6b32317f5518a8498218d77eb0c05faa067ffdde6a1e096e516f464f6ed87b2a646f5ecb02138bc9250b18e389ce1a353d0b367ec317ee3bef0dde5b2c8261820c8ec35db41d7585b5c86ab95bf17cd1c7389aba9012d2e6e27ac5a7b22ddcc5f9c640e9341786a09bd9b6511f395337b8f89023021d544401481293b3039422739b3a562112699501dad677a00439eb4f42c9aba916c3fa8190cc95c84507e6e392bb16541cba3c51da9f5d9d8bc46fce3b358eb411e88be5d007b584aeb50bc28ac2b12116e3ee1ec549e2251aa38e722d331eaa1b4d55331b6ead57f0259967fcdf524a11a12a00a2c9986d6c6f7f81fb97e79c154935800afcfe88474bdf61fa7d21041b4e2190865d9fb50c8f2b7dbcee94e210a12934e83407e3cf72769c6b2a3a1b150a34412202c9db830f948306b508621a86c06855a15d69b2893c122471457d10a7f0c2aa09b5031faccb166dcd969c14d1ac73154ca2c426977e5cc880bcc319034c688d6fe53a781ecf397dd0551a10035fc270cb9e4916e642889c44eb2014c4f6f24c7e06dc9b9f53aac31526291976b42fe01d08d38287e894349b6f731fc9d01dac12e5c3b15411dd00c125a813fc44255757de34dea9551441312f92183b96ee2ced15c255146b5991fdbab61ee51d153cb44d99b5ece2d77ade3432135e651a4b21a1ffe2a7db8d1916c2fe17b6fee09d6989bfdb9152f19f9b72fe1d319fac02e2b557299bd3f643a107bf12fec657835aa3adf38188f49489c96e17bdb82d24310c9970a2aa31faeb7b4a1d76939cb69ea75f86a50a5f7c738d4e1254b843648691f7e74ecd765bd030e21669a654fde775f5a1a280547204bd9a1c01e854feb06939a565d074c722b6e0110222ea3d1dc4870e7f045279abe3581682aa648d24b529d29e85a657961cef1b91ea5ed836115a438313c8f6dd71751136a5768e8495301464187a7455ced215834e14b2e5ba3c197d9e98d1ea81471b12b14c8fcff4c52f0e63ffc232b10c1156dd1f730372b7bbd13bbf737fdd4f15acd4e74f844ea52ac60aaf50dfd4a31aebe4f0b11251a7dabb666d920b65a610261e6d5e572551d16ac008774efecf14b0650836ad6ea58585f0ca14e2fd031a8e3fc5412c65877353d6fe4cad7e4210718f3652773e6950e88b3ea0581e53144e33c426158e8364e22e4ec8eee56719224075709a71a4fd9aba617a6adfc11f1548498600c786dea0147d8ca22bd9131a9adba7c0782816c9599c2f8b76cf18a180d2d1f096b25b3b7083fb2d54031f64902383569e4f19252632bd9c1462137e74ec23ec85a35faeaf7eecc3993a189d91e72c67678cf7995b9ee73440491e02bb107ca0c0b73a40f9c21021c8ed12c3e9149bc8b0654990b7f354293aa9173324dac1fa1cbf5b74a530aab388931da05628b91c7257b968675e4a70357c12486c72e7a34eade74201f65ccc421b175a074fe14ca298a1938133747f13e21c9864d10c7065ff44fc30a0a82f4c0d12bebd0510cc3e3f563b3dc8923b9f90162e2d07147f0ecf2b8a4c7a770ac7341c3d7c846c0f69615bd66fac8a66fca0114c9ba54753c339f2cb8b572d803b09161f028f192834c8eebe6ead38608a8b1b5361f90f99203d5537656c237c363711a8b9f753bf688c2a857e472c1b04851512a8f528ef822f75265e59f72145e61a0b899979d5b13d09d8da973a35ebcf104eebffd74a1e8d0b8ed13d8902e6031522e6ff8ddd65708ef1458d2b83df441ad5efbf78aa3f06f9b64b38fbb10b6b10caebef1695cf47b7a45e067a9ece8514bde6ab5c7ac319e54df687184642a7193670eb792c1a30aff0f954cf6b890810434c6698b720fcda6c382ac3c6ab0a1454df7f7ee528bb1188c6f473b8560d192ad71fde1ef329162af8f19066ac501f7ae6d34af337da4d1a3b971ac06b921319e0881df0c550e1e0093d21b00677181f18eb246cf7a419594c8c295cc8941e13ef1297a31a07b0b7aff79939fd1c13d8aad77c4ce1089ca59b7500883ce4178e950d9c9f190b038f029300aa4bdd1d797d88c103f0e66199e15b404a4faa12d79ceab104ac60baffd972d01ce354170d4465de05d7f8a87f908f04e41b2a1d884affaa63869bc94fbad9826e513a122a1dbf95fc6702bce3289023cae5c81674e42ebbfb0103ab1c3374ac3c1f7b1cc94efd543de1e1eaf19fc8eb85f3cc117ba23caa8c599a65eec7ba66673040161acbcbd4efef00ffe9796940813cd01bf05effe4f595603f32ec41c8d0256211ac363f5e73bb38cf3e6752fa44afba155704cf3550ea06830e01e738165b291bb662a1217252e411a96090e3edd8f91064bb09aa0e675d56d378745c294f38153d2a8c54d2c0f42b089791b3f362861a669ad77483f8781b65fe3a50d8fd931000810d52a4365762febd49644efdb81440e190664d04edfa7d2d5cfda13ce719c88c1a60b022d4bc6e9c593ee5853010fa2f21785c2b096c8a03f08d5ea73c14f87b299633760b076d046c3136d14b19f6dab37bc053ce488805c7bd83c59e1fda68504d58f4802d75639c56723bc3131083a4606e31e178527c43ec4e0ab4183030303130323033303430353036303730383039313031313132313331343135313631373138313932303231323232333234323532363237323832393330333133323333333433353336333733383339343034313432343334343435343634373438343935303531353235333534353535363537353835393630363136323633363436353636363736383639373037313732373337343735373637373738373938303831383238333834383538363837383838393930393139323933393439353936393739383939302e302f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6d61702f656e7472792e7273d3681000850000007001000036000000617373657274696f6e206661696c65643a20696478203c2043415041434954592f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e6f64652e7273617373657274696f6e206661696c65643a20656467652e686569676874203d3d2073656c662e686569676874202d20318869100080000000af020000090000008869100080000000b302000009000000617373657274696f6e206661696c65643a207372632e6c656e2829203d3d206473742e6c656e282988691000800000002f070000050000008869100080000000af040000230000008869100080000000ef04000024000000617373657274696f6e206661696c65643a20656467652e686569676874203d3d2073656c662e6e6f64652e686569676874202d20310000008869100080000000f0030000090000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e617669676174652e7273f86a10008400000059020000300000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f7061747465726e2e72738c6b100074000000b7050000140000008c6b100074000000b7050000210000008c6b100074000000ab050000210000008c6b1000740000003b040000240000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f7061747465726e2e7273406c100074000000b705000014000000406c100074000000b705000021000000406c100074000000ab050000210000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e617669676174652e7273e46c100084000000170200002f000000e46c100084000000a200000024000000406c1000740000003b040000240000003a3a416c6c2070617468207365676d656e74732073686f756c642062652076616c69642052757374206964656e746966696572734900000008000000040000004a0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f74792f706174682e72730000dc6d1000620000005a0000000e000000dc6d100062000000720000000a000000206973206e6f7420612076616c69642052757374206964656e74696669657200986d100000000000606e10001f000000dc6d100062000000900000002100000072234d697373696e675365676d656e7473496e76616c69644964656e7469666965727365676d656e740000004900000004000000040000004b0000004c00000004000000040000004d000000012f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f737562746c652d322e352e302f7372632f6c69622e72730000ed6e100059000000bf0200000900000057000000080000000400000058000000590000005a000000756e69746120737472696e6762797465206172726179626f6f6c65616e206060866f1000090000008f6f100001000000696e74656765722060000000a06f1000090000008f6f100001000000666c6f6174696e6720706f696e742060bc6f1000100000008f6f100001000000636861726163746572206000dc6f10000b0000008f6f100001000000737472696e672000f86f100007000000756e69742076616c75654f7074696f6e2076616c75656e6577747970652073747275637473657175656e63656d6170656e756d756e69742076617269616e746e6577747970652076617269616e747475706c652076617269616e747374727563742076617269616e746578706c696369742070616e69632f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264652d312e302e3230362f7372632f64652f6d6f642e72737f7010005d000000ec080000120000008f6f1000010000008f6f10000100000060206f72206000008f6f100001000000fc701000060000008f6f1000010000006f6e65206f66202c20000000586f1000000000002e307536346e756d6265720000000000000000000000f03f000000000000244000000000000059400000000000408f40000000000088c34000000000006af8400000000080842e4100000000d01263410000000084d797410000000065cdcd41000000205fa00242000000e876483742000000a2941a6d42000040e59c30a2420000901ec4bcd64200003426f56b0c430080e03779c3414300a0d8855734764300c84e676dc1ab43003d9160e458e143408cb5781daf154450efe2d6e41a4b4492d54d06cff08044f64ae1c7022db544b49dd9794378ea449102282c2a8b2045350332b7f4ad54450284fee471d9894581121f2fe727c04521d7e6fae031f445ea8ca039593e294624b00888ef8d5f46176e05b5b5b893469cc94622e3a6c846037cd8ea9bd0fe46824dc77261423347e32079cff91268471b695743b8179e47b1a1162ad3ced2471d4a9cf487820748a55cc3f129633d48e7191a37fa5d724861a0e0c478f5a64879c818f6d6b2dc484c7dcf59c6ef11499e5c43f0b76b4649c63354eca5067c495ca0b4b32784b14973c8a1a031e5e5498f3aca087e5e1b4a9a647ec50e1b514ac0fddd76d261854a307d951447baba4a3e6edd6c6cb4f04acec9148887e1244b41fc196ae9195a4ba93d50e23150904b134de45a3e64c44b57609df14d7df94b6db8046ea1dc2f4c44f3c2e4e4e9634c15b0f31d5ee4984c1b9c70a5751dcf4c916166876972034df5f93fe9034f384d72f88fe3c4626e4d47fb390ebbfda24d197ac8d129bdd74d9f983a4674ac0d4e649fe4abc88b424e3dc7ddd6ba2e774e0c39958c69faac4ea743ddf7811ce24e9194d475a2a3164fb5b949138b4c4c4f11140eecd6af814f169911a7cc1bb64f5bffd5d0bfa2eb4f99bf85e2b74521507f2f27db259755505ffbf051effc8a501b9d369315dec050624404f89a15f5507b5505b6015b2a516d55c311e1786051c82a3456199794517a35c1abdfbcc9516cc158cb0b160052c7f12ebe8e1b345239aeba6d72226952c75929090f6b9f521dd8b965e9a2d352244e28bfa38b0853ad61f2ae8cae3e530c7d57ed172d73534f5cade85df8a75363b3d86275f6dd531e70c75d09ba1254254c39b58b6847542e9f87a2ae427d547dc39425ad49b2545cf4f96e18dce6547371b88a1e931c55e846b316f3db5155a21860dcef528655ca1e78d3abe7bb553f132b64cb70f1550ed8353dfecc2556124e83cc3d405b56cb10d29f26089156fe94c647304ac5563d3ab859bc9cfa56662413b8f5a1305780ed172673ca6457e0e89def0ffd99578cb1c2f5293ed057ef5d3373b44d04586b35009021613958c54200f469b96f58bb298038e2d3a3582a34a0c6dac8d8583541487811fb0e59c1282debea5c4359f172f8a525347859ad8f760f2f41ae59cc19aa69bde8e2593fa014c4eca2175a4fc819f5a78b4d5a321d30f94877825a7e247c371b15b75a9e2d5b0562daec5a82fc58437d08225ba33b2f949c8a565b8c0a3bb9432d8c5b97e6c4534a9cc15b3d20b6e85c03f65b4da8e32234842b5c3049ce95a032615c7cdb41bb487f955c5b5212ea1adfca5c79734bd270cb005d5750de064dfe345d6de49548e03d6a5dc4ae5d2dac66a05d751ab5385780d45d1261e2066da0095eab7c4d244404405ed6db602d5505745ecc12b978aa06a95e7f57e7165548df5eaf96502e358d135f5bbce4798270485f72eb5d18a38c7e5f27b33aefe517b35ff15f096bdfdde75fedb7cb4557d51d60f4529f8b56a55260b127872eac4e87609df1283a5722bd60029759847635f260c3fc6f25d4c22661f4fbcb2e89735c61787d3fbd35c89161d65c8f2c433ac6610c34b3f7d3c8fb618700d07a845d3162a9008499e5b46562d400e5ff1e229b628420ef5f53f5d062a5e8ea37a8320563cfa2e545527f3a63c185af6b938f706332679b4678b3a463fe40425856e0d9639f6829f7352c1064c6c2f3744337446478b330521445796456e0bc665996af64360c36e0f7bde364438f43d875ad18651473544ed3d84e65ecc7f41084478365e8f931156519b86561787e5abe1fee653d0b8ff8d6d322660cceb2b6cc8857668f815fe4ff6a8d66f9b0bbeedf62c266389d6aea97fbf666864405e57dba2c67d44a23af8ef46167891dec5ab2719667eb24a7f11e0ecc6713770857d3880168d794ca2c08eb35680d3afd37ca656b684844fe629e1fa1685ad5bdfb8567d568b14aad7a67c10a69af4eacace0b840695a62d7d718e77469f13acd0ddf20aa69d644a0688b54e0690c56c842ae69146a8f6b7ad31984496a7306594820e57f6a08a4372d34efb36a0a8d853801ebe86a4cf0a686c1251f6b305628f49877536bbb6b32317f55886baa067ffdde6abe6b2a646f5ecb02f36b353d0b367ec3276c820c8ec35db45d6cd1c7389aba90926cc6f9c640e934c76c37b8f8902302fd6c23739b3a5621326deb4f42c9aba9666de6e392bb16549c6d70ce3b358eb4d16d0cc28ac2b121066e8f722d331eaa3b6e9967fcdf524a716e7f81fb97e79ca56edf61fa7d2104db6e2c7dbcee94e2106f769c6b2a3a1b456f948306b508627a6f3d122471457db06fcc166dcd969ce46f7f5cc880bcc31970cf397dd0551a507043889c44eb20847054aac3152629b970e994349b6f73ef7011dd00c125a82371561441312f9258716b5991fdbab68e71e3d77ade3432c371dc8d1916c2fef77153f19f9b72fe2d72d4f643a107bf627289f49489c96e9772ab31faeb7b4acd720b5f7c738d4e0273cd765bd030e2367381547204bd9a6c73d074c722b6e0a173045279abe358d67386a657961cef0b7414c8f6dd71754174187a7455ced275749e98d1ea8147ab7463ffc232b10ce1743cbf737fdd4f15750baf50dfd4a34a75676d920b65a68075c008774efecfb475f1ca14e2fd03ea75d6fe4cad7e4220768c3ea0581e5354762f4ec8eee5678976bb617a6adfc1bf76157d8ca22bd9f3765a9c2f8b76cf28777083fb2d54035f772632bd9c14629377b07eecc3993ac8775c9ee7344049fe77f9c21021c8ed3278b8f354293aa96778a530aab388939d78675e4a70357cd27801f65ccc421b07798233747f13e23c7931a0a82f4c0d72793dc8923b9f90a6794d7a770ac734dc7970ac8a66fca0117a8c572d803b09467a6fad38608a8b7b7a656c237c3637b17a7f472c1b0485e57a5e59f72145e61a7bdb973a35ebcf507bd23d8902e603857b468d2b83df44ba7b4c38fbb10b6bf07b5f067a9ece85247cf687184642a7597cfa54cf6b8908907c382ac3c6ab0ac47cc7f473b8560df97cf8f19066ac502f7d3b971ac06b92637d0a3d21b00677987d4c8c295cc894ce7db0f79939fd1c037e9c7500883ce4377e039300aa4bdd6d7ee25b404a4faaa27eda72d01ce354d77e908f04e41b2a0d7fbad9826e513a427f299023cae5c8767f3374ac3c1f7bac7fa0c8eb85f3cce17f600000000c000000040000006100000062000000630000006120446973706c617920696d706c656d656e746174696f6e2072657475726e656420616e206572726f7220756e65787065637465646c7900640000000000000001000000650000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f737472696e672e7273487b100070000000330a00000e0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f7061747465726e2e7273c87b1000740000000e06000014000000c87b1000740000000e06000021000000c87b1000740000000206000014000000c87b10007400000002060000210000004572726f72617373657274696f6e206661696c65643a2073656c662e69735f636861725f626f756e64617279286e65775f6c656e29000000487b100070000000740500000d000000c87b1000740000008f04000024000000454f46207768696c652070617273696e672061206c697374454f46207768696c652070617273696e6720616e206f626a656374454f46207768696c652070617273696e67206120737472696e67454f46207768696c652070617273696e6720612076616c7565657870656374656420603a60657870656374656420602c60206f7220605d60657870656374656420602c60206f7220607d606578706563746564206964656e7465787065637465642076616c7565657870656374656420602260696e76616c696420657363617065696e76616c6964206e756d6265726e756d626572206f7574206f662072616e6765696e76616c696420756e69636f646520636f646520706f696e74636f6e74726f6c2063686172616374657220285c75303030302d5c75303031462920666f756e64207768696c652070617273696e67206120737472696e676b6579206d757374206265206120737472696e67696e76616c69642076616c75653a206578706563746564206b657920746f2062652061206e756d62657220696e2071756f746573666c6f6174206b6579206d7573742062652066696e6974652028676f74204e614e206f72202b2f2d696e66296c6f6e65206c656164696e6720737572726f6761746520696e2068657820657363617065747261696c696e6720636f6d6d61747261696c696e672063686172616374657273756e657870656374656420656e64206f662068657820657363617065726563757273696f6e206c696d6974206578636565646564206174206c696e652020636f6c756d6e20000000c87b100000000000087f100009000000117f1000080000004572726f72282c206c696e653a202c20636f6c756d6e3a2029000000347f1000060000003a7f100008000000427f10000a0000004c7f100001000000696e76616c696420747970653a202c20657870656374656420000000707f10000e0000007e7f10000b000000696e76616c69642076616c75653a20009c7f10000f0000007e7f10000b000000666c6f6174696e6720706f696e74206060000000bc7f100010000000cc7f1000010000006e756c6c2f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264655f6a736f6e2d312e302e3132342f7372632f6572726f722e7273000000e47f100061000000f701000021000000e47f100061000000fb0100000c000000e47f1000610000000202000021000000e47f1000610000000b0200002a000000e47f1000610000000f0200002c00000030313233343536373839616263646566757575757575757562746e7566727575757575757575757575757575757575750000220000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264655f6a736f6e2d312e302e3132342f7372632f726561642e7273a881100060000000a301000045000000a881100060000000a80100003d000000a881100060000000b00100001a000000a881100060000000f801000013000000a881100060000000fd01000033000000a881100060000000010200003e000000a881100060000000070200003a000000a8811000600000005402000013000000a8811000600000006c02000025000000a881100060000000bc0300002f000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00010203040506070809ffffffffffffff0a0b0c0d0e0fffffffffffffffffffffffffffffffffffffffffffffffffffff0a0b0c0d0e0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff696e662d696e664e614e0000660000000c00000004000000670000006800000063000000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64652f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264655f6a736f6e2d312e302e3132342f7372632f696f2f636f72652e727300f48310006300000012000000090000005075626c696373705f6170706c69636174696f6e5f63727970746f3a3a737232353531393a3a6170705369676e617475726550617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e727300ab84100060000000b20000001e0000005075626c696373705f6170706c69636174696f6e5f63727970746f3a3a65636473613a3a61707073757065723a3a5075626c69635369676e617475726573757065723a3a5369676e617475726573705f6170706c69636174696f6e5f63727970746f3a3a656432353531393a3a61707041726974686d657469634572726f7273705f61726974686d65746963556e646572666c6f774f766572666c6f774469766973696f6e42795a65726f0073705f61726974686d657469633a3a7065725f7468696e677350657262696c6c50617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273000000f985100060000000b20000001e00000075333250617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e72738086100060000000b20000001e000000737232353531393a3a5075626c6963005075626c696373705f636f6e73656e7375735f617572613a3a737232353531393a3a6170705f7372323535313942414245736c6f74206e756d62657263757272656e742065706f6368636861696e2072616e646f6d6e6573732f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f636f6e73656e7375732f626162652f7372632f6c69622e7273736c6f74206e756d626572206973207536343b2069742073686f756c642072656c61746520696e20736f6d652077617920746f2077616c6c20636c6f636b2074696d653b20696620753634206973206e6f7420656e6f7567682077652073686f756c6420637261736820666f72207361666574793b207165642e0059871000680000007e0100000a00000042616265436f6e66696775726174696f6e73705f636f6e73656e7375735f62616265416c6c6f776564536c6f74735072696d617279536c6f74735072696d617279416e645365636f6e64617279506c61696e536c6f74735072696d617279416e645365636f6e64617279565246536c6f747376617269616e74206964656e7469666965724261626545706f6368436f6e66696775726174696f6e737472756374204261626545706f6368436f6e66696775726174696f6e4f70617175654b65794f776e65727368697050726f6f6645706f6368005072696d61727950726544696765737473705f636f6e73656e7375735f626162653a3a646967657374735365636f6e64617279506c61696e5072654469676573745365636f6e646172795652465072654469676573745072654469676573745072696d6172795365636f6e64617279506c61696e5365636f6e646172795652464e657874436f6e66696744657363726970746f7256315075626c696373705f636f6e73656e7375735f626162653a3a61707050617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e727300e389100060000000b20000001e000000617574686f726974795f696e64657873757065723a3a417574686f72697479496e646578736c6f74536c6f747672665f7369676e61747572655672665369676e61747572655072696d6172795072654469676573745365636f6e64617279506c61696e5072654469676573745365636f6e6461727956524650726544696765737463287536342c2075363429616c6c6f7765645f736c6f7473416c6c6f776564536c6f7473737232353531393a3a5075626c6963736c6f745f6475726174696f6e75363465706f63685f6c656e677468617574686f7269746965735665633c28417574686f7269747949642c2042616265417574686f72697479576569676874293e72616e646f6d6e65737352616e646f6d6e6573735665633c75383e65706f63685f696e64657873746172745f736c6f746475726174696f6e636f6e6669674261626545706f6368436f6e66696775726174696f6e50617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e727300bb8b100060000000b20000001e0000005075626c696373705f636f6e73656e7375735f6772616e6470613a3a617070656432353531393a3a5075626c69635369676e6174757265656432353531393a3a5369676e617475726550617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e72730000868c100060000000b20000001e000000536c6f7473705f636f6e73656e7375735f736c6f7473753634536c6f744475726174696f6e0000005353353850524573705f636f72653a3a63727970746f4b6579547970654964004f70617175654d6574616461746173705f636f7265566f69640000005672665369676e617475726573705f636f72653a3a737232353531393a3a76726650617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e727300008e8d100060000000b20000001e0000005b75383b20345d5665633c75383e7072655f6f75747075745672665072654f757470757470726f6f6656726650726f6f6650617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e72730000428e100060000000b20000001e00000042547265654d61704b560000496e686572656e744461746173705f696e686572656e74736461746142547265654d61703c496e686572656e744964656e7469666965722c205665633c75383e3e436865636b496e686572656e7473526573756c746f6b6179626f6f6c666174616c5f6572726f726572726f7273486f737420746f207761736d2076616c7565732061726520656e636f64656420636f72726563746c793b207165648a00000000000000010000008b0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f72756e74696d652d696e746572666163652f7372632f696d706c732e72730000006c8f10006d000000d10000002a0000004572726f72486f737420746f207761736d2070726f766964657320612076616c696420656e756d206469736372696d696e616e743b207165640000008d00000000000000010000008e0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f72756e74696d652d696e746572666163652f7372632f706173735f62792e727300389010006f000000a8010000200000008f0000000d0000009000000091000000486f737420746f207761736d2076616c7565732061726520656e636f64656420636f72726563746c793b2071656400008d000000000000000100000092000000389010006f000000000100002b00000028294572726f7200b89010000000000072756e74696d65d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4890b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe221cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c50617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273c091100060000000b20000001e000000436f6e73656e737573456e67696e6549645665633c75383e6c6f67735665633c4469676573744974656d3e696e64657875386572726f724d6f64756c654572726f72546f6b656e4572726f7241726974686d657469634572726f72496e76616c69645472616e73616374696f6e556e6b6e6f776e5472616e73616374696f6e7072696f726974795472616e73616374696f6e5072696f7269747972657175697265735665633c5472616e73616374696f6e5461673e70726f76696465736c6f6e6765766974795472616e73616374696f6e4c6f6e67657669747970726f706167617465626f6f6c5b75383b204d41585f4d4f44554c455f4552524f525f454e434f4445445f53495a455d5472616e73616374696f6e616c4572726f724469676573744974656d73705f72756e74696d653a3a67656e657269633a3a64696765737450726552756e74696d65436f6e73656e7375735365616c4f7468657252756e74696d65456e7669726f6e6d656e74557064617465644469676573744974656d206e6f7420657175616c446967657374c70000000c00000004000000c8000000c9000000ca000000cb0000000000000001000000cc000000cd000000ce000000f09310000000000048617368206e6f7420657175616c00005472616e73616374696f6e2063616c6c206973206e6f74206578706563746564496e6162696c69747920746f2070617920736f6d6520666565732028652e672e206163636f756e742062616c616e636520746f6f206c6f77295472616e73616374696f6e2077696c6c2062652076616c696420696e20746865206675747572655472616e73616374696f6e206973206f757464617465645472616e73616374696f6e20686173206120626164207369676e61747572655472616e73616374696f6e2068617320616e20616e6369656e7420626972746820626c6f636b5472616e73616374696f6e20776f756c6420657868617573742074686520626c6f636b206c696d697473496e76616c69645472616e73616374696f6e20637573746f6d206572726f72412063616c6c20776173206c6162656c6c6564206173206d616e6461746f72792c2062757420726573756c74656420696e20616e204572726f722e5472616e73616374696f6e206469737061746368206973206d616e6461746f72793b207472616e73616374696f6e73206d757374206e6f742062652076616c6964617465642e496e76616c6964207369676e696e672061646472657373436f756c64206e6f74206c6f6f6b757020696e666f726d6174696f6e20726571756972656420746f2076616c696461746520746865207472616e73616374696f6e436f756c64206e6f742066696e6420616e20756e7369676e65642076616c696461746f7220666f722074686520756e7369676e6564207472616e73616374696f6e556e6b6e6f776e5472616e73616374696f6e20637573746f6d206572726f723c7761736d3a73747269707065643e43616e6e6f744c6f6f6b7570496e76616c69645472616e73616374696f6e73705f72756e74696d653a3a7472616e73616374696f6e5f76616c696469747943616c6c5061796d656e744675747572655374616c6542616450726f6f66416e6369656e744269727468426c6f636b45786861757374735265736f7572636573437573746f6d4261644d616e6461746f72794d616e6461746f727956616c69646174696f6e4261645369676e6572556e6b6e6f776e5472616e73616374696f6e4e6f556e7369676e656456616c696461746f725472616e73616374696f6e56616c69646974794572726f72496e76616c6964556e6b6e6f776e5472616e73616374696f6e536f75726365496e426c6f636b4c6f63616c45787465726e616c56616c69645472616e73616374696f6e000000089410002894100061941000889410009f941000be941000e49410000e9510002d95100068951000ae951000200000003900000027000000170000001f000000260000002a0000001f0000003b0000004600000017000000c5951000069610004796100041000000410000001f00000044697370617463684572726f7273705f72756e74696d654d6f64756c654572726f725472616e73616374696f6e616c4572726f724c696d6974526561636865644e6f4c617965724f7468657243616e6e6f744c6f6f6b75704261644f726967696e4d6f64756c65436f6e73756d657252656d61696e696e674e6f50726f766964657273546f6f4d616e79436f6e73756d657273546f6b656e41726974686d657469635472616e73616374696f6e616c457868617573746564436f7272757074696f6e556e617661696c61626c65526f6f744e6f74416c6c6f776564546f6b656e4572726f7246756e6473556e617661696c61626c654f6e6c7950726f766964657242656c6f774d696e696d756d43616e6e6f74437265617465556e6b6e6f776e417373657446726f7a656e556e737570706f7274656443616e6e6f74437265617465486f6c644e6f74457870656e6461626c65426c6f636b656445787472696e736963496e636c7573696f6e4d6f6465416c6c45787472696e736963734f6e6c79496e686572656e74734f706171756556616c75652f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6d61702f656e7472792e72730000a9991000850000007001000036000000617373657274696f6e206661696c65643a20696478203c2043415041434954592f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e6f64652e7273617373657274696f6e206661696c65643a20656467652e686569676874203d3d2073656c662e686569676874202d2031609a100080000000af02000009000000609a100080000000b302000009000000617373657274696f6e206661696c65643a207372632e6c656e2829203d3d206473742e6c656e2829609a1000800000002f07000005000000609a100080000000af04000023000000609a100080000000ef04000024000000617373657274696f6e206661696c65643a20656467652e686569676874203d3d2073656c662e6e6f64652e686569676874202d2031000000609a100080000000f0030000090000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f73746174652d6d616368696e652f7372632f73746174732e7273000000d09b1000690000007c00000023000000d09b1000690000007b0000001e000000d09b1000690000008100000024000000d09b100069000000800000001e000000416e204f7665726c617956616c756520697320616c7761797320637265617465642077697468206174206c65617374206f6e65207472616e73616374696f6e20616e642064726f7070656420617320736f6f6e0a09617320746865206c617374207472616e73616374696f6e2069732072656d6f7665643b207165642f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f73746174652d6d616368696e652f7372632f6f7665726c617965645f6368616e6765732f6368616e67657365742e727300f89c10007f0000000a0100002b000000f89c10007f000000140100002b000000607365745f7072657660206973206f6e6c792060536f6d65285f29602c206966207468652076616c75652063616d652066726f6d20706172656e743b20716564f89c10007f0000007d010000160000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f7665632f6d6f642e7273000000e89d10007100000027080000240000004c61796f757473697a650000d20000000400000004000000d3000000616c69676e000000d20000000400000004000000d400000043617061636974794f766572666c6f77416c6c6f634572726c61796f75740000d50000000400000004000000d60000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f736d616c6c7665632d312e31332e322f7372632f6c69622e727363616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c756500d50000000800000004000000d7000000d09e10005c000000520100002e0000006361706163697479206f766572666c6f77000000d09e10005c0000004101000036000000d09e10005c000000ce0400000e000000617373657274696f6e206661696c65643a206e65775f636170203e3d206c656ed09e10005c000000990400000d0000003a6368696c645f73746f726167653a64656661756c743a50617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e727304a0100060000000b20000001e0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f747269652f7372632f6e6f64655f636f6465632e727300000074a01000650000003f0000002800000074a0100065000000480000001400000073705f747269653a3a73746f726167655f70726f6f6653746f7261676550726f6f66747269655f6e6f64657342547265655365743c5665633c75383e3e2f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f736c6963652f697465722e727339a1100073000000d60500001500000000000000617474656d707420746f20646976696465206279207a65726f4254726565536574542f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f747269652f7372632f747269655f73747265616d2e7273e2a11000660000004700000040000000e2a1100066000000470000004d0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f747269652f7372632f6c69622e7273000068a210005e000000020200000b00000050617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273000000e9a2100060000000b20000001e000000436f775452756e74696d6556657273696f6e73705f76657273696f6e737065635f6e616d6552756e74696d65537472696e67696d706c5f6e616d65617574686f72696e675f76657273696f6e753332737065635f76657273696f6e696d706c5f76657273696f6e61706973417069735665637472616e73616374696f6e5f76657273696f6e73746174655f76657273696f6e7538576569676874287265665f74696d653a202c2070726f6f665f73697a653a2029f0a310001100000001a410000e0000000fa410000100000057656967687473705f776569676874733a3a7765696768745f763252756e74696d65446257656967687473705f7765696768747350617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e72730000006da4100060000000b20000001e0000007265665f74696d6575363470726f6f665f73697a657265616477726974652f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6d61702f656e7472792e727300fea41000850000007001000036000000617373657274696f6e206661696c65643a20696478203c2043415041434954592f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e6f64652e7273617373657274696f6e206661696c65643a20656467652e686569676874203d3d2073656c662e686569676874202d2031b4a5100080000000af02000009000000b4a5100080000000b302000009000000617373657274696f6e206661696c65643a207372632e6c656e2829203d3d206473742e6c656e2829b4a51000800000002f07000005000000b4a5100080000000af04000023000000b4a5100080000000ef04000024000000617373657274696f6e206661696c65643a20656467652e686569676874203d3d2073656c662e6e6f64652e686569676874202d2031000000b4a5100080000000f003000009000000617373657274696f6e206661696c65643a206f6c645f6c6566745f6c656e203e3d20636f756e7400b4a5100080000000dd0500000d000000617373657274696f6e206661696c65643a206c656e203e2030000000b4a510008000000065010000090000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e617669676174652e727388a7100084000000590200003000000088a7100084000000310200002f0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f7472696564626d75742e72730000002ca81000610000006c0000001c0000002ca810006100000061010000320000002ca810006100000046010000320000002ca8100061000000d60100003c000000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64653a200000d0a810002a000000604e6f64654f776e65643a3a56616c7565602063616e206f6e6c792062652072657475726e656420666f72207468652068617368206f6620612076616c75652e04a91000400000002ca8100061000000bf0100001100000056616c7565206e6f64652063616e206e6576657220626520696e6c696e65643b207165645ca91000240000002ca8100061000000a400000015000000e10000000400000004000000e2000000e3000000e10000000400000004000000e4000000e50000004a75737420656e636f64656420746865206e6f64652c20736f2069742073686f756c64206465636f646520776974686f757420616e79206572726f72733b2071656400002ca81000610000005707000016000000e10000000800000004000000e6000000e70000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6962626c652f6d6f642e7273000028aa100062000000490000001c000000617373657274696f6e206661696c65643a20216578697374696e675f6b65792e69735f656d707479282900002ca8100061000000f5040000150000002ca81000610000001f0300001e0000002ca8100061000000130300001e0000002ca8100061000000a8060000290000004272616e63682077697468206e6f2073756276616c7565732e20536f6d657468696e672077656e742077726f6e672e002ca81000610000002906000019000000757365645f696e646578206f6e6c7920736574206966206f636375706965643b207165642ca8100061000000310600001e0000002ca810006100000059060000190000002ca81000610000005f0600001e000000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64652ca810006100000092060000220000002ca810006100000066020000170000002ca81000610000007302000025000000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64652f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264655f6a736f6e2d312e302e3132342f7372632f7365722e7273001cac10005f0000000b060000120000001cac10005f0000002e080000330000001cac10005f000000210800004000000066616c73655c225c5c5c625c665c6e5c725c743a6865617070616765735072696d617279536c6f74735072696d617279416e645365636f6e64617279506c61696e536c6f74735072696d617279416e645365636f6e64617279565246536c6f7473000000c9ac10000c000000d5ac10001d000000f2ac10001b00000063616c6c6f7765645f736c6f7473303030313032303330343035303630373038303931303131313231333134313531363137313831393230323132323233323432353236323732383239333033313332333333343335333633373338333934303431343234333434343534363437343834393530353135323533353435353536353735383539363036313632363336343635363636373638363937303731373237333734373537363737373837393830383138323833383438353836383738383839393039313932393339343935393639373938393944656661756c744572726f7200001cac10005f0000007b020000280000001cac10005f000000a0020000280000001cac10005f000000f8010000280000001cac10005f0000000602000028000000e80000000000000001000000e900000045787465726e616c6974696573206e6f7420616c6c6f77656420746f206661696c2077697468696e2072756e74696d652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f73746174652d6d616368696e652f7372632f6578742e7273008cae100067000000ae0000003a0000003a65787472696e7369635f696e6465782f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f7375627374726174655f746573745f70616c6c65742e72730014af100073000000850000004a0000006361706163697479206f766572666c6f7743617061636974794f766572666c6f77416c6c6f634572726c61796f757400fa0000000400000004000000fb0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f736d616c6c7665632d312e31332e322f7372632f6c69622e72732f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f627335382d302e352e312f7372632f6465636f64652e7273000034b010005a0000006b0000001e0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f627335382d302e352e312f7372632f656e636f64652e72730000a0b010005a000000410000001e00000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c756500fa0000000800000004000000fc000000d8af10005c000000520100002e000000d8af10005c0000004101000036000000d8af10005c000000ce0400000e000000617373657274696f6e206661696c65643a206e65775f636170203e3d206c656ed8af10005c000000990400000d0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f736c6963652f697465722e727300a8b1100073000000d60500001500000000000000617474656d707420746f20646976696465206279207a65726fe29c85206e6f206d6967726174696f6e20666f7220000049b21000150000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f626162652f7372632f6c69622e727372756e74696d653a3a6672616d652d737570706f727470616c6c65745f626162653a3a70616c6c6574f09f90a5204e65772070616c6c65742020646574656374656420696e207468652072756e74696d652e205468652070616c6c657420686173206e6f20646566696e65642073746f726167652076657273696f6e2c20736f20746865206f6e2d636861696e2076657273696f6e206973206265696e6720696e697469616c697a656420746f202eeab2100010000000fab21000750000006fb310000100000072656a656374696e6720756e7369676e6564207265706f72742065717569766f636174696f6e207472616e73616374696f6e2062656361757365206974206973206e6f74206c6f63616c2f696e2d626c6f636b2e88b31000540000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f626162652f7372632f65717569766f636174696f6e2e727372756e74696d653a3a6261626570616c6c65745f626162653a3a65717569766f636174696f6e65706f636820696e646578206973207536343b20697420697320616c77617973206f6e6c7920696e6372656d656e746564206279206f6e653b20696620753634206973206e6f7420656e6f7567682077652073686f756c6420637261736820666f72207361666574793b207165642e0068b2100059000000eb0200004600000045706f6368436f6e66696720697320696e697469616c697a656420696e2067656e657369733b207765206e65766572206074616b6560206f7220606b696c6c602069743b2071656468b2100059000000fd0200002900000068b2100059000000e40200001200000049676e6f72696e6720656d7074792065706f6368206368616e67652e54b510001c00000070616c6c65745f6261626565706f636820696e64696365732077696c6c206e6576657220726561636820325e3634206265666f726520746865206465617468206f662074686520756e6976657273653b2071656468b2100059000000a80200000e000000496e697469616c206e756d626572206f6620617574686f7269746965732073686f756c64206265206c6f776572207468616e20543a3a4d6178417574686f7269746965730a01000000000000010000000b01000068b21000590000002103000016000000417574686f7269746965732061726520616c726561647920696e697469616c697a65642140b610002400000068b21000590000001e0300000d0000002043757272656e742065706f636820696e6465782e2043757272656e742065706f636820617574686f7269746965732e2054686520736c6f74206174207768696368207468652066697273742065706f63682061637475616c6c7920737461727465642e2054686973206973203020756e74696c2074686520666972737420626c6f636b206f662074686520636861696e2e2043757272656e7420736c6f74206e756d6265722e205468652065706f63682072616e646f6d6e65737320666f7220746865202a63757272656e742a2065706f63682e20232053656375726974792054686973204d555354204e4f54206265207573656420666f722067616d626c696e672c2061732069742063616e20626520696e666c75656e6365642062792061206d616c6963696f75732076616c696461746f7220696e207468652073686f7274207465726d2e204974204d4159206265207573656420696e206d616e792063727970746f677261706869632070726f746f636f6c732c20686f77657665722c20736f206c6f6e67206173206f6e652072656d656d626572732074686174207468697320286c696b652065766572797468696e6720656c7365206f6e2d636861696e29206974206973207075626c69632e20466f72206578616d706c652c2069742063616e20626520757365642077686572652061206e756d626572206973206e656564656420746861742063616e6e6f742068617665206265656e2063686f73656e20627920616e206164766572736172792c20666f7220707572706f7365732073756368206173207075626c69632d636f696e207a65726f2d6b6e6f776c656467652070726f6f66732e2050656e64696e672065706f636820636f6e66696775726174696f6e206368616e676520746861742077696c6c206265206170706c696564207768656e20746865206e6578742065706f636820697320656e61637465642e204e6578742065706f63682072616e646f6d6e6573732e204e6578742065706f636820617574686f7269746965732e2052616e646f6d6e65737320756e64657220636f6e737472756374696f6e2e205765206d616b6520612074726164652d6f6666206265747765656e2073746f7261676520616363657373657320616e64206c697374206c656e6774682e2057652073746f72652074686520756e6465722d636f6e737472756374696f6e2072616e646f6d6e65737320696e207365676d656e7473206f6620757020746f2060554e4445525f434f4e535452554354494f4e5f5345474d454e545f4c454e475448602e204f6e63652061207365676d656e7420726561636865732074686973206c656e6774682c20776520626567696e20746865206e657874206f6e652e20576520726573657420616c6c207365676d656e747320616e642072657475726e20746f206030602061742074686520626567696e6e696e67206f662065766572792065706f63682e2054574f582d4e4f54453a20605365676d656e74496e6465786020697320616e20696e6372656173696e6720696e74656765722c20736f2074686973206973206f6b61792e2054656d706f726172792076616c75652028636c656172656420617420626c6f636b2066696e616c697a6174696f6e292077686963682069732060536f6d6560206966207065722d626c6f636b20696e697469616c697a6174696f6e2068617320616c7265616479206265656e2063616c6c656420666f722063757272656e7420626c6f636b2e2054686973206669656c642073686f756c6420616c7761797320626520706f70756c6174656420647572696e6720626c6f636b2070726f63657373696e6720756e6c657373207365636f6e6461727920706c61696e20736c6f74732061726520656e61626c65642028776869636820646f6e277420636f6e7461696e206120565246206f7574707574292e2049742069732073657420696e20606f6e5f66696e616c697a65602c206265666f72652069742077696c6c20636f6e7461696e207468652076616c75652066726f6d20746865206c61737420626c6f636b2e2054686520626c6f636b206e756d62657273207768656e20746865206c61737420616e642063757272656e742065706f6368206861766520737461727465642c20726573706563746976656c7920604e2d316020616e6420604e602e204e4f54453a20576520747261636b207468697320697320696e206f7264657220746f20616e6e6f746174652074686520626c6f636b206e756d626572207768656e206120676976656e20706f6f6c206f6620656e74726f7079207761732066697865642028692e652e20697420776173206b6e6f776e20746f20636861696e206f6273657276657273292e2053696e63652065706f6368732061726520646566696e656420696e20736c6f74732c207768696368206d617920626520736b69707065642c2074686520626c6f636b206e756d62657273206d6179206e6f74206c696e6520757020776974682074686520736c6f74206e756d626572732e20486f77206c617465207468652063757272656e7420626c6f636b20697320636f6d706172656420746f2069747320706172656e742e205468697320656e74727920697320706f70756c617465642061732070617274206f6620626c6f636b20657865637574696f6e20616e6420697320636c65616e6564207570206f6e20626c6f636b2066696e616c697a6174696f6e2e205175657279696e6720746869732073746f7261676520656e747279206f757473696465206f6620626c6f636b20657865637574696f6e20636f6e746578742073686f756c6420616c77617973207969656c64207a65726f2e2054686520636f6e66696775726174696f6e20666f72207468652063757272656e742065706f63682e2053686f756c64206e6576657220626520604e6f6e656020617320697420697320696e697469616c697a656420696e2067656e657369732e2054686520636f6e66696775726174696f6e20666f7220746865206e6578742065706f63682c20604e6f6e65602069662074686520636f6e6669672077696c6c206e6f74206368616e67652028796f752063616e2066616c6c6261636b20746f206045706f6368436f6e6669676020696e737465616420696e20746861742063617365292e2041206c697374206f6620746865206c6173742031303020736b69707065642065706f63687320616e642074686520636f72726573706f6e64696e672073657373696f6e20696e646578207768656e207468652065706f63682077617320736b69707065642e2054686973206973206f6e6c79207573656420666f722076616c69646174696e672065717569766f636174696f6e2070726f6f66732e20416e2065717569766f636174696f6e2070726f6f66206d75737420636f6e7461696e732061206b65792d6f776e6572736869702070726f6f6620666f72206120676976656e2073657373696f6e2c207468657265666f7265207765206e65656420612077617920746f2074696520746f6765746865722073657373696f6e7320616e642065706f636820696e64696365732c20692e652e207765206e65656420746f2076616c6964617465207468617420612076616c696461746f722077617320746865206f776e6572206f66206120676976656e206b6579206f6e206120676976656e2073657373696f6e2c20616e64207768617420746865206163746976652065706f636820696e6465782077617320647572696e6720746861742073657373696f6e2e2054686520616d6f756e74206f662074696d652c20696e20736c6f74732c207468617420656163682065706f63682073686f756c64206c6173742e204e4f54453a2043757272656e746c79206974206973206e6f7420706f737369626c6520746f206368616e6765207468652065706f6368206475726174696f6e2061667465722074686520636861696e2068617320737461727465642e20417474656d7074696e6720746f20646f20736f2077696c6c20627269636b20626c6f636b2070726f64756374696f6e2e45706f63684475726174696f6e20546865206578706563746564206176657261676520626c6f636b2074696d6520617420776869636820424142452073686f756c64206265206372656174696e6720626c6f636b732e2053696e636520424142452069732070726f626162696c6973746963206974206973206e6f74207472697669616c20746f20666967757265206f7574207768617420746865206578706563746564206176657261676520626c6f636b2074696d652073686f756c64206265206261736564206f6e2074686520736c6f74206475726174696f6e20616e642074686520736563757269747920706172616d657465722060636020287768657265206031202d20636020726570726573656e7473207468652070726f626162696c697479206f66206120736c6f74206265696e6720656d707479292e4578706563746564426c6f636b54696d65204d6178206e756d626572206f6620617574686f72697469657320616c6c6f7765644d6178417574686f72697469657320546865206d6178696d756d206e756d626572206f66206e6f6d696e61746f727320666f7220656163682076616c696461746f722e4d61784e6f6d696e61746f727343616c6c54436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e0000dbc31000430000007265706f72745f65717569766f636174696f6e7265706f72745f65717569766f636174696f6e5f756e7369676e6564706c616e5f636f6e6669675f6368616e67654572726f7254686520604572726f726020656e756d206f6620746869732070616c6c65742e00006ec4100020000000496e76616c696445717569766f636174696f6e50726f6f66496e76616c69644b65794f776e65727368697050726f6f664475706c69636174654f6666656e63655265706f7274496e76616c6964436f6e66696775726174696f6e617574686f72697469657365706f6368436f6e666967f2c410000b000000fdc410000b0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f656e7669726f6e6d656e74616c2d312e312e342f7372632f6c69622e727318c5100060000000920000000f00000018c51000600000008e0000000e0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f656e7669726f6e6d656e74616c2d312e312e342f7372632f6c6f63616c5f6b65792e7273000098c5100066000000280000001800000098c5100066000000230000001900000098c5100066000000210000001700000073657269616c697a6174696f6e20746f206a736f6e20697320657870656374656420746f20776f726b2e207165642e000c01000004000000040000000d0100002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f737570706f72742f7372632f67656e657369735f6275696c6465725f68656c7065722e72730070c610006f0000003400000012000000496e76616c6964204a534f4e20626c6f623a2000f0c61000130000007374727563742047656e65736973436f6e66696742616265565246496e4f7574436f6e7465787428290000000e01000004000000040000000f0100000e0100000400000004000000100100004261644261736535384261644c656e677468556e6b6e6f776e5373353841646472657373466f726d617400000e010000040000000400000011010000496e76616c6964436865636b73756d496e76616c6964507265666978496e76616c6964466f726d6174496e76616c696450617468466f726d61744e6f74416c6c6f77656450617373776f72644e6f74416c6c6f776564000018c5100060000000630000001b0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f7472696564622e72730000fcc710005e000000110100003d000000fcc710005e00000010010000370000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f636f72652f7372632f63727970746f2e72730000007cc81000610000005c01000014000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708ffffffffffffff090a0b0c0d0e0f10ff1112131415ff161718191a1b1c1d1e1f20ffffffffffff2122232425262728292a2bff2c2d2e2f30313233343536373839ffffffffff31323334353637383941424344454647484a4b4c4d4e505152535455565758595a6162636465666768696a6b6d6e6f707172737475767778797a00007cc8100061000000330100001d000000e4b3100000000000fcc710005e000000ea00000016000000fcc710005e000000d20000001a000000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64652f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264655f6a736f6e2d312e302e3132342f7372632f7365722e7273000cca10005f000000a00200002800000053746f7261676556657273696f6e00000e0100000400000004000000120100005373353841646472657373466f726d617470726566697873746167696e67666f6f626172666f6f62617263616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c75652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f6c69622e72737375627374726174655465737400f1ca100061000000ef02000016000000f1ca100061000000df020000190000003a20000080cb10000000000080cb1000020000006120646566656e73697665206661696c75726520686173206265656e207472696767657265643b20706c65617365207265706f72742074686520626c6f636b206e756d6265722061742068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f69737375657394cb10007800000072756e74696d653a3a646566656e736976652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f737570706f72742f7372632f7472616974732f6d6973632e72736672616d655f737570706f72743a3a7472616974733a3a6d697363526573756c7454454f6b4572726e6f7420696d706c656d656e7465642f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f73746174652d6d616368696e652f7372632f747269655f6261636b656e642e7273000000c1cc100070000000ba000000090000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f73797374656d2f7372632f6c69622e72734163636f756e74496e666f6672616d655f73797374656d4e6f6e63654163636f756e74446174614576656e745265636f7264436f646555706772616465417574686f72697a6174696f6e4c6f676963206572726f723a20556e657870656374656420756e646572666c6f7720696e207265647563696e6720636f6e73756d657200e9cd10003600000072756e74696d653a3a73797374656d4c6f676963206572726f723a20556e657870656374656420756e646572666c6f7720696e207265647563696e672070726f766964657200000037ce1000360000004c6f676963206572726f723a204163636f756e7420616c72656164792064656164207768656e207265647563696e672070726f766964657278ce100038000000c1cc1000700000009100000009000000c1cc1000700000009500000009000000c1cc1000700000008400000009000000c1cc1000700000007c00000009000000c1cc1000700000007800000009000000c1cc1000700000008800000009000000c1cc100070000000ad00000009000000c1cc100070000000a900000009000000003a65787472696e7369635f696e6465784576656e74546f70696373426c6f636b4861736852657365727665734c6f636b734163636f756e74467265657a6573556e646572436f6e737472756374696f6e45787472696e73696344617461486f6c6473426f756e646564566563626f756e6465645f636f6c6c656374696f6e733a3a626f756e6465645f7665635345717569766f636174696f6e50726f6f6673705f636f6e73656e7375735f6772616e647061484e45717569766f636174696f6e507265766f7465507265636f6d6d69742f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f627335382d302e352e312f7372632f656e636f64652e72730009d010005a000000d00100001b00000009d010005a000000d10100001000000009d010005a000000cc0100000900000009d010005a000000b90100002000000009d010005a000000c20100000d00000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c7565002b01000000000000010000002c01000009d010005a0000003d01000020000000427566666572546f6f536d616c6c4f74686572002d01000004000000040000002e01000043616e6e6f744c6f6f6b75704261644f726967696e4d6f64756c65002d01000004000000040000002f010000436f6e73756d657252656d61696e696e674e6f50726f766964657273546f6f4d616e79436f6e73756d657273546f6b656e0000002d01000004000000040000003001000041726974686d6574696300002d0100000400000004000000310100005472616e73616374696f6e616c0000002d010000040000000400000032010000457868617573746564436f7272757074696f6e556e617661696c61626c65526f6f744e6f74416c6c6f7765645068616e746f6d446174613c3e000000fcd110000c00000008d21000010000007375627374726174655f746573745f72756e74696d653a3a52756e74696d652f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6974657261746f722e7273003bd210006000000087000000160000007765277665206a757374206665746368656420746865206c61737420656c656d656e74207573696e6720606c6173745f6d75746020736f20746869732063616e6e6f74206661696c3b207165640000003bd2100060000000cc0100002b0000004372756d623a3a7374657020616e64205472696544424e6f64654974657261746f722061726520696d706c656d656e74656420736f2074686174207468652061626f76652061726d732061726520746865206f6e6c7920706f737369626c652073746174657300003bd2100060000000e2010000160000003bd21000600000008f0100002600000053746f7261676556657273696f6e00002d01000004000000040000001201000042594500b4d31000030000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f7375627374726174655f746573745f70616c6c65742e727300c0d3100073000000d500000011000000436f756c64206e6f742072656164202074696d65732066726f6d2074686520737461746544d410000f00000053d4100015000000c0d3100073000000cf0000001900000076616c69646174655f756e7369676e656420000088d41000120000007375627374726174655f746573745f70616c6c65747375627374726174655f746573745f72756e74696d653a3a7375627374726174655f746573745f70616c6c65743a3a70616c6c6574f09f90a5204e65772070616c6c65742020646574656374656420696e207468652072756e74696d652e205468652070616c6c657420686173206e6f20646566696e65642073746f726167652076657273696f6e2c20736f20746865206f6e2d636861696e2076657273696f6e206973206265696e6720696e697469616c697a656420746f202eeed4100010000000fed410007500000073d510000100000072756e74696d653a3a6672616d652d737570706f7274e29c85206e6f206d6967726174696f6e20666f722000a2d5100015000000617574686f72697469657300c0d510000b0000007374727563742047656e65736973436f6e6669673c7761736d3a73747269707065643e43616c6c436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e0000fbd510004300000062656e63685f63616c6c696e636c7564655f6461746173746f726167655f6368616e67656f6666636861696e5f696e6465785f7365746f6666636861696e5f696e6465785f636c656172696e64657865645f63616c6c6465706f7369745f6c6f675f6469676573745f6974656d63616c6c5f776974685f7072696f7269747963616c6c5f646f5f6e6f745f70726f70616761746566696c6c5f626c6f636b72656164726561645f616e645f70616e69630500000001000000010000000300000003000000030000000800000003000000030000000300000004000000040000000400000003000000030000000300000003000000436865636b4d6574616461746148617368436865636b576569676874436865636b4e6f6e6365436865636b4d65746164617461486173683a3a6164646974696f6e616c5f7369676e6564203d3e20000062d7100028000000307872756e74696d653a3a6d657461646174612d686173682f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f6d657461646174612d686173682d657874656e73696f6e2f7372632f6c69622e72736672616d655f6d657461646174615f686173685f657874656e73696f6e5573656420626c6f636b207765696768743a2035d81000130000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f73797374656d2f7372632f657874656e73696f6e732f636865636b5f7765696768742e727372756e74696d653a3a73797374656d6672616d655f73797374656d3a3a657874656e73696f6e733a3a636865636b5f7765696768745573656420626c6f636b206c656e6774683a2000f4d810001300000043757272656e742065706f636820696e64657820206973206c6f776572207468616e2073657373696f6e20696e646578202e000010d910001400000024d910001d00000041d91000010000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f626162652f7372632f6c69622e727372756e74696d653a3a6261626570616c6c65745f626162650000005cd9100059000000b00000000f000000656e636f646564206572726f7220697320726573697a656420746f20626520657175616c20746f20746865206d6178696d756d20656e636f646564206572726f722073697a653b20716564496e76616c696445717569766f636174696f6e50726f6f66496e76616c69644b65794f776e65727368697050726f6f664475706c69636174654f6666656e63655265706f7274496e76616c6964436f6e66696775726174696f6e0000004e0100000c000000040000004f0100006465636f646500006672616d655f73797374656d3a3a657874656e73696f6e733a3a636865636b5f6e6f6e636554457863656564656420626c6f636b206c656e677468206c696d69743a20203e200000c6da10001d000000e3da10000300000045787472696e736963202069732067726561746572207468616e20746865206d61782065787472696e73696320000000f8da10000a00000002db100023000000457863656564656420746865207065722d636c61737320616c6c6f77616e63652e00000038db100021000000546f74616c20626c6f636b207765696768742069732065786365656465642e0064db10001f00000050d810006f000000bc00000011000000416c6c2077656967687420636865636b656420616464206f766572666c6f772e9cdb1000200000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f73797374656d2f7372632f6c69622e727300c4db10005b000000590300000f000000496e76616c6964537065634e616d655370656356657273696f6e4e65656473546f496e6372656173654661696c6564546f4578747261637452756e74696d6556657273696f6e4e6f6e44656661756c74436f6d706f736974654e6f6e5a65726f526566436f756e7443616c6c46696c74657265644d756c7469426c6f636b4d6967726174696f6e734f6e676f696e674e6f7468696e67417574686f72697a6564556e617574686f72697a6564436f727275707465642073746174652061742060603a2000dcdc100014000000f0dc10000300000072756e74696d653a3a73746f726167652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f737570706f72742f7372632f73746f726167652f756e6861736865642e72736672616d655f737570706f72743a3a73746f726167653a3a756e6861736865642f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f62616c616e6365732f7372632f6c69622e727300009ddd10005d0000007d0100000f00000056657374696e6742616c616e63654c69717569646974795265737472696374696f6e73496e73756666696369656e7442616c616e63654578697374656e7469616c4465706f736974457870656e646162696c6974794578697374696e6756657374696e675363686564756c65446561644163636f756e74546f6f4d616e795265736572766573546f6f4d616e79486f6c6473546f6f4d616e79467265657a657349737375616e6365446561637469766174656444656c74615a65726f507265766f746566696e616c6974795f6772616e647061484e507265636f6d6d697445717569766f636174696f6e4964565347656e65736973536c6f74417574686f7269746965734576656e74436f756e744e65787445706f6368436f6e666967496e697469616c697a656443757272656e74536c6f744e756d6265724c61737452756e74696d6555706772616465417574686f72697a656455706772616465496e61637469766549737375616e636545787472696e736963436f756e74506172656e74486173684e65787452616e646f6d6e657373416c6c45787472696e736963734c656e546f74616c49737375616e63654576656e74735570677261646564546f553332526566436f756e74426c6f636b5765696768745365676d656e74496e646578446967657374457865637574696f6e506861736550656e64696e6745706f6368436f6e6669674368616e67654e657874417574686f72697469657345706f6368496e64657845706f6368436f6e66696745706f636853746172744c6174656e657373496e686572656e74734170706c696564536b697070656445706f636873417574686f7256726652616e646f6d6e6573735570677261646564546f547269706c65526566436f756e7452616e646f6d6e6573735765616b426f756e646564566563626f756e6465645f636f6c6c656374696f6e733a3a7765616b5f626f756e6465645f7665636c656e677468206f66206120626f756e64656420766563746f7220696e2073636f706520206973206e6f74207265737065637465642e0000d4e0100024000000f8e010001200000072756e74696d652f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f626f756e6465642d636f6c6c656374696f6e732d302e322e302f7372632f7765616b5f626f756e6465645f7665632e72735f7068616e746f6d0000500100000000000001000000510100006d6f6465520100000100000001000000530100006d657461646174615f6861736800000052010000210000000100000054010000550100000400000004000000560100005501000004000000040000005701000055010000040000000400000058010000550100000400000004000000590100005501000004000000040000005a0100005501000004000000040000005b0100005501000004000000040000005c0100005501000004000000040000005d01000046756e6473556e617661696c61626c654f6e6c7950726f766964657242656c6f774d696e696d756d43616e6e6f74437265617465556e6b6e6f776e417373657446726f7a656e556e737570706f7274656443616e6e6f74437265617465486f6c644e6f74457870656e6461626c65426c6f636b65644d6f64756c654572726f72696e6465780000005201000001000000010000005e0100006572726f720000005201000004000000010000005f0100006d65737361676500550100000400000004000000600100004c61796f757473697a6500005501000004000000040000003f010000616c69676e00000055010000040000000400000061010000557466384572726f7276616c69645f75705f746f6572726f725f6c656e0000005501000004000000040000006201000046726f6d557466384572726f7262797465730000550100000400000004000000630100004261644261736535384261644c656e677468556e6b6e6f776e5373353841646472657373466f726d6174000055010000040000000400000011010000496e76616c6964436865636b73756d496e76616c6964507265666978496e76616c6964466f726d6174496e76616c696450617468466f726d61744e6f74416c6c6f77656450617373776f72644e6f74416c6c6f7765644e6f6e65536f6d650000550100000400000004000000640100005765696768747265665f74696d6500006501000008000000080000006601000070726f6f665f73697a654c696d6974526561636865644e6f4c6179657263616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c7565670100001400000004000000680100002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f627335382d302e352e312f7372632f656e636f64652e72730000d8e410005a000000930000002b0000004572726f7244697361626c6564456e61626c6564a0da1000000000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f636f72652f7372632f63727970746f2e727300000060e51000610000005c01000014000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708ffffffffffffff090a0b0c0d0e0f10ff1112131415ff161718191a1b1c1d1e1f20ffffffffffff2122232425262728292a2bff2c2d2e2f30313233343536373839ffffffffff31323334353637383941424344454647484a4b4c4d4e505152535455565758595a6162636465666768696a6b6d6e6f707172737475767778797a000060e5100061000000330100001d000000466574636846726f6d456e76437573746f6d0000550100000400000004000000690100003c7761736d3a73747269707065643e5065724469737061746368436c6173736e6f726d616c0000006501000010000000080000006a0100006f7065726174696f6e616c6d616e6461746f72795501000004000000040000006b0100002bda100043da10005bda100071da1000180000001800000016000000140000000f0000001a0000001d000000130000000f0000000c0000001b000000110000000c00000030dc10003fdc100059dc100076dc100089dc100098dc1000a4dc1000bfdc1000d0dc10000e0000001500000013000000120000000d000000170000000b0000000f0000000c0000000e00000013000000090000000cde10001ade10002fde100042de100054de100061de100078de100083de100092de10009ede1000acde1000bfde1000100000000c0000000c0000000c0000000c000000060000000b000000100000000d0000000700000064e2100074e2100080e210008ce2100098e21000a4e21000aae21000b5e21000c5e21000d2e210007d436865636b53756273747261746543616c6c736565642047656e6572617465206120736574206f662073657373696f6e206b6579732077697468206f7074696f6e616c6c79207573696e672074686520676976656e20736565642e20546865206b6579732073686f756c642062652073746f7265642077697468696e20746865206b657973746f7265206578706f736564207669612072756e74696d652065787465726e616c69746965732e000000205468652073656564206e6565647320746f20626520612076616c69642060757466386020737472696e672e2052657475726e732074686520636f6e636174656e61746564205343414c4520656e636f646564207075626c6963206b6579732e67656e65726174655f73657373696f6e5f6b657973656e636f646564204465636f64652074686520676976656e207075626c69632073657373696f6e206b6579732e2052657475726e7320746865206c697374206f66207075626c696320726177207075626c6963206b657973202b206b657920747970652e6465636f64655f73657373696f6e5f6b6579732053657373696f6e206b6579732072756e74696d65206170692e53657373696f6e4b6579736865616465722053746172747320746865206f66662d636861696e207461736b20666f7220676976656e20626c6f636b206865616465722e6f6666636861696e5f776f726b657220546865206f6666636861696e20776f726b6572206170692e4f6666636861696e576f726b65724170690000a10100000800000004000000a2010000a3010000a4010000a5010000a6010000a7010000a801000061207475706c65206f662073697a652032612073657175656e636565787472696e736963204170706c792074686520676976656e2065787472696e7369632e2052657475726e7320616e20696e636c7573696f6e206f7574636f6d652077686963682073706563696669657320696620746869732065787472696e73696320697320696e636c7564656420696e207468697320626c6f636b206f72206e6f742e6170706c795f65787472696e7369632046696e697368207468652063757272656e7420626c6f636b2e66696e616c697a655f626c6f636b696e686572656e742047656e657261746520696e686572656e742065787472696e736963732e2054686520696e686572656e7420646174612077696c6c20766172792066726f6d20636861696e20746f20636861696e2e696e686572656e745f65787472696e73696373626c6f636b6461746120436865636b20746861742074686520696e686572656e7473206172652076616c69642e2054686520696e686572656e7420646174612077696c6c20766172792066726f6d20636861696e20746f20636861696e2e636865636b5f696e686572656e7473205468652060426c6f636b4275696c646572602061706920747261697420746861742070726f7669646573207468652072657175697265642066756e6374696f6e616c69747920666f72206275696c64696e67206120626c6f636b2e426c6f636b4275696c6465722052657475726e732074686520736c6f74206475726174696f6e20666f7220417572612e2043757272656e746c792c206f6e6c79207468652076616c75652070726f7669646564206279207468697320747970652061742067656e657369732077696c6c20626520757365642e736c6f745f6475726174696f6e2052657475726e207468652063757272656e7420736574206f6620617574686f7269746965732e617574686f72697469657320415049206e656365737361727920666f7220626c6f636b20617574686f7273686970207769746820617572612e417572614170692052657475726e2074686520636f6e66696775726174696f6e20666f7220424142452e636f6e66696775726174696f6e2052657475726e732074686520736c6f7420746861742073746172746564207468652063757272656e742065706f63682e63757272656e745f65706f63685f73746172742052657475726e7320696e666f726d6174696f6e20726567617264696e67207468652063757272656e742065706f63682e63757272656e745f65706f63682052657475726e7320696e666f726d6174696f6e20726567617264696e6720746865206e6578742065706f6368202877686963682077617320616c72656164792070726576696f75736c7920616e6e6f756e636564292e6e6578745f65706f6368736c6f74617574686f726974795f69642047656e65726174657320612070726f6f66206f66206b6579206f776e65727368697020666f722074686520676976656e20617574686f7269747920696e207468652063757272656e742065706f63682e20416e206578616d706c65207573616765206f662074686973206d6f64756c6520697320636f75706c65642077697468207468652073657373696f6e20686973746f726963616c206d6f64756c6520746f2070726f76652074686174206120676976656e20617574686f72697479206b6579206973207469656420746f206120676976656e207374616b696e67206964656e7469747920647572696e6720612073706563696669632073657373696f6e2e2050726f6f6673206f66206b6579206f776e65727368697020617265206e656365737361727920666f72207375626d697474696e672065717569766f636174696f6e207265706f7274732e204e4f54453a206576656e2074686f75676820746865204150492074616b657320612060736c6f746020617320706172616d65746572207468652063757272656e7420696d706c656d656e746174696f6e732069676e6f726573207468697320706172616d6574657220616e6420696e73746561642072656c696573206f6e2074686973206d6574686f64206265696e672063616c6c65642061742074686520636f727265637420626c6f636b206865696768742c20692e652e20616e7920706f696e74206174207768696368207468652065706f636820666f722074686520676976656e20736c6f74206973206c697665206f6e2d636861696e2e2046757475726520696d706c656d656e746174696f6e732077696c6c20696e73746561642075736520696e64657865642064617461207468726f75676820616e206f6666636861696e20776f726b65722c206e6f7420726571756972696e67206f6c6465722073746174657320746f20626520617661696c61626c652e67656e65726174655f6b65795f6f776e6572736869705f70726f6f6665717569766f636174696f6e5f70726f6f666b65795f6f776e65725f70726f6f66205375626d69747320616e20756e7369676e65642065787472696e73696320746f207265706f727420616e2065717569766f636174696f6e2e205468652063616c6c6572206d7573742070726f76696465207468652065717569766f636174696f6e2070726f6f6620616e642061206b6579206f776e6572736869702070726f6f66202873686f756c64206265206f627461696e6564207573696e67206067656e65726174655f6b65795f6f776e6572736869705f70726f6f6660292e205468652065787472696e7369632077696c6c20626520756e7369676e656420616e642073686f756c64206f6e6c7920626520616363657074656420666f72206c6f63616c20617574686f727368697020286e6f7420746f2062652062726f61646361737420746f20746865206e6574776f726b292e2054686973206d6574686f642072657475726e7320604e6f6e6560207768656e206372656174696f6e206f66207468652065787472696e736963206661696c732c20652e672e2069662065717569766f636174696f6e207265706f7274696e672069732064697361626c656420666f722074686520676976656e2072756e74696d652028692e652e2074686973206d6574686f642069732068617264636f64656420746f2072657475726e20604e6f6e6560292e204f6e6c792075736566756c20696e20616e206f6666636861696e20636f6e746578742e7375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e73696320415049206e656365737361727920666f7220626c6f636b20617574686f7273686970207769746820424142452e426162654170696a736f6e204275696c64206052756e74696d6547656e65736973436f6e666967602066726f6d2061204a534f4e20626c6f62206e6f74207573696e6720616e792064656661756c747320616e642073746f726520697420696e207468652073746f726167652e20496e207468652063617365206f662061204652414d452d62617365642072756e74696d652c20746869732066756e6374696f6e20646573657269616c697a6573207468652066756c6c206052756e74696d6547656e65736973436f6e666967602066726f6d2074686520676976656e204a534f4e20626c6f6220616e64207075747320697420696e746f207468652073746f726167652e204966207468652070726f7669646564204a534f4e20626c6f6220697320696e636f7272656374206f7220696e636f6d706c657465206f722074686520646573657269616c697a6174696f6e206661696c732c20616e206572726f722069732072657475726e65642e20506c65617365206e6f746520746861742070726f7669646564204a534f4e20626c6f62206d75737420636f6e7461696e20616c6c206052756e74696d6547656e65736973436f6e66696760206669656c64732c206e6f2064656661756c74732077696c6c20626520757365642e6275696c645f737461746569642052657475726e732061204a534f4e20626c6f6220726570726573656e746174696f6e206f6620746865206275696c742d696e206052756e74696d6547656e65736973436f6e66696760206964656e74696669656420627920606964602e204966206069646020697320604e6f6e6560207468652066756e6374696f6e2072657475726e73204a534f4e20626c6f6220726570726573656e746174696f6e206f66207468652064656661756c74206052756e74696d6547656e65736973436f6e6669676020737472756374206f66207468652072756e74696d652e20496d706c656d656e746174696f6e206d7573742070726f766964652064656661756c74206052756e74696d6547656e65736973436f6e666967602e204f74686572776973652066756e6374696f6e2072657475726e732061204a534f4e20726570726573656e746174696f6e206f6620746865206275696c742d696e2c206e616d6564206052756e74696d6547656e65736973436f6e6669676020707265736574206964656e74696669656420627920606964602c206f7220604e6f6e656020696620737563682070726573657420646f6573206e6f74206578697374732e2052657475726e656420605665633c75383e6020636f6e7461696e73206279746573206f66204a534f4e20626c6f62202870617463682920776869636820636f6d7072697365732061206c697374206f662028706f74656e7469616c6c79206e657374656429206b65792d76616c756520706169727320746861742061726520696e74656e64656420666f7220637573746f6d697a696e67207468652064656661756c742072756e74696d652067656e6573697320636f6e6669672e20546865207061746368207368616c6c206265206d657267656420287266633733383629207769746820746865204a534f4e20726570726573656e746174696f6e206f66207468652064656661756c74206052756e74696d6547656e65736973436f6e6669676020746f20637265617465206120636f6d70726568656e736976652067656e6573697320636f6e66696720746861742063616e206265207573656420696e20606275696c645f737461746560206d6574686f642e6765745f7072657365742052657475726e732061206c697374206f66206964656e7469666965727320666f7220617661696c61626c65206275696c74696e206052756e74696d6547656e65736973436f6e6669676020707265736574732e2054686520707265736574732066726f6d20746865206c6973742063616e20626520717565726965642077697468205b6047656e657369734275696c6465723a3a6765745f707265736574605d206d6574686f642e204966206e6f206e616d65642070726573657473206172652070726f7669646564206279207468652072756e74696d6520746865206c69737420697320656d7074792e7072657365745f6e616d65732041504920746f20696e74657261637420776974682052756e74696d6547656e65736973436f6e66696720666f72207468652072756e74696d6547656e657369734275696c646572736f757263657478626c6f636b5f686173682056616c696461746520746865207472616e73616374696f6e2e2054686973206d6574686f6420697320696e766f6b656420627920746865207472616e73616374696f6e20706f6f6c20746f206c6561726e2064657461696c732061626f757420676976656e207472616e73616374696f6e2e2054686520696d706c656d656e746174696f6e2073686f756c64206d616b65207375726520746f207665726966792074686520636f72726563746e657373206f6620746865207472616e73616374696f6e20616761696e73742063757272656e742073746174652e2054686520676976656e2060626c6f636b5f686173686020636f72726573706f6e647320746f207468652068617368206f662074686520626c6f636b207468617420697320757365642061732063757272656e742073746174652e204e6f7465207468617420746869732063616c6c206d617920626520706572666f726d65642062792074686520706f6f6c206d756c7469706c652074696d657320616e64207472616e73616374696f6e73206d6967687420626520766572696669656420696e20616e7920706f737369626c65206f726465722e76616c69646174655f7472616e73616374696f6e2054686520605461676765645472616e73616374696f6e5175657565602061706920747261697420666f7220696e746572666572696e67207769746820746865207472616e73616374696f6e2071756575652e5461676765645472616e73616374696f6e517565756520476574207468652063757272656e74204752414e44504120617574686f72697469657320616e6420776569676874732e20546869732073686f756c64206e6f74206368616e67652065786365707420666f72207768656e206368616e67657320617265207363686564756c656420616e642074686520636f72726573706f6e64696e672064656c617920686173207061737365642e205768656e2063616c6c656420617420626c6f636b20422c2069742077696c6c2072657475726e2074686520736574206f6620617574686f72697469657320746861742073686f756c64206265207573656420746f2066696e616c697a652064657363656e64616e7473206f66207468697320626c6f636b2028422b312c20422b322c202e2e2e292e2054686520626c6f636b204220697473656c662069732066696e616c697a65642062792074686520617574686f7269746965732066726f6d20626c6f636b20422d312e6772616e6470615f617574686f7269746965737365745f696420676976656e207365742e20416e206578616d706c65207573616765206f662074686973206d6f64756c6520697320636f75706c6564207769746820746865204e4f54453a206576656e2074686f75676820746865204150492074616b6573206120607365745f69646020617320706172616d65746572207468652063757272656e7420696d706c656d656e746174696f6e732069676e6f7265207468697320706172616d6574657220616e6420696e73746561642072656c79206f6e20746869732077686963682074686520676976656e20736574206964206973206c697665206f6e2d636861696e2e2046757475726520696d706c656d656e746174696f6e732077696c6c20696e73746561642075736520696e64657865642064617461207468726f75676820616e206f6666636861696e20776f726b65722c206e6f7420726571756972696e67206f6c6465722073746174657320746f20626520617661696c61626c652e204765742063757272656e74204752414e44504120617574686f72697479207365742069642e63757272656e745f7365745f6964204150497320666f7220696e746567726174696e6720746865204752414e4450412066696e616c6974792067616467657420696e746f2072756e74696d65732e20546869732073686f756c6420626520696d706c656d656e746564206f6e207468652072756e74696d6520736964652e2054686973206973207072696d6172696c79207573656420666f72206e65676f74696174696e6720617574686f726974792d736574206368616e67657320666f7220746865206761646765742e204752414e44504120757365732061207369676e616c696e67206d6f64656c206f66206368616e67696e6720617574686f7269747920736574733a206368616e6765732073686f756c64206265207369676e616c6564207769746820612064656c6179206f66204e20626c6f636b732c20616e64207468656e206175746f6d61746963616c6c79206170706c69656420696e207468652072756e74696d652061667465722074686f7365204e20626c6f636b732068617665207061737365642e2054686520636f6e73656e7375732070726f746f636f6c2077696c6c20636f6f7264696e617465207468652068616e646f66662065787465726e616c6c792e4772616e6470614170696163636f756e74204765742063757272656e74206163636f756e74206e6f6e6365206f6620676976656e20604163636f756e744964602e6163636f756e745f6e6f6e6365205468652041504920746f207175657279206163636f756e74206e6f6e63652e4163636f756e744e6f6e63654170692829a90100000000000001000000aa0100002052657475726e73207468652076657273696f6e206f66207468652072756e74696d652e76657273696f6e20457865637574652074686520676976656e20626c6f636b2e657865637574655f626c6f636b20496e697469616c697a65206120626c6f636b20776974682074686520676976656e2068656164657220616e642072657475726e207468652072756e74696d6520657865637574697665206d6f64652e696e697469616c697a655f626c6f636b205468652060436f7265602072756e74696d65206170692074686174206576657279205375627374726174652072756e74696d65206e6565647320746f20696d706c656d656e742e436f72652052657475726e7320746865206d65746164617461206f6620612072756e74696d652e6d657461646174612052657475726e7320746865206d65746164617461206174206120676976656e2076657273696f6e2e2049662074686520676976656e206076657273696f6e602069736e277420737570706f727465642c20746869732077696c6c2072657475726e20604e6f6e65602e20557365205b6053656c663a3a6d657461646174615f76657273696f6e73605d20746f2066696e64206f75742061626f757420737570706f72746564206d657461646174612076657273696f6e206f66207468652072756e74696d652e6d657461646174615f61745f76657273696f6e2052657475726e732074686520737570706f72746564206d657461646174612076657273696f6e732e20546869732063616e206265207573656420746f2063616c6c20606d657461646174615f61745f76657273696f6e602e6d657461646174615f76657273696f6e732054686520604d65746164617461602061706920747261697420746861742072657475726e73206d6574616461746120666f72207468652072756e74696d652e4d657461646174614f6b0000a10100000400000004000000ab01000045727200a10100000400000004000000ab010000a10100000c00000004000000ac010000ad010000ae010000af010000b0010000b1010000b2010000b30100007c00000004000000b4010000b5010000b60100003800000004000000b7010000b8010000b9010000ba010000bb010000bc010000bd010000a10100000400000004000000be01000001000000000000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f6c69622e727376616c6964617465000000d5051100080000007375627374726174652d746573742d72756e74696d657375627374726174655f746573745f72756e74696d653031303330303030303030303030303030303034363430343030303030303030303130333030303030303030303030303030303436393034303130303030303000000000b60100003800000004000000bf010000c0010000617373657274696f6e206661696c65643a20616c6c2e636f6e7461696e7328267075626c6963302974051100610000000003000005000000617373657274696f6e206661696c65643a20616c6c2e636f6e7461696e7328267075626c6963312974051100610000000103000005000000617373657274696f6e206661696c65643a20616c6c2e636f6e7461696e7328267075626c69633229740511006100000002030000050000006564323535313947656e65726174657320612076616c696420606564323535313960207369676e61747572652e0000007405110061000000040300002e000000617373657274696f6e206661696c65643a207075626c6963302e76657269667928262265643235353139222c20267369676e6174757265297405110061000000050300000500000074051100610000000f0300000500000074051100610000001003000005000000740511006100000011030000050000007372323535313947656e65726174657320612076616c696420607372323535313960207369676e61747572652e0000007405110061000000130300002e000000617373657274696f6e206661696c65643a207075626c6963302e76657269667928262273723235353139222c20267369676e6174757265297405110061000000140300000500000074051100610000001e0300000500000074051100610000001f0300000500000074051100610000002003000005000000656364736147656e65726174657320612076616c69642060656364736160207369676e61747572652e0000007405110061000000220300002c000000617373657274696f6e206661696c65643a207075626c6963302e7665726966792826226563647361222c20267369676e617475726529000074051100610000002403000005000000746573743a726561645f73746f72616765000000010000000400000074051100610000002e030000050000000809110074051100610000002f0300000500000074051100610000003303000005000000000000005809110074051100610000003403000005000000756e697175655f69645f313a726561645f6368696c645f73746f72616765000074051100610000003e0300000500000074051100610000003f03000005000000740511006100000043030000050000007405110061000000440300000500000076616c756533617373657274696f6e206661696c65643a206578742e73746f726167655f726f6f742844656661756c743a3a64656661756c742829292e61735f736c6963652829203d3d2026726f6f745b2e2e5d74051100610000005303000005000000617373657274696f6e206661696c65643a206578742e73746f726167655f726f6f742844656661756c743a3a64656661756c742829292e61735f736c696365282920213d2026726f6f745b2e2e5d000074051100610000005503000005000000617373657274696f6e206661696c65643a206578742e73746f7261676528622276616c75653322292e69735f736f6d6528290000740511006100000052030000050000003c7761736d3a73747269707065643e5472616e73666572446174612052657475726e207468652062616c616e6365206f662074686520676976656e206163636f756e742069642e62616c616e63655f6f6676616c20412062656e63686d61726b2066756e6374696f6e20746861742061646473206f6e6520746f2074686520676976656e2076616c756520616e642072657475726e732074686520726573756c742e62656e63686d61726b5f6164645f6f6e6576656320412062656e63686d61726b2066756e6374696f6e20746861742061646473206f6e6520746f20656163682076616c756520696e2074686520676976656e20766563746f7220616e642072657475726e732074686520726573756c742e62656e63686d61726b5f766563746f725f6164645f6f6e6520546865206e6577207369676e61747572652e66756e6374696f6e5f7369676e61747572655f6368616e6765642074726965206e6f5f7374642074657374696e677573655f747269652043616c6c732066756e6374696f6e20696e20746865206c6f6f70207573696e67206e657665722d696e6c696e65642066756e6374696f6e20706f696e74657262656e63686d61726b5f696e6469726563745f63616c6c2043616c6c732066756e6374696f6e20696e20746865206c6f6f7062656e63686d61726b5f6469726563745f63616c6c73697a6520416c6c6f636174657320766563746f72207769746820676976656e2063617061636974792e7665635f776974685f63617061636974792052657475726e732074686520696e697469616c697a656420626c6f636b206e756d6265722e6765745f626c6f636b5f6e756d62657220546573742074686174206065643235353139602063727970746f20776f726b7320696e207468652072756e74696d652e2052657475726e7320746865207369676e61747572652067656e65726174656420666f7220746865206d6573736167652060656432353531396020616e6420746865207075626c6963206b65792e746573745f656432353531395f63727970746f20546573742074686174206073723235353139602063727970746f20776f726b7320696e207468652072756e74696d652e2052657475726e7320746865207369676e61747572652067656e65726174656420666f7220746865206d657373616765206073723235353139602e746573745f737232353531395f63727970746f2054657374207468617420606563647361602063727970746f20776f726b7320696e207468652072756e74696d652e2052657475726e7320746865207369676e61747572652067656e65726174656420666f7220746865206d65737361676520606563647361602e746573745f65636473615f63727970746f2052756e20766172696f757320746573747320616761696e73742073746f726167652e746573745f73746f7261676570726f6f66726f6f7420436865636b2061207769746e6573732e746573745f7769746e6573736f746865726e756d2054657374207468617420656e737572657320746861742077652063616e2063616c6c20612066756e6374696f6e20746861742074616b6573206d756c7469706c6520617267756d656e74732e746573745f6d756c7469706c655f617267756d656e747320547261636573206c6f6720224865792049276d2072756e74696d652e22646f5f74726163655f6c6f677369677075626c69636d657373616765205665726966792074686520676976656e207369676e61747572652c207075626c69632026206d6573736167652062756e646c652e7665726966795f656432353531396b657976616c756570616e69632057726974652074686520676976656e206076616c75656020756e6465722074686520676976656e20606b65796020696e746f207468652073746f7261676520616e64207468656e206f7074696f6e616c2070616e69632e77726974655f6b65795f76616c756554657374415049657865637574655f626c6f636b3a200000871011000f0000007405110061000000d80100000d000000696e697469616c697a655f626c6f636b3a200000b0101100120000007405110061000000dd0100000d00000076616c69646174655f7472616e73616374696f6e20200000dc10110015000000f1101100010000005deb10000e0000007405110061000000010200000d0000007405110061000000510200000d0000007405110061000000520200000d0000004865792049276d2072756e74696d65003c1111000f000000544849532049532054524143494e474669656c6453657420636f727275707465642028746869732069732061206275672900000074051100610000005a0200000d0000004865792c2049276d2074726163696e679811110010000000a10100001800000004000000c1010000a10100000400000004000000c201000049276d206a75737420666f6c6c6f77696e67206d79206d6173746572d01111001c0000007405110061000000650200001100000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c756500a90100000000000001000000c30100007405110061000000a60200003f000000666f6f62617273746167696e676576656e74202f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f6c69622e72733a363032c20f110007000000daeb100004000000a10100000c00000004000000c4010000c5010000c6010000010000005a020000000000005d1211006b0000000809110004000000c812110002000000e0911100d8121100fe05110016000000740511006100000001000000a90100000000000001000000c7010000c8010000c9010000a90100000000000001000000c7010000c9010000c901000042616c616e636573537562737472617465546573744261626553797374656d52756e74696d65486f6c64526561736f6e73797374656d626162657375627374726174655465737462616c616e63657300901311000600000096131100040000009a1311000d000000a7131100080000007374727563742052756e74696d6547656e65736973436f6e66696752756e74696d6543616c6c52756e74696d654572726f7252756e74696d654576656e7452756e74696d652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f73797374656d2f7372632f6c69622e727372656d61726b7365745f686561705f70616765737365745f636f64657365745f636f64655f776974686f75745f636865636b737365745f73746f726167656b696c6c5f73746f726167656b696c6c5f70726566697872656d61726b5f776974685f6576656e74617574686f72697a655f75706772616465617574686f72697a655f757067726164655f776974686f75745f636865636b736170706c795f617574686f72697a65645f75706772616465e29c85206e6f206d6967726174696f6e20666f72201f1511001500000072756e74696d653a3a6672616d652d737570706f72746672616d655f73797374656d3a3a70616c6c65743a65787472696e7369635f696e646578f09f90a5204e65772070616c6c65742020646574656374656420696e207468652072756e74696d652e205468652070616c6c657420686173206e6f20646566696e65642073746f726167652076657273696f6e2c20736f20746865206f6e2d636861696e2076657273696f6e206973206265696e6720696e697469616c697a656420746f202e76151100100000008615110075000000fb151100010000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6f64652e72736672616d655f73797374656d3a3a696e697469616c697a653a696e747261626c6f636b5f656e74726f70790045787472696e736963206661696c656420617420626c6f636b28293a200000009c1611001a000000b61611000300000072756e74696d653a3a73797374656d6672616d655f73797374656d3a636f64655b5d202065787472696e736963732c206c656e6774683a2020286e6f726d616c20252c206f703a20252c206d616e6461746f7279202529202f206e6f726d616c207765696768743a20282529206f7020776569676874202529202f206d616e6461746f72792077656967687420252900ec16110001000000ed16110002000000ef1611001500000004171100090000000d17110007000000141711000d00000021171100130000003417110002000000361711000d00000034171100020000004317110016000000341711000200000059171100020000004e6f646520697320636f6e6669677572656420746f20757365207468652073616d6520686173683b20716564d80100000000000001000000d9010000151411005b000000470700000e000000205468652066756c6c206163636f756e7420696e666f726d6174696f6e20666f72206120706172746963756c6172206163636f756e742049442e20546f74616c2065787472696e7369637320636f756e7420666f72207468652063757272656e7420626c6f636b2e205768657468657220616c6c20696e686572656e74732068617665206265656e206170706c6965642e205468652063757272656e742077656967687420666f722074686520626c6f636b2e20546f74616c206c656e6774682028696e2062797465732920666f7220616c6c2065787472696e736963732070757420746f6765746865722c20666f72207468652063757272656e7420626c6f636b2e204d6170206f6620626c6f636b206e756d6265727320746f20626c6f636b206861736865732e2045787472696e73696373206461746120666f72207468652063757272656e7420626c6f636b20286d61707320616e2065787472696e736963277320696e64657820746f206974732064617461292e205468652063757272656e7420626c6f636b206e756d626572206265696e672070726f6365737365642e205365742062792060657865637574655f626c6f636b602e2048617368206f66207468652070726576696f757320626c6f636b2e20446967657374206f66207468652063757272656e7420626c6f636b2c20616c736f2070617274206f662074686520626c6f636b206865616465722e204576656e7473206465706f736974656420666f72207468652063757272656e7420626c6f636b2e204e4f54453a20546865206974656d20697320756e626f756e6420616e642073686f756c64207468657265666f7265206e657665722062652072656164206f6e20636861696e2e20497420636f756c64206f746865727769736520696e666c6174652074686520506f562073697a65206f66206120626c6f636b2e204576656e747320686176652061206c6172676520696e2d6d656d6f72792073697a652e20426f7820746865206576656e747320746f206e6f7420676f206f75742d6f662d6d656d6f7279206a75737420696e206361736520736f6d656f6e65207374696c6c207265616473207468656d2066726f6d2077697468696e207468652072756e74696d652e20546865206e756d626572206f66206576656e747320696e2074686520604576656e74733c543e60206c6973742e204d617070696e67206265747765656e206120746f7069632028726570726573656e74656420627920543a3a486173682920616e64206120766563746f72206f6620696e6465786573206f66206576656e747320696e2074686520603c4576656e74733c543e3e60206c6973742e20416c6c20746f70696320766563746f727320686176652064657465726d696e69737469632073746f72616765206c6f636174696f6e7320646570656e64696e67206f6e2074686520746f7069632e205468697320616c6c6f7773206c696768742d636c69656e747320746f206c6576657261676520746865206368616e67657320747269652073746f7261676520747261636b696e67206d656368616e69736d20616e6420696e2063617365206f66206368616e67657320666574636820746865206c697374206f66206576656e7473206f6620696e7465726573742e205468652076616c756520686173207468652074797065206028426c6f636b4e756d626572466f723c543e2c204576656e74496e646578296020626563617573652069662077652075736564206f6e6c79206a7573742074686520604576656e74496e64657860207468656e20696e20636173652069662074686520746f70696320686173207468652073616d6520636f6e74656e7473206f6e20746865206e65787420626c6f636b206e6f206e6f74696669636174696f6e2077696c6c20626520747269676765726564207468757320746865206576656e74206d69676874206265206c6f73742e2053746f726573207468652060737065635f76657273696f6e6020616e642060737065635f6e616d6560206f66207768656e20746865206c6173742072756e74696d6520757067726164652068617070656e65642e2054727565206966207765206861766520757067726164656420736f207468617420607479706520526566436f756e74602069732060753332602e2046616c7365202864656661756c7429206966206e6f742e2054727565206966207765206861766520757067726164656420736f2074686174204163636f756e74496e666f20636f6e7461696e73207468726565207479706573206f662060526566436f756e74602e2046616c7365202864656661756c7429206966206e6f742e2054686520657865637574696f6e207068617365206f662074686520626c6f636b2e2060536f6d6560206966206120636f6465207570677261646520686173206265656e20617574686f72697a65642e20426c6f636b20262065787472696e7369637320776569676874733a20626173652076616c75657320616e64206c696d6974732e426c6f636b5765696768747320546865206d6178696d756d206c656e677468206f66206120626c6f636b2028696e206279746573292e426c6f636b4c656e677468204d6178696d756d206e756d626572206f6620626c6f636b206e756d62657220746f20626c6f636b2068617368206d617070696e677320746f206b65657020286f6c64657374207072756e6564206669727374292e426c6f636b48617368436f756e742054686520776569676874206f662072756e74696d65206461746162617365206f7065726174696f6e73207468652072756e74696d652063616e20696e766f6b652e4462576569676874204765742074686520636861696e277320696e2d636f64652076657273696f6e2e56657273696f6e205468652064657369676e61746564205353353820707265666978206f66207468697320636861696e2e2054686973207265706c6163657320746865202273733538466f726d6174222070726f7065727479206465636c6172656420696e2074686520636861696e20737065632e20526561736f6e2069732074686174207468652072756e74696d652073686f756c64206b6e6f772061626f7574207468652070726566697820696e206f7264657220746f206d616b6520757365206f6620697420617320616e206964656e746966696572206f662074686520636861696e2e5353353850726566697843616c6c54436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e004c211100430000004572726f724572726f7220666f72207468652053797374656d2070616c6c65749d2111001b000000496e76616c6964537065634e616d655370656356657273696f6e4e65656473546f496e6372656173654661696c6564546f4578747261637452756e74696d6556657273696f6e4e6f6e44656661756c74436f6d706f736974654e6f6e5a65726f526566436f756e7443616c6c46696c74657265644d756c7469426c6f636b4d6967726174696f6e734f6e676f696e674e6f7468696e67417574686f72697a6564556e617574686f72697a65644576656e744576656e7420666f72207468652053797374656d2070616c6c65742e000000712211001c00000045787472696e7369635375636365737345787472696e7369634661696c6564436f6465557064617465644e65774163636f756e744b696c6c65644163636f756e7452656d61726b656455706772616465417574686f72697a65644964416d6f756e746672616d655f737570706f72743a3a7472616974733a3a746f6b656e733a3a6d697363496442616c616e63650000da010000da010000da010000da010000446566656e736976656c792062756d70696e67206120636f6e73756d6572207265662e0038231100230000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f62616c616e6365732f7372632f6c69622e727372756e74696d653a3a62616c616e63657370616c6c65745f62616c616e6365733a3a70616c6c6574617373657274696f6e206661696c65643a206163636f756e742e667265652e69735f7a65726f2829207c7c206163636f756e742e66726565203e3d206564207c7c20216163636f756e742e72657365727665642e69735f7a65726f28290000642311005d0000002b040000150000007374727563742047656e65736973436f6e666967141611005c000000e800000019000000141611005c000000da00000019000000141611005c000000870000001e00000053746f7261676556657273696f6e0000db010000040000000400000012010000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000010000000600000001000000010000000100000002000000020000000200000001000000010000000100000001000000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64652f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7061726974792d7363616c652d636f6465632d332e362e31322f7372632f636f6d706163742e72730000736869667465642073756666696369656e74206269747320726967687420746f206c656164206f6e6c79206c656164696e67207a65726f733b20716564000000b02511003d0000000000000000000000442511006a0000005e0100001100000056524648617368565246526573756c747672662d696e7672662d6f75742f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264655f6a736f6e2d312e302e3132342f7372632f64652e7273002d2611005e0000009a040000220000002d2611005e0000009004000026000000de0100000000000001000000aa0100003a5f5f53544f524147455f56455253494f4e5f5f3a000000de0100000000000001000000df010000de0100000000000001000000df010000de0100000000000001000000df01000063616c6c6f7765645f736c6f7473737472756374204261626545706f6368436f6e66696775726174696f6e2077697468203220656c656d656e747300122711002d000000e00100000800000004000000e1010000617574686f72697469657365706f6368436f6e6669677374727563742047656e65736973436f6e6669672077697468203220656c656d656e747300006e2711002400000062616c616e6365737374727563742047656e65736973436f6e6669672077697468203120656c656d656e7400a427110023000000e00100000400000004000000e20100007672662d6e6d2d706b6d697373696e67206669656c64206060000000e92711000f000000f827110001000000756e6b6e6f776e206669656c642060602c20746865726520617265206e6f206669656c64730000000c2811000f0000001b28110016000000602c206578706563746564200c2811000f000000442811000c000000696e76616c6964206c656e677468202c206578706563746564200000602811000f0000006f2811000b0000006475706c6963617465206669656c6420600000008c28110011000000f827110001000000756e6b6e6f776e2076617269616e742060602c20746865726520617265206e6f2076617269616e7473000000b028110011000000c128110018000000b028110011000000442811000c000000556e646572666c6f774f766572666c6f774469766973696f6e42795a65726f696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64653a20747269652073747265616d20636f646563206f6e6c7920666f72206e6f20657874656e73696f6e20747269650000001b291100560000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f747269652f7372632f747269655f73747265616d2e727300007c29110066000000760000000d0000007c2911006600000047000000400000007c29110066000000470000004d000000de0100000000000001000000e3010000de0100000000000001000000e3010000de0100000000000001000000e3010000de0100000000000001000000e4010000de0100000000000001000000e5010000de0100000000000001000000e6010000de0100000000000001000000e7010000de0100000000000001000000e8010000de0100000000000001000000e9010000de0100000000000001000000ea010000de0100000000000001000000eb010000de0100000000000001000000ec0100002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d726f6f742d302e31382e302f7372632f6c69622e7273000000d42a11005d0000002901000034000000d42a11005d000000400100002d000000d42a11005d0000003b01000034000000d42a11005d000000790100001f000000d42a11005d0000006001000034000000d42a11005d000000620100002e000000d42a11005d000000a100000026000000d42a11005d000000a10000002c000000d42a11005d000000a1000000250000007374727563742052756e74696d6547656e65736973436f6e6669672077697468203420656c656d656e747300c42b11002b00000073797374656d62616265737562737472617465546573740009000000080000000e000000fc281100052911000d29110050617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273000000392c110060000000b20000001e00000068656164657248656164657265787472696e736963735665633c45787472696e7369633e73746174655f726f6f74486173683a3a4f7574707574706172656e745f6861736865787472696e736963735f726f6f746e756d6265724e756d6265726469676573744469676573745265706f727420617574686f726974792065717569766f636174696f6e2f6d69736265686176696f722e2054686973206d6574686f642077696c6c207665726966797468652065717569766f636174696f6e2070726f6f6620616e642076616c69646174652074686520676976656e206b6579206f776e6572736869702070726f6f66616761696e73742074686520657874726163746564206f6666656e6465722e20496620626f7468206172652076616c69642c20746865206f6666656e63652077696c6c6265207265706f727465642e546869732065787472696e736963206d7573742062652063616c6c656420756e7369676e656420616e642069742069732065787065637465642074686174206f6e6c79626c6f636b20617574686f72732077696c6c2063616c6c206974202876616c69646174656420696e206056616c6964617465556e7369676e656460292c206173207375636869662074686520626c6f636b20617574686f7220697320646566696e65642069742077696c6c20626520646566696e6564206173207468652065717569766f636174696f6e7265706f727465722e182d1100420000005a2d1100410000009b2d110043000000de2d11000c000000ea2d1100430000002d2e110045000000722e110045000000b72e110009000000182d1100420000005a2d1100410000009b2d110043000000de2d11000c000000506c616e20616e2065706f636820636f6e666967206368616e67652e205468652065706f636820636f6e666967206368616e6765206973207265636f7264656420616e642077696c6c20626520656e6163746564206f6e746865206e6578742063616c6c20746f2060656e6163745f65706f63685f6368616e6765602e2054686520636f6e6669672077696c6c20626520616374697661746564206f6e652065706f63682061667465722e4d756c7469706c652063616c6c7320746f2074686973206d6574686f642077696c6c207265706c61636520616e79206578697374696e6720706c616e6e656420636f6e666967206368616e67652074686174206861646e6f74206265656e20656e6163746564207965742e0000202f110057000000772f110054000000cb2f11005600000021301100150000006b65795f6f776e65725f70726f6f66543a3a4b65794f776e657250726f6f6665717569766f636174696f6e5f70726f6f66426f783c45717569766f636174696f6e50726f6f663c486561646572466f723c543e3e3e636f6e6669674e657874436f6e66696744657363726970746f725375626d697474656420636f6e66696775726174696f6e20697320696e76616c69642e000000000000c730110023000000416e2065717569766f636174696f6e2070726f6f662070726f76696465642061732070617274206f6620616e2065717569766f636174696f6e207265706f727420697320696e76616c69642e00000000f83011004c0000004120676976656e2065717569766f636174696f6e207265706f72742069732076616c69642062757420616c72656164792070726576696f75736c79207265706f727465642e000000503111004500000041206b6579206f776e6572736869702070726f6f662070726f76696465642061732070617274206f6620616e2065717569766f636174696f6e207265706f727420697320696e76616c69642e00000000a03111004c000000543a3a4e6f6e63656e6f6e63654e6f6e636570726f766964657273526566436f756e74636f6e73756d657273646174614163636f756e744461746173756666696369656e74736576656e7445746f706963735665633c543e70686173655068617365636f64655f68617368543a3a48617368636865636b5f76657273696f6e626f6f6c4b696c6c20736f6d65206974656d732066726f6d2073746f726167652e7b3211001d00000053657420746865206e65772072756e74696d6520636f646520776974686f757420646f696e6720616e7920636865636b73206f662074686520676976656e2060636f6465602e4e6f746520746861742072756e74696d652075706772616465732077696c6c206e6f742072756e20696620746869732069732063616c6c656420776974682061206e6f742d696e6372656173696e67207370656376657273696f6e210000a032110046000000e632110000000000e6321100540000003a331100080000004d616b6520736f6d65206f6e2d636861696e2072656d61726b20616e6420656d6974206576656e742e000000643311002900000053657420746865206e65772072756e74696d6520636f64652e00000000000000983311001900000053657420746865206e756d626572206f6620706167657320696e2074686520576562417373656d626c7920656e7669726f6e6d656e74277320686561702e0000c03311003e0000004b696c6c20616c6c2073746f72616765206974656d7320776974682061206b657920746861742073746172747320776974682074686520676976656e207072656669782e2a2a4e4f54453a2a2a2057652072656c79206f6e2074686520526f6f74206f726967696e20746f2070726f7669646520757320746865206e756d626572206f66207375626b65797320756e64657274686520707265666978207765206172652072656d6f76696e6720746f2061636375726174656c792063616c63756c6174652074686520776569676874206f6620746869732066756e6374696f6e2e0000000834110044000000e6321100000000004c3411004e0000009a3411004f0000004d616b6520736f6d65206f6e2d636861696e2072656d61726b2e43616e20626520657865637574656420627920657665727920606f726967696e602e0c3511001a000000e632110000000000263511002200000053657420736f6d65206974656d73206f662073746f726167652e000000000000603511001a000000417574686f72697a6520616e207570677261646520746f206120676976656e2060636f64655f686173686020666f72207468652072756e74696d652e205468652072756e74696d652063616e20626520737570706c6965646c617465722e5741524e494e473a205468697320617574686f72697a657320616e207570677261646520746861742077696c6c2074616b6520706c61636520776974686f757420616e792073616665747920636865636b732c20666f726578616d706c652074686174207468652073706563206e616d652072656d61696e73207468652073616d6520616e642074686174207468652076657273696f6e206e756d62657220696e637265617365732e204e6f747265636f6d6d656e64656420666f72206e6f726d616c207573652e205573652060617574686f72697a655f757067726164656020696e73746561642e546869732063616c6c20726571756972657320526f6f74206f726967696e2e00008835110058000000e035110006000000e632110000000000e6351100570000003d36110056000000933611003c000000e632110000000000cf3611001f0000008835110058000000e035110006000000e632110000000000cf3611001f00000050726f766964652074686520707265696d616765202872756e74696d652062696e617279292060636f64656020666f7220616e2075706772616465207468617420686173206265656e20617574686f72697a65642e49662074686520617574686f72697a6174696f6e20726571756972656420612076657273696f6e20636865636b2c20746869732063616c6c2077696c6c20656e73757265207468652073706563206e616d6572656d61696e7320756e6368616e67656420616e6420746861742074686520737065632076657273696f6e2068617320696e637265617365642e446570656e64696e67206f6e207468652072756e74696d65277320604f6e536574436f64656020636f6e66696775726174696f6e2c20746869732066756e6374696f6e206d6179206469726563746c79206170706c79746865206e65772060636f64656020696e207468652073616d6520626c6f636b206f7220617474656d707420746f207363686564756c652074686520757067726164652e416c6c206f726967696e732061726520616c6c6f7765642e005037110055000000e632110000000000a537110052000000f73711003a000000e63211000000000031381100560000008738110044000000e632110000000000cb38110018000000636f64655665633c75383e70616765737536347072656669784b65796974656d735665633c4b657956616c75653e7375626b65797375333272656d61726b6b6579735665633c4b65793e546865206e616d65206f662073706563696669636174696f6e20646f6573206e6f74206d61746368206265747765656e207468652063757272656e742072756e74696d65616e6420746865206e65772072756e74696d652e00007639110044000000ba391100140000005468652073706563696669636174696f6e2076657273696f6e206973206e6f7420616c6c6f77656420746f206465637265617365206265747765656e207468652063757272656e742072756e74696d65e039110050000000ba39110014000000537569636964652063616c6c6564207768656e20746865206163636f756e7420686173206e6f6e2d64656661756c7420636f6d706f7369746520646174612e00403a11003f0000004e6f207570677261646520617574686f72697a65642e0000883a1100160000004661696c656420746f2065787472616374207468652072756e74696d652076657273696f6e2066726f6d20746865206e65772072756e74696d652e4569746865722063616c6c696e672060436f72655f76657273696f6e60206f72206465636f64696e67206052756e74696d6556657273696f6e60206661696c65642e000000a83a11003b000000e632110000000000e33a110042000000546865206f726967696e2066696c7465722070726576656e74207468652063616c6c20746f20626520646973706174636865642e00000000403b11003400000041206d756c74692d626c6f636b206d6967726174696f6e206973206f6e676f696e6720616e642070726576656e7473207468652063757272656e7420636f64652066726f6d206265696e67207265706c616365642e000000803b11005500000054686572652069732061206e6f6e2d7a65726f207265666572656e636520636f756e742070726576656e74696e6720746865206163636f756e742066726f6d206265696e67207075726765642e000000e03b11004d000000546865207375626d697474656420636f6465206973206e6f7420617574686f72697a65642e000000383c110025000000416e2065787472696e736963206661696c65642e00000000683c110014000000603a636f6465602077617320757064617465642e00000000883c11001400000041206e6577206163636f756e742077617320637265617465642e000000000000a83c11001a000000416e20757067726164652077617320617574686f72697a65642e000000000000d03c11001a000000416e206163636f756e7420776173207265617065642e0000f83c110016000000416e2065787472696e73696320636f6d706c65746564207375636365737366756c6c792e00000000183d1100240000004f6e206f6e2d636861696e2072656d61726b2068617070656e65642e00000000483d11001c0000006163636f756e74543a3a4163636f756e7449646861736864697370617463685f696e666f4469737061746368496e666f73656e64657264697370617463685f6572726f7244697370617463684572726f72616d6f756e7442616c616e6365696449646d616e6461746f7279546f7065726174696f6e616c6e6f726d616c4163636f756e744964666c6167734578747261466c61677366726f7a656e7265736572766564667265654c6f636b4964656e746966696572726561736f6e73526561736f6e73526573657276654964656e746966696572556e7265736572766520736f6d652062616c616e63652066726f6d2061207573657220627920666f7263652e43616e206f6e6c792062652063616c6c656420627920524f4f542e00443e11002c000000e632110000000000703e11001b0000005472616e736665722074686520656e74697265207472616e7366657261626c652062616c616e63652066726f6d207468652063616c6c6572206163636f756e742e4e4f54453a20546869732066756e6374696f6e206f6e6c7920617474656d70747320746f207472616e73666572205f7472616e7366657261626c655f2062616c616e6365732e2054686973206d65616e732074686174616e79206c6f636b65642c2072657365727665642c206f72206578697374656e7469616c206465706f7369747320287768656e20606b6565705f616c6976656020697320607472756560292c2077696c6c206e6f742062657472616e7366657272656420627920746869732066756e6374696f6e2e20546f20656e73757265207468617420746869732066756e6374696f6e20726573756c747320696e2061206b696c6c6564206163636f756e742c796f75206d69676874206e65656420746f207072657061726520746865206163636f756e742062792072656d6f76696e6720616e79207265666572656e636520636f756e746572732c2073746f726167656465706f736974732c206574632e2e2e546865206469737061746368206f726967696e206f6620746869732063616c6c206d757374206265205369676e65642e2d206064657374603a2054686520726563697069656e74206f6620746865207472616e736665722e2d20606b6565705f616c697665603a204120626f6f6c65616e20746f2064657465726d696e652069662074686520607472616e736665725f616c6c60206f7065726174696f6e2073686f756c642073656e6420616c6c20206f66207468652066756e647320746865206163636f756e74206861732c2063617573696e67207468652073656e646572206163636f756e7420746f206265206b696c6c6564202866616c7365292c206f7220207472616e736665722065766572797468696e6720657863657074206174206c6561737420746865206578697374656e7469616c206465706f7369742c2077686963682077696c6c2067756172616e74656520746f20206b656570207468652073656e646572206163636f756e7420616c697665202874727565292e000000a43e110041000000e632110000000000e53e1100560000003b3f110058000000933f110057000000ea3f1100510000003b40110010000000e6321100000000004b40110030000000e6321100000000007b40110028000000a340110056000000f9401100530000004c41110056000000a2411100270000004275726e2074686520737065636966696564206c697175696420667265652062616c616e63652066726f6d20746865206f726967696e206163636f756e742e496620746865206f726967696e2773206163636f756e7420656e64732075702062656c6f7720746865206578697374656e7469616c206465706f736974206173206120726573756c746f6620746865206275726e20616e6420606b6565705f616c697665602069732066616c73652c20746865206163636f756e742077696c6c206265207265617065642e556e6c696b652073656e64696e672066756e647320746f2061205f6275726e5f20616464726573732c207768696368206d6572656c79206d616b6573207468652066756e647320696e61636365737369626c652c7468697320606275726e60206f7065726174696f6e2077696c6c2072656475636520746f74616c2069737375616e63652062792074686520616d6f756e74205f6275726e65645f2e0000444211003f000000e6321100000000008342110049000000cc42110042000000e6321100000000000e43110054000000624311004800000053616d6520617320746865205b607472616e736665725f616c6c6f775f6465617468605d2063616c6c2c206275742077697468206120636865636b207468617420746865207472616e736665722077696c6c206e6f746b696c6c20746865206f726967696e206163636f756e742e393925206f66207468652074696d6520796f752077616e74205b607472616e736665725f616c6c6f775f6465617468605d20696e73746561642e5b607472616e736665725f616c6c6f775f6465617468605d3a207374727563742e50616c6c65742e68746d6c236d6574686f642e7472616e73666572e4431100560000003a44110018000000e632110000000000524411003a000000e6321100000000008c4411003c00000041646a7573742074686520746f74616c2069737375616e636520696e20612073617475726174696e67207761792e43616e206f6e6c792062652063616c6c656420627920726f6f7420616e6420616c77617973206e65656473206120706f736974697665206064656c7461602e23204578616d706c650000f84411002e000000e632110000000000264511003f000000e632110000000000654511000900000055706772616465206120737065636966696564206163636f756e742e2d20606f726967696e603a204d75737420626520605369676e6564602e2d206077686f603a20546865206163636f756e7420746f2062652075706772616465642e546869732077696c6c20776169766520746865207472616e73616374696f6e20666565206966206174206c6561737420616c6c2062757420313025206f6620746865206163636f756e7473206e656564656420746f62652075706772616465642e20285765206c657420736f6d65206e6f74206861766520746f206265207570677261646564206a75737420696e206f7264657220746f20616c6c6f7720666f7220746865706f73736962696c697479206f6620636875726e292e984511001c000000e632110000000000b44511001d000000d145110024000000e632110000000000f5451100550000004a461100500000009a4611001600000045786163746c7920617320607472616e736665725f616c6c6f775f6465617468602c2065786365707420746865206f726967696e206d75737420626520726f6f7420616e642074686520736f75726365206163636f756e746d6179206265207370656369666965642e000000f04611005800000048471100110000005365742074686520726567756c61722062616c616e6365206f66206120676976656e206163636f756e742e546865206469737061746368206f726967696e20666f7220746869732063616c6c2069732060726f6f74602e006c4711002b000000e632110000000000974711002c0000005472616e7366657220736f6d65206c697175696420667265652062616c616e636520746f20616e6f74686572206163636f756e742e607472616e736665725f616c6c6f775f6465617468602077696c6c207365742074686520604672656542616c616e636560206f66207468652073656e64657220616e642072656365697665722e4966207468652073656e6465722773206163636f756e742069732062656c6f7720746865206578697374656e7469616c206465706f736974206173206120726573756c746f6620746865207472616e736665722c20746865206163636f756e742077696c6c206265207265617065642e546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520605369676e65646020627920746865207472616e736163746f722e00dc47110035000000e632110000000000114811004d0000005e48110044000000a24811002c000000e632110000000000ce4811004500000076616c7565543a3a42616c616e6365646573744163636f756e7449644c6f6f6b75704f663c543e77686f6b6565705f616c697665646972656374696f6e41646a7573746d656e74446972656374696f6e5665633c543a3a4163636f756e7449643e736f7572636564656c74616e65775f6672656556657374696e672062616c616e636520746f6f206869676820746f2073656e642076616c75652e00c0491100270000004e756d626572206f6620667265657a65732065786365656420604d6178467265657a6573602e0000f0491100260000005472616e736665722f7061796d656e7420776f756c64206b696c6c206163636f756e742e00000000204a11002400000042656e6566696369617279206163636f756e74206d757374207072652d65786973742e0000000000504a1100230000004e756d626572206f6620686f6c647320657863656564206056617269616e74436f756e744f663c543a3a52756e74696d65486f6c64526561736f6e3e602e0000804a11003e00000056616c756520746f6f206c6f7720746f20637265617465206163636f756e742064756520746f206578697374656e7469616c206465706f7369742e0000000000c84a11003b0000004e756d626572206f66206e616d65642072657365727665732065786365656420604d61785265736572766573602e0000104b11002e00000042616c616e636520746f6f206c6f7720746f2073656e642076616c75652e0000484b11001e0000005468652064656c74612063616e6e6f74206265207a65726f2e00000000000000704b1100190000004163636f756e74206c6971756964697479207265737472696374696f6e732070726576656e74207769746864726177616c2e000000000000984b110032000000412076657374696e67207363686564756c6520616c72656164792065786973747320666f722074686973206163636f756e742e0000000000d84b1100330000005468652069737375616e63652063616e6e6f74206265206d6f6469666965642073696e636520697420697320616c72656164792064656163746976617465642e184c110040000000412062616c616e6365207761732073657420627920726f6f742e000000000000604c11001a000000536f6d6520616d6f756e742077617320726573746f72656420696e746f20616e206163636f756e742e00000000000000884c110029000000416e206163636f756e74207761732063726561746564207769746820736f6d6520667265652062616c616e63652e0000c04c11002e000000536f6d652062616c616e636520776173207468617765642ef84c110018000000416e206163636f756e74207761732075706772616465642e184d110018000000536f6d6520616d6f756e7420776173206465706f73697465642028652e672e20666f72207472616e73616374696f6e2066656573292e0000384d110036000000546f74616c2069737375616e636520776173206465637265617365642062792060616d6f756e74602c206372656174696e672061206465627420746f2062652062616c616e6365642e00000000000000784d110049000000536f6d652062616c616e636520776173206d6f7665642066726f6d207468652072657365727665206f6620746865206669727374206163636f756e7420746f20746865207365636f6e64206163636f756e742e46696e616c20617267756d656e7420696e64696361746573207468652064657374696e6174696f6e2062616c616e636520747970652e000000d04d110053000000234e110036000000536f6d652062616c616e636520776173206c6f636b65642e000000006c4e110018000000536f6d652062616c616e63652077617320726573657276656420286d6f7665642066726f6d206672656520746f207265736572766564292e904e110038000000536f6d6520616d6f756e74207761732072656d6f7665642066726f6d20746865206163636f756e742028652e672e20666f72206d69736265686176696f72292ed04e110040000000536f6d6520616d6f756e7420776173206d696e74656420696e746f20616e206163636f756e742e00184f110027000000546f74616c2069737375616e63652077617320696e637265617365642062792060616d6f756e74602c206372656174696e6720612063726564697420746f2062652062616c616e6365642e0000000000484f11004b0000005468652060546f74616c49737375616e6365602077617320666f72636566756c6c79206368616e6765642e0000000000a04f11002b0000005472616e73666572207375636365656465642e0000000000d84f110013000000536f6d652062616c616e63652077617320756e6c6f636b65642e000000000000f84f11001a000000416e206163636f756e74207761732072656d6f7665642077686f73652062616c616e636520776173206e6f6e2d7a65726f206275742062656c6f77204578697374656e7469616c4465706f7369742c726573756c74696e6720696e20616e206f75747269676874206c6f73732e000000205011004f0000006f5011001e000000536f6d6520616d6f756e74207761732077697468647261776e2066726f6d20746865206163636f756e742028652e672e20666f72207472616e73616374696f6e2066656573292e00a050110047000000536f6d652062616c616e6365207761732066726f7a656e2ef050110018000000536f6d652062616c616e63652077617320756e726573657276656420286d6f7665642066726f6d20726573657276656420746f2066726565292e000000000000105111003a000000536f6d6520616d6f756e74207761732073757370656e6465642066726f6d20616e206163636f756e74202869742063616e20626520726573746f726564206c61746572292e0000005851110045000000536f6d6520616d6f756e7420776173206275726e65642066726f6d20616e206163636f756e742e00a851110027000000746f66726f6d667265655f62616c616e636564657374696e6174696f6e5f7374617475735374617475736e65776f6c647461726765745f6e756d6265724e7461726765745f6861736848666972737428562c2053297365636f6e646964656e74697479726f756e645f6e756d6265726f6666656e64657266697273745f686561646572736c6f74536c6f747365636f6e645f6865616465727365745f6964536574496465717569766f636174696f6e45717569766f636174696f6e3c482c204e3e66696e616c6974795f6772616e6470613a3a45717569766f636174696f6e3c417574686f7269747949642c2066696e616c6974795f6772616e6470613a3a507265636f6d6d69740a3c482c204e3e2c20417574686f726974795369676e61747572652c3e66696e616c6974795f6772616e6470613a3a45717569766f636174696f6e3c417574686f7269747949642c2066696e616c6974795f6772616e6470613a3a507265766f74653c0a482c204e3e2c20417574686f726974795369676e61747572652c3e6d6f64654d6f64654c65676163792063616c6c207573656420696e207472616e73616374696f6e20706f6f6c2062656e63686d61726b732e0067531100300000007472616e736665725472616e7366657244617461496d706c696369746c792066696c6c206120626c6f636b20626f6479207769746820736f6d6520646174612eb45311002c0000005075742f64656c65746520736f6d6520646174612066726f6d2073746f726167652e20496e74656e64656420746f2075736520617320616e20756e7369676e65642065787472696e7369632e00000000e85311004c0000006b65794f7074696f6e3c5665633c75383e3e57726974652061206b65792076616c7565207061697220746f20746865206f6666636861696e2064617461626173652e000000000000525411003000000052656d6f76652061206b657920616e6420616e206173736f6369617465642076616c75652066726f6d20746865206f6666636861696e2064617461626173652e905411004000000043726561746520616e20696e64657820666f7220746869732063616c6c2e0000d85411001e0000004465706f73697420676976656e20646967657374206974656d7320696e746f207468652073797374656d2073746f726167652e20546865792077696c6c20626520696e636c7564656420696e206120686561646572647572696e672066696e616c697a6174696f6e2e000000005511005500000055551100140000006c6f6773705f72756e74696d653a3a67656e657269633a3a4469676573744974656d546869732063616c6c2069732076616c696461746564206173206056616c69645472616e73616374696f6e60207769746820676976656e207072696f726974792e009e551100410000007072696f726974795472616e73616374696f6e5072696f72697479546869732063616c6c2069732076616c696461746564206173206e6f6e2d70726f70616761626c65206056616c69645472616e73616374696f6e602e00035611003c00000046696c6c2074686520626c6f636b2077656967687420757020746f2074686520676976656e20726174696f2e00000000485611002c000000726174696f50657262696c6c5265616420582074696d65732066726f6d2074686520737461746520736f6d6520646174612e50616e6963732069662069742063616e206e6f742072656164206058602074696d65732e00008c56110026000000e632110000000000b256110024000000636f756e745265616420582074696d65732066726f6d2074686520737461746520736f6d65206461746120616e64207468656e2070616e69632152657475726e7320604f6b60206966206974206469646e2774207265616420616e797468696e672e0000f556110035000000e6321100000000002a5711002800000073656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c53797374656d2c2052756e74696d653e73656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c426162652c2052756e74696d653e73656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c537562737472617465546573742c2052756e74696d653e73656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c42616c616e6365732c2052756e74696d653e6672616d655f73797374656d3a3a4572726f723c52756e74696d653e70616c6c65745f626162653a3a4572726f723c52756e74696d653e70616c6c65745f62616c616e6365733a3a4572726f723c52756e74696d653e6672616d655f73797374656d3a3a4576656e743c52756e74696d653e70616c6c65745f62616c616e6365733a3a4576656e743c52756e74696d653e496e646578206f7574206f6620626f756e647300b0591100130000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f736c6963652f736f72742e727300cc591100730000003b0400000e000000cc59110073000000480400001c000000cc59110073000000490400001d000000cc591100730000004a04000025000000cc591100730000008e04000040000000cc59110073000000b40400004e000000cc59110073000000c204000056000000617373657274696f6e206661696c65643a20656e64203e3d20737461727420262620656e64203c3d206c656ecc591100730000002d05000005000000cc591100730000003e05000029000000617373657274696f6e206661696c65643a206f666673657420213d2030202626206f6666736574203c3d206c656e0000cc591100730000009b00000005000000746573747061726974792d7465737400df6acb689907609b0500000037e397fc7c91f5e402000000d2bc9897eed08f150300000040fe3ad401f8959a06000000bc9d89904f5b923f01000000c6e9a76309f39b0902000000dd718d5cc53262d401000000cbca25e39f14238702000000f78b278be53f454c02000000ab3c0572291feb8b01000000ed99c5acb25eedf503000000fbc577b9d747efd601000000000000803c5b11000400000000000080405b11000b000000000000804c5b11000c000000010000000200000002000000010000000100000042616420696e70757420646174612070726f766964656420746f2076657273696f6e3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000145c11005a0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f6c69622e7273000000785c110061000000d10100000100000042616420696e70757420646174612070726f766964656420746f20657865637574655f626c6f636b3a200000ec5c11002a00000042616420696e70757420646174612070726f766964656420746f20696e697469616c697a655f626c6f636b3a20000000205d11002d00000042616420696e70757420646174612070726f766964656420746f206d657461646174613a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e00585d11005b00000042616420696e70757420646174612070726f766964656420746f206d657461646174615f61745f76657273696f6e3a20bc5d11003000000042616420696e70757420646174612070726f766964656420746f206d657461646174615f76657273696f6e733a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792ef45d11006400000042616420696e70757420646174612070726f766964656420746f2076616c69646174655f7472616e73616374696f6e3a20000000605e11003100000042616420696e70757420646174612070726f766964656420746f206170706c795f65787472696e7369633a209c5e11002c00000042616420696e70757420646174612070726f766964656420746f2066696e616c697a655f626c6f636b3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e000000d05e11006100000042616420696e70757420646174612070726f766964656420746f20696e686572656e745f65787472696e736963733a203c5f11003000000042616420696e70757420646174612070726f766964656420746f20636865636b5f696e686572656e74733a20745f11002c00000042616420696e70757420646174612070726f766964656420746f206163636f756e745f6e6f6e63653a200000a85f11002a00000042616420696e70757420646174612070726f766964656420746f2062616c616e63655f6f663a2000dc5f11002700000042616420696e70757420646174612070726f766964656420746f2062656e63686d61726b5f6164645f6f6e653a2000000c6011002e00000042616420696e70757420646174612070726f766964656420746f2062656e63686d61726b5f766563746f725f6164645f6f6e653a20000000446011003500000042616420696e70757420646174612070726f766964656420746f2066756e6374696f6e5f7369676e61747572655f6368616e6765643a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e000000846011006d00000042616420696e70757420646174612070726f766964656420746f207573655f747269653a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e00fc6011005b00000042616420696e70757420646174612070726f766964656420746f2062656e63686d61726b5f696e6469726563745f63616c6c3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000606111006a00000042616420696e70757420646174612070726f766964656420746f2062656e63686d61726b5f6469726563745f63616c6c3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792ed46111006800000042616420696e70757420646174612070726f766964656420746f207665635f776974685f63617061636974793a200000446211002e00000042616420696e70757420646174612070726f766964656420746f206765745f626c6f636b5f6e756d6265723a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e007c6211006300000042616420696e70757420646174612070726f766964656420746f20746573745f656432353531395f63727970746f3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000e86211006600000042616420696e70757420646174612070726f766964656420746f20746573745f737232353531395f63727970746f3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000586311006600000042616420696e70757420646174612070726f766964656420746f20746573745f65636473615f63727970746f3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792ec86311006400000042616420696e70757420646174612070726f766964656420746f20746573745f73746f726167653a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e00346411005f00000042616420696e70757420646174612070726f766964656420746f20746573745f7769746e6573733a200000009c6411002900000042616420696e70757420646174612070726f766964656420746f20746573745f6d756c7469706c655f617267756d656e74733a20d06411003400000042616420696e70757420646174612070726f766964656420746f20646f5f74726163655f6c6f673a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e000c6511005f00000042616420696e70757420646174612070726f766964656420746f207665726966795f656432353531393a2000746511002b00000042616420696e70757420646174612070726f766964656420746f2077726974655f6b65795f76616c75653a20a86511002c00000042616420696e70757420646174612070726f766964656420746f20736c6f745f6475726174696f6e3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792edc6511006000000042616420696e70757420646174612070726f766964656420746f20617574686f7269746965733a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000446611005e00000042616420696e70757420646174612070726f766964656420746f20636f6e66696775726174696f6e3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792eac6611006000000042616420696e70757420646174612070726f766964656420746f2063757272656e745f65706f63685f73746172743a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000146711006600000042616420696e70757420646174612070726f766964656420746f2063757272656e745f65706f63683a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e846711006000000042616420696e70757420646174612070726f766964656420746f206e6578745f65706f63683a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e000000ec6711005d00000042616420696e70757420646174612070726f766964656420746f207375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e7369633a200000546811004a00000042616420696e70757420646174612070726f766964656420746f2067656e65726174655f6b65795f6f776e6572736869705f70726f6f663a20000000a86811003900000042616420696e70757420646174612070726f766964656420746f206f6666636861696e5f776f726b65723a20ec6811002c00000042616420696e70757420646174612070726f766964656420746f2067656e65726174655f73657373696f6e5f6b6579733a200000206911003200000042616420696e70757420646174612070726f766964656420746f206465636f64655f73657373696f6e5f6b6579733a205c6911003000000042616420696e70757420646174612070726f766964656420746f206772616e6470615f617574686f7269746965733a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000946911006600000042616420696e70757420646174612070726f766964656420746f2063757272656e745f7365745f69643a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e000000046a11006100000042616420696e70757420646174612070726f766964656420746f206275696c645f73746174653a20706a11002800000042616420696e70757420646174612070726f766964656420746f206765745f7072657365743a2000a06a11002700000042616420696e70757420646174612070726f766964656420746f207072657365745f6e616d65733a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e00d06a11005f0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f62616c616e6365732f7372632f6c69622e72737472616e736665725f616c6c6f775f6465617468666f7263655f7472616e736665727472616e736665725f6b6565705f616c6976657472616e736665725f616c6c666f7263655f756e72657365727665757067726164655f6163636f756e7473666f7263655f7365745f62616c616e6365666f7263655f61646a7573745f746f74616c5f69737375616e63656275726e4f7074696f6e544e6f6e65536f6d65e29c85206e6f206d6967726174696f6e20666f7220000000346c11001500000072756e74696d653a3a6672616d652d737570706f727470616c6c65745f62616c616e6365733a3a70616c6c65746475706c69636174652062616c616e63657320696e2067656e657369732e00816c11001e000000386b11005d000000110200000d0000007468652062616c616e6365206f6620616e79206163636f756e742073686f756c6420616c77617973206265206174206c6561737420746865206578697374656e7469616c206465706f7369742e000000b86c11004d000000386b11005d0000000302000011000000f09f90a5204e65772070616c6c65742020646574656374656420696e207468652072756e74696d652e20496e697469616c697a696e6720746865206f6e2d636861696e2073746f726167652076657273696f6e20746f206d61746368207468652073746f726167652076657273696f6e20646566696e656420696e207468652070616c6c65743a20206d110010000000306d1100780000000100006163636f756e7420776974682061206e6f6e2d7a65726f20726573657276652062616c616e636520686173206e6f2070726f766964657220726566732c206163636f756e745f69643a2027272ebb6d11004b000000066e11000200000072756e74696d653a3a62616c616e6365732054686520746f74616c20756e6974732069737375656420696e207468652073797374656d2e2054686520746f74616c20756e697473206f66206f75747374616e64696e672064656163746976617465642062616c616e636520696e207468652073797374656d2e205468652042616c616e6365732070616c6c6574206578616d706c65206f662073746f72696e67207468652062616c616e6365206f6620616e206163636f756e742e2023204578616d706c65206060606e6f636f6d70696c652020696d706c2070616c6c65745f62616c616e6365733a3a436f6e66696720666f722052756e74696d65207b2020202074797065204163636f756e7453746f7265203d2053746f726167654d61705368696d3c53656c663a3a4163636f756e743c52756e74696d653e2c206672616d655f73797374656d3a3a50726f76696465723c52756e74696d653e2c204163636f756e7449642c2053656c663a3a4163636f756e74446174613c42616c616e63653e3e20207d2060606020596f752063616e20616c736f2073746f7265207468652062616c616e6365206f6620616e206163636f756e7420696e20746865206053797374656d602070616c6c65742e20202074797065204163636f756e7453746f7265203d2053797374656d20427574207468697320636f6d657320776974682074726164656f6666732c2073746f72696e67206163636f756e742062616c616e63657320696e207468652073797374656d2070616c6c65742073746f72657320606672616d655f73797374656d60206461746120616c6f6e677369646520746865206163636f756e74206461746120636f6e747261727920746f2073746f72696e67206163636f756e742062616c616e63657320696e20746865206042616c616e636573602070616c6c65742c20776869636820757365732061206053746f726167654d61706020746f2073746f72652062616c616e6365732064617461206f6e6c792e204e4f54453a2054686973206973206f6e6c79207573656420696e207468652063617365207468617420746869732070616c6c6574206973207573656420746f2073746f72652062616c616e6365732e20416e79206c6971756964697479206c6f636b73206f6e20736f6d65206163636f756e742062616c616e6365732e204e4f54453a2053686f756c64206f6e6c79206265206163636573736564207768656e2073657474696e672c206368616e67696e6720616e642066726565696e672061206c6f636b2e20557365206f66206c6f636b73206973206465707265636174656420696e206661766f7572206f6620667265657a65732e20536565206068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f70756c6c2f31323935312f60204e616d6564207265736572766573206f6e20736f6d65206163636f756e742062616c616e6365732e20557365206f66207265736572766573206973206465707265636174656420696e206661766f7572206f6620686f6c64732e20536565206068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f70756c6c2f31323935312f6020486f6c6473206f6e206163636f756e742062616c616e6365732e20467265657a65206c6f636b73206f6e206163636f756e742062616c616e6365732e20546865206d696e696d756d20616d6f756e7420726571756972656420746f206b65657020616e206163636f756e74206f70656e2e204d5553542042452047524541544552205448414e205a45524f2120496620796f75202a7265616c6c792a206e65656420697420746f206265207a65726f2c20796f752063616e20656e61626c652074686520666561747572652060696e7365637572655f7a65726f5f65646020666f7220746869732070616c6c65742e20486f77657665722c20796f7520646f20736f20617420796f7572206f776e207269736b3a20746869732077696c6c206f70656e2075702061206d616a6f7220446f5320766563746f722e20496e206361736520796f752068617665206d756c7469706c6520736f7572636573206f662070726f7669646572207265666572656e6365732c20796f75206d617920616c736f2067657420756e6578706563746564206265686176696f757220696620796f7520736574207468697320746f207a65726f2e20426f74746f6d206c696e653a20446f20796f757273656c662061206661766f757220616e64206d616b65206974206174206c65617374206f6e65214578697374656e7469616c4465706f73697420546865206d6178696d756d206e756d626572206f66206c6f636b7320746861742073686f756c64206578697374206f6e20616e206163636f756e742e204e6f74207374726963746c7920656e666f726365642c20627574207573656420666f722077656967687420657374696d6174696f6e2e4d61784c6f636b7320546865206d6178696d756d206e756d626572206f66206e616d656420726573657276657320746861742063616e206578697374206f6e20616e206163636f756e742e4d6178526573657276657320546865206d6178696d756d206e756d626572206f6620696e646976696475616c20667265657a65206c6f636b7320746861742063616e206578697374206f6e20616e206163636f756e7420617420616e792074696d652e4d6178467265657a657362616c616e63657300f37511000800000043616c6c49436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e0000000009761100430000004572726f7254686520604572726f726020656e756d206f6620746869732070616c6c65742e0000005d7611002000000056657374696e6742616c616e63654c69717569646974795265737472696374696f6e73496e73756666696369656e7442616c616e6365457870656e646162696c6974794578697374696e6756657374696e675363686564756c65446561644163636f756e74546f6f4d616e795265736572766573546f6f4d616e79486f6c6473546f6f4d616e79467265657a657349737375616e6365446561637469766174656444656c74615a65726f4576656e7454686520604576656e746020656e756d206f6620746869732070616c6c65740000377711001f000000456e646f776564447573744c6f73745472616e7366657242616c616e63655365745265736572766564556e72657365727665645265736572766552657061747269617465644465706f7369745769746864726177536c61736865644d696e7465644275726e656453757370656e646564526573746f726564557067726164656449737375656452657363696e6465644c6f636b6564556e6c6f636b656446726f7a656e546861776564546f74616c49737375616e6365466f726365647374727563742047656e65736973436f6e666967ff0100000400000004000000e2010000ff01000004000000040000002e010000ff01000004000000040000005a0100002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f747269652f7372632f6e6f64655f636f6465632e72732f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6c6f6f6b75702e727300c57811005e000000460000001e000000ff0100000c0000000400000000020000010200005765206172652063616368696e67206120604e6f64654f776e65643a3a56616c75656020666f7220612076616c7565206e6f6465206861736820616e64207468697320636163686564206e6f64652068617320616c7761797320646174612061747461636865643b20716564c57811005e00000079000000160000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6962626c652f6d6f642e72730000c479110062000000490000001c000000ff01000014000000040000000202000003020000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64653a2000004c7a11002a000000604e6f64654f776e65643a3a56616c7565602063616e206e6f742062652072656163686564206279207573696e67207468652068617368206f662061206e6f64652e20604e6f64654f776e65643a3a56616c756560206973206f6e6c7920636f6e7374727563746564207768656e206c6f6164696e6720612076616c756520696e746f206d656d6f72792c207768696368206e6565647320746f2068617665206120646966666572656e742068617368207468616e20616e79206e6f64653b2071656400807a1100c3000000c57811005e000000d00200001900000053746f7261676556657273696f6e0000ff010000040000000400000012010000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64653a204e6f20657874656e73696f6e20636f6465632e0000007c7b11003d0000006078110065000000dd000000090000006078110065000000720000003400000060781100650000007b0000003200000060781100650000009e000000340000006078110065000000d60000000900000060781100650000000c0100000f00000060781100650000000501000029000000a86c1100000000006120646566656e73697665206661696c75726520686173206265656e207472696767657265643b20706c65617365207265706f72742074686520626c6f636b206e756d6265722061742068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f6973737565733c7c11007800000072756e74696d653a3a646566656e736976652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f737570706f72742f7372632f7472616974732f6d6973632e72736672616d655f737570706f72743a3a7472616974733a3a6d69736376616c69646174655f72756e74696d655f63616c6c20004d7d1100160000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f7375627374726174655f746573745f70616c6c65742e72737375627374726174655f746573745f70616c6c65747375627374726174655f746573745f72756e74696d653a3a7375627374726174655f746573745f70616c6c65740000002900000029000000490000002900000029000000290000004a000000290000002900000029000000290000002900000029000000290000002100000009000000090000002900000029000000290000002900000011000000426c6f636b73705f72756e74696d653a3a67656e657269633a3a626c6f636b48656164657245787472696e73696373705f72756e74696d653a3a67656e657269633a3a6865616465724e756d626572486173682f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e617669676174652e727300cf7e110084000000ae00000024000000556e636865636b656445787472696e736963282c20290000647f110013000000777f110002000000797f1100010000005065724469737061746368436c6173736672616d655f737570706f72743a3a6469737061746368544e6f6e65556e636865636b656445787472696e73696373705f72756e74696d653a3a67656e657269633a3a756e636865636b65645f65787472696e7369634164647265737343616c6c5369676e61747572654578747261004e756d626572206f6620646967657374206974656d73206d757374206d6174636820746861742063616c63756c617465642e000014801100320000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f6578656375746976652f7372632f6c69622e72730000508011005e000000360300000900000053746f7261676520726f6f74206d757374206d6174636820746861742063616c63756c617465642ec080110028000000508011005e00000044030000090000005472616e73616374696f6e207472696520726f6f74206d7573742062652076616c69642e0081110024000000508011005e0000004603000009000000446967657374206974656d206d757374206d6174636820746861742063616c63756c617465642e003c81110027000000508011005e0000003e0300000d0000003a206120646566656e73697665206661696c75726520686173206265656e207472696767657265643b20706c65617365207265706f72742074686520626c6f636b206e756d6265722061742068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f69737375657300007e8111007800000072756e74696d653a3a646566656e736976656672616d655f6578656375746976650000007c7e1100000000007c811100020000006e756d5f696e686572656e7473203d3d206e756d5f65787472696e7369637300348211001f000000506172656e7420686173682073686f756c642062652076616c69642e5c8211001c000000508011005e0000007102000009000000508011005e000000b7020000110000007c7e1100000000004163636f756e744461746170616c6c65745f62616c616e6365733a3a747970657342616c616e636542616c616e63654c6f636b5265736572766544617461526573657276654964656e7469666965722f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f73746174652d6d616368696e652f7372632f747269655f6261636b656e645f657373656e63652e727300f782110078000000a20100001b0000000902000000000000010000000a0200000b0200000c0200000d0200000902000000000000010000000e0200000f02000045717569766f636174696f6e50726f6f6673705f636f6e73656e7375735f736c6f74734964000000cf7e110084000000c700000027000000cf7e110084000000170200002f000000cf7e110084000000a200000024000000536f6d65100200000400000004000000110200003c7761736d3a73747269707065643e00140200000800000008000000150200001402000008000000080000001602000014020000080000000800000017020000180200000100000001000000190200001402000010000000080000001a0200001402000010000000080000001b0200008c841100000000001c02000008000000040000001d0200001e0200001f0200002002000021020000220200002302000024020000203d0000c084110001000000c1841100010000006d657373616765008c84110000000000c1841100010000001c0200000800000004000000250200001c020000180000000400000026020000417474656d7074656420746f2072656769737465722061206044656661756c7443616c6c7369746560207468617420616c7265616479206578697374732120546869732077696c6c20636175736520616e20696e66696e697465206c6f6f70207768656e20617474656d7074696e6720746f20726561642066726f6d207468652063616c6c736974652063616368652e2054686973206973206c696b656c792061206275672120596f752073686f756c64206f6e6c79206e65656420746f2063616c6c206044656661756c7443616c6c736974653a3a726567697374657260206f6e636520706572206044656661756c7443616c6c73697465602e000c851100fb0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f74726163696e672d636f72652d302e312e33322f7372632f63616c6c736974652e72730000001086110065000000bd0100000d00000027020000180000000400000026020000280200000000000001000000290200002a0200002b0200002c0200002d0200002e0200002f02000030020000310200003102000031020000320200003302000034020000350200003602000037020000040000000400000038020000370200000400000004000000380200002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f74726163696e672d636f72652d302e312e33322f7372632f6669656c642e7273000004871100620000000403000009000000617373657274696f6e206661696c65643a20696e646578203c3d206c656e2f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f736d616c6c7665632d312e31332e322f7372632f6c69622e72730000968711005c000000ef060000090000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6962626c652f6d6f642e727300000488110062000000490000001c0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6962626c652f6e6962626c65736c6963652e72730000788811006a0000003f0000001b000000788811006a0000005000000036000000788811006a000000560000003b000000788811006a0000008f0000002a000000788811006a000000900000002b000000788811006a000000990000004b000000788811006a0000009900000031000000788811006a000000af00000020000000788811006a000000ad00000029000000788811006a000000f400000018000000788811006a000000f600000018000000788811006a000000f60000003d0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6962626c652f6e6962626c657665632e7273a48911006800000035000000320000006c656e20213d20302073696e6365206c656e2025203220213d20303b20696e6e6572206861732061206c61737420656c656d656e743b207165640000a48911006800000042000000120000006c656e20213d20303b20696e6e657220686173206c61737420656c656d3b207165640000a4891100680000004d00000025000000a489110068000000690000003d000000a4891100680000007200000019000000a4891100680000007400000019000000a4891100680000007400000048000000a4891100680000008500000030000000a4891100680000008500000048000000a4891100680000008700000036000000a4891100680000009c00000043000000a4891100680000009e0000001b000000a4891100680000009f0000004000000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c7565003902000000000000010000003a0200002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f73796e632e72730000788b11006e000000b0070000290000004c61796f75744572726f722f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f7472696564626d75742e7273038c1100610000004a08000032000000038c1100610000004a080000100000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6962626c652f6d6f642e72730000848c110062000000880000001d000000848c1100620000007f00000022000000848c1100620000007e00000036000000848c1100620000007e00000047000000848c110062000000870000002f000000848c110062000000870000004400000041726320636f756e746572206f766572666c6f77488d1100140000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f73796e632e72730000648d11006e0000002e0600000d000000488d110000000000648d11006e000000dc0a00000d00000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c7565003c02000000000000010000003d020000648d11006e00000062070000290000004c61796f757473697a6500003e02000004000000040000003f020000616c69676e0000003e0200000400000004000000400200004c61796f75744572726f722f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6f64652e727300878e11005c000000bc01000042000000878e11005c000000bd01000046000000878e11005c000000da01000026000000878e11005c000000ed0100003c000000878e11005c000000ee0100003800000043617061636974794f766572666c6f77416c6c6f634572726c61796f757400004102000004000000040000004202000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c756500410200000800000004000000430200002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f736d616c6c7665632d312e31332e322f7372632f6c69622e7273a08f11005c000000520100002e0000006361706163697479206f766572666c6f77000000a08f11005c0000004101000036000000a08f11005c000000ce0400000e000000617373657274696f6e206661696c65643a206e65775f636170203e3d206c656ea08f11005c000000990400000d0000000041f0a0c6000b8c033300000000000000000000000000000000000000000000000000000054391000543910002a00000093000000000000009400000000000000950000000000000096000000000000009700000000000000980000000000000099000000000000009a000000000000009b000000000000009c000000000000009d000000000000009e000000000000009f00000000000000a000000000000000a100000000000000a200000000000000a300000000000000a400000000000000a500000000000000a600000000000000a700000000000000a800000000000000a900000000000000aa00000000000000ab00000000000000ac00000000000000ad00000000000000ae00000000000000af00000000000000b000000000000000b100000000000000b200000000000000b300000000000000b400000000000000b500000000000000b600000000000000b700000000000000b800000000000000b900000000000000ba00000000000000bb00000000000000f012110000000000ff00000000000000e486110098861100050000000041fca3c6000b1c0000000000000000000000000000000000000000000000000000000000330f72756e74696d655f76657273696f6e10746573742c7061726974792d74657374010000000200000002000000000100000001009d010c72756e74696d655f61706973df6acb689907609b0500000037e397fc7c91f5e402000000d2bc9897eed08f150300000040fe3ad401f8959a06000000bc9d89904f5b923f01000000c6e9a76309f39b0902000000dd718d5cc53262d401000000cbca25e39f14238702000000f78b278be53f454c02000000ab3c0572291feb8b01000000ed99c5acb25eedf503000000fbc577b9d747efd60100000000e2ca0c046e616d6501acca0c8b0e001c6578745f73746f726167655f617070656e645f76657273696f6e5f31011b6578745f73746f726167655f636c6561725f76657273696f6e5f3102226578745f73746f726167655f636c6561725f7072656669785f76657273696f6e5f3203286578745f73746f726167655f636f6d6d69745f7472616e73616374696f6e5f76657273696f6e5f3104196578745f73746f726167655f6765745f76657273696f6e5f31051e6578745f73746f726167655f6e6578745f6b65795f76657273696f6e5f31061a6578745f73746f726167655f726561645f76657273696f6e5f31072a6578745f73746f726167655f726f6c6c6261636b5f7472616e73616374696f6e5f76657273696f6e5f31081a6578745f73746f726167655f726f6f745f76657273696f6e5f3209196578745f73746f726167655f7365745f76657273696f6e5f310a276578745f73746f726167655f73746172745f7472616e73616374696f6e5f76657273696f6e5f310b206578745f68617368696e675f626c616b65325f3132385f76657273696f6e5f310c206578745f68617368696e675f626c616b65325f3235365f76657273696f6e5f310d1e6578745f68617368696e675f74776f785f3132385f76657273696f6e5f310e1d6578745f68617368696e675f74776f785f36345f76657273696f6e5f310f226578745f6f6666636861696e5f696e6465785f636c6561725f76657273696f6e5f3110206578745f6f6666636861696e5f696e6465785f7365745f76657273696f6e5f3111236578745f63727970746f5f65636473615f67656e65726174655f76657273696f6e5f3112266578745f63727970746f5f65636473615f7075626c69635f6b6579735f76657273696f6e5f31131f6578745f63727970746f5f65636473615f7369676e5f76657273696f6e5f3114216578745f63727970746f5f65636473615f7665726966795f76657273696f6e5f3215256578745f63727970746f5f656432353531395f67656e65726174655f76657273696f6e5f3116286578745f63727970746f5f656432353531395f7075626c69635f6b6579735f76657273696f6e5f3117216578745f63727970746f5f656432353531395f7369676e5f76657273696f6e5f3118236578745f63727970746f5f656432353531395f7665726966795f76657273696f6e5f3119256578745f63727970746f5f737232353531395f67656e65726174655f76657273696f6e5f311a286578745f63727970746f5f737232353531395f7075626c69635f6b6579735f76657273696f6e5f311b216578745f63727970746f5f737232353531395f7369676e5f76657273696f6e5f311c236578745f63727970746f5f737232353531395f7665726966795f76657273696f6e5f321d296578745f6f6666636861696e5f7375626d69745f7472616e73616374696f6e5f76657273696f6e5f311e256578745f7472616e73616374696f6e5f696e6465785f696e6465785f76657273696f6e5f311f196578745f6c6f6767696e675f6c6f675f76657273696f6e5f31201f6578745f6c6f6767696e675f6d61785f6c6576656c5f76657273696f6e5f31211c6578745f616c6c6f6361746f725f667265655f76657273696f6e5f31221e6578745f616c6c6f6361746f725f6d616c6c6f635f76657273696f6e5f3123286578745f64656661756c745f6368696c645f73746f726167655f726561645f76657273696f6e5f3124276578745f64656661756c745f6368696c645f73746f726167655f7365745f76657273696f6e5f31252a6578745f747269655f626c616b65325f3235365f6f7264657265645f726f6f745f76657273696f6e5f32261c6578745f6d6973635f7072696e745f6865785f76657273696f6e5f31271d6578745f6d6973635f7072696e745f757466385f76657273696f6e5f3128226578745f6d6973635f72756e74696d655f76657273696f6e5f76657273696f6e5f31291a5f5f727573745f616c6c6f635f6572726f725f68616e646c65722a305f5a4e34636f726533666d743557726974653977726974655f666d7431376861663163633133303635343162653038452b4c5f5a4e34636f726533707472343264726f705f696e5f706c616365244c5424616c6c6f632e2e737472696e672e2e537472696e672447542431376830336135366130626537376637613432452c5d5f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f73747231376830306136663730663763613531633235452d5f5f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e577269746524475424313077726974655f6368617231376836656230613834393730363936353764452e385f5a4e35616c6c6f63377261775f766563313763617061636974795f6f766572666c6f7731376833643832313234636131666431396363452f595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686261373865343038643263393666336545304b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376838393162626637303462336364333539452e6c6c766d2e31393739383737353732323834393835383834314c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f70757368313768626465353131306261356161353636634532375f5a4e35616c6c6f6335616c6c6f63313868616e646c655f616c6c6f635f6572726f72313768633962373561656666333933346231344533615f5a4e34636f726533707472333764726f705f696e5f706c616365244c5424636f72652e2e666d742e2e4572726f722447542431376866616235626136623938346139656434452e6c6c766d2e313039383239393330343532323938373935313534665f5a4e34636f726533707472343264726f705f696e5f706c616365244c5424616c6c6f632e2e737472696e672e2e537472696e672447542431376830336135366130626537376637613432452e6c6c766d2e3130393832393933303435323239383739353135356c5f5a4e35335f244c5424636f72652e2e666d742e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863313334333061303064613965336232452e6c6c766d2e3130393832393933303435323239383739353135367c5f5a4e36395f244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832393637613332326332613165663338452e6c6c766d2e313039383239393330343532323938373935313537095f5f72646c5f6f6f6d38365f5a4e35616c6c6f6333666d7436666f726d61743132666f726d61745f696e6e6572313768313738663061633662316238316561614539775f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f73747231376830306136663730663763613531633235452e6c6c766d2e31303938323939333034353232393837393531353a795f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e577269746524475424313077726974655f6368617231376836656230613834393730363936353764452e6c6c766d2e31303938323939333034353232393837393531353b375f5a4e36626c616b65323134426c616b653262566172436f726538636f6d707265737331376862373461393335333133656536623032453c305f5a4e3462733538366465636f646531316465636f64655f696e746f31376832366665303063303966626636623863453d575f5a4e34636f726533707472353364726f705f696e5f706c616365244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f722447542431376832613236653265366332366232333665453e625f5a4e36395f244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832393637613332326332613165663338453f7a5f5a4e39325f244c542462797465732e2e62797465732e2e4279746573247532302461732475323024636f72652e2e636f6e766572742e2e46726f6d244c5424616c6c6f632e2e7665632e2e566563244c542475382447542424475424244754243466726f6d3137683765643461346138383266613761633045404a5f5a4e35627974657335627974657331327374617469635f636c6f6e6531376833323130386566643438323766363666452e6c6c766d2e3334343432333333353235373333383435333141325f5a4e35627974657335627974657331337374617469635f746f5f7665633137683034383536303131313538303561363745424e5f5a4e35627974657335627974657331367374617469635f69735f756e6971756531376836633034666662366437323062666139452e6c6c766d2e3334343432333333353235373333383435333143495f5a4e35627974657335627974657331317374617469635f64726f7031376864643031336537396536363530303731452e6c6c766d2e33343434323333333532353733333834353331443a5f5a4e356279746573356279746573323170726f6d6f7461626c655f6576656e5f636c6f6e65313768343363333339313537326466383137614545365f5a4e35627974657335627974657331377368616c6c6f775f636c6f6e655f7665633137683061656661613439613631353238316145463b5f5a4e356279746573356279746573323270726f6d6f7461626c655f6576656e5f746f5f766563313768376337343431613235356465346530354547375f5a4e35627974657335627974657331387368617265645f746f5f7665635f696d706c313768326566333265646563363862616531384548395f5a4e356279746573356279746573323070726f6d6f7461626c655f6576656e5f64726f70313768396532366232633039653138383164384549395f5a4e356279746573356279746573323070726f6d6f7461626c655f6f64645f636c6f6e6531376861396666386432383230363239316630454a3a5f5a4e356279746573356279746573323170726f6d6f7461626c655f6f64645f746f5f76656331376839616166303361376137383561363530454b385f5a4e356279746573356279746573313970726f6d6f7461626c655f6f64645f64726f7031376837303033346434313735363163356530454c395f5a4e356279746573356279746573323070726f6d6f7461626c655f69735f756e6971756531376831333537643837376665396133633162454d4a5f5a4e35627974657335627974657331327368617265645f636c6f6e6531376862633730623138623230636237393064452e6c6c766d2e333434343233333335323537333338343533314e4b5f5a4e35627974657335627974657331337368617265645f746f5f76656331376839396139623662356236303333326238452e6c6c766d2e333434343233333335323537333338343533314f355f5a4e35627974657335627974657331367368617265645f69735f756e69717565313768383462326136323837323563336466644550495f5a4e35627974657335627974657331317368617265645f64726f7031376863383731383462666264363461336363452e6c6c766d2e3334343432333333353235373333383435333151235f5a4e3562797465733561626f7274313768616536373034383432313466636561384552305f5a4e34636f7265336e756d313466726f6d5f7374725f72616469783137686662343361316134393034616533366445534e5f5a4e34636f726533666d74336e756d313470617273655f7536345f696e746f31376866333161333061663037626334346262452e6c6c766d2e313832363532393630363530313031383537353054475f5a4e34636f726533666d74336e756d38666d745f7531323831376836663831323232666336353939343036452e6c6c766d2e3138323635323936303635303130313835373530555c5f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e42696e6172792475323024666f72247532302475382447542433666d74313768653039663762636130333134363631304556705f5a4e34636f726533707472353264726f705f696e5f706c616365244c5424636f72652e2e666d742e2e6275696c646572732e2e506164416461707465722447542431376833383639396237306233346438623161452e6c6c766d2e313134313435373138353633303837323235383957535f5a4e34636f72653463686172376d6574686f647332325f244c5424696d706c2475323024636861722447542431366573636170655f64656275675f657874313768313166356433366163663835653436654558565f5a4e35375f244c5424636f72652e2e666d742e2e417267756d656e7473247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768636532626463316362313866386538364559265f5a4e34636f726533666d7435777269746531376833316239306639333939316161316166455a585f5a4e35395f244c5424636f72652e2e666d742e2e417267756d656e7473247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376832323263633935653933313931633065455b385f5a4e34636f726533666d7439466f726d617474657231327061645f696e74656772616c31376865363062666135303835353662663637455c465f5a4e34636f726533666d7439466f726d617474657231327061645f696e74656772616c313277726974655f70726566697831376866363037623038356465323433646430455d2e5f5a4e34636f726533666d7439466f726d61747465723370616431376839643066373134613465333036653537455e3f5f5a4e34636f726533666d7439466f726d617474657231397061645f666f726d61747465645f706172747331376866613566626361396534663366643739455f415f5a4e34636f726533666d7439466f726d6174746572323177726974655f666f726d61747465645f7061727473313768633039663339346136333039306233354560465f5a4e34636f726533666d7439466f726d6174746572323664656275675f7374727563745f6669656c64335f66696e697368313768376531303563333761333334303632364561485f5a4e34335f244c5424626f6f6c247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d74313768386633653662656130636263643036664562455f5a4e34305f244c5424737472247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768373436386136383939373866633230654563465f5a4e34315f244c542463686172247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768383531393237346430323466633533654564485f5a4e34335f244c542463686172247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d74313768613762373831393835373164623434344565475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768346364336130343235653439383736384566475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768646238353166633131656430626537364567495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d74313768346164326330366665643939366634394568455f5a4e33365f244c542454247532302461732475323024636f72652e2e616e792e2e416e792447542437747970655f6964313768376665643762343638326265623161384569665f5a4e37335f244c5424636f72652e2e70616e69632e2e70616e69635f696e666f2e2e50616e6963496e666f247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376837363235313165623539666437646662456a395f5a4e34636f7265336e756d366269676e756d384269673332783430386d756c5f706f773231376832333939653931373730303134613065456b3c5f5a4e34636f7265336e756d366269676e756d38426967333278343031306d756c5f64696769747331376838323466373032346336373365353031456c3b5f5a4e34636f7265336e756d37666c743264656331376469676974735f746f5f6465635f73747231376861643333386363303862663761646464456d455f5a4e34636f726533666d7435666c6f61743239666c6f61745f746f5f646563696d616c5f636f6d6d6f6e5f657861637431376864626231396163396638623139653135456e485f5a4e34636f726533666d7435666c6f61743332666c6f61745f746f5f646563696d616c5f636f6d6d6f6e5f73686f727465737431376832356165303934633231343635623632456f4c5f5a4e34636f726533666d7435666c6f61743336666c6f61745f746f5f6578706f6e656e7469616c5f636f6d6d6f6e5f73686f72746573743137686634343439643662333438626433336545705e5f5a4e34636f726533666d7435666c6f617435305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230246636342447542433666d74313768313334356638336136653937306362614571605f5a4e34636f726533666d7435666c6f617435325f244c5424696d706c2475323024636f72652e2e666d742e2e446973706c61792475323024666f7224753230246636342447542433666d74313768323431376263646466626264396133304572395f5a4e34636f726533737472377061747465726e31315374725365617263686572336e65773137683635623239366636353436383834633645734a5f5a4e34636f726537756e69636f64653132756e69636f64655f6461746131356772617068656d655f657874656e64366c6f6f6b75703137683361303135363166376438363565343645743e5f5a4e34636f726533707472323864726f705f696e5f706c616365244c542424524624753634244754243137686632383034353838343339356361626245757a5f5a4e34636f726533707472383864726f705f696e5f706c616365244c5424636f72652e2e70616e69632e2e70616e69635f696e666f2e2e50616e6963496e666f2e2e696e7465726e616c5f636f6e7374727563746f722e2e4e6f5061796c6f616424475424313768393561663934356261646666663164354576305f5a4e34636f72653970616e69636b696e673970616e69635f666d743137683137656265323637393339346434363345773a5f5a4e34636f72653970616e69636b696e67313870616e69635f6e6f756e77696e645f666d743137683162623235323536323563626365336345782c5f5a4e34636f72653970616e69636b696e673570616e69633137683031333364623535393431663137393945793a5f5a4e34636f72653970616e69636b696e67313870616e69635f626f756e64735f636865636b31376863346539323535633831643234393765457a355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c656431376837343364343064623063656262303634457b3b5f5a4e34636f72653970616e69636b696e6731396173736572745f6661696c65645f696e6e657231376835316661346564363635633865663539457c335f5a4e34636f72653373747238636f6e76657274733966726f6d5f7574663831376830623066636266373533313961663332457d365f5a4e34636f72653373747235636f756e743134646f5f636f756e745f636861727331376833663034386438366363316436653337457e4a5f5a4e34636f726533666d74336e756d33696d7037666d745f75363431376837616563323263663137613936363734452e6c6c766d2e31313731353539313634323639313336333630377f615f5a4e34636f726533666d74336e756d33696d7035315f244c5424696d706c2475323024636f72652e2e666d742e2e446973706c61792475323024666f72247532302475382447542433666d7431376839663264653863653532666165626465458001625f5a4e34636f726533666d74336e756d33696d7035325f244c5424696d706c2475323024636f72652e2e666d742e2e446973706c61792475323024666f7224753230247533322447542433666d7431376862396262366262616238333065376239458101625f5a4e34636f726533666d74336e756d33696d7035325f244c5424696d706c2475323024636f72652e2e666d742e2e446973706c61792475323024666f7224753230246936342447542433666d7431376863663861323531343166393861333230458201625f5a4e34636f726533666d74336e756d33696d7035325f244c5424696d706c2475323024636f72652e2e666d742e2e446973706c61792475323024666f7224753230247536342447542433666d7431376833313364363335616231313038356364458301565f5a4e34636f726533707472353264726f705f696e5f706c616365244c5424636f72652e2e666d742e2e6275696c646572732e2e506164416461707465722447542431376833383639396237306233346438623161458401645f5a4e37315f244c5424636f72652e2e6f70732e2e72616e67652e2e52616e6765244c542449647824475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376862343735376365333866656262633462458501595f5a4e36305f244c5424636f72652e2e63656c6c2e2e426f72726f774572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768323332616130653564616239313365364586015c5f5a4e36335f244c5424636f72652e2e63656c6c2e2e426f72726f774d75744572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863353232313733616163616538656532458701395f5a4e34636f72653463656c6c323270616e69635f616c72656164795f626f72726f77656431376837303439376565663966656636633765458801415f5a4e34636f72653463656c6c333070616e69635f616c72656164795f6d757461626c795f626f72726f77656431376836326239396535336130613565643564458901325f5a4e34636f726536726573756c743133756e777261705f6661696c656431376833353261353332373338663466376236458a01675f5a4e36385f244c5424636f72652e2e666d742e2e6275696c646572732e2e50616441646170746572247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f73747231376863356532376635376237333137666235458b01695f5a4e36385f244c5424636f72652e2e666d742e2e6275696c646572732e2e50616441646170746572247532302461732475323024636f72652e2e666d742e2e577269746524475424313077726974655f6368617231376836666333306633356433313038323264458c013c5f5a4e34636f726533666d74386275696c6465727331314465627567537472756374356669656c6431376830363965386366316632636331343732458d013b5f5a4e34636f726533666d74386275696c64657273313044656275675475706c65356669656c6431376833316165313537643739313566313730458e01395f5a4e34636f726533666d74386275696c646572733944656275674c69737435656e74727931376866346338656232383861636362376565458f01305f5a4e34636f726533666d743557726974653977726974655f666d7431376834383634653635626533623334313461459001425f5a4e34636f7265336e756d37666c743264656338737472617465677936647261676f6e396d756c5f706f77313031376838636138613634343339646563343638459101495f5a4e34636f7265336e756d37666c743264656338737472617465677936647261676f6e3135666f726d61745f73686f727465737431376833666666313032623834656633373637459201465f5a4e34636f7265336e756d37666c743264656338737472617465677936647261676f6e3132666f726d61745f657861637431376832663931616639666233353432336333459301535f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e63653963616c6c5f6f6e636531376832363432343863326539396266316539452e6c6c766d2e313134333339373135393234303630383036389401445f5a4e34636f726535736c69636535696e6465783236736c6963655f73746172745f696e6465785f6c656e5f6661696c31376836343839663765366561306337333166459501425f5a4e34636f726535736c69636535696e6465783234736c6963655f656e645f696e6465785f6c656e5f6661696c31376838376562323035343262366635616166459601405f5a4e34636f726535736c69636535696e6465783232736c6963655f696e6465785f6f726465725f6661696c31376835373830636663326565346438646463459701325f5a4e34636f7265337374723136736c6963655f6572726f725f6661696c31376834626132313961633437626533396631459801355f5a4e34636f7265337374723139736c6963655f6572726f725f6661696c5f7274313768613266313161653166643639613535644599014d5f5a4e34636f726537756e69636f6465397072696e7461626c6535636865636b31376837346365663562363466373538633334452e6c6c766d2e313134333339373135393234303630383036389a013c5f5a4e34636f726537756e69636f6465397072696e7461626c65313269735f7072696e7461626c6531376832663963336261373564323438383466459b01755f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247533322447542433666d7431376830396334333131313138623438393031452e6c6c766d2e313139343530303336353038343732313030329c0183015f5a4e37375f244c5424636f72652e2e6e756d2e2e6e6f6e7a65726f2e2e4e6f6e5a65726f244c54247573697a6524475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376831323163323638626262326164613534452e6c6c766d2e313139343530303336353038343732313030329d014c5f5a4e34636f7265336e756d37666c74326465633873747261746567793567726973753139666f726d61745f73686f72746573745f6f707431376832303531386436386635656662396139459e01495f5a4e34636f7265336e756d37666c74326465633873747261746567793567726973753136666f726d61745f65786163745f6f707431376866396436363961303335663462643461459f01595f5a4e34636f7265336e756d37666c74326465633873747261746567793567726973753136666f726d61745f65786163745f6f70743134706f737369626c795f726f756e643137683932623532386134663563333831326345a001325f5a4e34636f7265366f7074696f6e3133756e777261705f6661696c65643137686434373562333534393730656433303945a101325f5a4e34636f7265366f7074696f6e31336578706563745f6661696c65643137683761373162626562623861336138346145a201655f5a4e34636f726535736c69636532395f244c5424696d706c24753230242475356224542475356424244754243135636f70795f66726f6d5f736c69636531376c656e5f6d69736d617463685f6661696c3137686161396465386631393031353636383445a3015d5f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c33753332356669656c6431364669656c64456c656d656e74323632353861735f62797465733137686366646330653336623538323836656145a40190015f5a4e3136637572766532353531395f64616c656b356669656c6438315f244c5424696d706c2475323024637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e6669656c642e2e4669656c64456c656d656e7432363235244754243132737172745f726174696f5f693137683461626638306435383439626462316445a5017b5f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c33753332356669656c6431364669656c64456c656d656e743236323531327371756172655f696e6e657231376832393661646431346662313362343863452e6c6c766d2e33313435303338383738363536343237363236a601d6015f5a4e3138345f244c542424524624637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e6669656c642e2e4669656c64456c656d656e7432363235247532302461732475323024636f72652e2e6f70732e2e61726974682e2e4d756c244c542424524624637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e6669656c642e2e4669656c64456c656d656e74323632352447542424475424336d756c3137686531633862376162376238646465333245a7015a5f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c33753332356669656c6431364669656c64456c656d656e743236323535706f77326b3137683333323961306331386633323239396145a801d6015f5a4e3138345f244c542424524624637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e6669656c642e2e4669656c64456c656d656e7432363235247532302461732475323024636f72652e2e6f70732e2e61726974682e2e537562244c542424524624637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e6669656c642e2e4669656c64456c656d656e74323632352447542424475424337375623137686366623936343361316632393537623245a9018b015f5a4e3130395f244c542424524624637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e6669656c642e2e4669656c64456c656d656e7432363235247532302461732475323024636f72652e2e6f70732e2e61726974682e2e4e656724475424336e65673137683836363239633063393965666636346245aa01605f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c33753332356669656c6431364669656c64456c656d656e7432363235313066726f6d5f62797465733137683637646232333431303863376333343845ab01585f5a4e3136637572766532353531395f64616c656b367363616c6172365363616c61723672656475636531376837616130396331613530383062303237452e6c6c766d2e3137393331343330323733373936393938373634ac0181015f5a4e3136637572766532353531395f64616c656b367363616c617237345f244c5424696d706c2475323024637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e7363616c61722e2e5363616c6172323924475424347061636b3137686133393036643737393836646631636545ad01505f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c33753332367363616c6172385363616c61723239337375623137683137393638663832636236353661396645ae01585f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c33753332367363616c6172385363616c61723239313066726f6d5f62797465733137686234626232333062633331333032376345af01fd015f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c313263757276655f6d6f64656c733137365f244c5424696d706c2475323024636f72652e2e6f70732e2e61726974682e2e416464244c542424524624637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e63757276655f6d6f64656c732e2e50726f6a6563746976654e69656c73506f696e74244754242475323024666f72247532302424524624637572766532353531395f64616c656b2e2e656477617264732e2e45647761726473506f696e7424475424336164643137686666333438303231386330353465363245b0016e5f5a4e38315f244c5424637572766532353531395f64616c656b2e2e656477617264732e2e45647761726473506f696e74247532302461732475323024636f72652e2e6f70732e2e61726974682e2e41646424475424336164643137683339646434643332326436393935383145b101545f5a4e3136637572766532353531395f64616c656b3972697374726574746f3139436f6d7072657373656452697374726574746f31306465636f6d70726573733137683463656465316565663362356534343245b2014c5f5a4e3136637572766532353531395f64616c656b3972697374726574746f313452697374726574746f506f696e7438636f6d70726573733137686465383739343065626437373931633245b301795f5a4e3136637572766532353531395f64616c656b3972697374726574746f313452697374726574746f506f696e743236656c6c696761746f725f72697374726574746f5f666c61766f7231376863383765363761373331636563663363452e6c6c766d2e3138313535353136363236333533343435313238b401575f5a4e3136637572766532353531395f64616c656b3972697374726574746f313452697374726574746f506f696e74313866726f6d5f756e69666f726d5f62797465733137683434383365323265616362353866373845b501755f5a4e38365f244c5424637572766532353531395f64616c656b2e2e72697374726574746f2e2e52697374726574746f506f696e74247532302461732475323024737562746c652e2e436f6e7374616e7454696d654571244754243563745f65713137686337646539363435646138333437393645b601465f5a4e3136637572766532353531395f64616c656b3972697374726574746f31306465636f6d707265737336737465705f313137683164393730623731313563313734366245b701465f5a4e3136637572766532353531395f64616c656b3972697374726574746f31306465636f6d707265737336737465705f323137683065653861323531646239393538346445b801465f5a4e31387061726974795f7363616c655f636f64656335636f6465633139656e636f64655f736c6963655f6e6f5f6c656e3137686231353637383563343936343739393145b9015d5f5a4e34636f726533707472353964726f705f696e5f706c616365244c54247363616c655f696e666f2e2e706f727461626c652e2e506f727461626c655265676973747279244754243137686364623134646231653164613836646645ba01ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683034653833336137323532393764633745bb01ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683462313431326235333839396533383345bc01ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683862643933623863303837633539653145bd01ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683831623061363765306436393534653045be01ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137686438396663646636333161393830333945bf01ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137686635633833616265306631623262346145c00185015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f697465723137683731393039303236643835396438613045c101a9015f5a4e31346672616d655f6d657461646174613132325f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c54246672616d655f6d657461646174612e2e52756e74696d654d657461646174615072656669786564244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c5424753824475424244754243466726f6d3137683737356566343935383737616665343645c2014c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376838336466636663303462336235303132452e6c6c766d2e3138303139373434373438303230313830383436c301595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686163313730333462386433613031616345c401f6015f5a4e35616c6c6f633131636f6c6c656374696f6e7335627472656536617070656e643137385f244c5424696d706c2475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4f776e65642443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c6561664f72496e7465726e616c24475424244754243962756c6b5f707573683137683235396539616534343864363030393045c50185015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137683362626131623361343562383339326545c60185015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137683364646265613164303537306637396545c70185015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137683564633737306237306666643266323745c80185015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137686562333033396561303761343465663045c901745f5a4e38365f244c5424616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683232376265663739626436356331303345ca01745f5a4e38365f244c5424616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686333376462356162363363666138323345cb018c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137683436343566616566316331366631623245cc01ae015f5a4e31346672616d655f6d6574616461746133763134315f3131365f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e50616c6c6574436f6e7374616e744d65746164617461244c542454244754242447542439656e636f64655f746f3137686233316434393131323239653565393145cd01ac015f5a4e31346672616d655f6d6574616461746133763134315f3131345f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e53746f72616765456e7472794d65746164617461244c542454244754242447542439656e636f64655f746f3137683534613532656633343633393832643045ce01435f5a4e31346672616d655f6d6574616461746133763134313852756e74696d654d65746164617461563134336e65773137686661383637613661666263623734343145cf0182015f5a4e39305f244c54246672616d655f6d657461646174612e2e7631342e2e50616c6c65744d657461646174612475323024617324753230247363616c655f696e666f2e2e72656769737472792e2e496e746f506f727461626c65244754243133696e746f5f706f727461626c653137686537613439306263303136633531636245d001a1015f5a4e31346672616d655f6d6574616461746133763134315f3130335f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e52756e74696d654d657461646174615631342447542439656e636f64655f746f3137683838653239666465373933633033633045d101a6015f5a4e31346672616d655f6d6574616461746133763134315f3130385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e50616c6c65744d65746164617461244c542454244754242447542439656e636f64655f746f3137683631313831316634663466323731346445d201ad015f5a4e31346672616d655f6d6574616461746133763134315f3131355f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e50616c6c657453746f726167654d65746164617461244c542454244754242447542439656e636f64655f746f3137686464666539313432646331326664356545d3019b015f5a4e31307363616c655f696e666f327479315f3130325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247363616c655f696e666f2e2e74792e2e54797065506172616d65746572244c542454244754242447542439656e636f64655f746f3137683365323165393033323465636362303445d401a2015f5a4e31307363616c655f696e666f327479366669656c6473315f3130325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247363616c655f696e666f2e2e74792e2e6669656c64732e2e4669656c64244c542454244754242447542439656e636f64655f746f3137686431313236616462353961386535643745d501a6015f5a4e31307363616c655f696e666f3274793776617269616e74315f3130355f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247363616c655f696e666f2e2e74792e2e76617269616e742e2e56617269616e74244c542454244754242447542439656e636f64655f746f3137683933376166663334623966323566343445d6019c015f5a4e31307363616c655f696e666f38706f727461626c65315f39385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247363616c655f696e666f2e2e706f727461626c652e2e506f727461626c65547970652447542439656e636f64655f746f3137683631383662646536323236346137376645d7013f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683637383730623630323462363833633345d80194015f5a4e31346672616d655f6d65746164617461315f39355f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e52756e74696d654d65746164617461244754243973697a655f68696e743137683234363333373636353463303162353545d901335f5a4e34636f726535736c69636534736f727431306d657267655f736f72743137683132646262386136303062393266616545da01425f5a4e34636f726535736c69636534736f72743235696e73657274696f6e5f736f72745f73686966745f6c6566743137686165383738336532656534363238643045db0193015f5a4e3131365f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137686561613831336538323664343561373745dc01645f5a4e36355f244c542424753562245424753564242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137683536343932353237376139353862663945dd01b0015f5a4e31346672616d655f6d6574616461746133763135315f3131385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631352e2e52756e74696d654170694d6574686f644d65746164617461244c542454244754242447542439656e636f64655f746f3137683531646264353632373935326536616345de0181015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686531636264666231613731366664663645df0181015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686431616434363739303737613065383445e001435f5a4e31346672616d655f6d6574616461746133763135313852756e74696d654d65746164617461563135336e65773137683035613339323630376565626238643645e10182015f5a4e39305f244c54246672616d655f6d657461646174612e2e7631352e2e437573746f6d4d657461646174612475323024617324753230247363616c655f696e666f2e2e72656769737472792e2e496e746f506f727461626c65244754243133696e746f5f706f727461626c653137683438326137373934313736306361386645e2018d015f5a4e3130305f244c54246672616d655f6d657461646174612e2e7631352e2e52756e74696d654170694d6574686f644d657461646174612475323024617324753230247363616c655f696e666f2e2e72656769737472792e2e496e746f506f727461626c65244754243133696e746f5f706f727461626c653137686634326430663564313764613464636345e30182015f5a4e39305f244c54246672616d655f6d657461646174612e2e7631352e2e50616c6c65744d657461646174612475323024617324753230247363616c655f696e666f2e2e72656769737472792e2e496e746f506f727461626c65244754243133696e746f5f706f727461626c653137686632313563383733646564386237303345e401a1015f5a4e31346672616d655f6d6574616461746133763135315f3130335f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631352e2e52756e74696d654d65746164617461563135244754243973697a655f68696e743137683734306265346164636163643564353645e501a1015f5a4e31346672616d655f6d6574616461746133763135315f3130335f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631352e2e52756e74696d654d657461646174615631352447542439656e636f64655f746f3137686439303738643136663363383232396445e601a6015f5a4e31346672616d655f6d6574616461746133763135315f3130385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631352e2e50616c6c65744d65746164617461244c542454244754242447542439656e636f64655f746f3137686566633362653338633266623432643745e701aa015f5a4e31346672616d655f6d6574616461746133763135315f3131325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631352e2e52756e74696d654170694d65746164617461244c542454244754242447542439656e636f64655f746f3137683766336333383032613938663239396145e801495f5a4e32396672616d655f6d657461646174615f686173685f657874656e73696f6e31324d657461646174614861736834686173683137683861383934363732376330366538623745e9019a015f5a4e32396672616d655f6d657461646174615f686173685f657874656e73696f6e315f38365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e4d6f64652447542439747970655f696e666f3137686464643638626163323937346132363345ea014c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376862386430393662663631323563313463452e6c6c766d2e3132393736303835353438343938383631353737eb014c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683364323163316434353435366436353945ec014b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376832313461356361366230373631643032452e6c6c766d2e32353638393138333230313631303133383939ed014c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683561333739303230313839313839336145ee014c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683633616434616535326336616366653945ef014c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686263393435633536383863363761623845f001595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683332663530636432636439373334663045f101595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686234393034653565663563316664376445f201595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686431633330636166626165343733383845f301595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686638663235393032643839633164303545f4018d015f5a4e31336672616d655f737570706f7274386469737061746368315f38305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e506179732447542439747970655f696e666f3137683933613030653461343866363835336545f50196015f5a4e31336672616d655f737570706f7274386469737061746368315f38395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e4469737061746368436c6173732447542439747970655f696e666f3137683162346138653234313337666231376345f60195015f5a4e31336672616d655f737570706f7274386469737061746368315f38385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e4469737061746368496e666f2447542439747970655f696e666f3137683330333965323338353137616531613545f701ad015f5a4e31336672616d655f737570706f72743674726169747336746f6b656e73346d697363315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e7472616974732e2e746f6b656e732e2e6d6973632e2e42616c616e63655374617475732447542439747970655f696e666f3137686664616465373732353836626238323645f801595f5a4e36305f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683864393031613030383666373938373245f901625f5a4e36395f244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686363656231383263633834363164346345fa016d5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c32316765745f7472616e73616374696f6e5f6c6576656c31376831653137366465656436393261346163452e6c6c766d2e33353936323533383937393435343238363335fb0180015f5a4e39385f244c54246672616d655f737570706f72742e2e73746f726167652e2e7472616e73616374696f6e616c2e2e53746f726167654c617965724775617264247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683538626338313333643830376138393345fc012e5f5a4e313161727261795f6279746573396279746573326865783137686631343332353634373665303466376445fd01465f5a4e31336672616d655f737570706f7274313664697370617463685f636f6e7465787436474c4f42414c365f5f696e69743137683464353735316263336661343564363845fe01ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683034656537646332633336366535343345ff01ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f6974657231376837643962306161376138613966306665458002ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137686366663138666363383630646666323345810285015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f697465723137683538313538623061376430616337633245820285015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137683364373530643366346539363039363045830295015f5a4e31307363616c655f696e666f35696d706c7339365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c542454244754242447542439747970655f696e666f31376837383362353261343933313939396431458402765f5a4e34636f726533707472353964726f705f696e5f706c616365244c54246672616d655f73797374656d2e2e6c696d6974732e2e56616c69646174696f6e4572726f72732447542431376837303864616334353033396330373037452e6c6c766d2e343136383139373135313634363230353530318502435f5a4e31326672616d655f73797374656d366c696d6974733132426c6f636b576569676874733876616c696461746531376833363762613931363631303861353462458602475f5a4e31326672616d655f73797374656d366c696d6974733139426c6f636b576569676874734275696c646572356275696c64313768626362643930313934363739316266614587028e015f5a4e31326672616d655f73797374656d366c696d697473315f38345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e6c696d6974732e2e426c6f636b4c656e6774682447542439747970655f696e666f31376832383036633662306365346434323264458802685f5a4e37355f244c54246672616d655f73797374656d2e2e6c696d6974732e2e56616c69646174696f6e4572726f7273247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863373336373966393830313039636266458902a0015f5a4e31326672616d655f73797374656d366c696d697473315f3130315f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f73797374656d2e2e6c696d6974732e2e57656967687473506572436c617373244754243973697a655f68696e7431376866343861306230666134313537633934458a0292015f5a4e31326672616d655f73797374656d366c696d697473315f38385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e6c696d6974732e2e57656967687473506572436c6173732447542439747970655f696e666f31376834396462643336346138323936343661458b029c015f5a4e31326672616d655f73797374656d366c696d697473315f39385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f73797374656d2e2e6c696d6974732e2e426c6f636b57656967687473244754243973697a655f68696e7431376834636435633431633038353437306339458c028f015f5a4e31326672616d655f73797374656d366c696d697473315f38355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e6c696d6974732e2e426c6f636b576569676874732447542439747970655f696e666f31376834626532376363613138613635646663458d02ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f6974657231376830383338346234346561366265666531458e02ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f6974657231376866376230336430643539393737306266458f02ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f6974657231376866383135386663353163396364656166459002a3015f5a4e31336672616d655f737570706f7274386469737061746368315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e5065724469737061746368436c617373244c542454244754242447542439747970655f696e666f31376830373666343234633562633634336534459102a3015f5a4e31336672616d655f737570706f7274386469737061746368315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e5065724469737061746368436c617373244c542454244754242447542439747970655f696e666f31376830633034303963316432393261366334459202795f5a4e31326672616d655f73797374656d315f37305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e50686173652447542439747970655f696e666f313768323562363666646130663631633337384593028a015f5a4e31326672616d655f73797374656d315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e4c61737452756e74696d6555706772616465496e666f2447542439747970655f696e666f313768316663333239646138343565363039344594024b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376864393665316435333530336665356563452e6c6c766d2e3233353739323532383235303835303038333495024c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f70757368313768393036613062636632323935613466374596024c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376866396663363665373938653436333731459702595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376834353232316161323963383438393835459802595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376861313265373761306232343831373731459902595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376862356230316132656562653565656466459a02445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376838323838373932303862343037393738459b02445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376865656463666165633630306137323236459c023f5f5a4e366b656363616b386b656363616b5f7031376863386161323430633639663264313561452e6c6c766d2e3339323632383034393030343233353234359d025f5f5a4e34636f726533707472333564726f705f696e5f706c616365244c54246c6f672e2e4e6f704c6f676765722447542431376835383965323339643935303930353363452e6c6c766d2e31303836323130343630343638393633323335399e02665f5a4e34335f244c54246c6f672e2e4e6f704c6f676765722475323024617324753230246c6f672e2e4c6f672447542437656e61626c656431376837626131363931643666333962303861452e6c6c766d2e31303836323130343630343638393633323335399f02625f5a4e34335f244c54246c6f672e2e4e6f704c6f676765722475323024617324753230246c6f672e2e4c6f6724475424336c6f6731376831333563643834666261313161316430452e6c6c766d2e3130383632313034363034363839363332333539a002645f5a4e34335f244c54246c6f672e2e4e6f704c6f676765722475323024617324753230246c6f672e2e4c6f672447542435666c75736831376834636339373639396636626565646331452e6c6c766d2e3130383632313034363034363839363332333539a102335f5a4e336c6f6731335f5f707269766174655f617069386c6f675f696d706c3137683233363835643934323362643030626545a202475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686334393539333131666462356162313345a3023d5f5a4e34636f726533707472323764726f705f696e5f706c616365244c5424245246247538244754243137683536353231383262643035383964353845a402355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683932633766366635313863616330373945a502385f5a4e366d65726c696e367374726f6265395374726f626531323838626567696e5f6f703137683935613138383161373539376632353245a6023a5f5a4e366d65726c696e31307472616e73637269707431305472616e736372697074336e65773137686630333838613739613261316435366545a702465f5a4e366d65726c696e31307472616e73637269707431305472616e7363726970743134617070656e645f6d6573736167653137686237386432613861626364316564616645a802475f5a4e366d65726c696e31307472616e73637269707431305472616e73637269707431356368616c6c656e67655f62797465733137683139396639343665653136613232653645a9024b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376833363434303339363835643063323930452e6c6c766d2e33313730333639323635313932303830323139aa024c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683961343862356639626162363031616645ab024c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686231636231343532373531396537373445ac028e015f5a4e313570616c6c65745f62616c616e636573357479706573315f38325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e526561736f6e732447542439747970655f696e666f3137683934623234613030333762363530363345ad0291015f5a4e313570616c6c65745f62616c616e636573357479706573315f38355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e4578747261466c6167732447542439747970655f696e666f3137683738363661323165396163336662353445ae029a015f5a4e313570616c6c65745f62616c616e636573357479706573315f39345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e41646a7573746d656e74446972656374696f6e2447542439747970655f696e666f3137683765323238613762666566366335373745af025e5f5a4e34636f726533666d74336e756d35325f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247573697a652447542433666d743137686464646433363838656435613663326345b0024b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376830383063646464613363663233643932452e6c6c766d2e37323338353837393031333039353239363535b102725f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376830306464383863383737323566396339452e6c6c766d2e37323338353837393031333039353239363535b20291015f5a4e39315f244c54247061726974795f7363616c655f636f6465632e2e636f6465632e2e4279746573437572736f722475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e496e7075742447542432377363616c655f696e7465726e616c5f6465636f64655f62797465733137686661393333316138376261633937616245b30285015f5a4e3130325f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e507265666978496e707574244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e496e7075742447542434726561643137683832316466626433326334336637656345b402595f5a4e35375f244c54247374722475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542436656e636f64653137683230646433646333616136356166366645b502645f5a4e37315f244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683736653231363062643337396131613845b6027e5f5a4e31357072696d69746976655f7479706573315f37325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247072696d69746976655f74797065732e2e483235362447542439747970655f696e666f3137683962666535363234313335363563363845b702655f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376830313865383033653035643636626566452e6c6c766d2e37313939383439303534333435303437353339b802ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137686565623638366333623730636330373345b902725f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376862386537663838636630313638306130452e6c6c766d2e37313939383439303534333435303437353339ba024b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376863306262636233663465353964643634452e6c6c766d2e37313939383439303534333435303437353339bb027e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137686239613237383564623830303561373245bc02405f5a4e3372797536707265747479386d616e7469737361313977726974655f6d616e74697373615f6c6f6e673137683663643363623032376435363635636245bd022b5f5a4e337279753670726574747938666f726d617436343137686430373033343031366539363934613945be029c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e673137683139636335616331336466383464376645bf029c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e673137683239643237333762323061613830393045c002a2015f5a4e3130365f244c5424636f72652e2e697465722e2e61646170746572732e2e636861696e2e2e436861696e244c5424412443244224475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e65787431376865386438666238623235346561393937452e6c6c766d2e39303538353435373438363939303634333233c102ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683430316334313061616162663461656445c202ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683236643532313365373031363061633245c302ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683736363639363566326363366361303645c402625f5a4e36375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e653137683061306166383237626337636538653245c502625f5a4e36375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e653137686530656466333463356564656531666145c60285015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f697465723137683161643437623264356430626261386145c70285015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f697465723137683636656237383536306632623164373545c80285015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f697465723137683734316665393834623930396465346645c9024c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376834393063626561366164316231636530452e6c6c766d2e3135393835393236373434363732363638333538ca024c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683766323232343466306633636135393045cb02595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683461363334346336316139623434666145cc02595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683539393961326663626432626365376245cd02595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683734386136303531643166653138646545ce0285015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137683565383762343736333961303339336445cf027d5f5a4e36385f244c54247363616c655f696e666f2e2e74792e2e54797065244c54245424475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e6531376862333962396564323865356239633933452e6c6c766d2e3130323733353337313336363433353738333932d0028b015f5a4e3130385f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e49746572244c54244b2443245624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137686438386238396161363135396630393545d1024b5f5a4e34636f72653373747232315f244c5424696d706c24753230247374722447542431387472696d5f73746172745f6d6174636865733137683136643634396330396130393836363245d202565f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565336d6170323542547265654d6170244c54244b24432456244324412447542436696e736572743137683864656666343634656335343261636445d30281015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683034393439363666393063613865353045d40281015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686231613133366539613136303232303945d5026f5f5a4e34636f726533707472353264726f705f696e5f706c616365244c54247363616c655f696e666f2e2e74792e2e706174682e2e506174684572726f722447542431376837643834633036333032336638636534452e6c6c766d2e35383434353639303536363434363637353039d602345f5a4e31307363616c655f696e666f32747934706174683450617468336e65773137683737646331386466393439363139323545d7023d5f5a4e31307363616c655f696e666f357574696c73313869735f727573745f6964656e7469666965723137683437663366383065643137396663303245d802425f5a4e31307363616c655f696e666f3274793470617468345061746831366e65775f776974685f7265706c6163653137683337613935333266363961386530623045d9027a5f5a4e36385f244c54247363616c655f696e666f2e2e74792e2e706174682e2e506174684572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863303238383064363233623735356563452e6c6c766d2e35383434353639303536363434363637353039da02475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683333663866303763653639386461396345db02475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683362396138353262306533643137646345dc024e5f5a4e31307363616c655f696e666f38696e7465726e65723137496e7465726e6572244c542454244754243133696e7465726e5f6f725f6765743137683763613431343133383531656436656545dd02445f5a4e31307363616c655f696e666f387265676973747279385265676973747279313372656769737465725f747970653137683866623063666161646332653436396645de02765f5a4e37385f244c54247363616c655f696e666f2e2e74792e2e547970654465662475323024617324753230247363616c655f696e666f2e2e72656769737472792e2e496e746f506f727461626c65244754243133696e746f5f706f727461626c653137683733386235373762396636396530303545df02485f5a4e31307363616c655f696e666f38726567697374727938526567697374727931376d61705f696e746f5f706f727461626c653137683063643334646364333138373138393945e0026c5f5a4e31307363616c655f696e666f35696d706c7335355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f6f6c2447542439747970655f696e666f3137683738613766616237623061383964363745e1026a5f5a4e31307363616c655f696e666f35696d706c7335335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302475382447542439747970655f696e666f3137683638323036303733636633633735306145e2026b5f5a4e31307363616c655f696e666f35696d706c7335345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247531362447542439747970655f696e666f3137683430313662323339626334336366666345e3026b5f5a4e31307363616c655f696e666f35696d706c7335345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247533322447542439747970655f696e666f3137686432303832383334363734663733626345e4026b5f5a4e31307363616c655f696e666f35696d706c7335345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247536342447542439747970655f696e666f3137683735363131616139643165313765383245e5026c5f5a4e31307363616c655f696e666f35696d706c7335355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024753132382447542439747970655f696e666f3137683736323365346661396266313736386245e602705f5a4e31307363616c655f696e666f35696d706c7335395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c5024245250242447542439747970655f696e666f3137683039666261303039623031346435616345e702475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683331653463666338336463386461346345e8023d5f5a4e34636f726533707472323764726f705f696e5f706c616365244c5424245246247538244754243137683763366334653362613536333161666345e902355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683738326430306264303434626232383045ea02405f5a4e31307363686e6f72726b656c32377363616c61725f66726f6d5f63616e6f6e6963616c5f62797465733137683231626133323032643233383433323045eb02475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686538643166363137613534356536633645ec02495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683263303135353537316533393461333845ed02495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137686436643630343036393461653862316345ee02305f5a4e34636f726533666d743557726974653977726974655f666d743137683731353738323431643336303761393145ef0293015f5a4e34636f72653370747231313264726f705f696e5f706c616365244c5424244c542473657264652e2e64652e2e57697468446563696d616c506f696e74247532302461732475323024636f72652e2e666d742e2e446973706c6179244754242e2e666d742e2e4c6f6f6b466f72446563696d616c506f696e74244754243137683735313030393237663835323937643445f002595f5a4e36305f244c542473657264652e2e64652e2e556e6578706563746564247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683535646662386339623734333434306145f1025f5f5a4e36365f244c542473657264652e2e64652e2e57697468446563696d616c506f696e74247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683965656534616530643961346539336445f2024c5f5a4e34375f244c54242452462473747224753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683133643066303835663963663039313145f302545f5a4e35355f244c542473657264652e2e64652e2e4f6e654f66247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683630656365623964613839656431393045f402a4015f5a4e3132385f244c5424244c542473657264652e2e64652e2e57697468446563696d616c506f696e74247532302461732475323024636f72652e2e666d742e2e446973706c6179244754242e2e666d742e2e4c6f6f6b466f72446563696d616c506f696e74247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f7374723137686639653136666134313235353564636345f502a6015f5a4e3132385f244c5424244c542473657264652e2e64652e2e57697468446563696d616c506f696e74247532302461732475323024636f72652e2e666d742e2e446973706c6179244754242e2e666d742e2e4c6f6f6b466f72446563696d616c506f696e74247532302461732475323024636f72652e2e666d742e2e577269746524475424313077726974655f636861723137686463343231626533393535643266383945f602425f5a4e313073657264655f6a736f6e32646531325061727365724e756d6265723132696e76616c69645f747970653137683736623166383430383066383931663245f702475f5a4e34636f726533707472333764726f705f696e5f706c616365244c5424636f72652e2e666d742e2e4572726f72244754243137686339396332366261663035366662366645f8024c5f5a4e34636f726533707472343264726f705f696e5f706c616365244c5424616c6c6f632e2e737472696e672e2e537472696e67244754243137683761313336376238316230303530613145f902525f5a4e35335f244c5424636f72652e2e666d742e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686331333433306130306461396533623245fa02575f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686435626234303238353735623832616645fb025f5f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e577269746524475424313077726974655f636861723137683665623061383439373036393635376445fc025d5f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f7374723137683030613666373066376361353163323545fd02325f5a4e313073657264655f6a736f6e356572726f72354572726f7232696f3137683739656331363261323531386161363545fe02605f5a4e36375f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f72436f6465247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683065666234646363613436386530383245ff025c5f5a4e36335f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d74313768316663323462383763363932333434364580035a5f5a4e36315f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376831346131386137346336366438366530458103775f5a4e36315f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f7224753230246173247532302473657264652e2e64652e2e4572726f722447542436637573746f6d31376863616237346631363832643338353433452e6c6c766d2e31333431383836383837393433313630393432368203355f5a4e313073657264655f6a736f6e356572726f7231306d616b655f6572726f7231376865313361356437393433623563623636458303645f5a4e36315f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f7224753230246173247532302473657264652e2e64652e2e4572726f72244754243132696e76616c69645f7479706531376830613931643064626332326365343036458403655f5a4e37325f244c542473657264655f6a736f6e2e2e6572726f722e2e4a736f6e556e6578706563746564247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376838663833663031313161623562383138458503655f5a4e36315f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f7224753230246173247532302473657264652e2e64652e2e4572726f72244754243133696e76616c69645f76616c756531376861383739623538386666663566323633458603375f5a4e313073657264655f6a736f6e337365723134696e76616c69645f6e756d626572313768646266333563643361333637376237624587035f5f5a4e313073657264655f6a736f6e347265616439536c696365526561643137706f736974696f6e5f6f665f696e64657831376833623736313464303635366238326134452e6c6c766d2e313430393032393737363337313432323534303788035c5f5a4e313073657264655f6a736f6e347265616439536c696365526561643134736b69705f746f5f65736361706531376837393532303930303231376665613639452e6c6c766d2e31343039303239373736333731343232353430378903475f5a4e313073657264655f6a736f6e347265616439536c696365526561643139736b69705f746f5f6573636170655f736c6f7731376865333631346237636530326431633435458a03695f5a4e37305f244c542473657264655f6a736f6e2e2e726561642e2e536c6963655265616424753230246173247532302473657264655f6a736f6e2e2e726561642e2e52656164244754243970617273655f73747231376835346165653164306261646265326437458b03725f5a4e37305f244c542473657264655f6a736f6e2e2e726561642e2e536c6963655265616424753230246173247532302473657264655f6a736f6e2e2e726561642e2e526561642447542431376465636f64655f6865785f65736361706531376830373739613934316163623332653234458c032e5f5a4e313073657264655f6a736f6e3472656164356572726f7231376862666162366537306165643663653863458d03355f5a4e313073657264655f6a736f6e347265616431317065656b5f6f725f656f6631376862393637366362366135633636346663458e036b5f5a4e37305f244c542473657264655f6a736f6e2e2e726561642e2e536c6963655265616424753230246173247532302473657264655f6a736f6e2e2e726561642e2e5265616424475424313069676e6f72655f73747231376861336661396633653430356665636335458f034c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376866626563383933396537346633386362452e6c6c766d2e313030343030323937363239343334383732393390034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376831653066383863363731616361643662459103595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376866376131666235343636396338356430459203305f5a4e34636f726533666d743557726974653977726974655f666d74313768643761313936356633656162333834644593034c5f5a4e34636f726533707472343264726f705f696e5f706c616365244c5424616c6c6f632e2e737472696e672e2e537472696e6724475424313768376131333637623831623030353061314594035f5f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e577269746524475424313077726974655f63686172313768366562306138343937303639363537644595035d5f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f73747231376830306136663730663763613531633235459603495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376837663239616336653031666334353965459703495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d74313768666639623865323262363462373032324598035e5f5a4e36355f244c542473657264655f6a736f6e2e2e696f2e2e696d702e2e4572726f72247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376862363139313461336363323963613730459903ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f6974657231376863373665373631353137653762663265459a03a6015f5a4e323173705f6170706c69636174696f6e5f63727970746f377372323535313933617070315f39345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f6170706c69636174696f6e5f63727970746f2e2e737232353531392e2e6170702e2e5075626c69632447542439747970655f696e666f31376837303664626431386233343538326432459b03a9015f5a4e323173705f6170706c69636174696f6e5f63727970746f377372323535313933617070315f39375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f6170706c69636174696f6e5f63727970746f2e2e737232353531392e2e6170702e2e5369676e61747572652447542439747970655f696e666f31376866326431653963646632386339303566459c034c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376838303362643764616634353632636234452e6c6c766d2e31373439383834333437363732353639343532399d034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376837363234626230303465353236643366459e03595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376835383539313937343665393331663663459f03a2015f5a4e323173705f6170706c69636174696f6e5f63727970746f35656364736133617070315f39325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f6170706c69636174696f6e5f63727970746f2e2e65636473612e2e6170702e2e5075626c69632447542439747970655f696e666f3137686433366365323132363032633732396545a003a5015f5a4e323173705f6170706c69636174696f6e5f63727970746f35656364736133617070315f39355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f6170706c69636174696f6e5f63727970746f2e2e65636473612e2e6170702e2e5369676e61747572652447542439747970655f696e666f3137683839623433643931646431366639353545a103a6015f5a4e323173705f6170706c69636174696f6e5f63727970746f376564323535313933617070315f39345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f6170706c69636174696f6e5f63727970746f2e2e656432353531392e2e6170702e2e5075626c69632447542439747970655f696e666f3137683134613730393530386335343432366545a203a9015f5a4e323173705f6170706c69636174696f6e5f63727970746f376564323535313933617070315f39375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f6170706c69636174696f6e5f63727970746f2e2e656432353531392e2e6170702e2e5369676e61747572652447542439747970655f696e666f3137683737393332393236363038623561656345a3037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683434393932363236393530636130366245a4037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683438666664323862306638633837656145a5037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683537643438323638663839646562356545a6037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683939363631636239303364663663643145a70385015f5a4e313373705f61726974686d65746963315f38315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f61726974686d657469632e2e41726974686d657469634572726f722447542439747970655f696e666f3137683165633739313933353365383535336445a803ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683031396664613763616466366136326145a903ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683131613736386137373564353335633245aa03ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683834633634656134623662343136633145ab034c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376833303065316536323263386334333066452e6c6c766d2e3136313430383732343135383835373835373838ac034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683232613634316461313338316135306345ad034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683834373531646437643365393239613845ae03595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683036626233323163356536343439356245af03595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686537373636396631313538333935316645b003595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686665316264346630623264393134346645b10395015f5a4e313373705f61726974686d6574696331307065725f7468696e6773315f38355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f61726974686d657469632e2e7065725f7468696e67732e2e50657262696c6c2447542439747970655f696e666f3137683564633862343136306363373832636245b203ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683334636131333136653736393463306145b303ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683636613234623730646533353633313845b403af015f5a4e313773705f636f6e73656e7375735f61757261377372323535313931316170705f73723235353139315f39385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f617572612e2e737232353531392e2e6170705f737232353531392e2e5075626c69632447542439747970655f696e666f3137686132356530323263373163313663613845b5034b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376834646539666336353665663230333532452e6c6c766d2e35363434383134383035313531343631323136b6034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683331306364326261306435643731616245b703595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683038656230313236643736313431666445b803595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683839306237383938396432616238353445b9037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683030303139613435616466333432306545ba034a5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376830353866643961363931663865663530452e6c6c766d2e373639353136303936333334333635353838bb034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683065633136343536373565636138633945bc034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686330343564326339373839343337363245bd03595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683731626461626234363634616161323745be03595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686534343135646637373265663133656345bf03595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686539363639643230303534353535383145c00380015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686239383864326263313435373163346145c10380015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686534646537373138626665643962343845c203ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683134613131396537356531333937343645c303ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683536333139376135656565636339343645c403ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137686637663366353635303133353031653345c503ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137686565653039623565643236323861303045c6038f015f5a4e313773705f636f6e73656e7375735f62616265315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e42616265436f6e66696775726174696f6e2447542439747970655f696e666f3137683133303039336163653864383735316545c7038a015f5a4e313773705f636f6e73656e7375735f62616265315f38325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e416c6c6f776564536c6f74732447542439747970655f696e666f3137686238653764313765363635353332636645c80394015f5a4e313773705f636f6e73656e7375735f62616265315f39325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e4261626545706f6368436f6e66696775726174696f6e2447542439747970655f696e666f3137686235313062306430343536653134356645c90395015f5a4e313773705f636f6e73656e7375735f62616265315f39335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e4f70617175654b65794f776e65727368697050726f6f662447542439747970655f696e666f3137683233383430656335653734376364613145ca0383015f5a4e313773705f636f6e73656e7375735f62616265315f37355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e45706f63682447542439747970655f696e666f3137683233656166383833373030393964633445cb03755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683135303065376633396238373134333145cc038d015f5a4e313773705f636f6e73656e7375735f6261626533617070315f38315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e6170702e2e5075626c69632447542439747970655f696e666f3137686230613764623135343632616538623645cd03755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683562323039393237313537313338303845ce037e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137686461326637613037306666366539353845cf039f015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f39355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e5072696d6172795072654469676573742447542439747970655f696e666f3137683864316135343232636665636263393145d003a7015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f3130325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e5365636f6e64617279506c61696e5072654469676573742447542439747970655f696e666f3137683431366134656236626331396536333245d103a5015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f3130305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e5365636f6e646172795652465072654469676573742447542439747970655f696e666f3137683538303435656338663162346665356345d20398015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f38385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e5072654469676573742447542439747970655f696e666f3137683630316564356639306339666165633145d303a3015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f39395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e4e657874436f6e66696744657363726970746f722447542439747970655f696e666f3137683064643965323734643561383565373345d403445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683162346266356466666437326436316445d503445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683964333231303561393462383030393445d603445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686330616161613965623834656532393345d703445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686462373064346162653466383735326445d8037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683339663139383531623739626436613545d9037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683765316430623430613062393563613945da037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137686131373465393562333964376464643745db034b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376838656538313264663439373636346330452e6c6c766d2e37353435323632393235353937383930363436dc034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686131336535626165396639313136636245dd0393015f5a4e323073705f636f6e73656e7375735f6772616e64706133617070315f38345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f6772616e6470612e2e6170702e2e5075626c69632447542439747970655f696e666f3137683834626335663166633962313461333545de0396015f5a4e323073705f636f6e73656e7375735f6772616e64706133617070315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f6772616e6470612e2e6170702e2e5369676e61747572652447542439747970655f696e666f3137686338383834663633386564643536353745df0384015f5a4e313873705f636f6e73656e7375735f736c6f7473315f37355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f736c6f74732e2e536c6f742447542439747970655f696e666f3137686438396633373730363133323364356645e0038c015f5a4e313873705f636f6e73656e7375735f736c6f7473315f38335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f736c6f74732e2e536c6f744475726174696f6e2447542439747970655f696e666f3137683033313030636665356130646130633645e103325f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f773137683464336263376134326237643032353545e2034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683663623963646462336162366264383345e3034c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376864396563376434376531623537656634452e6c6c766d2e3138333531353337343636303635373439303230e4034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683034396439386334363466613161343545e503595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683536306633613466623834303265336445e603595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683631356366383630613461313935616345e703595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686363323065333331323561633264663645e8032f5f5a4e3773705f636f72653663727970746f3873733538686173683137683238663732393033333736326233623145e90381015f5a4e3773705f636f72653663727970746f315f37375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f72652e2e63727970746f2e2e4b65795479706549642447542439747970655f696e666f3137683238326237386366323163366636626245ea037e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137683539373733366138633763383264383245eb03775f5a4e3773705f636f7265315f37345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f72652e2e4f70617175654d657461646174612447542439747970655f696e666f3137686666386331393336303266393331626545ec036d5f5a4e3773705f636f7265315f36345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f72652e2e566f69642447542439747970655f696e666f3137686562363633306436313935373263623945ed038c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686138636538626637613932616432303745ee036f5f5a4e37365f244c542473705f636f72652e2e737232353531392e2e7672662e2e5672665072654f75747075742475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683330393231376461393531323365383745ef036b5f5a4e37325f244c542473705f636f72652e2e737232353531392e2e7672662e2e56726650726f6f662475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137686462376161383863653335323836666245f0038f015f5a4e3773705f636f7265377372323535313933767266315f38365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f72652e2e737232353531392e2e7672662e2e5672665369676e61747572652447542439747970655f696e666f3137683731626661633661363630366337393845f1033f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137686635303235633330613364653762386145f20380015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683038383839333532653864656537633845f303ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683434333763636231323833393330353645f403ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683631636138316334326465613433393745f503ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137686638626564613134626663393033626145f603735f5a4e31307363616c655f696e666f35696d706c7336325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424753562245424753564242447542439747970655f696e666f3137686233633762363061653337613236623445f703755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683165626536363035343934616533323545f80380015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683362376336643634313061353731613545f9039e015f5a4e31307363616c655f696e666f35696d706c733130345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b24432456244754242447542439747970655f696e666f3137686466623936623836623632363963393145fa037e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137683330383333656637343465323132376145fb0380015f5a4e313273705f696e686572656e7473315f37375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f696e686572656e74732e2e496e686572656e74446174612447542439747970655f696e666f3137683163616437323962343236333439656445fc0388015f5a4e313273705f696e686572656e7473315f38355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f696e686572656e74732e2e436865636b496e686572656e7473526573756c742447542439747970655f696e666f3137686132313236633930353262626638633745fd034b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376835393161623434303232623334316461452e6c6c766d2e34383433383033393637313038353834313131fe034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686235343063316530346631383764636345ff03465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e31376836306666356365653137313431633963458004465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e31376838663732633037343362303133386130458104465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e31376866393836343039356430613034653063458204325f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f77313768343263353138373437363962376235394583044c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f70757368313768303435613061343463633239383838344584044c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376832653166356261623839333864626662458504a4015f5a4e323073705f72756e74696d655f696e7465726661636535696d706c7339355f244c5424696d706c247532302473705f72756e74696d655f696e746572666163652e2e7761736d2e2e46726f6d46464956616c75652475323024666f722475323024616c6c6f632e2e7665632e2e566563244c5424542447542424475424313466726f6d5f6666695f76616c756531376831366666613936363234353661343866458604a4015f5a4e323073705f72756e74696d655f696e7465726661636535696d706c7339355f244c5424696d706c247532302473705f72756e74696d655f696e746572666163652e2e7761736d2e2e46726f6d46464956616c75652475323024666f722475323024616c6c6f632e2e7665632e2e566563244c5424542447542424475424313466726f6d5f6666695f76616c756531376834643265323865373339313062663361458704a4015f5a4e323073705f72756e74696d655f696e7465726661636535696d706c7339355f244c5424696d706c247532302473705f72756e74696d655f696e746572666163652e2e7761736d2e2e46726f6d46464956616c75652475323024666f722475323024616c6c6f632e2e7665632e2e566563244c5424542447542424475424313466726f6d5f6666695f76616c756531376837356436356566663239343833643631458804a4015f5a4e323073705f72756e74696d655f696e7465726661636535696d706c7339355f244c5424696d706c247532302473705f72756e74696d655f696e746572666163652e2e7761736d2e2e496e746f46464956616c75652475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243134696e746f5f6666695f76616c756531376838343361646339303639343433633366458904715f5a4e34636f726533707472353364726f705f696e5f706c616365244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f722447542431376866666232363230313462646130643736452e6c6c766d2e31323633373838373031343536303132353636358a047c5f5a4e36395f244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863636562313832636338343631643463452e6c6c766d2e31323633373838373031343536303132353636358b04625f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376839633739396534343437343337346537452e6c6c766d2e333033343630303939323437323131313739338c04635f5a4e34355f244c5424244c502424525024247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376834303566303661613534323663666363452e6c6c766d2e333033343630303939323437323131313739338d04585f5a4e34636f726533707472323964726f705f696e5f706c616365244c5424244c5024245250242447542431376834396133376432383766346563623464452e6c6c766d2e333033343630303939323437323131313739338e04415f5a4e35616c6c6f6333666d7436666f726d617431376838346335363334623861393861353135452e6c6c766d2e333033343630303939323437323131313739338f04495f5a4e35627974657335627974657331317374617469635f64726f7031376864643031336537396536363530303731452e6c6c766d2e3330333436303039393234373231313137393390044a5f5a4e35627974657335627974657331327374617469635f636c6f6e6531376833323130386566643438323766363666452e6c6c766d2e3330333436303039393234373231313137393391044e5f5a4e35627974657335627974657331367374617469635f69735f756e6971756531376836633034666662366437323062666139452e6c6c766d2e3330333436303039393234373231313137393392047b5f5a4e36395f244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863636562313832636338343631643463452e6c6c766d2e33303334363030393932343732313131373933930411727573745f626567696e5f756e77696e64940480015f5a4e3573705f696f315f38385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302473705f696f2e2e4b696c6c53746f72616765526573756c7424475424366465636f646531376836373232303238373163626666343261459504485f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c7336617070656e6431376837656638376163633731303638616436459604475f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c7335636c656172313768666135643861313862363730623336664597044f5f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c733132636c6561725f70726566697831376837346164333366346133616366336662459804555f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c733138636f6d6d69745f7472616e73616374696f6e31376832613739373661363561346634326539459904455f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c733367657431376830643334386165303737646161623266459a044a5f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c73386e6578745f6b657931376832313061623939663834653434353262459b04465f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c73347265616431376839373039333561363633396438653766459c04575f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c733230726f6c6c6261636b5f7472616e73616374696f6e31376862363132613934643264623564396632459d04465f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c7334726f6f7431376835383939313164383561653631393035459e04455f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c733373657431376838656233646237653834376262343737459f04545f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c73313773746172745f7472616e73616374696f6e3137683165303531396566346636376130613845a0044d5f5a4e3573705f696f3768617368696e67323665787465726e5f686f73745f66756e6374696f6e5f696d706c733130626c616b65325f3132383137683361356361313931653563623962626645a1044d5f5a4e3573705f696f3768617368696e67323665787465726e5f686f73745f66756e6374696f6e5f696d706c733130626c616b65325f3235363137683161656336373863373636643165386545a2044a5f5a4e3573705f696f3768617368696e67323665787465726e5f686f73745f66756e6374696f6e5f696d706c733874776f785f3132383137686363383239356438653834663133646345a304495f5a4e3573705f696f3768617368696e67323665787465726e5f686f73745f66756e6374696f6e5f696d706c733774776f785f36343137686333646631316530373466653466366645a4044f5f5a4e3573705f696f31346f6666636861696e5f696e646578323665787465726e5f686f73745f66756e6374696f6e5f696d706c7335636c6561723137683531376237383864333164323830646445a5044d5f5a4e3573705f696f31346f6666636861696e5f696e646578323665787465726e5f686f73745f66756e6374696f6e5f696d706c73337365743137683130326561633064623336616530613445a604505f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c73313465636473615f67656e65726174653137686138323633393737323633616333316645a704535f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c73313765636473615f7075626c69635f6b6579733137683330623335653238356334363834663745a8044c5f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c73313065636473615f7369676e3137683238363963333661363234393537653245a9044e5f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c73313265636473615f7665726966793137683761633166346166333136616537323445aa04525f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733136656432353531395f67656e65726174653137686561613135616130333439333934643845ab04555f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733139656432353531395f7075626c69635f6b6579733137683266653032653161656164666332353645ac044e5f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733132656432353531395f7369676e3137686534306433623164363061646539623445ad04505f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733134656432353531395f7665726966793137686331303430653166613136353763366445ae04525f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733136737232353531395f67656e65726174653137686633363835346633363931326139623545af04555f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733139737232353531395f7075626c69635f6b6579733137683032626534646434303761346632343545b0044e5f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733132737232353531395f7369676e3137683765333432313835613237363664663445b104505f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733134737232353531395f7665726966793137686561346235363063623362346136323545b204565f5a4e3573705f696f386f6666636861696e323665787465726e5f686f73745f66756e6374696f6e5f696d706c7331387375626d69745f7472616e73616374696f6e3137686235333139653432336536323431636345b30486015f5a4e3130315f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683330383434316330633564633035646245b40485015f5a4e3130325f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e507265666978496e707574244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e496e7075742447542434726561643137686363393731323631663966623861386145b5048c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686461333636383235366232303338373445b604525f5a4e3573705f696f31377472616e73616374696f6e5f696e646578323665787465726e5f686f73745f66756e6374696f6e5f696d706c7335696e6465783137683237356565323738356263626662366145b704455f5a4e3573705f696f376c6f6767696e67323665787465726e5f686f73745f66756e6374696f6e5f696d706c73336c6f673137683530363462323831636535306338626245b8044b5f5a4e3573705f696f376c6f6767696e67323665787465726e5f686f73745f66756e6374696f6e5f696d706c73396d61785f6c6576656c3137683039323133633762323931343533363745b9043f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683736323532346232643062373662346145ba04735f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683035326462633961363633636161653145bb04735f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683434306165353537613438653862383345bc04735f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683734303966313861626165383464323245bd04735f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137686435653566336132323136383164643345be04485f5a4e3573705f696f39616c6c6f6361746f72323665787465726e5f686f73745f66756e6374696f6e5f696d706c7334667265653137683462313034643330313832316462623545bf044a5f5a4e3573705f696f39616c6c6f6361746f72323665787465726e5f686f73745f66756e6374696f6e5f696d706c73366d616c6c6f633137683666313530336666343237336661656545c004555f5a4e3573705f696f323164656661756c745f6368696c645f73746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c7334726561643137683439393566306462633964306430616445c104545f5a4e3573705f696f323164656661756c745f6368696c645f73746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c73337365743137683362383564346135336134336334303945c204575f5a4e3573705f696f3474726965323665787465726e5f686f73745f66756e6374696f6e5f696d706c733233626c616b65325f3235365f6f7264657265645f726f6f743137686333303664626430363134643032366645c304485f5a4e3573705f696f346d697363323665787465726e5f686f73745f66756e6374696f6e5f696d706c73397072696e745f6865783137683336336330313365373139373831333745c4044a5f5a4e3573705f696f346d697363323665787465726e5f686f73745f66756e6374696f6e5f696d706c7331307072696e745f757466383137683537646631656164326338316661373345c5044f5f5a4e3573705f696f346d697363323665787465726e5f686f73745f66756e6374696f6e5f696d706c73313572756e74696d655f76657273696f6e3137686165636232366331386530636531333945c604ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683833376166333633613931373333313045c704ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137686538646661303830643464363865656545c804ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137686632643232323238336133313534656145c904ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137686633666162393231656261653934346245ca04645f5a4e37305f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683535376230353166643836343730386545cb04645f5a4e35616c6c6f633376656339696e746f5f697465723231496e746f49746572244c54245424432441244754243332666f726765745f616c6c6f636174696f6e5f64726f705f72656d61696e696e673137686338383365666364366162383666666645cc04745f5a4e38365f244c5424616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683265356239323239646535393731616645cd04b7015f5a4e313473705f6d657461646174615f6972337631353133325f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542473705f6d657461646174615f69722e2e74797065732e2e50616c6c65744d657461646174614952244754242475323024666f7224753230246672616d655f6d657461646174612e2e7631352e2e50616c6c65744d65746164617461244754243466726f6d3137686435623565326163663831396134656145ce04355f5a4e313473705f6d657461646174615f69723132696e746f5f76657273696f6e3137683530333863323061393035333164303345cf04b5015f5a4e313473705f6d657461646174615f6972337631343133305f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542473705f6d657461646174615f69722e2e74797065732e2e4d657461646174614952244754242475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e52756e74696d654d65746164617461563134244754243466726f6d3137683139633931353936376261396537646445d004b7015f5a4e313473705f6d657461646174615f6972337631343133325f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542473705f6d657461646174615f69722e2e74797065732e2e50616c6c65744d657461646174614952244754242475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e50616c6c65744d65746164617461244754243466726f6d3137683235643161303161373464633364376545d104c3015f5a4e313473705f6d657461646174615f6972337631343134345f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542473705f6d657461646174615f69722e2e74797065732e2e53746f72616765456e7472794d657461646174614952244754242475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e53746f72616765456e7472794d65746164617461244754243466726f6d3137683665393739663030336133623038353245d204445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683035393961616461346535326437663845d304445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683062663263656631383766663830633645d404445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683138353132653333343430333030653045d504445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683333366537346564623830303865303245d604445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683533313530336365626338303836663345d704445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683635313738316139336339336634353945d804445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683765346636353631653535643638366545d904445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683765633739663066323433663237656345da04445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686339306638353136613862333939366545db04445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686438626361363832393365366333633945dc04445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686466653838373065376131303564653445dd04445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686563623833376463663535396361633745de044b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376861663733353962356437623435323839452e6c6c766d2e34333231353138303532333134313138383432df044c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683162623765623034376138316563643845e0044c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683837623334663636646631336262613945e1044c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686532323132633735613132313133353545e204595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683130343866396235333133393238653045e304595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683732653938653237396332383863636445e404595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683763613137613266353966333330393145e504595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686637366361333633666662336364303445e6043f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137686430623530613164316662636439366545e70480015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683963343130303562646137393163663245e80480015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686434663738393338366431316435663145e90480015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686666646231383766386365336539666645ea04ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683762313739636335376661336662646445eb04ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137686132376664646630313937323438346245ec04ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137686135353530333538646261623334343645ed0480015f5a4e39365f244c542473705f72756e74696d652e2e67656e657269632e2e6469676573742e2e4469676573744974656d5265662475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542436656e636f64653137683232366335316666316535343838633045ee04735f5a4e38305f244c542473705f72756e74696d652e2e67656e657269632e2e6469676573742e2e4469676573744974656d2475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137686337653238613663633730346662396145ef0480015f5a4e39305f244c542473705f72756e74696d652e2e67656e657269632e2e6469676573742e2e4469676573744974656d24753230246173247532302473705f72756e74696d652e2e7472616974732e2e436865636b457175616c244754243131636865636b5f657175616c3137686636313865623538613532373037323145f00496015f5a4e313073705f72756e74696d653767656e6572696336646967657374315f38365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e67656e657269632e2e6469676573742e2e4469676573742447542439747970655f696e666f3137686664613539666339343334386235633445f1047e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137683465336538303364333632356639363845f204495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683062316134646338383337653665333645f304325f5a4e34636f726533666d74355772697465313077726974655f636861723137686139333364376565343564306537363145f404305f5a4e34636f726533666d743557726974653977726974655f666d743137686635363931643462346464346563383645f504455f5a4e34636f726533707472333564726f705f696e5f706c616365244c542473705f7374642e2e577269746572244754243137683833376161323330313165393934653945f604795f5a4e34636f726533707472363264726f705f696e5f706c616365244c542473705f72756e74696d652e2e72756e74696d655f6c6f676765722e2e52756e74696d654c6f676765722447542431376865373335343064313163313234303131452e6c6c766d2e35373635393736363831363435383336383537f70480015f5a4e37305f244c542473705f72756e74696d652e2e72756e74696d655f6c6f676765722e2e52756e74696d654c6f676765722475323024617324753230246c6f672e2e4c6f672447542437656e61626c656431376834313132633135656262343364636134452e6c6c766d2e35373635393736363831363435383336383537f804635f5a4e37305f244c542473705f72756e74696d652e2e72756e74696d655f6c6f676765722e2e52756e74696d654c6f676765722475323024617324753230246c6f672e2e4c6f6724475424336c6f673137686631323665323333336165316132396545f9047e5f5a4e37305f244c542473705f72756e74696d652e2e72756e74696d655f6c6f676765722e2e52756e74696d654c6f676765722475323024617324753230246c6f672e2e4c6f672447542435666c75736831376862656333666262366235663265646233452e6c6c766d2e35373635393736363831363435383336383537fa04755f5a4e38325f244c542473705f72756e74696d652e2e72756e74696d655f737472696e672e2e52756e74696d65537472696e672475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137686562633832343165323336326264373145fb048c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137683436326130393931646164633165656445fc04bc015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c69646974793132335f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e5472616e73616374696f6e56616c69646974794572726f72244754242475323024666f72247532302424524624737472244754243466726f6d3137683532356261386464636366316662323245fd04595f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479313656616c69645472616e73616374696f6e3132636f6d62696e655f776974683137686233656130333266636134636436363945fe04af015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3130335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e496e76616c69645472616e73616374696f6e2447542439747970655f696e666f3137683461343164633436303933623039343345ff04af015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3130335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e556e6b6e6f776e5472616e73616374696f6e2447542439747970655f696e666f31376837316664613035633163376365386433458005b5015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3130395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e5472616e73616374696f6e56616c69646974794572726f722447542439747970655f696e666f31376866313332313565646631353639333066458105ae015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3130325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e5472616e73616374696f6e536f757263652447542439747970655f696e666f31376861323132383937646166376266363464458205ad015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e56616c69645472616e73616374696f6e2447542439747970655f696e666f313768346135333331333032613731346464374583057b5f5a4e313073705f72756e74696d65315f37345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e4d6f64756c654572726f722447542439747970655f696e666f3137686633356435363866663538633436626545840582015f5a4e313073705f72756e74696d65315f38315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e5472616e73616374696f6e616c4572726f722447542439747970655f696e666f313768626233306636643963613232396436614585057d5f5a4e313073705f72756e74696d65315f37365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e44697370617463684572726f722447542439747970655f696e666f313768326539636666636439663338356531394586057a5f5a4e313073705f72756e74696d65315f37335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e546f6b656e4572726f722447542439747970655f696e666f3137686638633139353666613738383566646445870586015f5a4e313073705f72756e74696d65315f38355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e45787472696e736963496e636c7573696f6e4d6f64652447542439747970655f696e666f313768613062393265373430613039626461624588057b5f5a4e313073705f72756e74696d65315f37345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e4f706171756556616c75652447542439747970655f696e666f313768353135613532643361346434623437634589059c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e6731376862393130613235323366636336323532458a059c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e6731376865353939333832616639313063653565458b054b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376865636165663963333333323162356439452e6c6c766d2e353633363233393834323730393533353139358c054c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376834633435393662396662353838666662458d05595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376864666430653266323034313436333362458e054c5f5a4e313673705f73746174655f6d616368696e6533657874313353746f72616765417070656e6431347265706c6163655f6c656e67746831376831623663363864323864626462373966458f059e015f5a4e313673705f73746174655f6d616368696e6531376f7665726c617965645f6368616e676573396368616e676573657438324f7665726c61796564456e747279244c542473705f73746174655f6d616368696e652e2e6f7665726c617965645f6368616e6765732e2e6368616e67657365742e2e53746f72616765456e747279244754243373657431376865646538393361333639336165363366459005b9015f5a4e313673705f73746174655f6d616368696e6531376f7665726c617965645f6368616e676573396368616e67657365743130384f7665726c617965644d6170244c5424616c6c6f632e2e7665632e2e566563244c542475382447542424432473705f73746174655f6d616368696e652e2e6f7665726c617965645f6368616e6765732e2e6368616e67657365742e2e53746f72616765456e7472792447542433736574313768303632636362613763373566353230624591056f5f5a4e38315f244c5424616c6c6f632e2e7665632e2e73706c6963652e2e53706c696365244c5424492443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f70313768336531323765303239373764666538344592053f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376831313637396536386239336435326137459305475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376835633038353733623131643266623333459405475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768643734653131653337373461366332334595055e5f5a4e34636f726533666d74336e756d35325f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247573697a652447542433666d74313768646464643336383865643561366332634596053c5f5a4e34636f726533707472323664726f705f696e5f706c616365244c54247573697a652447542431376865326239626139343666313039393164459705565f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565336d6170323542547265654d6170244c54244b24432456244324412447542436696e73657274313768396135373066346631303166633138354598056d5f5a4e34636f726533707472343964726f705f696e5f706c616365244c5424736d616c6c7665632e2e436f6c6c656374696f6e416c6c6f634572722447542431376839323766636536623831336632303330452e6c6c766d2e31373930353630373237323133373537303737309905785f5a4e36355f244c5424736d616c6c7665632e2e436f6c6c656374696f6e416c6c6f63457272247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376833333136353566633965353230393634452e6c6c766d2e31373930353630373237323133373537303737309a054a5f5a4e38736d616c6c7665633137536d616c6c566563244c542441244754243231726573657276655f6f6e655f756e636865636b656431376864303333643431383135396466623731459b054b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376835646634376333623866393962643264452e6c6c766d2e323037323134373132343138373337383334369c05725f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376866376634363230363333383535363865452e6c6c766d2e323037323134373132343138373337383334369d05565f5a4e35315f244c542473705f7374642e2e577269746572247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f73747231376838306237346666643465623161613832459e054b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376862386530376363663762666537303564452e6c6c766d2e393537383839383230323436313034313534349f05725f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376864333737366138356664323136613963452e6c6c766d2e39353738383938323032343631303431353434a005435f5a4e313073705f73746f72616765394368696c64496e666f323070726566697865645f73746f726167655f6b65793137683461386161343463313630313733316145a10593015f5a4e3773705f74726965313373746f726167655f70726f6f66315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f747269652e2e73746f726167655f70726f6f662e2e53746f7261676550726f6f662447542439747970655f696e666f3137686237663535333934356338383337333845a205735f5a4e31307363616c655f696e666f35696d706c7336325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424753562245424753564242447542439747970655f696e666f3137686265373866346237326364333438633045a30580015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683764623765623536653135653037353145a405ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683566383362333739363661303639643345a505ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683861653132363930336533393531363745a60587015f5a4e39375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f657874656e642e2e53706563457874656e64244c5424542443244924475424244754243131737065635f657874656e643137683765623365623531616535386133343445a7054c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376832363063323631656562393639646465452e6c6c766d2e3130393530333139383533363038363833313939a8054c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683937646534376331643466636466383645a9054c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683966353264663566653732646438323745aa05595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683764303935616637613231663465393345ab05595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683963313562353162666465663336323545ac05595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686335333864616432616463643132386545ad058c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137683632313236633362643133346332343845ae059a015f5a4e31307363616c655f696e666f35696d706c733130305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e7365742e2e4254726565536574244c542454244754242447542439747970655f696e666f3137686562383831333432316663396532396445af05705f5a4e37345f244c542473705f747269652e2e747269655f73747265616d2e2e5472696553747265616d247532302461732475323024747269655f726f6f742e2e5472696553747265616d244754243131617070656e645f6c6561663137686236666565333662663936383537333545b005735f5a4e31307363616c655f696e666f35696d706c7336325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424753562245424753564242447542439747970655f696e666f3137683238323863636265636637383533376145b105755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683538356366373361353437336132356545b2057e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137686235333833336264636439306539373045b30583015f5a4e31307363616c655f696e666f35696d706c7337385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e626f72726f772e2e436f77244c542454244754242447542439747970655f696e666f3137686535393730613739333139613339653345b4057e5f5a4e313073705f76657273696f6e315f37375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f76657273696f6e2e2e52756e74696d6556657273696f6e2447542439747970655f696e666f3137683464316230303235646636323439353345b5054b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376830656330653335393635356435646662452e6c6c766d2e39353132323739313539363038393431383337b6054c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683833303166623233626361323861313545b70595015f5a4e31307363616c655f696e666f35696d706c7339365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c542454244754242447542439747970655f696e666f3137686135323839303362656338396664626345b805615f5a4e36385f244c542473705f776569676874732e2e7765696768745f76322e2e576569676874247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683337343865366138326132313838616145b9058b015f5a4e313073705f77656967687473397765696768745f7632315f38305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f776569676874732e2e7765696768745f76322e2e5765696768742447542439747970655f696e666f3137683338626562633562636662643865656445ba057f5f5a4e313073705f77656967687473315f37385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f776569676874732e2e52756e74696d6544625765696768742447542439747970655f696e666f3137686439326334626438343632343235376545bb05655f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376837306638626536663361633732663732452e6c6c766d2e33373830303837383837373935313935353433bc054b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376839313930633465383236343363353736452e6c6c766d2e33373830303837383837373935313935353433bd059c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e673137683131346363653465333666643031366245be059c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e673137683535613734356536616362656331636145bf059c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e673137683637346639303866313638346139316545c0059c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e673137686662326364633663356132326131623845c105f6015f5a4e35616c6c6f633131636f6c6c656374696f6e7335627472656536617070656e643137385f244c5424696d706c2475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4f776e65642443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c6561664f72496e7465726e616c24475424244754243962756c6b5f707573683137683338353663666564353630396262666345c205f6015f5a4e35616c6c6f633131636f6c6c656374696f6e7335627472656536617070656e643137385f244c5424696d706c2475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4f776e65642443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c6561664f72496e7465726e616c24475424244754243962756c6b5f707573683137683538356539373534353336373463353845c305f6015f5a4e35616c6c6f633131636f6c6c656374696f6e7335627472656536617070656e643137385f244c5424696d706c2475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4f776e65642443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c6561664f72496e7465726e616c24475424244754243962756c6b5f707573683137686436653862316533346431613962333345c405d5015f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565367365617263683134325f244c5424696d706c2475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424426f72726f77547970652443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c6561664f72496e7465726e616c244754242447542431317365617263685f747265653137683531336161373762313636313439306445c505465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683033326465623466626639643034313845c605465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683430623731346230393362343862353145c705465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683433343537373131666630343138353145c805465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683436353837333462633934643931663645c905465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683562396562383632326538613761653045ca05465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683636336165383130313166633865363045cb05465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683733363938323065356235656134626145cc05465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683862306663356664633339613963323745cd05465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683863626533363332336633643262663845ce05465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137686133303066643436643138343932393045cf05465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137686261616138653232626364623433663345d005465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137686364663666666238396338663766356445d105465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137686430346565356330363436623266366645d205465f5a4e31387061726974795f7363616c655f636f64656335636f6465633139656e636f64655f736c6963655f6e6f5f6c656e3137683632643066363338383937373264373945d305465f5a4e31387061726974795f7363616c655f636f64656335636f6465633139656e636f64655f736c6963655f6e6f5f6c656e3137683761653038663635653035633738316245d405465f5a4e31387061726974795f7363616c655f636f64656335636f6465633139656e636f64655f736c6963655f6e6f5f6c656e3137686333323532313665323039353066616245d505475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f6465643137686236366637636233353130373531366645d6053f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683466623661326265326564323037303245d7053f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683561323865373533363739613734316245d8053f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137686136383437383761393133336666313145d9057c5f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542436636f6d6d697432385f24753762242475376224636c6f737572652475376424247537642431376839323133383435383330616537633739452e6c6c766d2e3132353235333233373335393830383932393830da0583015f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243132636f6d6d69745f6368696c6432385f24753762242475376224636c6f737572652475376424247537642431376839666630616538376464333130656261452e6c6c766d2e3132353235333233373335393830383932393830db057c5f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542436636f6d6d697432385f24753762242475376224636c6f737572652475376424247537642431376837616231363132313865613535356238452e6c6c766d2e3132353235333233373335393830383932393830dc0583015f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243132636f6d6d69745f6368696c6432385f24753762242475376224636c6f737572652475376424247537642431376865303664303766653561666435643961452e6c6c766d2e3132353235333233373335393830383932393830dd055a5f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e6365343063616c6c5f6f6e636524753762242475376224767461626c652e7368696d247537642424753764243137683261333338386662376137623531303045de05695f5a4e34636f726533707472373164726f705f696e5f706c616365244c542473705f747269652e2e6572726f722e2e4572726f72244c54247072696d69746976655f74797065732e2e4832353624475424244754243137683461363365306537646330353230356545df05b5015f5a4e34636f72653370747231343664726f705f696e5f706c616365244c5424616c6c6f632e2e626f7865642e2e426f78244c5424747269655f64622e2e547269654572726f72244c54247072696d69746976655f74797065732e2e4832353624432473705f747269652e2e6572726f722e2e4572726f72244c54247072696d69746976655f74797065732e2e48323536244754242447542424475424244754243137683666333363313066386139323431346645e0055a5f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e6365343063616c6c5f6f6e636524753762242475376224767461626c652e7368696d247537642424753764243137683336633066636239383763373161396445e1055a5f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e6365343063616c6c5f6f6e636524753762242475376224767461626c652e7368696d247537642424753764243137686532323961626532316333623261356445e2053a5f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e63653963616c6c5f6f6e63653137683062363033303564376532626538373645e30590015f5a4e34636f72653370747231303964726f705f696e5f706c616365244c5424747269655f64622e2e7472696564626d75742e2e4e6f6465244c542473705f747269652e2e4c61796f75745630244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424475424244754243137683636656262373231373030326234306345e40591015f5a4e34636f72653370747231313064726f705f696e5f706c616365244c5424747269655f64622e2e7472696564626d75742e2e56616c7565244c542473705f747269652e2e4c61796f75745630244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424475424244754243137683666346661333363616131323934373645e505ad015f5a4e34636f72653370747231333864726f705f696e5f706c616365244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c5424747269655f64622e2e7472696564626d75742e2e56616c7565244c542473705f747269652e2e4c61796f75745630244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f323536244754242447542424475424244754243137686464616263386632333534303430633845e605be015f5a4e34636f72653370747231353564726f705f696e5f706c616365244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c542473705f747269652e2e4c61796f75745630244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624475424244754242e2e63616368655f6e6f64652e2e24753762242475376224636c6f7375726524753764242475376424244754243137683539613639613664623061313439336145e705675f5a4e37355f244c5424747269655f64622e2e7472696564626d75742e2e56616c7565244c54244c24475424247532302461732475323024636f72652e2e636d702e2e5061727469616c4571244754243265713137683236343763396361653063666366353045e805465f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313266726f6d5f656e636f6465643137683466333032653430336637666465366245e905485f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c244754243134696e6c696e655f6f725f686173683137683966343064393064353534373032653845ea05645f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313266726f6d5f656e636f64656432385f24753762242475376224636c6f73757265247537642424753764243137683463653136343861653864643537643345eb05645f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313266726f6d5f656e636f64656432385f24753762242475376224636c6f73757265247537642424753764243137683031353833363735633234623664396245ec05465f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313266726f6d5f656e636f6465643137683733363436313237333437646366346645ed05485f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c244754243134696e6c696e655f6f725f686173683137683236326661346532303234666261343945ee05645f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313266726f6d5f656e636f64656432385f24753762242475376224636c6f73757265247537642424753764243137686238646465343838386132323265623145ef05645f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313266726f6d5f656e636f64656432385f24753762242475376224636c6f73757265247537642424753764243137686632363430653862336432643938316445f005495f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313566726f6d5f6e6f64655f6f776e65643137683061643664363230323366633034636445f1054e5f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c244754243230696e6c696e655f6f725f686173685f6f776e65643137683837313032653665336539653334323445f205495f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313566726f6d5f6e6f64655f6f776e65643137686162323033316433633638656362333445f3054e5f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c244754243230696e6c696e655f6f725f686173685f6f776e65643137686531636463386364373731386636373845f405475f5a4e37747269655f6462397472696564626d7574313456616c7565244c54244c244754243132696e746f5f656e636f6465643137683336396365383462323631626161626645f505475f5a4e37747269655f6462397472696564626d7574313456616c7565244c54244c244754243132696e746f5f656e636f6465643137683538643432616639363930313863316345f605475f5a4e37747269655f6462397472696564626d7574313456616c7565244c54244c244754243132696e746f5f656e636f6465643137683561613262393238613735333966643445f705475f5a4e37747269655f6462397472696564626d7574313456616c7565244c54244c244754243132696e746f5f656e636f6465643137686538376363653739363963336634313745f805495f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313063616368655f6e6f64653137686162353930313831623263353964656145f905495f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313063616368655f6e6f64653137686663386432393566666137333561323145fa05675f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313063616368655f6e6f646532385f24753762242475376224636c6f73757265247537642424753764243137683265663263623537366430643733386145fb05675f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313063616368655f6e6f646532385f24753762242475376224636c6f73757265247537642424753764243137683337636262633562383862303162353545fc05645f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313163616368655f76616c756531376837313233373464653531363639303734452e6c6c766d2e3132353235333233373335393830383932393830fd05645f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313163616368655f76616c756531376866363836353265646465326565656533452e6c6c766d2e3132353235333233373335393830383932393830fe05685f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313163616368655f76616c756532385f24753762242475376224636c6f73757265247537642424753764243137683037323834356562616665386165313345ff05655f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243132636f6d6d69745f6368696c6431376830306632306263363037383764613466452e6c6c766d2e31323532353332333733353938303839323938308006655f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243132636f6d6d69745f6368696c6431376838383664646632303934613566336332452e6c6c766d2e313235323533323337333539383038393239383081064f5f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243136696e736572745f696e73706563746f7231376836366239646534333233626166333133458206615f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542439696e736572745f617431376838636334643565343562653932623463452e6c6c766d2e31323532353332333733353938303839323938308306505f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542431377265706c6163655f6f6c645f76616c7565313768333939303033613033626133346136614584064f5f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243136696e736572745f696e73706563746f7231376862633336326561346630373438316335458506615f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542439696e736572745f617431376832653364316163643233663632346265452e6c6c766d2e31323532353332333733353938303839323938308606435f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542435636163686531376833663462363761383336366439353332458706435f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542435636163686531376837626630383935356361643930646233458806475f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424396669785f696e6e657231376831313930393666356261646130343139458906475f5a4e37747269655f6462397472696564626d757432304e6f646553746f72616765244c54244c244754243764657374726f7931376830323961386361326564336430356130458a06475f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424396669785f696e6e657231376832343966613334316432623030613265458b06475f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243972656d6f76655f617431376839656233356565363964636565323431458c06655f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243972656d6f76655f617432385f24753762242475376224636c6f737572652475376424247537642431376839393937613630346436633430326131458d06475f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243972656d6f76655f617431376866316265663065343338656636363937458e06655f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243972656d6f76655f617432385f24753762242475376224636c6f737572652475376424247537642431376833333462383665633563373463656266458f066e5f5a4e38305f244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c54244c24475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f70313768303964313966653730363430303536614590066e5f5a4e38305f244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c54244c24475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f7031376832316434383430366465626333383034459106745f5a4e38345f244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c54244c24475424247532302461732475323024747269655f64622e2e547269654d7574244c54244c244754242447542436696e7365727431376830623661623133663239343937303336459206745f5a4e38345f244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c54244c24475424247532302461732475323024747269655f64622e2e547269654d7574244c54244c244754242447542436696e7365727431376839373065633661633839393362643037459306745f5a4e38345f244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c54244c24475424247532302461732475323024747269655f64622e2e547269654d7574244c54244c24475424244754243672656d6f766531376835653539306337323764653036313034459406745f5a4e38345f244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c54244c24475424247532302461732475323024747269655f64622e2e547269654d7574244c54244c24475424244754243672656d6f7665313768393535313039363030336432376539324595064b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376830313337653736373364626637396536452e6c6c766d2e373231323330373831363737383531343238329606475f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243131616c6c6f636174655f696e313768363630626337313239343632363531304597064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f70757368313768306162373132643039383663356334394598064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f70757368313768306265396162623836346664663363314599064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376830636235633630343066303966313063459a064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376832653564616235616564623762313331459b064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376833323066303766656662626238323235459c064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376835633436323064323638336438393734459d064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376836323732323030636134303366663030459e064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376837363433633734393236643730623861459f064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683762616437653238323238633665346345a0064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683765643733656235313336633831646445a1064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683932633064333965313838646431623645a2064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686134653466303530626138363463653245a3064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686235353838323331373839613733383145a4064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686237353639643761333830376666393845a5064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686431363935336130666339333661633645a6064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686534653730666433376639373266353945a706595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683038393338346566313364356235326145a806595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683364393366373138626563306465313645a906595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683461336639666434343161646161333145aa06595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683661346466393932333536616435663845ab06595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683736623762316263306637343466353145ac06595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686138303937353232316662653730666645ad06595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686633613163643730303131346665656145ae063b5f5a4e313073657264655f6a736f6e337365723138666f726d61745f657363617065645f7374723137683361373464336331393330633930666245af069e015f5a4e313073657264655f6a736f6e3576616c75653373657237365f244c5424696d706c247532302473657264652e2e7365722e2e53657269616c697a652475323024666f72247532302473657264655f6a736f6e2e2e76616c75652e2e56616c7565244754243973657269616c697a6531376831313664616335383937343963643837452e6c6c766d2e37363631343238303731363137323835383130b0063a5f5a4e35736572646533736572313053657269616c697a65723131636f6c6c6563745f7365713137683133353762633362323835306432663045b1064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683132666365376231656564653232666145b2064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683230373162613062656163663535303445b3064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683536643439346163666638363764346445b4064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683538343031626231626463653036396145b5064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683561356331373936313262323031343545b6064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683562623962343566333832646635393245b7064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683661616535636132643264633932376345b8064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683662616232653132303166333237666345b9064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683665386536626630326630373362666245ba064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683738346335386331376133353736316545bb064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683739323661616134386363643036613745bc064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683830353233656636636437623733653645bd064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683863323861616534303636643663323445be064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683930383935616563303935373864663345bf064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683931613263373161643161643864633645c0064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686232326631666433353362663132363445c1064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686333353230636466376261623963336245c2064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686338306565336333666533366263303145c3064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686434316631306437613065383037646645c4064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686439353231353162616536366439616645c5064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686534303439333636656463623433666145c6064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686536323061346530353364356363373445c7064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686635323730656166663837333362313845c8064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686635393035366339626265653334383045c9064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686636613136656531613638313537623245ca06d1015f5a4e3137335f244c542473705f636f6e73656e7375735f626162652e2e5f2e2e244c5424696d706c247532302473657264652e2e64652e2e446573657269616c697a652475323024666f72247532302473705f636f6e73656e7375735f626162652e2e416c6c6f776564536c6f7473244754242e2e646573657269616c697a652e2e5f5f4669656c6456697369746f7224753230246173247532302473657264652e2e64652e2e56697369746f72244754243976697369745f7374723137686535653030346637306539383362663445cb068f015f5a4e313773705f636f6e73656e7375735f62616265315f38345f244c5424696d706c247532302473657264652e2e64652e2e446573657269616c697a652475323024666f72247532302473705f636f6e73656e7375735f626162652e2e416c6c6f776564536c6f7473244754243131646573657269616c697a653137683335346631326638353935333339303245cc063f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683232343336396233623134303639383445cd063f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683433623233306631373331613862303245ce063f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683738373230343137336162356233306645cf06465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683636393539613139633137323664633045d006465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683964356432353266656631633537313545d1066e5f5a4e34636f726533707472353164726f705f696e5f706c616365244c542473705f73746174655f6d616368696e652e2e44656661756c744572726f722447542431376836656330653864613137386438613066452e6c6c766d2e37363631343238303731363137323835383130d2063a5f5a4e35736572646533736572313053657269616c697a65723131636f6c6c6563745f7365713137686437316136363935396137363431373845d306405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683234666165366264343161343930363145d4069a015f5a4e38355f244c542473657264655f6a736f6e2e2e7365722e2e436f6d706f756e64244c542457244324462447542424753230246173247532302473657264652e2e7365722e2e53657269616c697a655475706c6524475424313773657269616c697a655f656c656d656e7431376831336565366338666134346665393839452e6c6c766d2e37363631343238303731363137323835383130d506405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683330623833333533343937663866323545d606405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683463383137313865363032333565396345d706405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683564666263626438663537656634623445d806405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683761306531386263323134653235653445d906405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683839636265363364366363313735353245da06405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683864386537396663373331316338363545db06405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137686136333239613263326631396634626445dc06405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137686433633461386665383831316137623745dd06795f5a4e36375f244c542473705f73746174655f6d616368696e652e2e44656661756c744572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376837323136666635326339346461646539452e6c6c766d2e37363631343238303731363137323835383130de06745f5a4e38365f244c5424616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683865393535343531636433333931383345df067c5f5a4e39315f244c542473705f73746174655f6d616368696e652e2e6578742e2e457874244c542448244324422447542424753230246173247532302473705f65787465726e616c69746965732e2e45787465726e616c6974696573244754243773746f726167653137683462623866386263643230653633356345e0066d5f5a4e31307363616c655f696e666f35696d706c7335365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424524624542447542439747970655f696e666f3137686163363436313539633264383139663145e106755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683334383232313130653038396134393145e20680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683432353362623338616438363837376245e306755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137686561393033386265643136343533343245e40680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683364383165356632393864623263343945e50680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683461303834306265616236343763633045e60680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683537623862643131386632643934613845e70680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683563316434656139616164356662646345e80680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683563346236393839616366316131633445e90680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683665666433666139653236373961316445ea0680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683730623533323366643962393633633945eb0680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683730633831323938633663316361643045ec0680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683839323538666433373663333337373945ed0680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683839353761353561386337616333343745ee0680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686162363435393230383137336363616545ef0680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686332636530326631316437303131663345f00680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686436623861383765373630623062363545f10680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686530333934326465386238383766613045f20680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686535663763393635316265303039373045f30680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686539343565303536383833643830363645f40680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686636383039303163383230646436316645f50680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686661306432313763316562383265646645f606a6015f5a4e3133335f244c5424736d616c6c7665632e2e536d616c6c566563244c54244124475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e636f6c6c6563742e2e457874656e64244c5424244c542441247532302461732475323024736d616c6c7665632e2e4172726179244754242e2e4974656d244754242447542436657874656e643137686630633639663932326163376438393945f706565f5a4e38736d616c6c7665633137536d616c6c566563244c54244124475424387472795f67726f7731376835376661373337366334356335393730452e6c6c766d2e3130393532323235313434363530373438323637f8064a5f5a4e38736d616c6c7665633137536d616c6c566563244c542441244754243231726573657276655f6f6e655f756e636865636b65643137683439646637656438363939323432666245f906ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683135323864363439626563323331383545fa06ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683666623739323031363866326231386445fb06ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683734636434646662633365313634343145fc069b015f5a4e31387061726974795f7363616c655f636f64656335636f6465633136696e6e65725f7475706c655f696d706c37395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f722475323024244c5024513024432452302452502424475424366465636f64653137683836646437666566363630643965386545fd069b015f5a4e31387061726974795f7363616c655f636f64656335636f6465633136696e6e65725f7475706c655f696d706c37395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f722475323024244c5024513024432452302452502424475424366465636f64653137686633616262303864656430333335343145fe069e015f5a4e31387061726974795f7363616c655f636f64656335636f6465633136696e6e65725f7475706c655f696d706c37395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f722475323024244c502451302443245230245250242447542439656e636f64655f746f3137683563313861623532383737386263373145ff066d5f5a4e34636f726533707472343964726f705f696e5f706c616365244c5424736d616c6c7665632e2e436f6c6c656374696f6e416c6c6f634572722447542431376833666264306261303836613233333534452e6c6c766d2e313039353232323531343436353037343832363780075a5f5a4e35355f244c5424582475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f313768313837613332646335353536363537644581075a5f5a4e35355f244c5424582475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f313768363134353735376263393666653137384582075a5f5a4e35355f244c5424582475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f31376866383036646135353233356165386634458307465f5a4e35616c6c6f63337665633136566563244c54245424432441244754243137657874656e645f66726f6d5f736c69636531376835386436306262626438306365636634458407ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f6974657231376838366430626338303239393733343035458507625f5a4e36305f244c5424245246246d7574247532302454247532302461732475323024627335382e2e6465636f64652e2e4465636f64655461726765742447542431316465636f64655f77697468313768363938346436393533666632373435624586075e5f5a4e36355f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376838326633383830343432666162373935458707785f5a4e36355f244c5424736d616c6c7665632e2e436f6c6c656374696f6e416c6c6f63457272247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376833333136353566633965353230393634452e6c6c766d2e31303935323232353134343635303734383236378807785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d697431376833353766323864373430376338666132458907785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d697431376834306564306261623664643838613964458a07625f5a4e36375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e6531376830613133303836323936373939623162458b07625f5a4e36375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e6531376833303032623036633430666461313233458c07625f5a4e36375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e6531376861333334663465336539326366646530458d07625f5a4e36375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e6531376864353761393339363337663532343466458e07635f5a4e36395f244c5424736d616c6c7665632e2e536d616c6c566563244c54244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f7031376865333561626530343936313131376166458f07645f5a4e37305f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f7031376836353938316562653430626633323837459007645f5a4e37305f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f7031376838383938623038633564333539643731459107645f5a4e37305f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f70313768393733393934663265303735616331314592076e5f5a4e37325f244c5424616c6c6f632e2e7665632e2e566563244c5424753824475424247532302461732475323024627335382e2e656e636f64652e2e456e636f6465546172676574244754243131656e636f64655f77697468313768636338313364316636326463303937334593076e5f5a4e37385f244c5424616c6c6f632e2e7665632e2e566563244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f6465313768376163333139353063613339383532394594076e5f5a4e37385f244c5424616c6c6f632e2e7665632e2e566563244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f646531376864303036616366333634393034633837459507395f5a4e3773705f7472696531316e6f64655f68656164657231316465636f64655f73697a65313768383538353837336231616233373036334596076f5f5a4e38305f244c5424736d616c6c7665632e2e536d616c6c566563244c54244124475424247532302461732475323024636f72652e2e6f70732e2e696e6465782e2e496e646578244c542449244754242447542435696e64657831376834623132313564373666326538663630459707765f5a4e38365f244c542473705f747269652e2e6e6f64655f6865616465722e2e4e6f64654865616465722475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f646531376866326532623830316361643465373138459807795f5a4e38365f244c542473705f747269652e2e6e6f64655f6865616465722e2e4e6f64654865616465722475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686362323764343539666137623939373945990787015f5a4e39375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f657874656e642e2e53706563457874656e64244c5424542443244924475424244754243131737065635f657874656e6431376832666337393939646230643131366535459a0787015f5a4e39375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f657874656e642e2e53706563457874656e64244c5424542443244924475424244754243131737065635f657874656e6431376865353762633734333463373832613732459b0785015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f6974657231376832356664626462343531623738633036459c0785015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f6974657231376832633736393065646566623836343338459d0785015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f6974657231376839353935366534663861303965666432459e0785015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f6974657231376863623331393938643433633537623762459f0785015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f697465723137686437313433393938616462323538643745a0079c015f5a4e3132305f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e7365742e2e4254726565536574244c54245424475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e636f6c6c6563742e2e46726f6d4974657261746f72244c54245424475424244754243966726f6d5f697465723137683865393137613335386432633634313145a1076d5f5a4e31307363616c655f696e666f35696d706c7335365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424524624542447542439747970655f696e666f3137686465303439376265633665313438623445a207755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683032343032303466343961646362366345a307755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683338616562623263383631333330393945a407755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683465383931393834353164373436663145a507755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137686135393864323635336666633139646245a607755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137686265616265663563626436326663386545a7077e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137683837373365663339663464326662633845a8077e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137686266663934386536613364636431636245a907ba015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3131345f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e56616c69645472616e73616374696f6e2447542439656e636f64655f746f3137683932643632336634356433613163656445aa07c2015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3132325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e5472616e73616374696f6e56616c69646974794572726f722447542439656e636f64655f746f3137683866663031363537666337336132363545ab07bf015f5a4e3131315f244c54246672616d655f73797374656d2e2e70616c6c65742e2e43616c6c244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e64697370617463682e2e556e66696c7465726564446973706174636861626c6524475424323264697370617463685f6279706173735f66696c74657232385f24753762242475376224636c6f73757265247537642424753764243137686235383865363334323538373038363445ac0790015f5a4e3131325f244c542470616c6c65745f626162652e2e70616c6c65742e2e47656e65736973436f6e666967244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4275696c6447656e65736973436f6e66696724475424356275696c643137683037613262636133366132616266616445ad07aa015f5a4e3131335f244c542470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4265666f7265416c6c52756e74696d654d6967726174696f6e732447542432396265666f72655f616c6c5f72756e74696d655f6d6967726174696f6e733137683065346136626334393135383062613245ae078a015f5a4e38345f244c54246672616d655f737570706f72742e2e7472616974732e2e6d657461646174612e2e53746f7261676556657273696f6e247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863323937656563623863373563383766452e6c6c766d2e39343631363039353438343436323532303735af07c6015f5a4e3131385f244c542470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c54245424432449244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e64697370617463682e2e556e66696c7465726564446973706174636861626c6524475424323264697370617463685f6279706173735f66696c74657232385f24753762242475376224636c6f73757265247537642424753764243137686433326539663565346335653235353845b0077d5f5a4e36395f244c5424616c6c6f632e2e626f7865642e2e426f78244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e6531376831376565303832613762326463353736452e6c6c766d2e39343631363039353438343436323532303735b107685f5a4e313170616c6c65745f6261626535345f244c5424696d706c247532302470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c542454244754242447542431306e6578745f65706f63683137683136663632663231306531353732396345b2076b5f5a4e313170616c6c65745f6261626535345f244c5424696d706c247532302470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c5424542447542424475424313363757272656e745f65706f63683137683963363535383962366139353862666545b307705f5a4e313170616c6c65745f6261626535345f244c5424696d706c247532302470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c54245424475424244754243138656e6163745f65706f63685f6368616e67653137683039336432646335303061633739393345b407715f5a4e313170616c6c65745f6261626535345f244c5424696d706c247532302470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c5424542447542424475424313973686f756c645f65706f63685f6368616e67653137686239306363633831366239653838646245b5074e5f5a4e313170616c6c65745f626162653670616c6c6574313550616c6c6574244c54245424475424313673746f726167655f6d657461646174613137683235626233396134643839356236613245b607575f5a4e313170616c6c65745f626162653670616c6c6574313550616c6c6574244c54245424475424323570616c6c65745f636f6e7374616e74735f6d657461646174613137686166333135303736303232313833373145b7078e015f5a4e313170616c6c65745f626162653670616c6c6574315f38355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f626162652e2e70616c6c65742e2e43616c6c244c542454244754242447542439747970655f696e666f3137683339313262653331316166316435386545b8078f015f5a4e313170616c6c65745f626162653670616c6c6574315f38365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f626162652e2e70616c6c65742e2e4572726f72244c542454244754242447542439747970655f696e666f3137683666313137333538656566303830373145b90798015f5a4e313170616c6c65745f626162653670616c6c6574315f39355f244c5424696d706c247532302473657264652e2e7365722e2e53657269616c697a652475323024666f72247532302470616c6c65745f626162652e2e70616c6c65742e2e47656e65736973436f6e666967244c54245424475424244754243973657269616c697a653137686263666137313832383731356563623545ba0798015f5a4e313170616c6c65745f626162653670616c6c6574315f39385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302470616c6c65745f626162652e2e70616c6c65742e2e43616c6c244c5424542447542424475424366465636f64653137683135303438653062653630326135656445bb079b015f5a4e313170616c6c65745f626162653670616c6c6574315f39385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f626162652e2e70616c6c65742e2e43616c6c244c542454244754242447542439656e636f64655f746f3137683539336663663337626663366532663545bc079b015f5a4e313170616c6c65745f626162653670616c6c6574315f39385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f626162652e2e70616c6c65742e2e43616c6c244c54245424475424244754243973697a655f68696e743137683964663930363632653766356161363045bd079c015f5a4e3132305f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e7365742e2e4254726565536574244c54245424475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e636f6c6c6563742e2e46726f6d4974657261746f72244c54245424475424244754243966726f6d5f697465723137686236353537653230613766343632663745be07aa015f5a4e3133385f244c542473705f72756e74696d652e2e67656e657269632e2e636865636b65645f65787472696e7369632e2e436865636b656445787472696e736963244c54244163636f756e74496424432443616c6c24432445787472612447542424753230246173247532302473705f72756e74696d652e2e7472616974732e2e4170706c7961626c6524475424356170706c793137686239633935636431396265636339316645bf07735f5a4e34636f726533707472353664726f705f696e5f706c616365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c2447542431376865643735326330326539353332656332452e6c6c766d2e39343631363039353438343436323532303735c007ad015f5a4e3133385f244c542473705f72756e74696d652e2e67656e657269632e2e636865636b65645f65787472696e7369632e2e436865636b656445787472696e736963244c54244163636f756e74496424432443616c6c24432445787472612447542424753230246173247532302473705f72756e74696d652e2e7472616974732e2e4170706c7961626c65244754243876616c69646174653137686264386666643132653563316166666245c107485f5a4e3133656e7669726f6e6d656e74616c396c6f63616c5f6b657931374c6f63616c4b6579244c5424542447542434776974683137683232663162323039353639636464666445c207485f5a4e3133656e7669726f6e6d656e74616c396c6f63616c5f6b657931374c6f63616c4b6579244c5424542447542434776974683137683532316362643865313434646331653845c307485f5a4e3133656e7669726f6e6d656e74616c396c6f63616c5f6b657931374c6f63616c4b6579244c5424542447542434776974683137686136353839623338326365336532303545c407e0015f5a4e3134345f244c54247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e43616c6c244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e64697370617463682e2e556e66696c7465726564446973706174636861626c6524475424323264697370617463685f6279706173735f66696c74657232385f24753762242475376224636c6f73757265247537642424753764243137686161626165313833653136633736316645c507485f5a4e3133656e7669726f6e6d656e74616c396c6f63616c5f6b657931374c6f63616c4b6579244c5424542447542434776974683137686530623431396530386539343037623345c6074a5f5a4e31336672616d655f737570706f7274323267656e657369735f6275696c6465725f68656c70657231306765745f7072657365743137686636343962323165643633353662323645c7074f5f5a4e34636f726533707472343564726f705f696e5f706c616365244c542473657264655f6a736f6e2e2e76616c75652e2e56616c7565244754243137683733356637666563393664656465613845c807645f5a4e3773705f636f72653663727970746f3953733538436f6465633235746f5f73733538636865636b5f776974685f76657273696f6e31376865323637376132633136346338303136452e6c6c766d2e39343631363039353438343436323532303735c9074b5f5a4e31336672616d655f737570706f7274323267656e657369735f6275696c6465725f68656c70657231316275696c645f73746174653137686435306265353165646563633930373045ca07535f5a4e31336672616d655f737570706f7274367472616974733773746f72616765313553746f72616765496e7374616e636531317072656669785f686173683137683132653939646132623161373536653145cb07535f5a4e31336672616d655f737570706f7274367472616974733773746f72616765313553746f72616765496e7374616e636531317072656669785f686173683137683937363037303730336132303835656145cc07a3015f5a4e31387061726974795f7363616c655f636f64656335636f6465633136696e6e65725f7475706c655f696d706c38345f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f722475323024244c5024503024432451302443245230245250242447542439656e636f64655f746f3137683631313139623830623437383439313745cd07475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f6465643137683261396461636530663964633761393245ce073f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683965663864643633313532343734653445cf07bd025f5a4e3237385f244c542470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4f6e46696e616c697a65244c5424244c5424244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e426c6f636b24753230246173247532302473705f72756e74696d652e2e7472616974732e2e48656164657250726f7669646572244754242e2e4865616465725424753230246173247532302473705f72756e74696d652e2e7472616974732e2e486561646572244754242e2e4e756d626572244754242447542431316f6e5f66696e616c697a653137683130663065333535613565633034383145d007c1025f5a4e3238305f244c542470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4f6e496e697469616c697a65244c5424244c5424244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e426c6f636b24753230246173247532302473705f72756e74696d652e2e7472616974732e2e48656164657250726f7669646572244754242e2e4865616465725424753230246173247532302473705f72756e74696d652e2e7472616974732e2e486561646572244754242e2e4e756d626572244754242447542431336f6e5f696e697469616c697a653137683532323039643163636332623664366245d107465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137686137306565646538373033376166626445d207475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683265336435616636383537353430386545d307475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683434613634303666316265313261353745d407475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683764613033323564633637633134313445d507475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686266616664653532306364383336663945d607475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686466346237373361346665623739633145d707635f5a4e34355f244c5424244c502424525024247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376834303566303661613534323663666363452e6c6c766d2e39343631363039353438343436323532303735d80797015f5a4e34636f72653370747231313664726f705f696e5f706c616365244c5424245246242452462473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c542433325f7573697a6524432473705f636f72652e2e737232353531392e2e537232353531395075626c696354616724475424244754243137686433343161303565316536303537616445d907585f5a4e34636f726533707472323964726f705f696e5f706c616365244c5424244c5024245250242447542431376830653433316634653265363962306630452e6c6c766d2e39343631363039353438343436323532303735da074f5f5a4e34636f726533707472343564726f705f696e5f706c616365244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f72244754243137686534363938663030393533343532613145db075e5f5a4e36355f244c542473705f636f72652e2e63727970746f2e2e5075626c69634572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686237303639316163613335313462346345dc07785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d69743137683039616266633131396433323330373245dd076d5f5a4e37355f244c5424747269655f64622e2e7472696564622e2e547269654442244c54244c24475424247532302461732475323024747269655f64622e2e54726965244c54244c2447542424475424386765745f776974683137683766356261393066306137383936633945de07bc015f5a4e3773705f636f726537737232353531393133335f244c5424696d706c247532302473657264652e2e64652e2e446573657269616c697a652475323024666f72247532302473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c696354616724475424244754243131646573657269616c697a653137686262303665643533353333663139393445df0799015f5a4e3773705f636f7265377372323535313933767266315f39395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302473705f636f72652e2e737232353531392e2e7672662e2e5672665369676e617475726524475424366465636f64653137686236636666643737393065363830636445e0079c015f5a4e3773705f636f7265377372323535313933767266315f39395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f636f72652e2e737232353531392e2e7672662e2e5672665369676e61747572652447542439656e636f64655f746f3137683438356234383663366364306433633545e107445f5a4e37747269655f6462367472696564623135547269654442244c54244c24475424313166657463685f76616c75653137686133303638623636643838303530363445e2074a5f5a4e37747269655f6462367472696564623135547269654442244c54244c2447542431376765745f7261775f6f725f6c6f6f6b75703137683839396361353734613134393163633545e3077b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683538303231336363326563643032306445e4078b015f5a4e39355f244c542470616c6c65745f626162652e2e70616c6c65742e2e43616c6c244c542454244754242475323024617324753230246672616d655f737570706f72742e2e64697370617463682e2e4765744469737061746368496e666f2447542431376765745f64697370617463685f696e666f3137683964383838393334366463666261333945e507745f5a4e36325f244c542473705f72756e74696d652e2e44697370617463684572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832343537376561373336343862333634452e6c6c766d2e34343635333131303635343831343136313531e60789015f5a4e31307363616c655f696e666f35696d706c7338345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e726573756c742e2e526573756c74244c54245424432445244754242447542439747970655f696e666f3137683237633234346263613464343563366445e70789015f5a4e31307363616c655f696e666f35696d706c7338345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e726573756c742e2e526573756c74244c54245424432445244754242447542439747970655f696e666f3137683338633839336262326233353965646145e80789015f5a4e31307363616c655f696e666f35696d706c7338345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e726573756c742e2e526573756c74244c54245424432445244754242447542439747970655f696e666f3137686464616434303265373165386364363345e90789015f5a4e31307363616c655f696e666f35696d706c7338345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e726573756c742e2e526573756c74244c54245424432445244754242447542439747970655f696e666f3137686666613866383636633261373263306445ea074a5f5a4e313073705f72756e74696d6536747261697473313656616c6964617465556e7369676e656431327072655f64697370617463683137683261623537386336646337666366393745eb07ac015f5a4e3132375f244c54247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e50616c6c6574244c5424542447542424753230246173247532302473705f72756e74696d652e2e7472616974732e2e56616c6964617465556e7369676e656424475424313776616c69646174655f756e7369676e65643137683365306535333762383466316433333445ec07a2015f5a4e3132385f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e547269654261636b656e64244c5424532443244824432443244324522447542424753230246173247532302473705f73746174655f6d616368696e652e2e6261636b656e642e2e4261636b656e64244c54244824475424244754243773746f726167653137683134306438336137373938393464326145ed0782015f5a4e34636f726533707472373164726f705f696e5f706c616365244c542473705f747269652e2e6572726f722e2e4572726f72244c54247072696d69746976655f74797065732e2e48323536244754242447542431376834613633653065376463303532303565452e6c6c766d2e34343635333131303635343831343136313531ee07ad015f5a4e3132395f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e7465645265636f7264657250726f7669646572244c5424482447542424753230246173247532302473705f747269652e2e547269655265636f7264657250726f7669646572244c5424482447542424475424313661735f747269655f7265636f726465723137683936626163303934663037653363613045ef079a015f5a4e31326672616d655f73797374656d315f3130325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f73797374656d2e2e4576656e745265636f7264244c54244524432454244754242447542439656e636f64655f746f3137686534346435323937393632363336383645f007b4015f5a4e31326672616d655f73797374656d315f3130335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e4163636f756e74496e666f244c54244e6f6e63652443244163636f756e7444617461244754242447542439747970655f696e666f31376861356339616239386635333233366263452e6c6c766d2e34343635333131303635343831343136313531f1078c015f5a4e31326672616d655f73797374656d315f38395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e4576656e745265636f7264244c54244524432454244754242447542439747970655f696e666f3137683561633331363564313635393565336645f20795015f5a4e31326672616d655f73797374656d315f39385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e436f646555706772616465417574686f72697a6174696f6e244c542454244754242447542439747970655f696e666f3137683263626564643639373935336164303745f30790015f5a4e31336672616d655f737570706f72743773746f72616765357479706573336d6170383153746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754243474616b653137683365343230316431333031303335653145f40799015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542431307472795f6d75746174653137683232613938646165323437376236366145f507605f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376864346531326335346534623064376538452e6c6c766d2e34343635333131303635343831343136313531f60799015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542431307472795f6d75746174653137686463656538666461326632666138306345f707a0015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542431377472795f6d75746174655f6578697374733137686531333834333538366532383863653945f80791015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424336765743137683030393039326162616232333938336245f90791015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424336765743137683132633739353633636264663864376345fa0791015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424336765743137683339343634353436316266393466336645fb0791015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424336765743137683561343934343838353335343735636445fc0792015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f72247532302447244754243474616b653137683535316530633332616638393233626245fd0794015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542436696e736572743137683366313030366364656537626234313345fe0794015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542436696e736572743137683533336434626134386462303233353745ff0794015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542436696e736572743137686462616363396637376564623432626145800894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542436696e736572743137686531336639613466333237663362656145810894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542436696e736572743137686562346335363162333939376639313045820894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424366d75746174653137683263323638653437306261303935363645830894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424366d75746174653137683365663539343766363638326535633445840894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424366d75746174653137683435373838366462343039353536336445850894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f72247532302447244754243672656d6f76653137683935636234333435313032666538393345860894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f72247532302447244754243672656d6f766531376839663430653730363033386464666339458708be015f5a4e3134365f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c5424482447542424753230246173247532302473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e54726965436163686550726f7669646572244c5424482447542424475424313661735f747269655f64625f636163686531376838343439306132336634343234613262458808c2015f5a4e3134365f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c5424482447542424753230246173247532302473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e54726965436163686550726f7669646572244c5424482447542424475424323061735f747269655f64625f6d75745f636163686531376861613462396464383762376235653361458908c7015f5a4e3135335f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c54244824475424247532302461732475323024747269655f64622e2e547269654361636865244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424244754242447542431386765745f6f725f696e736572745f6e6f646531376835626133383363343466666534366663458a08c8015f5a4e3135335f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c54244824475424247532302461732475323024747269655f64622e2e547269654361636865244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c542448244754242447542424475424313963616368655f76616c75655f666f725f6b657931376863656663303636346664386465346366458b08c9015f5a4e3135335f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c54244824475424247532302461732475323024747269655f64622e2e547269654361636865244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424244754242447542432306c6f6f6b75705f76616c75655f666f725f6b657931376838363066376566333836626339386531458c08bc015f5a4e3135335f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c54244824475424247532302461732475323024747269655f64622e2e547269654361636865244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c542448244754242447542424475424386765745f6e6f646531376866616537653961643963663838623238458d08d8015f5a4e3136315f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e7465645265636f7264657250726f7669646572244c54244824475424247532302461732475323024747269655f64622e2e547269655265636f72646572244c5424244c542448247532302461732475323024686173685f64622e2e486173686572244754242e2e4f757424475424244754243237747269655f6e6f6465735f7265636f726465645f666f725f6b657931376865643932643636643639633037653066458e08c2015f5a4e3136315f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e7465645265636f7264657250726f7669646572244c54244824475424247532302461732475323024747269655f64622e2e547269655265636f72646572244c5424244c542448247532302461732475323024686173685f64622e2e486173686572244754242e2e4f75742447542424475424367265636f726431376862353435343464666462323435643339458f08645f5a4e313673705f73746174655f6d616368696e6531376f7665726c617965645f6368616e67657332354f7665726c617965644368616e676573244c5424482447542431317365745f73746f7261676531376864333738646666366564376433343736459008655f5a4e313673705f73746174655f6d616368696e6531376f7665726c617965645f6368616e67657332354f7665726c617965644368616e676573244c54244824475424313273746f726167655f726f6f74313768383265626636626635666638623639634591085f5f5a4e313673705f73746174655f6d616368696e6531376f7665726c617965645f6368616e67657332354f7665726c617965644368616e676573244c542448244754243773746f7261676531376861346266363131363634356331623036459208475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376835653063643533316231623536346432459308475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376862343135656238373732383666333231459408475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376863313364623435363063653135323366459508585f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376836386231353732613461613337306561452e6c6c766d2e3434363533313130363534383134313631353196083f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376864353565386236353361656636306465459708ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376830393738326539643064303330383564459808ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376831363363656566303465366439633330459908cd015f5a4e3139626f756e6465645f636f6c6c656374696f6e733131626f756e6465645f766563315f3130385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e626f756e6465645f7665632e2e426f756e646564566563244c54245424432453244754242447542439747970655f696e666f31376862336661666136623733383239343334452e6c6c766d2e343436353331313036353438313431363135319a08ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376831633132303730623963323465343636459b08ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376835663236666438376163623639386537459c08ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376836613032303264336364396638353538459d08cd015f5a4e3139626f756e6465645f636f6c6c656374696f6e733131626f756e6465645f766563315f3130385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e626f756e6465645f7665632e2e426f756e646564566563244c54245424432453244754242447542439747970655f696e666f31376831313432386235313165633561633762452e6c6c766d2e343436353331313036353438313431363135319e08ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376837316230363031343636396137323261459f08cd015f5a4e3139626f756e6465645f636f6c6c656374696f6e733131626f756e6465645f766563315f3130385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e626f756e6465645f7665632e2e426f756e646564566563244c54245424432453244754242447542439747970655f696e666f31376832396533346563346531636436636164452e6c6c766d2e34343635333131303635343831343136313531a008ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d657461646174613137686165373162666362343635373764376445a108ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d657461646174613137686533653833326139326662363061623445a208cd015f5a4e3139626f756e6465645f636f6c6c656374696f6e733131626f756e6465645f766563315f3130385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e626f756e6465645f7665632e2e426f756e646564566563244c54245424432453244754242447542439747970655f696e666f31376838313839363565613438303932343866452e6c6c766d2e34343635333131303635343831343136313531a308b4015f5a4e3139626f756e6465645f636f6c6c656374696f6e733131626f756e6465645f766563315f3130385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e626f756e6465645f7665632e2e426f756e646564566563244c54245424432453244754242447542439747970655f696e666f3137683632326164343332663330346262396345a408a3015f5a4e323073705f636f6e73656e7375735f6772616e647061315f3130335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f6772616e6470612e2e45717569766f636174696f6e50726f6f66244c5424482443244e244754242447542439747970655f696e666f3137683538356338616330396335303332313945a5089d015f5a4e323073705f636f6e73656e7375735f6772616e647061315f39385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f6772616e6470612e2e45717569766f636174696f6e244c5424482443244e244754242447542439747970655f696e666f3137683363373265306238366130306461393645a608465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683032383039393935386265613032383945a708475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686563323865313866636363343834663345a808305f5a4e346273353836656e636f64653131656e636f64655f696e746f3137686130653937303161376663656634306445a908b2015f5a4e34636f72653370747231313864726f705f696e5f706c616365244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f323536244754242447542431376837343366356666663932653264303539452e6c6c766d2e34343635333131303635343831343136313531aa083e5f5a4e34636f726533707472323864726f705f696e5f706c616365244c542424524624753136244754243137683665616535333638616239373933636345ab086d5f5a4e34636f726535617272617936395f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f722475323024247535622454247533622424753230244e24753564242447542433666d743137683664633539656264356333613331306245ac086d5f5a4e34636f726535617272617936395f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f722475323024247535622454247533622424753230244e24753564242447542433666d743137686439653333636131656461373437343445ad086e5f5a4e35365f244c5424627335382e2e656e636f64652e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376862623965643035343061613830346334452e6c6c766d2e34343635333131303635343831343136313531ae08785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d69743137683136396539656638656531346431363845af08645f5a4e37315f244c5424636f72652e2e6d61726b65722e2e5068616e746f6d44617461244c54245424475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686437323734313730623666356231363245b008425f5a4e3773705f636f72653333746f5f7375627374726174655f7761736d5f666e5f72657475726e5f76616c75653137683464623033656131363732616134663545b108425f5a4e3773705f636f72653333746f5f7375627374726174655f7761736d5f666e5f72657475726e5f76616c75653137686231336232306537636632336662643445b208425f5a4e3773705f636f72653333746f5f7375627374726174655f7761736d5f666e5f72657475726e5f76616c75653137686635343031646464316132656463633445b308485f5a4e37747269655f6462386974657261746f7232365472696544425261774974657261746f72244c54244c24475424336e65773137683334353433623139373032613932333845b4084e5f5a4e37747269655f6462386974657261746f7232365472696544425261774974657261746f72244c54244c24475424396e6578745f6974656d3137686266303663353063386165373336303745b5088a015f5a4e38345f244c54246672616d655f737570706f72742e2e7472616974732e2e6d657461646174612e2e53746f7261676556657273696f6e247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863323937656563623863373563383766452e6c6c766d2e34343635333131303635343831343136313531b608775f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574313550616c6c6574244c5424542447542432336465706f7369745f6c6f675f6469676573745f6974656d3137686232386631323733356335653335383645b708635f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574313550616c6c6574244c5424542447542434726561643137683266363834653430643332626636633945b8086c5f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574313550616c6c6574244c542454244754243132657865637574655f726561643137686630343132346630393935653161316445b9086e5f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574313550616c6c6574244c542454244754243134726561645f616e645f70616e69633137683733613232323435393463353765653845ba08e0015f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574315f3133325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e43616c6c244c542454244754242447542439656e636f64655f746f3137686434366135336534363535303135373245bb08ae015f5a4e3132395f244c54247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e43616c6c244c542454244754242475323024617324753230246672616d655f737570706f72742e2e64697370617463682e2e4765744469737061746368496e666f2447542431376765745f64697370617463685f696e666f3137683862336231613264376162303635323945bc08705f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574313550616c6c6574244c54245424475424313673746f726167655f6d657461646174613137686337633866323336313830636435666145bd08cc015f5a4e3134375f244c54247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e50616c6c6574244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4265666f7265416c6c52756e74696d654d6967726174696f6e732447542432396265666f72655f616c6c5f72756e74696d655f6d6967726174696f6e733137686430646131386266373663636231393845be08dd015f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574315f3132395f244c5424696d706c247532302473657264652e2e7365722e2e53657269616c697a652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e47656e65736973436f6e666967244c54245424475424244754243973657269616c697a653137686465623536323735636339306430373645bf08cd015f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574315f3131375f244c5424696d706c2475323024636f72652e2e636c6f6e652e2e436c6f6e652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e43616c6c244c542454244754242447542435636c6f6e653137686637303937303464613631373164393245c008dd015f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574315f3133325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e43616c6c244c5424542447542424475424366465636f64653137683964643330356239383635653137653945c108d3015f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574315f3131395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e43616c6c244c542454244754242447542439747970655f696e666f3137683636616437663531306639613865663045c208755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683439633735333166633935653437343145c30892015f5a4e313666696e616c6974795f6772616e647061315f39315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302466696e616c6974795f6772616e6470612e2e507265636f6d6d6974244c5424482443244e244754242447542439747970655f696e666f3137683062383331356530383235316161616645c408755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683638363838646465613030663937626145c508755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137686135643337336631363435356132383645c608755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137686338333732643832633464313261646145c70890015f5a4e313666696e616c6974795f6772616e647061315f38395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302466696e616c6974795f6772616e6470612e2e507265766f7465244c5424482443244e244754242447542439747970655f696e666f3137683631316165633464653435636165613945c8087d5f5a4e31307363616c655f696e666f35696d706c7337325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c502441244324422443244324432444245250242447542439747970655f696e666f3137683134643531376233663132646366626245c908ba015f5a4e31326672616d655f73797374656d3130657874656e73696f6e733131636865636b5f6e6f6e6365315f3130395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c542454244754242447542439747970655f696e666f3137683966313231303161633862623464353545ca08b1015f5a4e32396672616d655f6d657461646174615f686173685f657874656e73696f6e315f3130385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c542454244754242447542439747970655f696e666f3137683563643234653436363135663765323145cb08bd015f5a4e31326672616d655f73797374656d3130657874656e73696f6e733132636865636b5f776569676874315f3131315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c542454244754242447542439747970655f696e666f3137683466633431303934656430363432646445cc088a015f5a4e313073705f72756e74696d65315f38395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f72756e74696d652e2e44697370617463684572726f722447542439656e636f64655f746f3137683965396532323862323839366165363845cd08b5015f5a4e3131365f244c54246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c5424542447542424753230246173247532302473705f72756e74696d652e2e7472616974732e2e5369676e6564457874656e73696f6e244754243133706f73745f646973706174636831376862343833663134383166613630616464452e6c6c766d2e363831373539343630333232373130383833ce0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137686636383238623939366237653363313445cf08745f5a4e38375f244c54246672616d655f737570706f72742e2e64697370617463682e2e5065724469737061746368436c617373244c54245424475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686338356638316434306262626633386345d0083b5f5a4e31336672616d655f737570706f72743773746f7261676538756e686173686564336765743137686633343130326665633334346565636145d1085c5f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247533322447542433666d743137683039633433313131313862343839303145d208a9015f5a4e313170616c6c65745f626162653670616c6c65743131385f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542470616c6c65745f626162652e2e70616c6c65742e2e4572726f72244c54245424475424244754242475323024666f72247532302473705f72756e74696d652e2e44697370617463684572726f72244754243466726f6d3137686264353735313430616262613433386345d3087e5f5a4e31326672616d655f73797374656d3130657874656e73696f6e733132636865636b5f7765696768743230436865636b576569676874244c542454244754243135646f5f7072655f646973706174636831376837313835366264363832363535663934452e6c6c766d2e363831373539343630333232373130383833d408aa015f5a4e3132355f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e74322443245475706c65456c656d656e74332452502424753230246173247532302473705f72756e74696d652e2e7472616974732e2e5369676e6564457874656e73696f6e2447542431376164646974696f6e616c5f7369676e65643137686562643666666362323432623862643745d5085f5f5a4e36365f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c54245424475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686436653932303764623531313734353745d608aa015f5a4e3132355f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e74322443245475706c65456c656d656e74332452502424753230246173247532302473705f72756e74696d652e2e7472616974732e2e5369676e6564457874656e73696f6e24475424313776616c69646174655f756e7369676e65643137683463633738646162303064633965346345d708695f5a4e31326672616d655f73797374656d3130657874656e73696f6e733132636865636b5f7765696768743230436865636b576569676874244c542454244754243138636865636b5f626c6f636b5f6c656e6774683137686434373666396639323539346662356345d8086d5f5a4e31326672616d655f73797374656d3130657874656e73696f6e733132636865636b5f7765696768743230436865636b576569676874244c542454244754243232636865636b5f65787472696e7369635f7765696768743137683536643330386534616638383864346445d908a0015f5a4e3132355f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e74322443245475706c65456c656d656e74332452502424753230246173247532302473705f72756e74696d652e2e7472616974732e2e5369676e6564457874656e73696f6e24475424386d657461646174613137686261663864356538616533386436393545da08a0015f5a4e3132355f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e74322443245475706c65456c656d656e74332452502424753230246173247532302473705f72756e74696d652e2e7472616974732e2e5369676e6564457874656e73696f6e244754243876616c69646174653137683864383663336665373630656435383345db08495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137686663313736633761623337386434396445dc08ab015f5a4e31326672616d655f73797374656d3670616c6c65743131395f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c54246672616d655f73797374656d2e2e70616c6c65742e2e4572726f72244c54245424475424244754242475323024666f72247532302473705f72756e74696d652e2e44697370617463684572726f72244754243466726f6d3137683439323434396438616365353764653145dd083b5f5a4e31336672616d655f737570706f72743773746f7261676538756e686173686564336765743137683036383136373332313164353262333345de087a5f5a4e36395f244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863636562313832636338343631643463452e6c6c766d2e363831373539343630333232373130383833df08715f5a4e36305f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376838643930316130303836663739383732452e6c6c766d2e363831373539343630333232373130383833e008535f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643367657431376831643061343633386265393631396331452e6c6c766d2e363831373539343630333232373130383833e1083b5f5a4e31336672616d655f737570706f72743773746f7261676538756e686173686564336765743137683233623431666266643334386335363145e208535f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643367657431376834313365393366653631653239646364452e6c6c766d2e363831373539343630333232373130383833e308495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683937303730333131386533396361383445e4083b5f5a4e31336672616d655f737570706f72743773746f7261676538756e686173686564336765743137683436336338663161383834343732396345e508535f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643367657431376835636432353630346332363666343430452e6c6c766d2e363831373539343630333232373130383833e608535f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643367657431376837623730613933656537623531353063452e6c6c766d2e363831373539343630333232373130383833e708535f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643367657431376863383637363566636335633065356464452e6c6c766d2e363831373539343630333232373130383833e808535f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643367657431376865333432316233616232383631316363452e6c6c766d2e363831373539343630333232373130383833e9083c5f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643474616b653137683437343139656634653239653162313845ea0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137683035663333613639353237336438323845eb0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137683061643832313565643832663138306245ec0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137683635316662383361346237356438663545ed0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137683665656138393966373463623063626245ee0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137683839376231313230643762336633383045ef0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137686136326566613532663665366534663545f00891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424337075743137683464303238656331373830313938316145f10891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424337075743137683630343239373738333034626265323745f20891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424337075743137686232386630373034616139633863323045f30891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424337075743137686464383666303565653339396436343945f40892015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f72247532302447244754243474616b653137686561616132653936363865643839366645f50894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f722475323024472447542436617070656e643137683762396565663239356265303763376645f60894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424366d75746174653137683765303134326562633665633461373445f70894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424366d75746174653137686539666164356566636562633866376545f808b5015f5a4e313570616c6c65745f62616c616e6365733670616c6c65743132365f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542470616c6c65745f62616c616e6365732e2e70616c6c65742e2e4572726f72244c5424542443244924475424244754242475323024666f72247532302473705f72756e74696d652e2e44697370617463684572726f72244754243466726f6d3137683565626264376664623235306333356445f908a5015f5a4e313666696e616c6974795f6772616e647061315f3131325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302466696e616c6974795f6772616e6470612e2e45717569766f636174696f6e244c5424496424432456244324532447542424475424366465636f64653137683438386439393231336237346338653345fa089a015f5a4e313666696e616c6974795f6772616e647061315f39395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302466696e616c6974795f6772616e6470612e2e45717569766f636174696f6e244c542449642443245624432453244754242447542439747970655f696e666f3137683162346235636266336365393564306545fb089a015f5a4e313666696e616c6974795f6772616e647061315f39395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302466696e616c6974795f6772616e6470612e2e45717569766f636174696f6e244c542449642443245624432453244754242447542439747970655f696e666f3137686434613034666430623365353738356345fc08d7015f5a4e3137335f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e76616c75652e2e53746f7261676556616c7565244c542450726566697824432456616c756524432451756572794b696e642443244f6e456d707479244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d657461646174613137683334636635333639306531323335363445fd08da015f5a4e3139626f756e6465645f636f6c6c656374696f6e7331367765616b5f626f756e6465645f766563315f3131375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e7765616b5f626f756e6465645f7665632e2e5765616b426f756e646564566563244c54245424432453244754242447542439747970655f696e666f31376833636465356335323837316365343634452e6c6c766d2e363831373539343630333232373130383833fe08d7015f5a4e3137335f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e76616c75652e2e53746f7261676556616c7565244c542450726566697824432456616c756524432451756572794b696e642443244f6e456d707479244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d657461646174613137683635653131633235303832396435613945ff08d7015f5a4e3137335f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e76616c75652e2e53746f7261676556616c7565244c542450726566697824432456616c756524432451756572794b696e642443244f6e456d707479244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376838663536326335373535366132393065458009d7015f5a4e3137335f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e76616c75652e2e53746f7261676556616c7565244c542450726566697824432456616c756524432451756572794b696e642443244f6e456d707479244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376861303465656238663230616331386466458109d7015f5a4e3137335f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e76616c75652e2e53746f7261676556616c7565244c542450726566697824432456616c756524432451756572794b696e642443244f6e456d707479244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376861666134363039313236646232313231458209d7015f5a4e3137335f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e76616c75652e2e53746f7261676556616c7565244c542450726566697824432456616c756524432451756572794b696e642443244f6e456d707479244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376865623265633538366362313934663265458309475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f646564313768613630613131333835366232303766364584093f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376866373263386264306633376532366462458509c2015f5a4e3139626f756e6465645f636f6c6c656374696f6e7331367765616b5f626f756e6465645f766563315f3131375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e7765616b5f626f756e6465645f7665632e2e5765616b426f756e646564566563244c54245424432453244754242447542439747970655f696e666f31376837303164363330393765353933386132458609475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376831313936306663346334646135633161458709475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376831623562656336363136343663626563458809475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376831643033326265636338323865306333458909475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832346662326634333933653362343162458a09475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832366333383762396631376139656363458b09475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832623735333133343339346638353237458c09475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376834353536346165356364626339663031458d09475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376835616131386563386630363065383432458e09475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376836323262373135613565383538363633458f09475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376837303231316436326463313864646464459009475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376838343635316636316232393039316464459109475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376839333934316436373961663662373832459209475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376839346165383636643238376263393861459309475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376861303161366163343165306366643533459409475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376861353032313234663738316561373966459509475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376861373365306230313439343134336238459609475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376862613137393863316561316362313731459709475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863396234613936653964613066323064459809475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376864636330323831373936336234646236459909475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376865333865346161343334646262613365459a09495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376838656231313430646437633330646531459b095b5f5a4e34636f726533666d74336e756d34395f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f72247532302475382447542433666d7431376838366432656134356430313966386636459c095c5f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247536342447542433666d7431376832643962636565363333316266326131459d0993015f5a4e34636f72653370747231313264726f705f696e5f706c616365244c5424245246246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242447542431376837393734336537333833613137326366459e09395f5a4e34636f726533707472323364726f705f696e5f706c616365244c542475382447542431376831363136316234616634343531613863459f093a5f5a4e34636f726533707472323464726f705f696e5f706c616365244c5424753634244754243137683064326333386435323663323762613145a009685f5a4e34636f726533707472343664726f705f696e5f706c616365244c5424616c6c6f632e2e7665632e2e566563244c54247538244754242447542431376838343231626263386233396462633533452e6c6c766d2e363831373539343630333232373130383833a1096b5f5a4e34636f726533707472343964726f705f696e5f706c616365244c5424616c6c6f632e2e737472696e672e2e46726f6d557466384572726f722447542431376836313730643138326564656234646266452e6c6c766d2e363831373539343630333232373130383833a2096f5f5a4e34636f726533707472353364726f705f696e5f706c616365244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f722447542431376866386339663061313434386130383237452e6c6c766d2e363831373539343630333232373130383833a309355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683265396463363139363632653336383145a409355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683265613064613961633530303030666145a509355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683432386631303536353936666339323945a609355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683630383339323439356531643537633845a709355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683736373833396463323062393162303445a809685f5a4e35616c6c6f633131636f6c6c656374696f6e73397665635f646571756532315665634465717565244c54245424432441244754243467726f7731376831343964363038613963613634393962452e6c6c766d2e363831373539343630333232373130383833a909625f5a4e36305f244c5424245246246d7574247532302454247532302461732475323024627335382e2e656e636f64652e2e456e636f6465546172676574244754243131656e636f64655f776974683137683261366439363839646263373632643945aa09765f5a4e36355f244c5424616c6c6f632e2e737472696e672e2e46726f6d557466384572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376835393137346462613138663336353532452e6c6c766d2e363831373539343630333232373130383833ab095e5f5a4e36355f244c542473705f636f72652e2e63727970746f2e2e5075626c69634572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686237303639316163613335313462346345ac095f5f5a4e36365f244c542473705f776569676874732e2e7765696768745f76322e2e576569676874247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683333346365376538623833366664373445ad09655f5a4e37325f244c54246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e4d6f6465247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683135396330356432356335393231666445ae096b5f5a4e37325f244c542473705f636f6e73656e7375735f626162652e2e6170702e2e5075626c696324753230246173247532302473657264652e2e7365722e2e53657269616c697a65244754243973657269616c697a653137686665326364396132633536316361613545af096f5f5a4e37335f244c542473705f636f6e73656e7375735f626162652e2e6170702e2e5075626c696324753230246173247532302473657264652e2e64652e2e446573657269616c697a65244754243131646573657269616c697a653137683939353933646237663863393633396145b0096d5f5a4e38305f244c54246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e4d6574616461746148617368247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686166616464303364353963333234666145b1099a015f5a4e32327375627374726174655f746573745f72756e74696d65315f39335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2447542439747970655f696e666f3137686539626263343730666536333261313745b209485f5a4e313274726163696e675f636f72653863616c6c736974653843616c6c736974653135707269766174655f747970655f69643137683563306264636438373465386266306445b309a8015f5a4e3133375f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e64656475705f736f727465645f697465722e2e4465647570536f7274656449746572244c54244b244324562443244924475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137683738623766316330343166653262353845b409af015f5a4e3133375f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e4173486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424475424313061735f686173685f64623137683733386430313837383861646531326145b509b3015f5a4e3133375f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e4173486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424475424313461735f686173685f64625f6d75743137686331333132653035396136626533366245b609cb015f5a4e3136375f244c542473657264652e2e64652e2e696d706c732e2e244c5424696d706c247532302473657264652e2e64652e2e446573657269616c697a652475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754242e2e646573657269616c697a652e2e56656356697369746f72244c5424542447542424753230246173247532302473657264652e2e64652e2e56697369746f72244754243976697369745f7365713137683338333865383832656362346165663345b709475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f6465643137686133653531353133396262646461613545b809a0015f5a4e32327375627374726174655f746573745f72756e74696d65315f39395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c2447542439656e636f64655f746f3137686539363262326538393264613935626545b909585f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376834343338643261363832646166643934452e6c6c766d2e32383034353134343737303732373934353638ba093f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683830336232383263646337663632666445bb09465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683266313238633266393938656161616645bc09465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683663393365316631306530376535306445bd09465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683830653964343161386331306139636345be09465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137686164373265356439313463346166366145bf09465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137686531393635393636343265396564303645c009465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137686563333138356165653831643066653645c109475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686161373834363830643961643162383545c209635f5a4e34355f244c5424244c502424525024247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376834303566303661613534323663666363452e6c6c766d2e32383034353134343737303732373934353638c309515f5a4e34636f7265336f70733866756e6374696f6e35466e4d75743863616c6c5f6d757431376864643638666565636462316135306133452e6c6c766d2e32383034353134343737303732373934353638c409735f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e6365343063616c6c5f6f6e636524753762242475376224767461626c652e7368696d2475376424247537642431376830623662356162393134373765373065452e6c6c766d2e32383034353134343737303732373934353638c509b9015f5a4e34636f72653370747231323564726f705f696e5f706c616365244c542473657264652e2e64652e2e696d706c732e2e244c5424696d706c247532302473657264652e2e64652e2e446573657269616c697a652475323024666f722475323024753634244754242e2e646573657269616c697a652e2e5072696d697469766556697369746f722447542431376832633438373739393063633837336265452e6c6c766d2e32383034353134343737303732373934353638c609ce015f5a4e34636f72653370747231373164726f705f696e5f706c616365244c54246d656d6f72795f64622e2e4d656d6f72794442244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362443246d656d6f72795f64622e2e50726566697865644b6579244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624475424244324616c6c6f632e2e7665632e2e566563244c542475382447542424475424244754243137686461333031613034303666356666306445c709a5025f5a4e34636f72653370747232353864726f705f696e5f706c616365244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c54246d656d6f72795f64622e2e4d656d6f72794442244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362443246d656d6f72795f64622e2e486173684b6579244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624475424244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624475424244754243137683265326562643765346433373065386545c80992045f5a4e34636f72653370747234373064726f705f696e5f706c616365244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e547269654261636b656e64457373656e6365244c54246d656d6f72795f64622e2e4d656d6f72794442244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362443246d656d6f72795f64622e2e486173684b6579244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624475424244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624432473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e7465645265636f7264657250726f7669646572244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624475424244754242447542431376864343233326631353931363835616634452e6c6c766d2e32383034353134343737303732373934353638c909735f5a4e34636f726533707472353664726f705f696e5f706c616365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c2447542431376865643735326330326539353332656332452e6c6c766d2e32383034353134343737303732373934353638ca09695f5a4e34636f726533707472373164726f705f696e5f706c616365244c542473705f747269652e2e6572726f722e2e4572726f72244c54247072696d69746976655f74797065732e2e4832353624475424244754243137683461363365306537646330353230356545cb096c5f5a4e34636f726533707472373464726f705f696e5f706c616365244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c5424616c6c6f632e2e7665632e2e566563244c542475382447542424475424244754243137683465633563653962343563363036306145cc09635f5a4e37305f244c5424636f72652e2e726573756c742e2e526573756c74244c5424542443244524475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683433376161653961336434353232303345cd096c5f5a4e37395f244c542474726163696e675f636f72652e2e6669656c642e2e446973706c617956616c7565244c54245424475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683731663366343637386639633833356145ce09305f5a4e3773705f74726965313564656c74615f747269655f726f6f743137683132363765636536363430333138393245cf09305f5a4e3773705f74726965313564656c74615f747269655f726f6f743137686234366236343335666436393337363645d009365f5a4e3773705f7472696532316368696c645f64656c74615f747269655f726f6f743137683336613536633230386630316331636345d109365f5a4e3773705f7472696532316368696c645f64656c74615f747269655f726f6f743137686131623063316635363531393665303445d209735f5a4e38365f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e486173684442244c542448244324542447542424475424336765743137686336356265633939363937353031303845d309765f5a4e38365f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e486173684442244c54244824432454244754242447542436696e736572743137683331323562383037323935343434366145d409765f5a4e38365f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e486173684442244c5424482443245424475424244754243672656d6f76653137686662363563663966306264626433363945d509775f5a4e38365f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e486173684442244c54244824432454244754242447542437656d706c6163653137683161613861333239633037343236366445d609785f5a4e38365f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e486173684442244c54244824432454244754242447542438636f6e7461696e733137686337663665353265646334323730376645d7097d5f5a4e38385f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e4173486173684442244c542448244324542447542424475424313061735f686173685f64623137686265373334373938313031643634373545d80981015f5a4e38385f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e4173486173684442244c542448244324542447542424475424313461735f686173685f64625f6d75743137683234396363393830393034333434663145d9097d5f5a4e38385f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e4173486173684442244c542448244324542447542424475424313061735f686173685f64623137683932383034353935303636636632373945da0981015f5a4e38385f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e4173486173684442244c542448244324542447542424475424313461735f686173685f64625f6d75743137683038386363646330333432396164343645db09795f5a4e38395f244c542474726163696e675f636f72652e2e6669656c642e2e446973706c617956616c7565244c5424542447542424753230246173247532302474726163696e675f636f72652e2e6669656c642e2e56616c756524475424367265636f72643137683632313736663931616432666139396645dc097e5f5a4e39325f244c542474726163696e675f636f72652e2e63616c6c736974652e2e44656661756c7443616c6c7369746524753230246173247532302474726163696e675f636f72652e2e63616c6c736974652e2e43616c6c7369746524475424386d657461646174613137683032373862373566376265346534323345dd0989015f5a4e39385f244c54247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c24753230246173247532302473705f72756e74696d652e2e7472616974732e2e5369676e6564457874656e73696f6e2447542431327072655f64697370617463683137683730356533303231333030653331643745de095b5f5a4e32327375627374726174655f746573745f72756e74696d65313762656e63686d61726b5f6164645f6f6e6531376864333461666361623764633464303137452e6c6c766d2e32383034353134343737303732373934353638df09a2015f5a4e32327375627374726174655f746573745f72756e74696d65315f3130305f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e5472616e73666572446174612447542439656e636f64655f746f3137686431396234356235633363323632386445e00994015f5a4e32327375627374726174655f746573745f72756e74696d65315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e5472616e73666572446174612447542439747970655f696e666f3137686230363038376636353061666534373845e1097f5f5a4e39385f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65426c6f636b57656967687473247532302461732475323024626f756e6465645f636f6c6c656374696f6e732e2e476574244c54245f492447542424475424336765743137683763333838653230303264616462323045e209455f5a4e32327375627374726174655f746573745f72756e74696d65313153657373696f6e4b6579733867656e65726174653137686137353433623064623231663463323145e309525f5a4e32327375627374726174655f746573745f72756e74696d65313153657373696f6e4b6579733230696e746f5f7261775f7075626c69635f6b6579733137686166383865613065633061663336376445e4095d5f5a4e32327375627374726174655f746573745f72756e74696d653752756e74696d6531316d657461646174615f697231376833376263393537633561663737366534452e6c6c766d2e32383034353134343737303732373934353638e509da075f5a4e3933385f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524753230246173247532302473705f7472616e73616374696f6e5f706f6f6c2e2e72756e74696d655f6170692e2e72756e74696d655f6465636c5f666f725f7461676765645f7472616e73616374696f6e5f71756575652e2e5461676765645472616e73616374696f6e51756575655633244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242452502424475424244754242447542424475424323076616c69646174655f7472616e73616374696f6e3137686363623362633033333064313535353745e609a4075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242452502424475424244754242447542424475424387573655f747269653137686363326261656138653839373635656445e709b0075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243139746573745f656432353531395f63727970746f3137683436633632326563353036636666653245e809b0075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243139746573745f737232353531395f63727970746f3137683436333437616266653366313064613545e909ae075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243137746573745f65636473615f63727970746f3137683437646132616337383833366533353745ea09a9075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243132746573745f73746f726167653137686636646136396433303663323262306445eb09a9075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243132746573745f7769746e6573733137683138616333363238346534396462666645ec09b4075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243233746573745f6d756c7469706c655f617267756d656e74733137683333386336323461363132343833363345ed09a9075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243132646f5f74726163655f6c6f673137683938323835313338653265303636613045ee09a5075f5a4e3839325f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524753230246173247532302473705f636f6e73656e7375735f626162652e2e72756e74696d655f6465636c5f666f725f626162655f6170692e2e426162654170695632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243133636f6e66696775726174696f6e3137686239666332353039636638333064643745ef09c5075f5a4e3839325f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524753230246173247532302473705f636f6e73656e7375735f626162652e2e72756e74696d655f6465636c5f666f725f626162655f6170692e2e426162654170695632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524475424245250242447542424475424244754242447542434357375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e7369633137686630396464613362373561303561373845f009b6075f5a4e3930375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524753230246173247532302473705f6f6666636861696e2e2e72756e74696d655f6465636c5f666f725f6f6666636861696e5f776f726b65725f6170692e2e4f6666636861696e576f726b65724170695632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524475424245250242447542424475424244754242447542431356f6666636861696e5f776f726b65723137683366626135393464643266633963333945f109cf015f5a4e3132315f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d654f726967696e247532302461732475323024636f72652e2e636f6e766572742e2e46726f6d244c54247375627374726174655f746573745f72756e74696d652e2e4f726967696e43616c6c657224475424244754243466726f6d32385f24753762242475376224636c6f737572652475376424247537642431376864303636643239376139373430323738452e6c6c766d2e32383034353134343737303732373934353638f209ea015f5a4e3230335f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d654f726967696e247532302461732475323024636f72652e2e636f6e766572742e2e46726f6d244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c5424244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e7449642447542424475424244754243466726f6d3137683364643932613435356237396637313145f3097a5f5a4e38385f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24753230246173247532302473705f72756e74696d652e2e7472616974732e2e446973706174636861626c65244754243864697370617463683137686632333937666433383236326439356245f40993015f5a4e32327375627374726174655f746573745f72756e74696d65315f38365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c2447542439747970655f696e666f3137683239623966616665666232666161333945f5098f015f5a4e32327375627374726174655f746573745f72756e74696d65315f38325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542439747970655f696e666f3137686462346630393030666665363439346445f60994015f5a4e32327375627374726174655f746573745f72756e74696d65315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d654576656e742447542439747970655f696e666f3137683262393366343361653934623132363245f70994015f5a4e32327375627374726174655f746573745f72756e74696d65315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d654572726f722447542439747970655f696e666f3137683930333133386565633964303937346645f80991015f5a4e3131335f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6547656e65736973436f6e6669672475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4275696c6447656e65736973436f6e66696724475424356275696c643137683664396534353630653431326232623045f90984015f5a4e38385f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524753230246173247532302473705f72756e74696d652e2e7472616974732e2e56616c6964617465556e7369676e656424475424313776616c69646174655f756e7369676e65643137686664623139316535336263306235616245fa0999015f5a4e32327375627374726174655f746573745f72756e74696d65315f39325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d65486f6c64526561736f6e2447542439747970655f696e666f3137686566363564613130363437663938646345fb099d015f5a4e32327375627374726174655f746573745f72756e74696d65315f39365f244c5424696d706c247532302473657264652e2e7365722e2e53657269616c697a652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d6547656e65736973436f6e666967244754243973657269616c697a653137683435363938366661383636353863336245fc09e3015f5a4e3139315f244c54247375627374726174655f746573745f72756e74696d652e2e5f2e2e244c5424696d706c247532302473657264652e2e64652e2e446573657269616c697a652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d6547656e65736973436f6e666967244754242e2e646573657269616c697a652e2e5f5f4669656c6456697369746f7224753230246173247532302473657264652e2e64652e2e56697369746f72244754243976697369745f7374723137683437396439643336333362656263306445fd099d015f5a4e32327375627374726174655f746573745f72756e74696d65315f39395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24475424366465636f64653137683461376632346664356331333266393945fe09655f5a4e37325f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683839326636663537303635343431623145ff0985015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137683030396663353565363965313064383945800a85015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137683836396235316235383536663236303245810a89015f5a4e3130365f244c5424636f72652e2e697465722e2e61646170746572732e2e47656e657269635368756e74244c5424492443245224475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137686561386362636263336262306237336145820a8d015f5a4e3130365f244c5424636f72652e2e697465722e2e61646170746572732e2e47656e657269635368756e74244c5424492443245224475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424387472795f666f6c643137683561663635316663663564303763333645830a415f5a4e37747269655f6462346e6f646531304e6f646548616e646c653135746f5f6f776e65645f68616e646c653137683231663965393066326537633534383145840a6c5f5a4e34636f726533707472373464726f705f696e5f706c616365244c5424747269655f64622e2e6e6f64652e2e4e6f64654f776e6564244c54247072696d69746976655f74797065732e2e4832353624475424244754243137683462386365613062656464353664383245850a695f5a4e34636f726533707472373164726f705f696e5f706c616365244c542473705f747269652e2e6572726f722e2e4572726f72244c54247072696d69746976655f74797065732e2e4832353624475424244754243137683461363365306537646330353230356545860a8d015f5a4e3130365f244c5424636f72652e2e697465722e2e61646170746572732e2e47656e657269635368756e74244c5424492443245224475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424387472795f666f6c643137686235656135663436353739313338383145870a415f5a4e37747269655f6462346e6f646531304e6f646548616e646c653135746f5f6f776e65645f68616e646c653137686233653961643936303138643965373845880a8d015f5a4e3130365f244c5424636f72652e2e697465722e2e61646170746572732e2e47656e657269635368756e74244c5424492443245224475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424387472795f666f6c643137686532656131653065636163303235326645890a8d015f5a4e3130365f244c5424636f72652e2e697465722e2e61646170746572732e2e47656e657269635368756e74244c5424492443245224475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424387472795f666f6c6431376866636633353363663464356433323264458a0a4a5f5a4e313073705f72756e74696d6536747261697473313656616c6964617465556e7369676e656431327072655f646973706174636831376839343631383135343031346431633032458b0a95015f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c5424542447542424475424323776616c69646174655f617574686f72697a65645f7570677261646531376861643931366330346635393236633565452e6c6c766d2e31373132343635353732373637363833303235308c0a91015f5a4e3131335f244c54246672616d655f73797374656d2e2e70616c6c65742e2e47656e65736973436f6e666967244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4275696c6447656e65736973436f6e66696724475424356275696c6431376834303339323931613632323230313734458d0a93015f5a4e3131365f244c542470616c6c65745f62616c616e6365732e2e696d706c5f63757272656e63792e2e696d62616c616e6365732e2e4e65676174697665496d62616c616e6365244c5424542443244924475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f7031376838646237336362366362366132636638458e0a8f015f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c542454244754242447542432316465706f7369745f6576656e745f696e646578656431376863356236303733346338323866623062452e6c6c766d2e31373132343635353732373637363833303235308f0a93015f5a4e3131365f244c542470616c6c65745f62616c616e6365732e2e696d706c5f63757272656e63792e2e696d62616c616e6365732e2e506f736974697665496d62616c616e6365244c5424542443244924475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683330626439373131323466383232333045900a385f5a4e313170616c6c65745f626162653138636f6d707574655f72616e646f6d6e6573733137683966376133623661303862303739346345910aa6015f5a4e3132305f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e7432245250242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4f6e52756e74696d65557067726164652447542431386f6e5f72756e74696d655f757067726164653137683866656666656336333862636233383545920aa7015f5a4e3132395f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e74322443245475706c65456c656d656e7433245250242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4f6e47656e657369732447542431306f6e5f67656e657369733137683838383031663964353934323735643045930ab0025f5a4e31326672616d655f73797374656d3234355f244c5424696d706c24753230246672616d655f737570706f72742e2e7472616974732e2e73746f7265645f6d61702e2e53746f7265644d6170244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e744964244324244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e7444617461244754242475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c542454244754242447542431377472795f6d75746174655f6578697374733137683533323961643038616539383936306145940ab0025f5a4e31326672616d655f73797374656d3234355f244c5424696d706c24753230246672616d655f737570706f72742e2e7472616974732e2e73746f7265645f6d61702e2e53746f7265644d6170244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e744964244324244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e7444617461244754242475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c542454244754242447542431377472795f6d75746174655f6578697374733137686231373635333836303366643030316545950ab0025f5a4e31326672616d655f73797374656d3234355f244c5424696d706c24753230246672616d655f737570706f72742e2e7472616974732e2e73746f7265645f6d61702e2e53746f7265644d6170244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e744964244324244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e7444617461244754242475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c542454244754242447542431377472795f6d75746174655f6578697374733137686462356435363266346438633962313845960a6a5f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c54245424475424244754243130696e697469616c697a653137683730346430333563623465363335313545970a6c5f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c5424542447542424475424313263616e5f7365745f636f64653137686264396233333131373765383265333045980a6c5f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c5424542447542424475424313272657365745f6576656e74733137686365356438343733373938666663616245990a765f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c542454244754242447542432326e6f74655f6170706c6965645f65787472696e73696331376831373337383933343937626563383362459a0a5c5f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247536342447542433666d7431376832643962636565363333316266326131459b0a785f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c542454244754242447542432346e6f74655f66696e69736865645f65787472696e7369637331376832623939323564376638376365393536459c0a7a5f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c54245424475424244754243236646f5f6170706c795f617574686f72697a655f7570677261646531376835396266666130383332633039383633459d0a675f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c54245424475424244754243866696e616c697a6531376864316236633764323436616530376661459e0a4f5f5a4e31326672616d655f73797374656d3670616c6c6574313550616c6c6574244c54245424475424313673746f726167655f6d6574616461746131376832343534393565633831326334666639459f0a585f5a4e31326672616d655f73797374656d3670616c6c6574313550616c6c6574244c54245424475424323570616c6c65745f636f6e7374616e74735f6d657461646174613137683362646363376130386336383366343845a00a9f015f5a4e31326672616d655f73797374656d3670616c6c6574315f3130305f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e4576656e74244c542454244754242447542439656e636f64655f746f3137683338663536306335353431353661386445a10a9f015f5a4e31326672616d655f73797374656d3670616c6c6574315f3130305f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e4576656e74244c54245424475424244754243973697a655f68696e743137683065623633323464383265323664646345a20a8a015f5a4e31326672616d655f73797374656d3670616c6c6574315f38345f244c5424696d706c2475323024636f72652e2e636c6f6e652e2e436c6f6e652475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e43616c6c244c542454244754242447542435636c6f6e653137683663663436393636323963623833616445a30a90015f5a4e31326672616d655f73797374656d3670616c6c6574315f38365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e43616c6c244c542454244754242447542439747970655f696e666f3137683536363733373163666238613534373545a40a91015f5a4e31326672616d655f73797374656d3670616c6c6574315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e4572726f72244c542454244754242447542439747970655f696e666f3137683361653831346237383064373962623245a50a91015f5a4e31326672616d655f73797374656d3670616c6c6574315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e4576656e74244c542454244754242447542439747970655f696e666f3137683137343038623832396561356134393845a60a9a015f5a4e31326672616d655f73797374656d3670616c6c6574315f39395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e43616c6c244c5424542447542424475424366465636f64653137683662653239666339383430316663633945a70a9d015f5a4e31326672616d655f73797374656d3670616c6c6574315f39395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e43616c6c244c542454244754242447542439656e636f64655f746f3137686339313031356637323934396463643445a80aa9015f5a4e3133385f244c54246672616d655f737570706f72742e2e7472616974732e2e746f6b656e732e2e66756e6769626c652e2e696d62616c616e63652e2e496d62616c616e6365244c5424422443244f6e44726f702443244f70706f736974654f6e44726f7024475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683933343634333363353264323535623145a90abc015f5a4e31336672616d655f737570706f72743674726169747336746f6b656e73346d697363315f3131365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e7472616974732e2e746f6b656e732e2e6d6973632e2e4964416d6f756e74244c5424496424432442616c616e6365244754242447542439747970655f696e666f3137683132303635396163343461666636363345aa0abc015f5a4e31336672616d655f737570706f72743674726169747336746f6b656e73346d697363315f3131365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e7472616974732e2e746f6b656e732e2e6d6973632e2e4964416d6f756e74244c5424496424432442616c616e6365244754242447542439747970655f696e666f3137683363333862373062316161613033666545ab0a535f5a4e31336672616d655f737570706f7274367472616974733773746f72616765313553746f72616765496e7374616e636531317072656669785f686173683137683837643166303733626537306238333545ac0a535f5a4e31336672616d655f737570706f7274367472616974733773746f72616765313553746f72616765496e7374616e636531317072656669785f686173683137686363393137396133393363343837393245ad0acb015f5a4e3134365f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e74322443245475706c65456c656d656e7433245250242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4265666f7265416c6c52756e74696d654d6967726174696f6e732447542432396265666f72655f616c6c5f72756e74696d655f6d6967726174696f6e733137686166326132386336613562383961326345ae0a715f5a4e38345f244c54246672616d655f737570706f72742e2e7472616974732e2e6d657461646174612e2e53746f7261676556657273696f6e247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686332393765656362386337356338376645af0ace025f5a4e3237345f244c542470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4f6e49646c65244c5424244c5424244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e426c6f636b24753230246173247532302473705f72756e74696d652e2e7472616974732e2e48656164657250726f7669646572244754242e2e4865616465725424753230246173247532302473705f72756e74696d652e2e7472616974732e2e486561646572244754242e2e4e756d6265722447542424475424376f6e5f69646c6531376862303164656131613465313733656362452e6c6c766d2e3137313234363535373237363736383330323530b00a465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137686336316461363234623733656264333045b10aa5015f5a4e34636f72653370747231333064726f705f696e5f706c616365244c54242475356224636f72652e2e6f7074696f6e2e2e4f7074696f6e244c5424747269655f64622e2e6e6f64652e2e4e6f646548616e646c654f776e6564244c54247072696d69746976655f74797065732e2e4832353624475424244754242475336224247532302431362475356424244754243137683464363263343032613462393966356245b20a3e5f5a4e34636f726533707472323864726f705f696e5f706c616365244c542424524624753136244754243137683665616535333638616239373933636345b30a715f5a4e34636f726533707472353364726f705f696e5f706c616365244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f722447542431376866386339663061313434386130383237452e6c6c766d2e3137313234363535373237363736383330323530b40a785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d69743137683964616534343632633339303363663745b50a7c5f5a4e36395f244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863636562313832636338343631643463452e6c6c766d2e3137313234363535373237363736383330323530b60a385f5a4e37747269655f6462346e6f6465344e6f64653133746f5f6f776e65645f6e6f64653137683939636439343431333765346334396245b70a385f5a4e37747269655f6462346e6f6465344e6f64653133746f5f6f776e65645f6e6f64653137686632346131306365306563333730653445b80a785f5a4e38375f244c542470616c6c65745f626162652e2e53616d65417574686f726974696573466f726576657224753230246173247532302470616c6c65745f626162652e2e45706f63684368616e6765547269676765722447542437747269676765723137683563393766633066646338646137646345b90a8a015f5a4e39345f244c54246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c5424542447542424753230246173247532302473705f72756e74696d652e2e7472616974732e2e56616c6964617465556e7369676e656424475424313776616c69646174655f756e7369676e65643137683763633366633333663562393939626245ba0a8c015f5a4e39365f244c54246672616d655f73797374656d2e2e70616c6c65742e2e43616c6c244c542454244754242475323024617324753230246672616d655f737570706f72742e2e64697370617463682e2e4765744469737061746368496e666f2447542431376765745f64697370617463685f696e666f3137683737366362346135636539313131633545bb0a86015f5a4e3130315f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683037333433373337663466363565313245bc0a86015f5a4e3130315f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137686162386230393332393466663765353545bd0a86015f5a4e3130315f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137686339613638313439636431656664636645be0a86015f5a4e3130315f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c5424753634244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683065343237336433373932373636383445bf0a86015f5a4e3130315f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c5424753634244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683939386433313432343839616330323945c00a8c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686130623235303763636430386336663945c10a8c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753634244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686162383430623030653531313334376645c20a95015f5a4e31307363616c655f696e666f35696d706c7339365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c542454244754242447542439747970655f696e666f3137686436666266333566353631333232643145c30a3c5f5a4e31307363686e6f72726b656c3376726638565246496e4f757431306d616b655f62797465733137683565326431363264636330633964663845c40a445f5a4e31307363686e6f72726b656c33767266395652465072654f757431376174746163685f696e7075745f686173683137686239323333336663616132313737343345c50a325f5a4e313073657264655f6a736f6e326465313066726f6d5f74726169743137686133616163653963653362613764616545c60a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f7374727563743137683330636661616663613032363930376245c70a495f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542431307065656b5f6572726f723137683233643937613864646630313864333545c80a4a5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542431317363616e5f6e756d6265723137683931356135646362653961386134656645c90a4c5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542431337363616e5f6578706f6e656e743137686666663863633264356330373030343545ca0a4a5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542431317363616e5f6f725f656f663137683934313964656662346237313064326145cb0a4c5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313370617273655f646563696d616c3137683130653563616664666163326334333045cc0a4d5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542431346636345f66726f6d5f70617274733137686332333536346565646433663139363845cd0a4d5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313470617273655f6578706f6e656e743137686539613036646233653532303532316445ce0a555f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424323270617273655f646563696d616c5f6f766572666c6f773137683661346533653430383835346633306345cf0a4c5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313370617273655f696e74656765723137686163383264616632613834343062326445d00a515f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313870617273655f6c6f6e675f696e74656765723137686436663433633030353966323837663845d10a4d5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313469676e6f72655f696e74656765723137683132646237623166306636393438666545d20a4e5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313569676e6f72655f6578706f6e656e743137686561663837613163326236323934346545d30a565f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424323370617273655f6578706f6e656e745f6f766572666c6f773137686163623532653432653165323862656245d40a4f5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313670617273655f616e795f6e756d6265723137686533666539303035323061373439656645d50a695f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542431377065656b5f696e76616c69645f7479706531376837626138363266646533336131303362452e6c6c766d2e37373639353536303138373433393139313834d60a565f5a4e313073657264655f6a736f6e356572726f72354572726f7231326669785f706f736974696f6e31376835393362366639643165386538333239452e6c6c766d2e37373639353536303138373433393139313834d70a6a5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313870617273655f6f626a6563745f636f6c6f6e31376864316632643330613939383561396530452e6c6c766d2e37373639353536303138373433393139313834d80a435f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424356572726f723137686431316338336139343666383533376445d90a455f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542437656e645f6d61703137683330643738346362333636626139326545da0a455f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542437656e645f7365713137683165303965653035643161393536323545db0a2e5f5a4e313161727261795f6279746573396279746573326865783137683063623463653263633034626330383045dc0a2e5f5a4e313161727261795f6279746573396279746573326865783137683232393761396336663734663439643245dd0a475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683737323034346237316564633364663845de0a8b015f5a4e34636f7265336f70733866756e6374696f6e35696d706c7338305f244c5424696d706c2475323024636f72652e2e6f70732e2e66756e6374696f6e2e2e466e4f6e6365244c542441244754242475323024666f722475323024245246246d7574247532302446244754243963616c6c5f6f6e63653137683737313436643233663735313461383545df0a565f5a4e34636f726533707472323764726f705f696e5f706c616365244c54242452462475382447542431376833376634303563343765346531373866452e6c6c766d2e37373639353536303138373433393139313834e00a6d5f5a4e34636f726533707472353064726f705f696e5f706c616365244c542473657264652e2e64652e2e696d706c732e2e556e697456697369746f722447542431376830393231376639376139393130616232452e6c6c766d2e37373639353536303138373433393139313834e10a3e5f5a4e35616c6c6f633473796e633136417263244c54245424432441244754243964726f705f736c6f773137686166393637633761376534636431646145e20a3e5f5a4e35616c6c6f633473796e633136417263244c54245424432441244754243964726f705f736c6f773137686563656130623932323531393531653745e30a355f5a4e357365726465326465354572726f7231336d697373696e675f6669656c643137683033306566393061666338653838636245e40a765f5a4e36315f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f7224753230246173247532302473657264652e2e64652e2e4572726f722447542436637573746f6d31376832373365313031396430336531316561452e6c6c766d2e37373639353536303138373433393139313834e50a355f5a4e357365726465326465354572726f723133756e6b6e6f776e5f6669656c643137683130373837303036663830633263633345e60a365f5a4e357365726465326465354572726f723134696e76616c69645f6c656e6774683137683664313765333462306438356364303845e70a375f5a4e357365726465326465354572726f7231356475706c69636174655f6669656c643137683163303365373735306264393965613445e80a375f5a4e357365726465326465354572726f723135756e6b6e6f776e5f76617269616e743137686335313534333036373462653431376245e90a385f5a4e3573657264653264653953657141636365737331326e6578745f656c656d656e743137683738656235363266616461613037663345ea0a5d5f5a4e36315f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f7224753230246173247532302473657264652e2e64652e2e4572726f722447542436637573746f6d3137683862363935326366396561353563656245eb0a775f5a4e37355f244c542473657264655f6a736f6e2e2e64652e2e536571416363657373244c5424522447542424753230246173247532302473657264652e2e64652e2e5365714163636573732447542431376e6578745f656c656d656e745f736565643137683365663434356464643431623033356145ec0a775f5a4e37355f244c542473657264655f6a736f6e2e2e64652e2e536571416363657373244c5424522447542424753230246173247532302473657264652e2e64652e2e5365714163636573732447542431376e6578745f656c656d656e745f736565643137683864313161346136366661643365323145ed0a775f5a4e37355f244c542473657264655f6a736f6e2e2e64652e2e536571416363657373244c5424522447542424753230246173247532302473657264652e2e64652e2e5365714163636573732447542431376e6578745f656c656d656e745f736565643137683962623239626138336432363334333345ee0a90015f5a4e38315f244c5424636f72652e2e6d61726b65722e2e5068616e746f6d44617461244c5424542447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6553656564244754243131646573657269616c697a6531376836386234313135373032353937303533452e6c6c766d2e37373639353536303138373433393139313834ef0a775f5a4e38315f244c5424636f72652e2e6d61726b65722e2e5068616e746f6d44617461244c5424542447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6553656564244754243131646573657269616c697a653137686565343631383532376534323532666145f00a7a5f5a4e38335f244c542473657264655f6a736f6e2e2e64652e2e56617269616e74416363657373244c5424522447542424753230246173247532302473657264652e2e64652e2e56617269616e74416363657373244754243132756e69745f76617269616e743137683164323563643663323337313437623645f10a87015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243135646573657269616c697a655f7365713137683133316532643334643832313063356645f20a87015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243135646573657269616c697a655f7365713137683333616565303336666437323937643745f30a87015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243135646573657269616c697a655f7365713137683663396638383330623338303337313545f40a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f737472696e673137683839653363643432376634353935343245f50a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f7374727563743137683133666266633963376265386462393445f60a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f7374727563743137686535623663386532373639373632626345f70a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f7374727563743137686566636263366436383438333235326245f80a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f7374727563743137686235353539633235633436323432356545f90a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f7374727563743137683430393835643635653733353831316645fa0a2d5f5a4e39747269655f726f6f7431306275696c645f747269653137683437353364386565376366326539383645fb0a325f5a4e39747269655f726f6f743135747269655f726f6f745f696e6e65723137686566653532343337373534333465623345fc0a445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683030616563653665323137386330646445fd0a445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683037313835633266366330333932393445fe0a445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683038643064363230373233316536636245ff0a445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683130336561626664653264353661353645800b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683130643134376238373439313331346545810b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683134323333613839656530623038303845820b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683135616335316334656332366666383045830b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683136666362373563613666386236613245840b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683163653735303634376465383064393445850b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683165613930653339666234366139653445860b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683230306234356262343932633065613445870b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683236623332343563653639383333666145880b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683237303838303066656432396633636645890b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376832383039643364363662366530373533458a0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376832393564336134613435376262613036458b0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376832633733386432363637663938303466458c0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376832646331303331353661306266663038458d0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376832646334386361623830663431336138458e0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376832666134383133626565333030306539458f0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683266653933346435336435313863653945900b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683335366562633262643764336166346245910b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683336353466663132396338313138346145920b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683365383139636234383536336538306445930b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683430666239643364333530666439366245940b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683435663761326331623865316562666345950b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683437323533623436643562616661366545960b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683439666436383365636532313233326245970b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683461316465626635623235393464363545980b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683461323566366265353038353861363245990b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376834626332386663333935646230643333459a0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376834633938663539663265613564643666459b0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376835393035333162633465336430663637459c0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376835653938333636326131306237616161459d0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376835663462646433393136653031366563459e0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376836343963393533396238326634666564459f0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683634666132373235396332663732636545a00b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683661376630643532316137353636363745a10b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683730373530393661383738343065646145a20b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683737356231613734343562333237336445a30b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683738643237373036303263356239396645a40b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683830343763633137326231393933666545a50b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683834303138643236386534336537383845a60b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683835666432633338313735313533653645a70b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683838376639343433353132663831316245a80b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683838653963303261666236633261343445a90b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683866373338343432346232313636613045aa0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683936316136653734656365653737356545ab0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683937383936356139383339613435343045ac0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683939303939336665343538346238393145ad0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683939653732666438653237303437396145ae0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683962346138653763356239656334346345af0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683962393538363530663533353839613845b00b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683964613439323066353366376535313645b10b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683966323338343265303431396661393145b20b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683966343335626261636266633736363445b30b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683966383466363636306634333664643245b40b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686131623965393436663166313138376245b50b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686132623264633339663762376138363745b60b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686137393339643062376337306231616345b70b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686138386235323738323335353038393445b80b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686163343137323631656663366563626345b90b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686230653739613163313362666462643045ba0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686237633161336133386236303064316145bb0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686332333163376537646533326434666345bc0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686332343831663966356432653234333545bd0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686332363437393531383731333639333645be0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686339656164383866613064633239383745bf0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686362633966633235616461666139653645c00b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686364366563623564396630653339663045c10b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686366626139343562626165653039653445c20b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686433353935363861383538666161383945c30b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686435356430343030666339306462343445c40b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686465306265363338386138373939303045c50b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686466316531316231633732306466623745c60b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686531663061343762326532303865316545c70b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686533353733306536616237393035616545c80b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686538333463336163323532633865316645c90b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686562346535653733653861346530343545ca0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686563313262653263396537353961316345cb0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686565373262633365336365616561306545cc0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686565383336636530373061666264666145cd0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686631623637326232383937643763393645ce0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686634393739666330356461613364613245cf0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686636336137623266373037313832306645d00b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686661356466633461303466636135623245d10b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686661396335666330393538383037303645d20b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686662643432613436323530623239356145d30b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686663393765626566616262353133626345d40b3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683065383237623433386462613730663345d50b475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683661386535316538663131333137366145d60b335f5a4e34636f726535736c69636534736f727431306d657267655f736f72743137683235623134616661336262353865303645d70b425f5a4e34636f726535736c69636534736f72743235696e73657274696f6e5f736f72745f73686966745f6c6566743137683030373765646163333738383764326545d80b335f5a4e34636f726535736c69636534736f727431306d657267655f736f72743137683438386463366339616430316232373145d90b425f5a4e34636f726535736c69636534736f72743235696e73657274696f6e5f736f72745f73686966745f6c6566743137686638376131316465326661346234663545da0b335f5a4e34636f726535736c69636534736f727431306d657267655f736f72743137683465366339386664343132333031646245db0b425f5a4e34636f726535736c69636534736f72743235696e73657274696f6e5f736f72745f73686966745f6c6566743137683965616238643961663662306561386245dc0b335f5a4e34636f726535736c69636534736f727431306d657267655f736f72743137683534323937616362663532376163653645dd0b425f5a4e34636f726535736c69636534736f72743235696e73657274696f6e5f736f72745f73686966745f6c6566743137683430656136363234633962303434343945de0b0c436f72655f76657273696f6edf0b12436f72655f657865637574655f626c6f636be00b15436f72655f696e697469616c697a655f626c6f636be10b114d657461646174615f6d65746164617461e20b1c4d657461646174615f6d657461646174615f61745f76657273696f6ee30b1a4d657461646174615f6d657461646174615f76657273696f6e73e40b2b5461676765645472616e73616374696f6e51756575655f76616c69646174655f7472616e73616374696f6ee50b1c426c6f636b4275696c6465725f6170706c795f65787472696e736963e60b1b426c6f636b4275696c6465725f66696e616c697a655f626c6f636be70b20426c6f636b4275696c6465725f696e686572656e745f65787472696e73696373e80b1c426c6f636b4275696c6465725f636865636b5f696e686572656e7473e90b1d4163636f756e744e6f6e63654170695f6163636f756e745f6e6f6e6365ea0b12546573744150495f62616c616e63655f6f66eb0b19546573744150495f62656e63686d61726b5f6164645f6f6e65ec0b20546573744150495f62656e63686d61726b5f766563746f725f6164645f6f6e65ed0b22546573744150495f66756e6374696f6e5f7369676e61747572655f6368616e676564ee0b10546573744150495f7573655f74726965ef0b1f546573744150495f62656e63686d61726b5f696e6469726563745f63616c6cf00b1d546573744150495f62656e63686d61726b5f6469726563745f63616c6cf10b19546573744150495f7665635f776974685f6361706163697479f20b18546573744150495f6765745f626c6f636b5f6e756d626572f30b1b546573744150495f746573745f656432353531395f63727970746ff40b1b546573744150495f746573745f737232353531395f63727970746ff50b19546573744150495f746573745f65636473615f63727970746ff60b14546573744150495f746573745f73746f72616765f70b14546573744150495f746573745f7769746e657373f80b1f546573744150495f746573745f6d756c7469706c655f617267756d656e7473f90b14546573744150495f646f5f74726163655f6c6f67fa0b16546573744150495f7665726966795f65643235353139fb0b17546573744150495f77726974655f6b65795f76616c7565fc0b15417572614170695f736c6f745f6475726174696f6efd0b13417572614170695f617574686f726974696573fe0b15426162654170695f636f6e66696775726174696f6eff0b1b426162654170695f63757272656e745f65706f63685f7374617274800c15426162654170695f63757272656e745f65706f6368810c12426162654170695f6e6578745f65706f6368820c35426162654170695f7375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e736963830c24426162654170695f67656e65726174655f6b65795f6f776e6572736869705f70726f6f66840c214f6666636861696e576f726b65724170695f6f6666636861696e5f776f726b6572850c2153657373696f6e4b6579735f67656e65726174655f73657373696f6e5f6b657973860c1f53657373696f6e4b6579735f6465636f64655f73657373696f6e5f6b657973870c1e4772616e6470614170695f6772616e6470615f617574686f726974696573880c194772616e6470614170695f63757272656e745f7365745f6964890c384772616e6470614170695f7375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e7369638a0c274772616e6470614170695f67656e65726174655f6b65795f6f776e6572736869705f70726f6f668b0c1a47656e657369734275696c6465725f6275696c645f73746174658c0c1947656e657369734275696c6465725f6765745f7072657365748d0c1b47656e657369734275696c6465725f7072657365745f6e616d65738e0c94015f5a4e3130335f244c542470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c54245424432449244754242475323024617324753230246672616d655f737570706f72742e2e64697370617463682e2e4765744469737061746368496e666f2447542431376765745f64697370617463685f696e666f31376838616666613935313965656633323339458f0c6d5f5a4e31307363616c655f696e666f35696d706c7335365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424524624542447542439747970655f696e666f3137683636666361346538633762323339616445900c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137683032393366666334386263613131643645910c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137683239303431626534353431656533323045920c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137683666636137386131386235313132313245930c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137683861356637323636636261316638313545940c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137686130656233363139643961623935623245950c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137686166326361666361363561316462356445960c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137686166616239643865646238326130666545970c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137686337366332323233316264626633613845980c98015f5a4e3132305f244c542470616c6c65745f62616c616e6365732e2e70616c6c65742e2e47656e65736973436f6e666967244c54245424432449244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4275696c6447656e65736973436f6e66696724475424356275696c643137683263343865643639393362323135333545990cb2015f5a4e3132315f244c542470616c6c65745f62616c616e6365732e2e70616c6c65742e2e50616c6c6574244c54245424432449244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4265666f7265416c6c52756e74696d654d6967726174696f6e732447542432396265666f72655f616c6c5f72756e74696d655f6d6967726174696f6e7331376861626231343531363065396666333539459a0c8a015f5a4e38345f244c54246672616d655f737570706f72742e2e7472616974732e2e6d657461646174612e2e53746f7261676556657273696f6e247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863323937656563623863373563383766452e6c6c766d2e383531373635383236373037343038323237359b0c7c5f5a4e31336672616d655f737570706f72743674726169747336746f6b656e733866756e6769626c6537726567756c61723130556e62616c616e636564313664656372656173655f62616c616e636531376836643366616235353565643963623762452e6c6c766d2e383531373635383236373037343038323237359c0cad025f5a4e313570616c6c65745f62616c616e6365733133696d706c5f66756e6769626c653230335f244c5424696d706c24753230246672616d655f737570706f72742e2e7472616974732e2e746f6b656e732e2e66756e6769626c652e2e726567756c61722e2e556e62616c616e636564244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e744964244754242475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e50616c6c6574244c542454244324492447542424475424313377726974655f62616c616e636531376831353333343339363330323865613831452e6c6c766d2e383531373635383236373037343038323237359d0c555f5a4e31336672616d655f737570706f72743674726169747336746f6b656e733866756e6769626c6537726567756c6172364d7574617465387472616e7366657231376838303261613936376539353332663664459e0c565f5a4e31336672616d655f737570706f72743674726169747336746f6b656e733866756e6769626c6537726567756c6172364d7574617465396275726e5f66726f6d31376865303134306335626565393033393631459f0c9a025f5a4e313570616c6c65745f62616c616e6365733133696d706c5f63757272656e63793231345f244c5424696d706c24753230246672616d655f737570706f72742e2e7472616974732e2e746f6b656e732e2e63757272656e63792e2e72657365727661626c652e2e52657365727661626c6543757272656e6379244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e744964244754242475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e50616c6c6574244c54245424432449244754242447542439756e726573657276653137686636633263303331396432633664663445a00c555f5a4e313570616c6c65745f62616c616e6365733670616c6c6574313950616c6c6574244c54245424432449244754243135656e737572655f75706772616465643137686161653230653531616331663136666345a10c565f5a4e313570616c6c65745f62616c616e6365733670616c6c6574313950616c6c6574244c5424542443244924475424313673746f726167655f6d657461646174613137683462373366346566306130363765313845a20c5f5f5a4e313570616c6c65745f62616c616e6365733670616c6c6574313950616c6c6574244c5424542443244924475424323570616c6c65745f636f6e7374616e74735f6d657461646174613137683931313030616265386164616432633245a30c625f5a4e313570616c6c65745f62616c616e6365733670616c6c6574313950616c6c6574244c542454244324492447542432386d75746174655f6163636f756e745f68616e646c696e675f647573743137686466353435633939303335356331356445a40ca5015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f3130335f244c5424696d706c247532302473657264652e2e7365722e2e53657269616c697a652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e47656e65736973436f6e666967244c5424542443244924475424244754243973657269616c697a653137686134333862383138653961336639373345a50ca5015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f3130365f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c542454244324492447542424475424366465636f64653137686631613331303832333539323230653145a60ca8015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f3130365f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c54245424432449244754242447542439656e636f64655f746f3137686639383839393837626365626265316345a70ca8015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f3130365f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c5424542443244924475424244754243973697a655f68696e743137686161313338616130666533383935303145a80ca9015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f3130375f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e4576656e74244c54245424432449244754242447542439656e636f64655f746f3137683561613033636133646530393239313645a90ca9015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f3130375f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e4576656e74244c5424542443244924475424244754243973697a655f68696e743137686135626466653236666337303064376345aa0c94015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f39315f244c5424696d706c2475323024636f72652e2e636c6f6e652e2e436c6f6e652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c54245424432449244754242447542435636c6f6e653137686633613263613039636236316434363045ab0c9a015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f39335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c54245424432449244754242447542439747970655f696e666f3137686464383063303636333030663661306645ac0c9b015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f39345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e4572726f72244c54245424432449244754242447542439747970655f696e666f3137683931363838333533316635313465373345ad0c9b015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f39345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e4576656e74244c54245424432449244754242447542439747970655f696e666f3137686262343536633138323264383963626445ae0ca3015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f3130315f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e50726544696765737424475424366465636f64653137686165346537376534363539393339343245af0cb0015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f3131315f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e4e65787445706f636844657363726970746f722447542439656e636f64655f746f3137686365396637626163306434386265636145b00cb1015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f3131325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e4e657874436f6e66696744657363726970746f722447542439656e636f64655f746f3137686364633034616339663262383636633745b10ca8015f5a4e31387061726974795f7363616c655f636f64656335636f6465633136696e6e65725f7475706c655f696d706c38395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f722475323024244c50244f30244324503024432451302443245230245250242447542439656e636f64655f746f3137683433663439383132626261343263333245b20c8f015f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f31376832613162373561323035646233633263452e6c6c766d2e38353137363538323637303734303832323735b30c3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683032346265353630383738313666663645b40c3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683064643463333836363661633961396545b50c3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137686534616164343835353932656463383945b60c3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137686634396532633239636638313337323945b70c465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137686439646431616132666662333065646345b80c475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683238636534333339373333346432316245b90c475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683662623638356631633230373232663945ba0c475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686463313334623230623362326435393245bb0c315f5a4e34636f7265336f70733866756e6374696f6e32466e3463616c6c3137686536313432333437666131616638386445bc0c89015f5a4e34636f7265336f70733866756e6374696f6e35696d706c7337395f244c5424696d706c2475323024636f72652e2e6f70732e2e66756e6374696f6e2e2e466e4d7574244c542441244754242475323024666f722475323024245246246d7574247532302446244754243863616c6c5f6d75743137683461363664643761386165333132383245bd0c89015f5a4e34636f7265336f70733866756e6374696f6e35696d706c7337395f244c5424696d706c2475323024636f72652e2e6f70732e2e66756e6374696f6e2e2e466e4d7574244c542441244754242475323024666f722475323024245246246d7574247532302446244754243863616c6c5f6d75743137686539333236653837353730653031613945be0c5a5f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e6365343063616c6c5f6f6e636524753762242475376224767461626c652e7368696d247537642424753764243137686263343230643933396564353465393145bf0c765f5a4e37747269655f6462366c6f6f6b757031394c6f6f6b7570244c54244c244324512447542432376c6f6f6b5f75705f776974685f63616368655f696e7465726e616c32385f24753762242475376224636c6f73757265247537642424753764243137686339633439643432333965356530343945c00c5a5f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e6365343063616c6c5f6f6e636524753762242475376224767461626c652e7368696d247537642424753764243137686366383239653539326533323031353845c10c3d5f5a4e34636f726533707472323764726f705f696e5f706c616365244c5424245246247538244754243137683337663430356334376534653137386645c20c80015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f6465632447542431316465636f64655f706c616e3137683163626365316538333232386338336545c30c4d5f5a4e37747269655f6462366c6f6f6b757031394c6f6f6b7570244c54244c244324512447542431366c6f61645f6f776e65645f76616c75653137683938303862306133313464646166643645c40c6b5f5a4e37747269655f6462366c6f6f6b757031394c6f6f6b7570244c54244c244324512447542431366c6f61645f6f776e65645f76616c756532385f24753762242475376224636c6f73757265247537642424753764243137683861626363663136623866363531633945c50c6d5f5a4e37747269655f6462366c6f6f6b757031394c6f6f6b7570244c54244c244324512447542431386c6f6f6b5f75705f776974685f636163686532385f24753762242475376224636c6f73757265247537642424753764243137683738326530353363613166643966303445c60c435f5a4e37747269655f6462366c6f6f6b757031394c6f6f6b7570244c54244c2443245124475424376c6f6f6b5f75703137683039633332636135613062313065323645c70c735f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137686430306162643333393938356563313545c80c765f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686365393539636261366432663034333745c90c765f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686566326534323335346535653666373945ca0c80015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f6465632447542431316272616e63685f6e6f64653137683438333833323634383332626431643245cb0c83015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f646563244754243134657874656e73696f6e5f6e6f64653137686562313332643132376363663866356445cc0c88015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f6465632447542431396272616e63685f6e6f64655f6e6962626c65643137683262356631643639323265306662353945cd0c88015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f6465632447542431396272616e63685f6e6f64655f6e6962626c65643137686133663734346261323639376661663045ce0c88015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f6465632447542431396272616e63685f6e6f64655f6e6962626c65643137686236613133316364346335396536356145cf0c88015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f6465632447542431396272616e63685f6e6f64655f6e6962626c65643137686439613634363266306461333065336445d00c7d5f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f64656324475424396c6561665f6e6f64653137686261396464373764653533373335366445d10c5d5f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c6574323176616c69646174655f72756e74696d655f63616c6c3137683836306266353738353930393135613945d20cac015f5a4e3133365f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b2443245624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e636f6c6c6563742e2e46726f6d4974657261746f72244c5424244c50244b244324562452502424475424244754243966726f6d5f697465723137683536643164323365356138313661613245d30c81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686135393236383334343631613333386545d40c8f015f5a4e3130375f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b24432456244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686366393330323439386233373934636345d50c6d5f5a4e31307363616c655f696e666f35696d706c7335365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424524624542447542439747970655f696e666f3137683038626339333737656366373463653245d60cac015f5a4e313073705f72756e74696d653767656e6572696336686561646572315f3130375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c54244e756d62657224432448617368244754242447542439747970655f696e666f3137683865663066323732343530363036303145d70c82015f5a4e31307363616c655f696e666f35696d706c7337375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e626f7865642e2e426f78244c542454244754242447542439747970655f696e666f3137683832373066666663316432333263386345d80c82015f5a4e31307363616c655f696e666f35696d706c7337375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e626f7865642e2e426f78244c542454244754242447542439747970655f696e666f3137686539376331383464303737613437353445d90ca5015f5a4e313873705f636f6e73656e7375735f736c6f7473315f3130375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f736c6f74732e2e45717569766f636174696f6e50726f6f66244c54244865616465722443244964244754242447542439747970655f696e666f3137686331353661643662343466373838303845da0cae015f5a4e313073705f72756e74696d653767656e6572696335626c6f636b315f3131305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542448656164657224432445787472696e736963244754242447542439747970655f696e666f3137683934303539386666323466623937353845db0cb3015f5a4e3134335f244c542473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c54244164647265737324432443616c6c2443245369676e61747572652443244578747261244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137686662323639313630316561613333323945dc0cd0015f5a4e313073705f72756e74696d653767656e6572696336686561646572315f3132305f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c54244e756d626572244324486173682447542424475424366465636f646531376832636162303533353564396634646261452e6c6c766d2e3138313031373033313138373539303137313631dd0cd3015f5a4e313073705f72756e74696d653767656e6572696336686561646572315f3132305f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c54244e756d62657224432448617368244754242447542439656e636f64655f746f31376832346161363264393065653734316231452e6c6c766d2e3138313031373033313138373539303137313631de0c425f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646539656e636f64655f746f3137683938333034656333636130633633643745df0c4d5f5a4e313073705f76657273696f6e313452756e74696d6556657273696f6e32346465636f64655f776974685f76657273696f6e5f68696e743137683164353430623435326438383665376145e00c8e015f5a4e3131315f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e497465724d7574244c54244b2443245624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137683966643839393537643137643833343545e10c93015f5a4e3131365f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137683666623561626332313434333336393145e20c93015f5a4e3131365f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137686363626231376336303463656532333945e30ca5015f5a4e3133355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424475424336765743137686137613137616435646636313164636645e40c735f5a4e38365f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442244c542448244324542447542424475424336765743137683639643861333435376236646162633445e50ccd015f5a4e3134395f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b46244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424753230246173247532302473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e547269654261636b656e6453746f72616765244c54244824475424244754243367657431376831393138373136346637356462663366452e6c6c766d2e3138313031373033313138373539303137313631e60ca8015f5a4e3133355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c5424753824475424244754242447542436696e736572743137683166303861393434346131393735616545e70c765f5a4e38365f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442244c54244824432454244754242447542436696e736572743137683433393435626236396464636266613245e80ca8015f5a4e3133355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c542475382447542424475424244754243672656d6f76653137683139636137613135653039353764353445e90c765f5a4e38365f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442244c5424482443245424475424244754243672656d6f76653137686132396562643361393730306162656545ea0ca9015f5a4e3133355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c5424753824475424244754242447542437656d706c6163653137686563616663366130356366386464616245eb0c775f5a4e38365f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442244c54244824432454244754242447542437656d706c6163653137686366346133346533343239376530613045ec0caa015f5a4e3133355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c5424753824475424244754242447542438636f6e7461696e733137683461396435393332316430376534316645ed0ca9015f5a4e3133395f244c542473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c54244164647265737324432443616c6c2443245369676e6174757265244324457874726124475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686634663865333431323537373033336245ee0c5f5f5a4e36365f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c54245424475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683165353837306562393762303264316545ef0ca3015f5a4e31336672616d655f737570706f7274386469737061746368315f3130315f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e4469737061746368496e666f2447542439656e636f64655f746f3137686263363463666435616161623033366645f00ca3015f5a4e31336672616d655f737570706f7274386469737061746368315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e5065724469737061746368436c617373244c542454244754242447542439747970655f696e666f3137686633663866386634386137363064326145f10cad015f5a4e31336672616d655f737570706f7274386469737061746368315f3131345f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e5065724469737061746368436c617373244c5424542447542424475424366465636f64653137683261633565376135333733323135316545f20cb0015f5a4e31336672616d655f737570706f7274386469737061746368315f3131345f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e5065724469737061746368436c617373244c542454244754242447542439656e636f64655f746f3137683333636539393337353030643631343245f30cb9015f5a4e3135355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e547269654261636b656e64457373656e6365244c54245324432448244324432443245224475424247532302461732475323024686173685f64622e2e486173684442526566244c542448244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424475424336765743137683162653135656637343731616435643945f40cbe015f5a4e3135355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e547269654261636b656e64457373656e6365244c54245324432448244324432443245224475424247532302461732475323024686173685f64622e2e486173684442526566244c542448244324616c6c6f632e2e7665632e2e566563244c5424753824475424244754242447542438636f6e7461696e733137683062326533613037353336623930623845f50cbd015f5a4e3135365f244c542473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c54244164647265737324432443616c6c2443245369676e61747572652443244578747261244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137686437376630616436393464663664313445f60c745f5a4e34636f726533707472353664726f705f696e5f706c616365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c2447542431376865643735326330326539353332656332452e6c6c766d2e3138313031373033313138373539303137313631f70cbd015f5a4e3135365f244c542473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c54244164647265737324432443616c6c2443245369676e61747572652443244578747261244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542436656e636f64653137683730376439356464356233623031353045f80cbb015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d65557067726164652447542431326f6e5f69646c655f686f6f6b31376831383766656437613963623039306539452e6c6c766d2e3138313031373033313138373539303137313631f90c5f5f5a4e35355f244c5424582475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542431337573696e675f656e636f6465643137683831373638333938333633333033306245fa0ca2015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d6555706772616465244754243133657865637574655f626c6f636b3137683063343561666665333663653436306145fb0ca5015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d6555706772616465244754243136696e697469616c697a655f626c6f636b3137683931653935623836356530336238303045fc0ca4015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d65557067726164652447542431356170706c795f65787472696e7369633137683439316434313334663736393261393645fd0cd7015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d65557067726164652447542431366170706c795f65787472696e7369637332385f24753762242475376224636c6f7375726524753764242475376424313870616e69635f636f6c645f646973706c61793137686364643631656330303265373534326545fe0cc0015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d6555706772616465244754243137696e686572656e74735f6170706c69656431376861383461383266323165663734373661452e6c6c766d2e3138313031373033313138373539303137313631ff0ca3015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d655570677261646524475424313466696e616c697a655f626c6f636b3137683933343261346466646133393330363045800de5015f5a4e3137315f244c542473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c54244c6f6f6b7570536f7572636524432443616c6c2443245369676e617475726524432445787472612447542424753230246173247532302473705f72756e74696d652e2e7472616974732e2e436865636b61626c65244c54244c6f6f6b7570244754242447542435636865636b31376864313234663163626239633562363831452e6c6c766d2e3138313031373033313138373539303137313631810da9015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d655570677261646524475424323076616c69646174655f7472616e73616374696f6e3137686533626636663266623762616137653745820da2015f5a4e313570616c6c65745f62616c616e636573357479706573315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e4163636f756e7444617461244c542442616c616e6365244754242447542439747970655f696e666f3137686134663065333463623066366537386445830da2015f5a4e313570616c6c65745f62616c616e636573357479706573315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e42616c616e63654c6f636b244c542442616c616e6365244754242447542439747970655f696e666f3137686461366330346230616464663931643545840daf015f5a4e313570616c6c65745f62616c616e636573357479706573315f3131345f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e4163636f756e7444617461244c542442616c616e6365244754242447542439656e636f64655f746f3137683035653134323635383662663030363345850db6015f5a4e313570616c6c65745f62616c616e636573357479706573315f3132315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e5265736572766544617461244c5424526573657276654964656e74696669657224432442616c616e6365244754242447542439747970655f696e666f3137683337306339643564353236313836653145860d765f5a4e313673705f73746174655f6d616368696e653230747269655f6261636b656e645f657373656e63653339547269654261636b656e64457373656e6365244c54245324432448244324432443245224475424313273746f726167655f726f6f743137683865666630326464376262303636333445870d695f5a4e34636f726533707472373164726f705f696e5f706c616365244c542473705f747269652e2e6572726f722e2e4572726f72244c54247072696d69746976655f74797065732e2e4832353624475424244754243137683461363365306537646330353230356545880d7c5f5a4e313673705f73746174655f6d616368696e653230747269655f6261636b656e645f657373656e63653339547269654261636b656e64457373656e6365244c5424532443244824432443244324522447542431386368696c645f73746f726167655f726f6f743137683366373864393233626563646561333245890d475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376836333366663933643065626462333132458a0d475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376836656362633037343939303031313061458b0d475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376865386434663665316538323630646237458c0d3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376831383263376235323162326232653630458d0d3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376837666664336633303830343134386662458e0d3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376862313363623731303961363536316162458f0dc9015f5a4e313873705f636f6e73656e7375735f736c6f7473315f3132305f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302473705f636f6e73656e7375735f736c6f74732e2e45717569766f636174696f6e50726f6f66244c542448656164657224432449642447542424475424366465636f646531376838666136643630663765366265636434452e6c6c766d2e3138313031373033313138373539303137313631900d475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686361636439366563393062393632343545910d495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137686635656232366434323534626535343245920dc5015f5a4e34636f72653370747231333664726f705f696e5f706c616365244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c5424616c6c6f632e2e7665632e2e566563244c5424753824475424244324244c5024616c6c6f632e2e7665632e2e566563244c542475382447542424432469333224525024244754242447542431376839643730633362626631376334313230452e6c6c766d2e3138313031373033313138373539303137313631930dea035f5a4e34636f72653370747234353564726f705f696e5f706c616365244c542424524624244c50242452462473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c542433325f7573697a6524432473705f636f72652e2e737232353531392e2e537232353531395075626c69635461672447542424432424524624244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242452502424525024244754243137683161306665303638363763346132373745940d715f5a4e34636f726533707472353364726f705f696e5f706c616365244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f722447542431376866386339663061313434386130383237452e6c6c766d2e3138313031373033313138373539303137313631950d565f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565336d6170323542547265654d6170244c54244b24432456244324412447542436696e736572743137683135646363323935343434666563323345960d565f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565336d6170323542547265654d6170244c54244b24432456244324412447542436696e736572743137683665313937353537323838653633313745970d785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d69743137683335326534363933313933653464326645980d785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d69743137683566366230393138373838366563393145990d785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d697431376863323961306165633566333636633636459a0dee015f5a4e3773705f74726965313373746f726167655f70726f6f663138345f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c54242452462473705f747269652e2e73746f726167655f70726f6f662e2e53746f7261676550726f6f66244754242475323024666f7224753230246d656d6f72795f64622e2e4d656d6f72794442244c5424482443246d656d6f72795f64622e2e486173684b6579244c54244824475424244324616c6c6f632e2e7665632e2e566563244c542475382447542424475424244754243466726f6d31376861623934623361316664313434343030459b0d785f5a4e38365f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442244c54244824432454244754242447542438636f6e7461696e7331376861623135376233326539323331633162459c0d765f5a4e38395f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442526566244c5424482443245424475424244754243367657431376830303965353733643864393062663764459d0d7b5f5a4e38395f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442526566244c54244824432454244754242447542438636f6e7461696e7331376864333366386466313231343538613835459e0d7d5f5a4e39335f244c542473705f72756e74696d652e2e67656e657269632e2e6469676573742e2e4469676573744974656d2475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f646531376833633830616566343032373932323931459f0d7d5f5a4e39335f244c542473705f72756e74696d652e2e67656e657269632e2e6469676573742e2e4469676573744974656d2475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137686633633061353438323530333035643445a00d7d5f5a4e39365f244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542448656164657224432445787472696e73696324475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683166353036303735386338633231313445a10d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683037643637626264633938386438353245a20d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683133313338353835373532333264363945a30d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686564363336353739313661396638326145a40d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683361336466663835326134633765373645a50d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683335663838613730393939656235363745a60d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683637383233316232383133303563366445a70d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683766393065373362353864666664333645a80d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686433396639663265653064366333656245a90d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686562313166663963323164383266663645aa0d9b015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f7031376833336539636534316536303136666136452e6c6c766d2e3138313031373033313138373539303137313631ab0d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686166386362623963653664613164326645ac0d4a5f5a4e396d656d6f72795f646232364d656d6f72794442244c5424482443244b4624432454244754243131636f6e736f6c69646174653137683238313430653033383934626438393345ad0d285f5a4e36737562746c6539626c61636b5f626f783137686536323366376466326130616262376645ae0d3d5f5a4e313274726163696e675f636f7265356669656c6435566973697431307265636f72645f6636343137683032636465383037316532366131373045af0da7015f5a4e3132375f244c5424244c542474726163696e672e2e6c6f672e2e4c6f6756616c7565536574247532302461732475323024636f72652e2e666d742e2e446973706c6179244754242e2e666d742e2e4c6f6756697369746f7224753230246173247532302474726163696e675f636f72652e2e6669656c642e2e56697369742447542431327265636f72645f64656275673137683035623037636631373862623438333145b00d3d5f5a4e313274726163696e675f636f7265356669656c6435566973697431307265636f72645f6936343137683433396262373930623838383436626245b10d3d5f5a4e313274726163696e675f636f7265356669656c6435566973697431307265636f72645f7536343137683639653062656331666361353130633545b20d3e5f5a4e313274726163696e675f636f7265356669656c6435566973697431317265636f72645f626f6f6c3137683762663261356164643464383863343345b30d3e5f5a4e313274726163696e675f636f7265356669656c6435566973697431317265636f72645f693132383137683963623938653735633836613462336245b40d3e5f5a4e313274726163696e675f636f7265356669656c6435566973697431317265636f72645f753132383137686561396665346331326634353739303845b50d465f5a4e34315f244c5424626f6f6c247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683465363937313737333463656137303845b60d475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683232363036316239616165643162393045b70d475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686330653239363533313033343163393845b80d495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683730376362363161356265646162303345b90d5c5f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230246936342447542433666d743137686334636434656639303331326536626445ba0d5c5f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247536342447542433666d743137683264396263656536333331626632613145bb0d5d5f5a4e34636f726533666d74336e756d35315f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f722475323024693132382447542433666d743137686137323862663432373035353335623445bc0d5d5f5a4e34636f726533666d74336e756d35315f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f722475323024753132382447542433666d743137683566373264343963666530306435336645bd0d88015f5a4e34636f72653370747231303164726f705f696e5f706c616365244c5424244c542474726163696e672e2e6c6f672e2e4c6f6756616c7565536574247532302461732475323024636f72652e2e666d742e2e446973706c6179244754242e2e666d742e2e4c6f6756697369746f72244754243137686562653432376662666164633931376245be0d3a5f5a4e34636f726533707472323464726f705f696e5f706c616365244c5424663634244754243137686339666135376563323131306438396245bf0d3b5f5a4e34636f726533707472323564726f705f696e5f706c616365244c5424626f6f6c244754243137686139326339383939636639633037383445c00d3f5f5a4e3774726163696e6731355f5f6d6163726f5f737570706f727431335f5f74726163696e675f6c6f673137683532346131313064333765356338383145c10d775f5a4e36345f244c542474726163696e672e2e6c6f672e2e4c6f6756616c7565536574247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376837366261326436393237626538666137452e6c6c766d2e3131373638333234303836353937383036383133c20da5015f5a4e3132375f244c5424244c542474726163696e672e2e6c6f672e2e4c6f6756616c7565536574247532302461732475323024636f72652e2e666d742e2e446973706c6179244754242e2e666d742e2e4c6f6756697369746f7224753230246173247532302474726163696e675f636f72652e2e6669656c642e2e56697369742447542431307265636f72645f7374723137683339316662663266616136303662393345c30d475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683166323337343539633033646162363345c40d4b5f5a4e34636f726533707472343164726f705f696e5f706c616365244c5424636f72652e2e666d742e2e417267756d656e7473244754243137683764303763313638373039316236653745c50d485f5a4e313274726163696e675f636f72653863616c6c73697465313544656661756c7443616c6c736974653872656769737465723137686630623433383431666539386634623145c60d83015f5a4e39325f244c542474726163696e675f636f72652e2e63616c6c736974652e2e44656661756c7443616c6c7369746524753230246173247532302474726163696e675f636f72652e2e63616c6c736974652e2e43616c6c736974652447542431327365745f696e7465726573743137686361386138323934313130643135363145c70d635f5a4e36375f244c5424636f72652e2e666d742e2e417267756d656e747324753230246173247532302474726163696e675f636f72652e2e6669656c642e2e56616c756524475424367265636f72643137683939643261656636643838393265613545c80d475f5a4e313274726163696e675f636f7265313073756273637269626572313053756273637269626572397472795f636c6f73653137686263386632666133646235333434646145c90d4b5f5a4e313274726163696e675f636f72653130737562736372696265723130537562736372696265723132646f776e636173745f7261773137683037653037303361386630383366383545ca0d81015f5a4e39355f244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f5375627363726962657224753230246173247532302474726163696e675f636f72652e2e737562736372696265722e2e5375627363726962657224475424386e65775f7370616e3137683562646637646638623162643266616545cb0d775f5a4e34636f726533707472353964726f705f696e5f706c616365244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f537562736372696265722447542431376833373964396464396234373139633638452e6c6c766d2e3130323639303030353131313334363639333430cc0d6d5f5a4e313274726163696e675f636f726531307375627363726962657231305375627363726962657232306f6e5f72656769737465725f646973706174636831376834643661353662653364386230313431452e6c6c766d2e3130323639303030353131313334363639333430cd0d675f5a4e313274726163696e675f636f726531307375627363726962657231305375627363726962657231346d61785f6c6576656c5f68696e7431376833343066346339643730633865343032452e6c6c766d2e3130323639303030353131313334363639333430ce0d665f5a4e313274726163696e675f636f726531307375627363726962657231305375627363726962657231336576656e745f656e61626c656431376833616263323733313839313039623462452e6c6c766d2e3130323639303030353131313334363639333430cf0d635f5a4e313274726163696e675f636f72653130737562736372696265723130537562736372696265723130636c6f6e655f7370616e31376835383138383935623936343235316638452e6c6c766d2e3130323639303030353131313334363639333430d00d615f5a4e313274726163696e675f636f72653130737562736372696265723130537562736372696265723964726f705f7370616e31376862323464363466303435306434636233452e6c6c766d2e3130323639303030353131313334363639333430d10d655f5a4e313274726163696e675f636f7265313073756273637269626572313053756273637269626572313263757272656e745f7370616e31376835396432613239363934366337646462452e6c6c766d2e3130323639303030353131313334363639333430d20da5015f5a4e39355f244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f5375627363726962657224753230246173247532302474726163696e675f636f72652e2e737562736372696265722e2e5375627363726962657224475424313772656769737465725f63616c6c7369746531376866323833386662633532643466316435452e6c6c766d2e3130323639303030353131313334363639333430d30d99015f5a4e39355f244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f5375627363726962657224753230246173247532302474726163696e675f636f72652e2e737562736372696265722e2e5375627363726962657224475424367265636f726431376831306563386338633164663962363030452e6c6c766d2e3130323639303030353131313334363639333430d40da7015f5a4e39355f244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f5375627363726962657224753230246173247532302474726163696e675f636f72652e2e737562736372696265722e2e537562736372696265722447542431397265636f72645f666f6c6c6f77735f66726f6d31376838653839643339656135643966376464452e6c6c766d2e3130323639303030353131313334363639333430d50d9a015f5a4e39355f244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f5375627363726962657224753230246173247532302474726163696e675f636f72652e2e737562736372696265722e2e537562736372696265722447542437656e61626c656431376833323734303061373662396461333461452e6c6c766d2e3130323639303030353131313334363639333430d60d97015f5a4e39355f244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f5375627363726962657224753230246173247532302474726163696e675f636f72652e2e737562736372696265722e2e5375627363726962657224475424346578697431376863623362646338663464303538383463452e6c6c766d2e3130323639303030353131313334363639333430d70d6e5f5a4e34636f726533707472373664726f705f696e5f706c616365244c542424524624244250246d7574247532302474726163696e675f636f72652e2e63616c6c736974652e2e44656661756c7443616c6c73697465244754243137683334633333616638313230383566333045d80d355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683366353738623363653165626438353545d90d6d5f5a4e37747269655f6462366e6962626c6531316e6962626c65736c69636534365f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c6963652447542439746f5f73746f7265643137683931356331353631343931613734666145da0d745f5a4e37747269655f6462366e6962626c6531316e6962626c65736c69636534365f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c696365244754243135746f5f73746f7265645f72616e67653137686433333136363561393364653634343145db0d725f5a4e37747269655f6462366e6962626c6531316e6962626c65736c69636534365f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c696365244754243133636f6d6d6f6e5f7072656669783137683235343737646533326132613162353645dc0d6f5f5a4e37747269655f6462366e6962626c6531316e6962626c65736c69636534365f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c69636524475424313072696768745f697465723137686235343230316661316537616364363045dd0d685f5a4e37747269655f6462366e6962626c6531316e6962626c65736c69636534365f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c69636524475424346c6566743137686635313365356637333463363664643045de0d745f5a4e37747269655f6462366e6962626c6531316e6962626c65736c69636534365f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c6963652447542431357374617274735f776974685f7665633137683734663162383465393461636635356145df0daa015f5a4e37747269655f6462366e6962626c6531316e6962626c65736c6963653131335f244c5424696d706c2475323024636f72652e2e636d702e2e5061727469616c4571244c5424747269655f64622e2e6e6962626c652e2e4e6962626c65566563244754242475323024666f722475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c696365244754243265713137683963306233343864386130653163663045e00d6a5f5a4e37747269655f6462366e6962626c65396e6962626c6576656334345f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c6556656324475424313064726f705f6c617374733137683633363961666232393238326361623545e10d655f5a4e37747269655f6462366e6962626c65396e6962626c6576656334345f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c655665632447542436617070656e643137683335666230356161626264346235303145e20d6e5f5a4e37747269655f6462366e6962626c65396e6962626c6576656334345f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65566563244754243134617070656e645f7061727469616c3137683461356639346431393430353232636345e30d80015f5a4e37747269655f6462366e6962626c65396e6962626c6576656334345f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65566563244754243332617070656e645f6f7074696f6e616c5f736c6963655f616e645f6e6962626c653137683635363635313439663931623065336445e40da8015f5a4e37747269655f6462366e6962626c65396e6962626c657665633131325f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c5424747269655f64622e2e6e6962626c652e2e4e6962626c65536c696365244754242475323024666f722475323024747269655f64622e2e6e6962626c652e2e4e6962626c65566563244754243466726f6d3137686534366236343339336536646133666645e50d705f5a4e34636f726533707472353364726f705f696e5f706c616365244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f722447542431376861626564323066396436393063386566452e6c6c766d2e31333436363036313031373639303532353734e60d7b5f5a4e36395f244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832393637613332326332613165663338452e6c6c766d2e31333436363036313031373639303532353734e70d365f5a4e37747269655f6462397472696564626d75743131636f6d62696e655f6b65793137683530623530376135646631343734613145e80d3c5f5a4e37747269655f6462366e6962626c6531306e6962626c655f6f70733973686966745f6b65793137683339633735366532633839303766323745e90d6c5f5a4e37385f244c5424747269655f64622e2e4279746573247532302461732475323024636f72652e2e636f6e766572742e2e46726f6d244c54242452462424753562247538247535642424475424244754243466726f6d3137683564653466383332346233346439333145ea0d475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683538363636663532356634663137613045eb0d475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683666353731353236306639323165623745ec0d495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137686265326338666336373362383163383845ed0d5e5f5a4e34636f726533666d74336e756d35325f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247573697a652447542433666d743137686464646433363838656435613663326345ee0d3c5f5a4e34636f726533707472323664726f705f696e5f706c616365244c54247573697a65244754243137686239336233313532626436383233646645ef0d705f5a4e34636f726533707472353364726f705f696e5f706c616365244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f722447542431376861626564323066396436393063386566452e6c6c766d2e36343031373133383638333435343636393034f00d6b5f5a4e35616c6c6f633473796e633136417263244c542454244324412447542439646f776e6772616465313870616e69635f636f6c645f646973706c617931376863653662613065323361326562653438452e6c6c766d2e36343031373133383638333435343636393034f10d3e5f5a4e35616c6c6f633473796e633136417263244c54245424432441244754243964726f705f736c6f773137683564653931393331643466363565636645f20d7d5f5a4e35616c6c6f633473796e6331375765616b244c542454244324412447542437757067726164653137636865636b65645f696e6372656d656e74313870616e69635f636f6c645f646973706c617931376865663764356135623466353639313230452e6c6c766d2e36343031373133383638333435343636393034f30d7b5f5a4e36395f244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832393637613332326332613165663338452e6c6c766d2e36343031373133383638333435343636393034f40d335f5a4e37747269655f6462346e6f6465384e6f6465506c616e356275696c643137686538616464623066313161666230323545f50da6015f5a4e3133335f244c5424736d616c6c7665632e2e536d616c6c566563244c54244124475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e636f6c6c6563742e2e457874656e64244c5424244c542441247532302461732475323024736d616c6c7665632e2e4172726179244754242e2e4974656d244754242447542436657874656e643137683630616130333963383834623164346145f60d555f5a4e38736d616c6c7665633137536d616c6c566563244c54244124475424387472795f67726f7731376835386632666135343233623631386433452e6c6c766d2e36393837333237333039333733343939333838f70d4a5f5a4e38736d616c6c7665633137536d616c6c566563244c542441244754243231726573657276655f6f6e655f756e636865636b65643137683764353134363435393339633539333945f80d6c5f5a4e34636f726533707472343964726f705f696e5f706c616365244c5424736d616c6c7665632e2e436f6c6c656374696f6e416c6c6f634572722447542431376862336365363536333030343731626264452e6c6c766d2e36393837333237333039333733343939333838f90d775f5a4e36355f244c5424736d616c6c7665632e2e436f6c6c656374696f6e416c6c6f63457272247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376833333136353566633965353230393634452e6c6c766d2e36393837333237333039333733343939333838fa0dd2015f5a4e37747269655f6462366e6962626c65396e6962626c657665633135345f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542424524624747269655f64622e2e6e6962626c652e2e4e6962626c65566563244754242475323024666f722475323024244c50247573697a65244324736d616c6c7665632e2e536d616c6c566563244c54242475356224753824753362242475323024343024753564242447542424525024244754243466726f6d3137683666336266376235316639313062633945fb0d515f5a4e3137636f6d70696c65725f6275696c74696e7333696e7431397370656369616c697a65645f6469765f72656d3132753132385f6469765f72656d3137686430656666646565363632343038643145fc0d095f5f6c736872746933fd0d3b5f5a4e3137636f6d70696c65725f6275696c74696e7333696e74336d756c385f5f6d756c7469333137683733666338306336316261303338656345fe0d076d656d6d6f7665ff0d3d5f5a4e3137636f6d70696c65725f6275696c74696e7333696e743475646976395f5f756469767469333137683033376537333965303466656466353445800e355f5a4e3137636f6d70696c65725f6275696c74696e73336d656d366d656d6370793137683336626139613364623131396632306145810e365f5a4e3137636f6d70696c65725f6275696c74696e73336d656d376d656d6d6f76653137683732666133663134303163653961663645820e355f5a4e3137636f6d70696c65725f6275696c74696e73336d656d366d656d7365743137686131343663346337393933363137383845830e355f5a4e3137636f6d70696c65725f6275696c74696e73336d656d366d656d636d703137683935383264343235323262343232666245840e066d656d637079850e095f5f6173686c746933860e3e5f5a4e3137636f6d70696c65725f6275696c74696e7333696e74357368696674395f5f6c7368727469333137686430316336383630363837336164333045870e085f5f6d756c746933880e066d656d636d70890e095f5f756469767469338a0e066d656d736574071201000f5f5f737461636b5f706f696e74657209170300072e726f6461746101052e6461746102042e627373003d0970726f647563657273010c70726f6365737365642d6279010572757374631d312e37372e30202861656464313733613220323032342d30332d313729", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_plain.json b/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_plain.json new file mode 100644 index 000000000000..fe1fb889f278 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_plain.json @@ -0,0 +1,40 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "relay_chain": "rococo-local", + "para_id": 10101, + "custom_field": "custom_value", + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_raw.json b/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_raw.json new file mode 100644 index 000000000000..0501d6cbe45b --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_raw.json @@ -0,0 +1,38 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92c2a60ec6dd16cd8ab911865ecf7555b186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e": "0x00000000000000000000000001000000000000000080e03779c311000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000", + "0x3a636f6465": "0x010203", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00806d8176de1800" + }, + "childrenDefault": {} + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/code_040506.blob b/substrate/bin/utils/chain-spec-builder/tests/input/code_040506.blob new file mode 100644 index 000000000000..2dd38093cfdb --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/input/code_040506.blob @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/full.json b/substrate/bin/utils/chain-spec-builder/tests/input/full.json new file mode 100644 index 000000000000..f05e3505a2bb --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/input/full.json @@ -0,0 +1,40 @@ +{ + "babe": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 2, + 4 + ] + } + }, + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 2000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 2000000000000000 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 5000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ] + }, + "system": {} +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/patch.json b/substrate/bin/utils/chain-spec-builder/tests/input/patch.json new file mode 100644 index 000000000000..cd909bbe3c3f --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/input/patch.json @@ -0,0 +1,25 @@ +{ + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1000000000000000 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 5000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ] + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/test.rs b/substrate/bin/utils/chain-spec-builder/tests/test.rs new file mode 100644 index 000000000000..f553f05f20a0 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/test.rs @@ -0,0 +1,194 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::fs::File; + +use clap::Parser; +use sc_chain_spec::update_code_in_json_chain_spec; +use staging_chain_spec_builder::ChainSpecBuilder; + +// note: the runtime path will not be read, runtime code will be set directly, to avoid hassle with +// creating the wasm file or providing a valid existing path during test execution. +const DUMMY_PATH: &str = "fake-runtime-path"; + +const OUTPUT_FILE: &str = "/tmp/chain_spec_builder.test_output_file.json"; + +/// Asserts that the JSON in output file matches the JSON in expected file. +/// +/// This helper function reads the JSON content from the file at `OUTPUT_FILE + suffix` path. If the +/// `overwrite_code` flag is set, it updates the output chain specification with a sample code +/// vector `[1, 2, 3]` (to avoid bulky *expected* files), and then compares it against the JSON +/// content from the given `expected_path`. +fn assert_output_eq_expected(overwrite_code: bool, output_suffix: &str, expected_path: &str) { + let path = OUTPUT_FILE.to_string() + output_suffix; + let mut output: serde_json::Value = + serde_json::from_reader(File::open(path.clone()).unwrap()).unwrap(); + if overwrite_code { + update_code_in_json_chain_spec(&mut output, &vec![1, 2, 3]); + } + let expected: serde_json::Value = + serde_json::from_reader(File::open(expected_path).unwrap()).unwrap(); + + assert_eq!(expected, output); + + std::fs::remove_file(path).expect("Failed to delete file"); +} + +fn get_builder(suffix: &str, command_args: Vec<&str>) -> ChainSpecBuilder { + let path = OUTPUT_FILE.to_string() + suffix; + let mut base_args = vec!["dummy", "-c", path.as_str()]; + base_args.extend(command_args); + ChainSpecBuilder::parse_from(base_args) +} + +#[test] +fn test_create_default() { + const SUFFIX: &str = "00"; + let mut builder = get_builder(SUFFIX, vec!["create", "-r", DUMMY_PATH, "default"]); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_default.json"); +} + +#[test] +fn test_create_with_named_preset() { + const SUFFIX: &str = "01"; + let mut builder = + get_builder(SUFFIX, vec!["create", "-r", DUMMY_PATH, "named-preset", "staging"]); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_with_named_preset.json"); +} + +#[test] +fn test_create_with_patch() { + const SUFFIX: &str = "02"; + let mut builder = + get_builder(SUFFIX, vec!["create", "-r", DUMMY_PATH, "patch", "tests/input/patch.json"]); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_with_patch.json"); +} + +#[test] +fn test_create_with_full() { + const SUFFIX: &str = "03"; + let mut builder = + get_builder(SUFFIX, vec!["create", "-r", DUMMY_PATH, "full", "tests/input/full.json"]); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_with_full.json"); +} + +#[test] +fn test_create_with_params() { + const SUFFIX: &str = "04"; + let mut builder = get_builder( + SUFFIX, + vec!["create", "-r", DUMMY_PATH, "-n", "test_chain", "-i", "100", "-t", "live", "default"], + ); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_with_params.json"); +} + +#[test] +fn test_create_parachain() { + const SUFFIX: &str = "05"; + let mut builder = get_builder( + SUFFIX, + vec![ + "create", + "-r", + DUMMY_PATH, + "-n", + "test_chain", + "-i", + "100", + "-t", + "live", + "--para-id", + "10101", + "--relay-chain", + "rococo-local", + "default", + ], + ); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_parachain.json"); +} + +#[test] +fn test_create_raw_storage() { + const SUFFIX: &str = "06"; + let mut builder = get_builder( + SUFFIX, + vec!["create", "-r", DUMMY_PATH, "-s", "patch", "tests/input/patch.json"], + ); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_raw_storage.json"); +} + +#[test] +fn test_update_code() { + const SUFFIX: &str = "07"; + let builder = get_builder( + SUFFIX, + vec!["update-code", "tests/input/chain_spec_plain.json", "tests/input/code_040506.blob"], + ); + builder.run().unwrap(); + assert_output_eq_expected(false, SUFFIX, "tests/expected/update_code.json"); +} + +#[test] +fn test_update_code_raw() { + const SUFFIX: &str = "08"; + let builder = get_builder( + SUFFIX, + vec!["update-code", "tests/input/chain_spec_raw.json", "tests/input/code_040506.blob"], + ); + builder.run().unwrap(); + assert_output_eq_expected(false, SUFFIX, "tests/expected/update_code_raw.json"); +} + +#[test] +fn test_convert_to_raw() { + const SUFFIX: &str = "09"; + let builder = + get_builder(SUFFIX, vec!["convert-to-raw", "tests/input/chain_spec_conversion_test.json"]); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/convert_to_raw.json"); +} + +#[test] +fn test_add_code_substitute() { + const SUFFIX: &str = "10"; + let builder = get_builder( + SUFFIX, + vec![ + "add-code-substitute", + "tests/input/chain_spec_plain.json", + "tests/input/code_040506.blob", + "100", + ], + ); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/add_code_substitute.json"); +} From 9b28a5453a83e21b71f18401f3662078fbd0de1b Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Wed, 4 Sep 2024 18:50:26 +0800 Subject: [PATCH 254/382] Avoid updating the block gap when it's unchanged (#5540) There are basically three commits in this PR. Since all these commits essentially have no logical changes, I packed them into one PR. Review by per-commit is recommended. - The first commit avoids unnecessarily updating the block gap storage when the value remains unchanged, as discovered when I worked on https://github.com/paritytech/polkadot-sdk/issues/5406. - The second commit is purely about format string style changes but deletes ~10 lines of code, which slightly helps me look into this file :P - The third commit is added to avoid the unnecessary block gap update in `BlockchainDb`. --------- Co-authored-by: Davide Galassi --- prdoc/pr_5540.prdoc | 12 +++ substrate/client/db/src/lib.rs | 156 +++++++++++++++------------------ 2 files changed, 82 insertions(+), 86 deletions(-) create mode 100644 prdoc/pr_5540.prdoc diff --git a/prdoc/pr_5540.prdoc b/prdoc/pr_5540.prdoc new file mode 100644 index 000000000000..2c00714c4f86 --- /dev/null +++ b/prdoc/pr_5540.prdoc @@ -0,0 +1,12 @@ +title: Avoid unnecessary block gap updates + +doc: + - audience: Node Dev + description: | + Previously, the block gap storage in database and state in `BlockchainDb` could be updated even if no changes occurred. + This commit refines the logic to ensure updates only occur when the block gap value actually changes, reducing unnecessary + writes and enhancing overall efficiency. + +crates: + - name: sc-client-db + bump: none diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs index ba0cbc09d53d..eadb26254a18 100644 --- a/substrate/client/db/src/lib.rs +++ b/substrate/client/db/src/lib.rs @@ -538,7 +538,7 @@ impl BlockchainDb { fn insert_justifications_if_pinned(&self, hash: Block::Hash, justification: Justification) { let mut cache = self.pinned_blocks_cache.write(); if !cache.contains(hash) { - return + return; } let justifications = Justifications::from(justification); @@ -551,7 +551,7 @@ impl BlockchainDb { fn insert_persisted_justifications_if_pinned(&self, hash: Block::Hash) -> ClientResult<()> { let mut cache = self.pinned_blocks_cache.write(); if !cache.contains(hash) { - return Ok(()) + return Ok(()); } let justifications = self.justifications_uncached(hash)?; @@ -565,7 +565,7 @@ impl BlockchainDb { fn insert_persisted_body_if_pinned(&self, hash: Block::Hash) -> ClientResult<()> { let mut cache = self.pinned_blocks_cache.write(); if !cache.contains(hash) { - return Ok(()) + return Ok(()); } let body = self.body_uncached(hash)?; @@ -594,8 +594,7 @@ impl BlockchainDb { Ok(justifications) => Ok(Some(justifications)), Err(err) => return Err(sp_blockchain::Error::Backend(format!( - "Error decoding justifications: {}", - err + "Error decoding justifications: {err}" ))), }, None => Ok(None), @@ -610,10 +609,7 @@ impl BlockchainDb { match Decode::decode(&mut &body[..]) { Ok(body) => return Ok(Some(body)), Err(err) => - return Err(sp_blockchain::Error::Backend(format!( - "Error decoding body: {}", - err - ))), + return Err(sp_blockchain::Error::Backend(format!("Error decoding body: {err}"))), } } @@ -636,8 +632,7 @@ impl BlockchainDb { let ex = Block::Extrinsic::decode(&mut input).map_err( |err| { sp_blockchain::Error::Backend(format!( - "Error decoding indexed extrinsic: {}", - err + "Error decoding indexed extrinsic: {err}" )) }, )?; @@ -645,8 +640,7 @@ impl BlockchainDb { }, None => return Err(sp_blockchain::Error::Backend(format!( - "Missing indexed transaction {:?}", - hash + "Missing indexed transaction {hash:?}" ))), }; }, @@ -655,12 +649,11 @@ impl BlockchainDb { }, } } - return Ok(Some(body)) + return Ok(Some(body)); }, Err(err) => return Err(sp_blockchain::Error::Backend(format!( - "Error decoding body list: {}", - err + "Error decoding body list: {err}", ))), } } @@ -672,7 +665,7 @@ impl sc_client_api::blockchain::HeaderBackend for Blockcha fn header(&self, hash: Block::Hash) -> ClientResult> { let mut cache = self.header_cache.lock(); if let Some(result) = cache.get_refresh(&hash) { - return Ok(result.clone()) + return Ok(result.clone()); } let header = utils::read_header( &*self.db, @@ -724,7 +717,7 @@ impl sc_client_api::blockchain::Backend for BlockchainDb ClientResult>> { let cache = self.pinned_blocks_cache.read(); if let Some(result) = cache.body(&hash) { - return Ok(result.clone()) + return Ok(result.clone()); } self.body_uncached(hash) @@ -733,7 +726,7 @@ impl sc_client_api::blockchain::Backend for BlockchainDb ClientResult> { let cache = self.pinned_blocks_cache.read(); if let Some(result) = cache.justifications(&hash) { - return Ok(result.clone()) + return Ok(result.clone()); } self.justifications_uncached(hash) @@ -778,8 +771,7 @@ impl sc_client_api::blockchain::Backend for BlockchainDb transactions.push(t), None => return Err(sp_blockchain::Error::Backend(format!( - "Missing indexed transaction {:?}", - hash + "Missing indexed transaction {hash:?}", ))), } } @@ -787,7 +779,7 @@ impl sc_client_api::blockchain::Backend for BlockchainDb - Err(sp_blockchain::Error::Backend(format!("Error decoding body list: {}", err))), + Err(sp_blockchain::Error::Backend(format!("Error decoding body list: {err}"))), } } } @@ -810,8 +802,7 @@ impl HeaderMetadata for BlockchainDb { }) .ok_or_else(|| { ClientError::UnknownBlock(format!( - "Header was not found in the database: {:?}", - hash + "Header was not found in the database: {hash:?}", )) }) }, @@ -858,7 +849,7 @@ impl BlockImportOperation { } if count > 0 { - log::debug!(target: "sc_offchain", "Applied {} offchain indexing changes.", count); + log::debug!(target: "sc_offchain", "Applied {count} offchain indexing changes."); } } @@ -877,7 +868,7 @@ impl BlockImportOperation { state_version: StateVersion, ) -> ClientResult { if storage.top.keys().any(|k| well_known_keys::is_child_storage_key(k)) { - return Err(sp_blockchain::Error::InvalidState) + return Err(sp_blockchain::Error::InvalidState); } let child_delta = storage.children_default.values().map(|child_content| { @@ -1011,7 +1002,7 @@ impl sp_state_machine::Storage> for StorageDb Backend { if meta.best_number.saturating_sub(best_number).saturated_into::() > self.canonicalization_delay { - return Err(sp_blockchain::Error::SetHeadTooOld) + return Err(sp_blockchain::Error::SetHeadTooOld); } let parent_exists = @@ -1307,7 +1298,7 @@ impl Backend { (&r.number, &r.hash) ); - return Err(sp_blockchain::Error::NotInFinalizedChain) + return Err(sp_blockchain::Error::NotInFinalizedChain); } retracted.push(r.hash); @@ -1349,10 +1340,9 @@ impl Backend { *header.parent_hash() != last_finalized { return Err(sp_blockchain::Error::NonSequentialFinalization(format!( - "Last finalized {:?} not parent of {:?}", - last_finalized, + "Last finalized {last_finalized:?} not parent of {:?}", header.hash() - ))) + ))); } Ok(()) } @@ -1429,10 +1419,10 @@ impl Backend { hash_to_canonicalize, to_canonicalize.saturated_into(), ) { - return Ok(()) + return Ok(()); } - trace!(target: "db", "Canonicalize block #{} ({:?})", to_canonicalize, hash_to_canonicalize); + trace!(target: "db", "Canonicalize block #{to_canonicalize} ({hash_to_canonicalize:?})"); let commit = self.storage.state_db.canonicalize_block(&hash_to_canonicalize).map_err( sp_blockchain::Error::from_state_db::< sc_state_db::Error, @@ -1456,6 +1446,8 @@ impl Backend { (meta.best_number, meta.finalized_hash, meta.finalized_number, meta.block_gap) }; + let mut block_gap_updated = false; + let mut current_transaction_justifications: HashMap = HashMap::new(); let mut finalized_blocks = operation.finalized_blocks.into_iter().peekable(); @@ -1623,13 +1615,8 @@ impl Backend { let is_best = pending_block.leaf_state.is_best(); debug!( target: "db", - "DB Commit {:?} ({}), best={}, state={}, existing={}, finalized={}", - hash, - number, - is_best, + "DB Commit {hash:?} ({number}), best={is_best}, state={}, existing={existing_header}, finalized={finalized}", operation.commit_state, - existing_header, - finalized, ); self.state_usage.merge_sm(operation.old_state.usage_info()); @@ -1693,19 +1680,20 @@ impl Backend { number, hash, )?; - } - if start > end { - transaction.remove(columns::META, meta_keys::BLOCK_GAP); - block_gap = None; - debug!(target: "db", "Removed block gap."); - } else { - block_gap = Some((start, end)); - debug!(target: "db", "Update block gap. {:?}", block_gap); - transaction.set( - columns::META, - meta_keys::BLOCK_GAP, - &(start, end).encode(), - ); + if start > end { + transaction.remove(columns::META, meta_keys::BLOCK_GAP); + block_gap = None; + debug!(target: "db", "Removed block gap."); + } else { + block_gap = Some((start, end)); + debug!(target: "db", "Update block gap. {block_gap:?}"); + transaction.set( + columns::META, + meta_keys::BLOCK_GAP, + &(start, end).encode(), + ); + } + block_gap_updated = true; } } else if number > best_num + One::one() && number > One::one() && self.blockchain.header(parent_hash)?.is_none() @@ -1713,7 +1701,8 @@ impl Backend { let gap = (best_num + One::one(), number - One::one()); transaction.set(columns::META, meta_keys::BLOCK_GAP, &gap.encode()); block_gap = Some(gap); - debug!(target: "db", "Detected block gap {:?}", block_gap); + block_gap_updated = true; + debug!(target: "db", "Detected block gap {block_gap:?}"); } } @@ -1747,9 +1736,8 @@ impl Backend { }); } else { return Err(sp_blockchain::Error::UnknownBlock(format!( - "Cannot set head {:?}", - set_head - ))) + "Cannot set head {set_head:?}", + ))); } } @@ -1759,7 +1747,7 @@ impl Backend { // Code beyond this point can't fail. if let Some((header, hash)) = imported { - trace!(target: "db", "DB Commit done {:?}", hash); + trace!(target: "db", "DB Commit done {hash:?}"); let header_metadata = CachedHeaderMetadata::from(&header); self.blockchain.insert_header_metadata(header_metadata.hash, header_metadata); cache_header(&mut self.blockchain.header_cache.lock(), hash, Some(header)); @@ -1768,7 +1756,9 @@ impl Backend { for m in meta_updates { self.blockchain.update_meta(m); } - self.blockchain.update_block_gap(block_gap); + if block_gap_updated { + self.blockchain.update_block_gap(block_gap); + } Ok(()) } @@ -1875,7 +1865,7 @@ impl Backend { transaction: &mut Transaction, id: BlockId, ) -> ClientResult<()> { - debug!(target: "db", "Removing block #{}", id); + debug!(target: "db", "Removing block #{id}"); utils::remove_from_db( transaction, &*self.storage.db, @@ -1909,8 +1899,7 @@ impl Backend { }, Err(err) => return Err(sp_blockchain::Error::Backend(format!( - "Error decoding body list: {}", - err + "Error decoding body list: {err}", ))), } } @@ -2138,14 +2127,14 @@ impl sc_client_api::backend::Backend for Backend { if number > self.blockchain.info().finalized_number || (hash != last_finalized && !is_descendent_of(&hash, &last_finalized)?) { - return Err(ClientError::NotInFinalizedChain) + return Err(ClientError::NotInFinalizedChain); } let justifications = if let Some(mut stored_justifications) = self.blockchain.justifications(hash)? { if !stored_justifications.append(justification) { - return Err(ClientError::BadJustification("Duplicate consensus engine ID".into())) + return Err(ClientError::BadJustification("Duplicate consensus engine ID".into())); } stored_justifications } else { @@ -2230,13 +2219,12 @@ impl sc_client_api::backend::Backend for Backend { let mut revert_blocks = || -> ClientResult> { for c in 0..n.saturated_into::() { if number_to_revert.is_zero() { - return Ok(c.saturated_into::>()) + return Ok(c.saturated_into::>()); } let mut transaction = Transaction::new(); let removed = self.blockchain.header(hash_to_revert)?.ok_or_else(|| { sp_blockchain::Error::UnknownBlock(format!( - "Error reverting to {}. Block header not found.", - hash_to_revert, + "Error reverting to {hash_to_revert}. Block header not found.", )) })?; let removed_hash = removed.hash(); @@ -2246,7 +2234,7 @@ impl sc_client_api::backend::Backend for Backend { if prev_number == best_number { best_hash } else { *removed.parent_hash() }; if !self.have_state_at(prev_hash, prev_number) { - return Ok(c.saturated_into::>()) + return Ok(c.saturated_into::>()); } match self.storage.state_db.revert_one() { @@ -2342,23 +2330,21 @@ impl sc_client_api::backend::Backend for Backend { let best_hash = self.blockchain.info().best_hash; if best_hash == hash { - return Err(sp_blockchain::Error::Backend(format!("Can't remove best block {:?}", hash))) + return Err(sp_blockchain::Error::Backend(format!("Can't remove best block {hash:?}"))); } let hdr = self.blockchain.header_metadata(hash)?; if !self.have_state_at(hash, hdr.number) { return Err(sp_blockchain::Error::UnknownBlock(format!( - "State already discarded for {:?}", - hash - ))) + "State already discarded for {hash:?}", + ))); } let mut leaves = self.blockchain.leaves.write(); if !leaves.contains(hdr.number, hash) { return Err(sp_blockchain::Error::Backend(format!( - "Can't remove non-leaf block {:?}", - hash - ))) + "Can't remove non-leaf block {hash:?}", + ))); } let mut transaction = Transaction::new(); @@ -2398,7 +2384,7 @@ impl sc_client_api::backend::Backend for Backend { if let Some(outcome) = remove_outcome { leaves.undo().undo_remove(outcome); } - return Err(e.into()) + return Err(e.into()); } self.blockchain().remove_header_metadata(hash); Ok(()) @@ -2420,7 +2406,7 @@ impl sc_client_api::backend::Backend for Backend { .build(); let state = RefTrackingState::new(db_state, self.storage.clone(), None); - return Ok(RecordStatsState::new(state, None, self.state_usage.clone())) + return Ok(RecordStatsState::new(state, None, self.state_usage.clone())); } } @@ -2446,8 +2432,7 @@ impl sc_client_api::backend::Backend for Backend { Ok(RecordStatsState::new(state, Some(hash), self.state_usage.clone())) } else { Err(sp_blockchain::Error::UnknownBlock(format!( - "State already discarded for {:?}", - hash + "State already discarded for {hash:?}", ))) } }, @@ -2512,16 +2497,14 @@ impl sc_client_api::backend::Backend for Backend { self.storage.state_db.pin(&hash, number.saturated_into::(), hint).map_err( |_| { sp_blockchain::Error::UnknownBlock(format!( - "Unable to pin: state already discarded for `{:?}`", - hash + "Unable to pin: state already discarded for `{hash:?}`", )) }, )?; } else { return Err(ClientError::UnknownBlock(format!( - "Can not pin block with hash `{:?}`. Block not found.", - hash - ))) + "Can not pin block with hash `{hash:?}`. Block not found.", + ))); } if self.blocks_pruning != BlocksPruning::KeepAll { @@ -4226,8 +4209,9 @@ pub(crate) mod tests { match pruning_mode { // we can only revert to blocks for which we have state, if pruning is enabled // then the last state available will be that of the latest finalized block - BlocksPruning::Some(_) => - assert_eq!(backend.blockchain().info().finalized_number, 8), + BlocksPruning::Some(_) => { + assert_eq!(backend.blockchain().info().finalized_number, 8) + }, // otherwise if we're not doing state pruning we can revert past finalized blocks _ => assert_eq!(backend.blockchain().info().finalized_number, 5), } From 4090be1ea34de666029ccf44528231b799418223 Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 4 Sep 2024 18:56:52 +0800 Subject: [PATCH 255/382] Support more foreign locations --- bridges/snowbridge/primitives/core/src/lib.rs | 27 ++--------- .../xcm-builder/src/location_conversion.rs | 46 +++++++++++++++++++ 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index c31124edf51d..ca5e7e39b8d7 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -28,9 +28,7 @@ use sp_core::{ConstU32, H256}; use sp_io::hashing::keccak_256; use sp_runtime::{traits::AccountIdConversion, RuntimeDebug}; use sp_std::prelude::*; -use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, Junction::Parachain, Location, PalletInstance, -}; +use xcm::prelude::{GlobalConsensus, Junction::Parachain, Location}; use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; /// The ID of an agent contract @@ -179,7 +177,8 @@ pub struct AssetMetadata { pub type TokenId = H256; /// Convert a token location to a stable ID that can be used on the Ethereum side -pub type TokenIdOf = HashedDescription>; +pub type TokenIdOf = + HashedDescription>>; pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); impl DescribeLocation for DescribeGlobalPrefix { @@ -188,27 +187,9 @@ impl DescribeLocation for DescribeGlobalPrefix (1, Some(GlobalConsensus(network))) => { let tail = l.clone().split_first_interior().0; let interior = Suffix::describe_location(&tail.into())?; - Some((b"pna", network, interior).encode()) + Some((b"PNA", network, interior).encode()) }, _ => None, } } } - -pub struct DescribeInner; -impl DescribeLocation for DescribeInner { - fn describe_location(l: &Location) -> Option> { - match l.unpack().1 { - [] => Some(Vec::::new().encode()), - [Parachain(id)] => Some((*id).encode()), - [Parachain(id), PalletInstance(instance)] => Some((*id, *instance).encode()), - [Parachain(id), PalletInstance(instance), GeneralIndex(index)] => - Some((*id, *instance, *index).encode()), - [Parachain(id), PalletInstance(instance), GeneralKey { data, .. }] => - Some((*id, *instance, *data).encode()), - [Parachain(id), GeneralIndex(index)] => Some((*id, *index).encode()), - [Parachain(id), GeneralKey { data, .. }] => Some((*id, *data).encode()), - _ => None, - } - } -} diff --git a/polkadot/xcm/xcm-builder/src/location_conversion.rs b/polkadot/xcm/xcm-builder/src/location_conversion.rs index 1d840e9c0dde..e90b86b04533 100644 --- a/polkadot/xcm/xcm-builder/src/location_conversion.rs +++ b/polkadot/xcm/xcm-builder/src/location_conversion.rs @@ -110,6 +110,48 @@ impl DescribeLocation for DescribeBodyTerminal { } } +pub struct DescribePalletInstanceAndGeneralIndexTerminal; +impl DescribeLocation for DescribePalletInstanceAndGeneralIndexTerminal { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, [PalletInstance(instance), GeneralIndex(index)]) => + Some((b"Pallet", b"GeneralIndex", *instance, *index).encode()), + _ => return None, + } + } +} + +pub struct DescribePalletInstanceAndGeneralKeyTerminal; +impl DescribeLocation for DescribePalletInstanceAndGeneralKeyTerminal { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, [PalletInstance(instance), GeneralKey { data, .. }]) => + Some((b"Pallet", b"GeneralKey", *instance, *data).encode()), + _ => return None, + } + } +} + +pub struct DescribeGeneralKeyTerminal; +impl DescribeLocation for DescribeGeneralKeyTerminal { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, [GeneralKey { data, .. }]) => Some((b"GeneralKey", *data).encode()), + _ => return None, + } + } +} + +pub struct DescribeGeneralIndexTerminal; +impl DescribeLocation for DescribeGeneralIndexTerminal { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, [GeneralIndex(index)]) => Some((b"GeneralIndex", *index).encode()), + _ => return None, + } + } +} + pub type DescribeAllTerminal = ( DescribeTerminus, DescribePalletTerminal, @@ -117,6 +159,10 @@ pub type DescribeAllTerminal = ( DescribeAccountKey20Terminal, DescribeTreasuryVoiceTerminal, DescribeBodyTerminal, + DescribePalletInstanceAndGeneralIndexTerminal, + DescribePalletInstanceAndGeneralKeyTerminal, + DescribeGeneralKeyTerminal, + DescribeGeneralIndexTerminal, ); pub struct DescribeFamily(PhantomData); From db41fab999c758ecaac81f73cc1eaa86e971bcf4 Mon Sep 17 00:00:00 2001 From: Egor_P Date: Wed, 4 Sep 2024 14:13:40 +0200 Subject: [PATCH 256/382] Add badges with the release info to the README.md (#5518) This PR adds the badges to the readme to show the info about the current stable release version and the version and date of the upcoming stable release. The idea here to give a possibility for people from the outside to have an overview about the current and upcoming release, until we have a proper public release calendar. Screenshot 2024-08-29 at 14 47 57 --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi --- README.md | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 34d657194daa..702c853684cd 100644 --- a/README.md +++ b/README.md @@ -38,27 +38,15 @@ curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/paritytec ## 🚀 Releases -> [!NOTE] -> Our release process is still Work-In-Progress and may not yet reflect the aspired outline -> here. - -The Polkadot-SDK has two release channels: `stable` and `nightly`. Production software is advised to -only use `stable`. `nightly` is meant for tinkerers to try out the latest features. The detailed -release process is described in [RELEASE.md](docs/RELEASE.md). - -You can use [`psvm`](https://github.com/paritytech/psvm) to manage your Polkadot-SDK dependency -versions in downstream projects. - -### 😌 Stable - -`stable` releases have a support duration of **three months**. In this period, the release will not -have any breaking changes. It will receive bug fixes, security fixes, performance fixes and new -non-breaking features on a **two week** cadence. + +![Current Stable Release](https://raw.githubusercontent.com/paritytech/release-registry/main/badges/polkadot-sdk-latest.svg)  ![Next Stable Release](https://raw.githubusercontent.com/paritytech/release-registry/main/badges/polkadot-sdk-next.svg) -### 🤠 Nightly +The Polkadot-SDK is released every three months as a `stableYYMMDD` release. They are supported for +one year with patches. See the next upcoming versions in the [Release +Registry](https://github.com/paritytech/release-registry/). -`nightly` releases are released every night from the `master` branch, potentially with breaking -changes. They have pre-release version numbers in the format `major.0.0-nightlyYYMMDD`. +You can use [`psvm`](https://github.com/paritytech/psvm) to update all dependencies to a specific +version without needing to manually select the correct version for each crate. ## 🛠️ Tooling From 2561fb667370410252e6d0c9af94822950de3e76 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 4 Sep 2024 14:32:07 +0200 Subject: [PATCH 257/382] Revert "Support more foreign locations" This reverts commit 4090be1ea34de666029ccf44528231b799418223. --- bridges/snowbridge/primitives/core/src/lib.rs | 27 +++++++++-- .../xcm-builder/src/location_conversion.rs | 46 ------------------- 2 files changed, 23 insertions(+), 50 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index ca5e7e39b8d7..c31124edf51d 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -28,7 +28,9 @@ use sp_core::{ConstU32, H256}; use sp_io::hashing::keccak_256; use sp_runtime::{traits::AccountIdConversion, RuntimeDebug}; use sp_std::prelude::*; -use xcm::prelude::{GlobalConsensus, Junction::Parachain, Location}; +use xcm::prelude::{ + GeneralIndex, GeneralKey, GlobalConsensus, Junction::Parachain, Location, PalletInstance, +}; use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; /// The ID of an agent contract @@ -177,8 +179,7 @@ pub struct AssetMetadata { pub type TokenId = H256; /// Convert a token location to a stable ID that can be used on the Ethereum side -pub type TokenIdOf = - HashedDescription>>; +pub type TokenIdOf = HashedDescription>; pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); impl DescribeLocation for DescribeGlobalPrefix { @@ -187,9 +188,27 @@ impl DescribeLocation for DescribeGlobalPrefix (1, Some(GlobalConsensus(network))) => { let tail = l.clone().split_first_interior().0; let interior = Suffix::describe_location(&tail.into())?; - Some((b"PNA", network, interior).encode()) + Some((b"pna", network, interior).encode()) }, _ => None, } } } + +pub struct DescribeInner; +impl DescribeLocation for DescribeInner { + fn describe_location(l: &Location) -> Option> { + match l.unpack().1 { + [] => Some(Vec::::new().encode()), + [Parachain(id)] => Some((*id).encode()), + [Parachain(id), PalletInstance(instance)] => Some((*id, *instance).encode()), + [Parachain(id), PalletInstance(instance), GeneralIndex(index)] => + Some((*id, *instance, *index).encode()), + [Parachain(id), PalletInstance(instance), GeneralKey { data, .. }] => + Some((*id, *instance, *data).encode()), + [Parachain(id), GeneralIndex(index)] => Some((*id, *index).encode()), + [Parachain(id), GeneralKey { data, .. }] => Some((*id, *data).encode()), + _ => None, + } + } +} diff --git a/polkadot/xcm/xcm-builder/src/location_conversion.rs b/polkadot/xcm/xcm-builder/src/location_conversion.rs index e90b86b04533..1d840e9c0dde 100644 --- a/polkadot/xcm/xcm-builder/src/location_conversion.rs +++ b/polkadot/xcm/xcm-builder/src/location_conversion.rs @@ -110,48 +110,6 @@ impl DescribeLocation for DescribeBodyTerminal { } } -pub struct DescribePalletInstanceAndGeneralIndexTerminal; -impl DescribeLocation for DescribePalletInstanceAndGeneralIndexTerminal { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, [PalletInstance(instance), GeneralIndex(index)]) => - Some((b"Pallet", b"GeneralIndex", *instance, *index).encode()), - _ => return None, - } - } -} - -pub struct DescribePalletInstanceAndGeneralKeyTerminal; -impl DescribeLocation for DescribePalletInstanceAndGeneralKeyTerminal { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, [PalletInstance(instance), GeneralKey { data, .. }]) => - Some((b"Pallet", b"GeneralKey", *instance, *data).encode()), - _ => return None, - } - } -} - -pub struct DescribeGeneralKeyTerminal; -impl DescribeLocation for DescribeGeneralKeyTerminal { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, [GeneralKey { data, .. }]) => Some((b"GeneralKey", *data).encode()), - _ => return None, - } - } -} - -pub struct DescribeGeneralIndexTerminal; -impl DescribeLocation for DescribeGeneralIndexTerminal { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, [GeneralIndex(index)]) => Some((b"GeneralIndex", *index).encode()), - _ => return None, - } - } -} - pub type DescribeAllTerminal = ( DescribeTerminus, DescribePalletTerminal, @@ -159,10 +117,6 @@ pub type DescribeAllTerminal = ( DescribeAccountKey20Terminal, DescribeTreasuryVoiceTerminal, DescribeBodyTerminal, - DescribePalletInstanceAndGeneralIndexTerminal, - DescribePalletInstanceAndGeneralKeyTerminal, - DescribeGeneralKeyTerminal, - DescribeGeneralIndexTerminal, ); pub struct DescribeFamily(PhantomData); From cc3b7bbd19f03e6db8d37c57f00f8e85fd1371fc Mon Sep 17 00:00:00 2001 From: Muharem Date: Wed, 4 Sep 2024 14:57:14 +0200 Subject: [PATCH 258/382] Collective: dynamic deposit based on number of proposals (#3151) Introduce a dynamic proposal deposit mechanism influenced by the total number of active proposals, with the option to set the deposit to none. The potential cost (e.g., balance hold) for proposal submission and storage is determined by the implementation of the `Consideration` trait. The footprint is defined as `proposal_count`, representing the total number of active proposals in the system, excluding the one currently being proposed. This cost may vary based on the proposal count. The pallet also offers various types to define a cost strategy based on the number of proposals. Two new calls are introduced: - kill(origin, proposal_hash): the cancellation of a proposal, accompanied by the burning of the associated cost/consideration ticket. - release_proposal_cost(origin, proposal_hash): the release of the cost for a non-active proposal. Additionally change: - benchmarks have been upgraded to benchmarks::v2 for collective pallet; - `ensure_successful` function added to the `Consideration` under `runtime-benchmarks` feature. --------- Co-authored-by: command-bot <> Co-authored-by: Oliver Tale-Yazdi Co-authored-by: GitHub Action --- Cargo.lock | 2 + .../collectives-westend/src/lib.rs | 3 + .../src/weights/pallet_collective.rs | 274 ++++++---- prdoc/pr_3151.prdoc | 49 ++ substrate/bin/node/runtime/src/lib.rs | 21 + substrate/frame/alliance/src/mock.rs | 3 + .../balances/src/tests/fungible_tests.rs | 13 +- substrate/frame/collective/Cargo.toml | 9 +- .../frame/collective/src/benchmarking.rs | 503 +++++++++++++----- substrate/frame/collective/src/lib.rs | 251 ++++++++- substrate/frame/collective/src/tests.rs | 266 ++++++++- substrate/frame/collective/src/weights.rs | 430 +++++++++------ substrate/frame/utility/src/tests.rs | 4 + 13 files changed, 1402 insertions(+), 426 deletions(-) create mode 100644 prdoc/pr_3151.prdoc diff --git a/Cargo.lock b/Cargo.lock index 83c40e9a4c7a..b3eefe1826c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10315,10 +10315,12 @@ dependencies = [ name = "pallet-collective" version = "28.0.0" dependencies = [ + "docify", "frame-benchmarking", "frame-support", "frame-system", "log", + "pallet-balances", "parity-scale-codec", "scale-info", "sp-core", diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index 21206d26dd57..dea2eb03db3a 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -552,6 +552,9 @@ impl pallet_collective::Config for Runtime { type SetMembersOrigin = EnsureRoot; type WeightInfo = weights::pallet_collective::WeightInfo; type MaxProposalWeight = MaxProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = (); } pub const MAX_FELLOWS: u32 = ALLIANCE_MAX_MEMBERS; diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_collective.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_collective.rs index 9133baa6120c..d456f5b8c460 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_collective.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_collective.rs @@ -1,42 +1,41 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_collective` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-polkadot-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-westend-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=collectives-polkadot-dev -// --wasm-execution=compiled -// --pallet=pallet_collective -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./parachains/runtimes/collectives/collectives-polkadot/src/weights/ +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_collective +// --chain=collectives-westend-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -63,14 +62,14 @@ impl pallet_collective::WeightInfo for WeightInfo { fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + m * (3232 ±0) + p * (3190 ±0)` - // Estimated: `15691 + m * (1967 ±23) + p * (4332 ±23)` - // Minimum execution time: 16_410_000 picoseconds. - Weight::from_parts(16_816_000, 0) - .saturating_add(Weight::from_parts(0, 15691)) - // Standard Error: 59_812 - .saturating_add(Weight::from_parts(4_516_537, 0).saturating_mul(m.into())) - // Standard Error: 59_812 - .saturating_add(Weight::from_parts(7_992_168, 0).saturating_mul(p.into())) + // Estimated: `15728 + m * (1967 ±23) + p * (4332 ±23)` + // Minimum execution time: 16_539_000 picoseconds. + Weight::from_parts(16_884_000, 0) + .saturating_add(Weight::from_parts(0, 15728)) + // Standard Error: 65_205 + .saturating_add(Weight::from_parts(4_926_489, 0).saturating_mul(m.into())) + // Standard Error: 65_205 + .saturating_add(Weight::from_parts(9_044_204, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes(2)) @@ -84,15 +83,15 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `m` is `[1, 100]`. fn execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `32 + m * (32 ±0)` - // Estimated: `1518 + m * (32 ±0)` - // Minimum execution time: 14_418_000 picoseconds. - Weight::from_parts(13_588_617, 0) - .saturating_add(Weight::from_parts(0, 1518)) - // Standard Error: 21 - .saturating_add(Weight::from_parts(1_711, 0).saturating_mul(b.into())) - // Standard Error: 223 - .saturating_add(Weight::from_parts(13_836, 0).saturating_mul(m.into())) + // Measured: `69 + m * (32 ±0)` + // Estimated: `1555 + m * (32 ±0)` + // Minimum execution time: 16_024_000 picoseconds. + Weight::from_parts(15_295_443, 0) + .saturating_add(Weight::from_parts(0, 1555)) + // Standard Error: 22 + .saturating_add(Weight::from_parts(1_501, 0).saturating_mul(b.into())) + // Standard Error: 229 + .saturating_add(Weight::from_parts(12_430, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -104,15 +103,15 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `m` is `[1, 100]`. fn propose_execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `32 + m * (32 ±0)` - // Estimated: `3498 + m * (32 ±0)` - // Minimum execution time: 17_174_000 picoseconds. - Weight::from_parts(16_192_764, 0) - .saturating_add(Weight::from_parts(0, 3498)) - // Standard Error: 27 - .saturating_add(Weight::from_parts(1_672, 0).saturating_mul(b.into())) - // Standard Error: 280 - .saturating_add(Weight::from_parts(24_343, 0).saturating_mul(m.into())) + // Measured: `69 + m * (32 ±0)` + // Estimated: `3535 + m * (32 ±0)` + // Minimum execution time: 18_277_000 picoseconds. + Weight::from_parts(17_322_061, 0) + .saturating_add(Weight::from_parts(0, 3535)) + // Standard Error: 29 + .saturating_add(Weight::from_parts(1_725, 0).saturating_mul(b.into())) + // Standard Error: 309 + .saturating_add(Weight::from_parts(25_640, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -131,17 +130,17 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `p` is `[1, 100]`. fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `322 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `3714 + m * (33 ±0) + p * (36 ±0)` - // Minimum execution time: 23_970_000 picoseconds. - Weight::from_parts(23_004_052, 0) - .saturating_add(Weight::from_parts(0, 3714)) - // Standard Error: 123 - .saturating_add(Weight::from_parts(2_728, 0).saturating_mul(b.into())) - // Standard Error: 1_291 - .saturating_add(Weight::from_parts(32_731, 0).saturating_mul(m.into())) - // Standard Error: 1_275 - .saturating_add(Weight::from_parts(199_537, 0).saturating_mul(p.into())) + // Measured: `359 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `3751 + m * (33 ±0) + p * (36 ±0)` + // Minimum execution time: 23_915_000 picoseconds. + Weight::from_parts(22_895_005, 0) + .saturating_add(Weight::from_parts(0, 3751)) + // Standard Error: 116 + .saturating_add(Weight::from_parts(4_047, 0).saturating_mul(b.into())) + // Standard Error: 1_211 + .saturating_add(Weight::from_parts(37_038, 0).saturating_mul(m.into())) + // Standard Error: 1_196 + .saturating_add(Weight::from_parts(203_435, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(m.into())) @@ -154,13 +153,13 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `771 + m * (64 ±0)` - // Estimated: `4235 + m * (64 ±0)` - // Minimum execution time: 25_843_000 picoseconds. - Weight::from_parts(26_092_578, 0) - .saturating_add(Weight::from_parts(0, 4235)) - // Standard Error: 1_785 - .saturating_add(Weight::from_parts(67_298, 0).saturating_mul(m.into())) + // Measured: `808 + m * (64 ±0)` + // Estimated: `4272 + m * (64 ±0)` + // Minimum execution time: 28_571_000 picoseconds. + Weight::from_parts(29_711_839, 0) + .saturating_add(Weight::from_parts(0, 4272)) + // Standard Error: 825 + .saturating_add(Weight::from_parts(39_661, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) @@ -177,15 +176,15 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `360 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `3805 + m * (65 ±0) + p * (36 ±0)` - // Minimum execution time: 27_543_000 picoseconds. - Weight::from_parts(26_505_473, 0) - .saturating_add(Weight::from_parts(0, 3805)) - // Standard Error: 1_054 - .saturating_add(Weight::from_parts(35_295, 0).saturating_mul(m.into())) - // Standard Error: 1_028 - .saturating_add(Weight::from_parts(190_508, 0).saturating_mul(p.into())) + // Measured: `397 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `3842 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 27_742_000 picoseconds. + Weight::from_parts(28_014_736, 0) + .saturating_add(Weight::from_parts(0, 3842)) + // Standard Error: 1_221 + .saturating_add(Weight::from_parts(35_335, 0).saturating_mul(m.into())) + // Standard Error: 1_191 + .saturating_add(Weight::from_parts(193_513, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) @@ -204,17 +203,17 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `662 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `3979 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` - // Minimum execution time: 40_375_000 picoseconds. - Weight::from_parts(34_081_294, 0) - .saturating_add(Weight::from_parts(0, 3979)) - // Standard Error: 196 - .saturating_add(Weight::from_parts(3_796, 0).saturating_mul(b.into())) - // Standard Error: 2_072 - .saturating_add(Weight::from_parts(50_954, 0).saturating_mul(m.into())) - // Standard Error: 2_020 - .saturating_add(Weight::from_parts(246_000, 0).saturating_mul(p.into())) + // Measured: `699 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `4016 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` + // Minimum execution time: 38_274_000 picoseconds. + Weight::from_parts(37_886_500, 0) + .saturating_add(Weight::from_parts(0, 4016)) + // Standard Error: 165 + .saturating_add(Weight::from_parts(3_242, 0).saturating_mul(b.into())) + // Standard Error: 1_753 + .saturating_add(Weight::from_parts(33_851, 0).saturating_mul(m.into())) + // Standard Error: 1_709 + .saturating_add(Weight::from_parts(229_245, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) @@ -235,15 +234,15 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `458 + m * (48 ±0) + p * (36 ±0)` - // Estimated: `3898 + m * (49 ±0) + p * (36 ±0)` - // Minimum execution time: 28_793_000 picoseconds. - Weight::from_parts(29_656_832, 0) - .saturating_add(Weight::from_parts(0, 3898)) - // Standard Error: 1_214 - .saturating_add(Weight::from_parts(22_148, 0).saturating_mul(m.into())) - // Standard Error: 1_184 - .saturating_add(Weight::from_parts(189_860, 0).saturating_mul(p.into())) + // Measured: `495 + m * (48 ±0) + p * (36 ±0)` + // Estimated: `3935 + m * (49 ±0) + p * (36 ±0)` + // Minimum execution time: 29_178_000 picoseconds. + Weight::from_parts(28_752_686, 0) + .saturating_add(Weight::from_parts(0, 3935)) + // Standard Error: 1_230 + .saturating_add(Weight::from_parts(42_254, 0).saturating_mul(m.into())) + // Standard Error: 1_200 + .saturating_add(Weight::from_parts(210_610, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 49).saturating_mul(m.into())) @@ -264,17 +263,17 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `682 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `3999 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` - // Minimum execution time: 40_887_000 picoseconds. - Weight::from_parts(39_529_567, 0) - .saturating_add(Weight::from_parts(0, 3999)) - // Standard Error: 191 - .saturating_add(Weight::from_parts(2_802, 0).saturating_mul(b.into())) - // Standard Error: 2_021 - .saturating_add(Weight::from_parts(35_956, 0).saturating_mul(m.into())) - // Standard Error: 1_970 - .saturating_add(Weight::from_parts(235_154, 0).saturating_mul(p.into())) + // Measured: `719 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `4036 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` + // Minimum execution time: 40_296_000 picoseconds. + Weight::from_parts(41_629_338, 0) + .saturating_add(Weight::from_parts(0, 4036)) + // Standard Error: 162 + .saturating_add(Weight::from_parts(2_608, 0).saturating_mul(b.into())) + // Standard Error: 1_717 + .saturating_add(Weight::from_parts(29_637, 0).saturating_mul(m.into())) + // Standard Error: 1_674 + .saturating_add(Weight::from_parts(230_371, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) @@ -290,15 +289,54 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `p` is `[1, 100]`. fn disapprove_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `189 + p * (32 ±0)` - // Estimated: `1674 + p * (32 ±0)` - // Minimum execution time: 14_040_000 picoseconds. - Weight::from_parts(15_075_964, 0) - .saturating_add(Weight::from_parts(0, 1674)) - // Standard Error: 854 - .saturating_add(Weight::from_parts(159_597, 0).saturating_mul(p.into())) + // Measured: `226 + p * (32 ±0)` + // Estimated: `1711 + p * (32 ±0)` + // Minimum execution time: 15_385_000 picoseconds. + Weight::from_parts(17_009_286, 0) + .saturating_add(Weight::from_parts(0, 1711)) + // Standard Error: 1_192 + .saturating_add(Weight::from_parts(170_070, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(p.into())) } + /// Storage: `AllianceMotion::ProposalOf` (r:1 w:1) + /// Proof: `AllianceMotion::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::CostOf` (r:1 w:0) + /// Proof: `AllianceMotion::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:1) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Voting` (r:0 w:1) + /// Proof: `AllianceMotion::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `d` is `[0, 1]`. + /// The range of component `p` is `[1, 100]`. + fn kill(d: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1497 + p * (36 ±0)` + // Estimated: `4896 + d * (123 ±6) + p * (37 ±0)` + // Minimum execution time: 22_455_000 picoseconds. + Weight::from_parts(24_273_426, 0) + .saturating_add(Weight::from_parts(0, 4896)) + // Standard Error: 82_114 + .saturating_add(Weight::from_parts(996_567, 0).saturating_mul(d.into())) + // Standard Error: 1_271 + .saturating_add(Weight::from_parts(213_968, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(Weight::from_parts(0, 123).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) + } + /// Storage: `AllianceMotion::ProposalOf` (r:1 w:0) + /// Proof: `AllianceMotion::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::CostOf` (r:1 w:0) + /// Proof: `AllianceMotion::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn release_proposal_cost() -> Weight { + // Proof Size summary in bytes: + // Measured: `911` + // Estimated: `4376` + // Minimum execution time: 18_273_000 picoseconds. + Weight::from_parts(19_196_000, 0) + .saturating_add(Weight::from_parts(0, 4376)) + .saturating_add(T::DbWeight::get().reads(2)) + } } diff --git a/prdoc/pr_3151.prdoc b/prdoc/pr_3151.prdoc new file mode 100644 index 000000000000..5e43b86a9753 --- /dev/null +++ b/prdoc/pr_3151.prdoc @@ -0,0 +1,49 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Dynamic deposit based on number of proposals + +doc: + - audience: + - Runtime User + - Runtime Dev + description: | + Introduce a dynamic proposal deposit mechanism influenced by the total number of active + proposals, with the option to set the deposit to none. + + The potential cost (e.g., balance hold) for proposal submission and storage is determined + by the implementation of the `Consideration` trait. The footprint is defined as `proposal_count`, + representing the total number of active proposals in the system, excluding the one currently + being proposed. This cost may vary based on the proposal count. The pallet also offers various + types to define a cost strategy based on the number of proposals. + + Two new calls are introduced: + - kill(origin, proposal_hash): the cancellation of a proposal, accompanied by the burning + of the associated cost/consideration ticket. + - release_proposal_cost(origin, proposal_hash): the release of the cost for a non-active proposal. + + New config parameters: + - DisapproveOrigin: origin from which a proposal in any status may be disapproved without + associated cost for a proposer; + - KillOrigin: Origin from which any malicious proposal may be killed with associated cost + for a proposer; + - Consideration: mechanism to assess the necessity of some cost for publishing and storing + a proposal. Set to unit type to have not submission cost; + + Additionally change: + - benchmarks have been upgraded to benchmarks::v2 for collective pallet; + - `ensure_successful` function added to the `Consideration` under `runtime-benchmarks` feature. + +crates: + - name: pallet-collective + bump: major + - name: collectives-westend-runtime + bump: major + - name: kitchensink-runtime + bump: major + - name: pallet-alliance + bump: patch + - name: pallet-balances + bump: patch + - name: pallet-utility + bump: patch diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 31584427b3b6..001b2273c9b2 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1114,6 +1114,9 @@ parameter_types! { pub const CouncilMotionDuration: BlockNumber = 5 * DAYS; pub const CouncilMaxProposals: u32 = 100; pub const CouncilMaxMembers: u32 = 100; + pub const ProposalDepositOffset: Balance = ExistentialDeposit::get() + ExistentialDeposit::get(); + pub const ProposalHoldReason: RuntimeHoldReason = + RuntimeHoldReason::Council(pallet_collective::HoldReason::ProposalSubmission); } type CouncilCollective = pallet_collective::Instance1; @@ -1128,6 +1131,18 @@ impl pallet_collective::Config for Runtime { type WeightInfo = pallet_collective::weights::SubstrateWeight; type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxCollectivesProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = HoldConsideration< + AccountId, + Balances, + ProposalHoldReason, + pallet_collective::deposit::Delayed< + ConstU32<2>, + pallet_collective::deposit::Linear, ProposalDepositOffset>, + >, + u32, + >; } parameter_types! { @@ -1189,6 +1204,9 @@ impl pallet_collective::Config for Runtime { type WeightInfo = pallet_collective::weights::SubstrateWeight; type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxCollectivesProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = (); } type EnsureRootOrHalfCouncil = EitherOfDiverse< @@ -2027,6 +2045,9 @@ impl pallet_collective::Config for Runtime { type WeightInfo = pallet_collective::weights::SubstrateWeight; type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxCollectivesProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = (); } parameter_types! { diff --git a/substrate/frame/alliance/src/mock.rs b/substrate/frame/alliance/src/mock.rs index 9cd96019781e..5442e8779020 100644 --- a/substrate/frame/alliance/src/mock.rs +++ b/substrate/frame/alliance/src/mock.rs @@ -77,6 +77,9 @@ impl pallet_collective::Config for Test { type WeightInfo = (); type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = (); } parameter_types! { diff --git a/substrate/frame/balances/src/tests/fungible_tests.rs b/substrate/frame/balances/src/tests/fungible_tests.rs index ac373a351d34..4a8b213c645e 100644 --- a/substrate/frame/balances/src/tests/fungible_tests.rs +++ b/substrate/frame/balances/src/tests/fungible_tests.rs @@ -518,8 +518,9 @@ fn freeze_consideration_works() { let who = 4; // freeze amount taken somewhere outside of our (Consideration) scope. let extend_freeze = 15; - let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); - assert!(zero_ticket.is_none()); + + let ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); + assert!(ticket.is_none()); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); @@ -536,7 +537,6 @@ fn freeze_consideration_works() { let ticket = ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(); assert!(ticket.is_none()); - assert_eq!(ticket, zero_ticket); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0 + extend_freeze); let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); @@ -565,11 +565,11 @@ fn hold_consideration_works() { // hold amount taken somewhere outside of our (Consideration) scope. let extend_hold = 15; - let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); - assert!(zero_ticket.is_none()); + let ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); + assert!(ticket.is_none()); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); @@ -583,7 +583,6 @@ fn hold_consideration_works() { let ticket = ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(); assert!(ticket.is_none()); - assert_eq!(ticket, zero_ticket); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0 + extend_hold); let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); diff --git a/substrate/frame/collective/Cargo.toml b/substrate/frame/collective/Cargo.toml index f6810f26f056..59a9d23f7b19 100644 --- a/substrate/frame/collective/Cargo.toml +++ b/substrate/frame/collective/Cargo.toml @@ -17,15 +17,19 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { features = ["derive"], workspace = true } +docify = { workspace = true } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } frame-benchmarking = { optional = true, workspace = true } -frame-support = { workspace = true } +frame-support = { features = ["experimental"], workspace = true } frame-system = { workspace = true } sp-core = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } +[dev-dependencies] +pallet-balances = { workspace = true, default-features = false } + [features] default = ["std"] std = [ @@ -34,6 +38,7 @@ std = [ "frame-support/std", "frame-system/std", "log/std", + "pallet-balances/std", "scale-info/std", "sp-core/std", "sp-io/std", @@ -43,10 +48,12 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", + "pallet-balances/try-runtime", "sp-runtime/try-runtime", ] diff --git a/substrate/frame/collective/src/benchmarking.rs b/substrate/frame/collective/src/benchmarking.rs index 7b5df17b60a6..baf7cce8437f 100644 --- a/substrate/frame/collective/src/benchmarking.rs +++ b/substrate/frame/collective/src/benchmarking.rs @@ -23,7 +23,10 @@ use crate::Pallet as Collective; use core::mem::size_of; use sp_runtime::traits::Bounded; -use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::{ + v1::{account, whitelisted_caller}, + v2::*, +}; use frame_system::{ pallet_prelude::BlockNumberFor, Call as SystemCall, Pallet as System, RawOrigin as SystemOrigin, }; @@ -36,24 +39,31 @@ fn assert_last_event, I: 'static>(generic_event: >:: frame_system::Pallet::::assert_last_event(generic_event.into()); } +fn assert_has_event, I: 'static>(generic_event: >::RuntimeEvent) { + frame_system::Pallet::::assert_has_event(generic_event.into()); +} + fn id_to_remark_data(id: u32, length: usize) -> Vec { id.to_le_bytes().into_iter().cycle().take(length).collect() } -benchmarks_instance_pallet! { - set_members { - let m in 0 .. T::MaxMembers::get(); - let n in 0 .. T::MaxMembers::get(); - let p in 0 .. T::MaxProposals::get(); +#[instance_benchmarks(where T: Config, I: 'static)] +mod benchmarks { + use super::*; + #[benchmark] + fn set_members( + m: Linear<0, { T::MaxMembers::get() }>, + n: Linear<0, { T::MaxMembers::get() }>, + p: Linear<0, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { // Set old members. // We compute the difference of old and new members, so it should influence timing. let mut old_members = vec![]; - for i in 0 .. m { + for i in 0..m { let old_member = account::("old member", i, SEED); old_members.push(old_member); } - let old_members_count = old_members.len() as u32; Collective::::set_members( SystemOrigin::Root.into(), @@ -64,24 +74,27 @@ benchmarks_instance_pallet! { // If there were any old members generate a bunch of proposals. if m > 0 { + let caller = old_members.last().unwrap().clone(); // Set a high threshold for proposals passing so that they stay around. let threshold = m.max(2); // Length of the proposals should be irrelevant to `set_members`. let length = 100; - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, length) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, length) }.into(); Collective::::propose( - SystemOrigin::Signed(old_members.last().unwrap().clone()).into(), + SystemOrigin::Signed(caller.clone()).into(), threshold, Box::new(proposal.clone()), MAX_BYTES, )?; let hash = T::Hashing::hash_of(&proposal); // Vote on the proposal to increase state relevant for `set_members`. - // Not voting for last old member because they proposed and not voting for the first member - // to keep the proposal from passing. - for j in 2 .. m - 1 { + // Not voting for last old member because they proposed and not voting for the first + // member to keep the proposal from passing. + for j in 2..m - 1 { let voter = &old_members[j as usize]; let approve = true; Collective::::vote( @@ -97,26 +110,33 @@ benchmarks_instance_pallet! { // Construct `new_members`. // It should influence timing since it will sort this vector. let mut new_members = vec![]; - for i in 0 .. n { + for i in 0..n { let member = account::("member", i, SEED); new_members.push(member); } + #[extrinsic_call] + _( + SystemOrigin::Root, + new_members.clone(), + new_members.last().cloned(), + T::MaxMembers::get(), + ); - }: _(SystemOrigin::Root, new_members.clone(), new_members.last().cloned(), T::MaxMembers::get()) - verify { new_members.sort(); assert_eq!(Members::::get(), new_members); + Ok(()) } - execute { - let b in 2 .. MAX_BYTES; - let m in 1 .. T::MaxMembers::get(); - + #[benchmark] + fn execute( + b: Linear<2, MAX_BYTES>, + m: Linear<1, { T::MaxMembers::get() }>, + ) -> Result<(), BenchmarkError> { let bytes_in_storage = b + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } @@ -124,29 +144,36 @@ benchmarks_instance_pallet! { let caller: T::AccountId = whitelisted_caller(); members.push(caller.clone()); - Collective::::set_members(SystemOrigin::Root.into(), members, None, T::MaxMembers::get())?; + Collective::::set_members( + SystemOrigin::Root.into(), + members, + None, + T::MaxMembers::get(), + )?; + + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(1, b as usize) }.into(); - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(1, b as usize) }.into(); + #[extrinsic_call] + _(SystemOrigin::Signed(caller), Box::new(proposal.clone()), bytes_in_storage); - }: _(SystemOrigin::Signed(caller), Box::new(proposal.clone()), bytes_in_storage) - verify { let proposal_hash = T::Hashing::hash_of(&proposal); // Note that execution fails due to mis-matched origin - assert_last_event::( - Event::MemberExecuted { proposal_hash, result: Ok(()) }.into() - ); + assert_last_event::(Event::MemberExecuted { proposal_hash, result: Ok(()) }.into()); + Ok(()) } // This tests when execution would happen immediately after proposal - propose_execute { - let b in 2 .. MAX_BYTES; - let m in 1 .. T::MaxMembers::get(); - + #[benchmark] + fn propose_execute( + b: Linear<2, MAX_BYTES>, + m: Linear<1, { T::MaxMembers::get() }>, + ) -> Result<(), BenchmarkError> { let bytes_in_storage = b + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } @@ -154,43 +181,62 @@ benchmarks_instance_pallet! { let caller: T::AccountId = whitelisted_caller(); members.push(caller.clone()); - Collective::::set_members(SystemOrigin::Root.into(), members, None, T::MaxMembers::get())?; + Collective::::set_members( + SystemOrigin::Root.into(), + members, + None, + T::MaxMembers::get(), + )?; - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(1, b as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(1, b as usize) }.into(); let threshold = 1; - }: propose(SystemOrigin::Signed(caller), threshold, Box::new(proposal.clone()), bytes_in_storage) - verify { + #[extrinsic_call] + propose( + SystemOrigin::Signed(caller), + threshold, + Box::new(proposal.clone()), + bytes_in_storage, + ); + let proposal_hash = T::Hashing::hash_of(&proposal); // Note that execution fails due to mis-matched origin - assert_last_event::( - Event::Executed { proposal_hash, result: Ok(()) }.into() - ); + assert_last_event::(Event::Executed { proposal_hash, result: Ok(()) }.into()); + Ok(()) } // This tests when proposal is created and queued as "proposed" - propose_proposed { - let b in 2 .. MAX_BYTES; - let m in 2 .. T::MaxMembers::get(); - let p in 1 .. T::MaxProposals::get(); - + #[benchmark] + fn propose_proposed( + b: Linear<2, MAX_BYTES>, + m: Linear<2, { T::MaxMembers::get() }>, + p: Linear<1, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { let bytes_in_storage = b + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } let caller: T::AccountId = whitelisted_caller(); members.push(caller.clone()); - Collective::::set_members(SystemOrigin::Root.into(), members, None, T::MaxMembers::get())?; + Collective::::set_members( + SystemOrigin::Root.into(), + members, + None, + T::MaxMembers::get(), + )?; let threshold = m; // Add previous proposals. - for i in 0 .. p - 1 { + for i in 0..p - 1 { + T::Consideration::ensure_successful(&caller, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); Collective::::propose( SystemOrigin::Signed(caller.clone()).into(), threshold, @@ -201,20 +247,31 @@ benchmarks_instance_pallet! { assert_eq!(Proposals::::get().len(), (p - 1) as usize); - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(p, b as usize) }.into(); + T::Consideration::ensure_successful(&caller, p); + + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(p, b as usize) }.into(); + #[extrinsic_call] + propose( + SystemOrigin::Signed(caller.clone()), + threshold, + Box::new(proposal.clone()), + bytes_in_storage, + ); - }: propose(SystemOrigin::Signed(caller.clone()), threshold, Box::new(proposal.clone()), bytes_in_storage) - verify { // New proposal is recorded assert_eq!(Proposals::::get().len(), p as usize); let proposal_hash = T::Hashing::hash_of(&proposal); - assert_last_event::(Event::Proposed { account: caller, proposal_index: p - 1, proposal_hash, threshold }.into()); + assert_last_event::( + Event::Proposed { account: caller, proposal_index: p - 1, proposal_hash, threshold } + .into(), + ); + Ok(()) } - vote { - // We choose 5 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) - let m in 5 .. T::MaxMembers::get(); - + #[benchmark] + // We choose 5 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) + fn vote(m: Linear<5, { T::MaxMembers::get() }>) -> Result<(), BenchmarkError> { let p = T::MaxProposals::get(); let b = MAX_BYTES; let bytes_in_storage = b + size_of::() as u32; @@ -223,22 +280,29 @@ benchmarks_instance_pallet! { let mut members = vec![]; let proposer: T::AccountId = account::("proposer", 0, SEED); members.push(proposer.clone()); - for i in 1 .. m - 1 { + for i in 1..m - 1 { let member = account::("member", i, SEED); members.push(member); } let voter: T::AccountId = account::("voter", 0, SEED); members.push(voter.clone()); - Collective::::set_members(SystemOrigin::Root.into(), members.clone(), None, T::MaxMembers::get())?; + Collective::::set_members( + SystemOrigin::Root.into(), + members.clone(), + None, + T::MaxMembers::get(), + )?; // Threshold is 1 less than the number of members so that one person can vote nay let threshold = m - 1; // Add previous proposals let mut last_hash = T::Hash::default(); - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&proposer, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); Collective::::propose( SystemOrigin::Signed(proposer.clone()).into(), threshold, @@ -250,7 +314,7 @@ benchmarks_instance_pallet! { let index = p - 1; // Have almost everyone vote aye on last proposal, while keeping it from passing. - for j in 0 .. m - 3 { + for j in 0..m - 3 { let voter = &members[j as usize]; let approve = true; Collective::::vote( @@ -277,20 +341,24 @@ benchmarks_instance_pallet! { // Whitelist voter account from further DB operations. let voter_key = frame_system::Account::::hashed_key_for(&voter); frame_benchmarking::benchmarking::add_to_whitelist(voter_key.into()); - }: _(SystemOrigin::Signed(voter), last_hash, index, approve) - verify { + + #[extrinsic_call] + _(SystemOrigin::Signed(voter), last_hash, index, approve); + // All proposals exist and the last proposal has just been updated. assert_eq!(Proposals::::get().len(), p as usize); let voting = Voting::::get(&last_hash).ok_or("Proposal Missing")?; assert_eq!(voting.ayes.len(), (m - 3) as usize); assert_eq!(voting.nays.len(), 1); + Ok(()) } - close_early_disapproved { - // We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) - let m in 4 .. T::MaxMembers::get(); - let p in 1 .. T::MaxProposals::get(); - + // We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) + #[benchmark] + fn close_early_disapproved( + m: Linear<4, { T::MaxMembers::get() }>, + p: Linear<1, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { let bytes = 100; let bytes_in_storage = bytes + size_of::() as u32; @@ -298,22 +366,29 @@ benchmarks_instance_pallet! { let mut members = vec![]; let proposer = account::("proposer", 0, SEED); members.push(proposer.clone()); - for i in 1 .. m - 1 { + for i in 1..m - 1 { let member = account::("member", i, SEED); members.push(member); } let voter = account::("voter", 0, SEED); members.push(voter.clone()); - Collective::::set_members(SystemOrigin::Root.into(), members.clone(), None, T::MaxMembers::get())?; + Collective::::set_members( + SystemOrigin::Root.into(), + members.clone(), + None, + T::MaxMembers::get(), + )?; // Threshold is total members so that one nay will disapprove the vote let threshold = m; // Add previous proposals let mut last_hash = T::Hash::default(); - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&proposer, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, bytes as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, bytes as usize) }.into(); Collective::::propose( SystemOrigin::Signed(proposer.clone()).into(), threshold, @@ -325,7 +400,7 @@ benchmarks_instance_pallet! { let index = p - 1; // Have most everyone vote aye on last proposal, while keeping it from passing. - for j in 0 .. m - 2 { + for j in 0..m - 2 { let voter = &members[j as usize]; let approve = true; Collective::::vote( @@ -358,39 +433,50 @@ benchmarks_instance_pallet! { // Whitelist voter account from further DB operations. let voter_key = frame_system::Account::::hashed_key_for(&voter); frame_benchmarking::benchmarking::add_to_whitelist(voter_key.into()); - }: close(SystemOrigin::Signed(voter), last_hash, index, Weight::MAX, bytes_in_storage) - verify { + + #[extrinsic_call] + close(SystemOrigin::Signed(voter), last_hash, index, Weight::MAX, bytes_in_storage); + // The last proposal is removed. assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); + Ok(()) } - close_early_approved { - let b in 2 .. MAX_BYTES; - // We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) - let m in 4 .. T::MaxMembers::get(); - let p in 1 .. T::MaxProposals::get(); - + // m: we choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) + #[benchmark] + fn close_early_approved( + b: Linear<2, MAX_BYTES>, + m: Linear<4, { T::MaxMembers::get() }>, + p: Linear<1, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { let bytes_in_storage = b + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } let caller: T::AccountId = whitelisted_caller(); members.push(caller.clone()); - Collective::::set_members(SystemOrigin::Root.into(), members.clone(), None, T::MaxMembers::get())?; + Collective::::set_members( + SystemOrigin::Root.into(), + members.clone(), + None, + T::MaxMembers::get(), + )?; // Threshold is 2 so any two ayes will approve the vote let threshold = 2; // Add previous proposals let mut last_hash = T::Hash::default(); - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); Collective::::propose( SystemOrigin::Signed(caller.clone()).into(), threshold, @@ -400,7 +486,8 @@ benchmarks_instance_pallet! { last_hash = T::Hashing::hash_of(&proposal); } - // Caller switches vote to nay on their own proposal, allowing them to be the deciding approval vote + // Caller switches vote to nay on their own proposal, allowing them to be the deciding + // approval vote Collective::::vote( SystemOrigin::Signed(caller.clone()).into(), last_hash, @@ -409,7 +496,7 @@ benchmarks_instance_pallet! { )?; // Have almost everyone vote nay on last proposal, while keeping it from failing. - for j in 2 .. m - 1 { + for j in 2..m - 1 { let voter = &members[j as usize]; let approve = false; Collective::::vote( @@ -436,27 +523,33 @@ benchmarks_instance_pallet! { Collective::::vote( SystemOrigin::Signed(caller.clone()).into(), last_hash, - index, approve, + index, + approve, )?; - }: close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage) - verify { + #[extrinsic_call] + close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage); + // The last proposal is removed. assert_eq!(Proposals::::get().len(), (p - 1) as usize); - assert_last_event::(Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into()); + assert_last_event::( + Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into(), + ); + Ok(()) } - close_disapproved { - // We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) - let m in 4 .. T::MaxMembers::get(); - let p in 1 .. T::MaxProposals::get(); - + // m: we choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) + #[benchmark] + fn close_disapproved( + m: Linear<4, { T::MaxMembers::get() }>, + p: Linear<1, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { let bytes = 100; let bytes_in_storage = bytes + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } @@ -474,9 +567,11 @@ benchmarks_instance_pallet! { // Add proposals let mut last_hash = T::Hash::default(); - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, bytes as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, bytes as usize) }.into(); Collective::::propose( SystemOrigin::Signed(caller.clone()).into(), threshold, @@ -490,7 +585,7 @@ benchmarks_instance_pallet! { // Have almost everyone vote aye on last proposal, while keeping it from passing. // A few abstainers will be the nay votes needed to fail the vote. let mut yes_votes: MemberCount = 0; - for j in 2 .. m - 1 { + for j in 2..m - 1 { let voter = &members[j as usize]; let approve = true; yes_votes += 1; @@ -499,7 +594,8 @@ benchmarks_instance_pallet! { Some(false), yes_votes, 0, - m,) { + m, + ) { break; } Collective::::vote( @@ -522,23 +618,26 @@ benchmarks_instance_pallet! { assert_eq!(Proposals::::get().len(), p as usize); // Prime nay will close it as disapproved - }: close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage) - verify { + #[extrinsic_call] + close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); + Ok(()) } - close_approved { - let b in 2 .. MAX_BYTES; - // We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) - let m in 4 .. T::MaxMembers::get(); - let p in 1 .. T::MaxProposals::get(); - + // m: we choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) + #[benchmark] + fn close_approved( + b: Linear<2, MAX_BYTES>, + m: Linear<4, { T::MaxMembers::get() }>, + p: Linear<1, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { let bytes_in_storage = b + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } @@ -556,9 +655,11 @@ benchmarks_instance_pallet! { // Add proposals let mut last_hash = T::Hash::default(); - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); Collective::::propose( SystemOrigin::Signed(caller.clone()).into(), threshold, @@ -573,19 +674,19 @@ benchmarks_instance_pallet! { SystemOrigin::Signed(caller.clone()).into(), last_hash, p - 1, - true // Vote aye. + true, // Vote aye. )?; // Have almost everyone vote nay on last proposal, while keeping it from failing. // A few abstainers will be the aye votes needed to pass the vote. - for j in 2 .. m - 1 { + for j in 2..m - 1 { let voter = &members[j as usize]; let approve = false; Collective::::vote( SystemOrigin::Signed(voter.clone()).into(), last_hash, p - 1, - approve + approve, )?; } @@ -594,22 +695,25 @@ benchmarks_instance_pallet! { assert_eq!(Proposals::::get().len(), p as usize); // Prime aye will close it as approved - }: close(SystemOrigin::Signed(caller), last_hash, p - 1, Weight::MAX, bytes_in_storage) - verify { + #[extrinsic_call] + close(SystemOrigin::Signed(caller), last_hash, p - 1, Weight::MAX, bytes_in_storage); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); - assert_last_event::(Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into()); + assert_last_event::( + Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into(), + ); + Ok(()) } - disapprove_proposal { - let p in 1 .. T::MaxProposals::get(); - + #[benchmark] + fn disapprove_proposal(p: Linear<1, { T::MaxProposals::get() }>) -> Result<(), BenchmarkError> { let m = 3; let b = MAX_BYTES; let bytes_in_storage = b + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } @@ -627,9 +731,11 @@ benchmarks_instance_pallet! { // Add proposals let mut last_hash = T::Hash::default(); - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); Collective::::propose( SystemOrigin::Signed(caller.clone()).into(), threshold, @@ -642,11 +748,150 @@ benchmarks_instance_pallet! { System::::set_block_number(BlockNumberFor::::max_value()); assert_eq!(Proposals::::get().len(), p as usize); - }: _(SystemOrigin::Root, last_hash) - verify { + let origin = + T::DisapproveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + + #[extrinsic_call] + _(origin as ::RuntimeOrigin, last_hash); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); + Ok(()) + } + + // d: `0` - if deposit is not present and `1` otherwise. + #[benchmark] + fn kill( + d: Linear<0, 1>, + p: Linear<1, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { + let m = 3; + let b = MAX_BYTES; + let bytes_in_storage = b + size_of::() as u32; + + // Construct `members`. + let mut members = vec![]; + for i in 0..m - 1 { + let member = account::("member", i, SEED); + members.push(member); + } + let caller = account::("caller", 0, SEED); + members.push(caller.clone()); + Collective::::set_members( + SystemOrigin::Root.into(), + members.clone(), + Some(caller.clone()), + T::MaxMembers::get(), + )?; + + // Threshold is one less than total members so that two nays will disapprove the vote + let threshold = m - 1; + + // Add proposals + let mut last_hash = T::Hash::default(); + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); + + // Proposals should be different so that different proposal hashes are generated + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + Collective::::propose( + SystemOrigin::Signed(caller.clone()).into(), + threshold, + Box::new(proposal.clone()), + bytes_in_storage, + )?; + last_hash = T::Hashing::hash_of(&proposal); + } + + System::::set_block_number(BlockNumberFor::::max_value()); + assert_eq!(Proposals::::get().len(), p as usize); + + if d == 0 { + CostOf::::remove(last_hash); + } + let cost_present = CostOf::::get(last_hash).is_some(); + + let origin = + T::KillOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + + #[extrinsic_call] + _(origin as ::RuntimeOrigin, last_hash); + + assert_eq!(Proposals::::get().len(), (p - 1) as usize); + assert_last_event::(Event::Killed { proposal_hash: last_hash }.into()); + if cost_present { + assert_has_event::( + Event::ProposalCostBurned { proposal_hash: last_hash, who: caller }.into(), + ); + } + Ok(()) + } + + #[benchmark] + fn release_proposal_cost() -> Result<(), BenchmarkError> { + let m = 3; + let p = T::MaxProposals::get(); + let b = MAX_BYTES; + let bytes_in_storage = b + size_of::() as u32; + + // Construct `members`. + let mut members = vec![]; + for i in 0..m - 1 { + let member = account::("member", i, SEED); + members.push(member); + } + let caller = account::("caller", 0, SEED); + members.push(caller.clone()); + Collective::::set_members( + SystemOrigin::Root.into(), + members.clone(), + Some(caller.clone()), + T::MaxMembers::get(), + )?; + + // Add proposals + let threshold = 2; + let mut last_hash = T::Hash::default(); + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); + + // Proposals should be different so that different proposal hashes are generated + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + Collective::::propose( + SystemOrigin::Signed(caller.clone()).into(), + threshold, + Box::new(proposal.clone()), + bytes_in_storage, + )?; + last_hash = T::Hashing::hash_of(&proposal); + } + + System::::set_block_number(BlockNumberFor::::max_value()); + assert_eq!(Proposals::::get().len(), p as usize); + + assert_eq!(Proposals::::get().len(), p as usize); + let _ = Collective::::remove_proposal(last_hash); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); + + let cost_present = CostOf::::get(last_hash).is_some(); + + #[extrinsic_call] + _(SystemOrigin::Signed(caller.clone()), last_hash); + + assert_eq!(CostOf::::get(last_hash), None); + if cost_present { + assert_last_event::( + Event::ProposalCostReleased { proposal_hash: last_hash, who: caller }.into(), + ); + } + Ok(()) } - impl_benchmark_test_suite!(Collective, crate::tests::ExtBuilder::default().build(), crate::tests::Test); + impl_benchmark_test_suite!( + Collective, + crate::tests::ExtBuilder::default().build(), + crate::tests::Test + ); } diff --git a/substrate/frame/collective/src/lib.rs b/substrate/frame/collective/src/lib.rs index 3544a8cddb45..79428689caaf 100644 --- a/substrate/frame/collective/src/lib.rs +++ b/substrate/frame/collective/src/lib.rs @@ -59,8 +59,8 @@ use frame_support::{ }, ensure, impl_ensure_origin_with_arg_ignoring_arg, traits::{ - Backing, ChangeMembers, EnsureOrigin, EnsureOriginWithArg, Get, GetBacking, - InitializeMembers, StorageVersion, + Backing, ChangeMembers, Consideration, EnsureOrigin, EnsureOriginWithArg, Get, GetBacking, + InitializeMembers, MaybeConsideration, StorageVersion, }, weights::Weight, }; @@ -173,6 +173,143 @@ pub struct Votes { end: BlockNumber, } +/// Types implementing various cost strategies for a given proposal count. +/// +/// These types implement [Convert](sp_runtime::traits::Convert) trait and can be used with types +/// like [HoldConsideration](`frame_support::traits::fungible::HoldConsideration`) implementing +/// [Consideration](`frame_support::traits::Consideration`) trait. +/// +/// ### Example: +/// +/// 1. Linear increasing with helper types. +#[doc = docify::embed!("src/tests.rs", deposit_types_with_linear_work)] +/// +/// 2. Geometrically increasing with helper types. +#[doc = docify::embed!("src/tests.rs", deposit_types_with_geometric_work)] +/// +/// 3. Geometrically increasing with rounding. +#[doc = docify::embed!("src/tests.rs", deposit_round_with_geometric_work)] +pub mod deposit { + use core::marker::PhantomData; + use sp_core::Get; + use sp_runtime::{traits::Convert, FixedPointNumber, FixedU128, Saturating}; + + /// Constant deposit amount regardless of current proposal count. + /// Returns `None` if configured with zero deposit. + pub struct Constant(PhantomData); + impl Convert for Constant + where + Deposit: Get, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(_: u32) -> Balance { + Deposit::get() + } + } + + /// Linear increasing with some offset. + /// f(x) = ax + b, a = `Slope`, x = `proposal_count`, b = `Offset`. + pub struct Linear(PhantomData<(Slope, Offset)>); + impl Convert for Linear + where + Slope: Get, + Offset: Get, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(proposal_count: u32) -> Balance { + let base: Balance = Slope::get().saturating_mul(proposal_count).into(); + Offset::get().saturating_add(base) + } + } + + /// Geometrically increasing. + /// f(x) = a * r^x, a = `Base`, x = `proposal_count`, r = `Ratio`. + pub struct Geometric(PhantomData<(Ratio, Base)>); + impl Convert for Geometric + where + Ratio: Get, + Base: Get, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(proposal_count: u32) -> Balance { + Ratio::get() + .saturating_pow(proposal_count as usize) + .saturating_mul_int(Base::get()) + } + } + + /// Rounds a `Deposit` result with `Precision`. + /// Particularly useful for types like [`Geometric`] that might produce deposits with high + /// precision. + pub struct Round(PhantomData<(Precision, Deposit)>); + impl Convert for Round + where + Precision: Get, + Deposit: Convert, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(proposal_count: u32) -> Balance { + let deposit = Deposit::convert(proposal_count); + if !deposit.is_zero() { + let factor: Balance = + Balance::from(10u32).saturating_pow(Precision::get() as usize); + if factor > deposit { + deposit + } else { + (deposit / factor) * factor + } + } else { + deposit + } + } + } + + /// Defines `Period` for supplied `Step` implementing [`Convert`] trait. + pub struct Stepped(PhantomData<(Period, Step)>); + impl Convert for Stepped + where + Period: Get, + Step: Convert, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(proposal_count: u32) -> Balance { + let step_num = proposal_count / Period::get(); + Step::convert(step_num) + } + } + + /// Defines `Delay` for supplied `Step` implementing [`Convert`] trait. + pub struct Delayed(PhantomData<(Delay, Deposit)>); + impl Convert for Delayed + where + Delay: Get, + Deposit: Convert, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(proposal_count: u32) -> Balance { + let delay = Delay::get(); + if delay > proposal_count { + return Balance::zero(); + } + let pos = proposal_count.saturating_sub(delay); + Deposit::convert(pos) + } + } + + /// Defines `Ceil` for supplied `Step` implementing [`Convert`] trait. + pub struct WithCeil(PhantomData<(Ceil, Deposit)>); + impl Convert for WithCeil + where + Ceil: Get, + Deposit: Convert, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(proposal_count: u32) -> Balance { + Deposit::convert(proposal_count).min(Ceil::get()) + } + } +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -229,6 +366,27 @@ pub mod pallet { /// The maximum weight of a dispatch call that can be proposed and executed. #[pallet::constant] type MaxProposalWeight: Get; + + /// Origin from which a proposal in any status may be disapproved without associated cost + /// for a proposer. + type DisapproveOrigin: EnsureOrigin<::RuntimeOrigin>; + + /// Origin from which any malicious proposal may be killed with associated cost for a + /// proposer. + /// + /// The associated cost is set by [`Config::Consideration`] and can be none. + type KillOrigin: EnsureOrigin<::RuntimeOrigin>; + + /// Mechanism to assess the necessity of some cost for publishing and storing a proposal. + /// + /// The footprint is defined as `proposal_count`, which reflects the total number of active + /// proposals in the system, excluding the one currently being proposed. The cost may vary + /// based on this count. + /// + /// Note: If the resulting deposits are excessively high and cause benchmark failures, + /// consider using a constant cost (e.g., [`crate::deposit::Constant`]) equal to the minimum + /// balance under the `runtime-benchmarks` feature. + type Consideration: MaybeConsideration; } #[pallet::genesis_config] @@ -272,6 +430,14 @@ pub mod pallet { pub type ProposalOf, I: 'static = ()> = StorageMap<_, Identity, T::Hash, >::Proposal, OptionQuery>; + /// Consideration cost created for publishing and storing a proposal. + /// + /// Determined by [Config::Consideration] and may be not present for certain proposals (e.g. if + /// the proposal count at the time of creation was below threshold N). + #[pallet::storage] + pub type CostOf, I: 'static = ()> = + StorageMap<_, Identity, T::Hash, (T::AccountId, T::Consideration), OptionQuery>; + /// Votes on a given proposal, if it is ongoing. #[pallet::storage] pub type Voting, I: 'static = ()> = @@ -320,6 +486,12 @@ pub mod pallet { MemberExecuted { proposal_hash: T::Hash, result: DispatchResult }, /// A proposal was closed because its threshold was reached or after its duration was up. Closed { proposal_hash: T::Hash, yes: MemberCount, no: MemberCount }, + /// A proposal was killed. + Killed { proposal_hash: T::Hash }, + /// Some cost for storing a proposal was burned. + ProposalCostBurned { proposal_hash: T::Hash, who: T::AccountId }, + /// Some cost for storing a proposal was released. + ProposalCostReleased { proposal_hash: T::Hash, who: T::AccountId }, } #[pallet::error] @@ -346,6 +518,8 @@ pub mod pallet { WrongProposalLength, /// Prime account is not a member PrimeAccountNotMember, + /// Proposal is still active. + ProposalActive, } #[pallet::hooks] @@ -356,6 +530,14 @@ pub mod pallet { } } + /// A reason for the pallet placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// Funds are held for submitting and storing a proposal. + #[codec(index = 0)] + ProposalSubmission, + } + // Note that councillor operations are assigned to the operational class. #[pallet::call] impl, I: 'static> Pallet { @@ -593,7 +775,7 @@ pub mod pallet { origin: OriginFor, proposal_hash: T::Hash, ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; + T::DisapproveOrigin::ensure_origin(origin)?; let proposal_count = Self::do_disapprove_proposal(proposal_hash); Ok(Some(T::WeightInfo::disapprove_proposal(proposal_count)).into()) } @@ -648,6 +830,63 @@ pub mod pallet { Self::do_close(proposal_hash, index, proposal_weight_bound, length_bound) } + + /// Disapprove the proposal and burn the cost held for storing this proposal. + /// + /// Parameters: + /// - `origin`: must be the `KillOrigin`. + /// - `proposal_hash`: The hash of the proposal that should be killed. + /// + /// Emits `Killed` and `ProposalCostBurned` if any cost was held for a given proposal. + #[pallet::call_index(7)] + #[pallet::weight(T::WeightInfo::kill(1, T::MaxProposals::get()))] + pub fn kill(origin: OriginFor, proposal_hash: T::Hash) -> DispatchResultWithPostInfo { + T::KillOrigin::ensure_origin(origin)?; + ensure!( + ProposalOf::::get(&proposal_hash).is_some(), + Error::::ProposalMissing + ); + let burned = if let Some((who, cost)) = >::take(proposal_hash) { + cost.burn(&who); + Self::deposit_event(Event::ProposalCostBurned { proposal_hash, who }); + true + } else { + false + }; + let proposal_count = Self::remove_proposal(proposal_hash); + + Self::deposit_event(Event::Killed { proposal_hash }); + + Ok(Some(T::WeightInfo::kill(burned as u32, proposal_count)).into()) + } + + /// Release the cost held for storing a proposal once the given proposal is completed. + /// + /// If there is no associated cost for the given proposal, this call will have no effect. + /// + /// Parameters: + /// - `origin`: must be `Signed` or `Root`. + /// - `proposal_hash`: The hash of the proposal. + /// + /// Emits `ProposalCostReleased` if any cost held for a given proposal. + #[pallet::call_index(8)] + #[pallet::weight(T::WeightInfo::release_proposal_cost())] + pub fn release_proposal_cost( + origin: OriginFor, + proposal_hash: T::Hash, + ) -> DispatchResult { + let _ = ensure_signed_or_root(origin)?; + ensure!( + ProposalOf::::get(&proposal_hash).is_none(), + Error::::ProposalActive + ); + if let Some((who, cost)) = >::take(proposal_hash) { + let _ = cost.drop(&who)?; + Self::deposit_event(Event::ProposalCostReleased { proposal_hash, who }); + } + + Ok(()) + } } } @@ -718,7 +957,13 @@ impl, I: 'static> Pallet { Ok(proposals.len()) })?; + let cost = T::Consideration::new(&who, active_proposals as u32 - 1)?; + if !cost.is_none() { + >::insert(proposal_hash, (who.clone(), cost)); + } + let index = ProposalCount::::get(); + >::mutate(|i| *i += 1); >::insert(proposal_hash, proposal); let votes = { diff --git a/substrate/frame/collective/src/tests.rs b/substrate/frame/collective/src/tests.rs index 5240dc215ff7..70ce221f10d0 100644 --- a/substrate/frame/collective/src/tests.rs +++ b/substrate/frame/collective/src/tests.rs @@ -21,12 +21,19 @@ use frame_support::{ assert_noop, assert_ok, derive_impl, dispatch::Pays, parameter_types, - traits::{ConstU32, ConstU64, StorageVersion}, + traits::{ + fungible::{HoldConsideration, Inspect, Mutate}, + ConstU32, ConstU64, StorageVersion, + }, Hashable, }; use frame_system::{EnsureRoot, EventRecord, Phase}; -use sp_core::H256; -use sp_runtime::{testing::Header, traits::BlakeTwo256, BuildStorage}; +use sp_core::{ConstU128, H256}; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, Convert, Zero}, + BuildStorage, FixedU128, +}; pub type Block = sp_runtime::generic::Block; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; @@ -35,6 +42,7 @@ frame_support::construct_runtime!( pub enum Test { System: frame_system, + Balances: pallet_balances, Collective: pallet_collective::, CollectiveMajority: pallet_collective::, DefaultCollective: pallet_collective, @@ -90,7 +98,26 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; + type AccountData = pallet_balances::AccountData; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +impl pallet_balances::Config for Test { + type ReserveIdentifier = [u8; 8]; + type AccountStore = System; + type RuntimeHoldReason = RuntimeHoldReason; } + +parameter_types! { + pub ProposalDepositBase: u64 = Balances::minimum_balance() + Balances::minimum_balance(); + pub const ProposalDepositDelay: u32 = 2; + pub const ProposalHoldReason: RuntimeHoldReason = + RuntimeHoldReason::Collective(pallet_collective::HoldReason::ProposalSubmission); +} + +type CollectiveDeposit = + deposit::Delayed>; + impl Config for Test { type RuntimeOrigin = RuntimeOrigin; type Proposal = RuntimeCall; @@ -102,7 +129,14 @@ impl Config for Test { type WeightInfo = (); type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = + HoldConsideration; } + +type CollectiveMajorityDeposit = deposit::Linear, ProposalDepositBase>; + impl Config for Test { type RuntimeOrigin = RuntimeOrigin; type Proposal = RuntimeCall; @@ -114,11 +148,22 @@ impl Config for Test { type WeightInfo = (); type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxProposalWeight; + // type ProposalDeposit = CollectiveMajorityDeposit; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = + HoldConsideration; } impl mock_democracy::Config for Test { type RuntimeEvent = RuntimeEvent; type ExternalMajorityOrigin = EnsureProportionAtLeast; } +parameter_types! { + pub const Ratio2: FixedU128 = FixedU128::from_u32(2); + pub ProposalDepositCeil: u64 = Balances::minimum_balance() * 100; +} +type DefaultCollectiveDeposit = + deposit::WithCeil>; impl Config for Test { type RuntimeOrigin = RuntimeOrigin; type Proposal = RuntimeCall; @@ -130,6 +175,12 @@ impl Config for Test { type WeightInfo = (); type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxProposalWeight; + // type ProposalDeposit = + // deposit::WithCeil>; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = + HoldConsideration; } pub struct ExtBuilder { @@ -151,6 +202,8 @@ impl ExtBuilder { pub fn build(self) -> sp_io::TestExternalities { let mut ext: sp_io::TestExternalities = RuntimeGenesisConfig { system: frame_system::GenesisConfig::default(), + // balances: pallet_balances::GenesisConfig::default(), + balances: pallet_balances::GenesisConfig { balances: vec![(1, 100), (2, 200)] }, collective: pallet_collective::GenesisConfig { members: self.collective_members, phantom: Default::default(), @@ -550,16 +603,28 @@ fn close_with_no_prime_but_majority_works() { MaxMembers::get() )); + let deposit = >::convert(0); + let ed = Balances::minimum_balance(); + let _ = Balances::mint_into(&5, ed + deposit); + System::reset_events(); + assert_ok!(CollectiveMajority::propose( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(5), 5, Box::new(proposal.clone()), proposal_len )); + assert_eq!(Balances::balance(&5), ed); + assert_ok!(CollectiveMajority::vote(RuntimeOrigin::signed(1), hash, 0, true)); assert_ok!(CollectiveMajority::vote(RuntimeOrigin::signed(2), hash, 0, true)); assert_ok!(CollectiveMajority::vote(RuntimeOrigin::signed(3), hash, 0, true)); + assert_noop!( + CollectiveMajority::release_proposal_cost(RuntimeOrigin::signed(1), hash), + Error::::ProposalActive + ); + System::set_block_number(4); assert_ok!(CollectiveMajority::close( RuntimeOrigin::signed(4), @@ -569,11 +634,14 @@ fn close_with_no_prime_but_majority_works() { proposal_len )); + assert_ok!(CollectiveMajority::release_proposal_cost(RuntimeOrigin::signed(5), hash)); + assert_eq!(Balances::balance(&5), ed + deposit); + assert_eq!( System::events(), vec![ record(RuntimeEvent::CollectiveMajority(CollectiveEvent::Proposed { - account: 1, + account: 5, proposal_index: 0, proposal_hash: hash, threshold: 5 @@ -610,6 +678,10 @@ fn close_with_no_prime_but_majority_works() { record(RuntimeEvent::CollectiveMajority(CollectiveEvent::Executed { proposal_hash: hash, result: Err(DispatchError::BadOrigin) + })), + record(RuntimeEvent::CollectiveMajority(CollectiveEvent::ProposalCostReleased { + proposal_hash: hash, + who: 5, })) ] ); @@ -757,9 +829,13 @@ fn propose_works() { #[test] fn limit_active_proposals() { ExtBuilder::default().build_and_execute(|| { + let ed = Balances::minimum_balance(); + assert_ok!(Balances::mint_into(&1, ed)); for i in 0..MaxProposals::get() { let proposal = make_proposal(i as u64); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); + let deposit = >::convert(0); + assert_ok!(Balances::mint_into(&1, deposit)); assert_ok!(Collective::propose( RuntimeOrigin::signed(1), 3, @@ -1499,3 +1575,183 @@ fn migration_v4() { crate::migrations::v4::post_migrate::(old_pallet); }); } + +#[test] +fn kill_proposal_with_deposit() { + ExtBuilder::default().build_and_execute(|| { + let ed = Balances::minimum_balance(); + assert_ok!(Balances::mint_into(&1, ed)); + let mut last_deposit = None; + let mut last_hash = None; + for i in 0..=ProposalDepositDelay::get() { + let proposal = make_proposal(i as u64); + let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); + last_hash = Some(BlakeTwo256::hash_of(&proposal)); + let deposit = >::convert(i); + assert_ok!(Balances::mint_into(&1, deposit)); + last_deposit = Some(deposit); + + assert_ok!(Collective::propose( + RuntimeOrigin::signed(1), + 3, + Box::new(proposal.clone()), + proposal_len + )); + assert_eq!( + CostOf::::get(last_hash.unwrap()).is_none(), + deposit.is_zero() + ); + } + let balance = Balances::total_balance(&1); + System::reset_events(); + + let unpublished = make_proposal((ProposalDepositDelay::get() + 1).into()); + assert_noop!( + Collective::kill(RuntimeOrigin::root(), BlakeTwo256::hash_of(&unpublished)), + Error::::ProposalMissing + ); + + assert_ok!(Collective::kill(RuntimeOrigin::root(), last_hash.unwrap())); + assert_eq!(Balances::total_balance(&1), balance - last_deposit.unwrap()); + + assert_eq!( + System::events(), + vec![ + record(RuntimeEvent::Collective(CollectiveEvent::ProposalCostBurned { + proposal_hash: last_hash.unwrap(), + who: 1, + })), + record(RuntimeEvent::Collective(CollectiveEvent::Killed { + proposal_hash: last_hash.unwrap(), + })), + ] + ); + }) +} + +#[docify::export] +#[test] +fn deposit_types_with_linear_work() { + type LinearWithSlop2 = crate::deposit::Linear, ConstU128<10>>; + assert_eq!(>::convert(0), 10); + assert_eq!(>::convert(1), 12); + assert_eq!(>::convert(2), 14); + assert_eq!(>::convert(3), 16); + assert_eq!(>::convert(4), 18); + + type SteppedWithStep3 = crate::deposit::Stepped, LinearWithSlop2>; + assert_eq!(>::convert(0), 10); + assert_eq!(>::convert(1), 10); + assert_eq!(>::convert(2), 10); + assert_eq!(>::convert(3), 12); + assert_eq!(>::convert(4), 12); + assert_eq!(>::convert(5), 12); + assert_eq!(>::convert(6), 14); + + type DelayedWithDelay4 = crate::deposit::Delayed, SteppedWithStep3>; + assert_eq!(>::convert(0), 0); + assert_eq!(>::convert(3), 0); + assert_eq!(>::convert(4), 10); + assert_eq!(>::convert(5), 10); + assert_eq!(>::convert(6), 10); + assert_eq!(>::convert(7), 12); + assert_eq!(>::convert(9), 12); + assert_eq!(>::convert(10), 14); + assert_eq!(>::convert(13), 16); + + type WithCeil13 = crate::deposit::WithCeil, DelayedWithDelay4>; + assert_eq!(>::convert(0), 0); + assert_eq!(>::convert(4), 10); + assert_eq!(>::convert(9), 12); + assert_eq!(>::convert(10), 13); + assert_eq!(>::convert(11), 13); + assert_eq!(>::convert(13), 13); +} + +#[docify::export] +#[test] +fn deposit_types_with_geometric_work() { + parameter_types! { + pub const Ratio2: FixedU128 = FixedU128::from_u32(2); + } + type WithRatio2Base10 = crate::deposit::Geometric>; + assert_eq!(>::convert(0), 10); + assert_eq!(>::convert(1), 20); + assert_eq!(>::convert(2), 40); + assert_eq!(>::convert(3), 80); + assert_eq!(>::convert(4), 160); + assert_eq!(>::convert(5), 320); + assert_eq!(>::convert(6), 640); + assert_eq!(>::convert(7), 1280); + assert_eq!(>::convert(8), 2560); + assert_eq!(>::convert(9), 5120); + + type SteppedWithStep3 = crate::deposit::Stepped, WithRatio2Base10>; + assert_eq!(>::convert(0), 10); + assert_eq!(>::convert(1), 10); + assert_eq!(>::convert(2), 10); + assert_eq!(>::convert(3), 20); + assert_eq!(>::convert(4), 20); + assert_eq!(>::convert(5), 20); + assert_eq!(>::convert(6), 40); + + type DelayedWithDelay4 = crate::deposit::Delayed, SteppedWithStep3>; + assert_eq!(>::convert(0), 0); + assert_eq!(>::convert(3), 0); + assert_eq!(>::convert(4), 10); + assert_eq!(>::convert(5), 10); + assert_eq!(>::convert(6), 10); + assert_eq!(>::convert(7), 20); + assert_eq!(>::convert(9), 20); + assert_eq!(>::convert(10), 40); + assert_eq!(>::convert(13), 80); + + type WithCeil21 = crate::deposit::WithCeil, DelayedWithDelay4>; + assert_eq!(>::convert(0), 0); + assert_eq!(>::convert(4), 10); + assert_eq!(>::convert(9), 20); + assert_eq!(>::convert(10), 21); + assert_eq!(>::convert(11), 21); + assert_eq!(>::convert(13), 21); +} + +#[docify::export] +#[test] +fn deposit_round_with_geometric_work() { + parameter_types! { + pub const Ratio1_5: FixedU128 = FixedU128::from_rational(3, 2); + } + type WithRatio1_5Base10 = crate::deposit::Geometric>; + assert_eq!(>::convert(0), 10000); + assert_eq!(>::convert(1), 15000); + assert_eq!(>::convert(2), 22500); + assert_eq!(>::convert(3), 33750); + assert_eq!(>::convert(4), 50625); + assert_eq!(>::convert(5), 75937); + + type RoundWithPrecision3 = crate::deposit::Round, WithRatio1_5Base10>; + assert_eq!(>::convert(0), 10000); + assert_eq!(>::convert(1), 15000); + assert_eq!(>::convert(2), 22000); + assert_eq!(>::convert(3), 33000); + assert_eq!(>::convert(4), 50000); + assert_eq!(>::convert(5), 75000); +} + +#[test] +fn constant_deposit_work() { + type Constant0 = crate::deposit::Constant>; + assert_eq!(>::convert(0), 0); + assert_eq!(>::convert(1), 0); + assert_eq!(>::convert(2), 0); + + type Constant1 = crate::deposit::Constant>; + assert_eq!(>::convert(0), 1); + assert_eq!(>::convert(1), 1); + assert_eq!(>::convert(2), 1); + + type Constant12 = crate::deposit::Constant>; + assert_eq!(>::convert(0), 12); + assert_eq!(>::convert(1), 12); + assert_eq!(>::convert(2), 12); +} diff --git a/substrate/frame/collective/src/weights.rs b/substrate/frame/collective/src/weights.rs index cadfbfcbfbfb..1a7485b4ab7b 100644 --- a/substrate/frame/collective/src/weights.rs +++ b/substrate/frame/collective/src/weights.rs @@ -18,27 +18,25 @@ //! Autogenerated weights for `pallet_collective` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-04-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-09-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-anb7yjbi-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/production/substrate-node +// target/production/substrate-node // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_collective -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --output=./substrate/frame/collective/src/weights.rs +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_collective +// --chain=dev // --header=./substrate/HEADER-APACHE2 +// --output=./substrate/frame/collective/src/weights.rs // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -61,6 +59,8 @@ pub trait WeightInfo { fn close_disapproved(m: u32, p: u32, ) -> Weight; fn close_approved(b: u32, m: u32, p: u32, ) -> Weight; fn disapprove_proposal(p: u32, ) -> Weight; + fn kill(d: u32, p: u32, ) -> Weight; + fn release_proposal_cost() -> Weight; } /// Weights for `pallet_collective` using the Substrate node and recommended hardware. @@ -80,13 +80,13 @@ impl WeightInfo for SubstrateWeight { fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + m * (3232 ±0) + p * (3190 ±0)` - // Estimated: `15894 + m * (1967 ±24) + p * (4332 ±24)` - // Minimum execution time: 15_780_000 picoseconds. - Weight::from_parts(16_193_000, 15894) - // Standard Error: 56_777 - .saturating_add(Weight::from_parts(4_269_920, 0).saturating_mul(m.into())) - // Standard Error: 56_777 - .saturating_add(Weight::from_parts(8_046_697, 0).saturating_mul(p.into())) + // Estimated: `15894 + m * (1967 ±23) + p * (4332 ±23)` + // Minimum execution time: 16_699_000 picoseconds. + Weight::from_parts(17_015_000, 15894) + // Standard Error: 63_844 + .saturating_add(Weight::from_parts(4_593_256, 0).saturating_mul(m.into())) + // Standard Error: 63_844 + .saturating_add(Weight::from_parts(8_935_845, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -106,12 +106,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `380 + m * (32 ±0)` // Estimated: `3997 + m * (32 ±0)` - // Minimum execution time: 19_760_000 picoseconds. - Weight::from_parts(18_849_113, 3997) - // Standard Error: 48 - .saturating_add(Weight::from_parts(2_076, 0).saturating_mul(b.into())) - // Standard Error: 501 - .saturating_add(Weight::from_parts(17_171, 0).saturating_mul(m.into())) + // Minimum execution time: 22_010_000 picoseconds. + Weight::from_parts(21_392_812, 3997) + // Standard Error: 34 + .saturating_add(Weight::from_parts(1_533, 0).saturating_mul(b.into())) + // Standard Error: 354 + .saturating_add(Weight::from_parts(15_866, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -129,12 +129,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `380 + m * (32 ±0)` // Estimated: `3997 + m * (32 ±0)` - // Minimum execution time: 21_535_000 picoseconds. - Weight::from_parts(20_564_012, 3997) - // Standard Error: 66 - .saturating_add(Weight::from_parts(2_252, 0).saturating_mul(b.into())) - // Standard Error: 689 - .saturating_add(Weight::from_parts(33_509, 0).saturating_mul(m.into())) + // Minimum execution time: 24_250_000 picoseconds. + Weight::from_parts(23_545_893, 3997) + // Standard Error: 40 + .saturating_add(Weight::from_parts(1_646, 0).saturating_mul(b.into())) + // Standard Error: 421 + .saturating_add(Weight::from_parts(26_248, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -144,27 +144,31 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Council::Proposals` (r:1 w:1) /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `MaxEncodedLen`) /// Storage: `Council::ProposalCount` (r:1 w:1) /// Proof: `Council::ProposalCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Council::Voting` (r:0 w:1) /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::CostOf` (r:0 w:1) + /// Proof: `Council::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[2, 100]`. /// The range of component `p` is `[1, 100]`. fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `525 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `3917 + m * (33 ±0) + p * (36 ±0)` - // Minimum execution time: 22_759_000 picoseconds. - Weight::from_parts(21_344_363, 3917) - // Standard Error: 121 - .saturating_add(Weight::from_parts(3_231, 0).saturating_mul(b.into())) - // Standard Error: 1_265 - .saturating_add(Weight::from_parts(31_197, 0).saturating_mul(m.into())) - // Standard Error: 1_249 - .saturating_add(Weight::from_parts(200_231, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + // Measured: `618 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `3991 + m * (33 ±0) + p * (36 ±0)` + // Minimum execution time: 46_538_000 picoseconds. + Weight::from_parts(63_900_448, 3991) + // Standard Error: 350 + .saturating_add(Weight::from_parts(2_827, 0).saturating_mul(b.into())) + // Standard Error: 3_658 + .saturating_add(Weight::from_parts(53_340, 0).saturating_mul(m.into())) + // Standard Error: 3_611 + .saturating_add(Weight::from_parts(213_719, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } @@ -175,12 +179,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `974 + m * (64 ±0)` - // Estimated: `4438 + m * (64 ±0)` - // Minimum execution time: 22_935_000 picoseconds. - Weight::from_parts(23_694_302, 4438) - // Standard Error: 959 - .saturating_add(Weight::from_parts(57_721, 0).saturating_mul(m.into())) + // Measured: `1011 + m * (64 ±0)` + // Estimated: `4475 + m * (64 ±0)` + // Minimum execution time: 28_413_000 picoseconds. + Weight::from_parts(28_981_832, 4475) + // Standard Error: 665 + .saturating_add(Weight::from_parts(43_005, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) @@ -197,14 +201,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `563 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `4008 + m * (65 ±0) + p * (36 ±0)` - // Minimum execution time: 25_507_000 picoseconds. - Weight::from_parts(24_849_399, 4008) - // Standard Error: 1_089 - .saturating_add(Weight::from_parts(40_933, 0).saturating_mul(m.into())) - // Standard Error: 1_062 - .saturating_add(Weight::from_parts(198_325, 0).saturating_mul(p.into())) + // Measured: `600 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `4042 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 27_725_000 picoseconds. + Weight::from_parts(30_174_093, 4042) + // Standard Error: 1_458 + .saturating_add(Weight::from_parts(41_100, 0).saturating_mul(m.into())) + // Standard Error: 1_422 + .saturating_add(Weight::from_parts(177_303, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) @@ -227,16 +231,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1010 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `4327 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` - // Minimum execution time: 43_927_000 picoseconds. - Weight::from_parts(43_733_720, 4327) - // Standard Error: 157 - .saturating_add(Weight::from_parts(3_737, 0).saturating_mul(b.into())) - // Standard Error: 1_664 - .saturating_add(Weight::from_parts(31_334, 0).saturating_mul(m.into())) - // Standard Error: 1_622 - .saturating_add(Weight::from_parts(222_964, 0).saturating_mul(p.into())) + // Measured: `1047 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `4360 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` + // Minimum execution time: 48_882_000 picoseconds. + Weight::from_parts(51_938_773, 4360) + // Standard Error: 208 + .saturating_add(Weight::from_parts(3_559, 0).saturating_mul(b.into())) + // Standard Error: 2_201 + .saturating_add(Weight::from_parts(38_678, 0).saturating_mul(m.into())) + // Standard Error: 2_145 + .saturating_add(Weight::from_parts(214_061, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) @@ -257,14 +261,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `583 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `4028 + m * (65 ±0) + p * (36 ±0)` - // Minimum execution time: 28_129_000 picoseconds. - Weight::from_parts(26_799_151, 4028) - // Standard Error: 1_325 - .saturating_add(Weight::from_parts(41_545, 0).saturating_mul(m.into())) - // Standard Error: 1_292 - .saturating_add(Weight::from_parts(224_499, 0).saturating_mul(p.into())) + // Measured: `620 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `4062 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 30_613_000 picoseconds. + Weight::from_parts(36_174_190, 4062) + // Standard Error: 1_899 + .saturating_add(Weight::from_parts(46_781, 0).saturating_mul(m.into())) + // Standard Error: 1_851 + .saturating_add(Weight::from_parts(185_875, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) @@ -289,16 +293,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1030 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `4347 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` - // Minimum execution time: 45_700_000 picoseconds. - Weight::from_parts(45_661_144, 4347) - // Standard Error: 149 - .saturating_add(Weight::from_parts(3_068, 0).saturating_mul(b.into())) - // Standard Error: 1_583 - .saturating_add(Weight::from_parts(36_426, 0).saturating_mul(m.into())) - // Standard Error: 1_543 - .saturating_add(Weight::from_parts(229_986, 0).saturating_mul(p.into())) + // Measured: `1067 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `4380 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` + // Minimum execution time: 51_253_000 picoseconds. + Weight::from_parts(56_399_941, 4380) + // Standard Error: 218 + .saturating_add(Weight::from_parts(2_920, 0).saturating_mul(b.into())) + // Standard Error: 2_310 + .saturating_add(Weight::from_parts(30_473, 0).saturating_mul(m.into())) + // Standard Error: 2_252 + .saturating_add(Weight::from_parts(208_468, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) @@ -316,14 +320,62 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `392 + p * (32 ±0)` // Estimated: `1877 + p * (32 ±0)` - // Minimum execution time: 13_163_000 picoseconds. - Weight::from_parts(14_467_031, 1877) - // Standard Error: 1_073 - .saturating_add(Weight::from_parts(190_879, 0).saturating_mul(p.into())) + // Minimum execution time: 14_646_000 picoseconds. + Weight::from_parts(17_305_497, 1877) + // Standard Error: 1_331 + .saturating_add(Weight::from_parts(156_038, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(p.into())) } + /// Storage: `Council::ProposalOf` (r:1 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::CostOf` (r:1 w:1) + /// Proof: `Council::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `MaxEncodedLen`) + /// Storage: `Council::Proposals` (r:1 w:1) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Voting` (r:0 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `d` is `[0, 1]`. + /// The range of component `p` is `[1, 100]`. + fn kill(d: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1863 + d * (212 ±0) + p * (41 ±0)` + // Estimated: `5172 + d * (1901 ±14) + p * (43 ±0)` + // Minimum execution time: 22_164_000 picoseconds. + Weight::from_parts(24_932_256, 5172) + // Standard Error: 404_014 + .saturating_add(Weight::from_parts(33_833_807, 0).saturating_mul(d.into())) + // Standard Error: 6_256 + .saturating_add(Weight::from_parts(281_910, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(d.into()))) + .saturating_add(Weight::from_parts(0, 1901).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(p.into())) + } + /// Storage: `Council::ProposalOf` (r:1 w:0) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::CostOf` (r:1 w:1) + /// Proof: `Council::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `MaxEncodedLen`) + fn release_proposal_cost() -> Weight { + // Proof Size summary in bytes: + // Measured: `1964` + // Estimated: `5429` + // Minimum execution time: 69_220_000 picoseconds. + Weight::from_parts(70_215_000, 5429) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } } // For backwards compatibility and tests. @@ -342,13 +394,13 @@ impl WeightInfo for () { fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + m * (3232 ±0) + p * (3190 ±0)` - // Estimated: `15894 + m * (1967 ±24) + p * (4332 ±24)` - // Minimum execution time: 15_780_000 picoseconds. - Weight::from_parts(16_193_000, 15894) - // Standard Error: 56_777 - .saturating_add(Weight::from_parts(4_269_920, 0).saturating_mul(m.into())) - // Standard Error: 56_777 - .saturating_add(Weight::from_parts(8_046_697, 0).saturating_mul(p.into())) + // Estimated: `15894 + m * (1967 ±23) + p * (4332 ±23)` + // Minimum execution time: 16_699_000 picoseconds. + Weight::from_parts(17_015_000, 15894) + // Standard Error: 63_844 + .saturating_add(Weight::from_parts(4_593_256, 0).saturating_mul(m.into())) + // Standard Error: 63_844 + .saturating_add(Weight::from_parts(8_935_845, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -368,12 +420,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `380 + m * (32 ±0)` // Estimated: `3997 + m * (32 ±0)` - // Minimum execution time: 19_760_000 picoseconds. - Weight::from_parts(18_849_113, 3997) - // Standard Error: 48 - .saturating_add(Weight::from_parts(2_076, 0).saturating_mul(b.into())) - // Standard Error: 501 - .saturating_add(Weight::from_parts(17_171, 0).saturating_mul(m.into())) + // Minimum execution time: 22_010_000 picoseconds. + Weight::from_parts(21_392_812, 3997) + // Standard Error: 34 + .saturating_add(Weight::from_parts(1_533, 0).saturating_mul(b.into())) + // Standard Error: 354 + .saturating_add(Weight::from_parts(15_866, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -391,12 +443,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `380 + m * (32 ±0)` // Estimated: `3997 + m * (32 ±0)` - // Minimum execution time: 21_535_000 picoseconds. - Weight::from_parts(20_564_012, 3997) - // Standard Error: 66 - .saturating_add(Weight::from_parts(2_252, 0).saturating_mul(b.into())) - // Standard Error: 689 - .saturating_add(Weight::from_parts(33_509, 0).saturating_mul(m.into())) + // Minimum execution time: 24_250_000 picoseconds. + Weight::from_parts(23_545_893, 3997) + // Standard Error: 40 + .saturating_add(Weight::from_parts(1_646, 0).saturating_mul(b.into())) + // Standard Error: 421 + .saturating_add(Weight::from_parts(26_248, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -406,27 +458,31 @@ impl WeightInfo for () { /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Council::Proposals` (r:1 w:1) /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `MaxEncodedLen`) /// Storage: `Council::ProposalCount` (r:1 w:1) /// Proof: `Council::ProposalCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Council::Voting` (r:0 w:1) /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::CostOf` (r:0 w:1) + /// Proof: `Council::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[2, 100]`. /// The range of component `p` is `[1, 100]`. fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `525 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `3917 + m * (33 ±0) + p * (36 ±0)` - // Minimum execution time: 22_759_000 picoseconds. - Weight::from_parts(21_344_363, 3917) - // Standard Error: 121 - .saturating_add(Weight::from_parts(3_231, 0).saturating_mul(b.into())) - // Standard Error: 1_265 - .saturating_add(Weight::from_parts(31_197, 0).saturating_mul(m.into())) - // Standard Error: 1_249 - .saturating_add(Weight::from_parts(200_231, 0).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + // Measured: `618 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `3991 + m * (33 ±0) + p * (36 ±0)` + // Minimum execution time: 46_538_000 picoseconds. + Weight::from_parts(63_900_448, 3991) + // Standard Error: 350 + .saturating_add(Weight::from_parts(2_827, 0).saturating_mul(b.into())) + // Standard Error: 3_658 + .saturating_add(Weight::from_parts(53_340, 0).saturating_mul(m.into())) + // Standard Error: 3_611 + .saturating_add(Weight::from_parts(213_719, 0).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } @@ -437,12 +493,12 @@ impl WeightInfo for () { /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `974 + m * (64 ±0)` - // Estimated: `4438 + m * (64 ±0)` - // Minimum execution time: 22_935_000 picoseconds. - Weight::from_parts(23_694_302, 4438) - // Standard Error: 959 - .saturating_add(Weight::from_parts(57_721, 0).saturating_mul(m.into())) + // Measured: `1011 + m * (64 ±0)` + // Estimated: `4475 + m * (64 ±0)` + // Minimum execution time: 28_413_000 picoseconds. + Weight::from_parts(28_981_832, 4475) + // Standard Error: 665 + .saturating_add(Weight::from_parts(43_005, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) @@ -459,14 +515,14 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `563 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `4008 + m * (65 ±0) + p * (36 ±0)` - // Minimum execution time: 25_507_000 picoseconds. - Weight::from_parts(24_849_399, 4008) - // Standard Error: 1_089 - .saturating_add(Weight::from_parts(40_933, 0).saturating_mul(m.into())) - // Standard Error: 1_062 - .saturating_add(Weight::from_parts(198_325, 0).saturating_mul(p.into())) + // Measured: `600 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `4042 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 27_725_000 picoseconds. + Weight::from_parts(30_174_093, 4042) + // Standard Error: 1_458 + .saturating_add(Weight::from_parts(41_100, 0).saturating_mul(m.into())) + // Standard Error: 1_422 + .saturating_add(Weight::from_parts(177_303, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) @@ -489,16 +545,16 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1010 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `4327 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` - // Minimum execution time: 43_927_000 picoseconds. - Weight::from_parts(43_733_720, 4327) - // Standard Error: 157 - .saturating_add(Weight::from_parts(3_737, 0).saturating_mul(b.into())) - // Standard Error: 1_664 - .saturating_add(Weight::from_parts(31_334, 0).saturating_mul(m.into())) - // Standard Error: 1_622 - .saturating_add(Weight::from_parts(222_964, 0).saturating_mul(p.into())) + // Measured: `1047 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `4360 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` + // Minimum execution time: 48_882_000 picoseconds. + Weight::from_parts(51_938_773, 4360) + // Standard Error: 208 + .saturating_add(Weight::from_parts(3_559, 0).saturating_mul(b.into())) + // Standard Error: 2_201 + .saturating_add(Weight::from_parts(38_678, 0).saturating_mul(m.into())) + // Standard Error: 2_145 + .saturating_add(Weight::from_parts(214_061, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) @@ -519,14 +575,14 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `583 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `4028 + m * (65 ±0) + p * (36 ±0)` - // Minimum execution time: 28_129_000 picoseconds. - Weight::from_parts(26_799_151, 4028) - // Standard Error: 1_325 - .saturating_add(Weight::from_parts(41_545, 0).saturating_mul(m.into())) - // Standard Error: 1_292 - .saturating_add(Weight::from_parts(224_499, 0).saturating_mul(p.into())) + // Measured: `620 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `4062 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 30_613_000 picoseconds. + Weight::from_parts(36_174_190, 4062) + // Standard Error: 1_899 + .saturating_add(Weight::from_parts(46_781, 0).saturating_mul(m.into())) + // Standard Error: 1_851 + .saturating_add(Weight::from_parts(185_875, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) @@ -551,16 +607,16 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1030 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `4347 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` - // Minimum execution time: 45_700_000 picoseconds. - Weight::from_parts(45_661_144, 4347) - // Standard Error: 149 - .saturating_add(Weight::from_parts(3_068, 0).saturating_mul(b.into())) - // Standard Error: 1_583 - .saturating_add(Weight::from_parts(36_426, 0).saturating_mul(m.into())) - // Standard Error: 1_543 - .saturating_add(Weight::from_parts(229_986, 0).saturating_mul(p.into())) + // Measured: `1067 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` + // Estimated: `4380 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` + // Minimum execution time: 51_253_000 picoseconds. + Weight::from_parts(56_399_941, 4380) + // Standard Error: 218 + .saturating_add(Weight::from_parts(2_920, 0).saturating_mul(b.into())) + // Standard Error: 2_310 + .saturating_add(Weight::from_parts(30_473, 0).saturating_mul(m.into())) + // Standard Error: 2_252 + .saturating_add(Weight::from_parts(208_468, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) @@ -578,12 +634,60 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `392 + p * (32 ±0)` // Estimated: `1877 + p * (32 ±0)` - // Minimum execution time: 13_163_000 picoseconds. - Weight::from_parts(14_467_031, 1877) - // Standard Error: 1_073 - .saturating_add(Weight::from_parts(190_879, 0).saturating_mul(p.into())) + // Minimum execution time: 14_646_000 picoseconds. + Weight::from_parts(17_305_497, 1877) + // Standard Error: 1_331 + .saturating_add(Weight::from_parts(156_038, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(p.into())) } + /// Storage: `Council::ProposalOf` (r:1 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::CostOf` (r:1 w:1) + /// Proof: `Council::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `MaxEncodedLen`) + /// Storage: `Council::Proposals` (r:1 w:1) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Voting` (r:0 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `d` is `[0, 1]`. + /// The range of component `p` is `[1, 100]`. + fn kill(d: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1863 + d * (212 ±0) + p * (41 ±0)` + // Estimated: `5172 + d * (1901 ±14) + p * (43 ±0)` + // Minimum execution time: 22_164_000 picoseconds. + Weight::from_parts(24_932_256, 5172) + // Standard Error: 404_014 + .saturating_add(Weight::from_parts(33_833_807, 0).saturating_mul(d.into())) + // Standard Error: 6_256 + .saturating_add(Weight::from_parts(281_910, 0).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(d.into()))) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(d.into()))) + .saturating_add(Weight::from_parts(0, 1901).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(p.into())) + } + /// Storage: `Council::ProposalOf` (r:1 w:0) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::CostOf` (r:1 w:1) + /// Proof: `Council::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `MaxEncodedLen`) + fn release_proposal_cost() -> Weight { + // Proof Size summary in bytes: + // Measured: `1964` + // Estimated: `5429` + // Minimum execution time: 69_220_000 picoseconds. + Weight::from_parts(70_215_000, 5429) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } } diff --git a/substrate/frame/utility/src/tests.rs b/substrate/frame/utility/src/tests.rs index 0a58a92b4c9e..9755efaea41a 100644 --- a/substrate/frame/utility/src/tests.rs +++ b/substrate/frame/utility/src/tests.rs @@ -29,6 +29,7 @@ use frame_support::{ traits::{ConstU64, Contains}, weights::Weight, }; +use frame_system::EnsureRoot; use pallet_collective::{EnsureProportionAtLeast, Instance1}; use sp_runtime::{ traits::{BadOrigin, BlakeTwo256, Dispatchable, Hash}, @@ -189,6 +190,9 @@ impl pallet_collective::Config for Test { type WeightInfo = (); type SetMembersOrigin = frame_system::EnsureRoot; type MaxProposalWeight = MaxProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = (); } impl example::Config for Test {} From de0b6f25725325db0e97379f2d49a41dc8a13c44 Mon Sep 17 00:00:00 2001 From: ordian Date: Wed, 4 Sep 2024 14:59:21 +0200 Subject: [PATCH 259/382] try making bench numbers make sense (#5526) Follow-up to #5270. The baseline numbers for Westend were too high to be representative of the reality as it seemed to do with how multi-variate linear regression is calculated. --------- Co-authored-by: command-bot <> --- .../parachains/src/inclusion/benchmarking.rs | 4 +- .../polkadot_runtime_parachains_inclusion.rs | 45 ++++++++-------- .../polkadot_runtime_parachains_inclusion.rs | 54 ++++++++++--------- prdoc/pr_5526.prdoc | 13 +++++ 4 files changed, 68 insertions(+), 48 deletions(-) create mode 100644 prdoc/pr_5526.prdoc diff --git a/polkadot/runtime/parachains/src/inclusion/benchmarking.rs b/polkadot/runtime/parachains/src/inclusion/benchmarking.rs index cb6329bf88ea..1dac3c92cf16 100644 --- a/polkadot/runtime/parachains/src/inclusion/benchmarking.rs +++ b/polkadot/runtime/parachains/src/inclusion/benchmarking.rs @@ -94,8 +94,8 @@ benchmarks! { } enact_candidate { - let u in 1 .. 32; - let h in 1 .. 32; + let u in 0 .. 2; + let h in 0 .. 2; let c in 0 .. 1; let para = 42_u32.into(); // not especially important. diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs index 4c6ce8835573..5824658383be 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `polkadot_runtime_parachains::inclusion` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -70,13 +70,13 @@ impl polkadot_runtime_parachains::inclusion::WeightInfo /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) - /// Storage: `MessageQueue::Pages` (r:1 w:32) + /// Storage: `MessageQueue::Pages` (r:1 w:10) /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) - /// Storage: `Hrmp::HrmpChannelDigests` (r:33 w:33) + /// Storage: `Hrmp::HrmpChannelDigests` (r:11 w:11) /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpChannels` (r:32 w:32) + /// Storage: `Hrmp::HrmpChannels` (r:10 w:10) /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpChannelContents` (r:32 w:32) + /// Storage: `Hrmp::HrmpChannelContents` (r:10 w:10) /// Proof: `Hrmp::HrmpChannelContents` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -96,28 +96,31 @@ impl polkadot_runtime_parachains::inclusion::WeightInfo /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// The range of component `u` is `[1, 32]`. - /// The range of component `h` is `[1, 32]`. + /// The range of component `u` is `[0, 10]`. + /// The range of component `h` is `[0, 10]`. /// The range of component `c` is `[0, 1]`. fn enact_candidate(u: u32, h: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `33353 + c * (16114 ±0) + h * (75 ±0)` - // Estimated: `36818 + c * (26467 ±0) + h * (2551 ±0)` - // Minimum execution time: 4_829_551_000 picoseconds. - Weight::from_parts(1_892_697_027, 0) - .saturating_add(Weight::from_parts(0, 36818)) - // Standard Error: 793_993 - .saturating_add(Weight::from_parts(126_698_671, 0).saturating_mul(u.into())) - // Standard Error: 793_993 - .saturating_add(Weight::from_parts(144_116_038, 0).saturating_mul(h.into())) - .saturating_add(T::DbWeight::get().reads(7)) + // Measured: `33352 + c * (16115 ±0) + h * (76 ±0)` + // Estimated: `36283 + c * (19327 ±403) + h * (3057 ±59) + u * (1314 ±59)` + // Minimum execution time: 1_334_017_000 picoseconds. + Weight::from_parts(5_805_317, 0) + .saturating_add(Weight::from_parts(0, 36283)) + // Standard Error: 282_194 + .saturating_add(Weight::from_parts(128_332_196, 0).saturating_mul(u.into())) + // Standard Error: 282_194 + .saturating_add(Weight::from_parts(146_910_684, 0).saturating_mul(h.into())) + // Standard Error: 1_905_330 + .saturating_add(Weight::from_parts(91_514_854, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(h.into()))) .saturating_add(T::DbWeight::get().reads((8_u64).saturating_mul(c.into()))) - .saturating_add(T::DbWeight::get().writes(10)) + .saturating_add(T::DbWeight::get().writes(8)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(h.into()))) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 26467).saturating_mul(c.into())) - .saturating_add(Weight::from_parts(0, 2551).saturating_mul(h.into())) + .saturating_add(Weight::from_parts(0, 19327).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 3057).saturating_mul(h.into())) + .saturating_add(Weight::from_parts(0, 1314).saturating_mul(u.into())) } } diff --git a/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs index 36a4c5c24c9a..28d8aa8ea323 100644 --- a/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs +++ b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `polkadot_runtime_parachains::inclusion` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -80,13 +80,13 @@ impl polkadot_runtime_parachains::inclusion::WeightInfo /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) - /// Storage: `MessageQueue::Pages` (r:1 w:32) + /// Storage: `MessageQueue::Pages` (r:1 w:2) /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(131122), added: 133597, mode: `MaxEncodedLen`) - /// Storage: `Hrmp::HrmpChannelDigests` (r:33 w:33) + /// Storage: `Hrmp::HrmpChannelDigests` (r:3 w:3) /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpChannels` (r:32 w:32) + /// Storage: `Hrmp::HrmpChannels` (r:2 w:2) /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpChannelContents` (r:32 w:32) + /// Storage: `Hrmp::HrmpChannelContents` (r:2 w:2) /// Proof: `Hrmp::HrmpChannelContents` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -104,28 +104,32 @@ impl polkadot_runtime_parachains::inclusion::WeightInfo /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// The range of component `u` is `[1, 32]`. - /// The range of component `h` is `[1, 32]`. + /// The range of component `u` is `[0, 2]`. + /// The range of component `h` is `[0, 2]`. /// The range of component `c` is `[0, 1]`. fn enact_candidate(u: u32, h: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `132737 + c * (15992 ±0) + h * (75 ±0)` - // Estimated: `136202 + c * (76098 ±0) + h * (2551 ±0)` - // Minimum execution time: 18_868_930_000 picoseconds. - Weight::from_parts(6_899_601_016, 0) - .saturating_add(Weight::from_parts(0, 136202)) - // Standard Error: 1_952_665 - .saturating_add(Weight::from_parts(467_810_135, 0).saturating_mul(u.into())) - // Standard Error: 1_952_665 - .saturating_add(Weight::from_parts(551_226_340, 0).saturating_mul(h.into())) - .saturating_add(T::DbWeight::get().reads(11)) + // Measured: `1447 + c * (15992 ±0) + h * (92 ±0) + u * (131259 ±0)` + // Estimated: `134587 + c * (25419 ±939) + h * (29985 ±511) + u * (82828 ±511)` + // Minimum execution time: 1_065_780_000 picoseconds. + Weight::from_parts(192_328_221, 0) + .saturating_add(Weight::from_parts(0, 134587)) + // Standard Error: 1_263_527 + .saturating_add(Weight::from_parts(454_948_671, 0).saturating_mul(u.into())) + // Standard Error: 1_263_527 + .saturating_add(Weight::from_parts(527_131_211, 0).saturating_mul(h.into())) + // Standard Error: 2_093_815 + .saturating_add(Weight::from_parts(11_112_489, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(h.into()))) - .saturating_add(T::DbWeight::get().reads((8_u64).saturating_mul(c.into()))) - .saturating_add(T::DbWeight::get().writes(11)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(h.into()))) - .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 76098).saturating_mul(c.into())) - .saturating_add(Weight::from_parts(0, 2551).saturating_mul(h.into())) + .saturating_add(T::DbWeight::get().reads((9_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(u.into()))) + .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(h.into()))) + .saturating_add(T::DbWeight::get().writes((8_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 25419).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 29985).saturating_mul(h.into())) + .saturating_add(Weight::from_parts(0, 82828).saturating_mul(u.into())) } } diff --git a/prdoc/pr_5526.prdoc b/prdoc/pr_5526.prdoc new file mode 100644 index 000000000000..0c0a4b055f6a --- /dev/null +++ b/prdoc/pr_5526.prdoc @@ -0,0 +1,13 @@ +title: "Fix enact_candidate weight generation" +doc: + - audience: Runtime Dev + description: | + This PR works around an issue in multivariate linear regression of weight generation. + +crates: + - name: polkadot-runtime-parachains + bump: patch + - name: rococo-runtime + bump: patch + - name: westend-runtime + bump: patch From 778a9e41f54e8e3f67cc57914100efa4a421ed0a Mon Sep 17 00:00:00 2001 From: Evgeny Snitko Date: Wed, 4 Sep 2024 18:22:57 +0400 Subject: [PATCH 260/382] build and publish images migration (#5558) migrate jobs to gha - build-linux-stable-cumulus - build-test-parachain - build-linux-stable - build-test-collators - build-malus - build-linux-substrate - prepare-bridges-zombienet-artifacts (might require help from zombienet team) - build-push-image-polkadot-parachain-debug - build-push-image-test-parachain - build-push-image-polkadot-debug - build-push-image-colander - build-push-image-malus - build-push-image-substrate-pr - build-push-image-bridges-zombienet-tests see [ci_cd#1013](https://github.com/paritytech/ci_cd/issues/1013) --- .github/actions/build-push-image/action.yml | 47 ++ .github/workflows/build-publish-images.yml | 525 ++++++++++++++++++++ 2 files changed, 572 insertions(+) create mode 100644 .github/actions/build-push-image/action.yml create mode 100644 .github/workflows/build-publish-images.yml diff --git a/.github/actions/build-push-image/action.yml b/.github/actions/build-push-image/action.yml new file mode 100644 index 000000000000..fead9cfe3369 --- /dev/null +++ b/.github/actions/build-push-image/action.yml @@ -0,0 +1,47 @@ +name: 'build and push image' +inputs: + dockerfile: + description: "dockerfile to build" + required: true + image-name: + description: "" + required: true +outputs: + branch: + description: 'Branch name for the PR' + value: ${{ steps.branch.outputs.branch }} + + +runs: + using: "composite" + steps: + + # gcloud + # https://github.com/paritytech/ci_cd/wiki/GitHub:-Push-Docker-image-to-GCP-Registry + - name: "Set up Cloud SDK" + uses: "google-github-actions/setup-gcloud@v2" + - name: "gcloud info" + shell: bash + run: "gcloud info" + - name: "Auth in gcloud registry" + shell: bash + run: "gcloud auth configure-docker europe-docker.pkg.dev --quiet" + + - name: build + shell: bash + env: + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.105" + run: | + export BRANCH_NAME=${{ github.head_ref || github.ref_name }} + export DOCKER_IMAGES_VERSION=${BRANCH_NAME/\//-} + if [[ ${{ github.event_name }} == "merge_group" ]]; then export DOCKER_IMAGES_VERSION="${GITHUB_SHA::8}"; fi + docker build \ + --build-arg VCS_REF="${GITHUB_SHA}" \ + --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \ + --build-arg IMAGE_NAME="${{ inputs.image-name }}" \ + --build-arg ZOMBIENET_IMAGE="${ZOMBIENET_IMAGE}" \ + -t "${{ inputs.image-name }}:$DOCKER_IMAGES_VERSION" \ + -f ${{ inputs.dockerfile }} \ + . + docker push "${{ inputs.image-name }}:$DOCKER_IMAGES_VERSION" + diff --git a/.github/workflows/build-publish-images.yml b/.github/workflows/build-publish-images.yml new file mode 100644 index 000000000000..735b727e58b6 --- /dev/null +++ b/.github/workflows/build-publish-images.yml @@ -0,0 +1,525 @@ +# GHA for build-* +name: Build and push images + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened, ready_for_review, labeled] + merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true +env: + COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + +jobs: + + # + # + # + set-image: + ## TODO: remove when ready + if: contains(github.event.label.name, 'GHA-migration') || contains(github.event.pull_request.labels.*.name, 'GHA-migration') + # GitHub Actions allows using 'env' in a container context. + # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 + # This workaround sets the container image for each job using 'set-image' job output. + runs-on: ubuntu-latest + env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + outputs: + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} + REF_NAME: ${{ steps.set_vars.outputs.REF_NAME }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT + - id: log + run: | + echo ${BRANCH_NAME} + echo ${COMMIT_SHA} + - id: set_vars + run: | + echo "REF_NAME=${BRANCH_NAME//\//-}" >> $GITHUB_OUTPUT + # By default we use spot machines that can be terminated at any time. + # Merge queues use persistent runners to avoid kicking off from queue when the runner is terminated. + - id: set_runner + run: | + # Run merge queues on persistent runners + if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT + else + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + fi + +### Build ######################## + + # + # + # + build-linux-stable: + needs: [set-image] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + RUST_TOOLCHAIN: stable + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: build + run: | + forklift cargo build --locked --profile testnet --features pyroscope,fast-runtime --bin polkadot --bin polkadot-prepare-worker --bin polkadot-execute-worker + ROCOCO_EPOCH_DURATION=10 ./polkadot/scripts/build-only-wasm.sh rococo-runtime $(pwd)/runtimes/rococo-runtime-10/ + ROCOCO_EPOCH_DURATION=100 ./polkadot/scripts/build-only-wasm.sh rococo-runtime $(pwd)/runtimes/rococo-runtime-100/ + ROCOCO_EPOCH_DURATION=600 ./polkadot/scripts/build-only-wasm.sh rococo-runtime $(pwd)/runtimes/rococo-runtime-600/ + pwd + ls -alR runtimes + - name: pack artifacts + run: | + mkdir -p ./artifacts + VERSION="${{ needs.set-image.outputs.REF_NAME }}" # will be tag or branch name + mv ./target/testnet/polkadot ./artifacts/. + mv ./target/testnet/polkadot-prepare-worker ./artifacts/. + mv ./target/testnet/polkadot-execute-worker ./artifacts/. + mv ./runtimes/ ./artifacts/. + cd artifacts/ + sha256sum polkadot | tee polkadot.sha256 + shasum -c polkadot.sha256 + cd ../ + EXTRATAG="${{ needs.set-image.outputs.REF_NAME }}-${COMMIT_SHA}" + echo "Polkadot version = ${VERSION} (EXTRATAG = ${EXTRATAG})" + echo -n ${VERSION} > ./artifacts/VERSION + echo -n ${EXTRATAG} > ./artifacts/EXTRATAG + echo -n ${GITHUB_RUN_ID} > ./artifacts/BUILD_LINUX_JOB_ID + RELEASE_VERSION=$(./artifacts/polkadot -V | awk '{print $2}'| awk -F "-" '{print $1}') + echo -n "v${RELEASE_VERSION}" > ./artifacts/BUILD_RELEASE_VERSION + cp -r docker/* ./artifacts + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + path: artifacts.tar + retention-days: 1 + + # + # + # + build-linux-stable-cumulus: + needs: [set-image] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: build + run: | + echo "___Building a binary, please refrain from using it in production since it goes with the debug assertions.___" + forklift cargo build --release --locked -p polkadot-parachain-bin --bin polkadot-parachain + echo "___Packing the artifacts___" + mkdir -p ./artifacts + mv ./target/release/polkadot-parachain ./artifacts/. + echo "___The VERSION is either a tag name or the curent branch if triggered not by a tag___" + echo ${{ needs.set-image.outputs.REF_NAME }} | tee ./artifacts/VERSION + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + path: artifacts.tar + retention-days: 1 + + # + # + # + build-test-parachain: + needs: [set-image] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: build + run: | + echo "___Building a binary, please refrain from using it in production since it goes with the debug assertions.___" + forklift cargo build --release --locked -p cumulus-test-service --bin test-parachain + - name: pack artifacts + run: | + echo "___Packing the artifacts___" + mkdir -p ./artifacts + mv ./target/release/test-parachain ./artifacts/. + mkdir -p ./artifacts/zombienet + mv ./target/release/wbuild/cumulus-test-runtime/wasm_binary_spec_version_incremented.rs.compact.compressed.wasm ./artifacts/zombienet/. + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + path: artifacts.tar + retention-days: 1 + + # + # + # + build-test-collators: + needs: [set-image] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: build + run: | + forklift cargo build --locked --profile testnet -p test-parachain-adder-collator + forklift cargo build --locked --profile testnet -p test-parachain-undying-collator + - name: pack artifacts + run: | + mkdir -p ./artifacts + mv ./target/testnet/adder-collator ./artifacts/. + mv ./target/testnet/undying-collator ./artifacts/. + echo -n "${{ needs.set-image.outputs.REF_NAME }}" > ./artifacts/VERSION + echo -n "${{ needs.set-image.outputs.REF_NAME }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG + echo "adder-collator version = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" + echo "undying-collator version = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" + cp -r ./docker/* ./artifacts + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + path: artifacts.tar + retention-days: 1 + + # + # + # + build-malus: + needs: [set-image] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: build + run: | + forklift cargo build --locked --profile testnet -p polkadot-test-malus --bin malus --bin polkadot-prepare-worker --bin polkadot-execute-worker + - name: pack artifacts + run: | + mkdir -p ./artifacts + mv ./target/testnet/malus ./artifacts/. + mv ./target/testnet/polkadot-execute-worker ./artifacts/. + mv ./target/testnet/polkadot-prepare-worker ./artifacts/. + echo -n "${{ needs.set-image.outputs.REF_NAME }}" > ./artifacts/VERSION + echo -n "${{ needs.set-image.outputs.REF_NAME }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG + echo "polkadot-test-malus = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" + cp -r ./docker/* ./artifacts + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + path: artifacts.tar + retention-days: 1 + + # + # + # + build-linux-substrate: + needs: [set-image] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # tldr: we need to checkout the branch HEAD explicitly because of our dynamic versioning approach while building the substrate binary + # see https://github.com/paritytech/ci_cd/issues/682#issuecomment-1340953589 + ref: ${{ github.head_ref || github.ref_name }} + - name: build + run: | + mkdir -p ./artifacts/substrate/ + WASM_BUILD_NO_COLOR=1 forklift cargo build --locked --release -p staging-node-cli + ls -la target/release/ + - name: pack artifacts + run: | + mv target/release/substrate-node ./artifacts/substrate/substrate + echo -n "Substrate version = " + if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + echo "${{ github.ref_name }}" | tee ./artifacts/substrate/VERSION; + else + ./artifacts/substrate/substrate --version | + cut -d ' ' -f 2 | tee ./artifacts/substrate/VERSION; + fi + sha256sum ./artifacts/substrate/substrate | tee ./artifacts/substrate/substrate.sha256 + cp -r ./docker/dockerfiles/substrate_injected.Dockerfile ./artifacts/substrate/ + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + path: artifacts.tar + retention-days: 1 + + # + # + # + prepare-bridges-zombienet-artifacts: + needs: [set-image] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: build + run: | + forklift cargo build --locked --profile testnet -p polkadot-test-malus --bin malus --bin polkadot-prepare-worker --bin polkadot-execute-worker + - name: pack artifacts + run: | + mkdir -p ./artifacts/bridges-polkadot-sdk/bridges + cp -r bridges/testing ./artifacts/bridges-polkadot-sdk/bridges/testing + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} + path: artifacts.tar + retention-days: 1 + +### Publish ######################## + + # + # + # + build-push-image-test-parachain: + needs: [set-image, build-test-parachain] + runs-on: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-test-parachain-${{ needs.set-image.outputs.REF_NAME }} + + - name: tar + run: tar -xvf artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/test-parachain" + dockerfile: "docker/dockerfiles/test-parachain_injected.Dockerfile" + + # + # + # + build-push-image-polkadot-debug: + needs: [set-image, build-linux-stable] + runs-on: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-stable-${{ needs.set-image.outputs.REF_NAME }} + + - name: tar + run: tar -xvf artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/polkadot-debug" + dockerfile: "docker/dockerfiles/polkadot/polkadot_injected_debug.Dockerfile" + + + # + # + # + build-push-image-colander: + needs: [set-image, build-test-collators] + runs-on: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-test-collators-${{ needs.set-image.outputs.REF_NAME }} + + - name: tar + run: tar -xvf artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/colander" + dockerfile: "docker/dockerfiles/collator_injected.Dockerfile" + + + # + # + # + build-push-image-malus: + needs: [set-image, build-malus] + runs-on: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-malus-${{ needs.set-image.outputs.REF_NAME }} + + - name: tar + run: tar -xvf artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/malus" + dockerfile: "docker/dockerfiles/malus_injected.Dockerfile" + + + # + # + # + build-push-image-substrate-pr: + needs: [set-image, build-linux-substrate] + runs-on: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-substrate-${{ needs.set-image.outputs.REF_NAME }} + + - name: tar + run: tar -xvf artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/substrate" + dockerfile: "docker/dockerfiles/substrate_injected.Dockerfile" + + + # + # + # + # unlike other images, bridges+zombienet image is based on Zombienet image that pulls required binaries + # from other fresh images (polkadot and cumulus) + build-push-image-bridges-zombienet-tests: + needs: [set-image, build-linux-stable, build-linux-stable-cumulus, prepare-bridges-zombienet-artifacts] + runs-on: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-stable-${{ needs.set-image.outputs.REF_NAME }} + - name: tar + run: | + tar -xvf artifacts.tar + rm artifacts.tar + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-stable-cumulus-${{ needs.set-image.outputs.REF_NAME }} + - name: tar + run: | + tar -xvf artifacts.tar + rm artifacts.tar + + - uses: actions/download-artifact@v4.1.8 + with: + name: prepare-bridges-zombienet-artifacts-${{ needs.set-image.outputs.REF_NAME }} + - name: tar + run: | + tar -xvf artifacts.tar + rm artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/bridges-zombienet-tests" + dockerfile: "docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile" + + + # + # + # + build-push-image-polkadot-parachain-debug: + needs: [set-image, build-linux-stable-cumulus] + runs-on: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-stable-cumulus-${{ needs.set-image.outputs.REF_NAME }} + + - name: tar + run: tar -xvf artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/polkadot-parachain-debug" + dockerfile: "docker/dockerfiles/polkadot-parachain/polkadot-parachain-debug_unsigned_injected.Dockerfile" \ No newline at end of file From 1cff666b5206c28e3116eea7edb36b65135775a1 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Wed, 4 Sep 2024 11:26:26 -0400 Subject: [PATCH 261/382] Create a Basic Proving Trie for the Runtime (#3881) This PR will introduce a `BasicProvingTrie` type, which makes it easy to construct and prove data in a base-16 merkle trie within the runtime. Data into the merkle trie only require that they implement `Encode` / `Decode`. A FRAME compatible `TrieError` was created and added to `DispatchError`. Expected usage is to construct the merkle trie with all data offline, and then place only the merkle root of that trie on-chain. Also offchain, a user is given a compact merkle proof of some data they want to prove exists on the blockchain. Then in the runtime, you can call `verify_single_value_proof` or `verify_proof` with the root, proof, and the keys and values you want to verify exists in the merkle trie. Closes https://github.com/paritytech/polkadot-sdk/issues/3880 Contributes to #5400 --------- Co-authored-by: Ankan <10196091+Ank4n@users.noreply.github.com> Co-authored-by: Oliver Tale-Yazdi --- Cargo.lock | 1 + prdoc/pr_3881.prdoc | 15 + substrate/primitives/runtime/Cargo.toml | 2 + substrate/primitives/runtime/src/lib.rs | 16 + .../primitives/runtime/src/proving_trie.rs | 388 ++++++++++++++++++ 5 files changed, 422 insertions(+) create mode 100644 prdoc/pr_3881.prdoc create mode 100644 substrate/primitives/runtime/src/proving_trie.rs diff --git a/Cargo.lock b/Cargo.lock index b3eefe1826c9..508fc2e89221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20741,6 +20741,7 @@ dependencies = [ "sp-state-machine", "sp-std 14.0.0", "sp-tracing 16.0.0", + "sp-trie", "sp-weights", "substrate-test-runtime-client", "tracing", diff --git a/prdoc/pr_3881.prdoc b/prdoc/pr_3881.prdoc new file mode 100644 index 000000000000..4cf6425e73a5 --- /dev/null +++ b/prdoc/pr_3881.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Introduce a Generic Proving Trie + +doc: + - audience: Runtime Dev + description: | + This PR introduces a Proving Trie object which can be used inside the runtime. This can allow + for things like airdrops where a single hash is stored on chain representing the whole airdrop + and individuals present a proof of their inclusion in the airdrop. + +crates: + - name: sp-runtime + bump: major diff --git a/substrate/primitives/runtime/Cargo.toml b/substrate/primitives/runtime/Cargo.toml index 55e9f4b31995..800bf4bd0737 100644 --- a/substrate/primitives/runtime/Cargo.toml +++ b/substrate/primitives/runtime/Cargo.toml @@ -32,6 +32,7 @@ sp-arithmetic = { workspace = true } sp-core = { workspace = true } sp-io = { workspace = true } sp-std = { workspace = true } +sp-trie = { workspace = true } sp-weights = { workspace = true } docify = { workspace = true } tracing = { workspace = true, features = ["log"], default-features = false } @@ -69,6 +70,7 @@ std = [ "sp-state-machine/std", "sp-std/std", "sp-tracing/std", + "sp-trie/std", "sp-weights/std", "tracing/std", ] diff --git a/substrate/primitives/runtime/src/lib.rs b/substrate/primitives/runtime/src/lib.rs index fd10dee2a7c5..ba1ea3769724 100644 --- a/substrate/primitives/runtime/src/lib.rs +++ b/substrate/primitives/runtime/src/lib.rs @@ -90,6 +90,7 @@ pub mod generic; pub mod legacy; mod multiaddress; pub mod offchain; +pub mod proving_trie; pub mod runtime_logger; mod runtime_string; #[cfg(feature = "std")] @@ -103,6 +104,8 @@ pub use crate::runtime_string::*; // Re-export Multiaddress pub use multiaddress::MultiAddress; +use proving_trie::TrieError; + /// Re-export these since they're only "kind of" generic. pub use generic::{Digest, DigestItem}; @@ -592,6 +595,8 @@ pub enum DispatchError { Unavailable, /// Root origin is not allowed. RootNotAllowed, + /// An error with tries. + Trie(TrieError), } /// Result of a `Dispatchable` which contains the `DispatchResult` and additional information about @@ -697,6 +702,12 @@ impl From for DispatchError { } } +impl From for DispatchError { + fn from(e: TrieError) -> DispatchError { + Self::Trie(e) + } +} + impl From<&'static str> for DispatchError { fn from(err: &'static str) -> DispatchError { Self::Other(err) @@ -721,6 +732,7 @@ impl From for &'static str { Corruption => "State corrupt", Unavailable => "Resource unavailable", RootNotAllowed => "Root not allowed", + Trie(e) => e.into(), } } } @@ -768,6 +780,10 @@ impl traits::Printable for DispatchError { Corruption => "State corrupt".print(), Unavailable => "Resource unavailable".print(), RootNotAllowed => "Root not allowed".print(), + Trie(e) => { + "Trie error: ".print(); + <&'static str>::from(*e).print(); + }, } } } diff --git a/substrate/primitives/runtime/src/proving_trie.rs b/substrate/primitives/runtime/src/proving_trie.rs new file mode 100644 index 000000000000..688bf81e0d7b --- /dev/null +++ b/substrate/primitives/runtime/src/proving_trie.rs @@ -0,0 +1,388 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Types for a compact base-16 merkle trie used for checking and generating proofs within the +//! runtime. The `sp-trie` crate exposes all of these same functionality (and more), but this +//! library is designed to work more easily with runtime native types, which simply need to +//! implement `Encode`/`Decode`. It also exposes a runtime friendly `TrieError` type which can be +//! use inside of a FRAME Pallet. +//! +//! Proofs are created with latest substrate trie format (`LayoutV1`), and are not compatible with +//! proofs using `LayoutV0`. + +use crate::{Decode, DispatchError, Encode, MaxEncodedLen, TypeInfo}; +#[cfg(feature = "serde")] +use crate::{Deserialize, Serialize}; + +use sp_std::vec::Vec; +use sp_trie::{ + trie_types::{TrieDBBuilder, TrieDBMutBuilderV1, TrieError as SpTrieError}, + LayoutV1, MemoryDB, Trie, TrieMut, VerifyError, +}; + +type HashOf = ::Out; + +/// A runtime friendly error type for tries. +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum TrieError { + /* From TrieError */ + /// Attempted to create a trie with a state root not in the DB. + InvalidStateRoot, + /// Trie item not found in the database, + IncompleteDatabase, + /// A value was found in the trie with a nibble key that was not byte-aligned. + ValueAtIncompleteKey, + /// Corrupt Trie item. + DecoderError, + /// Hash is not value. + InvalidHash, + /* From VerifyError */ + /// The statement being verified contains multiple key-value pairs with the same key. + DuplicateKey, + /// The proof contains at least one extraneous node. + ExtraneousNode, + /// The proof contains at least one extraneous value which should have been omitted from the + /// proof. + ExtraneousValue, + /// The proof contains at least one extraneous hash reference the should have been omitted. + ExtraneousHashReference, + /// The proof contains an invalid child reference that exceeds the hash length. + InvalidChildReference, + /// The proof indicates that an expected value was not found in the trie. + ValueMismatch, + /// The proof is missing trie nodes required to verify. + IncompleteProof, + /// The root hash computed from the proof is incorrect. + RootMismatch, + /// One of the proof nodes could not be decoded. + DecodeError, +} + +impl From> for TrieError { + fn from(error: SpTrieError) -> Self { + match error { + SpTrieError::InvalidStateRoot(..) => Self::InvalidStateRoot, + SpTrieError::IncompleteDatabase(..) => Self::IncompleteDatabase, + SpTrieError::ValueAtIncompleteKey(..) => Self::ValueAtIncompleteKey, + SpTrieError::DecoderError(..) => Self::DecoderError, + SpTrieError::InvalidHash(..) => Self::InvalidHash, + } + } +} + +impl From> for TrieError { + fn from(error: VerifyError) -> Self { + match error { + VerifyError::DuplicateKey(..) => Self::DuplicateKey, + VerifyError::ExtraneousNode => Self::ExtraneousNode, + VerifyError::ExtraneousValue(..) => Self::ExtraneousValue, + VerifyError::ExtraneousHashReference(..) => Self::ExtraneousHashReference, + VerifyError::InvalidChildReference(..) => Self::InvalidChildReference, + VerifyError::ValueMismatch(..) => Self::ValueMismatch, + VerifyError::IncompleteProof => Self::IncompleteProof, + VerifyError::RootMismatch(..) => Self::RootMismatch, + VerifyError::DecodeError(..) => Self::DecodeError, + } + } +} + +impl From for &'static str { + fn from(e: TrieError) -> &'static str { + match e { + TrieError::InvalidStateRoot => "The state root is not in the database.", + TrieError::IncompleteDatabase => "A trie item was not found in the database.", + TrieError::ValueAtIncompleteKey => + "A value was found with a key that is not byte-aligned.", + TrieError::DecoderError => "A corrupt trie item was encountered.", + TrieError::InvalidHash => "The hash does not match the expected value.", + TrieError::DuplicateKey => "The proof contains duplicate keys.", + TrieError::ExtraneousNode => "The proof contains extraneous nodes.", + TrieError::ExtraneousValue => "The proof contains extraneous values.", + TrieError::ExtraneousHashReference => "The proof contains extraneous hash references.", + TrieError::InvalidChildReference => "The proof contains an invalid child reference.", + TrieError::ValueMismatch => "The proof indicates a value mismatch.", + TrieError::IncompleteProof => "The proof is incomplete.", + TrieError::RootMismatch => "The root hash computed from the proof is incorrect.", + TrieError::DecodeError => "One of the proof nodes could not be decoded.", + } + } +} + +/// A helper structure for building a basic base-16 merkle trie and creating compact proofs for that +/// trie. Proofs are created with latest substrate trie format (`LayoutV1`), and are not compatible +/// with proofs using `LayoutV0`. +pub struct BasicProvingTrie +where + Hashing: sp_core::Hasher, +{ + db: MemoryDB, + root: HashOf, + _phantom: core::marker::PhantomData<(Key, Value)>, +} + +impl BasicProvingTrie +where + Hashing: sp_core::Hasher, + Key: Encode, + Value: Encode + Decode, +{ + /// Create a new instance of a `ProvingTrie` using an iterator of key/value pairs. + pub fn generate_for(items: I) -> Result + where + I: IntoIterator, + { + let mut db = MemoryDB::default(); + let mut root = Default::default(); + + { + let mut trie = TrieDBMutBuilderV1::new(&mut db, &mut root).build(); + for (key, value) in items.into_iter() { + key.using_encoded(|k| value.using_encoded(|v| trie.insert(k, v))) + .map_err(|_| "failed to insert into trie")?; + } + } + + Ok(Self { db, root, _phantom: Default::default() }) + } + + /// Access the underlying trie root. + pub fn root(&self) -> &HashOf { + &self.root + } + + /// Query a value contained within the current trie. Returns `None` if the + /// nodes within the current `MemoryDB` are insufficient to query the item. + pub fn query(&self, key: Key) -> Option { + let trie = TrieDBBuilder::new(&self.db, &self.root).build(); + key.using_encoded(|s| trie.get(s)) + .ok()? + .and_then(|raw| Value::decode(&mut &*raw).ok()) + } + + /// Create a compact merkle proof needed to prove all `keys` and their values are in the trie. + /// Returns `None` if the nodes within the current `MemoryDB` are insufficient to create a + /// proof. + /// + /// This function makes a proof with latest substrate trie format (`LayoutV1`), and is not + /// compatible with `LayoutV0`. + /// + /// When verifying the proof created by this function, you must include all of the keys and + /// values of the proof, else the verifier will complain that extra nodes are provided in the + /// proof that are not needed. + pub fn create_proof(&self, keys: &[Key]) -> Result>, DispatchError> { + sp_trie::generate_trie_proof::, _, _, _>( + &self.db, + self.root, + &keys.into_iter().map(|k| k.encode()).collect::>>(), + ) + .map_err(|err| TrieError::from(*err).into()) + } + + /// Create a compact merkle proof needed to prove a single key and its value are in the trie. + /// Returns `None` if the nodes within the current `MemoryDB` are insufficient to create a + /// proof. + /// + /// This function makes a proof with latest substrate trie format (`LayoutV1`), and is not + /// compatible with `LayoutV0`. + pub fn create_single_value_proof(&self, key: Key) -> Result>, DispatchError> { + self.create_proof(&[key]) + } +} + +/// Verify the existence or non-existence of `key` and `value` in a given trie root and proof. +/// +/// Proofs must be created with latest substrate trie format (`LayoutV1`). +pub fn verify_single_value_proof( + root: HashOf, + proof: &[Vec], + key: Key, + maybe_value: Option, +) -> Result<(), DispatchError> +where + Hashing: sp_core::Hasher, + Key: Encode, + Value: Encode, +{ + sp_trie::verify_trie_proof::, _, _, _>( + &root, + proof, + &[(key.encode(), maybe_value.map(|value| value.encode()))], + ) + .map_err(|err| TrieError::from(err).into()) +} + +/// Verify the existence or non-existence of multiple `items` in a given trie root and proof. +/// +/// Proofs must be created with latest substrate trie format (`LayoutV1`). +pub fn verify_proof( + root: HashOf, + proof: &[Vec], + items: &[(Key, Option)], +) -> Result<(), DispatchError> +where + Hashing: sp_core::Hasher, + Key: Encode, + Value: Encode, +{ + let items_encoded = items + .into_iter() + .map(|(key, maybe_value)| (key.encode(), maybe_value.as_ref().map(|value| value.encode()))) + .collect::, Option>)>>(); + + sp_trie::verify_trie_proof::, _, _, _>(&root, proof, &items_encoded) + .map_err(|err| TrieError::from(err).into()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::BlakeTwo256; + use sp_core::H256; + use sp_std::collections::btree_map::BTreeMap; + + // A trie which simulates a trie of accounts (u32) and balances (u128). + type BalanceTrie = BasicProvingTrie; + + // The expected root hash for an empty trie. + fn empty_root() -> H256 { + sp_trie::empty_trie_root::>() + } + + fn create_balance_trie() -> BalanceTrie { + // Create a map of users and their balances. + let mut map = BTreeMap::::new(); + for i in 0..100u32 { + map.insert(i, i.into()); + } + + // Put items into the trie. + let balance_trie = BalanceTrie::generate_for(map).unwrap(); + + // Root is changed. + let root = *balance_trie.root(); + assert!(root != empty_root()); + + // Assert valid keys are queryable. + assert_eq!(balance_trie.query(6u32), Some(6u128)); + assert_eq!(balance_trie.query(9u32), Some(9u128)); + assert_eq!(balance_trie.query(69u32), Some(69u128)); + // Invalid key returns none. + assert_eq!(balance_trie.query(6969u32), None); + + balance_trie + } + + #[test] + fn empty_trie_works() { + let empty_trie = BalanceTrie::generate_for(Vec::new()).unwrap(); + assert_eq!(*empty_trie.root(), empty_root()); + } + + #[test] + fn basic_end_to_end_single_value() { + let balance_trie = create_balance_trie(); + let root = *balance_trie.root(); + + // Create a proof for a valid key. + let proof = balance_trie.create_single_value_proof(6u32).unwrap(); + + // Assert key is provable, all other keys are invalid. + for i in 0..200u32 { + if i == 6 { + assert_eq!( + verify_single_value_proof::( + root, + &proof, + i, + Some(u128::from(i)) + ), + Ok(()) + ); + // Wrong value is invalid. + assert_eq!( + verify_single_value_proof::( + root, + &proof, + i, + Some(u128::from(i + 1)) + ), + Err(TrieError::RootMismatch.into()) + ); + } else { + assert!(verify_single_value_proof::( + root, + &proof, + i, + Some(u128::from(i)) + ) + .is_err()); + assert!(verify_single_value_proof::( + root, + &proof, + i, + None:: + ) + .is_err()); + } + } + } + + #[test] + fn basic_end_to_end_multi_value() { + let balance_trie = create_balance_trie(); + let root = *balance_trie.root(); + + // Create a proof for a valid and invalid key. + let proof = balance_trie.create_proof(&[6u32, 69u32, 6969u32]).unwrap(); + let items = [(6u32, Some(6u128)), (69u32, Some(69u128)), (6969u32, None)]; + + assert_eq!(verify_proof::(root, &proof, &items), Ok(())); + } + + #[test] + fn proof_fails_with_bad_data() { + let balance_trie = create_balance_trie(); + let root = *balance_trie.root(); + + // Create a proof for a valid key. + let proof = balance_trie.create_single_value_proof(6u32).unwrap(); + + // Correct data verifies successfully + assert_eq!( + verify_single_value_proof::(root, &proof, 6u32, Some(6u128)), + Ok(()) + ); + + // Fail to verify proof with wrong root + assert_eq!( + verify_single_value_proof::( + Default::default(), + &proof, + 6u32, + Some(6u128) + ), + Err(TrieError::RootMismatch.into()) + ); + + // Fail to verify proof with wrong data + assert_eq!( + verify_single_value_proof::(root, &[], 6u32, Some(6u128)), + Err(TrieError::IncompleteProof.into()) + ); + } +} From 06bc7af5d84cbebfd9e436528770a0fb5ae2d5d1 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 4 Sep 2024 22:07:18 +0200 Subject: [PATCH 262/382] Use describe family and decribe Account terminals --- bridges/snowbridge/primitives/core/src/lib.rs | 70 +++++++++++-------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index c31124edf51d..fd0d3bb51597 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -29,12 +29,12 @@ use sp_io::hashing::keccak_256; use sp_runtime::{traits::AccountIdConversion, RuntimeDebug}; use sp_std::prelude::*; use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, Junction::Parachain, Location, PalletInstance, + AccountId32, AccountKey20, GeneralIndex, GeneralKey, GlobalConsensus, Junction::Parachain, + Location, PalletInstance, }; use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; /// The ID of an agent contract -pub type AgentId = H256; pub use operating_mode::BasicOperatingMode; pub use pricing::{PricingParameters, Rewards}; @@ -153,21 +153,6 @@ pub const PRIMARY_GOVERNANCE_CHANNEL: ChannelId = pub const SECONDARY_GOVERNANCE_CHANNEL: ChannelId = ChannelId::new(hex!("0000000000000000000000000000000000000000000000000000000000000002")); -pub struct DescribeHere; -impl DescribeLocation for DescribeHere { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, []) => Some(Vec::::new().encode()), - _ => None, - } - } -} - -/// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on -/// Ethereum which acts as the sovereign account for the Location. -pub type AgentIdOf = - HashedDescription)>; - /// Metadata to include in the instantiated ERC20 token contract #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct AssetMetadata { @@ -176,38 +161,63 @@ pub struct AssetMetadata { pub decimals: u8, } +pub type AgentId = H256; + +/// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on +/// Ethereum which acts as the sovereign account for the Location. +pub type AgentIdOf = + HashedDescription)>; + pub type TokenId = H256; /// Convert a token location to a stable ID that can be used on the Ethereum side -pub type TokenIdOf = HashedDescription>; +pub type TokenIdOf = + HashedDescription>>; +pub struct DescribeHere; +impl DescribeLocation for DescribeHere { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, []) => Some(Vec::::new().encode()), + _ => None, + } + } +} pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); impl DescribeLocation for DescribeGlobalPrefix { fn describe_location(l: &Location) -> Option> { match (l.parent_count(), l.first_interior()) { - (1, Some(GlobalConsensus(network))) => { + (_, Some(GlobalConsensus(network))) => { let tail = l.clone().split_first_interior().0; let interior = Suffix::describe_location(&tail.into())?; - Some((b"pna", network, interior).encode()) + Some((b"PNA", network, interior).encode()) }, _ => None, } } } -pub struct DescribeInner; -impl DescribeLocation for DescribeInner { +pub struct DescribeToken; +impl DescribeLocation for DescribeToken { fn describe_location(l: &Location) -> Option> { match l.unpack().1 { [] => Some(Vec::::new().encode()), - [Parachain(id)] => Some((*id).encode()), - [Parachain(id), PalletInstance(instance)] => Some((*id, *instance).encode()), - [Parachain(id), PalletInstance(instance), GeneralIndex(index)] => - Some((*id, *instance, *index).encode()), - [Parachain(id), PalletInstance(instance), GeneralKey { data, .. }] => - Some((*id, *instance, *data).encode()), - [Parachain(id), GeneralIndex(index)] => Some((*id, *index).encode()), - [Parachain(id), GeneralKey { data, .. }] => Some((*id, *data).encode()), + [GeneralIndex(index)] => Some((*index).encode()), + [GeneralKey { data, .. }] => Some((*data).encode()), + [AccountKey20 { key, .. }] => Some((*key).encode()), + [AccountId32 { id, .. }] => Some((*id).encode()), + + // Pallet + [PalletInstance(instance)] => Some((*instance).encode()), + [PalletInstance(instance), GeneralIndex(index)] => Some((*instance, *index).encode()), + [PalletInstance(instance), GeneralKey { data, .. }] => + Some((*instance, *data).encode()), + + [PalletInstance(instance), AccountKey20 { key, .. }] => + Some((*instance, *key).encode()), + [PalletInstance(instance), AccountId32 { id, .. }] => Some((*instance, *id).encode()), + + // Reject all other locations _ => None, } } From 8092a1f4506d06d25176b81532ce38fefa145417 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 4 Sep 2024 22:54:17 +0200 Subject: [PATCH 263/382] separate to new file --- bridges/snowbridge/primitives/core/src/lib.rs | 70 +-------- .../primitives/core/src/location.rs | 138 ++++++++++++++++++ .../snowbridge/primitives/core/src/tests.rs | 41 +----- 3 files changed, 142 insertions(+), 107 deletions(-) create mode 100644 bridges/snowbridge/primitives/core/src/location.rs diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index fd0d3bb51597..01ad86a26018 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -9,11 +9,13 @@ mod tests; pub mod inbound; +pub mod location; pub mod operating_mode; pub mod outbound; pub mod pricing; pub mod ringbuffer; +pub use location::{AgentId, AgentIdOf, TokenId, TokenIdOf}; pub use polkadot_parachain_primitives::primitives::{ Id as ParaId, IsSystem, Sibling as SiblingParaId, }; @@ -28,11 +30,7 @@ use sp_core::{ConstU32, H256}; use sp_io::hashing::keccak_256; use sp_runtime::{traits::AccountIdConversion, RuntimeDebug}; use sp_std::prelude::*; -use xcm::prelude::{ - AccountId32, AccountKey20, GeneralIndex, GeneralKey, GlobalConsensus, Junction::Parachain, - Location, PalletInstance, -}; -use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; +use xcm::prelude::{Junction::Parachain, Location}; /// The ID of an agent contract pub use operating_mode::BasicOperatingMode; @@ -160,65 +158,3 @@ pub struct AssetMetadata { pub symbol: BoundedVec>, pub decimals: u8, } - -pub type AgentId = H256; - -/// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on -/// Ethereum which acts as the sovereign account for the Location. -pub type AgentIdOf = - HashedDescription)>; - -pub type TokenId = H256; - -/// Convert a token location to a stable ID that can be used on the Ethereum side -pub type TokenIdOf = - HashedDescription>>; - -pub struct DescribeHere; -impl DescribeLocation for DescribeHere { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, []) => Some(Vec::::new().encode()), - _ => None, - } - } -} -pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); -impl DescribeLocation for DescribeGlobalPrefix { - fn describe_location(l: &Location) -> Option> { - match (l.parent_count(), l.first_interior()) { - (_, Some(GlobalConsensus(network))) => { - let tail = l.clone().split_first_interior().0; - let interior = Suffix::describe_location(&tail.into())?; - Some((b"PNA", network, interior).encode()) - }, - _ => None, - } - } -} - -pub struct DescribeToken; -impl DescribeLocation for DescribeToken { - fn describe_location(l: &Location) -> Option> { - match l.unpack().1 { - [] => Some(Vec::::new().encode()), - [GeneralIndex(index)] => Some((*index).encode()), - [GeneralKey { data, .. }] => Some((*data).encode()), - [AccountKey20 { key, .. }] => Some((*key).encode()), - [AccountId32 { id, .. }] => Some((*id).encode()), - - // Pallet - [PalletInstance(instance)] => Some((*instance).encode()), - [PalletInstance(instance), GeneralIndex(index)] => Some((*instance, *index).encode()), - [PalletInstance(instance), GeneralKey { data, .. }] => - Some((*instance, *data).encode()), - - [PalletInstance(instance), AccountKey20 { key, .. }] => - Some((*instance, *key).encode()), - [PalletInstance(instance), AccountId32 { id, .. }] => Some((*instance, *id).encode()), - - // Reject all other locations - _ => None, - } - } -} diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs new file mode 100644 index 000000000000..28952cd29713 --- /dev/null +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +//! # Location +//! +//! Location helpers for dealing with Tokens and Agents + +pub use polkadot_parachain_primitives::primitives::{ + Id as ParaId, IsSystem, Sibling as SiblingParaId, +}; +pub use sp_core::U256; + +use codec::Encode; +use sp_core::H256; +use sp_std::prelude::*; +use xcm::prelude::{ + AccountId32, AccountKey20, GeneralIndex, GeneralKey, GlobalConsensus, Location, PalletInstance, +}; +use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; + +pub type AgentId = H256; + +/// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on +/// Ethereum which acts as the sovereign account for the Location. +pub type AgentIdOf = + HashedDescription)>; + +pub type TokenId = H256; + +/// Convert a token location to a stable ID that can be used on the Ethereum side +pub type TokenIdOf = HashedDescription< + TokenId, + DescribeGlobalPrefix<(DescribeHere, DescribeFamily)>, +>; + +pub struct DescribeHere; +impl DescribeLocation for DescribeHere { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, []) => Some(Vec::::new().encode()), + _ => None, + } + } +} +pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); +impl DescribeLocation for DescribeGlobalPrefix { + fn describe_location(l: &Location) -> Option> { + match (l.parent_count(), l.first_interior()) { + (_, Some(GlobalConsensus(network))) => { + let mut tail = l.clone().split_first_interior().0; + tail.dec_parent(); + let interior = Suffix::describe_location(&tail)?; + Some((b"PNA", network, interior).encode()) + }, + _ => None, + } + } +} + +pub struct DescribeTokenTerminal; +impl DescribeLocation for DescribeTokenTerminal { + fn describe_location(l: &Location) -> Option> { + match l.unpack().1 { + [] => Some(Vec::::new().encode()), + [GeneralIndex(index)] => Some((*index).encode()), + [GeneralKey { data, .. }] => Some((*data).encode()), + [AccountKey20 { key, .. }] => Some((*key).encode()), + [AccountId32 { id, .. }] => Some((*id).encode()), + + // Pallet + [PalletInstance(instance)] => Some((*instance).encode()), + [PalletInstance(instance), GeneralIndex(index)] => Some((*instance, *index).encode()), + [PalletInstance(instance), GeneralKey { data, .. }] => + Some((*instance, *data).encode()), + + [PalletInstance(instance), AccountKey20 { key, .. }] => + Some((*instance, *key).encode()), + [PalletInstance(instance), AccountId32 { id, .. }] => Some((*instance, *id).encode()), + + // Reject all other locations + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use crate::TokenIdOf; + use xcm::prelude::{ + GeneralIndex, GeneralKey, GlobalConsensus, Kusama, Location, PalletInstance, Parachain, + Westend, + }; + use xcm_executor::traits::ConvertLocation; + + #[test] + fn test_token_of_id() { + let token_locations = [ + // Relay Chain cases + // Relay Chain relative to Ethereum + Location::new(1, [GlobalConsensus(Westend)]), + // Relay Chain relative to another polkadot chain. + Location::new(2, [GlobalConsensus(Kusama)]), + // Parachain cases + // Parachain relative to Ethereum + Location::new(1, [GlobalConsensus(Westend), Parachain(2000)]), + // Parachain relative to another polkadot chain. + Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), + // Parachain general index + Location::new(1, [GlobalConsensus(Westend), Parachain(2000), GeneralIndex(1)]), + // Parchain Pallet instance cases + Location::new(1, [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8)]), + // Parachain General Key + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + GeneralKey { length: 32, data: [1; 32] }, + ], + ), + ]; + + for token in token_locations { + assert!( + TokenIdOf::convert_location(&token).is_some(), + "Valid token = {token:?} yeilds no TokenId." + ); + } + + let non_token_locations = [Location::new(1, [])]; + + for token in non_token_locations { + assert!( + TokenIdOf::convert_location(&token).is_none(), + "Invalid token = {token:?} yeilds a TokenId." + ); + } + } +} diff --git a/bridges/snowbridge/primitives/core/src/tests.rs b/bridges/snowbridge/primitives/core/src/tests.rs index 3667ff7f03f9..725fff1a9c94 100644 --- a/bridges/snowbridge/primitives/core/src/tests.rs +++ b/bridges/snowbridge/primitives/core/src/tests.rs @@ -1,9 +1,5 @@ -use crate::{ChannelId, ParaId, TokenIdOf}; +use crate::{ChannelId, ParaId}; use hex_literal::hex; -use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, Location, PalletInstance, Parachain, Westend, -}; -use xcm_executor::traits::ConvertLocation; const EXPECT_CHANNEL_ID: [u8; 32] = hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539"); @@ -15,38 +11,3 @@ fn generate_channel_id() { let channel_id: ChannelId = para_id.into(); assert_eq!(channel_id, EXPECT_CHANNEL_ID.into()); } - -#[test] -fn test_describe_relay_token() { - let asset_location: Location = Location::new(1, [GlobalConsensus(Westend)]); - assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); -} - -#[test] -fn test_describe_primary_token_from_parachain() { - let asset_location: Location = Location::new(1, [GlobalConsensus(Westend), Parachain(2000)]); - assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); -} - -#[test] -fn test_describe_token_with_pallet_instance_prefix() { - let asset_location: Location = - Location::new(1, [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8)]); - assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); -} - -#[test] -fn test_describe_token_with_general_index_prefix() { - let asset_location: Location = - Location::new(1, [GlobalConsensus(Westend), Parachain(2000), GeneralIndex(1)]); - assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); -} - -#[test] -fn test_describe_token_with_general_key_prefix() { - let asset_location: Location = Location::new( - 1, - [GlobalConsensus(Westend), Parachain(2000), GeneralKey { length: 32, data: [1; 32] }], - ); - assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); -} From 0ae39b95a56a6ba6825b4a55cd344d885c0e6a0b Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 4 Sep 2024 23:18:58 +0200 Subject: [PATCH 264/382] all cases added --- .../primitives/core/src/location.rs | 104 +++++++++++++++--- 1 file changed, 89 insertions(+), 15 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index 28952cd29713..17c5ada75684 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -49,7 +49,7 @@ impl DescribeLocation for DescribeGlobalPrefix let mut tail = l.clone().split_first_interior().0; tail.dec_parent(); let interior = Suffix::describe_location(&tail)?; - Some((b"PNA", network, interior).encode()) + Some((b"GlobalConsensus", network, interior).encode()) }, _ => None, } @@ -61,20 +61,22 @@ impl DescribeLocation for DescribeTokenTerminal { fn describe_location(l: &Location) -> Option> { match l.unpack().1 { [] => Some(Vec::::new().encode()), - [GeneralIndex(index)] => Some((*index).encode()), - [GeneralKey { data, .. }] => Some((*data).encode()), - [AccountKey20 { key, .. }] => Some((*key).encode()), - [AccountId32 { id, .. }] => Some((*id).encode()), + [GeneralIndex(index)] => Some((b"Index", *index).encode()), + [GeneralKey { data, .. }] => Some((b"Key", *data).encode()), + [AccountKey20 { key, .. }] => Some((b"AccountKey", *key).encode()), + [AccountId32 { id, .. }] => Some((b"AccountId", *id).encode()), // Pallet - [PalletInstance(instance)] => Some((*instance).encode()), - [PalletInstance(instance), GeneralIndex(index)] => Some((*instance, *index).encode()), + [PalletInstance(instance)] => Some((b"Pallet", *instance).encode()), + [PalletInstance(instance), GeneralIndex(index)] => + Some((b"Pallet", *instance, "Index", *index).encode()), [PalletInstance(instance), GeneralKey { data, .. }] => - Some((*instance, *data).encode()), + Some((b"Pallet", *instance, b"Key", *data).encode()), [PalletInstance(instance), AccountKey20 { key, .. }] => - Some((*instance, *key).encode()), - [PalletInstance(instance), AccountId32 { id, .. }] => Some((*instance, *id).encode()), + Some((b"Pallet", *instance, b"AccountKey", *key).encode()), + [PalletInstance(instance), AccountId32 { id, .. }] => + Some((b"Pallet", *instance, b"AccountId", *id).encode()), // Reject all other locations _ => None, @@ -86,8 +88,8 @@ impl DescribeLocation for DescribeTokenTerminal { mod tests { use crate::TokenIdOf; use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, Kusama, Location, PalletInstance, Parachain, - Westend, + GeneralIndex, GeneralKey, GlobalConsensus, Junction::*, Location, NetworkId::*, + PalletInstance, Parachain, }; use xcm_executor::traits::ConvertLocation; @@ -106,15 +108,82 @@ mod tests { Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), // Parachain general index Location::new(1, [GlobalConsensus(Westend), Parachain(2000), GeneralIndex(1)]), + // Parachain general key + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + GeneralKey { length: 32, data: [0; 32] }, + ], + ), + // Parachain account key 20 + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + AccountKey20 { network: None, key: [0; 20] }, + ], + ), + // Parachain account id 32 + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + AccountId32 { network: None, id: [0; 32] }, + ], + ), // Parchain Pallet instance cases + // Parachain pallet instance Location::new(1, [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8)]), - // Parachain General Key + // Parachain Pallet general index + Location::new( + 1, + [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8), GeneralIndex(1)], + ), + // Parachain Pallet general key Location::new( 1, [ GlobalConsensus(Westend), Parachain(2000), - GeneralKey { length: 32, data: [1; 32] }, + PalletInstance(8), + GeneralKey { length: 32, data: [0; 32] }, + ], + ), + // Parachain Pallet account key 20 + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + PalletInstance(8), + AccountKey20 { network: None, key: [0; 20] }, + ], + ), + // Parachain Pallet account id 32 + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + PalletInstance(8), + AccountId32 { network: None, id: [0; 32] }, + ], + ), + // Ethereum cases + // Ethereum location relative to Polkadot + Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]), + // Ethereum location relative to Ethereum + Location::new(1, [GlobalConsensus(Ethereum { chain_id: 2 })]), + // Ethereum ERC20 location relative to Polkadot + Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: 1 }), + AccountKey20 { network: None, key: [0; 20] }, ], ), ]; @@ -126,7 +195,12 @@ mod tests { ); } - let non_token_locations = [Location::new(1, [])]; + let non_token_locations = [ + // Relative location for a token should fail. + Location::new(1, []), + // Relative location for a token should fail. + Location::new(1, [Parachain(1000)]), + ]; for token in non_token_locations { assert!( From 3da4cbb031155436ff2505e0eedca18cf01098c0 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 08:32:03 +0800 Subject: [PATCH 265/382] Use full junction name --- .../snowbridge/primitives/core/src/location.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index 17c5ada75684..14776452ff16 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -61,22 +61,22 @@ impl DescribeLocation for DescribeTokenTerminal { fn describe_location(l: &Location) -> Option> { match l.unpack().1 { [] => Some(Vec::::new().encode()), - [GeneralIndex(index)] => Some((b"Index", *index).encode()), - [GeneralKey { data, .. }] => Some((b"Key", *data).encode()), - [AccountKey20 { key, .. }] => Some((b"AccountKey", *key).encode()), - [AccountId32 { id, .. }] => Some((b"AccountId", *id).encode()), + [GeneralIndex(index)] => Some((b"GeneralIndex", *index).encode()), + [GeneralKey { data, .. }] => Some((b"GeneralKey", *data).encode()), + [AccountKey20 { key, .. }] => Some((b"AccountKey20", *key).encode()), + [AccountId32 { id, .. }] => Some((b"AccountId32", *id).encode()), // Pallet - [PalletInstance(instance)] => Some((b"Pallet", *instance).encode()), + [PalletInstance(instance)] => Some((b"PalletInstance", *instance).encode()), [PalletInstance(instance), GeneralIndex(index)] => - Some((b"Pallet", *instance, "Index", *index).encode()), + Some((b"PalletInstance", *instance, "GeneralIndex", *index).encode()), [PalletInstance(instance), GeneralKey { data, .. }] => - Some((b"Pallet", *instance, b"Key", *data).encode()), + Some((b"PalletInstance", *instance, b"GeneralKey", *data).encode()), [PalletInstance(instance), AccountKey20 { key, .. }] => - Some((b"Pallet", *instance, b"AccountKey", *key).encode()), + Some((b"PalletInstance", *instance, b"AccountKey20", *key).encode()), [PalletInstance(instance), AccountId32 { id, .. }] => - Some((b"Pallet", *instance, b"AccountId", *id).encode()), + Some((b"PalletInstance", *instance, b"AccountId32", *id).encode()), // Reject all other locations _ => None, From 991de7c153bb2792b42ab1f7a825658aaf5801c3 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 10:19:22 +0800 Subject: [PATCH 266/382] Add SetFeesMode --- .../snowbridge/primitives/router/src/inbound/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 3e1ddb1ffc6a..85d3091543b2 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -394,7 +394,7 @@ impl< asset_hub_fee: u128, ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { let network = Ethereum { chain_id }; - let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee / 2).into(); + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); let (dest_para_id, beneficiary, dest_para_fee) = match destination { // Final destination is a 32-byte account on AssetHub @@ -431,15 +431,19 @@ impl< WithdrawAsset(asset.clone().into()), ]; + let bridge_location = Location::new(2, GlobalConsensus(network)); + match dest_para_id { Some(dest_para_id) => { let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); instructions.extend(vec![ + // `SetFeesMode` to pay transport fee from bridge sovereign + SetFeesMode { jit_withdraw: true }, // `SetAppendix` ensures that `fees` are not trapped in any case SetAppendix(Xcm(vec![DepositAsset { - assets: AllCounted(1).into(), - beneficiary: Location::new(1, [Parachain(dest_para_id)]), + assets: AllCounted(2).into(), + beneficiary: bridge_location, }])), // Perform a reserve withdraw to send to destination chain. Leave half of the // asset_hub_fee for the delivery cost @@ -451,7 +455,7 @@ impl< xcm: vec![ // Buy execution on target. BuyExecution { fees: dest_para_fee_asset, weight_limit: Unlimited }, - // Deposit asset to beneficiary. + // Deposit asset and fee to beneficiary. DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, // Forward message id to destination parachain. SetTopic(message_id.into()), From d5346e72e54b040670b60475b90a3c467f03b08c Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Thu, 5 Sep 2024 03:41:11 -0400 Subject: [PATCH 267/382] fix link check (#5590) PR Doc #5443 broke the link checker because it includes a link to this private repo: https://github.com/paritytech/devops/issues/3502 This PR adds the `prdoc` folder to the excluded paths of the link checker to ensure that historical PR Docs do not break the pipeline. I think this makes sense over whitelisting the github link because we probably expect that in a long enough time period, links from old PR docs will start to break, but I don't think we intend to update those old PR docs, or start whitelisting lots of urls. --- .config/lychee.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.config/lychee.toml b/.config/lychee.toml index b7bb6f0ce495..b1f08de33340 100644 --- a/.config/lychee.toml +++ b/.config/lychee.toml @@ -18,7 +18,10 @@ accept = [ "429", ] -exclude_path = ["./target"] +exclude_path = [ + "./prdoc", + "./target", +] exclude = [ # Place holders (no need to fix these): From cf330ccd58e62b8033acdd0e7b14f33d7a480c09 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Thu, 5 Sep 2024 10:14:50 +0200 Subject: [PATCH 268/382] [pallet-revive] refactor uapi with better types (#5555) start using better type for address, code_hash, and salt in runtime and the uapi crate fix https://github.com/paritytech/polkadot-sdk/issues/5575 --- prdoc/pr_5555.prdoc | 15 ++++ substrate/frame/revive/fixtures/build.rs | 1 + .../frame/revive/fixtures/contracts/call.rs | 2 +- .../fixtures/contracts/call_return_code.rs | 2 +- .../contracts/call_runtime_and_call.rs | 2 +- .../contracts/call_with_flags_and_value.rs | 2 +- .../fixtures/contracts/call_with_limit.rs | 2 +- .../fixtures/contracts/caller_contract.rs | 14 ++-- .../contracts/chain_extension_temp_storage.rs | 7 +- .../fixtures/contracts/common/src/lib.rs | 12 +++ .../contracts/create_storage_and_call.rs | 2 +- .../create_storage_and_instantiate.rs | 7 +- .../create_transient_storage_and_call.rs | 2 +- .../fixtures/contracts/delegate_call.rs | 2 +- .../fixtures/contracts/delegate_call_lib.rs | 5 +- .../contracts/delegate_call_simple.rs | 2 +- .../contracts/destroy_and_transfer.rs | 16 ++-- .../frame/revive/fixtures/contracts/drain.rs | 2 +- .../contracts/instantiate_return_code.rs | 4 +- .../contracts/locking_delegate_dependency.rs | 2 +- .../fixtures/contracts/read_only_call.rs | 2 +- .../revive/fixtures/contracts/recurse.rs | 7 +- .../fixtures/contracts/self_destruct.rs | 8 +- .../contracts/self_destructing_constructor.rs | 2 +- .../fixtures/contracts/set_code_hash.rs | 2 +- .../contracts/transfer_return_code.rs | 2 +- .../frame/revive/src/benchmarking/mod.rs | 26 +++--- substrate/frame/revive/src/debug.rs | 2 +- substrate/frame/revive/src/lib.rs | 8 +- substrate/frame/revive/src/storage.rs | 6 +- .../frame/revive/src/tests/test_debug.rs | 20 ++--- substrate/frame/revive/src/wasm/runtime.rs | 69 +++++++-------- substrate/frame/revive/src/weights.rs | 24 ++---- substrate/frame/revive/uapi/src/host.rs | 47 +++++------ .../frame/revive/uapi/src/host/riscv32.rs | 84 +++++++++---------- 35 files changed, 211 insertions(+), 201 deletions(-) create mode 100644 prdoc/pr_5555.prdoc diff --git a/prdoc/pr_5555.prdoc b/prdoc/pr_5555.prdoc new file mode 100644 index 000000000000..630345b9b5a1 --- /dev/null +++ b/prdoc/pr_5555.prdoc @@ -0,0 +1,15 @@ +title: Make salt optional + +doc: + - audience: Runtime Dev + description: | + Remove address_len and salt_len from uapi as both are now fixed size + +crates: + - name: pallet-revive + bump: patch + - name: pallet-revive-uapi + bump: patch + - name: pallet-revive-fixtures + bump: patch + diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index ed981a75467b..944ae246c1b8 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -159,6 +159,7 @@ mod build { fn post_process(input_path: &Path, output_path: &Path) -> Result<()> { let mut config = polkavm_linker::Config::default(); config.set_strip(true); + config.set_optimize(false); let orig = fs::read(input_path).with_context(|| format!("Failed to read {:?}", input_path))?; let linked = polkavm_linker::program_from_elf(config, orig.as_ref()) diff --git a/substrate/frame/revive/fixtures/contracts/call.rs b/substrate/frame/revive/fixtures/contracts/call.rs index 73f427650c20..93687441fa50 100644 --- a/substrate/frame/revive/fixtures/contracts/call.rs +++ b/substrate/frame/revive/fixtures/contracts/call.rs @@ -31,7 +31,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( callee_input: [u8; 4], - callee_addr: [u8; 20], + callee_addr: &[u8; 20], ); // Call the callee diff --git a/substrate/frame/revive/fixtures/contracts/call_return_code.rs b/substrate/frame/revive/fixtures/contracts/call_return_code.rs index e8f995cffc7f..29b77c343fe9 100644 --- a/substrate/frame/revive/fixtures/contracts/call_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/call_return_code.rs @@ -33,7 +33,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( 100, - callee_addr: [u8; 20], + callee_addr: &[u8; 20], input: [u8], ); diff --git a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs index f3d2ece21327..7cd46849655f 100644 --- a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs @@ -31,7 +31,7 @@ pub extern "C" fn call() { input!( 512, callee_input: [u8; 4], - callee_addr: [u8; 20], + callee_addr: &[u8; 20], call: [u8], ); diff --git a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs index 15c1124eeaee..c3204c29281c 100644 --- a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs +++ b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs @@ -31,7 +31,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( 256, - callee_addr: [u8; 20], + callee_addr: &[u8; 20], flags: u32, value: u64, forwarded_input: [u8], diff --git a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs index 985df6724110..a941aa9a3421 100644 --- a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs +++ b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs @@ -32,7 +32,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( 256, - callee_addr: [u8; 20], + callee_addr: &[u8; 20], ref_time: u64, proof_size: u64, forwarded_input: [u8], diff --git a/substrate/frame/revive/fixtures/contracts/caller_contract.rs b/substrate/frame/revive/fixtures/contracts/caller_contract.rs index dceab813f88f..3b83f208d623 100644 --- a/substrate/frame/revive/fixtures/contracts/caller_contract.rs +++ b/substrate/frame/revive/fixtures/contracts/caller_contract.rs @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(code_hash: [u8; 32],); + input!(code_hash: &[u8; 32],); // The value to transfer on instantiation and calls. Chosen to be greater than existential // deposit. @@ -73,7 +73,6 @@ pub extern "C" fn call() { // Deploy the contract successfully. let mut callee = [0u8; 20]; - let callee = &mut &mut callee[..]; api::instantiate( code_hash, @@ -82,17 +81,16 @@ pub extern "C" fn call() { None, // No deposit limit. &value, &input, - Some(callee), + Some(&mut callee), None, &salt, ) .unwrap(); - assert_eq!(callee.len(), 20); // Call the new contract and expect it to return failing exit code. let res = api::call( uapi::CallFlags::empty(), - callee, + &callee, 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, // No deposit limit. @@ -105,7 +103,7 @@ pub extern "C" fn call() { // Fail to call the contract due to insufficient ref_time weight. let res = api::call( uapi::CallFlags::empty(), - callee, + &callee, 1u64, // Too little ref_time weight. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, // No deposit limit. @@ -118,7 +116,7 @@ pub extern "C" fn call() { // Fail to call the contract due to insufficient proof_size weight. let res = api::call( uapi::CallFlags::empty(), - callee, + &callee, 0u64, // How much ref_time weight to devote for the execution. 0 = all. 1u64, // too little proof_size weight None, // No deposit limit. @@ -132,7 +130,7 @@ pub extern "C" fn call() { let mut output = [0u8; 4]; api::call( uapi::CallFlags::empty(), - callee, + &callee, 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, // No deposit limit. diff --git a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs index c7596e44dab3..bb5c1ccbc1d6 100644 --- a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs +++ b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs @@ -20,7 +20,7 @@ #![no_std] #![no_main] -use common::{input, output}; +use common::input; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -47,12 +47,13 @@ pub extern "C" fn call() { input[8] = 1u8; // Read the contract address. - output!(addr, [0u8; 32], api::address,); + let mut addr = [0u8; 20]; + api::address(&mut addr); // call self api::call( uapi::CallFlags::ALLOW_REENTRY, - addr, + &addr, 0u64, // How much ref_time to devote for the execution. 0 = all. 0u64, // How much proof_size to devote for the execution. 0 = all. None, // No deposit limit. diff --git a/substrate/frame/revive/fixtures/contracts/common/src/lib.rs b/substrate/frame/revive/fixtures/contracts/common/src/lib.rs index 6631af8292fe..947247e9cf74 100644 --- a/substrate/frame/revive/fixtures/contracts/common/src/lib.rs +++ b/substrate/frame/revive/fixtures/contracts/common/src/lib.rs @@ -100,11 +100,23 @@ macro_rules! input { input!(@inner $input, $cursor + $n, $($rest)*); }; + // Match an array reference of the given size. + // e.g input!(var1: &[u8; 32], ); + (@inner $input:expr, $cursor:expr, $var:ident: &[u8; $n:expr], $($rest:tt)*) => { + let $var: &[u8; $n] = &$input[$cursor..$cursor+$n].try_into().unwrap(); + input!(@inner $input, $cursor + $n, $($rest)*); + }; + // Size of a u8 slice. (@size $size:expr, $var:ident: [u8; $n:expr], $($rest:tt)*) => { input!(@size $size + $n, $($rest)*) }; + // Size of an array reference. + (@size $size:expr, $var:ident: &[u8; $n:expr], $($rest:tt)*) => { + input!(@size $size + $n, $($rest)*) + }; + // Entry point, with the buffer and it's size specified first. // e.g input!(buffer, 512, var1: u32, var2: [u8], ); ($buffer:ident, $size:expr, $($rest:tt)*) => { diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs index 7a0b497079cd..28d161791e5b 100644 --- a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs @@ -32,7 +32,7 @@ pub extern "C" fn call() { input!( buffer, input: [u8; 4], - callee: [u8; 20], + callee: &[u8; 20], deposit_limit: [u8; 8], ); diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs index 53b9afba7783..d87c2e8cd35a 100644 --- a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs @@ -31,14 +31,13 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( input: [u8; 4], - code_hash: [u8; 32], + code_hash: &[u8; 32], deposit_limit: [u8; 8], ); let value = 10_000u64.to_le_bytes(); let salt = [0u8; 32]; let mut address = [0u8; 20]; - let address = &mut &mut address[..]; api::instantiate( code_hash, @@ -47,12 +46,12 @@ pub extern "C" fn call() { Some(deposit_limit), &value, input, - Some(address), + Some(&mut address), None, &salt, ) .unwrap(); // Return the deployed contract address. - api::return_value(uapi::ReturnFlags::empty(), address); + api::return_value(uapi::ReturnFlags::empty(), &address); } diff --git a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs index 8788542a0c5a..753490cf26b7 100644 --- a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs @@ -35,7 +35,7 @@ pub extern "C" fn call() { buffer, len: u32, input: [u8; 4], - callee: [u8; 20], + callee: &[u8; 20], ); let rounds = len as usize / BUFFER.len(); diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call.rs b/substrate/frame/revive/fixtures/contracts/delegate_call.rs index d03ddab1bc58..9fd155408af3 100644 --- a/substrate/frame/revive/fixtures/contracts/delegate_call.rs +++ b/substrate/frame/revive/fixtures/contracts/delegate_call.rs @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(code_hash: [u8; 32],); + input!(code_hash: &[u8; 32],); let mut key = [0u8; 32]; key[0] = 1u8; diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs index 921543dced06..c5525423a9ee 100644 --- a/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs @@ -44,6 +44,7 @@ pub extern "C" fn call() { assert_eq!(value_transferred, 1337); // Assert that ALICE is the caller of the contract. - output!(caller, [0u8; 20], api::caller,); - assert_eq!(&caller[..], &[1u8; 20]); + let mut caller = [0u8; 20]; + api::caller(&mut caller); + assert_eq!(caller, [1u8; 20]); } diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs index cf3351c52fdc..20f8ec3364ee 100644 --- a/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(code_hash: [u8; 32],); + input!(code_hash: &[u8; 32],); // Delegate call into passed code hash. let input = [0u8; 0]; diff --git a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs index b86e761d53e7..4959a5e2e0ce 100644 --- a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs +++ b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs @@ -27,11 +27,10 @@ const VALUE: [u8; 8] = [0, 0, 1u8, 0, 0, 0, 0, 0]; #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn deploy() { - input!(code_hash: [u8; 32],); + input!(code_hash: &[u8; 32],); let input = [0u8; 0]; let mut address = [0u8; 20]; - let address = &mut &mut address[..]; let salt = [47u8; 32]; api::instantiate( @@ -41,27 +40,28 @@ pub extern "C" fn deploy() { None, // No deposit limit. &VALUE, &input, - Some(address), + Some(&mut address), None, &salt, ) .unwrap(); // Return the deployed contract address. - api::set_storage(StorageFlags::empty(), &ADDRESS_KEY, address); + api::set_storage(StorageFlags::empty(), &ADDRESS_KEY, &address); } #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { let mut callee_addr = [0u8; 20]; - let callee_addr = &mut &mut callee_addr[..]; - api::get_storage(StorageFlags::empty(), &ADDRESS_KEY, callee_addr).unwrap(); + let callee = &mut &mut callee_addr[..]; + api::get_storage(StorageFlags::empty(), &ADDRESS_KEY, callee).unwrap(); + assert!(callee.len() == 20); // Calling the destination contract with non-empty input data should fail. let res = api::call( uapi::CallFlags::empty(), - callee_addr, + &callee_addr, 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, // No deposit limit. @@ -74,7 +74,7 @@ pub extern "C" fn call() { // Call the destination contract regularly, forcing it to self-destruct. api::call( uapi::CallFlags::empty(), - callee_addr, + &callee_addr, 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, // No deposit limit. diff --git a/substrate/frame/revive/fixtures/contracts/drain.rs b/substrate/frame/revive/fixtures/contracts/drain.rs index f5c8681c9382..b46d4f7c8418 100644 --- a/substrate/frame/revive/fixtures/contracts/drain.rs +++ b/substrate/frame/revive/fixtures/contracts/drain.rs @@ -39,6 +39,6 @@ pub extern "C" fn call() { // Try to self-destruct by sending more balance to the 0 address. // The call will fail because a contract transfer has a keep alive requirement. - let res = api::transfer(&[0u8; 32], &balance.to_le_bytes()); + let res = api::transfer(&[0u8; 20], &balance.to_le_bytes()); assert!(matches!(res, Err(uapi::ReturnErrorCode::TransferFailed))); } diff --git a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs index 90884f1a2a68..a81ffea943d4 100644 --- a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(buffer, 36, code_hash: [u8; 32],); + input!(buffer, 36, code_hash: &[u8; 32],); let input = &buffer[32..]; let err_code = match api::instantiate( @@ -41,7 +41,7 @@ pub extern "C" fn call() { input, None, None, - &[0u8; 0], // Empty salt. + &[0u8; 32], // Salt. ) { Ok(_) => 0u32, Err(code) => code as u32, diff --git a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs index 2f3e5ae148cb..2efacb4e683f 100644 --- a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs +++ b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs @@ -30,7 +30,7 @@ const ETH_ALICE: [u8; 20] = [1u8; 20]; fn load_input(delegate_call: bool) { input!( action: u32, - code_hash: [u8; 32], + code_hash: &[u8; 32], ); match action { diff --git a/substrate/frame/revive/fixtures/contracts/read_only_call.rs b/substrate/frame/revive/fixtures/contracts/read_only_call.rs index ef8bc95f0098..7476b7a8366d 100644 --- a/substrate/frame/revive/fixtures/contracts/read_only_call.rs +++ b/substrate/frame/revive/fixtures/contracts/read_only_call.rs @@ -31,7 +31,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( 256, - callee_addr: [u8; 20], + callee_addr: &[u8; 20], callee_input: [u8], ); diff --git a/substrate/frame/revive/fixtures/contracts/recurse.rs b/substrate/frame/revive/fixtures/contracts/recurse.rs index f4dfd6c965d0..c15784b7f245 100644 --- a/substrate/frame/revive/fixtures/contracts/recurse.rs +++ b/substrate/frame/revive/fixtures/contracts/recurse.rs @@ -20,7 +20,7 @@ #![no_std] #![no_main] -use common::{input, output}; +use common::input; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -33,7 +33,8 @@ pub extern "C" fn call() { input!(calls_left: u32, ); // own address - output!(addr, [0u8; 32], api::address,); + let mut addr = [0u8; 20]; + api::address(&mut addr); if calls_left == 0 { return @@ -41,7 +42,7 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::ALLOW_REENTRY, - addr, + &addr, 0u64, // How much ref_time to devote for the execution. 0 = all. 0u64, // How much deposit_limit to devote for the execution. 0 = all. None, // No deposit limit. diff --git a/substrate/frame/revive/fixtures/contracts/self_destruct.rs b/substrate/frame/revive/fixtures/contracts/self_destruct.rs index b0e004018f10..0e1e4d30e6f3 100644 --- a/substrate/frame/revive/fixtures/contracts/self_destruct.rs +++ b/substrate/frame/revive/fixtures/contracts/self_destruct.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -use common::{input, output}; +use common::input; use uapi::{HostFn, HostFnImpl as api}; const ETH_DJANGO: [u8; 20] = [4u8; 20]; @@ -36,10 +36,12 @@ pub extern "C" fn call() { input!(input, 4,); if !input.is_empty() { - output!(addr, [0u8; 20], api::address,); + let mut addr = [0u8; 20]; + api::address(&mut addr); + api::call( uapi::CallFlags::ALLOW_REENTRY, - addr, + &addr, 0u64, // How much ref_time to devote for the execution. 0 = all. 0u64, // How much proof_size to devote for the execution. 0 = all. None, // No deposit limit. diff --git a/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs b/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs index 28bcef978231..3285aecbe780 100644 --- a/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs +++ b/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs @@ -24,7 +24,7 @@ use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn deploy() { - api::terminate(&[0u8; 32]); + api::terminate(&[0u8; 20]); } #[no_mangle] diff --git a/substrate/frame/revive/fixtures/contracts/set_code_hash.rs b/substrate/frame/revive/fixtures/contracts/set_code_hash.rs index e3cf4becfb97..75995d7bb8a2 100644 --- a/substrate/frame/revive/fixtures/contracts/set_code_hash.rs +++ b/substrate/frame/revive/fixtures/contracts/set_code_hash.rs @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(addr: [u8; 32],); + input!(addr: &[u8; 32],); api::set_code_hash(addr).unwrap(); // we return 1 after setting new code_hash diff --git a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs index d3f6a1dd3a08..3e1f2757c27a 100644 --- a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - let ret_code = match api::transfer(&[0u8; 32], &100u64.to_le_bytes()) { + let ret_code = match api::transfer(&[0u8; 20], &100u64.to_le_bytes()) { Ok(_) => 0u32, Err(code) => code as u32, }; diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index 3ffd53e3561c..2c5285622843 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -532,17 +532,17 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn seal_caller() { let len = H160::len_bytes(); - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [vec![0u8; len as _], ]); let result; #[block] { - result = runtime.bench_caller(memory.as_mut_slice(), 4, 0); + result = runtime.bench_caller(memory.as_mut_slice(), 0); } assert_ok!(result); assert_eq!( - ::decode(&mut &memory[4..]).unwrap(), + ::decode(&mut &memory[..]).unwrap(), T::AddressMapper::to_address(&runtime.ext().caller().account_id().unwrap()) ); } @@ -567,17 +567,17 @@ mod benchmarks { fn seal_code_hash() { let contract = Contract::::with_index(1, WasmModule::dummy(), vec![]).unwrap(); let len = ::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], contract.account_id.encode(), ]); + build_runtime!(runtime, memory: [vec![0u8; len as _], contract.account_id.encode(), ]); let result; #[block] { - result = runtime.bench_code_hash(memory.as_mut_slice(), 4 + len, 4, 0); + result = runtime.bench_code_hash(memory.as_mut_slice(), len, 0); } assert_ok!(result); assert_eq!( - ::decode(&mut &memory[4..]).unwrap(), + ::decode(&mut &memory[..]).unwrap(), contract.info().unwrap().code_hash ); } @@ -585,16 +585,16 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn seal_own_code_hash() { let len = ::max_encoded_len() as u32; - build_runtime!(runtime, contract, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, contract, memory: [vec![0u8; len as _], ]); let result; #[block] { - result = runtime.bench_own_code_hash(memory.as_mut_slice(), 4, 0); + result = runtime.bench_own_code_hash(memory.as_mut_slice(), 0); } assert_ok!(result); assert_eq!( - ::decode(&mut &memory[4..]).unwrap(), + ::decode(&mut &memory[..]).unwrap(), contract.info().unwrap().code_hash ); } @@ -629,15 +629,15 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn seal_address() { let len = H160::len_bytes(); - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [vec![0u8; len as _], ]); let result; #[block] { - result = runtime.bench_address(memory.as_mut_slice(), 4, 0); + result = runtime.bench_address(memory.as_mut_slice(), 0); } assert_ok!(result); - assert_eq!(::decode(&mut &memory[4..]).unwrap(), runtime.ext().address()); + assert_eq!(::decode(&mut &memory[..]).unwrap(), runtime.ext().address()); } #[benchmark(pov_mode = Measured)] @@ -1581,11 +1581,9 @@ mod benchmarks { offset(value_len), // input_data_ptr i, // input_data_len SENTINEL, // address_ptr - 0, // address_len_ptr SENTINEL, // output_ptr 0, // output_len_ptr offset(i), // salt_ptr - 32, // salt_len ); } diff --git a/substrate/frame/revive/src/debug.rs b/substrate/frame/revive/src/debug.rs index 00e893b94f86..d1fc0823e03d 100644 --- a/substrate/frame/revive/src/debug.rs +++ b/substrate/frame/revive/src/debug.rs @@ -64,7 +64,7 @@ impl Tracing for () { type CallSpan = (); fn new_call_span(contract_address: &H160, entry_point: ExportedFunction, input_data: &[u8]) { - log::trace!(target: LOG_TARGET, "call {entry_point:?} account: {contract_address:?}, input_data: {input_data:?}") + log::trace!(target: LOG_TARGET, "call {entry_point:?} address: {contract_address:?}, input_data: {input_data:?}") } } diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 9a99b01776c1..393acc8c9852 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -824,7 +824,7 @@ pub mod pallet { /// must be supplied. #[pallet::call_index(1)] #[pallet::weight( - T::WeightInfo::instantiate(data.len() as u32, 32).saturating_add(*gas_limit) + T::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit) )] pub fn instantiate( origin: OriginFor, @@ -855,7 +855,7 @@ pub mod pallet { dispatch_result( output.result.map(|result| result.result), output.gas_consumed, - T::WeightInfo::instantiate(data_len, 32), + T::WeightInfo::instantiate(data_len), ) } @@ -888,7 +888,7 @@ pub mod pallet { /// - The `deploy` function is executed in the context of the newly-created account. #[pallet::call_index(2)] #[pallet::weight( - T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32, 32) + T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32) .saturating_add(*gas_limit) )] pub fn instantiate_with_code( @@ -921,7 +921,7 @@ pub mod pallet { dispatch_result( output.result.map(|result| result.result), output.gas_consumed, - T::WeightInfo::instantiate_with_code(code_len, data_len, 32), + T::WeightInfo::instantiate_with_code(code_len, data_len), ) } diff --git a/substrate/frame/revive/src/storage.rs b/substrate/frame/revive/src/storage.rs index 91b7b904d2bb..ef7ce2db32cf 100644 --- a/substrate/frame/revive/src/storage.rs +++ b/substrate/frame/revive/src/storage.rs @@ -83,16 +83,16 @@ impl ContractInfo { /// This returns an `Err` if an contract with the supplied `account` already exists /// in storage. pub fn new( - account: &H160, + address: &H160, nonce: T::Nonce, code_hash: sp_core::H256, ) -> Result { - if >::contains_key(account) { + if >::contains_key(address) { return Err(Error::::DuplicateContract.into()) } let trie_id = { - let buf = ("bcontract_trie_v1", account, nonce).using_encoded(T::Hashing::hash); + let buf = ("bcontract_trie_v1", address, nonce).using_encoded(T::Hashing::hash); buf.as_ref() .to_vec() .try_into() diff --git a/substrate/frame/revive/src/tests/test_debug.rs b/substrate/frame/revive/src/tests/test_debug.rs index 7885d681e482..1e94d5cafb81 100644 --- a/substrate/frame/revive/src/tests/test_debug.rs +++ b/substrate/frame/revive/src/tests/test_debug.rs @@ -28,7 +28,7 @@ use std::cell::RefCell; #[derive(Clone, PartialEq, Eq, Debug)] struct DebugFrame { - contract_account: sp_core::H160, + contract_address: sp_core::H160, call: ExportedFunction, input: Vec, result: Option>, @@ -41,7 +41,7 @@ thread_local! { pub struct TestDebug; pub struct TestCallSpan { - contract_account: sp_core::H160, + contract_address: sp_core::H160, call: ExportedFunction, input: Vec, } @@ -50,20 +50,20 @@ impl Tracing for TestDebug { type CallSpan = TestCallSpan; fn new_call_span( - contract_account: &crate::H160, + contract_address: &crate::H160, entry_point: ExportedFunction, input_data: &[u8], ) -> TestCallSpan { DEBUG_EXECUTION_TRACE.with(|d| { d.borrow_mut().push(DebugFrame { - contract_account: *contract_account, + contract_address: *contract_address, call: entry_point, input: input_data.to_vec(), result: None, }) }); TestCallSpan { - contract_account: *contract_account, + contract_address: *contract_address, call: entry_point, input: input_data.to_vec(), } @@ -90,7 +90,7 @@ impl CallSpan for TestCallSpan { fn after_call(self, output: &ExecReturnValue) { DEBUG_EXECUTION_TRACE.with(|d| { d.borrow_mut().push(DebugFrame { - contract_account: self.contract_account, + contract_address: self.contract_address, call: self.call, input: self.input, result: Some(output.data.clone()), @@ -131,18 +131,18 @@ mod run_tests { .addr } - fn constructor_frame(contract_account: &H160, after: bool) -> DebugFrame { + fn constructor_frame(contract_address: &H160, after: bool) -> DebugFrame { DebugFrame { - contract_account: *contract_account, + contract_address: *contract_address, call: ExportedFunction::Constructor, input: vec![], result: if after { Some(vec![]) } else { None }, } } - fn call_frame(contract_account: &H160, args: Vec, after: bool) -> DebugFrame { + fn call_frame(contract_address: &H160, args: Vec, after: bool) -> DebugFrame { DebugFrame { - contract_account: *contract_account, + contract_address: *contract_address, call: ExportedFunction::Call, input: args, result: if after { Some(vec![]) } else { None }, diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 533baf8d2c82..51c723493847 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -453,7 +453,7 @@ impl Token for RuntimeCosts { DelegateCallBase => T::WeightInfo::seal_delegate_call(), CallTransferSurcharge => cost_args!(seal_call, 1, 0), CallInputCloned(len) => cost_args!(seal_call, 0, len), - Instantiate { input_data_len } => T::WeightInfo::seal_instantiate(input_data_len, 32), + Instantiate { input_data_len } => T::WeightInfo::seal_instantiate(input_data_len), HashSha256(len) => T::WeightInfo::seal_hash_sha2_256(len), HashKeccak256(len) => T::WeightInfo::seal_hash_keccak_256(len), HashBlake256(len) => T::WeightInfo::seal_hash_blake2_256(len), @@ -661,6 +661,27 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { memory.write(out_len_ptr, &buf_len.encode()) } + /// Same as `write_sandbox_output` but for static size output. + pub fn write_fixed_sandbox_output( + &mut self, + memory: &mut M, + out_ptr: u32, + buf: &[u8], + allow_skip: bool, + create_token: impl FnOnce(u32) -> Option, + ) -> Result<(), DispatchError> { + if allow_skip && out_ptr == SENTINEL { + return Ok(()) + } + + let buf_len = buf.len() as u32; + if let Some(costs) = create_token(buf_len) { + self.charge_gas(costs)?; + } + + memory.write(out_ptr, buf) + } + /// Computes the given hash function on the supplied input. /// /// Reads from the sandboxed input buffer into an intermediate buffer. @@ -1010,7 +1031,6 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { input_data_ptr: u32, input_data_len: u32, address_ptr: u32, - address_len_ptr: u32, output_ptr: u32, output_len_ptr: u32, salt_ptr: u32, @@ -1041,11 +1061,10 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { ); if let Ok((address, output)) = &instantiate_outcome { if !output.flags.contains(ReturnFlags::REVERT) { - self.write_sandbox_output( + self.write_fixed_sandbox_output( memory, address_ptr, - address_len_ptr, - &address.encode(), + &address.as_bytes(), true, already_charged, )?; @@ -1169,12 +1188,12 @@ pub mod env { fn transfer( &mut self, memory: &mut M, - account_ptr: u32, + address_ptr: u32, value_ptr: u32, ) -> Result { self.charge_gas(RuntimeCosts::Transfer)?; let mut callee = H160::zero(); - memory.read_into_buf(account_ptr, callee.as_bytes_mut())?; + memory.read_into_buf(address_ptr, callee.as_bytes_mut())?; let value: BalanceOf<::T> = memory.read_as(value_ptr)?; let result = self.ext.transfer(&callee, value); match result { @@ -1258,11 +1277,9 @@ pub mod env { input_data_ptr: u32, input_data_len: u32, address_ptr: u32, - address_len_ptr: u32, output_ptr: u32, output_len_ptr: u32, salt_ptr: u32, - _salt_len: u32, ) -> Result { self.instantiate( memory, @@ -1273,7 +1290,6 @@ pub mod env { input_data_ptr, input_data_len, address_ptr, - address_len_ptr, output_ptr, output_len_ptr, salt_ptr, @@ -1320,13 +1336,12 @@ pub mod env { /// Stores the address of the caller into the supplied buffer. /// See [`pallet_revive_uapi::HostFn::caller`]. #[api_version(0)] - fn caller(&mut self, memory: &mut M, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn caller(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::Caller)?; let caller = ::AddressMapper::to_address(self.ext.caller().account_id()?); - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, caller.as_bytes(), false, already_charged, @@ -1349,18 +1364,16 @@ pub mod env { fn code_hash( &mut self, memory: &mut M, - account_ptr: u32, + addr_ptr: u32, out_ptr: u32, - out_len_ptr: u32, ) -> Result { self.charge_gas(RuntimeCosts::CodeHash)?; let mut address = H160::zero(); - memory.read_into_buf(account_ptr, address.as_bytes_mut())?; + memory.read_into_buf(addr_ptr, address.as_bytes_mut())?; if let Some(value) = self.ext.code_hash(&address) { - self.write_sandbox_output( + self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, &value.encode(), false, already_charged, @@ -1374,18 +1387,12 @@ pub mod env { /// Retrieve the code hash of the currently executing contract. /// See [`pallet_revive_uapi::HostFn::own_code_hash`]. #[api_version(0)] - fn own_code_hash( - &mut self, - memory: &mut M, - out_ptr: u32, - out_len_ptr: u32, - ) -> Result<(), TrapReason> { + fn own_code_hash(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::OwnCodeHash)?; let code_hash_encoded = &self.ext.own_code_hash().encode(); - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, code_hash_encoded, false, already_charged, @@ -1411,18 +1418,12 @@ pub mod env { /// Stores the address of the current contract into the supplied buffer. /// See [`pallet_revive_uapi::HostFn::address`]. #[api_version(0)] - fn address( - &mut self, - memory: &mut M, - out_ptr: u32, - out_len_ptr: u32, - ) -> Result<(), TrapReason> { + fn address(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::Address)?; let address = self.ext.address(); - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, address.as_bytes(), false, already_charged, diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index 6a0d27529d87..7974cc1260e4 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -65,8 +65,8 @@ pub trait WeightInfo { fn on_runtime_upgrade_in_progress() -> Weight; fn on_runtime_upgrade() -> Weight; fn call_with_code_per_byte(c: u32, ) -> Weight; - fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight; - fn instantiate(i: u32, s: u32, ) -> Weight; + fn instantiate_with_code(c: u32, i: u32) -> Weight; + fn instantiate(i: u32) -> Weight; fn call() -> Weight; fn upload_code_determinism_enforced(c: u32, ) -> Weight; fn upload_code_determinism_relaxed(c: u32, ) -> Weight; @@ -115,7 +115,7 @@ pub trait WeightInfo { fn seal_transfer() -> Weight; fn seal_call(t: u32, i: u32, ) -> Weight; fn seal_delegate_call() -> Weight; - fn seal_instantiate(i: u32, s: u32, ) -> Weight; + fn seal_instantiate(i: u32) -> Weight; fn seal_hash_sha2_256(n: u32, ) -> Weight; fn seal_hash_keccak_256(n: u32, ) -> Weight; fn seal_hash_blake2_256(n: u32, ) -> Weight; @@ -382,7 +382,7 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 125952]`. /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. - fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { + fn instantiate_with_code(c: u32, i: u32) -> Weight { // Proof Size summary in bytes: // Measured: `323` // Estimated: `6262` @@ -393,7 +393,6 @@ impl WeightInfo for SubstrateWeight { // Standard Error: 22 .saturating_add(Weight::from_parts(2_143, 0).saturating_mul(i.into())) // Standard Error: 22 - .saturating_add(Weight::from_parts(2_210, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -415,7 +414,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. - fn instantiate(i: u32, s: u32, ) -> Weight { + fn instantiate(i: u32) -> Weight { // Proof Size summary in bytes: // Measured: `560` // Estimated: `4017` @@ -424,7 +423,6 @@ impl WeightInfo for SubstrateWeight { // Standard Error: 32 .saturating_add(Weight::from_parts(934, 0).saturating_mul(i.into())) // Standard Error: 32 - .saturating_add(Weight::from_parts(920, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -978,7 +976,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// The range of component `i` is `[0, 983040]`. /// The range of component `s` is `[0, 983040]`. - fn seal_instantiate(i: u32, s: u32, ) -> Weight { + fn seal_instantiate(i: u32) -> Weight { // Proof Size summary in bytes: // Measured: `676` // Estimated: `4132` @@ -987,7 +985,6 @@ impl WeightInfo for SubstrateWeight { // Standard Error: 24 .saturating_add(Weight::from_parts(581, 0).saturating_mul(i.into())) // Standard Error: 24 - .saturating_add(Weight::from_parts(915, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1375,7 +1372,7 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 125952]`. /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. - fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { + fn instantiate_with_code(c: u32, i: u32) -> Weight { // Proof Size summary in bytes: // Measured: `323` // Estimated: `6262` @@ -1386,7 +1383,6 @@ impl WeightInfo for () { // Standard Error: 22 .saturating_add(Weight::from_parts(2_143, 0).saturating_mul(i.into())) // Standard Error: 22 - .saturating_add(Weight::from_parts(2_210, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -1408,7 +1404,7 @@ impl WeightInfo for () { /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. - fn instantiate(i: u32, s: u32, ) -> Weight { + fn instantiate(i: u32) -> Weight { // Proof Size summary in bytes: // Measured: `560` // Estimated: `4017` @@ -1417,7 +1413,6 @@ impl WeightInfo for () { // Standard Error: 32 .saturating_add(Weight::from_parts(934, 0).saturating_mul(i.into())) // Standard Error: 32 - .saturating_add(Weight::from_parts(920, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1971,7 +1966,7 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// The range of component `i` is `[0, 983040]`. /// The range of component `s` is `[0, 983040]`. - fn seal_instantiate(i: u32, s: u32, ) -> Weight { + fn seal_instantiate(i: u32) -> Weight { // Proof Size summary in bytes: // Measured: `676` // Estimated: `4132` @@ -1980,7 +1975,6 @@ impl WeightInfo for () { // Standard Error: 24 .saturating_add(Weight::from_parts(581, 0).saturating_mul(i.into())) // Standard Error: 24 - .saturating_add(Weight::from_parts(915, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index 6eb662363f7c..f52ea9574025 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -40,12 +40,10 @@ pub enum HostFnImpl {} pub trait HostFn: private::Sealed { /// Stores the address of the current contract into the supplied buffer. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the address. - fn address(output: &mut &mut [u8]); + fn address(output: &mut [u8; 20]); /// Lock a new delegate dependency to the contract. /// @@ -56,7 +54,7 @@ pub trait HostFn: private::Sealed { /// /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps /// otherwise. - fn lock_delegate_dependency(code_hash: &[u8]); + fn lock_delegate_dependency(code_hash: &[u8; 32]); /// Stores the *free* balance of the current account into the supplied buffer. /// @@ -105,7 +103,7 @@ pub trait HostFn: private::Sealed { /// - [NotCallable][`crate::ReturnErrorCode::NotCallable] fn call( flags: CallFlags, - callee: &[u8], + callee: &[u8; 20], ref_time_limit: u64, proof_size_limit: u64, deposit: Option<&[u8]>, @@ -166,8 +164,6 @@ pub trait HostFn: private::Sealed { /// Stores the address of the caller into the supplied buffer. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// /// If this is a top-level call (i.e. initiated by an extrinsic) the origin address of the /// extrinsic will be returned. Otherwise, if this call is initiated by another contract then /// the address of the contract will be returned. @@ -178,7 +174,7 @@ pub trait HostFn: private::Sealed { /// # Parameters /// /// - `output`: A reference to the output data buffer to write the caller address. - fn caller(output: &mut &mut [u8]); + fn caller(output: &mut [u8; 20]); /// Checks whether the caller of the current contract is the origin of the whole call stack. /// @@ -216,15 +212,13 @@ pub trait HostFn: private::Sealed { /// /// # Parameters /// - /// - `account_id`: The address of the contract.Should be decodable as an `T::AccountId`. Traps - /// otherwise. + /// - `addr`: The address of the contract. /// - `output`: A reference to the output data buffer to write the code hash. /// - /// /// # Errors /// /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] - fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result; + fn code_hash(addr: &[u8; 20], output: &mut [u8; 32]) -> Result; /// Checks whether there is a value stored under the given key. /// @@ -281,7 +275,7 @@ pub trait HostFn: private::Sealed { /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] fn delegate_call( flags: CallFlags, - code_hash: &[u8], + code_hash: &[u8; 32], input_data: &[u8], output: Option<&mut &mut [u8]>, ) -> Result; @@ -405,28 +399,27 @@ pub trait HostFn: private::Sealed { /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] fn instantiate( - code_hash: &[u8], + code_hash: &[u8; 32], ref_time_limit: u64, proof_size_limit: u64, deposit: Option<&[u8]>, value: &[u8], input: &[u8], - address: Option<&mut &mut [u8]>, + address: Option<&mut [u8; 20]>, output: Option<&mut &mut [u8]>, - salt: &[u8], + salt: &[u8; 32], ) -> Result; /// Checks whether a specified address belongs to a contract. /// /// # Parameters /// - /// - `account_id`: The address to check. Should be decodable as an `T::AccountId`. Traps - /// otherwise. + /// - `address`: The address to check /// /// # Return /// /// Returns `true` if the address belongs to a contract. - fn is_contract(account_id: &[u8]) -> bool; + fn is_contract(address: &[u8; 20]) -> bool; /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. /// The data is encoded as `T::Balance`. @@ -443,7 +436,7 @@ pub trait HostFn: private::Sealed { /// # Parameters /// /// - `output`: A reference to the output data buffer to write the code hash. - fn own_code_hash(output: &mut [u8]); + fn own_code_hash(output: &mut [u8; 32]); /// Load the latest block timestamp into the supplied buffer /// @@ -462,7 +455,7 @@ pub trait HostFn: private::Sealed { /// /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps /// otherwise. - fn unlock_delegate_dependency(code_hash: &[u8]); + fn unlock_delegate_dependency(code_hash: &[u8; 32]); /// Cease contract execution and save a data buffer as a result of the execution. /// @@ -510,7 +503,7 @@ pub trait HostFn: private::Sealed { /// # Errors /// /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] - fn set_code_hash(code_hash: &[u8]) -> Result; + fn set_code_hash(code_hash: &[u8; 32]) -> Result; /// Set the value at the given key in the contract storage. /// @@ -554,14 +547,13 @@ pub trait HostFn: private::Sealed { /// /// # Parameters /// - /// - `account_id`: The address of the account to transfer funds to. Should be decodable as an - /// `T::AccountId`. Traps otherwise. + /// - `address`: The address of the account to transfer funds to. /// - `value`: The value to transfer. Should be decodable as a `T::Balance`. Traps otherwise. /// /// # Errors /// /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] - fn transfer(account_id: &[u8], value: &[u8]) -> Result; + fn transfer(address: &[u8; 20], value: &[u8]) -> Result; /// Remove the calling account and transfer remaining **free** balance. /// @@ -571,15 +563,14 @@ pub trait HostFn: private::Sealed { /// /// # Parameters /// - /// - `beneficiary`: The address of the beneficiary account, Should be decodable as an - /// `T::AccountId`. + /// - `beneficiary`: The address of the beneficiary account /// /// # Traps /// /// - The contract is live i.e is already on the call stack. /// - Failed to send the balance to the beneficiary. /// - The deletion queue is full. - fn terminate(beneficiary: &[u8]) -> !; + fn terminate(beneficiary: &[u8; 20]) -> !; /// Stores the value transferred along with this call/instantiate into the supplied buffer. /// The data is encoded as `T::Balance`. diff --git a/substrate/frame/revive/uapi/src/host/riscv32.rs b/substrate/frame/revive/uapi/src/host/riscv32.rs index 0b7130015f13..c8218bb8f737 100644 --- a/substrate/frame/revive/uapi/src/host/riscv32.rs +++ b/substrate/frame/revive/uapi/src/host/riscv32.rs @@ -58,7 +58,7 @@ mod sys { out_ptr: *mut u8, out_len_ptr: *mut u32, ) -> ReturnCode; - pub fn transfer(account_ptr: *const u8, value_ptr: *const u8) -> ReturnCode; + pub fn transfer(address_ptr: *const u8, value_ptr: *const u8) -> ReturnCode; pub fn call(ptr: *const u8) -> ReturnCode; pub fn delegate_call( flags: u32, @@ -72,17 +72,13 @@ mod sys { pub fn terminate(beneficiary_ptr: *const u8); pub fn input(out_ptr: *mut u8, out_len_ptr: *mut u32); pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32); - pub fn caller(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn caller(out_ptr: *mut u8); pub fn is_contract(account_ptr: *const u8) -> ReturnCode; - pub fn code_hash( - account_ptr: *const u8, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> ReturnCode; - pub fn own_code_hash(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn code_hash(address_ptr: *const u8, out_ptr: *mut u8) -> ReturnCode; + pub fn own_code_hash(out_ptr: *mut u8); pub fn caller_is_origin() -> ReturnCode; pub fn caller_is_root() -> ReturnCode; - pub fn address(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn address(out_ptr: *mut u8); pub fn weight_to_fee( ref_time: u64, proof_size: u64, @@ -198,17 +194,20 @@ fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { impl HostFn for HostFnImpl { fn instantiate( - code_hash: &[u8], + code_hash: &[u8; 32], ref_time_limit: u64, proof_size_limit: u64, deposit_limit: Option<&[u8]>, value: &[u8], input: &[u8], - mut address: Option<&mut &mut [u8]>, + mut address: Option<&mut [u8; 20]>, mut output: Option<&mut &mut [u8]>, - salt: &[u8], + salt: &[u8; 32], ) -> Result { - let (address_ptr, mut address_len) = ptr_len_or_sentinel(&mut address); + let address = match address { + Some(ref mut data) => data.as_mut_ptr(), + None => crate::SENTINEL as _, + }; let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); let deposit_limit_ptr = ptr_or_sentinel(&deposit_limit); #[repr(packed)] @@ -220,13 +219,11 @@ impl HostFn for HostFnImpl { deposit_limit: *const u8, value: *const u8, input: *const u8, - input_len: usize, + input_len: u32, address: *const u8, - address_len: *mut u32, output: *mut u8, output_len: *mut u32, salt: *const u8, - salt_len: usize, } let args = Args { code_hash: code_hash.as_ptr(), @@ -235,21 +232,15 @@ impl HostFn for HostFnImpl { deposit_limit: deposit_limit_ptr, value: value.as_ptr(), input: input.as_ptr(), - input_len: input.len(), - address: address_ptr, - address_len: &mut address_len as *mut _, + input_len: input.len() as _, + address, output: output_ptr, output_len: &mut output_len as *mut _, salt: salt.as_ptr(), - salt_len: salt.len(), }; let ret_code = { unsafe { sys::instantiate(&args as *const Args as *const _) } }; - if let Some(ref mut address) = address { - extract_from_slice(address, address_len as usize); - } - if let Some(ref mut output) = output { extract_from_slice(output, output_len as usize); } @@ -259,7 +250,7 @@ impl HostFn for HostFnImpl { fn call( flags: CallFlags, - callee: &[u8], + callee: &[u8; 20], ref_time_limit: u64, proof_size_limit: u64, deposit_limit: Option<&[u8]>, @@ -279,7 +270,7 @@ impl HostFn for HostFnImpl { deposit_limit: *const u8, value: *const u8, input: *const u8, - input_len: usize, + input_len: u32, output: *mut u8, output_len: *mut u32, } @@ -291,7 +282,7 @@ impl HostFn for HostFnImpl { deposit_limit: deposit_limit_ptr, value: value.as_ptr(), input: input.as_ptr(), - input_len: input.len(), + input_len: input.len() as _, output: output_ptr, output_len: &mut output_len as *mut _, }; @@ -311,7 +302,7 @@ impl HostFn for HostFnImpl { fn delegate_call( flags: CallFlags, - code_hash: &[u8], + code_hash: &[u8; 32], input: &[u8], mut output: Option<&mut &mut [u8]>, ) -> Result { @@ -336,8 +327,8 @@ impl HostFn for HostFnImpl { ret_code.into() } - fn transfer(account_id: &[u8], value: &[u8]) -> Result { - let ret_code = unsafe { sys::transfer(account_id.as_ptr(), value.as_ptr()) }; + fn transfer(address: &[u8; 20], value: &[u8]) -> Result { + let ret_code = unsafe { sys::transfer(address.as_ptr(), value.as_ptr()) }; ret_code.into() } @@ -415,7 +406,7 @@ impl HostFn for HostFnImpl { ret_code.into() } - fn terminate(beneficiary: &[u8]) -> ! { + fn terminate(beneficiary: &[u8; 20]) -> ! { unsafe { sys::terminate(beneficiary.as_ptr()) } panic!("terminate does not return"); } @@ -458,8 +449,16 @@ impl HostFn for HostFnImpl { ret_code.into() } + fn address(output: &mut [u8; 20]) { + unsafe { sys::address(output.as_mut_ptr()) } + } + + fn caller(output: &mut [u8; 20]) { + unsafe { sys::caller(output.as_mut_ptr()) } + } + impl_wrapper_for! { - caller, block_number, address, balance, + block_number, balance, value_transferred,now, minimum_balance, weight_left, } @@ -512,8 +511,8 @@ impl HostFn for HostFnImpl { ret_code.into() } - fn is_contract(account_id: &[u8]) -> bool { - let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) }; + fn is_contract(address: &[u8; 20]) -> bool { + let ret_val = unsafe { sys::is_contract(address.as_ptr()) }; ret_val.into_bool() } @@ -522,28 +521,25 @@ impl HostFn for HostFnImpl { ret_val.into_bool() } - fn set_code_hash(code_hash: &[u8]) -> Result { + fn set_code_hash(code_hash: &[u8; 32]) -> Result { let ret_val = unsafe { sys::set_code_hash(code_hash.as_ptr()) }; ret_val.into() } - fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_val = - unsafe { sys::code_hash(account_id.as_ptr(), output.as_mut_ptr(), &mut output_len) }; + fn code_hash(address: &[u8; 20], output: &mut [u8; 32]) -> Result { + let ret_val = unsafe { sys::code_hash(address.as_ptr(), output.as_mut_ptr()) }; ret_val.into() } - fn own_code_hash(output: &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) } + fn own_code_hash(output: &mut [u8; 32]) { + unsafe { sys::own_code_hash(output.as_mut_ptr()) } } - fn lock_delegate_dependency(code_hash: &[u8]) { + fn lock_delegate_dependency(code_hash: &[u8; 32]) { unsafe { sys::lock_delegate_dependency(code_hash.as_ptr()) } } - fn unlock_delegate_dependency(code_hash: &[u8]) { + fn unlock_delegate_dependency(code_hash: &[u8; 32]) { unsafe { sys::unlock_delegate_dependency(code_hash.as_ptr()) } } From 77cca934b5fe18f3f51e752e750a9badc01be0a6 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 16:29:29 +0800 Subject: [PATCH 269/382] Reanchor test --- .../primitives/core/src/location.rs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index 14776452ff16..4d29ef07733d 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -87,9 +87,10 @@ impl DescribeLocation for DescribeTokenTerminal { #[cfg(test)] mod tests { use crate::TokenIdOf; + use frame_support::assert_ok; use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, Junction::*, Location, NetworkId::*, - PalletInstance, Parachain, + GeneralIndex, GeneralKey, GlobalConsensus, InteriorLocation, Junction::*, Location, + NetworkId::*, PalletInstance, Parachain, Reanchorable, }; use xcm_executor::traits::ConvertLocation; @@ -209,4 +210,22 @@ mod tests { ); } } + + #[test] + fn test_reanchor_relay_token_from_different_consensus() { + let asset_id: Location = Location::new(2, [GlobalConsensus(Rococo)]); + let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); + let mut reanchored_asset = asset_id.clone(); + assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + assert_eq!( + reanchored_asset, + Location { parents: 1, interior: [GlobalConsensus(Rococo)].into() } + ); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); + let mut reanchored_asset = reanchored_asset.clone(); + assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); + assert_eq!(reanchored_asset, asset_id); + } } From 5e0ec3e0231b43392f569cc4c685b91cb481a3ec Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Thu, 5 Sep 2024 10:47:15 +0200 Subject: [PATCH 270/382] Update and test the `getting-started` script (#5446) Here are some changes to the `getting-started.sh` scripts we have advertised on top of the readme. ### Changes to the script 1. Change `echo` to a more portable `printf`. On my machine, the script printed a literal `\n` string if run with `bash`. If I changed it to `echo -e`, then it printed a literal `-e` if run with `sh`. Changed it to `printf` which is more portable. --- 2. Template selection The script proceeded to clone and build the `minimal` template, which is not always what we want. Added a selection prompt where the user can select one of the 3 templates, and choose if it should be built&run or not. The user can also select no template at all - that way, we have a starter of a dependencies-installation script. --- 3. Added some missing dependencies for some of the systems. ### A workflow testing the script I propose a workflow, that will test the script using the [expect](https://core.tcl-lang.org/expect/index) tool. For each OS mentioned in the script (macOS, Ubuntu, Debian, Arch, Fedora, OpenSUSE) we go through the script twice, and after that build and run the template binary. I'm using docker containers, so we start from scratch and make sure the scripts installs all required dependencies - with the exception of macOS, which I can't run from scratch in a container. The jobs use a selected combination of OSes, shell interpreters (`bash` or `sh`), and templates. There is too much combinations to run them all, but I have [run it once](https://github.com/paritytech-stg/polkadot-sdk/actions/runs/10509533645) in staging to make sure all pass. I'm adding a cron schedule because it can break without any code changes in this repository (e.g. new `latest` release of a container). --------- Co-authored-by: Oliver Tale-Yazdi --- .github/workflows/check-getting-started.yml | 296 ++++++++++++++++++++ scripts/getting-started.sh | 110 +++++--- 2 files changed, 367 insertions(+), 39 deletions(-) create mode 100644 .github/workflows/check-getting-started.yml diff --git a/.github/workflows/check-getting-started.yml b/.github/workflows/check-getting-started.yml new file mode 100644 index 000000000000..b43db33c63bf --- /dev/null +++ b/.github/workflows/check-getting-started.yml @@ -0,0 +1,296 @@ +name: Check the getting-started.sh script + +# This workflow aims to make sure that the `getting-started.sh` script +# is functional and allows to build the templates +# on different operating systems. +# +# There are two jobs inside. +# One for systems that can run in a docker container, and one for macOS. +# +# Each job consists of: +# 1. Some necessary prerequisites for the workflow itself. +# 2. A first pass of the script, which will install dependencies and clone a template. +# 3. A second pass of the script, to make sure the behaviour is as expected. +# 4. Building the template - making sure it's buildable and runnable. +# +# The script is interacted with using the `expect` tool, which is available on all relevant systems. +# The steps are not re-used between macOS and other systems, +# because they are very similar but a little different. +# Additionally, macOS does NOT start from scratch here - for example, we have homebrew already installed. +# +# There are many combinations of systems, shells and templates. +# We test a selected handful of combinations here. + +on: + pull_request: + paths: + - '.github/workflows/check-getting-started.yml' + - 'scripts/getting-started.sh' + schedule: + - cron: '0 5 * * *' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + check-getting-started: + strategy: + fail-fast: true + matrix: + include: + - name: ubuntu + container: ubuntu + template: minimal + shell: bash + - name: debian + container: debian + template: parachain + shell: sh + - name: arch + container: archlinux + template: solochain + shell: sh + - name: fedora + container: fedora + template: parachain + shell: sh + - name: opensuse + container: opensuse/tumbleweed + template: solochain + shell: sh + runs-on: arc-runners-polkadot-sdk-beefy + container: ${{ matrix.container }}:latest + steps: + # A minimal amount of prerequisites required before we can run the actual getting-started script, + # which will install the rest of requirements. + - name: Install ubuntu/debian prerequisites + run: apt update && apt install -y expect sudo git + if: contains(matrix.name, 'ubuntu') || contains(matrix.name, 'debian') + - name: Install arch prerequisites + run: pacman -Syu --needed --noconfirm expect sudo git + if: contains(matrix.name, 'arch') + - name: Install fedora prerequisites + run: dnf --assumeyes install expect sudo git + if: contains(matrix.name, 'fedora') + - name: Install opensuse prerequisites + run: zypper install --no-confirm expect sudo git + if: contains(matrix.name, 'opensuse') + + - name: Checkout + uses: actions/checkout@v4 + + - name: Set additional expect flags if necessary + run: | + # Add a debug flag to expect, if github is re-run with debug logging enabled. + [ "${{ runner.debug }}" = "1" ] && EXPECT_FLAGS="-d" || EXPECT_FLAGS="" + echo "EXPECT_FLAGS=${EXPECT_FLAGS}" >> $GITHUB_ENV + + - name: Check the first run of the script + run: | + expect $EXPECT_FLAGS -c ' + set timeout 240 + + spawn ${{ matrix.shell }} scripts/getting-started.sh + + expect_after { + timeout { puts stderr "Timed out on an expect"; exit 1 } + eof { puts stderr "EOF received on an expect"; exit 1 } + } + + expect -nocase "Detected ${{ matrix.name }}" + + expect "Rust is not installed. Install it?" { + send "y\r" + expect "Proceed with standard installation (default - just press enter)" { + send "\r" + expect "Rust is installed now" + } + } + + expect "Setup the Rust environment" { + send "y\r" + } + + expect "start with one of the templates" { + send "y\r" + } + + expect -re "(.)\\) ${{ matrix.template }} template" { + send "$expect_out(1,string)\r" + } + + expect "compile the node?" { + send "n\r" + } + + expect eof + ' + timeout-minutes: 15 + + - name: Check the second run of the script + run: | + expect $EXPECT_FLAGS -c ' + set timeout 120 + + spawn ${{ matrix.shell }} scripts/getting-started.sh + + expect_after { + timeout { puts stderr "Timed out on an expect"; exit 1 } + eof { puts stderr "EOF received on an expect"; exit 1 } + } + + expect "Rust already installed" {} + + expect "Setup the Rust environment" { + send "n\r" + } + + expect "start with one of the templates" { + send "y\r" + } + + expect -re "(.)\\) ${{ matrix.template }} template" { + send "$expect_out(1,string)\r" + expect "directory already exists" {} + } + + expect "compile the node?" { + send "n\r" + } + + expect eof + ' + timeout-minutes: 15 + + - name: Compile the node outside of the script + run: | + . "$HOME/.cargo/env" + cd ${{ matrix.template }}-template + cargo build --release + timeout-minutes: 120 + + - name: Check that the binary is executable + run: | + . "$HOME/.cargo/env" + cd ${{ matrix.template }}-template + cargo run --release -- --help + timeout-minutes: 5 + + check-getting-started-macos: + strategy: + fail-fast: true + matrix: + include: + - template: parachain + shell: sh + - template: solochain + shell: bash + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set additional expect flags if necessary + run: | + # Add a debug flag to expect, if github is re-run with debug logging enabled. + [ "${{ runner.debug }}" = "1" ] && EXPECT_FLAGS="-d" || EXPECT_FLAGS="" + echo "EXPECT_FLAGS=${EXPECT_FLAGS}" >> $GITHUB_ENV + + - name: Check the first run of the script + run: | + expect $EXPECT_FLAGS -c ' + set timeout 120 + + spawn ${{ matrix.shell }} scripts/getting-started.sh + + expect_after { + timeout { puts stderr "Timed out on an expect"; exit 1 } + eof { puts stderr "EOF received on an expect"; exit 1 } + } + + expect -nocase "Detected macOS" + + expect "Homebrew already installed" + + expect "Install cmake" { + send "y\r" + } + + expect "Rust already installed" {} + + expect "Setup the Rust environment" { + send "y\r" + } + + expect "start with one of the templates" { + send "y\r" + } + + expect -re "(.)\\) ${{ matrix.template }} template" { + send "$expect_out(1,string)\r" + } + + expect "compile the node?" { + send "n\r" + } + + expect eof + ' + timeout-minutes: 15 + + - name: Check the second run of the script + run: | + expect $EXPECT_FLAGS -c ' + set timeout 120 + + spawn ${{ matrix.shell }} scripts/getting-started.sh + + expect_after { + timeout { puts stderr "Timed out on an expect"; exit 1 } + eof { puts stderr "EOF received on an expect"; exit 1 } + } + + expect "Homebrew already installed" + + expect "Install cmake" { + send "y\r" + } + + expect "Rust already installed" {} + + expect "Setup the Rust environment" { + send "n\r" + } + + expect "start with one of the templates" { + send "y\r" + } + + expect -re "(.)\\) ${{ matrix.template }} template" { + send "$expect_out(1,string)\r" + expect "directory already exists" {} + } + + expect "compile the node?" { + send "n\r" + } + + expect eof + ' + timeout-minutes: 15 + + - name: Compile the node outside of the script + run: | + . "$HOME/.cargo/env" + cd ${{ matrix.template }}-template + cargo build --release + timeout-minutes: 120 + + - name: Check that the binary is executable + run: | + . "$HOME/.cargo/env" + cd ${{ matrix.template }}-template + cargo run --release -- --help + timeout-minutes: 5 diff --git a/scripts/getting-started.sh b/scripts/getting-started.sh index 57806280914b..d5fd545481d1 100755 --- a/scripts/getting-started.sh +++ b/scripts/getting-started.sh @@ -4,30 +4,41 @@ set -e prompt() { while true; do - echo "$1 [y/N]" + printf "$1 [y/N]\n" read yn case $yn in [Yy]* ) return 0;; # Yes, return 0 (true) [Nn]* ) return 1;; # No, return 1 (false) "" ) return 1;; # Default to no if user just presses Enter - * ) echo "Please answer yes or no.";; + * ) printf "Please answer yes or no.\n";; esac done } prompt_default_yes() { while true; do - echo "$1 [Y/n]" + printf "$1 [Y/n]\n" read yn case $yn in [Yy]* ) return 0;; # Yes, return 0 (true) [Nn]* ) return 1;; # No, return 1 (false) "" ) return 0;; # Default to yes if user just presses Enter - * ) echo "Please answer yes or no.";; + * ) printf "Please answer yes or no.\n";; esac done } +clone_and_enter_template() { + template="$1" # minimal, solochain, or parachain + if [ -d "${template}-template" ]; then + printf "\n✅︎ ${template}-template directory already exists. -> Entering.\n" + else + printf "\n↓ Let's grab the ${template} template from github.\n" + git clone --quiet https://github.com/paritytech/polkadot-sdk-${template}-template.git ${template}-template + fi + cd ${template}-template +} + cat </dev/null 2>&1; then - echo "\n✅︎🍺 Homebrew already installed." + printf "\n✅︎🍺 Homebrew already installed.\n" else - if prompt_default_yes "\n🍺 Homebrew is not installed. Install it?"; then - echo "🍺 Installing Homebrew." + if prompt_default_yes "\n🍺 Homebrew is not installed. Install it?\n"; then + printf "🍺 Installing Homebrew.\n" /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" else - echo "❌ Cannot continue without homebrew. Aborting." + printf "❌ Cannot continue without homebrew. Aborting.\n" exit 1 fi fi brew update if command -v git >/dev/null 2>&1; then - echo "\n✅︎🍺 git already installed." + printf "\n✅︎🍺 git already installed.\n" else - if prompt_default_yes "\n🍺 git seems to be missing but we will need it; install git?"; then + if prompt_default_yes "\n🍺 git seems to be missing but we will need it; install git?\n"; then brew install git else - echo "❌ Cannot continue without git. Aborting." + printf "❌ Cannot continue without git. Aborting.\n" exit 1 fi fi @@ -75,73 +86,94 @@ if [ "$os_name" = "Darwin" ]; then if prompt "\n🍺 Install cmake, openssl and protobuf?"; then brew install cmake openssl protobuf else - echo "🍺 Assuming cmake, openssl and protobuf are present." + printf "🍺 Assuming cmake, openssl and protobuf are present.\n" fi elif [ "$os_name" = "Linux" ]; then # find the distro name in the release files distro=$( cat /etc/*-release | tr '[:upper:]' '[:lower:]' | grep -Poi '(debian|ubuntu|arch|fedora|opensuse)' | uniq | head -n 1 ) if [ "$distro" = "ubuntu" ]; then - echo "\n🐧 Detected Ubuntu. Using apt to install dependencies." - sudo apt install --assume-yes git clang curl libssl-dev protobuf-compiler + printf "\n🐧 Detected Ubuntu. Using apt to install dependencies.\n" + sudo apt -qq update + sudo apt -qq install --assume-yes git clang curl libssl-dev protobuf-compiler make elif [ "$distro" = "debian" ]; then - echo "\n🐧 Detected Debian. Using apt to install dependencies." - sudo apt install --assume-yes git clang curl libssl-dev llvm libudev-dev make protobuf-compiler + printf "\n🐧 Detected Debian. Using apt to install dependencies.\n" + sudo apt -qq update + sudo apt -qq install --assume-yes git clang curl libssl-dev llvm libudev-dev make protobuf-compiler elif [ "$distro" = "arch" ]; then - echo "\n🐧 Detected Arch Linux. Using pacman to install dependencies." + printf "\n🐧 Detected Arch Linux. Using pacman to install dependencies.\n" pacman -Syu --needed --noconfirm curl git clang make protobuf elif [ "$distro" = "fedora" ]; then - echo "\n🐧 Detected Fedora. Using dnf to install dependencies." - sudo dnf update - sudo dnf install clang curl git openssl-devel make protobuf-compiler + printf "\n🐧 Detected Fedora. Using dnf to install dependencies.\n" + sudo dnf update --assumeyes + sudo dnf install --assumeyes clang curl git openssl-devel make protobuf-compiler perl elif [ "$distro" = "opensuse" ]; then - echo "\n🐧 Detected openSUSE. Using zypper to install dependencies." - sudo zypper install clang curl git openssl-devel llvm-devel libudev-devel make protobuf + printf "\n🐧 Detected openSUSE. Using zypper to install dependencies.\n" + sudo zypper install --no-confirm clang gcc gcc-c++ curl git openssl-devel llvm-devel libudev-devel make awk protobuf-devel else - if prompt "\n🐧 Unknown Linux distribution. Unable to install dependencies. Continue anyway?"; then - echo "\n🐧 Proceeding with unknown linux distribution..." + if prompt "\n🐧 Unknown Linux distribution. Unable to install dependencies. Continue anyway?\n"; then + printf "\n🐧 Proceeding with unknown linux distribution...\n" else exit 1 fi fi else - echo "❌ Unknown operating system. Aborting." + printf "❌ Unknown operating system. Aborting.\n" exit 1 fi # Check if rust is installed +[ -f "$HOME/.cargo/env" ] && . "$HOME/.cargo/env" if command -v rustc >/dev/null 2>&1; then - echo "\n✅︎🦀 Rust already installed." + printf "\n✅︎🦀 Rust already installed.\n" else if prompt_default_yes "\n🦀 Rust is not installed. Install it?"; then - echo "🦀 Installing via rustup." + printf "🦀 Installing via rustup.\n" curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + . "$HOME/.cargo/env" else - echo "Aborting." + printf "Aborting.\n" exit 1 fi fi # Ensure that we have wasm support if prompt_default_yes "\n🦀 Setup the Rust environment (e.g. WASM support)?"; then - echo "🦀 Setting up Rust environment." + printf "🦀 Setting up Rust environment.\n" rustup default stable rustup update rustup target add wasm32-unknown-unknown rustup component add rust-src fi -if [ -d "minimal-template" ]; then - echo "\n✅︎ minimal-template directory already exists. -> Entering." -else - echo "\n↓ Let's grab the minimal template from github." - git clone https://github.com/paritytech/polkadot-sdk-minimal-template.git minimal-template +if ! prompt "\nWould you like to start with one of the templates?"; then + printf "⚡ All done, the environment is ready for hacking.\n" + exit 0 +fi + +while true; do + printf "\nWhich template would you like to start with?\n" + printf "1) minimal template\n" + printf "2) parachain template\n" + printf "3) solochain template\n" + printf "q) cancel\n" + read -p "#? " template + case $template in + [1]* ) clone_and_enter_template minimal; break;; + [2]* ) clone_and_enter_template parachain; break;; + [3]* ) clone_and_enter_template solochain; break;; + [qQ]* ) printf "Canceling, not using a template.\n"; exit 0;; + * ) printf "Selection not recognized.\n";; + esac +done + +if ! prompt_default_yes "\n⚙️ Let's compile the node? It might take a while."; then + printf "⚡ Script finished, you can continue in the ${template}-template directory.\n" + exit 0 fi -cd minimal-template -echo "\n⚙️ Let's compile the node." cargo build --release -if prompt_default_yes "\n🚀 Everything ready to go, let's run the node?"; then +if prompt_default_yes "\n🚀 Everything ready to go, let's run the node?\n"; then cargo run --release -- --dev fi From ff870fe24fbeeda86641a361e4833e07bd889cd7 Mon Sep 17 00:00:00 2001 From: Ron Date: Thu, 5 Sep 2024 17:44:53 +0800 Subject: [PATCH 271/382] Update bridges/snowbridge/pallets/inbound-queue/src/lib.rs Co-authored-by: Francisco Aguirre --- bridges/snowbridge/pallets/inbound-queue/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs index 676cd8f3a373..423b92b9fae0 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs @@ -275,7 +275,7 @@ pub mod pallet { T::Token::transfer(&sovereign_account, &who, amount, Preservation::Preserve)?; } - // Decode payload into VersionMessage + // Decode payload into `VersionedMessage` let message = VersionedMessage::decode_all(&mut envelope.payload.as_ref()) .map_err(|_| Error::::InvalidPayload)?; From f3127cbd71ab6b47a72c51d33422710d5367c29f Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 17:49:42 +0800 Subject: [PATCH 272/382] Change ParaId to 1002 --- bridges/snowbridge/pallets/inbound-queue/src/mock.rs | 2 +- bridges/snowbridge/primitives/core/src/location.rs | 2 +- bridges/snowbridge/primitives/router/src/inbound/tests.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index e639dd8771f6..f5e44c2e46a8 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -113,7 +113,7 @@ parameter_types! { pub const InitialFund: u128 = 1_000_000_000_000; pub const InboundQueuePalletInstance: u8 = 80; pub UniversalLocation: InteriorLocation = - [GlobalConsensus(Westend), Parachain(1013)].into(); + [GlobalConsensus(Westend), Parachain(1002)].into(); pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]); } diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index 4d29ef07733d..ffdab463101f 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -222,7 +222,7 @@ mod tests { reanchored_asset, Location { parents: 1, interior: [GlobalConsensus(Rococo)].into() } ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); let mut reanchored_asset = reanchored_asset.clone(); assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index 282a16443fc8..ea1197cc38ea 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -50,7 +50,7 @@ fn test_reanchor_relay_token() { reanchored_asset, Location { parents: 1, interior: [GlobalConsensus(Westend)].into() } ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); let mut reanchored_asset = reanchored_asset.clone(); assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); @@ -78,7 +78,7 @@ fn test_reanchor_pna_from_ah() { .into() } ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); let mut reanchored_asset = reanchored_asset.clone(); assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); From 6a1b200c1277512ef93ea9e64957baea684fbe61 Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Thu, 5 Sep 2024 12:01:36 +0200 Subject: [PATCH 273/382] [ci] Move test-linux-stable to GHA (#5571) PR moves jobs `test-linux-stable`, `test-linux-stable-int` and `test-linux-stable-runtime-benchmarks` to github actions cc https://github.com/paritytech/ci_cd/issues/1006 --- .github/workflows/tests-linux-stable.yml | 52 +++++--- .gitlab-ci.yml | 21 ---- .gitlab/pipeline/test.yml | 151 ++--------------------- 3 files changed, 43 insertions(+), 181 deletions(-) diff --git a/.github/workflows/tests-linux-stable.yml b/.github/workflows/tests-linux-stable.yml index 4a13f5318f7d..997d7622f0c3 100644 --- a/.github/workflows/tests-linux-stable.yml +++ b/.github/workflows/tests-linux-stable.yml @@ -6,39 +6,51 @@ on: branches: - master pull_request: - types: [opened, synchronize, reopened, ready_for_review, labeled] + types: [opened, synchronize, reopened, ready_for_review] merge_group: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: - changes: - # TODO: remove once migration is complete or this workflow is fully stable - if: contains(github.event.label.name, 'GHA-migration') - permissions: - pull-requests: read - uses: ./.github/workflows/reusable-check-changed-files.yml + # changes: + # # TODO: remove once migration is complete or this workflow is fully stable + # if: contains(github.event.label.name, 'GHA-migration') + # permissions: + # pull-requests: read + # uses: ./.github/workflows/reusable-check-changed-files.yml set-image: # GitHub Actions allows using 'env' in a container context. # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 # This workaround sets the container image for each job using 'set-image' job output. - needs: changes - if: ${{ needs.changes.outputs.rust }} runs-on: ubuntu-latest outputs: IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} + OLDLINUXRUNNER: ${{ steps.set_runner.outputs.OLDLINUXRUNNER }} steps: - name: Checkout uses: actions/checkout@v4 - id: set_image run: cat .github/env >> $GITHUB_OUTPUT + # By default we use spot machines that can be terminated at any time. + # Merge queues use persistent runners to avoid kicking off from queue when the runner is terminated. + - id: set_runner + run: | + # Run merge queues on persistent runners + if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT + echo "OLDLINUXRUNNER=oldlinux-persistent" >> $GITHUB_OUTPUT + else + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + echo "OLDLINUXRUNNER=oldlinux" >> $GITHUB_OUTPUT + fi test-linux-stable-int: - needs: [set-image, changes] - if: ${{ needs.changes.outputs.rust }} - runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image] + # if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.set-image.outputs.RUNNER }} timeout-minutes: 60 container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -57,9 +69,9 @@ jobs: # https://github.com/paritytech/ci_cd/issues/864 test-linux-stable-runtime-benchmarks: - needs: [set-image, changes] - if: ${{ needs.changes.outputs.rust }} - runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image] + # if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.set-image.outputs.RUNNER }} timeout-minutes: 60 container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -75,15 +87,19 @@ jobs: run: forklift cargo nextest run --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet test-linux-stable: - needs: [set-image, changes] - if: ${{ needs.changes.outputs.rust }} + needs: [set-image] + # if: ${{ needs.changes.outputs.rust }} runs-on: ${{ matrix.runners }} timeout-minutes: 60 strategy: fail-fast: false matrix: partition: [1/3, 2/3, 3/3] - runners: [arc-runners-polkadot-sdk-beefy, oldlinux] + runners: + [ + "${{ needs.set-image.outputs.RUNNER }}", + "${{ needs.set-image.outputs.OLDLINUXRUNNER }}", + ] container: image: ${{ needs.set-image.outputs.IMAGE }} # needed for tests that use unshare syscall diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 56d426218c80..5b581c45fb85 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -268,22 +268,6 @@ remove-cancel-pipeline-message: PR_NUM: "${CI_COMMIT_REF_NAME}" trigger: project: "parity/infrastructure/ci_cd/pipeline-stopper" -# need to copy jobs this way because otherwise gitlab will wait -# for all 3 jobs to finish instead of cancelling if one fails -cancel-pipeline-test-linux-stable1: - extends: .cancel-pipeline-template - needs: - - job: "test-linux-stable 1/3" - -cancel-pipeline-test-linux-stable2: - extends: .cancel-pipeline-template - needs: - - job: "test-linux-stable 2/3" - -cancel-pipeline-test-linux-stable3: - extends: .cancel-pipeline-template - needs: - - job: "test-linux-stable 3/3" cancel-pipeline-cargo-check-benches1: extends: .cancel-pipeline-template @@ -295,11 +279,6 @@ cancel-pipeline-cargo-check-benches2: needs: - job: "cargo-check-benches 2/2" -cancel-pipeline-test-linux-stable-int: - extends: .cancel-pipeline-template - needs: - - job: test-linux-stable-int - cancel-pipeline-cargo-check-each-crate-1: extends: .cancel-pipeline-template needs: diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index 83af0e6b7b20..ca3a2394fb39 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -35,7 +35,7 @@ codecov-start: - .pipeline-stopper-artifacts - .run-immediately script: - - !reference [ .codecov-check, script ] + - !reference [.codecov-check, script] - > if [ "$CI_COMMIT_REF_NAME" != "master" ]; then codecovcli -v create-commit -t ${CODECOV_TOKEN} -r paritytech/polkadot-sdk --commit-sha ${CI_COMMIT_SHA} --fail-on-error --pr ${CI_COMMIT_REF_NAME} --git-service github; @@ -57,7 +57,7 @@ codecov-finish: needs: - test-linux-stable-codecov script: - - !reference [ .codecov-check, script ] + - !reference [.codecov-check, script] - codecovcli -v create-report-results -t ${CODECOV_TOKEN} -r paritytech/polkadot-sdk --commit-sha ${CI_COMMIT_SHA} --git-service github - codecovcli -v get-report-results -t ${CODECOV_TOKEN} -r paritytech/polkadot-sdk --commit-sha ${CI_COMMIT_SHA} --git-service github - codecovcli -v send-notifications -t ${CODECOV_TOKEN} -r paritytech/polkadot-sdk --commit-sha ${CI_COMMIT_SHA} --git-service github @@ -83,7 +83,7 @@ test-linux-stable-codecov: parallel: 2 script: # tools - - !reference [ .codecov-check, script ] + - !reference [.codecov-check, script] - rustup component add llvm-tools-preview - mkdir -p target/coverage/result/ # Place real test call here @@ -110,119 +110,6 @@ test-linux-stable-codecov: codecovcli -v do-upload -f target/coverage/result/report-${CI_NODE_INDEX}.lcov --disable-search -t ${CODECOV_TOKEN} -r paritytech/polkadot-sdk --commit-sha ${CI_COMMIT_SHA} --fail-on-error --git-service github; fi -test-linux-stable: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - - .pipeline-stopper-artifacts - variables: - RUST_TOOLCHAIN: stable - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - parallel: 3 - script: - # Build all but only execute 'runtime' tests. - - echo "Node index - ${CI_NODE_INDEX}. Total amount - ${CI_NODE_TOTAL}" - - > - time cargo nextest run \ - --workspace \ - --locked \ - --release \ - --no-fail-fast \ - --features try-runtime,experimental,riscv,ci-only-tests \ - --partition count:${CI_NODE_INDEX}/${CI_NODE_TOTAL} - # Upload tests results to Elasticsearch - - echo "Upload test results to Elasticsearch" - - cat target/nextest/default/junit.xml | xq . > target/nextest/default/junit.json - - > - curl -v -XPOST --http1.1 \ - -u ${ELASTIC_USERNAME}:${ELASTIC_PASSWORD} \ - https://elasticsearch.parity-build.parity.io/unit-tests/_doc/${CI_JOB_ID} \ - -H 'Content-Type: application/json' \ - -d @target/nextest/default/junit.json || echo "failed to upload junit report" - # run runtime-api tests with `enable-staging-api` feature on the 1st node - - if [ ${CI_NODE_INDEX} == 1 ]; then time cargo nextest run -p sp-api-test --features enable-staging-api; fi - artifacts: - when: always - paths: - - target/nextest/default/junit.xml - reports: - junit: target/nextest/default/junit.xml - timeout: 90m - -test-linux-oldkernel-stable: - extends: test-linux-stable - tags: - - oldkernel-vm - -# https://github.com/paritytech/ci_cd/issues/864 -test-linux-stable-runtime-benchmarks: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - - .pipeline-stopper-artifacts - variables: - RUST_TOOLCHAIN: stable - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - script: - - time cargo nextest run --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet - -# can be used to run all tests -# test-linux-stable-all: -# stage: test -# extends: -# - .docker-env -# - .common-refs -# - .run-immediately -# variables: -# RUST_TOOLCHAIN: stable -# # Enable debug assertions since we are running optimized builds for testing -# # but still want to have debug assertions. -# RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" -# parallel: 3 -# script: -# # Build all but only execute 'runtime' tests. -# - echo "Node index - ${CI_NODE_INDEX}. Total amount - ${CI_NODE_TOTAL}" -# - > -# time cargo nextest run \ -# --workspace \ -# --locked \ -# --release \ -# --no-fail-fast \ -# --features runtime-benchmarks,try-runtime \ -# --partition count:${CI_NODE_INDEX}/${CI_NODE_TOTAL} -# # todo: add flacky-test collector - -# takes about 1,5h without cache -# can be used to check that nextest works correctly -# test-linux-stable-polkadot: -# stage: test -# timeout: 2h -# extends: -# - .docker-env -# - .common-refs -# - .run-immediately -# - .collect-artifacts-short -# variables: -# RUST_TOOLCHAIN: stable -# # Enable debug assertions since we are running optimized builds for testing -# # but still want to have debug assertions. -# RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" -# script: -# - mkdir -p artifacts -# - time cargo test --workspace -# --locked -# --profile testnet -# --features=runtime-benchmarks,runtime-metrics,try-runtime -- -# --skip upgrade_version_checks_should_work - test-doc: stage: test extends: @@ -316,7 +203,7 @@ cargo-check-benches: git merge --verbose --no-edit FETCH_HEAD; fi fi' - - !reference [ .forklift-cache, before_script ] + - !reference [.forklift-cache, before_script] parallel: 2 script: - mkdir -p ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA @@ -360,7 +247,7 @@ node-bench-regression-guard: artifacts: true variables: CI_IMAGE: "paritytech/node-bench-regression-guard:latest" - before_script: [ "" ] + before_script: [""] script: - if [ $(ls -la artifacts/benches/ | grep master | wc -l) == 0 ]; then echo "Couldn't find master artifacts"; @@ -371,7 +258,7 @@ node-bench-regression-guard: - echo "In case of this job failure, check your pipeline's cargo-check-benches" - "node-bench-regression-guard --reference artifacts/benches/master-* --compare-with artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - after_script: [ "" ] + after_script: [""] # if this fails run `bot update-ui` in the Pull Request or "./scripts/update-ui-tests.sh" locally # see ./docs/contributor/CONTRIBUTING.md#ui-tests @@ -443,26 +330,6 @@ test-frame-examples-compile-to-wasm: # FIXME allow_failure: true -test-linux-stable-int: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - - .pipeline-stopper-artifacts - variables: - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-C debug-assertions -D warnings" - RUST_BACKTRACE: 1 - WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" - # Ensure we run the UI tests. - RUN_UI_TESTS: 1 - script: - - WASM_BUILD_NO_COLOR=1 - time cargo test -p staging-node-cli --release --locked -- --ignored - # more information about this job can be found here: # https://github.com/paritytech/substrate/pull/6916 check-tracing: @@ -533,9 +400,9 @@ cargo-check-each-crate-macos: # - .collect-artifacts before_script: # skip timestamp script, the osx bash doesn't support printf %()T - - !reference [ .job-switcher, before_script ] - - !reference [ .rust-info-script, script ] - - !reference [ .pipeline-stopper-vars, script ] + - !reference [.job-switcher, before_script] + - !reference [.rust-info-script, script] + - !reference [.pipeline-stopper-vars, script] variables: SKIP_WASM_BUILD: 1 script: From 34c655ba7bf8192b417f76e19be3d7d2435a3fcf Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 18:14:26 +0800 Subject: [PATCH 274/382] Fix parent count allow 1 only --- .../primitives/core/src/location.rs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index ffdab463101f..e47200ddd5c9 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -45,7 +45,7 @@ pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData DescribeLocation for DescribeGlobalPrefix { fn describe_location(l: &Location) -> Option> { match (l.parent_count(), l.first_interior()) { - (_, Some(GlobalConsensus(network))) => { + (1, Some(GlobalConsensus(network))) => { let mut tail = l.clone().split_first_interior().0; tail.dec_parent(); let interior = Suffix::describe_location(&tail)?; @@ -100,13 +100,9 @@ mod tests { // Relay Chain cases // Relay Chain relative to Ethereum Location::new(1, [GlobalConsensus(Westend)]), - // Relay Chain relative to another polkadot chain. - Location::new(2, [GlobalConsensus(Kusama)]), // Parachain cases // Parachain relative to Ethereum Location::new(1, [GlobalConsensus(Westend), Parachain(2000)]), - // Parachain relative to another polkadot chain. - Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), // Parachain general index Location::new(1, [GlobalConsensus(Westend), Parachain(2000), GeneralIndex(1)]), // Parachain general key @@ -174,19 +170,6 @@ mod tests { AccountId32 { network: None, id: [0; 32] }, ], ), - // Ethereum cases - // Ethereum location relative to Polkadot - Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]), - // Ethereum location relative to Ethereum - Location::new(1, [GlobalConsensus(Ethereum { chain_id: 2 })]), - // Ethereum ERC20 location relative to Polkadot - Location::new( - 2, - [ - GlobalConsensus(Ethereum { chain_id: 1 }), - AccountKey20 { network: None, key: [0; 20] }, - ], - ), ]; for token in token_locations { From a01e9fc5adbdb15b07236bc789319276e8d62da9 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 5 Sep 2024 12:25:43 +0200 Subject: [PATCH 275/382] deprecate DescribeHere --- bridges/snowbridge/primitives/core/src/location.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index e47200ddd5c9..ee91432b3184 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -15,12 +15,15 @@ use sp_std::prelude::*; use xcm::prelude::{ AccountId32, AccountKey20, GeneralIndex, GeneralKey, GlobalConsensus, Location, PalletInstance, }; -use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; +use xcm_builder::{ + DescribeAllTerminal, DescribeFamily, DescribeLocation, DescribeTerminus, HashedDescription, +}; pub type AgentId = H256; /// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on /// Ethereum which acts as the sovereign account for the Location. +#[allow(deprecated)] pub type AgentIdOf = HashedDescription)>; @@ -29,10 +32,16 @@ pub type TokenId = H256; /// Convert a token location to a stable ID that can be used on the Ethereum side pub type TokenIdOf = HashedDescription< TokenId, - DescribeGlobalPrefix<(DescribeHere, DescribeFamily)>, + DescribeGlobalPrefix<(DescribeTerminus, DescribeFamily)>, >; +/// This looks like DescribeTerminus that was added to xcm-builder. However this does an extra +/// `encode` to the Vector producing a different output to DescribeTerminus. `DescribeHere` +/// should NOT be used for new code. This is left here for backwards compatibility of channels and +/// agents. +#[deprecated(note = "Use DescribeTerminus from xcm-builder instead.")] pub struct DescribeHere; +#[allow(deprecated)] impl DescribeLocation for DescribeHere { fn describe_location(l: &Location) -> Option> { match l.unpack() { From d5b9b226477dcce85261f34e5bf6bfa67913619a Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 18:36:24 +0800 Subject: [PATCH 276/382] Improve comments --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 85d3091543b2..7935de40ac3d 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -438,7 +438,9 @@ impl< let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); instructions.extend(vec![ - // `SetFeesMode` to pay transport fee from bridge sovereign + // `SetFeesMode` to pay transport fee from bridge sovereign, which depends on + // unspent AH fees deposited to the bridge sovereign, + // more context and analysis in https://github.com/paritytech/polkadot-sdk/pull/5546#discussion_r1744682864 SetFeesMode { jit_withdraw: true }, // `SetAppendix` ensures that `fees` are not trapped in any case SetAppendix(Xcm(vec![DepositAsset { From 8d9ebcd51a8021f77d6ae16ef9d6388f55d0598f Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 5 Sep 2024 12:37:25 +0200 Subject: [PATCH 277/382] Clear other messages before dry-run to get only the ones produced during (#5581) The dry-run shows in `forwarded_xcms` all the messages in the queues at the time of calling the API. Each time the API is called, the result could be different. You could get messages even if you dry-run something that doesn't send a message, like a `System::remark`. This PR fixes this by clearing the message queues before doing the dry-run, so the only messages left are the ones the users of the API actually care about. --------- Co-authored-by: Adrian Catangiu --- .../modules/xcm-bridge-hub-router/src/lib.rs | 4 +++ .../modules/xcm-bridge-hub-router/src/mock.rs | 4 +++ bridges/modules/xcm-bridge-hub/src/mock.rs | 4 +++ cumulus/pallets/parachain-system/src/lib.rs | 4 +++ cumulus/pallets/xcmp-queue/src/lib.rs | 5 ++++ cumulus/primitives/utility/src/lib.rs | 4 +++ polkadot/runtime/common/src/xcm_sender.rs | 5 ++++ polkadot/xcm/pallet-xcm/src/lib.rs | 6 +++- polkadot/xcm/xcm-builder/src/routing.rs | 13 ++++++++ .../xcm/xcm-builder/src/universal_exports.rs | 4 +++ prdoc/pr_5581.prdoc | 30 +++++++++++++++++++ 11 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 prdoc/pr_5581.prdoc diff --git a/bridges/modules/xcm-bridge-hub-router/src/lib.rs b/bridges/modules/xcm-bridge-hub-router/src/lib.rs index 860c1a83878c..7ba524e95b1d 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/lib.rs @@ -408,6 +408,10 @@ impl, I: 'static> SendXcm for Pallet { } impl, I: 'static> InspectMessageQueues for Pallet { + fn clear_messages() { + ViaBridgeHubExporter::::clear_messages() + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { ViaBridgeHubExporter::::get_messages() } diff --git a/bridges/modules/xcm-bridge-hub-router/src/mock.rs b/bridges/modules/xcm-bridge-hub-router/src/mock.rs index f4806eb55b2a..bb265e1925a2 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/mock.rs @@ -130,6 +130,10 @@ impl SendXcm for TestToBridgeHubSender { } impl InspectMessageQueues for TestToBridgeHubSender { + fn clear_messages() { + SENT_XCM.with(|q| q.borrow_mut().clear()); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { SENT_XCM.with(|q| { (*q.borrow()) diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs index d32e0989dfd8..aff3526b5589 100644 --- a/bridges/modules/xcm-bridge-hub/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub/src/mock.rs @@ -295,6 +295,10 @@ impl SendXcm for TestExportXcmWithXcmOverBridge { } } impl InspectMessageQueues for TestExportXcmWithXcmOverBridge { + fn clear_messages() { + todo!() + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { todo!() } diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index bf136dc0644c..882dcb68fbbe 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -1540,6 +1540,10 @@ impl UpwardMessageSender for Pallet { } impl InspectMessageQueues for Pallet { + fn clear_messages() { + PendingUpwardMessages::::kill(); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { use xcm::prelude::*; diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 8c4446a925d4..732ee94f3e10 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -1008,6 +1008,11 @@ impl SendXcm for Pallet { } impl InspectMessageQueues for Pallet { + fn clear_messages() { + // Best effort. + let _ = OutboundXcmpMessages::::clear(u32::MAX, None); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { use xcm::prelude::*; diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 3ebcb44fa439..e568c79bb6a0 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -99,6 +99,10 @@ where impl InspectMessageQueues for ParentAsUmp { + fn clear_messages() { + T::clear_messages(); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { T::get_messages() } diff --git a/polkadot/runtime/common/src/xcm_sender.rs b/polkadot/runtime/common/src/xcm_sender.rs index dace785a535b..37fe7f0b59e9 100644 --- a/polkadot/runtime/common/src/xcm_sender.rs +++ b/polkadot/runtime/common/src/xcm_sender.rs @@ -141,6 +141,11 @@ where } impl InspectMessageQueues for ChildParachainRouter { + fn clear_messages() { + // Best effort. + let _ = dmp::DownwardMessageQueues::::clear(u32::MAX, None); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { dmp::DownwardMessageQueues::::iter() .map(|(para_id, messages)| { diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 6451901279b1..05d9046ab192 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -2457,10 +2457,14 @@ impl Pallet { ::RuntimeOrigin: From, { crate::Pallet::::set_record_xcm(true); - frame_system::Pallet::::reset_events(); // To make sure we only record events from current call. + // Clear other messages in queues... + Router::clear_messages(); + // ...and reset events to make sure we only record events from current call. + frame_system::Pallet::::reset_events(); let result = call.dispatch(origin.into()); crate::Pallet::::set_record_xcm(false); let local_xcm = crate::Pallet::::recorded_xcm(); + // Should only get messages from this call since we cleared previous ones. let forwarded_xcms = Router::get_messages(); let events: Vec<::RuntimeEvent> = frame_system::Pallet::::read_events_no_consensus() diff --git a/polkadot/xcm/xcm-builder/src/routing.rs b/polkadot/xcm/xcm-builder/src/routing.rs index 03ef780ef032..fc2de89d2128 100644 --- a/polkadot/xcm/xcm-builder/src/routing.rs +++ b/polkadot/xcm/xcm-builder/src/routing.rs @@ -62,6 +62,10 @@ impl SendXcm for WithUniqueTopic { } } impl InspectMessageQueues for WithUniqueTopic { + fn clear_messages() { + Inner::clear_messages() + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { Inner::get_messages() } @@ -149,12 +153,21 @@ impl EnsureDelivery for Tuple { /// Inspects messages in queues. /// Meant to be used in runtime APIs, not in runtimes. pub trait InspectMessageQueues { + /// Clear the queues at the beginning of Runtime API call, so that subsequent + /// `Self::get_messages()` will return only messages generated by said Runtime API. + fn clear_messages(); /// Get queued messages and their destinations. fn get_messages() -> Vec<(VersionedLocation, Vec>)>; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl InspectMessageQueues for Tuple { + fn clear_messages() { + for_tuples!( #( + Tuple::clear_messages(); + )* ); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { let mut messages = Vec::new(); diff --git a/polkadot/xcm/xcm-builder/src/universal_exports.rs b/polkadot/xcm/xcm-builder/src/universal_exports.rs index 8aa9602fcc29..30e0b7c72b03 100644 --- a/polkadot/xcm/xcm-builder/src/universal_exports.rs +++ b/polkadot/xcm/xcm-builder/src/universal_exports.rs @@ -340,6 +340,10 @@ impl InspectMessageQueues for SovereignPaidRemoteExporter { + fn clear_messages() { + Router::clear_messages() + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { Router::get_messages() } diff --git a/prdoc/pr_5581.prdoc b/prdoc/pr_5581.prdoc new file mode 100644 index 000000000000..e33690939d8b --- /dev/null +++ b/prdoc/pr_5581.prdoc @@ -0,0 +1,30 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Clear other messages before dry-running + +doc: + - audience: Runtime Dev + description: | + The DryRunApi.dry_run_call and DryRunApi.dry_run_xcm functions used to populate + `forwarded_xcms` with all the existing messages in the queues at the time. + Now, existing (irrelevant) messages are cleared when dry-running, meaning only the + messages produced by the dry-run call (or xcm) will be returned in `forwarded_xcms`. + +crates: + - name: pallet-xcm + bump: minor + - name: staging-xcm-builder + bump: major + - name: pallet-xcm-bridge-hub-router + bump: minor + - name: cumulus-pallet-parachain-system + bump: minor + - name: cumulus-pallet-xcmp-queue + bump: minor + - name: cumulus-primitives-utility + bump: minor + - name: polkadot-runtime-common + bump: minor + - name: pallet-xcm-bridge-hub + bump: minor From f6fd5bc5a644b2c4ed986e372b43ab8f049b7fc6 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Thu, 5 Sep 2024 13:26:46 +0200 Subject: [PATCH 278/382] Make the docs logo readable on light backgrounds (#5389) Currently, the logo on the docs site blends into the background on the light theme: [Here](https://internals.rust-lang.org/t/feedback-on-new-rust-documentation-logo-background/12787) is a discussion related to this. There doesn't seem to be a perfect way to handle this situation. --- Silent because the docs crate is not part of the regular release process. --------- Co-authored-by: ordian --- docs/sdk/assets/theme.css | 5 +++++ docs/sdk/src/lib.rs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/sdk/assets/theme.css b/docs/sdk/assets/theme.css index a488e15c36b7..1f47a8ef5b0c 100644 --- a/docs/sdk/assets/theme.css +++ b/docs/sdk/assets/theme.css @@ -9,9 +9,14 @@ body.sdk-docs { nav.sidebar>div.sidebar-crate>a>img { width: 190px; + height: 52px; } nav.sidebar { flex: 0 0 250px; } } + +html[data-theme="light"] .sidebar-crate > .logo-container > img { + content: url("https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/images/Polkadot_Logo_Horizontal_Pink_Black.png"); +} diff --git a/docs/sdk/src/lib.rs b/docs/sdk/src/lib.rs index 6dc87858530c..35f73c290bf2 100644 --- a/docs/sdk/src/lib.rs +++ b/docs/sdk/src/lib.rs @@ -27,7 +27,7 @@ #![warn(rustdoc::private_intra_doc_links)] #![doc(html_favicon_url = "https://polkadot.com/favicon.ico")] #![doc( - html_logo_url = "https://europe1.discourse-cdn.com/standard21/uploads/polkadot2/original/1X/eb57081e2bb7c39e5fcb1a98b443e423fa4448ae.svg" + html_logo_url = "https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/images/Polkadot_Logo_Horizontal_Pink_White.png" )] #![doc(issue_tracker_base_url = "https://github.com/paritytech/polkadot-sdk/issues")] From a947cb831d8d56228e7aa84355410c50864a816e Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Thu, 5 Sep 2024 15:37:27 +0300 Subject: [PATCH 279/382] Add benchmark for the number of minimum cpu cores (#5127) Fixes: https://github.com/paritytech/polkadot-sdk/issues/5122. This PR extends the existing single core `benchmark_cpu` to also build a score of the entire processor by spawning `EXPECTED_NUM_CORES(8)` threads and averaging their throughput. This is better than simply checking the number of cores, because also covers multi-tenant environments where the OS sees a high number of available CPUs, but because it has to share it with the rest of his neighbours its total throughput does not satisfy the minimum requirements. ## TODO - [x] Obtain reference values on the reference hardware. --------- Signed-off-by: Alexandru Gheorghe --- .../polkadot-parachain-lib/src/command.rs | 5 +- .../polkadot-parachain-lib/src/common/spec.rs | 4 +- polkadot/cli/src/command.rs | 2 +- polkadot/node/service/src/lib.rs | 31 ++++- prdoc/pr_5127.prdoc | 25 ++++ substrate/bin/node/cli/src/service.rs | 4 +- substrate/client/sysinfo/src/lib.rs | 22 ++- substrate/client/sysinfo/src/sysinfo.rs | 128 ++++++++++++++---- .../benchmarking-cli/src/machine/hardware.rs | 25 +++- .../frame/benchmarking-cli/src/machine/mod.rs | 8 +- .../src/machine/reference_hardware.json | 5 + templates/parachain/node/src/command.rs | 5 +- templates/parachain/node/src/service.rs | 2 +- 13 files changed, 217 insertions(+), 49 deletions(-) create mode 100644 prdoc/pr_5127.prdoc diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs index 320511ece5e5..43fb551f80d2 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs @@ -258,7 +258,10 @@ pub fn run(cmd_config: RunConfig) -> Result<() let hwbench = (!cli.no_hardware_benchmarks) .then_some(config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) + sc_sysinfo::gather_hwbench( + Some(database_path), + &SUBSTRATE_REFERENCE_HARDWARE, + ) })) .flatten(); diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs index 8e19cf304b07..0c0230296eb8 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs @@ -80,7 +80,9 @@ where fn warn_if_slow_hardware(hwbench: &sc_sysinfo::HwBench) { // Polkadot para-chains should generally use these requirements to ensure that the relay-chain // will not take longer than expected to import its blocks. - if let Err(err) = frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench) { + if let Err(err) = + frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench, false) + { log::warn!( "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\ https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", diff --git a/polkadot/cli/src/command.rs b/polkadot/cli/src/command.rs index 62d99122c301..168a645430ed 100644 --- a/polkadot/cli/src/command.rs +++ b/polkadot/cli/src/command.rs @@ -230,7 +230,7 @@ where let hwbench = (!cli.run.no_hardware_benchmarks) .then_some(config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) + sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) })) .flatten(); diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index ab91c6b9b92b..a8e7fc16eb4d 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -763,6 +763,7 @@ pub fn new_full< use polkadot_availability_recovery::FETCH_CHUNKS_THRESHOLD; use polkadot_node_network_protocol::request_response::IncomingRequest; use sc_network_sync::WarpSyncConfig; + use sc_sysinfo::Metric; let is_offchain_indexing_enabled = config.offchain_worker.indexing_enabled; let role = config.role; @@ -1080,13 +1081,31 @@ pub fn new_full< if let Some(hwbench) = hwbench { sc_sysinfo::print_hwbench(&hwbench); - match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) { + match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench, role.is_authority()) { Err(err) if role.is_authority() => { - log::warn!( - "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\ - https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", - err - ); + if err + .0 + .iter() + .any(|failure| matches!(failure.metric, Metric::Blake2256Parallel { .. })) + { + log::warn!( + "⚠️ Starting January 2025 the hardware will fail the minimal physical CPU cores requirements {} for role 'Authority',\n\ + find out more when this will become mandatory at:\n\ + https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", + err + ); + } + if err + .0 + .iter() + .any(|failure| !matches!(failure.metric, Metric::Blake2256Parallel { .. })) + { + log::warn!( + "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\ + https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", + err + ); + } }, _ => {}, } diff --git a/prdoc/pr_5127.prdoc b/prdoc/pr_5127.prdoc new file mode 100644 index 000000000000..c08f4e7fb8fa --- /dev/null +++ b/prdoc/pr_5127.prdoc @@ -0,0 +1,25 @@ +title: Add benchmark to check upcoming minimum required hw cores + +doc: + - audience: Node Operator + description: | + Add benchmark that checks hardware satisifies the minimum required hardware cores + for a validators. The new minimum requirements are schedule to come into effect + in January 2025, for more details see: https://polkadot.subsquare.io/referenda/1051. + + +crates: + - name: sc-sysinfo + bump: major + - name: frame-benchmarking-cli + bump: major + - name: staging-node-cli + bump: patch + - name: polkadot-service + bump: patch + - name: polkadot-parachain-lib + bump: patch + - name: polkadot-cli + bump: patch + - name: parachain-template-node + bump: patch diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 4f63473f632d..1b345a23f27e 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -416,7 +416,7 @@ pub fn new_full_base::Hash>>( let hwbench = (!disable_hardware_benchmarks) .then_some(config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) + sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) })) .flatten(); @@ -553,7 +553,7 @@ pub fn new_full_base::Hash>>( if let Some(hwbench) = hwbench { sc_sysinfo::print_hwbench(&hwbench); - match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) { + match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench, false) { Err(err) if role.is_authority() => { log::warn!( "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority'.", diff --git a/substrate/client/sysinfo/src/lib.rs b/substrate/client/sysinfo/src/lib.rs index 7065c9b997e7..7bc30ae0221d 100644 --- a/substrate/client/sysinfo/src/lib.rs +++ b/substrate/client/sysinfo/src/lib.rs @@ -27,10 +27,10 @@ mod sysinfo; mod sysinfo_linux; pub use sysinfo::{ - benchmark_cpu, benchmark_disk_random_writes, benchmark_disk_sequential_writes, - benchmark_memory, benchmark_sr25519_verify, gather_hwbench, gather_sysinfo, - serialize_throughput, serialize_throughput_option, Metric, Requirement, Requirements, - Throughput, + benchmark_cpu, benchmark_cpu_parallelism, benchmark_disk_random_writes, + benchmark_disk_sequential_writes, benchmark_memory, benchmark_sr25519_verify, gather_hwbench, + gather_sysinfo, serialize_throughput, serialize_throughput_option, Metric, Requirement, + Requirements, Throughput, }; /// The operating system part of the current target triplet. @@ -48,6 +48,12 @@ pub struct HwBench { /// The CPU speed, as measured in how many MB/s it can hash using the BLAKE2b-256 hash. #[serde(serialize_with = "serialize_throughput")] pub cpu_hashrate_score: Throughput, + /// The parallel CPU speed, as measured in how many MB/s it can hash in parallel using the + /// BLAKE2b-256 hash. + #[serde(serialize_with = "serialize_throughput")] + pub parallel_cpu_hashrate_score: Throughput, + /// The number of expected cores used for computing the parallel CPU speed. + pub parallel_cpu_cores: usize, /// Memory bandwidth in MB/s, calculated by measuring the throughput of `memcpy`. #[serde(serialize_with = "serialize_throughput")] pub memory_memcpy_score: Throughput, @@ -65,6 +71,7 @@ pub struct HwBench { pub disk_random_write_score: Option, } +#[derive(Copy, Clone, Debug)] /// Limit the execution time of a benchmark. pub enum ExecutionLimit { /// Limit by the maximal duration. @@ -132,7 +139,12 @@ pub fn print_sysinfo(sysinfo: &sc_telemetry::SysInfo) { /// Prints out the results of the hardware benchmarks in the logs. pub fn print_hwbench(hwbench: &HwBench) { - log::info!("🏁 CPU score: {}", hwbench.cpu_hashrate_score); + log::info!( + "🏁 CPU single core score: {}, parallelism score: {} with expected cores: {}", + hwbench.cpu_hashrate_score, + hwbench.parallel_cpu_hashrate_score, + hwbench.parallel_cpu_cores, + ); log::info!("🏁 Memory score: {}", hwbench.memory_memcpy_score); if let Some(score) = hwbench.disk_sequential_write_score { diff --git a/substrate/client/sysinfo/src/sysinfo.rs b/substrate/client/sysinfo/src/sysinfo.rs index 37b35fcb9103..4bcdd673455c 100644 --- a/substrate/client/sysinfo/src/sysinfo.rs +++ b/substrate/client/sysinfo/src/sysinfo.rs @@ -22,16 +22,18 @@ use sc_telemetry::SysInfo; use sp_core::{sr25519, Pair}; use sp_io::crypto::sr25519_verify; +use core::f64; use derive_more::From; use rand::{seq::SliceRandom, Rng, RngCore}; use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; use std::{ - fmt, - fmt::{Display, Formatter}, + borrow::Cow, + fmt::{self, Display, Formatter}, fs::File, io::{Seek, SeekFrom, Write}, ops::{Deref, DerefMut}, path::{Path, PathBuf}, + sync::{Arc, Barrier}, time::{Duration, Instant}, }; @@ -42,6 +44,8 @@ pub enum Metric { Sr25519Verify, /// Blake2-256 hashing algorithm. Blake2256, + /// Blake2-256 hashing algorithm executed in parallel + Blake2256Parallel { num_cores: usize }, /// Copying data in RAM. MemCopy, /// Disk sequential write. @@ -85,20 +89,22 @@ impl Metric { /// The category of the metric. pub fn category(&self) -> &'static str { match self { - Self::Sr25519Verify | Self::Blake2256 => "CPU", + Self::Sr25519Verify | Self::Blake2256 | Self::Blake2256Parallel { .. } => "CPU", Self::MemCopy => "Memory", Self::DiskSeqWrite | Self::DiskRndWrite => "Disk", } } /// The name of the metric. It is always prefixed by the [`self.category()`]. - pub fn name(&self) -> &'static str { + pub fn name(&self) -> Cow<'static, str> { match self { - Self::Sr25519Verify => "SR25519-Verify", - Self::Blake2256 => "BLAKE2-256", - Self::MemCopy => "Copy", - Self::DiskSeqWrite => "Seq Write", - Self::DiskRndWrite => "Rnd Write", + Self::Sr25519Verify => Cow::Borrowed("SR25519-Verify"), + Self::Blake2256 => Cow::Borrowed("BLAKE2-256"), + Self::Blake2256Parallel { num_cores } => + Cow::Owned(format!("BLAKE2-256-Parallel-{}", num_cores)), + Self::MemCopy => Cow::Borrowed("Copy"), + Self::DiskSeqWrite => Cow::Borrowed("Seq Write"), + Self::DiskRndWrite => Cow::Borrowed("Rnd Write"), } } } @@ -253,6 +259,10 @@ pub struct Requirement { deserialize_with = "deserialize_throughput" )] pub minimum: Throughput, + /// Check this requirement only for relay chain validator nodes. + #[serde(default)] + #[serde(skip_serializing_if = "core::ops::Not::not")] + pub validator_only: bool, } #[inline(always)] @@ -343,8 +353,18 @@ fn clobber_value(input: &mut T) { pub const DEFAULT_CPU_EXECUTION_LIMIT: ExecutionLimit = ExecutionLimit::Both { max_iterations: 4 * 1024, max_duration: Duration::from_millis(100) }; -// This benchmarks the CPU speed as measured by calculating BLAKE2b-256 hashes, in bytes per second. +// This benchmarks the single core CPU speed as measured by calculating BLAKE2b-256 hashes, in bytes +// per second. pub fn benchmark_cpu(limit: ExecutionLimit) -> Throughput { + benchmark_cpu_parallelism(limit, 1) +} + +// This benchmarks the entire CPU speed as measured by calculating BLAKE2b-256 hashes, in bytes per +// second. It spawns multiple threads to measure the throughput of the entire CPU and averages the +// score obtained by each thread. If we have at least `refhw_num_cores` available then the +// average throughput should be relatively close to the single core performance as measured by +// calling this function with refhw_num_cores equal to 1. +pub fn benchmark_cpu_parallelism(limit: ExecutionLimit, refhw_num_cores: usize) -> Throughput { // In general the results of this benchmark are somewhat sensitive to how much // data we hash at the time. The smaller this is the *less* B/s we can hash, // the bigger this is the *more* B/s we can hash, up until a certain point @@ -359,20 +379,38 @@ pub fn benchmark_cpu(limit: ExecutionLimit) -> Throughput { // but without hitting its theoretical maximum speed. const SIZE: usize = 32 * 1024; - let mut buffer = Vec::new(); - buffer.resize(SIZE, 0x66); - let mut hash = Default::default(); + let ready_to_run_benchmark = Arc::new(Barrier::new(refhw_num_cores)); + let mut benchmark_threads = Vec::new(); - let run = || -> Result<(), ()> { - clobber_slice(&mut buffer); - hash = sp_crypto_hashing::blake2_256(&buffer); - clobber_slice(&mut hash); + // Spawn a thread for each expected core and average the throughput for each of them. + for _ in 0..refhw_num_cores { + let ready_to_run_benchmark = ready_to_run_benchmark.clone(); - Ok(()) - }; + let handle = std::thread::spawn(move || { + let mut buffer = Vec::new(); + buffer.resize(SIZE, 0x66); + let mut hash = Default::default(); - benchmark("CPU score", SIZE, limit.max_iterations(), limit.max_duration(), run) - .expect("benchmark cannot fail; qed") + let run = || -> Result<(), ()> { + clobber_slice(&mut buffer); + hash = sp_crypto_hashing::blake2_256(&buffer); + clobber_slice(&mut hash); + + Ok(()) + }; + ready_to_run_benchmark.wait(); + benchmark("CPU score", SIZE, limit.max_iterations(), limit.max_duration(), run) + .expect("benchmark cannot fail; qed") + }); + benchmark_threads.push(handle); + } + + let average_score = benchmark_threads + .into_iter() + .map(|thread| thread.join().map(|throughput| throughput.as_kibs()).unwrap_or(0.0)) + .sum::() / + refhw_num_cores as f64; + Throughput::from_kibs(average_score) } /// A default [`ExecutionLimit`] that can be used to call [`benchmark_memory`]. @@ -624,10 +662,25 @@ pub fn benchmark_sr25519_verify(limit: ExecutionLimit) -> Throughput { /// Optionally accepts a path to a `scratch_directory` to use to benchmark the /// disk. Also accepts the `requirements` for the hardware benchmark and a /// boolean to specify if the node is an authority. -pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench { +pub fn gather_hwbench(scratch_directory: Option<&Path>, requirements: &Requirements) -> HwBench { + let cpu_hashrate_score = benchmark_cpu(DEFAULT_CPU_EXECUTION_LIMIT); + let (parallel_cpu_hashrate_score, parallel_cpu_cores) = requirements + .0 + .iter() + .filter_map(|req| { + if let Metric::Blake2256Parallel { num_cores } = req.metric { + Some((benchmark_cpu_parallelism(DEFAULT_CPU_EXECUTION_LIMIT, num_cores), num_cores)) + } else { + None + } + }) + .next() + .unwrap_or((cpu_hashrate_score, 1)); #[allow(unused_mut)] let mut hwbench = HwBench { - cpu_hashrate_score: benchmark_cpu(DEFAULT_CPU_EXECUTION_LIMIT), + cpu_hashrate_score, + parallel_cpu_hashrate_score, + parallel_cpu_cores, memory_memcpy_score: benchmark_memory(DEFAULT_MEMORY_EXECUTION_LIMIT), disk_sequential_write_score: None, disk_random_write_score: None, @@ -659,9 +712,17 @@ pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench { impl Requirements { /// Whether the hardware requirements are met by the provided benchmark results. - pub fn check_hardware(&self, hwbench: &HwBench) -> Result<(), CheckFailures> { + pub fn check_hardware( + &self, + hwbench: &HwBench, + is_rc_authority: bool, + ) -> Result<(), CheckFailures> { let mut failures = Vec::new(); for requirement in self.0.iter() { + if requirement.validator_only && !is_rc_authority { + continue + } + match requirement.metric { Metric::Blake2256 => if requirement.minimum > hwbench.cpu_hashrate_score { @@ -671,6 +732,14 @@ impl Requirements { found: hwbench.cpu_hashrate_score, }); }, + Metric::Blake2256Parallel { .. } => + if requirement.minimum > hwbench.parallel_cpu_hashrate_score { + failures.push(CheckFailure { + metric: requirement.metric, + expected: requirement.minimum, + found: hwbench.parallel_cpu_hashrate_score, + }); + }, Metric::MemCopy => if requirement.minimum > hwbench.memory_memcpy_score { failures.push(CheckFailure { @@ -732,6 +801,13 @@ mod tests { assert!(benchmark_cpu(DEFAULT_CPU_EXECUTION_LIMIT) > Throughput::from_mibs(0.0)); } + #[test] + fn test_benchmark_parallel_cpu() { + assert!( + benchmark_cpu_parallelism(DEFAULT_CPU_EXECUTION_LIMIT, 8) > Throughput::from_mibs(0.0) + ); + } + #[test] fn test_benchmark_memory() { assert!(benchmark_memory(DEFAULT_MEMORY_EXECUTION_LIMIT) > Throughput::from_mibs(0.0)); @@ -781,6 +857,8 @@ mod tests { fn hwbench_serialize_works() { let hwbench = HwBench { cpu_hashrate_score: Throughput::from_gibs(1.32), + parallel_cpu_hashrate_score: Throughput::from_gibs(1.32), + parallel_cpu_cores: 4, memory_memcpy_score: Throughput::from_kibs(9342.432), disk_sequential_write_score: Some(Throughput::from_kibs(4332.12)), disk_random_write_score: None, @@ -788,6 +866,6 @@ mod tests { let serialized = serde_json::to_string(&hwbench).unwrap(); // Throughput from all of the benchmarks should be converted to MiBs. - assert_eq!(serialized, "{\"cpu_hashrate_score\":1351,\"memory_memcpy_score\":9,\"disk_sequential_write_score\":4}"); + assert_eq!(serialized, "{\"cpu_hashrate_score\":1351,\"parallel_cpu_hashrate_score\":1351,\"parallel_cpu_cores\":4,\"memory_memcpy_score\":9,\"disk_sequential_write_score\":4}"); } } diff --git a/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs b/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs index 555e848f8ccc..ee1d490b8547 100644 --- a/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs +++ b/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs @@ -51,17 +51,36 @@ mod tests { assert_eq!( *SUBSTRATE_REFERENCE_HARDWARE, Requirements(vec![ - Requirement { metric: Metric::Blake2256, minimum: Throughput::from_mibs(1000.00) }, + Requirement { + metric: Metric::Blake2256, + minimum: Throughput::from_mibs(1000.00), + validator_only: false + }, + Requirement { + metric: Metric::Blake2256Parallel { num_cores: 8 }, + minimum: Throughput::from_mibs(1000.00), + validator_only: true, + }, Requirement { metric: Metric::Sr25519Verify, minimum: Throughput::from_kibs(637.619999744), + validator_only: false }, Requirement { metric: Metric::MemCopy, minimum: Throughput::from_gibs(11.4925205078125003), + validator_only: false, + }, + Requirement { + metric: Metric::DiskSeqWrite, + minimum: Throughput::from_mibs(950.0), + validator_only: false, + }, + Requirement { + metric: Metric::DiskRndWrite, + minimum: Throughput::from_mibs(420.0), + validator_only: false }, - Requirement { metric: Metric::DiskSeqWrite, minimum: Throughput::from_mibs(950.0) }, - Requirement { metric: Metric::DiskRndWrite, minimum: Throughput::from_mibs(420.0) }, ]) ); } diff --git a/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs b/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs index fb9f14c9a4af..0186ca58762d 100644 --- a/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs +++ b/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs @@ -29,9 +29,9 @@ use log::{error, info, warn}; use sc_cli::{CliConfiguration, Result, SharedParams}; use sc_service::Configuration; use sc_sysinfo::{ - benchmark_cpu, benchmark_disk_random_writes, benchmark_disk_sequential_writes, - benchmark_memory, benchmark_sr25519_verify, ExecutionLimit, Metric, Requirement, Requirements, - Throughput, + benchmark_cpu, benchmark_cpu_parallelism, benchmark_disk_random_writes, + benchmark_disk_sequential_writes, benchmark_memory, benchmark_sr25519_verify, ExecutionLimit, + Metric, Requirement, Requirements, Throughput, }; use crate::shared::check_build_profile; @@ -150,6 +150,8 @@ impl MachineCmd { let score = match metric { Metric::Blake2256 => benchmark_cpu(hash_limit), + Metric::Blake2256Parallel { num_cores } => + benchmark_cpu_parallelism(hash_limit, *num_cores), Metric::Sr25519Verify => benchmark_sr25519_verify(verify_limit), Metric::MemCopy => benchmark_memory(memory_limit), Metric::DiskSeqWrite => benchmark_disk_sequential_writes(disk_limit, dir)?, diff --git a/substrate/utils/frame/benchmarking-cli/src/machine/reference_hardware.json b/substrate/utils/frame/benchmarking-cli/src/machine/reference_hardware.json index cec42b8f245c..654eaa6ff138 100644 --- a/substrate/utils/frame/benchmarking-cli/src/machine/reference_hardware.json +++ b/substrate/utils/frame/benchmarking-cli/src/machine/reference_hardware.json @@ -3,6 +3,11 @@ "metric": "Blake2256", "minimum": 1000.00 }, + { + "metric": {"Blake2256Parallel":{"num_cores":8}}, + "minimum": 1000.00, + "validator_only": true + }, { "metric": "Sr25519Verify", "minimum": 0.622675781 diff --git a/templates/parachain/node/src/command.rs b/templates/parachain/node/src/command.rs index fa94d50a8bed..610dbd7a686a 100644 --- a/templates/parachain/node/src/command.rs +++ b/templates/parachain/node/src/command.rs @@ -220,7 +220,10 @@ pub fn run() -> Result<()> { let hwbench = (!cli.no_hardware_benchmarks) .then_some(config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) + sc_sysinfo::gather_hwbench( + Some(database_path), + &SUBSTRATE_REFERENCE_HARDWARE, + ) })) .flatten(); diff --git a/templates/parachain/node/src/service.rs b/templates/parachain/node/src/service.rs index fc0bcba4c0d1..04e20be2bd40 100644 --- a/templates/parachain/node/src/service.rs +++ b/templates/parachain/node/src/service.rs @@ -334,7 +334,7 @@ pub async fn start_parachain_node( // Here you can check whether the hardware meets your chains' requirements. Putting a link // in there and swapping out the requirements for your own are probably a good idea. The // requirements for a para-chain are dictated by its relay-chain. - match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) { + match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench, false) { Err(err) if validator => { log::warn!( "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority'.", From 663ac9bc981cb3ba39141df9a09f0990f3aa7215 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 20:46:56 +0800 Subject: [PATCH 280/382] Fix build --- .../pallets/ethereum-client/src/mock.rs | 10 +++++++--- .../snowbridge_pallet_ethereum_client.rs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs index 41d1835972b9..97cc2e7fe2bc 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs @@ -3,16 +3,20 @@ use crate as ethereum_beacon_client; use crate::config; use frame_support::{ - derive_impl, dispatch::DispatchResult, pallet_prelude::Weight, parameter_types, + derive_impl, + dispatch::DispatchResult, + migrations::MultiStepMigrator, + pallet_prelude::Weight, + parameter_types, + traits::{ConstU32, OnFinalize, OnInitialize}, }; use snowbridge_beacon_primitives::{Fork, ForkVersions}; use snowbridge_core::inbound::{Log, Proof}; +use sp_runtime::BuildStorage; use sp_std::default::Default; use std::{fs::File, path::PathBuf}; type Block = frame_system::mocking::MockBlock; -use frame_support::traits::ConstU32; -use sp_runtime::BuildStorage; fn load_fixture(basename: String) -> Result where diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_ethereum_client.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_ethereum_client.rs index 23e2a9cffb0b..7c95e69bc0c5 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_ethereum_client.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_ethereum_client.rs @@ -117,4 +117,23 @@ impl snowbridge_pallet_ethereum_client::WeightInfo for .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } + + /// Storage: UNKNOWN KEY `0xada12a87b9ccce83f328569cf9934e83e6d574e897864a327c716c553f277037` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xada12a87b9ccce83f328569cf9934e83e6d574e897864a327c716c553f277037` (r:1 w:1) + /// Storage: UNKNOWN KEY `0xada12a87b9ccce83f328569cf9934e834209354bdd86a5d7050a9b80004c2d6d` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xada12a87b9ccce83f328569cf9934e834209354bdd86a5d7050a9b80004c2d6d` (r:0 w:1) + /// Storage: UNKNOWN KEY `0xada12a87b9ccce83f328569cf9934e83964405908d330d65518e9e60960ba9f1` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xada12a87b9ccce83f328569cf9934e83964405908d330d65518e9e60960ba9f1` (r:0 w:1) + /// Storage: UNKNOWN KEY `0xada12a87b9ccce83f328569cf9934e83d98b4ec57d38fb2970ffc6289380767d` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xada12a87b9ccce83f328569cf9934e83d98b4ec57d38fb2970ffc6289380767d` (r:0 w:1) + fn step() -> Weight { + // Proof Size summary in bytes: + // Measured: `215` + // Estimated: `3680` + // Minimum execution time: 10_000_000 picoseconds. + Weight::from_parts(12_000_000, 0) + .saturating_add(Weight::from_parts(0, 3680)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(4)) + } } From 18460c8ebcf270a75e68440455874a25b9c43bee Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 21:20:54 +0800 Subject: [PATCH 281/382] Fix build --- .../assets/asset-hub-westend/src/genesis.rs | 7 --- .../assets/asset-hub-westend/src/lib.rs | 4 +- .../asset-hub-westend/src/xcm_config.rs | 61 ------------------- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 23 +++++++ 4 files changed, 25 insertions(+), 70 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs index e593bed8d3bb..73c5ef863309 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs @@ -89,13 +89,6 @@ pub fn genesis() -> Storage { true, ED, ), - // PenpalB's teleportable asset representation - ( - PenpalBTeleportableAssetLocation::get(), - PenpalBSiblingSovereignAccount::get(), - true, - ED, - ), ], ..Default::default() }, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 6c63a34b58c3..5d988f89d25c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -90,7 +90,6 @@ use assets_common::{ matching::{FromNetwork, FromSiblingParachain}, }; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; -use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm::{ latest::prelude::AssetId, prelude::{VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}, @@ -543,7 +542,8 @@ impl InstanceFilter for ProxyType { RuntimeCall::Utility { .. } | RuntimeCall::Multisig { .. } | RuntimeCall::NftFractionalization { .. } | - RuntimeCall::Nfts { .. } | RuntimeCall::Uniques { .. } + RuntimeCall::Nfts { .. } | + RuntimeCall::Uniques { .. } ) }, ProxyType::AssetOwner => matches!( diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index c3ab1831d54d..ceacc83ead15 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -653,67 +653,6 @@ pub mod bridging { >; } - pub mod to_ethereum { - use super::*; - use assets_common::matching::FromNetwork; - use sp_std::collections::btree_set::BTreeSet; - use testnet_parachains_constants::westend::snowbridge::{ - EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX, - }; - - parameter_types! { - /// User fee for ERC20 token transfer back to Ethereum. - /// (initially was calculated by test `OutboundQueue::calculate_fees` - ETH/WND 1/400 and fee_per_gas 20 GWEI = 2200698000000 + *25%) - /// Needs to be more than fee calculated from DefaultFeeConfig FeeConfigRecord in snowbridge:parachain/pallets/outbound-queue/src/lib.rs - /// Polkadot uses 10 decimals, Kusama,Rococo,Westend 12 decimals. - pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; - pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get(); - pub SiblingBridgeHubWithEthereumInboundQueueInstance: Location = Location::new( - 1, - [ - Parachain(SiblingBridgeHubParaId::get()), - PalletInstance(INBOUND_QUEUE_PALLET_INDEX) - ] - ); - - /// Set up exporters configuration. - /// `Option` represents static "base fee" which is used for total delivery fee calculation. - pub BridgeTable: sp_std::vec::Vec = sp_std::vec![ - NetworkExportTableItem::new( - EthereumNetwork::get(), - Some(sp_std::vec![Junctions::Here]), - SiblingBridgeHub::get(), - Some(( - XcmBridgeHubRouterFeeAssetId::get(), - BridgeHubEthereumBaseFee::get(), - ).into()) - ), - ]; - - /// Universal aliases - pub UniversalAliases: BTreeSet<(Location, Junction)> = BTreeSet::from_iter( - sp_std::vec![ - (SiblingBridgeHubWithEthereumInboundQueueInstance::get(), GlobalConsensus(EthereumNetwork::get())), - ] - ); - - pub EthereumBridgeTable: sp_std::vec::Vec = sp_std::vec::Vec::new().into_iter() - .chain(BridgeTable::get()) - .collect(); - } - - pub type EthereumNetworkExportTable = xcm_builder::NetworkExportTable; - - pub type IsTrustedBridgedReserveLocationForForeignAsset = - IsForeignConcreteAsset>; - - impl Contains<(Location, Junction)> for UniversalAliases { - fn contains(alias: &(Location, Junction)) -> bool { - UniversalAliases::get().contains(alias) - } - } - } - pub mod to_ethereum { use super::*; use alloc::collections::btree_set::BTreeSet; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 6fcd19fd8a33..a29ef7945ce8 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -535,6 +535,29 @@ impl pallet_utility::Config for Runtime { type WeightInfo = weights::pallet_utility::WeightInfo; } +parameter_types! { + pub MbmServiceWeight: Weight = Perbill::from_percent(80) * RuntimeBlockWeights::get().max_block; + pub const ExecutionHeaderCount: u32 = 8192 * 20; +} + +impl pallet_migrations::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations = snowbridge_pallet_ethereum_client::migration::EthereumExecutionHeaderCleanup< + Runtime, + crate::weights::snowbridge_pallet_ethereum_client::WeightInfo, + ExecutionHeaderCount, + >; + #[cfg(feature = "runtime-benchmarks")] + type Migrations = pallet_migrations::mock_helpers::MockedMigrations; + type CursorMaxLen = ConstU32<65_536>; + type IdentifierMaxLen = ConstU32<256>; + type MigrationStatusHandler = (); + type FailedMigrationHandler = frame_support::migrations::FreezeChainOnFailedMigration; + type MaxServiceWeight = MbmServiceWeight; + type WeightInfo = pallet_migrations::weights::SubstrateWeight; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime From aeef63f11d72cf5e39b52b39c7af48be0e58b565 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 21:24:03 +0800 Subject: [PATCH 282/382] Fix fmt --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 6 ++++-- bridges/snowbridge/primitives/router/src/outbound/mod.rs | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 7935de40ac3d..3ff6852dd001 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -168,7 +168,8 @@ impl< ConvertAssetId, UniversalLocation, GlobalAssetHubLocation, - > where + > +where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, @@ -226,7 +227,8 @@ impl< ConvertAssetId, UniversalLocation, GlobalAssetHubLocation, - > where + > +where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index d3b6c116dd7a..b23bce0e2185 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -44,7 +44,8 @@ impl where + > +where UniversalLocation: Get, EthereumNetwork: Get, OutboundQueue: SendMessage, From 435138967b1d9a94bae4062aefa00df10001af19 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 5 Sep 2024 15:29:38 +0200 Subject: [PATCH 283/382] make register token relative --- bridges/snowbridge/pallets/system/src/lib.rs | 13 +- bridges/snowbridge/pallets/system/src/mock.rs | 6 +- .../snowbridge/pallets/system/src/tests.rs | 176 +++++++++++++++++- .../src/tests/snowbridge.rs | 8 +- .../src/bridge_to_ethereum_config.rs | 2 + .../src/bridge_to_ethereum_config.rs | 2 + 6 files changed, 200 insertions(+), 7 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 051e15c387ad..c0be7dcb6e03 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -173,6 +173,12 @@ pub mod pallet { type WeightInfo: WeightInfo; + /// This chain's Universal Location. + type UniversalLocation: Get; + + // The bridges configured Ethereum network with chain id. + type EthereumNetwork: Get; + #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; } @@ -242,7 +248,6 @@ pub mod pallet { InvalidTokenTransferFees, InvalidPricingParameters, InvalidUpgradeParameters, - TokenExists, } /// The set of registered agents @@ -723,6 +728,12 @@ pub mod pallet { metadata: AssetMetadata, pays_fee: PaysFee, ) -> Result<(), DispatchError> { + let bridge_location = Location::new(2, [GlobalConsensus(T::EthereumNetwork::get())]); + let mut location = location.clone(); + location + .reanchor(&bridge_location, &T::UniversalLocation::get()) + .map_err(|_| Error::::LocationConversionFailed)?; + // Record the token id or fail if it has already been created let token_id = TokenIdOf::convert_location(&location) .ok_or(Error::::LocationConversionFailed)?; diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index 98bd3da9ab27..d8c905449d9a 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -170,6 +170,7 @@ parameter_types! { pub const RelayLocation: Location = Location::parent(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(1013)].into(); + pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; } pub const DOT: u128 = 10_000_000_000; @@ -177,8 +178,8 @@ pub const DOT: u128 = 10_000_000_000; parameter_types! { pub TreasuryAccount: AccountId = PalletId(*b"py/trsry").into_account_truncating(); pub Fee: u64 = 1000; - pub const RococoNetwork: NetworkId = NetworkId::Rococo; pub const InitialFunding: u128 = 1_000_000_000_000; + pub BridgeHubParaId: ParaId = ParaId::new(1002); pub AssetHubParaId: ParaId = ParaId::new(1000); pub TestParaId: u32 = 2000; pub Parameters: PricingParameters = PricingParameters { @@ -188,7 +189,6 @@ parameter_types! { multiplier: FixedU128::from_rational(4, 3) }; pub const InboundDeliveryCost: u128 = 1_000_000_000; - } #[cfg(feature = "runtime-benchmarks")] @@ -208,6 +208,8 @@ impl crate::Config for Test { type DefaultPricingParameters = Parameters; type WeightInfo = (); type InboundDeliveryCost = InboundDeliveryCost; + type UniversalLocation = UniversalLocation; + type EthereumNetwork = EthereumNetwork; #[cfg(feature = "runtime-benchmarks")] type Helper = (); } diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index ba016b9b4854..0745f435ba05 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -549,7 +549,7 @@ fn charge_fee_for_create_agent() { assert_ok!(EthereumSystem::create_agent(origin.clone())); let fee_charged = initial_sovereign_balance - Balances::balance(&sovereign_account); - assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal,)); + assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal)); // assert sovereign_balance decreased by (fee.base_fee + fee.delivery_fee) let message = Message { @@ -631,3 +631,177 @@ fn no_genesis_build_is_uninitialized() { assert!(!EthereumSystem::is_initialized(), "Ethereum initialized."); }); } + +#[test] +fn register_token_with_root_yeilds_success() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(1, []); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id = + hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); + let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_relative_address_reanchors_to_ethereum_and_succeeds() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(1, []); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id = + hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); + let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_complex_location_simplifies_and_succeeds() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(2, [GlobalConsensus(Kusama)]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id = + hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); + let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_doubled_bridged_polkadot_location_succeeds() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(2, [GlobalConsensus(Rococo)]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id = + hex!("62e8f33b7fb0e7e2d2276564061a2f3c7bcb612e733b8bf5733ea16cee0ecba6").into(); + let expected_location = Location::new(1, [GlobalConsensus(Rococo)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_ethereum_address_reanchors_to_relative_and_fails() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 11155111 })]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_noop!( + EthereumSystem::register_token(origin, versioned_location, asset_metadata), + Error::::LocationConversionFailed + ); + }); +} + +#[test] +fn register_token_with_double_bridged_ethereum_address_succeeds() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + const NETWORK: NetworkId = Ethereum { chain_id: 1 }; + let location = Location::new(2, [GlobalConsensus(NETWORK)]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id: H256 = + hex!("37fd94739deb1c2a8929b45a4f70ffcb52de8b54791609ee13ee0a2b33730269").into(); + let expected_location = Location::new(1, [GlobalConsensus(NETWORK)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_signed_yeilds_bad_origin() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::signed([14; 32].into()); + let location = Location::new(1, [Parachain(2000)]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_noop!( + EthereumSystem::register_token(origin, versioned_location, asset_metadata), + BadOrigin + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index 996ab2657329..abcc38c16ab3 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -295,9 +295,11 @@ fn transfer_relay_token() { ); BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); - let asset_id: Location = Location { parents: 1, interior: [GlobalConsensus(Westend)].into() }; + let asset_id: Location = Location { parents: 1, interior: [].into() }; + let expected_asset_id: Location = + Location { parents: 1, interior: [GlobalConsensus(Westend)].into() }; - let token_id = TokenIdOf::convert_location(&asset_id).unwrap(); + let expected_token_id = TokenIdOf::convert_location(&expected_asset_id).unwrap(); let ethereum_sovereign: AccountId = GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(&Location::new( @@ -389,7 +391,7 @@ fn transfer_relay_token() { let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, command: Command::SendNativeToken { - token_id, + token_id: expected_token_id, destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, amount: TOKEN_AMOUNT, fee: XCM_FEE, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 26857d262c20..1e096f19ef80 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -188,6 +188,8 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type UniversalLocation = UniversalLocation; + type EthereumNetwork = EthereumNetwork; } #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 621d39af5af0..fee9f9a2f610 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -188,6 +188,8 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type UniversalLocation = UniversalLocation; + type EthereumNetwork = EthereumNetwork; } #[cfg(feature = "runtime-benchmarks")] From f685929efbed676ca487ea576b8e2c0052e645f1 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 21:35:40 +0800 Subject: [PATCH 284/382] Fix check ci --- .github/workflows/parachain.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/parachain.yml b/.github/workflows/parachain.yml index 0df1fe3872a0..34c569acac06 100644 --- a/.github/workflows/parachain.yml +++ b/.github/workflows/parachain.yml @@ -128,7 +128,7 @@ jobs: rustup component add rustfmt --toolchain nightly rustup show - name: cargo check - run: cargo check --workspace --all-features + run: cargo check --release --verbose --package bridge-hub-westend-runtime --features fast-runtime --features runtime-benchmarks --features try-runtime - name: clippy run: cargo clippy --all-features -- -D warnings From e8f26e6748c537811342c4dd4b3837225db92a8b Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 5 Sep 2024 15:29:38 +0200 Subject: [PATCH 285/382] make register token relative --- bridges/snowbridge/pallets/system/src/lib.rs | 13 +- bridges/snowbridge/pallets/system/src/mock.rs | 6 +- .../snowbridge/pallets/system/src/tests.rs | 176 +++++++++++++++++- .../src/tests/snowbridge.rs | 8 +- .../src/bridge_to_ethereum_config.rs | 2 + .../src/bridge_to_ethereum_config.rs | 2 + 6 files changed, 200 insertions(+), 7 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 051e15c387ad..c0be7dcb6e03 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -173,6 +173,12 @@ pub mod pallet { type WeightInfo: WeightInfo; + /// This chain's Universal Location. + type UniversalLocation: Get; + + // The bridges configured Ethereum network with chain id. + type EthereumNetwork: Get; + #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; } @@ -242,7 +248,6 @@ pub mod pallet { InvalidTokenTransferFees, InvalidPricingParameters, InvalidUpgradeParameters, - TokenExists, } /// The set of registered agents @@ -723,6 +728,12 @@ pub mod pallet { metadata: AssetMetadata, pays_fee: PaysFee, ) -> Result<(), DispatchError> { + let bridge_location = Location::new(2, [GlobalConsensus(T::EthereumNetwork::get())]); + let mut location = location.clone(); + location + .reanchor(&bridge_location, &T::UniversalLocation::get()) + .map_err(|_| Error::::LocationConversionFailed)?; + // Record the token id or fail if it has already been created let token_id = TokenIdOf::convert_location(&location) .ok_or(Error::::LocationConversionFailed)?; diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index 98bd3da9ab27..d8c905449d9a 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -170,6 +170,7 @@ parameter_types! { pub const RelayLocation: Location = Location::parent(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(1013)].into(); + pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; } pub const DOT: u128 = 10_000_000_000; @@ -177,8 +178,8 @@ pub const DOT: u128 = 10_000_000_000; parameter_types! { pub TreasuryAccount: AccountId = PalletId(*b"py/trsry").into_account_truncating(); pub Fee: u64 = 1000; - pub const RococoNetwork: NetworkId = NetworkId::Rococo; pub const InitialFunding: u128 = 1_000_000_000_000; + pub BridgeHubParaId: ParaId = ParaId::new(1002); pub AssetHubParaId: ParaId = ParaId::new(1000); pub TestParaId: u32 = 2000; pub Parameters: PricingParameters = PricingParameters { @@ -188,7 +189,6 @@ parameter_types! { multiplier: FixedU128::from_rational(4, 3) }; pub const InboundDeliveryCost: u128 = 1_000_000_000; - } #[cfg(feature = "runtime-benchmarks")] @@ -208,6 +208,8 @@ impl crate::Config for Test { type DefaultPricingParameters = Parameters; type WeightInfo = (); type InboundDeliveryCost = InboundDeliveryCost; + type UniversalLocation = UniversalLocation; + type EthereumNetwork = EthereumNetwork; #[cfg(feature = "runtime-benchmarks")] type Helper = (); } diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index ba016b9b4854..0745f435ba05 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -549,7 +549,7 @@ fn charge_fee_for_create_agent() { assert_ok!(EthereumSystem::create_agent(origin.clone())); let fee_charged = initial_sovereign_balance - Balances::balance(&sovereign_account); - assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal,)); + assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal)); // assert sovereign_balance decreased by (fee.base_fee + fee.delivery_fee) let message = Message { @@ -631,3 +631,177 @@ fn no_genesis_build_is_uninitialized() { assert!(!EthereumSystem::is_initialized(), "Ethereum initialized."); }); } + +#[test] +fn register_token_with_root_yeilds_success() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(1, []); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id = + hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); + let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_relative_address_reanchors_to_ethereum_and_succeeds() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(1, []); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id = + hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); + let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_complex_location_simplifies_and_succeeds() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(2, [GlobalConsensus(Kusama)]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id = + hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); + let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_doubled_bridged_polkadot_location_succeeds() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(2, [GlobalConsensus(Rococo)]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id = + hex!("62e8f33b7fb0e7e2d2276564061a2f3c7bcb612e733b8bf5733ea16cee0ecba6").into(); + let expected_location = Location::new(1, [GlobalConsensus(Rococo)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_ethereum_address_reanchors_to_relative_and_fails() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 11155111 })]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_noop!( + EthereumSystem::register_token(origin, versioned_location, asset_metadata), + Error::::LocationConversionFailed + ); + }); +} + +#[test] +fn register_token_with_double_bridged_ethereum_address_succeeds() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + const NETWORK: NetworkId = Ethereum { chain_id: 1 }; + let location = Location::new(2, [GlobalConsensus(NETWORK)]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id: H256 = + hex!("37fd94739deb1c2a8929b45a4f70ffcb52de8b54791609ee13ee0a2b33730269").into(); + let expected_location = Location::new(1, [GlobalConsensus(NETWORK)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_signed_yeilds_bad_origin() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::signed([14; 32].into()); + let location = Location::new(1, [Parachain(2000)]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_noop!( + EthereumSystem::register_token(origin, versioned_location, asset_metadata), + BadOrigin + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index dd2d1197db76..53e9cf149c4b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -294,9 +294,11 @@ fn transfer_relay_token() { ); BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); - let asset_id: Location = Location { parents: 1, interior: [GlobalConsensus(Westend)].into() }; + let asset_id: Location = Location { parents: 1, interior: [].into() }; + let expected_asset_id: Location = + Location { parents: 1, interior: [GlobalConsensus(Westend)].into() }; - let token_id = TokenIdOf::convert_location(&asset_id).unwrap(); + let expected_token_id = TokenIdOf::convert_location(&expected_asset_id).unwrap(); let ethereum_sovereign: AccountId = GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(&Location::new( @@ -388,7 +390,7 @@ fn transfer_relay_token() { let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, command: Command::SendNativeToken { - token_id, + token_id: expected_token_id, destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, amount: TOKEN_AMOUNT, fee: XCM_FEE, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 26857d262c20..1e096f19ef80 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -188,6 +188,8 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type UniversalLocation = UniversalLocation; + type EthereumNetwork = EthereumNetwork; } #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 621d39af5af0..fee9f9a2f610 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -188,6 +188,8 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type UniversalLocation = UniversalLocation; + type EthereumNetwork = EthereumNetwork; } #[cfg(feature = "runtime-benchmarks")] From 7a1242f60de37cb28255c9d36538adbb51ba1fff Mon Sep 17 00:00:00 2001 From: Ron Date: Thu, 5 Sep 2024 21:54:28 +0800 Subject: [PATCH 286/382] make register token relative (#177) Co-authored-by: Alistair Singh From 49a68132882e58872411c5c0278b13a008b3682b Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:57:58 +0100 Subject: [PATCH 287/382] GHA Migration - test-misc (#5385) Closes: https://github.com/paritytech/ci_cd/issues/1018 ![image](https://github.com/user-attachments/assets/b434d809-2c38-47e9-8a62-448f32cf24cb) Added DAG similar to how it was on Gitlab --------- Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> --- .../scripts}/check-each-crate.py | 10 +- .github/workflows/tests-misc.yml | 413 ++++++++++++++++++ .github/workflows/tests.yml | 9 +- .gitlab-ci.yml | 87 +--- .gitlab/pipeline/test.yml | 294 ------------- 5 files changed, 438 insertions(+), 375 deletions(-) rename {.gitlab => .github/scripts}/check-each-crate.py (81%) create mode 100644 .github/workflows/tests-misc.yml diff --git a/.gitlab/check-each-crate.py b/.github/scripts/check-each-crate.py similarity index 81% rename from .gitlab/check-each-crate.py rename to .github/scripts/check-each-crate.py index 9b654f8071ac..7a53e812ddfc 100755 --- a/.gitlab/check-each-crate.py +++ b/.github/scripts/check-each-crate.py @@ -9,6 +9,7 @@ # # - `target_group`: Integer starting from 1, the group this script should execute. # - `groups_total`: Integer starting from 1, total number of groups. +# - `disable_forklift`: Boolean, whether to disable forklift or not. import subprocess, sys @@ -31,6 +32,9 @@ target_group = int(sys.argv[1]) - 1 groups_total = int(sys.argv[2]) +disable_forklift = bool(sys.argv[3] if len(sys.argv) > 3 else False) + +print(f"Target group: {target_group}, Total groups: {groups_total}, Disable forklift: {disable_forklift}", file=sys.stderr) if len(crates) == 0: print("No crates detected!", file=sys.stderr) @@ -55,7 +59,11 @@ print(f"Checking {crates[crate][0]}", file=sys.stderr) - res = subprocess.run(["forklift", "cargo", "check", "--locked"], cwd = crates[crate][1]) + cmd = ["cargo", "check", "--locked"] + + cmd.insert(0, 'forklift') if not disable_forklift else None + + res = subprocess.run(cmd, cwd = crates[crate][1]) if res.returncode != 0: sys.exit(1) diff --git a/.github/workflows/tests-misc.yml b/.github/workflows/tests-misc.yml new file mode 100644 index 000000000000..824e8c11c2a4 --- /dev/null +++ b/.github/workflows/tests-misc.yml @@ -0,0 +1,413 @@ +name: tests misc + +on: + push: + branches: + - master + pull_request: + types: [ opened, synchronize, reopened, ready_for_review ] + merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +# Jobs in this workflow depend on each other, only for limiting peak amount of spawned workers + +jobs: + #changes: + # permissions: + # pull-requests: read + # uses: ./.github/workflows/reusable-check-changed-files.yml + + set-image: + # needs: [ changes ] + # if: needs.changes.outputs.rust || needs.changes.outputs.current-workflow + # GitHub Actions allows using 'env' in a container context. + # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 + # This workaround sets the container image for each job using 'set-image' job output. + runs-on: ubuntu-latest + outputs: + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT + # By default, we use spot machines that can be terminated at any time. + # Merge queues use persistent runners to avoid kicking off from queue when the runner is terminated. + - id: set_runner + run: | + # Run merge queues on persistent runners + if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT + else + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + fi + + # more information about this job can be found here: + # https://github.com/paritytech/substrate/pull/3778 + test-full-crypto-feature: + needs: [ set-image ] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-C debug-assertions" + RUST_BACKTRACE: 1 + steps: + - name: Checkout + uses: actions/checkout@v4.1.7 + + - name: script + run: | + cd substrate/primitives/core/ + forklift cargo build --locked --no-default-features --features full_crypto + cd ../application-crypto + forklift cargo build --locked --no-default-features --features full_crypto + + test-frame-examples-compile-to-wasm: + timeout-minutes: 20 + # into one job + needs: [ set-image, test-full-crypto-feature ] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-C debug-assertions" + RUST_BACKTRACE: 1 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: script + run: | + cd substrate/frame/examples/offchain-worker/ + forklift cargo build --locked --target=wasm32-unknown-unknown --no-default-features + cd ../basic + forklift cargo build --locked --target=wasm32-unknown-unknown --no-default-features + + test-frame-ui: + timeout-minutes: 60 + needs: [ set-image, test-frame-examples-compile-to-wasm ] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-C debug-assertions -D warnings" + RUST_BACKTRACE: 1 + WASM_BUILD_NO_COLOR: 1 + WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" + # Ensure we run the UI tests. + RUN_UI_TESTS: 1 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: script + run: | + forklift cargo test --locked -q --profile testnet -p frame-support-test --features=frame-feature-testing,no-metadata-docs,try-runtime,experimental + forklift cargo test --locked -q --profile testnet -p frame-support-test --features=frame-feature-testing,frame-feature-testing-2,no-metadata-docs,try-runtime,experimental + forklift cargo test --locked -q --profile testnet -p xcm-procedural + forklift cargo test --locked -q --profile testnet -p frame-election-provider-solution-type + forklift cargo test --locked -q --profile testnet -p sp-api-test + # There is multiple version of sp-runtime-interface in the repo. So we point to the manifest. + forklift cargo test --locked -q --profile testnet --manifest-path substrate/primitives/runtime-interface/Cargo.toml + + test-deterministic-wasm: + timeout-minutes: 20 + needs: [ set-image, test-frame-ui ] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + WASM_BUILD_NO_COLOR: 1 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: script + run: | + # build runtime + forklift cargo build -q --locked --release -p westend-runtime -p rococo-runtime + # make checksum + sha256sum target/release/wbuild/*-runtime/target/wasm32-unknown-unknown/release/*.wasm > checksum.sha256 + cargo clean + # build again + forklift cargo build -q --locked --release -p westend-runtime -p rococo-runtime + # confirm checksum + sha256sum -c checksum.sha256 + + cargo-check-benches-branches: + needs: [ set-image ] + if: ${{ github.event_name == 'pull_request' }} + timeout-minutes: 60 + outputs: + branch: ${{ steps.branch.outputs.branch }} + runs-on: ubuntu-latest + steps: + - name: Branch + id: branch + run: | + echo "branch=['${{ github.base_ref }}', '${{ github.head_ref }}']" >> $GITHUB_OUTPUT + + cargo-check-benches: + needs: [ set-image, cargo-check-benches-branches ] + timeout-minutes: 60 + strategy: + matrix: + branch: ${{ fromJSON(needs.cargo-check-benches-branches.outputs.branch) }} + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ matrix.branch }} + + - name: script + run: | + ARTIFACTS_DIR=./artifacts + BENCH_TRIE_READ=::trie::read::small + BENCH_NODE_IMPORT=::node::import::sr25519::transfer_keep_alive::paritydb::small + mkdir -p $ARTIFACTS_DIR + + SKIP_WASM_BUILD=1 forklift cargo check --locked --benches --all; + forklift cargo run --locked --release -p node-bench -- $BENCH_TRIE_READ --json | tee $ARTIFACTS_DIR/bench_trie_read_small.json; + forklift cargo run --locked --release -p node-bench -- $BENCH_NODE_IMPORT --json | tee $ARTIFACTS_DIR/bench_transfer_keep_alive.json + + - name: Upload artifacts + uses: actions/upload-artifact@v4.3.6 + with: + path: ./artifacts + name: cargo-check-benches-${{ matrix.branch }}-${{ github.sha }} + retention-days: 1 + + node-bench-regression-guard: + timeout-minutes: 20 + runs-on: arc-runners-polkadot-sdk + needs: [ set-image, cargo-check-benches ] + steps: + - name: Checkout + uses: actions/checkout@v4.1.7 + + - name: Download artifact (master run) + uses: actions/download-artifact@v4.1.8 + with: + name: cargo-check-benches-${{ github.base_ref }}-${{ github.sha }} + path: ./artifacts/master + + - name: Download artifact (current run) + uses: actions/download-artifact@v4.1.8 + with: + name: cargo-check-benches-${{ github.head_ref }}-${{ github.sha }} + path: ./artifacts/current + + - name: script + id: compare + run: | + docker run --rm \ + -v $PWD/artifacts/master:/artifacts/master \ + -v $PWD/artifacts/current:/artifacts/current \ + paritytech/node-bench-regression-guard:latest \ + node-bench-regression-guard --reference /artifacts/master --compare-with /artifacts/current + + if [ $? -ne 0 ]; then + FAILED_MSG='### node-bench-regression-guard failed ❌, check the regression in *cargo-check-benches* job' + echo $FAILED_MSG + echo $FAILED_MSG >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo "### node-bench-regression-guard passed ✅" >> $GITHUB_STEP_SUMMARY + fi + + test-node-metrics: + needs: [ set-image ] + timeout-minutes: 30 + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4.1.7 + + - name: Run tests + id: tests + env: + RUST_TOOLCHAIN: stable + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + run: | + forklift cargo build --bin polkadot-execute-worker --bin polkadot-prepare-worker --profile testnet --verbose --locked + mkdir -p ./artifacts + forklift cargo test --profile testnet --locked --features=runtime-metrics -p polkadot-node-metrics > ./artifacts/log.txt + echo "Metrics test passed" + + - name: Upload artifacts if failed + if: ${{ steps.tests.outcome != 'success' }} + uses: actions/upload-artifact@v4.3.6 + with: + name: node-metrics-failed + path: ./artifacts + + # more information about this job can be found here: + # https://github.com/paritytech/substrate/pull/6916 + check-tracing: + timeout-minutes: 20 + needs: [ set-image, test-node-metrics ] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4.1.7 + + - name: script + run: | + forklift cargo test --locked --manifest-path ./substrate/primitives/tracing/Cargo.toml --no-default-features + forklift cargo test --locked --manifest-path ./substrate/primitives/tracing/Cargo.toml --no-default-features --features=with-tracing + + check-metadata-hash: + timeout-minutes: 20 + needs: [ set-image, check-tracing ] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4.1.7 + + - name: script + run: | + forklift cargo build --locked -p westend-runtime --features metadata-hash + + cargo-hfuzz: + timeout-minutes: 20 + needs: [ set-image, check-metadata-hash ] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + # max 10s per iteration, 60s per file + HFUZZ_RUN_ARGS: | + --exit_upon_crash + --exit_code_upon_crash 1 + --timeout 10 + --run_time 60 + + # use git version of honggfuzz-rs until v0.5.56 is out, we need a few recent changes: + # https://github.com/rust-fuzz/honggfuzz-rs/pull/75 to avoid breakage on debian + # https://github.com/rust-fuzz/honggfuzz-rs/pull/81 fix to the above pr + # https://github.com/rust-fuzz/honggfuzz-rs/pull/82 fix for handling absolute CARGO_TARGET_DIR + HFUZZ_BUILD_ARGS: | + --config=patch.crates-io.honggfuzz.git="https://github.com/altaua/honggfuzz-rs" + --config=patch.crates-io.honggfuzz.rev="205f7c8c059a0d98fe1cb912cdac84f324cb6981" + steps: + - name: Checkout + uses: actions/checkout@v4.1.7 + + - name: Run honggfuzz + run: | + cd substrate/primitives/arithmetic/fuzzer + forklift cargo hfuzz build + for target in $(cargo read-manifest | jq -r '.targets | .[] | .name'); + do + forklift cargo hfuzz run "$target" || { printf "fuzzing failure for %s\n" "$target"; exit 1; }; + done + + - name: Upload artifacts + uses: actions/upload-artifact@v4.3.6 + with: + path: substrate/primitives/arithmetic/fuzzer/hfuzz_workspace/ + name: hfuzz-${{ github.sha }} + + cargo-check-each-crate: + timeout-minutes: 140 + needs: [ set-image ] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + RUSTFLAGS: "-D warnings" + CI_JOB_NAME: cargo-check-each-crate + strategy: + matrix: + index: [ 1,2,3,4,5,6,7 ] # 7 parallel jobs + steps: + - name: Checkout + uses: actions/checkout@v4.1.7 + + - name: Check Rust + run: | + rustup show + rustup +nightly show + + - name: script + run: | + mkdir -p /github/home/.forklift + cp .forklift/config.toml /github/home/.forklift/config.toml + PYTHONUNBUFFERED=x .github/scripts/check-each-crate.py ${{ matrix.index }} ${{ strategy.job-total }} + + # TODO: enable when we have a macos Self-Hosted runners + # cargo-check-each-crate-macos: + # timeout-minutes: 120 + # needs: [ set-image ] + # runs-on: macos-latest + # env: + # RUSTFLAGS: "-D warnings" + # CI_JOB_NAME: cargo-check-each-crate + # IMAGE: ${{ needs.set-image.outputs.IMAGE }} + # strategy: + # fail-fast: false + # matrix: + # index: [ 1,2,3,4,5,6,7,8,9,10 ] # 10 parallel jobs + # steps: + # - name: Checkout + # uses: actions/checkout@v4.1.7 + # + # - run: | + # VERSION=$(echo $IMAGE | sed -E 's/.*:bullseye-([^-]+)-.*/\1/') + # echo $VERSION + # echo "VERSION=$VERSION" >> $GITHUB_ENV + # + # - run: | + # rustup install $VERSION + # rustup default $VERSION + # + # - name: Check Rust + # run: | + # rustup show + # rustup +nightly show + # + # - name: MacOS Deps + # run: | + # brew install protobuf openssl pkg-config zlib xz zstd llvm jq curl gcc make cmake + # rustup target add wasm32-unknown-unknown --toolchain $VERSION + # rustup component add rust-src rustfmt clippy --toolchain $VERSION + # + # - name: script + # run: | + # PYTHONUNBUFFERED=x .github/scripts/check-each-crate.py ${{ matrix.index }} ${{ strategy.job-total }} True + + confirm-required-test-misc-jobs-passed: + runs-on: ubuntu-latest + name: All test misc tests passed + # If any new job gets added, be sure to add it to this array + needs: + - test-full-crypto-feature + - test-frame-examples-compile-to-wasm + - test-frame-ui + - cargo-check-benches + - node-bench-regression-guard + - test-node-metrics + - check-tracing + - cargo-check-each-crate + # - cargo-hfuzz remove from required for now, as it's flaky + steps: + - run: echo '### Good job! All the required tests passed 🚀' >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 25761fb94fd3..ed2ef07736b9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,7 +5,7 @@ on: branches: - master pull_request: - types: [opened, synchronize, reopened, ready_for_review] + types: [ opened, synchronize, reopened, ready_for_review ] merge_group: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -44,7 +44,7 @@ jobs: # This job runs all benchmarks defined in the `/bin/node/runtime` once to check that there are no errors. quick-benchmarks: - needs: [set-image] + needs: [ set-image ] # if: ${{ needs.changes.outputs.rust }} runs-on: ${{ needs.set-image.outputs.RUNNER }} timeout-minutes: 60 @@ -63,7 +63,7 @@ jobs: # cf https://github.com/paritytech/polkadot-sdk/issues/1652 test-syscalls: - needs: [set-image] + needs: [ set-image ] # if: ${{ needs.changes.outputs.rust }} runs-on: ${{ needs.set-image.outputs.RUNNER }} timeout-minutes: 60 @@ -87,8 +87,9 @@ jobs: run: | echo "The x86_64 syscalls used by the worker binaries have changed. Please review if this is expected and update polkadot/scripts/list-syscalls/*-worker-syscalls as needed." >> $GITHUB_STEP_SUMMARY + cargo-check-all-benches: - needs: [set-image] + needs: [ set-image ] # if: ${{ needs.changes.outputs.rust }} runs-on: ${{ needs.set-image.outputs.RUNNER }} timeout-minutes: 60 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5b581c45fb85..8b4ca48150b1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -21,7 +21,7 @@ workflow: - if: $CI_COMMIT_BRANCH variables: - CI_IMAGE: !reference [.ci-unified, variables, CI_IMAGE] + CI_IMAGE: !reference [ .ci-unified, variables, CI_IMAGE ] # BUILDAH_IMAGE is defined in group variables BUILDAH_COMMAND: "buildah --storage-driver overlay2" RELENG_SCRIPTS_BRANCH: "master" @@ -39,7 +39,7 @@ default: - runner_system_failure - unknown_failure - api_failure - cache: {} + cache: { } interruptible: true .collect-artifacts: @@ -68,8 +68,8 @@ default: .common-before-script: before_script: - - !reference [.job-switcher, before_script] - - !reference [.pipeline-stopper-vars, script] + - !reference [ .job-switcher, before_script ] + - !reference [ .pipeline-stopper-vars, script ] .job-switcher: before_script: @@ -78,8 +78,8 @@ default: .kubernetes-env: image: "${CI_IMAGE}" before_script: - - !reference [.common-before-script, before_script] - - !reference [.prepare-env, before_script] + - !reference [ .common-before-script, before_script ] + - !reference [ .prepare-env, before_script ] tags: - kubernetes-parity-build @@ -107,12 +107,12 @@ default: .docker-env: image: "${CI_IMAGE}" variables: - FL_FORKLIFT_VERSION: !reference [.forklift, variables, FL_FORKLIFT_VERSION] + FL_FORKLIFT_VERSION: !reference [ .forklift, variables, FL_FORKLIFT_VERSION ] before_script: - - !reference [.common-before-script, before_script] - - !reference [.prepare-env, before_script] - - !reference [.rust-info-script, script] - - !reference [.forklift-cache, before_script] + - !reference [ .common-before-script, before_script ] + - !reference [ .prepare-env, before_script ] + - !reference [ .rust-info-script, script ] + - !reference [ .forklift-cache, before_script ] tags: - linux-docker @@ -269,56 +269,6 @@ remove-cancel-pipeline-message: trigger: project: "parity/infrastructure/ci_cd/pipeline-stopper" -cancel-pipeline-cargo-check-benches1: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-benches 1/2" - -cancel-pipeline-cargo-check-benches2: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-benches 2/2" - -cancel-pipeline-cargo-check-each-crate-1: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate 1/6" - -cancel-pipeline-cargo-check-each-crate-2: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate 2/6" - -cancel-pipeline-cargo-check-each-crate-3: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate 3/6" - -cancel-pipeline-cargo-check-each-crate-4: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate 4/6" - -cancel-pipeline-cargo-check-each-crate-5: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate 5/6" - -cancel-pipeline-cargo-check-each-crate-6: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate 6/6" - -cancel-pipeline-cargo-check-each-crate-macos: - extends: .cancel-pipeline-template - needs: - - job: cargo-check-each-crate-macos - -cancel-pipeline-check-tracing: - extends: .cancel-pipeline-template - needs: - - job: check-tracing - cancel-pipeline-build-linux-stable: extends: .cancel-pipeline-template needs: @@ -334,21 +284,6 @@ cancel-pipeline-build-linux-substrate: needs: - job: build-linux-substrate -cancel-pipeline-test-node-metrics: - extends: .cancel-pipeline-template - needs: - - job: test-node-metrics - -cancel-pipeline-test-frame-ui: - extends: .cancel-pipeline-template - needs: - - job: test-frame-ui - -cancel-pipeline-test-frame-examples-compile-to-wasm: - extends: .cancel-pipeline-template - needs: - - job: test-frame-examples-compile-to-wasm - cancel-pipeline-build-short-benchmark: extends: .cancel-pipeline-template needs: diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index ca3a2394fb39..00a0aa2c9771 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -137,159 +137,6 @@ test-rustdoc: script: - time cargo doc --workspace --all-features --no-deps -test-node-metrics: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - - .collect-artifacts-short - variables: - RUST_TOOLCHAIN: stable - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - script: - # Build the required workers. - - cargo build --bin polkadot-execute-worker --bin polkadot-prepare-worker --profile testnet --verbose --locked - - mkdir -p artifacts - - time cargo test --profile testnet - --locked - --features=runtime-metrics -p polkadot-node-metrics > artifacts/log.txt - -test-deterministic-wasm: - stage: test - extends: - - .docker-env - - .common-refs - # DAG - needs: - - job: test-frame-ui - artifacts: false - script: - # build runtime - - WASM_BUILD_NO_COLOR=1 cargo build -q --locked --release -p westend-runtime -p rococo-runtime - # make checksum - - sha256sum target/release/wbuild/*-runtime/target/wasm32-unknown-unknown/release/*.wasm > checksum.sha256 - - cargo clean - # build again - - WASM_BUILD_NO_COLOR=1 cargo build -q --locked --release -p westend-runtime -p rococo-runtime - # confirm checksum - - sha256sum -c checksum.sha256 - -cargo-check-benches: - stage: test - artifacts: - expire_in: 10 days - variables: - CI_JOB_NAME: "cargo-check-benches" - extends: - - .docker-env - - .common-refs - - .run-immediately - - .collect-artifacts - - .pipeline-stopper-artifacts - before_script: - # TODO: DON'T FORGET TO CHANGE FOR PROD VALUES!!! - # merges in the master branch on PRs. skip if base is not master - - 'if [ $CI_COMMIT_REF_NAME != "master" ]; then - BASE=$(curl -s -H "Authorization: Bearer ${GITHUB_PR_TOKEN}" https://api.github.com/repos/paritytech-stg/polkadot-sdk/pulls/${CI_COMMIT_REF_NAME} | jq -r .base.ref); - printf "Merging base branch %s\n" "${BASE:=master}"; - if [ $BASE != "master" ]; then - echo "$BASE is not master, skipping merge"; - else - git config user.email "ci@gitlab.parity.io"; - git fetch origin "refs/heads/${BASE}"; - git merge --verbose --no-edit FETCH_HEAD; - fi - fi' - - !reference [.forklift-cache, before_script] - parallel: 2 - script: - - mkdir -p ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA - # this job is executed in parallel on two runners - - echo "___Running benchmarks___"; - - case ${CI_NODE_INDEX} in - 1) - SKIP_WASM_BUILD=1 time cargo check --locked --benches --all; - cargo run --locked --release -p node-bench -- ::trie::read::small --json - | tee ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA/::trie::read::small.json; - echo "___Cache could be uploaded___"; - ;; - 2) - cargo run --locked --release -p node-bench -- ::node::import::sr25519::transfer_keep_alive::paritydb::small --json - | tee ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA/::node::import::sr25519::transfer_keep_alive::paritydb::small.json - ;; - esac - -node-bench-regression-guard: - # it's not belong to `build` semantically, but dag jobs can't depend on each other - # within the single stage - https://gitlab.com/gitlab-org/gitlab/-/issues/30632 - # more: https://github.com/paritytech/substrate/pull/8519#discussion_r608012402 - stage: build - extends: - - .docker-env - - .common-refs - needs: - # this is a DAG - - job: cargo-check-benches - artifacts: true - # polls artifact from master to compare with current result - # need to specify both parallel jobs from master because of the bug - # https://gitlab.com/gitlab-org/gitlab/-/issues/39063 - - project: $CI_PROJECT_PATH - job: "cargo-check-benches 1/2" - ref: master - artifacts: true - - project: $CI_PROJECT_PATH - job: "cargo-check-benches 2/2" - ref: master - artifacts: true - variables: - CI_IMAGE: "paritytech/node-bench-regression-guard:latest" - before_script: [""] - script: - - if [ $(ls -la artifacts/benches/ | grep master | wc -l) == 0 ]; then - echo "Couldn't find master artifacts"; - exit 1; - fi - - echo "------- IMPORTANT -------" - - echo "node-bench-regression-guard depends on the results of a cargo-check-benches job" - - echo "In case of this job failure, check your pipeline's cargo-check-benches" - - "node-bench-regression-guard --reference artifacts/benches/master-* - --compare-with artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - after_script: [""] - -# if this fails run `bot update-ui` in the Pull Request or "./scripts/update-ui-tests.sh" locally -# see ./docs/contributor/CONTRIBUTING.md#ui-tests -test-frame-ui: - stage: test - extends: - - .docker-env - - .common-refs - # DAG - needs: - - job: test-frame-examples-compile-to-wasm - artifacts: false - variables: - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-C debug-assertions -D warnings" - RUST_BACKTRACE: 1 - WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" - # Ensure we run the UI tests. - RUN_UI_TESTS: 1 - script: - - time cargo test --locked -q --profile testnet -p frame-support-test --features=frame-feature-testing,no-metadata-docs,try-runtime,experimental - - time cargo test --locked -q --profile testnet -p frame-support-test --features=frame-feature-testing,frame-feature-testing-2,no-metadata-docs,try-runtime,experimental - - time cargo test --locked -q --profile testnet -p xcm-procedural - - time cargo test --locked -q --profile testnet -p frame-election-provider-solution-type - - time cargo test --locked -q --profile testnet -p sp-api-test - # There is multiple version of sp-runtime-interface in the repo. So we point to the manifest. - - time cargo test --locked -q --profile testnet --manifest-path substrate/primitives/runtime-interface/Cargo.toml - - cat /cargo_target_dir/debug/.fingerprint/memory_units-759eddf317490d2b/lib-memory_units.json || true - quick-benchmarks-omni: stage: test extends: @@ -306,144 +153,3 @@ quick-benchmarks-omni: script: - time cargo build --locked --quiet --release -p asset-hub-westend-runtime --features runtime-benchmarks - time cargo run --locked --release -p frame-omni-bencher --quiet -- v1 benchmark pallet --runtime target/release/wbuild/asset-hub-westend-runtime/asset_hub_westend_runtime.compact.compressed.wasm --all --steps 2 --repeat 1 --quiet - -test-frame-examples-compile-to-wasm: - # into one job - stage: test - extends: - - .docker-env - - .common-refs - # DAG - needs: - - job: test-full-crypto-feature - artifacts: false - variables: - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-C debug-assertions" - RUST_BACKTRACE: 1 - script: - - cd ./substrate/frame/examples/offchain-worker/ - - cargo build --locked --target=wasm32-unknown-unknown --no-default-features - - cd ../basic - - cargo build --locked --target=wasm32-unknown-unknown --no-default-features - # FIXME - allow_failure: true - -# more information about this job can be found here: -# https://github.com/paritytech/substrate/pull/6916 -check-tracing: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - - .pipeline-stopper-artifacts - script: - # with-tracing must be explicitly activated, we run a test to ensure this works as expected in both cases - - time cargo test --locked --manifest-path ./substrate/primitives/tracing/Cargo.toml --no-default-features - - time cargo test --locked --manifest-path ./substrate/primitives/tracing/Cargo.toml --no-default-features --features=with-tracing - -# Check that `westend-runtime` compiles with the `metadata-hash` feature enabled. -check-metadata-hash: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - - .pipeline-stopper-artifacts - script: - - time cargo build --locked -p westend-runtime --features metadata-hash - -# more information about this job can be found here: -# https://github.com/paritytech/substrate/pull/3778 -test-full-crypto-feature: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - variables: - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-C debug-assertions" - RUST_BACKTRACE: 1 - script: - - cd substrate/primitives/core/ - - time cargo build --locked --no-default-features --features full_crypto - - cd ../application-crypto - - time cargo build --locked --no-default-features --features full_crypto - -cargo-check-each-crate: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - # - .collect-artifacts - variables: - RUSTFLAGS: "-D warnings" - # $CI_JOB_NAME is set manually so that cache could be shared for all jobs - # "cargo-check-each-crate I/N" jobs - CI_JOB_NAME: cargo-check-each-crate - timeout: 2h - script: - - PYTHONUNBUFFERED=x time .gitlab/check-each-crate.py "$CI_NODE_INDEX" "$CI_NODE_TOTAL" - parallel: 6 - -cargo-check-each-crate-macos: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - # - .collect-artifacts - before_script: - # skip timestamp script, the osx bash doesn't support printf %()T - - !reference [.job-switcher, before_script] - - !reference [.rust-info-script, script] - - !reference [.pipeline-stopper-vars, script] - variables: - SKIP_WASM_BUILD: 1 - script: - # TODO: use parallel jobs, as per cargo-check-each-crate, once more Mac runners are available - # - time ./scripts/ci/gitlab/check-each-crate.py 1 1 - - time cargo check --workspace --locked - timeout: 2h - tags: - - osx - -cargo-hfuzz: - stage: test - extends: - - .docker-env - - .common-refs - # DAG - needs: - - job: check-tracing - artifacts: false - variables: - # max 10s per iteration, 60s per file - HFUZZ_RUN_ARGS: > - --exit_upon_crash - --exit_code_upon_crash 1 - --timeout 10 - --run_time 60 - # use git version of honggfuzz-rs until v0.5.56 is out, we need a few recent changes: - # https://github.com/rust-fuzz/honggfuzz-rs/pull/75 to avoid breakage on debian - # https://github.com/rust-fuzz/honggfuzz-rs/pull/81 fix to the above pr - # https://github.com/rust-fuzz/honggfuzz-rs/pull/82 fix for handling absolute CARGO_TARGET_DIR - HFUZZ_BUILD_ARGS: > - --config=patch.crates-io.honggfuzz.git="https://github.com/altaua/honggfuzz-rs" - --config=patch.crates-io.honggfuzz.rev="205f7c8c059a0d98fe1cb912cdac84f324cb6981" - artifacts: - name: "hfuzz-$CI_COMMIT_SHORT_SHA" - expire_in: 7 days - when: on_failure - paths: - - substrate/primitives/arithmetic/fuzzer/hfuzz_workspace/ - script: - - cd ./substrate/primitives/arithmetic/fuzzer - - cargo hfuzz build - - for target in $(cargo read-manifest | jq -r '.targets | .[] | .name'); do - cargo hfuzz run "$target" || { printf "fuzzing failure for %s\n" "$target"; exit 1; }; done From 46d70746594ad2af6c81144ec687f9ae0ba05f88 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 22:10:22 +0800 Subject: [PATCH 288/382] Remove Parity's CI --- .github/workflows/build-misc.yml | 98 ---- .github/workflows/build-publish-images.yml | 525 ------------------ .../workflows/check-cargo-check-runtimes.yml | 136 ----- .../workflows/check-frame-omni-bencher.yml | 85 --- .github/workflows/check-getting-started.yml | 296 ---------- .github/workflows/checks.yml | 109 ---- .github/workflows/cmd.yml | 452 --------------- .github/workflows/command-backport.yml | 66 --- .github/workflows/docs.yml | 141 ----- .github/workflows/fork-sync-action.yml | 20 - .../misc-update-wishlist-leaderboard.yml | 37 -- .../reusable-check-changed-files.yml | 59 -- .github/workflows/runtimes-matrix.json | 98 ---- .github/workflows/subsystem-benchmarks.yml | 136 ----- 14 files changed, 2258 deletions(-) delete mode 100644 .github/workflows/build-misc.yml delete mode 100644 .github/workflows/build-publish-images.yml delete mode 100644 .github/workflows/check-cargo-check-runtimes.yml delete mode 100644 .github/workflows/check-frame-omni-bencher.yml delete mode 100644 .github/workflows/check-getting-started.yml delete mode 100644 .github/workflows/checks.yml delete mode 100644 .github/workflows/cmd.yml delete mode 100644 .github/workflows/command-backport.yml delete mode 100644 .github/workflows/docs.yml delete mode 100644 .github/workflows/fork-sync-action.yml delete mode 100644 .github/workflows/misc-update-wishlist-leaderboard.yml delete mode 100644 .github/workflows/reusable-check-changed-files.yml delete mode 100644 .github/workflows/runtimes-matrix.json delete mode 100644 .github/workflows/subsystem-benchmarks.yml diff --git a/.github/workflows/build-misc.yml b/.github/workflows/build-misc.yml deleted file mode 100644 index c85549b37999..000000000000 --- a/.github/workflows/build-misc.yml +++ /dev/null @@ -1,98 +0,0 @@ -name: Build Misc - -on: - push: - branches: - - master - pull_request: - types: [ opened, synchronize, reopened, ready_for_review ] - merge_group: - - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - set-image: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - RUNNER: ${{ steps.set_runner.outputs.RUNNER }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - - id: set_runner - run: | - # Run merge queues on persistent runners - if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then - echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT - else - echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT - fi - - build-runtimes-polkavm: - timeout-minutes: 20 - needs: [ set-image ] - runs-on: ${{ needs.set-image.outputs.RUNNER }} - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Check Rust - run: | - rustup show - rustup +nightly show - - - name: Build - env: - SUBSTRATE_RUNTIME_TARGET: riscv - run: | - forklift cargo check -p minimal-template-runtime - forklift cargo check -p westend-runtime - forklift cargo check -p rococo-runtime - forklift cargo check -p polkadot-test-runtime - - build-subkey: - timeout-minutes: 20 - needs: [ set-image ] - runs-on: ${{ needs.set-image.outputs.RUNNER }} - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Check Rust - run: | - rustup show - rustup +nightly show - - - name: Build - env: - SKIP_WASM_BUILD: 1 - run: | - cd ./substrate/bin/utils/subkey - forklift cargo build --locked --release - - confirm-required-build-misc-jobs-passed: - runs-on: ubuntu-latest - name: All build misc jobs passed - # If any new job gets added, be sure to add it to this array - needs: - [ - build-runtimes-polkavm, - build-subkey - ] - steps: - - run: echo '### Good job! All the build misc tests passed 🚀' >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/build-publish-images.yml b/.github/workflows/build-publish-images.yml deleted file mode 100644 index 735b727e58b6..000000000000 --- a/.github/workflows/build-publish-images.yml +++ /dev/null @@ -1,525 +0,0 @@ -# GHA for build-* -name: Build and push images - -on: - push: - branches: - - master - pull_request: - types: [opened, synchronize, reopened, ready_for_review, labeled] - merge_group: -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true -env: - COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} - -jobs: - - # - # - # - set-image: - ## TODO: remove when ready - if: contains(github.event.label.name, 'GHA-migration') || contains(github.event.pull_request.labels.*.name, 'GHA-migration') - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - runs-on: ubuntu-latest - env: - BRANCH_NAME: ${{ github.head_ref || github.ref_name }} - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - RUNNER: ${{ steps.set_runner.outputs.RUNNER }} - REF_NAME: ${{ steps.set_vars.outputs.REF_NAME }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - - id: log - run: | - echo ${BRANCH_NAME} - echo ${COMMIT_SHA} - - id: set_vars - run: | - echo "REF_NAME=${BRANCH_NAME//\//-}" >> $GITHUB_OUTPUT - # By default we use spot machines that can be terminated at any time. - # Merge queues use persistent runners to avoid kicking off from queue when the runner is terminated. - - id: set_runner - run: | - # Run merge queues on persistent runners - if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then - echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT - else - echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT - fi - -### Build ######################## - - # - # - # - build-linux-stable: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} - timeout-minutes: 60 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - env: - RUST_TOOLCHAIN: stable - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: build - run: | - forklift cargo build --locked --profile testnet --features pyroscope,fast-runtime --bin polkadot --bin polkadot-prepare-worker --bin polkadot-execute-worker - ROCOCO_EPOCH_DURATION=10 ./polkadot/scripts/build-only-wasm.sh rococo-runtime $(pwd)/runtimes/rococo-runtime-10/ - ROCOCO_EPOCH_DURATION=100 ./polkadot/scripts/build-only-wasm.sh rococo-runtime $(pwd)/runtimes/rococo-runtime-100/ - ROCOCO_EPOCH_DURATION=600 ./polkadot/scripts/build-only-wasm.sh rococo-runtime $(pwd)/runtimes/rococo-runtime-600/ - pwd - ls -alR runtimes - - name: pack artifacts - run: | - mkdir -p ./artifacts - VERSION="${{ needs.set-image.outputs.REF_NAME }}" # will be tag or branch name - mv ./target/testnet/polkadot ./artifacts/. - mv ./target/testnet/polkadot-prepare-worker ./artifacts/. - mv ./target/testnet/polkadot-execute-worker ./artifacts/. - mv ./runtimes/ ./artifacts/. - cd artifacts/ - sha256sum polkadot | tee polkadot.sha256 - shasum -c polkadot.sha256 - cd ../ - EXTRATAG="${{ needs.set-image.outputs.REF_NAME }}-${COMMIT_SHA}" - echo "Polkadot version = ${VERSION} (EXTRATAG = ${EXTRATAG})" - echo -n ${VERSION} > ./artifacts/VERSION - echo -n ${EXTRATAG} > ./artifacts/EXTRATAG - echo -n ${GITHUB_RUN_ID} > ./artifacts/BUILD_LINUX_JOB_ID - RELEASE_VERSION=$(./artifacts/polkadot -V | awk '{print $2}'| awk -F "-" '{print $1}') - echo -n "v${RELEASE_VERSION}" > ./artifacts/BUILD_RELEASE_VERSION - cp -r docker/* ./artifacts - - - name: tar - run: tar -cvf artifacts.tar artifacts - - - name: upload artifacts - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} - path: artifacts.tar - retention-days: 1 - - # - # - # - build-linux-stable-cumulus: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} - timeout-minutes: 60 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - env: - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: build - run: | - echo "___Building a binary, please refrain from using it in production since it goes with the debug assertions.___" - forklift cargo build --release --locked -p polkadot-parachain-bin --bin polkadot-parachain - echo "___Packing the artifacts___" - mkdir -p ./artifacts - mv ./target/release/polkadot-parachain ./artifacts/. - echo "___The VERSION is either a tag name or the curent branch if triggered not by a tag___" - echo ${{ needs.set-image.outputs.REF_NAME }} | tee ./artifacts/VERSION - - - name: tar - run: tar -cvf artifacts.tar artifacts - - - name: upload artifacts - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} - path: artifacts.tar - retention-days: 1 - - # - # - # - build-test-parachain: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} - timeout-minutes: 60 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - env: - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: build - run: | - echo "___Building a binary, please refrain from using it in production since it goes with the debug assertions.___" - forklift cargo build --release --locked -p cumulus-test-service --bin test-parachain - - name: pack artifacts - run: | - echo "___Packing the artifacts___" - mkdir -p ./artifacts - mv ./target/release/test-parachain ./artifacts/. - mkdir -p ./artifacts/zombienet - mv ./target/release/wbuild/cumulus-test-runtime/wasm_binary_spec_version_incremented.rs.compact.compressed.wasm ./artifacts/zombienet/. - - - name: tar - run: tar -cvf artifacts.tar artifacts - - - name: upload artifacts - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} - path: artifacts.tar - retention-days: 1 - - # - # - # - build-test-collators: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} - timeout-minutes: 60 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: build - run: | - forklift cargo build --locked --profile testnet -p test-parachain-adder-collator - forklift cargo build --locked --profile testnet -p test-parachain-undying-collator - - name: pack artifacts - run: | - mkdir -p ./artifacts - mv ./target/testnet/adder-collator ./artifacts/. - mv ./target/testnet/undying-collator ./artifacts/. - echo -n "${{ needs.set-image.outputs.REF_NAME }}" > ./artifacts/VERSION - echo -n "${{ needs.set-image.outputs.REF_NAME }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG - echo "adder-collator version = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" - echo "undying-collator version = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" - cp -r ./docker/* ./artifacts - - - name: tar - run: tar -cvf artifacts.tar artifacts - - - name: upload artifacts - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} - path: artifacts.tar - retention-days: 1 - - # - # - # - build-malus: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} - timeout-minutes: 60 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: build - run: | - forklift cargo build --locked --profile testnet -p polkadot-test-malus --bin malus --bin polkadot-prepare-worker --bin polkadot-execute-worker - - name: pack artifacts - run: | - mkdir -p ./artifacts - mv ./target/testnet/malus ./artifacts/. - mv ./target/testnet/polkadot-execute-worker ./artifacts/. - mv ./target/testnet/polkadot-prepare-worker ./artifacts/. - echo -n "${{ needs.set-image.outputs.REF_NAME }}" > ./artifacts/VERSION - echo -n "${{ needs.set-image.outputs.REF_NAME }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG - echo "polkadot-test-malus = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" - cp -r ./docker/* ./artifacts - - - name: tar - run: tar -cvf artifacts.tar artifacts - - - name: upload artifacts - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} - path: artifacts.tar - retention-days: 1 - - # - # - # - build-linux-substrate: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} - timeout-minutes: 60 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - # tldr: we need to checkout the branch HEAD explicitly because of our dynamic versioning approach while building the substrate binary - # see https://github.com/paritytech/ci_cd/issues/682#issuecomment-1340953589 - ref: ${{ github.head_ref || github.ref_name }} - - name: build - run: | - mkdir -p ./artifacts/substrate/ - WASM_BUILD_NO_COLOR=1 forklift cargo build --locked --release -p staging-node-cli - ls -la target/release/ - - name: pack artifacts - run: | - mv target/release/substrate-node ./artifacts/substrate/substrate - echo -n "Substrate version = " - if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then - echo "${{ github.ref_name }}" | tee ./artifacts/substrate/VERSION; - else - ./artifacts/substrate/substrate --version | - cut -d ' ' -f 2 | tee ./artifacts/substrate/VERSION; - fi - sha256sum ./artifacts/substrate/substrate | tee ./artifacts/substrate/substrate.sha256 - cp -r ./docker/dockerfiles/substrate_injected.Dockerfile ./artifacts/substrate/ - - - name: tar - run: tar -cvf artifacts.tar artifacts - - - name: upload artifacts - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} - path: artifacts.tar - retention-days: 1 - - # - # - # - prepare-bridges-zombienet-artifacts: - needs: [set-image] - runs-on: ${{ needs.set-image.outputs.RUNNER }} - timeout-minutes: 60 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: build - run: | - forklift cargo build --locked --profile testnet -p polkadot-test-malus --bin malus --bin polkadot-prepare-worker --bin polkadot-execute-worker - - name: pack artifacts - run: | - mkdir -p ./artifacts/bridges-polkadot-sdk/bridges - cp -r bridges/testing ./artifacts/bridges-polkadot-sdk/bridges/testing - - - name: tar - run: tar -cvf artifacts.tar artifacts - - - name: upload artifacts - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }}-${{ needs.set-image.outputs.REF_NAME }} - path: artifacts.tar - retention-days: 1 - -### Publish ######################## - - # - # - # - build-push-image-test-parachain: - needs: [set-image, build-test-parachain] - runs-on: arc-runners-polkadot-sdk - timeout-minutes: 60 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4.1.8 - with: - name: build-test-parachain-${{ needs.set-image.outputs.REF_NAME }} - - - name: tar - run: tar -xvf artifacts.tar - - - name: build and push image - uses: ./.github/actions/build-push-image - with: - image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/test-parachain" - dockerfile: "docker/dockerfiles/test-parachain_injected.Dockerfile" - - # - # - # - build-push-image-polkadot-debug: - needs: [set-image, build-linux-stable] - runs-on: arc-runners-polkadot-sdk - timeout-minutes: 60 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4.1.8 - with: - name: build-linux-stable-${{ needs.set-image.outputs.REF_NAME }} - - - name: tar - run: tar -xvf artifacts.tar - - - name: build and push image - uses: ./.github/actions/build-push-image - with: - image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/polkadot-debug" - dockerfile: "docker/dockerfiles/polkadot/polkadot_injected_debug.Dockerfile" - - - # - # - # - build-push-image-colander: - needs: [set-image, build-test-collators] - runs-on: arc-runners-polkadot-sdk - timeout-minutes: 60 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4.1.8 - with: - name: build-test-collators-${{ needs.set-image.outputs.REF_NAME }} - - - name: tar - run: tar -xvf artifacts.tar - - - name: build and push image - uses: ./.github/actions/build-push-image - with: - image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/colander" - dockerfile: "docker/dockerfiles/collator_injected.Dockerfile" - - - # - # - # - build-push-image-malus: - needs: [set-image, build-malus] - runs-on: arc-runners-polkadot-sdk - timeout-minutes: 60 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4.1.8 - with: - name: build-malus-${{ needs.set-image.outputs.REF_NAME }} - - - name: tar - run: tar -xvf artifacts.tar - - - name: build and push image - uses: ./.github/actions/build-push-image - with: - image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/malus" - dockerfile: "docker/dockerfiles/malus_injected.Dockerfile" - - - # - # - # - build-push-image-substrate-pr: - needs: [set-image, build-linux-substrate] - runs-on: arc-runners-polkadot-sdk - timeout-minutes: 60 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4.1.8 - with: - name: build-linux-substrate-${{ needs.set-image.outputs.REF_NAME }} - - - name: tar - run: tar -xvf artifacts.tar - - - name: build and push image - uses: ./.github/actions/build-push-image - with: - image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/substrate" - dockerfile: "docker/dockerfiles/substrate_injected.Dockerfile" - - - # - # - # - # unlike other images, bridges+zombienet image is based on Zombienet image that pulls required binaries - # from other fresh images (polkadot and cumulus) - build-push-image-bridges-zombienet-tests: - needs: [set-image, build-linux-stable, build-linux-stable-cumulus, prepare-bridges-zombienet-artifacts] - runs-on: arc-runners-polkadot-sdk - timeout-minutes: 60 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4.1.8 - with: - name: build-linux-stable-${{ needs.set-image.outputs.REF_NAME }} - - name: tar - run: | - tar -xvf artifacts.tar - rm artifacts.tar - - - uses: actions/download-artifact@v4.1.8 - with: - name: build-linux-stable-cumulus-${{ needs.set-image.outputs.REF_NAME }} - - name: tar - run: | - tar -xvf artifacts.tar - rm artifacts.tar - - - uses: actions/download-artifact@v4.1.8 - with: - name: prepare-bridges-zombienet-artifacts-${{ needs.set-image.outputs.REF_NAME }} - - name: tar - run: | - tar -xvf artifacts.tar - rm artifacts.tar - - - name: build and push image - uses: ./.github/actions/build-push-image - with: - image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/bridges-zombienet-tests" - dockerfile: "docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile" - - - # - # - # - build-push-image-polkadot-parachain-debug: - needs: [set-image, build-linux-stable-cumulus] - runs-on: arc-runners-polkadot-sdk - timeout-minutes: 60 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4.1.8 - with: - name: build-linux-stable-cumulus-${{ needs.set-image.outputs.REF_NAME }} - - - name: tar - run: tar -xvf artifacts.tar - - - name: build and push image - uses: ./.github/actions/build-push-image - with: - image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/polkadot-parachain-debug" - dockerfile: "docker/dockerfiles/polkadot-parachain/polkadot-parachain-debug_unsigned_injected.Dockerfile" \ No newline at end of file diff --git a/.github/workflows/check-cargo-check-runtimes.yml b/.github/workflows/check-cargo-check-runtimes.yml deleted file mode 100644 index ebcf6c5fc9bd..000000000000 --- a/.github/workflows/check-cargo-check-runtimes.yml +++ /dev/null @@ -1,136 +0,0 @@ -name: Check Cargo Check Runtimes - -on: - pull_request: - types: [ opened, synchronize, reopened, ready_for_review, labeled ] - - -# Jobs in this workflow depend on each other, only for limiting peak amount of spawned workers - -jobs: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - set-image: - if: contains(github.event.label.name, 'GHA-migration') || contains(github.event.pull_request.labels.*.name, 'GHA-migration') - runs-on: ubuntu-latest - timeout-minutes: 20 - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - check-runtime-assets: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [set-image] - timeout-minutes: 20 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Run cargo check - uses: ./.github/actions/cargo-check-runtimes - with: - root: cumulus/parachains/runtimes/assets - - check-runtime-collectives: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [check-runtime-assets, set-image] - timeout-minutes: 20 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Run cargo check - uses: ./.github/actions/cargo-check-runtimes - with: - root: cumulus/parachains/runtimes/collectives - - check-runtime-coretime: - runs-on: arc-runners-polkadot-sdk-beefy - container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [check-runtime-assets, set-image] - timeout-minutes: 20 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Run cargo check - uses: ./.github/actions/cargo-check-runtimes - with: - root: cumulus/parachains/runtimes/coretime - - check-runtime-bridge-hubs: - runs-on: arc-runners-polkadot-sdk-beefy - container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [set-image] - timeout-minutes: 20 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Run cargo check - uses: ./.github/actions/cargo-check-runtimes - with: - root: cumulus/parachains/runtimes/bridge-hubs - - check-runtime-contracts: - runs-on: arc-runners-polkadot-sdk-beefy - container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [check-runtime-collectives, set-image] - timeout-minutes: 20 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Run cargo check - uses: ./.github/actions/cargo-check-runtimes - with: - root: cumulus/parachains/runtimes/contracts - - check-runtime-starters: - runs-on: arc-runners-polkadot-sdk-beefy - container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [check-runtime-assets, set-image] - timeout-minutes: 20 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Run cargo check - uses: ./.github/actions/cargo-check-runtimes - with: - root: cumulus/parachains/runtimes/starters - - check-runtime-testing: - runs-on: arc-runners-polkadot-sdk-beefy - container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [check-runtime-starters, set-image] - timeout-minutes: 20 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Run cargo check - uses: ./.github/actions/cargo-check-runtimes - with: - root: cumulus/parachains/runtimes/testing - - confirm-required-jobs-passed: - runs-on: ubuntu-latest - name: All check-runtime-* tests passed - # If any new job gets added, be sure to add it to this array - needs: - - check-runtime-assets - - check-runtime-collectives - - check-runtime-coretime - - check-runtime-bridge-hubs - - check-runtime-contracts - - check-runtime-starters - - check-runtime-testing - steps: - - run: echo '### Good job! All the tests passed 🚀' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/check-frame-omni-bencher.yml b/.github/workflows/check-frame-omni-bencher.yml deleted file mode 100644 index e9db2d912979..000000000000 --- a/.github/workflows/check-frame-omni-bencher.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Short benchmarks (frame-omni-bencher) - -on: - push: - branches: - - master - pull_request: - types: [ opened, synchronize, reopened, ready_for_review, labeled ] - merge_group: - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -env: - ARTIFACTS_NAME: frame-omni-bencher-artifacts - -jobs: - changes: - # TODO: remove once migration is complete or this workflow is fully stable - if: contains(github.event.label.name, 'GHA-migration') - permissions: - pull-requests: read - uses: ./.github/workflows/reusable-check-changed-files.yml - - set-image: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - runs-on: ubuntu-latest - needs: changes - if: ${{ needs.changes.outputs.rust }} - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - - run-frame-omni-bencher: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [ set-image, changes ] # , build-frame-omni-bencher ] - if: ${{ needs.changes.outputs.rust }} - timeout-minutes: 30 - strategy: - fail-fast: false # keep running other workflows even if one fails, to see the logs of all possible failures - matrix: - runtime: - [ - westend-runtime, - rococo-runtime, - asset-hub-rococo-runtime, - asset-hub-westend-runtime, - bridge-hub-rococo-runtime, - bridge-hub-westend-runtime, - collectives-westend-runtime, - coretime-rococo-runtime, - coretime-westend-runtime, - people-rococo-runtime, - people-westend-runtime, - glutton-westend-runtime, - ] - container: - image: ${{ needs.set-image.outputs.IMAGE }} - env: - PACKAGE_NAME: ${{ matrix.runtime }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: script - run: | - RUNTIME_BLOB_NAME=$(echo $PACKAGE_NAME | sed 's/-/_/g').compact.compressed.wasm - RUNTIME_BLOB_PATH=./target/release/wbuild/$PACKAGE_NAME/$RUNTIME_BLOB_NAME - forklift cargo build --release --locked -p $PACKAGE_NAME -p frame-omni-bencher --features runtime-benchmarks - echo "Running short benchmarking for PACKAGE_NAME=$PACKAGE_NAME and RUNTIME_BLOB_PATH=$RUNTIME_BLOB_PATH" - ls -lrt $RUNTIME_BLOB_PATH - ./target/release/frame-omni-bencher v1 benchmark pallet --runtime $RUNTIME_BLOB_PATH --all --steps 2 --repeat 1 - confirm-frame-omni-benchers-passed: - runs-on: ubuntu-latest - name: All benchmarks passed - needs: run-frame-omni-bencher - steps: - - run: echo '### Good job! All the benchmarks passed 🚀' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/check-getting-started.yml b/.github/workflows/check-getting-started.yml deleted file mode 100644 index b43db33c63bf..000000000000 --- a/.github/workflows/check-getting-started.yml +++ /dev/null @@ -1,296 +0,0 @@ -name: Check the getting-started.sh script - -# This workflow aims to make sure that the `getting-started.sh` script -# is functional and allows to build the templates -# on different operating systems. -# -# There are two jobs inside. -# One for systems that can run in a docker container, and one for macOS. -# -# Each job consists of: -# 1. Some necessary prerequisites for the workflow itself. -# 2. A first pass of the script, which will install dependencies and clone a template. -# 3. A second pass of the script, to make sure the behaviour is as expected. -# 4. Building the template - making sure it's buildable and runnable. -# -# The script is interacted with using the `expect` tool, which is available on all relevant systems. -# The steps are not re-used between macOS and other systems, -# because they are very similar but a little different. -# Additionally, macOS does NOT start from scratch here - for example, we have homebrew already installed. -# -# There are many combinations of systems, shells and templates. -# We test a selected handful of combinations here. - -on: - pull_request: - paths: - - '.github/workflows/check-getting-started.yml' - - 'scripts/getting-started.sh' - schedule: - - cron: '0 5 * * *' - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - check-getting-started: - strategy: - fail-fast: true - matrix: - include: - - name: ubuntu - container: ubuntu - template: minimal - shell: bash - - name: debian - container: debian - template: parachain - shell: sh - - name: arch - container: archlinux - template: solochain - shell: sh - - name: fedora - container: fedora - template: parachain - shell: sh - - name: opensuse - container: opensuse/tumbleweed - template: solochain - shell: sh - runs-on: arc-runners-polkadot-sdk-beefy - container: ${{ matrix.container }}:latest - steps: - # A minimal amount of prerequisites required before we can run the actual getting-started script, - # which will install the rest of requirements. - - name: Install ubuntu/debian prerequisites - run: apt update && apt install -y expect sudo git - if: contains(matrix.name, 'ubuntu') || contains(matrix.name, 'debian') - - name: Install arch prerequisites - run: pacman -Syu --needed --noconfirm expect sudo git - if: contains(matrix.name, 'arch') - - name: Install fedora prerequisites - run: dnf --assumeyes install expect sudo git - if: contains(matrix.name, 'fedora') - - name: Install opensuse prerequisites - run: zypper install --no-confirm expect sudo git - if: contains(matrix.name, 'opensuse') - - - name: Checkout - uses: actions/checkout@v4 - - - name: Set additional expect flags if necessary - run: | - # Add a debug flag to expect, if github is re-run with debug logging enabled. - [ "${{ runner.debug }}" = "1" ] && EXPECT_FLAGS="-d" || EXPECT_FLAGS="" - echo "EXPECT_FLAGS=${EXPECT_FLAGS}" >> $GITHUB_ENV - - - name: Check the first run of the script - run: | - expect $EXPECT_FLAGS -c ' - set timeout 240 - - spawn ${{ matrix.shell }} scripts/getting-started.sh - - expect_after { - timeout { puts stderr "Timed out on an expect"; exit 1 } - eof { puts stderr "EOF received on an expect"; exit 1 } - } - - expect -nocase "Detected ${{ matrix.name }}" - - expect "Rust is not installed. Install it?" { - send "y\r" - expect "Proceed with standard installation (default - just press enter)" { - send "\r" - expect "Rust is installed now" - } - } - - expect "Setup the Rust environment" { - send "y\r" - } - - expect "start with one of the templates" { - send "y\r" - } - - expect -re "(.)\\) ${{ matrix.template }} template" { - send "$expect_out(1,string)\r" - } - - expect "compile the node?" { - send "n\r" - } - - expect eof - ' - timeout-minutes: 15 - - - name: Check the second run of the script - run: | - expect $EXPECT_FLAGS -c ' - set timeout 120 - - spawn ${{ matrix.shell }} scripts/getting-started.sh - - expect_after { - timeout { puts stderr "Timed out on an expect"; exit 1 } - eof { puts stderr "EOF received on an expect"; exit 1 } - } - - expect "Rust already installed" {} - - expect "Setup the Rust environment" { - send "n\r" - } - - expect "start with one of the templates" { - send "y\r" - } - - expect -re "(.)\\) ${{ matrix.template }} template" { - send "$expect_out(1,string)\r" - expect "directory already exists" {} - } - - expect "compile the node?" { - send "n\r" - } - - expect eof - ' - timeout-minutes: 15 - - - name: Compile the node outside of the script - run: | - . "$HOME/.cargo/env" - cd ${{ matrix.template }}-template - cargo build --release - timeout-minutes: 120 - - - name: Check that the binary is executable - run: | - . "$HOME/.cargo/env" - cd ${{ matrix.template }}-template - cargo run --release -- --help - timeout-minutes: 5 - - check-getting-started-macos: - strategy: - fail-fast: true - matrix: - include: - - template: parachain - shell: sh - - template: solochain - shell: bash - runs-on: macos-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set additional expect flags if necessary - run: | - # Add a debug flag to expect, if github is re-run with debug logging enabled. - [ "${{ runner.debug }}" = "1" ] && EXPECT_FLAGS="-d" || EXPECT_FLAGS="" - echo "EXPECT_FLAGS=${EXPECT_FLAGS}" >> $GITHUB_ENV - - - name: Check the first run of the script - run: | - expect $EXPECT_FLAGS -c ' - set timeout 120 - - spawn ${{ matrix.shell }} scripts/getting-started.sh - - expect_after { - timeout { puts stderr "Timed out on an expect"; exit 1 } - eof { puts stderr "EOF received on an expect"; exit 1 } - } - - expect -nocase "Detected macOS" - - expect "Homebrew already installed" - - expect "Install cmake" { - send "y\r" - } - - expect "Rust already installed" {} - - expect "Setup the Rust environment" { - send "y\r" - } - - expect "start with one of the templates" { - send "y\r" - } - - expect -re "(.)\\) ${{ matrix.template }} template" { - send "$expect_out(1,string)\r" - } - - expect "compile the node?" { - send "n\r" - } - - expect eof - ' - timeout-minutes: 15 - - - name: Check the second run of the script - run: | - expect $EXPECT_FLAGS -c ' - set timeout 120 - - spawn ${{ matrix.shell }} scripts/getting-started.sh - - expect_after { - timeout { puts stderr "Timed out on an expect"; exit 1 } - eof { puts stderr "EOF received on an expect"; exit 1 } - } - - expect "Homebrew already installed" - - expect "Install cmake" { - send "y\r" - } - - expect "Rust already installed" {} - - expect "Setup the Rust environment" { - send "n\r" - } - - expect "start with one of the templates" { - send "y\r" - } - - expect -re "(.)\\) ${{ matrix.template }} template" { - send "$expect_out(1,string)\r" - expect "directory already exists" {} - } - - expect "compile the node?" { - send "n\r" - } - - expect eof - ' - timeout-minutes: 15 - - - name: Compile the node outside of the script - run: | - . "$HOME/.cargo/env" - cd ${{ matrix.template }}-template - cargo build --release - timeout-minutes: 120 - - - name: Check that the binary is executable - run: | - . "$HOME/.cargo/env" - cd ${{ matrix.template }}-template - cargo run --release -- --help - timeout-minutes: 5 diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml deleted file mode 100644 index 9aebd83282e3..000000000000 --- a/.github/workflows/checks.yml +++ /dev/null @@ -1,109 +0,0 @@ -name: Checks - -on: - push: - branches: - - master - pull_request: - types: [opened, synchronize, reopened, ready_for_review] - merge_group: - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -permissions: { } - -jobs: - # temporary disabled because currently doesn't work in merge queue - # changes: - # permissions: - # pull-requests: read - # uses: ./.github/workflows/reusable-check-changed-files.yml - set-image: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - RUNNER: ${{ steps.set_runner.outputs.RUNNER }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - # By default we use spot machines that can be terminated at any time. - # Merge queues use persistent runners to avoid kicking off from queue when the runner is terminated. - - id: set_runner - run: | - # Run merge queues on persistent runners - if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then - echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT - else - echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT - fi - cargo-clippy: - runs-on: ${{ needs.set-image.outputs.RUNNER }} - needs: [set-image] - # if: ${{ needs.changes.outputs.rust }} - timeout-minutes: 40 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - env: - RUSTFLAGS: "-D warnings" - SKIP_WASM_BUILD: 1 - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - name: script - run: | - forklift cargo clippy --all-targets --locked --workspace - forklift cargo clippy --all-targets --all-features --locked --workspace - check-try-runtime: - runs-on: ${{ needs.set-image.outputs.RUNNER }} - needs: [set-image] - # if: ${{ needs.changes.outputs.rust }} - timeout-minutes: 40 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - name: script - run: | - forklift cargo check --locked --all --features try-runtime - # this is taken from cumulus - # Check that parachain-template will compile with `try-runtime` feature flag. - forklift cargo check --locked -p parachain-template-node --features try-runtime - # add after https://github.com/paritytech/substrate/pull/14502 is merged - # experimental code may rely on try-runtime and vice-versa - forklift cargo check --locked --all --features try-runtime,experimental - # check-core-crypto-features works fast without forklift - check-core-crypto-features: - runs-on: ${{ needs.set-image.outputs.RUNNER }} - needs: [set-image] - # if: ${{ needs.changes.outputs.rust }} - timeout-minutes: 30 - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - name: script - run: | - cd substrate/primitives/core - ./check-features-variants.sh - cd - - cd substrate/primitives/application-crypto - ./check-features-variants.sh - cd - - cd substrate/primitives/keyring - ./check-features-variants.sh - cd - - # name of this job must be unique across all workflows - # otherwise GitHub will mark all these jobs as required - confirm-required-checks-passed: - runs-on: ubuntu-latest - name: All checks passed - # If any new job gets added, be sure to add it to this array - needs: [cargo-clippy, check-try-runtime, check-core-crypto-features] - steps: - - run: echo '### Good job! All the checks passed 🚀' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml deleted file mode 100644 index dfdf771a6105..000000000000 --- a/.github/workflows/cmd.yml +++ /dev/null @@ -1,452 +0,0 @@ -name: Command - -on: - issue_comment: # listen for comments on issues - types: [ created ] - -permissions: # allow the action to comment on the PR - contents: write - issues: write - pull-requests: write - actions: read - -jobs: - is-org-member: - if: startsWith(github.event.comment.body, '/cmd') - runs-on: ubuntu-latest - outputs: - member: ${{ steps.is-member.outputs.result }} - steps: - - name: Generate token - id: generate_token - uses: tibdex/github-app-token@v2.1.0 - with: - app_id: ${{ secrets.CMD_BOT_APP_ID }} - private_key: ${{ secrets.CMD_BOT_APP_KEY }} - - - name: Check if user is a member of the organization - id: is-member - uses: actions/github-script@v7 - with: - github-token: ${{ steps.generate_token.outputs.token }} - result-encoding: string - script: | - const fs = require("fs"); - try { - const org = '${{ github.event.repository.owner.login }}'; - const username = '${{ github.event.comment.user.login }}'; - - const membership = await github.rest.orgs.checkMembershipForUser({ - org: org, - username: username - }); - - console.log(membership, membership.status, membership.status === 204); - - if (membership.status === 204) { - return 'true'; - } else { - console.log(membership); - fs.appendFileSync(process.env["GITHUB_STEP_SUMMARY"], `${membership.data && membership.data.message || 'Unknown error happened, please check logs'}`); - } - } catch (error) { - console.log(error) - } - - return 'false'; - - reject-non-members: - needs: is-org-member - if: ${{ startsWith(github.event.comment.body, '/cmd') && needs.is-org-member.outputs.member != 'true' }} - runs-on: ubuntu-latest - steps: - - name: Add reaction to rejected comment - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.reactions.createForIssueComment({ - comment_id: ${{ github.event.comment.id }}, - owner: context.repo.owner, - repo: context.repo.repo, - content: 'confused' - }) - - - name: Comment PR (Rejected) - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `Sorry, only members of the organization ${{ github.event.repository.owner.login }} members can run commands.` - }) - - acknowledge: - needs: is-org-member - if: ${{ startsWith(github.event.comment.body, '/cmd') && needs.is-org-member.outputs.member == 'true' }} - runs-on: ubuntu-latest - steps: - - name: Add reaction to triggered comment - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.reactions.createForIssueComment({ - comment_id: ${{ github.event.comment.id }}, - owner: context.repo.owner, - repo: context.repo.repo, - content: 'eyes' - }) - - clean: - needs: is-org-member - runs-on: ubuntu-latest - steps: - - name: Clean previous comments - if: ${{ startsWith(github.event.comment.body, '/cmd') && contains(github.event.comment.body, '--clean') && needs.is-org-member.outputs.member == 'true' }} - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.issues.listComments({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo - }).then(comments => { - for (let comment of comments.data) { - console.log(comment) - if ( - ${{ github.event.comment.id }} !== comment.id && - ( - ( - ( - comment.body.startsWith('Command') || - comment.body.startsWith('
Command') || - comment.body.startsWith('Sorry, only ') - ) && comment.user.type === 'Bot' - ) || - (comment.body.startsWith('/cmd') && comment.user.login === context.actor) - ) - ) { - github.rest.issues.deleteComment({ - comment_id: comment.id, - owner: context.repo.owner, - repo: context.repo.repo - }) - } - } - }) - help: - needs: [ clean, is-org-member ] - if: ${{ startsWith(github.event.comment.body, '/cmd') && contains(github.event.comment.body, '--help') && needs.is-org-member.outputs.member == 'true' }} - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Get command - uses: actions-ecosystem/action-regex-match@v2 - id: get-pr-comment - with: - text: ${{ github.event.comment.body }} - regex: '^(\/cmd )([-\/\s\w.=:]+)$' # see explanation in docs/contributor/commands-readme.md#examples - - - name: Save output of help - id: help - env: - CMD: ${{ steps.get-pr-comment.outputs.group2 }} # to avoid "" around the command - run: | - echo 'help<> $GITHUB_OUTPUT - python3 .github/scripts/cmd/cmd.py $CMD >> $GITHUB_OUTPUT - echo 'EOF' >> $GITHUB_OUTPUT - - - name: Comment PR (Help) - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `
Command help: - - \`\`\` - ${{ steps.help.outputs.help }} - \`\`\` - -
` - }) - - - name: Add confused reaction on failure - uses: actions/github-script@v7 - if: ${{ failure() }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.reactions.createForIssueComment({ - comment_id: ${{ github.event.comment.id }}, - owner: context.repo.owner, - repo: context.repo.repo, - content: 'confused' - }) - - - name: Add 👍 reaction on success - uses: actions/github-script@v7 - if: ${{ !failure() }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.reactions.createForIssueComment({ - comment_id: ${{ github.event.comment.id }}, - owner: context.repo.owner, - repo: context.repo.repo, - content: '+1' - }) - - set-image: - needs: [ clean, is-org-member ] - if: ${{ startsWith(github.event.comment.body, '/cmd') && !contains(github.event.comment.body, '--help') && needs.is-org-member.outputs.member == 'true' }} - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set-image.outputs.IMAGE }} - RUNNER: ${{ steps.set-image.outputs.RUNNER }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - - id: set-image - run: | - BODY=$(echo "${{ github.event.comment.body }}" | xargs) - IMAGE_OVERRIDE=$(echo $BODY | grep -oe 'docker.io/paritytech/ci-unified:.*\s' | xargs) - - cat .github/env >> $GITHUB_OUTPUT - - if [ -n "$IMAGE_OVERRIDE" ]; then - echo "IMAGE=$IMAGE_OVERRIDE" >> $GITHUB_OUTPUT - fi - - if [[ $BODY == "/cmd bench"* ]]; then - echo "RUNNER=arc-runners-polkadot-sdk-benchmark" >> $GITHUB_OUTPUT - elif [[ $BODY == "/cmd update-ui"* ]]; then - echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT - else - echo "RUNNER=ubuntu-latest" >> $GITHUB_OUTPUT - fi - - # Get PR branch name, because the issue_comment event does not contain the PR branch name - get-pr-branch: - needs: [ set-image ] - runs-on: ubuntu-latest - outputs: - pr-branch: ${{ steps.get-pr.outputs.pr_branch }} - repo: ${{ steps.get-pr.outputs.repo }} - steps: - - name: Check if the issue is a PR - id: check-pr - run: | - if [ -n "${{ github.event.issue.pull_request.url }}" ]; then - echo "This is a pull request comment" - else - echo "This is not a pull request comment" - exit 1 - fi - - - name: Get PR Branch Name and Repo - if: steps.check-pr.outcome == 'success' - id: get-pr - uses: actions/github-script@v7 - with: - script: | - const pr = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - }); - const prBranch = pr.data.head.ref; - const repo = pr.data.head.repo.full_name; - console.log(prBranch, repo) - core.setOutput('pr_branch', prBranch); - core.setOutput('repo', repo); - - - name: Use PR Branch Name and Repo - run: | - echo "The PR branch is ${{ steps.get-pr.outputs.pr_branch }}" - echo "The repository is ${{ steps.get-pr.outputs.repo }}" - - cmd: - needs: [ set-image, get-pr-branch ] - env: - JOB_NAME: 'cmd' - runs-on: ${{ needs.set-image.outputs.RUNNER }} - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - name: Get command - uses: actions-ecosystem/action-regex-match@v2 - id: get-pr-comment - with: - text: ${{ github.event.comment.body }} - regex: '^(\/cmd )([-\/\s\w.=:]+)$' # see explanation in docs/contributor/commands-readme.md#examples - - - name: Build workflow link - if: ${{ !contains(github.event.comment.body, '--quiet') }} - id: build-link - run: | - # Get exactly the CMD job link, filtering out the other jobs - jobLink=$(curl -s \ - -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ - -H "Accept: application/vnd.github.v3+json" \ - https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs | jq '.jobs[] | select(.name | contains("${{ env.JOB_NAME }}")) | .html_url') - - runLink=$(curl -s \ - -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ - -H "Accept: application/vnd.github.v3+json" \ - https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }} | jq '.html_url') - - echo "job_url=${jobLink}" - echo "run_url=${runLink}" - echo "job_url=$jobLink" >> $GITHUB_OUTPUT - echo "run_url=$runLink" >> $GITHUB_OUTPUT - - - - name: Comment PR (Start) - if: ${{ !contains(github.event.comment.body, '--quiet') }} - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - let job_url = ${{ steps.build-link.outputs.job_url }} - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `Command "${{ steps.get-pr-comment.outputs.group2 }}" has started 🚀 [See logs here](${job_url})` - }) - - - name: Checkout - uses: actions/checkout@v4 - with: - repository: ${{ needs.get-pr-branch.outputs.repo }} - ref: ${{ needs.get-pr-branch.outputs.pr-branch }} - - - name: Install dependencies for bench - if: startsWith(steps.get-pr-comment.outputs.group2, 'bench') - run: cargo install subweight frame-omni-bencher --locked - - - name: Run cmd - id: cmd - env: - CMD: ${{ steps.get-pr-comment.outputs.group2 }} # to avoid "" around the command - run: | - echo "Running command: '$CMD' on '${{ needs.set-image.outputs.RUNNER }}' runner, container: '${{ needs.set-image.outputs.IMAGE }}'" - echo "RUST_NIGHTLY_VERSION: $RUST_NIGHTLY_VERSION" - # Fixes "detected dubious ownership" error in the ci - git config --global --add safe.directory '*' - git remote -v - python3 .github/scripts/cmd/cmd.py $CMD - git status - git diff - - - name: Commit changes - run: | - if [ -n "$(git status --porcelain)" ]; then - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - - git pull origin ${{ needs.get-pr-branch.outputs.pr-branch }} - git add . - git restore --staged Cargo.lock # ignore changes in Cargo.lock - git commit -m "Update from ${{ github.actor }} running command '${{ steps.get-pr-comment.outputs.group2 }}'" || true - git push origin ${{ needs.get-pr-branch.outputs.pr-branch }} - else - echo "Nothing to commit"; - fi - - - name: Run Subweight - id: subweight - if: startsWith(steps.get-pr-comment.outputs.group2, 'bench') - shell: bash - run: | - git fetch - result=$(subweight compare commits \ - --path-pattern "./**/weights/**/*.rs,./**/weights.rs" \ - --method asymptotic \ - --format markdown \ - --no-color \ - --change added changed \ - --ignore-errors \ - refs/remotes/origin/master refs/heads/${{ needs.get-pr-branch.outputs.pr-branch }}) - - # Save the multiline result to the output - { - echo "result<> $GITHUB_OUTPUT - - - name: Comment PR (End) - if: ${{ !failure() && !contains(github.event.comment.body, '--quiet') }} - uses: actions/github-script@v7 - env: - SUBWEIGHT: '${{ steps.subweight.outputs.result }}' - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - let runUrl = ${{ steps.build-link.outputs.run_url }} - let subweight = process.env.SUBWEIGHT; - - let subweightCollapsed = subweight - ? `
\n\nSubweight results:\n\n${subweight}\n\n
` - : ''; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `Command "${{ steps.get-pr-comment.outputs.group2 }}" has finished ✅ [See logs here](${runUrl})${subweightCollapsed}` - }) - - - name: Comment PR (Failure) - if: ${{ failure() && !contains(github.event.comment.body, '--quiet') }} - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - let jobUrl = ${{ steps.build-link.outputs.job_url }} - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `Command "${{ steps.get-pr-comment.outputs.group2 }}" has failed ❌! [See logs here](${jobUrl})` - }) - - - name: Add 😕 reaction on failure - uses: actions/github-script@v7 - if: ${{ failure() }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.reactions.createForIssueComment({ - comment_id: ${{ github.event.comment.id }}, - owner: context.repo.owner, - repo: context.repo.repo, - content: 'confused' - }) - - - name: Add 👍 reaction on success - uses: actions/github-script@v7 - if: ${{ !failure() }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.reactions.createForIssueComment({ - comment_id: ${{ github.event.comment.id }}, - owner: context.repo.owner, - repo: context.repo.repo, - content: '+1' - }) diff --git a/.github/workflows/command-backport.yml b/.github/workflows/command-backport.yml deleted file mode 100644 index 1ad68d96a63f..000000000000 --- a/.github/workflows/command-backport.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: Backport into stable - -on: - # This trigger can be problematic, see: https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ - # In our case it is fine since we only run it on merged Pull Requests and do not execute any of the repo code itself. - pull_request_target: - types: [ closed, labeled ] - -permissions: - contents: write # so it can comment - pull-requests: write # so it can create pull requests - -jobs: - backport: - name: Backport pull request - runs-on: ubuntu-latest - - # The 'github.event.pull_request.merged' ensures that it got into master: - if: > - ( !startsWith(github.event.pull_request.base.ref, 'stable') ) && - ( - github.event_name == 'pull_request_target' && - github.event.pull_request.merged && - github.event.pull_request.base.ref == 'master' && - contains(github.event.pull_request.labels.*.name, 'A4-needs-backport') - ) - steps: - - uses: actions/checkout@v4 - - - name: Create backport pull requests - uses: korthout/backport-action@v3 - id: backport - with: - target_branches: stable2407 stable2409 - merge_commits: skip - github_token: ${{ secrets.GITHUB_TOKEN }} - pull_description: | - Backport #${pull_number} into `${target_branch}` (cc @${pull_author}). - - - pull_title: | - [${target_branch}] Backport #${pull_number} - experimental: > - { - "conflict_resolution": "draft_commit_conflicts" - } - - - name: Label Backports - if: ${{ steps.backport.outputs.created_pull_numbers != '' }} - uses: actions/github-script@v7 - with: - script: | - const pullNumbers = '${{ steps.backport.outputs.created_pull_numbers }}'.split(' '); - - for (const pullNumber of pullNumbers) { - await github.rest.issues.addLabels({ - issue_number: parseInt(pullNumber), - owner: context.repo.owner, - repo: context.repo.repo, - labels: ['A3-backport'] - }); - console.log(`Added A3-backport label to PR #${pullNumber}`); - } diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 523c2b19ba89..000000000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,141 +0,0 @@ -name: Docs - -on: - push: - branches: - - master - pull_request: - types: [opened, synchronize, reopened, ready_for_review, labeled] - merge_group: - -concurrency: - group: ${{ github.ref }} - cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} - -jobs: - set-image: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - # TODO: remove once migration is complete or this workflow is fully stable - if: contains(github.event.label.name, 'GHA-migration') || contains(github.event.pull_request.labels.*.name, 'GHA-migration') - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - test-rustdoc: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [set-image] - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - uses: actions/checkout@v4 - - run: forklift cargo doc --workspace --all-features --no-deps - env: - SKIP_WASM_BUILD: 1 - test-doc: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [set-image] - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - uses: actions/checkout@v4 - - run: forklift cargo test --doc --workspace - env: - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - - build-rustdoc: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [set-image, test-rustdoc] - container: - image: ${{ needs.set-image.outputs.IMAGE }} - steps: - - uses: actions/checkout@v4 - - run: forklift cargo doc --all-features --workspace --no-deps - env: - SKIP_WASM_BUILD: 1 - RUSTDOCFLAGS: "-Dwarnings --default-theme=ayu --html-in-header ./docs/sdk/assets/header.html --extend-css ./docs/sdk/assets/theme.css --html-after-content ./docs/sdk/assets/after-content.html" - - run: rm -f ./target/doc/.lock - - run: mv ./target/doc ./crate-docs - - name: Inject Simple Analytics script - run: | - script_content="" - docs_dir="./crate-docs" - - inject_simple_analytics() { - find "$1" -name '*.html' | xargs -I {} -P "$(nproc)" bash -c 'file="{}"; echo "Adding Simple Analytics script to $file"; sed -i "s||'"$2"'|" "$file";' - } - - inject_simple_analytics "$docs_dir" "$script_content" - - run: echo "" > ./crate-docs/index.html - - uses: actions/upload-artifact@v4 - with: - name: ${{ github.sha }}-doc - path: ./crate-docs/ - retention-days: 1 - if-no-files-found: error - - build-implementers-guide: - runs-on: ubuntu-latest - container: - image: paritytech/mdbook-utils:e14aae4a-20221123 - options: --user root - steps: - - uses: actions/checkout@v4 - - run: mdbook build ./polkadot/roadmap/implementers-guide - - run: mkdir -p artifacts - - run: mv polkadot/roadmap/implementers-guide/book artifacts/ - - uses: actions/upload-artifact@v4 - with: - name: ${{ github.sha }}-guide - path: ./artifacts/ - retention-days: 1 - if-no-files-found: error - - publish-rustdoc: - if: github.ref == 'refs/heads/master' - runs-on: ubuntu-latest - environment: subsystem-benchmarks - needs: [build-rustdoc, build-implementers-guide] - steps: - - uses: actions/checkout@v4 - with: - ref: gh-pages - - uses: actions/create-github-app-token@v1 - id: app-token - with: - app-id: ${{ secrets.POLKADOTSDK_GHPAGES_APP_ID }} - private-key: ${{ secrets.POLKADOTSDK_GHPAGES_APP_KEY }} - - name: Ensure destination dir does not exist - run: | - rm -rf book/ - rm -rf ${REF_NAME} - env: - REF_NAME: ${{ github.head_ref || github.ref_name }} - - name: Download rustdocs - uses: actions/download-artifact@v4 - with: - name: ${{ github.sha }}-doc - path: ${{ github.head_ref || github.ref_name }} - - name: Download guide - uses: actions/download-artifact@v4 - with: - name: ${{ github.sha }}-guide - path: /tmp - - run: mkdir -p book - - name: Move book files - run: mv /tmp/book/html/* book/ - - name: Push to GH-Pages branch - uses: github-actions-x/commit@v2.9 - with: - github-token: ${{ steps.app-token.outputs.token }} - push-branch: "gh-pages" - commit-message: "___Updated docs for ${{ github.head_ref || github.ref_name }}___" - force-add: "true" - files: ${{ github.head_ref || github.ref_name }}/ book/ - name: devops-parity - email: devops-team@parity.io diff --git a/.github/workflows/fork-sync-action.yml b/.github/workflows/fork-sync-action.yml deleted file mode 100644 index 69e9e93bf54b..000000000000 --- a/.github/workflows/fork-sync-action.yml +++ /dev/null @@ -1,20 +0,0 @@ -# This Workflow is not supposed to run in the paritytech/polkadot-sdk repo. -# This Workflow is supposed to run only in the forks of the repo, -# paritytech-release/polkadot-sdk specifically, -# to automatically maintain the critical fork synced with the upstream. -# This Workflow should be always disabled in the paritytech/polkadot-sdk repo. - -name: Sync the forked repo with the upstream -on: - schedule: - - cron: "0 0/4 * * *" - workflow_dispatch: - -jobs: - job_sync_branches: - uses: paritytech-release/sync-workflows/.github/workflows/sync-with-upstream.yml@latest - with: - fork_writer_app_id: ${{ vars.UPSTREAM_CONTENT_SYNC_APP_ID}} - fork_owner: ${{ vars.RELEASE_ORG}} - secrets: - fork_writer_app_key: ${{ secrets.UPSTREAM_CONTENT_SYNC_APP_KEY }} diff --git a/.github/workflows/misc-update-wishlist-leaderboard.yml b/.github/workflows/misc-update-wishlist-leaderboard.yml deleted file mode 100644 index 326168717674..000000000000 --- a/.github/workflows/misc-update-wishlist-leaderboard.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Update wishlist leaderboard - -on: - schedule: - # Run every 3 hours - - cron: '0 */3 * * *' - -permissions: - contents: read - issues: write - -jobs: - update-wishlist-leaderboard: - if: github.repository == 'paritytech/polkadot-sdk' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install PyGithub - - name: Update developer wishlist - env: - GH_TOKEN: ${{ github.token }} - WISHLIST_REPOSITORY: "paritytech/polkadot-sdk" - WISHLIST_ISSUE_NUMBER: "3900" - run: python .github/scripts/update-wishlist-leaderboard.py - - name: Update user wishlist - env: - GH_TOKEN: ${{ github.token }} - WISHLIST_REPOSITORY: "paritytech/polkadot-sdk" - WISHLIST_ISSUE_NUMBER: "3901" - run: python .github/scripts/update-wishlist-leaderboard.py diff --git a/.github/workflows/reusable-check-changed-files.yml b/.github/workflows/reusable-check-changed-files.yml deleted file mode 100644 index 47f0620439c7..000000000000 --- a/.github/workflows/reusable-check-changed-files.yml +++ /dev/null @@ -1,59 +0,0 @@ -# Reusable workflow to perform checks and generate conditions for other workflows. -# Currently it checks if any Rust (build-related) file is changed -# and if the current (caller) workflow file is changed. -# Example: -# -# jobs: -# changes: -# permissions: -# pull-requests: read -# uses: ./.github/workflows/reusable-check-changed-files.yml -# some-job: -# needs: changes -# if: ${{ needs.changes.outputs.rust }} -# ....... - -name: Check changes files - -on: - workflow_call: - # Map the workflow outputs to job outputs - outputs: - rust: - value: ${{ jobs.changes.outputs.rust }} - description: "true if any of the build-related OR current (caller) workflow files have changed" - current-workflow: - value: ${{ jobs.changes.outputs.current-workflow }} - description: "true if current (caller) workflow file has changed" - -jobs: - changes: - runs-on: ubuntu-latest - permissions: - pull-requests: read - outputs: - # true if current workflow (caller) file is changed - rust: ${{ steps.filter.outputs.rust == 'true' || steps.filter.outputs.current-workflow == 'true' }} - current-workflow: ${{ steps.filter.outputs.current-workflow }} - steps: - - id: current-file - run: echo "current-workflow-file=$(echo ${{ github.workflow_ref }} | sed -nE "s/.*(\.github\/workflows\/[a-zA-Z0-9_-]*\.y[a]?ml)@refs.*/\1/p")" >> $GITHUB_OUTPUT - - run: echo "${{ steps.current-file.outputs.current-workflow-file }}" - # For pull requests it's not necessary to checkout the code - - name: Checkout - if: github.event_name != 'pull_request' - uses: actions/checkout@v4 - - id: filter - uses: dorny/paths-filter@v3 - with: - predicate-quantifier: "every" - # current-workflow - check if the current (caller) workflow file is changed - # rust - check if any Rust (build-related) file is changed - filters: | - current-workflow: - - '${{ steps.current-file.outputs.current-workflow-file }}' - rust: - - '**/*' - - '!.github/**/*' - - '!prdoc/**/*' - - '!docs/**/*' diff --git a/.github/workflows/runtimes-matrix.json b/.github/workflows/runtimes-matrix.json deleted file mode 100644 index 45a3acd3f166..000000000000 --- a/.github/workflows/runtimes-matrix.json +++ /dev/null @@ -1,98 +0,0 @@ -[ - { - "name": "dev", - "package": "kitchensink-runtime", - "path": "substrate/frame", - "uri": null, - "is_relay": false - }, - { - "name": "westend", - "package": "westend-runtime", - "path": "polkadot/runtime/westend", - "uri": "wss://try-runtime-westend.polkadot.io:443", - "is_relay": true - }, - { - "name": "rococo", - "package": "rococo-runtime", - "path": "polkadot/runtime/rococo", - "uri": "wss://try-runtime-rococo.polkadot.io:443", - "is_relay": true - }, - { - "name": "asset-hub-westend", - "package": "asset-hub-westend-runtime", - "path": "cumulus/parachains/runtimes/assets/asset-hub-westend", - "uri": "wss://westend-asset-hub-rpc.polkadot.io:443", - "is_relay": false - }, - { - "name": "asset-hub-rococo", - "package": "asset-hub-rococo-runtime", - "path": "cumulus/parachains/runtimes/assets/asset-hub-rococo", - "uri": "wss://rococo-asset-hub-rpc.polkadot.io:443", - "is_relay": false - }, - { - "name": "bridge-hub-rococo", - "package": "bridge-hub-rococo-runtime", - "path": "cumulus/parachains/runtimes/bridges/bridge-hub-rococo", - "uri": "wss://rococo-bridge-hub-rpc.polkadot.io:443", - "is_relay": false - }, - { - "name": "bridge-hub-westend", - "package": "bridge-hub-rococo-runtime", - "path": "cumulus/parachains/runtimes/bridges/bridge-hub-westend", - "uri": "wss://westend-bridge-hub-rpc.polkadot.io:443", - "is_relay": false - }, - { - "name": "collectives-westend", - "package": "collectives-westend-runtime", - "path": "cumulus/parachains/runtimes/collectives/collectives-westend", - "uri": "wss://westend-collectives-rpc.polkadot.io:443" - }, - { - "name": "contracts-rococo", - "package": "contracts-rococo-runtime", - "path": "cumulus/parachains/runtimes/contracts/contracts-rococo", - "uri": "wss://rococo-contracts-rpc.polkadot.io:443", - "is_relay": false - }, - { - "name": "coretime-rococo", - "package": "coretime-rococo-runtime", - "path": "cumulus/parachains/runtimes/coretime/coretime-rococo", - "uri": "wss://rococo-coretime-rpc.polkadot.io:443", - "is_relay": false - }, - { - "name": "coretime-westend", - "package": "coretime-westend-runtime", - "path": "cumulus/parachains/runtimes/coretime/coretime-westend", - "uri": "wss://westend-coretime-rpc.polkadot.io:443", - "is_relay": false - }, - { - "name": "glutton-westend", - "package": "glutton-westend-runtime", - "path": "cumulus/parachains/runtimes/gluttons/glutton-westend", - "is_relay": false - }, - { - "name": "people-rococo", - "package": "people-rococo-runtime", - "path": "cumulus/parachains/runtimes/people/people-rococo", - "uri": "wss://rococo-people-rpc.polkadot.io:443", - "is_relay": false - }, - { - "name": "people-westend", - "package": "people-westend-runtime", - "path": "cumulus/parachains/runtimes/people/people-westend", - "uri": "wss://westend-people-rpc.polkadot.io:443", - "is_relay": false - } -] diff --git a/.github/workflows/subsystem-benchmarks.yml b/.github/workflows/subsystem-benchmarks.yml deleted file mode 100644 index c33c782a731c..000000000000 --- a/.github/workflows/subsystem-benchmarks.yml +++ /dev/null @@ -1,136 +0,0 @@ -on: - push: - branches: - - master - pull_request: - types: [ opened, synchronize, reopened, ready_for_review ] - merge_group: - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - set-image: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - - build: - timeout-minutes: 80 - needs: [ set-image ] - runs-on: arc-runners-polkadot-sdk-benchmark - container: - image: ${{ needs.set-image.outputs.IMAGE }} - strategy: - fail-fast: false - matrix: - features: [ - { name: "polkadot-availability-recovery", bench: "availability-recovery-regression-bench" }, - { name: "polkadot-availability-distribution", bench: "availability-distribution-regression-bench" }, - { name: "polkadot-node-core-approval-voting", bench: "approval-voting-regression-bench" }, - { name: "polkadot-statement-distribution", bench: "statement-distribution-regression-bench" } - ] - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Check Rust - run: | - rustup show - rustup +nightly show - - - name: Run Benchmarks - id: run-benchmarks - run: | - forklift cargo bench -p ${{ matrix.features.name }} --bench ${{ matrix.features.bench }} --features subsystem-benchmarks || echo "Benchmarks failed" - ls -lsa ./charts - mkdir ./artifacts - cp ./charts/${{ matrix.features.bench }}.json ./artifacts/${{ matrix.features.bench }}.json - - name: Upload artifacts - uses: actions/upload-artifact@v4.3.6 - with: - name: ${{matrix.features.bench}} - path: ./artifacts - - publish-benchmarks: - timeout-minutes: 60 - needs: [ build ] - if: github.ref == 'refs/heads/master' - environment: subsystem-benchmarks - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Download artifacts - uses: actions/download-artifact@v4.1.8 - with: - path: ./artifacts - - - name: Setup git - run: | - # Fixes "detected dubious ownership" error in the ci - git config --global --add safe.directory '*' - ls -lsR ./artifacts - - - uses: actions/create-github-app-token@v1 - id: app-token - with: - app-id: ${{ secrets.POLKADOTSDK_GHPAGES_APP_ID }} - private-key: ${{ secrets.POLKADOTSDK_GHPAGES_APP_KEY }} - - - name: Publish ${{ env.BENCH_NAME }} - uses: benchmark-action/github-action-benchmark@v1 - env: - BENCH_NAME: availability-recovery-regression-bench - with: - tool: "customSmallerIsBetter" - name: ${{ env.BENCH_NAME }} - output-file-path: ./artifacts/${{ env.BENCH_NAME }}/${{ env.BENCH_NAME }}.json - benchmark-data-dir-path: ./artifacts/${{ env.BENCH_NAME }} - github-token: ${{ steps.app-token.outputs.token }} - - - name: Publish ${{ env.BENCH_NAME }} - uses: benchmark-action/github-action-benchmark@v1 - env: - BENCH_NAME: availability-distribution-regression-bench - with: - tool: "customSmallerIsBetter" - name: ${{ env.BENCH_NAME }} - output-file-path: ./artifacts/${{ env.BENCH_NAME }}/${{ env.BENCH_NAME }}.json - benchmark-data-dir-path: ./artifacts/${{ env.BENCH_NAME }} - github-token: ${{ steps.app-token.outputs.token }} - - - name: Publish ${{ env.BENCH_NAME }} - uses: benchmark-action/github-action-benchmark@v1 - env: - BENCH_NAME: approval-voting-regression-bench - with: - tool: "customSmallerIsBetter" - name: ${{ env.BENCH_NAME }} - output-file-path: ./artifacts/${{ env.BENCH_NAME }}/${{ env.BENCH_NAME }}.json - benchmark-data-dir-path: ./artifacts/${{ env.BENCH_NAME }} - github-token: ${{ steps.app-token.outputs.token }} - - - name: Publish ${{ env.BENCH_NAME }} - uses: benchmark-action/github-action-benchmark@v1 - env: - BENCH_NAME: statement-distribution-regression-bench - with: - tool: "customSmallerIsBetter" - name: ${{ env.BENCH_NAME }} - output-file-path: ./artifacts/${{ env.BENCH_NAME }}/${{ env.BENCH_NAME }}.json - benchmark-data-dir-path: ./artifacts/${{ env.BENCH_NAME }} - github-token: ${{ steps.app-token.outputs.token }} From d806562f9d88dec38b5b5a8b4a9a8b2d1e38fca6 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 22:41:01 +0800 Subject: [PATCH 289/382] Fix test --- bridges/snowbridge/pallets/system/src/benchmarking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/benchmarking.rs b/bridges/snowbridge/pallets/system/src/benchmarking.rs index 4314c7528af6..20798b7c3493 100644 --- a/bridges/snowbridge/pallets/system/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/system/src/benchmarking.rs @@ -168,7 +168,7 @@ mod benchmarks { T::Token::mint_into(&caller, amount)?; - let relay_token_asset_id: Location = Location::new(1, [GlobalConsensus(Westend)]); + let relay_token_asset_id: Location = Location::parent(); let asset = Box::new(VersionedLocation::V4(relay_token_asset_id)); let asset_metadata = AssetMetadata { name: "wnd".as_bytes().to_vec().try_into().unwrap(), From b9b34fb983dac58ae05b0e1379e20363f6f7c88e Mon Sep 17 00:00:00 2001 From: Evgeny Snitko Date: Thu, 5 Sep 2024 19:27:18 +0400 Subject: [PATCH 290/382] Github actions coverage (#5148) Jobs for coverage collections and upload to codecov for github PR's Uses same test suit as test-linux-stable, splits tests into 5 parallel jobs, uploads to codecov, generates report comment and status checks (can be made required) | | | | --- | --- | | image | image | Codecov behavior (required coverage, thresholds, comment info etc.) is highly customizable via `.github/codecov.yaml` ([reference](https://docs.codecov.com/docs/codecovyml-reference)) Unfortunately, some tests are excluded because with `-C instrument-coverage` they run very slowly, flaky, or fail (see [nextest filter expression](https://github.com/paritytech/polkadot-sdk/pull/5148/files#diff-b19504a9520a2498d03020108344d8e6d93d254d812bfa26247faaa7f55263d6R80) of test-linux-stable-coverage). So for now, this workflow is optional, and will only run for pr's with the `GHA-coverage` label --- .github/codecov.yml | 8 +- .../workflows/tests-linux-stable-coverage.yml | 143 ++++++++++++++++++ substrate/bin/node/runtime/src/lib.rs | 7 +- substrate/frame/babe/src/mock.rs | 6 +- substrate/frame/grandpa/src/mock.rs | 6 +- 5 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/tests-linux-stable-coverage.yml diff --git a/.github/codecov.yml b/.github/codecov.yml index ceceb9e63654..b237c9fe6b04 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -6,4 +6,10 @@ coverage: project: default: target: 1.0 - threshold: 2.0 \ No newline at end of file + threshold: 2.0 + +comment: + behavior: new + +fixes: + - "/__w/polkadot-sdk/polkadot-sdk/::" \ No newline at end of file diff --git a/.github/workflows/tests-linux-stable-coverage.yml b/.github/workflows/tests-linux-stable-coverage.yml new file mode 100644 index 000000000000..ddf0642a4043 --- /dev/null +++ b/.github/workflows/tests-linux-stable-coverage.yml @@ -0,0 +1,143 @@ +# GHA for test-linux-stable-int, test-linux-stable, test-linux-stable-oldkernel +name: tests linux stable coverage + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened, ready_for_review, labeled] + merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + + set-image: + # GitHub Actions allows using 'env' in a container context. + # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 + # This workaround sets the container image for each job using 'set-image' job output. + if: contains(github.event.label.name, 'GHA-coverage') || contains(github.event.pull_request.labels.*.name, 'GHA-coverage') + runs-on: ubuntu-latest + outputs: + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT + - id: set_runner + run: | + # Run merge queues on persistent runners + if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT + else + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + fi + + # + # + # + test-linux-stable-coverage: + needs: [set-image] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + timeout-minutes: 120 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + RUST_TOOLCHAIN: stable + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + # + # -Cinstrument-coverage slows everything down but it is necessary for code coverage + # https://doc.rust-lang.org/rustc/instrument-coverage.html + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings -Cinstrument-coverage" + LLVM_PROFILE_FILE: "/__w/polkadot-sdk/polkadot-sdk/target/coverage/cargo-test-${{ matrix.ci_node_index }}-%p-%m.profraw" + strategy: + fail-fast: false + matrix: + ci_node_index: [1, 2, 3, 4, 5] + ci_node_total: [5] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - run: rustup component add llvm-tools-preview + - run: cargo install cargo-llvm-cov + + - run: mkdir -p target/coverage + + # Some tests are excluded because they run very slowly or fail with -Cinstrument-coverage + - name: run tests + run: > + time cargo llvm-cov nextest + --no-report --release + --workspace + --locked --no-fail-fast + --features try-runtime,ci-only-tests,experimental,riscv + --filter-expr " + !test(/.*benchmark.*/) + - test(/recovers_from_only_chunks_if_pov_large::case_1/) + - test(/participation_requests_reprioritized_for_newly_included/) + - test(/availability_is_recovered_from_chunks_if_no_group_provided::case_1/) + - test(/rejects_missing_inherent_digest/) + - test(/availability_is_recovered_from_chunks_even_if_backing_group_supplied_if_chunks_only::case_1/) + - test(/availability_is_recovered_from_chunks_if_no_group_provided::case_2/) + - test(/all_security_features_work/) + - test(/nonexistent_cache_dir/) + - test(/recovers_from_only_chunks_if_pov_large::case_3/) + - test(/recovers_from_only_chunks_if_pov_large::case_2/) + - test(/authoring_blocks/) + - test(/rejects_missing_seals/) + - test(/generate_chain_spec/) + - test(/get_preset/) + - test(/list_presets/) + - test(/tests::receive_rate_limit_is_enforced/) + - test(/polkadot-availability-recovery/) + " + --partition count:${{ matrix.ci_node_index }}/${{ matrix.ci_node_total }} + + - name: generate report + run: cargo llvm-cov report --release --codecov --output-path coverage-${{ matrix.ci_node_index }}.lcov + - name: upload report + uses: actions/upload-artifact@v4 + with: + name: coverage-report-${{ matrix.ci_node_index }}.lcov + path: coverage-${{ matrix.ci_node_index }}.lcov + + # + # + # Upload to codecov + upload-reports: + needs: [test-linux-stable-coverage] + runs-on: ubuntu-latest + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: reports + pattern: coverage-report-* + merge-multiple: true + - run: ls -al reports/ + - name: Upload to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true + directory: reports + root_dir: /__w/polkadot-sdk/polkadot-sdk/ + + # + # + # + remove-label: + runs-on: ubuntu-latest + needs: [upload-reports] + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@v2 + - uses: actions-ecosystem/action-remove-labels@v1 + with: + labels: GHA-coverage \ No newline at end of file diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 001b2273c9b2..6ae04902aa82 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -507,8 +507,7 @@ impl pallet_babe::Config for Runtime { type WeightInfo = (); type MaxAuthorities = MaxAuthorities; type MaxNominators = MaxNominators; - type KeyOwnerProof = - >::Proof; + type KeyOwnerProof = sp_session::MembershipProof; type EquivocationReportSystem = pallet_babe::EquivocationReportSystem; } @@ -1534,7 +1533,7 @@ impl pallet_grandpa::Config for Runtime { type MaxAuthorities = MaxAuthorities; type MaxNominators = MaxNominators; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; - type KeyOwnerProof = >::Proof; + type KeyOwnerProof = sp_session::MembershipProof; type EquivocationReportSystem = pallet_grandpa::EquivocationReportSystem; } @@ -2614,7 +2613,7 @@ impl pallet_beefy::Config for Runtime { type OnNewValidatorSet = MmrLeaf; type AncestryHelper = MmrLeaf; type WeightInfo = (); - type KeyOwnerProof = >::Proof; + type KeyOwnerProof = sp_session::MembershipProof; type EquivocationReportSystem = pallet_beefy::EquivocationReportSystem; } diff --git a/substrate/frame/babe/src/mock.rs b/substrate/frame/babe/src/mock.rs index 912cb3e27cd5..4e4052b2b566 100644 --- a/substrate/frame/babe/src/mock.rs +++ b/substrate/frame/babe/src/mock.rs @@ -25,12 +25,12 @@ use frame_election_provider_support::{ }; use frame_support::{ derive_impl, parameter_types, - traits::{ConstU128, ConstU32, ConstU64, KeyOwnerProofSystem, OnInitialize}, + traits::{ConstU128, ConstU32, ConstU64, OnInitialize}, }; use pallet_session::historical as pallet_session_historical; use sp_consensus_babe::{AuthorityId, AuthorityPair, Randomness, Slot, VrfSignature}; use sp_core::{ - crypto::{KeyTypeId, Pair, VrfSecret}, + crypto::{Pair, VrfSecret}, U256, }; use sp_io; @@ -182,7 +182,7 @@ impl Config for Test { type WeightInfo = (); type MaxAuthorities = ConstU32<10>; type MaxNominators = ConstU32<100>; - type KeyOwnerProof = >::Proof; + type KeyOwnerProof = sp_session::MembershipProof; type EquivocationReportSystem = super::EquivocationReportSystem; } diff --git a/substrate/frame/grandpa/src/mock.rs b/substrate/frame/grandpa/src/mock.rs index ae230a0209a7..caac4107cfb7 100644 --- a/substrate/frame/grandpa/src/mock.rs +++ b/substrate/frame/grandpa/src/mock.rs @@ -28,11 +28,11 @@ use frame_election_provider_support::{ }; use frame_support::{ derive_impl, parameter_types, - traits::{ConstU128, ConstU32, ConstU64, KeyOwnerProofSystem, OnFinalize, OnInitialize}, + traits::{ConstU128, ConstU32, ConstU64, OnFinalize, OnInitialize}, }; use pallet_session::historical as pallet_session_historical; use sp_consensus_grandpa::{RoundNumber, SetId, GRANDPA_ENGINE_ID}; -use sp_core::{crypto::KeyTypeId, H256}; +use sp_core::H256; use sp_keyring::Ed25519Keyring; use sp_runtime::{ curve::PiecewiseLinear, @@ -186,7 +186,7 @@ impl Config for Test { type MaxAuthorities = ConstU32<100>; type MaxNominators = ConstU32<1000>; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; - type KeyOwnerProof = >::Proof; + type KeyOwnerProof = sp_session::MembershipProof; type EquivocationReportSystem = super::EquivocationReportSystem; } From 702a15cbaa032899f2321fda892faf723d32efca Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:02:24 +0100 Subject: [PATCH 291/382] minor fixes pipeline (#5607) - [return macos jobs to gitlab](https://github.com/paritytech/polkadot-sdk/commit/dcd44b1d8bb681b66cbc0a063a6a999bd8253cdc) - [add benches to merge queue](https://github.com/paritytech/polkadot-sdk/commit/494eb21bb9ac4633f3217e6b58ba7256aea6e38a) - [require test-deterministic-wasm and run it earlier](https://github.com/paritytech/polkadot-sdk/commit/ab9ae5ca6c5128e002cc745d608e542138633250) --- .github/workflows/tests-misc.yml | 69 +++++++++++++------------------- .gitlab-ci.yml | 5 +++ .gitlab/pipeline/test.yml | 22 ++++++++++ 3 files changed, 54 insertions(+), 42 deletions(-) diff --git a/.github/workflows/tests-misc.yml b/.github/workflows/tests-misc.yml index 824e8c11c2a4..2e78f4a34ede 100644 --- a/.github/workflows/tests-misc.yml +++ b/.github/workflows/tests-misc.yml @@ -121,7 +121,7 @@ jobs: test-deterministic-wasm: timeout-minutes: 20 - needs: [ set-image, test-frame-ui ] + needs: [ set-image ] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -144,7 +144,7 @@ jobs: cargo-check-benches-branches: needs: [ set-image ] - if: ${{ github.event_name == 'pull_request' }} + if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} timeout-minutes: 60 outputs: branch: ${{ steps.branch.outputs.branch }} @@ -354,46 +354,30 @@ jobs: cp .forklift/config.toml /github/home/.forklift/config.toml PYTHONUNBUFFERED=x .github/scripts/check-each-crate.py ${{ matrix.index }} ${{ strategy.job-total }} - # TODO: enable when we have a macos Self-Hosted runners - # cargo-check-each-crate-macos: - # timeout-minutes: 120 - # needs: [ set-image ] - # runs-on: macos-latest - # env: - # RUSTFLAGS: "-D warnings" - # CI_JOB_NAME: cargo-check-each-crate - # IMAGE: ${{ needs.set-image.outputs.IMAGE }} - # strategy: - # fail-fast: false - # matrix: - # index: [ 1,2,3,4,5,6,7,8,9,10 ] # 10 parallel jobs - # steps: - # - name: Checkout - # uses: actions/checkout@v4.1.7 - # - # - run: | - # VERSION=$(echo $IMAGE | sed -E 's/.*:bullseye-([^-]+)-.*/\1/') - # echo $VERSION - # echo "VERSION=$VERSION" >> $GITHUB_ENV - # - # - run: | - # rustup install $VERSION - # rustup default $VERSION - # - # - name: Check Rust - # run: | - # rustup show - # rustup +nightly show - # - # - name: MacOS Deps - # run: | - # brew install protobuf openssl pkg-config zlib xz zstd llvm jq curl gcc make cmake - # rustup target add wasm32-unknown-unknown --toolchain $VERSION - # rustup component add rust-src rustfmt clippy --toolchain $VERSION - # - # - name: script - # run: | - # PYTHONUNBUFFERED=x .github/scripts/check-each-crate.py ${{ matrix.index }} ${{ strategy.job-total }} True + # cargo-check-each-crate-macos: + # timeout-minutes: 120 + # needs: [ set-image ] + # runs-on: macOS + # env: + # RUSTFLAGS: "-D warnings" + # CI_JOB_NAME: cargo-check-each-crate + # IMAGE: ${{ needs.set-image.outputs.IMAGE }} + # strategy: + # fail-fast: false + # matrix: + # index: [ 1,2,3,4,5,6,7,8,9,10 ] # 10 parallel jobs + # steps: + # - name: Checkout + # uses: actions/checkout@v4.1.7 + + # - name: Install dependencies + # uses: ./.github/actions/set-up-mac + # with: + # IMAGE: ${{ needs.set-image.outputs.IMAGE }} + + # - name: script + # run: | + # PYTHONUNBUFFERED=x .github/scripts/check-each-crate.py ${{ matrix.index }} ${{ strategy.job-total }} True confirm-required-test-misc-jobs-passed: runs-on: ubuntu-latest @@ -408,6 +392,7 @@ jobs: - test-node-metrics - check-tracing - cargo-check-each-crate + - test-deterministic-wasm # - cargo-hfuzz remove from required for now, as it's flaky steps: - run: echo '### Good job! All the required tests passed 🚀' >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8b4ca48150b1..43123cdbfc41 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -288,3 +288,8 @@ cancel-pipeline-build-short-benchmark: extends: .cancel-pipeline-template needs: - job: build-short-benchmark + +cancel-pipeline-cargo-check-each-crate-macos: + extends: .cancel-pipeline-template + needs: + - job: cargo-check-each-crate-macos \ No newline at end of file diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index 00a0aa2c9771..0879870ae13c 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -153,3 +153,25 @@ quick-benchmarks-omni: script: - time cargo build --locked --quiet --release -p asset-hub-westend-runtime --features runtime-benchmarks - time cargo run --locked --release -p frame-omni-bencher --quiet -- v1 benchmark pallet --runtime target/release/wbuild/asset-hub-westend-runtime/asset_hub_westend_runtime.compact.compressed.wasm --all --steps 2 --repeat 1 --quiet + +cargo-check-each-crate-macos: + stage: test + extends: + - .docker-env + - .common-refs + - .run-immediately + # - .collect-artifacts + before_script: + # skip timestamp script, the osx bash doesn't support printf %()T + - !reference [.job-switcher, before_script] + - !reference [.rust-info-script, script] + - !reference [.pipeline-stopper-vars, script] + variables: + SKIP_WASM_BUILD: 1 + script: + # TODO: use parallel jobs, as per cargo-check-each-crate, once more Mac runners are available + # - time ./scripts/ci/gitlab/check-each-crate.py 1 1 + - time cargo check --workspace --locked + timeout: 2h + tags: + - osx From 8d81f1e648a21d7d14f94bc86503d3c77ead5807 Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:07:14 +0100 Subject: [PATCH 292/382] /cmd followups (#5533) Closes: https://github.com/paritytech/polkadot-sdk/issues/5545 - add missing template for frame & xcm benchmarks - fix `git pull` -> https://github.com/paritytech/polkadot-sdk/actions/runs/10644887539/job/29510118915 - respect runtimes headers - use GNU instead of apache for runtimes - adds tests for cmd.py Tip: review this one with Whitespace hidden ![image](https://github.com/user-attachments/assets/3bcdc6c2-7371-428f-9962-556ca81c1467) --------- Co-authored-by: GitHub Action --- .github/scripts/cmd/cmd.py | 275 +++++++++++--------- .github/scripts/cmd/test_cmd.py | 321 ++++++++++++++++++++++++ .github/workflows/cmd-tests.yml | 14 ++ .github/workflows/cmd.yml | 2 +- .github/workflows/runtimes-matrix.json | 29 +++ substrate/frame/balances/src/weights.rs | 138 +++++----- 6 files changed, 584 insertions(+), 195 deletions(-) create mode 100644 .github/scripts/cmd/test_cmd.py create mode 100644 .github/workflows/cmd-tests.yml diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index 63bd6a2795aa..1c08b621467d 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -11,6 +11,8 @@ f = open('.github/workflows/runtimes-matrix.json', 'r') runtimesMatrix = json.load(f) +print(f'runtimesMatrix: {runtimesMatrix}\n') + runtimeNames = list(map(lambda x: x['name'], runtimesMatrix)) common_args = { @@ -67,130 +69,153 @@ for arg, config in common_args.items(): parser_ui.add_argument(arg, **config) +def main(): + global args, unknown, runtimesMatrix + args, unknown = parser.parse_known_args() + + print(f'args: {args}') + + if args.command == 'bench': + runtime_pallets_map = {} + failed_benchmarks = {} + successful_benchmarks = {} + + profile = "release" + + print(f'Provided runtimes: {args.runtime}') + # convert to mapped dict + runtimesMatrix = list(filter(lambda x: x['name'] in args.runtime, runtimesMatrix)) + runtimesMatrix = {x['name']: x for x in runtimesMatrix} + print(f'Filtered out runtimes: {runtimesMatrix}') + + # loop over remaining runtimes to collect available pallets + for runtime in runtimesMatrix.values(): + os.system(f"forklift cargo build -p {runtime['package']} --profile {profile} --features runtime-benchmarks") + print(f'-- listing pallets for benchmark for {runtime["name"]}') + wasm_file = f"target/{profile}/wbuild/{runtime['package']}/{runtime['package'].replace('-', '_')}.wasm" + output = os.popen( + f"frame-omni-bencher v1 benchmark pallet --no-csv-header --no-storage-info --no-min-squares --no-median-slopes --all --list --runtime={wasm_file}").read() + raw_pallets = output.strip().split('\n') + + all_pallets = set() + for pallet in raw_pallets: + if pallet: + all_pallets.add(pallet.split(',')[0].strip()) + + pallets = list(all_pallets) + print(f'Pallets in {runtime["name"]}: {pallets}') + runtime_pallets_map[runtime['name']] = pallets + + print(f'\n') + + # filter out only the specified pallets from collected runtimes/pallets + if args.pallet: + print(f'Pallets: {args.pallet}') + new_pallets_map = {} + # keep only specified pallets if they exist in the runtime + for runtime in runtime_pallets_map: + if set(args.pallet).issubset(set(runtime_pallets_map[runtime])): + new_pallets_map[runtime] = args.pallet + + runtime_pallets_map = new_pallets_map + + print(f'Filtered out runtimes & pallets: {runtime_pallets_map}\n') + + if not runtime_pallets_map: + if args.pallet and not args.runtime: + print(f"No pallets {args.pallet} found in any runtime") + elif args.runtime and not args.pallet: + print(f"{args.runtime} runtime does not have any pallets") + elif args.runtime and args.pallet: + print(f"No pallets {args.pallet} found in {args.runtime}") + else: + print('No runtimes found') + sys.exit(1) -args, unknown = parser.parse_known_args() - -print(f'args: {args}') - -if args.command == 'bench': - runtime_pallets_map = {} - failed_benchmarks = {} - successful_benchmarks = {} - - profile = "release" - - print(f'Provided runtimes: {args.runtime}') - # convert to mapped dict - runtimesMatrix = list(filter(lambda x: x['name'] in args.runtime, runtimesMatrix)) - runtimesMatrix = {x['name']: x for x in runtimesMatrix} - print(f'Filtered out runtimes: {runtimesMatrix}') - - # loop over remaining runtimes to collect available pallets - for runtime in runtimesMatrix.values(): - os.system(f"forklift cargo build -p {runtime['package']} --profile {profile} --features runtime-benchmarks") - print(f'-- listing pallets for benchmark for {runtime["name"]}') - wasm_file = f"target/{profile}/wbuild/{runtime['package']}/{runtime['package'].replace('-', '_')}.wasm" - output = os.popen( - f"frame-omni-bencher v1 benchmark pallet --no-csv-header --no-storage-info --no-min-squares --no-median-slopes --all --list --runtime={wasm_file}").read() - raw_pallets = output.strip().split('\n') - - all_pallets = set() - for pallet in raw_pallets: - if pallet: - all_pallets.add(pallet.split(',')[0].strip()) - - pallets = list(all_pallets) - print(f'Pallets in {runtime}: {pallets}') - runtime_pallets_map[runtime['name']] = pallets - - # filter out only the specified pallets from collected runtimes/pallets - if args.pallet: - print(f'Pallet: {args.pallet}') - new_pallets_map = {} - # keep only specified pallets if they exist in the runtime for runtime in runtime_pallets_map: - if set(args.pallet).issubset(set(runtime_pallets_map[runtime])): - new_pallets_map[runtime] = args.pallet - - runtime_pallets_map = new_pallets_map - - print(f'Filtered out runtimes & pallets: {runtime_pallets_map}') - - if not runtime_pallets_map: - if args.pallet and not args.runtime: - print(f"No pallets {args.pallet} found in any runtime") - elif args.runtime and not args.pallet: - print(f"{args.runtime} runtime does not have any pallets") - elif args.runtime and args.pallet: - print(f"No pallets {args.pallet} found in {args.runtime}") - else: - print('No runtimes found') - sys.exit(1) - - header_path = os.path.abspath('./substrate/HEADER-APACHE2') - - for runtime in runtime_pallets_map: - for pallet in runtime_pallets_map[runtime]: - config = runtimesMatrix[runtime] - print(f'-- config: {config}') - if runtime == 'dev': - # to support sub-modules (https://github.com/paritytech/command-bot/issues/275) - search_manifest_path = f"cargo metadata --locked --format-version 1 --no-deps | jq -r '.packages[] | select(.name == \"{pallet.replace('_', '-')}\") | .manifest_path'" - print(f'-- running: {search_manifest_path}') - manifest_path = os.popen(search_manifest_path).read() - if not manifest_path: - print(f'-- pallet {pallet} not found in dev runtime') - exit(1) - package_dir = os.path.dirname(manifest_path) - print(f'-- package_dir: {package_dir}') - print(f'-- manifest_path: {manifest_path}') - output_path = os.path.join(package_dir, "src", "weights.rs") - else: - default_path = f"./{config['path']}/src/weights" - xcm_path = f"./{config['path']}/src/weights/xcm" - output_path = default_path if not pallet.startswith("pallet_xcm_benchmarks") else xcm_path - print(f'-- benchmarking {pallet} in {runtime} into {output_path}') - cmd = f"frame-omni-bencher v1 benchmark pallet --extrinsic=* --runtime=target/{profile}/wbuild/{config['package']}/{config['package'].replace('-', '_')}.wasm --pallet={pallet} --header={header_path} --output={output_path} --wasm-execution=compiled --steps=50 --repeat=20 --heap-pages=4096 --no-storage-info --no-min-squares --no-median-slopes" - print(f'-- Running: {cmd}') - status = os.system(cmd) - if status != 0 and not args.continue_on_fail: - print(f'Failed to benchmark {pallet} in {runtime}') - sys.exit(1) - - # Otherwise collect failed benchmarks and print them at the end - # push failed pallets to failed_benchmarks - if status != 0: - failed_benchmarks[f'{runtime}'] = failed_benchmarks.get(f'{runtime}', []) + [pallet] - else: - successful_benchmarks[f'{runtime}'] = successful_benchmarks.get(f'{runtime}', []) + [pallet] - - if failed_benchmarks: - print('❌ Failed benchmarks of runtimes/pallets:') - for runtime, pallets in failed_benchmarks.items(): - print(f'-- {runtime}: {pallets}') - - if successful_benchmarks: - print('✅ Successful benchmarks of runtimes/pallets:') - for runtime, pallets in successful_benchmarks.items(): - print(f'-- {runtime}: {pallets}') - -elif args.command == 'fmt': - command = f"cargo +nightly fmt" - print(f'Formatting with `{command}`') - nightly_status = os.system(f'{command}') - taplo_status = os.system('taplo format --config .config/taplo.toml') - - if (nightly_status != 0 or taplo_status != 0) and not args.continue_on_fail: - print('❌ Failed to format code') - sys.exit(1) - -elif args.command == 'update-ui': - command = 'sh ./scripts/update-ui-tests.sh' - print(f'Updating ui with `{command}`') - status = os.system(f'{command}') - - if status != 0 and not args.continue_on_fail: - print('❌ Failed to format code') - sys.exit(1) - -print('🚀 Done') + for pallet in runtime_pallets_map[runtime]: + config = runtimesMatrix[runtime] + header_path = os.path.abspath(config['header']) + template = None + + print(f'-- config: {config}') + if runtime == 'dev': + # to support sub-modules (https://github.com/paritytech/command-bot/issues/275) + search_manifest_path = f"cargo metadata --locked --format-version 1 --no-deps | jq -r '.packages[] | select(.name == \"{pallet.replace('_', '-')}\") | .manifest_path'" + print(f'-- running: {search_manifest_path}') + manifest_path = os.popen(search_manifest_path).read() + if not manifest_path: + print(f'-- pallet {pallet} not found in dev runtime') + exit(1) + package_dir = os.path.dirname(manifest_path) + print(f'-- package_dir: {package_dir}') + print(f'-- manifest_path: {manifest_path}') + output_path = os.path.join(package_dir, "src", "weights.rs") + template = config['template'] + else: + default_path = f"./{config['path']}/src/weights" + xcm_path = f"./{config['path']}/src/weights/xcm" + output_path = default_path + if pallet.startswith("pallet_xcm_benchmarks"): + template = config['template'] + output_path = xcm_path + + print(f'-- benchmarking {pallet} in {runtime} into {output_path}') + cmd = f"frame-omni-bencher v1 benchmark pallet " \ + f"--extrinsic=* " \ + f"--runtime=target/{profile}/wbuild/{config['package']}/{config['package'].replace('-', '_')}.wasm " \ + f"--pallet={pallet} " \ + f"--header={header_path} " \ + f"--output={output_path} " \ + f"--wasm-execution=compiled " \ + f"--steps=50 " \ + f"--repeat=20 " \ + f"--heap-pages=4096 " \ + f"{f'--template={template} ' if template else ''}" \ + f"--no-storage-info --no-min-squares --no-median-slopes" + print(f'-- Running: {cmd} \n') + status = os.system(cmd) + if status != 0 and not args.continue_on_fail: + print(f'Failed to benchmark {pallet} in {runtime}') + sys.exit(1) + + # Otherwise collect failed benchmarks and print them at the end + # push failed pallets to failed_benchmarks + if status != 0: + failed_benchmarks[f'{runtime}'] = failed_benchmarks.get(f'{runtime}', []) + [pallet] + else: + successful_benchmarks[f'{runtime}'] = successful_benchmarks.get(f'{runtime}', []) + [pallet] + + if failed_benchmarks: + print('❌ Failed benchmarks of runtimes/pallets:') + for runtime, pallets in failed_benchmarks.items(): + print(f'-- {runtime}: {pallets}') + + if successful_benchmarks: + print('✅ Successful benchmarks of runtimes/pallets:') + for runtime, pallets in successful_benchmarks.items(): + print(f'-- {runtime}: {pallets}') + + elif args.command == 'fmt': + command = f"cargo +nightly fmt" + print(f'Formatting with `{command}`') + nightly_status = os.system(f'{command}') + taplo_status = os.system('taplo format --config .config/taplo.toml') + + if (nightly_status != 0 or taplo_status != 0) and not args.continue_on_fail: + print('❌ Failed to format code') + sys.exit(1) + + elif args.command == 'update-ui': + command = 'sh ./scripts/update-ui-tests.sh' + print(f'Updating ui with `{command}`') + status = os.system(f'{command}') + + if status != 0 and not args.continue_on_fail: + print('❌ Failed to format code') + sys.exit(1) + + print('🚀 Done') + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/.github/scripts/cmd/test_cmd.py b/.github/scripts/cmd/test_cmd.py new file mode 100644 index 000000000000..4cf1b290915d --- /dev/null +++ b/.github/scripts/cmd/test_cmd.py @@ -0,0 +1,321 @@ +import unittest +from unittest.mock import patch, mock_open, MagicMock, call +import json +import sys +import os +import argparse + +# Mock data for runtimes-matrix.json +mock_runtimes_matrix = [ + {"name": "dev", "package": "kitchensink-runtime", "path": "substrate/frame", "header": "substrate/HEADER-APACHE2", "template": "substrate/.maintain/frame-weight-template.hbs"}, + {"name": "westend", "package": "westend-runtime", "path": "polkadot/runtime/westend", "header": "polkadot/file_header.txt", "template": "polkadot/xcm/pallet-xcm-benchmarks/template.hbs"}, + {"name": "rococo", "package": "rococo-runtime", "path": "polkadot/runtime/rococo", "header": "polkadot/file_header.txt", "template": "polkadot/xcm/pallet-xcm-benchmarks/template.hbs"}, + {"name": "asset-hub-westend", "package": "asset-hub-westend-runtime", "path": "cumulus/parachains/runtimes/assets/asset-hub-westend", "header": "cumulus/file_header.txt", "template": "cumulus/templates/xcm-bench-template.hbs"}, +] + +def get_mock_bench_output(runtime, pallets, output_path, header, template = None): + return f"frame-omni-bencher v1 benchmark pallet --extrinsic=* " \ + f"--runtime=target/release/wbuild/{runtime}-runtime/{runtime.replace('-', '_')}_runtime.wasm " \ + f"--pallet={pallets} --header={header} " \ + f"--output={output_path} " \ + f"--wasm-execution=compiled " \ + f"--steps=50 --repeat=20 --heap-pages=4096 " \ + f"{f'--template={template} ' if template else ''}" \ + f"--no-storage-info --no-min-squares --no-median-slopes" + +class TestCmd(unittest.TestCase): + + def setUp(self): + self.patcher1 = patch('builtins.open', new_callable=mock_open, read_data=json.dumps(mock_runtimes_matrix)) + self.patcher2 = patch('json.load', return_value=mock_runtimes_matrix) + self.patcher3 = patch('argparse.ArgumentParser.parse_known_args') + self.patcher4 = patch('os.system', return_value=0) + self.patcher5 = patch('os.popen') + + self.mock_open = self.patcher1.start() + self.mock_json_load = self.patcher2.start() + self.mock_parse_args = self.patcher3.start() + self.mock_system = self.patcher4.start() + self.mock_popen = self.patcher5.start() + + # Ensure that cmd.py uses the mock_runtimes_matrix + import cmd + cmd.runtimesMatrix = mock_runtimes_matrix + + def tearDown(self): + self.patcher1.stop() + self.patcher2.stop() + self.patcher3.stop() + self.patcher4.stop() + self.patcher5.stop() + + def test_bench_command_normal_execution_all_runtimes(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=list(map(lambda x: x['name'], mock_runtimes_matrix)), + pallet=['pallet_balances'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + + self.mock_popen.return_value.read.side_effect = [ + "pallet_balances\npallet_staking\npallet_something\n", # Output for dev runtime + "pallet_balances\npallet_staking\npallet_something\n", # Output for westend runtime + "pallet_staking\npallet_something\n", # Output for rococo runtime - no pallet here + "pallet_balances\npallet_staking\npallet_something\n", # Output for asset-hub-westend runtime + "./substrate/frame/balances/Cargo.toml\n", # Mock manifest path for dev -> pallet_balances + ] + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p kitchensink-runtime --profile release --features runtime-benchmarks"), + call("forklift cargo build -p westend-runtime --profile release --features runtime-benchmarks"), + call("forklift cargo build -p rococo-runtime --profile release --features runtime-benchmarks"), + call("forklift cargo build -p asset-hub-westend-runtime --profile release --features runtime-benchmarks"), + + call(get_mock_bench_output('kitchensink', 'pallet_balances', './substrate/frame/balances/src/weights.rs', os.path.abspath('substrate/HEADER-APACHE2'), "substrate/.maintain/frame-weight-template.hbs")), + call(get_mock_bench_output('westend', 'pallet_balances', './polkadot/runtime/westend/src/weights', os.path.abspath('polkadot/file_header.txt'))), + # skips rococo benchmark + call(get_mock_bench_output('asset-hub-westend', 'pallet_balances', './cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights', os.path.abspath('cumulus/file_header.txt'))), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_normal_execution(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['westend'], + pallet=['pallet_balances', 'pallet_staking'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + header_path = os.path.abspath('polkadot/file_header.txt') + self.mock_popen.return_value.read.side_effect = [ + "pallet_balances\npallet_staking\npallet_something\n", # Output for westend runtime + ] + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p westend-runtime --profile release --features runtime-benchmarks"), + + # Westend runtime calls + call(get_mock_bench_output('westend', 'pallet_balances', './polkadot/runtime/westend/src/weights', header_path)), + call(get_mock_bench_output('westend', 'pallet_staking', './polkadot/runtime/westend/src/weights', header_path)), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + + def test_bench_command_normal_execution_xcm(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['westend'], + pallet=['pallet_xcm_benchmarks::generic'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + header_path = os.path.abspath('polkadot/file_header.txt') + self.mock_popen.return_value.read.side_effect = [ + "pallet_balances\npallet_staking\npallet_something\npallet_xcm_benchmarks::generic\n", # Output for westend runtime + ] + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p westend-runtime --profile release --features runtime-benchmarks"), + + # Westend runtime calls + call(get_mock_bench_output( + 'westend', + 'pallet_xcm_benchmarks::generic', + './polkadot/runtime/westend/src/weights/xcm', + header_path, + "polkadot/xcm/pallet-xcm-benchmarks/template.hbs" + )), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_two_runtimes_two_pallets(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['westend', 'rococo'], + pallet=['pallet_balances', 'pallet_staking'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + self.mock_popen.return_value.read.side_effect = [ + "pallet_staking\npallet_balances\n", # Output for westend runtime + "pallet_staking\npallet_balances\n", # Output for rococo runtime + ] + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + header_path = os.path.abspath('polkadot/file_header.txt') + + expected_calls = [ + # Build calls + call("forklift cargo build -p westend-runtime --profile release --features runtime-benchmarks"), + call("forklift cargo build -p rococo-runtime --profile release --features runtime-benchmarks"), + # Westend runtime calls + call(get_mock_bench_output('westend', 'pallet_staking', './polkadot/runtime/westend/src/weights', header_path)), + call(get_mock_bench_output('westend', 'pallet_balances', './polkadot/runtime/westend/src/weights', header_path)), + # Rococo runtime calls + call(get_mock_bench_output('rococo', 'pallet_staking', './polkadot/runtime/rococo/src/weights', header_path)), + call(get_mock_bench_output('rococo', 'pallet_balances', './polkadot/runtime/rococo/src/weights', header_path)), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_one_dev_runtime(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['dev'], + pallet=['pallet_balances'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + manifest_dir = "substrate/frame/kitchensink" + self.mock_popen.return_value.read.side_effect = [ + "pallet_balances\npallet_something", # Output for dev runtime + manifest_dir + "/Cargo.toml" # Output for manifest path in dev runtime + ] + header_path = os.path.abspath('substrate/HEADER-APACHE2') + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p kitchensink-runtime --profile release --features runtime-benchmarks"), + # Westend runtime calls + call(get_mock_bench_output( + 'kitchensink', + 'pallet_balances', + manifest_dir + "/src/weights.rs", + header_path, + "substrate/.maintain/frame-weight-template.hbs" + )), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_one_cumulus_runtime(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['asset-hub-westend'], + pallet=['pallet_assets'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + self.mock_popen.return_value.read.side_effect = [ + "pallet_assets\n", # Output for asset-hub-westend runtime + ] + header_path = os.path.abspath('cumulus/file_header.txt') + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p asset-hub-westend-runtime --profile release --features runtime-benchmarks"), + # Asset-hub-westend runtime calls + call(get_mock_bench_output( + 'asset-hub-westend', + 'pallet_assets', + './cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights', + header_path + )), + ] + + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_one_cumulus_runtime_xcm(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['asset-hub-westend'], + pallet=['pallet_xcm_benchmarks::generic', 'pallet_assets'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + self.mock_popen.return_value.read.side_effect = [ + "pallet_assets\npallet_xcm_benchmarks::generic\n", # Output for asset-hub-westend runtime + ] + header_path = os.path.abspath('cumulus/file_header.txt') + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p asset-hub-westend-runtime --profile release --features runtime-benchmarks"), + # Asset-hub-westend runtime calls + call(get_mock_bench_output( + 'asset-hub-westend', + 'pallet_xcm_benchmarks::generic', + './cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm', + header_path, + "cumulus/templates/xcm-bench-template.hbs" + )), + call(get_mock_bench_output( + 'asset-hub-westend', + 'pallet_assets', + './cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights', + header_path + )), + ] + + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + @patch('argparse.ArgumentParser.parse_known_args', return_value=(argparse.Namespace(command='fmt', continue_on_fail=False), [])) + @patch('os.system', return_value=0) + def test_fmt_command(self, mock_system, mock_parse_args): + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + mock_system.assert_any_call('cargo +nightly fmt') + mock_system.assert_any_call('taplo format --config .config/taplo.toml') + + @patch('argparse.ArgumentParser.parse_known_args', return_value=(argparse.Namespace(command='update-ui', continue_on_fail=False), [])) + @patch('os.system', return_value=0) + def test_update_ui_command(self, mock_system, mock_parse_args): + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + mock_system.assert_called_with('sh ./scripts/update-ui-tests.sh') + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/.github/workflows/cmd-tests.yml b/.github/workflows/cmd-tests.yml new file mode 100644 index 000000000000..87d7ee1dcc2d --- /dev/null +++ b/.github/workflows/cmd-tests.yml @@ -0,0 +1,14 @@ +name: Command Bot Tests + +on: + pull_request: + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: python3 .github/scripts/cmd/test_cmd.py \ No newline at end of file diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index dfdf771a6105..79a4f6c3b19c 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -358,7 +358,7 @@ jobs: git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - git pull origin ${{ needs.get-pr-branch.outputs.pr-branch }} + git pull --rebase origin ${{ needs.get-pr-branch.outputs.pr-branch }} git add . git restore --staged Cargo.lock # ignore changes in Cargo.lock git commit -m "Update from ${{ github.actor }} running command '${{ steps.get-pr-comment.outputs.group2 }}'" || true diff --git a/.github/workflows/runtimes-matrix.json b/.github/workflows/runtimes-matrix.json index 45a3acd3f166..102437876daf 100644 --- a/.github/workflows/runtimes-matrix.json +++ b/.github/workflows/runtimes-matrix.json @@ -3,6 +3,8 @@ "name": "dev", "package": "kitchensink-runtime", "path": "substrate/frame", + "header": "substrate/HEADER-APACHE2", + "template": "substrate/.maintain/frame-weight-template.hbs", "uri": null, "is_relay": false }, @@ -10,6 +12,8 @@ "name": "westend", "package": "westend-runtime", "path": "polkadot/runtime/westend", + "header": "polkadot/file_header.txt", + "template": "polkadot/xcm/pallet-xcm-benchmarks/template.hbs", "uri": "wss://try-runtime-westend.polkadot.io:443", "is_relay": true }, @@ -17,6 +21,8 @@ "name": "rococo", "package": "rococo-runtime", "path": "polkadot/runtime/rococo", + "header": "polkadot/file_header.txt", + "template": "polkadot/xcm/pallet-xcm-benchmarks/template.hbs", "uri": "wss://try-runtime-rococo.polkadot.io:443", "is_relay": true }, @@ -24,6 +30,8 @@ "name": "asset-hub-westend", "package": "asset-hub-westend-runtime", "path": "cumulus/parachains/runtimes/assets/asset-hub-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", "uri": "wss://westend-asset-hub-rpc.polkadot.io:443", "is_relay": false }, @@ -31,6 +39,8 @@ "name": "asset-hub-rococo", "package": "asset-hub-rococo-runtime", "path": "cumulus/parachains/runtimes/assets/asset-hub-rococo", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", "uri": "wss://rococo-asset-hub-rpc.polkadot.io:443", "is_relay": false }, @@ -38,6 +48,8 @@ "name": "bridge-hub-rococo", "package": "bridge-hub-rococo-runtime", "path": "cumulus/parachains/runtimes/bridges/bridge-hub-rococo", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", "uri": "wss://rococo-bridge-hub-rpc.polkadot.io:443", "is_relay": false }, @@ -45,6 +57,8 @@ "name": "bridge-hub-westend", "package": "bridge-hub-rococo-runtime", "path": "cumulus/parachains/runtimes/bridges/bridge-hub-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", "uri": "wss://westend-bridge-hub-rpc.polkadot.io:443", "is_relay": false }, @@ -52,12 +66,16 @@ "name": "collectives-westend", "package": "collectives-westend-runtime", "path": "cumulus/parachains/runtimes/collectives/collectives-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", "uri": "wss://westend-collectives-rpc.polkadot.io:443" }, { "name": "contracts-rococo", "package": "contracts-rococo-runtime", "path": "cumulus/parachains/runtimes/contracts/contracts-rococo", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", "uri": "wss://rococo-contracts-rpc.polkadot.io:443", "is_relay": false }, @@ -65,6 +83,8 @@ "name": "coretime-rococo", "package": "coretime-rococo-runtime", "path": "cumulus/parachains/runtimes/coretime/coretime-rococo", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", "uri": "wss://rococo-coretime-rpc.polkadot.io:443", "is_relay": false }, @@ -72,6 +92,8 @@ "name": "coretime-westend", "package": "coretime-westend-runtime", "path": "cumulus/parachains/runtimes/coretime/coretime-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", "uri": "wss://westend-coretime-rpc.polkadot.io:443", "is_relay": false }, @@ -79,12 +101,17 @@ "name": "glutton-westend", "package": "glutton-westend-runtime", "path": "cumulus/parachains/runtimes/gluttons/glutton-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "uri": null, "is_relay": false }, { "name": "people-rococo", "package": "people-rococo-runtime", "path": "cumulus/parachains/runtimes/people/people-rococo", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", "uri": "wss://rococo-people-rpc.polkadot.io:443", "is_relay": false }, @@ -92,6 +119,8 @@ "name": "people-westend", "package": "people-westend-runtime", "path": "cumulus/parachains/runtimes/people/people-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", "uri": "wss://westend-people-rpc.polkadot.io:443", "is_relay": false } diff --git a/substrate/frame/balances/src/weights.rs b/substrate/frame/balances/src/weights.rs index e82c97160efc..55decef273f6 100644 --- a/substrate/frame/balances/src/weights.rs +++ b/substrate/frame/balances/src/weights.rs @@ -17,27 +17,27 @@ //! Autogenerated weights for `pallet_balances` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 42.0.0 +//! DATE: 2024-09-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` +//! HOSTNAME: `8f4ffe8f7785`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: -// target/production/substrate-node +// frame-omni-bencher +// v1 // benchmark // pallet -// --steps=50 -// --repeat=20 // --extrinsic=* +// --runtime=target/release/wbuild/kitchensink-runtime/kitchensink_runtime.wasm +// --pallet=pallet_balances +// --header=/__w/polkadot-sdk/polkadot-sdk/substrate/HEADER-APACHE2 +// --output=/__w/polkadot-sdk/polkadot-sdk/substrate/frame/balances/src/weights.rs // --wasm-execution=compiled +// --steps=50 +// --repeat=20 // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_balances -// --chain=dev -// --header=./substrate/HEADER-APACHE2 -// --output=./substrate/frame/balances/src/weights.rs -// --template=./substrate/.maintain/frame-weight-template.hbs +// --template=substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -71,8 +71,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 47_552_000 picoseconds. - Weight::from_parts(48_363_000, 3593) + // Minimum execution time: 75_624_000 picoseconds. + Weight::from_parts(77_290_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -82,8 +82,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 37_565_000 picoseconds. - Weight::from_parts(38_159_000, 3593) + // Minimum execution time: 60_398_000 picoseconds. + Weight::from_parts(61_290_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -91,10 +91,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_set_balance_creating() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `52` // Estimated: `3593` - // Minimum execution time: 14_147_000 picoseconds. - Weight::from_parts(14_687_000, 3593) + // Minimum execution time: 18_963_000 picoseconds. + Weight::from_parts(19_802_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -102,10 +102,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_set_balance_killing() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `52` // Estimated: `3593` - // Minimum execution time: 19_188_000 picoseconds. - Weight::from_parts(19_929_000, 3593) + // Minimum execution time: 30_517_000 picoseconds. + Weight::from_parts(31_293_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -113,10 +113,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `103` + // Measured: `52` // Estimated: `6196` - // Minimum execution time: 48_903_000 picoseconds. - Weight::from_parts(49_944_000, 6196) + // Minimum execution time: 77_017_000 picoseconds. + Weight::from_parts(78_184_000, 6196) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -126,8 +126,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 46_573_000 picoseconds. - Weight::from_parts(47_385_000, 3593) + // Minimum execution time: 75_600_000 picoseconds. + Weight::from_parts(76_817_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -135,10 +135,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_unreserve() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `52` // Estimated: `3593` - // Minimum execution time: 16_750_000 picoseconds. - Weight::from_parts(17_233_000, 3593) + // Minimum execution time: 24_503_000 picoseconds. + Weight::from_parts(25_026_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -149,10 +149,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + u * (135 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 16_333_000 picoseconds. - Weight::from_parts(16_588_000, 990) - // Standard Error: 12_254 - .saturating_add(Weight::from_parts(13_973_659, 0).saturating_mul(u.into())) + // Minimum execution time: 24_077_000 picoseconds. + Weight::from_parts(24_339_000, 990) + // Standard Error: 18_669 + .saturating_add(Weight::from_parts(21_570_294, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -161,22 +161,22 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_265_000 picoseconds. - Weight::from_parts(6_594_000, 0) + // Minimum execution time: 8_070_000 picoseconds. + Weight::from_parts(8_727_000, 0) } fn burn_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 30_151_000 picoseconds. - Weight::from_parts(30_968_000, 0) + // Minimum execution time: 46_978_000 picoseconds. + Weight::from_parts(47_917_000, 0) } fn burn_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 20_055_000 picoseconds. - Weight::from_parts(20_711_000, 0) + // Minimum execution time: 31_141_000 picoseconds. + Weight::from_parts(31_917_000, 0) } } @@ -188,8 +188,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 47_552_000 picoseconds. - Weight::from_parts(48_363_000, 3593) + // Minimum execution time: 75_624_000 picoseconds. + Weight::from_parts(77_290_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -199,8 +199,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 37_565_000 picoseconds. - Weight::from_parts(38_159_000, 3593) + // Minimum execution time: 60_398_000 picoseconds. + Weight::from_parts(61_290_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -208,10 +208,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_set_balance_creating() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `52` // Estimated: `3593` - // Minimum execution time: 14_147_000 picoseconds. - Weight::from_parts(14_687_000, 3593) + // Minimum execution time: 18_963_000 picoseconds. + Weight::from_parts(19_802_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -219,10 +219,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_set_balance_killing() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `52` // Estimated: `3593` - // Minimum execution time: 19_188_000 picoseconds. - Weight::from_parts(19_929_000, 3593) + // Minimum execution time: 30_517_000 picoseconds. + Weight::from_parts(31_293_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -230,10 +230,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `103` + // Measured: `52` // Estimated: `6196` - // Minimum execution time: 48_903_000 picoseconds. - Weight::from_parts(49_944_000, 6196) + // Minimum execution time: 77_017_000 picoseconds. + Weight::from_parts(78_184_000, 6196) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -243,8 +243,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 46_573_000 picoseconds. - Weight::from_parts(47_385_000, 3593) + // Minimum execution time: 75_600_000 picoseconds. + Weight::from_parts(76_817_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -252,10 +252,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_unreserve() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `52` // Estimated: `3593` - // Minimum execution time: 16_750_000 picoseconds. - Weight::from_parts(17_233_000, 3593) + // Minimum execution time: 24_503_000 picoseconds. + Weight::from_parts(25_026_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -266,10 +266,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + u * (135 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 16_333_000 picoseconds. - Weight::from_parts(16_588_000, 990) - // Standard Error: 12_254 - .saturating_add(Weight::from_parts(13_973_659, 0).saturating_mul(u.into())) + // Minimum execution time: 24_077_000 picoseconds. + Weight::from_parts(24_339_000, 990) + // Standard Error: 18_669 + .saturating_add(Weight::from_parts(21_570_294, 0).saturating_mul(u.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -278,21 +278,21 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_265_000 picoseconds. - Weight::from_parts(6_594_000, 0) + // Minimum execution time: 8_070_000 picoseconds. + Weight::from_parts(8_727_000, 0) } fn burn_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 30_151_000 picoseconds. - Weight::from_parts(30_968_000, 0) + // Minimum execution time: 46_978_000 picoseconds. + Weight::from_parts(47_917_000, 0) } fn burn_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 20_055_000 picoseconds. - Weight::from_parts(20_711_000, 0) + // Minimum execution time: 31_141_000 picoseconds. + Weight::from_parts(31_917_000, 0) } } From 3191ea93888e97f175ef4821f908ab22fa755b41 Mon Sep 17 00:00:00 2001 From: Ron Date: Fri, 6 Sep 2024 00:41:09 +0800 Subject: [PATCH 293/382] Update bridges/snowbridge/primitives/core/src/location.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/primitives/core/src/location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index ee91432b3184..c126618bba35 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -78,7 +78,7 @@ impl DescribeLocation for DescribeTokenTerminal { // Pallet [PalletInstance(instance)] => Some((b"PalletInstance", *instance).encode()), [PalletInstance(instance), GeneralIndex(index)] => - Some((b"PalletInstance", *instance, "GeneralIndex", *index).encode()), + Some((b"PalletInstance", *instance, b"GeneralIndex", *index).encode()), [PalletInstance(instance), GeneralKey { data, .. }] => Some((b"PalletInstance", *instance, b"GeneralKey", *data).encode()), From 57b91712e4f5c3ab8d2a4acc90f468149fb2a624 Mon Sep 17 00:00:00 2001 From: Ron Date: Fri, 6 Sep 2024 00:50:15 +0800 Subject: [PATCH 294/382] Update bridges/snowbridge/pallets/system/src/lib.rs Co-authored-by: Adrian Catangiu --- bridges/snowbridge/pallets/system/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index c0be7dcb6e03..94fb80066960 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -613,7 +613,7 @@ pub mod pallet { /// Fee required: No /// /// - `origin`: Must be root - /// - `location`: Location of the asset + /// - `location`: Location of the asset (relative to this chain) /// - `metadata`: Metadata to include in the instantiated ERC20 contract on Ethereum #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::register_token())] From 0abc9ca6a53c4b56d5bf7a9f38811fe721fb7b5e Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 6 Sep 2024 01:16:18 +0800 Subject: [PATCH 295/382] Tx fee waived for root --- bridges/snowbridge/pallets/system/src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index c0be7dcb6e03..bcff080d2cda 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -136,6 +136,7 @@ where #[frame_support::pallet] pub mod pallet { + use frame_support::dispatch::PostDispatchInfo; use snowbridge_core::StaticLookup; use sp_core::U256; @@ -621,7 +622,7 @@ pub mod pallet { origin: OriginFor, location: Box, metadata: AssetMetadata, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { ensure_root(origin)?; let location: Location = @@ -631,7 +632,10 @@ pub mod pallet { Self::do_register_token(&location, metadata, pays_fee)?; - Ok(()) + Ok(PostDispatchInfo { + actual_weight: Some(T::WeightInfo::register_token()), + pays_fee: Pays::No, + }) } } From 41dc959371f45b8e25a424a7c58c17fb2a2091d1 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 6 Sep 2024 01:19:06 +0800 Subject: [PATCH 296/382] Fix benchmark --- bridges/snowbridge/pallets/system/src/benchmarking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/benchmarking.rs b/bridges/snowbridge/pallets/system/src/benchmarking.rs index 4314c7528af6..20798b7c3493 100644 --- a/bridges/snowbridge/pallets/system/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/system/src/benchmarking.rs @@ -168,7 +168,7 @@ mod benchmarks { T::Token::mint_into(&caller, amount)?; - let relay_token_asset_id: Location = Location::new(1, [GlobalConsensus(Westend)]); + let relay_token_asset_id: Location = Location::parent(); let asset = Box::new(VersionedLocation::V4(relay_token_asset_id)); let asset_metadata = AssetMetadata { name: "wnd".as_bytes().to_vec().try_into().unwrap(), From b7cf50747732757b1ca0bc202a7d04e2094b284a Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 6 Sep 2024 09:09:47 +0800 Subject: [PATCH 297/382] Revamp test to use relative location --- .../primitives/router/src/inbound/tests.rs | 21 +++++----- .../src/tests/snowbridge.rs | 39 ++++++++++++++----- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index ea1197cc38ea..7b4469c1a68c 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -59,12 +59,16 @@ fn test_reanchor_relay_token() { #[test] fn test_reanchor_pna_from_ah() { - let asset_id: Location = + let asset_id_in_ah: Location = Location { parents: 0, interior: [PalletInstance(50), GeneralIndex(2)].into() }; - let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let asset_id: Location = Location { + parents: 1, + interior: [Parachain(1000), PalletInstance(50), GeneralIndex(2)].into(), + }; + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); let mut reanchored_asset = asset_id.clone(); - assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + assert_ok!(reanchored_asset.reanchor(ðereum, &bh_context)); assert_eq!( reanchored_asset, Location { @@ -78,27 +82,26 @@ fn test_reanchor_pna_from_ah() { .into() } ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); let mut reanchored_asset = reanchored_asset.clone(); assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); - assert_eq!(reanchored_asset, asset_id); + assert_eq!(reanchored_asset, asset_id_in_ah); } #[test] fn test_reanchor_pna_from_para() { + let asset_id_in_ah: Location = Location { parents: 1, interior: [Parachain(2000)].into() }; let asset_id: Location = Location { parents: 1, interior: [Parachain(2000)].into() }; - let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); let mut reanchored_asset = asset_id.clone(); - assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + assert_ok!(reanchored_asset.reanchor(ðereum, &bh_context)); assert_eq!( reanchored_asset, Location { parents: 1, interior: [GlobalConsensus(Westend), Parachain(2000)].into() } ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); let mut reanchored_asset = reanchored_asset.clone(); assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); - assert_eq!(reanchored_asset, asset_id); + assert_eq!(reanchored_asset, asset_id_in_ah); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index abcc38c16ab3..389b3ec33427 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -458,6 +458,15 @@ fn transfer_ah_token() { let asset_id: Location = [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())].into(); + let asset_id_in_bh: Location = Location::new( + 1, + [ + Parachain(AssetHubWestend::para_id().into()), + PalletInstance(ASSETS_PALLET_ID), + GeneralIndex(RESERVABLE_ASSET_ID.into()), + ], + ); + let asset_id_after_reanchored = Location::new(1, [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())]) .appended_with(asset_id.clone().interior) @@ -467,12 +476,17 @@ fn transfer_ah_token() { // Register token BridgeHubWestend::execute_with(|| { - type Runtime = ::Runtime; + type RuntimeOrigin = ::RuntimeOrigin; - snowbridge_pallet_system::ForeignToNativeId::::insert( - token_id, - asset_id_after_reanchored.clone(), - ); + assert_ok!(::EthereumSystem::register_token( + RuntimeOrigin::root(), + Box::new(VersionedLocation::V4(asset_id_in_bh.clone())), + AssetMetadata { + name: "ah_asset".as_bytes().to_vec().try_into().unwrap(), + symbol: "ah_asset".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + )); }); // Mint some token @@ -637,12 +651,17 @@ fn transfer_penpal_native_token() { // Create token BridgeHubWestend::execute_with(|| { - type Runtime = ::Runtime; + type RuntimeOrigin = ::RuntimeOrigin; - snowbridge_pallet_system::ForeignToNativeId::::insert( - token_id, - penpal_asset_location_after_reanchored.clone(), - ); + assert_ok!(::EthereumSystem::register_token( + RuntimeOrigin::root(), + Box::new(VersionedLocation::V4(penpal_asset_location.clone())), + AssetMetadata { + name: "penpal_asset".as_bytes().to_vec().try_into().unwrap(), + symbol: "penpal_asset".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + )); }); // Send token to Ethereum From 170bccbfe4c0526807ff6d43578e3ee0878140bf Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 6 Sep 2024 10:49:12 +0800 Subject: [PATCH 298/382] Remove bridge-hub-rococo from CI --- .github/workflows/parachain.yml | 62 ++++++++++++++++----------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/.github/workflows/parachain.yml b/.github/workflows/parachain.yml index 34c569acac06..df0632f05daf 100644 --- a/.github/workflows/parachain.yml +++ b/.github/workflows/parachain.yml @@ -67,12 +67,8 @@ jobs: -p snowbridge-router-primitives -p snowbridge-runtime-common -p snowbridge-runtime-test-common - -p bridge-hub-rococo-runtime - -p bridge-hub-rococo-integration-tests - -p asset-hub-rococo-integration-tests -p bridge-hub-westend-runtime -p bridge-hub-westend-integration-tests - -p asset-hub-westend-integration-tests -- --check - name: install taplo @@ -128,9 +124,33 @@ jobs: rustup component add rustfmt --toolchain nightly rustup show - name: cargo check - run: cargo check --release --verbose --package bridge-hub-westend-runtime --features fast-runtime --features runtime-benchmarks --features try-runtime + run: | + cargo check -p snowbridge-pallet-ethereum-client + -p snowbridge-pallet-inbound-queue + -p snowbridge-pallet-outbound-queue + -p snowbridge-outbound-queue-merkle-tree + -p snowbridge-pallet-system + -p snowbridge-beacon-primitives + -p snowbridge-core + -p snowbridge-ethereum + -p snowbridge-router-primitives + -p snowbridge-runtime-common - name: clippy - run: cargo clippy --all-features -- -D warnings + run: | + cargo clippy -p snowbridge-pallet-ethereum-client + -p snowbridge-pallet-inbound-queue + -p snowbridge-pallet-outbound-queue + -p snowbridge-outbound-queue-merkle-tree + -p snowbridge-pallet-system + -p snowbridge-beacon-primitives + -p snowbridge-core + -p snowbridge-ethereum + -p snowbridge-router-primitives + -p snowbridge-runtime-common + -p snowbridge-runtime-test-common + -p bridge-hub-westend-runtime + -p bridge-hub-westend-integration-tests + -- -D warnings test: needs: check @@ -236,41 +256,19 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: setup rust toolchain run: rustup show - - name: check bridge-hub fast-runtime + - name: check bridge-hub features run: > cargo check --release --verbose - -p bridge-hub-rococo-runtime - -p bridge-hub-westend-runtime - --features fast-runtime - - name: check bridge-hub runtime-benchmarks - run: > - cargo check - --release --verbose - -p bridge-hub-rococo-runtime - -p bridge-hub-westend-runtime - --features runtime-benchmarks - - name: check bridge-hub try-runtime - run: > - cargo check - --release --verbose - -p bridge-hub-rococo-runtime -p bridge-hub-westend-runtime + --features fast-runtime + --features runtime-benchmarks --features try-runtime - - name: check bridge-hub all features - run: > - cargo check - --release --verbose - -p bridge-hub-rococo-runtime - -p bridge-hub-westend-runtime - --all-features - - name: check asset-hub all features + - name: check asset-hub run: > cargo check --release --verbose - -p asset-hub-rococo-runtime -p asset-hub-westend-runtime - --all-features runtime-tests: needs: check From 8bd2dc86cdb9f106ec825a738c86db1064431944 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 6 Sep 2024 10:56:53 +0800 Subject: [PATCH 299/382] Fix ci --- .github/workflows/parachain.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/parachain.yml b/.github/workflows/parachain.yml index df0632f05daf..aa2b2bce5339 100644 --- a/.github/workflows/parachain.yml +++ b/.github/workflows/parachain.yml @@ -124,7 +124,7 @@ jobs: rustup component add rustfmt --toolchain nightly rustup show - name: cargo check - run: | + run: > cargo check -p snowbridge-pallet-ethereum-client -p snowbridge-pallet-inbound-queue -p snowbridge-pallet-outbound-queue @@ -136,7 +136,7 @@ jobs: -p snowbridge-router-primitives -p snowbridge-runtime-common - name: clippy - run: | + run: > cargo clippy -p snowbridge-pallet-ethereum-client -p snowbridge-pallet-inbound-queue -p snowbridge-pallet-outbound-queue From fdb4554e26ebdd4d729158501a3ddb3c6ebdfb6f Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Fri, 6 Sep 2024 16:21:09 +0800 Subject: [PATCH 300/382] Introduce `BlockGap` (#5592) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, block gaps could only be created by warp sync, but block gaps will also be generated by fast sync once #5406 is fixed. This PR is part 1 of the detailed implementation plan in https://github.com/paritytech/polkadot-sdk/issues/5406#issuecomment-2325064863: refactor `BlockGap`. This refactor converts the existing `(NumberFor, NumberFor)` into a dedicated `BlockGap>` struct. This change is purely structural and does not alter existing logic, but lays the groundwork for the follow-up PR. The compatibility concern caused by the new structure is addressed in the second commit. cc @dmitry-markin --------- Co-authored-by: Bastian Köcher --- prdoc/pr_5592.prdoc | 26 +++++++ substrate/client/consensus/babe/src/lib.rs | 6 +- substrate/client/db/src/lib.rs | 73 ++++++++++++------- substrate/client/db/src/utils.rs | 47 ++++++++++-- .../network/sync/src/strategy/chain_sync.rs | 4 +- substrate/client/service/src/client/client.rs | 5 +- .../primitives/blockchain/src/backend.rs | 40 ++++++++-- 7 files changed, 153 insertions(+), 48 deletions(-) create mode 100644 prdoc/pr_5592.prdoc diff --git a/prdoc/pr_5592.prdoc b/prdoc/pr_5592.prdoc new file mode 100644 index 000000000000..9d51917db7b1 --- /dev/null +++ b/prdoc/pr_5592.prdoc @@ -0,0 +1,26 @@ +title: Introduce `BlockGap` + +doc: + - audience: Node Dev + description: | + This is the first step towards https://github.com/paritytech/polkadot-sdk/issues/5406, + refactoring the representation of block gap. This refactor converts the existing + `(NumberFor, NumberFor)` into a dedicated `BlockGap>` + struct. This change is purely structural and does not alter existing logic, but lays + the groundwork for the follow-up PR. The compatibility concern in the database caused + by the new structure transition is addressed as well. + + The `BlockGap` refactoring results in breaking changes in the `Info` structure returned + in `client.info()`. + +crates: + - name: sc-consensus-babe + bump: none + - name: sc-client-db + bump: none + - name: sc-network-sync + bump: none + - name: sc-service + bump: none + - name: sp-blockchain + bump: major diff --git a/substrate/client/consensus/babe/src/lib.rs b/substrate/client/consensus/babe/src/lib.rs index 9770b16871e1..4cf66302ec85 100644 --- a/substrate/client/consensus/babe/src/lib.rs +++ b/substrate/client/consensus/babe/src/lib.rs @@ -1146,7 +1146,9 @@ where let info = self.client.info(); let number = *block.header.number(); - if info.block_gap.map_or(false, |(s, e)| s <= number && number <= e) || block.with_state() { + if info.block_gap.map_or(false, |gap| gap.start <= number && number <= gap.end) || + block.with_state() + { // Verification for imported blocks is skipped in two cases: // 1. When importing blocks below the last finalized block during network initial // synchronization. @@ -1420,7 +1422,7 @@ where // Skip babe logic if block already in chain or importing blocks during initial sync, // otherwise the check for epoch changes will error because trying to re-import an // epoch change or because of missing epoch data in the tree, respectively. - if info.block_gap.map_or(false, |(s, e)| s <= number && number <= e) || + if info.block_gap.map_or(false, |gap| gap.start <= number && number <= gap.end) || block_status == BlockStatus::InChain { // When re-importing existing block strip away intermediates. diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs index eadb26254a18..4559a01e57e3 100644 --- a/substrate/client/db/src/lib.rs +++ b/substrate/client/db/src/lib.rs @@ -61,6 +61,7 @@ use codec::{Decode, Encode}; use hash_db::Prefix; use sc_client_api::{ backend::NewBlockState, + blockchain::{BlockGap, BlockGapType}, leaves::{FinalizationOutcome, LeafSet}, utils::is_descendent_of, IoInfo, MemoryInfo, MemorySize, UsageInfo, @@ -91,6 +92,7 @@ use sp_state_machine::{ StorageValue, UsageInfo as StateUsageInfo, }; use sp_trie::{cache::SharedTrieCache, prefixed_key, MemoryDB, MerkleValue, PrefixedMemoryDB}; +use utils::BLOCK_GAP_CURRENT_VERSION; // Re-export the Database trait so that one can pass an implementation of it. pub use sc_state_db::PruningMode; @@ -522,7 +524,7 @@ impl BlockchainDb { } } - fn update_block_gap(&self, gap: Option<(NumberFor, NumberFor)>) { + fn update_block_gap(&self, gap: Option>>) { let mut meta = self.meta.write(); meta.block_gap = gap; } @@ -1671,35 +1673,56 @@ impl Backend { ); } - if let Some((mut start, end)) = block_gap { - if number == start { - start += One::one(); - utils::insert_number_to_key_mapping( - &mut transaction, - columns::KEY_LOOKUP, - number, - hash, - )?; - if start > end { - transaction.remove(columns::META, meta_keys::BLOCK_GAP); - block_gap = None; - debug!(target: "db", "Removed block gap."); - } else { - block_gap = Some((start, end)); - debug!(target: "db", "Update block gap. {block_gap:?}"); - transaction.set( - columns::META, - meta_keys::BLOCK_GAP, - &(start, end).encode(), - ); - } - block_gap_updated = true; + if let Some(mut gap) = block_gap { + match gap.gap_type { + BlockGapType::MissingHeaderAndBody => + if number == gap.start { + gap.start += One::one(); + utils::insert_number_to_key_mapping( + &mut transaction, + columns::KEY_LOOKUP, + number, + hash, + )?; + if gap.start > gap.end { + transaction.remove(columns::META, meta_keys::BLOCK_GAP); + transaction.remove(columns::META, meta_keys::BLOCK_GAP_VERSION); + block_gap = None; + debug!(target: "db", "Removed block gap."); + } else { + block_gap = Some(gap); + debug!(target: "db", "Update block gap. {block_gap:?}"); + transaction.set( + columns::META, + meta_keys::BLOCK_GAP, + &gap.encode(), + ); + transaction.set( + columns::META, + meta_keys::BLOCK_GAP_VERSION, + &BLOCK_GAP_CURRENT_VERSION.encode(), + ); + } + block_gap_updated = true; + }, + BlockGapType::MissingBody => { + unreachable!("Unsupported block gap. TODO: https://github.com/paritytech/polkadot-sdk/issues/5406") + }, } } else if number > best_num + One::one() && number > One::one() && self.blockchain.header(parent_hash)?.is_none() { - let gap = (best_num + One::one(), number - One::one()); + let gap = BlockGap { + start: best_num + One::one(), + end: number - One::one(), + gap_type: BlockGapType::MissingHeaderAndBody, + }; transaction.set(columns::META, meta_keys::BLOCK_GAP, &gap.encode()); + transaction.set( + columns::META, + meta_keys::BLOCK_GAP_VERSION, + &BLOCK_GAP_CURRENT_VERSION.encode(), + ); block_gap = Some(gap); block_gap_updated = true; debug!(target: "db", "Detected block gap {block_gap:?}"); diff --git a/substrate/client/db/src/utils.rs b/substrate/client/db/src/utils.rs index b532e0d46662..0b591c967e60 100644 --- a/substrate/client/db/src/utils.rs +++ b/substrate/client/db/src/utils.rs @@ -25,10 +25,14 @@ use log::{debug, info}; use crate::{Database, DatabaseSource, DbHash}; use codec::Decode; +use sc_client_api::blockchain::{BlockGap, BlockGapType}; use sp_database::Transaction; use sp_runtime::{ generic::BlockId, - traits::{Block as BlockT, Header as HeaderT, UniqueSaturatedFrom, UniqueSaturatedInto, Zero}, + traits::{ + Block as BlockT, Header as HeaderT, NumberFor, UniqueSaturatedFrom, UniqueSaturatedInto, + Zero, + }, }; use sp_trie::DBValue; @@ -38,6 +42,9 @@ pub const NUM_COLUMNS: u32 = 13; /// Meta column. The set of keys in the column is shared by full && light storages. pub const COLUMN_META: u32 = 0; +/// Current block gap version. +pub const BLOCK_GAP_CURRENT_VERSION: u32 = 1; + /// Keys of entries in COLUMN_META. pub mod meta_keys { /// Type of storage (full or light). @@ -50,6 +57,8 @@ pub mod meta_keys { pub const FINALIZED_STATE: &[u8; 6] = b"fstate"; /// Block gap. pub const BLOCK_GAP: &[u8; 3] = b"gap"; + /// Block gap version. + pub const BLOCK_GAP_VERSION: &[u8; 7] = b"gap_ver"; /// Genesis block hash. pub const GENESIS_HASH: &[u8; 3] = b"gen"; /// Leaves prefix list key. @@ -73,8 +82,8 @@ pub struct Meta { pub genesis_hash: H, /// Finalized state, if any pub finalized_state: Option<(H, N)>, - /// Block gap, start and end inclusive, if any. - pub block_gap: Option<(N, N)>, + /// Block gap, if any. + pub block_gap: Option>, } /// A block lookup key: used for canonical lookup from block number to hash @@ -197,7 +206,7 @@ fn open_database_at( open_kvdb_rocksdb::(path, db_type, create, *cache_size)?, DatabaseSource::Custom { db, require_create_flag } => { if *require_create_flag && !create { - return Err(OpenDbError::DoesNotExist) + return Err(OpenDbError::DoesNotExist); } db.clone() }, @@ -364,7 +373,7 @@ pub fn check_database_type( return Err(OpenDbError::UnexpectedDbType { expected: db_type, found: stored_type.to_owned(), - }) + }); }, None => { let mut transaction = Transaction::new(); @@ -515,9 +524,31 @@ where } else { None }; - let block_gap = db - .get(COLUMN_META, meta_keys::BLOCK_GAP) - .and_then(|d| Decode::decode(&mut d.as_slice()).ok()); + let block_gap = match db + .get(COLUMN_META, meta_keys::BLOCK_GAP_VERSION) + .and_then(|d| u32::decode(&mut d.as_slice()).ok()) + { + None => { + let old_block_gap: Option<(NumberFor, NumberFor)> = db + .get(COLUMN_META, meta_keys::BLOCK_GAP) + .and_then(|d| Decode::decode(&mut d.as_slice()).ok()); + + old_block_gap.map(|(start, end)| BlockGap { + start, + end, + gap_type: BlockGapType::MissingHeaderAndBody, + }) + }, + Some(version) => match version { + BLOCK_GAP_CURRENT_VERSION => db + .get(COLUMN_META, meta_keys::BLOCK_GAP) + .and_then(|d| Decode::decode(&mut d.as_slice()).ok()), + v => + return Err(sp_blockchain::Error::Backend(format!( + "Unsupported block gap DB version: {v}" + ))), + }, + }; debug!(target: "db", "block_gap={:?}", block_gap); Ok(Meta { diff --git a/substrate/client/network/sync/src/strategy/chain_sync.rs b/substrate/client/network/sync/src/strategy/chain_sync.rs index 21e474048625..f29ed1b083e8 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync.rs @@ -44,7 +44,7 @@ use crate::{ use codec::Encode; use log::{debug, error, info, trace, warn}; use prometheus_endpoint::{register, Gauge, PrometheusError, Registry, U64}; -use sc_client_api::{BlockBackend, ProofProvider}; +use sc_client_api::{blockchain::BlockGap, BlockBackend, ProofProvider}; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; use sc_network_common::sync::message::{ BlockAnnounce, BlockAttributes, BlockData, BlockRequest, BlockResponse, Direction, FromBlock, @@ -1381,7 +1381,7 @@ where } } - if let Some((start, end)) = info.block_gap { + if let Some(BlockGap { start, end, .. }) = info.block_gap { debug!(target: LOG_TARGET, "Starting gap sync #{start} - #{end}"); self.gap_sync = Some(GapSync { best_queued_number: start - One::one(), diff --git a/substrate/client/service/src/client/client.rs b/substrate/client/service/src/client/client.rs index 22defd7c5514..8b699c7faffd 100644 --- a/substrate/client/service/src/client/client.rs +++ b/substrate/client/service/src/client/client.rs @@ -604,9 +604,8 @@ where } let info = self.backend.blockchain().info(); - let gap_block = info - .block_gap - .map_or(false, |(start, _)| *import_headers.post().number() == start); + let gap_block = + info.block_gap.map_or(false, |gap| *import_headers.post().number() == gap.start); // the block is lower than our last finalized block so it must revert // finality, refusing import. diff --git a/substrate/primitives/blockchain/src/backend.rs b/substrate/primitives/blockchain/src/backend.rs index fd0c5795cbfd..d7386a71a0d1 100644 --- a/substrate/primitives/blockchain/src/backend.rs +++ b/substrate/primitives/blockchain/src/backend.rs @@ -17,6 +17,7 @@ //! Substrate blockchain trait +use codec::{Decode, Encode}; use parking_lot::RwLock; use sp_runtime::{ generic::BlockId, @@ -109,7 +110,7 @@ pub trait ForkBackend: for block in tree_route.retracted() { expanded_forks.insert(block.hash); } - continue + continue; }, Err(_) => { // There are cases when blocks are missing (e.g. warp-sync). @@ -196,7 +197,7 @@ pub trait Backend: let info = self.info(); if info.finalized_number > *base_header.number() { // `base_header` is on a dead fork. - return Ok(None) + return Ok(None); } self.leaves()? }; @@ -207,7 +208,7 @@ pub trait Backend: // go backwards through the chain (via parent links) loop { if current_hash == base_hash { - return Ok(Some(leaf_hash)) + return Ok(Some(leaf_hash)); } let current_header = self @@ -216,7 +217,7 @@ pub trait Backend: // stop search in this chain once we go below the target's block number if current_header.number() < base_header.number() { - break + break; } current_hash = *current_header.parent_hash(); @@ -266,7 +267,7 @@ pub trait Backend: // If we have only one leaf there are no forks, and we can return early. if finalized_block_number == Zero::zero() || leaves.len() == 1 { - return Ok(DisplacedLeavesAfterFinalization::default()) + return Ok(DisplacedLeavesAfterFinalization::default()); } // Store hashes of finalized blocks for quick checking later, the last block is the @@ -332,7 +333,7 @@ pub trait Backend: elapsed = ?now.elapsed(), "Added genesis leaf to displaced leaves." ); - continue + continue; } debug!( @@ -539,6 +540,29 @@ impl DisplacedLeavesAfterFinalization { } } +/// Represents the type of block gaps that may result from either warp sync or fast sync. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)] +pub enum BlockGapType { + /// Both the header and body are missing, as a result of warp sync. + MissingHeaderAndBody, + /// The block body is missing, as a result of fast sync. + MissingBody, +} + +/// Represents the block gap resulted by warp sync or fast sync. +/// +/// A block gap is a range of blocks where either the bodies, or both headers and bodies are +/// missing. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)] +pub struct BlockGap { + /// The starting block number of the gap (inclusive). + pub start: N, + /// The ending block number of the gap (inclusive). + pub end: N, + /// The type of gap. + pub gap_type: BlockGapType, +} + /// Blockchain info #[derive(Debug, Eq, PartialEq, Clone)] pub struct Info { @@ -556,8 +580,8 @@ pub struct Info { pub finalized_state: Option<(Block::Hash, <::Header as HeaderT>::Number)>, /// Number of concurrent leave forks. pub number_leaves: usize, - /// Missing blocks after warp sync. (start, end). - pub block_gap: Option<(NumberFor, NumberFor)>, + /// Missing blocks after warp sync or fast sync. + pub block_gap: Option>>, } /// Block status. From 76df1ae460fb2f9910051e0dac2211ab8d156ced Mon Sep 17 00:00:00 2001 From: Egor_P Date: Fri, 6 Sep 2024 10:29:26 +0200 Subject: [PATCH 301/382] [CI/Release] Pipeline to create a stable release branch (#5598) This PR contains a pipeline which is going to branch off the new stable release branch (e.g. `stab2412`, `stable2503`) and bump `polkadot` `NODE_VERSION`, `spec_version` of the runtimes and reorganisation of the `prdocs` related to the new stable release. This is a first step in the automated `polkadot-sdk` release flow as part of the task: https://github.com/paritytech/polkadot-sdk/issues/3291 The pipeline is not supposed to be triggered in the main` polkadot-sdk` repo, but in the fork in the [`paritytech-release`](https://github.com/paritytech-release/polkadot-sdk) org, where the whole release flow is going to land. Closes: https://github.com/paritytech/release-engineering/issues/222 --- .github/scripts/common/lib.sh | 12 +- .github/scripts/release/release_lib.sh | 118 ++++++++++++++++++ .../workflows/release-branchoff-stable.yml | 105 ++++++++++++++++ 3 files changed, 229 insertions(+), 6 deletions(-) create mode 100644 .github/scripts/release/release_lib.sh create mode 100644 .github/workflows/release-branchoff-stable.yml diff --git a/.github/scripts/common/lib.sh b/.github/scripts/common/lib.sh index bfb3120ad9bb..5361db398ae7 100755 --- a/.github/scripts/common/lib.sh +++ b/.github/scripts/common/lib.sh @@ -299,23 +299,23 @@ function check_sha256() { } # Import GPG keys of the release team members -# This is done in parallel as it can take a while sometimes function import_gpg_keys() { - GPG_KEYSERVER=${GPG_KEYSERVER:-"keyserver.ubuntu.com"} + GPG_KEYSERVER=${GPG_KEYSERVER:-"hkps://keyserver.ubuntu.com"} SEC="9D4B2B6EB8F97156D19669A9FF0812D491B96798" EGOR="E6FC4D4782EB0FA64A4903CCDB7D3555DD3932D3" MORGAN="2E92A9D8B15D7891363D1AE8AF9E6C43F7F8C4CF" + PARITY_RELEASES="90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE" - echo "Importing GPG keys from $GPG_KEYSERVER in parallel" - for key in $SEC $EGOR $MORGAN; do + echo "Importing GPG keys from $GPG_KEYSERVER" + for key in $SEC $EGOR $MORGAN $PARITY_RELEASES; do ( echo "Importing GPG key $key" gpg --no-tty --quiet --keyserver $GPG_KEYSERVER --recv-keys $key echo -e "5\ny\n" | gpg --no-tty --command-fd 0 --expert --edit-key $key trust; - ) & + ) done wait - gpg -k $SEC + gpg -k } # Check the GPG signature for a given binary diff --git a/.github/scripts/release/release_lib.sh b/.github/scripts/release/release_lib.sh new file mode 100644 index 000000000000..81a3c14edec8 --- /dev/null +++ b/.github/scripts/release/release_lib.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash + +# Set the new version by replacing the value of the constant given as patetrn +# in the file. +# +# input: pattern, version, file +#output: none +set_version() { + pattern=$1 + version=$2 + file=$3 + + sed -i "s/$pattern/\1\"${version}\"/g" $file + return 0 +} + +# Commit changes to git with specific message. +# "|| true" does not let script to fail with exit code 1, +# in case there is nothing to commit. +# +# input: MESSAGE (any message which should be used for the commit) +# output: none +commit_with_message() { + MESSAGE=$1 + git commit -a -m "$MESSAGE" || true +} + +# Retun list of the runtimes filterd +# input: none +# output: list of filtered runtimes +get_filtered_runtimes_list() { + grep_filters=("runtime.*" "test|template|starters|substrate") + + git grep spec_version: | grep .rs: | grep -e "${grep_filters[0]}" | grep "lib.rs" | grep -vE "${grep_filters[1]}" | cut -d: -f1 +} + +# Sets provided spec version +# input: version +set_spec_versions() { + NEW_VERSION=$1 + runtimes_list=(${@:2}) + + printf "Setting spec_version to $NEW_VERSION\n" + + for f in ${runtimes_list[@]}; do + printf " processing $f" + sed -ri "s/spec_version: [0-9]+_[0-9]+_[0-9]+,/spec_version: $NEW_VERSION,/" $f + done + + commit_with_message "Bump spec_version to $NEW_VERSION" + + git_show_log 'spec_version' +} + +# Displays formated results of the git log command +# for the given pattern which needs to be found in logs +# input: pattern, count (optional, default is 10) +git_show_log() { + PATTERN="$1" + COUNT=${2:-10} + git log --pretty=format:"%h %ad | %s%d [%an]" --graph --date=iso-strict | \ + head -n $COUNT | grep -iE "$PATTERN" --color=always -z +} + +# Get a spec_version number from the crate version +# +# ## inputs +# - v1.12.0 or 1.12.0 +# +# ## output: +# 1_012_000 or 1_012_001 if SUFFIX is set +function get_spec_version() { + INPUT=$1 + SUFFIX=${SUFFIX:-000} #this variable makes it possible to set a specific ruuntime version like 93826 it can be intialised as sestem variable + [[ $INPUT =~ .*([0-9]+\.[0-9]+\.[0-9]{1,2}).* ]] + VERSION="${BASH_REMATCH[1]}" + MATCH="${BASH_REMATCH[0]}" + if [ -z $MATCH ]; then + return 1 + else + SPEC_VERSION="$(sed -e "s/\./_0/g" -e "s/_[^_]*\$/_$SUFFIX/" <<< $VERSION)" + echo "$SPEC_VERSION" + return 0 + fi +} + +# Reorganize the prdoc files for the release +# +# input: VERSION (e.g. v1.0.0) +# output: none +reorder_prdocs() { + VERSION="$1" + + printf "[+] ℹ️ Reordering prdocs:" + + VERSION=$(sed -E 's/^v([0-9]+\.[0-9]+\.[0-9]+).*$/\1/' <<< "$VERSION") #getting reed of the 'v' prefix + mkdir -p "prdoc/$VERSION" + mv prdoc/pr_*.prdoc prdoc/$VERSION + git add -A + commit_with_message "Reordering prdocs for the release $VERSION" +} + +# Bump the binary version of the polkadot-parachain binary with the +# new bumped version and commit changes. +# +# input: version e.g. 1.16.0 +set_polkadot_parachain_binary_version() { + bumped_version="$1" + cargo_toml_file="$2" + + set_version "\(^version = \)\".*\"" $bumped_version $cargo_toml_file + + cargo update --workspace --offline # we need this to update Cargo.loc with the new versions as well + + MESSAGE="Bump versions in: ${cargo_toml_file}" + commit_with_message "$MESSAGE" + git_show_log "$MESSAGE" +} diff --git a/.github/workflows/release-branchoff-stable.yml b/.github/workflows/release-branchoff-stable.yml new file mode 100644 index 000000000000..c236a66a9fae --- /dev/null +++ b/.github/workflows/release-branchoff-stable.yml @@ -0,0 +1,105 @@ +name: Release - Branch off stable branch + +on: + workflow_dispatch: + inputs: + stable_version: + description: New stable version in the format stableYYMM + required: true + type: string + + node_version: + description: Version of the polkadot node in the format vX.XX.X (e.g. 1.15.0) + required: true + +jobs: + # TODO: Activate this job when the pipeline is moved to the fork in the `paritytech-release` org + # check-workflow-can-run: + # uses: paritytech-release/sync-workflows/.github/workflows/check-syncronization.yml@latest + + + prepare-tooling: + runs-on: ubuntu-latest + outputs: + node_version: ${{ steps.validate_inputs.outputs.node_version }} + stable_version: ${{ steps.validate_inputs.outputs.stable_version }} + + steps: + - name: Checkout sources + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Validate inputs + id: validate_inputs + run: | + . ./.github/scripts/common/lib.sh + + node_version=$(filter_version_from_input "${{ inputs.node_version }}") + echo "node_version=${node_version}" >> $GITHUB_OUTPUT + + stable_version=$(validate_stable_tag ${{ inputs.stable_version }}) + echo "stable_version=${stable_version}" >> $GITHUB_OUTPUT + + create-stable-branch: + # needs: [check-workflow-can-run, prepare-tooling] + needs: [prepare-tooling] + # if: needs. check-workflow-can-run.outputs.checks_passed == 'true' + runs-on: ubuntu-latest + + env: + PGP_KMS_KEY: ${{ secrets.PGP_KMS_KEY }} + PGP_KMS_HASH: ${{ secrets.PGP_KMS_HASH }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} + STABLE_BRANCH_NAME: ${{ needs.prepare-tooling.outputs.stable_version }} + + steps: + - name: Install pgpkkms + run: | + # Install pgpkms that is used to sign commits + pip install git+https://github.com/paritytech-release/pgpkms.git@5a8f82fbb607ea102d8c178e761659de54c7af69 + + - name: Checkout sources + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + ref: master + + - name: Import gpg keys + run: | + . ./.github/scripts/common/lib.sh + + import_gpg_keys + + + - name: Config git + run: | + git config --global commit.gpgsign true + git config --global gpg.program /home/runner/.local/bin/pgpkms-git + git config --global user.name "ParityReleases" + git config --global user.email "release-team@parity.io" + git config --global user.signingKey "90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE" + + - name: Create stable branch + run: | + git checkout -b "$STABLE_BRANCH_NAME" + git show-ref "$STABLE_BRANCH_NAME" + + - name: Bump versions, reorder prdocs and push stable branch + run: | + . ./.github/scripts/release/release_lib.sh + + NODE_VERSION="${{ needs.prepare-tooling.outputs.node_version }}" + set_version "\(NODE_VERSION[^=]*= \)\".*\"" $NODE_VERSION "polkadot/node/primitives/src/lib.rs" + commit_with_message "Bump node version to $NODE_VERSION in polkadot-cli" + + SPEC_VERSION=$(get_spec_version $NODE_VERSION) + runtimes_list=$(get_filtered_runtimes_list) + set_spec_versions $SPEC_VERSION "${runtimes_list[@]}" + + # TODO: clarify what to do with the polkadot-parachain binary + # Set new version for polkadot-parachain binary to match the polkadot node binary + # set_polkadot_parachain_binary_version $NODE_VERSION "cumulus/polkadot-parachain/Cargo.toml" + + reorder_prdocs $NODE_VERSION + + git push origin "$STABLE_BRANCH_NAME" From 986e7ae4f29f804ee4dc89aaf52984d6eda5bd0b Mon Sep 17 00:00:00 2001 From: Radha <86818441+DrW3RK@users.noreply.github.com> Date: Fri, 6 Sep 2024 10:30:49 +0200 Subject: [PATCH 302/382] Update Templates Readme - Github Repo links (#5381) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When someone downloads the Polkadot SDK repo and navigates to the templates folder, the Readme instructions do not work. There is a getting started script of the Polkadot SDK readme which can be overlooked (and also it covers only minimal template and not the parachain/solochain templates). The instructions of the Readme files are updated such that they work for anyone on https://github.com/paritytech/polkadot-sdk https://github.com/paritytech/polkadot-sdk-minimal-template https://github.com/paritytech/polkadot-sdk-parachain-template https://github.com/paritytech/polkadot-sdk-solochain-template --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Bastian Köcher --- templates/minimal/README.md | 8 ++++++++ templates/parachain/README.md | 8 ++++++++ templates/solochain/README.md | 10 +++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/templates/minimal/README.md b/templates/minimal/README.md index 180c229e744e..fe1317a033c7 100644 --- a/templates/minimal/README.md +++ b/templates/minimal/README.md @@ -37,6 +37,14 @@ A Polkadot SDK based project such as this one consists of: * 🛠️ Depending on your operating system and Rust version, there might be additional packages required to compile this template - please take note of the Rust compiler output. +Fetch minimal template code: + +```sh +git clone https://github.com/paritytech/polkadot-sdk-minimal-template.git minimal-template + +cd minimal-template +``` + ### Build 🔨 Use the following command to build the node without launching it: diff --git a/templates/parachain/README.md b/templates/parachain/README.md index b912d8e005c7..3de85cbeb4dc 100644 --- a/templates/parachain/README.md +++ b/templates/parachain/README.md @@ -39,6 +39,14 @@ A Polkadot SDK based project such as this one consists of: * 🛠️ Depending on your operating system and Rust version, there might be additional packages required to compile this template - please take note of the Rust compiler output. +Fetch parachain template code: + +```sh +git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git parachain-template + +cd parachain-template +``` + ### Build 🔨 Use the following command to build the node without launching it: diff --git a/templates/solochain/README.md b/templates/solochain/README.md index 6a5a7853f9c0..c4ce5c7f3fbb 100644 --- a/templates/solochain/README.md +++ b/templates/solochain/README.md @@ -23,9 +23,17 @@ packages required to compile this template. Check the the most common dependencies. Alternatively, you can use one of the [alternative installation](#alternatives-installations) options. +Fetch solochain template code: + +```sh +git clone https://github.com/paritytech/polkadot-sdk-solochain-template.git solochain-template + +cd solochain-template +``` + ### Build -Use the following command to build the node without launching it: +🔨 Use the following command to build the node without launching it: ```sh cargo build --release From 5040b3c2186308a06bad408643a5e475df4cfeeb Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Fri, 6 Sep 2024 13:29:16 +0200 Subject: [PATCH 303/382] Fix PVF precompilation for Kusama (#5606) ![image](https://github.com/user-attachments/assets/2deaee85-67c3-4119-b0c0-d2e7f818b4ea) Because on Kusama validators.len() < discovery_keys.len() we can tweak the PVF precompilation to allow prepare PVFs when the node is an authority but not a validator. --- .../node/core/candidate-validation/src/lib.rs | 11 +++++----- .../core/candidate-validation/src/tests.rs | 21 +++++++++---------- prdoc/pr_5606.prdoc | 13 ++++++++++++ 3 files changed, 29 insertions(+), 16 deletions(-) create mode 100644 prdoc/pr_5606.prdoc diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index 103d29e8d269..a9732e934414 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -47,7 +47,7 @@ use polkadot_primitives::{ }, AuthorityDiscoveryId, CandidateCommitments, CandidateDescriptor, CandidateEvent, CandidateReceipt, ExecutorParams, Hash, OccupiedCoreAssumption, PersistedValidationData, - PvfExecKind, PvfPrepKind, SessionIndex, ValidationCode, ValidationCodeHash, + PvfExecKind, PvfPrepKind, SessionIndex, ValidationCode, ValidationCodeHash, ValidatorId, }; use sp_application_crypto::{AppCrypto, ByteArray}; use sp_keystore::KeystorePtr; @@ -427,14 +427,15 @@ where .iter() .any(|v| keystore.has_keys(&[(v.to_raw_vec(), AuthorityDiscoveryId::ID)])); - let is_present_authority = session_info - .discovery_keys + // We could've checked discovery_keys but on Kusama validators.len() < discovery_keys.len(). + let is_present_validator = session_info + .validators .iter() - .any(|v| keystore.has_keys(&[(v.to_raw_vec(), AuthorityDiscoveryId::ID)])); + .any(|v| keystore.has_keys(&[(v.to_raw_vec(), ValidatorId::ID)])); // There is still a chance to be a previous session authority, but this extra work does not // affect the finalization. - is_past_present_or_future_authority && !is_present_authority + is_past_present_or_future_authority && !is_present_validator } // Sends PVF with unknown code hashes to the validation host returning the list of code hashes sent. diff --git a/polkadot/node/core/candidate-validation/src/tests.rs b/polkadot/node/core/candidate-validation/src/tests.rs index 55282fdf4ee1..0dcd84bab6cf 100644 --- a/polkadot/node/core/candidate-validation/src/tests.rs +++ b/polkadot/node/core/candidate-validation/src/tests.rs @@ -25,13 +25,12 @@ use polkadot_node_subsystem::messages::AllMessages; use polkadot_node_subsystem_util::reexports::SubsystemContext; use polkadot_overseer::ActivatedLeaf; use polkadot_primitives::{ - CoreIndex, GroupIndex, HeadData, Id as ParaId, IndexedVec, SessionInfo, UpwardMessage, - ValidatorId, ValidatorIndex, + CoreIndex, GroupIndex, HeadData, Id as ParaId, SessionInfo, UpwardMessage, ValidatorId, }; use polkadot_primitives_test_helpers::{ dummy_collator, dummy_collator_signature, dummy_hash, make_valid_candidate_descriptor, }; -use sp_core::testing::TaskExecutor; +use sp_core::{sr25519::Public, testing::TaskExecutor}; use sp_keyring::Sr25519Keyring; use sp_keystore::{testing::MemoryKeystore, Keystore}; @@ -1194,10 +1193,10 @@ fn dummy_candidate_backed( ) } -fn dummy_session_info(discovery_keys: Vec) -> SessionInfo { +fn dummy_session_info(keys: Vec) -> SessionInfo { SessionInfo { - validators: IndexedVec::::from(vec![]), - discovery_keys, + validators: keys.iter().cloned().map(Into::into).collect(), + discovery_keys: keys.iter().cloned().map(Into::into).collect(), assignment_keys: vec![], validator_groups: Default::default(), n_cores: 4u32, @@ -1246,7 +1245,7 @@ fn maybe_prepare_validation_golden_path() { ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 1); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public()])))); } ); @@ -1364,7 +1363,7 @@ fn maybe_prepare_validation_resets_state_on_a_new_session() { ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 2); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public()])))); } ); }; @@ -1510,7 +1509,7 @@ fn maybe_prepare_validation_does_not_prepare_pvfs_if_not_a_validator_in_the_next ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 1); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public()])))); } ); }; @@ -1557,7 +1556,7 @@ fn maybe_prepare_validation_does_not_prepare_pvfs_if_a_validator_in_the_current_ ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 1); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Alice.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Alice.public()])))); } ); }; @@ -1604,7 +1603,7 @@ fn maybe_prepare_validation_prepares_a_limited_number_of_pvfs() { ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 1); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public()])))); } ); diff --git a/prdoc/pr_5606.prdoc b/prdoc/pr_5606.prdoc new file mode 100644 index 000000000000..46883c5722cd --- /dev/null +++ b/prdoc/pr_5606.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix PVF precompilation for Kusama + +doc: + - audience: Node Operator + description: | + Tweaks the PVF precompilation on Kusama to allow prepare PVFs when the node is an authority but not a validator. + +crates: + - name: polkadot-node-core-candidate-validation + bump: patch From 5fdc0369758989a7d05c95de76183d63240fdaec Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Fri, 6 Sep 2024 14:28:44 +0200 Subject: [PATCH 304/382] do not allow changes to token_id locations once set --- bridges/snowbridge/pallets/system/src/lib.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 16e9a6bb6361..092eb1addb47 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -733,17 +733,18 @@ pub mod pallet { pays_fee: PaysFee, ) -> Result<(), DispatchError> { let bridge_location = Location::new(2, [GlobalConsensus(T::EthereumNetwork::get())]); - let mut location = location.clone(); - location - .reanchor(&bridge_location, &T::UniversalLocation::get()) + let location = location + .clone() + .reanchored(&bridge_location, &T::UniversalLocation::get()) .map_err(|_| Error::::LocationConversionFailed)?; - // Record the token id or fail if it has already been created let token_id = TokenIdOf::convert_location(&location) .ok_or(Error::::LocationConversionFailed)?; - ForeignToNativeId::::insert(token_id, location.clone()); - NativeToForeignId::::insert(location.clone(), token_id); + if (!ForeignToNativeId::::contains_key(token_id)) { + ForeignToNativeId::::insert(token_id, location.clone()); + NativeToForeignId::::insert(location.clone(), token_id); + } let command = Command::RegisterForeignToken { token_id, From b395eafc4aa53042606fbda6fb7a7ba4e287fbb8 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Fri, 6 Sep 2024 14:31:42 +0200 Subject: [PATCH 305/382] fix syntax --- bridges/snowbridge/pallets/system/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 092eb1addb47..d935bb78579e 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -741,9 +741,9 @@ pub mod pallet { let token_id = TokenIdOf::convert_location(&location) .ok_or(Error::::LocationConversionFailed)?; - if (!ForeignToNativeId::::contains_key(token_id)) { - ForeignToNativeId::::insert(token_id, location.clone()); + if !ForeignToNativeId::::contains_key(token_id) { NativeToForeignId::::insert(location.clone(), token_id); + ForeignToNativeId::::insert(token_id, location.clone()); } let command = Command::RegisterForeignToken { From b2089d88bd6ef40ca17ddfa097b05b257cdcdf13 Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Fri, 6 Sep 2024 15:56:29 +0200 Subject: [PATCH 306/382] [ci] Fix final job for required workflows (#5619) Currently if a required job fails the final jobs is skipped which breaks the logic of required jobs. PR fixes it. Closes https://github.com/paritytech/ci_cd/issues/1033 --- .github/workflows/build-misc.yml | 24 ++++++----- .../workflows/check-cargo-check-runtimes.yml | 14 ++++-- .github/workflows/check-runtime-migration.yml | 13 +++++- .github/workflows/checks.yml | 13 +++++- .github/workflows/tests-linux-stable.yml | 11 ++++- .github/workflows/tests-misc.yml | 43 +++++++++++-------- 6 files changed, 83 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build-misc.yml b/.github/workflows/build-misc.yml index c85549b37999..a01384dc002c 100644 --- a/.github/workflows/build-misc.yml +++ b/.github/workflows/build-misc.yml @@ -5,10 +5,9 @@ on: branches: - master pull_request: - types: [ opened, synchronize, reopened, ready_for_review ] + types: [opened, synchronize, reopened, ready_for_review] merge_group: - concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true @@ -41,7 +40,7 @@ jobs: build-runtimes-polkavm: timeout-minutes: 20 - needs: [ set-image ] + needs: [set-image] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -65,7 +64,7 @@ jobs: build-subkey: timeout-minutes: 20 - needs: [ set-image ] + needs: [set-image] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -89,10 +88,15 @@ jobs: runs-on: ubuntu-latest name: All build misc jobs passed # If any new job gets added, be sure to add it to this array - needs: - [ - build-runtimes-polkavm, - build-subkey - ] + needs: [build-runtimes-polkavm, build-subkey] + if: always() && !cancelled() steps: - - run: echo '### Good job! All the build misc tests passed 🚀' >> $GITHUB_STEP_SUMMARY \ No newline at end of file + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed ❌" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed 🚀' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/check-cargo-check-runtimes.yml b/.github/workflows/check-cargo-check-runtimes.yml index ebcf6c5fc9bd..6325033d214c 100644 --- a/.github/workflows/check-cargo-check-runtimes.yml +++ b/.github/workflows/check-cargo-check-runtimes.yml @@ -2,8 +2,7 @@ name: Check Cargo Check Runtimes on: pull_request: - types: [ opened, synchronize, reopened, ready_for_review, labeled ] - + types: [opened, synchronize, reopened, ready_for_review, labeled] # Jobs in this workflow depend on each other, only for limiting peak amount of spawned workers @@ -132,5 +131,14 @@ jobs: - check-runtime-contracts - check-runtime-starters - check-runtime-testing + if: always() && !cancelled() steps: - - run: echo '### Good job! All the tests passed 🚀' >> $GITHUB_STEP_SUMMARY + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed ❌" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed 🚀' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/check-runtime-migration.yml b/.github/workflows/check-runtime-migration.yml index 5fb9dca38d17..0a1dbc4790c8 100644 --- a/.github/workflows/check-runtime-migration.yml +++ b/.github/workflows/check-runtime-migration.yml @@ -46,7 +46,7 @@ jobs: # We need to set this to rather long to allow the snapshot to be created, but the average time # should be much lower. timeout-minutes: 60 - needs: [ set-image ] + needs: [set-image] container: image: ${{ needs.set-image.outputs.IMAGE }} strategy: @@ -162,5 +162,14 @@ jobs: name: All runtime migrations passed # If any new job gets added, be sure to add it to this array needs: [check-runtime-migration] + if: always() && !cancelled() steps: - - run: echo '### Good job! All the checks passed 🚀' >> $GITHUB_STEP_SUMMARY + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed ❌" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed 🚀' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 9aebd83282e3..9de879d83676 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -12,7 +12,7 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true -permissions: { } +permissions: {} jobs: # temporary disabled because currently doesn't work in merge queue @@ -105,5 +105,14 @@ jobs: name: All checks passed # If any new job gets added, be sure to add it to this array needs: [cargo-clippy, check-try-runtime, check-core-crypto-features] + if: always() && !cancelled() steps: - - run: echo '### Good job! All the checks passed 🚀' >> $GITHUB_STEP_SUMMARY + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed ❌" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed 🚀' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/tests-linux-stable.yml b/.github/workflows/tests-linux-stable.yml index 997d7622f0c3..7ed67703395f 100644 --- a/.github/workflows/tests-linux-stable.yml +++ b/.github/workflows/tests-linux-stable.yml @@ -138,5 +138,14 @@ jobs: test-linux-stable-runtime-benchmarks, test-linux-stable, ] + if: always() && !cancelled() steps: - - run: echo '### Good job! All the tests passed 🚀' >> $GITHUB_STEP_SUMMARY + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed ❌" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed 🚀' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/tests-misc.yml b/.github/workflows/tests-misc.yml index 2e78f4a34ede..9aa6bf23727f 100644 --- a/.github/workflows/tests-misc.yml +++ b/.github/workflows/tests-misc.yml @@ -5,7 +5,7 @@ on: branches: - master pull_request: - types: [ opened, synchronize, reopened, ready_for_review ] + types: [opened, synchronize, reopened, ready_for_review] merge_group: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -43,12 +43,12 @@ jobs: echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT else echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT - fi - + fi + # more information about this job can be found here: # https://github.com/paritytech/substrate/pull/3778 test-full-crypto-feature: - needs: [ set-image ] + needs: [set-image] runs-on: ${{ needs.set-image.outputs.RUNNER }} timeout-minutes: 60 container: @@ -72,7 +72,7 @@ jobs: test-frame-examples-compile-to-wasm: timeout-minutes: 20 # into one job - needs: [ set-image, test-full-crypto-feature ] + needs: [set-image, test-full-crypto-feature] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -93,7 +93,7 @@ jobs: test-frame-ui: timeout-minutes: 60 - needs: [ set-image, test-frame-examples-compile-to-wasm ] + needs: [set-image, test-frame-examples-compile-to-wasm] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -121,7 +121,7 @@ jobs: test-deterministic-wasm: timeout-minutes: 20 - needs: [ set-image ] + needs: [set-image] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -143,7 +143,7 @@ jobs: sha256sum -c checksum.sha256 cargo-check-benches-branches: - needs: [ set-image ] + needs: [set-image] if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} timeout-minutes: 60 outputs: @@ -156,7 +156,7 @@ jobs: echo "branch=['${{ github.base_ref }}', '${{ github.head_ref }}']" >> $GITHUB_OUTPUT cargo-check-benches: - needs: [ set-image, cargo-check-benches-branches ] + needs: [set-image, cargo-check-benches-branches] timeout-minutes: 60 strategy: matrix: @@ -191,7 +191,7 @@ jobs: node-bench-regression-guard: timeout-minutes: 20 runs-on: arc-runners-polkadot-sdk - needs: [ set-image, cargo-check-benches ] + needs: [set-image, cargo-check-benches] steps: - name: Checkout uses: actions/checkout@v4.1.7 @@ -227,7 +227,7 @@ jobs: fi test-node-metrics: - needs: [ set-image ] + needs: [set-image] timeout-minutes: 30 runs-on: ${{ needs.set-image.outputs.RUNNER }} container: @@ -260,7 +260,7 @@ jobs: # https://github.com/paritytech/substrate/pull/6916 check-tracing: timeout-minutes: 20 - needs: [ set-image, test-node-metrics ] + needs: [set-image, test-node-metrics] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -275,7 +275,7 @@ jobs: check-metadata-hash: timeout-minutes: 20 - needs: [ set-image, check-tracing ] + needs: [set-image, check-tracing] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -289,7 +289,7 @@ jobs: cargo-hfuzz: timeout-minutes: 20 - needs: [ set-image, check-metadata-hash ] + needs: [set-image, check-metadata-hash] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -329,7 +329,7 @@ jobs: cargo-check-each-crate: timeout-minutes: 140 - needs: [ set-image ] + needs: [set-image] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -338,7 +338,7 @@ jobs: CI_JOB_NAME: cargo-check-each-crate strategy: matrix: - index: [ 1,2,3,4,5,6,7 ] # 7 parallel jobs + index: [1, 2, 3, 4, 5, 6, 7] # 7 parallel jobs steps: - name: Checkout uses: actions/checkout@v4.1.7 @@ -394,5 +394,14 @@ jobs: - cargo-check-each-crate - test-deterministic-wasm # - cargo-hfuzz remove from required for now, as it's flaky + if: always() && !cancelled() steps: - - run: echo '### Good job! All the required tests passed 🚀' >> $GITHUB_STEP_SUMMARY \ No newline at end of file + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed ❌" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed 🚀' >> $GITHUB_STEP_SUMMARY + fi From c0ce942513d27e15f4f084cb4f02bf718502c0ed Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 6 Sep 2024 22:25:17 +0800 Subject: [PATCH 307/382] Use Blake2_* hasher instead --- bridges/snowbridge/pallets/system/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 16e9a6bb6361..f2b8cce1def2 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -269,12 +269,12 @@ pub mod pallet { /// Lookup table for foreign to native token ID #[pallet::storage] pub type ForeignToNativeId = - StorageMap<_, Twox64Concat, TokenId, xcm::v4::Location, OptionQuery>; + StorageMap<_, Blake2_128Concat, TokenId, xcm::v4::Location, OptionQuery>; /// Lookup table for native to foreign token ID #[pallet::storage] pub type NativeToForeignId = - StorageMap<_, Twox64Concat, xcm::v4::Location, TokenId, OptionQuery>; + StorageMap<_, Blake2_128Concat, xcm::v4::Location, TokenId, OptionQuery>; #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] From 365d9928a8e71bf3467f06f89a1786f8d3d349c4 Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Fri, 6 Sep 2024 16:25:48 +0100 Subject: [PATCH 308/382] Update tests-misc.yml (#5615) Fixes https://github.com/paritytech/ci_cd/issues/1032 --------- Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> --- .github/workflows/tests-misc.yml | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/.github/workflows/tests-misc.yml b/.github/workflows/tests-misc.yml index 9aa6bf23727f..8e8f5770e92d 100644 --- a/.github/workflows/tests-misc.yml +++ b/.github/workflows/tests-misc.yml @@ -142,25 +142,13 @@ jobs: # confirm checksum sha256sum -c checksum.sha256 - cargo-check-benches-branches: + cargo-check-benches: needs: [set-image] if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} timeout-minutes: 60 - outputs: - branch: ${{ steps.branch.outputs.branch }} - runs-on: ubuntu-latest - steps: - - name: Branch - id: branch - run: | - echo "branch=['${{ github.base_ref }}', '${{ github.head_ref }}']" >> $GITHUB_OUTPUT - - cargo-check-benches: - needs: [set-image, cargo-check-benches-branches] - timeout-minutes: 60 strategy: matrix: - branch: ${{ fromJSON(needs.cargo-check-benches-branches.outputs.branch) }} + branch: [ master, current ] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -168,7 +156,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - ref: ${{ matrix.branch }} + # if branch is master, use the branch, otherwise set empty string, so it uses the current context + # either PR (including forks) or merge group (main repo) + ref: ${{ matrix.branch == 'master' && matrix.branch || '' }} - name: script run: | @@ -190,6 +180,7 @@ jobs: node-bench-regression-guard: timeout-minutes: 20 + if: always() && !cancelled() runs-on: arc-runners-polkadot-sdk needs: [set-image, cargo-check-benches] steps: @@ -199,13 +190,13 @@ jobs: - name: Download artifact (master run) uses: actions/download-artifact@v4.1.8 with: - name: cargo-check-benches-${{ github.base_ref }}-${{ github.sha }} + name: cargo-check-benches-master-${{ github.sha }} path: ./artifacts/master - name: Download artifact (current run) uses: actions/download-artifact@v4.1.8 with: - name: cargo-check-benches-${{ github.head_ref }}-${{ github.sha }} + name: cargo-check-benches-current-${{ github.sha }} path: ./artifacts/current - name: script From c94da518d0165a222d978925784ddea3b0c2fa19 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 08:49:32 +0800 Subject: [PATCH 309/382] Fix to reanchor in context of Ethereum --- .../primitives/router/src/inbound/tests.rs | 95 ++++++------------- .../src/bridge_to_ethereum_config.rs | 5 +- .../src/bridge_to_ethereum_config.rs | 6 +- 3 files changed, 37 insertions(+), 69 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index 7b4469c1a68c..1c018f3ab0c4 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -40,68 +40,35 @@ fn test_contract_location_with_incorrect_location_fails_convert() { } #[test] -fn test_reanchor_relay_token() { - let asset_id: Location = Location::parent(); - let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); - let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); - let mut reanchored_asset = asset_id.clone(); - assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); - assert_eq!( - reanchored_asset, - Location { parents: 1, interior: [GlobalConsensus(Westend)].into() } - ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); - let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); - let mut reanchored_asset = reanchored_asset.clone(); - assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); - assert_eq!(reanchored_asset, asset_id); -} - -#[test] -fn test_reanchor_pna_from_ah() { - let asset_id_in_ah: Location = - Location { parents: 0, interior: [PalletInstance(50), GeneralIndex(2)].into() }; - let asset_id: Location = Location { - parents: 1, - interior: [Parachain(1000), PalletInstance(50), GeneralIndex(2)].into(), - }; - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); - let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); - let mut reanchored_asset = asset_id.clone(); - assert_ok!(reanchored_asset.reanchor(ðereum, &bh_context)); - assert_eq!( - reanchored_asset, - Location { - parents: 1, - interior: [ - GlobalConsensus(Westend), - Parachain(1000), - PalletInstance(50), - GeneralIndex(2) - ] - .into() - } - ); - let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); - let mut reanchored_asset = reanchored_asset.clone(); - assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); - assert_eq!(reanchored_asset, asset_id_in_ah); -} - -#[test] -fn test_reanchor_pna_from_para() { - let asset_id_in_ah: Location = Location { parents: 1, interior: [Parachain(2000)].into() }; - let asset_id: Location = Location { parents: 1, interior: [Parachain(2000)].into() }; - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); - let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); - let mut reanchored_asset = asset_id.clone(); - assert_ok!(reanchored_asset.reanchor(ðereum, &bh_context)); - assert_eq!( - reanchored_asset, - Location { parents: 1, interior: [GlobalConsensus(Westend), Parachain(2000)].into() } - ); - let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); - let mut reanchored_asset = reanchored_asset.clone(); - assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); - assert_eq!(reanchored_asset, asset_id_in_ah); +fn test_reanchor_all_assets() { + let ethereum_context: InteriorLocation = [GlobalConsensus(Ethereum { chain_id: 1 })].into(); + let ethereum = Location::new(2, ethereum_context.clone()); + let ah_context: InteriorLocation = [GlobalConsensus(Polkadot), Parachain(1000)].into(); + let global_ah = Location::new(1, ah_context.clone()); + let bh_context: InteriorLocation = [GlobalConsensus(Polkadot), Parachain(1002)].into(); + let assets = vec![ + // DOT + Location::new(1, []), + // GLMR (Some Polkadot parachain currency) + Location::new(1, [Parachain(2004)]), + // AH asset + Location::new(0, [PalletInstance(50), GeneralIndex(42)]), + // KSM + Location::new(2, [GlobalConsensus(Kusama)]), + // KAR (Some Kusama parachain currency) + Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), + ]; + for asset in assets.iter() { + // reanchor logic in pallet_xcm on AH + let mut reanchored_asset = asset.clone(); + assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + // reanchor back to original location in context of Ethereum + let mut reanchored_asset_with_ethereum_context = reanchored_asset.clone(); + assert_ok!(reanchored_asset_with_ethereum_context.reanchor(&global_ah, ðereum_context)); + assert_eq!(reanchored_asset_with_ethereum_context, asset.clone()); + // reanchor back to original location in context of BH + let mut reanchored_asset_with_bh_context = reanchored_asset.clone(); + assert_ok!(reanchored_asset_with_bh_context.reanchor(&global_ah, &bh_context)); + assert_eq!(reanchored_asset_with_bh_context, asset.clone()); + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 1e096f19ef80..fde214ed42d6 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -41,7 +41,7 @@ use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, }; -use xcm::prelude::{GlobalConsensus, Location, Parachain}; +use xcm::prelude::{GlobalConsensus, InteriorLocation, Location, Parachain}; /// Exports message to the Ethereum Gateway contract. pub type SnowbridgeExporter = EthereumBlobExporter< @@ -67,6 +67,7 @@ parameter_types! { multiplier: FixedU128::from_rational(1, 1), }; pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(rococo_runtime_constants::system_parachain::ASSET_HUB_ID)]); + pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); } impl snowbridge_pallet_inbound_queue::Config for Runtime { @@ -88,7 +89,7 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { AccountId, Balance, EthereumSystem, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHub, >; type WeightToFee = WeightToFee; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index fee9f9a2f610..4b3f61a42ad4 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -42,7 +42,7 @@ use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, }; -use xcm::prelude::{GlobalConsensus, Location, Parachain}; +use xcm::prelude::{GlobalConsensus, InteriorLocation, Location, Parachain}; pub const SLOTS_PER_EPOCH: u32 = snowbridge_pallet_ethereum_client::config::SLOTS_PER_EPOCH as u32; @@ -70,8 +70,8 @@ parameter_types! { multiplier: FixedU128::from_rational(1, 1), }; pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(westend_runtime_constants::system_parachain::ASSET_HUB_ID)]); + pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); } - impl snowbridge_pallet_inbound_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Verifier = snowbridge_pallet_ethereum_client::Pallet; @@ -91,7 +91,7 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { AccountId, Balance, EthereumSystem, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHub, >; type WeightToFee = WeightToFee; From 176c7af58f73c8e78c004d35e7a30f3a9faa1d5f Mon Sep 17 00:00:00 2001 From: Ron Date: Sat, 7 Sep 2024 09:38:42 +0800 Subject: [PATCH 310/382] Update bridges/snowbridge/pallets/system/src/tests.rs Co-authored-by: Francisco Aguirre --- bridges/snowbridge/pallets/system/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 0745f435ba05..d7493e02acb2 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -633,7 +633,7 @@ fn no_genesis_build_is_uninitialized() { } #[test] -fn register_token_with_root_yeilds_success() { +fn register_token_with_root_yields_success() { new_test_ext(true).execute_with(|| { let origin = RuntimeOrigin::root(); let location = Location::new(1, []); From 0944d8a034404372e990edf6d29b62bf7fe5a772 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 09:46:00 +0800 Subject: [PATCH 311/382] Use Polkadot as RelayNetwork in tests --- bridges/snowbridge/pallets/system/src/mock.rs | 2 +- bridges/snowbridge/pallets/system/src/tests.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index d8c905449d9a..72605ea2283e 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -166,7 +166,7 @@ impl snowbridge_pallet_outbound_queue::Config for Test { parameter_types! { pub const SS58Prefix: u8 = 42; pub const AnyNetwork: Option = None; - pub const RelayNetwork: Option = Some(NetworkId::Kusama); + pub const RelayNetwork: Option = Some(NetworkId::Polkadot); pub const RelayLocation: Location = Location::parent(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(1013)].into(); diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 0745f435ba05..4f24b0eb9856 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -647,8 +647,8 @@ fn register_token_with_root_yeilds_success() { assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); let expected_token_id = - hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); - let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + hex!("4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2").into(); + let expected_location = Location::new(1, [GlobalConsensus(Polkadot)]); System::assert_last_event(RuntimeEvent::EthereumSystem( crate::Event::::RegisterToken { @@ -674,8 +674,8 @@ fn register_token_with_relative_address_reanchors_to_ethereum_and_succeeds() { assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); let expected_token_id = - hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); - let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + hex!("4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2").into(); + let expected_location = Location::new(1, [GlobalConsensus(Polkadot)]); System::assert_last_event(RuntimeEvent::EthereumSystem( crate::Event::::RegisterToken { From 0fa95ac5ac64a2f546e1c88f183781ea8b31c747 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 10:53:21 +0800 Subject: [PATCH 312/382] Refactor relative locations tests --- bridges/snowbridge/pallets/system/src/mock.rs | 1 + .../snowbridge/pallets/system/src/tests.rs | 251 +++++++----------- 2 files changed, 98 insertions(+), 154 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index 72605ea2283e..f70641288250 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -171,6 +171,7 @@ parameter_types! { pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(1013)].into(); pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub EthereumDestination: Location = Location::new(2,[GlobalConsensus(EthereumNetwork::get())]); } pub const DOT: u128 = 10_000_000_000; diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 574dd4c53bb7..3920d1c7e2ad 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -633,118 +633,10 @@ fn no_genesis_build_is_uninitialized() { } #[test] -fn register_token_with_root_yields_success() { - new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::root(); - let location = Location::new(1, []); - let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - - assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); - - let expected_token_id = - hex!("4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2").into(); - let expected_location = Location::new(1, [GlobalConsensus(Polkadot)]); - - System::assert_last_event(RuntimeEvent::EthereumSystem( - crate::Event::::RegisterToken { - location: expected_location.into(), - foreign_token_id: expected_token_id, - }, - )); - }); -} - -#[test] -fn register_token_with_relative_address_reanchors_to_ethereum_and_succeeds() { - new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::root(); - let location = Location::new(1, []); - let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - - assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); - - let expected_token_id = - hex!("4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2").into(); - let expected_location = Location::new(1, [GlobalConsensus(Polkadot)]); - - System::assert_last_event(RuntimeEvent::EthereumSystem( - crate::Event::::RegisterToken { - location: expected_location.into(), - foreign_token_id: expected_token_id, - }, - )); - }); -} - -#[test] -fn register_token_with_complex_location_simplifies_and_succeeds() { - new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::root(); - let location = Location::new(2, [GlobalConsensus(Kusama)]); - let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - - assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); - - let expected_token_id = - hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); - let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); - - System::assert_last_event(RuntimeEvent::EthereumSystem( - crate::Event::::RegisterToken { - location: expected_location.into(), - foreign_token_id: expected_token_id, - }, - )); - }); -} - -#[test] -fn register_token_with_doubled_bridged_polkadot_location_succeeds() { - new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::root(); - let location = Location::new(2, [GlobalConsensus(Rococo)]); - let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - - assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); - - let expected_token_id = - hex!("62e8f33b7fb0e7e2d2276564061a2f3c7bcb612e733b8bf5733ea16cee0ecba6").into(); - let expected_location = Location::new(1, [GlobalConsensus(Rococo)]); - - System::assert_last_event(RuntimeEvent::EthereumSystem( - crate::Event::::RegisterToken { - location: expected_location.into(), - foreign_token_id: expected_token_id, - }, - )); - }); -} - -#[test] -fn register_token_with_ethereum_address_reanchors_to_relative_and_fails() { +fn register_token_with_signed_yeilds_bad_origin() { new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::root(); - let location = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 11155111 })]); + let origin = RuntimeOrigin::signed([14; 32].into()); + let location = Location::new(1, [Parachain(2000)]); let versioned_location: Box = Box::new(location.clone().into()); let asset_metadata = AssetMetadata { decimals: 10, @@ -754,54 +646,105 @@ fn register_token_with_ethereum_address_reanchors_to_relative_and_fails() { assert_noop!( EthereumSystem::register_token(origin, versioned_location, asset_metadata), - Error::::LocationConversionFailed + BadOrigin ); }); } -#[test] -fn register_token_with_double_bridged_ethereum_address_succeeds() { - new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::root(); - const NETWORK: NetworkId = Ethereum { chain_id: 1 }; - let location = Location::new(2, [GlobalConsensus(NETWORK)]); - let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - - assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); - - let expected_token_id: H256 = - hex!("37fd94739deb1c2a8929b45a4f70ffcb52de8b54791609ee13ee0a2b33730269").into(); - let expected_location = Location::new(1, [GlobalConsensus(NETWORK)]); - - System::assert_last_event(RuntimeEvent::EthereumSystem( - crate::Event::::RegisterToken { - location: expected_location.into(), - foreign_token_id: expected_token_id, - }, - )); - }); +pub struct TokenInfo { + pub location: Location, + pub metadata: AssetMetadata, + pub foreign_token_id: TokenId, } #[test] -fn register_token_with_signed_yeilds_bad_origin() { - new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::signed([14; 32].into()); - let location = Location::new(1, [Parachain(2000)]); - let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - - assert_noop!( - EthereumSystem::register_token(origin, versioned_location, asset_metadata), - BadOrigin - ); - }); +fn register_all_tokens_succeeds() { + let assets = vec![ + // DOT + TokenInfo { + location: Location::parent(), + metadata: AssetMetadata { + decimals: 10, + name: b"DOT".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }, + foreign_token_id: hex!( + "4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2" + ) + .into(), + }, + // GLMR (Some Polkadot parachain currency) + TokenInfo { + location: Location::new(1, [Parachain(2004)]), + metadata: AssetMetadata { + decimals: 10, + name: b"GLMR".to_vec().try_into().unwrap(), + symbol: b"GLMR".to_vec().try_into().unwrap(), + }, + foreign_token_id: hex!( + "34c08fc90409b6924f0e8eabb7c2aaa0c749e23e31adad9f6d217b577737fafb" + ) + .into(), + }, + // USDT + TokenInfo { + location: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1084)]), + metadata: AssetMetadata { + decimals: 10, + name: b"USDT".to_vec().try_into().unwrap(), + symbol: b"USDT".to_vec().try_into().unwrap(), + }, + foreign_token_id: hex!( + "d49fe2118be0cca618e4d171e60ffea98b7b648dd80dc37d6342116b910b7aa5" + ) + .into(), + }, + // KSM + TokenInfo { + location: Location::new(2, [GlobalConsensus(Kusama)]), + metadata: AssetMetadata { + decimals: 12, + name: b"KSM".to_vec().try_into().unwrap(), + symbol: b"KSM".to_vec().try_into().unwrap(), + }, + foreign_token_id: hex!( + "03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852" + ) + .into(), + }, + // KAR (Some Kusama parachain currency) + TokenInfo { + location: Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), + metadata: AssetMetadata { + decimals: 12, + name: b"KAR".to_vec().try_into().unwrap(), + symbol: b"KAR".to_vec().try_into().unwrap(), + }, + foreign_token_id: hex!( + "d3e39ad6ea4cee68c9741181e94098823b2ea34a467577d0875c036f0fce5be0" + ) + .into(), + }, + ]; + for asset in assets.iter() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let versioned_location: Box = + Box::new(asset.location.clone().into()); + let asset_metadata = asset.metadata.clone(); + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let location = asset + .location + .clone() + .reanchored(&EthereumDestination::get(), &UniversalLocation::get()) + .unwrap(); + + System::assert_last_event(RuntimeEvent::EthereumSystem(Event::::RegisterToken { + location: location.into(), + foreign_token_id: asset.foreign_token_id, + })); + }); + } } From df60eb864e9d39bb5808d9ea284120bbf3f7a967 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 11:03:29 +0800 Subject: [PATCH 313/382] More tests --- .../snowbridge/pallets/system/src/tests.rs | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 3920d1c7e2ad..22b02d51dab2 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -677,7 +677,7 @@ fn register_all_tokens_succeeds() { TokenInfo { location: Location::new(1, [Parachain(2004)]), metadata: AssetMetadata { - decimals: 10, + decimals: 12, name: b"GLMR".to_vec().try_into().unwrap(), symbol: b"GLMR".to_vec().try_into().unwrap(), }, @@ -690,7 +690,7 @@ fn register_all_tokens_succeeds() { TokenInfo { location: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1084)]), metadata: AssetMetadata { - decimals: 10, + decimals: 6, name: b"USDT".to_vec().try_into().unwrap(), symbol: b"USDT".to_vec().try_into().unwrap(), }, @@ -748,3 +748,31 @@ fn register_all_tokens_succeeds() { }); } } + +#[test] +fn register_ethereum_native_token_fails() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: 11155111 }), + AccountKey20 { + network: None, + key: hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"), + }, + ], + ); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 18, + name: b"WETH".to_vec().try_into().unwrap(), + symbol: b"WETH".to_vec().try_into().unwrap(), + }; + + assert_noop!( + EthereumSystem::register_token(origin, versioned_location, asset_metadata), + Error::::LocationConversionFailed + ); + }); +} From ad9ad230c76a4ec7d387749071368519d46cbd04 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 11:05:11 +0800 Subject: [PATCH 314/382] Fix typo --- bridges/snowbridge/pallets/system/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 22b02d51dab2..d436724caba6 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -633,7 +633,7 @@ fn no_genesis_build_is_uninitialized() { } #[test] -fn register_token_with_signed_yeilds_bad_origin() { +fn register_token_with_signed_yields_bad_origin() { new_test_ext(true).execute_with(|| { let origin = RuntimeOrigin::signed([14; 32].into()); let location = Location::new(1, [Parachain(2000)]); From 388d1be8396925652212d52da6150eba5a32330f Mon Sep 17 00:00:00 2001 From: Ron Date: Sat, 7 Sep 2024 11:18:45 +0800 Subject: [PATCH 315/382] Update bridges/snowbridge/primitives/router/src/inbound/mod.rs Co-authored-by: Adrian Catangiu --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 7935de40ac3d..4aa721728449 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -479,6 +479,7 @@ impl< // Forward message id to Asset Hub. instructions.push(SetTopic(message_id.into())); + // `total_fees` to burn on this chain when sending `instructions` to run on AH (which also teleport fees) Ok((instructions.into(), total_fees.into())) } } From 7f110c2d2d7721ec1ea2151f83226bc8ea61cd8d Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 11:26:19 +0800 Subject: [PATCH 316/382] Rename var --- bridges/snowbridge/pallets/system/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 84b41cd0b9ea..fd2f2eb5b3ab 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -732,10 +732,11 @@ pub mod pallet { metadata: AssetMetadata, pays_fee: PaysFee, ) -> Result<(), DispatchError> { - let bridge_location = Location::new(2, [GlobalConsensus(T::EthereumNetwork::get())]); + let ethereum_location = Location::new(2, [GlobalConsensus(T::EthereumNetwork::get())]); + // reanchor to Ethereum context let location = location .clone() - .reanchored(&bridge_location, &T::UniversalLocation::get()) + .reanchored(ðereum_location, &T::UniversalLocation::get()) .map_err(|_| Error::::LocationConversionFailed)?; let token_id = TokenIdOf::convert_location(&location) From d0ac1bffe919226a49210177d6db32c4d1367991 Mon Sep 17 00:00:00 2001 From: Ron Date: Sat, 7 Sep 2024 11:26:42 +0800 Subject: [PATCH 317/382] Update bridges/snowbridge/primitives/core/src/location.rs Co-authored-by: Adrian Catangiu --- bridges/snowbridge/primitives/core/src/location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index c126618bba35..07c998a5c40e 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -29,7 +29,7 @@ pub type AgentIdOf = pub type TokenId = H256; -/// Convert a token location to a stable ID that can be used on the Ethereum side +/// Convert a token location (relative to Ethereum) to a stable ID that can be used on the Ethereum side pub type TokenIdOf = HashedDescription< TokenId, DescribeGlobalPrefix<(DescribeTerminus, DescribeFamily)>, From d65711353dda3fefb6abda5a367c255beddb08d5 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 11:38:50 +0800 Subject: [PATCH 318/382] Make test asset non sufficient --- .../chains/parachains/assets/asset-hub-westend/src/genesis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs index 73c5ef863309..a9cfcda0dacd 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs @@ -86,7 +86,7 @@ pub fn genesis() -> Storage { ( PenpalBTeleportableAssetLocation::get(), PenpalBSiblingSovereignAccount::get(), - true, + false, ED, ), ], From 5be8f8224af731e551671cb5f7412e90b9413e46 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 11:41:10 +0800 Subject: [PATCH 319/382] Bump package as minor --- prdoc/pr_5546.prdoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc index 16e2bd993463..44c8ee177a6f 100644 --- a/prdoc/pr_5546.prdoc +++ b/prdoc/pr_5546.prdoc @@ -13,11 +13,11 @@ crates: - name: snowbridge-pallet-outbound-queue bump: patch - name: snowbridge-pallet-system - bump: major + bump: minor - name: snowbridge-core - bump: major + bump: minor - name: snowbridge-router-primitives - bump: major + bump: minor - name: bridge-hub-westend-runtime bump: patch - name: bridge-hub-rococo-runtime From 8d1c9d7b18e258ac77146b2dc81e879ce818104a Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 11:47:08 +0800 Subject: [PATCH 320/382] Improve comments --- bridges/snowbridge/pallets/system/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index fd2f2eb5b3ab..9712f589b808 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -266,12 +266,12 @@ pub mod pallet { pub type PricingParameters = StorageValue<_, PricingParametersOf, ValueQuery, T::DefaultPricingParameters>; - /// Lookup table for foreign to native token ID + /// Lookup table for foreign token ID to native location relative to ethereum #[pallet::storage] pub type ForeignToNativeId = StorageMap<_, Blake2_128Concat, TokenId, xcm::v4::Location, OptionQuery>; - /// Lookup table for native to foreign token ID + /// Lookup table for native location relative to ethereum to foreign token ID #[pallet::storage] pub type NativeToForeignId = StorageMap<_, Blake2_128Concat, xcm::v4::Location, TokenId, OptionQuery>; From 9761118c357a15666743a84507fea168d9580961 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 16:13:18 +0800 Subject: [PATCH 321/382] Revert "Bump package as minor" This reverts commit 5be8f8224af731e551671cb5f7412e90b9413e46. --- prdoc/pr_5546.prdoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc index 44c8ee177a6f..16e2bd993463 100644 --- a/prdoc/pr_5546.prdoc +++ b/prdoc/pr_5546.prdoc @@ -13,11 +13,11 @@ crates: - name: snowbridge-pallet-outbound-queue bump: patch - name: snowbridge-pallet-system - bump: minor + bump: major - name: snowbridge-core - bump: minor + bump: major - name: snowbridge-router-primitives - bump: minor + bump: major - name: bridge-hub-westend-runtime bump: patch - name: bridge-hub-rococo-runtime From 96fecc3cfcfa91dc797a94f225027a266215d6e5 Mon Sep 17 00:00:00 2001 From: clangenb <37865735+clangenb@users.noreply.github.com> Date: Sat, 7 Sep 2024 12:15:47 +0200 Subject: [PATCH 322/382] Fix occasional `alloc` not found error in `format_runtime_string!` (#5632) The macro hygiene for the `format_runtime_string!` macro was broken since https://github.com/paritytech/polkadot-sdk/pull/5010, which resulted in the following build error under certain circumstances: ```console error[E0433]: failed to resolve: use of undeclared crate or module `alloc` --> /home/clang/.cargo/registry/src/index.crates.io-6f17d22bba15001f/frame-benchmarking-36.0.0/src/v1.rs:1738:2 | 1738 | / sp_runtime::format_runtime_string!( 1739 | | "\n* Pallet: {}\n\ 1740 | | * Benchmark: {}\n\ 1741 | | * Components: {:?}\n\ ... | 1750 | | error_message, 1751 | | ) | |_____^ use of undeclared crate or module `alloc` | = note: this error originates in the macro `sp_runtime::format_runtime_string` (in Nightly builds, run with -Z macro-backtrace for more info) For more information about this error, try `rustc --explain E0433`. ``` This bug has been known already, but hasn't been fixed so far, see https://github.com/paritytech/polkadot-sdk/issues/5213 and https://substrate.stackexchange.com/questions/11786/use-of-undeclared-crate-or-module-alloc-when-upgrade-to-v1-13-0. I have made a mini rust crate that can reproduce the bug, and it also shows that this PR will fix the issue: https://github.com/clangenb/sp-runtime-string-test. --- prdoc/pr_5632.prdoc | 13 +++++++++++++ substrate/primitives/runtime/src/lib.rs | 4 +--- substrate/primitives/runtime/src/runtime_string.rs | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 prdoc/pr_5632.prdoc diff --git a/prdoc/pr_5632.prdoc b/prdoc/pr_5632.prdoc new file mode 100644 index 000000000000..f76428bbc8f6 --- /dev/null +++ b/prdoc/pr_5632.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix `alloc` not found error in `format_runtime_string!` + +doc: + - audience: Runtime Dev + description: | + Fixes the macro hygiene in the `format_runtime_string!` macro to fix the `alloc` not found build error. + +crates: + - name: sp-runtime + bump: patch diff --git a/substrate/primitives/runtime/src/lib.rs b/substrate/primitives/runtime/src/lib.rs index ba1ea3769724..260c9a91855a 100644 --- a/substrate/primitives/runtime/src/lib.rs +++ b/substrate/primitives/runtime/src/lib.rs @@ -49,7 +49,7 @@ extern crate alloc; #[doc(hidden)] -pub use alloc::vec::Vec; +pub use alloc::{format, vec::Vec}; #[doc(hidden)] pub use codec; #[doc(hidden)] @@ -79,8 +79,6 @@ use sp_core::{ sr25519, }; -#[cfg(all(not(feature = "std"), feature = "serde"))] -use alloc::format; use alloc::vec; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; diff --git a/substrate/primitives/runtime/src/runtime_string.rs b/substrate/primitives/runtime/src/runtime_string.rs index 71aacf07a762..bb0347badcbb 100644 --- a/substrate/primitives/runtime/src/runtime_string.rs +++ b/substrate/primitives/runtime/src/runtime_string.rs @@ -50,7 +50,7 @@ macro_rules! format_runtime_string { } #[cfg(not(feature = "std"))] { - sp_runtime::RuntimeString::Owned(alloc::format!($($args)*).as_bytes().to_vec()) + sp_runtime::RuntimeString::Owned($crate::format!($($args)*).as_bytes().to_vec()) } }}; } From 016421ac71574333da92a56ef7bcbef8621ccc14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Molina=20Colmenero?= Date: Sat, 7 Sep 2024 23:42:02 +0200 Subject: [PATCH 323/382] Add debugging info for `StorageWeightReclaim` (#5594) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When inspecting the logs we often encounter the following message: `Benchmarked storage weight smaller than consumed storage weight. benchmarked: {benchmarked_weight} consumed: {consumed_weight} unspent: {unspent}` However, it is very hard to guess which call is causing the issue. With the changes proposed in this PR, information about the call is provided so that we can easily identify the source of the problem without further delay, and this way work more efficiently in solving the issue. --------- Co-authored-by: Bastian Köcher --- .../primitives/storage-weight-reclaim/src/lib.rs | 6 ++++-- prdoc/pr_5594.prdoc | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_5594.prdoc diff --git a/cumulus/primitives/storage-weight-reclaim/src/lib.rs b/cumulus/primitives/storage-weight-reclaim/src/lib.rs index a557e881e26b..2529297691e8 100644 --- a/cumulus/primitives/storage-weight-reclaim/src/lib.rs +++ b/cumulus/primitives/storage-weight-reclaim/src/lib.rs @@ -183,13 +183,15 @@ where if consumed_weight > benchmarked_weight { log::error!( target: LOG_TARGET, - "Benchmarked storage weight smaller than consumed storage weight. benchmarked: {benchmarked_weight} consumed: {consumed_weight} unspent: {unspent}" + "Benchmarked storage weight smaller than consumed storage weight. extrinsic: {} benchmarked: {benchmarked_weight} consumed: {consumed_weight} unspent: {unspent}", + frame_system::Pallet::::extrinsic_index().unwrap_or(0) ); current.accrue(Weight::from_parts(0, storage_size_diff), info.class) } else { log::trace!( target: LOG_TARGET, - "Reclaiming storage weight. benchmarked: {benchmarked_weight}, consumed: {consumed_weight} unspent: {unspent}" + "Reclaiming storage weight. extrinsic: {} benchmarked: {benchmarked_weight} consumed: {consumed_weight} unspent: {unspent}", + frame_system::Pallet::::extrinsic_index().unwrap_or(0) ); current.reduce(Weight::from_parts(0, storage_size_diff), info.class) } diff --git a/prdoc/pr_5594.prdoc b/prdoc/pr_5594.prdoc new file mode 100644 index 000000000000..dbdc7937b73d --- /dev/null +++ b/prdoc/pr_5594.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Add debugging info for `StorageWeightReclaim`" + +doc: + - audience: Runtime Dev + description: | + - Includes extrinsic index to be displayed in the logs when the consumed weight is higher than the measured one. + +crates: + - name: cumulus-primitives-storage-weight-reclaim + bump: patch From 868a36bd186f3ef9535ebf7deceac1b2fab19fcb Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Sun, 8 Sep 2024 23:14:51 +0200 Subject: [PATCH 324/382] [pallet-revive] update generic runtime types (#5608) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix #5574 - Use U256 instead of BalanceOf and MomentOf in Ext trait - Enforce H256 for T::Hash The Ext trait still depends on the associated type `T: Config`, we can look into refactoring it even more later but even in the current state it should not influence how the data is encoded / decoded between the contract and the host ``` fn caller(&self) -> Origin; -> only use to extract the address of the caller fn account_id(&self) -> &AccountIdOf; -> only used to expose the address or access the account_id internally fn gas_meter(&self) -> &GasMeter; fn gas_meter_mut(&mut self) -> &mut GasMeter; -> encoding does not depend on T fn call_runtime(&self, call: ::RuntimeCall) -> DispatchResultWithPostInfo; -> Substrate specific, just an opaque blob of bytes from the contract's perspective fn contract_info(&mut self) -> &mut ContractInfo; fn transient_storage(&mut self) -> &mut TransientStorage; -> gated by #[cfg(any(test, feature = "runtime-benchmarks"))] ``` --------- Co-authored-by: Alexander Theißen --- prdoc/pr_5608.prdoc | 16 + substrate/bin/node/runtime/src/lib.rs | 5 - substrate/frame/revive/Cargo.toml | 1 - substrate/frame/revive/build.rs | 78 --- .../revive/fixtures/contracts/balance.rs | 9 +- .../frame/revive/fixtures/contracts/call.rs | 8 +- .../fixtures/contracts/call_return_code.rs | 10 +- .../contracts/call_runtime_and_call.rs | 8 +- .../contracts/call_with_flags_and_value.rs | 10 +- .../fixtures/contracts/call_with_limit.rs | 4 +- .../fixtures/contracts/caller_contract.rs | 4 +- .../contracts/chain_extension_temp_storage.rs | 8 +- .../fixtures/contracts/common/src/lib.rs | 27 + .../contracts/create_storage_and_call.rs | 4 +- .../create_storage_and_instantiate.rs | 6 +- .../create_transient_storage_and_call.rs | 2 +- .../fixtures/contracts/delegate_call_lib.rs | 7 +- .../contracts/destroy_and_transfer.rs | 4 +- .../frame/revive/fixtures/contracts/drain.rs | 11 +- .../contracts/event_and_return_on_deploy.rs | 3 +- .../revive/fixtures/contracts/event_size.rs | 3 +- .../contracts/instantiate_return_code.rs | 6 +- .../fixtures/contracts/read_only_call.rs | 8 +- .../revive/fixtures/contracts/recurse.rs | 8 +- .../fixtures/contracts/self_destruct.rs | 8 +- .../contracts/transfer_return_code.rs | 4 +- .../revive/src/benchmarking/call_builder.rs | 10 +- .../frame/revive/src/benchmarking/mod.rs | 194 ++---- substrate/frame/revive/src/exec.rs | 178 +++-- substrate/frame/revive/src/lib.rs | 164 ++--- substrate/frame/revive/src/migration.rs | 650 ------------------ substrate/frame/revive/src/primitives.rs | 2 - substrate/frame/revive/src/storage.rs | 8 +- substrate/frame/revive/src/storage/meter.rs | 10 +- .../frame/revive/src/test_utils/builder.rs | 3 + substrate/frame/revive/src/tests.rs | 103 +-- substrate/frame/revive/src/wasm/mod.rs | 21 +- substrate/frame/revive/src/wasm/runtime.rs | 170 ++--- substrate/frame/revive/src/weights.rs | 365 ---------- substrate/frame/revive/uapi/src/host.rs | 61 +- .../frame/revive/uapi/src/host/riscv32.rs | 94 +-- 41 files changed, 507 insertions(+), 1788 deletions(-) create mode 100644 prdoc/pr_5608.prdoc delete mode 100644 substrate/frame/revive/build.rs delete mode 100644 substrate/frame/revive/src/migration.rs diff --git a/prdoc/pr_5608.prdoc b/prdoc/pr_5608.prdoc new file mode 100644 index 000000000000..9a0748e46bab --- /dev/null +++ b/prdoc/pr_5608.prdoc @@ -0,0 +1,16 @@ +title: "[pallet-revive] update runtime types" + +doc: + - audience: Runtime Dev + description: | + Refactor the Ext trait to use U256 instead of BalanceOf or MomentOf + +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-uapi + bump: patch + - name: pallet-revive-fixtures + bump: patch + + diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 6ae04902aa82..caebd63408db 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1417,10 +1417,6 @@ impl pallet_revive::Config for Runtime { type UploadOrigin = EnsureSigned; type InstantiateOrigin = EnsureSigned; type RuntimeHoldReason = RuntimeHoldReason; - #[cfg(not(feature = "runtime-benchmarks"))] - type Migrations = (); - #[cfg(feature = "runtime-benchmarks")] - type Migrations = pallet_revive::migration::codegen::BenchMigrations; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type Debug = (); type Xcm = (); @@ -2592,7 +2588,6 @@ type Migrations = ( pallet_nomination_pools::migration::versioned::V6ToV7, pallet_alliance::migration::Migration, pallet_contracts::Migration, - pallet_revive::Migration, pallet_identity::migration::versioned::V0ToV1, ); diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml index 6b7542e89202..667328ac2d0d 100644 --- a/substrate/frame/revive/Cargo.toml +++ b/substrate/frame/revive/Cargo.toml @@ -3,7 +3,6 @@ name = "pallet-revive" version = "0.1.0" authors.workspace = true edition.workspace = true -build = "build.rs" license = "Apache-2.0" homepage.workspace = true repository.workspace = true diff --git a/substrate/frame/revive/build.rs b/substrate/frame/revive/build.rs deleted file mode 100644 index ca8e62df6047..000000000000 --- a/substrate/frame/revive/build.rs +++ /dev/null @@ -1,78 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::io::Write; - -/// We start with version 2 instead of 0 when adding the pallet. -/// -/// Because otherwise we can't test any migrations since they require the storage version -/// to be lower than the pallet version in order to be triggerd. With the pallet version -/// at the minimum (0) this would not work. -const LOWEST_STORAGE_VERSION: u16 = 2; - -/// Get the latest migration version. -/// -/// Find the highest version number from the available migration files. -/// Each migration file should follow the naming convention `vXX.rs`, where `XX` is the version -/// number. -fn get_latest_version() -> u16 { - let Ok(dir) = std::fs::read_dir("src/migration") else { return LOWEST_STORAGE_VERSION }; - dir.filter_map(|entry| { - let file_name = entry.as_ref().ok()?.file_name(); - let file_name = file_name.to_str()?; - if file_name.starts_with('v') && file_name.ends_with(".rs") { - let version = &file_name[1..&file_name.len() - 3]; - let version = version.parse::().ok()?; - - // Ensure that the version matches the one defined in the file. - let path = entry.unwrap().path(); - let file_content = std::fs::read_to_string(&path).ok()?; - assert!( - file_content.contains(&format!("const VERSION: u16 = {}", version)), - "Invalid MigrationStep::VERSION in {:?}", - path - ); - - return Some(version) - } - None - }) - .max() - .unwrap_or(LOWEST_STORAGE_VERSION) -} - -/// Generates a module that exposes the latest migration version, and the benchmark migrations type. -fn main() -> Result<(), Box> { - let out_dir = std::env::var("OUT_DIR")?; - let path = std::path::Path::new(&out_dir).join("migration_codegen.rs"); - let mut f = std::fs::File::create(path)?; - let version = get_latest_version(); - write!( - f, - " - pub mod codegen {{ - use crate::NoopMigration; - /// The latest migration version, pulled from the latest migration file. - pub const LATEST_MIGRATION_VERSION: u16 = {version}; - /// The Migration Steps used for benchmarking the migration framework. - pub type BenchMigrations = (NoopMigration<{}>, NoopMigration<{version}>); - }}", - version - 1, - )?; - - Ok(()) -} diff --git a/substrate/frame/revive/fixtures/contracts/balance.rs b/substrate/frame/revive/fixtures/contracts/balance.rs index 4011b8379cbf..4606135d9807 100644 --- a/substrate/frame/revive/fixtures/contracts/balance.rs +++ b/substrate/frame/revive/fixtures/contracts/balance.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -use common::output; +use common::u64_output; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -28,9 +28,6 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - // Initialize buffer with 1s so that we can check that it is overwritten. - output!(balance, [1u8; 8], api::balance,); - - // Assert that the balance is 0. - assert_eq!(&[0u8; 8], balance); + let balance = u64_output!(api::balance,); + assert_eq!(balance, 0); } diff --git a/substrate/frame/revive/fixtures/contracts/call.rs b/substrate/frame/revive/fixtures/contracts/call.rs index 93687441fa50..ee51548879d9 100644 --- a/substrate/frame/revive/fixtures/contracts/call.rs +++ b/substrate/frame/revive/fixtures/contracts/call.rs @@ -38,10 +38,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::empty(), callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &0u64.to_le_bytes(), // Value transferred to the contract. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. callee_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/call_return_code.rs b/substrate/frame/revive/fixtures/contracts/call_return_code.rs index 29b77c343fe9..25370459acb4 100644 --- a/substrate/frame/revive/fixtures/contracts/call_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/call_return_code.rs @@ -21,7 +21,7 @@ #![no_std] #![no_main] -use common::input; +use common::{input, u256_bytes}; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -41,10 +41,10 @@ pub extern "C" fn call() { let err_code = match api::call( uapi::CallFlags::empty(), callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &100u64.to_le_bytes(), // Value transferred to the contract. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &u256_bytes(100u64), // Value transferred to the contract. input, None, ) { diff --git a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs index 7cd46849655f..8c8aee962849 100644 --- a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs @@ -42,10 +42,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::empty(), callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &0u64.to_le_bytes(), // Value transferred to the contract. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. callee_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs index c3204c29281c..330393e706e9 100644 --- a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs +++ b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs @@ -19,7 +19,7 @@ #![no_std] #![no_main] -use common::input; +use common::{input, u256_bytes}; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -40,10 +40,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::from_bits(flags).unwrap(), callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &value.to_le_bytes(), // Value transferred to the contract. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &u256_bytes(value), // Value transferred to the contract. forwarded_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs index a941aa9a3421..6ab892a6b7ae 100644 --- a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs +++ b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs @@ -43,8 +43,8 @@ pub extern "C" fn call() { callee_addr, ref_time, proof_size, - None, // No deposit limit. - &0u64.to_le_bytes(), // value transferred to the contract. + None, // No deposit limit. + &[0u8; 32], // value transferred to the contract. forwarded_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/caller_contract.rs b/substrate/frame/revive/fixtures/contracts/caller_contract.rs index 3b83f208d623..eb29fca87c15 100644 --- a/substrate/frame/revive/fixtures/contracts/caller_contract.rs +++ b/substrate/frame/revive/fixtures/contracts/caller_contract.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -use common::input; +use common::{input, u256_bytes}; use uapi::{HostFn, HostFnImpl as api, ReturnErrorCode}; #[no_mangle] @@ -32,7 +32,7 @@ pub extern "C" fn call() { // The value to transfer on instantiation and calls. Chosen to be greater than existential // deposit. - let value = 32768u64.to_le_bytes(); + let value = u256_bytes(32768u64); let salt = [0u8; 32]; // Callee will use the first 4 bytes of the input to return an exit status. diff --git a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs index bb5c1ccbc1d6..22d6c5b548d8 100644 --- a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs +++ b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs @@ -54,10 +54,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::ALLOW_REENTRY, &addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &0u64.to_le_bytes(), // Value transferred to the contract. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/common/src/lib.rs b/substrate/frame/revive/fixtures/contracts/common/src/lib.rs index 947247e9cf74..abfba282bec1 100644 --- a/substrate/frame/revive/fixtures/contracts/common/src/lib.rs +++ b/substrate/frame/revive/fixtures/contracts/common/src/lib.rs @@ -167,3 +167,30 @@ macro_rules! unwrap_output { $host_fn($($arg,)* $output).unwrap(); }; } + +/// Call the host function and convert the [u8; 32] output to u64. +#[macro_export] +macro_rules! u64_output { + ($host_fn:path, $($arg:expr),*) => {{ + let mut buffer = [1u8; 32]; + $host_fn($($arg,)* &mut buffer); + assert!(buffer[8..].iter().all(|&x| x == 0)); + u64::from_le_bytes(buffer[..8].try_into().unwrap()) + }}; +} + +/// Convert a u64 into a [u8; 32]. +pub const fn u256_bytes(value: u64) -> [u8; 32] { + let mut buffer = [0u8; 32]; + let bytes = value.to_le_bytes(); + + buffer[0] = bytes[0]; + buffer[1] = bytes[1]; + buffer[2] = bytes[2]; + buffer[3] = bytes[3]; + buffer[4] = bytes[4]; + buffer[5] = bytes[5]; + buffer[6] = bytes[6]; + buffer[7] = bytes[7]; + buffer +} diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs index 28d161791e5b..4fa2db0c8c1c 100644 --- a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs @@ -33,7 +33,7 @@ pub extern "C" fn call() { buffer, input: [u8; 4], callee: &[u8; 20], - deposit_limit: [u8; 8], + deposit_limit: &[u8; 32], ); // create 4 byte of storage before calling @@ -46,7 +46,7 @@ pub extern "C" fn call() { 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, // How much proof_size weight to devote for the execution. 0 = all. Some(deposit_limit), - &0u64.to_le_bytes(), // Value transferred to the contract. + &[0u8; 32], // Value transferred to the contract. input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs index d87c2e8cd35a..e1372e2eb8b6 100644 --- a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs @@ -19,7 +19,7 @@ #![no_std] #![no_main] -use common::input; +use common::{input, u256_bytes}; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -32,10 +32,10 @@ pub extern "C" fn call() { input!( input: [u8; 4], code_hash: &[u8; 32], - deposit_limit: [u8; 8], + deposit_limit: &[u8; 32], ); - let value = 10_000u64.to_le_bytes(); + let value = u256_bytes(10_000u64); let salt = [0u8; 32]; let mut address = [0u8; 20]; diff --git a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs index 753490cf26b7..d2efb26e5ceb 100644 --- a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs @@ -52,7 +52,7 @@ pub extern "C" fn call() { 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, - &0u64.to_le_bytes(), // Value transferred to the contract. + &[0u8; 32], // Value transferred to the contract. input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs index c5525423a9ee..95c1bd2aa6cd 100644 --- a/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -use common::output; +use common::u64_output; use uapi::{HostFn, HostFnImpl as api, StorageFlags}; #[no_mangle] @@ -39,9 +39,8 @@ pub extern "C" fn call() { // Assert that `value_transferred` is equal to the value // passed to the `caller` contract: 1337. - output!(value_transferred, [0u8; 8], api::value_transferred,); - let value_transferred = u64::from_le_bytes(value_transferred[..].try_into().unwrap()); - assert_eq!(value_transferred, 1337); + let value = u64_output!(api::value_transferred,); + assert_eq!(value, 1337); // Assert that ALICE is the caller of the contract. let mut caller = [0u8; 20]; diff --git a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs index 4959a5e2e0ce..d381db8e398f 100644 --- a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs +++ b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs @@ -18,11 +18,11 @@ #![no_std] #![no_main] -use common::input; +use common::{input, u256_bytes}; use uapi::{HostFn, HostFnImpl as api, StorageFlags}; const ADDRESS_KEY: [u8; 32] = [0u8; 32]; -const VALUE: [u8; 8] = [0, 0, 1u8, 0, 0, 0, 0, 0]; +const VALUE: [u8; 32] = u256_bytes(65536); #[no_mangle] #[polkavm_derive::polkavm_export] diff --git a/substrate/frame/revive/fixtures/contracts/drain.rs b/substrate/frame/revive/fixtures/contracts/drain.rs index b46d4f7c8418..0d644a4238c4 100644 --- a/substrate/frame/revive/fixtures/contracts/drain.rs +++ b/substrate/frame/revive/fixtures/contracts/drain.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -use common::output; +use common::{u256_bytes, u64_output}; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -28,17 +28,14 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - output!(balance, [0u8; 8], api::balance,); - let balance = u64::from_le_bytes(balance[..].try_into().unwrap()); - - output!(minimum_balance, [0u8; 8], api::minimum_balance,); - let minimum_balance = u64::from_le_bytes(minimum_balance[..].try_into().unwrap()); + let balance = u64_output!(api::balance,); + let minimum_balance = u64_output!(api::minimum_balance,); // Make the transferred value exceed the balance by adding the minimum balance. let balance = balance + minimum_balance; // Try to self-destruct by sending more balance to the 0 address. // The call will fail because a contract transfer has a keep alive requirement. - let res = api::transfer(&[0u8; 20], &balance.to_le_bytes()); + let res = api::transfer(&[0u8; 20], &u256_bytes(balance)); assert!(matches!(res, Err(uapi::ReturnErrorCode::TransferFailed))); } diff --git a/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs b/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs index 9186835d2911..5c438c1a75a1 100644 --- a/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs +++ b/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs @@ -25,7 +25,8 @@ use uapi::{HostFn, HostFnImpl as api}; #[polkavm_derive::polkavm_export] pub extern "C" fn deploy() { let buffer = [1u8, 2, 3, 4]; - api::deposit_event(&[0u8; 0], &buffer); + let topics = [[42u8; 32]; 1]; + api::deposit_event(&topics, &buffer); api::return_value(uapi::ReturnFlags::empty(), &buffer); } diff --git a/substrate/frame/revive/fixtures/contracts/event_size.rs b/substrate/frame/revive/fixtures/contracts/event_size.rs index 2b56de4bd3fd..7f04ae42765a 100644 --- a/substrate/frame/revive/fixtures/contracts/event_size.rs +++ b/substrate/frame/revive/fixtures/contracts/event_size.rs @@ -33,6 +33,7 @@ pub extern "C" fn call() { input!(len: u32,); let data = &BUFFER[..len as usize]; + let topics = [[0u8; 32]; 0]; - api::deposit_event(&[0u8; 0], data); + api::deposit_event(&topics, data); } diff --git a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs index a81ffea943d4..c5736850960a 100644 --- a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -use common::input; +use common::{input, u256_bytes}; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -36,8 +36,8 @@ pub extern "C" fn call() { 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, /* How much proof_size weight to devote for the execution. 0 = * all. */ - None, // No deposit limit. - &10_000u64.to_le_bytes(), // Value to transfer. + None, // No deposit limit. + &u256_bytes(10_000u64), // Value to transfer. input, None, None, diff --git a/substrate/frame/revive/fixtures/contracts/read_only_call.rs b/substrate/frame/revive/fixtures/contracts/read_only_call.rs index 7476b7a8366d..ea74d56867f5 100644 --- a/substrate/frame/revive/fixtures/contracts/read_only_call.rs +++ b/substrate/frame/revive/fixtures/contracts/read_only_call.rs @@ -39,10 +39,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::READ_ONLY, callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &0u64.to_le_bytes(), // Value transferred to the contract. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. callee_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/recurse.rs b/substrate/frame/revive/fixtures/contracts/recurse.rs index c15784b7f245..2e70d67d8c73 100644 --- a/substrate/frame/revive/fixtures/contracts/recurse.rs +++ b/substrate/frame/revive/fixtures/contracts/recurse.rs @@ -43,10 +43,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::ALLOW_REENTRY, &addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much deposit_limit to devote for the execution. 0 = all. - None, // No deposit limit. - &0u64.to_le_bytes(), // Value transferred to the contract. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much deposit_limit to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. &(calls_left - 1).to_le_bytes(), None, ) diff --git a/substrate/frame/revive/fixtures/contracts/self_destruct.rs b/substrate/frame/revive/fixtures/contracts/self_destruct.rs index 0e1e4d30e6f3..524979991ec7 100644 --- a/substrate/frame/revive/fixtures/contracts/self_destruct.rs +++ b/substrate/frame/revive/fixtures/contracts/self_destruct.rs @@ -42,10 +42,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::ALLOW_REENTRY, &addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &0u64.to_le_bytes(), // Value to transfer. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value to transfer. &[0u8; 0], None, ) diff --git a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs index 3e1f2757c27a..bfeca9b8b4a4 100644 --- a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -extern crate common; +use common::u256_bytes; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - let ret_code = match api::transfer(&[0u8; 20], &100u64.to_le_bytes()) { + let ret_code = match api::transfer(&[0u8; 20], &u256_bytes(100u64)) { Ok(_) => 0u32, Err(code) => code as u32, }; diff --git a/substrate/frame/revive/src/benchmarking/call_builder.rs b/substrate/frame/revive/src/benchmarking/call_builder.rs index c000817a8a39..020a578c3a3a 100644 --- a/substrate/frame/revive/src/benchmarking/call_builder.rs +++ b/substrate/frame/revive/src/benchmarking/call_builder.rs @@ -22,12 +22,14 @@ use crate::{ storage::meter::Meter, transient_storage::MeterEntry, wasm::{ApiVersion, PreparedCall, Runtime}, - BalanceOf, Config, DebugBuffer, Error, GasMeter, Origin, TypeInfo, WasmBlob, Weight, + BalanceOf, Config, DebugBuffer, Error, GasMeter, MomentOf, Origin, TypeInfo, WasmBlob, Weight, }; use alloc::{vec, vec::Vec}; use codec::{Encode, HasCompact}; use core::fmt::Debug; use frame_benchmarking::benchmarking; +use frame_support::traits::IsType; +use sp_core::{H256, U256}; type StackExt<'a, T> = Stack<'a, T, WasmBlob>; @@ -48,6 +50,9 @@ impl Default for CallSetup where T: Config + pallet_balances::Config, as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + BalanceOf: Into + TryFrom, + T::Hash: IsType, + MomentOf: Into, { fn default() -> Self { Self::new(WasmModule::dummy()) @@ -57,7 +62,10 @@ where impl CallSetup where T: Config + pallet_balances::Config, + T::Hash: IsType, as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + BalanceOf: Into + TryFrom, + MomentOf: Into, { /// Setup a new call for the given module. pub fn new(module: WasmModule) -> Self { diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index 2c5285622843..8cdd7da5db9d 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -25,7 +25,6 @@ use self::{call_builder::CallSetup, code::WasmModule}; use crate::{ exec::{Key, MomentOf}, limits, - migration::codegen::LATEST_MIGRATION_VERSION, storage::WriteOutcome, Pallet as Contracts, *, }; @@ -34,7 +33,6 @@ use codec::{Encode, MaxEncodedLen}; use frame_benchmarking::v2::*; use frame_support::{ self, assert_ok, - pallet_prelude::StorageVersion, storage::child, traits::{fungible::InspectHold, Currency}, weights::{Weight, WeightMeter}, @@ -65,14 +63,21 @@ const UNBALANCED_TRIE_LAYERS: u32 = 20; struct Contract { caller: T::AccountId, account_id: T::AccountId, - addr: T::AccountId, } impl Contract where T: Config + pallet_balances::Config, as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + BalanceOf: Into + TryFrom, + MomentOf: Into, + T::Hash: IsType, { + /// Returns the address of the contract. + fn address(&self) -> H160 { + T::AddressMapper::to_address(&self.account_id) + } + /// Create new contract and use a default account id as instantiator. fn new(module: WasmModule, data: Vec) -> Result, &'static str> { Self::with_index(0, module, data) @@ -110,7 +115,7 @@ where let address = outcome.result?.addr; let account_id = T::AddressMapper::to_account_id_contract(&address); - let result = Contract { caller, account_id: account_id.clone(), addr: account_id }; + let result = Contract { caller, account_id: account_id.clone() }; ContractInfoOf::::insert(&address, result.info()?); @@ -216,9 +221,12 @@ fn default_deposit_limit() -> BalanceOf { #[benchmarks( where - as codec::HasCompact>::Type: Clone + Eq + PartialEq + core::fmt::Debug + scale_info::TypeInfo + codec::Encode, + as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + BalanceOf: From< as Currency>::Balance> + Into + TryFrom, T: Config + pallet_balances::Config, - BalanceOf: From< as Currency>::Balance>, + MomentOf: Into, + ::RuntimeEvent: From>, + T::Hash: IsType, as Currency>::Balance: From>, )] mod benchmarks { @@ -246,73 +254,6 @@ mod benchmarks { Ok(()) } - // This benchmarks the weight of executing Migration::migrate to execute a noop migration. - #[benchmark(pov_mode = Measured)] - fn migration_noop() { - let version = LATEST_MIGRATION_VERSION; - StorageVersion::new(version).put::>(); - #[block] - { - Migration::::migrate(&mut WeightMeter::new()); - } - assert_eq!(StorageVersion::get::>(), version); - } - - // This benchmarks the weight of dispatching migrate to execute 1 `NoopMigration` - #[benchmark(pov_mode = Measured)] - fn migrate() { - let latest_version = LATEST_MIGRATION_VERSION; - StorageVersion::new(latest_version - 2).put::>(); - as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); - - #[extrinsic_call] - _(RawOrigin::Signed(whitelisted_caller()), Weight::MAX); - - assert_eq!(StorageVersion::get::>(), latest_version - 1); - } - - // This benchmarks the weight of running on_runtime_upgrade when there are no migration in - // progress. - #[benchmark(pov_mode = Measured)] - fn on_runtime_upgrade_noop() { - let latest_version = LATEST_MIGRATION_VERSION; - StorageVersion::new(latest_version).put::>(); - #[block] - { - as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); - } - assert!(MigrationInProgress::::get().is_none()); - } - - // This benchmarks the weight of running on_runtime_upgrade when there is a migration in - // progress. - #[benchmark(pov_mode = Measured)] - fn on_runtime_upgrade_in_progress() { - let latest_version = LATEST_MIGRATION_VERSION; - StorageVersion::new(latest_version - 2).put::>(); - let v = vec![42u8].try_into().ok(); - MigrationInProgress::::set(v.clone()); - #[block] - { - as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); - } - assert!(MigrationInProgress::::get().is_some()); - assert_eq!(MigrationInProgress::::get(), v); - } - - // This benchmarks the weight of running on_runtime_upgrade when there is a migration to - // process. - #[benchmark(pov_mode = Measured)] - fn on_runtime_upgrade() { - let latest_version = LATEST_MIGRATION_VERSION; - StorageVersion::new(latest_version - 2).put::>(); - #[block] - { - as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); - } - assert!(MigrationInProgress::::get().is_some()); - } - // This benchmarks the overhead of loading a code of size `c` byte from storage and into // the execution engine. This does **not** include the actual execution for which the gas meter // is responsible. This is achieved by generating all code to the `deploy` function @@ -326,7 +267,7 @@ mod benchmarks { let instance = Contract::::with_caller(whitelisted_caller(), WasmModule::sized(c), vec![])?; let value = Pallet::::min_balance(); - let callee = T::AddressMapper::to_address(&instance.addr); + let callee = T::AddressMapper::to_address(&instance.account_id); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] @@ -434,7 +375,7 @@ mod benchmarks { Contract::::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; let value = Pallet::::min_balance(); let origin = RawOrigin::Signed(instance.caller.clone()); - let callee = T::AddressMapper::to_address(&instance.addr); + let callee = T::AddressMapper::to_address(&instance.account_id); let before = T::Currency::balance(&instance.account_id); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] @@ -510,7 +451,7 @@ mod benchmarks { let storage_deposit = default_deposit_limit::(); let hash = >::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash; - let callee = T::AddressMapper::to_address(&instance.addr); + let callee = T::AddressMapper::to_address(&instance.account_id); assert_ne!(instance.info()?.code_hash, hash); #[extrinsic_call] _(RawOrigin::Root, callee, hash); @@ -661,85 +602,67 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn seal_balance() { - let len = ::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [[0u8;32], ]); let result; #[block] { - result = runtime.bench_balance(memory.as_mut_slice(), 4, 0); + result = runtime.bench_balance(memory.as_mut_slice(), 0); } assert_ok!(result); - assert_eq!( - ::decode(&mut &memory[4..]).unwrap(), - runtime.ext().balance().into() - ); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().balance()); } #[benchmark(pov_mode = Measured)] fn seal_value_transferred() { - let len = ::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [[0u8;32], ]); let result; #[block] { - result = runtime.bench_value_transferred(memory.as_mut_slice(), 4, 0); + result = runtime.bench_value_transferred(memory.as_mut_slice(), 0); } assert_ok!(result); - assert_eq!( - ::decode(&mut &memory[4..]).unwrap(), - runtime.ext().value_transferred().into() - ); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().value_transferred()); } #[benchmark(pov_mode = Measured)] fn seal_minimum_balance() { - let len = ::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [[0u8;32], ]); let result; #[block] { - result = runtime.bench_minimum_balance(memory.as_mut_slice(), 4, 0); + result = runtime.bench_minimum_balance(memory.as_mut_slice(), 0); } assert_ok!(result); - assert_eq!( - ::decode(&mut &memory[4..]).unwrap(), - runtime.ext().minimum_balance().into() - ); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().minimum_balance()); } #[benchmark(pov_mode = Measured)] fn seal_block_number() { - let len = as MaxEncodedLen>::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [[0u8;32], ]); let result; #[block] { - result = runtime.bench_block_number(memory.as_mut_slice(), 4, 0); + result = runtime.bench_block_number(memory.as_mut_slice(), 0); } assert_ok!(result); - assert_eq!( - >::decode(&mut &memory[4..]).unwrap(), - runtime.ext().block_number() - ); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().block_number()); } #[benchmark(pov_mode = Measured)] fn seal_now() { - let len = as MaxEncodedLen>::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [[0u8;32], ]); let result; #[block] { - result = runtime.bench_now(memory.as_mut_slice(), 4, 0); + result = runtime.bench_now(memory.as_mut_slice(), 0); } assert_ok!(result); - assert_eq!(>::decode(&mut &memory[4..]).unwrap(), *runtime.ext().now()); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().now()); } #[benchmark(pov_mode = Measured)] fn seal_weight_to_fee() { - let len = ::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [[0u8;32], ]); let weight = Weight::from_parts(500_000, 300_000); let result; #[block] @@ -748,15 +671,11 @@ mod benchmarks { memory.as_mut_slice(), weight.ref_time(), weight.proof_size(), - 4, 0, ); } assert_ok!(result); - assert_eq!( - >::decode(&mut &memory[4..]).unwrap(), - runtime.ext().get_weight_price(weight) - ); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().get_weight_price(weight)); } #[benchmark(pov_mode = Measured)] @@ -828,28 +747,37 @@ mod benchmarks { t: Linear<0, { limits::NUM_EVENT_TOPICS as u32 }>, n: Linear<0, { limits::PAYLOAD_BYTES }>, ) { - let topics = (0..t).map(|i| T::Hashing::hash_of(&i)).collect::>().encode(); - let topics_len = topics.len() as u32; - - build_runtime!(runtime, memory: [ - n.to_le_bytes(), - topics, - vec![0u8; n as _], - ]); + let num_topic = t as u32; + let topics = (0..t).map(|i| H256::repeat_byte(i as u8)).collect::>(); + let topics_data = + topics.iter().flat_map(|hash| hash.as_bytes().to_vec()).collect::>(); + let data = vec![42u8; n as _]; + build_runtime!(runtime, instance, memory: [ topics_data, data, ]); let result; #[block] { result = runtime.bench_deposit_event( memory.as_mut_slice(), - 4, // topics_ptr - topics_len, // topics_len - 4 + topics_len, // data_ptr - 0, // data_len + 0, // topics_ptr + num_topic, + topics_data.len() as u32, // data_ptr + n, // data_len ); } - assert_ok!(result); + + let events = System::::events(); + let record = &events[events.len() - 1]; + + assert_eq!( + record.event, + crate::Event::ContractEmitted { contract: instance.address(), data }.into(), + ); + assert_eq!( + record.topics.iter().map(|t| H256::from_slice(t.as_ref())).collect::>(), + topics, + ); } // Benchmark debug_message call @@ -1435,7 +1363,7 @@ mod benchmarks { let account_bytes = account.encode(); let account_len = account_bytes.len() as u32; - let value_bytes = value.encode(); + let value_bytes = Into::::into(value).encode(); let mut memory = memory!(account_bytes, value_bytes,); let result; @@ -1461,10 +1389,10 @@ mod benchmarks { let callee_len = callee_bytes.len() as u32; let value: BalanceOf = t.into(); - let value_bytes = value.encode(); + let value_bytes = Into::::into(value).encode(); let deposit: BalanceOf = (u32::MAX - 100).into(); - let deposit_bytes = deposit.encode(); + let deposit_bytes = Into::::into(deposit).encode(); let deposit_len = deposit_bytes.len() as u32; let mut setup = CallSetup::::default(); @@ -1536,11 +1464,11 @@ mod benchmarks { let hash_len = hash_bytes.len() as u32; let value: BalanceOf = 1u32.into(); - let value_bytes = value.encode(); + let value_bytes = Into::::into(value).encode(); let value_len = value_bytes.len() as u32; let deposit: BalanceOf = 0u32.into(); - let deposit_bytes = deposit.encode(); + let deposit_bytes = Into::::into(deposit).encode(); let deposit_len = deposit_bytes.len() as u32; let mut setup = CallSetup::::default(); diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 649479f7790f..016bdec37afd 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -37,7 +37,7 @@ use frame_support::{ traits::{ fungible::{Inspect, Mutate}, tokens::{Fortitude, Preservation}, - Contains, OriginTrait, Time, + Contains, IsType, OriginTrait, Time, }, weights::Weight, Blake2_128Concat, BoundedVec, StorageHasher, @@ -49,7 +49,7 @@ use frame_system::{ use sp_core::{ ecdsa::Public as ECDSAPublic, sr25519::{Public as SR25519Public, Signature as SR25519Signature}, - ConstU32, Get, H160, H256, + ConstU32, Get, H160, H256, U256, }; use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256}; use sp_runtime::{ @@ -61,9 +61,6 @@ pub type AccountIdOf = ::AccountId; pub type MomentOf = <::Time as Time>::Moment; pub type ExecResult = Result; -/// A type that represents a topic of an event. At the moment a hash is used. -pub type TopicOf = ::Hash; - /// Type for variable sized storage key. Used for transparent hashing. type VarSizedKey = BoundedVec>; @@ -184,9 +181,9 @@ pub trait Ext: sealing::Sealed { fn call( &mut self, gas_limit: Weight, - deposit_limit: BalanceOf, + deposit_limit: U256, to: &H160, - value: BalanceOf, + value: U256, input_data: Vec, allows_reentry: bool, read_only: bool, @@ -209,9 +206,9 @@ pub trait Ext: sealing::Sealed { fn instantiate( &mut self, gas_limit: Weight, - deposit_limit: BalanceOf, + deposit_limit: U256, code: H256, - value: BalanceOf, + value: U256, input_data: Vec, salt: Option<&[u8; 32]>, ) -> Result<(H160, ExecReturnValue), ExecError>; @@ -226,7 +223,7 @@ pub trait Ext: sealing::Sealed { fn terminate(&mut self, beneficiary: &H160) -> DispatchResult; /// Transfer some amount of funds into the specified account. - fn transfer(&mut self, to: &H160, value: BalanceOf) -> DispatchResult; + fn transfer(&mut self, to: &H160, value: U256) -> DispatchResult; /// Returns the storage entry of the executing account by the given `key`. /// @@ -304,30 +301,30 @@ pub trait Ext: sealing::Sealed { /// Returns the balance of the current contract. /// /// The `value_transferred` is already added. - fn balance(&self) -> BalanceOf; + fn balance(&self) -> U256; /// Returns the value transferred along with this call. - fn value_transferred(&self) -> BalanceOf; + fn value_transferred(&self) -> U256; - /// Returns a reference to the timestamp of the current block - fn now(&self) -> &MomentOf; + /// Returns the timestamp of the current block + fn now(&self) -> U256; /// Returns the minimum balance that is required for creating an account. - fn minimum_balance(&self) -> BalanceOf; + fn minimum_balance(&self) -> U256; /// Deposit an event with the given topics. /// /// There should not be any duplicates in `topics`. - fn deposit_event(&mut self, topics: Vec>, data: Vec); + fn deposit_event(&mut self, topics: Vec, data: Vec); /// Returns the current block number. - fn block_number(&self) -> BlockNumberFor; + fn block_number(&self) -> U256; /// Returns the maximum allowed size of a storage item. fn max_value_size(&self) -> u32; /// Returns the price for the specified amount of weight. - fn get_weight_price(&self, weight: Weight) -> BalanceOf; + fn get_weight_price(&self, weight: Weight) -> U256; /// Get an immutable reference to the nested gas meter. fn gas_meter(&self) -> &GasMeter; @@ -697,6 +694,9 @@ impl CachedContract { impl<'a, T, E> Stack<'a, T, E> where T: Config, + T::Hash: IsType, + BalanceOf: Into + TryFrom, + MomentOf: Into, E: Executable, { /// Create and run a new call stack by calling into `dest`. @@ -1239,16 +1239,19 @@ where impl<'a, T, E> Ext for Stack<'a, T, E> where T: Config, + T::Hash: IsType, E: Executable, + BalanceOf: Into + TryFrom, + MomentOf: Into, { type T = T; fn call( &mut self, gas_limit: Weight, - deposit_limit: BalanceOf, + deposit_limit: U256, dest: &H160, - value: BalanceOf, + value: U256, input_data: Vec, allows_reentry: bool, read_only: bool, @@ -1277,9 +1280,9 @@ where }); let executable = self.push_frame( FrameArgs::Call { dest, cached_info, delegated_call: None }, - value, + value.try_into().map_err(|_| Error::::BalanceConversionFailed)?, gas_limit, - deposit_limit, + deposit_limit.try_into().map_err(|_| Error::::BalanceConversionFailed)?, // Enable read-only access if requested; cannot disable it if already set. read_only || self.is_read_only(), )?; @@ -1322,9 +1325,9 @@ where fn instantiate( &mut self, gas_limit: Weight, - deposit_limit: BalanceOf, + deposit_limit: U256, code_hash: H256, - value: BalanceOf, + value: U256, input_data: Vec, salt: Option<&[u8; 32]>, ) -> Result<(H160, ExecReturnValue), ExecError> { @@ -1337,9 +1340,9 @@ where salt, input_data: input_data.as_ref(), }, - value, + value.try_into().map_err(|_| Error::::BalanceConversionFailed)?, gas_limit, - deposit_limit, + deposit_limit.try_into().map_err(|_| Error::::BalanceConversionFailed)?, self.is_read_only(), )?; let address = T::AddressMapper::to_address(&self.top_frame().account_id); @@ -1374,12 +1377,12 @@ where Ok(()) } - fn transfer(&mut self, to: &H160, value: BalanceOf) -> DispatchResult { + fn transfer(&mut self, to: &H160, value: U256) -> DispatchResult { Self::transfer( Preservation::Preserve, &self.top_frame().account_id, &T::AddressMapper::to_account_id(to), - value, + value.try_into().map_err(|_| Error::::BalanceConversionFailed)?, ) } @@ -1462,27 +1465,28 @@ where self.caller_is_origin() && self.origin == Origin::Root } - fn balance(&self) -> BalanceOf { + fn balance(&self) -> U256 { T::Currency::reducible_balance( &self.top_frame().account_id, Preservation::Preserve, Fortitude::Polite, ) + .into() } - fn value_transferred(&self) -> BalanceOf { - self.top_frame().value_transferred + fn value_transferred(&self) -> U256 { + self.top_frame().value_transferred.into() } - fn now(&self) -> &MomentOf { - &self.timestamp + fn now(&self) -> U256 { + self.timestamp.into() } - fn minimum_balance(&self) -> BalanceOf { - T::Currency::minimum_balance() + fn minimum_balance(&self) -> U256 { + T::Currency::minimum_balance().into() } - fn deposit_event(&mut self, topics: Vec, data: Vec) { + fn deposit_event(&mut self, topics: Vec, data: Vec) { Contracts::::deposit_indexed_event( topics, Event::ContractEmitted { @@ -1492,16 +1496,16 @@ where ); } - fn block_number(&self) -> BlockNumberFor { - self.block_number + fn block_number(&self) -> U256 { + self.block_number.into() } fn max_value_size(&self) -> u32 { limits::PAYLOAD_BYTES } - fn get_weight_price(&self, weight: Weight) -> BalanceOf { - T::WeightPrice::convert(weight) + fn get_weight_price(&self, weight: Weight) -> U256 { + T::WeightPrice::convert(weight).into() } fn gas_meter(&self) -> &GasMeter { @@ -1864,7 +1868,7 @@ mod tests { let value = 55; let success_ch = MockLoader::insert(Call, move |ctx, _| { - assert_eq!(ctx.ext.value_transferred(), value); + assert_eq!(ctx.ext.value_transferred(), U256::from(value)); Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }) }); @@ -1896,12 +1900,12 @@ mod tests { let value = 35; let success_ch = MockLoader::insert(Call, move |ctx, _| { - assert_eq!(ctx.ext.value_transferred(), value); + assert_eq!(ctx.ext.value_transferred(), U256::from(value)); Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }) }); let delegate_ch = MockLoader::insert(Call, move |ctx, _| { - assert_eq!(ctx.ext.value_transferred(), value); + assert_eq!(ctx.ext.value_transferred(), U256::from(value)); let _ = ctx.ext.delegate_call(success_ch, Vec::new())?; Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }) }); @@ -2112,9 +2116,9 @@ mod tests { // Try to call into yourself. let r = ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), &BOB_ADDR, - 0, + U256::zero(), vec![], true, false, @@ -2175,9 +2179,9 @@ mod tests { assert_matches!( ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), &CHARLIE_ADDR, - 0, + U256::zero(), vec![], true, false @@ -2316,9 +2320,9 @@ mod tests { // BOB calls CHARLIE ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), &CHARLIE_ADDR, - 0, + U256::zero(), vec![], true, false, @@ -2410,9 +2414,9 @@ mod tests { // BOB calls CHARLIE. ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), &CHARLIE_ADDR, - 0, + U256::zero(), vec![], true, false, @@ -2448,9 +2452,9 @@ mod tests { assert_matches!( ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), &CHARLIE_ADDR, - 0, + U256::zero(), vec![], true, false @@ -2621,9 +2625,9 @@ mod tests { .ext .instantiate( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), dummy_ch, - ::Currency::minimum_balance(), + ::Currency::minimum_balance().into(), vec![], Some(&[48; 32]), ) @@ -2699,9 +2703,9 @@ mod tests { assert_matches!( ctx.ext.instantiate( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), dummy_ch, - ::Currency::minimum_balance(), + ::Currency::minimum_balance().into(), vec![], Some(&[0; 32]), ), @@ -2804,9 +2808,9 @@ mod tests { assert_eq!( ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), &CHARLIE_ADDR, - 0, + U256::zero(), vec![], true, false @@ -2820,15 +2824,7 @@ mod tests { let code_charlie = MockLoader::insert(Call, |ctx, _| { assert!(ctx .ext - .call( - Weight::zero(), - BalanceOf::::zero(), - &BOB_ADDR, - 0, - vec![99], - true, - false - ) + .call(Weight::zero(), U256::zero(), &BOB_ADDR, U256::zero(), vec![99], true, false) .is_ok()); exec_trapped() }); @@ -2860,7 +2856,7 @@ mod tests { let addr = ::AddressMapper::to_address(&account_id); assert_matches!( - ctx.ext.call(Weight::zero(), BalanceOf::::zero(), &addr, 0, vec![], + ctx.ext.call(Weight::zero(), U256::zero(), &addr, U256::zero(), vec![], true, false), Err(ExecError{error, ..}) if error == >::ContractNotFound.into() ); exec_success() @@ -2998,7 +2994,7 @@ mod tests { let code_bob = MockLoader::insert(Call, |ctx, _| { let dest = H160::from_slice(ctx.input_data.as_ref()); ctx.ext - .call(Weight::zero(), BalanceOf::::zero(), &dest, 0, vec![], false, false) + .call(Weight::zero(), U256::zero(), &dest, U256::zero(), vec![], false, false) }); let code_charlie = MockLoader::insert(Call, |_, _| exec_success()); @@ -3043,9 +3039,9 @@ mod tests { if ctx.input_data[0] == 0 { ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), &CHARLIE_ADDR, - 0, + U256::zero(), vec![], false, false, @@ -3059,9 +3055,9 @@ mod tests { let code_charlie = MockLoader::insert(Call, |ctx, _| { ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), &BOB_ADDR, - 0, + U256::zero(), vec![1], true, false, @@ -3251,7 +3247,7 @@ mod tests { ctx.ext .instantiate( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), fail_code, ctx.ext.minimum_balance() * 100, vec![], @@ -3268,7 +3264,7 @@ mod tests { .ext .instantiate( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), success_code, ctx.ext.minimum_balance() * 100, vec![], @@ -3284,7 +3280,7 @@ mod tests { // a plain call should not influence the account counter ctx.ext - .call(Weight::zero(), BalanceOf::::zero(), &addr, 0, vec![], false, false) + .call(Weight::zero(), U256::zero(), &addr, U256::zero(), vec![], false, false) .unwrap(); assert_eq!(System::account_nonce(ALICE), alice_nonce); @@ -3822,9 +3818,9 @@ mod tests { assert_eq!( ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), &CHARLIE_ADDR, - 0, + U256::zero(), vec![], true, false, @@ -3849,15 +3845,7 @@ mod tests { let code_charlie = MockLoader::insert(Call, |ctx, _| { assert!(ctx .ext - .call( - Weight::zero(), - BalanceOf::::zero(), - &BOB_ADDR, - 0, - vec![99], - true, - false - ) + .call(Weight::zero(), U256::zero(), &BOB_ADDR, U256::zero(), vec![99], true, false) .is_ok()); // CHARLIE can not read BOB`s storage. assert_eq!(ctx.ext.get_transient_storage(storage_key_1), None); @@ -3934,9 +3922,9 @@ mod tests { assert_eq!( ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), &CHARLIE_ADDR, - 0, + U256::zero(), vec![], true, false @@ -3957,15 +3945,7 @@ mod tests { let code_charlie = MockLoader::insert(Call, |ctx, _| { assert!(ctx .ext - .call( - Weight::zero(), - BalanceOf::::zero(), - &BOB_ADDR, - 0, - vec![99], - true, - false - ) + .call(Weight::zero(), U256::zero(), &BOB_ADDR, U256::zero(), vec![99], true, false) .is_ok()); exec_trapped() }); diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 393acc8c9852..4c6e5cd26a11 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -27,7 +27,10 @@ mod benchmarking_dummy; mod exec; mod gas; mod primitives; +use crate::exec::MomentOf; +use frame_support::traits::IsType; pub use primitives::*; +use sp_core::U256; mod limits; mod storage; @@ -36,7 +39,6 @@ mod wasm; pub mod chain_extension; pub mod debug; -pub mod migration; pub mod test_utils; pub mod weights; @@ -54,7 +56,7 @@ use environmental::*; use frame_support::{ dispatch::{ DispatchErrorWithPostInfo, DispatchResultWithPostInfo, GetDispatchInfo, Pays, - PostDispatchInfo, RawOrigin, WithPostDispatchInfo, + PostDispatchInfo, RawOrigin, }, ensure, traits::{ @@ -79,7 +81,6 @@ use sp_runtime::{ pub use crate::{ address::{AddressMapper, DefaultAddressMapper}, debug::Tracing, - migration::{MigrateSequence, Migration, NoopMigration}, pallet::*, }; pub use weights::WeightInfo; @@ -129,6 +130,7 @@ pub mod pallet { use crate::debug::Debugger; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + use sp_core::U256; use sp_runtime::Perbill; /// The in-code storage version. @@ -206,7 +208,7 @@ pub mod pallet { /// /// # Note /// - /// It is safe to chage this value on a live chain as all refunds are pro rata. + /// It is safe to change this value on a live chain as all refunds are pro rata. #[pallet::constant] #[pallet::no_default_bounds] type DepositPerByte: Get>; @@ -215,7 +217,7 @@ pub mod pallet { /// /// # Note /// - /// It is safe to chage this value on a live chain as all refunds are pro rata. + /// It is safe to change this value on a live chain as all refunds are pro rata. #[pallet::constant] #[pallet::no_default_bounds] type DepositPerItem: Get>; @@ -271,25 +273,6 @@ pub mod pallet { #[pallet::no_default_bounds] type InstantiateOrigin: EnsureOrigin; - /// The sequence of migration steps that will be applied during a migration. - /// - /// # Examples - /// ```ignore - /// use pallet_revive::migration::{v10, v11}; - /// # struct Runtime {}; - /// # struct Currency {}; - /// type Migrations = (v10::Migration, v11::Migration); - /// ``` - /// - /// If you have a single migration step, you can use a tuple with a single element: - /// ```ignore - /// use pallet_revive::migration::v10; - /// # struct Runtime {}; - /// # struct Currency {}; - /// type Migrations = (v10::Migration,); - /// ``` - type Migrations: MigrateSequence; - /// For most production chains, it's recommended to use the `()` implementation of this /// trait. This implementation offers additional logging when the log target /// "runtime::revive" is set to trace. @@ -305,13 +288,13 @@ pub mod pallet { BlockNumberFor, >; - /// The amount of memory in bytes that parachain nodes alot to the runtime. + /// The amount of memory in bytes that parachain nodes a lot to the runtime. /// /// This is used in [`Pallet::integrity_test`] to make sure that the runtime has enough /// memory to support this pallet if set to the correct value. type RuntimeMemory: Get; - /// The amount of memory in bytes that relay chain validators alot to the PoV. + /// The amount of memory in bytes that relay chain validators a lot to the PoV. /// /// This is used in [`Pallet::integrity_test`] to make sure that the runtime has enough /// memory to support this pallet if set to the correct value. @@ -382,7 +365,6 @@ pub mod pallet { type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; type MaxCodeLen = ConstU32<{ 123 * 1024 }>; - type Migrations = (); type Time = Self; type UnsafeUnstableInterface = ConstBool; type UploadOrigin = EnsureSigned; @@ -549,10 +531,6 @@ pub mod pallet { /// A more detailed error can be found on the node console if debug messages are enabled /// by supplying `-lruntime::revive=debug`. CodeRejected, - /// A pending migration needs to complete before the extrinsic can be called. - MigrationInProgress, - /// Migrate dispatch call was attempted but no migration was performed. - NoMigrationPerformed, /// The contract has reached its maximum number of delegate dependencies. MaxDelegateDependenciesReached, /// The dependency was not found in the contract's delegate dependencies. @@ -569,6 +547,8 @@ pub mod pallet { InvalidStorageFlags, /// PolkaVM failed during code execution. Probably due to a malformed program. ExecutionFailed, + /// Failed to convert a U256 to a Balance. + BalanceConversionFailed, } /// A reason for the pallet contracts placing a hold on funds. @@ -605,12 +585,6 @@ pub mod pallet { pub(crate) type DeletionQueueCounter = StorageValue<_, DeletionQueueManager, ValueQuery>; - /// A migration can span across multiple blocks. This storage defines a cursor to track the - /// progress of the migration, enabling us to resume from the last completed position. - #[pallet::storage] - pub(crate) type MigrationInProgress = - StorageValue<_, migration::Cursor, OptionQuery>; - #[pallet::extra_constants] impl Pallet { #[pallet::constant_name(ApiVersion)] @@ -620,31 +594,17 @@ pub mod pallet { } #[pallet::hooks] - impl Hooks> for Pallet { + impl Hooks> for Pallet + where + T::Hash: IsType, + { fn on_idle(_block: BlockNumberFor, limit: Weight) -> Weight { - use migration::MigrateResult::*; let mut meter = WeightMeter::with_limit(limit); - - loop { - match Migration::::migrate(&mut meter) { - // There is not enough weight to perform a migration. - // We can't do anything more, so we return the used weight. - NoMigrationPerformed | InProgress { steps_done: 0 } => return meter.consumed(), - // Migration is still in progress, we can start the next step. - InProgress { .. } => continue, - // Either no migration is in progress, or we are done with all migrations, we - // can do some more other work with the remaining weight. - Completed | NoMigrationInProgress => break, - } - } - ContractInfo::::process_deletion_queue_batch(&mut meter); meter.consumed() } fn integrity_test() { - Migration::::integrity_test(); - // Total runtime memory limit let max_runtime_mem: u32 = T::RuntimeMemory::get(); // Memory limits for a single contract: @@ -771,7 +731,10 @@ pub mod pallet { #[pallet::call] impl Pallet where + T::Hash: IsType, as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + BalanceOf: Into + TryFrom, + MomentOf: Into, { /// Makes a call to an account, optionally transferring some balance. /// @@ -957,7 +920,6 @@ pub mod pallet { origin: OriginFor, code_hash: sp_core::H256, ) -> DispatchResultWithPostInfo { - Migration::::ensure_migrated()?; let origin = ensure_signed(origin)?; >::remove(&origin, code_hash)?; // we waive the fee because removing unused code is beneficial @@ -981,7 +943,6 @@ pub mod pallet { dest: H160, code_hash: sp_core::H256, ) -> DispatchResult { - Migration::::ensure_migrated()?; ensure_root(origin)?; >::try_mutate(&dest, |contract| { let contract = if let Some(contract) = contract { @@ -1000,40 +961,6 @@ pub mod pallet { Ok(()) }) } - - /// When a migration is in progress, this dispatchable can be used to run migration steps. - /// Calls that contribute to advancing the migration have their fees waived, as it's helpful - /// for the chain. Note that while the migration is in progress, the pallet will also - /// leverage the `on_idle` hooks to run migration steps. - #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::migrate().saturating_add(*weight_limit))] - pub fn migrate(origin: OriginFor, weight_limit: Weight) -> DispatchResultWithPostInfo { - use migration::MigrateResult::*; - ensure_signed(origin)?; - - let weight_limit = weight_limit.saturating_add(T::WeightInfo::migrate()); - let mut meter = WeightMeter::with_limit(weight_limit); - let result = Migration::::migrate(&mut meter); - - match result { - Completed => Ok(PostDispatchInfo { - actual_weight: Some(meter.consumed()), - pays_fee: Pays::No, - }), - InProgress { steps_done, .. } if steps_done > 0 => Ok(PostDispatchInfo { - actual_weight: Some(meter.consumed()), - pays_fee: Pays::No, - }), - InProgress { .. } => Ok(PostDispatchInfo { - actual_weight: Some(meter.consumed()), - pays_fee: Pays::Yes, - }), - NoMigrationInProgress | NoMigrationPerformed => { - let err: DispatchError = >::NoMigrationPerformed.into(); - Err(err.with_weight(meter.consumed())) - }, - } - } } } @@ -1053,7 +980,12 @@ fn dispatch_result( .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e }) } -impl Pallet { +impl Pallet +where + BalanceOf: Into + TryFrom, + MomentOf: Into, + T::Hash: IsType, +{ /// A generalized version of [`Self::call`]. /// /// Identical to [`Self::call`] but tailored towards being called by other code within the @@ -1078,7 +1010,6 @@ impl Pallet { None }; let try_call = || { - Migration::::ensure_migrated()?; let origin = Origin::from_runtime_origin(origin)?; let mut storage_meter = StorageMeter::new(&origin, storage_deposit_limit, value)?; let result = ExecStack::>::run_call( @@ -1131,7 +1062,6 @@ impl Pallet { let mut debug_message = if debug == DebugInfo::UnsafeDebug { Some(DebugBuffer::default()) } else { None }; let try_instantiate = || { - Migration::::ensure_migrated()?; let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?; let (executable, upload_deposit) = match code { Code::Upload(code) => { @@ -1192,7 +1122,6 @@ impl Pallet { code: Vec, storage_deposit_limit: BalanceOf, ) -> CodeUploadResult> { - Migration::::ensure_migrated()?; let origin = T::UploadOrigin::ensure_origin(origin)?; let (module, deposit) = Self::try_upload_code(origin, code, storage_deposit_limit, None)?; Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit }) @@ -1200,9 +1129,6 @@ impl Pallet { /// Query storage of a specified contract under a specified key. pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult { - if Migration::::in_progress() { - return Err(ContractAccessError::MigrationInProgress) - } let contract_info = ContractInfoOf::::get(&address).ok_or(ContractAccessError::DoesntExist)?; @@ -1226,24 +1152,6 @@ impl Pallet { Ok((module, deposit)) } - /// Deposit a pallet contracts event. - fn deposit_event(event: Event) { - >::deposit_event(::RuntimeEvent::from(event)) - } - - /// Deposit a pallet contracts indexed event. - fn deposit_indexed_event(topics: Vec, event: Event) { - >::deposit_event_indexed( - &topics, - ::RuntimeEvent::from(event).into(), - ) - } - - /// Return the existential deposit of [`Config::Currency`]. - fn min_balance() -> BalanceOf { - >>::minimum_balance() - } - /// Run the supplied function `f` if no other instance of this pallet is on the stack. fn run_guarded Result>(f: F) -> Result { executing_contract::using_once(&mut false, || { @@ -1264,6 +1172,30 @@ impl Pallet { } } +impl Pallet +where + T: Config, + T::Hash: IsType, +{ + /// Return the existential deposit of [`Config::Currency`]. + fn min_balance() -> BalanceOf { + >>::minimum_balance() + } + + /// Deposit a pallet contracts event. + fn deposit_event(event: Event) { + >::deposit_event(::RuntimeEvent::from(event)) + } + + /// Deposit a pallet contracts indexed event. + fn deposit_indexed_event(topics: Vec, event: Event) { + >::deposit_event_indexed( + &topics.into_iter().map(Into::into).collect::>(), + ::RuntimeEvent::from(event).into(), + ) + } +} + // Set up a global reference to the boolean flag used for the re-entrancy guard. environmental!(executing_contract: bool); diff --git a/substrate/frame/revive/src/migration.rs b/substrate/frame/revive/src/migration.rs deleted file mode 100644 index b67467b322f5..000000000000 --- a/substrate/frame/revive/src/migration.rs +++ /dev/null @@ -1,650 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Multi-block Migration framework for pallet-revive. -//! -//! This module allows us to define a migration as a sequence of [`MigrationStep`]s that can be -//! executed across multiple blocks. -//! -//! # Usage -//! -//! A migration step is defined under `src/migration/vX.rs`, where `X` is the version number. -//! For example, `vX.rs` defines a migration from version `X - 1` to version `X`. -//! -//! ## Example: -//! -//! To configure a migration to `v11` for a runtime using `v10` of pallet-revive on the chain, -//! you would set the `Migrations` type as follows: -//! -//! ```ignore -//! use pallet_revive::migration::{v10, v11}; -//! # pub enum Runtime {}; -//! # struct Currency; -//! type Migrations = (v10::Migration, v11::Migration); -//! ``` -//! -//! ## Notes: -//! -//! - Migrations should always be tested with `try-runtime` before being deployed. -//! - By testing with `try-runtime` against a live network, you ensure that all migration steps work -//! and that you have included the required steps. -//! -//! ## Low Level / Implementation Details -//! -//! When a migration starts and [`OnRuntimeUpgrade::on_runtime_upgrade`] is called, instead of -//! performing the actual migration, we set a custom storage item [`MigrationInProgress`]. -//! This storage item defines a [`Cursor`] for the current migration. -//! -//! If the [`MigrationInProgress`] storage item exists, it means a migration is in progress, and its -//! value holds a cursor for the current migration step. These migration steps are executed during -//! [`Hooks::on_idle`] or when the [`Pallet::migrate`] dispatchable is -//! called. -//! -//! While the migration is in progress, all dispatchables except `migrate`, are blocked, and returns -//! a `MigrationInProgress` error. - -include!(concat!(env!("OUT_DIR"), "/migration_codegen.rs")); - -use crate::{weights::WeightInfo, Config, Error, MigrationInProgress, Pallet, Weight, LOG_TARGET}; -use codec::{Codec, Decode}; -use core::marker::PhantomData; -use frame_support::{ - pallet_prelude::*, - traits::{ConstU32, OnRuntimeUpgrade}, - weights::WeightMeter, -}; -use sp_runtime::Saturating; - -#[cfg(feature = "try-runtime")] -use alloc::vec::Vec; -#[cfg(feature = "try-runtime")] -use sp_runtime::TryRuntimeError; - -const PROOF_ENCODE: &str = "Tuple::max_encoded_len() < Cursor::max_encoded_len()` is verified in `Self::integrity_test()`; qed"; -const PROOF_DECODE: &str = - "We encode to the same type in this trait only. No other code touches this item; qed"; - -fn invalid_version(version: StorageVersion) -> ! { - panic!("Required migration {version:?} not supported by this runtime. This is a bug."); -} - -/// The cursor used to encode the position (usually the last iterated key) of the current migration -/// step. -pub type Cursor = BoundedVec>; - -/// IsFinished describes whether a migration is finished or not. -pub enum IsFinished { - Yes, - No, -} - -/// A trait that allows to migrate storage from one version to another. -/// -/// The migration is done in steps. The migration is finished when -/// `step()` returns `IsFinished::Yes`. -pub trait MigrationStep: Codec + MaxEncodedLen + Default { - /// Returns the version of the migration. - const VERSION: u16; - - /// Returns the maximum weight that can be consumed in a single step. - fn max_step_weight() -> Weight; - - /// Process one step of the migration. - /// - /// Returns whether the migration is finished. - fn step(&mut self, meter: &mut WeightMeter) -> IsFinished; - - /// Verify that the migration step fits into `Cursor`, and that `max_step_weight` is not greater - /// than `max_block_weight`. - fn integrity_test(max_block_weight: Weight) { - if Self::max_step_weight().any_gt(max_block_weight) { - panic!( - "Invalid max_step_weight for Migration {}. Value should be lower than {}", - Self::VERSION, - max_block_weight - ); - } - - let len = ::max_encoded_len(); - let max = Cursor::bound(); - if len > max { - panic!( - "Migration {} has size {} which is bigger than the maximum of {}", - Self::VERSION, - len, - max, - ); - } - } - - /// Execute some pre-checks prior to running the first step of this migration. - #[cfg(feature = "try-runtime")] - fn pre_upgrade_step() -> Result, TryRuntimeError> { - Ok(Vec::new()) - } - - /// Execute some post-checks after running the last step of this migration. - #[cfg(feature = "try-runtime")] - fn post_upgrade_step(_state: Vec) -> Result<(), TryRuntimeError> { - Ok(()) - } -} - -/// A noop migration that can be used when there is no migration to be done for a given version. -#[doc(hidden)] -#[derive(frame_support::DefaultNoBound, Encode, Decode, MaxEncodedLen)] -pub struct NoopMigration; - -impl MigrationStep for NoopMigration { - const VERSION: u16 = N; - fn max_step_weight() -> Weight { - Weight::zero() - } - fn step(&mut self, _meter: &mut WeightMeter) -> IsFinished { - log::debug!(target: LOG_TARGET, "Noop migration for version {}", N); - IsFinished::Yes - } -} - -mod private { - use crate::migration::MigrationStep; - pub trait Sealed {} - #[impl_trait_for_tuples::impl_for_tuples(10)] - #[tuple_types_custom_trait_bound(MigrationStep)] - impl Sealed for Tuple {} -} - -/// Defines a sequence of migrations. -/// -/// The sequence must be defined by a tuple of migrations, each of which must implement the -/// `MigrationStep` trait. Migrations must be ordered by their versions with no gaps. -pub trait MigrateSequence: private::Sealed { - /// Returns the range of versions that this migrations sequence can handle. - /// Migrations must be ordered by their versions with no gaps. - /// - /// The following code will fail to compile: - /// - /// ```compile_fail - /// # use pallet_revive::{NoopMigration, MigrateSequence}; - /// let _ = <(NoopMigration<1>, NoopMigration<3>)>::VERSION_RANGE; - /// ``` - /// The following code will compile: - /// ``` - /// # use pallet_revive::{NoopMigration, MigrateSequence}; - /// let _ = <(NoopMigration<1>, NoopMigration<2>)>::VERSION_RANGE; - /// ``` - const VERSION_RANGE: (u16, u16); - - /// Returns the default cursor for the given version. - fn new(version: StorageVersion) -> Cursor; - - #[cfg(feature = "try-runtime")] - fn pre_upgrade_step(_version: StorageVersion) -> Result, TryRuntimeError> { - Ok(Vec::new()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade_step(_version: StorageVersion, _state: Vec) -> Result<(), TryRuntimeError> { - Ok(()) - } - - /// Execute the migration step until the available weight is consumed. - fn steps(version: StorageVersion, cursor: &[u8], meter: &mut WeightMeter) -> StepResult; - - /// Verify that the migration step fits into `Cursor`, and that `max_step_weight` is not greater - /// than `max_block_weight`. - fn integrity_test(max_block_weight: Weight); - - /// Returns whether migrating from `in_storage` to `target` is supported. - /// - /// A migration is supported if `VERSION_RANGE` is (in_storage + 1, target). - fn is_upgrade_supported(in_storage: StorageVersion, target: StorageVersion) -> bool { - let (low, high) = Self::VERSION_RANGE; - target == high && in_storage + 1 == low - } -} - -/// Performs all necessary migrations based on `StorageVersion`. -/// -/// If `TEST_ALL_STEPS == true` and `try-runtime` is enabled, this will run all the migrations -/// inside `on_runtime_upgrade`. This should be set to false in tests that want to ensure the step -/// by step migration works. -pub struct Migration(PhantomData); - -#[cfg(feature = "try-runtime")] -impl Migration { - fn run_all_steps() -> Result<(), TryRuntimeError> { - let mut meter = &mut WeightMeter::new(); - let name = >::name(); - loop { - let in_progress_version = >::on_chain_storage_version() + 1; - let state = T::Migrations::pre_upgrade_step(in_progress_version)?; - let before = meter.consumed(); - let status = Self::migrate(&mut meter); - log::info!( - target: LOG_TARGET, - "{name}: Migration step {:?} weight = {}", - in_progress_version, - meter.consumed() - before - ); - T::Migrations::post_upgrade_step(in_progress_version, state)?; - if matches!(status, MigrateResult::Completed) { - break - } - } - - let name = >::name(); - log::info!(target: LOG_TARGET, "{name}: Migration steps weight = {}", meter.consumed()); - Ok(()) - } -} - -impl OnRuntimeUpgrade for Migration { - fn on_runtime_upgrade() -> Weight { - let name = >::name(); - let in_code_version = >::in_code_storage_version(); - let on_chain_version = >::on_chain_storage_version(); - - if on_chain_version == in_code_version { - log::warn!( - target: LOG_TARGET, - "{name}: No Migration performed storage_version = latest_version = {:?}", - &on_chain_version - ); - return T::WeightInfo::on_runtime_upgrade_noop() - } - - // In case a migration is already in progress we create the next migration - // (if any) right when the current one finishes. - if Self::in_progress() { - log::warn!( - target: LOG_TARGET, - "{name}: Migration already in progress {:?}", - &on_chain_version - ); - - return T::WeightInfo::on_runtime_upgrade_in_progress() - } - - log::info!( - target: LOG_TARGET, - "{name}: Upgrading storage from {on_chain_version:?} to {in_code_version:?}.", - ); - - let cursor = T::Migrations::new(on_chain_version + 1); - MigrationInProgress::::set(Some(cursor)); - - #[cfg(feature = "try-runtime")] - if TEST_ALL_STEPS { - Self::run_all_steps().unwrap(); - } - - T::WeightInfo::on_runtime_upgrade() - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, TryRuntimeError> { - // We can't really do much here as our migrations do not happen during the runtime upgrade. - // Instead, we call the migrations `pre_upgrade` and `post_upgrade` hooks when we iterate - // over our migrations. - let on_chain_version = >::on_chain_storage_version(); - let in_code_version = >::in_code_storage_version(); - - if on_chain_version == in_code_version { - return Ok(Default::default()) - } - - log::debug!( - target: LOG_TARGET, - "Requested migration of {} from {:?}(on-chain storage version) to {:?}(in-code storage version)", - >::name(), on_chain_version, in_code_version - ); - - ensure!( - T::Migrations::is_upgrade_supported(on_chain_version, in_code_version), - "Unsupported upgrade: VERSION_RANGE should be (on-chain storage version + 1, in-code storage version)" - ); - - Ok(Default::default()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { - if !TEST_ALL_STEPS { - return Ok(()) - } - - log::info!(target: LOG_TARGET, "=== POST UPGRADE CHECKS ==="); - - // Ensure that the hashing algorithm is correct for each storage map. - if let Some(hash) = crate::CodeInfoOf::::iter_keys().next() { - crate::CodeInfoOf::::get(hash).expect("CodeInfo exists for hash; qed"); - } - if let Some(hash) = crate::PristineCode::::iter_keys().next() { - crate::PristineCode::::get(hash).expect("PristineCode exists for hash; qed"); - } - if let Some(account_id) = crate::ContractInfoOf::::iter_keys().next() { - crate::ContractInfoOf::::get(account_id) - .expect("ContractInfo exists for account_id; qed"); - } - if let Some(nonce) = crate::DeletionQueue::::iter_keys().next() { - crate::DeletionQueue::::get(nonce).expect("DeletionQueue exists for nonce; qed"); - } - - Ok(()) - } -} - -/// The result of running the migration. -#[derive(Debug, PartialEq)] -pub enum MigrateResult { - /// No migration was performed - NoMigrationPerformed, - /// No migration currently in progress - NoMigrationInProgress, - /// A migration is in progress - InProgress { steps_done: u32 }, - /// All migrations are completed - Completed, -} - -/// The result of running a migration step. -#[derive(Debug, PartialEq)] -pub enum StepResult { - InProgress { cursor: Cursor, steps_done: u32 }, - Completed { steps_done: u32 }, -} - -impl Migration { - /// Verify that each migration's step of the [`Config::Migrations`] sequence fits into - /// `Cursor`. - pub(crate) fn integrity_test() { - let max_weight = ::BlockWeights::get().max_block; - T::Migrations::integrity_test(max_weight) - } - - /// Execute the multi-step migration. - /// Returns whether or not a migration is in progress - pub(crate) fn migrate(mut meter: &mut WeightMeter) -> MigrateResult { - let name = >::name(); - - if meter.try_consume(T::WeightInfo::migrate()).is_err() { - return MigrateResult::NoMigrationPerformed - } - - MigrationInProgress::::mutate_exists(|progress| { - let Some(cursor_before) = progress.as_mut() else { - meter.consume(T::WeightInfo::migration_noop()); - return MigrateResult::NoMigrationInProgress - }; - - // if a migration is running it is always upgrading to the next version - let storage_version = >::on_chain_storage_version(); - let in_progress_version = storage_version + 1; - - log::info!( - target: LOG_TARGET, - "{name}: Migrating from {:?} to {:?},", - storage_version, - in_progress_version, - ); - - let result = - match T::Migrations::steps(in_progress_version, cursor_before.as_ref(), &mut meter) - { - StepResult::InProgress { cursor, steps_done } => { - *progress = Some(cursor); - MigrateResult::InProgress { steps_done } - }, - StepResult::Completed { steps_done } => { - in_progress_version.put::>(); - if >::in_code_storage_version() != in_progress_version { - log::info!( - target: LOG_TARGET, - "{name}: Next migration is {:?},", - in_progress_version + 1 - ); - *progress = Some(T::Migrations::new(in_progress_version + 1)); - MigrateResult::InProgress { steps_done } - } else { - log::info!( - target: LOG_TARGET, - "{name}: All migrations done. At version {:?},", - in_progress_version - ); - *progress = None; - MigrateResult::Completed - } - }, - }; - - result - }) - } - - pub(crate) fn ensure_migrated() -> DispatchResult { - if Self::in_progress() { - Err(Error::::MigrationInProgress.into()) - } else { - Ok(()) - } - } - - pub(crate) fn in_progress() -> bool { - MigrationInProgress::::exists() - } -} - -#[impl_trait_for_tuples::impl_for_tuples(10)] -#[tuple_types_custom_trait_bound(MigrationStep)] -impl MigrateSequence for Tuple { - const VERSION_RANGE: (u16, u16) = { - let mut versions: (u16, u16) = (0, 0); - for_tuples!( - #( - match versions { - (0, 0) => { - versions = (Tuple::VERSION, Tuple::VERSION); - }, - (min_version, last_version) if Tuple::VERSION == last_version + 1 => { - versions = (min_version, Tuple::VERSION); - }, - _ => panic!("Migrations must be ordered by their versions with no gaps.") - } - )* - ); - versions - }; - - fn new(version: StorageVersion) -> Cursor { - for_tuples!( - #( - if version == Tuple::VERSION { - return Tuple::default().encode().try_into().expect(PROOF_ENCODE) - } - )* - ); - invalid_version(version) - } - - #[cfg(feature = "try-runtime")] - /// Execute the pre-checks of the step associated with this version. - fn pre_upgrade_step(version: StorageVersion) -> Result, TryRuntimeError> { - for_tuples!( - #( - if version == Tuple::VERSION { - return Tuple::pre_upgrade_step() - } - )* - ); - invalid_version(version) - } - - #[cfg(feature = "try-runtime")] - /// Execute the post-checks of the step associated with this version. - fn post_upgrade_step(version: StorageVersion, state: Vec) -> Result<(), TryRuntimeError> { - for_tuples!( - #( - if version == Tuple::VERSION { - return Tuple::post_upgrade_step(state) - } - )* - ); - invalid_version(version) - } - - fn steps(version: StorageVersion, mut cursor: &[u8], meter: &mut WeightMeter) -> StepResult { - for_tuples!( - #( - if version == Tuple::VERSION { - let mut migration = ::decode(&mut cursor) - .expect(PROOF_DECODE); - let max_weight = Tuple::max_step_weight(); - let mut steps_done = 0; - while meter.can_consume(max_weight) { - steps_done.saturating_accrue(1); - if matches!(migration.step(meter), IsFinished::Yes) { - return StepResult::Completed{ steps_done } - } - } - return StepResult::InProgress{cursor: migration.encode().try_into().expect(PROOF_ENCODE), steps_done } - } - )* - ); - invalid_version(version) - } - - fn integrity_test(max_block_weight: Weight) { - for_tuples!( - #( - Tuple::integrity_test(max_block_weight); - )* - ); - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{ - migration::codegen::LATEST_MIGRATION_VERSION, - tests::{ExtBuilder, Test}, - }; - - #[derive(Default, Encode, Decode, MaxEncodedLen)] - struct MockMigration { - // MockMigration needs `N` steps to finish - count: u16, - } - - impl MigrationStep for MockMigration { - const VERSION: u16 = N; - fn max_step_weight() -> Weight { - Weight::from_all(1) - } - fn step(&mut self, meter: &mut WeightMeter) -> IsFinished { - assert!(self.count != N); - self.count += 1; - meter.consume(Weight::from_all(1)); - if self.count == N { - IsFinished::Yes - } else { - IsFinished::No - } - } - } - - #[test] - fn test_storage_version_matches_last_migration_file() { - assert_eq!(StorageVersion::new(LATEST_MIGRATION_VERSION), crate::pallet::STORAGE_VERSION); - } - - #[test] - fn version_range_works() { - let range = <(MockMigration<1>, MockMigration<2>)>::VERSION_RANGE; - assert_eq!(range, (1, 2)); - } - - #[test] - fn is_upgrade_supported_works() { - type Migrations = (MockMigration<9>, MockMigration<10>, MockMigration<11>); - assert!(Migrations::is_upgrade_supported(StorageVersion::new(8), StorageVersion::new(11))); - assert!(!Migrations::is_upgrade_supported(StorageVersion::new(9), StorageVersion::new(11))); - assert!(!Migrations::is_upgrade_supported(StorageVersion::new(8), StorageVersion::new(12))); - } - - #[test] - fn steps_works() { - type Migrations = (MockMigration<2>, MockMigration<3>); - let version = StorageVersion::new(2); - let mut cursor = Migrations::new(version); - - let mut meter = WeightMeter::with_limit(Weight::from_all(1)); - let result = Migrations::steps(version, &cursor, &mut meter); - cursor = alloc::vec![1u8, 0].try_into().unwrap(); - assert_eq!(result, StepResult::InProgress { cursor: cursor.clone(), steps_done: 1 }); - assert_eq!(meter.consumed(), Weight::from_all(1)); - - let mut meter = WeightMeter::with_limit(Weight::from_all(1)); - assert_eq!( - Migrations::steps(version, &cursor, &mut meter), - StepResult::Completed { steps_done: 1 } - ); - } - - #[test] - fn no_migration_in_progress_works() { - type TestMigration = Migration; - - ExtBuilder::default().build().execute_with(|| { - assert_eq!(StorageVersion::get::>(), LATEST_MIGRATION_VERSION); - assert_eq!( - TestMigration::migrate(&mut WeightMeter::new()), - MigrateResult::NoMigrationInProgress - ) - }); - } - - #[test] - fn migration_works() { - type TestMigration = Migration; - - ExtBuilder::default() - .set_storage_version(LATEST_MIGRATION_VERSION - 2) - .build() - .execute_with(|| { - assert_eq!(StorageVersion::get::>(), LATEST_MIGRATION_VERSION - 2); - TestMigration::on_runtime_upgrade(); - for (version, status) in [ - (LATEST_MIGRATION_VERSION - 1, MigrateResult::InProgress { steps_done: 1 }), - (LATEST_MIGRATION_VERSION, MigrateResult::Completed), - ] { - assert_eq!(TestMigration::migrate(&mut WeightMeter::new()), status); - assert_eq!( - >::on_chain_storage_version(), - StorageVersion::new(version) - ); - } - - assert_eq!( - TestMigration::migrate(&mut WeightMeter::new()), - MigrateResult::NoMigrationInProgress - ); - assert_eq!(StorageVersion::get::>(), LATEST_MIGRATION_VERSION); - }); - } -} diff --git a/substrate/frame/revive/src/primitives.rs b/substrate/frame/revive/src/primitives.rs index 98e8879457bf..1b48527d23d7 100644 --- a/substrate/frame/revive/src/primitives.rs +++ b/substrate/frame/revive/src/primitives.rs @@ -103,8 +103,6 @@ pub enum ContractAccessError { DoesntExist, /// Storage key cannot be decoded from the provided input data. KeyDecodingFailed, - /// Storage is migrating. Try again later. - MigrationInProgress, } /// Output of a contract call or instantiation which ran to completion. diff --git a/substrate/frame/revive/src/storage.rs b/substrate/frame/revive/src/storage.rs index ef7ce2db32cf..9939de1dfd19 100644 --- a/substrate/frame/revive/src/storage.rs +++ b/substrate/frame/revive/src/storage.rs @@ -33,11 +33,12 @@ use codec::{Decode, Encode, MaxEncodedLen}; use core::marker::PhantomData; use frame_support::{ storage::child::{self, ChildInfo}, + traits::IsType, weights::{Weight, WeightMeter}, CloneNoBound, DefaultNoBound, }; use scale_info::TypeInfo; -use sp_core::{ConstU32, Get, H160}; +use sp_core::{ConstU32, Get, H160, H256}; use sp_io::KillStorageResult; use sp_runtime::{ traits::{Hash, Saturating, Zero}, @@ -77,7 +78,10 @@ pub struct ContractInfo { delegate_dependencies: DelegateDependencyMap, } -impl ContractInfo { +impl ContractInfo +where + T::Hash: IsType, +{ /// Constructs a new contract info **without** writing it to storage. /// /// This returns an `Err` if an contract with the supplied `account` already exists diff --git a/substrate/frame/revive/src/storage/meter.rs b/substrate/frame/revive/src/storage/meter.rs index f6ad4c5fc346..9d70ddf85870 100644 --- a/substrate/frame/revive/src/storage/meter.rs +++ b/substrate/frame/revive/src/storage/meter.rs @@ -21,17 +21,17 @@ use crate::{ address::AddressMapper, storage::ContractInfo, AccountIdOf, BalanceOf, CodeInfo, Config, Error, Event, HoldReason, Inspect, Origin, Pallet, StorageDeposit as Deposit, System, LOG_TARGET, }; - use alloc::vec::Vec; use core::{fmt::Debug, marker::PhantomData}; use frame_support::{ traits::{ fungible::{Mutate, MutateHold}, tokens::{Fortitude, Fortitude::Polite, Precision, Preservation, Restriction}, - Get, + Get, IsType, }, DefaultNoBound, RuntimeDebugNoBound, }; +use sp_core::H256; use sp_runtime::{ traits::{Saturating, Zero}, DispatchError, FixedPointNumber, FixedU128, @@ -400,6 +400,7 @@ where impl RawMeter where T: Config, + T::Hash: IsType, E: Ext, { /// Charges `diff` from the meter. @@ -503,7 +504,10 @@ where } } -impl Ext for ReservingExt { +impl Ext for ReservingExt +where + T::Hash: IsType, +{ fn check_limit( origin: &T::AccountId, limit: BalanceOf, diff --git a/substrate/frame/revive/src/test_utils/builder.rs b/substrate/frame/revive/src/test_utils/builder.rs index b17067769c05..b17d7628fb80 100644 --- a/substrate/frame/revive/src/test_utils/builder.rs +++ b/substrate/frame/revive/src/test_utils/builder.rs @@ -54,6 +54,9 @@ macro_rules! builder { impl $name where as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + BalanceOf: Into + TryFrom, + crate::MomentOf: Into, + T::Hash: frame_support::traits::IsType, { $( #[doc = concat!("Set the ", stringify!($field))] diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 447d55f0dd8d..f2944c7932a6 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -33,7 +33,6 @@ use crate::{ }, exec::Key, limits, - migration::codegen::LATEST_MIGRATION_VERSION, primitives::CodeUploadReturnValue, storage::DeletionQueueManager, test_utils::*, @@ -41,8 +40,8 @@ use crate::{ wasm::Memory, weights::WeightInfo, BalanceOf, Code, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf, DebugInfo, - DefaultAddressMapper, DeletionQueueCounter, Error, HoldReason, MigrationInProgress, Origin, - Pallet, PristineCode, H160, + DefaultAddressMapper, DeletionQueueCounter, Error, HoldReason, Origin, Pallet, PristineCode, + H160, }; use crate::test_utils::builder::Contract; @@ -490,7 +489,6 @@ impl Config for Test { type UnsafeUnstableInterface = UnstableInterface; type UploadOrigin = EnsureAccount; type InstantiateOrigin = EnsureAccount; - type Migrations = crate::migration::codegen::BenchMigrations; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type Debug = TestDebug; } @@ -523,10 +521,6 @@ impl ExtBuilder { pub fn set_associated_consts(&self) { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); } - pub fn set_storage_version(mut self, version: u16) -> Self { - self.storage_version = Some(StorageVersion::new(version)); - self - } pub fn build(self) -> sp_io::TestExternalities { sp_tracing::try_init_simple(); self.set_associated_consts(); @@ -593,6 +587,7 @@ impl Default for Origin { mod run_tests { use super::*; use pretty_assertions::{assert_eq, assert_ne}; + use sp_core::U256; // Perform a call to a plain account. // The actual transfer fails because we can only call contracts. @@ -616,66 +611,6 @@ mod run_tests { }); } - #[test] - fn migration_on_idle_hooks_works() { - // Defines expectations of how many migration steps can be done given the weight limit. - let tests = [ - (Weight::zero(), LATEST_MIGRATION_VERSION - 2), - (::WeightInfo::migrate() + 1.into(), LATEST_MIGRATION_VERSION - 1), - (Weight::MAX, LATEST_MIGRATION_VERSION), - ]; - - for (weight, expected_version) in tests { - ExtBuilder::default() - .set_storage_version(LATEST_MIGRATION_VERSION - 2) - .build() - .execute_with(|| { - MigrationInProgress::::set(Some(Default::default())); - Contracts::on_idle(System::block_number(), weight); - assert_eq!(StorageVersion::get::>(), expected_version); - }); - } - } - - #[test] - fn migration_in_progress_works() { - let (wasm, code_hash) = compile_module("dummy").unwrap(); - - ExtBuilder::default().existential_deposit(1).build().execute_with(|| { - let _ = ::Currency::set_balance(&ALICE, 1_000_000); - MigrationInProgress::::set(Some(Default::default())); - - assert_err!( - Contracts::upload_code( - RuntimeOrigin::signed(ALICE), - vec![], - deposit_limit::(), - ), - Error::::MigrationInProgress, - ); - assert_err!( - Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash), - Error::::MigrationInProgress, - ); - assert_err!( - Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB_ADDR, code_hash), - Error::::MigrationInProgress, - ); - assert_err_ignore_postinfo!( - builder::call(BOB_ADDR).build(), - Error::::MigrationInProgress - ); - assert_err_ignore_postinfo!( - builder::instantiate_with_code(wasm).value(100_000).build(), - Error::::MigrationInProgress, - ); - assert_err_ignore_postinfo!( - builder::instantiate(code_hash).value(100_000).build(), - Error::::MigrationInProgress, - ); - }); - } - #[test] fn instantiate_and_call_and_deposit_event() { let (wasm, code_hash) = compile_module("event_and_return_on_deploy").unwrap(); @@ -745,7 +680,7 @@ mod run_tests { contract: addr, data: vec![1, 2, 3, 4] }), - topics: vec![], + topics: vec![H256::repeat_byte(42)], }, EventRecord { phase: Phase::Initialization, @@ -3402,7 +3337,7 @@ mod run_tests { assert_err_ignore_postinfo!( builder::call(addr_caller) .storage_deposit_limit(13) - .data((100u32, &addr_callee, 0u64).encode()) + .data((100u32, &addr_callee, U256::from(0u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3416,7 +3351,7 @@ mod run_tests { assert_err_ignore_postinfo!( builder::call(addr_caller) .storage_deposit_limit(14) - .data((101u32, &addr_callee, 0u64).encode()) + .data((101u32, &addr_callee, U256::from(0u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3429,7 +3364,7 @@ mod run_tests { assert_err_ignore_postinfo!( builder::call(addr_caller) .storage_deposit_limit(16) - .data((102u32, &addr_callee, 1u64).encode()) + .data((102u32, &addr_callee, U256::from(1u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3440,7 +3375,7 @@ mod run_tests { assert_err_ignore_postinfo!( builder::call(addr_caller) .storage_deposit_limit(0) - .data((87u32, &addr_callee, 0u64).encode()) + .data((87u32, &addr_callee, U256::from(0u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3450,7 +3385,9 @@ mod run_tests { // Require more than the sender's balance. // We don't set a special limit for the nested call. assert_err_ignore_postinfo!( - builder::call(addr_caller).data((512u32, &addr_callee, 1u64).encode()).build(), + builder::call(addr_caller) + .data((512u32, &addr_callee, U256::from(1u64)).encode()) + .build(), >::StorageDepositLimitExhausted, ); @@ -3459,7 +3396,7 @@ mod run_tests { // enforced as callee frees up storage. This should pass. assert_ok!(builder::call(addr_caller) .storage_deposit_limit(1) - .data((87u32, &addr_callee, 1u64).encode()) + .data((87u32, &addr_callee, U256::from(1u64)).encode()) .build()); }); } @@ -3500,7 +3437,7 @@ mod run_tests { builder::call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 1) - .data((0u32, &code_hash_callee, 0u64).encode()) + .data((0u32, &code_hash_callee, U256::from(0u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3514,7 +3451,7 @@ mod run_tests { builder::call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 2) - .data((1u32, &code_hash_callee, 0u64).encode()) + .data((1u32, &code_hash_callee, U256::from(0u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3528,7 +3465,10 @@ mod run_tests { builder::call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 2) - .data((0u32, &code_hash_callee, callee_info_len + 2 + ED + 1).encode()) + .data( + (0u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 1)) + .encode() + ) .build(), >::StorageDepositLimitExhausted, ); @@ -3543,7 +3483,10 @@ mod run_tests { builder::call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 3) // enough parent limit - .data((1u32, &code_hash_callee, callee_info_len + 2 + ED + 2).encode()) + .data( + (1u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 2)) + .encode() + ) .build(), >::StorageDepositLimitExhausted, ); @@ -3554,7 +3497,7 @@ mod run_tests { let result = builder::bare_call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 4) - .data((1u32, &code_hash_callee, callee_info_len + 2 + ED + 3).encode()) + .data((1u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 3)).encode()) .build(); let returned = result.result.unwrap(); diff --git a/substrate/frame/revive/src/wasm/mod.rs b/substrate/frame/revive/src/wasm/mod.rs index 9024390fd24f..5813903326bf 100644 --- a/substrate/frame/revive/src/wasm/mod.rs +++ b/substrate/frame/revive/src/wasm/mod.rs @@ -45,9 +45,9 @@ use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ dispatch::DispatchResult, ensure, - traits::{fungible::MutateHold, tokens::Precision::BestEffort}, + traits::{fungible::MutateHold, tokens::Precision::BestEffort, IsType}, }; -use sp_core::Get; +use sp_core::{Get, H256, U256}; use sp_runtime::DispatchError; /// Validated Wasm module ready for execution. @@ -123,7 +123,11 @@ impl Token for CodeLoadToken { } } -impl WasmBlob { +impl WasmBlob +where + T::Hash: IsType, + BalanceOf: Into + TryFrom, +{ /// We only check for size and nothing else when the code is uploaded. pub fn from_code( code: Vec, @@ -251,7 +255,11 @@ pub struct PreparedCall<'a, E: Ext> { api_version: ApiVersion, } -impl<'a, E: Ext> PreparedCall<'a, E> { +impl<'a, E: Ext> PreparedCall<'a, E> +where + BalanceOf: Into, + BalanceOf: TryFrom, +{ pub fn call(mut self) -> ExecResult { let exec_result = loop { let interrupt = self.instance.run(); @@ -315,7 +323,10 @@ impl WasmBlob { } } -impl Executable for WasmBlob { +impl Executable for WasmBlob +where + BalanceOf: Into + TryFrom, +{ fn from_storage( code_hash: sp_core::H256, gas_meter: &mut GasMeter, diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 51c723493847..528b0ababfa0 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -19,12 +19,12 @@ use crate::{ address::AddressMapper, - exec::{ExecError, ExecResult, Ext, Key, TopicOf}, + exec::{ExecError, ExecResult, Ext, Key}, gas::{ChargedAmount, Token}, limits, primitives::ExecReturnValue, weights::WeightInfo, - BalanceOf, Config, Error, LOG_TARGET, SENTINEL, + Config, Error, LOG_TARGET, SENTINEL, }; use alloc::{boxed::Box, vec, vec::Vec}; use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; @@ -35,15 +35,22 @@ use frame_support::{ }; use pallet_revive_proc_macro::define_env; use pallet_revive_uapi::{CallFlags, ReturnErrorCode, ReturnFlags, StorageFlags}; -use sp_core::{H160, H256}; +use sp_core::{H160, H256, U256}; use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256}; -use sp_runtime::{traits::Zero, DispatchError, RuntimeDebug}; +use sp_runtime::{DispatchError, RuntimeDebug}; type CallOf = ::RuntimeCall; /// The maximum nesting depth a contract can use when encoding types. const MAX_DECODE_NESTING: u32 = 256; +/// Encode a `U256` into a 32 byte buffer. +fn as_bytes(u: U256) -> [u8; 32] { + let mut bytes = [0u8; 32]; + u.to_little_endian(&mut bytes); + bytes +} + #[derive(Clone, Copy)] pub enum ApiVersion { /// Expose all APIs even unversioned ones. Only used for testing and benchmarking. @@ -84,6 +91,32 @@ pub trait Memory { Ok(buf) } + /// Same as `read` but reads into a fixed size buffer. + fn read_array(&self, ptr: u32) -> Result<[u8; N], DispatchError> { + let mut buf = [0u8; N]; + self.read_into_buf(ptr, &mut buf)?; + Ok(buf) + } + + /// Read a `u32` from the sandbox memory. + fn read_u32(&self, ptr: u32) -> Result { + let buf: [u8; 4] = self.read_array(ptr)?; + Ok(u32::from_le_bytes(buf)) + } + + /// Read a `U256` from the sandbox memory. + fn read_u256(&self, ptr: u32) -> Result { + let buf: [u8; 32] = self.read_array(ptr)?; + Ok(U256::from_little_endian(&buf)) + } + + /// Read a `H256` from the sandbox memory. + fn read_h256(&self, ptr: u32) -> Result { + let mut code_hash = H256::default(); + self.read_into_buf(ptr, code_hash.as_bytes_mut())?; + Ok(code_hash) + } + /// Read designated chunk from the sandbox memory and attempt to decode into the specified type. /// /// Returns `Err` if one of the following conditions occurs: @@ -647,7 +680,7 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { } let buf_len = buf.len() as u32; - let len: u32 = memory.read_as(out_len_ptr)?; + let len = memory.read_u32(out_len_ptr)?; if len < buf_len { return Err(Error::::OutputBufferTooSmall.into()) @@ -963,13 +996,13 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { CallType::Call { callee_ptr, value_ptr, deposit_ptr, weight } => { let mut callee = H160::zero(); memory.read_into_buf(callee_ptr, callee.as_bytes_mut())?; - let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { - BalanceOf::<::T>::zero() + let deposit_limit = if deposit_ptr == SENTINEL { + U256::zero() } else { - memory.read_as(deposit_ptr)? + memory.read_u256(deposit_ptr)? }; let read_only = flags.contains(CallFlags::READ_ONLY); - let value: BalanceOf<::T> = memory.read_as(value_ptr)?; + let value = memory.read_u256(value_ptr)?; if value > 0u32.into() { // If the call value is non-zero and state change is not allowed, issue an // error. @@ -992,7 +1025,8 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { if flags.intersects(CallFlags::ALLOW_REENTRY | CallFlags::READ_ONLY) { return Err(Error::::InvalidCallFlags.into()) } - let code_hash = memory.read_as(code_hash_ptr)?; + + let code_hash = memory.read_h256(code_hash_ptr)?; self.ext.delegate_call(code_hash, input_data) }, }; @@ -1036,19 +1070,15 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { salt_ptr: u32, ) -> Result { self.charge_gas(RuntimeCosts::Instantiate { input_data_len })?; - let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { - BalanceOf::<::T>::zero() - } else { - memory.read_as(deposit_ptr)? - }; - let value: BalanceOf<::T> = memory.read_as(value_ptr)?; - let code_hash: H256 = memory.read_as(code_hash_ptr)?; + let deposit_limit: U256 = + if deposit_ptr == SENTINEL { U256::zero() } else { memory.read_u256(deposit_ptr)? }; + let value = memory.read_u256(value_ptr)?; + let code_hash = memory.read_h256(code_hash_ptr)?; let input_data = memory.read(input_data_ptr, input_data_len)?; let salt = if salt_ptr == SENTINEL { None } else { - let mut salt = [0u8; 32]; - memory.read_into_buf(salt_ptr, salt.as_mut_slice())?; + let salt: [u8; 32] = memory.read_array(salt_ptr)?; Some(salt) }; let instantiate_outcome = self.ext.instantiate( @@ -1194,7 +1224,7 @@ pub mod env { self.charge_gas(RuntimeCosts::Transfer)?; let mut callee = H160::zero(); memory.read_into_buf(address_ptr, callee.as_bytes_mut())?; - let value: BalanceOf<::T> = memory.read_as(value_ptr)?; + let value: U256 = memory.read_u256(value_ptr)?; let result = self.ext.transfer(&callee, value); match result { Ok(()) => Ok(ReturnErrorCode::Success), @@ -1374,7 +1404,7 @@ pub mod env { self.write_fixed_sandbox_output( memory, out_ptr, - &value.encode(), + &value.as_bytes(), false, already_charged, )?; @@ -1389,11 +1419,11 @@ pub mod env { #[api_version(0)] fn own_code_hash(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::OwnCodeHash)?; - let code_hash_encoded = &self.ext.own_code_hash().encode(); + let code_hash = *self.ext.own_code_hash(); Ok(self.write_fixed_sandbox_output( memory, out_ptr, - code_hash_encoded, + code_hash.as_bytes(), false, already_charged, )?) @@ -1439,14 +1469,12 @@ pub mod env { ref_time_limit: u64, proof_size_limit: u64, out_ptr: u32, - out_len_ptr: u32, ) -> Result<(), TrapReason> { let weight = Weight::from_parts(ref_time_limit, proof_size_limit); self.charge_gas(RuntimeCosts::WeightToFee)?; - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, &self.ext.get_weight_price(weight).encode(), false, already_charged, @@ -1477,18 +1505,12 @@ pub mod env { /// Stores the *free* balance of the current account into the supplied buffer. /// See [`pallet_revive_uapi::HostFn::balance`]. #[api_version(0)] - fn balance( - &mut self, - memory: &mut M, - out_ptr: u32, - out_len_ptr: u32, - ) -> Result<(), TrapReason> { + fn balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::Balance)?; - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &self.ext.balance().encode(), + &as_bytes(self.ext.balance()), false, already_charged, )?) @@ -1497,18 +1519,12 @@ pub mod env { /// Stores the value transferred along with this call/instantiate into the supplied buffer. /// See [`pallet_revive_uapi::HostFn::value_transferred`]. #[api_version(0)] - fn value_transferred( - &mut self, - memory: &mut M, - out_ptr: u32, - out_len_ptr: u32, - ) -> Result<(), TrapReason> { + fn value_transferred(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::ValueTransferred)?; - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &self.ext.value_transferred().encode(), + &as_bytes(self.ext.value_transferred()), false, already_charged, )?) @@ -1517,13 +1533,12 @@ pub mod env { /// Load the latest block timestamp into the supplied buffer /// See [`pallet_revive_uapi::HostFn::now`]. #[api_version(0)] - fn now(&mut self, memory: &mut M, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn now(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::Now)?; - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &self.ext.now().encode(), + &as_bytes(self.ext.now()), false, already_charged, )?) @@ -1532,18 +1547,12 @@ pub mod env { /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. /// See [`pallet_revive_uapi::HostFn::minimum_balance`]. #[api_version(0)] - fn minimum_balance( - &mut self, - memory: &mut M, - out_ptr: u32, - out_len_ptr: u32, - ) -> Result<(), TrapReason> { + fn minimum_balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::MinimumBalance)?; - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &self.ext.minimum_balance().encode(), + &as_bytes(self.ext.minimum_balance()), false, already_charged, )?) @@ -1557,50 +1566,47 @@ pub mod env { &mut self, memory: &mut M, topics_ptr: u32, - topics_len: u32, + num_topic: u32, data_ptr: u32, data_len: u32, ) -> Result<(), TrapReason> { - let num_topic = topics_len - .checked_div(core::mem::size_of::>() as u32) - .ok_or("Zero sized topics are not allowed")?; self.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?; + + if num_topic > limits::NUM_EVENT_TOPICS { + return Err(Error::::TooManyTopics.into()); + } + if data_len > self.ext.max_value_size() { return Err(Error::::ValueTooLarge.into()); } - let topics: Vec::T>> = match topics_len { + let topics: Vec = match num_topic { 0 => Vec::new(), - _ => memory.read_as_unbounded(topics_ptr, topics_len)?, + _ => { + let mut v = Vec::with_capacity(num_topic as usize); + let topics_len = num_topic * H256::len_bytes() as u32; + let buf = memory.read(topics_ptr, topics_len)?; + for chunk in buf.chunks_exact(H256::len_bytes()) { + v.push(H256::from_slice(chunk)); + } + v + }, }; - // If there are more than `event_topics`, then trap. - if topics.len() as u32 > limits::NUM_EVENT_TOPICS { - return Err(Error::::TooManyTopics.into()); - } - let event_data = memory.read(data_ptr, data_len)?; - self.ext.deposit_event(topics, event_data); - Ok(()) } /// Stores the current block number of the current contract into the supplied buffer. /// See [`pallet_revive_uapi::HostFn::block_number`]. #[api_version(0)] - fn block_number( - &mut self, - memory: &mut M, - out_ptr: u32, - out_len_ptr: u32, - ) -> Result<(), TrapReason> { + fn block_number(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::BlockNumber)?; - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &self.ext.block_number().encode(), + &as_bytes(self.ext.block_number()), false, already_charged, )?) @@ -1884,7 +1890,7 @@ pub mod env { code_hash_ptr: u32, ) -> Result { self.charge_gas(RuntimeCosts::SetCodeHash)?; - let code_hash: H256 = memory.read_as(code_hash_ptr)?; + let code_hash: H256 = memory.read_h256(code_hash_ptr)?; match self.ext.set_code_hash(code_hash) { Err(err) => { let code = Self::err_into_return_code(err)?; @@ -1926,7 +1932,7 @@ pub mod env { code_hash_ptr: u32, ) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::LockDelegateDependency)?; - let code_hash = memory.read_as(code_hash_ptr)?; + let code_hash = memory.read_h256(code_hash_ptr)?; self.ext.lock_delegate_dependency(code_hash)?; Ok(()) } @@ -1941,7 +1947,7 @@ pub mod env { code_hash_ptr: u32, ) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::UnlockDelegateDependency)?; - let code_hash = memory.read_as(code_hash_ptr)?; + let code_hash = memory.read_h256(code_hash_ptr)?; self.ext.unlock_delegate_dependency(&code_hash)?; Ok(()) } diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index 7974cc1260e4..8913592c13bb 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -51,19 +51,6 @@ use core::marker::PhantomData; pub trait WeightInfo { fn on_process_deletion_queue_batch() -> Weight; fn on_initialize_per_trie_key(k: u32, ) -> Weight; - fn v9_migration_step(c: u32, ) -> Weight; - fn v10_migration_step() -> Weight; - fn v11_migration_step(k: u32, ) -> Weight; - fn v12_migration_step(c: u32, ) -> Weight; - fn v13_migration_step() -> Weight; - fn v14_migration_step() -> Weight; - fn v15_migration_step() -> Weight; - fn v16_migration_step() -> Weight; - fn migration_noop() -> Weight; - fn migrate() -> Weight; - fn on_runtime_upgrade_noop() -> Weight; - fn on_runtime_upgrade_in_progress() -> Weight; - fn on_runtime_upgrade() -> Weight; fn call_with_code_per_byte(c: u32, ) -> Weight; fn instantiate_with_code(c: u32, i: u32) -> Weight; fn instantiate(i: u32) -> Weight; @@ -162,182 +149,6 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:2 w:1) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:2 w:1) - /// The range of component `c` is `[0, 125952]`. - fn v9_migration_step(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `211 + c * (1 ±0)` - // Estimated: `6149 + c * (1 ±0)` - // Minimum execution time: 7_783_000 picoseconds. - Weight::from_parts(4_462_075, 6149) - // Standard Error: 5 - .saturating_add(Weight::from_parts(1_634, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:0) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - fn v10_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `510` - // Estimated: `6450` - // Minimum execution time: 15_971_000 picoseconds. - Weight::from_parts(16_730_000, 6450) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::DeletionQueue` (r:1 w:1025) - /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) - /// Storage: `Contracts::DeletionQueueCounter` (r:0 w:1) - /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - /// The range of component `k` is `[0, 1024]`. - fn v11_migration_step(k: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `171 + k * (1 ±0)` - // Estimated: `3635 + k * (1 ±0)` - // Minimum execution time: 3_149_000 picoseconds. - Weight::from_parts(3_264_000, 3635) - // Standard Error: 559 - .saturating_add(Weight::from_parts(1_111_209, 0).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(k.into())) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) - /// Storage: `System::Account` (r:1 w:0) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:0 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// The range of component `c` is `[0, 125952]`. - fn v12_migration_step(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `325 + c * (1 ±0)` - // Estimated: `6263 + c * (1 ±0)` - // Minimum execution time: 15_072_000 picoseconds. - Weight::from_parts(15_721_891, 6263) - // Standard Error: 2 - .saturating_add(Weight::from_parts(428, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - fn v13_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `440` - // Estimated: `6380` - // Minimum execution time: 12_047_000 picoseconds. - Weight::from_parts(12_500_000, 6380) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// Storage: `Balances::Holds` (r:1 w:0) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - fn v14_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `352` - // Estimated: `6292` - // Minimum execution time: 47_488_000 picoseconds. - Weight::from_parts(48_482_000, 6292) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `System::Account` (r:2 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - fn v15_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `594` - // Estimated: `6534` - // Minimum execution time: 52_801_000 picoseconds. - Weight::from_parts(54_230_000, 6534) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - fn v16_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `409` - // Estimated: `6349` - // Minimum execution time: 11_618_000 picoseconds. - Weight::from_parts(12_068_000, 6349) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - fn migration_noop() -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `1627` - // Minimum execution time: 2_131_000 picoseconds. - Weight::from_parts(2_255_000, 1627) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:1) - fn migrate() -> Weight { - // Proof Size summary in bytes: - // Measured: `166` - // Estimated: `3631` - // Minimum execution time: 10_773_000 picoseconds. - Weight::from_parts(11_118_000, 3631) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - fn on_runtime_upgrade_noop() -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 4_371_000 picoseconds. - Weight::from_parts(4_624_000, 3607) - .saturating_add(T::DbWeight::get().reads(1_u64)) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - fn on_runtime_upgrade_in_progress() -> Weight { - // Proof Size summary in bytes: - // Measured: `167` - // Estimated: `3632` - // Minimum execution time: 5_612_000 picoseconds. - Weight::from_parts(5_838_000, 3632) - .saturating_add(T::DbWeight::get().reads(2_u64)) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - fn on_runtime_upgrade() -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 5_487_000 picoseconds. - Weight::from_parts(5_693_000, 3607) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) @@ -1152,182 +963,6 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:2 w:1) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:2 w:1) - /// The range of component `c` is `[0, 125952]`. - fn v9_migration_step(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `211 + c * (1 ±0)` - // Estimated: `6149 + c * (1 ±0)` - // Minimum execution time: 7_783_000 picoseconds. - Weight::from_parts(4_462_075, 6149) - // Standard Error: 5 - .saturating_add(Weight::from_parts(1_634, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:0) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - fn v10_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `510` - // Estimated: `6450` - // Minimum execution time: 15_971_000 picoseconds. - Weight::from_parts(16_730_000, 6450) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::DeletionQueue` (r:1 w:1025) - /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) - /// Storage: `Contracts::DeletionQueueCounter` (r:0 w:1) - /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - /// The range of component `k` is `[0, 1024]`. - fn v11_migration_step(k: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `171 + k * (1 ±0)` - // Estimated: `3635 + k * (1 ±0)` - // Minimum execution time: 3_149_000 picoseconds. - Weight::from_parts(3_264_000, 3635) - // Standard Error: 559 - .saturating_add(Weight::from_parts(1_111_209, 0).saturating_mul(k.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(k.into())) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) - /// Storage: `System::Account` (r:1 w:0) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:0 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// The range of component `c` is `[0, 125952]`. - fn v12_migration_step(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `325 + c * (1 ±0)` - // Estimated: `6263 + c * (1 ±0)` - // Minimum execution time: 15_072_000 picoseconds. - Weight::from_parts(15_721_891, 6263) - // Standard Error: 2 - .saturating_add(Weight::from_parts(428, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - fn v13_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `440` - // Estimated: `6380` - // Minimum execution time: 12_047_000 picoseconds. - Weight::from_parts(12_500_000, 6380) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// Storage: `Balances::Holds` (r:1 w:0) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - fn v14_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `352` - // Estimated: `6292` - // Minimum execution time: 47_488_000 picoseconds. - Weight::from_parts(48_482_000, 6292) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `System::Account` (r:2 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - fn v15_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `594` - // Estimated: `6534` - // Minimum execution time: 52_801_000 picoseconds. - Weight::from_parts(54_230_000, 6534) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - fn v16_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `409` - // Estimated: `6349` - // Minimum execution time: 11_618_000 picoseconds. - Weight::from_parts(12_068_000, 6349) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - fn migration_noop() -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `1627` - // Minimum execution time: 2_131_000 picoseconds. - Weight::from_parts(2_255_000, 1627) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:1) - fn migrate() -> Weight { - // Proof Size summary in bytes: - // Measured: `166` - // Estimated: `3631` - // Minimum execution time: 10_773_000 picoseconds. - Weight::from_parts(11_118_000, 3631) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - fn on_runtime_upgrade_noop() -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 4_371_000 picoseconds. - Weight::from_parts(4_624_000, 3607) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - fn on_runtime_upgrade_in_progress() -> Weight { - // Proof Size summary in bytes: - // Measured: `167` - // Estimated: `3632` - // Minimum execution time: 5_612_000 picoseconds. - Weight::from_parts(5_838_000, 3632) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - fn on_runtime_upgrade() -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 5_487_000 picoseconds. - Weight::from_parts(5_693_000, 3607) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index f52ea9574025..101ae9aca465 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -58,21 +58,17 @@ pub trait HostFn: private::Sealed { /// Stores the *free* balance of the current account into the supplied buffer. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the balance. - fn balance(output: &mut &mut [u8]); + fn balance(output: &mut [u8; 32]); /// Stores the current block number of the current contract into the supplied buffer. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the block number. - fn block_number(output: &mut &mut [u8]); + fn block_number(output: &mut [u8; 32]); /// Call (possibly transferring some amount of funds) into the specified account. /// @@ -83,11 +79,10 @@ pub trait HostFn: private::Sealed { /// otherwise. /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. - /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a - /// `Option`. Traps otherwise. Passing `None` means setting no specific limit for - /// the call, which implies storage usage up to the limit of the parent call. - /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. - /// Traps otherwise. + /// - `deposit`: The storage deposit limit for instantiation. Passing `None` means setting no + /// specific limit for the call, which implies storage usage up to the limit of the parent + /// call. + /// - `value`: The value to transfer into the contract. /// - `input`: The input data buffer used to call the contract. /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` /// is provided then the output buffer is not copied. @@ -106,8 +101,8 @@ pub trait HostFn: private::Sealed { callee: &[u8; 20], ref_time_limit: u64, proof_size_limit: u64, - deposit: Option<&[u8]>, - value: &[u8], + deposit: Option<&[u8; 32]>, + value: &[u8; 32], input_data: &[u8], output: Option<&mut &mut [u8]>, ) -> Result; @@ -287,8 +282,8 @@ pub trait HostFn: private::Sealed { /// /// # Parameters /// - /// - `topics`: The topics list encoded as `Vec`. It can't contain duplicates. - fn deposit_event(topics: &[u8], data: &[u8]); + /// - `topics`: The topics list. It can't contain duplicates. + fn deposit_event(topics: &[[u8; 32]], data: &[u8]); /// Recovers the ECDSA public key from the given message hash and signature. /// @@ -374,11 +369,10 @@ pub trait HostFn: private::Sealed { /// - `code_hash`: The hash of the code to be instantiated. /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. - /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a - /// `Option`. Traps otherwise. Passing `None` means setting no specific limit for - /// the call, which implies storage usage up to the limit of the parent call. - /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. - /// Traps otherwise. + /// - `deposit`: The storage deposit limit for instantiation. Passing `None` means setting no + /// specific limit for the call, which implies storage usage up to the limit of the parent + /// call. + /// - `value`: The value to transfer into the contract. /// - `input`: The input data buffer. /// - `address`: A reference to the address buffer to write the address of the contract. If /// `None` is provided then the output buffer is not copied. @@ -402,8 +396,8 @@ pub trait HostFn: private::Sealed { code_hash: &[u8; 32], ref_time_limit: u64, proof_size_limit: u64, - deposit: Option<&[u8]>, - value: &[u8], + deposit: Option<&[u8; 32]>, + value: &[u8; 32], input: &[u8], address: Option<&mut [u8; 20]>, output: Option<&mut &mut [u8]>, @@ -422,14 +416,11 @@ pub trait HostFn: private::Sealed { fn is_contract(address: &[u8; 20]) -> bool; /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. - /// The data is encoded as `T::Balance`. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the minimum balance. - fn minimum_balance(output: &mut &mut [u8]); + fn minimum_balance(output: &mut [u8; 32]); /// Retrieve the code hash of the currently executing contract. /// @@ -440,12 +431,10 @@ pub trait HostFn: private::Sealed { /// Load the latest block timestamp into the supplied buffer /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the timestamp. - fn now(output: &mut &mut [u8]); + fn now(output: &mut [u8; 32]); /// Removes the delegate dependency from the contract. /// @@ -548,12 +537,12 @@ pub trait HostFn: private::Sealed { /// # Parameters /// /// - `address`: The address of the account to transfer funds to. - /// - `value`: The value to transfer. Should be decodable as a `T::Balance`. Traps otherwise. + /// - `value`: The U256 value to transfer. /// /// # Errors /// /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] - fn transfer(address: &[u8; 20], value: &[u8]) -> Result; + fn transfer(address: &[u8; 20], value: &[u8; 32]) -> Result; /// Remove the calling account and transfer remaining **free** balance. /// @@ -573,26 +562,20 @@ pub trait HostFn: private::Sealed { fn terminate(beneficiary: &[u8; 20]) -> !; /// Stores the value transferred along with this call/instantiate into the supplied buffer. - /// The data is encoded as `T::Balance`. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the transferred value. - fn value_transferred(output: &mut &mut [u8]); + fn value_transferred(output: &mut [u8; 32]); /// Stores the price for the specified amount of gas into the supplied buffer. - /// The data is encoded as `T::Balance`. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. /// /// # Parameters /// /// - `ref_time_limit`: The *ref_time* Weight limit to query the price for. /// - `proof_size_limit`: The *proof_size* Weight limit to query the price for. /// - `output`: A reference to the output data buffer to write the price. - fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]); + fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut [u8; 32]); /// Execute an XCM program locally, using the contract's address as the origin. /// This is equivalent to dispatching `pallet_xcm::execute` through call_runtime, except that diff --git a/substrate/frame/revive/uapi/src/host/riscv32.rs b/substrate/frame/revive/uapi/src/host/riscv32.rs index c8218bb8f737..b7b660c40837 100644 --- a/substrate/frame/revive/uapi/src/host/riscv32.rs +++ b/substrate/frame/revive/uapi/src/host/riscv32.rs @@ -79,24 +79,19 @@ mod sys { pub fn caller_is_origin() -> ReturnCode; pub fn caller_is_root() -> ReturnCode; pub fn address(out_ptr: *mut u8); - pub fn weight_to_fee( - ref_time: u64, - proof_size: u64, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ); + pub fn weight_to_fee(ref_time: u64, proof_size: u64, out_ptr: *mut u8); pub fn weight_left(out_ptr: *mut u8, out_len_ptr: *mut u32); - pub fn balance(out_ptr: *mut u8, out_len_ptr: *mut u32); - pub fn value_transferred(out_ptr: *mut u8, out_len_ptr: *mut u32); - pub fn now(out_ptr: *mut u8, out_len_ptr: *mut u32); - pub fn minimum_balance(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn balance(out_ptr: *mut u8); + pub fn value_transferred(out_ptr: *mut u8); + pub fn now(out_ptr: *mut u8); + pub fn minimum_balance(out_ptr: *mut u8); pub fn deposit_event( - topics_ptr: *const u8, - topics_len: u32, + topics_ptr: *const [u8; 32], + num_topic: u32, data_ptr: *const u8, data_len: u32, ); - pub fn block_number(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn block_number(out_ptr: *mut u8); pub fn hash_sha2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); pub fn hash_blake2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); @@ -136,21 +131,20 @@ mod sys { } } +/// A macro to implement all Host functions with a signature of `fn(&mut [u8; n])`. macro_rules! impl_wrapper_for { - ( $( $name:ident, )* ) => { - $( - fn $name(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { - sys::$name( - output.as_mut_ptr(), - &mut output_len, - ) - } - extract_from_slice(output, output_len as usize) - } - )* - } + (@impl_fn $name:ident, $n: literal) => { + fn $name(output: &mut [u8; $n]) { + unsafe { sys::$name(output.as_mut_ptr()) } + } + }; + + () => {}; + + ([u8; $n: literal] => $($name:ident),*; $($tail:tt)*) => { + $(impl_wrapper_for!(@impl_fn $name, $n);)* + impl_wrapper_for!($($tail)*); + }; } macro_rules! impl_hash_fn { @@ -185,7 +179,7 @@ fn ptr_len_or_sentinel(data: &mut Option<&mut &mut [u8]>) -> (*mut u8, u32) { } #[inline(always)] -fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { +fn ptr_or_sentinel(data: &Option<&[u8; 32]>) -> *const u8 { match data { Some(ref data) => data.as_ptr(), None => crate::SENTINEL as _, @@ -197,8 +191,8 @@ impl HostFn for HostFnImpl { code_hash: &[u8; 32], ref_time_limit: u64, proof_size_limit: u64, - deposit_limit: Option<&[u8]>, - value: &[u8], + deposit_limit: Option<&[u8; 32]>, + value: &[u8; 32], input: &[u8], mut address: Option<&mut [u8; 20]>, mut output: Option<&mut &mut [u8]>, @@ -253,8 +247,8 @@ impl HostFn for HostFnImpl { callee: &[u8; 20], ref_time_limit: u64, proof_size_limit: u64, - deposit_limit: Option<&[u8]>, - value: &[u8], + deposit_limit: Option<&[u8; 32]>, + value: &[u8; 32], input: &[u8], mut output: Option<&mut &mut [u8]>, ) -> Result { @@ -327,12 +321,12 @@ impl HostFn for HostFnImpl { ret_code.into() } - fn transfer(address: &[u8; 20], value: &[u8]) -> Result { + fn transfer(address: &[u8; 20], value: &[u8; 32]) -> Result { let ret_code = unsafe { sys::transfer(address.as_ptr(), value.as_ptr()) }; ret_code.into() } - fn deposit_event(topics: &[u8], data: &[u8]) { + fn deposit_event(topics: &[[u8; 32]], data: &[u8]) { unsafe { sys::deposit_event( topics.as_ptr(), @@ -449,33 +443,19 @@ impl HostFn for HostFnImpl { ret_code.into() } - fn address(output: &mut [u8; 20]) { - unsafe { sys::address(output.as_mut_ptr()) } - } - - fn caller(output: &mut [u8; 20]) { - unsafe { sys::caller(output.as_mut_ptr()) } - } - impl_wrapper_for! { - block_number, balance, - value_transferred,now, minimum_balance, - weight_left, + [u8; 32] => block_number, balance, value_transferred, now, minimum_balance; + [u8; 20] => address, caller; } - fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]) { + fn weight_left(output: &mut &mut [u8]) { let mut output_len = output.len() as u32; - { - unsafe { - sys::weight_to_fee( - ref_time_limit, - proof_size_limit, - output.as_mut_ptr(), - &mut output_len, - ) - }; - } - extract_from_slice(output, output_len as usize); + unsafe { sys::weight_left(output.as_mut_ptr(), &mut output_len) } + extract_from_slice(output, output_len as usize) + } + + fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut [u8; 32]) { + unsafe { sys::weight_to_fee(ref_time_limit, proof_size_limit, output.as_mut_ptr()) }; } impl_hash_fn!(sha2_256, 32); From 083f5273fa9d03b2188c0c41aa21f8b45c59a733 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 07:05:54 +0000 Subject: [PATCH 325/382] Bump the known_good_semver group with 3 updates (#5636) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the known_good_semver group with 3 updates: [serde](https://github.com/serde-rs/serde), [serde_derive](https://github.com/serde-rs/serde) and [serde_json](https://github.com/serde-rs/json). Updates `serde` from 1.0.209 to 1.0.210
Release notes

Sourced from serde's releases.

v1.0.210

  • Support serializing and deserializing IpAddr and SocketAddr in no-std mode on Rust 1.77+ (#2816, thanks @​MathiasKoch)
  • Make serde::ser::StdError and serde::de::StdError equivalent to core::error::Error on Rust 1.81+ (#2818)
Commits
  • 89c4b02 Release 1.0.210
  • eeb8e44 Merge pull request #2818 from dtolnay/coreerror
  • 785c2d9 Stabilize no-std StdError trait
  • d549f04 Reformat parse_ip_impl definition and calls
  • 4c0dd63 Delete attr support from core::net deserialization macros
  • 26fb134 Relocate cfg attrs out of parse_ip_impl and parse_socket_impl
  • 07e614b Merge pull request #2817 from dtolnay/corenet
  • b1f899f Delete doc(cfg) attribute from impls that are supported in no-std
  • b4f860e Merge pull request #2816 from MathiasKoch/chore/core-net
  • d940fe1 Reuse existing Buf wrapper as replacement for std::io::Write
  • Additional commits viewable in compare view

Updates `serde_derive` from 1.0.209 to 1.0.210
Release notes

Sourced from serde_derive's releases.

v1.0.210

  • Support serializing and deserializing IpAddr and SocketAddr in no-std mode on Rust 1.77+ (#2816, thanks @​MathiasKoch)
  • Make serde::ser::StdError and serde::de::StdError equivalent to core::error::Error on Rust 1.81+ (#2818)
Commits
  • 89c4b02 Release 1.0.210
  • eeb8e44 Merge pull request #2818 from dtolnay/coreerror
  • 785c2d9 Stabilize no-std StdError trait
  • d549f04 Reformat parse_ip_impl definition and calls
  • 4c0dd63 Delete attr support from core::net deserialization macros
  • 26fb134 Relocate cfg attrs out of parse_ip_impl and parse_socket_impl
  • 07e614b Merge pull request #2817 from dtolnay/corenet
  • b1f899f Delete doc(cfg) attribute from impls that are supported in no-std
  • b4f860e Merge pull request #2816 from MathiasKoch/chore/core-net
  • d940fe1 Reuse existing Buf wrapper as replacement for std::io::Write
  • Additional commits viewable in compare view

Updates `serde_json` from 1.0.127 to 1.0.128
Release notes

Sourced from serde_json's releases.

1.0.128

  • Support serializing maps containing 128-bit integer keys to serde_json::Value (#1188, thanks @​Mrreadiness)
Commits
  • d96b1d9 Release 1.0.128
  • 599228d Merge pull request #1188 from Mrreadiness/feat/add-hashmap-key-128-serializer
  • 5416cee feat: add support for 128 bit HashMap key serialization
  • 27a4ca9 Upload CI Cargo.lock for reproducing failures
  • See full diff in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 12 ++++++------ Cargo.toml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 508fc2e89221..e8510a4c6a5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19104,9 +19104,9 @@ checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -19131,9 +19131,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2 1.0.82", "quote 1.0.37", @@ -19162,9 +19162,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "indexmap 2.2.3", "itoa", diff --git a/Cargo.toml b/Cargo.toml index 6bd401d8e15f..50c7225c749e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1185,10 +1185,10 @@ secp256k1 = { version = "0.28.0", default-features = false } secrecy = { version = "0.8.0", default-features = false } seedling-runtime = { path = "cumulus/parachains/runtimes/starters/seedling" } separator = { version = "0.4.1" } -serde = { version = "1.0.209", default-features = false } +serde = { version = "1.0.210", default-features = false } serde-big-array = { version = "0.3.2" } serde_derive = { version = "1.0.117" } -serde_json = { version = "1.0.127", default-features = false } +serde_json = { version = "1.0.128", default-features = false } serde_yaml = { version = "0.9" } serial_test = { version = "2.0.0" } sha1 = { version = "0.10.6" } From def35b92d120a78869cc2e06d34d575eea80faff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 07:06:51 +0000 Subject: [PATCH 326/382] Bump lazy_static from 1.4.0 to 1.5.0 (#5639) Bumps [lazy_static](https://github.com/rust-lang-nursery/lazy-static.rs) from 1.4.0 to 1.5.0.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=lazy_static&package-manager=cargo&previous-version=1.4.0&new-version=1.5.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e8510a4c6a5b..f6071a97915f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7729,9 +7729,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" diff --git a/Cargo.toml b/Cargo.toml index 50c7225c749e..174811663deb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -822,7 +822,7 @@ kvdb-memorydb = { version = "0.13.0" } kvdb-rocksdb = { version = "0.19.0" } kvdb-shared-tests = { version = "0.11.0" } landlock = { version = "0.3.0" } -lazy_static = { version = "1.4.0" } +lazy_static = { version = "1.5.0" } libc = { version = "0.2.155" } libfuzzer-sys = { version = "0.4" } libp2p = { version = "0.52.4" } From f5783cc68b53b06decb97cdddbfb511280ffdc45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 07:07:23 +0000 Subject: [PATCH 327/382] Bump proc-macro2 from 1.0.82 to 1.0.86 (#5638) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.82 to 1.0.86.
Release notes

Sourced from proc-macro2's releases.

1.0.86

  • Documentation improvements

1.0.85

  • Mark some tests as only for 64-bit targets (#463)

1.0.84

1.0.83

  • Optimize the representation of Ident (#462)
Commits
  • aa9476b Release 1.0.86
  • 1961358 Merge pull request #466 from dtolnay/buildrs
  • e1bd2cc Bring build script comments up to date
  • 5b27127 Merge pull request #465 from dtolnay/ignorereason
  • 0da4629 Fill in ignore reasons in all #[ignore] attributes
  • 5ee1cab Release 1.0.85
  • aa64c20 Merge pull request #464 from dtolnay/testsize
  • bc9f4d9 Ignore size tests on non-64bit target
  • 1160ec3 Make size tests #[ignore] in cfg(randomize_layout)
  • 33c9578 Release 1.0.84
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=proc-macro2&package-manager=cargo&previous-version=1.0.82&new-version=1.0.86)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 220 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 111 insertions(+), 111 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6071a97915f..e1b0b3f8f9e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -157,7 +157,7 @@ dependencies = [ "dunce", "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", "syn-solidity", @@ -284,7 +284,7 @@ dependencies = [ "include_dir", "itertools 0.10.5", "proc-macro-error", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -518,7 +518,7 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -620,7 +620,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -724,7 +724,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", "synstructure 0.12.6", @@ -736,7 +736,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", "synstructure 0.13.1", @@ -748,7 +748,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -759,7 +759,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -1290,7 +1290,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -1307,7 +1307,7 @@ version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -1366,7 +1366,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -1503,7 +1503,7 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease 0.2.12", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "regex", "rustc-hash 1.1.0", @@ -2846,7 +2846,7 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -2858,7 +2858,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -3046,7 +3046,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d51beaa537d73d2d1ff34ee70bc095f170420ab2ec5d687ecd3ec2b0d092514b" dependencies = [ "nom", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -4169,7 +4169,7 @@ name = "cumulus-pallet-parachain-system-proc-macro" version = "0.6.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -4705,7 +4705,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -4744,7 +4744,7 @@ dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "scratch", "syn 2.0.65", @@ -4762,7 +4762,7 @@ version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50c49547d73ba8dcfd4ad7325d64c6d5391ff4224d498fc39a6f3f49825a530d" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -4868,7 +4868,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -4879,7 +4879,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -4890,7 +4890,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -4902,7 +4902,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "rustc_version 0.4.0", "syn 1.0.109", @@ -4998,7 +4998,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -5058,7 +5058,7 @@ dependencies = [ "common-path", "derive-syn-parse", "once_cell", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "regex", "syn 2.0.65", @@ -5107,7 +5107,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -5255,7 +5255,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -5267,7 +5267,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -5287,7 +5287,7 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -5298,7 +5298,7 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -5501,7 +5501,7 @@ dependencies = [ "file-guard", "fs-err", "prettyplease 0.2.12", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -5573,7 +5573,7 @@ dependencies = [ "expander", "indexmap 2.2.3", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -5905,7 +5905,7 @@ dependencies = [ "frame-support", "parity-scale-codec", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "scale-info", "sp-arithmetic", @@ -6100,7 +6100,7 @@ dependencies = [ "parity-scale-codec", "pretty_assertions", "proc-macro-warning 1.0.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "regex", "scale-info", @@ -6119,7 +6119,7 @@ version = "10.0.0" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -6128,7 +6128,7 @@ dependencies = [ name = "frame-support-procedural-tools-derive" version = "11.0.0" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -6379,7 +6379,7 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -7172,7 +7172,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -7192,7 +7192,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", ] @@ -7551,7 +7551,7 @@ checksum = "6b07a2daf52077ab1b197aea69a5c990c060143835bf04c77070e98903791715" dependencies = [ "heck 0.5.0", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -8137,7 +8137,7 @@ checksum = "c4d5ec2a3df00c7836d7696c136274c9c59705bac69133253696a6c932cd1d74" dependencies = [ "heck 0.4.1", "proc-macro-warning 0.4.2", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -8565,7 +8565,7 @@ dependencies = [ "const-random", "derive-syn-parse", "macro_magic_core_macros", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -8576,7 +8576,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -8921,7 +8921,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -8933,7 +8933,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" dependencies = [ "cfg-if", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -9045,7 +9045,7 @@ checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro-error", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", "synstructure 0.12.6", @@ -9093,7 +9093,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -9484,7 +9484,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -9660,7 +9660,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -9727,7 +9727,7 @@ dependencies = [ "itertools 0.11.0", "petgraph", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -10442,7 +10442,7 @@ dependencies = [ name = "pallet-contracts-proc-macro" version = "18.0.0" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -11545,7 +11545,7 @@ dependencies = [ name = "pallet-revive-proc-macro" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -11789,7 +11789,7 @@ name = "pallet-staking-reward-curve" version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "sp-runtime", "syn 2.0.65", @@ -12435,7 +12435,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -12464,7 +12464,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "syn 1.0.109", "synstructure 0.12.6", ] @@ -12884,7 +12884,7 @@ checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -12925,7 +12925,7 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -15342,7 +15342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ "polkavm-common 0.9.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -15354,7 +15354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7855353a5a783dd5d09e3b915474bddf66575f5a3cf45dec8d1c5e051ba320dc" dependencies = [ "polkavm-common 0.10.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -15592,7 +15592,7 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "syn 1.0.109", ] @@ -15602,7 +15602,7 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "syn 2.0.65", ] @@ -15663,7 +15663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", "version_check", @@ -15675,7 +15675,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "version_check", ] @@ -15692,7 +15692,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -15703,7 +15703,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b698b0b09d40e9b7c1a47b132d66a8b54bcd20583d9b6d06e4535e383b4405c" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -15719,9 +15719,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -15784,7 +15784,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -15892,7 +15892,7 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -15905,7 +15905,7 @@ checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.11.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -16145,7 +16145,7 @@ version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", ] [[package]] @@ -16344,7 +16344,7 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -16894,7 +16894,7 @@ checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" dependencies = [ "cfg-if", "glob", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "regex", "relative-path", @@ -17388,7 +17388,7 @@ name = "sc-chain-spec-derive" version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -18683,7 +18683,7 @@ name = "sc-tracing-proc-macro" version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -18797,7 +18797,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -18835,7 +18835,7 @@ version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "serde_derive_internals", "syn 1.0.109", @@ -19135,7 +19135,7 @@ version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -19146,7 +19146,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -19237,7 +19237,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -20112,7 +20112,7 @@ dependencies = [ "blake2 0.10.6", "expander", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -20515,7 +20515,7 @@ name = "sp-debug-derive" version = "8.0.0" source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -20524,7 +20524,7 @@ dependencies = [ name = "sp-debug-derive" version = "14.0.0" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -20797,7 +20797,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf5 dependencies = [ "Inflector", "proc-macro-crate 1.3.1", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -20809,7 +20809,7 @@ dependencies = [ "Inflector", "expander", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -21070,7 +21070,7 @@ name = "sp-version-proc-macro" version = "13.0.0" dependencies = [ "parity-scale-codec", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "sp-version", "syn 2.0.65", @@ -21155,7 +21155,7 @@ checksum = "5e6915280e2d0db8911e5032a5c275571af6bdded2916abd691a659be25d3439" dependencies = [ "Inflector", "num-format", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "serde", "serde_json", @@ -21180,7 +21180,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f07d54c4d01a1713eb363b55ba51595da15f6f1211435b71466460da022aa140" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -21375,7 +21375,7 @@ checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" dependencies = [ "cfg_aliases", "memchr", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -21448,7 +21448,7 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -21484,7 +21484,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "rustversion", "syn 1.0.109", @@ -21497,7 +21497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "rustversion", "syn 2.0.65", @@ -21510,7 +21510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "rustversion", "syn 2.0.65", @@ -21961,7 +21961,7 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "unicode-ident", ] @@ -21972,7 +21972,7 @@ version = "2.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "unicode-ident", ] @@ -21984,7 +21984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b837ef12ab88835251726eb12237655e61ec8dc8a280085d1961cdc3dfd047" dependencies = [ "paste", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -21995,7 +21995,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", "unicode-xid 0.2.4", @@ -22007,7 +22007,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -22126,7 +22126,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -22290,7 +22290,7 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", ] @@ -22301,7 +22301,7 @@ version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -22467,7 +22467,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -22685,7 +22685,7 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -22727,7 +22727,7 @@ dependencies = [ "assert_matches", "expander", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -23298,7 +23298,7 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", "wasm-bindgen-shared", @@ -23332,7 +23332,7 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", "wasm-bindgen-backend", @@ -23365,7 +23365,7 @@ version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", ] @@ -24440,7 +24440,7 @@ name = "xcm-procedural" version = "7.0.0" dependencies = [ "Inflector", - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "staging-xcm", "syn 2.0.65", @@ -24606,7 +24606,7 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] @@ -24626,7 +24626,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.65", ] diff --git a/Cargo.toml b/Cargo.toml index 174811663deb..e2611982a985 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1079,7 +1079,7 @@ pretty_assertions = { version = "1.3.0" } primitive-types = { version = "0.12.1", default-features = false } proc-macro-crate = { version = "3.0.0" } proc-macro-warning = { version = "1.0.0", default-features = false } -proc-macro2 = { version = "1.0.64" } +proc-macro2 = { version = "1.0.86" } procfs = { version = "0.16.0" } prometheus = { version = "0.13.0", default-features = false } prometheus-endpoint = { path = "substrate/utils/prometheus", default-features = false, package = "substrate-prometheus-endpoint" } From a3eda0a1bdc67fc14efbf64c1f0046d452f55e31 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 9 Sep 2024 10:34:53 +0200 Subject: [PATCH 328/382] [CI] Remove duplicate jobs (#5602) All of these things are already tested in Github Actions: https://github.com/paritytech/polkadot-sdk/blob/b3c2a25b73bb4854f26204068f0aec3e8577196c/.github/workflows/checks-quick.yml#L50 Q: the `job-starter` seems to be used by tests, so am keeping it, but not sure how useful it is. --------- Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Co-authored-by: alvicsam --- .github/workflows/checks-quick.yml | 26 ++++++++++++++++++++ .gitlab/pipeline/check.yml | 39 ------------------------------ 2 files changed, 26 insertions(+), 39 deletions(-) diff --git a/.github/workflows/checks-quick.yml b/.github/workflows/checks-quick.yml index ee5ac31e9caa..96f214e9427b 100644 --- a/.github/workflows/checks-quick.yml +++ b/.github/workflows/checks-quick.yml @@ -181,3 +181,29 @@ jobs: env: ASSERT_REGEX: "FAIL-CI" GIT_DEPTH: 1 + + confirm-required-checks-quick-jobs-passed: + runs-on: ubuntu-latest + name: All quick checks passed + # If any new job gets added, be sure to add it to this array + needs: + - fmt + - check-dependency-rules + - check-rust-feature-propagation + - test-rust-features + - check-toml-format + - check-workspace + - check-markdown + - check-umbrella + - check-fail-ci + if: always() && !cancelled() + steps: + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed ❌" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed 🚀' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.gitlab/pipeline/check.yml b/.gitlab/pipeline/check.yml index 2212c1aeb0a8..7d1f37dddd51 100644 --- a/.gitlab/pipeline/check.yml +++ b/.gitlab/pipeline/check.yml @@ -1,25 +1,3 @@ -# from substrate -# not sure if it's needed in monorepo -check-dependency-rules: - stage: check - extends: - - .kubernetes-env - - .test-refs-no-trigger-prs-only - variables: - CI_IMAGE: "paritytech/tools:latest" - allow_failure: true - script: - - cd substrate/ - - ../.gitlab/ensure-deps.sh - -test-rust-features: - stage: check - extends: - - .kubernetes-env - - .test-refs-no-trigger-prs-only - script: - - bash .gitlab/rust-features.sh . - job-starter: stage: check image: paritytech/tools:latest @@ -29,20 +7,3 @@ job-starter: allow_failure: true script: - echo ok - -check-rust-feature-propagation: - stage: check - extends: - - .kubernetes-env - - .common-refs - script: - - zepter run check - -check-toml-format: - stage: check - extends: - - .kubernetes-env - - .common-refs - script: - - taplo format --check --config .config/taplo.toml - - echo "Please run `taplo format --config .config/taplo.toml` to fix any toml formatting issues" From c72f9ab594c8c8ea3f51323de8e1846c75f4a428 Mon Sep 17 00:00:00 2001 From: Nick Vikeras Date: Mon, 9 Sep 2024 05:12:56 -0500 Subject: [PATCH 329/382] Plumb RPC listener up to caller (#5038) # Description This PR allows the RPC server's socket address to be returned when initializing the server. This allows the library consumer to easily programmatically determine which port the RPC server is listening on. My use case for this is automated testing. I'd like to be able to simply specify that the server bind to port '0' and then test against whatever port the OS assigns dynamically. I will have many RPC servers running in parallel across many tests within a single process, and I don't want to have to deal with port conflicts. ## Integration Integration is straightforward. My main concern is that I am making non-backwards-compatible changes to public library functions. Let me know if I should leave backwards-compatible wrappers in place for any/all of the public functions that were modified. ## Review Notes The rationale for making the new listen_addresses field on the RpcHandlers struct a ```[MultiAddr]``` rather than ```SocketAddr``` is because I wanted it to be transport-agnostic as well as capable of supporting multiple listening addresses in case that is ever required by the RPC server in the future. # Checklist * [x] My PR includes a detailed description as outlined in the "Description" and its two subsections above. * [ ] My PR follows the [labeling requirements](CONTRIBUTING.md#Process) of this project (at minimum one label for `T` required) * External contributors: ask maintainers to put the right label on your PR. * [x] I have made corresponding changes to the documentation (if applicable) * [ ] I have added tests that prove my fix is effective or that my feature works (if applicable) 1. I didn't understand what the 'T' label meant. Am I supposed to open a github Issue for my PR? 2. I didn't see an easy way to add tests since the functions I am modifying are not directly called by any tests. --------- Co-authored-by: Niklas Adolfsson --- prdoc/pr_5038.prdoc | 15 +++++++++ substrate/client/rpc-servers/src/lib.rs | 42 ++++++++++++++++++++--- substrate/client/service/src/builder.rs | 16 +++++++-- substrate/client/service/src/lib.rs | 45 +++++++++++++------------ 4 files changed, 89 insertions(+), 29 deletions(-) create mode 100644 prdoc/pr_5038.prdoc diff --git a/prdoc/pr_5038.prdoc b/prdoc/pr_5038.prdoc new file mode 100644 index 000000000000..2bab8ef69f89 --- /dev/null +++ b/prdoc/pr_5038.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Plumb RPC listener up to caller + +doc: + - audience: Node Dev + description: + This PR allows the RPC server's socket address to be returned when initializing the server. + This allows the library consumer to easily programmatically determine which port the RPC server is listening on. +crates: + - name: sc-rpc-server + bump: major + - name: sc-service + bump: major diff --git a/substrate/client/rpc-servers/src/lib.rs b/substrate/client/rpc-servers/src/lib.rs index ca74c2371c25..0472a0a2f63c 100644 --- a/substrate/client/rpc-servers/src/lib.rs +++ b/substrate/client/rpc-servers/src/lib.rs @@ -23,11 +23,13 @@ pub mod middleware; pub mod utils; -use std::{error::Error as StdError, time::Duration}; +use std::{error::Error as StdError, net::SocketAddr, time::Duration}; use jsonrpsee::{ core::BoxError, - server::{serve_with_graceful_shutdown, stop_channel, ws, PingConfig, StopHandle}, + server::{ + serve_with_graceful_shutdown, stop_channel, ws, PingConfig, ServerHandle, StopHandle, + }, Methods, RpcModule, }; use middleware::NodeHealthProxyLayer; @@ -46,8 +48,38 @@ pub use utils::{RpcEndpoint, RpcMethods}; const MEGABYTE: u32 = 1024 * 1024; -/// Type alias for the JSON-RPC server. -pub type Server = jsonrpsee::server::ServerHandle; +/// Type to encapsulate the server handle and listening address. +pub struct Server { + /// Handle to the rpc server + handle: ServerHandle, + /// Listening address of the server + listen_addrs: Vec, +} + +impl Server { + /// Creates a new Server. + pub fn new(handle: ServerHandle, listen_addrs: Vec) -> Server { + Server { handle, listen_addrs } + } + + /// Returns the `jsonrpsee::server::ServerHandle` for this Server. Can be used to stop the + /// server. + pub fn handle(&self) -> &ServerHandle { + &self.handle + } + + /// The listen address for the running RPC service. + pub fn listen_addrs(&self) -> &[SocketAddr] { + &self.listen_addrs + } +} + +impl Drop for Server { + fn drop(&mut self) { + // This doesn't not wait for the server to be stopped but fires the signal. + let _ = self.handle.stop(); + } +} /// Trait for providing subscription IDs that can be cloned. pub trait SubscriptionIdProvider: @@ -273,5 +305,5 @@ where // This is to make it work with old scripts/utils that parse the logs. log::info!("Running JSON-RPC server: addr={}", format_listen_addrs(&local_addrs)); - Ok(server_handle) + Ok(Server::new(server_handle, local_addrs)) } diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index 0dc28d1361cb..28a76847ac06 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -19,7 +19,7 @@ use crate::{ build_network_future, build_system_rpc_future, client::{Client, ClientConfig}, - config::{Configuration, ExecutorConfiguration, KeystoreConfig, PrometheusConfig}, + config::{Configuration, ExecutorConfiguration, KeystoreConfig, Multiaddr, PrometheusConfig}, error::Error, metrics::MetricsService, start_rpc_servers, BuildGenesisBlock, GenesisBlockBuilder, RpcHandlers, SpawnTaskHandle, @@ -43,6 +43,7 @@ use sc_executor::{ use sc_keystore::LocalKeystore; use sc_network::{ config::{FullNetworkConfiguration, SyncMode}, + multiaddr::Protocol, service::{ traits::{PeerStore, RequestResponseConfig}, NotificationMetrics, @@ -527,13 +528,24 @@ where gen_rpc_module, rpc_id_provider, )?; + + let listen_addrs = rpc_server_handle + .listen_addrs() + .into_iter() + .map(|socket_addr| { + let mut multiaddr: Multiaddr = socket_addr.ip().into(); + multiaddr.push(Protocol::Tcp(socket_addr.port())); + multiaddr + }) + .collect(); + let in_memory_rpc = { let mut module = gen_rpc_module()?; module.extensions_mut().insert(DenyUnsafe::No); module }; - let in_memory_rpc_handle = RpcHandlers::new(Arc::new(in_memory_rpc)); + let in_memory_rpc_handle = RpcHandlers::new(Arc::new(in_memory_rpc), listen_addrs); // Spawn informant task spawn_handle.spawn( diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index 251eef97be84..babb76f022f0 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -34,6 +34,7 @@ mod client; mod metrics; mod task_manager; +use crate::config::Multiaddr; use std::{ collections::HashMap, net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, @@ -50,6 +51,7 @@ use sc_network::{ }; use sc_network_sync::SyncingService; use sc_network_types::PeerId; +use sc_rpc_server::Server; use sc_utils::mpsc::TracingUnboundedReceiver; use sp_blockchain::HeaderMetadata; use sp_consensus::SyncOracle; @@ -101,14 +103,22 @@ use tokio::runtime::Handle; const DEFAULT_PROTOCOL_ID: &str = "sup"; -/// RPC handlers that can perform RPC queries. +/// A running RPC service that can perform in-memory RPC queries. #[derive(Clone)] -pub struct RpcHandlers(Arc>); +pub struct RpcHandlers { + // This is legacy and may be removed at some point, it was for WASM stuff before smoldot was a + // thing. https://github.com/paritytech/polkadot-sdk/pull/5038#discussion_r1694971805 + rpc_module: Arc>, + + // This can be used to introspect the port the RPC server is listening on. SDK consumers are + // depending on this and it should be supported even if in-memory query support is removed. + listen_addresses: Vec, +} impl RpcHandlers { /// Create PRC handlers instance. - pub fn new(inner: Arc>) -> Self { - Self(inner) + pub fn new(rpc_module: Arc>, listen_addresses: Vec) -> Self { + Self { rpc_module, listen_addresses } } /// Starts an RPC query. @@ -130,12 +140,17 @@ impl RpcHandlers { // This limit is used to prevent panics and is large enough. const TOKIO_MPSC_MAX_SIZE: usize = tokio::sync::Semaphore::MAX_PERMITS; - self.0.raw_json_request(json_query, TOKIO_MPSC_MAX_SIZE).await + self.rpc_module.raw_json_request(json_query, TOKIO_MPSC_MAX_SIZE).await } /// Provides access to the underlying `RpcModule` pub fn handle(&self) -> Arc> { - self.0.clone() + self.rpc_module.clone() + } + + /// Provides access to listen addresses + pub fn listen_addresses(&self) -> &[Multiaddr] { + &self.listen_addresses[..] } } @@ -363,20 +378,6 @@ pub async fn build_system_rpc_future< debug!("`NetworkWorker` has terminated, shutting down the system RPC future."); } -// Wrapper for HTTP and WS servers that makes sure they are properly shut down. -mod waiting { - pub struct Server(pub Option); - - impl Drop for Server { - fn drop(&mut self) { - if let Some(server) = self.0.take() { - // This doesn't not wait for the server to be stopped but fires the signal. - let _ = server.stop(); - } - } - } -} - /// Starts RPC servers. pub fn start_rpc_servers( rpc_configuration: &RpcConfiguration, @@ -384,7 +385,7 @@ pub fn start_rpc_servers( tokio_handle: &Handle, gen_rpc_module: R, rpc_id_provider: Option>, -) -> Result, error::Error> +) -> Result where R: Fn() -> Result, Error>, { @@ -451,7 +452,7 @@ where match tokio::task::block_in_place(|| { tokio_handle.block_on(sc_rpc_server::start_server(server_config)) }) { - Ok(server) => Ok(Box::new(waiting::Server(Some(server)))), + Ok(server) => Ok(server), Err(e) => Err(Error::Application(e)), } } From f4eb41773611008040c9d4d8a8e6b7323eccfca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 9 Sep 2024 14:07:43 +0200 Subject: [PATCH 330/382] pallet-utility: Improve weight annotations (#5644) Prevent allocations when calculating the weights. --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi --- prdoc/pr_5644.prdoc | 8 +++ substrate/frame/utility/src/lib.rs | 80 ++++++++++++------------------ 2 files changed, 39 insertions(+), 49 deletions(-) create mode 100644 prdoc/pr_5644.prdoc diff --git a/prdoc/pr_5644.prdoc b/prdoc/pr_5644.prdoc new file mode 100644 index 000000000000..3300d557fce4 --- /dev/null +++ b/prdoc/pr_5644.prdoc @@ -0,0 +1,8 @@ +title: 'pallet-utility: Improve weight annotations' +doc: +- audience: Runtime Dev + description: |- + Prevent allocations when calculating the weights. +crates: +- name: pallet-utility + bump: patch diff --git a/substrate/frame/utility/src/lib.rs b/substrate/frame/utility/src/lib.rs index 3ce5b4ff8649..ed5544fe55ca 100644 --- a/substrate/frame/utility/src/lib.rs +++ b/substrate/frame/utility/src/lib.rs @@ -74,7 +74,7 @@ pub use pallet::*; #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; + use frame_support::{dispatch::DispatchClass, pallet_prelude::*}; use frame_system::pallet_prelude::*; #[pallet::pallet] @@ -183,21 +183,8 @@ pub mod pallet { /// event is deposited. #[pallet::call_index(0)] #[pallet::weight({ - let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::>(); - let dispatch_weight = dispatch_infos.iter() - .map(|di| di.weight) - .fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight)) - .saturating_add(T::WeightInfo::batch(calls.len() as u32)); - let dispatch_class = { - let all_operational = dispatch_infos.iter() - .map(|di| di.class) - .all(|class| class == DispatchClass::Operational); - if all_operational { - DispatchClass::Operational - } else { - DispatchClass::Normal - } - }; + let (dispatch_weight, dispatch_class) = Pallet::::weight_and_dispatch_class(&calls); + let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::batch(calls.len() as u32)); (dispatch_weight, dispatch_class) })] pub fn batch( @@ -233,13 +220,13 @@ pub mod pallet { // Take the weight of this function itself into account. let base_weight = T::WeightInfo::batch(index.saturating_add(1) as u32); // Return the actual used weight + base_weight of this call. - return Ok(Some(base_weight + weight).into()) + return Ok(Some(base_weight.saturating_add(weight)).into()) } Self::deposit_event(Event::ItemCompleted); } Self::deposit_event(Event::BatchCompleted); let base_weight = T::WeightInfo::batch(calls_len as u32); - Ok(Some(base_weight + weight).into()) + Ok(Some(base_weight.saturating_add(weight)).into()) } /// Send a call through an indexed pseudonym of the sender. @@ -305,21 +292,8 @@ pub mod pallet { /// - O(C) where C is the number of calls to be batched. #[pallet::call_index(2)] #[pallet::weight({ - let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::>(); - let dispatch_weight = dispatch_infos.iter() - .map(|di| di.weight) - .fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight)) - .saturating_add(T::WeightInfo::batch_all(calls.len() as u32)); - let dispatch_class = { - let all_operational = dispatch_infos.iter() - .map(|di| di.class) - .all(|class| class == DispatchClass::Operational); - if all_operational { - DispatchClass::Operational - } else { - DispatchClass::Normal - } - }; + let (dispatch_weight, dispatch_class) = Pallet::::weight_and_dispatch_class(&calls); + let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::batch_all(calls.len() as u32)); (dispatch_weight, dispatch_class) })] pub fn batch_all( @@ -359,7 +333,7 @@ pub mod pallet { // Take the weight of this function itself into account. let base_weight = T::WeightInfo::batch_all(index.saturating_add(1) as u32); // Return the actual used weight + base_weight of this call. - err.post_info = Some(base_weight + weight).into(); + err.post_info = Some(base_weight.saturating_add(weight)).into(); err })?; Self::deposit_event(Event::ItemCompleted); @@ -414,21 +388,8 @@ pub mod pallet { /// - O(C) where C is the number of calls to be batched. #[pallet::call_index(4)] #[pallet::weight({ - let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::>(); - let dispatch_weight = dispatch_infos.iter() - .map(|di| di.weight) - .fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight)) - .saturating_add(T::WeightInfo::force_batch(calls.len() as u32)); - let dispatch_class = { - let all_operational = dispatch_infos.iter() - .map(|di| di.class) - .all(|class| class == DispatchClass::Operational); - if all_operational { - DispatchClass::Operational - } else { - DispatchClass::Normal - } - }; + let (dispatch_weight, dispatch_class) = Pallet::::weight_and_dispatch_class(&calls); + let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::force_batch(calls.len() as u32)); (dispatch_weight, dispatch_class) })] pub fn force_batch( @@ -494,6 +455,27 @@ pub mod pallet { res.map(|_| ()).map_err(|e| e.error) } } + + impl Pallet { + /// Get the accumulated `weight` and the dispatch class for the given `calls`. + fn weight_and_dispatch_class( + calls: &[::RuntimeCall], + ) -> (Weight, DispatchClass) { + let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()); + let (dispatch_weight, dispatch_class) = dispatch_infos.fold( + (Weight::zero(), DispatchClass::Operational), + |(total_weight, dispatch_class): (Weight, DispatchClass), di| { + ( + total_weight.saturating_add(di.weight), + // If not all are `Operational`, we want to use `DispatchClass::Normal`. + if di.class == DispatchClass::Normal { di.class } else { dispatch_class }, + ) + }, + ); + + (dispatch_weight, dispatch_class) + } + } } /// A pallet identifier. These are per pallet and should be stored in a registry somewhere. From 030cb4a71b0b390626a586bfe7117b7c66b4700c Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 9 Sep 2024 16:27:22 +0300 Subject: [PATCH 331/382] Allow to disable gap creation during block import (#5343) This feature is helpful for us with custom sync protocol that is similar to Warp sync except we do not ever sync the gap and don't want it to exist in the first place (see https://github.com/paritytech/polkadot-sdk/issues/5333 and its references for motivation). Otherwise we had to resort to this: https://github.com/autonomys/polkadot-sdk/commit/d5375125ca7c59fcc1ac72dc1b2ac251cbc80323 --------- Co-authored-by: Davide Galassi --- prdoc/pr_5343.prdoc | 19 +++++++++++++++++++ substrate/client/api/src/backend.rs | 3 +++ substrate/client/api/src/in_mem.rs | 2 ++ .../consensus/common/src/block_import.rs | 3 +++ substrate/client/db/src/lib.rs | 11 +++++++++-- substrate/client/service/src/client/client.rs | 3 +++ 6 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_5343.prdoc diff --git a/prdoc/pr_5343.prdoc b/prdoc/pr_5343.prdoc new file mode 100644 index 000000000000..3cec70de93cb --- /dev/null +++ b/prdoc/pr_5343.prdoc @@ -0,0 +1,19 @@ +title: Allow to disable gap creation during block import + +doc: + - audience: Node Dev + description: | + New property `BlockImportParams::create_gap` allows to change whether to create block gap in case block + has no parent (defaults to `true` keeping existing behavior), which is helpful for sync protocols that do not need + to sync the gap after this happens. `BlockImportOperation::create_gap()` method was also introduced, though in + most cases `BlockImportParams::create_gap` will be used. + +crates: + - name: sc-client-api + bump: major + - name: sc-consensus + bump: minor + - name: sc-client-db + bump: minor + - name: sc-service + bump: minor diff --git a/substrate/client/api/src/backend.rs b/substrate/client/api/src/backend.rs index 0b2a34952401..9c9601a912ac 100644 --- a/substrate/client/api/src/backend.rs +++ b/substrate/client/api/src/backend.rs @@ -232,6 +232,9 @@ pub trait BlockImportOperation { /// Add a transaction index operation. fn update_transaction_index(&mut self, index: Vec) -> sp_blockchain::Result<()>; + + /// Configure whether to create a block gap if newly imported block is missing parent + fn set_create_gap(&mut self, create_gap: bool); } /// Interface for performing operations on the backend. diff --git a/substrate/client/api/src/in_mem.rs b/substrate/client/api/src/in_mem.rs index ba89aede9147..c045a393bb21 100644 --- a/substrate/client/api/src/in_mem.rs +++ b/substrate/client/api/src/in_mem.rs @@ -584,6 +584,8 @@ impl backend::BlockImportOperation for BlockImportOperatio ) -> sp_blockchain::Result<()> { Ok(()) } + + fn set_create_gap(&mut self, _create_gap: bool) {} } /// In-memory backend. Keeps all states and blocks in memory. diff --git a/substrate/client/consensus/common/src/block_import.rs b/substrate/client/consensus/common/src/block_import.rs index 4d7b89f37d86..0fcf96a96368 100644 --- a/substrate/client/consensus/common/src/block_import.rs +++ b/substrate/client/consensus/common/src/block_import.rs @@ -214,6 +214,8 @@ pub struct BlockImportParams { pub fork_choice: Option, /// Re-validate existing block. pub import_existing: bool, + /// Whether to create "block gap" in case this block doesn't have parent. + pub create_gap: bool, /// Cached full header hash (with post-digests applied). pub post_hash: Option, } @@ -234,6 +236,7 @@ impl BlockImportParams { auxiliary: Vec::new(), fork_choice: None, import_existing: false, + create_gap: true, post_hash: None, } } diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs index 4559a01e57e3..72707c306f58 100644 --- a/substrate/client/db/src/lib.rs +++ b/substrate/client/db/src/lib.rs @@ -834,6 +834,7 @@ pub struct BlockImportOperation { finalized_blocks: Vec<(Block::Hash, Option)>, set_head: Option, commit_state: bool, + create_gap: bool, index_ops: Vec, } @@ -988,6 +989,10 @@ impl sc_client_api::backend::BlockImportOperation self.index_ops = index_ops; Ok(()) } + + fn set_create_gap(&mut self, create_gap: bool) { + self.create_gap = create_gap; + } } struct StorageDb { @@ -1709,8 +1714,9 @@ impl Backend { unreachable!("Unsupported block gap. TODO: https://github.com/paritytech/polkadot-sdk/issues/5406") }, } - } else if number > best_num + One::one() && - number > One::one() && self.blockchain.header(parent_hash)?.is_none() + } else if operation.create_gap && + number > best_num + One::one() && + self.blockchain.header(parent_hash)?.is_none() { let gap = BlockGap { start: best_num + One::one(), @@ -2072,6 +2078,7 @@ impl sc_client_api::backend::Backend for Backend { finalized_blocks: Vec::new(), set_head: None, commit_state: false, + create_gap: true, index_ops: Default::default(), }) } diff --git a/substrate/client/service/src/client/client.rs b/substrate/client/service/src/client/client.rs index 8b699c7faffd..ce5b92551bf2 100644 --- a/substrate/client/service/src/client/client.rs +++ b/substrate/client/service/src/client/client.rs @@ -513,6 +513,7 @@ where fork_choice, intermediates, import_existing, + create_gap, .. } = import_block; @@ -537,6 +538,8 @@ where *self.importing_block.write() = Some(hash); + operation.op.set_create_gap(create_gap); + let result = self.execute_and_import_block( operation, origin, From 2d4e89f091c103813e462bd9cb188473b2673708 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 9 Sep 2024 17:12:51 +0200 Subject: [PATCH 332/382] [Bot] Revive prdoc bot (#5648) Prdoc bot was deleted in https://github.com/paritytech/polkadot-sdk/pull/5457 just after being added in https://github.com/paritytech/polkadot-sdk/pull/5331 without replacement. Now bringing it back until it is integrated into the new command structure. Formatting is now also fixed, such that the title is always first and the description renders correctly. --------- Signed-off-by: Oliver Tale-Yazdi --- .github/scripts/generate-prdoc.py | 26 +++++++-- .github/workflows/command-prdoc.yml | 90 +++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/command-prdoc.yml diff --git a/.github/scripts/generate-prdoc.py b/.github/scripts/generate-prdoc.py index ba7def20fcb9..a6a97008dca6 100644 --- a/.github/scripts/generate-prdoc.py +++ b/.github/scripts/generate-prdoc.py @@ -48,9 +48,8 @@ def create_prdoc(pr, audience, title, description, patch, bump, force): else: print(f"No preexisting PrDoc for PR {pr}") - prdoc = { "doc": [{}], "crates": [] } + prdoc = { "title": title, "doc": [{}], "crates": [] } - prdoc["title"] = title prdoc["doc"][0]["audience"] = audience prdoc["doc"][0]["description"] = description @@ -58,13 +57,19 @@ def create_prdoc(pr, audience, title, description, patch, bump, force): modified_paths = [] for diff in whatthepatch.parse_patch(patch): - modified_paths.append(diff.header.new_path) + new_path = diff.header.new_path + # Sometimes this lib returns `/dev/null` as the new path... + if not new_path.startswith("/dev"): + modified_paths.append(new_path) modified_crates = {} for p in modified_paths: # Go up until we find a Cargo.toml p = os.path.join(workspace.path, p) while not os.path.exists(os.path.join(p, "Cargo.toml")): + print(f"Could not find Cargo.toml in {p}") + if p == '/': + exit(1) p = os.path.dirname(p) with open(os.path.join(p, "Cargo.toml")) as f: @@ -95,9 +100,19 @@ def create_prdoc(pr, audience, title, description, patch, bump, force): # write the parsed PR documentation back to the file with open(path, "w") as f: - yaml.dump(prdoc, f) + yaml.dump(prdoc, f, sort_keys=False) print(f"PrDoc for PR {pr} written to {path}") +# Make the `description` a multiline string instead of escaping \r\n. +def setup_yaml(): + def yaml_multiline_string_presenter(dumper, data): + if len(data.splitlines()) > 1: + data = '\n'.join([line.rstrip() for line in data.strip().splitlines()]) + return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|') + return dumper.represent_scalar('tag:yaml.org,2002:str', data) + + yaml.add_representer(str, yaml_multiline_string_presenter) + def parse_args(): parser = argparse.ArgumentParser() parser.add_argument("--pr", type=int, required=True) @@ -108,6 +123,7 @@ def parse_args(): if __name__ == "__main__": args = parse_args() - force = True if args.force.lower() == "true" else False + force = True if (args.force or "false").lower() == "true" else False print(f"Args: {args}, force: {force}") + setup_yaml() from_pr_number(args.pr, args.audience, args.bump, force) diff --git a/.github/workflows/command-prdoc.yml b/.github/workflows/command-prdoc.yml new file mode 100644 index 000000000000..3a08b9a5fb28 --- /dev/null +++ b/.github/workflows/command-prdoc.yml @@ -0,0 +1,90 @@ +name: Command PrDoc + +on: + workflow_dispatch: + inputs: + pr: + type: number + description: Number of the Pull Request + required: true + bump: + type: choice + description: Default bump level for all crates + default: "TODO" + required: true + options: + - "TODO" + - "no change" + - "patch" + - "minor" + - "major" + audience: + type: choice + description: Audience of the PrDoc + default: "TODO" + required: true + options: + - "TODO" + - "Runtime Dev" + - "Runtime User" + - "Node Dev" + - "Node User" + overwrite: + type: choice + description: Overwrite existing PrDoc + default: "true" + required: true + options: + - "true" + - "false" + +concurrency: + group: command-prdoc + cancel-in-progress: true + +jobs: + set-image: + runs-on: ubuntu-latest + outputs: + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT + cmd-prdoc: + needs: [set-image] + runs-on: ubuntu-latest + timeout-minutes: 20 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + permissions: + contents: write + pull-requests: write + steps: + - name: Download repo + uses: actions/checkout@v4 + - name: Install gh cli + id: gh + uses: ./.github/actions/set-up-gh + with: + pr-number: ${{ inputs.pr }} + GH_TOKEN: ${{ github.token }} + - name: Generate PrDoc + run: | + python3 -m pip install -q cargo-workspace PyGithub whatthepatch pyyaml toml + + python3 .github/scripts/generate-prdoc.py --pr "${{ inputs.pr }}" --bump "${{ inputs.bump }}" --audience "${{ inputs.audience }}" --force "${{ inputs.overwrite }}" + + - name: Report failure + if: ${{ failure() }} + run: gh pr comment ${{ inputs.pr }} --body "

Command failed ❌

Run by @${{ github.actor }} for ${{ github.workflow }} failed. See logs here." + env: + RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_TOKEN: ${{ github.token }} + - name: Push Commit + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: Add PrDoc (auto generated) + branch: ${{ steps.gh.outputs.branch }} + file_pattern: 'prdoc/*.prdoc' From 100c779e0d1be938dfe9ad1bdb4a80ac8b743dc7 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 9 Sep 2024 23:50:31 +0800 Subject: [PATCH 333/382] Revert "Revert "Bump package as minor"" This reverts commit 9761118c357a15666743a84507fea168d9580961. --- prdoc/pr_5546.prdoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc index 16e2bd993463..44c8ee177a6f 100644 --- a/prdoc/pr_5546.prdoc +++ b/prdoc/pr_5546.prdoc @@ -13,11 +13,11 @@ crates: - name: snowbridge-pallet-outbound-queue bump: patch - name: snowbridge-pallet-system - bump: major + bump: minor - name: snowbridge-core - bump: major + bump: minor - name: snowbridge-router-primitives - bump: major + bump: minor - name: bridge-hub-westend-runtime bump: patch - name: bridge-hub-rococo-runtime From de6d3ee61759711c10f4a88d4c8eaffe78edf1d7 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 9 Sep 2024 23:52:52 +0800 Subject: [PATCH 334/382] Ignore validate check --- prdoc/pr_5546.prdoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc index 44c8ee177a6f..95f02dbe13b2 100644 --- a/prdoc/pr_5546.prdoc +++ b/prdoc/pr_5546.prdoc @@ -14,10 +14,13 @@ crates: bump: patch - name: snowbridge-pallet-system bump: minor + validate: false - name: snowbridge-core bump: minor + validate: false - name: snowbridge-router-primitives bump: minor + validate: false - name: bridge-hub-westend-runtime bump: patch - name: bridge-hub-rococo-runtime From 116c421d1d56dc43923d6af01b5d5f7b38678099 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 10 Sep 2024 00:00:42 +0800 Subject: [PATCH 335/382] Rename as EthereumUniversalLocation be more clear --- .../primitives/router/src/inbound/mod.rs | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 4aa721728449..49bb731e5e77 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -102,14 +102,14 @@ pub struct MessageToXcm< AccountId, Balance, ConvertAssetId, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHubLocation, > where CreateAssetCall: Get, CreateAssetDeposit: Get, Balance: BalanceT, ConvertAssetId: MaybeEquivalence, - UniversalLocation: Get, + EthereumUniversalLocation: Get, GlobalAssetHubLocation: Get, { _phantom: PhantomData<( @@ -119,7 +119,7 @@ pub struct MessageToXcm< AccountId, Balance, ConvertAssetId, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHubLocation, )>, } @@ -156,7 +156,7 @@ impl< AccountId, Balance, ConvertAssetId, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHubLocation, > ConvertMessage for MessageToXcm< @@ -166,16 +166,17 @@ impl< AccountId, Balance, ConvertAssetId, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHubLocation, - > where + > +where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, ConvertAssetId: MaybeEquivalence, - UniversalLocation: Get, + EthereumUniversalLocation: Get, GlobalAssetHubLocation: Get, { type Balance = Balance; @@ -214,7 +215,7 @@ impl< AccountId, Balance, ConvertAssetId, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHubLocation, > MessageToXcm< @@ -224,16 +225,17 @@ impl< AccountId, Balance, ConvertAssetId, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHubLocation, - > where + > +where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, ConvertAssetId: MaybeEquivalence, - UniversalLocation: Get, + EthereumUniversalLocation: Get, GlobalAssetHubLocation: Get, { fn convert_register_token( @@ -416,7 +418,7 @@ impl< let mut reanchored_asset_loc = asset_loc.clone(); reanchored_asset_loc - .reanchor(&GlobalAssetHubLocation::get(), &UniversalLocation::get()) + .reanchor(&GlobalAssetHubLocation::get(), &EthereumUniversalLocation::get()) .map_err(|_| ConvertMessageError::CannotReanchor)?; let asset: Asset = (reanchored_asset_loc, amount).into(); @@ -479,7 +481,8 @@ impl< // Forward message id to Asset Hub. instructions.push(SetTopic(message_id.into())); - // `total_fees` to burn on this chain when sending `instructions` to run on AH (which also teleport fees) + // `total_fees` to burn on this chain when sending `instructions` to run on AH (which also + // teleport fees) Ok((instructions.into(), total_fees.into())) } } From 5203dc02b59d4743bca9633b3b6bcc7e37b7e883 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 10 Sep 2024 00:40:00 +0800 Subject: [PATCH 336/382] Switch to using half the fee for local exec and half for transport --- .../snowbridge/primitives/router/src/inbound/mod.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 49bb731e5e77..6c107cfa07c2 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -168,8 +168,7 @@ impl< ConvertAssetId, EthereumUniversalLocation, GlobalAssetHubLocation, - > -where + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, @@ -227,8 +226,7 @@ impl< ConvertAssetId, EthereumUniversalLocation, GlobalAssetHubLocation, - > -where + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, @@ -396,7 +394,7 @@ where asset_hub_fee: u128, ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { let network = Ethereum { chain_id }; - let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee / 2).into(); let (dest_para_id, beneficiary, dest_para_fee) = match destination { // Final destination is a 32-byte account on AssetHub @@ -440,10 +438,6 @@ where let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); instructions.extend(vec![ - // `SetFeesMode` to pay transport fee from bridge sovereign, which depends on - // unspent AH fees deposited to the bridge sovereign, - // more context and analysis in https://github.com/paritytech/polkadot-sdk/pull/5546#discussion_r1744682864 - SetFeesMode { jit_withdraw: true }, // `SetAppendix` ensures that `fees` are not trapped in any case SetAppendix(Xcm(vec![DepositAsset { assets: AllCounted(2).into(), From d2e962fc1ec5c33b33ca39029d9a525404048491 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 9 Sep 2024 19:21:51 +0200 Subject: [PATCH 337/382] Install prdoc from Parity fork (#5625) Prdoc is now published as a Parity fork under the [`parity-prdoc`](https://crates.io/crates/parity-prdoc) crate after the directions diverged from the ideas of the original creator (discussions [here](https://github.com/paritytech/prdoc/pull/40) and [here](https://github.com/paritytech/prdoc/issues/36)). Now updating the install instructions here. --------- Signed-off-by: Oliver Tale-Yazdi --- .github/workflows/check-prdoc.yml | 2 +- docs/contributor/prdoc.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-prdoc.yml b/.github/workflows/check-prdoc.yml index 69311c41dd6f..6c8f1ed7a300 100644 --- a/.github/workflows/check-prdoc.yml +++ b/.github/workflows/check-prdoc.yml @@ -6,7 +6,7 @@ on: merge_group: env: - IMAGE: docker.io/paritytech/prdoc:v0.0.8 + IMAGE: docker.io/paritytech/prdoc:v0.1.1 API_BASE: https://api.github.com/repos REPO: ${{ github.repository }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/contributor/prdoc.md b/docs/contributor/prdoc.md index 0c8165af40f4..3dddcc10f5d0 100644 --- a/docs/contributor/prdoc.md +++ b/docs/contributor/prdoc.md @@ -14,7 +14,7 @@ the [CODEOWNERS](../../.github/CODEOWNERS) for advice. A `.prdoc` file is a YAML file with a defined structure (ie JSON Schema). Please follow these steps to generate one: -1. Install the [`prdoc` CLI](https://github.com/paritytech/prdoc) by running `cargo install prdoc`. +1. Install the [`prdoc` CLI](https://github.com/paritytech/prdoc) by running `cargo install parity-prdoc`. 1. Open a Pull Request and get the PR number. 1. Generate the file with `prdoc generate `. The output filename will be printed. 1. Optional: Install the `prdoc/schema_user.json` schema in your editor, for example From f3f377f5ec7009036a33a822fdbb8439ca696ca7 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 9 Sep 2024 22:25:42 +0200 Subject: [PATCH 338/382] [pallet-revive] move event topics in event's body (#5640) Fix https://github.com/paritytech/polkadot-sdk/issues/5629 --- prdoc/pr_5640.prdoc | 10 ++++++++++ substrate/frame/revive/src/benchmarking/mod.rs | 6 +----- substrate/frame/revive/src/exec.rs | 10 ++++------ substrate/frame/revive/src/lib.rs | 11 +++-------- substrate/frame/revive/src/tests.rs | 5 +++-- 5 files changed, 21 insertions(+), 21 deletions(-) create mode 100644 prdoc/pr_5640.prdoc diff --git a/prdoc/pr_5640.prdoc b/prdoc/pr_5640.prdoc new file mode 100644 index 000000000000..fdd7f5e1b893 --- /dev/null +++ b/prdoc/pr_5640.prdoc @@ -0,0 +1,10 @@ +title: "[pallet-revive] Move event's topics" + +doc: + - audience: Runtime Dev + description: | + Move event's topics inside body + +crates: + - name: pallet-revive + bump: major diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index 8cdd7da5db9d..8601f5f53542 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -772,11 +772,7 @@ mod benchmarks { assert_eq!( record.event, - crate::Event::ContractEmitted { contract: instance.address(), data }.into(), - ); - assert_eq!( - record.topics.iter().map(|t| H256::from_slice(t.as_ref())).collect::>(), - topics, + crate::Event::ContractEmitted { contract: instance.address(), data, topics }.into(), ); } diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 016bdec37afd..468f5aa8240e 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -1487,13 +1487,11 @@ where } fn deposit_event(&mut self, topics: Vec, data: Vec) { - Contracts::::deposit_indexed_event( + Contracts::::deposit_event(Event::ContractEmitted { + contract: T::AddressMapper::to_address(self.account_id()), + data, topics, - Event::ContractEmitted { - contract: T::AddressMapper::to_address(self.account_id()), - data, - }, - ); + }); } fn block_number(&self) -> U256 { diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 4c6e5cd26a11..d1e17fb7b390 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -406,6 +406,9 @@ pub mod pallet { /// Data supplied by the contract. Metadata generated during contract compilation /// is needed to decode it. data: Vec, + /// A list of topics used to index the event. + /// Number of topics is capped by [`limits::NUM_EVENT_TOPICS`]. + topics: Vec, }, /// A code with the specified hash was removed. @@ -1186,14 +1189,6 @@ where fn deposit_event(event: Event) { >::deposit_event(::RuntimeEvent::from(event)) } - - /// Deposit a pallet contracts indexed event. - fn deposit_indexed_event(topics: Vec, event: Event) { - >::deposit_event_indexed( - &topics.into_iter().map(Into::into).collect::>(), - ::RuntimeEvent::from(event).into(), - ) - } } // Set up a global reference to the boolean flag used for the re-entrancy guard. diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index f2944c7932a6..73914c9aae07 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -678,9 +678,10 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::ContractEmitted { contract: addr, - data: vec![1, 2, 3, 4] + data: vec![1, 2, 3, 4], + topics: vec![H256::repeat_byte(42)], }), - topics: vec![H256::repeat_byte(42)], + topics: vec![], }, EventRecord { phase: Phase::Initialization, From aec2b10539251fc20450f8efa453f21dee6b95a1 Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Tue, 10 Sep 2024 07:31:11 +0900 Subject: [PATCH 339/382] frame pallet macro: fix span for error on wrong returned type. (#5580) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explicitly give the types in some generated code so that the error shows up good when user code is wrong. --------- Co-authored-by: Bastian Köcher --- prdoc/pr_5580.prdoc | 13 +++++++ .../procedural/src/pallet/expand/call.rs | 36 +++++++++++++----- .../procedural/src/pallet/parse/call.rs | 11 ++---- .../procedural/src/pallet/parse/helper.rs | 23 +++++++++--- .../tests/pallet_ui/call_span_for_error.rs | 37 +++++++++++++++++++ .../pallet_ui/call_span_for_error.stderr | 26 +++++++++++++ 6 files changed, 125 insertions(+), 21 deletions(-) create mode 100644 prdoc/pr_5580.prdoc create mode 100644 substrate/frame/support/test/tests/pallet_ui/call_span_for_error.rs create mode 100644 substrate/frame/support/test/tests/pallet_ui/call_span_for_error.stderr diff --git a/prdoc/pr_5580.prdoc b/prdoc/pr_5580.prdoc new file mode 100644 index 000000000000..e03b946070aa --- /dev/null +++ b/prdoc/pr_5580.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix error message on pallet macro + +doc: + - audience: Runtime Dev + description: | + Improve error message for pallet macro generated code. + +crates: + - name: frame-support-procedural + bump: patch diff --git a/substrate/frame/support/procedural/src/pallet/expand/call.rs b/substrate/frame/support/procedural/src/pallet/expand/call.rs index f395872c8a80..5dc8dc3146cf 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/call.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/call.rs @@ -18,7 +18,7 @@ use crate::{ pallet::{ expand::warnings::{weight_constant_warning, weight_witness_warning}, - parse::call::CallWeightDef, + parse::{call::CallWeightDef, helper::CallReturnType}, Def, }, COUNTER, @@ -197,18 +197,36 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; // Wrap all calls inside of storage layers - if let Some(syn::Item::Impl(item_impl)) = def - .call - .as_ref() - .map(|c| &mut def.item.content.as_mut().expect("Checked by def parser").1[c.index]) - { - item_impl.items.iter_mut().for_each(|i| { - if let syn::ImplItem::Fn(method) = i { + if let Some(call) = def.call.as_ref() { + let item_impl = + &mut def.item.content.as_mut().expect("Checked by def parser").1[call.index]; + let syn::Item::Impl(item_impl) = item_impl else { + unreachable!("Checked by def parser"); + }; + + item_impl.items.iter_mut().enumerate().for_each(|(i, item)| { + if let syn::ImplItem::Fn(method) = item { + let return_type = + &call.methods.get(i).expect("def should be consistent with item").return_type; + + let (ok_type, err_type) = match return_type { + CallReturnType::DispatchResult => ( + quote::quote!(()), + quote::quote!(#frame_support::pallet_prelude::DispatchError), + ), + CallReturnType::DispatchResultWithPostInfo => ( + quote::quote!(#frame_support::dispatch::PostDispatchInfo), + quote::quote!(#frame_support::dispatch::DispatchErrorWithPostInfo), + ), + }; + let block = &method.block; method.block = syn::parse_quote! {{ // We execute all dispatchable in a new storage layer, allowing them // to return an error at any point, and undoing any storage changes. - #frame_support::storage::with_storage_layer(|| #block) + #frame_support::storage::with_storage_layer::<#ok_type, #err_type, _>( + || #block + ) }}; } }); diff --git a/substrate/frame/support/procedural/src/pallet/parse/call.rs b/substrate/frame/support/procedural/src/pallet/parse/call.rs index 4e09b86fddec..68c2cb8bd1b3 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/call.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/call.rs @@ -89,6 +89,8 @@ pub struct CallVariantDef { pub cfg_attrs: Vec, /// The optional `feeless_if` attribute on the `pallet::call`. pub feeless_check: Option, + /// The return type of the call: `DispatchInfo` or `DispatchResultWithPostInfo`. + pub return_type: helper::CallReturnType, } /// Attributes for functions in call impl block. @@ -260,13 +262,7 @@ impl CallDef { }, } - if let syn::ReturnType::Type(_, type_) = &method.sig.output { - helper::check_pallet_call_return_type(type_)?; - } else { - let msg = "Invalid pallet::call, require return type \ - DispatchResultWithPostInfo"; - return Err(syn::Error::new(method.sig.span(), msg)) - } + let return_type = helper::check_pallet_call_return_type(&method.sig)?; let cfg_attrs: Vec = helper::get_item_cfg_attrs(&method.attrs); let mut call_idx_attrs = vec![]; @@ -447,6 +443,7 @@ impl CallDef { attrs: method.attrs.clone(), cfg_attrs, feeless_check, + return_type, }); } else { let msg = "Invalid pallet::call, only method accepted"; diff --git a/substrate/frame/support/procedural/src/pallet/parse/helper.rs b/substrate/frame/support/procedural/src/pallet/parse/helper.rs index d4f58a4c56df..d5ae607d90f9 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/helper.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/helper.rs @@ -597,25 +597,38 @@ pub fn check_type_value_gen( Ok(i) } +/// The possible return type of a dispatchable. +#[derive(Clone)] +pub enum CallReturnType { + DispatchResult, + DispatchResultWithPostInfo, +} + /// Check the keyword `DispatchResultWithPostInfo` or `DispatchResult`. -pub fn check_pallet_call_return_type(type_: &syn::Type) -> syn::Result<()> { - pub struct Checker; +pub fn check_pallet_call_return_type(sig: &syn::Signature) -> syn::Result { + let syn::ReturnType::Type(_, type_) = &sig.output else { + let msg = "Invalid pallet::call, require return type \ + DispatchResultWithPostInfo"; + return Err(syn::Error::new(sig.span(), msg)) + }; + + pub struct Checker(CallReturnType); impl syn::parse::Parse for Checker { fn parse(input: syn::parse::ParseStream) -> syn::Result { let lookahead = input.lookahead1(); if lookahead.peek(keyword::DispatchResultWithPostInfo) { input.parse::()?; - Ok(Self) + Ok(Self(CallReturnType::DispatchResultWithPostInfo)) } else if lookahead.peek(keyword::DispatchResult) { input.parse::()?; - Ok(Self) + Ok(Self(CallReturnType::DispatchResult)) } else { Err(lookahead.error()) } } } - syn::parse2::(type_.to_token_stream()).map(|_| ()) + syn::parse2::(type_.to_token_stream()).map(|c| c.0) } pub(crate) fn two128_str(s: &str) -> TokenStream { diff --git a/substrate/frame/support/test/tests/pallet_ui/call_span_for_error.rs b/substrate/frame/support/test/tests/pallet_ui/call_span_for_error.rs new file mode 100644 index 000000000000..08b42c29a68b --- /dev/null +++ b/substrate/frame/support/test/tests/pallet_ui/call_span_for_error.rs @@ -0,0 +1,37 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[frame_support::pallet(dev_mode)] +mod pallet { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + pub fn foo(origin: OriginFor) -> DispatchResultWithPostInfo { + return Err(DispatchError::BadOrigin); + } + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/pallet_ui/call_span_for_error.stderr b/substrate/frame/support/test/tests/pallet_ui/call_span_for_error.stderr new file mode 100644 index 000000000000..8f3003c02227 --- /dev/null +++ b/substrate/frame/support/test/tests/pallet_ui/call_span_for_error.stderr @@ -0,0 +1,26 @@ +error[E0308]: mismatched types + --> tests/pallet_ui/call_span_for_error.rs:32:15 + | +32 | return Err(DispatchError::BadOrigin); + | --- ^^^^^^^^^^^^^^^^^^^^^^^^ expected `DispatchErrorWithPostInfo`, found `DispatchError` + | | + | arguments to this enum variant are incorrect + | + = note: expected struct `DispatchErrorWithPostInfo` + found enum `frame_support::pallet_prelude::DispatchError` +help: the type constructed contains `frame_support::pallet_prelude::DispatchError` due to the type of the argument passed + --> tests/pallet_ui/call_span_for_error.rs:32:11 + | +32 | return Err(DispatchError::BadOrigin); + | ^^^^------------------------^ + | | + | this argument influences the type of `Err` +note: tuple variant defined here + --> $RUST/core/src/result.rs + | + | Err(#[stable(feature = "rust1", since = "1.0.0")] E), + | ^^^ +help: call `Into::into` on this expression to convert `frame_support::pallet_prelude::DispatchError` into `DispatchErrorWithPostInfo` + | +32 | return Err(DispatchError::BadOrigin.into()); + | +++++++ From 9930d2137fcbc55e32476627ec2e30071b393533 Mon Sep 17 00:00:00 2001 From: Vedhavyas Singareddi Date: Tue, 10 Sep 2024 11:35:41 +0530 Subject: [PATCH 340/382] Update `RuntimeVerison` type and use `system_version` to derive extrinsics root `StateVersion` instead of `V0` (#4257) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR - Renames `RuntimeVersion::state_version` to `system_version` - Uses `Runtime::system_version` to derive extrinsics root `StateVersion` instead of default `StateVersion::V0` This PR should not be breaking any existing chains so long as they use same `RuntimeVersion::state_version` for `Runtime::system_version` Using `RuntimeVersion::system_version = 2` will make the extrinsics root to use `StateVersion::V1` instead of `V0` RFC for this change - https://github.com/polkadot-fellows/RFCs/pull/42 --------- Co-authored-by: Bastian Köcher Co-authored-by: Koute Co-authored-by: Nazar Mokrynskyi --- Cargo.lock | 1 + cumulus/client/network/src/tests.rs | 2 +- cumulus/client/pov-recovery/src/tests.rs | 2 +- cumulus/pallets/parachain-system/src/mock.rs | 2 +- .../assets/asset-hub-rococo/src/lib.rs | 2 +- .../assets/asset-hub-westend/src/lib.rs | 2 +- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 2 +- .../bridge-hubs/bridge-hub-westend/src/lib.rs | 2 +- .../collectives-westend/src/lib.rs | 2 +- .../contracts/contracts-rococo/src/lib.rs | 2 +- .../coretime/coretime-rococo/src/lib.rs | 2 +- .../coretime/coretime-westend/src/lib.rs | 2 +- .../glutton/glutton-westend/src/lib.rs | 2 +- .../runtimes/people/people-rococo/src/lib.rs | 2 +- .../runtimes/people/people-westend/src/lib.rs | 2 +- .../runtimes/starters/seedling/src/lib.rs | 2 +- .../runtimes/starters/shell/src/lib.rs | 2 +- .../runtimes/testing/penpal/src/lib.rs | 2 +- .../testing/rococo-parachain/src/lib.rs | 2 +- cumulus/test/runtime/src/lib.rs | 4 +- .../chain_spec_runtime/src/runtime.rs | 2 +- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/test-runtime/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- prdoc/pr_4257.prdoc | 76 +++ substrate/bin/node/runtime/src/lib.rs | 2 +- substrate/client/block-builder/src/lib.rs | 2 +- substrate/client/executor/src/wasm_runtime.rs | 12 +- .../rpc-spec-v2/src/chain_head/tests.rs | 2 +- substrate/client/rpc/src/state/tests.rs | 3 +- .../support/test/compile_pass/src/lib.rs | 2 +- substrate/frame/system/src/lib.rs | 20 +- substrate/frame/system/src/mock.rs | 2 +- substrate/frame/system/src/tests.rs | 5 +- substrate/primitives/api/src/lib.rs | 2 +- substrate/primitives/storage/src/lib.rs | 1 + .../primitives/version/proc-macro/Cargo.toml | 1 + .../proc-macro/src/decl_runtime_version.rs | 44 +- substrate/primitives/version/src/lib.rs | 432 +++++++++++++++++- substrate/test-utils/runtime/src/lib.rs | 2 +- templates/minimal/runtime/src/lib.rs | 2 +- templates/parachain/runtime/src/lib.rs | 2 +- templates/solochain/runtime/src/lib.rs | 2 +- 43 files changed, 584 insertions(+), 79 deletions(-) create mode 100644 prdoc/pr_4257.prdoc diff --git a/Cargo.lock b/Cargo.lock index e1b0b3f8f9e9..66fd0d05cc34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21070,6 +21070,7 @@ name = "sp-version-proc-macro" version = "13.0.0" dependencies = [ "parity-scale-codec", + "proc-macro-warning 1.0.0", "proc-macro2 1.0.86", "quote 1.0.37", "sp-version", diff --git a/cumulus/client/network/src/tests.rs b/cumulus/client/network/src/tests.rs index cde73c4c5180..81c2d9f24f28 100644 --- a/cumulus/client/network/src/tests.rs +++ b/cumulus/client/network/src/tests.rs @@ -323,7 +323,7 @@ impl RelayChainInterface for DummyRelayChainInterface { impl_version: 0, apis: Cow::Owned(apis), transaction_version: 5, - state_version: 1, + system_version: 1, }) } } diff --git a/cumulus/client/pov-recovery/src/tests.rs b/cumulus/client/pov-recovery/src/tests.rs index 6f274ed18b6b..f300bdc5f2ba 100644 --- a/cumulus/client/pov-recovery/src/tests.rs +++ b/cumulus/client/pov-recovery/src/tests.rs @@ -329,7 +329,7 @@ impl RelayChainInterface for Relaychain { impl_version: 0, apis: Cow::Owned(apis), transaction_version: 5, - state_version: 1, + system_version: 1, }) } diff --git a/cumulus/pallets/parachain-system/src/mock.rs b/cumulus/pallets/parachain-system/src/mock.rs index b4d118aadf04..247de3a29b69 100644 --- a/cumulus/pallets/parachain-system/src/mock.rs +++ b/cumulus/pallets/parachain-system/src/mock.rs @@ -64,7 +64,7 @@ parameter_types! { impl_version: 1, apis: sp_version::create_apis_vec!([]), transaction_version: 1, - state_version: 1, + system_version: 1, }; pub const ParachainId: ParaId = ParaId::new(200); pub const ReservedXcmpWeight: Weight = Weight::zero(); diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 2f3fb6b68c4a..a4a2554b7afc 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -128,7 +128,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 97dbe7c361c1..6da2a0bc7b95 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -126,7 +126,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 14409ce4642d..6c6e2ec7efdd 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -238,7 +238,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 5717db456a77..ddd40dbf60e0 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -213,7 +213,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index dea2eb03db3a..f22feb70382a 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -129,7 +129,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 6, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index bf173fb618af..55770515d73f 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -148,7 +148,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 7, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index 25324bf17764..aea2bf232cbc 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -150,7 +150,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index a3051e4bf271..218afaab924d 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -149,7 +149,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs index 942e11e0b257..abf13a596a7d 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs @@ -106,7 +106,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index 77bfb99669c6..cb9177d0c23b 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -138,7 +138,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index 3343d2be749d..9813c5cb6acc 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -138,7 +138,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/starters/seedling/src/lib.rs b/cumulus/parachains/runtimes/starters/seedling/src/lib.rs index 1fe72604d373..f126ee861fa7 100644 --- a/cumulus/parachains/runtimes/starters/seedling/src/lib.rs +++ b/cumulus/parachains/runtimes/starters/seedling/src/lib.rs @@ -81,7 +81,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, - state_version: 0, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/starters/shell/src/lib.rs b/cumulus/parachains/runtimes/starters/shell/src/lib.rs index 1dfbe2b6c41c..fac2d1312c0f 100644 --- a/cumulus/parachains/runtimes/starters/shell/src/lib.rs +++ b/cumulus/parachains/runtimes/starters/shell/src/lib.rs @@ -89,7 +89,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 0, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 7d19c0ed8d85..266894c3e4ed 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -249,7 +249,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// This determines the average expected block time that we are targeting. diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index dff7046f1972..34646f84aedb 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -113,7 +113,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 6, - state_version: 0, + system_version: 0, }; pub const MILLISECS_PER_BLOCK: u64 = 6000; diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 274f16ab630d..ba0a3487011a 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -132,7 +132,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; #[cfg(feature = "increment-spec-version")] @@ -146,7 +146,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; pub const EPOCH_DURATION_IN_BLOCKS: u32 = 10 * MINUTES; diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs index 195d1b124474..5be3a59dc7bb 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs @@ -46,7 +46,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The signed extensions that are added to the runtime. diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 6b046e190830..6ec49c5830f7 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -174,7 +174,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 26, - state_version: 1, + system_version: 1, }; /// The BABE epoch configuration at genesis. diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 72d024e9a878..b03231569113 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -125,7 +125,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The BABE epoch configuration at genesis. diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index b02c2d8c671e..d0c1cd89de32 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -175,7 +175,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 26, - state_version: 1, + system_version: 1, }; /// The BABE epoch configuration at genesis. diff --git a/prdoc/pr_4257.prdoc b/prdoc/pr_4257.prdoc new file mode 100644 index 000000000000..860b85a4888e --- /dev/null +++ b/prdoc/pr_4257.prdoc @@ -0,0 +1,76 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Rename `state_version` in `RuntimeVersion` to `system_version`. + +doc: + - audience: Runtime Dev + description: | + This PR renames `state_version` in `RuntimeVersion` to `system_version`. `system_version=2` signifies + that extrinsic root derivation uses `StateVersion::V1`. + + - audience: Runtime User + description: | + `RuntimeVersion`'s `state_version` is renamed to `system_version`. Applications using that type and its field + must update their code to reflect the changes. For easier migration serde serialization produces both new + `systemVersion` and old `stateVersion` fields and deserialization supports `stateVersion` as an alias as too. + +crates: + - name: frame-system + bump: major + - name: sp-api + bump: none + - name: sp-version + bump: major + - name: sp-storage + bump: minor + - name: sp-version-proc-macro + bump: minor + - name: sc-block-builder + bump: major + - name: sc-executor + bump: major + - name: sc-rpc + bump: none + - name: sc-rpc-spec-v2 + bump: none + - name: cumulus-pallet-parachain-system + bump: none + - name: cumulus-client-pov-recovery + bump: none + - name: cumulus-client-network + bump: none + - name: rococo-runtime + bump: major + - name: westend-runtime + bump: major + - name: asset-hub-rococo-runtime + bump: major + - name: asset-hub-westend-runtime + bump: major + - name: bridge-hub-rococo-runtime + bump: major + - name: bridge-hub-westend-runtime + bump: major + - name: collectives-westend-runtime + bump: major + - name: coretime-rococo-runtime + bump: major + - name: coretime-westend-runtime + bump: major + - name: people-rococo-runtime + bump: major + - name: people-westend-runtime + bump: major + - name: penpal-runtime + bump: major + - name: contracts-rococo-runtime + bump: major + - name: glutton-westend-runtime + bump: major + - name: seedling-runtime + bump: major + - name: shell-runtime + bump: major + - name: rococo-parachain-runtime + bump: major diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index caebd63408db..c8409078af57 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -171,7 +171,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, - state_version: 1, + system_version: 1, }; /// The BABE epoch configuration at genesis. diff --git a/substrate/client/block-builder/src/lib.rs b/substrate/client/block-builder/src/lib.rs index 2f22cd42591f..d02d0e321805 100644 --- a/substrate/client/block-builder/src/lib.rs +++ b/substrate/client/block-builder/src/lib.rs @@ -320,7 +320,7 @@ where header.extrinsics_root().clone(), HashingFor::::ordered_trie_root( self.extrinsics.iter().map(Encode::encode).collect(), - sp_runtime::StateVersion::V0, + self.api.version(self.parent_hash)?.extrinsics_root_state_version(), ), ); diff --git a/substrate/client/executor/src/wasm_runtime.rs b/substrate/client/executor/src/wasm_runtime.rs index be8344ba79b7..77dfc09c8807 100644 --- a/substrate/client/executor/src/wasm_runtime.rs +++ b/substrate/client/executor/src/wasm_runtime.rs @@ -480,7 +480,7 @@ mod tests { let version = decode_version(&old_runtime_version.encode()).unwrap(); assert_eq!(1, version.transaction_version); - assert_eq!(0, version.state_version); + assert_eq!(0, version.system_version); } #[test] @@ -507,12 +507,12 @@ mod tests { impl_version: 1, apis: create_apis_vec!([(>::ID, 3)]), transaction_version: 3, - state_version: 4, + system_version: 4, }; let version = decode_version(&old_runtime_version.encode()).unwrap(); assert_eq!(3, version.transaction_version); - assert_eq!(0, version.state_version); + assert_eq!(0, version.system_version); let old_runtime_version = RuntimeVersion { spec_name: "test".into(), @@ -522,12 +522,12 @@ mod tests { impl_version: 1, apis: create_apis_vec!([(>::ID, 4)]), transaction_version: 3, - state_version: 4, + system_version: 4, }; let version = decode_version(&old_runtime_version.encode()).unwrap(); assert_eq!(3, version.transaction_version); - assert_eq!(4, version.state_version); + assert_eq!(4, version.system_version); } #[test] @@ -545,7 +545,7 @@ mod tests { impl_version: 100, apis: create_apis_vec!([(>::ID, 4)]), transaction_version: 100, - state_version: 1, + system_version: 1, }; let embedded = sp_version::embed::embed_runtime_version(&wasm, runtime_version.clone()) diff --git a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs index a638a9c7ec54..30a01b93b315 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -346,7 +346,7 @@ async fn follow_with_runtime() { [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ [\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\ [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\ - [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"stateVersion\":1}"; + [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"systemVersion\":1}"; let runtime: RuntimeVersion = serde_json::from_str(runtime_str).unwrap(); diff --git a/substrate/client/rpc/src/state/tests.rs b/substrate/client/rpc/src/state/tests.rs index eef795070343..6b711f2425e9 100644 --- a/substrate/client/rpc/src/state/tests.rs +++ b/substrate/client/rpc/src/state/tests.rs @@ -476,7 +476,8 @@ async fn should_return_runtime_version() { [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ [\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\ [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\ - [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"stateVersion\":1}"; + [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"systemVersion\":1,\ + \"stateVersion\":1}"; let runtime_version = api.runtime_version(None.into()).unwrap(); let serialized = serde_json::to_string(&runtime_version).unwrap(); diff --git a/substrate/frame/support/test/compile_pass/src/lib.rs b/substrate/frame/support/test/compile_pass/src/lib.rs index 37af683fbc7f..677ef4e94c89 100644 --- a/substrate/frame/support/test/compile_pass/src/lib.rs +++ b/substrate/frame/support/test/compile_pass/src/lib.rs @@ -40,7 +40,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: sp_version::create_apis_vec!([]), transaction_version: 0, - state_version: 0, + system_version: 0, }; pub type Signature = sr25519::Signature; diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index abacfa7b62cc..662b7f1a94bf 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -175,6 +175,7 @@ pub use extensions::{ pub use extensions::check_mortality::CheckMortality as CheckEra; pub use frame_support::dispatch::RawOrigin; use frame_support::traits::{PostInherents, PostTransactions, PreInherents}; +use sp_core::storage::StateVersion; pub use weights::WeightInfo; const LOG_TARGET: &str = "runtime::system"; @@ -182,17 +183,20 @@ const LOG_TARGET: &str = "runtime::system"; /// Compute the trie root of a list of extrinsics. /// /// The merkle proof is using the same trie as runtime state with -/// `state_version` 0. -pub fn extrinsics_root(extrinsics: &[E]) -> H::Output { - extrinsics_data_root::(extrinsics.iter().map(codec::Encode::encode).collect()) +/// `state_version` 0 or 1. +pub fn extrinsics_root( + extrinsics: &[E], + state_version: StateVersion, +) -> H::Output { + extrinsics_data_root::(extrinsics.iter().map(codec::Encode::encode).collect(), state_version) } /// Compute the trie root of a list of extrinsics. /// /// The merkle proof is using the same trie as runtime state with -/// `state_version` 0. -pub fn extrinsics_data_root(xts: Vec>) -> H::Output { - H::ordered_trie_root(xts, sp_core::storage::StateVersion::V0) +/// `state_version` 0 or 1. +pub fn extrinsics_data_root(xts: Vec>, state_version: StateVersion) -> H::Output { + H::ordered_trie_root(xts, state_version) } /// An object to track the currently used extrinsic weight in a block. @@ -1847,7 +1851,9 @@ impl Pallet { let extrinsics = (0..ExtrinsicCount::::take().unwrap_or_default()) .map(ExtrinsicData::::take) .collect(); - let extrinsics_root = extrinsics_data_root::(extrinsics); + let extrinsics_root_state_version = T::Version::get().extrinsics_root_state_version(); + let extrinsics_root = + extrinsics_data_root::(extrinsics, extrinsics_root_state_version); // move block hash pruning window by one block let block_hash_count = T::BlockHashCount::get(); diff --git a/substrate/frame/system/src/mock.rs b/substrate/frame/system/src/mock.rs index fff848b3b0e5..f43ffe3c87ee 100644 --- a/substrate/frame/system/src/mock.rs +++ b/substrate/frame/system/src/mock.rs @@ -40,7 +40,7 @@ parameter_types! { impl_version: 1, apis: sp_version::create_apis_vec!([]), transaction_version: 1, - state_version: 1, + system_version: 1, }; pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 10, diff --git a/substrate/frame/system/src/tests.rs b/substrate/frame/system/src/tests.rs index b2cd017e1e20..534ba1e863fc 100644 --- a/substrate/frame/system/src/tests.rs +++ b/substrate/frame/system/src/tests.rs @@ -789,7 +789,10 @@ fn extrinsics_root_is_calculated_correctly() { System::note_finished_extrinsics(); let header = System::finalize(); - let ext_root = extrinsics_data_root::(vec![vec![1], vec![2]]); + let ext_root = extrinsics_data_root::( + vec![vec![1], vec![2]], + sp_core::storage::StateVersion::V0, + ); assert_eq!(ext_root, *header.extrinsics_root()); }); } diff --git a/substrate/primitives/api/src/lib.rs b/substrate/primitives/api/src/lib.rs index d254bf20601f..4b5c35562bde 100644 --- a/substrate/primitives/api/src/lib.rs +++ b/substrate/primitives/api/src/lib.rs @@ -341,7 +341,7 @@ pub use sp_api_proc_macro::decl_runtime_apis; /// // Here we are exposing the runtime api versions. /// apis: RUNTIME_API_VERSIONS, /// transaction_version: 1, -/// state_version: 1, +/// system_version: 1, /// }; /// /// # fn main() {} diff --git a/substrate/primitives/storage/src/lib.rs b/substrate/primitives/storage/src/lib.rs index 3b9afae4ca07..4b25f85fba68 100644 --- a/substrate/primitives/storage/src/lib.rs +++ b/substrate/primitives/storage/src/lib.rs @@ -444,6 +444,7 @@ impl TryFrom for StateVersion { match val { 0 => Ok(StateVersion::V0), 1 => Ok(StateVersion::V1), + 2 => Ok(StateVersion::V1), _ => Err(()), } } diff --git a/substrate/primitives/version/proc-macro/Cargo.toml b/substrate/primitives/version/proc-macro/Cargo.toml index 35c49360b7f8..a3be654547d9 100644 --- a/substrate/primitives/version/proc-macro/Cargo.toml +++ b/substrate/primitives/version/proc-macro/Cargo.toml @@ -20,6 +20,7 @@ proc-macro = true [dependencies] codec = { features = ["derive"], workspace = true, default-features = true } +proc-macro-warning = { workspace = true } proc-macro2 = { workspace = true } quote = { workspace = true } syn = { features = ["extra-traits", "fold", "full", "visit"], workspace = true } diff --git a/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs b/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs index 3671d4aff6bb..b4f749c90f59 100644 --- a/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs +++ b/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs @@ -17,6 +17,7 @@ use codec::Encode; use proc_macro2::{Span, TokenStream}; +use proc_macro_warning::Warning; use quote::quote; use syn::{ parse::{Error, Result}, @@ -37,13 +38,19 @@ pub fn decl_runtime_version_impl(input: proc_macro::TokenStream) -> proc_macro:: } fn decl_runtime_version_impl_inner(item: ItemConst) -> Result { - let runtime_version = ParseRuntimeVersion::parse_expr(&item.expr)?.build(item.expr.span())?; + let (parsed_runtime_version, warnings) = ParseRuntimeVersion::parse_expr(&item.expr)?; + let runtime_version = parsed_runtime_version.build(item.expr.span())?; let link_section = generate_emit_link_section_decl(&runtime_version.encode(), "runtime_version"); Ok(quote! { #item #link_section + const _:() = { + #( + #warnings + )* + }; }) } @@ -63,7 +70,7 @@ struct RuntimeVersion { impl_version: u32, apis: u8, transaction_version: u32, - state_version: u8, + system_version: u8, } #[derive(Default, Debug)] @@ -74,11 +81,11 @@ struct ParseRuntimeVersion { spec_version: Option, impl_version: Option, transaction_version: Option, - state_version: Option, + system_version: Option, } impl ParseRuntimeVersion { - fn parse_expr(init_expr: &Expr) -> Result { + fn parse_expr(init_expr: &Expr) -> Result<(ParseRuntimeVersion, Vec)> { let init_expr = match init_expr { Expr::Struct(ref e) => e, _ => @@ -86,13 +93,14 @@ impl ParseRuntimeVersion { }; let mut parsed = ParseRuntimeVersion::default(); + let mut warnings = vec![]; for field_value in init_expr.fields.iter() { - parsed.parse_field_value(field_value)?; + warnings.append(&mut parsed.parse_field_value(field_value)?) } - Ok(parsed) + Ok((parsed, warnings)) } - fn parse_field_value(&mut self, field_value: &FieldValue) -> Result<()> { + fn parse_field_value(&mut self, field_value: &FieldValue) -> Result> { let field_name = match field_value.member { syn::Member::Named(ref ident) => ident, syn::Member::Unnamed(_) => @@ -112,6 +120,7 @@ impl ParseRuntimeVersion { } } + let mut warnings = vec![]; if field_name == "spec_name" { parse_once(&mut self.spec_name, field_value, Self::parse_str_literal)?; } else if field_name == "impl_name" { @@ -125,7 +134,16 @@ impl ParseRuntimeVersion { } else if field_name == "transaction_version" { parse_once(&mut self.transaction_version, field_value, Self::parse_num_literal)?; } else if field_name == "state_version" { - parse_once(&mut self.state_version, field_value, Self::parse_num_literal_u8)?; + let warning = Warning::new_deprecated("RuntimeVersion") + .old("state_version") + .new("system_version)") + .help_link("https://github.com/paritytech/polkadot-sdk/pull/4257") + .span(field_name.span()) + .build_or_panic(); + warnings.push(warning); + parse_once(&mut self.system_version, field_value, Self::parse_num_literal_u8)?; + } else if field_name == "system_version" { + parse_once(&mut self.system_version, field_value, Self::parse_num_literal_u8)?; } else if field_name == "apis" { // Intentionally ignored // @@ -136,7 +154,7 @@ impl ParseRuntimeVersion { return Err(Error::new(field_name.span(), "unknown field")) } - Ok(()) + Ok(warnings) } fn parse_num_literal(expr: &Expr) -> Result { @@ -198,7 +216,7 @@ impl ParseRuntimeVersion { spec_version, impl_version, transaction_version, - state_version, + system_version, } = self; Ok(RuntimeVersion { @@ -208,7 +226,7 @@ impl ParseRuntimeVersion { spec_version: required!(spec_version), impl_version: required!(impl_version), transaction_version: required!(transaction_version), - state_version: required!(state_version), + system_version: required!(system_version), apis: 0, }) } @@ -240,7 +258,7 @@ mod tests { impl_version: 1, apis: 0, transaction_version: 2, - state_version: 1, + system_version: 1, } .encode(); @@ -255,7 +273,7 @@ mod tests { impl_version: 1, apis: Cow::Owned(vec![]), transaction_version: 2, - state_version: 1, + system_version: 1, }, ); } diff --git a/substrate/primitives/version/src/lib.rs b/substrate/primitives/version/src/lib.rs index 55dea364eef4..a9f1c2373069 100644 --- a/substrate/primitives/version/src/lib.rs +++ b/substrate/primitives/version/src/lib.rs @@ -35,12 +35,12 @@ extern crate alloc; +#[cfg(any(feature = "std", feature = "serde"))] +use alloc::fmt; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::collections::HashSet; -#[cfg(feature = "std")] -use std::fmt; #[doc(hidden)] pub use alloc::borrow::Cow; @@ -83,7 +83,7 @@ pub mod embed; /// impl_version: 1, /// apis: RUNTIME_API_VERSIONS, /// transaction_version: 2, -/// state_version: 1, +/// system_version: 1, /// }; /// /// # const RUNTIME_API_VERSIONS: sp_version::ApisVec = sp_version::create_apis_vec!([]); @@ -160,8 +160,6 @@ macro_rules! create_apis_vec { /// `authoring_version`, absolutely not `impl_version` since they change the semantics of the /// runtime. #[derive(Clone, PartialEq, Eq, Encode, Default, sp_runtime::RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct RuntimeVersion { /// Identifies the different Substrate runtimes. There'll be at least polkadot and node. /// A different on-chain spec_name to that of the native runtime would normally result @@ -200,13 +198,6 @@ pub struct RuntimeVersion { pub impl_version: u32, /// List of supported API "features" along with their versions. - #[cfg_attr( - feature = "serde", - serde( - serialize_with = "apis_serialize::serialize", - deserialize_with = "apis_serialize::deserialize", - ) - )] pub apis: ApisVec, /// All existing calls (dispatchables) are fully compatible when this number doesn't change. If @@ -230,9 +221,406 @@ pub struct RuntimeVersion { /// This number should never decrease. pub transaction_version: u32, - /// Version of the state implementation used by this runtime. + /// Version of the system implementation used by this runtime. /// Use of an incorrect version is consensus breaking. - pub state_version: u8, + pub system_version: u8, +} + +// Manual implementation in order to sprinkle `stateVersion` at the end for migration purposes +// after the field was renamed from `state_version` to `system_version` +#[cfg(feature = "serde")] +impl serde::Serialize for RuntimeVersion { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + + let mut state = serializer.serialize_struct("RuntimeVersion", 9)?; + state.serialize_field("specName", &self.spec_name)?; + state.serialize_field("implName", &self.impl_name)?; + state.serialize_field("authoringVersion", &self.authoring_version)?; + state.serialize_field("specVersion", &self.spec_version)?; + state.serialize_field("implVersion", &self.impl_version)?; + state.serialize_field("apis", { + struct SerializeWith<'a>(&'a ApisVec); + + impl<'a> serde::Serialize for SerializeWith<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + apis_serialize::serialize(self.0, serializer) + } + } + + &SerializeWith(&self.apis) + })?; + state.serialize_field("transactionVersion", &self.transaction_version)?; + state.serialize_field("systemVersion", &self.system_version)?; + state.serialize_field("stateVersion", &self.system_version)?; + state.end() + } +} + +// Manual implementation in order to allow both old `stateVersion` and new `systemVersion` to be +// present at the same time +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for RuntimeVersion { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use core::marker::PhantomData; + + enum Field { + SpecName, + ImplName, + AuthoringVersion, + SpecVersion, + ImplVersion, + Apis, + TransactionVersion, + SystemVersion, + Ignore, + } + + struct FieldVisitor; + + impl<'de> serde::de::Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("field identifier") + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + match value { + 0 => Ok(Field::SpecName), + 1 => Ok(Field::ImplName), + 2 => Ok(Field::AuthoringVersion), + 3 => Ok(Field::SpecVersion), + 4 => Ok(Field::ImplVersion), + 5 => Ok(Field::Apis), + 6 => Ok(Field::TransactionVersion), + 7 => Ok(Field::SystemVersion), + _ => Ok(Field::Ignore), + } + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match value { + "specName" => Ok(Field::SpecName), + "implName" => Ok(Field::ImplName), + "authoringVersion" => Ok(Field::AuthoringVersion), + "specVersion" => Ok(Field::SpecVersion), + "implVersion" => Ok(Field::ImplVersion), + "apis" => Ok(Field::Apis), + "transactionVersion" => Ok(Field::TransactionVersion), + "systemVersion" | "stateVersion" => Ok(Field::SystemVersion), + _ => Ok(Field::Ignore), + } + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: serde::de::Error, + { + match value { + b"specName" => Ok(Field::SpecName), + b"implName" => Ok(Field::ImplName), + b"authoringVersion" => Ok(Field::AuthoringVersion), + b"specVersion" => Ok(Field::SpecVersion), + b"implVersion" => Ok(Field::ImplVersion), + b"apis" => Ok(Field::Apis), + b"transactionVersion" => Ok(Field::TransactionVersion), + b"systemVersion" | b"stateVersion" => Ok(Field::SystemVersion), + _ => Ok(Field::Ignore), + } + } + } + + impl<'de> serde::Deserialize<'de> for Field { + #[inline] + fn deserialize(deserializer: E) -> Result + where + E: serde::Deserializer<'de>, + { + deserializer.deserialize_identifier(FieldVisitor) + } + } + + struct Visitor<'de> { + lifetime: PhantomData<&'de ()>, + } + impl<'de> serde::de::Visitor<'de> for Visitor<'de> { + type Value = RuntimeVersion; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct RuntimeVersion") + } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let spec_name = match seq.next_element()? { + Some(spec_name) => spec_name, + None => + return Err(serde::de::Error::invalid_length( + 0usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let impl_name = match seq.next_element()? { + Some(impl_name) => impl_name, + None => + return Err(serde::de::Error::invalid_length( + 1usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let authoring_version = match seq.next_element()? { + Some(authoring_version) => authoring_version, + None => + return Err(serde::de::Error::invalid_length( + 2usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let spec_version = match seq.next_element()? { + Some(spec_version) => spec_version, + None => + return Err(serde::de::Error::invalid_length( + 3usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let impl_version = match seq.next_element()? { + Some(impl_version) => impl_version, + None => + return Err(serde::de::Error::invalid_length( + 4usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let apis = match { + struct DeserializeWith<'de> { + value: ApisVec, + + phantom: PhantomData, + lifetime: PhantomData<&'de ()>, + } + impl<'de> serde::Deserialize<'de> for DeserializeWith<'de> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(DeserializeWith { + value: apis_serialize::deserialize(deserializer)?, + phantom: PhantomData, + lifetime: PhantomData, + }) + } + } + seq.next_element::>()?.map(|wrap| wrap.value) + } { + Some(apis) => apis, + None => + return Err(serde::de::Error::invalid_length( + 5usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let transaction_version = match seq.next_element()? { + Some(transaction_version) => transaction_version, + None => + return Err(serde::de::Error::invalid_length( + 6usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let system_version = match seq.next_element()? { + Some(system_version) => system_version, + None => + return Err(serde::de::Error::invalid_length( + 7usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + Ok(RuntimeVersion { + spec_name, + impl_name, + authoring_version, + spec_version, + impl_version, + apis, + transaction_version, + system_version, + }) + } + + #[inline] + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut spec_name: Option = None; + let mut impl_name: Option = None; + let mut authoring_version: Option = None; + let mut spec_version: Option = None; + let mut impl_version: Option = None; + let mut apis: Option = None; + let mut transaction_version: Option = None; + let mut system_version: Option = None; + + while let Some(key) = map.next_key()? { + match key { + Field::SpecName => { + if spec_name.is_some() { + return Err(::duplicate_field( + "specName", + )); + } + spec_name = Some(map.next_value()?); + }, + Field::ImplName => { + if impl_name.is_some() { + return Err(::duplicate_field( + "implName", + )); + } + impl_name = Some(map.next_value()?); + }, + Field::AuthoringVersion => { + if authoring_version.is_some() { + return Err(::duplicate_field( + "authoringVersion", + )); + } + authoring_version = Some(map.next_value()?); + }, + Field::SpecVersion => { + if spec_version.is_some() { + return Err(::duplicate_field( + "specVersion", + )); + } + spec_version = Some(map.next_value()?); + }, + Field::ImplVersion => { + if impl_version.is_some() { + return Err(::duplicate_field( + "implVersion", + )); + } + impl_version = Some(map.next_value()?); + }, + Field::Apis => { + if apis.is_some() { + return Err(::duplicate_field("apis")); + } + apis = Some({ + struct DeserializeWith<'de> { + value: ApisVec, + lifetime: PhantomData<&'de ()>, + } + impl<'de> serde::Deserialize<'de> for DeserializeWith<'de> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(DeserializeWith { + value: apis_serialize::deserialize(deserializer)?, + lifetime: PhantomData, + }) + } + } + + map.next_value::>()?.value + }); + }, + Field::TransactionVersion => { + if transaction_version.is_some() { + return Err(::duplicate_field( + "transactionVersion", + )); + } + transaction_version = Some(map.next_value()?); + }, + Field::SystemVersion => + if let Some(system_version) = system_version { + let new_value = map.next_value::()?; + if system_version != new_value { + return Err(::custom( + alloc::format!( + r#"Duplicated "stateVersion" and "systemVersion" \ + fields must have the same value, but different values \ + were provided: {system_version} vs {new_value}"# + ), + )); + } + } else { + system_version = Some(map.next_value()?); + }, + _ => { + let _ = map.next_value::()?; + }, + } + } + let spec_name = spec_name + .ok_or_else(|| ::missing_field("specName"))?; + let impl_name = impl_name + .ok_or_else(|| ::missing_field("implName"))?; + let authoring_version = authoring_version.ok_or_else(|| { + ::missing_field("authoringVersion") + })?; + let spec_version = spec_version + .ok_or_else(|| ::missing_field("specVersion"))?; + let impl_version = impl_version + .ok_or_else(|| ::missing_field("implVersion"))?; + let apis = + apis.ok_or_else(|| ::missing_field("apis"))?; + let transaction_version = transaction_version.ok_or_else(|| { + ::missing_field("transactionVersion") + })?; + let system_version = system_version.ok_or_else(|| { + ::missing_field("systemVersion") + })?; + Ok(RuntimeVersion { + spec_name, + impl_name, + authoring_version, + spec_version, + impl_version, + apis, + transaction_version, + system_version, + }) + } + } + + const FIELDS: &[&str] = &[ + "specName", + "implName", + "authoringVersion", + "specVersion", + "implVersion", + "apis", + "transactionVersion", + "stateVersion", + "systemVersion", + ]; + + deserializer.deserialize_struct("RuntimeVersion", FIELDS, Visitor { lifetime: PhantomData }) + } } impl RuntimeVersion { @@ -257,7 +645,7 @@ impl RuntimeVersion { if core_version.is_some() { core_version } else { core_version_from_apis(&apis) }; let transaction_version = if core_version.map(|v| v >= 3).unwrap_or(false) { Decode::decode(input)? } else { 1 }; - let state_version = + let system_version = if core_version.map(|v| v >= 4).unwrap_or(false) { Decode::decode(input)? } else { 0 }; Ok(RuntimeVersion { spec_name, @@ -267,7 +655,7 @@ impl RuntimeVersion { impl_version, apis, transaction_version, - state_version, + system_version, }) } } @@ -334,7 +722,17 @@ impl RuntimeVersion { /// Otherwise, V1 trie version will be use. pub fn state_version(&self) -> StateVersion { // If version > than 1, keep using latest version. - self.state_version.try_into().unwrap_or(StateVersion::V1) + self.system_version.try_into().unwrap_or(StateVersion::V1) + } + + /// Returns the state version to use for Extrinsics root. + pub fn extrinsics_root_state_version(&self) -> StateVersion { + match self.system_version { + // for system version 0 and 1, return V0 + 0 | 1 => StateVersion::V0, + // anything above 1, return V1 + _ => StateVersion::V1, + } } } diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index 514f3bcba204..840081003b84 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -119,7 +119,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 2, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; fn version() -> RuntimeVersion { diff --git a/templates/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs index 474d9ddfb9e8..cce13c48af71 100644 --- a/templates/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -46,7 +46,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/templates/parachain/runtime/src/lib.rs b/templates/parachain/runtime/src/lib.rs index 83ae15700a94..ccec648ce4c1 100644 --- a/templates/parachain/runtime/src/lib.rs +++ b/templates/parachain/runtime/src/lib.rs @@ -172,7 +172,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: apis::RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; #[docify::export] diff --git a/templates/solochain/runtime/src/lib.rs b/templates/solochain/runtime/src/lib.rs index 6cbfbb879602..ce38c65479e5 100644 --- a/templates/solochain/runtime/src/lib.rs +++ b/templates/solochain/runtime/src/lib.rs @@ -71,7 +71,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: apis::RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; mod block_times { From 7c90f51dfd541db6df057d3c0ff787e92104c221 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 06:15:10 +0000 Subject: [PATCH 341/382] Bump the ci_dependencies group with 2 updates (#5637) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the ci_dependencies group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request). Updates `actions/checkout` from 2 to 4
Release notes

Sourced from actions/checkout's releases.

v4.0.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3...v4.0.0

v3.6.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3.5.3...v3.6.0

v3.5.3

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3...v3.5.3

v3.5.2

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v3.5.1...v3.5.2

v3.5.1

What's Changed

New Contributors

... (truncated)

Commits

Updates `peter-evans/create-pull-request` from 6.1.0 to 7.0.1
Release notes

Sourced from peter-evans/create-pull-request's releases.

Create Pull Request v7.0.1

⚙️ Fixes an issue affecting one particular use case where the action fails on diff --stat with fatal: ambiguous argument.

What's Changed

Full Changelog: https://github.com/peter-evans/create-pull-request/compare/v7.0.0...v7.0.1

Create Pull Request v7.0.0

:sparkles: Now supports commit signing with bot-generated tokens! See "What's new" below. :writing_hand::robot:

Behaviour changes

  • Action input git-token has been renamed branch-token, to be more clear about its purpose. The branch-token is the token that the action will use to create and update the branch.
  • The action now handles requests that have been rate-limited by GitHub. Requests hitting a primary rate limit will retry twice, for a total of three attempts. Requests hitting a secondary rate limit will not be retried.
  • The pull-request-operation output now returns none when no operation was executed.
  • Removed deprecated output environment variable PULL_REQUEST_NUMBER. Please use the pull-request-number action output instead.

What's new

  • The action can now sign commits as github-actions[bot] when using GITHUB_TOKEN, or your own bot when using GitHub App tokens. See commit signing for details.
  • Action input draft now accepts a new value always-true. This will set the pull request to draft status when the pull request is updated, as well as on creation.
  • A new action input maintainer-can-modify indicates whether maintainers can modify the pull request. The default is true, which retains the existing behaviour of the action.
  • A new output pull-request-commits-verified returns true or false, indicating whether GitHub considers the signature of the branch's commits to be verified.

What's Changed

... (truncated)

Commits
  • 8867c4a fix: handle ambiguous argument failure on diff stat (#3312)
  • 6073f54 build(deps-dev): bump @​typescript-eslint/eslint-plugin (#3291)
  • 6d01b56 build(deps-dev): bump eslint-plugin-import from 2.29.1 to 2.30.0 (#3290)
  • 25cf845 build(deps-dev): bump @​typescript-eslint/parser from 7.17.0 to 7.18.0 (#3289)
  • d87b980 build(deps-dev): bump @​types/node from 18.19.46 to 18.19.48 (#3288)
  • 119d131 build(deps): bump peter-evans/create-pull-request from 6 to 7 (#3283)
  • 73e6230 docs: update readme
  • c0348e8 ci: add v7 to workflow
  • 4320041 feat: signed commits (v7) (#3057)
  • 0c2a66f build(deps-dev): bump ts-jest from 29.2.4 to 29.2.5 (#3256)
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-licenses.yml | 2 +- .github/workflows/check-links.yml | 2 +- .github/workflows/check-prdoc.yml | 2 +- .github/workflows/check-semver.yml | 2 +- .github/workflows/checks-quick.yml | 16 ++++++++-------- .github/workflows/checks.yml | 6 +++--- .github/workflows/misc-sync-templates.yml | 4 ++-- .github/workflows/publish-check-crates.yml | 2 +- .github/workflows/publish-claim-crates.yml | 2 +- .github/workflows/release-10_rc-automation.yml | 2 +- .../release-30_publish_release_draft.yml | 6 +++--- .github/workflows/release-50_publish-docker.yml | 8 ++++---- .github/workflows/release-branchoff-stable.yml | 4 ++-- .github/workflows/release-check-runtimes.yml | 4 ++-- .github/workflows/release-clobber-stable.yml | 2 +- .github/workflows/release-srtool.yml | 4 ++-- .../workflows/tests-linux-stable-coverage.yml | 2 +- .github/workflows/tests-misc.yml | 16 ++++++++-------- 18 files changed, 43 insertions(+), 43 deletions(-) diff --git a/.github/workflows/check-licenses.yml b/.github/workflows/check-licenses.yml index a74986048976..e3fc27f0a94b 100644 --- a/.github/workflows/check-licenses.yml +++ b/.github/workflows/check-licenses.yml @@ -16,7 +16,7 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - uses: actions/setup-node@v4.0.3 with: node-version: "18.x" diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index d10f34e6faef..1060d252fd2a 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -25,7 +25,7 @@ jobs: # This should restore from the most recent one: restore-keys: cache-lychee- - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.0 (22. Sep 2023) + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.0 (22. Sep 2023) - name: Lychee link checker uses: lycheeverse/lychee-action@2b973e86fc7b1f6b36a93795fe2c9c6ae1118621 # for v1.9.1 (10. Jan 2024) diff --git a/.github/workflows/check-prdoc.yml b/.github/workflows/check-prdoc.yml index 6c8f1ed7a300..8f60b9cccf85 100644 --- a/.github/workflows/check-prdoc.yml +++ b/.github/workflows/check-prdoc.yml @@ -20,7 +20,7 @@ jobs: if: github.event.pull_request.number != '' steps: - name: Checkout repo - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc #v4.1.7 # we cannot show the version in this step (ie before checking out the repo) # due to https://github.com/paritytech/prdoc/issues/15 - name: Check if PRdoc is required diff --git a/.github/workflows/check-semver.yml b/.github/workflows/check-semver.yml index 15eb32f4062c..2c06df5a5092 100644 --- a/.github/workflows/check-semver.yml +++ b/.github/workflows/check-semver.yml @@ -18,7 +18,7 @@ jobs: container: image: docker.io/paritytech/ci-unified:bullseye-1.77.0-2024-04-10-v20240408 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 with: fetch-depth: 2 diff --git a/.github/workflows/checks-quick.yml b/.github/workflows/checks-quick.yml index 96f214e9427b..c936e7c89389 100644 --- a/.github/workflows/checks-quick.yml +++ b/.github/workflows/checks-quick.yml @@ -35,14 +35,14 @@ jobs: container: image: ${{ needs.set-image.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Cargo fmt run: cargo +nightly fmt --all -- --check check-dependency-rules: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: check dependency rules run: | cd substrate/ @@ -54,7 +54,7 @@ jobs: container: image: ${{ needs.set-image.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: fetch deps run: | # Pull all dependencies eagerly: @@ -70,7 +70,7 @@ jobs: container: image: ${{ needs.set-image.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: run rust features run: bash .gitlab/rust-features.sh . check-toml-format: @@ -80,7 +80,7 @@ jobs: container: image: ${{ needs.set-image.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: check toml format run: | taplo format --check --config .config/taplo.toml @@ -89,7 +89,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.0 (22. Sep 2023) + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.0 (22. Sep 2023) - name: install python deps run: | sudo apt-get update && sudo apt-get install -y python3-pip python3 @@ -109,7 +109,7 @@ jobs: timeout-minutes: 20 steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Setup Node.js uses: actions/setup-node@v4.0.3 with: @@ -134,7 +134,7 @@ jobs: container: image: ${{ needs.set-image.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.0 (22. Sep 2023) + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.0 (22. Sep 2023) - name: install python deps run: pip3 install "cargo-workspace>=1.2.4" toml - name: check umbrella correctness diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 9de879d83676..cba7df517425 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -54,7 +54,7 @@ jobs: RUSTFLAGS: "-D warnings" SKIP_WASM_BUILD: 1 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: script run: | forklift cargo clippy --all-targets --locked --workspace @@ -67,7 +67,7 @@ jobs: container: image: ${{ needs.set-image.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: script run: | forklift cargo check --locked --all --features try-runtime @@ -86,7 +86,7 @@ jobs: container: image: ${{ needs.set-image.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: script run: | cd substrate/primitives/core diff --git a/.github/workflows/misc-sync-templates.yml b/.github/workflows/misc-sync-templates.yml index c06beb5e98eb..658da4451dc2 100644 --- a/.github/workflows/misc-sync-templates.yml +++ b/.github/workflows/misc-sync-templates.yml @@ -157,7 +157,7 @@ jobs: timeout-minutes: 90 - name: Create PR on failure if: failure() && steps.check-compilation.outcome == 'failure' - uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v5 + uses: peter-evans/create-pull-request@8867c4aba1b742c39f8d0ba35429c2dfa4b6cb20 # v5 with: path: "${{ env.template-path }}" token: ${{ steps.app_token.outputs.token }} @@ -167,7 +167,7 @@ jobs: body: "The template has NOT been successfully built and needs to be inspected." branch: "update-template/${{ github.event.inputs.stable_release_branch }}" - name: Create PR on success - uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v5 + uses: peter-evans/create-pull-request@8867c4aba1b742c39f8d0ba35429c2dfa4b6cb20 # v5 with: path: "${{ env.template-path }}" token: ${{ steps.app_token.outputs.token }} diff --git a/.github/workflows/publish-check-crates.yml b/.github/workflows/publish-check-crates.yml index 9f96b92e0ce7..77653cd43b62 100644 --- a/.github/workflows/publish-check-crates.yml +++ b/.github/workflows/publish-check-crates.yml @@ -12,7 +12,7 @@ jobs: check-publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Rust Cache uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 diff --git a/.github/workflows/publish-claim-crates.yml b/.github/workflows/publish-claim-crates.yml index bee709a12076..77f04861bfff 100644 --- a/.github/workflows/publish-claim-crates.yml +++ b/.github/workflows/publish-claim-crates.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest environment: master steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Rust Cache uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 diff --git a/.github/workflows/release-10_rc-automation.yml b/.github/workflows/release-10_rc-automation.yml index 2d91850b82c1..195c14dbd5ab 100644 --- a/.github/workflows/release-10_rc-automation.yml +++ b/.github/workflows/release-10_rc-automation.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 with: fetch-depth: 0 diff --git a/.github/workflows/release-30_publish_release_draft.yml b/.github/workflows/release-30_publish_release_draft.yml index 4343dbf915a9..dd6a111d67e8 100644 --- a/.github/workflows/release-30_publish_release_draft.yml +++ b/.github/workflows/release-30_publish_release_draft.yml @@ -36,7 +36,7 @@ jobs: binary: [ [frame-omni-bencher, frame-omni-bencher], [staging-chain-spec-builder, chain-spec-builder] ] steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.0.0 - name: Install protobuf-compiler run: | @@ -63,7 +63,7 @@ jobs: asset_upload_url: ${{ steps.create-release.outputs.upload_url }} steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.0.0 - name: Download artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 @@ -134,7 +134,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.0.0 - name: Download artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index c5d214ec68ab..72e01a4833e2 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -86,7 +86,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Validate inputs id: validate_inputs @@ -111,7 +111,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 #TODO: this step will be needed when automated triggering will work #this step runs only if the workflow is triggered automatically when new release is published @@ -159,7 +159,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Download artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 @@ -295,7 +295,7 @@ jobs: environment: release steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Set up Docker Buildx uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 diff --git a/.github/workflows/release-branchoff-stable.yml b/.github/workflows/release-branchoff-stable.yml index c236a66a9fae..c4c50f5398e8 100644 --- a/.github/workflows/release-branchoff-stable.yml +++ b/.github/workflows/release-branchoff-stable.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Validate inputs id: validate_inputs @@ -60,7 +60,7 @@ jobs: pip install git+https://github.com/paritytech-release/pgpkms.git@5a8f82fbb607ea102d8c178e761659de54c7af69 - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 with: ref: master diff --git a/.github/workflows/release-check-runtimes.yml b/.github/workflows/release-check-runtimes.yml index 930b8da772d0..6666c115562f 100644 --- a/.github/workflows/release-check-runtimes.yml +++ b/.github/workflows/release-check-runtimes.yml @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Get list id: get-list @@ -56,7 +56,7 @@ jobs: steps: - name: Checkout the repo - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Fetch release artifacts based on release id env: diff --git a/.github/workflows/release-clobber-stable.yml b/.github/workflows/release-clobber-stable.yml index 50c20563b434..0d2ce78ab781 100644 --- a/.github/workflows/release-clobber-stable.yml +++ b/.github/workflows/release-clobber-stable.yml @@ -24,7 +24,7 @@ jobs: AUDITED: audited steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Prechecks run: | diff --git a/.github/workflows/release-srtool.yml b/.github/workflows/release-srtool.yml index 262203f05005..83119dd4ed24 100644 --- a/.github/workflows/release-srtool.yml +++ b/.github/workflows/release-srtool.yml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.0.0 with: fetch-depth: 0 @@ -69,7 +69,7 @@ jobs: matrix: ${{ fromJSON(needs.find-runtimes.outputs.runtime) }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.0.0 with: fetch-depth: 0 diff --git a/.github/workflows/tests-linux-stable-coverage.yml b/.github/workflows/tests-linux-stable-coverage.yml index ddf0642a4043..4c0a2629e418 100644 --- a/.github/workflows/tests-linux-stable-coverage.yml +++ b/.github/workflows/tests-linux-stable-coverage.yml @@ -137,7 +137,7 @@ jobs: needs: [upload-reports] if: github.event_name == 'pull_request' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions-ecosystem/action-remove-labels@v1 with: labels: GHA-coverage \ No newline at end of file diff --git a/.github/workflows/tests-misc.yml b/.github/workflows/tests-misc.yml index 8e8f5770e92d..97ad86e3998e 100644 --- a/.github/workflows/tests-misc.yml +++ b/.github/workflows/tests-misc.yml @@ -60,7 +60,7 @@ jobs: RUST_BACKTRACE: 1 steps: - name: Checkout - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4 - name: script run: | @@ -185,7 +185,7 @@ jobs: needs: [set-image, cargo-check-benches] steps: - name: Checkout - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4 - name: Download artifact (master run) uses: actions/download-artifact@v4.1.8 @@ -225,7 +225,7 @@ jobs: image: ${{ needs.set-image.outputs.IMAGE }} steps: - name: Checkout - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4 - name: Run tests id: tests @@ -257,7 +257,7 @@ jobs: image: ${{ needs.set-image.outputs.IMAGE }} steps: - name: Checkout - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4 - name: script run: | @@ -272,7 +272,7 @@ jobs: image: ${{ needs.set-image.outputs.IMAGE }} steps: - name: Checkout - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4 - name: script run: | @@ -301,7 +301,7 @@ jobs: --config=patch.crates-io.honggfuzz.rev="205f7c8c059a0d98fe1cb912cdac84f324cb6981" steps: - name: Checkout - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4 - name: Run honggfuzz run: | @@ -332,7 +332,7 @@ jobs: index: [1, 2, 3, 4, 5, 6, 7] # 7 parallel jobs steps: - name: Checkout - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4 - name: Check Rust run: | @@ -359,7 +359,7 @@ jobs: # index: [ 1,2,3,4,5,6,7,8,9,10 ] # 10 parallel jobs # steps: # - name: Checkout - # uses: actions/checkout@v4.1.7 + # uses: actions/checkout@v4 # - name: Install dependencies # uses: ./.github/actions/set-up-mac From 8236718e961cb357d46368946f0544ec8ce6718e Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Tue, 10 Sep 2024 17:07:03 +0800 Subject: [PATCH 342/382] Fix edge case where state sync is not triggered (#5635) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR addresses an issue where state sync may fail to start if the conditions required for its initiation are not met when a finalized block notification is received. `pending_state_sync_attempt` is introduced to trigger the state sync later when the conditions are satisfied. This issue was spotted when I worked on #5406, specifically, `queue_blocks` was not empty when the finalized block notification was received, and then the state sync was stalled. cc @dmitry-markin --------- Co-authored-by: Dmitry Markin Co-authored-by: Bastian Köcher --- prdoc/pr_5635.prdoc | 13 ++ .../network/sync/src/strategy/chain_sync.rs | 162 +++++++++++------- 2 files changed, 109 insertions(+), 66 deletions(-) create mode 100644 prdoc/pr_5635.prdoc diff --git a/prdoc/pr_5635.prdoc b/prdoc/pr_5635.prdoc new file mode 100644 index 000000000000..168d65970c95 --- /dev/null +++ b/prdoc/pr_5635.prdoc @@ -0,0 +1,13 @@ +title: Fix edge case where state sync is not triggered + +doc: + - audience: Node Dev + description: | + There is an edge case where the finalized block notification is received, but the conditions required to initiate the + state sync are not fully met. In such cases, state sync would fail to start as expected and remain stalled. + This patch addresses it by storing the pending attempt and trying to start the state sync later when the conditions + are satisfied. + +crates: + - name: sc-network-sync + bump: patch diff --git a/substrate/client/network/sync/src/strategy/chain_sync.rs b/substrate/client/network/sync/src/strategy/chain_sync.rs index f29ed1b083e8..cca83a5055cb 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync.rs @@ -254,6 +254,14 @@ pub struct ChainSync { /// A set of hashes of blocks that are being downloaded or have been /// downloaded and are queued for import. queue_blocks: HashSet, + /// A pending attempt to start the state sync. + /// + /// The initiation of state sync may be deferred in cases where other conditions + /// are not yet met when the finalized block notification is received, such as + /// when `queue_blocks` is not empty or there are no peers. This field holds the + /// necessary information to attempt the state sync at a later point when + /// conditions are satisfied. + pending_state_sync_attempt: Option<(B::Hash, NumberFor, bool)>, /// Fork sync targets. fork_targets: HashMap>, /// A set of peers for which there might be potential block requests @@ -376,6 +384,7 @@ where extra_justifications: ExtraRequests::new("justification", metrics_registry), mode, queue_blocks: Default::default(), + pending_state_sync_attempt: None, fork_targets: Default::default(), allowed_requests: Default::default(), max_parallel_downloads, @@ -497,7 +506,7 @@ where "💔 New peer {} with unknown genesis hash {} ({}).", peer_id, best_hash, best_number, ); - return Err(BadPeer(peer_id, rep::GENESIS_MISMATCH)) + return Err(BadPeer(peer_id, rep::GENESIS_MISMATCH)); } // If there are more than `MAJOR_SYNC_BLOCKS` in the import queue then we have @@ -521,7 +530,7 @@ where state: PeerSyncState::Available, }, ); - return Ok(None) + return Ok(None); } // If we are at genesis, just start downloading. @@ -644,14 +653,14 @@ where if self.is_known(hash) { debug!(target: LOG_TARGET, "Refusing to sync known hash {hash:?}"); - return + return; } trace!(target: LOG_TARGET, "Downloading requested old fork {hash:?}"); for peer_id in &peers { if let Some(peer) = self.peers.get_mut(peer_id) { if let PeerSyncState::AncestorSearch { .. } = peer.state { - continue + continue; } if number > peer.best_number { @@ -748,14 +757,14 @@ where blocks } else { debug!(target: LOG_TARGET, "Unexpected gap block response from {peer_id}"); - return Err(BadPeer(*peer_id, rep::NO_BLOCK)) + return Err(BadPeer(*peer_id, rep::NO_BLOCK)); } }, PeerSyncState::DownloadingStale(_) => { peer.state = PeerSyncState::Available; if blocks.is_empty() { debug!(target: LOG_TARGET, "Empty block response from {peer_id}"); - return Err(BadPeer(*peer_id, rep::NO_BLOCK)) + return Err(BadPeer(*peer_id, rep::NO_BLOCK)); } validate_blocks::(&blocks, peer_id, Some(request))?; blocks @@ -796,14 +805,14 @@ where target: LOG_TARGET, "Invalid response when searching for ancestor from {peer_id}", ); - return Err(BadPeer(*peer_id, rep::UNKNOWN_ANCESTOR)) + return Err(BadPeer(*peer_id, rep::UNKNOWN_ANCESTOR)); }, (_, Err(e)) => { info!( target: LOG_TARGET, "❌ Error answering legitimate blockchain query: {e}", ); - return Err(BadPeer(*peer_id, rep::BLOCKCHAIN_READ_ERROR)) + return Err(BadPeer(*peer_id, rep::BLOCKCHAIN_READ_ERROR)); }, }; if matching_hash.is_some() { @@ -837,7 +846,7 @@ where target: LOG_TARGET, "Ancestry search: genesis mismatch for peer {peer_id}", ); - return Err(BadPeer(*peer_id, rep::GENESIS_MISMATCH)) + return Err(BadPeer(*peer_id, rep::GENESIS_MISMATCH)); } if let Some((next_state, next_num)) = handle_ancestor_search_state(state, *current, matching_hash.is_some()) @@ -852,7 +861,7 @@ where peer_id: *peer_id, request, }); - return Ok(()) + return Ok(()); } else { // Ancestry search is complete. Check if peer is on a stale fork unknown // to us and add it to sync targets if necessary. @@ -892,7 +901,7 @@ where .insert(*peer_id); } peer.state = PeerSyncState::Available; - return Ok(()) + return Ok(()); } }, PeerSyncState::Available | @@ -925,7 +934,7 @@ where } } else { // We don't know of this peer, so we also did not request anything from it. - return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)); }; self.validate_and_queue_blocks(new_blocks, gap); @@ -947,7 +956,7 @@ where target: LOG_TARGET, "💔 Called on_block_justification with a peer ID of an unknown peer", ); - return Ok(()) + return Ok(()); }; self.allowed_requests.add(&peer_id); @@ -964,7 +973,7 @@ where hash, block.hash, ); - return Err(BadPeer(peer_id, rep::BAD_JUSTIFICATION)) + return Err(BadPeer(peer_id, rep::BAD_JUSTIFICATION)); } block @@ -990,7 +999,7 @@ where number, justifications, }); - return Ok(()) + return Ok(()); } } @@ -1013,26 +1022,11 @@ where }); if let ChainSyncMode::LightState { skip_proofs, .. } = &self.mode { - if self.state_sync.is_none() && !self.peers.is_empty() && self.queue_blocks.is_empty() { - // Finalized a recent block. - let mut heads: Vec<_> = self.peers.values().map(|peer| peer.best_number).collect(); - heads.sort(); - let median = heads[heads.len() / 2]; - if number + STATE_SYNC_FINALITY_THRESHOLD.saturated_into() >= median { - if let Ok(Some(header)) = self.client.header(*hash) { - log::debug!( - target: LOG_TARGET, - "Starting state sync for #{number} ({hash})", - ); - self.state_sync = Some(StateSync::new( - self.client.clone(), - header, - None, - None, - *skip_proofs, - )); - self.allowed_requests.set_all(); - } + if self.state_sync.is_none() { + if !self.peers.is_empty() && self.queue_blocks.is_empty() { + self.attempt_state_sync(*hash, number, *skip_proofs); + } else { + self.pending_state_sync_attempt.replace((*hash, number, *skip_proofs)); } } } @@ -1045,6 +1039,35 @@ where } } + fn attempt_state_sync( + &mut self, + finalized_hash: B::Hash, + finalized_number: NumberFor, + skip_proofs: bool, + ) { + let mut heads: Vec<_> = self.peers.values().map(|peer| peer.best_number).collect(); + heads.sort(); + let median = heads[heads.len() / 2]; + if finalized_number + STATE_SYNC_FINALITY_THRESHOLD.saturated_into() >= median { + if let Ok(Some(header)) = self.client.header(finalized_hash) { + log::debug!( + target: LOG_TARGET, + "Starting state sync for #{finalized_number} ({finalized_hash})", + ); + self.state_sync = + Some(StateSync::new(self.client.clone(), header, None, None, skip_proofs)); + self.allowed_requests.set_all(); + } else { + log::error!( + target: LOG_TARGET, + "Failed to start state sync: header for finalized block \ + #{finalized_number} ({finalized_hash}) is not available", + ); + debug_assert!(false); + } + } + } + /// Submit a validated block announcement. /// /// Returns new best hash & best number of the peer if they are updated. @@ -1067,12 +1090,12 @@ where peer } else { error!(target: LOG_TARGET, "💔 Called `on_validated_block_announce` with a bad peer ID {peer_id}"); - return Some((hash, number)) + return Some((hash, number)); }; if let PeerSyncState::AncestorSearch { .. } = peer.state { trace!(target: LOG_TARGET, "Peer {} is in the ancestor search state.", peer_id); - return None + return None; } let peer_info = is_best.then(|| { @@ -1102,7 +1125,7 @@ where if let Some(target) = self.fork_targets.get_mut(&hash) { target.peers.insert(peer_id); } - return peer_info + return peer_info; } if ancient_parent { @@ -1113,7 +1136,7 @@ where hash, announce.header, ); - return peer_info + return peer_info; } if self.status().state == SyncState::Idle { @@ -1281,7 +1304,7 @@ where for (n, peer) in self.peers.iter_mut() { if let PeerSyncState::AncestorSearch { .. } = peer.state { // Wait for ancestry search to complete first. - continue + continue; } let new_common_number = if peer.best_number >= number { number } else { peer.best_number }; @@ -1401,7 +1424,7 @@ where /// What is the status of the block corresponding to the given hash? fn block_status(&self, hash: &B::Hash) -> Result { if self.queue_blocks.contains(hash) { - return Ok(BlockStatus::Queued) + return Ok(BlockStatus::Queued); } self.client.block_status(*hash) } @@ -1521,12 +1544,12 @@ where /// Get block requests scheduled by sync to be sent out. fn block_requests(&mut self) -> Vec<(PeerId, BlockRequest)> { if self.allowed_requests.is_empty() || self.state_sync.is_some() { - return Vec::new() + return Vec::new(); } if self.queue_blocks.len() > MAX_IMPORTING_BLOCKS { trace!(target: LOG_TARGET, "Too many blocks in the queue."); - return Vec::new() + return Vec::new(); } let is_major_syncing = self.status().state.is_major_syncing(); let attrs = self.required_block_attributes(); @@ -1550,7 +1573,7 @@ where !allowed_requests.contains(&id) || !disconnected_peers.is_peer_available(&id) { - return None + return None; } // If our best queued is more than `MAX_BLOCKS_TO_LOOK_BACKWARDS` blocks away from @@ -1648,17 +1671,17 @@ where /// Get a state request scheduled by sync to be sent out (if any). fn state_request(&mut self) -> Option<(PeerId, OpaqueStateRequest)> { if self.allowed_requests.is_empty() { - return None + return None; } if self.state_sync.is_some() && self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingState) { // Only one pending state request is allowed. - return None + return None; } if let Some(sync) = &self.state_sync { if sync.is_complete() { - return None + return None; } for (id, peer) in self.peers.iter_mut() { @@ -1670,7 +1693,7 @@ where let request = sync.next_request(); trace!(target: LOG_TARGET, "New StateRequest for {}: {:?}", id, request); self.allowed_requests.clear(); - return Some((*id, OpaqueStateRequest(Box::new(request)))) + return Some((*id, OpaqueStateRequest(Box::new(request)))); } } } @@ -1709,7 +1732,7 @@ where sync.import(*response) } else { debug!(target: LOG_TARGET, "Ignored obsolete state response from {peer_id}"); - return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)); }; match import_result { @@ -1765,16 +1788,17 @@ where } for (result, hash) in results { if has_error { - break + break; } has_error |= result.is_err(); match result { - Ok(BlockImportStatus::ImportedKnown(number, peer_id)) => + Ok(BlockImportStatus::ImportedKnown(number, peer_id)) => { if let Some(peer) = peer_id { self.update_peer_common_number(&peer, number); - }, + } + }, Ok(BlockImportStatus::ImportedUnknown(number, aux, peer_id)) => { if aux.clear_justification_requests { trace!( @@ -1882,6 +1906,12 @@ where /// Get pending actions to perform. #[must_use] pub fn actions(&mut self) -> impl Iterator> { + if !self.peers.is_empty() && self.queue_blocks.is_empty() { + if let Some((hash, number, skip_proofs)) = self.pending_state_sync_attempt.take() { + self.attempt_state_sync(hash, number, skip_proofs); + } + } + let block_requests = self .block_requests() .into_iter() @@ -1964,7 +1994,7 @@ fn handle_ancestor_search_state( if block_hash_match && next_distance_to_tip == One::one() { // We found the ancestor in the first step so there is no need to execute binary // search. - return None + return None; } if block_hash_match { let left = curr_block_num; @@ -1983,7 +2013,7 @@ fn handle_ancestor_search_state( }, AncestorSearchState::BinarySearch(mut left, mut right) => { if left >= curr_block_num { - return None + return None; } if block_hash_match { left = curr_block_num; @@ -2014,7 +2044,7 @@ fn peer_block_request( ) -> Option<(Range>, BlockRequest)> { if best_num >= peer.best_number { // Will be downloaded as alternative fork instead. - return None + return None; } else if peer.common_number < finalized { trace!( target: LOG_TARGET, @@ -2103,7 +2133,7 @@ fn fork_sync_request( hash, r.number, ); - return false + return false; } if check_block(hash) != BlockStatus::Unknown { trace!( @@ -2112,7 +2142,7 @@ fn fork_sync_request( hash, r.number, ); - return false + return false; } true }); @@ -2121,7 +2151,7 @@ fn fork_sync_request( } for (hash, r) in fork_targets { if !r.peers.contains(&id) { - continue + continue; } // Download the fork only if it is behind or not too far ahead our tip of the chain // Otherwise it should be downloaded in full sync mode. @@ -2148,7 +2178,7 @@ fn fork_sync_request( direction: Direction::Descending, max: Some(count), }, - )) + )); } else { trace!(target: LOG_TARGET, "Fork too far in the future: {:?} (#{})", hash, r.number); } @@ -2167,7 +2197,7 @@ where T: HeaderMetadata + ?Sized, { if base == block { - return Ok(false) + return Ok(false); } let ancestor = sp_blockchain::lowest_common_ancestor(client, *block, *base)?; @@ -2194,7 +2224,7 @@ pub fn validate_blocks( blocks.len(), ); - return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)); } let block_header = @@ -2214,7 +2244,7 @@ pub fn validate_blocks( block_header, ); - return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)); } if request.fields.contains(BlockAttributes::HEADER) && @@ -2225,7 +2255,7 @@ pub fn validate_blocks( "Missing requested header for a block in response from {peer_id}.", ); - return Err(BadPeer(*peer_id, rep::BAD_RESPONSE)) + return Err(BadPeer(*peer_id, rep::BAD_RESPONSE)); } if request.fields.contains(BlockAttributes::BODY) && blocks.iter().any(|b| b.body.is_none()) @@ -2235,7 +2265,7 @@ pub fn validate_blocks( "Missing requested body for a block in response from {peer_id}.", ); - return Err(BadPeer(*peer_id, rep::BAD_RESPONSE)) + return Err(BadPeer(*peer_id, rep::BAD_RESPONSE)); } } @@ -2250,7 +2280,7 @@ pub fn validate_blocks( b.hash, hash, ); - return Err(BadPeer(*peer_id, rep::BAD_BLOCK)) + return Err(BadPeer(*peer_id, rep::BAD_BLOCK)); } } if let (Some(header), Some(body)) = (&b.header, &b.body) { @@ -2268,7 +2298,7 @@ pub fn validate_blocks( expected, got, ); - return Err(BadPeer(*peer_id, rep::BAD_BLOCK)) + return Err(BadPeer(*peer_id, rep::BAD_BLOCK)); } } } From 9079f36b99ee0910762ce6020ca9dfd6671f77b3 Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:48:09 +0200 Subject: [PATCH 343/382] [WIP][ci] GHA improvements (#5653) Changes in PR: - enables `Check Cargo Check Runtimes` (it was accidentally disabled) - reorder jobs in test-misc to make it faster - add `quick-benchmarks-omni` to `check-frame-omni-bencher` (the workflow is currently disabled) --- .../workflows/check-cargo-check-runtimes.yml | 33 ++++++++----- .../workflows/check-frame-omni-bencher.yml | 48 ++++++++++++++++--- .github/workflows/tests-misc.yml | 11 +++-- 3 files changed, 68 insertions(+), 24 deletions(-) diff --git a/.github/workflows/check-cargo-check-runtimes.yml b/.github/workflows/check-cargo-check-runtimes.yml index 6325033d214c..ea0a4b943213 100644 --- a/.github/workflows/check-cargo-check-runtimes.yml +++ b/.github/workflows/check-cargo-check-runtimes.yml @@ -2,27 +2,34 @@ name: Check Cargo Check Runtimes on: pull_request: - types: [opened, synchronize, reopened, ready_for_review, labeled] + types: [opened, synchronize, reopened, ready_for_review] # Jobs in this workflow depend on each other, only for limiting peak amount of spawned workers jobs: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. set-image: - if: contains(github.event.label.name, 'GHA-migration') || contains(github.event.pull_request.labels.*.name, 'GHA-migration') + # GitHub Actions allows using 'env' in a container context. + # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 + # This workaround sets the container image for each job using 'set-image' job output. runs-on: ubuntu-latest - timeout-minutes: 20 outputs: IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} steps: - name: Checkout uses: actions/checkout@v4 - id: set_image run: cat .github/env >> $GITHUB_OUTPUT + - id: set_runner + run: | + # Run merge queues on persistent runners + if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT + else + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + fi check-runtime-assets: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.set-image.outputs.RUNNER }} needs: [set-image] timeout-minutes: 20 container: @@ -36,7 +43,7 @@ jobs: root: cumulus/parachains/runtimes/assets check-runtime-collectives: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.set-image.outputs.RUNNER }} needs: [check-runtime-assets, set-image] timeout-minutes: 20 container: @@ -50,7 +57,7 @@ jobs: root: cumulus/parachains/runtimes/collectives check-runtime-coretime: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} needs: [check-runtime-assets, set-image] @@ -64,7 +71,7 @@ jobs: root: cumulus/parachains/runtimes/coretime check-runtime-bridge-hubs: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} needs: [set-image] @@ -78,7 +85,7 @@ jobs: root: cumulus/parachains/runtimes/bridge-hubs check-runtime-contracts: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} needs: [check-runtime-collectives, set-image] @@ -92,7 +99,7 @@ jobs: root: cumulus/parachains/runtimes/contracts check-runtime-starters: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} needs: [check-runtime-assets, set-image] @@ -106,7 +113,7 @@ jobs: root: cumulus/parachains/runtimes/starters check-runtime-testing: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} needs: [check-runtime-starters, set-image] diff --git a/.github/workflows/check-frame-omni-bencher.yml b/.github/workflows/check-frame-omni-bencher.yml index e9db2d912979..e035a30c7c22 100644 --- a/.github/workflows/check-frame-omni-bencher.yml +++ b/.github/workflows/check-frame-omni-bencher.yml @@ -5,7 +5,7 @@ on: branches: - master pull_request: - types: [ opened, synchronize, reopened, ready_for_review, labeled ] + types: [opened, synchronize, reopened, ready_for_review, labeled] merge_group: concurrency: @@ -28,19 +28,46 @@ jobs: # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 # This workaround sets the container image for each job using 'set-image' job output. runs-on: ubuntu-latest - needs: changes - if: ${{ needs.changes.outputs.rust }} outputs: IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} steps: - name: Checkout uses: actions/checkout@v4 - id: set_image run: cat .github/env >> $GITHUB_OUTPUT + - id: set_runner + run: | + # Run merge queues on persistent runners + if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT + else + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + fi + + quick-benchmarks-omni: + runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [set-image, changes] + if: ${{ needs.changes.outputs.rust }} + env: + RUSTFLAGS: "-C debug-assertions" + RUST_BACKTRACE: "full" + WASM_BUILD_NO_COLOR: 1 + WASM_BUILD_RUSTFLAGS: "-C debug-assertions" + timeout-minutes: 30 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: script + run: | + forklift cargo build --locked --quiet --release -p asset-hub-westend-runtime --features runtime-benchmarks + forklift cargo run --locked --release -p frame-omni-bencher --quiet -- v1 benchmark pallet --runtime target/release/wbuild/asset-hub-westend-runtime/asset_hub_westend_runtime.compact.compressed.wasm --all --steps 2 --repeat 1 --quiet run-frame-omni-bencher: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [ set-image, changes ] # , build-frame-omni-bencher ] + runs-on: ${{ needs.set-image.outputs.RUNNER }} + needs: [set-image, changes] # , build-frame-omni-bencher ] if: ${{ needs.changes.outputs.rust }} timeout-minutes: 30 strategy: @@ -81,5 +108,14 @@ jobs: runs-on: ubuntu-latest name: All benchmarks passed needs: run-frame-omni-bencher + if: always() && !cancelled() steps: - - run: echo '### Good job! All the benchmarks passed 🚀' >> $GITHUB_STEP_SUMMARY + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed ❌" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed 🚀' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/tests-misc.yml b/.github/workflows/tests-misc.yml index 97ad86e3998e..e4f0d6575c53 100644 --- a/.github/workflows/tests-misc.yml +++ b/.github/workflows/tests-misc.yml @@ -93,7 +93,7 @@ jobs: test-frame-ui: timeout-minutes: 60 - needs: [set-image, test-frame-examples-compile-to-wasm] + needs: [set-image] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -121,7 +121,7 @@ jobs: test-deterministic-wasm: timeout-minutes: 20 - needs: [set-image] + needs: [set-image, test-frame-examples-compile-to-wasm] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -148,7 +148,7 @@ jobs: timeout-minutes: 60 strategy: matrix: - branch: [ master, current ] + branch: [master, current] runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -158,7 +158,7 @@ jobs: with: # if branch is master, use the branch, otherwise set empty string, so it uses the current context # either PR (including forks) or merge group (main repo) - ref: ${{ matrix.branch == 'master' && matrix.branch || '' }} + ref: ${{ matrix.branch == 'master' && matrix.branch || '' }} - name: script run: | @@ -181,7 +181,8 @@ jobs: node-bench-regression-guard: timeout-minutes: 20 if: always() && !cancelled() - runs-on: arc-runners-polkadot-sdk + # runs-on: arc-runners-polkadot-sdk + runs-on: ubuntu-latest needs: [set-image, cargo-check-benches] steps: - name: Checkout From 3779af2bb81cb9f022c1b9d3567613ede8049629 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 10 Sep 2024 19:21:51 +0800 Subject: [PATCH 344/382] Update bridges/snowbridge/primitives/router/src/inbound/mod.rs Co-authored-by: Adrian Catangiu --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 6c107cfa07c2..10630fc2ad84 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -394,7 +394,7 @@ impl< asset_hub_fee: u128, ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { let network = Ethereum { chain_id }; - let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee / 2).into(); + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); let (dest_para_id, beneficiary, dest_para_fee) = match destination { // Final destination is a 32-byte account on AssetHub From 96c01dc5dc8750dc96458326b6170242e0909e73 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 10 Sep 2024 19:22:26 +0800 Subject: [PATCH 345/382] Update bridges/snowbridge/pallets/system/src/tests.rs Co-authored-by: Adrian Catangiu --- bridges/snowbridge/pallets/system/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index d436724caba6..51cb377b24a7 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -688,7 +688,7 @@ fn register_all_tokens_succeeds() { }, // USDT TokenInfo { - location: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1084)]), + location: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), metadata: AssetMetadata { decimals: 6, name: b"USDT".to_vec().try_into().unwrap(), From a802127d9248f6e006b5597cb56cae0e100e0cdc Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 10 Sep 2024 20:57:41 +0800 Subject: [PATCH 346/382] Use full location of Ethereum --- bridges/snowbridge/pallets/system/src/lib.rs | 6 +++--- bridges/snowbridge/pallets/system/src/mock.rs | 2 +- .../bridge-hub-rococo/src/bridge_to_ethereum_config.rs | 4 ++-- .../bridge-hub-westend/src/bridge_to_ethereum_config.rs | 4 ++-- cumulus/parachains/runtimes/constants/src/rococo.rs | 3 ++- cumulus/parachains/runtimes/constants/src/westend.rs | 3 ++- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 9712f589b808..8f3235279956 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -177,8 +177,8 @@ pub mod pallet { /// This chain's Universal Location. type UniversalLocation: Get; - // The bridges configured Ethereum network with chain id. - type EthereumNetwork: Get; + // The bridges configured Ethereum location + type EthereumLocation: Get; #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; @@ -732,7 +732,7 @@ pub mod pallet { metadata: AssetMetadata, pays_fee: PaysFee, ) -> Result<(), DispatchError> { - let ethereum_location = Location::new(2, [GlobalConsensus(T::EthereumNetwork::get())]); + let ethereum_location = T::EthereumLocation::get(); // reanchor to Ethereum context let location = location .clone() diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index f70641288250..47b089866a53 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -210,7 +210,7 @@ impl crate::Config for Test { type WeightInfo = (); type InboundDeliveryCost = InboundDeliveryCost; type UniversalLocation = UniversalLocation; - type EthereumNetwork = EthereumNetwork; + type EthereumLocation = EthereumDestination; #[cfg(feature = "runtime-benchmarks")] type Helper = (); } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index fde214ed42d6..d9a3869bd6e1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -29,7 +29,7 @@ use sp_core::H160; use testnet_parachains_constants::rococo::{ currency::*, fee::WeightToFee, - snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, + snowbridge::{EthereumLocation, EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, }; use crate::xcm_config::RelayNetwork; @@ -190,7 +190,7 @@ impl snowbridge_pallet_system::Config for Runtime { type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; type UniversalLocation = UniversalLocation; - type EthereumNetwork = EthereumNetwork; + type EthereumLocation = EthereumLocation; } #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 4b3f61a42ad4..1bd425ab4075 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -30,7 +30,7 @@ use sp_core::H160; use testnet_parachains_constants::westend::{ currency::*, fee::WeightToFee, - snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, + snowbridge::{EthereumLocation, EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, }; use crate::xcm_config::RelayNetwork; @@ -189,7 +189,7 @@ impl snowbridge_pallet_system::Config for Runtime { type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; type UniversalLocation = UniversalLocation; - type EthereumNetwork = EthereumNetwork; + type EthereumLocation = EthereumLocation; } #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/constants/src/rococo.rs b/cumulus/parachains/runtimes/constants/src/rococo.rs index 56f4868371c1..6c5380df2bfb 100644 --- a/cumulus/parachains/runtimes/constants/src/rococo.rs +++ b/cumulus/parachains/runtimes/constants/src/rococo.rs @@ -148,7 +148,7 @@ pub mod time { pub mod snowbridge { use frame_support::parameter_types; - use xcm::opaque::lts::NetworkId; + use xcm::prelude::{Location, NetworkId}; /// The pallet index of the Ethereum inbound queue pallet in the bridge hub runtime. pub const INBOUND_QUEUE_PALLET_INDEX: u8 = 80; @@ -159,6 +159,7 @@ pub mod snowbridge { /// /// pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub EthereumLocation: Location = Location::new(2,EthereumNetwork::get()); } } diff --git a/cumulus/parachains/runtimes/constants/src/westend.rs b/cumulus/parachains/runtimes/constants/src/westend.rs index fec66cec2eb6..a4667f83fbef 100644 --- a/cumulus/parachains/runtimes/constants/src/westend.rs +++ b/cumulus/parachains/runtimes/constants/src/westend.rs @@ -171,7 +171,7 @@ pub mod time { pub mod snowbridge { use frame_support::parameter_types; - use xcm::opaque::lts::NetworkId; + use xcm::prelude::{Location, NetworkId}; /// The pallet index of the Ethereum inbound queue pallet in the bridge hub runtime. pub const INBOUND_QUEUE_PALLET_INDEX: u8 = 80; @@ -182,5 +182,6 @@ pub mod snowbridge { /// /// pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub EthereumLocation: Location = Location::new(2,EthereumNetwork::get()); } } From baf5b27b37c0c26420d7ecd55ef2c3878da66bb4 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 10 Sep 2024 20:57:49 +0800 Subject: [PATCH 347/382] Fix test --- bridges/snowbridge/pallets/system/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 51cb377b24a7..b3699ec2f24d 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -695,7 +695,7 @@ fn register_all_tokens_succeeds() { symbol: b"USDT".to_vec().try_into().unwrap(), }, foreign_token_id: hex!( - "d49fe2118be0cca618e4d171e60ffea98b7b648dd80dc37d6342116b910b7aa5" + "14b0579be12d7d7f9971f1d4b41f0e88384b9b74799b0150d4aa6cd01afb4444" ) .into(), }, From d887804b235003282b26d4202f40c79fb302f16c Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 10 Sep 2024 15:28:28 +0200 Subject: [PATCH 348/382] [Bot] Use correct token in backport bot (#5654) The backport bot does currently not trigger the CI when opening a MR, like here: https://github.com/paritytech/polkadot-sdk/pull/5651 Devs need to push an empty commit manually. Now using a token that will also trigger the CI. --------- Signed-off-by: Oliver Tale-Yazdi --- .github/workflows/command-backport.yml | 32 ++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/.github/workflows/command-backport.yml b/.github/workflows/command-backport.yml index 1ad68d96a63f..85e7b77801d7 100644 --- a/.github/workflows/command-backport.yml +++ b/.github/workflows/command-backport.yml @@ -27,15 +27,24 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@v2.1.0 + with: + app_id: ${{ secrets.CMD_BOT_APP_ID }} + private_key: ${{ secrets.CMD_BOT_APP_KEY }} + - name: Create backport pull requests uses: korthout/backport-action@v3 id: backport with: target_branches: stable2407 stable2409 merge_commits: skip - github_token: ${{ secrets.GITHUB_TOKEN }} + github_token: ${{ steps.generate_token.outputs.token }} pull_description: | - Backport #${pull_number} into `${target_branch}` (cc @${pull_author}). + Backport #${pull_number} into `${target_branch}` from ${pull_author}. + + See the [documentation](https://github.com/paritytech/polkadot-sdk/blob/master/docs/BACKPORT.md) on how to use this bot. tests/construct_runtime_ui/undefined_event_part.rs:66:1 + | +66 | / construct_runtime! { +67 | | pub struct Runtime +68 | | { +69 | | System: frame_system expanded::{}::{Pallet, Call, Storage, Config, Event}, +70 | | Pallet: pallet expanded::{}::{Pallet, Event}, +71 | | } +72 | | } + | |_^ could not find `Event` in `pallet` + | + = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing one of these items + | +18 + use frame_support_test::Event; + | +18 + use frame_system::Event; + | diff --git a/substrate/frame/support/test/tests/enum_deprecation.rs b/substrate/frame/support/test/tests/enum_deprecation.rs new file mode 100644 index 000000000000..ed9b2b5a735d --- /dev/null +++ b/substrate/frame/support/test/tests/enum_deprecation.rs @@ -0,0 +1,173 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![allow(useless_deprecated, deprecated, clippy::deprecated_semver)] + +use std::collections::BTreeMap; + +use frame_support::{ + derive_impl, + dispatch::Parameter, + parameter_types, + traits::{ConstU32, StorageVersion}, + OrdNoBound, PartialOrdNoBound, +}; +use scale_info::TypeInfo; + +parameter_types! { + /// Used to control if the storage version should be updated. + storage UpdateStorageVersion: bool = false; +} + +pub struct SomeType1; +impl From for u64 { + fn from(_t: SomeType1) -> Self { + 0u64 + } +} + +pub trait SomeAssociation1 { + type _1: Parameter + codec::MaxEncodedLen + TypeInfo; +} +impl SomeAssociation1 for u64 { + type _1 = u64; +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + + pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(10); + + #[pallet::config] + pub trait Config: frame_system::Config + where + ::AccountId: From + SomeAssociation1, + { + type Balance: Parameter + Default + TypeInfo; + + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::error] + #[derive(PartialEq, Eq)] + pub enum Error { + /// error doc comment put in metadata + InsufficientProposersBalance, + NonExistentStorageValue, + Code(u8), + #[codec(skip)] + Skipped(u128), + CompactU8(#[codec(compact)] u8), + } + + #[pallet::event] + pub enum Event + where + T::AccountId: SomeAssociation1 + From, + { + #[deprecated = "second"] + A, + #[deprecated = "first"] + #[codec(index = 0)] + B, + } + + #[pallet::origin] + #[derive( + EqNoBound, + RuntimeDebugNoBound, + CloneNoBound, + PartialEqNoBound, + PartialOrdNoBound, + OrdNoBound, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + )] + pub struct Origin(PhantomData); +} + +frame_support::parameter_types!( + pub const MyGetParam3: u32 = 12; +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type Nonce = u64; + type RuntimeCall = RuntimeCall; + type Hash = sp_runtime::testing::H256; + type Hashing = sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = sp_runtime::traits::IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type MaxConsumers = ConstU32<16>; +} +impl pallet::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = u64; +} + +pub type Header = sp_runtime::generic::Header; +pub type Block = sp_runtime::generic::Block; +pub type UncheckedExtrinsic = + sp_runtime::testing::TestXt>; + +frame_support::construct_runtime!( + pub struct Runtime { + // Exclude part `Storage` in order not to check its metadata in tests. + System: frame_system exclude_parts { Pallet, Storage }, + Example: pallet, + + } +); + +#[test] +fn pallet_metadata() { + use sp_metadata_ir::{DeprecationInfoIR, DeprecationStatusIR}; + let pallets = Runtime::metadata_ir().pallets; + let example = pallets[0].clone(); + { + // Example pallet events are partially and fully deprecated + let meta = example.event.unwrap(); + assert_eq!( + // Result should be this, but instead we get the result below + // see: https://github.com/paritytech/parity-scale-codec/issues/507 + // + // DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([ + // (codec::Compact(0), DeprecationStatusIR::Deprecated { note: "first", since: None + // }), ( + // codec::Compact(1), + // DeprecationStatusIR::Deprecated { note: "second", since: None } + // ) + // ])), + DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([( + codec::Compact(0), + DeprecationStatusIR::Deprecated { note: "first", since: None } + ),])), + meta.deprecation_info + ); + } +} diff --git a/substrate/frame/support/test/tests/instance.rs b/substrate/frame/support/test/tests/instance.rs index 30b8338bc5c7..7f8423a0127e 100644 --- a/substrate/frame/support/test/tests/instance.rs +++ b/substrate/frame/support/test/tests/instance.rs @@ -441,6 +441,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0, 0, 0, 0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Map", @@ -452,6 +453,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: [0u8; 8].to_vec(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "DoubleMap", @@ -463,6 +465,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: [0u8; 8].to_vec(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ], } diff --git a/substrate/frame/support/test/tests/pallet.rs b/substrate/frame/support/test/tests/pallet.rs index eed8a22e8e79..7f1ce0556eab 100644 --- a/substrate/frame/support/test/tests/pallet.rs +++ b/substrate/frame/support/test/tests/pallet.rs @@ -14,6 +14,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(useless_deprecated, deprecated, clippy::deprecated_semver)] + +use std::collections::BTreeMap; use frame_support::{ assert_ok, derive_impl, @@ -212,6 +215,7 @@ pub mod pallet { /// call foo doc comment put in metadata #[pallet::call_index(0)] #[pallet::weight(Weight::from_parts(*foo as u64, 0))] + #[deprecated = "test"] pub fn foo( origin: OriginFor, #[pallet::compact] foo: u32, @@ -272,6 +276,7 @@ pub mod pallet { pub enum Error { /// error doc comment put in metadata InsufficientProposersBalance, + #[deprecated = "test"] NonExistentStorageValue, Code(u8), #[codec(skip)] @@ -283,6 +288,7 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] + #[deprecated = "test"] pub enum Event where T::AccountId: SomeAssociation1 + From, @@ -486,7 +492,7 @@ pub mod pallet { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType5); // Test for where clause if matches!(call, Call::foo_storage_layer { .. }) { - return Ok(ValidTransaction::default()) + return Ok(ValidTransaction::default()); } Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) } @@ -553,6 +559,7 @@ pub mod pallet { // Test that a pallet with non generic event and generic genesis_config is correctly handled // and that a pallet with the attribute without_storage_info is correctly handled. #[frame_support::pallet] +#[deprecated = "test"] pub mod pallet2 { use super::{SomeAssociation1, SomeType1, UpdateStorageVersion}; use frame_support::pallet_prelude::*; @@ -2478,3 +2485,56 @@ fn test_error_feature_parsing() { pallet::Error::__Ignore(_, _) => (), } } + +#[test] +fn pallet_metadata() { + use sp_metadata_ir::{DeprecationInfoIR, DeprecationStatusIR}; + let pallets = Runtime::metadata_ir().pallets; + let example = pallets[0].clone(); + let example2 = pallets[1].clone(); + { + // Example2 pallet is deprecated + assert_eq!( + &DeprecationStatusIR::Deprecated { note: "test", since: None }, + &example2.deprecation_info + ) + } + { + // Example pallet calls is fully and partially deprecated + let meta = &example.calls.unwrap(); + assert_eq!( + DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([( + codec::Compact(0), + DeprecationStatusIR::Deprecated { note: "test", since: None } + )])), + meta.deprecation_info + ) + } + { + // Example pallet errors are partially and fully deprecated + let meta = &example.error.unwrap(); + assert_eq!( + DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([( + codec::Compact(2), + DeprecationStatusIR::Deprecated { note: "test", since: None } + )])), + meta.deprecation_info + ) + } + { + // Example pallet events are partially and fully deprecated + let meta = example.event.unwrap(); + assert_eq!( + DeprecationInfoIR::ItemDeprecated(DeprecationStatusIR::Deprecated { + note: "test", + since: None + }), + meta.deprecation_info + ); + } + { + // Example2 pallet events are not deprecated + let meta = example2.event.unwrap(); + assert_eq!(DeprecationInfoIR::NotDeprecated, meta.deprecation_info); + } +} diff --git a/substrate/frame/support/test/tests/runtime_metadata.rs b/substrate/frame/support/test/tests/runtime_metadata.rs index 48e4d975eb08..81377210eb43 100644 --- a/substrate/frame/support/test/tests/runtime_metadata.rs +++ b/substrate/frame/support/test/tests/runtime_metadata.rs @@ -14,11 +14,13 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(useless_deprecated, deprecated, clippy::deprecated_semver)] use frame_support::{derive_impl, traits::ConstU32}; use scale_info::{form::MetaForm, meta_type}; use sp_metadata_ir::{ - RuntimeApiMetadataIR, RuntimeApiMethodMetadataIR, RuntimeApiMethodParamMetadataIR, + DeprecationStatusIR, RuntimeApiMetadataIR, RuntimeApiMethodMetadataIR, + RuntimeApiMethodParamMetadataIR, }; use sp_runtime::traits::Block as BlockT; @@ -64,12 +66,16 @@ sp_api::decl_runtime_apis! { /// ApiWithCustomVersion trait documentation /// /// Documentation on multiline. + #[deprecated] pub trait Api { fn test(data: u64); /// something_with_block. fn something_with_block(block: Block) -> Block; + #[deprecated = "example"] fn function_with_two_args(data: u64, block: Block); + #[deprecated(note = "example", since = "example")] fn same_name(); + #[deprecated(note = "example")] fn wild_card(_: u32); } } @@ -128,6 +134,7 @@ fn runtime_metadata() { }], output: meta_type::<()>(), docs: vec![], + deprecation_info: DeprecationStatusIR::NotDeprecated, }, RuntimeApiMethodMetadataIR { name: "something_with_block", @@ -137,6 +144,7 @@ fn runtime_metadata() { }], output: meta_type::(), docs: maybe_docs(vec![" something_with_block."]), + deprecation_info: DeprecationStatusIR::NotDeprecated, }, RuntimeApiMethodMetadataIR { name: "function_with_two_args", @@ -152,13 +160,21 @@ fn runtime_metadata() { ], output: meta_type::<()>(), docs: vec![], + deprecation_info: DeprecationStatusIR::Deprecated { + note: "example", + since: None, + } }, RuntimeApiMethodMetadataIR { name: "same_name", inputs: vec![], output: meta_type::<()>(), docs: vec![], - }, + deprecation_info: DeprecationStatusIR::Deprecated { + note: "example", + since: Some("example"), + } + }, RuntimeApiMethodMetadataIR { name: "wild_card", inputs: vec![RuntimeApiMethodParamMetadataIR:: { @@ -167,6 +183,10 @@ fn runtime_metadata() { }], output: meta_type::<()>(), docs: vec![], + deprecation_info: DeprecationStatusIR::Deprecated { + note: "example", + since: None, + } }, ], docs: maybe_docs(vec![ @@ -174,6 +194,8 @@ fn runtime_metadata() { "", " Documentation on multiline.", ]), + deprecation_info: DeprecationStatusIR::DeprecatedWithoutNote, + }, RuntimeApiMetadataIR { name: "Core", @@ -183,6 +205,7 @@ fn runtime_metadata() { inputs: vec![], output: meta_type::(), docs: maybe_docs(vec![" Returns the version of the runtime."]), + deprecation_info: DeprecationStatusIR::NotDeprecated, }, RuntimeApiMethodMetadataIR { name: "execute_block", @@ -192,6 +215,8 @@ fn runtime_metadata() { }], output: meta_type::<()>(), docs: maybe_docs(vec![" Execute the given block."]), + deprecation_info: DeprecationStatusIR::NotDeprecated, + }, RuntimeApiMethodMetadataIR { name: "initialize_block", @@ -201,11 +226,13 @@ fn runtime_metadata() { }], output: meta_type::(), docs: maybe_docs(vec![" Initialize a block with the given header and return the runtime executive mode."]), + deprecation_info: DeprecationStatusIR::NotDeprecated, }, ], docs: maybe_docs(vec![ " The `Core` runtime api that every Substrate runtime needs to implement.", ]), + deprecation_info: DeprecationStatusIR::NotDeprecated, }, ]; diff --git a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs index 2c423f8c28dd..21397abc8fc6 100644 --- a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -471,7 +471,7 @@ fn extend_with_api_version(mut trait_: Path, version: Option) -> Path { v } else { // nothing to do - return trait_ + return trait_; }; let trait_name = &mut trait_ @@ -762,7 +762,7 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result { error.combine(Error::new(other_span, "First trait implementation.")); - return Err(error) + return Err(error); } let id: Path = parse_quote!( #path ID ); @@ -892,7 +892,7 @@ fn extract_cfg_api_version(attrs: &Vec, span: Span) -> Result, span: Span) -> Result Each runtime API can have only one version.", API_VERSION_ATTRIBUTE ), - )) + )); } // Parse the runtime version if there exists one. diff --git a/substrate/primitives/api/proc-macro/src/runtime_metadata.rs b/substrate/primitives/api/proc-macro/src/runtime_metadata.rs index 9944927d5573..4cba524dbe25 100644 --- a/substrate/primitives/api/proc-macro/src/runtime_metadata.rs +++ b/substrate/primitives/api/proc-macro/src/runtime_metadata.rs @@ -94,7 +94,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { let is_changed_in = method.attrs.iter().any(|attr| attr.path().is_ident(CHANGED_IN_ATTRIBUTE)); if is_changed_in { - continue + continue; } let mut inputs = Vec::new(); @@ -131,6 +131,10 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { // Include the method metadata only if its `cfg` features are enabled. let attrs = filter_cfg_attributes(&method.attrs); + let deprecation = match crate::utils::get_deprecation(&crate_, &method.attrs) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; methods.push(quote!( #( #attrs )* #crate_::metadata_ir::RuntimeApiMethodMetadataIR { @@ -138,6 +142,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { inputs: #crate_::vec![ #( #inputs, )* ], output: #output, docs: #docs, + deprecation_info: #deprecation, } )); } @@ -145,6 +150,10 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { let trait_name_ident = &decl.ident; let trait_name = trait_name_ident.to_string(); let docs = collect_docs(&decl.attrs, &crate_); + let deprecation = match crate::utils::get_deprecation(&crate_, &decl.attrs) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; let attrs = filter_cfg_attributes(&decl.attrs); // The trait generics where already extended with `Block: BlockT`. let mut generics = decl.generics.clone(); @@ -174,6 +183,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { name: #trait_name, methods: #crate_::vec![ #( #methods, )* ], docs: #docs, + deprecation_info: #deprecation, } } } @@ -187,7 +197,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { /// exposed by `generate_decl_runtime_metadata`. pub fn generate_impl_runtime_metadata(impls: &[ItemImpl]) -> Result { if impls.is_empty() { - return Ok(quote!()) + return Ok(quote!()); } let crate_ = generate_crate_access(); diff --git a/substrate/primitives/api/proc-macro/src/utils.rs b/substrate/primitives/api/proc-macro/src/utils.rs index 36577670a40c..94da6748cbdb 100644 --- a/substrate/primitives/api/proc-macro/src/utils.rs +++ b/substrate/primitives/api/proc-macro/src/utils.rs @@ -21,8 +21,9 @@ use proc_macro2::{Span, TokenStream}; use proc_macro_crate::{crate_name, FoundCrate}; use quote::{format_ident, quote}; use syn::{ - parse_quote, spanned::Spanned, token::And, Attribute, Error, FnArg, GenericArgument, Ident, - ImplItem, ItemImpl, Pat, Path, PathArguments, Result, ReturnType, Signature, Type, TypePath, + parse_quote, punctuated::Punctuated, spanned::Spanned, token::And, Attribute, Error, Expr, + ExprLit, FnArg, GenericArgument, Ident, ImplItem, ItemImpl, Lit, Meta, MetaNameValue, Pat, + Path, PathArguments, Result, ReturnType, Signature, Token, Type, TypePath, }; /// Generates the access to the `sc_client` crate. @@ -33,7 +34,7 @@ pub fn generate_crate_access() -> TokenStream { let renamed_name = Ident::new(&renamed_name, Span::call_site()); quote!(#renamed_name::__private) }, - Err(e) => + Err(e) => { if let Ok(FoundCrate::Name(name)) = crate_name(&"polkadot-sdk-frame").or_else(|_| crate_name(&"frame")) { @@ -47,7 +48,8 @@ pub fn generate_crate_access() -> TokenStream { } else { let err = Error::new(Span::call_site(), e).to_compile_error(); quote!( #err ) - }, + } + }, } } @@ -144,7 +146,7 @@ pub fn extract_parameter_names_types_and_borrows( return Err(Error::new(input.span(), "`self` parameter not supported!")), FnArg::Receiver(recv) => if recv.mutability.is_some() || recv.reference.is_none() { - return Err(Error::new(recv.span(), "Only `&self` is supported!")) + return Err(Error::new(recv.span(), "Only `&self` is supported!")); }, } } @@ -284,6 +286,85 @@ pub fn filter_cfg_attributes(attrs: &[syn::Attribute]) -> Vec { attrs.iter().filter(|a| a.path().is_ident("cfg")).cloned().collect() } +fn deprecation_msg_formatter(msg: &str) -> String { + format!( + r#"{msg} + help: the following are the possible correct uses +| +| #[deprecated = "reason"] +| +| #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] +| +| #[deprecated] +|"# + ) +} + +fn parse_deprecated_meta(crate_: &TokenStream, attr: &syn::Attribute) -> Result { + match &attr.meta { + Meta::List(meta_list) => { + let parsed = meta_list + .parse_args_with(Punctuated::::parse_terminated) + .map_err(|e| Error::new(attr.span(), e.to_string()))?; + let (note, since) = parsed.iter().try_fold((None, None), |mut acc, item| { + let value = match &item.value { + Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }) => Ok(lit), + _ => Err(Error::new( + attr.span(), + deprecation_msg_formatter( + "Invalid deprecation attribute: expected string literal", + ), + )), + }?; + if item.path.is_ident("note") { + acc.0.replace(value); + } else if item.path.is_ident("since") { + acc.1.replace(value); + } + Ok::<(Option<&syn::Lit>, Option<&syn::Lit>), Error>(acc) + })?; + note.map_or_else( + || Err(Error::new(attr.span(), deprecation_msg_formatter( + "Invalid deprecation attribute: missing `note`"))), + |note| { + let since = if let Some(str) = since { + quote! { Some(#str) } + } else { + quote! { None } + }; + let doc = quote! { #crate_::metadata_ir::DeprecationStatusIR::Deprecated { note: #note, since: #since }}; + Ok(doc) + }, + ) + }, + Meta::NameValue(MetaNameValue { + value: Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }), + .. + }) => { + // #[deprecated = "lit"] + let doc = quote! { #crate_::metadata_ir::DeprecationStatusIR::Deprecated { note: #lit, since: None } }; + Ok(doc) + }, + Meta::Path(_) => { + // #[deprecated] + Ok(quote! { #crate_::metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote }) + }, + _ => Err(Error::new( + attr.span(), + deprecation_msg_formatter("Invalid deprecation attribute: expected string literal"), + )), + } +} + +/// collects deprecation attribute if its present. +pub fn get_deprecation(crate_: &TokenStream, attrs: &[syn::Attribute]) -> Result { + attrs + .iter() + .find(|a| a.path().is_ident("deprecated")) + .map(|a| parse_deprecated_meta(&crate_, a)) + .unwrap_or_else(|| Ok(quote! {#crate_::metadata_ir::DeprecationStatusIR::NotDeprecated})) +} + #[cfg(test)] mod tests { use assert_matches::assert_matches; @@ -330,4 +411,38 @@ mod tests { assert_eq!(cfg_std, filtered[0]); assert_eq!(cfg_benchmarks, filtered[1]); } + + #[test] + fn check_deprecated_attr() { + const FIRST: &'static str = "hello"; + const SECOND: &'static str = "WORLD"; + + let simple: Attribute = parse_quote!(#[deprecated]); + let simple_path: Attribute = parse_quote!(#[deprecated = #FIRST]); + let meta_list: Attribute = parse_quote!(#[deprecated(note = #FIRST)]); + let meta_list_with_since: Attribute = + parse_quote!(#[deprecated(note = #FIRST, since = #SECOND)]); + let extra_fields: Attribute = + parse_quote!(#[deprecated(note = #FIRST, since = #SECOND, extra = "Test")]); + assert_eq!( + get_deprecation("e! { crate }, &[simple]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote }.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[simple_path]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: None } }.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[meta_list]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: None } }.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[meta_list_with_since]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: Some(#SECOND) }}.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[extra_fields]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: Some(#SECOND) }}.to_string() + ); + } } diff --git a/substrate/primitives/api/src/lib.rs b/substrate/primitives/api/src/lib.rs index 4b5c35562bde..700e212688c8 100644 --- a/substrate/primitives/api/src/lib.rs +++ b/substrate/primitives/api/src/lib.rs @@ -258,6 +258,11 @@ pub const MAX_EXTRINSIC_DEPTH: u32 = 256; /// ``` /// Note that the latest version (4 in our example above) always contains all methods from all /// the versions before. +/// +/// ## Note on deprecation. +/// +/// - Usage of `deprecated` attribute will propagate deprecation information to the metadata. +/// - For general usage examples of `deprecated` attribute please refer to pub use sp_api_proc_macro::decl_runtime_apis; /// Tags given trait implementations as runtime apis. diff --git a/substrate/primitives/api/test/tests/ui/deprecation_info.rs b/substrate/primitives/api/test/tests/ui/deprecation_info.rs new file mode 100644 index 000000000000..61f93fcd921d --- /dev/null +++ b/substrate/primitives/api/test/tests/ui/deprecation_info.rs @@ -0,0 +1,29 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +sp_api::decl_runtime_apis! { + pub trait Api { + #[deprecated(unknown_kw = "test")] + fn test(); + #[deprecated(since = 5)] + fn test2(); + #[deprecated = 5] + fn test3(); + } +} + +fn main() {} diff --git a/substrate/primitives/api/test/tests/ui/deprecation_info.stderr b/substrate/primitives/api/test/tests/ui/deprecation_info.stderr new file mode 100644 index 000000000000..2466c3ea5d50 --- /dev/null +++ b/substrate/primitives/api/test/tests/ui/deprecation_info.stderr @@ -0,0 +1,25 @@ +error: Invalid deprecation attribute: missing `note` + help: the following are the possible correct uses + | + | #[deprecated = "reason"] + | + | #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | + | #[deprecated] + | + --> tests/ui/deprecation_info.rs:20:3 + | +20 | #[deprecated(unknown_kw = "test")] + | ^ + +error[E0541]: unknown meta item 'unknown_kw' + --> tests/ui/deprecation_info.rs:20:16 + | +20 | #[deprecated(unknown_kw = "test")] + | ^^^^^^^^^^^^^^^^^^^ expected one of `since`, `note` + +error[E0565]: literal in `deprecated` value must be a string + --> tests/ui/deprecation_info.rs:22:24 + | +22 | #[deprecated(since = 5)] + | ^ diff --git a/substrate/primitives/metadata-ir/src/types.rs b/substrate/primitives/metadata-ir/src/types.rs index b05f26ff55d4..4ebe8c25a675 100644 --- a/substrate/primitives/metadata-ir/src/types.rs +++ b/substrate/primitives/metadata-ir/src/types.rs @@ -15,11 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use codec::Encode; +use codec::{Compact, Encode}; use scale_info::{ form::{Form, MetaForm, PortableForm}, - prelude::vec::Vec, - IntoPortable, MetaType, Registry, + prelude::{collections::BTreeMap, vec::Vec}, + IntoPortable, Registry, }; /// The intermediate representation for the runtime metadata. @@ -52,6 +52,8 @@ pub struct RuntimeApiMetadataIR { pub methods: Vec>, /// Trait documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for RuntimeApiMetadataIR { @@ -62,6 +64,7 @@ impl IntoPortable for RuntimeApiMetadataIR { name: self.name.into_portable(registry), methods: registry.map_into_portable(self.methods), docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -77,6 +80,8 @@ pub struct RuntimeApiMethodMetadataIR { pub output: T::Type, /// Method documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for RuntimeApiMethodMetadataIR { @@ -88,6 +93,7 @@ impl IntoPortable for RuntimeApiMethodMetadataIR { inputs: registry.map_into_portable(self.inputs), output: registry.register_type(&self.output), docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -132,6 +138,8 @@ pub struct PalletMetadataIR { pub index: u8, /// Pallet documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for PalletMetadataIR { @@ -147,6 +155,7 @@ impl IntoPortable for PalletMetadataIR { error: self.error.map(|error| error.into_portable(registry)), index: self.index, docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -245,6 +254,8 @@ pub struct StorageEntryMetadataIR { pub default: Vec, /// Storage entry documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for StorageEntryMetadataIR { @@ -257,6 +268,7 @@ impl IntoPortable for StorageEntryMetadataIR { ty: self.ty.into_portable(registry), default: self.default, docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -331,19 +343,18 @@ impl IntoPortable for StorageEntryTypeIR { pub struct PalletCallMetadataIR { /// The corresponding enum type for the pallet call. pub ty: T::Type, + /// Deprecation status of the pallet call + pub deprecation_info: DeprecationInfoIR, } impl IntoPortable for PalletCallMetadataIR { type Output = PalletCallMetadataIR; fn into_portable(self, registry: &mut Registry) -> Self::Output { - PalletCallMetadataIR { ty: registry.register_type(&self.ty) } - } -} - -impl From for PalletCallMetadataIR { - fn from(ty: MetaType) -> Self { - Self { ty } + PalletCallMetadataIR { + ty: registry.register_type(&self.ty), + deprecation_info: self.deprecation_info.into_portable(registry), + } } } @@ -352,19 +363,18 @@ impl From for PalletCallMetadataIR { pub struct PalletEventMetadataIR { /// The Event type. pub ty: T::Type, + /// Deprecation info of the event + pub deprecation_info: DeprecationInfoIR, } impl IntoPortable for PalletEventMetadataIR { type Output = PalletEventMetadataIR; fn into_portable(self, registry: &mut Registry) -> Self::Output { - PalletEventMetadataIR { ty: registry.register_type(&self.ty) } - } -} - -impl From for PalletEventMetadataIR { - fn from(ty: MetaType) -> Self { - Self { ty } + PalletEventMetadataIR { + ty: registry.register_type(&self.ty), + deprecation_info: self.deprecation_info.into_portable(registry), + } } } @@ -379,6 +389,8 @@ pub struct PalletConstantMetadataIR { pub value: Vec, /// Documentation of the constant. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for PalletConstantMetadataIR { @@ -390,6 +402,7 @@ impl IntoPortable for PalletConstantMetadataIR { ty: registry.register_type(&self.ty), value: self.value, docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -399,19 +412,18 @@ impl IntoPortable for PalletConstantMetadataIR { pub struct PalletErrorMetadataIR { /// The error type information. pub ty: T::Type, + /// Deprecation info + pub deprecation_info: DeprecationInfoIR, } impl IntoPortable for PalletErrorMetadataIR { type Output = PalletErrorMetadataIR; fn into_portable(self, registry: &mut Registry) -> Self::Output { - PalletErrorMetadataIR { ty: registry.register_type(&self.ty) } - } -} - -impl From for PalletErrorMetadataIR { - fn from(ty: MetaType) -> Self { - Self { ty } + PalletErrorMetadataIR { + ty: registry.register_type(&self.ty), + deprecation_info: self.deprecation_info.into_portable(registry), + } } } @@ -451,3 +463,61 @@ impl IntoPortable for OuterEnumsIR { } } } + +/// Deprecation status for an entry inside MetadataIR +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub enum DeprecationStatusIR { + /// Entry is not deprecated + NotDeprecated, + /// Deprecated without a note. + DeprecatedWithoutNote, + /// Entry is deprecated with an note and an optional `since` field. + Deprecated { + /// Note explaining the deprecation + note: T::String, + /// Optional value for denoting version when the deprecation occured + since: Option, + }, +} +impl IntoPortable for DeprecationStatusIR { + type Output = DeprecationStatusIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + match self { + Self::Deprecated { note, since } => { + let note = note.into_portable(registry); + let since = since.map(|x| x.into_portable(registry)); + DeprecationStatusIR::Deprecated { note, since } + }, + Self::DeprecatedWithoutNote => DeprecationStatusIR::DeprecatedWithoutNote, + Self::NotDeprecated => DeprecationStatusIR::NotDeprecated, + } + } +} +/// Deprecation info for an enums/errors/calls. +/// Denotes full/partial deprecation of the type +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub enum DeprecationInfoIR { + /// Type is not deprecated + NotDeprecated, + /// Entry is fully deprecated. + ItemDeprecated(DeprecationStatusIR), + /// Entry is partially deprecated. + VariantsDeprecated(BTreeMap, DeprecationStatusIR>), +} +impl IntoPortable for DeprecationInfoIR { + type Output = DeprecationInfoIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + match self { + Self::VariantsDeprecated(entries) => { + let entries = + entries.into_iter().map(|(k, entry)| (k, entry.into_portable(registry))); + DeprecationInfoIR::VariantsDeprecated(entries.collect()) + }, + Self::ItemDeprecated(deprecation) => + DeprecationInfoIR::ItemDeprecated(deprecation.into_portable(registry)), + Self::NotDeprecated => DeprecationInfoIR::NotDeprecated, + } + } +} From a34cc8dff09dc3840a304befc2415343244b5fd0 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Thu, 12 Sep 2024 12:37:07 +0300 Subject: [PATCH 369/382] [4 / 5] Make approval-voting runnable on a worker thread (#4846) This is part of the work to further optimize the approval subsystems, if you want to understand the full context start with reading https://github.com/paritytech/polkadot-sdk/pull/4849#issue-2364261568, # Description This PR contain changes to make possible the run of single approval-voting instance on a worker thread, so that it can be instantiated by the approval-voting-parallel subsystem. This does not contain any functional changes it just decouples the subsystem from the subsystem Context and introduces more specific trait dependencies for each function instead of all of them requiring a context. This change can be merged independent of the followup PRs. --------- Signed-off-by: Alexandru Gheorghe Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> --- polkadot/cli/src/command.rs | 8 +- polkadot/node/core/approval-voting/Cargo.toml | 1 + .../node/core/approval-voting/src/import.rs | 114 ++--- polkadot/node/core/approval-voting/src/lib.rs | 392 ++++++++++++------ .../node/core/approval-voting/src/tests.rs | 25 +- polkadot/node/service/src/lib.rs | 13 +- polkadot/node/service/src/overseer.rs | 1 + .../subsystem-bench/src/lib/approval/mod.rs | 3 +- prdoc/pr_4846.prdoc | 13 + 9 files changed, 382 insertions(+), 188 deletions(-) create mode 100644 prdoc/pr_4846.prdoc diff --git a/polkadot/cli/src/command.rs b/polkadot/cli/src/command.rs index 168a645430ed..2947867c516e 100644 --- a/polkadot/cli/src/command.rs +++ b/polkadot/cli/src/command.rs @@ -373,16 +373,16 @@ pub fn run() -> Result<()> { Ok(runner.async_run(|mut config| { let (client, backend, _, task_manager) = polkadot_service::new_chain_ops(&mut config, None)?; + let task_handle = task_manager.spawn_handle(); let aux_revert = Box::new(|client, backend, blocks| { - polkadot_service::revert_backend(client, backend, blocks, config).map_err( - |err| { + polkadot_service::revert_backend(client, backend, blocks, config, task_handle) + .map_err(|err| { match err { polkadot_service::Error::Blockchain(err) => err.into(), // Generic application-specific error. err => sc_cli::Error::Application(err.into()), } - }, - ) + }) }); Ok(( cmd.run(client, backend, Some(aux_revert)).map_err(Error::SubstrateCli), diff --git a/polkadot/node/core/approval-voting/Cargo.toml b/polkadot/node/core/approval-voting/Cargo.toml index bc0187bf4922..e678118440f5 100644 --- a/polkadot/node/core/approval-voting/Cargo.toml +++ b/polkadot/node/core/approval-voting/Cargo.toml @@ -22,6 +22,7 @@ kvdb = { workspace = true } derive_more = { workspace = true, default-features = true } thiserror = { workspace = true } itertools = { workspace = true } +async-trait = { workspace = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } diff --git a/polkadot/node/core/approval-voting/src/import.rs b/polkadot/node/core/approval-voting/src/import.rs index b163d718eb25..bf6ea0c98149 100644 --- a/polkadot/node/core/approval-voting/src/import.rs +++ b/polkadot/node/core/approval-voting/src/import.rs @@ -44,6 +44,7 @@ use polkadot_node_subsystem::{ overseer, RuntimeApiError, SubsystemError, SubsystemResult, }; use polkadot_node_subsystem_util::{determine_new_blocks, runtime::RuntimeInfo}; +use polkadot_overseer::SubsystemSender; use polkadot_primitives::{ node_features, BlockNumber, CandidateEvent, CandidateHash, CandidateReceipt, ConsensusLog, CoreIndex, GroupIndex, Hash, Header, SessionIndex, @@ -111,8 +112,8 @@ enum ImportedBlockInfoError { /// Computes information about the imported block. Returns an error if the info couldn't be /// extracted. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn imported_block_info( - ctx: &mut Context, +async fn imported_block_info>( + sender: &mut Sender, env: ImportedBlockInfoEnv<'_>, block_hash: Hash, block_header: &Header, @@ -124,11 +125,12 @@ async fn imported_block_info( // fetch candidates let included_candidates: Vec<_> = { let (c_tx, c_rx) = oneshot::channel(); - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::CandidateEvents(c_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::CandidateEvents(c_tx), + )) + .await; let events: Vec = match c_rx.await { Ok(Ok(events)) => events, @@ -151,11 +153,12 @@ async fn imported_block_info( // short, that shouldn't happen. let session_index = { let (s_tx, s_rx) = oneshot::channel(); - ctx.send_message(RuntimeApiMessage::Request( - block_header.parent_hash, - RuntimeApiRequest::SessionIndexForChild(s_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_header.parent_hash, + RuntimeApiRequest::SessionIndexForChild(s_tx), + )) + .await; let session_index = match s_rx.await { Ok(Ok(s)) => s, @@ -201,11 +204,12 @@ async fn imported_block_info( // by one block. This gives us the opposite invariant for sessions - the parent block's // post-state gives us the canonical information about the session index for any of its // children, regardless of which slot number they might be produced at. - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::CurrentBabeEpoch(s_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::CurrentBabeEpoch(s_tx), + )) + .await; match s_rx.await { Ok(Ok(s)) => s, @@ -216,7 +220,7 @@ async fn imported_block_info( }; let extended_session_info = - get_extended_session_info(env.runtime_info, ctx.sender(), block_hash, session_index).await; + get_extended_session_info(env.runtime_info, sender, block_hash, session_index).await; let enable_v2_assignments = extended_session_info.map_or(false, |extended_session_info| { *extended_session_info .node_features @@ -225,7 +229,7 @@ async fn imported_block_info( .unwrap_or(&false) }); - let session_info = get_session_info(env.runtime_info, ctx.sender(), block_hash, session_index) + let session_info = get_session_info(env.runtime_info, sender, block_hash, session_index) .await .ok_or(ImportedBlockInfoError::SessionInfoUnavailable)?; @@ -329,9 +333,15 @@ pub struct BlockImportedCandidates { /// * and return information about all candidates imported under each block. /// /// It is the responsibility of the caller to schedule wakeups for each block. -#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -pub(crate) async fn handle_new_head( - ctx: &mut Context, +pub(crate) async fn handle_new_head< + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender, + AVSender: SubsystemSender, + B: Backend, +>( + sender: &mut Sender, + approval_voting_sender: &mut AVSender, state: &State, db: &mut OverlayedBackend<'_, B>, session_info_provider: &mut RuntimeInfo, @@ -349,7 +359,7 @@ pub(crate) async fn handle_new_head( let header = { let (h_tx, h_rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::BlockHeader(head, h_tx)).await; + sender.send_message(ChainApiMessage::BlockHeader(head, h_tx)).await; match h_rx.await? { Err(e) => { gum::debug!( @@ -375,7 +385,7 @@ pub(crate) async fn handle_new_head( let lower_bound_number = finalized_number.unwrap_or(lower_bound_number).max(lower_bound_number); let new_blocks = determine_new_blocks( - ctx.sender(), + sender, |h| db.load_block_entry(h).map(|e| e.is_some()), head, &header, @@ -401,12 +411,15 @@ pub(crate) async fn handle_new_head( keystore: &state.keystore, }; - match imported_block_info(ctx, env, block_hash, &block_header, finalized_number).await { + match imported_block_info(sender, env, block_hash, &block_header, finalized_number) + .await + { Ok(i) => imported_blocks_and_info.push((block_hash, block_header, i)), Err(error) => { // It's possible that we've lost a race with finality. let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::FinalizedBlockHash(block_header.number, tx)) + sender + .send_message(ChainApiMessage::FinalizedBlockHash(block_header.number, tx)) .await; let lost_to_finality = match rx.await { @@ -450,17 +463,11 @@ pub(crate) async fn handle_new_head( force_approve, } = imported_block_info; - let session_info = match get_session_info( - session_info_provider, - ctx.sender(), - head, - session_index, - ) - .await - { - Some(session_info) => session_info, - None => return Ok(Vec::new()), - }; + let session_info = + match get_session_info(session_info_provider, sender, head, session_index).await { + Some(session_info) => session_info, + None => return Ok(Vec::new()), + }; let (block_tick, no_show_duration) = { let block_tick = slot_number_to_tick(state.slot_duration_millis, slot); @@ -510,7 +517,7 @@ pub(crate) async fn handle_new_head( }; // If all bits are already set, then send an approve message. if approved_bitfield.count_ones() == approved_bitfield.len() { - ctx.send_message(ChainSelectionMessage::Approved(block_hash)).await; + sender.send_message(ChainSelectionMessage::Approved(block_hash)).await; } let block_entry = v3::BlockEntry { @@ -567,7 +574,7 @@ pub(crate) async fn handle_new_head( // Notify chain-selection of all approved hashes. for hash in approved_hashes { - ctx.send_message(ChainSelectionMessage::Approved(hash)).await; + sender.send_message(ChainSelectionMessage::Approved(hash)).await; } } @@ -603,7 +610,8 @@ pub(crate) async fn handle_new_head( "Informing distribution of newly imported chain", ); - ctx.send_unbounded_message(ApprovalDistributionMessage::NewBlocks(approval_meta)); + approval_voting_sender + .send_unbounded_message(ApprovalDistributionMessage::NewBlocks(approval_meta)); Ok(imported_candidates) } @@ -620,7 +628,10 @@ pub(crate) mod tests { approval::v1::{VrfSignature, VrfTranscript}, DISPUTE_WINDOW, }; - use polkadot_node_subsystem::messages::{AllMessages, ApprovalVotingMessage}; + use polkadot_node_subsystem::{ + messages::{AllMessages, ApprovalVotingMessage}, + SubsystemContext, + }; use polkadot_node_subsystem_test_helpers::make_subsystem_context; use polkadot_node_subsystem_util::database::Database; use polkadot_primitives::{ @@ -662,7 +673,7 @@ pub(crate) mod tests { State { keystore: Arc::new(LocalKeystore::in_memory()), slot_duration_millis: 6_000, - clock: Box::new(MockClock::default()), + clock: Arc::new(MockClock::default()), assignment_criteria: Box::new(MockAssignmentCriteria::default()), spans: HashMap::new(), per_block_assignments_gathering_times: LruMap::new(ByLength::new( @@ -806,8 +817,9 @@ pub(crate) mod tests { keystore: &LocalKeystore::in_memory(), }; - let info = - imported_block_info(&mut ctx, env, hash, &header, &Some(4)).await.unwrap(); + let info = imported_block_info(ctx.sender(), env, hash, &header, &Some(4)) + .await + .unwrap(); assert_eq!(info.included_candidates, included_candidates); assert_eq!(info.session_index, session); @@ -953,7 +965,7 @@ pub(crate) mod tests { keystore: &LocalKeystore::in_memory(), }; - let info = imported_block_info(&mut ctx, env, hash, &header, &Some(4)).await; + let info = imported_block_info(ctx.sender(), env, hash, &header, &Some(4)).await; assert_matches!(info, Err(ImportedBlockInfoError::VrfInfoUnavailable)); }) @@ -1092,7 +1104,7 @@ pub(crate) mod tests { keystore: &LocalKeystore::in_memory(), }; - let info = imported_block_info(&mut ctx, env, hash, &header, &Some(6)).await; + let info = imported_block_info(ctx.sender(), env, hash, &header, &Some(6)).await; assert_matches!(info, Err(ImportedBlockInfoError::BlockAlreadyFinalized)); }) @@ -1128,7 +1140,8 @@ pub(crate) mod tests { #[test] fn imported_block_info_extracts_force_approve() { let pool = TaskExecutor::new(); - let (mut ctx, mut handle) = make_subsystem_context(pool.clone()); + let (mut ctx, mut handle) = + make_subsystem_context::(pool.clone()); let session = 5; let session_info = dummy_session_info(session); @@ -1191,7 +1204,7 @@ pub(crate) mod tests { }; let info = - imported_block_info(&mut ctx, env, hash, &header, &Some(4)).await.unwrap(); + imported_block_info(ctx.sender(), env, hash, &header, &Some(4)).await.unwrap(); assert_eq!(info.included_candidates, included_candidates); assert_eq!(info.session_index, session); @@ -1384,8 +1397,11 @@ pub(crate) mod tests { let test_fut = { Box::pin(async move { let mut overlay_db = OverlayedBackend::new(&db); + + let mut approval_voting_sender = ctx.sender().clone(); let result = handle_new_head( - &mut ctx, + ctx.sender(), + &mut approval_voting_sender, &state, &mut overlay_db, &mut session_info_provider, diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs index 942922cba6df..2149ce81fa80 100644 --- a/polkadot/node/core/approval-voting/src/lib.rs +++ b/polkadot/node/core/approval-voting/src/lib.rs @@ -165,7 +165,8 @@ pub struct ApprovalVotingSubsystem { db: Arc, mode: Mode, metrics: Metrics, - clock: Box, + clock: Arc, + spawner: Arc, } #[derive(Clone)] @@ -484,6 +485,7 @@ impl ApprovalVotingSubsystem { keystore: Arc, sync_oracle: Box, metrics: Metrics, + spawner: Arc, ) -> Self { ApprovalVotingSubsystem::with_config_and_clock( config, @@ -491,7 +493,8 @@ impl ApprovalVotingSubsystem { keystore, sync_oracle, metrics, - Box::new(SystemClock {}), + Arc::new(SystemClock {}), + spawner, ) } @@ -502,7 +505,8 @@ impl ApprovalVotingSubsystem { keystore: Arc, sync_oracle: Box, metrics: Metrics, - clock: Box, + clock: Arc, + spawner: Arc, ) -> Self { ApprovalVotingSubsystem { keystore, @@ -512,6 +516,7 @@ impl ApprovalVotingSubsystem { mode: Mode::Syncing(sync_oracle), metrics, clock, + spawner, } } @@ -551,12 +556,21 @@ fn db_sanity_check(db: Arc, config: DatabaseConfig) -> SubsystemRe #[overseer::subsystem(ApprovalVoting, error = SubsystemError, prefix = self::overseer)] impl ApprovalVotingSubsystem { - fn start(self, ctx: Context) -> SpawnedSubsystem { + fn start(self, mut ctx: Context) -> SpawnedSubsystem { let backend = DbBackend::new(self.db.clone(), self.db_config); - let future = - run::(ctx, self, Box::new(RealAssignmentCriteria), backend) - .map_err(|e| SubsystemError::with_origin("approval-voting", e)) - .boxed(); + let to_other_subsystems = ctx.sender().clone(); + let to_approval_distr = ctx.sender().clone(); + + let future = run::( + ctx, + to_other_subsystems, + to_approval_distr, + self, + Box::new(RealAssignmentCriteria), + backend, + ) + .map_err(|e| SubsystemError::with_origin("approval-voting", e)) + .boxed(); SpawnedSubsystem { name: "approval-voting-subsystem", future } } @@ -825,7 +839,7 @@ where struct State { keystore: Arc, slot_duration_millis: u64, - clock: Box, + clock: Arc, assignment_criteria: Box, spans: HashMap, // Per block, candidate records about how long we take until we gather enough @@ -961,20 +975,20 @@ impl State { } // Returns the approval voting params from the RuntimeApi. - #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] - async fn get_approval_voting_params_or_default( + async fn get_approval_voting_params_or_default>( &self, - ctx: &mut Context, + sender: &mut Sender, session_index: SessionIndex, block_hash: Hash, ) -> Option { let (s_tx, s_rx) = oneshot::channel(); - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::ApprovalVotingParams(session_index, s_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::ApprovalVotingParams(session_index, s_tx), + )) + .await; match s_rx.await { Ok(Ok(params)) => { @@ -1143,9 +1157,36 @@ enum Action { Conclude, } +/// Trait for providing approval voting subsystem with work. +#[async_trait::async_trait] +pub trait ApprovalVotingWorkProvider { + async fn recv(&mut self) -> SubsystemResult>; +} + +#[async_trait::async_trait] +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +impl ApprovalVotingWorkProvider for Context { + async fn recv(&mut self) -> SubsystemResult> { + self.recv().await + } +} + #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn run( - mut ctx: Context, +async fn run< + B, + WorkProvider: ApprovalVotingWorkProvider, + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + Clone, + ADSender: SubsystemSender, +>( + mut work_provider: WorkProvider, + mut to_other_subsystems: Sender, + mut to_approval_distr: ADSender, mut subsystem: ApprovalVotingSubsystem, assignment_criteria: Box, mut backend: B, @@ -1169,19 +1210,11 @@ where no_show_stats: NoShowStats::default(), }; - // `None` on start-up. Gets initialized/updated on leaf update - let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { - keystore: None, - session_cache_lru_size: DISPUTE_WINDOW.get(), - }); - let mut wakeups = Wakeups::default(); - let mut currently_checking_set = CurrentlyCheckingSet::default(); - let mut delayed_approvals_timers = DelayedApprovalTimer::default(); - let mut approvals_cache = LruMap::new(ByLength::new(APPROVAL_CACHE_SIZE)); - let mut last_finalized_height: Option = { let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::FinalizedBlockNumber(tx)).await; + to_other_subsystems + .send_message(ChainApiMessage::FinalizedBlockNumber(tx)) + .await; match rx.await? { Ok(number) => Some(number), Err(err) => { @@ -1191,13 +1224,24 @@ where } }; + // `None` on start-up. Gets initialized/updated on leaf update + let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { + keystore: None, + session_cache_lru_size: DISPUTE_WINDOW.get(), + }); + + let mut wakeups = Wakeups::default(); + let mut currently_checking_set = CurrentlyCheckingSet::default(); + let mut delayed_approvals_timers = DelayedApprovalTimer::default(); + let mut approvals_cache = LruMap::new(ByLength::new(APPROVAL_CACHE_SIZE)); + loop { let mut overlayed_db = OverlayedBackend::new(&backend); let actions = futures::select! { (_tick, woken_block, woken_candidate) = wakeups.next(&*state.clock).fuse() => { subsystem.metrics.on_wakeup(); process_wakeup( - &mut ctx, + &mut to_other_subsystems, &mut state, &mut overlayed_db, &mut session_info_provider, @@ -1207,9 +1251,11 @@ where &wakeups, ).await? } - next_msg = ctx.recv().fuse() => { + next_msg = work_provider.recv().fuse() => { let mut actions = handle_from_overseer( - &mut ctx, + &mut to_other_subsystems, + &mut to_approval_distr, + &subsystem.spawner, &mut state, &mut overlayed_db, &mut session_info_provider, @@ -1269,7 +1315,8 @@ where &mut overlayed_db, &mut session_info_provider, &state, - &mut ctx, + &mut to_other_subsystems, + &mut to_approval_distr, block_hash, validator_index, &subsystem.metrics, @@ -1291,7 +1338,9 @@ where }; if handle_actions( - &mut ctx, + &mut to_other_subsystems, + &mut to_approval_distr, + &subsystem.spawner, &mut state, &mut overlayed_db, &mut session_info_provider, @@ -1318,6 +1367,63 @@ where Ok(()) } +// Starts a worker thread that runs the approval voting subsystem. +pub async fn start_approval_worker< + WorkProvider: ApprovalVotingWorkProvider + Send + 'static, + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + Clone, + ADSender: SubsystemSender, +>( + work_provider: WorkProvider, + to_other_subsystems: Sender, + to_approval_distr: ADSender, + config: Config, + db: Arc, + keystore: Arc, + sync_oracle: Box, + metrics: Metrics, + spawner: Arc, + task_name: &'static str, + group_name: &'static str, + clock: Arc, +) -> SubsystemResult<()> { + let approval_voting = ApprovalVotingSubsystem::with_config_and_clock( + config, + db.clone(), + keystore, + sync_oracle, + metrics, + clock, + spawner, + ); + let backend = DbBackend::new(db.clone(), approval_voting.db_config); + let spawner = approval_voting.spawner.clone(); + spawner.spawn_blocking( + task_name, + Some(group_name), + Box::pin(async move { + if let Err(err) = run( + work_provider, + to_other_subsystems, + to_approval_distr, + approval_voting, + Box::new(RealAssignmentCriteria), + backend, + ) + .await + { + gum::error!(target: LOG_TARGET, ?err, "Approval voting worker stopped processing messages"); + }; + }), + ); + Ok(()) +} + // Handle actions is a function that accepts a set of instructions // and subsequently updates the underlying approvals_db in accordance // with the linear set of instructions passed in. Therefore, actions @@ -1338,8 +1444,19 @@ where // // returns `true` if any of the actions was a `Conclude` command. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn handle_actions( - ctx: &mut Context, +async fn handle_actions< + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + Clone, + ADSender: SubsystemSender, +>( + sender: &mut Sender, + approval_voting_sender: &mut ADSender, + spawn_handle: &Arc, state: &mut State, overlayed_db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, @@ -1371,7 +1488,8 @@ async fn handle_actions( // Note that chaining these iterators is O(n) as we must consume // the prior iterator. let next_actions: Vec = issue_approval( - ctx, + sender, + approval_voting_sender, state, overlayed_db, session_info_provider, @@ -1422,10 +1540,12 @@ async fn handle_actions( let validator_index = indirect_cert.validator; if distribute_assignment { - ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeAssignment( - indirect_cert, - claimed_candidate_indices, - )); + approval_voting_sender.send_unbounded_message( + ApprovalDistributionMessage::DistributeAssignment( + indirect_cert, + claimed_candidate_indices, + ), + ); } match approvals_cache.get(&candidate_hash) { @@ -1440,7 +1560,8 @@ async fn handle_actions( actions_iter = new_actions.into_iter(); }, None => { - let ctx = &mut *ctx; + let sender = sender.clone(); + let spawn_handle = spawn_handle.clone(); currently_checking_set .insert_relay_block_hash( @@ -1449,7 +1570,8 @@ async fn handle_actions( relay_block_hash, async move { launch_approval( - ctx, + sender, + spawn_handle, metrics.clone(), session, candidate, @@ -1478,13 +1600,13 @@ async fn handle_actions( }) .with_string_tag("block-hash", format!("{:?}", block_hash)) .with_stage(jaeger::Stage::ApprovalChecking); - ctx.send_message(ChainSelectionMessage::Approved(block_hash)).await; + sender.send_message(ChainSelectionMessage::Approved(block_hash)).await; }, Action::BecomeActive => { *mode = Mode::Active; let (messages, next_actions) = distribution_messages_for_activation( - ctx, + sender, overlayed_db, state, delayed_approvals_timers, @@ -1492,7 +1614,7 @@ async fn handle_actions( ) .await?; - ctx.send_messages(messages.into_iter()).await; + approval_voting_sender.send_messages(messages.into_iter()).await; let next_actions: Vec = next_actions.into_iter().map(|v| v.clone()).chain(actions_iter).collect(); @@ -1566,8 +1688,8 @@ fn get_assignment_core_indices( } #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn distribution_messages_for_activation( - ctx: &mut Context, +async fn distribution_messages_for_activation>( + sender: &mut Sender, db: &OverlayedBackend<'_, impl Backend>, state: &State, delayed_approvals_timers: &mut DelayedApprovalTimer, @@ -1693,7 +1815,7 @@ async fn distribution_messages_for_activation( let ExtendedSessionInfo { ref executor_params, .. } = match get_extended_session_info( session_info_provider, - ctx.sender(), + sender, block_entry.block_hash(), block_entry.session(), ) @@ -1791,9 +1913,16 @@ async fn distribution_messages_for_activation( } // Handle an incoming signal from the overseer. Returns true if execution should conclude. -#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn handle_from_overseer( - ctx: &mut Context, +async fn handle_from_overseer< + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + Clone, + ADSender: SubsystemSender, +>( + sender: &mut Sender, + approval_voting_sender: &mut ADSender, + spawn_handle: &Arc, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, @@ -1811,7 +1940,8 @@ async fn handle_from_overseer( jaeger::PerLeafSpan::new(activated.span, "approval-voting"); state.spans.insert(head, approval_voting_span); match import::handle_new_head( - ctx, + sender, + approval_voting_sender, state, db, session_info_provider, @@ -1894,14 +2024,9 @@ async fn handle_from_overseer( }, FromOrchestra::Communication { msg } => match msg { ApprovalVotingMessage::ImportAssignment(checked_assignment, tx) => { - let (check_outcome, actions) = import_assignment( - ctx.sender(), - state, - db, - session_info_provider, - checked_assignment, - ) - .await?; + let (check_outcome, actions) = + import_assignment(sender, state, db, session_info_provider, checked_assignment) + .await?; // approval-distribution makes sure this assignment is valid and expected, // so this import should never fail, if it does it might mean one of two things, // there is a bug in the code or the two subsystems got out of sync. @@ -1912,16 +2037,9 @@ async fn handle_from_overseer( actions }, ApprovalVotingMessage::ImportApproval(a, tx) => { - let result = import_approval( - ctx.sender(), - state, - db, - session_info_provider, - metrics, - a, - &wakeups, - ) - .await?; + let result = + import_approval(sender, state, db, session_info_provider, metrics, a, &wakeups) + .await?; // approval-distribution makes sure this vote is valid and expected, // so this import should never fail, if it does it might mean one of two things, // there is a bug in the code or the two subsystems got out of sync. @@ -1941,7 +2059,7 @@ async fn handle_from_overseer( .with_stage(jaeger::Stage::ApprovalChecking) .with_string_tag("leaf", format!("{:?}", target)); match handle_approved_ancestor( - ctx, + sender, db, target, lower_bound, @@ -1964,7 +2082,14 @@ async fn handle_from_overseer( }, ApprovalVotingMessage::GetApprovalSignaturesForCandidate(candidate_hash, tx) => { metrics.on_candidate_signatures_request(); - get_approval_signatures_for_candidate(ctx, db, candidate_hash, tx).await?; + get_approval_signatures_for_candidate( + approval_voting_sender.clone(), + spawn_handle, + db, + candidate_hash, + tx, + ) + .await?; Vec::new() }, }, @@ -1978,8 +2103,11 @@ async fn handle_from_overseer( /// This involves an unbounded message send to approval-distribution, the caller has to ensure that /// calls to this function are infrequent and bounded. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn get_approval_signatures_for_candidate( - ctx: &mut Context, +async fn get_approval_signatures_for_candidate< + Sender: SubsystemSender, +>( + mut sender: Sender, + spawn_handle: &Arc, db: &OverlayedBackend<'_, impl Backend>, candidate_hash: CandidateHash, tx: oneshot::Sender, ValidatorSignature)>>, @@ -2038,7 +2166,6 @@ async fn get_approval_signatures_for_candidate( } } - let mut sender = ctx.sender().clone(); let get_approvals = async move { let (tx_distribution, rx_distribution) = oneshot::channel(); sender.send_unbounded_message(ApprovalDistributionMessage::GetApprovalSignatures( @@ -2118,12 +2245,17 @@ async fn get_approval_signatures_for_candidate( ?candidate_hash, "Spawning task for fetching signatures from approval-distribution" ); - ctx.spawn("get-approval-signatures", Box::pin(get_approvals)) + spawn_handle.spawn( + "get-approval-signatures", + Some("approval-voting-subsystem"), + Box::pin(get_approvals), + ); + Ok(()) } #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn handle_approved_ancestor( - ctx: &mut Context, +async fn handle_approved_ancestor>( + sender: &mut Sender, db: &OverlayedBackend<'_, impl Backend>, target: Hash, lower_bound: BlockNumber, @@ -2143,7 +2275,7 @@ async fn handle_approved_ancestor( let target_number = { let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::BlockNumber(target, tx)).await; + sender.send_message(ChainApiMessage::BlockNumber(target, tx)).await; match rx.await { Ok(Ok(Some(n))) => n, @@ -2164,12 +2296,13 @@ async fn handle_approved_ancestor( let ancestry = if target_number > lower_bound + 1 { let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::Ancestors { - hash: target, - k: (target_number - (lower_bound + 1)) as usize, - response_channel: tx, - }) - .await; + sender + .send_message(ChainApiMessage::Ancestors { + hash: target, + k: (target_number - (lower_bound + 1)) as usize, + response_channel: tx, + }) + .await; match rx.await { Ok(Ok(a)) => a, @@ -3107,9 +3240,8 @@ fn should_trigger_assignment( } } -#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn process_wakeup( - ctx: &mut Context, +async fn process_wakeup>( + sender: &mut Sender, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, @@ -3140,7 +3272,7 @@ async fn process_wakeup( let ExtendedSessionInfo { ref session_info, ref executor_params, .. } = match get_extended_session_info( session_info_provider, - ctx.sender(), + sender, block_entry.block_hash(), block_entry.session(), ) @@ -3282,7 +3414,7 @@ async fn process_wakeup( // Note that this function also schedules a wakeup as necessary. actions.extend( advance_approval_state( - ctx.sender(), + sender, state, db, session_info_provider, @@ -3303,8 +3435,14 @@ async fn process_wakeup( // spawned. When the background work is no longer needed, the `AbortHandle` should be dropped // to cancel the background work and any requests it has spawned. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn launch_approval( - ctx: &mut Context, +async fn launch_approval< + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender, +>( + mut sender: Sender, + spawn_handle: Arc, metrics: Metrics, session_index: SessionIndex, candidate: CandidateReceipt, @@ -3355,14 +3493,15 @@ async fn launch_approval( .with_stage(jaeger::Stage::ApprovalChecking); let timer = metrics.time_recover_and_approve(); - ctx.send_message(AvailabilityRecoveryMessage::RecoverAvailableData( - candidate.clone(), - session_index, - Some(backing_group), - core_index, - a_tx, - )) - .await; + sender + .send_message(AvailabilityRecoveryMessage::RecoverAvailableData( + candidate.clone(), + session_index, + Some(backing_group), + core_index, + a_tx, + )) + .await; let request_validation_result_span = span .child("request-validation-result") @@ -3371,15 +3510,18 @@ async fn launch_approval( .with_string_tag("block-hash", format!("{:?}", block_hash)) .with_stage(jaeger::Stage::ApprovalChecking); - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::ValidationCodeByHash(candidate.descriptor.validation_code_hash, code_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::ValidationCodeByHash( + candidate.descriptor.validation_code_hash, + code_tx, + ), + )) + .await; let candidate = candidate.clone(); let metrics_guard = StaleGuard(Some(metrics)); - let mut sender = ctx.sender().clone(); let background = async move { // Force the move of the timer into the background task. let _timer = timer; @@ -3509,14 +3651,19 @@ async fn launch_approval( } }; let (background, remote_handle) = background.remote_handle(); - ctx.spawn("approval-checks", Box::pin(background)).map(move |()| remote_handle) + spawn_handle.spawn("approval-checks", Some("approval-voting-subsystem"), Box::pin(background)); + Ok(remote_handle) } // Issue and import a local approval vote. Should only be invoked after approval checks // have been done. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn issue_approval( - ctx: &mut Context, +async fn issue_approval< + Sender: SubsystemSender, + ADSender: SubsystemSender, +>( + sender: &mut Sender, + approval_voting_sender: &mut ADSender, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, @@ -3595,7 +3742,7 @@ async fn issue_approval( let session_info = match get_session_info( session_info_provider, - ctx.sender(), + sender, block_entry.parent_hash(), block_entry.session(), ) @@ -3637,7 +3784,7 @@ async fn issue_approval( ); let actions = advance_approval_state( - ctx.sender(), + sender, state, db, session_info_provider, @@ -3654,7 +3801,8 @@ async fn issue_approval( db, session_info_provider, state, - ctx, + sender, + approval_voting_sender, block_hash, validator_index, metrics, @@ -3673,11 +3821,15 @@ async fn issue_approval( // Create signature for the approved candidates pending signatures #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn maybe_create_signature( +async fn maybe_create_signature< + Sender: SubsystemSender, + ADSender: SubsystemSender, +>( db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, state: &State, - ctx: &mut Context, + sender: &mut Sender, + approval_voting_sender: &mut ADSender, block_hash: Hash, validator_index: ValidatorIndex, metrics: &Metrics, @@ -3696,7 +3848,7 @@ async fn maybe_create_signature( }; let approval_params = state - .get_approval_voting_params_or_default(ctx, block_entry.session(), block_hash) + .get_approval_voting_params_or_default(sender, block_entry.session(), block_hash) .await .unwrap_or_default(); @@ -3716,7 +3868,7 @@ async fn maybe_create_signature( let session_info = match get_session_info( session_info_provider, - ctx.sender(), + sender, block_entry.parent_hash(), block_entry.session(), ) @@ -3797,7 +3949,7 @@ async fn maybe_create_signature( metrics.on_approval_produced(); - ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeApproval( + approval_voting_sender.send_unbounded_message(ApprovalDistributionMessage::DistributeApproval( IndirectSignedApprovalVoteV2 { block_hash: block_entry.block_hash(), candidate_indices: candidates_indices, @@ -3838,7 +3990,7 @@ fn issue_local_invalid_statement( candidate_hash: CandidateHash, candidate: CandidateReceipt, ) where - Sender: overseer::ApprovalVotingSenderTrait, + Sender: SubsystemSender, { // We need to send an unbounded message here to break a cycle: // DisputeCoordinatorMessage::IssueLocalStatement -> diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs index 7126f209a94f..65aa4f894c23 100644 --- a/polkadot/node/core/approval-voting/src/tests.rs +++ b/polkadot/node/core/approval-voting/src/tests.rs @@ -35,11 +35,11 @@ use polkadot_node_subsystem::{ messages::{ AllMessages, ApprovalVotingMessage, AssignmentCheckResult, AvailabilityRecoveryMessage, }, - ActiveLeavesUpdate, + ActiveLeavesUpdate, SubsystemContext, }; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::TimeoutExt; -use polkadot_overseer::HeadSupportsParachains; +use polkadot_overseer::{HeadSupportsParachains, SpawnGlue}; use polkadot_primitives::{ ApprovalVote, CandidateCommitments, CandidateEvent, CoreIndex, DisputeStatement, GroupIndex, Header, Id as ParaId, IndexedVec, NodeFeatures, ValidDisputeStatementKind, ValidationCode, @@ -536,7 +536,7 @@ impl Default for HarnessConfig { struct TestHarness { virtual_overseer: VirtualOverseer, - clock: Box, + clock: Arc, sync_oracle_handle: TestSyncOracleHandle, } @@ -550,8 +550,8 @@ fn test_harness>( config; let pool = sp_core::testing::TaskExecutor::new(); - let (context, virtual_overseer) = - polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + let (mut context, virtual_overseer) = + polkadot_node_subsystem_test_helpers::make_subsystem_context(pool.clone()); let keystore = LocalKeystore::in_memory(); let _ = keystore.sr25519_generate_new( @@ -559,12 +559,14 @@ fn test_harness>( Some(&Sr25519Keyring::Alice.to_seed()), ); - let clock = Box::new(clock); + let clock = Arc::new(clock); let db = kvdb_memorydb::create(test_constants::NUM_COLUMNS); let db = polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter::new(db, &[]); - + let sender = context.sender().clone(); let subsystem = run( context, + sender.clone(), + sender.clone(), ApprovalVotingSubsystem::with_config_and_clock( Config { col_approval_data: test_constants::TEST_CONFIG.col_approval_data, @@ -575,6 +577,7 @@ fn test_harness>( sync_oracle, Metrics::default(), clock.clone(), + Arc::new(SpawnGlue(pool)), ), assignment_criteria, backend, @@ -4114,7 +4117,7 @@ async fn handle_approval_on_max_coalesce_count( async fn handle_approval_on_max_wait_time( virtual_overseer: &mut VirtualOverseer, candidate_indices: Vec, - clock: Box, + clock: Arc, ) { const TICK_NOW_BEGIN: u64 = 1; const MAX_COALESCE_COUNT: u32 = 3; @@ -4412,7 +4415,7 @@ async fn build_chain_with_two_blocks_with_one_candidate_each( async fn setup_overseer_with_two_blocks_each_with_one_assignment_triggered( virtual_overseer: &mut VirtualOverseer, store: TestStore, - clock: &Box, + clock: &Arc, sync_oracle_handle: TestSyncOracleHandle, ) { assert_matches!( @@ -4926,7 +4929,7 @@ fn test_gathering_assignments_statements() { let mut state = State { keystore: Arc::new(LocalKeystore::in_memory()), slot_duration_millis: 6_000, - clock: Box::new(MockClock::default()), + clock: Arc::new(MockClock::default()), assignment_criteria: Box::new(MockAssignmentCriteria::check_only(|_| Ok(0))), spans: HashMap::new(), per_block_assignments_gathering_times: LruMap::new(ByLength::new( @@ -5021,7 +5024,7 @@ fn test_observe_assignment_gathering_status() { let mut state = State { keystore: Arc::new(LocalKeystore::in_memory()), slot_duration_millis: 6_000, - clock: Box::new(MockClock::default()), + clock: Arc::new(MockClock::default()), assignment_criteria: Box::new(MockAssignmentCriteria::check_only(|_| Ok(0))), spans: HashMap::new(), per_block_assignments_gathering_times: LruMap::new(ByLength::new( diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index a8e7fc16eb4d..fe96d29c1ceb 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -63,6 +63,7 @@ use { }; use polkadot_node_subsystem_util::database::Database; +use polkadot_overseer::SpawnGlue; #[cfg(feature = "full-node")] pub use { @@ -83,7 +84,7 @@ use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Duration}; use prometheus_endpoint::Registry; #[cfg(feature = "full-node")] use sc_service::KeystoreContainer; -use sc_service::RpcHandlers; +use sc_service::{RpcHandlers, SpawnTaskHandle}; use sc_telemetry::TelemetryWorker; #[cfg(feature = "full-node")] use sc_telemetry::{Telemetry, TelemetryWorkerHandle}; @@ -1500,6 +1501,7 @@ pub fn revert_backend( backend: Arc, blocks: BlockNumber, config: Configuration, + task_handle: SpawnTaskHandle, ) -> Result<(), Error> { let best_number = client.info().best_number; let finalized = client.info().finalized_number; @@ -1520,7 +1522,7 @@ pub fn revert_backend( let parachains_db = open_database(&config.database) .map_err(|err| sp_blockchain::Error::Backend(err.to_string()))?; - revert_approval_voting(parachains_db.clone(), hash)?; + revert_approval_voting(parachains_db.clone(), hash, task_handle)?; revert_chain_selection(parachains_db, hash)?; // Revert Substrate consensus related components sc_consensus_babe::revert(client.clone(), backend, blocks)?; @@ -1543,7 +1545,11 @@ fn revert_chain_selection(db: Arc, hash: Hash) -> sp_blockchain::R .map_err(|err| sp_blockchain::Error::Backend(err.to_string())) } -fn revert_approval_voting(db: Arc, hash: Hash) -> sp_blockchain::Result<()> { +fn revert_approval_voting( + db: Arc, + hash: Hash, + task_handle: SpawnTaskHandle, +) -> sp_blockchain::Result<()> { let config = approval_voting_subsystem::Config { col_approval_data: parachains_db::REAL_COLUMNS.col_approval_data, slot_duration_millis: Default::default(), @@ -1555,6 +1561,7 @@ fn revert_approval_voting(db: Arc, hash: Hash) -> sp_blockchain::R Arc::new(sc_keystore::LocalKeystore::in_memory()), Box::new(sp_consensus::NoNetwork), approval_voting_subsystem::Metrics::default(), + Arc::new(SpawnGlue(task_handle)), ); approval_voting diff --git a/polkadot/node/service/src/overseer.rs b/polkadot/node/service/src/overseer.rs index 0b57ff6e395f..3c071e34fe11 100644 --- a/polkadot/node/service/src/overseer.rs +++ b/polkadot/node/service/src/overseer.rs @@ -320,6 +320,7 @@ where keystore.clone(), Box::new(sync_service.clone()), Metrics::register(registry)?, + Arc::new(spawner.clone()), )) .gossip_support(GossipSupportSubsystem::new( keystore.clone(), diff --git a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs index f05d061f3fde..9d85039b8880 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs @@ -814,7 +814,8 @@ fn build_overseer( Arc::new(keystore), Box::new(TestSyncOracle {}), state.approval_voting_metrics.clone(), - Box::new(system_clock.clone()), + Arc::new(system_clock.clone()), + Arc::new(SpawnGlue(spawn_task_handle.clone())), ); let approval_distribution = ApprovalDistribution::new_with_clock( diff --git a/prdoc/pr_4846.prdoc b/prdoc/pr_4846.prdoc new file mode 100644 index 000000000000..eb18301b1010 --- /dev/null +++ b/prdoc/pr_4846.prdoc @@ -0,0 +1,13 @@ +title: "Make approval-voting runnable on a worker thread" + +doc: + - audience: Node Dev + description: | + Make approval-voting subsystem runnable on a separate worker thread without having to + to always pass to it an orchestra context. It achieves that by refactoring existing functions + to require only the minimal set of traits needed in the function instead of the general + `Context` + +crates: + - name: polkadot-node-core-approval-voting + bump: major From d50996948903fc7e5469213dd7ef718cef747831 Mon Sep 17 00:00:00 2001 From: Vincent Geddes <117534+vgeddes@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:24:19 +0200 Subject: [PATCH 370/382] Improve tests for snowbridge-pallet-ethereum-system --- .../snowbridge/pallets/system/src/tests.rs | 108 ++++++------------ bridges/snowbridge/primitives/core/src/lib.rs | 18 ++- 2 files changed, 53 insertions(+), 73 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index b3699ec2f24d..98c937d86328 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -638,112 +638,84 @@ fn register_token_with_signed_yields_bad_origin() { let origin = RuntimeOrigin::signed([14; 32].into()); let location = Location::new(1, [Parachain(2000)]); let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - assert_noop!( - EthereumSystem::register_token(origin, versioned_location, asset_metadata), + EthereumSystem::register_token(origin, versioned_location, Default::default()), BadOrigin ); }); } -pub struct TokenInfo { - pub location: Location, - pub metadata: AssetMetadata, - pub foreign_token_id: TokenId, +pub struct RegisterTokenTestCase { + /// Input: Location of Polkadot-native token relative to BH + pub native: Location, + /// Output: Reanchored, canonicalized location + pub reanchored: Location, + /// Output: Stable hash of reanchored location + pub foreign: TokenId, } #[test] fn register_all_tokens_succeeds() { - let assets = vec![ + let test_cases = vec![ // DOT - TokenInfo { - location: Location::parent(), - metadata: AssetMetadata { - decimals: 10, - name: b"DOT".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }, - foreign_token_id: hex!( + RegisterTokenTestCase { + native: Location::parent(), + reanchored: Location::new(1, GlobalConsensus(Polkadot)), + foreign: hex!( "4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2" ) .into(), }, // GLMR (Some Polkadot parachain currency) - TokenInfo { - location: Location::new(1, [Parachain(2004)]), - metadata: AssetMetadata { - decimals: 12, - name: b"GLMR".to_vec().try_into().unwrap(), - symbol: b"GLMR".to_vec().try_into().unwrap(), - }, - foreign_token_id: hex!( + RegisterTokenTestCase { + native: Location::new(1, [Parachain(2004)]), + reanchored: Location::new(1, [GlobalConsensus(Polkadot), Parachain(2004)]), + foreign: hex!( "34c08fc90409b6924f0e8eabb7c2aaa0c749e23e31adad9f6d217b577737fafb" ) .into(), }, // USDT - TokenInfo { - location: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), - metadata: AssetMetadata { - decimals: 6, - name: b"USDT".to_vec().try_into().unwrap(), - symbol: b"USDT".to_vec().try_into().unwrap(), - }, - foreign_token_id: hex!( + RegisterTokenTestCase { + native: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), + reanchored: Location::new(1, [GlobalConsensus(Polkadot), Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), + foreign: hex!( "14b0579be12d7d7f9971f1d4b41f0e88384b9b74799b0150d4aa6cd01afb4444" ) .into(), }, // KSM - TokenInfo { - location: Location::new(2, [GlobalConsensus(Kusama)]), - metadata: AssetMetadata { - decimals: 12, - name: b"KSM".to_vec().try_into().unwrap(), - symbol: b"KSM".to_vec().try_into().unwrap(), - }, - foreign_token_id: hex!( + RegisterTokenTestCase { + native: Location::new(2, [GlobalConsensus(Kusama)]), + reanchored: Location::new(1, [GlobalConsensus(Kusama)]), + foreign: hex!( "03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852" ) .into(), }, // KAR (Some Kusama parachain currency) - TokenInfo { - location: Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), - metadata: AssetMetadata { - decimals: 12, - name: b"KAR".to_vec().try_into().unwrap(), - symbol: b"KAR".to_vec().try_into().unwrap(), - }, - foreign_token_id: hex!( + RegisterTokenTestCase { + native: Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), + reanchored: Location::new(1, [GlobalConsensus(Kusama), Parachain(2000)]), + foreign: hex!( "d3e39ad6ea4cee68c9741181e94098823b2ea34a467577d0875c036f0fce5be0" ) .into(), }, ]; - for asset in assets.iter() { + for tc in test_cases.iter() { new_test_ext(true).execute_with(|| { let origin = RuntimeOrigin::root(); - let versioned_location: Box = - Box::new(asset.location.clone().into()); - let asset_metadata = asset.metadata.clone(); + let versioned_location: VersionedLocation = tc.native.clone().into(); - assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + assert_ok!(EthereumSystem::register_token(origin, Box::new(versioned_location), Default::default())); - let location = asset - .location - .clone() - .reanchored(&EthereumDestination::get(), &UniversalLocation::get()) - .unwrap(); + assert_eq!(NativeToForeignId::::get(tc.reanchored.clone()), Some(tc.foreign.clone())); + assert_eq!(ForeignToNativeId::::get(tc.foreign.clone()), Some(tc.reanchored.clone())); System::assert_last_event(RuntimeEvent::EthereumSystem(Event::::RegisterToken { - location: location.into(), - foreign_token_id: asset.foreign_token_id, + location: tc.reanchored.clone().into(), + foreign_token_id: tc.foreign, })); }); } @@ -764,14 +736,8 @@ fn register_ethereum_native_token_fails() { ], ); let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 18, - name: b"WETH".to_vec().try_into().unwrap(), - symbol: b"WETH".to_vec().try_into().unwrap(), - }; - assert_noop!( - EthereumSystem::register_token(origin, versioned_location, asset_metadata), + EthereumSystem::register_token(origin, versioned_location, Default::default()), Error::::LocationConversionFailed ); }); diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index 01ad86a26018..7ad129a52542 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -154,7 +154,21 @@ pub const SECONDARY_GOVERNANCE_CHANNEL: ChannelId = /// Metadata to include in the instantiated ERC20 token contract #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct AssetMetadata { - pub name: BoundedVec>, - pub symbol: BoundedVec>, + pub name: BoundedVec>, + pub symbol: BoundedVec>, pub decimals: u8, } + +#[cfg(any(test, feature = "std", feature = "runtime-benchmarks"))] +impl Default for AssetMetadata { + fn default() -> Self { + AssetMetadata { + name: BoundedVec::truncate_from(vec![]), + symbol: BoundedVec::truncate_from(vec![]), + decimals: 0, + } + } +} + +/// Maximum length of a string field in ERC20 token metada +const METADATA_FIELD_MAX_LEN: u32 = 32; From 5f72a1fe9d2c1e72325eea2e88f84c2dccbe9c5f Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:31:25 +0100 Subject: [PATCH 371/382] Fix subsystem bench publishing (#5667) Fixes: https://github.com/paritytech/ci_cd/issues/1034 --- .github/workflows/subsystem-benchmarks.yml | 110 +++++++++++++-------- 1 file changed, 71 insertions(+), 39 deletions(-) diff --git a/.github/workflows/subsystem-benchmarks.yml b/.github/workflows/subsystem-benchmarks.yml index c33c782a731c..6f9db9da9e69 100644 --- a/.github/workflows/subsystem-benchmarks.yml +++ b/.github/workflows/subsystem-benchmarks.yml @@ -1,9 +1,11 @@ +name: Subsystem Benchmarks + on: push: branches: - master pull_request: - types: [ opened, synchronize, reopened, ready_for_review ] + types: [opened, synchronize, reopened, ready_for_review] merge_group: concurrency: @@ -21,27 +23,49 @@ jobs: runs-on: ubuntu-latest outputs: IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} steps: - name: Checkout uses: actions/checkout@v4 - id: set_image run: cat .github/env >> $GITHUB_OUTPUT + - id: set_runner + run: | + # Run merge queues on persistent runners + if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT + else + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + fi build: timeout-minutes: 80 - needs: [ set-image ] - runs-on: arc-runners-polkadot-sdk-benchmark + needs: [set-image] + runs-on: ${{ needs.set-image.outputs.RUNNER }} container: image: ${{ needs.set-image.outputs.IMAGE }} strategy: fail-fast: false matrix: - features: [ - { name: "polkadot-availability-recovery", bench: "availability-recovery-regression-bench" }, - { name: "polkadot-availability-distribution", bench: "availability-distribution-regression-bench" }, - { name: "polkadot-node-core-approval-voting", bench: "approval-voting-regression-bench" }, - { name: "polkadot-statement-distribution", bench: "statement-distribution-regression-bench" } - ] + features: + [ + { + name: "polkadot-availability-recovery", + bench: "availability-recovery-regression-bench", + }, + { + name: "polkadot-availability-distribution", + bench: "availability-distribution-regression-bench", + }, + { + name: "polkadot-node-core-approval-voting", + bench: "approval-voting-regression-bench", + }, + { + name: "polkadot-statement-distribution", + bench: "statement-distribution-regression-bench", + }, + ] steps: - name: Checkout uses: actions/checkout@v4 @@ -56,34 +80,38 @@ jobs: run: | forklift cargo bench -p ${{ matrix.features.name }} --bench ${{ matrix.features.bench }} --features subsystem-benchmarks || echo "Benchmarks failed" ls -lsa ./charts - mkdir ./artifacts - cp ./charts/${{ matrix.features.bench }}.json ./artifacts/${{ matrix.features.bench }}.json + - name: Upload artifacts uses: actions/upload-artifact@v4.3.6 with: name: ${{matrix.features.bench}} - path: ./artifacts + path: ./charts publish-benchmarks: timeout-minutes: 60 - needs: [ build ] + needs: [build] if: github.ref == 'refs/heads/master' environment: subsystem-benchmarks runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 + with: + ref: gh-pages + fetch-depth: 0 + + - run: git checkout master -- - name: Download artifacts uses: actions/download-artifact@v4.1.8 with: - path: ./artifacts + path: ./charts - name: Setup git run: | # Fixes "detected dubious ownership" error in the ci git config --global --add safe.directory '*' - ls -lsR ./artifacts + ls -lsR ./charts - uses: actions/create-github-app-token@v1 id: app-token @@ -91,46 +119,50 @@ jobs: app-id: ${{ secrets.POLKADOTSDK_GHPAGES_APP_ID }} private-key: ${{ secrets.POLKADOTSDK_GHPAGES_APP_KEY }} - - name: Publish ${{ env.BENCH_NAME }} - uses: benchmark-action/github-action-benchmark@v1 + - name: Generate ${{ env.BENCH }} env: - BENCH_NAME: availability-recovery-regression-bench + BENCH: availability-recovery-regression-bench + uses: benchmark-action/github-action-benchmark@v1 with: tool: "customSmallerIsBetter" - name: ${{ env.BENCH_NAME }} - output-file-path: ./artifacts/${{ env.BENCH_NAME }}/${{ env.BENCH_NAME }}.json - benchmark-data-dir-path: ./artifacts/${{ env.BENCH_NAME }} + name: ${{ env.BENCH }} + output-file-path: ./charts/${{ env.BENCH }}/${{ env.BENCH }}.json + benchmark-data-dir-path: ./bench/${{ env.BENCH }} github-token: ${{ steps.app-token.outputs.token }} + auto-push: true - - name: Publish ${{ env.BENCH_NAME }} - uses: benchmark-action/github-action-benchmark@v1 + - name: Generate ${{ env.BENCH }} env: - BENCH_NAME: availability-distribution-regression-bench + BENCH: availability-distribution-regression-bench + uses: benchmark-action/github-action-benchmark@v1 with: tool: "customSmallerIsBetter" - name: ${{ env.BENCH_NAME }} - output-file-path: ./artifacts/${{ env.BENCH_NAME }}/${{ env.BENCH_NAME }}.json - benchmark-data-dir-path: ./artifacts/${{ env.BENCH_NAME }} + name: ${{ env.BENCH }} + output-file-path: ./charts/${{ env.BENCH }}/${{ env.BENCH }}.json + benchmark-data-dir-path: ./bench/${{ env.BENCH }} github-token: ${{ steps.app-token.outputs.token }} + auto-push: true - - name: Publish ${{ env.BENCH_NAME }} - uses: benchmark-action/github-action-benchmark@v1 + - name: Generate ${{ env.BENCH }} env: - BENCH_NAME: approval-voting-regression-bench + BENCH: approval-voting-regression-bench + uses: benchmark-action/github-action-benchmark@v1 with: tool: "customSmallerIsBetter" - name: ${{ env.BENCH_NAME }} - output-file-path: ./artifacts/${{ env.BENCH_NAME }}/${{ env.BENCH_NAME }}.json - benchmark-data-dir-path: ./artifacts/${{ env.BENCH_NAME }} + name: ${{ env.BENCH }} + output-file-path: ./charts/${{ env.BENCH }}/${{ env.BENCH }}.json + benchmark-data-dir-path: ./bench/${{ env.BENCH }} github-token: ${{ steps.app-token.outputs.token }} + auto-push: true - - name: Publish ${{ env.BENCH_NAME }} - uses: benchmark-action/github-action-benchmark@v1 + - name: Generate ${{ env.BENCH }} env: - BENCH_NAME: statement-distribution-regression-bench + BENCH: statement-distribution-regression-bench + uses: benchmark-action/github-action-benchmark@v1 with: tool: "customSmallerIsBetter" - name: ${{ env.BENCH_NAME }} - output-file-path: ./artifacts/${{ env.BENCH_NAME }}/${{ env.BENCH_NAME }}.json - benchmark-data-dir-path: ./artifacts/${{ env.BENCH_NAME }} + name: ${{ env.BENCH }} + output-file-path: ./charts/${{ env.BENCH }}/${{ env.BENCH }}.json + benchmark-data-dir-path: ./bench/${{ env.BENCH }} github-token: ${{ steps.app-token.outputs.token }} + auto-push: true From 4653c3791479cec68ad78624a403eaee1c147835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=B3nal=20Murray?= Date: Thu, 12 Sep 2024 17:42:28 +0100 Subject: [PATCH 372/382] Add `pallet_proxy` to People Chain and Coretime Chain testnet runtimes. (#5509) Proxies are possible in the runtimes for Kusama and Polkadot but this functionality was not previously available on testnets. Closes #5453. Proxies can now be used on `coretime-rococo`, `coretime-westend`, `people-rococo` and `people-westend` in the same way as they can be on Kusama and Polkadot chains. The exact same proxies are configured as the production runtimes for the respective system parachains. --- Cargo.lock | 4 + .../coretime/coretime-rococo/Cargo.toml | 4 + .../coretime/coretime-rococo/src/lib.rs | 143 ++++++++++- .../coretime-rococo/src/weights/mod.rs | 1 + .../src/weights/pallet_proxy.rs | 226 ++++++++++++++++++ .../coretime/coretime-westend/Cargo.toml | 4 + .../coretime/coretime-westend/src/lib.rs | 143 ++++++++++- .../coretime-westend/src/weights/mod.rs | 1 + .../src/weights/pallet_proxy.rs | 226 ++++++++++++++++++ .../runtimes/people/people-rococo/Cargo.toml | 4 + .../runtimes/people/people-rococo/src/lib.rs | 123 +++++++++- .../people/people-rococo/src/weights/mod.rs | 1 + .../people-rococo/src/weights/pallet_proxy.rs | 226 ++++++++++++++++++ .../runtimes/people/people-westend/Cargo.toml | 4 + .../runtimes/people/people-westend/src/lib.rs | 123 +++++++++- .../people/people-westend/src/weights/mod.rs | 1 + .../src/weights/pallet_proxy.rs | 226 ++++++++++++++++++ prdoc/pr_5509.prdoc | 17 ++ 18 files changed, 1465 insertions(+), 12 deletions(-) create mode 100644 cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_proxy.rs create mode 100644 cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_proxy.rs create mode 100644 cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_proxy.rs create mode 100644 cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_proxy.rs create mode 100644 prdoc/pr_5509.prdoc diff --git a/Cargo.lock b/Cargo.lock index 35dc61928178..d30acc0f5b25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3357,6 +3357,7 @@ dependencies = [ "pallet-collator-selection", "pallet-message-queue", "pallet-multisig", + "pallet-proxy", "pallet-session", "pallet-sudo", "pallet-timestamp", @@ -3453,6 +3454,7 @@ dependencies = [ "pallet-collator-selection", "pallet-message-queue", "pallet-multisig", + "pallet-proxy", "pallet-session", "pallet-timestamp", "pallet-transaction-payment", @@ -12683,6 +12685,7 @@ dependencies = [ "pallet-identity", "pallet-message-queue", "pallet-multisig", + "pallet-proxy", "pallet-session", "pallet-timestamp", "pallet-transaction-payment", @@ -12782,6 +12785,7 @@ dependencies = [ "pallet-identity", "pallet-message-queue", "pallet-multisig", + "pallet-proxy", "pallet-session", "pallet-timestamp", "pallet-transaction-payment", diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml index 07d133c80be7..80417ea00362 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml @@ -34,6 +34,7 @@ pallet-balances = { workspace = true } pallet-message-queue = { workspace = true } pallet-broker = { workspace = true } pallet-multisig = { workspace = true } +pallet-proxy = { workspace = true } pallet-session = { workspace = true } pallet-sudo = { workspace = true } pallet-timestamp = { workspace = true } @@ -108,6 +109,7 @@ std = [ "pallet-collator-selection/std", "pallet-message-queue/std", "pallet-multisig/std", + "pallet-proxy/std", "pallet-session/std", "pallet-sudo/std", "pallet-timestamp/std", @@ -158,6 +160,7 @@ runtime-benchmarks = [ "pallet-collator-selection/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", @@ -188,6 +191,7 @@ try-runtime = [ "pallet-collator-selection/try-runtime", "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", + "pallet-proxy/try-runtime", "pallet-session/try-runtime", "pallet-sudo/try-runtime", "pallet-timestamp/try-runtime", diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index aea2bf232cbc..0c9f9461f7f4 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -36,6 +36,7 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ @@ -43,7 +44,9 @@ use frame_support::{ dispatch::DispatchClass, genesis_builder_helper::{build_state, get_preset}, parameter_types, - traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, TransformOrigin}, + traits::{ + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, TransformOrigin, + }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; @@ -65,9 +68,9 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::Block as BlockT, + traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, + ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, RuntimeDebug, }; #[cfg(feature = "std")] use sp_version::NativeVersion; @@ -438,6 +441,138 @@ impl pallet_multisig::Config for Runtime { type WeightInfo = weights::pallet_multisig::WeightInfo; } +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + Any, + /// Can execute any call that does not transfer funds or assets. + NonTransfer, + /// Proxy with the ability to reject time-delay proxy announcements. + CancelProxy, + /// Proxy for all Broker pallet calls. + Broker, + /// Proxy for renewing coretime. + CoretimeRenewer, + /// Proxy able to purchase on-demand coretime credits. + OnDemandPurchaser, + /// Collator selection proxy. Can execute calls related to collator selection mechanism. + Collator, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances { .. } | + // `purchase`, `renew`, `transfer` and `purchase_credit` are pretty self explanatory. + RuntimeCall::Broker(pallet_broker::Call::purchase { .. }) | + RuntimeCall::Broker(pallet_broker::Call::renew { .. }) | + RuntimeCall::Broker(pallet_broker::Call::transfer { .. }) | + RuntimeCall::Broker(pallet_broker::Call::purchase_credit { .. }) | + // `pool` doesn't transfer, but it defines the account to be paid for contributions + RuntimeCall::Broker(pallet_broker::Call::pool { .. }) | + // `assign` is essentially a transfer of a region NFT + RuntimeCall::Broker(pallet_broker::Call::assign { .. }) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + ProxyType::Broker => { + matches!( + c, + RuntimeCall::Broker { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::CoretimeRenewer => { + matches!( + c, + RuntimeCall::Broker(pallet_broker::Call::renew { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::OnDemandPurchaser => { + matches!( + c, + RuntimeCall::Broker(pallet_broker::Call::purchase_credit { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::Collator => matches!( + c, + RuntimeCall::CollatorSelection { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + (ProxyType::Broker, ProxyType::CoretimeRenewer) => true, + (ProxyType::Broker, ProxyType::OnDemandPurchaser) => true, + (ProxyType::NonTransfer, ProxyType::Collator) => true, + _ => false, + } + } +} + +parameter_types! { + // One storage item; key size 32, value size 8; . + pub const ProxyDepositBase: Balance = deposit(1, 40); + // Additional storage item size of 33 bytes. + pub const ProxyDepositFactor: Balance = deposit(0, 33); + pub const MaxProxies: u16 = 32; + // One storage item; key size 32, value size 16 + pub const AnnouncementDepositBase: Balance = deposit(1, 48); + pub const AnnouncementDepositFactor: Balance = deposit(0, 66); + pub const MaxPending: u16 = 32; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = weights::pallet_proxy::WeightInfo; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; +} + impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -481,6 +616,7 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, + Proxy: pallet_proxy = 42, // The main stage. Broker: pallet_broker = 50, @@ -504,6 +640,7 @@ mod benches { [pallet_xcm, PalletXcmExtrinsicsBenchmark::] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_proxy, Proxy] [pallet_utility, Utility] // NOTE: Make sure you point to the individual modules below. [pallet_xcm_benchmarks::fungible, XcmBalances] diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs index f1050b3ae636..ab3d6704c937 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs @@ -27,6 +27,7 @@ pub mod pallet_broker; pub mod pallet_collator_selection; pub mod pallet_message_queue; pub mod pallet_multisig; +pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_proxy.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_proxy.rs new file mode 100644 index 000000000000..5f95906f473e --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_proxy.rs @@ -0,0 +1,226 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_proxy` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-07-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/production/polkadot-parachain +// benchmark +// pallet +// --chain=coretime-rococo-dev +// --wasm-execution=compiled +// --pallet=pallet_proxy +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./file_header.txt +// --output=./parachains/runtimes/coretime/coretime-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_proxy`. +pub struct WeightInfo(PhantomData); +impl pallet_proxy::WeightInfo for WeightInfo { + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 16_417_000 picoseconds. + Weight::from_parts(17_283_443, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_409 + .saturating_add(Weight::from_parts(32_123, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn proxy_announced(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `454 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `5698` + // Minimum execution time: 37_572_000 picoseconds. + Weight::from_parts(37_045_756, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_896 + .saturating_add(Weight::from_parts(139_561, 0).saturating_mul(a.into())) + // Standard Error: 2_993 + .saturating_add(Weight::from_parts(73_270, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn remove_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ±0)` + // Estimated: `5698` + // Minimum execution time: 24_066_000 picoseconds. + Weight::from_parts(24_711_403, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_626 + .saturating_add(Weight::from_parts(128_391, 0).saturating_mul(a.into())) + // Standard Error: 1_680 + .saturating_add(Weight::from_parts(23_124, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn reject_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ±0)` + // Estimated: `5698` + // Minimum execution time: 24_162_000 picoseconds. + Weight::from_parts(23_928_058, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_072 + .saturating_add(Weight::from_parts(152_299, 0).saturating_mul(a.into())) + // Standard Error: 2_141 + .saturating_add(Weight::from_parts(39_775, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn announce(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `386 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `5698` + // Minimum execution time: 33_858_000 picoseconds. + Weight::from_parts(33_568_059, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_816 + .saturating_add(Weight::from_parts(134_400, 0).saturating_mul(a.into())) + // Standard Error: 1_876 + .saturating_add(Weight::from_parts(57_028, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn add_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 24_947_000 picoseconds. + Weight::from_parts(26_235_199, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_363 + .saturating_add(Weight::from_parts(41_435, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 25_186_000 picoseconds. + Weight::from_parts(26_823_133, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_259 + .saturating_add(Weight::from_parts(34_224, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxies(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 22_156_000 picoseconds. + Weight::from_parts(23_304_060, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_738 + .saturating_add(Weight::from_parts(39_612, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn create_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `139` + // Estimated: `4706` + // Minimum execution time: 26_914_000 picoseconds. + Weight::from_parts(28_009_062, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_978 + .saturating_add(Weight::from_parts(12_255, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 30]`. + fn kill_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `164 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 23_281_000 picoseconds. + Weight::from_parts(24_392_989, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_943 + .saturating_add(Weight::from_parts(30_287, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml index 5029c82f971d..25bf777047d0 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml @@ -34,6 +34,7 @@ pallet-balances = { workspace = true } pallet-message-queue = { workspace = true } pallet-broker = { workspace = true } pallet-multisig = { workspace = true } +pallet-proxy = { workspace = true } pallet-session = { workspace = true } pallet-timestamp = { workspace = true } pallet-transaction-payment = { workspace = true } @@ -108,6 +109,7 @@ std = [ "pallet-collator-selection/std", "pallet-message-queue/std", "pallet-multisig/std", + "pallet-proxy/std", "pallet-session/std", "pallet-timestamp/std", "pallet-transaction-payment-rpc-runtime-api/std", @@ -157,6 +159,7 @@ runtime-benchmarks = [ "pallet-collator-selection/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", @@ -186,6 +189,7 @@ try-runtime = [ "pallet-collator-selection/try-runtime", "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", + "pallet-proxy/try-runtime", "pallet-session/try-runtime", "pallet-timestamp/try-runtime", "pallet-transaction-payment/try-runtime", diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index 218afaab924d..614eae895a74 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -36,6 +36,7 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ @@ -43,7 +44,9 @@ use frame_support::{ dispatch::DispatchClass, genesis_builder_helper::{build_state, get_preset}, parameter_types, - traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, TransformOrigin}, + traits::{ + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, TransformOrigin, + }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; @@ -65,9 +68,9 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::Block as BlockT, + traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, + ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, RuntimeDebug, }; #[cfg(feature = "std")] use sp_version::NativeVersion; @@ -438,6 +441,138 @@ impl pallet_multisig::Config for Runtime { type WeightInfo = weights::pallet_multisig::WeightInfo; } +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + Any, + /// Can execute any call that does not transfer funds or assets. + NonTransfer, + /// Proxy with the ability to reject time-delay proxy announcements. + CancelProxy, + /// Proxy for all Broker pallet calls. + Broker, + /// Proxy for renewing coretime. + CoretimeRenewer, + /// Proxy able to purchase on-demand coretime credits. + OnDemandPurchaser, + /// Collator selection proxy. Can execute calls related to collator selection mechanism. + Collator, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances { .. } | + // `purchase`, `renew`, `transfer` and `purchase_credit` are pretty self explanatory. + RuntimeCall::Broker(pallet_broker::Call::purchase { .. }) | + RuntimeCall::Broker(pallet_broker::Call::renew { .. }) | + RuntimeCall::Broker(pallet_broker::Call::transfer { .. }) | + RuntimeCall::Broker(pallet_broker::Call::purchase_credit { .. }) | + // `pool` doesn't transfer, but it defines the account to be paid for contributions + RuntimeCall::Broker(pallet_broker::Call::pool { .. }) | + // `assign` is essentially a transfer of a region NFT + RuntimeCall::Broker(pallet_broker::Call::assign { .. }) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + ProxyType::Broker => { + matches!( + c, + RuntimeCall::Broker { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::CoretimeRenewer => { + matches!( + c, + RuntimeCall::Broker(pallet_broker::Call::renew { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::OnDemandPurchaser => { + matches!( + c, + RuntimeCall::Broker(pallet_broker::Call::purchase_credit { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::Collator => matches!( + c, + RuntimeCall::CollatorSelection { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + (ProxyType::Broker, ProxyType::CoretimeRenewer) => true, + (ProxyType::Broker, ProxyType::OnDemandPurchaser) => true, + (ProxyType::NonTransfer, ProxyType::Collator) => true, + _ => false, + } + } +} + +parameter_types! { + // One storage item; key size 32, value size 8; . + pub const ProxyDepositBase: Balance = deposit(1, 40); + // Additional storage item size of 33 bytes. + pub const ProxyDepositFactor: Balance = deposit(0, 33); + pub const MaxProxies: u16 = 32; + // One storage item; key size 32, value size 16 + pub const AnnouncementDepositBase: Balance = deposit(1, 48); + pub const AnnouncementDepositFactor: Balance = deposit(0, 66); + pub const MaxPending: u16 = 32; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = weights::pallet_proxy::WeightInfo; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; +} + impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -475,6 +610,7 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, + Proxy: pallet_proxy = 42, // The main stage. Broker: pallet_broker = 50, @@ -495,6 +631,7 @@ mod benches { [pallet_xcm, PalletXcmExtrinsicsBenchmark::] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_proxy, Proxy] [pallet_utility, Utility] // NOTE: Make sure you point to the individual modules below. [pallet_xcm_benchmarks::fungible, XcmBalances] diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs index f1050b3ae636..ab3d6704c937 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs @@ -27,6 +27,7 @@ pub mod pallet_broker; pub mod pallet_collator_selection; pub mod pallet_message_queue; pub mod pallet_multisig; +pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_proxy.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_proxy.rs new file mode 100644 index 000000000000..d3edc1a8b200 --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_proxy.rs @@ -0,0 +1,226 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_proxy` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-07-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/production/polkadot-parachain +// benchmark +// pallet +// --chain=coretime-westend-dev +// --wasm-execution=compiled +// --pallet=pallet_proxy +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./file_header.txt +// --output=./parachains/runtimes/coretime/coretime-westend/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_proxy`. +pub struct WeightInfo(PhantomData); +impl pallet_proxy::WeightInfo for WeightInfo { + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 16_417_000 picoseconds. + Weight::from_parts(17_283_443, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_409 + .saturating_add(Weight::from_parts(32_123, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn proxy_announced(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `454 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `5698` + // Minimum execution time: 37_572_000 picoseconds. + Weight::from_parts(37_045_756, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_896 + .saturating_add(Weight::from_parts(139_561, 0).saturating_mul(a.into())) + // Standard Error: 2_993 + .saturating_add(Weight::from_parts(73_270, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn remove_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ±0)` + // Estimated: `5698` + // Minimum execution time: 24_066_000 picoseconds. + Weight::from_parts(24_711_403, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_626 + .saturating_add(Weight::from_parts(128_391, 0).saturating_mul(a.into())) + // Standard Error: 1_680 + .saturating_add(Weight::from_parts(23_124, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn reject_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ±0)` + // Estimated: `5698` + // Minimum execution time: 24_162_000 picoseconds. + Weight::from_parts(23_928_058, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_072 + .saturating_add(Weight::from_parts(152_299, 0).saturating_mul(a.into())) + // Standard Error: 2_141 + .saturating_add(Weight::from_parts(39_775, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn announce(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `386 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `5698` + // Minimum execution time: 33_858_000 picoseconds. + Weight::from_parts(33_568_059, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_816 + .saturating_add(Weight::from_parts(134_400, 0).saturating_mul(a.into())) + // Standard Error: 1_876 + .saturating_add(Weight::from_parts(57_028, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn add_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 24_947_000 picoseconds. + Weight::from_parts(26_235_199, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_363 + .saturating_add(Weight::from_parts(41_435, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 25_186_000 picoseconds. + Weight::from_parts(26_823_133, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_259 + .saturating_add(Weight::from_parts(34_224, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxies(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 22_156_000 picoseconds. + Weight::from_parts(23_304_060, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_738 + .saturating_add(Weight::from_parts(39_612, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn create_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `139` + // Estimated: `4706` + // Minimum execution time: 26_914_000 picoseconds. + Weight::from_parts(28_009_062, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_978 + .saturating_add(Weight::from_parts(12_255, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 30]`. + fn kill_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `164 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 23_281_000 picoseconds. + Weight::from_parts(24_392_989, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_943 + .saturating_add(Weight::from_parts(30_287, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml index c676587b1de4..c969bb2985bd 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml @@ -31,6 +31,7 @@ pallet-balances = { workspace = true } pallet-identity = { workspace = true } pallet-message-queue = { workspace = true } pallet-multisig = { workspace = true } +pallet-proxy = { workspace = true } pallet-session = { workspace = true } pallet-timestamp = { workspace = true } pallet-transaction-payment = { workspace = true } @@ -104,6 +105,7 @@ std = [ "pallet-identity/std", "pallet-message-queue/std", "pallet-multisig/std", + "pallet-proxy/std", "pallet-session/std", "pallet-timestamp/std", "pallet-transaction-payment-rpc-runtime-api/std", @@ -153,6 +155,7 @@ runtime-benchmarks = [ "pallet-identity/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", @@ -182,6 +185,7 @@ try-runtime = [ "pallet-identity/try-runtime", "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", + "pallet-proxy/try-runtime", "pallet-session/try-runtime", "pallet-timestamp/try-runtime", "pallet-transaction-payment/try-runtime", diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index cb9177d0c23b..9b251a90d678 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -25,6 +25,7 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ @@ -33,7 +34,8 @@ use frame_support::{ genesis_builder_helper::{build_state, get_preset}, parameter_types, traits::{ - ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, TransformOrigin, + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, InstanceFilter, + TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, @@ -57,11 +59,11 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::Block as BlockT, + traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; -pub use sp_runtime::{MultiAddress, Perbill, Permill}; +pub use sp_runtime::{MultiAddress, Perbill, Permill, RuntimeDebug}; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -401,6 +403,119 @@ impl pallet_multisig::Config for Runtime { type WeightInfo = weights::pallet_multisig::WeightInfo; } +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + Any, + /// Can execute any call that does not transfer funds or assets. + NonTransfer, + /// Proxy with the ability to reject time-delay proxy announcements. + CancelProxy, + /// Proxy for all Identity pallet calls. + Identity, + /// Proxy for identity registrars. + IdentityJudgement, + /// Collator selection proxy. Can execute calls related to collator selection mechanism. + Collator, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances { .. } | + // `request_judgement` puts up a deposit to transfer to a registrar + RuntimeCall::Identity(pallet_identity::Call::request_judgement { .. }) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + ProxyType::Identity => { + matches!( + c, + RuntimeCall::Identity { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::IdentityJudgement => matches!( + c, + RuntimeCall::Identity(pallet_identity::Call::provide_judgement { .. }) | + RuntimeCall::Utility(..) | + RuntimeCall::Multisig { .. } + ), + ProxyType::Collator => matches!( + c, + RuntimeCall::CollatorSelection { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + (ProxyType::Identity, ProxyType::IdentityJudgement) => true, + (ProxyType::NonTransfer, ProxyType::IdentityJudgement) => true, + (ProxyType::NonTransfer, ProxyType::Collator) => true, + _ => false, + } + } +} + +parameter_types! { + // One storage item; key size 32, value size 8. + pub const ProxyDepositBase: Balance = deposit(1, 40); + // Additional storage item size of 33 bytes. + pub const ProxyDepositFactor: Balance = deposit(0, 33); + pub const MaxProxies: u16 = 32; + // One storage item; key size 32, value size 16. + pub const AnnouncementDepositBase: Balance = deposit(1, 48); + pub const AnnouncementDepositFactor: Balance = deposit(0, 66); + pub const MaxPending: u16 = 32; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = weights::pallet_proxy::WeightInfo; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; +} + impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -446,6 +561,7 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, + Proxy: pallet_proxy = 42, // The main stage. Identity: pallet_identity = 50, @@ -464,6 +580,7 @@ mod benches { [pallet_identity, Identity] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_proxy, Proxy] [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs index 3396a8caea05..dce959e817be 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs @@ -25,6 +25,7 @@ pub mod pallet_collator_selection; pub mod pallet_identity; pub mod pallet_message_queue; pub mod pallet_multisig; +pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_proxy.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_proxy.rs new file mode 100644 index 000000000000..264213c94d44 --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_proxy.rs @@ -0,0 +1,226 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_proxy` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-07-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/production/polkadot-parachain +// benchmark +// pallet +// --chain=people-rococo-dev +// --wasm-execution=compiled +// --pallet=pallet_proxy +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./file_header.txt +// --output=./parachains/runtimes/people/people-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_proxy`. +pub struct WeightInfo(PhantomData); +impl pallet_proxy::WeightInfo for WeightInfo { + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 16_417_000 picoseconds. + Weight::from_parts(17_283_443, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_409 + .saturating_add(Weight::from_parts(32_123, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn proxy_announced(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `454 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `5698` + // Minimum execution time: 37_572_000 picoseconds. + Weight::from_parts(37_045_756, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_896 + .saturating_add(Weight::from_parts(139_561, 0).saturating_mul(a.into())) + // Standard Error: 2_993 + .saturating_add(Weight::from_parts(73_270, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn remove_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ±0)` + // Estimated: `5698` + // Minimum execution time: 24_066_000 picoseconds. + Weight::from_parts(24_711_403, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_626 + .saturating_add(Weight::from_parts(128_391, 0).saturating_mul(a.into())) + // Standard Error: 1_680 + .saturating_add(Weight::from_parts(23_124, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn reject_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ±0)` + // Estimated: `5698` + // Minimum execution time: 24_162_000 picoseconds. + Weight::from_parts(23_928_058, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_072 + .saturating_add(Weight::from_parts(152_299, 0).saturating_mul(a.into())) + // Standard Error: 2_141 + .saturating_add(Weight::from_parts(39_775, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn announce(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `386 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `5698` + // Minimum execution time: 33_858_000 picoseconds. + Weight::from_parts(33_568_059, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_816 + .saturating_add(Weight::from_parts(134_400, 0).saturating_mul(a.into())) + // Standard Error: 1_876 + .saturating_add(Weight::from_parts(57_028, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn add_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 24_947_000 picoseconds. + Weight::from_parts(26_235_199, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_363 + .saturating_add(Weight::from_parts(41_435, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 25_186_000 picoseconds. + Weight::from_parts(26_823_133, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_259 + .saturating_add(Weight::from_parts(34_224, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxies(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 22_156_000 picoseconds. + Weight::from_parts(23_304_060, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_738 + .saturating_add(Weight::from_parts(39_612, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn create_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `139` + // Estimated: `4706` + // Minimum execution time: 26_914_000 picoseconds. + Weight::from_parts(28_009_062, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_978 + .saturating_add(Weight::from_parts(12_255, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 30]`. + fn kill_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `164 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 23_281_000 picoseconds. + Weight::from_parts(24_392_989, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_943 + .saturating_add(Weight::from_parts(30_287, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml index ab7dd04bb78a..64e956d8b6b5 100644 --- a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml @@ -31,6 +31,7 @@ pallet-balances = { workspace = true } pallet-identity = { workspace = true } pallet-message-queue = { workspace = true } pallet-multisig = { workspace = true } +pallet-proxy = { workspace = true } pallet-session = { workspace = true } pallet-timestamp = { workspace = true } pallet-transaction-payment = { workspace = true } @@ -104,6 +105,7 @@ std = [ "pallet-identity/std", "pallet-message-queue/std", "pallet-multisig/std", + "pallet-proxy/std", "pallet-session/std", "pallet-timestamp/std", "pallet-transaction-payment-rpc-runtime-api/std", @@ -153,6 +155,7 @@ runtime-benchmarks = [ "pallet-identity/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", @@ -182,6 +185,7 @@ try-runtime = [ "pallet-identity/try-runtime", "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", + "pallet-proxy/try-runtime", "pallet-session/try-runtime", "pallet-timestamp/try-runtime", "pallet-transaction-payment/try-runtime", diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index 9813c5cb6acc..07bfba92c933 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -25,6 +25,7 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ @@ -33,7 +34,8 @@ use frame_support::{ genesis_builder_helper::{build_state, get_preset}, parameter_types, traits::{ - ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, TransformOrigin, + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, InstanceFilter, + TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, @@ -57,11 +59,11 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::Block as BlockT, + traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; -pub use sp_runtime::{MultiAddress, Perbill, Permill}; +pub use sp_runtime::{MultiAddress, Perbill, Permill, RuntimeDebug}; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -401,6 +403,119 @@ impl pallet_multisig::Config for Runtime { type WeightInfo = weights::pallet_multisig::WeightInfo; } +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + Any, + /// Can execute any call that does not transfer funds or assets. + NonTransfer, + /// Proxy with the ability to reject time-delay proxy announcements. + CancelProxy, + /// Proxy for all Identity pallet calls. + Identity, + /// Proxy for identity registrars. + IdentityJudgement, + /// Collator selection proxy. Can execute calls related to collator selection mechanism. + Collator, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances { .. } | + // `request_judgement` puts up a deposit to transfer to a registrar + RuntimeCall::Identity(pallet_identity::Call::request_judgement { .. }) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + ProxyType::Identity => { + matches!( + c, + RuntimeCall::Identity { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::IdentityJudgement => matches!( + c, + RuntimeCall::Identity(pallet_identity::Call::provide_judgement { .. }) | + RuntimeCall::Utility(..) | + RuntimeCall::Multisig { .. } + ), + ProxyType::Collator => matches!( + c, + RuntimeCall::CollatorSelection { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + (ProxyType::Identity, ProxyType::IdentityJudgement) => true, + (ProxyType::NonTransfer, ProxyType::IdentityJudgement) => true, + (ProxyType::NonTransfer, ProxyType::Collator) => true, + _ => false, + } + } +} + +parameter_types! { + // One storage item; key size 32, value size 8. + pub const ProxyDepositBase: Balance = deposit(1, 40); + // Additional storage item size of 33 bytes. + pub const ProxyDepositFactor: Balance = deposit(0, 33); + pub const MaxProxies: u16 = 32; + // One storage item; key size 32, value size 16. + pub const AnnouncementDepositBase: Balance = deposit(1, 48); + pub const AnnouncementDepositFactor: Balance = deposit(0, 66); + pub const MaxPending: u16 = 32; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = weights::pallet_proxy::WeightInfo; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; +} + impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -446,6 +561,7 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, + Proxy: pallet_proxy = 42, // The main stage. Identity: pallet_identity = 50, @@ -464,6 +580,7 @@ mod benches { [pallet_identity, Identity] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_proxy, Proxy] [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs index 3396a8caea05..dce959e817be 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs @@ -25,6 +25,7 @@ pub mod pallet_collator_selection; pub mod pallet_identity; pub mod pallet_message_queue; pub mod pallet_multisig; +pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_proxy.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_proxy.rs new file mode 100644 index 000000000000..e962123f216c --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_proxy.rs @@ -0,0 +1,226 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_proxy` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-07-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/production/polkadot-parachain +// benchmark +// pallet +// --chain=people-westend-dev +// --wasm-execution=compiled +// --pallet=pallet_proxy +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./file_header.txt +// --output=./parachains/runtimes/people/people-westend/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_proxy`. +pub struct WeightInfo(PhantomData); +impl pallet_proxy::WeightInfo for WeightInfo { + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 16_417_000 picoseconds. + Weight::from_parts(17_283_443, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_409 + .saturating_add(Weight::from_parts(32_123, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn proxy_announced(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `454 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `5698` + // Minimum execution time: 37_572_000 picoseconds. + Weight::from_parts(37_045_756, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_896 + .saturating_add(Weight::from_parts(139_561, 0).saturating_mul(a.into())) + // Standard Error: 2_993 + .saturating_add(Weight::from_parts(73_270, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn remove_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ±0)` + // Estimated: `5698` + // Minimum execution time: 24_066_000 picoseconds. + Weight::from_parts(24_711_403, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_626 + .saturating_add(Weight::from_parts(128_391, 0).saturating_mul(a.into())) + // Standard Error: 1_680 + .saturating_add(Weight::from_parts(23_124, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn reject_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ±0)` + // Estimated: `5698` + // Minimum execution time: 24_162_000 picoseconds. + Weight::from_parts(23_928_058, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_072 + .saturating_add(Weight::from_parts(152_299, 0).saturating_mul(a.into())) + // Standard Error: 2_141 + .saturating_add(Weight::from_parts(39_775, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn announce(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `386 + a * (68 ±0) + p * (37 ±0)` + // Estimated: `5698` + // Minimum execution time: 33_858_000 picoseconds. + Weight::from_parts(33_568_059, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_816 + .saturating_add(Weight::from_parts(134_400, 0).saturating_mul(a.into())) + // Standard Error: 1_876 + .saturating_add(Weight::from_parts(57_028, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn add_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 24_947_000 picoseconds. + Weight::from_parts(26_235_199, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_363 + .saturating_add(Weight::from_parts(41_435, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 25_186_000 picoseconds. + Weight::from_parts(26_823_133, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_259 + .saturating_add(Weight::from_parts(34_224, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxies(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 22_156_000 picoseconds. + Weight::from_parts(23_304_060, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_738 + .saturating_add(Weight::from_parts(39_612, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn create_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `139` + // Estimated: `4706` + // Minimum execution time: 26_914_000 picoseconds. + Weight::from_parts(28_009_062, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_978 + .saturating_add(Weight::from_parts(12_255, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 30]`. + fn kill_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `164 + p * (37 ±0)` + // Estimated: `4706` + // Minimum execution time: 23_281_000 picoseconds. + Weight::from_parts(24_392_989, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_943 + .saturating_add(Weight::from_parts(30_287, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/prdoc/pr_5509.prdoc b/prdoc/pr_5509.prdoc new file mode 100644 index 000000000000..154146034e6f --- /dev/null +++ b/prdoc/pr_5509.prdoc @@ -0,0 +1,17 @@ +title: Add `pallet_proxy` to People Chain and Coretime Chain testnet runtimes. + +doc: + - audience: Runtime User + description: | + Proxies can now be used on `coretime-rococo`, `coretime-westend`, `people-rococo` and + `people-westend` in the same way as they can be on Kusama and Polkadot chains. + +crates: + - name: coretime-rococo-runtime + bump: major + - name: coretime-westend-runtime + bump: major + - name: people-rococo-runtime + bump: major + - name: people-westend-runtime + bump: major From cd69f209b71c554f06d3061169ad00c5f1c170b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=B3nal=20Murray?= Date: Thu, 12 Sep 2024 17:48:04 +0100 Subject: [PATCH 373/382] Add emulated tests for the Coretime Interface calls to ensure sufficient weights (#2704) Add emulated test cases for the coretime chain. This tests the calls sent across the `CoretimeInterface` and ensures the weights are sufficient. --- Cargo.lock | 14 +- .../coretime/coretime-rococo/Cargo.toml | 2 +- .../coretime/coretime-westend/Cargo.toml | 2 +- .../tests/coretime/coretime-rococo/Cargo.toml | 5 +- .../tests/coretime/coretime-rococo/src/lib.rs | 4 +- .../src/tests/coretime_interface.rs | 235 ++++++++++++++++++ .../coretime/coretime-rococo/src/tests/mod.rs | 1 + .../coretime/coretime-westend/Cargo.toml | 5 +- .../coretime/coretime-westend/src/lib.rs | 4 +- .../src/tests/coretime_interface.rs | 223 +++++++++++++++++ .../coretime-westend/src/tests/mod.rs | 1 + 11 files changed, 484 insertions(+), 12 deletions(-) create mode 100644 cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/coretime_interface.rs create mode 100644 cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/coretime_interface.rs diff --git a/Cargo.lock b/Cargo.lock index d30acc0f5b25..e5b12a60da2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3299,7 +3299,7 @@ dependencies = [ [[package]] name = "coretime-rococo-emulated-chain" -version = "0.0.0" +version = "0.1.0" dependencies = [ "coretime-rococo-runtime", "cumulus-primitives-core", @@ -3312,14 +3312,17 @@ dependencies = [ [[package]] name = "coretime-rococo-integration-tests" -version = "0.1.0" +version = "0.0.0" dependencies = [ + "cumulus-pallet-parachain-system", "emulated-integration-tests-common", "frame-support", "pallet-balances", + "pallet-broker", "pallet-identity", "pallet-message-queue", "polkadot-runtime-common", + "polkadot-runtime-parachains", "rococo-runtime-constants", "rococo-system-emulated-network", "sp-runtime", @@ -3396,7 +3399,7 @@ dependencies = [ [[package]] name = "coretime-westend-emulated-chain" -version = "0.0.0" +version = "0.1.0" dependencies = [ "coretime-westend-runtime", "cumulus-primitives-core", @@ -3409,14 +3412,17 @@ dependencies = [ [[package]] name = "coretime-westend-integration-tests" -version = "0.1.0" +version = "0.0.0" dependencies = [ + "cumulus-pallet-parachain-system", "emulated-integration-tests-common", "frame-support", "pallet-balances", + "pallet-broker", "pallet-identity", "pallet-message-queue", "polkadot-runtime-common", + "polkadot-runtime-parachains", "sp-runtime", "staging-xcm", "staging-xcm-executor", diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo/Cargo.toml index 6af3f270a905..94d43c5eee2f 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "coretime-rococo-emulated-chain" -version = "0.0.0" +version = "0.1.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend/Cargo.toml index 895a984eccb2..2640c27d016b 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "coretime-westend-emulated-chain" -version = "0.0.0" +version = "0.1.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/Cargo.toml index 259be790c3e5..28d9da0993ff 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "coretime-rococo-integration-tests" -version = "0.1.0" +version = "0.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -12,16 +12,19 @@ publish = false # Substrate frame-support = { workspace = true } pallet-balances = { workspace = true } +pallet-broker = { workspace = true, default-features = true } pallet-message-queue = { workspace = true } pallet-identity = { workspace = true } sp-runtime = { workspace = true } # Polkadot polkadot-runtime-common = { workspace = true, default-features = true } +polkadot-runtime-parachains = { workspace = true, default-features = true } rococo-runtime-constants = { workspace = true, default-features = true } xcm = { workspace = true } xcm-executor = { workspace = true } # Cumulus +cumulus-pallet-parachain-system = { workspace = true, default-features = true } emulated-integration-tests-common = { workspace = true } rococo-system-emulated-network = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/lib.rs index ad3c4fd58da9..055bd50d8298 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/lib.rs @@ -24,7 +24,7 @@ mod imports { // Cumulus pub use emulated_integration_tests_common::xcm_emulator::{ - assert_expected_events, bx, TestExt, + assert_expected_events, bx, Chain, Parachain, TestExt, }; pub use rococo_system_emulated_network::{ coretime_rococo_emulated_chain::{ @@ -32,7 +32,7 @@ mod imports { CoretimeRococoParaPallet as CoretimeRococoPallet, }, CoretimeRococoPara as CoretimeRococo, CoretimeRococoParaReceiver as CoretimeRococoReceiver, - CoretimeRococoParaSender as CoretimeRococoSender, + CoretimeRococoParaSender as CoretimeRococoSender, RococoRelay as Rococo, }; } diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/coretime_interface.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/coretime_interface.rs new file mode 100644 index 000000000000..584bce8f1df7 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/coretime_interface.rs @@ -0,0 +1,235 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::imports::*; +use frame_support::traits::OnInitialize; +use pallet_broker::{ConfigRecord, Configuration, CoreAssignment, CoreMask, ScheduleItem}; +use rococo_runtime_constants::system_parachain::coretime::TIMESLICE_PERIOD; +use sp_runtime::Perbill; + +#[test] +fn transact_hardcoded_weights_are_sane() { + // There are three transacts with hardcoded weights sent from the Coretime Chain to the Relay + // Chain across the CoretimeInterface which are triggered at various points in the sales cycle. + // - Request core count - triggered directly by `start_sales` or `request_core_count` + // extrinsics. + // - Request revenue info - triggered when each timeslice is committed. + // - Assign core - triggered when an entry is encountered in the workplan for the next + // timeslice. + + // RuntimeEvent aliases to avoid warning from usage of qualified paths in assertions due to + // + type CoretimeEvent = ::RuntimeEvent; + type RelayEvent = ::RuntimeEvent; + + // Reserve a workload, configure broker and start sales. + CoretimeRococo::execute_with(|| { + // Hooks don't run in emulated tests - workaround as we need `on_initialize` to tick things + // along and have no concept of time passing otherwise. + ::Broker::on_initialize( + ::System::block_number(), + ); + + let coretime_root_origin = ::RuntimeOrigin::root(); + + // Create and populate schedule with the worst case assignment on this core. + let mut schedule = Vec::new(); + for i in 0..27 { + schedule.push(ScheduleItem { + mask: CoreMask::void().set(i), + assignment: CoreAssignment::Task(2000 + i), + }) + } + + assert_ok!(::Broker::reserve( + coretime_root_origin.clone(), + schedule.try_into().expect("Vector is within bounds."), + )); + + // Configure broker and start sales. + let config = ConfigRecord { + advance_notice: 1, + interlude_length: 1, + leadin_length: 2, + region_length: 1, + ideal_bulk_proportion: Perbill::from_percent(40), + limit_cores_offered: None, + renewal_bump: Perbill::from_percent(2), + contribution_timeout: 1, + }; + assert_ok!(::Broker::configure( + coretime_root_origin.clone(), + config + )); + assert_ok!(::Broker::start_sales( + coretime_root_origin, + 100, + 0 + )); + assert_eq!( + pallet_broker::Status::<::Runtime>::get() + .unwrap() + .core_count, + 1 + ); + + assert_expected_events!( + CoretimeRococo, + vec![ + CoretimeEvent::Broker( + pallet_broker::Event::ReservationMade { .. } + ) => {}, + CoretimeEvent::Broker( + pallet_broker::Event::CoreCountRequested { core_count: 1 } + ) => {}, + CoretimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); + }); + + // Check that the request_core_count message was processed successfully. This will fail if the + // weights are misconfigured. + Rococo::execute_with(|| { + Rococo::assert_ump_queue_processed(true, Some(CoretimeRococo::para_id()), None); + + assert_expected_events!( + Rococo, + vec![ + RelayEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); + }); + + // Keep track of the relay chain block number so we can fast forward while still checking the + // right block. + let mut block_number_cursor = Rococo::ext_wrapper(::System::block_number); + + let config = CoretimeRococo::ext_wrapper(|| { + Configuration::<::Runtime>::get() + .expect("Pallet was configured earlier.") + }); + + // Now run up to the block before the sale is rotated. + while block_number_cursor < TIMESLICE_PERIOD - config.advance_notice - 1 { + CoretimeRococo::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + }); + + Rococo::ext_wrapper(|| { + block_number_cursor = ::System::block_number(); + }); + } + + // In this block we trigger assign core. + CoretimeRococo::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + + assert_expected_events!( + CoretimeRococo, + vec![ + CoretimeEvent::Broker( + pallet_broker::Event::SaleInitialized { .. } + ) => {}, + CoretimeEvent::Broker( + pallet_broker::Event::CoreAssigned { .. } + ) => {}, + CoretimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); + }); + + // Check that the assign_core message was processed successfully. + // This will fail if the weights are misconfigured. + Rococo::execute_with(|| { + Rococo::assert_ump_queue_processed(true, Some(CoretimeRococo::para_id()), None); + + assert_expected_events!( + Rococo, + vec![ + RelayEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + RelayEvent::Coretime( + polkadot_runtime_parachains::coretime::Event::CoreAssigned { .. } + ) => {}, + ] + ); + }); + + // In this block we trigger request revenue. + CoretimeRococo::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + + assert_expected_events!( + CoretimeRococo, + vec![ + CoretimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); + }); + + // Check that the request_revenue_info_at message was processed successfully. + // This will fail if the weights are misconfigured. + Rococo::execute_with(|| { + Rococo::assert_ump_queue_processed(true, Some(CoretimeRococo::para_id()), None); + + assert_expected_events!( + Rococo, + vec![ + RelayEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); + }); + + // Here we receive and process the notify_revenue XCM with zero revenue. + CoretimeRococo::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + + assert_expected_events!( + CoretimeRococo, + vec![ + CoretimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + // Zero revenue in first timeslice so history is immediately dropped. + CoretimeEvent::Broker( + pallet_broker::Event::HistoryDropped { when: 0, revenue: 0 } + ) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/mod.rs index 0e78351bce03..bb0387a4b350 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/mod.rs @@ -14,3 +14,4 @@ // limitations under the License. mod claim_assets; +mod coretime_interface; diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/Cargo.toml index a8fa905d2e5e..d57e7926b0ec 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "coretime-westend-integration-tests" -version = "0.1.0" +version = "0.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -12,16 +12,19 @@ publish = false # Substrate frame-support = { workspace = true } pallet-balances = { workspace = true } +pallet-broker = { workspace = true, default-features = true } pallet-message-queue = { workspace = true } pallet-identity = { workspace = true } sp-runtime = { workspace = true } # Polkadot polkadot-runtime-common = { workspace = true, default-features = true } +polkadot-runtime-parachains = { workspace = true, default-features = true } westend-runtime-constants = { workspace = true, default-features = true } xcm = { workspace = true } xcm-executor = { workspace = true } # Cumulus +cumulus-pallet-parachain-system = { workspace = true, default-features = true } emulated-integration-tests-common = { workspace = true } westend-system-emulated-network = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/lib.rs index 838ca6eeafb6..ac844e0f3284 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/lib.rs @@ -24,7 +24,7 @@ mod imports { // Cumulus pub use emulated_integration_tests_common::xcm_emulator::{ - assert_expected_events, bx, TestExt, + assert_expected_events, bx, Chain, Parachain, TestExt, }; pub use westend_system_emulated_network::{ coretime_westend_emulated_chain::{ @@ -33,7 +33,7 @@ mod imports { }, CoretimeWestendPara as CoretimeWestend, CoretimeWestendParaReceiver as CoretimeWestendReceiver, - CoretimeWestendParaSender as CoretimeWestendSender, + CoretimeWestendParaSender as CoretimeWestendSender, WestendRelay as Westend, }; } diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/coretime_interface.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/coretime_interface.rs new file mode 100644 index 000000000000..f61bc4285a0c --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/coretime_interface.rs @@ -0,0 +1,223 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::imports::*; +use frame_support::traits::OnInitialize; +use pallet_broker::{ConfigRecord, Configuration, CoreAssignment, CoreMask, ScheduleItem}; +use sp_runtime::Perbill; +use westend_runtime_constants::system_parachain::coretime::TIMESLICE_PERIOD; + +#[test] +fn transact_hardcoded_weights_are_sane() { + // There are three transacts with hardcoded weights sent from the Coretime Chain to the Relay + // Chain across the CoretimeInterface which are triggered at various points in the sales cycle. + // - Request core count - triggered directly by `start_sales` or `request_core_count` + // extrinsics. + // - Request revenue info - triggered when each timeslice is committed. + // - Assign core - triggered when an entry is encountered in the workplan for the next + // timeslice. + + // RuntimeEvent aliases to avoid warning from usage of qualified paths in assertions due to + // + type CoretimeEvent = ::RuntimeEvent; + type RelayEvent = ::RuntimeEvent; + + // Reserve a workload, configure broker and start sales. + CoretimeWestend::execute_with(|| { + // Hooks don't run in emulated tests - workaround as we need `on_initialize` to tick things + // along and have no concept of time passing otherwise. + ::Broker::on_initialize( + ::System::block_number(), + ); + + let coretime_root_origin = ::RuntimeOrigin::root(); + + // Create and populate schedule with the worst case assignment on this core. + let mut schedule = Vec::new(); + for i in 0..27 { + schedule.push(ScheduleItem { + mask: CoreMask::void().set(i), + assignment: CoreAssignment::Task(2000 + i), + }) + } + + assert_ok!(::Broker::reserve( + coretime_root_origin.clone(), + schedule.try_into().expect("Vector is within bounds."), + )); + + // Configure broker and start sales. + let config = ConfigRecord { + advance_notice: 1, + interlude_length: 1, + leadin_length: 2, + region_length: 1, + ideal_bulk_proportion: Perbill::from_percent(40), + limit_cores_offered: None, + renewal_bump: Perbill::from_percent(2), + contribution_timeout: 1, + }; + assert_ok!(::Broker::configure( + coretime_root_origin.clone(), + config + )); + assert_ok!(::Broker::start_sales( + coretime_root_origin, + 100, + 0 + )); + assert_eq!( + pallet_broker::Status::<::Runtime>::get() + .unwrap() + .core_count, + 1 + ); + + assert_expected_events!( + CoretimeWestend, + vec![ + CoretimeEvent::Broker( + pallet_broker::Event::ReservationMade { .. } + ) => {}, + CoretimeEvent::Broker( + pallet_broker::Event::CoreCountRequested { core_count: 1 } + ) => {}, + CoretimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); + }); + + // Check that the request_core_count message was processed successfully. This will fail if the + // weights are misconfigured. + Westend::execute_with(|| { + Westend::assert_ump_queue_processed(true, Some(CoretimeWestend::para_id()), None); + + assert_expected_events!( + Westend, + vec![ + RelayEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); + }); + + // Keep track of the relay chain block number so we can fast forward while still checking the + // right block. + let mut block_number_cursor = Westend::ext_wrapper(::System::block_number); + + let config = CoretimeWestend::ext_wrapper(|| { + Configuration::<::Runtime>::get() + .expect("Pallet was configured earlier.") + }); + + // Now run up to the block before the sale is rotated. + while block_number_cursor < TIMESLICE_PERIOD - config.advance_notice - 1 { + CoretimeWestend::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + }); + + Westend::ext_wrapper(|| { + block_number_cursor = ::System::block_number(); + }); + } + + // In this block we trigger assign core. + CoretimeWestend::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + + assert_expected_events!( + CoretimeWestend, + vec![ + CoretimeEvent::Broker( + pallet_broker::Event::SaleInitialized { .. } + ) => {}, + CoretimeEvent::Broker( + pallet_broker::Event::CoreAssigned { .. } + ) => {}, + CoretimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); + }); + + // In this block we trigger request revenue. + CoretimeWestend::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + + assert_expected_events!( + CoretimeWestend, + vec![ + CoretimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); + }); + + // Check that the assign_core and request_revenue_info_at messages were processed successfully. + // This will fail if the weights are misconfigured. + Westend::execute_with(|| { + Westend::assert_ump_queue_processed(true, Some(CoretimeWestend::para_id()), None); + + assert_expected_events!( + Westend, + vec![ + RelayEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + RelayEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + RelayEvent::Coretime( + polkadot_runtime_parachains::coretime::Event::CoreAssigned { .. } + ) => {}, + ] + ); + }); + + // Here we receive and process the notify_revenue XCM with zero revenue. + CoretimeWestend::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + + assert_expected_events!( + CoretimeWestend, + vec![ + CoretimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + // Zero revenue in first timeslice so history is immediately dropped. + CoretimeEvent::Broker( + pallet_broker::Event::HistoryDropped { when: 0, revenue: 0 } + ) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/mod.rs index 0e78351bce03..bb0387a4b350 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/mod.rs @@ -14,3 +14,4 @@ // limitations under the License. mod claim_assets; +mod coretime_interface; From 8d0aab812ebb70e39b0af893862ba204dc098860 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Thu, 12 Sep 2024 19:26:59 +0200 Subject: [PATCH 374/382] [pallet-revive] fix xcm tests (#5684) fix https://github.com/paritytech/polkadot-sdk/issues/5683 --- Cargo.lock | 1 + prdoc/pr_5684.prdoc | 19 +++++++ substrate/frame/revive/fixtures/Cargo.toml | 2 + substrate/frame/revive/fixtures/src/lib.rs | 1 + .../frame/revive/mock-network/Cargo.toml | 1 + .../frame/revive/mock-network/src/tests.rs | 55 +++++++++---------- substrate/frame/revive/src/wasm/runtime.rs | 7 ++- .../frame/revive/uapi/src/host/riscv32.rs | 9 ++- umbrella/Cargo.toml | 1 + 9 files changed, 64 insertions(+), 32 deletions(-) create mode 100644 prdoc/pr_5684.prdoc diff --git a/Cargo.lock b/Cargo.lock index e5b12a60da2a..5f0fa0f613f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11471,6 +11471,7 @@ version = "0.1.0" dependencies = [ "anyhow", "frame-system", + "log", "parity-wasm", "polkavm-linker 0.10.0", "sp-core", diff --git a/prdoc/pr_5684.prdoc b/prdoc/pr_5684.prdoc new file mode 100644 index 000000000000..a17bacd2fb94 --- /dev/null +++ b/prdoc/pr_5684.prdoc @@ -0,0 +1,19 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "[pallet-revive]" + +doc: + - audience: Runtime Devs + description: | + Update xcm runtime api, and fix pallet-revive xcm tests + +crates: + - name: pallet-revive + bump: patch + - name: pallet-revive-fixtures + bump: patch + - name: pallet-revive-mock-network + bump: patch + - name: polkadot-sdk + bump: patch diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index db284c7cc062..903298d2df21 100644 --- a/substrate/frame/revive/fixtures/Cargo.toml +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -16,6 +16,7 @@ sp-core = { workspace = true, default-features = true, optional = true } sp-io = { workspace = true, default-features = true, optional = true } sp-runtime = { workspace = true, default-features = true, optional = true } anyhow = { workspace = true, default-features = true, optional = true } +log = { workspace = true } [build-dependencies] parity-wasm = { workspace = true } @@ -34,6 +35,7 @@ riscv = [] std = [ "anyhow", "frame-system", + "log/std", "sp-core", "sp-io", "sp-runtime", diff --git a/substrate/frame/revive/fixtures/src/lib.rs b/substrate/frame/revive/fixtures/src/lib.rs index 54e32130635a..5548dca66d07 100644 --- a/substrate/frame/revive/fixtures/src/lib.rs +++ b/substrate/frame/revive/fixtures/src/lib.rs @@ -24,6 +24,7 @@ extern crate alloc; pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec, sp_core::H256)> { let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); let fixture_path = out_dir.join(format!("{fixture_name}.polkavm")); + log::debug!("Loading fixture from {fixture_path:?}"); let binary = std::fs::read(fixture_path)?; let code_hash = sp_io::hashing::keccak_256(&binary); Ok((binary, sp_core::H256(code_hash))) diff --git a/substrate/frame/revive/mock-network/Cargo.toml b/substrate/frame/revive/mock-network/Cargo.toml index 0d597bbdc228..85656a57b49c 100644 --- a/substrate/frame/revive/mock-network/Cargo.toml +++ b/substrate/frame/revive/mock-network/Cargo.toml @@ -48,6 +48,7 @@ pallet-revive-fixtures = { workspace = true } [features] default = ["std"] +riscv = ["pallet-revive-fixtures/riscv"] std = [ "codec/std", "frame-support/std", diff --git a/substrate/frame/revive/mock-network/src/tests.rs b/substrate/frame/revive/mock-network/src/tests.rs index 9259dd6f169e..bd05726a1a45 100644 --- a/substrate/frame/revive/mock-network/src/tests.rs +++ b/substrate/frame/revive/mock-network/src/tests.rs @@ -16,10 +16,8 @@ // limitations under the License. use crate::{ - parachain::{self, Runtime}, - parachain_account_sovereign_account_id, - primitives::{AccountId, CENTS}, - relay_chain, MockNet, ParaA, ParachainBalances, Relay, ALICE, BOB, INITIAL_BALANCE, + parachain, parachain_account_sovereign_account_id, primitives::CENTS, relay_chain, MockNet, + ParaA, ParachainBalances, Relay, ALICE, BOB, INITIAL_BALANCE, }; use codec::{Decode, Encode}; use frame_support::traits::{fungibles::Mutate, Currency}; @@ -30,6 +28,7 @@ use pallet_revive::{ }; use pallet_revive_fixtures::compile_module; use pallet_revive_uapi::ReturnErrorCode; +use sp_core::H160; use xcm::{v4::prelude::*, VersionedLocation, VersionedXcm}; use xcm_simulator::TestExt; @@ -39,41 +38,43 @@ macro_rules! assert_return_code { }}; } -fn bare_call(dest: sp_runtime::AccountId32) -> BareCallBuilder { +fn bare_call(dest: H160) -> BareCallBuilder { BareCallBuilder::::bare_call(RawOrigin::Signed(ALICE).into(), dest) } /// Instantiate the tests contract, and fund it with some balance and assets. -fn instantiate_test_contract(name: &str) -> AccountId { - let (wasm, _) = compile_module::(name).unwrap(); +fn instantiate_test_contract(name: &str) -> Contract { + let (wasm, _) = compile_module(name).unwrap(); // Instantiate contract. - let contract_addr = ParaA::execute_with(|| { + let contract = ParaA::execute_with(|| { BareInstantiateBuilder::::bare_instantiate( RawOrigin::Signed(ALICE).into(), Code::Upload(wasm), ) - .build_and_unwrap_account_id() + .storage_deposit_limit(1_000_000_000_000) + .build_and_unwrap_contract() }); // Funds contract account with some balance and assets. ParaA::execute_with(|| { - parachain::Balances::make_free_balance_be(&contract_addr, INITIAL_BALANCE); - parachain::Assets::mint_into(0u32.into(), &contract_addr, INITIAL_BALANCE).unwrap(); + parachain::Balances::make_free_balance_be(&contract.account_id, INITIAL_BALANCE); + parachain::Assets::mint_into(0u32.into(), &contract.account_id, INITIAL_BALANCE).unwrap(); }); Relay::execute_with(|| { - let sovereign_account = parachain_account_sovereign_account_id(1u32, contract_addr.clone()); + let sovereign_account = + parachain_account_sovereign_account_id(1u32, contract.account_id.clone()); relay_chain::Balances::make_free_balance_be(&sovereign_account, INITIAL_BALANCE); }); - contract_addr + contract } #[test] fn test_xcm_execute() { MockNet::reset(); - let contract_addr = instantiate_test_contract("xcm_execute"); + let Contract { addr, account_id } = instantiate_test_contract("xcm_execute"); // Execute XCM instructions through the contract. ParaA::execute_with(|| { @@ -87,9 +88,7 @@ fn test_xcm_execute() { .deposit_asset(assets, beneficiary) .build(); - let result = bare_call(contract_addr.clone()) - .data(VersionedXcm::V4(message).encode()) - .build(); + let result = bare_call(addr).data(VersionedXcm::V4(message).encode()).build(); assert_eq!(result.gas_consumed, result.gas_required); assert_return_code!(&result.result.unwrap(), ReturnErrorCode::Success); @@ -98,7 +97,7 @@ fn test_xcm_execute() { // Bob. let initial = INITIAL_BALANCE; assert_eq!(ParachainBalances::free_balance(BOB), initial + amount); - assert_eq!(ParachainBalances::free_balance(&contract_addr), initial - amount); + assert_eq!(ParachainBalances::free_balance(&account_id), initial - amount); }); } @@ -106,7 +105,7 @@ fn test_xcm_execute() { fn test_xcm_execute_incomplete() { MockNet::reset(); - let contract_addr = instantiate_test_contract("xcm_execute"); + let Contract { addr, account_id } = instantiate_test_contract("xcm_execute"); let amount = 10 * CENTS; // Execute XCM instructions through the contract. @@ -124,15 +123,13 @@ fn test_xcm_execute_incomplete() { .deposit_asset(assets, beneficiary) .build(); - let result = bare_call(contract_addr.clone()) - .data(VersionedXcm::V4(message).encode()) - .build(); + let result = bare_call(addr).data(VersionedXcm::V4(message).encode()).build(); assert_eq!(result.gas_consumed, result.gas_required); assert_return_code!(&result.result.unwrap(), ReturnErrorCode::XcmExecutionFailed); assert_eq!(ParachainBalances::free_balance(BOB), INITIAL_BALANCE); - assert_eq!(ParachainBalances::free_balance(&contract_addr), INITIAL_BALANCE - amount); + assert_eq!(ParachainBalances::free_balance(&account_id), INITIAL_BALANCE - amount); }); } @@ -140,11 +137,11 @@ fn test_xcm_execute_incomplete() { fn test_xcm_execute_reentrant_call() { MockNet::reset(); - let contract_addr = instantiate_test_contract("xcm_execute"); + let Contract { addr, .. } = instantiate_test_contract("xcm_execute"); ParaA::execute_with(|| { let transact_call = parachain::RuntimeCall::Contracts(pallet_revive::Call::call { - dest: contract_addr.clone(), + dest: addr, gas_limit: 1_000_000.into(), storage_deposit_limit: test_utils::deposit_limit::(), data: vec![], @@ -157,7 +154,7 @@ fn test_xcm_execute_reentrant_call() { .expect_transact_status(MaybeErrorCode::Success) .build(); - let result = bare_call(contract_addr.clone()) + let result = bare_call(addr) .data(VersionedXcm::V4(message).encode()) .build_and_unwrap_result(); @@ -171,7 +168,7 @@ fn test_xcm_execute_reentrant_call() { #[test] fn test_xcm_send() { MockNet::reset(); - let contract_addr = instantiate_test_contract("xcm_send"); + let Contract { addr, account_id } = instantiate_test_contract("xcm_send"); let amount = 1_000 * CENTS; let fee = parachain::estimate_message_fee(4); // Accounts for the `DescendOrigin` instruction added by `send_xcm` @@ -188,7 +185,7 @@ fn test_xcm_send() { .deposit_asset(assets, beneficiary) .build(); - let result = bare_call(contract_addr.clone()) + let result = bare_call(addr) .data((dest, VersionedXcm::V4(message)).encode()) .build_and_unwrap_result(); @@ -197,7 +194,7 @@ fn test_xcm_send() { }); Relay::execute_with(|| { - let derived_contract_addr = ¶chain_account_sovereign_account_id(1, contract_addr); + let derived_contract_addr = ¶chain_account_sovereign_account_id(1, account_id); assert_eq!( INITIAL_BALANCE - amount, relay_chain::Balances::free_balance(derived_contract_addr) diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index d9257d38b66e..02d60ee9e34d 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -1790,6 +1790,7 @@ pub mod env { &mut self, memory: &mut M, dest_ptr: u32, + dest_len: u32, msg_ptr: u32, msg_len: u32, output_ptr: u32, @@ -1797,10 +1798,12 @@ pub mod env { use xcm::{VersionedLocation, VersionedXcm}; use xcm_builder::{SendController, SendControllerWeightInfo}; - self.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; - let dest: VersionedLocation = memory.read_as(dest_ptr)?; + self.charge_gas(RuntimeCosts::CopyFromContract(dest_len))?; + let dest: VersionedLocation = memory.read_as_unbounded(dest_ptr, dest_len)?; + self.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; let message: VersionedXcm<()> = memory.read_as_unbounded(msg_ptr, msg_len)?; + let weight = <::Xcm as SendController<_>>::WeightInfo::send(); self.charge_gas(RuntimeCosts::CallRuntime(weight))?; let origin = crate::RawOrigin::Signed(self.ext.account_id().clone()).into(); diff --git a/substrate/frame/revive/uapi/src/host/riscv32.rs b/substrate/frame/revive/uapi/src/host/riscv32.rs index b7b660c40837..89cf92786445 100644 --- a/substrate/frame/revive/uapi/src/host/riscv32.rs +++ b/substrate/frame/revive/uapi/src/host/riscv32.rs @@ -124,6 +124,7 @@ mod sys { pub fn xcm_execute(msg_ptr: *const u8, msg_len: u32) -> ReturnCode; pub fn xcm_send( dest_ptr: *const u8, + dest_len: *const u8, msg_ptr: *const u8, msg_len: u32, out_ptr: *mut u8, @@ -530,7 +531,13 @@ impl HostFn for HostFnImpl { fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result { let ret_code = unsafe { - sys::xcm_send(dest.as_ptr(), msg.as_ptr(), msg.len() as _, output.as_mut_ptr()) + sys::xcm_send( + dest.as_ptr(), + dest.len() as _, + msg.as_ptr(), + msg.len() as _, + output.as_mut_ptr(), + ) }; ret_code.into() } diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 6d380a4bcbb7..b7c1c375094f 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -607,6 +607,7 @@ tuples-96 = [ ] riscv = [ "pallet-revive-fixtures?/riscv", + "pallet-revive-mock-network?/riscv", "pallet-revive?/riscv", ] From 3c694436ab9cfb992ed666ecded40f904f1c8164 Mon Sep 17 00:00:00 2001 From: Vincent Geddes <117534+vgeddes@users.noreply.github.com> Date: Thu, 12 Sep 2024 20:22:47 +0200 Subject: [PATCH 375/382] Remove redundant code --- bridges/snowbridge/pallets/system/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 8f3235279956..1e8a788b7a5a 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -628,9 +628,7 @@ pub mod pallet { let location: Location = (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; - let pays_fee = PaysFee::::No; - - Self::do_register_token(&location, metadata, pays_fee)?; + Self::do_register_token(&location, metadata, PaysFee::::No)?; Ok(PostDispatchInfo { actual_weight: Some(T::WeightInfo::register_token()), From 315070f0fe94975f15c8caed2b3192ab410fd7db Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 09:23:05 +0800 Subject: [PATCH 376/382] Remove tests unused --- bridges/snowbridge/primitives/router/src/inbound/tests.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index 1c018f3ab0c4..e0672f3928af 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -66,9 +66,5 @@ fn test_reanchor_all_assets() { let mut reanchored_asset_with_ethereum_context = reanchored_asset.clone(); assert_ok!(reanchored_asset_with_ethereum_context.reanchor(&global_ah, ðereum_context)); assert_eq!(reanchored_asset_with_ethereum_context, asset.clone()); - // reanchor back to original location in context of BH - let mut reanchored_asset_with_bh_context = reanchored_asset.clone(); - assert_ok!(reanchored_asset_with_bh_context.reanchor(&global_ah, &bh_context)); - assert_eq!(reanchored_asset_with_bh_context, asset.clone()); } } From 37a28c4a6d486c80aac31fad62d68d5e3723f646 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 09:38:01 +0800 Subject: [PATCH 377/382] Rename multi_assets --- .../bridge-hub-westend/src/tests/snowbridge.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index f2cd15e71ad7..b668ac89d845 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -218,7 +218,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { )), fun: Fungible(TOKEN_AMOUNT), }]; - let multi_assets = VersionedAssets::V4(Assets::from(assets)); + let versioned_assets = VersionedAssets::V4(Assets::from(assets)); let destination = VersionedLocation::V4(Location::new( 2, @@ -239,7 +239,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { RuntimeOrigin::signed(AssetHubWestendReceiver::get()), Box::new(destination), Box::new(beneficiary), - Box::new(multi_assets), + Box::new(versioned_assets), 0, Unlimited, ) @@ -337,7 +337,7 @@ fn transfer_relay_token() { type RuntimeEvent = ::RuntimeEvent; let assets = vec![Asset { id: AssetId(Location::parent()), fun: Fungible(TOKEN_AMOUNT) }]; - let multi_assets = VersionedAssets::V4(Assets::from(assets)); + let versioned_assets = VersionedAssets::V4(Assets::from(assets)); let destination = VersionedLocation::V4(Location::new( 2, @@ -353,7 +353,7 @@ fn transfer_relay_token() { RuntimeOrigin::signed(AssetHubWestendSender::get()), Box::new(destination), Box::new(beneficiary), - Box::new(multi_assets), + Box::new(versioned_assets), 0, Unlimited, )); @@ -500,7 +500,7 @@ fn transfer_ah_token() { // Send partial of the token, will fail if send all let assets = vec![Asset { id: AssetId(asset_id.clone()), fun: Fungible(TOKEN_AMOUNT / 10) }]; - let multi_assets = VersionedAssets::V4(Assets::from(assets)); + let versioned_assets = VersionedAssets::V4(Assets::from(assets)); let beneficiary = VersionedLocation::V4(Location::new( 0, @@ -511,7 +511,7 @@ fn transfer_ah_token() { RuntimeOrigin::signed(AssetHubWestendSender::get()), Box::new(VersionedLocation::from(ethereum_destination)), Box::new(beneficiary), - Box::new(multi_assets), + Box::new(versioned_assets), 0, Unlimited, )); From 22f8dea20120f758eebb0285c6b37615be5456a6 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 09:42:47 +0800 Subject: [PATCH 378/382] Cleanup --- .../primitives/core/src/location.rs | 23 ++----------------- .../primitives/router/src/inbound/tests.rs | 1 - 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index fe8db0e35283..aad1c9ece05c 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -97,10 +97,9 @@ impl DescribeLocation for DescribeTokenTerminal { #[cfg(test)] mod tests { use crate::TokenIdOf; - use frame_support::assert_ok; use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, InteriorLocation, Junction::*, Location, - NetworkId::*, PalletInstance, Parachain, Reanchorable, + GeneralIndex, GeneralKey, GlobalConsensus, Junction::*, Location, NetworkId::*, + PalletInstance, Parachain, }; use xcm_executor::traits::ConvertLocation; @@ -203,22 +202,4 @@ mod tests { ); } } - - #[test] - fn test_reanchor_relay_token_from_different_consensus() { - let asset_id: Location = Location::new(2, [GlobalConsensus(Rococo)]); - let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); - let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); - let mut reanchored_asset = asset_id.clone(); - assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); - assert_eq!( - reanchored_asset, - Location { parents: 1, interior: [GlobalConsensus(Rococo)].into() } - ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); - let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); - let mut reanchored_asset = reanchored_asset.clone(); - assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); - assert_eq!(reanchored_asset, asset_id); - } } diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index e0672f3928af..e0e90e516be1 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -45,7 +45,6 @@ fn test_reanchor_all_assets() { let ethereum = Location::new(2, ethereum_context.clone()); let ah_context: InteriorLocation = [GlobalConsensus(Polkadot), Parachain(1000)].into(); let global_ah = Location::new(1, ah_context.clone()); - let bh_context: InteriorLocation = [GlobalConsensus(Polkadot), Parachain(1002)].into(); let assets = vec![ // DOT Location::new(1, []), From 1a67710130501cc245da1e6790f0f755e1d9a2a6 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 09:55:37 +0800 Subject: [PATCH 379/382] More checks --- .../tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index b668ac89d845..4e9dd5a77dd7 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -363,8 +363,8 @@ fn transfer_relay_token() { assert!( events.iter().any(|event| matches!( event, - RuntimeEvent::Balances(pallet_balances::Event::Transfer { amount, ..}) - if *amount == TOKEN_AMOUNT, + RuntimeEvent::Balances(pallet_balances::Event::Transfer { amount, to, ..}) + if *amount == TOKEN_AMOUNT && *to == ethereum_sovereign.clone(), )), "native token reserved to Ethereum sovereign account." ); From 211b450f06fc8b29f44a50147f40e6544ccbeffa Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 10:04:32 +0800 Subject: [PATCH 380/382] Rename GlobalAssetHub to AssetHubFromEthereum --- bridges/snowbridge/pallets/inbound-queue/src/mock.rs | 4 ++-- .../bridge-hub-rococo/src/bridge_to_ethereum_config.rs | 4 ++-- .../bridge-hub-westend/src/bridge_to_ethereum_config.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index f5e44c2e46a8..3e67d5ab738b 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -114,7 +114,7 @@ parameter_types! { pub const InboundQueuePalletInstance: u8 = 80; pub UniversalLocation: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); - pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]); + pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]); } #[cfg(feature = "runtime-benchmarks")] @@ -233,7 +233,7 @@ impl inbound_queue::Config for Test { Balance, MockTokenIdConvert, UniversalLocation, - GlobalAssetHub, + AssetHubFromEthereum, >; type PricingParameters = Parameters; type ChannelLookup = MockChannelLookup; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index d9a3869bd6e1..be7005b5379a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -66,7 +66,7 @@ parameter_types! { rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, multiplier: FixedU128::from_rational(1, 1), }; - pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(rococo_runtime_constants::system_parachain::ASSET_HUB_ID)]); + pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(rococo_runtime_constants::system_parachain::ASSET_HUB_ID)]); pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); } @@ -90,7 +90,7 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { Balance, EthereumSystem, EthereumUniversalLocation, - GlobalAssetHub, + AssetHubFromEthereum, >; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 1bd425ab4075..dbca4166a135 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -69,7 +69,7 @@ parameter_types! { rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, multiplier: FixedU128::from_rational(1, 1), }; - pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(westend_runtime_constants::system_parachain::ASSET_HUB_ID)]); + pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(westend_runtime_constants::system_parachain::ASSET_HUB_ID)]); pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); } impl snowbridge_pallet_inbound_queue::Config for Runtime { @@ -92,7 +92,7 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { Balance, EthereumSystem, EthereumUniversalLocation, - GlobalAssetHub, + AssetHubFromEthereum, >; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; From d1ac9166be92fc45986836f405c10fb07c94e7ef Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 10:22:50 +0800 Subject: [PATCH 381/382] Fix clippy --- .../snowbridge/pallets/system/src/tests.rs | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 98c937d86328..d0286e04abdf 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -661,46 +661,44 @@ fn register_all_tokens_succeeds() { RegisterTokenTestCase { native: Location::parent(), reanchored: Location::new(1, GlobalConsensus(Polkadot)), - foreign: hex!( - "4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2" - ) - .into(), + foreign: hex!("4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2") + .into(), }, // GLMR (Some Polkadot parachain currency) RegisterTokenTestCase { native: Location::new(1, [Parachain(2004)]), reanchored: Location::new(1, [GlobalConsensus(Polkadot), Parachain(2004)]), - foreign: hex!( - "34c08fc90409b6924f0e8eabb7c2aaa0c749e23e31adad9f6d217b577737fafb" - ) - .into(), + foreign: hex!("34c08fc90409b6924f0e8eabb7c2aaa0c749e23e31adad9f6d217b577737fafb") + .into(), }, // USDT RegisterTokenTestCase { native: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), - reanchored: Location::new(1, [GlobalConsensus(Polkadot), Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), - foreign: hex!( - "14b0579be12d7d7f9971f1d4b41f0e88384b9b74799b0150d4aa6cd01afb4444" - ) - .into(), + reanchored: Location::new( + 1, + [ + GlobalConsensus(Polkadot), + Parachain(1000), + PalletInstance(50), + GeneralIndex(1984), + ], + ), + foreign: hex!("14b0579be12d7d7f9971f1d4b41f0e88384b9b74799b0150d4aa6cd01afb4444") + .into(), }, // KSM RegisterTokenTestCase { native: Location::new(2, [GlobalConsensus(Kusama)]), reanchored: Location::new(1, [GlobalConsensus(Kusama)]), - foreign: hex!( - "03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852" - ) - .into(), + foreign: hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852") + .into(), }, // KAR (Some Kusama parachain currency) RegisterTokenTestCase { native: Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), reanchored: Location::new(1, [GlobalConsensus(Kusama), Parachain(2000)]), - foreign: hex!( - "d3e39ad6ea4cee68c9741181e94098823b2ea34a467577d0875c036f0fce5be0" - ) - .into(), + foreign: hex!("d3e39ad6ea4cee68c9741181e94098823b2ea34a467577d0875c036f0fce5be0") + .into(), }, ]; for tc in test_cases.iter() { @@ -708,10 +706,14 @@ fn register_all_tokens_succeeds() { let origin = RuntimeOrigin::root(); let versioned_location: VersionedLocation = tc.native.clone().into(); - assert_ok!(EthereumSystem::register_token(origin, Box::new(versioned_location), Default::default())); + assert_ok!(EthereumSystem::register_token( + origin, + Box::new(versioned_location), + Default::default() + )); - assert_eq!(NativeToForeignId::::get(tc.reanchored.clone()), Some(tc.foreign.clone())); - assert_eq!(ForeignToNativeId::::get(tc.foreign.clone()), Some(tc.reanchored.clone())); + assert_eq!(NativeToForeignId::::get(tc.reanchored.clone()), Some(tc.foreign)); + assert_eq!(ForeignToNativeId::::get(tc.foreign), Some(tc.reanchored.clone())); System::assert_last_event(RuntimeEvent::EthereumSystem(Event::::RegisterToken { location: tc.reanchored.clone().into(), From 7a37dd4162ab5e4f0c3532644e465af64063f832 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 10:22:50 +0800 Subject: [PATCH 382/382] Fix clippy --- .../snowbridge/pallets/system/src/tests.rs | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 98c937d86328..d0286e04abdf 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -661,46 +661,44 @@ fn register_all_tokens_succeeds() { RegisterTokenTestCase { native: Location::parent(), reanchored: Location::new(1, GlobalConsensus(Polkadot)), - foreign: hex!( - "4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2" - ) - .into(), + foreign: hex!("4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2") + .into(), }, // GLMR (Some Polkadot parachain currency) RegisterTokenTestCase { native: Location::new(1, [Parachain(2004)]), reanchored: Location::new(1, [GlobalConsensus(Polkadot), Parachain(2004)]), - foreign: hex!( - "34c08fc90409b6924f0e8eabb7c2aaa0c749e23e31adad9f6d217b577737fafb" - ) - .into(), + foreign: hex!("34c08fc90409b6924f0e8eabb7c2aaa0c749e23e31adad9f6d217b577737fafb") + .into(), }, // USDT RegisterTokenTestCase { native: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), - reanchored: Location::new(1, [GlobalConsensus(Polkadot), Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), - foreign: hex!( - "14b0579be12d7d7f9971f1d4b41f0e88384b9b74799b0150d4aa6cd01afb4444" - ) - .into(), + reanchored: Location::new( + 1, + [ + GlobalConsensus(Polkadot), + Parachain(1000), + PalletInstance(50), + GeneralIndex(1984), + ], + ), + foreign: hex!("14b0579be12d7d7f9971f1d4b41f0e88384b9b74799b0150d4aa6cd01afb4444") + .into(), }, // KSM RegisterTokenTestCase { native: Location::new(2, [GlobalConsensus(Kusama)]), reanchored: Location::new(1, [GlobalConsensus(Kusama)]), - foreign: hex!( - "03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852" - ) - .into(), + foreign: hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852") + .into(), }, // KAR (Some Kusama parachain currency) RegisterTokenTestCase { native: Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), reanchored: Location::new(1, [GlobalConsensus(Kusama), Parachain(2000)]), - foreign: hex!( - "d3e39ad6ea4cee68c9741181e94098823b2ea34a467577d0875c036f0fce5be0" - ) - .into(), + foreign: hex!("d3e39ad6ea4cee68c9741181e94098823b2ea34a467577d0875c036f0fce5be0") + .into(), }, ]; for tc in test_cases.iter() { @@ -708,10 +706,14 @@ fn register_all_tokens_succeeds() { let origin = RuntimeOrigin::root(); let versioned_location: VersionedLocation = tc.native.clone().into(); - assert_ok!(EthereumSystem::register_token(origin, Box::new(versioned_location), Default::default())); + assert_ok!(EthereumSystem::register_token( + origin, + Box::new(versioned_location), + Default::default() + )); - assert_eq!(NativeToForeignId::::get(tc.reanchored.clone()), Some(tc.foreign.clone())); - assert_eq!(ForeignToNativeId::::get(tc.foreign.clone()), Some(tc.reanchored.clone())); + assert_eq!(NativeToForeignId::::get(tc.reanchored.clone()), Some(tc.foreign)); + assert_eq!(ForeignToNativeId::::get(tc.foreign), Some(tc.reanchored.clone())); System::assert_last_event(RuntimeEvent::EthereumSystem(Event::::RegisterToken { location: tc.reanchored.clone().into(),
Release notes

Sourced from docker/setup-buildx-action's releases.

v3.6.1

Full Changelog: https://github.com/docker/setup-buildx-action/compare/v3.6.0...v3.6.1

v3.6.0

Full Changelog: https://github.com/docker/setup-buildx-action/compare/v3.5.0...v3.6.0